@mycadet/cli 0.12.11 → 0.13.0-dev.202610171626.d97b8b8

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 (94) hide show
  1. package/README.md +13 -0
  2. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  3. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
  4. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  5. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  6. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
  7. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  8. package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  9. package/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
  10. package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  11. package/package.json +8 -3
  12. package/src/__tests__/assistant-client-cloud-auth.test.ts +2 -2
  13. package/src/__tests__/backup-ops-matching.test.ts +15 -13
  14. package/src/__tests__/backup-ops.test.ts +105 -0
  15. package/src/__tests__/backup.test.ts +114 -673
  16. package/src/__tests__/cloud-releases.test.ts +82 -0
  17. package/src/__tests__/commands-mock.ts +101 -0
  18. package/src/__tests__/debug.test.ts +17 -91
  19. package/src/__tests__/devices.test.ts +2 -2
  20. package/src/__tests__/gateway-token.test.ts +267 -0
  21. package/src/__tests__/host-teleport-docker.test.ts +371 -0
  22. package/src/__tests__/host-teleport.test.ts +405 -0
  23. package/src/__tests__/nginx-ingress.test.ts +536 -23
  24. package/src/__tests__/orphan-detection.test.ts +1 -1
  25. package/src/__tests__/paired-lifecycle.test.ts +2 -2
  26. package/src/__tests__/ps-cloud-status.test.ts +6 -6
  27. package/src/__tests__/root-backup-docker.test.ts +294 -0
  28. package/src/__tests__/root-backup.test.ts +527 -0
  29. package/src/__tests__/root-restore-command.test.ts +147 -0
  30. package/src/__tests__/root-signing-key.test.ts +50 -0
  31. package/src/__tests__/setup.test.ts +3 -3
  32. package/src/__tests__/shell-mode.test.ts +83 -0
  33. package/src/__tests__/sleep.test.ts +11 -0
  34. package/src/__tests__/teleport.test.ts +68 -2996
  35. package/src/__tests__/tunnel.test.ts +10 -2
  36. package/src/__tests__/upgrade-local.test.ts +21 -11
  37. package/src/__tests__/upgrade-replay-env.test.ts +2 -2
  38. package/src/__tests__/upgrade-root-recovery.test.ts +185 -0
  39. package/src/__tests__/wake.test.ts +4 -1
  40. package/src/__tests__/windows-lifecycle.test.ts +25 -0
  41. package/src/commands/backup.ts +44 -399
  42. package/src/commands/client.ts +3 -3
  43. package/src/commands/completions.ts +0 -2
  44. package/src/commands/debug.ts +75 -131
  45. package/src/commands/devices.ts +51 -17
  46. package/src/commands/gateway/token.ts +77 -23
  47. package/src/commands/pair.ts +2 -2
  48. package/src/commands/restore.ts +77 -444
  49. package/src/commands/retire.ts +2 -3
  50. package/src/commands/sleep.ts +15 -2
  51. package/src/commands/teleport.ts +47 -1559
  52. package/src/commands/tunnel.ts +48 -7
  53. package/src/commands/upgrade.ts +78 -156
  54. package/src/commands/wake.ts +1 -1
  55. package/src/index.ts +0 -4
  56. package/src/lib/__tests__/http-client-version.test.ts +29 -0
  57. package/src/lib/__tests__/runtime-url.test.ts +3 -75
  58. package/src/lib/__tests__/web-dist.test.ts +27 -1
  59. package/src/lib/assistant-client.ts +1 -1
  60. package/src/lib/assistant-config.ts +23 -18
  61. package/src/lib/backup-ops.ts +37 -251
  62. package/src/lib/cloud-client.ts +10 -13
  63. package/src/lib/cloud-login.ts +2 -1
  64. package/src/lib/cloud-releases.ts +54 -10
  65. package/src/lib/cloud-values.ts +1 -1
  66. package/src/lib/confirm-action.ts +2 -2
  67. package/src/lib/docker.ts +59 -22
  68. package/src/lib/github-repository.ts +2 -0
  69. package/src/lib/guardian-token.ts +1 -23
  70. package/src/lib/hatch-local.ts +17 -1
  71. package/src/lib/host-teleport.ts +506 -0
  72. package/src/lib/http-client.ts +25 -0
  73. package/src/lib/ingress-config.ts +31 -0
  74. package/src/lib/live-voice/tunnel.test.ts +1 -1
  75. package/src/lib/live-voice/tunnel.ts +1 -1
  76. package/src/lib/local.ts +4 -2
  77. package/src/lib/nginx-ingress.ts +454 -79
  78. package/src/lib/process.ts +13 -0
  79. package/src/lib/root-backup.ts +917 -0
  80. package/src/lib/root-signing-key.ts +31 -0
  81. package/src/lib/runtime-url.ts +0 -29
  82. package/src/lib/teleport-backup.ts +3 -16
  83. package/src/lib/tunnel.ts +1 -0
  84. package/src/lib/upgrade-lifecycle.ts +63 -141
  85. package/src/lib/web-dist.ts +79 -1
  86. package/src/__tests__/bundle-staging.test.ts +0 -268
  87. package/src/__tests__/unpair.test.ts +0 -160
  88. package/src/commands/rollback.ts +0 -479
  89. package/src/commands/unpair.ts +0 -122
  90. package/src/lib/__tests__/job-polling.test.ts +0 -278
  91. package/src/lib/__tests__/local-runtime-client.test.ts +0 -666
  92. package/src/lib/bundle-staging.ts +0 -220
  93. package/src/lib/job-polling.ts +0 -195
  94. package/src/lib/local-runtime-client.ts +0 -374
@@ -1,479 +0,0 @@
1
- import { isCloudHome } from "../lib/cloud-values.js";
2
- import {
3
- findAssistantByName,
4
- getActiveAssistant,
5
- loadAllAssistants,
6
- saveAssistantEntry,
7
- type AssistantEntry,
8
- } from "../lib/assistant-config";
9
- import {
10
- GATEWAY_INTERNAL_PORT,
11
- ASSISTANT_INTERNAL_PORT,
12
- dockerResourceNames,
13
- startContainers,
14
- stopContainers,
15
- } from "../lib/docker";
16
- import type { ServiceName } from "../lib/docker";
17
- import { emitCliError, categorizeUpgradeError } from "../lib/cli-error.js";
18
- import {
19
- readCloudToken,
20
- rollbackCloudAssistant,
21
- } from "../lib/cloud-client.js";
22
- import {
23
- broadcastUpgradeEvent,
24
- buildCompleteEvent,
25
- buildProgressEvent,
26
- buildStartingEvent,
27
- buildUpgradeCommitMessage,
28
- captureReplayState,
29
- commitWorkspaceViaGateway,
30
- fetchCurrentVersion,
31
- fetchPreviousVersion,
32
- performDockerRollback,
33
- rollbackMigrations,
34
- UPGRADE_PROGRESS,
35
- waitForReady,
36
- } from "../lib/upgrade-lifecycle.js";
37
-
38
- function parseArgs(): { name: string | null; version: string | null } {
39
- const args = process.argv.slice(3);
40
- let name: string | null = null;
41
- let version: string | null = null;
42
-
43
- for (let i = 0; i < args.length; i++) {
44
- const arg = args[i];
45
- if (arg === "--help" || arg === "-h") {
46
- console.log("Usage: cadet rollback [<name>] [--version <version>]");
47
- console.log("");
48
- console.log(
49
- "Roll back a Docker or cloud assistant to a previous version.",
50
- );
51
- console.log("");
52
- console.log("Arguments:");
53
- console.log(
54
- " <name> Name of the assistant, defaults to active or only assistant",
55
- );
56
- console.log("");
57
- console.log("Options:");
58
- console.log(
59
- " --version <version> Target version, optional for managed — omit to roll back to previous",
60
- );
61
- console.log("");
62
- console.log("Examples:");
63
- console.log(
64
- " cadet rollback my-assistant # Roll back to previous version (Docker or managed)",
65
- );
66
- console.log(
67
- " cadet rollback my-assistant --version v1.2.3 # Roll back to a specific version",
68
- );
69
- process.exit(0);
70
- } else if (arg === "--version") {
71
- const next = args[i + 1];
72
- if (!next || next.startsWith("-")) {
73
- console.error("Error: --version requires a value");
74
- emitCliError("UNKNOWN", "--version requires a value");
75
- process.exit(1);
76
- }
77
- version = next;
78
- i++;
79
- } else if (!arg.startsWith("-")) {
80
- name = arg;
81
- } else {
82
- console.error(`Error: Unknown option '${arg}'.`);
83
- emitCliError("UNKNOWN", `Unknown option '${arg}'`);
84
- process.exit(1);
85
- }
86
- }
87
-
88
- return { name, version };
89
- }
90
-
91
- /**
92
- * Resolve which assistant to target for the rollback command. Priority:
93
- * 1. Explicit name argument
94
- * 2. Active assistant set via `cadet use`
95
- * 3. Sole assistant (when exactly one exists)
96
- */
97
- function resolveTargetAssistant(nameArg: string | null): AssistantEntry {
98
- if (nameArg) {
99
- const entry = findAssistantByName(nameArg);
100
- if (!entry) {
101
- console.error(`No assistant found with name '${nameArg}'.`);
102
- emitCliError(
103
- "ASSISTANT_NOT_FOUND",
104
- `No assistant found with name '${nameArg}'.`,
105
- );
106
- process.exit(1);
107
- }
108
- return entry;
109
- }
110
-
111
- const active = getActiveAssistant();
112
- if (active) {
113
- const entry = findAssistantByName(active);
114
- if (entry) return entry;
115
- }
116
-
117
- const all = loadAllAssistants();
118
- if (all.length === 1) return all[0];
119
-
120
- if (all.length === 0) {
121
- const msg = "No assistants found. Run 'cadet hatch' first.";
122
- console.error(msg);
123
- emitCliError("ASSISTANT_NOT_FOUND", msg);
124
- } else {
125
- const msg =
126
- "Multiple assistants found. Specify a name or set an active assistant with 'cadet use <name>'.";
127
- console.error(msg);
128
- emitCliError("ASSISTANT_NOT_FOUND", msg);
129
- }
130
- process.exit(1);
131
- }
132
-
133
- async function rollbackPlatformViaEndpoint(
134
- entry: AssistantEntry,
135
- version?: string,
136
- ): Promise<void> {
137
- // Step 1 — Authenticate
138
- const token = readCloudToken();
139
- if (!token) {
140
- const msg =
141
- "Error: Not logged in. Run `cadet login --token <token>` first.";
142
- console.error(msg);
143
- emitCliError("AUTH_FAILED", msg);
144
- process.exit(1);
145
- }
146
-
147
- // Fetch current version from health endpoint (best-effort)
148
- const currentVersion = await fetchCurrentVersion(entry.runtimeUrl);
149
-
150
- // Step 3 — Call rollback endpoint
151
- if (version) {
152
- console.log(`Rolling back to ${version}...`);
153
- } else {
154
- console.log("Rolling back to previous version...");
155
- }
156
-
157
- let result: { detail: string; version: string | null };
158
- try {
159
- result = await rollbackCloudAssistant(
160
- token,
161
- entry.assistantId,
162
- version,
163
- entry.runtimeUrl,
164
- );
165
- } catch (err) {
166
- const detail = err instanceof Error ? err.message : String(err);
167
-
168
- // Map specific server error messages to actionable CLI output
169
- if (detail.includes("No previous version")) {
170
- console.error(
171
- "No previous version available. A successful upgrade must have been performed first.",
172
- );
173
- } else if (detail.includes("not older")) {
174
- console.error(
175
- `Target version is not older than the current version. Use 'cadet upgrade --version' instead.`,
176
- );
177
- } else if (detail.includes("not found")) {
178
- console.error(
179
- version
180
- ? `Version ${version} not found.`
181
- : `Rollback target not found.`,
182
- );
183
- } else if (
184
- err instanceof TypeError ||
185
- detail.includes("fetch failed") ||
186
- detail.includes("ECONNREFUSED")
187
- ) {
188
- console.error(
189
- `Connection error: ${detail}\nIs Cadet Cloud reachable? Try 'cadet wake' if the assistant is asleep.`,
190
- );
191
- } else {
192
- console.error(`Error: ${detail}`);
193
- }
194
-
195
- emitCliError("PLATFORM_API_ERROR", "Cadet Cloud rollback failed", detail);
196
- await broadcastUpgradeEvent(
197
- entry.runtimeUrl,
198
- entry.assistantId,
199
- buildCompleteEvent(currentVersion ?? version ?? "unknown", false),
200
- );
201
- process.exit(1);
202
- }
203
-
204
- const rolledBackVersion = result.version ?? version ?? "unknown";
205
-
206
- // Step 4 — Print success
207
- console.log(`Rolled back to version ${rolledBackVersion}.`);
208
- if (!version) {
209
- console.log("Tip: Run 'cadet rollback' again to undo.");
210
- }
211
- }
212
-
213
- export const rollback = Object.assign(
214
- async function rollback(): Promise<void> {
215
- const { name, version } = parseArgs();
216
- const entry = resolveTargetAssistant(name);
217
- const cloud = entry.cloud;
218
-
219
- if (cloud === "apple-container") {
220
- console.error(
221
- `Error: '${entry.assistantId}' uses the Apple Containers runtime. Rollback is not yet supported for this topology.`,
222
- );
223
- process.exit(1);
224
- }
225
-
226
- // ---------- Managed (Cadet Cloud) rollback ----------
227
- if (isCloudHome(cloud)) {
228
- await rollbackPlatformViaEndpoint(entry, version ?? undefined);
229
- return;
230
- }
231
-
232
- // ---------- Unsupported topologies ----------
233
- if (cloud !== "docker") {
234
- const msg = "Rollback is only supported for Docker and cloud assistants.";
235
- console.error(msg);
236
- emitCliError("UNSUPPORTED_TOPOLOGY", msg);
237
- process.exit(1);
238
- }
239
-
240
- // ---------- Docker: Targeted version rollback (--version specified) ----------
241
- if (version) {
242
- try {
243
- await performDockerRollback(entry, { targetVersion: version });
244
- } catch (err) {
245
- const detail = err instanceof Error ? err.message : String(err);
246
- console.error(`\n❌ Rollback failed: ${detail}`);
247
- await broadcastUpgradeEvent(
248
- entry.runtimeUrl,
249
- entry.assistantId,
250
- buildCompleteEvent(version ?? "unknown", false),
251
- );
252
- emitCliError(categorizeUpgradeError(err), "Rollback failed", detail);
253
- process.exit(1);
254
- }
255
- return;
256
- }
257
-
258
- // ---------- Docker: Saved-state rollback (no --version) ----------
259
-
260
- // Fetch current + previous version from live APIs
261
- const currentVersion = await fetchCurrentVersion(entry.runtimeUrl);
262
- const previousVersion =
263
- (await fetchPreviousVersion(currentVersion, entry.previousVersion)) ??
264
- "unknown";
265
-
266
- // Verify rollback state exists
267
- if (!entry.previousContainerInfo) {
268
- const msg =
269
- "No rollback state available. Run `cadet upgrade` first to create a rollback point.";
270
- console.error(msg);
271
- emitCliError("ROLLBACK_NO_STATE", msg);
272
- process.exit(1);
273
- }
274
-
275
- // Roll back to the previous images. Older entries recorded per-service
276
- // digests (the image sha captured at upgrade time) — those are gone from
277
- // the contract now; the recorded image refs are the rollback target.
278
- const prev = entry.previousContainerInfo;
279
- if (!prev.assistantImage || !prev.gatewayImage || !prev.cesImage) {
280
- const msg =
281
- "Incomplete rollback state. Previous container image refs are missing.";
282
- console.error(msg);
283
- emitCliError("ROLLBACK_NO_STATE", msg);
284
- process.exit(1);
285
- }
286
-
287
- // Build image refs from the previous record
288
- const previousImageRefs: Record<ServiceName, string> = {
289
- assistant: prev.assistantImage,
290
- "credential-executor": prev.cesImage,
291
- gateway: prev.gatewayImage,
292
- };
293
-
294
- const instanceName = entry.assistantId;
295
- const res = dockerResourceNames(instanceName);
296
-
297
- try {
298
- // Record rollback start in workspace git history
299
- await commitWorkspaceViaGateway(
300
- entry.runtimeUrl,
301
- entry.assistantId,
302
- buildUpgradeCommitMessage({
303
- action: "rollback",
304
- phase: "starting",
305
- from: currentVersion ?? "unknown",
306
- to: previousVersion,
307
- topology: "docker",
308
- assistantId: entry.assistantId,
309
- }),
310
- );
311
-
312
- console.log(
313
- `🔄 Rolling back Docker assistant '${instanceName}' to ${previousVersion}...\n`,
314
- );
315
-
316
- const {
317
- bootstrapSecret,
318
- cesServiceToken,
319
- signingKey,
320
- extraAssistantEnv,
321
- extraGatewayEnv,
322
- } = await captureReplayState(res);
323
-
324
- // Parse gateway port from entry's runtimeUrl, fall back to default
325
- let gatewayPort = GATEWAY_INTERNAL_PORT;
326
- try {
327
- const parsed = new URL(entry.runtimeUrl);
328
- const port = parseInt(parsed.port, 10);
329
- if (!isNaN(port)) {
330
- gatewayPort = port;
331
- }
332
- } catch {
333
- // use default
334
- }
335
-
336
- // Recover the assistant host port from the entry, fall back to default.
337
- const assistantPort =
338
- entry.containerInfo?.assistantPort ?? ASSISTANT_INTERNAL_PORT;
339
-
340
- // Notify connected clients that a rollback is about to begin (best-effort)
341
- console.log("📢 Notifying connected clients...");
342
- await broadcastUpgradeEvent(
343
- entry.runtimeUrl,
344
- entry.assistantId,
345
- buildStartingEvent(previousVersion),
346
- );
347
- // Brief pause to allow SSE delivery before containers stop.
348
- await new Promise((r) => setTimeout(r, 500));
349
-
350
- // Roll back migrations to pre-upgrade state (must happen before containers stop)
351
- if (
352
- entry.previousDbMigrationVersion !== undefined ||
353
- entry.previousWorkspaceMigrationId !== undefined
354
- ) {
355
- console.log("🔄 Reverting database changes...");
356
- await broadcastUpgradeEvent(
357
- entry.runtimeUrl,
358
- entry.assistantId,
359
- buildProgressEvent(UPGRADE_PROGRESS.REVERTING_MIGRATIONS),
360
- );
361
- await rollbackMigrations(
362
- entry.runtimeUrl,
363
- entry.assistantId,
364
- entry.previousDbMigrationVersion,
365
- entry.previousWorkspaceMigrationId,
366
- );
367
- }
368
-
369
- // Progress: switching version (must be sent BEFORE stopContainers)
370
- await broadcastUpgradeEvent(
371
- entry.runtimeUrl,
372
- entry.assistantId,
373
- buildProgressEvent(UPGRADE_PROGRESS.SWITCHING),
374
- );
375
-
376
- console.log("🛑 Stopping existing containers...");
377
- await stopContainers(res);
378
- console.log("✅ Containers stopped\n");
379
-
380
- console.log("🚀 Starting containers with previous version...");
381
- await startContainers(
382
- {
383
- signingKey,
384
- bootstrapSecret,
385
- cesServiceToken,
386
- extraAssistantEnv,
387
- extraGatewayEnv,
388
- gatewayPort,
389
- assistantPort,
390
- imageTags: previousImageRefs,
391
- instanceName,
392
- res,
393
- },
394
- (msg) => console.log(msg),
395
- );
396
- console.log("✅ Containers started\n");
397
-
398
- console.log("Waiting for assistant to become ready...");
399
- const ready = await waitForReady(entry.runtimeUrl);
400
-
401
- if (ready) {
402
- // Swap current/previous state to enable "rollback the rollback"
403
- const updatedEntry: AssistantEntry = {
404
- ...entry,
405
- containerInfo: {
406
- assistantImage: prev.assistantImage ?? previousImageRefs.assistant,
407
- gatewayImage: prev.gatewayImage ?? previousImageRefs.gateway,
408
- cesImage: prev.cesImage ?? previousImageRefs["credential-executor"],
409
- assistantPort,
410
- },
411
- previousContainerInfo: entry.containerInfo,
412
- // Clear the backup path — it belonged to the upgrade we just rolled back
413
- preUpgradeBackupPath: undefined,
414
- previousDbMigrationVersion: undefined,
415
- previousWorkspaceMigrationId: undefined,
416
- };
417
- saveAssistantEntry(updatedEntry);
418
-
419
- // Notify clients that the rollback succeeded
420
- await broadcastUpgradeEvent(
421
- entry.runtimeUrl,
422
- entry.assistantId,
423
- buildCompleteEvent(previousVersion, true),
424
- );
425
-
426
- // Record successful rollback in workspace git history
427
- await commitWorkspaceViaGateway(
428
- entry.runtimeUrl,
429
- entry.assistantId,
430
- buildUpgradeCommitMessage({
431
- action: "rollback",
432
- phase: "complete",
433
- from: currentVersion ?? "unknown",
434
- to: previousVersion,
435
- topology: "docker",
436
- assistantId: entry.assistantId,
437
- result: "success",
438
- }),
439
- );
440
-
441
- console.log(
442
- `\n✅ Docker assistant '${instanceName}' rolled back to ${previousVersion}.`,
443
- );
444
- console.log(
445
- "\nTip: To also restore data from before the upgrade, use `cadet restore --from <backup-path>`.",
446
- );
447
- } else {
448
- console.error(
449
- `\n❌ Containers failed to become ready within the timeout.`,
450
- );
451
- console.log(
452
- ` Check logs with: docker logs -f ${res.assistantContainer}`,
453
- );
454
- await broadcastUpgradeEvent(
455
- entry.runtimeUrl,
456
- entry.assistantId,
457
- buildCompleteEvent(previousVersion, false),
458
- );
459
- emitCliError(
460
- "READINESS_TIMEOUT",
461
- "Rolled-back containers failed to become ready within the timeout.",
462
- );
463
- process.exit(1);
464
- }
465
- } catch (err) {
466
- const detail = err instanceof Error ? err.message : String(err);
467
- console.error(`\n❌ Rollback failed: ${detail}`);
468
- await broadcastUpgradeEvent(
469
- entry.runtimeUrl,
470
- entry.assistantId,
471
- buildCompleteEvent(previousVersion, false),
472
- );
473
- emitCliError(categorizeUpgradeError(err), "Rollback failed", detail);
474
- process.exit(1);
475
- }
476
- },
477
- { description: "Roll back an assistant to a previous version" },
478
- );
479
-
@@ -1,122 +0,0 @@
1
- /**
2
- * `cadet unpair <name> [--yes]`
3
- *
4
- * Forget a pairing imported from another machine via `cadet connect import`:
5
- * remove its lockfile entry and stored guardian token from THIS machine. Only
6
- * paired assistants (`cloud: "paired"`) can be unpaired — `cadet retire` owns
7
- * local and cloud assistants.
8
- *
9
- * This is client-side only: it forgets the connection here but does not revoke
10
- * the device on the host. (Host-side revocation is `cadet devices`.)
11
- */
12
-
13
- import {
14
- formatAssistantLookupError,
15
- getAssistantDisplayName,
16
- lookupAssistantByIdentifier,
17
- removeAssistantEntry,
18
- } from "../lib/assistant-config";
19
- import { parseAssistantTargetArg } from "../lib/assistant-target-args.js";
20
- import {
21
- canPromptForConfirmation,
22
- confirmAction,
23
- } from "../lib/confirm-action.js";
24
- import { deleteGuardianToken } from "../lib/guardian-token";
25
-
26
- function printUsage(): void {
27
- console.log(`cadet unpair [beta] - Forget a paired assistant imported from another machine
28
-
29
- USAGE:
30
- cadet unpair <name> [--yes]
31
-
32
- ARGUMENTS:
33
- <name> Name or id of the paired assistant to forget
34
-
35
- OPTIONS:
36
- --yes Skip the interactive confirmation prompt (for automation)
37
-
38
- Removes the local connection (lockfile entry + stored token). Only paired
39
- assistants (imported via 'cadet connect import') can be unpaired; use
40
- 'cadet retire' for local or cloud assistants.
41
-
42
- EXAMPLES:
43
- cadet unpair paired-desk
44
- cadet unpair "Desk Box"
45
- cadet unpair paired-desk --yes
46
- `);
47
- }
48
-
49
- export const unpair = Object.assign(
50
- async function unpair(): Promise<void> {
51
- const args = process.argv.slice(3);
52
-
53
- if (args.includes("--help") || args.includes("-h")) {
54
- printUsage();
55
- return;
56
- }
57
-
58
- const yes = args.includes("--yes");
59
- const name = parseAssistantTargetArg(args, []);
60
- if (!name) {
61
- console.error("Error: assistant name or id is required.");
62
- printUsage();
63
- process.exit(1);
64
- }
65
-
66
- const lookup = lookupAssistantByIdentifier(name);
67
- if (lookup.status !== "found") {
68
- console.error(formatAssistantLookupError(name, lookup));
69
- process.exit(1);
70
- }
71
- const entry = lookup.entry;
72
-
73
- if (entry.cloud !== "paired") {
74
- console.error(
75
- `Error: '${name}' is not a paired assistant. Use \`cadet retire\` to remove a local or cloud assistant.`,
76
- );
77
- process.exit(1);
78
- }
79
-
80
- // Print the resolved identity before acting (cli/AGENTS.md).
81
- const displayName = getAssistantDisplayName(entry);
82
- console.log("Pairing to unpair:");
83
- if (displayName !== entry.assistantId) {
84
- console.log(` Name: ${displayName}`);
85
- }
86
- console.log(` ID: ${entry.assistantId}`);
87
- if (entry.runtimeUrl) {
88
- console.log(` Host: ${entry.runtimeUrl}`);
89
- }
90
- console.log("");
91
-
92
- if (!yes) {
93
- if (!canPromptForConfirmation()) {
94
- console.error(
95
- "Error: Refusing to unpair without confirmation in a non-interactive terminal.",
96
- );
97
- console.error("Re-run with --yes to confirm from automation.");
98
- process.exit(1);
99
- }
100
- const confirmed = await confirmAction(
101
- "Press Enter to unpair, or Esc/q to cancel: ",
102
- );
103
- if (!confirmed) {
104
- console.log("Unpair cancelled.");
105
- process.exit(1);
106
- }
107
- }
108
-
109
- removeAssistantEntry(entry.assistantId);
110
- deleteGuardianToken(entry.assistantId);
111
-
112
- console.log(
113
- `Unpaired '${name}' — removed the local connection (lockfile entry + token).`,
114
- );
115
- console.log("");
116
- console.log(
117
- "Note: this only forgets the connection on this machine. The assistant's host can fully revoke this device from its side.",
118
- );
119
- },
120
- { description: "Forget a paired assistant imported from another machine [beta]" },
121
- );
122
-