@vellumai/cli 0.1.11 → 0.1.12

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 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.11",
3
+ "version": "0.1.12",
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
+ }
@@ -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,