@vibelet/cli 0.1.38 → 1.0.1

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 +1235 -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/src/logger.ts DELETED
@@ -1,112 +0,0 @@
1
- import { appendFileSync, mkdirSync } from 'fs';
2
- import {
3
- DAEMON_STDERR_LOG_PATH,
4
- DAEMON_STDOUT_LOG_PATH,
5
- VIBELET_LOG_DIR,
6
- } from './paths.js';
7
- import { writeStderrSafe, writeStdoutSafe } from './safe-stdio.js';
8
-
9
- /**
10
- * Structured logger for the Vibelet daemon.
11
- *
12
- * Outputs one JSON object per line (JSONL) to stdout/stderr.
13
- * Each entry includes: level, ts, msg, and any extra context fields.
14
- *
15
- * Usage:
16
- * import { logger } from './logger.js';
17
- * logger.info({ sessionId, agent }, 'session created');
18
- * const child = logger.child({ sessionId, agent });
19
- * child.info('driver started');
20
- */
21
-
22
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
23
-
24
- const LEVEL_VALUE: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };
25
-
26
- let minLevel: LogLevel = (process.env.VIBE_LOG_LEVEL as LogLevel) || 'info';
27
-
28
- export function setLogLevel(level: LogLevel): void {
29
- minLevel = level;
30
- }
31
-
32
- interface LogContext {
33
- [key: string]: unknown;
34
- }
35
-
36
- function isTestRuntime(): boolean {
37
- return process.env.VIBE_TEST === '1' || process.argv.includes('--test');
38
- }
39
-
40
- // Skip appendFileSync when launchd/systemd already redirects stdout/stderr
41
- // to the log files — writing to both causes every line to appear twice.
42
- function stdoutAlreadyRedirected(): boolean {
43
- try {
44
- const { fstatSync } = require('fs') as typeof import('fs');
45
- const stat = fstatSync(1);
46
- return stat.isFile();
47
- } catch {
48
- return false;
49
- }
50
- }
51
-
52
- const PERSIST_LOG_FILE = !isTestRuntime() && !stdoutAlreadyRedirected();
53
-
54
- if (PERSIST_LOG_FILE) {
55
- try { mkdirSync(VIBELET_LOG_DIR, { recursive: true }); } catch {}
56
- }
57
-
58
- function write(level: LogLevel, ctx: LogContext, msg: string): void {
59
- if (LEVEL_VALUE[level] < LEVEL_VALUE[minLevel]) return;
60
-
61
- const entry = {
62
- level,
63
- ts: new Date().toISOString(),
64
- msg,
65
- ...ctx,
66
- };
67
-
68
- const line = JSON.stringify(entry);
69
- if (level === 'error' || level === 'warn') {
70
- writeStderrSafe(line + '\n');
71
- } else {
72
- writeStdoutSafe(line + '\n');
73
- }
74
- if (PERSIST_LOG_FILE) {
75
- const logFile = level === 'error' || level === 'warn'
76
- ? DAEMON_STDERR_LOG_PATH
77
- : DAEMON_STDOUT_LOG_PATH;
78
- try { appendFileSync(logFile, line + '\n'); } catch {}
79
- }
80
- }
81
-
82
- export interface Logger {
83
- debug(msg: string): void;
84
- debug(ctx: LogContext, msg: string): void;
85
- info(msg: string): void;
86
- info(ctx: LogContext, msg: string): void;
87
- warn(msg: string): void;
88
- warn(ctx: LogContext, msg: string): void;
89
- error(msg: string): void;
90
- error(ctx: LogContext, msg: string): void;
91
- child(defaultCtx: LogContext): Logger;
92
- }
93
-
94
- function createLogger(defaultCtx: LogContext = {}): Logger {
95
- function log(level: LogLevel, ctxOrMsg: LogContext | string, maybeMsg?: string): void {
96
- if (typeof ctxOrMsg === 'string') {
97
- write(level, defaultCtx, ctxOrMsg);
98
- } else {
99
- write(level, { ...defaultCtx, ...ctxOrMsg }, maybeMsg!);
100
- }
101
- }
102
-
103
- return {
104
- debug: (ctxOrMsg: LogContext | string, maybeMsg?: string) => log('debug', ctxOrMsg, maybeMsg),
105
- info: (ctxOrMsg: LogContext | string, maybeMsg?: string) => log('info', ctxOrMsg, maybeMsg),
106
- warn: (ctxOrMsg: LogContext | string, maybeMsg?: string) => log('warn', ctxOrMsg, maybeMsg),
107
- error: (ctxOrMsg: LogContext | string, maybeMsg?: string) => log('error', ctxOrMsg, maybeMsg),
108
- child: (childCtx: LogContext) => createLogger({ ...defaultCtx, ...childCtx }),
109
- } as Logger;
110
- }
111
-
112
- export const logger = createLogger({ module: 'daemon' });
package/src/metrics.ts DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * Lightweight in-memory metrics for the Vibelet daemon.
3
- *
4
- * Provides counters and timers. No external dependencies.
5
- * Metrics are exposed via the /health endpoint and periodically logged.
6
- *
7
- * Usage:
8
- * import { metrics } from './metrics.js';
9
- * metrics.increment('session.create', { agent: 'claude' });
10
- * const end = metrics.startTimer('driver.spawn');
11
- * // ... work ...
12
- * end(); // records duration in ms
13
- */
14
-
15
- import { logger } from './logger.js';
16
-
17
- interface CounterEntry {
18
- value: number;
19
- labels: Record<string, string>;
20
- }
21
-
22
- interface TimerEntry {
23
- count: number;
24
- totalMs: number;
25
- minMs: number;
26
- maxMs: number;
27
- lastMs: number;
28
- }
29
-
30
- class MetricsRegistry {
31
- private counters = new Map<string, CounterEntry[]>();
32
- private timers = new Map<string, TimerEntry>();
33
- private gauges = new Map<string, number>();
34
- private startTime = Date.now();
35
- private logInterval: ReturnType<typeof setInterval> | null = null;
36
-
37
- /** Increment a counter, optionally with labels. */
38
- increment(name: string, labels: Record<string, string> = {}): void {
39
- if (!this.counters.has(name)) this.counters.set(name, []);
40
- const entries = this.counters.get(name)!;
41
- const key = JSON.stringify(labels);
42
- const existing = entries.find(e => JSON.stringify(e.labels) === key);
43
- if (existing) {
44
- existing.value++;
45
- } else {
46
- entries.push({ value: 1, labels });
47
- }
48
- }
49
-
50
- /** Set a gauge value (for current-state metrics). */
51
- gauge(name: string, value: number): void {
52
- this.gauges.set(name, value);
53
- }
54
-
55
- /** Start a timer, returns a function to call when done. */
56
- startTimer(name: string): () => number {
57
- const start = performance.now();
58
- return () => {
59
- const durationMs = Math.round(performance.now() - start);
60
- const entry = this.timers.get(name) ?? { count: 0, totalMs: 0, minMs: Infinity, maxMs: 0, lastMs: 0 };
61
- entry.count++;
62
- entry.totalMs += durationMs;
63
- entry.minMs = Math.min(entry.minMs, durationMs);
64
- entry.maxMs = Math.max(entry.maxMs, durationMs);
65
- entry.lastMs = durationMs;
66
- this.timers.set(name, entry);
67
- return durationMs;
68
- };
69
- }
70
-
71
- /** Get a snapshot of all metrics. */
72
- snapshot(): MetricsSnapshot {
73
- const counters: Record<string, CounterEntry[]> = {};
74
- for (const [name, entries] of this.counters) {
75
- counters[name] = entries.map(e => ({ ...e }));
76
- }
77
-
78
- const timers: Record<string, TimerEntry> = {};
79
- for (const [name, entry] of this.timers) {
80
- timers[name] = { ...entry, minMs: entry.minMs === Infinity ? 0 : entry.minMs };
81
- }
82
-
83
- const gauges: Record<string, number> = {};
84
- for (const [name, value] of this.gauges) {
85
- gauges[name] = value;
86
- }
87
-
88
- return { uptimeMs: Date.now() - this.startTime, counters, timers, gauges };
89
- }
90
-
91
- /** Start periodic metrics logging (default: every 60s). */
92
- startPeriodicLog(intervalMs = 60_000): void {
93
- if (this.logInterval) return;
94
- this.logInterval = setInterval(() => {
95
- const snap = this.snapshot();
96
- logger.info({ metrics: snap }, 'periodic metrics snapshot');
97
- }, intervalMs);
98
- this.logInterval.unref();
99
- }
100
-
101
- /** Stop periodic logging. */
102
- stopPeriodicLog(): void {
103
- if (this.logInterval) {
104
- clearInterval(this.logInterval);
105
- this.logInterval = null;
106
- }
107
- }
108
- }
109
-
110
- export interface MetricsSnapshot {
111
- uptimeMs: number;
112
- counters: Record<string, CounterEntry[]>;
113
- timers: Record<string, TimerEntry>;
114
- gauges: Record<string, number>;
115
- }
116
-
117
- export const metrics = new MetricsRegistry();
@@ -1,53 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtemp, readFile, rm } from 'fs/promises';
4
- import { tmpdir } from 'os';
5
- import { join } from 'path';
6
- import { PairingStore } from './pairing-store.js';
7
-
8
- test('PairingStore issues stable per-device tokens and validates them', async () => {
9
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-pairing-'));
10
- const pairingsPath = join(dir, 'pairings.json');
11
-
12
- try {
13
- const store = new PairingStore(pairingsPath);
14
- const token = store.issuePairToken('device-1', 'Leyang iPhone');
15
-
16
- assert.equal(store.pairedCount(), 1);
17
- assert.equal(store.validatePairToken('device-1', token), true);
18
- assert.equal(store.validateAnyPairToken(token)?.deviceId, 'device-1');
19
-
20
- const persisted = JSON.parse(await readFile(pairingsPath, 'utf8'));
21
- assert.equal(persisted.length, 1);
22
- assert.equal(persisted[0].deviceId, 'device-1');
23
- assert.notEqual(persisted[0].tokenHash, token);
24
- } finally {
25
- await rm(dir, { recursive: true, force: true });
26
- }
27
- });
28
-
29
- test('PairingStore expires one-time pairing windows and can revoke/reset pairings', async () => {
30
- const dir = await mkdtemp(join(tmpdir(), 'vibelet-pairing-'));
31
- const pairingsPath = join(dir, 'pairings.json');
32
-
33
- try {
34
- const store = new PairingStore(pairingsPath);
35
- const expired = store.openWindow(-1);
36
- const fresh = store.openWindow();
37
- const token = store.issuePairToken('device-2', 'Office iPhone');
38
-
39
- assert.equal(store.consumeWindow(expired.pairNonce), null);
40
- assert.ok(store.consumeWindow(fresh.pairNonce));
41
- assert.equal(store.consumeWindow(fresh.pairNonce), null);
42
-
43
- assert.equal(store.revoke('device-2'), true);
44
- assert.equal(store.validatePairToken('device-2', token, false), false);
45
- assert.equal(store.pairedCount(), 0);
46
-
47
- store.reset();
48
- const persisted = JSON.parse(await readFile(pairingsPath, 'utf8'));
49
- assert.deepEqual(persisted, []);
50
- } finally {
51
- await rm(dir, { recursive: true, force: true });
52
- }
53
- });
@@ -1,154 +0,0 @@
1
- import { createHash, randomBytes, timingSafeEqual } from 'crypto';
2
- import { mkdirSync, readFileSync, writeFileSync } from 'fs';
3
- import { dirname } from 'path';
4
- import { PAIRINGS_PATH } from './paths.js';
5
-
6
- export interface PairingRecord {
7
- deviceId: string;
8
- deviceName: string;
9
- tokenHash: string;
10
- createdAt: string;
11
- lastSeenAt: string;
12
- revokedAt: string | null;
13
- }
14
-
15
- export interface PairingWindow {
16
- pairingId: string;
17
- pairNonce: string;
18
- expiresAt: string;
19
- }
20
-
21
- function base64Url(bytes: number): string {
22
- return randomBytes(bytes).toString('base64url');
23
- }
24
-
25
- function hashToken(token: string): string {
26
- return createHash('sha256').update(token).digest('hex');
27
- }
28
-
29
- function sameHash(left: string, right: string): boolean {
30
- const leftBuffer = Buffer.from(left);
31
- const rightBuffer = Buffer.from(right);
32
- if (leftBuffer.length !== rightBuffer.length) return false;
33
- return timingSafeEqual(leftBuffer, rightBuffer);
34
- }
35
-
36
- function readRecords(pairingsPath: string): PairingRecord[] {
37
- try {
38
- const raw = readFileSync(pairingsPath, 'utf8');
39
- const parsed = JSON.parse(raw);
40
- if (!Array.isArray(parsed)) return [];
41
- return parsed.filter((entry): entry is PairingRecord => (
42
- entry &&
43
- typeof entry.deviceId === 'string' &&
44
- typeof entry.deviceName === 'string' &&
45
- typeof entry.tokenHash === 'string' &&
46
- typeof entry.createdAt === 'string' &&
47
- typeof entry.lastSeenAt === 'string' &&
48
- (typeof entry.revokedAt === 'string' || entry.revokedAt === null)
49
- ));
50
- } catch {
51
- return [];
52
- }
53
- }
54
-
55
- function writeRecords(pairingsPath: string, records: PairingRecord[]): void {
56
- mkdirSync(dirname(pairingsPath), { recursive: true });
57
- writeFileSync(pairingsPath, JSON.stringify(records, null, 2) + '\n', 'utf8');
58
- }
59
-
60
- export class PairingStore {
61
- private records: PairingRecord[];
62
- private windows = new Map<string, PairingWindow>();
63
-
64
- constructor(private readonly pairingsPath = PAIRINGS_PATH) {
65
- this.records = readRecords(pairingsPath);
66
- }
67
-
68
- list(): PairingRecord[] {
69
- return this.records.slice();
70
- }
71
-
72
- pairedCount(): number {
73
- return this.records.filter((record) => !record.revokedAt).length;
74
- }
75
-
76
- openWindow(windowMs = 5 * 60_000): PairingWindow {
77
- const pairingId = `pair_${base64Url(8)}`;
78
- const pairNonce = base64Url(24);
79
- const expiresAt = new Date(Date.now() + windowMs).toISOString();
80
- const window: PairingWindow = { pairingId, pairNonce, expiresAt };
81
- this.windows.set(pairNonce, window);
82
- return window;
83
- }
84
-
85
- consumeWindow(pairNonce: string): PairingWindow | null {
86
- const window = this.windows.get(pairNonce);
87
- if (!window) return null;
88
- this.windows.delete(pairNonce);
89
- if (new Date(window.expiresAt).getTime() < Date.now()) {
90
- return null;
91
- }
92
- return window;
93
- }
94
-
95
- issuePairToken(deviceId: string, deviceName: string): string {
96
- const now = new Date().toISOString();
97
- const pairToken = base64Url(32);
98
- const nextRecord: PairingRecord = {
99
- deviceId,
100
- deviceName,
101
- tokenHash: hashToken(pairToken),
102
- createdAt: now,
103
- lastSeenAt: now,
104
- revokedAt: null,
105
- };
106
-
107
- const existingIndex = this.records.findIndex((record) => record.deviceId === deviceId);
108
- if (existingIndex >= 0) {
109
- this.records[existingIndex] = nextRecord;
110
- } else {
111
- this.records.unshift(nextRecord);
112
- }
113
-
114
- writeRecords(this.pairingsPath, this.records);
115
- return pairToken;
116
- }
117
-
118
- validatePairToken(deviceId: string, token: string, touch = true): boolean {
119
- const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
120
- if (!record) return false;
121
- const tokenDigest = hashToken(token);
122
- if (!sameHash(record.tokenHash, tokenDigest)) return false;
123
- if (touch) {
124
- record.lastSeenAt = new Date().toISOString();
125
- writeRecords(this.pairingsPath, this.records);
126
- }
127
- return true;
128
- }
129
-
130
- validateAnyPairToken(token: string, touch = true): PairingRecord | null {
131
- const tokenDigest = hashToken(token);
132
- const record = this.records.find((entry) => !entry.revokedAt && sameHash(entry.tokenHash, tokenDigest));
133
- if (!record) return null;
134
- if (touch) {
135
- record.lastSeenAt = new Date().toISOString();
136
- writeRecords(this.pairingsPath, this.records);
137
- }
138
- return record;
139
- }
140
-
141
- revoke(deviceId: string): boolean {
142
- const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
143
- if (!record) return false;
144
- record.revokedAt = new Date().toISOString();
145
- writeRecords(this.pairingsPath, this.records);
146
- return true;
147
- }
148
-
149
- reset(): void {
150
- this.records = [];
151
- this.windows.clear();
152
- writeRecords(this.pairingsPath, this.records);
153
- }
154
- }
package/src/paths.ts DELETED
@@ -1,19 +0,0 @@
1
- import { homedir } from 'os';
2
- import { join } from 'path';
3
-
4
- export const VIBELET_DIR = join(homedir(), '.vibelet');
5
- export const VIBELET_LOG_DIR = join(VIBELET_DIR, 'logs');
6
- export const VIBELET_DATA_DIR = join(VIBELET_DIR, 'data');
7
- export const VIBELET_RUNTIME_DIR = join(VIBELET_DIR, 'runtime');
8
- export const VIBELET_RUNTIME_CURRENT_DIR = join(VIBELET_RUNTIME_DIR, 'current');
9
-
10
- export const IDENTITY_PATH = join(VIBELET_DIR, 'identity.json');
11
- export const PAIRINGS_PATH = join(VIBELET_DIR, 'pairings.json');
12
- export const PUSH_SUBSCRIPTIONS_PATH = join(VIBELET_DATA_DIR, 'push-subscriptions.json');
13
- export const SESSION_STORE_PATH = join(VIBELET_DATA_DIR, 'sessions.json');
14
- export const AUDIT_PATH = join(VIBELET_DATA_DIR, 'audit.jsonl');
15
- export const VIBELET_UPLOADS_DIR = join(VIBELET_DATA_DIR, 'uploads');
16
- export const DAEMON_STDOUT_LOG_PATH = join(VIBELET_LOG_DIR, 'daemon.stdout.log');
17
- export const DAEMON_STDERR_LOG_PATH = join(VIBELET_LOG_DIR, 'daemon.stderr.log');
18
- export const UPDATE_CHECK_PATH = join(VIBELET_DIR, 'update-check.json');
19
- export const PAIRING_QR_PNG_PATH = join(VIBELET_DIR, 'pairing-qr.png');
@@ -1,164 +0,0 @@
1
- /**
2
- * Compare two perf baselines and report regressions.
3
- *
4
- * Usage:
5
- * tsx src/perf-compare.ts # compare perf-baseline.json vs perf-baseline.prev.json
6
- * tsx src/perf-compare.ts <current> <previous> # compare two specific files
7
- *
8
- * Exit code:
9
- * 0 = no regressions
10
- * 1 = regressions detected (>20% slower)
11
- */
12
-
13
- import { readFileSync, existsSync } from 'fs';
14
-
15
- interface PerfEntry {
16
- test: string;
17
- suite: string;
18
- durationMs: number;
19
- passed: boolean;
20
- metrics: Record<string, number>;
21
- }
22
-
23
- interface PerfBaseline {
24
- timestamp: string;
25
- nodeVersion: string;
26
- platform: string;
27
- arch: string;
28
- results: PerfEntry[];
29
- summary: {
30
- total: number;
31
- passed: number;
32
- failed: number;
33
- totalDurationMs: number;
34
- };
35
- }
36
-
37
- const REGRESSION_THRESHOLD = 0.20; // 20% slower = regression
38
-
39
- function loadBaseline(path: string): PerfBaseline | null {
40
- if (!existsSync(path)) return null;
41
- return JSON.parse(readFileSync(path, 'utf-8'));
42
- }
43
-
44
- function formatDelta(current: number, previous: number): string {
45
- if (previous === 0) return 'N/A';
46
- const pct = ((current - previous) / previous) * 100;
47
- const sign = pct >= 0 ? '+' : '';
48
- return `${sign}${pct.toFixed(1)}%`;
49
- }
50
-
51
- function main() {
52
- const args = process.argv.slice(2);
53
- const currentPath = args[0] || 'perf-baseline.json';
54
- const previousPath = args[1] || 'perf-baseline.prev.json';
55
-
56
- const current = loadBaseline(currentPath);
57
- if (!current) {
58
- console.error(`Current baseline not found: ${currentPath}`);
59
- console.error('Run "pnpm test:perf:baseline" to generate it.');
60
- process.exit(1);
61
- }
62
-
63
- const previous = loadBaseline(previousPath);
64
- if (!previous) {
65
- console.log(`No previous baseline found at ${previousPath}.`);
66
- console.log(`Current baseline: ${current.summary.total} tests, ${current.summary.totalDurationMs.toFixed(0)}ms total`);
67
- console.log('To compare, save the current baseline as the previous:');
68
- console.log(` cp ${currentPath} ${previousPath}`);
69
- process.exit(0);
70
- }
71
-
72
- console.log('Performance Comparison');
73
- console.log('='.repeat(90));
74
- console.log(`Previous: ${previous.timestamp} (Node ${previous.nodeVersion})`);
75
- console.log(`Current: ${current.timestamp} (Node ${current.nodeVersion})`);
76
- console.log('');
77
-
78
- // Build lookup for previous results
79
- const prevMap = new Map<string, PerfEntry>();
80
- for (const entry of previous.results) {
81
- prevMap.set(`${entry.suite}::${entry.test}`, entry);
82
- }
83
-
84
- const regressions: string[] = [];
85
- const improvements: string[] = [];
86
- const newTests: string[] = [];
87
-
88
- console.log(`${'Test'.padEnd(55)} ${'Prev'.padStart(10)} ${'Curr'.padStart(10)} ${'Delta'.padStart(10)}`);
89
- console.log('-'.repeat(90));
90
-
91
- for (const entry of current.results) {
92
- const key = `${entry.suite}::${entry.test}`;
93
- const prev = prevMap.get(key);
94
-
95
- // Use the most representative metric for comparison
96
- const currentMs = entry.metrics.totalMs ?? entry.metrics.coldMs ?? entry.durationMs;
97
-
98
- if (!prev) {
99
- newTests.push(entry.test);
100
- const label = truncate(`${entry.suite} > ${entry.test}`, 55);
101
- console.log(`${label.padEnd(55)} ${'new'.padStart(10)} ${fmt(currentMs).padStart(10)} ${''.padStart(10)}`);
102
- continue;
103
- }
104
-
105
- const prevMs = prev.metrics.totalMs ?? prev.metrics.coldMs ?? prev.durationMs;
106
- const delta = formatDelta(currentMs, prevMs);
107
- const ratio = prevMs > 0 ? (currentMs - prevMs) / prevMs : 0;
108
-
109
- const label = truncate(`${entry.suite} > ${entry.test}`, 55);
110
- const marker = ratio > REGRESSION_THRESHOLD ? ' ⚠️' : ratio < -REGRESSION_THRESHOLD ? ' ✨' : '';
111
-
112
- console.log(`${label.padEnd(55)} ${fmt(prevMs).padStart(10)} ${fmt(currentMs).padStart(10)} ${delta.padStart(10)}${marker}`);
113
-
114
- if (ratio > REGRESSION_THRESHOLD) {
115
- regressions.push(`${entry.suite} > ${entry.test}: ${fmt(prevMs)} → ${fmt(currentMs)} (${delta})`);
116
- } else if (ratio < -REGRESSION_THRESHOLD) {
117
- improvements.push(`${entry.suite} > ${entry.test}: ${fmt(prevMs)} → ${fmt(currentMs)} (${delta})`);
118
- }
119
-
120
- prevMap.delete(key);
121
- }
122
-
123
- // Removed tests
124
- const removedTests = [...prevMap.keys()];
125
-
126
- console.log('');
127
- console.log('='.repeat(90));
128
- console.log(`Summary: ${current.summary.total} tests | ${fmt(current.summary.totalDurationMs)} total`);
129
-
130
- if (improvements.length > 0) {
131
- console.log(`\n✨ Improvements (>${REGRESSION_THRESHOLD * 100}% faster): ${improvements.length}`);
132
- for (const imp of improvements) console.log(` ${imp}`);
133
- }
134
-
135
- if (newTests.length > 0) {
136
- console.log(`\n🆕 New tests: ${newTests.length}`);
137
- for (const t of newTests) console.log(` ${t}`);
138
- }
139
-
140
- if (removedTests.length > 0) {
141
- console.log(`\n🗑️ Removed tests: ${removedTests.length}`);
142
- for (const t of removedTests) console.log(` ${t}`);
143
- }
144
-
145
- if (regressions.length > 0) {
146
- console.log(`\n⚠️ Regressions (>${REGRESSION_THRESHOLD * 100}% slower): ${regressions.length}`);
147
- for (const reg of regressions) console.log(` ${reg}`);
148
- process.exit(1);
149
- } else {
150
- console.log('\n✅ No performance regressions detected.');
151
- }
152
- }
153
-
154
- function fmt(ms: number): string {
155
- if (ms < 1) return `${(ms * 1000).toFixed(0)}µs`;
156
- if (ms < 1000) return `${ms.toFixed(2)}ms`;
157
- return `${(ms / 1000).toFixed(2)}s`;
158
- }
159
-
160
- function truncate(str: string, maxLen: number): string {
161
- return str.length <= maxLen ? str : str.slice(0, maxLen - 3) + '...';
162
- }
163
-
164
- main();
@@ -1,45 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { collectPortOccupants, parsePidList } from './port-conflict.js';
4
-
5
- test('parsePidList keeps only valid positive numeric pids', () => {
6
- assert.deepEqual(
7
- parsePidList('123\nabc\n\n0\n456\n123\n'),
8
- [123, 456],
9
- );
10
- });
11
-
12
- test('collectPortOccupants includes command details when available', () => {
13
- const occupants = collectPortOccupants(9876, (file, args) => {
14
- if (file === 'lsof') {
15
- assert.deepEqual(args, ['-ti', 'tcp:9876']);
16
- return '123\n456';
17
- }
18
- if (file === 'ps' && args[1] === '123') {
19
- return 'node dist/index.js';
20
- }
21
- if (file === 'ps' && args[1] === '456') {
22
- return 'python -m http.server';
23
- }
24
- throw new Error(`Unexpected command: ${file} ${args.join(' ')}`);
25
- });
26
-
27
- assert.deepEqual(occupants, [
28
- { pid: 123, command: 'node dist/index.js' },
29
- { pid: 456, command: 'python -m http.server' },
30
- ]);
31
- });
32
-
33
- test('collectPortOccupants falls back to pid-only entries when ps lookup fails', () => {
34
- const occupants = collectPortOccupants(9876, (file, args) => {
35
- if (file === 'lsof') {
36
- return '123';
37
- }
38
- if (file === 'ps' && args[1] === '123') {
39
- throw new Error('ps failed');
40
- }
41
- throw new Error(`Unexpected command: ${file} ${args.join(' ')}`);
42
- });
43
-
44
- assert.deepEqual(occupants, [{ pid: 123 }]);
45
- });