@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
package/src/lib/local.ts
CHANGED
|
@@ -74,11 +74,10 @@ import {
|
|
|
74
74
|
executableName,
|
|
75
75
|
isProcessAlive,
|
|
76
76
|
pathListDelimiter,
|
|
77
|
-
readProcessCommandLine,
|
|
78
77
|
resolveProcessState,
|
|
79
|
-
stopProcess,
|
|
80
78
|
stopProcessByPidFile,
|
|
81
79
|
} from "./process.js";
|
|
80
|
+
import { stopTunnelProcess } from "./tunnel.js";
|
|
82
81
|
import { stripVersionPrefix } from "./version-compat.js";
|
|
83
82
|
import { openLogFile, pipeToLogFile } from "./xdg-log.js";
|
|
84
83
|
|
|
@@ -699,7 +698,7 @@ function sleep(ms: number): Promise<void> {
|
|
|
699
698
|
* A readiness answer proves only that something listens on the port: a foreign
|
|
700
699
|
* listener holding it answers in the daemon's place, so a daemon aborting over
|
|
701
700
|
* the collision still reads as "up" or "migrating". Ownership is confirmed
|
|
702
|
-
* from the listening PID where the
|
|
701
|
+
* from the listening PID where the cloud can report it. Otherwise the spawn
|
|
703
702
|
* gets a short settle window, which is long enough because transports bind
|
|
704
703
|
* before migrations run, so an address collision aborts the daemon early.
|
|
705
704
|
*/
|
|
@@ -938,7 +937,7 @@ async function startDaemonWatchFromSource(
|
|
|
938
937
|
}
|
|
939
938
|
writeFileSync(pidFile, String(daemonPid), "utf-8");
|
|
940
939
|
|
|
941
|
-
console.log(" Assistant started in watch mode
|
|
940
|
+
console.log(" Assistant started in watch mode with bun --watch");
|
|
942
941
|
return trackDaemonSpawn(child);
|
|
943
942
|
}
|
|
944
943
|
|
|
@@ -985,6 +984,41 @@ function resolveGatewayDir(resources?: LocalInstanceResources): string {
|
|
|
985
984
|
}
|
|
986
985
|
}
|
|
987
986
|
|
|
987
|
+
/**
|
|
988
|
+
* How to invoke a `gateway <subcommand>` CLI process so it resolves the same
|
|
989
|
+
* workspace AND gateway security dir as the locally spawned gateway server:
|
|
990
|
+
* run the gateway package's CLI entry through bun with the spawn state root
|
|
991
|
+
* as cwd and the instance-pinned CADET_WORKSPACE_DIR / GATEWAY_SECURITY_DIR
|
|
992
|
+
* the server was given (the file-backed pairing store must resolve
|
|
993
|
+
* identically in both processes). Callers pass the resolved assistant
|
|
994
|
+
* entry's `resources` (undefined for the default instance, where both
|
|
995
|
+
* processes fall back to the same cwd-relative paths).
|
|
996
|
+
*/
|
|
997
|
+
export function gatewayCliInvocation(resources?: LocalInstanceResources): {
|
|
998
|
+
command: string;
|
|
999
|
+
args: string[];
|
|
1000
|
+
cwd: string;
|
|
1001
|
+
env: Record<string, string | undefined>;
|
|
1002
|
+
} {
|
|
1003
|
+
return {
|
|
1004
|
+
command: resolveBunExecutable(),
|
|
1005
|
+
args: ["run", join(resolveGatewayDir(resources), "src/cli/index.ts")],
|
|
1006
|
+
cwd: spawnStateRoot(),
|
|
1007
|
+
env: {
|
|
1008
|
+
...process.env,
|
|
1009
|
+
...(resources
|
|
1010
|
+
? {
|
|
1011
|
+
CADET_WORKSPACE_DIR: join(resources.instanceDir, "workspace"),
|
|
1012
|
+
GATEWAY_SECURITY_DIR: join(
|
|
1013
|
+
resources.instanceDir,
|
|
1014
|
+
"gateway-security",
|
|
1015
|
+
),
|
|
1016
|
+
}
|
|
1017
|
+
: {}),
|
|
1018
|
+
},
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
988
1022
|
function resolveCesDir(resources?: LocalInstanceResources): string {
|
|
989
1023
|
const runtimeCesDir = localRuntimeCesDir(resources);
|
|
990
1024
|
if (runtimeCesDir) return runtimeCesDir;
|
|
@@ -1119,9 +1153,7 @@ export async function startCes(
|
|
|
1119
1153
|
// Source tree / bunx: run the CES entry point via bun.
|
|
1120
1154
|
const cesDir = runtimeCesDir ?? resolveCesDir(resources);
|
|
1121
1155
|
const cesEntry = join(cesDir, "src/main.ts");
|
|
1122
|
-
const bunArgs = watch
|
|
1123
|
-
? ["--watch", "run", cesEntry]
|
|
1124
|
-
: ["run", cesEntry];
|
|
1156
|
+
const bunArgs = watch ? ["--watch", "run", cesEntry] : ["run", cesEntry];
|
|
1125
1157
|
const cesLogFd = openLogFile("hatch.log");
|
|
1126
1158
|
ces = spawn(resolveBunExecutable(), bunArgs, {
|
|
1127
1159
|
cwd: spawnStateRoot(),
|
|
@@ -1632,7 +1664,7 @@ export async function startLocalDaemon(
|
|
|
1632
1664
|
"ANTHROPIC_API_KEY",
|
|
1633
1665
|
"APP_VERSION",
|
|
1634
1666
|
"CADET_ENVIRONMENT",
|
|
1635
|
-
"
|
|
1667
|
+
"CADET_CLOUD_URL",
|
|
1636
1668
|
"QDRANT_HTTP_PORT",
|
|
1637
1669
|
"QDRANT_URL",
|
|
1638
1670
|
"RUNTIME_HTTP_PORT",
|
|
@@ -1642,7 +1674,7 @@ export async function startLocalDaemon(
|
|
|
1642
1674
|
"CADET_DEBUG",
|
|
1643
1675
|
"CADET_DEV",
|
|
1644
1676
|
"CADET_DESKTOP_APP",
|
|
1645
|
-
"
|
|
1677
|
+
"CADET_DISABLE_CLOUD",
|
|
1646
1678
|
"CADET_MIGRATION_EXPORT_ALLOWED_HOSTS",
|
|
1647
1679
|
"CADET_MIGRATION_IMPORT_ALLOWED_HOSTS",
|
|
1648
1680
|
"CADET_WORKSPACE_DIR",
|
|
@@ -1931,16 +1963,6 @@ export async function startGateway(
|
|
|
1931
1963
|
return gatewayUrl;
|
|
1932
1964
|
}
|
|
1933
1965
|
|
|
1934
|
-
/** Check whether a PID belongs to an ngrok process via its command line. */
|
|
1935
|
-
function isNgrokProcess(pid: number): boolean {
|
|
1936
|
-
try {
|
|
1937
|
-
const output = readProcessCommandLine(pid);
|
|
1938
|
-
return /ngrok/i.test(output);
|
|
1939
|
-
} catch {
|
|
1940
|
-
return false;
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
1966
|
/**
|
|
1945
1967
|
* Stop any locally-running daemon and gateway processes
|
|
1946
1968
|
* and clean up PID files. Called when hatch fails partway through
|
|
@@ -1969,22 +1991,12 @@ export async function stopLocalProcesses(
|
|
|
1969
1991
|
const cesPidFile = join(cadetDir, "ces.pid");
|
|
1970
1992
|
await stopProcessByPidFile(cesPidFile, "credential-executor");
|
|
1971
1993
|
|
|
1972
|
-
//
|
|
1973
|
-
//
|
|
1974
|
-
//
|
|
1975
|
-
|
|
1976
|
-
const ngrokPidFile = join(cadetDir, "ngrok.pid");
|
|
1977
|
-
if (existsSync(ngrokPidFile)) {
|
|
1978
|
-
try {
|
|
1979
|
-
const pid = parseInt(readFileSync(ngrokPidFile, "utf-8").trim(), 10);
|
|
1980
|
-
if (!isNaN(pid) && isNgrokProcess(pid)) {
|
|
1981
|
-
await stopProcess(pid, "ngrok");
|
|
1982
|
-
}
|
|
1983
|
-
unlinkSync(ngrokPidFile);
|
|
1984
|
-
} catch {}
|
|
1985
|
-
}
|
|
1994
|
+
// Stop the tunnel agent this CLI spawned (root lockfile `tunnel` entry),
|
|
1995
|
+
// verifying the pid still belongs to the provider's binary before signaling
|
|
1996
|
+
// it — PID reuse must never send an unrelated process a signal.
|
|
1997
|
+
await stopTunnelProcess();
|
|
1986
1998
|
|
|
1987
1999
|
// Stop the nginx ingress if one is fronting this gateway (it guards against
|
|
1988
|
-
// PID reuse itself
|
|
2000
|
+
// PID reuse itself).
|
|
1989
2001
|
await stopIngressNginx(join(cadetDir, "workspace"));
|
|
1990
2002
|
}
|
package/src/lib/nginx-ingress.ts
CHANGED
|
@@ -19,10 +19,13 @@ import { join } from "node:path";
|
|
|
19
19
|
import { cloudAssistantHubUrl } from "@mycadet/environments";
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
|
+
clearRootProcess,
|
|
22
23
|
getAssistantDisplayName,
|
|
23
24
|
lookupAssistantByIdentifier,
|
|
25
|
+
stampRootProcess,
|
|
24
26
|
type AssistantEntry,
|
|
25
27
|
} from "./assistant-config.js";
|
|
28
|
+
import type { RemoteHost } from "./constants.js";
|
|
26
29
|
import { getCurrentEnvironment } from "./environments/resolve.js";
|
|
27
30
|
import { waitForDaemonReady } from "./http-client.js";
|
|
28
31
|
import {
|
|
@@ -159,6 +162,12 @@ export function cloudWebHubUrl(env: string | undefined): string {
|
|
|
159
162
|
return cloudAssistantHubUrl(env);
|
|
160
163
|
}
|
|
161
164
|
|
|
165
|
+
// The edge serves remote browsers, so the bundle's remote-gateway machinery
|
|
166
|
+
// must always run: it is reached through this self-hosted edge however the
|
|
167
|
+
// cadet itself is hosted. "cloud" is never stamped (the cloud serves its
|
|
168
|
+
// own bundles).
|
|
169
|
+
const EDGE_SERVED_REMOTE: RemoteHost = "custom";
|
|
170
|
+
|
|
162
171
|
function remoteWebIngressConfig(
|
|
163
172
|
opts: Pick<
|
|
164
173
|
RemoteWebIngressOptions,
|
|
@@ -166,10 +175,8 @@ function remoteWebIngressConfig(
|
|
|
166
175
|
>,
|
|
167
176
|
): Record<string, unknown> {
|
|
168
177
|
return {
|
|
169
|
-
|
|
178
|
+
remote: EDGE_SERVED_REMOTE,
|
|
170
179
|
apiBaseUrl: "/v1",
|
|
171
|
-
platformDisabled: true,
|
|
172
|
-
disablePlatform: true,
|
|
173
180
|
...(opts.assistantName ? { assistantName: opts.assistantName } : {}),
|
|
174
181
|
...(opts.assistantId ? { assistantId: opts.assistantId } : {}),
|
|
175
182
|
...(opts.hubUrl ? { hubUrl: opts.hubUrl } : {}),
|
|
@@ -182,7 +189,7 @@ function remoteWebIngressConfig(
|
|
|
182
189
|
* fingerprint matches, so this must change whenever the generated index or
|
|
183
190
|
* nginx template does.
|
|
184
191
|
*/
|
|
185
|
-
export const EDGE_TEMPLATE_VERSION =
|
|
192
|
+
export const EDGE_TEMPLATE_VERSION = 10;
|
|
186
193
|
|
|
187
194
|
/**
|
|
188
195
|
* Stable fingerprint of the SPA config injected into the served index and
|
|
@@ -353,7 +360,7 @@ http {
|
|
|
353
360
|
# remote client cannot smuggle one. Every caller reaches this loopback-only
|
|
354
361
|
# listener through the TLS-terminating front (tunnel agent), so the raw peer
|
|
355
362
|
# is always 127.0.0.1; the front records the real client as the RIGHTMOST
|
|
356
|
-
# X-Forwarded-For entry (
|
|
363
|
+
# X-Forwarded-For entry (tunnel providers append one; tailscale serve sets it),
|
|
357
364
|
# which the remote client cannot control. Fall back to the raw peer when the
|
|
358
365
|
# front sets no X-Forwarded-For.
|
|
359
366
|
map $http_x_forwarded_for $cadet_edge_client_ip {
|
|
@@ -487,7 +494,8 @@ export function getNginxVersion(): string | null {
|
|
|
487
494
|
* process.ts helpers: stopProcessByPidFile's isCadetProcess() guard only
|
|
488
495
|
* matches command lines containing a cadet path, which fails for a custom
|
|
489
496
|
* CADET_WORKSPACE_DIR and would silently leave nginx running (the same
|
|
490
|
-
* reason local.ts
|
|
497
|
+
* reason local.ts stops the tunnel agent directly). This module is also
|
|
498
|
+
* imported by
|
|
491
499
|
* sleep/retire, whose tests mock.module() process.js process-globally —
|
|
492
500
|
* depending on it here would couple this lib's behavior to those mocks.
|
|
493
501
|
*/
|
|
@@ -618,13 +626,15 @@ function clearIngressState(workspaceDir: string): void {
|
|
|
618
626
|
function clearStoppedIngress(workspaceDir: string, pidPath: string): void {
|
|
619
627
|
clearIngressState(workspaceDir);
|
|
620
628
|
rmSync(pidPath, { force: true });
|
|
629
|
+
// The edge is gone; drop the root lockfile's process entry with it.
|
|
630
|
+
clearRootProcess("app");
|
|
621
631
|
}
|
|
622
632
|
|
|
623
633
|
/**
|
|
624
|
-
* Write the nginx config and spawn nginx detached (
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
634
|
+
* Write the nginx config and spawn nginx detached (stdout/stderr to a log
|
|
635
|
+
* file, fd closed after spawn, caller unrefs). nginx runs with `daemon off`
|
|
636
|
+
* so the spawned process is the master; it writes its pid to nginx.pid under
|
|
637
|
+
* the prefix dir.
|
|
628
638
|
*/
|
|
629
639
|
export function startIngressNginx(opts: {
|
|
630
640
|
workspaceDir: string;
|
|
@@ -1028,8 +1038,22 @@ export async function stopContainerTunnelEdge(
|
|
|
1028
1038
|
return stopIngressNginx(workspaceDir);
|
|
1029
1039
|
}
|
|
1030
1040
|
|
|
1031
|
-
/**
|
|
1032
|
-
|
|
1041
|
+
/** Record the running edge's pid in the root lockfile for `cadet ps`. */
|
|
1042
|
+
function stampAppProcessEntry(
|
|
1043
|
+
workspaceDir: string,
|
|
1044
|
+
assistantId?: string,
|
|
1045
|
+
): void {
|
|
1046
|
+
const pid = getIngressPid(workspaceDir);
|
|
1047
|
+
if (pid !== null) {
|
|
1048
|
+
stampRootProcess("app", {
|
|
1049
|
+
pid,
|
|
1050
|
+
startedAt: new Date().toISOString(),
|
|
1051
|
+
assistantId,
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/** Resolved edge a tunnel (or bring-your-own HTTPS front) should target. */ export interface TunnelEdge {
|
|
1033
1057
|
/** Loopback listen port the HTTPS front should forward to. */
|
|
1034
1058
|
port: number;
|
|
1035
1059
|
/** True when this call started the edge, false when a running one was reused. */
|
|
@@ -1081,7 +1105,6 @@ export async function ensureTunnelEdge(opts: {
|
|
|
1081
1105
|
const assistantName = opts.assistantId
|
|
1082
1106
|
? lockfileAssistantName(opts.assistantId)
|
|
1083
1107
|
: undefined;
|
|
1084
|
-
|
|
1085
1108
|
const result = await startRemoteWebIngress({
|
|
1086
1109
|
workspaceDir: opts.workspaceDir,
|
|
1087
1110
|
gatewayPort: opts.gatewayPort,
|
|
@@ -1093,6 +1116,7 @@ export async function ensureTunnelEdge(opts: {
|
|
|
1093
1116
|
|
|
1094
1117
|
switch (result.status) {
|
|
1095
1118
|
case "started":
|
|
1119
|
+
stampAppProcessEntry(opts.workspaceDir, opts.assistantId);
|
|
1096
1120
|
return {
|
|
1097
1121
|
port: result.listenPort,
|
|
1098
1122
|
started: true,
|
|
@@ -1126,6 +1150,7 @@ export async function ensureTunnelEdge(opts: {
|
|
|
1126
1150
|
stuckEdgeRecoveryHint(opts.workspaceDir),
|
|
1127
1151
|
);
|
|
1128
1152
|
}
|
|
1153
|
+
stampAppProcessEntry(opts.workspaceDir, opts.assistantId);
|
|
1129
1154
|
return {
|
|
1130
1155
|
port: result.listenPort,
|
|
1131
1156
|
started: false,
|
|
@@ -1135,13 +1160,13 @@ export async function ensureTunnelEdge(opts: {
|
|
|
1135
1160
|
case "nginx-missing":
|
|
1136
1161
|
throw new Error(
|
|
1137
1162
|
"nginx is not installed, so the tunnel edge cannot start. " +
|
|
1138
|
-
"
|
|
1163
|
+
"See https://nginx.org/en/docs/install.html for how to install, " +
|
|
1139
1164
|
"or point NGINX_BIN at an existing binary.",
|
|
1140
1165
|
);
|
|
1141
1166
|
case "web-dist-missing":
|
|
1142
1167
|
throw new Error(
|
|
1143
1168
|
"Unable to locate built web assets for the remote web edge. " +
|
|
1144
|
-
"Build the SPA
|
|
1169
|
+
"Build the SPA with `cd clients/web && VITE_CLOUD_MODE=false bun run build`, " +
|
|
1145
1170
|
"or install @mycadet/web so its packaged dist directory is available.",
|
|
1146
1171
|
);
|
|
1147
1172
|
case "unreachable":
|
package/src/lib/pair.ts
CHANGED
|
@@ -50,6 +50,6 @@ export function formatWebApproveFailure(
|
|
|
50
50
|
return [
|
|
51
51
|
`${rejection} ${gatewayUrl} (assistant "${assistantReference}", environment "${envName}").`,
|
|
52
52
|
`Codes are minted by the gateway that serves the pair page, expire after ${CODE_TTL_MINUTES} minutes, and are single-use.`,
|
|
53
|
-
"If the pair page came from a different assistant or environment
|
|
53
|
+
"If the pair page came from a different assistant or environment, such as the desktop app's, re-run with that environment's CADET_ENVIRONMENT and that assistant's name.",
|
|
54
54
|
].join("\n");
|
|
55
55
|
}
|
package/src/lib/process.ts
CHANGED
|
@@ -76,7 +76,7 @@ export function windowsCommandLineLookupArgs(pid: number): string[] {
|
|
|
76
76
|
];
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/** Read a process command line using the
|
|
79
|
+
/** Read a process command line using the cloud's process inspection tool. */
|
|
80
80
|
export function readProcessCommandLine(
|
|
81
81
|
pid: number,
|
|
82
82
|
hostPlatform: NodeJS.Platform = platform(),
|
|
@@ -86,7 +86,7 @@ const PROVIDER_LABELS: Record<LlmProviderId, string> = {
|
|
|
86
86
|
typesafe: "TypeSafe",
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
// Ranked for the hatch prompt: Cadet Cloud first (the
|
|
89
|
+
// Ranked for the hatch prompt: Cadet Cloud first (the cloud connection —
|
|
90
90
|
// no key to collect), then the hosted-inference gateways, then the BYO-key
|
|
91
91
|
// incumbents, then the rest, with the endpoint-supplied catalogs last.
|
|
92
92
|
export const HATCH_PROVIDER_CHOICES: readonly LlmProviderId[] = [
|
|
@@ -681,11 +681,11 @@ export async function ensureProviderApiKey(
|
|
|
681
681
|
|
|
682
682
|
if (!envVarName) {
|
|
683
683
|
// Managed-connection provider (e.g. Cadet Cloud): authenticated through
|
|
684
|
-
// the
|
|
684
|
+
// the cloud, not a user-supplied API key. Checked before the
|
|
685
685
|
// supported-provider guard so the keyless flow stays reachable.
|
|
686
686
|
return {
|
|
687
687
|
status: "skipped",
|
|
688
|
-
message: `${providerName} authenticates through the
|
|
688
|
+
message: `${providerName} authenticates through the cloud connection; no API key needed.`,
|
|
689
689
|
};
|
|
690
690
|
}
|
|
691
691
|
|
|
@@ -7,7 +7,7 @@ import type { AssistantEntry } from "./assistant-config.js";
|
|
|
7
7
|
* Retire an Apple Container assistant by sending a retire command to the
|
|
8
8
|
* macOS app via the management socket. The app handles the full lifecycle:
|
|
9
9
|
* stop the pod, archive the instance directory, remove the guardian token,
|
|
10
|
-
* deregister from the
|
|
10
|
+
* deregister from the cloud, and remove the lockfile entry.
|
|
11
11
|
*/
|
|
12
12
|
export async function retireAppleContainer(
|
|
13
13
|
name: string,
|
package/src/lib/runtime-url.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCloudHome } from "./cloud-values.js";
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
|
|
4
4
|
import { getLocalLanIPv4 } from "./local";
|
|
@@ -11,12 +11,12 @@ import type { AssistantEntry } from "./assistant-config.js";
|
|
|
11
11
|
* - For local/docker assistants, `runtimeUrl` is the loopback gateway and
|
|
12
12
|
* the runtime serves `/v1/migrations/<subpath>` directly. The CLI hits
|
|
13
13
|
* that path with guardian-token bearer auth.
|
|
14
|
-
* - For
|
|
15
|
-
* platform host (e.g. `https://cloud.mycadet.ai`). The
|
|
14
|
+
* - For cloud-managed (cloud="cadet") assistants, `runtimeUrl` is the
|
|
15
|
+
* platform host (e.g. `https://cloud.mycadet.ai`). The cloud's
|
|
16
16
|
* `MigrationViewSet` does NOT expose `export-to-gcs` or arbitrary runtime
|
|
17
17
|
* migration paths under `/v1/migrations/...`. The wildcard runtime proxy
|
|
18
18
|
* at `/v1/assistants/<id>/<path:rest>` is what forwards arbitrary runtime
|
|
19
|
-
* paths to the
|
|
19
|
+
* paths to the cloud runtime — vembda's unified proxy bootstraps the
|
|
20
20
|
* guardian token internally for the runtime call. From the CLI side it's
|
|
21
21
|
* user-session auth.
|
|
22
22
|
*
|
|
@@ -27,7 +27,7 @@ export function resolveRuntimeMigrationUrl(
|
|
|
27
27
|
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
28
28
|
subpath: string,
|
|
29
29
|
): string {
|
|
30
|
-
if (
|
|
30
|
+
if (isCloudHome(entry.cloud)) {
|
|
31
31
|
return `${entry.runtimeUrl}/v1/assistants/${entry.assistantId}/migrations/${subpath}`;
|
|
32
32
|
}
|
|
33
33
|
return `${entry.runtimeUrl}/v1/migrations/${subpath}`;
|
|
@@ -39,9 +39,9 @@ export function resolveRuntimeMigrationUrl(
|
|
|
39
39
|
*
|
|
40
40
|
* - For local/docker assistants, `runtimeUrl` is the loopback gateway and
|
|
41
41
|
* the runtime serves `/v1/<subpath>` directly.
|
|
42
|
-
* - For
|
|
42
|
+
* - For cloud-managed (cloud="cadet") assistants the path is rewritten
|
|
43
43
|
* to the wildcard runtime proxy:
|
|
44
|
-
* `{
|
|
44
|
+
* `{cloudUrl}/v1/assistants/<assistantId>/<subpath>`.
|
|
45
45
|
*
|
|
46
46
|
* The `subpath` is appended verbatim (e.g. `"identity"`).
|
|
47
47
|
*/
|
|
@@ -49,7 +49,7 @@ export function resolveRuntimeUrl(
|
|
|
49
49
|
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
50
50
|
subpath: string,
|
|
51
51
|
): string {
|
|
52
|
-
if (
|
|
52
|
+
if (isCloudHome(entry.cloud)) {
|
|
53
53
|
return `${entry.runtimeUrl}/v1/assistants/${entry.assistantId}/${subpath}`;
|
|
54
54
|
}
|
|
55
55
|
return `${entry.runtimeUrl}/v1/${subpath}`;
|
package/src/lib/statefulset.ts
CHANGED
|
@@ -199,7 +199,7 @@ export const DOCKER_STATEFUL_SET_SPEC: DockerStatefulSetSpec = {
|
|
|
199
199
|
(v): HostEnv => ({ kind: "host", name: v }),
|
|
200
200
|
),
|
|
201
201
|
{ kind: "host", name: "CADET_ENVIRONMENT" },
|
|
202
|
-
{ kind: "host", name: "
|
|
202
|
+
{ kind: "host", name: "CADET_CLOUD_URL" },
|
|
203
203
|
],
|
|
204
204
|
volumeMounts: [
|
|
205
205
|
{ volumeName: "assistant-workspace", mountPath: "/workspace" },
|
|
@@ -227,7 +227,7 @@ export const DOCKER_STATEFUL_SET_SPEC: DockerStatefulSetSpec = {
|
|
|
227
227
|
{ kind: "secret", name: "ACTOR_TOKEN_SIGNING_KEY", secret: "signingKey" },
|
|
228
228
|
{ kind: "secret", name: "GUARDIAN_BOOTSTRAP_SECRET", secret: "bootstrapSecret" },
|
|
229
229
|
{ kind: "host", name: "CADET_ENVIRONMENT" },
|
|
230
|
-
{ kind: "host", name: "
|
|
230
|
+
{ kind: "host", name: "CADET_CLOUD_URL" },
|
|
231
231
|
{ kind: "host", name: "TUNNEL_BASE_URL" },
|
|
232
232
|
],
|
|
233
233
|
volumeMounts: [
|
package/src/lib/step-runner.ts
CHANGED
|
@@ -112,11 +112,16 @@ export function execWithStdin(
|
|
|
112
112
|
export function execOutput(
|
|
113
113
|
command: string,
|
|
114
114
|
args: string[],
|
|
115
|
-
options: {
|
|
115
|
+
options: {
|
|
116
|
+
cwd?: string;
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
env?: Record<string, string | undefined>;
|
|
119
|
+
} = {},
|
|
116
120
|
): Promise<string> {
|
|
117
121
|
return new Promise((resolve, reject) => {
|
|
118
122
|
const child = spawn(command, args, {
|
|
119
123
|
cwd: options.cwd,
|
|
124
|
+
env: options.env,
|
|
120
125
|
stdio: ["pipe", "pipe", "pipe"],
|
|
121
126
|
windowsHide: true,
|
|
122
127
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Sync cloud-managed (platform) assistants into the local lockfile.
|
|
3
3
|
*
|
|
4
4
|
* - Adds new platform assistants that aren't in the lockfile yet.
|
|
5
|
-
* - Removes lockfile entries whose IDs are no longer returned by the
|
|
5
|
+
* - Removes lockfile entries whose IDs are no longer returned by the cloud
|
|
6
6
|
* (e.g. retired assistants).
|
|
7
7
|
*
|
|
8
8
|
* Used by both `cadet login` and `cadet ps` to keep the lockfile fresh.
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* **Contract:** callers must verify the user is logged in (i.e. a non-empty
|
|
11
11
|
* platform token exists) before invoking this helper. The "is there a token?"
|
|
12
12
|
* decision belongs at the command level so commands can render the right
|
|
13
|
-
* "Platform: …" status without ever entering the
|
|
13
|
+
* "Platform: …" status without ever entering the cloud fetch path.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { isCloudHome } from "./cloud-values.js";
|
|
17
17
|
import {
|
|
18
18
|
loadAllAssistants,
|
|
19
19
|
removeAssistantEntry,
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
} from "./assistant-config.js";
|
|
22
22
|
import {
|
|
23
23
|
fetchCurrentUser,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "./
|
|
24
|
+
fetchCloudAssistants,
|
|
25
|
+
getCloudUrl,
|
|
26
|
+
} from "./cloud-client.js";
|
|
27
27
|
|
|
28
28
|
export type SyncLogger = (message: string) => void;
|
|
29
29
|
|
|
@@ -58,12 +58,12 @@ export async function syncCloudAssistants(
|
|
|
58
58
|
): Promise<SyncResult | null> {
|
|
59
59
|
if (!token) {
|
|
60
60
|
throw new Error(
|
|
61
|
-
"syncCloudAssistants called without a token. Callers must check `
|
|
61
|
+
"syncCloudAssistants called without a token. Callers must check `readCloudToken()` first.",
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
const log = options?.log;
|
|
65
|
-
const
|
|
66
|
-
log?.(`Platform URL: ${
|
|
65
|
+
const cloudUrl = getCloudUrl();
|
|
66
|
+
log?.(`Platform URL: ${cloudUrl}`);
|
|
67
67
|
log?.(`Token found (${token.length} chars, prefix: ${token.slice(0, 6)}…)`);
|
|
68
68
|
|
|
69
69
|
// Fetch user info for the login status line
|
|
@@ -83,12 +83,12 @@ export async function syncCloudAssistants(
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
let
|
|
86
|
+
let cloudAssistants: { id: string; name: string; status: string }[];
|
|
87
87
|
try {
|
|
88
88
|
log?.("Fetching Cadet Cloud assistants…");
|
|
89
|
-
|
|
89
|
+
cloudAssistants = await fetchCloudAssistants(token);
|
|
90
90
|
log?.(
|
|
91
|
-
`Cadet Cloud returned ${
|
|
91
|
+
`Cadet Cloud returned ${cloudAssistants.length} ${cloudAssistants.length === 1 ? "assistant" : "assistants"}: ${cloudAssistants.map((a) => a.name || a.id).join(", ") || "(none)"}`,
|
|
92
92
|
);
|
|
93
93
|
} catch (err) {
|
|
94
94
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -101,29 +101,29 @@ export async function syncCloudAssistants(
|
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
if (
|
|
104
|
+
if (cloudAssistants.length === 0) {
|
|
105
105
|
log?.(
|
|
106
106
|
"Cadet Cloud returned 0 assistants — this may mean the API returned a non-ok status (check token validity)",
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
const platformIds = new Set(
|
|
110
|
+
const platformIds = new Set(cloudAssistants.map((a) => a.id));
|
|
111
111
|
|
|
112
112
|
// Add new platform assistants not yet in the lockfile
|
|
113
113
|
const existingCloudEntries = loadAllAssistants().filter((a) =>
|
|
114
|
-
|
|
114
|
+
isCloudHome(a.cloud),
|
|
115
115
|
);
|
|
116
116
|
const existingCloudById = new Map(
|
|
117
117
|
existingCloudEntries.map((a) => [a.assistantId, a]),
|
|
118
118
|
);
|
|
119
119
|
const existingCloudIds = new Set(existingCloudById.keys());
|
|
120
120
|
log?.(
|
|
121
|
-
`Lockfile has ${existingCloudIds.size} cloud assistant
|
|
121
|
+
`Lockfile has ${existingCloudIds.size} ${existingCloudIds.size === 1 ? "cloud assistant" : "cloud assistants"}: ${[...existingCloudIds].join(", ") || "(none)"}`,
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
let added = 0;
|
|
125
125
|
let updated = 0;
|
|
126
|
-
for (const pa of
|
|
126
|
+
for (const pa of cloudAssistants) {
|
|
127
127
|
const existing = existingCloudById.get(pa.id);
|
|
128
128
|
const assistantName = pa.name.trim();
|
|
129
129
|
const nameFields = assistantName ? { name: assistantName } : {};
|
|
@@ -132,7 +132,7 @@ export async function syncCloudAssistants(
|
|
|
132
132
|
saveAssistantEntry({
|
|
133
133
|
assistantId: pa.id,
|
|
134
134
|
...nameFields,
|
|
135
|
-
runtimeUrl:
|
|
135
|
+
runtimeUrl: getCloudUrl(),
|
|
136
136
|
cloud: "cloud",
|
|
137
137
|
hatchedAt: new Date().toISOString(),
|
|
138
138
|
});
|
|
@@ -147,7 +147,7 @@ export async function syncCloudAssistants(
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// Remove stale lockfile entries that the
|
|
150
|
+
// Remove stale lockfile entries that the cloud no longer knows about
|
|
151
151
|
let removed = 0;
|
|
152
152
|
for (const id of existingCloudIds) {
|
|
153
153
|
if (!platformIds.has(id)) {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
|
|
24
24
|
import type { AssistantEntry } from "./assistant-config.js";
|
|
25
25
|
import { loopbackSafeFetch } from "./loopback-fetch.js";
|
|
26
|
-
import { authHeaders, invalidateOrgIdCache } from "./
|
|
26
|
+
import { authHeaders, invalidateOrgIdCache } from "./cloud-client.js";
|
|
27
27
|
import { z } from "zod";
|
|
28
28
|
|
|
29
29
|
const ManagedBackupEntrySchema = z.object({
|
|
@@ -53,7 +53,7 @@ async function errorSuffix(response: Response): Promise<string> {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Send a
|
|
56
|
+
* Send a cloud backup request with user-session auth, refreshing the
|
|
57
57
|
* cached organization id once on 401 (the same recovery the runtime identity
|
|
58
58
|
* probe applies).
|
|
59
59
|
*/
|
|
@@ -93,7 +93,7 @@ async function fetchPlatformBackups(
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
* `created_at` of every restorable PVC snapshot of a
|
|
96
|
+
* `created_at` of every restorable PVC snapshot of a cloud assistant:
|
|
97
97
|
* `GET /v1/assistants/<id>/backups/`.
|
|
98
98
|
*/
|
|
99
99
|
export async function listPlatformBackups(
|
|
@@ -109,7 +109,7 @@ export interface CreatePlatformBackupOptions {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* Take a PVC snapshot of a
|
|
112
|
+
* Take a PVC snapshot of a cloud assistant and wait until it is
|
|
113
113
|
* restorable: `POST /v1/assistants/<id>/backups/`, then re-list until the
|
|
114
114
|
* returned `snapshot_name` reports `ready_to_use`. Throws if the POST fails
|
|
115
115
|
* or the snapshot is not ready within `timeoutMs`.
|