@vellumai/cli 0.10.12-dev.202607250140.9979030 → 0.10.12-dev.202607250508.d09ff97

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/cli",
3
- "version": "0.10.12-dev.202607250140.9979030",
3
+ "version": "0.10.12-dev.202607250508.d09ff97",
4
4
  "description": "CLI tools for vellum-assistant",
5
5
  "type": "module",
6
6
  "exports": {
@@ -179,7 +179,7 @@ mock.module("../lib/nginx-ingress.js", () => ({
179
179
  startRemoteWebIngress: startRemoteWebIngressMock,
180
180
  }));
181
181
 
182
- const { wake } = await import("../commands/wake.js");
182
+ const { wake, WEB_INGRESS_FLAG_RETRY } = await import("../commands/wake.js");
183
183
 
184
184
  let tempDir: string;
185
185
  let originalArgv: string[];
@@ -408,8 +408,18 @@ describe("vellum wake — web ingress restore", () => {
408
408
  ingress: { enabled: true, publicBaseUrl: "https://assistant.example.com" },
409
409
  };
410
410
 
411
+ const originalRetry = { ...WEB_INGRESS_FLAG_RETRY };
412
+
411
413
  beforeEach(() => {
412
414
  process.argv = ["bun", "vellum", "wake", "local-assistant"];
415
+ // Shrink the startup-window retry so failure-path tests stay fast.
416
+ WEB_INGRESS_FLAG_RETRY.attempts = 3;
417
+ WEB_INGRESS_FLAG_RETRY.intervalMs = 5;
418
+ });
419
+
420
+ afterEach(() => {
421
+ WEB_INGRESS_FLAG_RETRY.attempts = originalRetry.attempts;
422
+ WEB_INGRESS_FLAG_RETRY.intervalMs = originalRetry.intervalMs;
413
423
  });
414
424
 
415
425
  test("restores the edge when ingress is enabled, the flag is on, and it is not already running", async () => {
@@ -515,8 +525,35 @@ describe("vellum wake — web ingress restore", () => {
515
525
 
516
526
  await wake();
517
527
 
528
+ // The probe retries through the startup window before giving up.
529
+ expect(isAssistantFeatureFlagEnabledMock.mock.calls.length).toBe(3);
518
530
  expect(startRemoteWebIngressMock).not.toHaveBeenCalled();
519
531
  expect(warnSpy).toHaveBeenCalled();
520
532
  expect(logSpy).toHaveBeenCalledWith("Wake complete.");
521
533
  });
534
+
535
+ test("a flag probe that fails while the gateway is starting retries and then restores", async () => {
536
+ loadRawConfigMock.mockReturnValue(enabledConfig);
537
+ isAssistantFeatureFlagEnabledMock
538
+ .mockRejectedValueOnce(
539
+ new Error(
540
+ 'Failed to fetch feature flags: HTTP 503 {"status":"starting"}',
541
+ ),
542
+ )
543
+ .mockRejectedValueOnce(
544
+ new Error(
545
+ 'Failed to fetch feature flags: HTTP 503 {"status":"starting"}',
546
+ ),
547
+ )
548
+ .mockResolvedValueOnce(true);
549
+
550
+ await wake();
551
+
552
+ expect(isAssistantFeatureFlagEnabledMock.mock.calls.length).toBe(3);
553
+ expect(logSpy).toHaveBeenCalledWith(
554
+ " Waiting for the gateway before restoring the web ingress edge...",
555
+ );
556
+ expect(startRemoteWebIngressMock).toHaveBeenCalled();
557
+ expect(logSpy).toHaveBeenCalledWith("Wake complete.");
558
+ });
522
559
  });
@@ -390,7 +390,7 @@ export async function pair(): Promise<void> {
390
390
  `Re-run with a reachable URL, e.g.:\n vellum pair --url ${suggestion}`,
391
391
  );
392
392
  console.error(
393
- "Pairing a phone by QR instead? Use: vellum pair --qr " +
393
+ "Pairing another device by QR instead? Use: vellum pair --qr " +
394
394
  "(needs an https URL — run `vellum tunnel --provider tailscale` first).",
395
395
  );
396
396
  process.exit(1);
@@ -478,7 +478,7 @@ export async function pair(): Promise<void> {
478
478
  }'s website, not your assistant's address`,
479
479
  };
480
480
  console.error(
481
- "Error: --qr needs a public https URL the phone can open — " +
481
+ "Error: --qr needs a public https URL the scanning device can open — " +
482
482
  `${detailByReason[qrResult.reason]}.`,
483
483
  );
484
484
  console.error(
@@ -425,6 +425,52 @@ export async function wake(): Promise<void> {
425
425
  }
426
426
  }
427
427
 
428
+ /**
429
+ * Retry policy for the flag probe that gates the web-ingress restore. The
430
+ * gateway has typically been up for milliseconds at this point and answers
431
+ * `503 {"status":"starting"}` (or refuses connections) until its startup
432
+ * completes, so a single probe races it. Mutable so tests can shrink the
433
+ * window.
434
+ */
435
+ export const WEB_INGRESS_FLAG_RETRY = {
436
+ attempts: 15,
437
+ intervalMs: 2_000,
438
+ };
439
+
440
+ /**
441
+ * Probe the `web-remote-ingress` flag, riding out the gateway's startup
442
+ * window: transient failures retry on an interval until the attempt budget is
443
+ * spent, then the last error propagates to the caller's warn path.
444
+ */
445
+ async function verifyWebIngressFlagWithRetry(
446
+ assistantId: string,
447
+ gatewayPort: number,
448
+ ): Promise<boolean> {
449
+ let lastError: unknown;
450
+ for (let attempt = 1; attempt <= WEB_INGRESS_FLAG_RETRY.attempts; attempt++) {
451
+ try {
452
+ return await isAssistantFeatureFlagEnabled(
453
+ assistantId,
454
+ WEB_REMOTE_INGRESS_FLAG,
455
+ { runtimeUrl: `http://127.0.0.1:${gatewayPort}` },
456
+ );
457
+ } catch (err) {
458
+ lastError = err;
459
+ if (attempt === 1) {
460
+ console.log(
461
+ " Waiting for the gateway before restoring the web ingress edge...",
462
+ );
463
+ }
464
+ if (attempt < WEB_INGRESS_FLAG_RETRY.attempts) {
465
+ await new Promise((resolve) =>
466
+ setTimeout(resolve, WEB_INGRESS_FLAG_RETRY.intervalMs),
467
+ );
468
+ }
469
+ }
470
+ }
471
+ throw lastError;
472
+ }
473
+
428
474
  /**
429
475
  * Bring the nginx web ingress edge back up after a wake when the workspace
430
476
  * config still wants it. Only restores when ingress is explicitly enabled with
@@ -460,11 +506,7 @@ async function restoreWebIngressIfEnabled(
460
506
 
461
507
  let flagEnabled: boolean;
462
508
  try {
463
- flagEnabled = await isAssistantFeatureFlagEnabled(
464
- assistantId,
465
- WEB_REMOTE_INGRESS_FLAG,
466
- { runtimeUrl: `http://127.0.0.1:${gatewayPort}` },
467
- );
509
+ flagEnabled = await verifyWebIngressFlagWithRetry(assistantId, gatewayPort);
468
510
  } catch (err) {
469
511
  console.warn(
470
512
  ` Could not verify the \`${WEB_REMOTE_INGRESS_FLAG}\` flag to restore the web ingress edge; leaving it down. Bring it up manually with \`vellum nginx-ingress up\`. ${