@vibelet/cli 0.1.38 → 1.0.0

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.
Files changed (323) hide show
  1. package/README.md +80 -0
  2. package/bin/cloudflared-quick-tunnel.mjs +11 -0
  3. package/bin/cloudflared-resolver.mjs +171 -0
  4. package/bin/vibelet-runtime-policy.mjs +36 -0
  5. package/bin/vibelet.cjs +12 -0
  6. package/bin/vibelet.mjs +1062 -0
  7. package/dist/index.cjs +126 -0
  8. package/package.json +24 -22
  9. package/app.json +0 -5
  10. package/dist/advertised-hosts.d.ts +0 -34
  11. package/dist/advertised-hosts.d.ts.map +0 -1
  12. package/dist/advertised-hosts.js +0 -176
  13. package/dist/advertised-hosts.js.map +0 -1
  14. package/dist/advertised-hosts.test.d.ts +0 -2
  15. package/dist/advertised-hosts.test.d.ts.map +0 -1
  16. package/dist/advertised-hosts.test.js +0 -96
  17. package/dist/advertised-hosts.test.js.map +0 -1
  18. package/dist/audit.d.ts +0 -30
  19. package/dist/audit.d.ts.map +0 -1
  20. package/dist/audit.js +0 -73
  21. package/dist/audit.js.map +0 -1
  22. package/dist/audit.test.d.ts +0 -2
  23. package/dist/audit.test.d.ts.map +0 -1
  24. package/dist/audit.test.js +0 -33
  25. package/dist/audit.test.js.map +0 -1
  26. package/dist/auth.d.ts +0 -6
  27. package/dist/auth.d.ts.map +0 -1
  28. package/dist/auth.js +0 -27
  29. package/dist/auth.js.map +0 -1
  30. package/dist/claude-hooks.d.ts +0 -58
  31. package/dist/claude-hooks.d.ts.map +0 -1
  32. package/dist/claude-hooks.js +0 -129
  33. package/dist/claude-hooks.js.map +0 -1
  34. package/dist/cli-version.d.ts +0 -3
  35. package/dist/cli-version.d.ts.map +0 -1
  36. package/dist/cli-version.js +0 -35
  37. package/dist/cli-version.js.map +0 -1
  38. package/dist/cli-version.test.d.ts +0 -2
  39. package/dist/cli-version.test.d.ts.map +0 -1
  40. package/dist/cli-version.test.js +0 -38
  41. package/dist/cli-version.test.js.map +0 -1
  42. package/dist/config.d.ts +0 -30
  43. package/dist/config.d.ts.map +0 -1
  44. package/dist/config.js +0 -327
  45. package/dist/config.js.map +0 -1
  46. package/dist/config.test.d.ts +0 -2
  47. package/dist/config.test.d.ts.map +0 -1
  48. package/dist/config.test.js +0 -184
  49. package/dist/config.test.js.map +0 -1
  50. package/dist/dev-auth.test.d.ts +0 -2
  51. package/dist/dev-auth.test.d.ts.map +0 -1
  52. package/dist/dev-auth.test.js +0 -154
  53. package/dist/dev-auth.test.js.map +0 -1
  54. package/dist/dev-script.test.d.ts +0 -2
  55. package/dist/dev-script.test.d.ts.map +0 -1
  56. package/dist/dev-script.test.js +0 -412
  57. package/dist/dev-script.test.js.map +0 -1
  58. package/dist/drivers/claude.d.ts +0 -34
  59. package/dist/drivers/claude.d.ts.map +0 -1
  60. package/dist/drivers/claude.js +0 -413
  61. package/dist/drivers/claude.js.map +0 -1
  62. package/dist/drivers/claude.test.d.ts +0 -2
  63. package/dist/drivers/claude.test.d.ts.map +0 -1
  64. package/dist/drivers/claude.test.js +0 -951
  65. package/dist/drivers/claude.test.js.map +0 -1
  66. package/dist/drivers/codex.d.ts +0 -38
  67. package/dist/drivers/codex.d.ts.map +0 -1
  68. package/dist/drivers/codex.js +0 -771
  69. package/dist/drivers/codex.js.map +0 -1
  70. package/dist/drivers/codex.test.d.ts +0 -2
  71. package/dist/drivers/codex.test.d.ts.map +0 -1
  72. package/dist/drivers/codex.test.js +0 -939
  73. package/dist/drivers/codex.test.js.map +0 -1
  74. package/dist/drivers/types.d.ts +0 -14
  75. package/dist/drivers/types.d.ts.map +0 -1
  76. package/dist/drivers/types.js +0 -2
  77. package/dist/drivers/types.js.map +0 -1
  78. package/dist/e2e.test.d.ts +0 -2
  79. package/dist/e2e.test.d.ts.map +0 -1
  80. package/dist/e2e.test.js +0 -111
  81. package/dist/e2e.test.js.map +0 -1
  82. package/dist/identity.d.ts +0 -10
  83. package/dist/identity.d.ts.map +0 -1
  84. package/dist/identity.js +0 -66
  85. package/dist/identity.js.map +0 -1
  86. package/dist/identity.test.d.ts +0 -2
  87. package/dist/identity.test.d.ts.map +0 -1
  88. package/dist/identity.test.js +0 -25
  89. package/dist/identity.test.js.map +0 -1
  90. package/dist/index-entry.test.d.ts +0 -2
  91. package/dist/index-entry.test.d.ts.map +0 -1
  92. package/dist/index-entry.test.js +0 -272
  93. package/dist/index-entry.test.js.map +0 -1
  94. package/dist/index.d.ts +0 -2
  95. package/dist/index.d.ts.map +0 -1
  96. package/dist/index.js +0 -707
  97. package/dist/index.js.map +0 -1
  98. package/dist/logger.d.ts +0 -31
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -75
  101. package/dist/logger.js.map +0 -1
  102. package/dist/metrics.d.ts +0 -52
  103. package/dist/metrics.d.ts.map +0 -1
  104. package/dist/metrics.js +0 -89
  105. package/dist/metrics.js.map +0 -1
  106. package/dist/pairing-store.d.ts +0 -29
  107. package/dist/pairing-store.d.ts.map +0 -1
  108. package/dist/pairing-store.js +0 -131
  109. package/dist/pairing-store.js.map +0 -1
  110. package/dist/pairing-store.test.d.ts +0 -2
  111. package/dist/pairing-store.test.d.ts.map +0 -1
  112. package/dist/pairing-store.test.js +0 -47
  113. package/dist/pairing-store.test.js.map +0 -1
  114. package/dist/paths.d.ts +0 -16
  115. package/dist/paths.d.ts.map +0 -1
  116. package/dist/paths.js +0 -18
  117. package/dist/paths.js.map +0 -1
  118. package/dist/perf-compare.d.ts +0 -13
  119. package/dist/perf-compare.d.ts.map +0 -1
  120. package/dist/perf-compare.js +0 -125
  121. package/dist/perf-compare.js.map +0 -1
  122. package/dist/port-conflict.d.ts +0 -9
  123. package/dist/port-conflict.d.ts.map +0 -1
  124. package/dist/port-conflict.js +0 -33
  125. package/dist/port-conflict.js.map +0 -1
  126. package/dist/port-conflict.test.d.ts +0 -2
  127. package/dist/port-conflict.test.d.ts.map +0 -1
  128. package/dist/port-conflict.test.js +0 -38
  129. package/dist/port-conflict.test.js.map +0 -1
  130. package/dist/process-scanner.d.ts +0 -43
  131. package/dist/process-scanner.d.ts.map +0 -1
  132. package/dist/process-scanner.js +0 -453
  133. package/dist/process-scanner.js.map +0 -1
  134. package/dist/process-scanner.perf.test.d.ts +0 -2
  135. package/dist/process-scanner.perf.test.d.ts.map +0 -1
  136. package/dist/process-scanner.perf.test.js +0 -186
  137. package/dist/process-scanner.perf.test.js.map +0 -1
  138. package/dist/process-scanner.test.d.ts +0 -2
  139. package/dist/process-scanner.test.d.ts.map +0 -1
  140. package/dist/process-scanner.test.js +0 -399
  141. package/dist/process-scanner.test.js.map +0 -1
  142. package/dist/push-protocol.d.ts +0 -15
  143. package/dist/push-protocol.d.ts.map +0 -1
  144. package/dist/push-protocol.js +0 -23
  145. package/dist/push-protocol.js.map +0 -1
  146. package/dist/push-protocol.test.d.ts +0 -2
  147. package/dist/push-protocol.test.d.ts.map +0 -1
  148. package/dist/push-protocol.test.js +0 -57
  149. package/dist/push-protocol.test.js.map +0 -1
  150. package/dist/push-store.d.ts +0 -22
  151. package/dist/push-store.d.ts.map +0 -1
  152. package/dist/push-store.js +0 -103
  153. package/dist/push-store.js.map +0 -1
  154. package/dist/push-store.test.d.ts +0 -2
  155. package/dist/push-store.test.d.ts.map +0 -1
  156. package/dist/push-store.test.js +0 -79
  157. package/dist/push-store.test.js.map +0 -1
  158. package/dist/push.d.ts +0 -65
  159. package/dist/push.d.ts.map +0 -1
  160. package/dist/push.js +0 -202
  161. package/dist/push.js.map +0 -1
  162. package/dist/push.test.d.ts +0 -2
  163. package/dist/push.test.d.ts.map +0 -1
  164. package/dist/push.test.js +0 -199
  165. package/dist/push.test.js.map +0 -1
  166. package/dist/safe-stdio.d.ts +0 -3
  167. package/dist/safe-stdio.d.ts.map +0 -1
  168. package/dist/safe-stdio.js +0 -46
  169. package/dist/safe-stdio.js.map +0 -1
  170. package/dist/scanner.d.ts +0 -30
  171. package/dist/scanner.d.ts.map +0 -1
  172. package/dist/scanner.js +0 -859
  173. package/dist/scanner.js.map +0 -1
  174. package/dist/scanner.perf.test.d.ts +0 -2
  175. package/dist/scanner.perf.test.d.ts.map +0 -1
  176. package/dist/scanner.perf.test.js +0 -320
  177. package/dist/scanner.perf.test.js.map +0 -1
  178. package/dist/scanner.test.d.ts +0 -2
  179. package/dist/scanner.test.d.ts.map +0 -1
  180. package/dist/scanner.test.js +0 -948
  181. package/dist/scanner.test.js.map +0 -1
  182. package/dist/session-inventory.d.ts +0 -63
  183. package/dist/session-inventory.d.ts.map +0 -1
  184. package/dist/session-inventory.js +0 -525
  185. package/dist/session-inventory.js.map +0 -1
  186. package/dist/session-inventory.perf.test.d.ts +0 -2
  187. package/dist/session-inventory.perf.test.d.ts.map +0 -1
  188. package/dist/session-inventory.perf.test.js +0 -220
  189. package/dist/session-inventory.perf.test.js.map +0 -1
  190. package/dist/session-inventory.test.d.ts +0 -2
  191. package/dist/session-inventory.test.d.ts.map +0 -1
  192. package/dist/session-inventory.test.js +0 -712
  193. package/dist/session-inventory.test.js.map +0 -1
  194. package/dist/session-manager.d.ts +0 -75
  195. package/dist/session-manager.d.ts.map +0 -1
  196. package/dist/session-manager.js +0 -1515
  197. package/dist/session-manager.js.map +0 -1
  198. package/dist/session-manager.test.d.ts +0 -2
  199. package/dist/session-manager.test.d.ts.map +0 -1
  200. package/dist/session-manager.test.js +0 -2861
  201. package/dist/session-manager.test.js.map +0 -1
  202. package/dist/session-store.d.ts +0 -42
  203. package/dist/session-store.d.ts.map +0 -1
  204. package/dist/session-store.js +0 -163
  205. package/dist/session-store.js.map +0 -1
  206. package/dist/session-store.test.d.ts +0 -2
  207. package/dist/session-store.test.d.ts.map +0 -1
  208. package/dist/session-store.test.js +0 -236
  209. package/dist/session-store.test.js.map +0 -1
  210. package/dist/session-title.d.ts +0 -6
  211. package/dist/session-title.d.ts.map +0 -1
  212. package/dist/session-title.js +0 -105
  213. package/dist/session-title.js.map +0 -1
  214. package/dist/session-title.perf.test.d.ts +0 -2
  215. package/dist/session-title.perf.test.d.ts.map +0 -1
  216. package/dist/session-title.perf.test.js +0 -99
  217. package/dist/session-title.perf.test.js.map +0 -1
  218. package/dist/session-title.test.d.ts +0 -2
  219. package/dist/session-title.test.d.ts.map +0 -1
  220. package/dist/session-title.test.js +0 -199
  221. package/dist/session-title.test.js.map +0 -1
  222. package/dist/shutdown-endpoint.test.d.ts +0 -2
  223. package/dist/shutdown-endpoint.test.d.ts.map +0 -1
  224. package/dist/shutdown-endpoint.test.js +0 -93
  225. package/dist/shutdown-endpoint.test.js.map +0 -1
  226. package/dist/storage-housekeeping.d.ts +0 -28
  227. package/dist/storage-housekeeping.d.ts.map +0 -1
  228. package/dist/storage-housekeeping.js +0 -76
  229. package/dist/storage-housekeeping.js.map +0 -1
  230. package/dist/storage-housekeeping.test.d.ts +0 -2
  231. package/dist/storage-housekeeping.test.d.ts.map +0 -1
  232. package/dist/storage-housekeeping.test.js +0 -65
  233. package/dist/storage-housekeeping.test.js.map +0 -1
  234. package/dist/test-daemon-harness.d.ts +0 -31
  235. package/dist/test-daemon-harness.d.ts.map +0 -1
  236. package/dist/test-daemon-harness.js +0 -337
  237. package/dist/test-daemon-harness.js.map +0 -1
  238. package/dist/token-auth.test.d.ts +0 -2
  239. package/dist/token-auth.test.d.ts.map +0 -1
  240. package/dist/token-auth.test.js +0 -52
  241. package/dist/token-auth.test.js.map +0 -1
  242. package/dist/utils.d.ts +0 -4
  243. package/dist/utils.d.ts.map +0 -1
  244. package/dist/utils.js +0 -40
  245. package/dist/utils.js.map +0 -1
  246. package/dist/utils.test.d.ts +0 -2
  247. package/dist/utils.test.d.ts.map +0 -1
  248. package/dist/utils.test.js +0 -54
  249. package/dist/utils.test.js.map +0 -1
  250. package/dist/ws-data.d.ts +0 -4
  251. package/dist/ws-data.d.ts.map +0 -1
  252. package/dist/ws-data.js +0 -20
  253. package/dist/ws-data.js.map +0 -1
  254. package/dist/ws-data.test.d.ts +0 -2
  255. package/dist/ws-data.test.d.ts.map +0 -1
  256. package/dist/ws-data.test.js +0 -17
  257. package/dist/ws-data.test.js.map +0 -1
  258. package/perf-reporter.mjs +0 -138
  259. package/scripts/build-release.mjs +0 -41
  260. package/scripts/dev.mjs +0 -537
  261. package/src/advertised-hosts.test.ts +0 -125
  262. package/src/advertised-hosts.ts +0 -225
  263. package/src/audit.test.ts +0 -38
  264. package/src/audit.ts +0 -117
  265. package/src/auth.ts +0 -31
  266. package/src/claude-hooks.ts +0 -195
  267. package/src/cli-version.test.ts +0 -36
  268. package/src/cli-version.ts +0 -46
  269. package/src/config.test.ts +0 -254
  270. package/src/config.ts +0 -324
  271. package/src/dev-auth.test.ts +0 -183
  272. package/src/dev-script.test.ts +0 -511
  273. package/src/drivers/claude.test.ts +0 -1186
  274. package/src/drivers/claude.ts +0 -443
  275. package/src/drivers/codex.test.ts +0 -1096
  276. package/src/drivers/codex.ts +0 -879
  277. package/src/drivers/types.ts +0 -15
  278. package/src/e2e.test.ts +0 -139
  279. package/src/identity.test.ts +0 -26
  280. package/src/identity.ts +0 -82
  281. package/src/index-entry.test.ts +0 -336
  282. package/src/index.ts +0 -781
  283. package/src/logger.ts +0 -112
  284. package/src/metrics.ts +0 -117
  285. package/src/pairing-store.test.ts +0 -53
  286. package/src/pairing-store.ts +0 -154
  287. package/src/paths.ts +0 -19
  288. package/src/perf-compare.ts +0 -164
  289. package/src/port-conflict.test.ts +0 -45
  290. package/src/port-conflict.ts +0 -44
  291. package/src/process-scanner.perf.test.ts +0 -222
  292. package/src/process-scanner.test.ts +0 -575
  293. package/src/process-scanner.ts +0 -514
  294. package/src/push-protocol.test.ts +0 -74
  295. package/src/push-protocol.ts +0 -36
  296. package/src/push-store.test.ts +0 -89
  297. package/src/push-store.ts +0 -126
  298. package/src/push.test.ts +0 -234
  299. package/src/push.ts +0 -318
  300. package/src/safe-stdio.ts +0 -51
  301. package/src/scanner.perf.test.ts +0 -359
  302. package/src/scanner.test.ts +0 -1045
  303. package/src/scanner.ts +0 -924
  304. package/src/session-inventory.perf.test.ts +0 -250
  305. package/src/session-inventory.test.ts +0 -1002
  306. package/src/session-inventory.ts +0 -721
  307. package/src/session-manager.test.ts +0 -3430
  308. package/src/session-manager.ts +0 -1775
  309. package/src/session-store.test.ts +0 -276
  310. package/src/session-store.ts +0 -202
  311. package/src/session-title.perf.test.ts +0 -118
  312. package/src/session-title.test.ts +0 -286
  313. package/src/session-title.ts +0 -108
  314. package/src/shutdown-endpoint.test.ts +0 -95
  315. package/src/storage-housekeeping.test.ts +0 -78
  316. package/src/storage-housekeeping.ts +0 -111
  317. package/src/test-daemon-harness.ts +0 -410
  318. package/src/token-auth.test.ts +0 -67
  319. package/src/utils.test.ts +0 -65
  320. package/src/utils.ts +0 -47
  321. package/src/ws-data.test.ts +0 -20
  322. package/src/ws-data.ts +0 -26
  323. package/tsconfig.json +0 -12
package/dist/index.js DELETED
@@ -1,707 +0,0 @@
1
- import { createServer } from 'http';
2
- import { WebSocketServer } from 'ws';
3
- import { mkdir, readFile, stat, readdir, writeFile } from 'fs/promises';
4
- import { randomBytes } from 'crypto';
5
- import { extname, join } from 'path';
6
- import { networkInterfaces } from 'os';
7
- import QRCode from 'qrcode';
8
- import { config } from './config.js';
9
- import { SessionManager } from './session-manager.js';
10
- import { getExternalInventoryHealth } from './session-inventory.js';
11
- import { resolveFileRequestPath } from './utils.js';
12
- import { logger as rootLogger } from './logger.js';
13
- import { metrics } from './metrics.js';
14
- import { audit } from './audit.js';
15
- import { registerToken, sendPush, unregisterToken } from './push.js';
16
- import { handlePushProtocolMessage } from './push-protocol.js';
17
- import { collectPortOccupants } from './port-conflict.js';
18
- import { loadOrCreateDaemonIdentity } from './identity.js';
19
- import { PairingStore } from './pairing-store.js';
20
- import { isAuthorizedToken, isLegacyToken, isLoopbackAddress, resolveRequestToken } from './auth.js';
21
- import { computeAdvertisedConnectionTarget, computeAdvertisedHosts, readConfiguredFallbackHosts, readTailscaleHosts, } from './advertised-hosts.js';
22
- import { readRawDataAsText } from './ws-data.js';
23
- import { runStorageHousekeepingSync } from './storage-housekeeping.js';
24
- import { AUDIT_PATH, DAEMON_STDERR_LOG_PATH, DAEMON_STDOUT_LOG_PATH, PAIRING_QR_SVG_PATH, UPDATE_CHECK_PATH, VIBELET_DIR, VIBELET_UPLOADS_DIR } from './paths.js';
25
- import { CLI_VERSION } from './cli-version.js';
26
- import { CLAUDE_HOOK_SECRET_HEADER } from './claude-hooks.js';
27
- import { writeStdoutSafe } from './safe-stdio.js';
28
- const log = rootLogger.child({ module: 'daemon' });
29
- const wsLog = rootLogger.child({ module: 'ws' });
30
- function readLatestVersion() {
31
- try {
32
- const data = JSON.parse(require('fs').readFileSync(UPDATE_CHECK_PATH, 'utf8'));
33
- return typeof data.latestVersion === 'string' ? data.latestVersion : undefined;
34
- }
35
- catch {
36
- return undefined;
37
- }
38
- }
39
- const identity = loadOrCreateDaemonIdentity(config.port);
40
- const manager = new SessionManager((title, body, data) => sendPush(title, body, {
41
- daemonId: identity.daemonId,
42
- canonicalHost: getAdvertisedConnectionTarget().canonicalHost,
43
- ...(data ?? {}),
44
- }));
45
- const pairingStore = new PairingStore();
46
- const sockets = new Set();
47
- const authenticatedClients = new WeakMap();
48
- let shuttingDown = false;
49
- const startTime = Date.now();
50
- let storageHousekeepingInterval = null;
51
- let advertisedConnectionTargetCache = null;
52
- const ADVERTISED_CONNECTION_TARGET_CACHE_TTL_MS = 1_000;
53
- function getAdvertisedHosts() {
54
- const tailscaleHosts = readTailscaleHosts();
55
- return computeAdvertisedHosts({
56
- canonicalHost: identity.canonicalHost,
57
- configuredCanonicalHost: config.canonicalHost,
58
- configuredFallbackHosts: readConfiguredFallbackHosts(config.fallbackHosts),
59
- tailscaleCanonicalHost: tailscaleHosts.canonicalHost,
60
- tailscaleFallbackHosts: tailscaleHosts.fallbackHosts,
61
- interfaces: networkInterfaces(),
62
- });
63
- }
64
- function getAdvertisedConnectionTarget() {
65
- const now = Date.now();
66
- if (advertisedConnectionTargetCache && advertisedConnectionTargetCache.expiresAt > now) {
67
- return advertisedConnectionTargetCache.value;
68
- }
69
- const advertisedHosts = getAdvertisedHosts();
70
- const connectionTarget = computeAdvertisedConnectionTarget({
71
- canonicalHost: advertisedHosts.canonicalHost,
72
- fallbackHosts: advertisedHosts.fallbackHosts,
73
- port: identity.port,
74
- relayUrl: config.relayUrl,
75
- });
76
- // Tailscale probing is synchronous and can take a few seconds when the socket
77
- // is unavailable. Cache the computed target briefly so startup logging, health
78
- // checks, and immediate pairing calls do not repeatedly block the event loop.
79
- advertisedConnectionTargetCache = {
80
- value: connectionTarget,
81
- expiresAt: now + ADVERTISED_CONNECTION_TARGET_CACHE_TTL_MS,
82
- };
83
- return connectionTarget;
84
- }
85
- function createCompactPairingPayload(pairingPayload) {
86
- const compactPayload = {
87
- t: 'vp',
88
- d: pairingPayload.daemonId,
89
- n: pairingPayload.displayName,
90
- h: pairingPayload.canonicalHost,
91
- p: pairingPayload.port,
92
- c: pairingPayload.pairNonce,
93
- e: pairingPayload.expiresAt,
94
- };
95
- if (pairingPayload.fallbackHosts)
96
- compactPayload.f = pairingPayload.fallbackHosts;
97
- return compactPayload;
98
- }
99
- async function writeStartupPairingQr(pairingPayload) {
100
- const payload = JSON.stringify(createCompactPairingPayload(pairingPayload));
101
- if (process.stdout.isTTY) {
102
- const qr = await QRCode.toString(payload, {
103
- type: 'terminal',
104
- small: true,
105
- errorCorrectionLevel: 'L',
106
- });
107
- writeStdoutSafe(`\nScan this QR code with Vibelet app:\n\n${qr}\n`);
108
- return;
109
- }
110
- await mkdir(VIBELET_DIR, { recursive: true });
111
- const qrSvg = await QRCode.toString(payload, {
112
- type: 'svg',
113
- errorCorrectionLevel: 'L',
114
- margin: 1,
115
- });
116
- await writeFile(PAIRING_QR_SVG_PATH, qrSvg, 'utf8');
117
- writeStdoutSafe(`\nPairing QR saved to ${PAIRING_QR_SVG_PATH}\n`);
118
- writeStdoutSafe('Open that SVG file to scan in shells or UIs that distort terminal QR output.\n');
119
- }
120
- async function readJsonBody(req) {
121
- const chunks = [];
122
- for await (const chunk of req) {
123
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
124
- }
125
- const raw = Buffer.concat(chunks).toString('utf8');
126
- return raw ? JSON.parse(raw) : {};
127
- }
128
- function sendJson(res, statusCode, body) {
129
- res.writeHead(statusCode, { 'Content-Type': 'application/json' });
130
- res.end(JSON.stringify(body, null, 2));
131
- }
132
- function getFileContentType(filePath) {
133
- const ext = extname(filePath).toLowerCase();
134
- const mimeTypes = {
135
- '.png': 'image/png',
136
- '.jpg': 'image/jpeg',
137
- '.jpeg': 'image/jpeg',
138
- '.gif': 'image/gif',
139
- '.svg': 'image/svg+xml',
140
- '.webp': 'image/webp',
141
- '.heic': 'image/heic',
142
- '.heif': 'image/heif',
143
- '.pdf': 'application/pdf',
144
- '.md': 'text/markdown; charset=utf-8',
145
- '.markdown': 'text/markdown; charset=utf-8',
146
- '.txt': 'text/plain; charset=utf-8',
147
- '.log': 'text/plain; charset=utf-8',
148
- '.json': 'application/json; charset=utf-8',
149
- '.js': 'text/plain; charset=utf-8',
150
- '.jsx': 'text/plain; charset=utf-8',
151
- '.ts': 'text/plain; charset=utf-8',
152
- '.tsx': 'text/plain; charset=utf-8',
153
- '.mjs': 'text/plain; charset=utf-8',
154
- '.cjs': 'text/plain; charset=utf-8',
155
- '.css': 'text/plain; charset=utf-8',
156
- '.html': 'text/plain; charset=utf-8',
157
- '.yml': 'text/plain; charset=utf-8',
158
- '.yaml': 'text/plain; charset=utf-8',
159
- };
160
- return mimeTypes[ext] || 'application/octet-stream';
161
- }
162
- function buildPairingPayload() {
163
- const window = pairingStore.openWindow();
164
- const connectionTarget = getAdvertisedConnectionTarget();
165
- return {
166
- type: 'vibelet-pair',
167
- daemonId: identity.daemonId,
168
- displayName: identity.displayName,
169
- canonicalHost: connectionTarget.canonicalHost,
170
- fallbackHosts: connectionTarget.fallbackHosts.length > 0 ? connectionTarget.fallbackHosts : undefined,
171
- port: connectionTarget.port,
172
- pairNonce: window.pairNonce,
173
- expiresAt: window.expiresAt,
174
- };
175
- }
176
- async function listDirs(path, cwd) {
177
- const resolvedPath = resolveFileRequestPath(path, cwd);
178
- const entries = await readdir(resolvedPath);
179
- const results = [];
180
- for (const name of entries) {
181
- if (name.startsWith('.'))
182
- continue;
183
- const s = await stat(join(resolvedPath, name)).catch(() => null);
184
- if (s)
185
- results.push({ name, isDirectory: s.isDirectory() });
186
- }
187
- // Sort: dirs first, then files
188
- results.sort((a, b) => {
189
- if (a.isDirectory !== b.isDirectory)
190
- return a.isDirectory ? -1 : 1;
191
- return a.name.localeCompare(b.name);
192
- });
193
- return { entries: results };
194
- }
195
- // HTTP server for file serving (images) + WebSocket upgrade + health endpoint
196
- const httpServer = createServer(async (req, res) => {
197
- const url = new URL(req.url ?? '/', `http://localhost:${config.port}`);
198
- // Health endpoint — no token required for basic health check
199
- if (url.pathname === '/health') {
200
- const connectionTarget = getAdvertisedConnectionTarget();
201
- const health = {
202
- status: 'ok',
203
- version: CLI_VERSION,
204
- daemonId: identity.daemonId,
205
- displayName: identity.displayName,
206
- canonicalHost: connectionTarget.canonicalHost,
207
- uptimeSeconds: Math.round((Date.now() - startTime) / 1000),
208
- activeSessions: manager.getActiveSessionCount(),
209
- pairedDevices: pairingStore.pairedCount(),
210
- wsClients: wss.clients.size,
211
- drivers: manager.getDriverCounts(),
212
- sessionInventory: getExternalInventoryHealth(),
213
- metrics: metrics.snapshot(),
214
- };
215
- if (config.relayUrl) {
216
- health.relayUrl = config.relayUrl;
217
- }
218
- const latestVersion = readLatestVersion();
219
- if (latestVersion) {
220
- health.latestVersion = latestVersion;
221
- }
222
- sendJson(res, 200, health);
223
- return;
224
- }
225
- if (req.method === 'POST' && url.pathname === '/pair/open') {
226
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
227
- sendJson(res, 403, { error: 'forbidden' });
228
- return;
229
- }
230
- sendJson(res, 200, buildPairingPayload());
231
- return;
232
- }
233
- if (req.method === 'POST' && url.pathname === '/pair/reset') {
234
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
235
- sendJson(res, 403, { error: 'forbidden' });
236
- return;
237
- }
238
- pairingStore.reset();
239
- sendJson(res, 200, { ok: true });
240
- return;
241
- }
242
- if (req.method === 'POST' && url.pathname === '/shutdown') {
243
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
244
- sendJson(res, 403, { error: 'forbidden' });
245
- return;
246
- }
247
- sendJson(res, 200, { status: 'stopping' });
248
- setTimeout(() => shutdown('HTTP /shutdown request'), 50);
249
- return;
250
- }
251
- if (req.method === 'POST' && url.pathname === '/hook/claude/session-start') {
252
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
253
- sendJson(res, 403, { error: 'forbidden' });
254
- return;
255
- }
256
- try {
257
- const body = await readJsonBody(req);
258
- const secretHeader = req.headers[CLAUDE_HOOK_SECRET_HEADER];
259
- const secret = Array.isArray(secretHeader) ? secretHeader[0] : secretHeader;
260
- manager.handleClaudeSessionStartHook(secret, body);
261
- sendJson(res, 200, { ok: true });
262
- }
263
- catch (error) {
264
- sendJson(res, 400, { error: String(error) });
265
- }
266
- return;
267
- }
268
- if (req.method === 'POST' && url.pathname === '/hook/claude/permission-request') {
269
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
270
- sendJson(res, 403, { error: 'forbidden' });
271
- return;
272
- }
273
- try {
274
- const body = await readJsonBody(req);
275
- const secretHeader = req.headers[CLAUDE_HOOK_SECRET_HEADER];
276
- const secret = Array.isArray(secretHeader) ? secretHeader[0] : secretHeader;
277
- const response = await manager.handleClaudePermissionHook(secret, body);
278
- sendJson(res, 200, response);
279
- }
280
- catch (error) {
281
- sendJson(res, 400, { error: String(error) });
282
- }
283
- return;
284
- }
285
- if (req.method === 'POST' && url.pathname === '/pair/create') {
286
- try {
287
- const body = await readJsonBody(req);
288
- if (body.daemonId !== identity.daemonId ||
289
- typeof body.pairNonce !== 'string' ||
290
- typeof body.deviceId !== 'string' ||
291
- typeof body.deviceName !== 'string') {
292
- sendJson(res, 400, { error: 'invalid_pair_request' });
293
- return;
294
- }
295
- const window = pairingStore.consumeWindow(body.pairNonce);
296
- if (!window) {
297
- sendJson(res, 401, { error: 'pair_window_expired' });
298
- return;
299
- }
300
- const pairToken = pairingStore.issuePairToken(body.deviceId, body.deviceName);
301
- const connectionTarget = getAdvertisedConnectionTarget();
302
- sendJson(res, 200, {
303
- daemonId: identity.daemonId,
304
- displayName: identity.displayName,
305
- canonicalHost: connectionTarget.canonicalHost,
306
- fallbackHosts: connectionTarget.fallbackHosts,
307
- port: connectionTarget.port,
308
- deviceId: body.deviceId,
309
- pairToken,
310
- });
311
- }
312
- catch (error) {
313
- sendJson(res, 400, { error: String(error) });
314
- }
315
- return;
316
- }
317
- const token = resolveRequestToken(req.headers.authorization, url.searchParams.get('token'));
318
- if (!isAuthorizedToken(token, config.legacyToken, (value, touch) => pairingStore.validateAnyPairToken(value, touch), false)) {
319
- res.writeHead(401);
320
- res.end('Unauthorized');
321
- return;
322
- }
323
- // Upload image: POST /upload (raw binary body, Content-Type = image/*)
324
- if (req.method === 'POST' && url.pathname === '/upload') {
325
- const UPLOAD_MIME_TO_EXT = {
326
- 'image/png': '.png',
327
- 'image/jpeg': '.jpg',
328
- 'image/gif': '.gif',
329
- 'image/webp': '.webp',
330
- 'image/heic': '.heic',
331
- 'image/heif': '.heif',
332
- };
333
- const contentType = (req.headers['content-type'] ?? '').split(';')[0].trim();
334
- const ext = UPLOAD_MIME_TO_EXT[contentType];
335
- if (!ext) {
336
- sendJson(res, 400, { error: 'Unsupported content type' });
337
- return;
338
- }
339
- const MAX_UPLOAD_SIZE = 10 * 1024 * 1024; // 10MB
340
- const chunks = [];
341
- let totalSize = 0;
342
- for await (const chunk of req) {
343
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
344
- totalSize += buf.length;
345
- if (totalSize > MAX_UPLOAD_SIZE) {
346
- res.writeHead(413);
347
- res.end('File too large');
348
- return;
349
- }
350
- chunks.push(buf);
351
- }
352
- try {
353
- await mkdir(VIBELET_UPLOADS_DIR, { recursive: true });
354
- const filename = `${Date.now()}_${randomBytes(8).toString('hex')}${ext}`;
355
- const filePath = join(VIBELET_UPLOADS_DIR, filename);
356
- await writeFile(filePath, Buffer.concat(chunks));
357
- log.info({ filePath, size: totalSize }, 'file uploaded');
358
- sendJson(res, 200, { path: filePath });
359
- }
360
- catch (e) {
361
- log.error({ error: String(e) }, 'upload failed');
362
- sendJson(res, 500, { error: 'Upload failed' });
363
- }
364
- return;
365
- }
366
- // Serve files: GET /file?path=/path/to/file&cwd=/repo&token=xxx
367
- if (url.pathname === '/file') {
368
- const filePath = url.searchParams.get('path');
369
- const cwd = url.searchParams.get('cwd') || undefined;
370
- if (!filePath) {
371
- res.writeHead(400);
372
- res.end('Missing path parameter');
373
- return;
374
- }
375
- try {
376
- const resolvedPath = resolveFileRequestPath(filePath, cwd);
377
- const fileStat = await stat(resolvedPath);
378
- if (!fileStat.isFile()) {
379
- res.writeHead(404);
380
- res.end('Not a file');
381
- return;
382
- }
383
- const contentType = getFileContentType(resolvedPath);
384
- const data = await readFile(resolvedPath);
385
- res.writeHead(200, { 'Content-Type': contentType, 'Content-Length': data.length });
386
- res.end(data);
387
- }
388
- catch (e) {
389
- res.writeHead(404);
390
- res.end('File not found');
391
- }
392
- return;
393
- }
394
- res.writeHead(200);
395
- res.end(`Vibelet Daemon v${CLI_VERSION}`);
396
- });
397
- const wss = new WebSocketServer({ server: httpServer });
398
- httpServer.on('connection', (socket) => {
399
- sockets.add(socket);
400
- socket.on('close', () => {
401
- sockets.delete(socket);
402
- });
403
- });
404
- wss.on('connection', (ws, req) => {
405
- const url = new URL(req.url ?? '/', `http://localhost:${config.port}`);
406
- const token = url.searchParams.get('token');
407
- if (token && isLegacyToken(token, config.legacyToken)) {
408
- authenticatedClients.set(ws, { authMode: 'query_token' });
409
- metrics.increment('ws.auth.success', { mode: 'query_token' });
410
- manager.addGlobalClient(ws);
411
- manager.prewarmCaches();
412
- }
413
- else if (token) {
414
- const pairingRecord = pairingStore.validateAnyPairToken(token);
415
- if (pairingRecord) {
416
- authenticatedClients.set(ws, {
417
- authMode: 'query_token',
418
- deviceId: pairingRecord.deviceId,
419
- });
420
- metrics.increment('ws.auth.success', { mode: 'query_token' });
421
- manager.addGlobalClient(ws);
422
- manager.prewarmCaches();
423
- }
424
- else {
425
- metrics.increment('ws.auth.failure', { mode: 'query_token' });
426
- ws.close(4001, 'Unauthorized');
427
- return;
428
- }
429
- }
430
- wsLog.info({ clients: wss.clients.size }, 'client connected');
431
- metrics.increment('ws.connect');
432
- metrics.gauge('ws.clients', wss.clients.size);
433
- audit.emit('ws.connect', {
434
- clients: wss.clients.size,
435
- authMode: authenticatedClients.get(ws)?.authMode ?? 'pending',
436
- deviceId: authenticatedClients.get(ws)?.deviceId,
437
- });
438
- ws.on('message', async (data) => {
439
- try {
440
- const parsed = JSON.parse(readRawDataAsText(data));
441
- // Basic validation: action must exist and be a string
442
- if (!parsed || typeof parsed.action !== 'string') {
443
- ws.send(JSON.stringify({ type: 'error', sessionId: '', message: 'Invalid message: missing or non-string "action" field' }));
444
- return;
445
- }
446
- // Respond to heartbeat pings before auth check (harmless, keeps connection alive)
447
- if (parsed.action === 'ping') {
448
- ws.send(JSON.stringify({ type: 'pong' }));
449
- return;
450
- }
451
- const msg = parsed;
452
- if (!authenticatedClients.has(ws)) {
453
- if (msg.action === 'auth.hello' &&
454
- msg.daemonId === identity.daemonId &&
455
- pairingStore.validatePairToken(msg.deviceId, msg.pairToken)) {
456
- authenticatedClients.set(ws, {
457
- authMode: 'auth_hello',
458
- deviceId: msg.deviceId,
459
- });
460
- metrics.increment('ws.auth.success', { mode: 'auth_hello' });
461
- manager.addGlobalClient(ws);
462
- manager.prewarmCaches();
463
- ws.send(JSON.stringify({
464
- type: 'response',
465
- id: msg.id,
466
- ok: true,
467
- data: {
468
- daemonId: identity.daemonId,
469
- displayName: identity.displayName,
470
- },
471
- }));
472
- return;
473
- }
474
- if (msg.action === 'auth.hello') {
475
- metrics.increment('ws.auth.failure', { mode: 'auth_hello' });
476
- ws.send(JSON.stringify({ type: 'response', id: msg.id, ok: false, error: 'auth_failed' }));
477
- ws.close(4001, 'Unauthorized');
478
- return;
479
- }
480
- metrics.increment('ws.auth.failure', { mode: 'missing' });
481
- ws.send(JSON.stringify({ type: 'error', sessionId: '', message: 'Authentication required' }));
482
- ws.close(4001, 'Unauthorized');
483
- return;
484
- }
485
- // Handle log.report: app sends client-side logs to daemon audit trail
486
- if (msg.action === 'log.report') {
487
- for (const entry of msg.entries) {
488
- audit.emitApp(entry.event, entry.level, entry.data ?? {}, entry.ts);
489
- // Also log to daemon stdout for debugging
490
- const logFn = entry.level === 'error' ? log.error : entry.level === 'warn' ? log.warn : log.info;
491
- logFn.call(log, { event: entry.event, ...entry.data }, `[app] ${entry.event}`);
492
- }
493
- ws.send(JSON.stringify({ type: 'response', id: msg.id, ok: true }));
494
- return;
495
- }
496
- // Handle dirs.list directly in index.ts
497
- if (msg.action === 'dirs.list') {
498
- try {
499
- const result = await listDirs(msg.path, msg.cwd);
500
- ws.send(JSON.stringify({ type: 'response', id: msg.id, ok: true, data: result }));
501
- }
502
- catch (e) {
503
- ws.send(JSON.stringify({ type: 'response', id: msg.id, ok: false, error: String(e) }));
504
- }
505
- return;
506
- }
507
- if (parsed.action === 'push.register') {
508
- const context = authenticatedClients.get(ws);
509
- handlePushProtocolMessage(parsed, {
510
- deviceId: context?.deviceId,
511
- registerToken,
512
- unregisterToken,
513
- respond: (response) => ws.send(JSON.stringify(response)),
514
- });
515
- return;
516
- }
517
- if (parsed.action === 'push.unregister') {
518
- const context = authenticatedClients.get(ws);
519
- handlePushProtocolMessage(parsed, {
520
- deviceId: context?.deviceId,
521
- registerToken,
522
- unregisterToken,
523
- respond: (response) => ws.send(JSON.stringify(response)),
524
- });
525
- return;
526
- }
527
- await manager.handle(ws, msg);
528
- }
529
- catch (e) {
530
- wsLog.error({ error: String(e) }, 'message handling error');
531
- ws.send(JSON.stringify({ type: 'error', sessionId: '', message: String(e) }));
532
- }
533
- });
534
- ws.on('close', (code) => {
535
- wsLog.info({ clients: wss.clients.size, code }, 'client disconnected');
536
- metrics.increment('ws.disconnect');
537
- metrics.increment('ws.disconnect.code', { code: String(code) });
538
- metrics.gauge('ws.clients', wss.clients.size);
539
- audit.emit('ws.disconnect', { clients: wss.clients.size, code });
540
- manager.removeClient(ws);
541
- });
542
- ws.on('error', (err) => {
543
- wsLog.error({ error: err.message }, 'websocket error');
544
- });
545
- });
546
- function destroyAllSockets() {
547
- for (const socket of sockets) {
548
- try {
549
- socket.destroy();
550
- }
551
- catch { }
552
- }
553
- sockets.clear();
554
- }
555
- function runStorageHousekeeping() {
556
- const summary = runStorageHousekeepingSync({
557
- auditPath: AUDIT_PATH,
558
- stdoutLogPath: DAEMON_STDOUT_LOG_PATH,
559
- stderrLogPath: DAEMON_STDERR_LOG_PATH,
560
- auditMaxBytes: config.auditMaxBytes,
561
- logMaxBytes: config.daemonLogMaxBytes,
562
- });
563
- if (summary.trimmedFiles > 0) {
564
- log.info({
565
- trimmedFiles: summary.trimmedFiles,
566
- reclaimedBytes: summary.reclaimedBytes,
567
- files: summary.files
568
- .filter(file => file.trimmed)
569
- .map(file => ({
570
- path: file.path,
571
- beforeBytes: file.beforeBytes,
572
- afterBytes: file.afterBytes,
573
- })),
574
- }, 'trimmed vibelet storage');
575
- }
576
- }
577
- function startStorageHousekeeping() {
578
- if (storageHousekeepingInterval)
579
- return;
580
- storageHousekeepingInterval = setInterval(runStorageHousekeeping, config.storageHousekeepingIntervalMs);
581
- storageHousekeepingInterval.unref();
582
- }
583
- function stopStorageHousekeeping() {
584
- if (!storageHousekeepingInterval)
585
- return;
586
- clearInterval(storageHousekeepingInterval);
587
- storageHousekeepingInterval = null;
588
- }
589
- function shutdown(reason, exitCode = 0) {
590
- if (shuttingDown)
591
- return;
592
- shuttingDown = true;
593
- log.info({ reason, exitCode }, 'shutting down');
594
- audit.emit('daemon.shutdown', { reason, exitCode, uptimeSeconds: Math.round((Date.now() - startTime) / 1000) });
595
- metrics.stopPeriodicLog();
596
- stopStorageHousekeeping();
597
- manager.shutdown();
598
- for (const client of wss.clients) {
599
- try {
600
- client.close(1001, 'Server shutting down');
601
- client.terminate();
602
- }
603
- catch { }
604
- }
605
- wss.close((err) => {
606
- if (err)
607
- log.error({ error: String(err) }, 'websocket close error');
608
- });
609
- httpServer.close((err) => {
610
- if (err) {
611
- log.error({ error: String(err) }, 'http close error');
612
- destroyAllSockets();
613
- process.exit(1);
614
- return;
615
- }
616
- destroyAllSockets();
617
- process.exit(exitCode);
618
- });
619
- setTimeout(() => {
620
- log.error('force exiting after shutdown timeout');
621
- destroyAllSockets();
622
- process.exit(exitCode || 1);
623
- }, 3000).unref();
624
- }
625
- wss.on('error', (err) => {
626
- wsLog.error({ error: err.message }, 'wss error');
627
- });
628
- httpServer.on('error', (err) => {
629
- if (err.code === 'EADDRINUSE') {
630
- const occupants = collectPortOccupants(config.port).filter((occupant) => occupant.pid !== process.pid);
631
- log.error({ port: config.port, occupants }, 'port is already in use. Stop the existing process or use VIBE_PORT=<port> to specify another port');
632
- }
633
- else {
634
- log.error({ error: String(err) }, 'server error');
635
- }
636
- shutdown(`httpServer error: ${err.code ?? err.message}`, 1);
637
- });
638
- process.once('SIGINT', () => shutdown('SIGINT'));
639
- process.once('SIGTERM', () => shutdown('SIGTERM'));
640
- process.once('SIGHUP', () => shutdown('SIGHUP'));
641
- process.once('SIGTTIN', () => shutdown('SIGTTIN'));
642
- process.once('SIGTSTP', () => shutdown('SIGTSTP'));
643
- process.on('uncaughtException', (err) => {
644
- log.error({ error: String(err), stack: err.stack }, 'uncaughtException');
645
- audit.emit('daemon.error', { type: 'uncaughtException', error: String(err) });
646
- });
647
- process.on('unhandledRejection', (reason) => {
648
- log.error({ reason: String(reason) }, 'unhandledRejection');
649
- audit.emit('daemon.error', { type: 'unhandledRejection', reason: String(reason) });
650
- });
651
- httpServer.listen(config.port, async () => {
652
- runStorageHousekeeping();
653
- audit.emit('daemon.start', { port: config.port });
654
- metrics.startPeriodicLog();
655
- startStorageHousekeeping();
656
- const startupConnectionTarget = getAdvertisedConnectionTarget();
657
- log.info({
658
- port: config.port,
659
- pairingPort: startupConnectionTarget.port,
660
- daemonId: identity.daemonId,
661
- displayName: identity.displayName,
662
- canonicalHost: startupConnectionTarget.canonicalHost,
663
- fallbackHosts: startupConnectionTarget.fallbackHosts,
664
- claudePath: config.claudePath,
665
- codexPath: config.codexPath,
666
- auditMaxBytes: config.auditMaxBytes,
667
- daemonLogMaxBytes: config.daemonLogMaxBytes,
668
- storageHousekeepingIntervalMs: config.storageHousekeepingIntervalMs,
669
- turnStallTimeoutMs: config.turnStallTimeoutMs,
670
- }, 'daemon started');
671
- const window = pairingStore.openWindow();
672
- const qrPayload = {
673
- type: 'vibelet-pair',
674
- daemonId: identity.daemonId,
675
- displayName: identity.displayName,
676
- canonicalHost: startupConnectionTarget.canonicalHost,
677
- fallbackHosts: startupConnectionTarget.fallbackHosts.length > 0 ? startupConnectionTarget.fallbackHosts : undefined,
678
- port: startupConnectionTarget.port,
679
- pairNonce: window.pairNonce,
680
- expiresAt: window.expiresAt,
681
- };
682
- // Keep the human-friendly banner on stdout for interactive use
683
- writeStdoutSafe(`
684
- ╔══════════════════════════════════════╗
685
- ║ Vibelet Daemon v${CLI_VERSION} ║
686
- ╠══════════════════════════════════════╣
687
- ║ Port: ${String(qrPayload.port).padEnd(28)}║
688
- ║ ID: ${identity.daemonId.slice(0, 28).padEnd(28)}║
689
- ║ Host: ${qrPayload.canonicalHost.slice(0, 28).padEnd(28)}║
690
- ╚══════════════════════════════════════╝
691
-
692
- Pair with: npx @vibelet/cli or npx vibelet
693
- Host: ${qrPayload.canonicalHost}
694
- Fallbacks: ${qrPayload.fallbackHosts?.join(', ') || '(none)'}
695
-
696
- Claude: ${config.claudePath}
697
- Codex: ${config.codexPath}
698
- `);
699
- // Print QR code for app to scan
700
- try {
701
- await writeStartupPairingQr(qrPayload);
702
- }
703
- catch {
704
- // QR generation is best-effort
705
- }
706
- });
707
- //# sourceMappingURL=index.js.map