agent-cli-detector
Advanced tools
+5
-0
@@ -106,2 +106,7 @@ #!/usr/bin/env node | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| env: [{ name: "RORK_API_URL" }] | ||
| }, | ||
| { | ||
| id: "kiro", | ||
@@ -108,0 +113,0 @@ name: "Kiro", |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts","../src/cli.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n","#!/usr/bin/env node\nimport { detectAgent } from \"./index.js\";\nimport type { DetectionResult } from \"./types.js\";\n\nconst args = new Set(process.argv.slice(2));\nconst json = args.has(\"--json\");\nconst quiet = args.has(\"--quiet\");\nconst help = args.has(\"--help\") || args.has(\"-h\");\n\nif (help) {\n printHelp();\n process.exit(0);\n}\n\nconst result = detectAgent();\n\nif (json) {\n console.log(JSON.stringify(toJsonResult(result), null, 2));\n} else if (!quiet) {\n if (result.detected && result.agent !== undefined) {\n console.log(`detected: ${result.agent.name}`);\n } else {\n console.log(\"no coding agent detected\");\n }\n}\n\nprocess.exit(result.detected ? 0 : 1);\n\nfunction printHelp(): void {\n console.log(`Usage: detect-agent [--json] [--quiet]\n\nDetect whether the current process is running inside a coding agent.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet Print nothing and use only the exit code\n -h, --help\n Show this help message\n`);\n}\n\nfunction toJsonResult(result: DetectionResult): object {\n return {\n detected: result.detected,\n agent: result.agent === undefined\n ? undefined\n : {\n id: result.agent.id,\n name: result.agent.name,\n sessionId: result.agent.sessionId\n }\n };\n}\n"],"mappings":"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","names":["toEvidence","path","result"]} | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts","../src/cli.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"rork\",\n name: \"Rork\",\n env: [{ name: \"RORK_API_URL\" }]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n","#!/usr/bin/env node\nimport { detectAgent } from \"./index.js\";\nimport type { DetectionResult } from \"./types.js\";\n\nconst args = new Set(process.argv.slice(2));\nconst json = args.has(\"--json\");\nconst quiet = args.has(\"--quiet\");\nconst help = args.has(\"--help\") || args.has(\"-h\");\n\nif (help) {\n printHelp();\n process.exit(0);\n}\n\nconst result = detectAgent();\n\nif (json) {\n console.log(JSON.stringify(toJsonResult(result), null, 2));\n} else if (!quiet) {\n if (result.detected && result.agent !== undefined) {\n console.log(`detected: ${result.agent.name}`);\n } else {\n console.log(\"no coding agent detected\");\n }\n}\n\nprocess.exit(result.detected ? 0 : 1);\n\nfunction printHelp(): void {\n console.log(`Usage: detect-agent [--json] [--quiet]\n\nDetect whether the current process is running inside a coding agent.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet Print nothing and use only the exit code\n -h, --help\n Show this help message\n`);\n}\n\nfunction toJsonResult(result: DetectionResult): object {\n return {\n detected: result.detected,\n agent: result.agent === undefined\n ? undefined\n : {\n id: result.agent.id,\n name: result.agent.name,\n sessionId: result.agent.sessionId\n }\n };\n}\n"],"mappings":"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","names":["toEvidence","path","result"]} |
+5
-0
@@ -83,2 +83,7 @@ #!/usr/bin/env node | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| env: [{ name: "RORK_API_URL" }] | ||
| }, | ||
| { | ||
| id: "kiro", | ||
@@ -85,0 +90,0 @@ name: "Kiro", |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts","../src/cli.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n","#!/usr/bin/env node\nimport { detectAgent } from \"./index.js\";\nimport type { DetectionResult } from \"./types.js\";\n\nconst args = new Set(process.argv.slice(2));\nconst json = args.has(\"--json\");\nconst quiet = args.has(\"--quiet\");\nconst help = args.has(\"--help\") || args.has(\"-h\");\n\nif (help) {\n printHelp();\n process.exit(0);\n}\n\nconst result = detectAgent();\n\nif (json) {\n console.log(JSON.stringify(toJsonResult(result), null, 2));\n} else if (!quiet) {\n if (result.detected && result.agent !== undefined) {\n console.log(`detected: ${result.agent.name}`);\n } else {\n console.log(\"no coding agent detected\");\n }\n}\n\nprocess.exit(result.detected ? 0 : 1);\n\nfunction printHelp(): void {\n console.log(`Usage: detect-agent [--json] [--quiet]\n\nDetect whether the current process is running inside a coding agent.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet Print nothing and use only the exit code\n -h, --help\n Show this help message\n`);\n}\n\nfunction toJsonResult(result: DetectionResult): object {\n return {\n detected: result.detected,\n agent: result.agent === undefined\n ? undefined\n : {\n id: result.agent.id,\n name: result.agent.name,\n sessionId: result.agent.sessionId\n }\n };\n}\n"],"mappings":"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","names":["toEvidence","result"]} | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts","../src/cli.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"rork\",\n name: \"Rork\",\n env: [{ name: \"RORK_API_URL\" }]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n","#!/usr/bin/env node\nimport { detectAgent } from \"./index.js\";\nimport type { DetectionResult } from \"./types.js\";\n\nconst args = new Set(process.argv.slice(2));\nconst json = args.has(\"--json\");\nconst quiet = args.has(\"--quiet\");\nconst help = args.has(\"--help\") || args.has(\"-h\");\n\nif (help) {\n printHelp();\n process.exit(0);\n}\n\nconst result = detectAgent();\n\nif (json) {\n console.log(JSON.stringify(toJsonResult(result), null, 2));\n} else if (!quiet) {\n if (result.detected && result.agent !== undefined) {\n console.log(`detected: ${result.agent.name}`);\n } else {\n console.log(\"no coding agent detected\");\n }\n}\n\nprocess.exit(result.detected ? 0 : 1);\n\nfunction printHelp(): void {\n console.log(`Usage: detect-agent [--json] [--quiet]\n\nDetect whether the current process is running inside a coding agent.\n\nOptions:\n --json Print the full detection result as JSON\n --quiet Print nothing and use only the exit code\n -h, --help\n Show this help message\n`);\n}\n\nfunction toJsonResult(result: DetectionResult): object {\n return {\n detected: result.detected,\n agent: result.agent === undefined\n ? undefined\n : {\n id: result.agent.id,\n name: result.agent.name,\n sessionId: result.agent.sessionId\n }\n };\n}\n"],"mappings":"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","names":["toEvidence","result"]} |
+5
-0
@@ -123,2 +123,7 @@ "use strict"; | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| env: [{ name: "RORK_API_URL" }] | ||
| }, | ||
| { | ||
| id: "kiro", | ||
@@ -125,0 +130,0 @@ name: "Kiro", |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts"],"sourcesContent":["export { defaultAgents } from \"./agents.js\";\nexport {\n createDefaultStrategies,\n detectAgent,\n isRunningFromAgent\n} from \"./detector.js\";\nexport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nexport {\n ProcessTreeDetectionStrategy,\n PsProcessReader,\n type ProcessReader\n} from \"./strategies/process-tree.js\";\nexport type {\n AgentDefinition,\n AgentId,\n AgentIdentity,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy,\n DetectionStrategyName,\n EnvSignal,\n EnvValueMatcher,\n ProcessInfo,\n ProcessSignal\n} from \"./types.js\";\n","import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n"],"mappings":"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","names":["toEvidence","path"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts"],"sourcesContent":["export { defaultAgents } from \"./agents.js\";\nexport {\n createDefaultStrategies,\n detectAgent,\n isRunningFromAgent\n} from \"./detector.js\";\nexport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nexport {\n ProcessTreeDetectionStrategy,\n PsProcessReader,\n type ProcessReader\n} from \"./strategies/process-tree.js\";\nexport type {\n AgentDefinition,\n AgentId,\n AgentIdentity,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy,\n DetectionStrategyName,\n EnvSignal,\n EnvValueMatcher,\n ProcessInfo,\n ProcessSignal\n} from \"./types.js\";\n","import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"rork\",\n name: \"Rork\",\n env: [{ name: \"RORK_API_URL\" }]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n"],"mappings":"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","names":["toEvidence","path"]} |
+1
-1
@@ -71,3 +71,3 @@ declare const defaultAgents: ({ | ||
| type AgentId = "antigravity" | "bolt" | "cline" | "codex" | "copilot" | "cursor" | "devin" | "gemini" | "kiro" | "kilocode" | "claude-code" | "opencode" | "pi" | "replit"; | ||
| type AgentId = "antigravity" | "bolt" | "cline" | "codex" | "copilot" | "cursor" | "devin" | "gemini" | "kiro" | "kilocode" | "claude-code" | "opencode" | "pi" | "replit" | "rork"; | ||
| type DetectionStrategyName = "environment" | "process-tree"; | ||
@@ -74,0 +74,0 @@ type EnvValueMatcher = string | RegExp | ((value: string) => boolean); |
+1
-1
@@ -71,3 +71,3 @@ declare const defaultAgents: ({ | ||
| type AgentId = "antigravity" | "bolt" | "cline" | "codex" | "copilot" | "cursor" | "devin" | "gemini" | "kiro" | "kilocode" | "claude-code" | "opencode" | "pi" | "replit"; | ||
| type AgentId = "antigravity" | "bolt" | "cline" | "codex" | "copilot" | "cursor" | "devin" | "gemini" | "kiro" | "kilocode" | "claude-code" | "opencode" | "pi" | "replit" | "rork"; | ||
| type DetectionStrategyName = "environment" | "process-tree"; | ||
@@ -74,0 +74,0 @@ type EnvValueMatcher = string | RegExp | ((value: string) => boolean); |
+5
-0
@@ -81,2 +81,7 @@ // src/agents.ts | ||
| { | ||
| id: "rork", | ||
| name: "Rork", | ||
| env: [{ name: "RORK_API_URL" }] | ||
| }, | ||
| { | ||
| id: "kiro", | ||
@@ -83,0 +88,0 @@ name: "Kiro", |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n"],"mappings":"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","names":["toEvidence"]} | ||
| {"version":3,"sources":["../src/agents.ts","../src/strategies/environment.ts","../src/strategies/process-tree.ts","../src/detector.ts"],"sourcesContent":["import type { AgentDefinition } from \"./types.js\";\n\nexport const defaultAgents = [\n {\n id: \"codex\",\n name: \"Codex\",\n env: [\n { name: \"CODEX_CI\", value: \"1\" },\n { name: \"CODEX_SHELL\", value: \"1\" },\n { name: \"CODEX_THREAD_ID\" }\n ],\n process: [{ pattern: /^codex$/i }],\n sessionEnv: [\"CODEX_THREAD_ID\"]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n env: [\n { name: \"CURSOR_AGENT\", value: \"1\" },\n { name: \"CURSOR_CONVERSATION_ID\" }\n ],\n process: [{ pattern: /^cursor$/i }],\n sessionEnv: [\"CURSOR_CONVERSATION_ID\"]\n },\n {\n id: \"claude-code\",\n name: \"Claude Code\",\n env: [\n { name: \"CLAUDECODE\", value: \"1\" },\n { name: \"CLAUDE_CODE_SESSION_ID\" }\n ],\n process: [{ pattern: /^claude$/i }],\n sessionEnv: [\"CLAUDE_CODE_SESSION_ID\"]\n },\n {\n id: \"opencode\",\n name: \"OpenCode\",\n process: [{ pattern: /^opencode$/i }]\n },\n {\n id: \"devin\",\n name: \"Devin\",\n process: [{ pattern: /devin/i }]\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n env: [{ name: \"GEMINI_CLI\", value: \"1\" }],\n process: [{ pattern: /^gemini$/i }]\n },\n {\n id: \"antigravity\",\n name: \"Antigravity\",\n env: [\n { name: \"ANTIGRAVITY_AGENT\", value: \"1\" },\n { name: \"ANTIGRAVITY_TRAJECTORY_ID\" }\n ],\n process: [{ pattern: /^antigravity$/i }],\n sessionEnv: [\"ANTIGRAVITY_TRAJECTORY_ID\"]\n },\n {\n id: \"bolt\",\n name: \"Bolt\",\n env: [\n { name: \"BOLT_ENV\" },\n { name: \"BOLT_ORIGIN\" },\n { name: \"BOLT_SERVER_URL\" }\n ]\n },\n {\n id: \"pi\",\n name: \"Pi\",\n env: [{ name: \"PI_CODING_AGENT\", value: \"true\" }]\n },\n {\n id: \"replit\",\n name: \"Replit\",\n env: [{ name: \"REPLIT_SESSION\" }],\n sessionEnv: [\"REPLIT_SESSION\"]\n },\n {\n id: \"rork\",\n name: \"Rork\",\n env: [{ name: \"RORK_API_URL\" }]\n },\n {\n id: \"kiro\",\n name: \"Kiro\",\n env: [{ name: \"KIRO_SESSION_ID\" }],\n process: [{ pattern: /kiro/i }],\n sessionEnv: [\"KIRO_SESSION_ID\"]\n },\n {\n id: \"cline\",\n name: \"Cline\",\n env: [{ name: \"CLINE_WRAPPER_PATH\" }],\n process: [{ pattern: /^cline$/i }]\n },\n {\n id: \"kilocode\",\n name: \"Kilo Code\",\n env: [\n { name: \"KILO\", value: \"1\" },\n { name: \"KILOCODE_VERSION\" },\n { name: \"KILO_RUN_ID\" }\n ],\n process: [{ pattern: /^(kilo|kilocode)$/i }],\n sessionEnv: [\"KILO_RUN_ID\"]\n },\n {\n id: \"copilot\",\n name: \"GitHub Copilot CLI\",\n env: [\n { name: \"COPILOT_CLI\", value: \"1\" },\n { name: \"COPILOT_AGENT_SESSION_ID\" },\n { name: \"COPILOT_RUN_APP\", value: \"1\" }\n ],\n process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],\n sessionEnv: [\"COPILOT_AGENT_SESSION_ID\"]\n }\n] satisfies readonly AgentDefinition[];\n","import type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n EnvSignal,\n EnvValueMatcher\n} from \"../types.js\";\n\nexport class EnvironmentDetectionStrategy implements DetectionStrategy {\n readonly name = \"environment\";\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n\n for (const agent of context.agents) {\n for (const signal of agent.env ?? []) {\n const value = context.env[signal.name];\n\n if (value === undefined || !matchesEnvValue(value, signal.value)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, value));\n }\n }\n\n return matches;\n }\n}\n\nfunction matchesEnvValue(value: string, matcher: EnvValueMatcher | undefined): boolean {\n if (matcher === undefined) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return value === matcher;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(value);\n }\n\n return matcher(value);\n}\n\nfunction toEvidence(agent: AgentDefinition, signal: EnvSignal, value: string): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"environment\",\n signal: signal.name,\n value\n };\n}\n","import { execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type {\n AgentDefinition,\n DetectionContext,\n DetectionEvidence,\n DetectionStrategy,\n ProcessInfo,\n ProcessSignal\n} from \"../types.js\";\n\nexport interface ProcessReader {\n read(pid: number): ProcessInfo | undefined;\n}\n\nexport class PsProcessReader implements ProcessReader {\n read(pid: number): ProcessInfo | undefined {\n const command = readPsField(pid, \"comm\");\n const ppid = Number.parseInt(readPsField(pid, \"ppid\"), 10);\n\n if (!command || !Number.isInteger(ppid)) {\n return undefined;\n }\n\n return {\n pid,\n ppid,\n command: normalizeCommand(command)\n };\n }\n}\n\nexport class ProcessTreeDetectionStrategy implements DetectionStrategy {\n readonly name = \"process-tree\";\n\n constructor(private readonly reader: ProcessReader = new PsProcessReader()) {}\n\n detect(context: DetectionContext): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n let currentPid = context.pid;\n\n for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {\n const processInfo = this.reader.read(currentPid);\n\n if (processInfo === undefined) {\n break;\n }\n\n matches.push(...matchProcess(context.agents, processInfo));\n\n if (processInfo.ppid === undefined || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {\n break;\n }\n\n currentPid = processInfo.ppid;\n }\n\n return matches;\n }\n}\n\nfunction matchProcess(\n agents: readonly AgentDefinition[],\n processInfo: ProcessInfo\n): readonly DetectionEvidence[] {\n const matches: DetectionEvidence[] = [];\n const command = normalizeCommand(processInfo.command);\n\n for (const agent of agents) {\n for (const signal of agent.process ?? []) {\n if (!signal.pattern.test(command)) {\n continue;\n }\n\n matches.push(toEvidence(agent, signal, { ...processInfo, command }));\n }\n }\n\n return matches;\n}\n\nfunction toEvidence(\n agent: AgentDefinition,\n signal: ProcessSignal,\n processInfo: ProcessInfo\n): DetectionEvidence {\n return {\n agent: {\n id: agent.id,\n name: agent.name\n },\n strategy: \"process-tree\",\n signal: signal.pattern.toString(),\n value: `${processInfo.pid}:${processInfo.command}`\n };\n}\n\nfunction readPsField(pid: number, field: \"comm\" | \"ppid\"): string {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"]\n }).trim();\n } catch {\n return \"\";\n }\n}\n\nfunction normalizeCommand(command: string): string {\n const baseName = path.basename(command.trim());\n return baseName.replace(/^-+/, \"\");\n}\n","import { defaultAgents } from \"./agents.js\";\nimport { EnvironmentDetectionStrategy } from \"./strategies/environment.js\";\nimport { ProcessTreeDetectionStrategy } from \"./strategies/process-tree.js\";\nimport type {\n AgentDefinition,\n DetectAgentOptions,\n DetectedAgent,\n DetectionContext,\n DetectionEvidence,\n DetectionResult,\n DetectionStrategy\n} from \"./types.js\";\n\nexport function detectAgent(options: DetectAgentOptions = {}): DetectionResult {\n const agents = options.agents ?? defaultAgents;\n const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);\n const context: DetectionContext = {\n env: options.env ?? process.env,\n pid: options.pid ?? process.pid,\n agents,\n maxProcessDepth: options.maxProcessDepth ?? 20\n };\n\n const matches = strategies.flatMap((strategy) => strategy.detect(context));\n const bestMatch = selectBestAgent(agents, matches, context.env);\n\n if (bestMatch === undefined) {\n return {\n detected: false\n };\n }\n\n return {\n detected: true,\n agent: bestMatch\n };\n}\n\nexport function isRunningFromAgent(options: DetectAgentOptions = {}): boolean {\n return detectAgent(options).detected;\n}\n\nexport function createDefaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n return defaultStrategies(experimentalProcessTree);\n}\n\nfunction defaultStrategies(experimentalProcessTree = false): readonly DetectionStrategy[] {\n const strategies: DetectionStrategy[] = [new EnvironmentDetectionStrategy()];\n\n if (experimentalProcessTree) {\n strategies.push(new ProcessTreeDetectionStrategy());\n }\n\n return strategies;\n}\n\nfunction selectBestAgent(\n agents: readonly AgentDefinition[],\n matches: readonly DetectionEvidence[],\n env: NodeJS.ProcessEnv\n): DetectedAgent | undefined {\n const matchedAgentIds = new Set(matches.map((match) => match.agent.id));\n\n for (const agent of agents) {\n if (!matchedAgentIds.has(agent.id)) {\n continue;\n }\n\n return {\n id: agent.id,\n name: agent.name,\n ...sessionIdProperties(agent, env)\n };\n }\n\n return undefined;\n}\n\nfunction sessionIdProperties(\n agent: AgentDefinition,\n env: NodeJS.ProcessEnv\n): { readonly sessionId?: string } {\n for (const envName of agent.sessionEnv ?? []) {\n const value = env[envName];\n\n if (value !== undefined && value.length > 0) {\n return { sessionId: value };\n }\n }\n\n return {};\n}\n"],"mappings":";AAEO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,YAAY,OAAO,IAAI;AAAA,MAC/B,EAAE,MAAM,eAAe,OAAO,IAAI;AAAA,MAClC,EAAE,MAAM,kBAAkB;AAAA,IAC5B;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,WAAW,CAAC;AAAA,IACjC,YAAY,CAAC,iBAAiB;AAAA,EAChC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,gBAAgB,OAAO,IAAI;AAAA,MACnC,EAAE,MAAM,yBAAyB;AAAA,IACnC;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,YAAY,CAAC;AAAA,IAClC,YAAY,CAAC,wBAAwB;AAAA,EACvC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,cAAc,OAAO,IAAI;AAAA,MACjC,EAAE,MAAM,yBAAyB;AAAA,IACnC;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,YAAY,CAAC;AAAA,IAClC,YAAY,CAAC,wBAAwB;AAAA,EACvC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,EAAE,SAAS,cAAc,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,EAAE,SAAS,SAAS,CAAC;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,cAAc,OAAO,IAAI,CAAC;AAAA,IACxC,SAAS,CAAC,EAAE,SAAS,YAAY,CAAC;AAAA,EACpC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,qBAAqB,OAAO,IAAI;AAAA,MACxC,EAAE,MAAM,4BAA4B;AAAA,IACtC;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,iBAAiB,CAAC;AAAA,IACvC,YAAY,CAAC,2BAA2B;AAAA,EAC1C;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,WAAW;AAAA,MACnB,EAAE,MAAM,cAAc;AAAA,MACtB,EAAE,MAAM,kBAAkB;AAAA,IAC5B;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,mBAAmB,OAAO,OAAO,CAAC;AAAA,EAClD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAAA,IAChC,YAAY,CAAC,gBAAgB;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,eAAe,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAAA,IACjC,SAAS,CAAC,EAAE,SAAS,QAAQ,CAAC;AAAA,IAC9B,YAAY,CAAC,iBAAiB;AAAA,EAChC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAAA,IACpC,SAAS,CAAC,EAAE,SAAS,WAAW,CAAC;AAAA,EACnC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,QAAQ,OAAO,IAAI;AAAA,MAC3B,EAAE,MAAM,mBAAmB;AAAA,MAC3B,EAAE,MAAM,cAAc;AAAA,IACxB;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,qBAAqB,CAAC;AAAA,IAC3C,YAAY,CAAC,aAAa;AAAA,EAC5B;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,MACH,EAAE,MAAM,eAAe,OAAO,IAAI;AAAA,MAClC,EAAE,MAAM,2BAA2B;AAAA,MACnC,EAAE,MAAM,mBAAmB,OAAO,IAAI;AAAA,IACxC;AAAA,IACA,SAAS,CAAC,EAAE,SAAS,kCAAkC,CAAC;AAAA,IACxD,YAAY,CAAC,0BAA0B;AAAA,EACzC;AACF;;;AC/GO,IAAM,+BAAN,MAAgE;AAAA,EAC5D,OAAO;AAAA,EAEhB,OAAO,SAAyD;AAC9D,UAAM,UAA+B,CAAC;AAEtC,eAAW,SAAS,QAAQ,QAAQ;AAClC,iBAAW,UAAU,MAAM,OAAO,CAAC,GAAG;AACpC,cAAM,QAAQ,QAAQ,IAAI,OAAO,IAAI;AAErC,YAAI,UAAU,UAAa,CAAC,gBAAgB,OAAO,OAAO,KAAK,GAAG;AAChE;AAAA,QACF;AAEA,gBAAQ,KAAK,WAAW,OAAO,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,OAAe,SAA+C;AACrF,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,UAAU;AAAA,EACnB;AAEA,MAAI,mBAAmB,QAAQ;AAC7B,WAAO,QAAQ,KAAK,KAAK;AAAA,EAC3B;AAEA,SAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,WAAW,OAAwB,QAAmB,OAAkC;AAC/F,SAAO;AAAA,IACL,OAAO;AAAA,MACL,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,IACd;AAAA,IACA,UAAU;AAAA,IACV,QAAQ,OAAO;AAAA,IACf;AAAA,EACF;AACF;;;ACzDA,SAAS,oBAAoB;AAC7B,OAAO,UAAU;AAcV,IAAM,kBAAN,MAA+C;AAAA,EACpD,KAAK,KAAsC;AACzC,UAAM,UAAU,YAAY,KAAK,MAAM;AACvC,UAAM,OAAO,OAAO,SAAS,YAAY,KAAK,MAAM,GAAG,EAAE;AAEzD,QAAI,CAAC,WAAW,CAAC,OAAO,UAAU,IAAI,GAAG;AACvC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,iBAAiB,OAAO;AAAA,IACnC;AAAA,EACF;AACF;AAEO,IAAM,+BAAN,MAAgE;AAAA,EAGrE,YAA6B,SAAwB,IAAI,gBAAgB,GAAG;AAA/C;AAAA,EAAgD;AAAA,EAAhD;AAAA,EAFpB,OAAO;AAAA,EAIhB,OAAO,SAAyD;AAC9D,UAAM,UAA+B,CAAC;AACtC,QAAI,aAAa,QAAQ;AAEzB,aAAS,QAAQ,GAAG,QAAQ,QAAQ,iBAAiB,SAAS,GAAG;AAC/D,YAAM,cAAc,KAAK,OAAO,KAAK,UAAU;AAE/C,UAAI,gBAAgB,QAAW;AAC7B;AAAA,MACF;AAEA,cAAQ,KAAK,GAAG,aAAa,QAAQ,QAAQ,WAAW,CAAC;AAEzD,UAAI,YAAY,SAAS,UAAa,YAAY,QAAQ,KAAK,YAAY,SAAS,YAAY;AAC9F;AAAA,MACF;AAEA,mBAAa,YAAY;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aACP,QACA,aAC8B;AAC9B,QAAM,UAA+B,CAAC;AACtC,QAAM,UAAU,iBAAiB,YAAY,OAAO;AAEpD,aAAW,SAAS,QAAQ;AAC1B,eAAW,UAAU,MAAM,WAAW,CAAC,GAAG;AACxC,UAAI,CAAC,OAAO,QAAQ,KAAK,OAAO,GAAG;AACjC;AAAA,MACF;AAEA,cAAQ,KAAKA,YAAW,OAAO,QAAQ,EAAE,GAAG,aAAa,QAAQ,CAAC,CAAC;AAAA,IACrE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAASA,YACP,OACA,QACA,aACmB;AACnB,SAAO;AAAA,IACL,OAAO;AAAA,MACL,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,IACd;AAAA,IACA,UAAU;AAAA,IACV,QAAQ,OAAO,QAAQ,SAAS;AAAA,IAChC,OAAO,GAAG,YAAY,GAAG,IAAI,YAAY,OAAO;AAAA,EAClD;AACF;AAEA,SAAS,YAAY,KAAa,OAAgC;AAChE,MAAI;AACF,WAAO,aAAa,MAAM,CAAC,MAAM,GAAG,KAAK,KAAK,MAAM,OAAO,GAAG,CAAC,GAAG;AAAA,MAChE,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC,EAAE,KAAK;AAAA,EACV,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,SAAyB;AACjD,QAAM,WAAW,KAAK,SAAS,QAAQ,KAAK,CAAC;AAC7C,SAAO,SAAS,QAAQ,OAAO,EAAE;AACnC;;;AClGO,SAAS,YAAY,UAA8B,CAAC,GAAoB;AAC7E,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,aAAa,QAAQ,cAAc,kBAAkB,QAAQ,uBAAuB;AAC1F,QAAM,UAA4B;AAAA,IAChC,KAAK,QAAQ,OAAO,QAAQ;AAAA,IAC5B,KAAK,QAAQ,OAAO,QAAQ;AAAA,IAC5B;AAAA,IACA,iBAAiB,QAAQ,mBAAmB;AAAA,EAC9C;AAEA,QAAM,UAAU,WAAW,QAAQ,CAAC,aAAa,SAAS,OAAO,OAAO,CAAC;AACzE,QAAM,YAAY,gBAAgB,QAAQ,SAAS,QAAQ,GAAG;AAE9D,MAAI,cAAc,QAAW;AAC3B,WAAO;AAAA,MACL,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAA8B,CAAC,GAAY;AAC5E,SAAO,YAAY,OAAO,EAAE;AAC9B;AAEO,SAAS,wBAAwB,0BAA0B,OAAqC;AACrG,SAAO,kBAAkB,uBAAuB;AAClD;AAEA,SAAS,kBAAkB,0BAA0B,OAAqC;AACxF,QAAM,aAAkC,CAAC,IAAI,6BAA6B,CAAC;AAE3E,MAAI,yBAAyB;AAC3B,eAAW,KAAK,IAAI,6BAA6B,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAEA,SAAS,gBACP,QACA,SACA,KAC2B;AAC3B,QAAM,kBAAkB,IAAI,IAAI,QAAQ,IAAI,CAAC,UAAU,MAAM,MAAM,EAAE,CAAC;AAEtE,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,gBAAgB,IAAI,MAAM,EAAE,GAAG;AAClC;AAAA,IACF;AAEA,WAAO;AAAA,MACL,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,MACZ,GAAG,oBAAoB,OAAO,GAAG;AAAA,IACnC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,oBACP,OACA,KACiC;AACjC,aAAW,WAAW,MAAM,cAAc,CAAC,GAAG;AAC5C,UAAM,QAAQ,IAAI,OAAO;AAEzB,QAAI,UAAU,UAAa,MAAM,SAAS,GAAG;AAC3C,aAAO,EAAE,WAAW,MAAM;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,CAAC;AACV;","names":["toEvidence"]} |
+1
-1
| { | ||
| "name": "agent-cli-detector", | ||
| "version": "0.1.3", | ||
| "version": "0.1.4", | ||
| "description": "Detect whether a JavaScript CLI is running inside a coding agent.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+1
-0
@@ -51,2 +51,3 @@ # agent-cli-detector | ||
| | [Replit](https://replit.com/) | `replit` | ✅ | | ||
| | [Rork](https://rork.com/) | `rork` | 🚫 | | ||
@@ -53,0 +54,0 @@ Detection is data-driven: the exact environment variables and process |
120544
0.97%1484
1.37%179
0.56%