@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.
Files changed (98) hide show
  1. package/README.md +504 -76
  2. package/dist/apply-KYFPWI5U.js +27 -0
  3. package/dist/automation-RMAIBNVC.js +44 -0
  4. package/dist/bedrock-XVASJX2K.js +61 -0
  5. package/dist/{bootstrap-HL4DNUNG.js → bootstrap-HNGKY6WW.js} +31 -19
  6. package/dist/bootstrap.sh.tmpl +51 -31
  7. package/dist/channels-KBUR42IU.js +53 -0
  8. package/dist/chunk-2I7KCMXZ.js +142 -0
  9. package/dist/{chunk-DQDAWNGB.js → chunk-2Z7J3ERQ.js} +1 -1
  10. package/dist/chunk-3OLUJPCB.js +36 -0
  11. package/dist/chunk-5XT5IAJK.js +69 -0
  12. package/dist/chunk-AEEMRFZV.js +24 -0
  13. package/dist/chunk-BSLIYB5Q.js +106 -0
  14. package/dist/{chunk-3XK66DKK.js → chunk-E4NJSJ54.js} +57 -93
  15. package/dist/chunk-FEK7VJAY.js +16 -0
  16. package/dist/{chunk-UKTP332Z.js → chunk-GAZPPSKU.js} +6 -7
  17. package/dist/chunk-GIEVAGEK.js +126 -0
  18. package/dist/chunk-HSPHXG6W.js +67 -0
  19. package/dist/chunk-IVWP4QUJ.js +901 -0
  20. package/dist/{chunk-PVUVI35Y.js → chunk-JW5A7E3X.js} +5 -1
  21. package/dist/{chunk-GZN7PKP4.js → chunk-KC3GC3PE.js} +328 -121
  22. package/dist/chunk-LBE6COC4.js +244 -0
  23. package/dist/chunk-LEVIW2PV.js +890 -0
  24. package/dist/chunk-LT4XMO5D.js +50 -0
  25. package/dist/chunk-MC5ATCXS.js +108 -0
  26. package/dist/chunk-N4WZLWJ4.js +16 -0
  27. package/dist/chunk-NUDIHZEH.js +55 -0
  28. package/dist/chunk-NUL5F6YV.js +56 -0
  29. package/dist/chunk-RHOWYIWT.js +52 -0
  30. package/dist/chunk-STLRHLLV.js +193 -0
  31. package/dist/chunk-V25QRZRB.js +45 -0
  32. package/dist/chunk-VOEJYRIK.js +33 -0
  33. package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
  34. package/dist/chunk-W3OLNNJW.js +51 -0
  35. package/dist/cli-WLD3EBEM.js +11 -0
  36. package/dist/cli-auth-GD5SK7JU.js +13 -0
  37. package/dist/cli.js +1296 -695
  38. package/dist/config-validate-K2N6XLDX.js +76 -0
  39. package/dist/context-7EVC6W45.js +20 -0
  40. package/dist/docker-3PDO4TS7.js +12 -0
  41. package/dist/{firewall-XQGO7HWK.js → firewall-5VKZ4WBH.js} +32 -1
  42. package/dist/generate-I6YQ5V2T.js +29 -0
  43. package/dist/{harden-URPXLSVF.js → harden-72JMRT4E.js} +51 -11
  44. package/dist/health-5CCOXYGG.js +11 -0
  45. package/dist/index.d.ts +56 -0
  46. package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
  47. package/dist/logs-5A7YYA53.js +15 -0
  48. package/dist/{mcp-wire-7E5JQY47.js → mcp-wire-3SWBM7D6.js} +32 -16
  49. package/dist/models-AGTQWYDL.js +32 -0
  50. package/dist/network-args-PYJRJBYO.js +64 -0
  51. package/dist/{package-5AR4ZPK4.js → package-5BCMEBTE.js} +11 -4
  52. package/dist/passphrase-MJBE45N7.js +15 -0
  53. package/dist/plugins-DTST3OER.js +49 -0
  54. package/dist/{pool-QGFQRMGU.js → pool-PIIKJ4L2.js} +3 -2
  55. package/dist/preflight-EF52S3WW.js +162 -0
  56. package/dist/preflight-MWWM5JT3.js +181 -0
  57. package/dist/preflight-TFD4N5ZU.js +141 -0
  58. package/dist/privileged-JFA6LLTV.js +17 -0
  59. package/dist/providers-FW6H6OPF.js +11 -0
  60. package/dist/ready-KDNRBNSU.js +95 -0
  61. package/dist/{remote-config-QDLN4UMG.js → remote-config-3HZLUWEA.js} +4 -3
  62. package/dist/runtime-X3KW5TCP.js +44 -0
  63. package/dist/server-GBA7BJGK.js +46 -0
  64. package/dist/spec-path-KQOOIQCA.js +9 -0
  65. package/dist/ssh-VAI5DU5M.js +11 -0
  66. package/dist/ssh-key-DUO6OKOI.js +7 -0
  67. package/dist/stack-config-R4SPSUQZ.js +12 -0
  68. package/dist/upgrade-ULD4KFYC.js +88 -0
  69. package/dist/validate-YVI3BJ55.js +14 -0
  70. package/dist/{version-guard-VUCNZXQS.js → version-guard-XVYW5WFJ.js} +2 -2
  71. package/dist/{versions-2UVAT6PQ.js → versions-W5LRQCAJ.js} +2 -2
  72. package/dist/wait-WO72TRRQ.js +79 -0
  73. package/package.json +11 -4
  74. package/spec/deploy-plan.schema.json +114 -25
  75. package/spec/integrations.yaml +135 -28
  76. package/spec/mcp-tools.yaml +89 -37
  77. package/spec/models.yaml +23 -3
  78. package/spec/openclaw-2.0.config.schema.json +1 -0
  79. package/spec/openclaw-versions.yaml +116 -27
  80. package/spec/providers.schema.json +163 -28
  81. package/dist/apply-F3KY2HTB.js +0 -17
  82. package/dist/automation-RULUSJBW.js +0 -30
  83. package/dist/aws-L4RR2LSE.js +0 -254
  84. package/dist/azure-7OXQENQL.js +0 -262
  85. package/dist/chunk-35WVQY3X.js +0 -122
  86. package/dist/chunk-6X45DDGH.js +0 -42
  87. package/dist/chunk-A4QR4TXW.js +0 -120
  88. package/dist/chunk-C5X375RO.js +0 -85
  89. package/dist/chunk-FNS56UQY.js +0 -741
  90. package/dist/chunk-P5LKWRPL.js +0 -114
  91. package/dist/chunk-SEZMVVAS.js +0 -156
  92. package/dist/context-5LCUG2QR.js +0 -10
  93. package/dist/gcp-UG6AWPIZ.js +0 -193
  94. package/dist/generate-OAGYT3VH.js +0 -15
  95. package/dist/local-DXBEVZ5C.js +0 -47
  96. package/dist/server-FIZCOLHL.js +0 -32
  97. package/dist/ssh-CNJNLM3E.js +0 -8
  98. 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-35WVQY3X.js";
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-A4QR4TXW.js";
15
+ } from "./chunk-STLRHLLV.js";
11
16
  import {
12
17
  validatePlan
13
- } from "./chunk-6X45DDGH.js";
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
- handleConfigGet,
19
- handleConfigSet,
20
- handleConfigUnset,
21
- handleConfigValidate,
22
- okText,
23
- resolveConn
24
- } from "./chunk-FNS56UQY.js";
29
+ runDiagnostics
30
+ } from "./chunk-IVWP4QUJ.js";
25
31
  import {
26
- acquireSession,
27
- drainPool
28
- } from "./chunk-DQDAWNGB.js";
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-SEZMVVAS.js";
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.enum(["micro", "small", "medium", "large", "gpu"]).optional().default("small"),
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/cli/logs.ts
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 sinceFlag = input.sinceMin ? `--since "${input.sinceMin} min ago"` : "";
433
- const command = [
434
- "journalctl -u openclaw",
435
- `-n ${tailLines}`,
436
- sinceFlag,
437
- "2>/dev/null",
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 = result.stdout;
446
- if (Buffer.byteLength(output) > MAX_BYTES) {
447
- output = output.slice(0, MAX_BYTES) + "\n\n[output truncated at 8KB]";
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 || "(no log 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/agents.ts
473
- async function handleAgentsList(input, _server) {
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
- const result = await session.exec('docker exec openclaw openclaw agents list --json 2>&1 || echo "[]"');
479
- return okText(result.stdout.trim());
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 handleAgentsRestart(input, server) {
576
+ async function handleConfigSet(input, server) {
486
577
  const elicit = await server.server.elicitInput({
487
- message: `Restart agent "${input.agentId}" on stack "${input.stackName ?? "default"}"?`,
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 restart" } },
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("Agent restart cancelled.");
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
- const result = await session.exec(
502
- `docker exec openclaw openclaw agents restart ${input.agentId} 2>&1`
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(`Failed to restart agent: ${result.stderr || result.stdout}`);
729
+ return errText(`Cannot list agents: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`}`);
506
730
  }
507
- return okText(`Agent "${input.agentId}" restarted.`);
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.exec(IMAGE_INSPECT_CMD);
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 session.exec(
535
- gatewayRunCommand({ image: image.value, configPath: OPENCLAW_CONFIG })
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-HL4DNUNG.js");
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 stack = await ctx.getStack();
658
- const region = input.region ?? ctx.adapter.defaultRegion();
659
- const instanceType = ctx.adapter.normalizeInstanceType(instanceAlias);
660
- await stack.setConfig("region", { value: region });
661
- await stack.setConfig("instanceType", { value: instanceType });
662
- await stack.setConfig("openclawVersion", { value: openclawVersion });
663
- if (input.dryRun) {
664
- const lines = [];
665
- await stack.preview({ onOutput: (o) => {
666
- emit(o.trim());
667
- lines.push(o);
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 stack.up({
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"].value}
684
- ` : "") + (result.outputs["gatewayUrl"] ? `Gateway URL: ${result.outputs["gatewayUrl"].value}
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
- const fullOutput = lines.join("");
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-QGFQRMGU.js");
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
- clawops_agents_restart({ stackName: "${s}" })
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-5AR4ZPK4.js");
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);