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,106 @@
1
+ import * as core from "@actions/core";
2
+ import { log } from "#app/utils/cli";
3
+ import { isSensitiveEnvName } from "#app/utils/secrets";
4
+
5
+ /**
6
+ * Trim surrounding whitespace from a sensitive value and register it as a
7
+ * GitHub Actions log mask. Trailing newlines from terminal-copy paste are a
8
+ * common footgun: the value travels through GH Actions logs and any tool
9
+ * that re-emits parts of it leaks the unmasked tail. Trimming canonicalises
10
+ * the value so the mask matches exactly what downstream tools will print.
11
+ *
12
+ * Masking is delegated to `core.setSecret` (not raw `console.log`) so the
13
+ * toolkit percent-encodes `\r`/`\n`; the runner V2 parser decodes them and
14
+ * registers the full value plus every non-empty line as separate masks. That
15
+ * keeps us safe for embedded-newline values (PEMs, kubeconfigs, JSON blobs)
16
+ * even though they aren't currently used.
17
+ *
18
+ * Returns the trimmed value, or `null` when the input was whitespace-only —
19
+ * callers must leave `process.env` untouched in that case so a misconfigured
20
+ * value surfaces as a clear "missing key" downstream rather than silently
21
+ * mutating to the empty string.
22
+ */
23
+ export function sanitizeSecret(key: string, value: string): string | null {
24
+ const trimmed = value.trim();
25
+ if (trimmed.length === 0) {
26
+ log.warning(
27
+ `» ${key} is whitespace-only — leaving env var unchanged. check your secret value.`,
28
+ );
29
+ return null;
30
+ }
31
+ if (trimmed !== value) {
32
+ log.warning(
33
+ `» stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`,
34
+ );
35
+ }
36
+ core.setSecret(trimmed);
37
+ return trimmed;
38
+ }
39
+
40
+ /**
41
+ * Normalize environment variables to uppercase.
42
+ * This handles case-insensitive env var names (e.g., `anthropic_api_key` -> `ANTHROPIC_API_KEY`).
43
+ *
44
+ * If there are conflicts (same key with different capitalizations but different values),
45
+ * logs a warning and keeps the uppercase version.
46
+ *
47
+ * Also trims and masks sensitive values so accidental trailing whitespace
48
+ * doesn't defeat GitHub Actions log masking.
49
+ */
50
+ export function normalizeEnv(): void {
51
+ const upperKeys = new Map<string, string[]>();
52
+
53
+ // group keys by their uppercase form
54
+ for (const key of Object.keys(process.env)) {
55
+ const upper = key.toUpperCase();
56
+ const existing = upperKeys.get(upper) || [];
57
+ existing.push(key);
58
+ upperKeys.set(upper, existing);
59
+ }
60
+
61
+ // process each group
62
+ for (const [upperKey, keys] of upperKeys) {
63
+ if (keys.length === 1) {
64
+ const key = keys[0]!;
65
+ if (key !== upperKey) {
66
+ // single key, just needs uppercasing
67
+ process.env[upperKey] = process.env[key];
68
+ delete process.env[key];
69
+ }
70
+ continue;
71
+ }
72
+
73
+ // multiple keys with different capitalizations
74
+ const values = keys.map((k) => process.env[k]);
75
+ const uniqueValues = new Set(values);
76
+
77
+ if (uniqueValues.size > 1) {
78
+ // conflict: different values for different capitalizations
79
+ log.warning(
80
+ `env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`,
81
+ );
82
+ }
83
+
84
+ // prefer the uppercase version if it exists, otherwise use the first one
85
+ const preferredKey = keys.find((k) => k === upperKey) || keys[0]!;
86
+ const preferredValue = process.env[preferredKey];
87
+
88
+ // delete all variants
89
+ for (const key of keys) {
90
+ delete process.env[key];
91
+ }
92
+
93
+ // set the uppercase version
94
+ process.env[upperKey] = preferredValue;
95
+ }
96
+
97
+ // trim + mask sensitive values after case normalisation so each key is
98
+ // visited exactly once with its final, canonical value
99
+ for (const key of Object.keys(process.env)) {
100
+ if (!isSensitiveEnvName(key)) continue;
101
+ const value = process.env[key];
102
+ if (typeof value !== "string" || value.length === 0) continue;
103
+ const sanitized = sanitizeSecret(key, value);
104
+ if (sanitized !== null) process.env[key] = sanitized;
105
+ }
106
+ }
@@ -0,0 +1,86 @@
1
+ // OpenCode-as-source-of-truth for BYOK detection.
2
+ //
3
+ // `opencode models` returns the `provider/model` specifiers that OpenCode
4
+ // can actually route given the current env (workflow env block + GH Actions
5
+ // secrets) and `auth.json` (Codex / future managed credentials). This is
6
+ // authoritative — strictly more accurate than the static
7
+ // `provider.envVars + provider.managedCredentials` catalog in `models.ts`
8
+ // for the "do we have BYOK auth?" gate. The catalog can (and will) miss
9
+ // new auth shapes; OpenCode itself can't.
10
+ //
11
+ // Two captures per run:
12
+ // 1. `captureBaselineModels` — called BEFORE Codex `auth.json` is
13
+ // materialized. The set OpenCode can serve from the runner's pre-existing
14
+ // environment alone (workflow `env:` block + GH Actions secrets).
15
+ // 2. `captureAuthorizedModels` — called AFTER Codex `auth.json`
16
+ // materialization. The authoritative set for BYOK decisions (fallback +
17
+ // validateAgentApiKey).
18
+ //
19
+ // The set difference (`authorized - baseline`) is the contribution of the
20
+ // Codex OAuth credential to this run — logged once for operator visibility.
21
+ //
22
+ // Memoized at module scope so the two consumers
23
+ // (`selectFallbackModelIfNeeded` + `autoSelectModel`) share one shell-out.
24
+
25
+ import { execFileSync } from "node:child_process";
26
+ import { log } from "#app/utils/cli";
27
+
28
+ let baseline: Set<string> | undefined;
29
+ let authorized: Set<string> | undefined;
30
+
31
+ function readModels(cliPath: string): Set<string> {
32
+ try {
33
+ const output = execFileSync(cliPath, ["models"], {
34
+ encoding: "utf-8",
35
+ timeout: 30_000,
36
+ env: process.env,
37
+ });
38
+ return new Set(
39
+ output
40
+ .split("\n")
41
+ .map((line) => line.trim())
42
+ .filter(Boolean),
43
+ );
44
+ } catch (error) {
45
+ log.debug(
46
+ `» \`opencode models\` failed: ${error instanceof Error ? error.message : String(error)}`,
47
+ );
48
+ return new Set();
49
+ }
50
+ }
51
+
52
+ /** Snapshot the set of models OpenCode can serve from the current env, BEFORE
53
+ * Infraweaver-stored credentials are merged in. Call once early in `main.ts`. */
54
+ export function captureBaselineModels(cliPath: string): void {
55
+ baseline = readModels(cliPath);
56
+ log.debug(`» opencode baseline: ${baseline.size} models`);
57
+ }
58
+
59
+ /** Snapshot the set of models OpenCode can serve AFTER dbSecrets +
60
+ * Codex auth.json are in place. Logs the diff against the baseline as
61
+ * `» BYOK auth enabled N model(s): …`. */
62
+ export function captureAuthorizedModels(cliPath: string): void {
63
+ authorized = readModels(cliPath);
64
+ const base = baseline;
65
+ if (base) {
66
+ const diff = [...authorized].filter((m) => !base.has(m));
67
+ if (diff.length > 0) {
68
+ log.info(
69
+ `» BYOK auth enabled ${diff.length} model(s): ${diff.join(", ")}`,
70
+ );
71
+ }
72
+ }
73
+ log.debug(`» opencode authorized: ${authorized.size} models`);
74
+ }
75
+
76
+ /** Authorized set captured after Infraweaver-stored auth is applied. Throws if
77
+ * called before `captureAuthorizedModels` — the call sites (fallback gate,
78
+ * api-key validation, auto-select) all run strictly after capture. */
79
+ export function getAuthorizedModels(): Set<string> {
80
+ if (!authorized) {
81
+ throw new Error(
82
+ "getAuthorizedModels called before captureAuthorizedModels",
83
+ );
84
+ }
85
+ return authorized;
86
+ }
@@ -0,0 +1,52 @@
1
+ import { OPENAI_COMPATIBLE_MODEL_ID_ENV } from "#app/models";
2
+ import { readEnv } from "#app/utils/env";
3
+
4
+ /** endpoint base URL for the `openai-compatible/byok` route (e.g. an Ollama, vLLM,
5
+ * TGI, HuggingFace Inference Endpoint, or Azure OpenAI base URL). */
6
+ export const OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
7
+
8
+ /** API key for the endpoint. Optional — keyless self-hosted servers (a local
9
+ * Ollama) omit it and the adapter is handed a placeholder instead. */
10
+ export const OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
11
+
12
+ /** opencode provider name for the injected custom endpoint. */
13
+ export const OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
14
+
15
+ /**
16
+ * Is `model` the resolved id for the `openai-compatible/byok` route? Detected by
17
+ * the env sentinel — after `resolveModel`, a routing slug becomes its raw backing
18
+ * id (here `OPENAI_COMPATIBLE_MODEL_ID`), so this is how the harness re-identifies
19
+ * the route downstream (same pattern as `isVertexRoute` / the Bedrock sentinel).
20
+ * A HuggingFace model id can itself contain a `/`, so callers must NOT rely on the
21
+ * slash heuristic that distinguishes raw Bedrock/Vertex ids — use this instead.
22
+ */
23
+ export function isOpenAiCompatibleRoute(model: string | undefined): boolean {
24
+ const id = readEnv(OPENAI_COMPATIBLE_MODEL_ID_ENV);
25
+ return model !== undefined && id !== undefined && id === model;
26
+ }
27
+
28
+ /** When the route is active, the opencode model string is `openai-compatible/<id>`. */
29
+ export function resolveOpenAiCompatibleOpenCodeModel(model: string | undefined): string | undefined {
30
+ return isOpenAiCompatibleRoute(model) && model ? `${OPENAI_COMPATIBLE_PROVIDER}/${model}` : undefined;
31
+ }
32
+
33
+ /**
34
+ * The opencode `provider` block that registers the user's OpenAI-compatible
35
+ * endpoint via the AI SDK's `@ai-sdk/openai-compatible` adapter. Returns undefined
36
+ * unless both the model id and base URL are set. `apiKey` falls back to a non-empty
37
+ * placeholder so keyless self-hosted endpoints still satisfy the adapter.
38
+ */
39
+ export function openAiCompatibleProviderBlock(): Record<string, unknown> | undefined {
40
+ const id = readEnv(OPENAI_COMPATIBLE_MODEL_ID_ENV);
41
+ const baseURL = readEnv(OPENAI_COMPATIBLE_BASE_URL_ENV);
42
+ if (!id || !baseURL) return undefined;
43
+ const apiKey = readEnv(OPENAI_COMPATIBLE_API_KEY_ENV) ?? "sk-no-key-required";
44
+ return {
45
+ [OPENAI_COMPATIBLE_PROVIDER]: {
46
+ npm: "@ai-sdk/openai-compatible",
47
+ name: "OpenAI-compatible endpoint",
48
+ options: { baseURL, apiKey },
49
+ models: { [id]: { name: id } },
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * OpenTofu MCP server integration — the permissively-licensed sibling to
3
+ * [[terraformMcp]].
4
+ *
5
+ * When the `opentofu_mcp` input is on, the agent harness registers the OpenTofu
6
+ * project's registry MCP server next to infraweaver's. It gives the fixing agent
7
+ * live OpenTofu Registry knowledge — provider/resource docs, module details,
8
+ * current versions — which powers module-source-aware fixes and generation on an
9
+ * OpenTofu repo, the same way `terraform_mcp` does for the Terraform Registry.
10
+ * It's the natural pairing for the OpenTofu-first stance the engine already takes
11
+ * (the scanners fall back to the `tofu` CLI; the licence posture prefers OpenTofu's
12
+ * MPL fork over BUSL Terraform).
13
+ *
14
+ * Why the HOSTED endpoint (not a Docker image like terraform_mcp):
15
+ * - the OpenTofu MCP server has NO official Docker image; it's published as a
16
+ * hosted streamable-HTTP endpoint (OpenTofu's own first-party infrastructure)
17
+ * plus an npx package. The hosted endpoint is the zero-install path — no
18
+ * docker on the runner, no npm supply-chain surface to pin.
19
+ * - it serves PUBLIC registry data and needs NO credentials, so pointing the
20
+ * agent at it exposes no repo content or secrets — the queries are the same
21
+ * class of public lookups a `tofu init` already makes.
22
+ *
23
+ * Security posture:
24
+ * - opt-in + licence-gated (MPL-2.0): on only via the `opentofu_mcp` input or by
25
+ * naming "opentofu_mcp" in tools_enabled — the explicit, licence-aware
26
+ * acknowledgement (an external network surface the operator chooses).
27
+ * - read-only registry knowledge only; the server exposes no state/apply/deploy
28
+ * tools, so there is nothing mutating for the agent to reach.
29
+ */
30
+
31
+ import {
32
+ resolveToolSelection,
33
+ type ToolSelectionFlags,
34
+ } from "#app/utils/toolSelection";
35
+
36
+ /** OpenTofu's hosted streamable-HTTP MCP endpoint (first-party; public registry
37
+ * data; no credentials). The zero-install path — no docker, no npm pin. */
38
+ export const OPENTOFU_MCP_URL = "https://mcp.opentofu.org/mcp";
39
+
40
+ /** the registry name the server is registered under in agent MCP configs —
41
+ * matches the OpenTofu docs' own client-config examples, so agent guidance
42
+ * written against "the opentofu MCP server" finds it under the expected key. */
43
+ export const OPENTOFU_MCP_SERVER_NAME = "opentofu";
44
+
45
+ export type OpenTofuMcpResolution =
46
+ | { kind: "disabled" }
47
+ | { kind: "available"; url: string };
48
+
49
+ /**
50
+ * Decide whether the run gets the OpenTofu registry MCP server. Unlike
51
+ * `resolveTerraformMcp` there is no docker probe: the server is a hosted HTTP
52
+ * endpoint, so it's either opted-in (available) or off. A discriminated union so
53
+ * every harness handles both outcomes explicitly.
54
+ */
55
+ export function resolveOpenTofuMcp(
56
+ payload: ToolSelectionFlags,
57
+ ): OpenTofuMcpResolution {
58
+ if (!resolveToolSelection(payload).enabled("opentofu_mcp"))
59
+ return { kind: "disabled" };
60
+ return { kind: "available", url: OPENTOFU_MCP_URL };
61
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Parse + apply the action's `unsafe_overrides` input — a JSON object of env
3
+ * var overrides that mutate `process.env` at the start of a run. Designed for
4
+ * e2e testing / debugging from `workflow_dispatch`; only callers with
5
+ * `actions:write` on the repo can supply it.
6
+ *
7
+ * The `unsafe` prefix is load-bearing: GH Actions echoes the value verbatim
8
+ * in the runner's step-header log, so the raw JSON (including any values
9
+ * passed in) is visible to anyone with `actions:read` on the calling repo.
10
+ * Treat the run log as compromised for any value placed in `unsafe_overrides`.
11
+ */
12
+
13
+ import * as core from "@actions/core";
14
+
15
+ /**
16
+ * Names refused even when present in the input. Overriding these would let a
17
+ * caller escape infraweaver's scope (GITHUB_TOKEN), break runner internals
18
+ * (ACTIONS_RUNTIME_*), forge OIDC tokens (ACTIONS_ID_TOKEN_REQUEST_*), or
19
+ * substitute our server-side auth (INFRAWEAVER_API_SECRET). Customer-facing
20
+ * provider keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, CLAUDE_CODE_OAUTH_TOKEN,
21
+ * etc.) are intentionally NOT denied — overriding those is the use case.
22
+ */
23
+ export const DENIED_OVERRIDE_NAMES: ReadonlySet<string> = new Set([
24
+ "GITHUB_TOKEN",
25
+ "GH_TOKEN",
26
+ "ACTIONS_RUNTIME_TOKEN",
27
+ "ACTIONS_RUNTIME_URL",
28
+ "ACTIONS_ID_TOKEN_REQUEST_URL",
29
+ "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
30
+ "ACTIONS_CACHE_URL",
31
+ "INFRAWEAVER_API_SECRET",
32
+ "VERCEL_AUTOMATION_BYPASS_SECRET",
33
+ ]);
34
+
35
+ export interface ApplyOverridesResult {
36
+ applied: string[];
37
+ denied: string[];
38
+ }
39
+
40
+ /** Parse the JSON input. Returns `{}` for empty/whitespace. Throws on shape errors. */
41
+ export function parseOverrides(raw: string): Record<string, string> {
42
+ const trimmed = raw.trim();
43
+ if (!trimmed) return {};
44
+
45
+ let parsed: unknown;
46
+ try {
47
+ parsed = JSON.parse(trimmed);
48
+ } catch (err) {
49
+ throw new Error(
50
+ `invalid UNSAFE_OVERRIDES: not valid JSON (${err instanceof Error ? err.message : String(err)})`,
51
+ );
52
+ }
53
+
54
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
55
+ throw new Error(`invalid UNSAFE_OVERRIDES: must be a JSON object`);
56
+ }
57
+
58
+ const out: Record<string, string> = {};
59
+ for (const [key, value] of Object.entries(
60
+ parsed as Record<string, unknown>,
61
+ )) {
62
+ if (typeof value !== "string") {
63
+ throw new Error(
64
+ `invalid UNSAFE_OVERRIDES: key "${key}" must have a string value (got ${typeof value})`,
65
+ );
66
+ }
67
+ out[key] = value;
68
+ }
69
+ return out;
70
+ }
71
+
72
+ /**
73
+ * Mutate `params.env` in place with the supplied JSON overrides, skipping any
74
+ * names in `DENIED_OVERRIDE_NAMES`. Each applied value is registered with
75
+ * `core.setSecret` so the runner masks it in subsequent log output, and the
76
+ * raw `UNSAFE_OVERRIDES` env var is deleted so spawned subprocesses don't
77
+ * inherit the original JSON (which would defeat both the deny-list and the
78
+ * masking by exposing the values verbatim).
79
+ *
80
+ * Returns the applied/denied breakdown so the caller can render an audit log.
81
+ */
82
+ export function applyOverrides(params: {
83
+ raw: string;
84
+ env: NodeJS.ProcessEnv;
85
+ }): ApplyOverridesResult {
86
+ const overrides = parseOverrides(params.raw);
87
+ const applied: string[] = [];
88
+ const denied: string[] = [];
89
+ for (const [key, value] of Object.entries(overrides)) {
90
+ if (DENIED_OVERRIDE_NAMES.has(key)) {
91
+ denied.push(key);
92
+ continue;
93
+ }
94
+ if (value.length > 0) core.setSecret(value);
95
+ params.env[key] = value;
96
+ applied.push(key);
97
+ }
98
+ delete params.env.UNSAFE_OVERRIDES;
99
+ return { applied, denied };
100
+ }
@@ -0,0 +1,257 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile } from "node:fs/promises";
3
+ import { delimiter, join } from "node:path";
4
+ import semver from "semver";
5
+ import { log } from "#app/utils/cli";
6
+ import { spawn } from "#app/utils/subprocess";
7
+
8
+ export type SupportedPackageManager = "npm" | "pnpm" | "yarn" | "bun";
9
+
10
+ const SUPPORTED_NAMES: readonly SupportedPackageManager[] = ["npm", "pnpm", "yarn", "bun"];
11
+
12
+ // corepack ships pnpm + yarn shims out of the box. it does not ship bun or
13
+ // npm (npm comes with node). callers fall back to the legacy npm-install-g
14
+ // path for managers outside this set.
15
+ const COREPACK_MANAGED: readonly SupportedPackageManager[] = ["pnpm", "yarn"];
16
+
17
+ export interface PackageManagerSpec {
18
+ name: SupportedPackageManager;
19
+ /**
20
+ * either a concrete semver (e.g. "11.1.1") or a range (e.g. "^11.0.0").
21
+ * `concrete` distinguishes — corepack only accepts concrete versions.
22
+ */
23
+ version: string;
24
+ concrete: boolean;
25
+ /** which package.json field this came from */
26
+ source: "devEngines" | "packageManager";
27
+ }
28
+
29
+ interface PackageJson {
30
+ packageManager?: string;
31
+ devEngines?: {
32
+ packageManager?: {
33
+ name?: string;
34
+ version?: string;
35
+ onFail?: string;
36
+ };
37
+ };
38
+ }
39
+
40
+ function isSupported(name: string): name is SupportedPackageManager {
41
+ return (SUPPORTED_NAMES as readonly string[]).includes(name);
42
+ }
43
+
44
+ function parsePackageManagerField(value: string): PackageManagerSpec | null {
45
+ // npm spec form is "name@version[+integrity]" — corepack adds the integrity
46
+ // suffix; we strip it because it's not a semver.
47
+ const withoutHash = value.split("+")[0]!;
48
+ const at = withoutHash.lastIndexOf("@");
49
+ if (at <= 0) return null;
50
+ const name = withoutHash.slice(0, at);
51
+ const version = withoutHash.slice(at + 1);
52
+ if (!isSupported(name)) {
53
+ log.warning(`» unknown packageManager in package.json: ${value}`);
54
+ return null;
55
+ }
56
+ return {
57
+ name,
58
+ version,
59
+ concrete: semver.valid(version) !== null,
60
+ source: "packageManager",
61
+ };
62
+ }
63
+
64
+ function parseDevEnginesField(
65
+ field: NonNullable<NonNullable<PackageJson["devEngines"]>["packageManager"]>,
66
+ ): PackageManagerSpec | null {
67
+ if (!field.name || !field.version) return null;
68
+ if (!isSupported(field.name)) {
69
+ log.warning(`» unknown devEngines.packageManager.name in package.json: ${field.name}`);
70
+ return null;
71
+ }
72
+ const version = field.version.trim();
73
+ return {
74
+ name: field.name,
75
+ version,
76
+ concrete: semver.valid(version) !== null,
77
+ source: "devEngines",
78
+ };
79
+ }
80
+
81
+ /**
82
+ * resolve the project's intended package manager from package.json. precedence
83
+ * matches pnpm 11+: `devEngines.packageManager` wins over `packageManager`.
84
+ * when both are present, a concrete `packageManager` that satisfies a
85
+ * `devEngines` range is preferred (we can pin it via corepack); otherwise
86
+ * we warn on disagreement and stick with `devEngines`.
87
+ */
88
+ export async function resolvePackageManagerSpec(cwd: string): Promise<PackageManagerSpec | null> {
89
+ const pkgPath = join(cwd, "package.json");
90
+ if (!existsSync(pkgPath)) return null;
91
+
92
+ let pkg: PackageJson;
93
+ try {
94
+ pkg = JSON.parse(await readFile(pkgPath, "utf-8")) as PackageJson;
95
+ } catch (err) {
96
+ log.warning(
97
+ `» failed to parse package.json for package manager resolution: ${err instanceof Error ? err.message : String(err)}`,
98
+ );
99
+ return null;
100
+ }
101
+
102
+ const devSpec = pkg.devEngines?.packageManager
103
+ ? parseDevEnginesField(pkg.devEngines.packageManager)
104
+ : null;
105
+ const pmSpec = pkg.packageManager?.trim()
106
+ ? parsePackageManagerField(pkg.packageManager.trim())
107
+ : null;
108
+
109
+ if (!devSpec) return pmSpec;
110
+ if (!pmSpec) return devSpec;
111
+
112
+ if (devSpec.name !== pmSpec.name) {
113
+ log.warning(
114
+ `» devEngines.packageManager (${devSpec.name}) disagrees with packageManager (${pmSpec.name}); using devEngines per pnpm 11 precedence`,
115
+ );
116
+ return devSpec;
117
+ }
118
+
119
+ // same manager — try to land on a concrete version we can pin via corepack.
120
+ if (devSpec.concrete) {
121
+ if (pmSpec.concrete && devSpec.version !== pmSpec.version) {
122
+ log.warning(
123
+ `» devEngines.packageManager (${devSpec.version}) disagrees with packageManager (${pmSpec.version}); using devEngines per pnpm 11 precedence`,
124
+ );
125
+ }
126
+ return devSpec;
127
+ }
128
+
129
+ if (pmSpec.concrete && semver.satisfies(pmSpec.version, devSpec.version)) {
130
+ return pmSpec;
131
+ }
132
+
133
+ if (pmSpec.concrete) {
134
+ log.warning(
135
+ `» packageManager (${pmSpec.version}) does not satisfy devEngines range (${devSpec.version}); using devEngines`,
136
+ );
137
+ }
138
+ return devSpec;
139
+ }
140
+
141
+ interface CorepackResult {
142
+ exitCode: number;
143
+ stderr: string;
144
+ }
145
+
146
+ async function runCorepack(args: string[]): Promise<CorepackResult> {
147
+ const result = await spawn({
148
+ cmd: "corepack",
149
+ args,
150
+ env: {
151
+ PATH: process.env.PATH || "",
152
+ HOME: process.env.HOME || "",
153
+ COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
154
+ },
155
+ onStdout: (chunk) => process.stdout.write(chunk),
156
+ onStderr: (chunk) => process.stderr.write(chunk),
157
+ });
158
+ return { exitCode: result.exitCode, stderr: result.stderr };
159
+ }
160
+
161
+ async function currentVersion(name: SupportedPackageManager): Promise<string | null> {
162
+ const result = await spawn({
163
+ cmd: name,
164
+ args: ["--version"],
165
+ env: { PATH: process.env.PATH || "" },
166
+ });
167
+ if (result.exitCode !== 0) return null;
168
+ return result.stdout.trim();
169
+ }
170
+
171
+ /** the per-run directory the corepack shim is installed into. deliberately
172
+ * NOT the node bin dir: that's npm's `-g` target, and a corepack shim sitting
173
+ * there makes a customer setup script's `npm i -g pnpm` abort with EEXIST
174
+ * (npm refuses to clobber a binary it doesn't own). lives under the run
175
+ * tmpdir so it's cleaned up with everything else. */
176
+ export function packageManagerBinDir(tmpdir: string): string {
177
+ return join(tmpdir, "pm-bin");
178
+ }
179
+
180
+ export interface EnsurePackageManagerParams {
181
+ spec: PackageManagerSpec;
182
+ /** directory to install the corepack shim into (see `packageManagerBinDir`).
183
+ * prepended to PATH so the pinned binary resolves by name. */
184
+ binDir: string;
185
+ }
186
+
187
+ /**
188
+ * ensure the requested package manager is on PATH at the declared version,
189
+ * provisioning via corepack when applicable. returns true if PATH now
190
+ * resolves to that version, false if we couldn't pin it (in which case
191
+ * the caller should treat PATH as untrusted and may fall back to its
192
+ * legacy install path).
193
+ *
194
+ * the corepack shim is installed into `params.binDir` (prepended to PATH),
195
+ * not the node bin dir, so a later `npm i -g pnpm` in a setup hook can't
196
+ * collide with it. our dir wins the PATH lookup, so the pinned version is
197
+ * also what resolves even if that `npm i -g` succeeds into the node bin dir.
198
+ *
199
+ * never throws: network failure, missing corepack, range-only versions —
200
+ * all degrade to "log warning, return false". the existing PATH binary
201
+ * still works; we just don't get our version guarantee.
202
+ */
203
+ export async function ensurePackageManager(params: EnsurePackageManagerParams): Promise<boolean> {
204
+ const spec = params.spec;
205
+ if (spec.name === "npm") return true;
206
+
207
+ if (!(COREPACK_MANAGED as readonly string[]).includes(spec.name)) {
208
+ return false;
209
+ }
210
+
211
+ if (!spec.concrete) {
212
+ log.warning(
213
+ `» ${spec.name} ${spec.source} version is a range (${spec.version}); corepack requires a concrete pin. leaving PATH unchanged.`,
214
+ );
215
+ return false;
216
+ }
217
+
218
+ const existing = await currentVersion(spec.name);
219
+ if (existing === spec.version) {
220
+ log.info(`» ${spec.name}@${spec.version} already active`);
221
+ return true;
222
+ }
223
+
224
+ log.info(
225
+ `» corepack prepare ${spec.name}@${spec.version} --activate (shim dir: ${params.binDir})`,
226
+ );
227
+
228
+ await mkdir(params.binDir, { recursive: true });
229
+ const enable = await runCorepack(["enable", "--install-directory", params.binDir, spec.name]);
230
+ if (enable.exitCode !== 0) {
231
+ log.warning(
232
+ `» corepack enable failed (exit ${enable.exitCode}); leaving ${spec.name} from PATH. stderr: ${enable.stderr.trim() || "(empty)"}`,
233
+ );
234
+ return false;
235
+ }
236
+
237
+ // shim dir first so `pnpm` resolves to the pinned binary and shadows any
238
+ // `pnpm` a later `npm i -g pnpm` drops into the node bin dir.
239
+ process.env.PATH = `${params.binDir}${delimiter}${process.env.PATH ?? ""}`;
240
+
241
+ const prepare = await runCorepack(["prepare", `${spec.name}@${spec.version}`, "--activate"]);
242
+ if (prepare.exitCode !== 0) {
243
+ log.warning(
244
+ `» corepack prepare ${spec.name}@${spec.version} failed (exit ${prepare.exitCode}); leaving ${spec.name} from PATH. stderr: ${prepare.stderr.trim() || "(empty)"}`,
245
+ );
246
+ return false;
247
+ }
248
+
249
+ const after = await currentVersion(spec.name);
250
+ if (after !== spec.version) {
251
+ log.warning(
252
+ `» corepack activated ${spec.name}@${spec.version} but PATH still resolves to ${after ?? "(missing)"}; continuing anyway`,
253
+ );
254
+ }
255
+
256
+ return true;
257
+ }