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,676 @@
|
|
|
1
|
+
import { createSign } from "node:crypto";
|
|
2
|
+
import { rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import * as core from "@actions/core";
|
|
5
|
+
import { throttling } from "@octokit/plugin-throttling";
|
|
6
|
+
import { Octokit } from "@octokit/rest";
|
|
7
|
+
import { DOCS_URL, PRODUCT_NAME, PRODUCT_SLUG } from "#app/brand";
|
|
8
|
+
import { apiFetch } from "#app/utils/apiFetch";
|
|
9
|
+
import { cloudOidcAudience } from "#app/utils/apiUrl";
|
|
10
|
+
import { retry } from "#app/utils/retry";
|
|
11
|
+
|
|
12
|
+
function isObject(value: unknown) {
|
|
13
|
+
return typeof value === "object" && value !== null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// we don't get access to the actual class from @octokit/rest
|
|
17
|
+
// it's reachable from @octokit/request-error but we'd have to add a dependency on it
|
|
18
|
+
// and it would pose a risk of accidentally pulling a different version of that class (node_modules dep graphs ❤️)
|
|
19
|
+
// so it's safer to ducktype this
|
|
20
|
+
interface OctokitResponseShim {
|
|
21
|
+
headers: Record<string, string | number | undefined>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface InstallationToken {
|
|
25
|
+
token: string;
|
|
26
|
+
expires_at: string;
|
|
27
|
+
installation_id: number;
|
|
28
|
+
repository: string;
|
|
29
|
+
ref: string;
|
|
30
|
+
runner_environment: string;
|
|
31
|
+
owner?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface GitHubAppConfig {
|
|
35
|
+
appId: string;
|
|
36
|
+
privateKey: string;
|
|
37
|
+
repoOwner: string;
|
|
38
|
+
repoName: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface Installation {
|
|
42
|
+
id: number;
|
|
43
|
+
account: {
|
|
44
|
+
login: string;
|
|
45
|
+
type: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface Repository {
|
|
50
|
+
owner: {
|
|
51
|
+
login: string;
|
|
52
|
+
};
|
|
53
|
+
name: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface InstallationTokenResponse {
|
|
57
|
+
token: string;
|
|
58
|
+
expires_at: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface RepositoriesResponse {
|
|
62
|
+
repositories: Repository[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isOIDCAvailable(): boolean {
|
|
66
|
+
// OIDC requires both env vars to be set (only in real GitHub Actions with id-token permission)
|
|
67
|
+
return Boolean(
|
|
68
|
+
process.env.ACTIONS_ID_TOKEN_REQUEST_URL &&
|
|
69
|
+
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Snapshot the GHA OIDC request creds from the env. Returns undefined off-CI or
|
|
75
|
+
* once they have been scrubbed. Call this BEFORE main.ts's restricted-mode env
|
|
76
|
+
* scrub so a mid-run token re-mint can still fetch a fresh OIDC JWT afterwards.
|
|
77
|
+
*/
|
|
78
|
+
export function readOidcCreds(): OidcCreds | undefined {
|
|
79
|
+
const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
|
80
|
+
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
|
81
|
+
return requestUrl && requestToken ? { requestUrl, requestToken } : undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fetch a fresh OIDC JWT from the GHA token service using stashed creds — the
|
|
86
|
+
* env-independent equivalent of `core.getIDToken(audience)` (same
|
|
87
|
+
* `${url}&audience=` shape + bearer request token). Lets a re-mint succeed after
|
|
88
|
+
* the ACTIONS_ID_TOKEN_REQUEST_* env vars `core.getIDToken` reads are scrubbed.
|
|
89
|
+
*/
|
|
90
|
+
export async function fetchOidcIdToken(
|
|
91
|
+
creds: OidcCreds,
|
|
92
|
+
audience: string,
|
|
93
|
+
): Promise<string> {
|
|
94
|
+
const res = await fetch(
|
|
95
|
+
`${creds.requestUrl}&audience=${encodeURIComponent(audience)}`,
|
|
96
|
+
{
|
|
97
|
+
headers: {
|
|
98
|
+
Authorization: `Bearer ${creds.requestToken}`,
|
|
99
|
+
Accept: "application/json",
|
|
100
|
+
},
|
|
101
|
+
signal: AbortSignal.timeout(10_000),
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`OIDC token request failed: ${res.status} ${res.statusText}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const body = (await res.json()) as { value?: unknown };
|
|
110
|
+
if (typeof body.value !== "string" || !body.value) {
|
|
111
|
+
throw new Error("OIDC token response missing 'value'");
|
|
112
|
+
}
|
|
113
|
+
return body.value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type ReadWrite = "read" | "write";
|
|
117
|
+
type WriteOnly = "write";
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* GitHub App installation access token permissions.
|
|
121
|
+
* passed to `POST /app/installations/{id}/access_tokens` to scope the token.
|
|
122
|
+
* fields and allowed values come from the `app-permissions` OpenAPI schema.
|
|
123
|
+
* @see https://docs.github.com/en/rest/apps/installations#create-an-installation-access-token-for-an-app
|
|
124
|
+
* @see https://github.com/github/rest-api-description — components.schemas.app-permissions
|
|
125
|
+
*/
|
|
126
|
+
type GitHubAppPermissions = {
|
|
127
|
+
actions?: ReadWrite;
|
|
128
|
+
artifact_metadata?: ReadWrite;
|
|
129
|
+
attestations?: ReadWrite;
|
|
130
|
+
checks?: ReadWrite;
|
|
131
|
+
contents?: ReadWrite;
|
|
132
|
+
deployments?: ReadWrite;
|
|
133
|
+
discussions?: ReadWrite;
|
|
134
|
+
issues?: ReadWrite;
|
|
135
|
+
packages?: ReadWrite;
|
|
136
|
+
pages?: ReadWrite;
|
|
137
|
+
pull_requests?: ReadWrite;
|
|
138
|
+
security_events?: ReadWrite;
|
|
139
|
+
statuses?: ReadWrite;
|
|
140
|
+
workflows?: WriteOnly;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Stashed GitHub Actions OIDC request credentials (`ACTIONS_ID_TOKEN_REQUEST_URL`
|
|
145
|
+
* + `_TOKEN`). Captured by {@link readOidcCreds} while the env vars are still
|
|
146
|
+
* present so a mid-run token re-mint can fetch a fresh OIDC JWT even after main.ts
|
|
147
|
+
* scrubs those vars from the env (which it does in every shell mode).
|
|
148
|
+
*/
|
|
149
|
+
export type OidcCreds = { requestUrl: string; requestToken: string };
|
|
150
|
+
|
|
151
|
+
type AcquireTokenOptions = {
|
|
152
|
+
repos?: string[];
|
|
153
|
+
permissions?: GitHubAppPermissions;
|
|
154
|
+
/** when set, mint via OIDC using these stashed creds instead of reading the
|
|
155
|
+
* (possibly already-scrubbed) ACTIONS_ID_TOKEN_REQUEST_* env vars. */
|
|
156
|
+
oidc?: OidcCreds;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Thrown when our token-exchange endpoint returns a non-2xx response.
|
|
161
|
+
* The retry policy in `acquireNewToken` looks for this concrete type to
|
|
162
|
+
* skip retries — 4xx is terminal user state (not-installed, not-authorized)
|
|
163
|
+
* and 5xx is rare enough that re-running the workflow is the right escape
|
|
164
|
+
* hatch. Genuine network failures throw plain `Error` and stay retryable.
|
|
165
|
+
*/
|
|
166
|
+
class TokenExchangeError extends Error {
|
|
167
|
+
readonly status: number;
|
|
168
|
+
constructor(status: number, message: string) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.name = "TokenExchangeError";
|
|
171
|
+
this.status = status;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function acquireTokenViaOIDC(
|
|
176
|
+
opts?: AcquireTokenOptions,
|
|
177
|
+
): Promise<string> {
|
|
178
|
+
const oidcToken = opts?.oidc
|
|
179
|
+
? await fetchOidcIdToken(opts.oidc, cloudOidcAudience())
|
|
180
|
+
: await core.getIDToken(cloudOidcAudience());
|
|
181
|
+
|
|
182
|
+
const repos = [...(opts?.repos ?? [])];
|
|
183
|
+
const targetRepo = process.env.GITHUB_REPOSITORY?.split("/")[1];
|
|
184
|
+
if (targetRepo) {
|
|
185
|
+
repos.push(targetRepo);
|
|
186
|
+
}
|
|
187
|
+
const reposParam = repos.length ? `?repos=${repos.join(",")}` : "";
|
|
188
|
+
|
|
189
|
+
const timeoutMs = 30000;
|
|
190
|
+
const controller = new AbortController();
|
|
191
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const tokenResponse = await apiFetch({
|
|
195
|
+
path: `/api/github/installation-token${reposParam}`,
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
Authorization: `Bearer ${oidcToken}`,
|
|
199
|
+
"Content-Type": "application/json",
|
|
200
|
+
},
|
|
201
|
+
body: opts?.permissions
|
|
202
|
+
? JSON.stringify({ permissions: opts.permissions })
|
|
203
|
+
: undefined,
|
|
204
|
+
signal: controller.signal,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
clearTimeout(timeoutId);
|
|
208
|
+
|
|
209
|
+
if (!tokenResponse.ok) {
|
|
210
|
+
// prefer the server-side `error` field — it's the single source of
|
|
211
|
+
// truth for the install URL (uses GITHUB_APP_INSTALL_URL, or the
|
|
212
|
+
// slug from NEXT_PUBLIC_GITHUB_APP_SLUG, which vary per env). fall back
|
|
213
|
+
// to a generic message if the body isn't JSON or has no `error` field.
|
|
214
|
+
let serverMessage: string | undefined;
|
|
215
|
+
try {
|
|
216
|
+
const body = (await tokenResponse.json()) as { error?: unknown };
|
|
217
|
+
if (typeof body.error === "string") serverMessage = body.error;
|
|
218
|
+
} catch {
|
|
219
|
+
// body wasn't JSON — fall through to the generic message
|
|
220
|
+
}
|
|
221
|
+
throw new TokenExchangeError(
|
|
222
|
+
tokenResponse.status,
|
|
223
|
+
serverMessage ??
|
|
224
|
+
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText}`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const tokenData = (await tokenResponse.json()) as InstallationToken;
|
|
229
|
+
return tokenData.token;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
clearTimeout(timeoutId);
|
|
232
|
+
|
|
233
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
234
|
+
throw new Error(`Token exchange timed out after ${timeoutMs}ms`);
|
|
235
|
+
}
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const base64UrlEncode = (str: string): string => {
|
|
241
|
+
return Buffer.from(str)
|
|
242
|
+
.toString("base64")
|
|
243
|
+
.replace(/\+/g, "-")
|
|
244
|
+
.replace(/\//g, "_")
|
|
245
|
+
.replace(/=/g, "");
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const generateJWT = (appId: string, privateKey: string): string => {
|
|
249
|
+
const now = Math.floor(Date.now() / 1000);
|
|
250
|
+
const payload = {
|
|
251
|
+
iat: now - 60,
|
|
252
|
+
exp: now + 5 * 60,
|
|
253
|
+
iss: appId,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const header = {
|
|
257
|
+
alg: "RS256",
|
|
258
|
+
typ: "JWT",
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(header));
|
|
262
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
|
|
263
|
+
const signaturePart = `${encodedHeader}.${encodedPayload}`;
|
|
264
|
+
|
|
265
|
+
const signature = createSign("RSA-SHA256")
|
|
266
|
+
.update(signaturePart)
|
|
267
|
+
.sign(privateKey, "base64")
|
|
268
|
+
.replace(/\+/g, "-")
|
|
269
|
+
.replace(/\//g, "_")
|
|
270
|
+
.replace(/=/g, "");
|
|
271
|
+
|
|
272
|
+
return `${signaturePart}.${signature}`;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const githubRequest = async <T>(
|
|
276
|
+
path: string,
|
|
277
|
+
options: {
|
|
278
|
+
method?: string;
|
|
279
|
+
headers?: Record<string, string>;
|
|
280
|
+
body?: string;
|
|
281
|
+
} = {},
|
|
282
|
+
): Promise<T> => {
|
|
283
|
+
const { method = "GET", headers = {}, body } = options;
|
|
284
|
+
|
|
285
|
+
const url = `https://api.github.com${path}`;
|
|
286
|
+
const requestHeaders = {
|
|
287
|
+
Accept: "application/vnd.github.v3+json",
|
|
288
|
+
"User-Agent": `${PRODUCT_NAME}-Installation-Token-Generator/1.0`,
|
|
289
|
+
...headers,
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const response = await fetch(url, {
|
|
293
|
+
method,
|
|
294
|
+
headers: requestHeaders,
|
|
295
|
+
...(body && { body }),
|
|
296
|
+
signal: AbortSignal.timeout(30_000),
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
const errorText = await response.text();
|
|
301
|
+
throw new Error(
|
|
302
|
+
`GitHub API request failed: ${response.status} ${response.statusText}\n${errorText}`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return response.json() as T;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const checkRepositoryAccess = async (
|
|
310
|
+
token: string,
|
|
311
|
+
repoOwner: string,
|
|
312
|
+
repoName: string,
|
|
313
|
+
): Promise<boolean> => {
|
|
314
|
+
try {
|
|
315
|
+
const response = await githubRequest<RepositoriesResponse>(
|
|
316
|
+
"/installation/repositories",
|
|
317
|
+
{
|
|
318
|
+
headers: { Authorization: `token ${token}` },
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const ownerLower = repoOwner.toLowerCase();
|
|
323
|
+
const nameLower = repoName.toLowerCase();
|
|
324
|
+
return response.repositories.some(
|
|
325
|
+
(repo) =>
|
|
326
|
+
repo.owner.login.toLowerCase() === ownerLower &&
|
|
327
|
+
repo.name.toLowerCase() === nameLower,
|
|
328
|
+
);
|
|
329
|
+
} catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const createInstallationToken = async (
|
|
335
|
+
jwt: string,
|
|
336
|
+
installationId: number,
|
|
337
|
+
permissions?: GitHubAppPermissions,
|
|
338
|
+
): Promise<string> => {
|
|
339
|
+
const requestOpts: {
|
|
340
|
+
method: string;
|
|
341
|
+
headers: Record<string, string>;
|
|
342
|
+
body?: string;
|
|
343
|
+
} = {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
346
|
+
};
|
|
347
|
+
if (permissions) {
|
|
348
|
+
requestOpts.body = JSON.stringify({ permissions });
|
|
349
|
+
}
|
|
350
|
+
const response = await githubRequest<InstallationTokenResponse>(
|
|
351
|
+
`/app/installations/${installationId}/access_tokens`,
|
|
352
|
+
requestOpts,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
return response.token;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const findInstallationId = async (
|
|
359
|
+
jwt: string,
|
|
360
|
+
repoOwner: string,
|
|
361
|
+
repoName: string,
|
|
362
|
+
): Promise<number> => {
|
|
363
|
+
const installations = await githubRequest<Installation[]>(
|
|
364
|
+
"/app/installations",
|
|
365
|
+
{
|
|
366
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
// Probing each installation is best-effort — one that legitimately can't mint a
|
|
371
|
+
// token (suspended, revoked) must not abort the search. But swallowing the error
|
|
372
|
+
// entirely made a systemic failure (a malformed App private key, a clock skew
|
|
373
|
+
// rejecting every JWT) surface as the misleading "no installation found", which
|
|
374
|
+
// sends the reader to reinstall an App that is in fact installed correctly. Keep
|
|
375
|
+
// the last error and surface it alongside the diagnosis.
|
|
376
|
+
let lastError: unknown;
|
|
377
|
+
for (const installation of installations) {
|
|
378
|
+
try {
|
|
379
|
+
const tempToken = await createInstallationToken(jwt, installation.id);
|
|
380
|
+
const hasAccess = await checkRepositoryAccess(
|
|
381
|
+
tempToken,
|
|
382
|
+
repoOwner,
|
|
383
|
+
repoName,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
if (hasAccess) {
|
|
387
|
+
return installation.id;
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
lastError = error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const detail =
|
|
395
|
+
lastError === undefined
|
|
396
|
+
? ""
|
|
397
|
+
: ` Last error while probing ${installations.length} installation(s): ${
|
|
398
|
+
lastError instanceof Error ? lastError.message : String(lastError)
|
|
399
|
+
}`;
|
|
400
|
+
throw new Error(
|
|
401
|
+
`No installation found with access to ${repoOwner}/${repoName}. ` +
|
|
402
|
+
`Ensure the GitHub App is installed on the target repository.${detail}`,
|
|
403
|
+
);
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// for local development only
|
|
407
|
+
async function acquireTokenViaGitHubApp(
|
|
408
|
+
opts?: AcquireTokenOptions,
|
|
409
|
+
): Promise<string> {
|
|
410
|
+
if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_PRIVATE_KEY) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
"cannot acquire token via GitHub App: GITHUB_APP_ID and GITHUB_PRIVATE_KEY must be set",
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const repoContext = parseRepoContext();
|
|
417
|
+
|
|
418
|
+
const config: GitHubAppConfig = {
|
|
419
|
+
appId: process.env.GITHUB_APP_ID,
|
|
420
|
+
privateKey: process.env.GITHUB_PRIVATE_KEY.replace(/\\n/g, "\n"),
|
|
421
|
+
repoOwner: repoContext.owner,
|
|
422
|
+
repoName: repoContext.name,
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const jwt = generateJWT(config.appId, config.privateKey);
|
|
426
|
+
const installationId = await findInstallationId(
|
|
427
|
+
jwt,
|
|
428
|
+
config.repoOwner,
|
|
429
|
+
config.repoName,
|
|
430
|
+
);
|
|
431
|
+
return await createInstallationToken(jwt, installationId, opts?.permissions);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* ensure a GitHub token is available in the environment.
|
|
436
|
+
*
|
|
437
|
+
* when OIDC is available (CI), always mints a fresh token scoped to
|
|
438
|
+
* GITHUB_REPOSITORY — overriding any inherited GITHUB_TOKEN that may
|
|
439
|
+
* be scoped to the wrong repo.
|
|
440
|
+
*
|
|
441
|
+
* otherwise falls back to GitHub App credentials for local development.
|
|
442
|
+
*
|
|
443
|
+
* only called from dev-run.ts (test/dev path) — the live action calls
|
|
444
|
+
* main() directly and never calls this.
|
|
445
|
+
*/
|
|
446
|
+
export async function ensureGitHubToken(): Promise<void> {
|
|
447
|
+
// when OIDC is available, always mint a fresh token scoped to
|
|
448
|
+
// GITHUB_REPOSITORY. the inherited GITHUB_TOKEN may be scoped to a
|
|
449
|
+
// different repo (e.g., runner token for infraweaver/app when tests
|
|
450
|
+
// target infraweaver/test-repo).
|
|
451
|
+
if (isOIDCAvailable()) {
|
|
452
|
+
const token = await acquireNewToken();
|
|
453
|
+
process.env.GITHUB_TOKEN = token;
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!process.env.GITHUB_TOKEN && !process.env.GH_TOKEN) {
|
|
458
|
+
const token = await acquireNewToken();
|
|
459
|
+
process.env.GITHUB_TOKEN = token;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export async function acquireNewToken(
|
|
464
|
+
opts?: AcquireTokenOptions,
|
|
465
|
+
): Promise<string> {
|
|
466
|
+
// stashed OIDC creds (opts.oidc) take the OIDC path even after the
|
|
467
|
+
// ACTIONS_ID_TOKEN_REQUEST_* env vars are scrubbed — that's what lets a
|
|
468
|
+
// mid-run re-mint work after the scrub. otherwise fall back to the env check.
|
|
469
|
+
if (opts?.oidc || isOIDCAvailable()) {
|
|
470
|
+
return await retry(() => acquireTokenViaOIDC(opts), {
|
|
471
|
+
label: "token exchange",
|
|
472
|
+
shouldRetry: (error) => {
|
|
473
|
+
// 4xx is terminal user state (app not installed, permissions wrong) —
|
|
474
|
+
// retrying just triples our log noise and the user's CI bill (see
|
|
475
|
+
// #693). 5xx/429 are transient (vercel cold start, github outage,
|
|
476
|
+
// rate limit) and should ride the existing backoff.
|
|
477
|
+
if (error instanceof TokenExchangeError)
|
|
478
|
+
return error.status >= 500 || error.status === 429;
|
|
479
|
+
return (
|
|
480
|
+
error instanceof Error &&
|
|
481
|
+
(error.message.includes("timed out") ||
|
|
482
|
+
error.message.includes("fetch failed") ||
|
|
483
|
+
error.message.includes("ECONNRESET") ||
|
|
484
|
+
error.message.includes("ETIMEDOUT"))
|
|
485
|
+
);
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
// running inside GitHub Actions but the OIDC env vars are absent — the
|
|
490
|
+
// workflow is missing `permissions: id-token: write`. surface an
|
|
491
|
+
// actionable, customer-facing message; the GitHub-App branch below is
|
|
492
|
+
// local-dev only. see #739.
|
|
493
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
494
|
+
throw new Error(
|
|
495
|
+
`missing \`permissions: id-token: write\` on the ${PRODUCT_NAME} workflow job.\n` +
|
|
496
|
+
"\n" +
|
|
497
|
+
`${PRODUCT_NAME} mints short-lived GitHub App installation tokens via OIDC and\n` +
|
|
498
|
+
"requires `id-token: write` to be granted at the job level. add the\n" +
|
|
499
|
+
"following to your workflow yaml:\n" +
|
|
500
|
+
"\n" +
|
|
501
|
+
" jobs:\n" +
|
|
502
|
+
` ${PRODUCT_SLUG}:\n` +
|
|
503
|
+
" permissions:\n" +
|
|
504
|
+
` id-token: write # mint ${PRODUCT_NAME} installation tokens via OIDC\n` +
|
|
505
|
+
" contents: read # for actions/checkout\n" +
|
|
506
|
+
"\n" +
|
|
507
|
+
`see ${DOCS_URL}/headless-action#required-permissions for the full template.`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
// local development via GitHub App
|
|
511
|
+
return await acquireTokenViaGitHubApp(opts);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export interface RepoContext {
|
|
515
|
+
owner: string;
|
|
516
|
+
name: string;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Parse repository context from GITHUB_REPOSITORY environment variable.
|
|
521
|
+
*/
|
|
522
|
+
export function parseRepoContext(): RepoContext {
|
|
523
|
+
const githubRepo = process.env.GITHUB_REPOSITORY;
|
|
524
|
+
if (!githubRepo) {
|
|
525
|
+
throw new Error("GITHUB_REPOSITORY environment variable is required");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const [owner, name] = githubRepo.split("/");
|
|
529
|
+
if (!owner || !name) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`Invalid GITHUB_REPOSITORY format: ${githubRepo}. Expected 'owner/repo'`,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return { owner, name };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export type OctokitWithPlugins = InstanceType<
|
|
539
|
+
ReturnType<typeof Octokit.plugin<typeof Octokit, [typeof throttling]>>
|
|
540
|
+
>;
|
|
541
|
+
|
|
542
|
+
export interface ResourceUsage {
|
|
543
|
+
requestCount: number;
|
|
544
|
+
rateLimitRemaining: number | null;
|
|
545
|
+
rateLimitResetMs: number | null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function emptyResourceUsage(): ResourceUsage {
|
|
549
|
+
return {
|
|
550
|
+
requestCount: 0,
|
|
551
|
+
rateLimitRemaining: null,
|
|
552
|
+
rateLimitResetMs: null,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// `core` and `graphql` are always seeded below, so they're declared as required
|
|
557
|
+
// (the index signature still admits other resources, which are added on demand).
|
|
558
|
+
const usageByResource: Record<string, ResourceUsage> & {
|
|
559
|
+
core: ResourceUsage;
|
|
560
|
+
graphql: ResourceUsage;
|
|
561
|
+
} = {
|
|
562
|
+
core: emptyResourceUsage(),
|
|
563
|
+
graphql: emptyResourceUsage(),
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
export interface UsageSummary {
|
|
567
|
+
version: 1;
|
|
568
|
+
github: {
|
|
569
|
+
core: ResourceUsage;
|
|
570
|
+
graphql: ResourceUsage;
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function getGitHubUsageSummary(): UsageSummary {
|
|
575
|
+
return {
|
|
576
|
+
version: 1,
|
|
577
|
+
github: {
|
|
578
|
+
core: usageByResource.core,
|
|
579
|
+
graphql: usageByResource.graphql,
|
|
580
|
+
},
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export async function writeGitHubUsageSummaryToFile(
|
|
585
|
+
path: string,
|
|
586
|
+
): Promise<void> {
|
|
587
|
+
const summary = getGitHubUsageSummary();
|
|
588
|
+
const tmpPath = join(dirname(path), `.usage-summary-${process.pid}.tmp`);
|
|
589
|
+
await writeFile(tmpPath, JSON.stringify(summary));
|
|
590
|
+
await rename(tmpPath, path);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export function createOctokit(
|
|
594
|
+
token: string,
|
|
595
|
+
refreshAuth?: (stale: string) => Promise<string>,
|
|
596
|
+
): OctokitWithPlugins {
|
|
597
|
+
// `OctokitWithPlugins` initialization based on https://github.com/actions/toolkit/blob/2506e78e82fbd2f9e94d63e75f5309118c8de1b1/packages/github/src/github.ts#L15-L22
|
|
598
|
+
// we can't use it directly because it's stuck on `@octokit/core@v5` and we use the hottest `@octokit/core@v7`
|
|
599
|
+
//
|
|
600
|
+
// Auth is applied in the request hook below rather than via the constructor
|
|
601
|
+
// `auth` option: a 401 can swap in a re-minted installation token mid-run
|
|
602
|
+
// (GitHub can reject a freshly-minted token while it replicates; pullfrog #891
|
|
603
|
+
// re-acquires one), and the hook owning the Authorization header end-to-end
|
|
604
|
+
// means no auth strategy clobbers the refreshed token on the replayed request.
|
|
605
|
+
let currentToken = token;
|
|
606
|
+
const OctokitWithPlugins = Octokit.plugin(throttling);
|
|
607
|
+
const octokit = new OctokitWithPlugins({
|
|
608
|
+
throttle: {
|
|
609
|
+
onRateLimit: (_retryAfter, _options, _octokit, retryCount) => {
|
|
610
|
+
return retryCount <= 2;
|
|
611
|
+
},
|
|
612
|
+
onSecondaryRateLimit: (_retryAfter, _options, _octokit, retryCount) => {
|
|
613
|
+
return retryCount <= 2;
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
const onResponse = (response: OctokitResponseShim) => {
|
|
619
|
+
const resource = response.headers["x-ratelimit-resource"];
|
|
620
|
+
if (!resource) {
|
|
621
|
+
return response;
|
|
622
|
+
}
|
|
623
|
+
usageByResource[resource] ??= emptyResourceUsage();
|
|
624
|
+
const usage = usageByResource[resource];
|
|
625
|
+
usage.requestCount++;
|
|
626
|
+
const remaining = response.headers["x-ratelimit-remaining"];
|
|
627
|
+
const reset = response.headers["x-ratelimit-reset"];
|
|
628
|
+
if (remaining !== undefined) {
|
|
629
|
+
usage.rateLimitRemaining = Number(remaining);
|
|
630
|
+
}
|
|
631
|
+
if (reset !== undefined) {
|
|
632
|
+
usage.rateLimitResetMs = Number(reset) * 1000;
|
|
633
|
+
}
|
|
634
|
+
return response;
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
octokit.hook.wrap("request", async (request, options) => {
|
|
638
|
+
// snapshot the token we're about to use; refreshAuth single-flights on it so
|
|
639
|
+
// concurrent requests that all sent the same stale token coalesce to one mint.
|
|
640
|
+
const sentToken = currentToken;
|
|
641
|
+
options.headers.authorization = `token ${sentToken}`;
|
|
642
|
+
try {
|
|
643
|
+
const response = await request(options);
|
|
644
|
+
onResponse(response);
|
|
645
|
+
return response;
|
|
646
|
+
} catch (error) {
|
|
647
|
+
if (
|
|
648
|
+
isObject(error) &&
|
|
649
|
+
"response" in error &&
|
|
650
|
+
isObject(error.response) &&
|
|
651
|
+
"headers" in error.response &&
|
|
652
|
+
isObject(error.response.headers)
|
|
653
|
+
) {
|
|
654
|
+
onResponse(error.response as OctokitResponseShim);
|
|
655
|
+
}
|
|
656
|
+
// GitHub rejected the installation token with 401 — re-acquire a fresh one
|
|
657
|
+
// and replay the request once. covers the post-mint replication window
|
|
658
|
+
// where a just-minted token is briefly unrecognised. see pullfrog #891.
|
|
659
|
+
if (
|
|
660
|
+
refreshAuth &&
|
|
661
|
+
isObject(error) &&
|
|
662
|
+
"status" in error &&
|
|
663
|
+
error.status === 401
|
|
664
|
+
) {
|
|
665
|
+
currentToken = await refreshAuth(sentToken);
|
|
666
|
+
options.headers.authorization = `token ${currentToken}`;
|
|
667
|
+
const response = await request(options);
|
|
668
|
+
onResponse(response);
|
|
669
|
+
return response;
|
|
670
|
+
}
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
return octokit;
|
|
676
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
export const isCloudflareSandbox =
|
|
4
|
+
!!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
|
|
5
|
+
|
|
6
|
+
export const isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
|
7
|
+
|
|
8
|
+
// detect if running inside Docker container (CI tests run in Docker with host env vars)
|
|
9
|
+
export const isInsideDocker = existsSync("/.dockerenv");
|