@clawops/cli 2.1.3 → 2.2.0

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 (51) hide show
  1. package/README.md +120 -2
  2. package/dist/agent-activity-25FAWLAI.js +16 -0
  3. package/dist/{apply-JARPVA5Y.js → apply-F5SQ2ZOS.js} +7 -7
  4. package/dist/backup-flows-NDP5JTR3.js +29 -0
  5. package/dist/{bootstrap-3PBVY7MA.js → bootstrap-32TZXVDF.js} +3 -3
  6. package/dist/bootstrap.sh.tmpl +4 -0
  7. package/dist/chunk-27335SIH.js +51 -0
  8. package/dist/{chunk-E4NJSJ54.js → chunk-4PY6PN4G.js} +51 -7
  9. package/dist/{chunk-PTCVFLZ3.js → chunk-66R52R7K.js} +7 -11
  10. package/dist/{chunk-7T4CIT7C.js → chunk-AX65JMDZ.js} +15 -15
  11. package/dist/{chunk-TYHTISI5.js → chunk-CQOYUWR7.js} +610 -104
  12. package/dist/chunk-E6BRQK67.js +289 -0
  13. package/dist/chunk-LV7UO5CG.js +82 -0
  14. package/dist/{chunk-GIEVAGEK.js → chunk-N5ZYUTZS.js} +16 -1
  15. package/dist/{chunk-CPCO6UJK.js → chunk-OUQVKIIA.js} +368 -30
  16. package/dist/{chunk-XFN3YDZE.js → chunk-PASAVVNS.js} +1 -1
  17. package/dist/chunk-PCSX6Y3U.js +297 -0
  18. package/dist/chunk-POYS3EVQ.js +14 -0
  19. package/dist/chunk-SX75L2T3.js +46 -0
  20. package/dist/{chunk-2Z7J3ERQ.js → chunk-TYNMOYVM.js} +1 -1
  21. package/dist/{chunk-2I7KCMXZ.js → chunk-UAJJEDCJ.js} +2 -2
  22. package/dist/{chunk-NUDIHZEH.js → chunk-WPZIJONN.js} +14 -12
  23. package/dist/{chunk-CHNSL3TC.js → chunk-YPVWF7IA.js} +1 -1
  24. package/dist/{chunk-3OLUJPCB.js → chunk-Z3XLWZWF.js} +1 -1
  25. package/dist/cli.js +303 -698
  26. package/dist/conn-M2V5HBPJ.js +9 -0
  27. package/dist/{context-WOKSKAIA.js → context-EHSLWKSH.js} +2 -2
  28. package/dist/{docker-3PDO4TS7.js → docker-IKM5GRMC.js} +2 -2
  29. package/dist/{flows-HGJIA6EL.js → flows-6B2I6TRW.js} +5 -5
  30. package/dist/{generate-NE54BATS.js → generate-4YYNW3IU.js} +5 -4
  31. package/dist/{harden-RUOUA5BV.js → harden-UHAOVQMZ.js} +3 -3
  32. package/dist/{mcp-wire-FIO5DFBY.js → mcp-wire-BXF4DJFT.js} +21 -14
  33. package/dist/migrate-flow-24XA4GZL.js +25 -0
  34. package/dist/{package-MF5LPITL.js → package-HYGHRX35.js} +3 -1
  35. package/dist/{pool-PIIKJ4L2.js → pool-NT4XBSUT.js} +2 -2
  36. package/dist/{privileged-JFA6LLTV.js → privileged-5Y6O4H7Z.js} +1 -1
  37. package/dist/{ready-KDNRBNSU.js → ready-IJEUBFVX.js} +2 -2
  38. package/dist/{remote-config-3HZLUWEA.js → remote-config-OZYBMBSK.js} +3 -3
  39. package/dist/{runtime-X3KW5TCP.js → runtime-VW53TU2W.js} +5 -1
  40. package/dist/{server-OXWHBTXK.js → server-E3YA32ND.js} +21 -14
  41. package/dist/ssh-HOM7GF3M.js +13 -0
  42. package/dist/stack-delete-NTIGKWUF.js +13 -0
  43. package/dist/{tailscale-cutover-EKAQSDXP.js → tailscale-cutover-V42HO5UM.js} +1 -1
  44. package/dist/{validate-JXKN5WD6.js → validate-DZE6LFEB.js} +1 -1
  45. package/dist/{wait-WO72TRRQ.js → wait-DOUNNJNN.js} +2 -2
  46. package/package.json +3 -1
  47. package/spec/mcp-tools.yaml +213 -7
  48. package/dist/logs-5A7YYA53.js +0 -15
  49. package/dist/ssh-VAI5DU5M.js +0 -11
  50. package/dist/upgrade-ULD4KFYC.js +0 -88
  51. /package/dist/{chunk-NUL5F6YV.js → chunk-SOZ62ZXW.js} +0 -0
@@ -1,10 +1,39 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ migrationQuestion,
4
+ prepareMigration,
5
+ runMigration
6
+ } from "./chunk-E6BRQK67.js";
7
+ import {
8
+ ACTIVATED,
9
+ ARCHIVE_IS_A_CREDENTIAL,
10
+ PLUGINS_NOT_CARRIED,
11
+ activateStaged,
12
+ activatedLines,
13
+ activationFailureDetail,
14
+ createBackup,
15
+ defaultBackupFilename,
16
+ nothingChanged,
17
+ stageRestore
18
+ } from "./chunk-PCSX6Y3U.js";
19
+ import {
20
+ checkStackDelete,
21
+ deleteStackFromConfig,
22
+ forgetNotice
23
+ } from "./chunk-LV7UO5CG.js";
2
24
  import {
3
25
  applyPlan
4
- } from "./chunk-7T4CIT7C.js";
26
+ } from "./chunk-AX65JMDZ.js";
27
+ import {
28
+ resolveConn
29
+ } from "./chunk-SX75L2T3.js";
30
+ import {
31
+ DEFAULT_AGENT_LOG_LIMIT,
32
+ readAgentActivity
33
+ } from "./chunk-27335SIH.js";
5
34
  import {
6
35
  generatePlan
7
- } from "./chunk-PTCVFLZ3.js";
36
+ } from "./chunk-66R52R7K.js";
8
37
  import {
9
38
  extractBaseOutputs
10
39
  } from "./chunk-3QJBNAHW.js";
@@ -12,39 +41,44 @@ import {
12
41
  atomicWriteConfig,
13
42
  readRemoteConfig,
14
43
  restartGateway
15
- } from "./chunk-2I7KCMXZ.js";
44
+ } from "./chunk-UAJJEDCJ.js";
16
45
  import {
17
46
  validatePlan
18
- } from "./chunk-XFN3YDZE.js";
47
+ } from "./chunk-PASAVVNS.js";
19
48
  import {
20
49
  buildContext
21
50
  } from "./chunk-AVERNLUY.js";
22
51
  import {
23
52
  acquireSession,
24
53
  drainPool
25
- } from "./chunk-2Z7J3ERQ.js";
54
+ } from "./chunk-TYNMOYVM.js";
26
55
  import {
56
+ AUDIT_CLEAN_MESSAGE,
57
+ AUDIT_FIX_HINT,
58
+ NO_SECRETS_MESSAGE,
59
+ SET_SECRET_HINT,
60
+ auditSecrets,
61
+ currentGatewayVersion,
62
+ deleteConfirmQuestion,
63
+ deletePreparedSecret,
27
64
  formatUptime,
65
+ gatewayStatus,
28
66
  gatherSnapshot,
29
- runDiagnostics
30
- } from "./chunk-CPCO6UJK.js";
67
+ listSecrets,
68
+ prepareSecretDelete,
69
+ resolveUpdateVersion,
70
+ runDiagnostics,
71
+ updateGateway
72
+ } from "./chunk-OUQVKIIA.js";
31
73
  import {
32
74
  GATEWAY_LOGS_PROBE,
33
75
  chooseLogSource,
34
76
  containerLogsCommand,
35
77
  gatewayLogsCommand
36
- } from "./chunk-NUL5F6YV.js";
78
+ } from "./chunk-SOZ62ZXW.js";
37
79
  import {
38
80
  execPrivileged
39
- } from "./chunk-NUDIHZEH.js";
40
- import {
41
- STATE_DIR_HOST_LINUX,
42
- gatewayRunCommand
43
- } from "./chunk-GIEVAGEK.js";
44
- import {
45
- IMAGE_INSPECT_CMD,
46
- imageForRestart
47
- } from "./chunk-VOEJYRIK.js";
81
+ } from "./chunk-WPZIJONN.js";
48
82
  import {
49
83
  getConfig,
50
84
  getConfigDir
@@ -56,7 +90,7 @@ import {
56
90
 
57
91
  // src/mcp/server.ts
58
92
  import { createServer } from "http";
59
- import { randomUUID as randomUUID5, timingSafeEqual } from "crypto";
93
+ import { randomUUID as randomUUID8, timingSafeEqual } from "crypto";
60
94
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
61
95
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
62
96
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -156,7 +190,7 @@ var clawops_doctorSchema = z.object({
156
190
  stackName: z.string().optional().describe("Stack to include remote checks for. Without it, only the local machine is\nchecked \u2014 no SSH connection is made.\n"),
157
191
  failuresOnly: z.boolean().optional().default(false).describe("Return only failing and warning checks. Passing checks are counted, not listed.")
158
192
  });
159
- var clawops_doctorDescription = "Run clawops's diagnostics and return the report: Node and Pulumi runtime, config,\nSSH key and known_hosts, cloud credentials per configured provider, and the\nsupported OpenClaw range. With stackName, also contacts the host for container\nstate, the deployed OpenClaw version, a real gateway health probe, whether the\nport is published to the internet, disk usage on the state directory, log\nrotation, and hardening drift.\n\nUse when: something is not working and you do not yet know what; before any\ndeploy, upgrade or migration; or to find out which OpenClaw version a gateway is\nactually running.\n\nDo NOT use when: you already know the problem and want to fix it. This tool only\nreports \u2014 it changes nothing, and never runs `openclaw doctor --fix`. No tool\nrepairs a gateway: `clawops gateway update` is CLI-only, so tell the user to run\nit themselves, after `clawops backup create`.\n\nEvery check carries a status: fail (something is wrong that clawops can name),\nwarn (worth knowing, not broken), info (did not apply). `ok` is false only when\nsomething failed \u2014 a fresh machine with no stacks is full of warnings and healthy.";
193
+ var clawops_doctorDescription = "Run clawops's diagnostics and return the report: Node and Pulumi runtime, config,\nSSH key and known_hosts, cloud credentials per configured provider, and the\nsupported OpenClaw range. With stackName, also contacts the host for container\nstate, the deployed OpenClaw version, a real gateway health probe, whether the\nport is published to the internet, disk usage on the state directory, log\nrotation, and hardening drift.\n\nUse when: something is not working and you do not yet know what; before any\ndeploy, upgrade or migration; or to find out which OpenClaw version a gateway is\nactually running.\n\nDo NOT use when: you already know the problem and want to fix it. This tool only\nreports \u2014 it changes nothing, and never runs `openclaw doctor --fix`. To move a\ngateway to another version, use clawops_gateway_update after clawops_backup_create.\n\nEvery check carries a status: fail (something is wrong that clawops can name),\nwarn (worth knowing, not broken), info (did not apply). `ok` is false only when\nsomething failed \u2014 a fresh machine with no stacks is full of warnings and healthy.";
160
194
  var clawops_doctorAnnotations = {
161
195
  title: "Run Diagnostics",
162
196
  readOnlyHint: true,
@@ -218,7 +252,7 @@ var clawops_config_getAnnotations = {
218
252
  var clawops_agents_listSchema = z.object({
219
253
  stackName: z.string().optional().describe("Which stack's agents to list. Omitted = the default stack in ~/.clawops/config.json")
220
254
  });
221
- var clawops_agents_listDescription = "List agents currently registered on the remote OpenClaw gateway.\n\nUse when: the user wants to see which agents are running, debug agent\nrouting, or count active workspaces.\n\nDo NOT use when: the user wants one agent's logs \u2014 no tool exposes those; tell\nthe user to run `clawops agents logs <name>`.";
255
+ var clawops_agents_listDescription = "List agents currently registered on the remote OpenClaw gateway.\n\nUse when: the user wants to see which agents are running, debug agent\nrouting, or count active workspaces.\n\nDo NOT use when: the user wants one agent's activity \u2014 that is clawops_agents_logs.";
222
256
  var clawops_agents_listAnnotations = {
223
257
  title: "List OpenClaw Agents",
224
258
  readOnlyHint: true,
@@ -227,6 +261,53 @@ var clawops_agents_listAnnotations = {
227
261
  openWorldHint: true,
228
262
  toolsets: ["cli", "read"]
229
263
  };
264
+ var clawops_agents_logsSchema = z.object({
265
+ stackName: z.string().optional().describe("Which stack the agent runs on. Omitted = the default stack in ~/.clawops/config.json"),
266
+ name: z.string().describe("The agent's name, as clawops_agents_list reports it"),
267
+ limit: z.number().int().optional().default(50).describe("Maximum activity records to return. Omitted = 50"),
268
+ cursor: z.string().optional().describe("The cursor a previous call returned, to continue from where it stopped. Omitted = the most recent records")
269
+ });
270
+ var clawops_agents_logsDescription = "One agent's recent activity, read from the gateway's audit log, newest last.\nPaginated: pass the returned cursor to continue.\n\nUse when: the user asks what a specific agent did, or why it behaved as it did.\n\nDo NOT use when: the user wants the gateway's own log stream \u2014 that is\nclawops_logs_tail. Do NOT use to find agent names \u2014 that is clawops_agents_list.";
271
+ var clawops_agents_logsAnnotations = {
272
+ title: "Read One Agent's Activity",
273
+ readOnlyHint: true,
274
+ destructiveHint: false,
275
+ idempotentHint: true,
276
+ openWorldHint: true,
277
+ toolsets: ["cli", "read"]
278
+ };
279
+ var clawops_gateway_statusSchema = z.object({
280
+ stackName: z.string().optional().describe("Which stack's gateway to inspect. Omitted = the default stack in ~/.clawops/config.json")
281
+ });
282
+ var clawops_gateway_statusDescription = "Whether the OpenClaw gateway container is running, when it started, and which image it\nruns, as docker reports it on the host.\n\nUse when: the user asks whether the gateway is up or which OpenClaw version is deployed.\n\nDo NOT use when: the user wants a full health check \u2014 clawops_doctor probes the gateway\nitself rather than the container. Do NOT use to change anything; this only reads.";
283
+ var clawops_gateway_statusAnnotations = {
284
+ title: "Gateway Container Status",
285
+ readOnlyHint: true,
286
+ destructiveHint: false,
287
+ idempotentHint: true,
288
+ openWorldHint: true,
289
+ toolsets: ["cli", "read"]
290
+ };
291
+ var clawops_secret_listSchema = z.object({});
292
+ var clawops_secret_listDescription = "List the secrets clawops stores locally, by name and status. Never returns a value.\n\nUse when: the user asks which secrets are set, or before a deploy that references one.\n\nDo NOT use when: the user wants to set or rotate a secret. No tool does that, because the\nvalue would pass through the conversation (R6): ask the user to run\n`clawops secret set <NAME>` or `clawops secret rotate <NAME>` in a terminal.";
293
+ var clawops_secret_listAnnotations = {
294
+ title: "List Stored Secrets",
295
+ readOnlyHint: true,
296
+ destructiveHint: false,
297
+ idempotentHint: true,
298
+ openWorldHint: false,
299
+ toolsets: ["cli", "read"]
300
+ };
301
+ var clawops_secret_auditSchema = z.object({});
302
+ var clawops_secret_auditDescription = "Report secrets that stacks reference but clawops does not have, and `$secret:` references\nthat cannot be resolved. Never returns a value.\n\nUse when: a deploy or config change fails on a missing secret, or before applying a plan\nwhose config references secrets.\n\nDo NOT use when: the user wants to set the missing secret \u2014 ask them to run\n`clawops secret set <NAME>` in a terminal; no tool takes a secret value (R6).";
303
+ var clawops_secret_auditAnnotations = {
304
+ title: "Audit Secret References",
305
+ readOnlyHint: true,
306
+ destructiveHint: false,
307
+ idempotentHint: true,
308
+ openWorldHint: false,
309
+ toolsets: ["cli", "read"]
310
+ };
230
311
  var clawops_upSchema = z.object({
231
312
  stackName: z.string().optional().describe("Name for the stack to provision, and the name every later command refers to it by. Omitted = the default stack in ~/.clawops/config.json"),
232
313
  provider: z.enum(["aws", "gcp", "azure", "local"]).optional().describe("Defaults to provider configured for this stack"),
@@ -251,7 +332,7 @@ var clawops_destroySchema = z.object({
251
332
  stackName: z.string().describe("The stack to destroy. Required, and worth repeating back to the user before calling: this deletes every resource it provisioned"),
252
333
  yes: z.boolean().optional().default(false).describe("Skip elicitation; CI/scripted use only")
253
334
  });
254
- var clawops_destroyDescription = "Destroy a clawops stack. Removes ALL provisioned resources. Triggers\nelicitation confirmation showing the resource diff before execution\n(R19). Cannot be undone.\n\nUse when: the user explicitly asks to destroy, tear down, delete, or\nremove a stack. Always confirm the stack name.\n\nDo NOT use when: the user wants to stop the gateway temporarily \u2014 that is not\na destroy, and no tool stops a gateway; clawops_gateway_restart is the only\ngateway tool. Do NOT use when in doubt about which stack; list first with\nclawops_stacks_list.";
335
+ var clawops_destroyDescription = "Destroy a clawops stack. Removes ALL provisioned resources. Triggers\nelicitation confirmation showing the resource diff before execution\n(R19). Cannot be undone.\n\nUse when: the user explicitly asks to destroy, tear down, delete, or\nremove a stack. Always confirm the stack name.\n\nDo NOT use when: the user wants to stop the gateway temporarily \u2014 that is not\na destroy, and no tool stops a gateway; clawops_gateway_restart restarts it. Do NOT use when in doubt about which stack; list first with\nclawops_stacks_list.";
255
336
  var clawops_destroyAnnotations = {
256
337
  title: "Destroy Stack (DESTRUCTIVE)",
257
338
  readOnlyHint: false,
@@ -347,6 +428,89 @@ var clawops_gateway_restartAnnotations = {
347
428
  openWorldHint: true,
348
429
  toolsets: ["cli"]
349
430
  };
431
+ var clawops_gateway_updateSchema = z.object({
432
+ stackName: z.string().optional().describe("Which stack's gateway to update. Omitted = the default stack in ~/.clawops/config.json"),
433
+ version: z.string().describe("A concrete OpenClaw version to move to, e.g. 2026.9.2. Moving tags are refused"),
434
+ yes: z.boolean().optional().default(false).describe("Skip the confirmation. Only when the user has already approved this specific update")
435
+ });
436
+ var clawops_gateway_updateDescription = "Move the gateway to a different OpenClaw version: pull the image and restart the gateway\non it. Every agent on the host restarts with it.\n\nUse when: the user asks to upgrade or downgrade OpenClaw on a running stack, naming a\nversion.\n\nDo NOT use when: the user has not named a concrete version \u2014 moving tags such as\n`latest` are refused, as the CLI refuses them. Do NOT use without a backup the user can\nreturn to: suggest clawops_backup_create first.";
437
+ var clawops_gateway_updateAnnotations = {
438
+ title: "Update OpenClaw Version",
439
+ readOnlyHint: false,
440
+ destructiveHint: true,
441
+ idempotentHint: true,
442
+ openWorldHint: true,
443
+ toolsets: ["cli"]
444
+ };
445
+ var clawops_backup_createSchema = z.object({
446
+ stackName: z.string().optional().describe("Which stack to back up. Omitted = the default stack in ~/.clawops/config.json"),
447
+ out: z.string().optional().describe("Absolute local path to write the archive to. Omitted = a timestamped file under ~/.clawops/backups")
448
+ });
449
+ var clawops_backup_createDescription = "Take a backup of the gateway's state (config, database, sessions, transcripts and\ninstalled plugins) and download it to a local archive. Live state is not changed.\n\nUse when: the user asks for a backup, and before anything that replaces state: a\nversion update, a migration, a restore, or a plan that replaces the instance.\n\nDo NOT use when: the user wants to back up cloud infrastructure \u2014 Pulumi state and plans\ncover that, not this.";
450
+ var clawops_backup_createAnnotations = {
451
+ title: "Back Up Gateway State",
452
+ readOnlyHint: false,
453
+ destructiveHint: false,
454
+ idempotentHint: false,
455
+ openWorldHint: true,
456
+ toolsets: ["cli"]
457
+ };
458
+ var clawops_backup_restoreSchema = z.object({
459
+ stackName: z.string().optional().describe("Which stack to restore onto. Omitted = the default stack in ~/.clawops/config.json"),
460
+ file: z.string().describe("Absolute local path to the backup archive, as clawops_backup_create wrote it"),
461
+ activate: z.boolean().optional().default(false).describe("Put the restored state into service. Omitted = expand into a staging directory and touch nothing live"),
462
+ yes: z.boolean().optional().default(false).describe("Skip the confirmation. Only when the user has already approved this specific restore")
463
+ });
464
+ var clawops_backup_restoreDescription = "Verify a backup archive and expand it on the host beside the live state, never over it.\nWith activate: true, also put it into service: stop the gateway, swap the restored state\nin (keeping the state it replaces), restart, and roll back if the gateway does not answer.\n\nUse when: the user asks to restore or recover from a backup, naming the archive.\n\nDo NOT use when: the gateway is merely unhealthy \u2014 clawops_workflow_recover diagnoses that\nwithout discarding state. Restoring is time travel: everything since the backup is lost,\nand channel credentials may need relinking.";
465
+ var clawops_backup_restoreAnnotations = {
466
+ title: "Restore Gateway State (DESTRUCTIVE with activate)",
467
+ readOnlyHint: false,
468
+ destructiveHint: true,
469
+ idempotentHint: false,
470
+ openWorldHint: true,
471
+ toolsets: ["cli"]
472
+ };
473
+ var clawops_migrateSchema = z.object({
474
+ stackName: z.string().optional().describe("Which stack to migrate. Omitted = the default stack in ~/.clawops/config.json"),
475
+ openclawVersion: z.string().optional().describe("The OpenClaw 2.x version to migrate to. Omitted = the version this clawops release defaults to"),
476
+ yes: z.boolean().optional().default(false).describe("Skip the confirmation. Only when the user has already approved this migration")
477
+ });
478
+ var clawops_migrateDescription = "Move a deployment from OpenClaw 1.x onto the 2.0 runtime contract: state ownership,\nconfig, and the gateway's run command, in the order a real migration showed works.\n\nUse when: a stack still runs OpenClaw 1.x and the user wants it on 2.x. clawops_doctor\nand clawops_gateway_status say which line a stack runs.\n\nDo NOT use when: the stack is already on 2.x \u2014 use clawops_gateway_update to change\nversions within 2.x. Take a backup first with clawops_backup_create.";
479
+ var clawops_migrateAnnotations = {
480
+ title: "Migrate 1.x to 2.0 (DESTRUCTIVE)",
481
+ readOnlyHint: false,
482
+ destructiveHint: true,
483
+ idempotentHint: false,
484
+ openWorldHint: true,
485
+ toolsets: ["cli"]
486
+ };
487
+ var clawops_stacks_deleteSchema = z.object({
488
+ name: z.string().describe("The stack to remove from config, as clawops_stacks_list reports it"),
489
+ force: z.boolean().optional().default(false).describe("Skip the safety checks: allow deleting the default stack, or one still deployed. Omitted = refuse both"),
490
+ yes: z.boolean().optional().default(false).describe("Skip the confirmation. Only when the user has already approved this deletion")
491
+ });
492
+ var clawops_stacks_deleteDescription = "Remove a stack from ~/.clawops/config.json. This forgets the stack; it does not tear\ndown its infrastructure, and it refuses a stack that is still deployed unless forced.\n\nUse when: the user wants to stop tracking a stack whose resources are already gone.\n\nDo NOT use when: the user wants the infrastructure removed \u2014 that is clawops_destroy,\nfirst. Forcing the delete of a deployed stack leaves resources running and billing that\nclawops can no longer see.";
493
+ var clawops_stacks_deleteAnnotations = {
494
+ title: "Forget a Stack (DESTRUCTIVE)",
495
+ readOnlyHint: false,
496
+ destructiveHint: true,
497
+ idempotentHint: true,
498
+ openWorldHint: false,
499
+ toolsets: ["admin"]
500
+ };
501
+ var clawops_secret_deleteSchema = z.object({
502
+ name: z.string().describe("The secret's name, as clawops_secret_list reports it"),
503
+ yes: z.boolean().optional().default(false).describe("Skip the confirmation. Only when the user has already approved this deletion")
504
+ });
505
+ var clawops_secret_deleteDescription = "Delete a locally stored secret by name. Warns when a stack still references it.\n\nUse when: the user asks to remove a secret they no longer use.\n\nDo NOT use when: the user wants to replace a value \u2014 ask them to run\n`clawops secret rotate <NAME>`; no tool takes a secret value (R6).";
506
+ var clawops_secret_deleteAnnotations = {
507
+ title: "Delete a Stored Secret (DESTRUCTIVE)",
508
+ readOnlyHint: false,
509
+ destructiveHint: true,
510
+ idempotentHint: true,
511
+ openWorldHint: false,
512
+ toolsets: ["cli"]
513
+ };
350
514
  var clawops_initSchema = z.object({
351
515
  provider: z.enum(["aws", "gcp", "azure", "local"]).describe("Which cloud this stack deploys to, or 'local' for a machine you already have"),
352
516
  stackName: z.string().optional().describe('Name for the stack, used by every later call. Omitted = "default"'),
@@ -438,6 +602,10 @@ var TOOLSETS = {
438
602
  "clawops_monitor",
439
603
  "clawops_config_get",
440
604
  "clawops_agents_list",
605
+ "clawops_agents_logs",
606
+ "clawops_gateway_status",
607
+ "clawops_secret_list",
608
+ "clawops_secret_audit",
441
609
  "clawops_up",
442
610
  "clawops_destroy",
443
611
  "clawops_apply",
@@ -446,6 +614,11 @@ var TOOLSETS = {
446
614
  "clawops_config_unset",
447
615
  "clawops_config_validate",
448
616
  "clawops_gateway_restart",
617
+ "clawops_gateway_update",
618
+ "clawops_backup_create",
619
+ "clawops_backup_restore",
620
+ "clawops_migrate",
621
+ "clawops_secret_delete",
449
622
  "clawops_init",
450
623
  "clawops_harden",
451
624
  "clawops_task_status"
@@ -462,13 +635,18 @@ var TOOLSETS = {
462
635
  "clawops_stacks_list",
463
636
  "clawops_config_get",
464
637
  "clawops_agents_list",
638
+ "clawops_agents_logs",
639
+ "clawops_gateway_status",
640
+ "clawops_secret_list",
641
+ "clawops_secret_audit",
465
642
  "clawops_plan",
466
643
  "clawops_config_validate",
467
644
  "clawops_workflow_recover",
468
645
  "clawops_task_status"
469
646
  ],
470
647
  admin: [
471
- "clawops_stacks_list"
648
+ "clawops_stacks_list",
649
+ "clawops_stacks_delete"
472
650
  ]
473
651
  };
474
652
 
@@ -505,34 +683,6 @@ function text(t) {
505
683
  }
506
684
 
507
685
  // src/mcp/tools/_conn.ts
508
- async function resolveConn(ctx) {
509
- if (ctx.adapter.name === "local") {
510
- const state = ctx.localState;
511
- if (!state) throw new StateError("Stack has no local state \u2014 run `clawops up` first.");
512
- return {
513
- host: state.sshHost,
514
- port: state.sshPort,
515
- user: state.sshUser,
516
- privateKeyPath: state.privateKeyPath,
517
- knownHostsPath: state.knownHostsPath
518
- };
519
- }
520
- const { extractBaseOutputs: extractBaseOutputs2 } = await import("./outputs-DJHBY7EE.js");
521
- const stack = await ctx.getStack();
522
- const outputMap = await stack.outputs();
523
- const outputs = Object.fromEntries(
524
- Object.entries(outputMap).map(([k, v]) => [k, v.value])
525
- );
526
- if (!outputs["publicIp"]) {
527
- throw new StateError("Stack has no outputs \u2014 run `clawops up` first.");
528
- }
529
- const base = extractBaseOutputs2(outputs);
530
- return ctx.adapter.getConnectionInfo({
531
- ...base,
532
- privateKeyPath: ctx.config.ssh.keyPath,
533
- knownHostsPath: ctx.config.ssh.knownHostsPath
534
- });
535
- }
536
686
  function errText(message) {
537
687
  return { content: [{ type: "text", text: message }], isError: true };
538
688
  }
@@ -606,22 +756,6 @@ ${result.stdout}`;
606
756
  }
607
757
  }
608
758
 
609
- // src/mcp/tools/cli/stacks.ts
610
- async function handleStacksList(_input, _server) {
611
- const config = getConfig();
612
- if (!config) {
613
- return { content: [{ type: "text", text: JSON.stringify({ stacks: [] }) }] };
614
- }
615
- const stacks = Object.entries(config.stacks).map(([name, cfg]) => ({
616
- name,
617
- provider: cfg.provider,
618
- region: cfg.region,
619
- stateUrl: cfg.stateUrl,
620
- isDefault: name === config.defaults.stack
621
- }));
622
- return { content: [{ type: "text", text: JSON.stringify({ stacks }, null, 2) }] };
623
- }
624
-
625
759
  // src/mcp/tools/_confirm.ts
626
760
  async function confirmDestructive(server, opts) {
627
761
  const capabilities = server.server.getClientCapabilities();
@@ -645,6 +779,40 @@ async function confirmDestructive(server, opts) {
645
779
  return { confirmed: true };
646
780
  }
647
781
 
782
+ // src/mcp/tools/cli/stacks.ts
783
+ async function handleStacksList(_input, _server) {
784
+ const config = getConfig();
785
+ if (!config) {
786
+ return { content: [{ type: "text", text: JSON.stringify({ stacks: [] }) }] };
787
+ }
788
+ const stacks = Object.entries(config.stacks).map(([name, cfg]) => ({
789
+ name,
790
+ provider: cfg.provider,
791
+ region: cfg.region,
792
+ stateUrl: cfg.stateUrl,
793
+ isDefault: name === config.defaults.stack
794
+ }));
795
+ return { content: [{ type: "text", text: JSON.stringify({ stacks }, null, 2) }] };
796
+ }
797
+ async function handleStacksDelete(input, server) {
798
+ const force = input.force === true;
799
+ const check = await checkStackDelete(input.name, { force });
800
+ if (!check.ok) return errText(check.reason);
801
+ if (!input.yes) {
802
+ const consequence = force ? "force skips the still-deployed check: if this stack is still deployed, its resources stay running and billing, and clawops can no longer see them." : check.warnings.length > 0 ? `${check.warnings.join(" ")} If it is still deployed, its resources stay running and billing.` : "clawops sees no deployment for it, so nothing should be left running.";
803
+ const confirmation = await confirmDestructive(server, {
804
+ message: `Forget stack "${input.name}"? This removes it from ~/.clawops/config.json; it does NOT tear down its infrastructure. ${consequence}` + (check.isDefault ? " It is the default stack, so another stack becomes the default." : ""),
805
+ title: "Confirm forgetting the stack",
806
+ what: `forgetting stack "${input.name}"`
807
+ });
808
+ if (!confirmation.confirmed) return okText(confirmation.reason);
809
+ }
810
+ const deleted = deleteStackFromConfig(input.name);
811
+ const lines = [...check.warnings, forgetNotice(input.name), deleted.message];
812
+ if (deleted.newDefaultMessage) lines.push(deleted.newDefaultMessage);
813
+ return okText(lines.join("\n"));
814
+ }
815
+
648
816
  // src/mcp/tools/cli/config.ts
649
817
  async function handleConfigGet(input, _server) {
650
818
  const ac = new AbortController();
@@ -820,39 +988,34 @@ async function handleAgentsList(input, _server) {
820
988
  drainPool();
821
989
  }
822
990
  }
823
-
824
- // src/mcp/tools/cli/gateway.ts
825
- async function handleGatewayRestart(input, server) {
826
- const confirmation = await confirmDestructive(server, {
827
- message: `Restart the OpenClaw gateway on stack "${input.stackName ?? "default"}"? This will briefly interrupt connections.`,
828
- title: "Confirm restart",
829
- what: "restarting the gateway"
830
- });
831
- if (!confirmation.confirmed) {
832
- return okText(confirmation.reason);
991
+ async function handleAgentsLogs(input, _server) {
992
+ if (input.name.trim() === "") {
993
+ return errText("name is required: the agent name, as clawops_agents_list reports it.");
833
994
  }
834
995
  const ctx = buildContext({ stack: input.stackName });
835
996
  const conn = await resolveConn(ctx);
836
997
  const { session, release } = await acquireSession(conn);
837
998
  try {
838
- const imgResult = await execPrivileged(session, IMAGE_INSPECT_CMD);
839
- const image = imageForRestart(imgResult.stdout);
840
- if (!image.ok) return errText(image.error);
841
- const result = await execPrivileged(
842
- session,
843
- gatewayRunCommand({ image: image.value, stateDir: STATE_DIR_HOST_LINUX })
844
- );
845
- if (result.code !== 0) {
846
- return errText(`Gateway restart failed: ${result.stderr}`);
847
- }
848
- return okText("Gateway restarted successfully.");
999
+ const page = await readAgentActivity(session, {
1000
+ agentId: input.name,
1001
+ limit: input.limit ?? DEFAULT_AGENT_LOG_LIMIT,
1002
+ ...input.cursor ? { cursor: input.cursor } : {}
1003
+ });
1004
+ if (!page.ok) return errText(page.error);
1005
+ const body = {
1006
+ agent: input.name,
1007
+ ...page.value.cursor ? { cursor: page.value.cursor } : {},
1008
+ count: page.value.records.length,
1009
+ records: page.value.records
1010
+ };
1011
+ return okText(trimForMcp(JSON.stringify(body, null, 2), ctx.stackName).content);
849
1012
  } finally {
850
1013
  release();
851
1014
  drainPool();
852
1015
  }
853
1016
  }
854
1017
 
855
- // src/mcp/tools/cli/up.ts
1018
+ // src/mcp/tools/cli/gateway.ts
856
1019
  import { randomUUID as randomUUID2 } from "crypto";
857
1020
 
858
1021
  // src/mcp/progress.ts
@@ -886,7 +1049,91 @@ function makeProgressEmitter(server, progressToken) {
886
1049
  };
887
1050
  }
888
1051
 
1052
+ // src/mcp/tools/cli/gateway.ts
1053
+ async function handleGatewayRestart(input, server) {
1054
+ const confirmation = await confirmDestructive(server, {
1055
+ message: `Restart the OpenClaw gateway on stack "${input.stackName ?? "default"}"? This will briefly interrupt connections.`,
1056
+ title: "Confirm restart",
1057
+ what: "restarting the gateway"
1058
+ });
1059
+ if (!confirmation.confirmed) {
1060
+ return okText(confirmation.reason);
1061
+ }
1062
+ const ctx = buildContext({ stack: input.stackName });
1063
+ const conn = await resolveConn(ctx);
1064
+ const { session, release } = await acquireSession(conn);
1065
+ try {
1066
+ await restartGateway(session);
1067
+ return okText("Gateway restarted.");
1068
+ } catch (e) {
1069
+ return errText(e.message);
1070
+ } finally {
1071
+ release();
1072
+ drainPool();
1073
+ }
1074
+ }
1075
+ async function handleGatewayStatus(input, _server) {
1076
+ const ctx = buildContext({ stack: input.stackName });
1077
+ const conn = await resolveConn(ctx);
1078
+ const { session, release } = await acquireSession(conn);
1079
+ try {
1080
+ const status = await gatewayStatus(session);
1081
+ if (!status.ok) return errText(status.error);
1082
+ return okText(trimForMcp(JSON.stringify(status.value, null, 2), ctx.stackName).content);
1083
+ } finally {
1084
+ release();
1085
+ drainPool();
1086
+ }
1087
+ }
1088
+ async function handleGatewayUpdate(input, server) {
1089
+ if (input.version.trim() === "") {
1090
+ return errText("version is required: name the concrete OpenClaw version to move to, e.g. 2026.9.2.");
1091
+ }
1092
+ const target = await resolveUpdateVersion(input.version);
1093
+ if (!target.ok) return errText(target.error);
1094
+ const version = target.value;
1095
+ const ctx = buildContext({ stack: input.stackName });
1096
+ const conn = await resolveConn(ctx);
1097
+ const { session, release } = await acquireSession(conn);
1098
+ try {
1099
+ if (!input.yes) {
1100
+ const current = await currentGatewayVersion(session).catch(() => void 0);
1101
+ const confirmation = await confirmDestructive(server, {
1102
+ message: `Update the OpenClaw gateway on stack "${ctx.stackName}" ${current ? `from ${current} ` : ""}to ${version}? Every agent on the host restarts with it. A snapshot of the state database is taken first.`,
1103
+ title: "Confirm update",
1104
+ what: `updating the gateway to ${version}`
1105
+ });
1106
+ if (!confirmation.confirmed) return okText(confirmation.reason);
1107
+ }
1108
+ const taskId = randomUUID2();
1109
+ const emit = makeProgressEmitter(server, void 0);
1110
+ startTask(taskId, `clawops_gateway_update stack=${ctx.stackName} version=${version}`);
1111
+ const notes = [];
1112
+ try {
1113
+ emit(`Updating gateway to ${version}...`);
1114
+ const result = await updateGateway(session, version, {
1115
+ onProgress: emit,
1116
+ onNote: (text4) => {
1117
+ notes.push(text4);
1118
+ emit(text4);
1119
+ }
1120
+ });
1121
+ const text3 = [result.message, ...result.ok ? [] : result.hint ? [result.hint] : [], ...notes].join("\n");
1122
+ updateTask(taskId, result.ok ? "completed" : "failed", result.ok ? text3 : void 0, result.ok ? void 0 : text3);
1123
+ const { content } = trimForMcp(text3, ctx.stackName);
1124
+ return result.ok ? okText(content) : errText(content);
1125
+ } catch (e) {
1126
+ updateTask(taskId, "failed", void 0, e instanceof Error ? e.message : String(e));
1127
+ throw e;
1128
+ }
1129
+ } finally {
1130
+ release();
1131
+ drainPool();
1132
+ }
1133
+ }
1134
+
889
1135
  // src/mcp/tools/cli/up.ts
1136
+ import { randomUUID as randomUUID3 } from "crypto";
890
1137
  async function handleUp(input, server) {
891
1138
  if (!input.dryRun) {
892
1139
  const confirmation = await confirmDestructive(server, {
@@ -908,7 +1155,7 @@ async function handleUp(input, server) {
908
1155
  );
909
1156
  }
910
1157
  const { localOpts } = stackConfig;
911
- const { localBootstrap } = await import("./bootstrap-3PBVY7MA.js");
1158
+ const { localBootstrap } = await import("./bootstrap-32TZXVDF.js");
912
1159
  const ac = new AbortController();
913
1160
  const state = await localBootstrap({
914
1161
  host: localOpts.host,
@@ -927,12 +1174,12 @@ async function handleUp(input, server) {
927
1174
  if (!validation.ok) {
928
1175
  return errText(`Provider config invalid: ${validation.errors.join("; ")}`);
929
1176
  }
930
- const taskId = randomUUID2();
1177
+ const taskId = randomUUID3();
931
1178
  const progressToken = void 0;
932
1179
  const emit = makeProgressEmitter(server, progressToken);
933
1180
  startTask(taskId, `clawops_up stack=${ctx.stackName}`);
934
- const { generatePlan: generatePlan2 } = await import("./generate-NE54BATS.js");
935
- const { applyPlan: applyPlan2 } = await import("./apply-JARPVA5Y.js");
1181
+ const { generatePlan: generatePlan2 } = await import("./generate-4YYNW3IU.js");
1182
+ const { applyPlan: applyPlan2 } = await import("./apply-F5SQ2ZOS.js");
936
1183
  const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
937
1184
  const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
938
1185
  const network = await resolveNetworkFlags(
@@ -981,7 +1228,7 @@ async function handleUp(input, server) {
981
1228
  }
982
1229
 
983
1230
  // src/mcp/tools/cli/destroy.ts
984
- import { randomUUID as randomUUID3 } from "crypto";
1231
+ import { randomUUID as randomUUID4 } from "crypto";
985
1232
  async function handleDestroy(input, server) {
986
1233
  if (!input.yes) {
987
1234
  const confirmation = await confirmDestructive(server, {
@@ -997,7 +1244,7 @@ async function handleDestroy(input, server) {
997
1244
  if (ctx.adapter.name === "local") {
998
1245
  return errText("Local provider stacks are destroyed with `clawops down --destroy` via SSH. Use clawops_up with the local provider instead.");
999
1246
  }
1000
- const taskId = randomUUID3();
1247
+ const taskId = randomUUID4();
1001
1248
  const emit = makeProgressEmitter(server, void 0);
1002
1249
  startTask(taskId, `clawops_destroy stack=${input.stackName}`);
1003
1250
  try {
@@ -1047,7 +1294,7 @@ async function handlePlan(input, _server) {
1047
1294
  let network = resolved;
1048
1295
  if (input.privateOnly) {
1049
1296
  const { privateOnlyNetwork, assertTailnetReachable } = await import("./private-only-DIA26I2E.js");
1050
- const { probeSsh } = await import("./tailscale-cutover-EKAQSDXP.js");
1297
+ const { probeSsh } = await import("./tailscale-cutover-V42HO5UM.js");
1051
1298
  network = privateOnlyNetwork(flags, resolved, ctx.config.stacks[stackName]?.tailscale, stackName);
1052
1299
  await assertTailnetReachable(ctx, (conn) => probeSsh(conn));
1053
1300
  }
@@ -1103,12 +1350,12 @@ async function handleHarden(input, server) {
1103
1350
  return okText(confirmation.reason);
1104
1351
  }
1105
1352
  }
1106
- const { revertTailnet, cutOverToTailnet, connectionFor } = await import("./flows-HGJIA6EL.js");
1353
+ const { revertTailnet, cutOverToTailnet, connectionFor } = await import("./flows-6B2I6TRW.js");
1107
1354
  if (input.tailscaleRevert) {
1108
1355
  const outcome = await revertTailnet(ctx, config);
1109
1356
  return outcome.ok ? okText(outcome.message) : errText(outcome.reason);
1110
1357
  }
1111
- const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary, makeTailscaleModule } = await import("./harden-RUOUA5BV.js");
1358
+ const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary, makeTailscaleModule } = await import("./harden-UHAOVQMZ.js");
1112
1359
  const forStack = makeTailscaleModule(ctx.stackName);
1113
1360
  const resolved = resolveModules(MODULE_CATALOG, input.options, ctx.adapter.name).map(
1114
1361
  (m) => m.id === "tailscale" ? forStack : m
@@ -1178,7 +1425,7 @@ async function handleInit(input, _server) {
1178
1425
 
1179
1426
  // src/mcp/tools/cli/apply.ts
1180
1427
  import { readFileSync } from "fs";
1181
- import { randomUUID as randomUUID4 } from "crypto";
1428
+ import { randomUUID as randomUUID5 } from "crypto";
1182
1429
  async function handleApply(input, server) {
1183
1430
  let plan;
1184
1431
  try {
@@ -1208,7 +1455,7 @@ This will provision cloud resources and may incur costs.`,
1208
1455
  return okText(confirmation.reason);
1209
1456
  }
1210
1457
  }
1211
- const taskId = randomUUID4();
1458
+ const taskId = randomUUID5();
1212
1459
  const emit = makeProgressEmitter(server, void 0);
1213
1460
  startTask(taskId, `clawops_apply stack=${plan.spec.stackName}`);
1214
1461
  try {
@@ -1248,7 +1495,7 @@ async function handleTaskStatus(input, _server) {
1248
1495
 
1249
1496
  // src/mcp/tools/cli/monitor.ts
1250
1497
  async function handleMonitor(input, _server) {
1251
- const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-PIIKJ4L2.js");
1498
+ const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-NT4XBSUT.js");
1252
1499
  const ctx = buildContext({ stack: input.stackName });
1253
1500
  const tailLines = input.tailLines ?? 5;
1254
1501
  let conn;
@@ -1302,6 +1549,255 @@ function text2(t) {
1302
1549
  return { content: [{ type: "text", text: t }] };
1303
1550
  }
1304
1551
 
1552
+ // src/mcp/tools/cli/backup.ts
1553
+ import { randomUUID as randomUUID6 } from "crypto";
1554
+ import { mkdirSync as mkdirSync3 } from "fs";
1555
+ import path3 from "path";
1556
+ function relativePathRefusal(param, value) {
1557
+ return `"${param}" must be an absolute path; got "${value}". The MCP server does not resolve paths against a working directory. Pass the full path, e.g. /Users/me/backups/openclaw.tar.gz.`;
1558
+ }
1559
+ function capped(text3, stackName, isError = false) {
1560
+ const { content } = trimForMcp(text3, stackName);
1561
+ return isError ? errText(content) : okText(content);
1562
+ }
1563
+ async function handleBackupCreate(input, server) {
1564
+ if (input.out !== void 0 && !path3.isAbsolute(input.out)) {
1565
+ return errText(relativePathRefusal("out", input.out));
1566
+ }
1567
+ const ctx = buildContext({ stack: input.stackName });
1568
+ let outPath = input.out;
1569
+ if (outPath === void 0) {
1570
+ const dir = path3.join(getConfigDir(), "backups");
1571
+ mkdirSync3(dir, { recursive: true, mode: 448 });
1572
+ outPath = path3.join(dir, defaultBackupFilename());
1573
+ }
1574
+ const taskId = randomUUID6();
1575
+ const emit = makeProgressEmitter(server, void 0);
1576
+ startTask(taskId, `clawops_backup_create stack=${ctx.stackName}`);
1577
+ const ac = new AbortController();
1578
+ const conn = await resolveConn(ctx);
1579
+ const { session, release } = await acquireSession({ ...conn, signal: ac.signal });
1580
+ try {
1581
+ const created = await createBackup(session, {
1582
+ outPath,
1583
+ signal: ac.signal,
1584
+ onPhase: (phase) => emit(phase === "creating" ? "Creating backup on remote host..." : `Downloading to ${outPath}...`)
1585
+ });
1586
+ if (!created.ok) {
1587
+ updateTask(taskId, "failed", void 0, created.reason);
1588
+ return capped(created.reason, ctx.stackName, true);
1589
+ }
1590
+ const summary = [`Backup saved to ${created.path}`, ...ARCHIVE_IS_A_CREDENTIAL].join("\n");
1591
+ updateTask(taskId, "completed", summary);
1592
+ return capped(summary, ctx.stackName);
1593
+ } catch (err) {
1594
+ const msg = err instanceof Error ? err.message : String(err);
1595
+ updateTask(taskId, "failed", void 0, msg);
1596
+ return errText(`Backup failed: ${msg}`);
1597
+ } finally {
1598
+ release();
1599
+ drainPool();
1600
+ }
1601
+ }
1602
+ async function handleBackupRestore(input, server) {
1603
+ if (!path3.isAbsolute(input.file)) {
1604
+ return errText(relativePathRefusal("file", input.file));
1605
+ }
1606
+ const activate = input.activate === true;
1607
+ const stackLabel = input.stackName ?? "default";
1608
+ if (!input.yes) {
1609
+ const confirmation = await confirmDestructive(server, activate ? {
1610
+ message: `ACTIVATE the backup ${input.file} on stack "${stackLabel}"? The gateway is stopped and the restored state put into service. Everything since the backup is lost from the live gateway (the state it replaces is kept alongside it), and channel credentials may need relinking. clawops puts the previous state back if the gateway does not come up.`,
1611
+ title: "Confirm restore and activation",
1612
+ what: "activating a restored backup"
1613
+ } : {
1614
+ message: `Expand the backup ${input.file} onto stack "${stackLabel}", staging only? It is verified and expanded beside the live state; the gateway and its live state are not touched.`,
1615
+ title: "Confirm staging-only restore",
1616
+ what: "restoring a backup to a staging directory"
1617
+ });
1618
+ if (!confirmation.confirmed) return okText(confirmation.reason);
1619
+ }
1620
+ const ctx = buildContext({ stack: input.stackName });
1621
+ const taskId = randomUUID6();
1622
+ const emit = makeProgressEmitter(server, void 0);
1623
+ startTask(taskId, `clawops_backup_restore stack=${ctx.stackName}${activate ? " activate" : ""}`);
1624
+ const fail = (text3) => {
1625
+ updateTask(taskId, "failed", void 0, text3);
1626
+ return capped(text3, ctx.stackName, true);
1627
+ };
1628
+ const ac = new AbortController();
1629
+ const conn = await resolveConn(ctx);
1630
+ const { session, release } = await acquireSession({ ...conn, signal: ac.signal });
1631
+ try {
1632
+ const staged = await stageRestore(session, {
1633
+ file: input.file,
1634
+ signal: ac.signal,
1635
+ onPhase: (phase) => emit(phase === "uploading" ? "Uploading archive..." : "Verifying and restoring to a staging directory...")
1636
+ });
1637
+ const raw = staged.unparsedReport !== void 0 ? [`OpenClaw reported: ${staged.unparsedReport}`] : [];
1638
+ if (!staged.ok) {
1639
+ return fail([
1640
+ staged.reason,
1641
+ ...staged.stagingOnHost ? [nothingChanged(staged.stagingOnHost)] : [],
1642
+ ...raw
1643
+ ].join("\n"));
1644
+ }
1645
+ const lines = [
1646
+ `Archive verified and expanded to ${staged.stagingOnHost} on the host.`,
1647
+ ...staged.entryCount !== void 0 ? [`${staged.entryCount} entries restored.`] : [],
1648
+ ...raw,
1649
+ // Verbatim: rolled-back approvals and channel credentials are the operator's to judge.
1650
+ ...staged.warnings.map((w) => `Warning: ${w}`)
1651
+ ];
1652
+ if (!activate) {
1653
+ lines.push(
1654
+ "",
1655
+ "Nothing has been activated. Call clawops_backup_restore again with activate: true to put",
1656
+ "this state into service. The state to adopt is",
1657
+ ` ${staged.statePath}`,
1658
+ `(the archive holds ${staged.stateDirInArchive} under payload/posix), replacing ${staged.stateDir}.`,
1659
+ ...PLUGINS_NOT_CARRIED
1660
+ );
1661
+ const text4 = lines.join("\n");
1662
+ updateTask(taskId, "completed", text4);
1663
+ return capped(text4, ctx.stackName);
1664
+ }
1665
+ const outcome = await activateStaged(session, staged, {
1666
+ signal: ac.signal,
1667
+ onPhase: (phase) => {
1668
+ if (phase === "activating") emit("Activating the restored state...");
1669
+ else if (phase === "restarting") emit("Restarting the gateway...");
1670
+ else if (phase === "waiting") emit("Waiting for the gateway to answer...");
1671
+ }
1672
+ });
1673
+ if (!outcome.ok) {
1674
+ return fail([...lines, "", outcome.reason, ...activationFailureDetail(outcome)].join("\n"));
1675
+ }
1676
+ lines.push("", ACTIVATED, ...activatedLines(outcome.preserved));
1677
+ const text3 = lines.join("\n");
1678
+ updateTask(taskId, "completed", text3);
1679
+ return capped(text3, ctx.stackName);
1680
+ } catch (err) {
1681
+ return fail(`Restore failed: ${err instanceof Error ? err.message : String(err)}`);
1682
+ } finally {
1683
+ release();
1684
+ drainPool();
1685
+ }
1686
+ }
1687
+
1688
+ // src/mcp/tools/cli/migrate.ts
1689
+ import { randomUUID as randomUUID7 } from "crypto";
1690
+ var SYNC_WINDOW = { ms: 1e4 };
1691
+ async function handleMigrate(input, server) {
1692
+ let target;
1693
+ try {
1694
+ target = await prepareMigration({ stack: input.stackName, openclawVersion: input.openclawVersion });
1695
+ } catch (err) {
1696
+ if (err instanceof UsageError) return errText(err.message);
1697
+ throw err;
1698
+ }
1699
+ if (!input.yes) {
1700
+ const confirmation = await confirmDestructive(server, {
1701
+ message: migrationQuestion(target),
1702
+ title: `Confirm migrating "${target.stackName}" to ${target.version}`,
1703
+ what: `migrating stack "${target.stackName}"`
1704
+ });
1705
+ if (!confirmation.confirmed) return okText(confirmation.reason);
1706
+ }
1707
+ const taskId = randomUUID7();
1708
+ const emit = makeProgressEmitter(server, void 0);
1709
+ startTask(taskId, `clawops_migrate stack=${target.stackName} target=${target.version}`);
1710
+ const run = runMigration(target, {
1711
+ onStep: (text3) => {
1712
+ emit(text3);
1713
+ updateTask(taskId, "running", text3);
1714
+ }
1715
+ }).then(
1716
+ (result) => {
1717
+ const text3 = render(result, target.stackName);
1718
+ updateTask(taskId, result.exitCode === 0 ? "completed" : "failed", text3, result.exitCode === 0 ? void 0 : text3);
1719
+ return { kind: "done", result, text: text3 };
1720
+ },
1721
+ (err) => {
1722
+ const msg = err instanceof Error ? err.message : String(err);
1723
+ updateTask(taskId, "failed", void 0, msg);
1724
+ return { kind: "error", err, msg };
1725
+ }
1726
+ );
1727
+ let timer;
1728
+ const timeout = new Promise((resolve) => {
1729
+ timer = setTimeout(() => resolve({ kind: "pending" }), SYNC_WINDOW.ms);
1730
+ });
1731
+ const first = await Promise.race([run, timeout]);
1732
+ clearTimeout(timer);
1733
+ if (first.kind === "pending") {
1734
+ return okText(
1735
+ JSON.stringify(
1736
+ {
1737
+ taskId,
1738
+ status: "running",
1739
+ stack: target.stackName,
1740
+ target: target.version,
1741
+ message: "The migration is still running on the host. Poll clawops_task_status with this taskId for the outcome; do not start another migration of this stack meanwhile."
1742
+ },
1743
+ null,
1744
+ 2
1745
+ )
1746
+ );
1747
+ }
1748
+ if (first.kind === "error") {
1749
+ if (first.err instanceof UsageError) return errText(first.msg);
1750
+ throw first.err;
1751
+ }
1752
+ return first.result.exitCode === 0 ? okText(first.text) : errText(first.text);
1753
+ }
1754
+ function render(result, stackName) {
1755
+ const text3 = result.lines.map((l) => l.text).join("\n");
1756
+ return trimForMcp(text3, stackName).content;
1757
+ }
1758
+
1759
+ // src/mcp/tools/cli/secret.ts
1760
+ var RESOURCE_NAME = "secrets";
1761
+ function capped2(payload) {
1762
+ const { content } = trimForMcp(JSON.stringify(payload, null, 2), RESOURCE_NAME);
1763
+ return okText(content);
1764
+ }
1765
+ async function handleSecretList(_input, _server) {
1766
+ const secrets = listSecrets();
1767
+ if (secrets.length === 0) {
1768
+ return capped2({ secrets, message: `${NO_SECRETS_MESSAGE} ${SET_SECRET_HINT}` });
1769
+ }
1770
+ return capped2({ secrets });
1771
+ }
1772
+ async function handleSecretAudit(_input, _server) {
1773
+ const report = auditSecrets();
1774
+ return capped2({
1775
+ ...report,
1776
+ message: report.ok ? AUDIT_CLEAN_MESSAGE : AUDIT_FIX_HINT
1777
+ });
1778
+ }
1779
+ async function handleSecretDelete(input, server) {
1780
+ const prepared = prepareSecretDelete(input.name);
1781
+ if (!prepared.ok) return errText(prepared.reason);
1782
+ if (!input.yes) {
1783
+ const confirmation = await confirmDestructive(server, {
1784
+ message: [deleteConfirmQuestion(prepared.name), ...prepared.warnings].join("\n"),
1785
+ title: `Delete secret "${prepared.name}"`,
1786
+ what: `deleting secret "${prepared.name}"`
1787
+ });
1788
+ if (!confirmation.confirmed) {
1789
+ return okText([confirmation.reason, ...prepared.warnings].join("\n"));
1790
+ }
1791
+ }
1792
+ const message = deletePreparedSecret(prepared);
1793
+ return capped2({
1794
+ deleted: prepared.name,
1795
+ message,
1796
+ referencingStacks: prepared.referencingStacks,
1797
+ warnings: prepared.warnings
1798
+ });
1799
+ }
1800
+
1305
1801
  // src/mcp/tools/workflow/deploy_app.ts
1306
1802
  async function handleWorkflowDeployApp(input, server) {
1307
1803
  const parts = [`## Deployment Workflow \u2014 ${input.provider} / ${input.stackName}
@@ -1426,6 +1922,16 @@ var TOOL_REGISTRY = {
1426
1922
  clawops_config_unset: makeEntry(clawops_config_unsetSchema, clawops_config_unsetAnnotations, handleConfigUnset, clawops_config_unsetDescription),
1427
1923
  clawops_config_validate: makeEntry(clawops_config_validateSchema, clawops_config_validateAnnotations, handleConfigValidate, clawops_config_validateDescription),
1428
1924
  clawops_gateway_restart: makeEntry(clawops_gateway_restartSchema, clawops_gateway_restartAnnotations, handleGatewayRestart, clawops_gateway_restartDescription),
1925
+ clawops_agents_logs: makeEntry(clawops_agents_logsSchema, clawops_agents_logsAnnotations, handleAgentsLogs, clawops_agents_logsDescription),
1926
+ clawops_gateway_status: makeEntry(clawops_gateway_statusSchema, clawops_gateway_statusAnnotations, handleGatewayStatus, clawops_gateway_statusDescription),
1927
+ clawops_secret_list: makeEntry(clawops_secret_listSchema, clawops_secret_listAnnotations, handleSecretList, clawops_secret_listDescription),
1928
+ clawops_secret_audit: makeEntry(clawops_secret_auditSchema, clawops_secret_auditAnnotations, handleSecretAudit, clawops_secret_auditDescription),
1929
+ clawops_gateway_update: makeEntry(clawops_gateway_updateSchema, clawops_gateway_updateAnnotations, handleGatewayUpdate, clawops_gateway_updateDescription),
1930
+ clawops_backup_create: makeEntry(clawops_backup_createSchema, clawops_backup_createAnnotations, handleBackupCreate, clawops_backup_createDescription),
1931
+ clawops_backup_restore: makeEntry(clawops_backup_restoreSchema, clawops_backup_restoreAnnotations, handleBackupRestore, clawops_backup_restoreDescription),
1932
+ clawops_migrate: makeEntry(clawops_migrateSchema, clawops_migrateAnnotations, handleMigrate, clawops_migrateDescription),
1933
+ clawops_stacks_delete: makeEntry(clawops_stacks_deleteSchema, clawops_stacks_deleteAnnotations, handleStacksDelete, clawops_stacks_deleteDescription),
1934
+ clawops_secret_delete: makeEntry(clawops_secret_deleteSchema, clawops_secret_deleteAnnotations, handleSecretDelete, clawops_secret_deleteDescription),
1429
1935
  clawops_workflow_deploy_app: makeEntry(clawops_workflow_deploy_appSchema, clawops_workflow_deploy_appAnnotations, handleWorkflowDeployApp, clawops_workflow_deploy_appDescription),
1430
1936
  clawops_workflow_recover: makeEntry(clawops_workflow_recoverSchema, clawops_workflow_recoverAnnotations, handleWorkflowRecover, clawops_workflow_recoverDescription),
1431
1937
  clawops_task_status: makeEntry(clawops_task_statusSchema, clawops_task_statusAnnotations, handleTaskStatus, clawops_task_statusDescription)
@@ -1477,7 +1983,7 @@ function registerTools(server, opts) {
1477
1983
 
1478
1984
  // src/mcp/resources.ts
1479
1985
  import { readFileSync as readFileSync2 } from "fs";
1480
- import path3 from "path";
1986
+ import path4 from "path";
1481
1987
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1482
1988
  function registerResources(server) {
1483
1989
  server.registerResource(
@@ -1529,7 +2035,7 @@ function registerResources(server) {
1529
2035
  { description: "Full Pulumi output from the last up/destroy run for a stack" },
1530
2036
  async (uri, { name }) => {
1531
2037
  const stackName = Array.isArray(name) ? name[0] : name;
1532
- const filePath = path3.join(getConfigDir(), "state", `${stackName}.last-run.json`);
2038
+ const filePath = path4.join(getConfigDir(), "state", `${stackName}.last-run.json`);
1533
2039
  let text3;
1534
2040
  try {
1535
2041
  const raw = readFileSync2(filePath, "utf-8");
@@ -1726,7 +2232,7 @@ function bearerFrom(header) {
1726
2232
  return match?.[1]?.trim();
1727
2233
  }
1728
2234
  async function serveMcp(opts) {
1729
- const { version } = await import("./package-MF5LPITL.js");
2235
+ const { version } = await import("./package-HYGHRX35.js");
1730
2236
  const makeServer = () => {
1731
2237
  const server = new McpServer({ name: "clawops", version });
1732
2238
  registerTools(server, opts);
@@ -1794,7 +2300,7 @@ async function handleHttp(req, res, sessions, makeServer, token) {
1794
2300
  return;
1795
2301
  }
1796
2302
  const transport = new StreamableHTTPServerTransport({
1797
- sessionIdGenerator: () => randomUUID5(),
2303
+ sessionIdGenerator: () => randomUUID8(),
1798
2304
  onsessioninitialized: (id) => {
1799
2305
  sessions.set(id, transport);
1800
2306
  }