@mycadet/cli 0.12.7 → 0.12.8

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 (165) hide show
  1. package/README.md +2 -2
  2. package/knip.json +1 -1
  3. package/node_modules/@mycadet/assistant-api/events/cloud-disconnected.ts +21 -0
  4. package/node_modules/@mycadet/assistant-api/events/host-bash.ts +1 -1
  5. package/node_modules/@mycadet/assistant-api/events/host-cu.ts +1 -1
  6. package/node_modules/@mycadet/assistant-api/events/host-file.ts +1 -1
  7. package/node_modules/@mycadet/assistant-api/events/notification-intent.ts +1 -1
  8. package/node_modules/@mycadet/assistant-api/events/{show-platform-login.ts → show-cloud-login.ts} +3 -5
  9. package/node_modules/@mycadet/assistant-api/index.ts +11 -11
  10. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +1 -1
  11. package/node_modules/@mycadet/{service-contracts/src/platform-credential.ts → assistant-api/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
  12. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  13. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  14. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  15. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  16. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  17. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  18. package/node_modules/@mycadet/assistant-api/responses/home.ts +1 -1
  19. package/node_modules/@mycadet/assistant-api/surfaces.ts +4 -4
  20. package/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
  21. package/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
  22. package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
  23. package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
  24. package/node_modules/@mycadet/environments/src/types.ts +6 -6
  25. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
  26. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
  27. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/seeds.ts +6 -6
  28. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
  29. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/types.ts +6 -6
  30. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +1 -1
  31. package/node_modules/@mycadet/{assistant-api/node_modules/@mycadet/service-contracts/src/platform-credential.ts → local-mode/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
  32. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  33. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  34. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  35. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  36. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  37. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  38. package/node_modules/@mycadet/local-mode/src/__tests__/pair.test.ts +5 -5
  39. package/node_modules/@mycadet/local-mode/src/__tests__/retire.test.ts +1 -1
  40. package/node_modules/@mycadet/local-mode/src/config.ts +2 -2
  41. package/node_modules/@mycadet/local-mode/src/index.ts +1 -1
  42. package/node_modules/@mycadet/local-mode/src/lockfile-contract.test.ts +3 -3
  43. package/node_modules/@mycadet/local-mode/src/lockfile-contract.ts +10 -5
  44. package/node_modules/@mycadet/local-mode/src/lockfile.test.ts +15 -15
  45. package/node_modules/@mycadet/local-mode/src/lockfile.ts +9 -9
  46. package/node_modules/@mycadet/local-mode/src/pair.ts +2 -2
  47. package/node_modules/@mycadet/local-mode/src/retire.ts +3 -3
  48. package/node_modules/@mycadet/local-mode/src/teleport-backup-policy.ts +2 -2
  49. package/node_modules/@mycadet/local-mode/src/unpair.ts +2 -2
  50. package/node_modules/@mycadet/service-contracts/package.json +1 -1
  51. package/node_modules/@mycadet/{local-mode/node_modules/@mycadet/service-contracts/src/platform-credential.ts → service-contracts/src/cloud-credential.ts} +9 -9
  52. package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  53. package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  54. package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  55. package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  56. package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  57. package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  58. package/package.json +3 -2
  59. package/src/__tests__/assistant-api-key-resolution.test.ts +6 -6
  60. package/src/__tests__/{assistant-client-platform-auth.test.ts → assistant-client-cloud-auth.test.ts} +10 -10
  61. package/src/__tests__/assistant-client-refresh.test.ts +2 -2
  62. package/src/__tests__/backup.test.ts +86 -91
  63. package/src/__tests__/client-token.test.ts +1 -1
  64. package/src/__tests__/client-tui-refresh.test.ts +1 -1
  65. package/src/__tests__/client-web-config.test.ts +2 -2
  66. package/src/__tests__/{platform-client.test.ts → cloud-client.test.ts} +59 -59
  67. package/src/__tests__/{platform-releases.test.ts → cloud-releases.test.ts} +5 -5
  68. package/src/__tests__/{cloudflare-tunnel.test.ts → cloudflare.test.ts} +1 -1
  69. package/src/__tests__/connect-import.test.ts +55 -39
  70. package/src/__tests__/debug.test.ts +38 -38
  71. package/src/__tests__/guardian-token.test.ts +149 -56
  72. package/src/__tests__/hatch-provider-secrets.test.ts +2 -2
  73. package/src/__tests__/helpers/guardian-token.ts +22 -0
  74. package/src/__tests__/helpers/http.ts +10 -0
  75. package/src/__tests__/nginx-ingress.test.ts +18 -18
  76. package/src/__tests__/orphan-detection.test.ts +1 -1
  77. package/src/__tests__/pair.test.ts +1 -8
  78. package/src/__tests__/{ps-platform-status.test.ts → ps-cloud-status.test.ts} +79 -26
  79. package/src/__tests__/{recover.test.ts → restore.test.ts} +29 -28
  80. package/src/__tests__/retire.test.ts +58 -199
  81. package/src/__tests__/root-process-lockfile.test.ts +99 -0
  82. package/src/__tests__/sleep.test.ts +19 -19
  83. package/src/__tests__/statefulset.test.ts +8 -8
  84. package/src/__tests__/{tailscale-tunnel.test.ts → tailscale.test.ts} +1 -1
  85. package/src/__tests__/teleport-backup.test.ts +3 -3
  86. package/src/__tests__/teleport.test.ts +180 -196
  87. package/src/__tests__/tunnel.test.ts +255 -227
  88. package/src/__tests__/uninstall.test.ts +13 -4
  89. package/src/__tests__/upgrade-local.test.ts +3 -3
  90. package/src/__tests__/upgrade-replay-env.test.ts +12 -12
  91. package/src/__tests__/wake.test.ts +258 -41
  92. package/src/__tests__/workos-pkce.test.ts +2 -2
  93. package/src/commands/backup.ts +22 -22
  94. package/src/commands/client.ts +76 -76
  95. package/src/commands/debug.ts +20 -20
  96. package/src/commands/exec.ts +5 -5
  97. package/src/commands/hatch.ts +22 -22
  98. package/src/commands/login.ts +22 -22
  99. package/src/commands/logs.ts +1 -1
  100. package/src/commands/ps.ts +188 -79
  101. package/src/commands/restart.ts +30 -0
  102. package/src/commands/restore.ts +138 -20
  103. package/src/commands/retire.ts +46 -148
  104. package/src/commands/rollback.ts +18 -24
  105. package/src/commands/setup.ts +2 -2
  106. package/src/commands/teleport.ts +93 -93
  107. package/src/commands/terminal.ts +157 -17
  108. package/src/commands/tunnel.ts +80 -122
  109. package/src/commands/uninstall.ts +55 -7
  110. package/src/commands/unpair.ts +3 -3
  111. package/src/commands/upgrade.ts +22 -46
  112. package/src/commands/wake.ts +162 -46
  113. package/src/index.ts +3 -9
  114. package/src/lib/__tests__/{platform-client-signed-url.test.ts → cloud-client-signed-url.test.ts} +16 -16
  115. package/src/lib/__tests__/job-polling.test.ts +1 -1
  116. package/src/lib/__tests__/local-runtime-client.test.ts +4 -4
  117. package/src/lib/__tests__/runtime-url.test.ts +1 -1
  118. package/src/lib/assistant-api-key-resolution.ts +5 -5
  119. package/src/lib/assistant-client.ts +8 -8
  120. package/src/lib/assistant-config.ts +58 -12
  121. package/src/lib/{platform-client.ts → cloud-client.ts} +107 -98
  122. package/src/lib/cloud-login.ts +24 -24
  123. package/src/lib/{platform-releases.ts → cloud-releases.ts} +5 -5
  124. package/src/lib/cloud-values.ts +3 -0
  125. package/src/lib/config-utils.ts +1 -1
  126. package/src/lib/detached-process.ts +1 -1
  127. package/src/lib/docker.ts +111 -10
  128. package/src/lib/environments/__tests__/paths.test.ts +2 -2
  129. package/src/lib/environments/__tests__/resolve.test.ts +26 -26
  130. package/src/lib/environments/resolve.ts +8 -8
  131. package/src/lib/guardian-token.ts +36 -4
  132. package/src/lib/hatch-local.ts +19 -23
  133. package/src/lib/health-check.ts +6 -6
  134. package/src/lib/ingress-config.ts +8 -35
  135. package/src/lib/job-polling.ts +1 -1
  136. package/src/lib/live-voice/connection.ts +12 -12
  137. package/src/lib/live-voice/tunnel.test.ts +11 -11
  138. package/src/lib/live-voice/tunnel.ts +15 -15
  139. package/src/lib/local-runtime-client.ts +20 -20
  140. package/src/lib/local.ts +9 -30
  141. package/src/lib/nginx-ingress.ts +41 -14
  142. package/src/lib/process.ts +1 -1
  143. package/src/lib/provider-secrets.ts +3 -3
  144. package/src/lib/retire-apple-container.ts +1 -1
  145. package/src/lib/runtime-url.ts +8 -8
  146. package/src/lib/statefulset.ts +2 -2
  147. package/src/lib/sync-cloud-assistants.ts +18 -18
  148. package/src/lib/teleport-backup.ts +4 -4
  149. package/src/lib/terminal-client.ts +17 -17
  150. package/src/lib/terminal-session.ts +24 -24
  151. package/src/lib/{tunnel-edge.ts → tunnel.ts} +199 -11
  152. package/src/lib/tunnels/cadet.ts +29 -0
  153. package/src/lib/{cloudflare-tunnel.ts → tunnels/cloudflare.ts} +15 -4
  154. package/src/lib/tunnels/gateway-notify.ts +61 -0
  155. package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +201 -30
  156. package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +19 -5
  157. package/src/lib/upgrade-lifecycle.ts +3 -17
  158. package/src/lib/upgrade-preflight.ts +2 -2
  159. package/src/lib/workos-pkce.ts +5 -5
  160. package/node_modules/@mycadet/assistant-api/events/platform-disconnected.ts +0 -21
  161. package/src/commands/recover.ts +0 -131
  162. package/src/commands/roadmap.ts +0 -459
  163. package/src/commands/ssh.ts +0 -144
  164. package/src/commands/workflows.ts +0 -305
  165. package/src/lib/managed-cloud.ts +0 -3
@@ -1,4 +1,4 @@
1
- import { isManagedCloud } from "../lib/managed-cloud.js";
1
+ import { isCloudHome } from "../lib/cloud-values.js";
2
2
  import { join } from "path";
3
3
 
4
4
  import {
@@ -11,6 +11,7 @@ import {
11
11
  getDaemonPidPath,
12
12
  loadAllAssistants,
13
13
  lookupAssistantByIdentifier,
14
+ readRootProcesses,
14
15
  type AssistantEntry,
15
16
  } from "../lib/assistant-config";
16
17
  import { parseAssistantTargetArg } from "../lib/assistant-target-args.js";
@@ -23,7 +24,7 @@ import {
23
24
  fetchManagedPs,
24
25
  type ManagedProcessEntry,
25
26
  } from "../lib/health-check";
26
- import { readPlatformToken } from "../lib/platform-client";
27
+ import { readCloudToken } from "../lib/cloud-client";
27
28
  import { dockerResourceNames } from "../lib/docker";
28
29
  import { existsSync } from "fs";
29
30
  import {
@@ -44,14 +45,21 @@ import {
44
45
 
45
46
  // ── Table formatting helpers ────────────────────────────────────
46
47
 
48
+ /** What each row's `type` column says: the KIND of process the row describes. */
49
+ const ASSISTANT_TYPE = "🧑‍🚀 Assistant";
50
+ const TUNNEL_TYPE = "🚇 Tunnel";
51
+ const APP_TYPE = "🌐 App";
52
+
47
53
  interface TableRow {
48
54
  name: string;
55
+ type: string;
49
56
  status: string;
50
57
  info: string;
51
58
  }
52
59
 
53
60
  interface ColWidths {
54
61
  name: number;
62
+ type: number;
55
63
  status: number;
56
64
  info: number;
57
65
  }
@@ -62,27 +70,38 @@ function pad(s: string, w: number): string {
62
70
 
63
71
  function computeColWidths(
64
72
  rows: TableRow[],
65
- headers: TableRow = { name: "NAME", status: "STATUS", info: "INFO" },
73
+ headers: TableRow = {
74
+ name: "NAME",
75
+ type: "TYPE",
76
+ status: "STATUS",
77
+ info: "INFO",
78
+ },
66
79
  ): ColWidths {
67
80
  const all = [headers, ...rows];
68
81
  return {
69
82
  name: Math.max(...all.map((r) => r.name.length)),
83
+ type: Math.max(...all.map((r) => r.type.length)),
70
84
  status: Math.max(...all.map((r) => r.status.length), "checking...".length),
71
85
  info: Math.max(...all.map((r) => r.info.length)),
72
86
  };
73
87
  }
74
88
 
75
89
  function formatRow(r: TableRow, colWidths: ColWidths): string {
76
- return ` ${pad(r.name, colWidths.name)} ${pad(r.status, colWidths.status)} ${r.info}`;
90
+ return ` ${pad(r.name, colWidths.name)} ${pad(r.type, colWidths.type)} ${pad(r.status, colWidths.status)} ${r.info}`;
77
91
  }
78
92
 
79
93
  function printTable(
80
94
  rows: TableRow[],
81
- headers: TableRow = { name: "PROCESS", status: "STATUS", info: "INFO" },
95
+ headers: TableRow = {
96
+ name: "PROCESS",
97
+ type: "TYPE",
98
+ status: "STATUS",
99
+ info: "INFO",
100
+ },
82
101
  ): void {
83
102
  const colWidths = computeColWidths(rows, headers);
84
103
  console.log(formatRow(headers, colWidths));
85
- const sep = ` ${"-".repeat(colWidths.name)} ${"-".repeat(colWidths.status)} ${"-".repeat(colWidths.info)}`;
104
+ const sep = ` ${"-".repeat(colWidths.name)} ${"-".repeat(colWidths.type)} ${"-".repeat(colWidths.status)} ${"-".repeat(colWidths.info)}`;
86
105
  console.log(sep);
87
106
  for (const row of rows) {
88
107
  console.log(formatRow(row, colWidths));
@@ -108,6 +127,7 @@ function flattenProcessTree(
108
127
  rows.push({
109
128
  name:
110
129
  depth === 0 ? entry.name : `${" ".repeat(depth - 1)}├─ ${entry.name}`,
130
+ type: ASSISTANT_TYPE,
111
131
  status: withStatusEmoji(STATUS_LABELS[entry.status]),
112
132
  info: entry.info ?? "",
113
133
  });
@@ -118,6 +138,7 @@ function flattenProcessTree(
118
138
  const prefix = `${" ".repeat(depth)}${isLast ? "└─" : "├─"} ${child.name}`;
119
139
  rows.push({
120
140
  name: prefix,
141
+ type: ASSISTANT_TYPE,
121
142
  status: withStatusEmoji(STATUS_LABELS[child.status]),
122
143
  info: child.info ?? "",
123
144
  });
@@ -284,9 +305,7 @@ async function getLocalProcesses(entry: AssistantEntry): Promise<TableRow[]> {
284
305
  // tree (assistant + every child it owns, built from the OS process table)
285
306
  // with the gateway's own entry appended — no hard-coded subsystem list to
286
307
  // drift out of sync with what the assistant actually spawns.
287
- const tree = gatewayUrl
288
- ? await fetchGatewayPs(gatewayUrl, token)
289
- : null;
308
+ const tree = gatewayUrl ? await fetchGatewayPs(gatewayUrl, token) : null;
290
309
 
291
310
  const rows: TableRow[] =
292
311
  tree && tree.length > 0
@@ -303,6 +322,7 @@ async function getLocalProcesses(entry: AssistantEntry): Promise<TableRow[]> {
303
322
  });
304
323
  rows.push({
305
324
  name: "credential-executor",
325
+ type: ASSISTANT_TYPE,
306
326
  status: withStatusEmoji(ces.running ? "running" : "not running"),
307
327
  info: ces.running ? formatDetectionInfo(ces) : "not detected",
308
328
  });
@@ -349,6 +369,7 @@ async function detectLocalProcessesFallback(
349
369
 
350
370
  return results.map((proc, i) => ({
351
371
  name: allSpecs[i].name,
372
+ type: ASSISTANT_TYPE,
352
373
  status: withStatusEmoji(proc.running ? "running" : "not running"),
353
374
  info: proc.running ? formatDetectionInfo(proc) : "not detected",
354
375
  }));
@@ -393,12 +414,14 @@ async function getDockerProcesses(entry: AssistantEntry): Promise<TableRow[]> {
393
414
  if (!state) {
394
415
  return {
395
416
  name,
417
+ type: ASSISTANT_TYPE,
396
418
  status: withStatusEmoji("not found"),
397
419
  info: `container ${containerName}`,
398
420
  };
399
421
  }
400
422
  return {
401
423
  name,
424
+ type: ASSISTANT_TYPE,
402
425
  status: withStatusEmoji(state === "running" ? "running" : state),
403
426
  info: `container ${containerName}`,
404
427
  };
@@ -412,6 +435,7 @@ async function getDockerProcesses(entry: AssistantEntry): Promise<TableRow[]> {
412
435
  const alive = isLocalProcessAlive(watcherPid);
413
436
  results.push({
414
437
  name: "file-watcher",
438
+ type: ASSISTANT_TYPE,
415
439
  status: withStatusEmoji(alive ? "running" : "not running"),
416
440
  info: alive ? `PID ${watcherPid}` : "not detected",
417
441
  });
@@ -430,44 +454,39 @@ function locationFor(entry: AssistantEntry): string {
430
454
  return entry.runtimeUrl ?? "unknown — no runtime URL recorded";
431
455
  }
432
456
 
433
- async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
457
+ async function getAssistantProcessRows(
458
+ entry: AssistantEntry,
459
+ ): Promise<TableRow[]> {
434
460
  const cloud = entry.cloud;
435
461
 
436
462
  console.log(`Processes for ${formatAssistantReference(entry)} (${cloud}):`);
437
463
  console.log(`Location: ${locationFor(entry)}\n`);
438
464
 
439
465
  if (cloud === "local") {
440
- const rows = await getLocalProcesses(entry);
441
- printTable(rows);
442
- return;
466
+ return getLocalProcesses(entry);
443
467
  }
444
468
 
445
469
  if (cloud === "docker") {
446
- const rows = await getDockerProcesses(entry);
447
- printTable(rows);
448
- return;
470
+ return getDockerProcesses(entry);
449
471
  }
450
472
 
451
- if (isManagedCloud(cloud)) {
473
+ if (isCloudHome(cloud)) {
452
474
  console.log(` Cadet Cloud ID: ${entry.assistantId}\n`);
453
475
 
454
476
  const psData = await fetchManagedPs(entry.runtimeUrl, entry.assistantId);
455
477
 
456
478
  if (!psData) {
457
- const rows: TableRow[] = [
479
+ return [
458
480
  {
459
481
  name: "assistant",
482
+ type: ASSISTANT_TYPE,
460
483
  status: withStatusEmoji("unreachable"),
461
484
  info: "could not reach Cadet Cloud API — run `cadet login`",
462
485
  },
463
486
  ];
464
- printTable(rows);
465
- return;
466
487
  }
467
488
 
468
- const rows = flattenProcessTree(psData.processes);
469
- printTable(rows);
470
- return;
489
+ return flattenProcessTree(psData.processes);
471
490
  }
472
491
 
473
492
  if (cloud === "apple-container") {
@@ -476,6 +495,7 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
476
495
  const rows: TableRow[] = [
477
496
  {
478
497
  name: "container",
498
+ type: ASSISTANT_TYPE,
479
499
  status: withStatusEmoji(socketAlive ? "running" : "not running"),
480
500
  info: socketAlive
481
501
  ? `mgmt ${mgmtSocket}`
@@ -487,12 +507,12 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
487
507
  const health = await checkHealth(entry.runtimeUrl, token);
488
508
  rows.push({
489
509
  name: "gateway",
510
+ type: ASSISTANT_TYPE,
490
511
  status: withStatusEmoji(health.status),
491
512
  info: entry.runtimeUrl + (health.detail ? ` | ${health.detail}` : ""),
492
513
  });
493
514
  }
494
- printTable(rows);
495
- return;
515
+ return rows;
496
516
  }
497
517
 
498
518
  if (cloud === "paired") {
@@ -500,15 +520,14 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
500
520
  // list — probe the remote gateway's health over the bearer token instead.
501
521
  const token = loadGuardianToken(entry.assistantId)?.accessToken;
502
522
  const health = await checkHealth(entry.runtimeUrl, token);
503
- const rows: TableRow[] = [
523
+ return [
504
524
  {
505
525
  name: "gateway",
526
+ type: ASSISTANT_TYPE,
506
527
  status: withStatusEmoji(health.status),
507
528
  info: entry.runtimeUrl + (health.detail ? ` | ${health.detail}` : ""),
508
529
  },
509
530
  ];
510
- printTable(rows);
511
- return;
512
531
  }
513
532
 
514
533
  let output: string;
@@ -525,7 +544,7 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
525
544
  const msg = error instanceof Error ? error.message : String(error);
526
545
  if (msg.includes("timed out")) {
527
546
  console.warn(`Warning: remote process listing timed out — ${msg}`);
528
- return;
547
+ return [];
529
548
  }
530
549
  console.error(`Failed to list processes: ${msg}`);
531
550
  process.exit(1);
@@ -535,16 +554,93 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
535
554
 
536
555
  if (procs.length === 0) {
537
556
  console.log("No cadet processes found on the remote instance.");
538
- return;
557
+ return [];
539
558
  }
540
559
 
541
- const rows: TableRow[] = procs.map((p) => ({
560
+ return procs.map((p) => ({
542
561
  name: classifyProcess(p.command),
562
+ type: ASSISTANT_TYPE,
543
563
  status: withStatusEmoji("running"),
544
564
  info: `PID ${p.pid} | ${p.command.slice(0, 80)}`,
545
565
  }));
566
+ }
567
+
568
+ // ── Root-managed process rows (tunnel agent, app edge) ──────────
569
+
570
+ /** A CLI-managed process row, plus the assistant it fronts (when recorded). */
571
+ interface RootManagedRow {
572
+ row: TableRow;
573
+ assistantId?: string;
574
+ }
575
+
576
+ /**
577
+ * Rows for the CLI-managed processes recorded at the lockfile root: the
578
+ * `tunnel` agent and the `app` nginx edge. Pids are liveness-checked so a
579
+ * stale entry reads as not running rather than as a lie. Each row carries the
580
+ * assistant it fronts so the listing can nest it under that assistant.
581
+ */
582
+ async function getRootManagedRows(): Promise<RootManagedRow[]> {
583
+ const { tunnel, app } = readRootProcesses();
584
+ const rows: RootManagedRow[] = [];
585
+
586
+ if (tunnel) {
587
+ const alive = isLocalProcessAlive(tunnel.pid);
588
+ rows.push({
589
+ assistantId: tunnel.assistantId,
590
+ row: {
591
+ name: tunnel.provider ?? "unknown provider",
592
+ type: TUNNEL_TYPE,
593
+ status: withStatusEmoji(alive ? "running" : "not running"),
594
+ info: alive ? `PID ${tunnel.pid}` : `PID ${tunnel.pid} (exited)`,
595
+ },
596
+ });
597
+ }
598
+ if (app) {
599
+ const alive = isLocalProcessAlive(app.pid);
600
+ rows.push({
601
+ assistantId: app.assistantId,
602
+ row: {
603
+ name: "nginx edge",
604
+ type: APP_TYPE,
605
+ status: withStatusEmoji(alive ? "running" : "not running"),
606
+ info: alive ? `PID ${app.pid}` : `PID ${app.pid} (exited)`,
607
+ },
608
+ });
609
+ }
610
+ return rows;
611
+ }
612
+
613
+ /**
614
+ * Fold the CLI-managed process rows into ONE table with the assistant rows:
615
+ * a process fronts an assistant when it recorded that assistant's id, and
616
+ * those rows nest under it; anything unattributed (or fronting an assistant
617
+ * this environment no longer lists) renders at the bottom, machine-level.
618
+ */
619
+ async function assistantRowsWithRootProcesses(
620
+ assistants: AssistantEntry[],
621
+ healths: AssistantHealth[],
622
+ activeAssistantId: string | null,
623
+ ): Promise<TableRow[]> {
624
+ const nestedByAssistant = new Map<string, TableRow[]>();
625
+ const unattributed: TableRow[] = [];
626
+ const knownIds = new Set(assistants.map((entry) => entry.assistantId));
627
+ for (const managed of await getRootManagedRows()) {
628
+ if (managed.assistantId && knownIds.has(managed.assistantId)) {
629
+ const nested = nestedByAssistant.get(managed.assistantId) ?? [];
630
+ nested.push({ ...managed.row, name: ` └ ${managed.row.name}` });
631
+ nestedByAssistant.set(managed.assistantId, nested);
632
+ } else {
633
+ unattributed.push(managed.row);
634
+ }
635
+ }
546
636
 
547
- printTable(rows);
637
+ const rows: TableRow[] = [];
638
+ assistants.forEach((entry, i) => {
639
+ rows.push(formatAssistantListRow(entry, activeAssistantId, healths[i]!));
640
+ rows.push(...(nestedByAssistant.get(entry.assistantId) ?? []));
641
+ });
642
+ rows.push(...unattributed);
643
+ return rows;
548
644
  }
549
645
 
550
646
  // ── List all assistants (no arg) ────────────────────────────────
@@ -590,7 +686,7 @@ async function getAssistantListHealth(
590
686
  : { status: "unknown", detail: "no runtime URL" };
591
687
  }
592
688
 
593
- if (isManagedCloud(entry.cloud)) {
689
+ if (isCloudHome(entry.cloud)) {
594
690
  return checkManagedHealth(entry.runtimeUrl, entry.assistantId);
595
691
  }
596
692
 
@@ -611,6 +707,7 @@ function formatAssistantListRow(
611
707
  const prefix = entry.assistantId === activeAssistantId ? "* " : " ";
612
708
  return {
613
709
  name: prefix + getAssistantDisplayName(entry),
710
+ type: ASSISTANT_TYPE,
614
711
  status: withStatusEmoji(health.status),
615
712
  info: infoParts.join(" | "),
616
713
  };
@@ -630,13 +727,13 @@ export async function listAllAssistants(verbose: boolean): Promise<void> {
630
727
 
631
728
  // Sync cloud assistants (when logged in) before listing. Login status has
632
729
  // its own command — `cadet whoami` — so ps stays silent about it.
633
- const platformToken = readPlatformToken();
634
- if (!platformToken) {
730
+ const cloudToken = readCloudToken();
731
+ if (!cloudToken) {
635
732
  log?.("No platform token found — skipping cloud sync");
636
733
  } else {
637
734
  // A failed sync (offline, expired token) just means cloud-managed
638
735
  // assistants may be stale — the local list still renders.
639
- await syncCloudAssistants(platformToken, { log });
736
+ await syncCloudAssistants(cloudToken, { log });
640
737
  }
641
738
  console.log("");
642
739
 
@@ -654,6 +751,7 @@ export async function listAllAssistants(verbose: boolean): Promise<void> {
654
751
  console.log("\nOrphaned processes detected:\n");
655
752
  const rows: TableRow[] = orphans.map((o) => ({
656
753
  name: o.name,
754
+ type: ASSISTANT_TYPE,
657
755
  status: withStatusEmoji("running"),
658
756
  info: `PID ${o.pid} (from ${o.source})`,
659
757
  }));
@@ -666,60 +764,71 @@ export async function listAllAssistants(verbose: boolean): Promise<void> {
666
764
  return;
667
765
  }
668
766
 
669
- const rows = await Promise.all(
670
- assistants.map(async (entry) =>
671
- formatAssistantListRow(
672
- entry,
673
- activeAssistantId,
674
- await getAssistantListHealth(entry),
675
- ),
676
- ),
767
+ const healths = await Promise.all(
768
+ assistants.map((entry) => getAssistantListHealth(entry)),
677
769
  );
678
770
 
679
- printTable(rows, { name: "NAME", status: "STATUS", info: "INFO" });
680
- console.log("");
771
+ printTable(
772
+ await assistantRowsWithRootProcesses(
773
+ assistants,
774
+ healths,
775
+ activeAssistantId,
776
+ ),
777
+ { name: "NAME", type: "TYPE", status: "STATUS", info: "INFO" },
778
+ );
681
779
  }
682
780
 
683
781
  // ── Entry point ─────────────────────────────────────────────────
684
782
 
685
783
  export const ps = Object.assign(
686
784
  async function ps(): Promise<void> {
687
- const args = process.argv.slice(3);
688
- if (args.includes("--help") || args.includes("-h")) {
689
- console.log("Usage: cadet ps [<name-or-id>] [--verbose]");
690
- console.log("");
691
- console.log(
692
- "List all assistants, or show processes for a specific assistant.",
693
- );
694
- console.log("");
695
- console.log("Arguments:");
696
- console.log(
697
- " <name-or-id> Show processes for the assistant display name or ID",
698
- );
699
- console.log("");
700
- console.log("Options:");
701
- console.log(
702
- " --verbose Show diagnostic logs (platform sync, auth issues)",
703
- );
704
- process.exit(0);
705
- }
785
+ const args = process.argv.slice(3);
786
+ if (args.includes("--help") || args.includes("-h")) {
787
+ console.log("Usage: cadet ps [<name-or-id>] [--verbose]");
788
+ console.log("");
789
+ console.log(
790
+ "List all assistants, or show processes for a specific assistant.",
791
+ );
792
+ console.log("");
793
+ console.log("Arguments:");
794
+ console.log(
795
+ " <name-or-id> Show processes for the assistant display name or ID",
796
+ );
797
+ console.log("");
798
+ console.log("Options:");
799
+ console.log(
800
+ " --verbose Show diagnostic logs (platform sync, auth issues)",
801
+ );
802
+ process.exit(0);
803
+ }
706
804
 
707
- const verbose = args.includes("--verbose");
708
- const assistantIdentifier = parseAssistantTargetArg(args);
805
+ const verbose = args.includes("--verbose");
806
+ const assistantIdentifier = parseAssistantTargetArg(args);
709
807
 
710
- if (!assistantIdentifier) {
711
- await listAllAssistants(verbose);
712
- return;
713
- }
808
+ if (!assistantIdentifier) {
809
+ await listAllAssistants(verbose);
810
+ return;
811
+ }
714
812
 
715
- const result = lookupAssistantByIdentifier(assistantIdentifier);
716
- if (result.status !== "found") {
717
- console.error(formatAssistantLookupError(assistantIdentifier, result));
718
- process.exit(1);
719
- }
813
+ const result = lookupAssistantByIdentifier(assistantIdentifier);
814
+ if (result.status !== "found") {
815
+ console.error(formatAssistantLookupError(assistantIdentifier, result));
816
+ process.exit(1);
817
+ }
720
818
 
721
- await showAssistantProcesses(result.entry);
722
- },
819
+ const rows = await getAssistantProcessRows(result.entry);
820
+ // The CLI-managed processes (root lockfile) that front THIS assistant ride
821
+ // the same table; ones fronting another assistant stay in the list view.
822
+ for (const managed of await getRootManagedRows()) {
823
+ if (
824
+ managed.assistantId &&
825
+ managed.assistantId !== result.entry.assistantId
826
+ ) {
827
+ continue;
828
+ }
829
+ rows.push(managed.row);
830
+ }
831
+ printTable(rows);
832
+ },
723
833
  { description: "List assistants (or processes for a specific assistant)" },
724
834
  );
725
-
@@ -0,0 +1,30 @@
1
+ import { sleep } from "./sleep.js";
2
+ import { wake } from "./wake.js";
3
+
4
+ export const restart = Object.assign(
5
+ async function restart(): Promise<void> {
6
+ const args = process.argv.slice(3);
7
+ if (args.includes("--help") || args.includes("-h")) {
8
+ console.log(
9
+ "Usage: cadet restart [<name> | --all] [--force] [--wait [<duration>]] [--repair-guardian]",
10
+ );
11
+ console.log("");
12
+ console.log("Sleep, then wake the assistant.");
13
+ console.log("");
14
+ console.log(
15
+ "Arguments and flags pass through to `cadet sleep` and `cadet wake`.",
16
+ );
17
+ console.log(
18
+ " --repair-guardian Re-provision the guardian token during the wake",
19
+ );
20
+ process.exit(0);
21
+ }
22
+ if (args.includes("--all") && args.includes("--repair-guardian")) {
23
+ console.error("Error: --all cannot be combined with --repair-guardian.");
24
+ process.exit(1);
25
+ }
26
+ await sleep();
27
+ await wake();
28
+ },
29
+ { description: "Sleep, then wake the assistant" },
30
+ );