@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.
- package/README.md +120 -2
- package/dist/agent-activity-25FAWLAI.js +16 -0
- package/dist/{apply-JARPVA5Y.js → apply-F5SQ2ZOS.js} +7 -7
- package/dist/backup-flows-NDP5JTR3.js +29 -0
- package/dist/{bootstrap-3PBVY7MA.js → bootstrap-32TZXVDF.js} +3 -3
- package/dist/bootstrap.sh.tmpl +4 -0
- package/dist/chunk-27335SIH.js +51 -0
- package/dist/{chunk-E4NJSJ54.js → chunk-4PY6PN4G.js} +51 -7
- package/dist/{chunk-PTCVFLZ3.js → chunk-66R52R7K.js} +7 -11
- package/dist/{chunk-7T4CIT7C.js → chunk-AX65JMDZ.js} +15 -15
- package/dist/{chunk-TYHTISI5.js → chunk-CQOYUWR7.js} +610 -104
- package/dist/chunk-E6BRQK67.js +289 -0
- package/dist/chunk-LV7UO5CG.js +82 -0
- package/dist/{chunk-GIEVAGEK.js → chunk-N5ZYUTZS.js} +16 -1
- package/dist/{chunk-CPCO6UJK.js → chunk-OUQVKIIA.js} +368 -30
- package/dist/{chunk-XFN3YDZE.js → chunk-PASAVVNS.js} +1 -1
- package/dist/chunk-PCSX6Y3U.js +297 -0
- package/dist/chunk-POYS3EVQ.js +14 -0
- package/dist/chunk-SX75L2T3.js +46 -0
- package/dist/{chunk-2Z7J3ERQ.js → chunk-TYNMOYVM.js} +1 -1
- package/dist/{chunk-2I7KCMXZ.js → chunk-UAJJEDCJ.js} +2 -2
- package/dist/{chunk-NUDIHZEH.js → chunk-WPZIJONN.js} +14 -12
- package/dist/{chunk-CHNSL3TC.js → chunk-YPVWF7IA.js} +1 -1
- package/dist/{chunk-3OLUJPCB.js → chunk-Z3XLWZWF.js} +1 -1
- package/dist/cli.js +303 -698
- package/dist/conn-M2V5HBPJ.js +9 -0
- package/dist/{context-WOKSKAIA.js → context-EHSLWKSH.js} +2 -2
- package/dist/{docker-3PDO4TS7.js → docker-IKM5GRMC.js} +2 -2
- package/dist/{flows-HGJIA6EL.js → flows-6B2I6TRW.js} +5 -5
- package/dist/{generate-NE54BATS.js → generate-4YYNW3IU.js} +5 -4
- package/dist/{harden-RUOUA5BV.js → harden-UHAOVQMZ.js} +3 -3
- package/dist/{mcp-wire-FIO5DFBY.js → mcp-wire-BXF4DJFT.js} +21 -14
- package/dist/migrate-flow-24XA4GZL.js +25 -0
- package/dist/{package-MF5LPITL.js → package-HYGHRX35.js} +3 -1
- package/dist/{pool-PIIKJ4L2.js → pool-NT4XBSUT.js} +2 -2
- package/dist/{privileged-JFA6LLTV.js → privileged-5Y6O4H7Z.js} +1 -1
- package/dist/{ready-KDNRBNSU.js → ready-IJEUBFVX.js} +2 -2
- package/dist/{remote-config-3HZLUWEA.js → remote-config-OZYBMBSK.js} +3 -3
- package/dist/{runtime-X3KW5TCP.js → runtime-VW53TU2W.js} +5 -1
- package/dist/{server-OXWHBTXK.js → server-E3YA32ND.js} +21 -14
- package/dist/ssh-HOM7GF3M.js +13 -0
- package/dist/stack-delete-NTIGKWUF.js +13 -0
- package/dist/{tailscale-cutover-EKAQSDXP.js → tailscale-cutover-V42HO5UM.js} +1 -1
- package/dist/{validate-JXKN5WD6.js → validate-DZE6LFEB.js} +1 -1
- package/dist/{wait-WO72TRRQ.js → wait-DOUNNJNN.js} +2 -2
- package/package.json +3 -1
- package/spec/mcp-tools.yaml +213 -7
- package/dist/logs-5A7YYA53.js +0 -15
- package/dist/ssh-VAI5DU5M.js +0 -11
- package/dist/upgrade-ULD4KFYC.js +0 -88
- /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-
|
|
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-
|
|
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-
|
|
44
|
+
} from "./chunk-UAJJEDCJ.js";
|
|
16
45
|
import {
|
|
17
46
|
validatePlan
|
|
18
|
-
} from "./chunk-
|
|
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-
|
|
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
|
-
|
|
30
|
-
|
|
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-
|
|
78
|
+
} from "./chunk-SOZ62ZXW.js";
|
|
37
79
|
import {
|
|
38
80
|
execPrivileged
|
|
39
|
-
} from "./chunk-
|
|
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
|
|
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`.
|
|
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
|
|
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
|
|
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
|
-
|
|
825
|
-
|
|
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
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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/
|
|
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-
|
|
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 =
|
|
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-
|
|
935
|
-
const { applyPlan: applyPlan2 } = await import("./apply-
|
|
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
|
|
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 =
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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 =
|
|
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-
|
|
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
|
|
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 =
|
|
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-
|
|
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: () =>
|
|
2303
|
+
sessionIdGenerator: () => randomUUID8(),
|
|
1798
2304
|
onsessioninitialized: (id) => {
|
|
1799
2305
|
sessions.set(id, transport);
|
|
1800
2306
|
}
|