infraweaver 0.1.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 (524) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +244 -0
  3. package/dist/agents/claude.d.ts +113 -0
  4. package/dist/agents/claudePretoolGate.d.ts +137 -0
  5. package/dist/agents/dispatchMetrics.d.ts +77 -0
  6. package/dist/agents/gateServer.d.ts +7 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/nativeFsDenies.d.ts +46 -0
  9. package/dist/agents/opencode.d.ts +284 -0
  10. package/dist/agents/opencodePlugin.d.ts +96 -0
  11. package/dist/agents/opencodeShared.d.ts +40 -0
  12. package/dist/agents/postRun.d.ts +126 -0
  13. package/dist/agents/reviewer.d.ts +41 -0
  14. package/dist/agents/sessionLabeler.d.ts +97 -0
  15. package/dist/agents/shared.d.ts +246 -0
  16. package/dist/agents/subagentModels.d.ts +19 -0
  17. package/dist/agents/tokenQuota.d.ts +118 -0
  18. package/dist/agents/writeGateSource.d.ts +22 -0
  19. package/dist/agents/writePolicy.d.ts +79 -0
  20. package/dist/brand.d.ts +67 -0
  21. package/dist/cli.mjs +247846 -0
  22. package/dist/external.d.ts +227 -0
  23. package/dist/i18n/scaffolding.d.ts +59 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +247061 -0
  26. package/dist/internal/index.d.ts +18 -0
  27. package/dist/internal.js +2398 -0
  28. package/dist/lifecycle.d.ts +2 -0
  29. package/dist/main.d.ts +8 -0
  30. package/dist/mcp/arkConfig.d.ts +1 -0
  31. package/dist/mcp/assess.d.ts +157 -0
  32. package/dist/mcp/capabilityContext.d.ts +71 -0
  33. package/dist/mcp/changeSummary.d.ts +52 -0
  34. package/dist/mcp/checkSuite.d.ts +27 -0
  35. package/dist/mcp/checkout.d.ts +92 -0
  36. package/dist/mcp/comment.d.ts +127 -0
  37. package/dist/mcp/commitInfo.d.ts +11 -0
  38. package/dist/mcp/crosswalk.d.ts +178 -0
  39. package/dist/mcp/crosswalkDigest.d.ts +1 -0
  40. package/dist/mcp/cyberEssentials.d.ts +24 -0
  41. package/dist/mcp/dashboard.d.ts +125 -0
  42. package/dist/mcp/dependencies.d.ts +12 -0
  43. package/dist/mcp/frameworks.d.ts +74 -0
  44. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  45. package/dist/mcp/git.d.ts +60 -0
  46. package/dist/mcp/guardrails.d.ts +222 -0
  47. package/dist/mcp/issue.d.ts +20 -0
  48. package/dist/mcp/issueComments.d.ts +11 -0
  49. package/dist/mcp/issueEvents.d.ts +11 -0
  50. package/dist/mcp/issueInfo.d.ts +11 -0
  51. package/dist/mcp/labels.d.ts +14 -0
  52. package/dist/mcp/localContext.d.ts +26 -0
  53. package/dist/mcp/moduleExtraction.d.ts +79 -0
  54. package/dist/mcp/moduleTests.d.ts +106 -0
  55. package/dist/mcp/modules.d.ts +198 -0
  56. package/dist/mcp/output.d.ts +16 -0
  57. package/dist/mcp/pathSafety.d.ts +14 -0
  58. package/dist/mcp/policy.d.ts +50 -0
  59. package/dist/mcp/pr.d.ts +64 -0
  60. package/dist/mcp/prInfo.d.ts +11 -0
  61. package/dist/mcp/providerSchema.d.ts +52 -0
  62. package/dist/mcp/review.d.ts +212 -0
  63. package/dist/mcp/reviewComments.d.ts +245 -0
  64. package/dist/mcp/roots.d.ts +60 -0
  65. package/dist/mcp/scope.d.ts +26 -0
  66. package/dist/mcp/selectMode.d.ts +20 -0
  67. package/dist/mcp/server.d.ts +61 -0
  68. package/dist/mcp/shared.d.ts +62 -0
  69. package/dist/mcp/shell.d.ts +58 -0
  70. package/dist/mcp/staleFix.d.ts +138 -0
  71. package/dist/mcp/terraform/azurePrices.d.ts +8 -0
  72. package/dist/mcp/terraform/baseline.d.ts +57 -0
  73. package/dist/mcp/terraform/concernResult.d.ts +38 -0
  74. package/dist/mcp/terraform/cost.d.ts +55 -0
  75. package/dist/mcp/terraform/cspm.d.ts +80 -0
  76. package/dist/mcp/terraform/currency.d.ts +110 -0
  77. package/dist/mcp/terraform/decisions.d.ts +178 -0
  78. package/dist/mcp/terraform/eidas.d.ts +100 -0
  79. package/dist/mcp/terraform/evidence.d.ts +343 -0
  80. package/dist/mcp/terraform/findings.d.ts +76 -0
  81. package/dist/mcp/terraform/fixMemory.d.ts +81 -0
  82. package/dist/mcp/terraform/governance.d.ts +56 -0
  83. package/dist/mcp/terraform/hcl.d.ts +115 -0
  84. package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
  85. package/dist/mcp/terraform/idleFloor.d.ts +78 -0
  86. package/dist/mcp/terraform/keyless.d.ts +68 -0
  87. package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
  88. package/dist/mcp/terraform/nativeRules.d.ts +38 -0
  89. package/dist/mcp/terraform/nativeScan.d.ts +11 -0
  90. package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
  91. package/dist/mcp/terraform/normalization.d.ts +52 -0
  92. package/dist/mcp/terraform/oscal.d.ts +103 -0
  93. package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
  94. package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
  95. package/dist/mcp/terraform/paths.d.ts +28 -0
  96. package/dist/mcp/terraform/plan.d.ts +157 -0
  97. package/dist/mcp/terraform/planDrift.d.ts +199 -0
  98. package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
  99. package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
  100. package/dist/mcp/terraform/refactor.d.ts +183 -0
  101. package/dist/mcp/terraform/registry.d.ts +108 -0
  102. package/dist/mcp/terraform/risk.d.ts +41 -0
  103. package/dist/mcp/terraform/scanSession.d.ts +116 -0
  104. package/dist/mcp/terraform/scannerCache.d.ts +15 -0
  105. package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
  106. package/dist/mcp/terraform/scannerJson.d.ts +13 -0
  107. package/dist/mcp/terraform/scanners.d.ts +205 -0
  108. package/dist/mcp/terraform/signing.d.ts +61 -0
  109. package/dist/mcp/terraform/stateLocking.d.ts +18 -0
  110. package/dist/mcp/terraform/subprocess.d.ts +43 -0
  111. package/dist/mcp/terraform/suppressions.d.ts +90 -0
  112. package/dist/mcp/terraform/taxonomy.d.ts +39 -0
  113. package/dist/mcp/terraform/tfquery.d.ts +47 -0
  114. package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
  115. package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
  116. package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
  117. package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
  118. package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
  119. package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
  120. package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
  121. package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
  122. package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
  123. package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
  124. package/dist/mcp/terraform/tools/plan.d.ts +59 -0
  125. package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
  126. package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
  127. package/dist/mcp/terraform/tools/scan.d.ts +114 -0
  128. package/dist/mcp/terraform/tools/validate.d.ts +31 -0
  129. package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
  130. package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
  131. package/dist/mcp/terraform/tools.d.ts +16 -0
  132. package/dist/mcp/terraform/transformPacks.d.ts +267 -0
  133. package/dist/mcp/terraform/types.d.ts +226 -0
  134. package/dist/mcp/terraform/verification.d.ts +81 -0
  135. package/dist/mcp/terraform/vex.d.ts +115 -0
  136. package/dist/mcp/terraform/writeOnly.d.ts +67 -0
  137. package/dist/mcp/terraform.d.ts +51 -0
  138. package/dist/mcp/terratest.d.ts +85 -0
  139. package/dist/mcp/untrustedContent.d.ts +76 -0
  140. package/dist/mcp/upload.d.ts +8 -0
  141. package/dist/models.d.ts +181 -0
  142. package/dist/modes/address-reviews.d.ts +2 -0
  143. package/dist/modes/assess.d.ts +2 -0
  144. package/dist/modes/build.d.ts +2 -0
  145. package/dist/modes/compliance-audit.d.ts +2 -0
  146. package/dist/modes/cost-optimization.d.ts +2 -0
  147. package/dist/modes/drift-detect.d.ts +2 -0
  148. package/dist/modes/fix.d.ts +2 -0
  149. package/dist/modes/incremental-review.d.ts +2 -0
  150. package/dist/modes/index.d.ts +24 -0
  151. package/dist/modes/modernize-deprecated.d.ts +2 -0
  152. package/dist/modes/plan.d.ts +2 -0
  153. package/dist/modes/policy-gate.d.ts +2 -0
  154. package/dist/modes/prFormats.d.ts +3 -0
  155. package/dist/modes/refactor.d.ts +2 -0
  156. package/dist/modes/refresh-remediation.d.ts +2 -0
  157. package/dist/modes/remediate-and-refactor.d.ts +2 -0
  158. package/dist/modes/remediate.d.ts +2 -0
  159. package/dist/modes/resolve-conflicts.d.ts +2 -0
  160. package/dist/modes/review.d.ts +2 -0
  161. package/dist/modes/summarize-pr.d.ts +2 -0
  162. package/dist/modes/task.d.ts +2 -0
  163. package/dist/modes/terraform-code-review.d.ts +2 -0
  164. package/dist/modes/types.d.ts +9 -0
  165. package/dist/modes/update-dependencies.d.ts +2 -0
  166. package/dist/prep/index.d.ts +7 -0
  167. package/dist/prep/installNodeDependencies.d.ts +2 -0
  168. package/dist/prep/installPythonDependencies.d.ts +2 -0
  169. package/dist/prep/types.d.ts +31 -0
  170. package/dist/reviewQuality.d.ts +107 -0
  171. package/dist/skills/terraform-best-practices/SKILL.md +379 -0
  172. package/dist/toolState.d.ts +123 -0
  173. package/dist/utils/activity.d.ts +40 -0
  174. package/dist/utils/agent.d.ts +47 -0
  175. package/dist/utils/agentHangReport.d.ts +38 -0
  176. package/dist/utils/annotations.d.ts +17 -0
  177. package/dist/utils/apiFetch.d.ts +11 -0
  178. package/dist/utils/apiKeys.d.ts +48 -0
  179. package/dist/utils/apiUrl.d.ts +28 -0
  180. package/dist/utils/assets.d.ts +8 -0
  181. package/dist/utils/baseRefConfig.d.ts +121 -0
  182. package/dist/utils/billingErrors.d.ts +85 -0
  183. package/dist/utils/body.d.ts +34 -0
  184. package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
  185. package/dist/utils/byokFallback.d.ts +85 -0
  186. package/dist/utils/changeImpact.d.ts +101 -0
  187. package/dist/utils/claudeSubscription.d.ts +30 -0
  188. package/dist/utils/cli.d.ts +10 -0
  189. package/dist/utils/cloudClient.d.ts +33 -0
  190. package/dist/utils/cloudFetchCore.d.ts +42 -0
  191. package/dist/utils/cloudReport.d.ts +114 -0
  192. package/dist/utils/codexHome.d.ts +29 -0
  193. package/dist/utils/codexOAuth.d.ts +60 -0
  194. package/dist/utils/diffCoverage.d.ts +63 -0
  195. package/dist/utils/env.d.ts +46 -0
  196. package/dist/utils/errorReport.d.ts +17 -0
  197. package/dist/utils/exitHandler.d.ts +8 -0
  198. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  199. package/dist/utils/gitAuth.d.ts +84 -0
  200. package/dist/utils/gitAuthServer.d.ts +24 -0
  201. package/dist/utils/github.d.ts +104 -0
  202. package/dist/utils/globals.d.ts +3 -0
  203. package/dist/utils/infraweaverConfig.d.ts +56 -0
  204. package/dist/utils/install.d.ts +37 -0
  205. package/dist/utils/instructions.d.ts +48 -0
  206. package/dist/utils/keylessOidc.d.ts +28 -0
  207. package/dist/utils/leapingComment.d.ts +11 -0
  208. package/dist/utils/learnings.d.ts +62 -0
  209. package/dist/utils/learningsTruncate.d.ts +25 -0
  210. package/dist/utils/lifecycle.d.ts +57 -0
  211. package/dist/utils/log.d.ts +111 -0
  212. package/dist/utils/modelResolver.d.ts +63 -0
  213. package/dist/utils/moduleFetch.d.ts +39 -0
  214. package/dist/utils/normalizeEnv.d.ts +30 -0
  215. package/dist/utils/openCodeModels.d.ts +11 -0
  216. package/dist/utils/openaiCompatible.d.ts +26 -0
  217. package/dist/utils/opentofuMcp.d.ts +50 -0
  218. package/dist/utils/overrides.d.ts +40 -0
  219. package/dist/utils/packageManager.d.ts +49 -0
  220. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  221. package/dist/utils/payload.d.ts +249 -0
  222. package/dist/utils/prSummary.d.ts +61 -0
  223. package/dist/utils/presets.d.ts +36 -0
  224. package/dist/utils/progressComment.d.ts +146 -0
  225. package/dist/utils/providerErrors.d.ts +31 -0
  226. package/dist/utils/proxyModel.d.ts +76 -0
  227. package/dist/utils/rangeDiff.d.ts +51 -0
  228. package/dist/utils/redact.d.ts +33 -0
  229. package/dist/utils/registryAuth.d.ts +52 -0
  230. package/dist/utils/remediationCommand.d.ts +58 -0
  231. package/dist/utils/retry.d.ts +38 -0
  232. package/dist/utils/reviewCleanup.d.ts +14 -0
  233. package/dist/utils/run.d.ts +9 -0
  234. package/dist/utils/runContext.d.ts +112 -0
  235. package/dist/utils/runContextData.d.ts +42 -0
  236. package/dist/utils/runErrorRenderer.d.ts +64 -0
  237. package/dist/utils/runLifecycle.d.ts +89 -0
  238. package/dist/utils/runStartupLog.d.ts +15 -0
  239. package/dist/utils/secrets.d.ts +34 -0
  240. package/dist/utils/setup.d.ts +90 -0
  241. package/dist/utils/shell.d.ts +44 -0
  242. package/dist/utils/skills.d.ts +10 -0
  243. package/dist/utils/subprocess.d.ts +81 -0
  244. package/dist/utils/terraformMcp.d.ts +46 -0
  245. package/dist/utils/time.d.ts +15 -0
  246. package/dist/utils/timer.d.ts +23 -0
  247. package/dist/utils/todoTracking.d.ts +16 -0
  248. package/dist/utils/token.d.ts +52 -0
  249. package/dist/utils/toolLicensing.d.ts +56 -0
  250. package/dist/utils/toolSelection.d.ts +73 -0
  251. package/dist/utils/toon.d.ts +16 -0
  252. package/dist/utils/version.d.ts +2 -0
  253. package/dist/utils/versioning.d.ts +7 -0
  254. package/dist/utils/vertex.d.ts +16 -0
  255. package/dist/utils/workflow.d.ts +13 -0
  256. package/package.json +132 -0
  257. package/src/agents/claude.ts +1588 -0
  258. package/src/agents/claudePretoolGate.ts +286 -0
  259. package/src/agents/dispatchMetrics.ts +162 -0
  260. package/src/agents/gateServer.ts +178 -0
  261. package/src/agents/index.ts +10 -0
  262. package/src/agents/nativeFsDenies.ts +164 -0
  263. package/src/agents/opencode.ts +1600 -0
  264. package/src/agents/opencodePlugin.ts +304 -0
  265. package/src/agents/opencodeShared.ts +134 -0
  266. package/src/agents/postRun.ts +615 -0
  267. package/src/agents/reviewer.ts +134 -0
  268. package/src/agents/sessionLabeler.ts +185 -0
  269. package/src/agents/shared.ts +377 -0
  270. package/src/agents/subagentModels.ts +40 -0
  271. package/src/agents/tokenQuota.ts +203 -0
  272. package/src/agents/writeGateSource.ts +148 -0
  273. package/src/agents/writePolicy.ts +132 -0
  274. package/src/brand.ts +84 -0
  275. package/src/cli.ts +117 -0
  276. package/src/commands/gha.ts +188 -0
  277. package/src/commands/mcp.ts +126 -0
  278. package/src/commands/verifyEvidence.ts +100 -0
  279. package/src/entry.ts +7 -0
  280. package/src/entryPost.ts +109 -0
  281. package/src/external.ts +308 -0
  282. package/src/i18n/scaffolding.ts +191 -0
  283. package/src/index.ts +7 -0
  284. package/src/internal/index.ts +74 -0
  285. package/src/lifecycle.ts +2 -0
  286. package/src/main.ts +815 -0
  287. package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
  288. package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
  289. package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
  290. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  291. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  292. package/src/mcp/arkConfig.ts +7 -0
  293. package/src/mcp/assess.ts +723 -0
  294. package/src/mcp/capabilityContext.ts +80 -0
  295. package/src/mcp/changeSummary.ts +145 -0
  296. package/src/mcp/checkSuite.ts +261 -0
  297. package/src/mcp/checkout.ts +1015 -0
  298. package/src/mcp/comment.ts +683 -0
  299. package/src/mcp/commitInfo.ts +57 -0
  300. package/src/mcp/crosswalk.ts +1253 -0
  301. package/src/mcp/crosswalkDigest.ts +5 -0
  302. package/src/mcp/cyberEssentials.ts +60 -0
  303. package/src/mcp/dashboard.ts +232 -0
  304. package/src/mcp/dependencies.ts +190 -0
  305. package/src/mcp/frameworks.ts +236 -0
  306. package/src/mcp/geminiSanitizer.ts +212 -0
  307. package/src/mcp/git.ts +1116 -0
  308. package/src/mcp/guardrails.ts +771 -0
  309. package/src/mcp/issue.ts +74 -0
  310. package/src/mcp/issueComments.ts +48 -0
  311. package/src/mcp/issueEvents.ts +100 -0
  312. package/src/mcp/issueInfo.ts +72 -0
  313. package/src/mcp/labels.ts +35 -0
  314. package/src/mcp/localContext.ts +65 -0
  315. package/src/mcp/localServer.ts +217 -0
  316. package/src/mcp/moduleExtraction.ts +368 -0
  317. package/src/mcp/moduleTests.ts +421 -0
  318. package/src/mcp/modules.ts +752 -0
  319. package/src/mcp/output.ts +71 -0
  320. package/src/mcp/pathSafety.ts +28 -0
  321. package/src/mcp/policy.ts +226 -0
  322. package/src/mcp/pr.ts +238 -0
  323. package/src/mcp/prInfo.ts +91 -0
  324. package/src/mcp/providerSchema.ts +175 -0
  325. package/src/mcp/review.ts +1081 -0
  326. package/src/mcp/reviewComments.ts +1137 -0
  327. package/src/mcp/roots.ts +217 -0
  328. package/src/mcp/scope.ts +82 -0
  329. package/src/mcp/selectMode.ts +207 -0
  330. package/src/mcp/server.ts +498 -0
  331. package/src/mcp/shared.ts +120 -0
  332. package/src/mcp/shell.ts +631 -0
  333. package/src/mcp/staleFix.ts +557 -0
  334. package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
  335. package/src/mcp/terraform/azurePrices.ts +38 -0
  336. package/src/mcp/terraform/baseline.ts +172 -0
  337. package/src/mcp/terraform/concernResult.ts +111 -0
  338. package/src/mcp/terraform/cost.ts +175 -0
  339. package/src/mcp/terraform/cspm.ts +326 -0
  340. package/src/mcp/terraform/currency.ts +354 -0
  341. package/src/mcp/terraform/decisions.ts +590 -0
  342. package/src/mcp/terraform/eidas.ts +134 -0
  343. package/src/mcp/terraform/evidence.ts +771 -0
  344. package/src/mcp/terraform/findings.ts +383 -0
  345. package/src/mcp/terraform/fixMemory.ts +253 -0
  346. package/src/mcp/terraform/governance.ts +231 -0
  347. package/src/mcp/terraform/hcl.ts +273 -0
  348. package/src/mcp/terraform/iacLanguages.ts +431 -0
  349. package/src/mcp/terraform/idleFloor.ts +304 -0
  350. package/src/mcp/terraform/keyless.ts +123 -0
  351. package/src/mcp/terraform/moduleDocs.ts +303 -0
  352. package/src/mcp/terraform/nativeRules.ts +152 -0
  353. package/src/mcp/terraform/nativeScan.ts +67 -0
  354. package/src/mcp/terraform/noiseProfile.ts +183 -0
  355. package/src/mcp/terraform/normalization.ts +265 -0
  356. package/src/mcp/terraform/oscal.ts +283 -0
  357. package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
  358. package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
  359. package/src/mcp/terraform/paths.ts +59 -0
  360. package/src/mcp/terraform/plan.ts +377 -0
  361. package/src/mcp/terraform/planDrift.ts +520 -0
  362. package/src/mcp/terraform/policyAuthor.ts +886 -0
  363. package/src/mcp/terraform/prowlerOcsf.ts +380 -0
  364. package/src/mcp/terraform/refactor.ts +879 -0
  365. package/src/mcp/terraform/registry.ts +317 -0
  366. package/src/mcp/terraform/risk.ts +99 -0
  367. package/src/mcp/terraform/scanSession.ts +242 -0
  368. package/src/mcp/terraform/scannerCache.ts +86 -0
  369. package/src/mcp/terraform/scannerEgress.ts +141 -0
  370. package/src/mcp/terraform/scannerJson.ts +22 -0
  371. package/src/mcp/terraform/scanners.ts +1134 -0
  372. package/src/mcp/terraform/signing.ts +151 -0
  373. package/src/mcp/terraform/stateLocking.ts +106 -0
  374. package/src/mcp/terraform/subprocess.ts +102 -0
  375. package/src/mcp/terraform/suppressions.ts +551 -0
  376. package/src/mcp/terraform/taxonomy.ts +0 -0
  377. package/src/mcp/terraform/tfquery.ts +159 -0
  378. package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
  379. package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
  380. package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
  381. package/src/mcp/terraform/tools/emitCost.ts +130 -0
  382. package/src/mcp/terraform/tools/emitSarif.ts +108 -0
  383. package/src/mcp/terraform/tools/fixMemory.ts +82 -0
  384. package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
  385. package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
  386. package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
  387. package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
  388. package/src/mcp/terraform/tools/plan.ts +384 -0
  389. package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
  390. package/src/mcp/terraform/tools/readFindings.ts +138 -0
  391. package/src/mcp/terraform/tools/scan.ts +303 -0
  392. package/src/mcp/terraform/tools/validate.ts +160 -0
  393. package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
  394. package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
  395. package/src/mcp/terraform/tools.ts +62 -0
  396. package/src/mcp/terraform/transformPacks.ts +594 -0
  397. package/src/mcp/terraform/types.ts +382 -0
  398. package/src/mcp/terraform/verification.ts +150 -0
  399. package/src/mcp/terraform/vex.ts +367 -0
  400. package/src/mcp/terraform/writeOnly.ts +252 -0
  401. package/src/mcp/terraform.ts +52 -0
  402. package/src/mcp/terratest.ts +215 -0
  403. package/src/mcp/untrustedContent.ts +126 -0
  404. package/src/mcp/upload.ts +123 -0
  405. package/src/models.ts +753 -0
  406. package/src/modes/address-reviews.ts +35 -0
  407. package/src/modes/assess.ts +27 -0
  408. package/src/modes/build.ts +86 -0
  409. package/src/modes/compliance-audit.ts +25 -0
  410. package/src/modes/cost-optimization.ts +36 -0
  411. package/src/modes/drift-detect.ts +31 -0
  412. package/src/modes/fix.ts +29 -0
  413. package/src/modes/incremental-review.ts +103 -0
  414. package/src/modes/index.ts +94 -0
  415. package/src/modes/modernize-deprecated.ts +36 -0
  416. package/src/modes/plan.ts +21 -0
  417. package/src/modes/policy-gate.ts +27 -0
  418. package/src/modes/prFormats.ts +325 -0
  419. package/src/modes/refactor.ts +74 -0
  420. package/src/modes/refresh-remediation.ts +40 -0
  421. package/src/modes/remediate-and-refactor.ts +42 -0
  422. package/src/modes/remediate.ts +78 -0
  423. package/src/modes/resolve-conflicts.ts +32 -0
  424. package/src/modes/review.ts +135 -0
  425. package/src/modes/summarize-pr.ts +42 -0
  426. package/src/modes/task.ts +26 -0
  427. package/src/modes/terraform-code-review.ts +68 -0
  428. package/src/modes/types.ts +17 -0
  429. package/src/modes/update-dependencies.ts +44 -0
  430. package/src/prep/index.ts +96 -0
  431. package/src/prep/installNodeDependencies.ts +251 -0
  432. package/src/prep/installPythonDependencies.ts +235 -0
  433. package/src/prep/types.ts +38 -0
  434. package/src/reviewQuality.ts +237 -0
  435. package/src/runCli.ts +335 -0
  436. package/src/skills/terraform-best-practices/SKILL.md +379 -0
  437. package/src/toolState.ts +241 -0
  438. package/src/utils/activity.ts +210 -0
  439. package/src/utils/agent.ts +245 -0
  440. package/src/utils/agentHangReport.ts +180 -0
  441. package/src/utils/annotations.ts +56 -0
  442. package/src/utils/apiFetch.ts +19 -0
  443. package/src/utils/apiKeys.ts +305 -0
  444. package/src/utils/apiUrl.ts +42 -0
  445. package/src/utils/assets.ts +123 -0
  446. package/src/utils/baseRefConfig.ts +254 -0
  447. package/src/utils/billingErrors.ts +210 -0
  448. package/src/utils/body.ts +168 -0
  449. package/src/utils/buildInfraweaverFooter.ts +104 -0
  450. package/src/utils/byokFallback.ts +137 -0
  451. package/src/utils/changeImpact.ts +330 -0
  452. package/src/utils/claudeSubscription.ts +93 -0
  453. package/src/utils/cli.ts +36 -0
  454. package/src/utils/cloudClient.ts +57 -0
  455. package/src/utils/cloudFetchCore.ts +139 -0
  456. package/src/utils/cloudReport.ts +316 -0
  457. package/src/utils/codexHome.ts +204 -0
  458. package/src/utils/codexOAuth.ts +154 -0
  459. package/src/utils/codexRefreshDetect.ts +36 -0
  460. package/src/utils/diffCoverage.ts +404 -0
  461. package/src/utils/env.ts +84 -0
  462. package/src/utils/errorReport.ts +94 -0
  463. package/src/utils/exitHandler.ts +35 -0
  464. package/src/utils/fixDoubleEscapedString.ts +9 -0
  465. package/src/utils/ghaCore.ts +13 -0
  466. package/src/utils/gitAuth.ts +268 -0
  467. package/src/utils/gitAuthServer.ts +186 -0
  468. package/src/utils/github.ts +676 -0
  469. package/src/utils/globals.ts +9 -0
  470. package/src/utils/humanEditCapture.ts +198 -0
  471. package/src/utils/infraweaverConfig.ts +202 -0
  472. package/src/utils/install.ts +279 -0
  473. package/src/utils/instructions.ts +640 -0
  474. package/src/utils/keylessOidc.ts +47 -0
  475. package/src/utils/leapingComment.ts +20 -0
  476. package/src/utils/learnings.ts +145 -0
  477. package/src/utils/learningsTruncate.ts +42 -0
  478. package/src/utils/lifecycle.ts +198 -0
  479. package/src/utils/log.ts +452 -0
  480. package/src/utils/modelResolver.ts +105 -0
  481. package/src/utils/moduleFetch.ts +138 -0
  482. package/src/utils/normalizeEnv.ts +106 -0
  483. package/src/utils/openCodeModels.ts +86 -0
  484. package/src/utils/openaiCompatible.ts +52 -0
  485. package/src/utils/opentofuMcp.ts +61 -0
  486. package/src/utils/overrides.ts +100 -0
  487. package/src/utils/packageManager.ts +257 -0
  488. package/src/utils/patchWorkflowRunFields.ts +173 -0
  489. package/src/utils/payload.ts +1206 -0
  490. package/src/utils/prSummary.ts +148 -0
  491. package/src/utils/presets.ts +105 -0
  492. package/src/utils/progressComment.ts +266 -0
  493. package/src/utils/providerErrors.ts +189 -0
  494. package/src/utils/proxyModel.ts +239 -0
  495. package/src/utils/rangeDiff.ts +182 -0
  496. package/src/utils/redact.ts +86 -0
  497. package/src/utils/registryAuth.ts +115 -0
  498. package/src/utils/remediationCommand.ts +144 -0
  499. package/src/utils/retry.ts +180 -0
  500. package/src/utils/reviewCleanup.ts +116 -0
  501. package/src/utils/run.ts +100 -0
  502. package/src/utils/runContext.ts +276 -0
  503. package/src/utils/runContextData.ts +121 -0
  504. package/src/utils/runErrorRenderer.ts +282 -0
  505. package/src/utils/runFixture.ts +91 -0
  506. package/src/utils/runLifecycle.ts +360 -0
  507. package/src/utils/runStartupLog.ts +67 -0
  508. package/src/utils/secrets.ts +208 -0
  509. package/src/utils/setup.ts +368 -0
  510. package/src/utils/shell.ts +132 -0
  511. package/src/utils/skills.ts +67 -0
  512. package/src/utils/subprocess.ts +474 -0
  513. package/src/utils/terraformMcp.ts +99 -0
  514. package/src/utils/time.ts +59 -0
  515. package/src/utils/timer.ts +72 -0
  516. package/src/utils/todoTracking.ts +168 -0
  517. package/src/utils/token.ts +294 -0
  518. package/src/utils/toolLicensing.ts +152 -0
  519. package/src/utils/toolSelection.ts +239 -0
  520. package/src/utils/toon.ts +76 -0
  521. package/src/utils/version.ts +10 -0
  522. package/src/utils/versioning.ts +44 -0
  523. package/src/utils/vertex.ts +94 -0
  524. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Trust boundary for the repo-committed `.infraweaver.yml` on a PULL-REQUEST run.
3
+ *
4
+ * THE PROBLEM. `.infraweaver.yml` is read from the checked-out ref, which on a PR
5
+ * run is the PR's own head. Most of the run's shape is already out of reach —
6
+ * `push`, `shell`, `allow_replace` and the `*_instructions` are deliberately not
7
+ * file-readable, because "the run's shape belongs to the workflow that
8
+ * dispatches it" ([[infraweaverConfig]]). But several keys that ARE file-readable
9
+ * decide how strictly the run judges the code, and a PR that edits them is
10
+ * editing the gates that judge that same PR:
11
+ *
12
+ * allowed_paths / protected_paths → what the fix loop may write
13
+ * required_controls → which controls must be clean to pass
14
+ * severity_threshold → what counts as reportable at all
15
+ * autonomy_threshold → what escalates to a human
16
+ * tools_enabled → which scanners run
17
+ * scan_scope / iac_languages → how much of the tree is looked at
18
+ * suppressions → which findings are silenced outright
19
+ *
20
+ * None of these is an arbitrary-code-execution hole, and GitHub already defangs
21
+ * the fork case (read-only token, no secrets on `pull_request`). A workflow
22
+ * input also always wins over the file. But "the workflow author remembered to
23
+ * pin it" is not a control, and a same-repo PR silencing the finding that would
24
+ * have blocked it is a plausible, quiet failure.
25
+ *
26
+ * THE RULE. On a PR-triggered run these come from the BASE ref — the reviewed
27
+ * state of the branch being merged into. Everything else (prose language,
28
+ * grouping, the advisory module catalogue) still comes from the head, because
29
+ * none of it can weaken a verdict.
30
+ *
31
+ * This is the same principle as the shipped two-person SoD on waivers (§5.3): a
32
+ * suppression takes effect once someone other than its author has accepted it —
33
+ * here, by merging it. A PR that adds a waiver still SEES the finding it waives;
34
+ * the waiver applies from the next run on the merged base.
35
+ *
36
+ * HONEST DEGRADE. When the base ref can't be read (a shallow clone with no
37
+ * `origin/<base>`, a non-PR run, no git at all), the head value is used and the
38
+ * fact is stated for the run summary. Failing the run would turn a hardening
39
+ * measure into an outage on every shallow-checkout workflow; silently preferring
40
+ * the head with no record would be the dishonesty this codebase avoids
41
+ * everywhere else. So: use it, and say so.
42
+ *
43
+ * DIRECTION IS NOT INSPECTED. A PR that *tightens* a gate also defers to the
44
+ * base value — not because tightening is dangerous, but because "which value
45
+ * applies" must not depend on a per-key notion of stricter/looser that we would
46
+ * have to get right for every key forever. The new value takes effect on merge,
47
+ * which is when the reviewed state actually changes.
48
+ */
49
+
50
+ import { CONFIG_FILE } from "#app/brand";
51
+ // the sync, restricted-env, timeout-bounded runner every other git shell-out
52
+ // uses (`resolveBaseRef` in mcp/terraform/paths.ts does the same) — a config
53
+ // read must not inherit the ambient env any more than a scanner does.
54
+ import { run } from "#app/mcp/terraform/subprocess";
55
+ import { log } from "#app/utils/cli";
56
+ import {
57
+ type InfraweaverConfigKey,
58
+ type InfraweaverFileValues,
59
+ INFRAWEAVER_CONFIG_FILENAMES,
60
+ parseInfraweaverConfig,
61
+ } from "#app/utils/infraweaverConfig";
62
+
63
+ /**
64
+ * The file-readable keys that can weaken a verdict. Deliberately enumerated
65
+ * rather than derived by exclusion, and paired with
66
+ * {@link PRESENTATIONAL_CONFIG_KEYS} so that every key in
67
+ * `INFRAWEAVER_CONFIG_KEYS` is classified exactly once — `baseRefConfig.test.ts`
68
+ * asserts that partition, so a key added later cannot silently default to
69
+ * "PR-controllable".
70
+ */
71
+ export const GATE_RELEVANT_CONFIG_KEYS = [
72
+ "allowed_paths",
73
+ "protected_paths",
74
+ "required_controls",
75
+ "severity_threshold",
76
+ "autonomy_threshold",
77
+ "tools_enabled",
78
+ "scan_scope",
79
+ "iac_languages",
80
+ ] as const satisfies readonly InfraweaverConfigKey[];
81
+
82
+ /**
83
+ * Keys a PR may set from its own head: none can hide a finding, relax a
84
+ * guardrail, or shrink what is scanned. `language` is prose only; `grouping` is
85
+ * presentation; `module_catalogue` is an advisory list the fixer may prefer
86
+ * from, never a gate.
87
+ */
88
+ export const PRESENTATIONAL_CONFIG_KEYS = [
89
+ "language",
90
+ "grouping",
91
+ "module_catalogue",
92
+ ] as const satisfies readonly InfraweaverConfigKey[];
93
+
94
+ const GATE_KEY_SET: ReadonlySet<string> = new Set<string>(
95
+ GATE_RELEVANT_CONFIG_KEYS,
96
+ );
97
+
98
+ /** What the base ref had to say about `.infraweaver.*`. */
99
+ export type BaseConfigSource =
100
+ /** the base ref itself could not be resolved (shallow clone, no git, non-PR). */
101
+ | { kind: "unavailable" }
102
+ /** the base ref resolved and carries NO config file — so no file-level policy. */
103
+ | { kind: "absent"; ref: string }
104
+ | { kind: "file"; ref: string; raw: string; format: "yaml" | "json" };
105
+
106
+ /** `git show <ref>:<path>`, or null when the ref/path pair doesn't resolve. */
107
+ function showAtRef(cwd: string, ref: string, path: string): string | null {
108
+ const r = run("git", ["show", `${ref}:${path}`], cwd);
109
+ return r.status === 0 ? r.stdout : null;
110
+ }
111
+
112
+ /** Candidate git refs for the PR's base, most-specific first. */
113
+ function baseRefCandidates(baseRef: string): string[] {
114
+ return [`origin/${baseRef}`, `refs/remotes/origin/${baseRef}`, baseRef];
115
+ }
116
+
117
+ // One `git show` per (cwd, baseRef) per process. Both the scalar-key resolver
118
+ // and the suppression-policy loader ask the same question, and the answer
119
+ // cannot change mid-run.
120
+ const cache = new Map<string, BaseConfigSource>();
121
+
122
+ /** Drop the base-ref read cache — tests only. */
123
+ export function resetBaseConfigCache(): void {
124
+ cache.clear();
125
+ }
126
+
127
+ /**
128
+ * Read `.infraweaver.*` as it exists on the PR's base ref.
129
+ *
130
+ * The `absent` / `unavailable` distinction is load-bearing: "the base has no
131
+ * config" legitimately means *no file-level policy applies*, so a PR that ADDS
132
+ * one is correctly ignored. "We could not look" must never be read that way, or
133
+ * a shallow clone would silently drop a real policy.
134
+ */
135
+ export function readConfigAtBaseRef(
136
+ cwdOrUndefined: string | undefined,
137
+ baseRef: string | undefined,
138
+ ): BaseConfigSource {
139
+ if (!baseRef) return { kind: "unavailable" };
140
+ const cwd = cwdOrUndefined ?? process.cwd();
141
+ const key = `${cwd}::${baseRef}`;
142
+ const hit = cache.get(key);
143
+ if (hit) return hit;
144
+
145
+ let result: BaseConfigSource = { kind: "unavailable" };
146
+ for (const ref of baseRefCandidates(baseRef)) {
147
+ // Prove the ref resolves before concluding anything from a missing file.
148
+ if (
149
+ run("git", ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], cwd)
150
+ .status !== 0
151
+ ) {
152
+ continue;
153
+ }
154
+ result = { kind: "absent", ref };
155
+ for (const name of INFRAWEAVER_CONFIG_FILENAMES) {
156
+ const raw = showAtRef(cwd, ref, name);
157
+ if (raw === null) continue;
158
+ result = {
159
+ kind: "file",
160
+ ref,
161
+ raw,
162
+ format: name.endsWith(".json") ? "json" : "yaml",
163
+ };
164
+ break;
165
+ }
166
+ break;
167
+ }
168
+ cache.set(key, result);
169
+ return result;
170
+ }
171
+
172
+ export interface BaseRefConfigResult {
173
+ /** the config to use, gate keys taken from the base ref where available. */
174
+ values: InfraweaverFileValues;
175
+ /** human-readable notes for the run log: what was overridden, or why not. */
176
+ notes: string[];
177
+ }
178
+
179
+ /**
180
+ * Layer the base ref's gate-relevant keys over the head's config. `headValues`
181
+ * is what `loadInfraweaverConfig` already read from the checked-out tree; on a
182
+ * non-PR run (or when the base can't be resolved) it comes back unchanged.
183
+ */
184
+ export function resolveConfigAcrossRefs(args: {
185
+ cwd: string | undefined;
186
+ headValues: InfraweaverFileValues;
187
+ /** the PR's base branch — `GITHUB_BASE_REF`; empty/undefined on a non-PR run. */
188
+ baseRef: string | undefined;
189
+ /** injectable for tests; defaults to the real git read. */
190
+ readBase?: (
191
+ cwd: string | undefined,
192
+ baseRef: string | undefined,
193
+ ) => BaseConfigSource;
194
+ }): BaseRefConfigResult {
195
+ const { cwd, headValues, baseRef } = args;
196
+ const notes: string[] = [];
197
+ if (!baseRef) return { values: headValues, notes };
198
+
199
+ const source = (args.readBase ?? readConfigAtBaseRef)(cwd, baseRef);
200
+ const headGateKeys = Object.keys(headValues).filter((k) =>
201
+ GATE_KEY_SET.has(k),
202
+ );
203
+
204
+ if (source.kind === "unavailable") {
205
+ // Only worth a note when the head actually sets a gate key — otherwise
206
+ // there was nothing the base could have overridden.
207
+ if (headGateKeys.length > 0) {
208
+ notes.push(
209
+ `could not read the base ref (${baseRef}) to resolve gate-relevant config — using this ` +
210
+ `ref's values for [${headGateKeys.join(", ")}]. A shallow clone causes this; ` +
211
+ "`fetch-depth: 0` (or fetching the base branch) restores the check.",
212
+ );
213
+ }
214
+ return { values: headValues, notes };
215
+ }
216
+
217
+ const baseValues =
218
+ source.kind === "file"
219
+ ? parseInfraweaverConfig(source.raw, source.format).values
220
+ : {};
221
+ const values: InfraweaverFileValues = { ...headValues };
222
+ const changed: string[] = [];
223
+ for (const key of GATE_RELEVANT_CONFIG_KEYS) {
224
+ if (headValues[key] === baseValues[key]) continue;
225
+ changed.push(key);
226
+ const base = baseValues[key];
227
+ if (base === undefined) delete values[key];
228
+ else values[key] = base;
229
+ }
230
+
231
+ if (changed.length > 0) {
232
+ notes.push(
233
+ `gate-relevant config differs from the base ref (${source.ref}); using the BASE value for ` +
234
+ `[${changed.join(", ")}] so this change cannot relax the gates reviewing it. ` +
235
+ "It takes effect once merged.",
236
+ );
237
+ }
238
+ return { values, notes };
239
+ }
240
+
241
+ /**
242
+ * `resolveConfigAcrossRefs` + logging, for the one call site in `resolvePayload`.
243
+ * Notes are warnings, not info: a divergence belongs in the run summary rather
244
+ * than buried in debug output.
245
+ */
246
+ export function applyBaseRefConfigPolicy(args: {
247
+ cwd: string | undefined;
248
+ headValues: InfraweaverFileValues;
249
+ baseRef: string | undefined;
250
+ }): InfraweaverFileValues {
251
+ const { values, notes } = resolveConfigAcrossRefs(args);
252
+ for (const n of notes) log.warning(`» ${CONFIG_FILE}: ${n}`);
253
+ return values;
254
+ }
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Billing-error classification + user-facing copy for `/api/proxy-token`
3
+ * failures and OpenRouter mid-run exhaustion. Two error classes (Billing vs.
4
+ * Transient) keep the framing honest: a card decline is *not* the same UX as
5
+ * a 503 from the proxy service. Both originate in `utils/proxy.ts` (mint
6
+ * failures) and `utils/runErrorRenderer.ts` (mid-run keylimit reclassify).
7
+ *
8
+ * Renderers return markdown bodies that are written into both the GitHub
9
+ * Actions job summary and the PR progress comment.
10
+ *
11
+ * Lives outside `main.ts` so adding a new error `code` branch is a one-file
12
+ * edit that does not retrigger the full LLM CI matrix (`action/main.ts` is
13
+ * in `action/test/coverage.ts::ALWAYS_RUN_ALL`).
14
+ */
15
+
16
+ import { PRIMARY_DOMAIN, PRODUCT_NAME, STATUS_URL } from "#app/brand";
17
+
18
+ /**
19
+ * Billing-layer error surfaced from `/api/proxy-token` as a 402. User-actionable
20
+ * — distinct from TransientError (503 / transient sync issue) so the job
21
+ * summary + PR comment can use affirmative "you need to do X" copy rather than
22
+ * the ambiguous "billing error" label that makes transient outages look like
23
+ * the user's fault.
24
+ *
25
+ * `code` is a server-side discriminator: `router_requires_card` (no card + no
26
+ * wallet balance on Router), or null for unclassified. `declineCode` is
27
+ * Stripe's more specific sub-reason on `card_declined` (e.g.
28
+ * `insufficient_funds`, `lost_card`). `needsReauthentication` is the 3DS case
29
+ * broken out for convenience.
30
+ */
31
+ export class BillingError extends Error {
32
+ code: string | null;
33
+ declineCode: string | null;
34
+ needsReauthentication: boolean;
35
+
36
+ constructor(
37
+ message: string,
38
+ opts: {
39
+ code?: string | null;
40
+ declineCode?: string | null;
41
+ needsReauthentication?: boolean;
42
+ } = {},
43
+ ) {
44
+ super(message);
45
+ this.name = "BillingError";
46
+ this.code = opts.code ?? null;
47
+ this.declineCode = opts.declineCode ?? null;
48
+ this.needsReauthentication = opts.needsReauthentication ?? false;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Transient service failures from `/api/proxy-token` (503: partial OpenRouter
54
+ * usage sync, DB flake, in-flight payment intent). Not the user's fault — the
55
+ * summary uses "temporarily unavailable" framing, and the non-zero exit lets
56
+ * GH Actions apply whatever retry policy the workflow has configured.
57
+ */
58
+ export class TransientError extends Error {
59
+ constructor(message: string) {
60
+ super(message);
61
+ this.name = "TransientError";
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Deep link into the right console section for the failing account. Anchors
67
+ * are defined in `app/console/[owner]/page.tsx` (`#billing`, `#model-access`).
68
+ * `owner` is the GitHub login of the repo's account — i.e. the org or user
69
+ * that pays for this repo's runs, which is the right scope for billing.
70
+ */
71
+ function billingConsoleUrl(
72
+ owner: string,
73
+ anchor: "billing" | "model-access",
74
+ ): string {
75
+ return `https://infraweaver.dev/console/${encodeURIComponent(owner)}#${anchor}`;
76
+ }
77
+
78
+ /**
79
+ * Render a BillingError as user-facing markdown (shared between GH job summary
80
+ * and the PR progress comment). Goals:
81
+ *
82
+ * - quiet, not alarmist — bold first line instead of an `### ❌` H3, since
83
+ * the comment already has Infraweaver branding in the footer
84
+ * - actionable — every branch ends in a single CTA deep-linked to the
85
+ * correct section of the owner's console
86
+ * - honest — say what actually went wrong (card declined vs. balance
87
+ * empty vs. 3DS required), don't lump them under "billing error"
88
+ *
89
+ * Branches:
90
+ * - `router_requires_card`: user is on Router mode with no card AND no
91
+ * wallet balance (signup credit exhausted or not granted). Frame as
92
+ * "add a card to continue", link to `#model-access` where the Add
93
+ * Card flow lives.
94
+ * - `router_balance_exhausted`: user has a card on file but auto-reload is
95
+ * disabled and they've spent past their $5 overdraft buffer. Frame as
96
+ * "balance ran out" and surface both remediation paths (top up, or flip
97
+ * on auto-reload).
98
+ * - `router_keylimit_exhausted`: OpenRouter rejected mid-run because the
99
+ * per-run key budget was exhausted while the agent was working. The
100
+ * wallet is now negative; same remediation as `router_balance_exhausted`
101
+ * but framed for the after-the-fact case ("this run was cut short").
102
+ * - `needsReauthentication`: issuer requires 3DS on every off-session
103
+ * charge. Re-adding the card won't help — the only escape is a manual
104
+ * top-up where 3DS runs interactively in Stripe Checkout.
105
+ * - `declineCode` set: Stripe declined a real charge. Show the sub-code
106
+ * so support can act on it; tell the user we'll retry on next dispatch.
107
+ * - default: balance hit zero with no in-flight charge (auto-reload off
108
+ * or amount below threshold). Direct them to top up or enable auto-reload.
109
+ */
110
+ export function formatBillingErrorSummary(
111
+ error: BillingError,
112
+ owner: string,
113
+ ): string {
114
+ if (error.code === "router_requires_card") {
115
+ return [
116
+ `**Add a card to start using ${PRODUCT_NAME} Router.**`,
117
+ "",
118
+ "Router proxies OpenRouter at raw cost — no platform markup. Add a card and we'll auto-reload your wallet so runs keep flowing.",
119
+ "",
120
+ `[Add a card →](${billingConsoleUrl(owner, "model-access")})`,
121
+ ].join("\n");
122
+ }
123
+
124
+ if (error.code === "router_balance_exhausted") {
125
+ return [
126
+ `**Your ${PRODUCT_NAME} Router balance is exhausted.**`,
127
+ "",
128
+ "You have a card on file but auto-reload is disabled, so runs paused once your balance went past the overdraft buffer.",
129
+ "",
130
+ `[Top up balance →](${billingConsoleUrl(owner, "billing")}) · [Enable auto-reload →](${billingConsoleUrl(owner, "model-access")})`,
131
+ ].join("\n");
132
+ }
133
+
134
+ if (error.code === "router_keylimit_exhausted") {
135
+ return [
136
+ `**This run was cut short — your ${PRODUCT_NAME} Router balance ran out mid-run.**`,
137
+ "",
138
+ "OpenRouter stopped the agent because the per-run budget was exhausted. Your wallet is now negative; top up or enable auto-reload to keep runs flowing.",
139
+ "",
140
+ `[Top up balance →](${billingConsoleUrl(owner, "billing")}) · [Enable auto-reload →](${billingConsoleUrl(owner, "model-access")})`,
141
+ ].join("\n");
142
+ }
143
+
144
+ if (error.code === "router_requires_upgrade") {
145
+ return [
146
+ "**This managed model needs a higher plan.**",
147
+ "",
148
+ "Managed models run through our AWS Bedrock proxy (UK & EU): **Claude Opus 4.8** and **Claude Sonnet 5** run on OSS + Team, and **Claude Fable 5** is on Enterprise. Upgrade to run this model, or pick one your plan includes.",
149
+ "",
150
+ `[Upgrade your plan →](${billingConsoleUrl(owner, "billing")})`,
151
+ ].join("\n");
152
+ }
153
+
154
+ if (error.code === "router_monthly_limit") {
155
+ return [
156
+ `**${PRODUCT_NAME} Router hit its monthly spend limit.**`,
157
+ "",
158
+ "Auto-reloads are paused for the rest of this UTC month. Ask your admin to raise the cap, or wait for it to reset at 00:00 UTC on the 1st.",
159
+ "",
160
+ `[Adjust limit →](${billingConsoleUrl(owner, "model-access")})`,
161
+ ].join("\n");
162
+ }
163
+
164
+ if (error.needsReauthentication) {
165
+ const code = error.declineCode ?? "authentication_required";
166
+ return [
167
+ `**Your card issuer requires 3D Secure on every charge** (\`${code}\`).`,
168
+ "",
169
+ `${PRODUCT_NAME} can't complete a 3DS challenge from inside a workflow. Top up your Router balance once in Stripe Checkout — subsequent runs draw from the prepaid balance without re-triggering 3DS.`,
170
+ "",
171
+ `[Top up balance →](${billingConsoleUrl(owner, "billing")})`,
172
+ ].join("\n");
173
+ }
174
+
175
+ if (error.declineCode) {
176
+ return [
177
+ `**Your card was declined** (\`${error.declineCode}\`).`,
178
+ "",
179
+ `Update your payment method and ${PRODUCT_NAME} will retry on the next run.`,
180
+ "",
181
+ `[Update payment method →](${billingConsoleUrl(owner, "billing")})`,
182
+ ].join("\n");
183
+ }
184
+
185
+ return [
186
+ `**Your ${PRODUCT_NAME} balance is empty.**`,
187
+ "",
188
+ "Top up your balance or enable auto-reload to keep runs flowing.",
189
+ "",
190
+ `[Manage billing →](${billingConsoleUrl(owner, "billing")})`,
191
+ ].join("\n");
192
+ }
193
+
194
+ /**
195
+ * Render a TransientError as user-facing markdown. Distinct framing from
196
+ * BillingError so the user doesn't read an alarm and assume their card
197
+ * failed — this branch is "our fault, retry shortly", not theirs.
198
+ */
199
+ export function formatTransientErrorSummary(
200
+ error: TransientError,
201
+ owner: string,
202
+ ): string {
203
+ return [
204
+ `**${PRODUCT_NAME} billing is temporarily unavailable.**`,
205
+ "",
206
+ error.message,
207
+ "",
208
+ `Usually transient — the next dispatch should succeed. If it persists, check [status.${PRIMARY_DOMAIN}](${STATUS_URL}) or [your console](${billingConsoleUrl(owner, "billing")}).`,
209
+ ].join("\n");
210
+ }
@@ -0,0 +1,168 @@
1
+ import TurndownService from "turndown";
2
+ import type { PayloadEvent } from "#app/external";
3
+ import { downloadAssetsInMarkdown } from "#app/utils/assets";
4
+ import type { OctokitWithPlugins } from "#app/utils/github";
5
+ import type { RunContextData } from "#app/utils/runContextData";
6
+
7
+ const turndown = new TurndownService();
8
+
9
+ function hasImages(body: string | null | undefined): boolean {
10
+ if (!body) return false;
11
+ return body.includes("<img") || body.includes("![");
12
+ }
13
+
14
+ interface ResolveBodyContext {
15
+ event: PayloadEvent;
16
+ octokit: OctokitWithPlugins;
17
+ repo: RunContextData["repo"];
18
+ tmpdir: string;
19
+ githubToken: string;
20
+ }
21
+
22
+ /**
23
+ * resolves the body of an event by fetching body_html and converting to markdown.
24
+ * only fetches body_html if the body contains images (to avoid unnecessary API calls).
25
+ * this ensures agents receive markdown with working signed image URLs instead of
26
+ * broken user-attachments URLs.
27
+ */
28
+ export async function resolveBody(ctx: ResolveBodyContext): Promise<string | null> {
29
+ const body = ctx.event.body;
30
+ const bodyHtml = hasImages(body) ? await fetchBodyHtml(ctx) : undefined;
31
+ return resolveBodyAssets({
32
+ body,
33
+ bodyHtml,
34
+ tmpdir: ctx.tmpdir,
35
+ githubToken: ctx.githubToken,
36
+ });
37
+ }
38
+
39
+ interface ResolveBodyAssetsContext {
40
+ body: string | null | undefined;
41
+ bodyHtml: string | null | undefined;
42
+ tmpdir: string;
43
+ githubToken: string;
44
+ }
45
+
46
+ /**
47
+ * downloads github-hosted image assets in a body to disk and rewrites the urls to local
48
+ * paths so the agent can read them. when the body has images and a rendered `bodyHtml`
49
+ * is supplied, the html is turndowned first: github only exposes attachments as signed,
50
+ * self-authenticating `*.githubusercontent.com` urls through body_html — the raw
51
+ * `github.com/user-attachments/...` urls in unrendered markdown 404 for the installation
52
+ * token. callers that fetch a body should request it with the `application/vnd.github.full+json`
53
+ * media type and pass `body_html` here.
54
+ */
55
+ export async function resolveBodyAssets(ctx: ResolveBodyAssetsContext): Promise<string | null> {
56
+ let body = ctx.body ?? null;
57
+ if (body && hasImages(body) && ctx.bodyHtml) {
58
+ body = turndown.turndown(ctx.bodyHtml);
59
+ }
60
+ if (!body) return ctx.body ?? null;
61
+ return downloadAssetsInMarkdown(body, ctx.tmpdir, ctx.githubToken);
62
+ }
63
+
64
+ async function fetchBodyHtml(ctx: ResolveBodyContext): Promise<string | undefined> {
65
+ const event = ctx.event;
66
+ const headers = { accept: "application/vnd.github.full+json" };
67
+ const owner = ctx.repo.owner;
68
+ const repo = ctx.repo.name;
69
+
70
+ switch (event.trigger) {
71
+ case "issue_comment_created":
72
+ if (!event.comment_id) return;
73
+ return (
74
+ await ctx.octokit.rest.issues.getComment({
75
+ owner,
76
+ repo,
77
+ comment_id: event.comment_id,
78
+ headers,
79
+ })
80
+ ).data.body_html;
81
+
82
+ case "issues_opened":
83
+ case "issues_assigned":
84
+ case "issues_labeled":
85
+ if (!event.issue_number) return;
86
+ return (
87
+ await ctx.octokit.rest.issues.get({
88
+ owner,
89
+ repo,
90
+ issue_number: event.issue_number,
91
+ headers,
92
+ })
93
+ ).data.body_html;
94
+
95
+ case "pull_request_opened":
96
+ case "pull_request_ready_for_review":
97
+ case "pull_request_synchronize":
98
+ case "pull_request_review_requested":
99
+ // PRs are also issues - use issues.get which returns body_html
100
+ if (!event.issue_number) return;
101
+ return (
102
+ await ctx.octokit.rest.issues.get({
103
+ owner,
104
+ repo,
105
+ issue_number: event.issue_number,
106
+ headers,
107
+ })
108
+ ).data.body_html;
109
+
110
+ case "pull_request_review_submitted":
111
+ if (!event.issue_number || !event.review_id) return;
112
+ return (
113
+ await ctx.octokit.rest.pulls.getReview({
114
+ owner,
115
+ repo,
116
+ pull_number: event.issue_number,
117
+ review_id: event.review_id,
118
+ headers,
119
+ })
120
+ ).data.body_html;
121
+
122
+ case "pull_request_review_comment_created":
123
+ if (!event.comment_id) return;
124
+ return (
125
+ await ctx.octokit.rest.pulls.getReviewComment({
126
+ owner,
127
+ repo,
128
+ comment_id: event.comment_id,
129
+ headers,
130
+ })
131
+ ).data.body_html;
132
+
133
+ case "check_suite_completed":
134
+ // body is the PR body
135
+ if (!event.issue_number) return;
136
+ return (
137
+ await ctx.octokit.rest.issues.get({
138
+ owner,
139
+ repo,
140
+ issue_number: event.issue_number,
141
+ headers,
142
+ })
143
+ ).data.body_html;
144
+
145
+ case "implement_plan":
146
+ // body is the plan content from an issue comment
147
+ if (!event.plan_comment_id) return;
148
+ return (
149
+ await ctx.octokit.rest.issues.getComment({
150
+ owner,
151
+ repo,
152
+ comment_id: event.plan_comment_id,
153
+ headers,
154
+ })
155
+ ).data.body_html;
156
+
157
+ // triggers without a body field that needs resolution
158
+ case "workflow_dispatch":
159
+ case "fix_review":
160
+ case "unknown":
161
+ return undefined;
162
+
163
+ default:
164
+ // exhaustiveness check - TypeScript will error if a trigger is missing
165
+ event satisfies never;
166
+ return undefined;
167
+ }
168
+ }