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,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Executor for OpenVibe Companion
|
|
3
|
+
*/
|
|
4
|
+
import type { SecurityManager } from "../security/security-manager.js";
|
|
5
|
+
export interface BashTask {
|
|
6
|
+
id?: string;
|
|
7
|
+
command: string;
|
|
8
|
+
args?: string[];
|
|
9
|
+
workingDir?: string;
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
onOutput?: (data: Buffer, isStderr: boolean) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface BashResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
exitCode?: number;
|
|
17
|
+
stdout: string;
|
|
18
|
+
stderr: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
errorCode?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class BashExecutor {
|
|
23
|
+
private security;
|
|
24
|
+
private activeProcesses;
|
|
25
|
+
constructor(security: SecurityManager);
|
|
26
|
+
execute(task: BashTask): Promise<BashResult>;
|
|
27
|
+
cancel(taskId: string): boolean;
|
|
28
|
+
shutdown(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=bash-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../../../src/core/companion/executor/bash-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,WAAW,QAAQ;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,eAAe,CAAmC;IAE1D,YAAY,QAAQ,EAAE,eAAe,EAEpC;IAEK,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAqFjD;IAED,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQ9B;IAED,QAAQ,IAAI,IAAI,CAOf;CACD","sourcesContent":["/**\n * Bash Executor for OpenVibe Companion\n */\n\nimport { type ChildProcess, spawn } from \"child_process\";\nimport type { SecurityManager } from \"../security/security-manager.js\";\n\nexport interface BashTask {\n\tid?: string;\n\tcommand: string;\n\targs?: string[];\n\tworkingDir?: string;\n\tenv?: Record<string, string>;\n\ttimeout?: number;\n\tonOutput?: (data: Buffer, isStderr: boolean) => void;\n}\n\nexport interface BashResult {\n\tsuccess: boolean;\n\texitCode?: number;\n\tstdout: string;\n\tstderr: string;\n\terror?: string;\n\terrorCode?: string;\n}\n\nexport class BashExecutor {\n\tprivate security: SecurityManager;\n\tprivate activeProcesses = new Map<string, ChildProcess>();\n\n\tconstructor(security: SecurityManager) {\n\t\tthis.security = security;\n\t}\n\n\tasync execute(task: BashTask): Promise<BashResult> {\n\t\t// 安全检查\n\t\tif (!this.security.isCommandAllowed(task.command)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: \"\",\n\t\t\t\terror: `Command \"${task.command}\" is not allowed`,\n\t\t\t\terrorCode: \"COMMAND_NOT_ALLOWED\",\n\t\t\t};\n\t\t}\n\n\t\tif (task.workingDir && !this.security.isPathAllowed(task.workingDir)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: \"\",\n\t\t\t\terror: `Working directory \"${task.workingDir}\" is not allowed`,\n\t\t\t\terrorCode: \"PATH_NOT_ALLOWED\",\n\t\t\t};\n\t\t}\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(task.command, task.args || [], {\n\t\t\t\tcwd: task.workingDir,\n\t\t\t\tenv: { ...process.env, ...task.env },\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tif (task.id) {\n\t\t\t\tthis.activeProcesses.set(task.id, child);\n\t\t\t}\n\n\t\t\tlet timeoutId: NodeJS.Timeout;\n\t\t\tconst timeout = (task.timeout || 300) * 1000;\n\n\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t}\n\t\t\t\t}, 5000);\n\t\t\t}, timeout);\n\n\t\t\tchild.stdout?.on(\"data\", (data: Buffer) => {\n\t\t\t\tstdout.push(data);\n\t\t\t\ttask.onOutput?.(data, false);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (data: Buffer) => {\n\t\t\t\tstderr.push(data);\n\t\t\t\ttask.onOutput?.(data, true);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tif (task.id) {\n\t\t\t\t\tthis.activeProcesses.delete(task.id);\n\t\t\t\t}\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\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tif (task.id) {\n\t\t\t\t\tthis.activeProcesses.delete(task.id);\n\t\t\t\t}\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\t\t});\n\t}\n\n\tcancel(taskId: string): boolean {\n\t\tconst child = this.activeProcesses.get(taskId);\n\t\tif (child && !child.killed) {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tthis.activeProcesses.delete(taskId);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tshutdown(): void {\n\t\tfor (const [_taskId, child] of this.activeProcesses) {\n\t\t\tif (!child.killed) {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t}\n\t\t}\n\t\tthis.activeProcesses.clear();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Executor for OpenVibe Companion
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
export class BashExecutor {
|
|
6
|
+
security;
|
|
7
|
+
activeProcesses = new Map();
|
|
8
|
+
constructor(security) {
|
|
9
|
+
this.security = security;
|
|
10
|
+
}
|
|
11
|
+
async execute(task) {
|
|
12
|
+
// 安全检查
|
|
13
|
+
if (!this.security.isCommandAllowed(task.command)) {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
stdout: "",
|
|
17
|
+
stderr: "",
|
|
18
|
+
error: `Command "${task.command}" is not allowed`,
|
|
19
|
+
errorCode: "COMMAND_NOT_ALLOWED",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (task.workingDir && !this.security.isPathAllowed(task.workingDir)) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
stdout: "",
|
|
26
|
+
stderr: "",
|
|
27
|
+
error: `Working directory "${task.workingDir}" is not allowed`,
|
|
28
|
+
errorCode: "PATH_NOT_ALLOWED",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const stdout = [];
|
|
33
|
+
const stderr = [];
|
|
34
|
+
const child = spawn(task.command, task.args || [], {
|
|
35
|
+
cwd: task.workingDir,
|
|
36
|
+
env: { ...process.env, ...task.env },
|
|
37
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
38
|
+
});
|
|
39
|
+
if (task.id) {
|
|
40
|
+
this.activeProcesses.set(task.id, child);
|
|
41
|
+
}
|
|
42
|
+
let timeoutId;
|
|
43
|
+
const timeout = (task.timeout || 300) * 1000;
|
|
44
|
+
timeoutId = setTimeout(() => {
|
|
45
|
+
child.kill("SIGTERM");
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
if (!child.killed) {
|
|
48
|
+
child.kill("SIGKILL");
|
|
49
|
+
}
|
|
50
|
+
}, 5000);
|
|
51
|
+
}, timeout);
|
|
52
|
+
child.stdout?.on("data", (data) => {
|
|
53
|
+
stdout.push(data);
|
|
54
|
+
task.onOutput?.(data, false);
|
|
55
|
+
});
|
|
56
|
+
child.stderr?.on("data", (data) => {
|
|
57
|
+
stderr.push(data);
|
|
58
|
+
task.onOutput?.(data, true);
|
|
59
|
+
});
|
|
60
|
+
child.on("error", (error) => {
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
if (task.id) {
|
|
63
|
+
this.activeProcesses.delete(task.id);
|
|
64
|
+
}
|
|
65
|
+
resolve({
|
|
66
|
+
success: false,
|
|
67
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
68
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
69
|
+
error: error.message,
|
|
70
|
+
errorCode: "SPAWN_ERROR",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
child.on("close", (code) => {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
if (task.id) {
|
|
76
|
+
this.activeProcesses.delete(task.id);
|
|
77
|
+
}
|
|
78
|
+
resolve({
|
|
79
|
+
success: code === 0,
|
|
80
|
+
exitCode: code || 0,
|
|
81
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
82
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
cancel(taskId) {
|
|
88
|
+
const child = this.activeProcesses.get(taskId);
|
|
89
|
+
if (child && !child.killed) {
|
|
90
|
+
child.kill("SIGTERM");
|
|
91
|
+
this.activeProcesses.delete(taskId);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
shutdown() {
|
|
97
|
+
for (const [_taskId, child] of this.activeProcesses) {
|
|
98
|
+
if (!child.killed) {
|
|
99
|
+
child.kill("SIGTERM");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
this.activeProcesses.clear();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=bash-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../../../src/core/companion/executor/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash Executor for OpenVibe Companion\n */\n\nimport { type ChildProcess, spawn } from \"child_process\";\nimport type { SecurityManager } from \"../security/security-manager.js\";\n\nexport interface BashTask {\n\tid?: string;\n\tcommand: string;\n\targs?: string[];\n\tworkingDir?: string;\n\tenv?: Record<string, string>;\n\ttimeout?: number;\n\tonOutput?: (data: Buffer, isStderr: boolean) => void;\n}\n\nexport interface BashResult {\n\tsuccess: boolean;\n\texitCode?: number;\n\tstdout: string;\n\tstderr: string;\n\terror?: string;\n\terrorCode?: string;\n}\n\nexport class BashExecutor {\n\tprivate security: SecurityManager;\n\tprivate activeProcesses = new Map<string, ChildProcess>();\n\n\tconstructor(security: SecurityManager) {\n\t\tthis.security = security;\n\t}\n\n\tasync execute(task: BashTask): Promise<BashResult> {\n\t\t// 安全检查\n\t\tif (!this.security.isCommandAllowed(task.command)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: \"\",\n\t\t\t\terror: `Command \"${task.command}\" is not allowed`,\n\t\t\t\terrorCode: \"COMMAND_NOT_ALLOWED\",\n\t\t\t};\n\t\t}\n\n\t\tif (task.workingDir && !this.security.isPathAllowed(task.workingDir)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: \"\",\n\t\t\t\terror: `Working directory \"${task.workingDir}\" is not allowed`,\n\t\t\t\terrorCode: \"PATH_NOT_ALLOWED\",\n\t\t\t};\n\t\t}\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(task.command, task.args || [], {\n\t\t\t\tcwd: task.workingDir,\n\t\t\t\tenv: { ...process.env, ...task.env },\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tif (task.id) {\n\t\t\t\tthis.activeProcesses.set(task.id, child);\n\t\t\t}\n\n\t\t\tlet timeoutId: NodeJS.Timeout;\n\t\t\tconst timeout = (task.timeout || 300) * 1000;\n\n\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t}\n\t\t\t\t}, 5000);\n\t\t\t}, timeout);\n\n\t\t\tchild.stdout?.on(\"data\", (data: Buffer) => {\n\t\t\t\tstdout.push(data);\n\t\t\t\ttask.onOutput?.(data, false);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (data: Buffer) => {\n\t\t\t\tstderr.push(data);\n\t\t\t\ttask.onOutput?.(data, true);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tif (task.id) {\n\t\t\t\t\tthis.activeProcesses.delete(task.id);\n\t\t\t\t}\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\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tif (task.id) {\n\t\t\t\t\tthis.activeProcesses.delete(task.id);\n\t\t\t\t}\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\t\t});\n\t}\n\n\tcancel(taskId: string): boolean {\n\t\tconst child = this.activeProcesses.get(taskId);\n\t\tif (child && !child.killed) {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tthis.activeProcesses.delete(taskId);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tshutdown(): void {\n\t\tfor (const [_taskId, child] of this.activeProcesses) {\n\t\t\tif (!child.killed) {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t}\n\t\t}\n\t\tthis.activeProcesses.clear();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Executor for OpenVibe Companion
|
|
3
|
+
*
|
|
4
|
+
* 支持 Python, JavaScript, Rust 等语言的代码执行
|
|
5
|
+
*/
|
|
6
|
+
import type { SecurityManager } from "../security/security-manager.js";
|
|
7
|
+
export interface CodeTask {
|
|
8
|
+
id?: string;
|
|
9
|
+
code: string;
|
|
10
|
+
language: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
onOutput?: (data: Buffer, isStderr: boolean) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface CodeResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
output: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
errorCode?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class CodeExecutor {
|
|
21
|
+
private bashExecutor;
|
|
22
|
+
private activeTasks;
|
|
23
|
+
constructor(security: SecurityManager);
|
|
24
|
+
execute(task: CodeTask): Promise<CodeResult>;
|
|
25
|
+
private executePython;
|
|
26
|
+
private executeJavaScript;
|
|
27
|
+
private executeTypeScript;
|
|
28
|
+
private executeRust;
|
|
29
|
+
private createTempDir;
|
|
30
|
+
cancel(taskId: string): boolean;
|
|
31
|
+
shutdown(): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=code-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-executor.d.ts","sourceRoot":"","sources":["../../../../src/core/companion/executor/code-executor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGvE,MAAM,WAAW,QAAQ;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,YAAY;IACxB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAqB;IAExC,YAAY,QAAQ,EAAE,eAAe,EAEpC;IAEK,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAiCjD;YAEa,aAAa;YAoBb,iBAAiB;YAoBjB,iBAAiB;YAqBjB,WAAW;YAgCX,aAAa;IAO3B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG9B;IAED,QAAQ,IAAI,IAAI,CAEf;CACD","sourcesContent":["/**\n * Code Executor for OpenVibe Companion\n *\n * 支持 Python, JavaScript, Rust 等语言的代码执行\n */\n\nimport { mkdir, rm, writeFile } from \"fs/promises\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport type { SecurityManager } from \"../security/security-manager.js\";\nimport { BashExecutor } from \"./bash-executor.js\";\n\nexport interface CodeTask {\n\tid?: string;\n\tcode: string;\n\tlanguage: string;\n\ttimeout?: number;\n\tonOutput?: (data: Buffer, isStderr: boolean) => void;\n}\n\nexport interface CodeResult {\n\tsuccess: boolean;\n\toutput: string;\n\terror?: string;\n\terrorCode?: string;\n}\n\nexport class CodeExecutor {\n\tprivate bashExecutor: BashExecutor;\n\tprivate activeTasks = new Set<string>();\n\n\tconstructor(security: SecurityManager) {\n\t\tthis.bashExecutor = new BashExecutor(security);\n\t}\n\n\tasync execute(task: CodeTask): Promise<CodeResult> {\n\t\tconst tempDir = await this.createTempDir(task.id);\n\n\t\ttry {\n\t\t\tswitch (task.language.toLowerCase()) {\n\t\t\t\tcase \"python\":\n\t\t\t\tcase \"py\":\n\t\t\t\t\treturn await this.executePython(task, tempDir);\n\t\t\t\tcase \"javascript\":\n\t\t\t\tcase \"js\":\n\t\t\t\tcase \"node\":\n\t\t\t\t\treturn await this.executeJavaScript(task, tempDir);\n\t\t\t\tcase \"rust\":\n\t\t\t\tcase \"rs\":\n\t\t\t\t\treturn await this.executeRust(task, tempDir);\n\t\t\t\tcase \"typescript\":\n\t\t\t\tcase \"ts\":\n\t\t\t\t\treturn await this.executeTypeScript(task, tempDir);\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\toutput: \"\",\n\t\t\t\t\t\terror: `Unsupported language: ${task.language}`,\n\t\t\t\t\t\terrorCode: \"UNSUPPORTED_LANGUAGE\",\n\t\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tif (task.id) {\n\t\t\t\tthis.activeTasks.delete(task.id);\n\t\t\t}\n\t\t\t// 清理临时目录\n\t\t\tawait rm(tempDir, { recursive: true, force: true });\n\t\t}\n\t}\n\n\tprivate async executePython(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.py\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"python3\",\n\t\t\targs: [scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeJavaScript(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.js\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"node\",\n\t\t\targs: [scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeTypeScript(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.ts\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\t// 使用 ts-node 或tsx 运行 TypeScript\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"npx\",\n\t\t\targs: [\"tsx\", scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeRust(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst mainPath = join(tempDir, \"main.rs\");\n\t\tawait writeFile(mainPath, task.code);\n\n\t\t// 创建临时 Cargo 项目\n\t\tconst cargoToml = `\n[package]\nname = \"temp\"\nversion = \"0.1.0\"\nedition = \"2021\"\n`;\n\t\tawait writeFile(join(tempDir, \"Cargo.toml\"), cargoToml);\n\t\tawait mkdir(join(tempDir, \"src\"), { recursive: true });\n\t\tawait writeFile(join(tempDir, \"src\", \"main.rs\"), task.code);\n\n\t\t// 编译并运行\n\t\tconst runResult = await this.bashExecutor.execute({\n\t\t\tcommand: \"cargo\",\n\t\t\targs: [\"run\", \"--release\"],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: runResult.success,\n\t\t\toutput: runResult.stdout,\n\t\t\terror: runResult.error || runResult.stderr,\n\t\t\terrorCode: runResult.errorCode,\n\t\t};\n\t}\n\n\tprivate async createTempDir(taskId?: string): Promise<string> {\n\t\tconst dirName = taskId || `companion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\t\tconst tempDir = join(tmpdir(), dirName);\n\t\tawait mkdir(tempDir, { recursive: true });\n\t\treturn tempDir;\n\t}\n\n\tcancel(taskId: string): boolean {\n\t\tthis.activeTasks.delete(taskId);\n\t\treturn this.bashExecutor.cancel(taskId);\n\t}\n\n\tshutdown(): void {\n\t\tthis.bashExecutor.shutdown();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Executor for OpenVibe Companion
|
|
3
|
+
*
|
|
4
|
+
* 支持 Python, JavaScript, Rust 等语言的代码执行
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
7
|
+
import { tmpdir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { BashExecutor } from "./bash-executor.js";
|
|
10
|
+
export class CodeExecutor {
|
|
11
|
+
bashExecutor;
|
|
12
|
+
activeTasks = new Set();
|
|
13
|
+
constructor(security) {
|
|
14
|
+
this.bashExecutor = new BashExecutor(security);
|
|
15
|
+
}
|
|
16
|
+
async execute(task) {
|
|
17
|
+
const tempDir = await this.createTempDir(task.id);
|
|
18
|
+
try {
|
|
19
|
+
switch (task.language.toLowerCase()) {
|
|
20
|
+
case "python":
|
|
21
|
+
case "py":
|
|
22
|
+
return await this.executePython(task, tempDir);
|
|
23
|
+
case "javascript":
|
|
24
|
+
case "js":
|
|
25
|
+
case "node":
|
|
26
|
+
return await this.executeJavaScript(task, tempDir);
|
|
27
|
+
case "rust":
|
|
28
|
+
case "rs":
|
|
29
|
+
return await this.executeRust(task, tempDir);
|
|
30
|
+
case "typescript":
|
|
31
|
+
case "ts":
|
|
32
|
+
return await this.executeTypeScript(task, tempDir);
|
|
33
|
+
default:
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
output: "",
|
|
37
|
+
error: `Unsupported language: ${task.language}`,
|
|
38
|
+
errorCode: "UNSUPPORTED_LANGUAGE",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (task.id) {
|
|
44
|
+
this.activeTasks.delete(task.id);
|
|
45
|
+
}
|
|
46
|
+
// 清理临时目录
|
|
47
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async executePython(task, tempDir) {
|
|
51
|
+
const scriptPath = join(tempDir, "script.py");
|
|
52
|
+
await writeFile(scriptPath, task.code);
|
|
53
|
+
const result = await this.bashExecutor.execute({
|
|
54
|
+
command: "python3",
|
|
55
|
+
args: [scriptPath],
|
|
56
|
+
workingDir: tempDir,
|
|
57
|
+
timeout: task.timeout,
|
|
58
|
+
onOutput: task.onOutput,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
success: result.success,
|
|
62
|
+
output: result.stdout,
|
|
63
|
+
error: result.error || result.stderr,
|
|
64
|
+
errorCode: result.errorCode,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async executeJavaScript(task, tempDir) {
|
|
68
|
+
const scriptPath = join(tempDir, "script.js");
|
|
69
|
+
await writeFile(scriptPath, task.code);
|
|
70
|
+
const result = await this.bashExecutor.execute({
|
|
71
|
+
command: "node",
|
|
72
|
+
args: [scriptPath],
|
|
73
|
+
workingDir: tempDir,
|
|
74
|
+
timeout: task.timeout,
|
|
75
|
+
onOutput: task.onOutput,
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
success: result.success,
|
|
79
|
+
output: result.stdout,
|
|
80
|
+
error: result.error || result.stderr,
|
|
81
|
+
errorCode: result.errorCode,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async executeTypeScript(task, tempDir) {
|
|
85
|
+
const scriptPath = join(tempDir, "script.ts");
|
|
86
|
+
await writeFile(scriptPath, task.code);
|
|
87
|
+
// 使用 ts-node 或tsx 运行 TypeScript
|
|
88
|
+
const result = await this.bashExecutor.execute({
|
|
89
|
+
command: "npx",
|
|
90
|
+
args: ["tsx", scriptPath],
|
|
91
|
+
workingDir: tempDir,
|
|
92
|
+
timeout: task.timeout,
|
|
93
|
+
onOutput: task.onOutput,
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
success: result.success,
|
|
97
|
+
output: result.stdout,
|
|
98
|
+
error: result.error || result.stderr,
|
|
99
|
+
errorCode: result.errorCode,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async executeRust(task, tempDir) {
|
|
103
|
+
const mainPath = join(tempDir, "main.rs");
|
|
104
|
+
await writeFile(mainPath, task.code);
|
|
105
|
+
// 创建临时 Cargo 项目
|
|
106
|
+
const cargoToml = `
|
|
107
|
+
[package]
|
|
108
|
+
name = "temp"
|
|
109
|
+
version = "0.1.0"
|
|
110
|
+
edition = "2021"
|
|
111
|
+
`;
|
|
112
|
+
await writeFile(join(tempDir, "Cargo.toml"), cargoToml);
|
|
113
|
+
await mkdir(join(tempDir, "src"), { recursive: true });
|
|
114
|
+
await writeFile(join(tempDir, "src", "main.rs"), task.code);
|
|
115
|
+
// 编译并运行
|
|
116
|
+
const runResult = await this.bashExecutor.execute({
|
|
117
|
+
command: "cargo",
|
|
118
|
+
args: ["run", "--release"],
|
|
119
|
+
workingDir: tempDir,
|
|
120
|
+
timeout: task.timeout,
|
|
121
|
+
onOutput: task.onOutput,
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
success: runResult.success,
|
|
125
|
+
output: runResult.stdout,
|
|
126
|
+
error: runResult.error || runResult.stderr,
|
|
127
|
+
errorCode: runResult.errorCode,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async createTempDir(taskId) {
|
|
131
|
+
const dirName = taskId || `companion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
132
|
+
const tempDir = join(tmpdir(), dirName);
|
|
133
|
+
await mkdir(tempDir, { recursive: true });
|
|
134
|
+
return tempDir;
|
|
135
|
+
}
|
|
136
|
+
cancel(taskId) {
|
|
137
|
+
this.activeTasks.delete(taskId);
|
|
138
|
+
return this.bashExecutor.cancel(taskId);
|
|
139
|
+
}
|
|
140
|
+
shutdown() {
|
|
141
|
+
this.bashExecutor.shutdown();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=code-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-executor.js","sourceRoot":"","sources":["../../../../src/core/companion/executor/code-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Code Executor for OpenVibe Companion\n *\n * 支持 Python, JavaScript, Rust 等语言的代码执行\n */\n\nimport { mkdir, rm, writeFile } from \"fs/promises\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport type { SecurityManager } from \"../security/security-manager.js\";\nimport { BashExecutor } from \"./bash-executor.js\";\n\nexport interface CodeTask {\n\tid?: string;\n\tcode: string;\n\tlanguage: string;\n\ttimeout?: number;\n\tonOutput?: (data: Buffer, isStderr: boolean) => void;\n}\n\nexport interface CodeResult {\n\tsuccess: boolean;\n\toutput: string;\n\terror?: string;\n\terrorCode?: string;\n}\n\nexport class CodeExecutor {\n\tprivate bashExecutor: BashExecutor;\n\tprivate activeTasks = new Set<string>();\n\n\tconstructor(security: SecurityManager) {\n\t\tthis.bashExecutor = new BashExecutor(security);\n\t}\n\n\tasync execute(task: CodeTask): Promise<CodeResult> {\n\t\tconst tempDir = await this.createTempDir(task.id);\n\n\t\ttry {\n\t\t\tswitch (task.language.toLowerCase()) {\n\t\t\t\tcase \"python\":\n\t\t\t\tcase \"py\":\n\t\t\t\t\treturn await this.executePython(task, tempDir);\n\t\t\t\tcase \"javascript\":\n\t\t\t\tcase \"js\":\n\t\t\t\tcase \"node\":\n\t\t\t\t\treturn await this.executeJavaScript(task, tempDir);\n\t\t\t\tcase \"rust\":\n\t\t\t\tcase \"rs\":\n\t\t\t\t\treturn await this.executeRust(task, tempDir);\n\t\t\t\tcase \"typescript\":\n\t\t\t\tcase \"ts\":\n\t\t\t\t\treturn await this.executeTypeScript(task, tempDir);\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\toutput: \"\",\n\t\t\t\t\t\terror: `Unsupported language: ${task.language}`,\n\t\t\t\t\t\terrorCode: \"UNSUPPORTED_LANGUAGE\",\n\t\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tif (task.id) {\n\t\t\t\tthis.activeTasks.delete(task.id);\n\t\t\t}\n\t\t\t// 清理临时目录\n\t\t\tawait rm(tempDir, { recursive: true, force: true });\n\t\t}\n\t}\n\n\tprivate async executePython(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.py\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"python3\",\n\t\t\targs: [scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeJavaScript(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.js\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"node\",\n\t\t\targs: [scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeTypeScript(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst scriptPath = join(tempDir, \"script.ts\");\n\t\tawait writeFile(scriptPath, task.code);\n\n\t\t// 使用 ts-node 或tsx 运行 TypeScript\n\t\tconst result = await this.bashExecutor.execute({\n\t\t\tcommand: \"npx\",\n\t\t\targs: [\"tsx\", scriptPath],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: result.success,\n\t\t\toutput: result.stdout,\n\t\t\terror: result.error || result.stderr,\n\t\t\terrorCode: result.errorCode,\n\t\t};\n\t}\n\n\tprivate async executeRust(task: CodeTask, tempDir: string): Promise<CodeResult> {\n\t\tconst mainPath = join(tempDir, \"main.rs\");\n\t\tawait writeFile(mainPath, task.code);\n\n\t\t// 创建临时 Cargo 项目\n\t\tconst cargoToml = `\n[package]\nname = \"temp\"\nversion = \"0.1.0\"\nedition = \"2021\"\n`;\n\t\tawait writeFile(join(tempDir, \"Cargo.toml\"), cargoToml);\n\t\tawait mkdir(join(tempDir, \"src\"), { recursive: true });\n\t\tawait writeFile(join(tempDir, \"src\", \"main.rs\"), task.code);\n\n\t\t// 编译并运行\n\t\tconst runResult = await this.bashExecutor.execute({\n\t\t\tcommand: \"cargo\",\n\t\t\targs: [\"run\", \"--release\"],\n\t\t\tworkingDir: tempDir,\n\t\t\ttimeout: task.timeout,\n\t\t\tonOutput: task.onOutput,\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: runResult.success,\n\t\t\toutput: runResult.stdout,\n\t\t\terror: runResult.error || runResult.stderr,\n\t\t\terrorCode: runResult.errorCode,\n\t\t};\n\t}\n\n\tprivate async createTempDir(taskId?: string): Promise<string> {\n\t\tconst dirName = taskId || `companion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\t\tconst tempDir = join(tmpdir(), dirName);\n\t\tawait mkdir(tempDir, { recursive: true });\n\t\treturn tempDir;\n\t}\n\n\tcancel(taskId: string): boolean {\n\t\tthis.activeTasks.delete(taskId);\n\t\treturn this.bashExecutor.cancel(taskId);\n\t}\n\n\tshutdown(): void {\n\t\tthis.bashExecutor.shutdown();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Monitor for OpenVibe Companion
|
|
3
|
+
*
|
|
4
|
+
* 监控系统资源状态
|
|
5
|
+
*/
|
|
6
|
+
export interface ResourceStatus {
|
|
7
|
+
cpu: CpuStatus;
|
|
8
|
+
memory: MemoryStatus;
|
|
9
|
+
gpu?: GpuStatus;
|
|
10
|
+
disk: DiskStatus;
|
|
11
|
+
}
|
|
12
|
+
export interface CpuStatus {
|
|
13
|
+
coreCount: number;
|
|
14
|
+
usagePercent: number;
|
|
15
|
+
availableCores: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MemoryStatus {
|
|
18
|
+
totalMb: number;
|
|
19
|
+
usedMb: number;
|
|
20
|
+
freeMb: number;
|
|
21
|
+
usagePercent: number;
|
|
22
|
+
}
|
|
23
|
+
export interface GpuStatus {
|
|
24
|
+
deviceCount: number;
|
|
25
|
+
devices: GpuDevice[];
|
|
26
|
+
}
|
|
27
|
+
export interface GpuDevice {
|
|
28
|
+
index: number;
|
|
29
|
+
name: string;
|
|
30
|
+
memoryTotalMb: number;
|
|
31
|
+
memoryUsedMb: number;
|
|
32
|
+
memoryUsagePercent: number;
|
|
33
|
+
}
|
|
34
|
+
export interface DiskStatus {
|
|
35
|
+
disks: DiskInfo[];
|
|
36
|
+
}
|
|
37
|
+
export interface DiskInfo {
|
|
38
|
+
mountPoint: string;
|
|
39
|
+
totalBytes: number;
|
|
40
|
+
usedBytes: number;
|
|
41
|
+
freeBytes: number;
|
|
42
|
+
usagePercent: number;
|
|
43
|
+
}
|
|
44
|
+
export declare class ResourceMonitor {
|
|
45
|
+
private lastCpuInfo;
|
|
46
|
+
constructor();
|
|
47
|
+
private calculateCpuTimes;
|
|
48
|
+
getStatus(): ResourceStatus;
|
|
49
|
+
private getCpuStatus;
|
|
50
|
+
private getMemoryStatus;
|
|
51
|
+
private getGpuStatus;
|
|
52
|
+
private getDiskStatus;
|
|
53
|
+
private getDiskStatusAsync;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=resource-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-monitor.d.ts","sourceRoot":"","sources":["../../../../src/core/companion/executor/resource-monitor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,WAAW,CAAkC;IAErD,cAEC;IAED,OAAO,CAAC,iBAAiB;IAezB,SAAS,IAAI,cAAc,CAO1B;IAED,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,kBAAkB;CAoE1B","sourcesContent":["/**\n * Resource Monitor for OpenVibe Companion\n *\n * 监控系统资源状态\n */\n\nimport { statfsSync } from \"fs\";\nimport { cpus, freemem, totalmem } from \"os\";\n\nexport interface ResourceStatus {\n\tcpu: CpuStatus;\n\tmemory: MemoryStatus;\n\tgpu?: GpuStatus;\n\tdisk: DiskStatus;\n}\n\nexport interface CpuStatus {\n\tcoreCount: number;\n\tusagePercent: number;\n\tavailableCores: number;\n}\n\nexport interface MemoryStatus {\n\ttotalMb: number;\n\tusedMb: number;\n\tfreeMb: number;\n\tusagePercent: number;\n}\n\nexport interface GpuStatus {\n\tdeviceCount: number;\n\tdevices: GpuDevice[];\n}\n\nexport interface GpuDevice {\n\tindex: number;\n\tname: string;\n\tmemoryTotalMb: number;\n\tmemoryUsedMb: number;\n\tmemoryUsagePercent: number;\n}\n\nexport interface DiskStatus {\n\tdisks: DiskInfo[];\n}\n\nexport interface DiskInfo {\n\tmountPoint: string;\n\ttotalBytes: number;\n\tusedBytes: number;\n\tfreeBytes: number;\n\tusagePercent: number;\n}\n\nexport class ResourceMonitor {\n\tprivate lastCpuInfo: { idle: number; total: number };\n\n\tconstructor() {\n\t\tthis.lastCpuInfo = this.calculateCpuTimes();\n\t}\n\n\tprivate calculateCpuTimes(): { idle: number; total: number } {\n\t\tconst cpuData = cpus();\n\t\tlet totalIdle = 0;\n\t\tlet totalTick = 0;\n\n\t\tfor (const cpu of cpuData) {\n\t\t\tfor (const type in cpu.times) {\n\t\t\t\ttotalTick += cpu.times[type as keyof typeof cpu.times];\n\t\t\t}\n\t\t\ttotalIdle += cpu.times.idle;\n\t\t}\n\n\t\treturn { idle: totalIdle, total: totalTick };\n\t}\n\n\tgetStatus(): ResourceStatus {\n\t\treturn {\n\t\t\tcpu: this.getCpuStatus(),\n\t\t\tmemory: this.getMemoryStatus(),\n\t\t\tgpu: this.getGpuStatus(),\n\t\t\tdisk: this.getDiskStatus(),\n\t\t};\n\t}\n\n\tprivate getCpuStatus(): CpuStatus {\n\t\tconst coreCount = cpus().length;\n\t\tconst currentCpuInfo = this.calculateCpuTimes();\n\n\t\tconst idleDiff = currentCpuInfo.idle - this.lastCpuInfo.idle;\n\t\tconst totalDiff = currentCpuInfo.total - this.lastCpuInfo.total;\n\t\tconst usagePercent = totalDiff > 0 ? ((totalDiff - idleDiff) / totalDiff) * 100 : 0;\n\n\t\tthis.lastCpuInfo = currentCpuInfo;\n\n\t\treturn {\n\t\t\tcoreCount,\n\t\t\tusagePercent: Math.round(usagePercent * 100) / 100,\n\t\t\tavailableCores: coreCount,\n\t\t};\n\t}\n\n\tprivate getMemoryStatus(): MemoryStatus {\n\t\tconst total = totalmem();\n\t\tconst free = freemem();\n\t\tconst used = total - free;\n\n\t\treturn {\n\t\t\ttotalMb: Math.floor(total / (1024 * 1024)),\n\t\t\tusedMb: Math.floor(used / (1024 * 1024)),\n\t\t\tfreeMb: Math.floor(free / (1024 * 1024)),\n\t\t\tusagePercent: (used / total) * 100,\n\t\t};\n\t}\n\n\tprivate getGpuStatus(): GpuStatus | undefined {\n\t\t// 尝试检测 NVIDIA GPU\n\t\ttry {\n\t\t\t// 这里可以使用 nvidia-smi 或其他方式检测\n\t\t\t// 简化实现,返回空\n\t\t\treturn {\n\t\t\t\tdeviceCount: 0,\n\t\t\t\tdevices: [],\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate getDiskStatus(): DiskStatus {\n\t\treturn this.getDiskStatusAsync();\n\t}\n\n\tprivate getDiskStatusAsync(): DiskStatus {\n\t\tconst platform = process.platform;\n\t\tconst disks: DiskInfo[] = [];\n\n\t\tif (platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst { execSync } = require(\"child_process\");\n\t\t\t\tconst output = execSync(\"wmic logicaldisk get caption,freespace,size\", {\n\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\ttimeout: 5000,\n\t\t\t\t});\n\t\t\t\tconst lines = output.trim().split(\"\\n\").slice(1);\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst parts = line.trim().split(/\\s+/);\n\t\t\t\t\tif (parts.length >= 3) {\n\t\t\t\t\t\tconst mountPoint = parts[0];\n\t\t\t\t\t\tconst freeBytes = parseInt(parts[1], 10) || 0;\n\t\t\t\t\t\tconst totalBytes = parseInt(parts[2], 10) || 0;\n\t\t\t\t\t\tconst usedBytes = totalBytes - freeBytes;\n\n\t\t\t\t\t\tif (totalBytes > 0) {\n\t\t\t\t\t\t\tdisks.push({\n\t\t\t\t\t\t\t\tmountPoint,\n\t\t\t\t\t\t\t\ttotalBytes,\n\t\t\t\t\t\t\t\tusedBytes,\n\t\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\t\tusagePercent: (usedBytes / totalBytes) * 100,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"C:\",\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\tusedBytes: 0,\n\t\t\t\t\tfreeBytes: 0,\n\t\t\t\t\tusagePercent: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst stat = statfsSync(\"/\");\n\t\t\t\tconst totalBytes = stat.blocks * stat.bsize;\n\t\t\t\tconst freeBytes = stat.bfree * stat.bsize;\n\t\t\t\tconst usedBytes = totalBytes - freeBytes;\n\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"/\",\n\t\t\t\t\ttotalBytes,\n\t\t\t\t\tusedBytes,\n\t\t\t\t\tfreeBytes,\n\t\t\t\t\tusagePercent: totalBytes > 0 ? (usedBytes / totalBytes) * 100 : 0,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"/\",\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\tusedBytes: 0,\n\t\t\t\t\tfreeBytes: 0,\n\t\t\t\t\tusagePercent: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { disks };\n\t}\n}\n"]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Monitor for OpenVibe Companion
|
|
3
|
+
*
|
|
4
|
+
* 监控系统资源状态
|
|
5
|
+
*/
|
|
6
|
+
import { statfsSync } from "fs";
|
|
7
|
+
import { cpus, freemem, totalmem } from "os";
|
|
8
|
+
export class ResourceMonitor {
|
|
9
|
+
lastCpuInfo;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.lastCpuInfo = this.calculateCpuTimes();
|
|
12
|
+
}
|
|
13
|
+
calculateCpuTimes() {
|
|
14
|
+
const cpuData = cpus();
|
|
15
|
+
let totalIdle = 0;
|
|
16
|
+
let totalTick = 0;
|
|
17
|
+
for (const cpu of cpuData) {
|
|
18
|
+
for (const type in cpu.times) {
|
|
19
|
+
totalTick += cpu.times[type];
|
|
20
|
+
}
|
|
21
|
+
totalIdle += cpu.times.idle;
|
|
22
|
+
}
|
|
23
|
+
return { idle: totalIdle, total: totalTick };
|
|
24
|
+
}
|
|
25
|
+
getStatus() {
|
|
26
|
+
return {
|
|
27
|
+
cpu: this.getCpuStatus(),
|
|
28
|
+
memory: this.getMemoryStatus(),
|
|
29
|
+
gpu: this.getGpuStatus(),
|
|
30
|
+
disk: this.getDiskStatus(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
getCpuStatus() {
|
|
34
|
+
const coreCount = cpus().length;
|
|
35
|
+
const currentCpuInfo = this.calculateCpuTimes();
|
|
36
|
+
const idleDiff = currentCpuInfo.idle - this.lastCpuInfo.idle;
|
|
37
|
+
const totalDiff = currentCpuInfo.total - this.lastCpuInfo.total;
|
|
38
|
+
const usagePercent = totalDiff > 0 ? ((totalDiff - idleDiff) / totalDiff) * 100 : 0;
|
|
39
|
+
this.lastCpuInfo = currentCpuInfo;
|
|
40
|
+
return {
|
|
41
|
+
coreCount,
|
|
42
|
+
usagePercent: Math.round(usagePercent * 100) / 100,
|
|
43
|
+
availableCores: coreCount,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
getMemoryStatus() {
|
|
47
|
+
const total = totalmem();
|
|
48
|
+
const free = freemem();
|
|
49
|
+
const used = total - free;
|
|
50
|
+
return {
|
|
51
|
+
totalMb: Math.floor(total / (1024 * 1024)),
|
|
52
|
+
usedMb: Math.floor(used / (1024 * 1024)),
|
|
53
|
+
freeMb: Math.floor(free / (1024 * 1024)),
|
|
54
|
+
usagePercent: (used / total) * 100,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
getGpuStatus() {
|
|
58
|
+
// 尝试检测 NVIDIA GPU
|
|
59
|
+
try {
|
|
60
|
+
// 这里可以使用 nvidia-smi 或其他方式检测
|
|
61
|
+
// 简化实现,返回空
|
|
62
|
+
return {
|
|
63
|
+
deviceCount: 0,
|
|
64
|
+
devices: [],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
getDiskStatus() {
|
|
72
|
+
return this.getDiskStatusAsync();
|
|
73
|
+
}
|
|
74
|
+
getDiskStatusAsync() {
|
|
75
|
+
const platform = process.platform;
|
|
76
|
+
const disks = [];
|
|
77
|
+
if (platform === "win32") {
|
|
78
|
+
try {
|
|
79
|
+
const { execSync } = require("child_process");
|
|
80
|
+
const output = execSync("wmic logicaldisk get caption,freespace,size", {
|
|
81
|
+
encoding: "utf-8",
|
|
82
|
+
timeout: 5000,
|
|
83
|
+
});
|
|
84
|
+
const lines = output.trim().split("\n").slice(1);
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const parts = line.trim().split(/\s+/);
|
|
87
|
+
if (parts.length >= 3) {
|
|
88
|
+
const mountPoint = parts[0];
|
|
89
|
+
const freeBytes = parseInt(parts[1], 10) || 0;
|
|
90
|
+
const totalBytes = parseInt(parts[2], 10) || 0;
|
|
91
|
+
const usedBytes = totalBytes - freeBytes;
|
|
92
|
+
if (totalBytes > 0) {
|
|
93
|
+
disks.push({
|
|
94
|
+
mountPoint,
|
|
95
|
+
totalBytes,
|
|
96
|
+
usedBytes,
|
|
97
|
+
freeBytes,
|
|
98
|
+
usagePercent: (usedBytes / totalBytes) * 100,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
disks.push({
|
|
106
|
+
mountPoint: "C:",
|
|
107
|
+
totalBytes: 0,
|
|
108
|
+
usedBytes: 0,
|
|
109
|
+
freeBytes: 0,
|
|
110
|
+
usagePercent: 0,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
try {
|
|
116
|
+
const stat = statfsSync("/");
|
|
117
|
+
const totalBytes = stat.blocks * stat.bsize;
|
|
118
|
+
const freeBytes = stat.bfree * stat.bsize;
|
|
119
|
+
const usedBytes = totalBytes - freeBytes;
|
|
120
|
+
disks.push({
|
|
121
|
+
mountPoint: "/",
|
|
122
|
+
totalBytes,
|
|
123
|
+
usedBytes,
|
|
124
|
+
freeBytes,
|
|
125
|
+
usagePercent: totalBytes > 0 ? (usedBytes / totalBytes) * 100 : 0,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
disks.push({
|
|
130
|
+
mountPoint: "/",
|
|
131
|
+
totalBytes: 0,
|
|
132
|
+
usedBytes: 0,
|
|
133
|
+
freeBytes: 0,
|
|
134
|
+
usagePercent: 0,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { disks };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=resource-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-monitor.js","sourceRoot":"","sources":["../../../../src/core/companion/executor/resource-monitor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AA+C7C,MAAM,OAAO,eAAe;IACnB,WAAW,CAAkC;IAErD,cAAc;QACb,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAAA,CAC5C;IAEO,iBAAiB,GAAoC;QAC5D,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC;QACvB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC3B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC9B,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,IAA8B,CAAC,CAAC;YACxD,CAAC;YACD,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;QAC7B,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAA,CAC7C;IAED,SAAS,GAAmB;QAC3B,OAAO;YACN,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE;YACxB,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE;YAC9B,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE;YACxB,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE;SAC1B,CAAC;IAAA,CACF;IAEO,YAAY,GAAc;QACjC,MAAM,SAAS,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC;QAChC,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEhD,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAC7D,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QAChE,MAAM,YAAY,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,QAAQ,CAAC,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpF,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC;QAElC,OAAO;YACN,SAAS;YACT,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;YAClD,cAAc,EAAE,SAAS;SACzB,CAAC;IAAA,CACF;IAEO,eAAe,GAAiB;QACvC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;QAE1B,OAAO;YACN,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YAC1C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YACxC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YACxC,YAAY,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG;SAClC,CAAC;IAAA,CACF;IAEO,YAAY,GAA0B;QAC7C,0BAAkB;QAClB,IAAI,CAAC;YACJ,sDAA4B;YAC5B,2BAAW;YACX,OAAO;gBACN,WAAW,EAAE,CAAC;gBACd,OAAO,EAAE,EAAE;aACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,SAAS,CAAC;QAClB,CAAC;IAAA,CACD;IAEO,aAAa,GAAe;QACnC,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAAA,CACjC;IAEO,kBAAkB,GAAe;QACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,KAAK,GAAe,EAAE,CAAC;QAE7B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACJ,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,6CAA6C,EAAE;oBACtE,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,IAAI;iBACb,CAAC,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBACvC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;wBACvB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;wBAC5B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;wBAC9C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;wBAC/C,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;wBAEzC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;4BACpB,KAAK,CAAC,IAAI,CAAC;gCACV,UAAU;gCACV,UAAU;gCACV,SAAS;gCACT,SAAS;gCACT,YAAY,EAAE,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG;6BAC5C,CAAC,CAAC;wBACJ,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,KAAK,CAAC,IAAI,CAAC;oBACV,UAAU,EAAE,IAAI;oBAChB,UAAU,EAAE,CAAC;oBACb,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,CAAC;oBACZ,YAAY,EAAE,CAAC;iBACf,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;aAAM,CAAC;YACP,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;gBAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;gBAC1C,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;gBAEzC,KAAK,CAAC,IAAI,CAAC;oBACV,UAAU,EAAE,GAAG;oBACf,UAAU;oBACV,SAAS;oBACT,SAAS;oBACT,YAAY,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;iBACjE,CAAC,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACR,KAAK,CAAC,IAAI,CAAC;oBACV,UAAU,EAAE,GAAG;oBACf,UAAU,EAAE,CAAC;oBACb,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,CAAC;oBACZ,YAAY,EAAE,CAAC;iBACf,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,CAAC;IAAA,CACjB;CACD","sourcesContent":["/**\n * Resource Monitor for OpenVibe Companion\n *\n * 监控系统资源状态\n */\n\nimport { statfsSync } from \"fs\";\nimport { cpus, freemem, totalmem } from \"os\";\n\nexport interface ResourceStatus {\n\tcpu: CpuStatus;\n\tmemory: MemoryStatus;\n\tgpu?: GpuStatus;\n\tdisk: DiskStatus;\n}\n\nexport interface CpuStatus {\n\tcoreCount: number;\n\tusagePercent: number;\n\tavailableCores: number;\n}\n\nexport interface MemoryStatus {\n\ttotalMb: number;\n\tusedMb: number;\n\tfreeMb: number;\n\tusagePercent: number;\n}\n\nexport interface GpuStatus {\n\tdeviceCount: number;\n\tdevices: GpuDevice[];\n}\n\nexport interface GpuDevice {\n\tindex: number;\n\tname: string;\n\tmemoryTotalMb: number;\n\tmemoryUsedMb: number;\n\tmemoryUsagePercent: number;\n}\n\nexport interface DiskStatus {\n\tdisks: DiskInfo[];\n}\n\nexport interface DiskInfo {\n\tmountPoint: string;\n\ttotalBytes: number;\n\tusedBytes: number;\n\tfreeBytes: number;\n\tusagePercent: number;\n}\n\nexport class ResourceMonitor {\n\tprivate lastCpuInfo: { idle: number; total: number };\n\n\tconstructor() {\n\t\tthis.lastCpuInfo = this.calculateCpuTimes();\n\t}\n\n\tprivate calculateCpuTimes(): { idle: number; total: number } {\n\t\tconst cpuData = cpus();\n\t\tlet totalIdle = 0;\n\t\tlet totalTick = 0;\n\n\t\tfor (const cpu of cpuData) {\n\t\t\tfor (const type in cpu.times) {\n\t\t\t\ttotalTick += cpu.times[type as keyof typeof cpu.times];\n\t\t\t}\n\t\t\ttotalIdle += cpu.times.idle;\n\t\t}\n\n\t\treturn { idle: totalIdle, total: totalTick };\n\t}\n\n\tgetStatus(): ResourceStatus {\n\t\treturn {\n\t\t\tcpu: this.getCpuStatus(),\n\t\t\tmemory: this.getMemoryStatus(),\n\t\t\tgpu: this.getGpuStatus(),\n\t\t\tdisk: this.getDiskStatus(),\n\t\t};\n\t}\n\n\tprivate getCpuStatus(): CpuStatus {\n\t\tconst coreCount = cpus().length;\n\t\tconst currentCpuInfo = this.calculateCpuTimes();\n\n\t\tconst idleDiff = currentCpuInfo.idle - this.lastCpuInfo.idle;\n\t\tconst totalDiff = currentCpuInfo.total - this.lastCpuInfo.total;\n\t\tconst usagePercent = totalDiff > 0 ? ((totalDiff - idleDiff) / totalDiff) * 100 : 0;\n\n\t\tthis.lastCpuInfo = currentCpuInfo;\n\n\t\treturn {\n\t\t\tcoreCount,\n\t\t\tusagePercent: Math.round(usagePercent * 100) / 100,\n\t\t\tavailableCores: coreCount,\n\t\t};\n\t}\n\n\tprivate getMemoryStatus(): MemoryStatus {\n\t\tconst total = totalmem();\n\t\tconst free = freemem();\n\t\tconst used = total - free;\n\n\t\treturn {\n\t\t\ttotalMb: Math.floor(total / (1024 * 1024)),\n\t\t\tusedMb: Math.floor(used / (1024 * 1024)),\n\t\t\tfreeMb: Math.floor(free / (1024 * 1024)),\n\t\t\tusagePercent: (used / total) * 100,\n\t\t};\n\t}\n\n\tprivate getGpuStatus(): GpuStatus | undefined {\n\t\t// 尝试检测 NVIDIA GPU\n\t\ttry {\n\t\t\t// 这里可以使用 nvidia-smi 或其他方式检测\n\t\t\t// 简化实现,返回空\n\t\t\treturn {\n\t\t\t\tdeviceCount: 0,\n\t\t\t\tdevices: [],\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate getDiskStatus(): DiskStatus {\n\t\treturn this.getDiskStatusAsync();\n\t}\n\n\tprivate getDiskStatusAsync(): DiskStatus {\n\t\tconst platform = process.platform;\n\t\tconst disks: DiskInfo[] = [];\n\n\t\tif (platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst { execSync } = require(\"child_process\");\n\t\t\t\tconst output = execSync(\"wmic logicaldisk get caption,freespace,size\", {\n\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\ttimeout: 5000,\n\t\t\t\t});\n\t\t\t\tconst lines = output.trim().split(\"\\n\").slice(1);\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst parts = line.trim().split(/\\s+/);\n\t\t\t\t\tif (parts.length >= 3) {\n\t\t\t\t\t\tconst mountPoint = parts[0];\n\t\t\t\t\t\tconst freeBytes = parseInt(parts[1], 10) || 0;\n\t\t\t\t\t\tconst totalBytes = parseInt(parts[2], 10) || 0;\n\t\t\t\t\t\tconst usedBytes = totalBytes - freeBytes;\n\n\t\t\t\t\t\tif (totalBytes > 0) {\n\t\t\t\t\t\t\tdisks.push({\n\t\t\t\t\t\t\t\tmountPoint,\n\t\t\t\t\t\t\t\ttotalBytes,\n\t\t\t\t\t\t\t\tusedBytes,\n\t\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\t\tusagePercent: (usedBytes / totalBytes) * 100,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"C:\",\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\tusedBytes: 0,\n\t\t\t\t\tfreeBytes: 0,\n\t\t\t\t\tusagePercent: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst stat = statfsSync(\"/\");\n\t\t\t\tconst totalBytes = stat.blocks * stat.bsize;\n\t\t\t\tconst freeBytes = stat.bfree * stat.bsize;\n\t\t\t\tconst usedBytes = totalBytes - freeBytes;\n\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"/\",\n\t\t\t\t\ttotalBytes,\n\t\t\t\t\tusedBytes,\n\t\t\t\t\tfreeBytes,\n\t\t\t\t\tusagePercent: totalBytes > 0 ? (usedBytes / totalBytes) * 100 : 0,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tdisks.push({\n\t\t\t\t\tmountPoint: \"/\",\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\tusedBytes: 0,\n\t\t\t\t\tfreeBytes: 0,\n\t\t\t\t\tusagePercent: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { disks };\n\t}\n}\n"]}
|