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,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sink-independent secret redaction for log output.
|
|
3
|
+
*
|
|
4
|
+
* `core.setSecret()` only masks on the GitHub `actions` log sink. Every stdio MCP
|
|
5
|
+
* server runs with `setLogSink("stderr")` (stdout is the JSON-RPC channel there) and
|
|
6
|
+
* local dev prints to the console — neither is masked. So a stray `log.debug(env)`,
|
|
7
|
+
* an error that stringifies a request, or any line that merely contains a bearer would
|
|
8
|
+
* print it in cleartext on those sinks. This scrubs secret-shaped substrings from an
|
|
9
|
+
* already-formatted log line BEFORE it reaches any sink, so redaction no longer depends
|
|
10
|
+
* on which sink is active (it complements `core.setSecret`, it doesn't replace it).
|
|
11
|
+
*
|
|
12
|
+
* Two layers, applied in order:
|
|
13
|
+
* 1. Registered literals — exact values handed to {@link registerSecret} (e.g. the
|
|
14
|
+
* minted managed-proxy token). Caught even when their shape is unusual.
|
|
15
|
+
* 2. Shape patterns — known token formats (`tmnd_`/`tmpx_` org + proxy tokens, the
|
|
16
|
+
* GitHub `ghp_`/`gho_`/`ghu_`/`ghs_`/`ghr_` family + fine-grained PATs, Anthropic
|
|
17
|
+
* keys, AWS access-key ids, `Bearer …` headers) plus any JSON property whose NAME
|
|
18
|
+
* matches the sensitive-env patterns (reuses {@link isSensitiveEnvName}, so an
|
|
19
|
+
* `{"GITHUB_TOKEN":"…"}` env dump is masked by key even if the value shape is novel).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { TOKEN_PREFIX } from "#app/brand";
|
|
23
|
+
import { isSensitiveEnvName } from "#app/utils/secrets";
|
|
24
|
+
|
|
25
|
+
const REDACTED = "***";
|
|
26
|
+
|
|
27
|
+
// Literal secret values to scrub on sight — the sink-independent companion to
|
|
28
|
+
// core.setSecret, seeded by mint/auth code that already registers with @actions/core.
|
|
29
|
+
const registeredSecrets = new Set<string>();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register a literal secret value for sink-independent log redaction. Safe to call with
|
|
33
|
+
* any string; values under 8 chars are ignored so we never scrub a common substring.
|
|
34
|
+
* Complements `core.setSecret` (call both) — this one also masks on stderr/local sinks.
|
|
35
|
+
*/
|
|
36
|
+
export function registerSecret(value: string | undefined | null): void {
|
|
37
|
+
if (typeof value === "string" && value.length >= 8) registeredSecrets.add(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Test-only: clear the literal registry between cases. */
|
|
41
|
+
export function _resetRegisteredSecrets(): void {
|
|
42
|
+
registeredSecrets.clear();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Token-shaped secrets: a recognizable scheme prefix (kept, so the line still reads)
|
|
46
|
+
// followed by secret material (masked). The trailing class spans the base64url/JWT
|
|
47
|
+
// alphabet so dotted `tmpx_` proxy tokens are consumed whole. Anthropic `sk-ant-` is
|
|
48
|
+
// included; the generic OpenAI `sk-` is deliberately NOT (too collision-prone with
|
|
49
|
+
// ordinary `sk-…` slugs, and this engine routes Anthropic/Bedrock anyway).
|
|
50
|
+
const TOKEN_SHAPES = new RegExp(
|
|
51
|
+
`\\b(${TOKEN_PREFIX}|tmpx_|gh[pousr]_|github_pat_|sk-ant-)[A-Za-z0-9._-]{12,}`,
|
|
52
|
+
"g",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// AWS access-key id (AKIA/ASIA + 16 upper-alnum) — a distinctive fixed shape.
|
|
56
|
+
const AWS_ACCESS_KEY = /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g;
|
|
57
|
+
|
|
58
|
+
// `Authorization: Bearer <token>` / bare `bearer <token>` — mask the credential, keep
|
|
59
|
+
// the scheme word. Catches JWTs and any bearer that the prefix patterns don't.
|
|
60
|
+
const BEARER = /\b(bearer\s+)[A-Za-z0-9._-]{8,}/gi;
|
|
61
|
+
|
|
62
|
+
// A JSON string property `"NAME":"VALUE"`; VALUE is masked only when NAME matches the
|
|
63
|
+
// sensitive-env patterns. Tolerates escaped quotes inside the value.
|
|
64
|
+
const JSON_STRING_PROP = /"([A-Za-z_][A-Za-z0-9_]*)"\s*:\s*"(?:[^"\\]|\\.)*"/g;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Redact secret-shaped substrings from an already-formatted log line. Idempotent,
|
|
68
|
+
* allocation-light on the common (no-secret) path, and never throws.
|
|
69
|
+
*/
|
|
70
|
+
export function redactSecrets(text: string): string {
|
|
71
|
+
if (!text) return text;
|
|
72
|
+
let out = text;
|
|
73
|
+
|
|
74
|
+
for (const secret of registeredSecrets) {
|
|
75
|
+
if (out.includes(secret)) out = out.split(secret).join(REDACTED);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
out = out.replace(JSON_STRING_PROP, (full, key: string) =>
|
|
79
|
+
isSensitiveEnvName(key) ? `"${key}":"${REDACTED}"` : full,
|
|
80
|
+
);
|
|
81
|
+
out = out.replace(BEARER, (_m, scheme: string) => `${scheme}${REDACTED}`);
|
|
82
|
+
out = out.replace(TOKEN_SHAPES, (_m, scheme: string) => `${scheme}${REDACTED}`);
|
|
83
|
+
out = out.replace(AWS_ACCESS_KEY, REDACTED);
|
|
84
|
+
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private Terraform registry credential pass-through (the refactor `b.4` shape).
|
|
3
|
+
*
|
|
4
|
+
* Adopting a third-party module from a PRIVATE registry — HCP Terraform
|
|
5
|
+
* (`app.terraform.io`), Terraform Enterprise, or an org's own registry — needs
|
|
6
|
+
* Terraform/OpenTofu to authenticate to that registry's host when it resolves the
|
|
7
|
+
* module during `init`. The CLI reads a per-host bearer token from the env var
|
|
8
|
+
* `TF_TOKEN_<host>` (its documented convention; equivalent to a `credentials {}`
|
|
9
|
+
* block in the CLI config). The action runs scan/init under a restricted env that
|
|
10
|
+
* strips every `*_TOKEN` so a scanner can't exfiltrate a secret — which also strips
|
|
11
|
+
* any registry token. So an operator-supplied `registry_token` is re-admitted to the
|
|
12
|
+
* terraform subprocess ONLY, per-host and ephemerally, by this module — never written
|
|
13
|
+
* to disk, never a CLI-config file, never the action's own git.
|
|
14
|
+
*
|
|
15
|
+
* Host → env-var encoding follows Terraform's rule: lower-case the host, encode each
|
|
16
|
+
* `.` as `_` and each `-` as `__` (a literal `-` is invalid in an env var name).
|
|
17
|
+
* e.g. `app.terraform.io` → `TF_TOKEN_app_terraform_io`;
|
|
18
|
+
* `private.example-registry.io` → `TF_TOKEN_private_example__registry_io`.
|
|
19
|
+
*
|
|
20
|
+
* This is the registry-protocol counterpart of moduleFetch.ts (which handles the
|
|
21
|
+
* `git::` transport); `resolveModuleCredentialEnv` below merges the two so the
|
|
22
|
+
* scan/verify/plan/equivalence paths treat both credentials identically.
|
|
23
|
+
*/
|
|
24
|
+
import { resolveModuleFetchEnv } from "#app/utils/moduleFetch";
|
|
25
|
+
|
|
26
|
+
/** the HCP Terraform / private-registry host a `registry_token` defaults to. */
|
|
27
|
+
export const DEFAULT_REGISTRY_HOST = "app.terraform.io";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The `TF_TOKEN_<host>` env-var NAME for a registry host, per Terraform's encoding:
|
|
31
|
+
* lower-cased, each `.` → `_`, each `-` → `__`. The two replacements operate on
|
|
32
|
+
* disjoint characters, so order is immaterial. Pure.
|
|
33
|
+
*/
|
|
34
|
+
export function registryTokenEnvName(host: string): string {
|
|
35
|
+
const encoded = host.trim().toLowerCase().replace(/-/g, "__").replace(/\./g, "_");
|
|
36
|
+
return `TF_TOKEN_${encoded}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parse the `registry_host` input (newline/comma-separated) into the hosts a
|
|
41
|
+
* registry token authorises. Blank/whitespace entries are dropped and hosts are
|
|
42
|
+
* de-duplicated case-insensitively. Falls back to the HCP host when none is given —
|
|
43
|
+
* the overwhelmingly common private-registry target — so supplying just a token works.
|
|
44
|
+
*/
|
|
45
|
+
export function registryHosts(raw?: string | undefined): string[] {
|
|
46
|
+
const seen = new Set<string>();
|
|
47
|
+
const hosts: string[] = [];
|
|
48
|
+
for (const part of (raw ?? "").split(/[\n,]/)) {
|
|
49
|
+
const host = part.trim().toLowerCase();
|
|
50
|
+
if (!host || seen.has(host)) continue;
|
|
51
|
+
seen.add(host);
|
|
52
|
+
hosts.push(host);
|
|
53
|
+
}
|
|
54
|
+
return hosts.length > 0 ? hosts : [DEFAULT_REGISTRY_HOST];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build the `TF_TOKEN_<host>=<token>` env that authorises `terraform init` to fetch
|
|
59
|
+
* a private-registry module for each host. Pure (token + hosts in, env out) so it is
|
|
60
|
+
* unit-testable without a real terraform. The token only ever lands in a VALUE, never
|
|
61
|
+
* a key. Returns `{}` for a blank token or no usable host.
|
|
62
|
+
*/
|
|
63
|
+
export function buildRegistryTokenEnv(token: string, hosts: string[]): Record<string, string> {
|
|
64
|
+
const t = token.trim();
|
|
65
|
+
if (!t) return {};
|
|
66
|
+
const env: Record<string, string> = {};
|
|
67
|
+
for (const host of hosts) {
|
|
68
|
+
if (!host.trim()) continue;
|
|
69
|
+
env[registryTokenEnvName(host)] = t;
|
|
70
|
+
}
|
|
71
|
+
return env;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the registry-token env for a run, or undefined when no `registry_token`
|
|
76
|
+
* was supplied (the common public-registry/local case). Mirrors
|
|
77
|
+
* `resolveModuleFetchEnv` so the scan/verify/plan/refactor paths treat both
|
|
78
|
+
* credentials identically. The token authorises the hosts named in `registryHost`
|
|
79
|
+
* (default: the HCP host).
|
|
80
|
+
*/
|
|
81
|
+
export function resolveRegistryTokenEnv(payload: {
|
|
82
|
+
registryToken?: string | undefined;
|
|
83
|
+
registryHost?: string | undefined;
|
|
84
|
+
}): Record<string, string> | undefined {
|
|
85
|
+
const token = payload.registryToken?.trim();
|
|
86
|
+
if (!token) return undefined;
|
|
87
|
+
const env = buildRegistryTokenEnv(token, registryHosts(payload.registryHost));
|
|
88
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The full extra-env a terraform subprocess needs to FETCH private modules: the
|
|
93
|
+
* git-transport credential for cross-repo `git::` modules (moduleFetch.ts) merged
|
|
94
|
+
* with the registry-protocol token for private-registry modules (this file). The two
|
|
95
|
+
* key namespaces (`GIT_CONFIG_*` / `GIT_SSH_COMMAND` vs `TF_TOKEN_*`) never collide.
|
|
96
|
+
* Both are scoped to the subprocess and never persisted. Returns undefined when
|
|
97
|
+
* neither is configured (the common public/local-module case). This is the single
|
|
98
|
+
* resolver the scan / verify / plan / equivalence-check paths call so they treat both
|
|
99
|
+
* credentials alike.
|
|
100
|
+
*/
|
|
101
|
+
export function resolveModuleCredentialEnv(
|
|
102
|
+
payload: {
|
|
103
|
+
moduleFetchToken?: string | undefined;
|
|
104
|
+
moduleFetchSshKey?: string | undefined;
|
|
105
|
+
registryToken?: string | undefined;
|
|
106
|
+
registryHost?: string | undefined;
|
|
107
|
+
},
|
|
108
|
+
opts: { sshKeyDir?: string; writeKey?: (path: string, contents: string) => void } = {},
|
|
109
|
+
): Record<string, string> | undefined {
|
|
110
|
+
const env = {
|
|
111
|
+
...resolveModuleFetchEnv(payload, opts),
|
|
112
|
+
...resolveRegistryTokenEnv(payload),
|
|
113
|
+
};
|
|
114
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
115
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment-command interface. A developer can scope a remediation run from
|
|
3
|
+
* a PR/issue comment that mentions the bot:
|
|
4
|
+
*
|
|
5
|
+
* @infraweaver fix #3a9f1c2 → fix exactly one concern (by id or short id)
|
|
6
|
+
* @infraweaver fix all high-severity → fix every concern at/above a severity
|
|
7
|
+
* @infraweaver fix all → fix everything (still bounded by max_prs)
|
|
8
|
+
* @infraweaver fix main.tf → fix one file's group
|
|
9
|
+
*
|
|
10
|
+
* Propose, then let me steer — when a non-trivial finding has several
|
|
11
|
+
* genuinely distinct valid fixes, Remediate proposes 2–3 labelled strategies in
|
|
12
|
+
* a comment instead of guessing, and the reviewer picks one by replying:
|
|
13
|
+
*
|
|
14
|
+
* @infraweaver fix #3a9f1c2 with strategy B → apply strategy B to that concern
|
|
15
|
+
* @infraweaver strategy 2 → pick strategy 2 (concern from the thread)
|
|
16
|
+
*
|
|
17
|
+
* The parsing is pure + deterministic so the scoping doesn't depend on the
|
|
18
|
+
* model's reading of the comment. The Remediate mode applies the parsed scope
|
|
19
|
+
* (which group(s) to act on, and which strategy) instead of the default
|
|
20
|
+
* "highest-severity group, agent's-choice fix".
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { COMMENT_COMMAND } from "#app/brand";
|
|
24
|
+
|
|
25
|
+
export type RemediationCommand =
|
|
26
|
+
| { kind: "concern"; concernRef: string; strategy?: string }
|
|
27
|
+
| { kind: "severity"; severity: Severity }
|
|
28
|
+
| { kind: "file"; file: string }
|
|
29
|
+
// bulk remediation — sweep ONE scanner rule across every file it fires in
|
|
30
|
+
// (one coherent PR via by-rule grouping). e.g. `@infraweaver fix rule CKV_AWS_23`.
|
|
31
|
+
| { kind: "rule"; ruleId: string }
|
|
32
|
+
// a bare strategy pick (e.g. an in-thread reply to a proposal); the
|
|
33
|
+
// concern is resolved from the comment thread the run was triggered on.
|
|
34
|
+
| { kind: "strategy"; strategy: string }
|
|
35
|
+
| { kind: "all" };
|
|
36
|
+
|
|
37
|
+
const SEVERITIES = ["critical", "high", "medium", "low", "info"] as const;
|
|
38
|
+
type Severity = (typeof SEVERITIES)[number];
|
|
39
|
+
|
|
40
|
+
/** the bot handles a mention can use (with or without the `[bot]` suffix). */
|
|
41
|
+
const MENTION = new RegExp(`${COMMENT_COMMAND}(?:\\[bot\\])?\\b`, "i");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* the canonical phrasing the proposal comment must tell reviewers to reply
|
|
45
|
+
* with. Lives next to the parser that accepts it so the proposal template (in
|
|
46
|
+
* the Remediate prompt) and the parser can never drift apart. `<concern-id>` and
|
|
47
|
+
* `<A|B|C>` are placeholders the agent fills with the real id and the offered
|
|
48
|
+
* strategy labels.
|
|
49
|
+
*/
|
|
50
|
+
export const STRATEGY_REPLY_HINT = `${COMMENT_COMMAND} fix #<concern-id> with strategy <A|B|C>`;
|
|
51
|
+
|
|
52
|
+
// a strategy label is a single letter (A–Z, normalised to upper) or digit (1–9),
|
|
53
|
+
// introduced by `strategy` / `option` / `approach`. The single-char + `\b` bound
|
|
54
|
+
// keeps it off prose ("a good strategy overall" has no single-char boundary).
|
|
55
|
+
const STRATEGY = /\b(?:strategy|option|approach)\s+#?([A-Za-z]|[1-9])\b/i;
|
|
56
|
+
|
|
57
|
+
function parseStrategyToken(text: string): string | undefined {
|
|
58
|
+
// the capture is a single required char, so guarding `?.[1]` is equivalent to
|
|
59
|
+
// the old `m[1]!` but carries the non-null in the type rather than an assertion.
|
|
60
|
+
const tok = text.match(STRATEGY)?.[1];
|
|
61
|
+
if (!tok) return undefined;
|
|
62
|
+
return /[A-Za-z]/.test(tok) ? tok.toUpperCase() : tok;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Parse a `@infraweaver fix …` command out of a comment body. Returns null when
|
|
67
|
+
* the body isn't a recognised fix command (the run then falls back to its
|
|
68
|
+
* default scope). Tolerant of surrounding prose — it scans for the mention then
|
|
69
|
+
* the `fix` verb and its argument.
|
|
70
|
+
*/
|
|
71
|
+
export function parseRemediationCommand(
|
|
72
|
+
body: string | undefined,
|
|
73
|
+
): RemediationCommand | null {
|
|
74
|
+
if (!body) return null;
|
|
75
|
+
if (!MENTION.test(body)) return null;
|
|
76
|
+
|
|
77
|
+
// everything after the mention; we keep original case for the file/concern-ref
|
|
78
|
+
// capture (the lower-casing happens per match).
|
|
79
|
+
const afterMention = body.slice(body.search(MENTION));
|
|
80
|
+
const isSeverity = (s: string): s is Severity =>
|
|
81
|
+
(SEVERITIES as readonly string[]).includes(s);
|
|
82
|
+
|
|
83
|
+
// bulk — `fix rule <rule-id>` / `fix all rule <rule-id>`. Checked FIRST and
|
|
84
|
+
// gated on the explicit `rule` keyword so a scanner rule id (`CKV_AWS_23`,
|
|
85
|
+
// `terraform_required_version`, `trivy:AVD-AWS-0088`) is never confused with a
|
|
86
|
+
// severity word, a filename, or a hex concern id. Rule ids are case-significant
|
|
87
|
+
// — kept verbatim. The fix sweeps that ONE rule across every file it fires in.
|
|
88
|
+
const ruleId = afterMention.match(
|
|
89
|
+
/\bfix\s+(?:all\s+)?rule\s+([A-Za-z][A-Za-z0-9_.:-]+)\b/i,
|
|
90
|
+
)?.[1];
|
|
91
|
+
if (ruleId) return { kind: "rule", ruleId };
|
|
92
|
+
|
|
93
|
+
// `fix all <sev>[-severity]` / `fix all` — but a NON-severity word after "all"
|
|
94
|
+
// (prose like "fix all the bugs") is NOT the command: fall through rather than
|
|
95
|
+
// silently treating it as "fix everything".
|
|
96
|
+
const all = afterMention.match(
|
|
97
|
+
/\bfix\s+all\b(?:\s+([a-z]+)(?:\s*-?\s*severity)?)?/i,
|
|
98
|
+
);
|
|
99
|
+
if (all) {
|
|
100
|
+
const trailing = all[1]?.toLowerCase();
|
|
101
|
+
if (!trailing) return { kind: "all" };
|
|
102
|
+
if (isSeverity(trailing)) return { kind: "severity", severity: trailing };
|
|
103
|
+
// a non-severity word followed "all" → prose, not a command. fall through.
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// `fix <sev>-severity` (without "all")
|
|
107
|
+
const sev = afterMention
|
|
108
|
+
.match(/\bfix\s+([a-z]+)\s*-?\s*severity\b/i)?.[1]
|
|
109
|
+
?.toLowerCase();
|
|
110
|
+
if (sev && isSeverity(sev)) return { kind: "severity", severity: sev };
|
|
111
|
+
|
|
112
|
+
// `fix <path>.tf` / `fix <path>.tfvars` — a specific file's group. Checked
|
|
113
|
+
// BEFORE the concern-id form so an all-hex filename stem (e.g. `deadbeef.tf`)
|
|
114
|
+
// isn't mis-read as a concern id.
|
|
115
|
+
const file = afterMention.match(/\bfix\s+([^\s#]+\.tf(?:vars)?)\b/i)?.[1];
|
|
116
|
+
if (file) return { kind: "file", file };
|
|
117
|
+
|
|
118
|
+
// `fix #<id>` or `fix <id>` — a concern id is hex (content hash, 6–40 chars).
|
|
119
|
+
// carries an optional strategy label when the comment selects one.
|
|
120
|
+
const concernRef = afterMention
|
|
121
|
+
.match(/\bfix\s+#?([0-9a-f]{6,40})\b/i)?.[1]
|
|
122
|
+
?.toLowerCase();
|
|
123
|
+
if (concernRef) {
|
|
124
|
+
const strategy = parseStrategyToken(afterMention);
|
|
125
|
+
return strategy
|
|
126
|
+
? { kind: "concern", concernRef, strategy }
|
|
127
|
+
: { kind: "concern", concernRef };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// a strategy pick with no `fix` verb (e.g. an in-thread reply). If the
|
|
131
|
+
// reviewer still named a `#<id>`, keep it as a scoped concern; otherwise the
|
|
132
|
+
// concern is resolved from the thread the comment lives on.
|
|
133
|
+
const strategy = parseStrategyToken(afterMention);
|
|
134
|
+
if (strategy) {
|
|
135
|
+
const concernRef = afterMention
|
|
136
|
+
.match(/#([0-9a-f]{6,40})\b/i)?.[1]
|
|
137
|
+
?.toLowerCase();
|
|
138
|
+
return concernRef
|
|
139
|
+
? { kind: "concern", concernRef, strategy }
|
|
140
|
+
: { kind: "strategy", strategy };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
+
import { log } from "#app/utils/cli";
|
|
3
|
+
|
|
4
|
+
/** Default upper bound on a honored server Retry-After hint (ms). A hostile or
|
|
5
|
+
* buggy `Retry-After` could otherwise stall a run for its full value (GitHub
|
|
6
|
+
* secondary-rate-limit responses have carried multi-minute hints); 20s matches
|
|
7
|
+
* octokit's throttling-plugin ceiling. */
|
|
8
|
+
export const DEFAULT_RETRY_AFTER_CAP_MS = 20_000;
|
|
9
|
+
|
|
10
|
+
export type RetryOptions = {
|
|
11
|
+
maxAttempts?: number;
|
|
12
|
+
delayMs?: number;
|
|
13
|
+
/**
|
|
14
|
+
* explicit delay schedule — one entry per retry (length N ⇒ N+1 attempts).
|
|
15
|
+
* when set, overrides `maxAttempts` and `delayMs`. e.g. `[1_000, 3_000]`
|
|
16
|
+
* means up to 3 attempts, sleeping 1s before retry 2 and 3s before retry 3.
|
|
17
|
+
*/
|
|
18
|
+
delaysMs?: readonly number[];
|
|
19
|
+
shouldRetry?: (error: unknown) => boolean;
|
|
20
|
+
/** cap (ms) on a honored server Retry-After / rate-limit-reset hint. default
|
|
21
|
+
* {@link DEFAULT_RETRY_AFTER_CAP_MS}. The fixed backoff is never capped —
|
|
22
|
+
* this only bounds how long a *server-suggested* delay can extend it. */
|
|
23
|
+
retryAfterCapMs?: number;
|
|
24
|
+
label?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** A numeric HTTP status carried on an error (some clients set `.status`/`.statusCode`). */
|
|
28
|
+
function statusOf(error: unknown): number | undefined {
|
|
29
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
30
|
+
const e = error as { status?: unknown; statusCode?: unknown };
|
|
31
|
+
if (typeof e.status === "number") return e.status;
|
|
32
|
+
if (typeof e.statusCode === "number") return e.statusCode;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A broad transient predicate for octokit/GitHub calls: an HTTP 5xx (an upstream
|
|
38
|
+
* blip) OR a network-class error. Distinct from a deterministic 4xx (404/401/403/
|
|
39
|
+
* 422), which fails fast. Pass as `shouldRetry` for idempotent GitHub reads/
|
|
40
|
+
* dispatches whose client carries no 5xx retry of its own (mirrors pullfrog's
|
|
41
|
+
* `isTransientOctokitError`). see #999/#997.
|
|
42
|
+
*/
|
|
43
|
+
export function isTransientServerError(error: unknown): boolean {
|
|
44
|
+
const status = statusOf(error);
|
|
45
|
+
if (status !== undefined) return status >= 500;
|
|
46
|
+
if (!(error instanceof Error)) return false;
|
|
47
|
+
return (
|
|
48
|
+
error.name === "AbortError" ||
|
|
49
|
+
error.message.includes("fetch failed") ||
|
|
50
|
+
error.message.includes("ECONNRESET") ||
|
|
51
|
+
error.message.includes("ETIMEDOUT")
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Response headers hung off a thrown HTTP error (octokit sets `error.response.headers`). */
|
|
56
|
+
function errorResponseHeaders(error: unknown): Record<string, unknown> | undefined {
|
|
57
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
58
|
+
const response = (error as { response?: unknown }).response;
|
|
59
|
+
if (typeof response !== "object" || response === null) return undefined;
|
|
60
|
+
const headers = (response as { headers?: unknown }).headers;
|
|
61
|
+
if (typeof headers !== "object" || headers === null) return undefined;
|
|
62
|
+
return headers as Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A server-suggested retry delay in ms, resolved from (in order):
|
|
67
|
+
* 1. an explicit `retryAfterMs` property a caller stashed on the error (e.g.
|
|
68
|
+
* {@link parseRetryAfter} off a `Response`);
|
|
69
|
+
* 2. the `retry-after` response header (RFC-9110 delta-seconds / HTTP-date) —
|
|
70
|
+
* an explicit backoff directive whenever present (covers secondary rate
|
|
71
|
+
* limits, which carry it with `x-ratelimit-remaining > 0`);
|
|
72
|
+
* 3. `x-ratelimit-reset` (epoch seconds) but ONLY when `x-ratelimit-remaining`
|
|
73
|
+
* is `"0"` — GitHub returns the reset header on ~every response (~1h ahead),
|
|
74
|
+
* so honoring it unconditionally would balloon EVERY retried error (even a
|
|
75
|
+
* permissions 403) to the cap. Gating on real exhaustion matches octokit's
|
|
76
|
+
* throttling plugin.
|
|
77
|
+
* Returns `undefined` when no applicable hint is present/parseable.
|
|
78
|
+
*/
|
|
79
|
+
function retryAfterMsOf(error: unknown): number | undefined {
|
|
80
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
81
|
+
const r = (error as { retryAfterMs?: unknown }).retryAfterMs;
|
|
82
|
+
if (typeof r === "number" && Number.isFinite(r) && r >= 0) return r;
|
|
83
|
+
|
|
84
|
+
const headers = errorResponseHeaders(error);
|
|
85
|
+
if (!headers) return undefined;
|
|
86
|
+
const retryAfter = headers["retry-after"];
|
|
87
|
+
if (typeof retryAfter === "string" && retryAfter.trim() !== "") {
|
|
88
|
+
const parsed = parseRetryAfter(retryAfter);
|
|
89
|
+
if (parsed !== undefined) return parsed;
|
|
90
|
+
}
|
|
91
|
+
if (headers["x-ratelimit-remaining"] === "0") {
|
|
92
|
+
const reset = headers["x-ratelimit-reset"];
|
|
93
|
+
if (typeof reset === "string" && reset.trim() !== "") {
|
|
94
|
+
const resetEpoch = Number(reset);
|
|
95
|
+
if (Number.isFinite(resetEpoch)) return Math.max(0, resetEpoch * 1000 - Date.now());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Parse an HTTP `Retry-After` header value into milliseconds. Accepts the two
|
|
103
|
+
* RFC-9110 forms — delta-seconds (`"120"`) and an HTTP-date (`"Wed, 21 Oct 2026
|
|
104
|
+
* 07:28:00 GMT"`) — and returns `undefined` for anything else. Callers that hold
|
|
105
|
+
* the `Response` attach the result as `retryAfterMs` on the thrown error so
|
|
106
|
+
* {@link retry} can floor its backoff to it.
|
|
107
|
+
*/
|
|
108
|
+
export function parseRetryAfter(
|
|
109
|
+
value: string | null | undefined,
|
|
110
|
+
): number | undefined {
|
|
111
|
+
if (!value) return undefined;
|
|
112
|
+
const trimmed = value.trim();
|
|
113
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1000;
|
|
114
|
+
const at = Date.parse(trimmed);
|
|
115
|
+
if (!Number.isNaN(at)) return Math.max(0, at - Date.now());
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Statuses worth retrying: rate-limit (429) + transient server errors (502/503/504).
|
|
120
|
+
const RETRYABLE_STATUSES = new Set([429, 502, 503, 504]);
|
|
121
|
+
|
|
122
|
+
const defaultShouldRetry = (error: unknown): boolean => {
|
|
123
|
+
const status = statusOf(error);
|
|
124
|
+
if (status !== undefined && RETRYABLE_STATUSES.has(status)) return true;
|
|
125
|
+
if (!(error instanceof Error)) return false;
|
|
126
|
+
return (
|
|
127
|
+
error.name === "AbortError" ||
|
|
128
|
+
error.message.includes("fetch failed") ||
|
|
129
|
+
error.message.includes("ECONNRESET") ||
|
|
130
|
+
error.message.includes("ETIMEDOUT") ||
|
|
131
|
+
/\b(429|502|503|504)\b/.test(error.message)
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export async function retry<T>(
|
|
136
|
+
fn: () => Promise<T>,
|
|
137
|
+
options: RetryOptions = {},
|
|
138
|
+
): Promise<T> {
|
|
139
|
+
const shouldRetry = options.shouldRetry ?? defaultShouldRetry;
|
|
140
|
+
const label = options.label ?? "operation";
|
|
141
|
+
const delays = options.delaysMs
|
|
142
|
+
? Array.from(options.delaysMs)
|
|
143
|
+
: Array.from(
|
|
144
|
+
{ length: (options.maxAttempts ?? 3) - 1 },
|
|
145
|
+
(_, i) => (options.delayMs ?? 1000) * (i + 1),
|
|
146
|
+
);
|
|
147
|
+
const maxAttempts = delays.length + 1;
|
|
148
|
+
|
|
149
|
+
let lastError: unknown;
|
|
150
|
+
|
|
151
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
152
|
+
try {
|
|
153
|
+
return await fn();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
lastError = error;
|
|
156
|
+
|
|
157
|
+
if (attempt === maxAttempts || !shouldRetry(error)) {
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const scheduled = delays[attempt - 1] ?? options.delayMs ?? 1000;
|
|
162
|
+
const serverDelay = retryAfterMsOf(error);
|
|
163
|
+
// cap the server hint first, then floor at the fixed delay — so a hint
|
|
164
|
+
// never shortens below `scheduled`, and an overlong one can't hang the
|
|
165
|
+
// run. the fixed backoff itself is never capped.
|
|
166
|
+
const cap = options.retryAfterCapMs ?? DEFAULT_RETRY_AFTER_CAP_MS;
|
|
167
|
+
const base =
|
|
168
|
+
serverDelay !== undefined
|
|
169
|
+
? Math.max(scheduled, Math.min(cap, serverDelay))
|
|
170
|
+
: scheduled;
|
|
171
|
+
const delay = Math.round(base + Math.random() * base * 0.5);
|
|
172
|
+
log.info(
|
|
173
|
+
`» ${label} failed (attempt ${attempt}/${maxAttempts}), retrying in ${delay}ms...`,
|
|
174
|
+
);
|
|
175
|
+
await sleep(delay);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw lastError;
|
|
180
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { PRODUCT_SLUG } from "#app/brand";
|
|
2
|
+
import type { WriteablePayload } from "#app/external";
|
|
3
|
+
import { reportReviewNodeId } from "#app/mcp/review";
|
|
4
|
+
import type { ToolContext } from "#app/mcp/server";
|
|
5
|
+
import { log } from "#app/utils/cli";
|
|
6
|
+
|
|
7
|
+
const RE_REVIEW_PREAMBLE =
|
|
8
|
+
"Incrementally re-review the new commits on this pull request. Use the IncrementalReview mode.";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* post-agent review lifecycle: runs after the agent exits (success or timeout).
|
|
12
|
+
*
|
|
13
|
+
* normally the agent handles new commits inline: create_pull_request_review
|
|
14
|
+
* detects HEAD movement and tells the agent to pull and review the delta.
|
|
15
|
+
* this dispatch is a safety net for cases where the agent couldn't handle
|
|
16
|
+
* it (timeout, error, etc).
|
|
17
|
+
*
|
|
18
|
+
* ordering matters: reportReviewNodeId marks this run "done" FIRST so push
|
|
19
|
+
* webhooks stop being suppressed by dedup. the HEAD check runs SECOND to
|
|
20
|
+
* catch any pushes that were suppressed while this run was in-flight.
|
|
21
|
+
*/
|
|
22
|
+
export async function postReviewCleanup(ctx: ToolContext): Promise<void> {
|
|
23
|
+
const review = ctx.toolState.review;
|
|
24
|
+
if (!review) return;
|
|
25
|
+
delete ctx.toolState.review;
|
|
26
|
+
|
|
27
|
+
// mark review as submitted — unlocks webhook dedup for new pushes
|
|
28
|
+
await bestEffort(
|
|
29
|
+
() => reportReviewNodeId(ctx, { nodeId: review.nodeId }),
|
|
30
|
+
"reportReviewNodeId",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// dispatch follow-up if PR HEAD moved past the reviewed commit
|
|
34
|
+
if (review.reviewedSha) {
|
|
35
|
+
await bestEffort(
|
|
36
|
+
() => dispatchFollowUpReReview(ctx, review.reviewedSha!),
|
|
37
|
+
"follow-up re-review dispatch",
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function bestEffort(
|
|
43
|
+
fn: () => Promise<unknown>,
|
|
44
|
+
label: string,
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
await fn();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
log.debug(`${label} failed: ${error}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function dispatchFollowUpReReview(
|
|
54
|
+
ctx: ToolContext,
|
|
55
|
+
reviewedSha: string,
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
const issueNumber = ctx.payload.event.issue_number;
|
|
58
|
+
if (!issueNumber) return;
|
|
59
|
+
|
|
60
|
+
const pr = await ctx.octokit.rest.pulls.get({
|
|
61
|
+
owner: ctx.repo.owner,
|
|
62
|
+
repo: ctx.repo.name,
|
|
63
|
+
pull_number: issueNumber,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (pr.data.head.sha === reviewedSha) return;
|
|
67
|
+
if (pr.data.state !== "open") return;
|
|
68
|
+
if (pr.data.draft) return;
|
|
69
|
+
|
|
70
|
+
log.info(
|
|
71
|
+
`safety net: pr HEAD moved from ${reviewedSha.slice(0, 7)} to ${pr.data.head.sha.slice(0, 7)} ` +
|
|
72
|
+
`and agent did not review inline — dispatching follow-up re-review`,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const event: WriteablePayload["event"] = {
|
|
76
|
+
trigger: "pull_request_synchronize",
|
|
77
|
+
issue_number: issueNumber,
|
|
78
|
+
is_pr: true,
|
|
79
|
+
title: pr.data.title,
|
|
80
|
+
body: null,
|
|
81
|
+
branch: pr.data.head.ref,
|
|
82
|
+
before_sha: reviewedSha,
|
|
83
|
+
silent: true,
|
|
84
|
+
};
|
|
85
|
+
if (ctx.payload.event.authorPermission) {
|
|
86
|
+
event.authorPermission = ctx.payload.event.authorPermission;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const payload: WriteablePayload = {
|
|
90
|
+
"~infraweaver": true,
|
|
91
|
+
version: ctx.payload.version,
|
|
92
|
+
model: ctx.payload.model,
|
|
93
|
+
prompt: "",
|
|
94
|
+
eventInstructions: RE_REVIEW_PREAMBLE,
|
|
95
|
+
event,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
await ctx.octokit.rest.actions.createWorkflowDispatch({
|
|
99
|
+
owner: ctx.repo.owner,
|
|
100
|
+
repo: ctx.repo.name,
|
|
101
|
+
workflow_id: getCurrentWorkflowFilename(),
|
|
102
|
+
ref: pr.data.base.repo.default_branch,
|
|
103
|
+
inputs: { prompt: JSON.stringify(payload) },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* derive the running workflow's filename from `GITHUB_WORKFLOW_REF`, which has the form
|
|
109
|
+
* `<owner>/<repo>/.github/workflows/<filename>@<ref>` (e.g. `.../infraweaver.yaml@refs/heads/main`).
|
|
110
|
+
* falls back to `infraweaver.yml` if the env var is missing or malformed (shouldn't happen in CI).
|
|
111
|
+
*/
|
|
112
|
+
function getCurrentWorkflowFilename(): string {
|
|
113
|
+
const ref = process.env.GITHUB_WORKFLOW_REF ?? "";
|
|
114
|
+
const match = ref.match(/\/([^/]+)@/);
|
|
115
|
+
return match?.[1] ?? `${PRODUCT_SLUG}.yml`;
|
|
116
|
+
}
|