@vellumai/cli 0.8.11-dev.202606122025.f06346f → 0.8.11-dev.202606122104.e5b9dd5
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/@vellumai/local-mode/src/__tests__/wake.test.ts +19 -0
- package/node_modules/@vellumai/local-mode/src/index.ts +1 -1
- package/node_modules/@vellumai/local-mode/src/wake.ts +12 -1
- package/package.json +1 -1
- package/src/__tests__/wake.test.ts +15 -4
- package/src/__tests__/workos-pkce.test.ts +2 -0
- package/src/commands/wake.ts +7 -5
|
@@ -41,6 +41,25 @@ describe("runWake", () => {
|
|
|
41
41
|
expect(spawnArgs[0]).toEqual(["bun", ["run", "cli", "wake", "asst-42"]]);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
test("repairGuardian: true appends --repair-guardian to the CLI args", async () => {
|
|
45
|
+
const pending = runWake(invocation, "asst-42", { repairGuardian: true });
|
|
46
|
+
lastChild.emit("close", 0);
|
|
47
|
+
|
|
48
|
+
expect(await pending).toEqual({ ok: true });
|
|
49
|
+
expect(spawnArgs[0]).toEqual([
|
|
50
|
+
"bun",
|
|
51
|
+
["run", "cli", "wake", "asst-42", "--repair-guardian"],
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("repairGuardian: false omits the flag", async () => {
|
|
56
|
+
const pending = runWake(invocation, "asst-42", { repairGuardian: false });
|
|
57
|
+
lastChild.emit("close", 0);
|
|
58
|
+
|
|
59
|
+
expect(await pending).toEqual({ ok: true });
|
|
60
|
+
expect(spawnArgs[0]).toEqual(["bun", ["run", "cli", "wake", "asst-42"]]);
|
|
61
|
+
});
|
|
62
|
+
|
|
44
63
|
test("a non-zero exit resolves to a failure carrying the CLI's output", async () => {
|
|
45
64
|
const pending = runWake(invocation, "asst-42");
|
|
46
65
|
lastChild.stderr.emit("data", Buffer.from("no sibling environment to seed from"));
|
|
@@ -36,7 +36,7 @@ export type { HatchResult } from "./hatch";
|
|
|
36
36
|
export { runRetire } from "./retire";
|
|
37
37
|
export type { RetireResult } from "./retire";
|
|
38
38
|
export { runWake } from "./wake";
|
|
39
|
-
export type { WakeResult } from "./wake";
|
|
39
|
+
export type { WakeOptions, WakeResult } from "./wake";
|
|
40
40
|
export { getGuardianAccessToken } from "./guardian-token";
|
|
41
41
|
export type { TokenResult } from "./guardian-token";
|
|
42
42
|
export {
|
|
@@ -14,6 +14,11 @@ export type WakeResult =
|
|
|
14
14
|
| { ok: true }
|
|
15
15
|
| { ok: false; status: number; error: string };
|
|
16
16
|
|
|
17
|
+
export interface WakeOptions {
|
|
18
|
+
/** Pass --repair-guardian to re-provision a missing/expired guardian token. Revokes the assistant's other device-bound tokens, so callers must gate this behind explicit user confirmation. */
|
|
19
|
+
repairGuardian?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Start (or restart) a local assistant's daemon and gateway via the CLI's
|
|
19
24
|
* `wake`, which also re-seeds the guardian token from a sibling environment.
|
|
@@ -27,11 +32,17 @@ export type WakeResult =
|
|
|
27
32
|
export function runWake(
|
|
28
33
|
invocation: CliInvocation,
|
|
29
34
|
assistantId: string,
|
|
35
|
+
options?: WakeOptions,
|
|
30
36
|
): Promise<WakeResult> {
|
|
31
37
|
return new Promise((resolve) => {
|
|
32
38
|
const child = spawn(
|
|
33
39
|
invocation.command,
|
|
34
|
-
[
|
|
40
|
+
[
|
|
41
|
+
...invocation.baseArgs,
|
|
42
|
+
"wake",
|
|
43
|
+
assistantId,
|
|
44
|
+
...(options?.repairGuardian ? ["--repair-guardian"] : []),
|
|
45
|
+
],
|
|
35
46
|
{ stdio: ["ignore", "pipe", "pipe"] },
|
|
36
47
|
);
|
|
37
48
|
|
package/package.json
CHANGED
|
@@ -272,12 +272,23 @@ describe("vellum wake", () => {
|
|
|
272
272
|
expect(leaseGuardianTokenMock).not.toHaveBeenCalled();
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
-
test("
|
|
275
|
+
test("re-provisions even when a guardian token already exists", async () => {
|
|
276
|
+
// A connect can 401 off a token whose local state looks healthy
|
|
277
|
+
// (revoked, mis-seeded, wrong principal). The user explicitly confirmed
|
|
278
|
+
// the destructive repair, so the flag forces a re-lease instead of
|
|
279
|
+
// guessing from local token state and recreating the no-op loop.
|
|
276
280
|
process.argv = ["bun", "vellum", "wake", "--repair-guardian", "local-assistant"];
|
|
277
|
-
// loadGuardianToken returns a token by default
|
|
281
|
+
// loadGuardianToken returns a healthy-looking token by default.
|
|
278
282
|
await wake();
|
|
279
283
|
|
|
280
|
-
expect(resetGuardianBootstrapMock).
|
|
281
|
-
|
|
284
|
+
expect(resetGuardianBootstrapMock).toHaveBeenCalledWith(
|
|
285
|
+
"http://127.0.0.1:7830",
|
|
286
|
+
"generated-bootstrap-secret",
|
|
287
|
+
);
|
|
288
|
+
expect(leaseGuardianTokenMock).toHaveBeenCalledWith(
|
|
289
|
+
"http://127.0.0.1:7830",
|
|
290
|
+
"local-assistant",
|
|
291
|
+
"generated-bootstrap-secret",
|
|
292
|
+
);
|
|
282
293
|
});
|
|
283
294
|
});
|
|
@@ -84,7 +84,9 @@ describe("buildAuthorizeUrl", () => {
|
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
test("login hint is forwarded", () => {
|
|
87
|
+
// generic-examples:ignore-next-line — reason: test fixture for URL encoding, not a real email
|
|
87
88
|
const url = new URL(buildAuthorizeUrl({ ...base, loginHint: "a@b.co" }));
|
|
89
|
+
// generic-examples:ignore-next-line — reason: test fixture for URL encoding, not a real email
|
|
88
90
|
expect(url.searchParams.get("login_hint")).toBe("a@b.co");
|
|
89
91
|
|
|
90
92
|
const noHint = new URL(buildAuthorizeUrl(base));
|
package/src/commands/wake.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
import { dockerResourceNames, wakeContainers } from "../lib/docker.js";
|
|
10
10
|
import {
|
|
11
11
|
leaseGuardianToken,
|
|
12
|
-
loadGuardianToken,
|
|
13
12
|
resetGuardianBootstrap,
|
|
14
13
|
seedGuardianTokenFromSiblingEnv,
|
|
15
14
|
} from "../lib/guardian-token.js";
|
|
@@ -43,7 +42,7 @@ export async function wake(): Promise<void> {
|
|
|
43
42
|
" --foreground Run assistant in foreground with logs printed to terminal",
|
|
44
43
|
);
|
|
45
44
|
console.log(
|
|
46
|
-
" --repair-guardian
|
|
45
|
+
" --repair-guardian Force-re-provision the guardian token (resets the\n" +
|
|
47
46
|
" gateway bootstrap and re-leases — REVOKES other device-bound\n" +
|
|
48
47
|
" tokens, so only use deliberately, never from auto-repair)",
|
|
49
48
|
);
|
|
@@ -238,8 +237,11 @@ export async function wake(): Promise<void> {
|
|
|
238
237
|
console.log(" Seeded guardian token from sibling environment.");
|
|
239
238
|
}
|
|
240
239
|
|
|
241
|
-
// Last-resort recovery (explicit `--repair-guardian` only):
|
|
242
|
-
//
|
|
240
|
+
// Last-resort recovery (explicit `--repair-guardian` only): force a
|
|
241
|
+
// re-provision. Token health can't be judged locally — a connect can 401
|
|
242
|
+
// off a token whose local expiry looks fine (revoked, mis-seeded, wrong
|
|
243
|
+
// principal) — and the user explicitly confirmed the destructive repair,
|
|
244
|
+
// so guessing "looks healthy, skip" just recreates the no-op loop. The
|
|
243
245
|
// single-use bootstrap secret may already be spent — a prior connect can
|
|
244
246
|
// lease a token that's then lost, or the gateway marks the secret consumed
|
|
245
247
|
// before the client persists it — which otherwise bricks connect into a
|
|
@@ -248,7 +250,7 @@ export async function wake(): Promise<void> {
|
|
|
248
250
|
// by the lockfile secret — mirrors the macOS client's forceReBootstrap), then
|
|
249
251
|
// re-lease. Gated behind the flag because the re-lease revokes other
|
|
250
252
|
// device-bound tokens; it must never run from the automatic repair path.
|
|
251
|
-
if (repairGuardian
|
|
253
|
+
if (repairGuardian) {
|
|
252
254
|
const loopbackUrl = `http://127.0.0.1:${resources.gatewayPort}`;
|
|
253
255
|
const maxAttempts = 3;
|
|
254
256
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|