@dotsetlabs/bellwether 0.10.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 (403) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/LICENSE +21 -0
  3. package/README.md +739 -0
  4. package/dist/auth/credentials.d.ts +64 -0
  5. package/dist/auth/credentials.js +218 -0
  6. package/dist/auth/index.d.ts +6 -0
  7. package/dist/auth/index.js +6 -0
  8. package/dist/auth/keychain.d.ts +64 -0
  9. package/dist/auth/keychain.js +268 -0
  10. package/dist/baseline/ab-testing.d.ts +80 -0
  11. package/dist/baseline/ab-testing.js +236 -0
  12. package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
  13. package/dist/baseline/ai-compatibility-scorer.js +606 -0
  14. package/dist/baseline/calibration.d.ts +77 -0
  15. package/dist/baseline/calibration.js +136 -0
  16. package/dist/baseline/category-matching.d.ts +85 -0
  17. package/dist/baseline/category-matching.js +289 -0
  18. package/dist/baseline/change-impact-analyzer.d.ts +98 -0
  19. package/dist/baseline/change-impact-analyzer.js +592 -0
  20. package/dist/baseline/comparator.d.ts +64 -0
  21. package/dist/baseline/comparator.js +916 -0
  22. package/dist/baseline/confidence.d.ts +55 -0
  23. package/dist/baseline/confidence.js +122 -0
  24. package/dist/baseline/converter.d.ts +61 -0
  25. package/dist/baseline/converter.js +585 -0
  26. package/dist/baseline/dependency-analyzer.d.ts +89 -0
  27. package/dist/baseline/dependency-analyzer.js +567 -0
  28. package/dist/baseline/deprecation-tracker.d.ts +133 -0
  29. package/dist/baseline/deprecation-tracker.js +322 -0
  30. package/dist/baseline/diff.d.ts +55 -0
  31. package/dist/baseline/diff.js +1584 -0
  32. package/dist/baseline/documentation-scorer.d.ts +205 -0
  33. package/dist/baseline/documentation-scorer.js +466 -0
  34. package/dist/baseline/embeddings.d.ts +118 -0
  35. package/dist/baseline/embeddings.js +251 -0
  36. package/dist/baseline/error-analyzer.d.ts +198 -0
  37. package/dist/baseline/error-analyzer.js +721 -0
  38. package/dist/baseline/evaluation/evaluator.d.ts +42 -0
  39. package/dist/baseline/evaluation/evaluator.js +323 -0
  40. package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
  41. package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
  42. package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
  43. package/dist/baseline/evaluation/golden-dataset.js +717 -0
  44. package/dist/baseline/evaluation/index.d.ts +15 -0
  45. package/dist/baseline/evaluation/index.js +15 -0
  46. package/dist/baseline/evaluation/types.d.ts +186 -0
  47. package/dist/baseline/evaluation/types.js +8 -0
  48. package/dist/baseline/external-dependency-detector.d.ts +181 -0
  49. package/dist/baseline/external-dependency-detector.js +524 -0
  50. package/dist/baseline/golden-output.d.ts +162 -0
  51. package/dist/baseline/golden-output.js +636 -0
  52. package/dist/baseline/health-scorer.d.ts +174 -0
  53. package/dist/baseline/health-scorer.js +451 -0
  54. package/dist/baseline/incremental-checker.d.ts +97 -0
  55. package/dist/baseline/incremental-checker.js +174 -0
  56. package/dist/baseline/index.d.ts +31 -0
  57. package/dist/baseline/index.js +42 -0
  58. package/dist/baseline/migration-generator.d.ts +137 -0
  59. package/dist/baseline/migration-generator.js +554 -0
  60. package/dist/baseline/migrations.d.ts +60 -0
  61. package/dist/baseline/migrations.js +197 -0
  62. package/dist/baseline/performance-tracker.d.ts +214 -0
  63. package/dist/baseline/performance-tracker.js +577 -0
  64. package/dist/baseline/pr-comment-generator.d.ts +117 -0
  65. package/dist/baseline/pr-comment-generator.js +546 -0
  66. package/dist/baseline/response-fingerprint.d.ts +127 -0
  67. package/dist/baseline/response-fingerprint.js +728 -0
  68. package/dist/baseline/response-schema-tracker.d.ts +129 -0
  69. package/dist/baseline/response-schema-tracker.js +420 -0
  70. package/dist/baseline/risk-scorer.d.ts +54 -0
  71. package/dist/baseline/risk-scorer.js +434 -0
  72. package/dist/baseline/saver.d.ts +89 -0
  73. package/dist/baseline/saver.js +554 -0
  74. package/dist/baseline/scenario-generator.d.ts +151 -0
  75. package/dist/baseline/scenario-generator.js +905 -0
  76. package/dist/baseline/schema-compare.d.ts +86 -0
  77. package/dist/baseline/schema-compare.js +557 -0
  78. package/dist/baseline/schema-evolution.d.ts +189 -0
  79. package/dist/baseline/schema-evolution.js +467 -0
  80. package/dist/baseline/semantic.d.ts +203 -0
  81. package/dist/baseline/semantic.js +908 -0
  82. package/dist/baseline/synonyms.d.ts +60 -0
  83. package/dist/baseline/synonyms.js +386 -0
  84. package/dist/baseline/telemetry.d.ts +165 -0
  85. package/dist/baseline/telemetry.js +294 -0
  86. package/dist/baseline/test-pruner.d.ts +120 -0
  87. package/dist/baseline/test-pruner.js +387 -0
  88. package/dist/baseline/types.d.ts +449 -0
  89. package/dist/baseline/types.js +5 -0
  90. package/dist/baseline/version.d.ts +138 -0
  91. package/dist/baseline/version.js +206 -0
  92. package/dist/cache/index.d.ts +5 -0
  93. package/dist/cache/index.js +5 -0
  94. package/dist/cache/response-cache.d.ts +151 -0
  95. package/dist/cache/response-cache.js +287 -0
  96. package/dist/ci/index.d.ts +60 -0
  97. package/dist/ci/index.js +342 -0
  98. package/dist/cli/commands/auth.d.ts +12 -0
  99. package/dist/cli/commands/auth.js +352 -0
  100. package/dist/cli/commands/badge.d.ts +3 -0
  101. package/dist/cli/commands/badge.js +74 -0
  102. package/dist/cli/commands/baseline-accept.d.ts +15 -0
  103. package/dist/cli/commands/baseline-accept.js +178 -0
  104. package/dist/cli/commands/baseline-migrate.d.ts +12 -0
  105. package/dist/cli/commands/baseline-migrate.js +164 -0
  106. package/dist/cli/commands/baseline.d.ts +14 -0
  107. package/dist/cli/commands/baseline.js +449 -0
  108. package/dist/cli/commands/beta.d.ts +10 -0
  109. package/dist/cli/commands/beta.js +231 -0
  110. package/dist/cli/commands/check.d.ts +11 -0
  111. package/dist/cli/commands/check.js +820 -0
  112. package/dist/cli/commands/cloud/badge.d.ts +3 -0
  113. package/dist/cli/commands/cloud/badge.js +74 -0
  114. package/dist/cli/commands/cloud/diff.d.ts +6 -0
  115. package/dist/cli/commands/cloud/diff.js +79 -0
  116. package/dist/cli/commands/cloud/history.d.ts +6 -0
  117. package/dist/cli/commands/cloud/history.js +102 -0
  118. package/dist/cli/commands/cloud/link.d.ts +9 -0
  119. package/dist/cli/commands/cloud/link.js +119 -0
  120. package/dist/cli/commands/cloud/login.d.ts +7 -0
  121. package/dist/cli/commands/cloud/login.js +499 -0
  122. package/dist/cli/commands/cloud/projects.d.ts +6 -0
  123. package/dist/cli/commands/cloud/projects.js +44 -0
  124. package/dist/cli/commands/cloud/shared.d.ts +7 -0
  125. package/dist/cli/commands/cloud/shared.js +42 -0
  126. package/dist/cli/commands/cloud/teams.d.ts +8 -0
  127. package/dist/cli/commands/cloud/teams.js +169 -0
  128. package/dist/cli/commands/cloud/upload.d.ts +8 -0
  129. package/dist/cli/commands/cloud/upload.js +181 -0
  130. package/dist/cli/commands/contract.d.ts +11 -0
  131. package/dist/cli/commands/contract.js +280 -0
  132. package/dist/cli/commands/discover.d.ts +3 -0
  133. package/dist/cli/commands/discover.js +82 -0
  134. package/dist/cli/commands/eval.d.ts +9 -0
  135. package/dist/cli/commands/eval.js +187 -0
  136. package/dist/cli/commands/explore.d.ts +11 -0
  137. package/dist/cli/commands/explore.js +437 -0
  138. package/dist/cli/commands/feedback.d.ts +9 -0
  139. package/dist/cli/commands/feedback.js +174 -0
  140. package/dist/cli/commands/golden.d.ts +12 -0
  141. package/dist/cli/commands/golden.js +407 -0
  142. package/dist/cli/commands/history.d.ts +10 -0
  143. package/dist/cli/commands/history.js +202 -0
  144. package/dist/cli/commands/init.d.ts +9 -0
  145. package/dist/cli/commands/init.js +219 -0
  146. package/dist/cli/commands/interview.d.ts +3 -0
  147. package/dist/cli/commands/interview.js +903 -0
  148. package/dist/cli/commands/link.d.ts +10 -0
  149. package/dist/cli/commands/link.js +169 -0
  150. package/dist/cli/commands/login.d.ts +7 -0
  151. package/dist/cli/commands/login.js +499 -0
  152. package/dist/cli/commands/preset.d.ts +33 -0
  153. package/dist/cli/commands/preset.js +297 -0
  154. package/dist/cli/commands/profile.d.ts +33 -0
  155. package/dist/cli/commands/profile.js +286 -0
  156. package/dist/cli/commands/registry.d.ts +11 -0
  157. package/dist/cli/commands/registry.js +146 -0
  158. package/dist/cli/commands/shared.d.ts +79 -0
  159. package/dist/cli/commands/shared.js +196 -0
  160. package/dist/cli/commands/teams.d.ts +8 -0
  161. package/dist/cli/commands/teams.js +169 -0
  162. package/dist/cli/commands/test.d.ts +9 -0
  163. package/dist/cli/commands/test.js +500 -0
  164. package/dist/cli/commands/upload.d.ts +8 -0
  165. package/dist/cli/commands/upload.js +223 -0
  166. package/dist/cli/commands/validate-config.d.ts +6 -0
  167. package/dist/cli/commands/validate-config.js +35 -0
  168. package/dist/cli/commands/verify.d.ts +11 -0
  169. package/dist/cli/commands/verify.js +283 -0
  170. package/dist/cli/commands/watch.d.ts +12 -0
  171. package/dist/cli/commands/watch.js +253 -0
  172. package/dist/cli/index.d.ts +3 -0
  173. package/dist/cli/index.js +178 -0
  174. package/dist/cli/interactive.d.ts +47 -0
  175. package/dist/cli/interactive.js +216 -0
  176. package/dist/cli/output/terminal-reporter.d.ts +19 -0
  177. package/dist/cli/output/terminal-reporter.js +104 -0
  178. package/dist/cli/output.d.ts +226 -0
  179. package/dist/cli/output.js +438 -0
  180. package/dist/cli/utils/env.d.ts +5 -0
  181. package/dist/cli/utils/env.js +14 -0
  182. package/dist/cli/utils/progress.d.ts +59 -0
  183. package/dist/cli/utils/progress.js +206 -0
  184. package/dist/cli/utils/server-context.d.ts +10 -0
  185. package/dist/cli/utils/server-context.js +36 -0
  186. package/dist/cloud/auth.d.ts +144 -0
  187. package/dist/cloud/auth.js +374 -0
  188. package/dist/cloud/client.d.ts +24 -0
  189. package/dist/cloud/client.js +65 -0
  190. package/dist/cloud/http-client.d.ts +38 -0
  191. package/dist/cloud/http-client.js +215 -0
  192. package/dist/cloud/index.d.ts +23 -0
  193. package/dist/cloud/index.js +25 -0
  194. package/dist/cloud/mock-client.d.ts +107 -0
  195. package/dist/cloud/mock-client.js +545 -0
  196. package/dist/cloud/types.d.ts +515 -0
  197. package/dist/cloud/types.js +15 -0
  198. package/dist/config/defaults.d.ts +160 -0
  199. package/dist/config/defaults.js +169 -0
  200. package/dist/config/loader.d.ts +24 -0
  201. package/dist/config/loader.js +122 -0
  202. package/dist/config/template.d.ts +42 -0
  203. package/dist/config/template.js +647 -0
  204. package/dist/config/validator.d.ts +2112 -0
  205. package/dist/config/validator.js +658 -0
  206. package/dist/constants/cloud.d.ts +107 -0
  207. package/dist/constants/cloud.js +110 -0
  208. package/dist/constants/core.d.ts +521 -0
  209. package/dist/constants/core.js +556 -0
  210. package/dist/constants/testing.d.ts +1283 -0
  211. package/dist/constants/testing.js +1568 -0
  212. package/dist/constants.d.ts +10 -0
  213. package/dist/constants.js +10 -0
  214. package/dist/contract/index.d.ts +6 -0
  215. package/dist/contract/index.js +5 -0
  216. package/dist/contract/validator.d.ts +177 -0
  217. package/dist/contract/validator.js +574 -0
  218. package/dist/cost/index.d.ts +6 -0
  219. package/dist/cost/index.js +5 -0
  220. package/dist/cost/tracker.d.ts +134 -0
  221. package/dist/cost/tracker.js +313 -0
  222. package/dist/discovery/discovery.d.ts +16 -0
  223. package/dist/discovery/discovery.js +173 -0
  224. package/dist/discovery/types.d.ts +51 -0
  225. package/dist/discovery/types.js +2 -0
  226. package/dist/docs/agents.d.ts +3 -0
  227. package/dist/docs/agents.js +995 -0
  228. package/dist/docs/contract.d.ts +51 -0
  229. package/dist/docs/contract.js +1681 -0
  230. package/dist/docs/generator.d.ts +4 -0
  231. package/dist/docs/generator.js +4 -0
  232. package/dist/docs/html-reporter.d.ts +9 -0
  233. package/dist/docs/html-reporter.js +757 -0
  234. package/dist/docs/index.d.ts +10 -0
  235. package/dist/docs/index.js +11 -0
  236. package/dist/docs/junit-reporter.d.ts +18 -0
  237. package/dist/docs/junit-reporter.js +210 -0
  238. package/dist/docs/report.d.ts +14 -0
  239. package/dist/docs/report.js +44 -0
  240. package/dist/docs/sarif-reporter.d.ts +19 -0
  241. package/dist/docs/sarif-reporter.js +335 -0
  242. package/dist/docs/shared.d.ts +35 -0
  243. package/dist/docs/shared.js +162 -0
  244. package/dist/docs/templates.d.ts +12 -0
  245. package/dist/docs/templates.js +76 -0
  246. package/dist/errors/index.d.ts +6 -0
  247. package/dist/errors/index.js +6 -0
  248. package/dist/errors/retry.d.ts +92 -0
  249. package/dist/errors/retry.js +323 -0
  250. package/dist/errors/types.d.ts +321 -0
  251. package/dist/errors/types.js +584 -0
  252. package/dist/index.d.ts +32 -0
  253. package/dist/index.js +32 -0
  254. package/dist/interview/dependency-resolver.d.ts +11 -0
  255. package/dist/interview/dependency-resolver.js +32 -0
  256. package/dist/interview/interviewer.d.ts +232 -0
  257. package/dist/interview/interviewer.js +1939 -0
  258. package/dist/interview/mock-response-generator.d.ts +7 -0
  259. package/dist/interview/mock-response-generator.js +102 -0
  260. package/dist/interview/orchestrator.d.ts +237 -0
  261. package/dist/interview/orchestrator.js +1296 -0
  262. package/dist/interview/rate-limiter.d.ts +15 -0
  263. package/dist/interview/rate-limiter.js +55 -0
  264. package/dist/interview/response-validator.d.ts +10 -0
  265. package/dist/interview/response-validator.js +132 -0
  266. package/dist/interview/schema-inferrer.d.ts +8 -0
  267. package/dist/interview/schema-inferrer.js +71 -0
  268. package/dist/interview/schema-test-generator.d.ts +71 -0
  269. package/dist/interview/schema-test-generator.js +834 -0
  270. package/dist/interview/smart-value-generator.d.ts +155 -0
  271. package/dist/interview/smart-value-generator.js +554 -0
  272. package/dist/interview/stateful-test-runner.d.ts +19 -0
  273. package/dist/interview/stateful-test-runner.js +106 -0
  274. package/dist/interview/types.d.ts +561 -0
  275. package/dist/interview/types.js +2 -0
  276. package/dist/llm/anthropic.d.ts +41 -0
  277. package/dist/llm/anthropic.js +355 -0
  278. package/dist/llm/client.d.ts +123 -0
  279. package/dist/llm/client.js +42 -0
  280. package/dist/llm/factory.d.ts +38 -0
  281. package/dist/llm/factory.js +145 -0
  282. package/dist/llm/fallback.d.ts +140 -0
  283. package/dist/llm/fallback.js +379 -0
  284. package/dist/llm/index.d.ts +18 -0
  285. package/dist/llm/index.js +15 -0
  286. package/dist/llm/ollama.d.ts +37 -0
  287. package/dist/llm/ollama.js +330 -0
  288. package/dist/llm/openai.d.ts +25 -0
  289. package/dist/llm/openai.js +320 -0
  290. package/dist/llm/token-budget.d.ts +161 -0
  291. package/dist/llm/token-budget.js +395 -0
  292. package/dist/logging/logger.d.ts +70 -0
  293. package/dist/logging/logger.js +130 -0
  294. package/dist/metrics/collector.d.ts +106 -0
  295. package/dist/metrics/collector.js +547 -0
  296. package/dist/metrics/index.d.ts +7 -0
  297. package/dist/metrics/index.js +7 -0
  298. package/dist/metrics/prometheus.d.ts +20 -0
  299. package/dist/metrics/prometheus.js +241 -0
  300. package/dist/metrics/types.d.ts +209 -0
  301. package/dist/metrics/types.js +5 -0
  302. package/dist/persona/builtins.d.ts +54 -0
  303. package/dist/persona/builtins.js +219 -0
  304. package/dist/persona/index.d.ts +8 -0
  305. package/dist/persona/index.js +8 -0
  306. package/dist/persona/loader.d.ts +30 -0
  307. package/dist/persona/loader.js +190 -0
  308. package/dist/persona/types.d.ts +144 -0
  309. package/dist/persona/types.js +5 -0
  310. package/dist/persona/validation.d.ts +94 -0
  311. package/dist/persona/validation.js +332 -0
  312. package/dist/prompts/index.d.ts +5 -0
  313. package/dist/prompts/index.js +5 -0
  314. package/dist/prompts/templates.d.ts +180 -0
  315. package/dist/prompts/templates.js +431 -0
  316. package/dist/registry/client.d.ts +49 -0
  317. package/dist/registry/client.js +191 -0
  318. package/dist/registry/index.d.ts +7 -0
  319. package/dist/registry/index.js +6 -0
  320. package/dist/registry/types.d.ts +140 -0
  321. package/dist/registry/types.js +6 -0
  322. package/dist/scenarios/evaluator.d.ts +43 -0
  323. package/dist/scenarios/evaluator.js +206 -0
  324. package/dist/scenarios/index.d.ts +10 -0
  325. package/dist/scenarios/index.js +9 -0
  326. package/dist/scenarios/loader.d.ts +20 -0
  327. package/dist/scenarios/loader.js +285 -0
  328. package/dist/scenarios/types.d.ts +153 -0
  329. package/dist/scenarios/types.js +8 -0
  330. package/dist/security/index.d.ts +17 -0
  331. package/dist/security/index.js +18 -0
  332. package/dist/security/payloads.d.ts +61 -0
  333. package/dist/security/payloads.js +268 -0
  334. package/dist/security/security-tester.d.ts +42 -0
  335. package/dist/security/security-tester.js +582 -0
  336. package/dist/security/types.d.ts +166 -0
  337. package/dist/security/types.js +8 -0
  338. package/dist/transport/base-transport.d.ts +59 -0
  339. package/dist/transport/base-transport.js +38 -0
  340. package/dist/transport/http-transport.d.ts +67 -0
  341. package/dist/transport/http-transport.js +238 -0
  342. package/dist/transport/mcp-client.d.ts +141 -0
  343. package/dist/transport/mcp-client.js +496 -0
  344. package/dist/transport/sse-transport.d.ts +88 -0
  345. package/dist/transport/sse-transport.js +316 -0
  346. package/dist/transport/stdio-transport.d.ts +43 -0
  347. package/dist/transport/stdio-transport.js +238 -0
  348. package/dist/transport/types.d.ts +125 -0
  349. package/dist/transport/types.js +16 -0
  350. package/dist/utils/concurrency.d.ts +123 -0
  351. package/dist/utils/concurrency.js +213 -0
  352. package/dist/utils/formatters.d.ts +16 -0
  353. package/dist/utils/formatters.js +37 -0
  354. package/dist/utils/index.d.ts +8 -0
  355. package/dist/utils/index.js +8 -0
  356. package/dist/utils/jsonpath.d.ts +87 -0
  357. package/dist/utils/jsonpath.js +326 -0
  358. package/dist/utils/markdown.d.ts +113 -0
  359. package/dist/utils/markdown.js +265 -0
  360. package/dist/utils/network.d.ts +14 -0
  361. package/dist/utils/network.js +17 -0
  362. package/dist/utils/sanitize.d.ts +92 -0
  363. package/dist/utils/sanitize.js +191 -0
  364. package/dist/utils/semantic.d.ts +194 -0
  365. package/dist/utils/semantic.js +1051 -0
  366. package/dist/utils/smart-truncate.d.ts +94 -0
  367. package/dist/utils/smart-truncate.js +361 -0
  368. package/dist/utils/timeout.d.ts +153 -0
  369. package/dist/utils/timeout.js +205 -0
  370. package/dist/utils/yaml-parser.d.ts +58 -0
  371. package/dist/utils/yaml-parser.js +86 -0
  372. package/dist/validation/index.d.ts +32 -0
  373. package/dist/validation/index.js +32 -0
  374. package/dist/validation/semantic-test-generator.d.ts +50 -0
  375. package/dist/validation/semantic-test-generator.js +176 -0
  376. package/dist/validation/semantic-types.d.ts +66 -0
  377. package/dist/validation/semantic-types.js +94 -0
  378. package/dist/validation/semantic-validator.d.ts +38 -0
  379. package/dist/validation/semantic-validator.js +340 -0
  380. package/dist/verification/index.d.ts +6 -0
  381. package/dist/verification/index.js +5 -0
  382. package/dist/verification/types.d.ts +133 -0
  383. package/dist/verification/types.js +5 -0
  384. package/dist/verification/verifier.d.ts +30 -0
  385. package/dist/verification/verifier.js +309 -0
  386. package/dist/version.d.ts +19 -0
  387. package/dist/version.js +48 -0
  388. package/dist/workflow/auto-generator.d.ts +27 -0
  389. package/dist/workflow/auto-generator.js +513 -0
  390. package/dist/workflow/discovery.d.ts +40 -0
  391. package/dist/workflow/discovery.js +195 -0
  392. package/dist/workflow/executor.d.ts +82 -0
  393. package/dist/workflow/executor.js +611 -0
  394. package/dist/workflow/index.d.ts +10 -0
  395. package/dist/workflow/index.js +10 -0
  396. package/dist/workflow/loader.d.ts +24 -0
  397. package/dist/workflow/loader.js +194 -0
  398. package/dist/workflow/state-tracker.d.ts +98 -0
  399. package/dist/workflow/state-tracker.js +424 -0
  400. package/dist/workflow/types.d.ts +337 -0
  401. package/dist/workflow/types.js +5 -0
  402. package/package.json +94 -0
  403. package/schemas/bellwether-check.schema.json +651 -0
@@ -0,0 +1,545 @@
1
+ /**
2
+ * Mock cloud client for LOCAL DEVELOPMENT AND TESTING ONLY.
3
+ *
4
+ * ⚠️ WARNING: This is NOT a production implementation!
5
+ *
6
+ * This mock client:
7
+ * - Stores data locally in ~/.bellwether/mock-cloud/ as JSON files
8
+ * - Simulates cloud API responses for development purposes
9
+ * - Does NOT sync data to any remote server
10
+ * - Should ONLY be used with mock sessions (sess_mock_*)
11
+ *
12
+ * For production use, connect to the real Bellwether Cloud API.
13
+ *
14
+ * Usage:
15
+ * bellwether login --mock # Creates a mock session for development
16
+ */
17
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync } from 'fs';
18
+ import { join } from 'path';
19
+ import { homedir } from 'os';
20
+ import { randomBytes } from 'crypto';
21
+ import { isMockSession, MOCK_SESSION_PREFIX } from './auth.js';
22
+ import * as output from '../cli/output.js';
23
+ import { TIME_CONSTANTS } from '../constants.js';
24
+ /**
25
+ * Directory for mock cloud storage.
26
+ */
27
+ const MOCK_DATA_DIR = join(homedir(), '.bellwether', 'mock-cloud');
28
+ /**
29
+ * File for storing projects.
30
+ */
31
+ const PROJECTS_FILE = 'projects.json';
32
+ /**
33
+ * Generate a unique ID using cryptographically secure random bytes.
34
+ * Format: {prefix}_{timestamp}_{random}
35
+ * Example: proj_1a2b3c4d_e5f6a7b8
36
+ */
37
+ function generateId(prefix) {
38
+ const timestamp = Date.now().toString(36);
39
+ const random = randomBytes(8).toString('hex');
40
+ return `${prefix}_${timestamp}_${random}`;
41
+ }
42
+ /**
43
+ * Mock cloud client implementation for development and testing.
44
+ *
45
+ * ⚠️ DEVELOPMENT ONLY - NOT FOR PRODUCTION USE!
46
+ *
47
+ * This client:
48
+ * - Stores all data locally in ~/.bellwether/mock-cloud/
49
+ * - Provides a full implementation of the BellwetherCloudClient interface
50
+ * - Does NOT communicate with any remote servers
51
+ * - Should only be used with mock sessions (generated via `bellwether login --mock`)
52
+ *
53
+ * Features that work differently in mock mode:
54
+ * - Badge URLs point to shields.io for display purposes only
55
+ * - Project URLs are local file:// paths
56
+ * - No data synchronization across machines
57
+ *
58
+ * For production deployments, use the real CloudClient with proper authentication.
59
+ */
60
+ export class MockCloudClient {
61
+ dataDir;
62
+ sessionToken;
63
+ /**
64
+ * Create a new MockCloudClient.
65
+ *
66
+ * @param sessionToken - A mock session token (must start with 'sess_mock_')
67
+ */
68
+ constructor(sessionToken) {
69
+ this.dataDir = MOCK_DATA_DIR;
70
+ this.sessionToken = sessionToken ?? null;
71
+ this.ensureDataDir();
72
+ // Log warning if used with non-mock session
73
+ if (sessionToken && !isMockSession(sessionToken)) {
74
+ output.warn('Warning: MockCloudClient instantiated with non-mock session token.');
75
+ output.warn('This client stores data locally and does not sync to the cloud.');
76
+ }
77
+ }
78
+ /**
79
+ * Ensure the mock data directory exists.
80
+ */
81
+ ensureDataDir() {
82
+ if (!existsSync(this.dataDir)) {
83
+ mkdirSync(this.dataDir, { recursive: true });
84
+ }
85
+ }
86
+ /**
87
+ * Get path to projects file.
88
+ */
89
+ get projectsFile() {
90
+ return join(this.dataDir, PROJECTS_FILE);
91
+ }
92
+ /**
93
+ * Get path to baselines file for a project.
94
+ */
95
+ getBaselinesFile(projectId) {
96
+ return join(this.dataDir, `${projectId}-baselines.json`);
97
+ }
98
+ /**
99
+ * Get path to a specific baseline data file.
100
+ */
101
+ getBaselineDataFile(baselineId) {
102
+ return join(this.dataDir, `${baselineId}.json`);
103
+ }
104
+ /**
105
+ * Load projects from storage.
106
+ */
107
+ loadProjects() {
108
+ if (!existsSync(this.projectsFile)) {
109
+ return [];
110
+ }
111
+ try {
112
+ const content = readFileSync(this.projectsFile, 'utf-8');
113
+ return JSON.parse(content);
114
+ }
115
+ catch {
116
+ return [];
117
+ }
118
+ }
119
+ /**
120
+ * Save projects to storage.
121
+ */
122
+ saveProjects(projects) {
123
+ writeFileSync(this.projectsFile, JSON.stringify(projects, null, 2));
124
+ }
125
+ /**
126
+ * Load baselines for a project.
127
+ */
128
+ loadBaselines(projectId) {
129
+ const file = this.getBaselinesFile(projectId);
130
+ if (!existsSync(file)) {
131
+ return [];
132
+ }
133
+ try {
134
+ const content = readFileSync(file, 'utf-8');
135
+ return JSON.parse(content);
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ }
141
+ /**
142
+ * Save baselines for a project.
143
+ */
144
+ saveBaselines(projectId, baselines) {
145
+ const file = this.getBaselinesFile(projectId);
146
+ writeFileSync(file, JSON.stringify(baselines, null, 2));
147
+ }
148
+ // ============================================================================
149
+ // BellwetherCloudClient Implementation
150
+ // ============================================================================
151
+ isAuthenticated() {
152
+ if (!this.sessionToken) {
153
+ return false;
154
+ }
155
+ // For mock client, accept any mock session
156
+ return isMockSession(this.sessionToken);
157
+ }
158
+ async whoami() {
159
+ if (!this.isAuthenticated()) {
160
+ return null;
161
+ }
162
+ // Extract "user" from mock session for display
163
+ // Session format: sess_mock_<user>_<random>
164
+ const session = this.sessionToken ?? '';
165
+ const parts = session.split('_');
166
+ const user = parts.length >= 3 ? parts[2] : 'developer';
167
+ return {
168
+ id: 'usr_mock_' + user,
169
+ email: `${user}@localhost`,
170
+ githubLogin: user,
171
+ githubAvatarUrl: null,
172
+ githubName: user,
173
+ plan: 'free',
174
+ };
175
+ }
176
+ async listProjects() {
177
+ if (!this.isAuthenticated()) {
178
+ throw new Error('Not authenticated');
179
+ }
180
+ return this.loadProjects();
181
+ }
182
+ async createProject(name, serverCommand) {
183
+ if (!this.isAuthenticated()) {
184
+ throw new Error('Not authenticated');
185
+ }
186
+ const projects = this.loadProjects();
187
+ const project = {
188
+ id: generateId('proj'),
189
+ name,
190
+ serverCommand,
191
+ createdAt: new Date().toISOString(),
192
+ isPublic: false,
193
+ baselineCount: 0,
194
+ };
195
+ projects.push(project);
196
+ this.saveProjects(projects);
197
+ return project;
198
+ }
199
+ async getProject(projectId) {
200
+ if (!this.isAuthenticated()) {
201
+ throw new Error('Not authenticated');
202
+ }
203
+ const projects = this.loadProjects();
204
+ return projects.find((p) => p.id === projectId) ?? null;
205
+ }
206
+ async deleteProject(projectId) {
207
+ if (!this.isAuthenticated()) {
208
+ throw new Error('Not authenticated');
209
+ }
210
+ const projects = this.loadProjects();
211
+ const index = projects.findIndex((p) => p.id === projectId);
212
+ if (index === -1) {
213
+ throw new Error(`Project not found: ${projectId}`);
214
+ }
215
+ // Remove project
216
+ projects.splice(index, 1);
217
+ this.saveProjects(projects);
218
+ // Clean up baselines
219
+ const baselinesFile = this.getBaselinesFile(projectId);
220
+ if (existsSync(baselinesFile)) {
221
+ // Delete baseline data files first
222
+ const baselines = this.loadBaselines(projectId);
223
+ for (const baseline of baselines) {
224
+ const dataFile = this.getBaselineDataFile(baseline.id);
225
+ if (existsSync(dataFile)) {
226
+ unlinkSync(dataFile);
227
+ }
228
+ }
229
+ unlinkSync(baselinesFile);
230
+ }
231
+ }
232
+ async uploadBaseline(projectId, baseline) {
233
+ if (!this.isAuthenticated()) {
234
+ throw new Error('Not authenticated');
235
+ }
236
+ // Verify project exists
237
+ const projects = this.loadProjects();
238
+ const projectIndex = projects.findIndex((p) => p.id === projectId);
239
+ if (projectIndex === -1) {
240
+ throw new Error(`Project not found: ${projectId}`);
241
+ }
242
+ // Load existing baselines
243
+ const baselines = this.loadBaselines(projectId);
244
+ const version = baselines.length + 1;
245
+ const baselineId = generateId('bl');
246
+ // Create baseline version record
247
+ const baselineVersion = {
248
+ id: baselineId,
249
+ projectId,
250
+ version,
251
+ mode: baseline.metadata.mode,
252
+ uploadedAt: new Date().toISOString(),
253
+ cliVersion: baseline.metadata.cliVersion,
254
+ hash: baseline.hash,
255
+ // BaselineMetadata has specific typed fields, but BaselineVersion.metadata
256
+ // is a generic Record<string, unknown> for flexibility. Double cast required.
257
+ metadata: baseline.metadata,
258
+ };
259
+ // Save baseline version record
260
+ baselines.push(baselineVersion);
261
+ this.saveBaselines(projectId, baselines);
262
+ // Save full baseline data
263
+ const dataFile = this.getBaselineDataFile(baselineId);
264
+ writeFileSync(dataFile, JSON.stringify(baseline, null, 2));
265
+ // Update project
266
+ const project = projects[projectIndex];
267
+ project.baselineCount = version;
268
+ project.lastUploadAt = new Date().toISOString();
269
+ this.saveProjects(projects);
270
+ // Build result
271
+ const viewUrl = `file://${dataFile}`;
272
+ const diffUrl = version > 1 ? `mock://diff/${projectId}/${version - 1}/${version}` : undefined;
273
+ return {
274
+ baselineId,
275
+ version,
276
+ projectId,
277
+ viewUrl,
278
+ diffUrl,
279
+ };
280
+ }
281
+ async getHistory(projectId, limit = 10) {
282
+ if (!this.isAuthenticated()) {
283
+ throw new Error('Not authenticated');
284
+ }
285
+ // Verify project exists
286
+ const project = await this.getProject(projectId);
287
+ if (!project) {
288
+ throw new Error(`Project not found: ${projectId}`);
289
+ }
290
+ const baselines = this.loadBaselines(projectId);
291
+ // Return most recent first, limited
292
+ return baselines.slice(-limit).reverse();
293
+ }
294
+ async getBaseline(baselineId) {
295
+ if (!this.isAuthenticated()) {
296
+ throw new Error('Not authenticated');
297
+ }
298
+ const dataFile = this.getBaselineDataFile(baselineId);
299
+ if (!existsSync(dataFile)) {
300
+ return null;
301
+ }
302
+ try {
303
+ const content = readFileSync(dataFile, 'utf-8');
304
+ return JSON.parse(content);
305
+ }
306
+ catch {
307
+ return null;
308
+ }
309
+ }
310
+ async getDiff(projectId, fromVersion, toVersion) {
311
+ if (!this.isAuthenticated()) {
312
+ throw new Error('Not authenticated');
313
+ }
314
+ // Load baselines
315
+ const baselines = this.loadBaselines(projectId);
316
+ const fromBaseline = baselines.find((b) => b.version === fromVersion);
317
+ const toBaseline = baselines.find((b) => b.version === toVersion);
318
+ if (!fromBaseline || !toBaseline) {
319
+ throw new Error(`Baseline version not found: ${fromVersion} or ${toVersion}`);
320
+ }
321
+ // Load full baseline data
322
+ const fromData = await this.getBaseline(fromBaseline.id);
323
+ const toData = await this.getBaseline(toBaseline.id);
324
+ if (!fromData || !toData) {
325
+ throw new Error('Failed to load baseline data');
326
+ }
327
+ // Compute diff
328
+ return this.computeDiff(fromData, toData);
329
+ }
330
+ async getLatestDiff(projectId) {
331
+ if (!this.isAuthenticated()) {
332
+ throw new Error('Not authenticated');
333
+ }
334
+ const baselines = this.loadBaselines(projectId);
335
+ if (baselines.length < 2) {
336
+ return null;
337
+ }
338
+ // Get last two versions
339
+ const toVersion = baselines[baselines.length - 1].version;
340
+ const fromVersion = baselines[baselines.length - 2].version;
341
+ return this.getDiff(projectId, fromVersion, toVersion);
342
+ }
343
+ async getBadgeInfo(projectId) {
344
+ // Note: Badge info is public and doesn't require authentication
345
+ const project = this.loadProjects().find((p) => p.id === projectId);
346
+ if (!project) {
347
+ return null;
348
+ }
349
+ const baselines = this.loadBaselines(projectId);
350
+ const latestBaseline = baselines.length > 0 ? baselines[baselines.length - 1] : null;
351
+ // Determine status based on latest diff
352
+ let status = 'unknown';
353
+ let statusText = 'Not verified';
354
+ if (baselines.length === 0) {
355
+ status = 'unknown';
356
+ statusText = 'No baseline';
357
+ }
358
+ else if (baselines.length === 1) {
359
+ status = 'verified';
360
+ statusText = 'Verified';
361
+ }
362
+ else {
363
+ // Check drift between last two versions
364
+ const diff = await this.getLatestDiff(projectId);
365
+ if (diff) {
366
+ if (diff.severity === 'none' || diff.severity === 'info') {
367
+ status = 'verified';
368
+ statusText = 'Stable';
369
+ }
370
+ else if (diff.severity === 'warning') {
371
+ status = 'drift';
372
+ statusText = 'Drift detected';
373
+ }
374
+ else {
375
+ status = 'failing';
376
+ statusText = 'Breaking changes';
377
+ }
378
+ }
379
+ else {
380
+ status = 'verified';
381
+ statusText = 'Verified';
382
+ }
383
+ }
384
+ // Badge URL - using shields.io format for mock
385
+ const color = status === 'verified' ? 'brightgreen' : status === 'drift' ? 'yellow' : status === 'failing' ? 'red' : 'lightgrey';
386
+ const badgeUrl = `https://img.shields.io/badge/bellwether-${encodeURIComponent(statusText)}-${color}`;
387
+ // Generate markdown
388
+ const projectUrl = `https://bellwether.sh/projects/${projectId}`;
389
+ const markdown = `[![Bellwether](${badgeUrl})](${projectUrl})`;
390
+ return {
391
+ projectId,
392
+ projectName: project.name,
393
+ status,
394
+ statusText,
395
+ badgeUrl,
396
+ markdown,
397
+ lastVerified: latestBaseline?.uploadedAt,
398
+ latestVersion: latestBaseline?.version,
399
+ };
400
+ }
401
+ async submitVerification(projectId, result, report) {
402
+ if (!this.isAuthenticated()) {
403
+ throw new Error('Not authenticated');
404
+ }
405
+ // Verify project exists
406
+ const project = this.loadProjects().find((p) => p.id === projectId);
407
+ if (!project) {
408
+ throw new Error(`Project not found: ${projectId}`);
409
+ }
410
+ // Generate verification ID
411
+ const verificationId = generateId('ver');
412
+ // Save verification data to mock storage
413
+ const verificationData = {
414
+ id: verificationId,
415
+ projectId,
416
+ ...result,
417
+ report: report ?? null,
418
+ createdAt: new Date().toISOString(),
419
+ };
420
+ const verificationFile = join(this.dataDir, `${verificationId}.json`);
421
+ writeFileSync(verificationFile, JSON.stringify(verificationData, null, 2));
422
+ // Also save to a verifications list file for the project
423
+ const verificationsFile = join(this.dataDir, `${projectId}-verifications.json`);
424
+ let verifications = [];
425
+ if (existsSync(verificationsFile)) {
426
+ try {
427
+ verifications = JSON.parse(readFileSync(verificationsFile, 'utf-8'));
428
+ }
429
+ catch {
430
+ verifications = [];
431
+ }
432
+ }
433
+ verifications.push({ id: verificationId, createdAt: verificationData.createdAt });
434
+ writeFileSync(verificationsFile, JSON.stringify(verifications, null, 2));
435
+ return {
436
+ verificationId,
437
+ projectId,
438
+ viewUrl: `file://${verificationFile}`,
439
+ };
440
+ }
441
+ // ============================================================================
442
+ // Diff Computation
443
+ // ============================================================================
444
+ /**
445
+ * Compute diff between two baselines.
446
+ */
447
+ computeDiff(from, to) {
448
+ // Quick check - if hashes match, no changes
449
+ if (from.hash === to.hash) {
450
+ return {
451
+ severity: 'none',
452
+ toolsAdded: 0,
453
+ toolsRemoved: 0,
454
+ toolsModified: 0,
455
+ behaviorChanges: 0,
456
+ };
457
+ }
458
+ // Get tool names
459
+ const fromTools = new Set(from.capabilities.tools.map((t) => t.name));
460
+ const toTools = new Set(to.capabilities.tools.map((t) => t.name));
461
+ // Count additions and removals
462
+ const toolsAdded = [...toTools].filter((t) => !fromTools.has(t)).length;
463
+ const toolsRemoved = [...fromTools].filter((t) => !toTools.has(t)).length;
464
+ // Count modifications (tools in both with different schema hash)
465
+ let toolsModified = 0;
466
+ const fromToolMap = new Map(from.capabilities.tools.map((t) => [t.name, t]));
467
+ const toToolMap = new Map(to.capabilities.tools.map((t) => [t.name, t]));
468
+ for (const [name, fromTool] of fromToolMap) {
469
+ const toTool = toToolMap.get(name);
470
+ if (toTool && fromTool.schemaHash !== toTool.schemaHash) {
471
+ toolsModified++;
472
+ }
473
+ }
474
+ // Count behavior changes from assertions
475
+ const fromAssertions = new Set(from.assertions.map((a) => `${a.tool}:${a.condition}`));
476
+ const toAssertions = new Set(to.assertions.map((a) => `${a.tool}:${a.condition}`));
477
+ let behaviorChanges = 0;
478
+ for (const a of toAssertions) {
479
+ if (!fromAssertions.has(a)) {
480
+ behaviorChanges++;
481
+ }
482
+ }
483
+ for (const a of fromAssertions) {
484
+ if (!toAssertions.has(a)) {
485
+ behaviorChanges++;
486
+ }
487
+ }
488
+ // Determine severity
489
+ let severity = 'none';
490
+ if (toolsRemoved > 0) {
491
+ severity = 'breaking';
492
+ }
493
+ else if (toolsModified > 0 || behaviorChanges > 5) {
494
+ severity = 'warning';
495
+ }
496
+ else if (toolsAdded > 0 || behaviorChanges > 0) {
497
+ severity = 'info';
498
+ }
499
+ return {
500
+ severity,
501
+ toolsAdded,
502
+ toolsRemoved,
503
+ toolsModified,
504
+ behaviorChanges,
505
+ };
506
+ }
507
+ }
508
+ /**
509
+ * Generate a mock session for development.
510
+ */
511
+ export function generateMockSession(username = 'dev') {
512
+ const random = randomBytes(16).toString('hex');
513
+ const sessionToken = `${MOCK_SESSION_PREFIX}${username}_${random}`;
514
+ return {
515
+ sessionToken,
516
+ user: {
517
+ id: `usr_mock_${username}`,
518
+ email: `${username}@localhost`,
519
+ githubLogin: username,
520
+ githubAvatarUrl: null,
521
+ githubName: username,
522
+ plan: 'free',
523
+ },
524
+ expiresAt: new Date(Date.now() + TIME_CONSTANTS.SESSION_EXPIRATION_MS).toISOString(), // 30 days
525
+ };
526
+ }
527
+ /**
528
+ * Get the mock data directory path.
529
+ */
530
+ export function getMockDataDir() {
531
+ return MOCK_DATA_DIR;
532
+ }
533
+ /**
534
+ * Clear all mock data (for testing).
535
+ */
536
+ export function clearMockData() {
537
+ if (!existsSync(MOCK_DATA_DIR)) {
538
+ return;
539
+ }
540
+ const files = readdirSync(MOCK_DATA_DIR);
541
+ for (const file of files) {
542
+ unlinkSync(join(MOCK_DATA_DIR, file));
543
+ }
544
+ }
545
+ //# sourceMappingURL=mock-client.js.map