@vellumai/cli 0.4.0 → 0.4.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/adapters/install.sh +84 -0
- package/src/commands/hatch.ts +49 -2
- package/src/commands/login.ts +27 -0
- package/src/commands/pair.ts +8 -0
- package/src/commands/ps.ts +11 -0
- package/src/commands/recover.ts +11 -0
- package/src/commands/retire.ts +14 -0
- package/src/commands/sleep.ts +8 -0
- package/src/commands/ssh.ts +11 -0
- package/src/commands/wake.ts +8 -0
- package/src/index.ts +2 -2
package/package.json
CHANGED
package/src/adapters/install.sh
CHANGED
|
@@ -79,6 +79,82 @@ ensure_bun() {
|
|
|
79
79
|
success "bun installed ($(bun --version))"
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
# Ensure ~/.bun/bin is in the user's shell profile so bun and vellum are
|
|
83
|
+
# available in new terminal sessions. The bun installer sometimes skips
|
|
84
|
+
# this (e.g. when stdin is piped via curl | bash).
|
|
85
|
+
configure_shell_profile() {
|
|
86
|
+
local bun_line='export BUN_INSTALL="$HOME/.bun"'
|
|
87
|
+
local path_line='export PATH="$BUN_INSTALL/bin:$PATH"'
|
|
88
|
+
local snippet
|
|
89
|
+
snippet=$(printf '\n# bun\n%s\n%s\n' "$bun_line" "$path_line")
|
|
90
|
+
|
|
91
|
+
local profiles=()
|
|
92
|
+
local shell_name="${SHELL:-}"
|
|
93
|
+
|
|
94
|
+
if [[ "$shell_name" == */zsh ]]; then
|
|
95
|
+
profiles+=("$HOME/.zshrc")
|
|
96
|
+
elif [[ "$shell_name" == */bash ]]; then
|
|
97
|
+
# Write to both .bashrc (non-login shells, e.g. new terminal on Linux)
|
|
98
|
+
# and .bash_profile (login shells, e.g. macOS Terminal.app)
|
|
99
|
+
profiles+=("$HOME/.bashrc")
|
|
100
|
+
[ -f "$HOME/.bash_profile" ] && profiles+=("$HOME/.bash_profile")
|
|
101
|
+
else
|
|
102
|
+
# Unknown shell — try both
|
|
103
|
+
profiles+=("$HOME/.bashrc")
|
|
104
|
+
[ -f "$HOME/.zshrc" ] && profiles+=("$HOME/.zshrc")
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
for profile in "${profiles[@]}"; do
|
|
108
|
+
if [ -f "$profile" ] && grep -q 'BUN_INSTALL' "$profile" 2>/dev/null; then
|
|
109
|
+
continue
|
|
110
|
+
fi
|
|
111
|
+
printf '%s\n' "$snippet" >> "$profile"
|
|
112
|
+
success "Added bun to PATH in $profile"
|
|
113
|
+
done
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Create a symlink so `vellum` is available without ~/.bun/bin in PATH.
|
|
117
|
+
# Tries /usr/local/bin first (works on most systems), falls back to
|
|
118
|
+
# ~/.local/bin (user-writable, no sudo needed).
|
|
119
|
+
# This is best-effort — failure must not abort the install script.
|
|
120
|
+
symlink_vellum() {
|
|
121
|
+
local vellum_bin="$HOME/.bun/bin/vellum"
|
|
122
|
+
if [ ! -f "$vellum_bin" ]; then
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Skip if vellum is already resolvable outside of ~/.bun/bin
|
|
127
|
+
local resolved
|
|
128
|
+
resolved=$(command -v vellum 2>/dev/null || true)
|
|
129
|
+
if [ -n "$resolved" ] && [ "$resolved" != "$vellum_bin" ]; then
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Try /usr/local/bin (may need sudo on some systems)
|
|
134
|
+
if [ -d "/usr/local/bin" ] && [ -w "/usr/local/bin" ]; then
|
|
135
|
+
if ln -sf "$vellum_bin" /usr/local/bin/vellum 2>/dev/null; then
|
|
136
|
+
success "Symlinked /usr/local/bin/vellum → $vellum_bin"
|
|
137
|
+
return 0
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Fallback: ~/.local/bin
|
|
142
|
+
local local_bin="$HOME/.local/bin"
|
|
143
|
+
mkdir -p "$local_bin" 2>/dev/null || true
|
|
144
|
+
if ln -sf "$vellum_bin" "$local_bin/vellum" 2>/dev/null; then
|
|
145
|
+
success "Symlinked $local_bin/vellum → $vellum_bin"
|
|
146
|
+
# Ensure ~/.local/bin is in PATH in shell profile
|
|
147
|
+
for profile in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
|
|
148
|
+
if [ -f "$profile" ] && ! grep -q "$local_bin" "$profile" 2>/dev/null; then
|
|
149
|
+
printf '\nexport PATH="%s:$PATH"\n' "$local_bin" >> "$profile"
|
|
150
|
+
fi
|
|
151
|
+
done
|
|
152
|
+
return 0
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
return 0
|
|
156
|
+
}
|
|
157
|
+
|
|
82
158
|
install_vellum() {
|
|
83
159
|
if command -v vellum >/dev/null 2>&1; then
|
|
84
160
|
info "Updating vellum to latest..."
|
|
@@ -103,7 +179,15 @@ main() {
|
|
|
103
179
|
|
|
104
180
|
ensure_git
|
|
105
181
|
ensure_bun
|
|
182
|
+
configure_shell_profile
|
|
106
183
|
install_vellum
|
|
184
|
+
symlink_vellum
|
|
185
|
+
|
|
186
|
+
# Source the shell profile so vellum hatch runs with the correct PATH
|
|
187
|
+
# in this session (the profile changes only take effect in new shells
|
|
188
|
+
# otherwise).
|
|
189
|
+
export BUN_INSTALL="$HOME/.bun"
|
|
190
|
+
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
107
191
|
|
|
108
192
|
info "Running vellum hatch..."
|
|
109
193
|
printf "\n"
|
package/src/commands/hatch.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { loadAllAssistants, saveAssistantEntry, syncConfigToLockfile } from "../
|
|
|
13
13
|
import type { AssistantEntry } from "../lib/assistant-config";
|
|
14
14
|
import { hatchAws } from "../lib/aws";
|
|
15
15
|
import {
|
|
16
|
+
GATEWAY_PORT,
|
|
16
17
|
SPECIES_CONFIG,
|
|
17
18
|
VALID_REMOTE_HOSTS,
|
|
18
19
|
VALID_SPECIES,
|
|
@@ -21,6 +22,7 @@ import type { RemoteHost, Species } from "../lib/constants";
|
|
|
21
22
|
import { hatchGcp } from "../lib/gcp";
|
|
22
23
|
import type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
23
24
|
import { startLocalDaemon, startGateway, stopLocalProcesses } from "../lib/local";
|
|
25
|
+
import { probePort } from "../lib/port-probe";
|
|
24
26
|
import { isProcessAlive } from "../lib/process";
|
|
25
27
|
import { generateRandomSuffix } from "../lib/random-name";
|
|
26
28
|
import { validateAssistantName } from "../lib/retire-archive";
|
|
@@ -155,7 +157,22 @@ function parseArgs(): HatchArgs {
|
|
|
155
157
|
|
|
156
158
|
for (let i = 0; i < args.length; i++) {
|
|
157
159
|
const arg = args[i];
|
|
158
|
-
if (arg === "-
|
|
160
|
+
if (arg === "--help" || arg === "-h") {
|
|
161
|
+
console.log("Usage: vellum hatch [species] [options]");
|
|
162
|
+
console.log("");
|
|
163
|
+
console.log("Create a new assistant instance.");
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log("Species:");
|
|
166
|
+
console.log(" vellum Default assistant (default)");
|
|
167
|
+
console.log(" openclaw OpenClaw adapter");
|
|
168
|
+
console.log("");
|
|
169
|
+
console.log("Options:");
|
|
170
|
+
console.log(" -d Run in detached mode");
|
|
171
|
+
console.log(" --name <name> Custom instance name");
|
|
172
|
+
console.log(" --remote <host> Remote host (local, gcp, aws, custom)");
|
|
173
|
+
console.log(" --daemon-only Start daemon only, skip gateway");
|
|
174
|
+
process.exit(0);
|
|
175
|
+
} else if (arg === "-d") {
|
|
159
176
|
detached = true;
|
|
160
177
|
} else if (arg === "--daemon-only") {
|
|
161
178
|
daemonOnly = true;
|
|
@@ -496,6 +513,8 @@ async function displayPairingQRCode(runtimeUrl: string, bearerToken: string | un
|
|
|
496
513
|
});
|
|
497
514
|
|
|
498
515
|
if (!registerRes.ok) {
|
|
516
|
+
const body = await registerRes.text().catch(() => "");
|
|
517
|
+
console.warn(`⚠ Could not register pairing request: ${registerRes.status} ${registerRes.statusText}${body ? ` — ${body}` : ""}. Run \`vellum pair\` to try again.\n`);
|
|
499
518
|
return;
|
|
500
519
|
}
|
|
501
520
|
|
|
@@ -519,8 +538,10 @@ async function displayPairingQRCode(runtimeUrl: string, bearerToken: string | un
|
|
|
519
538
|
console.log(qrString);
|
|
520
539
|
console.log("This pairing request expires in 5 minutes.");
|
|
521
540
|
console.log("Run `vellum pair` to generate a new one.\n");
|
|
522
|
-
} catch {
|
|
541
|
+
} catch (err) {
|
|
523
542
|
// Non-fatal — pairing is optional
|
|
543
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
544
|
+
console.warn(`⚠ Could not generate pairing QR code: ${reason}. Run \`vellum pair\` to try again.\n`);
|
|
524
545
|
}
|
|
525
546
|
}
|
|
526
547
|
|
|
@@ -540,6 +561,32 @@ async function hatchLocal(species: Species, name: string | null, daemonOnly: boo
|
|
|
540
561
|
console.log("🧹 Cleaning up stale local processes (no lock file entry)...\n");
|
|
541
562
|
await stopLocalProcesses();
|
|
542
563
|
}
|
|
564
|
+
|
|
565
|
+
// Verify required ports are available before starting any services.
|
|
566
|
+
// Only check when no local assistants exist — if there are existing local
|
|
567
|
+
// assistants, their daemon/gateway/qdrant legitimately own these ports.
|
|
568
|
+
const RUNTIME_HTTP_PORT = Number(process.env.RUNTIME_HTTP_PORT) || 7821;
|
|
569
|
+
const QDRANT_PORT = 6333;
|
|
570
|
+
const requiredPorts = [
|
|
571
|
+
{ name: "daemon", port: RUNTIME_HTTP_PORT },
|
|
572
|
+
{ name: "gateway", port: GATEWAY_PORT },
|
|
573
|
+
{ name: "qdrant", port: QDRANT_PORT },
|
|
574
|
+
];
|
|
575
|
+
const conflicts: string[] = [];
|
|
576
|
+
await Promise.all(
|
|
577
|
+
requiredPorts.map(async ({ name, port }) => {
|
|
578
|
+
if (await probePort(port)) {
|
|
579
|
+
conflicts.push(` - Port ${port} (${name}) is already in use`);
|
|
580
|
+
}
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
if (conflicts.length > 0) {
|
|
584
|
+
throw new Error(
|
|
585
|
+
`Cannot hatch — required ports are already in use:\n${conflicts.join("\n")}\n\n` +
|
|
586
|
+
"Stop the conflicting processes or use environment variables to configure alternative ports " +
|
|
587
|
+
"(RUNTIME_HTTP_PORT, GATEWAY_PORT).",
|
|
588
|
+
);
|
|
589
|
+
}
|
|
543
590
|
}
|
|
544
591
|
|
|
545
592
|
const baseDataDir = join(process.env.BASE_DATA_DIR?.trim() || (process.env.HOME ?? userInfo().homedir), ".vellum");
|
package/src/commands/login.ts
CHANGED
|
@@ -7,6 +7,17 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export async function login(): Promise<void> {
|
|
9
9
|
const args = process.argv.slice(3);
|
|
10
|
+
|
|
11
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
12
|
+
console.log("Usage: vellum login --token <session-token>");
|
|
13
|
+
console.log("");
|
|
14
|
+
console.log("Log in to the Vellum platform.");
|
|
15
|
+
console.log("");
|
|
16
|
+
console.log("Options:");
|
|
17
|
+
console.log(" --token <token> Session token from the Vellum platform");
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
let token: string | null = null;
|
|
11
22
|
|
|
12
23
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -43,11 +54,27 @@ export async function login(): Promise<void> {
|
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
export async function logout(): Promise<void> {
|
|
57
|
+
const args = process.argv.slice(3);
|
|
58
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
59
|
+
console.log("Usage: vellum logout");
|
|
60
|
+
console.log("");
|
|
61
|
+
console.log("Log out of the Vellum platform and remove the stored session token.");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
46
65
|
clearPlatformToken();
|
|
47
66
|
console.log("Logged out. Platform token removed.");
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
export async function whoami(): Promise<void> {
|
|
70
|
+
const args = process.argv.slice(3);
|
|
71
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
72
|
+
console.log("Usage: vellum whoami");
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log("Show the currently logged-in Vellum platform user.");
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
const token = readPlatformToken();
|
|
52
79
|
if (!token) {
|
|
53
80
|
console.error("Not logged in. Run `vellum login --token <token>` first.");
|
package/src/commands/pair.ts
CHANGED
|
@@ -72,6 +72,14 @@ async function pollForApproval(
|
|
|
72
72
|
|
|
73
73
|
export async function pair(): Promise<void> {
|
|
74
74
|
const args = process.argv.slice(3);
|
|
75
|
+
|
|
76
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
77
|
+
console.log("Usage: vellum pair <path-to-qrcode.png>");
|
|
78
|
+
console.log("");
|
|
79
|
+
console.log("Pair with a remote assistant by scanning the QR code PNG generated during setup.");
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
const qrCodePath = args[0] || process.env.VELLUM_CUSTOM_QR_CODE_PATH;
|
|
76
84
|
|
|
77
85
|
if (!qrCodePath) {
|
package/src/commands/ps.ts
CHANGED
|
@@ -409,6 +409,17 @@ async function listAllAssistants(): Promise<void> {
|
|
|
409
409
|
// ── Entry point ─────────────────────────────────────────────────
|
|
410
410
|
|
|
411
411
|
export async function ps(): Promise<void> {
|
|
412
|
+
const args = process.argv.slice(3);
|
|
413
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
414
|
+
console.log("Usage: vellum ps [<name>]");
|
|
415
|
+
console.log("");
|
|
416
|
+
console.log("List all assistants, or show processes for a specific assistant.");
|
|
417
|
+
console.log("");
|
|
418
|
+
console.log("Arguments:");
|
|
419
|
+
console.log(" <name> Show processes for the named assistant");
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
422
|
+
|
|
412
423
|
const assistantId = process.argv[3];
|
|
413
424
|
|
|
414
425
|
if (!assistantId) {
|
package/src/commands/recover.ts
CHANGED
|
@@ -9,6 +9,17 @@ import { getArchivePath, getMetadataPath } from "../lib/retire-archive";
|
|
|
9
9
|
import { exec } from "../lib/step-runner";
|
|
10
10
|
|
|
11
11
|
export async function recover(): Promise<void> {
|
|
12
|
+
const args = process.argv.slice(3);
|
|
13
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
14
|
+
console.log("Usage: vellum recover <name>");
|
|
15
|
+
console.log("");
|
|
16
|
+
console.log("Restore a previously retired local assistant from its archive.");
|
|
17
|
+
console.log("");
|
|
18
|
+
console.log("Arguments:");
|
|
19
|
+
console.log(" <name> Name of the retired assistant to recover");
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
const name = process.argv[3];
|
|
13
24
|
if (!name) {
|
|
14
25
|
console.error("Usage: vellum recover <name>");
|
package/src/commands/retire.ts
CHANGED
|
@@ -121,6 +121,20 @@ function parseSource(): string | undefined {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export async function retire(): Promise<void> {
|
|
124
|
+
const args = process.argv.slice(3);
|
|
125
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
126
|
+
console.log("Usage: vellum retire <name> [--source <source>]");
|
|
127
|
+
console.log("");
|
|
128
|
+
console.log("Delete an assistant instance and archive its data.");
|
|
129
|
+
console.log("");
|
|
130
|
+
console.log("Arguments:");
|
|
131
|
+
console.log(" <name> Name of the assistant to retire");
|
|
132
|
+
console.log("");
|
|
133
|
+
console.log("Options:");
|
|
134
|
+
console.log(" --source <source> Source identifier for the retirement");
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
const name = process.argv[3];
|
|
125
139
|
|
|
126
140
|
if (!name) {
|
package/src/commands/sleep.ts
CHANGED
|
@@ -5,6 +5,14 @@ import { loadAllAssistants } from "../lib/assistant-config";
|
|
|
5
5
|
import { stopProcessByPidFile } from "../lib/process";
|
|
6
6
|
|
|
7
7
|
export async function sleep(): Promise<void> {
|
|
8
|
+
const args = process.argv.slice(3);
|
|
9
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
10
|
+
console.log("Usage: vellum sleep");
|
|
11
|
+
console.log("");
|
|
12
|
+
console.log("Stop the daemon and gateway processes.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
const assistants = loadAllAssistants();
|
|
9
17
|
const hasLocal = assistants.some((a) => a.cloud === "local");
|
|
10
18
|
if (!hasLocal) {
|
package/src/commands/ssh.ts
CHANGED
|
@@ -33,6 +33,17 @@ function extractHostFromUrl(url: string): string {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export async function ssh(): Promise<void> {
|
|
36
|
+
const args = process.argv.slice(3);
|
|
37
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
38
|
+
console.log("Usage: vellum ssh [<name>]");
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log("SSH into a remote assistant instance.");
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log("Arguments:");
|
|
43
|
+
console.log(" <name> Name of the assistant to connect to (defaults to latest)");
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
const name = process.argv[3];
|
|
37
48
|
const entry = name ? findAssistantByName(name) : loadLatestAssistant();
|
|
38
49
|
|
package/src/commands/wake.ts
CHANGED
|
@@ -7,6 +7,14 @@ import { isProcessAlive } from "../lib/process";
|
|
|
7
7
|
import { startLocalDaemon, startGateway } from "../lib/local";
|
|
8
8
|
|
|
9
9
|
export async function wake(): Promise<void> {
|
|
10
|
+
const args = process.argv.slice(3);
|
|
11
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
12
|
+
console.log("Usage: vellum wake");
|
|
13
|
+
console.log("");
|
|
14
|
+
console.log("Start the daemon and gateway processes.");
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
const assistants = loadAllAssistants();
|
|
11
19
|
const hasLocal = assistants.some((a) => a.cloud === "local");
|
|
12
20
|
if (!hasLocal) {
|
package/src/index.ts
CHANGED
|
@@ -31,8 +31,8 @@ const commands = {
|
|
|
31
31
|
email,
|
|
32
32
|
hatch,
|
|
33
33
|
login,
|
|
34
|
-
pair,
|
|
35
34
|
logout,
|
|
35
|
+
pair,
|
|
36
36
|
ps,
|
|
37
37
|
recover,
|
|
38
38
|
retire,
|
|
@@ -90,9 +90,9 @@ async function main() {
|
|
|
90
90
|
console.log(" contacts Manage the contact graph");
|
|
91
91
|
console.log(" email Email operations (status, create inbox)");
|
|
92
92
|
console.log(" hatch Create a new assistant instance");
|
|
93
|
-
console.log(" pair Pair with a remote assistant via QR code");
|
|
94
93
|
console.log(" login Log in to the Vellum platform");
|
|
95
94
|
console.log(" logout Log out of the Vellum platform");
|
|
95
|
+
console.log(" pair Pair with a remote assistant via QR code");
|
|
96
96
|
console.log(" ps List assistants (or processes for a specific assistant)");
|
|
97
97
|
console.log(" recover Restore a previously retired local assistant");
|
|
98
98
|
console.log(" retire Delete an assistant instance");
|