infraweaver 0.3.12 → 0.3.13
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/README.md +1 -1
- package/dist/agents/opencode.d.ts +6 -0
- package/dist/agents/quotaTokens.d.ts +6 -0
- package/dist/cli.mjs +554 -199
- package/dist/index.js +531 -176
- package/dist/internal.js +4 -4
- package/dist/mcp/git.d.ts +6 -4
- package/dist/mcp/localContext.d.ts +7 -0
- package/dist/mcp/reviewCommentLimit.d.ts +55 -5
- package/dist/mcp/reviewMarkers.d.ts +5 -0
- package/dist/mcp/staleFix.d.ts +36 -1
- package/dist/mcp/terraform/concernResult.d.ts +9 -0
- package/dist/mcp/terraform/deltaSummary.d.ts +3 -0
- package/dist/mcp/terraform/hardcodedSecrets.d.ts +24 -10
- package/dist/mcp/terraform/refactorScope.d.ts +14 -2
- package/dist/mcp/terraform/ruleContext.d.ts +3 -0
- package/dist/mcp/terraform/tree.d.ts +5 -0
- package/dist/mcp/terraform/types.d.ts +2 -1
- package/dist/mcp/terraform/verifySites.d.ts +11 -3
- package/dist/vendor/@infraweaver-io/models/tokenQuota.d.ts +7 -6
- package/package.json +2 -2
- package/src/agents/opencode.ts +8 -1
- package/src/agents/quotaTokens.ts +7 -1
- package/src/mcp/git.ts +64 -12
- package/src/mcp/localContext.ts +6 -1
- package/src/mcp/moduleExtraction.ts +5 -4
- package/src/mcp/review.ts +52 -3
- package/src/mcp/reviewCommentLimit.ts +169 -26
- package/src/mcp/reviewMarkers.ts +11 -0
- package/src/mcp/reviewProvenance.ts +1 -1
- package/src/mcp/staleFix.ts +73 -2
- package/src/mcp/terraform/concernResult.ts +17 -10
- package/src/mcp/terraform/deltaSummary.ts +4 -0
- package/src/mcp/terraform/hardcodedSecrets.ts +148 -27
- package/src/mcp/terraform/nativeRuleScanners.ts +2 -1
- package/src/mcp/terraform/nativeScan.ts +4 -0
- package/src/mcp/terraform/refactor/equivalence.ts +42 -9
- package/src/mcp/terraform/refactorScope.ts +90 -21
- package/src/mcp/terraform/ruleContext.ts +4 -0
- package/src/mcp/terraform/scanDelta.ts +67 -26
- package/src/mcp/terraform/tools/detectPlanDrift.ts +2 -0
- package/src/mcp/terraform/tools/ingestExternalFindings.ts +2 -0
- package/src/mcp/terraform/tools/normalizationCandidates.ts +5 -5
- package/src/mcp/terraform/tools/readFindings.ts +2 -0
- package/src/mcp/terraform/tools/scan.ts +3 -0
- package/src/mcp/terraform/tree.ts +8 -0
- package/src/mcp/terraform/types.ts +2 -1
- package/src/mcp/terraform/verifySites.ts +23 -5
- package/src/modes/refactor.ts +1 -1
- package/src/modes/remediate-and-refactor.ts +1 -1
- package/src/modes/remediate.ts +1 -1
- package/src/phases/runAgentWithWatchdogs.ts +3 -2
package/dist/cli.mjs
CHANGED
|
@@ -90400,23 +90400,23 @@ var require_helpers = __commonJS({
|
|
|
90400
90400
|
"node_modules/.pnpm/ip-address@10.7.3/node_modules/ip-address/dist/v6/helpers.js"(exports2) {
|
|
90401
90401
|
"use strict";
|
|
90402
90402
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
90403
|
-
exports2.escapeHtml =
|
|
90403
|
+
exports2.escapeHtml = escapeHtml2;
|
|
90404
90404
|
exports2.spanAllZeroes = spanAllZeroes;
|
|
90405
90405
|
exports2.spanAll = spanAll;
|
|
90406
90406
|
exports2.spanLeadingZeroes = spanLeadingZeroes;
|
|
90407
90407
|
exports2.simpleGroup = simpleGroup;
|
|
90408
|
-
function
|
|
90408
|
+
function escapeHtml2(s) {
|
|
90409
90409
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
90410
90410
|
}
|
|
90411
90411
|
function spanAllZeroes(s) {
|
|
90412
|
-
return
|
|
90412
|
+
return escapeHtml2(s).replace(/(0+)/g, '<span class="zero">$1</span>');
|
|
90413
90413
|
}
|
|
90414
90414
|
function spanAll(s, offset = 0) {
|
|
90415
90415
|
const letters = s.split("");
|
|
90416
|
-
return letters.map((n, i) => `<span class="digit value-${
|
|
90416
|
+
return letters.map((n, i) => `<span class="digit value-${escapeHtml2(n)} position-${i + offset}">${spanAllZeroes(n)}</span>`).join("");
|
|
90417
90417
|
}
|
|
90418
90418
|
function spanLeadingZeroesSimple(group2) {
|
|
90419
|
-
return
|
|
90419
|
+
return escapeHtml2(group2).replace(/^(0+)/, '<span class="zero">$1</span>');
|
|
90420
90420
|
}
|
|
90421
90421
|
function spanLeadingZeroes(address) {
|
|
90422
90422
|
const groups2 = address.split(":");
|
|
@@ -160536,7 +160536,7 @@ var init_cacheTwirpClient = __esm({
|
|
|
160536
160536
|
});
|
|
160537
160537
|
|
|
160538
160538
|
// node_modules/.pnpm/@actions+cache@6.3.0_supports-color@7.2.0/node_modules/@actions/cache/lib/internal/tar.js
|
|
160539
|
-
import { existsSync as
|
|
160539
|
+
import { existsSync as existsSync18, writeFileSync as writeFileSync21 } from "fs";
|
|
160540
160540
|
import * as path13 from "path";
|
|
160541
160541
|
function getTarPath() {
|
|
160542
160542
|
return __awaiter18(this, void 0, void 0, function* () {
|
|
@@ -160546,7 +160546,7 @@ function getTarPath() {
|
|
|
160546
160546
|
const systemTar = SystemTarPathOnWindows;
|
|
160547
160547
|
if (gnuTar) {
|
|
160548
160548
|
return { path: gnuTar, type: ArchiveToolType.GNU };
|
|
160549
|
-
} else if (
|
|
160549
|
+
} else if (existsSync18(systemTar)) {
|
|
160550
160550
|
return { path: systemTar, type: ArchiveToolType.BSD };
|
|
160551
160551
|
}
|
|
160552
160552
|
break;
|
|
@@ -191031,7 +191031,7 @@ var BOT_EMAIL = `${BOT_LOGIN}@users.noreply.github.com`;
|
|
|
191031
191031
|
var OIDC_AUDIENCE = `${PRODUCT_SLUG}-api`;
|
|
191032
191032
|
|
|
191033
191033
|
// src/commands/assess.ts
|
|
191034
|
-
import { existsSync as
|
|
191034
|
+
import { existsSync as existsSync9, statSync as statSync8 } from "node:fs";
|
|
191035
191035
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
191036
191036
|
var import_arg = __toESM(require_arg(), 1);
|
|
191037
191037
|
|
|
@@ -199939,7 +199939,7 @@ var log = {
|
|
|
199939
199939
|
debug: (...args2) => current.debug(...args2)
|
|
199940
199940
|
};
|
|
199941
199941
|
|
|
199942
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
199942
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.7/node_modules/@infraweaver-io/models/dist/usage.js
|
|
199943
199943
|
function formatCostUsd(costUsd) {
|
|
199944
199944
|
return costUsd.toFixed(4);
|
|
199945
199945
|
}
|
|
@@ -200490,7 +200490,7 @@ import { randomBytes } from "node:crypto";
|
|
|
200490
200490
|
// package.json
|
|
200491
200491
|
var package_default = {
|
|
200492
200492
|
name: "infraweaver",
|
|
200493
|
-
version: "0.3.
|
|
200493
|
+
version: "0.3.13",
|
|
200494
200494
|
description: "GitHub Action that remediates Terraform to best practices and opens one scoped pull request per concern.",
|
|
200495
200495
|
keywords: [
|
|
200496
200496
|
"ai-coding-agent",
|
|
@@ -200593,7 +200593,7 @@ var package_default = {
|
|
|
200593
200593
|
"@infraweaver-io/ci": "^0.1.8",
|
|
200594
200594
|
"@infraweaver-io/core": "^0.1.5",
|
|
200595
200595
|
"@infraweaver-io/crosswalk": "^1.2.0",
|
|
200596
|
-
"@infraweaver-io/models": "^0.1.
|
|
200596
|
+
"@infraweaver-io/models": "^0.1.7",
|
|
200597
200597
|
"@infraweaver-io/policy-spec": "^1.2.0",
|
|
200598
200598
|
"@infraweaver-io/report-contract": "^0.1.5",
|
|
200599
200599
|
"@infraweaver-io/sandbox": "^0.1.8",
|
|
@@ -207495,7 +207495,18 @@ var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
|
207495
207495
|
"credential",
|
|
207496
207496
|
"credentials"
|
|
207497
207497
|
]);
|
|
207498
|
-
var SECRET_KEY_PREFIXES = /* @__PURE__ */ new Set([
|
|
207498
|
+
var SECRET_KEY_PREFIXES = /* @__PURE__ */ new Set([
|
|
207499
|
+
"api",
|
|
207500
|
+
"secret",
|
|
207501
|
+
"private",
|
|
207502
|
+
"signing",
|
|
207503
|
+
"access",
|
|
207504
|
+
"account",
|
|
207505
|
+
"client",
|
|
207506
|
+
"shared",
|
|
207507
|
+
"preshared",
|
|
207508
|
+
"master"
|
|
207509
|
+
]);
|
|
207499
207510
|
var NOT_SECRET_TOKENS = /* @__PURE__ */ new Set(["creation", "idempotency", "client", "request", "continuation"]);
|
|
207500
207511
|
function words(name) {
|
|
207501
207512
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[_-]+/).filter(Boolean);
|
|
@@ -207507,24 +207518,70 @@ function isSecretName(name) {
|
|
|
207507
207518
|
if (last === void 0) return false;
|
|
207508
207519
|
if (last === "key") return prev !== void 0 && SECRET_KEY_PREFIXES.has(prev);
|
|
207509
207520
|
if (last === "token" && prev !== void 0 && NOT_SECRET_TOKENS.has(prev)) return false;
|
|
207521
|
+
if (last === "string") return prev === "connection";
|
|
207522
|
+
if (last === "secret" && w.length === 1) return false;
|
|
207510
207523
|
return SECRET_WORDS.has(last);
|
|
207511
207524
|
}
|
|
207512
207525
|
function isPlaceholder(inner) {
|
|
207513
|
-
return inner.trim() === "" || /^[*xX.]+$/.test(inner) || /^<[^>]*>$/.test(inner) || //
|
|
207514
|
-
|
|
207526
|
+
return inner.trim() === "" || /^[*xX.]+$/.test(inner) || /^<[^>]*>$/.test(inner) || /^(?:true|false|null)$/.test(inner) || /^-?\d+(?:\.\d+)?$/.test(inner) || // a template directive is computed, like an interpolation: `"%{ if … }…%{ endif }"`
|
|
207527
|
+
inner.includes("%{") || // the dummy credentials offline plans and LocalStack are given (`secret_key = "test"`,
|
|
207528
|
+
// `"example-secret"`) — but only when that is the whole value: a lowercase tail at
|
|
207529
|
+
// most, so `"Test-Pr0dDbPassw0rd!"` is a password that happens to start with a word
|
|
207530
|
+
isDummy(inner) || // a token's lifetime unit: `token_validity_units { access_token = "hours" }`
|
|
207515
207531
|
/^(?:seconds|minutes|hours|days)$/.test(inner);
|
|
207516
207532
|
}
|
|
207517
|
-
|
|
207533
|
+
function isDummy(inner) {
|
|
207534
|
+
const m = /^(?:test|mock|fake|dummy|fixture|example|placeholder|sample)([_-].*)?$/i.exec(inner);
|
|
207535
|
+
return m !== null && (m[1] === void 0 || /^[_-][a-z0-9_-]*$/.test(m[1]));
|
|
207536
|
+
}
|
|
207537
|
+
function isSecretReference(inner) {
|
|
207538
|
+
return inner.startsWith("arn:") || /^projects\/[^/]+\/secrets\//.test(inner) || /^https:\/\/[\w-]+\.vault\.azure\.net\/secrets\//i.test(inner) || /^\/[\w./-]+$/.test(inner);
|
|
207539
|
+
}
|
|
207540
|
+
var HEADER = /^(resource|data|provider|module|locals|variable|output|terraform)\b([^{]*)\{(.*)$/;
|
|
207518
207541
|
var ASSIGNMENT = /^\s*(?:"([A-Za-z_][\w-]*)"|([A-Za-z_][\w-]*))\s*=(?!=)\s*(.*)$/;
|
|
207519
207542
|
var HEREDOC = /<<-?([A-Za-z_]\w*)\s*$/;
|
|
207520
207543
|
var INLINE_PAIR = /(?:"([A-Za-z_][\w-]*)"|\b([A-Za-z_][\w-]*))\s*=(?!=)\s*("(?:[^"\\]|\\.)*")/g;
|
|
207521
|
-
function literalValue(raw2) {
|
|
207544
|
+
function literalValue(name, raw2) {
|
|
207522
207545
|
const value2 = stripTrailingComment(raw2).replace(/,$/, "").trim();
|
|
207523
|
-
|
|
207546
|
+
if (!isStringLiteral(value2)) return false;
|
|
207547
|
+
const inner = value2.slice(1, -1);
|
|
207548
|
+
if (isPlaceholder(inner) || isSecretReference(inner)) return false;
|
|
207549
|
+
return !(words(name).at(-1) === "auth" && /^[A-Z]+(?:_[A-Z]+)*$/.test(inner));
|
|
207550
|
+
}
|
|
207551
|
+
function variableName(holder) {
|
|
207552
|
+
return /^variable\s+"?([\w-]+)"?/.exec(holder)?.[1] ?? "";
|
|
207553
|
+
}
|
|
207554
|
+
function opensBlockComment(line) {
|
|
207555
|
+
for (let i = 0; i < line.length; i++) {
|
|
207556
|
+
const span = skipNonCode(line, i);
|
|
207557
|
+
if (!span) continue;
|
|
207558
|
+
if (line.startsWith("/*", i) && !line.includes("*/", i + 2)) return true;
|
|
207559
|
+
i = span.end;
|
|
207560
|
+
}
|
|
207561
|
+
return false;
|
|
207562
|
+
}
|
|
207563
|
+
function closesOnLine(rest) {
|
|
207564
|
+
let depth = 1;
|
|
207565
|
+
for (let i = 0; i < rest.length; i++) {
|
|
207566
|
+
const span = skipNonCode(rest, i);
|
|
207567
|
+
if (span) {
|
|
207568
|
+
i = span.end;
|
|
207569
|
+
continue;
|
|
207570
|
+
}
|
|
207571
|
+
if (rest[i] === "{") depth++;
|
|
207572
|
+
else if (rest[i] === "}" && --depth === 0) return true;
|
|
207573
|
+
}
|
|
207574
|
+
return false;
|
|
207524
207575
|
}
|
|
207525
207576
|
function findHardcodedSecrets(hcl2) {
|
|
207577
|
+
return findInLines(hcl2, null);
|
|
207578
|
+
}
|
|
207579
|
+
function findTfvarsSecrets(hcl2, path15) {
|
|
207580
|
+
return findInLines(hcl2, `tfvars input ${path15}`);
|
|
207581
|
+
}
|
|
207582
|
+
function findInLines(hcl2, fileHolder) {
|
|
207526
207583
|
const out = [];
|
|
207527
|
-
let holder =
|
|
207584
|
+
let holder = fileHolder;
|
|
207528
207585
|
let heredoc = null;
|
|
207529
207586
|
let inComment = false;
|
|
207530
207587
|
const lines = hcl2.split("\n");
|
|
@@ -207544,12 +207601,28 @@ function findHardcodedSecrets(hcl2) {
|
|
|
207544
207601
|
continue;
|
|
207545
207602
|
}
|
|
207546
207603
|
if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
|
|
207547
|
-
|
|
207604
|
+
if (opensBlockComment(line)) inComment = true;
|
|
207605
|
+
const header = fileHolder === null ? HEADER.exec(line) : null;
|
|
207548
207606
|
if (header) {
|
|
207549
207607
|
holder = `${header[1]}${header[2].trimEnd() ? ` ${header[2].trim()}` : ""}`;
|
|
207608
|
+
const rest = header[3];
|
|
207609
|
+
const opens2 = HEREDOC.exec(rest);
|
|
207610
|
+
if (opens2) heredoc = opens2[1];
|
|
207611
|
+
if (!holder.startsWith("output ") && !holder.startsWith("terraform")) {
|
|
207612
|
+
for (const pair of rest.matchAll(INLINE_PAIR)) {
|
|
207613
|
+
const key = pair[1] ?? pair[2];
|
|
207614
|
+
const isVariable = holder.startsWith("variable ");
|
|
207615
|
+
if (isVariable && key !== "default") continue;
|
|
207616
|
+
const name2 = isVariable ? variableName(holder) : key;
|
|
207617
|
+
if (isSecretName(name2) && literalValue(name2, pair[3])) {
|
|
207618
|
+
out.push({ name: name2, holder, line: i + 1 });
|
|
207619
|
+
}
|
|
207620
|
+
}
|
|
207621
|
+
}
|
|
207622
|
+
if (closesOnLine(rest)) holder = null;
|
|
207550
207623
|
continue;
|
|
207551
207624
|
}
|
|
207552
|
-
if (line.startsWith("}")) {
|
|
207625
|
+
if (fileHolder === null && line.startsWith("}")) {
|
|
207553
207626
|
holder = null;
|
|
207554
207627
|
continue;
|
|
207555
207628
|
}
|
|
@@ -207561,30 +207634,34 @@ function findHardcodedSecrets(hcl2) {
|
|
|
207561
207634
|
const name = m[1] ?? m[2];
|
|
207562
207635
|
const value2 = m[3];
|
|
207563
207636
|
if (holder.startsWith("variable ")) {
|
|
207564
|
-
const variable =
|
|
207565
|
-
if (name === "default" && isSecretName(variable) && literalValue(value2)) {
|
|
207637
|
+
const variable = variableName(holder);
|
|
207638
|
+
if (name === "default" && isSecretName(variable) && literalValue(variable, value2)) {
|
|
207566
207639
|
out.push({ name: variable, holder, line: i + 1 });
|
|
207567
207640
|
}
|
|
207568
207641
|
continue;
|
|
207569
207642
|
}
|
|
207570
207643
|
if (holder.startsWith("output ") || holder.startsWith("terraform")) continue;
|
|
207571
|
-
if (isSecretName(name) && literalValue(value2)) {
|
|
207644
|
+
if (isSecretName(name) && literalValue(name, value2)) {
|
|
207572
207645
|
out.push({ name, holder, line: i + 1 });
|
|
207573
207646
|
continue;
|
|
207574
207647
|
}
|
|
207575
207648
|
for (const pair of value2.matchAll(INLINE_PAIR)) {
|
|
207576
207649
|
const key = pair[1] ?? pair[2];
|
|
207577
|
-
if (isSecretName(key) && literalValue(pair[3])) {
|
|
207650
|
+
if (isSecretName(key) && literalValue(key, pair[3])) {
|
|
207578
207651
|
out.push({ name: key, holder, line: i + 1 });
|
|
207579
207652
|
}
|
|
207580
207653
|
}
|
|
207581
207654
|
}
|
|
207582
207655
|
return out;
|
|
207583
207656
|
}
|
|
207584
|
-
function scanHardcodedSecrets(files) {
|
|
207657
|
+
function scanHardcodedSecrets(files, tfvarsFiles = []) {
|
|
207585
207658
|
const concerns = [];
|
|
207586
|
-
|
|
207587
|
-
|
|
207659
|
+
const hits = [
|
|
207660
|
+
...files.map((f) => ({ path: f.path, found: findHardcodedSecrets(f.hcl) })),
|
|
207661
|
+
...tfvarsFiles.map((f) => ({ path: f.path, found: findTfvarsSecrets(f.hcl, f.path) }))
|
|
207662
|
+
];
|
|
207663
|
+
for (const { path: path15, found } of hits) {
|
|
207664
|
+
for (const hit of found) {
|
|
207588
207665
|
concerns.push(
|
|
207589
207666
|
buildConcern({
|
|
207590
207667
|
source: NATIVE_SOURCE,
|
|
@@ -209587,7 +209664,8 @@ var NATIVE_RULE_SCANNERS = [
|
|
|
209587
209664
|
name: "hardcoded-secrets",
|
|
209588
209665
|
rules: [HARDCODED_SECRET_ARGUMENT],
|
|
209589
209666
|
// Per file: the argument's name and its literal are both on the line itself.
|
|
209590
|
-
|
|
209667
|
+
// The only rule that reads `.tfvars`: an input file holds values, not config.
|
|
209668
|
+
scan: (ctx) => ({ concerns: scanHardcodedSecrets(ctx.files, ctx.tfvarsFiles) })
|
|
209591
209669
|
},
|
|
209592
209670
|
{
|
|
209593
209671
|
name: "write-only",
|
|
@@ -209672,6 +209750,7 @@ function createRuleContext(input2) {
|
|
|
209672
209750
|
cwd: input2.cwd,
|
|
209673
209751
|
files: input2.files,
|
|
209674
209752
|
tofuFiles: input2.tofuFiles ?? [],
|
|
209753
|
+
tfvarsFiles: input2.tfvarsFiles ?? [],
|
|
209675
209754
|
truncated: input2.truncated ?? null,
|
|
209676
209755
|
repeatedLiteralThreshold: input2.repeatedLiteralThreshold
|
|
209677
209756
|
};
|
|
@@ -209723,6 +209802,9 @@ function readTfTree(cwd, limits) {
|
|
|
209723
209802
|
function readTofuTree(cwd, limits) {
|
|
209724
209803
|
return readTree(cwd, ".tofu", limits);
|
|
209725
209804
|
}
|
|
209805
|
+
function readTfvarsTree(cwd, limits) {
|
|
209806
|
+
return readTree(cwd, ".tfvars", limits);
|
|
209807
|
+
}
|
|
209726
209808
|
var OVERSIZE_NAMED = 5;
|
|
209727
209809
|
function describeOversize(paths) {
|
|
209728
209810
|
const named = paths.slice(0, OVERSIZE_NAMED).join(", ");
|
|
@@ -209751,6 +209833,9 @@ function scanNative(cwd, opts = {}) {
|
|
|
209751
209833
|
// walk over a suffix most repos do not use at all, and threading it through
|
|
209752
209834
|
// the context keeps the scanners uniform.
|
|
209753
209835
|
tofuFiles: readTofuTree(cwd).files,
|
|
209836
|
+
// likewise only for the hardcoded-secret rule: a value kept out of the `.tf`
|
|
209837
|
+
// source is most often committed here instead
|
|
209838
|
+
tfvarsFiles: readTfvarsTree(cwd).files,
|
|
209754
209839
|
truncated: tree.truncated,
|
|
209755
209840
|
repeatedLiteralThreshold: opts.repeatedLiteralThreshold ?? DEFAULT_REPEAT_THRESHOLD
|
|
209756
209841
|
});
|
|
@@ -271571,7 +271656,7 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
271571
271656
|
}
|
|
271572
271657
|
};
|
|
271573
271658
|
|
|
271574
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
271659
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.7/node_modules/@infraweaver-io/models/dist/catalog.js
|
|
271575
271660
|
function provider(config3) {
|
|
271576
271661
|
return config3;
|
|
271577
271662
|
}
|
|
@@ -272932,12 +273017,47 @@ function readTfDir(cwd, dir) {
|
|
|
272932
273017
|
}
|
|
272933
273018
|
|
|
272934
273019
|
// src/mcp/terraform/refactorScope.ts
|
|
272935
|
-
import {
|
|
273020
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync5 } from "node:fs";
|
|
272936
273021
|
import { join as join22 } from "node:path";
|
|
272937
273022
|
function isExamplePath(path15) {
|
|
272938
|
-
return /(?:^|\/)examples
|
|
273023
|
+
return /(?:^|\/)examples\/|^example\//i.test(path15.replace(/\\/g, "/"));
|
|
273024
|
+
}
|
|
273025
|
+
var PROVIDER_OR_BACKEND = new RegExp(
|
|
273026
|
+
String.raw`(?:^|\n)\s*(?:provider|backend)\s+${BLOCK_LABEL}\s*\{`
|
|
273027
|
+
);
|
|
273028
|
+
var CLOUD_BLOCK = /(?:^|\n)\s*cloud\s*\{/;
|
|
273029
|
+
function configuresDeployment(name, text) {
|
|
273030
|
+
if (name.endsWith(".tf.json")) {
|
|
273031
|
+
let doc;
|
|
273032
|
+
try {
|
|
273033
|
+
doc = JSON.parse(text);
|
|
273034
|
+
} catch {
|
|
273035
|
+
return false;
|
|
273036
|
+
}
|
|
273037
|
+
if (typeof doc !== "object" || doc === null) return false;
|
|
273038
|
+
const { provider: provider2, terraform } = doc;
|
|
273039
|
+
if (provider2 !== void 0) return true;
|
|
273040
|
+
return [terraform].flat().some((t) => typeof t === "object" && t !== null && ("backend" in t || "cloud" in t));
|
|
273041
|
+
}
|
|
273042
|
+
if (PROVIDER_OR_BACKEND.test(text)) return true;
|
|
273043
|
+
return parseBlocks(text, ["terraform"]).some((b) => CLOUD_BLOCK.test(b.body));
|
|
273044
|
+
}
|
|
273045
|
+
var isTerraformFile2 = (name) => name.endsWith(".tf") || name.endsWith(".tf.json");
|
|
273046
|
+
function deploysFrom(dir) {
|
|
273047
|
+
let names;
|
|
273048
|
+
try {
|
|
273049
|
+
names = readdirSync6(dir).filter(isTerraformFile2);
|
|
273050
|
+
} catch {
|
|
273051
|
+
return false;
|
|
273052
|
+
}
|
|
273053
|
+
return names.some((name) => {
|
|
273054
|
+
try {
|
|
273055
|
+
return configuresDeployment(name, readFileSync12(join22(dir, name), "utf8"));
|
|
273056
|
+
} catch {
|
|
273057
|
+
return false;
|
|
273058
|
+
}
|
|
273059
|
+
});
|
|
272939
273060
|
}
|
|
272940
|
-
var PROVIDER_OR_BACKEND = /^\s*(?:provider\s+"|backend\s+")/m;
|
|
272941
273061
|
function holdsTerraform(dir, depth) {
|
|
272942
273062
|
let entries;
|
|
272943
273063
|
try {
|
|
@@ -272955,26 +273075,35 @@ function holdsTerraform(dir, depth) {
|
|
|
272955
273075
|
return false;
|
|
272956
273076
|
}
|
|
272957
273077
|
function libraryRepoReason(cwd) {
|
|
272958
|
-
let
|
|
273078
|
+
let rootEntries;
|
|
272959
273079
|
try {
|
|
272960
|
-
|
|
273080
|
+
rootEntries = readdirSync6(cwd);
|
|
272961
273081
|
} catch {
|
|
272962
273082
|
return null;
|
|
272963
273083
|
}
|
|
272964
|
-
if (
|
|
272965
|
-
|
|
272966
|
-
let text;
|
|
272967
|
-
try {
|
|
272968
|
-
text = readFileSync12(join22(cwd, f), "utf8");
|
|
272969
|
-
} catch {
|
|
272970
|
-
continue;
|
|
272971
|
-
}
|
|
272972
|
-
if (PROVIDER_OR_BACKEND.test(text)) return null;
|
|
272973
|
-
}
|
|
272974
|
-
const examples = ["examples", "example"].find((d) => existsSync6(join22(cwd, d)));
|
|
273084
|
+
if (!rootEntries.some(isTerraformFile2) || deploysFrom(cwd)) return null;
|
|
273085
|
+
const examples = rootEntries.find((d) => /^examples?$/i.test(d));
|
|
272975
273086
|
if (examples === void 0 || !holdsTerraform(join22(cwd, examples), 2)) return null;
|
|
272976
273087
|
return `the repository root configures no provider and no backend, and ships \`${examples}/\`: it is a module other configurations call, so restructuring it is churn for every caller`;
|
|
272977
273088
|
}
|
|
273089
|
+
function libraryScope(cwd) {
|
|
273090
|
+
const reason = libraryRepoReason(cwd);
|
|
273091
|
+
if (reason === null) return () => null;
|
|
273092
|
+
const deploys = /* @__PURE__ */ new Map();
|
|
273093
|
+
return (file2) => {
|
|
273094
|
+
const dirs = file2.replace(/\\/g, "/").split("/").slice(0, -1);
|
|
273095
|
+
for (let i = dirs.length; i > 0; i--) {
|
|
273096
|
+
const dir = dirs.slice(0, i).join("/");
|
|
273097
|
+
let d = deploys.get(dir);
|
|
273098
|
+
if (d === void 0) {
|
|
273099
|
+
d = deploysFrom(join22(cwd, dir));
|
|
273100
|
+
deploys.set(dir, d);
|
|
273101
|
+
}
|
|
273102
|
+
if (d) return null;
|
|
273103
|
+
}
|
|
273104
|
+
return reason;
|
|
273105
|
+
};
|
|
273106
|
+
}
|
|
272978
273107
|
function reportOnly(reason) {
|
|
272979
273108
|
return reason === null ? {} : { report_only: true, report_only_reason: reason };
|
|
272980
273109
|
}
|
|
@@ -273332,12 +273461,12 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
273332
273461
|
parameters: ModuleExtractionCandidatesParams,
|
|
273333
273462
|
execute: execute(async () => {
|
|
273334
273463
|
const cwd = ctx.payload.cwd ?? process.cwd();
|
|
273464
|
+
const library = libraryScope(cwd);
|
|
273335
273465
|
const candidates = findExtractionCandidates(
|
|
273336
273466
|
cwd,
|
|
273337
273467
|
ctx.payload.moduleCatalogue,
|
|
273338
273468
|
ctx.toolState.orgModuleHcl
|
|
273339
|
-
).filter((c) => ctx.payload.refactorExamples || !isExamplePath(c.cluster.file));
|
|
273340
|
-
const library = libraryRepoReason(cwd);
|
|
273469
|
+
).filter((c) => ctx.payload.refactorExamples || !isExamplePath(c.cluster.file)).map((c) => ({ ...reportOnly(library(c.cluster.file)), ...c }));
|
|
273341
273470
|
const matched = candidates.filter((c) => c.candidates.length > 0);
|
|
273342
273471
|
const reasons = {};
|
|
273343
273472
|
for (const c of candidates)
|
|
@@ -273351,7 +273480,6 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
273351
273480
|
`\xBB module_extraction_candidates: ${candidates.length} cluster(s), ${matched.length} with a module match` + (partialInterfaces.length > 0 ? `, ${partialInterfaces.length} partial-interface block(s)` : "")
|
|
273352
273481
|
);
|
|
273353
273482
|
return toolOk({
|
|
273354
|
-
...reportOnly(library),
|
|
273355
273483
|
cluster_count: candidates.length,
|
|
273356
273484
|
matched_count: matched.length,
|
|
273357
273485
|
candidates,
|
|
@@ -273375,7 +273503,7 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
273375
273503
|
}
|
|
273376
273504
|
|
|
273377
273505
|
// src/mcp/moduleTests.ts
|
|
273378
|
-
import { existsSync as
|
|
273506
|
+
import { existsSync as existsSync6, readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
273379
273507
|
import { join as join24 } from "node:path";
|
|
273380
273508
|
var MODULE_META_ARGS = /* @__PURE__ */ new Set([
|
|
273381
273509
|
"source",
|
|
@@ -273496,7 +273624,7 @@ function resolveSourceDir(fromDir, source) {
|
|
|
273496
273624
|
}
|
|
273497
273625
|
function isDir(p) {
|
|
273498
273626
|
try {
|
|
273499
|
-
return
|
|
273627
|
+
return existsSync6(p) && statSync6(p).isDirectory();
|
|
273500
273628
|
} catch {
|
|
273501
273629
|
return false;
|
|
273502
273630
|
}
|
|
@@ -273630,7 +273758,7 @@ function TerraformModuleTestsTool(ctx) {
|
|
|
273630
273758
|
}
|
|
273631
273759
|
|
|
273632
273760
|
// src/mcp/terraform/tools/advise.ts
|
|
273633
|
-
import { existsSync as
|
|
273761
|
+
import { existsSync as existsSync7, readFileSync as readFileSync16 } from "node:fs";
|
|
273634
273762
|
import { join as join26 } from "node:path";
|
|
273635
273763
|
|
|
273636
273764
|
// src/mcp/terraform/fixMemory.ts
|
|
@@ -273789,7 +273917,7 @@ function inlineWaivers(content, file2) {
|
|
|
273789
273917
|
}
|
|
273790
273918
|
function readLearnings(tmpdir6) {
|
|
273791
273919
|
const path15 = join26(tmpdir6, LEARNINGS_FILE);
|
|
273792
|
-
if (!
|
|
273920
|
+
if (!existsSync7(path15)) return [];
|
|
273793
273921
|
try {
|
|
273794
273922
|
return readFileSync16(path15, "utf-8").split("\n");
|
|
273795
273923
|
} catch {
|
|
@@ -275151,6 +275279,9 @@ function ConsolidationCandidatesTool(ctx) {
|
|
|
275151
275279
|
import { readFileSync as readFileSync18 } from "node:fs";
|
|
275152
275280
|
import { join as join28 } from "node:path";
|
|
275153
275281
|
|
|
275282
|
+
// src/mcp/staleFix.ts
|
|
275283
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
275284
|
+
|
|
275154
275285
|
// node_modules/.pnpm/@infraweaver-io+ci@0.1.8/node_modules/@infraweaver-io/ci/dist/progressComment.js
|
|
275155
275286
|
function parseProgressComment(raw2) {
|
|
275156
275287
|
if (!raw2?.id)
|
|
@@ -276206,7 +276337,31 @@ function groupIdFromBranch(branch) {
|
|
|
276206
276337
|
}
|
|
276207
276338
|
function remediationBranchSuffix(base, defaultBranch) {
|
|
276208
276339
|
if (!base || !defaultBranch || base === defaultBranch) return "";
|
|
276209
|
-
|
|
276340
|
+
const slug2 = base.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
276341
|
+
if (slug2 === base) return `--${slug2}`;
|
|
276342
|
+
return `--${slug2}-${createHash6("sha1").update(base).digest("hex").slice(0, 8)}`;
|
|
276343
|
+
}
|
|
276344
|
+
function remediationBranchFor(id, base, defaultBranch, open3) {
|
|
276345
|
+
const computed = `remediate/${id}${remediationBranchSuffix(base, defaultBranch)}`;
|
|
276346
|
+
const heads = (open3 ?? []).filter((pr) => pr.base === base && groupIdFromBranch(pr.head) === id).map((pr) => pr.head).filter((head) => !(open3 ?? []).some((pr) => pr.head === head && pr.base !== base));
|
|
276347
|
+
return heads.includes(computed) ? computed : heads[0] ?? computed;
|
|
276348
|
+
}
|
|
276349
|
+
async function listOpenRemediationPrs(ctx) {
|
|
276350
|
+
const { octokit, repo } = ctx;
|
|
276351
|
+
if (ctx.toolState?.selectedMode !== "Remediate") return [];
|
|
276352
|
+
if (!octokit || !repo?.owner || !repo.name) return [];
|
|
276353
|
+
try {
|
|
276354
|
+
const open3 = await octokit.paginate(octokit.rest.pulls.list, {
|
|
276355
|
+
owner: repo.owner,
|
|
276356
|
+
repo: repo.name,
|
|
276357
|
+
state: "open",
|
|
276358
|
+
per_page: 100
|
|
276359
|
+
});
|
|
276360
|
+
return open3.filter((pr) => pr.head.repo?.id === pr.base.repo.id && groupIdFromBranch(pr.head.ref)).map((pr) => ({ head: pr.head.ref, base: pr.base.ref }));
|
|
276361
|
+
} catch (e) {
|
|
276362
|
+
log2.warning(`\xBB could not list open remediation PRs: ${errorMessage(e)}`);
|
|
276363
|
+
return [];
|
|
276364
|
+
}
|
|
276210
276365
|
}
|
|
276211
276366
|
function matchesBotIdentity(identity) {
|
|
276212
276367
|
const normalized = identity.replace(/\[bot\]$/i, "").toLowerCase();
|
|
@@ -276919,15 +277074,17 @@ function siteOf(c, read3) {
|
|
|
276919
277074
|
const text = read3(c.location.file);
|
|
276920
277075
|
return text === null ? null : resourceAddressAt(text, c.location.line);
|
|
276921
277076
|
}
|
|
276922
|
-
function recordConcernSites(session, concerns, cwd) {
|
|
277077
|
+
function recordConcernSites(session, concerns, cwd, linesMatchTree) {
|
|
276923
277078
|
const read3 = fileReader(cwd);
|
|
276924
277079
|
session.siteById ??= /* @__PURE__ */ new Map();
|
|
276925
277080
|
session.firstSitesByKey ??= /* @__PURE__ */ new Map();
|
|
276926
277081
|
const first = session.firstSitesByKey;
|
|
276927
277082
|
const fresh = /* @__PURE__ */ new Map();
|
|
276928
277083
|
for (const c of concerns) {
|
|
276929
|
-
const site = siteOf(c, read3);
|
|
276930
|
-
session.siteById.
|
|
277084
|
+
const site = linesMatchTree ? siteOf(c, read3) : null;
|
|
277085
|
+
const prior = session.siteById.get(c.id);
|
|
277086
|
+
session.siteById.set(c.id, prior === void 0 || prior === site ? site : null);
|
|
277087
|
+
if (!linesMatchTree) continue;
|
|
276931
277088
|
const key = concernKeyOf(c);
|
|
276932
277089
|
if (first.has(key)) continue;
|
|
276933
277090
|
const sites = fresh.get(key) ?? /* @__PURE__ */ new Set();
|
|
@@ -276983,7 +277140,7 @@ function buildConcernResult(ctx, rawConcerns, opts) {
|
|
|
276983
277140
|
fullBaseline.map((c) => concernKeyOf(c))
|
|
276984
277141
|
);
|
|
276985
277142
|
recordConcernKeys(session, fullBaseline);
|
|
276986
|
-
recordConcernSites(session, fullBaseline, opts.cwd);
|
|
277143
|
+
recordConcernSites(session, fullBaseline, opts.cwd, opts.linesMatchTree ?? false);
|
|
276987
277144
|
session.baselineFmtDriftFiles = union3(
|
|
276988
277145
|
session.baselineFmtDriftFiles,
|
|
276989
277146
|
fullBaseline.filter((c) => c.source === "terraform-fmt").map((c) => c.location.file)
|
|
@@ -276998,13 +277155,13 @@ function buildConcernResult(ctx, rawConcerns, opts) {
|
|
|
276998
277155
|
active,
|
|
276999
277156
|
opts.autonomyThreshold
|
|
277000
277157
|
);
|
|
277001
|
-
const
|
|
277002
|
-
|
|
277003
|
-
|
|
277004
|
-
);
|
|
277005
|
-
const groups2 = (opts.priority ? prioritizeGroups(annotatedGroups, active, opts.priority) : annotatedGroups).map((g) => ({ ...g, branch: `remediate/${g.id}${suffix2}` }));
|
|
277158
|
+
const base = remediationBaseOf(ctx);
|
|
277159
|
+
const defaultBranch = ctx.repo?.data.default_branch?.trim() || void 0;
|
|
277160
|
+
const branchFor = (id) => remediationBranchFor(id, base, defaultBranch, opts.openRemediationPrs);
|
|
277161
|
+
const groups2 = (opts.priority ? prioritizeGroups(annotatedGroups, active, opts.priority) : annotatedGroups).map((g) => ({ ...g, branch: branchFor(g.id) }));
|
|
277006
277162
|
const planned = planBatches(groups2);
|
|
277007
|
-
const
|
|
277163
|
+
const batchId = planned.batch_branch ? groupIdFromBranch(planned.batch_branch) : null;
|
|
277164
|
+
const batchPlan = batchId ? { ...planned, batch_branch: branchFor(batchId) } : planned;
|
|
277008
277165
|
const bySeverity = {};
|
|
277009
277166
|
for (const c of active) bySeverity[c.severity] = (bySeverity[c.severity] ?? 0) + 1;
|
|
277010
277167
|
return {
|
|
@@ -277521,7 +277678,8 @@ function DetectPlanDriftTool(ctx) {
|
|
|
277521
277678
|
autonomyThreshold,
|
|
277522
277679
|
// drift is a narrow concern source — join any prior scan baseline so a
|
|
277523
277680
|
// later ✗→✓ verify doesn't read the whole workspace as regressions.
|
|
277524
|
-
baselineMode: "merge"
|
|
277681
|
+
baselineMode: "merge",
|
|
277682
|
+
openRemediationPrs: await listOpenRemediationPrs(ctx)
|
|
277525
277683
|
});
|
|
277526
277684
|
log2.info(
|
|
277527
277685
|
`\xBB detect_plan_drift: ${active.length} drifted resource(s) \u2265 ${threshold} from ${planPath}` + (noiseFiltered.length ? `, ${noiseFiltered.length} noise-filtered` : "") + (unlocated.length ? `, ${unlocated.length} unlocated` : "") + (statefulForgotten.length ? `, ${statefulForgotten.length} STATEFUL forget(s)` : "") + (suppressed.length ? ` (${suppressed.length} suppressed)` : "")
|
|
@@ -277592,13 +277750,13 @@ import { readFileSync as readFileSync19, rmSync as rmSync2 } from "node:fs";
|
|
|
277592
277750
|
import { join as join29 } from "node:path";
|
|
277593
277751
|
|
|
277594
277752
|
// src/mcp/terraform/emittedArtefacts.ts
|
|
277595
|
-
import { createHash as
|
|
277753
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
277596
277754
|
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
277597
277755
|
import { resolve as resolve5 } from "node:path";
|
|
277598
277756
|
var ARTEFACT_DIR = "compliance/";
|
|
277599
277757
|
function gitBlobId(bytes) {
|
|
277600
277758
|
const content = Buffer.from(bytes, "utf8");
|
|
277601
|
-
return
|
|
277759
|
+
return createHash7("sha1").update(`blob ${content.length}\0`).update(content).digest("hex");
|
|
277602
277760
|
}
|
|
277603
277761
|
function writeEmittedArtefact(toolState, path15, bytes) {
|
|
277604
277762
|
writeFileSync3(path15, bytes);
|
|
@@ -278795,7 +278953,7 @@ function buildSuppressionSummary(waivers) {
|
|
|
278795
278953
|
}
|
|
278796
278954
|
|
|
278797
278955
|
// node_modules/.pnpm/@infraweaver-io+attest@0.1.7_supports-color@7.2.0/node_modules/@infraweaver-io/attest/dist/signing.js
|
|
278798
|
-
import { createHash as
|
|
278956
|
+
import { createHash as createHash8, createPrivateKey, createPublicKey, sign as cryptoSign, verify as cryptoVerify } from "node:crypto";
|
|
278799
278957
|
var SIGNATURE_ALGORITHM = "Ed25519";
|
|
278800
278958
|
var DEFAULT_KEY_LABEL = "the signing key";
|
|
278801
278959
|
function loadSigningKey(raw2, options) {
|
|
@@ -278822,7 +278980,7 @@ function loadSigningKey(raw2, options) {
|
|
|
278822
278980
|
return {
|
|
278823
278981
|
privateKey,
|
|
278824
278982
|
publicKeyPem,
|
|
278825
|
-
fingerprint:
|
|
278983
|
+
fingerprint: createHash8("sha256").update(spkiDer).digest("hex")
|
|
278826
278984
|
};
|
|
278827
278985
|
}
|
|
278828
278986
|
function signBytes(bytes, key) {
|
|
@@ -278843,7 +279001,7 @@ function publicKeyFingerprint(publicKeyPem) {
|
|
|
278843
279001
|
type: "spki",
|
|
278844
279002
|
format: "der"
|
|
278845
279003
|
});
|
|
278846
|
-
return
|
|
279004
|
+
return createHash8("sha256").update(der).digest("hex");
|
|
278847
279005
|
}
|
|
278848
279006
|
function signatureMetadata(key, sigFile) {
|
|
278849
279007
|
return {
|
|
@@ -279073,7 +279231,7 @@ function buildEvidenceBundle(args2) {
|
|
|
279073
279231
|
// node_modules/.pnpm/@infraweaver-io+attest@0.1.7_supports-color@7.2.0/node_modules/@infraweaver-io/attest/dist/keyless.js
|
|
279074
279232
|
var import_bundle = __toESM(require_dist6(), 1);
|
|
279075
279233
|
var import_sign = __toESM(require_dist9(), 1);
|
|
279076
|
-
import { createHash as
|
|
279234
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
279077
279235
|
|
|
279078
279236
|
// node_modules/.pnpm/@infraweaver-io+attest@0.1.7_supports-color@7.2.0/node_modules/@infraweaver-io/attest/dist/keylessOidc.js
|
|
279079
279237
|
var SIGSTORE_OIDC_AUDIENCE = "sigstore";
|
|
@@ -279102,7 +279260,7 @@ function keylessSigningPossible(env2, envName) {
|
|
|
279102
279260
|
}
|
|
279103
279261
|
function buildInTotoStatement(args2) {
|
|
279104
279262
|
const data = typeof args2.artifactBytes === "string" ? Buffer.from(args2.artifactBytes, "utf8") : args2.artifactBytes;
|
|
279105
|
-
const sha2562 =
|
|
279263
|
+
const sha2562 = createHash9("sha256").update(data).digest("hex");
|
|
279106
279264
|
return {
|
|
279107
279265
|
_type: "https://in-toto.io/Statement/v1",
|
|
279108
279266
|
subject: [{ name: args2.subjectName, digest: { sha256: sha2562 } }],
|
|
@@ -279312,12 +279470,12 @@ import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
|
279312
279470
|
import { dirname as dirname6 } from "node:path";
|
|
279313
279471
|
|
|
279314
279472
|
// src/mcp/terraform/oscal.ts
|
|
279315
|
-
import { createHash as
|
|
279473
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
279316
279474
|
var OSCAL_VERSION = "1.1.2";
|
|
279317
279475
|
var INDICATIVE_REMARK = "Indicative, crosswalk-derived assessment from a deterministic starter rule-pack \u2014 NOT a formal OSCAL assessment against an authored assessment plan. Statuses are code-verified only; absence of a finding is not proof of compliance.";
|
|
279318
279476
|
var NS = "https://infraweaver.io/ns/oscal";
|
|
279319
279477
|
function deterministicUuid(seed) {
|
|
279320
|
-
const hex4 =
|
|
279478
|
+
const hex4 = createHash10("sha256").update(seed).digest("hex").slice(0, 32).split("");
|
|
279321
279479
|
hex4[12] = "5";
|
|
279322
279480
|
hex4[16] = (Number.parseInt(hex4[16] ?? "0", 16) & 3 | 8).toString(16);
|
|
279323
279481
|
const u = hex4.join("");
|
|
@@ -279456,7 +279614,7 @@ function TerraformEmitOscalTool(ctx) {
|
|
|
279456
279614
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
279457
279615
|
|
|
279458
279616
|
// src/mcp/terraform/lineage.ts
|
|
279459
|
-
import { createHash as
|
|
279617
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
279460
279618
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
279461
279619
|
import { join as join31 } from "node:path";
|
|
279462
279620
|
function repoFileReader(cwd) {
|
|
@@ -279469,7 +279627,7 @@ function repoFileReader(cwd) {
|
|
|
279469
279627
|
};
|
|
279470
279628
|
}
|
|
279471
279629
|
function lineageKeyOf(c, address) {
|
|
279472
|
-
return
|
|
279630
|
+
return createHash11("sha1").update(`${c.source}|${bareRuleOf(c.source, c.rule_id)}|${c.location.file}|${address ?? ""}`).digest("hex").slice(0, 16);
|
|
279473
279631
|
}
|
|
279474
279632
|
function lineageResolver(read3) {
|
|
279475
279633
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -280511,16 +280669,32 @@ function uncoveredInstanceKeys(p) {
|
|
|
280511
280669
|
if (expected === null) return null;
|
|
280512
280670
|
const kind = level[0].kind;
|
|
280513
280671
|
const written = (k) => kind === "count" ? k : `"${k}"`;
|
|
280672
|
+
const fromAt = level[0].at;
|
|
280673
|
+
const toAt = p.after.multiplicity[0]?.at;
|
|
280674
|
+
if (fromAt === void 0 || toAt === void 0) return null;
|
|
280514
280675
|
const declared2 = /* @__PURE__ */ new Set();
|
|
280515
280676
|
for (const blk of p.blocks) {
|
|
280677
|
+
if (stripIndices(normaliseAddress(blk.from)) !== p.before.address || stripIndices(normaliseAddress(blk.to)) !== p.after.address)
|
|
280678
|
+
return null;
|
|
280516
280679
|
const from = instanceKeysOf(blk.from);
|
|
280517
280680
|
const to = instanceKeysOf(blk.to);
|
|
280518
280681
|
if (from.length > 1 || to.length > 1) return null;
|
|
280519
280682
|
if (from.length === 0 && to.length === 0) return null;
|
|
280520
|
-
if (from.length
|
|
280683
|
+
if (from.length !== 1 || to.length !== 1 || from[0] !== to[0]) continue;
|
|
280684
|
+
const fromPlaced = keyAtLevel(blk.from, fromAt);
|
|
280685
|
+
const toPlaced = keyAtLevel(blk.to, toAt);
|
|
280686
|
+
if (fromPlaced === false || toPlaced === false) continue;
|
|
280687
|
+
if (fromPlaced === null || toPlaced === null) return null;
|
|
280688
|
+
declared2.add(from[0]);
|
|
280521
280689
|
}
|
|
280522
280690
|
return expected.filter((k) => !declared2.has(written(k)));
|
|
280523
280691
|
}
|
|
280692
|
+
function keyAtLevel(address, at) {
|
|
280693
|
+
const onResource = instanceKeysOf(stripModuleHops(normaliseAddress(address))).length > 0;
|
|
280694
|
+
if (at === "resource") return onResource;
|
|
280695
|
+
if (onResource) return false;
|
|
280696
|
+
return moduleHops(address).length === 1 ? true : null;
|
|
280697
|
+
}
|
|
280524
280698
|
function computeEquivalence(before, after, movedBlocks, ambiguous = [], planPairs, opts = {}) {
|
|
280525
280699
|
const reasons = [];
|
|
280526
280700
|
const corr = correspond(before, after, movedBlocks);
|
|
@@ -282447,7 +282621,8 @@ function IngestExternalFindingsTool(ctx) {
|
|
|
282447
282621
|
// an external feed is a narrow concern source — join any prior scan
|
|
282448
282622
|
// baseline so a later ✗→✓ verify doesn't read the workspace as regressions.
|
|
282449
282623
|
baselineMode: "merge",
|
|
282450
|
-
seedCaps: seedCapsFrom(input2)
|
|
282624
|
+
seedCaps: seedCapsFrom(input2),
|
|
282625
|
+
openRemediationPrs: await listOpenRemediationPrs(ctx)
|
|
282451
282626
|
});
|
|
282452
282627
|
const cappedBlock = cappedSummary(capped);
|
|
282453
282628
|
log2.info(
|
|
@@ -282917,15 +283092,13 @@ function NormalizationCandidatesTool(ctx) {
|
|
|
282917
283092
|
parameters: NormalizationCandidatesParams,
|
|
282918
283093
|
execute: execute(async () => {
|
|
282919
283094
|
const cwd = ctx.payload.cwd ?? process.cwd();
|
|
282920
|
-
const
|
|
282921
|
-
|
|
282922
|
-
);
|
|
283095
|
+
const library = libraryScope(cwd);
|
|
283096
|
+
const files = findNormalizationCandidates(cwd).filter((f) => ctx.payload.refactorExamples || !isExamplePath(f.file)).map((f) => ({ ...reportOnly(library(f.file)), ...f }));
|
|
282923
283097
|
const total = files.reduce((n, f) => n + f.count, 0);
|
|
282924
283098
|
log2.info(
|
|
282925
283099
|
`\xBB terraform_normalization_candidates: ${total} site(s) across ${files.length} file(s)`
|
|
282926
283100
|
);
|
|
282927
283101
|
return toolOk({
|
|
282928
|
-
...reportOnly(libraryRepoReason(cwd)),
|
|
282929
283102
|
file_count: files.length,
|
|
282930
283103
|
total,
|
|
282931
283104
|
files,
|
|
@@ -283460,7 +283633,8 @@ function ReadFindingsTool(ctx) {
|
|
|
283460
283633
|
minRank,
|
|
283461
283634
|
grouping,
|
|
283462
283635
|
autonomyThreshold,
|
|
283463
|
-
seedCaps: seedCapsFrom(input2)
|
|
283636
|
+
seedCaps: seedCapsFrom(input2),
|
|
283637
|
+
openRemediationPrs: await listOpenRemediationPrs(ctx)
|
|
283464
283638
|
});
|
|
283465
283639
|
const cappedBlock = cappedSummary(capped);
|
|
283466
283640
|
log2.info(
|
|
@@ -283639,23 +283813,54 @@ function computeScanDelta(input2) {
|
|
|
283639
283813
|
};
|
|
283640
283814
|
}
|
|
283641
283815
|
function markPossibleRenames(introduced, resolved, headAddresses) {
|
|
283642
|
-
const
|
|
283643
|
-
|
|
283644
|
-
|
|
283645
|
-
|
|
283816
|
+
const byResource = (findings) => {
|
|
283817
|
+
const out = /* @__PURE__ */ new Map();
|
|
283818
|
+
for (const f of [...findings].sort((a, b) => byLocation(a.concern, b.concern))) {
|
|
283819
|
+
if (f.address === null) continue;
|
|
283820
|
+
const dir = dirOf3(f.concern.location.file);
|
|
283821
|
+
const key = `${dir}|${f.address}`;
|
|
283822
|
+
const entry4 = out.get(key);
|
|
283823
|
+
if (entry4) entry4.findings.push(f);
|
|
283824
|
+
else out.set(key, { dir, address: f.address, findings: [f] });
|
|
283825
|
+
}
|
|
283826
|
+
return [...out.values()];
|
|
283827
|
+
};
|
|
283828
|
+
const vanished = byResource(resolved).filter((r) => !headAddresses(r.dir).has(r.address));
|
|
283829
|
+
const appeared = byResource(introduced);
|
|
283830
|
+
const shared = (from, to) => {
|
|
283831
|
+
const left = /* @__PURE__ */ new Map();
|
|
283832
|
+
for (const f of from.findings)
|
|
283833
|
+
left.set(ruleKey(f.concern), (left.get(ruleKey(f.concern)) ?? 0) + 1);
|
|
283834
|
+
return to.findings.filter((f) => {
|
|
283835
|
+
const n = left.get(ruleKey(f.concern)) ?? 0;
|
|
283836
|
+
if (n === 0) return false;
|
|
283837
|
+
left.set(ruleKey(f.concern), n - 1);
|
|
283838
|
+
return true;
|
|
283839
|
+
});
|
|
283840
|
+
};
|
|
283841
|
+
const order2 = (a, b) => a.dir.localeCompare(b.dir) || a.address.localeCompare(b.address);
|
|
283842
|
+
const candidates = vanished.flatMap(
|
|
283843
|
+
(from) => appeared.filter(
|
|
283844
|
+
(to) => (
|
|
283845
|
+
// a `moved` block cannot change a resource's type
|
|
283846
|
+
typeOf(from.address) === typeOf(to.address) && // renamed in place, or moved elsewhere under its own name
|
|
283847
|
+
from.dir === to.dir !== (from.address === to.address)
|
|
283848
|
+
)
|
|
283849
|
+
).map((to) => ({ from, to, findings: shared(from, to) })).filter((p) => p.findings.length > 0)
|
|
283850
|
+
);
|
|
283851
|
+
candidates.sort(
|
|
283852
|
+
(a, b) => b.findings.length - a.findings.length || order2(a.from, b.from) || order2(a.to, b.to)
|
|
283853
|
+
);
|
|
283646
283854
|
const used = /* @__PURE__ */ new Set();
|
|
283647
|
-
for (const
|
|
283648
|
-
if (
|
|
283649
|
-
|
|
283650
|
-
|
|
283651
|
-
|
|
283652
|
-
|
|
283653
|
-
|
|
283654
|
-
|
|
283655
|
-
|
|
283656
|
-
used.add(match5);
|
|
283657
|
-
finding.possiblyPreexisting = true;
|
|
283658
|
-
finding.renamedFrom = { address: match5.address, file: match5.concern.location.file };
|
|
283855
|
+
for (const { from, to, findings } of candidates) {
|
|
283856
|
+
if (used.has(from) || used.has(to)) continue;
|
|
283857
|
+
used.add(from);
|
|
283858
|
+
used.add(to);
|
|
283859
|
+
const file2 = from.findings[0]?.concern.location.file;
|
|
283860
|
+
for (const finding of findings) {
|
|
283861
|
+
finding.possiblyPreexisting = true;
|
|
283862
|
+
finding.renamedFrom = { address: from.address, file: file2 };
|
|
283863
|
+
}
|
|
283659
283864
|
}
|
|
283660
283865
|
}
|
|
283661
283866
|
function typeOf(address) {
|
|
@@ -283698,7 +283903,8 @@ function deltaVerdict(delta, requiredControls) {
|
|
|
283698
283903
|
return {
|
|
283699
283904
|
pass: gate.pass && required3.length === 0,
|
|
283700
283905
|
gating: gate.gating.length,
|
|
283701
|
-
required: required3.length
|
|
283906
|
+
required: required3.length,
|
|
283907
|
+
blocking: (/* @__PURE__ */ new Set([...gate.gating, ...required3])).size
|
|
283702
283908
|
};
|
|
283703
283909
|
}
|
|
283704
283910
|
function failsGate(finding, requiredControls) {
|
|
@@ -283991,7 +284197,9 @@ async function runScan(ctx, {
|
|
|
283991
284197
|
grouping,
|
|
283992
284198
|
autonomyThreshold,
|
|
283993
284199
|
inScope,
|
|
283994
|
-
priority
|
|
284200
|
+
priority,
|
|
284201
|
+
openRemediationPrs: await listOpenRemediationPrs(ctx),
|
|
284202
|
+
linesMatchTree: true
|
|
283995
284203
|
}
|
|
283996
284204
|
);
|
|
283997
284205
|
const { active, suppressed, groups: groups2, batchPlan, bySeverity: by_severity } = result;
|
|
@@ -284520,8 +284728,8 @@ function TerraformStandardsReportTool(ctx) {
|
|
|
284520
284728
|
}
|
|
284521
284729
|
|
|
284522
284730
|
// src/mcp/terraform/tools/tag.ts
|
|
284523
|
-
import { createHash as
|
|
284524
|
-
import { existsSync as
|
|
284731
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
284732
|
+
import { existsSync as existsSync8, readdirSync as readdirSync8, readFileSync as readFileSync26, realpathSync as realpathSync2, rmSync as rmSync7 } from "node:fs";
|
|
284525
284733
|
import { isAbsolute as isAbsolute3, join as join40, relative as relative5, resolve as resolve6, sep as sep2 } from "node:path";
|
|
284526
284734
|
var TerraformTagParams = type({
|
|
284527
284735
|
tags: type({ "[string]": "string" }).describe(
|
|
@@ -284543,7 +284751,7 @@ var TerraformTagParams = type({
|
|
|
284543
284751
|
var realRunners3 = { run };
|
|
284544
284752
|
function sha(path15) {
|
|
284545
284753
|
try {
|
|
284546
|
-
return
|
|
284754
|
+
return createHash12("sha256").update(readFileSync26(path15)).digest("hex");
|
|
284547
284755
|
} catch {
|
|
284548
284756
|
return null;
|
|
284549
284757
|
}
|
|
@@ -284557,7 +284765,7 @@ function toPosix(p) {
|
|
|
284557
284765
|
function terratagFiles(dir) {
|
|
284558
284766
|
const dirs = [dir];
|
|
284559
284767
|
const manifest = join40(dir, ".terraform", "modules", "modules.json");
|
|
284560
|
-
if (
|
|
284768
|
+
if (existsSync8(manifest)) {
|
|
284561
284769
|
let modules;
|
|
284562
284770
|
try {
|
|
284563
284771
|
modules = JSON.parse(readFileSync26(manifest, "utf8")).Modules;
|
|
@@ -284600,12 +284808,12 @@ function runTag(ctx, params, runners = realRunners3) {
|
|
|
284600
284808
|
return rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute3(rel);
|
|
284601
284809
|
};
|
|
284602
284810
|
const requested = resolve6(cwd, params.dir ?? ".");
|
|
284603
|
-
const target =
|
|
284811
|
+
const target = existsSync8(requested) ? realpathSync2(requested) : requested;
|
|
284604
284812
|
if (!inside(target)) {
|
|
284605
284813
|
throw new Error(`terraform_tag: \`dir\` must stay inside the workspace (got ${params.dir})`);
|
|
284606
284814
|
}
|
|
284607
284815
|
const relDir = toPosix(relative5(cwd, target)) || ".";
|
|
284608
|
-
if (!
|
|
284816
|
+
if (!existsSync8(target)) {
|
|
284609
284817
|
return toolSkip("no_terraform_files", `no .tf files under ${relDir}`);
|
|
284610
284818
|
}
|
|
284611
284819
|
const absFiles = terratagFiles(target);
|
|
@@ -284643,7 +284851,7 @@ function runTag(ctx, params, runners = realRunners3) {
|
|
|
284643
284851
|
if (params.filter) args2.push(`-filter=${params.filter}`);
|
|
284644
284852
|
if (params.skip) args2.push(`-skip=${params.skip}`);
|
|
284645
284853
|
const before = snapshot2(cwd, files);
|
|
284646
|
-
const priorBackups = new Set(files.filter((f) =>
|
|
284854
|
+
const priorBackups = new Set(files.filter((f) => existsSync8(join40(cwd, `${f}.bak`))));
|
|
284647
284855
|
const r = runners.run("terratag", args2, cwd);
|
|
284648
284856
|
if (r.missing) {
|
|
284649
284857
|
return toolSkip(
|
|
@@ -285066,10 +285274,10 @@ var DispatchRecorder = class {
|
|
|
285066
285274
|
* orchestrator counts as subagent — including a session the labeler could not
|
|
285067
285275
|
* name, because an unnamed session is by definition not the orchestrator's.
|
|
285068
285276
|
*/
|
|
285069
|
-
recordTokens(label,
|
|
285070
|
-
if (!Number.isFinite(
|
|
285071
|
-
if (label === ORCHESTRATOR_LABEL) this.#orchestratorTokens +=
|
|
285072
|
-
else this.#subagentTokens +=
|
|
285277
|
+
recordTokens(label, billableTokens) {
|
|
285278
|
+
if (!Number.isFinite(billableTokens) || billableTokens <= 0) return;
|
|
285279
|
+
if (label === ORCHESTRATOR_LABEL) this.#orchestratorTokens += billableTokens;
|
|
285280
|
+
else this.#subagentTokens += billableTokens;
|
|
285073
285281
|
}
|
|
285074
285282
|
tally() {
|
|
285075
285283
|
return {
|
|
@@ -285239,7 +285447,7 @@ function buildLocalTools(ctx) {
|
|
|
285239
285447
|
];
|
|
285240
285448
|
}
|
|
285241
285449
|
function cliVersion() {
|
|
285242
|
-
const raw2 = "0.3.
|
|
285450
|
+
const raw2 = "0.3.13";
|
|
285243
285451
|
if (raw2 !== void 0 && /^\d+\.\d+\.\d+$/.test(raw2)) {
|
|
285244
285452
|
return raw2;
|
|
285245
285453
|
}
|
|
@@ -285378,7 +285586,7 @@ async function runAssessCli(params) {
|
|
|
285378
285586
|
}
|
|
285379
285587
|
let cwd = process.cwd();
|
|
285380
285588
|
if (rawCwd) cwd = isAbsolute4(rawCwd) ? rawCwd : resolve7(process.cwd(), rawCwd);
|
|
285381
|
-
if (!
|
|
285589
|
+
if (!existsSync9(cwd) || !statSync8(cwd).isDirectory()) {
|
|
285382
285590
|
console.error(`assess: not a directory: ${cwd}`);
|
|
285383
285591
|
process.exit(ASSESS_EXIT.USAGE);
|
|
285384
285592
|
}
|
|
@@ -285652,7 +285860,7 @@ var import_arg3 = __toESM(require_arg(), 1);
|
|
|
285652
285860
|
|
|
285653
285861
|
// node_modules/.pnpm/@infraweaver-io+ci@0.1.8/node_modules/@infraweaver-io/ci/dist/gitAuth.js
|
|
285654
285862
|
import { execSync } from "node:child_process";
|
|
285655
|
-
import { createHash as
|
|
285863
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
285656
285864
|
import { readFileSync as readFileSync28, realpathSync as realpathSync3, unlinkSync } from "node:fs";
|
|
285657
285865
|
|
|
285658
285866
|
// node_modules/.pnpm/@infraweaver-io+sandbox@0.1.8/node_modules/@infraweaver-io/sandbox/dist/subprocess.js
|
|
@@ -285979,7 +286187,7 @@ ${errMsg}` : errMsg;
|
|
|
285979
286187
|
// node_modules/.pnpm/@infraweaver-io+ci@0.1.8/node_modules/@infraweaver-io/ci/dist/gitAuth.js
|
|
285980
286188
|
var gitBinary;
|
|
285981
286189
|
function hashFile(path15) {
|
|
285982
|
-
return
|
|
286190
|
+
return createHash13("sha256").update(readFileSync28(path15)).digest("hex");
|
|
285983
286191
|
}
|
|
285984
286192
|
function resolveGit() {
|
|
285985
286193
|
const lookup3 = process.platform === "win32" ? "where git" : "which git";
|
|
@@ -286352,7 +286560,7 @@ function TerraformChangeSummaryTool(ctx) {
|
|
|
286352
286560
|
}
|
|
286353
286561
|
|
|
286354
286562
|
// src/mcp/checkout.ts
|
|
286355
|
-
import { createHash as
|
|
286563
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
286356
286564
|
import { statSync as statSync9, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
|
|
286357
286565
|
import { join as join53 } from "node:path";
|
|
286358
286566
|
|
|
@@ -287546,7 +287754,7 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
287546
287754
|
- \`${t("terraform_normalization_candidates")}\` \u2192 in-place idiomatic cleanups that change SYNTAX, not behaviour (redundant whole-string \`"\${expr}"\` interpolation; legacy HCL0.11 map-argument block syntax like \`vars {\` on a \`template_file\`). These relocate NOTHING \u2014 zero \`moved {}\` blocks \u2014 and are proven by the same equivalence check.
|
|
287547
287755
|
- \`${t("terraform_consolidation_candidates")}\` \u2192 the SAME resource shape declared in several root directories (\`env/dev\` + \`env/staging\` + \`env/prod\`), with the single parameterised module they could all call already derived: which attributes are identical (module content) and which differ (per-environment inputs). A member root of a group will ALSO show up as a zero-candidate cluster in \`${t("module_extraction_candidates")}\` \u2014 prefer the consolidation, which does that work once for every environment instead of once per environment. Its \`near_groups\` are the opposite case: roots recognisably the same stack that DIFFER (one carries a resource the other does not), where folding them together is a design decision this mode has no basis for. Do NOT extract a near-group root's shared resources into a module on its own either \u2014 a module one twin calls and the other does not widens exactly the drift the near-group reports. Leave those roots alone and name the near-group and its \`differences\` in your report.
|
|
287548
287756
|
|
|
287549
|
-
**A
|
|
287757
|
+
**A candidate marked \`report_only\` is an observation, not work.** It sits in the code of a module other configurations call (\`report_only_reason\` says why), and restructuring that is churn for every caller: name those candidates in your \`${t("report_progress")}\` summary and open no PR for them. \`examples/\` roots are left out of every candidate list unless the operator set \`refactor_examples\`.
|
|
287550
287758
|
|
|
287551
287759
|
**Before picking, check the interface.** \`${t("module_extraction_candidates")}\` reports \`unmapped_attributes\` per candidate and a top-level \`partial_interfaces\` list: arguments the raw resources SET that the target module does not. Adopting such a module DROPS them silently \u2014 and the equivalence check cannot catch it, because once the argument is gone it is absent from both sides. Two of these are unrecoverable rather than merely wrong (\`object_lock_enabled\` cannot be set after creation; \`force_destroy\` changes deletion semantics). So: **decline the candidate, or extend the module to expose the argument and say so in the PR body.** Deleting the argument to make the shapes match is never the fix. A candidate whose \`unmapped_attributes\` is \`null\` was NOT checked \u2014 the module is external and its code is not in this repo \u2014 which is not the same as clean.
|
|
287552
287760
|
|
|
@@ -287679,7 +287887,7 @@ function remediateMode(t) {
|
|
|
287679
287887
|
|
|
287680
287888
|
4. **for the chosen group**:
|
|
287681
287889
|
- **base branch**: this run's base branch is resolved deterministically \u2014 \`${t("create_pull_request")}\` targets the \`base_branch\` input if set, else the branch the run started on, else the repository's default branch (\`main\`, or \`master\`). You do not choose it; just **omit** the \`base\` argument when opening the PR (below) and it is filled in.
|
|
287682
|
-
- **idempotency**: the remediation branch is the group's \`branch\` (\`remediate/<group-id>\`, with a \`--<base>\` suffix when the PR targets a non-default branch) \u2014 use it exactly, never a name of your own. Before doing anything, check whether that branch or an open PR for it already exists (\`${t("git")}\` / \`${t("get_pull_request")}\`). If one exists, update it rather than opening a duplicate.
|
|
287890
|
+
- **idempotency**: the remediation branch is the group's \`branch\` (\`remediate/<group-id>\`, with a \`--<base>\` suffix when the PR targets a non-default branch, or the branch of the PR already open for this group) \u2014 use it exactly, never a name of your own. Before doing anything, check whether that branch or an open PR for it already exists (\`${t("git")}\` / \`${t("get_pull_request")}\`). If one exists, update it rather than opening a duplicate.
|
|
287683
287891
|
- **branch**: create the group's \`branch\` from the **current HEAD** (the checkout that was just scanned) via \`${t("git")}\` (\`git({ command: "checkout", args: ["-b", <branch>] })\`). Do NOT switch to a different base first \u2014 branching from the scanned checkout keeps the PR diff to exactly your fix.
|
|
287684
287892
|
- **honest refusal (decide BEFORE fixing)**: if the group's concerns appear in the scan's \`refusal_candidates\` (the fix needs a human decision \u2014 narrowing an IAM wildcard, a KMS key policy, a real ingress CIDR), do **not** guess a fix that could break the stack. Instead open a structured issue (\`${t("create_issue")}\`) describing the concern, why it isn't auto-fixed, and what a human should do, and skip the PR for that group. A proven fix or an honest refusal \u2014 never a guessed, unverifiable PR.
|
|
287685
287893
|
- **propose, then let me steer (when there's no single right fix)**: distinct from honest refusal (which refuses a fix a human must *decide*), this is for a finding with **2\u20133 genuinely distinct, defensible fixes** that differ in trade-offs, not correctness (e.g. encrypt with an AWS-managed key **vs** a customer-managed KMS key; a narrow security-group rule **vs** a prefix list **vs** a VPC endpoint). When such a fork exists **and the triggering comment did not already select a strategy**, do **not** silently pick for the reviewer: via \`${t("create_issue_comment")}\` post one short comment listing the options as **A / B / C** \u2014 each a single line (what it does + its trade-off) \u2014 and ask the reviewer to reply \`${COMMENT_COMMAND} fix #<concern-id> with strategy <A|B|C>\`. Then **skip the PR for this group** this run and note it in your final report (it resumes when the reviewer replies). When the comment **did** select one (\`fix #<id> with strategy B\`, or a bare \`strategy B\` reply on the proposal thread), apply **exactly** that strategy \u2014 don't second-guess it. Reserve this for real forks in the road; a fix with one obvious correct answer just gets made.
|
|
@@ -287740,7 +287948,7 @@ This mode composes the **Remediate** and **Refactor** verbs into a single run. I
|
|
|
287740
287948
|
- **commit the fix** (\`git add\` only the changed \`*.tf\`/\`*.tfvars\`, a \`fix(tf): \u2026\` message). Do NOT push yet. This committed state is the refactor's equivalence BASELINE.
|
|
287741
287949
|
|
|
287742
287950
|
3. **REFACTOR (phase 2) \u2014 modularise on top of the committed fix**: follow the **Refactor** mode's flow:
|
|
287743
|
-
- \`${t("module_extraction_candidates")}\` / \`${t("terraform_normalization_candidates")}\` \u2192 pick one behaviour-preserving refactor; a
|
|
287951
|
+
- \`${t("module_extraction_candidates")}\` / \`${t("terraform_normalization_candidates")}\` \u2192 pick one behaviour-preserving refactor; a candidate marked \`report_only\` (it sits in the code of a module others call) is named in the summary, never proposed. Respect a pinned \`refactor_source\` if one was supplied (it constrains which module source you may use; the behaviour-altering b.4 path is only available when the operator pinned \`third-party\`).
|
|
287744
287952
|
- resolve the module source, wire the \`module\` call against its real interface (\`${t("terraform_module_interface")}\` for a local module dir, \`${t("terraform_module_lookup")}\` for a registry-sourced one), and emit a \`moved {}\` block for EVERY relocated address (\`${t("terraform_generate_moved")}\`).
|
|
287745
287953
|
- \`terraform fmt\` + \`${t("terraform_validate")}\`, then \u2014 **with the refactor edits still UNCOMMITTED** \u2014 call \`${t("terraform_equivalence_check")}\`. In this mode it diffs the working tree against the COMMITTED fix (not the run-start commit), so it measures only the refactor. It must return \`equivalent: true\` (zero uncovered moves, resource set + arguments preserved, validate + fmt clean) before you proceed; \`${t("push_branch")}\` hard-blocks an unproven one \u2014 and, once this run has added a \`moved {}\` block, it also hard-blocks a push that never ran the check at all, so abandoning the refactor means REVERTING its edits rather than leaving them in with the fix. If it can't be proven equivalent, abandon the refactor and ship the fix alone.
|
|
287746
287954
|
- **document the module (only when the \`docs\` input is enabled)**: after the equivalence check passes, call \`${t("terraform_module_docs")}\` for the module you created/adopted (pass the relocated addresses as \`moves\`). Do not hand-write docs \u2014 the tool builds from the interface, moved blocks, and equivalence verdict.
|
|
@@ -288158,7 +288366,7 @@ import { setTimeout as sleep3 } from "node:timers/promises";
|
|
|
288158
288366
|
|
|
288159
288367
|
// node_modules/.pnpm/@infraweaver-io+core@0.1.5/node_modules/@infraweaver-io/core/dist/packageManager.js
|
|
288160
288368
|
var import_semver = __toESM(require_semver2(), 1);
|
|
288161
|
-
import { existsSync as
|
|
288369
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
288162
288370
|
import { mkdir as mkdir2, readFile } from "node:fs/promises";
|
|
288163
288371
|
import { delimiter as delimiter2, join as join44 } from "node:path";
|
|
288164
288372
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
@@ -288201,7 +288409,7 @@ function parseDevEnginesField(field) {
|
|
|
288201
288409
|
}
|
|
288202
288410
|
async function resolvePackageManagerSpec(cwd) {
|
|
288203
288411
|
const pkgPath = join44(cwd, "package.json");
|
|
288204
|
-
if (!
|
|
288412
|
+
if (!existsSync10(pkgPath))
|
|
288205
288413
|
return null;
|
|
288206
288414
|
let pkg;
|
|
288207
288415
|
try {
|
|
@@ -289293,15 +289501,52 @@ var Git = type({
|
|
|
289293
289501
|
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
289294
289502
|
});
|
|
289295
289503
|
function splitWords(line) {
|
|
289296
|
-
|
|
289504
|
+
const words2 = [];
|
|
289505
|
+
let word = "";
|
|
289506
|
+
let inWord = false;
|
|
289507
|
+
let quote;
|
|
289508
|
+
for (let i = 0; i < line.length; i++) {
|
|
289509
|
+
const ch = line.charAt(i);
|
|
289510
|
+
if (quote === "'") {
|
|
289511
|
+
if (ch === "'") quote = void 0;
|
|
289512
|
+
else word += ch;
|
|
289513
|
+
} else if (quote === '"') {
|
|
289514
|
+
if (ch === '"') quote = void 0;
|
|
289515
|
+
else if (ch === "\\" && i + 1 < line.length && '"\\$`\n'.includes(line.charAt(i + 1))) {
|
|
289516
|
+
i += 1;
|
|
289517
|
+
if (line.charAt(i) !== "\n") word += line.charAt(i);
|
|
289518
|
+
} else word += ch;
|
|
289519
|
+
} else if (ch === " " || ch === " ") {
|
|
289520
|
+
if (inWord) words2.push(word);
|
|
289521
|
+
word = "";
|
|
289522
|
+
inWord = false;
|
|
289523
|
+
} else if (ch === "\n") {
|
|
289524
|
+
throw new Error(
|
|
289525
|
+
"command holds an unquoted newline \u2014 pass one git command per call, and quote a multi-line message"
|
|
289526
|
+
);
|
|
289527
|
+
} else if (ch === "\\") {
|
|
289528
|
+
if (i + 1 >= line.length) throw new Error("command ends in a backslash that escapes nothing");
|
|
289529
|
+
i += 1;
|
|
289530
|
+
if (line.charAt(i) === "\n") continue;
|
|
289531
|
+
word += line.charAt(i);
|
|
289532
|
+
inWord = true;
|
|
289533
|
+
} else if (ch === "'" || ch === '"') {
|
|
289534
|
+
quote = ch;
|
|
289535
|
+
inWord = true;
|
|
289536
|
+
} else {
|
|
289537
|
+
word += ch;
|
|
289538
|
+
inWord = true;
|
|
289539
|
+
}
|
|
289540
|
+
}
|
|
289541
|
+
if (quote) throw new Error(`command has an unterminated ${quote} quote`);
|
|
289542
|
+
if (inWord) words2.push(word);
|
|
289543
|
+
return words2;
|
|
289297
289544
|
}
|
|
289298
289545
|
function normalizeGitCall(params) {
|
|
289299
|
-
|
|
289300
|
-
|
|
289301
|
-
|
|
289302
|
-
|
|
289303
|
-
else return { command: params.command, args: params.args ?? [] };
|
|
289304
|
-
const [command, ...args2] = words2[0] === "git" ? words2.slice(1) : words2;
|
|
289546
|
+
const words2 = params.command === void 0 ? [] : /^git\s/.test(params.command) ? splitWords(params.command) : [params.command];
|
|
289547
|
+
words2.push(...params.args ?? []);
|
|
289548
|
+
while (words2[0] === "git") words2.shift();
|
|
289549
|
+
const [command, ...args2] = words2;
|
|
289305
289550
|
if (command === void 0 || !/^[a-z][a-z0-9-]*$/.test(command)) {
|
|
289306
289551
|
throw new Error(
|
|
289307
289552
|
`command must be a Git subcommand such as 'status' or 'checkout' (was "${command ?? ""}")`
|
|
@@ -289737,7 +289982,7 @@ function claimsBlockingSeverity(body2) {
|
|
|
289737
289982
|
init_core();
|
|
289738
289983
|
import { isAbsolute as isAbsolute6, resolve as resolve10 } from "node:path";
|
|
289739
289984
|
|
|
289740
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
289985
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.7/node_modules/@infraweaver-io/models/dist/monotonicQuota.js
|
|
289741
289986
|
function createMonotonicQuota(params) {
|
|
289742
289987
|
const { limit, unlimited, normalize: normalize5 } = params;
|
|
289743
289988
|
let used = 0;
|
|
@@ -289762,7 +290007,7 @@ function createMonotonicQuota(params) {
|
|
|
289762
290007
|
};
|
|
289763
290008
|
}
|
|
289764
290009
|
|
|
289765
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
290010
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.7/node_modules/@infraweaver-io/models/dist/costQuota.js
|
|
289766
290011
|
var CostQuotaExceededError = class extends Error {
|
|
289767
290012
|
code = "COST_QUOTA_EXCEEDED";
|
|
289768
290013
|
constructor(message2) {
|
|
@@ -289809,7 +290054,7 @@ function resolveRunCostQuota(parsed2) {
|
|
|
289809
290054
|
return parsed2 === void 0 ? DEFAULT_RUN_COST_QUOTA_USD : parsed2;
|
|
289810
290055
|
}
|
|
289811
290056
|
|
|
289812
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
290057
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.7/node_modules/@infraweaver-io/models/dist/tokenQuota.js
|
|
289813
290058
|
var TokenQuotaExceededError = class extends Error {
|
|
289814
290059
|
code = "TOKEN_QUOTA_EXCEEDED";
|
|
289815
290060
|
constructor(message2) {
|
|
@@ -289819,19 +290064,16 @@ var TokenQuotaExceededError = class extends Error {
|
|
|
289819
290064
|
};
|
|
289820
290065
|
var DEFAULT_RUN_TOKEN_QUOTA = 1e7;
|
|
289821
290066
|
var RUN_TOKEN_QUOTA_INPUT = "max_run_tokens";
|
|
289822
|
-
|
|
289823
|
-
|
|
289824
|
-
|
|
289825
|
-
|
|
289826
|
-
total += v;
|
|
289827
|
-
}
|
|
289828
|
-
return Math.floor(total);
|
|
290067
|
+
var CACHE_READ_WEIGHT = 0.1;
|
|
290068
|
+
function quotaTokens(t) {
|
|
290069
|
+
const part = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
290070
|
+
return Math.floor(part(t.input) + part(t.output) + part(t.cacheWrite) + part(t.cacheRead) * CACHE_READ_WEIGHT);
|
|
289829
290071
|
}
|
|
289830
290072
|
function normalizeTotal(total) {
|
|
289831
|
-
return typeof total === "number" ? Math.max(0, Math.floor(total)) :
|
|
290073
|
+
return typeof total === "number" ? Math.max(0, Math.floor(total)) : quotaTokens(total);
|
|
289832
290074
|
}
|
|
289833
290075
|
function tokenGap(used, ceiling) {
|
|
289834
|
-
return `run token quota exhausted: ${used.toLocaleString("en-GB")} of ${ceiling.toLocaleString("en-GB")}
|
|
290076
|
+
return `run token quota exhausted: ${used.toLocaleString("en-GB")} of ${ceiling.toLocaleString("en-GB")} tokens counted against the quota (a token read from the prompt cache counts as a tenth), so the run was stopped before it finished. Its results are INCOMPLETE and must not be read as a clean assessment \u2014 a finding that would have been raised after this point was never looked for. Raise \`${RUN_TOKEN_QUOTA_INPUT}\` if the run legitimately needs more budget, or set \`${RUN_TOKEN_QUOTA_INPUT}: 0\` to remove the bound entirely. A run that reaches this limit without finishing is more often a remediation loop that cannot converge (a fix that re-breaks its own check) than a genuinely large repository.`;
|
|
289835
290077
|
}
|
|
289836
290078
|
function unlimitedTokenQuota() {
|
|
289837
290079
|
return createMonotonicQuota({
|
|
@@ -290974,6 +291216,12 @@ function parseCommentMarker(body2) {
|
|
|
290974
291216
|
return null;
|
|
290975
291217
|
}
|
|
290976
291218
|
}
|
|
291219
|
+
function findingKeysIn(body2) {
|
|
291220
|
+
return [...body2.matchAll(new RegExp(MARKER.source, "g"))].flatMap((m) => {
|
|
291221
|
+
const marker2 = parseCommentMarker(m[0]);
|
|
291222
|
+
return marker2?.kind === "finding" ? [marker2.key] : [];
|
|
291223
|
+
});
|
|
291224
|
+
}
|
|
290977
291225
|
var SUGGESTION = /```suggestion\r?\n([\s\S]*?)```/;
|
|
290978
291226
|
function suggestionText(body2) {
|
|
290979
291227
|
const m = SUGGESTION.exec(body2);
|
|
@@ -291223,39 +291471,115 @@ function formatDuplicateCommentsNote(duplicates) {
|
|
|
291223
291471
|
var MAX_DUPLICATE_COMMENT_LINES = 50;
|
|
291224
291472
|
|
|
291225
291473
|
// src/mcp/reviewCommentLimit.ts
|
|
291226
|
-
var
|
|
291474
|
+
var MAX_NOTE_CHARS = 3e4;
|
|
291475
|
+
var TRAILER_RESERVE = 400;
|
|
291227
291476
|
var PROVEN_MARK = "\u2705 **Proven by re-scan**";
|
|
291228
|
-
|
|
291477
|
+
var SEVERITY_LEVEL = {
|
|
291478
|
+
critical: 0,
|
|
291479
|
+
high: 1,
|
|
291480
|
+
medium: 2,
|
|
291481
|
+
low: 3,
|
|
291482
|
+
info: 4
|
|
291483
|
+
};
|
|
291484
|
+
var MARKER_LEVEL = { "\u{1F6A8}": 1, "\u26A0\uFE0F": 2, "\u26A0": 2, "\u2139\uFE0F": 3, \u2139: 3 };
|
|
291485
|
+
var UNMARKED_LEVEL = 2;
|
|
291486
|
+
function rank(c, severity) {
|
|
291229
291487
|
const body2 = c.body ?? "";
|
|
291230
|
-
const
|
|
291231
|
-
|
|
291232
|
-
return severity + (body2.includes(PROVEN_MARK) ? 0 : 1);
|
|
291488
|
+
const level = severity ? SEVERITY_LEVEL[severity] : MARKER_LEVEL[leadingMarker(body2) ?? ""] ?? UNMARKED_LEVEL;
|
|
291489
|
+
return level * 2 + (body2.includes(PROVEN_MARK) ? 0 : 1);
|
|
291233
291490
|
}
|
|
291234
|
-
function limitInlineComments(comments, limit) {
|
|
291491
|
+
function limitInlineComments(comments, limit, severityOf) {
|
|
291235
291492
|
if (!limit || comments.length <= limit) return { kept: [...comments], overflow: [] };
|
|
291236
291493
|
const keep = new Set(
|
|
291237
|
-
comments.map((c, i) => ({ i, r: rank(c) })).sort((a, b) => a.r - b.r || a.i - b.i).slice(0, limit).map(({ i }) => i)
|
|
291494
|
+
comments.map((c, i) => ({ i, r: rank(c, severityOf?.(c)) })).sort((a, b) => a.r - b.r || a.i - b.i).slice(0, limit).map(({ i }) => i)
|
|
291238
291495
|
);
|
|
291239
291496
|
return {
|
|
291240
291497
|
kept: comments.filter((_, i) => keep.has(i)),
|
|
291241
291498
|
overflow: comments.filter((_, i) => !keep.has(i))
|
|
291242
291499
|
};
|
|
291243
291500
|
}
|
|
291244
|
-
|
|
291501
|
+
var escapeHtml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
291502
|
+
var whereOf = (c) => c.line ? `${c.path}:${c.line}` : c.path;
|
|
291503
|
+
function closesItself(body2) {
|
|
291504
|
+
const occurrences = (re) => body2.match(re)?.length ?? 0;
|
|
291505
|
+
const fences = body2.split("\n").filter((l) => /^\s*(```|~~~)/.test(l)).length;
|
|
291506
|
+
return fences % 2 === 0 && occurrences(/<details\b/gi) === occurrences(/<\/details\s*>/gi) && occurrences(/<!--/g) === occurrences(/-->/g);
|
|
291507
|
+
}
|
|
291508
|
+
function asText(body2) {
|
|
291509
|
+
const longest = Math.max(0, ...(body2.match(/`+/g) ?? []).map((run4) => run4.length));
|
|
291510
|
+
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
291511
|
+
return `${fence}text
|
|
291512
|
+
${body2}
|
|
291513
|
+
${fence}`;
|
|
291514
|
+
}
|
|
291515
|
+
function renderWhole(c, linkOf) {
|
|
291516
|
+
const where4 = escapeHtml(whereOf(c));
|
|
291517
|
+
const gist = gistOf(c.body);
|
|
291518
|
+
const href = linkOf?.(c);
|
|
291519
|
+
const body2 = c.body ?? "";
|
|
291520
|
+
return [
|
|
291521
|
+
`<details><summary><code>${where4}</code>${gist ? ` \u2014 ${gist}` : ""}</summary>`,
|
|
291522
|
+
...href ? [`<a href="${escapeHtml(href)}">${where4}</a>`] : [],
|
|
291523
|
+
...body2 ? [closesItself(body2) ? body2 : asText(body2)] : [],
|
|
291524
|
+
"</details>"
|
|
291525
|
+
].join("\n\n");
|
|
291526
|
+
}
|
|
291527
|
+
function renderBrief(c) {
|
|
291528
|
+
const gist = gistOf(c.body);
|
|
291529
|
+
const markers = findingKeysIn(c.body ?? "").map(
|
|
291530
|
+
(key) => renderCommentMarker({ kind: "finding", key, proven: false })
|
|
291531
|
+
);
|
|
291532
|
+
return `- \`${whereOf(c).replace(/`/g, "'")}\`${gist ? ` \u2014 ${gist}` : ""}${markers.map((m) => ` ${m}`).join("")}`;
|
|
291533
|
+
}
|
|
291534
|
+
function formatOverflowNote(overflow, limit, linkOf) {
|
|
291245
291535
|
if (overflow.length === 0) return "";
|
|
291246
|
-
const
|
|
291247
|
-
const where4 = c.line ? `${c.path}:${c.line}` : c.path;
|
|
291248
|
-
const gist = gistOf(c.body);
|
|
291249
|
-
return `- \`${where4.replace(/`/g, "'")}\`${gist ? ` \u2014 ${gist}` : ""}`;
|
|
291250
|
-
});
|
|
291251
|
-
const rest = overflow.length - shown.length;
|
|
291252
|
-
if (rest > 0) shown.push(`- \u2026and ${rest} more not shown`);
|
|
291253
|
-
return `
|
|
291536
|
+
const header = `
|
|
291254
291537
|
|
|
291255
291538
|
---
|
|
291256
291539
|
|
|
291257
|
-
**Note:** this review posts at most ${limit} inline comments (\`review_comment_limit\`), so ${overflow.length} more ${overflow.length === 1 ? "is" : "are"} listed here instead
|
|
291258
|
-
|
|
291540
|
+
**Note:** this review posts at most ${limit} inline comments (\`review_comment_limit\`), so ${overflow.length} more ${overflow.length === 1 ? "is" : "are"} listed here instead:`;
|
|
291541
|
+
const SEP = 2;
|
|
291542
|
+
let room = MAX_NOTE_CHARS - TRAILER_RESERVE - header.length;
|
|
291543
|
+
const items = [];
|
|
291544
|
+
for (const c of overflow) {
|
|
291545
|
+
const brief2 = renderBrief(c);
|
|
291546
|
+
if (brief2.length + SEP > room) break;
|
|
291547
|
+
items.push(brief2);
|
|
291548
|
+
room -= brief2.length + SEP;
|
|
291549
|
+
}
|
|
291550
|
+
let brief = items.length;
|
|
291551
|
+
for (const [i, c] of overflow.slice(0, items.length).entries()) {
|
|
291552
|
+
const whole = renderWhole(c, linkOf);
|
|
291553
|
+
const extra = whole.length - items[i].length;
|
|
291554
|
+
if (extra > room) continue;
|
|
291555
|
+
items[i] = whole;
|
|
291556
|
+
room -= extra;
|
|
291557
|
+
brief--;
|
|
291558
|
+
}
|
|
291559
|
+
const omitted = overflow.length - items.length;
|
|
291560
|
+
if (omitted > 0) items.push(`- \u2026and ${omitted} more not shown`);
|
|
291561
|
+
const cut = [
|
|
291562
|
+
brief > 0 ? `${brief} of them ${brief === 1 ? "is" : "are"} shown by place and first line only` : "",
|
|
291563
|
+
omitted > 0 ? `${omitted} ${omitted === 1 ? "is" : "are"} not shown at all` : ""
|
|
291564
|
+
].filter(Boolean);
|
|
291565
|
+
if (cut.length > 0) {
|
|
291566
|
+
items.push(`${cut.join(" and ")}, to keep this review within GitHub's size limit.`);
|
|
291567
|
+
}
|
|
291568
|
+
return `${header}
|
|
291569
|
+
|
|
291570
|
+
${items.join("\n\n")}`;
|
|
291571
|
+
}
|
|
291572
|
+
function foldedFindingsStillIntroduced(input2) {
|
|
291573
|
+
const folded = new Set(
|
|
291574
|
+
input2.reviews.flatMap(
|
|
291575
|
+
(r) => r.body && isOwnReviewComment(r.user, r.body) ? findingKeysIn(r.body) : []
|
|
291576
|
+
)
|
|
291577
|
+
);
|
|
291578
|
+
if (folded.size === 0) return [];
|
|
291579
|
+
const dismissed = new Set(input2.dismissed.map((d) => d.finding_id));
|
|
291580
|
+
return input2.delta.introduced.filter(
|
|
291581
|
+
(f) => folded.has(findingKey(f)) && !dismissed.has(f.concern.id)
|
|
291582
|
+
);
|
|
291259
291583
|
}
|
|
291260
291584
|
|
|
291261
291585
|
// src/mcp/reviewComments.ts
|
|
@@ -292135,7 +292459,7 @@ function where2(f) {
|
|
|
292135
292459
|
function renderReviewVerdict(input2) {
|
|
292136
292460
|
const { delta, verdict: verdict2 } = input2;
|
|
292137
292461
|
const introduced = delta.introduced.length;
|
|
292138
|
-
const blocking = verdict2
|
|
292462
|
+
const { blocking } = verdict2;
|
|
292139
292463
|
const renamedBlocking = delta.introduced.filter(
|
|
292140
292464
|
(f) => f.possiblyPreexisting && failsGate(f, [...input2.requiredControls ?? []])
|
|
292141
292465
|
).length;
|
|
@@ -292381,7 +292705,7 @@ function apply(worktree, path15, cs) {
|
|
|
292381
292705
|
}
|
|
292382
292706
|
|
|
292383
292707
|
// src/mcp/terraform/gateScan.ts
|
|
292384
|
-
import { existsSync as
|
|
292708
|
+
import { existsSync as existsSync11, mkdtempSync as mkdtempSync6, rmSync as rmSync10 } from "node:fs";
|
|
292385
292709
|
import { join as join50, posix as posix15 } from "node:path";
|
|
292386
292710
|
|
|
292387
292711
|
// src/mcp/terraform/secretScan.ts
|
|
@@ -292538,7 +292862,7 @@ async function reviewScans(ctx, pr, scanners) {
|
|
|
292538
292862
|
const changed = diffScope(ctx, cwd, scanners);
|
|
292539
292863
|
const scope2 = base ? reviewScope(ctx, cwd, base) : void 0;
|
|
292540
292864
|
const started = Date.now();
|
|
292541
|
-
const scanBase = async (b) =>
|
|
292865
|
+
const scanBase = async (b) => existsSync11(b.cwd) ? scanTree(ctx, b.cwd, changed, scanners, reviewedConcern, scope2) : [];
|
|
292542
292866
|
const [head, baseScan, secrets] = await Promise.allSettled([
|
|
292543
292867
|
scanTree(ctx, cwd, changed, scanners, reviewedConcern, scope2),
|
|
292544
292868
|
base ? scanBase(base) : Promise.resolve([]),
|
|
@@ -292970,6 +293294,22 @@ function CreatePullRequestReviewTool(ctx) {
|
|
|
292970
293294
|
`cannot approve: ${outstanding} unresolved ${PRODUCT_NAME} review thread(s) still open on this PR. approval requires every prior ${PRODUCT_NAME} finding to be resolved, not just the latest commits to be clean. inspect them with \`${listRef}\`; for each thread the current code genuinely addresses, reply then call \`${resolveRef2}\`, and retry this approval. if any thread is a real outstanding issue, do NOT approve \u2014 submit a non-approving review (omit \`approved\`, or set \`request_changes\` for a blocking one) that covers it instead.`
|
|
292971
293295
|
);
|
|
292972
293296
|
}
|
|
293297
|
+
if (delta) {
|
|
293298
|
+
const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
|
|
293299
|
+
owner: ctx.repo.owner,
|
|
293300
|
+
repo: ctx.repo.name,
|
|
293301
|
+
pull_number,
|
|
293302
|
+
per_page: 100
|
|
293303
|
+
});
|
|
293304
|
+
const folded = foldedFindingsStillIntroduced({ reviews, delta, dismissed });
|
|
293305
|
+
if (folded.length > 0) {
|
|
293306
|
+
throw new Error(
|
|
293307
|
+
`cannot approve: ${folded.length} introduced finding(s) an earlier ${PRODUCT_NAME} review listed in its body (past \`review_comment_limit\`, so with no thread to resolve) are still introduced by this head: ` + folded.slice(0, 20).map(
|
|
293308
|
+
(f) => `${f.concern.id} (${f.concern.rule_id} at ${f.concern.location.file}:${f.concern.location.line ?? "?"})`
|
|
293309
|
+
).join(", ") + `. approval requires every prior ${PRODUCT_NAME} finding to be addressed. if one genuinely needs no change, list it in \`dismissed\` with the reason and retry; otherwise do NOT approve \u2014 submit a non-approving review that covers it.`
|
|
293310
|
+
);
|
|
293311
|
+
}
|
|
293312
|
+
}
|
|
292973
293313
|
}
|
|
292974
293314
|
const skip = reviewSkipDecision({
|
|
292975
293315
|
approved: approved ?? false,
|
|
@@ -293076,6 +293416,7 @@ ${formatReanchoredNote(reanchored.moved)}`
|
|
|
293076
293416
|
params.event = event;
|
|
293077
293417
|
}
|
|
293078
293418
|
const proofs = delta ? await proveReviewSuggestions(ctx, auditedComments) : /* @__PURE__ */ new Map();
|
|
293419
|
+
const findingSeverity = /* @__PURE__ */ new Map();
|
|
293079
293420
|
const reviewComments = auditedComments.map((comment, index) => {
|
|
293080
293421
|
let commentBody = fixDoubleEscapedString(comment.body || "");
|
|
293081
293422
|
if (comment.suggestion !== void 0) {
|
|
@@ -293117,6 +293458,9 @@ ${marker2}`;
|
|
|
293117
293458
|
reviewComment.start_line = comment.start_line;
|
|
293118
293459
|
reviewComment.start_side = side;
|
|
293119
293460
|
}
|
|
293461
|
+
if (comment.provenance?.kind === "scanner") {
|
|
293462
|
+
findingSeverity.set(reviewComment, comment.provenance.finding.concern.severity);
|
|
293463
|
+
}
|
|
293120
293464
|
return reviewComment;
|
|
293121
293465
|
});
|
|
293122
293466
|
let droppedComments = [];
|
|
@@ -293152,7 +293496,11 @@ ${marker2}`;
|
|
|
293152
293496
|
`withholding ${duplicateComments.length}/${reviewComments.length} inline comment(s) that duplicate open review threads`
|
|
293153
293497
|
);
|
|
293154
293498
|
}
|
|
293155
|
-
const limited = limitInlineComments(
|
|
293499
|
+
const limited = limitInlineComments(
|
|
293500
|
+
deduped.kept,
|
|
293501
|
+
ctx.payload.reviewCommentLimit,
|
|
293502
|
+
(c) => findingSeverity.get(c)
|
|
293503
|
+
);
|
|
293156
293504
|
overflowComments = limited.overflow;
|
|
293157
293505
|
if (overflowComments.length > 0) {
|
|
293158
293506
|
log2.info(
|
|
@@ -293184,7 +293532,13 @@ ${marker2}`;
|
|
|
293184
293532
|
body2 = body2 ? body2 + note : note.replace(/^\n\n/, "");
|
|
293185
293533
|
}
|
|
293186
293534
|
if (overflowComments.length > 0) {
|
|
293187
|
-
const
|
|
293535
|
+
const server = process.env.GITHUB_SERVER_URL || "https://github.com";
|
|
293536
|
+
const commit = params.commit_id;
|
|
293537
|
+
const note = formatOverflowNote(
|
|
293538
|
+
overflowComments,
|
|
293539
|
+
ctx.payload.reviewCommentLimit,
|
|
293540
|
+
commit ? (c) => `${server}/${ctx.repo.owner}/${ctx.repo.name}/blob/${commit}/${c.path.split("/").map(encodeURIComponent).join("/")}${c.line && c.side !== "LEFT" ? `#L${c.line}` : ""}` : void 0
|
|
293541
|
+
);
|
|
293188
293542
|
body2 = body2 ? body2 + note : note.replace(/^\n\n/, "");
|
|
293189
293543
|
}
|
|
293190
293544
|
let reproved = 0;
|
|
@@ -294196,7 +294550,7 @@ function formatFilesWithLineNumbers(files) {
|
|
|
294196
294550
|
}
|
|
294197
294551
|
const tocLines = [`## Files (${files.length})`];
|
|
294198
294552
|
for (const entry4 of tocEntries) {
|
|
294199
|
-
const anchor2 =
|
|
294553
|
+
const anchor2 = createHash14("sha256").update(entry4.filename).digest("hex");
|
|
294200
294554
|
tocLines.push(
|
|
294201
294555
|
`- ${entry4.filename} \u2192 lines ${entry4.startLine}-${entry4.endLine} \xB7 diff-${anchor2}`
|
|
294202
294556
|
);
|
|
@@ -295042,7 +295396,7 @@ function UpsertRemediationDashboardTool(ctx) {
|
|
|
295042
295396
|
import { performance as performance3 } from "node:perf_hooks";
|
|
295043
295397
|
|
|
295044
295398
|
// src/prep/installNodeDependencies.ts
|
|
295045
|
-
import { existsSync as
|
|
295399
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
295046
295400
|
import { join as join56 } from "node:path";
|
|
295047
295401
|
|
|
295048
295402
|
// node_modules/.pnpm/package-manager-detector@1.8.0/node_modules/package-manager-detector/dist/commands.mjs
|
|
@@ -295439,7 +295793,7 @@ var installNodeDependencies = {
|
|
|
295439
295793
|
name: "installNodeDependencies",
|
|
295440
295794
|
shouldRun: () => {
|
|
295441
295795
|
const packageJsonPath = join56(process.cwd(), "package.json");
|
|
295442
|
-
return
|
|
295796
|
+
return existsSync12(packageJsonPath);
|
|
295443
295797
|
},
|
|
295444
295798
|
run: async (options) => {
|
|
295445
295799
|
const declared2 = await resolvePackageManagerSpec(process.cwd());
|
|
@@ -295547,7 +295901,7 @@ ${errorMessage2}`]
|
|
|
295547
295901
|
};
|
|
295548
295902
|
|
|
295549
295903
|
// src/prep/installPythonDependencies.ts
|
|
295550
|
-
import { existsSync as
|
|
295904
|
+
import { existsSync as existsSync13, readFileSync as readFileSync34 } from "node:fs";
|
|
295551
295905
|
import { join as join57 } from "node:path";
|
|
295552
295906
|
var PYTHON_CONFIGS = [
|
|
295553
295907
|
{
|
|
@@ -295633,13 +295987,13 @@ var installPythonDependencies = {
|
|
|
295633
295987
|
}
|
|
295634
295988
|
const cwd = process.cwd();
|
|
295635
295989
|
return PYTHON_CONFIGS.some(
|
|
295636
|
-
(config3) =>
|
|
295990
|
+
(config3) => existsSync13(join57(cwd, config3.file)) && configApplies(config3, cwd)
|
|
295637
295991
|
);
|
|
295638
295992
|
},
|
|
295639
295993
|
run: async (options) => {
|
|
295640
295994
|
const cwd = process.cwd();
|
|
295641
295995
|
const config3 = PYTHON_CONFIGS.find(
|
|
295642
|
-
(c) =>
|
|
295996
|
+
(c) => existsSync13(join57(cwd, c.file)) && configApplies(c, cwd)
|
|
295643
295997
|
);
|
|
295644
295998
|
if (!config3) {
|
|
295645
295999
|
return {
|
|
@@ -296220,7 +296574,7 @@ function SetOutputTool(ctx, outputSchema) {
|
|
|
296220
296574
|
|
|
296221
296575
|
// src/mcp/policy.ts
|
|
296222
296576
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
296223
|
-
import { existsSync as
|
|
296577
|
+
import { existsSync as existsSync14, readFileSync as readFileSync35 } from "node:fs";
|
|
296224
296578
|
import { join as join58 } from "node:path";
|
|
296225
296579
|
function parseConftestOutput(stdout) {
|
|
296226
296580
|
let parsed2;
|
|
@@ -296272,7 +296626,7 @@ function namespaceArgs(conftestCwd) {
|
|
|
296272
296626
|
function findPlanJson(cwd) {
|
|
296273
296627
|
for (const candidate of PLAN_JSON_NAMES) {
|
|
296274
296628
|
const abs = join58(cwd, candidate);
|
|
296275
|
-
if (
|
|
296629
|
+
if (existsSync14(abs)) return abs;
|
|
296276
296630
|
}
|
|
296277
296631
|
return null;
|
|
296278
296632
|
}
|
|
@@ -296313,11 +296667,11 @@ function resolvePolicyDir(cwd, explicit) {
|
|
|
296313
296667
|
} catch {
|
|
296314
296668
|
return null;
|
|
296315
296669
|
}
|
|
296316
|
-
return
|
|
296670
|
+
return existsSync14(abs) ? abs : null;
|
|
296317
296671
|
}
|
|
296318
296672
|
for (const d of DEFAULT_POLICY_DIRS) {
|
|
296319
296673
|
const abs = join58(cwd, d);
|
|
296320
|
-
if (
|
|
296674
|
+
if (existsSync14(abs)) return abs;
|
|
296321
296675
|
}
|
|
296322
296676
|
return null;
|
|
296323
296677
|
}
|
|
@@ -296351,7 +296705,7 @@ function PolicyCheckTool(ctx) {
|
|
|
296351
296705
|
} catch {
|
|
296352
296706
|
abs = null;
|
|
296353
296707
|
}
|
|
296354
|
-
targetFile = abs &&
|
|
296708
|
+
targetFile = abs && existsSync14(abs) ? abs : null;
|
|
296355
296709
|
if (!targetFile) {
|
|
296356
296710
|
return toolSkip(
|
|
296357
296711
|
"target_not_found",
|
|
@@ -299942,7 +300296,7 @@ async function startMcpHttpServer(ctx, options) {
|
|
|
299942
300296
|
|
|
299943
300297
|
// src/mcp/terraform/policyGate.ts
|
|
299944
300298
|
import {
|
|
299945
|
-
existsSync as
|
|
300299
|
+
existsSync as existsSync15,
|
|
299946
300300
|
mkdirSync as mkdirSync7,
|
|
299947
300301
|
mkdtempSync as mkdtempSync8,
|
|
299948
300302
|
readdirSync as readdirSync10,
|
|
@@ -300325,7 +300679,7 @@ function readOrEmpty(path15) {
|
|
|
300325
300679
|
function regoDirIn(root, skip) {
|
|
300326
300680
|
for (const dir of DEFAULT_POLICY_DIRS) {
|
|
300327
300681
|
const abs = join61(root, dir);
|
|
300328
|
-
if (
|
|
300682
|
+
if (existsSync15(abs) && walkFilesBySuffix(abs, ".rego").some(
|
|
300329
300683
|
(rel) => !skip(join61(dir, rel), readOrEmpty(join61(abs, rel)))
|
|
300330
300684
|
)) {
|
|
300331
300685
|
return dir;
|
|
@@ -300394,7 +300748,7 @@ function resolveRepoPolicy(cwd, baseRef, tmpdir6, notes, skip) {
|
|
|
300394
300748
|
return null;
|
|
300395
300749
|
}
|
|
300396
300750
|
function findWorkflowPlanJson(cwd, notes) {
|
|
300397
|
-
const present = PLAN_JSON_NAMES.filter((name) =>
|
|
300751
|
+
const present = PLAN_JSON_NAMES.filter((name) => existsSync15(join61(cwd, name)));
|
|
300398
300752
|
if (present.length === 0) return null;
|
|
300399
300753
|
const listing = run("git", ["ls-files", "-z"], cwd);
|
|
300400
300754
|
if (listing.status !== 0) {
|
|
@@ -301753,9 +302107,9 @@ function shouldRunGraceTurn(params) {
|
|
|
301753
302107
|
}
|
|
301754
302108
|
|
|
301755
302109
|
// src/agents/quotaTokens.ts
|
|
301756
|
-
var
|
|
301757
|
-
function
|
|
301758
|
-
return t.input + t.output + t.cacheWrite + t.cacheRead *
|
|
302110
|
+
var CACHE_READ_WEIGHT2 = 0.1;
|
|
302111
|
+
function quotaTokens2(t) {
|
|
302112
|
+
return t.input + t.output + (t.reasoning ?? 0) + t.cacheWrite + t.cacheRead * CACHE_READ_WEIGHT2;
|
|
301759
302113
|
}
|
|
301760
302114
|
|
|
301761
302115
|
// src/agents/toolPlaneWatch.ts
|
|
@@ -302153,7 +302507,7 @@ function createProcessOutputActivityTimeout(ctx) {
|
|
|
302153
302507
|
}
|
|
302154
302508
|
|
|
302155
302509
|
// src/utils/prompt/skills.ts
|
|
302156
|
-
import { existsSync as
|
|
302510
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync38, writeFileSync as writeFileSync19 } from "node:fs";
|
|
302157
302511
|
import { dirname as dirname9, join as join64 } from "node:path";
|
|
302158
302512
|
import { fileURLToPath } from "node:url";
|
|
302159
302513
|
var BUNDLED_SKILL_NAMES = ["terraform-best-practices"];
|
|
@@ -302164,7 +302518,7 @@ function resolveSkillPath(name) {
|
|
|
302164
302518
|
join64(here, "skills", name, "SKILL.md")
|
|
302165
302519
|
];
|
|
302166
302520
|
for (const candidate of candidates) {
|
|
302167
|
-
if (
|
|
302521
|
+
if (existsSync16(candidate)) return candidate;
|
|
302168
302522
|
}
|
|
302169
302523
|
throw new Error(`bundled skill not found: ${name} (looked in ${candidates.join(", ")})`);
|
|
302170
302524
|
}
|
|
@@ -302183,8 +302537,8 @@ function installBundledSkills(params) {
|
|
|
302183
302537
|
|
|
302184
302538
|
// src/utils/setup/install.ts
|
|
302185
302539
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
302186
|
-
import { createHash as
|
|
302187
|
-
import { chmodSync as chmodSync2, createWriteStream as createWriteStream2, existsSync as
|
|
302540
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
302541
|
+
import { chmodSync as chmodSync2, createWriteStream as createWriteStream2, existsSync as existsSync19, readFileSync as readFileSync39 } from "node:fs";
|
|
302188
302542
|
import { join as join69 } from "node:path";
|
|
302189
302543
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
302190
302544
|
|
|
@@ -302250,7 +302604,7 @@ function verifyTarballIntegrity(tarballPath, dist, descriptor) {
|
|
|
302250
302604
|
const expected = candidate.slice(separator2 + 1);
|
|
302251
302605
|
let actual;
|
|
302252
302606
|
try {
|
|
302253
|
-
actual =
|
|
302607
|
+
actual = createHash16(algorithm).update(bytes).digest("base64");
|
|
302254
302608
|
} catch {
|
|
302255
302609
|
continue;
|
|
302256
302610
|
}
|
|
@@ -302260,7 +302614,7 @@ function verifyTarballIntegrity(tarballPath, dist, descriptor) {
|
|
|
302260
302614
|
`Integrity check FAILED for ${descriptor}: the downloaded tarball does not match the registry's published digest (${integrity2}). Refusing to execute it.`
|
|
302261
302615
|
);
|
|
302262
302616
|
}
|
|
302263
|
-
const actualShasum =
|
|
302617
|
+
const actualShasum = createHash16("sha1").update(bytes).digest("hex");
|
|
302264
302618
|
if (actualShasum !== shasum) {
|
|
302265
302619
|
throw new Error(
|
|
302266
302620
|
`Integrity check FAILED for ${descriptor}: the downloaded tarball does not match the registry's published shasum (expected ${shasum}, got ${actualShasum}). Refusing to execute it.`
|
|
@@ -302271,7 +302625,7 @@ async function installFromNpmTarball(params) {
|
|
|
302271
302625
|
const tempDir = getTempDir();
|
|
302272
302626
|
const extractedDir = join69(tempDir, "package");
|
|
302273
302627
|
const cliPath = join69(extractedDir, params.executablePath);
|
|
302274
|
-
if (
|
|
302628
|
+
if (existsSync19(cliPath)) {
|
|
302275
302629
|
log2.debug(`\xBB using cached binary at ${cliPath}`);
|
|
302276
302630
|
return cliPath;
|
|
302277
302631
|
}
|
|
@@ -302362,7 +302716,7 @@ async function installFromNpmTarball(params) {
|
|
|
302362
302716
|
`Failed to extract tarball: ${extractResult.stderr || extractResult.stdout || "Unknown error"}`
|
|
302363
302717
|
);
|
|
302364
302718
|
}
|
|
302365
|
-
if (!
|
|
302719
|
+
if (!existsSync19(cliPath)) {
|
|
302366
302720
|
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
302367
302721
|
}
|
|
302368
302722
|
if (params.installDependencies) {
|
|
@@ -302602,7 +302956,7 @@ function timerFor(state5, label) {
|
|
|
302602
302956
|
function chargeTokenQuota(state5) {
|
|
302603
302957
|
const quota = state5.params.tokenQuota;
|
|
302604
302958
|
if (!quota || state5.stopRun.signal.aborted) return;
|
|
302605
|
-
if (!quota.observe(state5.quotaSource,
|
|
302959
|
+
if (!quota.observe(state5.quotaSource, quotaTokens2(state5.accumulatedTokens))) return;
|
|
302606
302960
|
log2.info(`\xBB ${state5.params.label}: ${quota.gap()}`);
|
|
302607
302961
|
state5.stopRun.abort(new TokenQuotaExceededError(quota.gap()));
|
|
302608
302962
|
}
|
|
@@ -309334,7 +309688,7 @@ function newTurn() {
|
|
|
309334
309688
|
turnCounter += 1;
|
|
309335
309689
|
return {
|
|
309336
309690
|
finalText: "",
|
|
309337
|
-
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
309691
|
+
tokens: { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 },
|
|
309338
309692
|
costUsd: 0,
|
|
309339
309693
|
sessionError: null,
|
|
309340
309694
|
lastToolError: null,
|
|
@@ -309344,7 +309698,7 @@ function newTurn() {
|
|
|
309344
309698
|
function chargeTokenQuota2(ctx) {
|
|
309345
309699
|
const quota = ctx.tokenQuota;
|
|
309346
309700
|
if (!quota || !ctx.currentTurn || ctx.tokenQuotaExceeded) return;
|
|
309347
|
-
if (!quota.observe(ctx.currentTurn.quotaSource,
|
|
309701
|
+
if (!quota.observe(ctx.currentTurn.quotaSource, quotaTokens2(ctx.currentTurn.tokens))) return;
|
|
309348
309702
|
ctx.tokenQuotaExceeded = true;
|
|
309349
309703
|
log2.info(`\xBB ${ctx.label}: ${quota.gap()}`);
|
|
309350
309704
|
ctx.abortRun?.(new TokenQuotaExceededError(quota.gap()));
|
|
@@ -309453,6 +309807,7 @@ async function onPartUpdated(ctx, part) {
|
|
|
309453
309807
|
if (t) {
|
|
309454
309808
|
ctx.currentTurn.tokens.input += t.input || 0;
|
|
309455
309809
|
ctx.currentTurn.tokens.output += t.output || 0;
|
|
309810
|
+
ctx.currentTurn.tokens.reasoning += t.reasoning || 0;
|
|
309456
309811
|
ctx.currentTurn.tokens.cacheRead += t.cache?.read || 0;
|
|
309457
309812
|
ctx.currentTurn.tokens.cacheWrite += t.cache?.write || 0;
|
|
309458
309813
|
}
|
|
@@ -310849,8 +311204,8 @@ function addQueryParameters(url5, parameters) {
|
|
|
310849
311204
|
}).join("&");
|
|
310850
311205
|
}
|
|
310851
311206
|
var urlVariableRegex = /\{[^{}}]+\}/g;
|
|
310852
|
-
function removeNonChars(
|
|
310853
|
-
return
|
|
311207
|
+
function removeNonChars(variableName2) {
|
|
311208
|
+
return variableName2.replace(/(?:^\W+)|(?:(?<!\W)\W+$)/g, "").split(/,/);
|
|
310854
311209
|
}
|
|
310855
311210
|
function extractUrlVariableNames(url5) {
|
|
310856
311211
|
const matches2 = url5.match(urlVariableRegex);
|
|
@@ -318529,7 +318884,7 @@ async function runAgentWithWatchdogs(params) {
|
|
|
318529
318884
|
const tokenQuota = reserved ? quotas.token : createTokenQuota(quotas.tokenLimit.full);
|
|
318530
318885
|
const costQuota = reserved ? quotas.cost : createCostQuota(quotas.costLimit.full);
|
|
318531
318886
|
log2.info(
|
|
318532
|
-
quotas.tokenLimit.full > 0 ? `\xBB run token quota: ${quotas.tokenLimit.full.toLocaleString("en-GB")}
|
|
318887
|
+
quotas.tokenLimit.full > 0 ? `\xBB run token quota: ${quotas.tokenLimit.full.toLocaleString("en-GB")} tokens` + (reserved ? ` (${tokenQuota.limit.toLocaleString("en-GB")} for the run, the rest reserved to deliver findings if it runs out)` : "") + "; a token read from the prompt cache counts as a tenth" : `\xBB run token quota: disabled (${RUN_TOKEN_QUOTA_INPUT}: 0)`
|
|
318533
318888
|
);
|
|
318534
318889
|
log2.info(
|
|
318535
318890
|
quotas.costLimit.full > 0 ? `\xBB run cost quota: $${quotas.costLimit.full.toFixed(2)}` + (reserved ? ` ($${costQuota.limit.toFixed(2)} for the run, the rest reserved to deliver findings if it runs out)` : "") : `\xBB run cost quota: disabled (${RUN_COST_QUOTA_INPUT}: 0)`
|
|
@@ -318627,7 +318982,7 @@ async function runAgentWithWatchdogs(params) {
|
|
|
318627
318982
|
}
|
|
318628
318983
|
|
|
318629
318984
|
// src/phases/startToolPlane.ts
|
|
318630
|
-
import { existsSync as
|
|
318985
|
+
import { existsSync as existsSync20, readdirSync as readdirSync11 } from "node:fs";
|
|
318631
318986
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
318632
318987
|
import { join as join77 } from "node:path";
|
|
318633
318988
|
|
|
@@ -319458,7 +319813,7 @@ ${instructions.user}` : null,
|
|
|
319458
319813
|
});
|
|
319459
319814
|
if (toolContext.agentId === "opencode") {
|
|
319460
319815
|
const pluginDir = join77(process.cwd(), ".opencode", "plugin");
|
|
319461
|
-
const hasPlugins =
|
|
319816
|
+
const hasPlugins = existsSync20(pluginDir) && readdirSync11(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
319462
319817
|
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
319463
319818
|
log2.info(
|
|
319464
319819
|
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|
|
@@ -319944,7 +320299,7 @@ async function run2(args2) {
|
|
|
319944
320299
|
}
|
|
319945
320300
|
|
|
319946
320301
|
// src/commands/mcp.ts
|
|
319947
|
-
import { existsSync as
|
|
320302
|
+
import { existsSync as existsSync21, statSync as statSync12 } from "node:fs";
|
|
319948
320303
|
import { isAbsolute as isAbsolute9, resolve as resolve14 } from "node:path";
|
|
319949
320304
|
var import_arg4 = __toESM(require_arg(), 1);
|
|
319950
320305
|
var SEVERITIES5 = ["critical", "high", "medium", "low", "info"];
|
|
@@ -320017,7 +320372,7 @@ async function runMcpCli(params) {
|
|
|
320017
320372
|
if (rawCwd) {
|
|
320018
320373
|
cwd = isAbsolute9(rawCwd) ? rawCwd : resolve14(process.cwd(), rawCwd);
|
|
320019
320374
|
}
|
|
320020
|
-
if (!
|
|
320375
|
+
if (!existsSync21(cwd) || !statSync12(cwd).isDirectory()) {
|
|
320021
320376
|
console.error(`--cwd is not a directory: ${cwd}`);
|
|
320022
320377
|
process.exit(1);
|
|
320023
320378
|
}
|
|
@@ -320116,7 +320471,7 @@ async function runVerifyEvidenceCli2(params) {
|
|
|
320116
320471
|
}
|
|
320117
320472
|
|
|
320118
320473
|
// src/cli.ts
|
|
320119
|
-
var VERSION14 = "0.3.
|
|
320474
|
+
var VERSION14 = "0.3.13";
|
|
320120
320475
|
var bin = basename9(process.argv[1] || "");
|
|
320121
320476
|
var PROG = bin === "pf" || bin === PRODUCT_SLUG ? bin : PRODUCT_SLUG;
|
|
320122
320477
|
var rawArgs = process.argv.slice(2);
|