openvibe 0.63.2 → 0.63.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/README.md +39 -0
- package/README_CN.md +1 -1
- package/dist/cli/args.d.ts +3 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +22 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +5 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/companion/executor/bash-executor.d.ts +30 -0
- package/dist/core/companion/executor/bash-executor.d.ts.map +1 -0
- package/dist/core/companion/executor/bash-executor.js +105 -0
- package/dist/core/companion/executor/bash-executor.js.map +1 -0
- package/dist/core/companion/executor/code-executor.d.ts +33 -0
- package/dist/core/companion/executor/code-executor.d.ts.map +1 -0
- package/dist/core/companion/executor/code-executor.js +144 -0
- package/dist/core/companion/executor/code-executor.js.map +1 -0
- package/dist/core/companion/executor/resource-monitor.d.ts +55 -0
- package/dist/core/companion/executor/resource-monitor.d.ts.map +1 -0
- package/dist/core/companion/executor/resource-monitor.js +141 -0
- package/dist/core/companion/executor/resource-monitor.js.map +1 -0
- package/dist/core/companion/http-server.d.ts +92 -0
- package/dist/core/companion/http-server.d.ts.map +1 -0
- package/dist/core/companion/http-server.js +229 -0
- package/dist/core/companion/http-server.js.map +1 -0
- package/dist/core/companion/index.d.ts +29 -0
- package/dist/core/companion/index.d.ts.map +1 -0
- package/dist/core/companion/index.js +48 -0
- package/dist/core/companion/index.js.map +1 -0
- package/dist/core/companion/scheduler/resource-scheduler.d.ts +82 -0
- package/dist/core/companion/scheduler/resource-scheduler.d.ts.map +1 -0
- package/dist/core/companion/scheduler/resource-scheduler.js +333 -0
- package/dist/core/companion/scheduler/resource-scheduler.js.map +1 -0
- package/dist/core/companion/scheduler/workers/base-worker.d.ts +87 -0
- package/dist/core/companion/scheduler/workers/base-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/base-worker.js +70 -0
- package/dist/core/companion/scheduler/workers/base-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.d.ts +12 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.js +134 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.d.ts +16 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.js +167 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/index.d.ts +6 -0
- package/dist/core/companion/scheduler/workers/index.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/index.js +6 -0
- package/dist/core/companion/scheduler/workers/index.js.map +1 -0
- package/dist/core/companion/scheduler/workers/io-worker.d.ts +17 -0
- package/dist/core/companion/scheduler/workers/io-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/io-worker.js +197 -0
- package/dist/core/companion/scheduler/workers/io-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/network-worker.d.ts +13 -0
- package/dist/core/companion/scheduler/workers/network-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/network-worker.js +167 -0
- package/dist/core/companion/scheduler/workers/network-worker.js.map +1 -0
- package/dist/core/companion/security/security-manager.d.ts +50 -0
- package/dist/core/companion/security/security-manager.d.ts.map +1 -0
- package/dist/core/companion/security/security-manager.js +102 -0
- package/dist/core/companion/security/security-manager.js.map +1 -0
- package/dist/core/hybrid-cloud/extension.d.ts +8 -0
- package/dist/core/hybrid-cloud/extension.d.ts.map +1 -0
- package/dist/core/hybrid-cloud/extension.js +64 -0
- package/dist/core/hybrid-cloud/extension.js.map +1 -0
- package/dist/core/hybrid-cloud/index.d.ts +98 -0
- package/dist/core/hybrid-cloud/index.d.ts.map +1 -0
- package/dist/core/hybrid-cloud/index.js +211 -0
- package/dist/core/hybrid-cloud/index.js.map +1 -0
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +6 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +35 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +7 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +46 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/todo-manager.d.ts +32 -0
- package/dist/core/todo-manager.d.ts.map +1 -0
- package/dist/core/todo-manager.js +117 -0
- package/dist/core/todo-manager.js.map +1 -0
- package/dist/core/tools/batch-write.d.ts +42 -0
- package/dist/core/tools/batch-write.d.ts.map +1 -0
- package/dist/core/tools/batch-write.js +267 -0
- package/dist/core/tools/batch-write.js.map +1 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +10 -2
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/fast-executor.d.ts +6 -1
- package/dist/core/tools/fast-executor.d.ts.map +1 -1
- package/dist/core/tools/fast-executor.js +83 -4
- package/dist/core/tools/fast-executor.js.map +1 -1
- package/dist/core/tools/index.d.ts +141 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +30 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/todo.d.ts +47 -0
- package/dist/core/tools/todo.d.ts.map +1 -0
- package/dist/core/tools/todo.js +212 -0
- package/dist/core/tools/todo.js.map +1 -0
- package/dist/core/tools/unified.d.ts +121 -0
- package/dist/core/tools/unified.d.ts.map +1 -0
- package/dist/core/tools/unified.js +481 -0
- package/dist/core/tools/unified.js.map +1 -0
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +20 -5
- package/dist/core/tools/write.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +63 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/index.d.ts +2 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +2 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/todo-display.d.ts +9 -0
- package/dist/modes/interactive/components/todo-display.d.ts.map +1 -0
- package/dist/modes/interactive/components/todo-display.js +60 -0
- package/dist/modes/interactive/components/todo-display.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +8 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +151 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/plan-mode.d.ts +14 -0
- package/dist/modes/plan-mode.d.ts.map +1 -0
- package/dist/modes/plan-mode.js +107 -0
- package/dist/modes/plan-mode.js.map +1 -0
- package/dist/modes/spec-mode.d.ts +16 -0
- package/dist/modes/spec-mode.d.ts.map +1 -0
- package/dist/modes/spec-mode.js +186 -0
- package/dist/modes/spec-mode.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -2
- package/dist/utils/version-check.js.map +1 -1
- package/docs/HYBRID_CLOUD_README.md +188 -0
- package/docs/hybrid-architecture-design.md +317 -0
- package/docs/todo.md +71 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { cpus, totalmem } from "os";
|
|
3
|
+
import { BaseWorker } from "./base-worker.js";
|
|
4
|
+
export class CPUWorker extends BaseWorker {
|
|
5
|
+
id;
|
|
6
|
+
type = "cpu";
|
|
7
|
+
capabilities;
|
|
8
|
+
constructor(id) {
|
|
9
|
+
super();
|
|
10
|
+
this.id = id || `cpu_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
11
|
+
this.capabilities = {
|
|
12
|
+
cpuCores: cpus().length,
|
|
13
|
+
memoryMb: Math.floor(totalmem() / (1024 * 1024)),
|
|
14
|
+
hasGpu: false,
|
|
15
|
+
supportsTypes: ["cpu_intensive", "code", "script", "bash"],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async execute(task, signal) {
|
|
19
|
+
this.markBusy(task.id);
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
try {
|
|
22
|
+
let result;
|
|
23
|
+
if (task.type === "bash" || task.type === "script") {
|
|
24
|
+
result = await this.executeBash(task, signal);
|
|
25
|
+
}
|
|
26
|
+
else if (task.type === "code") {
|
|
27
|
+
result = await this.executeCode(task, signal);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
result = await this.executeCPUTask(task, signal);
|
|
31
|
+
}
|
|
32
|
+
const executionTime = Date.now() - startTime;
|
|
33
|
+
this.updateStats(executionTime, result.success);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const executionTime = Date.now() - startTime;
|
|
38
|
+
this.updateStats(executionTime, false);
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: error instanceof Error ? error.message : String(error),
|
|
42
|
+
errorCode: "EXECUTION_ERROR",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
this.markIdle();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
executeBash(task, signal) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const stdout = [];
|
|
52
|
+
const stderr = [];
|
|
53
|
+
const payload = task.payload;
|
|
54
|
+
const child = spawn(payload.command || "bash", payload.args || [], {
|
|
55
|
+
cwd: payload.workingDir,
|
|
56
|
+
env: { ...process.env, ...payload.env },
|
|
57
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
58
|
+
});
|
|
59
|
+
const timeout = task.requirements?.maxExecutionTimeMs || 300000;
|
|
60
|
+
const timeoutId = setTimeout(() => {
|
|
61
|
+
child.kill("SIGTERM");
|
|
62
|
+
}, timeout);
|
|
63
|
+
const onAbort = () => {
|
|
64
|
+
child.kill("SIGTERM");
|
|
65
|
+
};
|
|
66
|
+
signal?.addEventListener("abort", onAbort);
|
|
67
|
+
child.stdout?.on("data", (data) => stdout.push(data));
|
|
68
|
+
child.stderr?.on("data", (data) => stderr.push(data));
|
|
69
|
+
child.on("close", (code) => {
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
signal?.removeEventListener("abort", onAbort);
|
|
72
|
+
resolve({
|
|
73
|
+
success: code === 0,
|
|
74
|
+
exitCode: code || 0,
|
|
75
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
76
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
child.on("error", (error) => {
|
|
80
|
+
clearTimeout(timeoutId);
|
|
81
|
+
signal?.removeEventListener("abort", onAbort);
|
|
82
|
+
resolve({
|
|
83
|
+
success: false,
|
|
84
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
85
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
86
|
+
error: error.message,
|
|
87
|
+
errorCode: "SPAWN_ERROR",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async executeCode(task, signal) {
|
|
93
|
+
const payload = task.payload;
|
|
94
|
+
const language = payload.language?.toLowerCase() || "python";
|
|
95
|
+
let command;
|
|
96
|
+
let args;
|
|
97
|
+
switch (language) {
|
|
98
|
+
case "python":
|
|
99
|
+
case "py":
|
|
100
|
+
command = "python3";
|
|
101
|
+
args = ["-c", payload.code || ""];
|
|
102
|
+
break;
|
|
103
|
+
case "javascript":
|
|
104
|
+
case "js":
|
|
105
|
+
case "node":
|
|
106
|
+
command = "node";
|
|
107
|
+
args = ["-e", payload.code || ""];
|
|
108
|
+
break;
|
|
109
|
+
case "typescript":
|
|
110
|
+
case "ts":
|
|
111
|
+
command = "npx";
|
|
112
|
+
args = ["tsx", "-e", payload.code || ""];
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: `Unsupported language: ${language}`,
|
|
118
|
+
errorCode: "UNSUPPORTED_LANGUAGE",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return this.executeBash({
|
|
122
|
+
...task,
|
|
123
|
+
payload: { ...payload, command, args },
|
|
124
|
+
}, signal);
|
|
125
|
+
}
|
|
126
|
+
async executeCPUTask(task, signal) {
|
|
127
|
+
return this.executeBash({
|
|
128
|
+
...task,
|
|
129
|
+
type: "bash",
|
|
130
|
+
payload: task.payload,
|
|
131
|
+
}, signal);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=cpu-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cpu-worker.js","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/cpu-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport { cpus, totalmem } from \"os\";\nimport { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from \"./base-worker.js\";\n\nexport class CPUWorker extends BaseWorker {\n\treadonly id: string;\n\treadonly type = \"cpu\";\n\treadonly capabilities: WorkerCapabilities;\n\n\tconstructor(id?: string) {\n\t\tsuper();\n\t\tthis.id = id || `cpu_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;\n\t\tthis.capabilities = {\n\t\t\tcpuCores: cpus().length,\n\t\t\tmemoryMb: Math.floor(totalmem() / (1024 * 1024)),\n\t\t\thasGpu: false,\n\t\t\tsupportsTypes: [\"cpu_intensive\", \"code\", \"script\", \"bash\"],\n\t\t};\n\t}\n\n\tasync execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tthis.markBusy(task.id);\n\t\tconst startTime = Date.now();\n\n\t\ttry {\n\t\t\tlet result: TaskResult;\n\n\t\t\tif (task.type === \"bash\" || task.type === \"script\") {\n\t\t\t\tresult = await this.executeBash(task, signal);\n\t\t\t} else if (task.type === \"code\") {\n\t\t\t\tresult = await this.executeCode(task, signal);\n\t\t\t} else {\n\t\t\t\tresult = await this.executeCPUTask(task, signal);\n\t\t\t}\n\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, result.success);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, false);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"EXECUTION_ERROR\",\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.markIdle();\n\t\t}\n\t}\n\n\tprivate executeBash(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst stdout: Buffer[] = [];\n\t\t\tconst stderr: Buffer[] = [];\n\t\t\tconst payload = task.payload;\n\n\t\t\tconst child = spawn(payload.command || \"bash\", payload.args || [], {\n\t\t\t\tcwd: payload.workingDir,\n\t\t\t\tenv: { ...process.env, ...payload.env },\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tconst timeout = task.requirements?.maxExecutionTimeMs || 300000;\n\t\t\tconst timeoutId = setTimeout(() => {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t}, timeout);\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t};\n\n\t\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\t\tchild.stdout?.on(\"data\", (data: Buffer) => stdout.push(data));\n\t\t\tchild.stderr?.on(\"data\", (data: Buffer) => stderr.push(data));\n\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: code === 0,\n\t\t\t\t\texitCode: code || 0,\n\t\t\t\t\tstdout: Buffer.concat(stdout).toString(),\n\t\t\t\t\tstderr: Buffer.concat(stderr).toString(),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstdout: Buffer.concat(stdout).toString(),\n\t\t\t\t\tstderr: Buffer.concat(stderr).toString(),\n\t\t\t\t\terror: error.message,\n\t\t\t\t\terrorCode: \"SPAWN_ERROR\",\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async executeCode(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tconst payload = task.payload;\n\t\tconst language = payload.language?.toLowerCase() || \"python\";\n\n\t\tlet command: string;\n\t\tlet args: string[];\n\n\t\tswitch (language) {\n\t\t\tcase \"python\":\n\t\t\tcase \"py\":\n\t\t\t\tcommand = \"python3\";\n\t\t\t\targs = [\"-c\", payload.code || \"\"];\n\t\t\t\tbreak;\n\t\t\tcase \"javascript\":\n\t\t\tcase \"js\":\n\t\t\tcase \"node\":\n\t\t\t\tcommand = \"node\";\n\t\t\t\targs = [\"-e\", payload.code || \"\"];\n\t\t\t\tbreak;\n\t\t\tcase \"typescript\":\n\t\t\tcase \"ts\":\n\t\t\t\tcommand = \"npx\";\n\t\t\t\targs = [\"tsx\", \"-e\", payload.code || \"\"];\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Unsupported language: ${language}`,\n\t\t\t\t\terrorCode: \"UNSUPPORTED_LANGUAGE\",\n\t\t\t\t};\n\t\t}\n\n\t\treturn this.executeBash(\n\t\t\t{\n\t\t\t\t...task,\n\t\t\t\tpayload: { ...payload, command, args },\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprivate async executeCPUTask(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\treturn this.executeBash(\n\t\t\t{\n\t\t\t\t...task,\n\t\t\t\ttype: \"bash\",\n\t\t\t\tpayload: task.payload,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from "./base-worker.js";
|
|
2
|
+
export declare class GPUWorker extends BaseWorker {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly type = "gpu";
|
|
5
|
+
readonly capabilities: WorkerCapabilities;
|
|
6
|
+
private devices;
|
|
7
|
+
constructor(id?: string);
|
|
8
|
+
private detectGPUs;
|
|
9
|
+
private buildCapabilities;
|
|
10
|
+
execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult>;
|
|
11
|
+
private executeGPUTask;
|
|
12
|
+
private selectBestDevice;
|
|
13
|
+
private executeGPUCode;
|
|
14
|
+
refreshGPUStatus(): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=gpu-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-worker.d.ts","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/gpu-worker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAU5G,qBAAa,SAAU,SAAQ,UAAU;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAC1C,OAAO,CAAC,OAAO,CAAmB;IAElC,YAAY,EAAE,CAAC,EAAE,MAAM,EAKtB;IAED,OAAO,CAAC,UAAU;IA8ClB,OAAO,CAAC,iBAAiB;IAcnB,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B5E;YAEa,cAAc;IA0B5B,OAAO,CAAC,gBAAgB;YAgBV,cAAc;IA6C5B,gBAAgB,IAAI,IAAI,CAGvB;CACD","sourcesContent":["import { execSync } from \"child_process\";\nimport { cpus, totalmem } from \"os\";\nimport { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from \"./base-worker.js\";\n\ninterface GPUDevice {\n\tindex: number;\n\tname: string;\n\tmemoryTotalMb: number;\n\tmemoryFreeMb: number;\n\tutilization: number;\n}\n\nexport class GPUWorker extends BaseWorker {\n\treadonly id: string;\n\treadonly type = \"gpu\";\n\treadonly capabilities: WorkerCapabilities;\n\tprivate devices: GPUDevice[] = [];\n\n\tconstructor(id?: string) {\n\t\tsuper();\n\t\tthis.id = id || `gpu_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;\n\t\tthis.devices = this.detectGPUs();\n\t\tthis.capabilities = this.buildCapabilities();\n\t}\n\n\tprivate detectGPUs(): GPUDevice[] {\n\t\tconst devices: GPUDevice[] = [];\n\n\t\tif (process.platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst output = execSync(\n\t\t\t\t\t\"nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits\",\n\t\t\t\t\t{ encoding: \"utf-8\", timeout: 5000 },\n\t\t\t\t);\n\n\t\t\t\tconst lines = output.trim().split(\"\\n\");\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst [index, name, memoryTotal, memoryFree, utilization] = line.split(\",\").map((s) => s.trim());\n\t\t\t\t\tdevices.push({\n\t\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tmemoryTotalMb: parseInt(memoryTotal, 10),\n\t\t\t\t\t\tmemoryFreeMb: parseInt(memoryFree, 10),\n\t\t\t\t\t\tutilization: parseInt(utilization, 10),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst output = execSync(\n\t\t\t\t\t\"nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits\",\n\t\t\t\t\t{ encoding: \"utf-8\", timeout: 5000 },\n\t\t\t\t);\n\n\t\t\t\tconst lines = output.trim().split(\"\\n\");\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst [index, name, memoryTotal, memoryFree, utilization] = line.split(\",\").map((s) => s.trim());\n\t\t\t\t\tdevices.push({\n\t\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tmemoryTotalMb: parseInt(memoryTotal, 10),\n\t\t\t\t\t\tmemoryFreeMb: parseInt(memoryFree, 10),\n\t\t\t\t\t\tutilization: parseInt(utilization, 10),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn devices;\n\t}\n\n\tprivate buildCapabilities(): WorkerCapabilities {\n\t\tconst hasGpu = this.devices.length > 0;\n\t\tconst totalGpuMemory = this.devices.reduce((sum, d) => sum + d.memoryTotalMb, 0);\n\n\t\treturn {\n\t\t\tcpuCores: cpus().length,\n\t\t\tmemoryMb: Math.floor(totalmem() / (1024 * 1024)),\n\t\t\thasGpu,\n\t\t\tgpuMemoryMb: totalGpuMemory > 0 ? totalGpuMemory : undefined,\n\t\t\tgpuName: this.devices[0]?.name,\n\t\t\tsupportsTypes: hasGpu ? [\"render\", \"cpu_intensive\", \"code\", \"script\"] : [\"cpu_intensive\", \"code\", \"script\"],\n\t\t};\n\t}\n\n\tasync execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tthis.markBusy(task.id);\n\t\tconst startTime = Date.now();\n\n\t\ttry {\n\t\t\tif (!this.capabilities.hasGpu) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No GPU available\",\n\t\t\t\t\terrorCode: \"NO_GPU\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await this.executeGPUTask(task, signal);\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, result.success);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, false);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"GPU_EXECUTION_ERROR\",\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.markIdle();\n\t\t}\n\t}\n\n\tprivate async executeGPUTask(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tconst device = this.selectBestDevice();\n\t\tif (!device) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"No suitable GPU device available\",\n\t\t\t\terrorCode: \"NO_GPU_DEVICE\",\n\t\t\t};\n\t\t}\n\n\t\tconst env: Record<string, string> = {\n\t\t\t...task.payload.env,\n\t\t\tCUDA_VISIBLE_DEVICES: device.index.toString(),\n\t\t};\n\n\t\tif (task.payload.code) {\n\t\t\treturn this.executeGPUCode(task, env, signal);\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: \"GPU task requires code payload\",\n\t\t\terrorCode: \"INVALID_PAYLOAD\",\n\t\t};\n\t}\n\n\tprivate selectBestDevice(): GPUDevice | null {\n\t\tif (this.devices.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.devices = this.detectGPUs();\n\n\t\tconst sorted = [...this.devices].sort((a, b) => {\n\t\t\tconst freeDiff = b.memoryFreeMb - a.memoryFreeMb;\n\t\t\tif (freeDiff !== 0) return freeDiff;\n\t\t\treturn a.utilization - b.utilization;\n\t\t});\n\n\t\treturn sorted[0];\n\t}\n\n\tprivate async executeGPUCode(\n\t\ttask: ScheduledTask,\n\t\tenv: Record<string, string>,\n\t\tsignal?: AbortSignal,\n\t): Promise<TaskResult> {\n\t\tconst { spawn } = await import(\"child_process\");\n\n\t\treturn new Promise((resolve) => {\n\t\t\tconst stdout: Buffer[] = [];\n\t\t\tconst stderr: Buffer[] = [];\n\n\t\t\tconst child = spawn(\"python3\", [\"-c\", task.payload.code || \"\"], {\n\t\t\t\tenv: { ...process.env, ...env },\n\t\t\t\tcwd: task.payload.workingDir,\n\t\t\t});\n\n\t\t\tconst timeout = task.requirements?.maxExecutionTimeMs || 600000;\n\t\t\tconst timeoutId = setTimeout(() => child.kill(\"SIGTERM\"), timeout);\n\n\t\t\tsignal?.addEventListener(\"abort\", () => child.kill(\"SIGTERM\"));\n\n\t\t\tchild.stdout?.on(\"data\", (data: Buffer) => stdout.push(data));\n\t\t\tchild.stderr?.on(\"data\", (data: Buffer) => stderr.push(data));\n\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: code === 0,\n\t\t\t\t\texitCode: code || 0,\n\t\t\t\t\tstdout: Buffer.concat(stdout).toString(),\n\t\t\t\t\tstderr: Buffer.concat(stderr).toString(),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\terrorCode: \"GPU_CODE_ERROR\",\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\trefreshGPUStatus(): void {\n\t\tthis.devices = this.detectGPUs();\n\t\tthis.capabilities.hasGpu = this.devices.length > 0;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { cpus, totalmem } from "os";
|
|
3
|
+
import { BaseWorker } from "./base-worker.js";
|
|
4
|
+
export class GPUWorker extends BaseWorker {
|
|
5
|
+
id;
|
|
6
|
+
type = "gpu";
|
|
7
|
+
capabilities;
|
|
8
|
+
devices = [];
|
|
9
|
+
constructor(id) {
|
|
10
|
+
super();
|
|
11
|
+
this.id = id || `gpu_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
12
|
+
this.devices = this.detectGPUs();
|
|
13
|
+
this.capabilities = this.buildCapabilities();
|
|
14
|
+
}
|
|
15
|
+
detectGPUs() {
|
|
16
|
+
const devices = [];
|
|
17
|
+
if (process.platform === "win32") {
|
|
18
|
+
try {
|
|
19
|
+
const output = execSync("nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits", { encoding: "utf-8", timeout: 5000 });
|
|
20
|
+
const lines = output.trim().split("\n");
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
const [index, name, memoryTotal, memoryFree, utilization] = line.split(",").map((s) => s.trim());
|
|
23
|
+
devices.push({
|
|
24
|
+
index: parseInt(index, 10),
|
|
25
|
+
name,
|
|
26
|
+
memoryTotalMb: parseInt(memoryTotal, 10),
|
|
27
|
+
memoryFreeMb: parseInt(memoryFree, 10),
|
|
28
|
+
utilization: parseInt(utilization, 10),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
try {
|
|
36
|
+
const output = execSync("nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits", { encoding: "utf-8", timeout: 5000 });
|
|
37
|
+
const lines = output.trim().split("\n");
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const [index, name, memoryTotal, memoryFree, utilization] = line.split(",").map((s) => s.trim());
|
|
40
|
+
devices.push({
|
|
41
|
+
index: parseInt(index, 10),
|
|
42
|
+
name,
|
|
43
|
+
memoryTotalMb: parseInt(memoryTotal, 10),
|
|
44
|
+
memoryFreeMb: parseInt(memoryFree, 10),
|
|
45
|
+
utilization: parseInt(utilization, 10),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
}
|
|
51
|
+
return devices;
|
|
52
|
+
}
|
|
53
|
+
buildCapabilities() {
|
|
54
|
+
const hasGpu = this.devices.length > 0;
|
|
55
|
+
const totalGpuMemory = this.devices.reduce((sum, d) => sum + d.memoryTotalMb, 0);
|
|
56
|
+
return {
|
|
57
|
+
cpuCores: cpus().length,
|
|
58
|
+
memoryMb: Math.floor(totalmem() / (1024 * 1024)),
|
|
59
|
+
hasGpu,
|
|
60
|
+
gpuMemoryMb: totalGpuMemory > 0 ? totalGpuMemory : undefined,
|
|
61
|
+
gpuName: this.devices[0]?.name,
|
|
62
|
+
supportsTypes: hasGpu ? ["render", "cpu_intensive", "code", "script"] : ["cpu_intensive", "code", "script"],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async execute(task, signal) {
|
|
66
|
+
this.markBusy(task.id);
|
|
67
|
+
const startTime = Date.now();
|
|
68
|
+
try {
|
|
69
|
+
if (!this.capabilities.hasGpu) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: "No GPU available",
|
|
73
|
+
errorCode: "NO_GPU",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const result = await this.executeGPUTask(task, signal);
|
|
77
|
+
const executionTime = Date.now() - startTime;
|
|
78
|
+
this.updateStats(executionTime, result.success);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const executionTime = Date.now() - startTime;
|
|
83
|
+
this.updateStats(executionTime, false);
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
error: error instanceof Error ? error.message : String(error),
|
|
87
|
+
errorCode: "GPU_EXECUTION_ERROR",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
this.markIdle();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async executeGPUTask(task, signal) {
|
|
95
|
+
const device = this.selectBestDevice();
|
|
96
|
+
if (!device) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: "No suitable GPU device available",
|
|
100
|
+
errorCode: "NO_GPU_DEVICE",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const env = {
|
|
104
|
+
...task.payload.env,
|
|
105
|
+
CUDA_VISIBLE_DEVICES: device.index.toString(),
|
|
106
|
+
};
|
|
107
|
+
if (task.payload.code) {
|
|
108
|
+
return this.executeGPUCode(task, env, signal);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: "GPU task requires code payload",
|
|
113
|
+
errorCode: "INVALID_PAYLOAD",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
selectBestDevice() {
|
|
117
|
+
if (this.devices.length === 0) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
this.devices = this.detectGPUs();
|
|
121
|
+
const sorted = [...this.devices].sort((a, b) => {
|
|
122
|
+
const freeDiff = b.memoryFreeMb - a.memoryFreeMb;
|
|
123
|
+
if (freeDiff !== 0)
|
|
124
|
+
return freeDiff;
|
|
125
|
+
return a.utilization - b.utilization;
|
|
126
|
+
});
|
|
127
|
+
return sorted[0];
|
|
128
|
+
}
|
|
129
|
+
async executeGPUCode(task, env, signal) {
|
|
130
|
+
const { spawn } = await import("child_process");
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
const stdout = [];
|
|
133
|
+
const stderr = [];
|
|
134
|
+
const child = spawn("python3", ["-c", task.payload.code || ""], {
|
|
135
|
+
env: { ...process.env, ...env },
|
|
136
|
+
cwd: task.payload.workingDir,
|
|
137
|
+
});
|
|
138
|
+
const timeout = task.requirements?.maxExecutionTimeMs || 600000;
|
|
139
|
+
const timeoutId = setTimeout(() => child.kill("SIGTERM"), timeout);
|
|
140
|
+
signal?.addEventListener("abort", () => child.kill("SIGTERM"));
|
|
141
|
+
child.stdout?.on("data", (data) => stdout.push(data));
|
|
142
|
+
child.stderr?.on("data", (data) => stderr.push(data));
|
|
143
|
+
child.on("close", (code) => {
|
|
144
|
+
clearTimeout(timeoutId);
|
|
145
|
+
resolve({
|
|
146
|
+
success: code === 0,
|
|
147
|
+
exitCode: code || 0,
|
|
148
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
149
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
child.on("error", (error) => {
|
|
153
|
+
clearTimeout(timeoutId);
|
|
154
|
+
resolve({
|
|
155
|
+
success: false,
|
|
156
|
+
error: error.message,
|
|
157
|
+
errorCode: "GPU_CODE_ERROR",
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
refreshGPUStatus() {
|
|
163
|
+
this.devices = this.detectGPUs();
|
|
164
|
+
this.capabilities.hasGpu = this.devices.length > 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=gpu-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-worker.js","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/gpu-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync } from \"child_process\";\nimport { cpus, totalmem } from \"os\";\nimport { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from \"./base-worker.js\";\n\ninterface GPUDevice {\n\tindex: number;\n\tname: string;\n\tmemoryTotalMb: number;\n\tmemoryFreeMb: number;\n\tutilization: number;\n}\n\nexport class GPUWorker extends BaseWorker {\n\treadonly id: string;\n\treadonly type = \"gpu\";\n\treadonly capabilities: WorkerCapabilities;\n\tprivate devices: GPUDevice[] = [];\n\n\tconstructor(id?: string) {\n\t\tsuper();\n\t\tthis.id = id || `gpu_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;\n\t\tthis.devices = this.detectGPUs();\n\t\tthis.capabilities = this.buildCapabilities();\n\t}\n\n\tprivate detectGPUs(): GPUDevice[] {\n\t\tconst devices: GPUDevice[] = [];\n\n\t\tif (process.platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst output = execSync(\n\t\t\t\t\t\"nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits\",\n\t\t\t\t\t{ encoding: \"utf-8\", timeout: 5000 },\n\t\t\t\t);\n\n\t\t\t\tconst lines = output.trim().split(\"\\n\");\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst [index, name, memoryTotal, memoryFree, utilization] = line.split(\",\").map((s) => s.trim());\n\t\t\t\t\tdevices.push({\n\t\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tmemoryTotalMb: parseInt(memoryTotal, 10),\n\t\t\t\t\t\tmemoryFreeMb: parseInt(memoryFree, 10),\n\t\t\t\t\t\tutilization: parseInt(utilization, 10),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst output = execSync(\n\t\t\t\t\t\"nvidia-smi --query-gpu=index,name,memory.total,memory.free,utilization.gpu --format=csv,noheader,nounits\",\n\t\t\t\t\t{ encoding: \"utf-8\", timeout: 5000 },\n\t\t\t\t);\n\n\t\t\t\tconst lines = output.trim().split(\"\\n\");\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst [index, name, memoryTotal, memoryFree, utilization] = line.split(\",\").map((s) => s.trim());\n\t\t\t\t\tdevices.push({\n\t\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tmemoryTotalMb: parseInt(memoryTotal, 10),\n\t\t\t\t\t\tmemoryFreeMb: parseInt(memoryFree, 10),\n\t\t\t\t\t\tutilization: parseInt(utilization, 10),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn devices;\n\t}\n\n\tprivate buildCapabilities(): WorkerCapabilities {\n\t\tconst hasGpu = this.devices.length > 0;\n\t\tconst totalGpuMemory = this.devices.reduce((sum, d) => sum + d.memoryTotalMb, 0);\n\n\t\treturn {\n\t\t\tcpuCores: cpus().length,\n\t\t\tmemoryMb: Math.floor(totalmem() / (1024 * 1024)),\n\t\t\thasGpu,\n\t\t\tgpuMemoryMb: totalGpuMemory > 0 ? totalGpuMemory : undefined,\n\t\t\tgpuName: this.devices[0]?.name,\n\t\t\tsupportsTypes: hasGpu ? [\"render\", \"cpu_intensive\", \"code\", \"script\"] : [\"cpu_intensive\", \"code\", \"script\"],\n\t\t};\n\t}\n\n\tasync execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tthis.markBusy(task.id);\n\t\tconst startTime = Date.now();\n\n\t\ttry {\n\t\t\tif (!this.capabilities.hasGpu) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No GPU available\",\n\t\t\t\t\terrorCode: \"NO_GPU\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await this.executeGPUTask(task, signal);\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, result.success);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, false);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"GPU_EXECUTION_ERROR\",\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.markIdle();\n\t\t}\n\t}\n\n\tprivate async executeGPUTask(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tconst device = this.selectBestDevice();\n\t\tif (!device) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"No suitable GPU device available\",\n\t\t\t\terrorCode: \"NO_GPU_DEVICE\",\n\t\t\t};\n\t\t}\n\n\t\tconst env: Record<string, string> = {\n\t\t\t...task.payload.env,\n\t\t\tCUDA_VISIBLE_DEVICES: device.index.toString(),\n\t\t};\n\n\t\tif (task.payload.code) {\n\t\t\treturn this.executeGPUCode(task, env, signal);\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: \"GPU task requires code payload\",\n\t\t\terrorCode: \"INVALID_PAYLOAD\",\n\t\t};\n\t}\n\n\tprivate selectBestDevice(): GPUDevice | null {\n\t\tif (this.devices.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.devices = this.detectGPUs();\n\n\t\tconst sorted = [...this.devices].sort((a, b) => {\n\t\t\tconst freeDiff = b.memoryFreeMb - a.memoryFreeMb;\n\t\t\tif (freeDiff !== 0) return freeDiff;\n\t\t\treturn a.utilization - b.utilization;\n\t\t});\n\n\t\treturn sorted[0];\n\t}\n\n\tprivate async executeGPUCode(\n\t\ttask: ScheduledTask,\n\t\tenv: Record<string, string>,\n\t\tsignal?: AbortSignal,\n\t): Promise<TaskResult> {\n\t\tconst { spawn } = await import(\"child_process\");\n\n\t\treturn new Promise((resolve) => {\n\t\t\tconst stdout: Buffer[] = [];\n\t\t\tconst stderr: Buffer[] = [];\n\n\t\t\tconst child = spawn(\"python3\", [\"-c\", task.payload.code || \"\"], {\n\t\t\t\tenv: { ...process.env, ...env },\n\t\t\t\tcwd: task.payload.workingDir,\n\t\t\t});\n\n\t\t\tconst timeout = task.requirements?.maxExecutionTimeMs || 600000;\n\t\t\tconst timeoutId = setTimeout(() => child.kill(\"SIGTERM\"), timeout);\n\n\t\t\tsignal?.addEventListener(\"abort\", () => child.kill(\"SIGTERM\"));\n\n\t\t\tchild.stdout?.on(\"data\", (data: Buffer) => stdout.push(data));\n\t\t\tchild.stderr?.on(\"data\", (data: Buffer) => stderr.push(data));\n\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: code === 0,\n\t\t\t\t\texitCode: code || 0,\n\t\t\t\t\tstdout: Buffer.concat(stdout).toString(),\n\t\t\t\t\tstderr: Buffer.concat(stderr).toString(),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\terrorCode: \"GPU_CODE_ERROR\",\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\trefreshGPUStatus(): void {\n\t\tthis.devices = this.detectGPUs();\n\t\tthis.capabilities.hasGpu = this.devices.length > 0;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BaseWorker, type ScheduledTask, type TaskPayload, type TaskPriority, type TaskRequirements, type TaskResult, type TaskStatus, type TaskType, type WorkerCapabilities, type WorkerInfo, type WorkerStats, } from "./base-worker.js";
|
|
2
|
+
export { CPUWorker } from "./cpu-worker.js";
|
|
3
|
+
export { GPUWorker } from "./gpu-worker.js";
|
|
4
|
+
export { IOWorker } from "./io-worker.js";
|
|
5
|
+
export { NetworkWorker } from "./network-worker.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,WAAW,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export {\n\tBaseWorker,\n\ttype ScheduledTask,\n\ttype TaskPayload,\n\ttype TaskPriority,\n\ttype TaskRequirements,\n\ttype TaskResult,\n\ttype TaskStatus,\n\ttype TaskType,\n\ttype WorkerCapabilities,\n\ttype WorkerInfo,\n\ttype WorkerStats,\n} from \"./base-worker.js\";\nexport { CPUWorker } from \"./cpu-worker.js\";\nexport { GPUWorker } from \"./gpu-worker.js\";\nexport { IOWorker } from \"./io-worker.js\";\nexport { NetworkWorker } from \"./network-worker.js\";\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BaseWorker, } from "./base-worker.js";
|
|
2
|
+
export { CPUWorker } from "./cpu-worker.js";
|
|
3
|
+
export { GPUWorker } from "./gpu-worker.js";
|
|
4
|
+
export { IOWorker } from "./io-worker.js";
|
|
5
|
+
export { NetworkWorker } from "./network-worker.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,GAWV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export {\n\tBaseWorker,\n\ttype ScheduledTask,\n\ttype TaskPayload,\n\ttype TaskPriority,\n\ttype TaskRequirements,\n\ttype TaskResult,\n\ttype TaskStatus,\n\ttype TaskType,\n\ttype WorkerCapabilities,\n\ttype WorkerInfo,\n\ttype WorkerStats,\n} from \"./base-worker.js\";\nexport { CPUWorker } from \"./cpu-worker.js\";\nexport { GPUWorker } from \"./gpu-worker.js\";\nexport { IOWorker } from \"./io-worker.js\";\nexport { NetworkWorker } from \"./network-worker.js\";\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from "./base-worker.js";
|
|
2
|
+
export declare class IOWorker extends BaseWorker {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly type = "io";
|
|
5
|
+
readonly capabilities: WorkerCapabilities;
|
|
6
|
+
constructor(id?: string);
|
|
7
|
+
execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult>;
|
|
8
|
+
private executeIOTask;
|
|
9
|
+
private handleRead;
|
|
10
|
+
private handleWrite;
|
|
11
|
+
private handleList;
|
|
12
|
+
private listDirectory;
|
|
13
|
+
private handleMkdir;
|
|
14
|
+
private handleDelete;
|
|
15
|
+
private handleCopy;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=io-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io-worker.d.ts","sourceRoot":"","sources":["../../../../../src/core/companion/scheduler/workers/io-worker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE5G,qBAAa,QAAS,SAAQ,UAAU;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,QAAQ;IACrB,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAE1C,YAAY,EAAE,CAAC,EAAE,MAAM,EAStB;IAEK,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAoB5E;YAEa,aAAa;YA0Bb,UAAU;YAkBV,WAAW;YAqBX,UAAU;YAgBV,aAAa;YA+Bb,WAAW;YAkBX,YAAY;YAoBZ,UAAU;CAqBxB","sourcesContent":["import { mkdir, readdir, readFile, rm, stat, writeFile } from \"fs/promises\";\nimport { cpus, totalmem } from \"os\";\nimport { dirname, join } from \"path\";\nimport { BaseWorker, type ScheduledTask, type TaskResult, type WorkerCapabilities } from \"./base-worker.js\";\n\nexport class IOWorker extends BaseWorker {\n\treadonly id: string;\n\treadonly type = \"io\";\n\treadonly capabilities: WorkerCapabilities;\n\n\tconstructor(id?: string) {\n\t\tsuper();\n\t\tthis.id = id || `io_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;\n\t\tthis.capabilities = {\n\t\t\tcpuCores: cpus().length,\n\t\t\tmemoryMb: Math.floor(totalmem() / (1024 * 1024)),\n\t\t\thasGpu: false,\n\t\t\tsupportsTypes: [\"io_heavy\", \"script\"],\n\t\t};\n\t}\n\n\tasync execute(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tthis.markBusy(task.id);\n\t\tconst startTime = Date.now();\n\n\t\ttry {\n\t\t\tconst result = await this.executeIOTask(task, signal);\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, result.success);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst executionTime = Date.now() - startTime;\n\t\t\tthis.updateStats(executionTime, false);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"IO_ERROR\",\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.markIdle();\n\t\t}\n\t}\n\n\tprivate async executeIOTask(task: ScheduledTask, signal?: AbortSignal): Promise<TaskResult> {\n\t\tconst payload = task.payload as Record<string, unknown>;\n\t\tconst operation = (payload.data as { operation?: string })?.operation;\n\n\t\tif (signal?.aborted) {\n\t\t\treturn { success: false, error: \"Task cancelled\", errorCode: \"CANCELLED\" };\n\t\t}\n\n\t\tswitch (operation) {\n\t\t\tcase \"read\":\n\t\t\t\treturn this.handleRead(payload);\n\t\t\tcase \"write\":\n\t\t\t\treturn this.handleWrite(payload);\n\t\t\tcase \"list\":\n\t\t\t\treturn this.handleList(payload);\n\t\t\tcase \"mkdir\":\n\t\t\t\treturn this.handleMkdir(payload);\n\t\t\tcase \"delete\":\n\t\t\t\treturn this.handleDelete(payload);\n\t\t\tcase \"copy\":\n\t\t\t\treturn this.handleCopy(payload);\n\t\t\tdefault:\n\t\t\t\treturn { success: false, error: `Unknown IO operation: ${operation}`, errorCode: \"UNKNOWN_OPERATION\" };\n\t\t}\n\t}\n\n\tprivate async handleRead(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst path = payload.path as string;\n\t\tif (!path) {\n\t\t\treturn { success: false, error: \"Path is required\", errorCode: \"MISSING_PATH\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = await readFile(path, \"utf-8\");\n\t\t\treturn { success: true, output: content };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"READ_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async handleWrite(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst path = payload.path as string;\n\t\tconst content = payload.content as string;\n\n\t\tif (!path || content === undefined) {\n\t\t\treturn { success: false, error: \"Path and content are required\", errorCode: \"MISSING_PARAMS\" };\n\t\t}\n\n\t\ttry {\n\t\t\tawait mkdir(dirname(path), { recursive: true });\n\t\t\tawait writeFile(path, content, \"utf-8\");\n\t\t\treturn { success: true, output: `Written to ${path}` };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"WRITE_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async handleList(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst path = (payload.path as string) || \".\";\n\t\tconst recursive = payload.recursive as boolean;\n\n\t\ttry {\n\t\t\tconst entries = await this.listDirectory(path, recursive);\n\t\t\treturn { success: true, output: JSON.stringify(entries, null, 2) };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"LIST_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async listDirectory(dir: string, recursive: boolean): Promise<unknown[]> {\n\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\tconst result: unknown[] = [];\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tconst info: Record<string, unknown> = {\n\t\t\t\tname: entry.name,\n\t\t\t\tpath: fullPath,\n\t\t\t\ttype: entry.isDirectory() ? \"directory\" : \"file\",\n\t\t\t};\n\n\t\t\tif (entry.isFile()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = await stat(fullPath);\n\t\t\t\t\tinfo.size = stats.size;\n\t\t\t\t\tinfo.modified = stats.mtime;\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\tresult.push(info);\n\n\t\t\tif (recursive && entry.isDirectory()) {\n\t\t\t\tconst children = await this.listDirectory(fullPath, true);\n\t\t\t\t(info as Record<string, unknown>).children = children;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate async handleMkdir(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst path = payload.path as string;\n\t\tif (!path) {\n\t\t\treturn { success: false, error: \"Path is required\", errorCode: \"MISSING_PATH\" };\n\t\t}\n\n\t\ttry {\n\t\t\tawait mkdir(path, { recursive: true });\n\t\t\treturn { success: true, output: `Created directory ${path}` };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"MKDIR_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async handleDelete(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst path = payload.path as string;\n\t\tconst recursive = payload.recursive as boolean;\n\n\t\tif (!path) {\n\t\t\treturn { success: false, error: \"Path is required\", errorCode: \"MISSING_PATH\" };\n\t\t}\n\n\t\ttry {\n\t\t\tawait rm(path, { recursive, force: true });\n\t\t\treturn { success: true, output: `Deleted ${path}` };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"DELETE_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async handleCopy(payload: Record<string, unknown>): Promise<TaskResult> {\n\t\tconst src = payload.source as string;\n\t\tconst dest = payload.destination as string;\n\n\t\tif (!src || !dest) {\n\t\t\treturn { success: false, error: \"Source and destination are required\", errorCode: \"MISSING_PARAMS\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = await readFile(src);\n\t\t\tawait mkdir(dirname(dest), { recursive: true });\n\t\t\tawait writeFile(dest, content);\n\t\t\treturn { success: true, output: `Copied ${src} to ${dest}` };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\terrorCode: \"COPY_ERROR\",\n\t\t\t};\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
2
|
+
import { cpus, totalmem } from "os";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { BaseWorker } from "./base-worker.js";
|
|
5
|
+
export class IOWorker extends BaseWorker {
|
|
6
|
+
id;
|
|
7
|
+
type = "io";
|
|
8
|
+
capabilities;
|
|
9
|
+
constructor(id) {
|
|
10
|
+
super();
|
|
11
|
+
this.id = id || `io_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
12
|
+
this.capabilities = {
|
|
13
|
+
cpuCores: cpus().length,
|
|
14
|
+
memoryMb: Math.floor(totalmem() / (1024 * 1024)),
|
|
15
|
+
hasGpu: false,
|
|
16
|
+
supportsTypes: ["io_heavy", "script"],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async execute(task, signal) {
|
|
20
|
+
this.markBusy(task.id);
|
|
21
|
+
const startTime = Date.now();
|
|
22
|
+
try {
|
|
23
|
+
const result = await this.executeIOTask(task, signal);
|
|
24
|
+
const executionTime = Date.now() - startTime;
|
|
25
|
+
this.updateStats(executionTime, result.success);
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const executionTime = Date.now() - startTime;
|
|
30
|
+
this.updateStats(executionTime, false);
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: error instanceof Error ? error.message : String(error),
|
|
34
|
+
errorCode: "IO_ERROR",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
this.markIdle();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async executeIOTask(task, signal) {
|
|
42
|
+
const payload = task.payload;
|
|
43
|
+
const operation = payload.data?.operation;
|
|
44
|
+
if (signal?.aborted) {
|
|
45
|
+
return { success: false, error: "Task cancelled", errorCode: "CANCELLED" };
|
|
46
|
+
}
|
|
47
|
+
switch (operation) {
|
|
48
|
+
case "read":
|
|
49
|
+
return this.handleRead(payload);
|
|
50
|
+
case "write":
|
|
51
|
+
return this.handleWrite(payload);
|
|
52
|
+
case "list":
|
|
53
|
+
return this.handleList(payload);
|
|
54
|
+
case "mkdir":
|
|
55
|
+
return this.handleMkdir(payload);
|
|
56
|
+
case "delete":
|
|
57
|
+
return this.handleDelete(payload);
|
|
58
|
+
case "copy":
|
|
59
|
+
return this.handleCopy(payload);
|
|
60
|
+
default:
|
|
61
|
+
return { success: false, error: `Unknown IO operation: ${operation}`, errorCode: "UNKNOWN_OPERATION" };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async handleRead(payload) {
|
|
65
|
+
const path = payload.path;
|
|
66
|
+
if (!path) {
|
|
67
|
+
return { success: false, error: "Path is required", errorCode: "MISSING_PATH" };
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const content = await readFile(path, "utf-8");
|
|
71
|
+
return { success: true, output: content };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: error instanceof Error ? error.message : String(error),
|
|
77
|
+
errorCode: "READ_ERROR",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async handleWrite(payload) {
|
|
82
|
+
const path = payload.path;
|
|
83
|
+
const content = payload.content;
|
|
84
|
+
if (!path || content === undefined) {
|
|
85
|
+
return { success: false, error: "Path and content are required", errorCode: "MISSING_PARAMS" };
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
await mkdir(dirname(path), { recursive: true });
|
|
89
|
+
await writeFile(path, content, "utf-8");
|
|
90
|
+
return { success: true, output: `Written to ${path}` };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
error: error instanceof Error ? error.message : String(error),
|
|
96
|
+
errorCode: "WRITE_ERROR",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async handleList(payload) {
|
|
101
|
+
const path = payload.path || ".";
|
|
102
|
+
const recursive = payload.recursive;
|
|
103
|
+
try {
|
|
104
|
+
const entries = await this.listDirectory(path, recursive);
|
|
105
|
+
return { success: true, output: JSON.stringify(entries, null, 2) };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
errorCode: "LIST_ERROR",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async listDirectory(dir, recursive) {
|
|
116
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
117
|
+
const result = [];
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
const fullPath = join(dir, entry.name);
|
|
120
|
+
const info = {
|
|
121
|
+
name: entry.name,
|
|
122
|
+
path: fullPath,
|
|
123
|
+
type: entry.isDirectory() ? "directory" : "file",
|
|
124
|
+
};
|
|
125
|
+
if (entry.isFile()) {
|
|
126
|
+
try {
|
|
127
|
+
const stats = await stat(fullPath);
|
|
128
|
+
info.size = stats.size;
|
|
129
|
+
info.modified = stats.mtime;
|
|
130
|
+
}
|
|
131
|
+
catch { }
|
|
132
|
+
}
|
|
133
|
+
result.push(info);
|
|
134
|
+
if (recursive && entry.isDirectory()) {
|
|
135
|
+
const children = await this.listDirectory(fullPath, true);
|
|
136
|
+
info.children = children;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
async handleMkdir(payload) {
|
|
142
|
+
const path = payload.path;
|
|
143
|
+
if (!path) {
|
|
144
|
+
return { success: false, error: "Path is required", errorCode: "MISSING_PATH" };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
await mkdir(path, { recursive: true });
|
|
148
|
+
return { success: true, output: `Created directory ${path}` };
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: error instanceof Error ? error.message : String(error),
|
|
154
|
+
errorCode: "MKDIR_ERROR",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async handleDelete(payload) {
|
|
159
|
+
const path = payload.path;
|
|
160
|
+
const recursive = payload.recursive;
|
|
161
|
+
if (!path) {
|
|
162
|
+
return { success: false, error: "Path is required", errorCode: "MISSING_PATH" };
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await rm(path, { recursive, force: true });
|
|
166
|
+
return { success: true, output: `Deleted ${path}` };
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
success: false,
|
|
171
|
+
error: error instanceof Error ? error.message : String(error),
|
|
172
|
+
errorCode: "DELETE_ERROR",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async handleCopy(payload) {
|
|
177
|
+
const src = payload.source;
|
|
178
|
+
const dest = payload.destination;
|
|
179
|
+
if (!src || !dest) {
|
|
180
|
+
return { success: false, error: "Source and destination are required", errorCode: "MISSING_PARAMS" };
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const content = await readFile(src);
|
|
184
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
185
|
+
await writeFile(dest, content);
|
|
186
|
+
return { success: true, output: `Copied ${src} to ${dest}` };
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
error: error instanceof Error ? error.message : String(error),
|
|
192
|
+
errorCode: "COPY_ERROR",
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=io-worker.js.map
|