@mycadet/cli 0.12.7 → 0.12.9-dev.202610131452.9acab64
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 +2 -2
- package/knip.json +1 -1
- package/node_modules/@mycadet/assistant-api/events/cloud-disconnected.ts +21 -0
- package/node_modules/@mycadet/assistant-api/events/host-bash.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/host-cu.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/host-file.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/notification-intent.ts +1 -1
- package/node_modules/@mycadet/assistant-api/events/show-cloud-login.ts +18 -0
- package/node_modules/@mycadet/assistant-api/index.ts +11 -11
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/{service-contracts/src/platform-credential.ts → assistant-api/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/assistant-api/responses/home.ts +1 -1
- package/node_modules/@mycadet/assistant-api/surfaces.ts +4 -4
- package/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
- package/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
- package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +2 -2
- 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
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/local-mode/src/__tests__/pair.test.ts +5 -5
- package/node_modules/@mycadet/local-mode/src/__tests__/retire.test.ts +1 -1
- package/node_modules/@mycadet/local-mode/src/config.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/index.ts +1 -1
- package/node_modules/@mycadet/local-mode/src/lockfile-contract.test.ts +3 -3
- package/node_modules/@mycadet/local-mode/src/lockfile-contract.ts +10 -5
- package/node_modules/@mycadet/local-mode/src/lockfile.test.ts +15 -15
- package/node_modules/@mycadet/local-mode/src/lockfile.ts +9 -9
- package/node_modules/@mycadet/local-mode/src/pair.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/retire.ts +3 -3
- package/node_modules/@mycadet/local-mode/src/teleport-backup-policy.ts +2 -2
- package/node_modules/@mycadet/local-mode/src/unpair.ts +2 -2
- package/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/{local-mode/node_modules/@mycadet/service-contracts/src/platform-credential.ts → service-contracts/src/cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/package.json +6 -4
- package/src/__tests__/assistant-api-key-resolution.test.ts +6 -6
- package/src/__tests__/{assistant-client-platform-auth.test.ts → assistant-client-cloud-auth.test.ts} +10 -10
- package/src/__tests__/assistant-client-refresh.test.ts +2 -2
- package/src/__tests__/backup.test.ts +86 -91
- package/src/__tests__/client-token.test.ts +1 -1
- package/src/__tests__/client-tui-refresh.test.ts +1 -1
- package/src/__tests__/client-web-config.test.ts +1 -2
- package/src/__tests__/{platform-client.test.ts → cloud-client.test.ts} +59 -59
- package/src/__tests__/{platform-releases.test.ts → cloud-releases.test.ts} +28 -18
- package/src/__tests__/{cloudflare-tunnel.test.ts → cloudflare.test.ts} +1 -1
- package/src/__tests__/connect-import.test.ts +55 -39
- package/src/__tests__/debug.test.ts +38 -38
- package/src/__tests__/ensure-binary.test.ts +182 -0
- package/src/__tests__/guardian-token.test.ts +149 -56
- package/src/__tests__/hatch-provider-secrets.test.ts +2 -2
- package/src/__tests__/helpers/guardian-token.ts +22 -0
- package/src/__tests__/helpers/http.ts +10 -0
- package/src/__tests__/nginx-ingress.test.ts +15 -20
- package/src/__tests__/ngrok-spawn.test.ts +49 -0
- package/src/__tests__/orphan-detection.test.ts +1 -1
- package/src/__tests__/pair.test.ts +149 -85
- package/src/__tests__/{ps-platform-status.test.ts → ps-cloud-status.test.ts} +80 -26
- package/src/__tests__/ps-table.test.ts +79 -0
- package/src/__tests__/{recover.test.ts → restore.test.ts} +29 -28
- package/src/__tests__/retire.test.ts +58 -199
- package/src/__tests__/root-process-lockfile.test.ts +99 -0
- package/src/__tests__/sleep.test.ts +19 -19
- package/src/__tests__/statefulset.test.ts +8 -8
- package/src/__tests__/{tailscale-tunnel.test.ts → tailscale.test.ts} +2 -2
- package/src/__tests__/teleport-backup.test.ts +3 -3
- package/src/__tests__/teleport.test.ts +180 -211
- package/src/__tests__/tunnel.test.ts +490 -270
- package/src/__tests__/uninstall.test.ts +150 -9
- package/src/__tests__/upgrade-local.test.ts +3 -3
- package/src/__tests__/upgrade-replay-env.test.ts +12 -12
- package/src/__tests__/wake.test.ts +260 -43
- package/src/__tests__/workos-pkce.test.ts +2 -2
- package/src/commands/backup.ts +24 -24
- package/src/commands/client.ts +76 -78
- package/src/commands/completions.test.ts +104 -0
- package/src/commands/completions.ts +87 -0
- package/src/commands/debug.ts +20 -20
- package/src/commands/exec.ts +12 -12
- package/src/commands/flags.ts +2 -2
- package/src/commands/hatch.ts +27 -24
- package/src/commands/login.ts +22 -22
- package/src/commands/logs.ts +8 -8
- package/src/commands/pair.ts +140 -37
- package/src/commands/ps.ts +232 -86
- package/src/commands/restart.ts +30 -0
- package/src/commands/restore.ts +140 -22
- package/src/commands/retire.ts +48 -150
- package/src/commands/rollback.ts +20 -26
- package/src/commands/setup.ts +2 -2
- package/src/commands/sleep.ts +2 -2
- package/src/commands/teleport.ts +110 -129
- package/src/commands/terminal.ts +158 -18
- package/src/commands/tunnel.ts +156 -175
- package/src/commands/uninstall.ts +137 -54
- package/src/commands/unpair.ts +3 -3
- package/src/commands/upgrade.ts +30 -54
- package/src/commands/use.ts +1 -1
- package/src/commands/wake.ts +166 -50
- package/src/index.ts +18 -9
- package/src/lib/__tests__/{platform-client-signed-url.test.ts → cloud-client-signed-url.test.ts} +16 -16
- package/src/lib/__tests__/docker.test.ts +168 -20
- package/src/lib/__tests__/job-polling.test.ts +2 -2
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/__tests__/local-runtime-client.test.ts +4 -4
- package/src/lib/__tests__/runtime-url.test.ts +1 -1
- package/src/lib/assistant-api-key-resolution.ts +5 -5
- package/src/lib/assistant-client.ts +8 -8
- package/src/lib/assistant-config.ts +58 -12
- package/src/lib/{platform-client.ts → cloud-client.ts} +105 -99
- package/src/lib/cloud-login.ts +23 -24
- package/src/lib/{platform-releases.ts → cloud-releases.ts} +11 -9
- package/src/lib/cloud-values.ts +3 -0
- package/src/lib/color.ts +4 -0
- package/src/lib/config-utils.ts +1 -1
- package/src/lib/detached-process.ts +1 -1
- package/src/lib/docker.ts +300 -111
- package/src/lib/ensure-binary.ts +171 -0
- package/src/lib/environments/__tests__/paths.test.ts +29 -3
- package/src/lib/environments/__tests__/resolve.test.ts +26 -26
- package/src/lib/environments/paths.ts +16 -8
- package/src/lib/environments/resolve.ts +10 -12
- package/src/lib/flag-args.test.ts +1 -1
- package/src/lib/flag-args.ts +1 -1
- package/src/lib/guardian-token.ts +37 -5
- package/src/lib/hatch-local.ts +32 -29
- package/src/lib/hatch-next-steps.ts +5 -9
- package/src/lib/health-check.ts +6 -6
- package/src/lib/ingress-config.ts +8 -35
- package/src/lib/job-polling.ts +2 -2
- package/src/lib/live-voice/connection.ts +12 -12
- package/src/lib/live-voice/tunnel.test.ts +11 -11
- package/src/lib/live-voice/tunnel.ts +15 -15
- package/src/lib/local-runtime-client.ts +20 -20
- package/src/lib/local.ts +46 -34
- package/src/lib/nginx-ingress.ts +40 -15
- package/src/lib/pair.ts +1 -1
- package/src/lib/process.ts +1 -1
- package/src/lib/provider-secrets.ts +3 -3
- package/src/lib/retire-apple-container.ts +1 -1
- package/src/lib/runtime-url.ts +8 -8
- package/src/lib/statefulset.ts +2 -2
- package/src/lib/step-runner.ts +6 -1
- package/src/lib/sync-cloud-assistants.ts +19 -19
- package/src/lib/teleport-backup.ts +4 -4
- package/src/lib/terminal-client.ts +17 -17
- package/src/lib/terminal-session.ts +24 -24
- 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} +36 -15
- package/src/lib/tunnels/gateway-notify.ts +61 -0
- package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +350 -89
- package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +53 -9
- package/src/lib/upgrade-lifecycle.ts +8 -21
- package/src/lib/upgrade-preflight.ts +2 -2
- package/src/lib/workos-pkce.ts +5 -5
- package/node_modules/@mycadet/assistant-api/events/platform-disconnected.ts +0 -21
- package/node_modules/@mycadet/assistant-api/events/show-platform-login.ts +0 -20
- package/src/commands/recover.ts +0 -131
- package/src/commands/roadmap.ts +0 -459
- package/src/commands/ssh.ts +0 -144
- package/src/commands/workflows.ts +0 -305
- package/src/lib/managed-cloud.ts +0 -3
|
@@ -1,19 +1,46 @@
|
|
|
1
1
|
import { existsSync, readFileSync, renameSync, rmSync, unlinkSync } from "fs";
|
|
2
|
-
import { basename, dirname, join } from "path";
|
|
2
|
+
import { basename, dirname, join, sep } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getDaemonPidPath,
|
|
7
|
+
loadAllAssistants,
|
|
8
|
+
type AssistantEntry,
|
|
9
|
+
} from "../lib/assistant-config.js";
|
|
6
10
|
import {
|
|
7
11
|
canPromptForConfirmation,
|
|
8
12
|
confirmAction,
|
|
9
13
|
} from "../lib/confirm-action.js";
|
|
14
|
+
import { retireDocker } from "../lib/docker.js";
|
|
10
15
|
import {
|
|
11
16
|
getConfigDirs,
|
|
12
|
-
getInputHistoryPath,
|
|
13
17
|
getLogDir,
|
|
18
|
+
getSharedStateDir,
|
|
14
19
|
getStateDir,
|
|
15
20
|
} from "../lib/environments/paths.js";
|
|
16
21
|
import { getCurrentEnvironment } from "../lib/environments/resolve.js";
|
|
22
|
+
import { stopProcessByPidFile } from "../lib/process";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The per-assistant teardown uninstall performs before deleting the data:
|
|
26
|
+
* docker assistants keep containers, volumes, and a network in docker that
|
|
27
|
+
* the filesystem sweep never reaches, and local assistants keep processes.
|
|
28
|
+
* Everything on-disk is deleted by the sweep regardless — this is the part
|
|
29
|
+
* uninstall could NOT do before it refused.
|
|
30
|
+
*/
|
|
31
|
+
async function retireAssistantData(entry: AssistantEntry): Promise<void> {
|
|
32
|
+
if (entry.cloud === "docker") {
|
|
33
|
+
await retireDocker(entry.assistantId);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const resources = entry.resources;
|
|
37
|
+
if (!resources) return;
|
|
38
|
+
await stopProcessByPidFile(
|
|
39
|
+
join(resources.instanceDir, "gateway.pid"),
|
|
40
|
+
"gateway",
|
|
41
|
+
);
|
|
42
|
+
await stopProcessByPidFile(getDaemonPidPath(resources), "assistant");
|
|
43
|
+
}
|
|
17
44
|
|
|
18
45
|
/** A directory (or file) scheduled for deletion, with what it holds. */
|
|
19
46
|
interface DeleteTarget {
|
|
@@ -71,62 +98,84 @@ function findInstalledPackageRoot(startDir: string): string | null {
|
|
|
71
98
|
return null;
|
|
72
99
|
}
|
|
73
100
|
|
|
101
|
+
/** The CLI's own install location, when it can be deleted. */
|
|
102
|
+
interface SelfDeleteTarget {
|
|
103
|
+
path: string;
|
|
104
|
+
description: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
74
107
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
108
|
+
* Where this CLI is running from, when that is an INSTALL we can remove:
|
|
109
|
+
* the compiled `cadet` binary (process.execPath IS the install) or a global
|
|
110
|
+
* package install (bun install -g mycadet / npm install -g mycadet). Under
|
|
111
|
+
* a source checkout or a bare runtime (bun/node), null — the runtime binary
|
|
112
|
+
* must never be a deletion target. The uninstall prompt lists the same
|
|
113
|
+
* target it later deletes, so both share this resolver.
|
|
80
114
|
*/
|
|
81
|
-
function
|
|
115
|
+
function resolveSelfDeleteTarget(): SelfDeleteTarget | null {
|
|
82
116
|
const execPath = process.execPath;
|
|
83
117
|
const execBase = basename(execPath);
|
|
84
118
|
if (execBase === "cadet" || execBase === "cadet.exe") {
|
|
119
|
+
return { path: execPath, description: "the Cadet executable" };
|
|
120
|
+
}
|
|
121
|
+
const runningFile = fileURLToPath(import.meta.url);
|
|
122
|
+
const pkgRoot = findInstalledPackageRoot(dirname(runningFile));
|
|
123
|
+
if (pkgRoot) {
|
|
124
|
+
return { path: pkgRoot, description: "the installed Cadet CLI package" };
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Delete the install {@link resolveSelfDeleteTarget} found. The two shapes
|
|
131
|
+
* delete differently — the running binary needs the win32 rename dance, a
|
|
132
|
+
* package dir is a plain recursive delete — and the bin symlinks the
|
|
133
|
+
* package manager planted (e.g. ~/.bun/bin/cadet, the npm global bin) are
|
|
134
|
+
* left dangling — say so.
|
|
135
|
+
*/
|
|
136
|
+
function selfDelete(): void {
|
|
137
|
+
const target = resolveSelfDeleteTarget();
|
|
138
|
+
if (!target) {
|
|
139
|
+
console.log(
|
|
140
|
+
"Cadet is running from a source checkout — skipping self-removal.",
|
|
141
|
+
);
|
|
142
|
+
console.log(
|
|
143
|
+
"Remove the installed copy with `bun remove -g mycadet` or `npm uninstall -g mycadet`.",
|
|
144
|
+
);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const execBase = basename(target.path);
|
|
148
|
+
const isCompiledBinary = execBase === "cadet" || execBase === "cadet.exe";
|
|
149
|
+
if (isCompiledBinary) {
|
|
85
150
|
try {
|
|
86
151
|
if (process.platform === "win32") {
|
|
87
152
|
// Windows refuses to unlink a running executable — stage it under a
|
|
88
153
|
// junk name and delete that.
|
|
89
|
-
const stale = `${
|
|
90
|
-
renameSync(
|
|
154
|
+
const stale = `${target.path}.uninstalled`;
|
|
155
|
+
renameSync(target.path, stale);
|
|
91
156
|
rmSync(stale, { force: true, recursive: true });
|
|
92
157
|
} else {
|
|
93
|
-
unlinkSync(
|
|
158
|
+
unlinkSync(target.path);
|
|
94
159
|
}
|
|
95
|
-
console.log(`Removed ${
|
|
96
|
-
return;
|
|
160
|
+
console.log(`Removed ${target.path}.`);
|
|
97
161
|
} catch (err) {
|
|
98
162
|
console.warn(
|
|
99
|
-
`⚠️ Could not remove ${
|
|
163
|
+
`⚠️ Could not remove ${target.path}: ${
|
|
100
164
|
err instanceof Error ? err.message : String(err)
|
|
101
165
|
} — remove it by hand.`,
|
|
102
166
|
);
|
|
103
|
-
return;
|
|
104
167
|
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Package-manager install: remove the package directory we are running
|
|
108
|
-
// from, best-effort. Bin symlinks the package manager planted (e.g.
|
|
109
|
-
// ~/.bun/bin/cadet, the npm global bin) are left dangling — say so.
|
|
110
|
-
const runningFile = fileURLToPath(import.meta.url);
|
|
111
|
-
const pkgRoot = findInstalledPackageRoot(dirname(runningFile));
|
|
112
|
-
if (!pkgRoot) {
|
|
113
|
-
console.log(
|
|
114
|
-
"Cadet is running from a source checkout — skipping self-removal.",
|
|
115
|
-
);
|
|
116
|
-
console.log(
|
|
117
|
-
"Remove the installed copy with `bun remove -g mycadet` (or `npm uninstall -g mycadet`).",
|
|
118
|
-
);
|
|
119
168
|
return;
|
|
120
169
|
}
|
|
121
170
|
try {
|
|
122
|
-
rmSync(
|
|
123
|
-
console.log(`Removed ${
|
|
171
|
+
rmSync(target.path, { recursive: true, force: true });
|
|
172
|
+
console.log(`Removed ${target.path}.`);
|
|
124
173
|
console.log(
|
|
125
174
|
"The `cadet` command on your PATH is now stale — remove it if your package manager didn't (`bun remove -g mycadet` or `npm uninstall -g mycadet`).",
|
|
126
175
|
);
|
|
127
176
|
} catch (err) {
|
|
128
177
|
console.warn(
|
|
129
|
-
`⚠️ Could not remove ${
|
|
178
|
+
`⚠️ Could not remove ${target.path}: ${
|
|
130
179
|
err instanceof Error ? err.message : String(err)
|
|
131
180
|
} — remove it with \`bun remove -g mycadet\` (or \`npm uninstall -g mycadet\`).`,
|
|
132
181
|
);
|
|
@@ -146,10 +195,24 @@ function collectDeleteTargets(env = getCurrentEnvironment()): DeleteTarget[] {
|
|
|
146
195
|
{ path: getLogDir(env), description: "logs" },
|
|
147
196
|
// Not environment-scoped: deleting it affects every cadet CLI on the
|
|
148
197
|
// machine — called out in the prompt, not hidden.
|
|
149
|
-
{
|
|
198
|
+
{
|
|
199
|
+
path: getSharedStateDir(),
|
|
200
|
+
description: "shared state, including the terminal input history",
|
|
201
|
+
},
|
|
150
202
|
];
|
|
151
203
|
// Only targets that exist — the prompt lists what will actually go.
|
|
152
|
-
|
|
204
|
+
const existing = targets.filter((t) => existsSync(t.path));
|
|
205
|
+
// The log dir lives INSIDE a listed target (the config dir on
|
|
206
|
+
// macOS/Linux, the state dir on Windows) and goes with its parent's
|
|
207
|
+
// recursive delete — never list a path twice.
|
|
208
|
+
const deduped: DeleteTarget[] = [];
|
|
209
|
+
for (const t of existing) {
|
|
210
|
+
const covered = deduped.some(
|
|
211
|
+
(k) => t.path === k.path || t.path.startsWith(k.path + sep),
|
|
212
|
+
);
|
|
213
|
+
if (!covered) deduped.push(t);
|
|
214
|
+
}
|
|
215
|
+
return deduped;
|
|
153
216
|
}
|
|
154
217
|
|
|
155
218
|
function rmrf(target: string): void {
|
|
@@ -163,42 +226,48 @@ export const uninstall = Object.assign(
|
|
|
163
226
|
console.log("Usage: cadet uninstall [--yes]");
|
|
164
227
|
console.log("");
|
|
165
228
|
console.log(
|
|
166
|
-
"Remove Cadet entirely: delete every location it uses for this environment (assistant state, retired archives, config, logs) and
|
|
229
|
+
"Remove Cadet entirely: retire any remaining assistants, delete every location it uses for this environment (assistant state, retired archives, config, logs), and remove the Cadet installation itself.",
|
|
167
230
|
);
|
|
168
231
|
process.exit(0);
|
|
169
232
|
}
|
|
170
233
|
const yes = args.includes("--yes");
|
|
171
234
|
|
|
172
|
-
// Retire tears down an assistant AND archives its data. Uninstall assumes
|
|
173
|
-
// an empty rack: retiring first is the way to keep an archive.
|
|
174
|
-
if (loadAllAssistants().length > 0) {
|
|
175
|
-
console.error(
|
|
176
|
-
"Error: You still have assistants. Retire them first (`cadet retire <name>`) — retiring archives their data before removing it.",
|
|
177
|
-
);
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
235
|
const env = getCurrentEnvironment();
|
|
182
236
|
const envLabel = env.name ?? "production";
|
|
237
|
+
const existing = loadAllAssistants();
|
|
183
238
|
const targets = collectDeleteTargets(env);
|
|
184
|
-
const
|
|
239
|
+
const selfTarget = resolveSelfDeleteTarget();
|
|
185
240
|
|
|
186
241
|
console.log(
|
|
187
242
|
`This removes Cadet entirely for the '${envLabel}' environment and cannot be undone.`,
|
|
188
243
|
);
|
|
189
|
-
|
|
244
|
+
|
|
245
|
+
// Assistants still on the rack are retired — with their data deleted —
|
|
246
|
+
// as part of the uninstall, and retiring happens only AFTER the
|
|
247
|
+
// confirmation below: it is part of what the prompt describes, never a
|
|
248
|
+
// side effect of running the command. Uninstall deletes every location
|
|
249
|
+
// cadet uses INCLUDING the retired archives, so retiring first (which
|
|
250
|
+
// archives) only matters for the teardown it performs: docker
|
|
251
|
+
// assistants keep containers, volumes, and a network in docker that
|
|
252
|
+
// the filesystem sweep below never reaches. A warning, not a refusal
|
|
253
|
+
// (v0.0.10 run feedback: the hard error just blocked the uninstall).
|
|
254
|
+
if (existing.length > 0) {
|
|
255
|
+
console.warn(
|
|
256
|
+
`\n⚠️ ${existing.length} ${existing.length === 1 ? "assistant still exists" : "assistants still exist"}: ${existing.map((a) => a.assistantId).join(", ")}`,
|
|
257
|
+
);
|
|
258
|
+
console.warn(
|
|
259
|
+
" They will be retired as part of this uninstall — their data (and any retired archives) will be permanently deleted.",
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (targets.length === 0 && selfTarget === null) {
|
|
190
264
|
console.log("No Cadet data directories found to delete.");
|
|
191
265
|
} else {
|
|
192
266
|
console.log("It will permanently delete:");
|
|
193
|
-
for (const t of targets) {
|
|
194
|
-
console.log(` - ${t.path}
|
|
267
|
+
for (const t of selfTarget ? [...targets, selfTarget] : targets) {
|
|
268
|
+
console.log(` - ${t.path} — ${t.description}`);
|
|
195
269
|
}
|
|
196
270
|
}
|
|
197
|
-
const execBase = basename(execPath);
|
|
198
|
-
const isCompiledBinary = execBase === "cadet" || execBase === "cadet.exe";
|
|
199
|
-
if (isCompiledBinary) {
|
|
200
|
-
console.log(`It will then remove the Cadet executable at ${execPath}.`);
|
|
201
|
-
}
|
|
202
271
|
|
|
203
272
|
if (!yes) {
|
|
204
273
|
if (!canPromptForConfirmation()) {
|
|
@@ -217,6 +286,20 @@ export const uninstall = Object.assign(
|
|
|
217
286
|
}
|
|
218
287
|
}
|
|
219
288
|
|
|
289
|
+
for (const entry of existing) {
|
|
290
|
+
if (entry.cloud === "apple-container" || entry.cloud === "paired") {
|
|
291
|
+
console.log(` Skipping '${entry.assistantId}' on ${entry.cloud} — its lifecycle is managed elsewhere.`);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
await retireAssistantData(entry);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error(
|
|
298
|
+
` Warning: could not fully retire '${entry.assistantId}': ${err instanceof Error ? err.message : String(err)}`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
220
303
|
for (const t of targets) {
|
|
221
304
|
rmrf(t.path);
|
|
222
305
|
console.log(`Deleted ${t.path}.`);
|
package/src/commands/unpair.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Forget a pairing imported from another machine via `cadet connect import`:
|
|
5
5
|
* remove its lockfile entry and stored guardian token from THIS machine. Only
|
|
6
6
|
* paired assistants (`cloud: "paired"`) can be unpaired — `cadet retire` owns
|
|
7
|
-
* local and
|
|
7
|
+
* local and cloud assistants.
|
|
8
8
|
*
|
|
9
9
|
* This is client-side only: it forgets the connection here but does not revoke
|
|
10
10
|
* the device on the host. (Host-side revocation is `cadet devices`.)
|
|
@@ -37,7 +37,7 @@ OPTIONS:
|
|
|
37
37
|
|
|
38
38
|
Removes the local connection (lockfile entry + stored token). Only paired
|
|
39
39
|
assistants (imported via 'cadet connect import') can be unpaired; use
|
|
40
|
-
'cadet retire' for local or
|
|
40
|
+
'cadet retire' for local or cloud assistants.
|
|
41
41
|
|
|
42
42
|
EXAMPLES:
|
|
43
43
|
cadet unpair paired-desk
|
|
@@ -72,7 +72,7 @@ export const unpair = Object.assign(
|
|
|
72
72
|
|
|
73
73
|
if (entry.cloud !== "paired") {
|
|
74
74
|
console.error(
|
|
75
|
-
`Error: '${name}' is not a paired assistant. Use \`cadet retire\` to remove a local or
|
|
75
|
+
`Error: '${name}' is not a paired assistant. Use \`cadet retire\` to remove a local or cloud assistant.`,
|
|
76
76
|
);
|
|
77
77
|
process.exit(1);
|
|
78
78
|
}
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { resolveExistingLocalSigningKey } from "@mycadet/local-mode";
|
|
2
|
-
import {
|
|
2
|
+
import { isCloudHome } from "../lib/cloud-values.js";
|
|
3
3
|
import { spawnSync } from "child_process";
|
|
4
|
-
import { writeFileSync } from "fs";
|
|
5
4
|
import { join } from "path";
|
|
6
5
|
|
|
7
6
|
import cliPkg from "../../package.json";
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
GATEWAY_INTERNAL_PORT,
|
|
21
20
|
ASSISTANT_INTERNAL_PORT,
|
|
22
21
|
dockerResourceNames,
|
|
22
|
+
pullImage,
|
|
23
23
|
startContainers,
|
|
24
24
|
stopContainers,
|
|
25
25
|
} from "../lib/docker";
|
|
@@ -27,14 +27,14 @@ import {
|
|
|
27
27
|
fetchLatestStableVersion,
|
|
28
28
|
fetchReleases,
|
|
29
29
|
resolveImageRefs,
|
|
30
|
-
} from "../lib/
|
|
30
|
+
} from "../lib/cloud-releases";
|
|
31
31
|
import {
|
|
32
32
|
authHeaders,
|
|
33
33
|
fetchAssistantDetail,
|
|
34
34
|
fetchUpgradeInProgress,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from "../lib/
|
|
35
|
+
getCloudUrl,
|
|
36
|
+
readCloudToken,
|
|
37
|
+
} from "../lib/cloud-client";
|
|
38
38
|
import { checkManagedHealth } from "../lib/health-check.js";
|
|
39
39
|
import {
|
|
40
40
|
evaluateUpgradePoll,
|
|
@@ -47,7 +47,6 @@ import {
|
|
|
47
47
|
} from "../lib/backup-ops.js";
|
|
48
48
|
import { emitCliError, categorizeUpgradeError } from "../lib/cli-error.js";
|
|
49
49
|
import { parseAssistantTargetArg } from "../lib/assistant-target-args.js";
|
|
50
|
-
import { exec } from "../lib/step-runner.js";
|
|
51
50
|
import {
|
|
52
51
|
broadcastUpgradeEvent,
|
|
53
52
|
attemptFailedStateRestore,
|
|
@@ -77,7 +76,7 @@ import {
|
|
|
77
76
|
startLocalDaemon,
|
|
78
77
|
stopLocalProcesses,
|
|
79
78
|
} from "../lib/local.js";
|
|
80
|
-
import { restoreTunnelEdgeAndAutoTunnel } from "../lib/tunnel
|
|
79
|
+
import { restoreTunnelEdgeAndAutoTunnel } from "../lib/tunnel.js";
|
|
81
80
|
import {
|
|
82
81
|
leaseGuardianToken,
|
|
83
82
|
resetGuardianBootstrap,
|
|
@@ -119,12 +118,12 @@ function parseArgs(): UpgradeArgs {
|
|
|
119
118
|
console.log("");
|
|
120
119
|
console.log("Arguments:");
|
|
121
120
|
console.log(
|
|
122
|
-
" <name> Name of the assistant to upgrade
|
|
121
|
+
" <name> Name of the assistant to upgrade, defaults to active or only assistant",
|
|
123
122
|
);
|
|
124
123
|
console.log("");
|
|
125
124
|
console.log("Options:");
|
|
126
125
|
console.log(
|
|
127
|
-
" --version <version> Target version to upgrade to
|
|
126
|
+
" --version <version> Target version to upgrade to, defaults to the CLI version",
|
|
128
127
|
);
|
|
129
128
|
console.log(
|
|
130
129
|
" --latest Upgrade to the latest stable release, updating the CLI first if needed",
|
|
@@ -317,7 +316,7 @@ async function upgradeDocker(
|
|
|
317
316
|
);
|
|
318
317
|
return;
|
|
319
318
|
}
|
|
320
|
-
console.log(`🔁 Reinstalling ${versionTag}
|
|
319
|
+
console.log(`🔁 Reinstalling ${versionTag} with --force...\n`);
|
|
321
320
|
}
|
|
322
321
|
|
|
323
322
|
console.log("🔍 Resolving image references...");
|
|
@@ -407,7 +406,7 @@ async function upgradeDocker(
|
|
|
407
406
|
try {
|
|
408
407
|
for (const [service, image] of pullImages) {
|
|
409
408
|
console.log(` Pulling ${service}: ${image}`);
|
|
410
|
-
await
|
|
409
|
+
await pullImage(image);
|
|
411
410
|
}
|
|
412
411
|
} catch (pullErr) {
|
|
413
412
|
const detail = pullErr instanceof Error ? pullErr.message : String(pullErr);
|
|
@@ -454,7 +453,7 @@ async function upgradeDocker(
|
|
|
454
453
|
// Clean up old pre-upgrade backups, keep last 3
|
|
455
454
|
pruneOldBackups(entry.assistantId, 3);
|
|
456
455
|
} else {
|
|
457
|
-
console.warn("⚠️ Pre-upgrade backup failed
|
|
456
|
+
console.warn("⚠️ Pre-upgrade backup failed, continuing with upgrade\n");
|
|
458
457
|
}
|
|
459
458
|
|
|
460
459
|
// Persist the backup path so `cadet rollback` can restore the exact backup
|
|
@@ -502,17 +501,12 @@ async function upgradeDocker(
|
|
|
502
501
|
const ready = await waitForReady(entry.runtimeUrl);
|
|
503
502
|
if (ready) {
|
|
504
503
|
// Update lockfile with new service group topology
|
|
505
|
-
const newDigests = await captureImageRefs(res);
|
|
506
504
|
const updatedEntry: AssistantEntry = {
|
|
507
505
|
...entry,
|
|
508
506
|
containerInfo: {
|
|
509
507
|
assistantImage: imageTags.assistant,
|
|
510
508
|
gatewayImage: imageTags.gateway,
|
|
511
509
|
cesImage: imageTags["credential-executor"],
|
|
512
|
-
assistantDigest: newDigests?.assistant,
|
|
513
|
-
gatewayDigest: newDigests?.gateway,
|
|
514
|
-
cesDigest: newDigests?.["credential-executor"],
|
|
515
|
-
networkName: res.network,
|
|
516
510
|
assistantPort,
|
|
517
511
|
},
|
|
518
512
|
previousContainerInfo: entry.containerInfo,
|
|
@@ -668,13 +662,9 @@ async function upgradeDocker(
|
|
|
668
662
|
);
|
|
669
663
|
}
|
|
670
664
|
|
|
671
|
-
// Capture fresh digests from the now-running rolled-back containers.
|
|
672
|
-
const rollbackDigests = await captureImageRefs(res);
|
|
673
|
-
|
|
674
665
|
// Restore previous container info in lockfile after rollback.
|
|
675
|
-
// The
|
|
676
|
-
//
|
|
677
|
-
// the running containers (or fall back to previousImageRefs).
|
|
666
|
+
// The recorded image refs are the rollback record now (digests are
|
|
667
|
+
// gone from the contract).
|
|
678
668
|
const rolledBackEntry: AssistantEntry = {
|
|
679
669
|
...entry,
|
|
680
670
|
containerInfo: {
|
|
@@ -686,14 +676,6 @@ async function upgradeDocker(
|
|
|
686
676
|
cesImage:
|
|
687
677
|
entry.containerInfo?.cesImage ??
|
|
688
678
|
previousImageRefs["credential-executor"],
|
|
689
|
-
assistantDigest:
|
|
690
|
-
rollbackDigests?.assistant ?? previousImageRefs.assistant,
|
|
691
|
-
gatewayDigest:
|
|
692
|
-
rollbackDigests?.gateway ?? previousImageRefs.gateway,
|
|
693
|
-
cesDigest:
|
|
694
|
-
rollbackDigests?.["credential-executor"] ??
|
|
695
|
-
previousImageRefs["credential-executor"],
|
|
696
|
-
networkName: res.network,
|
|
697
679
|
assistantPort,
|
|
698
680
|
},
|
|
699
681
|
previousContainerInfo: undefined,
|
|
@@ -909,7 +891,7 @@ async function upgradeLocal(
|
|
|
909
891
|
);
|
|
910
892
|
return;
|
|
911
893
|
}
|
|
912
|
-
console.log(`🔁 Reinstalling ${targetVersion}
|
|
894
|
+
console.log(`🔁 Reinstalling ${targetVersion} with --force...\n`);
|
|
913
895
|
}
|
|
914
896
|
|
|
915
897
|
console.log(
|
|
@@ -951,7 +933,7 @@ async function upgradeLocal(
|
|
|
951
933
|
console.log(` Backup saved: ${backupPath}\n`);
|
|
952
934
|
pruneOldBackups(entry.assistantId, 3);
|
|
953
935
|
} else {
|
|
954
|
-
console.warn("⚠️ Pre-upgrade backup failed
|
|
936
|
+
console.warn("⚠️ Pre-upgrade backup failed, continuing with upgrade\n");
|
|
955
937
|
}
|
|
956
938
|
|
|
957
939
|
let bootstrapSecret = entry.guardianBootstrapSecret;
|
|
@@ -1035,17 +1017,11 @@ async function upgradeLocal(
|
|
|
1035
1017
|
console.log("✅ Local assistant processes started\n");
|
|
1036
1018
|
|
|
1037
1019
|
const workspaceDir = join(entry.resources.instanceDir, "workspace");
|
|
1038
|
-
|
|
1020
|
+
await restoreTunnelEdgeAndAutoTunnel(
|
|
1039
1021
|
entry.assistantId,
|
|
1040
1022
|
entry.resources.gatewayPort,
|
|
1041
1023
|
workspaceDir,
|
|
1042
1024
|
);
|
|
1043
|
-
if (ngrokChild?.pid) {
|
|
1044
|
-
writeFileSync(
|
|
1045
|
-
join(entry.resources.instanceDir, "ngrok.pid"),
|
|
1046
|
-
String(ngrokChild.pid),
|
|
1047
|
-
);
|
|
1048
|
-
}
|
|
1049
1025
|
|
|
1050
1026
|
console.log("Waiting for assistant to become ready...");
|
|
1051
1027
|
const ready = await waitForReady(adminUrl);
|
|
@@ -1113,7 +1089,7 @@ function sleep(ms: number): Promise<void> {
|
|
|
1113
1089
|
}
|
|
1114
1090
|
|
|
1115
1091
|
/**
|
|
1116
|
-
* Poll the
|
|
1092
|
+
* Poll the cloud until the upgrade completes, then confirm health.
|
|
1117
1093
|
* Mirrors the web UI's Software Updates flow (poll the DB-backed
|
|
1118
1094
|
* `current_release_version` rather than the gateway, which bounces while
|
|
1119
1095
|
* the service group restarts).
|
|
@@ -1163,7 +1139,7 @@ async function waitForPlatformUpgrade(
|
|
|
1163
1139
|
const healthDeadline = Date.now() + PLATFORM_HEALTH_CONFIRM_TIMEOUT_MS;
|
|
1164
1140
|
while (Date.now() < healthDeadline) {
|
|
1165
1141
|
const health = await checkManagedHealth(
|
|
1166
|
-
entry.runtimeUrl ||
|
|
1142
|
+
entry.runtimeUrl || getCloudUrl(),
|
|
1167
1143
|
entry.assistantId,
|
|
1168
1144
|
);
|
|
1169
1145
|
if (health.status === "healthy") {
|
|
@@ -1187,7 +1163,7 @@ async function waitForPlatformUpgrade(
|
|
|
1187
1163
|
|
|
1188
1164
|
const timeoutMin = Math.round(timeoutMs / 60_000);
|
|
1189
1165
|
console.warn(
|
|
1190
|
-
`⚠️ Upgrade request was accepted but completion was not confirmed within ${timeoutMin} minutes. The
|
|
1166
|
+
`⚠️ Upgrade request was accepted but completion was not confirmed within ${timeoutMin} minutes. The cloud may still be working — check \`cadet ps\` or the web settings page.`,
|
|
1191
1167
|
);
|
|
1192
1168
|
}
|
|
1193
1169
|
|
|
@@ -1197,10 +1173,10 @@ async function upgradePlatform(
|
|
|
1197
1173
|
wait: boolean,
|
|
1198
1174
|
): Promise<void> {
|
|
1199
1175
|
console.log(
|
|
1200
|
-
`🔄 Upgrading
|
|
1176
|
+
`🔄 Upgrading cloud-hosted assistant '${entry.assistantId}'...\n`,
|
|
1201
1177
|
);
|
|
1202
1178
|
|
|
1203
|
-
const token =
|
|
1179
|
+
const token = readCloudToken();
|
|
1204
1180
|
if (!token) {
|
|
1205
1181
|
const msg =
|
|
1206
1182
|
"Error: Not logged in. Run `cadet login --token <token>` first.";
|
|
@@ -1214,7 +1190,7 @@ async function upgradePlatform(
|
|
|
1214
1190
|
// downgrades before POSTing, and bail if an upgrade is already running.
|
|
1215
1191
|
const [assistantDetail, health] = await Promise.all([
|
|
1216
1192
|
fetchAssistantDetail(token, entry.assistantId, entry.runtimeUrl),
|
|
1217
|
-
checkManagedHealth(entry.runtimeUrl ||
|
|
1193
|
+
checkManagedHealth(entry.runtimeUrl || getCloudUrl(), entry.assistantId),
|
|
1218
1194
|
]);
|
|
1219
1195
|
// Health probe first, DB-backed field as the sleeping-assistant fallback.
|
|
1220
1196
|
const currentVersion =
|
|
@@ -1225,7 +1201,7 @@ async function upgradePlatform(
|
|
|
1225
1201
|
// platform may differ from the active lockfile default.
|
|
1226
1202
|
const releases = await fetchReleases({
|
|
1227
1203
|
channel: releaseChannel,
|
|
1228
|
-
|
|
1204
|
+
cloudUrl: entry.runtimeUrl,
|
|
1229
1205
|
});
|
|
1230
1206
|
|
|
1231
1207
|
const resolution = resolveUpgradeTarget({
|
|
@@ -1276,7 +1252,7 @@ async function upgradePlatform(
|
|
|
1276
1252
|
|
|
1277
1253
|
const headers = await authHeaders(token, entry.runtimeUrl);
|
|
1278
1254
|
|
|
1279
|
-
const url = `${entry.runtimeUrl ||
|
|
1255
|
+
const url = `${entry.runtimeUrl || getCloudUrl()}/v1/assistants/${encodeURIComponent(entry.assistantId)}/upgrade/`;
|
|
1280
1256
|
const body: { version?: string } = {};
|
|
1281
1257
|
if (version) {
|
|
1282
1258
|
body.version = version;
|
|
@@ -1345,11 +1321,11 @@ async function upgradePlatform(
|
|
|
1345
1321
|
}
|
|
1346
1322
|
|
|
1347
1323
|
// NOTE: We intentionally do NOT broadcast a "complete" event here.
|
|
1348
|
-
// The
|
|
1324
|
+
// The cloud API returning 200 only means "upgrade request accepted" —
|
|
1349
1325
|
// the service group has not yet restarted with the new version. The
|
|
1350
1326
|
// completion signal will come from the client's health-check
|
|
1351
1327
|
// version-change detection (DaemonConnection.swift) once the new
|
|
1352
|
-
// version actually appears after the
|
|
1328
|
+
// version actually appears after the cloud restarts the service group.
|
|
1353
1329
|
|
|
1354
1330
|
console.log(`✅ ${result.detail}`);
|
|
1355
1331
|
if (result.version) {
|
|
@@ -1483,7 +1459,7 @@ async function resolveLatestStableTag(): Promise<string> {
|
|
|
1483
1459
|
);
|
|
1484
1460
|
emitCliError(
|
|
1485
1461
|
"UNKNOWN",
|
|
1486
|
-
"Could not determine the latest stable release from the
|
|
1462
|
+
"Could not determine the latest stable release from the cloud API",
|
|
1487
1463
|
);
|
|
1488
1464
|
process.exit(1);
|
|
1489
1465
|
}
|
|
@@ -1560,7 +1536,7 @@ export const upgrade = Object.assign(
|
|
|
1560
1536
|
return;
|
|
1561
1537
|
}
|
|
1562
1538
|
|
|
1563
|
-
// When --latest is passed, resolve the target from the
|
|
1539
|
+
// When --latest is passed, resolve the target from the cloud API and
|
|
1564
1540
|
// self-update the CLI if it's behind. The resolved version is then used
|
|
1565
1541
|
// as the explicit target for the rest of the upgrade flow.
|
|
1566
1542
|
let effectiveVersion = version;
|
|
@@ -1586,7 +1562,7 @@ export const upgrade = Object.assign(
|
|
|
1586
1562
|
return;
|
|
1587
1563
|
}
|
|
1588
1564
|
|
|
1589
|
-
if (
|
|
1565
|
+
if (isCloudHome(cloud)) {
|
|
1590
1566
|
await upgradePlatform(entry, effectiveVersion, !noWait);
|
|
1591
1567
|
return;
|
|
1592
1568
|
}
|
package/src/commands/use.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const use = Object.assign(
|
|
|
38
38
|
`Active assistant: ${formatAssistantReference(result.entry)}`,
|
|
39
39
|
);
|
|
40
40
|
} else {
|
|
41
|
-
console.log(`Active assistant: ${active}
|
|
41
|
+
console.log(`Active assistant: ${active} — not found in lockfile`);
|
|
42
42
|
}
|
|
43
43
|
} else {
|
|
44
44
|
console.log("No active assistant set.");
|