@vellumai/cli 0.5.12 → 0.5.14
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/AGENTS.md +6 -0
- package/package.json +1 -1
- package/src/__tests__/teleport.test.ts +829 -0
- package/src/commands/backup.ts +14 -5
- package/src/commands/hatch.ts +56 -5
- package/src/commands/ps.ts +1 -1
- package/src/commands/recover.ts +13 -4
- package/src/commands/restore.ts +9 -2
- package/src/commands/retire.ts +8 -4
- package/src/commands/rollback.ts +7 -11
- package/src/commands/teleport.ts +746 -0
- package/src/commands/upgrade.ts +2 -13
- package/src/commands/wake.ts +17 -4
- package/src/index.ts +3 -0
- package/src/lib/assistant-config.ts +6 -7
- package/src/lib/constants.ts +11 -0
- package/src/lib/docker.ts +2 -146
- package/src/lib/health-check.ts +1 -1
- package/src/lib/local.ts +32 -2
- package/src/lib/platform-client.ts +89 -16
- package/src/lib/upgrade-lifecycle.ts +0 -11
package/src/commands/backup.ts
CHANGED
|
@@ -66,7 +66,7 @@ export async function backup(): Promise<void> {
|
|
|
66
66
|
const cloud =
|
|
67
67
|
entry.cloud || (entry.project ? "gcp" : entry.sshUser ? "custom" : "local");
|
|
68
68
|
if (cloud === "vellum") {
|
|
69
|
-
await backupPlatform(name, outputArg);
|
|
69
|
+
await backupPlatform(name, outputArg, entry.runtimeUrl);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -184,7 +184,11 @@ export async function backup(): Promise<void> {
|
|
|
184
184
|
// Platform (Vellum-hosted) backup via Django async migration export
|
|
185
185
|
// ---------------------------------------------------------------------------
|
|
186
186
|
|
|
187
|
-
async function backupPlatform(
|
|
187
|
+
async function backupPlatform(
|
|
188
|
+
name: string,
|
|
189
|
+
outputArg?: string,
|
|
190
|
+
runtimeUrl?: string,
|
|
191
|
+
): Promise<void> {
|
|
188
192
|
// Step 1 — Authenticate
|
|
189
193
|
const token = readPlatformToken();
|
|
190
194
|
if (!token) {
|
|
@@ -194,7 +198,7 @@ async function backupPlatform(name: string, outputArg?: string): Promise<void> {
|
|
|
194
198
|
|
|
195
199
|
let orgId: string;
|
|
196
200
|
try {
|
|
197
|
-
orgId = await fetchOrganizationId(token);
|
|
201
|
+
orgId = await fetchOrganizationId(token, runtimeUrl);
|
|
198
202
|
} catch (err) {
|
|
199
203
|
const msg = err instanceof Error ? err.message : String(err);
|
|
200
204
|
if (msg.includes("401") || msg.includes("403")) {
|
|
@@ -207,7 +211,12 @@ async function backupPlatform(name: string, outputArg?: string): Promise<void> {
|
|
|
207
211
|
// Step 2 — Initiate export job
|
|
208
212
|
let jobId: string;
|
|
209
213
|
try {
|
|
210
|
-
const result = await platformInitiateExport(
|
|
214
|
+
const result = await platformInitiateExport(
|
|
215
|
+
token,
|
|
216
|
+
orgId,
|
|
217
|
+
"CLI backup",
|
|
218
|
+
runtimeUrl,
|
|
219
|
+
);
|
|
211
220
|
jobId = result.jobId;
|
|
212
221
|
} catch (err) {
|
|
213
222
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -235,7 +244,7 @@ async function backupPlatform(name: string, outputArg?: string): Promise<void> {
|
|
|
235
244
|
while (Date.now() < deadline) {
|
|
236
245
|
let status: { status: string; downloadUrl?: string; error?: string };
|
|
237
246
|
try {
|
|
238
|
-
status = await platformPollExportStatus(jobId, token, orgId);
|
|
247
|
+
status = await platformPollExportStatus(jobId, token, orgId, runtimeUrl);
|
|
239
248
|
} catch (err) {
|
|
240
249
|
const msg = err instanceof Error ? err.message : String(err);
|
|
241
250
|
// Let non-transient errors (e.g. 404 "job not found") propagate immediately
|
package/src/commands/hatch.ts
CHANGED
|
@@ -42,12 +42,17 @@ import { hatchGcp } from "../lib/gcp";
|
|
|
42
42
|
import type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
43
43
|
import { buildNestedConfig, writeInitialConfig } from "../lib/config-utils";
|
|
44
44
|
import {
|
|
45
|
+
generateLocalSigningKey,
|
|
45
46
|
startLocalDaemon,
|
|
46
47
|
startGateway,
|
|
47
48
|
stopLocalProcesses,
|
|
48
49
|
} from "../lib/local";
|
|
49
50
|
import { maybeStartNgrokTunnel } from "../lib/ngrok";
|
|
50
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
getPlatformUrl,
|
|
53
|
+
hatchAssistant,
|
|
54
|
+
readPlatformToken,
|
|
55
|
+
} from "../lib/platform-client";
|
|
51
56
|
import { httpHealthCheck } from "../lib/http-client";
|
|
52
57
|
import { detectOrphanedProcesses } from "../lib/orphan-detection";
|
|
53
58
|
import { isProcessAlive, stopProcess } from "../lib/process";
|
|
@@ -237,7 +242,7 @@ function parseArgs(): HatchArgs {
|
|
|
237
242
|
console.log(" -d Run in detached mode");
|
|
238
243
|
console.log(" --name <name> Custom instance name");
|
|
239
244
|
console.log(
|
|
240
|
-
" --remote <host> Remote host (local, gcp, aws, docker, custom)",
|
|
245
|
+
" --remote <host> Remote host (local, gcp, aws, docker, custom, vellum)",
|
|
241
246
|
);
|
|
242
247
|
console.log(
|
|
243
248
|
" --restart Restart processes without onboarding side effects",
|
|
@@ -772,12 +777,16 @@ async function hatchLocal(
|
|
|
772
777
|
const defaultWorkspaceConfigPath = writeInitialConfig(configValues);
|
|
773
778
|
|
|
774
779
|
emitProgress(4, 7, "Starting assistant...");
|
|
775
|
-
|
|
780
|
+
const signingKey = generateLocalSigningKey();
|
|
781
|
+
await startLocalDaemon(watch, resources, {
|
|
782
|
+
defaultWorkspaceConfigPath,
|
|
783
|
+
signingKey,
|
|
784
|
+
});
|
|
776
785
|
|
|
777
786
|
emitProgress(5, 7, "Starting gateway...");
|
|
778
787
|
let runtimeUrl = `http://127.0.0.1:${resources.gatewayPort}`;
|
|
779
788
|
try {
|
|
780
|
-
runtimeUrl = await startGateway(watch, resources);
|
|
789
|
+
runtimeUrl = await startGateway(watch, resources, { signingKey });
|
|
781
790
|
} catch (error) {
|
|
782
791
|
// Gateway failed — stop the daemon we just started so we don't leave
|
|
783
792
|
// orphaned processes with no lock file entry.
|
|
@@ -820,7 +829,7 @@ async function hatchLocal(
|
|
|
820
829
|
species,
|
|
821
830
|
hatchedAt: new Date().toISOString(),
|
|
822
831
|
serviceGroupVersion: cliPkg.version ? `v${cliPkg.version}` : undefined,
|
|
823
|
-
resources,
|
|
832
|
+
resources: { ...resources, signingKey },
|
|
824
833
|
};
|
|
825
834
|
emitProgress(7, 7, "Saving configuration...");
|
|
826
835
|
if (!restart) {
|
|
@@ -943,6 +952,48 @@ export async function hatch(): Promise<void> {
|
|
|
943
952
|
return;
|
|
944
953
|
}
|
|
945
954
|
|
|
955
|
+
if (remote === "vellum") {
|
|
956
|
+
await hatchVellumPlatform();
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
946
960
|
console.error(`Error: Remote host '${remote}' is not yet supported.`);
|
|
947
961
|
process.exit(1);
|
|
948
962
|
}
|
|
963
|
+
|
|
964
|
+
async function hatchVellumPlatform(): Promise<void> {
|
|
965
|
+
const token = readPlatformToken();
|
|
966
|
+
if (!token) {
|
|
967
|
+
console.error("Not logged in. Run `vellum login --token <token>` first.");
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
const config = SPECIES_CONFIG.vellum;
|
|
972
|
+
console.log("");
|
|
973
|
+
for (const line of config.art) {
|
|
974
|
+
console.log(` ${line}`);
|
|
975
|
+
}
|
|
976
|
+
console.log("");
|
|
977
|
+
console.log(" Hatching assistant on Vellum platform...");
|
|
978
|
+
console.log("");
|
|
979
|
+
|
|
980
|
+
const result = await hatchAssistant(token);
|
|
981
|
+
|
|
982
|
+
const platformUrl = getPlatformUrl();
|
|
983
|
+
|
|
984
|
+
saveAssistantEntry({
|
|
985
|
+
assistantId: result.id,
|
|
986
|
+
runtimeUrl: platformUrl,
|
|
987
|
+
cloud: "vellum",
|
|
988
|
+
species: "vellum",
|
|
989
|
+
hatchedAt: new Date().toISOString(),
|
|
990
|
+
});
|
|
991
|
+
setActiveAssistant(result.id);
|
|
992
|
+
|
|
993
|
+
console.log(` ${config.hatchedEmoji} Your assistant has hatched!`);
|
|
994
|
+
console.log("");
|
|
995
|
+
console.log(` ID: ${result.id}`);
|
|
996
|
+
console.log(` Name: ${result.name}`);
|
|
997
|
+
console.log(` Status: ${result.status}`);
|
|
998
|
+
console.log("");
|
|
999
|
+
}
|
package/src/commands/ps.ts
CHANGED
|
@@ -238,7 +238,7 @@ async function getLocalProcesses(entry: AssistantEntry): Promise<TableRow[]> {
|
|
|
238
238
|
name: "embed-worker",
|
|
239
239
|
pgrepName: "embed-worker",
|
|
240
240
|
port: 0,
|
|
241
|
-
pidFile: join(vellumDir, "embed-worker.pid"),
|
|
241
|
+
pidFile: join(vellumDir, "workspace", "embed-worker.pid"),
|
|
242
242
|
},
|
|
243
243
|
];
|
|
244
244
|
|
package/src/commands/recover.ts
CHANGED
|
@@ -4,7 +4,11 @@ import { join } from "path";
|
|
|
4
4
|
|
|
5
5
|
import { saveAssistantEntry } from "../lib/assistant-config";
|
|
6
6
|
import type { AssistantEntry } from "../lib/assistant-config";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
generateLocalSigningKey,
|
|
9
|
+
startLocalDaemon,
|
|
10
|
+
startGateway,
|
|
11
|
+
} from "../lib/local";
|
|
8
12
|
import { getArchivePath, getMetadataPath } from "../lib/retire-archive";
|
|
9
13
|
import { exec } from "../lib/step-runner";
|
|
10
14
|
|
|
@@ -66,9 +70,14 @@ export async function recover(): Promise<void> {
|
|
|
66
70
|
unlinkSync(archivePath);
|
|
67
71
|
unlinkSync(metadataPath);
|
|
68
72
|
|
|
69
|
-
// 7.
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
// 7. Persist signing key so it survives daemon/gateway restarts (same as wake)
|
|
74
|
+
const signingKey = generateLocalSigningKey();
|
|
75
|
+
entry.resources = { ...entry.resources, signingKey };
|
|
76
|
+
saveAssistantEntry(entry);
|
|
77
|
+
|
|
78
|
+
// 8. Start daemon + gateway
|
|
79
|
+
await startLocalDaemon(false, entry.resources, { signingKey });
|
|
80
|
+
await startGateway(false, entry.resources, { signingKey });
|
|
72
81
|
|
|
73
82
|
console.log(`✅ Recovered assistant '${name}'.`);
|
|
74
83
|
}
|
package/src/commands/restore.ts
CHANGED
|
@@ -179,7 +179,7 @@ async function restorePlatform(
|
|
|
179
179
|
|
|
180
180
|
let orgId: string;
|
|
181
181
|
try {
|
|
182
|
-
orgId = await fetchOrganizationId(token);
|
|
182
|
+
orgId = await fetchOrganizationId(token, entry.runtimeUrl);
|
|
183
183
|
} catch (err) {
|
|
184
184
|
const msg = err instanceof Error ? err.message : String(err);
|
|
185
185
|
if (msg.includes("401") || msg.includes("403")) {
|
|
@@ -206,6 +206,7 @@ async function restorePlatform(
|
|
|
206
206
|
new Uint8Array(bundleData),
|
|
207
207
|
token,
|
|
208
208
|
orgId,
|
|
209
|
+
entry.runtimeUrl,
|
|
209
210
|
);
|
|
210
211
|
} catch (err) {
|
|
211
212
|
if (err instanceof Error && err.name === "TimeoutError") {
|
|
@@ -315,7 +316,12 @@ async function restorePlatform(
|
|
|
315
316
|
);
|
|
316
317
|
|
|
317
318
|
try {
|
|
318
|
-
await rollbackPlatformAssistant(
|
|
319
|
+
await rollbackPlatformAssistant(
|
|
320
|
+
token,
|
|
321
|
+
orgId,
|
|
322
|
+
opts.version,
|
|
323
|
+
entry.runtimeUrl,
|
|
324
|
+
);
|
|
319
325
|
} catch (err) {
|
|
320
326
|
const msg = err instanceof Error ? err.message : String(err);
|
|
321
327
|
if (msg.includes("401") || msg.includes("403")) {
|
|
@@ -340,6 +346,7 @@ async function restorePlatform(
|
|
|
340
346
|
new Uint8Array(bundleData),
|
|
341
347
|
token,
|
|
342
348
|
orgId,
|
|
349
|
+
entry.runtimeUrl,
|
|
343
350
|
);
|
|
344
351
|
} catch (err) {
|
|
345
352
|
if (err instanceof Error && err.name === "TimeoutError") {
|
package/src/commands/retire.ts
CHANGED
|
@@ -197,7 +197,10 @@ async function retireCustom(entry: AssistantEntry): Promise<void> {
|
|
|
197
197
|
console.log(`\u2705 Custom instance retired.`);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
async function retireVellum(
|
|
200
|
+
async function retireVellum(
|
|
201
|
+
assistantId: string,
|
|
202
|
+
runtimeUrl?: string,
|
|
203
|
+
): Promise<void> {
|
|
201
204
|
console.log("\u{1F5D1}\ufe0f Retiring platform-hosted instance...\n");
|
|
202
205
|
|
|
203
206
|
const token = readPlatformToken();
|
|
@@ -208,9 +211,10 @@ async function retireVellum(assistantId: string): Promise<void> {
|
|
|
208
211
|
process.exit(1);
|
|
209
212
|
}
|
|
210
213
|
|
|
211
|
-
const orgId = await fetchOrganizationId(token);
|
|
214
|
+
const orgId = await fetchOrganizationId(token, runtimeUrl);
|
|
212
215
|
|
|
213
|
-
const
|
|
216
|
+
const platformUrl = runtimeUrl || getPlatformUrl();
|
|
217
|
+
const url = `${platformUrl}/v1/assistants/${encodeURIComponent(assistantId)}/retire/`;
|
|
214
218
|
const response = await fetch(url, {
|
|
215
219
|
method: "DELETE",
|
|
216
220
|
headers: {
|
|
@@ -343,7 +347,7 @@ async function retireInner(): Promise<void> {
|
|
|
343
347
|
} else if (cloud === "custom") {
|
|
344
348
|
await retireCustom(entry);
|
|
345
349
|
} else if (cloud === "vellum") {
|
|
346
|
-
await retireVellum(entry.assistantId);
|
|
350
|
+
await retireVellum(entry.assistantId, entry.runtimeUrl);
|
|
347
351
|
} else {
|
|
348
352
|
console.error(`Error: Unknown cloud type '${cloud}'.`);
|
|
349
353
|
process.exit(1);
|
package/src/commands/rollback.ts
CHANGED
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
captureImageRefs,
|
|
12
12
|
GATEWAY_INTERNAL_PORT,
|
|
13
13
|
dockerResourceNames,
|
|
14
|
-
migrateCesSecurityFiles,
|
|
15
|
-
migrateGatewaySecurityFiles,
|
|
16
14
|
startContainers,
|
|
17
15
|
stopContainers,
|
|
18
16
|
} from "../lib/docker";
|
|
@@ -185,7 +183,7 @@ async function rollbackPlatformViaEndpoint(
|
|
|
185
183
|
|
|
186
184
|
let orgId: string;
|
|
187
185
|
try {
|
|
188
|
-
orgId = await fetchOrganizationId(token);
|
|
186
|
+
orgId = await fetchOrganizationId(token, entry.runtimeUrl);
|
|
189
187
|
} catch (err) {
|
|
190
188
|
const msg = err instanceof Error ? err.message : String(err);
|
|
191
189
|
if (msg.includes("401") || msg.includes("403")) {
|
|
@@ -206,7 +204,12 @@ async function rollbackPlatformViaEndpoint(
|
|
|
206
204
|
|
|
207
205
|
let result: { detail: string; version: string | null };
|
|
208
206
|
try {
|
|
209
|
-
result = await rollbackPlatformAssistant(
|
|
207
|
+
result = await rollbackPlatformAssistant(
|
|
208
|
+
token,
|
|
209
|
+
orgId,
|
|
210
|
+
version,
|
|
211
|
+
entry.runtimeUrl,
|
|
212
|
+
);
|
|
210
213
|
} catch (err) {
|
|
211
214
|
const detail = err instanceof Error ? err.message : String(err);
|
|
212
215
|
|
|
@@ -423,13 +426,6 @@ export async function rollback(): Promise<void> {
|
|
|
423
426
|
await stopContainers(res);
|
|
424
427
|
console.log("✅ Containers stopped\n");
|
|
425
428
|
|
|
426
|
-
// Run security file migrations and signing key cleanup
|
|
427
|
-
console.log("🔄 Migrating security files to gateway volume...");
|
|
428
|
-
await migrateGatewaySecurityFiles(res, (msg) => console.log(msg));
|
|
429
|
-
|
|
430
|
-
console.log("🔄 Migrating credential files to CES security volume...");
|
|
431
|
-
await migrateCesSecurityFiles(res, (msg) => console.log(msg));
|
|
432
|
-
|
|
433
429
|
console.log("🚀 Starting containers with previous version...");
|
|
434
430
|
await startContainers(
|
|
435
431
|
{
|