infraweaver 0.3.9 → 0.3.11
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/quotaTokens.d.ts +8 -0
- package/dist/cli.mjs +1844 -1601
- package/dist/index.js +422 -182
- package/dist/internal.js +6 -4
- package/dist/mcp/crosswalk-digest.d.ts +1 -1
- package/dist/mcp/git.d.ts +19 -3
- package/dist/mcp/localContext.d.ts +9 -1
- package/dist/mcp/shell.d.ts +3 -3
- package/dist/mcp/staleFix.d.ts +13 -1
- package/dist/mcp/terraform/hardcodedSecrets.d.ts +49 -0
- package/dist/mcp/terraform/nativeRules.d.ts +1 -0
- package/dist/mcp/terraform/refactorScope.d.ts +12 -0
- package/dist/mcp/terraform/tools/scan.d.ts +1 -0
- package/dist/mcp/terraform/types.d.ts +3 -0
- package/dist/utils/config/payload.d.ts +2 -0
- package/package.json +1 -1
- package/src/agents/claude.ts +2 -1
- package/src/agents/opencode.ts +2 -1
- package/src/agents/quotaTokens.ts +25 -0
- package/src/mcp/crosswalk-digest.ts +1 -1
- package/src/mcp/crosswalk.ts +2 -0
- package/src/mcp/git.ts +44 -3
- package/src/mcp/localContext.ts +8 -0
- package/src/mcp/localServer.ts +3 -0
- package/src/mcp/moduleExtraction.ts +4 -1
- package/src/mcp/review.ts +3 -0
- package/src/mcp/shell.ts +2 -2
- package/src/mcp/staleFix.ts +21 -2
- package/src/mcp/terraform/concernResult.ts +24 -4
- package/src/mcp/terraform/hardcodedSecrets.ts +202 -0
- package/src/mcp/terraform/nativeRuleScanners.ts +8 -0
- package/src/mcp/terraform/nativeRules.ts +14 -0
- package/src/mcp/terraform/refactorScope.ts +86 -0
- package/src/mcp/terraform/standardsReport.ts +2 -0
- package/src/mcp/terraform/tools/consolidationCandidates.ts +5 -1
- package/src/mcp/terraform/tools/normalizationCandidates.ts +5 -1
- package/src/mcp/terraform/types.ts +3 -0
- package/src/modes/refactor.ts +2 -0
- package/src/modes/refresh-remediation.ts +1 -1
- package/src/modes/remediate-and-refactor.ts +1 -1
- package/src/modes/remediate.ts +2 -2
- package/src/utils/config/payload.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -84161,14 +84161,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
84161
84161
|
} else if (node3.nodeType === 1) {
|
|
84162
84162
|
replacement = replacementForNode.call(self2, node3);
|
|
84163
84163
|
}
|
|
84164
|
-
return
|
|
84164
|
+
return join74(output2, replacement);
|
|
84165
84165
|
}, "");
|
|
84166
84166
|
}
|
|
84167
84167
|
function postProcess(output2) {
|
|
84168
84168
|
var self2 = this;
|
|
84169
84169
|
this.rules.forEach(function(rule) {
|
|
84170
84170
|
if (typeof rule.append === "function") {
|
|
84171
|
-
output2 =
|
|
84171
|
+
output2 = join74(output2, rule.append(self2.options));
|
|
84172
84172
|
}
|
|
84173
84173
|
});
|
|
84174
84174
|
return output2.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -84180,7 +84180,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
84180
84180
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
84181
84181
|
return whitespace.leading + rule.replacement(content, node3, this.options) + whitespace.trailing;
|
|
84182
84182
|
}
|
|
84183
|
-
function
|
|
84183
|
+
function join74(output2, replacement) {
|
|
84184
84184
|
var s1 = trimTrailingNewlines(output2);
|
|
84185
84185
|
var s2 = trimLeadingNewlines(replacement);
|
|
84186
84186
|
var nls = Math.max(output2.length - s1.length, replacement.length - s2.length);
|
|
@@ -97300,7 +97300,7 @@ var require_polyfill = __commonJS({
|
|
|
97300
97300
|
var {
|
|
97301
97301
|
dirname: dirname15,
|
|
97302
97302
|
isAbsolute: isAbsolute7,
|
|
97303
|
-
join:
|
|
97303
|
+
join: join74,
|
|
97304
97304
|
parse: parse8,
|
|
97305
97305
|
resolve: resolve11,
|
|
97306
97306
|
sep: sep11,
|
|
@@ -97557,8 +97557,8 @@ var require_polyfill = __commonJS({
|
|
|
97557
97557
|
const dir = await readdir2(src);
|
|
97558
97558
|
for (let i = 0; i < dir.length; i++) {
|
|
97559
97559
|
const item = dir[i];
|
|
97560
|
-
const srcItem =
|
|
97561
|
-
const destItem =
|
|
97560
|
+
const srcItem = join74(src, item);
|
|
97561
|
+
const destItem = join74(dest, item);
|
|
97562
97562
|
const { destStat } = await checkPaths2(srcItem, destItem, opts);
|
|
97563
97563
|
await startCopy(destStat, srcItem, destItem, opts);
|
|
97564
97564
|
}
|
|
@@ -97631,7 +97631,7 @@ var require_cp = __commonJS({
|
|
|
97631
97631
|
// node_modules/.pnpm/@npmcli+fs@6.0.0/node_modules/@npmcli/fs/lib/with-temp-dir.js
|
|
97632
97632
|
var require_with_temp_dir = __commonJS({
|
|
97633
97633
|
"node_modules/.pnpm/@npmcli+fs@6.0.0/node_modules/@npmcli/fs/lib/with-temp-dir.js"(exports2, module) {
|
|
97634
|
-
var { join:
|
|
97634
|
+
var { join: join74, sep: sep11 } = __require("path");
|
|
97635
97635
|
var getOptions2 = require_get_options();
|
|
97636
97636
|
var { mkdir: mkdir5, mkdtemp, rm: rm2 } = __require("fs/promises");
|
|
97637
97637
|
var withTempDir = async (root, fn2, opts) => {
|
|
@@ -97639,7 +97639,7 @@ var require_with_temp_dir = __commonJS({
|
|
|
97639
97639
|
copy: ["tmpPrefix"]
|
|
97640
97640
|
});
|
|
97641
97641
|
await mkdir5(root, { recursive: true });
|
|
97642
|
-
const target = await mkdtemp(
|
|
97642
|
+
const target = await mkdtemp(join74(`${root}${sep11}`, options.tmpPrefix || ""));
|
|
97643
97643
|
let err;
|
|
97644
97644
|
let result;
|
|
97645
97645
|
try {
|
|
@@ -97664,13 +97664,13 @@ var require_with_temp_dir = __commonJS({
|
|
|
97664
97664
|
var require_readdir_scoped = __commonJS({
|
|
97665
97665
|
"node_modules/.pnpm/@npmcli+fs@6.0.0/node_modules/@npmcli/fs/lib/readdir-scoped.js"(exports2, module) {
|
|
97666
97666
|
var { readdir: readdir2 } = __require("fs/promises");
|
|
97667
|
-
var { join:
|
|
97667
|
+
var { join: join74 } = __require("path");
|
|
97668
97668
|
var readdirScoped = async (dir) => {
|
|
97669
97669
|
const results = [];
|
|
97670
97670
|
for (const item of await readdir2(dir)) {
|
|
97671
97671
|
if (item.startsWith("@")) {
|
|
97672
|
-
for (const scopedItem of await readdir2(
|
|
97673
|
-
results.push(
|
|
97672
|
+
for (const scopedItem of await readdir2(join74(dir, item))) {
|
|
97673
|
+
results.push(join74(item, scopedItem));
|
|
97674
97674
|
}
|
|
97675
97675
|
} else {
|
|
97676
97676
|
results.push(item);
|
|
@@ -97685,7 +97685,7 @@ var require_readdir_scoped = __commonJS({
|
|
|
97685
97685
|
// node_modules/.pnpm/@npmcli+fs@6.0.0/node_modules/@npmcli/fs/lib/move-file.js
|
|
97686
97686
|
var require_move_file = __commonJS({
|
|
97687
97687
|
"node_modules/.pnpm/@npmcli+fs@6.0.0/node_modules/@npmcli/fs/lib/move-file.js"(exports2, module) {
|
|
97688
|
-
var { dirname: dirname15, join:
|
|
97688
|
+
var { dirname: dirname15, join: join74, resolve: resolve11, relative: relative8, isAbsolute: isAbsolute7 } = __require("path");
|
|
97689
97689
|
var fs12 = __require("fs/promises");
|
|
97690
97690
|
var pathExists2 = async (path15) => {
|
|
97691
97691
|
try {
|
|
@@ -97715,7 +97715,7 @@ var require_move_file = __commonJS({
|
|
|
97715
97715
|
if (sourceStat.isDirectory()) {
|
|
97716
97716
|
const files = await fs12.readdir(source);
|
|
97717
97717
|
await Promise.all(files.map(
|
|
97718
|
-
(file2) => moveFile(
|
|
97718
|
+
(file2) => moveFile(join74(source, file2), join74(destination, file2), options, false, symlinks)
|
|
97719
97719
|
));
|
|
97720
97720
|
} else if (sourceStat.isSymbolicLink()) {
|
|
97721
97721
|
symlinks.push({ source, destination });
|
|
@@ -160384,7 +160384,7 @@ var init_cacheTwirpClient = __esm({
|
|
|
160384
160384
|
});
|
|
160385
160385
|
|
|
160386
160386
|
// node_modules/.pnpm/@actions+cache@6.3.0_supports-color@7.2.0/node_modules/@actions/cache/lib/internal/tar.js
|
|
160387
|
-
import { existsSync as
|
|
160387
|
+
import { existsSync as existsSync17, writeFileSync as writeFileSync21 } from "fs";
|
|
160388
160388
|
import * as path13 from "path";
|
|
160389
160389
|
function getTarPath() {
|
|
160390
160390
|
return __awaiter18(this, void 0, void 0, function* () {
|
|
@@ -160394,7 +160394,7 @@ function getTarPath() {
|
|
|
160394
160394
|
const systemTar = SystemTarPathOnWindows;
|
|
160395
160395
|
if (gnuTar) {
|
|
160396
160396
|
return { path: gnuTar, type: ArchiveToolType.GNU };
|
|
160397
|
-
} else if (
|
|
160397
|
+
} else if (existsSync17(systemTar)) {
|
|
160398
160398
|
return { path: systemTar, type: ArchiveToolType.BSD };
|
|
160399
160399
|
}
|
|
160400
160400
|
break;
|
|
@@ -258516,7 +258516,7 @@ var declare = ark.declare;
|
|
|
258516
258516
|
import { createHash as createHash4 } from "node:crypto";
|
|
258517
258517
|
|
|
258518
258518
|
// src/mcp/crosswalk-digest.ts
|
|
258519
|
-
var EXPECTED_CROSSWALK_DIGEST = "
|
|
258519
|
+
var EXPECTED_CROSSWALK_DIGEST = "f583001997c4c69f84f189a14e69e4f6eb2c52bddc04ca7f404286b5280d1ff4";
|
|
258520
258520
|
|
|
258521
258521
|
// node_modules/.pnpm/@infraweaver-io+crosswalk@1.2.0/node_modules/@infraweaver-io/crosswalk/dist/frameworks.js
|
|
258522
258522
|
var FRAMEWORKS = [
|
|
@@ -259020,9 +259020,9 @@ function boxString(text, options) {
|
|
|
259020
259020
|
if (line.length <= maxWidth - padding * 2) {
|
|
259021
259021
|
wrappedLines.push(line);
|
|
259022
259022
|
} else {
|
|
259023
|
-
const
|
|
259023
|
+
const words2 = line.split(" ");
|
|
259024
259024
|
let currentLine = "";
|
|
259025
|
-
for (const word of
|
|
259025
|
+
for (const word of words2) {
|
|
259026
259026
|
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
259027
259027
|
if (testLine.length <= maxWidth - padding * 2) {
|
|
259028
259028
|
currentLine = testLine;
|
|
@@ -259203,7 +259203,7 @@ import { randomBytes as randomBytes2 } from "node:crypto";
|
|
|
259203
259203
|
// package.json
|
|
259204
259204
|
var package_default = {
|
|
259205
259205
|
name: "infraweaver",
|
|
259206
|
-
version: "0.3.
|
|
259206
|
+
version: "0.3.11",
|
|
259207
259207
|
description: "GitHub Action that remediates Terraform to best practices and opens one scoped pull request per concern.",
|
|
259208
259208
|
keywords: [
|
|
259209
259209
|
"ai-coding-agent",
|
|
@@ -260156,6 +260156,7 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
260156
260156
|
var NATIVE_SOURCE = TOOL_NAME;
|
|
260157
260157
|
var SECRET_PERSISTED_TO_STATE = "secret-persisted-to-state";
|
|
260158
260158
|
var COMMITTED_STATE_FILE = "committed-state-file";
|
|
260159
|
+
var HARDCODED_SECRET_ARGUMENT = "hardcoded-secret-argument";
|
|
260159
260160
|
var IMPERATIVE_ACTION_TRIGGER = "imperative-action-trigger";
|
|
260160
260161
|
var DYNAMIC_DESTROY_PROTECTION = "dynamic-destroy-protection";
|
|
260161
260162
|
var DEPRECATED_STATE_LOCKING = "deprecated-state-locking";
|
|
@@ -260191,6 +260192,14 @@ var NATIVE_RULES = [
|
|
|
260191
260192
|
asserts: "git tracks a `.tfstate` / `.tfstate.backup` file at this path, so the plaintext record of every attribute Terraform read back from the provider is in the history of every clone and fork",
|
|
260192
260193
|
doesNotAssert: "that the state contains a live credential, or that anyone has read it \u2014 the file is never opened; the finding is that state is under version control, and what it recorded is for the owner to say"
|
|
260193
260194
|
},
|
|
260195
|
+
{
|
|
260196
|
+
id: HARDCODED_SECRET_ARGUMENT,
|
|
260197
|
+
title: "Credential written into the configuration as a literal",
|
|
260198
|
+
band: "verified",
|
|
260199
|
+
category: "security",
|
|
260200
|
+
asserts: "an argument, map key or variable whose name says it holds a credential (`password`, `token`, `client_secret`, `api_key`, \u2026) is assigned a string literal, so the value is in the repository and in its history",
|
|
260201
|
+
doesNotAssert: "that the value is a live credential \u2014 it may be a test value; the finding is that a credential slot is filled from the code rather than at run time"
|
|
260202
|
+
},
|
|
260194
260203
|
{
|
|
260195
260204
|
id: IMPERATIVE_ACTION_TRIGGER,
|
|
260196
260205
|
title: "Imperative side-effect invoked from declarative IaC (action / action_trigger)",
|
|
@@ -261515,7 +261524,9 @@ var THEME_LOCKED = /* @__PURE__ */ new Map([
|
|
|
261515
261524
|
// credentials", and state in git is the canonical failure of it. It deliberately
|
|
261516
261525
|
// does NOT also cite `encryption-at-rest`: whether the repository is encrypted is
|
|
261517
261526
|
// not what the finding establishes.
|
|
261518
|
-
["committed-state-file", "secrets-management"]
|
|
261527
|
+
["committed-state-file", "secrets-management"],
|
|
261528
|
+
// a credential in the code is the same CIS 16.4 failure one step earlier than state in git
|
|
261529
|
+
["hardcoded-secret-argument", "secrets-management"]
|
|
261519
261530
|
]);
|
|
261520
261531
|
function bareRuleId(ruleId) {
|
|
261521
261532
|
const colon = ruleId.indexOf(":");
|
|
@@ -266086,6 +266097,128 @@ function scanDialect(files) {
|
|
|
266086
266097
|
return concerns;
|
|
266087
266098
|
}
|
|
266088
266099
|
|
|
266100
|
+
// src/mcp/terraform/hardcodedSecrets.ts
|
|
266101
|
+
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
266102
|
+
"password",
|
|
266103
|
+
"passwd",
|
|
266104
|
+
"pwd",
|
|
266105
|
+
"passphrase",
|
|
266106
|
+
"secret",
|
|
266107
|
+
"token",
|
|
266108
|
+
"auth",
|
|
266109
|
+
"apikey",
|
|
266110
|
+
"credential",
|
|
266111
|
+
"credentials"
|
|
266112
|
+
]);
|
|
266113
|
+
var SECRET_KEY_PREFIXES = /* @__PURE__ */ new Set(["api", "secret", "private", "signing"]);
|
|
266114
|
+
var NOT_SECRET_TOKENS = /* @__PURE__ */ new Set(["creation", "idempotency", "client", "request", "continuation"]);
|
|
266115
|
+
function words(name) {
|
|
266116
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[_-]+/).filter(Boolean);
|
|
266117
|
+
}
|
|
266118
|
+
function isSecretName(name) {
|
|
266119
|
+
const w = words(name);
|
|
266120
|
+
const last = w.at(-1);
|
|
266121
|
+
const prev = w.at(-2);
|
|
266122
|
+
if (last === void 0) return false;
|
|
266123
|
+
if (last === "key") return prev !== void 0 && SECRET_KEY_PREFIXES.has(prev);
|
|
266124
|
+
if (last === "token" && prev !== void 0 && NOT_SECRET_TOKENS.has(prev)) return false;
|
|
266125
|
+
return SECRET_WORDS.has(last);
|
|
266126
|
+
}
|
|
266127
|
+
function isPlaceholder(inner) {
|
|
266128
|
+
return inner.trim() === "" || /^[*xX.]+$/.test(inner) || /^<[^>]*>$/.test(inner) || // the dummy credentials offline plans and LocalStack are given (`secret_key = "test"`)
|
|
266129
|
+
/^(?:test|mock|fake|dummy|fixture|example|placeholder|sample)(?:[_-].*)?$/i.test(inner) || // a token's lifetime unit: `token_validity_units { access_token = "hours" }`
|
|
266130
|
+
/^(?:seconds|minutes|hours|days)$/.test(inner);
|
|
266131
|
+
}
|
|
266132
|
+
var HEADER = /^(resource|data|provider|module|locals|variable|output|terraform)\b([^{]*)\{\s*$/;
|
|
266133
|
+
var ASSIGNMENT = /^\s*(?:"([A-Za-z_][\w-]*)"|([A-Za-z_][\w-]*))\s*=(?!=)\s*(.*)$/;
|
|
266134
|
+
var HEREDOC = /<<-?([A-Za-z_]\w*)\s*$/;
|
|
266135
|
+
var INLINE_PAIR = /(?:"([A-Za-z_][\w-]*)"|\b([A-Za-z_][\w-]*))\s*=(?!=)\s*("(?:[^"\\]|\\.)*")/g;
|
|
266136
|
+
function literalValue(raw2) {
|
|
266137
|
+
const value2 = stripTrailingComment(raw2).replace(/,$/, "").trim();
|
|
266138
|
+
return isStringLiteral(value2) && !isPlaceholder(value2.slice(1, -1));
|
|
266139
|
+
}
|
|
266140
|
+
function findHardcodedSecrets(hcl2) {
|
|
266141
|
+
const out = [];
|
|
266142
|
+
let holder = null;
|
|
266143
|
+
let heredoc = null;
|
|
266144
|
+
let inComment = false;
|
|
266145
|
+
const lines = hcl2.split("\n");
|
|
266146
|
+
for (let i = 0; i < lines.length; i++) {
|
|
266147
|
+
const line = lines[i].replace(/\r$/, "");
|
|
266148
|
+
if (heredoc !== null) {
|
|
266149
|
+
if (line.trim() === heredoc) heredoc = null;
|
|
266150
|
+
continue;
|
|
266151
|
+
}
|
|
266152
|
+
if (inComment) {
|
|
266153
|
+
if (line.includes("*/")) inComment = false;
|
|
266154
|
+
continue;
|
|
266155
|
+
}
|
|
266156
|
+
const trimmed = line.trimStart();
|
|
266157
|
+
if (trimmed.startsWith("/*")) {
|
|
266158
|
+
inComment = !trimmed.includes("*/");
|
|
266159
|
+
continue;
|
|
266160
|
+
}
|
|
266161
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
|
|
266162
|
+
const header = HEADER.exec(line);
|
|
266163
|
+
if (header) {
|
|
266164
|
+
holder = `${header[1]}${header[2].trimEnd() ? ` ${header[2].trim()}` : ""}`;
|
|
266165
|
+
continue;
|
|
266166
|
+
}
|
|
266167
|
+
if (line.startsWith("}")) {
|
|
266168
|
+
holder = null;
|
|
266169
|
+
continue;
|
|
266170
|
+
}
|
|
266171
|
+
const opens = HEREDOC.exec(line);
|
|
266172
|
+
if (opens) heredoc = opens[1];
|
|
266173
|
+
if (holder === null || opens) continue;
|
|
266174
|
+
const m = ASSIGNMENT.exec(line);
|
|
266175
|
+
if (!m) continue;
|
|
266176
|
+
const name = m[1] ?? m[2];
|
|
266177
|
+
const value2 = m[3];
|
|
266178
|
+
if (holder.startsWith("variable ")) {
|
|
266179
|
+
const variable = /^variable\s+"?([\w-]+)"?/.exec(holder)?.[1] ?? "";
|
|
266180
|
+
if (name === "default" && isSecretName(variable) && literalValue(value2)) {
|
|
266181
|
+
out.push({ name: variable, holder, line: i + 1 });
|
|
266182
|
+
}
|
|
266183
|
+
continue;
|
|
266184
|
+
}
|
|
266185
|
+
if (holder.startsWith("output ") || holder.startsWith("terraform")) continue;
|
|
266186
|
+
if (isSecretName(name) && literalValue(value2)) {
|
|
266187
|
+
out.push({ name, holder, line: i + 1 });
|
|
266188
|
+
continue;
|
|
266189
|
+
}
|
|
266190
|
+
for (const pair of value2.matchAll(INLINE_PAIR)) {
|
|
266191
|
+
const key = pair[1] ?? pair[2];
|
|
266192
|
+
if (isSecretName(key) && literalValue(pair[3])) {
|
|
266193
|
+
out.push({ name: key, holder, line: i + 1 });
|
|
266194
|
+
}
|
|
266195
|
+
}
|
|
266196
|
+
}
|
|
266197
|
+
return out;
|
|
266198
|
+
}
|
|
266199
|
+
function scanHardcodedSecrets(files) {
|
|
266200
|
+
const concerns = [];
|
|
266201
|
+
for (const { path: path15, hcl: hcl2 } of files) {
|
|
266202
|
+
for (const hit of findHardcodedSecrets(hcl2)) {
|
|
266203
|
+
concerns.push(
|
|
266204
|
+
buildConcern({
|
|
266205
|
+
source: NATIVE_SOURCE,
|
|
266206
|
+
rule: HARDCODED_SECRET_ARGUMENT,
|
|
266207
|
+
file: path15,
|
|
266208
|
+
line: hit.line,
|
|
266209
|
+
// a credential in the repository is readable by everyone who can read
|
|
266210
|
+
// it, and stays in the history after it is edited out
|
|
266211
|
+
severity: "high",
|
|
266212
|
+
category: "security",
|
|
266213
|
+
evidence: `\`${hit.name}\` in \`${hit.holder}\` is a string literal, so the credential it holds is committed to the repository (value not quoted here)`,
|
|
266214
|
+
remediation_hint: "Rotate the credential first \u2014 it stays in the branch history after the line is changed. Then pass it in at run time: a `sensitive` variable with no default, or a read from a secret store (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager)."
|
|
266215
|
+
})
|
|
266216
|
+
);
|
|
266217
|
+
}
|
|
266218
|
+
}
|
|
266219
|
+
return concerns;
|
|
266220
|
+
}
|
|
266221
|
+
|
|
266089
266222
|
// src/mcp/roots.ts
|
|
266090
266223
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
266091
266224
|
import { join as join10 } from "node:path";
|
|
@@ -268065,6 +268198,12 @@ var NATIVE_RULE_SCANNERS = [
|
|
|
268065
268198
|
// one. See [[stateFiles]].
|
|
268066
268199
|
scan: (ctx) => scanCommittedStateFiles(ctx.cwd)
|
|
268067
268200
|
},
|
|
268201
|
+
{
|
|
268202
|
+
name: "hardcoded-secrets",
|
|
268203
|
+
rules: [HARDCODED_SECRET_ARGUMENT],
|
|
268204
|
+
// Per file: the argument's name and its literal are both on the line itself.
|
|
268205
|
+
scan: (ctx) => ({ concerns: scanHardcodedSecrets(ctx.files) })
|
|
268206
|
+
},
|
|
268068
268207
|
{
|
|
268069
268208
|
name: "write-only",
|
|
268070
268209
|
rules: [SECRET_PERSISTED_TO_STATE],
|
|
@@ -268655,8 +268794,8 @@ function matchRoot(file2, keys, roots) {
|
|
|
268655
268794
|
const dir = dirOf2(file2);
|
|
268656
268795
|
const inside = candidates.filter((r) => r.dir !== "" && isWithin(dir, r.dir)).sort((a, b) => b.dir.length - a.dir.length);
|
|
268657
268796
|
if (inside[0]) return { root: inside[0].dir };
|
|
268658
|
-
const
|
|
268659
|
-
const scored = candidates.map((r) => ({ dir: r.dir, score: tokens(r.dir).filter((t) =>
|
|
268797
|
+
const words2 = new Set(tokens(file2));
|
|
268798
|
+
const scored = candidates.map((r) => ({ dir: r.dir, score: tokens(r.dir).filter((t) => words2.has(t)).length })).sort((a, b) => b.score - a.score);
|
|
268660
268799
|
if (scored[0] && scored[0].score > 0 && scored[0].score !== scored[1]?.score) {
|
|
268661
268800
|
return { root: scored[0].dir };
|
|
268662
268801
|
}
|
|
@@ -274140,7 +274279,11 @@ function isRemediationBranch(branch) {
|
|
|
274140
274279
|
return REMEDIATION_BRANCH.test(branch);
|
|
274141
274280
|
}
|
|
274142
274281
|
function groupIdFromBranch(branch) {
|
|
274143
|
-
return branch.match(/^remediate\/(
|
|
274282
|
+
return branch.match(/^remediate\/(.+?)(?:--.+)?$/)?.[1] ?? null;
|
|
274283
|
+
}
|
|
274284
|
+
function remediationBranchSuffix(base, defaultBranch) {
|
|
274285
|
+
if (!base || !defaultBranch || base === defaultBranch) return "";
|
|
274286
|
+
return `--${base.replace(/[^A-Za-z0-9._-]+/g, "-")}`;
|
|
274144
274287
|
}
|
|
274145
274288
|
function matchesBotIdentity(identity) {
|
|
274146
274289
|
const normalized = identity.replace(/\[bot\]$/i, "").toLowerCase();
|
|
@@ -276493,6 +276636,8 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
276493
276636
|
- \`${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.
|
|
276494
276637
|
- \`${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.
|
|
276495
276638
|
|
|
276639
|
+
**A result marked \`report_only\` is an observation, not work.** The repository is a module other configurations call (\`report_only_reason\` says why), and restructuring it 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\`.
|
|
276640
|
+
|
|
276496
276641
|
**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.
|
|
276497
276642
|
|
|
276498
276643
|
Pick the highest-value refactor across ALL THREE detectors. Only when they ALL come back empty \u2014 or all that remains is a cluster in a near-group root \u2014 is there genuinely nothing to do: call \`${t("report_progress")}\` with an ACCURATE message \u2014 e.g. "No behaviour-preserving refactor found: no extraction clusters and no idiomatic-normalisation candidates." \u2014 and **stop**. Two anti-patterns to avoid in that message: (i) never call a repo "already idiomatic" while normalisation candidates remain; (ii) when what's left is behaviour-CHANGING modernisation \u2014 a deprecated-resource swap (\`aws_launch_configuration\`\u2192\`aws_launch_template\`, \`aws_elb\`\u2192\`aws_lb\`, \`template_file\`\u2192\`templatefile()\`) \u2014 do NOT report "nothing to refactor": that is real work, but it ALTERS the resource set, so it belongs to Remediate. Say so explicitly rather than implying the code is clean.
|
|
@@ -276579,7 +276724,7 @@ This mode keeps already-open ${PRODUCT_NAME} remediation PRs healthy. A remediat
|
|
|
276579
276724
|
- **resolved-on-base? \u2192 close it**: look for a group in step 3's scan whose \`id\` equals the PR's \`group_id\` (for a by-rule/batch PR, match on the concern ids it covered). If **no** current group/concern corresponds, the concern was already fixed on the base (a human fix, or a base change removed the file) \u2014 the PR is redundant. Call \`${t("close_pull_request")}\` with a one-line \`comment\` explaining it's resolved on the base. Do not push anything for this PR.
|
|
276580
276725
|
- **still present \u2192 re-derive the fix on the current base**:
|
|
276581
276726
|
- **read WHY the CI failed, before changing anything** (\`checks_failing\` PRs only): when step 2 gave the PR a \`failing_check_suite_id\`, call \`${t("get_check_suite_logs")}\` with that id FIRST. Re-deriving without reading the failure is exactly how attempt 2 and attempt 3 become identical repeats of attempt 1 \u2014 same fix, same red check, three times, until the retry cap escalates a PR nobody learned anything about. Use the returned \`failed_steps\` and log excerpt to decide what to do DIFFERENTLY this attempt. If the log shows the failure is not ours \u2014 a broken base, a missing credential, an unrelated flaky job \u2014 do not spend an attempt re-deriving: add \`needs-human\` (\`${t("add_labels")}\`), post ONE comment naming the failing step, and move on. Record the cause in the PR body/comment you write below, so the next attempt and any human start from what you found. When the id is absent, or the tool reports no failed runs, say so in the comment and continue with the plain re-derive rather than guessing.
|
|
276582
|
-
- **branch**: recreate the remediation branch at the current base HEAD via \`${t("git")}\` (\`git checkout -B
|
|
276727
|
+
- **branch**: recreate the remediation branch at the current base HEAD via \`${t("git")}\` (\`git({ command: "checkout", args: ["-B", <the PR's branch>] })\`, the \`branch\` step 2 returned) \u2014 \`-B\` force-resets it to the just-scanned base so the diff is only your fix.
|
|
276583
276728
|
- **fix \u2192 validate \u2192 plan \u2192 keep tests consistent \u2192 prove it**: apply the minimal fix for that group exactly as in **Remediate** step 4 (same \`${t("terraform_validate")}\`, \`${t("terraform_plan")}\`, \`${t("terraform_module_tests")}\`, and \`${t("terraform_verify_remediation")}\` gates, and the same guardrails \u2014 never open/keep a PR whose validate didn't pass, abandon a group that would destroy a stateful resource, etc.).
|
|
276584
276729
|
- **force-update the PR branch**: \`${t("push_branch")}\` with \`force: true\` (the PR already exists; force-updating its branch refreshes it in place \u2014 do NOT open a second PR). The Terraform-only / secret / destroy guardrails still run at push time.
|
|
276585
276730
|
- **refresh the body**: \`${t("update_pull_request_body")}\` rebuilt from the fresh \`${t("terraform_verify_remediation")}\` result (the Remediation PR format below), and add a one-line note that it was rebased onto the current base (\`<short-sha>\`) on this run.
|
|
@@ -276624,8 +276769,8 @@ function remediateMode(t) {
|
|
|
276624
276769
|
|
|
276625
276770
|
4. **for the chosen group**:
|
|
276626
276771
|
- **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.
|
|
276627
|
-
- **idempotency**: the remediation branch is \`remediate/<group-id
|
|
276628
|
-
- **branch**: create
|
|
276772
|
+
- **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.
|
|
276773
|
+
- **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.
|
|
276629
276774
|
- **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.
|
|
276630
276775
|
- **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.
|
|
276631
276776
|
- **reuse a proven fix (optional, do this BEFORE editing)**: call \`${t("terraform_fix_memory")}\`. When proven-fix memory is enabled it returns any patterns whose \`finding_type\` matches this group's concerns \u2014 each a transformation that already passed a \`\u2717\u2192\u2713\` proof on this repo or elsewhere in the fleet, with its \`remediation_hint\` + a \`before_example\`. Use a matching pattern as your STARTING POINT (it saves you rediscovering the approach), but ADAPT it to this repo's own resources \u2014 it is a prior, never a patch to paste. You MUST still run \`${t("terraform_verify_remediation")}\` to prove \`\u2717\u2192\u2713\` here; a pattern is never blind-applied and never substitutes for the per-target proof. Returns an empty list (never an error) when memory is off or nothing matches \u2014 then fix from first principles as usual.
|
|
@@ -276685,7 +276830,7 @@ This mode composes the **Remediate** and **Refactor** verbs into a single run. I
|
|
|
276685
276830
|
- **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.
|
|
276686
276831
|
|
|
276687
276832
|
3. **REFACTOR (phase 2) \u2014 modularise on top of the committed fix**: follow the **Refactor** mode's flow:
|
|
276688
|
-
- \`${t("module_extraction_candidates")}\` / \`${t("terraform_normalization_candidates")}\` \u2192 pick one behaviour-preserving refactor. 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\`).
|
|
276833
|
+
- \`${t("module_extraction_candidates")}\` / \`${t("terraform_normalization_candidates")}\` \u2192 pick one behaviour-preserving refactor; a result marked \`report_only\` (the repository is 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\`).
|
|
276689
276834
|
- 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")}\`).
|
|
276690
276835
|
- \`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.
|
|
276691
276836
|
- **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.
|
|
@@ -277251,7 +277396,7 @@ async function ensurePackageManager(params) {
|
|
|
277251
277396
|
// src/mcp/shell.ts
|
|
277252
277397
|
var ShellParams = type({
|
|
277253
277398
|
command: "string",
|
|
277254
|
-
description: "string",
|
|
277399
|
+
"description?": "string",
|
|
277255
277400
|
"timeout?": type.number.describe(
|
|
277256
277401
|
"Timeout in MILLISECONDS (not seconds). Default 30000 (30s), max 120000 (2m). e.g. timeout: 180000 for 3 minutes; timeout: 180 means 180ms and will kill the process almost immediately."
|
|
277257
277402
|
),
|
|
@@ -277486,7 +277631,7 @@ function ShellTool(ctx) {
|
|
|
277486
277631
|
return tool({
|
|
277487
277632
|
name: "shell",
|
|
277488
277633
|
timeoutMs: 12e4,
|
|
277489
|
-
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
|
277634
|
+
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets. NOT for git: any command running \`git\` is refused here \u2014 use the git tool (and push_branch / git_fetch).
|
|
277490
277635
|
|
|
277491
277636
|
Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
|
|
277492
277637
|
|
|
@@ -278232,18 +278377,35 @@ function spillGitOutput(params) {
|
|
|
278232
278377
|
};
|
|
278233
278378
|
}
|
|
278234
278379
|
var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
|
|
278380
|
+
var commandLinePattern = regex("^git\\s+[a-z][a-z0-9-]*(?:\\s.*)?$");
|
|
278235
278381
|
var Git = type({
|
|
278236
|
-
command: type(subcommandPattern).describe("Git command (e.g., 'status', 'log', 'diff')"),
|
|
278382
|
+
command: type(subcommandPattern).or(type(commandLinePattern)).describe("Git command (e.g., 'status', 'log', 'diff')").optional(),
|
|
278237
278383
|
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
278238
278384
|
});
|
|
278385
|
+
function splitWords(line) {
|
|
278386
|
+
return [...line.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)].map((m) => m[1] ?? m[2] ?? m[3] ?? "");
|
|
278387
|
+
}
|
|
278388
|
+
function normalizeGitCall(params) {
|
|
278389
|
+
let words2;
|
|
278390
|
+
if (params.command === void 0) words2 = [...params.args ?? []];
|
|
278391
|
+
else if (/^git\s/.test(params.command))
|
|
278392
|
+
words2 = [...splitWords(params.command).slice(1), ...params.args ?? []];
|
|
278393
|
+
else return { command: params.command, args: params.args ?? [] };
|
|
278394
|
+
const [command, ...args2] = words2[0] === "git" ? words2.slice(1) : words2;
|
|
278395
|
+
if (command === void 0 || !/^[a-z][a-z0-9-]*$/.test(command)) {
|
|
278396
|
+
throw new Error(
|
|
278397
|
+
`command must be a Git subcommand such as 'status' or 'checkout' (was "${command ?? ""}")`
|
|
278398
|
+
);
|
|
278399
|
+
}
|
|
278400
|
+
return { command, args: args2 };
|
|
278401
|
+
}
|
|
278239
278402
|
function GitTool(ctx) {
|
|
278240
278403
|
return tool({
|
|
278241
278404
|
name: "git",
|
|
278242
278405
|
description: 'Run a git subcommand. `command` is the subcommand ONLY \u2014 never repeat it inside `args`. `args` is optional; omit it entirely for no-flag invocations like plain `git status`. Example: `git({ command: "status" })` for plain `git status`. Example: `git({ command: "log", args: ["--oneline", "-n", "20"] })`. Example: `git({ command: "diff", args: ["--merge-base", "origin/main"] })` \u2014 merge-base diff including uncommitted edits (single MCP call). Example: `git({ command: "diff", args: ["origin/main...HEAD"] })` \u2014 three-dot, committed-only changes vs merge-base. For PR-scope diffs ALWAYS use `--merge-base <base>` or three-dot `<base>...HEAD`. Bare `<base>` and two-dot `<base>..HEAD` are symmetric (working-tree-or-HEAD vs ref): when your branch is behind `<base>` they include the inverse of every commit on `<base>` you lack \u2014 pure noise, and this tool will reject those forms when the divergence is detected. Output >50K chars is spilled to a tmp file; the tool returns a head preview + path you can `read` selectively. For push/fetch, use the dedicated MCP tools (push_branch, git_fetch). git pull is not available \u2014 use git_fetch then this tool with command \'merge\'.',
|
|
278243
278406
|
parameters: Git,
|
|
278244
278407
|
execute: execute(async (params) => {
|
|
278245
|
-
const command = params
|
|
278246
|
-
const args2 = params.args ?? [];
|
|
278408
|
+
const { command, args: args2 } = normalizeGitCall(params);
|
|
278247
278409
|
if (args2[0]?.toLowerCase() === command.toLowerCase()) {
|
|
278248
278410
|
throw new Error(
|
|
278249
278411
|
`git ${command}: '${args2[0]}' duplicates the subcommand \u2014 drop args[0] (the subcommand only belongs in 'command'). git would otherwise parse it as a pathspec and silently return empty/clean output when nothing matches. if you really meant a pathspec named '${args2[0]}', use args: ["--", "${args2[0]}"].`
|
|
@@ -279431,6 +279593,7 @@ var Inputs = type({
|
|
|
279431
279593
|
// org-repo | author | third-party). Structured scope, not prompt text — see
|
|
279432
279594
|
// parseRefactorSource / withRefactorSource. Gates the behaviour-altering b.4.
|
|
279433
279595
|
"refactor_source?": type.string.or("undefined"),
|
|
279596
|
+
"refactor_examples?": type.string.or("undefined"),
|
|
279434
279597
|
"review_instructions?": type.string.or("undefined"),
|
|
279435
279598
|
"fp_filtering_instructions?": type.string.or("undefined"),
|
|
279436
279599
|
"remediation_instructions?": type.string.or("undefined"),
|
|
@@ -279508,6 +279671,7 @@ function resolveNonPromptInputs() {
|
|
|
279508
279671
|
registry_token: getInput("registry_token") || void 0,
|
|
279509
279672
|
registry_host: getInput("registry_host") || void 0,
|
|
279510
279673
|
refactor_source: getInput("refactor_source") || void 0,
|
|
279674
|
+
refactor_examples: getInput("refactor_examples") || void 0,
|
|
279511
279675
|
review_instructions: getInput("review_instructions") || void 0,
|
|
279512
279676
|
fp_filtering_instructions: getInput("fp_filtering_instructions") || void 0,
|
|
279513
279677
|
remediation_instructions: getInput("remediation_instructions") || void 0,
|
|
@@ -279809,6 +279973,8 @@ function resolvePayload(resolvedPromptInput, repoSettings, orgRequiredControls =
|
|
|
279809
279973
|
// Refactor mode for the b.4 consent gate. undefined → auto. Action-input/
|
|
279810
279974
|
// trusted-dispatch only (not the untrusted repo file).
|
|
279811
279975
|
refactorSource,
|
|
279976
|
+
// opt in to refactoring `examples/` roots, which Refactor leaves alone by default
|
|
279977
|
+
refactorExamples: parseBooleanInput(inputs.refactor_examples),
|
|
279812
279978
|
// a `@infraweaver fix …` command parsed from the triggering comment
|
|
279813
279979
|
// body (the raw prompt), scoping the run to a specific concern/severity/file.
|
|
279814
279980
|
// null when the prompt isn't a recognised command.
|
|
@@ -282069,6 +282235,10 @@ function annotateConcerns(rawConcerns, opts) {
|
|
|
282069
282235
|
opts.cwd
|
|
282070
282236
|
);
|
|
282071
282237
|
}
|
|
282238
|
+
function remediationBaseOf(ctx) {
|
|
282239
|
+
const head = ctx.toolState.initialHead;
|
|
282240
|
+
return ctx.payload.baseBranch?.trim() || (head?.kind === "branch" ? head.name : void 0) || ctx.repo?.data.default_branch?.trim() || void 0;
|
|
282241
|
+
}
|
|
282072
282242
|
function buildConcernResult(ctx, rawConcerns, opts) {
|
|
282073
282243
|
const fullBaseline = dedupe(rawConcerns);
|
|
282074
282244
|
const session = scanSessionOf(ctx.toolState);
|
|
@@ -282097,8 +282267,13 @@ function buildConcernResult(ctx, rawConcerns, opts) {
|
|
|
282097
282267
|
active,
|
|
282098
282268
|
opts.autonomyThreshold
|
|
282099
282269
|
);
|
|
282100
|
-
const
|
|
282101
|
-
|
|
282270
|
+
const suffix2 = remediationBranchSuffix(
|
|
282271
|
+
remediationBaseOf(ctx),
|
|
282272
|
+
ctx.repo?.data.default_branch?.trim() || void 0
|
|
282273
|
+
);
|
|
282274
|
+
const groups2 = (opts.priority ? prioritizeGroups(annotatedGroups, active, opts.priority) : annotatedGroups).map((g) => ({ ...g, branch: `remediate/${g.id}${suffix2}` }));
|
|
282275
|
+
const planned = planBatches(groups2);
|
|
282276
|
+
const batchPlan = planned.batch_branch ? { ...planned, batch_branch: `${planned.batch_branch}${suffix2}` } : planned;
|
|
282102
282277
|
const bySeverity = {};
|
|
282103
282278
|
for (const c of active) bySeverity[c.severity] = (bySeverity[c.severity] ?? 0) + 1;
|
|
282104
282279
|
return {
|
|
@@ -283385,6 +283560,7 @@ ${marker2}`;
|
|
|
283385
283560
|
await deleteProgressComment(ctx).catch((err) => {
|
|
283386
283561
|
log2.debug(`progress comment cleanup after review failed: ${err}`);
|
|
283387
283562
|
});
|
|
283563
|
+
ctx.toolState.progressComment = null;
|
|
283388
283564
|
if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
283389
283565
|
const fromSha = ctx.toolState.checkoutSha;
|
|
283390
283566
|
const toSha = latestHeadSha;
|
|
@@ -286224,8 +286400,8 @@ function AddLabelsTool(ctx) {
|
|
|
286224
286400
|
}
|
|
286225
286401
|
|
|
286226
286402
|
// src/mcp/moduleExtraction.ts
|
|
286227
|
-
import { readFileSync as
|
|
286228
|
-
import { join as
|
|
286403
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
286404
|
+
import { join as join42, posix as posix11 } from "node:path";
|
|
286229
286405
|
|
|
286230
286406
|
// src/mcp/terraform/partialInterface.ts
|
|
286231
286407
|
import { readdirSync as readdirSync6, readFileSync as readFileSync21 } from "node:fs";
|
|
@@ -286288,6 +286464,54 @@ function findPartialInterfaces(cwd) {
|
|
|
286288
286464
|
return out;
|
|
286289
286465
|
}
|
|
286290
286466
|
|
|
286467
|
+
// src/mcp/terraform/refactorScope.ts
|
|
286468
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync22, statSync as statSync6 } from "node:fs";
|
|
286469
|
+
import { join as join41 } from "node:path";
|
|
286470
|
+
function isExamplePath(path15) {
|
|
286471
|
+
return /(?:^|\/)examples?\//.test(path15.replace(/\\/g, "/"));
|
|
286472
|
+
}
|
|
286473
|
+
var PROVIDER_OR_BACKEND = /^\s*(?:provider\s+"|backend\s+")/m;
|
|
286474
|
+
function holdsTerraform(dir, depth) {
|
|
286475
|
+
let entries;
|
|
286476
|
+
try {
|
|
286477
|
+
entries = readdirSync7(dir);
|
|
286478
|
+
} catch {
|
|
286479
|
+
return false;
|
|
286480
|
+
}
|
|
286481
|
+
for (const name of entries) {
|
|
286482
|
+
const path15 = join41(dir, name);
|
|
286483
|
+
if (name.endsWith(".tf")) return true;
|
|
286484
|
+
if (depth > 0 && !name.startsWith(".") && statSync6(path15, { throwIfNoEntry: false })?.isDirectory()) {
|
|
286485
|
+
if (holdsTerraform(path15, depth - 1)) return true;
|
|
286486
|
+
}
|
|
286487
|
+
}
|
|
286488
|
+
return false;
|
|
286489
|
+
}
|
|
286490
|
+
function libraryRepoReason(cwd) {
|
|
286491
|
+
let rootFiles;
|
|
286492
|
+
try {
|
|
286493
|
+
rootFiles = readdirSync7(cwd).filter((f) => f.endsWith(".tf"));
|
|
286494
|
+
} catch {
|
|
286495
|
+
return null;
|
|
286496
|
+
}
|
|
286497
|
+
if (rootFiles.length === 0) return null;
|
|
286498
|
+
for (const f of rootFiles) {
|
|
286499
|
+
let text;
|
|
286500
|
+
try {
|
|
286501
|
+
text = readFileSync22(join41(cwd, f), "utf8");
|
|
286502
|
+
} catch {
|
|
286503
|
+
continue;
|
|
286504
|
+
}
|
|
286505
|
+
if (PROVIDER_OR_BACKEND.test(text)) return null;
|
|
286506
|
+
}
|
|
286507
|
+
const examples = ["examples", "example"].find((d) => existsSync10(join41(cwd, d)));
|
|
286508
|
+
if (examples === void 0 || !holdsTerraform(join41(cwd, examples), 2)) return null;
|
|
286509
|
+
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`;
|
|
286510
|
+
}
|
|
286511
|
+
function reportOnly(reason) {
|
|
286512
|
+
return reason === null ? {} : { report_only: true, report_only_reason: reason };
|
|
286513
|
+
}
|
|
286514
|
+
|
|
286291
286515
|
// src/mcp/moduleExtraction.ts
|
|
286292
286516
|
function parseResourceBlocks(hcl2) {
|
|
286293
286517
|
const out = [];
|
|
@@ -286576,7 +286800,7 @@ function findExtractionCandidates(cwd, rawCatalogue, orgModuleHcl) {
|
|
|
286576
286800
|
if (isInHouseModule(file2, graph) !== null) continue;
|
|
286577
286801
|
let text;
|
|
286578
286802
|
try {
|
|
286579
|
-
text =
|
|
286803
|
+
text = readFileSync23(join42(cwd, file2), "utf8");
|
|
286580
286804
|
} catch {
|
|
286581
286805
|
continue;
|
|
286582
286806
|
}
|
|
@@ -286645,7 +286869,8 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
286645
286869
|
cwd,
|
|
286646
286870
|
ctx.payload.moduleCatalogue,
|
|
286647
286871
|
ctx.toolState.orgModuleHcl
|
|
286648
|
-
);
|
|
286872
|
+
).filter((c) => ctx.payload.refactorExamples || !isExamplePath(c.cluster.file));
|
|
286873
|
+
const library = libraryRepoReason(cwd);
|
|
286649
286874
|
const matched = candidates.filter((c) => c.candidates.length > 0);
|
|
286650
286875
|
const reasons = {};
|
|
286651
286876
|
for (const c of candidates)
|
|
@@ -286659,6 +286884,7 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
286659
286884
|
`\xBB module_extraction_candidates: ${candidates.length} cluster(s), ${matched.length} with a module match` + (partialInterfaces.length > 0 ? `, ${partialInterfaces.length} partial-interface block(s)` : "")
|
|
286660
286885
|
);
|
|
286661
286886
|
return toolOk({
|
|
286887
|
+
...reportOnly(library),
|
|
286662
286888
|
cluster_count: candidates.length,
|
|
286663
286889
|
matched_count: matched.length,
|
|
286664
286890
|
candidates,
|
|
@@ -286682,8 +286908,8 @@ function ModuleExtractionCandidatesTool(ctx) {
|
|
|
286682
286908
|
}
|
|
286683
286909
|
|
|
286684
286910
|
// src/mcp/moduleTests.ts
|
|
286685
|
-
import { existsSync as
|
|
286686
|
-
import { join as
|
|
286911
|
+
import { existsSync as existsSync11, readdirSync as readdirSync8, readFileSync as readFileSync24, statSync as statSync7 } from "node:fs";
|
|
286912
|
+
import { join as join43 } from "node:path";
|
|
286687
286913
|
var MODULE_META_ARGS = /* @__PURE__ */ new Set([
|
|
286688
286914
|
"source",
|
|
286689
286915
|
"version",
|
|
@@ -286803,30 +287029,30 @@ function resolveSourceDir(fromDir, source) {
|
|
|
286803
287029
|
}
|
|
286804
287030
|
function isDir(p) {
|
|
286805
287031
|
try {
|
|
286806
|
-
return
|
|
287032
|
+
return existsSync11(p) && statSync7(p).isDirectory();
|
|
286807
287033
|
} catch {
|
|
286808
287034
|
return false;
|
|
286809
287035
|
}
|
|
286810
287036
|
}
|
|
286811
287037
|
function listFiles(absDir, suffix2) {
|
|
286812
287038
|
try {
|
|
286813
|
-
return
|
|
287039
|
+
return readdirSync8(absDir).filter((f) => f.endsWith(suffix2)).sort();
|
|
286814
287040
|
} catch {
|
|
286815
287041
|
return [];
|
|
286816
287042
|
}
|
|
286817
287043
|
}
|
|
286818
287044
|
function listSubdirs(absDir) {
|
|
286819
287045
|
try {
|
|
286820
|
-
return
|
|
287046
|
+
return readdirSync8(absDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
286821
287047
|
} catch {
|
|
286822
287048
|
return [];
|
|
286823
287049
|
}
|
|
286824
287050
|
}
|
|
286825
287051
|
function readAllTf(cwd, relDir) {
|
|
286826
287052
|
let text = "";
|
|
286827
|
-
for (const f of listFiles(
|
|
287053
|
+
for (const f of listFiles(join43(cwd, relDir), ".tf")) {
|
|
286828
287054
|
try {
|
|
286829
|
-
text += `${
|
|
287055
|
+
text += `${readFileSync24(join43(cwd, relDir, f), "utf8")}
|
|
286830
287056
|
`;
|
|
286831
287057
|
} catch {
|
|
286832
287058
|
}
|
|
@@ -286838,8 +287064,8 @@ function discoverModuleTestAssets(cwd, moduleDir) {
|
|
|
286838
287064
|
const assets = [];
|
|
286839
287065
|
const matchesModule = (source, fromDir) => resolveSourceDir(fromDir, source) === md;
|
|
286840
287066
|
for (const examplesRoot of [`${md}/examples`, "examples"]) {
|
|
286841
|
-
if (!isDir(
|
|
286842
|
-
for (const sub of listSubdirs(
|
|
287067
|
+
if (!isDir(join43(cwd, examplesRoot))) continue;
|
|
287068
|
+
for (const sub of listSubdirs(join43(cwd, examplesRoot))) {
|
|
286843
287069
|
const exDir = `${examplesRoot}/${sub}`;
|
|
286844
287070
|
const hcl2 = readAllTf(cwd, exDir);
|
|
286845
287071
|
if (!hcl2) continue;
|
|
@@ -286855,12 +287081,12 @@ function discoverModuleTestAssets(cwd, moduleDir) {
|
|
|
286855
287081
|
}
|
|
286856
287082
|
}
|
|
286857
287083
|
for (const testDir of [`${md}/tests`, md]) {
|
|
286858
|
-
if (!isDir(
|
|
286859
|
-
for (const f of listFiles(
|
|
287084
|
+
if (!isDir(join43(cwd, testDir))) continue;
|
|
287085
|
+
for (const f of listFiles(join43(cwd, testDir), ".tftest.hcl")) {
|
|
286860
287086
|
const rel = `${testDir}/${f}`;
|
|
286861
287087
|
let hcl2;
|
|
286862
287088
|
try {
|
|
286863
|
-
hcl2 =
|
|
287089
|
+
hcl2 = readFileSync24(join43(cwd, rel), "utf8");
|
|
286864
287090
|
} catch {
|
|
286865
287091
|
continue;
|
|
286866
287092
|
}
|
|
@@ -286868,12 +287094,12 @@ function discoverModuleTestAssets(cwd, moduleDir) {
|
|
|
286868
287094
|
}
|
|
286869
287095
|
}
|
|
286870
287096
|
for (const goDir of [`${md}/test`, `${md}/tests`]) {
|
|
286871
|
-
if (!isDir(
|
|
286872
|
-
for (const f of listFiles(
|
|
287097
|
+
if (!isDir(join43(cwd, goDir))) continue;
|
|
287098
|
+
for (const f of listFiles(join43(cwd, goDir), "_test.go")) {
|
|
286873
287099
|
const rel = `${goDir}/${f}`;
|
|
286874
287100
|
let go;
|
|
286875
287101
|
try {
|
|
286876
|
-
go =
|
|
287102
|
+
go = readFileSync24(join43(cwd, rel), "utf8");
|
|
286877
287103
|
} catch {
|
|
286878
287104
|
continue;
|
|
286879
287105
|
}
|
|
@@ -286997,8 +287223,8 @@ function SetOutputTool(ctx, outputSchema) {
|
|
|
286997
287223
|
|
|
286998
287224
|
// src/mcp/policy.ts
|
|
286999
287225
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
287000
|
-
import { existsSync as
|
|
287001
|
-
import { join as
|
|
287226
|
+
import { existsSync as existsSync12, readFileSync as readFileSync25 } from "node:fs";
|
|
287227
|
+
import { join as join44 } from "node:path";
|
|
287002
287228
|
function parseConftestOutput(stdout) {
|
|
287003
287229
|
let parsed2;
|
|
287004
287230
|
try {
|
|
@@ -287032,7 +287258,7 @@ var CONFTEST_CONFIG = "conftest.toml";
|
|
|
287032
287258
|
function conftestSelectsNamespaces(conftestCwd) {
|
|
287033
287259
|
let raw2;
|
|
287034
287260
|
try {
|
|
287035
|
-
raw2 =
|
|
287261
|
+
raw2 = readFileSync25(join44(conftestCwd, CONFTEST_CONFIG), "utf-8");
|
|
287036
287262
|
} catch {
|
|
287037
287263
|
return false;
|
|
287038
287264
|
}
|
|
@@ -287048,8 +287274,8 @@ function namespaceArgs(conftestCwd) {
|
|
|
287048
287274
|
}
|
|
287049
287275
|
function findPlanJson(cwd) {
|
|
287050
287276
|
for (const candidate of PLAN_JSON_NAMES) {
|
|
287051
|
-
const abs =
|
|
287052
|
-
if (
|
|
287277
|
+
const abs = join44(cwd, candidate);
|
|
287278
|
+
if (existsSync12(abs)) return abs;
|
|
287053
287279
|
}
|
|
287054
287280
|
return null;
|
|
287055
287281
|
}
|
|
@@ -287090,11 +287316,11 @@ function resolvePolicyDir(cwd, explicit) {
|
|
|
287090
287316
|
} catch {
|
|
287091
287317
|
return null;
|
|
287092
287318
|
}
|
|
287093
|
-
return
|
|
287319
|
+
return existsSync12(abs) ? abs : null;
|
|
287094
287320
|
}
|
|
287095
287321
|
for (const d of DEFAULT_POLICY_DIRS) {
|
|
287096
|
-
const abs =
|
|
287097
|
-
if (
|
|
287322
|
+
const abs = join44(cwd, d);
|
|
287323
|
+
if (existsSync12(abs)) return abs;
|
|
287098
287324
|
}
|
|
287099
287325
|
return null;
|
|
287100
287326
|
}
|
|
@@ -287128,7 +287354,7 @@ function PolicyCheckTool(ctx) {
|
|
|
287128
287354
|
} catch {
|
|
287129
287355
|
abs = null;
|
|
287130
287356
|
}
|
|
287131
|
-
targetFile = abs &&
|
|
287357
|
+
targetFile = abs && existsSync12(abs) ? abs : null;
|
|
287132
287358
|
if (!targetFile) {
|
|
287133
287359
|
return toolSkip(
|
|
287134
287360
|
"target_not_found",
|
|
@@ -287401,9 +287627,9 @@ function PullRequestInfoTool(ctx) {
|
|
|
287401
287627
|
|
|
287402
287628
|
// node_modules/.pnpm/@infraweaver-io+sandbox@0.1.8/node_modules/@infraweaver-io/sandbox/dist/writePolicy.js
|
|
287403
287629
|
import { renameSync, writeFileSync as writeFileSync13 } from "node:fs";
|
|
287404
|
-
import { join as
|
|
287630
|
+
import { join as join45 } from "node:path";
|
|
287405
287631
|
function writePolicyPath(tmpdir5, filename) {
|
|
287406
|
-
return
|
|
287632
|
+
return join45(tmpdir5, filename);
|
|
287407
287633
|
}
|
|
287408
287634
|
function publishWritePolicy(params) {
|
|
287409
287635
|
try {
|
|
@@ -287574,12 +287800,12 @@ function SelectModeTool(ctx) {
|
|
|
287574
287800
|
}
|
|
287575
287801
|
|
|
287576
287802
|
// src/mcp/terraform/tools/advise.ts
|
|
287577
|
-
import { existsSync as
|
|
287578
|
-
import { join as
|
|
287803
|
+
import { existsSync as existsSync13, readFileSync as readFileSync27 } from "node:fs";
|
|
287804
|
+
import { join as join47 } from "node:path";
|
|
287579
287805
|
|
|
287580
287806
|
// src/mcp/terraform/fixMemory.ts
|
|
287581
|
-
import { readFileSync as
|
|
287582
|
-
import { join as
|
|
287807
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
287808
|
+
import { join as join46 } from "node:path";
|
|
287583
287809
|
var FIX_MEMORY_FILENAME = FIX_MEMORY_FILE;
|
|
287584
287810
|
var MAX_DESCRIPTION = 400;
|
|
287585
287811
|
var MAX_BEFORE_EXAMPLE = 1200;
|
|
@@ -287666,7 +287892,7 @@ function loadFixMemory(cwd) {
|
|
|
287666
287892
|
const root = cwd ?? process.cwd();
|
|
287667
287893
|
for (const filename of [FIX_MEMORY_FILENAME, LEGACY_FIX_MEMORY_FILENAME]) {
|
|
287668
287894
|
try {
|
|
287669
|
-
return parseFixMemory(
|
|
287895
|
+
return parseFixMemory(readFileSync26(join46(root, filename), "utf-8")) ?? [];
|
|
287670
287896
|
} catch {
|
|
287671
287897
|
}
|
|
287672
287898
|
}
|
|
@@ -287732,10 +287958,10 @@ function inlineWaivers(content, file2) {
|
|
|
287732
287958
|
];
|
|
287733
287959
|
}
|
|
287734
287960
|
function readLearnings(tmpdir5) {
|
|
287735
|
-
const path15 =
|
|
287736
|
-
if (!
|
|
287961
|
+
const path15 = join47(tmpdir5, LEARNINGS_FILE);
|
|
287962
|
+
if (!existsSync13(path15)) return [];
|
|
287737
287963
|
try {
|
|
287738
|
-
return
|
|
287964
|
+
return readFileSync27(path15, "utf-8").split("\n");
|
|
287739
287965
|
} catch {
|
|
287740
287966
|
return [];
|
|
287741
287967
|
}
|
|
@@ -287754,7 +287980,7 @@ function runAdvise(ctx, input2) {
|
|
|
287754
287980
|
const learnings = readLearnings(ctx.tmpdir);
|
|
287755
287981
|
const openConcerns = session.lastScanConcerns;
|
|
287756
287982
|
const dossiers = files.map((file2) => {
|
|
287757
|
-
const content = readTextCapped(
|
|
287983
|
+
const content = readTextCapped(join47(cwd, file2));
|
|
287758
287984
|
const inline = content === null ? [] : inlineWaivers(content, file2);
|
|
287759
287985
|
const reviewedInline = reviewedCopy(
|
|
287760
287986
|
cwd,
|
|
@@ -289072,7 +289298,10 @@ function ConsolidationCandidatesTool(ctx) {
|
|
|
289072
289298
|
parameters: ConsolidationCandidatesParams,
|
|
289073
289299
|
execute: execute(async () => {
|
|
289074
289300
|
const cwd = ctx.payload.cwd ?? process.cwd();
|
|
289075
|
-
const
|
|
289301
|
+
const tree = readWorkingTree(cwd);
|
|
289302
|
+
const result = findConsolidationGroups(
|
|
289303
|
+
ctx.payload.refactorExamples ? tree : tree.filter((f) => !isExamplePath(f.path))
|
|
289304
|
+
);
|
|
289076
289305
|
log2.info(
|
|
289077
289306
|
`\xBB terraform_consolidation_candidates: ${result.groups.length} group(s), ${result.near_groups.length} near-group(s), ${result.declined_groups.length} declined`
|
|
289078
289307
|
);
|
|
@@ -289089,8 +289318,8 @@ function ConsolidationCandidatesTool(ctx) {
|
|
|
289089
289318
|
}
|
|
289090
289319
|
|
|
289091
289320
|
// src/mcp/terraform/tools/detectPlanDrift.ts
|
|
289092
|
-
import { readFileSync as
|
|
289093
|
-
import { join as
|
|
289321
|
+
import { readFileSync as readFileSync28 } from "node:fs";
|
|
289322
|
+
import { join as join48 } from "node:path";
|
|
289094
289323
|
|
|
289095
289324
|
// src/mcp/terraform/cspm.ts
|
|
289096
289325
|
var ExternalResource = type({
|
|
@@ -289508,7 +289737,7 @@ function readTfFiles(cwd) {
|
|
|
289508
289737
|
const out = [];
|
|
289509
289738
|
for (const file2 of walkTfFiles(cwd)) {
|
|
289510
289739
|
try {
|
|
289511
|
-
out.push({ file: file2, hcl:
|
|
289740
|
+
out.push({ file: file2, hcl: readFileSync28(join48(cwd, file2), "utf8") });
|
|
289512
289741
|
} catch {
|
|
289513
289742
|
}
|
|
289514
289743
|
}
|
|
@@ -289525,7 +289754,7 @@ function DetectPlanDriftTool(ctx) {
|
|
|
289525
289754
|
const planPath = path15 || !envPlanPath ? resolveWithinCwd(cwd, path15 ?? "plan.json") : envPlanPath;
|
|
289526
289755
|
let raw2;
|
|
289527
289756
|
try {
|
|
289528
|
-
raw2 =
|
|
289757
|
+
raw2 = readFileSync28(planPath, "utf8");
|
|
289529
289758
|
} catch {
|
|
289530
289759
|
return skipResult(
|
|
289531
289760
|
"plan_artifact_not_found",
|
|
@@ -289657,8 +289886,8 @@ function DetectPlanDriftTool(ctx) {
|
|
|
289657
289886
|
}
|
|
289658
289887
|
|
|
289659
289888
|
// src/mcp/terraform/tools/emitCost.ts
|
|
289660
|
-
import { readFileSync as
|
|
289661
|
-
import { join as
|
|
289889
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync14 } from "node:fs";
|
|
289890
|
+
import { join as join49 } from "node:path";
|
|
289662
289891
|
|
|
289663
289892
|
// src/mcp/terraform/awsPrices.ts
|
|
289664
289893
|
var AWS_PRICE_REGION = "eu-west-2";
|
|
@@ -289982,7 +290211,7 @@ function readTfFiles2(cwd) {
|
|
|
289982
290211
|
const out = [];
|
|
289983
290212
|
for (const file2 of walkTfFiles(cwd)) {
|
|
289984
290213
|
try {
|
|
289985
|
-
out.push({ file: file2, hcl:
|
|
290214
|
+
out.push({ file: file2, hcl: readFileSync29(join49(cwd, file2), "utf8") });
|
|
289986
290215
|
} catch {
|
|
289987
290216
|
}
|
|
289988
290217
|
}
|
|
@@ -291246,7 +291475,7 @@ function TerraformEmitVexTool(ctx) {
|
|
|
291246
291475
|
// src/mcp/terraform/tools/equivalenceCheck.ts
|
|
291247
291476
|
import { mkdtempSync as mkdtempSync5, rmSync as rmSync7 } from "node:fs";
|
|
291248
291477
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
291249
|
-
import { join as
|
|
291478
|
+
import { join as join50 } from "node:path";
|
|
291250
291479
|
|
|
291251
291480
|
// src/mcp/terraform/refactor/attribution.ts
|
|
291252
291481
|
import { posix as posix13 } from "node:path";
|
|
@@ -293038,12 +293267,12 @@ async function baselineValidateErrors(cwd, base, env2, session) {
|
|
|
293038
293267
|
}
|
|
293039
293268
|
const prefix2 = run("git", ["rev-parse", "--show-prefix"], cwd);
|
|
293040
293269
|
if (prefix2.status !== 0) return null;
|
|
293041
|
-
const tree = mkdtempSync5(
|
|
293270
|
+
const tree = mkdtempSync5(join50(tmpdir3(), "iw-baseline-"));
|
|
293042
293271
|
try {
|
|
293043
293272
|
if (run("git", ["worktree", "add", "--detach", "--quiet", tree, base], cwd).status !== 0) {
|
|
293044
293273
|
return null;
|
|
293045
293274
|
}
|
|
293046
|
-
const outcome2 = await scanValidate(
|
|
293275
|
+
const outcome2 = await scanValidate(join50(tree, prefix2.stdout.trim()), env2);
|
|
293047
293276
|
if (!outcome2.ran || outcome2.unvalidated) return null;
|
|
293048
293277
|
const errors = outcome2.concerns.map(validateErrorKey);
|
|
293049
293278
|
session.baselineValidateErrors = { base, errors };
|
|
@@ -293144,7 +293373,7 @@ function TerraformGenerateMovedTool(ctx) {
|
|
|
293144
293373
|
|
|
293145
293374
|
// src/mcp/terraform/cost.ts
|
|
293146
293375
|
import { mkdtempSync as mkdtempSync6, rmSync as rmSync8 } from "node:fs";
|
|
293147
|
-
import { join as
|
|
293376
|
+
import { join as join51 } from "node:path";
|
|
293148
293377
|
function parseInfracostBreakdown(stdout) {
|
|
293149
293378
|
const parsed2 = parseScannerJson(InfracostBreakdownOutput, stdout, "infracost");
|
|
293150
293379
|
const raw2 = parsed2.totalMonthlyCost;
|
|
@@ -293226,15 +293455,15 @@ async function infracostBaseline(cwd, key, tmpdir5) {
|
|
|
293226
293455
|
if (!baseRef) return null;
|
|
293227
293456
|
const prefixResult = run("git", ["rev-parse", "--show-prefix"], cwd);
|
|
293228
293457
|
const prefix2 = prefixResult.status === 0 ? prefixResult.stdout.trim() : "";
|
|
293229
|
-
const baseDir = mkdtempSync6(
|
|
293230
|
-
const worktree =
|
|
293458
|
+
const baseDir = mkdtempSync6(join51(tmpdir5, "infracost-base-"));
|
|
293459
|
+
const worktree = join51(baseDir, "wt");
|
|
293231
293460
|
const add = run("git", ["worktree", "add", "--detach", worktree, baseRef], cwd);
|
|
293232
293461
|
if (add.status !== 0) {
|
|
293233
293462
|
rmSync8(baseDir, { recursive: true, force: true });
|
|
293234
293463
|
return null;
|
|
293235
293464
|
}
|
|
293236
293465
|
try {
|
|
293237
|
-
const scanCwd = prefix2 ?
|
|
293466
|
+
const scanCwd = prefix2 ? join51(worktree, prefix2) : worktree;
|
|
293238
293467
|
const r = await runInfracostBreakdown(scanCwd, key);
|
|
293239
293468
|
if (r.missing || r.status !== 0) return null;
|
|
293240
293469
|
return parseInfracostBreakdown(r.stdout);
|
|
@@ -293304,8 +293533,8 @@ function InfracostDiffTool(ctx) {
|
|
|
293304
293533
|
}
|
|
293305
293534
|
|
|
293306
293535
|
// src/mcp/terraform/tools/ingestExternalFindings.ts
|
|
293307
|
-
import { statSync as
|
|
293308
|
-
import { join as
|
|
293536
|
+
import { statSync as statSync8 } from "node:fs";
|
|
293537
|
+
import { join as join52 } from "node:path";
|
|
293309
293538
|
|
|
293310
293539
|
// src/mcp/terraform/prowlerOcsf.ts
|
|
293311
293540
|
var DETECTION_FINDING_CREATE = 200401;
|
|
@@ -293531,7 +293760,7 @@ var MAX_FEED_BYTES = 100 * 1024 * 1024;
|
|
|
293531
293760
|
function readTfFiles3(cwd) {
|
|
293532
293761
|
const out = [];
|
|
293533
293762
|
for (const file2 of walkTfFiles(cwd)) {
|
|
293534
|
-
const hcl2 = readTextCapped(
|
|
293763
|
+
const hcl2 = readTextCapped(join52(cwd, file2));
|
|
293535
293764
|
if (hcl2 !== null) out.push({ file: file2, hcl: hcl2 });
|
|
293536
293765
|
}
|
|
293537
293766
|
return out;
|
|
@@ -293559,7 +293788,7 @@ function IngestExternalFindingsTool(ctx) {
|
|
|
293559
293788
|
return unusableFeed("external_findings_outside_workspace", errorMessage(error63));
|
|
293560
293789
|
}
|
|
293561
293790
|
try {
|
|
293562
|
-
const size =
|
|
293791
|
+
const size = statSync8(feedPath).size;
|
|
293563
293792
|
if (size > MAX_FEED_BYTES) {
|
|
293564
293793
|
return unusableFeed(
|
|
293565
293794
|
"external_findings_too_large",
|
|
@@ -293688,8 +293917,8 @@ function IngestExternalFindingsTool(ctx) {
|
|
|
293688
293917
|
}
|
|
293689
293918
|
|
|
293690
293919
|
// src/mcp/terraform/tools/moduleDocs.ts
|
|
293691
|
-
import { readFileSync as
|
|
293692
|
-
import { join as
|
|
293920
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync16 } from "node:fs";
|
|
293921
|
+
import { join as join53 } from "node:path";
|
|
293693
293922
|
|
|
293694
293923
|
// src/mcp/terraform/moduleDocs.ts
|
|
293695
293924
|
var VERDICT_CLAIM = {
|
|
@@ -293803,7 +294032,7 @@ function TerraformModuleDocsTool(ctx) {
|
|
|
293803
294032
|
);
|
|
293804
294033
|
}
|
|
293805
294034
|
const absDir = resolveWithinCwd(cwd, module_dir);
|
|
293806
|
-
const target =
|
|
294035
|
+
const target = join53(absDir, "README.md");
|
|
293807
294036
|
let gitHead = null;
|
|
293808
294037
|
try {
|
|
293809
294038
|
gitHead = $("git", ["rev-parse", "HEAD"], { cwd, log: false });
|
|
@@ -293811,7 +294040,7 @@ function TerraformModuleDocsTool(ctx) {
|
|
|
293811
294040
|
}
|
|
293812
294041
|
let existing = null;
|
|
293813
294042
|
try {
|
|
293814
|
-
existing =
|
|
294043
|
+
existing = readFileSync30(target, "utf8");
|
|
293815
294044
|
} catch {
|
|
293816
294045
|
}
|
|
293817
294046
|
const priorHead = readFooterGitHead(existing);
|
|
@@ -294115,12 +294344,15 @@ function NormalizationCandidatesTool(ctx) {
|
|
|
294115
294344
|
parameters: NormalizationCandidatesParams,
|
|
294116
294345
|
execute: execute(async () => {
|
|
294117
294346
|
const cwd = ctx.payload.cwd ?? process.cwd();
|
|
294118
|
-
const files = findNormalizationCandidates(cwd)
|
|
294347
|
+
const files = findNormalizationCandidates(cwd).filter(
|
|
294348
|
+
(f) => ctx.payload.refactorExamples || !isExamplePath(f.file)
|
|
294349
|
+
);
|
|
294119
294350
|
const total = files.reduce((n, f) => n + f.count, 0);
|
|
294120
294351
|
log2.info(
|
|
294121
294352
|
`\xBB terraform_normalization_candidates: ${total} site(s) across ${files.length} file(s)`
|
|
294122
294353
|
);
|
|
294123
294354
|
return toolOk({
|
|
294355
|
+
...reportOnly(libraryRepoReason(cwd)),
|
|
294124
294356
|
file_count: files.length,
|
|
294125
294357
|
total,
|
|
294126
294358
|
files,
|
|
@@ -294132,7 +294364,7 @@ function NormalizationCandidatesTool(ctx) {
|
|
|
294132
294364
|
|
|
294133
294365
|
// src/mcp/terraform/tools/plan.ts
|
|
294134
294366
|
import { rmSync as rmSync9 } from "node:fs";
|
|
294135
|
-
import { join as
|
|
294367
|
+
import { join as join54 } from "node:path";
|
|
294136
294368
|
var PLAN_JSON_ARGS = ["plan", "-input=false", "-no-color", "-lock=false", "-json"];
|
|
294137
294369
|
var planFileSeq = 0;
|
|
294138
294370
|
async function capturePlanPairs(absDir, planFile, creds) {
|
|
@@ -294155,7 +294387,7 @@ async function planOneRoot(absDir, creds, tmpdir5) {
|
|
|
294155
294387
|
skipReason: `terraform init failed: ${init.stderr.trim().slice(0, 200) || "unknown error"}`
|
|
294156
294388
|
};
|
|
294157
294389
|
}
|
|
294158
|
-
const planFile = tmpdir5 ?
|
|
294390
|
+
const planFile = tmpdir5 ? join54(tmpdir5, `plan-${process.pid}-${planFileSeq++}.tfplan`) : void 0;
|
|
294159
294391
|
const plan = await runTfAsync(
|
|
294160
294392
|
planFile ? [...PLAN_JSON_ARGS, `-out=${planFile}`] : PLAN_JSON_ARGS,
|
|
294161
294393
|
absDir,
|
|
@@ -294525,8 +294757,8 @@ function TerraformProviderUpgradeTool(ctx) {
|
|
|
294525
294757
|
}
|
|
294526
294758
|
|
|
294527
294759
|
// src/mcp/terraform/tfquery.ts
|
|
294528
|
-
import { readFileSync as
|
|
294529
|
-
import { join as
|
|
294760
|
+
import { readFileSync as readFileSync31 } from "node:fs";
|
|
294761
|
+
import { join as join55 } from "node:path";
|
|
294530
294762
|
var TFQUERY_SUFFIX = ".tfquery.hcl";
|
|
294531
294763
|
function parseQueryFile(hcl2) {
|
|
294532
294764
|
return parseBlocks(hcl2, ["list"]).map((block) => {
|
|
@@ -294552,7 +294784,7 @@ function collectQueryInventory(cwd) {
|
|
|
294552
294784
|
for (const rel of walkFilesBySuffix(cwd, TFQUERY_SUFFIX)) {
|
|
294553
294785
|
let text;
|
|
294554
294786
|
try {
|
|
294555
|
-
text =
|
|
294787
|
+
text = readFileSync31(join55(cwd, rel), "utf8");
|
|
294556
294788
|
} catch {
|
|
294557
294789
|
continue;
|
|
294558
294790
|
}
|
|
@@ -294587,7 +294819,7 @@ function TerraformQueryInventoryTool(ctx) {
|
|
|
294587
294819
|
}
|
|
294588
294820
|
|
|
294589
294821
|
// src/mcp/terraform/tools/readFindings.ts
|
|
294590
|
-
import { readFileSync as
|
|
294822
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
294591
294823
|
var ReadFindingsParams = type({
|
|
294592
294824
|
"path?": type.string.describe(
|
|
294593
294825
|
"path to the Assessor's findings.json. Defaults to $INFRAWEAVER_FINDINGS_PATH, then ./findings.json in the workspace."
|
|
@@ -294612,7 +294844,7 @@ function ReadFindingsTool(ctx) {
|
|
|
294612
294844
|
const findingsPath = path15 || !envFindingsPath ? resolveWithinCwd(cwd, path15 ?? "findings.json") : envFindingsPath;
|
|
294613
294845
|
let raw2;
|
|
294614
294846
|
try {
|
|
294615
|
-
raw2 =
|
|
294847
|
+
raw2 = readFileSync32(findingsPath, "utf8");
|
|
294616
294848
|
} catch {
|
|
294617
294849
|
return skipResult(
|
|
294618
294850
|
"findings_not_found",
|
|
@@ -294871,6 +295103,8 @@ var SUBJECT = {
|
|
|
294871
295103
|
// rule's silence a real statement. Whether git could answer is handled one
|
|
294872
295104
|
// level up by the blind set: a run that could not ask is never conforming.
|
|
294873
295105
|
"committed-state-file": ANY_BLOCK,
|
|
295106
|
+
// any block may hold a credential argument, a map of them, or a variable default
|
|
295107
|
+
"hardcoded-secret-argument": ANY_BLOCK,
|
|
294874
295108
|
"deprecated-s3-bucket-argument": /(?:^|\n)\s*resource\s+"?aws_s3_bucket(?:"|\s)/,
|
|
294875
295109
|
"superseded-resource-type": /(?:^|\n)\s*(?:resource\s+"?(?:aws_launch_configuration|aws_elb)(?:"|\s)|data\s+"?template_file(?:"|\s))/,
|
|
294876
295110
|
"missing-required-version": /(?:^|\n)\s*(?:resource|module|provider)\s+"/,
|
|
@@ -298542,15 +298776,15 @@ async function startMcpHttpServer(ctx, options) {
|
|
|
298542
298776
|
|
|
298543
298777
|
// src/mcp/terraform/policyGate.ts
|
|
298544
298778
|
import {
|
|
298545
|
-
existsSync as
|
|
298779
|
+
existsSync as existsSync14,
|
|
298546
298780
|
mkdirSync as mkdirSync7,
|
|
298547
298781
|
mkdtempSync as mkdtempSync7,
|
|
298548
|
-
readdirSync as
|
|
298549
|
-
readFileSync as
|
|
298782
|
+
readdirSync as readdirSync9,
|
|
298783
|
+
readFileSync as readFileSync34,
|
|
298550
298784
|
writeFileSync as writeFileSync17
|
|
298551
298785
|
} from "node:fs";
|
|
298552
298786
|
import { tmpdir as osTmpdir } from "node:os";
|
|
298553
|
-
import { dirname as dirname8, isAbsolute as isAbsolute6, join as
|
|
298787
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join57, relative as relative6, resolve as resolve8 } from "node:path";
|
|
298554
298788
|
function coercePolicyRules(raw2) {
|
|
298555
298789
|
const rules = [];
|
|
298556
298790
|
const rejected = [];
|
|
@@ -298578,7 +298812,7 @@ var UNKNOWN2 = /* @__PURE__ */ Symbol("unknown");
|
|
|
298578
298812
|
function isScalar(v) {
|
|
298579
298813
|
return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
298580
298814
|
}
|
|
298581
|
-
function
|
|
298815
|
+
function literalValue2(raw2) {
|
|
298582
298816
|
const v = raw2.includes("\n") ? raw2.trim() : stripTrailingComment(raw2);
|
|
298583
298817
|
if (v === "true") return true;
|
|
298584
298818
|
if (v === "false") return false;
|
|
@@ -298611,7 +298845,7 @@ function objectLiteral(v) {
|
|
|
298611
298845
|
for (const line of lines) {
|
|
298612
298846
|
const m = OBJECT_LINE.exec(line);
|
|
298613
298847
|
if (!m) return UNKNOWN2;
|
|
298614
|
-
const value2 =
|
|
298848
|
+
const value2 = literalValue2(m[3] ?? "");
|
|
298615
298849
|
if (value2 !== void 0) out[m[1] ?? m[2]] = value2;
|
|
298616
298850
|
}
|
|
298617
298851
|
return out;
|
|
@@ -298639,7 +298873,7 @@ function staticValues(body2) {
|
|
|
298639
298873
|
continue;
|
|
298640
298874
|
}
|
|
298641
298875
|
if (e.kind === "attr") {
|
|
298642
|
-
const value2 =
|
|
298876
|
+
const value2 = literalValue2(e.value ?? "");
|
|
298643
298877
|
if (value2 !== void 0) out[e.name] = value2;
|
|
298644
298878
|
continue;
|
|
298645
298879
|
}
|
|
@@ -298802,7 +299036,7 @@ function evaluateRulesStatically(cwd, rules, scope2 = { kind: "all", reason: ""
|
|
|
298802
299036
|
const files = [];
|
|
298803
299037
|
for (const rel of walk.files) {
|
|
298804
299038
|
try {
|
|
298805
|
-
files.push({ rel, text:
|
|
299039
|
+
files.push({ rel, text: readFileSync34(join57(cwd, rel), "utf-8") });
|
|
298806
299040
|
} catch {
|
|
298807
299041
|
}
|
|
298808
299042
|
}
|
|
@@ -298917,16 +299151,16 @@ function cloudEmittedRego(rules) {
|
|
|
298917
299151
|
}
|
|
298918
299152
|
function readOrEmpty(path15) {
|
|
298919
299153
|
try {
|
|
298920
|
-
return
|
|
299154
|
+
return readFileSync34(path15, "utf-8");
|
|
298921
299155
|
} catch {
|
|
298922
299156
|
return "";
|
|
298923
299157
|
}
|
|
298924
299158
|
}
|
|
298925
299159
|
function regoDirIn(root, skip) {
|
|
298926
299160
|
for (const dir of DEFAULT_POLICY_DIRS) {
|
|
298927
|
-
const abs =
|
|
298928
|
-
if (
|
|
298929
|
-
(rel) => !skip(
|
|
299161
|
+
const abs = join57(root, dir);
|
|
299162
|
+
if (existsSync14(abs) && walkFilesBySuffix(abs, ".rego").some(
|
|
299163
|
+
(rel) => !skip(join57(dir, rel), readOrEmpty(join57(abs, rel)))
|
|
298930
299164
|
)) {
|
|
298931
299165
|
return dir;
|
|
298932
299166
|
}
|
|
@@ -298934,14 +299168,14 @@ function regoDirIn(root, skip) {
|
|
|
298934
299168
|
return null;
|
|
298935
299169
|
}
|
|
298936
299170
|
function withoutSkipped(root, dir, skip, tmpdir5) {
|
|
298937
|
-
const abs =
|
|
298938
|
-
const files =
|
|
298939
|
-
const kept = files.filter((rel) => !skip(
|
|
299171
|
+
const abs = join57(root, dir);
|
|
299172
|
+
const files = readdirSync9(abs, { recursive: true, withFileTypes: true }).filter((e) => e.isFile()).map((e) => relative6(abs, join57(e.parentPath, e.name)));
|
|
299173
|
+
const kept = files.filter((rel) => !skip(join57(dir, rel), readOrEmpty(join57(abs, rel))));
|
|
298940
299174
|
if (kept.length === files.length) return abs;
|
|
298941
|
-
const copy =
|
|
299175
|
+
const copy = join57(mkdtempSync7(join57(tmpdir5 ?? osTmpdir(), "policy-own-")), dir);
|
|
298942
299176
|
for (const rel of kept) {
|
|
298943
|
-
mkdirSync7(dirname8(
|
|
298944
|
-
writeFileSync17(
|
|
299177
|
+
mkdirSync7(dirname8(join57(copy, rel)), { recursive: true });
|
|
299178
|
+
writeFileSync17(join57(copy, rel), readFileSync34(join57(abs, rel)));
|
|
298945
299179
|
}
|
|
298946
299180
|
return copy;
|
|
298947
299181
|
}
|
|
@@ -298963,18 +299197,18 @@ function resolveRepoPolicy(cwd, baseRef, tmpdir5, notes, skip) {
|
|
|
298963
299197
|
}
|
|
298964
299198
|
const own2 = tree.files.filter((f) => !skip(f.path, f.raw));
|
|
298965
299199
|
if (!own2.some((f) => f.path.endsWith(".rego"))) continue;
|
|
298966
|
-
const root = mkdtempSync7(
|
|
299200
|
+
const root = mkdtempSync7(join57(tmpdir5 ?? osTmpdir(), "policy-base-"));
|
|
298967
299201
|
for (const f of own2) {
|
|
298968
|
-
mkdirSync7(dirname8(
|
|
298969
|
-
writeFileSync17(
|
|
299202
|
+
mkdirSync7(dirname8(join57(root, f.path)), { recursive: true });
|
|
299203
|
+
writeFileSync17(join57(root, f.path), f.raw);
|
|
298970
299204
|
}
|
|
298971
299205
|
const toml = readFileAtBaseRef(cwd, baseRef, CONFTEST_CONFIG);
|
|
298972
|
-
if (toml.kind === "file") writeFileSync17(
|
|
298973
|
-
const same = head === dir &&
|
|
299206
|
+
if (toml.kind === "file") writeFileSync17(join57(root, CONFTEST_CONFIG), toml.raw);
|
|
299207
|
+
const same = head === dir && readdirSync9(join57(cwd, dir), { recursive: true, withFileTypes: true }).filter(
|
|
298974
299208
|
(e) => e.isFile()
|
|
298975
299209
|
).length === tree.files.length && tree.files.every((f) => {
|
|
298976
299210
|
try {
|
|
298977
|
-
return
|
|
299211
|
+
return readFileSync34(join57(cwd, f.path), "utf-8") === f.raw;
|
|
298978
299212
|
} catch {
|
|
298979
299213
|
return false;
|
|
298980
299214
|
}
|
|
@@ -298984,7 +299218,7 @@ function resolveRepoPolicy(cwd, baseRef, tmpdir5, notes, skip) {
|
|
|
298984
299218
|
`the Rego policy differs from the base ref (${tree.ref}) \u2014 applying the BASE ${dir}/ so this change cannot relax the policy reviewing it. It takes effect once merged.`
|
|
298985
299219
|
);
|
|
298986
299220
|
}
|
|
298987
|
-
return { policyDir:
|
|
299221
|
+
return { policyDir: join57(root, dir), conftestCwd: root };
|
|
298988
299222
|
}
|
|
298989
299223
|
if (head) {
|
|
298990
299224
|
notes.push(
|
|
@@ -298994,7 +299228,7 @@ function resolveRepoPolicy(cwd, baseRef, tmpdir5, notes, skip) {
|
|
|
298994
299228
|
return null;
|
|
298995
299229
|
}
|
|
298996
299230
|
function findWorkflowPlanJson(cwd, notes) {
|
|
298997
|
-
const present = PLAN_JSON_NAMES.filter((name) =>
|
|
299231
|
+
const present = PLAN_JSON_NAMES.filter((name) => existsSync14(join57(cwd, name)));
|
|
298998
299232
|
if (present.length === 0) return null;
|
|
298999
299233
|
const listing = run("git", ["ls-files", "-z"], cwd);
|
|
299000
299234
|
if (listing.status !== 0) {
|
|
@@ -299011,7 +299245,7 @@ function findWorkflowPlanJson(cwd, notes) {
|
|
|
299011
299245
|
);
|
|
299012
299246
|
continue;
|
|
299013
299247
|
}
|
|
299014
|
-
return
|
|
299248
|
+
return join57(cwd, name);
|
|
299015
299249
|
}
|
|
299016
299250
|
return null;
|
|
299017
299251
|
}
|
|
@@ -299041,7 +299275,7 @@ function evaluatePolicyGate(cwd, rawRules, opts = {}) {
|
|
|
299041
299275
|
let plan;
|
|
299042
299276
|
if (planPath) {
|
|
299043
299277
|
try {
|
|
299044
|
-
plan = asPlan(JSON.parse(
|
|
299278
|
+
plan = asPlan(JSON.parse(readFileSync34(planPath, "utf-8")));
|
|
299045
299279
|
} catch {
|
|
299046
299280
|
}
|
|
299047
299281
|
if (!plan) notes.push(`${planPath} is not a Terraform plan JSON \u2014 ignored`);
|
|
@@ -299144,7 +299378,7 @@ function capturePolicyGate(ctx) {
|
|
|
299144
299378
|
if (!result) return;
|
|
299145
299379
|
const workspace = process.env.GITHUB_WORKSPACE ?? cwd;
|
|
299146
299380
|
for (const o of [...result.violations, ...result.inconclusive]) {
|
|
299147
|
-
if (o.file) o.file = posix16(relative6(workspace,
|
|
299381
|
+
if (o.file) o.file = posix16(relative6(workspace, join57(cwd, o.file)));
|
|
299148
299382
|
}
|
|
299149
299383
|
session.policyGate = result;
|
|
299150
299384
|
for (const n of result.notes) log2.warning(`\xBB policy gate: ${n}`);
|
|
@@ -299479,7 +299713,7 @@ var ThinkingTimer = class {
|
|
|
299479
299713
|
// src/agents/claude.ts
|
|
299480
299714
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
299481
299715
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, writeFileSync as writeFileSync22 } from "node:fs";
|
|
299482
|
-
import { join as
|
|
299716
|
+
import { join as join66 } from "node:path";
|
|
299483
299717
|
import { performance as performance6 } from "node:perf_hooks";
|
|
299484
299718
|
import { setTimeout as sleep6 } from "node:timers/promises";
|
|
299485
299719
|
|
|
@@ -299839,7 +300073,7 @@ import { readFile as readFile2 } from "node:fs/promises";
|
|
|
299839
300073
|
|
|
299840
300074
|
// src/agents/shared.ts
|
|
299841
300075
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
299842
|
-
import { join as
|
|
300076
|
+
import { join as join58 } from "node:path";
|
|
299843
300077
|
var MAX_STDERR_LINES = 20;
|
|
299844
300078
|
var MAX_POST_RUN_RETRIES = 3;
|
|
299845
300079
|
function getGitStatus() {
|
|
@@ -299876,7 +300110,7 @@ var agent = (input2) => {
|
|
|
299876
300110
|
};
|
|
299877
300111
|
};
|
|
299878
300112
|
function agentHomeEnv(tmpdir5) {
|
|
299879
|
-
return { HOME: tmpdir5, XDG_CONFIG_HOME:
|
|
300113
|
+
return { HOME: tmpdir5, XDG_CONFIG_HOME: join58(tmpdir5, ".config") };
|
|
299880
300114
|
}
|
|
299881
300115
|
function baseAgentEnv(homeEnv, repoDir) {
|
|
299882
300116
|
return { ...process.env, ...homeEnv, PWD: repoDir };
|
|
@@ -300352,6 +300586,12 @@ function shouldRunGraceTurn(params) {
|
|
|
300352
300586
|
return params.stopReason === "quota" && params.hasReserve && params.canResume && params.unsubmitted !== null;
|
|
300353
300587
|
}
|
|
300354
300588
|
|
|
300589
|
+
// src/agents/quotaTokens.ts
|
|
300590
|
+
var CACHE_READ_WEIGHT = 0.1;
|
|
300591
|
+
function quotaTokens(t) {
|
|
300592
|
+
return t.input + t.output + t.cacheWrite + t.cacheRead * CACHE_READ_WEIGHT;
|
|
300593
|
+
}
|
|
300594
|
+
|
|
300355
300595
|
// src/agents/sessionLabeler.ts
|
|
300356
300596
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
300357
300597
|
var ORCHESTRATOR_LABEL = "orchestrator";
|
|
@@ -300673,7 +300913,7 @@ function matchesRouteSentinel(envVar, model) {
|
|
|
300673
300913
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
300674
300914
|
import { mkdirSync as mkdirSync8, rmSync as rmSync10, writeFileSync as writeFileSync18 } from "node:fs";
|
|
300675
300915
|
import { homedir } from "node:os";
|
|
300676
|
-
import { join as
|
|
300916
|
+
import { join as join59 } from "node:path";
|
|
300677
300917
|
var VERTEX_SERVICE_ACCOUNT_JSON_ENV = "VERTEX_SERVICE_ACCOUNT_JSON";
|
|
300678
300918
|
var GOOGLE_APPLICATION_CREDENTIALS_ENV = "GOOGLE_APPLICATION_CREDENTIALS";
|
|
300679
300919
|
var GOOGLE_CLOUD_PROJECT_ENV = "GOOGLE_CLOUD_PROJECT";
|
|
@@ -300697,7 +300937,7 @@ function readProjectIdFromVertexServiceAccountJson() {
|
|
|
300697
300937
|
}
|
|
300698
300938
|
function createSecretDir() {
|
|
300699
300939
|
const base = process.env.INFRAWEAVER_SECRET_HOME || process.env.HOME || homedir();
|
|
300700
|
-
const secretDir =
|
|
300940
|
+
const secretDir = join59(base, `.${PRODUCT_SLUG}`, "secrets", randomUUID9());
|
|
300701
300941
|
mkdirSync8(secretDir, { recursive: true, mode: 448 });
|
|
300702
300942
|
return secretDir;
|
|
300703
300943
|
}
|
|
@@ -300706,7 +300946,7 @@ function materializeVertexCredentials(params) {
|
|
|
300706
300946
|
const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
|
|
300707
300947
|
if (!blob) return void 0;
|
|
300708
300948
|
const secretDir = createSecretDir();
|
|
300709
|
-
const credentialsPath =
|
|
300949
|
+
const credentialsPath = join59(secretDir, "vertex-sa.json");
|
|
300710
300950
|
writeFileSync18(credentialsPath, blob, { mode: 384 });
|
|
300711
300951
|
process.env[GOOGLE_APPLICATION_CREDENTIALS_ENV] = credentialsPath;
|
|
300712
300952
|
const projectId = readProjectIdFromVertexServiceAccountJson();
|
|
@@ -300842,29 +301082,29 @@ function createProcessOutputActivityTimeout(ctx) {
|
|
|
300842
301082
|
}
|
|
300843
301083
|
|
|
300844
301084
|
// src/utils/prompt/skills.ts
|
|
300845
|
-
import { existsSync as
|
|
300846
|
-
import { dirname as dirname9, join as
|
|
301085
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync35, writeFileSync as writeFileSync19 } from "node:fs";
|
|
301086
|
+
import { dirname as dirname9, join as join60 } from "node:path";
|
|
300847
301087
|
import { fileURLToPath } from "node:url";
|
|
300848
301088
|
var BUNDLED_SKILL_NAMES = ["terraform-best-practices"];
|
|
300849
301089
|
function resolveSkillPath(name) {
|
|
300850
301090
|
const here = dirname9(fileURLToPath(import.meta.url));
|
|
300851
301091
|
const candidates = [
|
|
300852
|
-
|
|
300853
|
-
|
|
301092
|
+
join60(here, "..", "..", "skills", name, "SKILL.md"),
|
|
301093
|
+
join60(here, "skills", name, "SKILL.md")
|
|
300854
301094
|
];
|
|
300855
301095
|
for (const candidate of candidates) {
|
|
300856
|
-
if (
|
|
301096
|
+
if (existsSync15(candidate)) return candidate;
|
|
300857
301097
|
}
|
|
300858
301098
|
throw new Error(`bundled skill not found: ${name} (looked in ${candidates.join(", ")})`);
|
|
300859
301099
|
}
|
|
300860
301100
|
var SKILL_TARGET_DIRS = [".opencode/skills", ".claude/skills", ".agents/skills"];
|
|
300861
301101
|
function installBundledSkills(params) {
|
|
300862
301102
|
for (const name of BUNDLED_SKILL_NAMES) {
|
|
300863
|
-
const content =
|
|
301103
|
+
const content = readFileSync35(resolveSkillPath(name), "utf8");
|
|
300864
301104
|
for (const targetDir of SKILL_TARGET_DIRS) {
|
|
300865
|
-
const skillDir =
|
|
301105
|
+
const skillDir = join60(params.home, targetDir, name);
|
|
300866
301106
|
mkdirSync9(skillDir, { recursive: true });
|
|
300867
|
-
writeFileSync19(
|
|
301107
|
+
writeFileSync19(join60(skillDir, "SKILL.md"), content);
|
|
300868
301108
|
}
|
|
300869
301109
|
}
|
|
300870
301110
|
log2.success(`installed bundled skills: ${BUNDLED_SKILL_NAMES.join(", ")}`);
|
|
@@ -300873,8 +301113,8 @@ function installBundledSkills(params) {
|
|
|
300873
301113
|
// src/utils/setup/install.ts
|
|
300874
301114
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
300875
301115
|
import { createHash as createHash14 } from "node:crypto";
|
|
300876
|
-
import { chmodSync as chmodSync2, createWriteStream as createWriteStream2, existsSync as
|
|
300877
|
-
import { join as
|
|
301116
|
+
import { chmodSync as chmodSync2, createWriteStream as createWriteStream2, existsSync as existsSync18, readFileSync as readFileSync36 } from "node:fs";
|
|
301117
|
+
import { join as join65 } from "node:path";
|
|
300878
301118
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
300879
301119
|
|
|
300880
301120
|
// src/utils/setup/tarballCache.ts
|
|
@@ -300929,7 +301169,7 @@ function verifyTarballIntegrity(tarballPath, dist, descriptor) {
|
|
|
300929
301169
|
`Refusing to install ${descriptor}: the registry published no integrity digest (neither dist.integrity nor dist.shasum), so the download cannot be verified.`
|
|
300930
301170
|
);
|
|
300931
301171
|
}
|
|
300932
|
-
const bytes =
|
|
301172
|
+
const bytes = readFileSync36(tarballPath);
|
|
300933
301173
|
if (integrity2) {
|
|
300934
301174
|
const candidates = integrity2.trim().split(/\s+/).filter(Boolean);
|
|
300935
301175
|
for (const candidate of candidates) {
|
|
@@ -300958,9 +301198,9 @@ function verifyTarballIntegrity(tarballPath, dist, descriptor) {
|
|
|
300958
301198
|
}
|
|
300959
301199
|
async function installFromNpmTarball(params) {
|
|
300960
301200
|
const tempDir = getTempDir();
|
|
300961
|
-
const extractedDir =
|
|
300962
|
-
const cliPath =
|
|
300963
|
-
if (
|
|
301201
|
+
const extractedDir = join65(tempDir, "package");
|
|
301202
|
+
const cliPath = join65(extractedDir, params.executablePath);
|
|
301203
|
+
if (existsSync18(cliPath)) {
|
|
300964
301204
|
log2.debug(`\xBB using cached binary at ${cliPath}`);
|
|
300965
301205
|
return cliPath;
|
|
300966
301206
|
}
|
|
@@ -301011,7 +301251,7 @@ async function installFromNpmTarball(params) {
|
|
|
301011
301251
|
dist = (await manifestResponse.json()).dist;
|
|
301012
301252
|
}
|
|
301013
301253
|
log2.debug(`\xBB installing ${params.packageName}@${resolvedVersion}...`);
|
|
301014
|
-
const tarballPath =
|
|
301254
|
+
const tarballPath = join65(tempDir, "package.tgz");
|
|
301015
301255
|
const tarballFileName = params.packageName.startsWith("@") ? params.packageName.split("/")[1] : params.packageName;
|
|
301016
301256
|
const tarballUrl = `${npmRegistry}/${encodedName}/-/${tarballFileName}-${resolvedVersion}.tgz`;
|
|
301017
301257
|
const restored = await restoreTarball({
|
|
@@ -301051,7 +301291,7 @@ async function installFromNpmTarball(params) {
|
|
|
301051
301291
|
`Failed to extract tarball: ${extractResult.stderr || extractResult.stdout || "Unknown error"}`
|
|
301052
301292
|
);
|
|
301053
301293
|
}
|
|
301054
|
-
if (!
|
|
301294
|
+
if (!existsSync18(cliPath)) {
|
|
301055
301295
|
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
301056
301296
|
}
|
|
301057
301297
|
if (params.installDependencies) {
|
|
@@ -301162,9 +301402,9 @@ var CLAUDE_DISALLOWED_TOOLS = CLAUDE_EXEC_TOOL_DENY_RULES.join(",");
|
|
|
301162
301402
|
var INFRAWEAVER_MCP_AUTH_HEADER = `Bearer \${${MCP_SERVER_TOKEN_ENV}}`;
|
|
301163
301403
|
var CLAUDE_MAX_CONCURRENT_SUBAGENTS_DEFAULT = "4";
|
|
301164
301404
|
function writeMcpConfig(ctx) {
|
|
301165
|
-
const configDir =
|
|
301405
|
+
const configDir = join66(ctx.tmpdir, ".claude");
|
|
301166
301406
|
mkdirSync10(configDir, { recursive: true });
|
|
301167
|
-
const configPath =
|
|
301407
|
+
const configPath = join66(configDir, "mcp.json");
|
|
301168
301408
|
const terraformMcp = resolveTerraformMcp(ctx.payload);
|
|
301169
301409
|
if (terraformMcp.kind === "docker_missing") log2.info(`\xBB ${terraformMcp.note}`);
|
|
301170
301410
|
const opentofuMcp = resolveOpenTofuMcp(ctx.payload);
|
|
@@ -301196,13 +301436,13 @@ function writeMcpConfig(ctx) {
|
|
|
301196
301436
|
return configPath;
|
|
301197
301437
|
}
|
|
301198
301438
|
function writePretoolGateAssets(ctx, stopHookPath) {
|
|
301199
|
-
const scriptPath =
|
|
301439
|
+
const scriptPath = join66(ctx.tmpdir, CLAUDE_PRETOOL_GATE_FILENAME);
|
|
301200
301440
|
writeFileSync22(
|
|
301201
301441
|
scriptPath,
|
|
301202
301442
|
buildClaudePretoolGateSource(ctx.subagentDeniedTools, writePolicyPath2(ctx.tmpdir))
|
|
301203
301443
|
);
|
|
301204
301444
|
chmodSync3(scriptPath, 493);
|
|
301205
|
-
const settingsPath =
|
|
301445
|
+
const settingsPath = join66(ctx.tmpdir, "infraweaver-claude-settings.json");
|
|
301206
301446
|
const settings = buildClaudePretoolGateSettings(scriptPath, CLAUDE_EXEC_TOOL_DENY_RULES);
|
|
301207
301447
|
settings.permissions.deny.push(...harnessTmpdirWriteDenyClaude(ctx.tmpdir));
|
|
301208
301448
|
if (stopHookPath) {
|
|
@@ -301291,7 +301531,7 @@ function timerFor(state5, label) {
|
|
|
301291
301531
|
function chargeTokenQuota(state5) {
|
|
301292
301532
|
const quota = state5.params.tokenQuota;
|
|
301293
301533
|
if (!quota || state5.stopRun.signal.aborted) return;
|
|
301294
|
-
if (!quota.observe(state5.quotaSource, state5.accumulatedTokens)) return;
|
|
301534
|
+
if (!quota.observe(state5.quotaSource, quotaTokens(state5.accumulatedTokens))) return;
|
|
301295
301535
|
log2.info(`\xBB ${state5.params.label}: ${quota.gap()}`);
|
|
301296
301536
|
state5.stopRun.abort(new TokenQuotaExceededError(quota.gap()));
|
|
301297
301537
|
}
|
|
@@ -301827,11 +302067,11 @@ var claude = agent({
|
|
|
301827
302067
|
const onVertex = matchesRouteSentinel(VERTEX_MODEL_ID_ENV, specifier) && specifier !== void 0 && isVertexAnthropicId(specifier);
|
|
301828
302068
|
const model = !specifier ? void 0 : onBedrock ? specifier : onVertex ? void 0 : stripProviderPrefix(specifier);
|
|
301829
302069
|
const homeEnv = agentHomeEnv(ctx.tmpdir);
|
|
301830
|
-
mkdirSync10(
|
|
302070
|
+
mkdirSync10(join66(homeEnv.XDG_CONFIG_HOME, "claude"), { recursive: true });
|
|
301831
302071
|
installBundledSkills({ home: homeEnv.HOME });
|
|
301832
302072
|
const mcpConfigPath = writeMcpConfig(ctx);
|
|
301833
302073
|
const effort = resolveEffort(model);
|
|
301834
|
-
const stopHookPath =
|
|
302074
|
+
const stopHookPath = join66(ctx.tmpdir, "infraweaver-stop-hook.sh");
|
|
301835
302075
|
writeFileSync22(stopHookPath, buildStopHookScript(), { mode: 493 });
|
|
301836
302076
|
const pretoolGate = writePretoolGateAssets(ctx, stopHookPath);
|
|
301837
302077
|
installManagedSettings({
|
|
@@ -301997,7 +302237,7 @@ var claude = agent({
|
|
|
301997
302237
|
init_core3();
|
|
301998
302238
|
import { spawn as nodeSpawn2 } from "node:child_process";
|
|
301999
302239
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync24 } from "node:fs";
|
|
302000
|
-
import { join as
|
|
302240
|
+
import { join as join68 } from "node:path";
|
|
302001
302241
|
import { performance as performance7 } from "node:perf_hooks";
|
|
302002
302242
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
302003
302243
|
import { setTimeout as sleep7 } from "node:timers/promises";
|
|
@@ -307680,7 +307920,7 @@ function autoSelectModel() {
|
|
|
307680
307920
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
307681
307921
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync23 } from "node:fs";
|
|
307682
307922
|
import { homedir as homedir3, userInfo as userInfo2 } from "node:os";
|
|
307683
|
-
import { join as
|
|
307923
|
+
import { join as join67 } from "node:path";
|
|
307684
307924
|
|
|
307685
307925
|
// src/utils/agent/codexOAuth.ts
|
|
307686
307926
|
init_webapi();
|
|
@@ -307734,8 +307974,8 @@ function installCodexAuth() {
|
|
|
307734
307974
|
}
|
|
307735
307975
|
const expiresMs = decodeJwtExpMs(body2.tokens.access_token) ?? 0;
|
|
307736
307976
|
const xdgDataHome = resolveDataHome();
|
|
307737
|
-
const opencodeDir =
|
|
307738
|
-
const authPath =
|
|
307977
|
+
const opencodeDir = join67(xdgDataHome, "opencode");
|
|
307978
|
+
const authPath = join67(opencodeDir, "auth.json");
|
|
307739
307979
|
const opencodeAuth = {
|
|
307740
307980
|
openai: {
|
|
307741
307981
|
type: "oauth",
|
|
@@ -307759,7 +307999,7 @@ function installCodexAuth() {
|
|
|
307759
307999
|
};
|
|
307760
308000
|
}
|
|
307761
308001
|
function resolveDataHome() {
|
|
307762
|
-
if (process.env.CI !== "true") return
|
|
308002
|
+
if (process.env.CI !== "true") return join67(homedir3(), ".local", "share");
|
|
307763
308003
|
bootstrapInfraweaverDataDir();
|
|
307764
308004
|
return INFRAWEAVER_DATA_DIR;
|
|
307765
308005
|
}
|
|
@@ -308033,7 +308273,7 @@ function newTurn() {
|
|
|
308033
308273
|
function chargeTokenQuota2(ctx) {
|
|
308034
308274
|
const quota = ctx.tokenQuota;
|
|
308035
308275
|
if (!quota || !ctx.currentTurn || ctx.tokenQuotaExceeded) return;
|
|
308036
|
-
if (!quota.observe(ctx.currentTurn.quotaSource, ctx.currentTurn.tokens)) return;
|
|
308276
|
+
if (!quota.observe(ctx.currentTurn.quotaSource, quotaTokens(ctx.currentTurn.tokens))) return;
|
|
308037
308277
|
ctx.tokenQuotaExceeded = true;
|
|
308038
308278
|
log2.info(`\xBB ${ctx.label}: ${quota.gap()}`);
|
|
308039
308279
|
ctx.abortRun?.(new TokenQuotaExceededError(quota.gap()));
|
|
@@ -308500,10 +308740,10 @@ var opencode = agent({
|
|
|
308500
308740
|
const openAiCompatModel = resolveOpenAiCompatibleOpenCodeModel(rawModel);
|
|
308501
308741
|
const model = isBedrockRoute ? `amazon-bedrock/${rawModel}` : vertexModel ?? openAiCompatModel ?? rawModel;
|
|
308502
308742
|
const homeEnv = agentHomeEnv(ctx.tmpdir);
|
|
308503
|
-
const opencodePluginDir =
|
|
308743
|
+
const opencodePluginDir = join68(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
|
|
308504
308744
|
mkdirSync12(opencodePluginDir, { recursive: true });
|
|
308505
308745
|
writeFileSync24(
|
|
308506
|
-
|
|
308746
|
+
join68(opencodePluginDir, INFRAWEAVER_OPENCODE_GATE_PLUGIN_FILENAME),
|
|
308507
308747
|
buildOpencodeSubagentGateSource(ctx.subagentDeniedTools, writePolicyPath2(ctx.tmpdir))
|
|
308508
308748
|
);
|
|
308509
308749
|
installBundledSkills({ home: homeEnv.HOME });
|
|
@@ -309539,7 +309779,7 @@ async function fetchRunContext(params) {
|
|
|
309539
309779
|
// src/utils/github/github.ts
|
|
309540
309780
|
init_core3();
|
|
309541
309781
|
import { rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
309542
|
-
import { dirname as dirname12, join as
|
|
309782
|
+
import { dirname as dirname12, join as join69 } from "node:path";
|
|
309543
309783
|
|
|
309544
309784
|
// node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js
|
|
309545
309785
|
function getUserAgent2() {
|
|
@@ -315355,7 +315595,7 @@ function getGitHubUsageSummary() {
|
|
|
315355
315595
|
}
|
|
315356
315596
|
async function writeGitHubUsageSummaryToFile(path15) {
|
|
315357
315597
|
const summary2 = getGitHubUsageSummary();
|
|
315358
|
-
const tmpPath =
|
|
315598
|
+
const tmpPath = join69(dirname12(path15), `.usage-summary-${process.pid}.tmp`);
|
|
315359
315599
|
await writeFile2(tmpPath, JSON.stringify(summary2));
|
|
315360
315600
|
await rename2(tmpPath, path15);
|
|
315361
315601
|
}
|
|
@@ -315900,7 +316140,7 @@ async function resolveModelAndAuth(params) {
|
|
|
315900
316140
|
// src/utils/run/runLifecycle.ts
|
|
315901
316141
|
init_core3();
|
|
315902
316142
|
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
315903
|
-
import { join as
|
|
316143
|
+
import { join as join72, resolve as resolve10 } from "node:path";
|
|
315904
316144
|
|
|
315905
316145
|
// src/utils/cloud/cloudReport.ts
|
|
315906
316146
|
init_core3();
|
|
@@ -316294,7 +316534,7 @@ async function reportToCloud(input2) {
|
|
|
316294
316534
|
|
|
316295
316535
|
// src/utils/cloud/learnings.ts
|
|
316296
316536
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
316297
|
-
import { dirname as dirname13, join as
|
|
316537
|
+
import { dirname as dirname13, join as join70 } from "node:path";
|
|
316298
316538
|
|
|
316299
316539
|
// src/utils/cloud/learningsTruncate.ts
|
|
316300
316540
|
var MAX_LEARNINGS_LENGTH = 1e5;
|
|
@@ -316311,7 +316551,7 @@ function truncateAtLineBoundary(body2, cap2) {
|
|
|
316311
316551
|
// src/utils/cloud/learnings.ts
|
|
316312
316552
|
var LEARNINGS_FILE_NAME = LEARNINGS_FILE;
|
|
316313
316553
|
function learningsFilePath(tmpdir5) {
|
|
316314
|
-
return
|
|
316554
|
+
return join70(tmpdir5, LEARNINGS_FILE_NAME);
|
|
316315
316555
|
}
|
|
316316
316556
|
async function seedLearningsFile(params) {
|
|
316317
316557
|
const path15 = learningsFilePath(params.tmpdir);
|
|
@@ -316442,7 +316682,7 @@ ${ctx.error}` : ctx.error;
|
|
|
316442
316682
|
|
|
316443
316683
|
// src/utils/github/prSummary.ts
|
|
316444
316684
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
316445
|
-
import { dirname as dirname14, join as
|
|
316685
|
+
import { dirname as dirname14, join as join71 } from "node:path";
|
|
316446
316686
|
var SUMMARY_FILE_NAME = SUMMARY_FILE;
|
|
316447
316687
|
var SUMMARY_SCAFFOLD = `# PR summary
|
|
316448
316688
|
|
|
@@ -316452,7 +316692,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
|
|
|
316452
316692
|
var MIN_SNAPSHOT_LENGTH = 60;
|
|
316453
316693
|
var MAX_SNAPSHOT_LENGTH = 32768;
|
|
316454
316694
|
function summaryFilePath(tmpdir5) {
|
|
316455
|
-
return
|
|
316695
|
+
return join71(tmpdir5, SUMMARY_FILE_NAME);
|
|
316456
316696
|
}
|
|
316457
316697
|
async function seedSummaryFile(params) {
|
|
316458
316698
|
const path15 = summaryFilePath(params.tmpdir);
|
|
@@ -316986,7 +317226,7 @@ async function emitFindingsOutputs(toolState, requiredControls) {
|
|
|
316986
317226
|
if (a.level === "error") error62(a.message, props);
|
|
316987
317227
|
else warning(a.message, props);
|
|
316988
317228
|
}
|
|
316989
|
-
const sarifPath =
|
|
317229
|
+
const sarifPath = join72(workspace, SARIF_FILE);
|
|
316990
317230
|
if (session.emittedSarifPath && resolve10(session.emittedSarifPath) === resolve10(sarifPath)) {
|
|
316991
317231
|
log2.info(`\xBB findings output: replacing the agent-emitted ${SARIF_FILE} with the gate's`);
|
|
316992
317232
|
}
|
|
@@ -317416,9 +317656,9 @@ async function runAgentWithWatchdogs(params) {
|
|
|
317416
317656
|
}
|
|
317417
317657
|
|
|
317418
317658
|
// src/phases/startToolPlane.ts
|
|
317419
|
-
import { existsSync as
|
|
317659
|
+
import { existsSync as existsSync19, readdirSync as readdirSync10 } from "node:fs";
|
|
317420
317660
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
317421
|
-
import { join as
|
|
317661
|
+
import { join as join73 } from "node:path";
|
|
317422
317662
|
|
|
317423
317663
|
// src/utils/cloud/runTerminalStatus.ts
|
|
317424
317664
|
var TERMINAL_STATE_KEY = "run_terminal";
|
|
@@ -318246,8 +318486,8 @@ ${instructions.user}` : null,
|
|
|
318246
318486
|
log2.info(instructions.full);
|
|
318247
318487
|
});
|
|
318248
318488
|
if (toolContext.agentId === "opencode") {
|
|
318249
|
-
const pluginDir =
|
|
318250
|
-
const hasPlugins =
|
|
318489
|
+
const pluginDir = join73(process.cwd(), ".opencode", "plugin");
|
|
318490
|
+
const hasPlugins = existsSync19(pluginDir) && readdirSync10(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
318251
318491
|
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
318252
318492
|
log2.info(
|
|
318253
318493
|
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|