@vellumai/cli 0.5.10 → 0.5.11
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 +1 -1
- package/src/commands/hatch.ts +10 -0
- package/src/lib/assistant-config.ts +1 -1
- package/src/lib/aws.ts +6 -0
- package/src/lib/desktop-progress.ts +22 -0
- package/src/lib/docker.ts +24 -1
- package/src/lib/gcp.ts +6 -0
- package/src/lib/local.ts +1 -0
package/package.json
CHANGED
package/src/commands/hatch.ts
CHANGED
|
@@ -55,6 +55,7 @@ import { generateInstanceName } from "../lib/random-name";
|
|
|
55
55
|
import { validateAssistantName } from "../lib/retire-archive";
|
|
56
56
|
import { leaseGuardianToken } from "../lib/guardian-token";
|
|
57
57
|
import { archiveLogFile, resetLogFile } from "../lib/xdg-log";
|
|
58
|
+
import { emitProgress } from "../lib/desktop-progress.js";
|
|
58
59
|
|
|
59
60
|
export type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
60
61
|
|
|
@@ -645,6 +646,8 @@ async function hatchLocal(
|
|
|
645
646
|
name ?? process.env.VELLUM_ASSISTANT_NAME,
|
|
646
647
|
);
|
|
647
648
|
|
|
649
|
+
emitProgress(1, 7, "Preparing workspace...");
|
|
650
|
+
|
|
648
651
|
// Clean up stale local state: if daemon/gateway processes are running but
|
|
649
652
|
// the lock file has no entries AND the daemon is not healthy, stop them
|
|
650
653
|
// before starting fresh. A healthy daemon should be reused, not killed —
|
|
@@ -703,6 +706,8 @@ async function hatchLocal(
|
|
|
703
706
|
}
|
|
704
707
|
}
|
|
705
708
|
|
|
709
|
+
emitProgress(2, 7, "Allocating resources...");
|
|
710
|
+
|
|
706
711
|
// Reuse existing resources if re-hatching with --name that matches a known
|
|
707
712
|
// local assistant, otherwise allocate fresh per-instance ports and directories.
|
|
708
713
|
let resources: LocalInstanceResources;
|
|
@@ -763,10 +768,13 @@ async function hatchLocal(
|
|
|
763
768
|
process.env.APP_VERSION = cliPkg.version;
|
|
764
769
|
}
|
|
765
770
|
|
|
771
|
+
emitProgress(3, 7, "Writing configuration...");
|
|
766
772
|
const defaultWorkspaceConfigPath = writeInitialConfig(configValues);
|
|
767
773
|
|
|
774
|
+
emitProgress(4, 7, "Starting assistant...");
|
|
768
775
|
await startLocalDaemon(watch, resources, { defaultWorkspaceConfigPath });
|
|
769
776
|
|
|
777
|
+
emitProgress(5, 7, "Starting gateway...");
|
|
770
778
|
let runtimeUrl = `http://127.0.0.1:${resources.gatewayPort}`;
|
|
771
779
|
try {
|
|
772
780
|
runtimeUrl = await startGateway(watch, resources);
|
|
@@ -782,6 +790,7 @@ async function hatchLocal(
|
|
|
782
790
|
|
|
783
791
|
// Lease a guardian token so the desktop app can import it on first launch
|
|
784
792
|
// instead of hitting /v1/guardian/init itself.
|
|
793
|
+
emitProgress(6, 7, "Securing connection...");
|
|
785
794
|
try {
|
|
786
795
|
await leaseGuardianToken(runtimeUrl, instanceName);
|
|
787
796
|
} catch (err) {
|
|
@@ -813,6 +822,7 @@ async function hatchLocal(
|
|
|
813
822
|
serviceGroupVersion: cliPkg.version ? `v${cliPkg.version}` : undefined,
|
|
814
823
|
resources,
|
|
815
824
|
};
|
|
825
|
+
emitProgress(7, 7, "Saving configuration...");
|
|
816
826
|
if (!restart) {
|
|
817
827
|
saveAssistantEntry(localEntry);
|
|
818
828
|
setActiveAssistant(instanceName);
|
|
@@ -438,7 +438,7 @@ export async function allocateLocalResources(
|
|
|
438
438
|
): Promise<LocalInstanceResources> {
|
|
439
439
|
// First local assistant gets the home directory with default ports.
|
|
440
440
|
// Respect BASE_DATA_DIR when set (e.g. in e2e tests) so the daemon,
|
|
441
|
-
// gateway, and
|
|
441
|
+
// gateway, and credential store all resolve paths under the same root.
|
|
442
442
|
const existingLocals = loadAllAssistants().filter((e) => e.cloud === "local");
|
|
443
443
|
if (existingLocals.length === 0) {
|
|
444
444
|
const baseDir = getBaseDir();
|
package/src/lib/aws.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { Species } from "./constants";
|
|
|
11
11
|
import { leaseGuardianToken } from "./guardian-token";
|
|
12
12
|
import { generateInstanceName } from "./random-name";
|
|
13
13
|
import { exec, execOutput } from "./step-runner";
|
|
14
|
+
import { emitProgress } from "./desktop-progress.js";
|
|
14
15
|
|
|
15
16
|
const KEY_PAIR_NAME = "vellum-assistant";
|
|
16
17
|
const DEFAULT_SSH_USER = "admin";
|
|
@@ -443,6 +444,7 @@ export async function hatchAws(
|
|
|
443
444
|
console.log("\u{1F50D} Finding latest Debian AMI...");
|
|
444
445
|
const amiId = await getLatestDebianAmi(region);
|
|
445
446
|
|
|
447
|
+
emitProgress(1, 5, "Preparing startup script...");
|
|
446
448
|
const { script: startupScript, laptopBootstrapSecret } =
|
|
447
449
|
await buildStartupScript(
|
|
448
450
|
species,
|
|
@@ -455,6 +457,7 @@ export async function hatchAws(
|
|
|
455
457
|
const startupScriptPath = join(tmpdir(), `${instanceName}-startup.sh`);
|
|
456
458
|
writeFileSync(startupScriptPath, startupScript);
|
|
457
459
|
|
|
460
|
+
emitProgress(2, 5, "Launching instance...");
|
|
458
461
|
console.log("\u{1F528} Launching instance...");
|
|
459
462
|
let instanceId: string;
|
|
460
463
|
try {
|
|
@@ -491,6 +494,7 @@ export async function hatchAws(
|
|
|
491
494
|
const runtimeUrl = externalIp
|
|
492
495
|
? `http://${externalIp}:${GATEWAY_PORT}`
|
|
493
496
|
: `http://${instanceName}:${GATEWAY_PORT}`;
|
|
497
|
+
emitProgress(3, 5, "Saving configuration...");
|
|
494
498
|
const awsEntry: AssistantEntry = {
|
|
495
499
|
assistantId: instanceName,
|
|
496
500
|
runtimeUrl,
|
|
@@ -522,6 +526,7 @@ export async function hatchAws(
|
|
|
522
526
|
|
|
523
527
|
if (externalIp) {
|
|
524
528
|
const ip = externalIp;
|
|
529
|
+
emitProgress(4, 5, "Installing software...");
|
|
525
530
|
const result = await watchHatching(
|
|
526
531
|
() => pollAwsInstance(ip, keyPath),
|
|
527
532
|
instanceName,
|
|
@@ -539,6 +544,7 @@ export async function hatchAws(
|
|
|
539
544
|
process.exit(1);
|
|
540
545
|
}
|
|
541
546
|
|
|
547
|
+
emitProgress(5, 5, "Finalizing...");
|
|
542
548
|
try {
|
|
543
549
|
await leaseGuardianToken(
|
|
544
550
|
runtimeUrl,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight progress reporting for the desktop app.
|
|
3
|
+
*
|
|
4
|
+
* Writes structured `HATCH_PROGRESS:{...}` lines to stdout so the Electron
|
|
5
|
+
* wrapper can parse them and render a progress bar during hatch.
|
|
6
|
+
*
|
|
7
|
+
* This module intentionally has ZERO internal imports to avoid circular
|
|
8
|
+
* dependency issues — it is a leaf module.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Emit a structured progress event to stdout.
|
|
13
|
+
*
|
|
14
|
+
* Only emits when `VELLUM_DESKTOP_APP` env var is set (desktop mode).
|
|
15
|
+
* The desktop app parses lines matching `HATCH_PROGRESS:{...}` to update
|
|
16
|
+
* its progress UI.
|
|
17
|
+
*/
|
|
18
|
+
export function emitProgress(step: number, total: number, label: string): void {
|
|
19
|
+
if (!process.env.VELLUM_DESKTOP_APP) return;
|
|
20
|
+
const payload = JSON.stringify({ step, total, label });
|
|
21
|
+
process.stdout.write(`HATCH_PROGRESS:${payload}\n`);
|
|
22
|
+
}
|
package/src/lib/docker.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resetLogFile,
|
|
27
27
|
writeToLogFile,
|
|
28
28
|
} from "./xdg-log";
|
|
29
|
+
import { emitProgress } from "./desktop-progress.js";
|
|
29
30
|
|
|
30
31
|
export type ServiceName = "assistant" | "credential-executor" | "gateway";
|
|
31
32
|
|
|
@@ -831,10 +832,11 @@ export async function sleepContainers(
|
|
|
831
832
|
}
|
|
832
833
|
}
|
|
833
834
|
|
|
834
|
-
/** Start existing stopped containers. */
|
|
835
|
+
/** Start existing stopped containers, starting Colima first if it isn't running. */
|
|
835
836
|
export async function wakeContainers(
|
|
836
837
|
res: ReturnType<typeof dockerResourceNames>,
|
|
837
838
|
): Promise<void> {
|
|
839
|
+
await ensureColimaRunning();
|
|
838
840
|
for (const container of [
|
|
839
841
|
res.assistantContainer,
|
|
840
842
|
res.gatewayContainer,
|
|
@@ -844,6 +846,20 @@ export async function wakeContainers(
|
|
|
844
846
|
}
|
|
845
847
|
}
|
|
846
848
|
|
|
849
|
+
/**
|
|
850
|
+
* Checks whether Colima is running and starts it if not.
|
|
851
|
+
* Assumes the Docker/Colima toolchain is already installed (handled during hatch).
|
|
852
|
+
*/
|
|
853
|
+
async function ensureColimaRunning(): Promise<void> {
|
|
854
|
+
ensureLocalBinOnPath();
|
|
855
|
+
try {
|
|
856
|
+
await exec("colima", ["status"]);
|
|
857
|
+
} catch {
|
|
858
|
+
console.log("🚀 Colima is not running. Starting Colima...");
|
|
859
|
+
await exec("colima", ["start"]);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
847
863
|
/**
|
|
848
864
|
* Capture the current image references for running service containers.
|
|
849
865
|
* Returns a complete record of service → immutable image ID (sha256 digest)
|
|
@@ -1061,6 +1077,7 @@ export async function hatchDocker(
|
|
|
1061
1077
|
};
|
|
1062
1078
|
|
|
1063
1079
|
try {
|
|
1080
|
+
emitProgress(1, 6, "Checking Docker...");
|
|
1064
1081
|
await ensureDockerInstalled();
|
|
1065
1082
|
|
|
1066
1083
|
const instanceName = generateInstanceName(species, name);
|
|
@@ -1075,6 +1092,7 @@ export async function hatchDocker(
|
|
|
1075
1092
|
let repoRoot: string | undefined;
|
|
1076
1093
|
|
|
1077
1094
|
if (watch) {
|
|
1095
|
+
emitProgress(2, 6, "Building images...");
|
|
1078
1096
|
repoRoot = findRepoRoot();
|
|
1079
1097
|
const localTag = `local-${instanceName}`;
|
|
1080
1098
|
imageTags.assistant = `vellum-assistant:${localTag}`;
|
|
@@ -1095,6 +1113,7 @@ export async function hatchDocker(
|
|
|
1095
1113
|
await buildAllImages(repoRoot, imageTags, log);
|
|
1096
1114
|
log("✅ Docker images built");
|
|
1097
1115
|
} else {
|
|
1116
|
+
emitProgress(2, 6, "Pulling images...");
|
|
1098
1117
|
const version = cliPkg.version;
|
|
1099
1118
|
const versionTag = version ? `v${version}` : "latest";
|
|
1100
1119
|
log("🔍 Resolving image references...");
|
|
@@ -1121,6 +1140,7 @@ export async function hatchDocker(
|
|
|
1121
1140
|
|
|
1122
1141
|
const res = dockerResourceNames(instanceName);
|
|
1123
1142
|
|
|
1143
|
+
emitProgress(3, 6, "Creating volumes...");
|
|
1124
1144
|
log("📁 Creating network and volumes...");
|
|
1125
1145
|
await exec("docker", ["network", "create", res.network]);
|
|
1126
1146
|
await exec("docker", ["volume", "create", res.socketVolume]);
|
|
@@ -1158,6 +1178,7 @@ export async function hatchDocker(
|
|
|
1158
1178
|
? `${preExisting},${ownSecret}`
|
|
1159
1179
|
: ownSecret;
|
|
1160
1180
|
|
|
1181
|
+
emitProgress(4, 6, "Starting containers...");
|
|
1161
1182
|
await startContainers(
|
|
1162
1183
|
{
|
|
1163
1184
|
signingKey,
|
|
@@ -1193,9 +1214,11 @@ export async function hatchDocker(
|
|
|
1193
1214
|
networkName: res.network,
|
|
1194
1215
|
},
|
|
1195
1216
|
};
|
|
1217
|
+
emitProgress(5, 6, "Saving configuration...");
|
|
1196
1218
|
saveAssistantEntry(dockerEntry);
|
|
1197
1219
|
setActiveAssistant(instanceName);
|
|
1198
1220
|
|
|
1221
|
+
emitProgress(6, 6, "Waiting for services...");
|
|
1199
1222
|
const { ready } = await waitForGatewayAndLease({
|
|
1200
1223
|
bootstrapSecret: ownSecret,
|
|
1201
1224
|
containerName: res.assistantContainer,
|
package/src/lib/gcp.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { leaseGuardianToken } from "./guardian-token";
|
|
|
14
14
|
import { getPlatformUrl } from "./platform-client";
|
|
15
15
|
import { generateInstanceName } from "./random-name";
|
|
16
16
|
import { exec, execOutput } from "./step-runner";
|
|
17
|
+
import { emitProgress } from "./desktop-progress.js";
|
|
17
18
|
|
|
18
19
|
export async function getActiveProject(): Promise<string> {
|
|
19
20
|
const output = await execOutput("gcloud", ["config", "get-value", "project"]);
|
|
@@ -522,6 +523,7 @@ export async function hatchGcp(
|
|
|
522
523
|
);
|
|
523
524
|
process.exit(1);
|
|
524
525
|
}
|
|
526
|
+
emitProgress(1, 5, "Preparing startup script...");
|
|
525
527
|
const { script: startupScript, laptopBootstrapSecret } =
|
|
526
528
|
await buildStartupScript(
|
|
527
529
|
species,
|
|
@@ -534,6 +536,7 @@ export async function hatchGcp(
|
|
|
534
536
|
const startupScriptPath = join(tmpdir(), `${instanceName}-startup.sh`);
|
|
535
537
|
writeFileSync(startupScriptPath, startupScript);
|
|
536
538
|
|
|
539
|
+
emitProgress(2, 5, "Launching instance...");
|
|
537
540
|
console.log("\ud83d\udd28 Creating instance with startup script...");
|
|
538
541
|
try {
|
|
539
542
|
const createArgs = [
|
|
@@ -595,6 +598,7 @@ export async function hatchGcp(
|
|
|
595
598
|
const runtimeUrl = externalIp
|
|
596
599
|
? `http://${externalIp}:${GATEWAY_PORT}`
|
|
597
600
|
: `http://${instanceName}:${GATEWAY_PORT}`;
|
|
601
|
+
emitProgress(3, 5, "Saving configuration...");
|
|
598
602
|
const gcpEntry: AssistantEntry = {
|
|
599
603
|
assistantId: instanceName,
|
|
600
604
|
runtimeUrl,
|
|
@@ -624,6 +628,7 @@ export async function hatchGcp(
|
|
|
624
628
|
console.log(" Press Ctrl+C to detach (instance will keep running)");
|
|
625
629
|
console.log("");
|
|
626
630
|
|
|
631
|
+
emitProgress(4, 5, "Installing software...");
|
|
627
632
|
const result = await watchHatching(
|
|
628
633
|
() => pollInstance(instanceName, project, zone, account),
|
|
629
634
|
instanceName,
|
|
@@ -662,6 +667,7 @@ export async function hatchGcp(
|
|
|
662
667
|
}
|
|
663
668
|
}
|
|
664
669
|
|
|
670
|
+
emitProgress(5, 5, "Finalizing...");
|
|
665
671
|
try {
|
|
666
672
|
await leaseGuardianToken(
|
|
667
673
|
runtimeUrl,
|
package/src/lib/local.ts
CHANGED