@clawops/cli 1.7.9 → 2.0.1
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/README.md +504 -76
- package/dist/apply-KYFPWI5U.js +27 -0
- package/dist/automation-RMAIBNVC.js +44 -0
- package/dist/bedrock-XVASJX2K.js +61 -0
- package/dist/{bootstrap-HL4DNUNG.js → bootstrap-HNGKY6WW.js} +31 -19
- package/dist/bootstrap.sh.tmpl +51 -31
- package/dist/channels-KBUR42IU.js +53 -0
- package/dist/chunk-2I7KCMXZ.js +142 -0
- package/dist/{chunk-DQDAWNGB.js → chunk-2Z7J3ERQ.js} +1 -1
- package/dist/chunk-3OLUJPCB.js +36 -0
- package/dist/chunk-5XT5IAJK.js +69 -0
- package/dist/chunk-AEEMRFZV.js +24 -0
- package/dist/chunk-BSLIYB5Q.js +106 -0
- package/dist/{chunk-3XK66DKK.js → chunk-E4NJSJ54.js} +57 -93
- package/dist/chunk-FEK7VJAY.js +16 -0
- package/dist/{chunk-UKTP332Z.js → chunk-GAZPPSKU.js} +6 -7
- package/dist/chunk-GIEVAGEK.js +126 -0
- package/dist/chunk-HSPHXG6W.js +67 -0
- package/dist/chunk-IVWP4QUJ.js +901 -0
- package/dist/{chunk-PVUVI35Y.js → chunk-JW5A7E3X.js} +5 -1
- package/dist/{chunk-GZN7PKP4.js → chunk-KC3GC3PE.js} +328 -121
- package/dist/chunk-LBE6COC4.js +244 -0
- package/dist/chunk-LEVIW2PV.js +890 -0
- package/dist/chunk-LT4XMO5D.js +50 -0
- package/dist/chunk-MC5ATCXS.js +108 -0
- package/dist/chunk-N4WZLWJ4.js +16 -0
- package/dist/chunk-NUDIHZEH.js +55 -0
- package/dist/chunk-NUL5F6YV.js +56 -0
- package/dist/chunk-RHOWYIWT.js +52 -0
- package/dist/chunk-STLRHLLV.js +193 -0
- package/dist/chunk-V25QRZRB.js +45 -0
- package/dist/chunk-VOEJYRIK.js +33 -0
- package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
- package/dist/chunk-W3OLNNJW.js +51 -0
- package/dist/cli-WLD3EBEM.js +11 -0
- package/dist/cli-auth-GD5SK7JU.js +13 -0
- package/dist/cli.js +1296 -695
- package/dist/config-validate-K2N6XLDX.js +76 -0
- package/dist/context-7EVC6W45.js +20 -0
- package/dist/docker-3PDO4TS7.js +12 -0
- package/dist/{firewall-XQGO7HWK.js → firewall-5VKZ4WBH.js} +32 -1
- package/dist/generate-I6YQ5V2T.js +29 -0
- package/dist/{harden-URPXLSVF.js → harden-72JMRT4E.js} +51 -11
- package/dist/health-5CCOXYGG.js +11 -0
- package/dist/index.d.ts +56 -0
- package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
- package/dist/logs-5A7YYA53.js +15 -0
- package/dist/{mcp-wire-7E5JQY47.js → mcp-wire-3SWBM7D6.js} +32 -16
- package/dist/models-AGTQWYDL.js +32 -0
- package/dist/network-args-PYJRJBYO.js +64 -0
- package/dist/{package-5AR4ZPK4.js → package-5BCMEBTE.js} +11 -4
- package/dist/passphrase-MJBE45N7.js +15 -0
- package/dist/plugins-DTST3OER.js +49 -0
- package/dist/{pool-QGFQRMGU.js → pool-PIIKJ4L2.js} +3 -2
- package/dist/preflight-EF52S3WW.js +162 -0
- package/dist/preflight-MWWM5JT3.js +181 -0
- package/dist/preflight-TFD4N5ZU.js +141 -0
- package/dist/privileged-JFA6LLTV.js +17 -0
- package/dist/providers-FW6H6OPF.js +11 -0
- package/dist/ready-KDNRBNSU.js +95 -0
- package/dist/{remote-config-QDLN4UMG.js → remote-config-3HZLUWEA.js} +4 -3
- package/dist/runtime-X3KW5TCP.js +44 -0
- package/dist/server-GBA7BJGK.js +46 -0
- package/dist/spec-path-KQOOIQCA.js +9 -0
- package/dist/ssh-VAI5DU5M.js +11 -0
- package/dist/ssh-key-DUO6OKOI.js +7 -0
- package/dist/stack-config-R4SPSUQZ.js +12 -0
- package/dist/upgrade-ULD4KFYC.js +88 -0
- package/dist/validate-YVI3BJ55.js +14 -0
- package/dist/{version-guard-VUCNZXQS.js → version-guard-XVYW5WFJ.js} +2 -2
- package/dist/{versions-2UVAT6PQ.js → versions-W5LRQCAJ.js} +2 -2
- package/dist/wait-WO72TRRQ.js +79 -0
- package/package.json +11 -4
- package/spec/deploy-plan.schema.json +114 -25
- package/spec/integrations.yaml +135 -28
- package/spec/mcp-tools.yaml +89 -37
- package/spec/models.yaml +23 -3
- package/spec/openclaw-2.0.config.schema.json +1 -0
- package/spec/openclaw-versions.yaml +116 -27
- package/spec/providers.schema.json +163 -28
- package/dist/apply-F3KY2HTB.js +0 -17
- package/dist/automation-RULUSJBW.js +0 -30
- package/dist/aws-L4RR2LSE.js +0 -254
- package/dist/azure-7OXQENQL.js +0 -262
- package/dist/chunk-35WVQY3X.js +0 -122
- package/dist/chunk-6X45DDGH.js +0 -42
- package/dist/chunk-A4QR4TXW.js +0 -120
- package/dist/chunk-C5X375RO.js +0 -85
- package/dist/chunk-FNS56UQY.js +0 -741
- package/dist/chunk-P5LKWRPL.js +0 -114
- package/dist/chunk-SEZMVVAS.js +0 -156
- package/dist/context-5LCUG2QR.js +0 -10
- package/dist/gcp-UG6AWPIZ.js +0 -193
- package/dist/generate-OAGYT3VH.js +0 -15
- package/dist/local-DXBEVZ5C.js +0 -47
- package/dist/server-FIZCOLHL.js +0 -32
- package/dist/ssh-CNJNLM3E.js +0 -8
- package/dist/validate-CFQ4FH2T.js +0 -10
|
@@ -23,7 +23,11 @@ function resolveSpecDir(startUrl = import.meta.url) {
|
|
|
23
23
|
`Cannot locate the spec/ directory (searched upward from ${dirname(fileURLToPath(startUrl))}). If this is an installed package, spec/ may be missing from the published files.`
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
|
+
function _resetSpecDirCache() {
|
|
27
|
+
_cached = void 0;
|
|
28
|
+
}
|
|
26
29
|
|
|
27
30
|
export {
|
|
28
|
-
resolveSpecDir
|
|
31
|
+
resolveSpecDir,
|
|
32
|
+
_resetSpecDirCache
|
|
29
33
|
};
|
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
applyPlan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LBE6COC4.js";
|
|
5
5
|
import {
|
|
6
6
|
extractBaseOutputs
|
|
7
7
|
} from "./chunk-3QJBNAHW.js";
|
|
8
|
+
import {
|
|
9
|
+
atomicWriteConfig,
|
|
10
|
+
readRemoteConfig,
|
|
11
|
+
restartGateway
|
|
12
|
+
} from "./chunk-2I7KCMXZ.js";
|
|
8
13
|
import {
|
|
9
14
|
generatePlan
|
|
10
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-STLRHLLV.js";
|
|
11
16
|
import {
|
|
12
17
|
validatePlan
|
|
13
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-BSLIYB5Q.js";
|
|
19
|
+
import {
|
|
20
|
+
acquireSession,
|
|
21
|
+
drainPool
|
|
22
|
+
} from "./chunk-2Z7J3ERQ.js";
|
|
23
|
+
import {
|
|
24
|
+
buildContext
|
|
25
|
+
} from "./chunk-5XT5IAJK.js";
|
|
14
26
|
import {
|
|
15
|
-
errText,
|
|
16
27
|
formatUptime,
|
|
17
28
|
gatherSnapshot,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
handleConfigUnset,
|
|
21
|
-
handleConfigValidate,
|
|
22
|
-
okText,
|
|
23
|
-
resolveConn
|
|
24
|
-
} from "./chunk-FNS56UQY.js";
|
|
29
|
+
runDiagnostics
|
|
30
|
+
} from "./chunk-IVWP4QUJ.js";
|
|
25
31
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
GATEWAY_LOGS_PROBE,
|
|
33
|
+
chooseLogSource,
|
|
34
|
+
containerLogsCommand,
|
|
35
|
+
gatewayLogsCommand
|
|
36
|
+
} from "./chunk-NUL5F6YV.js";
|
|
37
|
+
import {
|
|
38
|
+
execPrivileged
|
|
39
|
+
} from "./chunk-NUDIHZEH.js";
|
|
40
|
+
import {
|
|
41
|
+
STATE_DIR_HOST_LINUX,
|
|
42
|
+
gatewayRunCommand
|
|
43
|
+
} from "./chunk-GIEVAGEK.js";
|
|
29
44
|
import {
|
|
30
45
|
IMAGE_INSPECT_CMD,
|
|
31
|
-
OPENCLAW_CONFIG,
|
|
32
|
-
gatewayRunCommand,
|
|
33
46
|
imageForRestart
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import {
|
|
36
|
-
buildContext
|
|
37
|
-
} from "./chunk-P5LKWRPL.js";
|
|
47
|
+
} from "./chunk-VOEJYRIK.js";
|
|
38
48
|
import {
|
|
39
49
|
getConfig,
|
|
40
50
|
getConfigDir
|
|
@@ -141,6 +151,18 @@ var clawops_statusAnnotations = {
|
|
|
141
151
|
openWorldHint: true,
|
|
142
152
|
toolsets: ["cli", "read"]
|
|
143
153
|
};
|
|
154
|
+
var clawops_doctorSchema = z.object({
|
|
155
|
+
stackName: z.string().optional(),
|
|
156
|
+
failuresOnly: z.boolean().optional().default(false)
|
|
157
|
+
});
|
|
158
|
+
var clawops_doctorAnnotations = {
|
|
159
|
+
title: "Run Diagnostics",
|
|
160
|
+
readOnlyHint: true,
|
|
161
|
+
destructiveHint: false,
|
|
162
|
+
idempotentHint: true,
|
|
163
|
+
openWorldHint: true,
|
|
164
|
+
toolsets: ["cli", "read"]
|
|
165
|
+
};
|
|
144
166
|
var clawops_logs_tailSchema = z.object({
|
|
145
167
|
stackName: z.string().optional(),
|
|
146
168
|
tailLines: z.number().int().optional().default(100),
|
|
@@ -202,7 +224,10 @@ var clawops_upSchema = z.object({
|
|
|
202
224
|
stackName: z.string().optional(),
|
|
203
225
|
provider: z.enum(["aws", "gcp", "azure", "local"]).optional(),
|
|
204
226
|
region: z.string().optional(),
|
|
205
|
-
instanceType: z.
|
|
227
|
+
instanceType: z.string().optional().default("small"),
|
|
228
|
+
sshCidr: z.string().optional(),
|
|
229
|
+
gatewayCidr: z.string().optional(),
|
|
230
|
+
publishGateway: z.enum(["loopback", "all"]).optional(),
|
|
206
231
|
openclawVersion: z.string().optional(),
|
|
207
232
|
dryRun: z.boolean().optional().default(false)
|
|
208
233
|
});
|
|
@@ -291,18 +316,6 @@ var clawops_config_validateAnnotations = {
|
|
|
291
316
|
openWorldHint: true,
|
|
292
317
|
toolsets: ["cli", "read"]
|
|
293
318
|
};
|
|
294
|
-
var clawops_agents_restartSchema = z.object({
|
|
295
|
-
stackName: z.string().optional(),
|
|
296
|
-
agentId: z.string()
|
|
297
|
-
});
|
|
298
|
-
var clawops_agents_restartAnnotations = {
|
|
299
|
-
title: "Restart OpenClaw Agent",
|
|
300
|
-
readOnlyHint: false,
|
|
301
|
-
destructiveHint: true,
|
|
302
|
-
idempotentHint: true,
|
|
303
|
-
openWorldHint: true,
|
|
304
|
-
toolsets: ["cli"]
|
|
305
|
-
};
|
|
306
319
|
var clawops_gateway_restartSchema = z.object({
|
|
307
320
|
stackName: z.string().optional()
|
|
308
321
|
});
|
|
@@ -353,6 +366,7 @@ var clawops_task_statusAnnotations = {
|
|
|
353
366
|
var TOOLSETS = {
|
|
354
367
|
cli: [
|
|
355
368
|
"clawops_status",
|
|
369
|
+
"clawops_doctor",
|
|
356
370
|
"clawops_logs_tail",
|
|
357
371
|
"clawops_monitor",
|
|
358
372
|
"clawops_config_get",
|
|
@@ -364,7 +378,6 @@ var TOOLSETS = {
|
|
|
364
378
|
"clawops_config_set",
|
|
365
379
|
"clawops_config_unset",
|
|
366
380
|
"clawops_config_validate",
|
|
367
|
-
"clawops_agents_restart",
|
|
368
381
|
"clawops_gateway_restart",
|
|
369
382
|
"clawops_task_status"
|
|
370
383
|
],
|
|
@@ -374,6 +387,7 @@ var TOOLSETS = {
|
|
|
374
387
|
],
|
|
375
388
|
read: [
|
|
376
389
|
"clawops_status",
|
|
390
|
+
"clawops_doctor",
|
|
377
391
|
"clawops_logs_tail",
|
|
378
392
|
"clawops_monitor",
|
|
379
393
|
"clawops_stacks_list",
|
|
@@ -421,32 +435,102 @@ function text(t) {
|
|
|
421
435
|
return { content: [{ type: "text", text: t }] };
|
|
422
436
|
}
|
|
423
437
|
|
|
424
|
-
// src/mcp/tools/
|
|
438
|
+
// src/mcp/tools/_conn.ts
|
|
439
|
+
async function resolveConn(ctx) {
|
|
440
|
+
if (ctx.adapter.name === "local") {
|
|
441
|
+
const state = ctx.localState;
|
|
442
|
+
if (!state) throw new StateError("Stack has no local state \u2014 run `clawops up` first.");
|
|
443
|
+
return {
|
|
444
|
+
host: state.sshHost,
|
|
445
|
+
port: state.sshPort,
|
|
446
|
+
user: state.sshUser,
|
|
447
|
+
privateKeyPath: state.privateKeyPath,
|
|
448
|
+
knownHostsPath: state.knownHostsPath
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
const { extractBaseOutputs: extractBaseOutputs2 } = await import("./outputs-DJHBY7EE.js");
|
|
452
|
+
const stack = await ctx.getStack();
|
|
453
|
+
const outputMap = await stack.outputs();
|
|
454
|
+
const outputs = Object.fromEntries(
|
|
455
|
+
Object.entries(outputMap).map(([k, v]) => [k, v.value])
|
|
456
|
+
);
|
|
457
|
+
if (!outputs["publicIp"]) {
|
|
458
|
+
throw new StateError("Stack has no outputs \u2014 run `clawops up` first.");
|
|
459
|
+
}
|
|
460
|
+
const base = extractBaseOutputs2(outputs);
|
|
461
|
+
return ctx.adapter.getConnectionInfo({
|
|
462
|
+
...base,
|
|
463
|
+
privateKeyPath: ctx.config.ssh.keyPath,
|
|
464
|
+
knownHostsPath: ctx.config.ssh.knownHostsPath
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
function errText(message) {
|
|
468
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
469
|
+
}
|
|
470
|
+
function okText(t) {
|
|
471
|
+
return { content: [{ type: "text", text: t }] };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/mcp/tools/_trim.ts
|
|
475
|
+
import { writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
476
|
+
import path2 from "path";
|
|
425
477
|
var MAX_BYTES = 8 * 1024;
|
|
478
|
+
function trimForMcp(output, stackName) {
|
|
479
|
+
try {
|
|
480
|
+
const dir = path2.join(getConfigDir(), "state");
|
|
481
|
+
mkdirSync2(dir, { recursive: true });
|
|
482
|
+
writeFileSync(path2.join(dir, `${stackName}.last-run.json`), JSON.stringify({ output }), "utf-8");
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
if (Buffer.byteLength(output) <= MAX_BYTES) {
|
|
486
|
+
return { content: output, truncated: false };
|
|
487
|
+
}
|
|
488
|
+
const trimmed = output.slice(0, MAX_BYTES);
|
|
489
|
+
return {
|
|
490
|
+
content: trimmed + `
|
|
491
|
+
|
|
492
|
+
[Output truncated at 8KB. Full output available at clawops://stacks/${stackName}/last-run]`,
|
|
493
|
+
truncated: true
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/mcp/tools/cli/doctor.ts
|
|
498
|
+
async function handleDoctor(input, _server) {
|
|
499
|
+
const ac = new AbortController();
|
|
500
|
+
const report = await runDiagnostics({ stack: input.stackName, signal: ac.signal });
|
|
501
|
+
const payload = input.failuresOnly ? failuresOnly(report) : report;
|
|
502
|
+
const { content } = trimForMcp(JSON.stringify(payload, null, 2), input.stackName ?? "local");
|
|
503
|
+
return okText(content);
|
|
504
|
+
}
|
|
505
|
+
function failuresOnly(report) {
|
|
506
|
+
const sections = report.sections.map((s) => ({ ...s, checks: s.checks.filter((c) => c.status === "fail" || c.status === "warn") })).filter((s) => s.checks.length > 0);
|
|
507
|
+
return { sections, ok: report.ok, counts: report.counts };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/mcp/tools/cli/logs.ts
|
|
511
|
+
var MAX_BYTES2 = 8 * 1024;
|
|
426
512
|
async function handleLogsTail(input, _server) {
|
|
427
513
|
const ctx = buildContext({ stack: input.stackName });
|
|
428
514
|
const conn = await resolveConn(ctx);
|
|
429
515
|
const { session, release } = await acquireSession(conn);
|
|
430
516
|
try {
|
|
431
517
|
const tailLines = input.tailLines ?? 100;
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
"|| docker logs openclaw",
|
|
439
|
-
`-n ${tailLines}`
|
|
440
|
-
].filter(Boolean).join(" ");
|
|
441
|
-
const result = await session.exec(command);
|
|
518
|
+
const since = input.sinceMin ? `${input.sinceMin}m` : void 0;
|
|
519
|
+
const probe = since ? { stdout: "" } : await execPrivileged(session, GATEWAY_LOGS_PROBE);
|
|
520
|
+
const choice = chooseLogSource({ since, gatewayReachable: probe.stdout.trim() === "ok" });
|
|
521
|
+
const opts = { tail: tailLines, follow: false, since, json: choice.source === "gateway" };
|
|
522
|
+
const command = choice.source === "gateway" ? gatewayLogsCommand(opts) : containerLogsCommand(opts);
|
|
523
|
+
const result = await execPrivileged(session, command);
|
|
442
524
|
if (result.code !== 0 && !result.stdout) {
|
|
443
525
|
return errText(`Failed to fetch logs: ${result.stderr}`);
|
|
444
526
|
}
|
|
445
|
-
let output =
|
|
446
|
-
|
|
447
|
-
|
|
527
|
+
let output = `[source: ${choice.source} \u2014 ${choice.reason}]
|
|
528
|
+
${result.stdout}`;
|
|
529
|
+
if (Buffer.byteLength(output) > MAX_BYTES2) {
|
|
530
|
+
output = output.slice(0, MAX_BYTES2) + "\n\n[output truncated at 8KB]";
|
|
448
531
|
}
|
|
449
|
-
return okText(output
|
|
532
|
+
return okText(result.stdout ? output : `[source: ${choice.source}]
|
|
533
|
+
(no log output)`);
|
|
450
534
|
} finally {
|
|
451
535
|
release();
|
|
452
536
|
drainPool();
|
|
@@ -469,42 +553,182 @@ async function handleStacksList(_input, _server) {
|
|
|
469
553
|
return { content: [{ type: "text", text: JSON.stringify({ stacks }, null, 2) }] };
|
|
470
554
|
}
|
|
471
555
|
|
|
472
|
-
// src/mcp/tools/cli/
|
|
473
|
-
async function
|
|
556
|
+
// src/mcp/tools/cli/config.ts
|
|
557
|
+
async function handleConfigGet(input, _server) {
|
|
558
|
+
const ac = new AbortController();
|
|
474
559
|
const ctx = buildContext({ stack: input.stackName });
|
|
475
560
|
const conn = await resolveConn(ctx);
|
|
476
561
|
const { session, release } = await acquireSession(conn);
|
|
477
562
|
try {
|
|
478
|
-
|
|
479
|
-
|
|
563
|
+
let cfg;
|
|
564
|
+
try {
|
|
565
|
+
cfg = await readRemoteConfig(session, ac.signal);
|
|
566
|
+
} catch (err) {
|
|
567
|
+
return errText(err.message);
|
|
568
|
+
}
|
|
569
|
+
const value = input.key ? getPath(cfg, input.key) : cfg;
|
|
570
|
+
return okText(JSON.stringify(value, null, 2));
|
|
480
571
|
} finally {
|
|
481
572
|
release();
|
|
482
573
|
drainPool();
|
|
483
574
|
}
|
|
484
575
|
}
|
|
485
|
-
async function
|
|
576
|
+
async function handleConfigSet(input, server) {
|
|
486
577
|
const elicit = await server.server.elicitInput({
|
|
487
|
-
message: `
|
|
578
|
+
message: `Set ${input.key} = ${input.value} on stack "${input.stackName ?? "default"}"?`,
|
|
488
579
|
requestedSchema: {
|
|
489
580
|
type: "object",
|
|
490
|
-
properties: { confirmed: { type: "boolean", title: "Confirm
|
|
581
|
+
properties: { confirmed: { type: "boolean", title: "Confirm config change" } },
|
|
491
582
|
required: ["confirmed"]
|
|
492
583
|
}
|
|
493
584
|
});
|
|
494
585
|
if (elicit.action !== "accept" || !elicit.content?.["confirmed"]) {
|
|
495
|
-
return okText("
|
|
586
|
+
return okText("Config change cancelled.");
|
|
496
587
|
}
|
|
588
|
+
const ac = new AbortController();
|
|
497
589
|
const ctx = buildContext({ stack: input.stackName });
|
|
498
590
|
const conn = await resolveConn(ctx);
|
|
499
591
|
const { session, release } = await acquireSession(conn);
|
|
500
592
|
try {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
593
|
+
let cfg;
|
|
594
|
+
try {
|
|
595
|
+
cfg = await readRemoteConfig(session, ac.signal);
|
|
596
|
+
} catch (err) {
|
|
597
|
+
return errText(err.message);
|
|
598
|
+
}
|
|
599
|
+
let parsedValue = input.value;
|
|
600
|
+
try {
|
|
601
|
+
parsedValue = JSON.parse(input.value);
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
setPath(cfg, input.key, parsedValue);
|
|
605
|
+
try {
|
|
606
|
+
await atomicWriteConfig(session, cfg, ac.signal);
|
|
607
|
+
} catch (err) {
|
|
608
|
+
return errText(`Failed to write config: ${err.message}`);
|
|
609
|
+
}
|
|
610
|
+
let note = "";
|
|
611
|
+
if (input.restart) {
|
|
612
|
+
try {
|
|
613
|
+
await restartGateway(session, ac.signal);
|
|
614
|
+
note = " (gateway restarted)";
|
|
615
|
+
} catch (err) {
|
|
616
|
+
return errText(`Gateway restart failed: ${err.message}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return okText(`Config set: ${input.key}${note}`);
|
|
620
|
+
} finally {
|
|
621
|
+
release();
|
|
622
|
+
drainPool();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
async function handleConfigUnset(input, server) {
|
|
626
|
+
const elicit = await server.server.elicitInput({
|
|
627
|
+
message: `Remove config key "${input.key}" on stack "${input.stackName ?? "default"}"?`,
|
|
628
|
+
requestedSchema: {
|
|
629
|
+
type: "object",
|
|
630
|
+
properties: { confirmed: { type: "boolean", title: "Confirm key removal" } },
|
|
631
|
+
required: ["confirmed"]
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
if (elicit.action !== "accept" || !elicit.content?.["confirmed"]) {
|
|
635
|
+
return okText("Config unset cancelled.");
|
|
636
|
+
}
|
|
637
|
+
const ac = new AbortController();
|
|
638
|
+
const ctx = buildContext({ stack: input.stackName });
|
|
639
|
+
const conn = await resolveConn(ctx);
|
|
640
|
+
const { session, release } = await acquireSession(conn);
|
|
641
|
+
try {
|
|
642
|
+
let cfg;
|
|
643
|
+
try {
|
|
644
|
+
cfg = await readRemoteConfig(session, ac.signal);
|
|
645
|
+
} catch (err) {
|
|
646
|
+
return errText(err.message);
|
|
647
|
+
}
|
|
648
|
+
deletePath(cfg, input.key);
|
|
649
|
+
try {
|
|
650
|
+
await atomicWriteConfig(session, cfg, ac.signal);
|
|
651
|
+
} catch (err) {
|
|
652
|
+
return errText(`Failed to write config: ${err.message}`);
|
|
653
|
+
}
|
|
654
|
+
let note = "";
|
|
655
|
+
if (input.restart) {
|
|
656
|
+
try {
|
|
657
|
+
await restartGateway(session, ac.signal);
|
|
658
|
+
note = " (gateway restarted)";
|
|
659
|
+
} catch (err) {
|
|
660
|
+
return errText(`Gateway restart failed: ${err.message}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return okText(`Config key removed: ${input.key}${note}`);
|
|
664
|
+
} finally {
|
|
665
|
+
release();
|
|
666
|
+
drainPool();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
async function handleConfigValidate(input, _server) {
|
|
670
|
+
const ac = new AbortController();
|
|
671
|
+
const ctx = buildContext({ stack: input.stackName });
|
|
672
|
+
const conn = await resolveConn(ctx);
|
|
673
|
+
const { session, release } = await acquireSession(conn);
|
|
674
|
+
try {
|
|
675
|
+
let cfg;
|
|
676
|
+
try {
|
|
677
|
+
cfg = await readRemoteConfig(session, ac.signal);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
return okText(JSON.stringify({ valid: false, issues: [err.message] }));
|
|
680
|
+
}
|
|
681
|
+
const { validateConfig } = await import("./config-validate-K2N6XLDX.js");
|
|
682
|
+
const yaml = await import("./js-yaml-PTEEG4FO.js");
|
|
683
|
+
const { loadVersionSpec } = await import("./versions-W5LRQCAJ.js");
|
|
684
|
+
const spec = loadVersionSpec(yaml);
|
|
685
|
+
const { errors, warnings } = await validateConfig(cfg, {
|
|
686
|
+
schemaCapturedFrom: spec.runtime?.configSchemaCapturedFrom
|
|
687
|
+
});
|
|
688
|
+
return okText(JSON.stringify({ valid: errors.length === 0, issues: errors, warnings }));
|
|
689
|
+
} finally {
|
|
690
|
+
release();
|
|
691
|
+
drainPool();
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
function getPath(obj, dotKey) {
|
|
695
|
+
return dotKey.split(".").reduce((cur, k) => {
|
|
696
|
+
if (cur !== null && typeof cur === "object") return cur[k];
|
|
697
|
+
return void 0;
|
|
698
|
+
}, obj);
|
|
699
|
+
}
|
|
700
|
+
function setPath(obj, dotKey, value) {
|
|
701
|
+
const keys = dotKey.split(".");
|
|
702
|
+
let cur = obj;
|
|
703
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
704
|
+
const k = keys[i];
|
|
705
|
+
if (typeof cur[k] !== "object" || cur[k] === null) cur[k] = {};
|
|
706
|
+
cur = cur[k];
|
|
707
|
+
}
|
|
708
|
+
cur[keys[keys.length - 1]] = value;
|
|
709
|
+
}
|
|
710
|
+
function deletePath(obj, dotKey) {
|
|
711
|
+
const keys = dotKey.split(".");
|
|
712
|
+
let cur = obj;
|
|
713
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
714
|
+
const k = keys[i];
|
|
715
|
+
if (typeof cur[k] !== "object" || cur[k] === null) return;
|
|
716
|
+
cur = cur[k];
|
|
717
|
+
}
|
|
718
|
+
delete cur[keys[keys.length - 1]];
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/mcp/tools/cli/agents.ts
|
|
722
|
+
async function handleAgentsList(input, _server) {
|
|
723
|
+
const ctx = buildContext({ stack: input.stackName });
|
|
724
|
+
const conn = await resolveConn(ctx);
|
|
725
|
+
const { session, release } = await acquireSession(conn);
|
|
726
|
+
try {
|
|
727
|
+
const result = await execPrivileged(session, "docker exec openclaw openclaw agents list --json");
|
|
504
728
|
if (result.code !== 0) {
|
|
505
|
-
return errText(`
|
|
729
|
+
return errText(`Cannot list agents: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`}`);
|
|
506
730
|
}
|
|
507
|
-
return okText(
|
|
731
|
+
return okText(result.stdout.trim() || "[]");
|
|
508
732
|
} finally {
|
|
509
733
|
release();
|
|
510
734
|
drainPool();
|
|
@@ -528,11 +752,12 @@ async function handleGatewayRestart(input, server) {
|
|
|
528
752
|
const conn = await resolveConn(ctx);
|
|
529
753
|
const { session, release } = await acquireSession(conn);
|
|
530
754
|
try {
|
|
531
|
-
const imgResult = await session
|
|
755
|
+
const imgResult = await execPrivileged(session, IMAGE_INSPECT_CMD);
|
|
532
756
|
const image = imageForRestart(imgResult.stdout);
|
|
533
757
|
if (!image.ok) return errText(image.error);
|
|
534
|
-
const result = await
|
|
535
|
-
|
|
758
|
+
const result = await execPrivileged(
|
|
759
|
+
session,
|
|
760
|
+
gatewayRunCommand({ image: image.value, stateDir: STATE_DIR_HOST_LINUX })
|
|
536
761
|
);
|
|
537
762
|
if (result.code !== 0) {
|
|
538
763
|
return errText(`Gateway restart failed: ${result.stderr}`);
|
|
@@ -578,31 +803,7 @@ function makeProgressEmitter(server, progressToken) {
|
|
|
578
803
|
};
|
|
579
804
|
}
|
|
580
805
|
|
|
581
|
-
// src/mcp/tools/_trim.ts
|
|
582
|
-
import { writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
583
|
-
import path2 from "path";
|
|
584
|
-
var MAX_BYTES2 = 8 * 1024;
|
|
585
|
-
function trimForMcp(output, stackName) {
|
|
586
|
-
try {
|
|
587
|
-
const dir = path2.join(getConfigDir(), "state");
|
|
588
|
-
mkdirSync2(dir, { recursive: true });
|
|
589
|
-
writeFileSync(path2.join(dir, `${stackName}.last-run.json`), JSON.stringify({ output }), "utf-8");
|
|
590
|
-
} catch {
|
|
591
|
-
}
|
|
592
|
-
if (Buffer.byteLength(output) <= MAX_BYTES2) {
|
|
593
|
-
return { content: output, truncated: false };
|
|
594
|
-
}
|
|
595
|
-
const trimmed = output.slice(0, MAX_BYTES2);
|
|
596
|
-
return {
|
|
597
|
-
content: trimmed + `
|
|
598
|
-
|
|
599
|
-
[Output truncated at 8KB. Full output available at clawops://stacks/${stackName}/last-run]`,
|
|
600
|
-
truncated: true
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
|
|
604
806
|
// src/mcp/tools/cli/up.ts
|
|
605
|
-
var VALID_INSTANCE_TYPES = ["micro", "small", "medium", "large", "gpu"];
|
|
606
807
|
async function handleUp(input, server) {
|
|
607
808
|
if (!input.dryRun) {
|
|
608
809
|
const elicit = await server.server.elicitInput({
|
|
@@ -627,7 +828,7 @@ async function handleUp(input, server) {
|
|
|
627
828
|
);
|
|
628
829
|
}
|
|
629
830
|
const { localOpts } = stackConfig;
|
|
630
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
831
|
+
const { localBootstrap } = await import("./bootstrap-HNGKY6WW.js");
|
|
631
832
|
const ac = new AbortController();
|
|
632
833
|
const state = await localBootstrap({
|
|
633
834
|
host: localOpts.host,
|
|
@@ -642,10 +843,6 @@ async function handleUp(input, server) {
|
|
|
642
843
|
});
|
|
643
844
|
return okText(JSON.stringify({ stack: ctx.stackName, ...state }, null, 2));
|
|
644
845
|
}
|
|
645
|
-
const instanceAlias = input.instanceType ?? "small";
|
|
646
|
-
if (!VALID_INSTANCE_TYPES.includes(instanceAlias)) {
|
|
647
|
-
throw new UsageError(`Invalid instanceType: ${instanceAlias}`);
|
|
648
|
-
}
|
|
649
846
|
const validation = await ctx.adapter.validateConfig();
|
|
650
847
|
if (!validation.ok) {
|
|
651
848
|
return errText(`Provider config invalid: ${validation.errors.join("; ")}`);
|
|
@@ -654,38 +851,47 @@ async function handleUp(input, server) {
|
|
|
654
851
|
const progressToken = void 0;
|
|
655
852
|
const emit = makeProgressEmitter(server, progressToken);
|
|
656
853
|
startTask(taskId, `clawops_up stack=${ctx.stackName}`);
|
|
657
|
-
const
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
await
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const output = lines.join("");
|
|
670
|
-
updateTask(taskId, "completed", output);
|
|
671
|
-
const { content } = trimForMcp(output, ctx.stackName);
|
|
672
|
-
return okText(content);
|
|
673
|
-
}
|
|
854
|
+
const { generatePlan: generatePlan2 } = await import("./generate-I6YQ5V2T.js");
|
|
855
|
+
const { applyPlan: applyPlan2 } = await import("./apply-KYFPWI5U.js");
|
|
856
|
+
const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
|
|
857
|
+
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
858
|
+
const network = await resolveNetworkFlags(
|
|
859
|
+
{
|
|
860
|
+
...input.sshCidr ? { sshCidr: input.sshCidr } : {},
|
|
861
|
+
...input.gatewayCidr ? { gatewayCidr: input.gatewayCidr } : {},
|
|
862
|
+
...input.publishGateway ? { publishGateway: input.publishGateway } : {}
|
|
863
|
+
},
|
|
864
|
+
{ detectEgressIp: () => detectEgressIp("https://ifconfig.me/ip") }
|
|
865
|
+
);
|
|
674
866
|
try {
|
|
867
|
+
const plan = await generatePlan2({
|
|
868
|
+
stackName: ctx.stackName,
|
|
869
|
+
provider: ctx.adapter.name,
|
|
870
|
+
...input.region ? { region: input.region } : {},
|
|
871
|
+
...input.instanceType ? { instanceType: input.instanceType } : {},
|
|
872
|
+
openclawVersion,
|
|
873
|
+
network
|
|
874
|
+
});
|
|
875
|
+
if (input.dryRun) {
|
|
876
|
+
const summary2 = JSON.stringify(plan.diff ?? { totalChanges: 0 }, null, 2);
|
|
877
|
+
updateTask(taskId, "completed", summary2);
|
|
878
|
+
const { content } = trimForMcp(summary2, ctx.stackName);
|
|
879
|
+
return okText(content);
|
|
880
|
+
}
|
|
675
881
|
const lines = [];
|
|
676
|
-
const result = await
|
|
882
|
+
const result = await applyPlan2(plan, {
|
|
677
883
|
onOutput: (o) => {
|
|
678
884
|
emit(o.trim());
|
|
679
885
|
lines.push(o);
|
|
680
|
-
}
|
|
886
|
+
},
|
|
887
|
+
onProgress: (o) => emit(o)
|
|
681
888
|
});
|
|
682
889
|
const summary = `Stack "${ctx.stackName}" deployed.
|
|
683
|
-
` + (result.outputs["publicIp"] ? `Public IP: ${result.outputs["publicIp"]
|
|
684
|
-
` : "") + (result.outputs["gatewayUrl"] ? `Gateway URL: ${result.outputs["gatewayUrl"]
|
|
890
|
+
` + (result.outputs["publicIp"] ? `Public IP: ${String(result.outputs["publicIp"])}
|
|
891
|
+
` : "") + (result.outputs["gatewayUrl"] ? `Gateway URL: ${String(result.outputs["gatewayUrl"])}
|
|
685
892
|
` : "");
|
|
686
893
|
updateTask(taskId, "completed", summary);
|
|
687
|
-
|
|
688
|
-
trimForMcp(fullOutput, ctx.stackName);
|
|
894
|
+
trimForMcp(lines.join(""), ctx.stackName);
|
|
689
895
|
return okText(summary);
|
|
690
896
|
} catch (err) {
|
|
691
897
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -850,7 +1056,7 @@ async function handleTaskStatus(input, _server) {
|
|
|
850
1056
|
|
|
851
1057
|
// src/mcp/tools/cli/monitor.ts
|
|
852
1058
|
async function handleMonitor(input, _server) {
|
|
853
|
-
const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-
|
|
1059
|
+
const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-PIIKJ4L2.js");
|
|
854
1060
|
const ctx = buildContext({ stack: input.stackName });
|
|
855
1061
|
const tailLines = input.tailLines ?? 5;
|
|
856
1062
|
let conn;
|
|
@@ -1015,6 +1221,7 @@ function makeEntry(schema, annotations, handler) {
|
|
|
1015
1221
|
}
|
|
1016
1222
|
var TOOL_REGISTRY = {
|
|
1017
1223
|
clawops_status: makeEntry(clawops_statusSchema, clawops_statusAnnotations, handleStatus),
|
|
1224
|
+
clawops_doctor: makeEntry(clawops_doctorSchema, clawops_doctorAnnotations, handleDoctor),
|
|
1018
1225
|
clawops_logs_tail: makeEntry(clawops_logs_tailSchema, clawops_logs_tailAnnotations, handleLogsTail),
|
|
1019
1226
|
clawops_monitor: makeEntry(clawops_monitorSchema, clawops_monitorAnnotations, handleMonitor),
|
|
1020
1227
|
clawops_stacks_list: makeEntry(clawops_stacks_listSchema, clawops_stacks_listAnnotations, handleStacksList),
|
|
@@ -1027,12 +1234,12 @@ var TOOL_REGISTRY = {
|
|
|
1027
1234
|
clawops_config_set: makeEntry(clawops_config_setSchema, clawops_config_setAnnotations, handleConfigSet),
|
|
1028
1235
|
clawops_config_unset: makeEntry(clawops_config_unsetSchema, clawops_config_unsetAnnotations, handleConfigUnset),
|
|
1029
1236
|
clawops_config_validate: makeEntry(clawops_config_validateSchema, clawops_config_validateAnnotations, handleConfigValidate),
|
|
1030
|
-
clawops_agents_restart: makeEntry(clawops_agents_restartSchema, clawops_agents_restartAnnotations, handleAgentsRestart),
|
|
1031
1237
|
clawops_gateway_restart: makeEntry(clawops_gateway_restartSchema, clawops_gateway_restartAnnotations, handleGatewayRestart),
|
|
1032
1238
|
clawops_workflow_deploy_app: makeEntry(clawops_workflow_deploy_appSchema, clawops_workflow_deploy_appAnnotations, handleWorkflowDeployApp),
|
|
1033
1239
|
clawops_workflow_recover: makeEntry(clawops_workflow_recoverSchema, clawops_workflow_recoverAnnotations, handleWorkflowRecover),
|
|
1034
1240
|
clawops_task_status: makeEntry(clawops_task_statusSchema, clawops_task_statusAnnotations, handleTaskStatus)
|
|
1035
1241
|
};
|
|
1242
|
+
var TOOL_NAMES = Object.keys(TOOL_REGISTRY);
|
|
1036
1243
|
function resolveEnabledTools(opts) {
|
|
1037
1244
|
let names;
|
|
1038
1245
|
if (opts.readOnly) {
|
|
@@ -1258,7 +1465,7 @@ clawops_gateway_restart({ stackName: "${s}" })
|
|
|
1258
1465
|
### Agents not running
|
|
1259
1466
|
\`\`\`
|
|
1260
1467
|
clawops_agents_list({ stackName: "${s}" })
|
|
1261
|
-
|
|
1468
|
+
clawops_gateway_restart({ stackName: "${s}" })
|
|
1262
1469
|
\`\`\`
|
|
1263
1470
|
|
|
1264
1471
|
### Bootstrap failed or incomplete
|
|
@@ -1325,7 +1532,7 @@ function bearerFrom(header) {
|
|
|
1325
1532
|
return match?.[1]?.trim();
|
|
1326
1533
|
}
|
|
1327
1534
|
async function serveMcp(opts) {
|
|
1328
|
-
const { version } = await import("./package-
|
|
1535
|
+
const { version } = await import("./package-5BCMEBTE.js");
|
|
1329
1536
|
const makeServer = () => {
|
|
1330
1537
|
const server = new McpServer({ name: "clawops", version });
|
|
1331
1538
|
registerTools(server, opts);
|