@peakinfer/cli 1.0.133

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 (367) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.env.example +6 -0
  3. package/.github/workflows/peakinfer.yml +64 -0
  4. package/CHANGELOG.md +31 -0
  5. package/LICENSE +190 -0
  6. package/README.md +335 -0
  7. package/data/inferencemax.json +274 -0
  8. package/dist/agent-analyzer.d.ts +45 -0
  9. package/dist/agent-analyzer.d.ts.map +1 -0
  10. package/dist/agent-analyzer.js +374 -0
  11. package/dist/agent-analyzer.js.map +1 -0
  12. package/dist/agent.d.ts +76 -0
  13. package/dist/agent.d.ts.map +1 -0
  14. package/dist/agent.js +965 -0
  15. package/dist/agent.js.map +1 -0
  16. package/dist/agents/correlation-analyzer.d.ts +34 -0
  17. package/dist/agents/correlation-analyzer.d.ts.map +1 -0
  18. package/dist/agents/correlation-analyzer.js +261 -0
  19. package/dist/agents/correlation-analyzer.js.map +1 -0
  20. package/dist/agents/index.d.ts +91 -0
  21. package/dist/agents/index.d.ts.map +1 -0
  22. package/dist/agents/index.js +111 -0
  23. package/dist/agents/index.js.map +1 -0
  24. package/dist/agents/runtime-analyzer.d.ts +38 -0
  25. package/dist/agents/runtime-analyzer.d.ts.map +1 -0
  26. package/dist/agents/runtime-analyzer.js +244 -0
  27. package/dist/agents/runtime-analyzer.js.map +1 -0
  28. package/dist/analysis-types.d.ts +500 -0
  29. package/dist/analysis-types.d.ts.map +1 -0
  30. package/dist/analysis-types.js +11 -0
  31. package/dist/analysis-types.js.map +1 -0
  32. package/dist/analytics.d.ts +25 -0
  33. package/dist/analytics.d.ts.map +1 -0
  34. package/dist/analytics.js +94 -0
  35. package/dist/analytics.js.map +1 -0
  36. package/dist/analyzer.d.ts +48 -0
  37. package/dist/analyzer.d.ts.map +1 -0
  38. package/dist/analyzer.js +547 -0
  39. package/dist/analyzer.js.map +1 -0
  40. package/dist/artifacts.d.ts +44 -0
  41. package/dist/artifacts.d.ts.map +1 -0
  42. package/dist/artifacts.js +165 -0
  43. package/dist/artifacts.js.map +1 -0
  44. package/dist/benchmarks/index.d.ts +88 -0
  45. package/dist/benchmarks/index.d.ts.map +1 -0
  46. package/dist/benchmarks/index.js +205 -0
  47. package/dist/benchmarks/index.js.map +1 -0
  48. package/dist/cli.d.ts +3 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +427 -0
  51. package/dist/cli.js.map +1 -0
  52. package/dist/commands/ci.d.ts +19 -0
  53. package/dist/commands/ci.d.ts.map +1 -0
  54. package/dist/commands/ci.js +253 -0
  55. package/dist/commands/ci.js.map +1 -0
  56. package/dist/commands/config.d.ts +16 -0
  57. package/dist/commands/config.d.ts.map +1 -0
  58. package/dist/commands/config.js +249 -0
  59. package/dist/commands/config.js.map +1 -0
  60. package/dist/commands/demo.d.ts +15 -0
  61. package/dist/commands/demo.d.ts.map +1 -0
  62. package/dist/commands/demo.js +106 -0
  63. package/dist/commands/demo.js.map +1 -0
  64. package/dist/commands/export.d.ts +14 -0
  65. package/dist/commands/export.d.ts.map +1 -0
  66. package/dist/commands/export.js +209 -0
  67. package/dist/commands/export.js.map +1 -0
  68. package/dist/commands/history.d.ts +15 -0
  69. package/dist/commands/history.d.ts.map +1 -0
  70. package/dist/commands/history.js +389 -0
  71. package/dist/commands/history.js.map +1 -0
  72. package/dist/commands/template.d.ts +14 -0
  73. package/dist/commands/template.d.ts.map +1 -0
  74. package/dist/commands/template.js +341 -0
  75. package/dist/commands/template.js.map +1 -0
  76. package/dist/commands/validate-map.d.ts +12 -0
  77. package/dist/commands/validate-map.d.ts.map +1 -0
  78. package/dist/commands/validate-map.js +274 -0
  79. package/dist/commands/validate-map.js.map +1 -0
  80. package/dist/commands/whatif.d.ts +17 -0
  81. package/dist/commands/whatif.d.ts.map +1 -0
  82. package/dist/commands/whatif.js +206 -0
  83. package/dist/commands/whatif.js.map +1 -0
  84. package/dist/comparison.d.ts +38 -0
  85. package/dist/comparison.d.ts.map +1 -0
  86. package/dist/comparison.js +223 -0
  87. package/dist/comparison.js.map +1 -0
  88. package/dist/config.d.ts +42 -0
  89. package/dist/config.d.ts.map +1 -0
  90. package/dist/config.js +158 -0
  91. package/dist/config.js.map +1 -0
  92. package/dist/connectors/helicone.d.ts +9 -0
  93. package/dist/connectors/helicone.d.ts.map +1 -0
  94. package/dist/connectors/helicone.js +106 -0
  95. package/dist/connectors/helicone.js.map +1 -0
  96. package/dist/connectors/index.d.ts +37 -0
  97. package/dist/connectors/index.d.ts.map +1 -0
  98. package/dist/connectors/index.js +65 -0
  99. package/dist/connectors/index.js.map +1 -0
  100. package/dist/connectors/langsmith.d.ts +9 -0
  101. package/dist/connectors/langsmith.d.ts.map +1 -0
  102. package/dist/connectors/langsmith.js +122 -0
  103. package/dist/connectors/langsmith.js.map +1 -0
  104. package/dist/connectors/types.d.ts +83 -0
  105. package/dist/connectors/types.d.ts.map +1 -0
  106. package/dist/connectors/types.js +98 -0
  107. package/dist/connectors/types.js.map +1 -0
  108. package/dist/cost-estimator.d.ts +46 -0
  109. package/dist/cost-estimator.d.ts.map +1 -0
  110. package/dist/cost-estimator.js +104 -0
  111. package/dist/cost-estimator.js.map +1 -0
  112. package/dist/costs.d.ts +57 -0
  113. package/dist/costs.d.ts.map +1 -0
  114. package/dist/costs.js +251 -0
  115. package/dist/costs.js.map +1 -0
  116. package/dist/counterfactuals.d.ts +29 -0
  117. package/dist/counterfactuals.d.ts.map +1 -0
  118. package/dist/counterfactuals.js +448 -0
  119. package/dist/counterfactuals.js.map +1 -0
  120. package/dist/enhancement-prompts.d.ts +41 -0
  121. package/dist/enhancement-prompts.d.ts.map +1 -0
  122. package/dist/enhancement-prompts.js +88 -0
  123. package/dist/enhancement-prompts.js.map +1 -0
  124. package/dist/envelopes.d.ts +20 -0
  125. package/dist/envelopes.d.ts.map +1 -0
  126. package/dist/envelopes.js +790 -0
  127. package/dist/envelopes.js.map +1 -0
  128. package/dist/format-normalizer.d.ts +71 -0
  129. package/dist/format-normalizer.d.ts.map +1 -0
  130. package/dist/format-normalizer.js +1331 -0
  131. package/dist/format-normalizer.js.map +1 -0
  132. package/dist/history.d.ts +79 -0
  133. package/dist/history.d.ts.map +1 -0
  134. package/dist/history.js +313 -0
  135. package/dist/history.js.map +1 -0
  136. package/dist/html.d.ts +11 -0
  137. package/dist/html.d.ts.map +1 -0
  138. package/dist/html.js +463 -0
  139. package/dist/html.js.map +1 -0
  140. package/dist/impact.d.ts +42 -0
  141. package/dist/impact.d.ts.map +1 -0
  142. package/dist/impact.js +443 -0
  143. package/dist/impact.js.map +1 -0
  144. package/dist/index.d.ts +26 -0
  145. package/dist/index.d.ts.map +1 -0
  146. package/dist/index.js +34 -0
  147. package/dist/index.js.map +1 -0
  148. package/dist/insights.d.ts +5 -0
  149. package/dist/insights.d.ts.map +1 -0
  150. package/dist/insights.js +271 -0
  151. package/dist/insights.js.map +1 -0
  152. package/dist/joiner.d.ts +9 -0
  153. package/dist/joiner.d.ts.map +1 -0
  154. package/dist/joiner.js +247 -0
  155. package/dist/joiner.js.map +1 -0
  156. package/dist/orchestrator.d.ts +34 -0
  157. package/dist/orchestrator.d.ts.map +1 -0
  158. package/dist/orchestrator.js +827 -0
  159. package/dist/orchestrator.js.map +1 -0
  160. package/dist/pdf.d.ts +26 -0
  161. package/dist/pdf.d.ts.map +1 -0
  162. package/dist/pdf.js +84 -0
  163. package/dist/pdf.js.map +1 -0
  164. package/dist/prediction.d.ts +33 -0
  165. package/dist/prediction.d.ts.map +1 -0
  166. package/dist/prediction.js +316 -0
  167. package/dist/prediction.js.map +1 -0
  168. package/dist/prompts/loader.d.ts +38 -0
  169. package/dist/prompts/loader.d.ts.map +1 -0
  170. package/dist/prompts/loader.js +60 -0
  171. package/dist/prompts/loader.js.map +1 -0
  172. package/dist/renderer.d.ts +64 -0
  173. package/dist/renderer.d.ts.map +1 -0
  174. package/dist/renderer.js +923 -0
  175. package/dist/renderer.js.map +1 -0
  176. package/dist/runid.d.ts +57 -0
  177. package/dist/runid.d.ts.map +1 -0
  178. package/dist/runid.js +199 -0
  179. package/dist/runid.js.map +1 -0
  180. package/dist/runtime.d.ts +29 -0
  181. package/dist/runtime.d.ts.map +1 -0
  182. package/dist/runtime.js +366 -0
  183. package/dist/runtime.js.map +1 -0
  184. package/dist/scanner.d.ts +11 -0
  185. package/dist/scanner.d.ts.map +1 -0
  186. package/dist/scanner.js +426 -0
  187. package/dist/scanner.js.map +1 -0
  188. package/dist/templates.d.ts +120 -0
  189. package/dist/templates.d.ts.map +1 -0
  190. package/dist/templates.js +429 -0
  191. package/dist/templates.js.map +1 -0
  192. package/dist/tools/index.d.ts +153 -0
  193. package/dist/tools/index.d.ts.map +1 -0
  194. package/dist/tools/index.js +177 -0
  195. package/dist/tools/index.js.map +1 -0
  196. package/dist/types.d.ts +3647 -0
  197. package/dist/types.d.ts.map +1 -0
  198. package/dist/types.js +703 -0
  199. package/dist/types.js.map +1 -0
  200. package/dist/version.d.ts +7 -0
  201. package/dist/version.d.ts.map +1 -0
  202. package/dist/version.js +23 -0
  203. package/dist/version.js.map +1 -0
  204. package/docs/demo-guide.md +423 -0
  205. package/docs/events-format.md +295 -0
  206. package/docs/inferencemap-spec.md +344 -0
  207. package/docs/migration-v2.md +293 -0
  208. package/fixtures/demo/precomputed.json +142 -0
  209. package/fixtures/demo-project/README.md +52 -0
  210. package/fixtures/demo-project/ai-service.ts +65 -0
  211. package/fixtures/demo-project/sample-events.jsonl +15 -0
  212. package/fixtures/demo-project/src/ai-service.ts +128 -0
  213. package/fixtures/demo-project/src/llm-client.ts +155 -0
  214. package/package.json +65 -0
  215. package/prompts/agent-analyzer.yaml +47 -0
  216. package/prompts/ci-gate.yaml +98 -0
  217. package/prompts/correlation-analyzer.yaml +178 -0
  218. package/prompts/format-normalizer.yaml +46 -0
  219. package/prompts/peak-performance.yaml +180 -0
  220. package/prompts/pr-comment.yaml +111 -0
  221. package/prompts/runtime-analyzer.yaml +189 -0
  222. package/prompts/unified-analyzer.yaml +241 -0
  223. package/schemas/inference-map.v0.1.json +215 -0
  224. package/scripts/benchmark.ts +394 -0
  225. package/scripts/demo-v1.5.sh +158 -0
  226. package/scripts/sync-from-site.sh +197 -0
  227. package/scripts/validate-sync.sh +178 -0
  228. package/src/agent-analyzer.ts +481 -0
  229. package/src/agent.ts +1232 -0
  230. package/src/agents/correlation-analyzer.ts +353 -0
  231. package/src/agents/index.ts +235 -0
  232. package/src/agents/runtime-analyzer.ts +343 -0
  233. package/src/analysis-types.ts +558 -0
  234. package/src/analytics.ts +100 -0
  235. package/src/analyzer.ts +692 -0
  236. package/src/artifacts.ts +218 -0
  237. package/src/benchmarks/index.ts +309 -0
  238. package/src/cli.ts +503 -0
  239. package/src/commands/ci.ts +336 -0
  240. package/src/commands/config.ts +288 -0
  241. package/src/commands/demo.ts +175 -0
  242. package/src/commands/export.ts +297 -0
  243. package/src/commands/history.ts +425 -0
  244. package/src/commands/template.ts +385 -0
  245. package/src/commands/validate-map.ts +324 -0
  246. package/src/commands/whatif.ts +272 -0
  247. package/src/comparison.ts +283 -0
  248. package/src/config.ts +188 -0
  249. package/src/connectors/helicone.ts +164 -0
  250. package/src/connectors/index.ts +93 -0
  251. package/src/connectors/langsmith.ts +179 -0
  252. package/src/connectors/types.ts +180 -0
  253. package/src/cost-estimator.ts +146 -0
  254. package/src/costs.ts +347 -0
  255. package/src/counterfactuals.ts +516 -0
  256. package/src/enhancement-prompts.ts +118 -0
  257. package/src/envelopes.ts +814 -0
  258. package/src/format-normalizer.ts +1486 -0
  259. package/src/history.ts +400 -0
  260. package/src/html.ts +512 -0
  261. package/src/impact.ts +522 -0
  262. package/src/index.ts +83 -0
  263. package/src/insights.ts +341 -0
  264. package/src/joiner.ts +289 -0
  265. package/src/orchestrator.ts +1015 -0
  266. package/src/pdf.ts +110 -0
  267. package/src/prediction.ts +392 -0
  268. package/src/prompts/loader.ts +88 -0
  269. package/src/renderer.ts +1045 -0
  270. package/src/runid.ts +261 -0
  271. package/src/runtime.ts +450 -0
  272. package/src/scanner.ts +508 -0
  273. package/src/templates.ts +561 -0
  274. package/src/tools/index.ts +214 -0
  275. package/src/types.ts +873 -0
  276. package/src/version.ts +24 -0
  277. package/templates/context-accumulation.yaml +23 -0
  278. package/templates/cost-concentration.yaml +20 -0
  279. package/templates/dead-code.yaml +20 -0
  280. package/templates/latency-explainer.yaml +23 -0
  281. package/templates/optimizations/ab-testing-framework.yaml +74 -0
  282. package/templates/optimizations/api-gateway-optimization.yaml +81 -0
  283. package/templates/optimizations/api-model-routing-strategy.yaml +126 -0
  284. package/templates/optimizations/auto-scaling-optimization.yaml +85 -0
  285. package/templates/optimizations/batch-utilization-diagnostic.yaml +142 -0
  286. package/templates/optimizations/comprehensive-apm.yaml +76 -0
  287. package/templates/optimizations/context-window-optimization.yaml +91 -0
  288. package/templates/optimizations/cost-sensitive-batch-processing.yaml +77 -0
  289. package/templates/optimizations/distributed-training-optimization.yaml +77 -0
  290. package/templates/optimizations/document-analysis-edge.yaml +77 -0
  291. package/templates/optimizations/document-pipeline-optimization.yaml +78 -0
  292. package/templates/optimizations/domain-specific-distillation.yaml +78 -0
  293. package/templates/optimizations/error-handling-optimization.yaml +76 -0
  294. package/templates/optimizations/gptq-4bit-quantization.yaml +96 -0
  295. package/templates/optimizations/long-context-memory-management.yaml +78 -0
  296. package/templates/optimizations/max-tokens-optimization.yaml +76 -0
  297. package/templates/optimizations/memory-bandwidth-optimization.yaml +73 -0
  298. package/templates/optimizations/multi-framework-resilience.yaml +75 -0
  299. package/templates/optimizations/multi-tenant-optimization.yaml +75 -0
  300. package/templates/optimizations/prompt-caching-optimization.yaml +143 -0
  301. package/templates/optimizations/pytorch-to-onnx-migration.yaml +109 -0
  302. package/templates/optimizations/quality-monitoring.yaml +74 -0
  303. package/templates/optimizations/realtime-budget-controls.yaml +74 -0
  304. package/templates/optimizations/realtime-latency-optimization.yaml +74 -0
  305. package/templates/optimizations/sglang-concurrency-optimization.yaml +78 -0
  306. package/templates/optimizations/smart-model-routing.yaml +96 -0
  307. package/templates/optimizations/streaming-batch-selection.yaml +167 -0
  308. package/templates/optimizations/system-prompt-optimization.yaml +75 -0
  309. package/templates/optimizations/tensorrt-llm-performance.yaml +77 -0
  310. package/templates/optimizations/vllm-high-throughput-optimization.yaml +93 -0
  311. package/templates/optimizations/vllm-migration-memory-bound.yaml +78 -0
  312. package/templates/overpowered-extraction.yaml +32 -0
  313. package/templates/overpowered-model.yaml +31 -0
  314. package/templates/prompt-bloat.yaml +24 -0
  315. package/templates/retry-explosion.yaml +28 -0
  316. package/templates/schema/insight.schema.json +113 -0
  317. package/templates/schema/optimization.schema.json +180 -0
  318. package/templates/streaming-drift.yaml +30 -0
  319. package/templates/throughput-gap.yaml +21 -0
  320. package/templates/token-underutilization.yaml +28 -0
  321. package/templates/untested-fallback.yaml +21 -0
  322. package/tests/accuracy/drift-detection.test.ts +184 -0
  323. package/tests/accuracy/false-positives.test.ts +166 -0
  324. package/tests/accuracy/templates.test.ts +205 -0
  325. package/tests/action/commands.test.ts +125 -0
  326. package/tests/action/comments.test.ts +347 -0
  327. package/tests/cli.test.ts +203 -0
  328. package/tests/comparison.test.ts +309 -0
  329. package/tests/correlation-analyzer.test.ts +534 -0
  330. package/tests/counterfactuals.test.ts +347 -0
  331. package/tests/fixtures/events/missing-id.jsonl +1 -0
  332. package/tests/fixtures/events/missing-input.jsonl +1 -0
  333. package/tests/fixtures/events/missing-latency.jsonl +1 -0
  334. package/tests/fixtures/events/missing-model.jsonl +1 -0
  335. package/tests/fixtures/events/missing-output.jsonl +1 -0
  336. package/tests/fixtures/events/missing-provider.jsonl +1 -0
  337. package/tests/fixtures/events/missing-ts.jsonl +1 -0
  338. package/tests/fixtures/events/valid.csv +3 -0
  339. package/tests/fixtures/events/valid.json +1 -0
  340. package/tests/fixtures/events/valid.jsonl +2 -0
  341. package/tests/fixtures/events/with-callsite.jsonl +1 -0
  342. package/tests/fixtures/events/with-intent.jsonl +1 -0
  343. package/tests/fixtures/events/wrong-type.jsonl +1 -0
  344. package/tests/fixtures/repos/empty/.gitkeep +0 -0
  345. package/tests/fixtures/repos/hybrid-router/router.py +35 -0
  346. package/tests/fixtures/repos/saas-anthropic/agent.ts +27 -0
  347. package/tests/fixtures/repos/saas-openai/assistant.js +33 -0
  348. package/tests/fixtures/repos/saas-openai/client.py +26 -0
  349. package/tests/fixtures/repos/self-hosted-vllm/inference.py +22 -0
  350. package/tests/github-action.test.ts +292 -0
  351. package/tests/insights.test.ts +878 -0
  352. package/tests/joiner.test.ts +168 -0
  353. package/tests/performance/action-latency.test.ts +132 -0
  354. package/tests/performance/benchmark.test.ts +189 -0
  355. package/tests/performance/cli-latency.test.ts +102 -0
  356. package/tests/pr-comment.test.ts +313 -0
  357. package/tests/prediction.test.ts +296 -0
  358. package/tests/runtime-analyzer.test.ts +375 -0
  359. package/tests/runtime.test.ts +205 -0
  360. package/tests/scanner.test.ts +122 -0
  361. package/tests/template-conformance.test.ts +526 -0
  362. package/tests/unit/cost-calculator.test.ts +303 -0
  363. package/tests/unit/credits.test.ts +180 -0
  364. package/tests/unit/inference-map.test.ts +276 -0
  365. package/tests/unit/schema.test.ts +300 -0
  366. package/tsconfig.json +20 -0
  367. package/vitest.config.ts +14 -0
package/src/cli.ts ADDED
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+ import 'dotenv/config';
3
+ import { Command } from 'commander';
4
+ import { existsSync, writeFileSync } from 'fs';
5
+ import { exec } from 'child_process';
6
+ import * as readline from 'readline';
7
+ import { Agent } from './agent.js';
8
+ import { createRenderer, renderCostEstimate, renderMaxCostExceeded } from './renderer.js';
9
+ import { estimateAnalysisCost, exceedsMaxCost } from './cost-estimator.js';
10
+ import { VERSION } from './version.js';
11
+
12
+ // v1.6 Command imports
13
+ import { registerTemplateCommands } from './commands/template.js';
14
+ import { registerConfigCommands } from './commands/config.js';
15
+ import { registerHistoryCommands } from './commands/history.js';
16
+ import { registerCICommand } from './commands/ci.js';
17
+ import { registerExportCommand } from './commands/export.js';
18
+ import { registerWhatIfCommand } from './commands/whatif.js';
19
+
20
+ // v2.0 Demo command (works offline, no API key needed)
21
+ import { registerDemoCommand } from './commands/demo.js';
22
+
23
+ // v1.9.3 Validate-map command (InferenceMap schema validation)
24
+ import { registerValidateMapCommand } from './commands/validate-map.js';
25
+
26
+ // v1.8 Analytics (respects DO_NOT_TRACK)
27
+ import { initAnalytics, track, flush } from './analytics.js';
28
+
29
+ // Initialize analytics at startup
30
+ initAnalytics();
31
+
32
+ // =============================================================================
33
+ // CONSTANTS
34
+ // =============================================================================
35
+
36
+ const DESCRIPTION = 'llm inference performance optimization';
37
+
38
+ // =============================================================================
39
+ // MAIN
40
+ // =============================================================================
41
+
42
+ const program = new Command()
43
+ .name('peakinfer')
44
+ .description(DESCRIPTION)
45
+ .version(VERSION);
46
+
47
+ // Analyze command: peakinfer analyze <path>
48
+ program
49
+ .command('analyze')
50
+ .description('analyze codebase or runtime events')
51
+ .argument('[path]', 'path to repository or events file', '.')
52
+ .option('--events <file>', 'add runtime telemetry to static analysis')
53
+ .option('--events-url <url>', 'URL to fetch runtime events')
54
+ .option('--html', 'generate html report')
55
+ .option('--pdf', 'generate pdf report')
56
+ .option('--open', 'open report in browser/viewer')
57
+ .option('--output <format>', 'output format: text (default), json, or inference-map')
58
+ .option('--out <file>', 'write output to file')
59
+ .option('--cached', 'view previous analysis (offline, no API key needed)')
60
+ .option('--verbose', 'show detailed task progress')
61
+ // Format detection options (PRD §6.4)
62
+ .option('--format <type>', 'specify runtime format: jsonl, json, csv, otel, jaeger, zipkin, langsmith, litellm')
63
+ .option('--map <mappings...>', 'field mappings: --map latency_ms=duration model=model_name')
64
+ .option('--events-map <mappings...>', 'alias for --map (field mappings for non-standard event formats)')
65
+ .option('--lenient', 'accept low-confidence field mappings')
66
+ .option('--strict', 'fail on missing required fields or unknown formats')
67
+ .option('--redact', 'redact code snippets from artifacts')
68
+ // Fix suggestions (v1.8)
69
+ .option('--fixes', 'show code fix suggestions for issues')
70
+ // Runtime connectors (v1.9.5)
71
+ .option('--runtime <source>', 'runtime data source: helicone, langsmith')
72
+ .option('--runtime-key <key>', 'API key for runtime source (or use env var)')
73
+ .option('--runtime-days <days>', 'days of runtime data to fetch', '7')
74
+ // Benchmark comparison (v1.9.5)
75
+ .option('--benchmark', 'compare to InferenceMAX benchmarks')
76
+ // Cost estimation options (v1.9.3)
77
+ .option('--estimate', 'show cost estimate before analysis')
78
+ .option('--yes', 'auto-proceed without confirmation (use with --estimate)')
79
+ .option('--max-cost <dollars>', 'skip analysis if estimated cost exceeds threshold')
80
+ // History options (v1.5)
81
+ .option('--no-history', 'skip saving run to history (disables comparison/prediction)')
82
+ .option('--compare [runId]', 'compare with previous run (default: latest)')
83
+ .option('--predict', 'generate deploy-time latency predictions')
84
+ .option('--target-p95 <ms>', 'target p95 latency for budget calculation (use with --predict)')
85
+ .action(async (path: string, options: {
86
+ events?: string;
87
+ eventsUrl?: string; // --events-url
88
+ html?: boolean;
89
+ pdf?: boolean;
90
+ open?: boolean;
91
+ output?: string;
92
+ out?: string; // --out
93
+ cached?: boolean;
94
+ verbose?: boolean;
95
+ // Format detection options
96
+ format?: string;
97
+ map?: string[];
98
+ eventsMap?: string[]; // --events-map alias for --map
99
+ lenient?: boolean;
100
+ strict?: boolean;
101
+ redact?: boolean;
102
+ // Fix suggestions (v1.8)
103
+ fixes?: boolean;
104
+ // Runtime connectors (v1.9.5)
105
+ runtime?: string;
106
+ runtimeKey?: string;
107
+ runtimeDays?: string;
108
+ // Benchmark comparison (v1.9.5)
109
+ benchmark?: boolean;
110
+ // Cost estimation options (v1.9.3)
111
+ estimate?: boolean;
112
+ yes?: boolean;
113
+ maxCost?: string;
114
+ // History options (v1.5)
115
+ history?: boolean; // Commander negates --no-history to history: false
116
+ compare?: string | boolean; // --compare or --compare <runId>
117
+ predict?: boolean; // --predict flag
118
+ targetP95?: string; // --target-p95 <ms>
119
+ }) => {
120
+ try {
121
+ // Validate path exists
122
+ if (!existsSync(path)) {
123
+ console.error(`Error: Path not found: ${path}`);
124
+ process.exit(1);
125
+ }
126
+
127
+ // Validate events file if provided
128
+ if (options.events && !existsSync(options.events)) {
129
+ console.error(`Error: Events file not found: ${options.events}`);
130
+ process.exit(1);
131
+ }
132
+
133
+ // Cost estimation (v1.9.3) - PRD Section 2.3
134
+ // Handle --estimate and --max-cost before running analysis
135
+ const maxCost = options.maxCost ? parseFloat(options.maxCost) : undefined;
136
+
137
+ if (options.estimate || maxCost !== undefined) {
138
+ try {
139
+ const estimate = await estimateAnalysisCost(path);
140
+
141
+ // Check --max-cost threshold first
142
+ if (maxCost !== undefined && exceedsMaxCost(estimate, maxCost)) {
143
+ renderMaxCostExceeded(estimate, maxCost);
144
+ process.exit(0); // Exit gracefully, not an error
145
+ }
146
+
147
+ // Show estimate if --estimate flag is used
148
+ if (options.estimate) {
149
+ renderCostEstimate(estimate);
150
+
151
+ // If --yes flag, auto-proceed
152
+ if (!options.yes) {
153
+ // Prompt user for confirmation
154
+ const proceed = await promptUserConfirmation('Proceed? [y/N] ');
155
+ if (!proceed) {
156
+ console.log('Analysis cancelled.');
157
+ process.exit(0);
158
+ }
159
+ }
160
+ }
161
+ } catch (error) {
162
+ // If estimation fails, warn but continue (don't block analysis)
163
+ if (options.verbose) {
164
+ console.warn(`Warning: Cost estimation failed: ${error instanceof Error ? error.message : 'unknown error'}`);
165
+ }
166
+ }
167
+ }
168
+
169
+ const renderer = createRenderer({ verbose: options.verbose, showFixes: options.fixes });
170
+ renderer.renderHeader();
171
+
172
+ // Track analysis start (v1.8)
173
+ track('analysis_started', {
174
+ has_events: !!options.events,
175
+ has_runtime: !!options.runtime,
176
+ runtime_source: options.runtime,
177
+ has_benchmark: !!options.benchmark,
178
+ html: options.html,
179
+ pdf: options.pdf,
180
+ predict: options.predict,
181
+ compare: options.compare !== undefined,
182
+ });
183
+
184
+ const agent = new Agent({
185
+ onResumed: (runId) => renderer.renderResumed(runId),
186
+ onPlanReady: (plan) => renderer.renderPlan(plan),
187
+ onTaskStart: (task) => renderer.renderTaskStart(task),
188
+ onTaskComplete: (task, result) => renderer.renderTaskComplete(task, result),
189
+ onProgress: (data) => renderer.renderProgress(data),
190
+ onPartial: (warnings) => renderer.renderPartial(warnings),
191
+ onComplete: (results) => {
192
+ // Handle --output inference-map: output only InferenceMap JSON
193
+ if (options.output === 'inference-map') {
194
+ if (!results.inferenceMap) {
195
+ console.error('No InferenceMap data available');
196
+ process.exit(1);
197
+ }
198
+ const output = JSON.stringify(results.inferenceMap, null, 2);
199
+ if (options.out) {
200
+ writeFileSync(options.out, output);
201
+ console.error(`InferenceMap written to ${options.out}`);
202
+ } else {
203
+ console.log(output);
204
+ }
205
+ } else if (options.output === 'json') {
206
+ // Handle --output json: output full results as JSON
207
+ const output = JSON.stringify({
208
+ inferenceMap: results.inferenceMap,
209
+ insights: results.insights,
210
+ runtime: results.runtimeSummary,
211
+ joined: results.joined,
212
+ }, null, 2);
213
+ if (options.out) {
214
+ writeFileSync(options.out, output);
215
+ console.error(`Results written to ${options.out}`);
216
+ } else {
217
+ console.log(output);
218
+ }
219
+ } else {
220
+ // Default: render text output
221
+ renderer.renderResults(results);
222
+ }
223
+
224
+ // Benchmark comparison (v1.9.5)
225
+ // Note: Requires runtime events with latency data to compare against benchmarks
226
+ if (options.benchmark && results.inferenceMap?.callsites) {
227
+ import('./benchmarks/index.js').then(({ compareToBenchmark, formatBenchmarkComparison }) => {
228
+ console.log('\n' + '─'.repeat(60));
229
+ console.log('\nBENCHMARK COMPARISON (InferenceMAX)');
230
+ console.log('');
231
+
232
+ let hasComparisons = false;
233
+ for (const point of results.inferenceMap!.callsites) {
234
+ // Static analysis provides model info; latency requires runtime events
235
+ if (point.model) {
236
+ const comparison = compareToBenchmark(
237
+ point.id,
238
+ point.model,
239
+ {} // Latency data comes from runtime events
240
+ );
241
+ if (comparison) {
242
+ hasComparisons = true;
243
+ console.log(`[${point.file}:${point.line}]`);
244
+ console.log(formatBenchmarkComparison(comparison));
245
+ console.log('');
246
+ }
247
+ }
248
+ }
249
+
250
+ if (!hasComparisons) {
251
+ console.log('No benchmark data available for the detected models.');
252
+ console.log('Tip: Benchmarks are available for GPT-4o, Claude 3.5, Gemini, Llama, Mistral, etc.');
253
+ }
254
+ }).catch(() => {
255
+ console.error('Warning: Could not load benchmark data');
256
+ });
257
+ }
258
+
259
+ // Track analysis completion (v1.8)
260
+ track('analysis_completed', {
261
+ inference_points: results.inferenceMap?.summary?.totalCallsites || 0,
262
+ insights_count: results.insights?.length || 0,
263
+ has_runtime: !!results.events,
264
+ providers: results.inferenceMap?.summary?.providers || [],
265
+ });
266
+
267
+ // Open report if requested (prefer PDF if generated, else HTML)
268
+ if (options.open) {
269
+ if (results.pdfPath) {
270
+ openInBrowser(results.pdfPath);
271
+ } else if (results.htmlPath) {
272
+ openInBrowser(results.htmlPath);
273
+ }
274
+ }
275
+ },
276
+ onError: (error) => renderer.renderError(error),
277
+ });
278
+
279
+ // Parse field mappings from --map or --events-map option
280
+ // (--events-map is an alias for --map, consistent with GitHub Action input naming)
281
+ const fieldHints: Record<string, string> = {};
282
+ // Merge if both are provided (--map takes precedence for conflicts)
283
+ if (options.eventsMap) {
284
+ for (const mapping of options.eventsMap) {
285
+ const [target, source] = mapping.split('=');
286
+ if (target && source) {
287
+ fieldHints[target.trim()] = source.trim();
288
+ }
289
+ }
290
+ }
291
+ if (options.map) {
292
+ for (const mapping of options.map) {
293
+ const [target, source] = mapping.split('=');
294
+ if (target && source) {
295
+ fieldHints[target.trim()] = source.trim();
296
+ }
297
+ }
298
+ }
299
+
300
+ // Runtime connector integration (v1.9.5)
301
+ // If --runtime is provided, fetch events from the API
302
+ let runtimeEventsFile = options.events;
303
+ let runtimeResult: { events: unknown[]; summary: unknown } | undefined;
304
+
305
+ if (options.runtime) {
306
+ const { fetchRuntimeData, getApiKeyFromEnv, isValidSource } = await import('./connectors/index.js');
307
+
308
+ if (!isValidSource(options.runtime)) {
309
+ console.error(`Error: Unknown runtime source: ${options.runtime}. Supported: helicone, langsmith`);
310
+ process.exit(1);
311
+ }
312
+
313
+ const apiKey = options.runtimeKey || getApiKeyFromEnv(options.runtime);
314
+ if (!apiKey) {
315
+ const envVarName = options.runtime === 'helicone' ? 'HELICONE_API_KEY' : 'LANGSMITH_API_KEY';
316
+ console.error(`Error: API key required for --runtime ${options.runtime}`);
317
+ console.error(`Provide via --runtime-key or set ${envVarName} environment variable`);
318
+ process.exit(1);
319
+ }
320
+
321
+ try {
322
+ const days = parseInt(options.runtimeDays || '7', 10);
323
+ const startDate = new Date();
324
+ startDate.setDate(startDate.getDate() - days);
325
+
326
+ if (options.verbose) {
327
+ console.log(`Fetching runtime data from ${options.runtime} (last ${days} days)...`);
328
+ }
329
+
330
+ runtimeResult = await fetchRuntimeData({
331
+ source: options.runtime,
332
+ apiKey,
333
+ startDate: startDate.toISOString(),
334
+ limit: 1000,
335
+ });
336
+
337
+ if (options.verbose) {
338
+ console.log(`Fetched ${runtimeResult.events.length} events from ${options.runtime}`);
339
+ }
340
+
341
+ // Write events to a temp file for the agent
342
+ const { tmpdir } = await import('os');
343
+ const { join } = await import('path');
344
+ const tempFile = join(tmpdir(), `peakinfer-runtime-${Date.now()}.jsonl`);
345
+ const eventsJsonl = (runtimeResult.events as unknown[]).map(e => JSON.stringify(e)).join('\n');
346
+ writeFileSync(tempFile, eventsJsonl);
347
+ runtimeEventsFile = tempFile;
348
+ } catch (error) {
349
+ console.error(`Error fetching from ${options.runtime}: ${error instanceof Error ? error.message : 'Unknown error'}`);
350
+ process.exit(1);
351
+ }
352
+ }
353
+
354
+ await agent.run({
355
+ path,
356
+ events: runtimeEventsFile,
357
+ eventsUrl: options.eventsUrl, // --events-url
358
+ html: options.html || options.pdf || options.open, // Generate HTML if PDF or open requested
359
+ pdf: options.pdf,
360
+ open: options.open,
361
+ out: options.out, // --out
362
+ offline: false,
363
+ noCache: !options.cached, // --cached means use cache
364
+ verbose: options.verbose,
365
+ // Format detection options
366
+ formatHint: options.format,
367
+ fieldHints: Object.keys(fieldHints).length > 0 ? fieldHints : undefined,
368
+ lenient: options.lenient,
369
+ strict: options.strict,
370
+ redact: options.redact,
371
+ // History options (v1.5)
372
+ noHistory: options.history === false, // --no-history sets history to false
373
+ compare: options.compare !== undefined, // --compare flag was used
374
+ compareRunId: typeof options.compare === 'string' ? options.compare : undefined, // specific run ID
375
+ predict: options.predict, // --predict flag
376
+ targetP95: options.targetP95 ? parseInt(options.targetP95, 10) : undefined, // --target-p95 <ms>
377
+ });
378
+
379
+ // Flush analytics before exit (v1.8)
380
+ await flush();
381
+ } catch (error) {
382
+ // Track error and flush analytics (v1.8)
383
+ track('analysis_error', {
384
+ error_type: error instanceof Error ? error.constructor.name : 'unknown',
385
+ });
386
+ await flush();
387
+
388
+ if (error instanceof Error) {
389
+ console.error(`Error: ${error.message}`);
390
+ if (options.verbose && error.stack) {
391
+ console.error(error.stack);
392
+ }
393
+ } else {
394
+ console.error('An unexpected error occurred');
395
+ }
396
+ process.exit(1);
397
+ }
398
+ });
399
+
400
+ // =============================================================================
401
+ // REGISTER v1.6 COMMANDS
402
+ // =============================================================================
403
+
404
+ registerTemplateCommands(program);
405
+ registerConfigCommands(program);
406
+ registerHistoryCommands(program);
407
+ registerCICommand(program);
408
+ registerExportCommand(program);
409
+ registerWhatIfCommand(program);
410
+
411
+ // =============================================================================
412
+ // REGISTER v2.0 COMMANDS
413
+ // =============================================================================
414
+
415
+ registerDemoCommand(program);
416
+
417
+ // =============================================================================
418
+ // REGISTER v1.9.3 COMMANDS
419
+ // =============================================================================
420
+
421
+ registerValidateMapCommand(program);
422
+
423
+ // Custom help text (PRD-aligned, Julie Zhou style)
424
+ program.addHelpText('after', `
425
+ quick start:
426
+ peakinfer demo # see it in action (no API key needed)
427
+ peakinfer analyze . # analyze your codebase
428
+
429
+ analyze modes:
430
+ peakinfer analyze . # static: scan codebase for LLM calls
431
+ peakinfer analyze events.jsonl # runtime: analyze inference telemetry
432
+ peakinfer analyze . --events prod.jsonl # combined: static + runtime
433
+ peakinfer analyze . --events-url https://api.example.com/events # fetch events from URL
434
+ peakinfer analyze . --out results.json # write output to file
435
+ peakinfer analyze . --output inference-map # output only InferenceMap v0.1 JSON
436
+
437
+ runtime connectors (v1.9.5):
438
+ peakinfer analyze . --runtime helicone # fetch from Helicone
439
+ peakinfer analyze . --runtime langsmith # fetch from LangSmith
440
+ peakinfer analyze . --runtime helicone --runtime-days 30 # last 30 days
441
+
442
+ benchmarks (v1.9.5):
443
+ peakinfer analyze . --benchmark # compare to InferenceMAX benchmarks
444
+
445
+ cost estimation:
446
+ peakinfer analyze . --estimate # show cost estimate before analysis
447
+ peakinfer analyze . --estimate --yes # estimate + auto-proceed (for CI)
448
+ peakinfer analyze . --max-cost 10 # skip if estimated cost > $10
449
+
450
+ more commands:
451
+ peakinfer template list # browse optimization templates
452
+ peakinfer config show # view configuration
453
+ peakinfer history # view analysis history
454
+ peakinfer history compare <id1> [id2] # compare two analysis runs
455
+ peakinfer export # export results (json, prometheus)
456
+ peakinfer whatif --model gpt-4o-mini # counterfactual analysis
457
+ peakinfer ci ./src --baseline base.json # CI/CD integration
458
+ peakinfer validate-map ./map.json # validate InferenceMap schema
459
+ `);
460
+
461
+ // Parse and run
462
+ program.parse();
463
+
464
+ // =============================================================================
465
+ // HELPERS
466
+ // =============================================================================
467
+
468
+ /**
469
+ * Prompt user for Y/N confirmation.
470
+ * Returns true if user enters 'y' or 'Y', false otherwise.
471
+ */
472
+ function promptUserConfirmation(prompt: string): Promise<boolean> {
473
+ return new Promise((resolve) => {
474
+ const rl = readline.createInterface({
475
+ input: process.stdin,
476
+ output: process.stdout,
477
+ });
478
+
479
+ rl.question(prompt, (answer) => {
480
+ rl.close();
481
+ resolve(answer.toLowerCase() === 'y');
482
+ });
483
+ });
484
+ }
485
+
486
+ function openInBrowser(filePath: string): void {
487
+ const platform = process.platform;
488
+ let command: string;
489
+
490
+ if (platform === 'darwin') {
491
+ command = `open "${filePath}"`;
492
+ } else if (platform === 'win32') {
493
+ command = `start "" "${filePath}"`;
494
+ } else {
495
+ command = `xdg-open "${filePath}"`;
496
+ }
497
+
498
+ exec(command, (error) => {
499
+ if (error) {
500
+ console.error(`Could not open browser: ${error.message}`);
501
+ }
502
+ });
503
+ }