@vellumai/cli 0.3.14 → 0.3.16
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 +79 -0
- package/package.json +6 -1
- package/src/commands/contacts.ts +6 -5
- package/src/commands/hatch.ts +2 -150
- package/src/commands/pair.ts +163 -0
- package/src/index.ts +3 -0
package/bun.lock
CHANGED
|
@@ -6,12 +6,17 @@
|
|
|
6
6
|
"name": "@vellumai/cli",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"ink": "^6.7.0",
|
|
9
|
+
"jsqr": "^1.4.0",
|
|
10
|
+
"pngjs": "^7.0.0",
|
|
11
|
+
"qrcode": "^1.5.4",
|
|
9
12
|
"qrcode-terminal": "^0.12.0",
|
|
10
13
|
"react": "^19.2.4",
|
|
11
14
|
"react-devtools-core": "^6.1.2",
|
|
12
15
|
},
|
|
13
16
|
"devDependencies": {
|
|
14
17
|
"@types/bun": "^1.2.4",
|
|
18
|
+
"@types/pngjs": "^6.0.5",
|
|
19
|
+
"@types/qrcode": "^1.5.6",
|
|
15
20
|
"@types/qrcode-terminal": "^0.12.2",
|
|
16
21
|
"@types/react": "^19.2.14",
|
|
17
22
|
"eslint": "^10.0.0",
|
|
@@ -55,6 +60,10 @@
|
|
|
55
60
|
|
|
56
61
|
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
|
57
62
|
|
|
63
|
+
"@types/pngjs": ["@types/pngjs@6.0.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ=="],
|
|
64
|
+
|
|
65
|
+
"@types/qrcode": ["@types/qrcode@1.5.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="],
|
|
66
|
+
|
|
58
67
|
"@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="],
|
|
59
68
|
|
|
60
69
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
|
@@ -99,6 +108,8 @@
|
|
|
99
108
|
|
|
100
109
|
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
101
110
|
|
|
111
|
+
"camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
|
112
|
+
|
|
102
113
|
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
|
103
114
|
|
|
104
115
|
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
|
@@ -107,8 +118,14 @@
|
|
|
107
118
|
|
|
108
119
|
"cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="],
|
|
109
120
|
|
|
121
|
+
"cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
|
122
|
+
|
|
110
123
|
"code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
|
111
124
|
|
|
125
|
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
|
126
|
+
|
|
127
|
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
|
128
|
+
|
|
112
129
|
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
|
113
130
|
|
|
114
131
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
|
@@ -117,8 +134,12 @@
|
|
|
117
134
|
|
|
118
135
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
119
136
|
|
|
137
|
+
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
|
|
138
|
+
|
|
120
139
|
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
|
121
140
|
|
|
141
|
+
"dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
|
|
142
|
+
|
|
122
143
|
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
|
123
144
|
|
|
124
145
|
"environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
|
|
@@ -159,6 +180,8 @@
|
|
|
159
180
|
|
|
160
181
|
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
|
161
182
|
|
|
183
|
+
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
|
184
|
+
|
|
162
185
|
"get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
|
|
163
186
|
|
|
164
187
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
|
@@ -187,6 +210,8 @@
|
|
|
187
210
|
|
|
188
211
|
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
|
189
212
|
|
|
213
|
+
"jsqr": ["jsqr@1.4.0", "", {}, "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A=="],
|
|
214
|
+
|
|
190
215
|
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
|
191
216
|
|
|
192
217
|
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
|
@@ -209,6 +234,8 @@
|
|
|
209
234
|
|
|
210
235
|
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
|
211
236
|
|
|
237
|
+
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
|
|
238
|
+
|
|
212
239
|
"patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
|
213
240
|
|
|
214
241
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
|
@@ -217,10 +244,14 @@
|
|
|
217
244
|
|
|
218
245
|
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
|
219
246
|
|
|
247
|
+
"pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="],
|
|
248
|
+
|
|
220
249
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
|
221
250
|
|
|
222
251
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
|
223
252
|
|
|
253
|
+
"qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
|
|
254
|
+
|
|
224
255
|
"qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="],
|
|
225
256
|
|
|
226
257
|
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
|
@@ -229,12 +260,18 @@
|
|
|
229
260
|
|
|
230
261
|
"react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
|
|
231
262
|
|
|
263
|
+
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
|
264
|
+
|
|
265
|
+
"require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
|
|
266
|
+
|
|
232
267
|
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
|
233
268
|
|
|
234
269
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
|
235
270
|
|
|
236
271
|
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
|
237
272
|
|
|
273
|
+
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
|
|
274
|
+
|
|
238
275
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
|
239
276
|
|
|
240
277
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
|
@@ -273,6 +310,8 @@
|
|
|
273
310
|
|
|
274
311
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
|
275
312
|
|
|
313
|
+
"which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
|
|
314
|
+
|
|
276
315
|
"widest-line": ["widest-line@6.0.0", "", { "dependencies": { "string-width": "^8.1.0" } }, "sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA=="],
|
|
277
316
|
|
|
278
317
|
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
|
@@ -281,6 +320,12 @@
|
|
|
281
320
|
|
|
282
321
|
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
|
283
322
|
|
|
323
|
+
"y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
|
|
324
|
+
|
|
325
|
+
"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=="],
|
|
326
|
+
|
|
327
|
+
"yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
|
|
328
|
+
|
|
284
329
|
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
|
285
330
|
|
|
286
331
|
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
|
@@ -291,14 +336,48 @@
|
|
|
291
336
|
|
|
292
337
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
293
338
|
|
|
339
|
+
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
340
|
+
|
|
341
|
+
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
342
|
+
|
|
343
|
+
"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=="],
|
|
344
|
+
|
|
345
|
+
"qrcode/pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="],
|
|
346
|
+
|
|
294
347
|
"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=="],
|
|
295
348
|
|
|
296
349
|
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
|
297
350
|
|
|
298
351
|
"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=="],
|
|
299
352
|
|
|
353
|
+
"yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
|
354
|
+
|
|
355
|
+
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
356
|
+
|
|
300
357
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
|
301
358
|
|
|
359
|
+
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
360
|
+
|
|
361
|
+
"cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
|
362
|
+
|
|
363
|
+
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
364
|
+
|
|
365
|
+
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
366
|
+
|
|
367
|
+
"yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
|
368
|
+
|
|
369
|
+
"yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
370
|
+
|
|
371
|
+
"yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
|
372
|
+
|
|
373
|
+
"yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
374
|
+
|
|
302
375
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
|
376
|
+
|
|
377
|
+
"yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
|
378
|
+
|
|
379
|
+
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
380
|
+
|
|
381
|
+
"yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
|
303
382
|
}
|
|
304
383
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vellumai/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "CLI tools for vellum-assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -22,12 +22,17 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"ink": "^6.7.0",
|
|
25
|
+
"jsqr": "^1.4.0",
|
|
26
|
+
"pngjs": "^7.0.0",
|
|
27
|
+
"qrcode": "^1.5.4",
|
|
25
28
|
"qrcode-terminal": "^0.12.0",
|
|
26
29
|
"react": "^19.2.4",
|
|
27
30
|
"react-devtools-core": "^6.1.2"
|
|
28
31
|
},
|
|
29
32
|
"devDependencies": {
|
|
30
33
|
"@types/bun": "^1.2.4",
|
|
34
|
+
"@types/pngjs": "^6.0.5",
|
|
35
|
+
"@types/qrcode": "^1.5.6",
|
|
31
36
|
"@types/qrcode-terminal": "^0.12.2",
|
|
32
37
|
"@types/react": "^19.2.14",
|
|
33
38
|
"eslint": "^10.0.0",
|
package/src/commands/contacts.ts
CHANGED
|
@@ -3,15 +3,16 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { loadLatestAssistant } from "../lib/assistant-config";
|
|
6
|
+
import { GATEWAY_PORT } from "../lib/constants.js";
|
|
6
7
|
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
|
-
//
|
|
9
|
+
// Gateway API client
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
|
|
11
|
-
function
|
|
12
|
+
function getGatewayUrl(): string {
|
|
12
13
|
const entry = loadLatestAssistant();
|
|
13
14
|
if (entry?.runtimeUrl) return entry.runtimeUrl;
|
|
14
|
-
return
|
|
15
|
+
return `http://localhost:${GATEWAY_PORT}`;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function getBearerToken(): string | undefined {
|
|
@@ -45,7 +46,7 @@ function buildHeaders(): Record<string, string> {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
async function apiGet(path: string): Promise<unknown> {
|
|
48
|
-
const url = `${
|
|
49
|
+
const url = `${getGatewayUrl()}/v1/${path}`;
|
|
49
50
|
const response = await fetch(url, { headers: buildHeaders() });
|
|
50
51
|
if (!response.ok) {
|
|
51
52
|
const text = await response.text();
|
|
@@ -58,7 +59,7 @@ async function apiPost(
|
|
|
58
59
|
path: string,
|
|
59
60
|
body: unknown,
|
|
60
61
|
): Promise<unknown> {
|
|
61
|
-
const url = `${
|
|
62
|
+
const url = `${getGatewayUrl()}/v1/${path}`;
|
|
62
63
|
const response = await fetch(url, {
|
|
63
64
|
method: "POST",
|
|
64
65
|
headers: buildHeaders(),
|
package/src/commands/hatch.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { homedir, tmpdir, userInfo } from "os";
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, symlinkSync, unlinkSync } from "fs";
|
|
2
|
+
import { homedir, userInfo } from "os";
|
|
4
3
|
import { join } from "path";
|
|
5
4
|
|
|
6
5
|
// Direct import — bun embeds this at compile time so it works in compiled binaries.
|
|
@@ -11,7 +10,6 @@ import { loadAllAssistants, saveAssistantEntry } from "../lib/assistant-config";
|
|
|
11
10
|
import type { AssistantEntry } from "../lib/assistant-config";
|
|
12
11
|
import { hatchAws } from "../lib/aws";
|
|
13
12
|
import {
|
|
14
|
-
GATEWAY_PORT,
|
|
15
13
|
SPECIES_CONFIG,
|
|
16
14
|
VALID_REMOTE_HOSTS,
|
|
17
15
|
VALID_SPECIES,
|
|
@@ -23,21 +21,12 @@ import { startLocalDaemon, startGateway, stopLocalProcesses } from "../lib/local
|
|
|
23
21
|
import { isProcessAlive } from "../lib/process";
|
|
24
22
|
import { generateRandomSuffix } from "../lib/random-name";
|
|
25
23
|
import { validateAssistantName } from "../lib/retire-archive";
|
|
26
|
-
import { exec } from "../lib/step-runner";
|
|
27
24
|
|
|
28
25
|
export type { PollResult, WatchHatchingResult } from "../lib/gcp";
|
|
29
26
|
|
|
30
27
|
const INSTALL_SCRIPT_REMOTE_PATH = "/tmp/vellum-install.sh";
|
|
31
28
|
|
|
32
|
-
// Embedded install script — bun --compile doesn't bundle non-JS assets,
|
|
33
|
-
// so we inline it to ensure it's available in the compiled binary.
|
|
34
|
-
import INSTALL_SCRIPT_CONTENT from "../adapters/install.sh" with { type: "text" };
|
|
35
29
|
|
|
36
|
-
function resolveInstallScriptPath(): string {
|
|
37
|
-
const tmpPath = join(tmpdir(), `vellum-install-${process.pid}.sh`);
|
|
38
|
-
writeFileSync(tmpPath, INSTALL_SCRIPT_CONTENT, { mode: 0o755 });
|
|
39
|
-
return tmpPath;
|
|
40
|
-
}
|
|
41
30
|
const HATCH_TIMEOUT_MS: Record<Species, number> = {
|
|
42
31
|
vellum: 2 * 60 * 1000,
|
|
43
32
|
openclaw: 10 * 60 * 1000,
|
|
@@ -403,138 +392,6 @@ function watchHatchingDesktop(
|
|
|
403
392
|
});
|
|
404
393
|
}
|
|
405
394
|
|
|
406
|
-
function buildSshArgs(host: string): string[] {
|
|
407
|
-
const args: string[] = [host];
|
|
408
|
-
const keyPath = process.env.VELLUM_SSH_KEY_PATH;
|
|
409
|
-
if (keyPath) {
|
|
410
|
-
args.push("-i", keyPath);
|
|
411
|
-
}
|
|
412
|
-
args.push(
|
|
413
|
-
"-o", "StrictHostKeyChecking=no",
|
|
414
|
-
"-o", "UserKnownHostsFile=/dev/null",
|
|
415
|
-
"-o", "ConnectTimeout=10",
|
|
416
|
-
"-o", "LogLevel=ERROR",
|
|
417
|
-
);
|
|
418
|
-
return args;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function buildScpArgs(keyPath?: string): string[] {
|
|
422
|
-
const args: string[] = [];
|
|
423
|
-
if (keyPath) {
|
|
424
|
-
args.push("-i", keyPath);
|
|
425
|
-
}
|
|
426
|
-
args.push(
|
|
427
|
-
"-o", "StrictHostKeyChecking=no",
|
|
428
|
-
"-o", "UserKnownHostsFile=/dev/null",
|
|
429
|
-
"-o", "LogLevel=ERROR",
|
|
430
|
-
);
|
|
431
|
-
return args;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function extractHostname(host: string): string {
|
|
435
|
-
return host.includes("@") ? host.split("@")[1] : host;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
async function hatchCustom(
|
|
439
|
-
species: Species,
|
|
440
|
-
detached: boolean,
|
|
441
|
-
name: string | null,
|
|
442
|
-
): Promise<void> {
|
|
443
|
-
const host = process.env.VELLUM_CUSTOM_HOST;
|
|
444
|
-
if (!host) {
|
|
445
|
-
console.error("Error: VELLUM_CUSTOM_HOST environment variable is required when using --remote custom (e.g., user@hostname)");
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
const hostname = extractHostname(host);
|
|
451
|
-
const instanceName = name ?? `${species}-${generateRandomSuffix()}`;
|
|
452
|
-
|
|
453
|
-
console.log(`🥚 Creating new assistant: ${instanceName}`);
|
|
454
|
-
console.log(` Species: ${species}`);
|
|
455
|
-
console.log(` Cloud: Custom`);
|
|
456
|
-
console.log(` Host: ${host}`);
|
|
457
|
-
console.log("");
|
|
458
|
-
|
|
459
|
-
const sshUser = host.includes("@") ? host.split("@")[0] : userInfo().username;
|
|
460
|
-
const bearerToken = randomBytes(32).toString("hex");
|
|
461
|
-
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
462
|
-
if (!anthropicApiKey) {
|
|
463
|
-
console.error("Error: ANTHROPIC_API_KEY environment variable is not set.");
|
|
464
|
-
process.exit(1);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const startupScript = await buildStartupScript(
|
|
468
|
-
species,
|
|
469
|
-
bearerToken,
|
|
470
|
-
sshUser,
|
|
471
|
-
anthropicApiKey,
|
|
472
|
-
instanceName,
|
|
473
|
-
"custom",
|
|
474
|
-
);
|
|
475
|
-
const startupScriptPath = join(tmpdir(), `${instanceName}-startup.sh`);
|
|
476
|
-
writeFileSync(startupScriptPath, startupScript);
|
|
477
|
-
|
|
478
|
-
const sshKeyPath = process.env.VELLUM_SSH_KEY_PATH;
|
|
479
|
-
|
|
480
|
-
const installScriptPath = resolveInstallScriptPath();
|
|
481
|
-
|
|
482
|
-
try {
|
|
483
|
-
console.log("📋 Uploading install script to instance...");
|
|
484
|
-
await exec("scp", [
|
|
485
|
-
...buildScpArgs(sshKeyPath),
|
|
486
|
-
installScriptPath,
|
|
487
|
-
`${host}:${INSTALL_SCRIPT_REMOTE_PATH}`,
|
|
488
|
-
]);
|
|
489
|
-
|
|
490
|
-
console.log("📋 Uploading startup script to instance...");
|
|
491
|
-
const remoteStartupPath = `/tmp/${instanceName}-startup.sh`;
|
|
492
|
-
await exec("scp", [
|
|
493
|
-
...buildScpArgs(sshKeyPath),
|
|
494
|
-
startupScriptPath,
|
|
495
|
-
`${host}:${remoteStartupPath}`,
|
|
496
|
-
]);
|
|
497
|
-
|
|
498
|
-
console.log("🔨 Running startup script on instance...");
|
|
499
|
-
await exec("ssh", [
|
|
500
|
-
...buildSshArgs(host),
|
|
501
|
-
`chmod +x ${remoteStartupPath} ${INSTALL_SCRIPT_REMOTE_PATH} && bash ${remoteStartupPath}`,
|
|
502
|
-
]);
|
|
503
|
-
} finally {
|
|
504
|
-
try { unlinkSync(startupScriptPath); } catch {}
|
|
505
|
-
try { unlinkSync(installScriptPath); } catch {}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const runtimeUrl = `http://${hostname}:${GATEWAY_PORT}`;
|
|
509
|
-
const customEntry: AssistantEntry = {
|
|
510
|
-
assistantId: instanceName,
|
|
511
|
-
runtimeUrl,
|
|
512
|
-
bearerToken,
|
|
513
|
-
cloud: "custom",
|
|
514
|
-
species,
|
|
515
|
-
sshUser,
|
|
516
|
-
hatchedAt: new Date().toISOString(),
|
|
517
|
-
};
|
|
518
|
-
saveAssistantEntry(customEntry);
|
|
519
|
-
|
|
520
|
-
if (detached) {
|
|
521
|
-
console.log("");
|
|
522
|
-
console.log("✅ Assistant is hatching!\n");
|
|
523
|
-
} else {
|
|
524
|
-
console.log("");
|
|
525
|
-
console.log("✅ Assistant has been set up!");
|
|
526
|
-
}
|
|
527
|
-
console.log("Instance details:");
|
|
528
|
-
console.log(` Name: ${instanceName}`);
|
|
529
|
-
console.log(` Host: ${host}`);
|
|
530
|
-
console.log(` Runtime URL: ${runtimeUrl}`);
|
|
531
|
-
console.log("");
|
|
532
|
-
} catch (error) {
|
|
533
|
-
console.error("❌ Error:", error instanceof Error ? error.message : error);
|
|
534
|
-
process.exit(1);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
395
|
function installCLISymlink(): void {
|
|
539
396
|
const cliBinary = process.execPath;
|
|
540
397
|
if (!cliBinary || !existsSync(cliBinary)) return;
|
|
@@ -665,11 +522,6 @@ export async function hatch(): Promise<void> {
|
|
|
665
522
|
return;
|
|
666
523
|
}
|
|
667
524
|
|
|
668
|
-
if (remote === "custom") {
|
|
669
|
-
await hatchCustom(species, detached, name);
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
525
|
if (remote === "aws") {
|
|
674
526
|
await hatchAws(species, detached, name);
|
|
675
527
|
return;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import jsQR from "jsqr";
|
|
4
|
+
import { hostname, userInfo } from "os";
|
|
5
|
+
import { PNG } from "pngjs";
|
|
6
|
+
|
|
7
|
+
import { saveAssistantEntry } from "../lib/assistant-config";
|
|
8
|
+
import type { AssistantEntry } from "../lib/assistant-config";
|
|
9
|
+
import type { Species } from "../lib/constants";
|
|
10
|
+
import { generateRandomSuffix } from "../lib/random-name";
|
|
11
|
+
|
|
12
|
+
interface QRPairingPayload {
|
|
13
|
+
type: string;
|
|
14
|
+
v: number;
|
|
15
|
+
id?: string;
|
|
16
|
+
g: string;
|
|
17
|
+
pairingRequestId: string;
|
|
18
|
+
pairingSecret: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface PairingResponse {
|
|
22
|
+
status: "approved" | "pending";
|
|
23
|
+
bearerToken?: string;
|
|
24
|
+
gatewayUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function decodeQRCodeFromPng(pngPath: string): string {
|
|
28
|
+
const fileData = readFileSync(pngPath);
|
|
29
|
+
const png = PNG.sync.read(fileData);
|
|
30
|
+
const code = jsQR(new Uint8ClampedArray(png.data), png.width, png.height);
|
|
31
|
+
if (!code) {
|
|
32
|
+
throw new Error("Could not decode QR code from the provided PNG image.");
|
|
33
|
+
}
|
|
34
|
+
return code.data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getDeviceId(): string {
|
|
38
|
+
const raw = hostname() + userInfo().username;
|
|
39
|
+
return createHash("sha256").update(raw).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const PAIRING_POLL_INTERVAL_MS = 2000;
|
|
43
|
+
const PAIRING_POLL_TIMEOUT_MS = 120_000;
|
|
44
|
+
|
|
45
|
+
async function pollForApproval(
|
|
46
|
+
gatewayUrl: string,
|
|
47
|
+
pairingRequestId: string,
|
|
48
|
+
pairingSecret: string,
|
|
49
|
+
): Promise<PairingResponse> {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
|
|
52
|
+
while (Date.now() - startTime < PAIRING_POLL_TIMEOUT_MS) {
|
|
53
|
+
const statusUrl = `${gatewayUrl}/pairing/status?id=${encodeURIComponent(pairingRequestId)}&secret=${encodeURIComponent(pairingSecret)}`;
|
|
54
|
+
const statusRes = await fetch(statusUrl);
|
|
55
|
+
|
|
56
|
+
if (!statusRes.ok) {
|
|
57
|
+
const body = await statusRes.text().catch(() => "");
|
|
58
|
+
throw new Error(`Failed to check pairing status: HTTP ${statusRes.status}: ${body || statusRes.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const statusBody = await statusRes.json() as PairingResponse;
|
|
62
|
+
|
|
63
|
+
if (statusBody.status === "approved") {
|
|
64
|
+
return statusBody;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, PAIRING_POLL_INTERVAL_MS));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new Error("Pairing timed out waiting for approval.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function pair(): Promise<void> {
|
|
74
|
+
const args = process.argv.slice(3);
|
|
75
|
+
const qrCodePath = args[0] || process.env.VELLUM_CUSTOM_QR_CODE_PATH;
|
|
76
|
+
|
|
77
|
+
if (!qrCodePath) {
|
|
78
|
+
console.error("Usage: vellum pair <path-to-qrcode.png>");
|
|
79
|
+
console.error("");
|
|
80
|
+
console.error("Pair with a remote assistant by scanning the QR code PNG generated during setup.");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const species: Species = "vellum";
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
console.log("Reading QR code from provided image...");
|
|
88
|
+
const qrData = decodeQRCodeFromPng(qrCodePath);
|
|
89
|
+
|
|
90
|
+
let payload: QRPairingPayload;
|
|
91
|
+
try {
|
|
92
|
+
payload = JSON.parse(qrData) as QRPairingPayload;
|
|
93
|
+
} catch {
|
|
94
|
+
throw new Error("QR code does not contain valid pairing data.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (payload.type !== "vellum-daemon" || !payload.g || !payload.pairingRequestId || !payload.pairingSecret) {
|
|
98
|
+
throw new Error("QR code does not contain valid Vellum pairing data.");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const instanceName = `${species}-${generateRandomSuffix()}`;
|
|
102
|
+
const runtimeUrl = payload.g;
|
|
103
|
+
const deviceId = getDeviceId();
|
|
104
|
+
const deviceName = hostname();
|
|
105
|
+
|
|
106
|
+
console.log(`Pairing with remote assistant at ${runtimeUrl}...`);
|
|
107
|
+
|
|
108
|
+
const requestUrl = `${runtimeUrl}/pairing/request`;
|
|
109
|
+
const requestRes = await fetch(requestUrl, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: { "Content-Type": "application/json" },
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
pairingRequestId: payload.pairingRequestId,
|
|
114
|
+
pairingSecret: payload.pairingSecret,
|
|
115
|
+
deviceId,
|
|
116
|
+
deviceName,
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!requestRes.ok) {
|
|
121
|
+
const body = await requestRes.text().catch(() => "");
|
|
122
|
+
throw new Error(`Failed to initiate pairing: HTTP ${requestRes.status}: ${body || requestRes.statusText}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const requestBody = await requestRes.json() as PairingResponse;
|
|
126
|
+
|
|
127
|
+
let bearerToken: string | undefined;
|
|
128
|
+
|
|
129
|
+
if (requestBody.status === "approved") {
|
|
130
|
+
bearerToken = requestBody.bearerToken;
|
|
131
|
+
} else if (requestBody.status === "pending") {
|
|
132
|
+
console.log("Waiting for pairing approval...");
|
|
133
|
+
const approvedResponse = await pollForApproval(
|
|
134
|
+
runtimeUrl,
|
|
135
|
+
payload.pairingRequestId,
|
|
136
|
+
payload.pairingSecret,
|
|
137
|
+
);
|
|
138
|
+
bearerToken = approvedResponse.bearerToken;
|
|
139
|
+
} else {
|
|
140
|
+
throw new Error(`Unexpected pairing response status: ${requestBody.status}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const customEntry: AssistantEntry = {
|
|
144
|
+
assistantId: instanceName,
|
|
145
|
+
runtimeUrl,
|
|
146
|
+
bearerToken,
|
|
147
|
+
cloud: "custom",
|
|
148
|
+
species,
|
|
149
|
+
hatchedAt: new Date().toISOString(),
|
|
150
|
+
};
|
|
151
|
+
saveAssistantEntry(customEntry);
|
|
152
|
+
|
|
153
|
+
console.log("");
|
|
154
|
+
console.log("Successfully paired with remote assistant!");
|
|
155
|
+
console.log("Instance details:");
|
|
156
|
+
console.log(` Name: ${instanceName}`);
|
|
157
|
+
console.log(` Runtime URL: ${runtimeUrl}`);
|
|
158
|
+
console.log("");
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { contacts } from "./commands/contacts";
|
|
|
14
14
|
import { email } from "./commands/email";
|
|
15
15
|
import { hatch } from "./commands/hatch";
|
|
16
16
|
import { login, logout, whoami } from "./commands/login";
|
|
17
|
+
import { pair } from "./commands/pair";
|
|
17
18
|
import { ps } from "./commands/ps";
|
|
18
19
|
import { recover } from "./commands/recover";
|
|
19
20
|
import { retire } from "./commands/retire";
|
|
@@ -30,6 +31,7 @@ const commands = {
|
|
|
30
31
|
email,
|
|
31
32
|
hatch,
|
|
32
33
|
login,
|
|
34
|
+
pair,
|
|
33
35
|
logout,
|
|
34
36
|
ps,
|
|
35
37
|
recover,
|
|
@@ -88,6 +90,7 @@ async function main() {
|
|
|
88
90
|
console.log(" contacts Manage the contact graph");
|
|
89
91
|
console.log(" email Email operations (status, create inbox)");
|
|
90
92
|
console.log(" hatch Create a new assistant instance");
|
|
93
|
+
console.log(" pair Pair with a remote assistant via QR code");
|
|
91
94
|
console.log(" login Log in to the Vellum platform");
|
|
92
95
|
console.log(" logout Log out of the Vellum platform");
|
|
93
96
|
console.log(" ps List assistants (or processes for a specific assistant)");
|