@vellumai/cli 0.4.49 → 0.4.51

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
@@ -20,6 +20,7 @@
20
20
  "@types/qrcode-terminal": "^0.12.2",
21
21
  "@types/react": "^19.2.14",
22
22
  "eslint": "^10.0.0",
23
+ "knip": "^5.86.0",
23
24
  "prettier": "^3.8.1",
24
25
  "typescript": "^5.9.3",
25
26
  "typescript-eslint": "^8.55.0",
@@ -29,6 +30,12 @@
29
30
  "packages": {
30
31
  "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="],
31
32
 
33
+ "@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="],
34
+
35
+ "@emnapi/runtime": ["@emnapi/runtime@1.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw=="],
36
+
37
+ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
38
+
32
39
  "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
33
40
 
34
41
  "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
@@ -51,6 +58,56 @@
51
58
 
52
59
  "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
53
60
 
61
+ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
62
+
63
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
64
+
65
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
66
+
67
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
68
+
69
+ "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.19.1", "", { "os": "android", "cpu": "arm" }, "sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg=="],
70
+
71
+ "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.19.1", "", { "os": "android", "cpu": "arm64" }, "sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA=="],
72
+
73
+ "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.19.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ=="],
74
+
75
+ "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.19.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ=="],
76
+
77
+ "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.19.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw=="],
78
+
79
+ "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1", "", { "os": "linux", "cpu": "arm" }, "sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A=="],
80
+
81
+ "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.19.1", "", { "os": "linux", "cpu": "arm" }, "sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ=="],
82
+
83
+ "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.19.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig=="],
84
+
85
+ "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.19.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew=="],
86
+
87
+ "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.19.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ=="],
88
+
89
+ "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.19.1", "", { "os": "linux", "cpu": "none" }, "sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w=="],
90
+
91
+ "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.19.1", "", { "os": "linux", "cpu": "none" }, "sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw=="],
92
+
93
+ "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.19.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA=="],
94
+
95
+ "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.19.1", "", { "os": "linux", "cpu": "x64" }, "sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ=="],
96
+
97
+ "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.19.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw=="],
98
+
99
+ "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.19.1", "", { "os": "none", "cpu": "arm64" }, "sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA=="],
100
+
101
+ "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.19.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg=="],
102
+
103
+ "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.19.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ=="],
104
+
105
+ "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.19.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA=="],
106
+
107
+ "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.19.1", "", { "os": "win32", "cpu": "x64" }, "sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw=="],
108
+
109
+ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
110
+
54
111
  "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
55
112
 
56
113
  "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
@@ -107,6 +164,8 @@
107
164
 
108
165
  "brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
109
166
 
167
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
168
+
110
169
  "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
111
170
 
112
171
  "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
@@ -167,20 +226,30 @@
167
226
 
168
227
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
169
228
 
229
+ "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
230
+
170
231
  "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
171
232
 
172
233
  "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
173
234
 
235
+ "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
236
+
237
+ "fd-package-json": ["fd-package-json@2.0.0", "", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="],
238
+
174
239
  "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
175
240
 
176
241
  "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
177
242
 
243
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
244
+
178
245
  "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
179
246
 
180
247
  "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
181
248
 
182
249
  "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
183
250
 
251
+ "formatly": ["formatly@0.3.0", "", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="],
252
+
184
253
  "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
185
254
 
186
255
  "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
@@ -203,8 +272,12 @@
203
272
 
204
273
  "is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="],
205
274
 
275
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
276
+
206
277
  "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
207
278
 
279
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
280
+
208
281
  "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
209
282
 
210
283
  "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
@@ -215,14 +288,22 @@
215
288
 
216
289
  "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
217
290
 
291
+ "knip": ["knip@5.86.0", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.5.2", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4 <7" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-tGpRCbP+L+VysXnAp1bHTLQ0k/SdC3M3oX18+Cpiqax1qdS25iuCPzpK8LVmAKARZv0Ijri81Wq09Rzk0JTl+Q=="],
292
+
218
293
  "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
219
294
 
220
295
  "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
221
296
 
297
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
298
+
299
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
300
+
222
301
  "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
223
302
 
224
303
  "minimatch": ["minimatch@10.2.1", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A=="],
225
304
 
305
+ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
306
+
226
307
  "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
227
308
 
228
309
  "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
@@ -231,6 +312,8 @@
231
312
 
232
313
  "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
233
314
 
315
+ "oxc-resolver": ["oxc-resolver@11.19.1", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.19.1", "@oxc-resolver/binding-android-arm64": "11.19.1", "@oxc-resolver/binding-darwin-arm64": "11.19.1", "@oxc-resolver/binding-darwin-x64": "11.19.1", "@oxc-resolver/binding-freebsd-x64": "11.19.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.19.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.19.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.19.1", "@oxc-resolver/binding-linux-arm64-musl": "11.19.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.19.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-musl": "11.19.1", "@oxc-resolver/binding-openharmony-arm64": "11.19.1", "@oxc-resolver/binding-wasm32-wasi": "11.19.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.19.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.19.1", "@oxc-resolver/binding-win32-x64-msvc": "11.19.1" } }, "sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg=="],
316
+
234
317
  "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
235
318
 
236
319
  "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
@@ -243,6 +326,8 @@
243
326
 
244
327
  "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
245
328
 
329
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
330
+
246
331
  "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
247
332
 
248
333
  "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="],
@@ -257,6 +342,8 @@
257
342
 
258
343
  "qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="],
259
344
 
345
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
346
+
260
347
  "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
261
348
 
262
349
  "react-devtools-core": ["react-devtools-core@6.1.5", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA=="],
@@ -269,6 +356,10 @@
269
356
 
270
357
  "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
271
358
 
359
+ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
360
+
361
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
362
+
272
363
  "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
273
364
 
274
365
  "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
@@ -285,20 +376,28 @@
285
376
 
286
377
  "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
287
378
 
379
+ "smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="],
380
+
288
381
  "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
289
382
 
290
383
  "string-width": ["string-width@8.2.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
291
384
 
292
385
  "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
293
386
 
387
+ "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="],
388
+
294
389
  "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
295
390
 
296
391
  "terminal-size": ["terminal-size@4.0.1", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
297
392
 
298
393
  "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
299
394
 
395
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
396
+
300
397
  "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
301
398
 
399
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
400
+
302
401
  "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
303
402
 
304
403
  "type-fest": ["type-fest@5.4.4", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw=="],
@@ -307,10 +406,14 @@
307
406
 
308
407
  "typescript-eslint": ["typescript-eslint@8.56.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.0", "@typescript-eslint/parser": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/utils": "8.56.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg=="],
309
408
 
409
+ "unbash": ["unbash@2.2.0", "", {}, "sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w=="],
410
+
310
411
  "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
311
412
 
312
413
  "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
313
414
 
415
+ "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
416
+
314
417
  "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
315
418
 
316
419
  "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
@@ -325,6 +428,8 @@
325
428
 
326
429
  "y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
327
430
 
431
+ "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
432
+
328
433
  "yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
329
434
 
330
435
  "yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
@@ -333,6 +438,8 @@
333
438
 
334
439
  "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
335
440
 
441
+ "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
442
+
336
443
  "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
337
444
 
338
445
  "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
@@ -345,6 +452,10 @@
345
452
 
346
453
  "cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
347
454
 
455
+ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
456
+
457
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
458
+
348
459
  "qrcode/pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="],
349
460
 
350
461
  "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=="],
package/knip.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "entry": ["src/**/*.test.ts", "src/**/__tests__/**/*.ts"],
3
+ "project": ["src/**/*.ts", "src/**/*.tsx"],
4
+ "ignore": ["src/adapters/openclaw-http-server.ts"],
5
+ "ignoreDependencies": ["chalk"]
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/cli",
3
- "version": "0.4.49",
3
+ "version": "0.4.51",
4
4
  "description": "CLI tools for vellum-assistant",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,6 +17,7 @@
17
17
  "format": "prettier --write .",
18
18
  "format:check": "prettier --check .",
19
19
  "lint": "eslint",
20
+ "lint:unused": "knip --include files,dependencies,unlisted",
20
21
  "test": "bun test",
21
22
  "typecheck": "bunx tsc --noEmit"
22
23
  },
@@ -38,6 +39,7 @@
38
39
  "@types/qrcode-terminal": "^0.12.2",
39
40
  "@types/react": "^19.2.14",
40
41
  "eslint": "^10.0.0",
42
+ "knip": "^5.86.0",
41
43
  "prettier": "^3.8.1",
42
44
  "typescript": "^5.9.3",
43
45
  "typescript-eslint": "^8.55.0"
@@ -760,6 +760,10 @@ async function hatchLocal(
760
760
  console.log(` Species: ${species}`);
761
761
  console.log("");
762
762
 
763
+ if (!process.env.APP_VERSION) {
764
+ process.env.APP_VERSION = cliPkg.version;
765
+ }
766
+
763
767
  await startLocalDaemon(watch, resources);
764
768
 
765
769
  let runtimeUrl: string;
@@ -6,7 +6,7 @@ import {
6
6
  loadAllAssistants,
7
7
  type AssistantEntry,
8
8
  } from "../lib/assistant-config";
9
- import { checkHealth } from "../lib/health-check";
9
+ import { checkHealth, checkManagedHealth } from "../lib/health-check";
10
10
  import {
11
11
  classifyProcess,
12
12
  detectOrphanedProcesses,
@@ -359,6 +359,8 @@ async function listAllAssistants(): Promise<void> {
359
359
  } else {
360
360
  health = await checkHealth(a.localUrl ?? a.runtimeUrl, a.bearerToken);
361
361
  }
362
+ } else if (a.cloud === "vellum") {
363
+ health = await checkManagedHealth(a.runtimeUrl, a.assistantId);
362
364
  } else {
363
365
  health = await checkHealth(a.localUrl ?? a.runtimeUrl, a.bearerToken);
364
366
  }
@@ -500,6 +500,9 @@ async function handleScopeSelection(
500
500
 
501
501
  export const TYPING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
502
502
 
503
+ /** ASCII-safe spinner frames for the connection screen. */
504
+ const CONNECTION_SPINNER_FRAMES = ["|", "/", "-", "\\"];
505
+
503
506
  export interface ToolCallInfo {
504
507
  name: string;
505
508
  input: Record<string, unknown>;
@@ -675,6 +678,75 @@ function SpinnerDisplay({ text }: { text: string }): ReactElement {
675
678
  );
676
679
  }
677
680
 
681
+ type ConnectionState = "connecting" | "connected" | "error";
682
+
683
+ function ConnectionScreen({
684
+ state,
685
+ errorMessage,
686
+ species,
687
+ terminalRows,
688
+ terminalColumns,
689
+ onRetry,
690
+ onExit,
691
+ }: {
692
+ state: ConnectionState;
693
+ errorMessage?: string;
694
+ species: Species;
695
+ terminalRows: number;
696
+ terminalColumns: number;
697
+ onRetry: () => void;
698
+ onExit: () => void;
699
+ }): ReactElement {
700
+ const [frameIndex, setFrameIndex] = useState(0);
701
+
702
+ useEffect(() => {
703
+ if (state !== "connecting") return;
704
+ const timer = setInterval(() => {
705
+ setFrameIndex((prev) => (prev + 1) % CONNECTION_SPINNER_FRAMES.length);
706
+ }, 150);
707
+ return () => clearInterval(timer);
708
+ }, [state]);
709
+
710
+ useInput((input, key) => {
711
+ if (key.ctrl && input === "c") {
712
+ onExit();
713
+ }
714
+ if (state === "error" && input === "r") {
715
+ onRetry();
716
+ }
717
+ });
718
+
719
+ const config = SPECIES_CONFIG[species];
720
+ const title = `Vellum ${config.hatchedEmoji} ${species}`;
721
+ const width = Math.min(terminalColumns, MAX_TOTAL_WIDTH);
722
+
723
+ return (
724
+ <Box
725
+ flexDirection="column"
726
+ height={terminalRows}
727
+ width={width}
728
+ justifyContent="center"
729
+ alignItems="center"
730
+ >
731
+ <Text dimColor bold>
732
+ {title}
733
+ </Text>
734
+ <Text> </Text>
735
+ {state === "connecting" ? (
736
+ <Text dimColor>
737
+ {CONNECTION_SPINNER_FRAMES[frameIndex]} Connecting to assistant...
738
+ </Text>
739
+ ) : (
740
+ <>
741
+ <Text color="red">Failed to connect: {errorMessage}</Text>
742
+ <Text> </Text>
743
+ <Text dimColor>Press r to retry or Ctrl+C to quit</Text>
744
+ </>
745
+ )}
746
+ </Box>
747
+ );
748
+ }
749
+
678
750
  export function renderErrorMainScreen(error: unknown): number {
679
751
  const msg = error instanceof Error ? error.message : String(error);
680
752
  console.log(
@@ -1224,6 +1296,11 @@ function ChatApp({
1224
1296
  const [healthStatus, setHealthStatus] = useState<string | undefined>(
1225
1297
  undefined,
1226
1298
  );
1299
+ const [connectionState, setConnectionState] =
1300
+ useState<ConnectionState>("connecting");
1301
+ const [connectionError, setConnectionError] = useState<string | undefined>(
1302
+ undefined,
1303
+ );
1227
1304
  const prevFeedLengthRef = useRef(0);
1228
1305
  const busyRef = useRef(false);
1229
1306
  const connectedRef = useRef(false);
@@ -1240,7 +1317,7 @@ function ChatApp({
1240
1317
  const headerHeight = calculateHeaderHeight(species, terminalColumns);
1241
1318
 
1242
1319
  const isCompact = terminalColumns < COMPACT_THRESHOLD;
1243
- const compactInputAreaHeight = 2; // separator + input row only
1320
+ const compactInputAreaHeight = 1; // input row only, no separators
1244
1321
  const inputAreaHeight = isCompact
1245
1322
  ? compactInputAreaHeight
1246
1323
  : INPUT_AREA_HEIGHT;
@@ -1476,6 +1553,8 @@ function ChatApp({
1476
1553
  return false;
1477
1554
  }
1478
1555
  connectingRef.current = true;
1556
+ setConnectionState("connecting");
1557
+ setConnectionError(undefined);
1479
1558
  const h = handleRef_.current;
1480
1559
 
1481
1560
  h.showSpinner("Connecting...");
@@ -1538,12 +1617,15 @@ function ChatApp({
1538
1617
 
1539
1618
  connectedRef.current = true;
1540
1619
  connectingRef.current = false;
1620
+ setConnectionState("connected");
1541
1621
  return true;
1542
1622
  } catch (err) {
1543
1623
  h.hideSpinner();
1544
1624
  connectingRef.current = false;
1545
1625
  h.updateHealthStatus("unreachable");
1546
1626
  const msg = err instanceof Error ? err.message : String(err);
1627
+ setConnectionState("error");
1628
+ setConnectionError(msg);
1547
1629
  h.addStatus(
1548
1630
  `${statusEmoji("unreachable")} Failed to connect: ${msg}`,
1549
1631
  "red",
@@ -2065,6 +2147,7 @@ function ChatApp({
2065
2147
  role: "assistant",
2066
2148
  content: msg.content,
2067
2149
  });
2150
+ process.stdout.write("\x07");
2068
2151
  h.setBusy(false);
2069
2152
  h.hideSpinner();
2070
2153
  return;
@@ -2174,6 +2257,13 @@ function ChatApp({
2174
2257
  updateHealthStatus,
2175
2258
  ]);
2176
2259
 
2260
+ const retryConnection = useCallback(() => {
2261
+ if (connectingRef.current) return; // already retrying
2262
+ connectedRef.current = false;
2263
+ setConnectionState("connecting");
2264
+ ensureConnected();
2265
+ }, [ensureConnected]);
2266
+
2177
2267
  useEffect(() => {
2178
2268
  ensureConnected();
2179
2269
  }, [ensureConnected]);
@@ -2262,6 +2352,20 @@ function ChatApp({
2262
2352
  }
2263
2353
  }, [selection]);
2264
2354
 
2355
+ if (connectionState !== "connected") {
2356
+ return (
2357
+ <ConnectionScreen
2358
+ state={connectionState}
2359
+ errorMessage={connectionError}
2360
+ species={species}
2361
+ terminalRows={terminalRows}
2362
+ terminalColumns={terminalColumns}
2363
+ onRetry={retryConnection}
2364
+ onExit={onExit}
2365
+ />
2366
+ );
2367
+ }
2368
+
2265
2369
  return (
2266
2370
  <Box flexDirection="column" height={terminalRows}>
2267
2371
  <DefaultMainScreen
@@ -2274,8 +2378,9 @@ function ChatApp({
2274
2378
  <Box flexDirection="column" flexGrow={1} overflow="hidden">
2275
2379
  {visibleWindow.hiddenAbove > 0 ? (
2276
2380
  <Text dimColor>
2277
- {"\u2191"} {visibleWindow.hiddenAbove} more above
2278
- (Shift+\u2191/Cmd+\u2191)
2381
+ {isCompact
2382
+ ? `\u2191 ${visibleWindow.hiddenAbove} more above`
2383
+ : `\u2191 ${visibleWindow.hiddenAbove} more above (Shift+\u2191/Cmd+\u2191)`}
2279
2384
  </Text>
2280
2385
  ) : null}
2281
2386
 
@@ -2341,12 +2446,14 @@ function ChatApp({
2341
2446
 
2342
2447
  {!selection && !secretInput ? (
2343
2448
  <Box flexDirection="column" flexShrink={0}>
2344
- <Text dimColor>
2345
- {unicodeOrFallback("\u2500", "-").repeat(terminalColumns)}
2346
- </Text>
2347
- <Box paddingLeft={1} height={1} flexShrink={0}>
2449
+ {isCompact ? null : (
2450
+ <Text dimColor>
2451
+ {unicodeOrFallback("\u2500", "-").repeat(terminalColumns)}
2452
+ </Text>
2453
+ )}
2454
+ <Box paddingLeft={isCompact ? 0 : 1} height={1} flexShrink={0}>
2348
2455
  <Text color="green" bold>
2349
- you{">"}
2456
+ {isCompact ? ">" : "you>"}
2350
2457
  {" "}
2351
2458
  </Text>
2352
2459
  <TextInput
package/src/index.ts CHANGED
@@ -15,6 +15,13 @@ import { ssh } from "./commands/ssh";
15
15
  import { tunnel } from "./commands/tunnel";
16
16
  import { use } from "./commands/use";
17
17
  import { wake } from "./commands/wake";
18
+ import {
19
+ getActiveAssistant,
20
+ findAssistantByName,
21
+ loadLatestAssistant,
22
+ setActiveAssistant,
23
+ } from "./lib/assistant-config";
24
+ import { checkHealth } from "./lib/health-check";
18
25
 
19
26
  const commands = {
20
27
  clean,
@@ -37,37 +44,106 @@ const commands = {
37
44
 
38
45
  type CommandName = keyof typeof commands;
39
46
 
47
+ function printHelp(): void {
48
+ console.log("Usage: vellum <command> [options]");
49
+ console.log("");
50
+ console.log("Commands:");
51
+ console.log(" clean Kill orphaned vellum processes");
52
+ console.log(" client Connect to a hatched assistant");
53
+ console.log(" hatch Create a new assistant instance");
54
+ console.log(" login Log in to the Vellum platform");
55
+ console.log(" logout Log out of the Vellum platform");
56
+ console.log(" pair Pair with a remote assistant via QR code");
57
+ console.log(
58
+ " ps List assistants (or processes for a specific assistant)",
59
+ );
60
+ console.log(" recover Restore a previously retired local assistant");
61
+ console.log(" retire Delete an assistant instance");
62
+ console.log(" setup Configure API keys interactively");
63
+ console.log(" sleep Stop the assistant process");
64
+ console.log(" ssh SSH into a remote assistant instance");
65
+ console.log(" tunnel Create a tunnel for a locally hosted assistant");
66
+ console.log(" use Set the active assistant for commands");
67
+ console.log(" wake Start the assistant and gateway");
68
+ console.log(" whoami Show current logged-in user");
69
+ console.log("");
70
+ console.log("Options:");
71
+ console.log(
72
+ " --no-color, --plain Disable colored output (honors NO_COLOR env)",
73
+ );
74
+ console.log(" --version, -v Show version");
75
+ console.log(" --help, -h Show this help");
76
+ }
77
+
78
+ /**
79
+ * Check for --no-color / --plain flags and set NO_COLOR env var
80
+ * before any terminal capability detection runs.
81
+ *
82
+ * Per https://no-color.org/, setting NO_COLOR to any non-empty value
83
+ * signals that color output should be suppressed.
84
+ */
85
+ function applyNoColorFlags(argv: string[]): void {
86
+ if (argv.includes("--no-color") || argv.includes("--plain")) {
87
+ process.env.NO_COLOR = "1";
88
+ }
89
+ }
90
+
91
+ /**
92
+ * If a running assistant is detected, launch the TUI client and return true.
93
+ * Otherwise return false so the caller can fall back to help text.
94
+ */
95
+ async function tryLaunchClient(): Promise<boolean> {
96
+ const activeName = getActiveAssistant();
97
+ const entry = activeName
98
+ ? findAssistantByName(activeName)
99
+ : loadLatestAssistant();
100
+
101
+ if (!entry) return false;
102
+
103
+ const url = entry.localUrl || entry.runtimeUrl;
104
+ if (!url) return false;
105
+
106
+ const result = await checkHealth(url, entry.bearerToken);
107
+ if (result.status !== "healthy") return false;
108
+
109
+ // Ensure the resolved assistant is active so client() can find it
110
+ // (client() independently reads the active assistant from config).
111
+ setActiveAssistant(String(entry.assistantId));
112
+
113
+ await client();
114
+ return true;
115
+ }
116
+
40
117
  async function main() {
41
118
  const args = process.argv.slice(2);
42
- const commandName = args[0];
119
+
120
+ // Must run before any command or terminal-capabilities usage
121
+ applyNoColorFlags(args);
122
+
123
+ // Global flags that are not command names
124
+ const GLOBAL_FLAGS = new Set(["--no-color", "--plain"]);
125
+ const commandName = args.find((a) => !GLOBAL_FLAGS.has(a));
126
+
127
+ // Strip global flags from process.argv so subcommands that parse
128
+ // process.argv.slice(3) don't see them as positional arguments.
129
+ const filteredArgs = args.filter((a) => !GLOBAL_FLAGS.has(a));
130
+ process.argv = [...process.argv.slice(0, 2), ...filteredArgs];
43
131
 
44
132
  if (commandName === "--version" || commandName === "-v") {
45
133
  console.log(`@vellumai/cli v${cliPkg.version}`);
46
134
  process.exit(0);
47
135
  }
48
136
 
49
- if (!commandName || commandName === "--help" || commandName === "-h") {
50
- console.log("Usage: vellum <command> [options]");
51
- console.log("");
52
- console.log("Commands:");
53
- console.log(" clean Kill orphaned vellum processes");
54
- console.log(" client Connect to a hatched assistant");
55
- console.log(" hatch Create a new assistant instance");
56
- console.log(" login Log in to the Vellum platform");
57
- console.log(" logout Log out of the Vellum platform");
58
- console.log(" pair Pair with a remote assistant via QR code");
59
- console.log(
60
- " ps List assistants (or processes for a specific assistant)",
61
- );
62
- console.log(" recover Restore a previously retired local assistant");
63
- console.log(" retire Delete an assistant instance");
64
- console.log(" setup Configure API keys interactively");
65
- console.log(" sleep Stop the assistant process");
66
- console.log(" ssh SSH into a remote assistant instance");
67
- console.log(" tunnel Create a tunnel for a locally hosted assistant");
68
- console.log(" use Set the active assistant for commands");
69
- console.log(" wake Start the assistant and gateway");
70
- console.log(" whoami Show current logged-in user");
137
+ if (commandName === "--help" || commandName === "-h") {
138
+ printHelp();
139
+ process.exit(0);
140
+ }
141
+
142
+ if (!commandName) {
143
+ const launched = await tryLaunchClient();
144
+ if (!launched) {
145
+ printHelp();
146
+ }
71
147
  process.exit(0);
72
148
  }
73
149
 
package/src/lib/docker.ts CHANGED
@@ -7,7 +7,6 @@ import { saveAssistantEntry, setActiveAssistant } from "./assistant-config";
7
7
  import type { AssistantEntry } from "./assistant-config";
8
8
  import { DEFAULT_GATEWAY_PORT } from "./constants";
9
9
  import type { Species } from "./constants";
10
- import { discoverPublicUrl } from "./local";
11
10
  import { generateRandomSuffix } from "./random-name";
12
11
  import { exec, execOutput } from "./step-runner";
13
12
  import {
@@ -34,6 +33,36 @@ async function ensureDockerInstalled(): Promise<void> {
34
33
  }
35
34
 
36
35
  if (!installed) {
36
+ // Check whether Homebrew is available before attempting to use it.
37
+ let hasBrew = false;
38
+ try {
39
+ await execOutput("brew", ["--version"]);
40
+ hasBrew = true;
41
+ } catch {
42
+ // brew not found
43
+ }
44
+
45
+ if (!hasBrew) {
46
+ console.log("🍺 Homebrew not found. Installing Homebrew...");
47
+ try {
48
+ await exec("bash", [
49
+ "-c",
50
+ 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
51
+ ]);
52
+ } catch (err) {
53
+ const message = err instanceof Error ? err.message : String(err);
54
+ throw new Error(
55
+ `Failed to install Homebrew. Please install Docker manually from https://www.docker.com/products/docker-desktop/\n${message}`,
56
+ );
57
+ }
58
+
59
+ // Homebrew on Apple Silicon installs to /opt/homebrew; add it to PATH
60
+ // so subsequent brew/colima/docker invocations work in this session.
61
+ if (!process.env.PATH?.includes("/opt/homebrew")) {
62
+ process.env.PATH = `/opt/homebrew/bin:/opt/homebrew/sbin:${process.env.PATH}`;
63
+ }
64
+ }
65
+
37
66
  console.log("🐳 Docker not found. Installing via Homebrew...");
38
67
  try {
39
68
  await exec("brew", ["install", "colima", "docker"]);
@@ -58,6 +87,21 @@ async function ensureDockerInstalled(): Promise<void> {
58
87
  try {
59
88
  await exec("docker", ["info"]);
60
89
  } catch {
90
+ let hasColima = false;
91
+ try {
92
+ await execOutput("colima", ["version"]);
93
+ hasColima = true;
94
+ } catch {
95
+ // colima not found
96
+ }
97
+
98
+ if (!hasColima) {
99
+ throw new Error(
100
+ "Docker daemon is not running and Colima is not installed.\n" +
101
+ "Please start Docker Desktop, or install Colima with 'brew install colima' and run 'colima start'.",
102
+ );
103
+ }
104
+
61
105
  console.log("🚀 Docker daemon not running. Starting Colima...");
62
106
  try {
63
107
  await exec("colima", ["start"]);
@@ -326,8 +370,10 @@ export async function hatchDocker(
326
370
  );
327
371
  }
328
372
 
329
- const publicUrl = await discoverPublicUrl(gatewayPort);
330
- const runtimeUrl = publicUrl || `http://localhost:${gatewayPort}`;
373
+ // Docker containers bind to 0.0.0.0 so localhost always works. Skip
374
+ // mDNS/LAN discovery the .local hostname often fails to resolve on the
375
+ // host machine itself (mDNS is designed for cross-device discovery).
376
+ const runtimeUrl = `http://localhost:${gatewayPort}`;
331
377
  const dockerEntry: AssistantEntry = {
332
378
  assistantId: instanceName,
333
379
  runtimeUrl,
@@ -10,6 +10,93 @@ export interface HealthCheckResult {
10
10
  detail: string | null;
11
11
  }
12
12
 
13
+ interface OrgListResponse {
14
+ results: { id: string }[];
15
+ }
16
+
17
+ async function fetchOrganizationId(
18
+ platformUrl: string,
19
+ token: string,
20
+ ): Promise<{ orgId: string } | { error: string }> {
21
+ try {
22
+ const response = await fetch(`${platformUrl}/v1/organizations/`, {
23
+ headers: { "X-Session-Token": token },
24
+ });
25
+ if (!response.ok) {
26
+ return { error: `org lookup failed (${response.status})` };
27
+ }
28
+ const body = (await response.json()) as OrgListResponse;
29
+ const orgId = body.results?.[0]?.id;
30
+ if (!orgId) {
31
+ return { error: "no organization found" };
32
+ }
33
+ return { orgId };
34
+ } catch {
35
+ return { error: "org lookup unreachable" };
36
+ }
37
+ }
38
+
39
+ export async function checkManagedHealth(
40
+ runtimeUrl: string,
41
+ assistantId: string,
42
+ ): Promise<HealthCheckResult> {
43
+ const { readPlatformToken } = await import("./platform-client.js");
44
+ const token = readPlatformToken();
45
+ if (!token) {
46
+ return {
47
+ status: "error (auth)",
48
+ detail: "not logged in — run `vellum login`",
49
+ };
50
+ }
51
+
52
+ const orgResult = await fetchOrganizationId(runtimeUrl, token);
53
+ if ("error" in orgResult) {
54
+ return {
55
+ status: "error (auth)",
56
+ detail: orgResult.error,
57
+ };
58
+ }
59
+ const { orgId } = orgResult;
60
+
61
+ try {
62
+ const url = `${runtimeUrl}/v1/assistants/${encodeURIComponent(assistantId)}/healthz/`;
63
+ const controller = new AbortController();
64
+ const timeoutId = setTimeout(
65
+ () => controller.abort(),
66
+ HEALTH_CHECK_TIMEOUT_MS,
67
+ );
68
+
69
+ const headers: Record<string, string> = {
70
+ "X-Session-Token": token,
71
+ "Vellum-Organization-Id": orgId,
72
+ };
73
+
74
+ const response = await fetch(url, {
75
+ signal: controller.signal,
76
+ headers,
77
+ });
78
+
79
+ clearTimeout(timeoutId);
80
+
81
+ if (!response.ok) {
82
+ return { status: `error (${response.status})`, detail: null };
83
+ }
84
+
85
+ const data = (await response.json()) as HealthResponse;
86
+ const status = data.status || "unknown";
87
+ return {
88
+ status,
89
+ detail: status !== "healthy" ? (data.message ?? null) : null,
90
+ };
91
+ } catch (error) {
92
+ const status =
93
+ error instanceof Error && error.name === "AbortError"
94
+ ? "timeout"
95
+ : "unreachable";
96
+ return { status, detail: null };
97
+ }
98
+ }
99
+
13
100
  export async function checkHealth(
14
101
  runtimeUrl: string,
15
102
  bearerToken?: string,
@@ -1,23 +1,3 @@
1
- import { readFileSync } from "fs";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
-
5
- import { DEFAULT_DAEMON_PORT } from "./constants.js";
6
-
7
- /**
8
- * Resolve the HTTP port for the daemon runtime server.
9
- * Uses RUNTIME_HTTP_PORT env var, or the default (7821).
10
- */
11
- export function resolveDaemonPort(overridePort?: number): number {
12
- if (overridePort !== undefined) return overridePort;
13
- const envPort = process.env.RUNTIME_HTTP_PORT;
14
- if (envPort) {
15
- const parsed = parseInt(envPort, 10);
16
- if (!isNaN(parsed)) return parsed;
17
- }
18
- return DEFAULT_DAEMON_PORT;
19
- }
20
-
21
1
  /**
22
2
  * Build the base URL for the daemon HTTP server.
23
3
  */
@@ -25,23 +5,6 @@ export function buildDaemonUrl(port: number): string {
25
5
  return `http://127.0.0.1:${port}`;
26
6
  }
27
7
 
28
- /**
29
- * Read the HTTP bearer token from `<vellumDir>/http-token`.
30
- * Respects BASE_DATA_DIR for named instances.
31
- * Returns undefined if the token file doesn't exist or is empty.
32
- */
33
- export function readHttpToken(instanceDir?: string): string | undefined {
34
- const baseDataDir =
35
- instanceDir ?? (process.env.BASE_DATA_DIR?.trim() || homedir());
36
- const tokenPath = join(baseDataDir, ".vellum", "http-token");
37
- try {
38
- const token = readFileSync(tokenPath, "utf-8").trim();
39
- return token || undefined;
40
- } catch {
41
- return undefined;
42
- }
43
- }
44
-
45
8
  /**
46
9
  * Perform an HTTP health check against the daemon's `/healthz` endpoint.
47
10
  * Returns true if the daemon responds with HTTP 200, false otherwise.
@@ -82,32 +45,3 @@ export async function waitForDaemonReady(
82
45
  }
83
46
  return false;
84
47
  }
85
-
86
- /**
87
- * Make an authenticated HTTP request to the daemon.
88
- *
89
- * @param port - The daemon's HTTP port
90
- * @param path - The request path (e.g. `/v1/sessions`)
91
- * @param options - Fetch options (method, body, etc.)
92
- * @param bearerToken - The bearer token for authentication
93
- * @returns The fetch Response
94
- */
95
- export async function httpSend(
96
- port: number,
97
- path: string,
98
- options: RequestInit = {},
99
- bearerToken?: string,
100
- ): Promise<Response> {
101
- const url = `${buildDaemonUrl(port)}${path}`;
102
- const headers: Record<string, string> = {
103
- "Content-Type": "application/json",
104
- ...(options.headers as Record<string, string> | undefined),
105
- };
106
- if (bearerToken) {
107
- headers["Authorization"] = `Bearer ${bearerToken}`;
108
- }
109
- return fetch(url, {
110
- ...options,
111
- headers,
112
- });
113
- }
package/src/lib/local.ts CHANGED
@@ -528,46 +528,77 @@ export async function discoverPublicUrl(
528
528
  ): Promise<string | undefined> {
529
529
  const effectivePort = port ?? GATEWAY_PORT;
530
530
 
531
- // Discover local and cloud addresses in parallel so the cloud metadata
532
- // timeout (1s) doesn't block startup when a local address is immediately
533
- // available.
531
+ // Start cloud metadata lookup (may take up to 1s on non-cloud hosts).
534
532
  const cloudIpPromise = discoverCloudExternalIp();
535
533
 
536
- // Resolve local address synchronously (no I/O).
537
- const localUrl = discoverLocalUrl(effectivePort);
534
+ // Resolve local address synchronously (no I/O) — does not log.
535
+ const localResult = discoverLocalUrl(effectivePort);
536
+
537
+ // Race: if cloud IP resolves quickly, prefer it; otherwise return the
538
+ // local URL immediately instead of blocking on the full metadata timeout.
539
+ const cloudIp = await Promise.race([
540
+ cloudIpPromise,
541
+ // Give cloud metadata a short grace period (150ms) before falling back
542
+ // to the local address. This is enough for on-cloud hosts where the
543
+ // metadata endpoint responds in single-digit ms, but avoids the full
544
+ // 1s timeout on non-cloud machines.
545
+ new Promise<undefined>((resolve) =>
546
+ setTimeout(() => resolve(undefined), 150),
547
+ ),
548
+ ]);
538
549
 
539
- const cloudIp = await cloudIpPromise;
540
550
  if (cloudIp) {
541
551
  console.log(` Discovered external IP: ${cloudIp}`);
542
552
  return `http://${cloudIp}:${effectivePort}`;
543
553
  }
544
554
 
545
- return localUrl;
555
+ // Log the local address source only when we actually use it.
556
+ if (localResult.source === "hostname") {
557
+ console.log(` Discovered macOS local hostname: ${localResult.label}`);
558
+ } else if (localResult.source === "lan") {
559
+ console.log(` Discovered LAN IP: ${localResult.label}`);
560
+ }
561
+
562
+ return localResult.url;
546
563
  }
547
564
 
548
565
  /**
549
566
  * Resolve a LAN-reachable URL without any async I/O. Returns the best local
550
- * address or falls back to localhost.
567
+ * address or falls back to localhost. Does not emit any logs — the caller
568
+ * decides whether to log based on which result is actually used.
551
569
  */
552
- function discoverLocalUrl(effectivePort: number): string {
570
+ function discoverLocalUrl(effectivePort: number): {
571
+ url: string;
572
+ source: "hostname" | "lan" | "localhost";
573
+ label?: string;
574
+ } {
553
575
  // On macOS, prefer the .local hostname (Bonjour/mDNS) so other devices on
554
576
  // the same network can reach the gateway by name.
555
577
  if (platform() === "darwin") {
556
578
  const localHostname = getMacLocalHostname();
557
579
  if (localHostname) {
558
- console.log(` Discovered macOS local hostname: ${localHostname}`);
559
- return `http://${localHostname}:${effectivePort}`;
580
+ return {
581
+ url: `http://${localHostname}:${effectivePort}`,
582
+ source: "hostname",
583
+ label: localHostname,
584
+ };
560
585
  }
561
586
  }
562
587
 
563
588
  const lanIp = getLocalLanIPv4();
564
589
  if (lanIp) {
565
- console.log(` Discovered LAN IP: ${lanIp}`);
566
- return `http://${lanIp}:${effectivePort}`;
590
+ return {
591
+ url: `http://${lanIp}:${effectivePort}`,
592
+ source: "lan",
593
+ label: lanIp,
594
+ };
567
595
  }
568
596
 
569
597
  // Final fallback to localhost when no LAN address could be discovered.
570
- return `http://localhost:${effectivePort}`;
598
+ return {
599
+ url: `http://localhost:${effectivePort}`,
600
+ source: "localhost",
601
+ };
571
602
  }
572
603
 
573
604
  /**
@@ -692,18 +723,14 @@ export async function startLocalDaemon(
692
723
  watch: boolean = false,
693
724
  resources: LocalInstanceResources,
694
725
  ): Promise<void> {
695
- if (process.env.VELLUM_DESKTOP_APP && !watch) {
696
- // When running inside the desktop app, the CLI owns the daemon lifecycle.
697
- // Find the vellum-daemon binary adjacent to the CLI binary.
726
+ // Check for a compiled daemon binary adjacent to the CLI executable.
727
+ // This covers both the desktop app (VELLUM_DESKTOP_APP) and the case where
728
+ // the user runs the compiled CLI directly from the terminal (e.g. via a
729
+ // /usr/local/bin/vellum symlink into the app bundle).
730
+ const daemonBinary = join(dirname(process.execPath), "vellum-daemon");
731
+ if (existsSync(daemonBinary) && !watch) {
698
732
  // In watch mode, skip the bundled binary and use source (bun --watch
699
733
  // only works with source files, not compiled binaries).
700
- const daemonBinary = join(dirname(process.execPath), "vellum-daemon");
701
- if (!existsSync(daemonBinary)) {
702
- throw new Error(
703
- `vellum-daemon binary not found at ${daemonBinary}.\n` +
704
- " Ensure the daemon binary is bundled alongside the CLI in the app bundle.",
705
- );
706
- }
707
734
 
708
735
  const pidFile = resources.pidFile;
709
736
 
@@ -772,6 +799,7 @@ export async function startLocalDaemon(
772
799
  // Forward optional config env vars the daemon may need
773
800
  for (const key of [
774
801
  "ANTHROPIC_API_KEY",
802
+ "APP_VERSION",
775
803
  "BASE_DATA_DIR",
776
804
  "QDRANT_HTTP_PORT",
777
805
  "QDRANT_URL",
@@ -943,18 +971,11 @@ export async function startGateway(
943
971
 
944
972
  let gateway;
945
973
 
946
- if (process.env.VELLUM_DESKTOP_APP && !watch) {
947
- // Desktop app: spawn the compiled gateway binary directly (mirrors daemon pattern).
948
- // In watch mode, skip the bundled binary and use source (bun --watch
949
- // only works with source files, not compiled binaries).
950
- const gatewayBinary = join(dirname(process.execPath), "vellum-gateway");
951
- if (!existsSync(gatewayBinary)) {
952
- throw new Error(
953
- `vellum-gateway binary not found at ${gatewayBinary}.\n` +
954
- " Ensure the gateway binary is bundled alongside the CLI in the app bundle.",
955
- );
956
- }
957
-
974
+ const gatewayBinary = join(dirname(process.execPath), "vellum-gateway");
975
+ if (existsSync(gatewayBinary) && !watch) {
976
+ // Use the compiled gateway binary when available (desktop app or compiled
977
+ // CLI invoked from the terminal). In watch mode, skip the bundled binary
978
+ // and use source (bun --watch only works with source files).
958
979
  const gatewayLogFd = openLogFile("hatch.log");
959
980
  gateway = spawn(gatewayBinary, [], {
960
981
  detached: true,
@@ -107,13 +107,6 @@ export function getTerminalCapabilities(): TerminalCapabilities {
107
107
  return _cached;
108
108
  }
109
109
 
110
- // ── Convenience helpers ──────────────────────────────────────
111
-
112
- /** True when colors should be used (any level above "none"). */
113
- export function supportsColor(): boolean {
114
- return getTerminalCapabilities().colorLevel !== "none";
115
- }
116
-
117
110
  /**
118
111
  * Return `fancy` when unicode is supported, otherwise `fallback`.
119
112
  *