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.
- package/LICENSE +661 -0
- package/README.md +244 -0
- package/dist/agents/claude.d.ts +113 -0
- package/dist/agents/claudePretoolGate.d.ts +137 -0
- package/dist/agents/dispatchMetrics.d.ts +77 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +46 -0
- package/dist/agents/opencode.d.ts +284 -0
- package/dist/agents/opencodePlugin.d.ts +96 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +126 -0
- package/dist/agents/reviewer.d.ts +41 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +246 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/tokenQuota.d.ts +118 -0
- package/dist/agents/writeGateSource.d.ts +22 -0
- package/dist/agents/writePolicy.d.ts +79 -0
- package/dist/brand.d.ts +67 -0
- package/dist/cli.mjs +247846 -0
- package/dist/external.d.ts +227 -0
- package/dist/i18n/scaffolding.d.ts +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +247061 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +2398 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/assess.d.ts +157 -0
- package/dist/mcp/capabilityContext.d.ts +71 -0
- package/dist/mcp/changeSummary.d.ts +52 -0
- package/dist/mcp/checkSuite.d.ts +27 -0
- package/dist/mcp/checkout.d.ts +92 -0
- package/dist/mcp/comment.d.ts +127 -0
- package/dist/mcp/commitInfo.d.ts +11 -0
- package/dist/mcp/crosswalk.d.ts +178 -0
- package/dist/mcp/crosswalkDigest.d.ts +1 -0
- package/dist/mcp/cyberEssentials.d.ts +24 -0
- package/dist/mcp/dashboard.d.ts +125 -0
- package/dist/mcp/dependencies.d.ts +12 -0
- package/dist/mcp/frameworks.d.ts +74 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +60 -0
- package/dist/mcp/guardrails.d.ts +222 -0
- package/dist/mcp/issue.d.ts +20 -0
- package/dist/mcp/issueComments.d.ts +11 -0
- package/dist/mcp/issueEvents.d.ts +11 -0
- package/dist/mcp/issueInfo.d.ts +11 -0
- package/dist/mcp/labels.d.ts +14 -0
- package/dist/mcp/localContext.d.ts +26 -0
- package/dist/mcp/moduleExtraction.d.ts +79 -0
- package/dist/mcp/moduleTests.d.ts +106 -0
- package/dist/mcp/modules.d.ts +198 -0
- package/dist/mcp/output.d.ts +16 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +50 -0
- package/dist/mcp/pr.d.ts +64 -0
- package/dist/mcp/prInfo.d.ts +11 -0
- package/dist/mcp/providerSchema.d.ts +52 -0
- package/dist/mcp/review.d.ts +212 -0
- package/dist/mcp/reviewComments.d.ts +245 -0
- package/dist/mcp/roots.d.ts +60 -0
- package/dist/mcp/scope.d.ts +26 -0
- package/dist/mcp/selectMode.d.ts +20 -0
- package/dist/mcp/server.d.ts +61 -0
- package/dist/mcp/shared.d.ts +62 -0
- package/dist/mcp/shell.d.ts +58 -0
- package/dist/mcp/staleFix.d.ts +138 -0
- package/dist/mcp/terraform/azurePrices.d.ts +8 -0
- package/dist/mcp/terraform/baseline.d.ts +57 -0
- package/dist/mcp/terraform/concernResult.d.ts +38 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/cspm.d.ts +80 -0
- package/dist/mcp/terraform/currency.d.ts +110 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/eidas.d.ts +100 -0
- package/dist/mcp/terraform/evidence.d.ts +343 -0
- package/dist/mcp/terraform/findings.d.ts +76 -0
- package/dist/mcp/terraform/fixMemory.d.ts +81 -0
- package/dist/mcp/terraform/governance.d.ts +56 -0
- package/dist/mcp/terraform/hcl.d.ts +115 -0
- package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
- package/dist/mcp/terraform/idleFloor.d.ts +78 -0
- package/dist/mcp/terraform/keyless.d.ts +68 -0
- package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
- package/dist/mcp/terraform/nativeRules.d.ts +38 -0
- package/dist/mcp/terraform/nativeScan.d.ts +11 -0
- package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
- package/dist/mcp/terraform/normalization.d.ts +52 -0
- package/dist/mcp/terraform/oscal.d.ts +103 -0
- package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
- package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
- package/dist/mcp/terraform/paths.d.ts +28 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/planDrift.d.ts +199 -0
- package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
- package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
- package/dist/mcp/terraform/refactor.d.ts +183 -0
- package/dist/mcp/terraform/registry.d.ts +108 -0
- package/dist/mcp/terraform/risk.d.ts +41 -0
- package/dist/mcp/terraform/scanSession.d.ts +116 -0
- package/dist/mcp/terraform/scannerCache.d.ts +15 -0
- package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
- package/dist/mcp/terraform/scannerJson.d.ts +13 -0
- package/dist/mcp/terraform/scanners.d.ts +205 -0
- package/dist/mcp/terraform/signing.d.ts +61 -0
- package/dist/mcp/terraform/stateLocking.d.ts +18 -0
- package/dist/mcp/terraform/subprocess.d.ts +43 -0
- package/dist/mcp/terraform/suppressions.d.ts +90 -0
- package/dist/mcp/terraform/taxonomy.d.ts +39 -0
- package/dist/mcp/terraform/tfquery.d.ts +47 -0
- package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
- package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
- package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
- package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
- package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
- package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
- package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
- package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
- package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
- package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
- package/dist/mcp/terraform/tools/plan.d.ts +59 -0
- package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
- package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
- package/dist/mcp/terraform/tools/scan.d.ts +114 -0
- package/dist/mcp/terraform/tools/validate.d.ts +31 -0
- package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
- package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
- package/dist/mcp/terraform/tools.d.ts +16 -0
- package/dist/mcp/terraform/transformPacks.d.ts +267 -0
- package/dist/mcp/terraform/types.d.ts +226 -0
- package/dist/mcp/terraform/verification.d.ts +81 -0
- package/dist/mcp/terraform/vex.d.ts +115 -0
- package/dist/mcp/terraform/writeOnly.d.ts +67 -0
- package/dist/mcp/terraform.d.ts +51 -0
- package/dist/mcp/terratest.d.ts +85 -0
- package/dist/mcp/untrustedContent.d.ts +76 -0
- package/dist/mcp/upload.d.ts +8 -0
- package/dist/models.d.ts +181 -0
- package/dist/modes/address-reviews.d.ts +2 -0
- package/dist/modes/assess.d.ts +2 -0
- package/dist/modes/build.d.ts +2 -0
- package/dist/modes/compliance-audit.d.ts +2 -0
- package/dist/modes/cost-optimization.d.ts +2 -0
- package/dist/modes/drift-detect.d.ts +2 -0
- package/dist/modes/fix.d.ts +2 -0
- package/dist/modes/incremental-review.d.ts +2 -0
- package/dist/modes/index.d.ts +24 -0
- package/dist/modes/modernize-deprecated.d.ts +2 -0
- package/dist/modes/plan.d.ts +2 -0
- package/dist/modes/policy-gate.d.ts +2 -0
- package/dist/modes/prFormats.d.ts +3 -0
- package/dist/modes/refactor.d.ts +2 -0
- package/dist/modes/refresh-remediation.d.ts +2 -0
- package/dist/modes/remediate-and-refactor.d.ts +2 -0
- package/dist/modes/remediate.d.ts +2 -0
- package/dist/modes/resolve-conflicts.d.ts +2 -0
- package/dist/modes/review.d.ts +2 -0
- package/dist/modes/summarize-pr.d.ts +2 -0
- package/dist/modes/task.d.ts +2 -0
- package/dist/modes/terraform-code-review.d.ts +2 -0
- package/dist/modes/types.d.ts +9 -0
- package/dist/modes/update-dependencies.d.ts +2 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +31 -0
- package/dist/reviewQuality.d.ts +107 -0
- package/dist/skills/terraform-best-practices/SKILL.md +379 -0
- package/dist/toolState.d.ts +123 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +47 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/annotations.d.ts +17 -0
- package/dist/utils/apiFetch.d.ts +11 -0
- package/dist/utils/apiKeys.d.ts +48 -0
- package/dist/utils/apiUrl.d.ts +28 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/baseRefConfig.d.ts +121 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/changeImpact.d.ts +101 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/cloudClient.d.ts +33 -0
- package/dist/utils/cloudFetchCore.d.ts +42 -0
- package/dist/utils/cloudReport.d.ts +114 -0
- package/dist/utils/codexHome.d.ts +29 -0
- package/dist/utils/codexOAuth.d.ts +60 -0
- package/dist/utils/diffCoverage.d.ts +63 -0
- package/dist/utils/env.d.ts +46 -0
- package/dist/utils/errorReport.d.ts +17 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +84 -0
- package/dist/utils/gitAuthServer.d.ts +24 -0
- package/dist/utils/github.d.ts +104 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/infraweaverConfig.d.ts +56 -0
- package/dist/utils/install.d.ts +37 -0
- package/dist/utils/instructions.d.ts +48 -0
- package/dist/utils/keylessOidc.d.ts +28 -0
- package/dist/utils/leapingComment.d.ts +11 -0
- package/dist/utils/learnings.d.ts +62 -0
- package/dist/utils/learningsTruncate.d.ts +25 -0
- package/dist/utils/lifecycle.d.ts +57 -0
- package/dist/utils/log.d.ts +111 -0
- package/dist/utils/modelResolver.d.ts +63 -0
- package/dist/utils/moduleFetch.d.ts +39 -0
- package/dist/utils/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -0
- package/dist/utils/openaiCompatible.d.ts +26 -0
- package/dist/utils/opentofuMcp.d.ts +50 -0
- package/dist/utils/overrides.d.ts +40 -0
- package/dist/utils/packageManager.d.ts +49 -0
- package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
- package/dist/utils/payload.d.ts +249 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/presets.d.ts +36 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/proxyModel.d.ts +76 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/redact.d.ts +33 -0
- package/dist/utils/registryAuth.d.ts +52 -0
- package/dist/utils/remediationCommand.d.ts +58 -0
- package/dist/utils/retry.d.ts +38 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +112 -0
- package/dist/utils/runContextData.d.ts +42 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +89 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +34 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +44 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +81 -0
- package/dist/utils/terraformMcp.d.ts +46 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/timer.d.ts +23 -0
- package/dist/utils/todoTracking.d.ts +16 -0
- package/dist/utils/token.d.ts +52 -0
- package/dist/utils/toolLicensing.d.ts +56 -0
- package/dist/utils/toolSelection.d.ts +73 -0
- package/dist/utils/toon.d.ts +16 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/vertex.d.ts +16 -0
- package/dist/utils/workflow.d.ts +13 -0
- package/package.json +132 -0
- package/src/agents/claude.ts +1588 -0
- package/src/agents/claudePretoolGate.ts +286 -0
- package/src/agents/dispatchMetrics.ts +162 -0
- package/src/agents/gateServer.ts +178 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +164 -0
- package/src/agents/opencode.ts +1600 -0
- package/src/agents/opencodePlugin.ts +304 -0
- package/src/agents/opencodeShared.ts +134 -0
- package/src/agents/postRun.ts +615 -0
- package/src/agents/reviewer.ts +134 -0
- package/src/agents/sessionLabeler.ts +185 -0
- package/src/agents/shared.ts +377 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/tokenQuota.ts +203 -0
- package/src/agents/writeGateSource.ts +148 -0
- package/src/agents/writePolicy.ts +132 -0
- package/src/brand.ts +84 -0
- package/src/cli.ts +117 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +126 -0
- package/src/commands/verifyEvidence.ts +100 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.ts +109 -0
- package/src/external.ts +308 -0
- package/src/i18n/scaffolding.ts +191 -0
- package/src/index.ts +7 -0
- package/src/internal/index.ts +74 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.ts +815 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
- package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
- package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
- package/src/mcp/arkConfig.ts +7 -0
- package/src/mcp/assess.ts +723 -0
- package/src/mcp/capabilityContext.ts +80 -0
- package/src/mcp/changeSummary.ts +145 -0
- package/src/mcp/checkSuite.ts +261 -0
- package/src/mcp/checkout.ts +1015 -0
- package/src/mcp/comment.ts +683 -0
- package/src/mcp/commitInfo.ts +57 -0
- package/src/mcp/crosswalk.ts +1253 -0
- package/src/mcp/crosswalkDigest.ts +5 -0
- package/src/mcp/cyberEssentials.ts +60 -0
- package/src/mcp/dashboard.ts +232 -0
- package/src/mcp/dependencies.ts +190 -0
- package/src/mcp/frameworks.ts +236 -0
- package/src/mcp/geminiSanitizer.ts +212 -0
- package/src/mcp/git.ts +1116 -0
- package/src/mcp/guardrails.ts +771 -0
- package/src/mcp/issue.ts +74 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.ts +35 -0
- package/src/mcp/localContext.ts +65 -0
- package/src/mcp/localServer.ts +217 -0
- package/src/mcp/moduleExtraction.ts +368 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.ts +752 -0
- package/src/mcp/output.ts +71 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.ts +226 -0
- package/src/mcp/pr.ts +238 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.ts +1081 -0
- package/src/mcp/reviewComments.ts +1137 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.ts +82 -0
- package/src/mcp/selectMode.ts +207 -0
- package/src/mcp/server.ts +498 -0
- package/src/mcp/shared.ts +120 -0
- package/src/mcp/shell.ts +631 -0
- package/src/mcp/staleFix.ts +557 -0
- package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
- package/src/mcp/terraform/azurePrices.ts +38 -0
- package/src/mcp/terraform/baseline.ts +172 -0
- package/src/mcp/terraform/concernResult.ts +111 -0
- package/src/mcp/terraform/cost.ts +175 -0
- package/src/mcp/terraform/cspm.ts +326 -0
- package/src/mcp/terraform/currency.ts +354 -0
- package/src/mcp/terraform/decisions.ts +590 -0
- package/src/mcp/terraform/eidas.ts +134 -0
- package/src/mcp/terraform/evidence.ts +771 -0
- package/src/mcp/terraform/findings.ts +383 -0
- package/src/mcp/terraform/fixMemory.ts +253 -0
- package/src/mcp/terraform/governance.ts +231 -0
- package/src/mcp/terraform/hcl.ts +273 -0
- package/src/mcp/terraform/iacLanguages.ts +431 -0
- package/src/mcp/terraform/idleFloor.ts +304 -0
- package/src/mcp/terraform/keyless.ts +123 -0
- package/src/mcp/terraform/moduleDocs.ts +303 -0
- package/src/mcp/terraform/nativeRules.ts +152 -0
- package/src/mcp/terraform/nativeScan.ts +67 -0
- package/src/mcp/terraform/noiseProfile.ts +183 -0
- package/src/mcp/terraform/normalization.ts +265 -0
- package/src/mcp/terraform/oscal.ts +283 -0
- package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
- package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
- package/src/mcp/terraform/paths.ts +59 -0
- package/src/mcp/terraform/plan.ts +377 -0
- package/src/mcp/terraform/planDrift.ts +520 -0
- package/src/mcp/terraform/policyAuthor.ts +886 -0
- package/src/mcp/terraform/prowlerOcsf.ts +380 -0
- package/src/mcp/terraform/refactor.ts +879 -0
- package/src/mcp/terraform/registry.ts +317 -0
- package/src/mcp/terraform/risk.ts +99 -0
- package/src/mcp/terraform/scanSession.ts +242 -0
- package/src/mcp/terraform/scannerCache.ts +86 -0
- package/src/mcp/terraform/scannerEgress.ts +141 -0
- package/src/mcp/terraform/scannerJson.ts +22 -0
- package/src/mcp/terraform/scanners.ts +1134 -0
- package/src/mcp/terraform/signing.ts +151 -0
- package/src/mcp/terraform/stateLocking.ts +106 -0
- package/src/mcp/terraform/subprocess.ts +102 -0
- package/src/mcp/terraform/suppressions.ts +551 -0
- package/src/mcp/terraform/taxonomy.ts +0 -0
- package/src/mcp/terraform/tfquery.ts +159 -0
- package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
- package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
- package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
- package/src/mcp/terraform/tools/emitCost.ts +130 -0
- package/src/mcp/terraform/tools/emitSarif.ts +108 -0
- package/src/mcp/terraform/tools/fixMemory.ts +82 -0
- package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
- package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
- package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
- package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
- package/src/mcp/terraform/tools/plan.ts +384 -0
- package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
- package/src/mcp/terraform/tools/readFindings.ts +138 -0
- package/src/mcp/terraform/tools/scan.ts +303 -0
- package/src/mcp/terraform/tools/validate.ts +160 -0
- package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
- package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
- package/src/mcp/terraform/tools.ts +62 -0
- package/src/mcp/terraform/transformPacks.ts +594 -0
- package/src/mcp/terraform/types.ts +382 -0
- package/src/mcp/terraform/verification.ts +150 -0
- package/src/mcp/terraform/vex.ts +367 -0
- package/src/mcp/terraform/writeOnly.ts +252 -0
- package/src/mcp/terraform.ts +52 -0
- package/src/mcp/terratest.ts +215 -0
- package/src/mcp/untrustedContent.ts +126 -0
- package/src/mcp/upload.ts +123 -0
- package/src/models.ts +753 -0
- package/src/modes/address-reviews.ts +35 -0
- package/src/modes/assess.ts +27 -0
- package/src/modes/build.ts +86 -0
- package/src/modes/compliance-audit.ts +25 -0
- package/src/modes/cost-optimization.ts +36 -0
- package/src/modes/drift-detect.ts +31 -0
- package/src/modes/fix.ts +29 -0
- package/src/modes/incremental-review.ts +103 -0
- package/src/modes/index.ts +94 -0
- package/src/modes/modernize-deprecated.ts +36 -0
- package/src/modes/plan.ts +21 -0
- package/src/modes/policy-gate.ts +27 -0
- package/src/modes/prFormats.ts +325 -0
- package/src/modes/refactor.ts +74 -0
- package/src/modes/refresh-remediation.ts +40 -0
- package/src/modes/remediate-and-refactor.ts +42 -0
- package/src/modes/remediate.ts +78 -0
- package/src/modes/resolve-conflicts.ts +32 -0
- package/src/modes/review.ts +135 -0
- package/src/modes/summarize-pr.ts +42 -0
- package/src/modes/task.ts +26 -0
- package/src/modes/terraform-code-review.ts +68 -0
- package/src/modes/types.ts +17 -0
- package/src/modes/update-dependencies.ts +44 -0
- package/src/prep/index.ts +96 -0
- package/src/prep/installNodeDependencies.ts +251 -0
- package/src/prep/installPythonDependencies.ts +235 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.ts +237 -0
- package/src/runCli.ts +335 -0
- package/src/skills/terraform-best-practices/SKILL.md +379 -0
- package/src/toolState.ts +241 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.ts +245 -0
- package/src/utils/agentHangReport.ts +180 -0
- package/src/utils/annotations.ts +56 -0
- package/src/utils/apiFetch.ts +19 -0
- package/src/utils/apiKeys.ts +305 -0
- package/src/utils/apiUrl.ts +42 -0
- package/src/utils/assets.ts +123 -0
- package/src/utils/baseRefConfig.ts +254 -0
- package/src/utils/billingErrors.ts +210 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildInfraweaverFooter.ts +104 -0
- package/src/utils/byokFallback.ts +137 -0
- package/src/utils/changeImpact.ts +330 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +36 -0
- package/src/utils/cloudClient.ts +57 -0
- package/src/utils/cloudFetchCore.ts +139 -0
- package/src/utils/cloudReport.ts +316 -0
- package/src/utils/codexHome.ts +204 -0
- package/src/utils/codexOAuth.ts +154 -0
- package/src/utils/codexRefreshDetect.ts +36 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/env.ts +84 -0
- package/src/utils/errorReport.ts +94 -0
- package/src/utils/exitHandler.ts +35 -0
- package/src/utils/fixDoubleEscapedString.ts +9 -0
- package/src/utils/ghaCore.ts +13 -0
- package/src/utils/gitAuth.ts +268 -0
- package/src/utils/gitAuthServer.ts +186 -0
- package/src/utils/github.ts +676 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.ts +198 -0
- package/src/utils/infraweaverConfig.ts +202 -0
- package/src/utils/install.ts +279 -0
- package/src/utils/instructions.ts +640 -0
- package/src/utils/keylessOidc.ts +47 -0
- package/src/utils/leapingComment.ts +20 -0
- package/src/utils/learnings.ts +145 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.ts +452 -0
- package/src/utils/modelResolver.ts +105 -0
- package/src/utils/moduleFetch.ts +138 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +86 -0
- package/src/utils/openaiCompatible.ts +52 -0
- package/src/utils/opentofuMcp.ts +61 -0
- package/src/utils/overrides.ts +100 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.ts +173 -0
- package/src/utils/payload.ts +1206 -0
- package/src/utils/prSummary.ts +148 -0
- package/src/utils/presets.ts +105 -0
- package/src/utils/progressComment.ts +266 -0
- package/src/utils/providerErrors.ts +189 -0
- package/src/utils/proxyModel.ts +239 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/redact.ts +86 -0
- package/src/utils/registryAuth.ts +115 -0
- package/src/utils/remediationCommand.ts +144 -0
- package/src/utils/retry.ts +180 -0
- package/src/utils/reviewCleanup.ts +116 -0
- package/src/utils/run.ts +100 -0
- package/src/utils/runContext.ts +276 -0
- package/src/utils/runContextData.ts +121 -0
- package/src/utils/runErrorRenderer.ts +282 -0
- package/src/utils/runFixture.ts +91 -0
- package/src/utils/runLifecycle.ts +360 -0
- package/src/utils/runStartupLog.ts +67 -0
- package/src/utils/secrets.ts +208 -0
- package/src/utils/setup.ts +368 -0
- package/src/utils/shell.ts +132 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.ts +474 -0
- package/src/utils/terraformMcp.ts +99 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.ts +168 -0
- package/src/utils/token.ts +294 -0
- package/src/utils/toolLicensing.ts +152 -0
- package/src/utils/toolSelection.ts +239 -0
- package/src/utils/toon.ts +76 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +94 -0
- package/src/utils/workflow.ts +25 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline + gate (2c).
|
|
3
|
+
*
|
|
4
|
+
* A committed `.infraweaver.baseline.json` records the line-independent keys of the
|
|
5
|
+
* concerns that already existed when the baseline was taken. With a baseline in
|
|
6
|
+
* place the check fails only on findings that are NEW relative to it (and at or
|
|
7
|
+
* above the gate severity) — so a first scan on a messy repo doesn't fail the
|
|
8
|
+
* build wholesale, and the team isn't pushed to suppress everything at once.
|
|
9
|
+
*
|
|
10
|
+
* The gate defaults to High/Critical: only NEW high/critical findings fail the
|
|
11
|
+
* check. Lower-severity findings are still reported (and counted) — just not
|
|
12
|
+
* gated — matching the Checkov/tfsec "baseline + severity gate" convention.
|
|
13
|
+
*
|
|
14
|
+
* Pure module (the file read lives in `loadBaseline`); the gate evaluation is
|
|
15
|
+
* fully unit-testable.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { BASELINE_FILE } from "#app/brand";
|
|
20
|
+
import {
|
|
21
|
+
type Concern,
|
|
22
|
+
concernKeyOf,
|
|
23
|
+
SEVERITY_RANK,
|
|
24
|
+
type Severity,
|
|
25
|
+
} from "#app/mcp/terraform/types";
|
|
26
|
+
|
|
27
|
+
export const BASELINE_FILENAME = BASELINE_FILE;
|
|
28
|
+
|
|
29
|
+
/** Findings at/above this severity fail the gate. High = high + critical. */
|
|
30
|
+
export const DEFAULT_GATE_SEVERITY: Severity = "high";
|
|
31
|
+
|
|
32
|
+
export interface BaselineFile {
|
|
33
|
+
version: 1;
|
|
34
|
+
/** ISO date the baseline was taken (informational). */
|
|
35
|
+
created?: string;
|
|
36
|
+
/** line-independent concern keys (see `concernKeyOf`) present at baseline time. */
|
|
37
|
+
keys: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The serialized baseline for the current concern set (what a `--write-baseline` emits). */
|
|
41
|
+
export function serializeBaseline(
|
|
42
|
+
concerns: Concern[],
|
|
43
|
+
created: string,
|
|
44
|
+
): BaselineFile {
|
|
45
|
+
const keys = Array.from(new Set(concerns.map((c) => concernKeyOf(c)))).sort();
|
|
46
|
+
return { version: 1, created, keys };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Parse baseline JSON into a key set; tolerant of a malformed/old file (→ null). */
|
|
50
|
+
export function parseBaseline(raw: string): Set<string> | null {
|
|
51
|
+
let doc: unknown;
|
|
52
|
+
try {
|
|
53
|
+
doc = JSON.parse(raw);
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!doc || typeof doc !== "object") return null;
|
|
58
|
+
const keys = (doc as { keys?: unknown }).keys;
|
|
59
|
+
if (!Array.isArray(keys)) return null;
|
|
60
|
+
return new Set(keys.filter((k): k is string => typeof k === "string"));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Load `.infraweaver.baseline.json` under `cwd`; null when absent/unreadable/malformed. */
|
|
64
|
+
export function loadBaseline(cwd: string | undefined): Set<string> | null {
|
|
65
|
+
const root = cwd ?? process.cwd();
|
|
66
|
+
let raw: string;
|
|
67
|
+
try {
|
|
68
|
+
raw = readFileSync(join(root, BASELINE_FILENAME), "utf-8");
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return parseBaseline(raw);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface LoadedBaseline {
|
|
76
|
+
keys: Set<string>;
|
|
77
|
+
/** ISO date the baseline was taken — the report's "since baseline (…)" label. */
|
|
78
|
+
created: string | null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Load the baseline WITH its `created` date (the report's delta needs both the
|
|
82
|
+
* keys to diff against and the date to label the comparison). Null when
|
|
83
|
+
* absent/unreadable/malformed. `loadBaseline` stays the keys-only path the gate uses. */
|
|
84
|
+
export function loadBaselineWithMeta(
|
|
85
|
+
cwd: string | undefined,
|
|
86
|
+
): LoadedBaseline | null {
|
|
87
|
+
const root = cwd ?? process.cwd();
|
|
88
|
+
let raw: string;
|
|
89
|
+
try {
|
|
90
|
+
raw = readFileSync(join(root, BASELINE_FILENAME), "utf-8");
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const keys = parseBaseline(raw);
|
|
95
|
+
if (!keys) return null;
|
|
96
|
+
let created: string | null = null;
|
|
97
|
+
try {
|
|
98
|
+
const doc = JSON.parse(raw) as { created?: unknown };
|
|
99
|
+
if (typeof doc.created === "string") created = doc.created;
|
|
100
|
+
} catch {
|
|
101
|
+
// parseBaseline already accepted the file, so this never throws — guard anyway.
|
|
102
|
+
}
|
|
103
|
+
return { keys, created };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface BaselineDelta {
|
|
107
|
+
/** active keys NOT in the baseline — new since the checkpoint. */
|
|
108
|
+
added: number;
|
|
109
|
+
/** baseline keys no longer present — cleared since the checkpoint. */
|
|
110
|
+
fixed: number;
|
|
111
|
+
/** keys in both — carried, unchanged. */
|
|
112
|
+
unchanged: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The new / fixed / unchanged split of the current ACTIVE concern set against a
|
|
117
|
+
* baseline checkpoint, keyed line-independently (`concernKeyOf`) so a line shift
|
|
118
|
+
* never reads as a fix or a regression. Pure — the report's "what changed" line.
|
|
119
|
+
*/
|
|
120
|
+
export function baselineDelta(
|
|
121
|
+
concerns: Concern[],
|
|
122
|
+
baselineKeys: Set<string>,
|
|
123
|
+
): BaselineDelta {
|
|
124
|
+
const current = new Set(concerns.map((c) => concernKeyOf(c)));
|
|
125
|
+
let added = 0;
|
|
126
|
+
let unchanged = 0;
|
|
127
|
+
for (const key of current) {
|
|
128
|
+
if (baselineKeys.has(key)) unchanged++;
|
|
129
|
+
else added++;
|
|
130
|
+
}
|
|
131
|
+
let fixed = 0;
|
|
132
|
+
for (const key of baselineKeys) if (!current.has(key)) fixed++;
|
|
133
|
+
return { added, fixed, unchanged };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function meetsGate(severity: Severity, threshold: Severity): boolean {
|
|
137
|
+
return SEVERITY_RANK[severity] >= SEVERITY_RANK[threshold];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface GateResult {
|
|
141
|
+
/** active concerns whose key is NOT in the baseline (or all, when no baseline). */
|
|
142
|
+
newConcerns: Concern[];
|
|
143
|
+
/** the new concerns at/above the gate severity — these fail the check. */
|
|
144
|
+
gating: Concern[];
|
|
145
|
+
/** true = the gate passes (no new finding at/above the gate severity). */
|
|
146
|
+
pass: boolean;
|
|
147
|
+
/** whether a baseline was supplied (false ⇒ every active concern is "new"). */
|
|
148
|
+
hasBaseline: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Evaluate the gate. `concerns` should already be the ACTIVE set (suppressed
|
|
153
|
+
* concerns excluded — a suppression keeps a finding out of the gate by design).
|
|
154
|
+
* With no baseline (`baselineKeys` null) every active concern counts as new, so
|
|
155
|
+
* the gate behaves as a plain High/Critical gate.
|
|
156
|
+
*/
|
|
157
|
+
export function evaluateGate(
|
|
158
|
+
concerns: Concern[],
|
|
159
|
+
baselineKeys: Set<string> | null,
|
|
160
|
+
threshold: Severity = DEFAULT_GATE_SEVERITY,
|
|
161
|
+
): GateResult {
|
|
162
|
+
const newConcerns = baselineKeys
|
|
163
|
+
? concerns.filter((c) => !baselineKeys.has(concernKeyOf(c)))
|
|
164
|
+
: [...concerns];
|
|
165
|
+
const gating = newConcerns.filter((c) => meetsGate(c.severity, threshold));
|
|
166
|
+
return {
|
|
167
|
+
newConcerns,
|
|
168
|
+
gating,
|
|
169
|
+
pass: gating.length === 0,
|
|
170
|
+
hasBaseline: baselineKeys !== null,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { LocalToolContext } from "#app/mcp/localContext";
|
|
2
|
+
import {
|
|
3
|
+
annotateGroups,
|
|
4
|
+
groupConcerns,
|
|
5
|
+
groupConcernsByRule,
|
|
6
|
+
planBatches,
|
|
7
|
+
} from "#app/mcp/terraform/decisions";
|
|
8
|
+
import { scanSessionOf } from "#app/mcp/terraform/scanSession";
|
|
9
|
+
import { suppressConcerns } from "#app/mcp/terraform/suppressions";
|
|
10
|
+
import { dedupeCrossScanner } from "#app/mcp/terraform/taxonomy";
|
|
11
|
+
import {
|
|
12
|
+
type Concern,
|
|
13
|
+
concernKeyOf,
|
|
14
|
+
dedupe,
|
|
15
|
+
isTerraformConcern,
|
|
16
|
+
SEVERITY_RANK,
|
|
17
|
+
type Severity,
|
|
18
|
+
sortConcerns,
|
|
19
|
+
} from "#app/mcp/terraform/types";
|
|
20
|
+
|
|
21
|
+
export interface ConcernResult {
|
|
22
|
+
/** non-suppressed concerns ≥ threshold, sorted */
|
|
23
|
+
active: Concern[];
|
|
24
|
+
/** concerns that matched a suppression rule */
|
|
25
|
+
suppressed: Concern[];
|
|
26
|
+
/** active ∪ suppressed (post-suppression annotation) — scan stores this as lastScanConcerns */
|
|
27
|
+
annotated: Concern[];
|
|
28
|
+
groups: ReturnType<typeof annotateGroups>;
|
|
29
|
+
batchPlan: ReturnType<typeof planBatches>;
|
|
30
|
+
bySeverity: Record<string, number>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The shared concerns→result pipeline behind `terraform_scan` and `read_findings`:
|
|
35
|
+
* capture the line-pinned + line-independent baseline (so verify's regression diff
|
|
36
|
+
* is apples-to-apples), filter to in-scope Terraform concerns ≥ threshold, apply
|
|
37
|
+
* suppressions, group + annotate autonomy, plan batches, and tally by severity.
|
|
38
|
+
* Both tools then wrap their own envelope around this. The diff-scope predicate is
|
|
39
|
+
* `scan`-only (defaults to "everything in scope"); `lastScanConcerns` is written by
|
|
40
|
+
* `scan` from `annotated`, not here, since `read_findings` does not set it.
|
|
41
|
+
*/
|
|
42
|
+
export function buildConcernResult(
|
|
43
|
+
ctx: LocalToolContext,
|
|
44
|
+
rawConcerns: Concern[],
|
|
45
|
+
opts: {
|
|
46
|
+
cwd: string;
|
|
47
|
+
minRank: number;
|
|
48
|
+
grouping: "file" | "rule";
|
|
49
|
+
autonomyThreshold: Severity;
|
|
50
|
+
inScope?: (c: Concern) => boolean;
|
|
51
|
+
/** How this call affects the session's verify baseline. `replace` (default)
|
|
52
|
+
* is for the full-workspace sources (`scan`, `read_findings`). The narrow
|
|
53
|
+
* concern sources (`detect_plan_drift`, `ingest_external_findings`) pass
|
|
54
|
+
* `merge`: their small set must JOIN an existing scan baseline, not clobber
|
|
55
|
+
* it — otherwise a later `terraform_verify_remediation` diffs the full
|
|
56
|
+
* re-scan against the narrow set and reports every pre-existing scanner
|
|
57
|
+
* concern as a false regression. */
|
|
58
|
+
baselineMode?: "replace" | "merge";
|
|
59
|
+
},
|
|
60
|
+
): ConcernResult {
|
|
61
|
+
const fullBaseline = dedupe(rawConcerns);
|
|
62
|
+
const session = scanSessionOf(ctx.toolState);
|
|
63
|
+
const merge = opts.baselineMode === "merge";
|
|
64
|
+
const union = (prior: string[] | undefined, next: string[]) =>
|
|
65
|
+
merge ? [...new Set([...(prior ?? []), ...next])] : next;
|
|
66
|
+
session.baselineConcernIds = union(
|
|
67
|
+
session.baselineConcernIds,
|
|
68
|
+
fullBaseline.map((c) => c.id),
|
|
69
|
+
);
|
|
70
|
+
session.baselineConcernKeys = union(
|
|
71
|
+
session.baselineConcernKeys,
|
|
72
|
+
fullBaseline.map((c) => concernKeyOf(c)),
|
|
73
|
+
);
|
|
74
|
+
// the files already unformatted at this (pre-fix) scan. terraform_validate
|
|
75
|
+
// subtracts these so PRE-EXISTING `terraform fmt` drift can't force a
|
|
76
|
+
// whole-file reformat into a scoped fix. Captured from the raw set (fmt
|
|
77
|
+
// concerns survive the threshold/scope filters that shape `active`).
|
|
78
|
+
session.baselineFmtDriftFiles = union(
|
|
79
|
+
session.baselineFmtDriftFiles,
|
|
80
|
+
fullBaseline.filter((c) => c.source === "terraform-fmt").map((c) => c.location.file),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const inScope = opts.inScope ?? (() => true);
|
|
84
|
+
const annotated = suppressConcerns(
|
|
85
|
+
sortConcerns(dedupe(rawConcerns))
|
|
86
|
+
.filter(isTerraformConcern)
|
|
87
|
+
.filter(inScope)
|
|
88
|
+
.filter((c) => SEVERITY_RANK[c.severity] >= opts.minRank),
|
|
89
|
+
opts.cwd,
|
|
90
|
+
);
|
|
91
|
+
// Cross-scanner canonical dedupe (2c) is applied ONLY to the ACTIVE set that
|
|
92
|
+
// feeds grouping/PRs/coLocated/display — NOT to `annotated` or the baseline. The
|
|
93
|
+
// ✗→✓ verifier re-scans raw and key-maps requested ids through `lastScanConcerns`
|
|
94
|
+
// (= annotated) and `baselineConcernKeys`, both of which stay raw, so collapsing a
|
|
95
|
+
// cross-tool duplicate here can never make the verifier read a merged-away concern
|
|
96
|
+
// as resolved or as a regression. The user still gets ONE PR per cross-tool dup.
|
|
97
|
+
const active = dedupeCrossScanner(annotated.filter((c) => !c.suppression));
|
|
98
|
+
const suppressed = annotated.filter((c) => c.suppression);
|
|
99
|
+
|
|
100
|
+
const groups = annotateGroups(
|
|
101
|
+
opts.grouping === "rule" ? groupConcernsByRule(active) : groupConcerns(active),
|
|
102
|
+
active,
|
|
103
|
+
opts.autonomyThreshold,
|
|
104
|
+
);
|
|
105
|
+
const batchPlan = planBatches(groups);
|
|
106
|
+
|
|
107
|
+
const bySeverity: Record<string, number> = {};
|
|
108
|
+
for (const c of active) bySeverity[c.severity] = (bySeverity[c.severity] ?? 0) + 1;
|
|
109
|
+
|
|
110
|
+
return { active, suppressed, annotated, groups, batchPlan, bySeverity };
|
|
111
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { type } from "arktype";
|
|
4
|
+
import { resolveBaseRef } from "#app/mcp/terraform/paths";
|
|
5
|
+
import { parseScannerJson } from "#app/mcp/terraform/scannerJson";
|
|
6
|
+
import { type RunResult, run } from "#app/mcp/terraform/subprocess";
|
|
7
|
+
|
|
8
|
+
// --- infracost (cost lens) ------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export interface CostBreakdown {
|
|
11
|
+
/** total estimated monthly cost, or null when no resources are priced. */
|
|
12
|
+
totalMonthlyCost: number | null;
|
|
13
|
+
currency: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse `infracost breakdown --format json`. The top-level `totalMonthlyCost`
|
|
18
|
+
* is a decimal string (absent / null when a project has no priced resources);
|
|
19
|
+
* `currency` defaults to USD. A missing/unparseable cost becomes null so the
|
|
20
|
+
* caller reports "unpriced" rather than a misleading $0.00.
|
|
21
|
+
*/
|
|
22
|
+
export function parseInfracostBreakdown(stdout: string): CostBreakdown {
|
|
23
|
+
const parsed = parseScannerJson(InfracostBreakdownOutput, stdout, "infracost");
|
|
24
|
+
const raw = parsed.totalMonthlyCost;
|
|
25
|
+
const num = typeof raw === "number" ? raw : raw != null ? Number.parseFloat(raw) : Number.NaN;
|
|
26
|
+
return {
|
|
27
|
+
totalMonthlyCost: Number.isFinite(num) ? num : null,
|
|
28
|
+
currency: parsed.currency || "USD",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const InfracostBreakdownOutput = type({
|
|
33
|
+
"totalMonthlyCost?": "string | number | null",
|
|
34
|
+
"currency?": "string",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export interface ResourceCost {
|
|
38
|
+
name: string;
|
|
39
|
+
monthlyCost: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parse the per-resource monthly costs from `infracost breakdown --format json`
|
|
44
|
+
* (`projects[].breakdown.resources[]`), so a cost increase can be attributed to
|
|
45
|
+
* the specific resources that drove it instead of just a total. Skips unpriced
|
|
46
|
+
* (null/zero) resources; returns them sorted most-expensive first. Pure.
|
|
47
|
+
*/
|
|
48
|
+
export function parseInfracostResources(stdout: string): ResourceCost[] {
|
|
49
|
+
let parsed: typeof InfracostResourcesOutput.infer;
|
|
50
|
+
try {
|
|
51
|
+
parsed = parseScannerJson(InfracostResourcesOutput, stdout, "infracost");
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const out: ResourceCost[] = [];
|
|
56
|
+
for (const project of parsed.projects ?? []) {
|
|
57
|
+
for (const r of project.breakdown?.resources ?? []) {
|
|
58
|
+
const raw = r.monthlyCost;
|
|
59
|
+
const cost =
|
|
60
|
+
typeof raw === "number" ? raw : raw != null ? Number.parseFloat(raw) : Number.NaN;
|
|
61
|
+
if (Number.isFinite(cost) && cost > 0 && r.name) {
|
|
62
|
+
out.push({ name: r.name, monthlyCost: Math.round(cost * 100) / 100 });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out.sort((a, b) => b.monthlyCost - a.monthlyCost);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const InfracostResourcesOutput = type({
|
|
70
|
+
"projects?": type({
|
|
71
|
+
"breakdown?": {
|
|
72
|
+
"resources?": type({
|
|
73
|
+
"name?": "string",
|
|
74
|
+
"monthlyCost?": "string | number | null",
|
|
75
|
+
}).array(),
|
|
76
|
+
},
|
|
77
|
+
}).array(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export interface CostDelta {
|
|
81
|
+
currency: string;
|
|
82
|
+
baselineMonthly: number | null;
|
|
83
|
+
currentMonthly: number | null;
|
|
84
|
+
/** current − baseline, rounded to cents; null when either side is unknown. */
|
|
85
|
+
deltaMonthly: number | null;
|
|
86
|
+
direction: "increase" | "decrease" | "no-change" | "unknown";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Pure cost-delta computation: current (post-fix) vs the base-branch baseline. */
|
|
90
|
+
export function computeCostDelta(
|
|
91
|
+
baseline: CostBreakdown | null,
|
|
92
|
+
current: CostBreakdown,
|
|
93
|
+
): CostDelta {
|
|
94
|
+
const currency = current.currency || baseline?.currency || "USD";
|
|
95
|
+
const baselineMonthly = baseline?.totalMonthlyCost ?? null;
|
|
96
|
+
const currentMonthly = current.totalMonthlyCost;
|
|
97
|
+
if (baselineMonthly === null || currentMonthly === null) {
|
|
98
|
+
return { currency, baselineMonthly, currentMonthly, deltaMonthly: null, direction: "unknown" };
|
|
99
|
+
}
|
|
100
|
+
const deltaMonthly = Math.round((currentMonthly - baselineMonthly) * 100) / 100;
|
|
101
|
+
const direction = deltaMonthly > 0 ? "increase" : deltaMonthly < 0 ? "decrease" : "no-change";
|
|
102
|
+
return { currency, baselineMonthly, currentMonthly, deltaMonthly, direction };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface CostEscalation {
|
|
106
|
+
/** true when the monthly increase meets/exceeds the operator's threshold. */
|
|
107
|
+
escalate: boolean;
|
|
108
|
+
reason?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* decide whether a cost increase is large enough to escalate the PR
|
|
113
|
+
* to human review (`needs-human`). Compares the monthly delta against the
|
|
114
|
+
* operator's `cost_increase_block_usd` threshold. No threshold set, an unknown
|
|
115
|
+
* delta, or a decrease/no-change ⇒ no escalation. Pure + deterministic so the
|
|
116
|
+
* decision is auditable, not a model judgement.
|
|
117
|
+
*/
|
|
118
|
+
export function classifyCostEscalation(
|
|
119
|
+
deltaMonthly: number | null,
|
|
120
|
+
thresholdUsd: number | undefined,
|
|
121
|
+
): CostEscalation {
|
|
122
|
+
if (thresholdUsd === undefined || deltaMonthly === null || deltaMonthly <= 0) {
|
|
123
|
+
return { escalate: false };
|
|
124
|
+
}
|
|
125
|
+
if (deltaMonthly >= thresholdUsd) {
|
|
126
|
+
return {
|
|
127
|
+
escalate: true,
|
|
128
|
+
reason: `the fix raises monthly cost by ${deltaMonthly}, at or above the ${thresholdUsd} escalation threshold`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { escalate: false };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function runInfracostBreakdown(scanCwd: string, key: string): RunResult {
|
|
135
|
+
return run("infracost", ["breakdown", "--path", ".", "--format", "json", "--no-color"], scanCwd, {
|
|
136
|
+
INFRACOST_API_KEY: key,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Cost of the base-branch version of the same Terraform, computed in a detached
|
|
142
|
+
* git worktree so the current (fixed) checkout is never disturbed. Best-effort:
|
|
143
|
+
* any failure (no base ref, worktree add fails, infracost errors) returns null
|
|
144
|
+
* and the caller falls back to reporting current cost only.
|
|
145
|
+
*/
|
|
146
|
+
export function infracostBaseline(cwd: string, key: string, tmpdir: string): CostBreakdown | null {
|
|
147
|
+
const baseRef = resolveBaseRef(cwd);
|
|
148
|
+
if (!baseRef) return null;
|
|
149
|
+
const prefixResult = run("git", ["rev-parse", "--show-prefix"], cwd);
|
|
150
|
+
const prefix = prefixResult.status === 0 ? prefixResult.stdout.trim() : "";
|
|
151
|
+
// unique, unpredictable worktree path. the old `infracost-base-<pid>` name was
|
|
152
|
+
// predictable (a local actor could pre-create/symlink it) and collided when
|
|
153
|
+
// two baselines ran in the same process. mkdtemp gives a fresh PARENT dir;
|
|
154
|
+
// the worktree itself goes in a not-yet-existing child (git worktree add
|
|
155
|
+
// refuses a path that already exists). the finally removes both the git
|
|
156
|
+
// worktree registration and the parent dir.
|
|
157
|
+
const baseDir = mkdtempSync(join(tmpdir, "infracost-base-"));
|
|
158
|
+
const worktree = join(baseDir, "wt");
|
|
159
|
+
const add = run("git", ["worktree", "add", "--detach", worktree, baseRef], cwd);
|
|
160
|
+
if (add.status !== 0) {
|
|
161
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const scanCwd = prefix ? join(worktree, prefix) : worktree;
|
|
166
|
+
const r = runInfracostBreakdown(scanCwd, key);
|
|
167
|
+
if (r.missing || r.status !== 0) return null;
|
|
168
|
+
return parseInfracostBreakdown(r.stdout);
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
} finally {
|
|
172
|
+
run("git", ["worktree", "remove", "--force", worktree], cwd);
|
|
173
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
}
|