@vellumai/cli 0.6.0 → 0.6.1

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "../lib/assistant-config";
5
5
  import type { AssistantEntry } from "../lib/assistant-config";
6
6
  import {
7
- fetchOrganizationId,
7
+ authHeaders,
8
8
  getPlatformUrl,
9
9
  readPlatformToken,
10
10
  } from "../lib/platform-client";
@@ -93,16 +93,11 @@ async function retireVellum(
93
93
  process.exit(1);
94
94
  }
95
95
 
96
- const orgId = await fetchOrganizationId(token, runtimeUrl);
97
-
98
96
  const platformUrl = runtimeUrl || getPlatformUrl();
99
97
  const url = `${platformUrl}/v1/assistants/${encodeURIComponent(assistantId)}/retire/`;
100
98
  const response = await fetch(url, {
101
99
  method: "DELETE",
102
- headers: {
103
- "X-Session-Token": token,
104
- "Vellum-Organization-Id": orgId,
105
- },
100
+ headers: await authHeaders(token, runtimeUrl),
106
101
  });
107
102
 
108
103
  if (!response.ok) {
@@ -17,7 +17,6 @@ import {
17
17
  import type { ServiceName } from "../lib/docker";
18
18
  import { emitCliError, categorizeUpgradeError } from "../lib/cli-error.js";
19
19
  import {
20
- fetchOrganizationId,
21
20
  readPlatformToken,
22
21
  rollbackPlatformAssistant,
23
22
  } from "../lib/platform-client.js";
@@ -35,7 +34,7 @@ import {
35
34
  UPGRADE_PROGRESS,
36
35
  waitForReady,
37
36
  } from "../lib/upgrade-lifecycle.js";
38
- import { parseVersion } from "../lib/version-compat.js";
37
+ import { compareVersions } from "../lib/version-compat.js";
39
38
 
40
39
  function parseArgs(): { name: string | null; version: string | null } {
41
40
  const args = process.argv.slice(3);
@@ -153,21 +152,12 @@ async function rollbackPlatformViaEndpoint(
153
152
 
154
153
  // Step 1 — Version validation (only if version provided)
155
154
  if (version && currentVersion) {
156
- const current = parseVersion(currentVersion);
157
- const target = parseVersion(version);
158
- if (current && target) {
159
- const isOlder =
160
- target.major < current.major ||
161
- (target.major === current.major && target.minor < current.minor) ||
162
- (target.major === current.major &&
163
- target.minor === current.minor &&
164
- target.patch < current.patch);
165
- if (!isOlder) {
166
- const msg = `Target version ${version} is not older than the current version ${currentVersion}. Use \`vellum upgrade --version ${version}\` to upgrade.`;
167
- console.error(msg);
168
- emitCliError("VERSION_DIRECTION", msg);
169
- process.exit(1);
170
- }
155
+ const cmp = compareVersions(version, currentVersion);
156
+ if (cmp !== null && cmp >= 0) {
157
+ const msg = `Target version ${version} is not older than the current version ${currentVersion}. Use \`vellum upgrade --version ${version}\` to upgrade.`;
158
+ console.error(msg);
159
+ emitCliError("VERSION_DIRECTION", msg);
160
+ process.exit(1);
171
161
  }
172
162
  }
173
163
 
@@ -181,20 +171,6 @@ async function rollbackPlatformViaEndpoint(
181
171
  process.exit(1);
182
172
  }
183
173
 
184
- let orgId: string;
185
- try {
186
- orgId = await fetchOrganizationId(token, entry.runtimeUrl);
187
- } catch (err) {
188
- const msg = err instanceof Error ? err.message : String(err);
189
- if (msg.includes("401") || msg.includes("403")) {
190
- console.error("Authentication failed. Run 'vellum login' to refresh.");
191
- } else {
192
- console.error(`Error: ${msg}`);
193
- }
194
- emitCliError("AUTH_FAILED", "Failed to authenticate with platform", msg);
195
- process.exit(1);
196
- }
197
-
198
174
  // Step 3 — Call rollback endpoint
199
175
  if (version) {
200
176
  console.log(`Rolling back to ${version}...`);
@@ -204,12 +180,7 @@ async function rollbackPlatformViaEndpoint(
204
180
 
205
181
  let result: { detail: string; version: string | null };
206
182
  try {
207
- result = await rollbackPlatformAssistant(
208
- token,
209
- orgId,
210
- version,
211
- entry.runtimeUrl,
212
- );
183
+ result = await rollbackPlatformAssistant(token, version, entry.runtimeUrl);
213
184
  } catch (err) {
214
185
  const detail = err instanceof Error ? err.message : String(err);
215
186
 
@@ -12,7 +12,6 @@ import {
12
12
  } from "../lib/guardian-token.js";
13
13
  import {
14
14
  readPlatformToken,
15
- fetchOrganizationId,
16
15
  getPlatformUrl,
17
16
  hatchAssistant,
18
17
  platformInitiateExport,
@@ -423,7 +422,7 @@ async function importViaHttp(
423
422
  "Content-Type": "application/octet-stream",
424
423
  },
425
424
  body: new Blob([bundleData]),
426
- signal: AbortSignal.timeout(120_000),
425
+ signal: AbortSignal.timeout(300_000),
427
426
  });
428
427
 
429
428
  // Retry once with a fresh token on 401
@@ -447,13 +446,13 @@ async function importViaHttp(
447
446
  "Content-Type": "application/octet-stream",
448
447
  },
449
448
  body: new Blob([bundleData]),
450
- signal: AbortSignal.timeout(120_000),
449
+ signal: AbortSignal.timeout(300_000),
451
450
  });
452
451
  }
453
452
  }
454
453
  } catch (err) {
455
454
  if (err instanceof Error && err.name === "TimeoutError") {
456
- console.error("Error: Import request timed out after 2 minutes.");
455
+ console.error("Error: Import request timed out after 5 minutes.");
457
456
  process.exit(1);
458
457
  }
459
458
  const msg = err instanceof Error ? err.message : String(err);
@@ -489,36 +488,12 @@ async function exportFromAssistant(
489
488
  process.exit(1);
490
489
  }
491
490
 
492
- let orgId: string;
493
- try {
494
- orgId = await fetchOrganizationId(token, entry.runtimeUrl);
495
- } catch (err) {
496
- const msg = err instanceof Error ? err.message : String(err);
497
- if (msg.includes("401") || msg.includes("403")) {
498
- console.error("Authentication failed. Run 'vellum login' to refresh.");
499
- process.exit(1);
500
- }
501
- throw err;
502
- }
503
-
504
491
  // Initiate export job
505
- let jobId: string;
506
- try {
507
- const result = await platformInitiateExport(
508
- token,
509
- orgId,
510
- "teleport export",
511
- entry.runtimeUrl,
512
- );
513
- jobId = result.jobId;
514
- } catch (err) {
515
- const msg = err instanceof Error ? err.message : String(err);
516
- if (msg.includes("401") || msg.includes("403")) {
517
- console.error("Authentication failed. Run 'vellum login' to refresh.");
518
- process.exit(1);
519
- }
520
- throw err;
521
- }
492
+ const { jobId } = await platformInitiateExport(
493
+ token,
494
+ "teleport export",
495
+ entry.runtimeUrl,
496
+ );
522
497
 
523
498
  console.log(`Export started (job ${jobId})...`);
524
499
 
@@ -531,12 +506,7 @@ async function exportFromAssistant(
531
506
  while (Date.now() < deadline) {
532
507
  let status: { status: string; downloadUrl?: string; error?: string };
533
508
  try {
534
- status = await platformPollExportStatus(
535
- jobId,
536
- token,
537
- orgId,
538
- entry.runtimeUrl,
539
- );
509
+ status = await platformPollExportStatus(jobId, token, entry.runtimeUrl);
540
510
  } catch (err) {
541
511
  const msg = err instanceof Error ? err.message : String(err);
542
512
  if (msg.includes("not found")) {
@@ -642,18 +612,6 @@ async function importToAssistant(
642
612
  process.exit(1);
643
613
  }
644
614
 
645
- let orgId: string;
646
- try {
647
- orgId = await fetchOrganizationId(token, entry.runtimeUrl);
648
- } catch (err) {
649
- const msg = err instanceof Error ? err.message : String(err);
650
- if (msg.includes("401") || msg.includes("403")) {
651
- console.error("Authentication failed. Run 'vellum login' to refresh.");
652
- process.exit(1);
653
- }
654
- throw err;
655
- }
656
-
657
615
  // Use pre-uploaded bundle key if provided (string), skip upload if null
658
616
  // (signals signed URLs were already tried and unavailable), or try
659
617
  // signed-URL upload if undefined (never attempted).
@@ -663,7 +621,6 @@ async function importToAssistant(
663
621
  try {
664
622
  const { uploadUrl, bundleKey: key } = await platformRequestUploadUrl(
665
623
  token,
666
- orgId,
667
624
  entry.runtimeUrl,
668
625
  );
669
626
  bundleKey = key;
@@ -692,15 +649,9 @@ async function importToAssistant(
692
649
  ? await platformImportPreflightFromGcs(
693
650
  bundleKey,
694
651
  token,
695
- orgId,
696
652
  entry.runtimeUrl,
697
653
  )
698
- : await platformImportPreflight(
699
- bundleData,
700
- token,
701
- orgId,
702
- entry.runtimeUrl,
703
- );
654
+ : await platformImportPreflight(bundleData, token, entry.runtimeUrl);
704
655
  } catch (err) {
705
656
  if (err instanceof Error && err.name === "TimeoutError") {
706
657
  console.error("Error: Preflight request timed out after 2 minutes.");
@@ -751,21 +702,11 @@ async function importToAssistant(
751
702
  let importResult: { statusCode: number; body: Record<string, unknown> };
752
703
  try {
753
704
  importResult = bundleKey
754
- ? await platformImportBundleFromGcs(
755
- bundleKey,
756
- token,
757
- orgId,
758
- entry.runtimeUrl,
759
- )
760
- : await platformImportBundle(
761
- bundleData,
762
- token,
763
- orgId,
764
- entry.runtimeUrl,
765
- );
705
+ ? await platformImportBundleFromGcs(bundleKey, token, entry.runtimeUrl)
706
+ : await platformImportBundle(bundleData, token, entry.runtimeUrl);
766
707
  } catch (err) {
767
708
  if (err instanceof Error && err.name === "TimeoutError") {
768
- console.error("Error: Import request timed out after 2 minutes.");
709
+ console.error("Error: Import request timed out after 5 minutes.");
769
710
  process.exit(1);
770
711
  }
771
712
  throw err;
@@ -796,7 +737,6 @@ async function importToAssistant(
796
737
  export async function resolveOrHatchTarget(
797
738
  targetEnv: "local" | "docker" | "platform",
798
739
  targetName?: string,
799
- orgId?: string,
800
740
  ): Promise<AssistantEntry> {
801
741
  // If a name is provided, try to find an existing assistant
802
742
  if (targetName) {
@@ -876,25 +816,7 @@ export async function resolveOrHatchTarget(
876
816
  process.exit(1);
877
817
  }
878
818
 
879
- let resolvedOrgId: string;
880
- if (orgId) {
881
- resolvedOrgId = orgId;
882
- } else {
883
- try {
884
- resolvedOrgId = await fetchOrganizationId(token);
885
- } catch (err) {
886
- const msg = err instanceof Error ? err.message : String(err);
887
- if (msg.includes("401") || msg.includes("403")) {
888
- console.error(
889
- "Authentication failed. Run 'vellum login' to refresh.",
890
- );
891
- process.exit(1);
892
- }
893
- throw err;
894
- }
895
- }
896
-
897
- const result = await hatchAssistant(token, resolvedOrgId);
819
+ const result = await hatchAssistant(token);
898
820
  const entry: AssistantEntry = {
899
821
  assistantId: result.id,
900
822
  runtimeUrl: getPlatformUrl(),
@@ -1170,7 +1092,7 @@ export async function teleport(): Promise<void> {
1170
1092
  // Platform target: reordered flow — upload to GCS before hatching so that
1171
1093
  // if upload fails, no empty assistant is left dangling on the platform.
1172
1094
  if (targetEnv === "platform") {
1173
- // Step B — Auth + Org ID
1095
+ // Step B — Auth
1174
1096
  const token = readPlatformToken();
1175
1097
  if (!token) {
1176
1098
  console.error("Not logged in. Run 'vellum login' first.");
@@ -1191,22 +1113,10 @@ export async function teleport(): Promise<void> {
1191
1113
  }
1192
1114
  }
1193
1115
 
1194
- // Use the existing target's runtimeUrl for all platform calls so upload,
1195
- // org ID fetch, and import hit the same instance.
1116
+ // Use the existing target's runtimeUrl for all platform calls so upload
1117
+ // and import hit the same instance.
1196
1118
  const targetPlatformUrl = existingTarget?.runtimeUrl;
1197
1119
 
1198
- let orgId: string;
1199
- try {
1200
- orgId = await fetchOrganizationId(token, targetPlatformUrl);
1201
- } catch (err) {
1202
- const msg = err instanceof Error ? err.message : String(err);
1203
- if (msg.includes("401") || msg.includes("403")) {
1204
- console.error("Authentication failed. Run 'vellum login' to refresh.");
1205
- process.exit(1);
1206
- }
1207
- throw err;
1208
- }
1209
-
1210
1120
  // Step C — Upload to GCS
1211
1121
  // bundleKey: string = uploaded successfully, null = tried but unavailable,
1212
1122
  // undefined would mean "never tried" (not used here).
@@ -1214,7 +1124,6 @@ export async function teleport(): Promise<void> {
1214
1124
  try {
1215
1125
  const { uploadUrl, bundleKey: key } = await platformRequestUploadUrl(
1216
1126
  token,
1217
- orgId,
1218
1127
  targetPlatformUrl,
1219
1128
  );
1220
1129
  bundleKey = key;
@@ -1231,7 +1140,7 @@ export async function teleport(): Promise<void> {
1231
1140
  }
1232
1141
 
1233
1142
  // Step D — Hatch (upload succeeded or fallback to inline — safe to hatch)
1234
- const toEntry = await resolveOrHatchTarget(targetEnv, targetName, orgId);
1143
+ const toEntry = await resolveOrHatchTarget(targetEnv, targetName);
1235
1144
  const toCloud = resolveCloud(toEntry);
1236
1145
 
1237
1146
  // Step E — Import from GCS (or inline fallback)
@@ -18,7 +18,7 @@ import {
18
18
  } from "../lib/docker";
19
19
  import { resolveImageRefs } from "../lib/platform-releases";
20
20
  import {
21
- fetchOrganizationId,
21
+ authHeaders,
22
22
  getPlatformUrl,
23
23
  readPlatformToken,
24
24
  } from "../lib/platform-client";
@@ -42,7 +42,7 @@ import {
42
42
  UPGRADE_PROGRESS,
43
43
  waitForReady,
44
44
  } from "../lib/upgrade-lifecycle.js";
45
- import { parseVersion } from "../lib/version-compat.js";
45
+ import { compareVersions } from "../lib/version-compat.js";
46
46
 
47
47
  interface UpgradeArgs {
48
48
  name: string | null;
@@ -193,21 +193,12 @@ async function upgradeDocker(
193
193
  // Users should use `vellum rollback --version <version>` for downgrades.
194
194
  const currentVersion = entry.serviceGroupVersion;
195
195
  if (currentVersion && versionTag) {
196
- const current = parseVersion(currentVersion);
197
- const target = parseVersion(versionTag);
198
- if (current && target) {
199
- const isOlder =
200
- target.major < current.major ||
201
- (target.major === current.major && target.minor < current.minor) ||
202
- (target.major === current.major &&
203
- target.minor === current.minor &&
204
- target.patch < current.patch);
205
- if (isOlder) {
206
- const msg = `Cannot upgrade to an older version (${versionTag} < ${currentVersion}). Use \`vellum rollback --version ${versionTag}\` instead.`;
207
- console.error(msg);
208
- emitCliError("VERSION_DIRECTION", msg);
209
- process.exit(1);
210
- }
196
+ const cmp = compareVersions(versionTag, currentVersion);
197
+ if (cmp !== null && cmp < 0) {
198
+ const msg = `Cannot upgrade to an older version (${versionTag} < ${currentVersion}). Use \`vellum rollback --version ${versionTag}\` instead.`;
199
+ console.error(msg);
200
+ emitCliError("VERSION_DIRECTION", msg);
201
+ process.exit(1);
211
202
  }
212
203
  }
213
204
 
@@ -694,21 +685,12 @@ async function upgradePlatform(
694
685
  // we must not block the request based on the local CLI version.
695
686
  const currentVersion = entry.serviceGroupVersion;
696
687
  if (version && currentVersion) {
697
- const current = parseVersion(currentVersion);
698
- const target = parseVersion(version);
699
- if (current && target) {
700
- const isOlder =
701
- target.major < current.major ||
702
- (target.major === current.major && target.minor < current.minor) ||
703
- (target.major === current.major &&
704
- target.minor === current.minor &&
705
- target.patch < current.patch);
706
- if (isOlder) {
707
- const msg = `Cannot upgrade to an older version (${version} < ${currentVersion}). Use \`vellum rollback --version ${version}\` instead.`;
708
- console.error(msg);
709
- emitCliError("VERSION_DIRECTION", msg);
710
- process.exit(1);
711
- }
688
+ const cmp = compareVersions(version, currentVersion);
689
+ if (cmp !== null && cmp < 0) {
690
+ const msg = `Cannot upgrade to an older version (${version} < ${currentVersion}). Use \`vellum rollback --version ${version}\` instead.`;
691
+ console.error(msg);
692
+ emitCliError("VERSION_DIRECTION", msg);
693
+ process.exit(1);
712
694
  }
713
695
  }
714
696
 
@@ -725,7 +707,7 @@ async function upgradePlatform(
725
707
  process.exit(1);
726
708
  }
727
709
 
728
- const orgId = await fetchOrganizationId(token, entry.runtimeUrl);
710
+ const headers = await authHeaders(token, entry.runtimeUrl);
729
711
 
730
712
  const url = `${entry.runtimeUrl || getPlatformUrl()}/v1/assistants/upgrade/`;
731
713
  const body: { assistant_id?: string; version?: string } = {
@@ -737,14 +719,28 @@ async function upgradePlatform(
737
719
 
738
720
  const response = await fetch(url, {
739
721
  method: "POST",
740
- headers: {
741
- "Content-Type": "application/json",
742
- "X-Session-Token": token,
743
- "Vellum-Organization-Id": orgId,
744
- },
722
+ headers,
745
723
  body: JSON.stringify(body),
746
724
  });
747
725
 
726
+ if (response.status === 401 || response.status === 403) {
727
+ const text = await response.text();
728
+ console.error(
729
+ `Authentication failed (${response.status}). Run 'vellum login' to refresh.`,
730
+ );
731
+ emitCliError("AUTH_FAILED", "Authentication failed", text);
732
+ try {
733
+ await broadcastUpgradeEvent(
734
+ entry.runtimeUrl,
735
+ entry.assistantId,
736
+ buildCompleteEvent(entry.serviceGroupVersion ?? "unknown", false),
737
+ );
738
+ } catch {
739
+ // Best-effort — broadcast may fail if the assistant is unreachable
740
+ }
741
+ process.exit(1);
742
+ }
743
+
748
744
  if (!response.ok) {
749
745
  const text = await response.text();
750
746
  console.error(
@@ -755,11 +751,15 @@ async function upgradePlatform(
755
751
  `Platform upgrade failed (${response.status})`,
756
752
  text,
757
753
  );
758
- await broadcastUpgradeEvent(
759
- entry.runtimeUrl,
760
- entry.assistantId,
761
- buildCompleteEvent(entry.serviceGroupVersion ?? "unknown", false),
762
- );
754
+ try {
755
+ await broadcastUpgradeEvent(
756
+ entry.runtimeUrl,
757
+ entry.assistantId,
758
+ buildCompleteEvent(entry.serviceGroupVersion ?? "unknown", false),
759
+ );
760
+ } catch {
761
+ // Best-effort — broadcast may fail if the assistant is unreachable
762
+ }
763
763
  process.exit(1);
764
764
  }
765
765
 
package/src/index.ts CHANGED
@@ -4,8 +4,10 @@ import cliPkg from "../package.json";
4
4
  import { backup } from "./commands/backup";
5
5
  import { clean } from "./commands/clean";
6
6
  import { client } from "./commands/client";
7
+ import { events } from "./commands/events";
7
8
  import { hatch } from "./commands/hatch";
8
9
  import { login, logout, whoami } from "./commands/login";
10
+ import { message } from "./commands/message";
9
11
  import { pair } from "./commands/pair";
10
12
  import { ps } from "./commands/ps";
11
13
  import { recover } from "./commands/recover";
@@ -33,9 +35,11 @@ const commands = {
33
35
  backup,
34
36
  clean,
35
37
  client,
38
+ events,
36
39
  hatch,
37
40
  login,
38
41
  logout,
42
+ message,
39
43
  pair,
40
44
  ps,
41
45
  recover,
@@ -62,9 +66,11 @@ function printHelp(): void {
62
66
  console.log(" backup Export a backup of a running assistant");
63
67
  console.log(" clean Kill orphaned vellum processes");
64
68
  console.log(" client Connect to a hatched assistant");
69
+ console.log(" events Stream events from a running assistant");
65
70
  console.log(" hatch Create a new assistant instance");
66
71
  console.log(" login Log in to the Vellum platform");
67
72
  console.log(" logout Log out of the Vellum platform");
73
+ console.log(" message Send a message to a running assistant");
68
74
  console.log(" pair Pair with a remote assistant via QR code");
69
75
  console.log(
70
76
  " ps List assistants (or processes for a specific assistant)",
@@ -0,0 +1,13 @@
1
+ /** Extract a named flag's value from an arg list, returning [value, remaining]. */
2
+ export function extractFlag(
3
+ args: string[],
4
+ flag: string,
5
+ ): [string | undefined, string[]] {
6
+ const idx = args.indexOf(flag);
7
+ if (idx === -1 || idx + 1 >= args.length) {
8
+ return [undefined, args.filter((a) => a !== flag)];
9
+ }
10
+ const value = args[idx + 1]!;
11
+ const remaining = [...args.slice(0, idx), ...args.slice(idx + 2)];
12
+ return [value, remaining];
13
+ }