@moda-ai/cli 1.55.14 → 1.55.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/{agent-y47qgmf1.js → agent-a2xwfeeh.js} +9 -9
- package/dist/{cli-ryd7nzke.js → cli-2ars875d.js} +2 -2
- package/dist/{cli-bfmn3bsk.js → cli-3gwbg0xp.js} +2 -2
- package/dist/{cli-4qe6n8am.js → cli-438g4nwh.js} +122 -2
- package/dist/{cli-k7vwt56h.js → cli-53mvsfgn.js} +3 -3
- package/dist/{cli-ssqb8bfw.js → cli-6ca1sz0a.js} +1 -1
- package/dist/{cli-k9nae6t9.js → cli-7fc44hhq.js} +1123 -187
- package/dist/{cli-ndn5m6wn.js → cli-86q35fpq.js} +8 -8
- package/dist/{cli-09rr5smg.js → cli-atx672xv.js} +1 -1
- package/dist/{cli-kwz900s4.js → cli-b3cxcxgn.js} +5 -4
- package/dist/{cli-4x9w6ey7.js → cli-kf3d6y8c.js} +1 -1
- package/dist/{cli-weqp7xc1.js → cli-xy6zn80e.js} +5 -5
- package/dist/{cli-y8gbbe9k.js → cli-zdeywyn9.js} +4 -4
- package/dist/cli.js +299 -94
- package/dist/{doctor-grvx6xx0.js → doctor-e2xrbpmp.js} +9 -9
- package/dist/{fix-91sr76gw.js → fix-v8xka487.js} +7 -7
- package/dist/{harness-vm0xd80p.js → harness-28wbzncq.js} +6 -6
- package/dist/{harness-github-actions-tmy3gqwa.js → harness-github-actions-301nak95.js} +6 -6
- package/dist/{index-f6cydfxg.js → index-ccdpy9ma.js} +11 -11
- package/dist/{production-intelligence-mg3qp2dg.js → production-intelligence-0wqcvvxk.js} +6 -5
- package/dist/{profile-commands-2g5bjzj2.js → profile-commands-f5kysjrv.js} +3 -3
- package/dist/prompt-source-mcp.js +6 -6
- package/dist/{prompts-kw5kzm39.js → prompts-jg0stjfb.js} +7 -7
- package/dist/{provision-rm0yc08f.js → provision-25bw5rd7.js} +4 -4
- package/dist/{registry-mbz65bjy.js → registry-39xzetsc.js} +4 -4
- package/dist/{skills-sagxq9sc.js → skills-gjesa81c.js} +6 -6
- package/dist/{sync-xv04wnth.js → sync-7ts9psky.js} +8 -8
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +31 -4
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
createCommandContext,
|
|
6
6
|
stringOption
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-kf3d6y8c.js";
|
|
8
8
|
import {
|
|
9
9
|
assertFlags,
|
|
10
10
|
assertPositionals,
|
|
11
11
|
requireTenant
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-53mvsfgn.js";
|
|
13
13
|
import {
|
|
14
14
|
ApiError,
|
|
15
15
|
callControlAPI
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-2ars875d.js";
|
|
17
17
|
import {
|
|
18
18
|
CliInputError,
|
|
19
19
|
CliInputRequiredError,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
resolveModaBaseUrl,
|
|
40
40
|
resolveTenantId,
|
|
41
41
|
shouldInventoryFile
|
|
42
|
-
} from "./cli-
|
|
42
|
+
} from "./cli-b3cxcxgn.js";
|
|
43
43
|
import {
|
|
44
44
|
__commonJS,
|
|
45
45
|
__require,
|
|
@@ -180406,6 +180406,22 @@ function isCommentLine(line) {
|
|
|
180406
180406
|
const t = line.trimStart();
|
|
180407
180407
|
return t.startsWith("//") || t.startsWith("#") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("--") || t.startsWith("<!--");
|
|
180408
180408
|
}
|
|
180409
|
+
function globRegex(glob) {
|
|
180410
|
+
let out = "";
|
|
180411
|
+
for (let i = 0;i < glob.length; i++) {
|
|
180412
|
+
const ch = glob[i];
|
|
180413
|
+
if (ch === "*" && glob[i + 1] === "*" && glob[i + 2] === "/") {
|
|
180414
|
+
out += "(?:.*/)?";
|
|
180415
|
+
i += 2;
|
|
180416
|
+
} else if (ch === "*")
|
|
180417
|
+
out += "[^/]*";
|
|
180418
|
+
else if (ch === "?")
|
|
180419
|
+
out += "[^/]";
|
|
180420
|
+
else
|
|
180421
|
+
out += ch.replace(/[.+^${}()|[\]\\]/, "\\$&");
|
|
180422
|
+
}
|
|
180423
|
+
return new RegExp(`^${out}$`);
|
|
180424
|
+
}
|
|
180409
180425
|
|
|
180410
180426
|
// ../../packages/harness-bundle/src/detect/files.ts
|
|
180411
180427
|
var DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
@@ -180727,6 +180743,7 @@ function pyStringStateAfter(line, open) {
|
|
|
180727
180743
|
}
|
|
180728
180744
|
return open;
|
|
180729
180745
|
}
|
|
180746
|
+
var PY_HEADER = /^(?:if|elif|else|try|except|finally|with|for|while|async\s+with|async\s+for)\b[^:]*:\s*/;
|
|
180730
180747
|
function pyImports(text) {
|
|
180731
180748
|
const out = [];
|
|
180732
180749
|
const lines = text.split(/\r?\n/);
|
|
@@ -180737,24 +180754,34 @@ function pyImports(text) {
|
|
|
180737
180754
|
inString = pyStringStateAfter(line, inString);
|
|
180738
180755
|
if (wasInString !== undefined)
|
|
180739
180756
|
continue;
|
|
180740
|
-
|
|
180741
|
-
|
|
180742
|
-
|
|
180743
|
-
|
|
180744
|
-
|
|
180745
|
-
|
|
180746
|
-
|
|
180747
|
-
|
|
180748
|
-
|
|
180749
|
-
|
|
180750
|
-
|
|
180757
|
+
const literals = [];
|
|
180758
|
+
const code = line.replace(/(?:\b[rRbBuUfF]{1,2})?(['"])((?:\\.|(?!\1).)*)\1/g, (_m, _q, body) => `\x00${literals.push(body) - 1}\x00`).replace(/#.*$/, "");
|
|
180759
|
+
for (const m of code.matchAll(/\b(?:importlib\.import_module|__import__)\(\s*\u0000(\d+)\u0000/g)) {
|
|
180760
|
+
const spec = literals[Number(m[1])];
|
|
180761
|
+
if (/^[A-Za-z_][\w.]*$/.test(spec))
|
|
180762
|
+
out.push({ spec, line: i + 1 });
|
|
180763
|
+
}
|
|
180764
|
+
for (let stmt of code.split(";")) {
|
|
180765
|
+
stmt = stmt.trim();
|
|
180766
|
+
for (let h = PY_HEADER.exec(stmt);h; h = PY_HEADER.exec(stmt))
|
|
180767
|
+
stmt = stmt.slice(h[0].length);
|
|
180768
|
+
let m = /^from\s+([A-Za-z_][\w.]*)\s+import\s+(.+)/.exec(stmt);
|
|
180769
|
+
if (m) {
|
|
180770
|
+
out.push({ spec: m[1], line: i + 1 });
|
|
180771
|
+
let list = m[2];
|
|
180772
|
+
for (let j = i + 1;j < lines.length && (list.includes("(") && !list.includes(")") || /\\\s*$/.test(list)); j++)
|
|
180773
|
+
list += ` ${lines[j].replace(/#.*$/, "")}`;
|
|
180774
|
+
for (const raw of list.replace(/[()\\]/g, "").split(",")) {
|
|
180775
|
+
const name = raw.trim().split(/\s+as\s+/)[0];
|
|
180776
|
+
if (/^[A-Za-z_]\w*$/.test(name))
|
|
180777
|
+
out.push({ spec: `${m[1]}.${name}`, line: i + 1 });
|
|
180778
|
+
}
|
|
180779
|
+
continue;
|
|
180751
180780
|
}
|
|
180752
|
-
|
|
180753
|
-
|
|
180754
|
-
|
|
180755
|
-
|
|
180756
|
-
for (const raw of m[1].split(","))
|
|
180757
|
-
out.push({ spec: raw.trim().split(/\s+as\s+/)[0], line: i + 1 });
|
|
180781
|
+
m = /^import\s+([A-Za-z_][\w.]*(?:\s+as\s+\w+)?(?:\s*,\s*[A-Za-z_][\w.]*(?:\s+as\s+\w+)?)*)/.exec(stmt);
|
|
180782
|
+
if (m)
|
|
180783
|
+
for (const raw of m[1].split(","))
|
|
180784
|
+
out.push({ spec: raw.trim().split(/\s+as\s+/)[0], line: i + 1 });
|
|
180758
180785
|
}
|
|
180759
180786
|
}
|
|
180760
180787
|
return out;
|
|
@@ -180873,7 +180900,7 @@ var PROVIDERS = [
|
|
|
180873
180900
|
p("libsql", "database", "engine", { npm: ["@libsql/client", "drizzle-orm/libsql", "@prisma/adapter-libsql"], py: ["libsql_client", "libsql_experimental", "libsql"], pypi: ["libsql-client", "libsql-experimental"], hosts: [".turso.io"], env: ["TURSO_*", "LIBSQL_*"], schemes: ["libsql"], images: ["ghcr.io/tursodatabase/libsql-server"] }),
|
|
180874
180901
|
p("mssql", "database", "engine", { npm: ["mssql", "tedious"], py: ["pyodbc", "pymssql"], go: ["github.com/microsoft/go-mssqldb", "github.com/denisenkom/go-mssqldb"], hosts: [".database.windows.net"], env: ["MSSQL_*"], schemes: ["mssql", "sqlserver"], images: ["mcr.microsoft.com/mssql/server", "mcr.microsoft.com/azure-sql-edge"] }),
|
|
180875
180902
|
p("cloudflare.d1", "database", "emulated", { npm: ["drizzle-orm/d1", "@prisma/adapter-d1"] }),
|
|
180876
|
-
p("redis", "kv", "engine", { npm: ["redis", "ioredis", "@redis/client", "@upstash/redis", "@vercel/kv"], py: ["redis", "aioredis", "upstash_redis", "valkey"], pypi: ["redis", "aioredis", "upstash-redis", "valkey"], go: ["github.com/redis/go-redis", "github.com/go-redis/redis", "github.com/gomodule/redigo"], gems: ["redis"], hosts: [".upstash.io", ".redis.cache.windows.net", ".redislabs.com", ".redis-cloud.com", ".cache.amazonaws.com"], env: ["REDIS_*", "UPSTASH_REDIS_*", "KV_REST_API_*", "KV_URL", "VALKEY_*", "*_REDIS_URL"], schemes: ["redis", "rediss", "valkey"], images: ["redis", "valkey/valkey", "redislabs/redismod", "redis/redis-stack", "redis/redis-stack-server", "bitnami/redis", "eqalpha/keydb", "docker.dragonflydb.io/dragonflydb/dragonfly"] }),
|
|
180903
|
+
p("redis", "kv", "engine", { npm: ["redis", "ioredis", "@redis/client", "@upstash/redis", "@vercel/kv", "bullmq", "bull", "bee-queue", "@nestjs/bull", "@nestjs/bullmq"], py: ["redis", "aioredis", "upstash_redis", "valkey"], pypi: ["redis", "aioredis", "upstash-redis", "valkey"], go: ["github.com/redis/go-redis", "github.com/go-redis/redis", "github.com/gomodule/redigo"], gems: ["redis"], hosts: [".upstash.io", ".redis.cache.windows.net", ".redislabs.com", ".redis-cloud.com", ".cache.amazonaws.com"], env: ["REDIS_*", "UPSTASH_REDIS_*", "KV_REST_API_*", "KV_URL", "VALKEY_*", "*_REDIS_URL"], schemes: ["redis", "rediss", "valkey"], images: ["redis", "valkey/valkey", "redislabs/redismod", "redis/redis-stack", "redis/redis-stack-server", "bitnami/redis", "eqalpha/keydb", "docker.dragonflydb.io/dragonflydb/dragonfly"] }),
|
|
180877
180904
|
p("memcached", "kv", "engine", { npm: ["memjs", "memcached"], py: ["pymemcache", "pylibmc"], env: ["MEMCACHE_*", "MEMCACHED_*"], schemes: ["memcached"], images: ["memcached"] }),
|
|
180878
180905
|
p("mongodb", "document", "engine", { npm: ["mongodb", "mongoose"], py: ["pymongo", "motor", "mongoengine", "beanie"], go: ["go.mongodb.org/mongo-driver"], gems: ["mongoid", "mongo"], hosts: [".mongodb.net"], env: ["MONGODB_*", "MONGO_*"], schemes: ["mongodb", "mongodb+srv"], images: ["mongo", "mongodb/mongodb-community-server", "bitnami/mongodb"] }),
|
|
180879
180906
|
p("clickhouse", "database", "engine", { npm: ["@clickhouse/client", "@clickhouse/client-web"], py: ["clickhouse_connect", "clickhouse_driver"], pypi: ["clickhouse-connect", "clickhouse-driver"], go: ["github.com/ClickHouse/clickhouse-go"], hosts: [".clickhouse.cloud"], env: ["CLICKHOUSE_*"], schemes: ["clickhouse"], images: ["clickhouse/clickhouse-server", "yandex/clickhouse-server"] }),
|
|
@@ -180887,7 +180914,7 @@ var PROVIDERS = [
|
|
|
180887
180914
|
p("snowflake", "database", "none", { npm: ["snowflake-sdk"], py: ["snowflake.connector", "snowflake.snowpark"], pypi: ["snowflake-connector-python"], hosts: [".snowflakecomputing.com"], env: ["SNOWFLAKE_*"] }),
|
|
180888
180915
|
p("aws.s3", "object_store", "emulated", { npm: ["@aws-sdk/client-s3", "@aws-sdk/lib-storage", "@aws-sdk/s3-request-presigner", "@aws-sdk/s3-presigned-post", "minio"], py: ["minio", "s3fs"], go: [...goAws("s3"), "github.com/minio/minio-go"], hosts: [/(^|\.)s3([.-][a-z0-9-]+)?\.amazonaws\.com$/], env: ["S3_*", "AWS_S3_*", "*_S3_BUCKET*", "MINIO_*"], schemes: ["s3"], images: ["minio/minio", "quay.io/minio/minio", "bitnami/minio"], code: [botoTracked("s3"), awsV2("S3")] }),
|
|
180889
180916
|
p("cloudflare.r2", "object_store", "emulated", { hosts: [".r2.cloudflarestorage.com", ".r2.dev"], env: ["R2_*", "*_R2_*"] }),
|
|
180890
|
-
p("gcp.
|
|
180917
|
+
p("gcp.storage", "object_store", "emulated", { npm: ["@google-cloud/storage", "firebase-admin/storage"], py: ["google.cloud.storage", "firebase_admin.storage"], pypi: ["google-cloud-storage"], go: ["cloud.google.com/go/storage"], hosts: ["storage.googleapis.com"], env: ["GCS_*", "*_GCS_*"], schemes: ["gs"], images: ["fsouza/fake-gcs-server"] }),
|
|
180891
180918
|
p("azure.blob", "object_store", "emulated", { npm: ["@azure/storage-blob"], py: ["azure.storage.blob"], pypi: ["azure-storage-blob"], go: ["github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"], hosts: [/\.blob\.core\.windows\.net$/], env: ["AZURE_STORAGE_*", "AzureWebJobsStorage"], images: ["mcr.microsoft.com/azure-storage/azurite"] }),
|
|
180892
180919
|
p("vercel.blob", "object_store", "saas", { npm: ["@vercel/blob"], hosts: [".blob.vercel-storage.com"], env: ["BLOB_READ_WRITE_TOKEN"] }),
|
|
180893
180920
|
p("uploadthing", "object_store", "saas", { npm: ["uploadthing", "@uploadthing/react"], env: ["UPLOADTHING_*"], hosts: ["api.uploadthing.com"] }),
|
|
@@ -180905,7 +180932,6 @@ var PROVIDERS = [
|
|
|
180905
180932
|
p("kafka", "queue", "engine", { npm: ["kafkajs", "node-rdkafka", "@confluentinc/kafka-javascript"], py: ["kafka", "confluent_kafka", "aiokafka"], pypi: ["kafka-python", "confluent-kafka", "aiokafka"], go: ["github.com/segmentio/kafka-go", "github.com/confluentinc/confluent-kafka-go", "github.com/IBM/sarama", "github.com/Shopify/sarama"], hosts: [".confluent.cloud"], env: ["KAFKA_*"], schemes: ["kafka"], images: ["confluentinc/cp-kafka", "bitnami/kafka", "apache/kafka", "wurstmeister/kafka", "redpandadata/redpanda", "docker.redpanda.com/redpandadata/redpanda", "vectorized/redpanda"] }),
|
|
180906
180933
|
p("rabbitmq", "queue", "engine", { npm: ["amqplib", "amqp-connection-manager"], py: ["pika", "aio_pika", "kombu"], pypi: ["pika", "aio-pika", "kombu"], go: ["github.com/rabbitmq/amqp091-go", "github.com/streadway/amqp"], hosts: [".cloudamqp.com"], env: ["RABBITMQ_*", "AMQP_*"], schemes: ["amqp", "amqps"], images: ["rabbitmq", "bitnami/rabbitmq"] }),
|
|
180907
180934
|
p("nats", "queue", "engine", { npm: ["nats"], py: ["nats"], pypi: ["nats-py"], go: ["github.com/nats-io/nats.go"], env: ["NATS_*"], schemes: ["nats"], images: ["nats"] }),
|
|
180908
|
-
p("bullmq", "queue", "engine", { npm: ["bullmq", "bull", "bee-queue"] }),
|
|
180909
180935
|
p("celery", "queue", "engine", { py: ["celery"], env: ["CELERY_*"] }),
|
|
180910
180936
|
p("inngest", "queue", "emulated", { npm: ["inngest"], py: ["inngest"], env: ["INNGEST_*"], hosts: ["api.inngest.com", "inn.gs"] }),
|
|
180911
180937
|
p("trigger.dev", "queue", "saas", { npm: ["@trigger.dev/sdk"], env: ["TRIGGER_*"], hosts: ["api.trigger.dev"] }),
|
|
@@ -180921,7 +180947,11 @@ var PROVIDERS = [
|
|
|
180921
180947
|
p("aws.bedrock_agentcore", "function.invoke", "none", { npm: ["bedrock-agentcore", "@aws-sdk/client-bedrock-agentcore", "@aws-sdk/client-bedrock-agentcore-control"], py: ["bedrock_agentcore"], pypi: ["bedrock-agentcore"], env: ["BEDROCK_AGENTCORE_*"], code: [botoTracked("bedrock-agentcore", "bedrock-agentcore-control")] }),
|
|
180922
180948
|
p("cloud.metadata", "other", "emulated", { hosts: ["metadata.google.internal", "169.254.169.254", "metadata"] }),
|
|
180923
180949
|
p("gcp.secretmanager", "other", "saas", { npm: ["@google-cloud/secret-manager"], py: ["google.cloud.secretmanager"], pypi: ["google-cloud-secret-manager"], hosts: ["secretmanager.googleapis.com"] }),
|
|
180924
|
-
p("gcp.
|
|
180950
|
+
p("gcp.vision", "http.request", "saas", { npm: ["@google-cloud/vision"], py: ["google.cloud.vision"], pypi: ["google-cloud-vision"], hosts: ["vision.googleapis.com"] }),
|
|
180951
|
+
p("gcp.translate", "http.request", "saas", { npm: ["@google-cloud/translate"], py: ["google.cloud.translate", "google.cloud.translate_v2", "google.cloud.translate_v3"], pypi: ["google-cloud-translate"], hosts: ["translation.googleapis.com"] }),
|
|
180952
|
+
p("gcp.speech", "http.request", "saas", { npm: ["@google-cloud/speech", "@google-cloud/text-to-speech"], py: ["google.cloud.speech", "google.cloud.texttospeech"], hosts: ["speech.googleapis.com", "texttospeech.googleapis.com"] }),
|
|
180953
|
+
p("gcp.language", "http.request", "saas", { npm: ["@google-cloud/language"], py: ["google.cloud.language"], hosts: ["language.googleapis.com"] }),
|
|
180954
|
+
p("gcp.documentai", "http.request", "saas", { npm: ["@google-cloud/documentai", "@google-cloud/video-intelligence", "@google-cloud/dialogflow", "@google-cloud/dlp"], py: ["google.cloud.documentai", "google.cloud.videointelligence", "google.cloud.dialogflow"], hosts: ["documentai.googleapis.com"] }),
|
|
180925
180955
|
p("cloudflare.api", "http.request", "saas", { npm: ["cloudflare"], py: ["cloudflare"], hosts: ["api.cloudflare.com"], env: ["CLOUDFLARE_API_TOKEN", "CF_API_TOKEN"] }),
|
|
180926
180956
|
p("pinecone", "vector_store", "emulated", { npm: ["@pinecone-database/pinecone", "@langchain/pinecone"], py: ["pinecone", "langchain_pinecone"], pypi: ["pinecone", "pinecone-client"], hosts: [".pinecone.io"], env: ["PINECONE_*"], images: ["ghcr.io/pinecone-io/pinecone-local"] }),
|
|
180927
180957
|
p("weaviate", "vector_store", "engine", { npm: ["weaviate-client", "weaviate-ts-client", "@langchain/weaviate"], py: ["weaviate"], pypi: ["weaviate-client"], hosts: [".weaviate.network", ".weaviate.cloud"], env: ["WEAVIATE_*"], images: ["semitechnologies/weaviate", "cr.weaviate.io/semitechnologies/weaviate"] }),
|
|
@@ -180943,7 +180973,7 @@ var PROVIDERS = [
|
|
|
180943
180973
|
p("anthropic", "model.inference", "model", { npm: ["@anthropic-ai/sdk", "@ai-sdk/anthropic", "@langchain/anthropic", "@anthropic-ai/claude-agent-sdk", "@anthropic-ai/claude-code", "@anthropic-ai/bedrock-sdk", "@anthropic-ai/vertex-sdk"], py: ["anthropic", "langchain_anthropic", "claude_agent_sdk", "claude_code_sdk", "llama_index.llms.anthropic"], pypi: ["langchain-anthropic", "claude-agent-sdk", "claude-code-sdk"], go: ["github.com/anthropics/anthropic-sdk-go"], hosts: ["api.anthropic.com"], env: ["ANTHROPIC_*"] }),
|
|
180944
180974
|
p("openrouter", "model.inference", "model", { npm: ["@openrouter/ai-sdk-provider", "@openrouter/sdk"], hosts: ["openrouter.ai"], env: ["OPENROUTER_*"] }),
|
|
180945
180975
|
p("google.genai", "model.inference", "model", { npm: ["@google/genai", "@google/generative-ai", "@ai-sdk/google", "@langchain/google-genai", "@genkit-ai/googleai", "@genkit-ai/google-genai"], py: ["google.genai", "google.generativeai", "langchain_google_genai"], pypi: ["google-genai", "google-generativeai", "langchain-google-genai"], go: ["google.golang.org/genai", "github.com/google/generative-ai-go"], hosts: ["generativelanguage.googleapis.com"], env: ["GEMINI_*", "GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_*", "GOOGLE_GENAI_*"] }),
|
|
180946
|
-
p("google.vertex", "model.inference", "model", { npm: ["@google-cloud/vertexai", "@ai-sdk/google-vertex", "@langchain/google-vertexai", "@genkit-ai/vertexai"], py: ["vertexai", "google.cloud.aiplatform", "langchain_google_vertexai"], pypi: ["google-cloud-aiplatform", "langchain-google-vertexai"], hosts: [/aiplatform\.googleapis\.com$/], env: ["VERTEX_*", "GOOGLE_VERTEX_*"] }),
|
|
180976
|
+
p("google.vertex", "model.inference", "model", { npm: ["@google-cloud/vertexai", "@ai-sdk/google-vertex", "@langchain/google-vertexai", "@genkit-ai/vertexai"], py: ["vertexai", "google.cloud.aiplatform", "langchain_google_vertexai", "pydantic_ai.models.vertexai"], pypi: ["google-cloud-aiplatform", "langchain-google-vertexai"], hosts: [/aiplatform\.googleapis\.com$/], env: ["VERTEX_*", "GOOGLE_VERTEX_*", "GOOGLE_GENAI_USE_VERTEXAI"], code: [/\bvertexai\.init\s*\(/, /\bClient\s*\([^)]*\bvertexai\s*=\s*True/, /\bnew\s+GoogleGenAI\s*\(\s*\{[^}]*\bvertexai\s*:\s*true/, /GOOGLE_GENAI_USE_VERTEXAI['"]?\s*\]?\s*=\s*['"]?(?:True|true|TRUE|1)\b/] }),
|
|
180947
180977
|
p("aws.bedrock", "model.inference", "model", { npm: ["@aws-sdk/client-bedrock-runtime", "@aws-sdk/client-bedrock-agent-runtime", "@ai-sdk/amazon-bedrock", "@langchain/aws"], py: ["langchain_aws"], pypi: ["langchain-aws"], go: goAws("bedrockruntime"), hosts: [/^bedrock(-runtime|-agent-runtime)?\.[a-z0-9-]+\.amazonaws\.com$/], env: ["BEDROCK_*", "AWS_BEDROCK_*"], code: [botoTracked("bedrock-runtime", "bedrock-agent-runtime", "bedrock")] }),
|
|
180948
180978
|
p("azure.openai", "model.inference", "model", { npm: ["@azure/openai", "@ai-sdk/azure"], py: ["azure.ai.inference", "azure.ai.openai"], hosts: [/\.openai\.azure\.com$/, /\.services\.ai\.azure\.com$/], env: ["AZURE_OPENAI_*", "AZURE_AI_*"], code: [/\bAzureOpenAI\s*\(/, /\bAzureChatOpenAI\s*\(/, /\bnew\s+AzureOpenAI\s*\(/] }),
|
|
180949
180979
|
p("groq", "model.inference", "model", { npm: ["groq-sdk", "@ai-sdk/groq", "@langchain/groq"], py: ["groq", "langchain_groq"], hosts: ["api.groq.com"], env: ["GROQ_*"] }),
|
|
@@ -180999,7 +181029,7 @@ var PROVIDERS = [
|
|
|
180999
181029
|
p("shopify", "http.request", "saas", { npm: ["@shopify/shopify-api", "@shopify/admin-api-client", "shopify-api-node"], py: ["shopify"], pypi: ["ShopifyAPI"], hosts: [".myshopify.com"], env: ["SHOPIFY_*"] }),
|
|
181000
181030
|
p("jira", "http.request", "saas", { npm: ["jira-client", "jira.js"], py: ["jira", "atlassian"], pypi: ["atlassian-python-api"], hosts: [".atlassian.net"], env: ["JIRA_*", "ATLASSIAN_*", "CONFLUENCE_*"] }),
|
|
181001
181031
|
p("airtable", "http.request", "saas", { npm: ["airtable"], py: ["pyairtable"], hosts: ["api.airtable.com"], env: ["AIRTABLE_*"] }),
|
|
181002
|
-
p("google.apis", "http.request", "saas", { npm: ["googleapis", "@googleapis/sheets", "@googleapis/gmail", "@googleapis/drive", "@googleapis/calendar"], py: ["googleapiclient", "google_auth_oauthlib"
|
|
181032
|
+
p("google.apis", "http.request", "saas", { npm: ["googleapis", "@googleapis/sheets", "@googleapis/gmail", "@googleapis/drive", "@googleapis/calendar"], py: ["googleapiclient", "google_auth_oauthlib"], pypi: ["google-api-python-client", "google-auth-oauthlib"], go: ["google.golang.org/api"], hosts: ["www.googleapis.com", "sheets.googleapis.com", "gmail.googleapis.com", "oauth2.googleapis.com", "accounts.google.com", ".googleapis.com"], env: ["GOOGLE_CLIENT_*", "GOOGLE_SERVICE_ACCOUNT*"] }),
|
|
181003
181033
|
p("google.maps", "http.request", "saas", { npm: ["@googlemaps/google-maps-services-js"], py: ["googlemaps"], hosts: ["maps.googleapis.com", "places.googleapis.com"], env: ["GOOGLE_MAPS_*"] }),
|
|
181004
181034
|
p("serpapi", "http.request", "saas", { npm: ["serpapi"], py: ["serpapi"], pypi: ["google-search-results"], hosts: ["serpapi.com"], env: ["SERPAPI_*", "SERP_API_*"] }),
|
|
181005
181035
|
p("serper", "http.request", "saas", { hosts: ["google.serper.dev"], env: ["SERPER_*"], code: [/\bSerperDevTool\s*\(/] }),
|
|
@@ -181025,22 +181055,52 @@ var PROVIDERS = [
|
|
|
181025
181055
|
p("arxiv", "http.request", "saas", { py: ["arxiv"], hosts: ["export.arxiv.org"], code: [/\bArxiv(?:Retriever|APIWrapper|QueryRun|Loader)\s*\(/] }),
|
|
181026
181056
|
p("linkup", "http.request", "saas", { npm: ["linkup-sdk"], py: ["linkup"], pypi: ["linkup-sdk"], hosts: ["api.linkup.so"], env: ["LINKUP_*"] }),
|
|
181027
181057
|
p("ncbi", "http.request", "saas", { hosts: ["eutils.ncbi.nlm.nih.gov", "pubmed.ncbi.nlm.nih.gov"], code: [/\bPubMed(?:APIWrapper|QueryRun|Retriever)\s*\(/], env: ["NCBI_*", "PUBMED_*"] }),
|
|
181058
|
+
p("mcp", "function.invoke", "saas", { npm: ["@modelcontextprotocol/sdk/client", "@ai-sdk/mcp", "@langchain/mcp-adapters"], py: ["mcp.client", "langchain_mcp_adapters", "fastmcp.client"], code: [/\baddMcpServer\s*\(/, /\bcreateSdkMcpServer\s*\(/, /\bexperimental_createMCPClient\s*\(|\bcreateMCPClient\s*\(/, /\bmcpServers\b/, /\bMultiServerMCPClient\s*\(/, /\bClientSession\s*\(\s*read/] }),
|
|
181028
181059
|
p("pusher", "http.request", "saas", { npm: ["pusher"], py: ["pusher"], hosts: [".pusher.com"], env: ["PUSHER_*"] }),
|
|
181029
181060
|
p("ably", "http.request", "saas", { npm: ["ably"], py: ["ably"], hosts: ["rest.ably.io", "realtime.ably.io"], env: ["ABLY_*"] }),
|
|
181030
181061
|
p("mapbox", "http.request", "saas", { hosts: ["api.mapbox.com"], env: ["MAPBOX_*", "*_MAPBOX_*"] }),
|
|
181031
181062
|
p("openweathermap", "http.request", "saas", { hosts: ["api.openweathermap.org"], env: ["OPENWEATHER*"] }),
|
|
181032
181063
|
p("open-meteo", "http.request", "saas", { hosts: ["api.open-meteo.com", "geocoding-api.open-meteo.com", "archive-api.open-meteo.com"] }),
|
|
181033
|
-
p("
|
|
181034
|
-
p("
|
|
181035
|
-
p("
|
|
181036
|
-
p("
|
|
181037
|
-
p("
|
|
181038
|
-
p("
|
|
181064
|
+
p("sentry", "other", "saas", { npm: ["@sentry/node", "@sentry/nextjs", "@sentry/cloudflare", "@sentry/serverless", "@sentry/aws-serverless", "@sentry/bun", "@sentry/react"], py: ["sentry_sdk"], pypi: ["sentry-sdk"], hosts: [".ingest.sentry.io", ".ingest.us.sentry.io"], env: ["SENTRY_*", "*_SENTRY_*"] }),
|
|
181065
|
+
p("langsmith", "other", "saas", { npm: ["langsmith"], py: ["langsmith"], hosts: ["api.smith.langchain.com"], env: ["LANGSMITH_*", "LANGCHAIN_API_KEY", "LANGCHAIN_TRACING*", "LANGCHAIN_ENDPOINT", "LANGCHAIN_PROJECT"] }),
|
|
181066
|
+
p("langfuse", "other", "saas", { npm: ["langfuse", "@langfuse/tracing", "@langfuse/otel", "langfuse-vercel"], py: ["langfuse"], hosts: ["cloud.langfuse.com", "us.cloud.langfuse.com"], env: ["LANGFUSE_*"] }),
|
|
181067
|
+
p("posthog", "other", "saas", { npm: ["posthog-node", "posthog-js"], py: ["posthog"], hosts: [".posthog.com"], env: ["POSTHOG_*", "*_POSTHOG_*"] }),
|
|
181068
|
+
p("datadog", "other", "saas", { npm: ["dd-trace"], py: ["ddtrace", "datadog"], hosts: [".datadoghq.com", ".datadoghq.eu"], env: ["DD_API_KEY", "DD_SITE", "DATADOG_*"] }),
|
|
181069
|
+
p("braintrust", "other", "saas", { npm: ["braintrust"], py: ["braintrust"], hosts: ["api.braintrust.dev"], env: ["BRAINTRUST_*"] }),
|
|
181039
181070
|
p("telemetry.gcp", "other", "saas", { npm: ["@google-cloud/logging", "@google-cloud/logging-winston", "@google-cloud/monitoring", "@google-cloud/opentelemetry-cloud-trace-exporter", "@google-cloud/trace-agent"], py: ["google.cloud.logging"], pypi: ["google-cloud-logging"] }),
|
|
181040
181071
|
p("telemetry.azure_monitor", "other", "saas", { npm: ["@azure/monitor-opentelemetry", "applicationinsights"], py: ["azure.monitor.opentelemetry", "opencensus.ext.azure"], pypi: ["azure-monitor-opentelemetry"], hosts: [".applicationinsights.azure.com", "dc.services.visualstudio.com"], env: ["APPLICATIONINSIGHTS_*"] }),
|
|
181041
|
-
p("telemetry.otlp", "other", "saas", { npm: ["@opentelemetry/exporter-trace-otlp-http", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/exporter-trace-otlp-grpc", "@vercel/otel"], py: ["opentelemetry.exporter.otlp"], pypi: ["opentelemetry-exporter-otlp"], env: ["OTEL_EXPORTER_OTLP_*"] }),
|
|
181042
181072
|
p("local_fs", "file", "engine", { code: [/\b(?:fs\.)?(?:writeFile|writeFileSync|appendFile|appendFileSync)\s*\(/, /\bBun\.write\s*\(/, /\bopen\([^)\n]*,\s*['"](?:w|a|wb|ab|w\+|a\+)['"]/, /\.write_(?:text|bytes)\s*\(/] })
|
|
181043
181073
|
];
|
|
181074
|
+
var SDK_DEFAULT_ENV = {
|
|
181075
|
+
"npm:openai": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_ORG_ID", "OPENAI_PROJECT_ID"],
|
|
181076
|
+
"pypi:openai": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_ORG_ID", "OPENAI_PROJECT_ID"],
|
|
181077
|
+
"npm:@openai/agents": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_DEFAULT_MODEL", "OPENAI_AGENTS_DISABLE_TRACING", "OPENAI_AGENTS_DONT_LOG_MODEL_DATA", "OPENAI_AGENTS_DONT_LOG_TOOL_DATA"],
|
|
181078
|
+
"pypi:openai-agents": ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_DEFAULT_MODEL", "OPENAI_AGENTS_DISABLE_SENSITIVE_DATA", "OPENAI_AGENTS_DONT_LOG_MODEL_DATA", "OPENAI_AGENTS_DONT_LOG_TOOL_DATA"],
|
|
181079
|
+
"npm:@anthropic-ai/sdk": ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"],
|
|
181080
|
+
"pypi:anthropic": ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"],
|
|
181081
|
+
"npm:@anthropic-ai/claude-agent-sdk": ["ANTHROPIC_API_KEY"],
|
|
181082
|
+
"pypi:claude-agent-sdk": ["ANTHROPIC_API_KEY"],
|
|
181083
|
+
"npm:@google/genai": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
181084
|
+
"pypi:google-genai": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
181085
|
+
"npm:groq-sdk": ["GROQ_API_KEY"],
|
|
181086
|
+
"pypi:groq": ["GROQ_API_KEY"],
|
|
181087
|
+
"npm:@mistralai/mistralai": ["MISTRAL_API_KEY"],
|
|
181088
|
+
"pypi:mistralai": ["MISTRAL_API_KEY"],
|
|
181089
|
+
"npm:cohere-ai": ["CO_API_KEY"],
|
|
181090
|
+
"pypi:cohere": ["CO_API_KEY"],
|
|
181091
|
+
"npm:together-ai": ["TOGETHER_API_KEY"],
|
|
181092
|
+
"pypi:together": ["TOGETHER_API_KEY"],
|
|
181093
|
+
"npm:replicate": ["REPLICATE_API_TOKEN"],
|
|
181094
|
+
"pypi:replicate": ["REPLICATE_API_TOKEN"],
|
|
181095
|
+
"npm:@pinecone-database/pinecone": ["PINECONE_API_KEY"],
|
|
181096
|
+
"pypi:pinecone": ["PINECONE_API_KEY"],
|
|
181097
|
+
"pypi:logfire": ["LOGFIRE_TOKEN"],
|
|
181098
|
+
"npm:@pydantic/logfire-node": ["LOGFIRE_TOKEN"],
|
|
181099
|
+
"npm:langsmith": ["LANGSMITH_API_KEY"],
|
|
181100
|
+
"pypi:langsmith": ["LANGSMITH_API_KEY"],
|
|
181101
|
+
"npm:@livekit/agents": ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"],
|
|
181102
|
+
"pypi:livekit-agents": ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"]
|
|
181103
|
+
};
|
|
181044
181104
|
var WORKERS_BINDINGS = {
|
|
181045
181105
|
d1_databases: { capability: "database", provider: "cloudflare.d1", cls: "emulated", type: "D1" },
|
|
181046
181106
|
kv_namespaces: { capability: "kv", provider: "cloudflare.kv", cls: "emulated", type: "KV" },
|
|
@@ -181075,8 +181135,42 @@ var CFN_RESOURCES = {
|
|
|
181075
181135
|
"AWS::ElastiCache::CacheCluster": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181076
181136
|
"AWS::ElastiCache::ReplicationGroup": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181077
181137
|
"AWS::ElastiCache::ServerlessCache": { capability: "kv", provider: "redis", cls: "engine" },
|
|
181078
|
-
"AWS::OpenSearchService::Domain": { capability: "search", provider: "opensearch", cls: "engine" }
|
|
181138
|
+
"AWS::OpenSearchService::Domain": { capability: "search", provider: "opensearch", cls: "engine" },
|
|
181139
|
+
"AWS::Cognito::UserPoolClient": { capability: "auth", provider: "aws.cognito", cls: "emulated" },
|
|
181140
|
+
"AWS::Cognito::IdentityPool": { capability: "auth", provider: "aws.cognito", cls: "emulated" },
|
|
181141
|
+
"AWS::Bedrock::Agent": { capability: "model.inference", provider: "aws.bedrock", cls: "model" },
|
|
181142
|
+
"AWS::Bedrock::KnowledgeBase": { capability: "vector_store", provider: "aws.bedrock", cls: "none" },
|
|
181143
|
+
"AWS::BedrockAgentCore::Runtime": { capability: "function.invoke", provider: "aws.bedrock_agentcore", cls: "none" },
|
|
181144
|
+
"AWS::BedrockAgentCore::Gateway": { capability: "function.invoke", provider: "aws.bedrock_agentcore", cls: "none" },
|
|
181145
|
+
"AWS::BedrockAgentCore::Memory": { capability: "document", provider: "aws.bedrock_agentcore", cls: "none" }
|
|
181146
|
+
};
|
|
181147
|
+
var CFN_SHORT = {
|
|
181148
|
+
dynamodb: "dynamodb",
|
|
181149
|
+
s3: "s3",
|
|
181150
|
+
sqs: "sqs",
|
|
181151
|
+
sns: "sns",
|
|
181152
|
+
events: "eventbridge",
|
|
181153
|
+
kinesis: "kinesis",
|
|
181154
|
+
stepfunctions: "stepfunctions",
|
|
181155
|
+
cognito: "cognito",
|
|
181156
|
+
secretsmanager: "secretsmanager",
|
|
181157
|
+
ssm: "ssm",
|
|
181158
|
+
elasticache: "elasticache",
|
|
181159
|
+
opensearchservice: "opensearch",
|
|
181160
|
+
rds: "rds"
|
|
181079
181161
|
};
|
|
181162
|
+
function cfnBindingType(type) {
|
|
181163
|
+
const [, service = "", resource = ""] = type.split("::");
|
|
181164
|
+
if (type === "AWS::Serverless::SimpleTable")
|
|
181165
|
+
return "dynamodb";
|
|
181166
|
+
if (type === "AWS::Serverless::StateMachine")
|
|
181167
|
+
return "stepfunctions";
|
|
181168
|
+
const svc = service.toLowerCase();
|
|
181169
|
+
if (svc === "bedrock" || svc === "bedrockagentcore") {
|
|
181170
|
+
return `${svc === "bedrock" ? "bedrock" : "agentcore"}_${resource.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase()}`;
|
|
181171
|
+
}
|
|
181172
|
+
return CFN_SHORT[svc] ?? svc;
|
|
181173
|
+
}
|
|
181080
181174
|
function rdsEngineProvider(engine) {
|
|
181081
181175
|
const e = (engine ?? "").toLowerCase();
|
|
181082
181176
|
if (e.includes("postgres"))
|
|
@@ -181573,6 +181667,9 @@ function scanSources(ctx, declaredPypi, workspaceNames) {
|
|
|
181573
181667
|
evidence: evidence.slice(0, 3)
|
|
181574
181668
|
};
|
|
181575
181669
|
ctx.gaps.push(gap);
|
|
181670
|
+
if (evidence.some((e) => !isTestPath(e.path))) {
|
|
181671
|
+
ctx.signals.push({ kind: "code", capability: "http.request", provider: "http", host: undefined, evidence: evidence[0] });
|
|
181672
|
+
}
|
|
181576
181673
|
}
|
|
181577
181674
|
return { frameworks };
|
|
181578
181675
|
}
|
|
@@ -181597,6 +181694,7 @@ var CODE_ENV_RES = [
|
|
|
181597
181694
|
/\bos\.environ\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
|
|
181598
181695
|
/\bos\.environ\.get\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
|
|
181599
181696
|
/\bos\.getenv\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
|
|
181697
|
+
/['"]([A-Z_][A-Z0-9_]*)['"]\s+(?:not\s+)?in\s+os\.environ\b/g,
|
|
181600
181698
|
/\bos\.Getenv\(\s*"([A-Za-z_][A-Za-z0-9_]*)"/g,
|
|
181601
181699
|
/\bos\.LookupEnv\(\s*"([A-Za-z_][A-Za-z0-9_]*)"/g,
|
|
181602
181700
|
/\bENV\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
|
|
@@ -181604,11 +181702,15 @@ var CODE_ENV_RES = [
|
|
|
181604
181702
|
/\benv\.(?:str|int|bool|url|db|list|float|json)\(\s*['"]([A-Z_][A-Z0-9_]*)['"]/g,
|
|
181605
181703
|
/\bconfig\(\s*['"]([A-Z_][A-Z0-9_]*)['"]/g
|
|
181606
181704
|
];
|
|
181607
|
-
var ENV_EXAMPLE_FILE = /^(?:\.env\.(?:example|sample|template|dist|defaults
|
|
181705
|
+
var ENV_EXAMPLE_FILE = /^(?:\.env\.(?:example|sample|template|dist|defaults?|local\.example|development\.example)|\.env-(?:example|sample)|example\.env|sample\.env|env\.example|\.dev\.vars\.example|\.dev\.vars\.sample)$/;
|
|
181608
181706
|
var ENV_SECRET_FILE = /^(?:\.env(?:\.[\w.-]+)?|\.dev\.vars)$/;
|
|
181609
181707
|
function isEnvExampleFile(path) {
|
|
181610
181708
|
return ENV_EXAMPLE_FILE.test(basename2(path));
|
|
181611
181709
|
}
|
|
181710
|
+
function isEnvFile(path) {
|
|
181711
|
+
const name = basename2(path);
|
|
181712
|
+
return (ENV_EXAMPLE_FILE.test(name) || ENV_SECRET_FILE.test(name)) && !/\.test\b/.test(name);
|
|
181713
|
+
}
|
|
181612
181714
|
function scanEnv(ctx) {
|
|
181613
181715
|
for (const file of ctx.files) {
|
|
181614
181716
|
const name = basename2(file.path);
|
|
@@ -181634,6 +181736,8 @@ function scanEnv(ctx) {
|
|
|
181634
181736
|
continue;
|
|
181635
181737
|
const at = lineIndex(file.text);
|
|
181636
181738
|
const lines = splitLines(file.text);
|
|
181739
|
+
if (file.path.endsWith(".py") && /\bBaseSettings\b/.test(file.text))
|
|
181740
|
+
settingsFields(ctx, file.path, lines);
|
|
181637
181741
|
const seen = new Set;
|
|
181638
181742
|
for (const re of CODE_ENV_RES) {
|
|
181639
181743
|
re.lastIndex = 0;
|
|
@@ -181655,6 +181759,63 @@ function scanEnv(ctx) {
|
|
|
181655
181759
|
}
|
|
181656
181760
|
}
|
|
181657
181761
|
}
|
|
181762
|
+
function sdkDefaultEnv(ctx, declared) {
|
|
181763
|
+
const seen = new Set;
|
|
181764
|
+
for (const d of declared) {
|
|
181765
|
+
const file = ctx.byPath.get(d.path);
|
|
181766
|
+
if (d.dev || file?.isTest)
|
|
181767
|
+
continue;
|
|
181768
|
+
const key = `${d.eco}:${d.eco === "pypi" ? d.name.toLowerCase().replace(/[_.]/g, "-") : d.name}`;
|
|
181769
|
+
for (const name of SDK_DEFAULT_ENV[key] ?? []) {
|
|
181770
|
+
if (seen.has(`${name}:${d.path}`))
|
|
181771
|
+
continue;
|
|
181772
|
+
seen.add(`${name}:${d.path}`);
|
|
181773
|
+
const line = d.line ?? 1;
|
|
181774
|
+
ctx.env.push({
|
|
181775
|
+
name,
|
|
181776
|
+
source: `${d.path}:${line}`,
|
|
181777
|
+
evidence: { path: d.path, line, excerpt: excerpt(file ? splitLines(file.text)[line - 1] : undefined), detector: `${DETECTOR2}.sdk_default` }
|
|
181778
|
+
});
|
|
181779
|
+
}
|
|
181780
|
+
}
|
|
181781
|
+
}
|
|
181782
|
+
function settingsFields(ctx, path, lines) {
|
|
181783
|
+
const settings = new Map([["BaseSettings", ""]]);
|
|
181784
|
+
for (let i = 0;i < lines.length; i++) {
|
|
181785
|
+
const cls = /^class\s+(\w+)\s*\(([^)]*)\)/.exec(lines[i]);
|
|
181786
|
+
const base = cls?.[2].split(",").map((b) => b.trim().split(".").pop() ?? "").find((b) => settings.has(b));
|
|
181787
|
+
if (!cls || base === undefined)
|
|
181788
|
+
continue;
|
|
181789
|
+
const body = [];
|
|
181790
|
+
let prefix = settings.get(base) ?? "";
|
|
181791
|
+
let docstring = false;
|
|
181792
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
181793
|
+
const line = lines[j];
|
|
181794
|
+
if (line.trim() !== "" && !/^\s/.test(line))
|
|
181795
|
+
break;
|
|
181796
|
+
const quotes = (line.match(/"""|'''/g) ?? []).length;
|
|
181797
|
+
if (docstring || quotes > 0) {
|
|
181798
|
+
if (quotes % 2 === 1)
|
|
181799
|
+
docstring = !docstring;
|
|
181800
|
+
continue;
|
|
181801
|
+
}
|
|
181802
|
+
const p2 = /\benv_prefix\s*=\s*['"]([^'"]*)['"]/.exec(line);
|
|
181803
|
+
if (p2)
|
|
181804
|
+
prefix = p2[1];
|
|
181805
|
+
const m = /^ {4}([A-Za-z][A-Za-z0-9_]*)\s*:/.exec(line);
|
|
181806
|
+
if (m && m[1] !== "model_config" && !isCommentLine(line))
|
|
181807
|
+
body.push({ name: m[1], line: j + 1 });
|
|
181808
|
+
}
|
|
181809
|
+
settings.set(cls[1], prefix);
|
|
181810
|
+
for (const f of body) {
|
|
181811
|
+
ctx.env.push({
|
|
181812
|
+
name: `${prefix}${f.name}`.toUpperCase(),
|
|
181813
|
+
source: `${path}:${f.line}`,
|
|
181814
|
+
evidence: { path, line: f.line, excerpt: excerpt(lines[f.line - 1]), detector: `${DETECTOR2}.settings` }
|
|
181815
|
+
});
|
|
181816
|
+
}
|
|
181817
|
+
}
|
|
181818
|
+
}
|
|
181658
181819
|
var GENERIC_DB_URL = /^(?:[A-Z0-9]+_)*(?:DATABASE|DB)_(?:URL|URI|DSN|CONNECTION(?:_STRING)?)(?:_[A-Z0-9]+)*$|^SQLALCHEMY_DATABASE_UR[IL]$|^(?:DATABASE|DB)$/;
|
|
181659
181820
|
function isGenericDatabaseUrl(name) {
|
|
181660
181821
|
return GENERIC_DB_URL.test(name);
|
|
@@ -181689,6 +181850,8 @@ function envSignals(ctx) {
|
|
|
181689
181850
|
}
|
|
181690
181851
|
if (resolved)
|
|
181691
181852
|
continue;
|
|
181853
|
+
if (/(?:^|_)(?:USE|ENABLE|ENABLED)(?:_|$)/.test(obs.name) && obs.value !== undefined && /^(?:false|0|no|off)$/i.test(obs.value.trim()))
|
|
181854
|
+
continue;
|
|
181692
181855
|
const sig = matchEnvName(obs.name);
|
|
181693
181856
|
if (sig) {
|
|
181694
181857
|
ctx.signals.push({ kind: "env", capability: sig.capability, provider: sig.provider, envVar: obs.name, evidence: ev, weak: isTestPath(ev.path) });
|
|
@@ -181698,14 +181861,18 @@ function envSignals(ctx) {
|
|
|
181698
181861
|
var SECRET_NAME = /(?:^|_)(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|PASS|CREDENTIALS?|PRIVATE|AUTH|SIGNING|SALT|CERT|DSN)(?:_|$)|APIKEY|_PAT$/;
|
|
181699
181862
|
var ENDPOINT_NAME = /(?:^|_)(?:URL|URI|ENDPOINT|HOST|HOSTNAME|BASE|BASE_URL|ADDR|ADDRESS|PORT|DSN|CONNECTION(?:_STRING)?|SERVER|REGION|BUCKET|TABLE|QUEUE|TOPIC|DATABASE|DB|INDEX|NAMESPACE|PROJECT_ID|ACCOUNT_ID)(?:_|$)/;
|
|
181700
181863
|
var SELF_CONFIG = /^(?:NODE_ENV|ENV|ENVIRONMENT|APP_ENV|DEBUG|LOG_LEVEL|LOGLEVEL|PORT|HOST|HOSTNAME|TZ|PYTHONPATH|PYTHONUNBUFFERED|PATH|HOME|CI|VERCEL_ENV|VERCEL_URL|NEXT_PUBLIC_APP_URL|APP_URL|SITE_URL|PUBLIC_URL|BASE_URL|NEXTAUTH_URL|AUTH_URL|DJANGO_SETTINGS_MODULE|FLASK_APP|FLASK_ENV|WORKERS|WEB_CONCURRENCY|ALLOWED_HOSTS|DJANGO_ALLOWED_HOSTS|CORS_ORIGINS?)$/;
|
|
181864
|
+
var PUBLIC_NAME = /^(?:NEXT_PUBLIC_|VITE_|PUBLIC_|EXPO_PUBLIC_|REACT_APP_|NUXT_PUBLIC_|GATSBY_)|(?:^|_)(?:PUBLISHABLE|ANON)_KEY$/;
|
|
181865
|
+
var CREDENTIAL_SETTING = /^(?:ENABLE|ENABLED|ALLOW|DISABLE|USE|MIN|MAX)_|_(?:LENGTH|EXPIRY|EXPIRES_IN|INTERVAL|INTERVAL_\w+|SECONDS|SECS|HOURS|MINUTES|MS|TTL|MAX|WINDOW|PREFIX|PREFIX_VAR|KIND|PATH|FORMAT|PROVIDER|DURATION|VIOLATIONS|COUNT|LIMIT)$/;
|
|
181701
181866
|
function classifyName(name) {
|
|
181702
|
-
if (SELF_CONFIG.test(name))
|
|
181867
|
+
if (SELF_CONFIG.test(name) || PUBLIC_NAME.test(name))
|
|
181703
181868
|
return "config";
|
|
181704
181869
|
if (SECRET_NAME.test(name))
|
|
181705
181870
|
return "secret";
|
|
181706
181871
|
return "unknown";
|
|
181707
181872
|
}
|
|
181708
181873
|
function classifyLinked(name, deps) {
|
|
181874
|
+
if (PUBLIC_NAME.test(name))
|
|
181875
|
+
return "config";
|
|
181709
181876
|
const isModel = deps.some((d) => d.capability === "model.inference");
|
|
181710
181877
|
const secret = SECRET_NAME.test(name) && !/(?:^|_)(?:URL|URI|HOST|ENDPOINT)$/.test(name);
|
|
181711
181878
|
if (isModel) {
|
|
@@ -181721,6 +181888,21 @@ function classifyLinked(name, deps) {
|
|
|
181721
181888
|
return "dependency_endpoint";
|
|
181722
181889
|
return "config";
|
|
181723
181890
|
}
|
|
181891
|
+
var AMBIENT_ENV = /^(?:PATH|HOME|USER|LOGNAME|SHELL|PWD|OLDPWD|TERM|LANG|LANGUAGE|LC_\w+|TMPDIR|TEMP|TMP|EDITOR|PAGER|LD_PRELOAD|LD_LIBRARY_PATH|PYTHONDONTWRITEBYTECODE|PYTHONASYNCIODEBUG|PYTHONHASHSEED|PYTHONFAULTHANDLER|NX_\w+|APPDATA|LOCALAPPDATA|PROGRAMDATA|USERPROFILE|COMSPEC|SYSTEMROOT|WINDIR|COLORTERM|COLORFGBG|NO_COLOR|FORCE_COLOR|TERM_PROGRAM|DEBIAN_FRONTEND|BUILDPLATFORM|TARGETPLATFORM|TARGETOS|TARGETARCH|TARGETVARIANT|BUILDOS|BUILDARCH|BUILDKIT_\w+|DOCKER_BUILDKIT|DOCKER_METADATA_\w+|GITHUB_(?:ACTIONS|WORKSPACE|SHA|REF|REF_NAME|REF_TYPE|HEAD_REF|BASE_REF|RUN_ID|RUN_NUMBER|RUN_ATTEMPT|REPOSITORY|REPOSITORY_OWNER|EVENT_NAME|EVENT_PATH|ENV|OUTPUT|STEP_SUMMARY|PATH|JOB|ACTOR|SERVER_URL|API_URL|GRAPHQL_URL|WORKFLOW|ACTION|ACTION_PATH|RETENTION_DAYS)|RUNNER_\w+|ACTIONS_\w+|CARGO_\w+|RUSTFLAGS|BASH_\w+|BASH|ZSH_\w+|CONDA_\w+|VIRTUAL_ENV|PYENV_\w+|NVM_\w+|npm_\w+|CODECOV_TOKEN|DOCKERHUB_\w+|UV_\w+|PIP_\w+|POETRY_\w+|CMAKE_\w+|CC|CXX|CFLAGS|CXXFLAGS|LDFLAGS|MAKEFLAGS|ANDROID_\w+|XDG_\w+|SSH_\w+|DISPLAY|SHLVL|_)$/;
|
|
181892
|
+
var TOOLING_SOURCE = /(^|\/)(\.github|\.gitlab|\.circleci|\.buildkite|ci|\.devcontainer)\/|(^|\/)(Makefile|GNUmakefile|CMakeLists\.txt|Justfile|Jenkinsfile)$|(^|\/)(?:jest|vitest|playwright|karma|cypress)\.config\.[cm]?[jt]s$|\.(cmake|mk|bat|ps1)$/;
|
|
181893
|
+
function isRuntimeEnvSource(source) {
|
|
181894
|
+
if (/ ARG$/.test(source))
|
|
181895
|
+
return false;
|
|
181896
|
+
const path = source.split(/[: ]/)[0];
|
|
181897
|
+
if (TOOLING_SOURCE.test(path))
|
|
181898
|
+
return false;
|
|
181899
|
+
if (!/[/.]/.test(path))
|
|
181900
|
+
return true;
|
|
181901
|
+
return !isTestPath(path);
|
|
181902
|
+
}
|
|
181903
|
+
function isAmbientEnv(name) {
|
|
181904
|
+
return AMBIENT_ENV.test(name);
|
|
181905
|
+
}
|
|
181724
181906
|
function buildEnvReferences(ctx, deps) {
|
|
181725
181907
|
const byName = new Map;
|
|
181726
181908
|
for (const ref of ctx.manifest?.env ?? []) {
|
|
@@ -181747,12 +181929,16 @@ function buildEnvReferences(ctx, deps) {
|
|
|
181747
181929
|
const entry = byName.get(name);
|
|
181748
181930
|
const linked = depsByEnv.get(name) ?? [];
|
|
181749
181931
|
let classification;
|
|
181750
|
-
if (
|
|
181932
|
+
if (PUBLIC_NAME.test(name))
|
|
181933
|
+
classification = "config";
|
|
181934
|
+
else if (linked.length > 0)
|
|
181751
181935
|
classification = classifyLinked(name, linked);
|
|
181752
181936
|
else if (entry.manifest && entry.manifest.classification !== "unknown")
|
|
181753
181937
|
classification = entry.manifest.classification;
|
|
181754
181938
|
else
|
|
181755
181939
|
classification = classifyName(name);
|
|
181940
|
+
if (classification === "secret" && CREDENTIAL_SETTING.test(name))
|
|
181941
|
+
classification = "config";
|
|
181756
181942
|
const ref = {
|
|
181757
181943
|
name,
|
|
181758
181944
|
sources: uniqSorted(entry.sources).slice(0, 20),
|
|
@@ -182107,6 +182293,9 @@ function detectMigrations(ctx) {
|
|
|
182107
182293
|
if (env)
|
|
182108
182294
|
ctx.envLinks.push({ envVar: env, capability: providerName === "mongodb" ? "document" : "database", provider: providerName, evidence });
|
|
182109
182295
|
}
|
|
182296
|
+
if (/\bextensions\s*=\s*\[[^\]]*\b(?:pgvector|vector)\b/.test(file.text)) {
|
|
182297
|
+
ctx.signals.push({ kind: "config", capability: "vector_store", provider: "pgvector", evidence: evidenceAt(file.path, file.text, findLine(file.text, /\bextensions\s*=/), `${DETECTOR3}.prisma`) });
|
|
182298
|
+
}
|
|
182110
182299
|
continue;
|
|
182111
182300
|
}
|
|
182112
182301
|
if (/^drizzle\.config\.(ts|js|mjs|cjs|json)$/.test(name)) {
|
|
@@ -182119,10 +182308,21 @@ function detectMigrations(ctx) {
|
|
|
182119
182308
|
b.schemaFiles.add(file.path);
|
|
182120
182309
|
for (const s of schemas) {
|
|
182121
182310
|
const p2 = joinPath(dir, s);
|
|
182122
|
-
if (
|
|
182311
|
+
if (/[*?]/.test(s)) {
|
|
182312
|
+
const re = globRegex(p2);
|
|
182313
|
+
for (const f of ctx.files)
|
|
182314
|
+
if (re.test(f.path))
|
|
182315
|
+
b.schemaFiles.add(f.path);
|
|
182316
|
+
} else if (ctx.byPath.has(p2) || ctx.files.some((f) => isUnder(f.path, p2)))
|
|
182123
182317
|
b.schemaFiles.add(p2);
|
|
182124
182318
|
}
|
|
182125
|
-
|
|
182319
|
+
let outDir = joinPath(dir, out2);
|
|
182320
|
+
if (!ctx.files.some((f) => isUnder(f.path, outDir))) {
|
|
182321
|
+
const nestedRoots = ctx.files.filter((f) => /^(?:package\.json|pyproject\.toml|go\.mod)$/.test(basename2(f.path)) && dirname2(f.path) !== dir && isUnder(f.path, dir)).map((f) => dirname2(f.path));
|
|
182322
|
+
const sql = ctx.files.find((f) => isUnder(f.path, dir) && /(^|\/)migrations?\/[^/]+\.(?:sql|[cm]?[jt]s)$/.test(f.path) && !isTestPath(f.path) && !nestedRoots.some((r) => isUnder(f.path, r)));
|
|
182323
|
+
if (sql)
|
|
182324
|
+
outDir = dirname2(sql.path);
|
|
182325
|
+
}
|
|
182126
182326
|
if (ctx.files.some((f) => isUnder(f.path, outDir))) {
|
|
182127
182327
|
b.paths.add(outDir);
|
|
182128
182328
|
claimDir(outDir);
|
|
@@ -182276,13 +182476,50 @@ function detectMigrations(ctx) {
|
|
|
182276
182476
|
b.paths.add(dir);
|
|
182277
182477
|
}
|
|
182278
182478
|
}
|
|
182479
|
+
for (const f of ctx.files) {
|
|
182480
|
+
if (!/^wrangler\.(?:toml|jsonc?)$/.test(basename2(f.path)) || !/new_sqlite_classes/.test(f.text) || isTestPath(f.path))
|
|
182481
|
+
continue;
|
|
182482
|
+
ctx.gaps.push({
|
|
182483
|
+
kind: "durable_object_migrations",
|
|
182484
|
+
message: `${f.path} declares SQLite-backed Durable Object migrations; the workerd runtime adapter applies them`,
|
|
182485
|
+
evidence: [{ path: f.path, line: findLine(f.text, /new_sqlite_classes/), detector: DETECTOR3 }]
|
|
182486
|
+
});
|
|
182487
|
+
}
|
|
182488
|
+
const all = [...sets.values()];
|
|
182489
|
+
for (const b of all) {
|
|
182490
|
+
if (b.tool !== "raw_sql" || b.paths.size > 0)
|
|
182491
|
+
continue;
|
|
182492
|
+
const parent = [...b.schemaFiles].map(dirname2)[0];
|
|
182493
|
+
const sibling = all.find((o) => o !== b && o.tool === "raw_sql" && [...o.paths].some((p2) => isUnder(p2, parent ?? "\x00")));
|
|
182494
|
+
if (sibling) {
|
|
182495
|
+
for (const f of b.schemaFiles)
|
|
182496
|
+
sibling.schemaFiles.add(f);
|
|
182497
|
+
sibling.first = [...sibling.first ?? [], ...uniqSorted(b.schemaFiles)];
|
|
182498
|
+
if (sibling.engine === "unknown")
|
|
182499
|
+
sibling.engine = b.engine;
|
|
182500
|
+
b.schemaFiles.clear();
|
|
182501
|
+
}
|
|
182502
|
+
}
|
|
182279
182503
|
const out = [];
|
|
182504
|
+
const testOnly = (p2) => isTestPath(p2) || /(^|\/)(?:conformance|e2e|[\w-]+-tests?|tests?-[\w-]+|e2e-[\w-]+)\//.test(p2);
|
|
182280
182505
|
for (const b of [...sets.values()].sort((x, y) => cmp(`${x.tool}:${x.key}`, `${y.tool}:${y.key}`))) {
|
|
182506
|
+
if (b.tool === "django" && b.paths.size === 0)
|
|
182507
|
+
continue;
|
|
182281
182508
|
if (b.paths.size === 0 && b.schemaFiles.size === 0)
|
|
182282
182509
|
continue;
|
|
182283
|
-
if (b.
|
|
182510
|
+
if ([...b.paths, ...b.schemaFiles].every(testOnly))
|
|
182284
182511
|
continue;
|
|
182285
|
-
|
|
182512
|
+
if (b.paths.size === 0) {
|
|
182513
|
+
for (const f of b.schemaFiles)
|
|
182514
|
+
b.paths.add(f);
|
|
182515
|
+
ctx.gaps.push({
|
|
182516
|
+
kind: "migration_schema_only",
|
|
182517
|
+
message: `${b.tool} has no migration files at ${[...b.schemaFiles][0]}; the schema is applied directly`,
|
|
182518
|
+
evidence: [...b.schemaFiles].slice(0, 1).map((path) => ({ path, detector: DETECTOR3 }))
|
|
182519
|
+
});
|
|
182520
|
+
}
|
|
182521
|
+
const first = b.first ?? [];
|
|
182522
|
+
out.push({ engine: b.engine, tool: b.tool, paths: [...first, ...uniqSorted(b.paths).filter((x) => !first.includes(x))], schemaFiles: uniqSorted(b.schemaFiles) });
|
|
182286
182523
|
}
|
|
182287
182524
|
return out;
|
|
182288
182525
|
}
|
|
@@ -183290,6 +183527,19 @@ function fromPyproject(f, out) {
|
|
|
183290
183527
|
for (const req of asArray(uv["dev-dependencies"]))
|
|
183291
183528
|
if (typeof req === "string")
|
|
183292
183529
|
add(req, true);
|
|
183530
|
+
const hatch = isRecord2(tool.hatch) && isRecord2(tool.hatch.metadata) && isRecord2(tool.hatch.metadata.hooks) ? tool.hatch.metadata.hooks : {};
|
|
183531
|
+
for (const hook of Object.values(hatch)) {
|
|
183532
|
+
if (!isRecord2(hook))
|
|
183533
|
+
continue;
|
|
183534
|
+
for (const req of asArray(hook.dependencies))
|
|
183535
|
+
if (typeof req === "string")
|
|
183536
|
+
add(req, false);
|
|
183537
|
+
const extra = isRecord2(hook["optional-dependencies"]) ? hook["optional-dependencies"] : {};
|
|
183538
|
+
for (const [group, reqs] of Object.entries(extra))
|
|
183539
|
+
for (const req of asArray(reqs))
|
|
183540
|
+
if (typeof req === "string")
|
|
183541
|
+
add(req, /dev|test|lint|doc/i.test(group));
|
|
183542
|
+
}
|
|
183293
183543
|
}
|
|
183294
183544
|
function fromPipfile(f, out) {
|
|
183295
183545
|
let doc;
|
|
@@ -183553,7 +183803,7 @@ function cfnResources(ctx, resources, file, runtime) {
|
|
|
183553
183803
|
if (!spec)
|
|
183554
183804
|
continue;
|
|
183555
183805
|
const resource = asString(props.TableName) ?? asString(props.BucketName) ?? asString(props.QueueName) ?? asString(props.TopicName) ?? asString(props.Name) ?? asString(props.StateMachineName) ?? logicalId;
|
|
183556
|
-
runtime.bindings.push({ name: logicalId, type, resource });
|
|
183806
|
+
runtime.bindings.push({ name: logicalId, type: cfnBindingType(type), resource });
|
|
183557
183807
|
bindingSignal(ctx, { ...spec, binding: logicalId, resource, evidence: ev(file, logicalId) });
|
|
183558
183808
|
}
|
|
183559
183809
|
}
|
|
@@ -183603,6 +183853,30 @@ function detectSam(ctx, file, doc) {
|
|
|
183603
183853
|
envFromVariables(ctx, isRecord2(props.Environment) ? props.Environment.Variables : undefined, file, logicalId);
|
|
183604
183854
|
}
|
|
183605
183855
|
cfnResources(ctx, resources, file, runtime);
|
|
183856
|
+
const ids = Object.keys(resources);
|
|
183857
|
+
const direct = (text, self) => ids.filter((id) => id !== self && new RegExp(`\\b${escapeRe(id)}\\b`).test(text));
|
|
183858
|
+
const globalsText = JSON.stringify(isRecord2(doc.Globals) && isRecord2(doc.Globals.Function) ? doc.Globals.Function : {});
|
|
183859
|
+
const refs = new Map;
|
|
183860
|
+
for (const [fnId, res] of Object.entries(resources)) {
|
|
183861
|
+
if (!isRecord2(res) || !/^AWS::(?:Serverless|Lambda)::Function$/.test(asString(res.Type) ?? ""))
|
|
183862
|
+
continue;
|
|
183863
|
+
const seen = new Set(direct(`${JSON.stringify(res.Properties ?? {})} ${globalsText}`, fnId));
|
|
183864
|
+
const queue = [...seen];
|
|
183865
|
+
while (queue.length > 0) {
|
|
183866
|
+
const id = queue.shift();
|
|
183867
|
+
const other = resources[id];
|
|
183868
|
+
if (!isRecord2(other) || /^AWS::(?:Serverless|Lambda)::Function$/.test(asString(other.Type) ?? ""))
|
|
183869
|
+
continue;
|
|
183870
|
+
for (const next of direct(JSON.stringify(other.Properties ?? {}), id)) {
|
|
183871
|
+
if (next !== fnId && !seen.has(next)) {
|
|
183872
|
+
seen.add(next);
|
|
183873
|
+
queue.push(next);
|
|
183874
|
+
}
|
|
183875
|
+
}
|
|
183876
|
+
}
|
|
183877
|
+
refs.set(fnId, seen);
|
|
183878
|
+
}
|
|
183879
|
+
ctx.functionRefs.set(file.path, refs);
|
|
183606
183880
|
if (runtime.entrypoints.length === 0)
|
|
183607
183881
|
return;
|
|
183608
183882
|
return runtime;
|
|
@@ -183666,6 +183940,17 @@ var CDK_RESOURCE_TYPES = {
|
|
|
183666
183940
|
StateMachine: "AWS::StepFunctions::StateMachine",
|
|
183667
183941
|
UserPool: "AWS::Cognito::UserPool"
|
|
183668
183942
|
};
|
|
183943
|
+
function cdkTrigger(text, variable) {
|
|
183944
|
+
const v = `(?:this\\.|self\\.)?${escapeRe(variable)}\\b`;
|
|
183945
|
+
const re = (pattern) => new RegExp(pattern);
|
|
183946
|
+
if (re(`LambdaIntegration\\(\\s*(?:['"][^'"]*['"]\\s*,\\s*)?${v}`).test(text) || re(`LambdaRestApi\\([^)]*?handler\\s*[:=]\\s*${v}`).test(text) || re(`${v}\\s*\\.\\s*(?:addFunctionUrl|add_function_url)\\(`).test(text) || re(`FunctionUrl\\([^)]*?function\\s*[:=]\\s*${v}`).test(text))
|
|
183947
|
+
return "http";
|
|
183948
|
+
if (re(`${v}\\s*\\.\\s*(?:addEventSource|add_event_source)\\(\\s*(?:new\\s+)?(?:\\w+\\.)?SqsEventSource`).test(text))
|
|
183949
|
+
return "queue";
|
|
183950
|
+
if (re(`Schedule\\.[\\s\\S]{0,400}LambdaFunction\\(\\s*${v}`).test(text))
|
|
183951
|
+
return "cron";
|
|
183952
|
+
return "event";
|
|
183953
|
+
}
|
|
183669
183954
|
function detectCdk(ctx, cdkJson) {
|
|
183670
183955
|
const root = dirname2(cdkJson.path);
|
|
183671
183956
|
const runtime = newRuntime("aws_lambda", root, [cdkJson.path], [{ path: cdkJson.path, detector: `${DETECTOR4}.cdk` }], 0.8);
|
|
@@ -183691,7 +183976,8 @@ function detectCdk(ctx, cdkJson) {
|
|
|
183691
183976
|
runtime.runtimeVersion = lang.version;
|
|
183692
183977
|
}
|
|
183693
183978
|
const handlerRef = entry ? joinPath(root, entry) + (handler ? `#${handler}` : "") : asset ? `${joinPath(root, asset)}/${handler ?? ""}` : handler;
|
|
183694
|
-
|
|
183979
|
+
const variable = /(?:\b|\.)(\w+)\s*=\s*$/.exec(file.text.slice(Math.max(0, m.index - 120), m.index))?.[1];
|
|
183980
|
+
runtime.entrypoints.push({ name: id, handler: handlerRef, trigger: variable ? cdkTrigger(file.text, variable) : "event" });
|
|
183695
183981
|
runtime.evidence.push({ path: file.path, line: at(m.index), excerpt: excerpt(splitLines(file.text)[at(m.index) - 1]), detector: `${DETECTOR4}.cdk:${construct}` });
|
|
183696
183982
|
}
|
|
183697
183983
|
CDK_RESOURCE.lastIndex = 0;
|
|
@@ -183703,12 +183989,12 @@ function detectCdk(ctx, cdkJson) {
|
|
|
183703
183989
|
const type = CDK_RESOURCE_TYPES[kind];
|
|
183704
183990
|
if (type) {
|
|
183705
183991
|
const spec = CFN_RESOURCES[type];
|
|
183706
|
-
runtime.bindings.push({ name: id, type });
|
|
183992
|
+
runtime.bindings.push({ name: id, type: cfnBindingType(type) });
|
|
183707
183993
|
bindingSignal(ctx, { ...spec, binding: id, evidence });
|
|
183708
183994
|
} else if (kind === "DatabaseInstance" || kind === "DatabaseCluster") {
|
|
183709
183995
|
const engine = /DatabaseInstanceEngine\.(\w+)|DatabaseClusterEngine\.(\w+)/.exec(file.text.slice(m.index, m.index + 800));
|
|
183710
183996
|
const provider = rdsEngineProvider(engine?.[1] ?? engine?.[2]);
|
|
183711
|
-
runtime.bindings.push({ name: id, type:
|
|
183997
|
+
runtime.bindings.push({ name: id, type: "rds" });
|
|
183712
183998
|
bindingSignal(ctx, { capability: "database", provider, cls: "engine", binding: id, evidence });
|
|
183713
183999
|
}
|
|
183714
184000
|
}
|
|
@@ -183842,7 +184128,10 @@ function detectCloudflare(input) {
|
|
|
183842
184128
|
if (seen.has(key))
|
|
183843
184129
|
return false;
|
|
183844
184130
|
seen.add(key);
|
|
183845
|
-
|
|
184131
|
+
if (type === "QueueConsumer")
|
|
184132
|
+
return true;
|
|
184133
|
+
const normalized = type.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
184134
|
+
runtime.bindings.push(resource ? { name, type: normalized, resource } : { name, type: normalized });
|
|
183846
184135
|
return true;
|
|
183847
184136
|
};
|
|
183848
184137
|
const produced = new Set(sections(config).flatMap((section) => asArray(isRecord2(section.queues) ? section.queues.producers : undefined).map((p2) => isRecord2(p2) ? asString(p2.queue) : undefined)));
|
|
@@ -183915,7 +184204,9 @@ function detectCloudflare(input) {
|
|
|
183915
184204
|
const script = asString(item.script_name);
|
|
183916
184205
|
if (!addBinding(name, "DurableObject", className))
|
|
183917
184206
|
continue;
|
|
183918
|
-
if (script
|
|
184207
|
+
if (!script || workerNames.has(script)) {
|
|
184208
|
+
bindingSignal(ctx, { capability: "document", provider: "cloudflare.durable_objects", cls: "emulated", binding: name, resource: className, evidence: ev2(file, name) });
|
|
184209
|
+
} else {
|
|
183919
184210
|
bindingSignal(ctx, { capability: "function.invoke", provider: "cloudflare.durable_object", cls: "saas", binding: name, resource: className, evidence: ev2(file, name) });
|
|
183920
184211
|
ctx.gaps.push({ kind: "external_service_binding", message: `Durable Object ${name} lives in worker \`${script}\`, which is not in the bundle`, evidence: [ev2(file, name)] });
|
|
183921
184212
|
}
|
|
@@ -183961,6 +184252,21 @@ var DETECTOR6 = "runtime.container";
|
|
|
183961
184252
|
var DOCKERFILE = /^(?:Dockerfile|Containerfile)(?:\.[\w.-]+)?$|\.(?:Dockerfile|dockerfile)$/;
|
|
183962
184253
|
var COMPOSE = /^(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml$/;
|
|
183963
184254
|
var INFRA_IMAGES = /^(?:nginx|traefik|caddy|haproxy|envoyproxy\/envoy|busybox|alpine|ubuntu|debian|node|python|golang|grafana\/.*|prom\/.*|otel\/.*|jaegertracing\/.*|adminer|dpage\/pgadmin4|portainer\/.*|.*\/?(?:loki|promtail|tempo))$/;
|
|
184255
|
+
var PROJECT_MANIFEST = /^(?:package\.json|pyproject\.toml|requirements\.txt|setup\.py|go\.mod|Gemfile|Cargo\.toml|pom\.xml|build\.gradle(?:\.kts)?)$/;
|
|
184256
|
+
function buildContext(ctx, dir, copies) {
|
|
184257
|
+
if (copies.length === 0 || ctx.files.some((f) => dirname2(f.path) === dir && PROJECT_MANIFEST.test(basename2(f.path))))
|
|
184258
|
+
return dir;
|
|
184259
|
+
const has = (base, src) => {
|
|
184260
|
+
const p2 = joinPath(base, src);
|
|
184261
|
+
return ctx.byPath.has(p2) || ctx.files.some((f) => f.path.startsWith(`${p2}/`));
|
|
184262
|
+
};
|
|
184263
|
+
for (let base = dir;; base = dirname2(base)) {
|
|
184264
|
+
if (copies.every((src) => has(base, src)))
|
|
184265
|
+
return base;
|
|
184266
|
+
if (base === ".")
|
|
184267
|
+
return dir;
|
|
184268
|
+
}
|
|
184269
|
+
}
|
|
183964
184270
|
function parseInstructionArgs(rest) {
|
|
183965
184271
|
const t = rest.trim();
|
|
183966
184272
|
if (t.startsWith("[")) {
|
|
@@ -183973,7 +184279,7 @@ function parseInstructionArgs(rest) {
|
|
|
183973
184279
|
return ["sh", "-c", t];
|
|
183974
184280
|
}
|
|
183975
184281
|
function parseDockerfile(file) {
|
|
183976
|
-
const out = { path: file.path, from: [], expose: [], env: [], args: [], stages: {} };
|
|
184282
|
+
const out = { path: file.path, from: [], expose: [], env: [], args: [], copies: [], stages: {} };
|
|
183977
184283
|
const raw = splitLines(file.text);
|
|
183978
184284
|
const stages = new Map;
|
|
183979
184285
|
let stage = { base: "", expose: [], env: [] };
|
|
@@ -184018,6 +184324,11 @@ function parseDockerfile(file) {
|
|
|
184018
184324
|
for (const [name, value] of pairs)
|
|
184019
184325
|
if (name)
|
|
184020
184326
|
stage.env.push({ name, value, line: startLine });
|
|
184327
|
+
} else if ((instr === "COPY" || instr === "ADD") && !/--from=/.test(rest)) {
|
|
184328
|
+
const parts = rest.trim().startsWith("[") ? parseInstructionArgs(rest) : rest.split(/\s+/).filter((w) => !w.startsWith("--"));
|
|
184329
|
+
for (const src of parts.slice(0, -1))
|
|
184330
|
+
if (src && !/^https?:/.test(src) && !/[*?$]/.test(src) && src !== "." && src !== "./")
|
|
184331
|
+
out.copies.push(src.replace(/^\.\//, "").replace(/\/$/, ""));
|
|
184021
184332
|
} else if (instr === "ARG") {
|
|
184022
184333
|
const name = rest.split("=")[0].trim();
|
|
184023
184334
|
if (name)
|
|
@@ -184051,7 +184362,7 @@ function detectContainers(input) {
|
|
|
184051
184362
|
continue;
|
|
184052
184363
|
const df = parseDockerfile(file);
|
|
184053
184364
|
dockerfiles.push(df);
|
|
184054
|
-
const root = dirname2(file.path);
|
|
184365
|
+
const root = buildContext(ctx, dirname2(file.path), df.copies);
|
|
184055
184366
|
const base = df.from[df.from.length - 1] ?? "";
|
|
184056
184367
|
const lambda = /^(?:public\.ecr\.aws\/lambda\/|amazon\/aws-lambda-)/.test(base);
|
|
184057
184368
|
const kind = lambda ? "aws_lambda" : "container";
|
|
@@ -184136,12 +184447,16 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184136
184447
|
const ports = asArray(svc.ports);
|
|
184137
184448
|
const svcEntrypoint = composeArgs(svc.entrypoint);
|
|
184138
184449
|
const svcCommand = composeArgs(svc.command);
|
|
184139
|
-
|
|
184450
|
+
let serviceRoot = build !== undefined ? joinPath(base, typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".") : undefined;
|
|
184140
184451
|
if (build !== undefined) {
|
|
184141
184452
|
const context = typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".";
|
|
184142
|
-
const
|
|
184453
|
+
const contextRoot = joinPath(base, context);
|
|
184454
|
+
const dockerfile = joinPath(contextRoot, (isRecord2(build) ? asString(build.dockerfile) : undefined) ?? "Dockerfile");
|
|
184455
|
+
const dfDir = dirname2(dockerfile);
|
|
184456
|
+
const projectDir = dfDir !== contextRoot && isUnder(dfDir, contextRoot) && ctx.files.some((f) => dirname2(f.path) === dfDir && PROJECT_MANIFEST.test(basename2(f.path)));
|
|
184457
|
+
const root = projectDir ? dfDir : contextRoot;
|
|
184458
|
+
serviceRoot = root;
|
|
184143
184459
|
const key = `container:${root}`;
|
|
184144
|
-
const dockerfile = joinPath(root, (isRecord2(build) ? asString(build.dockerfile) : undefined) ?? "Dockerfile");
|
|
184145
184460
|
const contribution = contributions.get(dockerfile);
|
|
184146
184461
|
const runtime = byRoot.get(key) ?? newRuntime("container", root, [], [], 0.85);
|
|
184147
184462
|
runtime.configFiles.push(file.path);
|
|
@@ -184178,28 +184493,11 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184178
184493
|
if (sig) {
|
|
184179
184494
|
ctx.signals.push({ kind: "image", capability: sig.capability, provider: sig.provider, resource: name, evidence });
|
|
184180
184495
|
} else if (!INFRA_IMAGES.test(image.split(":")[0])) {
|
|
184181
|
-
|
|
184182
|
-
|
|
184183
|
-
|
|
184184
|
-
|
|
184185
|
-
|
|
184186
|
-
if (!runtime.entrypoints.some((e) => e.name === name)) {
|
|
184187
|
-
const entry = { name, handler: image, trigger: ports.length > 0 ? "http" : "unknown" };
|
|
184188
|
-
if (svcEntrypoint)
|
|
184189
|
-
entry.command = [...svcEntrypoint, ...svcCommand ?? []];
|
|
184190
|
-
else if (svcCommand) {
|
|
184191
|
-
ctx.gaps.push({
|
|
184192
|
-
kind: "unresolved_command",
|
|
184193
|
-
message: `Compose service \`${name}\` overrides the CMD of image \`${image}\`, whose ENTRYPOINT is not in the bundle; the full launch command is unknown`,
|
|
184194
|
-
evidence: [evidence]
|
|
184195
|
-
});
|
|
184196
|
-
}
|
|
184197
|
-
runtime.entrypoints.push(entry);
|
|
184198
|
-
}
|
|
184199
|
-
byRoot.set(key, runtime);
|
|
184200
|
-
} else {
|
|
184201
|
-
ctx.gaps.push({ kind: "unrecognized_service", message: `Compose service \`${name}\` uses image \`${image}\`, which maps to no known provider`, evidence: [evidence] });
|
|
184202
|
-
}
|
|
184496
|
+
ctx.gaps.push({
|
|
184497
|
+
kind: "unrecognized_service",
|
|
184498
|
+
message: `Compose service \`${name}\` runs prebuilt image \`${image}\`, which maps to no known provider; treated as a sidecar, not a harness entrypoint`,
|
|
184499
|
+
evidence: [evidence]
|
|
184500
|
+
});
|
|
184203
184501
|
}
|
|
184204
184502
|
}
|
|
184205
184503
|
const env = svc.environment;
|
|
@@ -184223,7 +184521,8 @@ function composeServices(ctx, file, byRoot, contributions, relocated) {
|
|
|
184223
184521
|
source: `${file.path} services.${name}.environment`,
|
|
184224
184522
|
value: literal,
|
|
184225
184523
|
evidence: { path: file.path, line: l, excerpt: l ? excerpt(lines[l - 1]) : undefined, detector: `${DETECTOR6}.compose` },
|
|
184226
|
-
scopeRoot: serviceRoot
|
|
184524
|
+
scopeRoot: serviceRoot,
|
|
184525
|
+
service: name
|
|
184227
184526
|
});
|
|
184228
184527
|
}
|
|
184229
184528
|
}
|
|
@@ -184246,14 +184545,21 @@ var ROOT_MARKERS = new Set([
|
|
|
184246
184545
|
"host.json",
|
|
184247
184546
|
"deno.json",
|
|
184248
184547
|
"deno.jsonc",
|
|
184249
|
-
"Cargo.toml"
|
|
184548
|
+
"Cargo.toml",
|
|
184549
|
+
"project.json"
|
|
184250
184550
|
]);
|
|
184251
184551
|
function projectRoots(files) {
|
|
184252
|
-
const
|
|
184253
|
-
for (const f of files)
|
|
184254
|
-
if (ROOT_MARKERS.has(basename2(f.path)))
|
|
184255
|
-
|
|
184256
|
-
|
|
184552
|
+
const markers = new Map;
|
|
184553
|
+
for (const f of files) {
|
|
184554
|
+
if (!ROOT_MARKERS.has(basename2(f.path)))
|
|
184555
|
+
continue;
|
|
184556
|
+
const dir = dirname2(f.path);
|
|
184557
|
+
if (!markers.has(dir))
|
|
184558
|
+
markers.set(dir, new Set);
|
|
184559
|
+
markers.get(dir).add(basename2(f.path));
|
|
184560
|
+
}
|
|
184561
|
+
const pyProjects = [...markers.entries()].filter(([, m]) => m.has("pyproject.toml") || m.has("setup.py")).map(([d]) => d);
|
|
184562
|
+
return [...markers.entries()].filter(([dir, m]) => !(m.size === 1 && m.has("requirements.txt") && pyProjects.some((p2) => p2 !== dir && isUnder(dir, p2)))).map(([dir]) => dir).sort();
|
|
184257
184563
|
}
|
|
184258
184564
|
function nearestRoot(path, roots) {
|
|
184259
184565
|
let best;
|
|
@@ -184291,6 +184597,15 @@ function cloudRunMarkers(ctx) {
|
|
|
184291
184597
|
out.push({ root: dirname2(file.path), path: file.path, line: findLine(file.text, /serving\.knative\.dev/) });
|
|
184292
184598
|
continue;
|
|
184293
184599
|
}
|
|
184600
|
+
if (name.endsWith(".tf")) {
|
|
184601
|
+
const services = (file.text.match(/resource\s+"google_cloud_run(?:_v2)?_service"/g) ?? []).length;
|
|
184602
|
+
if (services > 0) {
|
|
184603
|
+
const dir = dirname2(file.path);
|
|
184604
|
+
const root = /^(?:terraform|infra|infrastructure|deploy|deployment)$/.test(basename2(dir)) ? dirname2(dir) : dir;
|
|
184605
|
+
out.push({ root, path: file.path, line: findLine(file.text, /google_cloud_run/), services });
|
|
184606
|
+
}
|
|
184607
|
+
continue;
|
|
184608
|
+
}
|
|
184294
184609
|
const re = /gcloud\s+(?:beta\s+)?run\s+(?:deploy|services\s+replace)\b([^\n]*)/g;
|
|
184295
184610
|
let m;
|
|
184296
184611
|
while ((m = re.exec(file.text)) !== null) {
|
|
@@ -184495,76 +184810,72 @@ function decoratedDefs(text, decorator) {
|
|
|
184495
184810
|
// ../../packages/harness-bundle/src/detect/runtimes/azure.ts
|
|
184496
184811
|
var DETECTOR8 = "runtime.azure";
|
|
184497
184812
|
var BINDINGS = {
|
|
184498
|
-
|
|
184499
|
-
|
|
184500
|
-
|
|
184501
|
-
|
|
184502
|
-
blobTrigger: { trigger: "event", dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184503
|
-
blob: { dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184813
|
+
http: { trigger: "http" },
|
|
184814
|
+
timer: { trigger: "cron" },
|
|
184815
|
+
queue: { trigger: "queue", dep: { capability: "queue", provider: "azure.queue", cls: "emulated" } },
|
|
184816
|
+
blob: { trigger: "event", dep: { capability: "object_store", provider: "azure.blob", cls: "emulated" } },
|
|
184504
184817
|
table: { dep: { capability: "document", provider: "azure.table", cls: "emulated" } },
|
|
184505
|
-
|
|
184506
|
-
|
|
184507
|
-
|
|
184508
|
-
|
|
184509
|
-
|
|
184510
|
-
|
|
184511
|
-
|
|
184512
|
-
|
|
184513
|
-
|
|
184514
|
-
|
|
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" } }
|
|
184515
184829
|
};
|
|
184516
|
-
var
|
|
184517
|
-
|
|
184518
|
-
|
|
184519
|
-
|
|
184520
|
-
|
|
184521
|
-
|
|
184522
|
-
|
|
184523
|
-
|
|
184524
|
-
|
|
184525
|
-
|
|
184526
|
-
|
|
184527
|
-
|
|
184528
|
-
|
|
184529
|
-
|
|
184530
|
-
|
|
184531
|
-
|
|
184532
|
-
|
|
184533
|
-
|
|
184534
|
-
|
|
184535
|
-
|
|
184536
|
-
|
|
184537
|
-
|
|
184538
|
-
|
|
184539
|
-
|
|
184540
|
-
|
|
184541
|
-
|
|
184542
|
-
|
|
184543
|
-
|
|
184544
|
-
|
|
184545
|
-
|
|
184546
|
-
|
|
184547
|
-
|
|
184548
|
-
|
|
184549
|
-
|
|
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: "
|
|
184564
|
-
serviceBusQueue: "
|
|
184565
|
-
serviceBusTopic: "
|
|
184566
|
-
eventHub: "
|
|
184567
|
-
eventGrid: "
|
|
184874
|
+
cosmosDB: "cosmosdb",
|
|
184875
|
+
serviceBusQueue: "servicebus",
|
|
184876
|
+
serviceBusTopic: "servicebus",
|
|
184877
|
+
eventHub: "eventhub",
|
|
184878
|
+
eventGrid: "eventgrid",
|
|
184568
184879
|
sql: "sql"
|
|
184569
184880
|
};
|
|
184570
184881
|
function detectAzure(input) {
|
|
@@ -184583,12 +184894,12 @@ function detectAzure(input) {
|
|
|
184583
184894
|
const seenDeps = new Set;
|
|
184584
184895
|
const addDep = (type, name, rawConnection, evidence) => {
|
|
184585
184896
|
const connection = rawConnection && /^[A-Za-z_][A-Za-z0-9_]*$/.test(rawConnection) ? rawConnection : undefined;
|
|
184897
|
+
if (!runtime.bindings.some((b) => b.name === name && b.type === type))
|
|
184898
|
+
runtime.bindings.push(connection ? { name, type, resource: connection } : { name, type });
|
|
184586
184899
|
const spec = BINDINGS[type]?.dep;
|
|
184587
184900
|
if (!spec)
|
|
184588
184901
|
return;
|
|
184589
184902
|
const key = `${spec.provider}:${connection ?? ""}`;
|
|
184590
|
-
if (!runtime.bindings.some((b) => b.name === name && b.type === type))
|
|
184591
|
-
runtime.bindings.push({ name, type, resource: connection });
|
|
184592
184903
|
if (seenDeps.has(key))
|
|
184593
184904
|
return;
|
|
184594
184905
|
seenDeps.add(key);
|
|
@@ -184611,7 +184922,7 @@ function detectAzure(input) {
|
|
|
184611
184922
|
continue;
|
|
184612
184923
|
if (basename2(file.path) === "function.json") {
|
|
184613
184924
|
handleFunctionJson(file, runtime, addDep);
|
|
184614
|
-
} else if (file.path.endsWith(".py") && /azure\.functions|func\.FunctionApp|func\.Blueprint/.test(file.text)) {
|
|
184925
|
+
} else if (file.path.endsWith(".py") && /azure\.functions|azure\.durable_functions|func\.FunctionApp|func\.Blueprint|\bDFApp\(/.test(file.text)) {
|
|
184615
184926
|
handlePython(file, runtime, addDep);
|
|
184616
184927
|
} else if (/\.[cm]?[jt]s$/.test(file.path) && /@azure\/functions/.test(file.text)) {
|
|
184617
184928
|
handleNode(file, runtime, addDep);
|
|
@@ -184632,17 +184943,22 @@ function handleFunctionJson(file, runtime, addDep) {
|
|
|
184632
184943
|
for (const b of asArray(json.bindings)) {
|
|
184633
184944
|
if (!isRecord2(b))
|
|
184634
184945
|
continue;
|
|
184635
|
-
const
|
|
184636
|
-
const
|
|
184637
|
-
if (
|
|
184638
|
-
|
|
184946
|
+
const raw = asString(b.type) ?? "";
|
|
184947
|
+
const type = azureBindingType(raw);
|
|
184948
|
+
if (!type)
|
|
184949
|
+
continue;
|
|
184950
|
+
if (/trigger$/i.test(raw) && BINDINGS[type].trigger)
|
|
184951
|
+
trigger = BINDINGS[type].trigger;
|
|
184639
184952
|
const name = asString(b.name) ?? type;
|
|
184640
|
-
addDep(type, name, asString(b.connection) ?? (type
|
|
184953
|
+
addDep(type, name, asString(b.connection) ?? defaultConnection(type), evidenceAt(file.path, file.text, findLine(file.text, new RegExp(`"${escapeRe(raw)}"`)), DETECTOR8));
|
|
184641
184954
|
}
|
|
184642
184955
|
const script = asString(json.scriptFile);
|
|
184643
184956
|
runtime.entrypoints.push({ name: fnName, handler: script ? joinPath(dirname2(file.path), script) : dirname2(file.path), trigger });
|
|
184644
184957
|
runtime.language ??= script ? languageFromPath(script) : undefined;
|
|
184645
184958
|
}
|
|
184959
|
+
function defaultConnection(type) {
|
|
184960
|
+
return type === "blob" || type === "queue" || type === "table" ? "AzureWebJobsStorage" : undefined;
|
|
184961
|
+
}
|
|
184646
184962
|
function parenDepth(text) {
|
|
184647
184963
|
let depth = 0;
|
|
184648
184964
|
for (const ch of text.replace(/'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"|#[^\n]*/g, "")) {
|
|
@@ -184665,24 +184981,34 @@ function handlePython(file, runtime, addDep) {
|
|
|
184665
184981
|
while (parenDepth(lines.slice(start, i + 1).join(`
|
|
184666
184982
|
`)) > 0 && i + 1 < lines.length)
|
|
184667
184983
|
i++;
|
|
184668
|
-
|
|
184984
|
+
const args = [deco[2], ...lines.slice(start + 1, i + 1)].join(`
|
|
184669
184985
|
`);
|
|
184670
184986
|
const kind = deco[1];
|
|
184671
184987
|
if (kind === "function_name")
|
|
184672
|
-
pending.name = /name\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184673
|
-
const
|
|
184988
|
+
pending.name = /name\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184989
|
+
const stem = kind === "route" || kind === "schedule" ? kind : /^(\w+?)_(?:trigger|input|output)$/.exec(kind)?.[1];
|
|
184990
|
+
const type = stem ? azureBindingType(stem) : undefined;
|
|
184674
184991
|
if (type) {
|
|
184675
|
-
|
|
184992
|
+
const isTrigger = kind === "route" || kind === "schedule" || kind.endsWith("_trigger");
|
|
184993
|
+
if (isTrigger && BINDINGS[type].trigger)
|
|
184676
184994
|
pending.trigger = BINDINGS[type].trigger;
|
|
184677
184995
|
if (kind === "route")
|
|
184678
|
-
pending.name ??= /route\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184679
|
-
const connection = /connection\s*=\s*['"]([^'"]+)['"]/.exec(
|
|
184680
|
-
|
|
184996
|
+
pending.name ??= /route\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184997
|
+
const connection = /connection\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1] ?? defaultConnection(type);
|
|
184998
|
+
const argName = /\b(?:arg_name|client_name|context_name|input_name|name)\s*=\s*['"]([^'"]+)['"]/.exec(args)?.[1];
|
|
184999
|
+
const evidence = { path: file.path, line: start + 1, excerpt: excerpt(line), detector: DETECTOR8 };
|
|
185000
|
+
if (type === "http" && !argName)
|
|
185001
|
+
pending.httpBinding = { line: start + 1, connection };
|
|
185002
|
+
else
|
|
185003
|
+
addDep(type, argName ?? kind, connection, evidence);
|
|
184681
185004
|
}
|
|
184682
185005
|
continue;
|
|
184683
185006
|
}
|
|
184684
|
-
const def = /^\s*(?:async\s+)?def\s+(\w+)
|
|
185007
|
+
const def = /^\s*(?:async\s+)?def\s+(\w+)\s*\(\s*(\w+)?/.exec(line);
|
|
184685
185008
|
if (def && pending) {
|
|
185009
|
+
if (pending.httpBinding) {
|
|
185010
|
+
addDep("http", def[2] ?? "req", undefined, { path: file.path, line: pending.httpBinding.line, excerpt: excerpt(lines[pending.httpBinding.line - 1]), detector: DETECTOR8 });
|
|
185011
|
+
}
|
|
184686
185012
|
if (pending.trigger) {
|
|
184687
185013
|
runtime.entrypoints.push({ name: pending.name ?? def[1], handler: `${file.path}#${def[1]}`, trigger: pending.trigger });
|
|
184688
185014
|
runtime.language ??= "python";
|
|
@@ -184696,10 +185022,10 @@ function handlePython(file, runtime, addDep) {
|
|
|
184696
185022
|
function handleNode(file, runtime, addDep) {
|
|
184697
185023
|
const at = lineIndex(file.text);
|
|
184698
185024
|
const lines = splitLines(file.text);
|
|
184699
|
-
const reg = /\
|
|
185025
|
+
const reg = /\b(?:app|df\.app)\.(\w+)\(\s*['"]([^'"]+)['"]/g;
|
|
184700
185026
|
let m;
|
|
184701
185027
|
while ((m = reg.exec(file.text)) !== null) {
|
|
184702
|
-
const type =
|
|
185028
|
+
const type = azureBindingType(m[1]);
|
|
184703
185029
|
if (!type)
|
|
184704
185030
|
continue;
|
|
184705
185031
|
const line = at(m.index);
|
|
@@ -184707,7 +185033,7 @@ function handleNode(file, runtime, addDep) {
|
|
|
184707
185033
|
runtime.language ??= languageFromPath(file.path);
|
|
184708
185034
|
const window2 = callArgs(file.text, m.index);
|
|
184709
185035
|
const connection = /connection\s*:\s*['"]([^'"]+)['"]/.exec(window2)?.[1];
|
|
184710
|
-
addDep(type, m[2], connection ?? (type
|
|
185036
|
+
addDep(type, m[2], connection ?? defaultConnection(type), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
|
|
184711
185037
|
}
|
|
184712
185038
|
const io = /\b(?:input|output)\.(\w+)\(\s*\{/g;
|
|
184713
185039
|
while ((m = io.exec(file.text)) !== null) {
|
|
@@ -184717,7 +185043,8 @@ function handleNode(file, runtime, addDep) {
|
|
|
184717
185043
|
const line = at(m.index);
|
|
184718
185044
|
const window2 = callArgs(file.text, m.index);
|
|
184719
185045
|
const connection = /connection\s*:\s*['"]([^'"]+)['"]/.exec(window2)?.[1];
|
|
184720
|
-
|
|
185046
|
+
const name = /(?:const|let|var)\s+(\w+)\s*=\s*$/.exec(file.text.slice(Math.max(0, m.index - 80), m.index))?.[1] ?? m[1];
|
|
185047
|
+
addDep(type, name, connection ?? defaultConnection(type), { path: file.path, line, excerpt: excerpt(lines[line - 1]), detector: DETECTOR8 });
|
|
184721
185048
|
}
|
|
184722
185049
|
}
|
|
184723
185050
|
|
|
@@ -184771,7 +185098,10 @@ function detectProcesses(input, roots) {
|
|
|
184771
185098
|
continue;
|
|
184772
185099
|
if (json.workspaces && !start && !bin)
|
|
184773
185100
|
continue;
|
|
184774
|
-
const
|
|
185101
|
+
const runsOnBun = /^\s*bun\s+(?:run\s+|--\S+\s+)*[\w./-]+\.[cm]?[jt]sx?\b/.test(start ?? asString(scripts.dev) ?? "");
|
|
185102
|
+
const rt = upsert(map, runsOnBun ? "other_process" : "node_process", pkg.root, pkg.path);
|
|
185103
|
+
if (runsOnBun)
|
|
185104
|
+
rt.language = "typescript";
|
|
184775
185105
|
const pkgFile = ctx.byPath.get(pkg.path);
|
|
184776
185106
|
rt.evidence.push(evidenceAt(pkg.path, pkgFile?.text ?? "", findLine(pkgFile?.text ?? "", /"(start|bin|main)"/), DETECTOR9));
|
|
184777
185107
|
const trigger = server2 ? "http" : bin ? "cli" : "unknown";
|
|
@@ -184956,6 +185286,10 @@ function pythonFile(file, map, roots) {
|
|
|
184956
185286
|
hits.push({ entry: { name: `mcp ${mod}:${m[1]}`, handler: `${file.path}:${m[1]}`, command: ["python", fromRoot(file.path, root)], trigger: /sse|streamable_http/.test(text) ? "http" : "cli" }, offset: m.index, detector: `${DETECTOR9}.mcp` });
|
|
184957
185287
|
}
|
|
184958
185288
|
}
|
|
185289
|
+
const agentcore = /\bBedrockAgentCoreApp\s*\(/.exec(text);
|
|
185290
|
+
if (agentcore && /bedrock_agentcore/.test(text)) {
|
|
185291
|
+
hits.push({ entry: { name: `agentcore ${file.path}`, handler: file.path, command: ["python", rel], trigger: "http" }, offset: agentcore.index, detector: `${DETECTOR9}.agentcore` });
|
|
185292
|
+
}
|
|
184959
185293
|
const main = /^if\s+__name__\s*==\s*['"]__main__['"]\s*:/m.exec(text);
|
|
184960
185294
|
if (main && hits.length === 0) {
|
|
184961
185295
|
const serves = /uvicorn\.run|\.run\(\s*(?:host|port|debug)|app\.run\(|web\.run_app|serve_forever/.test(text);
|
|
@@ -184972,10 +185306,24 @@ function pythonFile(file, map, roots) {
|
|
|
184972
185306
|
}
|
|
184973
185307
|
}
|
|
184974
185308
|
}
|
|
185309
|
+
var SUPABASE_EDGE = /^(.*?supabase\/functions)\/([^_./][^/]*)\/index\.[cm]?[jt]s$/;
|
|
184975
185310
|
function nodeFile(file, map, roots) {
|
|
184976
185311
|
const text = file.text;
|
|
185312
|
+
const edge = SUPABASE_EDGE.exec(file.path);
|
|
185313
|
+
if (edge) {
|
|
185314
|
+
const rt2 = upsert(map, "other_process", edge[1]);
|
|
185315
|
+
rt2.language = "typescript";
|
|
185316
|
+
rt2.frameworks = [...new Set([...rt2.frameworks ?? [], "supabase-edge-functions"])].sort();
|
|
185317
|
+
addEntry(rt2, { name: `edge ${edge[2]}`, handler: file.path, command: ["supabase", "functions", "serve", edge[2]], trigger: "http" });
|
|
185318
|
+
if (rt2.evidence.length < 5)
|
|
185319
|
+
rt2.evidence.push(evidenceAt(file.path, text, 1, `${DETECTOR9}.supabase_edge`));
|
|
185320
|
+
return;
|
|
185321
|
+
}
|
|
184977
185322
|
const hits = [];
|
|
184978
185323
|
let m;
|
|
185324
|
+
if (/bedrock-agentcore/.test(text) && (m = /\bBedrockAgentCoreApp\b|\.entrypoint\s*\(/.exec(text))) {
|
|
185325
|
+
hits.push({ entry: { name: `agentcore ${file.path}`, handler: file.path, trigger: "http" }, offset: m.index, detector: `${DETECTOR9}.agentcore` });
|
|
185326
|
+
}
|
|
184979
185327
|
if (/@modelcontextprotocol\/sdk/.test(text) && (m = /new\s+(McpServer|Server)\s*\(/.exec(text))) {
|
|
184980
185328
|
const http = /StreamableHTTPServerTransport|SSEServerTransport/.test(text);
|
|
184981
185329
|
hits.push({ entry: { name: `mcp ${file.path}`, handler: file.path, trigger: http ? "http" : "cli" }, offset: m.index, detector: `${DETECTOR9}.mcp` });
|
|
@@ -184985,10 +185333,15 @@ function nodeFile(file, map, roots) {
|
|
|
184985
185333
|
}
|
|
184986
185334
|
if (m = /\b(?:app|server|fastify)\.listen\s*\(|\bserve\(\s*\{\s*fetch|\bBun\.serve\s*\(|\bDeno\.serve\s*\(|\bcreateServer\s*\([^)]*\)\s*\.listen/.exec(text)) {
|
|
184987
185335
|
hits.push({ entry: { name: `listen ${file.path}`, handler: file.path, trigger: "http" }, offset: m.index, detector: `${DETECTOR9}.listen` });
|
|
185336
|
+
const bun = /\bBun\.serve\s*\(/.exec(text);
|
|
185337
|
+
if (bun && /\bwebsocket\s*[:,}]/.test(callArgs(text, bun.index + bun[0].length - 1, 50000))) {
|
|
185338
|
+
hits.push({ entry: { name: `websocket ${file.path}`, handler: file.path, trigger: "websocket" }, offset: bun.index, detector: `${DETECTOR9}.listen` });
|
|
185339
|
+
}
|
|
184988
185340
|
}
|
|
184989
185341
|
if (hits.length === 0)
|
|
184990
185342
|
return;
|
|
184991
|
-
const
|
|
185343
|
+
const kind = /\bBun\.serve\s*\(/.test(text) ? "other_process" : "node_process";
|
|
185344
|
+
const rt = upsert(map, kind, nearestRoot(file.path, roots));
|
|
184992
185345
|
rt.language = languageFromPath(file.path) ?? rt.language;
|
|
184993
185346
|
const at = lineIndex(text);
|
|
184994
185347
|
const lines = splitLines(text);
|
|
@@ -185068,7 +185421,9 @@ function detectVercel(input) {
|
|
|
185068
185421
|
if (info.next && /^(?:src\/)?app\/(?:.*\/)?route\.[cm]?[jt]sx?$/.test(r)) {
|
|
185069
185422
|
const methods = exportedMethods(f.text);
|
|
185070
185423
|
const path = routePath(r.replace(/^src\//, "").replace(/^app\//, ""), "app");
|
|
185071
|
-
entries.push({ name: `${methods.join(",")
|
|
185424
|
+
entries.push({ name: `${methods.length > 0 ? methods.join(",") : "ANY"} ${path}`, handler: f.path, trigger: "http" });
|
|
185425
|
+
} else if (info.next && /^(?:src\/)?middleware\.[cm]?[jt]s$/.test(r)) {
|
|
185426
|
+
entries.push({ name: "middleware", handler: `${f.path}#middleware`, trigger: "http" });
|
|
185072
185427
|
} else if (info.next && /^(?:src\/)?pages\/api\/.+\.[cm]?[jt]sx?$/.test(r)) {
|
|
185073
185428
|
entries.push({ name: `ANY ${routePath(r.replace(/^src\//, "").replace(/^pages\//, ""), "pages")}`, handler: f.path, trigger: "http" });
|
|
185074
185429
|
} else if (!info.next && /^api\/.+\.([cm]?[jt]s|py|go)$/.test(r) && !/\/_/.test(r)) {
|
|
@@ -185097,11 +185452,277 @@ function detectVercel(input) {
|
|
|
185097
185452
|
function rel(path, root) {
|
|
185098
185453
|
return root === "." ? path : path.slice(root.length + 1);
|
|
185099
185454
|
}
|
|
185455
|
+
|
|
185456
|
+
// ../../packages/harness-bundle/src/detect/scope.ts
|
|
185457
|
+
var DEPLOY_CONFIG = /^(?:wrangler\.(?:toml|json|jsonc)|template\.ya?ml|serverless\.ya?ml|vercel\.json|firebase\.json|cloudbuild\.ya?ml|service\.ya?ml|cdk\.json|langgraph\.json|Procfile|render\.ya?ml|fly\.toml|railway\.(?:json|toml)|netlify\.toml|(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml)$/;
|
|
185458
|
+
var PROJECT_MANIFEST2 = /^(?:package\.json|pyproject\.toml|requirements\.txt|go\.mod|Cargo\.toml|Gemfile)$/;
|
|
185459
|
+
var ANCESTOR_ORM = [
|
|
185460
|
+
{ uses: /@prisma\/client|\bPrismaClient\b/, files: /^(?:prisma\/.+|[\w.-]+\.prisma|(?:\.config\/)?prisma(?:\.config)?\.[cm]?[jt]s)$/ },
|
|
185461
|
+
{ uses: /\bdrizzle-orm\b/, files: /^drizzle\.config\.[cm]?[jt]s$/ },
|
|
185462
|
+
{ uses: /\b(?:sqlalchemy|sqlmodel|alembic)\b/, files: /^(?:alembic\.ini|alembic\/.+)$/ }
|
|
185463
|
+
];
|
|
185464
|
+
var CODE_FILE = /\.(?:[cm]?[jt]sx?|py)$/;
|
|
185465
|
+
function pyDependencyNames(doc) {
|
|
185466
|
+
const out = [];
|
|
185467
|
+
const project = isRecord2(doc.project) ? doc.project : {};
|
|
185468
|
+
for (const req of asArray(project.dependencies)) {
|
|
185469
|
+
const m = typeof req === "string" ? /^\s*([A-Za-z0-9][\w.-]*)/.exec(req) : null;
|
|
185470
|
+
if (m)
|
|
185471
|
+
out.push(m[1]);
|
|
185472
|
+
}
|
|
185473
|
+
const poetry = isRecord2(doc.tool) && isRecord2(doc.tool.poetry) ? doc.tool.poetry : {};
|
|
185474
|
+
if (isRecord2(poetry.dependencies))
|
|
185475
|
+
out.push(...Object.keys(poetry.dependencies));
|
|
185476
|
+
return out.map(pyName);
|
|
185477
|
+
}
|
|
185478
|
+
function pyName(name) {
|
|
185479
|
+
return name.toLowerCase().replace(/[_.]/g, "-");
|
|
185480
|
+
}
|
|
185481
|
+
function npmDeps(json) {
|
|
185482
|
+
const out = [];
|
|
185483
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
185484
|
+
const deps = json[field];
|
|
185485
|
+
if (isRecord2(deps))
|
|
185486
|
+
out.push(...Object.keys(deps));
|
|
185487
|
+
}
|
|
185488
|
+
return out;
|
|
185489
|
+
}
|
|
185490
|
+
function tomlDoc(file) {
|
|
185491
|
+
if (!file)
|
|
185492
|
+
return;
|
|
185493
|
+
try {
|
|
185494
|
+
return parse(file.text);
|
|
185495
|
+
} catch {
|
|
185496
|
+
return;
|
|
185497
|
+
}
|
|
185498
|
+
}
|
|
185499
|
+
function directDeps(dir, byPath, npmNames, pyNames, goModules, workspaces) {
|
|
185500
|
+
const out = [];
|
|
185501
|
+
const pkg = byPath.get(joinPath(dir, "package.json"));
|
|
185502
|
+
if (pkg) {
|
|
185503
|
+
const json = parseJsonLoose(pkg.text);
|
|
185504
|
+
if (isRecord2(json)) {
|
|
185505
|
+
for (const name of npmDeps(json)) {
|
|
185506
|
+
const target = npmNames.get(name);
|
|
185507
|
+
if (target !== undefined)
|
|
185508
|
+
out.push(target);
|
|
185509
|
+
}
|
|
185510
|
+
}
|
|
185511
|
+
}
|
|
185512
|
+
const pyproject = tomlDoc(byPath.get(joinPath(dir, "pyproject.toml")));
|
|
185513
|
+
if (pyproject) {
|
|
185514
|
+
const tool = isRecord2(pyproject.tool) ? pyproject.tool : {};
|
|
185515
|
+
const uv = isRecord2(tool.uv) ? tool.uv : {};
|
|
185516
|
+
const sources = isRecord2(uv.sources) ? uv.sources : {};
|
|
185517
|
+
for (const [name, src] of Object.entries(sources)) {
|
|
185518
|
+
if (!isRecord2(src))
|
|
185519
|
+
continue;
|
|
185520
|
+
if (src.workspace === true) {
|
|
185521
|
+
const target = pyNames.get(pyName(name));
|
|
185522
|
+
if (target !== undefined)
|
|
185523
|
+
out.push(target);
|
|
185524
|
+
}
|
|
185525
|
+
const path = asString(src.path);
|
|
185526
|
+
if (path)
|
|
185527
|
+
out.push(joinPath(dir, path));
|
|
185528
|
+
}
|
|
185529
|
+
for (const ws of workspaces) {
|
|
185530
|
+
if (!isUnder(dir, ws.root))
|
|
185531
|
+
continue;
|
|
185532
|
+
for (const name of pyDependencyNames(pyproject)) {
|
|
185533
|
+
const target = ws.sources.get(name);
|
|
185534
|
+
if (target !== undefined)
|
|
185535
|
+
out.push(target);
|
|
185536
|
+
}
|
|
185537
|
+
}
|
|
185538
|
+
const poetry = isRecord2(tool.poetry) ? tool.poetry : {};
|
|
185539
|
+
const groups = [poetry.dependencies, ...isRecord2(poetry.group) ? Object.values(poetry.group).map((g) => isRecord2(g) ? g.dependencies : undefined) : []];
|
|
185540
|
+
for (const deps of groups) {
|
|
185541
|
+
if (!isRecord2(deps))
|
|
185542
|
+
continue;
|
|
185543
|
+
for (const spec of Object.values(deps)) {
|
|
185544
|
+
const path = isRecord2(spec) ? asString(spec.path) : undefined;
|
|
185545
|
+
if (path)
|
|
185546
|
+
out.push(joinPath(dir, path));
|
|
185547
|
+
}
|
|
185548
|
+
}
|
|
185549
|
+
}
|
|
185550
|
+
for (const name of ["requirements.txt", "requirements.in"]) {
|
|
185551
|
+
const req = byPath.get(joinPath(dir, name));
|
|
185552
|
+
if (!req)
|
|
185553
|
+
continue;
|
|
185554
|
+
for (const line of splitLines(req.text)) {
|
|
185555
|
+
const m = /^\s*(?:-e\s+)?(\.{1,2}\/[^\s#;]+)/.exec(line);
|
|
185556
|
+
if (m)
|
|
185557
|
+
out.push(joinPath(dir, m[1]));
|
|
185558
|
+
}
|
|
185559
|
+
}
|
|
185560
|
+
const gomod = byPath.get(joinPath(dir, "go.mod"));
|
|
185561
|
+
if (gomod) {
|
|
185562
|
+
for (const m of gomod.text.matchAll(/=>\s*(\.{1,2}\/\S+)/g))
|
|
185563
|
+
out.push(joinPath(dir, m[1]));
|
|
185564
|
+
for (const m of gomod.text.matchAll(/^\s*(?:require\s+)?([\w.-]+\.[\w./-]+)\s+v[\w.+-]+/gm)) {
|
|
185565
|
+
const target = goModules.get(m[1]);
|
|
185566
|
+
if (target !== undefined)
|
|
185567
|
+
out.push(target);
|
|
185568
|
+
}
|
|
185569
|
+
const own = /^module\s+(\S+)/m.exec(gomod.text)?.[1];
|
|
185570
|
+
const others = [...goModules].filter(([mod]) => mod !== own);
|
|
185571
|
+
if (others.length > 0) {
|
|
185572
|
+
for (const f of byPath.values()) {
|
|
185573
|
+
if (!f.path.endsWith(".go") || !isUnder(f.path, dir))
|
|
185574
|
+
continue;
|
|
185575
|
+
for (const [mod, target] of others)
|
|
185576
|
+
if (f.text.includes(`"${mod}"`) || f.text.includes(`"${mod}/`))
|
|
185577
|
+
out.push(target);
|
|
185578
|
+
}
|
|
185579
|
+
}
|
|
185580
|
+
}
|
|
185581
|
+
return out;
|
|
185582
|
+
}
|
|
185583
|
+
function composeBuildsUnder(svc, base, root) {
|
|
185584
|
+
if (!isRecord2(svc) || svc.build === undefined)
|
|
185585
|
+
return false;
|
|
185586
|
+
const build = svc.build;
|
|
185587
|
+
const context = joinPath(base, typeof build === "string" ? build : isRecord2(build) ? asString(build.context) ?? "." : ".");
|
|
185588
|
+
if (isUnder(context, root))
|
|
185589
|
+
return true;
|
|
185590
|
+
const dockerfile = isRecord2(build) ? asString(build.dockerfile) : undefined;
|
|
185591
|
+
return dockerfile !== undefined && isUnder(joinPath(context, dockerfile), root);
|
|
185592
|
+
}
|
|
185593
|
+
function computeScope(files, root) {
|
|
185594
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
185595
|
+
if (root === ".")
|
|
185596
|
+
return { root, closure: [], configs: [] };
|
|
185597
|
+
const npmNames = new Map;
|
|
185598
|
+
const pyNames = new Map;
|
|
185599
|
+
const goModules = new Map;
|
|
185600
|
+
for (const f of files) {
|
|
185601
|
+
const name = basename2(f.path);
|
|
185602
|
+
if (name === "package.json") {
|
|
185603
|
+
const json = parseJsonLoose(f.text);
|
|
185604
|
+
if (isRecord2(json) && typeof json.name === "string")
|
|
185605
|
+
npmNames.set(json.name, dirname2(f.path));
|
|
185606
|
+
} else if (name === "pyproject.toml") {
|
|
185607
|
+
const doc = tomlDoc(f);
|
|
185608
|
+
const project = doc && isRecord2(doc.project) ? doc.project : undefined;
|
|
185609
|
+
const poetry = doc && isRecord2(doc.tool) && isRecord2(doc.tool.poetry) ? doc.tool.poetry : undefined;
|
|
185610
|
+
const projectName = asString(project?.name) ?? asString(poetry?.name);
|
|
185611
|
+
if (projectName)
|
|
185612
|
+
pyNames.set(pyName(projectName), dirname2(f.path));
|
|
185613
|
+
} else if (name === "go.mod") {
|
|
185614
|
+
const mod = /^module\s+(\S+)/m.exec(f.text)?.[1];
|
|
185615
|
+
if (mod)
|
|
185616
|
+
goModules.set(mod, dirname2(f.path));
|
|
185617
|
+
}
|
|
185618
|
+
}
|
|
185619
|
+
const workspaces = [];
|
|
185620
|
+
for (const f of files) {
|
|
185621
|
+
if (basename2(f.path) !== "pyproject.toml")
|
|
185622
|
+
continue;
|
|
185623
|
+
const doc = tomlDoc(f);
|
|
185624
|
+
const uv = doc && isRecord2(doc.tool) && isRecord2(doc.tool.uv) ? doc.tool.uv : undefined;
|
|
185625
|
+
if (!uv || !isRecord2(uv.workspace))
|
|
185626
|
+
continue;
|
|
185627
|
+
const base = dirname2(f.path);
|
|
185628
|
+
const members = asArray(uv.workspace.members).filter((m) => typeof m === "string").map((m) => globRegex(joinPath(base, m)));
|
|
185629
|
+
if (!members.some((re) => re.test(root) || [...pyNames.values()].some((d) => isUnder(root, d) && re.test(d))))
|
|
185630
|
+
continue;
|
|
185631
|
+
const sources = new Map;
|
|
185632
|
+
for (const [name, src] of Object.entries(isRecord2(uv.sources) ? uv.sources : {})) {
|
|
185633
|
+
if (!isRecord2(src))
|
|
185634
|
+
continue;
|
|
185635
|
+
const target = src.workspace === true ? pyNames.get(pyName(name)) : asString(src.path) ? joinPath(base, asString(src.path)) : undefined;
|
|
185636
|
+
if (target !== undefined)
|
|
185637
|
+
sources.set(pyName(name), target);
|
|
185638
|
+
}
|
|
185639
|
+
workspaces.push({ root: base, sources });
|
|
185640
|
+
}
|
|
185641
|
+
const MANIFEST = /^(?:package\.json|pyproject\.toml|requirements\.(?:txt|in)|go\.mod)$/;
|
|
185642
|
+
const nested = files.filter((f) => MANIFEST.test(basename2(f.path)) && isUnder(f.path, root) && !/(^|\/)node_modules\//.test(f.path)).map((f) => dirname2(f.path));
|
|
185643
|
+
const closure = new Set;
|
|
185644
|
+
const queue = [...new Set([root, ...nested])];
|
|
185645
|
+
while (queue.length > 0) {
|
|
185646
|
+
const dir = queue.shift();
|
|
185647
|
+
for (const dep of directDeps(dir, byPath, npmNames, pyNames, goModules, workspaces)) {
|
|
185648
|
+
if (dep === root || isUnder(dep, root) || closure.has(dep) || dep === ".")
|
|
185649
|
+
continue;
|
|
185650
|
+
closure.add(dep);
|
|
185651
|
+
queue.push(dep);
|
|
185652
|
+
}
|
|
185653
|
+
}
|
|
185654
|
+
const owned = (p2) => isUnder(p2, root) || [...closure].some((c) => isUnder(p2, c));
|
|
185655
|
+
const ancestors = [];
|
|
185656
|
+
for (let d = dirname2(root);; d = dirname2(d)) {
|
|
185657
|
+
ancestors.push(d);
|
|
185658
|
+
if (d === ".")
|
|
185659
|
+
break;
|
|
185660
|
+
}
|
|
185661
|
+
const harnessFiles = files.filter((f) => owned(f.path));
|
|
185662
|
+
const configs = new Set;
|
|
185663
|
+
if (!harnessFiles.some((f) => dirname2(f.path) === root && PROJECT_MANIFEST2.test(basename2(f.path)))) {
|
|
185664
|
+
const dir = ancestors.find((a) => files.some((f) => dirname2(f.path) === a && PROJECT_MANIFEST2.test(basename2(f.path))));
|
|
185665
|
+
for (const f of files)
|
|
185666
|
+
if (dir !== undefined && dirname2(f.path) === dir && PROJECT_MANIFEST2.test(basename2(f.path)))
|
|
185667
|
+
configs.add(f.path);
|
|
185668
|
+
}
|
|
185669
|
+
const users = [...harnessFiles, ...[...configs].map((p2) => byPath.get(p2))];
|
|
185670
|
+
const relToRoot = (p2) => p2.slice(root.length + 1);
|
|
185671
|
+
const orms = ANCESTOR_ORM.filter((o) => users.some((f) => (CODE_FILE.test(f.path) || PROJECT_MANIFEST2.test(basename2(f.path))) && o.uses.test(f.text)) && !files.some((f) => isUnder(f.path, root) && o.files.test(relToRoot(f.path))));
|
|
185672
|
+
const ormDirs = [];
|
|
185673
|
+
for (const f of files) {
|
|
185674
|
+
if (owned(f.path))
|
|
185675
|
+
continue;
|
|
185676
|
+
const name = basename2(f.path);
|
|
185677
|
+
const base = dirname2(f.path);
|
|
185678
|
+
if (ancestors.includes(base) && isEnvFile(f.path))
|
|
185679
|
+
configs.add(f.path);
|
|
185680
|
+
for (const a of ancestors) {
|
|
185681
|
+
if (!isUnder(f.path, a))
|
|
185682
|
+
continue;
|
|
185683
|
+
const rel3 = a === "." ? f.path : f.path.slice(a.length + 1);
|
|
185684
|
+
if (!orms.some((o) => o.files.test(rel3)))
|
|
185685
|
+
continue;
|
|
185686
|
+
configs.add(f.path);
|
|
185687
|
+
if (!/^(?:drizzle\.config\.|alembic\.ini$)/.test(name))
|
|
185688
|
+
continue;
|
|
185689
|
+
if (name.startsWith("drizzle.config.") && !/\bout\s*:/.test(f.text))
|
|
185690
|
+
ormDirs.push(joinPath(a, "drizzle"));
|
|
185691
|
+
for (const m of f.text.matchAll(/\b(?:out|schema|script_location)\s*[:=]\s*['"]?([\w./-]+)/g))
|
|
185692
|
+
ormDirs.push(joinPath(a, m[1].replace(/\/?\*.*$/, "")));
|
|
185693
|
+
}
|
|
185694
|
+
if (!DEPLOY_CONFIG.test(name) || !ancestors.includes(base))
|
|
185695
|
+
continue;
|
|
185696
|
+
const rel2 = base === "." ? root : root.slice(base.length + 1);
|
|
185697
|
+
if (/compose/.test(name)) {
|
|
185698
|
+
const doc = loadYaml(f.text);
|
|
185699
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? doc.services : {};
|
|
185700
|
+
if (Object.values(services).some((svc) => composeBuildsUnder(svc, base, root)))
|
|
185701
|
+
configs.add(f.path);
|
|
185702
|
+
} else if (new RegExp(`(?:^|[\\s"'=:/])\\.?/?${rel2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[/"'\\s]|$)`, "m").test(f.text)) {
|
|
185703
|
+
configs.add(f.path);
|
|
185704
|
+
}
|
|
185705
|
+
}
|
|
185706
|
+
for (const f of files)
|
|
185707
|
+
if (!owned(f.path) && ormDirs.some((d) => d !== "." && (f.path === d || isUnder(f.path, d))))
|
|
185708
|
+
configs.add(f.path);
|
|
185709
|
+
return { root, closure: [...closure].sort(), configs: [...configs].sort() };
|
|
185710
|
+
}
|
|
185711
|
+
var configSets = new WeakMap;
|
|
185712
|
+
function inScope(path, scope) {
|
|
185713
|
+
if (scope.root === ".")
|
|
185714
|
+
return true;
|
|
185715
|
+
let configs = configSets.get(scope);
|
|
185716
|
+
if (!configs)
|
|
185717
|
+
configSets.set(scope, configs = new Set(scope.configs));
|
|
185718
|
+
return isUnder(path, scope.root) || scope.closure.some((c) => isUnder(path, c)) || configs.has(path);
|
|
185719
|
+
}
|
|
185100
185720
|
// ../../packages/harness-bundle/src/detect/index.ts
|
|
185101
185721
|
var DETECTOR_NAME = "@moda/harness-bundle/detect";
|
|
185102
185722
|
var DETECTOR_VERSION = "0.1.0";
|
|
185103
185723
|
var SERVER_FRAMEWORKS = new Set(FRAMEWORKS.filter((f) => f.kind === "server").map((f) => f.name));
|
|
185104
185724
|
var MIGRATION_ENGINE_PROVIDERS = new Set(["postgres", "mysql", "sqlite", "libsql", "mssql", "clickhouse", "cloudflare.d1", "supabase", "mongodb"]);
|
|
185725
|
+
var CLOSURE_CAPABILITIES = new Set(["database", "document", "kv", "vector_store", "object_store", "queue"]);
|
|
185105
185726
|
var PLATFORM_KINDS = new Set(["cloudflare_workers", "aws_lambda", "gcp_cloud_functions", "gcp_cloud_run", "azure_functions", "vercel_functions"]);
|
|
185106
185727
|
var PROCESS_KINDS = new Set(["node_process", "python_process", "other_process"]);
|
|
185107
185728
|
function packageSignals(ctx, declared) {
|
|
@@ -185122,6 +185743,237 @@ function packageSignals(ctx, declared) {
|
|
|
185122
185743
|
});
|
|
185123
185744
|
}
|
|
185124
185745
|
}
|
|
185746
|
+
function normalizeRoot(raw) {
|
|
185747
|
+
if (!raw)
|
|
185748
|
+
return ".";
|
|
185749
|
+
const root = joinPath(raw);
|
|
185750
|
+
return root === "" ? "." : root;
|
|
185751
|
+
}
|
|
185752
|
+
var RUNTIME_CONFIG = /^(?:wrangler\.(?:toml|jsonc?)|template\.ya?ml|serverless\.ya?ml|host\.json|vercel\.json|next\.config\.\w+|Dockerfile[\w.-]*|(?:docker-)?compose[\w.-]*\.ya?ml|langgraph\.json|firebase\.json|cdk\.json)$/;
|
|
185753
|
+
function outsideContext(files, scope) {
|
|
185754
|
+
const configs = files.filter((f) => RUNTIME_CONFIG.test(basename2(f.path))).map((f) => f.path);
|
|
185755
|
+
const providers = new Set;
|
|
185756
|
+
for (const d of collectDeclaredPackages(files)) {
|
|
185757
|
+
const m = matchModule(d.eco === "pypi" ? "pypi" : d.eco, d.name);
|
|
185758
|
+
if (m && !d.dev)
|
|
185759
|
+
providers.add(m.signature.provider);
|
|
185760
|
+
}
|
|
185761
|
+
const list = [...providers].sort();
|
|
185762
|
+
return {
|
|
185763
|
+
kind: "outside_harness_root",
|
|
185764
|
+
message: `${files.length} file(s) outside harness root ${scope.root}${scope.closure.length > 0 ? ` and its workspace dependencies (${scope.closure.join(", ")})` : ""} were not analyzed` + (configs.length > 0 ? `; runtime configs there: ${configs.slice(0, 5).join(", ")}${configs.length > 5 ? `, +${configs.length - 5} more` : ""}` : "") + (list.length > 0 ? `; providers declared there: ${list.slice(0, 10).join(", ")}${list.length > 10 ? `, +${list.length - 10} more` : ""}` : ""),
|
|
185765
|
+
evidence: configs.slice(0, 3).map((path) => ({ path, detector: "scope" }))
|
|
185766
|
+
};
|
|
185767
|
+
}
|
|
185768
|
+
function scopeRuntimes(ctx, runtimes, scope) {
|
|
185769
|
+
if (scope.root === ".")
|
|
185770
|
+
return runtimes;
|
|
185771
|
+
const out = [];
|
|
185772
|
+
for (const rt of runtimes) {
|
|
185773
|
+
if (isUnder(rt.root, scope.root)) {
|
|
185774
|
+
out.push(rt);
|
|
185775
|
+
continue;
|
|
185776
|
+
}
|
|
185777
|
+
if (!isUnder(scope.root, rt.root))
|
|
185778
|
+
continue;
|
|
185779
|
+
const pointsIn = (ep) => [ep.handler ?? "", ...ep.command ?? []].some((p2) => isUnder(joinPath(rt.root, p2.split(/[#:]/)[0]), scope.root) || isUnder(p2.split(/[#:]/)[0], scope.root));
|
|
185780
|
+
const entrypoints = rt.entrypoints.filter(pointsIn);
|
|
185781
|
+
if (entrypoints.length === 0)
|
|
185782
|
+
continue;
|
|
185783
|
+
const kept = new Set(entrypoints.map((e) => e.name));
|
|
185784
|
+
let bindings = rt.bindings;
|
|
185785
|
+
for (const [template, refs] of ctx.functionRefs) {
|
|
185786
|
+
if (!rt.configFiles.includes(template))
|
|
185787
|
+
continue;
|
|
185788
|
+
const dropped = [...refs.keys()].filter((fn) => !kept.has(fn));
|
|
185789
|
+
const allowed = new Set([...refs.entries()].filter(([fn]) => kept.has(fn)).flatMap(([, ids]) => [...ids]));
|
|
185790
|
+
ctx.env = ctx.env.filter((e) => !dropped.some((fn) => e.source === `${template} ${fn}`));
|
|
185791
|
+
ctx.signals = ctx.signals.filter((s) => s.kind !== "binding" || s.evidence.path !== template || allowed.has(s.binding ?? ""));
|
|
185792
|
+
bindings = bindings.filter((b) => allowed.has(b.name));
|
|
185793
|
+
}
|
|
185794
|
+
out.push({ ...rt, entrypoints, bindings });
|
|
185795
|
+
}
|
|
185796
|
+
return out;
|
|
185797
|
+
}
|
|
185798
|
+
function scopeComposeImages(ctx, scope) {
|
|
185799
|
+
const optional = new Set;
|
|
185800
|
+
for (const f of ctx.files) {
|
|
185801
|
+
if (!/^(?:docker-)?compose(?:\.[\w-]+)*\.ya?ml$/.test(basename2(f.path)))
|
|
185802
|
+
continue;
|
|
185803
|
+
const base = f.path.includes("/") ? f.path.slice(0, f.path.lastIndexOf("/")) : ".";
|
|
185804
|
+
if (base === scope.root)
|
|
185805
|
+
continue;
|
|
185806
|
+
const doc = loadYaml(f.text);
|
|
185807
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? Object.values(doc.services) : [];
|
|
185808
|
+
const builds = services.some((svc) => isRecord2(svc) && svc.build !== undefined);
|
|
185809
|
+
if (!builds)
|
|
185810
|
+
optional.add(f.path);
|
|
185811
|
+
}
|
|
185812
|
+
for (const s of ctx.signals)
|
|
185813
|
+
if (s.kind === "image" && optional.has(s.evidence.path))
|
|
185814
|
+
s.weak = true;
|
|
185815
|
+
if (scope.root === ".")
|
|
185816
|
+
return ctx.signals;
|
|
185817
|
+
const allowed = new Map;
|
|
185818
|
+
for (const path of scope.configs) {
|
|
185819
|
+
if (!/compose/.test(basename2(path)))
|
|
185820
|
+
continue;
|
|
185821
|
+
const doc = loadYaml(ctx.byPath.get(path)?.text ?? "");
|
|
185822
|
+
const services = isRecord2(doc) && isRecord2(doc.services) ? doc.services : {};
|
|
185823
|
+
const base = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : ".";
|
|
185824
|
+
const deps = new Set;
|
|
185825
|
+
let declared = false;
|
|
185826
|
+
for (const svc of Object.values(services)) {
|
|
185827
|
+
if (!isRecord2(svc) || !composeBuildsUnder(svc, base, scope.root))
|
|
185828
|
+
continue;
|
|
185829
|
+
const names = [...Array.isArray(svc.depends_on) ? svc.depends_on : isRecord2(svc.depends_on) ? Object.keys(svc.depends_on) : [], ...Array.isArray(svc.links) ? svc.links : []];
|
|
185830
|
+
for (const n of names)
|
|
185831
|
+
if (typeof n === "string") {
|
|
185832
|
+
declared = true;
|
|
185833
|
+
deps.add(n.split(":")[0]);
|
|
185834
|
+
}
|
|
185835
|
+
}
|
|
185836
|
+
if (declared)
|
|
185837
|
+
allowed.set(path, deps);
|
|
185838
|
+
}
|
|
185839
|
+
ctx.env = ctx.env.filter((e) => e.service === undefined || e.scopeRoot !== undefined || !e.evidence || !allowed.has(e.evidence.path) || allowed.get(e.evidence.path).has(e.service));
|
|
185840
|
+
return ctx.signals.filter((s) => s.kind !== "image" || !allowed.has(s.evidence.path) || allowed.get(s.evidence.path).has(s.resource ?? ""));
|
|
185841
|
+
}
|
|
185842
|
+
var SERVERLESS_KINDS = new Set(["cloudflare_workers", "aws_lambda", "gcp_cloud_functions", "azure_functions", "vercel_functions"]);
|
|
185843
|
+
function isAuxiliary(path, harnessRoot) {
|
|
185844
|
+
if (!isUnder(path, harnessRoot))
|
|
185845
|
+
return false;
|
|
185846
|
+
const rel2 = harnessRoot === "." ? path : path === harnessRoot ? "." : path.slice(harnessRoot.length + 1);
|
|
185847
|
+
return AUXILIARY_DIR.test(rel2);
|
|
185848
|
+
}
|
|
185849
|
+
var AUXILIARY_DIR = /(^|\/)(vendor|third_party|examples?|samples?|demos?|docs?|tests?|e2e|benchmarks?|fixtures|\.devops|\.github|cloud-deployments|hack|playground)(\/|$)/;
|
|
185850
|
+
function foldNestedRuntimes(ctx, runtimes, harnessRoot) {
|
|
185851
|
+
const rel2 = (root) => harnessRoot === "." ? root : root === harnessRoot ? "." : root.slice(harnessRoot.length + 1);
|
|
185852
|
+
const auxiliary = runtimes.filter((r) => isUnder(r.root, harnessRoot) && AUXILIARY_DIR.test(rel2(r.root)));
|
|
185853
|
+
if (auxiliary.length > 0) {
|
|
185854
|
+
ctx.gaps.push({
|
|
185855
|
+
kind: "auxiliary_runtime",
|
|
185856
|
+
message: `${auxiliary.length} runtime(s) under example, vendored, test, or deployment-variant directories were not treated as the harness: ${auxiliary.slice(0, 5).map((r) => `${r.kind}@${r.root}`).join(", ")}${auxiliary.length > 5 ? ", ..." : ""}`,
|
|
185857
|
+
evidence: auxiliary.slice(0, 3).map((r) => r.evidence[0]).filter((e) => e !== undefined)
|
|
185858
|
+
});
|
|
185859
|
+
}
|
|
185860
|
+
let out = runtimes.filter((r) => !auxiliary.includes(r));
|
|
185861
|
+
const merge = (into, from) => {
|
|
185862
|
+
into.configFiles = uniqSorted([...into.configFiles, ...from.configFiles]);
|
|
185863
|
+
into.frameworks = uniqSorted([...into.frameworks ?? [], ...from.frameworks ?? []]);
|
|
185864
|
+
for (const ep of from.entrypoints) {
|
|
185865
|
+
const moved = ep.command && from.root !== into.root ? { ...ep, command: rebaseCommand(ep.command, from.root, into.root, ctx) } : ep;
|
|
185866
|
+
if (into.entrypoints.some((e) => e.name === moved.name && e.handler === moved.handler && (e.command ?? []).join(" ") === (moved.command ?? []).join(" ")))
|
|
185867
|
+
continue;
|
|
185868
|
+
into.entrypoints.push(moved);
|
|
185869
|
+
}
|
|
185870
|
+
into.language ??= from.language;
|
|
185871
|
+
};
|
|
185872
|
+
for (const v of out.filter((r) => r.kind === "vercel_functions")) {
|
|
185873
|
+
const container = out.find((r) => (r.kind === "container" || r.kind === "gcp_cloud_run") && r.root === v.root);
|
|
185874
|
+
if (container && !v.configFiles.some((f) => basename2(f) === "vercel.json")) {
|
|
185875
|
+
merge(container, v);
|
|
185876
|
+
out = out.filter((r) => r !== v);
|
|
185877
|
+
}
|
|
185878
|
+
}
|
|
185879
|
+
for (const p2 of out.filter((r) => PROCESS_KINDS.has(r.kind))) {
|
|
185880
|
+
const owners = out.filter((o) => o !== p2 && !PROCESS_KINDS.has(o.kind) && isUnder(p2.root, o.root) && o.root !== p2.root).sort((a, b) => b.root.length - a.root.length);
|
|
185881
|
+
const owner = owners.find((o) => o.kind === "container" || o.kind === "gcp_cloud_run" || SERVERLESS_KINDS.has(o.kind) && !p2.entrypoints.some((e) => e.trigger === "http"));
|
|
185882
|
+
if (owner) {
|
|
185883
|
+
merge(owner, p2);
|
|
185884
|
+
out = out.filter((r) => r !== p2);
|
|
185885
|
+
}
|
|
185886
|
+
}
|
|
185887
|
+
return out;
|
|
185888
|
+
}
|
|
185889
|
+
function resolveCommandHandlers(rt, ctx) {
|
|
185890
|
+
const exists = (p2) => ctx.byPath.has(p2);
|
|
185891
|
+
const under = (p2) => joinPath(rt.root, p2.replace(/^\/(?:app|usr\/src\/app|code|workspace|srv)\//, ""));
|
|
185892
|
+
for (const ep of rt.entrypoints) {
|
|
185893
|
+
if (ep.handler || !ep.command)
|
|
185894
|
+
continue;
|
|
185895
|
+
const words = ep.command.flatMap((w) => w.split(/\s+/)).filter(Boolean);
|
|
185896
|
+
for (let i = 0;i < words.length && !ep.handler; i++) {
|
|
185897
|
+
const w = words[i];
|
|
185898
|
+
const mod = /^([A-Za-z_][\w.]*):([A-Za-z_]\w*)(?:\(\))?$/.exec(w) ?? (words[i - 1] === "-m" && /^[A-Za-z_][\w.]*$/.test(w) ? [w, w, ""] : null);
|
|
185899
|
+
if (mod) {
|
|
185900
|
+
const base = mod[1].replace(/\./g, "/");
|
|
185901
|
+
const file = [`${base}.py`, `${base}/__init__.py`, `${base}/__main__.py`, `src/${base}.py`].map(under).find(exists);
|
|
185902
|
+
if (file)
|
|
185903
|
+
ep.handler = mod[2] ? `${file}:${mod[2]}` : file;
|
|
185904
|
+
continue;
|
|
185905
|
+
}
|
|
185906
|
+
if (/\.(?:py|[cm]?[jt]sx?|sh)$/.test(w)) {
|
|
185907
|
+
const file = [under(w), joinPath(w)].find(exists);
|
|
185908
|
+
if (file)
|
|
185909
|
+
ep.handler = file;
|
|
185910
|
+
}
|
|
185911
|
+
}
|
|
185912
|
+
}
|
|
185913
|
+
}
|
|
185914
|
+
var INFERRED_ENTRY = /^(?:fastapi|starlette|litestar|quart|sanic|flask) /;
|
|
185915
|
+
function dedupeEntrypoints(rt) {
|
|
185916
|
+
const handlerFile = (h) => h.split(/[#:]/)[0];
|
|
185917
|
+
const kept = rt.entrypoints.filter((e) => !e.handler || !isAuxiliary(handlerFile(e.handler), rt.root));
|
|
185918
|
+
const pool = kept.length > 0 ? kept : rt.entrypoints;
|
|
185919
|
+
const commanded = new Set(pool.filter((e) => e.command && e.handler).map((e) => `${e.trigger}:${e.handler}`));
|
|
185920
|
+
const explicit = new Set(pool.filter((e) => e.command && e.handler && !INFERRED_ENTRY.test(e.name)).map((e) => `${e.trigger}:${e.handler}`));
|
|
185921
|
+
const seen = new Set;
|
|
185922
|
+
const out = [];
|
|
185923
|
+
for (const ep of pool) {
|
|
185924
|
+
if (!ep.command && ep.handler && commanded.has(`${ep.trigger}:${ep.handler}`))
|
|
185925
|
+
continue;
|
|
185926
|
+
if (ep.handler && INFERRED_ENTRY.test(ep.name) && explicit.has(`${ep.trigger}:${ep.handler}`))
|
|
185927
|
+
continue;
|
|
185928
|
+
const key = ep.command ? `${ep.trigger}:${ep.handler ?? ""}:${ep.command.join(" ")}` : `${ep.trigger}:${ep.handler ?? ""}:${ep.name}`;
|
|
185929
|
+
if (seen.has(key))
|
|
185930
|
+
continue;
|
|
185931
|
+
seen.add(key);
|
|
185932
|
+
out.push(ep);
|
|
185933
|
+
}
|
|
185934
|
+
return out;
|
|
185935
|
+
}
|
|
185936
|
+
function scriptLocalFilter(ctx, runtimes) {
|
|
185937
|
+
const scripts = ctx.files.filter((f) => /\.(?:sh|bash)$/.test(f.path));
|
|
185938
|
+
if (scripts.length === 0)
|
|
185939
|
+
return () => false;
|
|
185940
|
+
const launched = new Set(runtimes.flatMap((r) => r.entrypoints.flatMap((e) => [...(e.command ?? []).flatMap((w) => w.split(/[\s/;&|]+/)), basename2(e.handler ?? "")])));
|
|
185941
|
+
const runners = ctx.files.filter((f) => /^(?:Dockerfile[\w.-]*|Procfile|package\.json|azure\.ya?ml)$/.test(basename2(f.path)) || !f.isDoc && !f.isTest && languageOf(f.path) !== undefined).map((f) => f.text);
|
|
185942
|
+
for (const s of scripts)
|
|
185943
|
+
if (runners.some((t) => t.includes(basename2(s.path))))
|
|
185944
|
+
launched.add(basename2(s.path));
|
|
185945
|
+
for (let changed = true;changed; ) {
|
|
185946
|
+
changed = false;
|
|
185947
|
+
for (const s of scripts) {
|
|
185948
|
+
if (!launched.has(basename2(s.path)))
|
|
185949
|
+
continue;
|
|
185950
|
+
for (const m of s.text.matchAll(/(?:^|[\s;&|])(?:source|\.)\s+["']?([^\s"';&|]+\.(?:sh|bash))/gm)) {
|
|
185951
|
+
const name = basename2(m[1]);
|
|
185952
|
+
if (!launched.has(name)) {
|
|
185953
|
+
launched.add(name);
|
|
185954
|
+
changed = true;
|
|
185955
|
+
}
|
|
185956
|
+
}
|
|
185957
|
+
}
|
|
185958
|
+
}
|
|
185959
|
+
return (path) => /\.(?:sh|bash)$/.test(path) && !launched.has(basename2(path));
|
|
185960
|
+
}
|
|
185961
|
+
function rebaseCommand(command, fromRoot2, toRoot, ctx) {
|
|
185962
|
+
const sub = toRoot === "." ? fromRoot2 : fromRoot2.slice(toRoot.length + 1);
|
|
185963
|
+
let rebased = false;
|
|
185964
|
+
const out = command.map((arg) => {
|
|
185965
|
+
const path = joinPath(fromRoot2, arg);
|
|
185966
|
+
if (/[./]/.test(arg) && !arg.startsWith("-") && !arg.startsWith("/") && (ctx.byPath.has(path) || ctx.files.some((f) => f.path.startsWith(`${path}/`)))) {
|
|
185967
|
+
rebased = true;
|
|
185968
|
+
return joinPath(sub, arg);
|
|
185969
|
+
}
|
|
185970
|
+
return arg;
|
|
185971
|
+
});
|
|
185972
|
+
if (rebased)
|
|
185973
|
+
return out;
|
|
185974
|
+
const quote = (a) => /^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`;
|
|
185975
|
+
return ["sh", "-c", `cd ${quote(sub)} && ${command.map(quote).join(" ")}`];
|
|
185976
|
+
}
|
|
185125
185977
|
function consolidateRuntimes(runtimes) {
|
|
185126
185978
|
const out = [];
|
|
185127
185979
|
const owners = runtimes.filter((r) => !PROCESS_KINDS.has(r.kind));
|
|
@@ -185151,8 +186003,17 @@ function applyCloudRun(ctx, runtimes) {
|
|
|
185151
186003
|
for (const marker of markers) {
|
|
185152
186004
|
const candidates = runtimes.filter((r) => (r.kind === "container" || PROCESS_KINDS.has(r.kind) || r.kind === "gcp_cloud_run") && isUnder(r.root, marker.root));
|
|
185153
186005
|
const exact = candidates.filter((r) => r.root === marker.root);
|
|
185154
|
-
const pool = exact.length > 0 ? exact : candidates;
|
|
186006
|
+
const pool = exact.length > 0 && (marker.services ?? 1) <= 1 ? exact : candidates;
|
|
185155
186007
|
const roots = uniqSorted(pool.map((r) => r.root));
|
|
186008
|
+
if (roots.length > 1 && (marker.services ?? 1) >= roots.length) {
|
|
186009
|
+
for (const rt of pool.filter((r) => r.kind === "container" || PROCESS_KINDS.has(r.kind))) {
|
|
186010
|
+
rt.kind = "gcp_cloud_run";
|
|
186011
|
+
if (!rt.configFiles.includes(marker.path))
|
|
186012
|
+
rt.configFiles = uniqSorted([...rt.configFiles, marker.path]);
|
|
186013
|
+
rt.evidence.push({ path: marker.path, line: marker.line, detector: "runtime.gcp.cloud_run" });
|
|
186014
|
+
}
|
|
186015
|
+
continue;
|
|
186016
|
+
}
|
|
185156
186017
|
if (roots.length > 1) {
|
|
185157
186018
|
ctx.gaps.push({
|
|
185158
186019
|
kind: "ambiguous_deploy_target",
|
|
@@ -185190,6 +186051,9 @@ function applyCloudRun(ctx, runtimes) {
|
|
|
185190
186051
|
function dedupeGaps(gaps) {
|
|
185191
186052
|
const seen = new Map;
|
|
185192
186053
|
for (const g of gaps) {
|
|
186054
|
+
g.message = redact(g.message);
|
|
186055
|
+
if (g.evidence)
|
|
186056
|
+
g.evidence = g.evidence.map((e) => ({ ...e, path: redact(e.path) }));
|
|
185193
186057
|
const key = `${g.kind}:${g.message}`;
|
|
185194
186058
|
if (!seen.has(key))
|
|
185195
186059
|
seen.set(key, g);
|
|
@@ -185199,9 +186063,13 @@ function dedupeGaps(gaps) {
|
|
|
185199
186063
|
function detectDependencies(input) {
|
|
185200
186064
|
const opts = typeof input === "string" ? { dir: input } : input;
|
|
185201
186065
|
const loaded = loadFiles(opts.dir, { maxFileBytes: opts.maxFileBytes, maxFiles: opts.maxFiles });
|
|
186066
|
+
const harnessRoot = normalizeRoot(opts.harnessRoot ?? loaded.manifest?.harness?.root);
|
|
186067
|
+
const scope = computeScope(loaded.files, harnessRoot);
|
|
186068
|
+
const scoped = loaded.files.filter((f) => inScope(f.path, scope));
|
|
186069
|
+
const outside = loaded.files.filter((f) => !inScope(f.path, scope));
|
|
185202
186070
|
const ctx = {
|
|
185203
|
-
files:
|
|
185204
|
-
byPath: new Map(
|
|
186071
|
+
files: scoped,
|
|
186072
|
+
byPath: new Map(scoped.map((f) => [f.path, f])),
|
|
185205
186073
|
manifest: loaded.manifest,
|
|
185206
186074
|
signals: [],
|
|
185207
186075
|
runtimes: [],
|
|
@@ -185209,12 +186077,28 @@ function detectDependencies(input) {
|
|
|
185209
186077
|
gaps: [],
|
|
185210
186078
|
env: [],
|
|
185211
186079
|
envLinks: [],
|
|
185212
|
-
migrationHints: []
|
|
186080
|
+
migrationHints: [],
|
|
186081
|
+
functionRefs: new Map
|
|
185213
186082
|
};
|
|
185214
|
-
|
|
185215
|
-
|
|
185216
|
-
|
|
186083
|
+
if (ctx.manifest) {
|
|
186084
|
+
const scopedEnv = [];
|
|
186085
|
+
for (const ref of ctx.manifest.env ?? []) {
|
|
186086
|
+
if (isAmbientEnv(ref.name))
|
|
186087
|
+
continue;
|
|
186088
|
+
const sources = ref.sources.filter((src) => {
|
|
186089
|
+
const path = src.split(/[: ]/)[0];
|
|
186090
|
+
return (!/[/.]/.test(path) || inScope(path, scope)) && isRuntimeEnvSource(src) && !isAuxiliary(path, harnessRoot);
|
|
186091
|
+
});
|
|
186092
|
+
if (ref.sources.length > 0 && sources.length === 0)
|
|
186093
|
+
continue;
|
|
186094
|
+
scopedEnv.push({ ...ref, sources });
|
|
186095
|
+
for (const source of sources.length > 0 ? sources : ["manifest.json"])
|
|
186096
|
+
ctx.env.push({ name: ref.name, source });
|
|
186097
|
+
}
|
|
186098
|
+
ctx.manifest = { ...ctx.manifest, env: scopedEnv };
|
|
185217
186099
|
}
|
|
186100
|
+
if (outside.length > 0)
|
|
186101
|
+
ctx.gaps.push(outsideContext(outside, scope));
|
|
185218
186102
|
if (loaded.skipped.length > 0) {
|
|
185219
186103
|
const byReason = new Map;
|
|
185220
186104
|
for (const sk of loaded.skipped) {
|
|
@@ -185233,13 +186117,25 @@ function detectDependencies(input) {
|
|
|
185233
186117
|
}
|
|
185234
186118
|
if (loaded.truncated)
|
|
185235
186119
|
ctx.gaps.push({ kind: "truncated", message: `Only the first ${ctx.files.length} files were analyzed` });
|
|
185236
|
-
const
|
|
186120
|
+
const harnessFiles = ctx.files.filter((f) => isUnder(f.path, scope.root) && !f.isDoc && !f.isTest);
|
|
186121
|
+
const harnessCode = harnessFiles.map((f) => f.text);
|
|
186122
|
+
const pyImported = new Set(harnessFiles.filter((f) => languageOf(f.path) === "python").flatMap((f) => extractImports("python", f.text).map((r) => r.spec)));
|
|
186123
|
+
const usesPackage = (d) => {
|
|
186124
|
+
if (d.eco !== "pypi") {
|
|
186125
|
+
const re = new RegExp(`['"\`]${escapeRe(d.name)}(?:/[^'"\`]*)?['"\`]`);
|
|
186126
|
+
return harnessCode.some((t) => re.test(t));
|
|
186127
|
+
}
|
|
186128
|
+
const modules = [d.name.toLowerCase().replace(/-/g, "_"), ...matchModule("pypi", d.name)?.signature.py ?? []];
|
|
186129
|
+
return [...pyImported].some((spec) => modules.some((m) => spec === m || spec.startsWith(`${m}.`)));
|
|
186130
|
+
};
|
|
186131
|
+
const declared = collectDeclaredPackages(ctx.files).filter((d) => isUnder(d.path, scope.root) || !scope.configs.includes(d.path) || usesPackage(d));
|
|
185237
186132
|
const packageJsons = parsePackageJsons(ctx.files);
|
|
185238
186133
|
packageSignals(ctx, declared);
|
|
185239
186134
|
const declaredPypi = new Set(declared.filter((d) => d.eco === "pypi").map((d) => d.name));
|
|
185240
186135
|
const workspaceNames = new Set(packageJsons.map((p2) => typeof p2.json.name === "string" ? p2.json.name : "").filter(Boolean));
|
|
185241
186136
|
const scan = scanSources(ctx, declaredPypi, workspaceNames);
|
|
185242
186137
|
scanEnv(ctx);
|
|
186138
|
+
sdkDefaultEnv(ctx, declared);
|
|
185243
186139
|
const roots = projectRoots(ctx.files);
|
|
185244
186140
|
const runtimeInput = { ctx, packageJsons, declared, frameworks: scan.frameworks };
|
|
185245
186141
|
let runtimes = [
|
|
@@ -185253,6 +186149,12 @@ function detectDependencies(input) {
|
|
|
185253
186149
|
];
|
|
185254
186150
|
runtimes = applyCloudRun(ctx, runtimes);
|
|
185255
186151
|
runtimes = consolidateRuntimes(runtimes);
|
|
186152
|
+
runtimes = foldNestedRuntimes(ctx, runtimes, harnessRoot);
|
|
186153
|
+
runtimes = scopeRuntimes(ctx, runtimes, scope);
|
|
186154
|
+
const runtimeConfigs = new Set(runtimes.flatMap((r) => r.configFiles));
|
|
186155
|
+
ctx.signals = ctx.signals.filter((s) => s.kind !== "binding" || isUnder(s.evidence.path, scope.root) || runtimeConfigs.has(s.evidence.path));
|
|
186156
|
+
ctx.signals = scopeComposeImages(ctx, scope);
|
|
186157
|
+
ctx.env = ctx.env.filter((e) => e.scopeRoot === undefined || inScope(e.scopeRoot, scope) || isUnder(scope.root, e.scopeRoot));
|
|
185256
186158
|
runtimes.sort((a, b) => cmp(a.root, b.root) || cmp(a.kind, b.kind));
|
|
185257
186159
|
for (const rt of runtimes) {
|
|
185258
186160
|
if (PLATFORM_KINDS.has(rt.kind) || rt.kind === "container" || PROCESS_KINDS.has(rt.kind)) {
|
|
@@ -185261,11 +186163,15 @@ function detectDependencies(input) {
|
|
|
185261
186163
|
if (all.length > 0)
|
|
185262
186164
|
rt.frameworks = all;
|
|
185263
186165
|
}
|
|
186166
|
+
resolveCommandHandlers(rt, ctx);
|
|
186167
|
+
if (rt.entrypoints.some((e) => !e.name.startsWith("__main__ ")))
|
|
186168
|
+
rt.entrypoints = rt.entrypoints.filter((e) => !e.name.startsWith("__main__ ") || e.trigger !== "cli");
|
|
185264
186169
|
const serves = (rt.frameworks ?? []).some((f) => SERVER_FRAMEWORKS.has(f));
|
|
185265
186170
|
for (const ep of rt.entrypoints) {
|
|
185266
186171
|
if (ep.trigger === "unknown" && (serves || /gunicorn|uvicorn|hypercorn|daphne|waitress|next start|runserver|flask run|serve/.test((ep.command ?? []).join(" "))))
|
|
185267
186172
|
ep.trigger = "http";
|
|
185268
186173
|
}
|
|
186174
|
+
rt.entrypoints = dedupeEntrypoints(rt);
|
|
185269
186175
|
for (const ep of rt.entrypoints)
|
|
185270
186176
|
if (ep.command)
|
|
185271
186177
|
ep.command = ep.command.map((arg, i) => i === 2 && ep.command[0] === "sh" && ep.command[1] === "-c" ? redactScript(arg) : redactArg(arg));
|
|
@@ -185281,7 +186187,28 @@ function detectDependencies(input) {
|
|
|
185281
186187
|
}
|
|
185282
186188
|
}
|
|
185283
186189
|
const migrations = detectMigrations(ctx);
|
|
186190
|
+
const scriptLocal = scriptLocalFilter(ctx, runtimes);
|
|
186191
|
+
ctx.env = ctx.env.filter((e) => !scriptLocal(e.source.split(/[: ]/)[0]));
|
|
186192
|
+
if (ctx.manifest?.env) {
|
|
186193
|
+
const env2 = ctx.manifest.env.flatMap((ref) => {
|
|
186194
|
+
const sources = ref.sources.filter((src) => !scriptLocal(src.split(/[: ]/)[0]));
|
|
186195
|
+
return ref.sources.length > 0 && sources.length === 0 ? [] : [{ ...ref, sources }];
|
|
186196
|
+
});
|
|
186197
|
+
ctx.manifest = { ...ctx.manifest, env: env2 };
|
|
186198
|
+
}
|
|
185284
186199
|
envSignals(ctx);
|
|
186200
|
+
const touched = new Set(ctx.signals.filter((s) => isUnder(s.evidence.path, scope.root)).map((s) => s.provider));
|
|
186201
|
+
for (const sig of ctx.signals) {
|
|
186202
|
+
if (!scope.closure.some((c) => isUnder(sig.evidence.path, c)) || sig.capability === "model.inference")
|
|
186203
|
+
continue;
|
|
186204
|
+
if (sig.kind === "host" && sig.provider === "http" && !scope.closure.some((c) => isAuxiliary(sig.evidence.path, c)))
|
|
186205
|
+
continue;
|
|
186206
|
+
if (["host", "env", "env_value", "code"].includes(sig.kind) || !CLOSURE_CAPABILITIES.has(sig.capability) && !touched.has(sig.provider))
|
|
186207
|
+
sig.weak = true;
|
|
186208
|
+
}
|
|
186209
|
+
for (const sig of ctx.signals)
|
|
186210
|
+
if (isAuxiliary(sig.evidence.path, harnessRoot))
|
|
186211
|
+
sig.weak = true;
|
|
185285
186212
|
const dependencies = mergeSignals(ctx, runtimes);
|
|
185286
186213
|
linkEnv(ctx, dependencies, runtimes);
|
|
185287
186214
|
for (const m of migrations) {
|
|
@@ -185290,6 +186217,15 @@ function detectDependencies(input) {
|
|
|
185290
186217
|
const candidates = dependencies.filter((d) => MIGRATION_ENGINE_PROVIDERS.has(d.provider) && (root === undefined || (d.runtimeRoots ?? []).includes(root)));
|
|
185291
186218
|
resolveMigrationEngines([m], candidates.map((d) => d.provider), ctx);
|
|
185292
186219
|
}
|
|
186220
|
+
const keepSource = (source) => isRuntimeEnvSource(source) && !isAuxiliary(source.split(/[: ]/)[0], harnessRoot);
|
|
186221
|
+
ctx.env = ctx.env.filter((e) => !isAmbientEnv(e.name) && keepSource(e.source));
|
|
186222
|
+
if (ctx.manifest?.env) {
|
|
186223
|
+
const env2 = ctx.manifest.env.flatMap((ref) => {
|
|
186224
|
+
const sources = ref.sources.filter(keepSource);
|
|
186225
|
+
return ref.sources.length > 0 && sources.length === 0 ? [] : [{ ...ref, sources }];
|
|
186226
|
+
});
|
|
186227
|
+
ctx.manifest = { ...ctx.manifest, env: env2 };
|
|
186228
|
+
}
|
|
185293
186229
|
const env = buildEnvReferences(ctx, dependencies);
|
|
185294
186230
|
return {
|
|
185295
186231
|
schema: DEPENDENCY_MAP_SCHEMA,
|
|
@@ -193545,19 +194481,19 @@ function assertHarnessGraphSyncSize(graph) {
|
|
|
193545
194481
|
// ../../packages/messaging/messaging.json
|
|
193546
194482
|
var messaging_default = {
|
|
193547
194483
|
$schema: "./messaging.schema.json",
|
|
193548
|
-
version: "1.0.
|
|
193549
|
-
locked_on: "2026-10-
|
|
194484
|
+
version: "1.0.4",
|
|
194485
|
+
locked_on: "2026-10-15",
|
|
193550
194486
|
approved_by: "mo",
|
|
193551
|
-
source: "Branding project docs/positioning.md v1.0.
|
|
194487
|
+
source: "Branding project docs/positioning.md v1.0.4",
|
|
193552
194488
|
canonical_strings: {
|
|
193553
|
-
version: "1.0.
|
|
193554
|
-
locked_on: "2026-10-
|
|
194489
|
+
version: "1.0.4",
|
|
194490
|
+
locked_on: "2026-10-15",
|
|
193555
194491
|
approved_by: "mo",
|
|
193556
194492
|
strings: [
|
|
193557
194493
|
{
|
|
193558
194494
|
id: "one_liner",
|
|
193559
194495
|
source: "approved",
|
|
193560
|
-
text: "
|
|
194496
|
+
text: "Infrastructure for catching AI agent mistakes in production and turning them into evals.",
|
|
193561
194497
|
match: "exact",
|
|
193562
194498
|
surfaces: [
|
|
193563
194499
|
"apps/moda-frontend/components/landing/hero.tsx (subhead under the H1)",
|
|
@@ -193617,7 +194553,7 @@ var messaging_default = {
|
|
|
193617
194553
|
{
|
|
193618
194554
|
id: "meta_description",
|
|
193619
194555
|
source: "derived",
|
|
193620
|
-
text: "Production monitoring and evals for AI agents.
|
|
194556
|
+
text: "Production monitoring and evals for AI agents. Infrastructure for catching AI agent mistakes in production and turning them into evals.",
|
|
193621
194557
|
match: "exact",
|
|
193622
194558
|
max_chars: 160,
|
|
193623
194559
|
surfaces: [
|
|
@@ -193642,7 +194578,7 @@ var messaging_default = {
|
|
|
193642
194578
|
{
|
|
193643
194579
|
id: "long_description",
|
|
193644
194580
|
source: "derived",
|
|
193645
|
-
text: "
|
|
194581
|
+
text: "Infrastructure for catching AI agent mistakes in production and turning them into evals. Agents fail in ways that never throw an error: frustrated users, failing tools, lazy or hallucinated answers. Moda reads every production trace end to end, detects these failures, and groups them into problems ranked by how many users they affect, with the traces behind each one. When your team changes a prompt, tool, or model, Moda replays the change against those traces and shows whether it worked, then keeps monitoring after release. Works with any model provider through SDKs, OpenTelemetry, or HTTP. Backed by Y Combinator.",
|
|
193646
194582
|
match: "exact",
|
|
193647
194583
|
surfaces: [
|
|
193648
194584
|
"LinkedIn About",
|
|
@@ -193756,7 +194692,7 @@ var messaging_default = {
|
|
|
193756
194692
|
]
|
|
193757
194693
|
},
|
|
193758
194694
|
banned_phrases: {
|
|
193759
|
-
version: "1.0.
|
|
194695
|
+
version: "1.0.4",
|
|
193760
194696
|
phrases: [
|
|
193761
194697
|
{
|
|
193762
194698
|
id: "fixes_your_agent",
|
|
@@ -193933,7 +194869,7 @@ var messaging_default = {
|
|
|
193933
194869
|
]
|
|
193934
194870
|
},
|
|
193935
194871
|
terminology: {
|
|
193936
|
-
version: "1.0.
|
|
194872
|
+
version: "1.0.4",
|
|
193937
194873
|
terms: [
|
|
193938
194874
|
{
|
|
193939
194875
|
use: "trace",
|
|
@@ -194935,7 +195871,7 @@ async function runHarnessCommand(context) {
|
|
|
194935
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.");
|
|
194936
195872
|
}
|
|
194937
195873
|
if (context.flags["github-actions"] === "true") {
|
|
194938
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
195874
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-301nak95.js");
|
|
194939
195875
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
194940
195876
|
writeReport: (report2) => {
|
|
194941
195877
|
const normalized = normalizeHarnessReport(report2);
|