@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.
- package/README.md +24 -24
- package/package.json +1 -1
- package/src/__tests__/assistant-config.test.ts +17 -5
- package/src/__tests__/retire-archive.test.ts +6 -2
- package/src/adapters/openclaw-http-server.ts +22 -7
- package/src/commands/autonomy.ts +10 -11
- package/src/commands/client.ts +25 -9
- package/src/commands/config.ts +2 -6
- package/src/commands/contacts.ts +1 -4
- package/src/commands/hatch.ts +131 -36
- package/src/commands/login.ts +6 -2
- package/src/commands/pair.ts +26 -9
- package/src/commands/ps.ts +55 -23
- package/src/commands/recover.ts +4 -2
- package/src/commands/retire.ts +42 -14
- package/src/commands/sleep.ts +15 -3
- package/src/commands/ssh.ts +20 -13
- package/src/commands/tunnel.ts +6 -7
- package/src/commands/wake.ts +13 -4
- package/src/components/DefaultMainScreen.tsx +309 -99
- package/src/index.ts +2 -2
- package/src/lib/assistant-config.ts +9 -3
- package/src/lib/aws.ts +36 -11
- package/src/lib/constants.ts +3 -1
- package/src/lib/doctor-client.ts +23 -7
- package/src/lib/gcp.ts +74 -24
- package/src/lib/health-check.ts +14 -4
- package/src/lib/local.ts +249 -33
- package/src/lib/ngrok.ts +1 -3
- package/src/lib/openclaw-runtime-server.ts +7 -2
- package/src/lib/platform-client.ts +16 -3
- package/src/lib/xdg-log.ts +25 -5
package/src/commands/retire.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
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", [
|
|
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(
|
|
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
|
|
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",
|
|
107
|
-
"
|
|
108
|
-
"-o",
|
|
109
|
-
"
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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);
|
package/src/commands/sleep.ts
CHANGED
|
@@ -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
|
|
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("
|
|
26
|
+
console.log("Assistant is not running.");
|
|
26
27
|
} else {
|
|
27
|
-
console.log("
|
|
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
|
}
|
package/src/commands/ssh.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
|
|
3
|
-
import {
|
|
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",
|
|
8
|
-
"
|
|
9
|
-
"-o",
|
|
10
|
-
"
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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);
|
package/src/commands/tunnel.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
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
|
}
|
package/src/commands/wake.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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(
|
|
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(`
|
|
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
|
}
|