@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.
Files changed (31) hide show
  1. package/README.md +12 -0
  2. package/dist/{agent-rd9deae9.js → agent-a2xwfeeh.js} +9 -9
  3. package/dist/{cli-ryd7nzke.js → cli-2ars875d.js} +2 -2
  4. package/dist/{cli-bfmn3bsk.js → cli-3gwbg0xp.js} +2 -2
  5. package/dist/{cli-4qe6n8am.js → cli-438g4nwh.js} +122 -2
  6. package/dist/{cli-k7vwt56h.js → cli-53mvsfgn.js} +3 -3
  7. package/dist/{cli-ssqb8bfw.js → cli-6ca1sz0a.js} +1 -1
  8. package/dist/{cli-m0fa54ny.js → cli-7fc44hhq.js} +2258 -383
  9. package/dist/{cli-90hb4ng0.js → cli-86q35fpq.js} +8 -8
  10. package/dist/{cli-09rr5smg.js → cli-atx672xv.js} +1 -1
  11. package/dist/{cli-kwz900s4.js → cli-b3cxcxgn.js} +5 -4
  12. package/dist/{cli-4x9w6ey7.js → cli-kf3d6y8c.js} +1 -1
  13. package/dist/{cli-34zdvcpg.js → cli-xy6zn80e.js} +5 -5
  14. package/dist/{cli-y8gbbe9k.js → cli-zdeywyn9.js} +4 -4
  15. package/dist/cli.js +299 -94
  16. package/dist/{doctor-8yb6r66e.js → doctor-e2xrbpmp.js} +9 -9
  17. package/dist/{fix-bt5zn07e.js → fix-v8xka487.js} +7 -7
  18. package/dist/{harness-9psndqtm.js → harness-28wbzncq.js} +6 -6
  19. package/dist/{harness-github-actions-nph5t2s7.js → harness-github-actions-301nak95.js} +6 -6
  20. package/dist/{index-qjaprhrv.js → index-ccdpy9ma.js} +11 -11
  21. package/dist/{production-intelligence-mg3qp2dg.js → production-intelligence-0wqcvvxk.js} +6 -5
  22. package/dist/{profile-commands-2g5bjzj2.js → profile-commands-f5kysjrv.js} +3 -3
  23. package/dist/prompt-source-mcp.js +6 -6
  24. package/dist/{prompts-mzazn8ce.js → prompts-jg0stjfb.js} +7 -7
  25. package/dist/{provision-rm0yc08f.js → provision-25bw5rd7.js} +4 -4
  26. package/dist/{registry-mbz65bjy.js → registry-39xzetsc.js} +4 -4
  27. package/dist/{skills-sagxq9sc.js → skills-gjesa81c.js} +6 -6
  28. package/dist/{sync-gkwexz68.js → sync-7ts9psky.js} +8 -8
  29. package/package.json +1 -1
  30. package/skills/integration/index.json +2 -2
  31. 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-4x9w6ey7.js";
7
+ } from "./cli-kf3d6y8c.js";
8
8
  import {
9
9
  assertFlags,
10
10
  assertPositionals,
11
11
  requireTenant
12
- } from "./cli-k7vwt56h.js";
12
+ } from "./cli-53mvsfgn.js";
13
13
  import {
14
14
  ApiError,
15
15
  callControlAPI
16
- } from "./cli-ryd7nzke.js";
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-kwz900s4.js";
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: () => 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 ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
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(ignoredPaths, (searchPath) => isInPath(path, searchPath)) || isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames2, getCurrentDirectory);
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(ignoredPaths, (p2) => path.includes(p2));
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, mask2) {
75262
- return getTypeFactsWorker(type, mask2) & mask2;
75261
+ function getTypeFacts(type, mask22) {
75262
+ return getTypeFactsWorker(type, mask22) & mask22;
75263
75263
  }
75264
- function hasTypeFacts(type, mask2) {
75265
- return getTypeFacts(type, mask2) !== 0;
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 mask2 = 131071 ^ (1 | 1024 | 16);
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
- mask2 ^= 128;
110611
+ mask22 ^= 128;
110612
110612
  additions = 0;
110613
110613
  }
110614
- return maskModifierFlags(node, mask2, additions);
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(ignoredPaths, (searchPath) => path.includes(searchPath)) ? undefined : path;
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 mask2 = 0;
163649
+ let mask22 = 0;
163650
163650
  if (ruleAction & 1) {
163651
- mask2 |= 28;
163651
+ mask22 |= 28;
163652
163652
  }
163653
163653
  if (ruleAction & 2) {
163654
- mask2 |= 96;
163654
+ mask22 |= 96;
163655
163655
  }
163656
163656
  if (ruleAction & 28) {
163657
- mask2 |= 28;
163657
+ mask22 |= 28;
163658
163658
  }
163659
163659
  if (ruleAction & 96) {
163660
- mask2 |= 96;
163660
+ mask22 |= 96;
163661
163661
  }
163662
- return mask2;
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 mask = 31;
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 & mask;
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 & mask) + 1;
163734
- Debug.assert((value & mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
163735
- return indexBitmap & ~(mask << maskPosition) | value << maskPosition;
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: () => 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 readFileSync17,
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 readFileSync7, rmSync as rmSync3 } from "node:fs";
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, readFileSync as readFileSync6, realpathSync as realpathSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
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
- let m = /^\s*from\s+([A-Za-z_][\w.]*)\s+import\s+(.+)/.exec(line);
180741
- if (m) {
180742
- out.push({ spec: m[1], line: i + 1 });
180743
- let list = m[2].replace(/#.*$/, "");
180744
- for (let j = i + 1;j < lines.length && (list.includes("(") && !list.includes(")") || /\\\s*$/.test(list)); j++)
180745
- list += ` ${lines[j].replace(/#.*$/, "")}`;
180746
- const names = list.replace(/[()\\]/g, "").split(",");
180747
- for (const raw of names) {
180748
- const name = raw.trim().split(/\s+as\s+/)[0];
180749
- if (/^[A-Za-z_]\w*$/.test(name))
180750
- out.push({ spec: `${m[1]}.${name}`, line: i + 1 });
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
- continue;
180753
- }
180754
- m = /^\s*import\s+([A-Za-z_][\w.]*(?:\s+as\s+\w+)?(?:\s*,\s*[A-Za-z_][\w.]*(?:\s+as\s+\w+)?)*)/.exec(line);
180755
- if (m) {
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.gcs", "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"] }),
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.ml_apis", "http.request", "saas", { npm: ["@google-cloud/vision", "@google-cloud/language", "@google-cloud/translate", "@google-cloud/speech", "@google-cloud/text-to-speech", "@google-cloud/video-intelligence", "@google-cloud/documentai", "@google-cloud/dialogflow", "@google-cloud/dlp"], py: ["google.cloud.vision", "google.cloud.language", "google.cloud.translate", "google.cloud.translate_v2", "google.cloud.translate_v3", "google.cloud.speech", "google.cloud.texttospeech", "google.cloud.videointelligence", "google.cloud.documentai", "google.cloud.dialogflow"], hosts: ["vision.googleapis.com", "translation.googleapis.com", "language.googleapis.com", "speech.googleapis.com", "texttospeech.googleapis.com", "documentai.googleapis.com"] }),
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", "google.oauth2", "google.auth"], 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*"] }),
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("telemetry.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_*"] }),
181034
- p("telemetry.langsmith", "other", "saas", { npm: ["langsmith"], py: ["langsmith"], hosts: ["api.smith.langchain.com"], env: ["LANGSMITH_*", "LANGCHAIN_API_KEY", "LANGCHAIN_TRACING*", "LANGCHAIN_ENDPOINT", "LANGCHAIN_PROJECT"] }),
181035
- p("telemetry.langfuse", "other", "saas", { npm: ["langfuse", "@langfuse/tracing", "@langfuse/otel", "langfuse-vercel"], py: ["langfuse"], hosts: ["cloud.langfuse.com", "us.cloud.langfuse.com"], env: ["LANGFUSE_*"] }),
181036
- p("telemetry.posthog", "other", "saas", { npm: ["posthog-node", "posthog-js"], py: ["posthog"], hosts: [".posthog.com"], env: ["POSTHOG_*", "*_POSTHOG_*"] }),
181037
- p("telemetry.datadog", "other", "saas", { npm: ["dd-trace"], py: ["ddtrace", "datadog"], hosts: [".datadoghq.com", ".datadoghq.eu"], env: ["DD_API_KEY", "DD_SITE", "DATADOG_*"] }),
181038
- p("telemetry.braintrust", "other", "saas", { npm: ["braintrust"], py: ["braintrust"], hosts: ["api.braintrust.dev"], env: ["BRAINTRUST_*"] }),
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|local\.example|development\.example)|\.env-(?:example|sample)|example\.env|sample\.env|env\.example|\.dev\.vars\.example|\.dev\.vars\.sample)$/;
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 (linked.length > 0)
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 (ctx.byPath.has(p2) || ctx.files.some((f) => isUnder(f.path, p2)))
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
- const outDir = joinPath(dir, out2);
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.tool === "django" && b.paths.size === 0)
182510
+ if ([...b.paths, ...b.schemaFiles].every(testOnly))
182284
182511
  continue;
182285
- out.push({ engine: b.engine, tool: b.tool, paths: uniqSorted(b.paths), schemaFiles: uniqSorted(b.schemaFiles) });
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
- runtime.entrypoints.push({ name: id, handler: handlerRef, trigger: "event" });
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: `AWS::RDS::${kind.replace("Database", "DB")}` });
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
- runtime.bindings.push(resource ? { name, type, resource } : { name, type });
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 && !workerNames.has(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
- const serviceRoot = build !== undefined ? joinPath(base, typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".") : undefined;
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 root = joinPath(base, context);
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
- if (svcEntrypoint || svcCommand || ports.length > 0) {
184182
- const key = `container:${base}`;
184183
- const runtime = byRoot.get(key) ?? newRuntime("container", base, [], [], 0.75);
184184
- runtime.configFiles.push(file.path);
184185
- runtime.evidence.push(evidence);
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 out = new Set;
184253
- for (const f of files)
184254
- if (ROOT_MARKERS.has(basename2(f.path)))
184255
- out.add(dirname2(f.path));
184256
- return [...out].sort();
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
- httpTrigger: { trigger: "http" },
184499
- timerTrigger: { trigger: "cron" },
184500
- queueTrigger: { trigger: "queue", dep: { capability: "queue", provider: "azure.queue", cls: "emulated" } },
184501
- queue: { dep: { capability: "queue", provider: "azure.queue", cls: "emulated" } },
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
- cosmosDBTrigger: { trigger: "event", dep: { capability: "document", provider: "azure.cosmos", cls: "emulated" } },
184506
- cosmosDB: { dep: { capability: "document", provider: "azure.cosmos", cls: "emulated" } },
184507
- serviceBusTrigger: { trigger: "queue", dep: { capability: "queue", provider: "azure.servicebus", cls: "emulated" } },
184508
- serviceBus: { dep: { capability: "queue", provider: "azure.servicebus", cls: "emulated" } },
184509
- eventHubTrigger: { trigger: "queue", dep: { capability: "queue", provider: "azure.eventhubs", cls: "emulated" } },
184510
- eventHub: { dep: { capability: "queue", provider: "azure.eventhubs", cls: "emulated" } },
184511
- eventGridTrigger: { trigger: "event" },
184512
- eventGrid: { dep: { capability: "queue", provider: "azure.eventgrid", cls: "saas" } },
184513
- sql: { dep: { capability: "database", provider: "mssql", cls: "engine" } },
184514
- sqlTrigger: { trigger: "event", dep: { capability: "database", provider: "mssql", cls: "engine" } }
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 NODE_REGISTRATIONS = {
184543
- http: "httpTrigger",
184544
- get: "httpTrigger",
184545
- post: "httpTrigger",
184546
- put: "httpTrigger",
184547
- patch: "httpTrigger",
184548
- deleteRequest: "httpTrigger",
184549
- timer: "timerTrigger",
184550
- storageQueue: "queueTrigger",
184551
- storageBlob: "blobTrigger",
184552
- cosmosDB: "cosmosDBTrigger",
184553
- serviceBusQueue: "serviceBusTrigger",
184554
- serviceBusTopic: "serviceBusTrigger",
184555
- eventHub: "eventHubTrigger",
184556
- eventGrid: "eventGridTrigger",
184557
- sql: "sqlTrigger"
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: "cosmosDB",
184564
- serviceBusQueue: "serviceBus",
184565
- serviceBusTopic: "serviceBus",
184566
- eventHub: "eventHub",
184567
- eventGrid: "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 type = asString(b.type) ?? "";
184636
- const spec = BINDINGS[type];
184637
- if (spec?.trigger)
184638
- trigger = spec.trigger;
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.startsWith("blob") || type.startsWith("queue") ? "AzureWebJobsStorage" : undefined), evidenceAt(file.path, file.text, findLine(file.text, new RegExp(`"${escapeRe(type)}"`)), DETECTOR8));
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
- deco[2] = [deco[2], ...lines.slice(start + 1, i + 1)].join(`
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(deco[2])?.[1];
184673
- const type = PY_DECORATORS[kind];
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
- if (BINDINGS[type]?.trigger)
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(deco[2])?.[1];
184679
- const connection = /connection\s*=\s*['"]([^'"]+)['"]/.exec(deco[2])?.[1];
184680
- addDep(type, kind, connection ?? (type.startsWith("blob") || type.startsWith("queue") ? "AzureWebJobsStorage" : undefined), { path: file.path, line: start + 1, excerpt: excerpt(line), detector: DETECTOR8 });
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+)/.exec(line);
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 = /\bapp\.(\w+)\(\s*['"]([^'"]+)['"]/g;
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 = NODE_REGISTRATIONS[m[1]];
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.startsWith("blob") || type.startsWith("queue") ? "AzureWebJobsStorage" : undefined), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
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
- addDep(type, m[1], connection ?? (type === "blob" || type === "queue" || type === "table" ? "AzureWebJobsStorage" : undefined), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
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 rt = upsert(map, "node_process", pkg.root, pkg.path);
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 rt = upsert(map, "node_process", nearestRoot(file.path, roots));
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(",") || "ANY"} ${path}`, handler: f.path, trigger: "http" });
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: loaded.files,
185204
- byPath: new Map(loaded.files.map((f) => [f.path, f])),
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
- for (const ref of ctx.manifest?.env ?? []) {
185215
- for (const source of ref.sources.length > 0 ? ref.sources : ["manifest.json"])
185216
- ctx.env.push({ name: ref.name, source });
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 declared = collectDeclaredPackages(ctx.files);
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 ENV_EXAMPLE_SUFFIXES = new Set(["example", "sample", "template", "dist", "defaults.example", "tpl"]);
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
- for (const prefix of [".env.", ".dev.vars.", "env."]) {
185395
- if (lower.startsWith(prefix) && ENV_EXAMPLE_SUFFIXES.has(lower.slice(prefix.length)))
185396
- return true;
185397
- }
185398
- return /^(example|sample|template)\.env$/.test(lower) || /\.env\.(example|sample|template)$/.test(lower);
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 (lower === "credentials.json" || lower === "credentials" || lower === "credentials.yml" || lower === "credentials.yaml") {
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
- function registryAuthRule(path, text) {
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
- if (name === ".npmrc") {
185460
- for (const match of text.matchAll(/(?:_authToken|_auth|_password)\s*=\s*(.*)/g)) {
185461
- if (literal(match[1] ?? ""))
185462
- return "npmrc_auth_token";
185463
- }
185464
- }
185465
- if (name === ".yarnrc.yml" || name === ".yarnrc") {
185466
- for (const match of text.matchAll(/npmAuth(?:Token|Ident)\s*:\s*(.*)/g)) {
185467
- if (literal(match[1] ?? ""))
185468
- return "yarnrc_auth_token";
185469
- }
185470
- }
185471
- if (name === ".pypirc") {
185472
- for (const match of text.matchAll(/^\s*password\s*[=:]\s*(.*)$/gm)) {
185473
- if (literal(match[1] ?? ""))
185474
- return "pypirc_password";
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
- if (/https?:\/\/[^\s/:@]+:[^\s/@$]+@/.test(text))
185479
- return "registry_url_credentials";
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
- if (name === "settings.xml" && /<password>(?!\s*\$\{)[^<]+<\/password>/.test(text))
185482
- return "maven_settings_password";
185483
- return null;
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
- const value = stripInlineValue(m[2] ?? "");
186230
- this.add(m[1], `${path}:${index + 1}`, value !== "");
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/@'"]+):([^@\s/'"]{3,})@([^/\s'"?#]+)/g,
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, match) => {
186961
- const host = match[3] ?? "";
187975
+ accept: (password) => {
186962
187976
  if (WEAK_PASSWORDS.has(password.toLowerCase()))
186963
187977
  return false;
186964
- if (looksLikePlaceholder(password) || /^[$%:]/.test(password))
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_-]*)["']?\s*[:=]\s*(["'`])([A-Za-z0-9+/=_\-.~]{16,})\2/g,
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: /^\s*(?:export\s+)?([A-Za-z][A-Za-z0-9_-]*)\s*[:=]\s*([A-Za-z0-9+/=_\-.~]{16,})\s*(?:#.*)?$/gm,
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 = lsFiles(dir, ["--others", "--ignored", "--exclude-standard", "--directory", "--no-empty-directory"]).sort().map(withPrefix);
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
- if (pathRule) {
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
- const finding = contentSecretFinding(path, buf, text, categories);
188110
- if (finding) {
188111
- this.excludeFile(path, finding.reason, finding.rule, buf.length);
188112
- return;
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: buf.length,
188117
- sha256: sha256(buf),
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(({ absolutePath: _absolutePath, linkTargetOnDisk: _linkTargetOnDisk, ...file }) => file);
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
- var MAX_ENV_RESCAN_BYTES = 2 * 1024 * 1024;
188717
- function redactEnvExamples(result, dir) {
188718
- const byPath = new Map(result.manifest.files.map((file) => [file.path, file]));
188719
- let redacted = false;
188720
- for (const file of result.files) {
188721
- if (!file.categories.includes("env_example") || file.symlinkTarget !== undefined)
188722
- continue;
188723
- redacted = true;
188724
- const body = Buffer.from(envNamesOnly(readFileSync6(file.absolutePath, "utf8")));
188725
- const target = join7(dir, file.path);
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
- names.push(match[1]);
188759
- const quote = match[2][0];
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(readFileSync7(join8(root, "package.json"), "utf8")).name;
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 readFileSync8, realpathSync as realpathSync5 } from "node:fs";
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 readFileSync8(absPath, "utf8");
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 tripleQuoted = /("""|''')([\s\S]*?)\1/g;
191732
+ const tripleQuoted2 = /("""|''')([\s\S]*?)\1/g;
189858
191733
  let match;
189859
- while ((match = tripleQuoted.exec(file.text)) !== null) {
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 readFileSync11, realpathSync as realpathSync8 } from "node:fs";
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 readFileSync9, realpathSync as realpathSync6 } from "node:fs";
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 = readFileSync9(path, "utf8");
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 readFileSync10, realpathSync as realpathSync7 } from "node:fs";
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 = readFileSync10(sourcePath, "utf8");
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(readFileSync11(sourceAbs, "utf8"), sourcePath, ext);
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 = readFileSync11(sourceAbs, "utf8");
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(readFileSync11(depAbs, "utf8"), dependency.path, extname3(depAbs).toLowerCase());
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(readFileSync11(path)) }));
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(readFileSync11(file, "utf8"), sourcePath, ext);
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 readFileSync11(file, "utf8").matchAll(/^(?:(?:async\s+)?def\s+([A-Za-z_]\w*)|([A-Za-z_]\w*)\s*=\s*([^\n#]+))/gm)) {
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, readFileSync11(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TSX);
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 readFileSync15 } from "node:fs";
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 readFileSync14 } from "node:fs";
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 readFileSync12 } from "node:fs";
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: readFileSync12(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
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 readFileSync13 } from "node:fs";
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 = readFileSync13(file, "utf8");
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 = readFileSync14(safePromptEvidencePath(rootDir, item.path), "utf8");
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 = readFileSync15(safePromptEvidencePath(rootDir, source.path), "utf8");
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() || readFileSync15(safePromptEvidencePath(rootDir, source.path), "utf8") !== sourceBytes)
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 readFileSync16 } from "node:fs";
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 = readFileSync16(resolve12(rootDir, path), "utf8").split(/\r?\n/);
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.3",
192610
- locked_on: "2026-10-14",
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.3",
194487
+ source: "Branding project docs/positioning.md v1.0.4",
192613
194488
  canonical_strings: {
192614
- version: "1.0.3",
192615
- locked_on: "2026-10-14",
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: "Moda gives engineers the infrastructure to monitor AI agents in production, find the mistakes they keep repeating, and test every change against real traces before it ships.",
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. Find the mistakes your agents keep repeating and test every change against real traces before it ships.",
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: "Moda gives engineers the infrastructure to monitor AI agents in production, find the mistakes they keep repeating, and test every change against real traces before it ships. 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.",
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.3",
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.3",
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-nph5t2s7.js");
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(readFileSync17(manifest, "utf8"));
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 = readFileSync17(candidatePath, "utf8");
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 = readFileSync17(current, "utf8");
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(readFileSync17(path, "utf8"));
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(readFileSync17(manifestPath, "utf8"));
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 = readFileSync17(current, "utf8");
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 ?? readFileSync17(file.absPath, "utf8");
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(readFileSync17(packageJsonPath, "utf8"));
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(readFileSync17(pyprojectPath, "utf8"));
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 readFileSync17(absPath, "utf8");
199826
+ return readFileSync16(absPath, "utf8");
197952
199827
  } catch {
197953
199828
  return;
197954
199829
  }