@mycadet/cli 0.12.11 → 0.13.0
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 +13 -0
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/package.json +3 -2
- package/src/__tests__/assistant-client-cloud-auth.test.ts +2 -2
- package/src/__tests__/backup-ops-matching.test.ts +15 -13
- package/src/__tests__/backup-ops.test.ts +105 -0
- package/src/__tests__/backup.test.ts +114 -673
- package/src/__tests__/cloud-releases.test.ts +82 -0
- package/src/__tests__/commands-mock.ts +101 -0
- package/src/__tests__/debug.test.ts +17 -91
- package/src/__tests__/devices.test.ts +2 -2
- package/src/__tests__/gateway-token.test.ts +267 -0
- package/src/__tests__/host-teleport-docker.test.ts +371 -0
- package/src/__tests__/host-teleport.test.ts +405 -0
- package/src/__tests__/nginx-ingress.test.ts +182 -23
- package/src/__tests__/orphan-detection.test.ts +1 -1
- package/src/__tests__/paired-lifecycle.test.ts +2 -2
- package/src/__tests__/ps-cloud-status.test.ts +6 -6
- package/src/__tests__/root-backup-docker.test.ts +294 -0
- package/src/__tests__/root-backup.test.ts +527 -0
- package/src/__tests__/root-restore-command.test.ts +147 -0
- package/src/__tests__/root-signing-key.test.ts +50 -0
- package/src/__tests__/setup.test.ts +3 -3
- package/src/__tests__/sleep.test.ts +11 -0
- package/src/__tests__/teleport.test.ts +68 -2996
- package/src/__tests__/tunnel.test.ts +1 -1
- package/src/__tests__/upgrade-local.test.ts +21 -11
- package/src/__tests__/upgrade-replay-env.test.ts +2 -2
- package/src/__tests__/upgrade-root-recovery.test.ts +185 -0
- package/src/__tests__/windows-lifecycle.test.ts +25 -0
- package/src/commands/backup.ts +44 -399
- package/src/commands/client.ts +3 -3
- package/src/commands/completions.ts +0 -2
- package/src/commands/debug.ts +75 -131
- package/src/commands/devices.ts +51 -17
- package/src/commands/gateway/token.ts +77 -23
- package/src/commands/pair.ts +2 -2
- package/src/commands/restore.ts +77 -444
- package/src/commands/retire.ts +2 -3
- package/src/commands/sleep.ts +15 -2
- package/src/commands/teleport.ts +47 -1559
- package/src/commands/upgrade.ts +78 -156
- package/src/commands/wake.ts +1 -1
- package/src/index.ts +0 -4
- package/src/lib/__tests__/http-client-version.test.ts +29 -0
- package/src/lib/__tests__/runtime-url.test.ts +3 -75
- package/src/lib/__tests__/web-dist.test.ts +27 -1
- package/src/lib/assistant-client.ts +1 -1
- package/src/lib/assistant-config.ts +0 -18
- package/src/lib/backup-ops.ts +37 -251
- package/src/lib/cloud-client.ts +10 -13
- package/src/lib/cloud-login.ts +2 -1
- package/src/lib/cloud-releases.ts +54 -10
- package/src/lib/cloud-values.ts +1 -1
- package/src/lib/confirm-action.ts +2 -2
- package/src/lib/docker.ts +59 -22
- package/src/lib/github-repository.ts +2 -0
- package/src/lib/guardian-token.ts +1 -23
- package/src/lib/hatch-local.ts +17 -1
- package/src/lib/host-teleport.ts +506 -0
- package/src/lib/http-client.ts +25 -0
- package/src/lib/live-voice/tunnel.test.ts +1 -1
- package/src/lib/live-voice/tunnel.ts +1 -1
- package/src/lib/local.ts +4 -2
- package/src/lib/nginx-ingress.ts +134 -18
- package/src/lib/process.ts +13 -0
- package/src/lib/root-backup.ts +917 -0
- package/src/lib/root-signing-key.ts +31 -0
- package/src/lib/runtime-url.ts +0 -29
- package/src/lib/teleport-backup.ts +3 -16
- package/src/lib/upgrade-lifecycle.ts +63 -141
- package/src/lib/web-dist.ts +23 -1
- package/src/__tests__/bundle-staging.test.ts +0 -268
- package/src/__tests__/unpair.test.ts +0 -160
- package/src/commands/rollback.ts +0 -479
- package/src/commands/unpair.ts +0 -122
- package/src/lib/__tests__/job-polling.test.ts +0 -278
- package/src/lib/__tests__/local-runtime-client.test.ts +0 -666
- package/src/lib/bundle-staging.ts +0 -220
- package/src/lib/job-polling.ts +0 -195
- package/src/lib/local-runtime-client.ts +0 -374
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { isCloudHome } from "./cloud-values.js";
|
|
2
|
-
import type { AssistantEntry } from "./assistant-config.js";
|
|
3
|
-
import {
|
|
4
|
-
authHeaders,
|
|
5
|
-
invalidateOrgIdCache,
|
|
6
|
-
parseUnifiedJobStatus,
|
|
7
|
-
type UnifiedJobStatus,
|
|
8
|
-
} from "./cloud-client.js";
|
|
9
|
-
import {
|
|
10
|
-
resolveRuntimeMigrationUrl,
|
|
11
|
-
resolveRuntimeUrl,
|
|
12
|
-
} from "./runtime-url.js";
|
|
13
|
-
import { loopbackSafeFetch } from "./loopback-fetch.js";
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
|
|
16
|
-
const RuntimeIdentitySchema = z.object({ version: z.string().min(1) });
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Thrown when the local runtime returns 409 for an export/import request
|
|
20
|
-
* because another migration of the same type is already in-flight. The
|
|
21
|
-
* caller can inspect {@link existingJobId} and decide whether to poll the
|
|
22
|
-
* existing job instead of retrying.
|
|
23
|
-
*/
|
|
24
|
-
export class MigrationInProgressError extends Error {
|
|
25
|
-
readonly existingJobId: string;
|
|
26
|
-
readonly kind: "export_in_progress" | "import_in_progress";
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
29
|
-
kind: "export_in_progress" | "import_in_progress",
|
|
30
|
-
jobId: string,
|
|
31
|
-
) {
|
|
32
|
-
super(
|
|
33
|
-
`A migration is already in progress (${kind}); existing job_id=${jobId}`,
|
|
34
|
-
);
|
|
35
|
-
this.name = "MigrationInProgressError";
|
|
36
|
-
this.kind = kind;
|
|
37
|
-
this.existingJobId = jobId;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function bearerHeaders(token: string): Record<string, string> {
|
|
42
|
-
return {
|
|
43
|
-
Authorization: `Bearer ${token}`,
|
|
44
|
-
"Content-Type": "application/json",
|
|
45
|
-
Accept: "application/json",
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Build the auth + content headers for a runtime migration request.
|
|
51
|
-
*
|
|
52
|
-
* - For `isCloudHome(cloud)` we go through the cloud's wildcard runtime
|
|
53
|
-
* proxy, which authenticates user-session / vak_ tokens via DRF's default
|
|
54
|
-
* authentication classes — `authHeaders()` produces the right combination
|
|
55
|
-
* (`X-Session-Token` + `Cadet-Organization-Id`, or `Authorization: Bearer
|
|
56
|
-
* vak_...`).
|
|
57
|
-
* - For local/docker the runtime endpoint expects a guardian-token bearer.
|
|
58
|
-
*/
|
|
59
|
-
async function migrationRequestHeaders(
|
|
60
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl">,
|
|
61
|
-
token: string,
|
|
62
|
-
): Promise<Record<string, string>> {
|
|
63
|
-
if (isCloudHome(entry.cloud)) {
|
|
64
|
-
return {
|
|
65
|
-
...(await authHeaders(token, entry.runtimeUrl)),
|
|
66
|
-
Accept: "application/json",
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
return bearerHeaders(token);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const Raw409BodySchema = z.object({
|
|
73
|
-
detail: z.string().optional(),
|
|
74
|
-
// The runtime's current 409 contract nests the payload under `error`:
|
|
75
|
-
// { error: { code: "export_in_progress" | "import_in_progress", job_id } }
|
|
76
|
-
// We also tolerate a legacy flat shape ({ code, job_id }) for resilience.
|
|
77
|
-
error: z
|
|
78
|
-
.union([
|
|
79
|
-
z.string(),
|
|
80
|
-
z.object({
|
|
81
|
-
code: z.string().optional(),
|
|
82
|
-
job_id: z.string().optional(),
|
|
83
|
-
}),
|
|
84
|
-
])
|
|
85
|
-
.optional(),
|
|
86
|
-
code: z.string().optional(),
|
|
87
|
-
job_id: z.string().optional(),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
/** Common 409 → MigrationInProgressError parsing used by the two POST helpers. */
|
|
91
|
-
async function throwIfInProgress(
|
|
92
|
-
response: Response,
|
|
93
|
-
defaultKind: "export_in_progress" | "import_in_progress",
|
|
94
|
-
): Promise<void> {
|
|
95
|
-
if (response.status !== 409) return;
|
|
96
|
-
const parsed = Raw409BodySchema.safeParse(
|
|
97
|
-
await response.json().catch(() => ({})),
|
|
98
|
-
);
|
|
99
|
-
const body = parsed.success ? parsed.data : {};
|
|
100
|
-
const nested = typeof body.error === "object" ? body.error : undefined;
|
|
101
|
-
const jobId = nested?.job_id ?? body.job_id ?? "";
|
|
102
|
-
const rawKind =
|
|
103
|
-
nested?.code ??
|
|
104
|
-
body.code ??
|
|
105
|
-
(typeof body.error === "string" ? body.error : undefined) ??
|
|
106
|
-
defaultKind;
|
|
107
|
-
const kind: "export_in_progress" | "import_in_progress" =
|
|
108
|
-
rawKind === "export_in_progress" || rawKind === "import_in_progress"
|
|
109
|
-
? rawKind
|
|
110
|
-
: defaultKind;
|
|
111
|
-
throw new MigrationInProgressError(kind, jobId);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Kick off an async export-to-GCS job on the assistant's runtime.
|
|
116
|
-
*
|
|
117
|
-
* For local/docker assistants this POSTs to
|
|
118
|
-
* `{runtimeUrl}/v1/migrations/export-to-gcs` with guardian-token bearer
|
|
119
|
-
* auth. For cloud-managed (cloud="cadet") assistants the URL is rewritten
|
|
120
|
-
* to the wildcard-runtime-proxy shape
|
|
121
|
-
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/export-to-gcs` and
|
|
122
|
-
* authenticated via the cloud-token header set the cloud's DRF auth
|
|
123
|
-
* accepts (session / vak_).
|
|
124
|
-
*
|
|
125
|
-
* Returns the 202-accepted `job_id`. On 409 (another export in flight)
|
|
126
|
-
* throws {@link MigrationInProgressError} with the existing job_id.
|
|
127
|
-
*/
|
|
128
|
-
export async function localRuntimeExportToGcs(
|
|
129
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
130
|
-
token: string,
|
|
131
|
-
params: {
|
|
132
|
-
uploadUrl: string;
|
|
133
|
-
description?: string;
|
|
134
|
-
/**
|
|
135
|
-
* `"debug"` builds a bundle for Cadet staff: no credentials, plus the
|
|
136
|
-
* gateway's database and logs. Omitted means a normal teleport bundle.
|
|
137
|
-
*/
|
|
138
|
-
profile?: "migration" | "debug";
|
|
139
|
-
},
|
|
140
|
-
): Promise<{ jobId: string }> {
|
|
141
|
-
const body: Record<string, unknown> = { upload_url: params.uploadUrl };
|
|
142
|
-
if (params.description !== undefined) {
|
|
143
|
-
body.description = params.description;
|
|
144
|
-
}
|
|
145
|
-
if (params.profile !== undefined) {
|
|
146
|
-
body.profile = params.profile;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const response = await loopbackSafeFetch(
|
|
150
|
-
resolveRuntimeMigrationUrl(entry, "export-to-gcs"),
|
|
151
|
-
{
|
|
152
|
-
method: "POST",
|
|
153
|
-
headers: await migrationRequestHeaders(entry, token),
|
|
154
|
-
body: JSON.stringify(body),
|
|
155
|
-
},
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
await throwIfInProgress(response, "export_in_progress");
|
|
159
|
-
|
|
160
|
-
if (response.status !== 202) {
|
|
161
|
-
const errText = await response.text().catch(() => "");
|
|
162
|
-
throw new Error(
|
|
163
|
-
`Local runtime export-to-gcs failed (${response.status}): ${
|
|
164
|
-
errText || response.statusText
|
|
165
|
-
}`,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const json = (await response.json()) as {
|
|
170
|
-
job_id: string;
|
|
171
|
-
status?: string;
|
|
172
|
-
type?: string;
|
|
173
|
-
};
|
|
174
|
-
return { jobId: json.job_id };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Kick off an async import-from-GCS job on the assistant's runtime.
|
|
179
|
-
*
|
|
180
|
-
* For local/docker assistants this POSTs to
|
|
181
|
-
* `{runtimeUrl}/v1/migrations/import-from-gcs` with guardian-token bearer
|
|
182
|
-
* auth. For cloud-managed (cloud="cadet") assistants the URL is rewritten
|
|
183
|
-
* to the wildcard-runtime-proxy shape
|
|
184
|
-
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/import-from-gcs` and
|
|
185
|
-
* authenticated via the cloud token. On 409 throws
|
|
186
|
-
* {@link MigrationInProgressError}.
|
|
187
|
-
*/
|
|
188
|
-
export async function localRuntimeImportFromGcs(
|
|
189
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
190
|
-
token: string,
|
|
191
|
-
params: { bundleUrl: string },
|
|
192
|
-
): Promise<{ jobId: string }> {
|
|
193
|
-
const response = await loopbackSafeFetch(
|
|
194
|
-
resolveRuntimeMigrationUrl(entry, "import-from-gcs"),
|
|
195
|
-
{
|
|
196
|
-
method: "POST",
|
|
197
|
-
headers: await migrationRequestHeaders(entry, token),
|
|
198
|
-
body: JSON.stringify({ bundle_url: params.bundleUrl }),
|
|
199
|
-
},
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
await throwIfInProgress(response, "import_in_progress");
|
|
203
|
-
|
|
204
|
-
if (response.status !== 202) {
|
|
205
|
-
const errText = await response.text().catch(() => "");
|
|
206
|
-
throw new Error(
|
|
207
|
-
`Local runtime import-from-gcs failed (${response.status}): ${
|
|
208
|
-
errText || response.statusText
|
|
209
|
-
}`,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const json = (await response.json()) as {
|
|
214
|
-
job_id: string;
|
|
215
|
-
status?: string;
|
|
216
|
-
type?: string;
|
|
217
|
-
};
|
|
218
|
-
return { jobId: json.job_id };
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Run a dry-run preflight analysis on a .vbundle archive stored at a signed
|
|
223
|
-
* GCS download URL.
|
|
224
|
-
*
|
|
225
|
-
* For local/docker assistants this POSTs to
|
|
226
|
-
* `{runtimeUrl}/v1/migrations/preflight-from-gcs` with guardian-token bearer
|
|
227
|
-
* auth. The daemon fetches the bundle from GCS, validates it, and returns a
|
|
228
|
-
* report of what would change on import — without writing anything to disk.
|
|
229
|
-
*
|
|
230
|
-
* Returns the raw preflight response body. Callers should cast to their
|
|
231
|
-
* local `PreflightResponse` shape (mirrors the `/import-preflight` shape).
|
|
232
|
-
*/
|
|
233
|
-
export async function localRuntimePreflightFromGcs(
|
|
234
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
235
|
-
token: string,
|
|
236
|
-
params: { bundleUrl: string },
|
|
237
|
-
): Promise<Record<string, unknown>> {
|
|
238
|
-
const response = await fetch(
|
|
239
|
-
resolveRuntimeMigrationUrl(entry, "preflight-from-gcs"),
|
|
240
|
-
{
|
|
241
|
-
method: "POST",
|
|
242
|
-
headers: await migrationRequestHeaders(entry, token),
|
|
243
|
-
body: JSON.stringify({ bundle_url: params.bundleUrl }),
|
|
244
|
-
},
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
if (response.status !== 200) {
|
|
248
|
-
const errText = await response.text().catch(() => "");
|
|
249
|
-
throw new Error(
|
|
250
|
-
`Local runtime preflight-from-gcs failed (${response.status}): ${
|
|
251
|
-
errText || response.statusText
|
|
252
|
-
}`,
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return (await response.json()) as Record<string, unknown>;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Poll the runtime's unified job-status endpoint.
|
|
261
|
-
*
|
|
262
|
-
* For local/docker assistants this GETs
|
|
263
|
-
* `{runtimeUrl}/v1/migrations/jobs/{jobId}` directly (guardian-token
|
|
264
|
-
* bearer). For cloud assistants it routes through the wildcard
|
|
265
|
-
* runtime proxy at
|
|
266
|
-
* `{cloudUrl}/v1/assistants/<assistantId>/migrations/jobs/{jobId}` with
|
|
267
|
-
* platform-token auth — important: the cloud's dedicated
|
|
268
|
-
* `/v1/migrations/jobs/{id}/` endpoint queries platform-side ImportJob
|
|
269
|
-
* records and would 404 on runtime-created job IDs.
|
|
270
|
-
*/
|
|
271
|
-
export async function localRuntimePollJobStatus(
|
|
272
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
273
|
-
token: string,
|
|
274
|
-
jobId: string,
|
|
275
|
-
): Promise<UnifiedJobStatus> {
|
|
276
|
-
const response = await loopbackSafeFetch(
|
|
277
|
-
resolveRuntimeMigrationUrl(entry, `jobs/${jobId}`),
|
|
278
|
-
{
|
|
279
|
-
headers: await migrationRequestHeaders(entry, token),
|
|
280
|
-
},
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
if (response.status === 404) {
|
|
284
|
-
throw new Error("Migration job not found");
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (!response.ok) {
|
|
288
|
-
throw new Error(
|
|
289
|
-
`Local job status check failed: ${response.status} ${response.statusText}`,
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const raw = (await response.json()) as Parameters<
|
|
294
|
-
typeof parseUnifiedJobStatus
|
|
295
|
-
>[0];
|
|
296
|
-
return parseUnifiedJobStatus(raw);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* The subset of `/v1/health` we care about. The runtime's full response
|
|
301
|
-
* includes additional fields (status, disk, memory, cpu, migrations, etc.)
|
|
302
|
-
* — we only model `version` here because that's all the CLI consumes today.
|
|
303
|
-
*/
|
|
304
|
-
export interface RuntimeIdentity {
|
|
305
|
-
version: string;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Fetch the target runtime's APP_VERSION via `/v1/health`. Used by
|
|
310
|
-
* `cadet teleport` and `cadet backup` to stamp the exported bundle's
|
|
311
|
-
* `min_runtime_version` with the version of the runtime that actually
|
|
312
|
-
* produced it — which can diverge from the orchestrating CLI's version when
|
|
313
|
-
* the target was upgraded independently.
|
|
314
|
-
*
|
|
315
|
-
* GETs `/v1/health` (not `/v1/identity`) so the call works on freshly-
|
|
316
|
-
* hatched runtimes that haven't completed onboarding. The `/v1/identity`
|
|
317
|
-
* handler reads `IDENTITY.md` from the workspace and 404s if it's missing
|
|
318
|
-
* — and `IDENTITY.md` is only written during onboarding, not hatch. The
|
|
319
|
-
* `/v1/health` handler returns the same `version` field unconditionally
|
|
320
|
-
* (no filesystem reads), so it's safe to call against any running runtime.
|
|
321
|
-
*
|
|
322
|
-
* For local/docker assistants this GETs `{runtimeUrl}/v1/health` with
|
|
323
|
-
* guardian-token bearer auth. For cloud-managed (cloud="cadet")
|
|
324
|
-
* assistants the URL is rewritten to the wildcard runtime proxy shape
|
|
325
|
-
* `{cloudUrl}/v1/assistants/<assistantId>/health` and authenticated via
|
|
326
|
-
* the cloud token.
|
|
327
|
-
*
|
|
328
|
-
* For the cadet target this is the FIRST network call in the
|
|
329
|
-
* teleport/backup export flow, so a stale `Cadet-Organization-Id` cache
|
|
330
|
-
* entry would surface as a hard abort before any retry-friendly call (like
|
|
331
|
-
* `cloudRequestSignedUrl`) gets a chance to recover. Mirror that helper's
|
|
332
|
-
* one-shot 401-retry: invalidate the org-ID cache and retry once. Local /
|
|
333
|
-
* docker entries do not use the org-ID cache and are wrapped in
|
|
334
|
-
* `callRuntimeWithAuthRetry` by callers for guardian-token refresh, so the
|
|
335
|
-
* retry is intentionally cadet-only.
|
|
336
|
-
*
|
|
337
|
-
* The function name is intentionally retained ("identity-ish info about the
|
|
338
|
-
* runtime") even though the implementation now hits `/v1/health` — renaming
|
|
339
|
-
* would force changes in 4+ callsites for no behavioral benefit.
|
|
340
|
-
*
|
|
341
|
-
* Throws on non-2xx so callers can surface the failure (we never silently
|
|
342
|
-
* fall back — see teleport.ts call site).
|
|
343
|
-
*/
|
|
344
|
-
export async function localRuntimeIdentity(
|
|
345
|
-
entry: Pick<AssistantEntry, "cloud" | "runtimeUrl" | "assistantId">,
|
|
346
|
-
token: string,
|
|
347
|
-
): Promise<RuntimeIdentity> {
|
|
348
|
-
const url = resolveRuntimeUrl(entry, "health");
|
|
349
|
-
const doRequest = async (): Promise<Response> =>
|
|
350
|
-
loopbackSafeFetch(url, {
|
|
351
|
-
method: "GET",
|
|
352
|
-
headers: await migrationRequestHeaders(entry, token),
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
let response = await doRequest();
|
|
356
|
-
if (response.status === 401 && isCloudHome(entry.cloud)) {
|
|
357
|
-
// `entry.runtimeUrl` is the cloud host for cadet-cloud entries
|
|
358
|
-
// (the wildcard runtime proxy lives there). Pass it as the cache key
|
|
359
|
-
// cloudUrl so we invalidate the same entry that authHeaders cached.
|
|
360
|
-
invalidateOrgIdCache(token, entry.runtimeUrl);
|
|
361
|
-
response = await doRequest();
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (!response.ok) {
|
|
365
|
-
throw new Error(
|
|
366
|
-
`Failed to fetch runtime identity: ${response.status} ${response.statusText}`,
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
const parsed = RuntimeIdentitySchema.safeParse(await response.json());
|
|
370
|
-
if (!parsed.success) {
|
|
371
|
-
throw new Error("Runtime identity response missing version");
|
|
372
|
-
}
|
|
373
|
-
return { version: parsed.data.version };
|
|
374
|
-
}
|