@vellumai/cli 0.4.19 → 0.4.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/cli",
3
- "version": "0.4.19",
3
+ "version": "0.4.21",
4
4
  "description": "CLI tools for vellum-assistant",
5
5
  "type": "module",
6
6
  "exports": {
@@ -7,7 +7,7 @@ import { findAssistantByName, removeAssistantEntry } from "../lib/assistant-conf
7
7
  import type { AssistantEntry } from "../lib/assistant-config";
8
8
  import { retireInstance as retireAwsInstance } from "../lib/aws";
9
9
  import { retireInstance as retireGcpInstance } from "../lib/gcp";
10
- import { stopProcessByPidFile } from "../lib/process";
10
+ import { stopOrphanedDaemonProcesses, stopProcessByPidFile } from "../lib/process";
11
11
  import { getArchivePath, getMetadataPath } from "../lib/retire-archive";
12
12
  import { exec } from "../lib/step-runner";
13
13
  import { openLogFile, closeLogFile, writeToLogFile } from "../lib/xdg-log";
@@ -38,29 +38,20 @@ async function retireLocal(name: string, entry: AssistantEntry): Promise<void> {
38
38
  console.log("\u{1F5D1}\ufe0f Stopping local daemon...\n");
39
39
 
40
40
  const vellumDir = join(homedir(), ".vellum");
41
- const isDesktopApp = !!process.env.VELLUM_DESKTOP_APP;
42
41
 
43
42
  // Stop daemon via PID file
44
43
  const daemonPidFile = join(vellumDir, "vellum.pid");
45
44
  const socketFile = join(vellumDir, "vellum.sock");
46
- await stopProcessByPidFile(daemonPidFile, "daemon", [socketFile]);
45
+ const daemonStopped = await stopProcessByPidFile(daemonPidFile, "daemon", [socketFile]);
47
46
 
48
47
  // Stop gateway via PID file
49
48
  const gatewayPidFile = join(vellumDir, "gateway.pid");
50
49
  await stopProcessByPidFile(gatewayPidFile, "gateway");
51
50
 
52
- if (!isDesktopApp) {
53
- // Non-desktop: also stop daemon via bunx (fallback)
54
- try {
55
- const child = spawn("bunx", ["vellum", "daemon", "stop"], {
56
- stdio: "inherit",
57
- });
58
-
59
- await new Promise<void>((resolve) => {
60
- child.on("close", () => resolve());
61
- child.on("error", () => resolve());
62
- });
63
- } catch {}
51
+ // If the PID file didn't track a running daemon, scan for orphaned
52
+ // daemon processes that may have been started without writing a PID.
53
+ if (!daemonStopped) {
54
+ await stopOrphanedDaemonProcesses();
64
55
  }
65
56
 
66
57
  // Move ~/.vellum out of the way so the path is immediately available for the
@@ -144,3 +144,39 @@ export async function stopProcessByPidFile(
144
144
 
145
145
  return stopped;
146
146
  }
147
+
148
+ /**
149
+ * Find and stop any vellum daemon processes that may not be tracked by a PID
150
+ * file. Scans `ps` output for the `vellum-daemon` binary name.
151
+ *
152
+ * Returns true if at least one process was stopped.
153
+ */
154
+ export async function stopOrphanedDaemonProcesses(): Promise<boolean> {
155
+ let output: string;
156
+ try {
157
+ output = execFileSync("ps", ["-axww", "-o", "pid=,command="], {
158
+ encoding: "utf-8",
159
+ timeout: 5000,
160
+ stdio: ["ignore", "pipe", "ignore"],
161
+ });
162
+ } catch {
163
+ return false;
164
+ }
165
+
166
+ let stopped = false;
167
+ for (const line of output.split("\n")) {
168
+ const trimmed = line.trim();
169
+ if (!trimmed) continue;
170
+ const spaceIdx = trimmed.indexOf(" ");
171
+ if (spaceIdx === -1) continue;
172
+ const pid = parseInt(trimmed.slice(0, spaceIdx), 10);
173
+ if (isNaN(pid) || pid === process.pid) continue;
174
+ const cmd = trimmed.slice(spaceIdx + 1);
175
+
176
+ if (cmd.includes("vellum-daemon")) {
177
+ const result = await stopProcess(pid, "orphaned daemon");
178
+ if (result) stopped = true;
179
+ }
180
+ }
181
+ return stopped;
182
+ }