@vellumai/cli 0.1.2 → 0.1.4
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 +52 -14
- package/src/commands/retire.ts +14 -3
- package/src/lib/aws.ts +18 -0
- package/src/lib/gcp.ts +17 -0
- package/src/lib/interfaces-seed.ts +3 -7
- package/src/lib/openclaw-runtime-server.ts +1 -3
package/package.json
CHANGED
package/src/commands/hatch.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from "child_process";
|
|
|
2
2
|
import { randomBytes } from "crypto";
|
|
3
3
|
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
4
4
|
import { tmpdir, userInfo } from "os";
|
|
5
|
-
import { join } from "path";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
6
|
|
|
7
7
|
import { buildOpenclawStartupScript } from "../adapters/openclaw";
|
|
8
8
|
import { saveAssistantEntry } from "../lib/assistant-config";
|
|
@@ -583,7 +583,8 @@ async function hatchGcp(
|
|
|
583
583
|
(await checkCurlFailure(instanceName, project, zone, account))
|
|
584
584
|
) {
|
|
585
585
|
console.log("");
|
|
586
|
-
|
|
586
|
+
const installScriptUrl = `${process.env.VELLUM_ASSISTANT_PLATFORM_URL ?? "https://assistant.vellum.ai"}/install.sh`;
|
|
587
|
+
console.log(`🔄 Detected install script curl failure for ${installScriptUrl}, attempting recovery...`);
|
|
587
588
|
await recoverFromCurlFailure(instanceName, project, zone, sshUser, account);
|
|
588
589
|
console.log("✅ Recovery successful!");
|
|
589
590
|
} else {
|
|
@@ -722,21 +723,58 @@ async function hatchLocal(species: Species, name: string | null): Promise<void>
|
|
|
722
723
|
console.log("");
|
|
723
724
|
|
|
724
725
|
console.log("🔨 Starting local daemon...");
|
|
725
|
-
const child = spawn("bunx", ["vellum", "daemon", "start"], {
|
|
726
|
-
stdio: "inherit",
|
|
727
|
-
env: { ...process.env },
|
|
728
|
-
});
|
|
729
726
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
727
|
+
if (process.env.VELLUM_DESKTOP_APP) {
|
|
728
|
+
const daemonBinary = join(dirname(process.execPath), "vellum-daemon");
|
|
729
|
+
const child = spawn(daemonBinary, [], {
|
|
730
|
+
detached: true,
|
|
731
|
+
stdio: "ignore",
|
|
732
|
+
env: { ...process.env },
|
|
733
|
+
});
|
|
734
|
+
child.unref();
|
|
735
|
+
|
|
736
|
+
const homeDir = process.env.HOME ?? userInfo().homedir;
|
|
737
|
+
const socketPath = join(homeDir, ".vellum", "vellum.sock");
|
|
738
|
+
const maxWait = 10000;
|
|
739
|
+
const pollInterval = 100;
|
|
740
|
+
let waited = 0;
|
|
741
|
+
while (waited < maxWait) {
|
|
742
|
+
if (existsSync(socketPath)) {
|
|
743
|
+
break;
|
|
736
744
|
}
|
|
745
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
746
|
+
waited += pollInterval;
|
|
747
|
+
}
|
|
748
|
+
if (!existsSync(socketPath)) {
|
|
749
|
+
console.warn("⚠️ Daemon socket did not appear within 10s — continuing anyway");
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
const assistantDir = join(import.meta.dir, "..", "..", "..", "assistant");
|
|
753
|
+
const assistantIndex = join(assistantDir, "src", "index.ts");
|
|
754
|
+
|
|
755
|
+
if (!existsSync(assistantIndex)) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"vellum-daemon binary not found and assistant source not available.\n" +
|
|
758
|
+
" Ensure the daemon binary is bundled alongside the CLI, or run from the source tree.",
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const child = spawn("bun", ["run", assistantIndex, "daemon", "start"], {
|
|
763
|
+
stdio: "inherit",
|
|
764
|
+
env: { ...process.env },
|
|
737
765
|
});
|
|
738
|
-
|
|
739
|
-
|
|
766
|
+
|
|
767
|
+
await new Promise<void>((resolve, reject) => {
|
|
768
|
+
child.on("close", (code) => {
|
|
769
|
+
if (code === 0) {
|
|
770
|
+
resolve();
|
|
771
|
+
} else {
|
|
772
|
+
reject(new Error(`Daemon start exited with code ${code}`));
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
child.on("error", reject);
|
|
776
|
+
});
|
|
777
|
+
}
|
|
740
778
|
|
|
741
779
|
console.log("🌐 Starting gateway...");
|
|
742
780
|
const gatewayDir = join(import.meta.dir, "..", "..", "..", "gateway");
|
package/src/commands/retire.ts
CHANGED
|
@@ -92,12 +92,22 @@ async function retireCustom(entry: AssistantEntry): Promise<void> {
|
|
|
92
92
|
console.log(`\u2705 Custom instance retired.`);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
function parseSource(): string | undefined {
|
|
96
|
+
const args = process.argv.slice(4);
|
|
97
|
+
for (let i = 0; i < args.length; i++) {
|
|
98
|
+
if (args[i] === "--source" && args[i + 1]) {
|
|
99
|
+
return args[i + 1];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
95
105
|
export async function retire(): Promise<void> {
|
|
96
106
|
const name = process.argv[3];
|
|
97
107
|
|
|
98
108
|
if (!name) {
|
|
99
109
|
console.error("Error: Instance name is required.");
|
|
100
|
-
console.error("Usage: vellum-cli retire <name>");
|
|
110
|
+
console.error("Usage: vellum-cli retire <name> [--source <source>]");
|
|
101
111
|
process.exit(1);
|
|
102
112
|
}
|
|
103
113
|
|
|
@@ -108,6 +118,7 @@ export async function retire(): Promise<void> {
|
|
|
108
118
|
process.exit(1);
|
|
109
119
|
}
|
|
110
120
|
|
|
121
|
+
const source = parseSource();
|
|
111
122
|
const cloud = resolveCloud(entry);
|
|
112
123
|
|
|
113
124
|
if (cloud === "gcp") {
|
|
@@ -117,14 +128,14 @@ export async function retire(): Promise<void> {
|
|
|
117
128
|
console.error("Error: GCP project and zone not found in assistant config.");
|
|
118
129
|
process.exit(1);
|
|
119
130
|
}
|
|
120
|
-
await retireGcpInstance(name, project, zone);
|
|
131
|
+
await retireGcpInstance(name, project, zone, source);
|
|
121
132
|
} else if (cloud === "aws") {
|
|
122
133
|
const region = entry.region;
|
|
123
134
|
if (!region) {
|
|
124
135
|
console.error("Error: AWS region not found in assistant config.");
|
|
125
136
|
process.exit(1);
|
|
126
137
|
}
|
|
127
|
-
await retireAwsInstance(name, region);
|
|
138
|
+
await retireAwsInstance(name, region, source);
|
|
128
139
|
} else if (cloud === "local") {
|
|
129
140
|
await retireLocal();
|
|
130
141
|
} else if (cloud === "custom") {
|
package/src/lib/aws.ts
CHANGED
|
@@ -547,6 +547,7 @@ async function getInstanceIdByName(
|
|
|
547
547
|
export async function retireInstance(
|
|
548
548
|
name: string,
|
|
549
549
|
region: string,
|
|
550
|
+
source?: string,
|
|
550
551
|
): Promise<void> {
|
|
551
552
|
const instanceId = await getInstanceIdByName(name, region);
|
|
552
553
|
if (!instanceId) {
|
|
@@ -556,6 +557,23 @@ export async function retireInstance(
|
|
|
556
557
|
return;
|
|
557
558
|
}
|
|
558
559
|
|
|
560
|
+
if (source) {
|
|
561
|
+
try {
|
|
562
|
+
await exec("aws", [
|
|
563
|
+
"ec2",
|
|
564
|
+
"create-tags",
|
|
565
|
+
"--resources",
|
|
566
|
+
instanceId,
|
|
567
|
+
"--tags",
|
|
568
|
+
`Key=retired-by,Value=${source}`,
|
|
569
|
+
"--region",
|
|
570
|
+
region,
|
|
571
|
+
]);
|
|
572
|
+
} catch {
|
|
573
|
+
console.warn(`\u26a0\ufe0f Could not tag instance before termination`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
559
577
|
console.log(`\u{1F5D1}\ufe0f Terminating AWS instance ${name} (${instanceId})\n`);
|
|
560
578
|
|
|
561
579
|
const child = spawnChild(
|
package/src/lib/gcp.ts
CHANGED
|
@@ -278,6 +278,7 @@ export async function retireInstance(
|
|
|
278
278
|
name: string,
|
|
279
279
|
project: string,
|
|
280
280
|
zone: string,
|
|
281
|
+
source?: string,
|
|
281
282
|
): Promise<void> {
|
|
282
283
|
const exists = await instanceExists(name, project, zone);
|
|
283
284
|
if (!exists) {
|
|
@@ -287,6 +288,22 @@ export async function retireInstance(
|
|
|
287
288
|
return;
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
if (source) {
|
|
292
|
+
try {
|
|
293
|
+
await exec("gcloud", [
|
|
294
|
+
"compute",
|
|
295
|
+
"instances",
|
|
296
|
+
"add-labels",
|
|
297
|
+
name,
|
|
298
|
+
`--project=${project}`,
|
|
299
|
+
`--zone=${zone}`,
|
|
300
|
+
`--labels=retired-by=${source}`,
|
|
301
|
+
]);
|
|
302
|
+
} catch {
|
|
303
|
+
console.warn(`\u26a0\ufe0f Could not label instance before deletion`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
290
307
|
console.log(`\u{1F5D1}\ufe0f Deleting GCP instance ${name}\n`);
|
|
291
308
|
|
|
292
309
|
const child = spawn(
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
2
1
|
import { join } from "path";
|
|
3
2
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const constantsSource = await Bun.file(join(import.meta.dir, "constants.ts")).text();
|
|
4
|
+
const defaultMainScreenSource = await Bun.file(join(import.meta.dir, "..", "components", "DefaultMainScreen.tsx")).text();
|
|
6
5
|
|
|
7
6
|
function inlineLocalImports(source: string): string {
|
|
8
|
-
const constantsSource = readFileSync(CONSTANTS_PATH, "utf-8");
|
|
9
|
-
|
|
10
7
|
return source
|
|
11
8
|
.replace(/import\s*\{[^}]*\}\s*from\s*["'][^"']*\/constants["'];?\s*\n/, constantsSource + "\n")
|
|
12
9
|
.replace(/import\s*\{[^}]*\}\s*from\s*["']path["'];?\s*\n/, "");
|
|
13
10
|
}
|
|
14
11
|
|
|
15
12
|
export function buildInterfacesSeed(): string {
|
|
16
|
-
const
|
|
17
|
-
const mainWindowSource = inlineLocalImports(rawSource);
|
|
13
|
+
const mainWindowSource = inlineLocalImports(defaultMainScreenSource);
|
|
18
14
|
|
|
19
15
|
return `
|
|
20
16
|
INTERFACES_SEED_DIR="/tmp/interfaces-seed"
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
2
1
|
import { join } from "path";
|
|
3
2
|
|
|
4
|
-
const
|
|
3
|
+
const serverSource = await Bun.file(join(import.meta.dir, "..", "adapters", "openclaw-http-server.ts")).text();
|
|
5
4
|
|
|
6
5
|
export function buildOpenclawRuntimeServer(): string {
|
|
7
|
-
const serverSource = readFileSync(ADAPTER_PATH, "utf-8");
|
|
8
6
|
|
|
9
7
|
return `
|
|
10
8
|
cat > /opt/openclaw-runtime-server.ts << 'RUNTIME_SERVER_EOF'
|