|
@@ -2558,6 +2558,89 @@ void ScriptEngine::setupBuiltinAPIs() {
|
|
|
}
|
|
}
|
|
|
}, "stat", 1));
|
|
}, "stat", 1));
|
|
|
|
|
|
|
|
|
|
+ // fs.readdir(path) - List a directory, one level only. Recursing here would
|
|
|
|
|
+ // be an unbounded walk driven by whatever happens to be on disk, so a node
|
|
|
|
|
+ // that wants a tree walks it itself.
|
|
|
|
|
+ JS_SetPropertyStr(ctx, fs, "readdir", JS_NewCFunction(ctx, [](JSContext* ctx, JSValue this_val, int argc, JSValue* argv) -> JSValue {
|
|
|
|
|
+ if (argc < 1) {
|
|
|
|
|
+ return JS_ThrowTypeError(ctx, "fs.readdir requires path argument");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const char* path = JS_ToCString(ctx, argv[0]);
|
|
|
|
|
+ if (!path) {
|
|
|
|
|
+ return JS_ThrowTypeError(ctx, "path must be a string");
|
|
|
|
|
+ }
|
|
|
|
|
+ std::string path_str(path);
|
|
|
|
|
+ JS_FreeCString(ctx, path);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!std::filesystem::exists(path_str)) {
|
|
|
|
|
+ JSValue response = JS_NewObject(ctx);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "success", JS_FALSE);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "error",
|
|
|
|
|
+ JS_NewString(ctx, ("Directory not found: " + path_str).c_str()));
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!std::filesystem::is_directory(path_str)) {
|
|
|
|
|
+ JSValue response = JS_NewObject(ctx);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "success", JS_FALSE);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "error",
|
|
|
|
|
+ JS_NewString(ctx, ("Not a directory: " + path_str).c_str()));
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSValue entries = JS_NewArray(ctx);
|
|
|
|
|
+ uint32_t index = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for (const auto& entry : std::filesystem::directory_iterator(path_str)) {
|
|
|
|
|
+ JSValue item = JS_NewObject(ctx);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, item, "name",
|
|
|
|
|
+ JS_NewString(ctx, entry.path().filename().string().c_str()));
|
|
|
|
|
+ JS_SetPropertyStr(ctx, item, "path",
|
|
|
|
|
+ JS_NewString(ctx, entry.path().string().c_str()));
|
|
|
|
|
+
|
|
|
|
|
+ const bool is_dir = entry.is_directory();
|
|
|
|
|
+ JS_SetPropertyStr(ctx, item, "isDirectory", is_dir ? JS_TRUE : JS_FALSE);
|
|
|
|
|
+
|
|
|
|
|
+ int64_t size = 0;
|
|
|
|
|
+ int64_t mtime_ms = 0;
|
|
|
|
|
+ // A file removed between listing and stat is ordinary on a
|
|
|
|
|
+ // directory being written to, and is reported with zeroes
|
|
|
|
|
+ // rather than failing the whole listing.
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (entry.is_regular_file()) {
|
|
|
|
|
+ size = static_cast<int64_t>(entry.file_size());
|
|
|
|
|
+ }
|
|
|
|
|
+ auto mtime = entry.last_write_time();
|
|
|
|
|
+ // file_time_type's epoch is not guaranteed to match
|
|
|
|
|
+ // system_clock's (it does not on this libstdc++), so this
|
|
|
|
|
+ // has to go through clock_cast rather than
|
|
|
|
|
+ // time_since_epoch() directly, or modifiedAt comes out
|
|
|
|
|
+ // shifted by whatever offset the filesystem clock uses.
|
|
|
|
|
+ auto sys_time = std::chrono::clock_cast<std::chrono::system_clock>(mtime);
|
|
|
|
|
+ mtime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
|
+ sys_time.time_since_epoch()).count();
|
|
|
|
|
+ } catch (const std::exception&) {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JS_SetPropertyStr(ctx, item, "size", JS_NewInt64(ctx, size));
|
|
|
|
|
+ JS_SetPropertyStr(ctx, item, "modifiedAt", JS_NewInt64(ctx, mtime_ms));
|
|
|
|
|
+
|
|
|
|
|
+ JS_SetPropertyUint32(ctx, entries, index++, item);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSValue response = JS_NewObject(ctx);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "success", JS_TRUE);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "entries", entries);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ } catch (const std::exception& e) {
|
|
|
|
|
+ JSValue response = JS_NewObject(ctx);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "success", JS_FALSE);
|
|
|
|
|
+ JS_SetPropertyStr(ctx, response, "error", JS_NewString(ctx, e.what()));
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+ }, "readdir", 1));
|
|
|
|
|
+
|
|
|
JS_SetPropertyStr(ctx, smartbotic, "fs", fs);
|
|
JS_SetPropertyStr(ctx, smartbotic, "fs", fs);
|
|
|
|
|
|
|
|
// smartbotic.process - Process execution API for external commands
|
|
// smartbotic.process - Process execution API for external commands
|
|
@@ -4392,7 +4475,8 @@ ScriptResult ScriptEngine::execute(const std::string& script, const ScriptContex
|
|
|
// Create context object for the execute function
|
|
// Create context object for the execute function
|
|
|
nlohmann::json ctx_json = {
|
|
nlohmann::json ctx_json = {
|
|
|
{"executionId", context.execution_id},
|
|
{"executionId", context.execution_id},
|
|
|
- {"nodeId", context.node_id}
|
|
|
|
|
|
|
+ {"nodeId", context.node_id},
|
|
|
|
|
+ {"workflowId", context.workflow_id}
|
|
|
};
|
|
};
|
|
|
std::string ctx_str = ctx_json.dump();
|
|
std::string ctx_str = ctx_json.dump();
|
|
|
JSValue ctx_val = JS_ParseJSON(context_, ctx_str.c_str(), ctx_str.size(), "<context>");
|
|
JSValue ctx_val = JS_ParseJSON(context_, ctx_str.c_str(), ctx_str.size(), "<context>");
|