@vellumai/cli 0.4.25 → 0.4.29

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.
@@ -3,11 +3,17 @@ import { renameSync, writeFileSync } from "fs";
3
3
  import { homedir } from "os";
4
4
  import { basename, dirname, join } from "path";
5
5
 
6
- import { findAssistantByName, removeAssistantEntry } from "../lib/assistant-config";
6
+ import {
7
+ findAssistantByName,
8
+ removeAssistantEntry,
9
+ } from "../lib/assistant-config";
7
10
  import type { AssistantEntry } from "../lib/assistant-config";
8
11
  import { retireInstance as retireAwsInstance } from "../lib/aws";
9
12
  import { retireInstance as retireGcpInstance } from "../lib/gcp";
10
- import { stopOrphanedDaemonProcesses, stopProcessByPidFile } from "../lib/process";
13
+ import {
14
+ stopOrphanedDaemonProcesses,
15
+ stopProcessByPidFile,
16
+ } from "../lib/process";
11
17
  import { getArchivePath, getMetadataPath } from "../lib/retire-archive";
12
18
  import { exec } from "../lib/step-runner";
13
19
  import { openLogFile, closeLogFile, writeToLogFile } from "../lib/xdg-log";
@@ -35,19 +41,25 @@ function extractHostFromUrl(url: string): string {
35
41
  }
36
42
 
37
43
  async function retireLocal(name: string, entry: AssistantEntry): Promise<void> {
38
- console.log("\u{1F5D1}\ufe0f Stopping local daemon...\n");
44
+ console.log("\u{1F5D1}\ufe0f Stopping local assistant...\n");
39
45
 
40
46
  const vellumDir = join(homedir(), ".vellum");
41
47
 
42
48
  // Stop daemon via PID file
43
49
  const daemonPidFile = join(vellumDir, "vellum.pid");
44
50
  const socketFile = join(vellumDir, "vellum.sock");
45
- const daemonStopped = await stopProcessByPidFile(daemonPidFile, "daemon", [socketFile]);
51
+ const daemonStopped = await stopProcessByPidFile(daemonPidFile, "daemon", [
52
+ socketFile,
53
+ ]);
46
54
 
47
55
  // Stop gateway via PID file
48
56
  const gatewayPidFile = join(vellumDir, "gateway.pid");
49
57
  await stopProcessByPidFile(gatewayPidFile, "gateway");
50
58
 
59
+ // Stop outbound proxy via PID file
60
+ const outboundProxyPidFile = join(vellumDir, "outbound-proxy.pid");
61
+ await stopProcessByPidFile(outboundProxyPidFile, "outbound-proxy");
62
+
51
63
  // If the PID file didn't track a running daemon, scan for orphaned
52
64
  // daemon processes that may have been started without writing a PID.
53
65
  if (!daemonStopped) {
@@ -63,7 +75,9 @@ async function retireLocal(name: string, entry: AssistantEntry): Promise<void> {
63
75
  try {
64
76
  renameSync(vellumDir, stagingDir);
65
77
  } catch (err) {
66
- console.warn(`⚠️ Failed to move ${vellumDir}: ${err instanceof Error ? err.message : err}`);
78
+ console.warn(
79
+ `⚠️ Failed to move ${vellumDir}: ${err instanceof Error ? err.message : err}`,
80
+ );
67
81
  console.warn("Skipping archive.");
68
82
  console.log("\u2705 Local instance retired.");
69
83
  return;
@@ -96,17 +110,21 @@ async function retireCustom(entry: AssistantEntry): Promise<void> {
96
110
  console.log(`\u{1F5D1}\ufe0f Retiring custom instance on ${sshHost}...\n`);
97
111
 
98
112
  const remoteCmd = [
99
- "bunx vellum daemon stop 2>/dev/null || true",
113
+ "bunx vellum sleep 2>/dev/null || true",
100
114
  "pkill -f gateway 2>/dev/null || true",
101
115
  "rm -rf ~/.vellum",
102
116
  ].join(" && ");
103
117
 
104
118
  try {
105
119
  await exec("ssh", [
106
- "-o", "StrictHostKeyChecking=no",
107
- "-o", "UserKnownHostsFile=/dev/null",
108
- "-o", "ConnectTimeout=10",
109
- "-o", "LogLevel=ERROR",
120
+ "-o",
121
+ "StrictHostKeyChecking=no",
122
+ "-o",
123
+ "UserKnownHostsFile=/dev/null",
124
+ "-o",
125
+ "ConnectTimeout=10",
126
+ "-o",
127
+ "LogLevel=ERROR",
110
128
  sshHost,
111
129
  remoteCmd,
112
130
  ]);
@@ -145,16 +163,24 @@ function teeConsoleToLogFile(fd: number | "ignore"): void {
145
163
  };
146
164
  console.warn = (...args: unknown[]) => {
147
165
  origWarn(...args);
148
- writeToLogFile(fd, `[${timestamp()}] WARN: ${args.map(String).join(" ")}\n`);
166
+ writeToLogFile(
167
+ fd,
168
+ `[${timestamp()}] WARN: ${args.map(String).join(" ")}\n`,
169
+ );
149
170
  };
150
171
  console.error = (...args: unknown[]) => {
151
172
  origError(...args);
152
- writeToLogFile(fd, `[${timestamp()}] ERROR: ${args.map(String).join(" ")}\n`);
173
+ writeToLogFile(
174
+ fd,
175
+ `[${timestamp()}] ERROR: ${args.map(String).join(" ")}\n`,
176
+ );
153
177
  };
154
178
  }
155
179
 
156
180
  export async function retire(): Promise<void> {
157
- const logFd = process.env.VELLUM_DESKTOP_APP ? openLogFile("retire.log") : "ignore";
181
+ const logFd = process.env.VELLUM_DESKTOP_APP
182
+ ? openLogFile("retire.log")
183
+ : "ignore";
158
184
  teeConsoleToLogFile(logFd);
159
185
 
160
186
  try {
@@ -201,7 +227,9 @@ async function retireInner(): Promise<void> {
201
227
  const project = entry.project;
202
228
  const zone = entry.zone;
203
229
  if (!project || !zone) {
204
- console.error("Error: GCP project and zone not found in assistant config.");
230
+ console.error(
231
+ "Error: GCP project and zone not found in assistant config.",
232
+ );
205
233
  process.exit(1);
206
234
  }
207
235
  await retireGcpInstance(name, project, zone, source);
@@ -8,7 +8,7 @@ export async function sleep(): Promise<void> {
8
8
  if (args.includes("--help") || args.includes("-h")) {
9
9
  console.log("Usage: vellum sleep");
10
10
  console.log("");
11
- console.log("Stop the daemon and gateway processes.");
11
+ console.log("Stop the assistant, gateway, and outbound-proxy processes.");
12
12
  process.exit(0);
13
13
  }
14
14
 
@@ -16,15 +16,16 @@ export async function sleep(): Promise<void> {
16
16
  const daemonPidFile = join(vellumDir, "vellum.pid");
17
17
  const socketFile = join(vellumDir, "vellum.sock");
18
18
  const gatewayPidFile = join(vellumDir, "gateway.pid");
19
+ const outboundProxyPidFile = join(vellumDir, "outbound-proxy.pid");
19
20
 
20
21
  // Stop daemon
21
22
  const daemonStopped = await stopProcessByPidFile(daemonPidFile, "daemon", [
22
23
  socketFile,
23
24
  ]);
24
25
  if (!daemonStopped) {
25
- console.log("Daemon is not running.");
26
+ console.log("Assistant is not running.");
26
27
  } else {
27
- console.log("Daemon stopped.");
28
+ console.log("Assistant stopped.");
28
29
  }
29
30
 
30
31
  // Stop gateway
@@ -34,4 +35,15 @@ export async function sleep(): Promise<void> {
34
35
  } else {
35
36
  console.log("Gateway stopped.");
36
37
  }
38
+
39
+ // Stop outbound proxy
40
+ const outboundProxyStopped = await stopProcessByPidFile(
41
+ outboundProxyPidFile,
42
+ "outbound-proxy",
43
+ );
44
+ if (!outboundProxyStopped) {
45
+ console.log("Outbound proxy is not running.");
46
+ } else {
47
+ console.log("Outbound proxy stopped.");
48
+ }
37
49
  }
@@ -1,13 +1,20 @@
1
1
  import { spawn } from "child_process";
2
2
 
3
- import { findAssistantByName, loadLatestAssistant } from "../lib/assistant-config";
3
+ import {
4
+ findAssistantByName,
5
+ loadLatestAssistant,
6
+ } from "../lib/assistant-config";
4
7
  import type { AssistantEntry } from "../lib/assistant-config";
5
8
 
6
9
  const SSH_OPTS = [
7
- "-o", "StrictHostKeyChecking=no",
8
- "-o", "UserKnownHostsFile=/dev/null",
9
- "-o", "ConnectTimeout=10",
10
- "-o", "LogLevel=ERROR",
10
+ "-o",
11
+ "StrictHostKeyChecking=no",
12
+ "-o",
13
+ "UserKnownHostsFile=/dev/null",
14
+ "-o",
15
+ "ConnectTimeout=10",
16
+ "-o",
17
+ "LogLevel=ERROR",
11
18
  ];
12
19
 
13
20
  function resolveCloud(entry: AssistantEntry): string {
@@ -40,7 +47,9 @@ export async function ssh(): Promise<void> {
40
47
  console.log("SSH into a remote assistant instance.");
41
48
  console.log("");
42
49
  console.log("Arguments:");
43
- console.log(" <name> Name of the assistant to connect to (defaults to latest)");
50
+ console.log(
51
+ " <name> Name of the assistant to connect to (defaults to latest)",
52
+ );
44
53
  process.exit(0);
45
54
  }
46
55
 
@@ -61,7 +70,7 @@ export async function ssh(): Promise<void> {
61
70
  if (cloud === "local") {
62
71
  console.error(
63
72
  "Cannot SSH into a local assistant. Local assistants run directly on this machine.\n" +
64
- `Use 'vellum ps ${entry.assistantId}' to check its processes instead.`,
73
+ `Use 'vellum ps ${entry.assistantId}' to check its processes instead.`,
65
74
  );
66
75
  process.exit(1);
67
76
  }
@@ -77,7 +86,9 @@ export async function ssh(): Promise<void> {
77
86
  const project = entry.project;
78
87
  const zone = entry.zone;
79
88
  if (!project || !zone) {
80
- console.error("Error: GCP project and zone not found in assistant config.");
89
+ console.error(
90
+ "Error: GCP project and zone not found in assistant config.",
91
+ );
81
92
  process.exit(1);
82
93
  }
83
94
 
@@ -102,11 +113,7 @@ export async function ssh(): Promise<void> {
102
113
 
103
114
  console.log(`🔗 Connecting to ${entry.assistantId} via ssh...\n`);
104
115
 
105
- child = spawn(
106
- "ssh",
107
- [...SSH_OPTS, sshTarget],
108
- { stdio: "inherit" },
109
- );
116
+ child = spawn("ssh", [...SSH_OPTS, sshTarget], { stdio: "inherit" });
110
117
  } else {
111
118
  console.error(`Error: Unknown cloud type '${cloud}'.`);
112
119
  process.exit(1);
@@ -1,4 +1,7 @@
1
- import { findAssistantByName, loadLatestAssistant } from "../lib/assistant-config";
1
+ import {
2
+ findAssistantByName,
3
+ loadLatestAssistant,
4
+ } from "../lib/assistant-config";
2
5
  import { runNgrokTunnel } from "../lib/ngrok";
3
6
 
4
7
  const VALID_PROVIDERS = ["vellum", "ngrok", "cloudflare", "tailscale"] as const;
@@ -70,9 +73,7 @@ export async function tunnel(): Promise<void> {
70
73
  `No assistant instance found with name '${assistantName}'.`,
71
74
  );
72
75
  } else {
73
- console.error(
74
- "No assistant instance found. Run `vellum hatch` first.",
75
- );
76
+ console.error("No assistant instance found. Run `vellum hatch` first.");
76
77
  }
77
78
  process.exit(1);
78
79
  }
@@ -82,7 +83,5 @@ export async function tunnel(): Promise<void> {
82
83
  return;
83
84
  }
84
85
 
85
- throw new Error(
86
- `Tunnel provider '${provider}' is not yet implemented.`,
87
- );
86
+ throw new Error(`Tunnel provider '${provider}' is not yet implemented.`);
88
87
  }
@@ -4,21 +4,27 @@ import { join } from "path";
4
4
 
5
5
  import { loadAllAssistants } from "../lib/assistant-config";
6
6
  import { isProcessAlive } from "../lib/process";
7
- import { startLocalDaemon, startGateway } from "../lib/local";
7
+ import {
8
+ startLocalDaemon,
9
+ startGateway,
10
+ startOutboundProxy,
11
+ } from "../lib/local";
8
12
 
9
13
  export async function wake(): Promise<void> {
10
14
  const args = process.argv.slice(3);
11
15
  if (args.includes("--help") || args.includes("-h")) {
12
16
  console.log("Usage: vellum wake");
13
17
  console.log("");
14
- console.log("Start the daemon and gateway processes.");
18
+ console.log("Start the assistant and gateway processes.");
15
19
  process.exit(0);
16
20
  }
17
21
 
18
22
  const assistants = loadAllAssistants();
19
23
  const hasLocal = assistants.some((a) => a.cloud === "local");
20
24
  if (!hasLocal) {
21
- console.error("Error: No local assistant found in lock file. Run 'vellum hatch local' first.");
25
+ console.error(
26
+ "Error: No local assistant found in lock file. Run 'vellum hatch local' first.",
27
+ );
22
28
  process.exit(1);
23
29
  }
24
30
 
@@ -34,7 +40,7 @@ export async function wake(): Promise<void> {
34
40
  try {
35
41
  process.kill(pid, 0);
36
42
  daemonRunning = true;
37
- console.log(`Daemon already running (pid ${pid}).`);
43
+ console.log(`Assistant already running (pid ${pid}).`);
38
44
  } catch {
39
45
  // Process not alive, will start below
40
46
  }
@@ -56,5 +62,8 @@ export async function wake(): Promise<void> {
56
62
  }
57
63
  }
58
64
 
65
+ // Start outbound proxy
66
+ await startOutboundProxy();
67
+
59
68
  console.log("✅ Wake complete.");
60
69
  }