sandbox-cli-detector
Advanced tools
| // src/sandboxes.ts | ||
| var defaultSandboxes = [ | ||
| { | ||
| id: "replit", | ||
| name: "Replit", | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }] | ||
| }, | ||
| { | ||
| id: "bolt", | ||
| name: "bolt.new", | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }] | ||
| }, | ||
| { | ||
| id: "e2b", | ||
| name: "E2B", | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }] | ||
| }, | ||
| { | ||
| id: "vercel-sandbox", | ||
| name: "Vercel Sandbox", | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }] | ||
| }, | ||
| { | ||
| id: "daytona", | ||
| name: "Daytona", | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }] | ||
| }, | ||
| { | ||
| id: "modal", | ||
| name: "Modal", | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }] | ||
| }, | ||
| { | ||
| id: "cloudflare-sandbox", | ||
| name: "Cloudflare Sandbox", | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }] | ||
| }, | ||
| { | ||
| id: "codespaces", | ||
| name: "GitHub Codespaces", | ||
| env: [{ name: "CODESPACES", value: "true" }] | ||
| }, | ||
| { | ||
| id: "codesandbox", | ||
| name: "CodeSandbox", | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }] | ||
| } | ||
| ]; | ||
| // src/index.ts | ||
| function detectSandbox(options = {}) { | ||
| const env = options.env ?? process.env; | ||
| const sandboxes = options.sandboxes ?? defaultSandboxes; | ||
| const sandbox = sandboxes.find( | ||
| (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env)) | ||
| ); | ||
| if (sandbox === void 0) return { detected: false }; | ||
| return { | ||
| detected: true, | ||
| sandbox: { id: sandbox.id, name: sandbox.name } | ||
| }; | ||
| } | ||
| function isRunningInSandbox(options = {}) { | ||
| return detectSandbox(options).detected; | ||
| } | ||
| function matchesEnvSignal(signal, env) { | ||
| const value = env[signal.name]; | ||
| return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value); | ||
| } | ||
| export { | ||
| defaultSandboxes, | ||
| detectSandbox, | ||
| isRunningInSandbox | ||
| }; | ||
| //# sourceMappingURL=chunk-HZH3SATY.js.map |
| {"version":3,"sources":["../src/sandboxes.ts","../src/index.ts"],"sourcesContent":["import type { SandboxDefinition } from \"./types.js\";\n\nexport const defaultSandboxes = [\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n },\n {\n id: \"e2b\",\n name: \"E2B\",\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n },\n {\n id: \"modal\",\n name: \"Modal\",\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n },\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n },\n] satisfies readonly SandboxDefinition[];\n","import { defaultSandboxes } from \"./sandboxes.js\";\nimport type { DetectSandboxOptions, DetectionResult, EnvSignal } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport type {\n DetectedSandbox,\n DetectionResult,\n DetectSandboxOptions,\n EnvSignal,\n SandboxDefinition,\n} from \"./types.js\";\n\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const sandbox = sandboxes.find((candidate) =>\n candidate.env.some((signal) => matchesEnvSignal(signal, env)),\n );\n\n if (sandbox === undefined) return { detected: false };\n\n return {\n detected: true,\n sandbox: { id: sandbox.id, name: sandbox.name },\n };\n}\n\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n\nfunction matchesEnvSignal(signal: EnvSignal, env: NodeJS.ProcessEnv): boolean {\n const value = env[signal.name];\n return (\n value !== undefined &&\n value !== \"\" &&\n (signal.value === undefined || value === signal.value)\n );\n}\n"],"mappings":";AAEO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,iBAAiB,GAAG,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,cAAc,CAAC;AAAA,EACzF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,cAAc,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAClF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,eAAe,OAAO,OAAO,CAAC;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,QAAQ,OAAO,uBAAuB,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,+BAA+B,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,cAAc,OAAO,OAAO,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,OAAO,OAAO,OAAO,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAAA,EAClE;AACF;;;ACpCO,SAAS,cAAc,UAAgC,CAAC,GAAoB;AACjF,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,UAAU;AAAA,IAAK,CAAC,cAC9B,UAAU,IAAI,KAAK,CAAC,WAAW,iBAAiB,QAAQ,GAAG,CAAC;AAAA,EAC9D;AAEA,MAAI,YAAY,OAAW,QAAO,EAAE,UAAU,MAAM;AAEpD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,SAAS,EAAE,IAAI,QAAQ,IAAI,MAAM,QAAQ,KAAK;AAAA,EAChD;AACF;AAEO,SAAS,mBAAmB,UAAgC,CAAC,GAAY;AAC9E,SAAO,cAAc,OAAO,EAAE;AAChC;AAEA,SAAS,iBAAiB,QAAmB,KAAiC;AAC5E,QAAM,QAAQ,IAAI,OAAO,IAAI;AAC7B,SACE,UAAU,UACV,UAAU,OACT,OAAO,UAAU,UAAa,UAAU,OAAO;AAEpD;","names":[]} |
+20
-115
@@ -6,13 +6,6 @@ #!/usr/bin/env node | ||
| var defaultSandboxes = [ | ||
| // ── AI app builders ──────────────────────────────────────────────────── | ||
| // Lovable, v0, and Base44 are intentionally absent: they expose no shell, | ||
| // so a CLI can never run inside them. | ||
| { | ||
| id: "replit", | ||
| name: "Replit", | ||
| category: "app-builder", | ||
| // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT | ||
| // are NOT present in current Replit environments. | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }], | ||
| verified: true | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }] | ||
| }, | ||
@@ -22,23 +15,8 @@ { | ||
| name: "bolt.new", | ||
| category: "app-builder", | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }], | ||
| verified: true | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }] | ||
| }, | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| category: "app-builder", | ||
| // Rork runs on E2B, so e2b also appears in matches. | ||
| env: [{ name: "RORK_API_URL" }], | ||
| verified: true | ||
| }, | ||
| // ── AI code-execution sandboxes ──────────────────────────────────────── | ||
| { | ||
| id: "e2b", | ||
| name: "E2B", | ||
| category: "ai-sandbox", | ||
| // Documented: https://e2b.dev/docs/sandbox/environment-variables | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }], | ||
| idEnv: ["E2B_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }] | ||
| }, | ||
@@ -48,7 +26,3 @@ { | ||
| name: "Vercel Sandbox", | ||
| category: "ai-sandbox", | ||
| // No dedicated marker; the microVMs run as the vercel-sandbox user. | ||
| // (PS1="▲ $PWD/" is also set, but only in interactive shells.) | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }], | ||
| verified: true | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }] | ||
| }, | ||
@@ -58,6 +32,3 @@ { | ||
| name: "Daytona", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }], | ||
| idEnv: ["DAYTONA_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }] | ||
| }, | ||
@@ -67,8 +38,3 @@ { | ||
| name: "Modal", | ||
| category: "ai-sandbox", | ||
| // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the | ||
| // documented container id (https://modal.com/docs/guide/environment_variables). | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }], | ||
| idEnv: ["MODAL_SANDBOX_ID", "MODAL_TASK_ID"], | ||
| verified: true | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }] | ||
| }, | ||
@@ -78,15 +44,8 @@ { | ||
| name: "Cloudflare Sandbox", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }], | ||
| idEnv: ["CLOUDFLARE_DURABLE_OBJECT_ID"], | ||
| verified: true | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }] | ||
| }, | ||
| // ── Cloud development environments ───────────────────────────────────── | ||
| { | ||
| id: "codespaces", | ||
| name: "GitHub Codespaces", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CODESPACES", value: "true" }], | ||
| idEnv: ["CODESPACE_NAME"], | ||
| verified: true | ||
| env: [{ name: "CODESPACES", value: "true" }] | ||
| }, | ||
@@ -96,51 +55,6 @@ { | ||
| name: "CodeSandbox", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }], | ||
| idEnv: ["CSB_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }] | ||
| } | ||
| ]; | ||
| // src/strategies.ts | ||
| function matchCondition(condition, env) { | ||
| const value = env[condition.name]; | ||
| if (value === void 0 || value === "") return null; | ||
| if (condition.value !== void 0 && value !== condition.value) return null; | ||
| return { envVar: condition.name, value }; | ||
| } | ||
| var EnvironmentDetectionStrategy = class { | ||
| id = "env"; | ||
| detect(sandbox, context) { | ||
| for (const matcher of sandbox.env) { | ||
| const conditions = Array.isArray(matcher) ? matcher : [matcher]; | ||
| if (conditions.length === 0) continue; | ||
| const evidence = []; | ||
| for (const condition of conditions) { | ||
| const matched = matchCondition(condition, context.env); | ||
| if (!matched) break; | ||
| evidence.push(matched); | ||
| } | ||
| if (evidence.length !== conditions.length) continue; | ||
| return { | ||
| id: sandbox.id, | ||
| name: sandbox.name, | ||
| category: sandbox.category, | ||
| instanceId: this.findInstanceId(sandbox, context), | ||
| evidence | ||
| }; | ||
| } | ||
| return null; | ||
| } | ||
| findInstanceId(sandbox, context) { | ||
| for (const name of sandbox.idEnv ?? []) { | ||
| const value = context.env[name]; | ||
| if (value) return value; | ||
| } | ||
| return void 0; | ||
| } | ||
| }; | ||
| function createDefaultStrategies() { | ||
| return [new EnvironmentDetectionStrategy()]; | ||
| } | ||
| // src/index.ts | ||
@@ -150,20 +64,15 @@ function detectSandbox(options = {}) { | ||
| const sandboxes = options.sandboxes ?? defaultSandboxes; | ||
| const strategies = options.strategies ?? createDefaultStrategies(); | ||
| const context = { env }; | ||
| const matches = []; | ||
| for (const sandbox of sandboxes) { | ||
| for (const strategy of strategies) { | ||
| const detected = strategy.detect(sandbox, context); | ||
| if (detected) { | ||
| matches.push(detected); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| const sandbox = sandboxes.find( | ||
| (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env)) | ||
| ); | ||
| if (sandbox === void 0) return { detected: false }; | ||
| return { | ||
| detected: matches.length > 0, | ||
| sandbox: matches[0] ?? null, | ||
| matches | ||
| detected: true, | ||
| sandbox: { id: sandbox.id, name: sandbox.name } | ||
| }; | ||
| } | ||
| function matchesEnvSignal(signal, env) { | ||
| const value = env[signal.name]; | ||
| return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value); | ||
| } | ||
@@ -192,7 +101,3 @@ // src/cli.ts | ||
| } else if (result.sandbox) { | ||
| const id = result.sandbox.instanceId ? ` (${result.sandbox.instanceId})` : ""; | ||
| console.log(`detected: ${result.sandbox.name}${id}`); | ||
| for (const match of result.matches.slice(1)) { | ||
| console.log(`also matched: ${match.name}`); | ||
| } | ||
| console.log(`detected: ${result.sandbox.name}`); | ||
| } else { | ||
@@ -199,0 +104,0 @@ console.log("no sandbox detected"); |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/sandboxes.ts","../src/strategies.ts","../src/index.ts","../src/cli.ts"],"sourcesContent":["import type { SandboxDefinition } from \"./types.js\";\n\n/**\n * Built-in sandbox definitions.\n *\n * Ordered by specificity: app builders first (they often run on top of the\n * generic AI sandboxes below), then AI code-execution sandboxes, then cloud\n * development environments. Detection returns the first match as the primary\n * result but reports all matches.\n *\n * Only environments whose markers were confirmed by probing the real\n * platform are listed — no guessed or documentation-only markers.\n */\nexport const defaultSandboxes: readonly SandboxDefinition[] = [\n // ── AI app builders ────────────────────────────────────────────────────\n // Lovable, v0, and Base44 are intentionally absent: they expose no shell,\n // so a CLI can never run inside them.\n {\n id: \"replit\",\n name: \"Replit\",\n category: \"app-builder\",\n // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT\n // are NOT present in current Replit environments.\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n verified: true,\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n category: \"app-builder\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n verified: true,\n },\n {\n id: \"rork\",\n name: \"Rork\",\n category: \"app-builder\",\n // Rork runs on E2B, so e2b also appears in matches.\n env: [{ name: \"RORK_API_URL\" }],\n verified: true,\n },\n\n // ── AI code-execution sandboxes ────────────────────────────────────────\n {\n id: \"e2b\",\n name: \"E2B\",\n category: \"ai-sandbox\",\n // Documented: https://e2b.dev/docs/sandbox/environment-variables\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n idEnv: [\"E2B_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n category: \"ai-sandbox\",\n // No dedicated marker; the microVMs run as the vercel-sandbox user.\n // (PS1=\"▲ $PWD/\" is also set, but only in interactive shells.)\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n verified: true,\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n category: \"ai-sandbox\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n idEnv: [\"DAYTONA_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"modal\",\n name: \"Modal\",\n category: \"ai-sandbox\",\n // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the\n // documented container id (https://modal.com/docs/guide/environment_variables).\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n idEnv: [\"MODAL_SANDBOX_ID\", \"MODAL_TASK_ID\"],\n verified: true,\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n category: \"ai-sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n idEnv: [\"CLOUDFLARE_DURABLE_OBJECT_ID\"],\n verified: true,\n },\n\n // ── Cloud development environments ─────────────────────────────────────\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n category: \"cloud-ide\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n idEnv: [\"CODESPACE_NAME\"],\n verified: true,\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n category: \"cloud-ide\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n idEnv: [\"CSB_SANDBOX_ID\"],\n verified: true,\n },\n];\n","import type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvCondition,\n SandboxDefinition,\n} from \"./types.js\";\n\nfunction matchCondition(\n condition: EnvCondition,\n env: Record<string, string | undefined>,\n): DetectionEvidence | null {\n const value = env[condition.name];\n if (value === undefined || value === \"\") return null;\n if (condition.value !== undefined && value !== condition.value) return null;\n return { envVar: condition.name, value };\n}\n\n/** Detects a sandbox purely from environment variables. */\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly id = \"env\";\n\n detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null {\n for (const matcher of sandbox.env) {\n const conditions = Array.isArray(matcher) ? matcher : [matcher];\n if (conditions.length === 0) continue;\n\n const evidence: DetectionEvidence[] = [];\n for (const condition of conditions) {\n const matched = matchCondition(condition, context.env);\n if (!matched) break;\n evidence.push(matched);\n }\n if (evidence.length !== conditions.length) continue;\n\n return {\n id: sandbox.id,\n name: sandbox.name,\n category: sandbox.category,\n instanceId: this.findInstanceId(sandbox, context),\n evidence,\n };\n }\n return null;\n }\n\n private findInstanceId(\n sandbox: SandboxDefinition,\n context: DetectionContext,\n ): string | undefined {\n for (const name of sandbox.idEnv ?? []) {\n const value = context.env[name];\n if (value) return value;\n }\n return undefined;\n }\n}\n\nexport function createDefaultStrategies(): DetectionStrategy[] {\n return [new EnvironmentDetectionStrategy()];\n}\n","import { defaultSandboxes } from \"./sandboxes.js\";\nimport { createDefaultStrategies } from \"./strategies.js\";\nimport type { DetectedSandbox, DetectionResult, DetectSandboxOptions } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport { createDefaultStrategies, EnvironmentDetectionStrategy } from \"./strategies.js\";\nexport type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy,\n DetectSandboxOptions,\n EnvCondition,\n EnvMatcher,\n SandboxCategory,\n SandboxDefinition,\n} from \"./types.js\";\n\n/**\n * Detect whether the current process runs inside a known sandboxed\n * environment (AI code-execution sandbox, AI app builder, or cloud IDE).\n */\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const strategies = options.strategies ?? createDefaultStrategies();\n const context = { env };\n\n const matches: DetectedSandbox[] = [];\n for (const sandbox of sandboxes) {\n for (const strategy of strategies) {\n const detected = strategy.detect(sandbox, context);\n if (detected) {\n matches.push(detected);\n break;\n }\n }\n }\n\n return {\n detected: matches.length > 0,\n sandbox: matches[0] ?? null,\n matches,\n };\n}\n\n/** Convenience wrapper returning just the boolean. */\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n","#!/usr/bin/env node\nimport { detectSandbox } from \"./index.js\";\n\nconst args = new Set(process.argv.slice(2));\n\nif (args.has(\"-h\") || args.has(\"--help\")) {\n console.log(`Usage: sandbox-cli-detector [options]\n\nDetect whether this process runs inside a sandboxed environment.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet No output, exit code only\n -h, --help Show this help\n\nExit codes:\n 0 sandbox detected\n 1 no sandbox detected`);\n process.exit(0);\n}\n\nconst result = detectSandbox();\n\nif (!args.has(\"--quiet\")) {\n if (args.has(\"--json\")) {\n console.log(JSON.stringify(result, null, 2));\n } else if (result.sandbox) {\n const id = result.sandbox.instanceId ? ` (${result.sandbox.instanceId})` : \"\";\n console.log(`detected: ${result.sandbox.name}${id}`);\n for (const match of result.matches.slice(1)) {\n console.log(`also matched: ${match.name}`);\n }\n } else {\n console.log(\"no sandbox detected\");\n }\n}\n\nprocess.exitCode = result.detected ? 0 : 1;\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/sandboxes.ts","../src/index.ts","../src/cli.ts"],"sourcesContent":["import type { SandboxDefinition } from \"./types.js\";\n\nexport const defaultSandboxes = [\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n },\n {\n id: \"e2b\",\n name: \"E2B\",\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n },\n {\n id: \"modal\",\n name: \"Modal\",\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n },\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n },\n] satisfies readonly SandboxDefinition[];\n","import { defaultSandboxes } from \"./sandboxes.js\";\nimport type { DetectSandboxOptions, DetectionResult, EnvSignal } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport type {\n DetectedSandbox,\n DetectionResult,\n DetectSandboxOptions,\n EnvSignal,\n SandboxDefinition,\n} from \"./types.js\";\n\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const sandbox = sandboxes.find((candidate) =>\n candidate.env.some((signal) => matchesEnvSignal(signal, env)),\n );\n\n if (sandbox === undefined) return { detected: false };\n\n return {\n detected: true,\n sandbox: { id: sandbox.id, name: sandbox.name },\n };\n}\n\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n\nfunction matchesEnvSignal(signal: EnvSignal, env: NodeJS.ProcessEnv): boolean {\n const value = env[signal.name];\n return (\n value !== undefined &&\n value !== \"\" &&\n (signal.value === undefined || value === signal.value)\n );\n}\n","#!/usr/bin/env node\nimport { detectSandbox } from \"./index.js\";\n\nconst args = new Set(process.argv.slice(2));\n\nif (args.has(\"-h\") || args.has(\"--help\")) {\n console.log(`Usage: sandbox-cli-detector [options]\n\nDetect whether this process runs inside a sandboxed environment.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet No output, exit code only\n -h, --help Show this help\n\nExit codes:\n 0 sandbox detected\n 1 no sandbox detected`);\n process.exit(0);\n}\n\nconst result = detectSandbox();\n\nif (!args.has(\"--quiet\")) {\n if (args.has(\"--json\")) {\n console.log(JSON.stringify(result, null, 2));\n } else if (result.sandbox) {\n console.log(`detected: ${result.sandbox.name}`);\n } else {\n console.log(\"no sandbox detected\");\n }\n}\n\nprocess.exitCode = result.detected ? 0 : 1;\n"],"mappings":";;;;AAEO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,iBAAiB,GAAG,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,cAAc,CAAC;AAAA,EACzF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,cAAc,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAClF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,eAAe,OAAO,OAAO,CAAC;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,QAAQ,OAAO,uBAAuB,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,+BAA+B,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,cAAc,OAAO,OAAO,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,OAAO,OAAO,OAAO,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAAA,EAClE;AACF;;;ACpCO,SAAS,cAAc,UAAgC,CAAC,GAAoB;AACjF,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,UAAU;AAAA,IAAK,CAAC,cAC9B,UAAU,IAAI,KAAK,CAAC,WAAW,iBAAiB,QAAQ,GAAG,CAAC;AAAA,EAC9D;AAEA,MAAI,YAAY,OAAW,QAAO,EAAE,UAAU,MAAM;AAEpD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,SAAS,EAAE,IAAI,QAAQ,IAAI,MAAM,QAAQ,KAAK;AAAA,EAChD;AACF;AAMA,SAAS,iBAAiB,QAAmB,KAAiC;AAC5E,QAAM,QAAQ,IAAI,OAAO,IAAI;AAC7B,SACE,UAAU,UACV,UAAU,OACT,OAAO,UAAU,UAAa,UAAU,OAAO;AAEpD;;;ACnCA,IAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AAE1C,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,QAAQ,GAAG;AACxC,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAWW;AACvB,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,cAAc;AAE7B,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;AACxB,MAAI,KAAK,IAAI,QAAQ,GAAG;AACtB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,WAAW,OAAO,SAAS;AACzB,YAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE;AAAA,EAChD,OAAO;AACL,YAAQ,IAAI,qBAAqB;AAAA,EACnC;AACF;AAEA,QAAQ,WAAW,OAAO,WAAW,IAAI;","names":[]} |
+2
-6
| #!/usr/bin/env node | ||
| import { | ||
| detectSandbox | ||
| } from "./chunk-2S3637PR.js"; | ||
| } from "./chunk-HZH3SATY.js"; | ||
@@ -28,7 +28,3 @@ // src/cli.ts | ||
| } else if (result.sandbox) { | ||
| const id = result.sandbox.instanceId ? ` (${result.sandbox.instanceId})` : ""; | ||
| console.log(`detected: ${result.sandbox.name}${id}`); | ||
| for (const match of result.matches.slice(1)) { | ||
| console.log(`also matched: ${match.name}`); | ||
| } | ||
| console.log(`detected: ${result.sandbox.name}`); | ||
| } else { | ||
@@ -35,0 +31,0 @@ console.log("no sandbox detected"); |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { detectSandbox } from \"./index.js\";\n\nconst args = new Set(process.argv.slice(2));\n\nif (args.has(\"-h\") || args.has(\"--help\")) {\n console.log(`Usage: sandbox-cli-detector [options]\n\nDetect whether this process runs inside a sandboxed environment.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet No output, exit code only\n -h, --help Show this help\n\nExit codes:\n 0 sandbox detected\n 1 no sandbox detected`);\n process.exit(0);\n}\n\nconst result = detectSandbox();\n\nif (!args.has(\"--quiet\")) {\n if (args.has(\"--json\")) {\n console.log(JSON.stringify(result, null, 2));\n } else if (result.sandbox) {\n const id = result.sandbox.instanceId ? ` (${result.sandbox.instanceId})` : \"\";\n console.log(`detected: ${result.sandbox.name}${id}`);\n for (const match of result.matches.slice(1)) {\n console.log(`also matched: ${match.name}`);\n }\n } else {\n console.log(\"no sandbox detected\");\n }\n}\n\nprocess.exitCode = result.detected ? 0 : 1;\n"],"mappings":";;;;;;AAGA,IAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AAE1C,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,QAAQ,GAAG;AACxC,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAWW;AACvB,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,cAAc;AAE7B,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;AACxB,MAAI,KAAK,IAAI,QAAQ,GAAG;AACtB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,WAAW,OAAO,SAAS;AACzB,UAAM,KAAK,OAAO,QAAQ,aAAa,KAAK,OAAO,QAAQ,UAAU,MAAM;AAC3E,YAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,GAAG,EAAE,EAAE;AACnD,eAAW,SAAS,OAAO,QAAQ,MAAM,CAAC,GAAG;AAC3C,cAAQ,IAAI,iBAAiB,MAAM,IAAI,EAAE;AAAA,IAC3C;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,qBAAqB;AAAA,EACnC;AACF;AAEA,QAAQ,WAAW,OAAO,WAAW,IAAI;","names":[]} | ||
| {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { detectSandbox } from \"./index.js\";\n\nconst args = new Set(process.argv.slice(2));\n\nif (args.has(\"-h\") || args.has(\"--help\")) {\n console.log(`Usage: sandbox-cli-detector [options]\n\nDetect whether this process runs inside a sandboxed environment.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet No output, exit code only\n -h, --help Show this help\n\nExit codes:\n 0 sandbox detected\n 1 no sandbox detected`);\n process.exit(0);\n}\n\nconst result = detectSandbox();\n\nif (!args.has(\"--quiet\")) {\n if (args.has(\"--json\")) {\n console.log(JSON.stringify(result, null, 2));\n } else if (result.sandbox) {\n console.log(`detected: ${result.sandbox.name}`);\n } else {\n console.log(\"no sandbox detected\");\n }\n}\n\nprocess.exitCode = result.detected ? 0 : 1;\n"],"mappings":";;;;;;AAGA,IAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AAE1C,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,QAAQ,GAAG;AACxC,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAWW;AACvB,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,cAAc;AAE7B,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;AACxB,MAAI,KAAK,IAAI,QAAQ,GAAG;AACtB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,WAAW,OAAO,SAAS;AACzB,YAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE;AAAA,EAChD,OAAO;AACL,YAAQ,IAAI,qBAAqB;AAAA,EACnC;AACF;AAEA,QAAQ,WAAW,OAAO,WAAW,IAAI;","names":[]} |
+19
-114
@@ -23,4 +23,2 @@ "use strict"; | ||
| __export(index_exports, { | ||
| EnvironmentDetectionStrategy: () => EnvironmentDetectionStrategy, | ||
| createDefaultStrategies: () => createDefaultStrategies, | ||
| defaultSandboxes: () => defaultSandboxes, | ||
@@ -34,13 +32,6 @@ detectSandbox: () => detectSandbox, | ||
| var defaultSandboxes = [ | ||
| // ── AI app builders ──────────────────────────────────────────────────── | ||
| // Lovable, v0, and Base44 are intentionally absent: they expose no shell, | ||
| // so a CLI can never run inside them. | ||
| { | ||
| id: "replit", | ||
| name: "Replit", | ||
| category: "app-builder", | ||
| // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT | ||
| // are NOT present in current Replit environments. | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }], | ||
| verified: true | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }] | ||
| }, | ||
@@ -50,23 +41,8 @@ { | ||
| name: "bolt.new", | ||
| category: "app-builder", | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }], | ||
| verified: true | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }] | ||
| }, | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| category: "app-builder", | ||
| // Rork runs on E2B, so e2b also appears in matches. | ||
| env: [{ name: "RORK_API_URL" }], | ||
| verified: true | ||
| }, | ||
| // ── AI code-execution sandboxes ──────────────────────────────────────── | ||
| { | ||
| id: "e2b", | ||
| name: "E2B", | ||
| category: "ai-sandbox", | ||
| // Documented: https://e2b.dev/docs/sandbox/environment-variables | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }], | ||
| idEnv: ["E2B_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }] | ||
| }, | ||
@@ -76,7 +52,3 @@ { | ||
| name: "Vercel Sandbox", | ||
| category: "ai-sandbox", | ||
| // No dedicated marker; the microVMs run as the vercel-sandbox user. | ||
| // (PS1="▲ $PWD/" is also set, but only in interactive shells.) | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }], | ||
| verified: true | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }] | ||
| }, | ||
@@ -86,6 +58,3 @@ { | ||
| name: "Daytona", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }], | ||
| idEnv: ["DAYTONA_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }] | ||
| }, | ||
@@ -95,8 +64,3 @@ { | ||
| name: "Modal", | ||
| category: "ai-sandbox", | ||
| // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the | ||
| // documented container id (https://modal.com/docs/guide/environment_variables). | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }], | ||
| idEnv: ["MODAL_SANDBOX_ID", "MODAL_TASK_ID"], | ||
| verified: true | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }] | ||
| }, | ||
@@ -106,15 +70,8 @@ { | ||
| name: "Cloudflare Sandbox", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }], | ||
| idEnv: ["CLOUDFLARE_DURABLE_OBJECT_ID"], | ||
| verified: true | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }] | ||
| }, | ||
| // ── Cloud development environments ───────────────────────────────────── | ||
| { | ||
| id: "codespaces", | ||
| name: "GitHub Codespaces", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CODESPACES", value: "true" }], | ||
| idEnv: ["CODESPACE_NAME"], | ||
| verified: true | ||
| env: [{ name: "CODESPACES", value: "true" }] | ||
| }, | ||
@@ -124,51 +81,6 @@ { | ||
| name: "CodeSandbox", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }], | ||
| idEnv: ["CSB_SANDBOX_ID"], | ||
| verified: true | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }] | ||
| } | ||
| ]; | ||
| // src/strategies.ts | ||
| function matchCondition(condition, env) { | ||
| const value = env[condition.name]; | ||
| if (value === void 0 || value === "") return null; | ||
| if (condition.value !== void 0 && value !== condition.value) return null; | ||
| return { envVar: condition.name, value }; | ||
| } | ||
| var EnvironmentDetectionStrategy = class { | ||
| id = "env"; | ||
| detect(sandbox, context) { | ||
| for (const matcher of sandbox.env) { | ||
| const conditions = Array.isArray(matcher) ? matcher : [matcher]; | ||
| if (conditions.length === 0) continue; | ||
| const evidence = []; | ||
| for (const condition of conditions) { | ||
| const matched = matchCondition(condition, context.env); | ||
| if (!matched) break; | ||
| evidence.push(matched); | ||
| } | ||
| if (evidence.length !== conditions.length) continue; | ||
| return { | ||
| id: sandbox.id, | ||
| name: sandbox.name, | ||
| category: sandbox.category, | ||
| instanceId: this.findInstanceId(sandbox, context), | ||
| evidence | ||
| }; | ||
| } | ||
| return null; | ||
| } | ||
| findInstanceId(sandbox, context) { | ||
| for (const name of sandbox.idEnv ?? []) { | ||
| const value = context.env[name]; | ||
| if (value) return value; | ||
| } | ||
| return void 0; | ||
| } | ||
| }; | ||
| function createDefaultStrategies() { | ||
| return [new EnvironmentDetectionStrategy()]; | ||
| } | ||
| // src/index.ts | ||
@@ -178,18 +90,9 @@ function detectSandbox(options = {}) { | ||
| const sandboxes = options.sandboxes ?? defaultSandboxes; | ||
| const strategies = options.strategies ?? createDefaultStrategies(); | ||
| const context = { env }; | ||
| const matches = []; | ||
| for (const sandbox of sandboxes) { | ||
| for (const strategy of strategies) { | ||
| const detected = strategy.detect(sandbox, context); | ||
| if (detected) { | ||
| matches.push(detected); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| const sandbox = sandboxes.find( | ||
| (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env)) | ||
| ); | ||
| if (sandbox === void 0) return { detected: false }; | ||
| return { | ||
| detected: matches.length > 0, | ||
| sandbox: matches[0] ?? null, | ||
| matches | ||
| detected: true, | ||
| sandbox: { id: sandbox.id, name: sandbox.name } | ||
| }; | ||
@@ -200,6 +103,8 @@ } | ||
| } | ||
| function matchesEnvSignal(signal, env) { | ||
| const value = env[signal.name]; | ||
| return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value); | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| EnvironmentDetectionStrategy, | ||
| createDefaultStrategies, | ||
| defaultSandboxes, | ||
@@ -206,0 +111,0 @@ detectSandbox, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/sandboxes.ts","../src/strategies.ts"],"sourcesContent":["import { defaultSandboxes } from \"./sandboxes.js\";\nimport { createDefaultStrategies } from \"./strategies.js\";\nimport type { DetectedSandbox, DetectionResult, DetectSandboxOptions } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport { createDefaultStrategies, EnvironmentDetectionStrategy } from \"./strategies.js\";\nexport type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy,\n DetectSandboxOptions,\n EnvCondition,\n EnvMatcher,\n SandboxCategory,\n SandboxDefinition,\n} from \"./types.js\";\n\n/**\n * Detect whether the current process runs inside a known sandboxed\n * environment (AI code-execution sandbox, AI app builder, or cloud IDE).\n */\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const strategies = options.strategies ?? createDefaultStrategies();\n const context = { env };\n\n const matches: DetectedSandbox[] = [];\n for (const sandbox of sandboxes) {\n for (const strategy of strategies) {\n const detected = strategy.detect(sandbox, context);\n if (detected) {\n matches.push(detected);\n break;\n }\n }\n }\n\n return {\n detected: matches.length > 0,\n sandbox: matches[0] ?? null,\n matches,\n };\n}\n\n/** Convenience wrapper returning just the boolean. */\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n","import type { SandboxDefinition } from \"./types.js\";\n\n/**\n * Built-in sandbox definitions.\n *\n * Ordered by specificity: app builders first (they often run on top of the\n * generic AI sandboxes below), then AI code-execution sandboxes, then cloud\n * development environments. Detection returns the first match as the primary\n * result but reports all matches.\n *\n * Only environments whose markers were confirmed by probing the real\n * platform are listed — no guessed or documentation-only markers.\n */\nexport const defaultSandboxes: readonly SandboxDefinition[] = [\n // ── AI app builders ────────────────────────────────────────────────────\n // Lovable, v0, and Base44 are intentionally absent: they expose no shell,\n // so a CLI can never run inside them.\n {\n id: \"replit\",\n name: \"Replit\",\n category: \"app-builder\",\n // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT\n // are NOT present in current Replit environments.\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n verified: true,\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n category: \"app-builder\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n verified: true,\n },\n {\n id: \"rork\",\n name: \"Rork\",\n category: \"app-builder\",\n // Rork runs on E2B, so e2b also appears in matches.\n env: [{ name: \"RORK_API_URL\" }],\n verified: true,\n },\n\n // ── AI code-execution sandboxes ────────────────────────────────────────\n {\n id: \"e2b\",\n name: \"E2B\",\n category: \"ai-sandbox\",\n // Documented: https://e2b.dev/docs/sandbox/environment-variables\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n idEnv: [\"E2B_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n category: \"ai-sandbox\",\n // No dedicated marker; the microVMs run as the vercel-sandbox user.\n // (PS1=\"▲ $PWD/\" is also set, but only in interactive shells.)\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n verified: true,\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n category: \"ai-sandbox\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n idEnv: [\"DAYTONA_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"modal\",\n name: \"Modal\",\n category: \"ai-sandbox\",\n // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the\n // documented container id (https://modal.com/docs/guide/environment_variables).\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n idEnv: [\"MODAL_SANDBOX_ID\", \"MODAL_TASK_ID\"],\n verified: true,\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n category: \"ai-sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n idEnv: [\"CLOUDFLARE_DURABLE_OBJECT_ID\"],\n verified: true,\n },\n\n // ── Cloud development environments ─────────────────────────────────────\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n category: \"cloud-ide\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n idEnv: [\"CODESPACE_NAME\"],\n verified: true,\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n category: \"cloud-ide\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n idEnv: [\"CSB_SANDBOX_ID\"],\n verified: true,\n },\n];\n","import type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvCondition,\n SandboxDefinition,\n} from \"./types.js\";\n\nfunction matchCondition(\n condition: EnvCondition,\n env: Record<string, string | undefined>,\n): DetectionEvidence | null {\n const value = env[condition.name];\n if (value === undefined || value === \"\") return null;\n if (condition.value !== undefined && value !== condition.value) return null;\n return { envVar: condition.name, value };\n}\n\n/** Detects a sandbox purely from environment variables. */\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly id = \"env\";\n\n detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null {\n for (const matcher of sandbox.env) {\n const conditions = Array.isArray(matcher) ? matcher : [matcher];\n if (conditions.length === 0) continue;\n\n const evidence: DetectionEvidence[] = [];\n for (const condition of conditions) {\n const matched = matchCondition(condition, context.env);\n if (!matched) break;\n evidence.push(matched);\n }\n if (evidence.length !== conditions.length) continue;\n\n return {\n id: sandbox.id,\n name: sandbox.name,\n category: sandbox.category,\n instanceId: this.findInstanceId(sandbox, context),\n evidence,\n };\n }\n return null;\n }\n\n private findInstanceId(\n sandbox: SandboxDefinition,\n context: DetectionContext,\n ): string | undefined {\n for (const name of sandbox.idEnv ?? []) {\n const value = context.env[name];\n if (value) return value;\n }\n return undefined;\n }\n}\n\nexport function createDefaultStrategies(): DetectionStrategy[] {\n return [new EnvironmentDetectionStrategy()];\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/sandboxes.ts"],"sourcesContent":["import { defaultSandboxes } from \"./sandboxes.js\";\nimport type { DetectSandboxOptions, DetectionResult, EnvSignal } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport type {\n DetectedSandbox,\n DetectionResult,\n DetectSandboxOptions,\n EnvSignal,\n SandboxDefinition,\n} from \"./types.js\";\n\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const sandbox = sandboxes.find((candidate) =>\n candidate.env.some((signal) => matchesEnvSignal(signal, env)),\n );\n\n if (sandbox === undefined) return { detected: false };\n\n return {\n detected: true,\n sandbox: { id: sandbox.id, name: sandbox.name },\n };\n}\n\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n\nfunction matchesEnvSignal(signal: EnvSignal, env: NodeJS.ProcessEnv): boolean {\n const value = env[signal.name];\n return (\n value !== undefined &&\n value !== \"\" &&\n (signal.value === undefined || value === signal.value)\n );\n}\n","import type { SandboxDefinition } from \"./types.js\";\n\nexport const defaultSandboxes = [\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n },\n {\n id: \"e2b\",\n name: \"E2B\",\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n },\n {\n id: \"modal\",\n name: \"Modal\",\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n },\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n },\n] satisfies readonly SandboxDefinition[];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,iBAAiB,GAAG,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,cAAc,CAAC;AAAA,EACzF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,cAAc,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAClF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,eAAe,OAAO,OAAO,CAAC;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,QAAQ,OAAO,uBAAuB,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,mBAAmB,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,+BAA+B,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,cAAc,OAAO,OAAO,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,OAAO,OAAO,OAAO,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAAA,EAClE;AACF;;;ADpCO,SAAS,cAAc,UAAgC,CAAC,GAAoB;AACjF,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,UAAU;AAAA,IAAK,CAAC,cAC9B,UAAU,IAAI,KAAK,CAAC,WAAW,iBAAiB,QAAQ,GAAG,CAAC;AAAA,EAC9D;AAEA,MAAI,YAAY,OAAW,QAAO,EAAE,UAAU,MAAM;AAEpD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,SAAS,EAAE,IAAI,QAAQ,IAAI,MAAM,QAAQ,KAAK;AAAA,EAChD;AACF;AAEO,SAAS,mBAAmB,UAAgC,CAAC,GAAY;AAC9E,SAAO,cAAc,OAAO,EAAE;AAChC;AAEA,SAAS,iBAAiB,QAAmB,KAAiC;AAC5E,QAAM,QAAQ,IAAI,OAAO,IAAI;AAC7B,SACE,UAAU,UACV,UAAU,OACT,OAAO,UAAU,UAAa,UAAU,OAAO;AAEpD;","names":[]} |
+23
-90
@@ -1,103 +0,36 @@ | ||
| /** A single environment variable condition. */ | ||
| interface EnvCondition { | ||
| /** Environment variable name. */ | ||
| name: string; | ||
| /** Exact expected value. If omitted, any non-empty value matches. */ | ||
| value?: string; | ||
| interface EnvSignal { | ||
| readonly name: string; | ||
| readonly value?: string; | ||
| } | ||
| /** | ||
| * One way a sandbox can be recognized from the environment. | ||
| * A single condition, or a group of conditions that must ALL hold. | ||
| * A sandbox's `env` list is OR-ed: any one matcher is enough. | ||
| */ | ||
| type EnvMatcher = EnvCondition | EnvCondition[]; | ||
| type SandboxCategory = | ||
| /** Ephemeral code-execution sandboxes used by AI agents (E2B, Daytona, Modal, ...). */ | ||
| "ai-sandbox" | ||
| /** AI app builders that run user code in their own sandboxes (Lovable, Replit, bolt.new, ...). */ | ||
| | "app-builder" | ||
| /** Cloud development environments (Codespaces, Gitpod, Cloud Shell, ...). */ | ||
| | "cloud-ide"; | ||
| interface SandboxDefinition { | ||
| /** Stable identifier, e.g. "e2b". */ | ||
| id: string; | ||
| /** Display name, e.g. "E2B". */ | ||
| name: string; | ||
| category: SandboxCategory; | ||
| /** Env matchers; matching ANY entry means this sandbox is detected. */ | ||
| env: EnvMatcher[]; | ||
| /** Env vars that carry the sandbox/instance id; first one present wins. */ | ||
| idEnv?: string[]; | ||
| /** True once the markers have been confirmed on the real platform. */ | ||
| verified: boolean; | ||
| } | ||
| /** A matched env var, kept as evidence for debugging. */ | ||
| interface DetectionEvidence { | ||
| envVar: string; | ||
| value: string; | ||
| } | ||
| interface DetectedSandbox { | ||
| id: string; | ||
| name: string; | ||
| category: SandboxCategory; | ||
| /** Sandbox/instance id when the platform exposes one (e.g. E2B_SANDBOX_ID). */ | ||
| instanceId?: string; | ||
| evidence: DetectionEvidence[]; | ||
| readonly id: string; | ||
| readonly name: string; | ||
| } | ||
| interface DetectionContext { | ||
| env: Record<string, string | undefined>; | ||
| interface SandboxDefinition extends DetectedSandbox { | ||
| readonly env: readonly EnvSignal[]; | ||
| } | ||
| interface DetectionStrategy { | ||
| /** Strategy identifier, e.g. "env". */ | ||
| readonly id: string; | ||
| detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null; | ||
| } | ||
| interface DetectSandboxOptions { | ||
| /** Environment to inspect. Defaults to process.env. */ | ||
| env?: Record<string, string | undefined>; | ||
| /** Sandbox definitions to check. Defaults to the built-in list. */ | ||
| sandboxes?: readonly SandboxDefinition[]; | ||
| /** Detection strategies to run. Defaults to env-variable detection. */ | ||
| strategies?: readonly DetectionStrategy[]; | ||
| readonly env?: NodeJS.ProcessEnv; | ||
| readonly sandboxes?: readonly SandboxDefinition[]; | ||
| } | ||
| interface DetectionResult { | ||
| detected: boolean; | ||
| /** | ||
| * The most specific match (definitions are ordered app-builder → | ||
| * ai-sandbox → cloud-ide, so a Lovable build running on E2B reports Lovable). | ||
| */ | ||
| sandbox: DetectedSandbox | null; | ||
| /** All sandboxes that matched, in definition order. */ | ||
| matches: DetectedSandbox[]; | ||
| readonly detected: boolean; | ||
| readonly sandbox?: DetectedSandbox; | ||
| } | ||
| /** | ||
| * Built-in sandbox definitions. | ||
| * | ||
| * Ordered by specificity: app builders first (they often run on top of the | ||
| * generic AI sandboxes below), then AI code-execution sandboxes, then cloud | ||
| * development environments. Detection returns the first match as the primary | ||
| * result but reports all matches. | ||
| * | ||
| * Only environments whose markers were confirmed by probing the real | ||
| * platform are listed — no guessed or documentation-only markers. | ||
| */ | ||
| declare const defaultSandboxes: readonly SandboxDefinition[]; | ||
| declare const defaultSandboxes: { | ||
| id: string; | ||
| name: string; | ||
| env: ({ | ||
| name: string; | ||
| value: string; | ||
| } | { | ||
| name: string; | ||
| value?: undefined; | ||
| })[]; | ||
| }[]; | ||
| /** Detects a sandbox purely from environment variables. */ | ||
| declare class EnvironmentDetectionStrategy implements DetectionStrategy { | ||
| readonly id = "env"; | ||
| detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null; | ||
| private findInstanceId; | ||
| } | ||
| declare function createDefaultStrategies(): DetectionStrategy[]; | ||
| /** | ||
| * Detect whether the current process runs inside a known sandboxed | ||
| * environment (AI code-execution sandbox, AI app builder, or cloud IDE). | ||
| */ | ||
| declare function detectSandbox(options?: DetectSandboxOptions): DetectionResult; | ||
| /** Convenience wrapper returning just the boolean. */ | ||
| declare function isRunningInSandbox(options?: DetectSandboxOptions): boolean; | ||
| export { type DetectSandboxOptions, type DetectedSandbox, type DetectionContext, type DetectionEvidence, type DetectionResult, type DetectionStrategy, type EnvCondition, type EnvMatcher, EnvironmentDetectionStrategy, type SandboxCategory, type SandboxDefinition, createDefaultStrategies, defaultSandboxes, detectSandbox, isRunningInSandbox }; | ||
| export { type DetectSandboxOptions, type DetectedSandbox, type DetectionResult, type EnvSignal, type SandboxDefinition, defaultSandboxes, detectSandbox, isRunningInSandbox }; |
+23
-90
@@ -1,103 +0,36 @@ | ||
| /** A single environment variable condition. */ | ||
| interface EnvCondition { | ||
| /** Environment variable name. */ | ||
| name: string; | ||
| /** Exact expected value. If omitted, any non-empty value matches. */ | ||
| value?: string; | ||
| interface EnvSignal { | ||
| readonly name: string; | ||
| readonly value?: string; | ||
| } | ||
| /** | ||
| * One way a sandbox can be recognized from the environment. | ||
| * A single condition, or a group of conditions that must ALL hold. | ||
| * A sandbox's `env` list is OR-ed: any one matcher is enough. | ||
| */ | ||
| type EnvMatcher = EnvCondition | EnvCondition[]; | ||
| type SandboxCategory = | ||
| /** Ephemeral code-execution sandboxes used by AI agents (E2B, Daytona, Modal, ...). */ | ||
| "ai-sandbox" | ||
| /** AI app builders that run user code in their own sandboxes (Lovable, Replit, bolt.new, ...). */ | ||
| | "app-builder" | ||
| /** Cloud development environments (Codespaces, Gitpod, Cloud Shell, ...). */ | ||
| | "cloud-ide"; | ||
| interface SandboxDefinition { | ||
| /** Stable identifier, e.g. "e2b". */ | ||
| id: string; | ||
| /** Display name, e.g. "E2B". */ | ||
| name: string; | ||
| category: SandboxCategory; | ||
| /** Env matchers; matching ANY entry means this sandbox is detected. */ | ||
| env: EnvMatcher[]; | ||
| /** Env vars that carry the sandbox/instance id; first one present wins. */ | ||
| idEnv?: string[]; | ||
| /** True once the markers have been confirmed on the real platform. */ | ||
| verified: boolean; | ||
| } | ||
| /** A matched env var, kept as evidence for debugging. */ | ||
| interface DetectionEvidence { | ||
| envVar: string; | ||
| value: string; | ||
| } | ||
| interface DetectedSandbox { | ||
| id: string; | ||
| name: string; | ||
| category: SandboxCategory; | ||
| /** Sandbox/instance id when the platform exposes one (e.g. E2B_SANDBOX_ID). */ | ||
| instanceId?: string; | ||
| evidence: DetectionEvidence[]; | ||
| readonly id: string; | ||
| readonly name: string; | ||
| } | ||
| interface DetectionContext { | ||
| env: Record<string, string | undefined>; | ||
| interface SandboxDefinition extends DetectedSandbox { | ||
| readonly env: readonly EnvSignal[]; | ||
| } | ||
| interface DetectionStrategy { | ||
| /** Strategy identifier, e.g. "env". */ | ||
| readonly id: string; | ||
| detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null; | ||
| } | ||
| interface DetectSandboxOptions { | ||
| /** Environment to inspect. Defaults to process.env. */ | ||
| env?: Record<string, string | undefined>; | ||
| /** Sandbox definitions to check. Defaults to the built-in list. */ | ||
| sandboxes?: readonly SandboxDefinition[]; | ||
| /** Detection strategies to run. Defaults to env-variable detection. */ | ||
| strategies?: readonly DetectionStrategy[]; | ||
| readonly env?: NodeJS.ProcessEnv; | ||
| readonly sandboxes?: readonly SandboxDefinition[]; | ||
| } | ||
| interface DetectionResult { | ||
| detected: boolean; | ||
| /** | ||
| * The most specific match (definitions are ordered app-builder → | ||
| * ai-sandbox → cloud-ide, so a Lovable build running on E2B reports Lovable). | ||
| */ | ||
| sandbox: DetectedSandbox | null; | ||
| /** All sandboxes that matched, in definition order. */ | ||
| matches: DetectedSandbox[]; | ||
| readonly detected: boolean; | ||
| readonly sandbox?: DetectedSandbox; | ||
| } | ||
| /** | ||
| * Built-in sandbox definitions. | ||
| * | ||
| * Ordered by specificity: app builders first (they often run on top of the | ||
| * generic AI sandboxes below), then AI code-execution sandboxes, then cloud | ||
| * development environments. Detection returns the first match as the primary | ||
| * result but reports all matches. | ||
| * | ||
| * Only environments whose markers were confirmed by probing the real | ||
| * platform are listed — no guessed or documentation-only markers. | ||
| */ | ||
| declare const defaultSandboxes: readonly SandboxDefinition[]; | ||
| declare const defaultSandboxes: { | ||
| id: string; | ||
| name: string; | ||
| env: ({ | ||
| name: string; | ||
| value: string; | ||
| } | { | ||
| name: string; | ||
| value?: undefined; | ||
| })[]; | ||
| }[]; | ||
| /** Detects a sandbox purely from environment variables. */ | ||
| declare class EnvironmentDetectionStrategy implements DetectionStrategy { | ||
| readonly id = "env"; | ||
| detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null; | ||
| private findInstanceId; | ||
| } | ||
| declare function createDefaultStrategies(): DetectionStrategy[]; | ||
| /** | ||
| * Detect whether the current process runs inside a known sandboxed | ||
| * environment (AI code-execution sandbox, AI app builder, or cloud IDE). | ||
| */ | ||
| declare function detectSandbox(options?: DetectSandboxOptions): DetectionResult; | ||
| /** Convenience wrapper returning just the boolean. */ | ||
| declare function isRunningInSandbox(options?: DetectSandboxOptions): boolean; | ||
| export { type DetectSandboxOptions, type DetectedSandbox, type DetectionContext, type DetectionEvidence, type DetectionResult, type DetectionStrategy, type EnvCondition, type EnvMatcher, EnvironmentDetectionStrategy, type SandboxCategory, type SandboxDefinition, createDefaultStrategies, defaultSandboxes, detectSandbox, isRunningInSandbox }; | ||
| export { type DetectSandboxOptions, type DetectedSandbox, type DetectionResult, type EnvSignal, type SandboxDefinition, defaultSandboxes, detectSandbox, isRunningInSandbox }; |
+1
-5
| import { | ||
| EnvironmentDetectionStrategy, | ||
| createDefaultStrategies, | ||
| defaultSandboxes, | ||
| detectSandbox, | ||
| isRunningInSandbox | ||
| } from "./chunk-2S3637PR.js"; | ||
| } from "./chunk-HZH3SATY.js"; | ||
| export { | ||
| EnvironmentDetectionStrategy, | ||
| createDefaultStrategies, | ||
| defaultSandboxes, | ||
@@ -12,0 +8,0 @@ detectSandbox, |
+2
-2
| { | ||
| "name": "sandbox-cli-detector", | ||
| "version": "0.1.1", | ||
| "description": "Detect whether a CLI is running inside a sandboxed environment (E2B, Vercel Sandbox, Replit, Daytona, Modal, and more)", | ||
| "version": "0.2.0", | ||
| "description": "Detect whether a CLI is running inside a sandboxed environment", | ||
| "keywords": [ | ||
@@ -6,0 +6,0 @@ "sandbox", |
+61
-164
@@ -6,12 +6,9 @@ # sandbox-cli-detector | ||
| This is useful for CLIs, SDKs, test runners, and install scripts that need to | ||
| adjust behavior inside AI sandboxes, AI app builders, and cloud IDEs. | ||
| Please [open an issue](https://github.com/davidmokos/sandbox-cli-detector/issues/new) | ||
| if your sandbox is not properly detected. | ||
| Inspired by [ci-info](https://github.com/watson/ci-info) and built as a | ||
| companion to [agent-cli-detector](https://github.com/davidmokos/detect-agent): | ||
| `agent-cli-detector` tells you which agent is driving your CLI; | ||
| `sandbox-cli-detector` tells you where that CLI is running. | ||
| [](https://www.npmjs.com/package/sandbox-cli-detector) | ||
| [](https://github.com/davidmokos/sandbox-cli-detector/actions/workflows/test.yml) | ||
| [](LICENSE) | ||
| Please open an issue if a sandbox is missing or incorrectly detected. | ||
| ## Installation | ||
@@ -26,99 +23,32 @@ | ||
| ```ts | ||
| import { detectSandbox, isRunningInSandbox } from "sandbox-cli-detector"; | ||
| import { detectSandbox } from "sandbox-cli-detector"; | ||
| if (isRunningInSandbox()) { | ||
| // Skip interactive prompts, tune telemetry, relax auth flows, etc. | ||
| } | ||
| const result = detectSandbox(); | ||
| if (result.detected) { | ||
| console.log("sandbox:", result.sandbox?.name); | ||
| if (result.detected && result.sandbox) { | ||
| console.log("The name of the sandbox is:", result.sandbox.name); | ||
| } else { | ||
| console.log("not running in a known sandbox"); | ||
| console.log("This program is not running inside a known sandbox"); | ||
| } | ||
| ``` | ||
| Example result: | ||
| ## Supported sandboxes | ||
| ```json | ||
| { | ||
| "detected": true, | ||
| "sandbox": { | ||
| "id": "e2b", | ||
| "name": "E2B", | ||
| "category": "ai-sandbox", | ||
| "instanceId": "sbx_abc123", | ||
| "evidence": [{ "envVar": "E2B_SANDBOX", "value": "true" }] | ||
| }, | ||
| "matches": [ | ||
| { | ||
| "id": "e2b", | ||
| "name": "E2B", | ||
| "category": "ai-sandbox", | ||
| "instanceId": "sbx_abc123", | ||
| "evidence": [{ "envVar": "E2B_SANDBOX", "value": "true" }] | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| Officially supported sandbox environments: | ||
| Layered environments are common. For example, an app builder may run user code | ||
| inside a generic AI sandbox. `result.sandbox` is the primary match and | ||
| `result.matches` contains every matched environment in definition order. The | ||
| built-in definitions are ordered from most specific to least specific: | ||
| app builders, then AI code-execution sandboxes, then cloud IDEs. | ||
| Use `result.sandbox.id` for stable checks. `result.sandbox.name` is display text | ||
| and may change. | ||
| ## CLI | ||
| ```sh | ||
| npx sandbox-cli-detector | ||
| # detected: E2B (sbx_abc123) | ||
| # no sandbox detected | ||
| npx sandbox-cli-detector --json | ||
| # prints the full DetectionResult as JSON | ||
| npx sandbox-cli-detector --quiet | ||
| # no output, exit code only | ||
| ``` | ||
| Exit codes: | ||
| | Code | Meaning | | ||
| | Name | ID | | ||
| | --- | --- | | ||
| | `0` | a known sandbox was detected | | ||
| | `1` | no known sandbox was detected | | ||
| | [Replit](https://replit.com) | `replit` | | ||
| | [bolt.new](https://bolt.new) | `bolt` | | ||
| | [E2B](https://e2b.dev) | `e2b` | | ||
| | [Vercel Sandbox](https://vercel.com/docs/sandbox) | `vercel-sandbox` | | ||
| | [Daytona](https://daytona.io) | `daytona` | | ||
| | [Modal](https://modal.com) | `modal` | | ||
| | [Cloudflare Sandbox](https://developers.cloudflare.com/sandbox/) | `cloudflare-sandbox` | | ||
| | [GitHub Codespaces](https://github.com/features/codespaces) | `codespaces` | | ||
| | [CodeSandbox](https://codesandbox.io) | `codesandbox` | | ||
| ## Supported environments | ||
| Detection is data-driven. The exact environment variables for each sandbox | ||
| live in [`src/sandboxes.ts`](src/sandboxes.ts). | ||
| Detection currently uses environment variables only. Every built-in marker was | ||
| confirmed by probing the real platform; guessed or documentation-only markers | ||
| are intentionally not included. | ||
| | Name | Category | Stable id | Detection env vars | Instance id | | ||
| | --- | --- | --- | --- | --- | | ||
| | [Replit](https://replit.com) | app builder | `replit` | `REPLIT_SESSION`, `REPLIT_CONTAINER`, or `REPLIT_USER` | - | | ||
| | [bolt.new](https://bolt.new) | app builder | `bolt` | `BOLT_ENV`, `BOLT_ORIGIN`, or `BOLT_SERVER_URL` | - | | ||
| | [Rork](https://rork.com) | app builder | `rork` | `RORK_API_URL` | - | | ||
| | [E2B](https://e2b.dev) | AI sandbox | `e2b` | `E2B_SANDBOX=true` | `E2B_SANDBOX_ID` | | ||
| | [Vercel Sandbox](https://vercel.com/docs/sandbox) | AI sandbox | `vercel-sandbox` | `HOME=/home/vercel-sandbox` | - | | ||
| | [Daytona](https://daytona.io) | AI sandbox | `daytona` | `DAYTONA_SANDBOX_ID` | `DAYTONA_SANDBOX_ID` | | ||
| | [Modal](https://modal.com) | AI sandbox | `modal` | `MODAL_SANDBOX_ID` or `MODAL_TASK_ID` | `MODAL_SANDBOX_ID`, `MODAL_TASK_ID` | | ||
| | [Cloudflare Sandbox](https://developers.cloudflare.com/sandbox/) | AI sandbox | `cloudflare-sandbox` | `CLOUDFLARE_DURABLE_OBJECT_ID` | `CLOUDFLARE_DURABLE_OBJECT_ID` | | ||
| | [GitHub Codespaces](https://github.com/features/codespaces) | cloud IDE | `codespaces` | `CODESPACES=true` | `CODESPACE_NAME` | | ||
| | [CodeSandbox](https://codesandbox.io) | cloud IDE | `codesandbox` | `CSB=true` or `CSB_SANDBOX_ID` | `CSB_SANDBOX_ID` | | ||
| Notes: | ||
| - Rork currently runs on E2B, so `detectSandbox()` can report both `rork` and | ||
| `e2b` in `matches`. | ||
| - Lovable, v0, and Base44 are intentionally absent because they do not expose a | ||
| shell where a CLI can run. | ||
| - Some platforms do not provide a dedicated marker. Vercel Sandbox is detected | ||
| by its sandbox user home directory. | ||
| ## API | ||
@@ -128,62 +58,46 @@ | ||
| Returns a `DetectionResult`. | ||
| Returns a detection result: | ||
| ```ts | ||
| interface DetectionResult { | ||
| detected: boolean; | ||
| sandbox: DetectedSandbox | null; | ||
| matches: DetectedSandbox[]; | ||
| { | ||
| detected: true, | ||
| sandbox: { | ||
| id: "e2b", | ||
| name: "E2B" | ||
| } | ||
| } | ||
| ``` | ||
| `sandbox` is the primary match. `matches` includes all matches, in definition | ||
| order. | ||
| When no sandbox is detected, it returns `{ detected: false }`. | ||
| ```ts | ||
| interface DetectedSandbox { | ||
| id: string; | ||
| name: string; | ||
| category: "ai-sandbox" | "app-builder" | "cloud-ide"; | ||
| instanceId?: string; | ||
| evidence: DetectionEvidence[]; | ||
| } | ||
| ### `result.detected` | ||
| interface DetectionEvidence { | ||
| envVar: string; | ||
| value: string; | ||
| } | ||
| ``` | ||
| A boolean that is `true` when the process is running inside a known sandbox and | ||
| `false` otherwise. | ||
| `evidence` contains the environment variables that caused a match. Treat the | ||
| values as runtime metadata and avoid logging them unless you are comfortable | ||
| with exposing your environment. | ||
| ### `result.sandbox.id` | ||
| Options: | ||
| A stable identifier for the detected sandbox. Prefer comparing this value over | ||
| `result.sandbox.name`. | ||
| ```ts | ||
| interface DetectSandboxOptions { | ||
| env?: Record<string, string | undefined>; | ||
| sandboxes?: readonly SandboxDefinition[]; | ||
| strategies?: readonly DetectionStrategy[]; | ||
| } | ||
| ``` | ||
| ### `result.sandbox.name` | ||
| - `env` defaults to `process.env`. | ||
| - `sandboxes` defaults to the built-in `defaultSandboxes` list. | ||
| - `strategies` defaults to environment-variable detection. | ||
| The display name of the detected sandbox. This may change without it being a | ||
| breaking change. | ||
| ### `isRunningInSandbox(options?)` | ||
| Returns `true` when `detectSandbox(options).detected` is `true`. | ||
| A convenience function that returns `result.detected` as a boolean: | ||
| ```ts | ||
| import { isRunningInSandbox } from "sandbox-cli-detector"; | ||
| if (isRunningInSandbox()) { | ||
| // running in a known sandbox | ||
| // Adjust CLI behavior for sandboxed execution. | ||
| } | ||
| ``` | ||
| ### `defaultSandboxes` | ||
| ### Custom sandboxes | ||
| The built-in `SandboxDefinition[]`, exported so you can extend, filter, or | ||
| replace the supported environment list. | ||
| Pass custom definitions when you need to detect an internal environment: | ||
@@ -194,19 +108,2 @@ ```ts | ||
| const result = detectSandbox({ | ||
| sandboxes: defaultSandboxes.filter((sandbox) => sandbox.category !== "cloud-ide"), | ||
| }); | ||
| ``` | ||
| ### Custom sandbox definitions | ||
| Use custom definitions when you know about an internal sandbox or want to test | ||
| against a controlled environment object. | ||
| ```ts | ||
| import { detectSandbox, defaultSandboxes } from "sandbox-cli-detector"; | ||
| const result = detectSandbox({ | ||
| env: { | ||
| INTERNAL_SANDBOX: "true", | ||
| INTERNAL_SANDBOX_ID: "dev-123", | ||
| }, | ||
| sandboxes: [ | ||
@@ -216,6 +113,3 @@ { | ||
| name: "Internal Sandbox", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "INTERNAL_SANDBOX", value: "true" }], | ||
| idEnv: ["INTERNAL_SANDBOX_ID"], | ||
| verified: true, | ||
| }, | ||
@@ -227,22 +121,25 @@ ...defaultSandboxes, | ||
| ## Contributing platform detections | ||
| Each environment signal matches either an exact `value` or any non-empty value | ||
| when `value` is omitted. If multiple sandboxes match, the first definition is | ||
| returned. | ||
| The best detection signals are stable environment variables that are present in | ||
| every shell for that platform and absent elsewhere. | ||
| ## CLI | ||
| To propose a new platform, open an issue or PR with: | ||
| The package also ships a CLI: | ||
| - The platform name and URL. | ||
| - The output of `env | sort` from inside the platform, with secrets removed. | ||
| - Which variables identify the platform. | ||
| - Which variable, if any, contains the sandbox or instance id. | ||
| You can also run: | ||
| ```sh | ||
| npx sandbox-cli-detector | ||
| npx sandbox-cli-detector --json | ||
| npx sandbox-cli-detector --quiet | ||
| ``` | ||
| It exits with `0` when a sandbox is detected and `1` otherwise. | ||
| ## Contributing detections | ||
| Please include the platform name, a redacted `env | sort` captured inside the | ||
| platform, and the variables that identify it. | ||
| ## License | ||
| MIT | ||
| [MIT](LICENSE) |
| // src/sandboxes.ts | ||
| var defaultSandboxes = [ | ||
| // ── AI app builders ──────────────────────────────────────────────────── | ||
| // Lovable, v0, and Base44 are intentionally absent: they expose no shell, | ||
| // so a CLI can never run inside them. | ||
| { | ||
| id: "replit", | ||
| name: "Replit", | ||
| category: "app-builder", | ||
| // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT | ||
| // are NOT present in current Replit environments. | ||
| env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "bolt", | ||
| name: "bolt.new", | ||
| category: "app-builder", | ||
| env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| category: "app-builder", | ||
| // Rork runs on E2B, so e2b also appears in matches. | ||
| env: [{ name: "RORK_API_URL" }], | ||
| verified: true | ||
| }, | ||
| // ── AI code-execution sandboxes ──────────────────────────────────────── | ||
| { | ||
| id: "e2b", | ||
| name: "E2B", | ||
| category: "ai-sandbox", | ||
| // Documented: https://e2b.dev/docs/sandbox/environment-variables | ||
| env: [{ name: "E2B_SANDBOX", value: "true" }], | ||
| idEnv: ["E2B_SANDBOX_ID"], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "vercel-sandbox", | ||
| name: "Vercel Sandbox", | ||
| category: "ai-sandbox", | ||
| // No dedicated marker; the microVMs run as the vercel-sandbox user. | ||
| // (PS1="▲ $PWD/" is also set, but only in interactive shells.) | ||
| env: [{ name: "HOME", value: "/home/vercel-sandbox" }], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "daytona", | ||
| name: "Daytona", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "DAYTONA_SANDBOX_ID" }], | ||
| idEnv: ["DAYTONA_SANDBOX_ID"], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "modal", | ||
| name: "Modal", | ||
| category: "ai-sandbox", | ||
| // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the | ||
| // documented container id (https://modal.com/docs/guide/environment_variables). | ||
| env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }], | ||
| idEnv: ["MODAL_SANDBOX_ID", "MODAL_TASK_ID"], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "cloudflare-sandbox", | ||
| name: "Cloudflare Sandbox", | ||
| category: "ai-sandbox", | ||
| env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }], | ||
| idEnv: ["CLOUDFLARE_DURABLE_OBJECT_ID"], | ||
| verified: true | ||
| }, | ||
| // ── Cloud development environments ───────────────────────────────────── | ||
| { | ||
| id: "codespaces", | ||
| name: "GitHub Codespaces", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CODESPACES", value: "true" }], | ||
| idEnv: ["CODESPACE_NAME"], | ||
| verified: true | ||
| }, | ||
| { | ||
| id: "codesandbox", | ||
| name: "CodeSandbox", | ||
| category: "cloud-ide", | ||
| env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }], | ||
| idEnv: ["CSB_SANDBOX_ID"], | ||
| verified: true | ||
| } | ||
| ]; | ||
| // src/strategies.ts | ||
| function matchCondition(condition, env) { | ||
| const value = env[condition.name]; | ||
| if (value === void 0 || value === "") return null; | ||
| if (condition.value !== void 0 && value !== condition.value) return null; | ||
| return { envVar: condition.name, value }; | ||
| } | ||
| var EnvironmentDetectionStrategy = class { | ||
| id = "env"; | ||
| detect(sandbox, context) { | ||
| for (const matcher of sandbox.env) { | ||
| const conditions = Array.isArray(matcher) ? matcher : [matcher]; | ||
| if (conditions.length === 0) continue; | ||
| const evidence = []; | ||
| for (const condition of conditions) { | ||
| const matched = matchCondition(condition, context.env); | ||
| if (!matched) break; | ||
| evidence.push(matched); | ||
| } | ||
| if (evidence.length !== conditions.length) continue; | ||
| return { | ||
| id: sandbox.id, | ||
| name: sandbox.name, | ||
| category: sandbox.category, | ||
| instanceId: this.findInstanceId(sandbox, context), | ||
| evidence | ||
| }; | ||
| } | ||
| return null; | ||
| } | ||
| findInstanceId(sandbox, context) { | ||
| for (const name of sandbox.idEnv ?? []) { | ||
| const value = context.env[name]; | ||
| if (value) return value; | ||
| } | ||
| return void 0; | ||
| } | ||
| }; | ||
| function createDefaultStrategies() { | ||
| return [new EnvironmentDetectionStrategy()]; | ||
| } | ||
| // src/index.ts | ||
| function detectSandbox(options = {}) { | ||
| const env = options.env ?? process.env; | ||
| const sandboxes = options.sandboxes ?? defaultSandboxes; | ||
| const strategies = options.strategies ?? createDefaultStrategies(); | ||
| const context = { env }; | ||
| const matches = []; | ||
| for (const sandbox of sandboxes) { | ||
| for (const strategy of strategies) { | ||
| const detected = strategy.detect(sandbox, context); | ||
| if (detected) { | ||
| matches.push(detected); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| detected: matches.length > 0, | ||
| sandbox: matches[0] ?? null, | ||
| matches | ||
| }; | ||
| } | ||
| function isRunningInSandbox(options = {}) { | ||
| return detectSandbox(options).detected; | ||
| } | ||
| export { | ||
| defaultSandboxes, | ||
| EnvironmentDetectionStrategy, | ||
| createDefaultStrategies, | ||
| detectSandbox, | ||
| isRunningInSandbox | ||
| }; | ||
| //# sourceMappingURL=chunk-2S3637PR.js.map |
| {"version":3,"sources":["../src/sandboxes.ts","../src/strategies.ts","../src/index.ts"],"sourcesContent":["import type { SandboxDefinition } from \"./types.js\";\n\n/**\n * Built-in sandbox definitions.\n *\n * Ordered by specificity: app builders first (they often run on top of the\n * generic AI sandboxes below), then AI code-execution sandboxes, then cloud\n * development environments. Detection returns the first match as the primary\n * result but reports all matches.\n *\n * Only environments whose markers were confirmed by probing the real\n * platform are listed — no guessed or documentation-only markers.\n */\nexport const defaultSandboxes: readonly SandboxDefinition[] = [\n // ── AI app builders ────────────────────────────────────────────────────\n // Lovable, v0, and Base44 are intentionally absent: they expose no shell,\n // so a CLI can never run inside them.\n {\n id: \"replit\",\n name: \"Replit\",\n category: \"app-builder\",\n // Observed inside Replit. The often-cited REPL_ID and REPLIT_ENVIRONMENT\n // are NOT present in current Replit environments.\n env: [{ name: \"REPLIT_SESSION\" }, { name: \"REPLIT_CONTAINER\" }, { name: \"REPLIT_USER\" }],\n verified: true,\n },\n {\n id: \"bolt\",\n name: \"bolt.new\",\n category: \"app-builder\",\n env: [{ name: \"BOLT_ENV\" }, { name: \"BOLT_ORIGIN\" }, { name: \"BOLT_SERVER_URL\" }],\n verified: true,\n },\n {\n id: \"rork\",\n name: \"Rork\",\n category: \"app-builder\",\n // Rork runs on E2B, so e2b also appears in matches.\n env: [{ name: \"RORK_API_URL\" }],\n verified: true,\n },\n\n // ── AI code-execution sandboxes ────────────────────────────────────────\n {\n id: \"e2b\",\n name: \"E2B\",\n category: \"ai-sandbox\",\n // Documented: https://e2b.dev/docs/sandbox/environment-variables\n env: [{ name: \"E2B_SANDBOX\", value: \"true\" }],\n idEnv: [\"E2B_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"vercel-sandbox\",\n name: \"Vercel Sandbox\",\n category: \"ai-sandbox\",\n // No dedicated marker; the microVMs run as the vercel-sandbox user.\n // (PS1=\"▲ $PWD/\" is also set, but only in interactive shells.)\n env: [{ name: \"HOME\", value: \"/home/vercel-sandbox\" }],\n verified: true,\n },\n {\n id: \"daytona\",\n name: \"Daytona\",\n category: \"ai-sandbox\",\n env: [{ name: \"DAYTONA_SANDBOX_ID\" }],\n idEnv: [\"DAYTONA_SANDBOX_ID\"],\n verified: true,\n },\n {\n id: \"modal\",\n name: \"Modal\",\n category: \"ai-sandbox\",\n // MODAL_SANDBOX_ID observed in Modal Sandboxes; MODAL_TASK_ID is the\n // documented container id (https://modal.com/docs/guide/environment_variables).\n env: [{ name: \"MODAL_SANDBOX_ID\" }, { name: \"MODAL_TASK_ID\" }],\n idEnv: [\"MODAL_SANDBOX_ID\", \"MODAL_TASK_ID\"],\n verified: true,\n },\n {\n id: \"cloudflare-sandbox\",\n name: \"Cloudflare Sandbox\",\n category: \"ai-sandbox\",\n env: [{ name: \"CLOUDFLARE_DURABLE_OBJECT_ID\" }],\n idEnv: [\"CLOUDFLARE_DURABLE_OBJECT_ID\"],\n verified: true,\n },\n\n // ── Cloud development environments ─────────────────────────────────────\n {\n id: \"codespaces\",\n name: \"GitHub Codespaces\",\n category: \"cloud-ide\",\n env: [{ name: \"CODESPACES\", value: \"true\" }],\n idEnv: [\"CODESPACE_NAME\"],\n verified: true,\n },\n {\n id: \"codesandbox\",\n name: \"CodeSandbox\",\n category: \"cloud-ide\",\n env: [{ name: \"CSB\", value: \"true\" }, { name: \"CSB_SANDBOX_ID\" }],\n idEnv: [\"CSB_SANDBOX_ID\"],\n verified: true,\n },\n];\n","import type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvCondition,\n SandboxDefinition,\n} from \"./types.js\";\n\nfunction matchCondition(\n condition: EnvCondition,\n env: Record<string, string | undefined>,\n): DetectionEvidence | null {\n const value = env[condition.name];\n if (value === undefined || value === \"\") return null;\n if (condition.value !== undefined && value !== condition.value) return null;\n return { envVar: condition.name, value };\n}\n\n/** Detects a sandbox purely from environment variables. */\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly id = \"env\";\n\n detect(sandbox: SandboxDefinition, context: DetectionContext): DetectedSandbox | null {\n for (const matcher of sandbox.env) {\n const conditions = Array.isArray(matcher) ? matcher : [matcher];\n if (conditions.length === 0) continue;\n\n const evidence: DetectionEvidence[] = [];\n for (const condition of conditions) {\n const matched = matchCondition(condition, context.env);\n if (!matched) break;\n evidence.push(matched);\n }\n if (evidence.length !== conditions.length) continue;\n\n return {\n id: sandbox.id,\n name: sandbox.name,\n category: sandbox.category,\n instanceId: this.findInstanceId(sandbox, context),\n evidence,\n };\n }\n return null;\n }\n\n private findInstanceId(\n sandbox: SandboxDefinition,\n context: DetectionContext,\n ): string | undefined {\n for (const name of sandbox.idEnv ?? []) {\n const value = context.env[name];\n if (value) return value;\n }\n return undefined;\n }\n}\n\nexport function createDefaultStrategies(): DetectionStrategy[] {\n return [new EnvironmentDetectionStrategy()];\n}\n","import { defaultSandboxes } from \"./sandboxes.js\";\nimport { createDefaultStrategies } from \"./strategies.js\";\nimport type { DetectedSandbox, DetectionResult, DetectSandboxOptions } from \"./types.js\";\n\nexport { defaultSandboxes } from \"./sandboxes.js\";\nexport { createDefaultStrategies, EnvironmentDetectionStrategy } from \"./strategies.js\";\nexport type {\n DetectedSandbox,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy,\n DetectSandboxOptions,\n EnvCondition,\n EnvMatcher,\n SandboxCategory,\n SandboxDefinition,\n} from \"./types.js\";\n\n/**\n * Detect whether the current process runs inside a known sandboxed\n * environment (AI code-execution sandbox, AI app builder, or cloud IDE).\n */\nexport function detectSandbox(options: DetectSandboxOptions = {}): DetectionResult {\n const env = options.env ?? process.env;\n const sandboxes = options.sandboxes ?? defaultSandboxes;\n const strategies = options.strategies ?? createDefaultStrategies();\n const context = { env };\n\n const matches: DetectedSandbox[] = [];\n for (const sandbox of sandboxes) {\n for (const strategy of strategies) {\n const detected = strategy.detect(sandbox, context);\n if (detected) {\n matches.push(detected);\n break;\n }\n }\n }\n\n return {\n detected: matches.length > 0,\n sandbox: matches[0] ?? null,\n matches,\n };\n}\n\n/** Convenience wrapper returning just the boolean. */\nexport function isRunningInSandbox(options: DetectSandboxOptions = {}): boolean {\n return detectSandbox(options).detected;\n}\n"],"mappings":"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","names":[]} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
31999
-55.46%338
-51.23%140
-42.39%1
Infinity%