@moda-ai/cli 1.55.13 → 1.55.15
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 +12 -0
- package/dist/{agent-rd9deae9.js → agent-a2xwfeeh.js} +9 -9
- package/dist/{cli-ryd7nzke.js → cli-2ars875d.js} +2 -2
- package/dist/{cli-bfmn3bsk.js → cli-3gwbg0xp.js} +2 -2
- package/dist/{cli-4qe6n8am.js → cli-438g4nwh.js} +122 -2
- package/dist/{cli-k7vwt56h.js → cli-53mvsfgn.js} +3 -3
- package/dist/{cli-ssqb8bfw.js → cli-6ca1sz0a.js} +1 -1
- package/dist/{cli-m0fa54ny.js → cli-7fc44hhq.js} +2258 -383
- package/dist/{cli-90hb4ng0.js → cli-86q35fpq.js} +8 -8
- package/dist/{cli-09rr5smg.js → cli-atx672xv.js} +1 -1
- package/dist/{cli-kwz900s4.js → cli-b3cxcxgn.js} +5 -4
- package/dist/{cli-4x9w6ey7.js → cli-kf3d6y8c.js} +1 -1
- package/dist/{cli-34zdvcpg.js → cli-xy6zn80e.js} +5 -5
- package/dist/{cli-y8gbbe9k.js → cli-zdeywyn9.js} +4 -4
- package/dist/cli.js +299 -94
- package/dist/{doctor-8yb6r66e.js → doctor-e2xrbpmp.js} +9 -9
- package/dist/{fix-bt5zn07e.js → fix-v8xka487.js} +7 -7
- package/dist/{harness-9psndqtm.js → harness-28wbzncq.js} +6 -6
- package/dist/{harness-github-actions-nph5t2s7.js → harness-github-actions-301nak95.js} +6 -6
- package/dist/{index-qjaprhrv.js → index-ccdpy9ma.js} +11 -11
- package/dist/{production-intelligence-mg3qp2dg.js → production-intelligence-0wqcvvxk.js} +6 -5
- package/dist/{profile-commands-2g5bjzj2.js → profile-commands-f5kysjrv.js} +3 -3
- package/dist/prompt-source-mcp.js +6 -6
- package/dist/{prompts-mzazn8ce.js → prompts-jg0stjfb.js} +7 -7
- package/dist/{provision-rm0yc08f.js → provision-25bw5rd7.js} +4 -4
- package/dist/{registry-mbz65bjy.js → registry-39xzetsc.js} +4 -4
- package/dist/{skills-sagxq9sc.js → skills-gjesa81c.js} +6 -6
- package/dist/{sync-gkwexz68.js → sync-7ts9psky.js} +8 -8
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +31 -4
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
createCommandContext,
|
|
6
6
|
stringOption
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-kf3d6y8c.js";
|
|
8
8
|
import {
|
|
9
9
|
assertFlags,
|
|
10
10
|
assertPositionals,
|
|
11
11
|
requireTenant
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-53mvsfgn.js";
|
|
13
13
|
import {
|
|
14
14
|
ApiError,
|
|
15
15
|
callControlAPI
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-2ars875d.js";
|
|
17
17
|
import {
|
|
18
18
|
CliInputError,
|
|
19
19
|
CliInputRequiredError,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
resolveModaBaseUrl,
|
|
40
40
|
resolveTenantId,
|
|
41
41
|
shouldInventoryFile
|
|
42
|
-
} from "./cli-
|
|
42
|
+
} from "./cli-b3cxcxgn.js";
|
|
43
43
|
import {
|
|
44
44
|
__commonJS,
|
|
45
45
|
__require,
|
|
@@ -10304,7 +10304,7 @@ var require_typescript = __commonJS((exports, module) => {
|
|
|
10304
10304
|
identity: () => identity2,
|
|
10305
10305
|
identitySourceMapConsumer: () => identitySourceMapConsumer,
|
|
10306
10306
|
ignoreSourceNewlines: () => ignoreSourceNewlines,
|
|
10307
|
-
ignoredPaths: () =>
|
|
10307
|
+
ignoredPaths: () => ignoredPaths2,
|
|
10308
10308
|
importFromModuleSpecifier: () => importFromModuleSpecifier,
|
|
10309
10309
|
importSyntaxAffectsModuleResolution: () => importSyntaxAffectsModuleResolution,
|
|
10310
10310
|
indexOfAnyCharCode: () => indexOfAnyCharCode,
|
|
@@ -16689,7 +16689,7 @@ ${lanes.join(`
|
|
|
16689
16689
|
function getFileWatcherEventKind(oldTime, newTime) {
|
|
16690
16690
|
return oldTime === 0 ? 0 : newTime === 0 ? 2 : 1;
|
|
16691
16691
|
}
|
|
16692
|
-
var
|
|
16692
|
+
var ignoredPaths2 = ["/node_modules/.", "/.git", "/.#"];
|
|
16693
16693
|
var curSysLog = noop;
|
|
16694
16694
|
function sysLog(s) {
|
|
16695
16695
|
return curSysLog(s);
|
|
@@ -16907,7 +16907,7 @@ ${lanes.join(`
|
|
|
16907
16907
|
}
|
|
16908
16908
|
}
|
|
16909
16909
|
function isIgnoredPath(path, options) {
|
|
16910
|
-
return some(
|
|
16910
|
+
return some(ignoredPaths2, (searchPath) => isInPath(path, searchPath)) || isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames2, getCurrentDirectory);
|
|
16911
16911
|
}
|
|
16912
16912
|
function isInPath(path, searchPath) {
|
|
16913
16913
|
if (path.includes(searchPath))
|
|
@@ -31305,7 +31305,7 @@ ${lanes.join(`
|
|
|
31305
31305
|
}
|
|
31306
31306
|
}
|
|
31307
31307
|
function containsIgnoredPath(path) {
|
|
31308
|
-
return some(
|
|
31308
|
+
return some(ignoredPaths2, (p2) => path.includes(p2));
|
|
31309
31309
|
}
|
|
31310
31310
|
function getContainingNodeArray(node) {
|
|
31311
31311
|
if (!node.parent)
|
|
@@ -75258,11 +75258,11 @@ ${lanes.join(`
|
|
|
75258
75258
|
const resolved = resolveStructuredTypeMembers(type);
|
|
75259
75259
|
return !!(resolved.callSignatures.length || resolved.constructSignatures.length || resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType));
|
|
75260
75260
|
}
|
|
75261
|
-
function getTypeFacts(type,
|
|
75262
|
-
return getTypeFactsWorker(type,
|
|
75261
|
+
function getTypeFacts(type, mask22) {
|
|
75262
|
+
return getTypeFactsWorker(type, mask22) & mask22;
|
|
75263
75263
|
}
|
|
75264
|
-
function hasTypeFacts(type,
|
|
75265
|
-
return getTypeFacts(type,
|
|
75264
|
+
function hasTypeFacts(type, mask22) {
|
|
75265
|
+
return getTypeFacts(type, mask22) !== 0;
|
|
75266
75266
|
}
|
|
75267
75267
|
function getTypeFactsWorker(type, callerOnlyNeeds) {
|
|
75268
75268
|
if (type.flags & (2097152 | 465829888)) {
|
|
@@ -110604,14 +110604,14 @@ ${lanes.join(`
|
|
|
110604
110604
|
return factory2.createModifiersFromModifierFlags(newFlags);
|
|
110605
110605
|
}
|
|
110606
110606
|
function ensureModifierFlags(node) {
|
|
110607
|
-
let
|
|
110607
|
+
let mask22 = 131071 ^ (1 | 1024 | 16);
|
|
110608
110608
|
let additions = needsDeclare && !isAlwaysType(node) ? 128 : 0;
|
|
110609
110609
|
const parentIsFile = node.parent.kind === 308;
|
|
110610
110610
|
if (!parentIsFile || isBundledEmit && parentIsFile && isExternalModule(node.parent)) {
|
|
110611
|
-
|
|
110611
|
+
mask22 ^= 128;
|
|
110612
110612
|
additions = 0;
|
|
110613
110613
|
}
|
|
110614
|
-
return maskModifierFlags(node,
|
|
110614
|
+
return maskModifierFlags(node, mask22, additions);
|
|
110615
110615
|
}
|
|
110616
110616
|
function transformHeritageClauses(nodes) {
|
|
110617
110617
|
return factory2.createNodeArray(filter(map(nodes, (clause) => factory2.updateHeritageClause(clause, visitNodes2(factory2.createNodeArray(filter(clause.types, (t) => {
|
|
@@ -121243,7 +121243,7 @@ ${lanes.join(`
|
|
|
121243
121243
|
if (endsWith(path, "/node_modules/.staging")) {
|
|
121244
121244
|
return removeSuffix(path, "/.staging");
|
|
121245
121245
|
}
|
|
121246
|
-
return some(
|
|
121246
|
+
return some(ignoredPaths2, (searchPath) => path.includes(searchPath)) ? undefined : path;
|
|
121247
121247
|
}
|
|
121248
121248
|
function perceivedOsRootLengthForWatching(pathComponents2, length2) {
|
|
121249
121249
|
if (length2 <= 1)
|
|
@@ -163646,20 +163646,20 @@ ${options.prefix}` : `
|
|
|
163646
163646
|
return rulesMapCache;
|
|
163647
163647
|
}
|
|
163648
163648
|
function getRuleActionExclusion(ruleAction) {
|
|
163649
|
-
let
|
|
163649
|
+
let mask22 = 0;
|
|
163650
163650
|
if (ruleAction & 1) {
|
|
163651
|
-
|
|
163651
|
+
mask22 |= 28;
|
|
163652
163652
|
}
|
|
163653
163653
|
if (ruleAction & 2) {
|
|
163654
|
-
|
|
163654
|
+
mask22 |= 96;
|
|
163655
163655
|
}
|
|
163656
163656
|
if (ruleAction & 28) {
|
|
163657
|
-
|
|
163657
|
+
mask22 |= 28;
|
|
163658
163658
|
}
|
|
163659
163659
|
if (ruleAction & 96) {
|
|
163660
|
-
|
|
163660
|
+
mask22 |= 96;
|
|
163661
163661
|
}
|
|
163662
|
-
return
|
|
163662
|
+
return mask22;
|
|
163663
163663
|
}
|
|
163664
163664
|
function createRulesMap(rules) {
|
|
163665
163665
|
const map2 = buildMap(rules);
|
|
@@ -163704,7 +163704,7 @@ ${options.prefix}` : `
|
|
|
163704
163704
|
return row * mapRowLength + column;
|
|
163705
163705
|
}
|
|
163706
163706
|
var maskBitSize = 5;
|
|
163707
|
-
var
|
|
163707
|
+
var mask2 = 31;
|
|
163708
163708
|
var mapRowLength = 166 + 1;
|
|
163709
163709
|
var RulesPosition = ((RulesPosition2) => {
|
|
163710
163710
|
RulesPosition2[RulesPosition2["StopRulesSpecific"] = 0] = "StopRulesSpecific";
|
|
@@ -163724,15 +163724,15 @@ ${options.prefix}` : `
|
|
|
163724
163724
|
function getInsertionIndex(indexBitmap, maskPosition) {
|
|
163725
163725
|
let index = 0;
|
|
163726
163726
|
for (let pos = 0;pos <= maskPosition; pos += maskBitSize) {
|
|
163727
|
-
index += indexBitmap &
|
|
163727
|
+
index += indexBitmap & mask2;
|
|
163728
163728
|
indexBitmap >>= maskBitSize;
|
|
163729
163729
|
}
|
|
163730
163730
|
return index;
|
|
163731
163731
|
}
|
|
163732
163732
|
function increaseInsertionIndex(indexBitmap, maskPosition) {
|
|
163733
|
-
const value = (indexBitmap >> maskPosition &
|
|
163734
|
-
Debug.assert((value &
|
|
163735
|
-
return indexBitmap & ~(
|
|
163733
|
+
const value = (indexBitmap >> maskPosition & mask2) + 1;
|
|
163734
|
+
Debug.assert((value & mask2) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
|
|
163735
|
+
return indexBitmap & ~(mask2 << maskPosition) | value << maskPosition;
|
|
163736
163736
|
}
|
|
163737
163737
|
function createTextRangeWithKind(pos, end, kind) {
|
|
163738
163738
|
const textRangeWithKind = { pos, end, kind };
|
|
@@ -166304,7 +166304,7 @@ ${options.prefix}` : `
|
|
|
166304
166304
|
identity: () => identity2,
|
|
166305
166305
|
identitySourceMapConsumer: () => identitySourceMapConsumer,
|
|
166306
166306
|
ignoreSourceNewlines: () => ignoreSourceNewlines,
|
|
166307
|
-
ignoredPaths: () =>
|
|
166307
|
+
ignoredPaths: () => ignoredPaths2,
|
|
166308
166308
|
importFromModuleSpecifier: () => importFromModuleSpecifier,
|
|
166309
166309
|
importSyntaxAffectsModuleResolution: () => importSyntaxAffectsModuleResolution,
|
|
166310
166310
|
indexOfAnyCharCode: () => indexOfAnyCharCode,
|
|
@@ -177901,7 +177901,7 @@ import {
|
|
|
177901
177901
|
lstatSync as lstatSync10,
|
|
177902
177902
|
mkdtempSync as mkdtempSync2,
|
|
177903
177903
|
mkdirSync as mkdirSync6,
|
|
177904
|
-
readFileSync as
|
|
177904
|
+
readFileSync as readFileSync16,
|
|
177905
177905
|
readdirSync as readdirSync4,
|
|
177906
177906
|
writeFileSync as writeFileSync6,
|
|
177907
177907
|
rmSync as rmSync4
|
|
@@ -180097,7 +180097,7 @@ async function remoteAnalyzeFetch(url, apiKey, init, timeoutMs) {
|
|
|
180097
180097
|
|
|
180098
180098
|
// src/harness-register/register.ts
|
|
180099
180099
|
import * as fs from "node:fs";
|
|
180100
|
-
import { mkdtempSync, readFileSync as
|
|
180100
|
+
import { mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync3 } from "node:fs";
|
|
180101
180101
|
import { Readable as Readable2 } from "node:stream";
|
|
180102
180102
|
import { tmpdir } from "node:os";
|
|
180103
180103
|
import { basename as basename4, join as join8, resolve as resolve4 } from "node:path";
|
|
@@ -180105,7 +180105,7 @@ import { basename as basename4, join as join8, resolve as resolve4 } from "node:
|
|
|
180105
180105
|
// src/harness-register/collect-adapter.ts
|
|
180106
180106
|
import { createHash as createHash4 } from "node:crypto";
|
|
180107
180107
|
import { execFileSync } from "node:child_process";
|
|
180108
|
-
import { copyFileSync, createReadStream, existsSync as existsSync6, mkdirSync as mkdirSync4,
|
|
180108
|
+
import { copyFileSync, createReadStream, existsSync as existsSync6, mkdirSync as mkdirSync4, realpathSync as realpathSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
180109
180109
|
import { dirname as dirname5, join as join7 } from "node:path";
|
|
180110
180110
|
|
|
180111
180111
|
// ../../packages/harness-bundle/src/contract.ts
|
|
@@ -180406,6 +180406,22 @@ function isCommentLine(line) {
|
|
|
180406
180406
|
const t = line.trimStart();
|
|
180407
180407
|
return t.startsWith("//") || t.startsWith("#") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("--") || t.startsWith("<!--");
|
|
180408
180408
|
}
|
|
180409
|
+
function globRegex(glob) {
|
|
180410
|
+
let out = "";
|
|
180411
|
+
for (let i = 0;i < glob.length; i++) {
|
|
180412
|
+
const ch = glob[i];
|
|
180413
|
+
if (ch === "*" && glob[i + 1] === "*" && glob[i + 2] === "/") {
|
|
180414
|
+
out += "(?:.*/)?";
|
|
180415
|
+
i += 2;
|
|
180416
|
+
} else if (ch === "*")
|
|
180417
|
+
out += "[^/]*";
|
|
180418
|
+
else if (ch === "?")
|
|
180419
|
+
out += "[^/]";
|
|
180420
|
+
else
|
|
180421
|
+
out += ch.replace(/[.+^${}()|[\]\\]/, "\\$&");
|
|
180422
|
+
}
|
|
180423
|
+
return new RegExp(`^${out}$`);
|
|
180424
|
+
}
|
|
180409
180425
|
|
|
180410
180426
|
// ../../packages/harness-bundle/src/detect/files.ts
|
|
180411
180427
|
var DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
@@ -180727,6 +180743,7 @@ function pyStringStateAfter(line, open) {
|
|
|
180727
180743
|
}
|
|
180728
180744
|
return open;
|
|
180729
180745
|
}
|
|
180746
|
+
var PY_HEADER = /^(?:if|elif|else|try|except|finally|with|for|while|async\s+with|async\s+for)\b[^:]*:\s*/;
|
|
180730
180747
|
function pyImports(text) {
|
|
180731
180748
|
const out = [];
|
|
180732
180749
|
const lines = text.split(/\r?\n/);
|
|
@@ -180737,24 +180754,34 @@ function pyImports(text) {
|
|
|
180737
180754
|
inString = pyStringStateAfter(line, inString);
|
|
180738
180755
|
if (wasInString !== undefined)
|
|
180739
180756
|
continue;
|
|
180740
|
-
|
|
180741
|
-
|
|
180742
|
-
|
|
180743
|
-
|
|
180744
|
-
|
|
180745
|
-
|
|
180746
|
-
|
|
180747
|
-
|
|
180748
|
-
|
|
180749
|
-
|
|
180750
|
-
|
|
180757
|
+
const literals = [];
|
|
180758
|
+
const code = line.replace(/(?:\b[rRbBuUfF]{1,2})?(['"])((?:\\.|(?!\1).)*)\1/g, (_m, _q, body) => `\x00${literals.push(body) - 1}\x00`).replace(/#.*$/, "");
|
|
180759
|
+
for (const m of code.matchAll(/\b(?:importlib\.import_module|__import__)\(\s*\u0000(\d+)\u0000/g)) {
|
|
180760
|
+
const spec = literals[Number(m[1])];
|
|
180761
|
+
if (/^[A-Za-z_][\w.]*$/.test(spec))
|
|
180762
|
+
out.push({ spec, line: i + 1 });
|
|
180763
|
+
}
|
|
180764
|
+
for (let stmt of code.split(";")) {
|
|
180765
|
+
stmt = stmt.trim();
|
|
180766
|
+
for (let h = PY_HEADER.exec(stmt);h; h = PY_HEADER.exec(stmt))
|
|
180767
|
+
stmt = stmt.slice(h[0].length);
|
|
180768
|
+
let m = /^from\s+([A-Za-z_][\w.]*)\s+import\s+(.+)/.exec(stmt);
|
|
180769
|
+
if (m) {
|
|
180770
|
+
out.push({ spec: m[1], line: i + 1 });
|
|
180771
|
+
let list = m[2];
|
|
180772
|
+
for (let j = i + 1;j < lines.length && (list.includes("(") && !list.includes(")") || /\\\s*$/.test(list)); j++)
|
|
180773
|
+
list += ` ${lines[j].replace(/#.*$/, "")}`;
|
|
180774
|
+
for (const raw of list.replace(/[()\\]/g, "").split(",")) {
|
|
180775
|
+
const name = raw.trim().split(/\s+as\s+/)[0];
|
|
180776
|
+
if (/^[A-Za-z_]\w*$/.test(name))
|
|
180777
|
+
out.push({ spec: `${m[1]}.${name}`, line: i + 1 });
|
|
180778
|
+
}
|
|
180779
|
+
continue;
|
|
180751
180780
|
}
|
|
180752
|
-
|
|
180753
|
-
|
|
180754
|
-
|
|
180755
|
-
|
|
180756
|
-
for (const raw of m[1].split(","))
|
|
180757
|
-
out.push({ spec: raw.trim().split(/\s+as\s+/)[0], line: i + 1 });
|
|
180781
|
+
m = /^import\s+([A-Za-z_][\w.]*(?:\s+as\s+\w+)?(?:\s*,\s*[A-Za-z_][\w.]*(?:\s+as\s+\w+)?)*)/.exec(stmt);
|
|
180782
|
+
if (m)
|
|
180783
|
+
for (const raw of m[1].split(","))
|
|
180784
|
+
out.push({ spec: raw.trim().split(/\s+as\s+/)[0], line: i + 1 });
|
|
180758
180785
|
}
|
|
180759
180786
|
}
|
|
180760
180787
|
return out;
|
|
@@ -180873,7 +180900,7 @@ var PROVIDERS = [
|
|
|
180873
180900
|
p("libsql", "database", "engine", { npm: ["@libsql/client", "drizzle-orm/libsql", "@prisma/adapter-libsql"], py: ["libsql_client", "libsql_experimental", "libsql"], pypi: ["libsql-client", "libsql-experimental"], hosts: [".turso.io"], env: ["TURSO_*", "LIBSQL_*"], schemes: ["libsql"], images: ["ghcr.io/tursodatabase/libsql-server"] }),
|
|
180874
180901
|
p("mssql", "database", "engine", { npm: ["mssql", "tedious"], py: ["pyodbc", "pymssql"], go: ["github.com/microsoft/go-mssqldb", "github.com/denisenkom/go-mssqldb"], hosts: [".database.windows.net"], env: ["MSSQL_*"], schemes: ["mssql", "sqlserver"], images: ["mcr.microsoft.com/mssql/server", "mcr.microsoft.com/azure-sql-edge"] }),
|
|
180875
180902
|
p("cloudflare.d1", "database", "emulated", { npm: ["drizzle-orm/d1", "@prisma/adapter-d1"] }),
|
|
180876
|
-
p("redis", "kv", "engine", { npm: ["redis", "ioredis", "@redis/client", "@upstash/redis", "@vercel/kv"], py: ["redis", "aioredis", "upstash_redis", "valkey"], pypi: ["redis", "aioredis", "upstash-redis", "valkey"], go: ["github.com/redis/go-redis", "github.com/go-redis/redis", "github.com/gomodule/redigo"], gems: ["redis"], hosts: [".upstash.io", ".redis.cache.windows.net", ".redislabs.com", ".redis-cloud.com", ".cache.amazonaws.com"], env: ["REDIS_*", "UPSTASH_REDIS_*", "KV_REST_API_*", "KV_URL", "VALKEY_*", "*_REDIS_URL"], schemes: ["redis", "rediss", "valkey"], images: ["redis", "valkey/valkey", "redislabs/redismod", "redis/redis-stack", "redis/redis-stack-server", "bitnami/redis", "eqalpha/keydb", "docker.dragonflydb.io/dragonflydb/dragonfly"] }),
|
|
180903
|
+
p("redis", "kv", "engine", { npm: ["redis", "ioredis", "@redis/client", "@upstash/redis", "@vercel/kv", "bullmq", "bull", "bee-queue", "@nestjs/bull", "@nestjs/bullmq"], py: ["redis", "aioredis", "upstash_redis", "valkey"], pypi: ["redis", "aioredis", "upstash-redis", "valkey"], go: ["github.com/redis/go-redis", "github.com/go-redis/redis", "github.com/gomodule/redigo"], gems: ["redis"], hosts: [".upstash.io", ".redis.cache.windows.net", ".redislabs.com", ".redis-cloud.com", ".cache.amazonaws.com"], env: ["REDIS_*", "UPSTASH_REDIS_*", "KV_REST_API_*", "KV_URL", "VALKEY_*", "*_REDIS_URL"], schemes: ["redis", "rediss", "valkey"], images: ["redis", "valkey/valkey", "redislabs/redismod", "redis/redis-stack", "redis/redis-stack-server", "bitnami/redis", "eqalpha/keydb", "docker.dragonflydb.io/dragonflydb/dragonfly"] }),
|
|
180877
180904
|
p("memcached", "kv", "engine", { npm: ["memjs", "memcached"], py: ["pymemcache", "pylibmc"], env: ["MEMCACHE_*", "MEMCACHED_*"], schemes: ["memcached"], images: ["memcached"] }),
|
|
180878
180905
|
p("mongodb", "document", "engine", { npm: ["mongodb", "mongoose"], py: ["pymongo", "motor", "mongoengine", "beanie"], go: ["go.mongodb.org/mongo-driver"], gems: ["mongoid", "mongo"], hosts: [".mongodb.net"], env: ["MONGODB_*", "MONGO_*"], schemes: ["mongodb", "mongodb+srv"], images: ["mongo", "mongodb/mongodb-community-server", "bitnami/mongodb"] }),
|
|
180879
180906
|
p("clickhouse", "database", "engine", { npm: ["@clickhouse/client", "@clickhouse/client-web"], py: ["clickhouse_connect", "clickhouse_driver"], pypi: ["clickhouse-connect", "clickhouse-driver"], go: ["github.com/ClickHouse/clickhouse-go"], hosts: [".clickhouse.cloud"], env: ["CLICKHOUSE_*"], schemes: ["clickhouse"], images: ["clickhouse/clickhouse-server", "yandex/clickhouse-server"] }),
|
|
@@ -180887,7 +180914,7 @@ var PROVIDERS = [
|
|
|
180887
180914
|
p("snowflake", "database", "none", { npm: ["snowflake-sdk"], py: ["snowflake.connector", "snowflake.snowpark"], pypi: ["snowflake-connector-python"], hosts: [".snowflakecomputing.com"], env: ["SNOWFLAKE_*"] }),
|
|
180888
180915
|
p("aws.s3", "object_store", "emulated", { npm: ["@aws-sdk/client-s3", "@aws-sdk/lib-storage", "@aws-sdk/s3-request-presigner", "@aws-sdk/s3-presigned-post", "minio"], py: ["minio", "s3fs"], go: [...goAws("s3"), "github.com/minio/minio-go"], hosts: [/(^|\.)s3([.-][a-z0-9-]+)?\.amazonaws\.com$/], env: ["S3_*", "AWS_S3_*", "*_S3_BUCKET*", "MINIO_*"], schemes: ["s3"], images: ["minio/minio", "quay.io/minio/minio", "bitnami/minio"], code: [botoTracked("s3"), awsV2("S3")] }),
|
|
180889
180916
|
p("cloudflare.r2", "object_store", "emulated", { hosts: [".r2.cloudflarestorage.com", ".r2.dev"], env: ["R2_*", "*_R2_*"] }),
|
|
180890
|
-
p("gcp.
|
|
180917
|
+
p("gcp.storage", "object_store", "emulated", { npm: ["@google-cloud/storage", "firebase-admin/storage"], py: ["google.cloud.storage", "firebase_admin.storage"], pypi: ["google-cloud-storage"], go: ["cloud.google.com/go/storage"], hosts: ["storage.googleapis.com"], env: ["GCS_*", "*_GCS_*"], schemes: ["gs"], images: ["fsouza/fake-gcs-server"] }),
|
|
180891
180918
|
p("azure.blob", "object_store", "emulated", { npm: ["@azure/storage-blob"], py: ["azure.storage.blob"], pypi: ["azure-storage-blob"], go: ["github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"], hosts: [/\.blob\.core\.windows\.net$/], env: ["AZURE_STORAGE_*", "AzureWebJobsStorage"], images: ["mcr.microsoft.com/azure-storage/azurite"] }),
|
|
180892
180919
|
p("vercel.blob", "object_store", "saas", { npm: ["@vercel/blob"], hosts: [".blob.vercel-storage.com"], env: ["BLOB_READ_WRITE_TOKEN"] }),
|
|
180893
180920
|
p("uploadthing", "object_store", "saas", { npm: ["uploadthing", "@uploadthing/react"], env: ["UPLOADTHING_*"], hosts: ["api.uploadthing.com"] }),
|
|
@@ -180905,7 +180932,6 @@ var PROVIDERS = [
|
|
|
180905
180932
|
p("kafka", "queue", "engine", { npm: ["kafkajs", "node-rdkafka", "@confluentinc/kafka-javascript"], py: ["kafka", "confluent_kafka", "aiokafka"], pypi: ["kafka-python", "confluent-kafka", "aiokafka"], go: ["github.com/segmentio/kafka-go", "github.com/confluentinc/confluent-kafka-go", "github.com/IBM/sarama", "github.com/Shopify/sarama"], hosts: [".confluent.cloud"], env: ["KAFKA_*"], schemes: ["kafka"], images: ["confluentinc/cp-kafka", "bitnami/kafka", "apache/kafka", "wurstmeister/kafka", "redpandadata/redpanda", "docker.redpanda.com/redpandadata/redpanda", "vectorized/redpanda"] }),
|
|
180906
180933
|
p("rabbitmq", "queue", "engine", { npm: ["amqplib", "amqp-connection-manager"], py: ["pika", "aio_pika", "kombu"], pypi: ["pika", "aio-pika", "kombu"], go: ["github.com/rabbitmq/amqp091-go", "github.com/streadway/amqp"], hosts: [".cloudamqp.com"], env: ["RABBITMQ_*", "AMQP_*"], schemes: ["amqp", "amqps"], images: ["rabbitmq", "bitnami/rabbitmq"] }),
|
|
180907
180934
|
p("nats", "queue", "engine", { npm: ["nats"], py: ["nats"], pypi: ["nats-py"], go: ["github.com/nats-io/nats.go"], env: ["NATS_*"], schemes: ["nats"], images: ["nats"] }),
|
|
180908
|
-
p("bullmq", "queue", "engine", { npm: ["bullmq", "bull", "bee-queue"] }),
|
|
180909
180935
|
p("celery", "queue", "engine", { py: ["celery"], env: ["CELERY_*"] }),
|
|
180910
180936
|
p("inngest", "queue", "emulated", { npm: ["inngest"], py: ["inngest"], env: ["INNGEST_*"], hosts: ["api.inngest.com", "inn.gs"] }),
|
|
180911
180937
|
p("trigger.dev", "queue", "saas", { npm: ["@trigger.dev/sdk"], env: ["TRIGGER_*"], hosts: ["api.trigger.dev"] }),
|
|
@@ -180921,7 +180947,11 @@ var PROVIDERS = [
|
|
|
180921
180947
|
p("aws.bedrock_agentcore", "function.invoke", "none", { npm: ["bedrock-agentcore", "@aws-sdk/client-bedrock-agentcore", "@aws-sdk/client-bedrock-agentcore-control"], py: ["bedrock_agentcore"], pypi: ["bedrock-agentcore"], env: ["BEDROCK_AGENTCORE_*"], code: [botoTracked("bedrock-agentcore", "bedrock-agentcore-control")] }),
|
|
180922
180948
|
p("cloud.metadata", "other", "emulated", { hosts: ["metadata.google.internal", "169.254.169.254", "metadata"] }),
|
|
180923
180949
|
p("gcp.secretmanager", "other", "saas", { npm: ["@google-cloud/secret-manager"], py: ["google.cloud.secretmanager"], pypi: ["google-cloud-secret-manager"], hosts: ["secretmanager.googleapis.com"] }),
|
|
180924
|
-
p("gcp.
|
|
180950
|
+
p("gcp.vision", "http.request", "saas", { npm: ["@google-cloud/vision"], py: ["google.cloud.vision"], pypi: ["google-cloud-vision"], hosts: ["vision.googleapis.com"] }),
|
|
180951
|
+
p("gcp.translate", "http.request", "saas", { npm: ["@google-cloud/translate"], py: ["google.cloud.translate", "google.cloud.translate_v2", "google.cloud.translate_v3"], pypi: ["google-cloud-translate"], hosts: ["translation.googleapis.com"] }),
|
|
180952
|
+
p("gcp.speech", "http.request", "saas", { npm: ["@google-cloud/speech", "@google-cloud/text-to-speech"], py: ["google.cloud.speech", "google.cloud.texttospeech"], hosts: ["speech.googleapis.com", "texttospeech.googleapis.com"] }),
|
|
180953
|
+
p("gcp.language", "http.request", "saas", { npm: ["@google-cloud/language"], py: ["google.cloud.language"], hosts: ["language.googleapis.com"] }),
|
|
180954
|
+
p("gcp.documentai", "http.request", "saas", { npm: ["@google-cloud/documentai", "@google-cloud/video-intelligence", "@google-cloud/dialogflow", "@google-cloud/dlp"], py: ["google.cloud.documentai", "google.cloud.videointelligence", "google.cloud.dialogflow"], hosts: ["documentai.googleapis.com"] }),
|
|
180925
180955
|
p("cloudflare.api", "http.request", "saas", { npm: ["cloudflare"], py: ["cloudflare"], hosts: ["api.cloudflare.com"], env: ["CLOUDFLARE_API_TOKEN", "CF_API_TOKEN"] }),
|
|
180926
180956
|
p("pinecone", "vector_store", "emulated", { npm: ["@pinecone-database/pinecone", "@langchain/pinecone"], py: ["pinecone", "langchain_pinecone"], pypi: ["pinecone", "pinecone-client"], hosts: [".pinecone.io"], env: ["PINECONE_*"], images: ["ghcr.io/pinecone-io/pinecone-local"] }),
|
|
180927
180957
|
p("weaviate", "vector_store", "engine", { npm: ["weaviate-client", "weaviate-ts-client", "@langchain/weaviate"], py: ["weaviate"], pypi: ["weaviate-client"], hosts: [".weaviate.network", ".weaviate.cloud"], env: ["WEAVIATE_*"], images: ["semitechnologies/weaviate", "cr.weaviate.io/semitechnologies/weaviate"] }),
|
|
@@ -180943,7 +180973,7 @@ var PROVIDERS = [
|
|
|
180943
180973
|
p("anthropic", "model.inference", "model", { npm: ["@anthropic-ai/sdk", "@ai-sdk/anthropic", "@langchain/anthropic", "@anthropic-ai/claude-agent-sdk", "@anthropic-ai/claude-code", "@anthropic-ai/bedrock-sdk", "@anthropic-ai/vertex-sdk"], py: ["anthropic", "langchain_anthropic", "claude_agent_sdk", "claude_code_sdk", "llama_index.llms.anthropic"], pypi: ["langchain-anthropic", "claude-agent-sdk", "claude-code-sdk"], go: ["github.com/anthropics/anthropic-sdk-go"], hosts: ["api.anthropic.com"], env: ["ANTHROPIC_*"] }),
|
|
180944
180974
|
p("openrouter", "model.inference", "model", { npm: ["@openrouter/ai-sdk-provider", "@openrouter/sdk"], hosts: ["openrouter.ai"], env: ["OPENROUTER_*"] }),
|
|
180945
180975
|
p("google.genai", "model.inference", "model", { npm: ["@google/genai", "@google/generative-ai", "@ai-sdk/google", "@langchain/google-genai", "@genkit-ai/googleai", "@genkit-ai/google-genai"], py: ["google.genai", "google.generativeai", "langchain_google_genai"], pypi: ["google-genai", "google-generativeai", "langchain-google-genai"], go: ["google.golang.org/genai", "github.com/google/generative-ai-go"], hosts: ["generativelanguage.googleapis.com"], env: ["GEMINI_*", "GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_*", "GOOGLE_GENAI_*"] }),
|
|
180946
|
-
p("google.vertex", "model.inference", "model", { npm: ["@google-cloud/vertexai", "@ai-sdk/google-vertex", "@langchain/google-vertexai", "@genkit-ai/vertexai"], py: ["vertexai", "google.cloud.aiplatform", "langchain_google_vertexai"], pypi: ["google-cloud-aiplatform", "langchain-google-vertexai"], hosts: [/aiplatform\.googleapis\.com$/], env: ["VERTEX_*", "GOOGLE_VERTEX_*"] }),
|
|
180976
|
+
p("google.vertex", "model.inference", "model", { npm: ["@google-cloud/vertexai", "@ai-sdk/google-vertex", "@langchain/google-vertexai", "@genkit-ai/vertexai"], py: ["vertexai", "google.cloud.aiplatform", "langchain_google_vertexai", "pydantic_ai.models.vertexai"], pypi: ["google-cloud-aiplatform", "langchain-google-vertexai"], hosts: [/aiplatform\.googleapis\.com$/], env: ["VERTEX_*", "GOOGLE_VERTEX_*", "GOOGLE_GENAI_USE_VERTEXAI"], code: [/\bvertexai\.init\s*\(/, /\bClient\s*\([^)]*\bvertexai\s*=\s*True/, /\bnew\s+GoogleGenAI\s*\(\s*\{[^}]*\bvertexai\s*:\s*true/, /GOOGLE_GENAI_USE_VERTEXAI['"]?\s*\]?\s*=\s*['"]?(?:True|true|TRUE|1)\b/] }),
|
|
180947
180977
|
p("aws.bedrock", "model.inference", "model", { npm: ["@aws-sdk/client-bedrock-runtime", "@aws-sdk/client-bedrock-agent-runtime", "@ai-sdk/amazon-bedrock", "@langchain/aws"], py: ["langchain_aws"], pypi: ["langchain-aws"], go: goAws("bedrockruntime"), hosts: [/^bedrock(-runtime|-agent-runtime)?\.[a-z0-9-]+\.amazonaws\.com$/], env: ["BEDROCK_*", "AWS_BEDROCK_*"], code: [botoTracked("bedrock-runtime", "bedrock-agent-runtime", "bedrock")] }),
|
|
180948
180978
|
p("azure.openai", "model.inference", "model", { npm: ["@azure/openai", "@ai-sdk/azure"], py: ["azure.ai.inference", "azure.ai.openai"], hosts: [/\.openai\.azure\.com$/, /\.services\.ai\.azure\.com$/], env: ["AZURE_OPENAI_*", "AZURE_AI_*"], code: [/\bAzureOpenAI\s*\(/, /\bAzureChatOpenAI\s*\(/, /\bnew\s+AzureOpenAI\s*\(/] }),
|
|
180949
180979
|
p("groq", "model.inference", "model", { npm: ["groq-sdk", "@ai-sdk/groq", "@langchain/groq"], py: ["groq", "langchain_groq"], hosts: ["api.groq.com"], env: ["GROQ_*"] }),
|
|
@@ -180999,7 +181029,7 @@ var PROVIDERS = [
|
|
|
180999
181029
|
p("shopify", "http.request", "saas", { npm: ["@shopify/shopify-api", "@shopify/admin-api-client", "shopify-api-node"], py: ["shopify"], pypi: ["ShopifyAPI"], hosts: [".myshopify.com"], env: ["SHOPIFY_*"] }),
|
|
181000
181030
|
p("jira", "http.request", "saas", { npm: ["jira-client", "jira.js"], py: ["jira", "atlassian"], pypi: ["atlassian-python-api"], hosts: [".atlassian.net"], env: ["JIRA_*", "ATLASSIAN_*", "CONFLUENCE_*"] }),
|
|
181001
181031
|
p("airtable", "http.request", "saas", { npm: ["airtable"], py: ["pyairtable"], hosts: ["api.airtable.com"], env: ["AIRTABLE_*"] }),
|
|
181002
|
-
p("google.apis", "http.request", "saas", { npm: ["googleapis", "@googleapis/sheets", "@googleapis/gmail", "@googleapis/drive", "@googleapis/calendar"], py: ["googleapiclient", "google_auth_oauthlib"
|
|
181032
|
+
p("google.apis", "http.request", "saas", { npm: ["googleapis", "@googleapis/sheets", "@googleapis/gmail", "@googleapis/drive", "@googleapis/calendar"], py: ["googleapiclient", "google_auth_oauthlib"], pypi: ["google-api-python-client", "google-auth-oauthlib"], go: ["google.golang.org/api"], hosts: ["www.googleapis.com", "sheets.googleapis.com", "gmail.googleapis.com", "oauth2.googleapis.com", "accounts.google.com", ".googleapis.com"], env: ["GOOGLE_CLIENT_*", "GOOGLE_SERVICE_ACCOUNT*"] }),
|
|
181003
181033
|
p("google.maps", "http.request", "saas", { npm: ["@googlemaps/google-maps-services-js"], py: ["googlemaps"], hosts: ["maps.googleapis.com", "places.googleapis.com"], env: ["GOOGLE_MAPS_*"] }),
|
|
181004
181034
|
p("serpapi", "http.request", "saas", { npm: ["serpapi"], py: ["serpapi"], pypi: ["google-search-results"], hosts: ["serpapi.com"], env: ["SERPAPI_*", "SERP_API_*"] }),
|
|
181005
181035
|
p("serper", "http.request", "saas", { hosts: ["google.serper.dev"], env: ["SERPER_*"], code: [/\bSerperDevTool\s*\(/] }),
|
|
@@ -181025,22 +181055,52 @@ var PROVIDERS = [
|
|
|
181025
181055
|
p("arxiv", "http.request", "saas", { py: ["arxiv"], hosts: ["export.arxiv.org"], code: [/\bArxiv(?:Retriever|APIWrapper|QueryRun|Loader)\s*\(/] }),
|
|
181026
181056
|
p("linkup", "http.request", "saas", { npm: ["linkup-sdk"], py: ["linkup"], pypi: ["linkup-sdk"], hosts: ["api.linkup.so"], env: ["LINKUP_*"] }),
|
|
181027
181057
|
p("ncbi", "http.request", "saas", { hosts: ["eutils.ncbi.nlm.nih.gov", "pubmed.ncbi.nlm.nih.gov"], code: [/\bPubMed(?:APIWrapper|QueryRun|Retriever)\s*\(/], env: ["NCBI_*", "PUBMED_*"] }),
|
|
181058
|
+
p("mcp", "function.invoke", "saas", { npm: ["@modelcontextprotocol/sdk/client", "@ai-sdk/mcp", "@langchain/mcp-adapters"], py: ["mcp.client", "langchain_mcp_adapters", "fastmcp.client"], code: [/\baddMcpServer\s*\(/, /\bcreateSdkMcpServer\s*\(/, /\bexperimental_createMCPClient\s*\(|\bcreateMCPClient\s*\(/, /\bmcpServers\b/, /\bMultiServerMCPClient\s*\(/, /\bClientSession\s*\(\s*read/] }),
|
|
181028
181059
|
p("pusher", "http.request", "saas", { npm: ["pusher"], py: ["pusher"], hosts: [".pusher.com"], env: ["PUSHER_*"] }),
|
|
181029
181060
|
p("ably", "http.request", "saas", { npm: ["ably"], py: ["ably"], hosts: ["rest.ably.io", "realtime.ably.io"], env: ["ABLY_*"] }),
|
|
181030
181061
|
p("mapbox", "http.request", "saas", { hosts: ["api.mapbox.com"], env: ["MAPBOX_*", "*_MAPBOX_*"] }),
|
|
181031
181062
|
p("openweathermap", "http.request", "saas", { hosts: ["api.openweathermap.org"], env: ["OPENWEATHER*"] }),
|
|
181032
181063
|
p("open-meteo", "http.request", "saas", { hosts: ["api.open-meteo.com", "geocoding-api.open-meteo.com", "archive-api.open-meteo.com"] }),
|
|
181033
|
-
p("
|
|
181034
|
-
p("
|
|
181035
|
-
p("
|
|
181036
|
-
p("
|
|
181037
|
-
p("
|
|
181038
|
-
p("
|
|
181064
|
+
p("sentry", "other", "saas", { npm: ["@sentry/node", "@sentry/nextjs", "@sentry/cloudflare", "@sentry/serverless", "@sentry/aws-serverless", "@sentry/bun", "@sentry/react"], py: ["sentry_sdk"], pypi: ["sentry-sdk"], hosts: [".ingest.sentry.io", ".ingest.us.sentry.io"], env: ["SENTRY_*", "*_SENTRY_*"] }),
|
|
181065
|
+
p("langsmith", "other", "saas", { npm: ["langsmith"], py: ["langsmith"], hosts: ["api.smith.langchain.com"], env: ["LANGSMITH_*", "LANGCHAIN_API_KEY", "LANGCHAIN_TRACING*", "LANGCHAIN_ENDPOINT", "LANGCHAIN_PROJECT"] }),
|
|
181066
|
+
p("langfuse", "other", "saas", { npm: ["langfuse", "@langfuse/tracing", "@langfuse/otel", "langfuse-vercel"], py: ["langfuse"], hosts: ["cloud.langfuse.com", "us.cloud.langfuse.com"], env: ["LANGFUSE_*"] }),
|
|
181067
|
+
p("posthog", "other", "saas", { npm: ["posthog-node", "posthog-js"], py: ["posthog"], hosts: [".posthog.com"], env: ["POSTHOG_*", "*_POSTHOG_*"] }),
|
|
181068
|
+
p("datadog", "other", "saas", { npm: ["dd-trace"], py: ["ddtrace", "datadog"], hosts: [".datadoghq.com", ".datadoghq.eu"], env: ["DD_API_KEY", "DD_SITE", "DATADOG_*"] }),
|
|
181069
|
+
p("braintrust", "other", "saas", { npm: ["braintrust"], py: ["braintrust"], hosts: ["api.braintrust.dev"], env: ["BRAINTRUST_*"] }),
|
|
181039
181070
|
p("telemetry.gcp", "other", "saas", { npm: ["@google-cloud/logging", "@google-cloud/logging-winston", "@google-cloud/monitoring", "@google-cloud/opentelemetry-cloud-trace-exporter", "@google-cloud/trace-agent"], py: ["google.cloud.logging"], pypi: ["google-cloud-logging"] }),
|
|
181040
181071
|
p("telemetry.azure_monitor", "other", "saas", { npm: ["@azure/monitor-opentelemetry", "applicationinsights"], py: ["azure.monitor.opentelemetry", "opencensus.ext.azure"], pypi: ["azure-monitor-opentelemetry"], hosts: [".applicationinsights.azure.com", "dc.services.visualstudio.com"], env: ["APPLICATIONINSIGHTS_*"] }),
|
|
181041
|
-
p("telemetry.otlp", "other", "saas", { npm: ["@opentelemetry/exporter-trace-otlp-http", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/exporter-trace-otlp-grpc", "@vercel/otel"], py: ["opentelemetry.exporter.otlp"], pypi: ["opentelemetry-exporter-otlp"], env: ["OTEL_EXPORTER_OTLP_*"] }),
|
|
181042
181072
|
p("local_fs", "file", "engine", { code: [/\b(?:fs\.)?(?:writeFile|writeFileSync|appendFile|appendFileSync)\s*\(/, /\bBun\.write\s*\(/, /\bopen\([^)\n]*,\s*['"](?:w|a|wb|ab|w\+|a\+)['"]/, /\.write_(?:text|bytes)\s*\(/] })
|
|
181043
181073
|
];
|
|
181074
|
+
var SDK_DEFAULT_ENV = {
|
|
181075
|
+
"npm:openai": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_ORG_ID", "OPENAI_PROJECT_ID"],
|
|
181076
|
+
"pypi:openai": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_ORG_ID", "OPENAI_PROJECT_ID"],
|
|
181077
|
+
"npm:@openai/agents": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_DEFAULT_MODEL", "OPENAI_AGENTS_DISABLE_TRACING", "OPENAI_AGENTS_DONT_LOG_MODEL_DATA", "OPENAI_AGENTS_DONT_LOG_TOOL_DATA"],
|
|
181078
|
+
"pypi:openai-agents": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_DEFAULT_MODEL", "OPENAI_AGENTS_DISABLE_SENSITIVE_DATA", "OPENAI_AGENTS_DONT_LOG_MODEL_DATA", "OPENAI_AGENTS_DONT_LOG_TOOL_DATA"],
|
|
181079
|
+
"npm:@anthropic-ai/sdk": ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"],
|
|
181080
|
+
"pypi:anthropic": ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"],
|
|
181081
|
+
"npm:@anthropic-ai/claude-agent-sdk": ["ANTHROPIC_API_KEY"],
|
|
181082
|
+
"pypi:claude-agent-sdk": ["ANTHROPIC_API_KEY"],
|
|
181083
|
+
"npm:@google/genai": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
181084
|
+
"pypi:google-genai": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
181085
|
+
"npm:groq-sdk": ["GROQ_API_KEY"],
|
|
181086
|
+
"pypi:groq": ["GROQ_API_KEY"],
|
|
181087
|
+
"npm:@mistralai/mistralai": ["MISTRAL_API_KEY"],
|
|
181088
|
+
"pypi:mistralai": ["MISTRAL_API_KEY"],
|
|
181089
|
+
"npm:cohere-ai": ["CO_API_KEY"],
|
|
181090
|
+
"pypi:cohere": ["CO_API_KEY"],
|
|
181091
|
+
"npm:together-ai": ["TOGETHER_API_KEY"],
|
|
181092
|
+
"pypi:together": ["TOGETHER_API_KEY"],
|
|
181093
|
+
"npm:replicate": ["REPLICATE_API_TOKEN"],
|
|
181094
|
+
"pypi:replicate": ["REPLICATE_API_TOKEN"],
|
|
181095
|
+
"npm:@pinecone-database/pinecone": ["PINECONE_API_KEY"],
|
|
181096
|
+
"pypi:pinecone": ["PINECONE_API_KEY"],
|
|
181097
|
+
"pypi:logfire": ["LOGFIRE_TOKEN"],
|
|
181098
|
+
"npm:@pydantic/logfire-node": ["LOGFIRE_TOKEN"],
|
|
181099
|
+
"npm:langsmith": ["LANGSMITH_API_KEY"],
|
|
181100
|
+
"pypi:langsmith": ["LANGSMITH_API_KEY"],
|
|
181101
|
+
"npm:@livekit/agents": ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"],
|
|
181102
|
+
"pypi:livekit-agents": ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"]
|
|
181103
|
+
};
|
|
181044
181104
|
var WORKERS_BINDINGS = {
|
|
181045
181105
|
d1_databases: { capability: "database", provider: "cloudflare.d1", cls: "emulated", type: "D1" },
|
|
181046
181106
|
kv_namespaces: { capability: "kv", provider: "cloudflare.kv", cls: "emulated", type: "KV" },
|
|
@@ -181075,8 +181135,42 @@ var CFN_RESOURCES = {
|
|
|
181075
181135
|
"AWS::ElastiCache::CacheCluster": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181076
181136
|
"AWS::ElastiCache::ReplicationGroup": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181077
181137
|
"AWS::ElastiCache::ServerlessCache": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181078
|
-
"AWS::OpenSearchService::Domain": { capability: "search", provider: "opensearch", cls: "engine" }
|
|
181138
|
+
"AWS::OpenSearchService::Domain": { capability: "search", provider: "opensearch", cls: "engine" },
|
|
181139
|
+
"AWS::Cognito::UserPoolClient": { capability: "auth", provider: "aws.cognito", cls: "emulated" },
|
|
181140
|
+
"AWS::Cognito::IdentityPool": { capability: "auth", provider: "aws.cognito", cls: "emulated" },
|
|
181141
|
+
"AWS::Bedrock::Agent": { capability: "model.inference", provider: "aws.bedrock", cls: "model" },
|
|
181142
|
+
"AWS::Bedrock::KnowledgeBase": { capability: "vector_store", provider: "aws.bedrock", cls: "none" },
|
|
181143
|
+
"AWS::BedrockAgentCore::Runtime": { capability: "function.invoke", provider: "aws.bedrock_agentcore", cls: "none" },
|
|
181144
|
+
"AWS::BedrockAgentCore::Gateway": { capability: "function.invoke", provider: "aws.bedrock_agentcore", cls: "none" },
|
|
181145
|
+
"AWS::BedrockAgentCore::Memory": { capability: "document", provider: "aws.bedrock_agentcore", cls: "none" }
|
|
181146
|
+
};
|
|
181147
|
+
var CFN_SHORT = {
|
|
181148
|
+
dynamodb: "dynamodb",
|
|
181149
|
+
s3: "s3",
|
|
181150
|
+
sqs: "sqs",
|
|
181151
|
+
sns: "sns",
|
|
181152
|
+
events: "eventbridge",
|
|
181153
|
+
kinesis: "kinesis",
|
|
181154
|
+
stepfunctions: "stepfunctions",
|
|
181155
|
+
cognito: "cognito",
|
|
181156
|
+
secretsmanager: "secretsmanager",
|
|
181157
|
+
ssm: "ssm",
|
|
181158
|
+
elasticache: "elasticache",
|
|
181159
|
+
opensearchservice: "opensearch",
|
|
181160
|
+
rds: "rds"
|
|
181079
181161
|
};
|
|
181162
|
+
function cfnBindingType(type) {
|
|
181163
|
+
const [, service = "", resource = ""] = type.split("::");
|
|
181164
|
+
if (type === "AWS::Serverless::SimpleTable")
|
|
181165
|
+
return "dynamodb";
|
|
181166
|
+
if (type === "AWS::Serverless::StateMachine")
|
|
181167
|
+
return "stepfunctions";
|
|
181168
|
+
const svc = service.toLowerCase();
|
|
181169
|
+
if (svc === "bedrock" || svc === "bedrockagentcore") {
|
|
181170
|
+
return `${svc === "bedrock" ? "bedrock" : "agentcore"}_${resource.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase()}`;
|
|
181171
|
+
}
|
|
181172
|
+
return CFN_SHORT[svc] ?? svc;
|
|
181173
|
+
}
|
|
181080
181174
|
function rdsEngineProvider(engine) {
|
|
181081
181175
|
const e = (engine ?? "").toLowerCase();
|
|
181082
181176
|
if (e.includes("postgres"))
|
|
@@ -181573,6 +181667,9 @@ function scanSources(ctx, declaredPypi, workspaceNames) {
|
|
|
181573
181667
|
evidence: evidence.slice(0, 3)
|
|
181574
181668
|
};
|
|
181575
181669
|
ctx.gaps.push(gap);
|
|
181670
|
+
if (evidence.some((e) => !isTestPath(e.path))) {
|
|
181671
|
+
ctx.signals.push({ kind: "code", capability: "http.request", provider: "http", host: undefined, evidence: evidence[0] });
|
|
181672
|
+
}
|
|
181576
181673
|
}
|
|
181577
181674
|
return { frameworks };
|
|
181578
181675
|
}
|
|
@@ -181597,6 +181694,7 @@ var CODE_ENV_RES = [
|
|
|
181597
181694
|
/\bos\.environ\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
|
|
181598
181695
|
/\bos\.environ\.get\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
|
|
181599
181696
|
/\bos\.getenv\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
|
|
181697
|
+
/['"]([A-Z_][A-Z0-9_]*)['"]\s+(?:not\s+)?in\s+os\.environ\b/g,
|
|
181600
181698
|
/\bos\.Getenv\(\s*"([A-Za-z_][A-Za-z0-9_]*)"/g,
|
|
181601
181699
|
/\bos\.LookupEnv\(\s*"([A-Za-z_][A-Za-z0-9_]*)"/g,
|
|
181602
181700
|
/\bENV\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
|
|
@@ -181604,11 +181702,15 @@ var CODE_ENV_RES = [
|
|
|
181604
181702
|
/\benv\.(?:str|int|bool|url|db|list|float|json)\(\s*['"]([A-Z_][A-Z0-9_]*)['"]/g,
|
|
181605
181703
|
/\bconfig\(\s*['"]([A-Z_][A-Z0-9_]*)['"]/g
|
|
181606
181704
|
];
|
|
181607
|
-
var ENV_EXAMPLE_FILE = /^(?:\.env\.(?:example|sample|template|dist|defaults
|
|
181705
|
+
var ENV_EXAMPLE_FILE = /^(?:\.env\.(?:example|sample|template|dist|defaults?|local\.example|development\.example)|\.env-(?:example|sample)|example\.env|sample\.env|env\.example|\.dev\.vars\.example|\.dev\.vars\.sample)$/;
|
|
181608
181706
|
var ENV_SECRET_FILE = /^(?:\.env(?:\.[\w.-]+)?|\.dev\.vars)$/;
|
|
181609
181707
|
function isEnvExampleFile(path) {
|
|
181610
181708
|
return ENV_EXAMPLE_FILE.test(basename2(path));
|
|
181611
181709
|
}
|
|
181710
|
+
function isEnvFile(path) {
|
|
181711
|
+
const name = basename2(path);
|
|
181712
|
+
return (ENV_EXAMPLE_FILE.test(name) || ENV_SECRET_FILE.test(name)) && !/\.test\b/.test(name);
|
|
181713
|
+
}
|
|
181612
181714
|
function scanEnv(ctx) {
|
|
181613
181715
|
for (const file of ctx.files) {
|
|
181614
181716
|
const name = basename2(file.path);
|
|
@@ -181634,6 +181736,8 @@ function scanEnv(ctx) {
|
|
|
181634
181736
|
continue;
|
|
181635
181737
|
const at = lineIndex(file.text);
|
|
181636
181738
|
const lines = splitLines(file.text);
|
|
181739
|
+
if (file.path.endsWith(".py") && /\bBaseSettings\b/.test(file.text))
|
|
181740
|
+
settingsFields(ctx, file.path, lines);
|
|
181637
181741
|
const seen = new Set;
|
|
181638
181742
|
for (const re of CODE_ENV_RES) {
|
|
181639
181743
|
re.lastIndex = 0;
|
|
@@ -181655,6 +181759,63 @@ function scanEnv(ctx) {
|
|
|
181655
181759
|
}
|
|
181656
181760
|
}
|
|
181657
181761
|
}
|
|
181762
|
+
function sdkDefaultEnv(ctx, declared) {
|
|
181763
|
+
const seen = new Set;
|
|
181764
|
+
for (const d of declared) {
|
|
181765
|
+
const file = ctx.byPath.get(d.path);
|
|
181766
|
+
if (d.dev || file?.isTest)
|
|
181767
|
+
continue;
|
|
181768
|
+
const key = `${d.eco}:${d.eco === "pypi" ? d.name.toLowerCase().replace(/[_.]/g, "-") : d.name}`;
|
|
181769
|
+
for (const name of SDK_DEFAULT_ENV[key] ?? []) {
|
|
181770
|
+
if (seen.has(`${name}:${d.path}`))
|
|
181771
|
+
continue;
|
|
181772
|
+
seen.add(`${name}:${d.path}`);
|
|
181773
|
+
const line = d.line ?? 1;
|
|
181774
|
+
ctx.env.push({
|
|
181775
|
+
name,
|
|
181776
|
+
source: `${d.path}:${line}`,
|
|
181777
|
+
evidence: { path: d.path, line, excerpt: excerpt(file ? splitLines(file.text)[line - 1] : undefined), detector: `${DETECTOR2}.sdk_default` }
|
|
181778
|
+
});
|
|
181779
|
+
}
|
|
181780
|
+
}
|
|
181781
|
+
}
|
|
181782
|
+
function settingsFields(ctx, path, lines) {
|
|
181783
|
+
const settings = new Map([["BaseSettings", ""]]);
|
|
181784
|
+
for (let i = 0;i < lines.length; i++) {
|
|
181785
|
+
const cls = /^class\s+(\w+)\s*\(([^)]*)\)/.exec(lines[i]);
|
|
181786
|
+
const base = cls?.[2].split(",").map((b) => b.trim().split(".").pop() ?? "").find((b) => settings.has(b));
|
|
181787
|
+
if (!cls || base === undefined)
|
|
181788
|
+
continue;
|
|
181789
|
+
const body = [];
|
|
181790
|
+
let prefix = settings.get(base) ?? "";
|
|
181791
|
+
let docstring = false;
|
|
181792
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
181793
|
+
const line = lines[j];
|
|
181794
|
+
if (line.trim() !== "" && !/^\s/.test(line))
|
|
181795
|
+
break;
|
|
181796
|
+
const quotes = (line.match(/"""|'''/g) ?? []).length;
|
|
181797
|
+
if (docstring || quotes > 0) {
|
|
181798
|
+
if (quotes % 2 === 1)
|
|
181799
|
+
docstring = !docstring;
|
|
181800
|
+
continue;
|
|
181801
|
+
}
|
|
181802
|
+
const p2 = /\benv_prefix\s*=\s*['"]([^'"]*)['"]/.exec(line);
|
|
181803
|
+
if (p2)
|
|
181804
|
+
prefix = p2[1];
|
|
181805
|
+
const m = /^ {4}([A-Za-z][A-Za-z0-9_]*)\s*:/.exec(line);
|
|
181806
|
+
if (m && m[1] !== "model_config" && !isCommentLine(line))
|
|
181807
|
+
body.push({ name: m[1], line: j + 1 });
|
|
181808
|
+
}
|
|
181809
|
+
settings.set(cls[1], prefix);
|
|
181810
|
+
for (const f of body) {
|
|
181811
|
+
ctx.env.push({
|
|
181812
|
+
name: `${prefix}${f.name}`.toUpperCase(),
|
|
181813
|
+
source: `${path}:${f.line}`,
|
|
181814
|
+
evidence: { path, line: f.line, excerpt: excerpt(lines[f.line - 1]), detector: `${DETECTOR2}.settings` }
|
|
181815
|
+
});
|
|
181816
|
+
}
|
|
181817
|
+
}
|
|
181818
|
+
}
|
|
181658
181819
|
var GENERIC_DB_URL = /^(?:[A-Z0-9]+_)*(?:DATABASE|DB)_(?:URL|URI|DSN|CONNECTION(?:_STRING)?)(?:_[A-Z0-9]+)*$|^SQLALCHEMY_DATABASE_UR[IL]$|^(?:DATABASE|DB)$/;
|
|
181659
181820
|
function isGenericDatabaseUrl(name) {
|
|
181660
181821
|
return GENERIC_DB_URL.test(name);
|
|
@@ -181689,6 +181850,8 @@ function envSignals(ctx) {
|
|
|
181689
181850
|
}
|
|
181690
181851
|
if (resolved)
|
|
181691
181852
|
continue;
|
|
181853
|
+
if (/(?:^|_)(?:USE|ENABLE|ENABLED)(?:_|$)/.test(obs.name) && obs.value !== undefined && /^(?:false|0|no|off)$/i.test(obs.value.trim()))
|
|
181854
|
+
continue;
|
|
181692
181855
|
const sig = matchEnvName(obs.name);
|
|
181693
181856
|
if (sig) {
|
|
181694
181857
|
ctx.signals.push({ kind: "env", capability: sig.capability, provider: sig.provider, envVar: obs.name, evidence: ev, weak: isTestPath(ev.path) });
|
|
@@ -181698,14 +181861,18 @@ function envSignals(ctx) {
|
|
|
181698
181861
|
var SECRET_NAME = /(?:^|_)(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|PASS|CREDENTIALS?|PRIVATE|AUTH|SIGNING|SALT|CERT|DSN)(?:_|$)|APIKEY|_PAT$/;
|
|
181699
181862
|
var ENDPOINT_NAME = /(?:^|_)(?:URL|URI|ENDPOINT|HOST|HOSTNAME|BASE|BASE_URL|ADDR|ADDRESS|PORT|DSN|CONNECTION(?:_STRING)?|SERVER|REGION|BUCKET|TABLE|QUEUE|TOPIC|DATABASE|DB|INDEX|NAMESPACE|PROJECT_ID|ACCOUNT_ID)(?:_|$)/;
|
|
181700
181863
|
var SELF_CONFIG = /^(?:NODE_ENV|ENV|ENVIRONMENT|APP_ENV|DEBUG|LOG_LEVEL|LOGLEVEL|PORT|HOST|HOSTNAME|TZ|PYTHONPATH|PYTHONUNBUFFERED|PATH|HOME|CI|VERCEL_ENV|VERCEL_URL|NEXT_PUBLIC_APP_URL|APP_URL|SITE_URL|PUBLIC_URL|BASE_URL|NEXTAUTH_URL|AUTH_URL|DJANGO_SETTINGS_MODULE|FLASK_APP|FLASK_ENV|WORKERS|WEB_CONCURRENCY|ALLOWED_HOSTS|DJANGO_ALLOWED_HOSTS|CORS_ORIGINS?)$/;
|
|
181864
|
+
var PUBLIC_NAME = /^(?:NEXT_PUBLIC_|VITE_|PUBLIC_|EXPO_PUBLIC_|REACT_APP_|NUXT_PUBLIC_|GATSBY_)|(?:^|_)(?:PUBLISHABLE|ANON)_KEY$/;
|
|
181865
|
+
var CREDENTIAL_SETTING = /^(?:ENABLE|ENABLED|ALLOW|DISABLE|USE|MIN|MAX)_|_(?:LENGTH|EXPIRY|EXPIRES_IN|INTERVAL|INTERVAL_\w+|SECONDS|SECS|HOURS|MINUTES|MS|TTL|MAX|WINDOW|PREFIX|PREFIX_VAR|KIND|PATH|FORMAT|PROVIDER|DURATION|VIOLATIONS|COUNT|LIMIT)$/;
|
|
181701
181866
|
function classifyName(name) {
|
|
181702
|
-
if (SELF_CONFIG.test(name))
|
|
181867
|
+
if (SELF_CONFIG.test(name) || PUBLIC_NAME.test(name))
|
|
181703
181868
|
return "config";
|
|
181704
181869
|
if (SECRET_NAME.test(name))
|
|
181705
181870
|
return "secret";
|
|
181706
181871
|
return "unknown";
|
|
181707
181872
|
}
|
|
181708
181873
|
function classifyLinked(name, deps) {
|
|
181874
|
+
if (PUBLIC_NAME.test(name))
|
|
181875
|
+
return "config";
|
|
181709
181876
|
const isModel = deps.some((d) => d.capability === "model.inference");
|
|
181710
181877
|
const secret = SECRET_NAME.test(name) && !/(?:^|_)(?:URL|URI|HOST|ENDPOINT)$/.test(name);
|
|
181711
181878
|
if (isModel) {
|
|
@@ -181721,6 +181888,21 @@ function classifyLinked(name, deps) {
|
|
|
181721
181888
|
return "dependency_endpoint";
|
|
181722
181889
|
return "config";
|
|
181723
181890
|
}
|
|
181891
|
+
var AMBIENT_ENV = /^(?:PATH|HOME|USER|LOGNAME|SHELL|PWD|OLDPWD|TERM|LANG|LANGUAGE|LC_\w+|TMPDIR|TEMP|TMP|EDITOR|PAGER|LD_PRELOAD|LD_LIBRARY_PATH|PYTHONDONTWRITEBYTECODE|PYTHONASYNCIODEBUG|PYTHONHASHSEED|PYTHONFAULTHANDLER|NX_\w+|APPDATA|LOCALAPPDATA|PROGRAMDATA|USERPROFILE|COMSPEC|SYSTEMROOT|WINDIR|COLORTERM|COLORFGBG|NO_COLOR|FORCE_COLOR|TERM_PROGRAM|DEBIAN_FRONTEND|BUILDPLATFORM|TARGETPLATFORM|TARGETOS|TARGETARCH|TARGETVARIANT|BUILDOS|BUILDARCH|BUILDKIT_\w+|DOCKER_BUILDKIT|DOCKER_METADATA_\w+|GITHUB_(?:ACTIONS|WORKSPACE|SHA|REF|REF_NAME|REF_TYPE|HEAD_REF|BASE_REF|RUN_ID|RUN_NUMBER|RUN_ATTEMPT|REPOSITORY|REPOSITORY_OWNER|EVENT_NAME|EVENT_PATH|ENV|OUTPUT|STEP_SUMMARY|PATH|JOB|ACTOR|SERVER_URL|API_URL|GRAPHQL_URL|WORKFLOW|ACTION|ACTION_PATH|RETENTION_DAYS)|RUNNER_\w+|ACTIONS_\w+|CARGO_\w+|RUSTFLAGS|BASH_\w+|BASH|ZSH_\w+|CONDA_\w+|VIRTUAL_ENV|PYENV_\w+|NVM_\w+|npm_\w+|CODECOV_TOKEN|DOCKERHUB_\w+|UV_\w+|PIP_\w+|POETRY_\w+|CMAKE_\w+|CC|CXX|CFLAGS|CXXFLAGS|LDFLAGS|MAKEFLAGS|ANDROID_\w+|XDG_\w+|SSH_\w+|DISPLAY|SHLVL|_)$/;
|
|
181892
|
+
var TOOLING_SOURCE = /(^|\/)(\.github|\.gitlab|\.circleci|\.buildkite|ci|\.devcontainer)\/|(^|\/)(Makefile|GNUmakefile|CMakeLists\.txt|Justfile|Jenkinsfile)$|(^|\/)(?:jest|vitest|playwright|karma|cypress)\.config\.[cm]?[jt]s$|\.(cmake|mk|bat|ps1)$/;
|
|
181893
|
+
function isRuntimeEnvSource(source) {
|
|
181894
|
+
if (/ ARG$/.test(source))
|
|
181895
|
+
return false;
|
|
181896
|
+
const path = source.split(/[: ]/)[0];
|
|
181897
|
+
if (TOOLING_SOURCE.test(path))
|
|
181898
|
+
return false;
|
|
181899
|
+
if (!/[/.]/.test(path))
|
|
181900
|
+
return true;
|
|
181901
|
+
return !isTestPath(path);
|
|
181902
|
+
}
|
|
181903
|
+
function isAmbientEnv(name) {
|
|
181904
|
+
return AMBIENT_ENV.test(name);
|
|
181905
|
+
}
|
|
181724
181906
|
function buildEnvReferences(ctx, deps) {
|
|
181725
181907
|
const byName = new Map;
|
|
181726
181908
|
for (const ref of ctx.manifest?.env ?? []) {
|
|
@@ -181747,12 +181929,16 @@ function buildEnvReferences(ctx, deps) {
|
|
|
181747
181929
|
const entry = byName.get(name);
|
|
181748
181930
|
const linked = depsByEnv.get(name) ?? [];
|
|
181749
181931
|
let classification;
|
|
181750
|
-
if (
|
|
181932
|
+
if (PUBLIC_NAME.test(name))
|
|
181933
|
+
classification = "config";
|
|
181934
|
+
else if (linked.length > 0)
|
|
181751
181935
|
classification = classifyLinked(name, linked);
|
|
181752
181936
|
else if (entry.manifest && entry.manifest.classification !== "unknown")
|
|
181753
181937
|
classification = entry.manifest.classification;
|
|
181754
181938
|
else
|
|
181755
181939
|
classification = classifyName(name);
|
|
181940
|
+
if (classification === "secret" && CREDENTIAL_SETTING.test(name))
|
|
181941
|
+
classification = "config";
|
|
181756
181942
|
const ref = {
|
|
181757
181943
|
name,
|
|
181758
181944
|
sources: uniqSorted(entry.sources).slice(0, 20),
|
|
@@ -182107,6 +182293,9 @@ function detectMigrations(ctx) {
|
|
|
182107
182293
|
if (env)
|
|
182108
182294
|
ctx.envLinks.push({ envVar: env, capability: providerName === "mongodb" ? "document" : "database", provider: providerName, evidence });
|
|
182109
182295
|
}
|
|
182296
|
+
if (/\bextensions\s*=\s*\[[^\]]*\b(?:pgvector|vector)\b/.test(file.text)) {
|
|
182297
|
+
ctx.signals.push({ kind: "config", capability: "vector_store", provider: "pgvector", evidence: evidenceAt(file.path, file.text, findLine(file.text, /\bextensions\s*=/), `${DETECTOR3}.prisma`) });
|
|
182298
|
+
}
|
|
182110
182299
|
continue;
|
|
182111
182300
|
}
|
|
182112
182301
|
if (/^drizzle\.config\.(ts|js|mjs|cjs|json)$/.test(name)) {
|
|
@@ -182119,10 +182308,21 @@ function detectMigrations(ctx) {
|
|
|
182119
182308
|
b.schemaFiles.add(file.path);
|
|
182120
182309
|
for (const s of schemas) {
|
|
182121
182310
|
const p2 = joinPath(dir, s);
|
|
182122
|
-
if (
|
|
182311
|
+
if (/[*?]/.test(s)) {
|
|
182312
|
+
const re = globRegex(p2);
|
|
182313
|
+
for (const f of ctx.files)
|
|
182314
|
+
if (re.test(f.path))
|
|
182315
|
+
b.schemaFiles.add(f.path);
|
|
182316
|
+
} else if (ctx.byPath.has(p2) || ctx.files.some((f) => isUnder(f.path, p2)))
|
|
182123
182317
|
b.schemaFiles.add(p2);
|
|
182124
182318
|
}
|
|
182125
|
-
|
|
182319
|
+
let outDir = joinPath(dir, out2);
|
|
182320
|
+
if (!ctx.files.some((f) => isUnder(f.path, outDir))) {
|
|
182321
|
+
const nestedRoots = ctx.files.filter((f) => /^(?:package\.json|pyproject\.toml|go\.mod)$/.test(basename2(f.path)) && dirname2(f.path) !== dir && isUnder(f.path, dir)).map((f) => dirname2(f.path));
|
|
182322
|
+
const sql = ctx.files.find((f) => isUnder(f.path, dir) && /(^|\/)migrations?\/[^/]+\.(?:sql|[cm]?[jt]s)$/.test(f.path) && !isTestPath(f.path) && !nestedRoots.some((r) => isUnder(f.path, r)));
|
|
182323
|
+
if (sql)
|
|
182324
|
+
outDir = dirname2(sql.path);
|
|
182325
|
+
}
|
|
182126
182326
|
if (ctx.files.some((f) => isUnder(f.path, outDir))) {
|
|
182127
182327
|
b.paths.add(outDir);
|
|
182128
182328
|
claimDir(outDir);
|
|
@@ -182276,13 +182476,50 @@ function detectMigrations(ctx) {
|
|
|
182276
182476
|
b.paths.add(dir);
|
|
182277
182477
|
}
|
|
182278
182478
|
}
|
|
182479
|
+
for (const f of ctx.files) {
|
|
182480
|
+
if (!/^wrangler\.(?:toml|jsonc?)$/.test(basename2(f.path)) || !/new_sqlite_classes/.test(f.text) || isTestPath(f.path))
|
|
182481
|
+
continue;
|
|
182482
|
+
ctx.gaps.push({
|
|
182483
|
+
kind: "durable_object_migrations",
|
|
182484
|
+
message: `${f.path} declares SQLite-backed Durable Object migrations; the workerd runtime adapter applies them`,
|
|
182485
|
+
evidence: [{ path: f.path, line: findLine(f.text, /new_sqlite_classes/), detector: DETECTOR3 }]
|
|
182486
|
+
});
|
|
182487
|
+
}
|
|
182488
|
+
const all = [...sets.values()];
|
|
182489
|
+
for (const b of all) {
|
|
182490
|
+
if (b.tool !== "raw_sql" || b.paths.size > 0)
|
|
182491
|
+
continue;
|
|
182492
|
+
const parent = [...b.schemaFiles].map(dirname2)[0];
|
|
182493
|
+
const sibling = all.find((o) => o !== b && o.tool === "raw_sql" && [...o.paths].some((p2) => isUnder(p2, parent ?? "\x00")));
|
|
182494
|
+
if (sibling) {
|
|
182495
|
+
for (const f of b.schemaFiles)
|
|
182496
|
+
sibling.schemaFiles.add(f);
|
|
182497
|
+
sibling.first = [...sibling.first ?? [], ...uniqSorted(b.schemaFiles)];
|
|
182498
|
+
if (sibling.engine === "unknown")
|
|
182499
|
+
sibling.engine = b.engine;
|
|
182500
|
+
b.schemaFiles.clear();
|
|
182501
|
+
}
|
|
182502
|
+
}
|
|
182279
182503
|
const out = [];
|
|
182504
|
+
const testOnly = (p2) => isTestPath(p2) || /(^|\/)(?:conformance|e2e|[\w-]+-tests?|tests?-[\w-]+|e2e-[\w-]+)\//.test(p2);
|
|
182280
182505
|
for (const b of [...sets.values()].sort((x, y) => cmp(`${x.tool}:${x.key}`, `${y.tool}:${y.key}`))) {
|
|
182506
|
+
if (b.tool === "django" && b.paths.size === 0)
|
|
182507
|
+
continue;
|
|
182281
182508
|
if (b.paths.size === 0 && b.schemaFiles.size === 0)
|
|
182282
182509
|
continue;
|
|
182283
|
-
if (b.
|
|
182510
|
+
if ([...b.paths, ...b.schemaFiles].every(testOnly))
|
|
182284
182511
|
continue;
|
|
182285
|
-
|
|
182512
|
+
if (b.paths.size === 0) {
|
|
182513
|
+
for (const f of b.schemaFiles)
|
|
182514
|
+
b.paths.add(f);
|
|
182515
|
+
ctx.gaps.push({
|
|
182516
|
+
kind: "migration_schema_only",
|
|
182517
|
+
message: `${b.tool} has no migration files at ${[...b.schemaFiles][0]}; the schema is applied directly`,
|
|
182518
|
+
evidence: [...b.schemaFiles].slice(0, 1).map((path) => ({ path, detector: DETECTOR3 }))
|
|
182519
|
+
});
|
|
182520
|
+
}
|
|
182521
|
+
const first = b.first ?? [];
|
|
182522
|
+
out.push({ engine: b.engine, tool: b.tool, paths: [...first, ...uniqSorted(b.paths).filter((x) => !first.includes(x))], schemaFiles: uniqSorted(b.schemaFiles) });
|
|
182286
182523
|
}
|
|
182287
182524
|
return out;
|
|
182288
182525
|
}
|
|
@@ -183290,6 +183527,19 @@ function fromPyproject(f, out) {
|
|
|
183290
183527
|
for (const req of asArray(uv["dev-dependencies"]))
|
|
183291
183528
|
if (typeof req === "string")
|
|
183292
183529
|
add(req, true);
|
|
183530
|
+
const hatch = isRecord2(tool.hatch) && isRecord2(tool.hatch.metadata) && isRecord2(tool.hatch.metadata.hooks) ? tool.hatch.metadata.hooks : {};
|
|
183531
|
+
for (const hook of Object.values(hatch)) {
|
|
183532
|
+
if (!isRecord2(hook))
|
|
183533
|
+
continue;
|
|
183534
|
+
for (const req of asArray(hook.dependencies))
|
|
183535
|
+
if (typeof req === "string")
|
|
183536
|
+
add(req, false);
|
|
183537
|
+
const extra = isRecord2(hook["optional-dependencies"]) ? hook["optional-dependencies"] : {};
|
|
183538
|
+
for (const [group, reqs] of Object.entries(extra))
|
|
183539
|
+
for (const req of asArray(reqs))
|
|
183540
|
+
if (typeof req === "string")
|
|
183541
|
+
add(req, /dev|test|lint|doc/i.test(group));
|
|
183542
|
+
}
|
|
183293
183543
|
}
|
|
183294
183544
|
function fromPipfile(f, out) {
|
|
183295
183545
|
let doc;
|
|
@@ -183553,7 +183803,7 @@ function cfnResources(ctx, resources, file, runtime) {
|
|
|
183553
183803
|
if (!spec)
|
|
183554
183804
|
continue;
|
|
183555
183805
|
const resource = asString(props.TableName) ?? asString(props.BucketName) ?? asString(props.QueueName) ?? asString(props.TopicName) ?? asString(props.Name) ?? asString(props.StateMachineName) ?? logicalId;
|
|
183556
|
-
runtime.bindings.push({ name: logicalId, type, resource });
|
|
183806
|
+
runtime.bindings.push({ name: logicalId, type: cfnBindingType(type), resource });
|
|
183557
183807
|
bindingSignal(ctx, { ...spec, binding: logicalId, resource, evidence: ev(file, logicalId) });
|
|
183558
183808
|
}
|
|
183559
183809
|
}
|
|
@@ -183603,6 +183853,30 @@ function detectSam(ctx, file, doc) {
|
|
|
183603
183853
|
envFromVariables(ctx, isRecord2(props.Environment) ? props.Environment.Variables : undefined, file, logicalId);
|
|
183604
183854
|
}
|
|
183605
183855
|
cfnResources(ctx, resources, file, runtime);
|
|
183856
|
+
const ids = Object.keys(resources);
|
|
183857
|
+
const direct = (text, self) => ids.filter((id) => id !== self && new RegExp(`\\b${escapeRe(id)}\\b`).test(text));
|
|
183858
|
+
const globalsText = JSON.stringify(isRecord2(doc.Globals) && isRecord2(doc.Globals.Function) ? doc.Globals.Function : {});
|
|
183859
|
+
const refs = new Map;
|
|
183860
|
+
for (const [fnId, res] of Object.entries(resources)) {
|
|
183861
|
+
if (!isRecord2(res) || !/^AWS::(?:Serverless|Lambda)::Function$/.test(asString(res.Type) ?? ""))
|
|
183862
|
+
continue;
|
|
183863
|
+
const seen = new Set(direct(`${JSON.stringify(res.Properties ?? {})} ${globalsText}`, fnId));
|
|
183864
|
+
const queue = [...seen];
|
|
183865
|
+
while (queue.length > 0) {
|
|
183866
|
+
const id = queue.shift();
|
|
183867
|
+
const other = resources[id];
|
|
183868
|
+
if (!isRecord2(other) || /^AWS::(?:Serverless|Lambda)::Function$/.test(asString(other.Type) ?? ""))
|
|
183869
|
+
continue;
|
|
183870
|
+
for (const next of direct(JSON.stringify(other.Properties ?? {}), id)) {
|
|
183871
|
+
if (next !== fnId && !seen.has(next)) {
|
|
183872
|
+
seen.add(next);
|
|
183873
|
+
queue.push(next);
|
|
183874
|
+
}
|
|
183875
|
+
}
|
|
183876
|
+
}
|
|
183877
|
+
refs.set(fnId, seen);
|
|
183878
|
+
}
|
|
183879
|
+
ctx.functionRefs.set(file.path, refs);
|
|
183606
183880
|
if (runtime.entrypoints.length === 0)
|
|
183607
183881
|
return;
|
|
183608
183882
|
return runtime;
|
|
@@ -183666,6 +183940,17 @@ var CDK_RESOURCE_TYPES = {
|
|
|
183666
183940
|
StateMachine: "AWS::StepFunctions::StateMachine",
|
|
183667
183941
|
UserPool: "AWS::Cognito::UserPool"
|
|
183668
183942
|
};
|
|
183943
|
+
function cdkTrigger(text, variable) {
|
|
183944
|
+
const v = `(?:this\\.|self\\.)?${escapeRe(variable)}\\b`;
|
|
183945
|
+
const re = (pattern) => new RegExp(pattern);
|
|
183946
|
+
if (re(`LambdaIntegration\\(\\s*(?:['"][^'"]*['"]\\s*,\\s*)?${v}`).test(text) || re(`LambdaRestApi\\([^)]*?handler\\s*[:=]\\s*${v}`).test(text) || re(`${v}\\s*\\.\\s*(?:addFunctionUrl|add_function_url)\\(`).test(text) || re(`FunctionUrl\\([^)]*?function\\s*[:=]\\s*${v}`).test(text))
|
|
183947
|
+
return "http";
|
|
183948
|
+
if (re(`${v}\\s*\\.\\s*(?:addEventSource|add_event_source)\\(\\s*(?:new\\s+)?(?:\\w+\\.)?SqsEventSource`).test(text))
|
|
183949
|
+
return "queue";
|
|
183950
|
+
if (re(`Schedule\\.[\\s\\S]{0,400}LambdaFunction\\(\\s*${v}`).test(text))
|
|
183951
|
+
return "cron";
|
|
183952
|
+
return "event";
|
|
183953
|
+
}
|
|
183669
183954
|
function detectCdk(ctx, cdkJson) {
|
|
183670
183955
|
const root = dirname2(cdkJson.path);
|
|
183671
183956
|
const runtime = newRuntime("aws_lambda", root, [cdkJson.path], [{ path: cdkJson.path, detector: `${DETECTOR4}.cdk` }], 0.8);
|
|
@@ -183691,7 +183976,8 @@ function detectCdk(ctx, cdkJson) {
|
|
|
183691
183976
|
runtime.runtimeVersion = lang.version;
|
|
183692
183977
|
}
|
|
183693
183978
|
const handlerRef = entry ? joinPath(root, entry) + (handler ? `#${handler}` : "") : asset ? `${joinPath(root, asset)}/${handler ?? ""}` : handler;
|
|
183694
|
-
|
|
183979
|
+
const variable = /(?:\b|\.)(\w+)\s*=\s*$/.exec(file.text.slice(Math.max(0, m.index - 120), m.index))?.[1];
|
|
183980
|
+
runtime.entrypoints.push({ name: id, handler: handlerRef, trigger: variable ? cdkTrigger(file.text, variable) : "event" });
|
|
183695
183981
|
runtime.evidence.push({ path: file.path, line: at(m.index), excerpt: excerpt(splitLines(file.text)[at(m.index) - 1]), detector: `${DETECTOR4}.cdk:${construct}` });
|
|
183696
183982
|
}
|
|
183697
183983
|
CDK_RESOURCE.lastIndex = 0;
|
|
@@ -183703,12 +183989,12 @@ function detectCdk(ctx, cdkJson) {
|
|
|
183703
183989
|
const type = CDK_RESOURCE_TYPES[kind];
|
|
183704
183990
|
if (type) {
|
|
183705
183991
|
const spec = CFN_RESOURCES[type];
|
|
183706
|
-
runtime.bindings.push({ name: id, type });
|
|
183992
|
+
runtime.bindings.push({ name: id, type: cfnBindingType(type) });
|
|
183707
183993
|
bindingSignal(ctx, { ...spec, binding: id, evidence });
|
|
183708
183994
|
} else if (kind === "DatabaseInstance" || kind === "DatabaseCluster") {
|
|
183709
183995
|
const engine = /DatabaseInstanceEngine\.(\w+)|DatabaseClusterEngine\.(\w+)/.exec(file.text.slice(m.index, m.index + 800));
|
|
183710
183996
|
const provider = rdsEngineProvider(engine?.[1] ?? engine?.[2]);
|
|
183711
|
-
runtime.bindings.push({ name: id, type:
|
|
183997
|
+
runtime.bindings.push({ name: id, type: "rds" });
|
|
183712
183998
|
bindingSignal(ctx, { capability: "database", provider, cls: "engine", binding: id, evidence });
|
|
183713
183999
|
}
|
|
183714
184000
|
}
|
|
@@ -183842,7 +184128,10 @@ function detectCloudflare(input) {
|
|
|
183842
184128
|
if (seen.has(key))
|
|
183843
184129
|
return false;
|
|
183844
184130
|
seen.add(key);
|
|
183845
|
-
|
|
184131
|
+
if (type === "QueueConsumer")
|
|
184132
|
+
return true;
|
|
184133
|
+
const normalized = type.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
184134
|
+
runtime.bindings.push(resource ? { name, type: normalized, resource } : { name, type: normalized });
|
|
183846
184135
|
return true;
|
|
183847
184136
|
};
|
|
183848
184137
|
const produced = new Set(sections(config).flatMap((section) => asArray(isRecord2(section.queues) ? section.queues.producers : undefined).map((p2) => isRecord2(p2) ? asString(p2.queue) : undefined)));
|
|
@@ -183915,7 +184204,9 @@ function detectCloudflare(input) {
|
|
|
183915
184204
|
const script = asString(item.script_name);
|
|
183916
184205
|
if (!addBinding(name, "DurableObject", className))
|
|
183917
184206
|
continue;
|
|
183918
|
-
if (script
|
|
184207
|
+
if (!script || workerNames.has(script)) {
|
|
184208
|
+
bindingSignal(ctx, { capability: "document", provider: "cloudflare.durable_objects", cls: "emulated", binding: name, resource: className, evidence: ev2(file, name) });
|
|
184209
|
+
} else {
|
|
183919
184210
|
bindingSignal(ctx, { capability: "function.invoke", provider: "cloudflare.durable_object", cls: "saas", binding: name, resource: className, evidence: ev2(file, name) });
|
|
183920
184211
|
ctx.gaps.push({ kind: "external_service_binding", message: `Durable Object ${name} lives in worker \`${script}\`, which is not in the bundle`, evidence: [ev2(file, name)] });
|
|
183921
184212
|
}
|
|
@@ -183961,6 +184252,21 @@ var DETECTOR6 = "runtime.container";
|
|
|
183961
184252
|
var DOCKERFILE = /^(?:Dockerfile|Containerfile)(?:\.[\w.-]+)?$|\.(?:Dockerfile|dockerfile)$/;
|
|
183962
184253
|
var COMPOSE = /^(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml$/;
|
|
183963
184254
|
var INFRA_IMAGES = /^(?:nginx|traefik|caddy|haproxy|envoyproxy\/envoy|busybox|alpine|ubuntu|debian|node|python|golang|grafana\/.*|prom\/.*|otel\/.*|jaegertracing\/.*|adminer|dpage\/pgadmin4|portainer\/.*|.*\/?(?:loki|promtail|tempo))$/;
|
|
184255
|
+
var PROJECT_MANIFEST = /^(?:package\.json|pyproject\.toml|requirements\.txt|setup\.py|go\.mod|Gemfile|Cargo\.toml|pom\.xml|build\.gradle(?:\.kts)?)$/;
|
|
184256
|
+
function buildContext(ctx, dir, copies) {
|
|
184257
|
+
if (copies.length === 0 || ctx.files.some((f) => dirname2(f.path) === dir && PROJECT_MANIFEST.test(basename2(f.path))))
|
|
184258
|
+
return dir;
|
|
184259
|
+
const has = (base, src) => {
|
|
184260
|
+
const p2 = joinPath(base, src);
|
|
184261
|
+
return ctx.byPath.has(p2) || ctx.files.some((f) => f.path.startsWith(`${p2}/`));
|
|
184262
|
+
};
|
|
184263
|
+
for (let base = dir;; base = dirname2(base)) {
|
|
184264
|
+
if (copies.every((src) => has(base, src)))
|
|
184265
|
+
return base;
|
|
184266
|
+
if (base === ".")
|
|
184267
|
+
return dir;
|
|
184268
|
+
}
|
|
184269
|
+
}
|
|
183964
184270
|
function parseInstructionArgs(rest) {
|
|
183965
184271
|
const t = rest.trim();
|
|
183966
184272
|
if (t.startsWith("[")) {
|
|
@@ -183973,7 +184279,7 @@ function parseInstructionArgs(rest) {
|
|
|
183973
184279
|
return ["sh", "-c", t];
|
|
183974
184280
|
}
|
|
183975
184281
|
function parseDockerfile(file) {
|
|
183976
|
-
const out = { path: file.path, from: [], expose: [], env: [], args: [], stages: {} };
|
|
184282
|
+
const out = { path: file.path, from: [], expose: [], env: [], args: [], copies: [], stages: {} };
|
|
183977
184283
|
const raw = splitLines(file.text);
|
|
183978
184284
|
const stages = new Map;
|
|
183979
184285
|
let stage = { base: "", expose: [], env: [] };
|
|
@@ -184018,6 +184324,11 @@ function parseDockerfile(file) {
|
|
|
184018
184324
|
for (const [name, value] of pairs)
|
|
184019
184325
|
if (name)
|
|
184020
184326
|
stage.env.push({ name, value, line: startLine });
|
|
184327
|
+
} else if ((instr === "COPY" || instr === "ADD") && !/--from=/.test(rest)) {
|
|
184328
|
+
const parts = rest.trim().startsWith("[") ? parseInstructionArgs(rest) : rest.split(/\s+/).filter((w) => !w.startsWith("--"));
|
|
184329
|
+
for (const src of parts.slice(0, -1))
|
|
184330
|
+
if (src && !/^https?:/.test(src) && !/[*?$]/.test(src) && src !== "." && src !== "./")
|
|
184331
|
+
out.copies.push(src.replace(/^\.\//, "").replace(/\/$/, ""));
|
|
184021
184332
|
} else if (instr === "ARG") {
|
|
184022
184333
|
const name = rest.split("=")[0].trim();
|
|
184023
184334
|
if (name)
|
|
@@ -184051,7 +184362,7 @@ function detectContainers(input) {
|
|
|
184051
184362
|
continue;
|
|
184052
184363
|
const df = parseDockerfile(file);
|
|
184053
184364
|
dockerfiles.push(df);
|
|
184054
|
-
const root = dirname2(file.path);
|
|
184365
|
+
const root = buildContext(ctx, dirname2(file.path), df.copies);
|
|
184055
184366
|
const base = df.from[df.from.length - 1] ?? "";
|
|
184056
184367
|
const lambda = /^(?:public\.ecr\.aws\/lambda\/|amazon\/aws-lambda-)/.test(base);
|
|
184057
184368
|
const kind = lambda ? "aws_lambda" : "container";
|
|
@@ -184136,12 +184447,16 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184136
184447
|
const ports = asArray(svc.ports);
|
|
184137
184448
|
const svcEntrypoint = composeArgs(svc.entrypoint);
|
|
184138
184449
|
const svcCommand = composeArgs(svc.command);
|
|
184139
|
-
|
|
184450
|
+
let serviceRoot = build !== undefined ? joinPath(base, typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".") : undefined;
|
|
184140
184451
|
if (build !== undefined) {
|
|
184141
184452
|
const context = typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".";
|
|
184142
|
-
const
|
|
184453
|
+
const contextRoot = joinPath(base, context);
|
|
184454
|
+
const dockerfile = joinPath(contextRoot, (isRecord2(build) ? asString(build.dockerfile) : undefined) ?? "Dockerfile");
|
|
184455
|
+
const dfDir = dirname2(dockerfile);
|
|
184456
|
+
const projectDir = dfDir !== contextRoot && isUnder(dfDir, contextRoot) && ctx.files.some((f) => dirname2(f.path) === dfDir && PROJECT_MANIFEST.test(basename2(f.path)));
|
|
184457
|
+
const root = projectDir ? dfDir : contextRoot;
|
|
184458
|
+
serviceRoot = root;
|
|
184143
184459
|
const key = `container:${root}`;
|
|
184144
|
-
const dockerfile = joinPath(root, (isRecord2(build) ? asString(build.dockerfile) : undefined) ?? "Dockerfile");
|
|
184145
184460
|
const contribution = contributions.get(dockerfile);
|
|
184146
184461
|
const runtime = byRoot.get(key) ?? newRuntime("container", root, [], [], 0.85);
|
|
184147
184462
|
runtime.configFiles.push(file.path);
|
|
@@ -184178,28 +184493,11 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184178
184493
|
if (sig) {
|
|
184179
184494
|
ctx.signals.push({ kind: "image", capability: sig.capability, provider: sig.provider, resource: name, evidence });
|
|
184180
184495
|
} else if (!INFRA_IMAGES.test(image.split(":")[0])) {
|
|
184181
|
-
|
|
184182
|
-
|
|
184183
|
-
|
|
184184
|
-
|
|
184185
|
-
|
|
184186
|
-
if (!runtime.entrypoints.some((e) => e.name === name)) {
|
|
184187
|
-
const entry = { name, handler: image, trigger: ports.length > 0 ? "http" : "unknown" };
|
|
184188
|
-
if (svcEntrypoint)
|
|
184189
|
-
entry.command = [...svcEntrypoint, ...svcCommand ?? []];
|
|
184190
|
-
else if (svcCommand) {
|
|
184191
|
-
ctx.gaps.push({
|
|
184192
|
-
kind: "unresolved_command",
|
|
184193
|
-
message: `Compose service \`${name}\` overrides the CMD of image \`${image}\`, whose ENTRYPOINT is not in the bundle; the full launch command is unknown`,
|
|
184194
|
-
evidence: [evidence]
|
|
184195
|
-
});
|
|
184196
|
-
}
|
|
184197
|
-
runtime.entrypoints.push(entry);
|
|
184198
|
-
}
|
|
184199
|
-
byRoot.set(key, runtime);
|
|
184200
|
-
} else {
|
|
184201
|
-
ctx.gaps.push({ kind: "unrecognized_service", message: `Compose service \`${name}\` uses image \`${image}\`, which maps to no known provider`, evidence: [evidence] });
|
|
184202
|
-
}
|
|
184496
|
+
ctx.gaps.push({
|
|
184497
|
+
kind: "unrecognized_service",
|
|
184498
|
+
message: `Compose service \`${name}\` runs prebuilt image \`${image}\`, which maps to no known provider; treated as a sidecar, not a harness entrypoint`,
|
|
184499
|
+
evidence: [evidence]
|
|
184500
|
+
});
|
|
184203
184501
|
}
|
|
184204
184502
|
}
|
|
184205
184503
|
const env = svc.environment;
|
|
@@ -184223,7 +184521,8 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184223
184521
|
source: `${file.path} services.${name}.environment`,
|
|
184224
184522
|
value: literal,
|
|
184225
184523
|
evidence: { path: file.path, line: l, excerpt: l ? excerpt(lines[l - 1]) : undefined, detector: `${DETECTOR6}.compose` },
|
|
184226
|
-
scopeRoot: serviceRoot
|
|
184524
|
+
scopeRoot: serviceRoot,
|
|
184525
|
+
service: name
|
|
184227
184526
|
});
|
|
184228
184527
|
}
|
|
184229
184528
|
}
|
|
@@ -184246,14 +184545,21 @@ var ROOT_MARKERS = new Set([
|
|
|
184246
184545
|
"host.json",
|
|
184247
184546
|
"deno.json",
|
|
184248
184547
|
"deno.jsonc",
|
|
184249
|
-
"Cargo.toml"
|
|
184548
|
+
"Cargo.toml",
|
|
184549
|
+
"project.json"
|
|
184250
184550
|
]);
|
|
184251
184551
|
function projectRoots(files) {
|
|
184252
|
-
const
|
|
184253
|
-
for (const f of files)
|
|
184254
|
-
if (ROOT_MARKERS.has(basename2(f.path)))
|
|
184255
|
-
|
|
184256
|
-
|
|
184552
|
+
const markers = new Map;
|
|
184553
|
+
for (const f of files) {
|
|
184554
|
+
if (!ROOT_MARKERS.has(basename2(f.path)))
|
|
184555
|
+
continue;
|
|
184556
|
+
const dir = dirname2(f.path);
|
|
184557
|
+
if (!markers.has(dir))
|
|
184558
|
+
markers.set(dir, new Set);
|
|
184559
|
+
markers.get(dir).add(basename2(f.path));
|
|
184560
|
+
}
|
|
184561
|
+
const pyProjects = [...markers.entries()].filter(([, m]) => m.has("pyproject.toml") || m.has("setup.py")).map(([d]) => d);
|
|
184562
|
+
return [...markers.entries()].filter(([dir, m]) => !(m.size === 1 && m.has("requirements.txt") && pyProjects.some((p2) => p2 !== dir && isUnder(dir, p2)))).map(([dir]) => dir).sort();
|
|
184257
184563
|
}
|
|
184258
184564
|
function nearestRoot(path, roots) {
|
|
184259
184565
|
let best;
|
|
@@ -184291,6 +184597,15 @@ function cloudRunMarkers(ctx) {
|
|
|
184291
184597
|
out.push({ root: dirname2(file.path), path: file.path, line: findLine(file.text, /serving\.knative\.dev/) });
|
|
184292
184598
|
continue;
|
|
184293
184599
|
}
|
|
184600
|
+
if (name.endsWith(".tf")) {
|
|
184601
|
+
const services = (file.text.match(/resource\s+"google_cloud_run(?:_v2)?_service"/g) ?? []).length;
|
|
184602
|
+
if (services > 0) {
|
|
184603
|
+
const dir = dirname2(file.path);
|
|
184604
|
+
const root = /^(?:terraform|infra|infrastructure|deploy|deployment)$/.test(basename2(dir)) ? dirname2(dir) : dir;
|
|
184605
|
+
out.push({ root, path: file.path, line: findLine(file.text, /google_cloud_run/), services });
|
|
184606
|
+
}
|
|
184607
|
+
continue;
|
|
184608
|
+
}
|
|
184294
184609
|
const re = /gcloud\s+(?:beta\s+)?run\s+(?:deploy|services\s+replace)\b([^\n]*)/g;
|
|
184295
184610
|
let m;
|
|
184296
184611
|
while ((m = re.exec(file.text)) !== null) {
|
|
@@ -184495,76 +184810,72 @@ function decoratedDefs(text, decorator) {
|
|
|
184495
184810
|
// ../../packages/harness-bundle/src/detect/runtimes/azure.ts
|
|
184496
184811
|
var DETECTOR8 = "runtime.azure";
|
|
184497
184812
|
var BINDINGS = {
|
|
184498
|
-
|
|
184499
|
-
|
|
184500
|
-
|
|
184501
|
-
|
|
184502
|
-
blobTrigger: { trigger: "event", dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184503
|
-
blob: { dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184813
|
+
http: { trigger: "http" },
|
|
184814
|
+
timer: { trigger: "cron" },
|
|
184815
|
+
queue: { trigger: "queue", dep: { capability: "queue", provider: "azure.queue", cls: "emulated" } },
|
|
184816
|
+
blob: { trigger: "event", dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184504
184817
|
table: { dep: { capability: "document", provider: "azure.table", cls: "emulated" } },
|
|
184505
|
-
|
|
184506
|
-
|
|
184507
|
-
|
|
184508
|
-
|
|
184509
|
-
|
|
184510
|
-
|
|
184511
|
-
|
|
184512
|
-
|
|
184513
|
-
|
|
184514
|
-
|
|
184515
|
-
}
|
|
184516
|
-
var PY_DECORATORS = {
|
|
184517
|
-
route: "httpTrigger",
|
|
184518
|
-
timer_trigger: "timerTrigger",
|
|
184519
|
-
schedule: "timerTrigger",
|
|
184520
|
-
queue_trigger: "queueTrigger",
|
|
184521
|
-
queue_output: "queue",
|
|
184522
|
-
blob_trigger: "blobTrigger",
|
|
184523
|
-
blob_input: "blob",
|
|
184524
|
-
blob_output: "blob",
|
|
184525
|
-
cosmos_db_trigger: "cosmosDBTrigger",
|
|
184526
|
-
cosmos_db_input: "cosmosDB",
|
|
184527
|
-
cosmos_db_output: "cosmosDB",
|
|
184528
|
-
service_bus_queue_trigger: "serviceBusTrigger",
|
|
184529
|
-
service_bus_topic_trigger: "serviceBusTrigger",
|
|
184530
|
-
service_bus_queue_output: "serviceBus",
|
|
184531
|
-
service_bus_topic_output: "serviceBus",
|
|
184532
|
-
event_hub_message_trigger: "eventHubTrigger",
|
|
184533
|
-
event_hub_output: "eventHub",
|
|
184534
|
-
event_grid_trigger: "eventGridTrigger",
|
|
184535
|
-
event_grid_output: "eventGrid",
|
|
184536
|
-
table_input: "table",
|
|
184537
|
-
table_output: "table",
|
|
184538
|
-
sql_input: "sql",
|
|
184539
|
-
sql_output: "sql",
|
|
184540
|
-
sql_trigger: "sqlTrigger"
|
|
184818
|
+
cosmosdb: { trigger: "event", dep: { capability: "document", provider: "azure.cosmos", cls: "emulated" } },
|
|
184819
|
+
servicebus: { trigger: "queue", dep: { capability: "queue", provider: "azure.servicebus", cls: "emulated" } },
|
|
184820
|
+
eventhub: { trigger: "queue", dep: { capability: "queue", provider: "azure.eventhubs", cls: "emulated" } },
|
|
184821
|
+
eventgrid: { trigger: "event", dep: { capability: "queue", provider: "azure.eventgrid", cls: "saas" } },
|
|
184822
|
+
sql: { trigger: "event", dep: { capability: "database", provider: "mssql", cls: "engine" } },
|
|
184823
|
+
durable_client: {},
|
|
184824
|
+
durable_orchestration: { trigger: "event" },
|
|
184825
|
+
durable_activity: { trigger: "event" },
|
|
184826
|
+
durable_entity: { trigger: "event" },
|
|
184827
|
+
embeddings_store: { dep: { capability: "vector_store", provider: "azure.search", cls: "saas" } },
|
|
184828
|
+
semantic_search: { dep: { capability: "vector_store", provider: "azure.search", cls: "saas" } }
|
|
184541
184829
|
};
|
|
184542
|
-
var
|
|
184543
|
-
|
|
184544
|
-
|
|
184545
|
-
|
|
184546
|
-
|
|
184547
|
-
|
|
184548
|
-
|
|
184549
|
-
|
|
184550
|
-
|
|
184551
|
-
|
|
184552
|
-
|
|
184553
|
-
|
|
184554
|
-
|
|
184555
|
-
|
|
184556
|
-
|
|
184557
|
-
|
|
184830
|
+
var TYPE_ALIASES = {
|
|
184831
|
+
httptrigger: "http",
|
|
184832
|
+
route: "http",
|
|
184833
|
+
get: "http",
|
|
184834
|
+
post: "http",
|
|
184835
|
+
put: "http",
|
|
184836
|
+
patch: "http",
|
|
184837
|
+
deleterequest: "http",
|
|
184838
|
+
timertrigger: "timer",
|
|
184839
|
+
schedule: "timer",
|
|
184840
|
+
queuetrigger: "queue",
|
|
184841
|
+
storagequeue: "queue",
|
|
184842
|
+
blobtrigger: "blob",
|
|
184843
|
+
storageblob: "blob",
|
|
184844
|
+
cosmosdbtrigger: "cosmosdb",
|
|
184845
|
+
cosmos_db: "cosmosdb",
|
|
184846
|
+
servicebustrigger: "servicebus",
|
|
184847
|
+
service_bus_queue: "servicebus",
|
|
184848
|
+
service_bus_topic: "servicebus",
|
|
184849
|
+
servicebusqueue: "servicebus",
|
|
184850
|
+
servicebustopic: "servicebus",
|
|
184851
|
+
eventhubtrigger: "eventhub",
|
|
184852
|
+
event_hub_message: "eventhub",
|
|
184853
|
+
event_hub: "eventhub",
|
|
184854
|
+
eventgridtrigger: "eventgrid",
|
|
184855
|
+
event_grid: "eventgrid",
|
|
184856
|
+
sqltrigger: "sql",
|
|
184857
|
+
durableclient: "durable_client",
|
|
184858
|
+
orchestrationtrigger: "durable_orchestration",
|
|
184859
|
+
orchestration: "durable_orchestration",
|
|
184860
|
+
activitytrigger: "durable_activity",
|
|
184861
|
+
activity: "durable_activity",
|
|
184862
|
+
entitytrigger: "durable_entity",
|
|
184863
|
+
entity: "durable_entity"
|
|
184558
184864
|
};
|
|
184865
|
+
function azureBindingType(raw) {
|
|
184866
|
+
const key = raw.toLowerCase();
|
|
184867
|
+
const t = TYPE_ALIASES[key] ?? key;
|
|
184868
|
+
return t in BINDINGS ? t : undefined;
|
|
184869
|
+
}
|
|
184559
184870
|
var NODE_IO = {
|
|
184560
184871
|
storageBlob: "blob",
|
|
184561
184872
|
storageQueue: "queue",
|
|
184562
184873
|
table: "table",
|
|
184563
|
-
cosmosDB: "
|
|
184564
|
-
serviceBusQueue: "
|
|
184565
|
-
serviceBusTopic: "
|
|
184566
|
-
eventHub: "
|
|
184567
|
-
eventGrid: "
|
|
184874
|
+
cosmosDB: "cosmosdb",
|
|
184875
|
+
serviceBusQueue: "servicebus",
|
|
184876
|
+
serviceBusTopic: "servicebus",
|
|
184877
|
+
eventHub: "eventhub",
|
|
184878
|
+
eventGrid: "eventgrid",
|
|
184568
184879
|
sql: "sql"
|
|
184569
184880
|
};
|
|
184570
184881
|
function detectAzure(input) {
|
|
@@ -184583,12 +184894,12 @@ function detectAzure(input) {
|
|
|
184583
184894
|
const seenDeps = new Set;
|
|
184584
184895
|
const addDep = (type, name, rawConnection, evidence) => {
|
|
184585
184896
|
const connection = rawConnection && /^[A-Za-z_][A-Za-z0-9_]*$/.test(rawConnection) ? rawConnection : undefined;
|
|
184897
|
+
if (!runtime.bindings.some((b) => b.name === name && b.type === type))
|
|
184898
|
+
runtime.bindings.push(connection ? { name, type, resource: connection } : { name, type });
|
|
184586
184899
|
const spec = BINDINGS[type]?.dep;
|
|
184587
184900
|
if (!spec)
|
|
184588
184901
|
return;
|
|
184589
184902
|
const key = `${spec.provider}:${connection ?? ""}`;
|
|
184590
|
-
if (!runtime.bindings.some((b) => b.name === name && b.type === type))
|
|
184591
|
-
runtime.bindings.push({ name, type, resource: connection });
|
|
184592
184903
|
if (seenDeps.has(key))
|
|
184593
184904
|
return;
|
|
184594
184905
|
seenDeps.add(key);
|
|
@@ -184611,7 +184922,7 @@ function detectAzure(input) {
|
|
|
184611
184922
|
continue;
|
|
184612
184923
|
if (basename2(file.path) === "function.json") {
|
|
184613
184924
|
handleFunctionJson(file, runtime, addDep);
|
|
184614
|
-
} else if (file.path.endsWith(".py") && /azure\.functions|func\.FunctionApp|func\.Blueprint/.test(file.text)) {
|
|
184925
|
+
} else if (file.path.endsWith(".py") && /azure\.functions|azure\.durable_functions|func\.FunctionApp|func\.Blueprint|\bDFApp\(/.test(file.text)) {
|
|
184615
184926
|
handlePython(file, runtime, addDep);
|
|
184616
184927
|
} else if (/\.[cm]?[jt]s$/.test(file.path) && /@azure\/functions/.test(file.text)) {
|
|
184617
184928
|
handleNode(file, runtime, addDep);
|
|
@@ -184632,17 +184943,22 @@ function handleFunctionJson(file, runtime, addDep) {
|
|
|
184632
184943
|
for (const b of asArray(json.bindings)) {
|
|
184633
184944
|
if (!isRecord2(b))
|
|
184634
184945
|
continue;
|
|
184635
|
-
const
|
|
184636
|
-
const
|
|
184637
|
-
if (
|
|
184638
|
-
|
|
184946
|
+
const raw = asString(b.type) ?? "";
|
|
184947
|
+
const type = azureBindingType(raw);
|
|
184948
|
+
if (!type)
|
|
184949
|
+
continue;
|
|
184950
|
+
if (/trigger$/i.test(raw) && BINDINGS[type].trigger)
|
|
184951
|
+
trigger = BINDINGS[type].trigger;
|
|
184639
184952
|
const name = asString(b.name) ?? type;
|
|
184640
|
-
addDep(type, name, asString(b.connection) ?? (type
|
|
184953
|
+
addDep(type, name, asString(b.connection) ?? defaultConnection(type), evidenceAt(file.path, file.text, findLine(file.text, new RegExp(`"${escapeRe(raw)}"`)), DETECTOR8));
|
|
184641
184954
|
}
|
|
184642
184955
|
const script = asString(json.scriptFile);
|
|
184643
184956
|
runtime.entrypoints.push({ name: fnName, handler: script ? joinPath(dirname2(file.path), script) : dirname2(file.path), trigger });
|
|
184644
184957
|
runtime.language ??= script ? languageFromPath(script) : undefined;
|
|
184645
184958
|
}
|
|
184959
|
+
function defaultConnection(type) {
|
|
184960
|
+
return type === "blob" || type === "queue" || type === "table" ? "AzureWebJobsStorage" : undefined;
|
|
184961
|
+
}
|
|
184646
184962
|
function parenDepth(text) {
|
|
184647
184963
|
let depth = 0;
|
|
184648
184964
|
for (const ch of text.replace(/'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"|#[^\n]*/g, "")) {
|
|
@@ -184665,24 +184981,34 @@ function handlePython(file, runtime, addDep) {
|
|
|
184665
184981
|
while (parenDepth(lines.slice(start, i + 1).join(`
|
|
184666
184982
|
`)) > 0 && i + 1 < lines.length)
|
|
184667
184983
|
i++;
|
|
184668
|
-
|
|
184984
|
+
const args = [deco[2], ...lines.slice(start + 1, i + 1)].join(`
|
|
184669
184985
|
`);
|
|
184670
184986
|
const kind = deco[1];
|
|
184671
184987
|
if (kind === "function_name")
|
|
184672
|
-
pending.name = /name\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184673
|
-
const
|
|
184988
|
+
pending.name = /name\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184989
|
+
const stem = kind === "route" || kind === "schedule" ? kind : /^(\w+?)_(?:trigger|input|output)$/.exec(kind)?.[1];
|
|
184990
|
+
const type = stem ? azureBindingType(stem) : undefined;
|
|
184674
184991
|
if (type) {
|
|
184675
|
-
|
|
184992
|
+
const isTrigger = kind === "route" || kind === "schedule" || kind.endsWith("_trigger");
|
|
184993
|
+
if (isTrigger && BINDINGS[type].trigger)
|
|
184676
184994
|
pending.trigger = BINDINGS[type].trigger;
|
|
184677
184995
|
if (kind === "route")
|
|
184678
|
-
pending.name ??= /route\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184679
|
-
const connection = /connection\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184680
|
-
|
|
184996
|
+
pending.name ??= /route\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184997
|
+
const connection = /connection\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1] ?? defaultConnection(type);
|
|
184998
|
+
const argName = /\b(?:arg_name|client_name|context_name|input_name|name)\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184999
|
+
const evidence = { path: file.path, line: start + 1, excerpt: excerpt(line), detector: DETECTOR8 };
|
|
185000
|
+
if (type === "http" && !argName)
|
|
185001
|
+
pending.httpBinding = { line: start + 1, connection };
|
|
185002
|
+
else
|
|
185003
|
+
addDep(type, argName ?? kind, connection, evidence);
|
|
184681
185004
|
}
|
|
184682
185005
|
continue;
|
|
184683
185006
|
}
|
|
184684
|
-
const def = /^\s*(?:async\s+)?def\s+(\w+)
|
|
185007
|
+
const def = /^\s*(?:async\s+)?def\s+(\w+)\s*\(\s*(\w+)?/.exec(line);
|
|
184685
185008
|
if (def && pending) {
|
|
185009
|
+
if (pending.httpBinding) {
|
|
185010
|
+
addDep("http", def[2] ?? "req", undefined, { path: file.path, line: pending.httpBinding.line, excerpt: excerpt(lines[pending.httpBinding.line - 1]), detector: DETECTOR8 });
|
|
185011
|
+
}
|
|
184686
185012
|
if (pending.trigger) {
|
|
184687
185013
|
runtime.entrypoints.push({ name: pending.name ?? def[1], handler: `${file.path}#${def[1]}`, trigger: pending.trigger });
|
|
184688
185014
|
runtime.language ??= "python";
|
|
@@ -184696,10 +185022,10 @@ function handlePython(file, runtime, addDep) {
|
|
|
184696
185022
|
function handleNode(file, runtime, addDep) {
|
|
184697
185023
|
const at = lineIndex(file.text);
|
|
184698
185024
|
const lines = splitLines(file.text);
|
|
184699
|
-
const reg = /\
|
|
185025
|
+
const reg = /\b(?:app|df\.app)\.(\w+)\(\s*['"]([^'"]+)['"]/g;
|
|
184700
185026
|
let m;
|
|
184701
185027
|
while ((m = reg.exec(file.text)) !== null) {
|
|
184702
|
-
const type =
|
|
185028
|
+
const type = azureBindingType(m[1]);
|
|
184703
185029
|
if (!type)
|
|
184704
185030
|
continue;
|
|
184705
185031
|
const line = at(m.index);
|
|
@@ -184707,7 +185033,7 @@ function handleNode(file, runtime, addDep) {
|
|
|
184707
185033
|
runtime.language ??= languageFromPath(file.path);
|
|
184708
185034
|
const window2 = callArgs(file.text, m.index);
|
|
184709
185035
|
const connection = /connection\s*:\s*['"]([^'"]+)['"]/.exec(window2)?.[1];
|
|
184710
|
-
addDep(type, m[2], connection ?? (type
|
|
185036
|
+
addDep(type, m[2], connection ?? defaultConnection(type), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
|
|
184711
185037
|
}
|
|
184712
185038
|
const io = /\b(?:input|output)\.(\w+)\(\s*\{/g;
|
|
184713
185039
|
while ((m = io.exec(file.text)) !== null) {
|
|
@@ -184717,7 +185043,8 @@ function handleNode(file, runtime, addDep) {
|
|
|
184717
185043
|
const line = at(m.index);
|
|
184718
185044
|
const window2 = callArgs(file.text, m.index);
|
|
184719
185045
|
const connection = /connection\s*:\s*['"]([^'"]+)['"]/.exec(window2)?.[1];
|
|
184720
|
-
|
|
185046
|
+
const name = /(?:const|let|var)\s+(\w+)\s*=\s*$/.exec(file.text.slice(Math.max(0, m.index - 80), m.index))?.[1] ?? m[1];
|
|
185047
|
+
addDep(type, name, connection ?? defaultConnection(type), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
|
|
184721
185048
|
}
|
|
184722
185049
|
}
|
|
184723
185050
|
|
|
@@ -184771,7 +185098,10 @@ function detectProcesses(input, roots) {
|
|
|
184771
185098
|
continue;
|
|
184772
185099
|
if (json.workspaces && !start && !bin)
|
|
184773
185100
|
continue;
|
|
184774
|
-
const
|
|
185101
|
+
const runsOnBun = /^\s*bun\s+(?:run\s+|--\S+\s+)*[\w./-]+\.[cm]?[jt]sx?\b/.test(start ?? asString(scripts.dev) ?? "");
|
|
185102
|
+
const rt = upsert(map, runsOnBun ? "other_process" : "node_process", pkg.root, pkg.path);
|
|
185103
|
+
if (runsOnBun)
|
|
185104
|
+
rt.language = "typescript";
|
|
184775
185105
|
const pkgFile = ctx.byPath.get(pkg.path);
|
|
184776
185106
|
rt.evidence.push(evidenceAt(pkg.path, pkgFile?.text ?? "", findLine(pkgFile?.text ?? "", /"(start|bin|main)"/), DETECTOR9));
|
|
184777
185107
|
const trigger = server2 ? "http" : bin ? "cli" : "unknown";
|
|
@@ -184956,6 +185286,10 @@ function pythonFile(file, map, roots) {
|
|
|
184956
185286
|
hits.push({ entry: { name: `mcp ${mod}:${m[1]}`, handler: `${file.path}:${m[1]}`, command: ["python", fromRoot(file.path, root)], trigger: /sse|streamable_http/.test(text) ? "http" : "cli" }, offset: m.index, detector: `${DETECTOR9}.mcp` });
|
|
184957
185287
|
}
|
|
184958
185288
|
}
|
|
185289
|
+
const agentcore = /\bBedrockAgentCoreApp\s*\(/.exec(text);
|
|
185290
|
+
if (agentcore && /bedrock_agentcore/.test(text)) {
|
|
185291
|
+
hits.push({ entry: { name: `agentcore ${file.path}`, handler: file.path, command: ["python", rel], trigger: "http" }, offset: agentcore.index, detector: `${DETECTOR9}.agentcore` });
|
|
185292
|
+
}
|
|
184959
185293
|
const main = /^if\s+__name__\s*==\s*['"]__main__['"]\s*:/m.exec(text);
|
|
184960
185294
|
if (main && hits.length === 0) {
|
|
184961
185295
|
const serves = /uvicorn\.run|\.run\(\s*(?:host|port|debug)|app\.run\(|web\.run_app|serve_forever/.test(text);
|
|
@@ -184972,10 +185306,24 @@ function pythonFile(file, map, roots) {
|
|
|
184972
185306
|
}
|
|
184973
185307
|
}
|
|
184974
185308
|
}
|
|
185309
|
+
var SUPABASE_EDGE = /^(.*?supabase\/functions)\/([^_./][^/]*)\/index\.[cm]?[jt]s$/;
|
|
184975
185310
|
function nodeFile(file, map, roots) {
|
|
184976
185311
|
const text = file.text;
|
|
185312
|
+
const edge = SUPABASE_EDGE.exec(file.path);
|
|
185313
|
+
if (edge) {
|
|
185314
|
+
const rt2 = upsert(map, "other_process", edge[1]);
|
|
185315
|
+
rt2.language = "typescript";
|
|
185316
|
+
rt2.frameworks = [...new Set([...rt2.frameworks ?? [], "supabase-edge-functions"])].sort();
|
|
185317
|
+
addEntry(rt2, { name: `edge ${edge[2]}`, handler: file.path, command: ["supabase", "functions", "serve", edge[2]], trigger: "http" });
|
|
185318
|
+
if (rt2.evidence.length < 5)
|
|
185319
|
+
rt2.evidence.push(evidenceAt(file.path, text, 1, `${DETECTOR9}.supabase_edge`));
|
|
185320
|
+
return;
|
|
185321
|
+
}
|
|
184977
185322
|
const hits = [];
|
|
184978
185323
|
let m;
|
|
185324
|
+
if (/bedrock-agentcore/.test(text) && (m = /\bBedrockAgentCoreApp\b|\.entrypoint\s*\(/.exec(text))) {
|
|
185325
|
+
hits.push({ entry: { name: `agentcore ${file.path}`, handler: file.path, trigger: "http" }, offset: m.index, detector: `${DETECTOR9}.agentcore` });
|
|
185326
|
+
}
|
|
184979
185327
|
if (/@modelcontextprotocol\/sdk/.test(text) && (m = /new\s+(McpServer|Server)\s*\(/.exec(text))) {
|
|
184980
185328
|
const http = /StreamableHTTPServerTransport|SSEServerTransport/.test(text);
|
|
184981
185329
|
hits.push({ entry: { name: `mcp ${file.path}`, handler: file.path, trigger: http ? "http" : "cli" }, offset: m.index, detector: `${DETECTOR9}.mcp` });
|
|
@@ -184985,10 +185333,15 @@ function nodeFile(file, map, roots) {
|
|
|
184985
185333
|
}
|
|
184986
185334
|
if (m = /\b(?:app|server|fastify)\.listen\s*\(|\bserve\(\s*\{\s*fetch|\bBun\.serve\s*\(|\bDeno\.serve\s*\(|\bcreateServer\s*\([^)]*\)\s*\.listen/.exec(text)) {
|
|
184987
185335
|
hits.push({ entry: { name: `listen ${file.path}`, handler: file.path, trigger: "http" }, offset: m.index, detector: `${DETECTOR9}.listen` });
|
|
185336
|
+
const bun = /\bBun\.serve\s*\(/.exec(text);
|
|
185337
|
+
if (bun && /\bwebsocket\s*[:,}]/.test(callArgs(text, bun.index + bun[0].length - 1, 50000))) {
|
|
185338
|
+
hits.push({ entry: { name: `websocket ${file.path}`, handler: file.path, trigger: "websocket" }, offset: bun.index, detector: `${DETECTOR9}.listen` });
|
|
185339
|
+
}
|
|
184988
185340
|
}
|
|
184989
185341
|
if (hits.length === 0)
|
|
184990
185342
|
return;
|
|
184991
|
-
const
|
|
185343
|
+
const kind = /\bBun\.serve\s*\(/.test(text) ? "other_process" : "node_process";
|
|
185344
|
+
const rt = upsert(map, kind, nearestRoot(file.path, roots));
|
|
184992
185345
|
rt.language = languageFromPath(file.path) ?? rt.language;
|
|
184993
185346
|
const at = lineIndex(text);
|
|
184994
185347
|
const lines = splitLines(text);
|
|
@@ -185068,7 +185421,9 @@ function detectVercel(input) {
|
|
|
185068
185421
|
if (info.next && /^(?:src\/)?app\/(?:.*\/)?route\.[cm]?[jt]sx?$/.test(r)) {
|
|
185069
185422
|
const methods = exportedMethods(f.text);
|
|
185070
185423
|
const path = routePath(r.replace(/^src\//, "").replace(/^app\//, ""), "app");
|
|
185071
|
-
entries.push({ name: `${methods.join(",")
|
|
185424
|
+
entries.push({ name: `${methods.length > 0 ? methods.join(",") : "ANY"} ${path}`, handler: f.path, trigger: "http" });
|
|
185425
|
+
} else if (info.next && /^(?:src\/)?middleware\.[cm]?[jt]s$/.test(r)) {
|
|
185426
|
+
entries.push({ name: "middleware", handler: `${f.path}#middleware`, trigger: "http" });
|
|
185072
185427
|
} else if (info.next && /^(?:src\/)?pages\/api\/.+\.[cm]?[jt]sx?$/.test(r)) {
|
|
185073
185428
|
entries.push({ name: `ANY ${routePath(r.replace(/^src\//, "").replace(/^pages\//, ""), "pages")}`, handler: f.path, trigger: "http" });
|
|
185074
185429
|
} else if (!info.next && /^api\/.+\.([cm]?[jt]s|py|go)$/.test(r) && !/\/_/.test(r)) {
|
|
@@ -185097,11 +185452,277 @@ function detectVercel(input) {
|
|
|
185097
185452
|
function rel(path, root) {
|
|
185098
185453
|
return root === "." ? path : path.slice(root.length + 1);
|
|
185099
185454
|
}
|
|
185455
|
+
|
|
185456
|
+
// ../../packages/harness-bundle/src/detect/scope.ts
|
|
185457
|
+
var DEPLOY_CONFIG = /^(?:wrangler\.(?:toml|json|jsonc)|template\.ya?ml|serverless\.ya?ml|vercel\.json|firebase\.json|cloudbuild\.ya?ml|service\.ya?ml|cdk\.json|langgraph\.json|Procfile|render\.ya?ml|fly\.toml|railway\.(?:json|toml)|netlify\.toml|(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml)$/;
|
|
185458
|
+
var PROJECT_MANIFEST2 = /^(?:package\.json|pyproject\.toml|requirements\.txt|go\.mod|Cargo\.toml|Gemfile)$/;
|
|
185459
|
+
var ANCESTOR_ORM = [
|
|
185460
|
+
{ uses: /@prisma\/client|\bPrismaClient\b/, files: /^(?:prisma\/.+|[\w.-]+\.prisma|(?:\.config\/)?prisma(?:\.config)?\.[cm]?[jt]s)$/ },
|
|
185461
|
+
{ uses: /\bdrizzle-orm\b/, files: /^drizzle\.config\.[cm]?[jt]s$/ },
|
|
185462
|
+
{ uses: /\b(?:sqlalchemy|sqlmodel|alembic)\b/, files: /^(?:alembic\.ini|alembic\/.+)$/ }
|
|
185463
|
+
];
|
|
185464
|
+
var CODE_FILE = /\.(?:[cm]?[jt]sx?|py)$/;
|
|
185465
|
+
function pyDependencyNames(doc) {
|
|
185466
|
+
const out = [];
|
|
185467
|
+
const project = isRecord2(doc.project) ? doc.project : {};
|
|
185468
|
+
for (const req of asArray(project.dependencies)) {
|
|
185469
|
+
const m = typeof req === "string" ? /^\s*([A-Za-z0-9][\w.-]*)/.exec(req) : null;
|
|
185470
|
+
if (m)
|
|
185471
|
+
out.push(m[1]);
|
|
185472
|
+
}
|
|
185473
|
+
const poetry = isRecord2(doc.tool) && isRecord2(doc.tool.poetry) ? doc.tool.poetry : {};
|
|
185474
|
+
if (isRecord2(poetry.dependencies))
|
|
185475
|
+
out.push(...Object.keys(poetry.dependencies));
|
|
185476
|
+
return out.map(pyName);
|
|
185477
|
+
}
|
|
185478
|
+
function pyName(name) {
|
|
185479
|
+
return name.toLowerCase().replace(/[_.]/g, "-");
|
|
185480
|
+
}
|
|
185481
|
+
function npmDeps(json) {
|
|
185482
|
+
const out = [];
|
|
185483
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
185484
|
+
const deps = json[field];
|
|
185485
|
+
if (isRecord2(deps))
|
|
185486
|
+
out.push(...Object.keys(deps));
|
|
185487
|
+
}
|
|
185488
|
+
return out;
|
|
185489
|
+
}
|
|
185490
|
+
function tomlDoc(file) {
|
|
185491
|
+
if (!file)
|
|
185492
|
+
return;
|
|
185493
|
+
try {
|
|
185494
|
+
return parse(file.text);
|
|
185495
|
+
} catch {
|
|
185496
|
+
return;
|
|
185497
|
+
}
|
|
185498
|
+
}
|
|
185499
|
+
function directDeps(dir, byPath, npmNames, pyNames, goModules, workspaces) {
|
|
185500
|
+
const out = [];
|
|
185501
|
+
const pkg = byPath.get(joinPath(dir, "package.json"));
|
|
185502
|
+
if (pkg) {
|
|
185503
|
+
const json = parseJsonLoose(pkg.text);
|
|
185504
|
+
if (isRecord2(json)) {
|
|
185505
|
+
for (const name of npmDeps(json)) {
|
|
185506
|
+
const target = npmNames.get(name);
|
|
185507
|
+
if (target !== undefined)
|
|
185508
|
+
out.push(target);
|
|
185509
|
+
}
|
|
185510
|
+
}
|
|
185511
|
+
}
|
|
185512
|
+
const pyproject = tomlDoc(byPath.get(joinPath(dir, "pyproject.toml")));
|
|
185513
|
+
if (pyproject) {
|
|
185514
|
+
const tool = isRecord2(pyproject.tool) ? pyproject.tool : {};
|
|
185515
|
+
const uv = isRecord2(tool.uv) ? tool.uv : {};
|
|
185516
|
+
const sources = isRecord2(uv.sources) ? uv.sources : {};
|
|
185517
|
+
for (const [name, src] of Object.entries(sources)) {
|
|
185518
|
+
if (!isRecord2(src))
|
|
185519
|
+
continue;
|
|
185520
|
+
if (src.workspace === true) {
|
|
185521
|
+
const target = pyNames.get(pyName(name));
|
|
185522
|
+
if (target !== undefined)
|
|
185523
|
+
out.push(target);
|
|
185524
|
+
}
|
|
185525
|
+
const path = asString(src.path);
|
|
185526
|
+
if (path)
|
|
185527
|
+
out.push(joinPath(dir, path));
|
|
185528
|
+
}
|
|
185529
|
+
for (const ws of workspaces) {
|
|
185530
|
+
if (!isUnder(dir, ws.root))
|
|
185531
|
+
continue;
|
|
185532
|
+
for (const name of pyDependencyNames(pyproject)) {
|
|
185533
|
+
const target = ws.sources.get(name);
|
|
185534
|
+
if (target !== undefined)
|
|
185535
|
+
out.push(target);
|
|
185536
|
+
}
|
|
185537
|
+
}
|
|
185538
|
+
const poetry = isRecord2(tool.poetry) ? tool.poetry : {};
|
|
185539
|
+
const groups = [poetry.dependencies, ...isRecord2(poetry.group) ? Object.values(poetry.group).map((g) => isRecord2(g) ? g.dependencies : undefined) : []];
|
|
185540
|
+
for (const deps of groups) {
|
|
185541
|
+
if (!isRecord2(deps))
|
|
185542
|
+
continue;
|
|
185543
|
+
for (const spec of Object.values(deps)) {
|
|
185544
|
+
const path = isRecord2(spec) ? asString(spec.path) : undefined;
|
|
185545
|
+
if (path)
|
|
185546
|
+
out.push(joinPath(dir, path));
|
|
185547
|
+
}
|
|
185548
|
+
}
|
|
185549
|
+
}
|
|
185550
|
+
for (const name of ["requirements.txt", "requirements.in"]) {
|
|
185551
|
+
const req = byPath.get(joinPath(dir, name));
|
|
185552
|
+
if (!req)
|
|
185553
|
+
continue;
|
|
185554
|
+
for (const line of splitLines(req.text)) {
|
|
185555
|
+
const m = /^\s*(?:-e\s+)?(\.{1,2}\/[^\s#;]+)/.exec(line);
|
|
185556
|
+
if (m)
|
|
185557
|
+
out.push(joinPath(dir, m[1]));
|
|
185558
|
+
}
|
|
185559
|
+
}
|
|
185560
|
+
const gomod = byPath.get(joinPath(dir, "go.mod"));
|
|
185561
|
+
if (gomod) {
|
|
185562
|
+
for (const m of gomod.text.matchAll(/=>\s*(\.{1,2}\/\S+)/g))
|
|
185563
|
+
out.push(joinPath(dir, m[1]));
|
|
185564
|
+
for (const m of gomod.text.matchAll(/^\s*(?:require\s+)?([\w.-]+\.[\w./-]+)\s+v[\w.+-]+/gm)) {
|
|
185565
|
+
const target = goModules.get(m[1]);
|
|
185566
|
+
if (target !== undefined)
|
|
185567
|
+
out.push(target);
|
|
185568
|
+
}
|
|
185569
|
+
const own = /^module\s+(\S+)/m.exec(gomod.text)?.[1];
|
|
185570
|
+
const others = [...goModules].filter(([mod]) => mod !== own);
|
|
185571
|
+
if (others.length > 0) {
|
|
185572
|
+
for (const f of byPath.values()) {
|
|
185573
|
+
if (!f.path.endsWith(".go") || !isUnder(f.path, dir))
|
|
185574
|
+
continue;
|
|
185575
|
+
for (const [mod, target] of others)
|
|
185576
|
+
if (f.text.includes(`"${mod}"`) || f.text.includes(`"${mod}/`))
|
|
185577
|
+
out.push(target);
|
|
185578
|
+
}
|
|
185579
|
+
}
|
|
185580
|
+
}
|
|
185581
|
+
return out;
|
|
185582
|
+
}
|
|
185583
|
+
function composeBuildsUnder(svc, base, root) {
|
|
185584
|
+
if (!isRecord2(svc) || svc.build === undefined)
|
|
185585
|
+
return false;
|
|
185586
|
+
const build = svc.build;
|
|
185587
|
+
const context = joinPath(base, typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".");
|
|
185588
|
+
if (isUnder(context, root))
|
|
185589
|
+
return true;
|
|
185590
|
+
const dockerfile = isRecord2(build) ? asString(build.dockerfile) : undefined;
|
|
185591
|
+
return dockerfile !== undefined && isUnder(joinPath(context, dockerfile), root);
|
|
185592
|
+
}
|
|
185593
|
+
function computeScope(files, root) {
|
|
185594
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
185595
|
+
if (root === ".")
|
|
185596
|
+
return { root, closure: [], configs: [] };
|
|
185597
|
+
const npmNames = new Map;
|
|
185598
|
+
const pyNames = new Map;
|
|
185599
|
+
const goModules = new Map;
|
|
185600
|
+
for (const f of files) {
|
|
185601
|
+
const name = basename2(f.path);
|
|
185602
|
+
if (name === "package.json") {
|
|
185603
|
+
const json = parseJsonLoose(f.text);
|
|
185604
|
+
if (isRecord2(json) && typeof json.name === "string")
|
|
185605
|
+
npmNames.set(json.name, dirname2(f.path));
|
|
185606
|
+
} else if (name === "pyproject.toml") {
|
|
185607
|
+
const doc = tomlDoc(f);
|
|
185608
|
+
const project = doc && isRecord2(doc.project) ? doc.project : undefined;
|
|
185609
|
+
const poetry = doc && isRecord2(doc.tool) && isRecord2(doc.tool.poetry) ? doc.tool.poetry : undefined;
|
|
185610
|
+
const projectName = asString(project?.name) ?? asString(poetry?.name);
|
|
185611
|
+
if (projectName)
|
|
185612
|
+
pyNames.set(pyName(projectName), dirname2(f.path));
|
|
185613
|
+
} else if (name === "go.mod") {
|
|
185614
|
+
const mod = /^module\s+(\S+)/m.exec(f.text)?.[1];
|
|
185615
|
+
if (mod)
|
|
185616
|
+
goModules.set(mod, dirname2(f.path));
|
|
185617
|
+
}
|
|
185618
|
+
}
|
|
185619
|
+
const workspaces = [];
|
|
185620
|
+
for (const f of files) {
|
|
185621
|
+
if (basename2(f.path) !== "pyproject.toml")
|
|
185622
|
+
continue;
|
|
185623
|
+
const doc = tomlDoc(f);
|
|
185624
|
+
const uv = doc && isRecord2(doc.tool) && isRecord2(doc.tool.uv) ? doc.tool.uv : undefined;
|
|
185625
|
+
if (!uv || !isRecord2(uv.workspace))
|
|
185626
|
+
continue;
|
|
185627
|
+
const base = dirname2(f.path);
|
|
185628
|
+
const members = asArray(uv.workspace.members).filter((m) => typeof m === "string").map((m) => globRegex(joinPath(base, m)));
|
|
185629
|
+
if (!members.some((re) => re.test(root) || [...pyNames.values()].some((d) => isUnder(root, d) && re.test(d))))
|
|
185630
|
+
continue;
|
|
185631
|
+
const sources = new Map;
|
|
185632
|
+
for (const [name, src] of Object.entries(isRecord2(uv.sources) ? uv.sources : {})) {
|
|
185633
|
+
if (!isRecord2(src))
|
|
185634
|
+
continue;
|
|
185635
|
+
const target = src.workspace === true ? pyNames.get(pyName(name)) : asString(src.path) ? joinPath(base, asString(src.path)) : undefined;
|
|
185636
|
+
if (target !== undefined)
|
|
185637
|
+
sources.set(pyName(name), target);
|
|
185638
|
+
}
|
|
185639
|
+
workspaces.push({ root: base, sources });
|
|
185640
|
+
}
|
|
185641
|
+
const MANIFEST = /^(?:package\.json|pyproject\.toml|requirements\.(?:txt|in)|go\.mod)$/;
|
|
185642
|
+
const nested = files.filter((f) => MANIFEST.test(basename2(f.path)) && isUnder(f.path, root) && !/(^|\/)node_modules\//.test(f.path)).map((f) => dirname2(f.path));
|
|
185643
|
+
const closure = new Set;
|
|
185644
|
+
const queue = [...new Set([root, ...nested])];
|
|
185645
|
+
while (queue.length > 0) {
|
|
185646
|
+
const dir = queue.shift();
|
|
185647
|
+
for (const dep of directDeps(dir, byPath, npmNames, pyNames, goModules, workspaces)) {
|
|
185648
|
+
if (dep === root || isUnder(dep, root) || closure.has(dep) || dep === ".")
|
|
185649
|
+
continue;
|
|
185650
|
+
closure.add(dep);
|
|
185651
|
+
queue.push(dep);
|
|
185652
|
+
}
|
|
185653
|
+
}
|
|
185654
|
+
const owned = (p2) => isUnder(p2, root) || [...closure].some((c) => isUnder(p2, c));
|
|
185655
|
+
const ancestors = [];
|
|
185656
|
+
for (let d = dirname2(root);; d = dirname2(d)) {
|
|
185657
|
+
ancestors.push(d);
|
|
185658
|
+
if (d === ".")
|
|
185659
|
+
break;
|
|
185660
|
+
}
|
|
185661
|
+
const harnessFiles = files.filter((f) => owned(f.path));
|
|
185662
|
+
const configs = new Set;
|
|
185663
|
+
if (!harnessFiles.some((f) => dirname2(f.path) === root && PROJECT_MANIFEST2.test(basename2(f.path)))) {
|
|
185664
|
+
const dir = ancestors.find((a) => files.some((f) => dirname2(f.path) === a && PROJECT_MANIFEST2.test(basename2(f.path))));
|
|
185665
|
+
for (const f of files)
|
|
185666
|
+
if (dir !== undefined && dirname2(f.path) === dir && PROJECT_MANIFEST2.test(basename2(f.path)))
|
|
185667
|
+
configs.add(f.path);
|
|
185668
|
+
}
|
|
185669
|
+
const users = [...harnessFiles, ...[...configs].map((p2) => byPath.get(p2))];
|
|
185670
|
+
const relToRoot = (p2) => p2.slice(root.length + 1);
|
|
185671
|
+
const orms = ANCESTOR_ORM.filter((o) => users.some((f) => (CODE_FILE.test(f.path) || PROJECT_MANIFEST2.test(basename2(f.path))) && o.uses.test(f.text)) && !files.some((f) => isUnder(f.path, root) && o.files.test(relToRoot(f.path))));
|
|
185672
|
+
const ormDirs = [];
|
|
185673
|
+
for (const f of files) {
|
|
185674
|
+
if (owned(f.path))
|
|
185675
|
+
continue;
|
|
185676
|
+
const name = basename2(f.path);
|
|
185677
|
+
const base = dirname2(f.path);
|
|
185678
|
+
if (ancestors.includes(base) && isEnvFile(f.path))
|
|
185679
|
+
configs.add(f.path);
|
|
185680
|
+
for (const a of ancestors) {
|
|
185681
|
+
if (!isUnder(f.path, a))
|
|
185682
|
+
continue;
|
|
185683
|
+
const rel3 = a === "." ? f.path : f.path.slice(a.length + 1);
|
|
185684
|
+
if (!orms.some((o) => o.files.test(rel3)))
|
|
185685
|
+
continue;
|
|
185686
|
+
configs.add(f.path);
|
|
185687
|
+
if (!/^(?:drizzle\.config\.|alembic\.ini$)/.test(name))
|
|
185688
|
+
continue;
|
|
185689
|
+
if (name.startsWith("drizzle.config.") && !/\bout\s*:/.test(f.text))
|
|
185690
|
+
ormDirs.push(joinPath(a, "drizzle"));
|
|
185691
|
+
for (const m of f.text.matchAll(/\b(?:out|schema|script_location)\s*[:=]\s*['"]?([\w./-]+)/g))
|
|
185692
|
+
ormDirs.push(joinPath(a, m[1].replace(/\/?\*.*$/, "")));
|
|
185693
|
+
}
|
|
185694
|
+
if (!DEPLOY_CONFIG.test(name) || !ancestors.includes(base))
|
|
185695
|
+
continue;
|
|
185696
|
+
const rel2 = base === "." ? root : root.slice(base.length + 1);
|
|
185697
|
+
if (/compose/.test(name)) {
|
|
185698
|
+
const doc = loadYaml(f.text);
|
|
185699
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? doc.services : {};
|
|
185700
|
+
if (Object.values(services).some((svc) => composeBuildsUnder(svc, base, root)))
|
|
185701
|
+
configs.add(f.path);
|
|
185702
|
+
} else if (new RegExp(`(?:^|[\\s"'=:/])\\.?/?${rel2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[/"'\\s]|$)`, "m").test(f.text)) {
|
|
185703
|
+
configs.add(f.path);
|
|
185704
|
+
}
|
|
185705
|
+
}
|
|
185706
|
+
for (const f of files)
|
|
185707
|
+
if (!owned(f.path) && ormDirs.some((d) => d !== "." && (f.path === d || isUnder(f.path, d))))
|
|
185708
|
+
configs.add(f.path);
|
|
185709
|
+
return { root, closure: [...closure].sort(), configs: [...configs].sort() };
|
|
185710
|
+
}
|
|
185711
|
+
var configSets = new WeakMap;
|
|
185712
|
+
function inScope(path, scope) {
|
|
185713
|
+
if (scope.root === ".")
|
|
185714
|
+
return true;
|
|
185715
|
+
let configs = configSets.get(scope);
|
|
185716
|
+
if (!configs)
|
|
185717
|
+
configSets.set(scope, configs = new Set(scope.configs));
|
|
185718
|
+
return isUnder(path, scope.root) || scope.closure.some((c) => isUnder(path, c)) || configs.has(path);
|
|
185719
|
+
}
|
|
185100
185720
|
// ../../packages/harness-bundle/src/detect/index.ts
|
|
185101
185721
|
var DETECTOR_NAME = "@moda/harness-bundle/detect";
|
|
185102
185722
|
var DETECTOR_VERSION = "0.1.0";
|
|
185103
185723
|
var SERVER_FRAMEWORKS = new Set(FRAMEWORKS.filter((f) => f.kind === "server").map((f) => f.name));
|
|
185104
185724
|
var MIGRATION_ENGINE_PROVIDERS = new Set(["postgres", "mysql", "sqlite", "libsql", "mssql", "clickhouse", "cloudflare.d1", "supabase", "mongodb"]);
|
|
185725
|
+
var CLOSURE_CAPABILITIES = new Set(["database", "document", "kv", "vector_store", "object_store", "queue"]);
|
|
185105
185726
|
var PLATFORM_KINDS = new Set(["cloudflare_workers", "aws_lambda", "gcp_cloud_functions", "gcp_cloud_run", "azure_functions", "vercel_functions"]);
|
|
185106
185727
|
var PROCESS_KINDS = new Set(["node_process", "python_process", "other_process"]);
|
|
185107
185728
|
function packageSignals(ctx, declared) {
|
|
@@ -185122,6 +185743,237 @@ function packageSignals(ctx, declared) {
|
|
|
185122
185743
|
});
|
|
185123
185744
|
}
|
|
185124
185745
|
}
|
|
185746
|
+
function normalizeRoot(raw) {
|
|
185747
|
+
if (!raw)
|
|
185748
|
+
return ".";
|
|
185749
|
+
const root = joinPath(raw);
|
|
185750
|
+
return root === "" ? "." : root;
|
|
185751
|
+
}
|
|
185752
|
+
var RUNTIME_CONFIG = /^(?:wrangler\.(?:toml|jsonc?)|template\.ya?ml|serverless\.ya?ml|host\.json|vercel\.json|next\.config\.\w+|Dockerfile[\w.-]*|(?:docker-)?compose[\w.-]*\.ya?ml|langgraph\.json|firebase\.json|cdk\.json)$/;
|
|
185753
|
+
function outsideContext(files, scope) {
|
|
185754
|
+
const configs = files.filter((f) => RUNTIME_CONFIG.test(basename2(f.path))).map((f) => f.path);
|
|
185755
|
+
const providers = new Set;
|
|
185756
|
+
for (const d of collectDeclaredPackages(files)) {
|
|
185757
|
+
const m = matchModule(d.eco === "pypi" ? "pypi" : d.eco, d.name);
|
|
185758
|
+
if (m && !d.dev)
|
|
185759
|
+
providers.add(m.signature.provider);
|
|
185760
|
+
}
|
|
185761
|
+
const list = [...providers].sort();
|
|
185762
|
+
return {
|
|
185763
|
+
kind: "outside_harness_root",
|
|
185764
|
+
message: `${files.length} file(s) outside harness root ${scope.root}${scope.closure.length > 0 ? ` and its workspace dependencies (${scope.closure.join(", ")})` : ""} were not analyzed` + (configs.length > 0 ? `; runtime configs there: ${configs.slice(0, 5).join(", ")}${configs.length > 5 ? `, +${configs.length - 5} more` : ""}` : "") + (list.length > 0 ? `; providers declared there: ${list.slice(0, 10).join(", ")}${list.length > 10 ? `, +${list.length - 10} more` : ""}` : ""),
|
|
185765
|
+
evidence: configs.slice(0, 3).map((path) => ({ path, detector: "scope" }))
|
|
185766
|
+
};
|
|
185767
|
+
}
|
|
185768
|
+
function scopeRuntimes(ctx, runtimes, scope) {
|
|
185769
|
+
if (scope.root === ".")
|
|
185770
|
+
return runtimes;
|
|
185771
|
+
const out = [];
|
|
185772
|
+
for (const rt of runtimes) {
|
|
185773
|
+
if (isUnder(rt.root, scope.root)) {
|
|
185774
|
+
out.push(rt);
|
|
185775
|
+
continue;
|
|
185776
|
+
}
|
|
185777
|
+
if (!isUnder(scope.root, rt.root))
|
|
185778
|
+
continue;
|
|
185779
|
+
const pointsIn = (ep) => [ep.handler ?? "", ...ep.command ?? []].some((p2) => isUnder(joinPath(rt.root, p2.split(/[#:]/)[0]), scope.root) || isUnder(p2.split(/[#:]/)[0], scope.root));
|
|
185780
|
+
const entrypoints = rt.entrypoints.filter(pointsIn);
|
|
185781
|
+
if (entrypoints.length === 0)
|
|
185782
|
+
continue;
|
|
185783
|
+
const kept = new Set(entrypoints.map((e) => e.name));
|
|
185784
|
+
let bindings = rt.bindings;
|
|
185785
|
+
for (const [template, refs] of ctx.functionRefs) {
|
|
185786
|
+
if (!rt.configFiles.includes(template))
|
|
185787
|
+
continue;
|
|
185788
|
+
const dropped = [...refs.keys()].filter((fn) => !kept.has(fn));
|
|
185789
|
+
const allowed = new Set([...refs.entries()].filter(([fn]) => kept.has(fn)).flatMap(([, ids]) => [...ids]));
|
|
185790
|
+
ctx.env = ctx.env.filter((e) => !dropped.some((fn) => e.source === `${template} ${fn}`));
|
|
185791
|
+
ctx.signals = ctx.signals.filter((s) => s.kind !== "binding" || s.evidence.path !== template || allowed.has(s.binding ?? ""));
|
|
185792
|
+
bindings = bindings.filter((b) => allowed.has(b.name));
|
|
185793
|
+
}
|
|
185794
|
+
out.push({ ...rt, entrypoints, bindings });
|
|
185795
|
+
}
|
|
185796
|
+
return out;
|
|
185797
|
+
}
|
|
185798
|
+
function scopeComposeImages(ctx, scope) {
|
|
185799
|
+
const optional = new Set;
|
|
185800
|
+
for (const f of ctx.files) {
|
|
185801
|
+
if (!/^(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml$/.test(basename2(f.path)))
|
|
185802
|
+
continue;
|
|
185803
|
+
const base = f.path.includes("/") ? f.path.slice(0, f.path.lastIndexOf("/")) : ".";
|
|
185804
|
+
if (base === scope.root)
|
|
185805
|
+
continue;
|
|
185806
|
+
const doc = loadYaml(f.text);
|
|
185807
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? Object.values(doc.services) : [];
|
|
185808
|
+
const builds = services.some((svc) => isRecord2(svc) && svc.build !== undefined);
|
|
185809
|
+
if (!builds)
|
|
185810
|
+
optional.add(f.path);
|
|
185811
|
+
}
|
|
185812
|
+
for (const s of ctx.signals)
|
|
185813
|
+
if (s.kind === "image" && optional.has(s.evidence.path))
|
|
185814
|
+
s.weak = true;
|
|
185815
|
+
if (scope.root === ".")
|
|
185816
|
+
return ctx.signals;
|
|
185817
|
+
const allowed = new Map;
|
|
185818
|
+
for (const path of scope.configs) {
|
|
185819
|
+
if (!/compose/.test(basename2(path)))
|
|
185820
|
+
continue;
|
|
185821
|
+
const doc = loadYaml(ctx.byPath.get(path)?.text ?? "");
|
|
185822
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? doc.services : {};
|
|
185823
|
+
const base = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : ".";
|
|
185824
|
+
const deps = new Set;
|
|
185825
|
+
let declared = false;
|
|
185826
|
+
for (const svc of Object.values(services)) {
|
|
185827
|
+
if (!isRecord2(svc) || !composeBuildsUnder(svc, base, scope.root))
|
|
185828
|
+
continue;
|
|
185829
|
+
const names = [...Array.isArray(svc.depends_on) ? svc.depends_on : isRecord2(svc.depends_on) ? Object.keys(svc.depends_on) : [], ...Array.isArray(svc.links) ? svc.links : []];
|
|
185830
|
+
for (const n of names)
|
|
185831
|
+
if (typeof n === "string") {
|
|
185832
|
+
declared = true;
|
|
185833
|
+
deps.add(n.split(":")[0]);
|
|
185834
|
+
}
|
|
185835
|
+
}
|
|
185836
|
+
if (declared)
|
|
185837
|
+
allowed.set(path, deps);
|
|
185838
|
+
}
|
|
185839
|
+
ctx.env = ctx.env.filter((e) => e.service === undefined || e.scopeRoot !== undefined || !e.evidence || !allowed.has(e.evidence.path) || allowed.get(e.evidence.path).has(e.service));
|
|
185840
|
+
return ctx.signals.filter((s) => s.kind !== "image" || !allowed.has(s.evidence.path) || allowed.get(s.evidence.path).has(s.resource ?? ""));
|
|
185841
|
+
}
|
|
185842
|
+
var SERVERLESS_KINDS = new Set(["cloudflare_workers", "aws_lambda", "gcp_cloud_functions", "azure_functions", "vercel_functions"]);
|
|
185843
|
+
function isAuxiliary(path, harnessRoot) {
|
|
185844
|
+
if (!isUnder(path, harnessRoot))
|
|
185845
|
+
return false;
|
|
185846
|
+
const rel2 = harnessRoot === "." ? path : path === harnessRoot ? "." : path.slice(harnessRoot.length + 1);
|
|
185847
|
+
return AUXILIARY_DIR.test(rel2);
|
|
185848
|
+
}
|
|
185849
|
+
var AUXILIARY_DIR = /(^|\/)(vendor|third_party|examples?|samples?|demos?|docs?|tests?|e2e|benchmarks?|fixtures|\.devops|\.github|cloud-deployments|hack|playground)(\/|$)/;
|
|
185850
|
+
function foldNestedRuntimes(ctx, runtimes, harnessRoot) {
|
|
185851
|
+
const rel2 = (root) => harnessRoot === "." ? root : root === harnessRoot ? "." : root.slice(harnessRoot.length + 1);
|
|
185852
|
+
const auxiliary = runtimes.filter((r) => isUnder(r.root, harnessRoot) && AUXILIARY_DIR.test(rel2(r.root)));
|
|
185853
|
+
if (auxiliary.length > 0) {
|
|
185854
|
+
ctx.gaps.push({
|
|
185855
|
+
kind: "auxiliary_runtime",
|
|
185856
|
+
message: `${auxiliary.length} runtime(s) under example, vendored, test, or deployment-variant directories were not treated as the harness: ${auxiliary.slice(0, 5).map((r) => `${r.kind}@${r.root}`).join(", ")}${auxiliary.length > 5 ? ", ..." : ""}`,
|
|
185857
|
+
evidence: auxiliary.slice(0, 3).map((r) => r.evidence[0]).filter((e) => e !== undefined)
|
|
185858
|
+
});
|
|
185859
|
+
}
|
|
185860
|
+
let out = runtimes.filter((r) => !auxiliary.includes(r));
|
|
185861
|
+
const merge = (into, from) => {
|
|
185862
|
+
into.configFiles = uniqSorted([...into.configFiles, ...from.configFiles]);
|
|
185863
|
+
into.frameworks = uniqSorted([...into.frameworks ?? [], ...from.frameworks ?? []]);
|
|
185864
|
+
for (const ep of from.entrypoints) {
|
|
185865
|
+
const moved = ep.command && from.root !== into.root ? { ...ep, command: rebaseCommand(ep.command, from.root, into.root, ctx) } : ep;
|
|
185866
|
+
if (into.entrypoints.some((e) => e.name === moved.name && e.handler === moved.handler && (e.command ?? []).join(" ") === (moved.command ?? []).join(" ")))
|
|
185867
|
+
continue;
|
|
185868
|
+
into.entrypoints.push(moved);
|
|
185869
|
+
}
|
|
185870
|
+
into.language ??= from.language;
|
|
185871
|
+
};
|
|
185872
|
+
for (const v of out.filter((r) => r.kind === "vercel_functions")) {
|
|
185873
|
+
const container = out.find((r) => (r.kind === "container" || r.kind === "gcp_cloud_run") && r.root === v.root);
|
|
185874
|
+
if (container && !v.configFiles.some((f) => basename2(f) === "vercel.json")) {
|
|
185875
|
+
merge(container, v);
|
|
185876
|
+
out = out.filter((r) => r !== v);
|
|
185877
|
+
}
|
|
185878
|
+
}
|
|
185879
|
+
for (const p2 of out.filter((r) => PROCESS_KINDS.has(r.kind))) {
|
|
185880
|
+
const owners = out.filter((o) => o !== p2 && !PROCESS_KINDS.has(o.kind) && isUnder(p2.root, o.root) && o.root !== p2.root).sort((a, b) => b.root.length - a.root.length);
|
|
185881
|
+
const owner = owners.find((o) => o.kind === "container" || o.kind === "gcp_cloud_run" || SERVERLESS_KINDS.has(o.kind) && !p2.entrypoints.some((e) => e.trigger === "http"));
|
|
185882
|
+
if (owner) {
|
|
185883
|
+
merge(owner, p2);
|
|
185884
|
+
out = out.filter((r) => r !== p2);
|
|
185885
|
+
}
|
|
185886
|
+
}
|
|
185887
|
+
return out;
|
|
185888
|
+
}
|
|
185889
|
+
function resolveCommandHandlers(rt, ctx) {
|
|
185890
|
+
const exists = (p2) => ctx.byPath.has(p2);
|
|
185891
|
+
const under = (p2) => joinPath(rt.root, p2.replace(/^\/(?:app|usr\/src\/app|code|workspace|srv)\//, ""));
|
|
185892
|
+
for (const ep of rt.entrypoints) {
|
|
185893
|
+
if (ep.handler || !ep.command)
|
|
185894
|
+
continue;
|
|
185895
|
+
const words = ep.command.flatMap((w) => w.split(/\s+/)).filter(Boolean);
|
|
185896
|
+
for (let i = 0;i < words.length && !ep.handler; i++) {
|
|
185897
|
+
const w = words[i];
|
|
185898
|
+
const mod = /^([A-Za-z_][\w.]*):([A-Za-z_]\w*)(?:\(\))?$/.exec(w) ?? (words[i - 1] === "-m" && /^[A-Za-z_][\w.]*$/.test(w) ? [w, w, ""] : null);
|
|
185899
|
+
if (mod) {
|
|
185900
|
+
const base = mod[1].replace(/\./g, "/");
|
|
185901
|
+
const file = [`${base}.py`, `${base}/__init__.py`, `${base}/__main__.py`, `src/${base}.py`].map(under).find(exists);
|
|
185902
|
+
if (file)
|
|
185903
|
+
ep.handler = mod[2] ? `${file}:${mod[2]}` : file;
|
|
185904
|
+
continue;
|
|
185905
|
+
}
|
|
185906
|
+
if (/\.(?:py|[cm]?[jt]sx?|sh)$/.test(w)) {
|
|
185907
|
+
const file = [under(w), joinPath(w)].find(exists);
|
|
185908
|
+
if (file)
|
|
185909
|
+
ep.handler = file;
|
|
185910
|
+
}
|
|
185911
|
+
}
|
|
185912
|
+
}
|
|
185913
|
+
}
|
|
185914
|
+
var INFERRED_ENTRY = /^(?:fastapi|starlette|litestar|quart|sanic|flask) /;
|
|
185915
|
+
function dedupeEntrypoints(rt) {
|
|
185916
|
+
const handlerFile = (h) => h.split(/[#:]/)[0];
|
|
185917
|
+
const kept = rt.entrypoints.filter((e) => !e.handler || !isAuxiliary(handlerFile(e.handler), rt.root));
|
|
185918
|
+
const pool = kept.length > 0 ? kept : rt.entrypoints;
|
|
185919
|
+
const commanded = new Set(pool.filter((e) => e.command && e.handler).map((e) => `${e.trigger}:${e.handler}`));
|
|
185920
|
+
const explicit = new Set(pool.filter((e) => e.command && e.handler && !INFERRED_ENTRY.test(e.name)).map((e) => `${e.trigger}:${e.handler}`));
|
|
185921
|
+
const seen = new Set;
|
|
185922
|
+
const out = [];
|
|
185923
|
+
for (const ep of pool) {
|
|
185924
|
+
if (!ep.command && ep.handler && commanded.has(`${ep.trigger}:${ep.handler}`))
|
|
185925
|
+
continue;
|
|
185926
|
+
if (ep.handler && INFERRED_ENTRY.test(ep.name) && explicit.has(`${ep.trigger}:${ep.handler}`))
|
|
185927
|
+
continue;
|
|
185928
|
+
const key = ep.command ? `${ep.trigger}:${ep.handler ?? ""}:${ep.command.join(" ")}` : `${ep.trigger}:${ep.handler ?? ""}:${ep.name}`;
|
|
185929
|
+
if (seen.has(key))
|
|
185930
|
+
continue;
|
|
185931
|
+
seen.add(key);
|
|
185932
|
+
out.push(ep);
|
|
185933
|
+
}
|
|
185934
|
+
return out;
|
|
185935
|
+
}
|
|
185936
|
+
function scriptLocalFilter(ctx, runtimes) {
|
|
185937
|
+
const scripts = ctx.files.filter((f) => /\.(?:sh|bash)$/.test(f.path));
|
|
185938
|
+
if (scripts.length === 0)
|
|
185939
|
+
return () => false;
|
|
185940
|
+
const launched = new Set(runtimes.flatMap((r) => r.entrypoints.flatMap((e) => [...(e.command ?? []).flatMap((w) => w.split(/[\s/;&|]+/)), basename2(e.handler ?? "")])));
|
|
185941
|
+
const runners = ctx.files.filter((f) => /^(?:Dockerfile[\w.-]*|Procfile|package\.json|azure\.ya?ml)$/.test(basename2(f.path)) || !f.isDoc && !f.isTest && languageOf(f.path) !== undefined).map((f) => f.text);
|
|
185942
|
+
for (const s of scripts)
|
|
185943
|
+
if (runners.some((t) => t.includes(basename2(s.path))))
|
|
185944
|
+
launched.add(basename2(s.path));
|
|
185945
|
+
for (let changed = true;changed; ) {
|
|
185946
|
+
changed = false;
|
|
185947
|
+
for (const s of scripts) {
|
|
185948
|
+
if (!launched.has(basename2(s.path)))
|
|
185949
|
+
continue;
|
|
185950
|
+
for (const m of s.text.matchAll(/(?:^|[\s;&|])(?:source|\.)\s+["']?([^\s"';&|]+\.(?:sh|bash))/gm)) {
|
|
185951
|
+
const name = basename2(m[1]);
|
|
185952
|
+
if (!launched.has(name)) {
|
|
185953
|
+
launched.add(name);
|
|
185954
|
+
changed = true;
|
|
185955
|
+
}
|
|
185956
|
+
}
|
|
185957
|
+
}
|
|
185958
|
+
}
|
|
185959
|
+
return (path) => /\.(?:sh|bash)$/.test(path) && !launched.has(basename2(path));
|
|
185960
|
+
}
|
|
185961
|
+
function rebaseCommand(command, fromRoot2, toRoot, ctx) {
|
|
185962
|
+
const sub = toRoot === "." ? fromRoot2 : fromRoot2.slice(toRoot.length + 1);
|
|
185963
|
+
let rebased = false;
|
|
185964
|
+
const out = command.map((arg) => {
|
|
185965
|
+
const path = joinPath(fromRoot2, arg);
|
|
185966
|
+
if (/[./]/.test(arg) && !arg.startsWith("-") && !arg.startsWith("/") && (ctx.byPath.has(path) || ctx.files.some((f) => f.path.startsWith(`${path}/`)))) {
|
|
185967
|
+
rebased = true;
|
|
185968
|
+
return joinPath(sub, arg);
|
|
185969
|
+
}
|
|
185970
|
+
return arg;
|
|
185971
|
+
});
|
|
185972
|
+
if (rebased)
|
|
185973
|
+
return out;
|
|
185974
|
+
const quote = (a) => /^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`;
|
|
185975
|
+
return ["sh", "-c", `cd ${quote(sub)} && ${command.map(quote).join(" ")}`];
|
|
185976
|
+
}
|
|
185125
185977
|
function consolidateRuntimes(runtimes) {
|
|
185126
185978
|
const out = [];
|
|
185127
185979
|
const owners = runtimes.filter((r) => !PROCESS_KINDS.has(r.kind));
|
|
@@ -185151,8 +186003,17 @@ function applyCloudRun(ctx, runtimes) {
|
|
|
185151
186003
|
for (const marker of markers) {
|
|
185152
186004
|
const candidates = runtimes.filter((r) => (r.kind === "container" || PROCESS_KINDS.has(r.kind) || r.kind === "gcp_cloud_run") && isUnder(r.root, marker.root));
|
|
185153
186005
|
const exact = candidates.filter((r) => r.root === marker.root);
|
|
185154
|
-
const pool = exact.length > 0 ? exact : candidates;
|
|
186006
|
+
const pool = exact.length > 0 && (marker.services ?? 1) <= 1 ? exact : candidates;
|
|
185155
186007
|
const roots = uniqSorted(pool.map((r) => r.root));
|
|
186008
|
+
if (roots.length > 1 && (marker.services ?? 1) >= roots.length) {
|
|
186009
|
+
for (const rt of pool.filter((r) => r.kind === "container" || PROCESS_KINDS.has(r.kind))) {
|
|
186010
|
+
rt.kind = "gcp_cloud_run";
|
|
186011
|
+
if (!rt.configFiles.includes(marker.path))
|
|
186012
|
+
rt.configFiles = uniqSorted([...rt.configFiles, marker.path]);
|
|
186013
|
+
rt.evidence.push({ path: marker.path, line: marker.line, detector: "runtime.gcp.cloud_run" });
|
|
186014
|
+
}
|
|
186015
|
+
continue;
|
|
186016
|
+
}
|
|
185156
186017
|
if (roots.length > 1) {
|
|
185157
186018
|
ctx.gaps.push({
|
|
185158
186019
|
kind: "ambiguous_deploy_target",
|
|
@@ -185190,6 +186051,9 @@ function applyCloudRun(ctx, runtimes) {
|
|
|
185190
186051
|
function dedupeGaps(gaps) {
|
|
185191
186052
|
const seen = new Map;
|
|
185192
186053
|
for (const g of gaps) {
|
|
186054
|
+
g.message = redact(g.message);
|
|
186055
|
+
if (g.evidence)
|
|
186056
|
+
g.evidence = g.evidence.map((e) => ({ ...e, path: redact(e.path) }));
|
|
185193
186057
|
const key = `${g.kind}:${g.message}`;
|
|
185194
186058
|
if (!seen.has(key))
|
|
185195
186059
|
seen.set(key, g);
|
|
@@ -185199,9 +186063,13 @@ function dedupeGaps(gaps) {
|
|
|
185199
186063
|
function detectDependencies(input) {
|
|
185200
186064
|
const opts = typeof input === "string" ? { dir: input } : input;
|
|
185201
186065
|
const loaded = loadFiles(opts.dir, { maxFileBytes: opts.maxFileBytes, maxFiles: opts.maxFiles });
|
|
186066
|
+
const harnessRoot = normalizeRoot(opts.harnessRoot ?? loaded.manifest?.harness?.root);
|
|
186067
|
+
const scope = computeScope(loaded.files, harnessRoot);
|
|
186068
|
+
const scoped = loaded.files.filter((f) => inScope(f.path, scope));
|
|
186069
|
+
const outside = loaded.files.filter((f) => !inScope(f.path, scope));
|
|
185202
186070
|
const ctx = {
|
|
185203
|
-
files:
|
|
185204
|
-
byPath: new Map(
|
|
186071
|
+
files: scoped,
|
|
186072
|
+
byPath: new Map(scoped.map((f) => [f.path, f])),
|
|
185205
186073
|
manifest: loaded.manifest,
|
|
185206
186074
|
signals: [],
|
|
185207
186075
|
runtimes: [],
|
|
@@ -185209,12 +186077,28 @@ function detectDependencies(input) {
|
|
|
185209
186077
|
gaps: [],
|
|
185210
186078
|
env: [],
|
|
185211
186079
|
envLinks: [],
|
|
185212
|
-
migrationHints: []
|
|
186080
|
+
migrationHints: [],
|
|
186081
|
+
functionRefs: new Map
|
|
185213
186082
|
};
|
|
185214
|
-
|
|
185215
|
-
|
|
185216
|
-
|
|
186083
|
+
if (ctx.manifest) {
|
|
186084
|
+
const scopedEnv = [];
|
|
186085
|
+
for (const ref of ctx.manifest.env ?? []) {
|
|
186086
|
+
if (isAmbientEnv(ref.name))
|
|
186087
|
+
continue;
|
|
186088
|
+
const sources = ref.sources.filter((src) => {
|
|
186089
|
+
const path = src.split(/[: ]/)[0];
|
|
186090
|
+
return (!/[/.]/.test(path) || inScope(path, scope)) && isRuntimeEnvSource(src) && !isAuxiliary(path, harnessRoot);
|
|
186091
|
+
});
|
|
186092
|
+
if (ref.sources.length > 0 && sources.length === 0)
|
|
186093
|
+
continue;
|
|
186094
|
+
scopedEnv.push({ ...ref, sources });
|
|
186095
|
+
for (const source of sources.length > 0 ? sources : ["manifest.json"])
|
|
186096
|
+
ctx.env.push({ name: ref.name, source });
|
|
186097
|
+
}
|
|
186098
|
+
ctx.manifest = { ...ctx.manifest, env: scopedEnv };
|
|
185217
186099
|
}
|
|
186100
|
+
if (outside.length > 0)
|
|
186101
|
+
ctx.gaps.push(outsideContext(outside, scope));
|
|
185218
186102
|
if (loaded.skipped.length > 0) {
|
|
185219
186103
|
const byReason = new Map;
|
|
185220
186104
|
for (const sk of loaded.skipped) {
|
|
@@ -185233,13 +186117,25 @@ function detectDependencies(input) {
|
|
|
185233
186117
|
}
|
|
185234
186118
|
if (loaded.truncated)
|
|
185235
186119
|
ctx.gaps.push({ kind: "truncated", message: `Only the first ${ctx.files.length} files were analyzed` });
|
|
185236
|
-
const
|
|
186120
|
+
const harnessFiles = ctx.files.filter((f) => isUnder(f.path, scope.root) && !f.isDoc && !f.isTest);
|
|
186121
|
+
const harnessCode = harnessFiles.map((f) => f.text);
|
|
186122
|
+
const pyImported = new Set(harnessFiles.filter((f) => languageOf(f.path) === "python").flatMap((f) => extractImports("python", f.text).map((r) => r.spec)));
|
|
186123
|
+
const usesPackage = (d) => {
|
|
186124
|
+
if (d.eco !== "pypi") {
|
|
186125
|
+
const re = new RegExp(`['"\`]${escapeRe(d.name)}(?:/[^'"\`]*)?['"\`]`);
|
|
186126
|
+
return harnessCode.some((t) => re.test(t));
|
|
186127
|
+
}
|
|
186128
|
+
const modules = [d.name.toLowerCase().replace(/-/g, "_"), ...matchModule("pypi", d.name)?.signature.py ?? []];
|
|
186129
|
+
return [...pyImported].some((spec) => modules.some((m) => spec === m || spec.startsWith(`${m}.`)));
|
|
186130
|
+
};
|
|
186131
|
+
const declared = collectDeclaredPackages(ctx.files).filter((d) => isUnder(d.path, scope.root) || !scope.configs.includes(d.path) || usesPackage(d));
|
|
185237
186132
|
const packageJsons = parsePackageJsons(ctx.files);
|
|
185238
186133
|
packageSignals(ctx, declared);
|
|
185239
186134
|
const declaredPypi = new Set(declared.filter((d) => d.eco === "pypi").map((d) => d.name));
|
|
185240
186135
|
const workspaceNames = new Set(packageJsons.map((p2) => typeof p2.json.name === "string" ? p2.json.name : "").filter(Boolean));
|
|
185241
186136
|
const scan = scanSources(ctx, declaredPypi, workspaceNames);
|
|
185242
186137
|
scanEnv(ctx);
|
|
186138
|
+
sdkDefaultEnv(ctx, declared);
|
|
185243
186139
|
const roots = projectRoots(ctx.files);
|
|
185244
186140
|
const runtimeInput = { ctx, packageJsons, declared, frameworks: scan.frameworks };
|
|
185245
186141
|
let runtimes = [
|
|
@@ -185253,6 +186149,12 @@ function detectDependencies(input) {
|
|
|
185253
186149
|
];
|
|
185254
186150
|
runtimes = applyCloudRun(ctx, runtimes);
|
|
185255
186151
|
runtimes = consolidateRuntimes(runtimes);
|
|
186152
|
+
runtimes = foldNestedRuntimes(ctx, runtimes, harnessRoot);
|
|
186153
|
+
runtimes = scopeRuntimes(ctx, runtimes, scope);
|
|
186154
|
+
const runtimeConfigs = new Set(runtimes.flatMap((r) => r.configFiles));
|
|
186155
|
+
ctx.signals = ctx.signals.filter((s) => s.kind !== "binding" || isUnder(s.evidence.path, scope.root) || runtimeConfigs.has(s.evidence.path));
|
|
186156
|
+
ctx.signals = scopeComposeImages(ctx, scope);
|
|
186157
|
+
ctx.env = ctx.env.filter((e) => e.scopeRoot === undefined || inScope(e.scopeRoot, scope) || isUnder(scope.root, e.scopeRoot));
|
|
185256
186158
|
runtimes.sort((a, b) => cmp(a.root, b.root) || cmp(a.kind, b.kind));
|
|
185257
186159
|
for (const rt of runtimes) {
|
|
185258
186160
|
if (PLATFORM_KINDS.has(rt.kind) || rt.kind === "container" || PROCESS_KINDS.has(rt.kind)) {
|
|
@@ -185261,11 +186163,15 @@ function detectDependencies(input) {
|
|
|
185261
186163
|
if (all.length > 0)
|
|
185262
186164
|
rt.frameworks = all;
|
|
185263
186165
|
}
|
|
186166
|
+
resolveCommandHandlers(rt, ctx);
|
|
186167
|
+
if (rt.entrypoints.some((e) => !e.name.startsWith("__main__ ")))
|
|
186168
|
+
rt.entrypoints = rt.entrypoints.filter((e) => !e.name.startsWith("__main__ ") || e.trigger !== "cli");
|
|
185264
186169
|
const serves = (rt.frameworks ?? []).some((f) => SERVER_FRAMEWORKS.has(f));
|
|
185265
186170
|
for (const ep of rt.entrypoints) {
|
|
185266
186171
|
if (ep.trigger === "unknown" && (serves || /gunicorn|uvicorn|hypercorn|daphne|waitress|next start|runserver|flask run|serve/.test((ep.command ?? []).join(" "))))
|
|
185267
186172
|
ep.trigger = "http";
|
|
185268
186173
|
}
|
|
186174
|
+
rt.entrypoints = dedupeEntrypoints(rt);
|
|
185269
186175
|
for (const ep of rt.entrypoints)
|
|
185270
186176
|
if (ep.command)
|
|
185271
186177
|
ep.command = ep.command.map((arg, i) => i === 2 && ep.command[0] === "sh" && ep.command[1] === "-c" ? redactScript(arg) : redactArg(arg));
|
|
@@ -185281,7 +186187,28 @@ function detectDependencies(input) {
|
|
|
185281
186187
|
}
|
|
185282
186188
|
}
|
|
185283
186189
|
const migrations = detectMigrations(ctx);
|
|
186190
|
+
const scriptLocal = scriptLocalFilter(ctx, runtimes);
|
|
186191
|
+
ctx.env = ctx.env.filter((e) => !scriptLocal(e.source.split(/[: ]/)[0]));
|
|
186192
|
+
if (ctx.manifest?.env) {
|
|
186193
|
+
const env2 = ctx.manifest.env.flatMap((ref) => {
|
|
186194
|
+
const sources = ref.sources.filter((src) => !scriptLocal(src.split(/[: ]/)[0]));
|
|
186195
|
+
return ref.sources.length > 0 && sources.length === 0 ? [] : [{ ...ref, sources }];
|
|
186196
|
+
});
|
|
186197
|
+
ctx.manifest = { ...ctx.manifest, env: env2 };
|
|
186198
|
+
}
|
|
185284
186199
|
envSignals(ctx);
|
|
186200
|
+
const touched = new Set(ctx.signals.filter((s) => isUnder(s.evidence.path, scope.root)).map((s) => s.provider));
|
|
186201
|
+
for (const sig of ctx.signals) {
|
|
186202
|
+
if (!scope.closure.some((c) => isUnder(sig.evidence.path, c)) || sig.capability === "model.inference")
|
|
186203
|
+
continue;
|
|
186204
|
+
if (sig.kind === "host" && sig.provider === "http" && !scope.closure.some((c) => isAuxiliary(sig.evidence.path, c)))
|
|
186205
|
+
continue;
|
|
186206
|
+
if (["host", "env", "env_value", "code"].includes(sig.kind) || !CLOSURE_CAPABILITIES.has(sig.capability) && !touched.has(sig.provider))
|
|
186207
|
+
sig.weak = true;
|
|
186208
|
+
}
|
|
186209
|
+
for (const sig of ctx.signals)
|
|
186210
|
+
if (isAuxiliary(sig.evidence.path, harnessRoot))
|
|
186211
|
+
sig.weak = true;
|
|
185285
186212
|
const dependencies = mergeSignals(ctx, runtimes);
|
|
185286
186213
|
linkEnv(ctx, dependencies, runtimes);
|
|
185287
186214
|
for (const m of migrations) {
|
|
@@ -185290,6 +186217,15 @@ function detectDependencies(input) {
|
|
|
185290
186217
|
const candidates = dependencies.filter((d) => MIGRATION_ENGINE_PROVIDERS.has(d.provider) && (root === undefined || (d.runtimeRoots ?? []).includes(root)));
|
|
185291
186218
|
resolveMigrationEngines([m], candidates.map((d) => d.provider), ctx);
|
|
185292
186219
|
}
|
|
186220
|
+
const keepSource = (source) => isRuntimeEnvSource(source) && !isAuxiliary(source.split(/[: ]/)[0], harnessRoot);
|
|
186221
|
+
ctx.env = ctx.env.filter((e) => !isAmbientEnv(e.name) && keepSource(e.source));
|
|
186222
|
+
if (ctx.manifest?.env) {
|
|
186223
|
+
const env2 = ctx.manifest.env.flatMap((ref) => {
|
|
186224
|
+
const sources = ref.sources.filter(keepSource);
|
|
186225
|
+
return ref.sources.length > 0 && sources.length === 0 ? [] : [{ ...ref, sources }];
|
|
186226
|
+
});
|
|
186227
|
+
ctx.manifest = { ...ctx.manifest, env: env2 };
|
|
186228
|
+
}
|
|
185293
186229
|
const env = buildEnvReferences(ctx, dependencies);
|
|
185294
186230
|
return {
|
|
185295
186231
|
schema: DEPENDENCY_MAP_SCHEMA,
|
|
@@ -185388,15 +186324,16 @@ var CACHE_FILE_PATTERNS = [
|
|
|
185388
186324
|
[/^\.yarn-integrity$/, "yarn_integrity"],
|
|
185389
186325
|
[/^install-state\.gz$/, "yarn_install_state"]
|
|
185390
186326
|
];
|
|
185391
|
-
var
|
|
186327
|
+
var ENV_TEMPLATE_WORDS = new Set(["example", "examples", "sample", "template", "dist", "tpl", "default", "defaults"]);
|
|
185392
186328
|
function isEnvExampleName(name) {
|
|
185393
186329
|
const lower = name.toLowerCase();
|
|
185394
|
-
|
|
185395
|
-
|
|
185396
|
-
|
|
185397
|
-
|
|
185398
|
-
return
|
|
186330
|
+
const base = /^(\.env|\.dev\.vars|env)\./.exec(lower);
|
|
186331
|
+
if (base)
|
|
186332
|
+
return lower.slice(base[0].length).split(".").some((part) => ENV_TEMPLATE_WORDS.has(part));
|
|
186333
|
+
const named = /^(.+)\.env$/.exec(lower);
|
|
186334
|
+
return named !== null && named[1].split(".").some((part) => ENV_TEMPLATE_WORDS.has(part));
|
|
185399
186335
|
}
|
|
186336
|
+
var ENV_VALUE_FILE_RULES = new Set(["dotenv", "direnv", "wrangler_dev_vars"]);
|
|
185400
186337
|
function secretPathRule(path) {
|
|
185401
186338
|
const name = basenameOf(path);
|
|
185402
186339
|
const lower = name.toLowerCase();
|
|
@@ -185417,7 +186354,7 @@ function secretPathRule(path) {
|
|
|
185417
186354
|
return "key_material_file";
|
|
185418
186355
|
if (/^id_(rsa|dsa|ecdsa|ed25519)/.test(lower))
|
|
185419
186356
|
return "ssh_key";
|
|
185420
|
-
if (
|
|
186357
|
+
if (/^credentials(\.(json|ya?ml|toml|conf|cfg|ini|txt|env|properties|xml|csv))?$/.test(lower) || /\.credentials$/.test(lower)) {
|
|
185421
186358
|
return "credentials_file";
|
|
185422
186359
|
}
|
|
185423
186360
|
if (/(^|\/)\.aws\/(credentials|config)$/.test(lowerPath))
|
|
@@ -185453,34 +186390,58 @@ function secretPathRule(path) {
|
|
|
185453
186390
|
return "secrets_file";
|
|
185454
186391
|
return null;
|
|
185455
186392
|
}
|
|
185456
|
-
|
|
186393
|
+
var PROVIDER_MARKER = /^(?:sk-ant-(?:api|admin|oat)\d{2}-|sk-or-v1-|sk-(?:proj|svcacct|admin)-|sk-|(?:sk|rk)_(?:live|test)_|gh[pousr]_|github_pat_|glpat-|xox[abposr]-|whsec_|gsk_|hf_|xai-|npm_|pypi-|do[opr]_v1_|shp(?:at|ca|pa|ss)_|moda_(?:sk|ct)_)/;
|
|
186394
|
+
function registryAuthFindings(path, text) {
|
|
185457
186395
|
const name = basenameOf(path).toLowerCase();
|
|
185458
186396
|
const literal = (value) => value.trim() !== "" && !/^["']?\$\{?[A-Za-z_]/.test(value.trim());
|
|
185459
|
-
|
|
185460
|
-
|
|
185461
|
-
|
|
185462
|
-
|
|
185463
|
-
|
|
185464
|
-
|
|
185465
|
-
|
|
185466
|
-
|
|
185467
|
-
|
|
185468
|
-
|
|
185469
|
-
|
|
185470
|
-
|
|
185471
|
-
|
|
185472
|
-
|
|
185473
|
-
|
|
185474
|
-
|
|
186397
|
+
const out = [];
|
|
186398
|
+
const scan = (pattern, rule2, check, yaml = false) => {
|
|
186399
|
+
for (const match of text.matchAll(pattern)) {
|
|
186400
|
+
if (!check(match[1] ?? ""))
|
|
186401
|
+
continue;
|
|
186402
|
+
const [from] = match.indices[1];
|
|
186403
|
+
let raw = match[1];
|
|
186404
|
+
if (yaml) {
|
|
186405
|
+
const quoted = /^\s*(?:"(?:[^"\\\n]|\\.)*"|'(?:[^'\n]|'')*')/.exec(raw);
|
|
186406
|
+
raw = quoted ? quoted[0] : raw.replace(/\s+#.*$/, "");
|
|
186407
|
+
}
|
|
186408
|
+
const lead = raw.length - raw.replace(/^[\s"']+/, "").length;
|
|
186409
|
+
const value = raw.replace(/[\s"']+$/, "");
|
|
186410
|
+
if (value.length <= lead)
|
|
186411
|
+
continue;
|
|
186412
|
+
out.push({ rule: rule2, line: 0, start: from + lead, end: from + value.length });
|
|
185475
186413
|
}
|
|
185476
|
-
}
|
|
186414
|
+
};
|
|
186415
|
+
if (name === ".npmrc")
|
|
186416
|
+
scan(/(?:_authToken|_auth|_password)\s*=\s*(.*)/dg, "npmrc_auth_token", literal);
|
|
186417
|
+
if (name === ".yarnrc.yml" || name === ".yarnrc")
|
|
186418
|
+
scan(/npmAuth(?:Token|Ident)\s*:\s*(.*)/dg, "yarnrc_auth_token", literal, name === ".yarnrc.yml");
|
|
186419
|
+
if (name === ".pypirc")
|
|
186420
|
+
scan(/^\s*password\s*[=:]\s*(.*)$/dgm, "pypirc_password", literal);
|
|
185477
186421
|
if (name === "pip.conf" || name === "pip.ini" || name === "poetry.toml" || name === "bunfig.toml" || name === ".condarc") {
|
|
185478
|
-
|
|
185479
|
-
|
|
186422
|
+
scan(/https?:\/\/[^\s/:@]+:([^\s/?#]+)@[^\s/?#@]+/dg, "registry_url_credentials", (value) => !value.startsWith("$"));
|
|
186423
|
+
}
|
|
186424
|
+
if (name === "settings.xml")
|
|
186425
|
+
scan(/<password>(?!\s*\$\{)([^<]+)<\/password>/dg, "maven_settings_password", () => true);
|
|
186426
|
+
out.sort((a, b) => a.start - b.start);
|
|
186427
|
+
let line = 1;
|
|
186428
|
+
let cursor = 0;
|
|
186429
|
+
for (const finding of out) {
|
|
186430
|
+
for (;cursor < finding.start; cursor += 1)
|
|
186431
|
+
if (text.charCodeAt(cursor) === 10)
|
|
186432
|
+
line += 1;
|
|
186433
|
+
finding.line = line;
|
|
185480
186434
|
}
|
|
185481
|
-
|
|
185482
|
-
|
|
185483
|
-
|
|
186435
|
+
return out;
|
|
186436
|
+
}
|
|
186437
|
+
function registryAuthRule(path, text) {
|
|
186438
|
+
return registryAuthFindings(path, text)[0]?.rule ?? null;
|
|
186439
|
+
}
|
|
186440
|
+
var LOG_DIRS = new Set(["logs", ".logs"]);
|
|
186441
|
+
function runtimeLogRule(path, status) {
|
|
186442
|
+
if (status === "tracked")
|
|
186443
|
+
return null;
|
|
186444
|
+
return /\.log(\.\d+)?$/i.test(basenameOf(path)) ? { reason: "regenerable_output", rule: "runtime_log" } : null;
|
|
185484
186445
|
}
|
|
185485
186446
|
function dirRule(dirPath, status) {
|
|
185486
186447
|
const name = basenameOf(dirPath);
|
|
@@ -185491,6 +186452,8 @@ function dirRule(dirPath, status) {
|
|
|
185491
186452
|
}
|
|
185492
186453
|
if (status === "tracked")
|
|
185493
186454
|
return null;
|
|
186455
|
+
if (LOG_DIRS.has(name))
|
|
186456
|
+
return { reason: "regenerable_output", rule: "runtime_log" };
|
|
185494
186457
|
if (DEPENDENCY_DIRS.has(name) || DEPENDENCY_DIR_SUFFIXES.some((s) => hasSuffix(dirPath, s))) {
|
|
185495
186458
|
return { reason: "dependency_install", rule: name };
|
|
185496
186459
|
}
|
|
@@ -186222,12 +187185,21 @@ class EnvCollector {
|
|
|
186222
187185
|
}
|
|
186223
187186
|
}
|
|
186224
187187
|
scanDotenv(path, lines) {
|
|
187188
|
+
let open = null;
|
|
186225
187189
|
lines.forEach((line, index) => {
|
|
187190
|
+
if (open) {
|
|
187191
|
+
if (closesQuote(line, open, 0))
|
|
187192
|
+
open = null;
|
|
187193
|
+
return;
|
|
187194
|
+
}
|
|
186226
187195
|
const m = line.match(new RegExp(`^\\s*(?:export\\s+)?(${NAME})\\s*=\\s*(.*)$`));
|
|
186227
187196
|
if (!m)
|
|
186228
187197
|
return;
|
|
186229
|
-
|
|
186230
|
-
|
|
187198
|
+
this.add(m[1], `${path}:${index + 1}`, false);
|
|
187199
|
+
const value = m[2] ?? "";
|
|
187200
|
+
const quote = value[0];
|
|
187201
|
+
if ((quote === '"' || quote === "'" || quote === "`") && !closesQuote(value, quote, 1))
|
|
187202
|
+
open = quote;
|
|
186231
187203
|
});
|
|
186232
187204
|
}
|
|
186233
187205
|
scanDestructuring(path, line, lineNo) {
|
|
@@ -186468,6 +187440,15 @@ function shellExpandable(line) {
|
|
|
186468
187440
|
}
|
|
186469
187441
|
return out;
|
|
186470
187442
|
}
|
|
187443
|
+
function closesQuote(text, quote, from) {
|
|
187444
|
+
for (let i = from;i < text.length; i += 1) {
|
|
187445
|
+
if (text[i] === "\\" && quote !== "'")
|
|
187446
|
+
i += 1;
|
|
187447
|
+
else if (text[i] === quote)
|
|
187448
|
+
return true;
|
|
187449
|
+
}
|
|
187450
|
+
return false;
|
|
187451
|
+
}
|
|
186471
187452
|
function stripInlineValue(raw) {
|
|
186472
187453
|
let value = raw.trim();
|
|
186473
187454
|
if (/^["']/.test(value)) {
|
|
@@ -186587,6 +187568,24 @@ function lsFiles(root, args) {
|
|
|
186587
187568
|
throw new Error(`git ls-files ${args.join(" ")} failed in ${root}`);
|
|
186588
187569
|
return result.stdout.split("\x00").filter((entry) => entry !== "");
|
|
186589
187570
|
}
|
|
187571
|
+
function ignoredPaths(root) {
|
|
187572
|
+
const result = git(root, ["status", "--porcelain=v1", "-z", "--ignored=matching", "--untracked-files=all"]);
|
|
187573
|
+
if (!result.ok)
|
|
187574
|
+
throw new Error(`git status --ignored failed in ${root}`);
|
|
187575
|
+
const out = [];
|
|
187576
|
+
const fields = result.stdout.split("\x00");
|
|
187577
|
+
for (let i = 0;i < fields.length; i += 1) {
|
|
187578
|
+
const field = fields[i];
|
|
187579
|
+
if (field.length < 4)
|
|
187580
|
+
continue;
|
|
187581
|
+
const code = field.slice(0, 2);
|
|
187582
|
+
if (code === "!!")
|
|
187583
|
+
out.push(field.slice(3));
|
|
187584
|
+
else if (code[0] === "R" || code[0] === "C")
|
|
187585
|
+
i += 1;
|
|
187586
|
+
}
|
|
187587
|
+
return out;
|
|
187588
|
+
}
|
|
186590
187589
|
function gitMetadata(root) {
|
|
186591
187590
|
const commit = git(root, ["rev-parse", "--verify", "-q", "HEAD"]);
|
|
186592
187591
|
const branch = git(root, ["symbolic-ref", "--short", "-q", "HEAD"]);
|
|
@@ -186877,7 +187876,6 @@ function shannonEntropy(value) {
|
|
|
186877
187876
|
return entropy;
|
|
186878
187877
|
}
|
|
186879
187878
|
var notPlaceholder = (value) => !looksLikePlaceholder(value);
|
|
186880
|
-
var LOCAL_HOSTS = /^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|::1|host\.docker\.internal|[a-z0-9_-]+)(:\d+)?$/i;
|
|
186881
187879
|
var WEAK_PASSWORDS = new Set([
|
|
186882
187880
|
"password",
|
|
186883
187881
|
"pass",
|
|
@@ -186903,9 +187901,20 @@ var WEAK_PASSWORDS = new Set([
|
|
|
186903
187901
|
var SECRET_RULES = [
|
|
186904
187902
|
{
|
|
186905
187903
|
name: "private_key_block",
|
|
186906
|
-
pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?:\r?\n|(?:\\r)?\\n)[A-Za-z][A-Za-z0-9-]*:[^\r\n\\]*)*(?:\\n|\\r|\s)+([A-Za-z0-9+/=]{32,})/g,
|
|
187904
|
+
pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?:\r?\n|(?:\\r)?\\n)[A-Za-z][A-Za-z0-9-]*:[^\r\n\\]*)*(?:\\n|\\r|\s)+([A-Za-z0-9+/=]{32,8192})/g,
|
|
186907
187905
|
group: 1,
|
|
186908
|
-
accept: notPlaceholder
|
|
187906
|
+
accept: notPlaceholder,
|
|
187907
|
+
withholdFile: true
|
|
187908
|
+
},
|
|
187909
|
+
{
|
|
187910
|
+
name: "private_key_block",
|
|
187911
|
+
pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----([^-]{1,8192}?)-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g,
|
|
187912
|
+
group: 1,
|
|
187913
|
+
accept: (body) => {
|
|
187914
|
+
const compact = body.replace(/\\[rn]|\s/g, "");
|
|
187915
|
+
return compact.length >= 16 && /^[A-Za-z0-9+/=:,-]+$/.test(compact) && !/x{4,}|X{4,}|\.\.\./.test(compact);
|
|
187916
|
+
},
|
|
187917
|
+
withholdFile: true
|
|
186909
187918
|
},
|
|
186910
187919
|
{
|
|
186911
187920
|
name: "aws_access_key_id",
|
|
@@ -186919,57 +187928,60 @@ var SECRET_RULES = [
|
|
|
186919
187928
|
group: 1,
|
|
186920
187929
|
accept: (value) => !/EXAMPLE/.test(value) && notPlaceholder(value)
|
|
186921
187930
|
},
|
|
186922
|
-
{ name: "anthropic_api_key", pattern: /\bsk-ant-(?:api|admin|oat)\d{2}-([A-Za-z0-9_-]{32,})/g, group: 1, accept: (v) => randomLooking(v, 32) },
|
|
186923
|
-
{ name: "openrouter_api_key", pattern: /\bsk-or-v1-[a-f0-9]{48,}\b/g, accept: notPlaceholder },
|
|
187931
|
+
{ name: "anthropic_api_key", pattern: /\bsk-ant-(?:api|admin|oat)\d{2}-([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g, group: 1, accept: (v) => randomLooking(v, 32) },
|
|
187932
|
+
{ name: "openrouter_api_key", pattern: /\bsk-or-v1-[a-f0-9]{48,512}\b/g, accept: notPlaceholder },
|
|
186924
187933
|
{
|
|
186925
187934
|
name: "openai_api_key",
|
|
186926
|
-
pattern: /\bsk-(?!ant-|or-)(?:proj-|svcacct-|admin-|None-)?([A-Za-z0-9_-]{32,})/g,
|
|
187935
|
+
pattern: /\bsk-(?!ant-|or-)(?:proj-|svcacct-|admin-|None-)?([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g,
|
|
186927
187936
|
group: 1,
|
|
186928
187937
|
accept: (value) => randomLooking(value, 32)
|
|
186929
187938
|
},
|
|
186930
|
-
{ name: "groq_api_key", pattern: /\bgsk_[A-Za-z0-9]{40,}\b/g, accept: notPlaceholder },
|
|
187939
|
+
{ name: "groq_api_key", pattern: /\bgsk_[A-Za-z0-9]{40,512}\b/g, accept: notPlaceholder },
|
|
186931
187940
|
{ name: "google_api_key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g, accept: notPlaceholder },
|
|
186932
|
-
{ name: "huggingface_token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g, accept: notPlaceholder },
|
|
186933
|
-
{ name: "xai_api_key", pattern: /\bxai-[A-Za-z0-9]{40,}\b/g, accept: notPlaceholder },
|
|
186934
|
-
{ name: "github_token", pattern: /\b(?:gh[pousr]_([A-Za-z0-9]{36,})|github_pat_([A-Za-z0-9_]{60,}))\b/g, accept: (_v, m) => randomLooking(m[1] ?? m[2] ?? "", 36) },
|
|
186935
|
-
{ name: "gitlab_token", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g, accept: notPlaceholder },
|
|
186936
|
-
{ name: "slack_token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,}/g, accept: notPlaceholder },
|
|
187941
|
+
{ name: "huggingface_token", pattern: /\bhf_[A-Za-z0-9]{30,512}\b/g, accept: notPlaceholder },
|
|
187942
|
+
{ name: "xai_api_key", pattern: /\bxai-[A-Za-z0-9]{40,512}\b/g, accept: notPlaceholder },
|
|
187943
|
+
{ name: "github_token", pattern: /\b(?:gh[pousr]_([A-Za-z0-9]{36,512})|github_pat_([A-Za-z0-9_]{60,512}))\b/g, accept: (_v, m) => randomLooking(m[1] ?? m[2] ?? "", 36) },
|
|
187944
|
+
{ name: "gitlab_token", pattern: /\bglpat-[A-Za-z0-9_-]{20,512}\b/g, accept: notPlaceholder },
|
|
187945
|
+
{ name: "slack_token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,512}(?![A-Za-z0-9-])/g, accept: notPlaceholder },
|
|
186937
187946
|
{
|
|
186938
187947
|
name: "slack_webhook",
|
|
186939
|
-
pattern: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{16,}/g,
|
|
187948
|
+
pattern: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{16,512}/g,
|
|
186940
187949
|
accept: notPlaceholder
|
|
186941
187950
|
},
|
|
186942
|
-
{ name: "stripe_secret_key", pattern: /\b(?:sk|rk)_(?:live|test)_([A-Za-z0-9]{20,})\b/g, group: 1, accept: (v) => randomLooking(v, 20) },
|
|
186943
|
-
{ name: "stripe_webhook_secret", pattern: /\bwhsec_[A-Za-z0-9]{24,}\b/g, accept: notPlaceholder },
|
|
187951
|
+
{ name: "stripe_secret_key", pattern: /\b(?:sk|rk)_(?:live|test)_([A-Za-z0-9]{20,512})\b/g, group: 1, accept: (v) => randomLooking(v, 20) },
|
|
187952
|
+
{ name: "stripe_webhook_secret", pattern: /\bwhsec_[A-Za-z0-9]{24,512}\b/g, accept: notPlaceholder },
|
|
186944
187953
|
{ name: "sendgrid_api_key", pattern: /\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g, accept: notPlaceholder },
|
|
186945
187954
|
{ name: "npm_token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g, accept: notPlaceholder },
|
|
186946
|
-
{ name: "pypi_token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}/g, accept: notPlaceholder },
|
|
187955
|
+
{ name: "pypi_token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,512}(?![A-Za-z0-9_-])/g, accept: notPlaceholder },
|
|
186947
187956
|
{ name: "digitalocean_token", pattern: /\bdo[opr]_v1_[a-f0-9]{64}\b/g, accept: notPlaceholder },
|
|
186948
187957
|
{ name: "shopify_token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g, accept: notPlaceholder },
|
|
186949
187958
|
{ name: "databricks_token", pattern: /\bdapi[a-f0-9]{32}\b/g, accept: notPlaceholder },
|
|
186950
|
-
{ name: "moda_api_key", pattern: /\bmoda_(?:sk|ct)_([A-Za-z0-9_-]{32,})/g, group: 1, accept: (v) => randomLooking(v, 32) },
|
|
187959
|
+
{ name: "moda_api_key", pattern: /\bmoda_(?:sk|ct)_([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g, group: 1, accept: (v) => randomLooking(v, 32) },
|
|
186951
187960
|
{
|
|
186952
187961
|
name: "jwt",
|
|
186953
|
-
pattern: /\b(eyJ[A-Za-z0-9_-]{10,})\.eyJ[A-Za-z0-9_-]{10,}\.([A-Za-z0-9_-]{16,})/g,
|
|
187962
|
+
pattern: /\b(eyJ[A-Za-z0-9_-]{10,1024})\.eyJ[A-Za-z0-9_-]{10,65536}\.([A-Za-z0-9_-]{16,1024})(?![A-Za-z0-9_-])/g,
|
|
186954
187963
|
accept: (_v, m) => isJwtHeader(m[1] ?? "") && !KNOWN_SAMPLE_JWT_SIGNATURES.has(m[2] ?? "") && notPlaceholder(m[2] ?? "")
|
|
186955
187964
|
},
|
|
187965
|
+
{
|
|
187966
|
+
name: "bearer_token",
|
|
187967
|
+
pattern: /\bBearer[ \t]+([A-Za-z0-9._~+/=-]{20,65536})(?![A-Za-z0-9._~+/=-])/g,
|
|
187968
|
+
group: 1,
|
|
187969
|
+
accept: (value) => !looksLikePlaceholder(value) && /[0-9]/.test(value) && /[A-Za-z]/.test(value)
|
|
187970
|
+
},
|
|
186956
187971
|
{
|
|
186957
187972
|
name: "url_embedded_credentials",
|
|
186958
|
-
pattern: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?|mssql|sqlserver|clickhouse|https?):\/\/([^:\s/@'"]
|
|
187973
|
+
pattern: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?|mssql|sqlserver|clickhouse|https?):\/\/([^:\s/@'"]{1,256}):([^\s/?#'"]{3,65536})@([^/\s'"?#@]+)/g,
|
|
186959
187974
|
group: 2,
|
|
186960
|
-
accept: (password
|
|
186961
|
-
const host = match[3] ?? "";
|
|
187975
|
+
accept: (password) => {
|
|
186962
187976
|
if (WEAK_PASSWORDS.has(password.toLowerCase()))
|
|
186963
187977
|
return false;
|
|
186964
|
-
|
|
186965
|
-
return false;
|
|
186966
|
-
return !LOCAL_HOSTS.test(host);
|
|
187978
|
+
return !looksLikePlaceholder(password) && !/^[$%:{]/.test(password);
|
|
186967
187979
|
}
|
|
186968
187980
|
},
|
|
186969
187981
|
{
|
|
186970
187982
|
name: "generic_secret_assignment",
|
|
186971
187983
|
heuristic: true,
|
|
186972
|
-
pattern: /\b([A-Za-z][A-Za-z0-9_-]
|
|
187984
|
+
pattern: /\b([A-Za-z][A-Za-z0-9_-]{0,127})["']?[ \t]*[:=][ \t]*(["'`])([A-Za-z0-9+/=_\-.~]{16,})\2/g,
|
|
186973
187985
|
group: 3,
|
|
186974
187986
|
accept: (value, match) => isSecretName(match[1] ?? "") && isSecretLikeValue(value)
|
|
186975
187987
|
},
|
|
@@ -186977,7 +187989,7 @@ var SECRET_RULES = [
|
|
|
186977
187989
|
name: "generic_secret_assignment",
|
|
186978
187990
|
heuristic: true,
|
|
186979
187991
|
configOnly: true,
|
|
186980
|
-
pattern:
|
|
187992
|
+
pattern: /^[ \t]*(?:export[ \t]+)?([A-Za-z][A-Za-z0-9_-]{0,127})[ \t]*[:=][ \t]*([A-Za-z0-9+/=_\-.~]{16,512})[ \t]*(?:#.*)?$/gm,
|
|
186981
187993
|
group: 2,
|
|
186982
187994
|
accept: (value, match) => isSecretName(match[1] ?? "") && isSecretLikeValue(value)
|
|
186983
187995
|
}
|
|
@@ -187035,6 +188047,842 @@ function scanForSecrets(text, opts) {
|
|
|
187035
188047
|
}
|
|
187036
188048
|
return null;
|
|
187037
188049
|
}
|
|
188050
|
+
var INDEXED = new Map;
|
|
188051
|
+
function withIndices(pattern) {
|
|
188052
|
+
let indexed = INDEXED.get(pattern);
|
|
188053
|
+
if (!indexed) {
|
|
188054
|
+
indexed = new RegExp(pattern.source, `${pattern.flags.replace("d", "")}d`);
|
|
188055
|
+
INDEXED.set(pattern, indexed);
|
|
188056
|
+
}
|
|
188057
|
+
return indexed;
|
|
188058
|
+
}
|
|
188059
|
+
function findSecretSpans(text, opts) {
|
|
188060
|
+
const spans = [];
|
|
188061
|
+
for (const rule2 of SECRET_RULES) {
|
|
188062
|
+
if (rule2.heuristic && !opts.heuristics)
|
|
188063
|
+
continue;
|
|
188064
|
+
if (rule2.configOnly && !opts.config)
|
|
188065
|
+
continue;
|
|
188066
|
+
const pattern = withIndices(rule2.pattern);
|
|
188067
|
+
pattern.lastIndex = 0;
|
|
188068
|
+
for (const match of text.matchAll(pattern)) {
|
|
188069
|
+
const group = rule2.group ?? 0;
|
|
188070
|
+
const value = match[group] ?? "";
|
|
188071
|
+
if (rule2.accept && !rule2.accept(value, match))
|
|
188072
|
+
continue;
|
|
188073
|
+
const range = match.indices?.[group] ?? [match.index ?? 0, (match.index ?? 0) + match[0].length];
|
|
188074
|
+
spans.push({ rule: rule2.name, start: range[0], end: range[1], withholdFile: rule2.withholdFile === true });
|
|
188075
|
+
}
|
|
188076
|
+
}
|
|
188077
|
+
return spans;
|
|
188078
|
+
}
|
|
188079
|
+
|
|
188080
|
+
// ../../packages/harness-bundle/src/collect/redact.ts
|
|
188081
|
+
var CONFIG_EXT = /\.(ya?ml|toml|json|jsonc|json5|properties|ini|cfg|conf|env|tfvars|hcl|cnf|nix)$/i;
|
|
188082
|
+
var CONFIG_NAMES = /^(\.npmrc|\.yarnrc|\.yarnrc\.yml|\.pypirc|pip\.conf|pip\.ini|\.condarc|\.netrc|\.envrc|\.dev\.vars.*|\.env.*|[^/]*\.env|dockerfile.*|containerfile.*|.*\.dockerfile|procfile|settings\.xml)$/i;
|
|
188083
|
+
function isConfigLike(path) {
|
|
188084
|
+
const name = basenameOf(path);
|
|
188085
|
+
return CONFIG_EXT.test(name) || CONFIG_NAMES.test(name);
|
|
188086
|
+
}
|
|
188087
|
+
var CREDENTIAL_WORDS = new Set([
|
|
188088
|
+
"token",
|
|
188089
|
+
"secret",
|
|
188090
|
+
"password",
|
|
188091
|
+
"passwd",
|
|
188092
|
+
"pwd",
|
|
188093
|
+
"passphrase",
|
|
188094
|
+
"credential",
|
|
188095
|
+
"credentials",
|
|
188096
|
+
"dsn",
|
|
188097
|
+
"apikey"
|
|
188098
|
+
]);
|
|
188099
|
+
var NOT_CREDENTIAL_WORDS = new Set([
|
|
188100
|
+
"public",
|
|
188101
|
+
"publishable",
|
|
188102
|
+
"anon",
|
|
188103
|
+
"primary",
|
|
188104
|
+
"foreign",
|
|
188105
|
+
"sort",
|
|
188106
|
+
"partition",
|
|
188107
|
+
"cache",
|
|
188108
|
+
"idempotency",
|
|
188109
|
+
"dedup",
|
|
188110
|
+
"lookup",
|
|
188111
|
+
"index",
|
|
188112
|
+
"hash",
|
|
188113
|
+
"column",
|
|
188114
|
+
"cursor",
|
|
188115
|
+
"row",
|
|
188116
|
+
"composite",
|
|
188117
|
+
"unique",
|
|
188118
|
+
"map",
|
|
188119
|
+
"dict"
|
|
188120
|
+
]);
|
|
188121
|
+
var KEY_QUALIFIERS = new Set([
|
|
188122
|
+
"api",
|
|
188123
|
+
"access",
|
|
188124
|
+
"secret",
|
|
188125
|
+
"private",
|
|
188126
|
+
"signing",
|
|
188127
|
+
"encryption",
|
|
188128
|
+
"enc",
|
|
188129
|
+
"master",
|
|
188130
|
+
"service",
|
|
188131
|
+
"auth",
|
|
188132
|
+
"client",
|
|
188133
|
+
"license",
|
|
188134
|
+
"app",
|
|
188135
|
+
"webhook",
|
|
188136
|
+
"session",
|
|
188137
|
+
"jwt",
|
|
188138
|
+
"hmac",
|
|
188139
|
+
"admin",
|
|
188140
|
+
"bearer",
|
|
188141
|
+
"account",
|
|
188142
|
+
"subscription",
|
|
188143
|
+
"crypto",
|
|
188144
|
+
"cipher",
|
|
188145
|
+
"root",
|
|
188146
|
+
"role",
|
|
188147
|
+
"write",
|
|
188148
|
+
"read",
|
|
188149
|
+
"deploy",
|
|
188150
|
+
"gateway",
|
|
188151
|
+
"aes",
|
|
188152
|
+
"creds",
|
|
188153
|
+
"credential",
|
|
188154
|
+
"credentials",
|
|
188155
|
+
"pat",
|
|
188156
|
+
"sas",
|
|
188157
|
+
"shared"
|
|
188158
|
+
]);
|
|
188159
|
+
var IDENTIFIER_KEY_WORDS = new Set([
|
|
188160
|
+
"s3",
|
|
188161
|
+
"r2",
|
|
188162
|
+
"gcs",
|
|
188163
|
+
"blob",
|
|
188164
|
+
"object",
|
|
188165
|
+
"output",
|
|
188166
|
+
"path",
|
|
188167
|
+
"plugin",
|
|
188168
|
+
"file",
|
|
188169
|
+
"prompt",
|
|
188170
|
+
"config",
|
|
188171
|
+
"json",
|
|
188172
|
+
"user",
|
|
188173
|
+
"storage",
|
|
188174
|
+
"routing",
|
|
188175
|
+
"shard",
|
|
188176
|
+
"message",
|
|
188177
|
+
"event",
|
|
188178
|
+
"bucket",
|
|
188179
|
+
"record",
|
|
188180
|
+
"i18n",
|
|
188181
|
+
"translation",
|
|
188182
|
+
"metric",
|
|
188183
|
+
"label",
|
|
188184
|
+
"group",
|
|
188185
|
+
"state",
|
|
188186
|
+
"query",
|
|
188187
|
+
"field",
|
|
188188
|
+
"attribute",
|
|
188189
|
+
"property",
|
|
188190
|
+
"tag",
|
|
188191
|
+
"header",
|
|
188192
|
+
"item",
|
|
188193
|
+
"table",
|
|
188194
|
+
"range",
|
|
188195
|
+
"prefix",
|
|
188196
|
+
"ssh",
|
|
188197
|
+
"gpg",
|
|
188198
|
+
"pgp",
|
|
188199
|
+
"idem",
|
|
188200
|
+
"sort",
|
|
188201
|
+
"partition",
|
|
188202
|
+
"cache",
|
|
188203
|
+
"lookup",
|
|
188204
|
+
"index",
|
|
188205
|
+
"hash",
|
|
188206
|
+
"column",
|
|
188207
|
+
"row",
|
|
188208
|
+
"map",
|
|
188209
|
+
"dict",
|
|
188210
|
+
"composite",
|
|
188211
|
+
"unique"
|
|
188212
|
+
]);
|
|
188213
|
+
function isCredentialName(rawName) {
|
|
188214
|
+
const name = rawName.split(/[./:\\]/).filter(Boolean).pop() ?? rawName;
|
|
188215
|
+
const words = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").split(/[_\-\s]+/).filter(Boolean).map((w) => w.toLowerCase());
|
|
188216
|
+
const last = words[words.length - 1];
|
|
188217
|
+
const previous = words[words.length - 2];
|
|
188218
|
+
if (!last || words.slice(0, -1).some((w) => NOT_CREDENTIAL_WORDS.has(w)))
|
|
188219
|
+
return false;
|
|
188220
|
+
if (CREDENTIAL_WORDS.has(last))
|
|
188221
|
+
return true;
|
|
188222
|
+
if (/^_auth$/i.test(name) || last === "ident" && previous === "auth")
|
|
188223
|
+
return true;
|
|
188224
|
+
if (last !== "key" || !previous)
|
|
188225
|
+
return false;
|
|
188226
|
+
if (KEY_QUALIFIERS.has(previous))
|
|
188227
|
+
return true;
|
|
188228
|
+
return /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)+$/.test(name) && !IDENTIFIER_KEY_WORDS.has(previous);
|
|
188229
|
+
}
|
|
188230
|
+
function isCredentialValue(raw) {
|
|
188231
|
+
const value = raw.trim();
|
|
188232
|
+
if (value.length < 4 || /[\s\p{C}]/u.test(value))
|
|
188233
|
+
return false;
|
|
188234
|
+
if (/^(true|false|null|none|nil|undefined|yes|no|on|off|~)$/i.test(value))
|
|
188235
|
+
return false;
|
|
188236
|
+
if (/^\$\{?[A-Za-z_]/.test(value) || /\$\{\{|\{\{|%\(|^\$\(/.test(value))
|
|
188237
|
+
return false;
|
|
188238
|
+
if (/^(?:os\.environ(?:\.get)?|os\.getenv|process\.env|import\.meta\.env|Deno\.env\.get|System\.getenv|getenv|ENV)(?:\(.*\)|\[.*\]|\.[A-Za-z_]\w*)$/.test(value))
|
|
188239
|
+
return false;
|
|
188240
|
+
if (/^(env|secret|vault|op|arn|ssm|awssecretsmanager|azurekeyvault|sm|gcpsm|projects)[:(/]/i.test(value) || /^@Microsoft\.KeyVault/i.test(value))
|
|
188241
|
+
return false;
|
|
188242
|
+
if (/^<.*>$/.test(value) || /^#\//.test(value))
|
|
188243
|
+
return false;
|
|
188244
|
+
if (/^(\/|\.\.?\/|~\/|[A-Za-z]:\\)/.test(value))
|
|
188245
|
+
return false;
|
|
188246
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value) && !/^[a-z][a-z0-9+.-]*:\/\/[^/@\s]+@/i.test(value))
|
|
188247
|
+
return false;
|
|
188248
|
+
if (/^[-+]?\d*\.?\d+(e[-+]?\d+)?$/i.test(value))
|
|
188249
|
+
return false;
|
|
188250
|
+
if (/^[\^~<>=v]*\d+(\.(\d+|x|\*)){0,3}([-+.][\w.-]+)?$/i.test(value))
|
|
188251
|
+
return false;
|
|
188252
|
+
if (/^(workspace|npm|file|link|portal|patch|catalog|git\+\w+|github|gitlab):/i.test(value) || /^(latest|next|canary|beta|\*)$/i.test(value))
|
|
188253
|
+
return false;
|
|
188254
|
+
if (WEAK_PASSWORDS.has(value.toLowerCase()))
|
|
188255
|
+
return false;
|
|
188256
|
+
if (/^(.)\1+$/.test(value) || sequentialRuns(value) >= 2)
|
|
188257
|
+
return false;
|
|
188258
|
+
if (/x{4,}|X{4,}|\*{3,}|\.\.\.|(^|[-_.])your[-_.]|changeme|change[-_]me|placeholder|example|replace[-_]?me|redacted|dummy|(^|[-_.])(fake|mock|sample|test|demo)([-_.]|$)/i.test(value))
|
|
188259
|
+
return false;
|
|
188260
|
+
const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((re) => re.test(value)).length;
|
|
188261
|
+
return /[0-9]/.test(value) || value.length >= 20 && classes >= 3;
|
|
188262
|
+
}
|
|
188263
|
+
var PREFIX = String.raw`[ \t]*(?:-[ \t]+)?(?:export[ \t]+|ENV[ \t]+|ARG[ \t]+)?`;
|
|
188264
|
+
var EQUALS_LINE = new RegExp(String.raw`^(${PREFIX})(["']?)([A-Za-z_@/][\w.\-/:@]*)\2[ \t]*=[ \t]*(.*)$`, "gm");
|
|
188265
|
+
var COLON_LINE = new RegExp(String.raw`^(${PREFIX})(["']?)([A-Za-z_@][\w.\-/@]*)\2[ \t]*:(?:[ \t]+|(?=["']))(.*)$`, "gm");
|
|
188266
|
+
function closingQuote(text, open, lineEnd) {
|
|
188267
|
+
const quote = text[open];
|
|
188268
|
+
for (let i = open + 1;i < lineEnd; i += 1) {
|
|
188269
|
+
if (text[i] === "\\" && quote === '"') {
|
|
188270
|
+
i += 1;
|
|
188271
|
+
continue;
|
|
188272
|
+
}
|
|
188273
|
+
if (quote === "'" && text[i] === quote && text[i + 1] === quote) {
|
|
188274
|
+
i += 1;
|
|
188275
|
+
continue;
|
|
188276
|
+
}
|
|
188277
|
+
if (text[i] === quote)
|
|
188278
|
+
return i;
|
|
188279
|
+
}
|
|
188280
|
+
return -1;
|
|
188281
|
+
}
|
|
188282
|
+
function isDockerfile(path) {
|
|
188283
|
+
const name = basenameOf(path);
|
|
188284
|
+
return /^(dockerfile|containerfile)/i.test(name) || /\.dockerfile$/i.test(name);
|
|
188285
|
+
}
|
|
188286
|
+
function valueEnds(path, envFile) {
|
|
188287
|
+
const chars = isDockerfile(path) ? "\\s" : envFile ? "\\s;" : "\\s,;)\\]}";
|
|
188288
|
+
return { terminator: new RegExp(`[${chars}]`), segmentEnd: new RegExp(`[${chars}"'\`]`) };
|
|
188289
|
+
}
|
|
188290
|
+
function dockerfileEnvSpans(source, detected) {
|
|
188291
|
+
const directives = /^(?:[ \t]*#[ \t]*[A-Za-z]+[ \t]*=[^\n]*\n)*/.exec(source)[0];
|
|
188292
|
+
const backtick = /^[ \t]*#[ \t]*escape[ \t]*=[ \t]*`[ \t]*\r?$/im.test(directives);
|
|
188293
|
+
const text = backtick ? source.replace(/[`\\]/g, (ch) => ch === "`" ? "\\" : "`") : source;
|
|
188294
|
+
const spans = [];
|
|
188295
|
+
const known = [...detected].sort((a, b) => a.start - b.start);
|
|
188296
|
+
let nextKnown = 0;
|
|
188297
|
+
let knownEnd = -1;
|
|
188298
|
+
let covered = -1;
|
|
188299
|
+
const skipIgnored = (from) => {
|
|
188300
|
+
let q = from;
|
|
188301
|
+
while (q < text.length) {
|
|
188302
|
+
const lineEnd = text.indexOf(`
|
|
188303
|
+
`, q) === -1 ? text.length : text.indexOf(`
|
|
188304
|
+
`, q);
|
|
188305
|
+
if (!/^[ \t]*(?:#.*)?\r?$/.test(text.slice(q, lineEnd)) || lineEnd === text.length)
|
|
188306
|
+
break;
|
|
188307
|
+
q = lineEnd + 1;
|
|
188308
|
+
}
|
|
188309
|
+
return q;
|
|
188310
|
+
};
|
|
188311
|
+
for (const m of text.matchAll(/^[ \t]*(ENV|ARG)[ \t]+/gim)) {
|
|
188312
|
+
if (m.index < covered)
|
|
188313
|
+
continue;
|
|
188314
|
+
let p2 = m.index + m[0].length;
|
|
188315
|
+
let instructionEnd = text.indexOf(`
|
|
188316
|
+
`, p2);
|
|
188317
|
+
while (instructionEnd !== -1) {
|
|
188318
|
+
let slash = instructionEnd - (text[instructionEnd - 1] === "\r" ? 1 : 0);
|
|
188319
|
+
const end = slash;
|
|
188320
|
+
while (slash > p2 && text[slash - 1] === "\\")
|
|
188321
|
+
slash -= 1;
|
|
188322
|
+
if ((end - slash) % 2 === 0)
|
|
188323
|
+
break;
|
|
188324
|
+
instructionEnd = text.indexOf(`
|
|
188325
|
+
`, skipIgnored(instructionEnd + 1));
|
|
188326
|
+
}
|
|
188327
|
+
if (instructionEnd === -1)
|
|
188328
|
+
instructionEnd = text.length;
|
|
188329
|
+
covered = instructionEnd;
|
|
188330
|
+
let first = true;
|
|
188331
|
+
for (;; ) {
|
|
188332
|
+
for (;; ) {
|
|
188333
|
+
if (text[p2] === " " || text[p2] === "\t" || text[p2] === "\r")
|
|
188334
|
+
p2 += 1;
|
|
188335
|
+
else if (text.startsWith("\\\n", p2))
|
|
188336
|
+
p2 = skipIgnored(p2 + 2);
|
|
188337
|
+
else if (text.startsWith(`\\\r
|
|
188338
|
+
`, p2))
|
|
188339
|
+
p2 = skipIgnored(p2 + 3);
|
|
188340
|
+
else
|
|
188341
|
+
break;
|
|
188342
|
+
}
|
|
188343
|
+
const rest = text.slice(p2, Math.min(p2 + 256, instructionEnd));
|
|
188344
|
+
let name = /^(["']?)([A-Za-z_][\w.-]*)\1=/.exec(rest);
|
|
188345
|
+
const legacy = !name && first && m[1].toUpperCase() === "ENV";
|
|
188346
|
+
if (legacy)
|
|
188347
|
+
name = /^(["']?)([A-Za-z_][\w.-]*)\1[ \t]+/.exec(rest);
|
|
188348
|
+
if (!name)
|
|
188349
|
+
break;
|
|
188350
|
+
first = false;
|
|
188351
|
+
p2 += name[0].length;
|
|
188352
|
+
const valueStart = p2;
|
|
188353
|
+
const parts = [];
|
|
188354
|
+
const addPart = (start, end) => {
|
|
188355
|
+
let from = start;
|
|
188356
|
+
for (const continuation of text.slice(start, end).matchAll(/\\\r?\n/g)) {
|
|
188357
|
+
const stop = start + continuation.index;
|
|
188358
|
+
if (stop > from)
|
|
188359
|
+
parts.push({ start: from, end: stop, rule: "credential_assignment" });
|
|
188360
|
+
from = stop + continuation[0].length;
|
|
188361
|
+
}
|
|
188362
|
+
if (end > from)
|
|
188363
|
+
parts.push({ start: from, end, rule: "credential_assignment" });
|
|
188364
|
+
};
|
|
188365
|
+
let literal = "";
|
|
188366
|
+
while (p2 < instructionEnd && (legacy || !/\s/.test(text[p2]))) {
|
|
188367
|
+
const q = text[p2];
|
|
188368
|
+
if (q === '"' || q === "'") {
|
|
188369
|
+
const close = closingQuote(text, p2, instructionEnd);
|
|
188370
|
+
const end = close === -1 ? instructionEnd : close;
|
|
188371
|
+
addPart(p2 + 1, end);
|
|
188372
|
+
literal += text.slice(p2 + 1, end);
|
|
188373
|
+
p2 = close === -1 ? instructionEnd : close + 1;
|
|
188374
|
+
} else {
|
|
188375
|
+
let e = p2;
|
|
188376
|
+
while (e < instructionEnd && !(legacy ? /["']/ : /[\s"']/).test(text[e])) {
|
|
188377
|
+
if (text[e] === "\\" && e + 1 < instructionEnd)
|
|
188378
|
+
e += text.startsWith(`\\\r
|
|
188379
|
+
`, e) ? 3 : 2;
|
|
188380
|
+
else
|
|
188381
|
+
e += 1;
|
|
188382
|
+
}
|
|
188383
|
+
addPart(p2, e);
|
|
188384
|
+
literal += text.slice(p2, e);
|
|
188385
|
+
p2 = e;
|
|
188386
|
+
}
|
|
188387
|
+
}
|
|
188388
|
+
while (nextKnown < known.length && known[nextKnown].start < valueStart) {
|
|
188389
|
+
knownEnd = Math.max(knownEnd, known[nextKnown++].end);
|
|
188390
|
+
}
|
|
188391
|
+
const hasSecret = knownEnd > valueStart || nextKnown < known.length && known[nextKnown].start < p2;
|
|
188392
|
+
const value = literal.replace(/\\\r?\n/g, "").replace(/\\(.)/g, "$1").trim();
|
|
188393
|
+
if (isCredentialName(name[2]) && (hasSecret || value.split(/[ \t]+/).some(isCredentialValue)))
|
|
188394
|
+
for (const part of parts)
|
|
188395
|
+
spans.push(part);
|
|
188396
|
+
if (legacy)
|
|
188397
|
+
break;
|
|
188398
|
+
}
|
|
188399
|
+
}
|
|
188400
|
+
return spans;
|
|
188401
|
+
}
|
|
188402
|
+
function assignmentSpans(text, everyValue, path, ends) {
|
|
188403
|
+
const toml = /\.toml$/i.test(path);
|
|
188404
|
+
const spans = [];
|
|
188405
|
+
const seenLines = new Set;
|
|
188406
|
+
for (const pattern of [EQUALS_LINE, COLON_LINE]) {
|
|
188407
|
+
pattern.lastIndex = 0;
|
|
188408
|
+
for (const m of text.matchAll(pattern)) {
|
|
188409
|
+
const lineStart = m.index ?? 0;
|
|
188410
|
+
if (seenLines.has(lineStart))
|
|
188411
|
+
continue;
|
|
188412
|
+
seenLines.add(lineStart);
|
|
188413
|
+
const name = m[3];
|
|
188414
|
+
const rest = m[4];
|
|
188415
|
+
const restStart = lineStart + m[0].length - rest.length;
|
|
188416
|
+
let value = valueBounds(rest, everyValue, toml) ?? multilineQuoted(text, restStart, toml);
|
|
188417
|
+
if (!value && everyValue && rest.trim() !== "")
|
|
188418
|
+
value = { start: 0, end: rest.replace(/[ \t]+$/, "").length };
|
|
188419
|
+
if (!value)
|
|
188420
|
+
continue;
|
|
188421
|
+
const extra = [];
|
|
188422
|
+
if (value.start > 0) {
|
|
188423
|
+
const lineEnd = text.indexOf(`
|
|
188424
|
+
`, restStart + value.end) === -1 ? text.length : text.indexOf(`
|
|
188425
|
+
`, restStart + value.end);
|
|
188426
|
+
let p2 = restStart + value.end + value.start;
|
|
188427
|
+
while (p2 < lineEnd && !ends.terminator.test(text[p2])) {
|
|
188428
|
+
const q = text[p2];
|
|
188429
|
+
if (q === '"' || q === "'" || q === "`") {
|
|
188430
|
+
const close = closingQuote(text, p2, lineEnd);
|
|
188431
|
+
const contentEnd = close === -1 ? lineEnd : close;
|
|
188432
|
+
if (contentEnd > p2 + 1)
|
|
188433
|
+
extra.push({ start: p2 + 1, end: contentEnd, rule: "" });
|
|
188434
|
+
p2 = close === -1 ? lineEnd : close + 1;
|
|
188435
|
+
} else {
|
|
188436
|
+
let e = p2;
|
|
188437
|
+
while (e < lineEnd && !ends.segmentEnd.test(text[e]))
|
|
188438
|
+
e += 1;
|
|
188439
|
+
extra.push({ start: p2, end: e, rule: "" });
|
|
188440
|
+
p2 = e;
|
|
188441
|
+
}
|
|
188442
|
+
}
|
|
188443
|
+
if (everyValue && p2 < lineEnd) {
|
|
188444
|
+
const line = text.slice(p2, lineEnd);
|
|
188445
|
+
const comment = line.search(/[ \t]+#/);
|
|
188446
|
+
const body = (comment === -1 ? line : line.slice(0, comment)).replace(/[ \t\r]+$/, "");
|
|
188447
|
+
const lead = body.length - body.replace(/^[\s,;)\]}]+/, "").length;
|
|
188448
|
+
if (body.length > lead)
|
|
188449
|
+
extra.push({ start: p2 + lead, end: p2 + body.length, rule: "" });
|
|
188450
|
+
}
|
|
188451
|
+
}
|
|
188452
|
+
const joined = extra.length > 0;
|
|
188453
|
+
const rule2 = everyValue ? "env_value" : "credential_assignment";
|
|
188454
|
+
const literal = rest.slice(value.start, Math.min(value.end, rest.length)) + (value.end > rest.length ? text.slice(restStart + rest.length, restStart + value.end) : "");
|
|
188455
|
+
if (!everyValue && /\.(hcl|tfvars)$/i.test(path) && value.start === 0 && /^[A-Za-z_]\w*(?:\.|\[|\()/.test(literal))
|
|
188456
|
+
continue;
|
|
188457
|
+
if (everyValue ? literal.trim() === "" : !(isCredentialName(name) && (joined || isCredentialValue(literal))))
|
|
188458
|
+
continue;
|
|
188459
|
+
spans.push({ start: restStart + value.start, end: restStart + value.end, rule: rule2 });
|
|
188460
|
+
for (const span of extra)
|
|
188461
|
+
spans.push({ ...span, rule: rule2 });
|
|
188462
|
+
}
|
|
188463
|
+
}
|
|
188464
|
+
return spans;
|
|
188465
|
+
}
|
|
188466
|
+
function unparsedEnvLines(text, covered) {
|
|
188467
|
+
const out = [];
|
|
188468
|
+
const assignment = new RegExp(String.raw`^${PREFIX}(["']?)[A-Za-z_@/][\w.\-/:@]*\1[ \t]*[=:]`);
|
|
188469
|
+
const spans = [...covered].sort((a, b) => a.start - b.start);
|
|
188470
|
+
let next = 0;
|
|
188471
|
+
let reach = -1;
|
|
188472
|
+
let offset = 0;
|
|
188473
|
+
for (const line of text.split(`
|
|
188474
|
+
`)) {
|
|
188475
|
+
const start = offset;
|
|
188476
|
+
const end = start + Math.max(line.length, 1);
|
|
188477
|
+
offset += line.length + 1;
|
|
188478
|
+
while (next < spans.length && spans[next].start < end)
|
|
188479
|
+
reach = Math.max(reach, spans[next++].end);
|
|
188480
|
+
if (reach > start)
|
|
188481
|
+
continue;
|
|
188482
|
+
const trimmed = line.trim();
|
|
188483
|
+
if (trimmed === "" || trimmed.startsWith("#") || assignment.test(line))
|
|
188484
|
+
continue;
|
|
188485
|
+
const lead = line.length - line.trimStart().length;
|
|
188486
|
+
out.push({ start: start + lead, end: start + line.replace(/[ \t\r]+$/, "").length, rule: "env_value" });
|
|
188487
|
+
}
|
|
188488
|
+
return out;
|
|
188489
|
+
}
|
|
188490
|
+
function tripleQuoted(text, from) {
|
|
188491
|
+
const delim = text.slice(from, from + 3);
|
|
188492
|
+
if (delim !== '"""' && delim !== "'''")
|
|
188493
|
+
return null;
|
|
188494
|
+
for (let i = from + 3;i < text.length; i += 1) {
|
|
188495
|
+
if (text[i] === "\\" && delim === '"""') {
|
|
188496
|
+
i += 1;
|
|
188497
|
+
continue;
|
|
188498
|
+
}
|
|
188499
|
+
if (text.startsWith(delim, i)) {
|
|
188500
|
+
let close = i;
|
|
188501
|
+
while (close - i < 2 && text[close + 3] === delim[0])
|
|
188502
|
+
close += 1;
|
|
188503
|
+
return close > from + 3 ? { start: 3, end: close - from } : null;
|
|
188504
|
+
}
|
|
188505
|
+
}
|
|
188506
|
+
return null;
|
|
188507
|
+
}
|
|
188508
|
+
function multilineQuoted(text, restStart, toml) {
|
|
188509
|
+
const quote = text[restStart];
|
|
188510
|
+
if (quote !== '"' && quote !== "'" && quote !== "`")
|
|
188511
|
+
return null;
|
|
188512
|
+
if (toml && quote !== "`" && text.startsWith(quote.repeat(3), restStart))
|
|
188513
|
+
return tripleQuoted(text, restStart);
|
|
188514
|
+
for (let i = restStart + 1;i < text.length; i += 1) {
|
|
188515
|
+
if (text[i] === "\\" && quote !== "'") {
|
|
188516
|
+
i += 1;
|
|
188517
|
+
continue;
|
|
188518
|
+
}
|
|
188519
|
+
if (quote === "'" && text[i] === quote && text[i + 1] === quote) {
|
|
188520
|
+
i += 1;
|
|
188521
|
+
continue;
|
|
188522
|
+
}
|
|
188523
|
+
if (text[i] === quote)
|
|
188524
|
+
return i > restStart + 1 ? { start: 1, end: i - restStart } : null;
|
|
188525
|
+
}
|
|
188526
|
+
return null;
|
|
188527
|
+
}
|
|
188528
|
+
function valueBounds(rest, envFile, toml) {
|
|
188529
|
+
if (rest === "" || /^[{[|>]/.test(rest))
|
|
188530
|
+
return null;
|
|
188531
|
+
const quote = rest[0];
|
|
188532
|
+
if (quote === '"' || quote === "'" || quote === "`") {
|
|
188533
|
+
if (toml && quote !== "`" && rest.startsWith(quote.repeat(3)))
|
|
188534
|
+
return tripleQuoted(rest, 0);
|
|
188535
|
+
for (let i = 1;i < rest.length; i += 1) {
|
|
188536
|
+
if (rest[i] === "\\" && quote === '"') {
|
|
188537
|
+
i += 1;
|
|
188538
|
+
continue;
|
|
188539
|
+
}
|
|
188540
|
+
if (quote === "'" && rest[i] === quote && rest[i + 1] === quote) {
|
|
188541
|
+
i += 1;
|
|
188542
|
+
continue;
|
|
188543
|
+
}
|
|
188544
|
+
if (rest[i] === quote)
|
|
188545
|
+
return i > 1 ? { start: 1, end: i } : null;
|
|
188546
|
+
}
|
|
188547
|
+
return null;
|
|
188548
|
+
}
|
|
188549
|
+
let end = rest.length;
|
|
188550
|
+
const comment = rest.search(envFile ? /[ \t]+#/ : /[ \t]+[#;]/);
|
|
188551
|
+
if (comment !== -1)
|
|
188552
|
+
end = comment;
|
|
188553
|
+
const trimmed = rest.slice(0, end).replace(envFile ? /[ \t]+$/ : /[ \t,]+$/, "");
|
|
188554
|
+
return trimmed === "" ? null : { start: 0, end: trimmed.length };
|
|
188555
|
+
}
|
|
188556
|
+
function xmlSpans(text) {
|
|
188557
|
+
const spans = [];
|
|
188558
|
+
for (const m of text.matchAll(/<(password|passphrase|privateKey|secret|token|apiKey|accessKey|secretKey)>([^<]+)<\/\1>/dgi)) {
|
|
188559
|
+
const value = m[2].trim();
|
|
188560
|
+
if (value === "" || /^\$\{/.test(value))
|
|
188561
|
+
continue;
|
|
188562
|
+
const [start, end] = m.indices[2];
|
|
188563
|
+
spans.push({ start, end, rule: "credential_assignment" });
|
|
188564
|
+
}
|
|
188565
|
+
return spans;
|
|
188566
|
+
}
|
|
188567
|
+
function inlinePairSpans(text, path) {
|
|
188568
|
+
const cComments = /\.json5$/i.test(path);
|
|
188569
|
+
const toml = /\.toml$/i.test(path);
|
|
188570
|
+
const yaml = /\.ya?ml$/i.test(path);
|
|
188571
|
+
const spans = [];
|
|
188572
|
+
const tomlKey = /[ \t]*(["']?)([A-Za-z_@][\w.\-/@]{0,127})\1[ \t]*=[ \t]*("""|''')/y;
|
|
188573
|
+
const pair = /[ \t]*(["']?)([A-Za-z_@][\w.\-/@]{0,127})\1[ \t]*[:=][ \t]*(?:"((?:[^"\\\n]|\\.)*)"|'((?:[^'\n]|'')*)'|([^,{}[\]\s"'][^,{}\n]*))/dy;
|
|
188574
|
+
let depth = 0;
|
|
188575
|
+
let quote = "";
|
|
188576
|
+
let previous = `
|
|
188577
|
+
`;
|
|
188578
|
+
let expectKey = false;
|
|
188579
|
+
for (let i = 0;i < text.length; i += 1) {
|
|
188580
|
+
const ch = text[i];
|
|
188581
|
+
if (quote) {
|
|
188582
|
+
if (ch === "\\" && quote === '"')
|
|
188583
|
+
i += 1;
|
|
188584
|
+
else if (quote === "'" && ch === "'" && text[i + 1] === "'")
|
|
188585
|
+
i += 1;
|
|
188586
|
+
else if (ch === quote || ch === `
|
|
188587
|
+
` && !yaml)
|
|
188588
|
+
quote = "";
|
|
188589
|
+
continue;
|
|
188590
|
+
}
|
|
188591
|
+
if (ch === " " || ch === "\t" || ch === "\r")
|
|
188592
|
+
continue;
|
|
188593
|
+
if (ch === `
|
|
188594
|
+
`) {
|
|
188595
|
+
previous = ch;
|
|
188596
|
+
continue;
|
|
188597
|
+
}
|
|
188598
|
+
if (cComments && ch === "/" && (text[i + 1] === "/" || text[i + 1] === "*") && /[\s{,[]/.test(text[i - 1] ?? `
|
|
188599
|
+
`)) {
|
|
188600
|
+
const close = text[i + 1] === "*" ? text.indexOf("*/", i + 2) : text.indexOf(`
|
|
188601
|
+
`, i);
|
|
188602
|
+
i = close === -1 ? text.length : text[i + 1] === "*" ? close + 1 : close - 1;
|
|
188603
|
+
continue;
|
|
188604
|
+
}
|
|
188605
|
+
if (ch === "#" && /\s/.test(text[i - 1] ?? `
|
|
188606
|
+
`)) {
|
|
188607
|
+
const lineEnd = text.indexOf(`
|
|
188608
|
+
`, i);
|
|
188609
|
+
i = (lineEnd === -1 ? text.length : lineEnd) - 1;
|
|
188610
|
+
continue;
|
|
188611
|
+
}
|
|
188612
|
+
if (expectKey) {
|
|
188613
|
+
expectKey = false;
|
|
188614
|
+
if (toml) {
|
|
188615
|
+
tomlKey.lastIndex = i;
|
|
188616
|
+
const k = tomlKey.exec(text);
|
|
188617
|
+
if (k) {
|
|
188618
|
+
const open = k.index + k[0].length - 3;
|
|
188619
|
+
const bounds = tripleQuoted(text, open);
|
|
188620
|
+
if (!bounds) {
|
|
188621
|
+
const close = text.indexOf(k[3], open + 3);
|
|
188622
|
+
i = close === -1 ? text.length - 1 : close + 2;
|
|
188623
|
+
} else {
|
|
188624
|
+
const from = open + bounds.start;
|
|
188625
|
+
const to = open + bounds.end;
|
|
188626
|
+
if (isCredentialName(k[2]) && text.slice(from, to).trim().split(/\s+/).some(isCredentialValue)) {
|
|
188627
|
+
spans.push({ start: from, end: to, rule: "credential_assignment" });
|
|
188628
|
+
}
|
|
188629
|
+
i = to + 2;
|
|
188630
|
+
while (text[i + 1] === k[3][0])
|
|
188631
|
+
i += 1;
|
|
188632
|
+
}
|
|
188633
|
+
previous = text[i];
|
|
188634
|
+
continue;
|
|
188635
|
+
}
|
|
188636
|
+
}
|
|
188637
|
+
pair.lastIndex = i;
|
|
188638
|
+
const m = pair.exec(text);
|
|
188639
|
+
if (m) {
|
|
188640
|
+
const group = m[3] !== undefined ? 3 : m[4] !== undefined ? 4 : 5;
|
|
188641
|
+
const value = m[group];
|
|
188642
|
+
if (isCredentialName(m[2]) && isCredentialValue(value)) {
|
|
188643
|
+
const [from] = m.indices[group];
|
|
188644
|
+
spans.push({ start: from, end: from + value.replace(/[ \t]+$/, "").length, rule: "credential_assignment" });
|
|
188645
|
+
}
|
|
188646
|
+
i = m.index + m[0].length - 1;
|
|
188647
|
+
previous = text[i];
|
|
188648
|
+
continue;
|
|
188649
|
+
}
|
|
188650
|
+
}
|
|
188651
|
+
if ((ch === '"' || ch === "'") && /[\n:=,{[(]/.test(previous)) {
|
|
188652
|
+
if (toml && text.startsWith(ch.repeat(3), i)) {
|
|
188653
|
+
const close = tripleQuoted(text, i);
|
|
188654
|
+
const end = close === null ? text.indexOf(ch.repeat(3), i + 3) : i + close.end;
|
|
188655
|
+
i = end === -1 ? text.length : end + 2;
|
|
188656
|
+
while (text[i + 1] === ch)
|
|
188657
|
+
i += 1;
|
|
188658
|
+
previous = ch;
|
|
188659
|
+
continue;
|
|
188660
|
+
}
|
|
188661
|
+
quote = ch;
|
|
188662
|
+
previous = ch;
|
|
188663
|
+
continue;
|
|
188664
|
+
}
|
|
188665
|
+
previous = ch;
|
|
188666
|
+
if (ch === "{")
|
|
188667
|
+
depth += 1;
|
|
188668
|
+
else if (ch === "}" && depth > 0)
|
|
188669
|
+
depth -= 1;
|
|
188670
|
+
if ((ch === "{" || ch === ",") && depth > 0)
|
|
188671
|
+
expectKey = true;
|
|
188672
|
+
}
|
|
188673
|
+
return spans;
|
|
188674
|
+
}
|
|
188675
|
+
function jsonSpans(text) {
|
|
188676
|
+
const spans = [];
|
|
188677
|
+
for (const m of text.matchAll(/"([^"\\\n]{1,128})"\s*:\s*"((?:[^"\\\n]|\\.)*)"/dg)) {
|
|
188678
|
+
if (!isCredentialName(m[1]) || !isCredentialValue(m[2]))
|
|
188679
|
+
continue;
|
|
188680
|
+
const [start, end] = m.indices[2];
|
|
188681
|
+
spans.push({ start, end, rule: "credential_assignment" });
|
|
188682
|
+
}
|
|
188683
|
+
return spans;
|
|
188684
|
+
}
|
|
188685
|
+
var GENERIC_RULES = new Set(["generic_secret_assignment", "credential_assignment", "env_value", "url_embedded_credentials", "bearer_token"]);
|
|
188686
|
+
var SEPARATOR2 = /[-_./:@+=,;]/;
|
|
188687
|
+
function mask(value) {
|
|
188688
|
+
const marker = PROVIDER_MARKER.exec(value)?.[0] ?? "";
|
|
188689
|
+
const keep = marker.length <= value.length - 8 ? marker : "";
|
|
188690
|
+
const body = value.slice(keep.length);
|
|
188691
|
+
const hasContent = /[\p{L}\p{N}]/u.test(body);
|
|
188692
|
+
let out = "";
|
|
188693
|
+
for (let i = 0;i < body.length; i += 1) {
|
|
188694
|
+
const ch = body[i];
|
|
188695
|
+
if (ch === "\\" && i + 1 < body.length && body[i + 1] !== `
|
|
188696
|
+
` && body[i + 1] !== "\r") {
|
|
188697
|
+
out += "xx";
|
|
188698
|
+
i += 1;
|
|
188699
|
+
continue;
|
|
188700
|
+
}
|
|
188701
|
+
if (ch === `
|
|
188702
|
+
` || ch === "\r")
|
|
188703
|
+
out += ch;
|
|
188704
|
+
else if (hasContent && SEPARATOR2.test(ch))
|
|
188705
|
+
out += ch;
|
|
188706
|
+
else
|
|
188707
|
+
out += "x";
|
|
188708
|
+
}
|
|
188709
|
+
return keep + out;
|
|
188710
|
+
}
|
|
188711
|
+
function propertiesContinuations(text, spans) {
|
|
188712
|
+
const out = [];
|
|
188713
|
+
const visited = new Set;
|
|
188714
|
+
const lineEndAt = (i) => text.indexOf(`
|
|
188715
|
+
`, i) === -1 ? text.length : text.indexOf(`
|
|
188716
|
+
`, i);
|
|
188717
|
+
const continuation = (from, end) => {
|
|
188718
|
+
let contentEnd = end;
|
|
188719
|
+
if (text[contentEnd - 1] === "\r")
|
|
188720
|
+
contentEnd -= 1;
|
|
188721
|
+
let run = contentEnd;
|
|
188722
|
+
while (run > from && text[run - 1] === "\\")
|
|
188723
|
+
run -= 1;
|
|
188724
|
+
return (contentEnd - run) % 2 === 1 ? contentEnd - 1 : -1;
|
|
188725
|
+
};
|
|
188726
|
+
const comments = new Set;
|
|
188727
|
+
let continued = false;
|
|
188728
|
+
for (let from = 0;from < text.length; ) {
|
|
188729
|
+
const end = lineEndAt(from);
|
|
188730
|
+
const comment = !continued && /^[ \t\f]*[#!]/.test(text.slice(from, end));
|
|
188731
|
+
if (comment)
|
|
188732
|
+
comments.add(end);
|
|
188733
|
+
continued = !comment && continuation(from, end) !== -1;
|
|
188734
|
+
from = end + 1;
|
|
188735
|
+
}
|
|
188736
|
+
for (const span of spans) {
|
|
188737
|
+
let end = lineEndAt(span.start);
|
|
188738
|
+
if (comments.has(end))
|
|
188739
|
+
continue;
|
|
188740
|
+
let slash = continuation(span.start, end);
|
|
188741
|
+
if (slash === -1)
|
|
188742
|
+
continue;
|
|
188743
|
+
if (span.end > slash)
|
|
188744
|
+
span.end = slash;
|
|
188745
|
+
while (slash !== -1 && end < text.length && !visited.has(end)) {
|
|
188746
|
+
visited.add(end);
|
|
188747
|
+
const from = end + 1;
|
|
188748
|
+
end = lineEndAt(from);
|
|
188749
|
+
let start = from;
|
|
188750
|
+
while (start < end && (text[start] === " " || text[start] === "\t"))
|
|
188751
|
+
start += 1;
|
|
188752
|
+
slash = continuation(start, end);
|
|
188753
|
+
const stop = slash === -1 ? text[end - 1] === "\r" ? end - 1 : end : slash;
|
|
188754
|
+
if (stop > start)
|
|
188755
|
+
out.push({ start, end: stop, rule: span.rule });
|
|
188756
|
+
}
|
|
188757
|
+
}
|
|
188758
|
+
return out;
|
|
188759
|
+
}
|
|
188760
|
+
function yamlValueSpans(text, spans, everyValue) {
|
|
188761
|
+
if (spans.length === 0) {
|
|
188762
|
+
let candidate = false;
|
|
188763
|
+
COLON_LINE.lastIndex = 0;
|
|
188764
|
+
for (const match of text.matchAll(COLON_LINE)) {
|
|
188765
|
+
if (!isCredentialName(match[3]))
|
|
188766
|
+
continue;
|
|
188767
|
+
const rest = match[4].replace(/\r$/, "");
|
|
188768
|
+
const lineEnd = match.index + match[0].length;
|
|
188769
|
+
const nextLine = /^\n([ \t]*)\S/.exec(text.slice(lineEnd, lineEnd + 512));
|
|
188770
|
+
const indent = /^[ \t]*/.exec(match[0])[0].length;
|
|
188771
|
+
if (/^[&!]/.test(rest) || /(?:^|[ \t])[|>][-+1-9]*[ \t]*(?:#.*)?$/.test(rest) || /^"(?:[^"\\]|\\.)*$/.test(rest) || /^'(?:[^']|'')*$/.test(rest) || rest.trim() !== "" && !/^["'{[]/.test(rest) && nextLine !== null && nextLine[1].length > indent) {
|
|
188772
|
+
candidate = true;
|
|
188773
|
+
break;
|
|
188774
|
+
}
|
|
188775
|
+
}
|
|
188776
|
+
if (!candidate)
|
|
188777
|
+
return [];
|
|
188778
|
+
} else if (!/[|>&!]/.test(text) && !/[\r\n][^\r\n]/.test(text))
|
|
188779
|
+
return [];
|
|
188780
|
+
const out = [];
|
|
188781
|
+
const sorted = [...spans].sort((a, b) => a.start - b.start);
|
|
188782
|
+
let next = 0;
|
|
188783
|
+
let reach = -1;
|
|
188784
|
+
let reachRule = "";
|
|
188785
|
+
for (const doc of $parseAllDocuments(text, { keepSourceTokens: true, prettyErrors: false })) {
|
|
188786
|
+
if (doc.errors.length > 0)
|
|
188787
|
+
throw new Error("YAML value bounds are unavailable");
|
|
188788
|
+
$visit(doc, (_key, node, path) => {
|
|
188789
|
+
if (!$isScalar(node) || !node.srcToken || !("source" in node.srcToken) || !node.range)
|
|
188790
|
+
return;
|
|
188791
|
+
const block = node.srcToken.type === "block-scalar";
|
|
188792
|
+
const quoted = node.type === "QUOTE_SINGLE" || node.type === "QUOTE_DOUBLE";
|
|
188793
|
+
const raw = node.srcToken.source;
|
|
188794
|
+
if (!block && !node.anchor && !node.tag && !/[\r\n]/.test(raw))
|
|
188795
|
+
return;
|
|
188796
|
+
const source = quoted ? raw.slice(1, -1) : raw;
|
|
188797
|
+
const to = node.range[1] - (quoted ? 1 : 0);
|
|
188798
|
+
const from = to - source.length;
|
|
188799
|
+
const parent = path[path.length - 1];
|
|
188800
|
+
const name = $isPair(parent) && $isScalar(parent.key) ? String(parent.key.value) : "";
|
|
188801
|
+
while (next < sorted.length && sorted[next].start < from) {
|
|
188802
|
+
if (sorted[next].end > reach)
|
|
188803
|
+
[reach, reachRule] = [sorted[next].end, sorted[next].rule];
|
|
188804
|
+
next += 1;
|
|
188805
|
+
}
|
|
188806
|
+
const inside = reach > from ? reachRule : next < sorted.length && sorted[next].start < to ? sorted[next].rule : "";
|
|
188807
|
+
const credential = isCredentialName(name) && String(node.value).trim().split(/[ \t\r\n]+/).some(isCredentialValue);
|
|
188808
|
+
if (!inside && !everyValue && !credential)
|
|
188809
|
+
return;
|
|
188810
|
+
const rule2 = inside || (everyValue ? "env_value" : "credential_assignment");
|
|
188811
|
+
for (const line of source.matchAll(/[^\r\n]+/g)) {
|
|
188812
|
+
const lead = /^[ \t]*/.exec(line[0])[0].length;
|
|
188813
|
+
if (lead < line[0].length)
|
|
188814
|
+
out.push({ start: from + line.index + lead, end: from + line.index + line[0].length, rule: rule2 });
|
|
188815
|
+
}
|
|
188816
|
+
});
|
|
188817
|
+
}
|
|
188818
|
+
return out;
|
|
188819
|
+
}
|
|
188820
|
+
function redactText(path, text, categories, opts) {
|
|
188821
|
+
const heuristics = !categories.includes("lockfile") && !/\.min\.(js|css)$|\.map$/.test(path);
|
|
188822
|
+
const config = isConfigLike(path);
|
|
188823
|
+
const found = findSecretSpans(text, { heuristics, config: !categories.includes("source") });
|
|
188824
|
+
const withhold = found.find((s) => s.withholdFile);
|
|
188825
|
+
if (withhold)
|
|
188826
|
+
return { text, redactions: [], ranges: [], withholdRule: withhold.rule };
|
|
188827
|
+
const spans = found.map(({ start, end, rule: rule2 }) => ({ start, end, rule: rule2 }));
|
|
188828
|
+
const lockfile = categories.includes("lockfile");
|
|
188829
|
+
if (opts.envFile) {
|
|
188830
|
+
spans.push(...assignmentSpans(text, true, path, valueEnds(path, true)));
|
|
188831
|
+
spans.push(...unparsedEnvLines(text, spans));
|
|
188832
|
+
} else if (config && !lockfile && !isDockerfile(path))
|
|
188833
|
+
spans.push(...assignmentSpans(text, false, path, valueEnds(path, false)));
|
|
188834
|
+
if (isDockerfile(path))
|
|
188835
|
+
for (const span of dockerfileEnvSpans(text, spans))
|
|
188836
|
+
spans.push(span);
|
|
188837
|
+
if (config && !lockfile && /\.json[c5]?$/i.test(path))
|
|
188838
|
+
spans.push(...jsonSpans(text));
|
|
188839
|
+
if (config && !lockfile && !opts.envFile && !/\.jsonc?$/i.test(path))
|
|
188840
|
+
spans.push(...inlinePairSpans(text, path));
|
|
188841
|
+
if (!lockfile && /\.xml$/i.test(path))
|
|
188842
|
+
spans.push(...xmlSpans(text));
|
|
188843
|
+
if (/\.properties$/i.test(path))
|
|
188844
|
+
spans.push(...propertiesContinuations(text, spans));
|
|
188845
|
+
if (/\.ya?ml$/i.test(path)) {
|
|
188846
|
+
try {
|
|
188847
|
+
spans.push(...yamlValueSpans(text, spans, opts.envFile));
|
|
188848
|
+
} catch {
|
|
188849
|
+
return { text, redactions: [], ranges: [], withholdRule: "credential_assignment" };
|
|
188850
|
+
}
|
|
188851
|
+
}
|
|
188852
|
+
if (spans.length === 0)
|
|
188853
|
+
return { text, redactions: [], ranges: [] };
|
|
188854
|
+
spans.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
188855
|
+
const merged = [];
|
|
188856
|
+
for (const span of spans) {
|
|
188857
|
+
if (span.end <= span.start)
|
|
188858
|
+
continue;
|
|
188859
|
+
const last = merged[merged.length - 1];
|
|
188860
|
+
if (last && span.start < last.end) {
|
|
188861
|
+
last.end = Math.max(last.end, span.end);
|
|
188862
|
+
if (GENERIC_RULES.has(last.rule) && !GENERIC_RULES.has(span.rule))
|
|
188863
|
+
last.rule = span.rule;
|
|
188864
|
+
} else {
|
|
188865
|
+
merged.push({ ...span });
|
|
188866
|
+
}
|
|
188867
|
+
}
|
|
188868
|
+
let out = "";
|
|
188869
|
+
let cursor = 0;
|
|
188870
|
+
let lineNo = 1;
|
|
188871
|
+
let lineCursor = 0;
|
|
188872
|
+
const redactions = [];
|
|
188873
|
+
for (const span of merged) {
|
|
188874
|
+
for (let i = lineCursor;i < span.start; i += 1)
|
|
188875
|
+
if (text.charCodeAt(i) === 10)
|
|
188876
|
+
lineNo += 1;
|
|
188877
|
+
lineCursor = span.start;
|
|
188878
|
+
const value = text.slice(span.start, span.end);
|
|
188879
|
+
out += text.slice(cursor, span.start) + mask(value);
|
|
188880
|
+
cursor = span.end;
|
|
188881
|
+
redactions.push({ line: lineNo, rule: span.rule, length: value.length });
|
|
188882
|
+
}
|
|
188883
|
+
out += text.slice(cursor);
|
|
188884
|
+
return { text: out, redactions, ranges: merged.map((span) => [span.start, span.end]) };
|
|
188885
|
+
}
|
|
187038
188886
|
|
|
187039
188887
|
// ../../packages/harness-bundle/src/collect/toolchain.ts
|
|
187040
188888
|
function isToolchainFile(path) {
|
|
@@ -187735,6 +189583,7 @@ class Collector {
|
|
|
187735
189583
|
texts = new Map;
|
|
187736
189584
|
env = new EnvCollector;
|
|
187737
189585
|
parentCache = new Map;
|
|
189586
|
+
redactions = [];
|
|
187738
189587
|
listedArtifacts = new Map;
|
|
187739
189588
|
constructor(repoRoot, opts) {
|
|
187740
189589
|
this.opts = opts;
|
|
@@ -187793,7 +189642,7 @@ class Collector {
|
|
|
187793
189642
|
const withPrefix = (p2) => prefix === "" ? p2 : `${prefix}/${p2}`;
|
|
187794
189643
|
const tracked = [...new Set(lsFiles(dir, ["--cached"]))].sort().map(withPrefix);
|
|
187795
189644
|
const untracked = lsFiles(dir, ["--others", "--exclude-standard"]).sort().map(withPrefix);
|
|
187796
|
-
const ignored =
|
|
189645
|
+
const ignored = ignoredPaths(dir).sort().map(withPrefix);
|
|
187797
189646
|
const ignoredDirs = ignored.filter((p2) => p2.endsWith("/"));
|
|
187798
189647
|
const ignoredRuleFiles = lsFiles(dir, ["--others", "--ignored", "--exclude-standard", "--", ":(glob)**/.modaignore"]).map(withPrefix).filter((p2) => !ignoredDirs.some((d) => p2.startsWith(d))).filter((p2) => {
|
|
187799
189648
|
const excluded = this.ancestorMatch(p2, "ignored");
|
|
@@ -187962,13 +189811,14 @@ class Collector {
|
|
|
187962
189811
|
this.addToAggregate(ancestor.dir, ancestor.rule, path, isDir);
|
|
187963
189812
|
return;
|
|
187964
189813
|
}
|
|
189814
|
+
const collectAll = this.opts.collectIgnored !== false;
|
|
187965
189815
|
if (isDir) {
|
|
187966
189816
|
const rule3 = this.dirMatch(path, "ignored");
|
|
187967
189817
|
if (rule3) {
|
|
187968
189818
|
this.recordDirOrFile(path, rule3);
|
|
187969
189819
|
return;
|
|
187970
189820
|
}
|
|
187971
|
-
if (this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, true)) {
|
|
189821
|
+
if (collectAll || this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, true)) {
|
|
187972
189822
|
this.walk(path, "ignored", null);
|
|
187973
189823
|
return;
|
|
187974
189824
|
}
|
|
@@ -187988,7 +189838,7 @@ class Collector {
|
|
|
187988
189838
|
this.excludeFile(path, "user_exclude", "modaignore", this.sizeOf(path));
|
|
187989
189839
|
return;
|
|
187990
189840
|
}
|
|
187991
|
-
if (this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, false)) {
|
|
189841
|
+
if (collectAll || this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, false)) {
|
|
187992
189842
|
this.consider(path, "ignored");
|
|
187993
189843
|
return;
|
|
187994
189844
|
}
|
|
@@ -188074,8 +189924,9 @@ class Collector {
|
|
|
188074
189924
|
}
|
|
188075
189925
|
return;
|
|
188076
189926
|
}
|
|
188077
|
-
const pathRule = fileRule(path);
|
|
188078
|
-
|
|
189927
|
+
const pathRule = fileRule(path) ?? runtimeLogRule(path, status);
|
|
189928
|
+
const envValueFile = pathRule?.reason === "secret_path" && ENV_VALUE_FILE_RULES.has(pathRule.rule) && status === "tracked";
|
|
189929
|
+
if (pathRule && !envValueFile) {
|
|
188079
189930
|
this.excludeFile(path, pathRule.reason, pathRule.rule, st.size);
|
|
188080
189931
|
return;
|
|
188081
189932
|
}
|
|
@@ -188106,20 +189957,48 @@ class Collector {
|
|
|
188106
189957
|
const text = encoding === "text" ? buf.toString("utf8") : undefined;
|
|
188107
189958
|
const categoryText = text !== undefined && /^(package\.json|pyproject\.toml|Cargo\.toml)$/.test(basenameOf(path)) ? text : undefined;
|
|
188108
189959
|
const categories = categorize({ path, encoding, head: text?.slice(0, 1024), text: categoryText });
|
|
188109
|
-
|
|
188110
|
-
|
|
188111
|
-
|
|
188112
|
-
|
|
189960
|
+
if (envValueFile && !categories.includes("env_example"))
|
|
189961
|
+
categories.push("env_example");
|
|
189962
|
+
const envFile = envValueFile || categories.includes("env_example");
|
|
189963
|
+
let shipped = buf;
|
|
189964
|
+
if (text !== undefined) {
|
|
189965
|
+
const redacted = redactText(path, text, categories, { envFile });
|
|
189966
|
+
if (redacted.withholdRule) {
|
|
189967
|
+
this.excludeFile(path, "secret_content", redacted.withholdRule, buf.length);
|
|
189968
|
+
return;
|
|
189969
|
+
}
|
|
189970
|
+
const credential = redacted.redactions.find((r) => r.rule !== "env_value");
|
|
189971
|
+
if (credential && status !== "tracked" && !categories.includes("env_example")) {
|
|
189972
|
+
this.excludeFile(path, "secret_content", credential.rule, buf.length);
|
|
189973
|
+
return;
|
|
189974
|
+
}
|
|
189975
|
+
const unmasked = registryAuthFindings(path, text).find((finding) => !rangeCovered(redacted.ranges, finding.start, finding.end));
|
|
189976
|
+
if (unmasked) {
|
|
189977
|
+
this.excludeFile(path, "secret_path", unmasked.rule, buf.length);
|
|
189978
|
+
return;
|
|
189979
|
+
}
|
|
189980
|
+
if (redacted.redactions.length > 0) {
|
|
189981
|
+
shipped = Buffer.from(redacted.text, "utf8");
|
|
189982
|
+
for (const r of redacted.redactions)
|
|
189983
|
+
this.redactions.push({ path, ...r });
|
|
189984
|
+
}
|
|
189985
|
+
} else {
|
|
189986
|
+
const finding = contentSecretFinding(path, buf, text, categories);
|
|
189987
|
+
if (finding) {
|
|
189988
|
+
this.excludeFile(path, finding.reason, finding.rule, buf.length);
|
|
189989
|
+
return;
|
|
189990
|
+
}
|
|
188113
189991
|
}
|
|
188114
189992
|
this.included.set(path, {
|
|
188115
189993
|
path,
|
|
188116
|
-
bytes:
|
|
188117
|
-
sha256: sha256(
|
|
189994
|
+
bytes: shipped.length,
|
|
189995
|
+
sha256: sha256(shipped),
|
|
188118
189996
|
executable: (st.mode & 73) !== 0,
|
|
188119
189997
|
encoding,
|
|
188120
|
-
categories,
|
|
189998
|
+
categories: [...categories].sort(),
|
|
188121
189999
|
git: status,
|
|
188122
|
-
absolutePath: abs
|
|
190000
|
+
absolutePath: abs,
|
|
190001
|
+
...shipped !== buf ? { redaction: { envFile }, sourceSha256: sha256(buf) } : {}
|
|
188123
190002
|
});
|
|
188124
190003
|
if (text !== undefined) {
|
|
188125
190004
|
if (isToolchainFile(path))
|
|
@@ -188304,7 +190183,15 @@ class Collector {
|
|
|
188304
190183
|
].sort((a, b) => compare(a.path, b.path) || compare(a.reason, b.reason));
|
|
188305
190184
|
const textOf = (path) => this.texts.get(path);
|
|
188306
190185
|
const toolchain = detectToolchain(files.map((f) => f.path), textOf, harnessRel);
|
|
188307
|
-
const manifestFiles = files.map(({
|
|
190186
|
+
const manifestFiles = files.map(({
|
|
190187
|
+
absolutePath: _absolutePath,
|
|
190188
|
+
linkTargetOnDisk: _linkTargetOnDisk,
|
|
190189
|
+
redaction: _redaction,
|
|
190190
|
+
sourceSha256: _sourceSha256,
|
|
190191
|
+
...file
|
|
190192
|
+
}) => file);
|
|
190193
|
+
const shippedPaths = new Set(manifestFiles.map((f) => f.path));
|
|
190194
|
+
const redactions = this.redactions.filter((r) => shippedPaths.has(r.path)).sort((a, b) => compare(a.path, b.path) || a.line - b.line);
|
|
188308
190195
|
const bytes = manifestFiles.reduce((sum, f) => sum + f.bytes, 0);
|
|
188309
190196
|
const manifest = {
|
|
188310
190197
|
schema: HARNESS_BUNDLE_SCHEMA,
|
|
@@ -188326,6 +190213,7 @@ class Collector {
|
|
|
188326
190213
|
totals: { files: manifestFiles.length, bytes, excluded: excluded.length },
|
|
188327
190214
|
files: manifestFiles,
|
|
188328
190215
|
excluded,
|
|
190216
|
+
...redactions.length > 0 ? { redactions } : {},
|
|
188329
190217
|
ignoredPresent: this.collapseIgnored(),
|
|
188330
190218
|
toolchain,
|
|
188331
190219
|
env: this.env.finalize(new Set(manifestFiles.map((f) => f.path))),
|
|
@@ -188471,6 +190359,19 @@ function isOutside(rel2) {
|
|
|
188471
190359
|
function stripUndefined(value) {
|
|
188472
190360
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
188473
190361
|
}
|
|
190362
|
+
function rangeCovered(ranges, start, end) {
|
|
190363
|
+
let at = start;
|
|
190364
|
+
for (const [s, e] of ranges) {
|
|
190365
|
+
if (e <= at)
|
|
190366
|
+
continue;
|
|
190367
|
+
if (s > at)
|
|
190368
|
+
return false;
|
|
190369
|
+
at = e;
|
|
190370
|
+
if (at >= end)
|
|
190371
|
+
return true;
|
|
190372
|
+
}
|
|
190373
|
+
return at >= end;
|
|
190374
|
+
}
|
|
188474
190375
|
// ../../packages/harness-bundle/src/collect/write.ts
|
|
188475
190376
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
188476
190377
|
import { createWriteStream, existsSync as existsSync5, lstatSync as lstatSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync3, renameSync, rmSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -188634,6 +190535,14 @@ function replaceTogether(moves, token) {
|
|
|
188634
190535
|
for (const [backup] of backups)
|
|
188635
190536
|
rmSync(backup, { force: true });
|
|
188636
190537
|
}
|
|
190538
|
+
function shippedBody(file, disk = readFileSync5(file.absolutePath)) {
|
|
190539
|
+
if (!file.redaction)
|
|
190540
|
+
return disk;
|
|
190541
|
+
const body = Buffer.from(redactText(file.path, disk.toString("utf8"), file.categories, file.redaction).text, "utf8");
|
|
190542
|
+
if (sha256(body) !== file.sha256)
|
|
190543
|
+
throw new Error(`${file.path} changed after collection; re-run the collector`);
|
|
190544
|
+
return body;
|
|
190545
|
+
}
|
|
188637
190546
|
function* tarChunks(files) {
|
|
188638
190547
|
const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
188639
190548
|
for (const file of sorted) {
|
|
@@ -188652,10 +190561,10 @@ function* tarChunks(files) {
|
|
|
188652
190561
|
} else {
|
|
188653
190562
|
const body = readFileSync5(file.absolutePath);
|
|
188654
190563
|
const executableNow = (statSync(file.absolutePath).mode & 73) !== 0;
|
|
188655
|
-
if (sha256(body) !== file.sha256 || executableNow !== file.executable) {
|
|
190564
|
+
if (sha256(body) !== (file.sourceSha256 ?? file.sha256) || executableNow !== file.executable) {
|
|
188656
190565
|
throw new Error(`${file.path} changed after collection; re-run the collector`);
|
|
188657
190566
|
}
|
|
188658
|
-
entry = { path: file.path, type: "file", mode: file.executable ? 493 : 420, body };
|
|
190567
|
+
entry = { path: file.path, type: "file", mode: file.executable ? 493 : 420, body: shippedBody(file, body) };
|
|
188659
190568
|
}
|
|
188660
190569
|
yield* encodeEntry(entry);
|
|
188661
190570
|
}
|
|
@@ -188689,7 +190598,6 @@ async function collectRegistration(request) {
|
|
|
188689
190598
|
throw new Error(`Git could not list this checkout, so the bundle would ignore .gitignore; nothing was collected. ${gitFailure ?? "Check that git works here (safe.directory, a readable index)."}`);
|
|
188690
190599
|
}
|
|
188691
190600
|
const deployArtifacts = request.imageRef ? [{ producedBy: "customer", kind: "oci_image", digest: request.imageRef.slice(request.imageRef.lastIndexOf("@") + 1), imageRef: request.imageRef }] : [];
|
|
188692
|
-
redactEnvExamples(result, join7(request.outDir, "redacted"));
|
|
188693
190601
|
const written = await writeBundle(result, request.outDir, { deployArtifacts: async () => deployArtifacts });
|
|
188694
190602
|
const stage = join7(request.outDir, "stage");
|
|
188695
190603
|
let dependencyMap;
|
|
@@ -188701,6 +190609,7 @@ async function collectRegistration(request) {
|
|
|
188701
190609
|
} finally {
|
|
188702
190610
|
rmSync2(stage, { recursive: true, force: true });
|
|
188703
190611
|
}
|
|
190612
|
+
keepRedactedExamplesFalse(dependencyMap, written.manifest);
|
|
188704
190613
|
const mapPath = join7(request.outDir, "dependency-map.json");
|
|
188705
190614
|
writeFileSync4(mapPath, `${JSON.stringify(dependencyMap, null, 2)}
|
|
188706
190615
|
`);
|
|
@@ -188713,64 +190622,20 @@ async function collectRegistration(request) {
|
|
|
188713
190622
|
dependencyMapFile: await digestFile(mapPath)
|
|
188714
190623
|
};
|
|
188715
190624
|
}
|
|
188716
|
-
|
|
188717
|
-
|
|
188718
|
-
const
|
|
188719
|
-
|
|
188720
|
-
|
|
188721
|
-
|
|
188722
|
-
|
|
188723
|
-
|
|
188724
|
-
const
|
|
188725
|
-
|
|
188726
|
-
mkdirSync4(dirname5(target), { recursive: true });
|
|
188727
|
-
writeFileSync4(target, body, { mode: file.executable ? 493 : 420 });
|
|
188728
|
-
const sha2562 = createHash4("sha256").update(body).digest("hex");
|
|
188729
|
-
Object.assign(file, { absolutePath: target, sha256: sha2562, bytes: body.byteLength });
|
|
188730
|
-
const listed = byPath.get(file.path);
|
|
188731
|
-
if (listed)
|
|
188732
|
-
Object.assign(listed, { sha256: sha2562, bytes: body.byteLength });
|
|
188733
|
-
}
|
|
188734
|
-
result.manifest.totals.bytes = result.manifest.files.reduce((sum, file) => sum + file.bytes, 0);
|
|
188735
|
-
if (redacted) {
|
|
188736
|
-
const env = new EnvCollector;
|
|
188737
|
-
for (const file of result.files) {
|
|
188738
|
-
if (file.encoding !== "text" || file.symlinkTarget !== undefined || file.bytes > MAX_ENV_RESCAN_BYTES)
|
|
188739
|
-
continue;
|
|
188740
|
-
env.scanFile(file.path, readFileSync6(file.absolutePath, "utf8"), file.categories);
|
|
188741
|
-
}
|
|
188742
|
-
result.manifest.env = env.finalize(new Set(result.files.map((file) => file.path)));
|
|
188743
|
-
}
|
|
188744
|
-
}
|
|
188745
|
-
function envNamesOnly(text) {
|
|
188746
|
-
const names = [];
|
|
188747
|
-
let open = null;
|
|
188748
|
-
for (const line of text.split(`
|
|
188749
|
-
`)) {
|
|
188750
|
-
if (open) {
|
|
188751
|
-
if (closes(line, open, 0))
|
|
188752
|
-
open = null;
|
|
188753
|
-
continue;
|
|
188754
|
-
}
|
|
188755
|
-
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
188756
|
-
if (!match)
|
|
190625
|
+
function keepRedactedExamplesFalse(map, manifest) {
|
|
190626
|
+
const redactedPaths = new Set((manifest.redactions ?? []).map((r) => r.path));
|
|
190627
|
+
const redactedEnvFiles = new Set(manifest.files.filter((file) => file.categories.includes("env_example") && redactedPaths.has(file.path)).map((file) => file.path));
|
|
190628
|
+
if (redactedEnvFiles.size === 0)
|
|
190629
|
+
return;
|
|
190630
|
+
const collected = new Map(manifest.env.map((ref) => [ref.name, ref]));
|
|
190631
|
+
const sourcePath = (source) => source.replace(/:\d+$/, "");
|
|
190632
|
+
for (const ref of map.env) {
|
|
190633
|
+
const seen = collected.get(ref.name);
|
|
190634
|
+
if (!seen || seen.hasExampleValue || !ref.hasExampleValue)
|
|
188757
190635
|
continue;
|
|
188758
|
-
|
|
188759
|
-
|
|
188760
|
-
if ((quote === '"' || quote === "'" || quote === "`") && !closes(match[2], quote, 1))
|
|
188761
|
-
open = quote;
|
|
188762
|
-
}
|
|
188763
|
-
return names.map((name) => `${name}=
|
|
188764
|
-
`).join("");
|
|
188765
|
-
}
|
|
188766
|
-
function closes(text, quote, from) {
|
|
188767
|
-
for (let i = from;i < text.length; i++) {
|
|
188768
|
-
if (text[i] === "\\" && quote !== "'")
|
|
188769
|
-
i++;
|
|
188770
|
-
else if (text[i] === quote)
|
|
188771
|
-
return true;
|
|
190636
|
+
if (seen.sources.some((source) => redactedEnvFiles.has(sourcePath(source))))
|
|
190637
|
+
ref.hasExampleValue = false;
|
|
188772
190638
|
}
|
|
188773
|
-
return false;
|
|
188774
190639
|
}
|
|
188775
190640
|
function insideCheckout(dir) {
|
|
188776
190641
|
for (let current = realpathSync4(dir);; current = dirname5(current)) {
|
|
@@ -188983,7 +190848,7 @@ function parseRegisterOptions(context, argv) {
|
|
|
188983
190848
|
}
|
|
188984
190849
|
function defaultHarnessName(root) {
|
|
188985
190850
|
try {
|
|
188986
|
-
const name = JSON.parse(
|
|
190851
|
+
const name = JSON.parse(readFileSync6(join8(root, "package.json"), "utf8")).name;
|
|
188987
190852
|
if (typeof name === "string" && name)
|
|
188988
190853
|
return sanitizeName(name.replace(/^@[^/]+\//, ""));
|
|
188989
190854
|
} catch {}
|
|
@@ -189019,6 +190884,7 @@ function buildRegisterReport(collected) {
|
|
|
189019
190884
|
byCategory: [...byCategory].map(([category, entry]) => ({ category, ...entry })).sort((a, b) => b.bytes - a.bytes)
|
|
189020
190885
|
},
|
|
189021
190886
|
excluded: manifest.excluded,
|
|
190887
|
+
redactions: manifest.redactions ?? [],
|
|
189022
190888
|
ignoredPresent: manifest.ignoredPresent,
|
|
189023
190889
|
env,
|
|
189024
190890
|
toolchain: manifest.toolchain,
|
|
@@ -189133,6 +190999,15 @@ function renderRegisterReport(report, registration) {
|
|
|
189133
190999
|
const rule2 = item.rule ? `, rule ${item.rule}` : "";
|
|
189134
191000
|
lines.push(` ${pad(item.path, 40)} ${item.reason}: ${EXCLUSION_REASONS[item.reason] ?? item.reason}${rule2}${size}`);
|
|
189135
191001
|
}
|
|
191002
|
+
if (report.redactions.length > 0) {
|
|
191003
|
+
lines.push("");
|
|
191004
|
+
lines.push(`Redacted: ${report.redactions.length} value(s) masked in place. The files ship; the values never leave this machine.`);
|
|
191005
|
+
for (const item of report.redactions.slice(0, 40)) {
|
|
191006
|
+
lines.push(` ${pad(`${item.path}:${item.line}`, 40)} ${item.rule} (${item.length} chars)`);
|
|
191007
|
+
}
|
|
191008
|
+
if (report.redactions.length > 40)
|
|
191009
|
+
lines.push(` ... ${report.redactions.length - 40} more (see the manifest)`);
|
|
191010
|
+
}
|
|
189136
191011
|
if (report.ignoredPresent.length > 0) {
|
|
189137
191012
|
lines.push("");
|
|
189138
191013
|
lines.push(`Git-ignored but present, not uploaded (names only): ${report.ignoredPresent.length}`);
|
|
@@ -189239,7 +191114,7 @@ function formatBytes(bytes) {
|
|
|
189239
191114
|
}
|
|
189240
191115
|
|
|
189241
191116
|
// src/harness-artifact-bodies.ts
|
|
189242
|
-
import { existsSync as existsSync7, lstatSync as lstatSync5, readFileSync as
|
|
191117
|
+
import { existsSync as existsSync7, lstatSync as lstatSync5, readFileSync as readFileSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
189243
191118
|
import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5 } from "node:path";
|
|
189244
191119
|
var MAX_SCANNED_FILE_BYTES2 = 512 * 1024;
|
|
189245
191120
|
var SNAPSHOT_BUDGET_SKIP_REASONS = new Set([
|
|
@@ -189303,7 +191178,7 @@ function readBoundedRepoText(absPath, rootDir) {
|
|
|
189303
191178
|
return;
|
|
189304
191179
|
}
|
|
189305
191180
|
try {
|
|
189306
|
-
return
|
|
191181
|
+
return readFileSync7(absPath, "utf8");
|
|
189307
191182
|
} catch {
|
|
189308
191183
|
return;
|
|
189309
191184
|
}
|
|
@@ -189854,9 +191729,9 @@ function detectPromptLiterals(file, out) {
|
|
|
189854
191729
|
});
|
|
189855
191730
|
};
|
|
189856
191731
|
if (ext === ".py") {
|
|
189857
|
-
const
|
|
191732
|
+
const tripleQuoted2 = /("""|''')([\s\S]*?)\1/g;
|
|
189858
191733
|
let match;
|
|
189859
|
-
while ((match =
|
|
191734
|
+
while ((match = tripleQuoted2.exec(file.text)) !== null) {
|
|
189860
191735
|
record(match.index, match.index + match[0].length - 1, match[2], "python.triple_quoted_string");
|
|
189861
191736
|
}
|
|
189862
191737
|
return;
|
|
@@ -190036,7 +191911,7 @@ import { extname as extname8 } from "node:path";
|
|
|
190036
191911
|
// src/prompt-sources.ts
|
|
190037
191912
|
var import_typescript2 = __toESM(require_typescript(), 1);
|
|
190038
191913
|
import { createHash as createHash5 } from "node:crypto";
|
|
190039
|
-
import { lstatSync as lstatSync7, readFileSync as
|
|
191914
|
+
import { lstatSync as lstatSync7, readFileSync as readFileSync10, realpathSync as realpathSync8 } from "node:fs";
|
|
190040
191915
|
import { extname as extname3, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve9 } from "node:path";
|
|
190041
191916
|
|
|
190042
191917
|
// src/prompt-frameworks.ts
|
|
@@ -190058,7 +191933,7 @@ var TYPESCRIPT_AGENT_MODULES = [
|
|
|
190058
191933
|
|
|
190059
191934
|
// src/prompt-source-code.ts
|
|
190060
191935
|
var import_typescript = __toESM(require_typescript(), 1);
|
|
190061
|
-
import { existsSync as existsSync8, lstatSync as lstatSync6, readFileSync as
|
|
191936
|
+
import { existsSync as existsSync8, lstatSync as lstatSync6, readFileSync as readFileSync8, realpathSync as realpathSync6 } from "node:fs";
|
|
190062
191937
|
import { dirname as dirname8, extname as extname2, join as join9, resolve as resolve7 } from "node:path";
|
|
190063
191938
|
var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
190064
191939
|
var MAX_BYTES = 2 * 1024 * 1024;
|
|
@@ -190099,7 +191974,7 @@ function parseFile(filePath, state) {
|
|
|
190099
191974
|
const cached = state.parsed.get(path);
|
|
190100
191975
|
if (cached)
|
|
190101
191976
|
return cached;
|
|
190102
|
-
const text =
|
|
191977
|
+
const text = readFileSync8(path, "utf8");
|
|
190103
191978
|
state.bytesRead += Buffer.byteLength(text);
|
|
190104
191979
|
state.files.push(path);
|
|
190105
191980
|
const scriptKind = extname2(path) === ".json" ? import_typescript.default.ScriptKind.JSON : /\.tsx?$/.test(extname2(path)) ? import_typescript.default.ScriptKind.TSX : import_typescript.default.ScriptKind.JSX;
|
|
@@ -190537,7 +192412,7 @@ function referenceNames(node) {
|
|
|
190537
192412
|
|
|
190538
192413
|
// src/prompt-source-python.ts
|
|
190539
192414
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
190540
|
-
import { readFileSync as
|
|
192415
|
+
import { readFileSync as readFileSync9, realpathSync as realpathSync7 } from "node:fs";
|
|
190541
192416
|
import { resolve as resolve8 } from "node:path";
|
|
190542
192417
|
var PYTHON_AGENT_MODULES_LITERAL = JSON.stringify(PYTHON_AGENT_MODULES);
|
|
190543
192418
|
var SELECTOR_HELPER = String.raw`import ast,json,os,sys
|
|
@@ -191004,7 +192879,7 @@ except Exception as e:
|
|
|
191004
192879
|
function extractPythonSource(rootDir, sourcePath, symbol, _forceDependencyGraph = false) {
|
|
191005
192880
|
let raw = "";
|
|
191006
192881
|
try {
|
|
191007
|
-
raw =
|
|
192882
|
+
raw = readFileSync9(sourcePath, "utf8");
|
|
191008
192883
|
} catch {}
|
|
191009
192884
|
const result = spawnSync2("python3", ["-I", "-c", HELPER], {
|
|
191010
192885
|
input: JSON.stringify({ root: rootDir, path: sourcePath, symbol, force: _forceDependencyGraph }),
|
|
@@ -191098,7 +192973,7 @@ function resolvePromptSource(rootDir, source) {
|
|
|
191098
192973
|
} else if (source.selector?.symbol) {
|
|
191099
192974
|
throw new Error(`${sourcePath}: symbol selectors require a JavaScript, TypeScript, or Python source file`);
|
|
191100
192975
|
} else if (ext === ".json" || ext === ".yaml" || ext === ".yml" || hasPointer || source.kind === "messages") {
|
|
191101
|
-
const parsed = parseStructured(
|
|
192976
|
+
const parsed = parseStructured(readFileSync10(sourceAbs, "utf8"), sourcePath, ext);
|
|
191102
192977
|
const selected = hasPointer ? jsonPointer(parsed, source.selector.pointer, sourcePath) : parsed;
|
|
191103
192978
|
const selectedField = source.selector?.pointer?.split("/").at(-1)?.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
191104
192979
|
const inferredKind = !source.kind && selectedField && /^(?:template|system_template|instance_template)$/.test(selectedField) ? "template" : source.kind;
|
|
@@ -191108,7 +192983,7 @@ function resolvePromptSource(rootDir, source) {
|
|
|
191108
192983
|
messages = structured.messages;
|
|
191109
192984
|
kind = structured.kind;
|
|
191110
192985
|
} else {
|
|
191111
|
-
const raw =
|
|
192986
|
+
const raw = readFileSync10(sourceAbs, "utf8");
|
|
191112
192987
|
if (ext === ".md" && /\.prompt\.md$/i.test(sourcePath)) {
|
|
191113
192988
|
const legacy = parseLegacyMarkdown(raw);
|
|
191114
192989
|
content = legacy.content;
|
|
@@ -191131,7 +193006,7 @@ function resolvePromptSource(rootDir, source) {
|
|
|
191131
193006
|
const depAbs = safePath(root, dependency.path);
|
|
191132
193007
|
files.add(depAbs);
|
|
191133
193008
|
if (typeof dependency.selector?.pointer === "string") {
|
|
191134
|
-
const parsed = parseStructured(
|
|
193009
|
+
const parsed = parseStructured(readFileSync10(depAbs, "utf8"), dependency.path, extname3(depAbs).toLowerCase());
|
|
191135
193010
|
jsonPointer(parsed, dependency.selector.pointer, dependency.path);
|
|
191136
193011
|
} else if (dependency.selector?.symbol && CODE_EXTENSIONS2.has(extname3(depAbs).toLowerCase())) {
|
|
191137
193012
|
const dep = extname3(depAbs).toLowerCase() === ".py" ? extractPythonSource(root, depAbs, dependency.selector.symbol) : extractCodeSource(root, depAbs, dependency.selector.symbol);
|
|
@@ -191154,7 +193029,7 @@ function resolvePromptSource(rootDir, source) {
|
|
|
191154
193029
|
assertSafeMaterializedContent(content, sourcePath);
|
|
191155
193030
|
else
|
|
191156
193031
|
assertSafeMaterializedContent(materialized, sourcePath);
|
|
191157
|
-
const dependencies = [...files].sort().map((path) => ({ path: relative5(root, path).replaceAll("\\", "/"), contentHash: sha2562(
|
|
193032
|
+
const dependencies = [...files].sort().map((path) => ({ path: relative5(root, path).replaceAll("\\", "/"), contentHash: sha2562(readFileSync10(path)) }));
|
|
191158
193033
|
const sourceHash = sha2562(JSON.stringify({ kind, content, systemPrompt: systemPrompt ?? null, messages: messages ?? null }));
|
|
191159
193034
|
const sourceDefinition = {
|
|
191160
193035
|
schemaVersion: 1,
|
|
@@ -191224,7 +193099,7 @@ function discoverStructuredPromptSources(rootDir, sourcePath) {
|
|
|
191224
193099
|
const ext = extname3(file).toLowerCase();
|
|
191225
193100
|
if (![".json", ".yaml", ".yml"].includes(ext))
|
|
191226
193101
|
return [];
|
|
191227
|
-
const value = parseStructured(
|
|
193102
|
+
const value = parseStructured(readFileSync10(file, "utf8"), sourcePath, ext);
|
|
191228
193103
|
const sources = [];
|
|
191229
193104
|
const base = sourcePath.replace(/\.[^.]+$/, "").replace(/[^A-Za-z0-9_.-]+/g, ".");
|
|
191230
193105
|
const dedicatedPath = /(^|\/)prompts?(?:\/|\.[^/]+$)|\.prompt\.[^.]+$/i.test(sourcePath);
|
|
@@ -191300,14 +193175,14 @@ function discoverCodePromptCandidates(rootDir, sourcePath) {
|
|
|
191300
193175
|
addName(candidate.symbol, candidate.knownBuilder === true);
|
|
191301
193176
|
modelInputs.add(candidate.symbol);
|
|
191302
193177
|
}
|
|
191303
|
-
for (const match of
|
|
193178
|
+
for (const match of readFileSync10(file, "utf8").matchAll(/^(?:(?:async\s+)?def\s+([A-Za-z_]\w*)|([A-Za-z_]\w*)\s*=\s*([^\n#]+))/gm)) {
|
|
191304
193179
|
const name = match[1] ?? match[2];
|
|
191305
193180
|
const rhs = match[3] ?? "";
|
|
191306
193181
|
if (name && isPromptName2(name) && (match[1] || /['"`\[({+]/.test(rhs) || /^[A-Za-z_]\w*\s*$/.test(rhs)))
|
|
191307
193182
|
addName(name, Boolean(match[1]));
|
|
191308
193183
|
}
|
|
191309
193184
|
} else {
|
|
191310
|
-
const sf = import_typescript2.default.createSourceFile(file,
|
|
193185
|
+
const sf = import_typescript2.default.createSourceFile(file, readFileSync10(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TSX);
|
|
191311
193186
|
const agentConstructors = new Set;
|
|
191312
193187
|
for (const statement of sf.statements) {
|
|
191313
193188
|
if (!import_typescript2.default.isImportDeclaration(statement) || !import_typescript2.default.isStringLiteral(statement.moduleSpecifier) || !TYPESCRIPT_AGENT_MODULES.includes(statement.moduleSpecifier.text))
|
|
@@ -191541,7 +193416,7 @@ function sha2562(input) {
|
|
|
191541
193416
|
}
|
|
191542
193417
|
|
|
191543
193418
|
// src/prompt-source-verification/index.ts
|
|
191544
|
-
import { readFileSync as
|
|
193419
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
191545
193420
|
import { z } from "zod";
|
|
191546
193421
|
|
|
191547
193422
|
// src/prompt-source-validation.ts
|
|
@@ -191630,7 +193505,7 @@ function isPromptDefinitionPath(path) {
|
|
|
191630
193505
|
}
|
|
191631
193506
|
|
|
191632
193507
|
// src/prompt-source-verification/evidence.ts
|
|
191633
|
-
import { readFileSync as
|
|
193508
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
191634
193509
|
import { extname as extname7 } from "node:path";
|
|
191635
193510
|
|
|
191636
193511
|
// src/prompt-source-verification/paths.ts
|
|
@@ -191660,14 +193535,14 @@ function safePromptEvidencePath(rootDir, path) {
|
|
|
191660
193535
|
var import_typescript3 = __toESM(require_typescript(), 1);
|
|
191661
193536
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
191662
193537
|
import { extname as extname5 } from "node:path";
|
|
191663
|
-
import { readFileSync as
|
|
193538
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
191664
193539
|
function proveSink(root, sink) {
|
|
191665
193540
|
const file = safePromptEvidencePath(root, sink.path);
|
|
191666
193541
|
const cache = new Map;
|
|
191667
193542
|
if (!cache.has(file)) {
|
|
191668
193543
|
if (extname5(file) === ".py") {
|
|
191669
193544
|
try {
|
|
191670
|
-
const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input:
|
|
193545
|
+
const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync11(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
|
|
191671
193546
|
cache.set(file, new Map(Object.entries(rows).map(([line, proof]) => [Number(line), proof])));
|
|
191672
193547
|
} catch {
|
|
191673
193548
|
cache.set(file, new Map);
|
|
@@ -191834,7 +193709,7 @@ print(json.dumps(rows))
|
|
|
191834
193709
|
`;
|
|
191835
193710
|
|
|
191836
193711
|
// src/prompt-source-location.ts
|
|
191837
|
-
import { readFileSync as
|
|
193712
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
191838
193713
|
import { extname as extname6 } from "node:path";
|
|
191839
193714
|
function structuredExcerpt(text, source) {
|
|
191840
193715
|
if (source.selector?.pointer === undefined)
|
|
@@ -191888,7 +193763,7 @@ function promptDefinitionExcerpt(rootDir, source) {
|
|
|
191888
193763
|
return;
|
|
191889
193764
|
return { ...extraction.range, text: extraction.sourceText, ...extraction.declarationKind ? { declarationKind: extraction.declarationKind } : {} };
|
|
191890
193765
|
}
|
|
191891
|
-
const text =
|
|
193766
|
+
const text = readFileSync12(file, "utf8");
|
|
191892
193767
|
return structuredExcerpt(text, source);
|
|
191893
193768
|
}
|
|
191894
193769
|
function promptDefinitionRange(rootDir, source) {
|
|
@@ -191915,7 +193790,7 @@ function reviewEvidence(rootDir, source, evidence) {
|
|
|
191915
193790
|
for (const item of evidence) {
|
|
191916
193791
|
let text;
|
|
191917
193792
|
try {
|
|
191918
|
-
text =
|
|
193793
|
+
text = readFileSync13(safePromptEvidencePath(rootDir, item.path), "utf8");
|
|
191919
193794
|
} catch (error) {
|
|
191920
193795
|
if (item.kind === "model_endpoint")
|
|
191921
193796
|
return { valid: false, endpointVerified: false, reason: error instanceof Error ? error.message : String(error) };
|
|
@@ -192030,7 +193905,7 @@ async function independentlyVerifyPromptSource(rootDir, source, verifier) {
|
|
|
192030
193905
|
let priorJudgment;
|
|
192031
193906
|
try {
|
|
192032
193907
|
const before = JSON.stringify(checked.resolved.sourceDefinition);
|
|
192033
|
-
const sourceBytes =
|
|
193908
|
+
const sourceBytes = readFileSync14(safePromptEvidencePath(rootDir, source.path), "utf8");
|
|
192034
193909
|
const instruction = promptVerificationInstruction(checked.source, checked.sourceHash, promptDefinitionExcerpt(rootDir, checked.source));
|
|
192035
193910
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
192036
193911
|
const repair = attempt ? `
|
|
@@ -192061,7 +193936,7 @@ ${output}` : "";
|
|
|
192061
193936
|
}
|
|
192062
193937
|
if (reviewed.truncated || reviewed.sourceHash !== checked.sourceHash)
|
|
192063
193938
|
return pending("Verifier output is truncated or cites a different source hash", reviewed.evidence, reviewed.role, normalizeUsage(undefined, false));
|
|
192064
|
-
if (before !== snapshot() ||
|
|
193939
|
+
if (before !== snapshot() || readFileSync14(safePromptEvidencePath(rootDir, source.path), "utf8") !== sourceBytes)
|
|
192065
193940
|
return pending("Source changed during verification", reviewed.evidence, reviewed.role, normalizeUsage(undefined, false));
|
|
192066
193941
|
if (reviewed.role === "non_prompt")
|
|
192067
193942
|
return { status: "rejected", reason: reviewed.reason, evidence: reviewed.evidence, sourceHash: checked.sourceHash, role: reviewed.role, usage: normalizeUsage(undefined, false) };
|
|
@@ -192421,7 +194296,7 @@ function applyPromptDiscovery(graph, discovery) {
|
|
|
192421
194296
|
}
|
|
192422
194297
|
|
|
192423
194298
|
// src/harness-candidate-promotion.ts
|
|
192424
|
-
import { readFileSync as
|
|
194299
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
192425
194300
|
import { resolve as resolve12 } from "node:path";
|
|
192426
194301
|
var PROMOTED_CONFIDENCE = {
|
|
192427
194302
|
high: 0.85,
|
|
@@ -192452,7 +194327,7 @@ function readFileLines(rootDir, path, cache) {
|
|
|
192452
194327
|
return cached;
|
|
192453
194328
|
let lines;
|
|
192454
194329
|
try {
|
|
192455
|
-
lines =
|
|
194330
|
+
lines = readFileSync15(resolve12(rootDir, path), "utf8").split(/\r?\n/);
|
|
192456
194331
|
} catch {
|
|
192457
194332
|
lines = null;
|
|
192458
194333
|
}
|
|
@@ -192606,19 +194481,19 @@ function assertHarnessGraphSyncSize(graph) {
|
|
|
192606
194481
|
// ../../packages/messaging/messaging.json
|
|
192607
194482
|
var messaging_default = {
|
|
192608
194483
|
$schema: "./messaging.schema.json",
|
|
192609
|
-
version: "1.0.
|
|
192610
|
-
locked_on: "2026-10-
|
|
194484
|
+
version: "1.0.4",
|
|
194485
|
+
locked_on: "2026-10-15",
|
|
192611
194486
|
approved_by: "mo",
|
|
192612
|
-
source: "Branding project docs/positioning.md v1.0.
|
|
194487
|
+
source: "Branding project docs/positioning.md v1.0.4",
|
|
192613
194488
|
canonical_strings: {
|
|
192614
|
-
version: "1.0.
|
|
192615
|
-
locked_on: "2026-10-
|
|
194489
|
+
version: "1.0.4",
|
|
194490
|
+
locked_on: "2026-10-15",
|
|
192616
194491
|
approved_by: "mo",
|
|
192617
194492
|
strings: [
|
|
192618
194493
|
{
|
|
192619
194494
|
id: "one_liner",
|
|
192620
194495
|
source: "approved",
|
|
192621
|
-
text: "
|
|
194496
|
+
text: "Infrastructure for catching AI agent mistakes in production and turning them into evals.",
|
|
192622
194497
|
match: "exact",
|
|
192623
194498
|
surfaces: [
|
|
192624
194499
|
"apps/moda-frontend/components/landing/hero.tsx (subhead under the H1)",
|
|
@@ -192678,7 +194553,7 @@ var messaging_default = {
|
|
|
192678
194553
|
{
|
|
192679
194554
|
id: "meta_description",
|
|
192680
194555
|
source: "derived",
|
|
192681
|
-
text: "Production monitoring and evals for AI agents.
|
|
194556
|
+
text: "Production monitoring and evals for AI agents. Infrastructure for catching AI agent mistakes in production and turning them into evals.",
|
|
192682
194557
|
match: "exact",
|
|
192683
194558
|
max_chars: 160,
|
|
192684
194559
|
surfaces: [
|
|
@@ -192703,7 +194578,7 @@ var messaging_default = {
|
|
|
192703
194578
|
{
|
|
192704
194579
|
id: "long_description",
|
|
192705
194580
|
source: "derived",
|
|
192706
|
-
text: "
|
|
194581
|
+
text: "Infrastructure for catching AI agent mistakes in production and turning them into evals. Agents fail in ways that never throw an error: frustrated users, failing tools, lazy or hallucinated answers. Moda reads every production trace end to end, detects these failures, and groups them into problems ranked by how many users they affect, with the traces behind each one. When your team changes a prompt, tool, or model, Moda replays the change against those traces and shows whether it worked, then keeps monitoring after release. Works with any model provider through SDKs, OpenTelemetry, or HTTP. Backed by Y Combinator.",
|
|
192707
194582
|
match: "exact",
|
|
192708
194583
|
surfaces: [
|
|
192709
194584
|
"LinkedIn About",
|
|
@@ -192817,7 +194692,7 @@ var messaging_default = {
|
|
|
192817
194692
|
]
|
|
192818
194693
|
},
|
|
192819
194694
|
banned_phrases: {
|
|
192820
|
-
version: "1.0.
|
|
194695
|
+
version: "1.0.4",
|
|
192821
194696
|
phrases: [
|
|
192822
194697
|
{
|
|
192823
194698
|
id: "fixes_your_agent",
|
|
@@ -192994,7 +194869,7 @@ var messaging_default = {
|
|
|
192994
194869
|
]
|
|
192995
194870
|
},
|
|
192996
194871
|
terminology: {
|
|
192997
|
-
version: "1.0.
|
|
194872
|
+
version: "1.0.4",
|
|
192998
194873
|
terms: [
|
|
192999
194874
|
{
|
|
193000
194875
|
use: "trace",
|
|
@@ -193996,7 +195871,7 @@ async function runHarnessCommand(context) {
|
|
|
193996
195871
|
throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
|
|
193997
195872
|
}
|
|
193998
195873
|
if (context.flags["github-actions"] === "true") {
|
|
193999
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
195874
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-301nak95.js");
|
|
194000
195875
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
194001
195876
|
writeReport: (report2) => {
|
|
194002
195877
|
const normalized = normalizeHarnessReport(report2);
|
|
@@ -194330,7 +196205,7 @@ async function runPromptConcernAnalysis(context, rootDir, scanPlan, tenant) {
|
|
|
194330
196205
|
const manifest = join10(rootDir, ".moda/prompts.yml");
|
|
194331
196206
|
let existing = [];
|
|
194332
196207
|
if (existsSync10(manifest)) {
|
|
194333
|
-
const document = $parseDocument(
|
|
196208
|
+
const document = $parseDocument(readFileSync16(manifest, "utf8"));
|
|
194334
196209
|
if (document.errors.length)
|
|
194335
196210
|
throw new CliInputError("Invalid prompt manifest.", document.errors[0].message);
|
|
194336
196211
|
existing = document.toJS({ maxAliasCount: 20 })?.sources ?? [];
|
|
@@ -194801,7 +196676,7 @@ function repairCitationLines(report, rootDir) {
|
|
|
194801
196676
|
continue;
|
|
194802
196677
|
let text;
|
|
194803
196678
|
try {
|
|
194804
|
-
text =
|
|
196679
|
+
text = readFileSync16(candidatePath, "utf8");
|
|
194805
196680
|
} catch {
|
|
194806
196681
|
continue;
|
|
194807
196682
|
}
|
|
@@ -196018,7 +197893,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
|
|
|
196018
197893
|
let text = "";
|
|
196019
197894
|
if (isTextFile(path)) {
|
|
196020
197895
|
try {
|
|
196021
|
-
text =
|
|
197896
|
+
text = readFileSync16(current, "utf8");
|
|
196022
197897
|
inventoryTextBytes += Buffer.byteLength(text, "utf8");
|
|
196023
197898
|
} catch {
|
|
196024
197899
|
return;
|
|
@@ -196258,7 +198133,7 @@ function readHarnessSyncState(rootDir) {
|
|
|
196258
198133
|
if (!existsSync10(path))
|
|
196259
198134
|
return;
|
|
196260
198135
|
try {
|
|
196261
|
-
const parsed = JSON.parse(
|
|
198136
|
+
const parsed = JSON.parse(readFileSync16(path, "utf8"));
|
|
196262
198137
|
if (!isRecord4(parsed) || parsed.schemaVersion !== "sync_state.v0.1") {
|
|
196263
198138
|
return;
|
|
196264
198139
|
}
|
|
@@ -196392,7 +198267,7 @@ function warnFresherSubdirHarnessReports(context, rootDir, explicitReport) {
|
|
|
196392
198267
|
writeHarnessNotice(context, `A fresher harness report exists at ${newest.path} (this directory has ${rootState}). If you meant that report, re-run with --path=${subdir}.`);
|
|
196393
198268
|
}
|
|
196394
198269
|
function readHarnessGraph(manifestPath) {
|
|
196395
|
-
return JSON.parse(
|
|
198270
|
+
return JSON.parse(readFileSync16(manifestPath, "utf8"));
|
|
196396
198271
|
}
|
|
196397
198272
|
function writeHarnessArtifacts(graph, rootDir, options = {}) {
|
|
196398
198273
|
const modaDir = join10(rootDir, MODA_DIR);
|
|
@@ -196631,7 +198506,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
|
|
|
196631
198506
|
recordSkipped(skipped, { path: rel2, reason: "scan text budget exceeded" });
|
|
196632
198507
|
} else {
|
|
196633
198508
|
try {
|
|
196634
|
-
item.text =
|
|
198509
|
+
item.text = readFileSync16(current, "utf8");
|
|
196635
198510
|
textBytes += Buffer.byteLength(item.text, "utf8");
|
|
196636
198511
|
} catch {
|
|
196637
198512
|
recordSkipped(skipped, { path: rel2, reason: "text read failed" });
|
|
@@ -196938,7 +198813,7 @@ function maybeAddPromptArtifact(graph, file) {
|
|
|
196938
198813
|
if (!isPrompt)
|
|
196939
198814
|
return;
|
|
196940
198815
|
const id = stableId2("prompt", file.path);
|
|
196941
|
-
const text = file.text ??
|
|
198816
|
+
const text = file.text ?? readFileSync16(file.absPath, "utf8");
|
|
196942
198817
|
upsertArtifact(graph, {
|
|
196943
198818
|
id,
|
|
196944
198819
|
type: "prompt",
|
|
@@ -197519,7 +199394,7 @@ function detectProviderPackagesInManifests(rootDir) {
|
|
|
197519
199394
|
const packageJsonPath = join10(rootDir, "package.json");
|
|
197520
199395
|
if (existsSync10(packageJsonPath)) {
|
|
197521
199396
|
try {
|
|
197522
|
-
const deps = packageDependencyNames(
|
|
199397
|
+
const deps = packageDependencyNames(readFileSync16(packageJsonPath, "utf8"));
|
|
197523
199398
|
for (const provider of PROVIDER_CATALOG) {
|
|
197524
199399
|
for (const name of provider.packageNames) {
|
|
197525
199400
|
if (deps.has(name))
|
|
@@ -197531,7 +199406,7 @@ function detectProviderPackagesInManifests(rootDir) {
|
|
|
197531
199406
|
const pyprojectPath = join10(rootDir, "pyproject.toml");
|
|
197532
199407
|
if (existsSync10(pyprojectPath)) {
|
|
197533
199408
|
try {
|
|
197534
|
-
const deps = pyprojectDependencyNames(
|
|
199409
|
+
const deps = pyprojectDependencyNames(readFileSync16(pyprojectPath, "utf8"));
|
|
197535
199410
|
for (const provider of PROVIDER_CATALOG) {
|
|
197536
199411
|
for (const name of provider.packageNames) {
|
|
197537
199412
|
if (deps.has(name.toLowerCase()))
|
|
@@ -197948,7 +199823,7 @@ function readSmallTextFileForHash(absPath, rootDir) {
|
|
|
197948
199823
|
if (!isTextFile(rel2))
|
|
197949
199824
|
return;
|
|
197950
199825
|
try {
|
|
197951
|
-
return
|
|
199826
|
+
return readFileSync16(absPath, "utf8");
|
|
197952
199827
|
} catch {
|
|
197953
199828
|
return;
|
|
197954
199829
|
}
|