@mycadet/cli 0.12.7-dev.202610111049.d2b52a8 → 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.
- package/node_modules/@mycadet/assistant-api/events/cloud-disconnected.ts +2 -2
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +10 -4
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +10 -4
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +10 -4
- package/package.json +4 -3
- package/src/__tests__/{cloudflare-tunnel.test.ts → cloudflare.test.ts} +1 -1
- package/src/__tests__/ps-cloud-status.test.ts +53 -0
- package/src/__tests__/retire.test.ts +37 -178
- package/src/__tests__/root-process-lockfile.test.ts +99 -0
- package/src/__tests__/{tailscale-tunnel.test.ts → tailscale.test.ts} +1 -1
- package/src/__tests__/tunnel.test.ts +127 -80
- package/src/__tests__/upgrade-local.test.ts +3 -3
- package/src/__tests__/wake.test.ts +16 -9
- package/src/commands/client.ts +1 -1
- package/src/commands/ps.ts +181 -72
- package/src/commands/retire.ts +28 -137
- package/src/commands/tunnel.ts +76 -118
- package/src/commands/upgrade.ts +2 -9
- package/src/commands/wake.ts +5 -9
- package/src/lib/assistant-config.ts +53 -0
- package/src/lib/detached-process.ts +1 -1
- package/src/lib/ingress-config.ts +7 -34
- package/src/lib/local.ts +6 -27
- package/src/lib/nginx-ingress.ts +29 -8
- package/src/lib/{tunnel-edge.ts → tunnel.ts} +199 -11
- package/src/lib/tunnels/cadet.ts +29 -0
- package/src/lib/{cloudflare-tunnel.ts → tunnels/cloudflare.ts} +15 -4
- package/src/lib/tunnels/gateway-notify.ts +61 -0
- package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +200 -29
- package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +19 -5
package/src/commands/ps.ts
CHANGED
|
@@ -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";
|
|
@@ -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 = {
|
|
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 = {
|
|
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,22 +454,20 @@ function locationFor(entry: AssistantEntry): string {
|
|
|
430
454
|
return entry.runtimeUrl ?? "unknown — no runtime URL recorded";
|
|
431
455
|
}
|
|
432
456
|
|
|
433
|
-
async function
|
|
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
|
-
|
|
441
|
-
printTable(rows);
|
|
442
|
-
return;
|
|
466
|
+
return getLocalProcesses(entry);
|
|
443
467
|
}
|
|
444
468
|
|
|
445
469
|
if (cloud === "docker") {
|
|
446
|
-
|
|
447
|
-
printTable(rows);
|
|
448
|
-
return;
|
|
470
|
+
return getDockerProcesses(entry);
|
|
449
471
|
}
|
|
450
472
|
|
|
451
473
|
if (isCloudHome(cloud)) {
|
|
@@ -454,20 +476,17 @@ async function showAssistantProcesses(entry: AssistantEntry): Promise<void> {
|
|
|
454
476
|
const psData = await fetchManagedPs(entry.runtimeUrl, entry.assistantId);
|
|
455
477
|
|
|
456
478
|
if (!psData) {
|
|
457
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) ────────────────────────────────
|
|
@@ -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
|
};
|
|
@@ -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
|
|
670
|
-
assistants.map(
|
|
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(
|
|
680
|
-
|
|
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
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
708
|
-
|
|
805
|
+
const verbose = args.includes("--verbose");
|
|
806
|
+
const assistantIdentifier = parseAssistantTargetArg(args);
|
|
709
807
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
808
|
+
if (!assistantIdentifier) {
|
|
809
|
+
await listAllAssistants(verbose);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
714
812
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
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
|
-
|
package/src/commands/retire.ts
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
authHeaders,
|
|
26
26
|
authHeadersForKnownOrganization,
|
|
27
27
|
getCloudUrl,
|
|
28
|
-
readGatewayCredential,
|
|
29
28
|
readCloudToken,
|
|
30
29
|
} from "../lib/cloud-client.js";
|
|
31
30
|
import { retireDocker, dockerResourceNames } from "../lib/docker.js";
|
|
@@ -51,12 +50,6 @@ interface RetireArgs {
|
|
|
51
50
|
yes: boolean;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
interface SelfHostedPlatformRegistration {
|
|
55
|
-
cloudAssistantId: string;
|
|
56
|
-
cloudBaseUrl: string | null;
|
|
57
|
-
organizationId: string | null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
53
|
const CLOUD_TOKEN_ENV = "CADET_CLOUD_TOKEN";
|
|
61
54
|
const LEGACY_CLOUD_TOKEN_ENV = "CADET_PLATFORM_TOKEN";
|
|
62
55
|
|
|
@@ -68,54 +61,11 @@ function readPlatformRetireToken(): string | null {
|
|
|
68
61
|
return readCloudToken();
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
function platformOrigin(cloudUrl: string): string | null {
|
|
72
|
-
try {
|
|
73
|
-
return new URL(cloudUrl).origin;
|
|
74
|
-
} catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function resolveTrustedSelfHostedCloudUrl(
|
|
80
|
-
storedCloudUrl: string | null,
|
|
81
|
-
): string | null {
|
|
82
|
-
const configuredCloudUrl = getCloudUrl();
|
|
83
|
-
if (!storedCloudUrl) return configuredCloudUrl;
|
|
84
|
-
|
|
85
|
-
const storedOrigin = platformOrigin(storedCloudUrl);
|
|
86
|
-
const configuredOrigin = platformOrigin(configuredCloudUrl);
|
|
87
|
-
if (storedOrigin && configuredOrigin && storedOrigin === configuredOrigin) {
|
|
88
|
-
return configuredCloudUrl;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.warn(
|
|
92
|
-
"⚠️ Stored platform URL does not match the configured platform URL; skipping platform unregister.",
|
|
93
|
-
);
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
64
|
function nonEmptyString(value: string | null | undefined): string | null {
|
|
98
65
|
const trimmed = value?.trim();
|
|
99
66
|
return trimmed ? trimmed : null;
|
|
100
67
|
}
|
|
101
68
|
|
|
102
|
-
function lockfileSelfHostedPlatformRegistration(
|
|
103
|
-
entry: AssistantEntry,
|
|
104
|
-
): SelfHostedPlatformRegistration | null {
|
|
105
|
-
const cloudAssistantId = nonEmptyString(
|
|
106
|
-
entry.cloudAssistantId ?? entry.cloudAssistantId,
|
|
107
|
-
);
|
|
108
|
-
if (!cloudAssistantId) return null;
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
cloudAssistantId,
|
|
112
|
-
cloudBaseUrl: nonEmptyString(entry.cloudBaseUrl ?? entry.platformBaseUrl),
|
|
113
|
-
organizationId: nonEmptyString(
|
|
114
|
-
entry.cloudOrganizationId ?? entry.platformOrganizationId,
|
|
115
|
-
),
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
69
|
async function deleteCloudAssistant(
|
|
120
70
|
assistantId: string,
|
|
121
71
|
options: {
|
|
@@ -150,85 +100,23 @@ async function deleteCloudAssistant(
|
|
|
150
100
|
}
|
|
151
101
|
}
|
|
152
102
|
|
|
153
|
-
async function readSelfHostedPlatformRegistration(
|
|
154
|
-
entry: AssistantEntry,
|
|
155
|
-
): Promise<SelfHostedPlatformRegistration | null> {
|
|
156
|
-
const fallback = lockfileSelfHostedPlatformRegistration(entry);
|
|
157
|
-
const gatewayUrl = entry.localUrl ?? entry.runtimeUrl;
|
|
158
|
-
const cloudRegistration = await readGatewayCredential(
|
|
159
|
-
gatewayUrl,
|
|
160
|
-
"cadet:platform_assistant_id",
|
|
161
|
-
entry.bearerToken,
|
|
162
|
-
);
|
|
163
|
-
if (cloudRegistration.unreachable) {
|
|
164
|
-
if (fallback) {
|
|
165
|
-
console.warn(
|
|
166
|
-
"\u26a0\ufe0f Could not read platform registration from the local assistant; using saved platform registration.",
|
|
167
|
-
);
|
|
168
|
-
return fallback;
|
|
169
|
-
}
|
|
170
|
-
console.warn(
|
|
171
|
-
"\u26a0\ufe0f Could not read cloud registration from the local assistant and no saved cloud registration is available; skipping cloud unregister.",
|
|
172
|
-
);
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const cloudAssistantId =
|
|
177
|
-
nonEmptyString(cloudRegistration.value) ?? fallback?.cloudAssistantId;
|
|
178
|
-
if (!cloudAssistantId) return null;
|
|
179
|
-
|
|
180
|
-
const [cloudBaseUrl, organizationId] = await Promise.all([
|
|
181
|
-
readGatewayCredential(
|
|
182
|
-
gatewayUrl,
|
|
183
|
-
"cadet:platform_base_url",
|
|
184
|
-
entry.bearerToken,
|
|
185
|
-
),
|
|
186
|
-
readGatewayCredential(
|
|
187
|
-
gatewayUrl,
|
|
188
|
-
"cadet:platform_organization_id",
|
|
189
|
-
entry.bearerToken,
|
|
190
|
-
),
|
|
191
|
-
]);
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
cloudAssistantId,
|
|
195
|
-
cloudBaseUrl:
|
|
196
|
-
nonEmptyString(cloudBaseUrl.value) ??
|
|
197
|
-
fallback?.cloudBaseUrl ??
|
|
198
|
-
null,
|
|
199
|
-
organizationId:
|
|
200
|
-
nonEmptyString(organizationId.value) ?? fallback?.organizationId ?? null,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
103
|
async function unregisterSelfHostedLocalPlatformRecord(
|
|
205
104
|
entry: AssistantEntry,
|
|
206
105
|
): Promise<void> {
|
|
207
106
|
const token = readPlatformRetireToken();
|
|
208
107
|
if (!token) return;
|
|
209
108
|
|
|
210
|
-
const
|
|
211
|
-
if (!
|
|
212
|
-
|
|
213
|
-
const cloudUrl = resolveTrustedSelfHostedCloudUrl(
|
|
214
|
-
registration.cloudBaseUrl,
|
|
215
|
-
);
|
|
216
|
-
if (!cloudUrl) return;
|
|
217
|
-
if (!registration.organizationId) {
|
|
218
|
-
console.warn(
|
|
219
|
-
"⚠️ Could not read platform organization ID from the local assistant; skipping platform unregister.",
|
|
220
|
-
);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
109
|
+
const cloudAssistantId = nonEmptyString(entry.cloudAssistantId);
|
|
110
|
+
if (!cloudAssistantId) return;
|
|
223
111
|
|
|
224
|
-
await deleteCloudAssistant(
|
|
112
|
+
await deleteCloudAssistant(cloudAssistantId, {
|
|
225
113
|
token,
|
|
226
|
-
cloudUrl,
|
|
227
|
-
organizationId:
|
|
114
|
+
cloudUrl: getCloudUrl(),
|
|
115
|
+
organizationId: nonEmptyString(entry.cloudOrganizationId) ?? undefined,
|
|
228
116
|
label: "Platform self-hosted unregister",
|
|
229
|
-
successMessage: "
|
|
117
|
+
successMessage: "✅ Platform self-hosted registration unregistered.",
|
|
230
118
|
alreadyGoneMessage:
|
|
231
|
-
"
|
|
119
|
+
"✅ Platform self-hosted registration already unregistered (404).",
|
|
232
120
|
});
|
|
233
121
|
}
|
|
234
122
|
|
|
@@ -331,8 +219,8 @@ function printRetireTarget(entry: AssistantEntry, cloud: string): void {
|
|
|
331
219
|
: {}),
|
|
332
220
|
...(entry.cloud === "docker"
|
|
333
221
|
? {
|
|
334
|
-
container:
|
|
335
|
-
|
|
222
|
+
container: dockerResourceNames(entry.assistantId)
|
|
223
|
+
.assistantContainer,
|
|
336
224
|
}
|
|
337
225
|
: {}),
|
|
338
226
|
},
|
|
@@ -373,28 +261,29 @@ function teeConsoleToLogFile(fd: number | "ignore"): void {
|
|
|
373
261
|
|
|
374
262
|
export const retire = Object.assign(
|
|
375
263
|
async function retire(): Promise<void> {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
264
|
+
if (process.env.CADET_DESKTOP_APP) {
|
|
265
|
+
resetLogFile("retire.log");
|
|
266
|
+
}
|
|
267
|
+
const logFd = process.env.CADET_DESKTOP_APP
|
|
268
|
+
? openLogFile("retire.log")
|
|
269
|
+
: "ignore";
|
|
270
|
+
teeConsoleToLogFile(logFd);
|
|
383
271
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
},
|
|
272
|
+
try {
|
|
273
|
+
await retireInner();
|
|
274
|
+
} finally {
|
|
275
|
+
closeLogFile(logFd);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
390
278
|
{ description: "Delete an assistant instance" },
|
|
391
279
|
);
|
|
392
280
|
|
|
393
|
-
|
|
394
281
|
async function retireInner(): Promise<void> {
|
|
395
282
|
const args = process.argv.slice(3);
|
|
396
283
|
if (args.includes("--help") || args.includes("-h")) {
|
|
397
|
-
console.log(
|
|
284
|
+
console.log(
|
|
285
|
+
"Usage: cadet retire [<name-or-id>] [--source <source>] [--yes]",
|
|
286
|
+
);
|
|
398
287
|
console.log("");
|
|
399
288
|
console.log("Delete an assistant instance and archive its data.");
|
|
400
289
|
console.log(
|
|
@@ -422,7 +311,9 @@ async function retireInner(): Promise<void> {
|
|
|
422
311
|
console.error(
|
|
423
312
|
"Error: No assistant specified and no active assistant. Pass a name or ID, or pick one with `cadet use <name>`.",
|
|
424
313
|
);
|
|
425
|
-
console.error(
|
|
314
|
+
console.error(
|
|
315
|
+
"Usage: cadet retire <name-or-id> [--source <source>] [--yes]",
|
|
316
|
+
);
|
|
426
317
|
process.exit(1);
|
|
427
318
|
}
|
|
428
319
|
|