@vellumai/cli 0.5.16 → 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.
- package/bun.lock +46 -52
- package/package.json +1 -1
- package/src/__tests__/teleport.test.ts +430 -4
- package/src/__tests__/version-compat.test.ts +206 -0
- package/src/commands/backup.ts +1 -15
- package/src/commands/events.ts +146 -0
- package/src/commands/message.ts +105 -0
- package/src/commands/restore.ts +1 -21
- package/src/commands/retire.ts +2 -7
- package/src/commands/rollback.ts +14 -37
- package/src/commands/teleport.ts +125 -65
- package/src/commands/upgrade.ts +50 -43
- package/src/index.ts +6 -0
- package/src/lib/arg-utils.ts +13 -0
- package/src/lib/assistant-client.ts +228 -0
- package/src/lib/aws.ts +2 -1
- package/src/lib/constants.ts +0 -11
- package/src/lib/docker.ts +168 -62
- package/src/lib/gcp.ts +2 -5
- package/src/lib/hatch-local.ts +5 -2
- package/src/lib/health-check.ts +3 -8
- package/src/lib/ngrok.ts +11 -1
- package/src/lib/platform-client.ts +191 -36
- package/src/lib/upgrade-lifecycle.ts +13 -15
- package/src/lib/version-compat.ts +67 -5
- package/src/shared/provider-env-vars.ts +19 -0
package/src/commands/teleport.ts
CHANGED
|
@@ -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,
|
|
@@ -20,6 +19,10 @@ import {
|
|
|
20
19
|
platformDownloadExport,
|
|
21
20
|
platformImportPreflight,
|
|
22
21
|
platformImportBundle,
|
|
22
|
+
platformRequestUploadUrl,
|
|
23
|
+
platformUploadToSignedUrl,
|
|
24
|
+
platformImportPreflightFromGcs,
|
|
25
|
+
platformImportBundleFromGcs,
|
|
23
26
|
} from "../lib/platform-client.js";
|
|
24
27
|
import {
|
|
25
28
|
hatchDocker,
|
|
@@ -419,7 +422,7 @@ async function importViaHttp(
|
|
|
419
422
|
"Content-Type": "application/octet-stream",
|
|
420
423
|
},
|
|
421
424
|
body: new Blob([bundleData]),
|
|
422
|
-
signal: AbortSignal.timeout(
|
|
425
|
+
signal: AbortSignal.timeout(300_000),
|
|
423
426
|
});
|
|
424
427
|
|
|
425
428
|
// Retry once with a fresh token on 401
|
|
@@ -443,13 +446,13 @@ async function importViaHttp(
|
|
|
443
446
|
"Content-Type": "application/octet-stream",
|
|
444
447
|
},
|
|
445
448
|
body: new Blob([bundleData]),
|
|
446
|
-
signal: AbortSignal.timeout(
|
|
449
|
+
signal: AbortSignal.timeout(300_000),
|
|
447
450
|
});
|
|
448
451
|
}
|
|
449
452
|
}
|
|
450
453
|
} catch (err) {
|
|
451
454
|
if (err instanceof Error && err.name === "TimeoutError") {
|
|
452
|
-
console.error("Error: Import request timed out after
|
|
455
|
+
console.error("Error: Import request timed out after 5 minutes.");
|
|
453
456
|
process.exit(1);
|
|
454
457
|
}
|
|
455
458
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -485,36 +488,12 @@ async function exportFromAssistant(
|
|
|
485
488
|
process.exit(1);
|
|
486
489
|
}
|
|
487
490
|
|
|
488
|
-
let orgId: string;
|
|
489
|
-
try {
|
|
490
|
-
orgId = await fetchOrganizationId(token, entry.runtimeUrl);
|
|
491
|
-
} catch (err) {
|
|
492
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
493
|
-
if (msg.includes("401") || msg.includes("403")) {
|
|
494
|
-
console.error("Authentication failed. Run 'vellum login' to refresh.");
|
|
495
|
-
process.exit(1);
|
|
496
|
-
}
|
|
497
|
-
throw err;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
491
|
// Initiate export job
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
"teleport export",
|
|
507
|
-
entry.runtimeUrl,
|
|
508
|
-
);
|
|
509
|
-
jobId = result.jobId;
|
|
510
|
-
} catch (err) {
|
|
511
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
512
|
-
if (msg.includes("401") || msg.includes("403")) {
|
|
513
|
-
console.error("Authentication failed. Run 'vellum login' to refresh.");
|
|
514
|
-
process.exit(1);
|
|
515
|
-
}
|
|
516
|
-
throw err;
|
|
517
|
-
}
|
|
492
|
+
const { jobId } = await platformInitiateExport(
|
|
493
|
+
token,
|
|
494
|
+
"teleport export",
|
|
495
|
+
entry.runtimeUrl,
|
|
496
|
+
);
|
|
518
497
|
|
|
519
498
|
console.log(`Export started (job ${jobId})...`);
|
|
520
499
|
|
|
@@ -527,12 +506,7 @@ async function exportFromAssistant(
|
|
|
527
506
|
while (Date.now() < deadline) {
|
|
528
507
|
let status: { status: string; downloadUrl?: string; error?: string };
|
|
529
508
|
try {
|
|
530
|
-
status = await platformPollExportStatus(
|
|
531
|
-
jobId,
|
|
532
|
-
token,
|
|
533
|
-
orgId,
|
|
534
|
-
entry.runtimeUrl,
|
|
535
|
-
);
|
|
509
|
+
status = await platformPollExportStatus(jobId, token, entry.runtimeUrl);
|
|
536
510
|
} catch (err) {
|
|
537
511
|
const msg = err instanceof Error ? err.message : String(err);
|
|
538
512
|
if (msg.includes("not found")) {
|
|
@@ -628,6 +602,7 @@ async function importToAssistant(
|
|
|
628
602
|
cloud: string,
|
|
629
603
|
bundleData: Uint8Array<ArrayBuffer>,
|
|
630
604
|
dryRun: boolean,
|
|
605
|
+
preUploadedBundleKey?: string | null,
|
|
631
606
|
): Promise<void> {
|
|
632
607
|
if (cloud === "vellum") {
|
|
633
608
|
// Platform target
|
|
@@ -637,16 +612,29 @@ async function importToAssistant(
|
|
|
637
612
|
process.exit(1);
|
|
638
613
|
}
|
|
639
614
|
|
|
640
|
-
|
|
641
|
-
try
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
615
|
+
// Use pre-uploaded bundle key if provided (string), skip upload if null
|
|
616
|
+
// (signals signed URLs were already tried and unavailable), or try
|
|
617
|
+
// signed-URL upload if undefined (never attempted).
|
|
618
|
+
let bundleKey: string | undefined =
|
|
619
|
+
preUploadedBundleKey === null ? undefined : preUploadedBundleKey;
|
|
620
|
+
if (preUploadedBundleKey === undefined) {
|
|
621
|
+
try {
|
|
622
|
+
const { uploadUrl, bundleKey: key } = await platformRequestUploadUrl(
|
|
623
|
+
token,
|
|
624
|
+
entry.runtimeUrl,
|
|
625
|
+
);
|
|
626
|
+
bundleKey = key;
|
|
627
|
+
console.log("Uploading bundle...");
|
|
628
|
+
await platformUploadToSignedUrl(uploadUrl, bundleData);
|
|
629
|
+
} catch (err) {
|
|
630
|
+
// If signed uploads unavailable (503), fall back to inline upload
|
|
631
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
632
|
+
if (msg.includes("not available")) {
|
|
633
|
+
bundleKey = undefined;
|
|
634
|
+
} else {
|
|
635
|
+
throw err;
|
|
636
|
+
}
|
|
648
637
|
}
|
|
649
|
-
throw err;
|
|
650
638
|
}
|
|
651
639
|
|
|
652
640
|
if (dryRun) {
|
|
@@ -657,12 +645,13 @@ async function importToAssistant(
|
|
|
657
645
|
body: Record<string, unknown>;
|
|
658
646
|
};
|
|
659
647
|
try {
|
|
660
|
-
preflightResult =
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
648
|
+
preflightResult = bundleKey
|
|
649
|
+
? await platformImportPreflightFromGcs(
|
|
650
|
+
bundleKey,
|
|
651
|
+
token,
|
|
652
|
+
entry.runtimeUrl,
|
|
653
|
+
)
|
|
654
|
+
: await platformImportPreflight(bundleData, token, entry.runtimeUrl);
|
|
666
655
|
} catch (err) {
|
|
667
656
|
if (err instanceof Error && err.name === "TimeoutError") {
|
|
668
657
|
console.error("Error: Preflight request timed out after 2 minutes.");
|
|
@@ -712,15 +701,12 @@ async function importToAssistant(
|
|
|
712
701
|
|
|
713
702
|
let importResult: { statusCode: number; body: Record<string, unknown> };
|
|
714
703
|
try {
|
|
715
|
-
importResult =
|
|
716
|
-
|
|
717
|
-
token,
|
|
718
|
-
orgId,
|
|
719
|
-
entry.runtimeUrl,
|
|
720
|
-
);
|
|
704
|
+
importResult = bundleKey
|
|
705
|
+
? await platformImportBundleFromGcs(bundleKey, token, entry.runtimeUrl)
|
|
706
|
+
: await platformImportBundle(bundleData, token, entry.runtimeUrl);
|
|
721
707
|
} catch (err) {
|
|
722
708
|
if (err instanceof Error && err.name === "TimeoutError") {
|
|
723
|
-
console.error("Error: Import request timed out after
|
|
709
|
+
console.error("Error: Import request timed out after 5 minutes.");
|
|
724
710
|
process.exit(1);
|
|
725
711
|
}
|
|
726
712
|
throw err;
|
|
@@ -1080,10 +1066,19 @@ export async function teleport(): Promise<void> {
|
|
|
1080
1066
|
// No existing target — just describe what would happen
|
|
1081
1067
|
console.log("Dry run summary:");
|
|
1082
1068
|
console.log(` Would export data from: ${from} (${fromCloud})`);
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1069
|
+
if (targetEnv === "platform") {
|
|
1070
|
+
// For platform targets, reflect the reordered flow
|
|
1071
|
+
console.log(` Would upload bundle via signed URL (if available)`);
|
|
1072
|
+
console.log(
|
|
1073
|
+
` Would hatch a new ${targetEnv} assistant${targetName ? ` named '${targetName}'` : ""}`,
|
|
1074
|
+
);
|
|
1075
|
+
console.log(` Would import data into the new assistant`);
|
|
1076
|
+
} else {
|
|
1077
|
+
console.log(
|
|
1078
|
+
` Would hatch a new ${targetEnv} assistant${targetName ? ` named '${targetName}'` : ""}`,
|
|
1079
|
+
);
|
|
1080
|
+
console.log(` Would import data into the new assistant`);
|
|
1081
|
+
}
|
|
1087
1082
|
}
|
|
1088
1083
|
|
|
1089
1084
|
console.log(`Dry run complete — no changes were made.`);
|
|
@@ -1094,6 +1089,71 @@ export async function teleport(): Promise<void> {
|
|
|
1094
1089
|
console.log(`Exporting from ${from} (${fromCloud})...`);
|
|
1095
1090
|
const bundleData = await exportFromAssistant(fromEntry, fromCloud);
|
|
1096
1091
|
|
|
1092
|
+
// Platform target: reordered flow — upload to GCS before hatching so that
|
|
1093
|
+
// if upload fails, no empty assistant is left dangling on the platform.
|
|
1094
|
+
if (targetEnv === "platform") {
|
|
1095
|
+
// Step B — Auth
|
|
1096
|
+
const token = readPlatformToken();
|
|
1097
|
+
if (!token) {
|
|
1098
|
+
console.error("Not logged in. Run 'vellum login' first.");
|
|
1099
|
+
process.exit(1);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// If targeting an existing assistant, validate cloud match early — before
|
|
1103
|
+
// uploading — so we don't waste a GCS upload on an invalid command.
|
|
1104
|
+
const existingTarget = targetName ? findAssistantByName(targetName) : null;
|
|
1105
|
+
if (existingTarget) {
|
|
1106
|
+
const existingCloud = resolveCloud(existingTarget);
|
|
1107
|
+
if (existingCloud !== "vellum") {
|
|
1108
|
+
console.error(
|
|
1109
|
+
`Error: Assistant '${targetName}' is a ${existingCloud} assistant, not platform. ` +
|
|
1110
|
+
`Use --${existingCloud} to target it.`,
|
|
1111
|
+
);
|
|
1112
|
+
process.exit(1);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Use the existing target's runtimeUrl for all platform calls so upload
|
|
1117
|
+
// and import hit the same instance.
|
|
1118
|
+
const targetPlatformUrl = existingTarget?.runtimeUrl;
|
|
1119
|
+
|
|
1120
|
+
// Step C — Upload to GCS
|
|
1121
|
+
// bundleKey: string = uploaded successfully, null = tried but unavailable,
|
|
1122
|
+
// undefined would mean "never tried" (not used here).
|
|
1123
|
+
let bundleKey: string | null = null;
|
|
1124
|
+
try {
|
|
1125
|
+
const { uploadUrl, bundleKey: key } = await platformRequestUploadUrl(
|
|
1126
|
+
token,
|
|
1127
|
+
targetPlatformUrl,
|
|
1128
|
+
);
|
|
1129
|
+
bundleKey = key;
|
|
1130
|
+
console.log("Uploading bundle to GCS...");
|
|
1131
|
+
await platformUploadToSignedUrl(uploadUrl, bundleData);
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
// If signed uploads unavailable (503), fall back to inline upload later
|
|
1134
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1135
|
+
if (msg.includes("not available")) {
|
|
1136
|
+
bundleKey = null;
|
|
1137
|
+
} else {
|
|
1138
|
+
throw err;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Step D — Hatch (upload succeeded or fallback to inline — safe to hatch)
|
|
1143
|
+
const toEntry = await resolveOrHatchTarget(targetEnv, targetName);
|
|
1144
|
+
const toCloud = resolveCloud(toEntry);
|
|
1145
|
+
|
|
1146
|
+
// Step E — Import from GCS (or inline fallback)
|
|
1147
|
+
// Pass bundleKey (string) or null to signal "already tried, use inline".
|
|
1148
|
+
console.log(`Importing to ${toEntry.assistantId} (${toCloud})...`);
|
|
1149
|
+
await importToAssistant(toEntry, toCloud, bundleData, false, bundleKey);
|
|
1150
|
+
|
|
1151
|
+
// Success summary
|
|
1152
|
+
console.log(`Teleport complete: ${from} → ${toEntry.assistantId}`);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// Non-platform targets (local/docker): existing flow unchanged
|
|
1097
1157
|
// For local<->docker transfers, stop (sleep) the source to free up ports
|
|
1098
1158
|
// before hatching the target. We do NOT retire yet — if hatch or import
|
|
1099
1159
|
// fails, the user can recover by running `vellum wake <source>`.
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "../lib/docker";
|
|
19
19
|
import { resolveImageRefs } from "../lib/platform-releases";
|
|
20
20
|
import {
|
|
21
|
-
|
|
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 {
|
|
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
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
|
@@ -291,6 +282,11 @@ async function upgradeDocker(
|
|
|
291
282
|
` Captured ${Object.keys(capturedEnv).length} env var(s) from ${res.assistantContainer}\n`,
|
|
292
283
|
);
|
|
293
284
|
|
|
285
|
+
// Capture GUARDIAN_BOOTSTRAP_SECRET from the gateway container (it is only
|
|
286
|
+
// set on gateway, not assistant) so it persists across container restarts.
|
|
287
|
+
const gatewayEnv = await captureContainerEnv(res.gatewayContainer);
|
|
288
|
+
const bootstrapSecret = gatewayEnv["GUARDIAN_BOOTSTRAP_SECRET"];
|
|
289
|
+
|
|
294
290
|
// Notify connected clients that an upgrade is about to begin.
|
|
295
291
|
// This must fire BEFORE any progress broadcasts so the UI sets
|
|
296
292
|
// isUpdateInProgress = true and starts displaying status messages.
|
|
@@ -419,6 +415,7 @@ async function upgradeDocker(
|
|
|
419
415
|
await startContainers(
|
|
420
416
|
{
|
|
421
417
|
signingKey,
|
|
418
|
+
bootstrapSecret,
|
|
422
419
|
cesServiceToken,
|
|
423
420
|
extraAssistantEnv,
|
|
424
421
|
gatewayPort,
|
|
@@ -517,6 +514,7 @@ async function upgradeDocker(
|
|
|
517
514
|
await startContainers(
|
|
518
515
|
{
|
|
519
516
|
signingKey,
|
|
517
|
+
bootstrapSecret,
|
|
520
518
|
cesServiceToken,
|
|
521
519
|
extraAssistantEnv,
|
|
522
520
|
gatewayPort,
|
|
@@ -687,21 +685,12 @@ async function upgradePlatform(
|
|
|
687
685
|
// we must not block the request based on the local CLI version.
|
|
688
686
|
const currentVersion = entry.serviceGroupVersion;
|
|
689
687
|
if (version && currentVersion) {
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
(target.major === current.major &&
|
|
697
|
-
target.minor === current.minor &&
|
|
698
|
-
target.patch < current.patch);
|
|
699
|
-
if (isOlder) {
|
|
700
|
-
const msg = `Cannot upgrade to an older version (${version} < ${currentVersion}). Use \`vellum rollback --version ${version}\` instead.`;
|
|
701
|
-
console.error(msg);
|
|
702
|
-
emitCliError("VERSION_DIRECTION", msg);
|
|
703
|
-
process.exit(1);
|
|
704
|
-
}
|
|
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);
|
|
705
694
|
}
|
|
706
695
|
}
|
|
707
696
|
|
|
@@ -718,7 +707,7 @@ async function upgradePlatform(
|
|
|
718
707
|
process.exit(1);
|
|
719
708
|
}
|
|
720
709
|
|
|
721
|
-
const
|
|
710
|
+
const headers = await authHeaders(token, entry.runtimeUrl);
|
|
722
711
|
|
|
723
712
|
const url = `${entry.runtimeUrl || getPlatformUrl()}/v1/assistants/upgrade/`;
|
|
724
713
|
const body: { assistant_id?: string; version?: string } = {
|
|
@@ -730,14 +719,28 @@ async function upgradePlatform(
|
|
|
730
719
|
|
|
731
720
|
const response = await fetch(url, {
|
|
732
721
|
method: "POST",
|
|
733
|
-
headers
|
|
734
|
-
"Content-Type": "application/json",
|
|
735
|
-
"X-Session-Token": token,
|
|
736
|
-
"Vellum-Organization-Id": orgId,
|
|
737
|
-
},
|
|
722
|
+
headers,
|
|
738
723
|
body: JSON.stringify(body),
|
|
739
724
|
});
|
|
740
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
|
+
|
|
741
744
|
if (!response.ok) {
|
|
742
745
|
const text = await response.text();
|
|
743
746
|
console.error(
|
|
@@ -748,11 +751,15 @@ async function upgradePlatform(
|
|
|
748
751
|
`Platform upgrade failed (${response.status})`,
|
|
749
752
|
text,
|
|
750
753
|
);
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
+
}
|
|
756
763
|
process.exit(1);
|
|
757
764
|
}
|
|
758
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
|
+
}
|