@mycadet/cli 0.12.8 → 0.12.9-dev.202610131620.d833e01
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/node_modules/@mycadet/assistant-api/events/show-cloud-login.ts +2 -2
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/package.json +4 -3
- package/src/__tests__/client-web-config.test.ts +0 -1
- package/src/__tests__/cloud-releases.test.ts +23 -13
- package/src/__tests__/ensure-binary.test.ts +182 -0
- package/src/__tests__/nginx-ingress.test.ts +6 -11
- package/src/__tests__/ngrok-spawn.test.ts +49 -0
- package/src/__tests__/pair.test.ts +149 -78
- package/src/__tests__/ps-cloud-status.test.ts +4 -3
- package/src/__tests__/ps-table.test.ts +79 -0
- package/src/__tests__/tailscale.test.ts +1 -1
- package/src/__tests__/teleport.test.ts +1 -16
- package/src/__tests__/tunnel.test.ts +274 -82
- package/src/__tests__/uninstall.test.ts +141 -9
- package/src/__tests__/wake.test.ts +2 -2
- package/src/commands/backup.ts +3 -3
- package/src/commands/client.ts +4 -6
- package/src/commands/completions.test.ts +104 -0
- package/src/commands/completions.ts +87 -0
- package/src/commands/exec.ts +7 -7
- package/src/commands/flags.ts +2 -2
- package/src/commands/hatch.ts +5 -2
- package/src/commands/logs.ts +7 -7
- package/src/commands/pair.ts +140 -37
- package/src/commands/ps.ts +63 -26
- package/src/commands/restore.ts +2 -2
- package/src/commands/retire.ts +2 -2
- package/src/commands/rollback.ts +2 -2
- package/src/commands/sleep.ts +2 -2
- package/src/commands/teleport.ts +18 -37
- package/src/commands/terminal.ts +1 -1
- package/src/commands/tunnel.ts +101 -78
- package/src/commands/uninstall.ts +108 -73
- package/src/commands/upgrade.ts +8 -8
- package/src/commands/use.ts +1 -1
- package/src/commands/wake.ts +6 -6
- package/src/index.ts +15 -0
- package/src/lib/__tests__/docker.test.ts +168 -20
- package/src/lib/__tests__/job-polling.test.ts +1 -1
- package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
- package/src/lib/cloud-client.ts +0 -3
- package/src/lib/cloud-login.ts +0 -1
- package/src/lib/cloud-releases.ts +6 -4
- package/src/lib/color.ts +4 -0
- package/src/lib/docker.ts +190 -102
- package/src/lib/ensure-binary.ts +171 -0
- package/src/lib/environments/__tests__/paths.test.ts +27 -1
- package/src/lib/environments/paths.ts +16 -8
- package/src/lib/environments/resolve.ts +2 -4
- package/src/lib/flag-args.test.ts +1 -1
- package/src/lib/flag-args.ts +1 -1
- package/src/lib/guardian-token.ts +1 -1
- package/src/lib/hatch-local.ts +15 -8
- package/src/lib/hatch-next-steps.ts +5 -9
- package/src/lib/job-polling.ts +1 -1
- package/src/lib/local.ts +37 -4
- package/src/lib/nginx-ingress.ts +3 -5
- package/src/lib/pair.ts +1 -1
- package/src/lib/step-runner.ts +6 -1
- package/src/lib/sync-cloud-assistants.ts +2 -2
- package/src/lib/tunnels/cloudflare.ts +21 -11
- package/src/lib/tunnels/ngrok.ts +198 -108
- package/src/lib/tunnels/tailscale.ts +34 -4
- package/src/lib/upgrade-lifecycle.ts +5 -4
package/src/commands/wake.ts
CHANGED
|
@@ -58,7 +58,7 @@ export const wake = Object.assign(
|
|
|
58
58
|
console.log("");
|
|
59
59
|
console.log("Arguments:");
|
|
60
60
|
console.log(
|
|
61
|
-
" <name> Name of the assistant to start
|
|
61
|
+
" <name> Name of the assistant to start, defaults to active or only local",
|
|
62
62
|
);
|
|
63
63
|
console.log("");
|
|
64
64
|
console.log("Options:");
|
|
@@ -122,7 +122,7 @@ export const wake = Object.assign(
|
|
|
122
122
|
// Foreground blocking is a single-assistant concern (--all refuses the
|
|
123
123
|
// flag), and the docker path already warned that foreground is ignored.
|
|
124
124
|
if (foreground && entry.cloud !== "docker") {
|
|
125
|
-
console.log("Running in foreground
|
|
125
|
+
console.log("Running in the foreground, Ctrl+C to stop...\n");
|
|
126
126
|
// Block forever — the daemon is running with inherited stdio so its
|
|
127
127
|
// output streams to this terminal. When the user hits Ctrl+C, SIGINT
|
|
128
128
|
// propagates to the daemon child and both exit.
|
|
@@ -213,7 +213,7 @@ async function wakeAssistant(
|
|
|
213
213
|
.filter(Boolean)
|
|
214
214
|
.join(" and ");
|
|
215
215
|
console.warn(
|
|
216
|
-
`Warning: ${ignored} ignored for Docker instances
|
|
216
|
+
`Warning: ${ignored} ignored for Docker instances.`,
|
|
217
217
|
);
|
|
218
218
|
}
|
|
219
219
|
const res = dockerResourceNames(entry.assistantId);
|
|
@@ -364,7 +364,7 @@ async function wakeAssistant(
|
|
|
364
364
|
daemonMigrationsFailed = daemonState.status === "migration_failed";
|
|
365
365
|
if (watch) {
|
|
366
366
|
console.log(
|
|
367
|
-
`Assistant running (pid ${daemonState.pid}) — watch mode not available
|
|
367
|
+
`Assistant running (pid ${daemonState.pid}) — watch mode not available, no source files. Keeping existing process.`,
|
|
368
368
|
);
|
|
369
369
|
} else if (daemonMigrationsFailed) {
|
|
370
370
|
console.log(
|
|
@@ -488,7 +488,7 @@ async function wakeAssistant(
|
|
|
488
488
|
} else {
|
|
489
489
|
if (watch) {
|
|
490
490
|
console.log(
|
|
491
|
-
`Gateway running (pid ${gatewayState.pid}) — watch mode not available
|
|
491
|
+
`Gateway running (pid ${gatewayState.pid}) — watch mode not available, no source files. Keeping existing process.`,
|
|
492
492
|
);
|
|
493
493
|
} else {
|
|
494
494
|
console.log(`Gateway already running (pid ${gatewayState.pid}).`);
|
|
@@ -569,7 +569,7 @@ async function wakeAssistant(
|
|
|
569
569
|
await new Promise((r) => setTimeout(r, 2000 * 2 ** (attempt - 1)));
|
|
570
570
|
} else {
|
|
571
571
|
console.warn(
|
|
572
|
-
` Guardian token re-provision failed${err instanceof GuardianLeasePermanentError ?
|
|
572
|
+
` Guardian token re-provision failed${err instanceof GuardianLeasePermanentError ?", not retrying — a malformed 200 will not fix itself" : ` after ${maxAttempts} attempts`}: ${err}`,
|
|
573
573
|
);
|
|
574
574
|
// The user explicitly confirmed this destructive repair — a
|
|
575
575
|
// success exit here would make callers (the web recovery flow)
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import cliPkg from "../package.json";
|
|
|
14
14
|
import { backup } from "./commands/backup";
|
|
15
15
|
import { clean } from "./commands/clean";
|
|
16
16
|
import { client } from "./commands/client";
|
|
17
|
+
import { completeCadet, completions } from "./commands/completions";
|
|
17
18
|
import { confirm } from "./commands/confirm";
|
|
18
19
|
import { connect } from "./commands/connect";
|
|
19
20
|
import { devices } from "./commands/devices";
|
|
@@ -53,6 +54,7 @@ const commands = {
|
|
|
53
54
|
backup,
|
|
54
55
|
clean,
|
|
55
56
|
client,
|
|
57
|
+
completions,
|
|
56
58
|
confirm,
|
|
57
59
|
connect,
|
|
58
60
|
debug,
|
|
@@ -231,6 +233,19 @@ async function main() {
|
|
|
231
233
|
process.exit(0);
|
|
232
234
|
}
|
|
233
235
|
|
|
236
|
+
// Hidden completion hook: the bash completion script calls
|
|
237
|
+
// `cadet __complete <words-up-to-cursor>` and filters the printed
|
|
238
|
+
// candidates against the word under the cursor. Deliberately unlisted in
|
|
239
|
+
// the command map so it never shows up in help.
|
|
240
|
+
if (commandName === "__complete") {
|
|
241
|
+
const candidates = completeCadet(
|
|
242
|
+
process.argv.slice(3),
|
|
243
|
+
Object.keys(commands),
|
|
244
|
+
);
|
|
245
|
+
console.log(candidates.join("\n"));
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
|
|
234
249
|
const command = commands[commandName as CommandName];
|
|
235
250
|
|
|
236
251
|
if (!command) {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
collectWatchTargets,
|
|
9
9
|
dockerResourceNames,
|
|
10
10
|
ensureDockerDaemonRunning,
|
|
11
|
+
pullImage,
|
|
11
12
|
resolveAvatarDevicePath,
|
|
12
13
|
resolveDockerHatchMode,
|
|
13
14
|
resolveDockerProviderCredentialSetupAction,
|
|
@@ -158,6 +159,38 @@ describe("resolveDockerProviderCredentialSetupAction", () => {
|
|
|
158
159
|
}),
|
|
159
160
|
).toBe("skip");
|
|
160
161
|
});
|
|
162
|
+
|
|
163
|
+
test("detached hatch with a pre-collected key configures instead of deferring", () => {
|
|
164
|
+
expect(
|
|
165
|
+
resolveDockerProviderCredentialSetupAction({
|
|
166
|
+
provider: "fireworks",
|
|
167
|
+
guardianAccessToken: "guardian-token",
|
|
168
|
+
detached: true,
|
|
169
|
+
precollectedKey: "fw-key-123",
|
|
170
|
+
}),
|
|
171
|
+
).toBe("configure");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("pre-collected key without a guardian token reports missing-token (no silent defer)", () => {
|
|
175
|
+
expect(
|
|
176
|
+
resolveDockerProviderCredentialSetupAction({
|
|
177
|
+
provider: "fireworks",
|
|
178
|
+
detached: true,
|
|
179
|
+
precollectedKey: "fw-key-123",
|
|
180
|
+
}),
|
|
181
|
+
).toBe("missing-token");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("an explicitly skipped key still counts as answered — configure runs and reports the gap", () => {
|
|
185
|
+
expect(
|
|
186
|
+
resolveDockerProviderCredentialSetupAction({
|
|
187
|
+
provider: "fireworks",
|
|
188
|
+
guardianAccessToken: "guardian-token",
|
|
189
|
+
detached: true,
|
|
190
|
+
precollectedKey: "",
|
|
191
|
+
}),
|
|
192
|
+
).toBe("configure");
|
|
193
|
+
});
|
|
161
194
|
});
|
|
162
195
|
|
|
163
196
|
describe("buildServiceRunArgs — gateway", () => {
|
|
@@ -384,25 +417,48 @@ describe("collectWatchTargets", () => {
|
|
|
384
417
|
});
|
|
385
418
|
|
|
386
419
|
/**
|
|
387
|
-
* Build a fake `exec`
|
|
388
|
-
*
|
|
389
|
-
* (
|
|
390
|
-
*
|
|
420
|
+
* Build a STATEFUL fake `exec` modeling the Docker daemon + Colima interplay:
|
|
421
|
+
* `docker info` succeeds only while `daemonUp`; a `colima start` heals the
|
|
422
|
+
* daemon (unless the fake is told to model the zombie state, where Colima's
|
|
423
|
+
* state file says "already running, ignoring" but the daemon stays dead —
|
|
424
|
+
* only the stop → start cycle recovers). Injected into
|
|
425
|
+
* `ensureDockerDaemonRunning` so these cases need no `mock.module` (which is
|
|
426
|
+
* process-global and would leak across the CLI's single-process test run) and
|
|
427
|
+
* no real Docker/Colima subprocesses.
|
|
391
428
|
*/
|
|
392
|
-
function fakeExec(
|
|
429
|
+
function fakeExec(
|
|
430
|
+
opts: {
|
|
431
|
+
/** Daemon already reachable when the test starts. */
|
|
432
|
+
daemonUpAtStart?: boolean;
|
|
433
|
+
/** `colima start` exits nonzero instead of healing. */
|
|
434
|
+
startFails?: boolean;
|
|
435
|
+
/** First `colima start` is a zombie no-op that does NOT heal the daemon. */
|
|
436
|
+
zombieFirstStart?: boolean;
|
|
437
|
+
} = {},
|
|
438
|
+
) {
|
|
393
439
|
const calls: Array<{ cmd: string; args: string[] }> = [];
|
|
440
|
+
let daemonUp = opts.daemonUpAtStart ?? false;
|
|
441
|
+
let starts = 0;
|
|
394
442
|
const exec = async (cmd: string, args: string[] = []): Promise<void> => {
|
|
395
443
|
calls.push({ cmd, args });
|
|
396
|
-
if (cmd === "docker" && args[0] === "info"
|
|
397
|
-
throw new Error("Cannot connect to the Docker daemon");
|
|
444
|
+
if (cmd === "docker" && args[0] === "info") {
|
|
445
|
+
if (!daemonUp) throw new Error("Cannot connect to the Docker daemon");
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (cmd === "colima" && args[0] === "start") {
|
|
449
|
+
starts++;
|
|
450
|
+
if (opts.startFails) throw new Error("colima start exited 1");
|
|
451
|
+
if (!opts.zombieFirstStart || starts > 1) daemonUp = true;
|
|
452
|
+
return;
|
|
398
453
|
}
|
|
399
|
-
if (cmd === "colima" && args[0] === "
|
|
400
|
-
|
|
454
|
+
if (cmd === "colima" && args[0] === "stop") {
|
|
455
|
+
daemonUp = false;
|
|
456
|
+
return;
|
|
401
457
|
}
|
|
402
458
|
};
|
|
403
459
|
const ran = (cmd: string, sub: string): boolean =>
|
|
404
460
|
calls.some((c) => c.cmd === cmd && c.args[0] === sub);
|
|
405
|
-
return { exec
|
|
461
|
+
return { exec, ran };
|
|
406
462
|
}
|
|
407
463
|
|
|
408
464
|
describe("ensureDockerDaemonRunning", () => {
|
|
@@ -410,30 +466,122 @@ describe("ensureDockerDaemonRunning", () => {
|
|
|
410
466
|
// Colima, because that switches the active `docker context` and points
|
|
411
467
|
// `docker start` at a VM that never held the instance's containers.
|
|
412
468
|
test("reuses an already-reachable daemon (e.g. Docker Desktop) and never touches Colima", async () => {
|
|
413
|
-
const { exec, ran } = fakeExec({
|
|
469
|
+
const { exec, ran } = fakeExec({ daemonUpAtStart: true });
|
|
414
470
|
|
|
415
471
|
await ensureDockerDaemonRunning(exec);
|
|
416
472
|
|
|
417
473
|
expect(ran("docker", "info")).toBe(true);
|
|
418
|
-
expect(ran("colima", "status")).toBe(false);
|
|
419
474
|
expect(ran("colima", "start")).toBe(false);
|
|
475
|
+
expect(ran("colima", "stop")).toBe(false);
|
|
420
476
|
});
|
|
421
477
|
|
|
422
|
-
test("starts Colima
|
|
423
|
-
const { exec, ran } = fakeExec(
|
|
478
|
+
test("starts Colima when no daemon is reachable and confirms the daemon answered", async () => {
|
|
479
|
+
const { exec, ran } = fakeExec();
|
|
424
480
|
|
|
425
|
-
await ensureDockerDaemonRunning(exec);
|
|
481
|
+
await ensureDockerDaemonRunning(exec, { settleTimeoutMs: 0 });
|
|
426
482
|
|
|
427
483
|
expect(ran("docker", "info")).toBe(true);
|
|
428
484
|
expect(ran("colima", "start")).toBe(true);
|
|
485
|
+
expect(ran("colima", "stop")).toBe(false);
|
|
429
486
|
});
|
|
430
487
|
|
|
431
|
-
|
|
432
|
-
|
|
488
|
+
// Regression (v0.12.8 release feedback): a zombie Colima — stale state file
|
|
489
|
+
// saying "already running" while the daemon inside is dead — used to sail
|
|
490
|
+
// through wake (no `colima status` oracle anymore) and leave every later
|
|
491
|
+
// docker command dying on the unreachable socket.
|
|
492
|
+
test("recovers a zombie Colima: start no-ops, daemon stays dead → stop --force → start → daemon up", async () => {
|
|
493
|
+
const { exec, ran } = fakeExec({ zombieFirstStart: true });
|
|
433
494
|
|
|
434
|
-
await ensureDockerDaemonRunning(exec);
|
|
495
|
+
await ensureDockerDaemonRunning(exec, { settleTimeoutMs: 0 });
|
|
435
496
|
|
|
436
|
-
expect(ran("colima", "
|
|
437
|
-
expect(ran("colima", "
|
|
497
|
+
expect(ran("colima", "start")).toBe(true);
|
|
498
|
+
expect(ran("colima", "stop")).toBe(true);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("never deletes the Colima VM — the VM disk holds every image and volume", async () => {
|
|
502
|
+
const { exec, ran } = fakeExec({ zombieFirstStart: true });
|
|
503
|
+
|
|
504
|
+
await ensureDockerDaemonRunning(exec, { settleTimeoutMs: 0 });
|
|
505
|
+
|
|
506
|
+
expect(ran("colima", "delete")).toBe(false);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("throws an actionable error when even the restart cannot bring the daemon up", () => {
|
|
510
|
+
// A start that always exits nonzero: the first failure surfaces its
|
|
511
|
+
// output, the recovery stop+start runs, and the final wait still finds
|
|
512
|
+
// no daemon → the helper throws instead of letting hatch proceed.
|
|
513
|
+
const { exec } = fakeExec({ startFails: true });
|
|
514
|
+
expect(
|
|
515
|
+
ensureDockerDaemonRunning(exec, { settleTimeoutMs: 0 }),
|
|
516
|
+
).rejects.toThrow(/Failed to restart Colima/);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
describe("pullImage", () => {
|
|
521
|
+
function fakePull(
|
|
522
|
+
opts: {
|
|
523
|
+
failFirstPullWith?: string;
|
|
524
|
+
failAllPullsWith?: string;
|
|
525
|
+
} = {},
|
|
526
|
+
) {
|
|
527
|
+
const calls: Array<{ cmd: string; args: string[] }> = [];
|
|
528
|
+
let pulls = 0;
|
|
529
|
+
const exec = async (cmd: string, args: string[] = []): Promise<void> => {
|
|
530
|
+
calls.push({ cmd, args });
|
|
531
|
+
if (cmd === "docker" && args[0] === "pull") {
|
|
532
|
+
pulls++;
|
|
533
|
+
if (opts.failAllPullsWith) throw new Error(opts.failAllPullsWith);
|
|
534
|
+
if (opts.failFirstPullWith && pulls === 1) {
|
|
535
|
+
throw new Error(opts.failFirstPullWith);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// `docker logout` (and anything else) succeeds silently.
|
|
539
|
+
};
|
|
540
|
+
const ran = (sub: string): boolean =>
|
|
541
|
+
calls.some((c) => c.cmd === "docker" && c.args[0] === sub);
|
|
542
|
+
return { exec, ran };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
test("pulls without touching stored credentials on success", async () => {
|
|
546
|
+
const { exec, ran } = fakePull();
|
|
547
|
+
|
|
548
|
+
await pullImage("mycadet/mycadet-assistant:v1", () => {}, exec);
|
|
549
|
+
|
|
550
|
+
expect(ran("pull")).toBe(true);
|
|
551
|
+
expect(ran("logout")).toBe(false);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test("clears stale Docker Hub credentials and retries once on an expired-token error", async () => {
|
|
555
|
+
const { exec, ran } = fakePull({
|
|
556
|
+
failFirstPullWith:
|
|
557
|
+
"Error response from daemon: authentication required - personal access token is expired",
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
await pullImage("mycadet/mycadet-assistant:v1", () => {}, exec);
|
|
561
|
+
|
|
562
|
+
expect(ran("logout")).toBe(true);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test("wraps the failure with actionable copy when the auth retry also fails", () => {
|
|
566
|
+
const { exec } = fakePull({
|
|
567
|
+
failAllPullsWith:
|
|
568
|
+
"Error response from daemon: authentication required - personal access token is expired",
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
expect(
|
|
572
|
+
pullImage("mycadet/mycadet-assistant:v1", () => {}, exec),
|
|
573
|
+
).rejects.toThrow("Failed to pull mycadet/mycadet-assistant:v1");
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
test("rethrows non-auth pull errors raw and never logs out", () => {
|
|
577
|
+
const { exec, ran } = fakePull({
|
|
578
|
+
failAllPullsWith:
|
|
579
|
+
"Cannot connect to the Docker daemon at unix:///var/run/docker.sock",
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
expect(
|
|
583
|
+
pullImage("mycadet/mycadet-assistant:v1", () => {}, exec),
|
|
584
|
+
).rejects.toThrow("Cannot connect to the Docker daemon");
|
|
585
|
+
expect(ran("logout")).toBe(false);
|
|
438
586
|
});
|
|
439
587
|
});
|
|
@@ -203,7 +203,7 @@ describe("pollJobUntilDone", () => {
|
|
|
203
203
|
expect(refreshes).toBe(1);
|
|
204
204
|
expect(calls).toBe(2);
|
|
205
205
|
// The 401 branch logs its own distinct warning (not the generic
|
|
206
|
-
// "polling failed
|
|
206
|
+
// "polling failed:" one) so operators can distinguish an
|
|
207
207
|
// auth refresh from a transient-error retry in the output.
|
|
208
208
|
expect(warnSpy).toHaveBeenCalledWith(
|
|
209
209
|
expect.stringContaining("refreshing auth"),
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { LocalInstanceResources } from "../assistant-config.js";
|
|
4
|
+
import { gatewayCliInvocation } from "../local.js";
|
|
5
|
+
|
|
6
|
+
function fakeResources(instanceDir: string): LocalInstanceResources {
|
|
7
|
+
// gatewayCliInvocation only reads instanceDir; the rest of the resources
|
|
8
|
+
// shape is irrelevant here.
|
|
9
|
+
return { instanceDir } as unknown as LocalInstanceResources;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("gatewayCliInvocation", () => {
|
|
13
|
+
test("runs the gateway package's CLI entry through bun", () => {
|
|
14
|
+
const invocation = gatewayCliInvocation();
|
|
15
|
+
expect(invocation.args[0]).toBe("run");
|
|
16
|
+
expect(invocation.args[1]).toMatch(/gateway\/src\/cli\/index\.ts$/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("pins CADET_WORKSPACE_DIR to the instance dir for named instances", () => {
|
|
20
|
+
const invocation = gatewayCliInvocation(
|
|
21
|
+
fakeResources("/data/assistants/testy"),
|
|
22
|
+
);
|
|
23
|
+
expect(invocation.env.CADET_WORKSPACE_DIR).toBe(
|
|
24
|
+
"/data/assistants/testy/workspace",
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("pins GATEWAY_SECURITY_DIR to the instance dir for named instances", () => {
|
|
29
|
+
const invocation = gatewayCliInvocation(
|
|
30
|
+
fakeResources("/data/assistants/testy"),
|
|
31
|
+
);
|
|
32
|
+
expect(invocation.env.GATEWAY_SECURITY_DIR).toBe(
|
|
33
|
+
"/data/assistants/testy/gateway-security",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("leaves the workspace resolution untouched for the default instance", () => {
|
|
38
|
+
delete process.env.CADET_WORKSPACE_DIR;
|
|
39
|
+
const invocation = gatewayCliInvocation();
|
|
40
|
+
expect(invocation.env.CADET_WORKSPACE_DIR).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
});
|
package/src/lib/cloud-client.ts
CHANGED
|
@@ -604,7 +604,6 @@ export interface CredentialInjectionParams {
|
|
|
604
604
|
bearerToken?: string;
|
|
605
605
|
assistantApiKey?: string | null;
|
|
606
606
|
cloudAssistantId: string;
|
|
607
|
-
cloudBaseUrl: string;
|
|
608
607
|
organizationId: string;
|
|
609
608
|
userId?: string;
|
|
610
609
|
webhookSecret?: string | null;
|
|
@@ -631,8 +630,6 @@ export async function injectCredentialsIntoAssistant(
|
|
|
631
630
|
promises.push(inject("cadet:assistant_api_key", params.assistantApiKey));
|
|
632
631
|
}
|
|
633
632
|
|
|
634
|
-
promises.push(inject("cadet:platform_base_url", params.cloudBaseUrl));
|
|
635
|
-
|
|
636
633
|
if (params.webhookSecret) {
|
|
637
634
|
promises.push(inject("cadet:webhook_secret", params.webhookSecret));
|
|
638
635
|
}
|
package/src/lib/cloud-login.ts
CHANGED
|
@@ -409,7 +409,6 @@ export async function registerLocalAssistantWithPlatform(
|
|
|
409
409
|
bearerToken: options.bearerToken ?? entry.bearerToken,
|
|
410
410
|
assistantApiKey,
|
|
411
411
|
cloudAssistantId: registration.assistant.id,
|
|
412
|
-
cloudBaseUrl: cloudUrl,
|
|
413
412
|
organizationId: orgId,
|
|
414
413
|
userId: user.id,
|
|
415
414
|
webhookSecret: registration.webhook_secret,
|
|
@@ -85,7 +85,9 @@ export async function fetchLatestStableVersion(): Promise<string | null> {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* Image refs for a target version: the tag is the target version
|
|
88
|
+
* Image refs for a target version: the tag is the target version exactly as
|
|
89
|
+
* the release workflows push it — bare ("0.12.8", "0.12.8-dev…"); a
|
|
90
|
+
* git-tag-style "v" prefix on the input is tolerated and stripped. The repo
|
|
89
91
|
* follows the CLI's own version shape (dev CLI → `-dev` repos).
|
|
90
92
|
*/
|
|
91
93
|
export function resolveImageRefs(
|
|
@@ -93,8 +95,8 @@ export function resolveImageRefs(
|
|
|
93
95
|
cliVersion: string,
|
|
94
96
|
): Record<ServiceName, string> {
|
|
95
97
|
const versionTag = targetVersion.startsWith("v")
|
|
96
|
-
? targetVersion
|
|
97
|
-
:
|
|
98
|
+
? targetVersion.slice(1)
|
|
99
|
+
: targetVersion;
|
|
98
100
|
const images = dockerhubImages(cliVersion);
|
|
99
101
|
return {
|
|
100
102
|
assistant: `${images.assistant}:${versionTag}`,
|
|
@@ -107,5 +109,5 @@ export function resolveImageRefs(
|
|
|
107
109
|
export function resolveOwnImageRefs(
|
|
108
110
|
cliVersion: string,
|
|
109
111
|
): Record<ServiceName, string> {
|
|
110
|
-
return resolveImageRefs(cliVersion
|
|
112
|
+
return resolveImageRefs(cliVersion || "latest", cliVersion);
|
|
111
113
|
}
|
package/src/lib/color.ts
CHANGED
|
@@ -14,3 +14,7 @@ export function red(text: string, stream?: NodeJS.WriteStream): string {
|
|
|
14
14
|
export function yellow(text: string, stream?: NodeJS.WriteStream): string {
|
|
15
15
|
return colorize("\x1b[33m", text, stream);
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export function green(text: string, stream?: NodeJS.WriteStream): string {
|
|
19
|
+
return colorize("\x1b[32m", text, stream);
|
|
20
|
+
}
|