vibecheck-ai 2.0.1 → 5.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 (456) hide show
  1. package/bin/.generated +25 -0
  2. package/bin/_deprecations.js +463 -0
  3. package/bin/_router.js +46 -0
  4. package/bin/cli-hygiene.js +241 -0
  5. package/bin/dev/run-v2-torture.js +30 -0
  6. package/bin/registry.js +656 -0
  7. package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
  8. package/bin/runners/ENHANCEMENT_GUIDE.md +121 -0
  9. package/bin/runners/REPORT_AUDIT.md +64 -0
  10. package/bin/runners/cli-utils.js +1070 -0
  11. package/bin/runners/context/ai-task-decomposer.js +337 -0
  12. package/bin/runners/context/analyzer.js +513 -0
  13. package/bin/runners/context/api-contracts.js +427 -0
  14. package/bin/runners/context/context-diff.js +342 -0
  15. package/bin/runners/context/context-pruner.js +291 -0
  16. package/bin/runners/context/dependency-graph.js +414 -0
  17. package/bin/runners/context/generators/claude.js +107 -0
  18. package/bin/runners/context/generators/codex.js +108 -0
  19. package/bin/runners/context/generators/copilot.js +119 -0
  20. package/bin/runners/context/generators/cursor-enhanced.js +2525 -0
  21. package/bin/runners/context/generators/cursor.js +514 -0
  22. package/bin/runners/context/generators/mcp.js +169 -0
  23. package/bin/runners/context/generators/windsurf.js +180 -0
  24. package/bin/runners/context/git-context.js +304 -0
  25. package/bin/runners/context/index.js +1110 -0
  26. package/bin/runners/context/insights.js +173 -0
  27. package/bin/runners/context/mcp-server/generate-rules.js +337 -0
  28. package/bin/runners/context/mcp-server/index.js +1176 -0
  29. package/bin/runners/context/mcp-server/package.json +24 -0
  30. package/bin/runners/context/memory.js +200 -0
  31. package/bin/runners/context/monorepo.js +215 -0
  32. package/bin/runners/context/multi-repo-federation.js +404 -0
  33. package/bin/runners/context/patterns.js +253 -0
  34. package/bin/runners/context/proof-context.js +1264 -0
  35. package/bin/runners/context/security-scanner.js +541 -0
  36. package/bin/runners/context/semantic-search.js +350 -0
  37. package/bin/runners/context/shared.js +264 -0
  38. package/bin/runners/context/team-conventions.js +336 -0
  39. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
  40. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  41. package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
  42. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  43. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  44. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  45. package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
  46. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  47. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  48. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  49. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  50. package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
  51. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
  52. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
  53. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
  54. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
  55. package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
  56. package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
  57. package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
  58. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
  59. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  60. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  61. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
  62. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  63. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
  64. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  65. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  66. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  67. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  68. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  69. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  70. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  71. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  72. package/bin/runners/lib/agent-firewall/index.js +200 -0
  73. package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
  74. package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
  75. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -0
  76. package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
  77. package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
  78. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
  79. package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
  80. package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
  81. package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
  82. package/bin/runners/lib/agent-firewall/interceptor/base.js +308 -0
  83. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  84. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  85. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  86. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  87. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  88. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  89. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  90. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  91. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  92. package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
  93. package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
  94. package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
  95. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  96. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  97. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +79 -0
  98. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +227 -0
  99. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +191 -0
  100. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  101. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  102. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  103. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  104. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  105. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  106. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  107. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  108. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  109. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  110. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  111. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  112. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  113. package/bin/runners/lib/agent-firewall/risk/thresholds.js +322 -0
  114. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  115. package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
  116. package/bin/runners/lib/agent-firewall/session/index.js +26 -0
  117. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  118. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  119. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  120. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  121. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  122. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  123. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  124. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  125. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  126. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  127. package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
  128. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  129. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  130. package/bin/runners/lib/ai-bridge.js +416 -0
  131. package/bin/runners/lib/analysis-core.js +309 -0
  132. package/bin/runners/lib/analyzers.js +2500 -0
  133. package/bin/runners/lib/api-client.js +269 -0
  134. package/bin/runners/lib/approve-output.js +235 -0
  135. package/bin/runners/lib/artifact-envelope.js +540 -0
  136. package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
  137. package/bin/runners/lib/audit-bridge.js +391 -0
  138. package/bin/runners/lib/auth-shared.js +977 -0
  139. package/bin/runners/lib/auth-truth.js +193 -0
  140. package/bin/runners/lib/auth.js +215 -0
  141. package/bin/runners/lib/authority-badge.js +425 -0
  142. package/bin/runners/lib/backup.js +62 -0
  143. package/bin/runners/lib/billing.js +107 -0
  144. package/bin/runners/lib/checkpoint.js +941 -0
  145. package/bin/runners/lib/claims.js +118 -0
  146. package/bin/runners/lib/classify-output.js +204 -0
  147. package/bin/runners/lib/cleanup/engine.js +571 -0
  148. package/bin/runners/lib/cleanup/index.js +53 -0
  149. package/bin/runners/lib/cleanup/output.js +375 -0
  150. package/bin/runners/lib/cleanup/rules.js +1060 -0
  151. package/bin/runners/lib/cli-output.js +400 -0
  152. package/bin/runners/lib/cli-ui.js +540 -0
  153. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  154. package/bin/runners/lib/compliance-bridge.js +165 -0
  155. package/bin/runners/lib/contracts/auth-contract.js +202 -0
  156. package/bin/runners/lib/contracts/env-contract.js +181 -0
  157. package/bin/runners/lib/contracts/external-contract.js +206 -0
  158. package/bin/runners/lib/contracts/guard.js +168 -0
  159. package/bin/runners/lib/contracts/index.js +89 -0
  160. package/bin/runners/lib/contracts/plan-validator.js +311 -0
  161. package/bin/runners/lib/contracts/route-contract.js +199 -0
  162. package/bin/runners/lib/contracts.js +804 -0
  163. package/bin/runners/lib/default-config.js +127 -0
  164. package/bin/runners/lib/detect.js +89 -0
  165. package/bin/runners/lib/detectors-v2.js +622 -0
  166. package/bin/runners/lib/doctor/autofix.js +254 -0
  167. package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
  168. package/bin/runners/lib/doctor/failure-signatures.js +526 -0
  169. package/bin/runners/lib/doctor/fix-script.js +336 -0
  170. package/bin/runners/lib/doctor/index.js +37 -0
  171. package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
  172. package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
  173. package/bin/runners/lib/doctor/modules/index.js +105 -0
  174. package/bin/runners/lib/doctor/modules/network.js +250 -0
  175. package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
  176. package/bin/runners/lib/doctor/modules/project.js +312 -0
  177. package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
  178. package/bin/runners/lib/doctor/modules/runtime.js +224 -0
  179. package/bin/runners/lib/doctor/modules/security.js +350 -0
  180. package/bin/runners/lib/doctor/modules/system.js +213 -0
  181. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
  182. package/bin/runners/lib/doctor/reporter.js +262 -0
  183. package/bin/runners/lib/doctor/safe-repair.js +384 -0
  184. package/bin/runners/lib/doctor/service.js +262 -0
  185. package/bin/runners/lib/doctor/types.js +113 -0
  186. package/bin/runners/lib/doctor/ui.js +263 -0
  187. package/bin/runners/lib/doctor-enhanced.js +233 -0
  188. package/bin/runners/lib/doctor-output.js +226 -0
  189. package/bin/runners/lib/doctor-v2.js +608 -0
  190. package/bin/runners/lib/drift.js +425 -0
  191. package/bin/runners/lib/enforcement.js +72 -0
  192. package/bin/runners/lib/engine/ast-cache.js +210 -0
  193. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  194. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  195. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  196. package/bin/runners/lib/engine/env-extractor.js +207 -0
  197. package/bin/runners/lib/engine/express-extractor.js +208 -0
  198. package/bin/runners/lib/engine/extractors.js +849 -0
  199. package/bin/runners/lib/engine/index.js +207 -0
  200. package/bin/runners/lib/engine/repo-index.js +514 -0
  201. package/bin/runners/lib/engine/types.js +124 -0
  202. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  203. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  204. package/bin/runners/lib/engines/ast-cache.js +99 -0
  205. package/bin/runners/lib/engines/attack-detector.js +1192 -0
  206. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  207. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  208. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  209. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  210. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  211. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  212. package/bin/runners/lib/engines/file-filter.js +131 -0
  213. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  214. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  215. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  216. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  217. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  218. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  219. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  220. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  221. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  222. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  223. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  224. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  225. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  226. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  227. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  228. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  229. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  230. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  231. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  232. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  233. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  234. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  235. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  236. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  237. package/bin/runners/lib/enterprise-detect.js +603 -0
  238. package/bin/runners/lib/enterprise-init.js +942 -0
  239. package/bin/runners/lib/entitlements-v2.js +265 -0
  240. package/bin/runners/lib/entitlements.generated.js +0 -0
  241. package/bin/runners/lib/entitlements.js +340 -0
  242. package/bin/runners/lib/env-resolver.js +417 -0
  243. package/bin/runners/lib/env-template.js +66 -0
  244. package/bin/runners/lib/env.js +189 -0
  245. package/bin/runners/lib/error-handler.js +368 -0
  246. package/bin/runners/lib/error-messages.js +289 -0
  247. package/bin/runners/lib/evidence-pack.js +684 -0
  248. package/bin/runners/lib/exit-codes.js +275 -0
  249. package/bin/runners/lib/extractors/client-calls.js +990 -0
  250. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
  251. package/bin/runners/lib/extractors/fastify-routes.js +426 -0
  252. package/bin/runners/lib/extractors/index.js +363 -0
  253. package/bin/runners/lib/extractors/next-routes.js +524 -0
  254. package/bin/runners/lib/extractors/proof-graph.js +431 -0
  255. package/bin/runners/lib/extractors/route-matcher.js +451 -0
  256. package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
  257. package/bin/runners/lib/extractors/ui-bindings.js +547 -0
  258. package/bin/runners/lib/finding-id.js +69 -0
  259. package/bin/runners/lib/finding-sorter.js +89 -0
  260. package/bin/runners/lib/findings-schema.js +281 -0
  261. package/bin/runners/lib/fingerprint.js +377 -0
  262. package/bin/runners/lib/firewall-prompt.js +50 -0
  263. package/bin/runners/lib/fix-output.js +228 -0
  264. package/bin/runners/lib/global-flags.js +250 -0
  265. package/bin/runners/lib/graph/graph-builder.js +265 -0
  266. package/bin/runners/lib/graph/html-renderer.js +413 -0
  267. package/bin/runners/lib/graph/index.js +32 -0
  268. package/bin/runners/lib/graph/runtime-collector.js +215 -0
  269. package/bin/runners/lib/graph/static-extractor.js +518 -0
  270. package/bin/runners/lib/help-formatter.js +413 -0
  271. package/bin/runners/lib/html-proof-report.js +913 -0
  272. package/bin/runners/lib/html-report.js +650 -0
  273. package/bin/runners/lib/init-wizard.js +601 -0
  274. package/bin/runners/lib/interactive-menu.js +1496 -0
  275. package/bin/runners/lib/json-output.js +76 -0
  276. package/bin/runners/lib/llm.js +75 -0
  277. package/bin/runners/lib/logger.js +38 -0
  278. package/bin/runners/lib/meter.js +61 -0
  279. package/bin/runners/lib/missions/briefing.js +427 -0
  280. package/bin/runners/lib/missions/checkpoint.js +753 -0
  281. package/bin/runners/lib/missions/evidence.js +126 -0
  282. package/bin/runners/lib/missions/hardening.js +851 -0
  283. package/bin/runners/lib/missions/plan.js +648 -0
  284. package/bin/runners/lib/missions/safety-gates.js +645 -0
  285. package/bin/runners/lib/missions/schema.js +478 -0
  286. package/bin/runners/lib/missions/templates.js +317 -0
  287. package/bin/runners/lib/next-action.js +560 -0
  288. package/bin/runners/lib/packs/bundle.js +675 -0
  289. package/bin/runners/lib/packs/evidence-pack.js +671 -0
  290. package/bin/runners/lib/packs/pack-factory.js +837 -0
  291. package/bin/runners/lib/packs/permissions-pack.js +686 -0
  292. package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
  293. package/bin/runners/lib/patch.js +40 -0
  294. package/bin/runners/lib/permissions/auth-model.js +213 -0
  295. package/bin/runners/lib/permissions/idor-prover.js +205 -0
  296. package/bin/runners/lib/permissions/index.js +45 -0
  297. package/bin/runners/lib/permissions/matrix-builder.js +198 -0
  298. package/bin/runners/lib/pkgjson.js +28 -0
  299. package/bin/runners/lib/policy.js +295 -0
  300. package/bin/runners/lib/polish/accessibility.js +62 -0
  301. package/bin/runners/lib/polish/analyzer.js +93 -0
  302. package/bin/runners/lib/polish/backend.js +87 -0
  303. package/bin/runners/lib/polish/configuration.js +83 -0
  304. package/bin/runners/lib/polish/documentation.js +83 -0
  305. package/bin/runners/lib/polish/frontend.js +817 -0
  306. package/bin/runners/lib/polish/index.js +27 -0
  307. package/bin/runners/lib/polish/infrastructure.js +80 -0
  308. package/bin/runners/lib/polish/internationalization.js +85 -0
  309. package/bin/runners/lib/polish/libraries.js +180 -0
  310. package/bin/runners/lib/polish/observability.js +75 -0
  311. package/bin/runners/lib/polish/performance.js +64 -0
  312. package/bin/runners/lib/polish/privacy.js +110 -0
  313. package/bin/runners/lib/polish/resilience.js +92 -0
  314. package/bin/runners/lib/polish/security.js +78 -0
  315. package/bin/runners/lib/polish/seo.js +71 -0
  316. package/bin/runners/lib/polish/styles.js +62 -0
  317. package/bin/runners/lib/polish/utils.js +104 -0
  318. package/bin/runners/lib/preflight.js +142 -0
  319. package/bin/runners/lib/prerequisites.js +149 -0
  320. package/bin/runners/lib/prove-output.js +220 -0
  321. package/bin/runners/lib/reality/correlation-detectors.js +359 -0
  322. package/bin/runners/lib/reality/index.js +318 -0
  323. package/bin/runners/lib/reality/request-hashing.js +416 -0
  324. package/bin/runners/lib/reality/request-mapper.js +453 -0
  325. package/bin/runners/lib/reality/safety-rails.js +463 -0
  326. package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
  327. package/bin/runners/lib/reality/toast-detector.js +393 -0
  328. package/bin/runners/lib/reality-findings.js +84 -0
  329. package/bin/runners/lib/reality-output.js +231 -0
  330. package/bin/runners/lib/receipts.js +179 -0
  331. package/bin/runners/lib/redact.js +29 -0
  332. package/bin/runners/lib/replay/capsule-manager.js +154 -0
  333. package/bin/runners/lib/replay/index.js +263 -0
  334. package/bin/runners/lib/replay/player.js +348 -0
  335. package/bin/runners/lib/replay/recorder.js +331 -0
  336. package/bin/runners/lib/report-engine.js +626 -0
  337. package/bin/runners/lib/report-html.js +1233 -0
  338. package/bin/runners/lib/report-output.js +366 -0
  339. package/bin/runners/lib/report-templates.js +967 -0
  340. package/bin/runners/lib/report.js +135 -0
  341. package/bin/runners/lib/route-detection.js +1209 -0
  342. package/bin/runners/lib/route-truth.js +1322 -0
  343. package/bin/runners/lib/safelist/index.js +96 -0
  344. package/bin/runners/lib/safelist/integration.js +334 -0
  345. package/bin/runners/lib/safelist/matcher.js +696 -0
  346. package/bin/runners/lib/safelist/schema.js +948 -0
  347. package/bin/runners/lib/safelist/store.js +438 -0
  348. package/bin/runners/lib/sandbox/index.js +59 -0
  349. package/bin/runners/lib/sandbox/proof-chain.js +399 -0
  350. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -0
  351. package/bin/runners/lib/sandbox/worktree.js +174 -0
  352. package/bin/runners/lib/scan-cache.js +330 -0
  353. package/bin/runners/lib/scan-output-schema.js +344 -0
  354. package/bin/runners/lib/scan-output.js +631 -0
  355. package/bin/runners/lib/scan-runner.js +135 -0
  356. package/bin/runners/lib/schema-validator.js +350 -0
  357. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  358. package/bin/runners/lib/schemas/contracts.schema.json +160 -0
  359. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  360. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  361. package/bin/runners/lib/schemas/finding.schema.json +100 -0
  362. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
  363. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
  364. package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
  365. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  366. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  367. package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
  368. package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
  369. package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
  370. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
  371. package/bin/runners/lib/schemas/validator.js +465 -0
  372. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  373. package/bin/runners/lib/score-history.js +282 -0
  374. package/bin/runners/lib/security-bridge.js +249 -0
  375. package/bin/runners/lib/server-usage.js +513 -0
  376. package/bin/runners/lib/share-pack.js +239 -0
  377. package/bin/runners/lib/ship-gate.js +832 -0
  378. package/bin/runners/lib/ship-manifest.js +1153 -0
  379. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  380. package/bin/runners/lib/ship-output.js +1128 -0
  381. package/bin/runners/lib/snippets.js +67 -0
  382. package/bin/runners/lib/status-output.js +340 -0
  383. package/bin/runners/lib/terminal-ui.js +356 -0
  384. package/bin/runners/lib/truth.js +1691 -0
  385. package/bin/runners/lib/ui.js +562 -0
  386. package/bin/runners/lib/unified-cli-output.js +947 -0
  387. package/bin/runners/lib/unified-output.js +197 -0
  388. package/bin/runners/lib/upsell.js +410 -0
  389. package/bin/runners/lib/usage.js +153 -0
  390. package/bin/runners/lib/validate-patch.js +156 -0
  391. package/bin/runners/lib/verdict-engine.js +628 -0
  392. package/bin/runners/lib/verification.js +345 -0
  393. package/bin/runners/lib/why-tree.js +650 -0
  394. package/bin/runners/reality/engine.js +917 -0
  395. package/bin/runners/reality/flows.js +122 -0
  396. package/bin/runners/reality/report.js +378 -0
  397. package/bin/runners/reality/session.js +193 -0
  398. package/bin/runners/runAIAgent.js +229 -0
  399. package/bin/runners/runAgent.d.ts +5 -0
  400. package/bin/runners/runAgent.js +161 -0
  401. package/bin/runners/runAllowlist.js +418 -0
  402. package/bin/runners/runApprove.js +320 -0
  403. package/bin/runners/runAudit.js +692 -0
  404. package/bin/runners/runAuth.js +731 -0
  405. package/bin/runners/runCI.js +353 -0
  406. package/bin/runners/runCheckpoint.js +530 -0
  407. package/bin/runners/runClassify.js +928 -0
  408. package/bin/runners/runCleanup.js +343 -0
  409. package/bin/runners/runContext.d.ts +4 -0
  410. package/bin/runners/runContext.js +175 -0
  411. package/bin/runners/runDoctor.js +877 -0
  412. package/bin/runners/runEvidencePack.js +362 -0
  413. package/bin/runners/runFirewall.d.ts +5 -0
  414. package/bin/runners/runFirewall.js +134 -0
  415. package/bin/runners/runFirewallHook.d.ts +5 -0
  416. package/bin/runners/runFirewallHook.js +56 -0
  417. package/bin/runners/runFix.js +1355 -0
  418. package/bin/runners/runForge.js +451 -0
  419. package/bin/runners/runGuard.js +262 -0
  420. package/bin/runners/runInit.js +1927 -0
  421. package/bin/runners/runIntent.js +906 -0
  422. package/bin/runners/runKickoff.js +878 -0
  423. package/bin/runners/runLabs.js +424 -0
  424. package/bin/runners/runLaunch.js +2000 -0
  425. package/bin/runners/runLink.js +785 -0
  426. package/bin/runners/runMcp.js +1875 -0
  427. package/bin/runners/runPacks.js +2089 -0
  428. package/bin/runners/runPolish.d.ts +4 -0
  429. package/bin/runners/runPolish.js +390 -0
  430. package/bin/runners/runPromptFirewall.js +211 -0
  431. package/bin/runners/runProve.js +1411 -0
  432. package/bin/runners/runQuickstart.js +531 -0
  433. package/bin/runners/runReality.js +2260 -0
  434. package/bin/runners/runReport.js +726 -0
  435. package/bin/runners/runRuntime.js +110 -0
  436. package/bin/runners/runSafelist.js +1190 -0
  437. package/bin/runners/runScan.js +688 -0
  438. package/bin/runners/runShield.js +1282 -0
  439. package/bin/runners/runShip.js +1660 -0
  440. package/bin/runners/runTruth.d.ts +5 -0
  441. package/bin/runners/runTruth.js +101 -0
  442. package/bin/runners/runValidate.js +179 -0
  443. package/bin/runners/runWatch.js +478 -0
  444. package/bin/runners/utils.js +360 -0
  445. package/bin/scan.js +617 -0
  446. package/bin/vibecheck.js +1617 -0
  447. package/dist/guardrail/index.d.ts +2405 -0
  448. package/dist/guardrail/index.js +9747 -0
  449. package/dist/guardrail/index.js.map +1 -0
  450. package/dist/scanner/index.d.ts +282 -0
  451. package/dist/scanner/index.js +3395 -0
  452. package/dist/scanner/index.js.map +1 -0
  453. package/package.json +123 -104
  454. package/README.md +0 -491
  455. package/dist/index.js +0 -99711
  456. package/dist/index.js.map +0 -1
@@ -0,0 +1,977 @@
1
+ /**
2
+ * Shared Auth Module Bridge (JavaScript)
3
+ *
4
+ * Provides shared auth functionality for CLI commands.
5
+ * This bridges the TypeScript shared/auth module for JS consumers.
6
+ *
7
+ * @module lib/auth-shared
8
+ */
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const os = require("os");
13
+ const crypto = require("crypto");
14
+
15
+ // ═══════════════════════════════════════════════════════════════════════════════
16
+ // PATHS
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+
19
+ const VIBECHECK_DIR = ".vibecheck";
20
+ const AUTH_FILE = "auth.json";
21
+ const CACHE_FILE = "entitlements-cache.json";
22
+ const AUDIT_FILE = "auth-audit.log";
23
+ const ACTIVITY_FILE = ".auth-activity.json";
24
+ const RATE_LIMIT_FILE = ".rate-limit.json";
25
+
26
+ // Rate limit state in memory
27
+ let rateLimitState = { retryAfter: null, timestamp: null };
28
+
29
+ // Constants
30
+ const MAX_RETRIES = 3;
31
+ const BASE_RETRY_DELAY_MS = 1000;
32
+ const FAILED_ATTEMPT_THRESHOLD = 5;
33
+ const LOCKOUT_DURATION_MS = 15 * 60 * 1000; // 15 minutes
34
+ const MAX_AUDIT_SIZE = 1024 * 1024; // 1MB
35
+
36
+ /**
37
+ * Get vibecheck config directory.
38
+ * @returns {string}
39
+ */
40
+ function getConfigDir() {
41
+ return path.join(os.homedir(), VIBECHECK_DIR);
42
+ }
43
+
44
+ /**
45
+ * Get auth file path.
46
+ * @returns {string}
47
+ */
48
+ function getAuthPath() {
49
+ return path.join(getConfigDir(), AUTH_FILE);
50
+ }
51
+
52
+ /**
53
+ * Get cache file path.
54
+ * @returns {string}
55
+ */
56
+ function getCachePath() {
57
+ return path.join(getConfigDir(), CACHE_FILE);
58
+ }
59
+
60
+ /**
61
+ * Get legacy config path (for migration).
62
+ * @returns {string}
63
+ */
64
+ function getLegacyConfigPath() {
65
+ const home = os.homedir();
66
+ if (process.platform === "win32") {
67
+ return path.join(
68
+ process.env.APPDATA || path.join(home, "AppData", "Roaming"),
69
+ "vibecheck",
70
+ "config.json"
71
+ );
72
+ }
73
+ return path.join(home, ".config", "vibecheck", "config.json");
74
+ }
75
+
76
+ // ═══════════════════════════════════════════════════════════════════════════════
77
+ // REDACTION
78
+ // ═══════════════════════════════════════════════════════════════════════════════
79
+
80
+ /**
81
+ * Mask API key for secure display.
82
+ * @param {string} key
83
+ * @returns {string}
84
+ */
85
+ function redactApiKey(key) {
86
+ if (!key || typeof key !== "string" || key.length < 8) {
87
+ return "****";
88
+ }
89
+
90
+ // New format: grl_ prefix
91
+ if (key.startsWith("grl_")) {
92
+ return `grl_${"*".repeat(8)}${key.slice(-4)}`;
93
+ }
94
+
95
+ // Legacy format: gr_<tier>_ prefix
96
+ const legacyMatch = key.match(/^(gr_[a-z]+_)/);
97
+ if (legacyMatch) {
98
+ return `${legacyMatch[1]}${"*".repeat(8)}${key.slice(-4)}`;
99
+ }
100
+
101
+ // Unknown format
102
+ if (key.length >= 12) {
103
+ return `${key.slice(0, 3)}****${key.slice(-4)}`;
104
+ }
105
+
106
+ return "****";
107
+ }
108
+
109
+ /**
110
+ * Redact API keys in output text.
111
+ * @param {string} text
112
+ * @returns {string}
113
+ */
114
+ function redactOutput(text) {
115
+ if (!text || typeof text !== "string") return text;
116
+
117
+ return text
118
+ .replace(/grl_[a-zA-Z0-9]{10,}/g, (m) => redactApiKey(m))
119
+ .replace(/gr_[a-z]+_[a-zA-Z0-9]{10,}/g, (m) => redactApiKey(m))
120
+ .replace(
121
+ /VIBECHECK_API_KEY\s*=\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
122
+ (_, k) => `VIBECHECK_API_KEY=${redactApiKey(k)}`
123
+ );
124
+ }
125
+
126
+ // ═══════════════════════════════════════════════════════════════════════════════
127
+ // VALIDATION
128
+ // ═══════════════════════════════════════════════════════════════════════════════
129
+
130
+ /**
131
+ * Validate API key format.
132
+ * @param {string} key
133
+ * @returns {{ valid: boolean, error?: string, format?: string }}
134
+ */
135
+ function validateKeyFormat(key) {
136
+ if (!key || typeof key !== "string") {
137
+ return { valid: false, error: "API key is required" };
138
+ }
139
+
140
+ const trimmed = key.trim();
141
+
142
+ if (trimmed.length < 20) {
143
+ return { valid: false, error: "API key is too short (minimum 20 characters)" };
144
+ }
145
+
146
+ // New format: grl_ prefix
147
+ if (trimmed.startsWith("grl_")) {
148
+ if (!/^grl_[a-zA-Z0-9]+$/.test(trimmed)) {
149
+ return { valid: false, error: "API key contains invalid characters", format: "grl" };
150
+ }
151
+ return { valid: true, format: "grl" };
152
+ }
153
+
154
+ // Legacy format: gr_<tier>_ prefix
155
+ if (trimmed.startsWith("gr_")) {
156
+ if (!/^gr_[a-z]+_[a-zA-Z0-9]+$/.test(trimmed)) {
157
+ return { valid: false, error: "Invalid legacy API key format", format: "legacy" };
158
+ }
159
+ return { valid: true, format: "legacy" };
160
+ }
161
+
162
+ // Also accept generic alphanumeric keys (backward compat)
163
+ if (/^[a-zA-Z0-9_-]+$/.test(trimmed) && trimmed.length >= 20) {
164
+ return { valid: true, format: "unknown" };
165
+ }
166
+
167
+ return { valid: false, error: 'API key must start with "grl_"' };
168
+ }
169
+
170
+ /**
171
+ * Quick format check.
172
+ * @param {string} key
173
+ * @returns {boolean}
174
+ */
175
+ function isValidKeyFormat(key) {
176
+ return validateKeyFormat(key).valid;
177
+ }
178
+
179
+ // ═══════════════════════════════════════════════════════════════════════════════
180
+ // STORAGE
181
+ // ═══════════════════════════════════════════════════════════════════════════════
182
+
183
+ /**
184
+ * Ensure config directory exists.
185
+ */
186
+ function ensureConfigDir() {
187
+ const dir = getConfigDir();
188
+ if (!fs.existsSync(dir)) {
189
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Read credentials from new auth.json.
195
+ * @returns {object|null}
196
+ */
197
+ function readCredentials() {
198
+ try {
199
+ const authPath = getAuthPath();
200
+ if (fs.existsSync(authPath)) {
201
+ return JSON.parse(fs.readFileSync(authPath, "utf8"));
202
+ }
203
+ } catch {
204
+ // Ignore
205
+ }
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Write credentials to auth.json.
211
+ * @param {object} credentials
212
+ */
213
+ function writeCredentials(credentials) {
214
+ ensureConfigDir();
215
+ const authPath = getAuthPath();
216
+ const data = { version: 2, ...credentials };
217
+ fs.writeFileSync(authPath, JSON.stringify(data, null, 2), { mode: 0o600 });
218
+ }
219
+
220
+ /**
221
+ * Delete credentials.
222
+ */
223
+ function deleteCredentials() {
224
+ try {
225
+ const authPath = getAuthPath();
226
+ if (fs.existsSync(authPath)) {
227
+ fs.unlinkSync(authPath);
228
+ }
229
+ } catch {
230
+ // Ignore
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Read entitlements cache.
236
+ * @returns {object|null}
237
+ */
238
+ function readCache() {
239
+ try {
240
+ const cachePath = getCachePath();
241
+ if (fs.existsSync(cachePath)) {
242
+ return JSON.parse(fs.readFileSync(cachePath, "utf8"));
243
+ }
244
+ } catch {
245
+ // Ignore
246
+ }
247
+ return null;
248
+ }
249
+
250
+ /**
251
+ * Write entitlements cache.
252
+ * @param {object} cache
253
+ */
254
+ function writeCache(cache) {
255
+ ensureConfigDir();
256
+ const cachePath = getCachePath();
257
+ fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2), { mode: 0o600 });
258
+ }
259
+
260
+ /**
261
+ * Delete cache.
262
+ */
263
+ function deleteCache() {
264
+ try {
265
+ const cachePath = getCachePath();
266
+ if (fs.existsSync(cachePath)) {
267
+ fs.unlinkSync(cachePath);
268
+ }
269
+ } catch {
270
+ // Ignore
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Clear all auth data (logout).
276
+ */
277
+ function clearAll() {
278
+ deleteCredentials();
279
+ deleteCache();
280
+
281
+ // Clear activity tracker
282
+ try {
283
+ const activityPath = path.join(getConfigDir(), ACTIVITY_FILE);
284
+ if (fs.existsSync(activityPath)) {
285
+ fs.unlinkSync(activityPath);
286
+ }
287
+ } catch {
288
+ // Ignore
289
+ }
290
+
291
+ // Clear rate limit
292
+ try {
293
+ const ratePath = path.join(getConfigDir(), RATE_LIMIT_FILE);
294
+ if (fs.existsSync(ratePath)) {
295
+ fs.unlinkSync(ratePath);
296
+ }
297
+ } catch {
298
+ // Ignore
299
+ }
300
+
301
+ // Also clear legacy locations
302
+ try {
303
+ const legacyPath = getLegacyConfigPath();
304
+ if (fs.existsSync(legacyPath)) {
305
+ const config = JSON.parse(fs.readFileSync(legacyPath, "utf8"));
306
+ delete config.apiKey;
307
+ fs.writeFileSync(legacyPath, JSON.stringify(config, null, 2), { mode: 0o600 });
308
+ }
309
+ } catch {
310
+ // Ignore
311
+ }
312
+
313
+ auditLog("LOGOUT", null, "cli");
314
+ }
315
+
316
+ // ═══════════════════════════════════════════════════════════════════════════════
317
+ // AUDIT LOGGING
318
+ // ═══════════════════════════════════════════════════════════════════════════════
319
+
320
+ /**
321
+ * Write audit log entry.
322
+ * @param {string} event
323
+ * @param {string|null} apiKey
324
+ * @param {string} source
325
+ * @param {object} [details]
326
+ */
327
+ function auditLog(event, apiKey, source, details) {
328
+ try {
329
+ const configDir = getConfigDir();
330
+ const auditPath = path.join(configDir, AUDIT_FILE);
331
+
332
+ ensureConfigDir();
333
+
334
+ // Rotate log if too large
335
+ try {
336
+ const stats = fs.statSync(auditPath);
337
+ if (stats.size > MAX_AUDIT_SIZE) {
338
+ const backupPath = auditPath + ".1";
339
+ if (fs.existsSync(backupPath)) {
340
+ fs.unlinkSync(backupPath);
341
+ }
342
+ fs.renameSync(auditPath, backupPath);
343
+ }
344
+ } catch {
345
+ // File doesn't exist yet
346
+ }
347
+
348
+ const entry = {
349
+ timestamp: new Date().toISOString(),
350
+ event,
351
+ keyMasked: apiKey ? redactApiKey(apiKey) : "****",
352
+ source,
353
+ details: details || undefined,
354
+ };
355
+
356
+ fs.appendFileSync(auditPath, JSON.stringify(entry) + "\n", { mode: 0o600 });
357
+ } catch {
358
+ // Audit logging should never break auth flow
359
+ }
360
+ }
361
+
362
+ // ═══════════════════════════════════════════════════════════════════════════════
363
+ // RATE LIMITING
364
+ // ═══════════════════════════════════════════════════════════════════════════════
365
+
366
+ /**
367
+ * Check if currently rate limited.
368
+ * @returns {{ limited: boolean, retryAfter?: number }}
369
+ */
370
+ function checkRateLimit() {
371
+ if (rateLimitState.retryAfter && rateLimitState.timestamp) {
372
+ const elapsed = Date.now() - rateLimitState.timestamp;
373
+ const remaining = (rateLimitState.retryAfter * 1000) - elapsed;
374
+
375
+ if (remaining > 0) {
376
+ return { limited: true, retryAfter: Math.ceil(remaining / 1000) };
377
+ }
378
+ }
379
+ return { limited: false };
380
+ }
381
+
382
+ /**
383
+ * Record rate limit from response.
384
+ * @param {number} retryAfter
385
+ */
386
+ function recordRateLimit(retryAfter) {
387
+ rateLimitState = { retryAfter, timestamp: Date.now() };
388
+ auditLog("RATE_LIMITED", null, "api", { retryAfter });
389
+ }
390
+
391
+ /**
392
+ * Clear rate limit state.
393
+ */
394
+ function clearRateLimitState() {
395
+ rateLimitState = { retryAfter: null, timestamp: null };
396
+ }
397
+
398
+ // ═══════════════════════════════════════════════════════════════════════════════
399
+ // ACTIVITY TRACKING (LOCKOUT PROTECTION)
400
+ // ═══════════════════════════════════════════════════════════════════════════════
401
+
402
+ /**
403
+ * Track failed auth attempt.
404
+ * @returns {{ locked: boolean, lockoutRemaining?: number }}
405
+ */
406
+ function trackFailedAttempt() {
407
+ try {
408
+ const activityPath = path.join(getConfigDir(), ACTIVITY_FILE);
409
+
410
+ let activity = { failedAttempts: 0, lastAttempt: 0 };
411
+
412
+ try {
413
+ activity = JSON.parse(fs.readFileSync(activityPath, "utf8"));
414
+ } catch {
415
+ // File doesn't exist
416
+ }
417
+
418
+ // Check if currently locked out
419
+ if (activity.lockoutUntil && Date.now() < activity.lockoutUntil) {
420
+ return {
421
+ locked: true,
422
+ lockoutRemaining: Math.ceil((activity.lockoutUntil - Date.now()) / 1000)
423
+ };
424
+ }
425
+
426
+ // Reset if lock expired
427
+ if (activity.lockoutUntil && Date.now() >= activity.lockoutUntil) {
428
+ activity = { failedAttempts: 0, lastAttempt: 0 };
429
+ }
430
+
431
+ // Track new attempt
432
+ activity.failedAttempts++;
433
+ activity.lastAttempt = Date.now();
434
+
435
+ // Check if should lock out
436
+ if (activity.failedAttempts >= FAILED_ATTEMPT_THRESHOLD) {
437
+ activity.lockoutUntil = Date.now() + LOCKOUT_DURATION_MS;
438
+ auditLog("SUSPICIOUS_ACTIVITY", null, "local", {
439
+ reason: "Too many failed attempts",
440
+ attempts: activity.failedAttempts,
441
+ });
442
+ }
443
+
444
+ fs.writeFileSync(activityPath, JSON.stringify(activity), { mode: 0o600 });
445
+
446
+ if (activity.lockoutUntil) {
447
+ return { locked: true, lockoutRemaining: Math.ceil(LOCKOUT_DURATION_MS / 1000) };
448
+ }
449
+
450
+ return { locked: false };
451
+ } catch {
452
+ return { locked: false };
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Reset activity tracker on successful auth.
458
+ */
459
+ function resetActivityTracker() {
460
+ try {
461
+ const activityPath = path.join(getConfigDir(), ACTIVITY_FILE);
462
+ if (fs.existsSync(activityPath)) {
463
+ fs.unlinkSync(activityPath);
464
+ }
465
+ } catch {
466
+ // Non-critical
467
+ }
468
+ }
469
+
470
+ // ═══════════════════════════════════════════════════════════════════════════════
471
+ // INPUT SANITIZATION
472
+ // ═══════════════════════════════════════════════════════════════════════════════
473
+
474
+ /**
475
+ * Deeply sanitize user input.
476
+ * @param {*} input
477
+ * @returns {string}
478
+ */
479
+ function sanitizeInput(input) {
480
+ if (input === null || input === undefined) return "";
481
+
482
+ let str = String(input);
483
+
484
+ // Remove null bytes
485
+ str = str.replace(/\0/g, "");
486
+
487
+ // Remove control characters
488
+ str = str.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
489
+
490
+ // Trim whitespace
491
+ str = str.trim();
492
+
493
+ // Limit length
494
+ if (str.length > 256) {
495
+ str = str.slice(0, 256);
496
+ }
497
+
498
+ return str;
499
+ }
500
+
501
+ // ═══════════════════════════════════════════════════════════════════════════════
502
+ // ENVIRONMENT DETECTION
503
+ // ═══════════════════════════════════════════════════════════════════════════════
504
+
505
+ /**
506
+ * Detect execution environment.
507
+ * @returns {object}
508
+ */
509
+ function detectEnvironment() {
510
+ const env = process.env;
511
+
512
+ let isCI = env.CI === "true" || env.CI === "1";
513
+ let ciProvider;
514
+
515
+ if (isCI) {
516
+ if (env.GITHUB_ACTIONS) ciProvider = "github";
517
+ else if (env.GITLAB_CI) ciProvider = "gitlab";
518
+ else if (env.CIRCLECI) ciProvider = "circleci";
519
+ else ciProvider = "unknown";
520
+ }
521
+
522
+ return {
523
+ isCI,
524
+ ciProvider,
525
+ isProduction: env.NODE_ENV === "production",
526
+ isDevelopment: env.NODE_ENV === "development" || (!env.NODE_ENV && !isCI),
527
+ isInteractive: process.stdin.isTTY && !isCI,
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Check if dev bypass is allowed.
533
+ * @returns {boolean}
534
+ */
535
+ function isDevBypassAllowed() {
536
+ const env = detectEnvironment();
537
+ if (env.isProduction || env.isCI) return false;
538
+ return env.isDevelopment && process.env.VIBECHECK_DEV_PRO === "1";
539
+ }
540
+
541
+ // ═══════════════════════════════════════════════════════════════════════════════
542
+ // RESOLUTION
543
+ // ═══════════════════════════════════════════════════════════════════════════════
544
+
545
+ /**
546
+ * Hash API key for cache.
547
+ * @param {string} key
548
+ * @returns {string}
549
+ */
550
+ function hashApiKey(key) {
551
+ return crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
552
+ }
553
+
554
+ /**
555
+ * Resolve API key from all sources.
556
+ * @param {string} [directKey] Key passed via --key flag
557
+ * @returns {{ key: string|null, source: string }}
558
+ */
559
+ function resolveApiKey(directKey) {
560
+ // 1. Direct argument
561
+ if (directKey && isValidKeyFormat(directKey)) {
562
+ return { key: directKey.trim(), source: "args" };
563
+ }
564
+
565
+ // 2. Environment variable
566
+ const envKey = process.env.VIBECHECK_API_KEY;
567
+ if (envKey && isValidKeyFormat(envKey)) {
568
+ return { key: envKey.trim(), source: "env" };
569
+ }
570
+
571
+ // 3. New auth.json
572
+ const creds = readCredentials();
573
+ if (creds?.apiKey && isValidKeyFormat(creds.apiKey)) {
574
+ return { key: creds.apiKey, source: "file" };
575
+ }
576
+
577
+ // 4. Legacy config.json
578
+ try {
579
+ const legacyPath = getLegacyConfigPath();
580
+ if (fs.existsSync(legacyPath)) {
581
+ const config = JSON.parse(fs.readFileSync(legacyPath, "utf8"));
582
+ if (config.apiKey && isValidKeyFormat(config.apiKey)) {
583
+ return { key: config.apiKey, source: "legacy" };
584
+ }
585
+ }
586
+ } catch {
587
+ // Ignore
588
+ }
589
+
590
+ return { key: null, source: "none" };
591
+ }
592
+
593
+ /**
594
+ * Format source for display.
595
+ * @param {string} source
596
+ * @returns {string}
597
+ */
598
+ function formatSource(source) {
599
+ switch (source) {
600
+ case "env": return "Environment Variable";
601
+ case "file": return "Config File";
602
+ case "args": return "Command Line";
603
+ case "legacy": return "Legacy Config";
604
+ case "none": return "Not Found";
605
+ default: return source;
606
+ }
607
+ }
608
+
609
+ // ═══════════════════════════════════════════════════════════════════════════════
610
+ // CACHE HELPERS
611
+ // ═══════════════════════════════════════════════════════════════════════════════
612
+
613
+ const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
614
+ const STALE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
615
+
616
+ /**
617
+ * Check if cache is valid.
618
+ * @param {object} cache
619
+ * @param {string} keyHash
620
+ * @returns {boolean}
621
+ */
622
+ function isCacheValid(cache, keyHash) {
623
+ if (!cache || cache.keyHash !== keyHash) return false;
624
+ return Date.now() - cache.timestamp < CACHE_TTL_MS;
625
+ }
626
+
627
+ /**
628
+ * Check if cache is stale but usable.
629
+ * @param {object} cache
630
+ * @param {string} keyHash
631
+ * @returns {boolean}
632
+ */
633
+ function isCacheStale(cache, keyHash) {
634
+ if (!cache || cache.keyHash !== keyHash) return false;
635
+ const age = Date.now() - cache.timestamp;
636
+ return age >= CACHE_TTL_MS && age < STALE_TTL_MS;
637
+ }
638
+
639
+ /**
640
+ * Get cache age in seconds.
641
+ * @param {object} cache
642
+ * @returns {number}
643
+ */
644
+ function getCacheAge(cache) {
645
+ if (!cache) return 0;
646
+ return Math.floor((Date.now() - cache.timestamp) / 1000);
647
+ }
648
+
649
+ // ═══════════════════════════════════════════════════════════════════════════════
650
+ // AUTH CHECK
651
+ // ═══════════════════════════════════════════════════════════════════════════════
652
+
653
+ /**
654
+ * Sleep helper.
655
+ * @param {number} ms
656
+ * @returns {Promise<void>}
657
+ */
658
+ function sleep(ms) {
659
+ return new Promise(resolve => setTimeout(resolve, ms));
660
+ }
661
+
662
+ /**
663
+ * Calculate retry delay with exponential backoff.
664
+ * @param {number} attempt
665
+ * @returns {number}
666
+ */
667
+ function getRetryDelay(attempt) {
668
+ const exponentialDelay = BASE_RETRY_DELAY_MS * Math.pow(2, attempt);
669
+ const jitter = Math.random() * 500;
670
+ return Math.min(exponentialDelay + jitter, 30000);
671
+ }
672
+
673
+ /**
674
+ * Fetch with retry and rate limit handling.
675
+ * @param {string} apiKey
676
+ * @param {number} [attempt]
677
+ * @returns {Promise<object>}
678
+ */
679
+ async function fetchWithRetry(apiKey, attempt = 0) {
680
+ const apiUrl = process.env.VIBECHECK_API_URL || "https://api.vibecheckai.dev";
681
+
682
+ // Check rate limit
683
+ const rateCheck = checkRateLimit();
684
+ if (rateCheck.limited) {
685
+ return { ok: false, error: { code: "RATE_LIMITED", retryAfter: rateCheck.retryAfter } };
686
+ }
687
+
688
+ try {
689
+ const response = await fetch(`${apiUrl}/v1/auth/whoami`, {
690
+ headers: {
691
+ Authorization: `Bearer ${apiKey}`,
692
+ "X-Client-Version": "2.0.0",
693
+ },
694
+ signal: AbortSignal.timeout(10000),
695
+ });
696
+
697
+ // Handle rate limiting
698
+ if (response.status === 429) {
699
+ const retryAfter = parseInt(response.headers.get("Retry-After") || "60", 10);
700
+ recordRateLimit(retryAfter);
701
+ return { ok: false, error: { code: "RATE_LIMITED", retryAfter } };
702
+ }
703
+
704
+ // Auth errors - don't retry
705
+ if (response.status === 401) {
706
+ trackFailedAttempt();
707
+ auditLog("KEY_INVALID", apiKey, "api");
708
+ return { ok: false, error: { code: "INVALID_API_KEY" } };
709
+ }
710
+
711
+ if (response.status === 403) {
712
+ auditLog("KEY_REVOKED", apiKey, "api");
713
+ return { ok: false, error: { code: "KEY_REVOKED" } };
714
+ }
715
+
716
+ // Server errors - retry
717
+ if (response.status >= 500 && attempt < MAX_RETRIES) {
718
+ await sleep(getRetryDelay(attempt));
719
+ return fetchWithRetry(apiKey, attempt + 1);
720
+ }
721
+
722
+ if (!response.ok) {
723
+ return { ok: false, error: { code: "API_ERROR", status: response.status } };
724
+ }
725
+
726
+ clearRateLimitState();
727
+ resetActivityTracker();
728
+
729
+ const data = await response.json();
730
+ auditLog("KEY_VALIDATED", apiKey, "api");
731
+ return { ok: true, data };
732
+ } catch (err) {
733
+ // Network error - retry
734
+ if (attempt < MAX_RETRIES) {
735
+ await sleep(getRetryDelay(attempt));
736
+ return fetchWithRetry(apiKey, attempt + 1);
737
+ }
738
+ return { ok: false, error: { code: "NETWORK_ERROR" } };
739
+ }
740
+ }
741
+
742
+ /**
743
+ * Comprehensive auth check (for --check flag).
744
+ * @param {string} [directKey]
745
+ * @returns {Promise<object>}
746
+ */
747
+ async function checkAuth(directKey) {
748
+ // Check for lockout first
749
+ const lockout = trackFailedAttempt();
750
+ if (lockout.locked) {
751
+ // Reset the failed attempt we just added since we're checking lockout
752
+ resetActivityTracker();
753
+ return {
754
+ authenticated: false,
755
+ tier: "free",
756
+ source: "none",
757
+ keyMasked: "****",
758
+ networkOk: false,
759
+ cacheValid: false,
760
+ error: {
761
+ code: "LOCKED_OUT",
762
+ message: `Too many failed attempts. Try again in ${lockout.lockoutRemaining}s`,
763
+ recovery: `Wait ${Math.ceil(lockout.lockoutRemaining / 60)} minutes before retrying`,
764
+ },
765
+ };
766
+ }
767
+ resetActivityTracker(); // Reset the test attempt we just made
768
+
769
+ const resolved = resolveApiKey(directKey);
770
+
771
+ // No key found
772
+ if (!resolved.key) {
773
+ return {
774
+ authenticated: false,
775
+ tier: "free",
776
+ source: "none",
777
+ keyMasked: "****",
778
+ networkOk: false,
779
+ cacheValid: false,
780
+ error: {
781
+ code: "AUTH_REQUIRED",
782
+ message: "Not logged in",
783
+ recovery: 'Run "vibecheck login" or set VIBECHECK_API_KEY',
784
+ },
785
+ };
786
+ }
787
+
788
+ const keyHash = hashApiKey(resolved.key);
789
+ const cache = readCache();
790
+ const creds = readCredentials();
791
+
792
+ // Check cache status
793
+ const cacheValid = cache ? isCacheValid(cache, keyHash) : false;
794
+ const cacheAge = cache ? getCacheAge(cache) : undefined;
795
+
796
+ // Try to validate with API (with retry)
797
+ const result = await fetchWithRetry(resolved.key);
798
+
799
+ let networkOk = result.ok;
800
+ let entitlements = result.data || null;
801
+ let error = null;
802
+
803
+ if (!result.ok) {
804
+ error = {
805
+ code: result.error?.code || "UNKNOWN",
806
+ message: getErrorMessage(result.error?.code),
807
+ recovery: getErrorRecovery(result.error?.code),
808
+ };
809
+
810
+ // Use cache if available
811
+ if (cache && (isCacheValid(cache, keyHash) || isCacheStale(cache, keyHash))) {
812
+ entitlements = cache.data;
813
+ }
814
+ }
815
+
816
+ // Map plan to tier
817
+ const tier = entitlements?.plan === "free" || !entitlements?.plan ? "free" : "pro";
818
+
819
+ return {
820
+ authenticated: !!entitlements || !!creds?.apiKey,
821
+ tier: entitlements?.tier || tier,
822
+ source: resolved.source,
823
+ keyMasked: redactApiKey(resolved.key),
824
+ networkOk,
825
+ cacheValid,
826
+ cacheAge,
827
+ user: entitlements?.user || creds?.user,
828
+ entitlements,
829
+ error,
830
+ };
831
+ }
832
+
833
+ /**
834
+ * Get human-readable error message.
835
+ * @param {string} code
836
+ * @returns {string}
837
+ */
838
+ function getErrorMessage(code) {
839
+ switch (code) {
840
+ case "RATE_LIMITED": return "Rate limit exceeded";
841
+ case "INVALID_API_KEY": return "Invalid API key";
842
+ case "KEY_REVOKED": return "API key has been revoked";
843
+ case "KEY_EXPIRED": return "API key has expired";
844
+ case "NETWORK_ERROR": return "Cannot connect to API";
845
+ case "LOCKED_OUT": return "Too many failed attempts";
846
+ default: return "Authentication error";
847
+ }
848
+ }
849
+
850
+ /**
851
+ * Get error recovery message.
852
+ * @param {string} code
853
+ * @returns {string}
854
+ */
855
+ function getErrorRecovery(code) {
856
+ switch (code) {
857
+ case "RATE_LIMITED": return "Wait and try again";
858
+ case "INVALID_API_KEY": return "Check your API key format";
859
+ case "KEY_REVOKED": return "Generate a new key at vibecheckai.dev/settings/keys";
860
+ case "KEY_EXPIRED": return "Generate a new key at vibecheckai.dev/settings/keys";
861
+ case "NETWORK_ERROR": return "Check your network. Offline mode uses cached data.";
862
+ case "LOCKED_OUT": return "Wait before retrying";
863
+ default: return 'Run "vibecheck login" to re-authenticate';
864
+ }
865
+ }
866
+
867
+ /**
868
+ * Refresh entitlements (for --refresh flag).
869
+ * @param {string} [directKey]
870
+ * @returns {Promise<object|null>}
871
+ */
872
+ async function refreshEntitlements(directKey) {
873
+ const resolved = resolveApiKey(directKey);
874
+
875
+ if (!resolved.key) {
876
+ return null;
877
+ }
878
+
879
+ const keyHash = hashApiKey(resolved.key);
880
+
881
+ try {
882
+ const apiUrl = process.env.VIBECHECK_API_URL || "https://api.vibecheckai.dev";
883
+ const response = await fetch(`${apiUrl}/v1/auth/whoami`, {
884
+ headers: { Authorization: `Bearer ${resolved.key}` },
885
+ signal: AbortSignal.timeout(10000),
886
+ });
887
+
888
+ if (!response.ok) {
889
+ return null;
890
+ }
891
+
892
+ const entitlements = await response.json();
893
+
894
+ // Update cache
895
+ writeCache({
896
+ keyHash,
897
+ timestamp: Date.now(),
898
+ data: entitlements,
899
+ });
900
+
901
+ // Update stored credentials
902
+ const creds = readCredentials() || { version: 2, keySource: "file" };
903
+ writeCredentials({
904
+ ...creds,
905
+ user: entitlements.user,
906
+ tier: entitlements.tier || (entitlements.plan === "free" ? "free" : "pro"),
907
+ lastValidated: new Date().toISOString(),
908
+ });
909
+
910
+ return entitlements;
911
+ } catch {
912
+ return null;
913
+ }
914
+ }
915
+
916
+ // ═══════════════════════════════════════════════════════════════════════════════
917
+ // EXPORTS
918
+ // ═══════════════════════════════════════════════════════════════════════════════
919
+
920
+ module.exports = {
921
+ // Paths
922
+ getConfigDir,
923
+ getAuthPath,
924
+ getCachePath,
925
+ getLegacyConfigPath,
926
+
927
+ // Redaction
928
+ redactApiKey,
929
+ redactOutput,
930
+
931
+ // Validation
932
+ validateKeyFormat,
933
+ isValidKeyFormat,
934
+ sanitizeInput,
935
+
936
+ // Storage
937
+ ensureConfigDir,
938
+ readCredentials,
939
+ writeCredentials,
940
+ deleteCredentials,
941
+ readCache,
942
+ writeCache,
943
+ deleteCache,
944
+ clearAll,
945
+
946
+ // Resolution
947
+ hashApiKey,
948
+ resolveApiKey,
949
+ formatSource,
950
+
951
+ // Cache
952
+ isCacheValid,
953
+ isCacheStale,
954
+ getCacheAge,
955
+ CACHE_TTL_MS,
956
+ STALE_TTL_MS,
957
+
958
+ // Rate limiting
959
+ checkRateLimit,
960
+ recordRateLimit,
961
+ clearRateLimitState,
962
+
963
+ // Activity tracking
964
+ trackFailedAttempt,
965
+ resetActivityTracker,
966
+
967
+ // Audit
968
+ auditLog,
969
+
970
+ // Environment
971
+ detectEnvironment,
972
+ isDevBypassAllowed,
973
+
974
+ // Auth
975
+ checkAuth,
976
+ refreshEntitlements,
977
+ };