@vellumai/cli 0.3.0 โ 0.3.2
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 +46 -9
- package/src/lib/local.ts +23 -0
package/package.json
CHANGED
package/src/commands/hatch.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from "crypto";
|
|
2
|
-
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
|
-
import { tmpdir, userInfo } from "os";
|
|
5
|
-
import { join } from "path";
|
|
4
|
+
import { homedir, tmpdir, userInfo } from "os";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
6
|
|
|
7
7
|
import { buildOpenclawStartupScript } from "../adapters/openclaw";
|
|
8
|
-
import { saveAssistantEntry } from "../lib/assistant-config";
|
|
8
|
+
import { loadAllAssistants, saveAssistantEntry } from "../lib/assistant-config";
|
|
9
9
|
import type { AssistantEntry } from "../lib/assistant-config";
|
|
10
10
|
import { hatchAws } from "../lib/aws";
|
|
11
11
|
import {
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
import type { RemoteHost, Species } from "../lib/constants";
|
|
18
18
|
import { hatchGcp } from "../lib/gcp";
|
|
19
19
|
import type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
20
|
-
import { startLocalDaemon, startGateway } from "../lib/local";
|
|
20
|
+
import { startLocalDaemon, startGateway, stopLocalProcesses } from "../lib/local";
|
|
21
|
+
import { isProcessAlive } from "../lib/process";
|
|
21
22
|
import { generateRandomSuffix } from "../lib/random-name";
|
|
22
23
|
import { exec } from "../lib/step-runner";
|
|
23
24
|
|
|
@@ -514,13 +515,36 @@ async function hatchLocal(species: Species, name: string | null, daemonOnly: boo
|
|
|
514
515
|
const instanceName =
|
|
515
516
|
name ?? process.env.VELLUM_ASSISTANT_NAME ?? `${species}-${generateRandomSuffix()}`;
|
|
516
517
|
|
|
518
|
+
// Clean up stale local state: if daemon/gateway processes are running but
|
|
519
|
+
// the lock file has no entries, stop them before starting fresh.
|
|
520
|
+
const vellumDir = join(homedir(), ".vellum");
|
|
521
|
+
const existingAssistants = loadAllAssistants();
|
|
522
|
+
const localAssistants = existingAssistants.filter((a) => a.cloud === "local");
|
|
523
|
+
if (localAssistants.length === 0) {
|
|
524
|
+
const daemonPid = isProcessAlive(join(vellumDir, "vellum.pid"));
|
|
525
|
+
const gatewayPid = isProcessAlive(join(vellumDir, "gateway.pid"));
|
|
526
|
+
if (daemonPid.alive || gatewayPid.alive) {
|
|
527
|
+
console.log("๐งน Cleaning up stale local processes (no lock file entry)...\n");
|
|
528
|
+
await stopLocalProcesses();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
517
532
|
console.log(`๐ฅ Hatching local assistant: ${instanceName}`);
|
|
518
533
|
console.log(` Species: ${species}`);
|
|
519
534
|
console.log("");
|
|
520
535
|
|
|
521
536
|
await startLocalDaemon();
|
|
522
537
|
|
|
523
|
-
|
|
538
|
+
let runtimeUrl: string;
|
|
539
|
+
try {
|
|
540
|
+
runtimeUrl = await startGateway();
|
|
541
|
+
} catch (error) {
|
|
542
|
+
// Gateway failed โ stop the daemon we just started so we don't leave
|
|
543
|
+
// orphaned processes with no lock file entry.
|
|
544
|
+
console.error(`\nโ Gateway startup failed โ stopping daemon to avoid orphaned processes.`);
|
|
545
|
+
await stopLocalProcesses();
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
524
548
|
|
|
525
549
|
const baseDataDir = join(process.env.BASE_DATA_DIR?.trim() || (process.env.HOME ?? userInfo().homedir), ".vellum");
|
|
526
550
|
const localEntry: AssistantEntry = {
|
|
@@ -545,14 +569,27 @@ async function hatchLocal(species: Species, name: string | null, daemonOnly: boo
|
|
|
545
569
|
}
|
|
546
570
|
|
|
547
571
|
function getCliVersion(): string {
|
|
572
|
+
// Strategy 1: createRequire โ works in Bun dev (source tree).
|
|
548
573
|
try {
|
|
549
|
-
// Use createRequire for JSON import โ works in both Bun dev and compiled binary.
|
|
550
574
|
const require = createRequire(import.meta.url);
|
|
551
575
|
const pkg = require("../../package.json") as { version?: string };
|
|
552
|
-
|
|
576
|
+
if (pkg.version) return pkg.version;
|
|
553
577
|
} catch {
|
|
554
|
-
|
|
578
|
+
// Fall through to next strategy.
|
|
555
579
|
}
|
|
580
|
+
|
|
581
|
+
// Strategy 2: Read package.json adjacent to the compiled binary.
|
|
582
|
+
try {
|
|
583
|
+
const binPkg = join(dirname(process.execPath), "package.json");
|
|
584
|
+
if (existsSync(binPkg)) {
|
|
585
|
+
const pkg = JSON.parse(readFileSync(binPkg, "utf-8")) as { version?: string };
|
|
586
|
+
if (pkg.version) return pkg.version;
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
// Fall through.
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return "unknown";
|
|
556
593
|
}
|
|
557
594
|
|
|
558
595
|
export async function hatch(): Promise<void> {
|
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;
|
|
@@ -339,3 +347,18 @@ export async function startGateway(): Promise<string> {
|
|
|
339
347
|
console.log("โ
Gateway started\n");
|
|
340
348
|
return publicUrl || `http://localhost:${GATEWAY_PORT}`;
|
|
341
349
|
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Stop any locally-running daemon and gateway processes and clean up
|
|
353
|
+
* PID/socket files. Called when hatch fails partway through so we don't
|
|
354
|
+
* leave orphaned processes with no lock file entry.
|
|
355
|
+
*/
|
|
356
|
+
export async function stopLocalProcesses(): Promise<void> {
|
|
357
|
+
const vellumDir = join(homedir(), ".vellum");
|
|
358
|
+
const daemonPidFile = join(vellumDir, "vellum.pid");
|
|
359
|
+
const socketFile = join(vellumDir, "vellum.sock");
|
|
360
|
+
await stopProcessByPidFile(daemonPidFile, "daemon", [socketFile]);
|
|
361
|
+
|
|
362
|
+
const gatewayPidFile = join(vellumDir, "gateway.pid");
|
|
363
|
+
await stopProcessByPidFile(gatewayPidFile, "gateway");
|
|
364
|
+
}
|