@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/hatch-local.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
setActiveAssistant,
|
|
22
22
|
} from "./assistant-config.js";
|
|
23
23
|
import type { AssistantEntry } from "./assistant-config.js";
|
|
24
|
+
import { green, yellow } from "./color.js";
|
|
24
25
|
import { buildHatchConfigValues, writeInitialConfig } from "./config-utils.js";
|
|
25
26
|
import {
|
|
26
27
|
generateLocalSigningKey,
|
|
@@ -32,7 +33,10 @@ import {
|
|
|
32
33
|
} from "./local.js";
|
|
33
34
|
|
|
34
35
|
import { generateInstanceName } from "./random-name.js";
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
GuardianLeasePermanentError,
|
|
38
|
+
leaseGuardianToken,
|
|
39
|
+
} from "./guardian-token.js";
|
|
36
40
|
import { archiveLogFile, resetLogFile } from "./xdg-log.js";
|
|
37
41
|
import {
|
|
38
42
|
consoleLifecycleReporter,
|
|
@@ -54,7 +58,7 @@ import {
|
|
|
54
58
|
ensureCloudLoginInteractive,
|
|
55
59
|
registerLocalAssistantWithPlatform,
|
|
56
60
|
} from "./cloud-login.js";
|
|
57
|
-
import { fetchCurrentUser,
|
|
61
|
+
import { fetchCurrentUser, readCloudToken } from "./cloud-client.js";
|
|
58
62
|
import { withLocalHatchLock } from "./local-hatch-lock.js";
|
|
59
63
|
|
|
60
64
|
/**
|
|
@@ -185,8 +189,6 @@ export interface HatchLocalResult {
|
|
|
185
189
|
guardianAccessToken?: string;
|
|
186
190
|
}
|
|
187
191
|
|
|
188
|
-
|
|
189
|
-
|
|
190
192
|
export async function hatchLocal(
|
|
191
193
|
params: HatchLocalParams = {},
|
|
192
194
|
): Promise<HatchLocalResult> {
|
|
@@ -199,8 +201,7 @@ export async function hatchLocal(
|
|
|
199
201
|
} = params;
|
|
200
202
|
const reporter = params.reporter ?? consoleLifecycleReporter;
|
|
201
203
|
const verbose = params.verbose ?? false;
|
|
202
|
-
const setupProviderCredentials =
|
|
203
|
-
params.setupProviderCredentials !== false;
|
|
204
|
+
const setupProviderCredentials = params.setupProviderCredentials !== false;
|
|
204
205
|
setStartupLogging(verbose);
|
|
205
206
|
let provider = setupProviderCredentials
|
|
206
207
|
? resolveHatchProvider(configValues)
|
|
@@ -226,12 +227,7 @@ export async function hatchLocal(
|
|
|
226
227
|
|
|
227
228
|
const resources = await allocateLocalResources(instanceName);
|
|
228
229
|
|
|
229
|
-
const logsDir = join(
|
|
230
|
-
resources.instanceDir,
|
|
231
|
-
"workspace",
|
|
232
|
-
"data",
|
|
233
|
-
"logs",
|
|
234
|
-
);
|
|
230
|
+
const logsDir = join(resources.instanceDir, "workspace", "data", "logs");
|
|
235
231
|
archiveLogFile("hatch.log", logsDir);
|
|
236
232
|
resetLogFile("hatch.log");
|
|
237
233
|
|
|
@@ -263,7 +259,7 @@ export async function hatchLocal(
|
|
|
263
259
|
picked === "cadet" &&
|
|
264
260
|
!process.env.CADET_DESKTOP_APP &&
|
|
265
261
|
process.stdin.isTTY &&
|
|
266
|
-
!
|
|
262
|
+
!readCloudToken()
|
|
267
263
|
) {
|
|
268
264
|
reporter.log("");
|
|
269
265
|
reporter.log(
|
|
@@ -276,9 +272,7 @@ export async function hatchLocal(
|
|
|
276
272
|
reporter.warn(
|
|
277
273
|
`⚠️ Cadet Cloud login failed: ${describeLoginError(error)}`,
|
|
278
274
|
);
|
|
279
|
-
reporter.warn(
|
|
280
|
-
" You can retry later with `cadet login`.",
|
|
281
|
-
);
|
|
275
|
+
reporter.warn(" You can retry later with `cadet login`.");
|
|
282
276
|
}
|
|
283
277
|
}
|
|
284
278
|
}
|
|
@@ -380,17 +374,24 @@ export async function hatchLocal(
|
|
|
380
374
|
guardianAccessToken = tokenData.accessToken;
|
|
381
375
|
break;
|
|
382
376
|
} catch (err) {
|
|
383
|
-
if (
|
|
377
|
+
if (
|
|
378
|
+
attempt < maxLeaseAttempts &&
|
|
379
|
+
!(err instanceof GuardianLeasePermanentError)
|
|
380
|
+
) {
|
|
384
381
|
const delayMs = 2000 * 2 ** (attempt - 1);
|
|
385
382
|
reporter.error(
|
|
386
|
-
|
|
383
|
+
yellow(
|
|
384
|
+
`⚠️ Guardian token lease attempt ${attempt}/${maxLeaseAttempts} failed — retrying in ${delayMs / 1000}s: ${err}`,
|
|
385
|
+
),
|
|
387
386
|
);
|
|
388
387
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
389
388
|
} else {
|
|
390
389
|
reporter.error(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
yellow(
|
|
391
|
+
`⚠️ Guardian token lease failed${err instanceof GuardianLeasePermanentError ? ", not retrying — a malformed 200 will not fix itself" : ` after ${maxLeaseAttempts} attempts`}: ${err}\n` +
|
|
392
|
+
` The assistant is running but guardian-token.json was not written.\n` +
|
|
393
|
+
` Run \`cadet restart ${JSON.stringify(instanceName)} --repair-guardian\` to re-lease a token.`,
|
|
394
|
+
),
|
|
394
395
|
);
|
|
395
396
|
}
|
|
396
397
|
}
|
|
@@ -402,8 +403,10 @@ export async function hatchLocal(
|
|
|
402
403
|
|
|
403
404
|
if (provider !== undefined && provider !== null && !guardianAccessToken) {
|
|
404
405
|
reporter.error(
|
|
405
|
-
|
|
406
|
-
|
|
406
|
+
yellow(
|
|
407
|
+
`⚠️ Provider credential setup skipped because the guardian token was not leased.\n` +
|
|
408
|
+
` The assistant is still hatched. Run \`cadet setup --provider ${provider}\` after fixing the connection.`,
|
|
409
|
+
),
|
|
407
410
|
);
|
|
408
411
|
} else if (provider !== undefined) {
|
|
409
412
|
await configureHatchProviderApiKey({
|
|
@@ -415,13 +418,13 @@ export async function hatchLocal(
|
|
|
415
418
|
});
|
|
416
419
|
}
|
|
417
420
|
|
|
418
|
-
// Picking Cadet Cloud ends with the assistant registered on the
|
|
421
|
+
// Picking Cadet Cloud ends with the assistant registered on the cloud
|
|
419
422
|
// and its cloud credentials injected — the same post-login flow
|
|
420
|
-
// `cadet login` runs — so messages route through the
|
|
423
|
+
// `cadet login` runs — so messages route through the cloud connection
|
|
421
424
|
// immediately after hatch. Non-fatal: the assistant is up either way.
|
|
422
|
-
if (provider === "cadet" &&
|
|
425
|
+
if (provider === "cadet" && readCloudToken()) {
|
|
423
426
|
try {
|
|
424
|
-
const token =
|
|
427
|
+
const token = readCloudToken()!;
|
|
425
428
|
const user = await fetchCurrentUser(token);
|
|
426
429
|
await registerLocalAssistantWithPlatform(token, user, {
|
|
427
430
|
bearerToken: guardianAccessToken,
|
|
@@ -433,7 +436,7 @@ export async function hatchLocal(
|
|
|
433
436
|
}
|
|
434
437
|
|
|
435
438
|
reporter.log("");
|
|
436
|
-
reporter.log(`✅ Local assistant hatched
|
|
439
|
+
reporter.log(green(`✅ Local assistant hatched!`, process.stdout));
|
|
437
440
|
printInstanceDetails(
|
|
438
441
|
{
|
|
439
442
|
name: instanceName,
|
|
@@ -442,7 +445,7 @@ export async function hatchLocal(
|
|
|
442
445
|
},
|
|
443
446
|
(message) => reporter.log(message),
|
|
444
447
|
);
|
|
445
|
-
logHatchNextSteps((message) => reporter.log(message)
|
|
448
|
+
logHatchNextSteps((message) => reporter.log(message));
|
|
446
449
|
|
|
447
450
|
const result: HatchLocalResult = {
|
|
448
451
|
assistantId: instanceName,
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
export function logHatchNextSteps(
|
|
2
|
-
log: (message: string) => void,
|
|
3
|
-
verbose = false,
|
|
4
|
-
): void {
|
|
1
|
+
export function logHatchNextSteps(log: (message: string) => void): void {
|
|
5
2
|
log("Next steps:");
|
|
6
|
-
log(" cadet client");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
3
|
+
log(" - Run `cadet client` to interact with your cadet via the terminal");
|
|
4
|
+
log(
|
|
5
|
+
" - Run `cadet tunnel` to make your cadet publicly accessible and interact with it via the browser",
|
|
6
|
+
);
|
|
11
7
|
log("");
|
|
12
8
|
}
|
package/src/lib/health-check.ts
CHANGED
|
@@ -19,8 +19,8 @@ export async function checkManagedHealth(
|
|
|
19
19
|
runtimeUrl: string,
|
|
20
20
|
assistantId: string,
|
|
21
21
|
): Promise<HealthCheckResult> {
|
|
22
|
-
const {
|
|
23
|
-
const token =
|
|
22
|
+
const { readCloudToken } = await import("./cloud-client.js");
|
|
23
|
+
const token = readCloudToken();
|
|
24
24
|
if (!token) {
|
|
25
25
|
return {
|
|
26
26
|
status: "error (auth)",
|
|
@@ -30,7 +30,7 @@ export async function checkManagedHealth(
|
|
|
30
30
|
|
|
31
31
|
let headers: Record<string, string>;
|
|
32
32
|
try {
|
|
33
|
-
const { authHeaders } = await import("./
|
|
33
|
+
const { authHeaders } = await import("./cloud-client.js");
|
|
34
34
|
headers = await authHeaders(token, runtimeUrl);
|
|
35
35
|
} catch (err) {
|
|
36
36
|
return {
|
|
@@ -130,9 +130,9 @@ export async function fetchManagedPs(
|
|
|
130
130
|
runtimeUrl: string,
|
|
131
131
|
assistantId: string,
|
|
132
132
|
): Promise<ManagedPsResponse | null> {
|
|
133
|
-
const {
|
|
134
|
-
await import("./
|
|
135
|
-
const token =
|
|
133
|
+
const { readCloudToken, authHeaders } =
|
|
134
|
+
await import("./cloud-client.js");
|
|
135
|
+
const token = readCloudToken();
|
|
136
136
|
if (!token) return null;
|
|
137
137
|
|
|
138
138
|
let headers: Record<string, string>;
|
|
@@ -18,14 +18,13 @@ import { getStateDir } from "./environments/paths.js";
|
|
|
18
18
|
import { getCurrentEnvironment } from "./environments/resolve.js";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Shared workspace-config helpers for tunnel providers
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* no-`.cadet/`-reads boundary in cli/AGENTS.md.
|
|
21
|
+
* Shared workspace-config helpers for tunnel providers and the nginx ingress
|
|
22
|
+
* proxy. Each provider fronts the local edge and records the resulting public
|
|
23
|
+
* URL under `ingress.publicBaseUrl` so webhook integrations can reach the
|
|
24
|
+
* assistant. The workspace config is the gateway-facing contract; when an
|
|
25
|
+
* `assistantId` is supplied, the URL is also mirrored onto the lockfile entry
|
|
26
|
+
* (`ingressUrl`) — the CLI-owned contract that CLI features (e.g. remote-web
|
|
27
|
+
* pairing defaults) read, per the no-`.cadet/`-reads boundary in cli/AGENTS.md.
|
|
29
28
|
*/
|
|
30
29
|
|
|
31
30
|
function parsePortFromUrl(url: unknown): number | undefined {
|
|
@@ -48,7 +47,7 @@ export function isLocalContainerEntry(entry: AssistantEntry): boolean {
|
|
|
48
47
|
/**
|
|
49
48
|
* Derive the gateway port from an entry's recorded URLs, preferring the
|
|
50
49
|
* loopback `localUrl` over `runtimeUrl`. Undefined when neither carries an
|
|
51
|
-
* explicit port (e.g. a
|
|
50
|
+
* explicit port (e.g. a cloud-hosted https runtime URL).
|
|
52
51
|
*/
|
|
53
52
|
export function parseGatewayPortFromEntryUrls(
|
|
54
53
|
entry: AssistantEntry | undefined,
|
|
@@ -186,32 +185,6 @@ export function savePairingTunnel(
|
|
|
186
185
|
saveRawConfig(workspaceDir, config);
|
|
187
186
|
}
|
|
188
187
|
|
|
189
|
-
/** Persist a reserved ngrok domain under `ingress.ngrok.domain`; null clears it. */
|
|
190
|
-
export function saveNgrokDomain(
|
|
191
|
-
workspaceDir: string,
|
|
192
|
-
domain: string | null,
|
|
193
|
-
): void {
|
|
194
|
-
const config = loadRawConfig(workspaceDir);
|
|
195
|
-
const ingress = (config.ingress ?? {}) as Record<string, unknown>;
|
|
196
|
-
if (domain) {
|
|
197
|
-
ingress.ngrok = { domain };
|
|
198
|
-
} else {
|
|
199
|
-
delete ingress.ngrok;
|
|
200
|
-
}
|
|
201
|
-
config.ingress = ingress;
|
|
202
|
-
saveRawConfig(workspaceDir, config);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** Read the reserved ngrok domain from the workspace config, if saved. */
|
|
206
|
-
export function loadNgrokDomain(workspaceDir: string): string | null {
|
|
207
|
-
const ingress = loadRawConfig(workspaceDir).ingress as
|
|
208
|
-
| Record<string, unknown>
|
|
209
|
-
| undefined;
|
|
210
|
-
const ngrok = ingress?.ngrok as Record<string, unknown> | undefined;
|
|
211
|
-
const domain = ngrok?.domain;
|
|
212
|
-
return typeof domain === "string" && domain.trim() ? domain : null;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
188
|
/** Clear the ingress public base URL from the workspace config. */
|
|
216
189
|
export function clearIngressUrl(
|
|
217
190
|
workspaceDir: string,
|
package/src/lib/job-polling.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UnifiedJobStatus } from "./
|
|
1
|
+
import type { UnifiedJobStatus } from "./cloud-client.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Terminal status returned by {@link pollJobUntilDone}. Callers decide
|
|
@@ -166,7 +166,7 @@ export async function pollJobUntilDone(
|
|
|
166
166
|
throw err;
|
|
167
167
|
}
|
|
168
168
|
const msg = err instanceof Error ? err.message : String(err);
|
|
169
|
-
console.warn(`${options.label} polling failed
|
|
169
|
+
console.warn(`${options.label} polling failed: ${msg}. Retrying...`);
|
|
170
170
|
if (Date.now() >= deadline) {
|
|
171
171
|
throw new Error(
|
|
172
172
|
`Timed out waiting for ${options.label} after ${Math.round(
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* passes by construction, and there is nothing new to provision here.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { isCloudHome } from "../cloud-values.js";
|
|
29
29
|
import {
|
|
30
30
|
formatAssistantLookupError,
|
|
31
31
|
formatAssistantReference,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
resolveTargetAssistant,
|
|
34
34
|
type AssistantEntry,
|
|
35
35
|
} from "../assistant-config.js";
|
|
36
|
-
import {
|
|
36
|
+
import { getCloudUrl, readCloudToken } from "../cloud-client.js";
|
|
37
37
|
import {
|
|
38
38
|
guardianTokenDueForRenewal,
|
|
39
39
|
loadGuardianToken,
|
|
@@ -88,11 +88,11 @@ export async function resolveLiveVoiceConnection(
|
|
|
88
88
|
? resolveNamedAssistant(assistantIdArg)
|
|
89
89
|
: resolveTargetAssistant();
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// Cloud assistants are reached the other way round. Their gateway
|
|
92
92
|
// is behind tunnel, which authenticates nobody and forwards an attestation
|
|
93
93
|
// the gateway trusts, so the credential is not the guardian JWT below but a
|
|
94
|
-
// WS token minted from the
|
|
95
|
-
if (
|
|
94
|
+
// WS token minted from the cloud session the CLI already holds.
|
|
95
|
+
if (isCloudHome(entry.cloud)) {
|
|
96
96
|
return await resolveCloudConnection(entry);
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -126,7 +126,7 @@ export async function resolveLiveVoiceConnection(
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* Resolve a live-voice connection to a
|
|
129
|
+
* Resolve a live-voice connection to a cloud-hosted assistant.
|
|
130
130
|
*
|
|
131
131
|
* The session token is the CLI's own cloud login, not anything specific to
|
|
132
132
|
* this assistant, so a missing one is a `cadet login` problem rather than an
|
|
@@ -139,7 +139,7 @@ export async function resolveLiveVoiceConnection(
|
|
|
139
139
|
async function resolveCloudConnection(
|
|
140
140
|
entry: AssistantEntry,
|
|
141
141
|
): Promise<LiveVoiceConnection> {
|
|
142
|
-
const sessionToken =
|
|
142
|
+
const sessionToken = readCloudToken();
|
|
143
143
|
if (!sessionToken) {
|
|
144
144
|
throw new LiveVoiceConnectionError(
|
|
145
145
|
`${formatAssistantReference(entry)} is a Cadet cloud assistant, which ` +
|
|
@@ -147,10 +147,10 @@ async function resolveCloudConnection(
|
|
|
147
147
|
);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// `runtimeUrl` is the
|
|
151
|
-
// one that can mint for it. `
|
|
150
|
+
// `runtimeUrl` is the cloud this entry was hatched against, which is the
|
|
151
|
+
// one that can mint for it. `getCloudUrl()` is the fallback for an entry
|
|
152
152
|
// written before that field was recorded.
|
|
153
|
-
const
|
|
153
|
+
const cloudUrl = (entry.runtimeUrl || getCloudUrl()).replace(
|
|
154
154
|
/\/+$/,
|
|
155
155
|
"",
|
|
156
156
|
);
|
|
@@ -158,7 +158,7 @@ async function resolveCloudConnection(
|
|
|
158
158
|
let token: string;
|
|
159
159
|
try {
|
|
160
160
|
token = await mintTunnelWsToken({
|
|
161
|
-
|
|
161
|
+
cloudUrl,
|
|
162
162
|
assistantId: entry.assistantId,
|
|
163
163
|
sessionToken,
|
|
164
164
|
});
|
|
@@ -171,7 +171,7 @@ async function resolveCloudConnection(
|
|
|
171
171
|
|
|
172
172
|
return {
|
|
173
173
|
url: buildTunnelLiveVoiceUrl({
|
|
174
|
-
|
|
174
|
+
cloudUrl,
|
|
175
175
|
assistantId: entry.assistantId,
|
|
176
176
|
token,
|
|
177
177
|
}),
|
|
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
buildTunnelLiveVoiceUrl,
|
|
5
|
-
|
|
5
|
+
tunnelHostForCloudUrl,
|
|
6
6
|
tunnelWsScheme,
|
|
7
7
|
} from "./tunnel.js";
|
|
8
8
|
|
|
@@ -16,13 +16,13 @@ afterEach(() => {
|
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
describe("
|
|
20
|
-
test("maps a
|
|
19
|
+
describe("tunnelHostForCloudUrl", () => {
|
|
20
|
+
test("maps a cloud host onto its environment's tunnel", () => {
|
|
21
21
|
delete process.env.CADET_TUNNEL_HOST;
|
|
22
|
-
expect(
|
|
22
|
+
expect(tunnelHostForCloudUrl("https://cloud.mycadet.ai")).toBe(
|
|
23
23
|
"tunnel.mycadet.ai",
|
|
24
24
|
);
|
|
25
|
-
expect(
|
|
25
|
+
expect(tunnelHostForCloudUrl("https://dev-cloud.mycadet.ai")).toBe(
|
|
26
26
|
"tunnel-dev.mycadet.ai",
|
|
27
27
|
);
|
|
28
28
|
});
|
|
@@ -31,15 +31,15 @@ describe("tunnelHostForPlatformUrl", () => {
|
|
|
31
31
|
delete process.env.CADET_TUNNEL_HOST;
|
|
32
32
|
// A local platform predicts no tunnel of its own; guessing one would be
|
|
33
33
|
// worse than dialing the host that certainly exists.
|
|
34
|
-
expect(
|
|
34
|
+
expect(tunnelHostForCloudUrl("http://localhost:8000")).toBe(
|
|
35
35
|
"tunnel.mycadet.ai",
|
|
36
36
|
);
|
|
37
|
-
expect(
|
|
37
|
+
expect(tunnelHostForCloudUrl("not a url")).toBe("tunnel.mycadet.ai");
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
test("the env override wins, which is how a local vel up tunnel is reached", () => {
|
|
41
41
|
process.env.CADET_TUNNEL_HOST = "localhost:8501";
|
|
42
|
-
expect(
|
|
42
|
+
expect(tunnelHostForCloudUrl("https://cloud.mycadet.ai")).toBe(
|
|
43
43
|
"localhost:8501",
|
|
44
44
|
);
|
|
45
45
|
});
|
|
@@ -58,7 +58,7 @@ describe("buildTunnelLiveVoiceUrl", () => {
|
|
|
58
58
|
delete process.env.CADET_TUNNEL_HOST;
|
|
59
59
|
const url = new URL(
|
|
60
60
|
buildTunnelLiveVoiceUrl({
|
|
61
|
-
|
|
61
|
+
cloudUrl: "https://cloud.mycadet.ai",
|
|
62
62
|
assistantId: "01a02514-e452-7019-88d5-30828f42cf17",
|
|
63
63
|
token: "tok_abc",
|
|
64
64
|
}),
|
|
@@ -76,7 +76,7 @@ describe("buildTunnelLiveVoiceUrl", () => {
|
|
|
76
76
|
delete process.env.CADET_TUNNEL_HOST;
|
|
77
77
|
const url = new URL(
|
|
78
78
|
buildTunnelLiveVoiceUrl({
|
|
79
|
-
|
|
79
|
+
cloudUrl: "https://cloud.mycadet.ai",
|
|
80
80
|
assistantId: "a1",
|
|
81
81
|
token: "a+b/c=&d",
|
|
82
82
|
}),
|
|
@@ -88,7 +88,7 @@ describe("buildTunnelLiveVoiceUrl", () => {
|
|
|
88
88
|
process.env.CADET_TUNNEL_HOST = "localhost:8501";
|
|
89
89
|
const url = new URL(
|
|
90
90
|
buildTunnelLiveVoiceUrl({
|
|
91
|
-
|
|
91
|
+
cloudUrl: "http://localhost:8000",
|
|
92
92
|
assistantId: "a1",
|
|
93
93
|
token: "t",
|
|
94
94
|
}),
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
* The cloud leg of a live-voice session: minting a tunnel WS token and building
|
|
3
3
|
* the URL it opens.
|
|
4
4
|
*
|
|
5
|
-
* A
|
|
5
|
+
* A cloud-hosted assistant is not reachable the way a local one is. Its
|
|
6
6
|
* gateway sits behind tunnel, tunnel performs no user authentication of its own,
|
|
7
7
|
* and the gateway upstream accepts only an actor edge JWT or a tunnel
|
|
8
8
|
* attestation. The CLI holds neither for a cloud instance: what it has is a
|
|
9
9
|
* platform session token, which is the wrong credential for the socket but the
|
|
10
|
-
* right one for the
|
|
10
|
+
* right one for the cloud.
|
|
11
11
|
*
|
|
12
12
|
* So the connection is made in two hops, the same two the web voice room makes
|
|
13
13
|
* (`clients/web/src/domains/chat/voice/live-voice/connection.ts`):
|
|
14
14
|
*
|
|
15
|
-
* 1. POST the
|
|
15
|
+
* 1. POST the cloud for a short-lived, org+assistant-scoped WS token.
|
|
16
16
|
* 2. Open `wss://<tunnel>/<assistantId>/v1/live-voice?token=<wsToken>`.
|
|
17
17
|
*
|
|
18
18
|
* tunnel consumes the token on the upgrade and injects the authenticated user
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
import { tunnelHostForPlatformHost } from "@mycadet/service-contracts/ingress";
|
|
32
32
|
|
|
33
|
-
import { authHeaders } from "../
|
|
33
|
+
import { authHeaders } from "../cloud-client.js";
|
|
34
34
|
|
|
35
|
-
/** Production tunnel, when the
|
|
35
|
+
/** Production tunnel, when the cloud host implies nothing more specific. */
|
|
36
36
|
const DEFAULT_TUNNEL_HOST = "tunnel.mycadet.ai";
|
|
37
37
|
|
|
38
38
|
/** Give up on the mint rather than hang the session behind a stalled platform. */
|
|
@@ -53,20 +53,20 @@ export class TunnelWsTokenError extends Error {
|
|
|
53
53
|
*
|
|
54
54
|
* `CADET_TUNNEL_HOST` wins when set, which is how a local `vel up` stack is
|
|
55
55
|
* reached: its tunnel is a loopback port that no naming convention predicts.
|
|
56
|
-
* Otherwise the deployment convention maps the
|
|
56
|
+
* Otherwise the deployment convention maps the cloud host onto its sibling
|
|
57
57
|
* (`cloud.mycadet.ai` to `tunnel.mycadet.ai`, `dev-cloud.mycadet.ai` to
|
|
58
58
|
* `tunnel-dev.mycadet.ai`). A host outside that convention, including localhost,
|
|
59
59
|
* yields the production default, because guessing a tunnel for it would be
|
|
60
60
|
* worse than dialing the one host that certainly exists.
|
|
61
61
|
*/
|
|
62
|
-
export function
|
|
62
|
+
export function tunnelHostForCloudUrl(cloudUrl: string): string {
|
|
63
63
|
const override = process.env.CADET_TUNNEL_HOST?.trim();
|
|
64
64
|
if (override) {
|
|
65
65
|
return override;
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
68
68
|
return (
|
|
69
|
-
tunnelHostForPlatformHost(new URL(
|
|
69
|
+
tunnelHostForPlatformHost(new URL(cloudUrl).host) ??
|
|
70
70
|
DEFAULT_TUNNEL_HOST
|
|
71
71
|
);
|
|
72
72
|
} catch {
|
|
@@ -106,11 +106,11 @@ export function tunnelWsScheme(host: string): "ws" | "wss" {
|
|
|
106
106
|
* credential anywhere tunnel reads except the query string.
|
|
107
107
|
*/
|
|
108
108
|
export function buildTunnelLiveVoiceUrl(args: {
|
|
109
|
-
|
|
109
|
+
cloudUrl: string;
|
|
110
110
|
assistantId: string;
|
|
111
111
|
token: string;
|
|
112
112
|
}): string {
|
|
113
|
-
const host =
|
|
113
|
+
const host = tunnelHostForCloudUrl(args.cloudUrl);
|
|
114
114
|
const url = new URL(
|
|
115
115
|
`${tunnelWsScheme(host)}://${host}/${args.assistantId}/v1/live-voice`,
|
|
116
116
|
);
|
|
@@ -131,22 +131,22 @@ interface LiveVoiceTokenResponse {
|
|
|
131
131
|
* (`SessionAuthentication` and `XSessionTokenAuthentication`) and deliberately
|
|
132
132
|
* excludes API-key auth, so `X-Session-Token` is the CLI's way in and a
|
|
133
133
|
* `vak_` key is not: a caller holding only an API key cannot mint here, by
|
|
134
|
-
* design on the
|
|
134
|
+
* design on the cloud's side.
|
|
135
135
|
*
|
|
136
136
|
* Throws {@link TunnelWsTokenError} carrying the HTTP status, so the caller can
|
|
137
|
-
* tell "sign in again" (401) from "not your assistant" (403) from a
|
|
137
|
+
* tell "sign in again" (401) from "not your assistant" (403) from a cloud
|
|
138
138
|
* that is simply down.
|
|
139
139
|
*/
|
|
140
140
|
export async function mintTunnelWsToken(args: {
|
|
141
|
-
|
|
141
|
+
cloudUrl: string;
|
|
142
142
|
assistantId: string;
|
|
143
143
|
sessionToken: string;
|
|
144
144
|
}): Promise<string> {
|
|
145
|
-
const url = `${args.
|
|
145
|
+
const url = `${args.cloudUrl.replace(/\/+$/, "")}/v1/auth/live-voice-token/`;
|
|
146
146
|
// `authHeaders` resolves `Cadet-Organization-Id`, which the endpoint needs
|
|
147
147
|
// to scope the token, and which is cached per token so a session does not
|
|
148
148
|
// pay for an extra round trip.
|
|
149
|
-
const headers = await authHeaders(args.sessionToken, args.
|
|
149
|
+
const headers = await authHeaders(args.sessionToken, args.cloudUrl);
|
|
150
150
|
|
|
151
151
|
let response: Response;
|
|
152
152
|
try {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCloudHome } from "./cloud-values.js";
|
|
2
2
|
import type { AssistantEntry } from "./assistant-config.js";
|
|
3
3
|
import {
|
|
4
4
|
authHeaders,
|
|
5
5
|
invalidateOrgIdCache,
|
|
6
6
|
parseUnifiedJobStatus,
|
|
7
7
|
type UnifiedJobStatus,
|
|
8
|
-
} from "./
|
|
8
|
+
} from "./cloud-client.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveRuntimeMigrationUrl,
|
|
11
11
|
resolveRuntimeUrl,
|
|
@@ -49,7 +49,7 @@ function bearerHeaders(token: string): Record<string, string> {
|
|
|
49
49
|
/**
|
|
50
50
|
* Build the auth + content headers for a runtime migration request.
|
|
51
51
|
*
|
|
52
|
-
* - For `
|
|
52
|
+
* - For `isCloudHome(cloud)` we go through the cloud's wildcard runtime
|
|
53
53
|
* proxy, which authenticates user-session / vak_ tokens via DRF's default
|
|
54
54
|
* authentication classes — `authHeaders()` produces the right combination
|
|
55
55
|
* (`X-Session-Token` + `Cadet-Organization-Id`, or `Authorization: Bearer
|
|
@@ -60,7 +60,7 @@ async function migrationRequestHeaders(
|
|
|
60
60
|
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl">,
|
|
61
61
|
token: string,
|
|
62
62
|
): Promise<Record<string, string>> {
|
|
63
|
-
if (
|
|
63
|
+
if (isCloudHome(entry.cloud)) {
|
|
64
64
|
return {
|
|
65
65
|
...(await authHeaders(token, entry.runtimeUrl)),
|
|
66
66
|
Accept: "application/json",
|
|
@@ -116,10 +116,10 @@ async function throwIfInProgress(
|
|
|
116
116
|
*
|
|
117
117
|
* For local/docker assistants this POSTs to
|
|
118
118
|
* `{runtimeUrl}/v1/migrations/export-to-gcs` with guardian-token bearer
|
|
119
|
-
* auth. For
|
|
119
|
+
* auth. For cloud-managed (cloud="cadet") assistants the URL is rewritten
|
|
120
120
|
* to the wildcard-runtime-proxy shape
|
|
121
|
-
* `{
|
|
122
|
-
* authenticated via the
|
|
121
|
+
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/export-to-gcs` and
|
|
122
|
+
* authenticated via the cloud-token header set the cloud's DRF auth
|
|
123
123
|
* accepts (session / vak_).
|
|
124
124
|
*
|
|
125
125
|
* Returns the 202-accepted `job_id`. On 409 (another export in flight)
|
|
@@ -179,10 +179,10 @@ export async function localRuntimeExportToGcs(
|
|
|
179
179
|
*
|
|
180
180
|
* For local/docker assistants this POSTs to
|
|
181
181
|
* `{runtimeUrl}/v1/migrations/import-from-gcs` with guardian-token bearer
|
|
182
|
-
* auth. For
|
|
182
|
+
* auth. For cloud-managed (cloud="cadet") assistants the URL is rewritten
|
|
183
183
|
* to the wildcard-runtime-proxy shape
|
|
184
|
-
* `{
|
|
185
|
-
* authenticated via the
|
|
184
|
+
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/import-from-gcs` and
|
|
185
|
+
* authenticated via the cloud token. On 409 throws
|
|
186
186
|
* {@link MigrationInProgressError}.
|
|
187
187
|
*/
|
|
188
188
|
export async function localRuntimeImportFromGcs(
|
|
@@ -261,10 +261,10 @@ export async function localRuntimePreflightFromGcs(
|
|
|
261
261
|
*
|
|
262
262
|
* For local/docker assistants this GETs
|
|
263
263
|
* `{runtimeUrl}/v1/migrations/jobs/{jobId}` directly (guardian-token
|
|
264
|
-
* bearer). For
|
|
264
|
+
* bearer). For cloud assistants it routes through the wildcard
|
|
265
265
|
* runtime proxy at
|
|
266
|
-
* `{
|
|
267
|
-
* platform-token auth — important: the
|
|
266
|
+
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/jobs/{jobId}` with
|
|
267
|
+
* platform-token auth — important: the cloud's dedicated
|
|
268
268
|
* `/v1/migrations/jobs/{id}/` endpoint queries platform-side ImportJob
|
|
269
269
|
* records and would 404 on runtime-created job IDs.
|
|
270
270
|
*/
|
|
@@ -320,15 +320,15 @@ export interface RuntimeIdentity {
|
|
|
320
320
|
* (no filesystem reads), so it's safe to call against any running runtime.
|
|
321
321
|
*
|
|
322
322
|
* For local/docker assistants this GETs `{runtimeUrl}/v1/health` with
|
|
323
|
-
* guardian-token bearer auth. For
|
|
323
|
+
* guardian-token bearer auth. For cloud-managed (cloud="cadet")
|
|
324
324
|
* assistants the URL is rewritten to the wildcard runtime proxy shape
|
|
325
|
-
* `{
|
|
326
|
-
* the
|
|
325
|
+
* `{cloudUrl}/v1/assistants/<assistantId>/health` and authenticated via
|
|
326
|
+
* the cloud token.
|
|
327
327
|
*
|
|
328
328
|
* For the cadet target this is the FIRST network call in the
|
|
329
329
|
* teleport/backup export flow, so a stale `Cadet-Organization-Id` cache
|
|
330
330
|
* entry would surface as a hard abort before any retry-friendly call (like
|
|
331
|
-
* `
|
|
331
|
+
* `cloudRequestSignedUrl`) gets a chance to recover. Mirror that helper's
|
|
332
332
|
* one-shot 401-retry: invalidate the org-ID cache and retry once. Local /
|
|
333
333
|
* docker entries do not use the org-ID cache and are wrapped in
|
|
334
334
|
* `callRuntimeWithAuthRetry` by callers for guardian-token refresh, so the
|
|
@@ -353,10 +353,10 @@ export async function localRuntimeIdentity(
|
|
|
353
353
|
});
|
|
354
354
|
|
|
355
355
|
let response = await doRequest();
|
|
356
|
-
if (response.status === 401 &&
|
|
357
|
-
// `entry.runtimeUrl` is the
|
|
356
|
+
if (response.status === 401 && isCloudHome(entry.cloud)) {
|
|
357
|
+
// `entry.runtimeUrl` is the cloud host for cadet-cloud entries
|
|
358
358
|
// (the wildcard runtime proxy lives there). Pass it as the cache key
|
|
359
|
-
//
|
|
359
|
+
// cloudUrl so we invalidate the same entry that authHeaders cached.
|
|
360
360
|
invalidateOrgIdCache(token, entry.runtimeUrl);
|
|
361
361
|
response = await doRequest();
|
|
362
362
|
}
|