@mycadet/cli 0.12.7 → 0.12.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-platform-login.ts → show-cloud-login.ts} +3 -5
- package/node_modules/@mycadet/assistant-api/index.ts +11 -11
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +3 -2
- 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 +2 -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} +5 -5
- 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__/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 +18 -18
- package/src/__tests__/orphan-detection.test.ts +1 -1
- package/src/__tests__/pair.test.ts +1 -8
- package/src/__tests__/{ps-platform-status.test.ts → ps-cloud-status.test.ts} +79 -26
- 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} +1 -1
- package/src/__tests__/teleport-backup.test.ts +3 -3
- package/src/__tests__/teleport.test.ts +180 -196
- package/src/__tests__/tunnel.test.ts +255 -227
- package/src/__tests__/uninstall.test.ts +13 -4
- 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 +258 -41
- package/src/__tests__/workos-pkce.test.ts +2 -2
- package/src/commands/backup.ts +22 -22
- package/src/commands/client.ts +76 -76
- package/src/commands/debug.ts +20 -20
- package/src/commands/exec.ts +5 -5
- package/src/commands/hatch.ts +22 -22
- package/src/commands/login.ts +22 -22
- package/src/commands/logs.ts +1 -1
- package/src/commands/ps.ts +188 -79
- package/src/commands/restart.ts +30 -0
- package/src/commands/restore.ts +138 -20
- package/src/commands/retire.ts +46 -148
- package/src/commands/rollback.ts +18 -24
- package/src/commands/setup.ts +2 -2
- package/src/commands/teleport.ts +93 -93
- package/src/commands/terminal.ts +157 -17
- package/src/commands/tunnel.ts +80 -122
- package/src/commands/uninstall.ts +55 -7
- package/src/commands/unpair.ts +3 -3
- package/src/commands/upgrade.ts +22 -46
- package/src/commands/wake.ts +162 -46
- package/src/index.ts +3 -9
- package/src/lib/__tests__/{platform-client-signed-url.test.ts → cloud-client-signed-url.test.ts} +16 -16
- package/src/lib/__tests__/job-polling.test.ts +1 -1
- 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} +107 -98
- package/src/lib/cloud-login.ts +24 -24
- package/src/lib/{platform-releases.ts → cloud-releases.ts} +5 -5
- package/src/lib/cloud-values.ts +3 -0
- package/src/lib/config-utils.ts +1 -1
- package/src/lib/detached-process.ts +1 -1
- package/src/lib/docker.ts +111 -10
- package/src/lib/environments/__tests__/paths.test.ts +2 -2
- package/src/lib/environments/__tests__/resolve.test.ts +26 -26
- package/src/lib/environments/resolve.ts +8 -8
- package/src/lib/guardian-token.ts +36 -4
- package/src/lib/hatch-local.ts +19 -23
- package/src/lib/health-check.ts +6 -6
- package/src/lib/ingress-config.ts +8 -35
- package/src/lib/job-polling.ts +1 -1
- 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 +9 -30
- package/src/lib/nginx-ingress.ts +41 -14
- 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/sync-cloud-assistants.ts +18 -18
- 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} +15 -4
- package/src/lib/tunnels/gateway-notify.ts +61 -0
- package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +201 -30
- package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +19 -5
- package/src/lib/upgrade-lifecycle.ts +3 -17
- 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/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/commands/backup.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCloudHome } from "../lib/cloud-values.js";
|
|
2
2
|
import { mkdirSync, writeFileSync } from "fs";
|
|
3
3
|
import { dirname, join } from "path";
|
|
4
4
|
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
localRuntimePollJobStatus,
|
|
15
15
|
} from "../lib/local-runtime-client.js";
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "../lib/
|
|
17
|
+
cloudRequestSignedUrl,
|
|
18
|
+
readCloudToken,
|
|
19
|
+
} from "../lib/cloud-client.js";
|
|
20
20
|
import { loopbackSafeFetch } from "../lib/loopback-fetch.js";
|
|
21
21
|
|
|
22
22
|
// Default timeout for the runtime-direct export request. Overridable via
|
|
@@ -102,7 +102,7 @@ export const backup = Object.assign(
|
|
|
102
102
|
process.exit(1);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
if (
|
|
105
|
+
if (isCloudHome(cloud)) {
|
|
106
106
|
await backupPlatform(entry, name, outputArg);
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
@@ -233,9 +233,9 @@ export const backup = Object.assign(
|
|
|
233
233
|
// ---------------------------------------------------------------------------
|
|
234
234
|
// Platform-managed (cloud="cadet") backup over GCS.
|
|
235
235
|
//
|
|
236
|
-
// The runtime exports the bundle straight to a
|
|
236
|
+
// The runtime exports the bundle straight to a cloud-issued signed GCS
|
|
237
237
|
// URL; the CLI then downloads from GCS to local disk. Bytes never flow
|
|
238
|
-
// through Django. Same architectural shape as the
|
|
238
|
+
// through Django. Same architectural shape as the cloud-source half of
|
|
239
239
|
// `cadet teleport`. Output format and success log lines match mode 1
|
|
240
240
|
// (runtime-direct local backup) so users see one consistent UX.
|
|
241
241
|
//
|
|
@@ -251,24 +251,24 @@ async function backupPlatform(
|
|
|
251
251
|
name: string,
|
|
252
252
|
outputArg?: string,
|
|
253
253
|
): Promise<void> {
|
|
254
|
-
const
|
|
255
|
-
if (!
|
|
254
|
+
const cloudToken = readCloudToken();
|
|
255
|
+
if (!cloudToken) {
|
|
256
256
|
console.error(
|
|
257
|
-
"Not logged in. Run 'cadet login' first (required for
|
|
257
|
+
"Not logged in. Run 'cadet login' first (required for cloud-managed backup).",
|
|
258
258
|
);
|
|
259
259
|
process.exit(1);
|
|
260
260
|
}
|
|
261
261
|
// Pin upload, download, and runtime requests to the same platform instance
|
|
262
|
-
// the assistant lives on. Using `
|
|
262
|
+
// the assistant lives on. Using `getCloudUrl()` instead would target
|
|
263
263
|
// whatever the lockfile / env-var resolves to, which may differ from
|
|
264
264
|
// `entry.runtimeUrl` for staging/dev assistants and end up signing URLs
|
|
265
|
-
// for the wrong GCS bucket. Mirrors the teleport
|
|
265
|
+
// for the wrong GCS bucket. Mirrors the teleport bundleCloudUrl
|
|
266
266
|
// threading at `cli/src/commands/teleport.ts:1311-1312`.
|
|
267
|
-
const
|
|
267
|
+
const cloudUrl = entry.runtimeUrl;
|
|
268
268
|
// Track the working platform token across kickoff/poll/download so a
|
|
269
269
|
// 401-driven refresh during polling stays consistent through the final
|
|
270
270
|
// signed-download request.
|
|
271
|
-
let exportPlatformToken =
|
|
271
|
+
let exportPlatformToken = cloudToken;
|
|
272
272
|
|
|
273
273
|
// Step 0 — Ask the source runtime which version it's running. The bundle
|
|
274
274
|
// is produced by the daemon (not the CLI), and the CLI version can drift
|
|
@@ -288,17 +288,17 @@ async function backupPlatform(
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// Step 1 — Request a signed upload URL.
|
|
291
|
-
const { url: uploadUrl, bundleKey } = await
|
|
291
|
+
const { url: uploadUrl, bundleKey } = await cloudRequestSignedUrl(
|
|
292
292
|
{
|
|
293
293
|
operation: "upload",
|
|
294
294
|
minRuntimeVersion: runtimeIdentity.version,
|
|
295
295
|
maxRuntimeVersion: null,
|
|
296
296
|
},
|
|
297
297
|
exportPlatformToken,
|
|
298
|
-
|
|
298
|
+
cloudUrl,
|
|
299
299
|
);
|
|
300
300
|
|
|
301
|
-
// Step 2 — Kick off runtime export-to-GCS through the
|
|
301
|
+
// Step 2 — Kick off runtime export-to-GCS through the cloud's
|
|
302
302
|
// wildcard runtime proxy. `localRuntimeExportToGcs` builds the
|
|
303
303
|
// `/v1/assistants/<id>/migrations/export-to-gcs` URL for cloud="cadet"
|
|
304
304
|
// and uses platform-token auth (no guardian-token bootstrap).
|
|
@@ -327,7 +327,7 @@ async function backupPlatform(
|
|
|
327
327
|
label: "platform export",
|
|
328
328
|
poll: () => localRuntimePollJobStatus(entry, exportPlatformToken, jobId),
|
|
329
329
|
refreshOn401: async () => {
|
|
330
|
-
const refreshed =
|
|
330
|
+
const refreshed = readCloudToken();
|
|
331
331
|
if (!refreshed) {
|
|
332
332
|
throw new Error(
|
|
333
333
|
"Platform auth expired during export and no credential was found on disk. Run 'cadet login' and retry.",
|
|
@@ -344,7 +344,7 @@ async function backupPlatform(
|
|
|
344
344
|
|
|
345
345
|
// Step 4 — Request a signed download URL for the same bundle and fetch
|
|
346
346
|
// it from GCS directly. No auth on signed URLs.
|
|
347
|
-
// Use `exportPlatformToken` (not the original `
|
|
347
|
+
// Use `exportPlatformToken` (not the original `cloudToken`) so a
|
|
348
348
|
// poll-loop 401 refresh doesn't get clobbered here — otherwise a long
|
|
349
349
|
// export that recovered mid-poll via re-auth would still 401 on the
|
|
350
350
|
// download-URL request and abort an otherwise successful run.
|
|
@@ -354,15 +354,15 @@ async function backupPlatform(
|
|
|
354
354
|
// runtime to gate against, and the user can later restore the file
|
|
355
355
|
// into any compatible runtime. Sending the CLI's version would
|
|
356
356
|
// incorrectly block older CLIs from backing up newer assistants.
|
|
357
|
-
// The
|
|
357
|
+
// The cloud treats `target_runtime_version` as optional and skips
|
|
358
358
|
// the version check when it's omitted.
|
|
359
|
-
const { url: bundleUrl } = await
|
|
359
|
+
const { url: bundleUrl } = await cloudRequestSignedUrl(
|
|
360
360
|
{
|
|
361
361
|
operation: "download",
|
|
362
362
|
bundleKey,
|
|
363
363
|
},
|
|
364
364
|
exportPlatformToken,
|
|
365
|
-
|
|
365
|
+
cloudUrl,
|
|
366
366
|
);
|
|
367
367
|
|
|
368
368
|
let downloadResponse: Response;
|
package/src/commands/client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCloudHome } from "../lib/cloud-values.js";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
getLockfileData,
|
|
31
31
|
renameLockfileAssistantIfPresent,
|
|
32
32
|
upsertRendererLockfileAssistant,
|
|
33
|
-
|
|
33
|
+
replaceCloudAssistants,
|
|
34
34
|
isActiveAssistant,
|
|
35
35
|
isPairedLockfileEntry,
|
|
36
36
|
PAIRED_GUARDIAN_TOKEN_HOST_ONLY_ERROR,
|
|
@@ -64,13 +64,13 @@ import { parseAssistantTargetArg } from "../lib/assistant-target-args.js";
|
|
|
64
64
|
import { parseFeatureFlagArgs, readAmbientFlagEnvVars } from "../lib/flag-args";
|
|
65
65
|
import {
|
|
66
66
|
fetchOrganizationId,
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
fetchCloudAssistants,
|
|
68
|
+
getCloudUrl,
|
|
69
69
|
getWebUrl,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} from "../lib/
|
|
70
|
+
readCloudToken,
|
|
71
|
+
saveCloudToken,
|
|
72
|
+
clearCloudToken,
|
|
73
|
+
} from "../lib/cloud-client";
|
|
74
74
|
import { tuiLog } from "../lib/tui-log";
|
|
75
75
|
import { loopbackSafeFetch } from "../lib/loopback-fetch.js";
|
|
76
76
|
import { relaunchDetached } from "../lib/detached-process.js";
|
|
@@ -87,7 +87,7 @@ const TokenBodySchema = z.object({ token: z.unknown() }).nullable();
|
|
|
87
87
|
|
|
88
88
|
const SyncPlatformBodySchema = z.object({
|
|
89
89
|
syncPlatform: z.unknown().optional(),
|
|
90
|
-
|
|
90
|
+
cloudAssistants: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
91
91
|
organizationId: z.string().optional(),
|
|
92
92
|
rename: z
|
|
93
93
|
.object({ assistantId: z.string(), name: z.string() })
|
|
@@ -133,10 +133,10 @@ interface ParsedArgs {
|
|
|
133
133
|
assistantName?: string;
|
|
134
134
|
/** Assistant data root (home-collapsed) for the TUI details panel. */
|
|
135
135
|
workspaceRoot?: string;
|
|
136
|
-
/** "cadet" for
|
|
136
|
+
/** "cadet" for cloud-hosted assistants, undefined for local. */
|
|
137
137
|
cloud?: string;
|
|
138
|
-
/** Platform session token (X-Session-Token), set when
|
|
139
|
-
|
|
138
|
+
/** Platform session token (X-Session-Token), set when isCloudHome(cloud). */
|
|
139
|
+
cloudToken?: string;
|
|
140
140
|
/** Guardian JWT (Authorization: Bearer), set for local assistants. */
|
|
141
141
|
bearerToken?: string;
|
|
142
142
|
/** Interface identifier sent as X-Cadet-Interface-Id on all requests. */
|
|
@@ -145,7 +145,7 @@ interface ParsedArgs {
|
|
|
145
145
|
flagEnvVars: Record<string, string>;
|
|
146
146
|
/** Parsed --flag overrides: kebab-case key -> typed value (for web injection). */
|
|
147
147
|
parsedFlagOverrides: Record<string, boolean | string>;
|
|
148
|
-
|
|
148
|
+
disableCloud: boolean;
|
|
149
149
|
/** Auto-open the web interface in the default browser (--interface web only). */
|
|
150
150
|
openBrowser: boolean;
|
|
151
151
|
/** Explicit web server port (--interface web only). Binds strictly — no scan. */
|
|
@@ -179,10 +179,10 @@ export function parseArgs(): ParsedArgs {
|
|
|
179
179
|
const { envVars: cliFlagVars, remaining: argsWithoutFlags } =
|
|
180
180
|
parseFeatureFlagArgs(process.argv.slice(3));
|
|
181
181
|
const flagEnvVars = { ...readAmbientFlagEnvVars(), ...cliFlagVars };
|
|
182
|
-
const
|
|
183
|
-
process.env.
|
|
184
|
-
let
|
|
185
|
-
|
|
182
|
+
const disableCloudAmbient =
|
|
183
|
+
process.env.CADET_DISABLE_CLOUD?.trim().toLowerCase();
|
|
184
|
+
let disableCloud =
|
|
185
|
+
disableCloudAmbient === "true" || disableCloudAmbient === "1";
|
|
186
186
|
const args = argsWithoutFlags;
|
|
187
187
|
|
|
188
188
|
// Build parsedFlagOverrides from the extracted env vars:
|
|
@@ -222,8 +222,8 @@ export function parseArgs(): ParsedArgs {
|
|
|
222
222
|
if (arg === "--help" || arg === "-h") {
|
|
223
223
|
printUsage();
|
|
224
224
|
process.exit(0);
|
|
225
|
-
} else if (arg === "--disable-
|
|
226
|
-
|
|
225
|
+
} else if (arg === "--disable-cloud") {
|
|
226
|
+
disableCloud = true;
|
|
227
227
|
} else if (arg === "--no-open") {
|
|
228
228
|
openBrowserPref = false;
|
|
229
229
|
} else if (arg === "--background") {
|
|
@@ -312,14 +312,14 @@ export function parseArgs(): ParsedArgs {
|
|
|
312
312
|
// other topology — local, docker, and "paired" (a remote assistant paired
|
|
313
313
|
// from another machine) — uses a bearer guardian JWT. Both are skipped
|
|
314
314
|
// entirely when --token supplies the credential, so no saved creds are read.
|
|
315
|
-
const
|
|
315
|
+
const cloudToken = bearerTokenOverride
|
|
316
316
|
? undefined
|
|
317
|
-
:
|
|
318
|
-
? (
|
|
317
|
+
: isCloudHome(cloud)
|
|
318
|
+
? (readCloudToken() ?? undefined)
|
|
319
319
|
: undefined;
|
|
320
320
|
const bearerToken = bearerTokenOverride
|
|
321
321
|
? bearerTokenOverride
|
|
322
|
-
:
|
|
322
|
+
: isCloudHome(cloud)
|
|
323
323
|
? undefined
|
|
324
324
|
: (loadGuardianToken(entry?.assistantId ?? "")?.accessToken ?? undefined);
|
|
325
325
|
|
|
@@ -365,12 +365,12 @@ export function parseArgs(): ParsedArgs {
|
|
|
365
365
|
assistantName,
|
|
366
366
|
workspaceRoot,
|
|
367
367
|
cloud,
|
|
368
|
-
|
|
368
|
+
cloudToken,
|
|
369
369
|
bearerToken,
|
|
370
370
|
interfaceId,
|
|
371
371
|
flagEnvVars,
|
|
372
372
|
parsedFlagOverrides,
|
|
373
|
-
|
|
373
|
+
disableCloud,
|
|
374
374
|
openBrowser: openBrowserPref,
|
|
375
375
|
webPort,
|
|
376
376
|
background,
|
|
@@ -401,7 +401,7 @@ ${ANSI.bold}OPTIONS:${ANSI.reset}
|
|
|
401
401
|
(--interface web). Prints the URL, PID, and log
|
|
402
402
|
path, then returns to the shell.
|
|
403
403
|
--flag <key=value> Feature flag override (repeatable, kebab-case key)
|
|
404
|
-
--disable-
|
|
404
|
+
--disable-cloud Suppress all outbound platform API calls
|
|
405
405
|
-h, --help Show this help message
|
|
406
406
|
|
|
407
407
|
${ANSI.bold}EXAMPLES:${ANSI.reset}
|
|
@@ -421,19 +421,19 @@ ${ANSI.bold}EXAMPLES:${ANSI.reset}
|
|
|
421
421
|
`);
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
async function
|
|
424
|
+
async function maybeHydrateCloudAssistantName(
|
|
425
425
|
assistantId: string,
|
|
426
426
|
assistantName: string | undefined,
|
|
427
427
|
cloud: string | undefined,
|
|
428
|
-
|
|
428
|
+
cloudToken: string | undefined,
|
|
429
429
|
): Promise<string | undefined> {
|
|
430
|
-
if (!
|
|
430
|
+
if (!isCloudHome(cloud) || assistantName || !cloudToken) {
|
|
431
431
|
return assistantName;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
try {
|
|
435
435
|
const matchedAssistant = (
|
|
436
|
-
await
|
|
436
|
+
await fetchCloudAssistants(cloudToken)
|
|
437
437
|
).find((assistant) => assistant.id === assistantId);
|
|
438
438
|
const hydratedName = matchedAssistant?.name.trim();
|
|
439
439
|
if (!hydratedName) {
|
|
@@ -500,18 +500,18 @@ const PAIRING_CANCEL_PATTERN = /^(?:\/assistant)?\/__local\/pairing-cancel$/;
|
|
|
500
500
|
const GUARDIAN_TOKEN_PATTERN =
|
|
501
501
|
/^(?:\/assistant)?\/__local\/guardian-token\/([^/]+)$/;
|
|
502
502
|
const PLATFORM_SESSION_PATTERN =
|
|
503
|
-
/^(?:\/assistant)?\/__local\/
|
|
503
|
+
/^(?:\/assistant)?\/__local\/cloud-session$/;
|
|
504
504
|
|
|
505
505
|
// The loopback platform session token. Persisted via the same store the CLI
|
|
506
506
|
// uses (so `cadet client` restarts and CLI logins stay in sync), cached here
|
|
507
507
|
// to keep it off the per-request proxy path. Set only after the SPA validates
|
|
508
508
|
// the loopback `state`, so an unsolicited /callback can't fixate a session.
|
|
509
|
-
let
|
|
509
|
+
let cloudSessionToken: string | null | undefined;
|
|
510
510
|
function currentPlatformToken(): string | null {
|
|
511
|
-
if (
|
|
512
|
-
|
|
511
|
+
if (cloudSessionToken === undefined) {
|
|
512
|
+
cloudSessionToken = readCloudToken();
|
|
513
513
|
}
|
|
514
|
-
return
|
|
514
|
+
return cloudSessionToken;
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
// Whether to attach a host-owned credential to a proxied request. The browser
|
|
@@ -582,8 +582,8 @@ async function handleLocalEndpoints(
|
|
|
582
582
|
// platform. The browser never holds a session cookie.
|
|
583
583
|
if (PLATFORM_SESSION_PATTERN.test(pathname)) {
|
|
584
584
|
if (req.method === "DELETE") {
|
|
585
|
-
|
|
586
|
-
|
|
585
|
+
clearCloudToken();
|
|
586
|
+
cloudSessionToken = null;
|
|
587
587
|
return Response.json({ ok: true });
|
|
588
588
|
}
|
|
589
589
|
if (req.method === "POST") {
|
|
@@ -598,8 +598,8 @@ async function handleLocalEndpoints(
|
|
|
598
598
|
{ status: 400 },
|
|
599
599
|
);
|
|
600
600
|
}
|
|
601
|
-
|
|
602
|
-
|
|
601
|
+
saveCloudToken(token);
|
|
602
|
+
cloudSessionToken = token;
|
|
603
603
|
return Response.json({ ok: true });
|
|
604
604
|
}
|
|
605
605
|
return new Response(null, { status: 405 });
|
|
@@ -627,10 +627,10 @@ async function handleLocalEndpoints(
|
|
|
627
627
|
);
|
|
628
628
|
}
|
|
629
629
|
let result;
|
|
630
|
-
if (body.syncPlatform && body.
|
|
631
|
-
result =
|
|
630
|
+
if (body.syncPlatform && body.cloudAssistants) {
|
|
631
|
+
result = replaceCloudAssistants(
|
|
632
632
|
lockfilePaths,
|
|
633
|
-
body.
|
|
633
|
+
body.cloudAssistants,
|
|
634
634
|
body.organizationId,
|
|
635
635
|
);
|
|
636
636
|
} else if (body.rename) {
|
|
@@ -1223,7 +1223,7 @@ const WEB_BACKGROUND_START_TIMEOUT_MS = 30_000;
|
|
|
1223
1223
|
* The port is resolved up front (explicit --port must be free; otherwise the
|
|
1224
1224
|
* first free port at/above 3000) and pinned via `--port` on the child so the
|
|
1225
1225
|
* URL printed here is the one the child binds. The child's stdout/stderr go to
|
|
1226
|
-
* `<xdg-log-dir>/client-web.log` — same detach idiom as the nginx/
|
|
1226
|
+
* `<xdg-log-dir>/client-web.log` — same detach idiom as the nginx/tunnel
|
|
1227
1227
|
* spawns. Success is only reported once the child is accepting connections on
|
|
1228
1228
|
* the port; an early child exit (e.g. missing @mycadet/web assets) or a
|
|
1229
1229
|
* startup timeout fails with a pointer at the log file.
|
|
@@ -1303,17 +1303,17 @@ async function spawnBackgroundWebInterface(
|
|
|
1303
1303
|
*/
|
|
1304
1304
|
export function buildWebInterfaceConfig(opts: {
|
|
1305
1305
|
webUrl: string;
|
|
1306
|
-
|
|
1307
|
-
|
|
1306
|
+
cloudUrl: string;
|
|
1307
|
+
disableCloud: boolean;
|
|
1308
1308
|
}): Record<string, unknown> {
|
|
1309
|
-
const { webUrl,
|
|
1310
|
-
return { webUrl,
|
|
1309
|
+
const { webUrl, cloudUrl, disableCloud } = opts;
|
|
1310
|
+
return { webUrl, cloudUrl, disableCloud };
|
|
1311
1311
|
}
|
|
1312
1312
|
|
|
1313
1313
|
async function runWebInterface(
|
|
1314
1314
|
flagEnvVars: Record<string, string>,
|
|
1315
1315
|
parsedFlagOverrides: Record<string, boolean | string>,
|
|
1316
|
-
|
|
1316
|
+
disableCloud: boolean,
|
|
1317
1317
|
openInBrowser: boolean,
|
|
1318
1318
|
webPort: number | undefined,
|
|
1319
1319
|
): Promise<void> {
|
|
@@ -1327,7 +1327,7 @@ async function runWebInterface(
|
|
|
1327
1327
|
return runViteDevServer(
|
|
1328
1328
|
webSourceDir,
|
|
1329
1329
|
flagEnvVars,
|
|
1330
|
-
|
|
1330
|
+
disableCloud,
|
|
1331
1331
|
openInBrowser,
|
|
1332
1332
|
webPort,
|
|
1333
1333
|
);
|
|
@@ -1339,18 +1339,18 @@ async function runWebInterface(
|
|
|
1339
1339
|
`${ANSI.bold}--interface web${ANSI.reset}: unable to locate ` +
|
|
1340
1340
|
`@mycadet/web assets.\n\n` +
|
|
1341
1341
|
` npm/bunx install: npm install @mycadet/web\n` +
|
|
1342
|
-
` source checkout: cd clients/web &&
|
|
1342
|
+
` source checkout: cd clients/web && VITE_CLOUD_MODE=false bun run build`,
|
|
1343
1343
|
);
|
|
1344
1344
|
process.exit(1);
|
|
1345
1345
|
}
|
|
1346
1346
|
|
|
1347
1347
|
const rawIndexHtml = await Bun.file(path.join(distDir, "index.html")).text();
|
|
1348
|
-
const
|
|
1348
|
+
const cloudUrl = getCloudUrl();
|
|
1349
1349
|
const webUrl = getWebUrl();
|
|
1350
1350
|
const safeJson = (v: unknown) =>
|
|
1351
1351
|
JSON.stringify(v).replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
|
|
1352
1352
|
const configJson = safeJson(
|
|
1353
|
-
buildWebInterfaceConfig({ webUrl,
|
|
1353
|
+
buildWebInterfaceConfig({ webUrl, cloudUrl, disableCloud }),
|
|
1354
1354
|
);
|
|
1355
1355
|
const hasOverrides = Object.keys(parsedFlagOverrides).length > 0;
|
|
1356
1356
|
const flagOverridesSnippet = hasOverrides
|
|
@@ -1380,9 +1380,9 @@ async function runWebInterface(
|
|
|
1380
1380
|
return Response.redirect(SPA_BASE, 302);
|
|
1381
1381
|
}
|
|
1382
1382
|
|
|
1383
|
-
// Loopback auth: the
|
|
1383
|
+
// Loopback auth: the cloud redirects here after login with
|
|
1384
1384
|
// ?state=...&session_token=... — forward into the SPA, which validates the
|
|
1385
|
-
// `state` nonce before registering the token via /__local/
|
|
1385
|
+
// `state` nonce before registering the token via /__local/cloud-session.
|
|
1386
1386
|
if (pathname === "/callback") {
|
|
1387
1387
|
return Response.redirect(`/account/platform-callback${url.search}`, 302);
|
|
1388
1388
|
}
|
|
@@ -1404,9 +1404,9 @@ async function runWebInterface(
|
|
|
1404
1404
|
pathname.startsWith("/_allauth/") ||
|
|
1405
1405
|
pathname.startsWith("/accounts/")
|
|
1406
1406
|
) {
|
|
1407
|
-
const target = new URL(pathname + url.search,
|
|
1407
|
+
const target = new URL(pathname + url.search, cloudUrl);
|
|
1408
1408
|
const headers = new Headers(req.headers);
|
|
1409
|
-
headers.set("Host", new URL(
|
|
1409
|
+
headers.set("Host", new URL(cloudUrl).host);
|
|
1410
1410
|
headers.delete("Origin");
|
|
1411
1411
|
headers.delete("Referer");
|
|
1412
1412
|
|
|
@@ -1430,7 +1430,7 @@ async function runWebInterface(
|
|
|
1430
1430
|
headers.set("X-Session-Token", sessionToken);
|
|
1431
1431
|
}
|
|
1432
1432
|
} else if (sessionToken) {
|
|
1433
|
-
// allauth / accounts: the
|
|
1433
|
+
// allauth / accounts: the cloud expects the Django session cookie.
|
|
1434
1434
|
headers.set(
|
|
1435
1435
|
"Cookie",
|
|
1436
1436
|
`sessionid=${sessionToken}; __Secure-sessionid=${sessionToken}`,
|
|
@@ -1507,7 +1507,7 @@ async function runWebInterface(
|
|
|
1507
1507
|
console.log(`Port ${preferredPort} in use; using ${port}.`);
|
|
1508
1508
|
}
|
|
1509
1509
|
// Advertise `localhost` (not `127.0.0.1`) so the app origin matches the host
|
|
1510
|
-
// the
|
|
1510
|
+
// the cloud hardcodes in its loopback callback. We bind both loopback
|
|
1511
1511
|
// families above so `localhost` reaches us whichever one it resolves to.
|
|
1512
1512
|
const webInterfaceUrl = `http://localhost:${port}${SPA_BASE}`;
|
|
1513
1513
|
console.log(`Cadet web interface: ${webInterfaceUrl}`);
|
|
@@ -1526,11 +1526,11 @@ async function runWebInterface(
|
|
|
1526
1526
|
async function runViteDevServer(
|
|
1527
1527
|
webSourceDir: string,
|
|
1528
1528
|
flagEnvVars: Record<string, string>,
|
|
1529
|
-
|
|
1529
|
+
disableCloud: boolean,
|
|
1530
1530
|
openInBrowser: boolean,
|
|
1531
1531
|
webPort: number | undefined,
|
|
1532
1532
|
): Promise<void> {
|
|
1533
|
-
const
|
|
1533
|
+
const cloudUrl = getCloudUrl();
|
|
1534
1534
|
|
|
1535
1535
|
// Build VITE_CADET_FLAG_* vars so Vite exposes them to the browser bundle.
|
|
1536
1536
|
const viteFlagVars: Record<string, string> = {};
|
|
@@ -1555,11 +1555,11 @@ async function runViteDevServer(
|
|
|
1555
1555
|
...process.env,
|
|
1556
1556
|
...flagEnvVars,
|
|
1557
1557
|
...viteFlagVars,
|
|
1558
|
-
...(
|
|
1559
|
-
|
|
1560
|
-
API_PROXY_TARGET:
|
|
1558
|
+
...(disableCloud ? { VITE_CADET_DISABLE_CLOUD: "true" } : {}),
|
|
1559
|
+
VITE_CLOUD_MODE: "false",
|
|
1560
|
+
API_PROXY_TARGET: cloudUrl,
|
|
1561
1561
|
CADET_WEB_URL: getWebUrl(),
|
|
1562
|
-
|
|
1562
|
+
CADET_CLOUD_URL: cloudUrl,
|
|
1563
1563
|
PORT: String(port),
|
|
1564
1564
|
},
|
|
1565
1565
|
});
|
|
@@ -1590,7 +1590,7 @@ async function runViteDevServer(
|
|
|
1590
1590
|
* Return a possibly-refreshed bearer token for the TUI's startup auth.
|
|
1591
1591
|
*
|
|
1592
1592
|
* Only a STORED guardian token is refreshable: platform session auth
|
|
1593
|
-
* (`
|
|
1593
|
+
* (`isCloudHome(cloud)`) and ephemeral `--token` overrides (whose token won't
|
|
1594
1594
|
* match the store) are left untouched, as is a token that's still fresh. When
|
|
1595
1595
|
* the stored token has passed its `refreshAfter` (or expiry) and a refresh
|
|
1596
1596
|
* token is available, refresh once via the concurrency-safe refreshGuardianToken
|
|
@@ -1603,7 +1603,7 @@ export async function resolveFreshBearerToken(
|
|
|
1603
1603
|
bearerToken: string | undefined,
|
|
1604
1604
|
cloud: string | undefined,
|
|
1605
1605
|
): Promise<string | undefined> {
|
|
1606
|
-
if (
|
|
1606
|
+
if (isCloudHome(cloud) || !bearerToken || !assistantId) return bearerToken;
|
|
1607
1607
|
|
|
1608
1608
|
const stored = loadGuardianToken(assistantId);
|
|
1609
1609
|
// Refresh only the stored token (an ephemeral --token won't match), and only
|
|
@@ -1637,19 +1637,19 @@ export const client = Object.assign(
|
|
|
1637
1637
|
assistantName: parsedAssistantName,
|
|
1638
1638
|
workspaceRoot,
|
|
1639
1639
|
cloud,
|
|
1640
|
-
|
|
1640
|
+
cloudToken,
|
|
1641
1641
|
bearerToken,
|
|
1642
1642
|
interfaceId,
|
|
1643
1643
|
flagEnvVars,
|
|
1644
1644
|
parsedFlagOverrides,
|
|
1645
|
-
|
|
1645
|
+
disableCloud,
|
|
1646
1646
|
openBrowser: openInBrowser,
|
|
1647
1647
|
webPort,
|
|
1648
1648
|
background,
|
|
1649
1649
|
} = parseArgs();
|
|
1650
1650
|
|
|
1651
|
-
if (
|
|
1652
|
-
process.env.
|
|
1651
|
+
if (disableCloud) {
|
|
1652
|
+
process.env.CADET_DISABLE_CLOUD = "true";
|
|
1653
1653
|
}
|
|
1654
1654
|
|
|
1655
1655
|
if (interfaceId === WEB_INTERFACE_ID) {
|
|
@@ -1660,7 +1660,7 @@ export const client = Object.assign(
|
|
|
1660
1660
|
await runWebInterface(
|
|
1661
1661
|
flagEnvVars,
|
|
1662
1662
|
parsedFlagOverrides,
|
|
1663
|
-
|
|
1663
|
+
disableCloud,
|
|
1664
1664
|
openInBrowser,
|
|
1665
1665
|
webPort,
|
|
1666
1666
|
);
|
|
@@ -1675,23 +1675,23 @@ export const client = Object.assign(
|
|
|
1675
1675
|
interfaceId,
|
|
1676
1676
|
});
|
|
1677
1677
|
|
|
1678
|
-
const assistantName = await
|
|
1678
|
+
const assistantName = await maybeHydrateCloudAssistantName(
|
|
1679
1679
|
assistantId,
|
|
1680
1680
|
parsedAssistantName,
|
|
1681
1681
|
cloud,
|
|
1682
|
-
|
|
1682
|
+
cloudToken,
|
|
1683
1683
|
);
|
|
1684
1684
|
|
|
1685
1685
|
// Build pre-constructed request headers merged from auth + client registration.
|
|
1686
1686
|
// Spreading into every fetch site ensures consistency across REST and SSE endpoints.
|
|
1687
1687
|
let auth: Record<string, string> | undefined;
|
|
1688
|
-
if (
|
|
1689
|
-
const orgId = await fetchOrganizationId(
|
|
1688
|
+
if (isCloudHome(cloud) && cloudToken) {
|
|
1689
|
+
const orgId = await fetchOrganizationId(cloudToken).catch((err) => {
|
|
1690
1690
|
tuiLog.warn("failed to fetch organization id", { err: String(err) });
|
|
1691
1691
|
return undefined;
|
|
1692
1692
|
});
|
|
1693
1693
|
auth = {
|
|
1694
|
-
"X-Session-Token":
|
|
1694
|
+
"X-Session-Token": cloudToken,
|
|
1695
1695
|
...(orgId ? { "Cadet-Organization-Id": orgId } : {}),
|
|
1696
1696
|
...getClientRegistrationHeaders(interfaceId),
|
|
1697
1697
|
};
|
package/src/commands/debug.ts
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Send Cadet staff a debug bundle of a self-hosted assistant: the
|
|
5
5
|
* workspace, its logs, and the gateway's own database, with no credentials.
|
|
6
|
-
* Staff open it on a throwaway debug clone. The
|
|
6
|
+
* Staff open it on a throwaway debug clone. The cloud only hands out the
|
|
7
7
|
* upload URL while "Allow Staff Access" is on, and the bundle is deleted
|
|
8
8
|
* after seven days.
|
|
9
9
|
*
|
|
10
|
-
* Steps: the daemon is checked for the debug profile, the
|
|
10
|
+
* Steps: the daemon is checked for the debug profile, the cloud mints
|
|
11
11
|
* the upload URL, the daemon exports with the debug profile, and this
|
|
12
12
|
* command waits for the job so the result is real.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { isCloudHome } from "../lib/cloud-values.js";
|
|
15
15
|
import {
|
|
16
16
|
formatAssistantReference,
|
|
17
17
|
resolveTargetAssistant,
|
|
@@ -29,9 +29,9 @@ import { loopbackSafeFetch } from "../lib/loopback-fetch.js";
|
|
|
29
29
|
import {
|
|
30
30
|
authHeaders,
|
|
31
31
|
authHeadersForKnownOrganization,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from "../lib/
|
|
32
|
+
getCloudUrl,
|
|
33
|
+
readCloudToken,
|
|
34
|
+
} from "../lib/cloud-client.js";
|
|
35
35
|
import { compareVersions } from "../lib/version-compat.js";
|
|
36
36
|
|
|
37
37
|
// Matches the daemon's own upload deadline (EXPORT_TO_GCS_PUT_TIMEOUT_MS in
|
|
@@ -60,7 +60,7 @@ Arguments:
|
|
|
60
60
|
Requirements:
|
|
61
61
|
- 'Allow Staff Access' is on for the assistant (Settings > Privacy).
|
|
62
62
|
- You are logged in ('cadet login'), which also registers the
|
|
63
|
-
assistant with the
|
|
63
|
+
assistant with the cloud.
|
|
64
64
|
- The assistant runs version ${DEBUG_PROFILE_MIN_VERSION} or newer.
|
|
65
65
|
- The assistant is self-hosted. Cadet-hosted assistants need no bundle;
|
|
66
66
|
staff clone them directly.
|
|
@@ -88,30 +88,30 @@ export const debug = Object.assign(
|
|
|
88
88
|
|
|
89
89
|
const entry = resolveTargetAssistant(parseAssistantTargetArg(args));
|
|
90
90
|
const reference = formatAssistantReference(entry);
|
|
91
|
-
if (
|
|
91
|
+
if (isCloudHome(entry.cloud)) {
|
|
92
92
|
console.error(
|
|
93
93
|
`${reference} is hosted by Cadet, so staff can clone it directly. No bundle is needed.`,
|
|
94
94
|
);
|
|
95
95
|
process.exit(1);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
98
|
+
const cloudToken = readCloudToken();
|
|
99
|
+
if (!cloudToken) {
|
|
100
100
|
console.error("Not logged in. Run 'cadet login' first.");
|
|
101
101
|
process.exit(1);
|
|
102
102
|
}
|
|
103
|
-
const
|
|
104
|
-
if (!
|
|
103
|
+
const cloudAssistantId = entry.cloudAssistantId?.trim();
|
|
104
|
+
if (!cloudAssistantId) {
|
|
105
105
|
console.error(
|
|
106
|
-
`${reference} is not registered with the
|
|
106
|
+
`${reference} is not registered with the cloud yet. Run 'cadet login' and retry.`,
|
|
107
107
|
);
|
|
108
108
|
process.exit(1);
|
|
109
109
|
}
|
|
110
|
-
const
|
|
111
|
-
const organizationId = entry.
|
|
110
|
+
const cloudUrl = entry.cloudBaseUrl?.trim() || getCloudUrl();
|
|
111
|
+
const organizationId = entry.cloudOrganizationId?.trim();
|
|
112
112
|
|
|
113
113
|
// Step 1: a guardian token for the daemon, and a check that the daemon
|
|
114
|
-
// knows the debug profile. This comes before the
|
|
114
|
+
// knows the debug profile. This comes before the cloud call so a
|
|
115
115
|
// daemon that would export credentials is refused before any URL exists.
|
|
116
116
|
let accessToken = await daemonAccessToken(entry, reference, false);
|
|
117
117
|
const { version } = await localRuntimeIdentity(entry, accessToken);
|
|
@@ -123,13 +123,13 @@ export const debug = Object.assign(
|
|
|
123
123
|
process.exit(1);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
// Step 2: the
|
|
126
|
+
// Step 2: the cloud mints the upload URL, and refuses unless the
|
|
127
127
|
// owner's staff access grant is active.
|
|
128
128
|
const headers = organizationId
|
|
129
|
-
? authHeadersForKnownOrganization(
|
|
130
|
-
: await authHeaders(
|
|
129
|
+
? authHeadersForKnownOrganization(cloudToken, organizationId)
|
|
130
|
+
: await authHeaders(cloudToken, cloudUrl);
|
|
131
131
|
const urlResponse = await loopbackSafeFetch(
|
|
132
|
-
`${
|
|
132
|
+
`${cloudUrl}/v1/assistants/${encodeURIComponent(cloudAssistantId)}/debug-bundle-upload-url/`,
|
|
133
133
|
{ method: "POST", headers },
|
|
134
134
|
);
|
|
135
135
|
if (urlResponse.status === 403) {
|