@vellumai/cli 0.1.11 → 0.1.13
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/bun.lock +7 -0
- package/package.json +9 -2
- package/src/commands/client.ts +110 -0
- package/src/commands/hatch.ts +74 -5
- package/src/components/DefaultMainScreen.tsx +1738 -49
- package/src/components/TextInput.tsx +115 -0
- package/src/index.ts +3 -0
- package/src/lib/constants.ts +1 -1
- package/src/lib/doctor-client.ts +127 -0
- package/src/lib/local.ts +2 -5
- package/src/lib/interfaces-seed.ts +0 -28
package/bun.lock
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"ink": "^6.7.0",
|
|
9
9
|
"react": "^19.2.4",
|
|
10
|
+
"react-devtools-core": "^6.1.2",
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
12
13
|
"@types/bun": "^1.2.4",
|
|
@@ -218,6 +219,8 @@
|
|
|
218
219
|
|
|
219
220
|
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
|
220
221
|
|
|
222
|
+
"react-devtools-core": ["react-devtools-core@6.1.5", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA=="],
|
|
223
|
+
|
|
221
224
|
"react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
|
|
222
225
|
|
|
223
226
|
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
|
@@ -230,6 +233,8 @@
|
|
|
230
233
|
|
|
231
234
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
|
232
235
|
|
|
236
|
+
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
|
|
237
|
+
|
|
233
238
|
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
|
234
239
|
|
|
235
240
|
"slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
|
|
@@ -280,6 +285,8 @@
|
|
|
280
285
|
|
|
281
286
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
282
287
|
|
|
288
|
+
"react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
|
289
|
+
|
|
283
290
|
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
|
284
291
|
|
|
285
292
|
"wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vellumai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "CLI tools for vellum-assistant",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts",
|
|
8
|
+
"./package.json": "./package.json",
|
|
9
|
+
"./src/components/DefaultMainScreen": "./src/components/DefaultMainScreen.tsx",
|
|
10
|
+
"./src/lib/constants": "./src/lib/constants.ts"
|
|
11
|
+
},
|
|
6
12
|
"bin": {
|
|
7
13
|
"vellum-cli": "./src/index.ts"
|
|
8
14
|
},
|
|
@@ -14,7 +20,8 @@
|
|
|
14
20
|
"license": "MIT",
|
|
15
21
|
"dependencies": {
|
|
16
22
|
"ink": "^6.7.0",
|
|
17
|
-
"react": "^19.2.4"
|
|
23
|
+
"react": "^19.2.4",
|
|
24
|
+
"react-devtools-core": "^6.1.2"
|
|
18
25
|
},
|
|
19
26
|
"devDependencies": {
|
|
20
27
|
"@types/bun": "^1.2.4",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ANSI, renderChatApp } from "../components/DefaultMainScreen";
|
|
2
|
+
import { findAssistantByName, loadLatestAssistant } from "../lib/assistant-config";
|
|
3
|
+
import { GATEWAY_PORT, type Species } from "../lib/constants";
|
|
4
|
+
|
|
5
|
+
const FALLBACK_RUNTIME_URL = `http://127.0.0.1:${GATEWAY_PORT}`;
|
|
6
|
+
const FALLBACK_ASSISTANT_ID = "default";
|
|
7
|
+
|
|
8
|
+
interface ParsedArgs {
|
|
9
|
+
runtimeUrl: string;
|
|
10
|
+
assistantId: string;
|
|
11
|
+
species: Species;
|
|
12
|
+
bearerToken?: string;
|
|
13
|
+
project?: string;
|
|
14
|
+
zone?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseArgs(): ParsedArgs {
|
|
18
|
+
const args = process.argv.slice(3);
|
|
19
|
+
|
|
20
|
+
let positionalName: string | undefined;
|
|
21
|
+
const flagArgs: string[] = [];
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
if (arg === "--help" || arg === "-h") {
|
|
25
|
+
printUsage();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
} else if (
|
|
28
|
+
(arg === "--url" || arg === "-u" || arg === "--assistant-id" || arg === "-a") &&
|
|
29
|
+
args[i + 1]
|
|
30
|
+
) {
|
|
31
|
+
flagArgs.push(arg, args[++i]);
|
|
32
|
+
} else if (!arg.startsWith("-") && positionalName === undefined) {
|
|
33
|
+
positionalName = arg;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const entry = positionalName ? findAssistantByName(positionalName) : loadLatestAssistant();
|
|
38
|
+
if (positionalName && !entry) {
|
|
39
|
+
console.error(`No assistant instance found with name '${positionalName}'.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let runtimeUrl = process.env.RUNTIME_URL || entry?.runtimeUrl || FALLBACK_RUNTIME_URL;
|
|
44
|
+
let assistantId = process.env.ASSISTANT_ID || entry?.assistantId || FALLBACK_ASSISTANT_ID;
|
|
45
|
+
const bearerToken = process.env.RUNTIME_PROXY_BEARER_TOKEN || entry?.bearerToken || undefined;
|
|
46
|
+
const species: Species = (entry?.species as Species) ?? "vellum";
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < flagArgs.length; i++) {
|
|
49
|
+
const flag = flagArgs[i];
|
|
50
|
+
if ((flag === "--url" || flag === "-u") && flagArgs[i + 1]) {
|
|
51
|
+
runtimeUrl = flagArgs[++i];
|
|
52
|
+
} else if ((flag === "--assistant-id" || flag === "-a") && flagArgs[i + 1]) {
|
|
53
|
+
assistantId = flagArgs[++i];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
runtimeUrl: runtimeUrl.replace(/\/+$/, ""),
|
|
59
|
+
assistantId,
|
|
60
|
+
species,
|
|
61
|
+
bearerToken,
|
|
62
|
+
project: entry?.project,
|
|
63
|
+
zone: entry?.zone,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function printUsage(): void {
|
|
68
|
+
console.log(`${ANSI.bold}vellum-cli client${ANSI.reset} - Connect to a hatched assistant
|
|
69
|
+
|
|
70
|
+
${ANSI.bold}USAGE:${ANSI.reset}
|
|
71
|
+
vellum-cli client [name] [options]
|
|
72
|
+
|
|
73
|
+
${ANSI.bold}ARGUMENTS:${ANSI.reset}
|
|
74
|
+
[name] Instance name (default: latest)
|
|
75
|
+
|
|
76
|
+
${ANSI.bold}OPTIONS:${ANSI.reset}
|
|
77
|
+
-u, --url <url> Runtime URL
|
|
78
|
+
-a, --assistant-id <id> Assistant ID
|
|
79
|
+
-h, --help Show this help message
|
|
80
|
+
|
|
81
|
+
${ANSI.bold}DEFAULTS:${ANSI.reset}
|
|
82
|
+
Reads from ~/.vellum.lock.json (created by vellum-cli hatch).
|
|
83
|
+
Override with flags above or env vars RUNTIME_URL / ASSISTANT_ID.
|
|
84
|
+
|
|
85
|
+
${ANSI.bold}EXAMPLES:${ANSI.reset}
|
|
86
|
+
vellum-cli client
|
|
87
|
+
vellum-cli client vellum-assistant-foo
|
|
88
|
+
vellum-cli client --url http://34.56.78.90:${GATEWAY_PORT}
|
|
89
|
+
vellum-cli client vellum-assistant-foo --url http://localhost:${GATEWAY_PORT}
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function client(): Promise<void> {
|
|
94
|
+
const { runtimeUrl, assistantId, species, bearerToken, project, zone } = parseArgs();
|
|
95
|
+
|
|
96
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
97
|
+
|
|
98
|
+
const app = renderChatApp(
|
|
99
|
+
runtimeUrl,
|
|
100
|
+
assistantId,
|
|
101
|
+
species,
|
|
102
|
+
() => {
|
|
103
|
+
app.unmount();
|
|
104
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
105
|
+
console.log(`${ANSI.dim}Disconnected.${ANSI.reset}`);
|
|
106
|
+
process.exit(0);
|
|
107
|
+
},
|
|
108
|
+
{ bearerToken, project, zone },
|
|
109
|
+
);
|
|
110
|
+
}
|
package/src/commands/hatch.ts
CHANGED
|
@@ -17,7 +17,6 @@ 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 { buildInterfacesSeed } from "../lib/interfaces-seed";
|
|
21
20
|
import { startLocalDaemon, startGateway } from "../lib/local";
|
|
22
21
|
import { generateRandomSuffix } from "../lib/random-name";
|
|
23
22
|
import { exec } from "../lib/step-runner";
|
|
@@ -42,6 +41,12 @@ const DEFAULT_SPECIES: Species = "vellum";
|
|
|
42
41
|
|
|
43
42
|
const SPINNER_FRAMES= ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
44
43
|
|
|
44
|
+
const IS_DESKTOP = !!process.env.VELLUM_DESKTOP_APP;
|
|
45
|
+
|
|
46
|
+
function desktopLog(msg: string): void {
|
|
47
|
+
process.stdout.write(msg + "\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
function buildTimestampRedirect(): string {
|
|
46
51
|
return `exec > >(while IFS= read -r line; do printf '[%s] %s\\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$line"; done > /var/log/startup-script.log) 2>&1`;
|
|
47
52
|
}
|
|
@@ -88,8 +93,6 @@ export async function buildStartupScript(
|
|
|
88
93
|
);
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
const interfacesSeed = await buildInterfacesSeed();
|
|
92
|
-
|
|
93
96
|
return `#!/bin/bash
|
|
94
97
|
set -e
|
|
95
98
|
|
|
@@ -102,13 +105,11 @@ GATEWAY_RUNTIME_PROXY_ENABLED=true
|
|
|
102
105
|
RUNTIME_PROXY_BEARER_TOKEN=${bearerToken}
|
|
103
106
|
VELLUM_ASSISTANT_NAME=${instanceName}
|
|
104
107
|
VELLUM_CLOUD=${cloud}
|
|
105
|
-
${interfacesSeed}
|
|
106
108
|
mkdir -p "\$HOME/.vellum"
|
|
107
109
|
cat > "\$HOME/.vellum/.env" << DOTENV_EOF
|
|
108
110
|
ANTHROPIC_API_KEY=\$ANTHROPIC_API_KEY
|
|
109
111
|
GATEWAY_RUNTIME_PROXY_ENABLED=\$GATEWAY_RUNTIME_PROXY_ENABLED
|
|
110
112
|
RUNTIME_PROXY_BEARER_TOKEN=\$RUNTIME_PROXY_BEARER_TOKEN
|
|
111
|
-
INTERFACES_SEED_DIR=\$INTERFACES_SEED_DIR
|
|
112
113
|
RUNTIME_HTTP_PORT=7821
|
|
113
114
|
VELLUM_CLOUD=\$VELLUM_CLOUD
|
|
114
115
|
DOTENV_EOF
|
|
@@ -215,6 +216,10 @@ export async function watchHatching(
|
|
|
215
216
|
startTime: number,
|
|
216
217
|
species: Species,
|
|
217
218
|
): Promise<WatchHatchingResult> {
|
|
219
|
+
if (IS_DESKTOP) {
|
|
220
|
+
return watchHatchingDesktop(pollFn, instanceName, startTime, species);
|
|
221
|
+
}
|
|
222
|
+
|
|
218
223
|
let spinnerIdx = 0;
|
|
219
224
|
let lastLogLine: string | null = null;
|
|
220
225
|
let linesDrawn = 0;
|
|
@@ -321,6 +326,70 @@ export async function watchHatching(
|
|
|
321
326
|
});
|
|
322
327
|
}
|
|
323
328
|
|
|
329
|
+
function watchHatchingDesktop(
|
|
330
|
+
pollFn: () => Promise<PollResult>,
|
|
331
|
+
instanceName: string,
|
|
332
|
+
startTime: number,
|
|
333
|
+
species: Species,
|
|
334
|
+
): Promise<WatchHatchingResult> {
|
|
335
|
+
return new Promise<WatchHatchingResult>((resolve) => {
|
|
336
|
+
let prevLogLine: string | null = null;
|
|
337
|
+
let lastErrorContent = "";
|
|
338
|
+
let pollInFlight = false;
|
|
339
|
+
let nextPollAt = Date.now() + 15000;
|
|
340
|
+
|
|
341
|
+
desktopLog("Waiting for instance to start...");
|
|
342
|
+
|
|
343
|
+
const interval = setInterval(async () => {
|
|
344
|
+
const elapsed = Date.now() - startTime;
|
|
345
|
+
|
|
346
|
+
if (elapsed >= HATCH_TIMEOUT_MS[species]) {
|
|
347
|
+
clearInterval(interval);
|
|
348
|
+
desktopLog(`Timed out after ${formatElapsed(elapsed)}. Instance is still running.`);
|
|
349
|
+
desktopLog(`Monitor with: vel logs ${instanceName}`);
|
|
350
|
+
resolve({ success: true, errorContent: lastErrorContent });
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (Date.now() < nextPollAt || pollInFlight) return;
|
|
355
|
+
|
|
356
|
+
pollInFlight = true;
|
|
357
|
+
try {
|
|
358
|
+
const result = await pollFn();
|
|
359
|
+
|
|
360
|
+
if (result.lastLine && result.lastLine !== prevLogLine) {
|
|
361
|
+
prevLogLine = result.lastLine;
|
|
362
|
+
desktopLog(result.lastLine);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (result.errorContent) {
|
|
366
|
+
lastErrorContent = result.errorContent;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (result.done) {
|
|
370
|
+
clearInterval(interval);
|
|
371
|
+
if (result.failed) {
|
|
372
|
+
desktopLog("Startup script failed");
|
|
373
|
+
} else {
|
|
374
|
+
desktopLog("Your assistant has hatched!");
|
|
375
|
+
}
|
|
376
|
+
resolve({ success: !result.failed, errorContent: lastErrorContent });
|
|
377
|
+
}
|
|
378
|
+
} finally {
|
|
379
|
+
pollInFlight = false;
|
|
380
|
+
nextPollAt = Date.now() + 5000;
|
|
381
|
+
}
|
|
382
|
+
}, 5000);
|
|
383
|
+
|
|
384
|
+
process.on("SIGINT", () => {
|
|
385
|
+
clearInterval(interval);
|
|
386
|
+
desktopLog("Detaching. Instance is still running.");
|
|
387
|
+
desktopLog(`Monitor with: vel logs ${instanceName}`);
|
|
388
|
+
process.exit(0);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
324
393
|
function buildSshArgs(host: string): string[] {
|
|
325
394
|
return [
|
|
326
395
|
host,
|