@vellumai/cli 0.3.0 โ 0.3.3
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 +32 -13
- package/src/index.ts +28 -4
- package/src/lib/local.ts +52 -7
package/package.json
CHANGED
package/src/commands/hatch.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { randomBytes } from "crypto";
|
|
2
2
|
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
-
import {
|
|
4
|
-
import { tmpdir, userInfo } from "os";
|
|
3
|
+
import { homedir, tmpdir, userInfo } from "os";
|
|
5
4
|
import { join } from "path";
|
|
6
5
|
|
|
6
|
+
// Direct import โ bun embeds this at compile time so it works in compiled binaries.
|
|
7
|
+
import cliPkg from "../../package.json";
|
|
8
|
+
|
|
7
9
|
import { buildOpenclawStartupScript } from "../adapters/openclaw";
|
|
8
|
-
import { saveAssistantEntry } from "../lib/assistant-config";
|
|
10
|
+
import { loadAllAssistants, saveAssistantEntry } from "../lib/assistant-config";
|
|
9
11
|
import type { AssistantEntry } from "../lib/assistant-config";
|
|
10
12
|
import { hatchAws } from "../lib/aws";
|
|
11
13
|
import {
|
|
@@ -17,7 +19,8 @@ import {
|
|
|
17
19
|
import type { RemoteHost, Species } from "../lib/constants";
|
|
18
20
|
import { hatchGcp } from "../lib/gcp";
|
|
19
21
|
import type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
20
|
-
import { startLocalDaemon, startGateway } from "../lib/local";
|
|
22
|
+
import { startLocalDaemon, startGateway, stopLocalProcesses } from "../lib/local";
|
|
23
|
+
import { isProcessAlive } from "../lib/process";
|
|
21
24
|
import { generateRandomSuffix } from "../lib/random-name";
|
|
22
25
|
import { exec } from "../lib/step-runner";
|
|
23
26
|
|
|
@@ -514,13 +517,36 @@ async function hatchLocal(species: Species, name: string | null, daemonOnly: boo
|
|
|
514
517
|
const instanceName =
|
|
515
518
|
name ?? process.env.VELLUM_ASSISTANT_NAME ?? `${species}-${generateRandomSuffix()}`;
|
|
516
519
|
|
|
520
|
+
// Clean up stale local state: if daemon/gateway processes are running but
|
|
521
|
+
// the lock file has no entries, stop them before starting fresh.
|
|
522
|
+
const vellumDir = join(homedir(), ".vellum");
|
|
523
|
+
const existingAssistants = loadAllAssistants();
|
|
524
|
+
const localAssistants = existingAssistants.filter((a) => a.cloud === "local");
|
|
525
|
+
if (localAssistants.length === 0) {
|
|
526
|
+
const daemonPid = isProcessAlive(join(vellumDir, "vellum.pid"));
|
|
527
|
+
const gatewayPid = isProcessAlive(join(vellumDir, "gateway.pid"));
|
|
528
|
+
if (daemonPid.alive || gatewayPid.alive) {
|
|
529
|
+
console.log("๐งน Cleaning up stale local processes (no lock file entry)...\n");
|
|
530
|
+
await stopLocalProcesses();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
517
534
|
console.log(`๐ฅ Hatching local assistant: ${instanceName}`);
|
|
518
535
|
console.log(` Species: ${species}`);
|
|
519
536
|
console.log("");
|
|
520
537
|
|
|
521
538
|
await startLocalDaemon();
|
|
522
539
|
|
|
523
|
-
|
|
540
|
+
let runtimeUrl: string;
|
|
541
|
+
try {
|
|
542
|
+
runtimeUrl = await startGateway();
|
|
543
|
+
} catch (error) {
|
|
544
|
+
// Gateway failed โ stop the daemon we just started so we don't leave
|
|
545
|
+
// orphaned processes with no lock file entry.
|
|
546
|
+
console.error(`\nโ Gateway startup failed โ stopping daemon to avoid orphaned processes.`);
|
|
547
|
+
await stopLocalProcesses();
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
524
550
|
|
|
525
551
|
const baseDataDir = join(process.env.BASE_DATA_DIR?.trim() || (process.env.HOME ?? userInfo().homedir), ".vellum");
|
|
526
552
|
const localEntry: AssistantEntry = {
|
|
@@ -545,14 +571,7 @@ async function hatchLocal(species: Species, name: string | null, daemonOnly: boo
|
|
|
545
571
|
}
|
|
546
572
|
|
|
547
573
|
function getCliVersion(): string {
|
|
548
|
-
|
|
549
|
-
// Use createRequire for JSON import โ works in both Bun dev and compiled binary.
|
|
550
|
-
const require = createRequire(import.meta.url);
|
|
551
|
-
const pkg = require("../../package.json") as { version?: string };
|
|
552
|
-
return pkg.version ?? "unknown";
|
|
553
|
-
} catch {
|
|
554
|
-
return "unknown";
|
|
555
|
-
}
|
|
574
|
+
return cliPkg.version ?? "unknown";
|
|
556
575
|
}
|
|
557
576
|
|
|
558
577
|
export async function hatch(): Promise<void> {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
3
6
|
import { client } from "./commands/client";
|
|
4
7
|
import { hatch } from "./commands/hatch";
|
|
5
8
|
import { ps } from "./commands/ps";
|
|
@@ -23,7 +26,7 @@ async function main() {
|
|
|
23
26
|
const commandName = args[0];
|
|
24
27
|
|
|
25
28
|
if (!commandName || commandName === "--help" || commandName === "-h") {
|
|
26
|
-
console.log("Usage: vellum
|
|
29
|
+
console.log("Usage: vellum <command> [options]");
|
|
27
30
|
console.log("");
|
|
28
31
|
console.log("Commands:");
|
|
29
32
|
console.log(" client Connect to a hatched assistant");
|
|
@@ -38,9 +41,30 @@ async function main() {
|
|
|
38
41
|
const command = commands[commandName as CommandName];
|
|
39
42
|
|
|
40
43
|
if (!command) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
try {
|
|
45
|
+
const require = createRequire(import.meta.url);
|
|
46
|
+
const assistantPkgPath = require.resolve(
|
|
47
|
+
"@vellumai/assistant/package.json"
|
|
48
|
+
);
|
|
49
|
+
const assistantEntry = join(
|
|
50
|
+
dirname(assistantPkgPath),
|
|
51
|
+
"src",
|
|
52
|
+
"index.ts"
|
|
53
|
+
);
|
|
54
|
+
const child = spawn("bun", ["run", assistantEntry, ...args], {
|
|
55
|
+
stdio: "inherit",
|
|
56
|
+
});
|
|
57
|
+
child.on("exit", (code) => {
|
|
58
|
+
process.exit(code ?? 1);
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
console.error(`Unknown command: ${commandName}`);
|
|
62
|
+
console.error(
|
|
63
|
+
"Install the full stack with: bun install -g vellum"
|
|
64
|
+
);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
44
68
|
}
|
|
45
69
|
|
|
46
70
|
try {
|
package/src/lib/local.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { dirname, join } from "path";
|
|
|
6
6
|
|
|
7
7
|
import { loadAllAssistants, loadLatestAssistant } from "./assistant-config.js";
|
|
8
8
|
import { GATEWAY_PORT } from "./constants.js";
|
|
9
|
+
import { stopProcessByPidFile } from "./process.js";
|
|
9
10
|
|
|
10
11
|
const _require = createRequire(import.meta.url);
|
|
11
12
|
|
|
@@ -49,11 +50,18 @@ function resolveGatewayDir(): string {
|
|
|
49
50
|
return override;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
// Source tree: cli/src/lib/ โ ../../.. โ repo root โ gateway/
|
|
52
54
|
const sourceDir = join(import.meta.dir, "..", "..", "..", "gateway");
|
|
53
55
|
if (isGatewaySourceDir(sourceDir)) {
|
|
54
56
|
return sourceDir;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
// Compiled binary: gateway/ bundled adjacent to the CLI executable.
|
|
60
|
+
const binGateway = join(dirname(process.execPath), "gateway");
|
|
61
|
+
if (isGatewaySourceDir(binGateway)) {
|
|
62
|
+
return binGateway;
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
const cwdSourceDir = findGatewaySourceFromCwd();
|
|
58
66
|
if (cwdSourceDir) {
|
|
59
67
|
return cwdSourceDir;
|
|
@@ -286,7 +294,7 @@ export async function startGateway(): Promise<string> {
|
|
|
286
294
|
}
|
|
287
295
|
|
|
288
296
|
console.log("๐ Starting gateway...");
|
|
289
|
-
|
|
297
|
+
|
|
290
298
|
// Only auto-configure default routing when the workspace has exactly one
|
|
291
299
|
// assistant. In multi-assistant deployments, falling back to "default"
|
|
292
300
|
// would silently deliver unmapped Telegram chats to whichever assistant was
|
|
@@ -323,12 +331,34 @@ export async function startGateway(): Promise<string> {
|
|
|
323
331
|
console.log(` Ingress URL: ${ingressPublicBaseUrl}`);
|
|
324
332
|
}
|
|
325
333
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
334
|
+
let gateway;
|
|
335
|
+
|
|
336
|
+
if (process.env.VELLUM_DESKTOP_APP) {
|
|
337
|
+
// Desktop app: spawn the compiled gateway binary directly (mirrors daemon pattern).
|
|
338
|
+
const gatewayBinary = join(dirname(process.execPath), "vellum-gateway");
|
|
339
|
+
if (!existsSync(gatewayBinary)) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`vellum-gateway binary not found at ${gatewayBinary}.\n` +
|
|
342
|
+
" Ensure the gateway binary is bundled alongside the CLI in the app bundle.",
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
gateway = spawn(gatewayBinary, [], {
|
|
347
|
+
detached: true,
|
|
348
|
+
stdio: "ignore",
|
|
349
|
+
env: gatewayEnv,
|
|
350
|
+
});
|
|
351
|
+
} else {
|
|
352
|
+
// Source tree / bunx: resolve the gateway source directory and run via bun.
|
|
353
|
+
const gatewayDir = resolveGatewayDir();
|
|
354
|
+
gateway = spawn("bun", ["run", "src/index.ts"], {
|
|
355
|
+
cwd: gatewayDir,
|
|
356
|
+
detached: true,
|
|
357
|
+
stdio: "ignore",
|
|
358
|
+
env: gatewayEnv,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
332
362
|
gateway.unref();
|
|
333
363
|
|
|
334
364
|
if (gateway.pid) {
|
|
@@ -339,3 +369,18 @@ export async function startGateway(): Promise<string> {
|
|
|
339
369
|
console.log("โ
Gateway started\n");
|
|
340
370
|
return publicUrl || `http://localhost:${GATEWAY_PORT}`;
|
|
341
371
|
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Stop any locally-running daemon and gateway processes and clean up
|
|
375
|
+
* PID/socket files. Called when hatch fails partway through so we don't
|
|
376
|
+
* leave orphaned processes with no lock file entry.
|
|
377
|
+
*/
|
|
378
|
+
export async function stopLocalProcesses(): Promise<void> {
|
|
379
|
+
const vellumDir = join(homedir(), ".vellum");
|
|
380
|
+
const daemonPidFile = join(vellumDir, "vellum.pid");
|
|
381
|
+
const socketFile = join(vellumDir, "vellum.sock");
|
|
382
|
+
await stopProcessByPidFile(daemonPidFile, "daemon", [socketFile]);
|
|
383
|
+
|
|
384
|
+
const gatewayPidFile = join(vellumDir, "gateway.pid");
|
|
385
|
+
await stopProcessByPidFile(gatewayPidFile, "gateway");
|
|
386
|
+
}
|