@stdiobus/workers-registry 1.5.0-beta.2 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../workers-registry/registry-launcher/src/config/config.ts", "../../../../workers-registry/registry-launcher/src/config/types.ts", "../../../../workers-registry/registry-launcher/src/config/api-keys.ts", "../../../../workers-registry/registry-launcher/src/registry/resolver.ts", "../../../../workers-registry/registry-launcher/src/registry/index.ts", "../../../../workers-registry/registry-launcher/src/stream/ndjson-handler.ts", "../../../../workers-registry/registry-launcher/src/runtime/agent-runtime.ts", "../../../../workers-registry/registry-launcher/src/runtime/manager.ts", "../../../../workers-registry/registry-launcher/src/router/message-router.ts", "../../../../workers-registry/registry-launcher/src/auth/types.ts", "../../../../workers-registry/registry-launcher/src/log.ts", "../../../../workers-registry/registry-launcher/src/auth/flows/terminal-auth-flow.ts", "../../../../workers-registry/registry-launcher/src/auth/session.ts", "../../../../workers-registry/registry-launcher/src/auth/state.ts", "../../../../workers-registry/registry-launcher/src/auth/pkce.ts", "../../../../workers-registry/registry-launcher/src/auth/flows/callback-server.ts", "../../../../workers-registry/registry-launcher/src/auth/flows/agent-auth-flow.ts", "../../../../workers-registry/registry-launcher/src/auth/storage/keychain-backend.ts", "../../../../workers-registry/registry-launcher/src/auth/storage/encrypted-file-backend.ts", "../../../../workers-registry/registry-launcher/src/auth/storage/memory-backend.ts", "../../../../workers-registry/registry-launcher/src/auth/storage/credential-store.ts", "../../../../workers-registry/registry-launcher/src/auth/token-manager.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/base-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/github-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/google-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/cognito-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/entra-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/oidc-provider.ts", "../../../../workers-registry/registry-launcher/src/auth/providers/index.ts", "../../../../workers-registry/registry-launcher/src/auth/model-credentials/types.ts", "../../../../workers-registry/registry-launcher/src/auth/model-credentials/openai-api-key.ts", "../../../../workers-registry/registry-launcher/src/auth/model-credentials/anthropic-api-key.ts", "../../../../workers-registry/registry-launcher/src/auth/auth-manager.ts", "../../../../workers-registry/registry-launcher/src/auth/cli/setup-command.ts", "../../../../workers-registry/registry-launcher/src/auth/cli/status-command.ts", "../../../../workers-registry/registry-launcher/src/auth/cli/logout-command.ts", "../../../../workers-registry/registry-launcher/src/auth/cli/login-command.ts", "../../../../workers-registry/registry-launcher/src/index.ts"],
4
- "sourcesContent": ["/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration loading for the Registry Launcher.\n *\n * Handles loading configuration from JSON files with support for:\n * - Environment variable overrides (ACP_REGISTRY_URL)\n * - Default values for missing fields\n * - Warning logs for missing or malformed config files\n *\n * @module config\n */\n\nimport { readFileSync } from 'node:fs';\nimport { DEFAULT_CONFIG, LauncherConfig } from './types.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Environment variable name for API keys path override.\n */\nconst ENV_API_KEYS_PATH = 'ACP_API_KEYS_PATH';\n\n/**\n * Environment variable name for custom agents file path override.\n */\nconst ENV_CUSTOM_AGENTS_PATH = 'ACP_CUSTOM_AGENTS_PATH';\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [config] ${message}`);\n}\n\n/**\n * Validate that a value is a non-empty string.\n * @param value - Value to validate\n * @returns True if value is a non-empty string\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a positive number.\n * @param value - Value to validate\n * @returns True if value is a positive number\n */\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && value > 0 && Number.isFinite(value);\n}\n\n/**\n * Parse and validate a configuration object.\n * Applies default values for missing or invalid fields.\n *\n * @param obj - Parsed JSON object\n * @returns Validated LauncherConfig with defaults applied\n */\nfunction parseConfigObject(obj: unknown): LauncherConfig {\n const config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (obj === null || typeof obj !== 'object') {\n logWarning('Config file does not contain a valid object, using defaults');\n return config;\n }\n\n const rawConfig = obj as Record<string, unknown>;\n\n // Parse registryUrl\n if ('registryUrl' in rawConfig) {\n if (isNonEmptyString(rawConfig.registryUrl)) {\n config.registryUrl = rawConfig.registryUrl;\n } else {\n logWarning('Config field \"registryUrl\" is not a valid string, using default');\n }\n }\n\n // Parse apiKeysPath\n if ('apiKeysPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.apiKeysPath)) {\n config.apiKeysPath = rawConfig.apiKeysPath;\n } else {\n logWarning('Config field \"apiKeysPath\" is not a valid string, using default');\n }\n }\n\n // Parse shutdownTimeoutSec\n if ('shutdownTimeoutSec' in rawConfig) {\n if (isPositiveNumber(rawConfig.shutdownTimeoutSec)) {\n config.shutdownTimeoutSec = rawConfig.shutdownTimeoutSec;\n } else {\n logWarning('Config field \"shutdownTimeoutSec\" is not a valid positive number, using default');\n }\n }\n\n // Parse customAgentsPath\n if ('customAgentsPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.customAgentsPath)) {\n config.customAgentsPath = rawConfig.customAgentsPath;\n } else {\n logWarning('Config field \"customAgentsPath\" is not a valid string, ignoring');\n }\n }\n\n return config;\n}\n\n/**\n * Apply environment variable overrides to the configuration.\n * Environment variables take precedence over config file values.\n *\n * @param config - Configuration to apply overrides to\n * @returns Configuration with environment overrides applied\n */\nfunction applyEnvironmentOverrides(config: LauncherConfig): LauncherConfig {\n const envRegistryUrl = process.env[ENV_REGISTRY_URL];\n const envApiKeysPath = process.env[ENV_API_KEYS_PATH];\n const envCustomAgentsPath = process.env[ENV_CUSTOM_AGENTS_PATH];\n\n const overrides: Partial<LauncherConfig> = {};\n\n if (isNonEmptyString(envRegistryUrl)) {\n overrides.registryUrl = envRegistryUrl;\n }\n\n if (isNonEmptyString(envApiKeysPath)) {\n overrides.apiKeysPath = envApiKeysPath;\n }\n\n if (isNonEmptyString(envCustomAgentsPath)) {\n overrides.customAgentsPath = envCustomAgentsPath;\n }\n\n return {\n ...config,\n ...overrides,\n };\n}\n\n/**\n * Load configuration from a JSON file.\n *\n * This function:\n * 1. Reads the config file from the specified path\n * 2. Parses it as JSON\n * 3. Validates and applies default values for missing fields\n * 4. Applies environment variable overrides\n *\n * If the config file is missing or malformed, default values are used\n * and a warning is logged to stderr.\n *\n * @param configPath - Path to the JSON config file (optional)\n * @returns Loaded and validated configuration\n */\nexport function loadConfig(configPath?: string): LauncherConfig {\n let config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (configPath) {\n try {\n const fileContent = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(fileContent);\n config = parseConfigObject(parsed);\n } catch (error) {\n if (error instanceof SyntaxError) {\n logWarning(`Config file \"${configPath}\" contains malformed JSON, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`Config file \"${configPath}\" not found, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n logWarning(`Config file \"${configPath}\" is not readable, using defaults`);\n } else {\n logWarning(`Failed to read config file \"${configPath}\": ${(error as Error).message}, using defaults`);\n }\n }\n }\n\n // Apply environment variable overrides\n config = applyEnvironmentOverrides(config);\n\n return config;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration for the Registry Launcher.\n */\nexport interface LauncherConfig {\n /** Registry URL (default: official ACP Registry) */\n registryUrl: string;\n /** Path to API keys JSON file (default: ./api-keys.json) */\n apiKeysPath: string;\n /** Agent shutdown timeout in seconds (default: 5) */\n shutdownTimeoutSec: number;\n /** Path to custom agents JSON file (optional, loaded via --custom-agents CLI arg) */\n customAgentsPath?: string;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: LauncherConfig = {\n registryUrl: 'https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json',\n apiKeysPath: './api-keys.json',\n shutdownTimeoutSec: 5,\n};\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * API Keys loading for the Registry Launcher.\n *\n * Handles loading API keys from JSON files for agent authentication.\n *\n * @module config/api-keys\n */\n\nimport { readFileSync } from 'node:fs';\n\n/**\n * API keys structure for a single agent.\n */\nexport interface AgentApiKeys {\n /** API key for the agent */\n apiKey: string;\n /** Environment variables to pass to the agent */\n env: Record<string, string>;\n}\n\n/**\n * API keys file structure.\n */\nexport interface ApiKeysFile {\n /** Map of agent ID to API keys */\n agents: Record<string, AgentApiKeys>;\n /** Version of the API keys file format */\n version: string;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [api-keys] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [api-keys] ${message}`);\n}\n\n/**\n * Load API keys from a JSON file.\n *\n * @param apiKeysPath - Path to the API keys JSON file\n * @returns Loaded API keys or empty object if file is missing/malformed\n */\nexport function loadApiKeys(apiKeysPath: string): Record<string, AgentApiKeys> {\n try {\n const fileContent = readFileSync(apiKeysPath, 'utf-8');\n const parsed = JSON.parse(fileContent) as ApiKeysFile;\n\n if (!parsed.agents || typeof parsed.agents !== 'object') {\n logWarning(`API keys file \"${apiKeysPath}\" does not contain valid \"agents\" object`);\n return {};\n }\n\n // Count agents with non-empty API keys\n const agentsWithKeys = Object.entries(parsed.agents).filter(\n ([_, keys]) => keys.apiKey && keys.apiKey.length > 0,\n );\n\n logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from \"${apiKeysPath}\"`);\n\n return parsed.agents;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`API keys file \"${apiKeysPath}\" not found, agents will not be authenticated`);\n } else if (error instanceof SyntaxError) {\n logWarning(`API keys file \"${apiKeysPath}\" contains malformed JSON`);\n } else {\n logWarning(`Failed to read API keys file \"${apiKeysPath}\": ${(error as Error).message}`);\n }\n return {};\n }\n}\n\n/**\n * Get API key for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get key for\n * @returns API key or undefined if not found\n */\nexport function getAgentApiKey(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): string | undefined {\n const keys = apiKeys[agentId];\n if (!keys || !keys.apiKey || keys.apiKey.length === 0) {\n return undefined;\n }\n return keys.apiKey;\n}\n\n/**\n * Get environment variables for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get env vars for\n * @returns Environment variables or empty object if not found\n */\nexport function getAgentEnv(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): Record<string, string> {\n const keys = apiKeys[agentId];\n if (!keys || !keys.env) {\n return {};\n }\n return keys.env;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Distribution Resolver for the ACP Registry.\n *\n * Resolves distribution metadata to spawn commands based on distribution type.\n * Supports binary (platform-specific), npx (npm), and uvx (Python) distributions.\n *\n * @module registry/resolver\n */\n\nimport {\n BinaryDistribution,\n BinaryTarget,\n Distribution,\n NpxDistribution,\n Platform,\n SpawnCommand,\n UvxDistribution,\n} from './types.js';\n\n/**\n * Error thrown when the current platform is not supported by a binary distribution.\n */\nexport class PlatformNotSupportedError extends Error {\n constructor(\n public readonly agentId: string,\n public readonly platform: Platform,\n ) {\n super(`Platform not supported: ${platform} for agent ${agentId}`);\n this.name = 'PlatformNotSupportedError';\n }\n}\n\n/**\n * Error thrown when no supported distribution type is available.\n */\nexport class NoDistributionError extends Error {\n constructor(public readonly agentId: string) {\n super(`No supported distribution type for agent ${agentId}`);\n this.name = 'NoDistributionError';\n }\n}\n\n/**\n * Get the current platform identifier.\n * Maps Node.js platform/arch to ACP Registry platform format.\n *\n * @returns Platform identifier for the current OS and architecture\n */\nexport function getCurrentPlatform(): Platform {\n const platform = process.platform;\n const arch = process.arch;\n\n if (platform === 'darwin' && arch === 'arm64') return 'darwin-aarch64';\n if (platform === 'darwin' && arch === 'x64') return 'darwin-x86_64';\n if (platform === 'linux' && arch === 'arm64') return 'linux-aarch64';\n if (platform === 'linux' && arch === 'x64') return 'linux-x86_64';\n if (platform === 'win32' && arch === 'arm64') return 'windows-aarch64';\n if (platform === 'win32' && arch === 'x64') return 'windows-x86_64';\n\n // Default to linux-x86_64 for unsupported platforms\n return 'linux-x86_64';\n}\n\n/**\n * Resolve a binary distribution to a spawn command.\n *\n * @param distribution - Binary distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if current platform is not supported\n */\nexport function resolveBinary(\n distribution: BinaryDistribution,\n agentId: string,\n): SpawnCommand {\n const currentPlatform = getCurrentPlatform();\n const target: BinaryTarget | undefined = distribution[currentPlatform];\n\n if (!target) {\n throw new PlatformNotSupportedError(agentId, currentPlatform);\n }\n\n return {\n command: target.cmd,\n args: target.args ?? [],\n env: target.env,\n };\n}\n\n/**\n * Resolve an npx distribution to a spawn command.\n *\n * @param distribution - NPX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveNpx(distribution: NpxDistribution): SpawnCommand {\n return {\n command: 'npx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a uvx distribution to a spawn command.\n *\n * @param distribution - UVX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveUvx(distribution: UvxDistribution): SpawnCommand {\n return {\n command: 'uvx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a distribution to a spawn command.\n *\n * Priority order: npx > uvx > binary\n * (npx/uvx are preferred as they don't require platform-specific handling)\n *\n * @param distribution - Distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n * @throws NoDistributionError if no distribution type is available\n */\nexport function resolve(\n distribution: Distribution,\n agentId: string,\n): SpawnCommand {\n // Prefer npx (most common, cross-platform)\n if (distribution.npx) {\n return resolveNpx(distribution.npx);\n }\n\n // Then uvx (Python packages)\n if (distribution.uvx) {\n return resolveUvx(distribution.uvx);\n }\n\n // Finally binary (platform-specific)\n if (distribution.binary) {\n return resolveBinary(distribution.binary, agentId);\n }\n\n throw new NoDistributionError(agentId);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Registry Index for the ACP Registry.\n *\n * Handles fetching, parsing, and querying the ACP Registry.\n * Supports configurable registry URL via environment variable.\n *\n * @module registry/index\n */\n\nimport { AgentAuthMethod, Distribution, McpServerConfig, Registry, RegistryAgent, SpawnCommand } from './types.js';\nimport { resolve as resolveDistribution } from './resolver.js';\nimport { readFileSync } from 'node:fs';\n\n// Re-export types for external use\nexport type {\n Platform,\n BinaryDistribution,\n BinaryTarget,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n McpServerConfig,\n AgentAuthMethod,\n} from './types.js';\n\n// Re-export resolver functions and errors for external use\nexport { PlatformNotSupportedError, NoDistributionError, getCurrentPlatform, resolve } from './resolver.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Error thrown when registry fetch fails.\n */\nexport class RegistryFetchError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryFetchError';\n }\n}\n\n/**\n * Error thrown when registry JSON is malformed.\n */\nexport class RegistryParseError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryParseError';\n }\n}\n\n/**\n * Error thrown when an agent is not found in the registry.\n */\nexport class AgentNotFoundError extends Error {\n constructor(public readonly agentId: string) {\n super(`Agent not found: ${agentId}`);\n this.name = 'AgentNotFoundError';\n }\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [registry] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [registry] ${message}`);\n}\n\n/**\n * Cached auth requirements for an agent.\n *\n * Requirements: 11.2, 11.3\n */\nexport interface AgentAuthRequirements {\n /** Whether authentication is required */\n authRequired: boolean;\n /** Authentication methods supported/required by the agent */\n authMethods: AgentAuthMethod[];\n /** Primary OAuth provider ID (first oauth2 method's providerId) */\n primaryOAuthProviderId?: string;\n}\n\n/**\n * Interface for the RegistryIndex.\n */\nexport interface IRegistryIndex {\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n fetch(): Promise<void>;\n\n /**\n * Look up an agent by ID.\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined;\n\n /**\n * Resolve an agent ID to a spawn command.\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand;\n\n /**\n * Get auth requirements for an agent.\n *\n * Checks the agent definition in the registry for `authRequired` or `authMethods` fields.\n * Results are cached per agent for efficient repeated lookups.\n *\n * Requirements: 11.2, 11.3\n *\n * @param agentId - Agent ID to query\n * @returns Auth requirements or undefined if agent not found\n */\n getAuthRequirements(agentId: string): AgentAuthRequirements | undefined;\n}\n\n/**\n * Validate that a value is a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a valid distribution object.\n * Distribution must have at least one of: binary, npx, uvx\n */\nfunction isValidDistribution(value: unknown): value is Distribution {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n const dist = value as Record<string, unknown>;\n\n // Must have at least one distribution type\n const hasBinary = dist.binary !== undefined && typeof dist.binary === 'object';\n const hasNpx = dist.npx !== undefined && typeof dist.npx === 'object';\n const hasUvx = dist.uvx !== undefined && typeof dist.uvx === 'object';\n\n if (!hasBinary && !hasNpx && !hasUvx) {\n return false;\n }\n\n // Validate npx if present\n if (hasNpx) {\n const npx = dist.npx as Record<string, unknown>;\n if (!isNonEmptyString(npx.package)) {\n return false;\n }\n }\n\n // Validate uvx if present\n if (hasUvx) {\n const uvx = dist.uvx as Record<string, unknown>;\n if (!isNonEmptyString(uvx.package)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate and parse a single MCP server configuration.\n */\nfunction parseMcpServer(value: unknown, agentIndex: number, serverIndex: number): McpServerConfig | null {\n if (value === null || typeof value !== 'object') {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] is not an object, skipping`);\n return null;\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.name)) {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing \"name\" field, skipping`);\n return null;\n }\n\n if (!isNonEmptyString(raw.command)) {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing \"command\" field, skipping`);\n return null;\n }\n\n const server: McpServerConfig = {\n name: raw.name,\n command: raw.command,\n };\n\n // Optional args\n if (Array.isArray(raw.args)) {\n server.args = raw.args.filter((a): a is string => typeof a === 'string');\n }\n\n // Optional env\n if (raw.env !== null && typeof raw.env === 'object' && !Array.isArray(raw.env)) {\n const env: Record<string, string> = {};\n for (const [key, val] of Object.entries(raw.env as Record<string, unknown>)) {\n if (typeof val === 'string') {\n env[key] = val;\n }\n }\n if (Object.keys(env).length > 0) {\n server.env = env;\n }\n }\n\n return server;\n}\n\n/**\n * Parse and validate mcpServers array for an agent.\n */\nfunction parseMcpServers(servers: unknown[], agentIndex: number): McpServerConfig[] {\n const result: McpServerConfig[] = [];\n\n for (let i = 0; i < servers.length; i++) {\n const server = parseMcpServer(servers[i], agentIndex, i);\n if (server !== null) {\n result.push(server);\n }\n }\n\n return result;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [registry] ${message}`);\n}\n\n/**\n * Valid auth method types for validation.\n */\nconst VALID_AUTH_METHOD_TYPES: readonly string[] = ['oauth2', 'api-key'];\n\n/**\n * Validate and parse a single auth method configuration.\n *\n * Requirements: 11.2, 11.3\n *\n * @param value - Raw auth method object\n * @param agentIndex - Index of the agent in the registry (for error messages)\n * @param methodIndex - Index of the auth method in the array (for error messages)\n * @returns Parsed AgentAuthMethod or null if invalid\n */\nfunction parseAuthMethod(value: unknown, agentIndex: number, methodIndex: number): AgentAuthMethod | null {\n if (value === null || typeof value !== 'object') {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] is not an object, skipping`);\n return null;\n }\n\n const raw = value as Record<string, unknown>;\n\n // Validate required 'id' field\n if (!isNonEmptyString(raw.id)) {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing \"id\" field, skipping`);\n return null;\n }\n\n // Validate required 'type' field\n if (!isNonEmptyString(raw.type) || !VALID_AUTH_METHOD_TYPES.includes(raw.type)) {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing \"type\" field (must be 'oauth2' or 'api-key'), skipping`);\n return null;\n }\n\n const method: AgentAuthMethod = {\n id: raw.id,\n type: raw.type as 'oauth2' | 'api-key',\n };\n\n // Optional providerId field\n if (isNonEmptyString(raw.providerId)) {\n method.providerId = raw.providerId;\n }\n\n return method;\n}\n\n/**\n * Parse and validate authMethods array for an agent.\n *\n * Requirements: 11.2, 11.3\n *\n * @param methods - Raw auth methods array\n * @param agentIndex - Index of the agent in the registry (for error messages)\n * @returns Array of validated AgentAuthMethod entries\n */\nfunction parseAuthMethods(methods: unknown[], agentIndex: number): AgentAuthMethod[] {\n const result: AgentAuthMethod[] = [];\n\n for (let i = 0; i < methods.length; i++) {\n const method = parseAuthMethod(methods[i], agentIndex, i);\n if (method !== null) {\n result.push(method);\n }\n }\n\n return result;\n}\n\n/**\n * Validate and parse a registry agent entry.\n */\nfunction parseAgent(value: unknown, index: number): RegistryAgent {\n if (value === null || typeof value !== 'object') {\n throw new RegistryParseError(`Agent at index ${index} is not an object`);\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.id)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"id\" field`);\n }\n\n if (!isNonEmptyString(raw.name)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"name\" field`);\n }\n\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"version\" field`);\n }\n\n if (!isValidDistribution(raw.distribution)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"distribution\" field`);\n }\n\n const agent: RegistryAgent = {\n id: raw.id,\n name: raw.name,\n version: raw.version,\n distribution: raw.distribution as Distribution,\n };\n\n // Optional fields\n if (typeof raw.description === 'string') {\n agent.description = raw.description;\n }\n\n if (typeof raw.repository === 'string') {\n agent.repository = raw.repository;\n }\n\n if (Array.isArray(raw.authors)) {\n agent.authors = raw.authors.filter((a): a is string => typeof a === 'string');\n }\n\n if (typeof raw.license === 'string') {\n agent.license = raw.license;\n }\n\n if (typeof raw.icon === 'string') {\n agent.icon = raw.icon;\n }\n\n // Parse mcpServers if present\n if (Array.isArray(raw.mcpServers)) {\n const mcpServers = parseMcpServers(raw.mcpServers, index);\n if (mcpServers.length > 0) {\n agent.mcpServers = mcpServers;\n }\n }\n\n // Parse authRequired if present (Requirements: 11.2, 11.3)\n if (typeof raw.authRequired === 'boolean') {\n agent.authRequired = raw.authRequired;\n }\n\n // Parse authMethods if present (Requirements: 11.2, 11.3)\n if (Array.isArray(raw.authMethods)) {\n const authMethods = parseAuthMethods(raw.authMethods, index);\n if (authMethods.length > 0) {\n agent.authMethods = authMethods;\n }\n }\n\n return agent;\n}\n\n/**\n * Parse and validate registry JSON data.\n *\n * @param data - Raw JSON data to parse\n * @returns Parsed and validated Registry object\n * @throws RegistryParseError if JSON is malformed or invalid\n */\nexport function parseRegistry(data: unknown): Registry {\n if (data === null || typeof data !== 'object') {\n throw new RegistryParseError('Registry data is not an object');\n }\n\n const raw = data as Record<string, unknown>;\n\n // Validate version field\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError('Registry has invalid or missing \"version\" field');\n }\n\n // Validate agents array\n if (!Array.isArray(raw.agents)) {\n throw new RegistryParseError('Registry has invalid or missing \"agents\" field');\n }\n\n // Parse each agent\n const agents: RegistryAgent[] = [];\n for (let i = 0; i < raw.agents.length; i++) {\n agents.push(parseAgent(raw.agents[i], i));\n }\n\n return {\n version: raw.version,\n agents,\n };\n}\n\n/**\n * Registry Index implementation.\n *\n * Fetches, parses, and provides lookup functionality for the ACP Registry.\n */\nexport class RegistryIndex implements IRegistryIndex {\n /** Configured registry URL */\n private readonly registryUrl: string;\n\n /** Parsed registry data (null until fetch() is called) */\n private registry: Registry | null = null;\n\n /** Map of agent ID to agent entry for fast lookup */\n private agentMap: Map<string, RegistryAgent> = new Map();\n\n /**\n * Cache of auth requirements per agent.\n *\n * Requirements: 11.2, 11.3\n */\n private authRequirementsCache: Map<string, AgentAuthRequirements> = new Map();\n\n /**\n * Create a new RegistryIndex.\n *\n * @param registryUrl - URL to fetch the registry from (can be overridden by ACP_REGISTRY_URL env var)\n */\n constructor(registryUrl: string) {\n // Environment variable takes precedence\n const envUrl = process.env[ENV_REGISTRY_URL];\n this.registryUrl = isNonEmptyString(envUrl) ? envUrl : registryUrl;\n }\n\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n *\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n async fetch(): Promise<void> {\n logInfo(`Fetching registry from ${this.registryUrl}`);\n\n let response: Response;\n try {\n response = await fetch(this.registryUrl);\n } catch (error) {\n const message = `Failed to fetch registry from ${this.registryUrl}: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n if (!response.ok) {\n const message = `Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;\n logError(message);\n throw new RegistryFetchError(message);\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch (error) {\n const message = `Failed to read registry response body: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch (error) {\n const message = `Failed to parse registry JSON: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n try {\n this.registry = parseRegistry(data);\n } catch (error) {\n if (error instanceof RegistryParseError) {\n logError(error.message);\n throw error;\n }\n const message = `Failed to validate registry data: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n // Build the agent lookup map\n this.agentMap.clear();\n this.authRequirementsCache.clear();\n for (const agent of this.registry.agents) {\n this.agentMap.set(agent.id, agent);\n }\n\n logInfo(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`);\n }\n\n /**\n * Look up an agent by ID.\n *\n * @param agentId - Agent ID to look up\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined {\n return this.agentMap.get(agentId);\n }\n\n /**\n * Resolve an agent ID to a spawn command.\n *\n * @param agentId - Agent ID to resolve\n * @returns Resolved spawn command\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand {\n const agent = this.lookup(agentId);\n if (!agent) {\n throw new AgentNotFoundError(agentId);\n }\n\n return resolveDistribution(agent.distribution, agentId);\n }\n\n /**\n * Get the parsed registry data.\n * @returns The parsed registry or null if not yet fetched\n */\n getRegistry(): Registry | null {\n return this.registry;\n }\n\n /**\n * Get auth requirements for an agent.\n *\n * Checks the agent definition in the registry for `authRequired` or `authMethods` fields.\n * Results are cached per agent for efficient repeated lookups.\n *\n * Requirements: 11.2, 11.3\n *\n * @param agentId - Agent ID to query\n * @returns Auth requirements or undefined if agent not found\n */\n getAuthRequirements(agentId: string): AgentAuthRequirements | undefined {\n // Check cache first\n const cached = this.authRequirementsCache.get(agentId);\n if (cached !== undefined) {\n return cached;\n }\n\n // Look up agent\n const agent = this.lookup(agentId);\n if (!agent) {\n return undefined;\n }\n\n // Build auth requirements from agent definition\n const authMethods = agent.authMethods ?? [];\n\n // Determine if auth is required:\n // - Explicitly set via authRequired field\n // - Implicitly required if authMethods contains oauth2 methods\n const hasOAuthMethods = authMethods.some(m => m.type === 'oauth2');\n const authRequired = agent.authRequired ?? hasOAuthMethods;\n\n // Find primary OAuth provider ID (first oauth2 method with providerId)\n let primaryOAuthProviderId: string | undefined;\n for (const method of authMethods) {\n if (method.type === 'oauth2' && method.providerId) {\n primaryOAuthProviderId = method.providerId;\n break;\n }\n }\n\n const requirements: AgentAuthRequirements = {\n authRequired,\n authMethods,\n primaryOAuthProviderId,\n };\n\n // Cache the result\n this.authRequirementsCache.set(agentId, requirements);\n\n if (authRequired) {\n logInfo(`Agent \"${agentId}\" requires authentication${primaryOAuthProviderId ? ` (OAuth provider: ${primaryOAuthProviderId})` : ''}`);\n }\n\n return requirements;\n }\n\n /**\n * Clear the auth requirements cache for a specific agent or all agents.\n *\n * @param agentId - Optional agent ID to clear. If not provided, clears all cached requirements.\n */\n clearAuthRequirementsCache(agentId?: string): void {\n if (agentId) {\n this.authRequirementsCache.delete(agentId);\n logInfo(`Cleared auth requirements cache for agent \"${agentId}\"`);\n } else {\n this.authRequirementsCache.clear();\n logInfo('Cleared all auth requirements cache');\n }\n }\n\n /**\n * Merge custom agents into the registry.\n *\n * Custom agents take precedence over remote registry agents with the same ID.\n * This allows users to override or extend the official ACP Registry with\n * locally-defined agents (e.g., AWS Bedrock, custom internal agents).\n *\n * @param agents - Array of custom RegistryAgent entries to merge\n */\n mergeCustomAgents(agents: RegistryAgent[]): void {\n if (agents.length === 0) {\n return;\n }\n\n // Initialize registry if fetch() hasn't been called yet\n if (!this.registry) {\n this.registry = { version: 'custom', agents: [] };\n }\n\n for (const agent of agents) {\n // Custom agents override remote agents with the same ID\n const existingIndex = this.registry.agents.findIndex((a) => a.id === agent.id);\n if (existingIndex !== -1) {\n this.registry.agents[existingIndex] = agent;\n logInfo(`Custom agent \"${agent.id}\" overrides remote registry entry`);\n } else {\n this.registry.agents.push(agent);\n logInfo(`Custom agent \"${agent.id}\" added to registry`);\n }\n\n this.agentMap.set(agent.id, agent);\n\n // Clear cached auth requirements for this agent (may have changed)\n this.authRequirementsCache.delete(agent.id);\n }\n\n logInfo(`Registry now contains ${this.registry.agents.length} agents (${agents.length} custom)`);\n }\n}\n\n/**\n * Error thrown when custom agents file cannot be loaded.\n */\nexport class CustomAgentsLoadError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'CustomAgentsLoadError';\n }\n}\n\n/**\n * Load and validate custom agents from a JSON file.\n *\n * The file must contain a JSON object with an \"agents\" array field.\n * Each agent entry is validated using the same rules as the remote registry.\n *\n * Expected file format:\n * ```json\n * {\n * \"agents\": [\n * {\n * \"id\": \"my-custom-agent\",\n * \"name\": \"My Custom Agent\",\n * \"version\": \"1.0.0\",\n * \"distribution\": {\n * \"npx\": { \"package\": \"@my-org/my-agent@latest\" }\n * }\n * }\n * ]\n * }\n * ```\n *\n * @param filePath - Path to the custom agents JSON file\n * @returns Array of validated RegistryAgent entries\n * @throws CustomAgentsLoadError if file cannot be read or parsed\n * @throws RegistryParseError if agent entries are malformed\n */\nexport function loadCustomAgents(filePath: string): RegistryAgent[] {\n let fileContent: string;\n try {\n fileContent = readFileSync(filePath, 'utf-8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CustomAgentsLoadError(`Custom agents file not found: ${filePath}`);\n }\n if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n throw new CustomAgentsLoadError(`Custom agents file not readable: ${filePath}`);\n }\n throw new CustomAgentsLoadError(\n `Failed to read custom agents file \"${filePath}\": ${(error as Error).message}`,\n error as Error,\n );\n }\n\n let data: unknown;\n try {\n data = JSON.parse(fileContent);\n } catch (error) {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" contains malformed JSON: ${(error as Error).message}`,\n error as Error,\n );\n }\n\n if (data === null || typeof data !== 'object') {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" does not contain a valid object`,\n );\n }\n\n const raw = data as Record<string, unknown>;\n\n if (!Array.isArray(raw.agents)) {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" does not contain a valid \"agents\" array`,\n );\n }\n\n // Reuse parseRegistry for validation \u2014 wrap in registry structure\n const registryData = {\n version: 'custom',\n agents: raw.agents,\n };\n\n const parsed = parseRegistry(registryData);\n return parsed.agents;\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * NDJSON (Newline-Delimited JSON) stream handler for the Registry Launcher.\n *\n * Handles buffering and parsing of NDJSON messages from stdin and writing\n * NDJSON messages to stdout. Supports partial reads across multiple chunks.\n *\n * @module stream/ndjson-handler\n */\n\nimport { Writable } from 'node:stream';\n\n/**\n * Callback type for parsed messages.\n */\nexport type MessageCallback = (message: object) => void;\n\n/**\n * Callback type for parse errors.\n */\nexport type ErrorCallback = (error: Error, line: string) => void;\n\n/**\n * Interface for NDJSON stream handling.\n */\nexport interface INDJSONHandler {\n /**\n * Register a callback for parsed messages.\n */\n onMessage(callback: MessageCallback): void;\n\n /**\n * Register a callback for parse errors.\n */\n onError(callback: ErrorCallback): void;\n\n /**\n * Write a message to the output stream.\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean;\n\n /**\n * Process incoming data chunk (call from stdin 'data' event).\n */\n processChunk(chunk: Buffer): void;\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`);\n}\n\n/**\n * NDJSON stream handler implementation.\n *\n * This class handles:\n * - Buffering incoming data until complete newline-delimited messages are received\n * - Handling partial JSON messages that span multiple read operations\n * - Appending newline characters after each JSON message when writing\n * - Splitting on newline boundaries when reading\n * - Logging errors and skipping malformed lines\n */\nexport class NDJSONHandler implements INDJSONHandler {\n /** Buffer for accumulating partial data */\n private buffer: string = '';\n\n /** Output stream for writing messages */\n private readonly output: Writable;\n\n /** Registered message callback */\n private messageCallback: MessageCallback | null = null;\n\n /** Registered error callback */\n private errorCallback: ErrorCallback | null = null;\n\n /**\n * Create a new NDJSONHandler.\n * @param output - Writable stream for output (typically process.stdout)\n */\n constructor(output: Writable) {\n this.output = output;\n }\n\n /**\n * Register a callback for parsed messages.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with each parsed message\n */\n onMessage(callback: MessageCallback): void {\n this.messageCallback = callback;\n }\n\n /**\n * Register a callback for parse errors.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with parse errors\n */\n onError(callback: ErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /**\n * Write a message to the output stream.\n *\n * Serializes the message as JSON and appends a newline character.\n *\n * @param message - Object to serialize and write\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean {\n if (!this.output.writable) {\n return false;\n }\n\n try {\n const json = JSON.stringify(message);\n this.output.write(json + '\\n');\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Process incoming data chunk.\n *\n * Buffers data and emits complete messages when newline boundaries are found.\n * Handles partial messages that span multiple chunks.\n *\n * @param chunk - Buffer containing incoming data\n */\n processChunk(chunk: Buffer): void {\n // Append chunk to buffer\n this.buffer += chunk.toString('utf-8');\n\n // Process complete lines\n this.processBuffer();\n }\n\n /**\n * Process the internal buffer, extracting and parsing complete lines.\n *\n * Splits on newline boundaries and parses each complete line as JSON.\n * Incomplete lines remain in the buffer for the next chunk.\n */\n private processBuffer(): void {\n let newlineIndex: number;\n\n // Process all complete lines in the buffer\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n // Extract the line (without the newline)\n const line = this.buffer.slice(0, newlineIndex);\n\n // Remove the processed line and newline from buffer\n this.buffer = this.buffer.slice(newlineIndex + 1);\n\n // Skip empty lines\n if (line.trim().length === 0) {\n continue;\n }\n\n // Parse and emit the message\n this.parseLine(line);\n }\n }\n\n /**\n * Parse a single line as JSON and emit the message.\n *\n * If parsing fails, logs the error and invokes the error callback.\n * Malformed lines are skipped.\n *\n * @param line - Line to parse as JSON\n */\n private parseLine(line: string): void {\n try {\n const message = JSON.parse(line);\n\n // Ensure the parsed value is an object (not a primitive)\n if (message === null || typeof message !== 'object') {\n const error = new Error('Parsed JSON is not an object');\n logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);\n this.errorCallback?.(error, line);\n return;\n }\n\n // Emit the parsed message\n this.messageCallback?.(message);\n } catch (error) {\n // Log the error and skip the malformed line\n logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);\n this.errorCallback?.(error as Error, line);\n }\n }\n\n /**\n * Truncate a line for logging purposes.\n * @param line - Line to truncate\n * @param maxLength - Maximum length (default: 100)\n * @returns Truncated line with ellipsis if needed\n */\n private truncateLine(line: string, maxLength: number = 100): string {\n if (line.length <= maxLength) {\n return line;\n }\n return line.slice(0, maxLength) + '...';\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport type { AgentRuntime, RuntimeState } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_TERMINATE_TIMEOUT_MS = 5000;\n\n/**\n * Implementation of AgentRuntime that manages a spawned agent process.\n *\n * Handles process spawning with non-blocking file descriptors,\n * message writing to stdin, and graceful termination with SIGTERM/SIGKILL.\n */\nexport class AgentRuntimeImpl implements AgentRuntime {\n public readonly agentId: string;\n public state: RuntimeState;\n public readonly process: ChildProcess;\n\n private readonly onExitCallback?: (code: number | null, signal: string | null) => void;\n\n /**\n * Create a new AgentRuntime by spawning a process.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command\n * @param onExit - Optional callback when process exits\n */\n private constructor(\n agentId: string,\n process: ChildProcess,\n onExit?: (code: number | null, signal: string | null) => void,\n ) {\n this.agentId = agentId;\n this.process = process;\n this.state = 'starting';\n this.onExitCallback = onExit;\n\n this.setupProcessHandlers();\n }\n\n /**\n * Spawn a new agent process and create an AgentRuntime instance.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command with command, args, and optional env\n * @param onExit - Optional callback when process exits\n * @returns A new AgentRuntime instance\n */\n public static spawn(\n agentId: string,\n spawnCommand: SpawnCommand,\n onExit?: (code: number | null, signal: string | null) => void,\n ): AgentRuntimeImpl {\n const childProcess = spawn(spawnCommand.command, spawnCommand.args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n ...spawnCommand.env,\n },\n // Detach from parent's controlling terminal to avoid signal propagation issues\n detached: false,\n });\n\n // Set file descriptors to non-blocking mode\n // Node.js streams are already non-blocking by default when using 'pipe',\n // but we ensure the streams are in flowing mode for proper async handling\n if (childProcess.stdout) {\n childProcess.stdout.setEncoding('utf8');\n }\n if (childProcess.stderr) {\n childProcess.stderr.setEncoding('utf8');\n }\n\n return new AgentRuntimeImpl(agentId, childProcess, onExit);\n }\n\n /**\n * Set up event handlers for the child process.\n */\n private setupProcessHandlers(): void {\n // Handle process spawn success\n this.process.on('spawn', () => {\n if (this.state === 'starting') {\n this.state = 'running';\n }\n });\n\n // Handle process errors (e.g., spawn failure)\n this.process.on('error', (error: Error) => {\n this.state = 'stopped';\n // Log to stderr as per requirements\n process.stderr.write(\n `[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}\\n`,\n );\n });\n\n // Handle process exit\n this.process.on('exit', (code: number | null, signal: string | null) => {\n this.state = 'stopped';\n if (this.onExitCallback) {\n this.onExitCallback(code, signal);\n }\n });\n\n // Handle stdin errors (e.g., broken pipe)\n if (this.process.stdin) {\n this.process.stdin.on('error', (error: Error) => {\n // Log stdin write errors but don't change state\n process.stderr.write(\n `[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}\\n`,\n );\n });\n }\n }\n\n /**\n * Write a message to the agent's stdin as NDJSON.\n *\n * @param message - The message object to send\n * @returns true if the write was accepted, false if backpressure or error\n */\n public write(message: object): boolean {\n if (this.state !== 'running' && this.state !== 'starting') {\n return false;\n }\n\n if (!this.process.stdin || this.process.stdin.destroyed) {\n return false;\n }\n\n try {\n const ndjsonLine = JSON.stringify(message) + '\\n';\n return this.process.stdin.write(ndjsonLine);\n } catch {\n return false;\n }\n }\n\n /**\n * Terminate the agent process gracefully.\n *\n * Sends SIGTERM first, then SIGKILL after timeout if process doesn't exit.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n * @returns Promise that resolves when process has exited\n */\n public async terminate(timeout: number = DEFAULT_TERMINATE_TIMEOUT_MS): Promise<void> {\n // Already stopped or stopping\n if (this.state === 'stopped') {\n return;\n }\n\n if (this.state === 'stopping') {\n // Wait for existing termination to complete\n return this.waitForExit();\n }\n\n this.state = 'stopping';\n\n // Close stdin to signal no more input\n if (this.process.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.end();\n }\n\n // Send SIGTERM for graceful shutdown\n this.process.kill('SIGTERM');\n\n // Wait for exit with timeout\n const exitPromise = this.waitForExit();\n const timeoutPromise = new Promise<'timeout'>((resolve) => {\n setTimeout(() => resolve('timeout'), timeout);\n });\n\n const result = await Promise.race([exitPromise, timeoutPromise]);\n\n if (result === 'timeout' && !this.process.killed && this.process.exitCode === null) {\n // Force kill if still running after timeout\n this.process.kill('SIGKILL');\n await this.waitForExit();\n }\n }\n\n /**\n * Wait for the process to exit.\n *\n * @returns Promise that resolves when process exits\n */\n private waitForExit(): Promise<void> {\n if (this.state === 'stopped') {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.process.once('exit', () => {\n resolve();\n });\n });\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AgentRuntime } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\nimport { AgentRuntimeImpl } from './agent-runtime.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;\n\n/**\n * Callback type for agent exit events.\n */\nexport type AgentExitCallback = (agentId: string, code: number | null) => void;\n\n/**\n * Manager for agent runtime lifecycle.\n *\n * Handles spawning, tracking, and terminating agent processes.\n * Stores runtimes in a Map keyed by agentId.\n */\nexport class AgentRuntimeManager {\n /** Map of agentId to AgentRuntime instances */\n private readonly runtimes: Map<string, AgentRuntime> = new Map();\n\n /** Registered callbacks for agent exit events */\n private readonly exitCallbacks: AgentExitCallback[] = [];\n\n /**\n * Get an existing runtime or spawn a new one for the given agentId.\n *\n * If a runtime already exists for the agentId, returns it.\n * Otherwise, spawns a new agent process using the provided spawn command.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command for spawning a new process\n * @returns The existing or newly spawned AgentRuntime\n */\n public async getOrSpawn(agentId: string, spawnCommand: SpawnCommand): Promise<AgentRuntime> {\n // Return existing runtime if available\n const existing = this.runtimes.get(agentId);\n if (existing && existing.state !== 'stopped') {\n return existing;\n }\n\n // Spawn new runtime\n const runtime = AgentRuntimeImpl.spawn(\n agentId,\n spawnCommand,\n (code: number | null, _signal: string | null) => {\n this.handleAgentExit(agentId, code);\n },\n );\n\n // Store in map\n this.runtimes.set(agentId, runtime);\n\n return runtime;\n }\n\n /**\n * Get an existing runtime without spawning.\n *\n * @param agentId - The agent identifier\n * @returns The AgentRuntime if it exists, undefined otherwise\n */\n public get(agentId: string): AgentRuntime | undefined {\n return this.runtimes.get(agentId);\n }\n\n /**\n * Terminate a specific agent runtime.\n *\n * Sends SIGTERM to the agent process and waits for it to exit.\n * If the process doesn't exit within the timeout, sends SIGKILL.\n *\n * @param agentId - The agent identifier\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminate(agentId: string, timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const runtime = this.runtimes.get(agentId);\n if (!runtime) {\n return;\n }\n\n await runtime.terminate(timeout);\n this.runtimes.delete(agentId);\n }\n\n /**\n * Terminate all agent runtimes for graceful shutdown.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminateAll(timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const terminatePromises: Promise<void>[] = [];\n\n for (const [agentId, runtime] of this.runtimes) {\n if (runtime.state !== 'stopped') {\n terminatePromises.push(\n runtime.terminate(timeout).then(() => {\n this.runtimes.delete(agentId);\n }),\n );\n }\n }\n\n await Promise.all(terminatePromises);\n }\n\n /**\n * Register a callback for agent exit events.\n *\n * The callback is invoked when an agent process exits unexpectedly.\n *\n * @param callback - Function to call when an agent exits\n */\n public onAgentExit(callback: AgentExitCallback): void {\n this.exitCallbacks.push(callback);\n }\n\n /**\n * Handle agent process exit.\n *\n * Removes the runtime from the map and notifies registered callbacks.\n *\n * @param agentId - The agent identifier\n * @param code - The exit code (null if terminated by signal)\n */\n private handleAgentExit(agentId: string, code: number | null): void {\n // Remove from map\n this.runtimes.delete(agentId);\n\n // Notify all registered callbacks\n for (const callback of this.exitCallbacks) {\n try {\n callback(agentId, code);\n } catch {\n // Ignore callback errors to prevent one failing callback from affecting others\n }\n }\n }\n\n /**\n * Get the number of active runtimes.\n *\n * @returns The count of runtimes currently in the map\n */\n public get size(): number {\n return this.runtimes.size;\n }\n\n /**\n * Check if a runtime exists for the given agentId.\n *\n * @param agentId - The agent identifier\n * @returns true if a runtime exists, false otherwise\n */\n public has(agentId: string): boolean {\n return this.runtimes.has(agentId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Message Router for the Registry Launcher.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles agentId extraction, message transformation, and error response generation.\n * Integrates with AuthManager for OAuth authentication (Requirements 11.2, 11.4).\n *\n * @module router/message-router\n */\n\nimport { spawn } from 'node:child_process';\nimport type { IRegistryIndex } from '../registry/index.js';\nimport { AgentNotFoundError, PlatformNotSupportedError } from '../registry/index.js';\nimport type { AgentRuntimeManager } from '../runtime/manager.js';\nimport type { AgentRuntime } from '../runtime/types.js';\nimport { getAgentApiKey, getAgentEnv } from '../config/api-keys.js';\nimport type { AuthManager } from '../auth/auth-manager.js';\nimport type { AcpAuthMethod, AuthProviderId } from '../auth/types.js';\nimport { isValidProviderId } from '../auth/types.js';\n\n/**\n * JSON-RPC error codes for routing errors.\n */\nexport const RoutingErrorCodes = {\n /** Missing agentId in request */\n MISSING_AGENT_ID: -32600,\n /** Agent not found in registry */\n AGENT_NOT_FOUND: -32001,\n /** Platform not supported for binary distribution */\n PLATFORM_NOT_SUPPORTED: -32002,\n /** Agent spawn failed */\n SPAWN_FAILED: -32003,\n /** Authentication required (Requirement 11.2) */\n AUTH_REQUIRED: -32004,\n} as const;\n\n// =============================================================================\n// Auth Method Parsing Types and Constants (Task 21.1)\n// =============================================================================\n\n/**\n * Valid auth method types from agent responses.\n * - 'oauth2': Standard OAuth 2.1 flow (client handles OAuth)\n * - 'agent': Agent handles OAuth internally (ACP-compliant, default)\n * - 'terminal': Interactive terminal auth (TUI)\n * - 'api-key': API key authentication\n */\nexport type AuthMethodType = 'oauth2' | 'agent' | 'terminal' | 'api-key';\n\n/**\n * Parsed auth method with validated fields.\n * Discriminated union for type-safe handling.\n */\nexport type ParsedAuthMethod =\n | { kind: 'oauth2'; id: string; providerId: AuthProviderId }\n | { kind: 'agent'; id: string; providerId?: AuthProviderId }\n | { kind: 'terminal'; id: string; args?: string[]; env?: Record<string, string> }\n | { kind: 'api-key'; id: string; providerId?: AuthProviderId };\n\n/**\n * Explicit mapping from auth method IDs to provider IDs.\n * Security: Uses explicit allowlist mapping, no substring heuristics.\n *\n * Requirement 3.1: Support OAuth authentication with type \"agent\" or \"oauth2\"\n * Requirement 11.2: Map authMethod.id to AuthProviderId\n */\nexport const AUTH_METHOD_ID_TO_PROVIDER: Readonly<Record<string, AuthProviderId>> = {\n // OAuth2 method IDs\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n 'oauth2-github': 'github',\n 'oauth2-google': 'google',\n 'oauth2-cognito': 'cognito',\n 'oauth2-azure': 'azure',\n // Agent auth method IDs (legacy format)\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n 'agent-github': 'github',\n 'agent-google': 'google',\n 'agent-cognito': 'cognito',\n 'agent-azure': 'azure',\n // API key method IDs - these map to providers that support API key auth\n // Note: OpenAI and Anthropic API key mappings will be handled by model-credentials module\n 'github-api-key': 'github',\n 'google-api-key': 'google',\n 'azure-api-key': 'azure',\n 'cognito-api-key': 'cognito',\n} as const;\n\n/**\n * Maximum number of auth methods to process (DoS protection).\n */\nconst MAX_AUTH_METHODS = 50;\n\n/**\n * Maximum length for auth method ID strings.\n */\nconst MAX_METHOD_ID_LENGTH = 128;\n\n/**\n * Valid auth method types allowlist.\n */\nconst VALID_AUTH_METHOD_TYPES: readonly string[] = ['oauth2', 'agent', 'terminal', 'api-key'];\n\n/**\n * Parse and validate auth methods from agent initialize response.\n *\n * Extracts type and providerId from each auth method, using explicit mapping\n * for id-to-provider resolution. Validates all fields and rejects invalid methods.\n *\n * Security considerations:\n * - Uses explicit allowlist for method types\n * - Uses explicit mapping for id-to-provider (no substring heuristics)\n * - Validates providerId against known providers\n * - Limits number of methods processed (DoS protection)\n * - Deduplicates by method ID\n *\n * Requirement 3.1: Identify methods with type \"oauth2\" or \"agent\"\n * Requirement 11.2: Map authMethod.id to AuthProviderId using explicit mapping\n *\n * @param raw - Raw auth methods array from agent response (untrusted input)\n * @returns Array of validated and parsed auth methods\n */\nexport function parseAuthMethods(raw: unknown): ParsedAuthMethod[] {\n // Validate input is an array\n if (!Array.isArray(raw)) {\n logError('authMethods is not an array, skipping parsing');\n return [];\n }\n\n // Limit number of methods (DoS protection)\n const methods = raw.slice(0, MAX_AUTH_METHODS);\n const parsed: ParsedAuthMethod[] = [];\n const seenIds = new Set<string>();\n\n for (const method of methods) {\n const result = parseAuthMethod(method, seenIds);\n if (result) {\n parsed.push(result);\n seenIds.add(result.id);\n }\n }\n\n logInfo(`Parsed ${parsed.length} valid auth methods from ${methods.length} raw methods`);\n return parsed;\n}\n\n/**\n * Parse a single auth method entry.\n *\n * @param method - Raw method object from agent response\n * @param seenIds - Set of already processed method IDs (for deduplication)\n * @returns Parsed auth method or null if invalid\n */\nfunction parseAuthMethod(method: unknown, seenIds: Set<string>): ParsedAuthMethod | null {\n // Validate method is an object\n if (method === null || typeof method !== 'object') {\n return null;\n }\n\n const obj = method as Record<string, unknown>;\n\n // Extract and validate id\n const id = obj.id;\n if (typeof id !== 'string' || id.length === 0 || id.length > MAX_METHOD_ID_LENGTH) {\n logError(`Invalid auth method id: ${typeof id === 'string' ? id.substring(0, 50) : typeof id}`);\n return null;\n }\n\n // Deduplicate by id\n if (seenIds.has(id)) {\n logInfo(`Skipping duplicate auth method id: ${id}`);\n return null;\n }\n\n // Extract and validate type\n const type = obj.type;\n if (typeof type !== 'string' || !VALID_AUTH_METHOD_TYPES.includes(type)) {\n logError(`Invalid auth method type for id ${id}: ${type}`);\n return null;\n }\n\n // Extract providerId from method object (if present)\n const rawProviderId = obj.providerId;\n let providerId: AuthProviderId | undefined;\n\n // Validate providerId if present in the method object\n if (rawProviderId !== undefined) {\n if (isValidProviderId(rawProviderId)) {\n providerId = rawProviderId;\n } else {\n logError(`Invalid providerId in auth method ${id}: ${rawProviderId}`);\n // Don't reject the method, try to resolve from id mapping\n }\n }\n\n // Resolve providerId from explicit id mapping\n const mappedProviderId = AUTH_METHOD_ID_TO_PROVIDER[id];\n\n // Check for conflicts between mapped and explicit providerId\n if (providerId && mappedProviderId && providerId !== mappedProviderId) {\n logError(`Conflict: auth method ${id} has providerId ${providerId} but maps to ${mappedProviderId}, rejecting`);\n return null;\n }\n\n // Use mapped providerId if explicit one not available\n const resolvedProviderId = providerId ?? mappedProviderId;\n\n // Build parsed method based on type\n if (type === 'oauth2') {\n // OAuth methods require a valid providerId\n if (!resolvedProviderId) {\n logError(`OAuth auth method ${id} has no valid providerId, skipping`);\n return null;\n }\n return {\n kind: 'oauth2',\n id,\n providerId: resolvedProviderId,\n };\n }\n\n if (type === 'agent') {\n // Agent auth: agent handles OAuth internally (ACP-compliant)\n // AUTH_REQUIREMENTS.md: When type is not specified, \"agent\" is assumed as default\n return {\n kind: 'agent',\n id,\n providerId: resolvedProviderId, // Optional for agent auth\n };\n }\n\n if (type === 'terminal') {\n // Terminal auth: interactive TUI setup\n // Extract args and env from the method object\n const args = Array.isArray(obj.args) ? obj.args.filter((a): a is string => typeof a === 'string') : undefined;\n const env = obj.env && typeof obj.env === 'object' && !Array.isArray(obj.env)\n ? Object.fromEntries(\n Object.entries(obj.env as Record<string, unknown>)\n .filter(([, v]) => typeof v === 'string')\n ) as Record<string, string>\n : undefined;\n\n return {\n kind: 'terminal',\n id,\n args,\n env,\n };\n }\n\n if (type === 'api-key') {\n return {\n kind: 'api-key',\n id,\n providerId: resolvedProviderId, // Optional for api-key\n };\n }\n\n // Should not reach here due to type validation above\n return null;\n}\n\n/**\n * Filter parsed auth methods to get only OAuth methods.\n *\n * Requirement 3.1: Identify methods with type \"oauth2\" or \"agent\"\n *\n * @param methods - Parsed auth methods\n * @returns Only OAuth methods (kind: 'oauth2')\n */\nexport function getOAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'oauth2' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'oauth2' } => m.kind === 'oauth2');\n}\n\n/**\n * Filter parsed auth methods to get only Agent Auth methods.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth is the default authentication method\n * where the agent manages the entire OAuth flow independently.\n *\n * @param methods - Parsed auth methods\n * @returns Only Agent Auth methods (kind: 'agent')\n */\nexport function getAgentAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'agent' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'agent' } => m.kind === 'agent');\n}\n\n/**\n * Filter parsed auth methods to get only Terminal Auth methods.\n *\n * AUTH_REQUIREMENTS.md: Terminal Auth enables agents to run an interactive\n * setup experience within a terminal environment.\n *\n * @param methods - Parsed auth methods\n * @returns Only Terminal Auth methods (kind: 'terminal')\n */\nexport function getTerminalAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'terminal' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'terminal' } => m.kind === 'terminal');\n}\n\n/**\n * Filter parsed auth methods to get only API key methods.\n *\n * @param methods - Parsed auth methods\n * @returns Only API key methods (kind: 'api-key')\n */\nexport function getApiKeyMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'api-key' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'api-key' } => m.kind === 'api-key');\n}\n\n/**\n * JSON-RPC error response structure.\n */\nexport interface ErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\n/**\n * Pending request tracking structure.\n */\ninterface PendingRequest {\n id: string | number;\n agentId: string;\n timestamp: number;\n /** The JSON-RPC method name (for response correlation) */\n method?: string;\n /** Client session ID for session mapping */\n clientSessionId?: string;\n}\n\n// =============================================================================\n// Auth State Machine Types (Task 21.3)\n// =============================================================================\n\n/**\n * Authentication state for an agent.\n *\n * State transitions:\n * - none \u2192 pending: OAuth flow initiated\n * - pending \u2192 authenticated: OAuth flow succeeded\n * - pending \u2192 failed: OAuth flow failed or timed out\n * - failed \u2192 pending: Retry OAuth flow\n * - authenticated \u2192 none: Logout or token invalidation\n *\n * Requirement 3.1: Track auth state during OAuth 2.1 Authorization Code flow\n * Requirement 3.5: Handle timeout transitions to failed state\n */\nexport type AuthState = 'none' | 'pending' | 'authenticated' | 'failed';\n\n/**\n * Queued request structure for requests waiting on OAuth authentication.\n *\n * When an OAuth flow is pending for an agent, incoming requests are queued\n * and resumed after successful authentication.\n *\n * Requirement 3.1: Queue requests while OAuth flow is in progress\n */\nexport interface QueuedRequest {\n /** The original message to be routed */\n message: object;\n /** Timestamp when the request was queued */\n queuedAt: number;\n /** Resolve function to signal completion */\n resolve: (result: ErrorResponse | undefined) => void;\n}\n\n/**\n * Pending authenticate request tracking structure.\n *\n * Tracks authenticate JSON-RPC requests sent to agents for Agent Auth flow.\n * Used to correlate authenticate responses with the original auth flow.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n */\nexport interface PendingAuthenticateRequest {\n /** The authenticate request ID */\n requestId: string;\n /** The agent ID */\n agentId: string;\n /** The auth method ID from authMethods */\n authMethodId: string;\n /** Timestamp when the request was sent */\n sentAt: number;\n /** Resolve function to signal completion */\n resolve: (success: boolean, error?: string) => void;\n}\n\n/**\n * Default timeout for Agent Auth authenticate requests in milliseconds (5 minutes).\n * Matches the OAuth flow timeout from AUTH_REQUIREMENTS.md.\n */\nconst AGENT_AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Default timeout for Terminal Auth setup process in milliseconds (10 minutes).\n * Terminal Auth may require user interaction, so we allow more time.\n */\nconst TERMINAL_AUTH_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * Default timeout for queued requests in milliseconds (5 minutes).\n * Matches the OAuth flow timeout from Requirement 3.5.\n */\nconst QUEUED_REQUEST_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Callback type for writing responses to stdout.\n */\nexport type WriteCallback = (message: object) => boolean;\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [router] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [router] ${message}`);\n}\n\n\n/**\n * Create a JSON-RPC error response.\n *\n * @param id - Request ID (null for notifications or unknown)\n * @param code - Error code\n * @param message - Error message\n * @param data - Optional additional error data\n * @returns Error response object\n */\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): ErrorResponse {\n const response: ErrorResponse = {\n jsonrpc: '2.0',\n id,\n error: { code, message },\n };\n\n if (data !== undefined) {\n response.error.data = data;\n }\n\n return response;\n}\n\n/**\n * Extract the agentId field from a message.\n *\n * @param message - The message object to extract from\n * @returns The agentId string or undefined if not present\n */\nexport function extractAgentId(message: object): string | undefined {\n const msg = message as Record<string, unknown>;\n const agentId = msg.agentId;\n\n if (typeof agentId === 'string' && agentId.length > 0) {\n return agentId;\n }\n\n return undefined;\n}\n\n/**\n * Extract the JSON-RPC id field from a message.\n *\n * @param message - The message object to extract from\n * @returns The id (string, number, or null)\n */\nexport function extractId(message: object): string | number | null {\n const msg = message as Record<string, unknown>;\n const id = msg.id;\n\n if (typeof id === 'string' || typeof id === 'number') {\n return id;\n }\n\n return null;\n}\n\n/**\n * Transform a message for forwarding to an agent.\n *\n * Removes the agentId field while preserving all other fields.\n *\n * @param message - The original message\n * @returns A new message object without the agentId field\n */\nexport function transformMessage(message: object): object {\n const { agentId: _, ...rest } = message as Record<string, unknown>;\n return rest;\n}\n\n\n/**\n * Spawn function type for dependency injection in tests.\n */\nexport type SpawnFn = typeof spawn;\n\n/**\n * Optional dependencies for MessageRouter (for testing).\n */\nexport interface MessageRouterDeps {\n /** Custom spawn function (default: child_process.spawn) */\n spawnFn?: SpawnFn;\n /** Custom function to check if stdin is TTY (default: process.stdin.isTTY) */\n isStdinTTY?: () => boolean;\n /** Custom function to check if stdout is TTY (default: process.stdout.isTTY) */\n isStdoutTTY?: () => boolean;\n}\n\n/**\n * Message Router implementation.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles message transformation, error generation, and request correlation.\n * Integrates with AuthManager for OAuth authentication (Requirements 11.2, 11.4).\n * Implements auth state machine for pending OAuth flows (Task 21.3).\n */\nexport class MessageRouter {\n /** Registry index for agent lookup and resolution */\n private readonly registry: IRegistryIndex;\n\n /** Runtime manager for agent process lifecycle */\n private readonly runtimeManager: AgentRuntimeManager;\n\n /** Callback for writing responses to stdout */\n private readonly writeCallback: WriteCallback;\n\n /** API keys for agent authentication */\n private readonly apiKeys: Record<string, any>;\n\n /** Spawn function for Terminal Auth (injectable for testing) */\n private readonly spawnFn: SpawnFn;\n\n /** Function to check if stdin is TTY (injectable for testing) */\n private readonly isStdinTTY: () => boolean;\n\n /** Function to check if stdout is TTY (injectable for testing) */\n private readonly isStdoutTTY: () => boolean;\n /** Optional AuthManager for OAuth authentication (Requirements 11.2, 11.4) */\n private readonly authManager?: AuthManager;\n\n /** Map of request ID to pending request info for correlation */\n private readonly pendingRequests: Map<string | number, PendingRequest> = new Map();\n\n /**\n * Map of agent ID to authentication state.\n *\n * State machine (Task 21.3):\n * - none: No authentication in progress\n * - pending: OAuth flow in progress, requests are queued\n * - authenticated: OAuth flow completed successfully\n * - failed: OAuth flow failed or timed out\n *\n * Requirement 3.1: Track auth state during OAuth 2.1 Authorization Code flow\n */\n private readonly authState: Map<string, AuthState> = new Map();\n\n /**\n * Map of agent ID to required OAuth provider ID.\n *\n * Tracks which agents require OAuth authentication and with which provider.\n * This is populated when we receive an initialize response with authMethods\n * containing OAuth methods.\n *\n * Requirement 11.2: Track auth requirements to block requests when OAuth\n * is required but credentials are not available.\n */\n private readonly agentOAuthRequirements: Map<string, AuthProviderId> = new Map();\n\n /**\n * Map of agent ID to queued requests waiting for OAuth authentication.\n *\n * When an OAuth flow is pending for an agent, incoming requests are queued\n * here and processed after successful authentication.\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n */\n private readonly requestQueue: Map<string, QueuedRequest[]> = new Map();\n\n /**\n * Map of authenticate request ID to pending authenticate request info.\n *\n * Tracks authenticate JSON-RPC requests sent to agents for Agent Auth flow.\n * Used to correlate authenticate responses with the original auth flow.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n */\n private readonly pendingAuthenticateRequests: Map<string, PendingAuthenticateRequest> = new Map();\n\n /** Map of agent sessionId to client sessionId for notification routing */\n private readonly sessionIdMap: Map<string, string> = new Map();\n\n /**\n * Whether to automatically trigger OAuth browser flow when agent requires it.\n * When false, returns AUTH_REQUIRED error instead of opening browser.\n * Controlled by AUTH_AUTO_OAUTH environment variable (default: false for safety).\n */\n private readonly autoOAuth: boolean;\n\n /**\n * Create a new MessageRouter.\n *\n * @param registry - Registry index for agent lookup\n * @param runtimeManager - Runtime manager for agent processes\n * @param writeCallback - Callback for writing responses to stdout\n * @param apiKeys - API keys for agent authentication (optional)\n * @param authManager - AuthManager for OAuth authentication (optional, Requirements 11.2, 11.4)\n * @param autoOAuth - Whether to auto-trigger OAuth browser flow (default: from AUTH_AUTO_OAUTH env, or false)\n * @param deps - Optional dependencies for testing (spawnFn, TTY checks)\n */\n constructor(\n registry: IRegistryIndex,\n runtimeManager: AgentRuntimeManager,\n writeCallback: WriteCallback,\n apiKeys: Record<string, any> = {},\n authManager?: AuthManager,\n autoOAuth?: boolean,\n deps?: MessageRouterDeps,\n ) {\n this.registry = registry;\n this.runtimeManager = runtimeManager;\n this.writeCallback = writeCallback;\n this.apiKeys = apiKeys;\n this.authManager = authManager;\n // Default to false for safety - existing deployments won't suddenly open browsers\n // Can be enabled via AUTH_AUTO_OAUTH=true environment variable\n this.autoOAuth = autoOAuth ?? this.getAutoOAuthFromEnv();\n // Injectable dependencies for testing\n this.spawnFn = deps?.spawnFn ?? spawn;\n this.isStdinTTY = deps?.isStdinTTY ?? (() => process.stdin.isTTY ?? false);\n this.isStdoutTTY = deps?.isStdoutTTY ?? (() => process.stdout.isTTY ?? false);\n }\n\n /**\n * Get auto-OAuth setting from environment variable.\n * AUTH_AUTO_OAUTH=true enables auto-OAuth, any other value or unset disables it.\n */\n private getAutoOAuthFromEnv(): boolean {\n const envValue = process.env.AUTH_AUTO_OAUTH;\n return envValue === 'true' || envValue === '1' || envValue === 'yes';\n }\n\n /**\n * Get supported authentication methods for ACP initialize response.\n *\n * Requirement 11.1: WHEN responding to an initialize request, THE Registry_Launcher\n * SHALL include an `authMethods` array listing supported authentication methods.\n *\n * @returns Array of supported authentication methods\n */\n getSupportedAuthMethods(): AcpAuthMethod[] {\n const methods: AcpAuthMethod[] = [\n // Legacy API key authentication\n // Note: OpenAI and Anthropic API key support will be handled by model-credentials module\n { id: 'api-key', type: 'api-key' },\n ];\n\n // Add OAuth methods if AuthManager is available\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n if (this.authManager) {\n methods.push(\n { id: 'oauth2-github', type: 'oauth2', providerId: 'github' },\n { id: 'oauth2-google', type: 'oauth2', providerId: 'google' },\n { id: 'oauth2-cognito', type: 'oauth2', providerId: 'cognito' },\n { id: 'oauth2-azure', type: 'oauth2', providerId: 'azure' },\n { id: 'oauth2-oidc', type: 'oauth2', providerId: 'oidc' },\n );\n }\n\n return methods;\n }\n\n /**\n * Check if authentication is available for an agent.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response.\n *\n * @param agentId - The agent identifier\n * @returns True if authentication is available (OAuth or legacy API key)\n */\n async hasAuthenticationForAgent(agentId: string): Promise<boolean> {\n // Check OAuth credentials first (Requirement 10.3)\n if (this.authManager) {\n const token = await this.authManager.getTokenForAgent(agentId);\n if (token) {\n return true;\n }\n }\n\n // Fall back to legacy API key\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n return apiKey !== undefined;\n }\n\n /**\n * Check if api-key credentials are available for an agent.\n * This is a synchronous check for api-keys.json credentials.\n *\n * @param agentId - The agent identifier\n * @returns True if api-key credentials are available\n */\n hasCredentialsForAgent(agentId: string): boolean {\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n return apiKey !== undefined;\n }\n\n /**\n * Create an AUTH_REQUIRED error response.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response with the required\n * authentication method specified.\n *\n * @param id - The request ID\n * @param agentId - The agent identifier\n * @param requiredMethod - The required authentication method\n * @returns AUTH_REQUIRED error response\n */\n createAuthRequiredError(\n id: string | number | null,\n agentId: string,\n requiredMethod?: string\n ): ErrorResponse {\n // Build remediation instructions for the user\n // Use npx command (works without global install) and stdiobus (after global install)\n const remediation: Record<string, unknown> = {\n type: 'login_required',\n commands: [\n 'npx @stdiobus/workers-registry acp-registry --setup',\n 'stdiobus acp-registry --setup',\n ],\n hint: 'Run: npx @stdiobus/workers-registry acp-registry --setup',\n docsUrl: 'https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md'\n };\n\n return createErrorResponse(\n id,\n RoutingErrorCodes.AUTH_REQUIRED,\n 'Authentication required',\n {\n agentId,\n requiredMethod: requiredMethod ?? 'api-key',\n supportedMethods: this.getSupportedAuthMethods().map(m => m.id),\n remediation,\n }\n );\n }\n\n /**\n * Inject authentication into a request using AuthManager.\n *\n * Requirement 11.4: WHEN authentication is successful, THE Auth_Module SHALL inject\n * the access token into agent requests according to the provider's token injection method.\n *\n * @param agentId - The agent identifier\n * @param message - The message to inject auth into\n * @returns The message with authentication injected\n */\n async injectAuthentication(agentId: string, message: object): Promise<object> {\n if (this.authManager) {\n return this.authManager.injectAuth(agentId, message);\n }\n return message;\n }\n\n /**\n * Inject mcpServers from registry into session/new request params.\n *\n * If the agent has mcpServers configured in the registry, they are merged\n * with any mcpServers already present in the request params.\n * Registry servers are added first, then request servers (request takes precedence for duplicates).\n *\n * @param message - The transformed message (without agentId)\n * @param agentId - The agent ID to look up in registry\n * @returns Message with mcpServers injected into params\n */\n private injectMcpServers(message: object, agentId: string): object {\n const agent = this.registry.lookup(agentId);\n if (!agent?.mcpServers || agent.mcpServers.length === 0) {\n return message;\n }\n\n const msg = message as Record<string, unknown>;\n const params = (msg.params as Record<string, unknown>) || {};\n const existingServers = Array.isArray(params.mcpServers) ? params.mcpServers : [];\n\n // Convert registry McpServerConfig to ACP McpServer format\n const registryServers = agent.mcpServers.map((server) => ({\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env ? Object.entries(server.env).map(([name, value]) => ({ name, value })) : undefined,\n }));\n\n // Merge: registry servers first, then existing (existing can override by name)\n const existingNames = new Set(\n existingServers\n .filter((s): s is Record<string, unknown> => s !== null && typeof s === 'object')\n .map((s) => s.name)\n .filter((n): n is string => typeof n === 'string'),\n );\n\n const mergedServers = [\n ...registryServers.filter((s) => !existingNames.has(s.name)),\n ...existingServers,\n ];\n\n logInfo(`Injecting ${registryServers.length} MCP servers from registry for agent ${agentId}`);\n\n return {\n ...msg,\n params: {\n ...params,\n mcpServers: mergedServers,\n },\n };\n }\n\n /**\n * Route an incoming message to the appropriate agent.\n *\n * Extracts agentId, resolves spawn command, and forwards message.\n * If OAuth authentication is pending for the agent, queues the request\n * and resumes it after successful authentication (Task 21.3).\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n * Requirement 11.2: Block requests when OAuth required but not authenticated\n *\n * @param message - The incoming JSON-RPC message\n * @returns Error response if routing fails, undefined on success\n */\n async route(message: object): Promise<ErrorResponse | undefined> {\n const id = extractId(message);\n const agentId = extractAgentId(message);\n\n // Return error if agentId is missing\n if (agentId === undefined) {\n logError('Missing agentId in request');\n return createErrorResponse(id, RoutingErrorCodes.MISSING_AGENT_ID, 'Missing agentId');\n }\n\n // Check auth state for this agent (Task 21.3)\n const currentAuthState = this.getAuthState(agentId);\n\n // If OAuth flow is pending, queue the request (Requirement 3.1)\n if (currentAuthState === 'pending') {\n logInfo(`OAuth flow pending for agent ${agentId}, queueing request (id=${id})`);\n return this.queueRequest(agentId, message);\n }\n\n // If auth previously failed, return AUTH_REQUIRED error\n // This allows the client to retry or handle the failure\n if (currentAuthState === 'failed') {\n logError(`Authentication failed for agent ${agentId}, returning AUTH_REQUIRED`);\n const requiredProviderId = this.agentOAuthRequirements.get(agentId);\n return this.createAuthRequiredErrorWithProvider(id, agentId, requiredProviderId);\n }\n\n // Task 23.1: Block requests when OAuth required but not authenticated\n // Requirement 11.2: WHEN an agent requires authentication and credentials\n // are not available, THE Registry_Launcher SHALL return an AUTH_REQUIRED error\n const requiredProviderId = this.agentOAuthRequirements.get(agentId);\n if (requiredProviderId && currentAuthState !== 'authenticated') {\n // Agent requires OAuth - check if credentials are available\n const hasCredentials = await this.hasOAuthCredentialsForAgent(agentId, requiredProviderId);\n if (!hasCredentials) {\n logError(`Agent ${agentId} requires OAuth (provider: ${requiredProviderId}) but credentials not available`);\n return this.createAuthRequiredErrorWithProvider(id, agentId, requiredProviderId);\n }\n }\n\n // Proceed with normal routing\n return this.routeInternal(message, agentId, id);\n }\n\n /**\n * Check if OAuth credentials are available for an agent.\n *\n * Requirement 11.2: Check if credentials are available before routing.\n *\n * @param agentId - The agent identifier\n * @param providerId - The OAuth provider ID\n * @returns True if OAuth credentials are available\n */\n private async hasOAuthCredentialsForAgent(agentId: string, providerId: AuthProviderId): Promise<boolean> {\n if (!this.authManager) {\n return false;\n }\n\n // Check if we have a valid token for this agent/provider\n const token = await this.authManager.getTokenForAgent(agentId, providerId);\n return token !== null && token !== undefined;\n }\n\n /**\n * Create an AUTH_REQUIRED error response with provider information.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response with the required\n * authentication method specified.\n *\n * @param id - The request ID\n * @param agentId - The agent identifier\n * @param providerId - The required OAuth provider ID (optional)\n * @returns AUTH_REQUIRED error response with requiredMethod, supportedMethods, providerId\n */\n private createAuthRequiredErrorWithProvider(\n id: string | number | null,\n agentId: string,\n providerId?: AuthProviderId\n ): ErrorResponse {\n const supportedMethods = this.getSupportedAuthMethods();\n\n // Build remediation instructions for the user\n // Use npx command (works without global install) and stdiobus (after global install)\n const remediation: Record<string, unknown> = {\n type: 'login_required',\n provider: providerId || 'unknown',\n commands: providerId\n ? [\n `npx @stdiobus/workers-registry acp-registry --login ${providerId}`,\n `stdiobus acp-registry --login ${providerId}`,\n ]\n : [\n 'npx @stdiobus/workers-registry acp-registry --setup',\n 'stdiobus acp-registry --setup',\n ],\n hint: providerId\n ? `Run: npx @stdiobus/workers-registry acp-registry --login ${providerId}`\n : 'Run: npx @stdiobus/workers-registry acp-registry --setup',\n docsUrl: 'https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md'\n };\n\n return createErrorResponse(\n id,\n RoutingErrorCodes.AUTH_REQUIRED,\n 'Authentication required',\n {\n agentId,\n requiredMethod: providerId ? `oauth2-${providerId}` : 'oauth2',\n supportedMethods: supportedMethods.map(m => m.id),\n providerId: providerId,\n remediation,\n }\n );\n }\n\n /**\n * Internal routing logic after auth state checks.\n *\n * @param message - The incoming JSON-RPC message\n * @param agentId - The agent identifier\n * @param id - The request ID\n * @returns Error response if routing fails, undefined on success\n */\n private async routeInternal(\n message: object,\n agentId: string,\n id: string | number | null,\n ): Promise<ErrorResponse | undefined> {\n // Resolve agent to spawn command\n let spawnCommand;\n try {\n spawnCommand = this.registry.resolve(agentId);\n } catch (error) {\n if (error instanceof AgentNotFoundError) {\n logError(`Agent not found: ${agentId}`);\n return createErrorResponse(id, RoutingErrorCodes.AGENT_NOT_FOUND, 'Agent not found', {\n agentId,\n });\n }\n if (error instanceof PlatformNotSupportedError) {\n logError(`Platform not supported for agent: ${agentId}`);\n return createErrorResponse(\n id,\n RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,\n 'Platform not supported',\n { agentId, platform: (error as PlatformNotSupportedError).platform },\n );\n }\n throw error;\n }\n\n // Merge env from api-keys.json into spawn command\n // This ensures credentials are passed to the agent process\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: {\n ...spawnCommand.env,\n ...agentEnv,\n },\n };\n logInfo(`Injected ${Object.keys(agentEnv).length} env vars from api-keys.json for agent ${agentId}`);\n }\n\n // Get or spawn agent runtime\n let runtime: AgentRuntime;\n try {\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to spawn agent ${agentId}: ${(error as Error).message}`);\n return createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Agent spawn failed', {\n agentId,\n error: (error as Error).message,\n });\n }\n\n // Track pending request for correlation\n if (id !== null) {\n const msg = message as Record<string, unknown>;\n const clientSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : undefined;\n const method = typeof msg.method === 'string' ? msg.method : undefined;\n\n this.pendingRequests.set(id, {\n id,\n agentId,\n timestamp: Date.now(),\n method,\n clientSessionId,\n });\n }\n\n // Transform message (remove agentId) and forward to agent\n let transformedMessage = transformMessage(message);\n\n // For session/new requests, inject mcpServers from registry if agent has them configured\n const msg = message as Record<string, unknown>;\n if (msg.method === 'session/new') {\n transformedMessage = this.injectMcpServers(transformedMessage, agentId);\n }\n\n // Inject authentication into the message (Requirement 11.4)\n // This adds OAuth tokens or legacy API keys to the request\n transformedMessage = await this.injectAuthentication(agentId, transformedMessage);\n\n const success = runtime.write(transformedMessage);\n\n if (!success) {\n logError(`Failed to write to agent ${agentId}`);\n // Remove from pending if write failed\n if (id !== null) {\n this.pendingRequests.delete(id);\n }\n } else {\n logInfo(`Routed message to agent ${agentId}`);\n }\n\n return undefined;\n }\n\n // =============================================================================\n // Auth State Machine Methods (Task 21.3)\n // =============================================================================\n\n /**\n * Get the current authentication state for an agent.\n *\n * @param agentId - The agent identifier\n * @returns The current auth state (defaults to 'none')\n */\n getAuthState(agentId: string): AuthState {\n return this.authState.get(agentId) ?? 'none';\n }\n\n /**\n * Set the authentication state for an agent.\n *\n * Handles state transitions and triggers appropriate actions:\n * - none \u2192 pending: OAuth flow started\n * - pending \u2192 authenticated: Resume queued requests\n * - pending \u2192 failed: Reject queued requests with AUTH_REQUIRED\n *\n * Requirement 3.1: Track auth state during OAuth flow\n * Requirement 3.5: Handle timeout transitions to failed state\n *\n * @param agentId - The agent identifier\n * @param newState - The new auth state\n */\n setAuthState(agentId: string, newState: AuthState): void {\n const oldState = this.getAuthState(agentId);\n\n if (oldState === newState) {\n return; // No state change\n }\n\n logInfo(`Auth state transition for ${agentId}: ${oldState} \u2192 ${newState}`);\n this.authState.set(agentId, newState);\n\n // Handle state transition side effects\n if (newState === 'authenticated' && oldState === 'pending') {\n // Resume queued requests after successful authentication\n void this.processQueuedRequests(agentId);\n } else if (newState === 'failed' && oldState === 'pending') {\n // Reject queued requests with AUTH_REQUIRED error\n void this.rejectQueuedRequests(agentId);\n }\n }\n\n /**\n * Queue a request while OAuth authentication is pending.\n *\n * Returns a promise that resolves when the request is processed\n * (either routed successfully or rejected with an error).\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n *\n * @param agentId - The agent identifier\n * @param message - The message to queue\n * @returns Promise that resolves with the routing result\n */\n private queueRequest(agentId: string, message: object): Promise<ErrorResponse | undefined> {\n return new Promise((resolve) => {\n const queuedRequest: QueuedRequest = {\n message,\n queuedAt: Date.now(),\n resolve,\n };\n\n // Get or create queue for this agent\n let queue = this.requestQueue.get(agentId);\n if (!queue) {\n queue = [];\n this.requestQueue.set(agentId, queue);\n }\n\n queue.push(queuedRequest);\n logInfo(`Queued request for agent ${agentId}, queue size: ${queue.length}`);\n\n // Set up timeout for this request (Requirement 3.5)\n setTimeout(() => {\n this.handleQueuedRequestTimeout(agentId, queuedRequest);\n }, QUEUED_REQUEST_TIMEOUT_MS);\n });\n }\n\n /**\n * Handle timeout for a queued request.\n *\n * If the request is still in the queue when timeout fires,\n * remove it and resolve with a timeout error.\n *\n * Requirement 3.5: Handle timeout for queued requests\n *\n * @param agentId - The agent identifier\n * @param queuedRequest - The queued request that timed out\n */\n private handleQueuedRequestTimeout(agentId: string, queuedRequest: QueuedRequest): void {\n const queue = this.requestQueue.get(agentId);\n if (!queue) {\n return; // Queue already cleared\n }\n\n const index = queue.indexOf(queuedRequest);\n if (index === -1) {\n return; // Request already processed\n }\n\n // Remove from queue\n queue.splice(index, 1);\n logError(`Queued request timed out for agent ${agentId}`);\n\n // Resolve with timeout error\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.AUTH_REQUIRED, 'Authentication timeout', {\n agentId,\n reason: 'OAuth flow timed out while request was queued',\n })\n );\n }\n\n /**\n * Process queued requests after successful OAuth authentication.\n *\n * Routes all queued requests for the agent now that authentication\n * is complete.\n *\n * Requirement 3.1: Resume queued requests after successful authentication\n *\n * @param agentId - The agent identifier\n */\n private async processQueuedRequests(agentId: string): Promise<void> {\n const queue = this.requestQueue.get(agentId);\n if (!queue || queue.length === 0) {\n return;\n }\n\n logInfo(`Processing ${queue.length} queued requests for agent ${agentId}`);\n\n // Clear the queue before processing to prevent re-queueing\n this.requestQueue.delete(agentId);\n\n // Process each queued request\n for (const queuedRequest of queue) {\n try {\n const id = extractId(queuedRequest.message);\n const result = await this.routeInternal(queuedRequest.message, agentId, id);\n queuedRequest.resolve(result);\n } catch (error) {\n const id = extractId(queuedRequest.message);\n logError(`Error processing queued request for ${agentId}: ${(error as Error).message}`);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Failed to process queued request', {\n agentId,\n error: (error as Error).message,\n })\n );\n }\n }\n\n logInfo(`Completed processing queued requests for agent ${agentId}`);\n }\n\n /**\n * Reject all queued requests after OAuth authentication failure.\n *\n * Returns AUTH_REQUIRED error for all queued requests.\n *\n * Requirement 3.5: Handle failed authentication for queued requests\n *\n * @param agentId - The agent identifier\n */\n private async rejectQueuedRequests(agentId: string): Promise<void> {\n const queue = this.requestQueue.get(agentId);\n if (!queue || queue.length === 0) {\n return;\n }\n\n logInfo(`Rejecting ${queue.length} queued requests for agent ${agentId} due to auth failure`);\n\n // Clear the queue\n this.requestQueue.delete(agentId);\n\n // Reject each queued request with AUTH_REQUIRED error\n for (const queuedRequest of queue) {\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n this.createAuthRequiredError(id, agentId, 'oauth2')\n );\n }\n }\n\n /**\n * Get the number of queued requests for an agent.\n *\n * @param agentId - The agent identifier\n * @returns The number of queued requests\n */\n getQueuedRequestCount(agentId: string): number {\n const queue = this.requestQueue.get(agentId);\n return queue?.length ?? 0;\n }\n\n /**\n * Get the total number of queued requests across all agents.\n *\n * @returns The total number of queued requests\n */\n getTotalQueuedRequestCount(): number {\n let total = 0;\n for (const queue of this.requestQueue.values()) {\n total += queue.length;\n }\n return total;\n }\n\n /**\n * Handle a response from an agent process.\n *\n * Intercepts initialize responses to trigger automatic authentication and\n * inject authMethods (Requirement 11.1).\n * Handles agent-to-client requests (like session/request_permission) by\n * auto-responding when they cannot be forwarded to the client.\n * Tracks sessionId mapping for proper notification routing.\n * Handles authenticate responses for Agent Auth flow (Task 35.2).\n * Forwards all responses to stdout.\n *\n * @param agentId - The agent that sent the response\n * @param response - The response object from the agent\n */\n handleAgentResponse(agentId: string, response: object): void {\n const id = extractId(response);\n let msg = response as Record<string, unknown>;\n const method = typeof msg.method === 'string' ? msg.method : undefined;\n\n // Task 35.2: Handle authenticate response for Agent Auth flow\n // Check if this is a response to a pending authenticate request\n if (id !== null && typeof id === 'string') {\n const pendingAuth = this.pendingAuthenticateRequests.get(id);\n if (pendingAuth && pendingAuth.agentId === agentId) {\n this.handleAuthenticateResponse(pendingAuth, msg);\n return; // Don't forward authenticate responses to client\n }\n }\n\n // Handle agent-to-client requests (messages with both id and method).\n // These are requests FROM the agent TO the client (e.g., session/request_permission).\n // Since we are a headless launcher we cannot forward them to a human,\n // so we auto-respond to keep the agent unblocked.\n if (id !== null && method) {\n this.handleAgentRequest(agentId, id, method, msg);\n return;\n }\n\n // Check if this is a response to a tracked request (has id, no method)\n if (id !== null) {\n const pending = this.pendingRequests.get(id);\n if (pending && pending.agentId === agentId) {\n const result = msg.result as Record<string, unknown> | undefined;\n\n // Check if this is an initialize response - inject our authMethods (Requirement 11.1)\n // Use tracked method from pending request for reliable detection\n const isInitializeResponse = pending.method === 'initialize' && result !== undefined;\n if (isInitializeResponse) {\n const ourAuthMethods = this.getSupportedAuthMethods();\n const existingAuthMethods = Array.isArray(result.authMethods) ? result.authMethods : [];\n\n // Merge our auth methods with agent's auth methods (ours first)\n const mergedAuthMethods = [\n ...ourAuthMethods,\n ...existingAuthMethods.filter((m: any) =>\n !ourAuthMethods.some(our => our.id === m.id)\n ),\n ];\n\n // Create enriched response with authMethods\n msg = {\n ...msg,\n result: {\n ...result,\n authMethods: mergedAuthMethods,\n },\n };\n\n logInfo(`Injected ${ourAuthMethods.length} auth methods into initialize response for ${agentId}`);\n }\n\n // Check if this is an initialize response with authMethods (from agent)\n // Only trigger auto-auth on initialize responses to reduce attack surface\n if (isInitializeResponse && result && Array.isArray(result.authMethods) && result.authMethods.length > 0) {\n // Parse and validate auth methods using explicit mapping (Task 21.1)\n const parsedMethods = parseAuthMethods(result.authMethods);\n if (parsedMethods.length > 0) {\n // Task 23.1: Track OAuth requirements for this agent\n // Requirement 11.2: Cache auth requirements per agent\n const oauthMethods = getOAuthMethods(parsedMethods);\n const apiKeyMethods = getApiKeyMethods(parsedMethods);\n\n // Check if agent has api-key credentials available\n // If api-key is supported AND credentials exist, don't require OAuth\n const hasApiKeyCredentials = this.hasCredentialsForAgent(agentId);\n\n if (oauthMethods.length > 0 && !(apiKeyMethods.length > 0 && hasApiKeyCredentials)) {\n // Store the first OAuth provider as the required provider\n // Only if api-key fallback is not available\n const requiredProviderId = oauthMethods[0].providerId;\n this.agentOAuthRequirements.set(agentId, requiredProviderId);\n logInfo(`Agent ${agentId} requires OAuth authentication with provider: ${requiredProviderId}`);\n } else if (apiKeyMethods.length > 0 && hasApiKeyCredentials) {\n logInfo(`Agent ${agentId} supports OAuth but api-key credentials available, using api-key`);\n }\n\n // Task 33.2: Only auto-trigger OAuth if AUTH_AUTO_OAUTH is enabled\n // Default is false for backward compatibility - existing deployments won't suddenly open browsers\n if (this.autoOAuth) {\n logInfo(`Agent ${agentId} requires authentication, attempting auto-auth with ${parsedMethods.length} valid methods`);\n this.setAuthState(agentId, 'pending');\n void this.attemptAuthentication(agentId, parsedMethods);\n } else {\n logInfo(`Agent ${agentId} requires authentication but AUTH_AUTO_OAUTH is disabled. Use --login to authenticate.`);\n // Don't set auth state to pending - let requests fail with AUTH_REQUIRED\n // This allows users to explicitly authenticate via --login command\n }\n } else {\n logError(`Agent ${agentId} has authMethods but none are valid after parsing`);\n this.setAuthState(agentId, 'none');\n }\n }\n\n // Check if this is a session/new response - track sessionId mapping\n // session/new responses have sessionId in result, not in params\n if (result && typeof result.sessionId === 'string') {\n const agentSessionId = result.sessionId;\n const clientSessionId = pending.clientSessionId;\n if (clientSessionId) {\n this.sessionIdMap.set(agentSessionId, clientSessionId);\n logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`);\n }\n }\n\n this.pendingRequests.delete(id);\n }\n }\n\n // Handle notifications (no id) - map sessionId from agent to client\n if (id === null && method) {\n logInfo(`Received notification: ${method}`);\n const params = msg.params as Record<string, unknown> | undefined;\n if (params && typeof params.sessionId === 'string') {\n const agentSessionId = params.sessionId;\n const clientSessionId = this.sessionIdMap.get(agentSessionId);\n\n if (clientSessionId) {\n // Replace agent sessionId with client sessionId for stdio Bus routing\n const enriched = {\n ...msg,\n sessionId: clientSessionId,\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for agent context\n },\n };\n logInfo(`Forwarding notification with mapped sessionId: ${clientSessionId}`);\n this.writeCallback(enriched);\n return;\n } else {\n // CRITICAL FIX: If no mapping exists, use default sessionId for routing\n // Cannot forward with unmapped agentSessionId as stdio_bus won't recognize it\n logError(`Notification with unmapped agentSessionId: ${agentSessionId}, using default sessionId`);\n const enriched = {\n ...msg,\n sessionId: 'global-notifications',\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for context\n },\n };\n this.writeCallback(enriched);\n return;\n }\n } else {\n // CRITICAL FIX: Handle notifications without sessionId in params\n // Check if sessionId is at top level or if this is a global notification\n const topLevelSessionId = msg.sessionId as string | undefined;\n if (topLevelSessionId) {\n // sessionId already at top level, forward as-is\n this.writeCallback(response);\n return;\n } else {\n // Global notification without sessionId - add a default sessionId for routing\n logError(`Notification without sessionId: ${method}, adding default sessionId for routing`);\n const enriched = {\n ...msg,\n sessionId: 'global-notifications', // Default sessionId for stdio_bus routing\n };\n this.writeCallback(enriched);\n return;\n }\n }\n }\n\n // Forward response unchanged\n this.writeCallback(msg);\n }\n\n /**\n * Handle a request from an agent to the client.\n *\n * Agent-to-client requests (JSON-RPC messages with both `id` and `method`)\n * require a response. Since the Registry Launcher is headless and cannot\n * forward these to a human, we auto-respond to keep the agent unblocked.\n *\n * Known methods:\n * - session/request_permission: Auto-approve with the first \"allow\" option\n *\n * Unknown methods get a generic success response so the agent continues.\n *\n * @param agentId - The agent that sent the request\n * @param id - The JSON-RPC request id\n * @param method - The JSON-RPC method name\n * @param msg - The full message object\n */\n private handleAgentRequest(\n agentId: string,\n id: string | number,\n method: string,\n msg: Record<string, unknown>,\n ): void {\n logInfo(`Agent ${agentId} sent request: ${method} (id=${id}), auto-responding`);\n\n let result: Record<string, unknown>;\n\n if (method === 'session/request_permission') {\n result = this.buildPermissionResponse(msg);\n } else {\n // Fallback: generic success so the agent is never stuck waiting\n logInfo(`Unknown agent request method: ${method}, sending generic success`);\n result = {};\n }\n\n const response = {\n jsonrpc: '2.0' as const,\n id,\n result,\n };\n\n // Send directly to the agent, not to stdout\n this.sendToAgent(agentId, response);\n }\n\n /**\n * Build an auto-approve result for session/request_permission.\n *\n * Picks the first \"allow\" option from the request, preferring\n * allow_always > allow_once > first option as fallback.\n *\n * @param msg - The request_permission message\n * @returns The result object for the response\n */\n private buildPermissionResponse(msg: Record<string, unknown>): Record<string, unknown> {\n const params = msg.params as Record<string, unknown> | undefined;\n const options = params?.options as Array<Record<string, unknown>> | undefined;\n\n if (!options || options.length === 0) {\n return { optionId: 'approved' };\n }\n\n // Prefer allow_always, then allow_once, then first option\n const allowAlways = options.find(o => o.kind === 'allow_always');\n if (allowAlways && typeof allowAlways.optionId === 'string') {\n logInfo(`Auto-approving permission with option: ${allowAlways.optionId} (allow_always)`);\n return { optionId: allowAlways.optionId };\n }\n\n const allowOnce = options.find(o => o.kind === 'allow_once');\n if (allowOnce && typeof allowOnce.optionId === 'string') {\n logInfo(`Auto-approving permission with option: ${allowOnce.optionId} (allow_once)`);\n return { optionId: allowOnce.optionId };\n }\n\n // Fallback to first option\n const firstOption = options[0];\n const optionId = typeof firstOption.optionId === 'string' ? firstOption.optionId : 'approved';\n logInfo(`Auto-approving permission with fallback option: ${optionId}`);\n return { optionId };\n }\n\n /**\n * Handle an authenticate response from an agent.\n *\n * Task 35.2: Handle authenticate response\n * - On success: resolve the pending authenticate request with success\n * - On error: resolve with failure and log the error\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - after agent completes OAuth flow,\n * it responds to the authenticate request.\n *\n * @param pendingAuth - The pending authenticate request\n * @param response - The response from the agent\n */\n private handleAuthenticateResponse(\n pendingAuth: PendingAuthenticateRequest,\n response: Record<string, unknown>,\n ): void {\n const { agentId, authMethodId, requestId } = pendingAuth;\n\n // Check for error response\n if (response.error) {\n const error = response.error as Record<string, unknown>;\n const errorCode = error.code ?? 'UNKNOWN';\n const errorMessage = typeof error.message === 'string' ? error.message : 'Unknown error';\n logError(`Agent Auth failed for ${agentId}: [${errorCode}] ${errorMessage}`);\n pendingAuth.resolve(false, errorMessage);\n return;\n }\n\n // Check for success response\n if (response.result !== undefined) {\n logInfo(`Agent Auth succeeded for ${agentId} (method: ${authMethodId}, request: ${requestId})`);\n pendingAuth.resolve(true);\n return;\n }\n\n // Unexpected response format\n logError(`Unexpected authenticate response format for ${agentId}: ${JSON.stringify(response)}`);\n pendingAuth.resolve(false, 'Unexpected response format');\n }\n\n /**\n * Send a JSON-RPC message directly to an agent process.\n *\n * @param agentId - The agent to send to\n * @param message - The message to send\n */\n private sendToAgent(agentId: string, message: object): void {\n let runtime: AgentRuntime | undefined;\n try {\n runtime = this.runtimeManager.get(agentId);\n } catch {\n logError(`Failed to get runtime for agent ${agentId} to send response`);\n return;\n }\n\n if (!runtime) {\n logError(`No runtime found for agent ${agentId}, cannot send response`);\n return;\n }\n\n const success = runtime.write(message);\n if (!success) {\n logError(`Failed to write response to agent ${agentId}`);\n } else {\n logInfo(`Sent auto-response to agent ${agentId}`);\n }\n }\n\n /**\n * Attempt automatic authentication for an agent.\n *\n * Selects the best authentication method and initiates authentication.\n * Uses parsed auth methods with validated types and provider IDs.\n *\n * Authentication method precedence (AUTH_REQUIREMENTS.md):\n * 1. Agent Auth (type: \"agent\" or no type) - agent handles OAuth internally\n * 2. OAuth methods (type: \"oauth2\") - client handles browser-based flow\n * 3. API key methods - only if no OAuth methods are present\n *\n * AUTH_REQUIREMENTS.md: Agent Auth is the default authentication method\n * where the agent manages the entire OAuth flow independently.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Parsed and validated authentication methods (Task 21.1)\n */\n private async attemptAuthentication(\n agentId: string,\n authMethods: ParsedAuthMethod[],\n ): Promise<void> {\n // Check for Agent Auth methods first (AUTH_REQUIREMENTS.md: Agent Auth is default)\n const agentAuthMethods = getAgentAuthMethods(authMethods);\n\n if (agentAuthMethods.length > 0) {\n // Agent Auth: call authenticate method on agent, agent handles OAuth internally\n await this.attemptAgentAuthentication(agentId, agentAuthMethods);\n return;\n }\n\n // Check for Terminal Auth methods (Task 36: type: \"terminal\")\n // Terminal Auth: spawn agent with args/env for interactive TUI setup\n const terminalAuthMethods = getTerminalAuthMethods(authMethods);\n\n if (terminalAuthMethods.length > 0) {\n // Terminal Auth: spawn interactive setup process\n await this.attemptTerminalAuthentication(agentId, terminalAuthMethods);\n return;\n }\n\n // Check for OAuth methods (type: \"oauth2\") - client handles browser flow\n const oauthMethods = getOAuthMethods(authMethods);\n\n if (oauthMethods.length > 0) {\n // OAuth methods present - initiate OAuth flow (Requirement 3.1, 3.2)\n // Do NOT fall back to API key when agent explicitly requires OAuth\n await this.attemptOAuthAuthentication(agentId, oauthMethods);\n return;\n }\n\n // No OAuth methods - try API key authentication\n await this.attemptApiKeyAuthentication(agentId, authMethods);\n }\n\n /**\n * Attempt Agent Auth authentication for an agent.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls `authenticate` method on agent,\n * agent handles: HTTP server, browser launch, OAuth callback, token storage.\n *\n * Task 35.1: Call `authenticate` JSON-RPC method on agent\n * - Send: { jsonrpc: \"2.0\", method: \"authenticate\", params: { id: authMethod.id }, id: requestId }\n * - Wait for response from agent\n *\n * Task 35.2: Handle authenticate response\n * - On success: retry original request (session/new)\n * - On error: return error to client\n *\n * @param agentId - The agent to authenticate\n * @param agentAuthMethods - Agent Auth methods from agent's authMethods\n */\n private async attemptAgentAuthentication(\n agentId: string,\n agentAuthMethods: Array<ParsedAuthMethod & { kind: 'agent' }>,\n ): Promise<void> {\n // Select the first Agent Auth method\n const selectedMethod = agentAuthMethods[0];\n\n logInfo(`Agent ${agentId} requires Agent Auth with method: ${selectedMethod.id}`);\n logInfo(`Calling authenticate method on agent - agent will handle OAuth flow internally`);\n\n // Set auth state to pending while agent handles OAuth\n this.setAuthState(agentId, 'pending');\n\n try {\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for Agent Auth: ${(error as Error).message}`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Call authenticate method on agent and wait for response\n const success = await this.callAgentAuthenticate(agentId, selectedMethod.id, runtime);\n\n if (success) {\n logInfo(`Agent Auth successful for agent ${agentId}`);\n this.setAuthState(agentId, 'authenticated');\n } else {\n logError(`Agent Auth failed for agent ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Agent Auth error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Call the `authenticate` JSON-RPC method on an agent.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n * Send: { jsonrpc: \"2.0\", method: \"authenticate\", params: { id: authMethod.id }, id: requestId }\n *\n * Task 35.1: Call `authenticate` JSON-RPC method on agent\n *\n * @param agentId - The agent to authenticate\n * @param authMethodId - The auth method ID from authMethods\n * @param runtime - The agent runtime\n * @returns Promise that resolves to true on success, false on failure\n */\n private callAgentAuthenticate(\n agentId: string,\n authMethodId: string,\n runtime: AgentRuntime,\n ): Promise<boolean> {\n return new Promise((resolve) => {\n const requestId = `agent-auth-${agentId}-${Date.now()}`;\n\n // Build authenticate request per AUTH_REQUIREMENTS.md\n const authenticateRequest = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'authenticate',\n params: {\n id: authMethodId,\n },\n };\n\n // Track the pending authenticate request\n const pendingRequest: PendingAuthenticateRequest = {\n requestId,\n agentId,\n authMethodId,\n sentAt: Date.now(),\n resolve: (success: boolean, error?: string) => {\n // Clean up the pending request\n this.pendingAuthenticateRequests.delete(requestId);\n if (error) {\n logError(`Agent Auth response error: ${error}`);\n }\n resolve(success);\n },\n };\n\n this.pendingAuthenticateRequests.set(requestId, pendingRequest);\n\n // Set up timeout for the authenticate request\n setTimeout(() => {\n const pending = this.pendingAuthenticateRequests.get(requestId);\n if (pending) {\n logError(`Agent Auth timeout for agent ${agentId} (method: ${authMethodId})`);\n this.pendingAuthenticateRequests.delete(requestId);\n resolve(false);\n }\n }, AGENT_AUTH_TIMEOUT_MS);\n\n // Send authenticate request to agent\n const success = runtime.write(authenticateRequest);\n\n if (!success) {\n logError(`Failed to send authenticate request to agent ${agentId}`);\n this.pendingAuthenticateRequests.delete(requestId);\n resolve(false);\n } else {\n logInfo(`Sent authenticate request to agent ${agentId} (id: ${requestId}, method: ${authMethodId})`);\n }\n });\n }\n\n /**\n * Attempt Terminal Auth authentication for an agent.\n *\n * AUTH_REQUIREMENTS.md: Terminal Auth - client spawns agent binary with args/env\n * from authMethod for interactive TUI setup.\n *\n * Task 36.1: Parse Terminal Auth from authMethods\n * Task 36.2: Launch agent binary with args/env\n * Task 36.3: Retry after Terminal Auth\n *\n * Flow:\n * 1. Stop current agent runtime (if running)\n * 2. Spawn agent with args/env from authMethod (stdio: 'inherit' for TUI)\n * 3. Wait for process exit\n * 4. On exit code 0: restart normal runtime and verify auth\n * 5. On non-zero exit: mark as failed\n *\n * @param agentId - The agent to authenticate\n * @param terminalAuthMethods - Terminal Auth methods from agent's authMethods\n */\n private async attemptTerminalAuthentication(\n agentId: string,\n terminalAuthMethods: Array<ParsedAuthMethod & { kind: 'terminal' }>,\n ): Promise<void> {\n // Select the first Terminal Auth method\n const selectedMethod = terminalAuthMethods[0];\n\n logInfo(`Agent ${agentId} requires Terminal Auth with method: ${selectedMethod.id}`);\n\n // Check if we're in a TTY environment (required for interactive TUI)\n if (!this.isStdinTTY() || !this.isStdoutTTY()) {\n logError(`Terminal Auth requires interactive terminal (TTY). Run in a terminal with stdin/stdout connected.`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Set auth state to pending\n this.setAuthState(agentId, 'pending');\n\n try {\n // Step 1: Stop current agent runtime if running\n const existingRuntime = this.runtimeManager.get(agentId);\n if (existingRuntime) {\n logInfo(`Stopping existing runtime for agent ${agentId} before Terminal Auth`);\n await this.runtimeManager.terminate(agentId);\n }\n\n // Step 2: Get spawn command for the agent\n const baseSpawnCommand = this.registry.resolve(agentId);\n\n // Build Terminal Auth spawn command using args/env from authMethod (replacement, not merge)\n const terminalArgs = selectedMethod.args ?? [];\n const terminalEnv = {\n ...process.env, // Inherit current environment\n ...(selectedMethod.env ?? {}), // Override with authMethod env\n };\n\n logInfo(`Launching Terminal Auth for ${agentId}: ${baseSpawnCommand.command} ${terminalArgs.join(' ')}`);\n\n // Step 3: Spawn interactive process with inherited stdio\n const exitCode = await this.runTerminalAuthProcess(\n baseSpawnCommand.command,\n terminalArgs,\n terminalEnv,\n );\n\n // Step 4: Handle exit code\n if (exitCode === 0) {\n logInfo(`Terminal Auth process exited successfully for ${agentId}`);\n\n // Restart normal runtime and verify auth\n const authVerified = await this.verifyTerminalAuthSuccess(agentId);\n\n if (authVerified) {\n logInfo(`Terminal Auth verified for ${agentId}`);\n this.setAuthState(agentId, 'authenticated');\n } else {\n logError(`Terminal Auth completed but verification failed for ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } else {\n logError(`Terminal Auth process exited with code ${exitCode} for ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Terminal Auth error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Run the Terminal Auth process with inherited stdio for interactive TUI.\n *\n * @param command - The command to execute\n * @param args - Command-line arguments\n * @param env - Environment variables\n * @returns Promise that resolves to the exit code\n */\n private runTerminalAuthProcess(\n command: string,\n args: string[],\n env: Record<string, string | undefined>,\n ): Promise<number> {\n return new Promise((resolve, reject) => {\n logInfo(`Spawning Terminal Auth process: ${command} ${args.join(' ')}`);\n\n const child = this.spawnFn(command, args, {\n env: env as NodeJS.ProcessEnv,\n stdio: 'inherit', // Inherit stdin/stdout/stderr for interactive TUI\n shell: false,\n });\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n logError(`Terminal Auth process timed out after ${TERMINAL_AUTH_TIMEOUT_MS}ms`);\n child.kill('SIGTERM');\n // Give it a moment to terminate gracefully, then SIGKILL\n setTimeout(() => {\n if (!child.killed) {\n child.kill('SIGKILL');\n }\n }, 5000);\n }, TERMINAL_AUTH_TIMEOUT_MS);\n\n child.on('error', (error) => {\n clearTimeout(timeoutId);\n reject(error);\n });\n\n child.on('exit', (code, signal) => {\n clearTimeout(timeoutId);\n if (signal) {\n logError(`Terminal Auth process killed by signal: ${signal}`);\n resolve(1); // Treat signal termination as failure\n } else {\n resolve(code ?? 1);\n }\n });\n });\n }\n\n /**\n * Verify that Terminal Auth was successful by restarting the agent\n * and checking if authentication is now available.\n *\n * @param agentId - The agent to verify\n * @returns true if auth is now available, false otherwise\n */\n private async verifyTerminalAuthSuccess(agentId: string): Promise<boolean> {\n try {\n // Restart the agent runtime\n let spawnCommand = this.registry.resolve(agentId);\n\n // Merge env from api-keys.json (credentials may have been stored by Terminal Auth)\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n\n const runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n\n // For now, we trust that Terminal Auth stored credentials properly\n // A more robust verification would send an initialize request and check\n // if AUTH_REQUIRED is still returned, but that adds complexity.\n // The next actual request will verify auth status.\n return runtime.state === 'running';\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Failed to verify Terminal Auth for ${agentId}: ${errorMessage}`);\n return false;\n }\n }\n\n /**\n * Attempt OAuth authentication for an agent using browser-based flow.\n *\n * Requirement 3.1: WHEN an agent requires OAuth authentication with `type: \"agent\"`,\n * THE Auth_Module SHALL initiate the OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirement 3.2: WHEN initiating the authorization flow, THE Auth_Module SHALL\n * open the system default browser to the provider's authorization URL.\n *\n * @param agentId - The agent to authenticate\n * @param oauthMethods - OAuth methods from agent's authMethods (already validated)\n */\n private async attemptOAuthAuthentication(\n agentId: string,\n oauthMethods: Array<ParsedAuthMethod & { kind: 'oauth2' }>,\n ): Promise<void> {\n // Check if AuthManager is available for OAuth\n if (!this.authManager) {\n logError(`OAuth authentication required for agent ${agentId}, but AuthManager not available`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Select the first OAuth method (could be enhanced with user preference later)\n const selectedMethod = oauthMethods[0];\n const providerId = selectedMethod.providerId;\n\n logInfo(`Agent ${agentId} requires OAuth authentication with provider: ${providerId}`);\n logInfo(`Initiating OAuth 2.1 Authorization Code flow with PKCE for ${providerId}`);\n\n // Set auth state to pending while browser flow is in progress\n this.setAuthState(agentId, 'pending');\n\n try {\n // Call AuthManager.authenticateAgent to start the browser-based OAuth flow\n // This opens the system default browser and waits for the callback\n const result = await this.authManager.authenticateAgent(providerId);\n\n if (result.success) {\n logInfo(`OAuth authentication successful for agent ${agentId} with provider ${providerId}`);\n this.setAuthState(agentId, 'authenticated');\n\n // After successful OAuth, send authenticate request to agent with the token\n await this.sendOAuthCredentialsToAgent(agentId, selectedMethod);\n } else {\n const errorMsg = result.error?.message ?? 'Unknown error';\n const errorCode = result.error?.code ?? 'UNKNOWN';\n logError(`OAuth authentication failed for agent ${agentId}: [${errorCode}] ${errorMsg}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`OAuth authentication error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Send OAuth credentials to agent after successful browser-based authentication.\n *\n * After the OAuth flow completes successfully, this method retrieves the token\n * from AuthManager and sends an authenticate request to the agent.\n *\n * @param agentId - The agent to send credentials to\n * @param method - The OAuth method used for authentication\n */\n private async sendOAuthCredentialsToAgent(\n agentId: string,\n method: ParsedAuthMethod & { kind: 'oauth2' },\n ): Promise<void> {\n if (!this.authManager) {\n logError(`Cannot send OAuth credentials: AuthManager not available`);\n return;\n }\n\n // Get the access token from AuthManager\n const token = await this.authManager.getTokenForAgent(agentId, method.providerId);\n if (!token) {\n logError(`No OAuth token available for agent ${agentId} after successful auth`);\n return;\n }\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for OAuth credential injection: ${(error as Error).message}`);\n return;\n }\n\n // Build authenticate request with OAuth token\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: method.id,\n credentials: {\n accessToken: token,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send OAuth authenticate request to ${agentId}: ${error.message}`);\n } else {\n logInfo(`Sent OAuth authenticate request to agent ${agentId}`);\n }\n });\n }\n }\n\n /**\n * Attempt API key authentication for an agent.\n *\n * This is the fallback authentication method when no OAuth methods are present.\n * Uses the legacy api-keys.json configuration.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Parsed authentication methods (already validated)\n */\n private async attemptApiKeyAuthentication(\n agentId: string,\n authMethods: ParsedAuthMethod[],\n ): Promise<void> {\n // Get API key for this agent\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n\n if (!apiKey) {\n logError(`No API key found for agent ${agentId}, authentication will fail`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Get API key methods from parsed methods (already validated)\n const apiKeyMethods = getApiKeyMethods(authMethods);\n\n // Allowlist of safe method IDs for API key authentication\n // Only send API keys to methods we explicitly trust\n // Note: OpenAI and Anthropic API key methods will be handled by model-credentials module\n const SAFE_API_KEY_METHODS = [\n 'api-key',\n 'openai-api-key',\n 'github-api-key',\n 'google-api-key',\n 'azure-api-key',\n 'cognito-api-key',\n ];\n\n // Select authentication method from allowlist only (security: don't send API key to arbitrary methods)\n const selectedMethod = apiKeyMethods.find(m => SAFE_API_KEY_METHODS.includes(m.id));\n\n if (!selectedMethod) {\n // No safe API key method available - do not fall back to arbitrary methods\n logError(`No safe API key method available for agent ${agentId}, skipping auto-auth`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n logInfo(`Authenticating agent ${agentId} with API key method: ${selectedMethod.id} (providerId: ${selectedMethod.providerId ?? 'none'})`);\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for authentication: ${(error as Error).message}`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Build authenticate request\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: selectedMethod.id,\n credentials: {\n apiKey: apiKey,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send authenticate request to ${agentId}: ${error.message}`);\n this.setAuthState(agentId, 'failed');\n } else {\n logInfo(`Sent authenticate request to agent ${agentId}`);\n // Mark as authenticated (optimistic)\n this.setAuthState(agentId, 'authenticated');\n }\n });\n }\n }\n\n /**\n * Get the number of pending requests.\n *\n * @returns The count of pending requests\n */\n get pendingCount(): number {\n return this.pendingRequests.size;\n }\n\n /**\n * Check if a request ID is pending.\n *\n * @param id - The request ID to check\n * @returns true if the request is pending, false otherwise\n */\n isPending(id: string | number): boolean {\n return this.pendingRequests.has(id);\n }\n\n /**\n * Clear all pending requests.\n * Useful for cleanup during shutdown.\n */\n clearPending(): void {\n this.pendingRequests.clear();\n }\n\n /**\n * Clear all queued requests and auth state.\n * Useful for cleanup during shutdown.\n *\n * Rejects all queued requests with a shutdown error.\n */\n clearQueues(): void {\n // Reject all queued requests\n for (const [agentId, queue] of this.requestQueue.entries()) {\n for (const queuedRequest of queue) {\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Router shutdown', {\n agentId,\n reason: 'Router is shutting down',\n })\n );\n }\n }\n\n this.requestQueue.clear();\n this.authState.clear();\n this.agentOAuthRequirements.clear();\n logInfo('Cleared all request queues, auth state, and OAuth requirements');\n }\n\n /**\n * Reset auth state for an agent.\n * Useful for retry scenarios or logout.\n *\n * @param agentId - The agent identifier\n */\n resetAuthState(agentId: string): void {\n this.setAuthState(agentId, 'none');\n }\n\n /**\n * Get the OAuth requirement for an agent.\n *\n * Requirement 11.2: Check agent auth requirements.\n *\n * @param agentId - The agent identifier\n * @returns The required OAuth provider ID, or undefined if no OAuth required\n */\n getAgentOAuthRequirement(agentId: string): AuthProviderId | undefined {\n return this.agentOAuthRequirements.get(agentId);\n }\n\n /**\n * Set the OAuth requirement for an agent.\n *\n * Requirement 11.2: Cache auth requirements per agent.\n *\n * @param agentId - The agent identifier\n * @param providerId - The required OAuth provider ID\n */\n setAgentOAuthRequirement(agentId: string, providerId: AuthProviderId): void {\n this.agentOAuthRequirements.set(agentId, providerId);\n logInfo(`Set OAuth requirement for agent ${agentId}: provider ${providerId}`);\n }\n\n /**\n * Clear the OAuth requirement for an agent.\n *\n * @param agentId - The agent identifier\n */\n clearAgentOAuthRequirement(agentId: string): void {\n this.agentOAuthRequirements.delete(agentId);\n logInfo(`Cleared OAuth requirement for agent ${agentId}`);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Core type definitions for OAuth 2.1 authentication.\n *\n * @module types\n */\n\n// =============================================================================\n// Provider and Status Types\n// =============================================================================\n\n/**\n * Supported OAuth/OIDC provider identifiers for user identity.\n *\n * Note: OpenAI and Anthropic are NOT public OAuth IdPs for third-party login.\n * They use API keys instead. See ModelProviderId and model-credentials module.\n *\n * Requirements: 7.1, 7a.1\n */\nexport type AuthProviderId =\n | 'google'\n | 'azure'\n | 'cognito'\n | 'github'\n | 'oidc';\n\n/**\n * Model API providers that use API keys (NOT OAuth).\n *\n * These providers do not offer public OAuth IdP for third-party login.\n * Use API key authentication instead.\n *\n * Requirements: 7b.1, 7b.2\n */\nexport type ModelProviderId =\n | 'openai'\n | 'anthropic';\n\n/**\n * Storage backend types.\n */\nexport type StorageBackendType = 'keychain' | 'encrypted-file' | 'memory';\n\n/**\n * Token status for a provider.\n */\nexport type TokenStatus =\n | 'authenticated' // Valid tokens available\n | 'expired' // Tokens expired, refresh needed\n | 'refresh-failed' // Refresh attempted but failed\n | 'not-configured'; // No credentials stored\n\n/**\n * Authentication error codes.\n */\nexport type AuthErrorCode =\n | 'INVALID_STATE'\n | 'TIMEOUT'\n | 'NETWORK_ERROR'\n | 'INVALID_CREDENTIALS'\n | 'STORAGE_ERROR'\n | 'PROVIDER_ERROR'\n | 'UNSUPPORTED_PROVIDER'\n | 'CALLBACK_ERROR'\n | 'TOKEN_REFRESH_FAILED'\n | 'HEADLESS_ENVIRONMENT';\n\n// =============================================================================\n// Token and Credential Types\n// =============================================================================\n\n/**\n * OAuth token response from provider.\n */\nexport interface TokenResponse {\n accessToken: string;\n tokenType: string;\n expiresIn?: number; // Seconds until expiration\n refreshToken?: string;\n scope?: string;\n idToken?: string;\n}\n\n/**\n * Token injection method for agent requests.\n */\nexport interface TokenInjectionMethod {\n type: 'header' | 'query' | 'body';\n key: string; // e.g., 'Authorization', 'x-api-key', 'access_token'\n format?: string; // e.g., 'Bearer {token}'\n}\n\n/**\n * Custom provider endpoints (for Cognito/Azure).\n */\nexport interface ProviderEndpoints {\n authorizationEndpoint: string;\n tokenEndpoint: string;\n userInfoEndpoint?: string;\n}\n\n/**\n * Stored credentials in credential store.\n */\nexport interface StoredCredentials {\n providerId: AuthProviderId;\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number; // Unix timestamp\n scope?: string;\n clientId?: string; // For terminal auth\n clientSecret?: string; // For terminal auth (encrypted)\n customEndpoints?: ProviderEndpoints; // For Cognito/Azure\n storedAt: number; // Unix timestamp\n}\n\n// =============================================================================\n// Authorization Flow Types\n// =============================================================================\n\n/**\n * Authorization parameters for building auth URL.\n */\nexport interface AuthorizationParams {\n clientId: string;\n redirectUri: string;\n scope: string;\n state: string;\n codeChallenge: string;\n codeChallengeMethod: 'S256';\n responseType: 'code';\n additionalParams?: Record<string, string>;\n}\n\n/**\n * Successful callback result from OAuth redirect.\n */\nexport interface CallbackSuccess {\n success: true;\n code: string;\n state: string;\n}\n\n/**\n * Error callback result from OAuth redirect.\n */\nexport interface CallbackErrorResult {\n success: false;\n error: string;\n errorDescription?: string;\n state?: string; // State may be present in error callbacks\n}\n\n/**\n * Callback result from OAuth redirect (discriminated union).\n * OAuth error redirects may not include 'code', only 'error'.\n */\nexport type CallbackResult = CallbackSuccess | CallbackErrorResult;\n\n/**\n * Agent auth flow options.\n */\nexport interface AgentAuthOptions {\n timeoutMs?: number; // Default: 300000 (5 minutes)\n scopes?: string[]; // Override default scopes\n clientId?: string; // Override default client ID\n}\n\n// =============================================================================\n// Result and Error Types\n// =============================================================================\n\n/**\n * Authentication error with details.\n */\nexport interface AuthError {\n code: AuthErrorCode;\n message: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Successful authentication result.\n */\nexport interface AuthResultSuccess {\n success: true;\n providerId: AuthProviderId;\n}\n\n/**\n * Failed authentication result.\n */\nexport interface AuthResultFailure {\n success: false;\n providerId: AuthProviderId;\n error: AuthError;\n}\n\n/**\n * Authentication result (discriminated union).\n */\nexport type AuthResult = AuthResultSuccess | AuthResultFailure;\n\n/**\n * Auth status for display.\n */\nexport interface AuthStatusEntry {\n providerId: AuthProviderId;\n status: TokenStatus;\n expiresAt?: number;\n scope?: string;\n lastRefresh?: number;\n}\n\n/**\n * Map of provider IDs to their auth status.\n */\nexport type AuthStatusMap = Map<AuthProviderId, AuthStatusEntry>;\n\n// =============================================================================\n// Configuration Types\n// =============================================================================\n\n/**\n * Provider configuration.\n */\nexport interface ProviderConfig {\n clientId: string;\n clientSecret?: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n defaultScopes: string[];\n tokenInjection: TokenInjectionMethod;\n}\n\n/**\n * Authentication method types.\n * Used for method precedence selection.\n */\nexport type AuthMethodType = 'oauth2' | 'api-key';\n\n/**\n * Valid authentication method types for runtime validation.\n */\nexport const VALID_AUTH_METHOD_TYPES: readonly AuthMethodType[] = [\n 'oauth2',\n 'api-key',\n] as const;\n\n/**\n * Type guard to check if a value is a valid AuthMethodType.\n * @param value - The value to check\n * @returns True if the value is a valid AuthMethodType\n */\nexport function isValidAuthMethodType(value: unknown): value is AuthMethodType {\n return typeof value === 'string' && VALID_AUTH_METHOD_TYPES.includes(value as AuthMethodType);\n}\n\n/**\n * Configuration for authentication method precedence.\n *\n * Defines the order in which authentication methods are attempted.\n * Default precedence: oauth2 > api-key (OAuth preferred when available)\n *\n * Requirements: 3.1, 10.3\n */\nexport interface AuthMethodPrecedenceConfig {\n /**\n * Ordered list of authentication methods by preference.\n * First method in the list has highest priority.\n * Default: ['oauth2', 'api-key']\n */\n methodPrecedence: AuthMethodType[];\n\n /**\n * Whether to fail fast when an unsupported method is encountered.\n * If true, throws an error immediately.\n * If false, skips the unsupported method and tries the next one.\n * Default: true\n */\n failFastOnUnsupported: boolean;\n\n /**\n * Whether to fail fast when provider ID is ambiguous.\n * Ambiguity occurs when multiple providers could match an agent.\n * If true, throws an error immediately.\n * If false, uses the first matching provider.\n * Default: true\n */\n failFastOnAmbiguous: boolean;\n}\n\n/**\n * Default authentication method precedence configuration.\n *\n * OAuth2 is preferred over API keys when both are available.\n * This aligns with Requirement 10.3: prefer OAuth credentials.\n */\nexport const DEFAULT_AUTH_METHOD_PRECEDENCE: AuthMethodPrecedenceConfig = {\n methodPrecedence: ['oauth2', 'api-key'],\n failFastOnUnsupported: true,\n failFastOnAmbiguous: true,\n};\n\n/**\n * Extended launcher config with auth settings.\n */\nexport interface AuthConfig {\n /** Default auth timeout in seconds */\n authTimeoutSec: number;\n /** Proactive token refresh threshold in seconds */\n tokenRefreshThresholdSec: number;\n /** Preferred storage backend */\n preferredStorageBackend?: StorageBackendType;\n /**\n * Authentication method precedence configuration.\n * Controls which auth method is preferred when multiple are available.\n * Default: oauth2 > api-key\n */\n methodPrecedence?: Partial<AuthMethodPrecedenceConfig>;\n}\n\n// =============================================================================\n// ACP Protocol Types\n// =============================================================================\n\n/**\n * ACP protocol auth method advertisement.\n */\nexport interface AcpAuthMethod {\n id: string;\n type: 'oauth2' | 'api-key';\n providerId?: AuthProviderId;\n}\n\n// =============================================================================\n// Type Guards and Validation Functions\n// =============================================================================\n\n/**\n * Valid provider IDs for runtime validation.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n */\nexport const VALID_PROVIDER_IDS: readonly AuthProviderId[] = [\n 'github',\n 'google',\n 'cognito',\n 'azure',\n 'oidc',\n] as const;\n\n/**\n * Valid storage backend types for runtime validation.\n */\nexport const VALID_STORAGE_BACKENDS: readonly StorageBackendType[] = [\n 'keychain',\n 'encrypted-file',\n 'memory',\n] as const;\n\n/**\n * Valid token status values for runtime validation.\n */\nexport const VALID_TOKEN_STATUSES: readonly TokenStatus[] = [\n 'authenticated',\n 'expired',\n 'refresh-failed',\n 'not-configured',\n] as const;\n\n/**\n * Valid auth error codes for runtime validation.\n */\nexport const VALID_ERROR_CODES: readonly AuthErrorCode[] = [\n 'INVALID_STATE',\n 'TIMEOUT',\n 'NETWORK_ERROR',\n 'INVALID_CREDENTIALS',\n 'STORAGE_ERROR',\n 'PROVIDER_ERROR',\n 'UNSUPPORTED_PROVIDER',\n 'CALLBACK_ERROR',\n 'TOKEN_REFRESH_FAILED',\n 'HEADLESS_ENVIRONMENT',\n] as const;\n\n/**\n * Type guard to check if a value is a valid AuthProviderId.\n * @param value - The value to check\n * @returns True if the value is a valid AuthProviderId\n */\nexport function isValidProviderId(value: unknown): value is AuthProviderId {\n return typeof value === 'string' && VALID_PROVIDER_IDS.includes(value as AuthProviderId);\n}\n\n/**\n * Type guard to check if a value is a valid StorageBackendType.\n * @param value - The value to check\n * @returns True if the value is a valid StorageBackendType\n */\nexport function isValidStorageBackend(value: unknown): value is StorageBackendType {\n return typeof value === 'string' && VALID_STORAGE_BACKENDS.includes(value as StorageBackendType);\n}\n\n/**\n * Type guard to check if a value is a valid TokenStatus.\n * @param value - The value to check\n * @returns True if the value is a valid TokenStatus\n */\nexport function isValidTokenStatus(value: unknown): value is TokenStatus {\n return typeof value === 'string' && VALID_TOKEN_STATUSES.includes(value as TokenStatus);\n}\n\n/**\n * Type guard to check if a value is a valid AuthErrorCode.\n * @param value - The value to check\n * @returns True if the value is a valid AuthErrorCode\n */\nexport function isValidErrorCode(value: unknown): value is AuthErrorCode {\n return typeof value === 'string' && VALID_ERROR_CODES.includes(value as AuthErrorCode);\n}\n\n// =============================================================================\n// AuthMethod ID to Provider ID Mapping\n// =============================================================================\n\n/**\n * Explicit mapping from authMethod.id to AuthProviderId.\n *\n * This mapping table provides a secure, explicit translation from\n * ACP authMethod identifiers to internal provider IDs.\n *\n * SECURITY: No substring or heuristic matching is used.\n * Only exact matches in this table are accepted.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n *\n * Requirements: 7.1, 13.4\n */\nexport const AUTH_METHOD_ID_TO_PROVIDER_ID: Readonly<Record<string, AuthProviderId>> = {\n // OAuth2 method IDs\n 'oauth2-github': 'github',\n 'oauth2-google': 'google',\n 'oauth2-cognito': 'cognito',\n 'oauth2-azure': 'azure',\n 'oauth2-oidc': 'oidc',\n\n // Direct provider IDs (for backward compatibility)\n 'github': 'github',\n 'google': 'google',\n 'cognito': 'cognito',\n 'azure': 'azure',\n 'oidc': 'oidc',\n} as const;\n\n/**\n * Valid authMethod.id values for runtime validation.\n */\nexport const VALID_AUTH_METHOD_IDS: readonly string[] = Object.keys(AUTH_METHOD_ID_TO_PROVIDER_ID);\n\n/**\n * Error thrown when an unknown authMethod.id is encountered.\n *\n * Requirements: 13.4\n */\nexport class UnknownAuthMethodIdError extends Error {\n public readonly code = 'UNSUPPORTED_PROVIDER' as const;\n public readonly unknownMethodId: string;\n public readonly supportedMethodIds: readonly string[];\n public readonly supportedProviders: readonly AuthProviderId[];\n\n constructor(unknownMethodId: string) {\n const supportedMethodIds = VALID_AUTH_METHOD_IDS;\n const supportedProviders = VALID_PROVIDER_IDS;\n\n super(\n `Unknown authMethod.id: \"${unknownMethodId}\". ` +\n `Supported method IDs: ${supportedMethodIds.join(', ')}. ` +\n `Supported providers: ${supportedProviders.join(', ')}.`\n );\n\n this.name = 'UnknownAuthMethodIdError';\n this.unknownMethodId = unknownMethodId;\n this.supportedMethodIds = supportedMethodIds;\n this.supportedProviders = supportedProviders;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UnknownAuthMethodIdError);\n }\n }\n}\n\n/**\n * Type guard to check if a value is a valid authMethod.id.\n *\n * @param value - The value to check\n * @returns True if the value is a valid authMethod.id\n */\nexport function isValidAuthMethodId(value: unknown): value is string {\n return typeof value === 'string' && value in AUTH_METHOD_ID_TO_PROVIDER_ID;\n}\n\n/**\n * Resolves an authMethod.id to its corresponding AuthProviderId.\n *\n * This function uses explicit mapping only - no substring matching,\n * no heuristics, no fuzzy matching. This is a security requirement\n * to prevent provider confusion attacks.\n *\n * @param authMethodId - The authMethod.id to resolve\n * @returns The corresponding AuthProviderId\n * @throws UnknownAuthMethodIdError if the authMethod.id is not recognized\n *\n * Requirements: 7.1, 13.4\n *\n * @example\n * ```typescript\n * // Valid mappings\n * resolveAuthMethodIdToProviderId('oauth2-openai'); // returns 'openai'\n * resolveAuthMethodIdToProviderId('oauth2-github'); // returns 'github'\n * resolveAuthMethodIdToProviderId('github'); // returns 'github' (direct)\n *\n * // Invalid - throws UnknownAuthMethodIdError\n * resolveAuthMethodIdToProviderId('oauth2-unknown');\n * resolveAuthMethodIdToProviderId('openai-oauth2'); // wrong format\n * resolveAuthMethodIdToProviderId('OPENAI'); // case sensitive\n * ```\n */\nexport function resolveAuthMethodIdToProviderId(authMethodId: string): AuthProviderId {\n const providerId = AUTH_METHOD_ID_TO_PROVIDER_ID[authMethodId];\n\n if (providerId === undefined) {\n throw new UnknownAuthMethodIdError(authMethodId);\n }\n\n return providerId;\n}\n\n/**\n * Safely resolves an authMethod.id to its corresponding AuthProviderId.\n *\n * Unlike `resolveAuthMethodIdToProviderId`, this function returns null\n * instead of throwing an error for unknown method IDs.\n *\n * @param authMethodId - The authMethod.id to resolve\n * @returns The corresponding AuthProviderId, or null if not recognized\n *\n * Requirements: 7.1, 13.4\n */\nexport function tryResolveAuthMethodIdToProviderId(authMethodId: string): AuthProviderId | null {\n const providerId = AUTH_METHOD_ID_TO_PROVIDER_ID[authMethodId];\n return providerId ?? null;\n}\n", "/**\n * Logging utilities for the Registry Launcher.\n *\n * Provides structured logging to stderr with ISO 8601 timestamps and severity levels.\n * All logs go to stderr only - stdout is reserved for NDJSON protocol messages.\n *\n * @module log\n */\n\n/**\n * Log severity levels.\n */\nexport type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';\n\n/**\n * Log context identifier for the registry launcher.\n */\nconst LOG_CONTEXT = 'registry-launcher';\n\n/**\n * Format a log message with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n * @returns Formatted log line\n */\nfunction formatLogMessage(level: LogLevel, message: string, context: string = LOG_CONTEXT): string {\n const timestamp = new Date().toISOString();\n return `[${timestamp}] [${level}] [${context}] ${message}`;\n}\n\n/**\n * Log a message to stderr with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level (DEBUG, INFO, WARN, ERROR)\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n */\nexport function log(level: LogLevel, message: string, context?: string): void {\n const formatted = formatLogMessage(level, message, context);\n console.error(formatted);\n}\n\n/**\n * Log an agent spawn event.\n *\n * @param agentId - The agent identifier\n * @param command - The spawn command\n * @param args - The spawn command arguments\n */\nexport function logSpawn(agentId: string, command: string, args: string[]): void {\n const fullCommand = [command, ...args].join(' ');\n log('INFO', `Spawning agent \"${agentId}\": ${fullCommand}`);\n}\n\n/**\n * Log an agent exit event.\n *\n * @param agentId - The agent identifier\n * @param exitCode - The exit code (null if terminated by signal)\n * @param signal - The signal that terminated the process (null if exited normally)\n */\nexport function logExit(agentId: string, exitCode: number | null, signal?: string | null): void {\n if (signal) {\n log('INFO', `Agent \"${agentId}\" exited with signal ${signal}`);\n } else if (exitCode !== null) {\n log('INFO', `Agent \"${agentId}\" exited with code ${exitCode}`);\n } else {\n log('INFO', `Agent \"${agentId}\" exited`);\n }\n}\n\n/**\n * Log a backpressure warning when writing to an agent process fails.\n *\n * @param agentId - The agent identifier\n */\nexport function logBackpressure(agentId: string): void {\n log('WARN', `Write to agent \"${agentId}\" failed due to backpressure`);\n}\n\n/**\n * Log an error when an agent's stdin closes unexpectedly.\n *\n * @param agentId - The agent identifier\n * @param error - The error that occurred\n */\nexport function logStdinClosed(agentId: string, error?: Error): void {\n const errorMessage = error ? `: ${error.message}` : '';\n log('ERROR', `Agent \"${agentId}\" stdin closed unexpectedly${errorMessage}`);\n}\n\n/**\n * Convenience function for logging debug messages.\n *\n * @param message - Debug message\n * @param context - Optional context identifier\n */\nexport function logDebug(message: string, context?: string): void {\n log('DEBUG', message, context);\n}\n\n/**\n * Convenience function for logging info messages.\n *\n * @param message - Info message\n * @param context - Optional context identifier\n */\nexport function logInfo(message: string, context?: string): void {\n log('INFO', message, context);\n}\n\n/**\n * Convenience function for logging warning messages.\n *\n * @param message - Warning message\n * @param context - Optional context identifier\n */\nexport function logWarn(message: string, context?: string): void {\n log('WARN', message, context);\n}\n\n/**\n * Convenience function for logging error messages.\n *\n * @param message - Error message\n * @param context - Optional context identifier\n */\nexport function logError(message: string, context?: string): void {\n log('ERROR', message, context);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Interactive CLI setup flow for headless/manual credential configuration.\n *\n * Implements the terminal auth flow for configuring OAuth credentials\n * in headless environments without browser access.\n *\n * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6\n *\n * @module flows/terminal-auth-flow\n */\n\nimport * as readline from 'readline';\nimport type {\n AuthProviderId,\n AuthResult,\n StoredCredentials,\n ProviderEndpoints,\n} from '../types.js';\nimport { VALID_PROVIDER_IDS } from '../types.js';\nimport type { ICredentialStore } from '../storage/types.js';\n\n/**\n * Authentication mode selected by the user.\n * Requirements: 3.1, 4.2\n */\nexport type AuthenticationMode = 'browser-oauth' | 'manual-api-key';\n\n/**\n * Result indicating browser OAuth flow should be used.\n * This is returned when the user selects \"Browser OAuth\" mode.\n */\nexport interface BrowserOAuthResult {\n /** Indicates browser OAuth flow should be used */\n useBrowserOAuth: true;\n /** The selected provider ID */\n providerId: AuthProviderId;\n}\n\n/**\n * Result indicating manual credential flow completed.\n */\nexport interface ManualCredentialResult {\n /** Indicates manual credential flow was used */\n useBrowserOAuth: false;\n /** The authentication result from manual flow */\n authResult: AuthResult;\n}\n\n/**\n * Combined result type for terminal auth flow execution.\n */\nexport type TerminalAuthFlowResult = BrowserOAuthResult | ManualCredentialResult;\n\n/**\n * Provider display information for the selection menu.\n */\ninterface ProviderInfo {\n id: AuthProviderId;\n name: string;\n requiresClientSecret: boolean;\n requiresCustomEndpoints: boolean;\n /** Whether this provider supports simple API key authentication */\n supportsApiKey: boolean;\n /** Whether this provider supports browser-based OAuth flow */\n supportsOAuth: boolean;\n /** Label for the API key (e.g., \"API Key\", \"Personal Access Token\") */\n apiKeyLabel?: string;\n /** Environment variable name for the API key */\n apiKeyEnvVar?: string;\n}\n\n/**\n * Provider information for display and configuration.\n * Per ACP Registry spec: GitHub supports Personal Access Token alternative.\n * All providers support browser-based OAuth flow.\n *\n * Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys only.\n * OpenAI and Anthropic API key support is handled by the model-credentials module.\n *\n * Requirements: 7.1, 7b.1\n */\nconst PROVIDER_INFO: readonly ProviderInfo[] = [\n { id: 'github', name: 'GitHub', requiresClientSecret: true, requiresCustomEndpoints: false, supportsApiKey: true, supportsOAuth: true, apiKeyLabel: 'Personal Access Token', apiKeyEnvVar: 'GITHUB_TOKEN' },\n { id: 'google', name: 'Google', requiresClientSecret: true, requiresCustomEndpoints: false, supportsApiKey: false, supportsOAuth: true },\n { id: 'cognito', name: 'AWS Cognito', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n { id: 'azure', name: 'Microsoft Entra ID', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n { id: 'oidc', name: 'Generic OIDC', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n] as const;\n\n\n/**\n * Collected credentials from user input.\n */\nexport interface CollectedCredentials {\n clientId: string;\n clientSecret?: string;\n customEndpoints?: ProviderEndpoints;\n}\n\n/**\n * Dependencies for the terminal auth flow.\n */\nexport interface TerminalAuthFlowDependencies {\n /** Credential store for persisting credentials */\n credentialStore: ICredentialStore;\n /** Function to validate credentials (attempts token request) */\n validateCredentials: (\n providerId: AuthProviderId,\n credentials: CollectedCredentials\n ) => Promise<{ valid: boolean; error?: string; accessToken?: string }>;\n /** Optional custom input/output streams (for testing) */\n input?: NodeJS.ReadableStream;\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Terminal auth flow - interactive CLI setup.\n *\n * Provides an interactive terminal interface for configuring OAuth credentials\n * in headless environments. The flow:\n * 1. Prompts user to select a provider\n * 2. Prompts for required credentials\n * 3. Validates credentials by attempting a token request\n * 4. Stores credentials securely on success\n * 5. Prompts for re-entry on validation failure\n */\nexport class TerminalAuthFlow {\n private readonly credentialStore: ICredentialStore;\n private readonly validateCredentials: TerminalAuthFlowDependencies['validateCredentials'];\n private readonly input: NodeJS.ReadableStream;\n private readonly output: NodeJS.WritableStream;\n private rl: readline.Interface | null = null;\n\n /**\n * Create a new terminal auth flow.\n *\n * @param dependencies - Flow dependencies\n */\n constructor(dependencies: TerminalAuthFlowDependencies) {\n this.credentialStore = dependencies.credentialStore;\n this.validateCredentials = dependencies.validateCredentials;\n this.input = dependencies.input ?? process.stdin;\n this.output = dependencies.output ?? process.stderr;\n }\n\n /**\n * Execute the terminal auth flow.\n *\n * Runs the interactive setup wizard to configure OAuth credentials.\n * For providers supporting OAuth, offers a choice between browser OAuth\n * and manual API key entry.\n *\n * Requirements: 3.1, 4.2\n *\n * @param providerId - Optional pre-selected provider (skips provider selection)\n * @returns Terminal auth flow result indicating mode selection and outcome\n */\n async execute(providerId?: AuthProviderId): Promise<TerminalAuthFlowResult> {\n this.rl = readline.createInterface({\n input: this.input,\n output: this.output,\n });\n\n try {\n this.writeLine('\\n=== OAuth Authentication Setup ===\\n');\n\n // Step 1: Select provider (Requirement 4.2)\n const selectedProvider = providerId ?? await this.selectProvider();\n const providerInfo = PROVIDER_INFO.find(p => p.id === selectedProvider);\n\n if (!providerInfo) {\n return {\n useBrowserOAuth: false,\n authResult: {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${selectedProvider}' is not supported.`,\n details: { supportedProviders: VALID_PROVIDER_IDS },\n },\n },\n };\n }\n\n this.writeLine(`\\nConfiguring ${providerInfo.name}...\\n`);\n\n // Step 2: Select authentication mode (Requirement 3.1, 4.2)\n // For providers supporting OAuth, offer choice between browser OAuth and manual\n if (providerInfo.supportsOAuth) {\n const authMode = await this.selectAuthenticationMode(providerInfo);\n\n if (authMode === 'browser-oauth') {\n // Return indicator that browser OAuth flow should be used\n this.writeLine('\\nBrowser OAuth selected. Launching browser authentication flow...\\n');\n return {\n useBrowserOAuth: true,\n providerId: selectedProvider,\n };\n }\n }\n\n // Step 3-5: Collect and validate credentials with retry loop (manual flow)\n const result = await this.collectAndValidateWithRetry(selectedProvider, providerInfo);\n return {\n useBrowserOAuth: false,\n authResult: result,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[TerminalAuthFlow] Error: ${errorMessage}`);\n\n // Use the provided providerId or default to 'github' for error reporting\n // Note: OpenAI is NOT an OAuth provider - it uses API keys\n const errorProviderId = providerId || 'github';\n return {\n useBrowserOAuth: false,\n authResult: {\n success: false,\n providerId: errorProviderId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Terminal auth flow failed: ${errorMessage}`,\n },\n },\n };\n } finally {\n this.cleanup();\n }\n }\n\n /**\n * Select authentication mode for providers supporting OAuth.\n * Offers choice between browser OAuth (recommended) and manual API key.\n *\n * Requirements: 3.1, 4.2\n *\n * @param providerInfo - Provider information\n * @returns Selected authentication mode\n */\n private async selectAuthenticationMode(providerInfo: ProviderInfo): Promise<AuthenticationMode> {\n this.writeLine(`${providerInfo.name} supports multiple authentication methods:\\n`);\n this.writeLine(' 1. Browser OAuth (recommended) - Opens browser for secure authentication');\n this.writeLine(' 2. Manual API Key - Enter credentials directly in terminal\\n');\n\n const selection = await this.promptSelection('Select authentication method (1-2) [default: 1]: ', 1, 2, 1);\n\n return selection === 1 ? 'browser-oauth' : 'manual-api-key';\n }\n\n\n /**\n * Collect and validate credentials with retry loop.\n * Requirements: 4.3, 4.4, 4.5, 4.6\n *\n * Note: When this method is called, the user has already selected \"Manual API Key\"\n * in the authentication mode selection. For providers that support simple API key\n * authentication (OpenAI, Anthropic, GitHub), we collect the API key directly.\n * For providers that don't support simple API key (Google, Cognito, Azure),\n * we collect OAuth client credentials.\n */\n private async collectAndValidateWithRetry(\n selectedProvider: AuthProviderId,\n providerInfo: ProviderInfo\n ): Promise<AuthResult> {\n let credentials: CollectedCredentials | null = null;\n let validationResult: { valid: boolean; error?: string; accessToken?: string } | null = null;\n let attempts = 0;\n const maxAttempts = 3;\n\n // For providers that support simple API key, use API key mode directly\n // (user already selected \"Manual API Key\" in auth mode selection)\n const useApiKey = providerInfo.supportsApiKey;\n\n while (attempts < maxAttempts) {\n attempts++;\n\n // Collect credentials (Requirement 4.3)\n if (useApiKey) {\n credentials = await this.collectApiKeyCredentials(providerInfo);\n } else {\n credentials = await this.collectCredentials(providerInfo);\n }\n\n // Validate credentials (Requirement 4.4)\n this.writeLine('\\nValidating credentials...');\n validationResult = await this.validateCredentials(selectedProvider, credentials);\n\n if (validationResult.valid) {\n break;\n }\n\n // Display error and prompt for re-entry (Requirement 4.6)\n this.writeLine(`\\nValidation failed: ${validationResult.error || 'Unknown error'}`);\n\n if (attempts < maxAttempts) {\n const retry = await this.promptYesNo('Would you like to try again?');\n if (!retry) {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: 'Credential validation failed and user cancelled retry.',\n details: { validationError: validationResult.error },\n },\n };\n }\n this.writeLine('');\n }\n }\n\n if (!validationResult?.valid || !credentials) {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: `Credential validation failed after ${maxAttempts} attempts.`,\n details: { validationError: validationResult?.error },\n },\n };\n }\n\n // Enforce invariant: valid credentials must have a non-empty access token\n if (!validationResult.accessToken || validationResult.accessToken.trim() === '') {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: 'Credential validation succeeded but no access token was returned.',\n },\n };\n }\n\n // Step 5: Store credentials securely (Requirement 4.5)\n const storedCredentials: StoredCredentials = {\n providerId: selectedProvider,\n accessToken: validationResult.accessToken,\n clientId: credentials.clientId,\n clientSecret: credentials.clientSecret,\n customEndpoints: credentials.customEndpoints,\n storedAt: Date.now(),\n };\n\n await this.credentialStore.store(selectedProvider, storedCredentials);\n\n this.writeLine(`\\n${providerInfo.name} credentials configured successfully!\\n`);\n\n return {\n success: true,\n providerId: selectedProvider,\n };\n }\n\n /**\n * Prompt for a numeric selection within a range.\n * Supports an optional default value that is used when user presses Enter without input.\n *\n * @param message - The prompt message\n * @param min - Minimum valid selection\n * @param max - Maximum valid selection\n * @param defaultValue - Optional default value used when input is empty\n * @returns The selected number\n */\n private async promptSelection(message: string, min: number, max: number, defaultValue?: number): Promise<number> {\n while (true) {\n const input = await this.prompt(message);\n const trimmed = input.trim();\n\n // If input is empty and we have a default, use it\n if (trimmed === '' && defaultValue !== undefined) {\n return defaultValue;\n }\n\n const selection = parseInt(trimmed, 10);\n\n if (selection >= min && selection <= max) {\n return selection;\n }\n\n this.writeLine(`Invalid selection. Please enter a number between ${min} and ${max}.`);\n }\n }\n\n /**\n * Collect API key credentials (simple mode for OpenAI, Anthropic, GitHub).\n */\n private async collectApiKeyCredentials(providerInfo: ProviderInfo): Promise<CollectedCredentials> {\n const label = providerInfo.apiKeyLabel || 'API Key';\n const envVar = providerInfo.apiKeyEnvVar;\n\n if (envVar) {\n this.writeLine(`(You can also set this via ${envVar} environment variable)\\n`);\n }\n\n const apiKey = await this.promptSecret(`${label}: `);\n\n // For API key auth, we store the API key as the clientId\n // The actual token will be the API key itself\n return {\n clientId: apiKey,\n // No clientSecret needed for API key auth\n };\n }\n\n /**\n * Prompt user to select a provider from the supported list.\n * Requirement 4.2\n */\n private async selectProvider(): Promise<AuthProviderId> {\n this.writeLine('Select an OAuth provider:\\n');\n\n PROVIDER_INFO.forEach((provider, index) => {\n this.writeLine(` ${index + 1}. ${provider.name}`);\n });\n\n this.writeLine('');\n\n while (true) {\n const input = await this.prompt(`Enter selection (1-${PROVIDER_INFO.length}): `);\n const selection = parseInt(input.trim(), 10);\n\n if (selection >= 1 && selection <= PROVIDER_INFO.length) {\n return PROVIDER_INFO[selection - 1].id;\n }\n\n this.writeLine(`Invalid selection. Please enter a number between 1 and ${PROVIDER_INFO.length}.`);\n }\n }\n\n\n /**\n * Collect credentials from user input.\n * Requirement 4.3\n */\n private async collectCredentials(providerInfo: ProviderInfo): Promise<CollectedCredentials> {\n const credentials: CollectedCredentials = {\n clientId: '',\n };\n\n // Always prompt for client ID\n credentials.clientId = await this.promptRequired('Client ID: ');\n\n // Prompt for client secret if required\n if (providerInfo.requiresClientSecret) {\n credentials.clientSecret = await this.promptSecret('Client Secret: ');\n }\n\n // Prompt for custom endpoints if required (Cognito/Azure)\n if (providerInfo.requiresCustomEndpoints) {\n credentials.customEndpoints = await this.collectCustomEndpoints(providerInfo);\n }\n\n return credentials;\n }\n\n /**\n * Collect custom endpoints for providers that require them (Cognito/Azure/OIDC).\n * Validates all endpoints to ensure HTTPS and no embedded credentials.\n */\n private async collectCustomEndpoints(providerInfo: ProviderInfo): Promise<ProviderEndpoints> {\n this.writeLine(`\\n${providerInfo.name} requires custom endpoint configuration:\\n`);\n\n if (providerInfo.id === 'cognito') {\n return this.collectCognitoEndpoints();\n } else if (providerInfo.id === 'azure') {\n return this.collectAzureEndpoints();\n } else if (providerInfo.id === 'oidc') {\n return this.collectOidcEndpoints();\n }\n\n // Generic custom endpoints with HTTPS validation\n const authEndpoint = await this.promptValidatedUrl('Authorization Endpoint URL: ');\n const tokenEndpoint = await this.promptValidatedUrl('Token Endpoint URL: ');\n\n return {\n authorizationEndpoint: authEndpoint,\n tokenEndpoint: tokenEndpoint,\n };\n }\n\n /**\n * Prompt for a validated HTTPS URL.\n * Ensures the URL is valid, uses HTTPS, and has no embedded credentials.\n */\n private async promptValidatedUrl(message: string): Promise<string> {\n while (true) {\n const input = await this.promptRequired(message);\n const error = this.validateHttpsUrl(input);\n if (error === null) {\n return input;\n }\n this.writeLine(`Error: ${error}`);\n }\n }\n\n /**\n * Validate that a URL is a valid HTTPS URL without embedded credentials.\n */\n private validateHttpsUrl(value: string): string | null {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return 'Invalid URL format.';\n }\n if (url.protocol !== 'https:') {\n return 'URL must use HTTPS protocol for security.';\n }\n if (url.username || url.password) {\n return 'URL must not contain embedded credentials.';\n }\n return null;\n }\n\n /**\n * Collect Cognito-specific endpoint configuration.\n * Validates input to prevent URL injection attacks.\n */\n private async collectCognitoEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your Cognito User Pool details:\\n');\n\n const userPoolDomain = await this.promptValidated(\n 'User Pool Domain (e.g., my-app): ',\n this.validateCognitoDomain.bind(this)\n );\n const region = await this.promptValidated(\n 'AWS Region (e.g., us-east-1): ',\n this.validateAwsRegion.bind(this)\n );\n\n const baseUrl = `https://${userPoolDomain}.auth.${region}.amazoncognito.com`;\n\n return {\n authorizationEndpoint: `${baseUrl}/oauth2/authorize`,\n tokenEndpoint: `${baseUrl}/oauth2/token`,\n };\n }\n\n /**\n * Collect Azure AD-specific endpoint configuration.\n * Validates input to prevent URL injection attacks.\n */\n private async collectAzureEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your Microsoft Entra ID details:\\n');\n\n const tenantId = await this.promptValidated(\n 'Tenant ID (or \"common\" for multi-tenant): ',\n this.validateAzureTenantId.bind(this)\n );\n\n const baseUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0`;\n\n return {\n authorizationEndpoint: `${baseUrl}/authorize`,\n tokenEndpoint: `${baseUrl}/token`,\n };\n }\n\n /**\n * Collect Generic OIDC endpoint configuration.\n * Supports issuer-based discovery or manual endpoint entry.\n * Validates input to prevent URL injection attacks.\n *\n * Requirements: 7a.1, 7a.2\n */\n private async collectOidcEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your OIDC provider details:\\n');\n this.writeLine('You can provide an issuer URL for automatic discovery,');\n this.writeLine('or manually enter the authorization and token endpoints.\\n');\n\n const useDiscovery = await this.promptYesNo('Use OIDC Discovery (recommended)?');\n\n if (useDiscovery) {\n const issuerUrl = await this.promptValidatedUrl('Issuer URL (e.g., https://auth.example.com): ');\n\n // Build discovery URL from issuer\n const discoveryUrl = issuerUrl.endsWith('/')\n ? `${issuerUrl}.well-known/openid-configuration`\n : `${issuerUrl}/.well-known/openid-configuration`;\n\n this.writeLine(`\\nOIDC Discovery URL: ${discoveryUrl}`);\n this.writeLine('The authorization and token endpoints will be fetched automatically.\\n');\n\n // For OIDC with discovery, we store the issuer URL\n // The actual endpoints will be discovered at runtime\n return {\n authorizationEndpoint: issuerUrl, // Store issuer URL, discovery happens at runtime\n tokenEndpoint: discoveryUrl, // Store discovery URL for reference\n };\n }\n\n // Manual endpoint entry\n this.writeLine('\\nEnter the endpoints manually:\\n');\n const authEndpoint = await this.promptValidatedUrl('Authorization Endpoint URL: ');\n const tokenEndpoint = await this.promptValidatedUrl('Token Endpoint URL: ');\n\n return {\n authorizationEndpoint: authEndpoint,\n tokenEndpoint: tokenEndpoint,\n };\n }\n\n /**\n * Validate Cognito user pool domain.\n * Must be alphanumeric with hyphens, no URL injection characters.\n */\n private validateCognitoDomain(value: string): string | null {\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(value)) {\n return 'Invalid domain format. Must be alphanumeric with hyphens, no leading/trailing hyphens.';\n }\n if (value.length > 63) {\n return 'Domain must be 63 characters or less.';\n }\n if (/[/:?#@\\s]/.test(value)) {\n return 'Domain contains invalid characters (/, :, ?, #, @, or whitespace).';\n }\n return null;\n }\n\n /**\n * Validate AWS region format.\n * Must match pattern like us-east-1, eu-west-2.\n */\n private validateAwsRegion(value: string): string | null {\n if (!/^[a-z]{2}-[a-z]+-\\d+$/.test(value)) {\n return 'Invalid AWS region format. Expected format: us-east-1, eu-west-2, etc.';\n }\n return null;\n }\n\n /**\n * Validate Azure tenant ID.\n * Must be 'common', 'organizations', 'consumers', a valid GUID, or a domain name.\n */\n private validateAzureTenantId(value: string): string | null {\n const wellKnown = ['common', 'organizations', 'consumers'];\n if (wellKnown.includes(value.toLowerCase())) {\n return null;\n }\n // GUID pattern\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {\n return null;\n }\n // Domain pattern (no URL injection chars)\n if (/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(value)) {\n return null;\n }\n if (/[/:?#@\\s]/.test(value)) {\n return 'Tenant ID contains invalid characters (/, :, ?, #, @, or whitespace).';\n }\n return \"Invalid tenant ID. Must be 'common', 'organizations', 'consumers', a valid GUID, or a domain name.\";\n }\n\n /**\n * Prompt for input with validation.\n */\n private async promptValidated(\n message: string,\n validator: (value: string) => string | null\n ): Promise<string> {\n while (true) {\n const input = await this.promptRequired(message);\n const error = validator(input);\n if (error === null) {\n return input;\n }\n this.writeLine(`Error: ${error}`);\n }\n }\n\n\n /**\n * Prompt for required input (non-empty).\n */\n private async promptRequired(message: string): Promise<string> {\n while (true) {\n const input = await this.prompt(message);\n const trimmed = input.trim();\n\n if (trimmed.length > 0) {\n return trimmed;\n }\n\n this.writeLine('This field is required. Please enter a value.');\n }\n }\n\n /**\n * Prompt for secret input (hidden if possible).\n * Note: In a real implementation, this would hide input.\n * For headless environments, we accept visible input.\n */\n private async promptSecret(message: string): Promise<string> {\n // In headless environments, we can't easily hide input\n // The user should be aware they're entering sensitive data\n this.writeLine('(Note: Input will be visible in terminal)');\n return this.promptRequired(message);\n }\n\n /**\n * Prompt for yes/no confirmation.\n */\n private async promptYesNo(message: string): Promise<boolean> {\n while (true) {\n const input = await this.prompt(`${message} (y/n): `);\n const normalized = input.trim().toLowerCase();\n\n if (normalized === 'y' || normalized === 'yes') {\n return true;\n }\n if (normalized === 'n' || normalized === 'no') {\n return false;\n }\n\n this.writeLine('Please enter \"y\" or \"n\".');\n }\n }\n\n /**\n * Prompt for user input.\n */\n private prompt(message: string): Promise<string> {\n return new Promise((resolve) => {\n if (!this.rl) {\n resolve('');\n return;\n }\n\n this.rl.question(message, (answer) => {\n resolve(answer);\n });\n });\n }\n\n /**\n * Write a line to output.\n */\n private writeLine(message: string): void {\n this.output.write(message + '\\n');\n }\n\n /**\n * Clean up resources.\n */\n private cleanup(): void {\n if (this.rl) {\n this.rl.close();\n this.rl = null;\n }\n }\n}\n\n/**\n * Create a terminal auth flow with the given dependencies.\n *\n * @param dependencies - Flow dependencies\n * @returns A new TerminalAuthFlow instance\n */\nexport function createTerminalAuthFlow(\n dependencies: TerminalAuthFlowDependencies\n): TerminalAuthFlow {\n return new TerminalAuthFlow(dependencies);\n}\n\n/**\n * Get provider information by ID.\n *\n * @param providerId - The provider identifier\n * @returns Provider info or undefined if not found\n */\nexport function getProviderInfo(providerId: AuthProviderId): ProviderInfo | undefined {\n return PROVIDER_INFO.find(p => p.id === providerId);\n}\n\n/**\n * Get all supported provider information.\n *\n * @returns Array of provider information\n */\nexport function getAllProviderInfo(): readonly ProviderInfo[] {\n return PROVIDER_INFO;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Auth session management for OAuth 2.1 authorization flows.\n *\n * Tracks in-progress OAuth authorization sessions including PKCE parameters,\n * state for CSRF protection, and timeout handling.\n *\n * @module session\n */\n\nimport { randomUUID } from 'crypto';\nimport type { AuthProviderId } from './types.js';\nimport { validateState, generateState } from './state.js';\nimport { generatePKCEPair } from './pkce.js';\n\n/**\n * Represents an in-progress OAuth authorization flow.\n */\nexport interface IAuthSession {\n /** Unique session identifier */\n readonly sessionId: string;\n\n /** Provider being authenticated */\n readonly providerId: AuthProviderId;\n\n /** PKCE code verifier (kept secret) */\n readonly codeVerifier: string;\n\n /** PKCE code challenge (sent to provider) */\n readonly codeChallenge: string;\n\n /** State parameter for CSRF protection */\n readonly state: string;\n\n /** Session start timestamp */\n readonly startedAt: number;\n\n /** Session timeout in milliseconds */\n readonly timeoutMs: number;\n\n /** Check if session has expired */\n isExpired(): boolean;\n\n /** Get remaining time in milliseconds */\n remainingTime(): number;\n\n /** Validate returned state parameter */\n validateState(returnedState: string): boolean;\n}\n\n/**\n * Default session timeout in milliseconds (5 minutes).\n */\nexport const DEFAULT_SESSION_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Maximum allowed session timeout in milliseconds (1 hour).\n */\nexport const MAX_SESSION_TIMEOUT_MS = 60 * 60 * 1000;\n\n/**\n * Validate and normalize a timeout value.\n *\n * Ensures the timeout is a finite positive number within allowed bounds.\n * Returns the default timeout for invalid values (NaN, Infinity, negative, zero).\n *\n * @param timeoutMs - The timeout value to validate\n * @returns A valid timeout value within bounds\n */\nexport function validateTimeout(timeoutMs: number): number {\n // Check for NaN, Infinity, or non-finite values\n if (!Number.isFinite(timeoutMs)) {\n return DEFAULT_SESSION_TIMEOUT_MS;\n }\n\n // Check for non-positive values\n if (timeoutMs <= 0) {\n return DEFAULT_SESSION_TIMEOUT_MS;\n }\n\n // Clamp to maximum allowed\n if (timeoutMs > MAX_SESSION_TIMEOUT_MS) {\n return MAX_SESSION_TIMEOUT_MS;\n }\n\n // Round to integer (floor to avoid extending timeout)\n return Math.floor(timeoutMs);\n}\n\n/**\n * Represents an in-progress OAuth authorization flow.\n *\n * Implements the IAuthSession interface from the design document.\n * Tracks all PKCE and state parameters needed for a secure OAuth 2.1 flow.\n */\nexport class AuthSession implements IAuthSession {\n /** Unique session identifier */\n readonly sessionId: string;\n\n /** Provider being authenticated */\n readonly providerId: AuthProviderId;\n\n /** PKCE code verifier (kept secret) */\n readonly codeVerifier: string;\n\n /** PKCE code challenge (sent to provider) */\n readonly codeChallenge: string;\n\n /** State parameter for CSRF protection */\n readonly state: string;\n\n /** Session start timestamp (Unix milliseconds) */\n readonly startedAt: number;\n\n /** Session timeout in milliseconds */\n readonly timeoutMs: number;\n\n /**\n * Create a new auth session.\n *\n * @param providerId - The OAuth provider being authenticated\n * @param codeVerifier - PKCE code verifier (kept secret)\n * @param codeChallenge - PKCE code challenge (sent to provider)\n * @param state - State parameter for CSRF protection\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n */\n constructor(\n providerId: AuthProviderId,\n codeVerifier: string,\n codeChallenge: string,\n state: string,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n ) {\n this.sessionId = randomUUID();\n this.providerId = providerId;\n this.codeVerifier = codeVerifier;\n this.codeChallenge = codeChallenge;\n this.state = state;\n this.startedAt = Date.now();\n this.timeoutMs = validateTimeout(timeoutMs);\n }\n\n /**\n * Check if the session has expired.\n *\n * A session is expired if the current time exceeds startedAt + timeoutMs.\n *\n * @returns True if the session has expired, false otherwise\n */\n isExpired(): boolean {\n return this.remainingTime() <= 0;\n }\n\n /**\n * Get the remaining time until session expiration.\n *\n * Returns the number of milliseconds until the session expires.\n * Returns 0 if the session has already expired.\n *\n * @returns Remaining time in milliseconds (0 if expired)\n */\n remainingTime(): number {\n const elapsed = Date.now() - this.startedAt;\n const remaining = this.timeoutMs - elapsed;\n return Math.max(0, remaining);\n }\n\n /**\n * Validate a returned state parameter against this session's state.\n *\n * Uses constant-time comparison via the validateState function\n * to prevent timing attacks.\n *\n * @param returnedState - The state parameter from the OAuth callback\n * @returns True if the state matches, false otherwise\n */\n validateState(returnedState: string): boolean {\n return validateState(this.state, returnedState);\n }\n}\n\n/**\n * Factory function to create a new auth session.\n *\n * Generates PKCE parameters and state, then creates a new AuthSession.\n * This is a convenience function that handles all the cryptographic\n * parameter generation.\n *\n * @param providerId - The OAuth provider to authenticate with\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n * @returns A new AuthSession with generated PKCE and state parameters\n */\nexport function createSession(\n providerId: AuthProviderId,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n): AuthSession {\n const { verifier, challenge } = generatePKCEPair();\n const state = generateState();\n\n return new AuthSession(providerId, verifier, challenge, state, validateTimeout(timeoutMs));\n}\n\n/**\n * Session manager for tracking and cleaning up OAuth authorization sessions.\n *\n * Provides centralized management of active auth sessions including:\n * - Session storage and retrieval by session ID or state parameter\n * - Automatic cleanup of expired sessions\n * - Session lifecycle management (create, get, remove, list)\n *\n * The manager uses a configurable cleanup interval to periodically remove\n * expired sessions, preventing memory leaks in long-running processes.\n */\nexport class SessionManager {\n /** Map of session ID to AuthSession */\n private readonly sessions: Map<string, AuthSession> = new Map();\n\n /** Map of state parameter to session ID for quick lookup */\n private readonly stateToSessionId: Map<string, string> = new Map();\n\n /** Cleanup interval timer reference */\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n /** Default cleanup interval in milliseconds (1 minute) */\n static readonly DEFAULT_CLEANUP_INTERVAL_MS = 60 * 1000;\n\n /**\n * Create a new SessionManager.\n *\n * @param cleanupIntervalMs - Interval for automatic cleanup (default: 1 minute)\n * @param autoStartCleanup - Whether to start automatic cleanup immediately (default: true)\n */\n constructor(\n private readonly cleanupIntervalMs: number = SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,\n autoStartCleanup: boolean = true,\n ) {\n // Validate cleanup interval - use default for invalid values\n if (!Number.isFinite(this.cleanupIntervalMs) || this.cleanupIntervalMs <= 0) {\n // TypeScript doesn't allow reassigning readonly in constructor after initial assignment,\n // so we use Object.defineProperty to override\n Object.defineProperty(this, 'cleanupIntervalMs', {\n value: SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,\n writable: false,\n });\n }\n\n if (autoStartCleanup) {\n this.startCleanup();\n }\n }\n\n /**\n * Create and register a new auth session.\n *\n * Generates PKCE parameters and state, creates a new AuthSession,\n * and registers it with the manager for tracking.\n *\n * @param providerId - The OAuth provider to authenticate with\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n * @returns The newly created and registered AuthSession\n */\n create(\n providerId: AuthProviderId,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n ): AuthSession {\n const session = createSession(providerId, validateTimeout(timeoutMs));\n this.sessions.set(session.sessionId, session);\n this.stateToSessionId.set(session.state, session.sessionId);\n return session;\n }\n\n /**\n * Get a session by its session ID.\n *\n * @param sessionId - The unique session identifier\n * @returns The session if found and not expired, undefined otherwise\n */\n get(sessionId: string): AuthSession | undefined {\n const session = this.sessions.get(sessionId);\n if (session && session.isExpired()) {\n this.remove(sessionId);\n return undefined;\n }\n return session;\n }\n\n /**\n * Get a session by its state parameter.\n *\n * Useful for looking up sessions during OAuth callback handling.\n *\n * @param state - The state parameter from the OAuth callback\n * @returns The session if found and not expired, undefined otherwise\n */\n getByState(state: string): AuthSession | undefined {\n const sessionId = this.stateToSessionId.get(state);\n if (!sessionId) {\n return undefined;\n }\n return this.get(sessionId);\n }\n\n /**\n * Remove a session by its session ID.\n *\n * Cleans up both the session and its state parameter mapping.\n *\n * @param sessionId - The unique session identifier\n * @returns True if the session was removed, false if it didn't exist\n */\n remove(sessionId: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return false;\n }\n\n this.stateToSessionId.delete(session.state);\n this.sessions.delete(sessionId);\n return true;\n }\n\n /**\n * Remove a session by its state parameter.\n *\n * @param state - The state parameter\n * @returns True if the session was removed, false if it didn't exist\n */\n removeByState(state: string): boolean {\n const sessionId = this.stateToSessionId.get(state);\n if (!sessionId) {\n return false;\n }\n return this.remove(sessionId);\n }\n\n /**\n * List all active (non-expired) sessions.\n *\n * This method also performs cleanup of any expired sessions found.\n *\n * @returns Array of active AuthSession objects\n */\n list(): AuthSession[] {\n const activeSessions: AuthSession[] = [];\n const expiredSessionIds: string[] = [];\n\n for (const [sessionId, session] of this.sessions) {\n if (session.isExpired()) {\n expiredSessionIds.push(sessionId);\n } else {\n activeSessions.push(session);\n }\n }\n\n // Clean up expired sessions found during listing\n for (const sessionId of expiredSessionIds) {\n this.remove(sessionId);\n }\n\n return activeSessions;\n }\n\n /**\n * Get the count of active sessions.\n *\n * Note: This may include sessions that have expired but not yet been cleaned up.\n * Use list().length for an accurate count of non-expired sessions.\n *\n * @returns The number of tracked sessions\n */\n size(): number {\n return this.sessions.size;\n }\n\n /**\n * Check if a session exists by session ID.\n *\n * @param sessionId - The unique session identifier\n * @returns True if the session exists and is not expired\n */\n has(sessionId: string): boolean {\n return this.get(sessionId) !== undefined;\n }\n\n /**\n * Check if a session exists by state parameter.\n *\n * @param state - The state parameter\n * @returns True if a session with this state exists and is not expired\n */\n hasByState(state: string): boolean {\n return this.getByState(state) !== undefined;\n }\n\n /**\n * Remove all expired sessions.\n *\n * This is called automatically by the cleanup timer, but can also\n * be called manually to force immediate cleanup.\n *\n * @returns The number of expired sessions that were removed\n */\n cleanup(): number {\n const expiredSessionIds: string[] = [];\n\n for (const [sessionId, session] of this.sessions) {\n if (session.isExpired()) {\n expiredSessionIds.push(sessionId);\n }\n }\n\n for (const sessionId of expiredSessionIds) {\n this.remove(sessionId);\n }\n\n return expiredSessionIds.length;\n }\n\n /**\n * Start the automatic cleanup timer.\n *\n * If cleanup is already running, this method does nothing.\n */\n startCleanup(): void {\n if (this.cleanupTimer !== null) {\n return;\n }\n\n this.cleanupTimer = setInterval(() => {\n this.cleanup();\n }, this.cleanupIntervalMs);\n\n // Ensure the timer doesn't prevent Node.js from exiting\n if (this.cleanupTimer.unref) {\n this.cleanupTimer.unref();\n }\n }\n\n /**\n * Stop the automatic cleanup timer.\n *\n * Call this method when shutting down to clean up resources.\n */\n stopCleanup(): void {\n if (this.cleanupTimer !== null) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n /**\n * Clear all sessions and stop cleanup.\n *\n * Use this for cleanup during shutdown or testing.\n */\n clear(): void {\n this.stopCleanup();\n this.sessions.clear();\n this.stateToSessionId.clear();\n }\n\n /**\n * Check if automatic cleanup is running.\n *\n * @returns True if the cleanup timer is active\n */\n isCleanupRunning(): boolean {\n return this.cleanupTimer !== null;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * State parameter generation and validation for OAuth 2.1 CSRF protection.\n *\n * @module state\n */\n\nimport { randomBytes, timingSafeEqual } from 'crypto';\n\n/**\n * Minimum number of random bytes for state parameter.\n * 32 bytes provides 256 bits of entropy for CSRF protection.\n */\nexport const STATE_MIN_BYTES = 32;\n\n/**\n * Generate a cryptographically secure state parameter.\n *\n * Generates at least 32 bytes of cryptographic randomness using\n * Node.js crypto.randomBytes, then encodes as base64url without padding.\n *\n * @returns Base64url-encoded random bytes (at least 32 bytes)\n */\nexport function generateState(): string {\n // Generate 32 cryptographically random bytes\n const randomBuffer = randomBytes(STATE_MIN_BYTES);\n\n // Convert to base64url encoding without padding\n // base64url: replace + with -, / with _, remove = padding\n return randomBuffer\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Validate a returned state parameter against the expected value.\n *\n * Uses constant-time comparison to prevent timing attacks.\n * Returns false for missing, empty, or mismatched state parameters.\n * Never throws exceptions - always returns boolean.\n *\n * @param expected - The original state parameter\n * @param received - The state parameter from the callback\n * @returns True if the states match exactly, false otherwise\n */\nexport function validateState(expected: string | null | undefined, received: string | null | undefined): boolean {\n // Return false for missing or empty state parameters\n if (!expected || !received) {\n return false;\n }\n\n // Convert strings to Buffers first to compare byte lengths\n // This handles Unicode characters correctly\n const expectedBuffer = Buffer.from(expected, 'utf8');\n const receivedBuffer = Buffer.from(received, 'utf8');\n\n // Return false if byte lengths don't match\n // This is safe to do before timingSafeEqual since length comparison\n // doesn't leak information about the content\n if (expectedBuffer.length !== receivedBuffer.length) {\n return false;\n }\n\n // Use constant-time comparison to prevent timing attacks\n try {\n return timingSafeEqual(expectedBuffer, receivedBuffer);\n } catch {\n // Safety net: if timingSafeEqual throws for any reason, return false\n // This should never happen with the byte length check above, but\n // provides defense in depth\n return false;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * PKCE (Proof Key for Code Exchange) implementation.\n *\n * Provides code verifier and challenge generation for OAuth 2.1 PKCE flow.\n * Implements RFC 7636 with S256 challenge method as required by OAuth 2.1.\n *\n * @module pkce\n */\n\nimport { randomBytes, createHash } from 'crypto';\n\n/**\n * Minimum length for PKCE code verifier per RFC 7636.\n */\nexport const PKCE_VERIFIER_MIN_LENGTH = 43;\n\n/**\n * Maximum length for PKCE code verifier per RFC 7636.\n */\nexport const PKCE_VERIFIER_MAX_LENGTH = 128;\n\n/**\n * Default length for PKCE code verifier.\n * Using 64 characters provides good entropy while staying well within limits.\n */\nconst DEFAULT_VERIFIER_LENGTH = 64;\n\n/**\n * PKCE code challenge method.\n * OAuth 2.1 requires S256 (SHA-256) method.\n */\nexport const PKCE_CODE_CHALLENGE_METHOD = 'S256' as const;\n\n/**\n * Unreserved URI characters allowed in PKCE code verifier per RFC 7636.\n * Characters: A-Z, a-z, 0-9, hyphen (-), period (.), underscore (_), tilde (~)\n */\nconst UNRESERVED_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\n/**\n * Regex pattern for validating PKCE code verifier format.\n * Only unreserved URI characters are allowed per RFC 7636.\n */\nconst UNRESERVED_CHARS_REGEX = /^[A-Za-z0-9\\-._~]+$/;\n\n/**\n * Generate a cryptographically secure PKCE code verifier.\n *\n * The verifier is generated using crypto.randomBytes for cryptographic randomness,\n * then encoded using only unreserved URI characters as specified in RFC 7636.\n * Uses rejection sampling to avoid modulo bias.\n *\n * @param length - Optional length of the verifier (default: 64, must be 43-128)\n * @returns A random string between 43-128 characters using unreserved URI characters\n * @throws Error if length is outside the valid range (43-128) or not a valid integer\n */\nexport function generateCodeVerifier(length: number = DEFAULT_VERIFIER_LENGTH): string {\n // Validate length is a valid integer\n if (!Number.isInteger(length) || !Number.isFinite(length)) {\n throw new Error(\n `PKCE code verifier length must be a valid integer, got ${length}`,\n );\n }\n\n if (length < PKCE_VERIFIER_MIN_LENGTH || length > PKCE_VERIFIER_MAX_LENGTH) {\n throw new Error(\n `PKCE code verifier length must be between ${PKCE_VERIFIER_MIN_LENGTH} and ${PKCE_VERIFIER_MAX_LENGTH}, got ${length}`,\n );\n }\n\n const charsetLength = UNRESERVED_CHARS.length; // 66 characters\n\n // Calculate the largest multiple of charsetLength that fits in a byte (256)\n // This is used for rejection sampling to avoid modulo bias\n // For 66 chars: 256 - (256 % 66) = 256 - 58 = 198\n const maxValidByte = 256 - (256 % charsetLength);\n\n let verifier = '';\n let bytesNeeded = length;\n\n while (verifier.length < length) {\n // Generate more random bytes than needed to account for rejections\n // On average, we reject about 22.6% of bytes (58/256), so request ~30% extra\n const randomBuffer = randomBytes(Math.ceil(bytesNeeded * 1.4));\n\n for (let i = 0; i < randomBuffer.length && verifier.length < length; i++) {\n const byte = randomBuffer[i];\n\n // Rejection sampling: only use bytes that don't cause modulo bias\n if (byte < maxValidByte) {\n verifier += UNRESERVED_CHARS[byte % charsetLength];\n }\n }\n\n bytesNeeded = length - verifier.length;\n }\n\n return verifier;\n}\n\n/**\n * Validate a PKCE code verifier format.\n *\n * Checks that the verifier meets RFC 7636 requirements:\n * - Length between 43 and 128 characters\n * - Contains only unreserved URI characters\n *\n * @param verifier - The code verifier to validate\n * @returns True if the verifier is valid, false otherwise\n */\nexport function validateCodeVerifier(verifier: string): boolean {\n if (typeof verifier !== 'string') {\n return false;\n }\n\n if (verifier.length < PKCE_VERIFIER_MIN_LENGTH || verifier.length > PKCE_VERIFIER_MAX_LENGTH) {\n return false;\n }\n\n return UNRESERVED_CHARS_REGEX.test(verifier);\n}\n\n/**\n * Generate a PKCE code challenge from a code verifier.\n *\n * Computes the SHA-256 hash of the verifier and encodes it as base64url\n * without padding, as required by RFC 7636 S256 method.\n *\n * @param verifier - The code verifier to hash\n * @param strict - If true, validates verifier format (default: false for backward compatibility)\n * @returns Base64url-encoded SHA-256 hash of the verifier (without padding)\n * @throws Error if strict mode is enabled and verifier format is invalid\n */\nexport function generateCodeChallenge(verifier: string, strict: boolean = false): string {\n if (strict && !validateCodeVerifier(verifier)) {\n throw new Error(\n `Invalid PKCE code verifier format. Must be ${PKCE_VERIFIER_MIN_LENGTH}-${PKCE_VERIFIER_MAX_LENGTH} characters using only unreserved URI characters.`,\n );\n }\n\n // Compute SHA-256 hash of the verifier\n const hash = createHash('sha256').update(verifier, 'ascii').digest();\n\n // Convert to base64url encoding without padding\n // base64url: replace + with -, / with _, remove = padding\n return hash\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a PKCE pair (verifier and challenge).\n *\n * Creates a cryptographically secure code verifier and computes\n * the corresponding S256 code challenge.\n *\n * @param length - Optional length of the verifier (default: 64, must be 43-128)\n * @returns Object containing both the verifier and challenge\n */\nexport function generatePKCEPair(length?: number): { verifier: string; challenge: string } {\n const verifier = generateCodeVerifier(length);\n const challenge = generateCodeChallenge(verifier);\n\n return { verifier, challenge };\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Loopback HTTP server for OAuth callbacks.\n *\n * @module flows/callback-server\n */\n\nimport * as http from 'node:http';\nimport { URL } from 'node:url';\nimport type { CallbackResult } from '../types.js';\n\n/**\n * Loopback HTTP server for OAuth callbacks.\n */\nexport interface ICallbackServer {\n /** Start the server and return the redirect URI */\n start(): Promise<string>;\n\n /** Wait for the authorization callback */\n waitForCallback(timeoutMs: number): Promise<CallbackResult>;\n\n /** Stop the server and clean up resources */\n stop(): Promise<void>;\n\n /** Get the current server port (0 if not started) */\n getPort(): number;\n\n /** Check if server is running */\n isRunning(): boolean;\n}\n\n/**\n * Default callback path for OAuth redirects.\n */\nconst DEFAULT_CALLBACK_PATH = '/callback';\n\n/**\n * Loopback address for binding the server (IPv4).\n */\nconst LOOPBACK_HOST_IPV4 = '127.0.0.1';\n\n/**\n * Maximum URL length to prevent memory abuse (8KB).\n */\nconst MAX_URL_LENGTH = 8192;\n\n/**\n * Maximum header size to prevent memory abuse (8KB).\n */\nconst MAX_HEADER_SIZE = 8192;\n\n/**\n * Security response headers for browser-facing responses.\n */\nconst SECURITY_HEADERS: Record<string, string> = {\n 'Cache-Control': 'no-store',\n 'Pragma': 'no-cache',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Content-Security-Policy': \"default-src 'none'; style-src 'unsafe-inline'\",\n};\n\n/**\n * Check if an address is a loopback address.\n * Supports both IPv4 (127.x.x.x) and IPv6 (::1) loopback addresses.\n *\n * @param address - The IP address to check\n * @returns True if the address is a loopback address\n */\nexport function isLoopbackAddress(address: string | undefined): boolean {\n if (!address) {\n return false;\n }\n\n // IPv4 loopback: 127.0.0.0/8 (any address starting with 127.)\n if (address.startsWith('127.')) {\n return true;\n }\n\n // IPv6 loopback: ::1\n if (address === '::1') {\n return true;\n }\n\n // IPv4-mapped IPv6 loopback: ::ffff:127.x.x.x\n if (address.startsWith('::ffff:127.')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Validate the Host header against allowed loopback hosts.\n * Prevents DNS rebinding and host confusion attacks.\n *\n * @param hostHeader - The Host header value from the request\n * @param expectedPort - The expected port number\n * @returns True if the Host header is valid\n */\nexport function isValidHostHeader(hostHeader: string | undefined, expectedPort: number): boolean {\n if (!hostHeader) {\n return false;\n }\n\n // Allowed host patterns for loopback\n const allowedHosts = [\n `127.0.0.1:${expectedPort}`,\n `localhost:${expectedPort}`,\n `[::1]:${expectedPort}`,\n ];\n\n return allowedHosts.includes(hostHeader.toLowerCase());\n}\n\n/**\n * Check if a query parameter appears multiple times (potential injection).\n *\n * @param url - The parsed URL object\n * @param paramName - The parameter name to check\n * @returns True if the parameter appears more than once\n */\nfunction hasDuplicateParam(url: URL, paramName: string): boolean {\n return url.searchParams.getAll(paramName).length > 1;\n}\n\n\n/**\n * Callback server implementation.\n * Creates an HTTP server on a loopback address with dynamic port allocation\n * to receive OAuth authorization callbacks.\n *\n * @implements {ICallbackServer}\n */\nexport class CallbackServer implements ICallbackServer {\n private server: http.Server | null = null;\n private port = 0;\n private running = false;\n private callbackPath: string;\n private callbackPromise: Promise<CallbackResult> | null = null;\n private callbackResolve: ((result: CallbackResult) => void) | null = null;\n private callbackReject: ((error: Error) => void) | null = null;\n private timeoutId: NodeJS.Timeout | null = null;\n private callbackHandled = false; // One-shot guard to prevent multiple callbacks\n\n /** Minimum timeout in milliseconds (1 second) */\n private static readonly MIN_TIMEOUT_MS = 1000;\n /** Maximum timeout in milliseconds (10 minutes) */\n private static readonly MAX_TIMEOUT_MS = 600000;\n\n /**\n * Creates a new CallbackServer instance.\n * @param callbackPath - The path to listen for callbacks (default: '/callback')\n */\n constructor(callbackPath: string = DEFAULT_CALLBACK_PATH) {\n this.callbackPath = callbackPath;\n }\n\n /**\n * Start the server and return the redirect URI.\n * The server binds to a loopback address (127.0.0.1) with a dynamically allocated port.\n *\n * @returns The redirect URI to use for OAuth callbacks\n * @throws Error if the server is already running or fails to start\n */\n async start(): Promise<string> {\n if (this.running) {\n throw new Error('Callback server is already running');\n }\n\n return new Promise<string>((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n // Handle server errors\n this.server.on('error', (error) => {\n this.running = false;\n reject(new Error(`Failed to start callback server: ${error.message}`));\n });\n\n // Bind to loopback address with dynamic port (port 0)\n this.server.listen(0, LOOPBACK_HOST_IPV4, () => {\n const address = this.server?.address();\n if (address && typeof address === 'object') {\n this.port = address.port;\n this.running = true;\n const redirectUri = `http://${LOOPBACK_HOST_IPV4}:${this.port}${this.callbackPath}`;\n resolve(redirectUri);\n } else {\n reject(new Error('Failed to get server address'));\n }\n });\n });\n }\n\n /**\n * Wait for the authorization callback.\n * Returns when a callback is received or the timeout is reached.\n *\n * @param timeoutMs - Maximum time to wait for the callback in milliseconds\n * @returns The callback result containing the authorization code and state\n * @throws Error if the server is not running, timeout is reached, or callback fails\n */\n async waitForCallback(timeoutMs: number): Promise<CallbackResult> {\n if (!this.running) {\n throw new Error('Callback server is not running');\n }\n\n if (this.callbackPromise) {\n throw new Error('Already waiting for callback');\n }\n\n // Validate timeout parameter\n if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs)) {\n throw new Error('Timeout must be a finite number');\n }\n if (timeoutMs < CallbackServer.MIN_TIMEOUT_MS) {\n throw new Error(`Timeout must be at least ${CallbackServer.MIN_TIMEOUT_MS}ms`);\n }\n if (timeoutMs > CallbackServer.MAX_TIMEOUT_MS) {\n throw new Error(`Timeout must not exceed ${CallbackServer.MAX_TIMEOUT_MS}ms`);\n }\n\n // Reset one-shot guard for new wait\n this.callbackHandled = false;\n\n this.callbackPromise = new Promise<CallbackResult>((resolve, reject) => {\n this.callbackResolve = resolve;\n this.callbackReject = reject;\n\n // Set up timeout\n this.timeoutId = setTimeout(() => {\n this.callbackReject?.(new Error('Callback timeout exceeded'));\n this.cleanup();\n }, timeoutMs);\n });\n\n try {\n return await this.callbackPromise;\n } finally {\n this.callbackPromise = null;\n }\n }\n\n /**\n * Stop the server and clean up resources.\n */\n async stop(): Promise<void> {\n this.cleanup();\n\n if (this.server) {\n return new Promise<void>((resolve, reject) => {\n this.server?.close((error) => {\n // Ignore \"Server is not running\" errors if already closed by stopAcceptingConnections\n if (error && !error.message.includes('Server is not running')) {\n reject(new Error(`Failed to stop callback server: ${error.message}`));\n } else {\n this.server = null;\n this.port = 0;\n this.running = false;\n resolve();\n }\n });\n });\n }\n }\n\n /**\n * Get the current server port.\n * @returns The port number, or 0 if the server is not started\n */\n getPort(): number {\n return this.port;\n }\n\n /**\n * Check if the server is running.\n * @returns True if the server is running\n */\n isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Handle incoming HTTP requests.\n * Parses the callback URL and extracts the authorization code and state.\n * Rejects connections from non-loopback addresses for security.\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n // Security: Only accept GET requests (Requirement 8.2)\n if (req.method !== 'GET') {\n this.sendErrorResponse(res, 405, 'Method Not Allowed', 'Only GET requests are accepted');\n return;\n }\n\n // Security: Reject connections from non-loopback addresses (Requirement 8.2)\n const remoteAddress = req.socket.remoteAddress;\n if (!isLoopbackAddress(remoteAddress)) {\n this.sendErrorResponse(res, 403, 'Forbidden', 'Only loopback connections are allowed');\n return;\n }\n\n // Security: Validate Host header to prevent DNS rebinding attacks (Requirement 8.2)\n const hostHeader = req.headers.host;\n if (!isValidHostHeader(hostHeader, this.port)) {\n this.sendErrorResponse(res, 400, 'Bad Request', 'Invalid Host header');\n return;\n }\n\n // Security: Enforce max URL length to prevent memory abuse\n const rawUrl = req.url || '/';\n if (rawUrl.length > MAX_URL_LENGTH) {\n this.sendErrorResponse(res, 414, 'URI Too Long', 'Request URL exceeds maximum length');\n return;\n }\n\n // Security: Check total header size\n const headerSize = Object.entries(req.headers).reduce(\n (sum, [key, value]) => sum + key.length + (Array.isArray(value) ? value.join('').length : (value?.length || 0)),\n 0\n );\n if (headerSize > MAX_HEADER_SIZE) {\n this.sendErrorResponse(res, 431, 'Request Header Fields Too Large', 'Headers exceed maximum size');\n return;\n }\n\n let url: URL;\n try {\n url = new URL(rawUrl, `http://${LOOPBACK_HOST_IPV4}:${this.port}`);\n } catch {\n this.sendErrorResponse(res, 400, 'Bad Request', 'Invalid URL format');\n return;\n }\n\n // Only handle requests to the callback path\n if (url.pathname !== this.callbackPath) {\n this.sendErrorResponse(res, 404, 'Not Found', 'Invalid callback path');\n return;\n }\n\n // One-shot guard: reject duplicate callbacks (Requirement 8.3)\n if (this.callbackHandled) {\n this.sendErrorResponse(res, 409, 'Conflict', 'Callback already processed');\n return;\n }\n\n // Security: Reject duplicate query parameters (potential injection)\n const sensitiveParams = ['code', 'state', 'error', 'error_description'];\n for (const param of sensitiveParams) {\n if (hasDuplicateParam(url, param)) {\n this.sendErrorResponse(res, 400, 'Bad Request', `Duplicate parameter: ${param}`);\n return;\n }\n }\n\n // Mark callback as handled atomically before any processing\n this.callbackHandled = true;\n\n // Parse query parameters\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Build callback result based on whether it's success or error\n let result: CallbackResult;\n\n try {\n // Send response to browser\n if (error) {\n // OAuth error response - state is optional in error responses\n result = {\n success: false,\n error: error,\n errorDescription: errorDescription || undefined,\n state: state || undefined,\n };\n this.sendHtmlResponse(res, 400, this.buildErrorPage(error, errorDescription));\n } else if (code && state) {\n // Successful authorization - both code and state are required\n result = {\n success: true,\n code: code,\n state: state,\n };\n this.sendHtmlResponse(res, 200, this.buildSuccessPage());\n } else {\n // Missing required parameters\n result = {\n success: false,\n error: 'missing_params',\n errorDescription: 'Missing code or state parameter',\n state: state || undefined,\n };\n this.sendHtmlResponse(res, 400, this.buildErrorPage('missing_params', 'Missing code or state parameter'));\n }\n\n // Resolve the callback promise and stop accepting new connections (Requirement 8.3)\n if (this.callbackResolve) {\n this.callbackResolve(result);\n }\n } finally {\n // Always cleanup and stop accepting connections, even on exceptions\n this.cleanup();\n this.stopAcceptingConnections();\n }\n }\n\n /**\n * Send an error response with security headers.\n */\n private sendErrorResponse(res: http.ServerResponse, statusCode: number, _statusMessage: string, body: string): void {\n res.writeHead(statusCode, {\n 'Content-Type': 'text/plain',\n ...SECURITY_HEADERS,\n });\n res.end(body);\n }\n\n /**\n * Send an HTML response with security headers.\n */\n private sendHtmlResponse(res: http.ServerResponse, statusCode: number, html: string): void {\n res.writeHead(statusCode, {\n 'Content-Type': 'text/html; charset=utf-8',\n ...SECURITY_HEADERS,\n });\n res.end(html);\n }\n\n /**\n * Stop accepting new connections without fully closing the server.\n * This ensures the server stops accepting new requests after processing the callback\n * while allowing the current response to complete.\n * \n * @remarks\n * This implements Requirement 8.3: The Callback_Server SHALL immediately close\n * after processing the single expected request.\n */\n private stopAcceptingConnections(): void {\n if (this.server) {\n // Close the server to stop accepting new connections\n // This allows existing connections to finish but rejects new ones\n this.server.close();\n this.running = false;\n }\n }\n\n /**\n * Clean up timeout and callback state.\n */\n private cleanup(): void {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId);\n this.timeoutId = null;\n }\n this.callbackResolve = null;\n this.callbackReject = null;\n }\n\n /**\n * Build a success HTML page to display in the browser.\n */\n private buildSuccessPage(): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <title>Authorization Successful</title>\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; \n display: flex; justify-content: center; align-items: center; height: 100vh; \n margin: 0; background: #f5f5f5; }\n .container { text-align: center; padding: 40px; background: white; \n border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }\n h1 { color: #22c55e; margin-bottom: 16px; }\n p { color: #666; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>\u2713 Authorization Successful</h1>\n <p>You can close this window and return to the application.</p>\n </div>\n</body>\n</html>`;\n }\n\n /**\n * Build an error HTML page to display in the browser.\n */\n private buildErrorPage(error: string, description?: string | null): string {\n const safeError = this.escapeHtml(error);\n const safeDescription = description ? this.escapeHtml(description) : 'An error occurred during authorization.';\n\n return `<!DOCTYPE html>\n<html>\n<head>\n <title>Authorization Failed</title>\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; \n display: flex; justify-content: center; align-items: center; height: 100vh; \n margin: 0; background: #f5f5f5; }\n .container { text-align: center; padding: 40px; background: white; \n border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }\n h1 { color: #ef4444; margin-bottom: 16px; }\n p { color: #666; }\n .error-code { font-family: monospace; background: #f5f5f5; padding: 4px 8px; \n border-radius: 4px; color: #333; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>\u2717 Authorization Failed</h1>\n <p>${safeDescription}</p>\n <p>Error code: <span class=\"error-code\">${safeError}</span></p>\n </div>\n</body>\n</html>`;\n }\n\n /**\n * Escape HTML special characters to prevent XSS.\n */\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Orchestrates the complete agent authentication flow:\n * 1. Generate PKCE code verifier and challenge\n * 2. Generate state parameter for CSRF protection\n * 3. Start the callback server on loopback address\n * 4. Build the authorization URL with all required parameters\n * 5. Launch the system default browser to the authorization URL\n * 6. Wait for the callback with the authorization code\n * 7. Validate the state parameter\n * 8. Exchange the authorization code for tokens\n * 9. Return the authentication result\n *\n * Requirements: 3.1, 3.2, 3.3, 3.4\n *\n * @module flows/agent-auth-flow\n */\n\nimport type {\n AuthProviderId,\n AuthResult,\n AgentAuthOptions,\n AuthorizationParams,\n TokenResponse,\n} from '../types.js';\nimport { createSession, DEFAULT_SESSION_TIMEOUT_MS } from '../session.js';\nimport type { AuthSession } from '../session.js';\nimport { CallbackServer } from './callback-server.js';\nimport type { IAuthProvider } from '../providers/types.js';\n\n/**\n * Default timeout for the agent auth flow in milliseconds (5 minutes).\n */\nexport const DEFAULT_AUTH_TIMEOUT_MS = DEFAULT_SESSION_TIMEOUT_MS;\n\n/**\n * List of environment variables that indicate a CI/headless environment.\n * These are commonly set by CI systems and indicate that browser-based\n * authentication should not be attempted.\n */\nconst CI_ENVIRONMENT_VARIABLES = [\n 'CI', // Generic CI indicator (GitHub Actions, GitLab CI, etc.)\n 'CONTINUOUS_INTEGRATION', // Generic CI indicator\n 'GITHUB_ACTIONS', // GitHub Actions\n 'GITLAB_CI', // GitLab CI\n 'JENKINS', // Jenkins\n 'JENKINS_URL', // Jenkins (alternative)\n 'TRAVIS', // Travis CI\n 'CIRCLECI', // CircleCI\n 'BUILDKITE', // Buildkite\n 'DRONE', // Drone CI\n 'TEAMCITY_VERSION', // TeamCity\n 'TF_BUILD', // Azure Pipelines\n 'CODEBUILD_BUILD_ID', // AWS CodeBuild\n 'BITBUCKET_BUILD_NUMBER', // Bitbucket Pipelines\n 'HEROKU_TEST_RUN_ID', // Heroku CI\n 'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI', // Azure DevOps\n] as const;\n\n/**\n * Detect if the current environment is headless (no display/browser available).\n *\n * A headless environment is detected when any of the following conditions are true:\n * 1. CI environment variables are set (CI, GITHUB_ACTIONS, GITLAB_CI, JENKINS, etc.)\n * 2. HEADLESS environment variable is set to a truthy value\n * 3. SSH_TTY environment variable is set (indicates SSH session without display)\n * 4. stdout or stderr are not TTY (indicates non-interactive environment)\n *\n * This detection is used to prevent browser launch in environments where\n * it would fail or be inappropriate (CI pipelines, SSH sessions, etc.).\n *\n * Requirements: 3.1, 4.1\n *\n * @returns true if running in a headless environment, false otherwise\n */\nexport function isHeadlessEnvironment(): boolean {\n // Check for CI environment variables\n for (const envVar of CI_ENVIRONMENT_VARIABLES) {\n const value = process.env[envVar];\n if (value !== undefined && value !== '' && value !== '0' && value.toLowerCase() !== 'false') {\n return true;\n }\n }\n\n // Check for explicit HEADLESS environment variable\n const headlessEnv = process.env['HEADLESS'];\n if (headlessEnv !== undefined && headlessEnv !== '' && headlessEnv !== '0' && headlessEnv.toLowerCase() !== 'false') {\n return true;\n }\n\n // Check for SSH_TTY (indicates SSH session without display)\n if (process.env['SSH_TTY'] !== undefined && process.env['SSH_TTY'] !== '') {\n return true;\n }\n\n // Check if stdout or stderr are not TTY (non-interactive environment)\n // Note: In a headless environment, typically neither stdout nor stderr is a TTY\n if (!process.stdout.isTTY || !process.stderr.isTTY) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Dependencies for the agent auth flow.\n */\nexport interface AgentAuthFlowDependencies {\n /** Function to get a provider by ID */\n getProvider: (providerId: AuthProviderId) => IAuthProvider;\n /** Function to store tokens after successful authentication */\n storeTokens: (providerId: AuthProviderId, tokens: TokenResponse) => Promise<void>;\n /** Optional custom browser launcher (for testing) */\n launchBrowser?: (url: string) => Promise<void>;\n}\n\n/**\n * Agent auth flow - browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * This class orchestrates the complete OAuth 2.1 agent authentication flow,\n * handling PKCE generation, browser launch, callback handling, and token exchange.\n */\nexport class AgentAuthFlow {\n private readonly getProvider: (providerId: AuthProviderId) => IAuthProvider;\n private readonly storeTokens: (providerId: AuthProviderId, tokens: TokenResponse) => Promise<void>;\n private readonly launchBrowser: (url: string) => Promise<void>;\n\n /**\n * Create a new agent auth flow.\n *\n * @param dependencies - Flow dependencies including provider resolver and token storage\n */\n constructor(dependencies: AgentAuthFlowDependencies) {\n this.getProvider = dependencies.getProvider;\n this.storeTokens = dependencies.storeTokens;\n this.launchBrowser = dependencies.launchBrowser ?? openSystemBrowser;\n }\n\n /**\n * Execute the agent auth flow.\n *\n * Performs the complete OAuth 2.1 Authorization Code flow with PKCE:\n * 1. Checks for headless environment (fails fast if detected)\n * 2. Creates an auth session with PKCE and state parameters\n * 3. Starts a callback server on loopback address\n * 4. Builds and opens the authorization URL in the system browser\n * 5. Waits for the OAuth callback\n * 6. Validates the state parameter\n * 7. Exchanges the authorization code for tokens\n * 8. Stores the tokens and returns the result\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n async execute(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n // Step 0: Check for headless environment FIRST (fail-fast)\n // Requirements: 3.1, 13.2\n if (isHeadlessEnvironment()) {\n console.error(`[AgentAuthFlow] Headless environment detected, cannot launch browser for ${providerId}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'HEADLESS_ENVIRONMENT',\n message: 'Browser OAuth not available in headless environment',\n details: {\n suggestion: 'Use --setup for manual credential configuration',\n },\n },\n };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_AUTH_TIMEOUT_MS;\n let callbackServer: CallbackServer | null = null;\n let session: AuthSession | null = null;\n\n try {\n // Step 1: Get the provider\n const provider = this.getProvider(providerId);\n\n // Step 1.5: Validate provider configuration (Requirement 7.1, 7.5)\n const clientId = options?.clientId ?? this.getDefaultClientId(providerId);\n const configValidation = this.validateProviderConfig(providerId, provider, clientId);\n if (!configValidation.valid) {\n console.error(`[AgentAuthFlow] Provider configuration invalid: ${configValidation.error}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: configValidation.error!,\n details: configValidation.details,\n },\n };\n }\n\n // Step 2: Create auth session with PKCE and state parameters\n // (Requirement 3.1: Initiate OAuth 2.1 Authorization Code flow with PKCE)\n session = createSession(providerId, timeoutMs);\n console.error(`[AgentAuthFlow] Created auth session ${session.sessionId} for ${providerId}`);\n\n // Step 3: Start the callback server on loopback address\n // (Requirement 3.3: Start Callback_Server on loopback address)\n callbackServer = new CallbackServer();\n const redirectUri = await callbackServer.start();\n console.error(`[AgentAuthFlow] Callback server started at ${redirectUri}`);\n\n // Step 4: Build the authorization URL with all required parameters\n // (Requirement 3.2: Open browser with required OAuth parameters)\n const scopes = options?.scopes ?? [...provider.defaultScopes];\n\n const authParams: AuthorizationParams = {\n clientId,\n redirectUri,\n scope: scopes.join(' '),\n state: session.state,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: 'S256',\n responseType: 'code',\n };\n\n const authorizationUrl = provider.buildAuthorizationUrl(authParams);\n console.error(`[AgentAuthFlow] Authorization URL built for ${providerId}`);\n\n // Step 5: Launch the system default browser to the authorization URL\n // (Requirement 3.2: Open system default browser)\n await this.launchBrowser(authorizationUrl);\n console.error(`[AgentAuthFlow] Browser launched for ${providerId} authentication`);\n\n // Step 6: Wait for the callback with the authorization code\n // (Requirement 3.4: Receive authorization code via callback)\n const callbackResult = await callbackServer.waitForCallback(session.remainingTime());\n\n // Check for OAuth error in callback (discriminated union check)\n if (!callbackResult.success) {\n console.error(`[AgentAuthFlow] OAuth error: ${callbackResult.error} - ${callbackResult.errorDescription}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: callbackResult.errorDescription || callbackResult.error,\n details: {\n oauthError: callbackResult.error,\n oauthErrorDescription: callbackResult.errorDescription,\n },\n },\n };\n }\n\n // Step 7: Validate the state parameter\n // (Requirement 2.2, 2.3: Validate state parameter)\n if (!session.validateState(callbackResult.state)) {\n console.error(`[AgentAuthFlow] State validation failed for ${providerId}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'INVALID_STATE',\n message: 'State parameter validation failed. The authorization response may have been tampered with.',\n },\n };\n }\n\n // Step 8: Exchange the authorization code for tokens\n // (Requirement 3.4: Exchange code for tokens with code verifier)\n // Note: callbackResult.code is guaranteed to exist here due to discriminated union\n console.error(`[AgentAuthFlow] Exchanging authorization code for tokens`);\n const tokenResponse = await provider.exchangeCode(\n callbackResult.code,\n session.codeVerifier,\n redirectUri\n );\n\n // Step 9: Store the tokens\n await this.storeTokens(providerId, tokenResponse);\n console.error(`[AgentAuthFlow] Tokens stored successfully for ${providerId}`);\n\n return {\n success: true,\n providerId,\n };\n } catch (error) {\n console.error(`[AgentAuthFlow] Authentication failed for ${providerId}: ${error}`);\n\n // Determine error type\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Check for timeout\n if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {\n return {\n success: false,\n providerId,\n error: {\n code: 'TIMEOUT',\n message: 'Authentication flow timed out. Please try again.',\n },\n };\n }\n\n // Check for network errors\n if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND') || errorMessage.includes('fetch')) {\n return {\n success: false,\n providerId,\n error: {\n code: 'NETWORK_ERROR',\n message: `Network error during authentication: ${errorMessage}`,\n },\n };\n }\n\n // Generic error\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: errorMessage,\n },\n };\n } finally {\n // Clean up: Stop the callback server\n if (callbackServer) {\n try {\n await callbackServer.stop();\n console.error(`[AgentAuthFlow] Callback server stopped`);\n } catch (stopError) {\n console.error(`[AgentAuthFlow] Error stopping callback server: ${stopError}`);\n }\n }\n }\n }\n\n /**\n * Validate provider configuration before starting OAuth flow.\n *\n * Checks:\n * - Client ID is present and valid\n * - Provider endpoints are HTTPS (via provider.validateConfig())\n * - Provider supports PKCE with S256\n *\n * @param providerId - The provider identifier\n * @param provider - The provider instance\n * @param clientId - The client ID to validate\n * @returns Validation result with error details if invalid\n */\n private validateProviderConfig(\n providerId: AuthProviderId,\n provider: IAuthProvider,\n clientId: string\n ): { valid: boolean; error?: string; details?: Record<string, unknown> } {\n // Validate client ID\n if (!clientId || typeof clientId !== 'string') {\n return {\n valid: false,\n error: `Client ID is required for ${providerId}. Set OAUTH_${providerId.toUpperCase()}_CLIENT_ID environment variable.`,\n details: { providerId, suggestion: 'Configure client_id via environment variable or --setup' },\n };\n }\n\n const trimmedClientId = clientId.trim();\n if (trimmedClientId.length === 0) {\n return {\n valid: false,\n error: `Client ID cannot be empty for ${providerId}.`,\n details: { providerId },\n };\n }\n\n // Check for control characters in client ID\n if (containsControlCharacters(trimmedClientId)) {\n return {\n valid: false,\n error: `Client ID contains invalid characters for ${providerId}.`,\n details: { providerId },\n };\n }\n\n // Validate provider configuration (HTTPS endpoints, etc.)\n try {\n provider.validateConfig();\n } catch (error) {\n return {\n valid: false,\n error: `Provider configuration invalid for ${providerId}: ${(error as Error).message}`,\n details: { providerId },\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Get the default client ID for a provider.\n *\n * This is a placeholder that should be overridden with actual client IDs\n * from configuration or environment variables.\n *\n * @param providerId - The provider identifier\n * @returns The default client ID for the provider\n */\n private getDefaultClientId(providerId: AuthProviderId): string {\n // In a real implementation, these would come from configuration\n // For now, return a placeholder that indicates configuration is needed\n const envKey = `OAUTH_${providerId.toUpperCase()}_CLIENT_ID`;\n const clientId = process.env[envKey];\n\n if (!clientId) {\n throw new Error(\n `No client ID configured for ${providerId}. ` +\n `Set the ${envKey} environment variable or provide clientId in options.`\n );\n }\n\n return clientId;\n }\n}\n\n/**\n * Sensitive URL parameters that should be redacted in logs.\n * These parameters may contain security-sensitive values.\n */\nconst SENSITIVE_URL_PARAMS = [\n 'state',\n 'code_challenge',\n 'code',\n 'access_token',\n 'refresh_token',\n 'id_token',\n 'client_secret',\n] as const;\n\n/**\n * Redact sensitive parameters from a URL for safe logging.\n *\n * @param url - The URL to redact\n * @returns URL string with sensitive parameters replaced with [REDACTED]\n */\nexport function redactUrlForLogging(url: string): string {\n try {\n const parsedUrl = new URL(url);\n for (const param of SENSITIVE_URL_PARAMS) {\n if (parsedUrl.searchParams.has(param)) {\n parsedUrl.searchParams.set(param, '[REDACTED]');\n }\n }\n return parsedUrl.toString();\n } catch {\n // If URL parsing fails, return a generic redacted message\n return '[INVALID URL - REDACTED]';\n }\n}\n\n/**\n * Check if a string contains control characters (ASCII 0-31 except tab, newline, carriage return).\n *\n * @param str - The string to check\n * @returns true if control characters are found, false otherwise\n */\nfunction containsControlCharacters(str: string): boolean {\n // Match control characters except tab (0x09), newline (0x0A), carriage return (0x0D)\n // eslint-disable-next-line no-control-regex\n const controlCharRegex = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/;\n return controlCharRegex.test(str);\n}\n\n/**\n * Open a URL in the system default browser.\n *\n * Uses platform-specific commands to launch the browser:\n * - macOS: `open`\n * - Windows: `start`\n * - Linux: `xdg-open`\n *\n * Security measures:\n * - Uses execFile with argument arrays to prevent command injection\n * - Validates URL is HTTPS only (OAuth authorization URLs must use HTTPS)\n * - Disallows userinfo (username:password) in URL\n * - Validates no control characters in URL (checked before URL parsing)\n * - Logs redacted URL for security\n *\n * Requirements: 3.6, 8.1\n *\n * @param url - The URL to open\n * @throws Error if the browser cannot be launched or URL is invalid\n */\nexport async function openSystemBrowser(url: string): Promise<void> {\n // Security: Check for control characters in the original URL string BEFORE parsing\n // This catches attempts to inject control characters that might be normalized by URL class\n if (containsControlCharacters(url)) {\n throw new Error('URL contains invalid control characters');\n }\n\n // Validate URL to prevent command injection and ensure it's a valid HTTPS URL\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new Error('Invalid URL format');\n }\n\n // Security: Only allow HTTPS protocol for OAuth authorization URLs\n // This is a security requirement for OAuth 2.1 - authorization endpoints must use HTTPS\n // Requirements: 3.6, 8.1\n if (parsedUrl.protocol !== 'https:') {\n throw new Error('OAuth authorization URL must use HTTPS');\n }\n\n // Security: Disallow userinfo (username:password) in URL\n // URLs like https://user:pass@auth.example.com/ are suspicious and should be rejected\n if (parsedUrl.username !== '' || parsedUrl.password !== '') {\n throw new Error('URL must not contain credentials');\n }\n\n // Use the parsed URL's toString() for the final URL (properly encoded)\n const finalUrl = parsedUrl.toString();\n\n // Log redacted URL for debugging (security: sensitive params are redacted)\n const redactedUrl = redactUrlForLogging(finalUrl);\n console.error(`[openSystemBrowser] Opening browser: ${redactedUrl}`);\n\n const platform = process.platform;\n\n // Use execFile with argument arrays to prevent shell injection\n // This is safer than exec() with string interpolation\n const { execFile } = await import('child_process');\n const { promisify } = await import('util');\n const execFileAsync = promisify(execFile);\n\n try {\n switch (platform) {\n case 'darwin':\n // macOS: open command with URL as argument\n await execFileAsync('open', [finalUrl]);\n break;\n case 'win32':\n // Windows: use cmd.exe /c start with proper escaping\n // Note: Windows requires special handling for URLs with special chars\n await execFileAsync('cmd.exe', ['/c', 'start', '', finalUrl]);\n break;\n default:\n // Linux and others: xdg-open with URL as argument\n await execFileAsync('xdg-open', [finalUrl]);\n break;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to open browser: ${errorMessage}`);\n }\n}\n\n/**\n * Create an agent auth flow with the given dependencies.\n *\n * @param dependencies - Flow dependencies\n * @returns A new AgentAuthFlow instance\n */\nexport function createAgentAuthFlow(dependencies: AgentAuthFlowDependencies): AgentAuthFlow {\n return new AgentAuthFlow(dependencies);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * OS keychain storage backend.\n *\n * Uses macOS Keychain, Windows Credential Manager, or Linux Secret Service\n * via the keytar package for secure credential storage.\n *\n * @module storage/keychain-backend\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport { isValidProviderId } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/** Service name for keychain entries */\nconst SERVICE_NAME = 'stdio-bus-registry-launcher';\n\n/** Account prefix for provider entries */\nconst ACCOUNT_PREFIX = 'oauth-';\n\n/** Special account for tracking stored providers */\nconst PROVIDERS_LIST_ACCOUNT = '__providers_list__';\n\n/**\n * Keytar module interface (subset of keytar API we use).\n */\nexport interface KeytarModule {\n setPassword(service: string, account: string, password: string): Promise<void>;\n getPassword(service: string, account: string): Promise<string | null>;\n deletePassword(service: string, account: string): Promise<boolean>;\n findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;\n}\n\n/**\n * Keychain storage backend implementation.\n *\n * Uses the system keychain for secure credential storage:\n * - macOS: Keychain Access\n * - Windows: Credential Manager\n * - Linux: Secret Service (libsecret)\n *\n * Handles keychain unavailability gracefully by returning false from isAvailable().\n */\nexport class KeychainBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'keychain';\n\n private keytar: KeytarModule | null = null;\n private keytarLoadAttempted = false;\n private keytarLoadError: Error | null = null;\n\n /**\n * Lazily load keytar module.\n * Uses dynamic import to handle cases where keytar is not installed.\n */\n private async loadKeytar(): Promise<KeytarModule | null> {\n if (this.keytarLoadAttempted) {\n return this.keytar;\n }\n\n this.keytarLoadAttempted = true;\n\n try {\n // Dynamic import to handle missing keytar gracefully\n // Use a variable to prevent TypeScript from resolving the module at compile time\n const moduleName = 'keytar';\n const keytarModule = await import(/* webpackIgnore: true */ moduleName) as { default?: KeytarModule } & KeytarModule;\n this.keytar = keytarModule.default || keytarModule;\n return this.keytar;\n } catch (error) {\n this.keytarLoadError = error instanceof Error ? error : new Error(String(error));\n console.error(`[KeychainBackend] Failed to load keytar: ${this.keytarLoadError.message}`);\n return null;\n }\n }\n\n /**\n * Get account name for a provider.\n */\n private getAccountName(providerId: AuthProviderId): string {\n return `${ACCOUNT_PREFIX}${providerId}`;\n }\n\n /**\n * Check if the keychain backend is available on this system.\n * Returns false if keytar is not installed or keychain access fails.\n */\n async isAvailable(): Promise<boolean> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return false;\n }\n\n try {\n // Test keychain access by attempting to read a non-existent entry\n await keytar.getPassword(SERVICE_NAME, '__availability_check__');\n return true;\n } catch (error) {\n console.error(`[KeychainBackend] Keychain access check failed: ${error}`);\n return false;\n }\n }\n\n /**\n * Store credentials for a provider in the system keychain.\n */\n async store(providerId: AuthProviderId, credentials: StoredCredentials): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n throw new Error('Keychain backend is not available');\n }\n\n const account = this.getAccountName(providerId);\n const serialized = JSON.stringify(credentials);\n\n try {\n await keytar.setPassword(SERVICE_NAME, account, serialized);\n // Update providers list\n await this.addToProvidersList(providerId);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to store credentials in keychain: ${message}`);\n }\n }\n\n /**\n * Retrieve credentials for a provider from the system keychain.\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return null;\n }\n\n const account = this.getAccountName(providerId);\n\n try {\n const serialized = await keytar.getPassword(SERVICE_NAME, account);\n if (!serialized) {\n return null;\n }\n\n const credentials = JSON.parse(serialized) as StoredCredentials;\n return credentials;\n } catch (error) {\n console.error(`[KeychainBackend] Failed to retrieve credentials: ${error}`);\n return null;\n }\n }\n\n /**\n * Delete credentials for a provider from the system keychain.\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return;\n }\n\n const account = this.getAccountName(providerId);\n\n try {\n await keytar.deletePassword(SERVICE_NAME, account);\n // Update providers list\n await this.removeFromProvidersList(providerId);\n } catch (error) {\n // Ignore errors when deleting (entry might not exist)\n console.error(`[KeychainBackend] Failed to delete credentials: ${error}`);\n }\n }\n\n /**\n * Delete all stored credentials from the system keychain.\n */\n async deleteAll(): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return;\n }\n\n try {\n const credentials = await keytar.findCredentials(SERVICE_NAME);\n for (const cred of credentials) {\n await keytar.deletePassword(SERVICE_NAME, cred.account);\n }\n } catch (error) {\n console.error(`[KeychainBackend] Failed to delete all credentials: ${error}`);\n }\n }\n\n /**\n * List all providers with stored credentials.\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return [];\n }\n\n try {\n const credentials = await keytar.findCredentials(SERVICE_NAME);\n const providers: AuthProviderId[] = [];\n\n for (const cred of credentials) {\n if (cred.account.startsWith(ACCOUNT_PREFIX)) {\n const candidateId = cred.account.slice(ACCOUNT_PREFIX.length);\n // Validate that the extracted ID is a valid provider ID\n if (isValidProviderId(candidateId)) {\n providers.push(candidateId);\n }\n }\n }\n\n return providers;\n } catch (error) {\n console.error(`[KeychainBackend] Failed to list providers: ${error}`);\n return [];\n }\n }\n\n /**\n * Add a provider to the internal providers list.\n */\n private async addToProvidersList(providerId: AuthProviderId): Promise<void> {\n const keytar = this.keytar;\n if (!keytar) return;\n\n try {\n const existing = await keytar.getPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT);\n let providers: AuthProviderId[] = [];\n\n if (existing) {\n const parsed = JSON.parse(existing);\n // Validate that parsed is an array and filter to valid provider IDs only\n if (Array.isArray(parsed)) {\n providers = parsed.filter((p): p is AuthProviderId => isValidProviderId(p));\n }\n }\n\n if (!providers.includes(providerId)) {\n providers.push(providerId);\n await keytar.setPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT, JSON.stringify(providers));\n }\n } catch {\n // Ignore errors updating providers list\n }\n }\n\n /**\n * Remove a provider from the internal providers list.\n */\n private async removeFromProvidersList(providerId: AuthProviderId): Promise<void> {\n const keytar = this.keytar;\n if (!keytar) return;\n\n try {\n const existing = await keytar.getPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT);\n if (existing) {\n const parsed = JSON.parse(existing);\n // Validate that parsed is an array and filter to valid provider IDs only\n if (Array.isArray(parsed)) {\n const providers = parsed.filter((p): p is AuthProviderId => isValidProviderId(p));\n const filtered = providers.filter(p => p !== providerId);\n await keytar.setPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT, JSON.stringify(filtered));\n }\n }\n } catch {\n // Ignore errors updating providers list\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Encrypted file storage backend.\n *\n * Uses AES-256-GCM encryption with random salt + machine-specific key derivation.\n * Stores credentials in ~/.stdio-bus/auth-credentials.enc\n *\n * File format v2: Salt (32 bytes) + IV (12 bytes) + Auth Tag (16 bytes) + Encrypted JSON data\n *\n * @module storage/encrypted-file-backend\n */\n\nimport * as crypto from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/** AES-256-GCM configuration constants */\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // 96 bits for GCM\nconst AUTH_TAG_LENGTH = 16; // 128 bits\nconst KEY_LENGTH = 32; // 256 bits\nconst SALT_LENGTH = 32; // 256 bits random salt\nconst PBKDF2_ITERATIONS = 100000;\nconst PBKDF2_DIGEST = 'sha256';\n\n/** File permission mode: owner read/write only (0600) */\nconst FILE_PERMISSION_MODE = 0o600;\n\n/** Default storage directory and file names */\nconst CONFIG_DIR_NAME = '.stdio-bus';\nconst CREDENTIALS_FILE_NAME = 'auth-credentials.enc';\n\n/**\n * Custom error for credential store corruption.\n */\nexport class CredentialStoreCorruptedError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'CredentialStoreCorruptedError';\n }\n}\n\n/**\n * Internal storage format for credentials map.\n */\ninterface CredentialsStore {\n version: number;\n credentials: Record<string, StoredCredentials>;\n}\n\n\n/**\n * Encrypted file storage backend implementation.\n *\n * Uses AES-256-GCM encryption with a key derived from:\n * - Random salt (stored with file, unique per file)\n * - Machine-specific entropy (hostname, username)\n *\n * This provides both uniqueness (random salt) and machine binding (entropy).\n *\n * Requirements: 5.2, 5.3\n */\nexport class EncryptedFileBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'encrypted-file';\n\n /** Cached encryption key (derived once per instance, per salt) */\n private encryptionKey: Buffer | null = null;\n\n /** Cached salt from current file */\n private currentSalt: Buffer | null = null;\n\n /** Path to the credentials file */\n private readonly filePath: string;\n\n /** Path to the config directory */\n private readonly configDir: string;\n\n /**\n * Create a new EncryptedFileBackend instance.\n * @param customPath - Optional custom path for the credentials file (for testing)\n */\n constructor(customPath?: string) {\n if (customPath) {\n this.filePath = customPath;\n this.configDir = path.dirname(customPath);\n } else {\n this.configDir = path.join(os.homedir(), CONFIG_DIR_NAME);\n this.filePath = path.join(this.configDir, CREDENTIALS_FILE_NAME);\n }\n }\n\n /**\n * Check if the backend is available.\n * Tests if the config directory is writable.\n * @returns True if the file system is writable\n */\n async isAvailable(): Promise<boolean> {\n try {\n // Ensure config directory exists\n await fs.mkdir(this.configDir, { recursive: true });\n\n // Test write access by creating a temporary file\n const testFile = path.join(this.configDir, `.write-test-${Date.now()}`);\n await fs.writeFile(testFile, 'test');\n await fs.unlink(testFile);\n\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<void> {\n const store = await this.loadStore();\n store.credentials[providerId] = credentials;\n await this.saveStore(store);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const store = await this.loadStore();\n return store.credentials[providerId] ?? null;\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const store = await this.loadStore();\n delete store.credentials[providerId];\n await this.saveStore(store);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n try {\n await fs.unlink(this.filePath);\n // Clear cached key and salt\n this.encryptionKey = null;\n this.currentSalt = null;\n } catch (error) {\n // Ignore if file doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider IDs that have stored credentials\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const store = await this.loadStore();\n return Object.keys(store.credentials) as AuthProviderId[];\n }\n\n /**\n * Derive the encryption key from salt and machine-specific entropy.\n * Uses PBKDF2 with random salt + hostname + username.\n * @param salt - The random salt (32 bytes)\n * @returns The derived 256-bit encryption key\n */\n private async deriveKey(salt: Buffer): Promise<Buffer> {\n // Check if we can use cached key (same salt)\n if (this.encryptionKey && this.currentSalt && salt.equals(this.currentSalt)) {\n return this.encryptionKey;\n }\n\n // Gather machine-specific entropy\n const hostname = os.hostname();\n const username = os.userInfo().username;\n const machineEntropy = `${hostname}:${username}`;\n\n // Combine random salt with machine entropy for key derivation\n const combinedSalt = Buffer.concat([salt, Buffer.from(machineEntropy, 'utf8')]);\n\n // Derive key using PBKDF2\n const derivedKey = await new Promise<Buffer>((resolve, reject) => {\n crypto.pbkdf2(\n machineEntropy,\n combinedSalt,\n PBKDF2_ITERATIONS,\n KEY_LENGTH,\n PBKDF2_DIGEST,\n (err, key) => {\n if (err) {\n reject(err);\n } else {\n resolve(key);\n }\n }\n );\n });\n\n // Cache the key and salt\n this.encryptionKey = derivedKey;\n this.currentSalt = salt;\n\n return derivedKey;\n }\n\n /**\n * Encrypt data using AES-256-GCM.\n * @param plaintext - The data to encrypt\n * @param salt - The salt to use for key derivation\n * @returns Buffer containing Salt + IV + Auth Tag + Ciphertext\n */\n private async encrypt(plaintext: string, salt: Buffer): Promise<Buffer> {\n const key = await this.deriveKey(salt);\n const iv = crypto.randomBytes(IV_LENGTH);\n\n const cipher = crypto.createCipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n\n const authTag = cipher.getAuthTag();\n\n // Format: Salt (32 bytes) + IV (12 bytes) + Auth Tag (16 bytes) + Encrypted data\n return Buffer.concat([salt, iv, authTag, encrypted]);\n }\n\n /**\n * Decrypt data using AES-256-GCM.\n * @param data - Buffer containing Salt + IV + Auth Tag + Ciphertext\n * @returns The decrypted plaintext\n * @throws CredentialStoreCorruptedError if decryption fails\n */\n private async decrypt(data: Buffer): Promise<string> {\n const minLength = SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH;\n if (data.length < minLength) {\n throw new CredentialStoreCorruptedError(\n `Invalid encrypted data: too short (${data.length} bytes, minimum ${minLength})`\n );\n }\n\n // Extract components\n const salt = data.subarray(0, SALT_LENGTH);\n const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const authTag = data.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = data.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\n const key = await this.deriveKey(salt);\n\n try {\n const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n\n return decrypted.toString('utf8');\n } catch (error) {\n throw new CredentialStoreCorruptedError(\n 'Failed to decrypt credential store: authentication failed or data corrupted',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n\n /**\n * Load the credentials store from the encrypted file.\n * @returns The decrypted credentials store\n * @throws CredentialStoreCorruptedError if the file exists but cannot be decrypted\n */\n private async loadStore(): Promise<CredentialsStore> {\n try {\n const encryptedData = await fs.readFile(this.filePath);\n const jsonData = await this.decrypt(encryptedData);\n\n let store: CredentialsStore;\n try {\n store = JSON.parse(jsonData) as CredentialsStore;\n } catch (parseError) {\n throw new CredentialStoreCorruptedError(\n 'Failed to parse credential store: invalid JSON',\n parseError instanceof Error ? parseError : undefined\n );\n }\n\n // Validate store structure\n if (typeof store.version !== 'number' || typeof store.credentials !== 'object') {\n throw new CredentialStoreCorruptedError(\n 'Invalid credential store format: missing version or credentials'\n );\n }\n\n return store;\n } catch (error) {\n // Return empty store only if file doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { version: 1, credentials: {} };\n }\n\n // Re-throw CredentialStoreCorruptedError as-is\n if (error instanceof CredentialStoreCorruptedError) {\n throw error;\n }\n\n // Wrap other errors\n throw new CredentialStoreCorruptedError(\n 'Failed to load credential store',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Save the credentials store to the encrypted file.\n * Sets restrictive file permissions (0600 - owner read/write only).\n * @param store - The credentials store to save\n */\n private async saveStore(store: CredentialsStore): Promise<void> {\n // Ensure config directory exists\n await fs.mkdir(this.configDir, { recursive: true });\n\n // Generate new random salt for each save (provides forward secrecy)\n const salt = crypto.randomBytes(SALT_LENGTH);\n\n const jsonData = JSON.stringify(store);\n const encryptedData = await this.encrypt(jsonData, salt);\n\n // Write atomically using a temporary file\n const tempFile = `${this.filePath}.tmp`;\n await fs.writeFile(tempFile, encryptedData, { mode: FILE_PERMISSION_MODE });\n\n // Rename atomically\n await fs.rename(tempFile, this.filePath);\n\n // Ensure final file has correct permissions (rename may not preserve mode on all systems)\n try {\n await fs.chmod(this.filePath, FILE_PERMISSION_MODE);\n } catch {\n // Ignore chmod errors on systems that don't support it (e.g., Windows)\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * In-memory storage backend for testing.\n *\n * This backend stores credentials in a Map and is primarily intended\n * for testing purposes. All operations are synchronous but return\n * Promises for interface compatibility with other backends.\n *\n * @module storage/memory-backend\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/**\n * In-memory storage backend implementation.\n *\n * Uses a Map<AuthProviderId, StoredCredentials> for storage.\n * This backend is always available and is primarily used for testing.\n */\nexport class MemoryBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'memory';\n\n /** Internal storage map */\n private readonly storage: Map<AuthProviderId, StoredCredentials> = new Map();\n\n /**\n * Check if the backend is available.\n * Memory backend is always available.\n * @returns Always resolves to true\n */\n async isAvailable(): Promise<boolean> {\n return true;\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<void> {\n this.storage.set(providerId, credentials);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n return this.storage.get(providerId) ?? null;\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n this.storage.delete(providerId);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n this.storage.clear();\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider IDs that have stored credentials\n */\n async listProviders(): Promise<AuthProviderId[]> {\n return Array.from(this.storage.keys());\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main credential store facade.\n *\n * Selects the best available storage backend (keychain > encrypted file > memory).\n * Provides a unified interface for credential storage regardless of the underlying backend.\n *\n * @module storage/credential-store\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { ICredentialStore, IStorageBackend } from './types.js';\nimport { KeychainBackend } from './keychain-backend.js';\nimport { EncryptedFileBackend } from './encrypted-file-backend.js';\nimport { MemoryBackend } from './memory-backend.js';\n\n/**\n * Options for creating a credential store.\n */\nexport interface CredentialStoreOptions {\n /** Preferred storage backend (overrides automatic selection) */\n preferredBackend?: StorageBackendType;\n /** Custom path for encrypted file storage */\n encryptedFilePath?: string;\n}\n\n/**\n * Credential store implementation.\n *\n * Automatically selects the best available storage backend:\n * 1. Keychain (most secure, OS-level protection)\n * 2. Encrypted file (secure, portable)\n * 3. Memory (testing only, not persistent)\n *\n * The backend selection happens lazily on first use.\n */\nexport class CredentialStore implements ICredentialStore {\n private backend: IStorageBackend | null = null;\n private backendInitialized = false;\n private readonly options: CredentialStoreOptions;\n\n /**\n * Create a new credential store.\n * @param options - Configuration options\n */\n constructor(options: CredentialStoreOptions = {}) {\n this.options = options;\n }\n\n /**\n * Initialize the storage backend.\n * Selects the best available backend based on system capabilities.\n */\n private async initializeBackend(): Promise<IStorageBackend> {\n if (this.backendInitialized && this.backend) {\n return this.backend;\n }\n\n this.backendInitialized = true;\n\n // If a preferred backend is specified, try to use it\n if (this.options.preferredBackend) {\n const preferred = await this.createBackend(this.options.preferredBackend);\n if (preferred && await preferred.isAvailable()) {\n this.backend = preferred;\n console.error(`[CredentialStore] Using preferred backend: ${preferred.type}`);\n return this.backend;\n }\n console.error(`[CredentialStore] Preferred backend ${this.options.preferredBackend} not available, falling back`);\n }\n\n // Try backends in order of preference\n const backends: IStorageBackend[] = [\n new KeychainBackend(),\n new EncryptedFileBackend(this.options.encryptedFilePath),\n new MemoryBackend(),\n ];\n\n for (const backend of backends) {\n try {\n if (await backend.isAvailable()) {\n this.backend = backend;\n console.error(`[CredentialStore] Using backend: ${backend.type}`);\n return this.backend;\n }\n } catch (error) {\n console.error(`[CredentialStore] Backend ${backend.type} check failed: ${error}`);\n }\n }\n\n // Fallback to memory backend (always available)\n this.backend = new MemoryBackend();\n console.error(`[CredentialStore] Falling back to memory backend`);\n return this.backend;\n }\n\n /**\n * Create a specific backend by type.\n */\n private async createBackend(type: StorageBackendType): Promise<IStorageBackend | null> {\n switch (type) {\n case 'keychain':\n return new KeychainBackend();\n case 'encrypted-file':\n return new EncryptedFileBackend(this.options.encryptedFilePath);\n case 'memory':\n return new MemoryBackend();\n default:\n return null;\n }\n }\n\n /**\n * Get the initialized backend.\n */\n private async getBackend(): Promise<IStorageBackend> {\n return this.initializeBackend();\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(providerId: AuthProviderId, credentials: StoredCredentials): Promise<void> {\n const backend = await this.getBackend();\n await backend.store(providerId, credentials);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const backend = await this.getBackend();\n return backend.retrieve(providerId);\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const backend = await this.getBackend();\n await backend.delete(providerId);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n const backend = await this.getBackend();\n await backend.deleteAll();\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider identifiers\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const backend = await this.getBackend();\n return backend.listProviders();\n }\n\n /**\n * Get the active storage backend type.\n * @returns The backend type currently in use\n */\n getBackendType(): StorageBackendType {\n if (!this.backend) {\n // Return 'memory' as default before initialization\n return 'memory';\n }\n return this.backend.type;\n }\n\n /**\n * Check if the store has been initialized.\n * @returns True if a backend has been selected\n */\n isInitialized(): boolean {\n return this.backendInitialized;\n }\n\n /**\n * Force re-initialization of the backend.\n * Useful for testing or when system capabilities change.\n */\n async reinitialize(): Promise<void> {\n this.backend = null;\n this.backendInitialized = false;\n await this.initializeBackend();\n }\n}\n\n/**\n * Create a credential store with default options.\n * @returns A new credential store instance\n */\nexport function createCredentialStore(options?: CredentialStoreOptions): CredentialStore {\n return new CredentialStore(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Token lifecycle management.\n *\n * Handles token storage, refresh, and expiration.\n * Implements proactive token refresh and concurrent refresh serialization.\n *\n * @module token-manager\n */\n\nimport type { AuthProviderId, TokenResponse, TokenStatus, StoredCredentials } from './types.js';\nimport type { ICredentialStore } from './storage/types.js';\nimport type { IAuthProvider } from './providers/types.js';\n\n/**\n * Default token refresh threshold in milliseconds (5 minutes).\n * Tokens will be proactively refreshed when they expire within this threshold.\n */\nexport const DEFAULT_REFRESH_THRESHOLD_MS = 5 * 60 * 1000;\n\n/**\n * Token lifecycle management interface.\n */\nexport interface ITokenManager {\n /** Get a valid access token, refreshing if necessary */\n getAccessToken(providerId: AuthProviderId): Promise<string | null>;\n\n /** Store new tokens from an OAuth response */\n storeTokens(providerId: AuthProviderId, tokens: TokenResponse): Promise<void>;\n\n /** Check if tokens exist and are valid for a provider */\n hasValidTokens(providerId: AuthProviderId): Promise<boolean>;\n\n /** Force refresh tokens for a provider */\n forceRefresh(providerId: AuthProviderId): Promise<string | null>;\n\n /** Clear tokens for a provider (triggers re-auth) */\n clearTokens(providerId: AuthProviderId): Promise<void>;\n\n /** Get token status for all providers */\n getStatus(): Promise<Map<AuthProviderId, TokenStatus>>;\n}\n\n/**\n * Provider resolver function type.\n * Used to get provider instances for token refresh operations.\n */\nexport type ProviderResolver = (providerId: AuthProviderId) => IAuthProvider | null;\n\n/**\n * Options for creating a token manager.\n */\nexport interface TokenManagerOptions {\n /** Credential store for persisting tokens */\n credentialStore: ICredentialStore;\n /** Function to resolve provider instances */\n providerResolver: ProviderResolver;\n /** Token refresh threshold in milliseconds (default: 5 minutes) */\n refreshThresholdMs?: number;\n}\n\n/**\n * Token manager implementation.\n *\n * Manages token lifecycle including:\n * - Proactive token refresh when tokens are near expiration\n * - Concurrent refresh serialization (only one refresh per provider at a time)\n * - Automatic credential cleanup on refresh failure\n * - Refresh token rotation handling\n */\nexport class TokenManager implements ITokenManager {\n private readonly credentialStore: ICredentialStore;\n private readonly providerResolver: ProviderResolver;\n private readonly refreshThresholdMs: number;\n\n /**\n * Map of provider IDs to pending refresh promises.\n * Used to serialize concurrent refresh requests.\n */\n private readonly pendingRefreshes = new Map<AuthProviderId, Promise<string | null>>();\n\n /**\n * Create a new token manager.\n * @param options - Configuration options\n */\n constructor(options: TokenManagerOptions) {\n this.credentialStore = options.credentialStore;\n this.providerResolver = options.providerResolver;\n this.refreshThresholdMs = options.refreshThresholdMs ?? DEFAULT_REFRESH_THRESHOLD_MS;\n }\n\n /**\n * Get a valid access token, refreshing if necessary.\n *\n * If the token is within the refresh threshold of expiration, it will be\n * proactively refreshed before returning.\n *\n * @param providerId - The provider identifier\n * @returns The access token or null if not available\n */\n async getAccessToken(providerId: AuthProviderId): Promise<string | null> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return null;\n }\n\n // Check if token needs refresh\n if (this.shouldRefresh(credentials)) {\n // Attempt proactive refresh\n const refreshedToken = await this.refreshTokenInternal(providerId, credentials);\n if (refreshedToken !== null) {\n return refreshedToken;\n }\n // If refresh failed but we still have a valid token, return it\n if (!this.isExpired(credentials)) {\n return credentials.accessToken;\n }\n return null;\n }\n\n return credentials.accessToken;\n }\n\n /**\n * Store new tokens from an OAuth response.\n *\n * Converts the token response to stored credentials format and persists them.\n * Preserves the existing refresh token if the new response doesn't include one\n * (some providers only return refresh tokens on initial auth, not on refresh).\n *\n * @param providerId - The provider identifier\n * @param tokens - The token response from the OAuth provider\n */\n async storeTokens(providerId: AuthProviderId, tokens: TokenResponse): Promise<void> {\n const now = Date.now();\n\n // Validate token response\n if (!tokens.accessToken || typeof tokens.accessToken !== 'string') {\n throw new Error('Invalid token response: missing or invalid accessToken');\n }\n if (tokens.expiresIn !== undefined) {\n if (typeof tokens.expiresIn !== 'number' || !Number.isFinite(tokens.expiresIn) || tokens.expiresIn < 0) {\n throw new Error('Invalid token response: expiresIn must be a non-negative finite number');\n }\n }\n\n // Calculate expiration timestamp if expiresIn is provided\n const expiresAt = tokens.expiresIn\n ? now + tokens.expiresIn * 1000\n : undefined;\n\n // Get existing credentials to preserve client info and refresh token\n const existing = await this.credentialStore.retrieve(providerId);\n\n // Preserve existing refresh token if new response doesn't include one\n // (some providers only return refresh tokens on initial auth, not on refresh)\n const refreshToken = tokens.refreshToken ?? existing?.refreshToken;\n\n const credentials: StoredCredentials = {\n providerId,\n accessToken: tokens.accessToken,\n refreshToken,\n expiresAt,\n scope: tokens.scope,\n // Preserve client info from existing credentials\n clientId: existing?.clientId,\n clientSecret: existing?.clientSecret,\n customEndpoints: existing?.customEndpoints,\n storedAt: now,\n };\n\n await this.credentialStore.store(providerId, credentials);\n }\n\n /**\n * Check if tokens exist and are valid for a provider.\n *\n * A token is considered valid if:\n * - Credentials exist for the provider\n * - The access token is not expired\n *\n * @param providerId - The provider identifier\n * @returns True if valid tokens exist\n */\n async hasValidTokens(providerId: AuthProviderId): Promise<boolean> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return false;\n }\n\n return !this.isExpired(credentials);\n }\n\n /**\n * Force refresh tokens for a provider.\n *\n * Unlike getAccessToken, this always attempts a refresh regardless of\n * the current token's expiration status.\n *\n * @param providerId - The provider identifier\n * @returns The new access token or null if refresh failed\n */\n async forceRefresh(providerId: AuthProviderId): Promise<string | null> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return null;\n }\n\n return this.refreshTokenInternal(providerId, credentials);\n }\n\n /**\n * Clear tokens for a provider.\n *\n * This triggers re-authentication on the next token request.\n *\n * @param providerId - The provider identifier\n */\n async clearTokens(providerId: AuthProviderId): Promise<void> {\n await this.credentialStore.delete(providerId);\n }\n\n /**\n * Get token status for all providers.\n *\n * @returns Map of provider IDs to their token status\n */\n async getStatus(): Promise<Map<AuthProviderId, TokenStatus>> {\n const providers = await this.credentialStore.listProviders();\n const statusMap = new Map<AuthProviderId, TokenStatus>();\n\n for (const providerId of providers) {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n statusMap.set(providerId, 'not-configured');\n continue;\n }\n\n if (this.isExpired(credentials)) {\n // Check if we have a refresh token to potentially recover\n if (credentials.refreshToken) {\n statusMap.set(providerId, 'expired');\n } else {\n statusMap.set(providerId, 'refresh-failed');\n }\n } else {\n statusMap.set(providerId, 'authenticated');\n }\n }\n\n return statusMap;\n }\n\n /**\n * Check if credentials are expired.\n *\n * @param credentials - The stored credentials\n * @returns True if the token is expired\n */\n private isExpired(credentials: StoredCredentials): boolean {\n if (!credentials.expiresAt) {\n // No expiration info, assume valid\n return false;\n }\n\n return Date.now() >= credentials.expiresAt;\n }\n\n /**\n * Check if credentials should be proactively refreshed.\n *\n * Returns true if the token will expire within the refresh threshold.\n *\n * @param credentials - The stored credentials\n * @returns True if the token should be refreshed\n */\n private shouldRefresh(credentials: StoredCredentials): boolean {\n if (!credentials.expiresAt) {\n // No expiration info, don't refresh\n return false;\n }\n\n if (!credentials.refreshToken) {\n // No refresh token available\n return false;\n }\n\n const timeUntilExpiry = credentials.expiresAt - Date.now();\n return timeUntilExpiry <= this.refreshThresholdMs;\n }\n\n /**\n * Internal token refresh implementation with concurrent request serialization.\n *\n * Ensures only one refresh operation occurs at a time per provider.\n * If a refresh is already in progress, returns the pending promise.\n *\n * @param providerId - The provider identifier\n * @param credentials - The current stored credentials\n * @returns The new access token or null if refresh failed\n */\n private async refreshTokenInternal(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<string | null> {\n // Check for pending refresh\n const pending = this.pendingRefreshes.get(providerId);\n if (pending) {\n return pending;\n }\n\n // No refresh token available\n if (!credentials.refreshToken) {\n console.error(`[TokenManager] No refresh token available for ${providerId}`);\n return null;\n }\n\n // Create and store the refresh promise\n const refreshPromise = this.executeRefresh(providerId, credentials.refreshToken);\n this.pendingRefreshes.set(providerId, refreshPromise);\n\n try {\n return await refreshPromise;\n } finally {\n // Clean up pending refresh\n this.pendingRefreshes.delete(providerId);\n }\n }\n\n /**\n * Execute the actual token refresh operation.\n *\n * @param providerId - The provider identifier\n * @param refreshToken - The refresh token to use\n * @returns The new access token or null if refresh failed\n */\n private async executeRefresh(\n providerId: AuthProviderId,\n refreshToken: string\n ): Promise<string | null> {\n const provider = this.providerResolver(providerId);\n\n if (!provider) {\n console.error(`[TokenManager] Provider not found for ${providerId}`);\n return null;\n }\n\n try {\n console.error(`[TokenManager] Refreshing token for ${providerId}`);\n\n const tokenResponse = await provider.refreshToken(refreshToken);\n\n // Store the new tokens (handles refresh token rotation)\n await this.storeTokens(providerId, tokenResponse);\n\n console.error(`[TokenManager] Token refreshed successfully for ${providerId}`);\n return tokenResponse.accessToken;\n } catch (error) {\n // Sanitize error logging to avoid leaking sensitive information\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n // Remove any potential tokens or secrets from error message\n const sanitizedMessage = errorMessage\n .replace(/[A-Za-z0-9_-]{20,}/g, '[REDACTED]')\n .replace(/Bearer\\s+\\S+/gi, 'Bearer [REDACTED]');\n console.error(`[TokenManager] Token refresh failed for ${providerId}: ${sanitizedMessage}`);\n\n // Clear credentials on refresh failure (Requirement 6.3)\n // This signals that re-authentication is required\n await this.credentialStore.delete(providerId);\n\n return null;\n }\n }\n}\n\n/**\n * Create a token manager with the given options.\n *\n * @param options - Configuration options\n * @returns A new token manager instance\n */\nexport function createTokenManager(options: TokenManagerOptions): TokenManager {\n return new TokenManager(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Base OAuth 2.1 provider implementation.\n *\n * Provides common OAuth 2.1 URL building logic and HTTPS endpoint validation.\n *\n * @module providers/base-provider\n */\n\nimport type {\n AuthProviderId,\n AuthorizationParams,\n TokenResponse,\n TokenInjectionMethod,\n ProviderEndpoints,\n} from '../types.js';\nimport type { IAuthProvider } from './types.js';\n\n/**\n * Configuration for a base auth provider.\n */\nexport interface BaseProviderConfig {\n /** Unique provider identifier */\n id: AuthProviderId;\n /** Human-readable provider name */\n name: string;\n /** OAuth authorization endpoint URL */\n authorizationEndpoint: string;\n /** OAuth token endpoint URL */\n tokenEndpoint: string;\n /** Default scopes for this provider */\n defaultScopes: string[];\n /** Token injection method for agent requests */\n tokenInjection: TokenInjectionMethod;\n /** Client ID for OAuth flow */\n clientId?: string;\n /** Client secret for OAuth flow (optional, for confidential clients) */\n clientSecret?: string;\n}\n\n/**\n * Abstract base class for OAuth 2.1 providers.\n *\n * Implements common OAuth 2.1 functionality:\n * - Authorization URL building with required parameters\n * - HTTPS endpoint validation\n * - Token exchange and refresh\n *\n * Requirements: 3.2, 3.6, 7.5\n */\nexport abstract class BaseAuthProvider implements IAuthProvider {\n readonly id: AuthProviderId;\n readonly name: string;\n readonly defaultScopes: readonly string[];\n\n protected readonly authorizationEndpoint: string;\n protected readonly tokenEndpoint: string;\n protected readonly tokenInjection: TokenInjectionMethod;\n protected clientId?: string;\n protected clientSecret?: string;\n\n /** Default timeout for HTTP requests in milliseconds (30 seconds) */\n protected static readonly DEFAULT_REQUEST_TIMEOUT_MS = 30000;\n\n /**\n * Security-critical OAuth parameters that cannot be overridden by additionalParams.\n * These parameters are set by the OAuth flow and must not be tampered with.\n */\n private static readonly PROTECTED_PARAMS = new Set([\n 'client_id',\n 'redirect_uri',\n 'response_type',\n 'scope',\n 'state',\n 'code_challenge',\n 'code_challenge_method',\n 'code',\n 'code_verifier',\n 'grant_type',\n 'refresh_token',\n 'client_secret',\n ]);\n\n /**\n * Create a new base auth provider.\n * @param config - Provider configuration\n * @throws Error if endpoints are not HTTPS or contain embedded credentials\n */\n constructor(config: BaseProviderConfig) {\n this.id = config.id;\n this.name = config.name;\n this.authorizationEndpoint = config.authorizationEndpoint;\n this.tokenEndpoint = config.tokenEndpoint;\n this.defaultScopes = Object.freeze([...config.defaultScopes]);\n this.tokenInjection = config.tokenInjection;\n this.clientId = config.clientId;\n this.clientSecret = config.clientSecret;\n\n // Validate HTTPS endpoints at construction time\n this.validateConfig();\n }\n\n /**\n * Build the authorization URL for the OAuth flow.\n *\n * Includes all required OAuth 2.1 parameters:\n * - client_id\n * - redirect_uri\n * - response_type=code\n * - scope\n * - state\n * - code_challenge\n * - code_challenge_method=S256\n *\n * @param params - Authorization parameters\n * @returns The complete authorization URL\n * @throws Error if additionalParams attempts to override protected parameters\n */\n buildAuthorizationUrl(params: AuthorizationParams): string {\n const url = new URL(this.authorizationEndpoint);\n\n // Required OAuth 2.1 parameters\n url.searchParams.set('client_id', params.clientId);\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', params.responseType);\n url.searchParams.set('scope', params.scope);\n url.searchParams.set('state', params.state);\n\n // PKCE parameters (required for OAuth 2.1)\n url.searchParams.set('code_challenge', params.codeChallenge);\n url.searchParams.set('code_challenge_method', params.codeChallengeMethod);\n\n // Add any additional provider-specific parameters\n // Security: Reject attempts to override protected OAuth parameters\n if (params.additionalParams) {\n for (const [key, value] of Object.entries(params.additionalParams)) {\n if (BaseAuthProvider.PROTECTED_PARAMS.has(key.toLowerCase())) {\n throw new Error(\n `Security violation: additionalParams cannot override protected OAuth parameter '${key}'`\n );\n }\n url.searchParams.set(key, value);\n }\n }\n\n return url.toString();\n }\n\n /**\n * Exchange authorization code for tokens.\n *\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n * @param redirectUri - The redirect URI used in the authorization request\n * @returns The token response\n */\n async exchangeCode(\n code: string,\n codeVerifier: string,\n redirectUri: string\n ): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n });\n\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n\n // Use AbortController for timeout and prevent redirect following\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error', // Prevent following redirects for security\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token exchange timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token\n * @returns The new token response\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n });\n\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n\n // Use AbortController for timeout and prevent redirect following\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error', // Prevent following redirects for security\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token refresh timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Validate provider configuration.\n *\n * Ensures all endpoints use HTTPS (required for OAuth 2.1).\n *\n * @throws Error if configuration is invalid\n */\n validateConfig(): void {\n this.validateHttpsEndpoint(this.authorizationEndpoint, 'authorization');\n this.validateHttpsEndpoint(this.tokenEndpoint, 'token');\n }\n\n /**\n * Get token injection method for agent requests.\n *\n * @returns The token injection configuration\n */\n getTokenInjection(): TokenInjectionMethod {\n return this.tokenInjection;\n }\n\n /**\n * Get the provider endpoints.\n *\n * @returns The provider endpoint URLs\n */\n getEndpoints(): ProviderEndpoints {\n return {\n authorizationEndpoint: this.authorizationEndpoint,\n tokenEndpoint: this.tokenEndpoint,\n };\n }\n\n /**\n * Set the client credentials.\n *\n * @param clientId - The OAuth client ID\n * @param clientSecret - The OAuth client secret (optional)\n */\n setClientCredentials(clientId: string, clientSecret?: string): void {\n this.clientId = clientId;\n this.clientSecret = clientSecret;\n }\n\n /**\n * Validate that an endpoint uses HTTPS and has no embedded credentials.\n *\n * @param endpoint - The endpoint URL to validate\n * @param name - The name of the endpoint (for error messages)\n * @throws Error if the endpoint does not use HTTPS or contains embedded credentials\n */\n protected validateHttpsEndpoint(endpoint: string, name: string): void {\n const url = new URL(endpoint);\n if (url.protocol !== 'https:') {\n throw new Error(\n `${this.name} ${name} endpoint must use HTTPS: ${endpoint}`\n );\n }\n // Reject URLs with embedded credentials (username:password@host)\n if (url.username || url.password) {\n throw new Error(\n `${this.name} ${name} endpoint must not contain embedded credentials: ${endpoint}`\n );\n }\n }\n\n /**\n * Parse a token response from the provider.\n *\n * @param data - The raw response data\n * @returns The parsed token response\n */\n protected parseTokenResponse(data: Record<string, unknown>): TokenResponse {\n const accessToken = data.access_token;\n if (typeof accessToken !== 'string') {\n throw new Error('Invalid token response: missing access_token');\n }\n\n const tokenType = data.token_type;\n if (typeof tokenType !== 'string') {\n throw new Error('Invalid token response: missing token_type');\n }\n\n const response: TokenResponse = {\n accessToken,\n tokenType,\n };\n\n // Optional fields\n if (typeof data.expires_in === 'number') {\n response.expiresIn = data.expires_in;\n }\n\n if (typeof data.refresh_token === 'string') {\n response.refreshToken = data.refresh_token;\n }\n\n if (typeof data.scope === 'string') {\n response.scope = data.scope;\n }\n\n if (typeof data.id_token === 'string') {\n response.idToken = data.id_token;\n }\n\n return response;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * GitHub OAuth 2.1 provider implementation.\n *\n * @module providers/github-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * GitHub OAuth provider.\n *\n * Endpoints:\n * - Authorization: https://github.com/login/oauth/authorize\n * - Token: https://github.com/login/oauth/access_token\n *\n * Default scopes: read:user\n * Token injection: Bearer header\n */\nexport class GitHubProvider extends BaseAuthProvider {\n constructor(clientId?: string, clientSecret?: string) {\n super({\n id: 'github',\n name: 'GitHub',\n authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n tokenEndpoint: 'https://github.com/login/oauth/access_token',\n defaultScopes: ['read:user'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId,\n clientSecret,\n });\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Google OAuth 2.1 provider implementation.\n *\n * @module providers/google-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Google OAuth provider.\n *\n * Endpoints:\n * - Authorization: https://accounts.google.com/o/oauth2/v2/auth\n * - Token: https://oauth2.googleapis.com/token\n *\n * Default scopes: openid, profile, email\n * Token injection: Bearer header\n */\nexport class GoogleProvider extends BaseAuthProvider {\n constructor(clientId?: string, clientSecret?: string) {\n super({\n id: 'google',\n name: 'Google',\n authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenEndpoint: 'https://oauth2.googleapis.com/token',\n defaultScopes: ['openid', 'profile', 'email'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId,\n clientSecret,\n });\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * AWS Cognito OAuth 2.1 provider implementation.\n *\n * @module providers/cognito-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Configuration options for Cognito provider.\n */\nexport interface CognitoProviderConfig {\n /** Cognito user pool domain (e.g., 'my-app' for my-app.auth.us-east-1.amazoncognito.com) */\n userPoolDomain: string;\n /** AWS region (e.g., 'us-east-1') */\n region: string;\n /** OAuth client ID */\n clientId?: string;\n /** OAuth client secret (optional) */\n clientSecret?: string;\n}\n\n/**\n * Pattern for valid Cognito user pool domain names.\n * Must be alphanumeric with hyphens, no leading/trailing hyphens.\n */\nconst VALID_DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i;\n\n/**\n * Pattern for valid AWS region names.\n * Format: xx-xxxx-N (e.g., us-east-1, eu-west-2)\n */\nconst VALID_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\\d+$/;\n\n/**\n * AWS Cognito OAuth provider.\n *\n * Endpoints are dynamically constructed based on user pool domain and region:\n * - Authorization: https://{domain}.auth.{region}.amazoncognito.com/oauth2/authorize\n * - Token: https://{domain}.auth.{region}.amazoncognito.com/oauth2/token\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n */\nexport class CognitoProvider extends BaseAuthProvider {\n constructor(config: CognitoProviderConfig) {\n // Validate userPoolDomain to prevent URL injection\n CognitoProvider.validateUserPoolDomain(config.userPoolDomain);\n // Validate region to prevent URL injection\n CognitoProvider.validateRegion(config.region);\n\n const baseUrl = `https://${config.userPoolDomain}.auth.${config.region}.amazoncognito.com`;\n\n super({\n id: 'cognito',\n name: 'AWS Cognito',\n authorizationEndpoint: `${baseUrl}/oauth2/authorize`,\n tokenEndpoint: `${baseUrl}/oauth2/token`,\n defaultScopes: ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n }\n\n /**\n * Validate Cognito user pool domain name.\n * @param domain - The user pool domain to validate\n * @throws Error if domain is invalid or contains injection characters\n */\n private static validateUserPoolDomain(domain: string): void {\n if (!domain || typeof domain !== 'string') {\n throw new Error('Cognito userPoolDomain is required');\n }\n\n const trimmed = domain.trim();\n if (trimmed !== domain) {\n throw new Error('Cognito userPoolDomain must not contain leading/trailing whitespace');\n }\n\n if (domain.length === 0) {\n throw new Error('Cognito userPoolDomain cannot be empty');\n }\n\n if (domain.length > 63) {\n throw new Error('Cognito userPoolDomain must be 63 characters or less');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(domain)) {\n throw new Error('Cognito userPoolDomain contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n if (!VALID_DOMAIN_PATTERN.test(domain)) {\n throw new Error(\n 'Cognito userPoolDomain must be alphanumeric with hyphens, no leading/trailing hyphens'\n );\n }\n }\n\n /**\n * Validate AWS region name.\n * @param region - The AWS region to validate\n * @throws Error if region is invalid or contains injection characters\n */\n private static validateRegion(region: string): void {\n if (!region || typeof region !== 'string') {\n throw new Error('Cognito region is required');\n }\n\n const trimmed = region.trim();\n if (trimmed !== region) {\n throw new Error('Cognito region must not contain leading/trailing whitespace');\n }\n\n if (region.length === 0) {\n throw new Error('Cognito region cannot be empty');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(region)) {\n throw new Error('Cognito region contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n if (!VALID_REGION_PATTERN.test(region)) {\n throw new Error(\n 'Cognito region must be a valid AWS region format (e.g., us-east-1, eu-west-2)'\n );\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Microsoft Entra ID (formerly Azure AD) OAuth 2.1 provider implementation.\n *\n * @module providers/entra-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Configuration options for Microsoft Entra ID provider.\n * \n * @remarks\n * Microsoft renamed Azure AD to Microsoft Entra ID in 2023.\n * This provider supports both single-tenant and multi-tenant configurations.\n */\nexport interface EntraProviderConfig {\n /** Microsoft Entra ID tenant ID or 'common' for multi-tenant */\n tenantId: string;\n /** OAuth client ID */\n clientId?: string;\n /** OAuth client secret (optional) */\n clientSecret?: string;\n}\n\n/**\n * @deprecated Use EntraProviderConfig instead. Kept for backward compatibility.\n */\nexport type AzureProviderConfig = EntraProviderConfig;\n\n/**\n * Well-known Microsoft Entra ID tenant values for multi-tenant scenarios.\n */\nconst WELL_KNOWN_TENANTS = new Set(['common', 'organizations', 'consumers']);\n\n/**\n * Pattern for valid Microsoft Entra ID tenant GUIDs.\n * Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n */\nconst GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Pattern for valid Microsoft Entra ID verified domain names.\n * Must be a valid domain format without URL injection characters.\n */\nconst DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;\n\n/**\n * Microsoft Entra ID (formerly Azure AD) OAuth provider.\n *\n * Endpoints are dynamically constructed based on tenant ID:\n * - Authorization: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize\n * - Token: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token\n *\n * Use 'common' for multi-tenant applications.\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n * \n * @remarks\n * The provider ID remains 'azure' for backward compatibility with existing\n * configurations and the ACP Registry.\n */\nexport class EntraIdProvider extends BaseAuthProvider {\n constructor(config: EntraProviderConfig) {\n // Validate tenantId to prevent URL injection\n EntraIdProvider.validateTenantId(config.tenantId);\n\n const baseUrl = `https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0`;\n\n super({\n id: 'azure', // Keep 'azure' as provider ID for backward compatibility\n name: 'Microsoft Entra ID',\n authorizationEndpoint: `${baseUrl}/authorize`,\n tokenEndpoint: `${baseUrl}/token`,\n defaultScopes: ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n }\n\n /**\n * Validate Microsoft Entra ID tenant ID.\n * @param tenantId - The tenant ID to validate\n * @throws Error if tenantId is invalid or contains injection characters\n */\n private static validateTenantId(tenantId: string): void {\n if (!tenantId || typeof tenantId !== 'string') {\n throw new Error('Entra ID tenantId is required');\n }\n\n const trimmed = tenantId.trim();\n if (trimmed !== tenantId) {\n throw new Error('Entra ID tenantId must not contain leading/trailing whitespace');\n }\n\n if (tenantId.length === 0) {\n throw new Error('Entra ID tenantId cannot be empty');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(tenantId)) {\n throw new Error('Entra ID tenantId contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n // Accept well-known tenant values\n if (WELL_KNOWN_TENANTS.has(tenantId.toLowerCase())) {\n return;\n }\n\n // Accept valid GUIDs\n if (GUID_PATTERN.test(tenantId)) {\n return;\n }\n\n // Accept valid domain names (verified domains)\n if (DOMAIN_PATTERN.test(tenantId)) {\n return;\n }\n\n throw new Error(\n `Entra ID tenantId must be 'common', 'organizations', 'consumers', a valid GUID, or a verified domain name`\n );\n }\n}\n\n/**\n * @deprecated Use EntraIdProvider instead. Kept for backward compatibility.\n */\nexport const AzureProvider = EntraIdProvider;\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Generic OIDC Discovery provider implementation.\n *\n * Supports any OIDC-compliant provider via issuer-based discovery\n * (.well-known/openid-configuration) with manual endpoint override fallback.\n *\n * Requirements: 7a.1, 7a.2, 7a.3, 7a.5, 7a.6\n *\n * @module providers/oidc-provider\n */\n\nimport * as crypto from 'crypto';\nimport { BaseAuthProvider } from './base-provider.js';\nimport type { TokenResponse } from '../types.js';\n\n// =============================================================================\n// JWKS Types\n// =============================================================================\n\n/**\n * JSON Web Key (JWK) structure for RSA keys.\n */\nexport interface JWK {\n /** Key type (e.g., 'RSA') */\n kty: string;\n /** Key ID - used to match keys in JWKS */\n kid?: string;\n /** Algorithm (e.g., 'RS256') */\n alg?: string;\n /** Key use (e.g., 'sig' for signature) */\n use?: string;\n /** RSA modulus (base64url encoded) */\n n?: string;\n /** RSA exponent (base64url encoded) */\n e?: string;\n /** X.509 certificate chain */\n x5c?: string[];\n}\n\n/**\n * JSON Web Key Set (JWKS) structure.\n */\nexport interface JWKS {\n /** Array of JSON Web Keys */\n keys: JWK[];\n}\n\n/**\n * Cached JWKS with metadata.\n */\nexport interface CachedJWKS {\n /** The JWKS data */\n jwks: JWKS;\n /** Timestamp when the JWKS was fetched */\n fetchedAt: number;\n /** TTL in milliseconds */\n ttlMs: number;\n}\n\n/**\n * Decoded JWT header.\n */\nexport interface JWTHeader {\n /** Algorithm used for signing */\n alg: string;\n /** Token type (usually 'JWT') */\n typ?: string;\n /** Key ID - used to find the signing key in JWKS */\n kid?: string;\n}\n\n/**\n * ID Token claims structure.\n * Requirements: 7a.5\n */\nexport interface IDTokenClaims {\n /** Issuer - must match the configured issuer */\n iss: string;\n /** Subject - unique identifier for the user */\n sub: string;\n /** Audience - must contain the client_id */\n aud: string | string[];\n /** Expiration time (Unix timestamp) */\n exp: number;\n /** Issued at time (Unix timestamp) */\n iat: number;\n /** Nonce - if provided in auth request, must match */\n nonce?: string;\n /** Authentication time */\n auth_time?: number;\n /** Access token hash */\n at_hash?: string;\n /** Additional claims */\n [key: string]: unknown;\n}\n\n/**\n * Result of ID token validation.\n */\nexport interface IDTokenValidationResult {\n /** Whether validation was successful */\n valid: boolean;\n /** The decoded claims if valid */\n claims?: IDTokenClaims;\n /** Error message if validation failed */\n error?: string;\n}\n\n/**\n * Options for ID token validation.\n */\nexport interface IDTokenValidationOptions {\n /** Expected audience (client_id) */\n audience: string;\n /** Expected nonce (if used in auth request) */\n nonce?: string;\n /** Clock skew tolerance in seconds (default: 60) */\n clockSkewSeconds?: number;\n}\n\n/**\n * OIDC Discovery document structure.\n * Contains the endpoints and capabilities advertised by the OIDC provider.\n */\nexport interface OIDCDiscoveryDocument {\n /** The issuer identifier (must match the issuer URL) */\n issuer: string;\n /** URL of the authorization endpoint */\n authorization_endpoint: string;\n /** URL of the token endpoint */\n token_endpoint: string;\n /** URL of the JWKS endpoint for token validation */\n jwks_uri?: string;\n /** URL of the userinfo endpoint */\n userinfo_endpoint?: string;\n /** Supported response types */\n response_types_supported?: string[];\n /** Supported grant types */\n grant_types_supported?: string[];\n /** Supported scopes */\n scopes_supported?: string[];\n /** Supported token endpoint authentication methods */\n token_endpoint_auth_methods_supported?: string[];\n /** Supported code challenge methods for PKCE */\n code_challenge_methods_supported?: string[];\n}\n\n/**\n * Configuration options for OIDC provider.\n */\nexport interface OIDCProviderConfig {\n /**\n * The OIDC issuer URL (e.g., 'https://auth.example.com').\n * Used for discovery via {issuer}/.well-known/openid-configuration.\n */\n issuer: string;\n\n /**\n * Manual override for authorization endpoint.\n * Used when discovery is unavailable.\n * Requirements: 7a.2\n */\n authorizationEndpoint?: string;\n\n /**\n * Manual override for token endpoint.\n * Used when discovery is unavailable.\n * Requirements: 7a.2\n */\n tokenEndpoint?: string;\n\n /**\n * Manual override for JWKS URI.\n * Used for token validation when discovery is unavailable.\n */\n jwksUri?: string;\n\n /** OAuth client ID */\n clientId?: string;\n\n /** OAuth client secret (optional, for confidential clients) */\n clientSecret?: string;\n\n /**\n * Token endpoint authentication method.\n * Supported: 'client_secret_post', 'client_secret_basic'\n * Default: 'client_secret_post'\n * Requirements: 7a.7\n */\n tokenEndpointAuthMethod?: 'client_secret_post' | 'client_secret_basic';\n\n /**\n * Custom scopes to use instead of defaults.\n * Default: ['openid', 'profile']\n */\n scopes?: string[];\n\n /**\n * Whether to skip discovery and use manual endpoints only.\n * Default: false (discovery is attempted first)\n */\n skipDiscovery?: boolean;\n\n /**\n * Timeout for discovery request in milliseconds.\n * Default: 10000 (10 seconds)\n */\n discoveryTimeoutMs?: number;\n}\n\n/**\n * Result of OIDC discovery operation.\n */\nexport interface OIDCDiscoveryResult {\n /** Whether discovery was successful */\n success: boolean;\n /** The discovery document if successful */\n document?: OIDCDiscoveryDocument;\n /** Error message if discovery failed */\n error?: string;\n}\n\n/**\n * Generic OIDC Discovery provider.\n *\n * Supports any OIDC-compliant provider (Auth0, Okta, Keycloak, etc.)\n * via issuer-based discovery with manual endpoint override fallback.\n *\n * Features:\n * - Automatic discovery via .well-known/openid-configuration\n * - Manual endpoint override when discovery unavailable\n * - PKCE S256 enforcement (per Requirement 7a.3)\n * - Cached discovery document\n * - Support for client_secret_post and client_secret_basic auth methods\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n *\n * Requirements: 7a.1, 7a.2, 7a.3, 7a.7\n */\nexport class OIDCProvider extends BaseAuthProvider {\n private readonly issuer: string;\n private readonly tokenEndpointAuthMethod: 'client_secret_post' | 'client_secret_basic';\n private readonly discoveryTimeoutMs: number;\n private readonly skipDiscovery: boolean;\n private readonly manualJwksUri?: string;\n\n /** Discovered authorization endpoint (overrides base class endpoint after discovery) */\n private discoveredAuthorizationEndpoint?: string;\n\n /** Discovered token endpoint (overrides base class endpoint after discovery) */\n private discoveredTokenEndpoint?: string;\n\n /** Cached discovery document */\n private discoveryDocument?: OIDCDiscoveryDocument;\n\n /** Whether discovery has been attempted */\n private discoveryAttempted = false;\n\n /** Cached JWKS for token validation */\n private cachedJWKS?: CachedJWKS;\n\n /** Default timeout for discovery requests (10 seconds) */\n private static readonly DEFAULT_DISCOVERY_TIMEOUT_MS = 10000;\n\n /** Default JWKS cache TTL (1 hour) */\n private static readonly DEFAULT_JWKS_CACHE_TTL_MS = 60 * 60 * 1000;\n\n /** Default clock skew tolerance for token validation (60 seconds) */\n private static readonly DEFAULT_CLOCK_SKEW_SECONDS = 60;\n\n constructor(config: OIDCProviderConfig) {\n // Validate issuer URL\n OIDCProvider.validateIssuer(config.issuer);\n\n // Determine initial endpoints - use manual overrides or placeholders for discovery\n const authorizationEndpoint = config.authorizationEndpoint ||\n `${config.issuer}/authorize`;\n const tokenEndpoint = config.tokenEndpoint ||\n `${config.issuer}/oauth/token`;\n\n // Validate manual endpoints if provided\n if (config.authorizationEndpoint) {\n OIDCProvider.validateEndpoint(config.authorizationEndpoint, 'authorization');\n }\n if (config.tokenEndpoint) {\n OIDCProvider.validateEndpoint(config.tokenEndpoint, 'token');\n }\n if (config.jwksUri) {\n OIDCProvider.validateEndpoint(config.jwksUri, 'jwks');\n }\n\n super({\n id: 'oidc',\n name: 'Generic OIDC',\n authorizationEndpoint,\n tokenEndpoint,\n defaultScopes: config.scopes || ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n\n this.issuer = config.issuer;\n this.tokenEndpointAuthMethod = config.tokenEndpointAuthMethod || 'client_secret_post';\n this.discoveryTimeoutMs = config.discoveryTimeoutMs || OIDCProvider.DEFAULT_DISCOVERY_TIMEOUT_MS;\n this.skipDiscovery = config.skipDiscovery || false;\n this.manualJwksUri = config.jwksUri;\n\n // If manual endpoints are provided, mark discovery as not needed\n if (config.authorizationEndpoint && config.tokenEndpoint) {\n this.discoveryAttempted = true;\n }\n }\n\n /**\n * Validate the issuer URL.\n * @param issuer - The issuer URL to validate\n * @throws Error if issuer is invalid\n */\n private static validateIssuer(issuer: string): void {\n if (!issuer || typeof issuer !== 'string') {\n throw new Error('OIDC issuer is required');\n }\n\n const trimmed = issuer.trim();\n if (trimmed !== issuer) {\n throw new Error('OIDC issuer must not contain leading/trailing whitespace');\n }\n\n if (issuer.length === 0) {\n throw new Error('OIDC issuer cannot be empty');\n }\n\n // Parse and validate URL\n let url: URL;\n try {\n url = new URL(issuer);\n } catch {\n throw new Error(`OIDC issuer must be a valid URL: ${issuer}`);\n }\n\n // Must be HTTPS\n if (url.protocol !== 'https:') {\n throw new Error(`OIDC issuer must use HTTPS: ${issuer}`);\n }\n\n // Must not have embedded credentials\n if (url.username || url.password) {\n throw new Error('OIDC issuer must not contain embedded credentials');\n }\n\n // Must not have query string or fragment\n if (url.search || url.hash) {\n throw new Error('OIDC issuer must not contain query string or fragment');\n }\n }\n\n /**\n * Validate an endpoint URL.\n * @param endpoint - The endpoint URL to validate\n * @param name - The name of the endpoint for error messages\n * @throws Error if endpoint is invalid\n */\n private static validateEndpoint(endpoint: string, name: string): void {\n if (!endpoint || typeof endpoint !== 'string') {\n throw new Error(`OIDC ${name} endpoint is required`);\n }\n\n let url: URL;\n try {\n url = new URL(endpoint);\n } catch {\n throw new Error(`OIDC ${name} endpoint must be a valid URL: ${endpoint}`);\n }\n\n if (url.protocol !== 'https:') {\n throw new Error(`OIDC ${name} endpoint must use HTTPS: ${endpoint}`);\n }\n\n if (url.username || url.password) {\n throw new Error(`OIDC ${name} endpoint must not contain embedded credentials`);\n }\n }\n\n /**\n * Get the issuer URL.\n * @returns The issuer URL\n */\n getIssuer(): string {\n return this.issuer;\n }\n\n /**\n * Get the JWKS URI for token validation.\n * Returns the discovered or manually configured JWKS URI.\n * @returns The JWKS URI or undefined if not available\n */\n getJwksUri(): string | undefined {\n return this.discoveryDocument?.jwks_uri || this.manualJwksUri;\n }\n\n /**\n * Get the cached discovery document.\n * @returns The discovery document or undefined if not discovered\n */\n getDiscoveryDocument(): OIDCDiscoveryDocument | undefined {\n return this.discoveryDocument;\n }\n\n /**\n * Check if discovery has been performed.\n * @returns True if discovery was attempted\n */\n isDiscoveryAttempted(): boolean {\n return this.discoveryAttempted;\n }\n\n /**\n * Get the effective authorization endpoint.\n * Returns discovered endpoint if available, otherwise the initial endpoint.\n * @returns The authorization endpoint URL\n */\n getAuthorizationEndpoint(): string {\n return this.discoveredAuthorizationEndpoint || this.getEndpoints().authorizationEndpoint;\n }\n\n /**\n * Get the effective token endpoint.\n * Returns discovered endpoint if available, otherwise the initial endpoint.\n * @returns The token endpoint URL\n */\n getTokenEndpoint(): string {\n return this.discoveredTokenEndpoint || this.getEndpoints().tokenEndpoint;\n }\n\n /**\n * Perform OIDC discovery by fetching the .well-known/openid-configuration.\n *\n * This method fetches and parses the discovery document, updating the\n * provider's endpoints if successful.\n *\n * Requirements: 7a.1\n *\n * @returns The discovery result\n */\n async discover(): Promise<OIDCDiscoveryResult> {\n if (this.skipDiscovery) {\n return {\n success: false,\n error: 'Discovery skipped by configuration',\n };\n }\n\n const discoveryUrl = `${this.issuer}/.well-known/openid-configuration`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.discoveryTimeoutMs);\n\n try {\n const response = await fetch(discoveryUrl, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n signal: controller.signal,\n redirect: 'error', // Don't follow redirects for security\n });\n\n if (!response.ok) {\n this.discoveryAttempted = true;\n return {\n success: false,\n error: `Discovery failed: HTTP ${response.status} ${response.statusText}`,\n };\n }\n\n const document = await response.json() as OIDCDiscoveryDocument;\n\n // Validate required fields\n const validationError = this.validateDiscoveryDocument(document);\n if (validationError) {\n this.discoveryAttempted = true;\n return {\n success: false,\n error: validationError,\n };\n }\n\n // Cache the discovery document\n this.discoveryDocument = document;\n this.discoveryAttempted = true;\n\n // Store discovered endpoints\n this.discoveredAuthorizationEndpoint = document.authorization_endpoint;\n this.discoveredTokenEndpoint = document.token_endpoint;\n\n return {\n success: true,\n document,\n };\n } catch (error) {\n this.discoveryAttempted = true;\n\n if (error instanceof Error && error.name === 'AbortError') {\n return {\n success: false,\n error: `Discovery timed out after ${this.discoveryTimeoutMs}ms`,\n };\n }\n\n return {\n success: false,\n error: `Discovery failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Validate the discovery document.\n * @param document - The discovery document to validate\n * @returns Error message if invalid, undefined if valid\n */\n private validateDiscoveryDocument(document: OIDCDiscoveryDocument): string | undefined {\n if (!document.issuer) {\n return 'Discovery document missing required field: issuer';\n }\n\n if (!document.authorization_endpoint) {\n return 'Discovery document missing required field: authorization_endpoint';\n }\n\n if (!document.token_endpoint) {\n return 'Discovery document missing required field: token_endpoint';\n }\n\n // Validate issuer matches\n // Note: Some providers may have trailing slash differences\n const normalizedDocIssuer = document.issuer.replace(/\\/$/, '');\n const normalizedConfigIssuer = this.issuer.replace(/\\/$/, '');\n if (normalizedDocIssuer !== normalizedConfigIssuer) {\n return `Discovery document issuer mismatch: expected ${this.issuer}, got ${document.issuer}`;\n }\n\n // Validate endpoints are HTTPS\n try {\n OIDCProvider.validateEndpoint(document.authorization_endpoint, 'authorization');\n OIDCProvider.validateEndpoint(document.token_endpoint, 'token');\n if (document.jwks_uri) {\n OIDCProvider.validateEndpoint(document.jwks_uri, 'jwks');\n }\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n\n return undefined;\n }\n\n /**\n * Ensure discovery has been performed before operations that need endpoints.\n * If discovery hasn't been attempted and manual endpoints weren't provided,\n * this will perform discovery.\n */\n async ensureDiscovered(): Promise<void> {\n if (!this.discoveryAttempted && !this.skipDiscovery) {\n await this.discover();\n }\n }\n\n /**\n * Exchange authorization code for tokens.\n *\n * Overrides base implementation to support different token endpoint\n * authentication methods (client_secret_post, client_secret_basic).\n *\n * Requirements: 7a.7\n *\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n * @param redirectUri - The redirect URI used in the authorization request\n * @returns The token response\n */\n override async exchangeCode(\n code: string,\n codeVerifier: string,\n redirectUri: string\n ): Promise<TokenResponse> {\n // Ensure discovery has been performed\n await this.ensureDiscovered();\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n };\n\n // Handle different token endpoint auth methods\n if (this.tokenEndpointAuthMethod === 'client_secret_basic') {\n // client_secret_basic: credentials in Authorization header\n if (this.clientId && this.clientSecret) {\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');\n headers['Authorization'] = `Basic ${credentials}`;\n }\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n } else {\n // client_secret_post: credentials in request body (default)\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n // Use discovered endpoint if available\n const tokenEndpoint = this.getTokenEndpoint();\n\n try {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers,\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token exchange timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * Overrides base implementation to support different token endpoint\n * authentication methods.\n *\n * @param refreshToken - The refresh token\n * @returns The new token response\n */\n override async refreshToken(refreshToken: string): Promise<TokenResponse> {\n // Ensure discovery has been performed\n await this.ensureDiscovered();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n };\n\n // Handle different token endpoint auth methods\n if (this.tokenEndpointAuthMethod === 'client_secret_basic') {\n if (this.clientId && this.clientSecret) {\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');\n headers['Authorization'] = `Basic ${credentials}`;\n }\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n } else {\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n // Use discovered endpoint if available\n const tokenEndpoint = this.getTokenEndpoint();\n\n try {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers,\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token refresh timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // =============================================================================\n // JWKS Retrieval and Caching (Requirements: 7a.6)\n // =============================================================================\n\n /**\n * Fetch JWKS from the jwks_uri endpoint.\n *\n * Requirements: 7a.6\n *\n * @param forceRefresh - If true, bypasses cache and fetches fresh JWKS\n * @returns The JWKS or null if unavailable\n */\n async fetchJWKS(forceRefresh = false): Promise<JWKS | null> {\n // Check cache first (unless force refresh)\n if (!forceRefresh && this.cachedJWKS) {\n const now = Date.now();\n const cacheAge = now - this.cachedJWKS.fetchedAt;\n if (cacheAge < this.cachedJWKS.ttlMs) {\n return this.cachedJWKS.jwks;\n }\n }\n\n // Ensure discovery has been performed to get jwks_uri\n await this.ensureDiscovered();\n\n const jwksUri = this.getJwksUri();\n if (!jwksUri) {\n return null;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.discoveryTimeoutMs);\n\n try {\n const response = await fetch(jwksUri, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n throw new Error(`JWKS fetch failed: HTTP ${response.status} ${response.statusText}`);\n }\n\n const jwks = await response.json() as JWKS;\n\n // Validate JWKS structure\n if (!jwks.keys || !Array.isArray(jwks.keys)) {\n throw new Error('Invalid JWKS: missing or invalid keys array');\n }\n\n // Cache the JWKS\n this.cachedJWKS = {\n jwks,\n fetchedAt: Date.now(),\n ttlMs: OIDCProvider.DEFAULT_JWKS_CACHE_TTL_MS,\n };\n\n return jwks;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`JWKS fetch timed out after ${this.discoveryTimeoutMs}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Find a key in the JWKS by key ID (kid).\n *\n * If the key is not found in the cache, attempts to refresh the JWKS\n * to handle key rotation.\n *\n * Requirements: 7a.6 (key rotation handling)\n *\n * @param kid - The key ID to find\n * @returns The JWK or null if not found\n */\n async findKey(kid: string): Promise<JWK | null> {\n // First, try to find in cached JWKS\n let jwks = await this.fetchJWKS(false);\n if (jwks) {\n const key = jwks.keys.find(k => k.kid === kid);\n if (key) {\n return key;\n }\n }\n\n // Key not found - might be key rotation, refresh JWKS\n jwks = await this.fetchJWKS(true);\n if (jwks) {\n const key = jwks.keys.find(k => k.kid === kid);\n if (key) {\n return key;\n }\n }\n\n return null;\n }\n\n /**\n * Clear the JWKS cache.\n * Useful for testing or when key rotation is detected.\n */\n clearJWKSCache(): void {\n this.cachedJWKS = undefined;\n }\n\n /**\n * Get the cached JWKS if available.\n * @returns The cached JWKS or undefined\n */\n getCachedJWKS(): CachedJWKS | undefined {\n return this.cachedJWKS;\n }\n\n // =============================================================================\n // ID Token Validation (Requirements: 7a.5)\n // =============================================================================\n\n /**\n * Validate an ID token.\n *\n * Validates the following claims per OIDC Core spec:\n * - iss: Must match the configured issuer\n * - aud: Must contain the client_id\n * - exp: Must not be expired\n * - iat: Must be present and reasonable\n *\n * Also validates the JWT signature using JWKS.\n *\n * Requirements: 7a.5, 7a.6\n *\n * @param idToken - The ID token to validate\n * @param options - Validation options\n * @returns The validation result\n */\n async validateIdToken(\n idToken: string,\n options: IDTokenValidationOptions\n ): Promise<IDTokenValidationResult> {\n try {\n // Split the JWT into parts\n const parts = idToken.split('.');\n if (parts.length !== 3) {\n return { valid: false, error: 'Invalid JWT format: expected 3 parts' };\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode header\n let header: JWTHeader;\n try {\n header = JSON.parse(OIDCProvider.base64UrlDecode(headerB64));\n } catch {\n return { valid: false, error: 'Invalid JWT header: failed to decode' };\n }\n\n // Decode payload (claims)\n let claims: IDTokenClaims;\n try {\n claims = JSON.parse(OIDCProvider.base64UrlDecode(payloadB64));\n } catch {\n return { valid: false, error: 'Invalid JWT payload: failed to decode' };\n }\n\n // Validate signature\n const signatureValid = await this.validateJWTSignature(\n headerB64,\n payloadB64,\n signatureB64,\n header\n );\n if (!signatureValid) {\n return { valid: false, error: 'Invalid JWT signature' };\n }\n\n // Validate claims\n const claimsValidation = this.validateIDTokenClaims(claims, options);\n if (!claimsValidation.valid) {\n return claimsValidation;\n }\n\n return { valid: true, claims };\n } catch (error) {\n return {\n valid: false,\n error: `Token validation failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n /**\n * Validate the JWT signature using JWKS.\n *\n * Requirements: 7a.6\n *\n * @param headerB64 - Base64url encoded header\n * @param payloadB64 - Base64url encoded payload\n * @param signatureB64 - Base64url encoded signature\n * @param header - Decoded JWT header\n * @returns True if signature is valid\n */\n private async validateJWTSignature(\n headerB64: string,\n payloadB64: string,\n signatureB64: string,\n header: JWTHeader\n ): Promise<boolean> {\n // Only support RS256 for now (most common)\n if (header.alg !== 'RS256') {\n throw new Error(`Unsupported algorithm: ${header.alg}. Only RS256 is supported.`);\n }\n\n // Find the signing key\n if (!header.kid) {\n throw new Error('JWT header missing kid (key ID)');\n }\n\n const key = await this.findKey(header.kid);\n if (!key) {\n throw new Error(`Key not found in JWKS: ${header.kid}`);\n }\n\n // Verify the key is RSA\n if (key.kty !== 'RSA') {\n throw new Error(`Unsupported key type: ${key.kty}. Only RSA is supported.`);\n }\n\n if (!key.n || !key.e) {\n throw new Error('Invalid RSA key: missing n or e');\n }\n\n // Convert JWK to PEM format for Node.js crypto\n const publicKey = OIDCProvider.jwkToPem(key);\n\n // Verify signature\n const signedData = `${headerB64}.${payloadB64}`;\n const signature = OIDCProvider.base64UrlToBuffer(signatureB64);\n\n const verifier = crypto.createVerify('RSA-SHA256');\n verifier.update(signedData);\n\n return verifier.verify(publicKey, signature);\n }\n\n /**\n * Validate ID token claims.\n *\n * Requirements: 7a.5\n *\n * @param claims - The decoded claims\n * @param options - Validation options\n * @returns The validation result\n */\n private validateIDTokenClaims(\n claims: IDTokenClaims,\n options: IDTokenValidationOptions\n ): IDTokenValidationResult {\n const clockSkew = options.clockSkewSeconds ?? OIDCProvider.DEFAULT_CLOCK_SKEW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\n\n // Validate iss (issuer)\n // Normalize trailing slashes for comparison\n const normalizedClaimsIss = claims.iss?.replace(/\\/$/, '');\n const normalizedIssuer = this.issuer.replace(/\\/$/, '');\n if (!claims.iss || normalizedClaimsIss !== normalizedIssuer) {\n return {\n valid: false,\n error: `Invalid issuer: expected ${this.issuer}, got ${claims.iss}`,\n };\n }\n\n // Validate aud (audience)\n const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];\n if (!audiences.includes(options.audience)) {\n return {\n valid: false,\n error: `Invalid audience: expected ${options.audience}, got ${claims.aud}`,\n };\n }\n\n // Validate exp (expiration)\n if (typeof claims.exp !== 'number') {\n return { valid: false, error: 'Missing or invalid exp claim' };\n }\n if (claims.exp + clockSkew < now) {\n return { valid: false, error: 'Token has expired' };\n }\n\n // Validate iat (issued at)\n if (typeof claims.iat !== 'number') {\n return { valid: false, error: 'Missing or invalid iat claim' };\n }\n // iat should not be in the future (with clock skew tolerance)\n if (claims.iat - clockSkew > now) {\n return { valid: false, error: 'Token issued in the future' };\n }\n\n // Validate nonce if provided\n if (options.nonce !== undefined) {\n if (claims.nonce !== options.nonce) {\n return {\n valid: false,\n error: `Invalid nonce: expected ${options.nonce}, got ${claims.nonce}`,\n };\n }\n }\n\n return { valid: true, claims };\n }\n\n // =============================================================================\n // Utility Methods\n // =============================================================================\n\n /**\n * Decode a base64url encoded string to UTF-8.\n *\n * @param input - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\n private static base64UrlDecode(input: string): string {\n // Replace base64url characters with base64 characters\n let base64 = input.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary\n const padding = base64.length % 4;\n if (padding) {\n base64 += '='.repeat(4 - padding);\n }\n\n return Buffer.from(base64, 'base64').toString('utf-8');\n }\n\n /**\n * Convert a base64url encoded string to a Buffer.\n *\n * @param input - Base64url encoded string\n * @returns Buffer\n */\n private static base64UrlToBuffer(input: string): Buffer {\n // Replace base64url characters with base64 characters\n let base64 = input.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary\n const padding = base64.length % 4;\n if (padding) {\n base64 += '='.repeat(4 - padding);\n }\n\n return Buffer.from(base64, 'base64');\n }\n\n /**\n * Convert a JWK RSA public key to PEM format.\n *\n * @param jwk - The JWK to convert\n * @returns PEM formatted public key\n */\n private static jwkToPem(jwk: JWK): string {\n if (!jwk.n || !jwk.e) {\n throw new Error('Invalid JWK: missing n or e');\n }\n\n // Use Node.js crypto to create the key from JWK\n const keyObject = crypto.createPublicKey({\n key: {\n kty: jwk.kty,\n n: jwk.n,\n e: jwk.e,\n },\n format: 'jwk',\n });\n\n return keyObject.export({ type: 'spki', format: 'pem' }) as string;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Provider registry and factory.\n *\n * Provides access to OAuth provider implementations.\n *\n * @module providers\n */\n\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId as isValidProviderIdFromTypes } from '../types.js';\nimport type { IAuthProvider } from './types.js';\n\n/**\n * Provider factory function type.\n */\nexport type ProviderFactory = () => IAuthProvider;\n\n/**\n * Internal registry of provider factories.\n */\nconst providerRegistry = new Map<AuthProviderId, ProviderFactory>();\n\n/**\n * List of supported OAuth provider IDs.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n */\nexport const SUPPORTED_PROVIDERS: readonly AuthProviderId[] = [\n 'github',\n 'google',\n 'cognito',\n 'azure',\n 'oidc',\n] as const;\n\n/**\n * Register a provider factory.\n *\n * @param providerId - The provider identifier\n * @param factory - Factory function that creates the provider\n * @throws Error if providerId is not a valid supported provider ID\n */\nexport function registerProvider(providerId: AuthProviderId, factory: ProviderFactory): void {\n // Runtime validation to ensure only valid provider IDs are registered\n if (!isValidProviderIdFromTypes(providerId)) {\n throw new Error(\n `Invalid provider ID: '${providerId}'. Supported providers: ${SUPPORTED_PROVIDERS.join(', ')}`\n );\n }\n providerRegistry.set(providerId, factory);\n}\n\n/**\n * Unregister a provider.\n *\n * @param providerId - The provider identifier to unregister\n * @returns True if the provider was unregistered\n */\nexport function unregisterProvider(providerId: AuthProviderId): boolean {\n return providerRegistry.delete(providerId);\n}\n\n/**\n * Clear all registered providers.\n * Useful for testing.\n */\nexport function clearProviders(): void {\n providerRegistry.clear();\n}\n\n/**\n * Get a provider implementation by ID.\n *\n * @param providerId - The provider identifier\n * @returns The provider implementation\n * @throws Error if provider is not registered\n */\nexport function getProvider(providerId: AuthProviderId): IAuthProvider {\n const factory = providerRegistry.get(providerId);\n if (!factory) {\n const supported = getRegisteredProviders().join(', ') || 'none';\n throw new Error(\n `Provider '${providerId}' is not registered. Registered providers: ${supported}`\n );\n }\n return factory();\n}\n\n/**\n * Check if a provider is registered.\n *\n * @param providerId - The provider identifier\n * @returns True if the provider is registered\n */\nexport function hasProvider(providerId: AuthProviderId): boolean {\n return providerRegistry.has(providerId);\n}\n\n/**\n * Get the list of registered provider IDs.\n *\n * @returns Array of registered provider identifiers\n */\nexport function getRegisteredProviders(): AuthProviderId[] {\n return Array.from(providerRegistry.keys());\n}\n\n/**\n * Get the list of supported provider IDs.\n *\n * @returns Array of supported provider identifiers\n */\nexport function getSupportedProviders(): readonly AuthProviderId[] {\n return SUPPORTED_PROVIDERS;\n}\n\n/**\n * Check if a provider ID is valid (supported).\n * Re-exports the centralized type guard from types.ts.\n *\n * @param providerId - The provider identifier to check\n * @returns True if the provider is supported\n */\nexport function isValidProviderId(providerId: unknown): providerId is AuthProviderId {\n return isValidProviderIdFromTypes(providerId);\n}\n\n/**\n * Check if a provider ID is registered and available.\n *\n * @param providerId - The provider identifier to check\n * @returns True if the provider is registered\n */\nexport function isProviderAvailable(providerId: string): boolean {\n return isValidProviderId(providerId) && hasProvider(providerId as AuthProviderId);\n}\n\n// Re-export types\nexport type { IAuthProvider } from './types.js';\nexport { BaseAuthProvider } from './base-provider.js';\nexport type { BaseProviderConfig } from './base-provider.js';\n\n// Re-export concrete providers\nexport { GitHubProvider } from './github-provider.js';\nexport { GoogleProvider } from './google-provider.js';\nexport { CognitoProvider } from './cognito-provider.js';\nexport type { CognitoProviderConfig } from './cognito-provider.js';\n// Microsoft Entra ID (formerly Azure AD) - export both new and legacy names\nexport { EntraIdProvider, AzureProvider } from './entra-provider.js';\nexport type { EntraProviderConfig, AzureProviderConfig } from './entra-provider.js';\nexport { OIDCProvider } from './oidc-provider.js';\nexport type { OIDCProviderConfig, OIDCDiscoveryDocument, OIDCDiscoveryResult } from './oidc-provider.js';\n\n// Import providers for registration\nimport { GitHubProvider } from './github-provider.js';\nimport { GoogleProvider } from './google-provider.js';\nimport { CognitoProvider } from './cognito-provider.js';\nimport { EntraIdProvider } from './entra-provider.js';\nimport { OIDCProvider } from './oidc-provider.js';\n\n/**\n * Initialize all OAuth providers.\n * Must be called before using AuthManager.\n *\n * This registers all supported OAuth provider implementations\n * in the provider registry.\n *\n * Note: OpenAI and Anthropic are NOT registered here - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n *\n * Note: Cognito, Azure, and OIDC require environment-specific configuration\n * and are only registered if their config is available via env vars.\n */\nexport function initializeProviders(): void {\n // Only register if not already registered (idempotent)\n if (!providerRegistry.has('github')) {\n registerProvider('github', () => new GitHubProvider());\n }\n if (!providerRegistry.has('google')) {\n registerProvider('google', () => new GoogleProvider());\n }\n\n // Cognito requires userPoolDomain and region from environment\n const cognitoUserPoolDomain = process.env.COGNITO_USER_POOL_DOMAIN;\n const cognitoRegion = process.env.COGNITO_REGION || 'us-east-1';\n if (cognitoUserPoolDomain && !providerRegistry.has('cognito')) {\n registerProvider('cognito', () => new CognitoProvider({\n userPoolDomain: cognitoUserPoolDomain,\n region: cognitoRegion,\n }));\n }\n\n // Microsoft Entra ID (formerly Azure AD) requires tenantId from environment\n const azureTenantId = process.env.AZURE_TENANT_ID;\n if (azureTenantId && !providerRegistry.has('azure')) {\n registerProvider('azure', () => new EntraIdProvider({\n tenantId: azureTenantId,\n }));\n }\n\n // OIDC requires issuer from environment\n // Manual endpoint overrides are optional (discovery is used by default)\n const oidcIssuer = process.env.OIDC_ISSUER;\n if (oidcIssuer && !providerRegistry.has('oidc')) {\n registerProvider('oidc', () => new OIDCProvider({\n issuer: oidcIssuer,\n authorizationEndpoint: process.env.OIDC_AUTHORIZATION_ENDPOINT,\n tokenEndpoint: process.env.OIDC_TOKEN_ENDPOINT,\n jwksUri: process.env.OIDC_JWKS_URI,\n clientId: process.env.OIDC_CLIENT_ID,\n clientSecret: process.env.OIDC_CLIENT_SECRET,\n tokenEndpointAuthMethod: process.env.OIDC_TOKEN_ENDPOINT_AUTH_METHOD as 'client_secret_post' | 'client_secret_basic' | undefined,\n }));\n }\n}\n\n// Auto-initialize providers on module load\ninitializeProviders();\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Type definitions for model API credentials.\n *\n * This module defines types for upstream model provider credentials (API keys).\n * These providers (OpenAI, Anthropic) do NOT offer public OAuth IdP for\n * third-party login - they use API keys instead.\n *\n * This separation clearly distinguishes:\n * - User identity (OAuth/OIDC): Google, Microsoft Entra ID, AWS Cognito, GitHub, Generic OIDC\n * - Model API access (API Keys): OpenAI, Anthropic\n *\n * Requirements: 7b.1, 7b.3\n *\n * @module model-credentials/types\n */\n\n// =============================================================================\n// Model Provider Types\n// =============================================================================\n\n/**\n * Model API provider identifiers.\n *\n * These providers use API keys for authentication, NOT OAuth.\n * They do not offer public OAuth IdP for third-party login.\n *\n * Requirements: 7b.1, 7b.2\n */\nexport type ModelProviderId = 'openai' | 'anthropic';\n\n/**\n * Valid model provider IDs for runtime validation.\n */\nexport const VALID_MODEL_PROVIDER_IDS: readonly ModelProviderId[] = [\n 'openai',\n 'anthropic',\n] as const;\n\n/**\n * Type guard to check if a value is a valid ModelProviderId.\n *\n * @param value - The value to check\n * @returns True if the value is a valid ModelProviderId\n */\nexport function isValidModelProviderId(value: unknown): value is ModelProviderId {\n return typeof value === 'string' && VALID_MODEL_PROVIDER_IDS.includes(value as ModelProviderId);\n}\n\n// =============================================================================\n// Model Credential Types\n// =============================================================================\n\n/**\n * Model credential for storing API keys.\n *\n * Represents an API key credential for a model provider.\n * These credentials are stored securely in the Credential_Store.\n *\n * Requirements: 7b.1, 7b.4\n */\nexport interface ModelCredential {\n /**\n * The model provider this credential is for.\n */\n providerId: ModelProviderId;\n\n /**\n * The API key value.\n * This is stored encrypted in the Credential_Store.\n */\n apiKey: string;\n\n /**\n * Optional human-readable label for this credential.\n * Useful when multiple keys are stored for the same provider.\n */\n label?: string;\n\n /**\n * Unix timestamp when this credential was stored.\n */\n storedAt: number;\n\n /**\n * Optional Unix timestamp when this credential expires.\n * Most API keys don't expire, but some providers may issue\n * time-limited keys.\n */\n expiresAt?: number;\n}\n\n// =============================================================================\n// Model Credential Injection Types\n// =============================================================================\n\n/**\n * Header injection type for model credentials.\n */\nexport interface HeaderInjection {\n type: 'header';\n /**\n * The header name to use.\n * e.g., 'Authorization' for OpenAI, 'x-api-key' for Anthropic\n */\n headerName: string;\n /**\n * Optional format string for the header value.\n * Use '{key}' as placeholder for the API key.\n * e.g., 'Bearer {key}' for OpenAI\n * If not provided, the API key is used directly.\n */\n format?: string;\n}\n\n/**\n * Model credential injection configuration.\n *\n * Defines how API keys should be injected into requests\n * for each model provider.\n *\n * Requirements: 7b.5\n */\nexport type ModelCredentialInjection = HeaderInjection;\n\n/**\n * Provider-specific injection configurations.\n *\n * OpenAI: Authorization header with Bearer token\n * Anthropic: x-api-key header with raw key\n *\n * Requirements: 7b.5\n */\nexport const MODEL_CREDENTIAL_INJECTION_CONFIG: Readonly<\n Record<ModelProviderId, ModelCredentialInjection>\n> = {\n openai: {\n type: 'header',\n headerName: 'Authorization',\n format: 'Bearer {key}',\n },\n anthropic: {\n type: 'header',\n headerName: 'x-api-key',\n // No format - raw key is used directly\n },\n} as const;\n\n// =============================================================================\n// Stored Model Credential Types\n// =============================================================================\n\n/**\n * Stored model credentials in the credential store.\n *\n * This is the format used when persisting model credentials\n * to the Credential_Store.\n *\n * Requirements: 7b.4\n */\nexport interface StoredModelCredential {\n /**\n * The model provider this credential is for.\n */\n providerId: ModelProviderId;\n\n /**\n * The encrypted API key value.\n * Encryption is handled by the storage backend.\n */\n apiKey: string;\n\n /**\n * Optional human-readable label.\n */\n label?: string;\n\n /**\n * Unix timestamp when this credential was stored.\n */\n storedAt: number;\n\n /**\n * Optional Unix timestamp when this credential expires.\n */\n expiresAt?: number;\n}\n\n// =============================================================================\n// Model Credential Result Types\n// =============================================================================\n\n/**\n * Result of a model credential retrieval operation.\n */\nexport interface ModelCredentialResult {\n /**\n * Whether the credential was found.\n */\n found: boolean;\n\n /**\n * The credential, if found.\n */\n credential?: ModelCredential;\n\n /**\n * Error message if retrieval failed.\n */\n error?: string;\n}\n\n// =============================================================================\n// Model Credential Status Types\n// =============================================================================\n\n/**\n * Status of a model credential.\n */\nexport type ModelCredentialStatus =\n | 'configured' // API key is stored and available\n | 'not-configured' // No API key stored\n | 'expired'; // API key has expired (if expiration is set)\n\n/**\n * Model credential status entry for display.\n */\nexport interface ModelCredentialStatusEntry {\n /**\n * The model provider.\n */\n providerId: ModelProviderId;\n\n /**\n * Current status of the credential.\n */\n status: ModelCredentialStatus;\n\n /**\n * Optional label for the credential.\n */\n label?: string;\n\n /**\n * Unix timestamp when the credential was stored.\n */\n storedAt?: number;\n\n /**\n * Unix timestamp when the credential expires.\n */\n expiresAt?: number;\n}\n\n/**\n * Map of model provider IDs to their credential status.\n */\nexport type ModelCredentialStatusMap = Map<ModelProviderId, ModelCredentialStatusEntry>;\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * OpenAI API Key Handler.\n *\n * Handles storage, retrieval, validation, and injection of OpenAI API keys.\n * OpenAI uses the Authorization header with Bearer token format.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n *\n * @module model-credentials/openai-api-key\n */\n\nimport type {\n ModelCredential,\n ModelProviderId,\n StoredModelCredential,\n ModelCredentialResult,\n ModelCredentialStatusEntry,\n HeaderInjection,\n} from './types.js';\nimport { MODEL_CREDENTIAL_INJECTION_CONFIG } from './types.js';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * The provider ID for OpenAI.\n */\nexport const OPENAI_PROVIDER_ID: ModelProviderId = 'openai';\n\n/**\n * OpenAI API key prefix for validation.\n * OpenAI API keys typically start with 'sk-'.\n */\nexport const OPENAI_API_KEY_PREFIX = 'sk-';\n\n/**\n * Minimum length for OpenAI API keys.\n * OpenAI keys are typically 51+ characters.\n */\nexport const OPENAI_API_KEY_MIN_LENGTH = 20;\n\n/**\n * Storage key prefix for OpenAI credentials.\n */\nexport const OPENAI_STORAGE_KEY = 'model-credential:openai';\n\n// =============================================================================\n// Storage Interface\n// =============================================================================\n\n/**\n * Interface for credential storage operations.\n * This allows the handler to work with any storage backend.\n */\nexport interface IModelCredentialStorage {\n /**\n * Store a model credential.\n * @param key - The storage key\n * @param credential - The credential to store\n */\n store(key: string, credential: StoredModelCredential): Promise<void>;\n\n /**\n * Retrieve a model credential.\n * @param key - The storage key\n * @returns The stored credential or null if not found\n */\n retrieve(key: string): Promise<StoredModelCredential | null>;\n\n /**\n * Delete a model credential.\n * @param key - The storage key\n */\n delete(key: string): Promise<void>;\n\n /**\n * Check if a credential exists.\n * @param key - The storage key\n * @returns True if the credential exists\n */\n exists(key: string): Promise<boolean>;\n}\n\n// =============================================================================\n// OpenAI API Key Handler\n// =============================================================================\n\n/**\n * OpenAI API Key Handler.\n *\n * Provides methods for storing, retrieving, validating, and injecting\n * OpenAI API keys. Integrates with the Credential_Store for secure storage.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n */\nexport class OpenAIApiKeyHandler {\n private readonly storage: IModelCredentialStorage;\n\n /**\n * Create a new OpenAI API key handler.\n * @param storage - The credential storage backend\n */\n constructor(storage: IModelCredentialStorage) {\n this.storage = storage;\n }\n\n /**\n * Get the provider ID for this handler.\n * @returns The OpenAI provider ID\n */\n getProviderId(): ModelProviderId {\n return OPENAI_PROVIDER_ID;\n }\n\n /**\n * Get the injection configuration for OpenAI.\n *\n * OpenAI uses the Authorization header with Bearer token format:\n * Authorization: Bearer {key}\n *\n * Requirements: 7b.5\n *\n * @returns The header injection configuration\n */\n getInjectionConfig(): HeaderInjection {\n return MODEL_CREDENTIAL_INJECTION_CONFIG.openai;\n }\n\n /**\n * Validate an OpenAI API key format.\n *\n * Performs basic format validation:\n * - Must be a non-empty string\n * - Must meet minimum length requirement\n * - Optionally checks for 'sk-' prefix (warning only)\n *\n * Note: This does not validate the key against OpenAI's API.\n * Use validateWithApi() for full validation.\n *\n * @param apiKey - The API key to validate\n * @returns Validation result with success flag and optional warning\n */\n validateFormat(apiKey: string): { valid: boolean; warning?: string } {\n if (!apiKey || typeof apiKey !== 'string') {\n return { valid: false };\n }\n\n const trimmedKey = apiKey.trim();\n\n if (trimmedKey.length < OPENAI_API_KEY_MIN_LENGTH) {\n return { valid: false };\n }\n\n // Check for expected prefix (warning only, not a hard requirement)\n if (!trimmedKey.startsWith(OPENAI_API_KEY_PREFIX)) {\n return {\n valid: true,\n warning: `API key does not start with expected prefix '${OPENAI_API_KEY_PREFIX}'`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Store an OpenAI API key in the credential store.\n *\n * The key is stored with encryption handled by the storage backend.\n *\n * Requirements: 7b.4\n *\n * @param apiKey - The API key to store\n * @param label - Optional human-readable label\n * @returns Promise that resolves when stored\n * @throws Error if the API key format is invalid\n */\n async store(apiKey: string, label?: string): Promise<void> {\n const validation = this.validateFormat(apiKey);\n if (!validation.valid) {\n throw new Error('Invalid OpenAI API key format');\n }\n\n const credential: StoredModelCredential = {\n providerId: OPENAI_PROVIDER_ID,\n apiKey: apiKey.trim(),\n label,\n storedAt: Date.now(),\n };\n\n await this.storage.store(OPENAI_STORAGE_KEY, credential);\n }\n\n /**\n * Retrieve the stored OpenAI API key.\n *\n * Requirements: 7b.4\n *\n * @returns The credential result with the API key if found\n */\n async retrieve(): Promise<ModelCredentialResult> {\n try {\n const stored = await this.storage.retrieve(OPENAI_STORAGE_KEY);\n\n if (!stored) {\n return { found: false };\n }\n\n // Check for expiration if set\n if (stored.expiresAt && stored.expiresAt < Date.now()) {\n return {\n found: false,\n error: 'API key has expired',\n };\n }\n\n const credential: ModelCredential = {\n providerId: stored.providerId,\n apiKey: stored.apiKey,\n label: stored.label,\n storedAt: stored.storedAt,\n expiresAt: stored.expiresAt,\n };\n\n return { found: true, credential };\n } catch (error) {\n return {\n found: false,\n error: error instanceof Error ? error.message : 'Failed to retrieve credential',\n };\n }\n }\n\n /**\n * Delete the stored OpenAI API key.\n *\n * @returns Promise that resolves when deleted\n */\n async delete(): Promise<void> {\n await this.storage.delete(OPENAI_STORAGE_KEY);\n }\n\n /**\n * Check if an OpenAI API key is configured.\n *\n * @returns True if a valid API key is stored\n */\n async isConfigured(): Promise<boolean> {\n const result = await this.retrieve();\n return result.found;\n }\n\n /**\n * Get the status of the OpenAI API key credential.\n *\n * @returns The credential status entry\n */\n async getStatus(): Promise<ModelCredentialStatusEntry> {\n const result = await this.retrieve();\n\n if (!result.found) {\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'not-configured',\n };\n }\n\n const credential = result.credential!;\n\n // Check for expiration\n if (credential.expiresAt && credential.expiresAt < Date.now()) {\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'expired',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'configured',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n /**\n * Inject the OpenAI API key into request headers.\n *\n * Creates the Authorization header with Bearer token format:\n * Authorization: Bearer {key}\n *\n * Requirements: 7b.5\n *\n * @param headers - Existing headers object (will be modified)\n * @returns The headers object with the Authorization header added\n * @throws Error if no API key is configured\n */\n async injectHeader(headers: Record<string, string> = {}): Promise<Record<string, string>> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No OpenAI API key configured');\n }\n\n const injection = this.getInjectionConfig();\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n ...headers,\n [injection.headerName]: headerValue,\n };\n }\n\n /**\n * Get the header injection for a request.\n *\n * Returns the header name and value for injecting the API key.\n * This is useful when you need the header separately from the request.\n *\n * Requirements: 7b.5\n *\n * @returns Object with headerName and headerValue\n * @throws Error if no API key is configured\n */\n async getHeaderInjection(): Promise<{ headerName: string; headerValue: string }> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No OpenAI API key configured');\n }\n\n const injection = this.getInjectionConfig();\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n headerName: injection.headerName,\n headerValue,\n };\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a new OpenAI API key handler.\n *\n * @param storage - The credential storage backend\n * @returns A new OpenAI API key handler instance\n */\nexport function createOpenAIApiKeyHandler(storage: IModelCredentialStorage): OpenAIApiKeyHandler {\n return new OpenAIApiKeyHandler(storage);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Anthropic API Key Handler.\n *\n * Handles storage, retrieval, validation, and injection of Anthropic API keys.\n * Anthropic uses the x-api-key header with the raw key (no Bearer prefix).\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n *\n * @module model-credentials/anthropic-api-key\n */\n\nimport type {\n ModelCredential,\n ModelProviderId,\n StoredModelCredential,\n ModelCredentialResult,\n ModelCredentialStatusEntry,\n HeaderInjection,\n} from './types.js';\nimport { MODEL_CREDENTIAL_INJECTION_CONFIG } from './types.js';\nimport type { IModelCredentialStorage } from './openai-api-key.js';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * The provider ID for Anthropic.\n */\nexport const ANTHROPIC_PROVIDER_ID: ModelProviderId = 'anthropic';\n\n/**\n * Anthropic API key prefix for validation.\n * Anthropic API keys typically start with 'sk-ant-'.\n */\nexport const ANTHROPIC_API_KEY_PREFIX = 'sk-ant-';\n\n/**\n * Minimum length for Anthropic API keys.\n * Anthropic keys are typically 40+ characters.\n */\nexport const ANTHROPIC_API_KEY_MIN_LENGTH = 20;\n\n/**\n * Storage key prefix for Anthropic credentials.\n */\nexport const ANTHROPIC_STORAGE_KEY = 'model-credential:anthropic';\n\n// =============================================================================\n// Anthropic API Key Handler\n// =============================================================================\n\n/**\n * Anthropic API Key Handler.\n *\n * Provides methods for storing, retrieving, validating, and injecting\n * Anthropic API keys. Integrates with the Credential_Store for secure storage.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n */\nexport class AnthropicApiKeyHandler {\n private readonly storage: IModelCredentialStorage;\n\n /**\n * Create a new Anthropic API key handler.\n * @param storage - The credential storage backend\n */\n constructor(storage: IModelCredentialStorage) {\n this.storage = storage;\n }\n\n /**\n * Get the provider ID for this handler.\n * @returns The Anthropic provider ID\n */\n getProviderId(): ModelProviderId {\n return ANTHROPIC_PROVIDER_ID;\n }\n\n /**\n * Get the injection configuration for Anthropic.\n *\n * Anthropic uses the x-api-key header with the raw key:\n * x-api-key: {key}\n *\n * Requirements: 7b.5\n *\n * @returns The header injection configuration\n */\n getInjectionConfig(): HeaderInjection {\n return MODEL_CREDENTIAL_INJECTION_CONFIG.anthropic;\n }\n\n /**\n * Validate an Anthropic API key format.\n *\n * Performs basic format validation:\n * - Must be a non-empty string\n * - Must meet minimum length requirement\n * - Optionally checks for 'sk-ant-' prefix (warning only)\n *\n * Note: This does not validate the key against Anthropic's API.\n * Use validateWithApi() for full validation.\n *\n * @param apiKey - The API key to validate\n * @returns Validation result with success flag and optional warning\n */\n validateFormat(apiKey: string): { valid: boolean; warning?: string } {\n if (!apiKey || typeof apiKey !== 'string') {\n return { valid: false };\n }\n\n const trimmedKey = apiKey.trim();\n\n if (trimmedKey.length < ANTHROPIC_API_KEY_MIN_LENGTH) {\n return { valid: false };\n }\n\n // Check for expected prefix (warning only, not a hard requirement)\n if (!trimmedKey.startsWith(ANTHROPIC_API_KEY_PREFIX)) {\n return {\n valid: true,\n warning: `API key does not start with expected prefix '${ANTHROPIC_API_KEY_PREFIX}'`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Store an Anthropic API key in the credential store.\n *\n * The key is stored with encryption handled by the storage backend.\n *\n * Requirements: 7b.4\n *\n * @param apiKey - The API key to store\n * @param label - Optional human-readable label\n * @returns Promise that resolves when stored\n * @throws Error if the API key format is invalid\n */\n async store(apiKey: string, label?: string): Promise<void> {\n const validation = this.validateFormat(apiKey);\n if (!validation.valid) {\n throw new Error('Invalid Anthropic API key format');\n }\n\n const credential: StoredModelCredential = {\n providerId: ANTHROPIC_PROVIDER_ID,\n apiKey: apiKey.trim(),\n label,\n storedAt: Date.now(),\n };\n\n await this.storage.store(ANTHROPIC_STORAGE_KEY, credential);\n }\n\n /**\n * Retrieve the stored Anthropic API key.\n *\n * Requirements: 7b.4\n *\n * @returns The credential result with the API key if found\n */\n async retrieve(): Promise<ModelCredentialResult> {\n try {\n const stored = await this.storage.retrieve(ANTHROPIC_STORAGE_KEY);\n\n if (!stored) {\n return { found: false };\n }\n\n // Check for expiration if set\n if (stored.expiresAt && stored.expiresAt < Date.now()) {\n return {\n found: false,\n error: 'API key has expired',\n };\n }\n\n const credential: ModelCredential = {\n providerId: stored.providerId,\n apiKey: stored.apiKey,\n label: stored.label,\n storedAt: stored.storedAt,\n expiresAt: stored.expiresAt,\n };\n\n return { found: true, credential };\n } catch (error) {\n return {\n found: false,\n error: error instanceof Error ? error.message : 'Failed to retrieve credential',\n };\n }\n }\n\n /**\n * Delete the stored Anthropic API key.\n *\n * @returns Promise that resolves when deleted\n */\n async delete(): Promise<void> {\n await this.storage.delete(ANTHROPIC_STORAGE_KEY);\n }\n\n /**\n * Check if an Anthropic API key is configured.\n *\n * @returns True if a valid API key is stored\n */\n async isConfigured(): Promise<boolean> {\n const result = await this.retrieve();\n return result.found;\n }\n\n /**\n * Get the status of the Anthropic API key credential.\n *\n * @returns The credential status entry\n */\n async getStatus(): Promise<ModelCredentialStatusEntry> {\n const result = await this.retrieve();\n\n if (!result.found) {\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'not-configured',\n };\n }\n\n const credential = result.credential!;\n\n // Check for expiration\n if (credential.expiresAt && credential.expiresAt < Date.now()) {\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'expired',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'configured',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n /**\n * Inject the Anthropic API key into request headers.\n *\n * Creates the x-api-key header with the raw key:\n * x-api-key: {key}\n *\n * Requirements: 7b.5\n *\n * @param headers - Existing headers object (will be modified)\n * @returns The headers object with the x-api-key header added\n * @throws Error if no API key is configured\n */\n async injectHeader(headers: Record<string, string> = {}): Promise<Record<string, string>> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No Anthropic API key configured');\n }\n\n const injection = this.getInjectionConfig();\n // Anthropic uses raw key without format transformation\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n ...headers,\n [injection.headerName]: headerValue,\n };\n }\n\n /**\n * Get the header injection for a request.\n *\n * Returns the header name and value for injecting the API key.\n * This is useful when you need the header separately from the request.\n *\n * Requirements: 7b.5\n *\n * @returns Object with headerName and headerValue\n * @throws Error if no API key is configured\n */\n async getHeaderInjection(): Promise<{ headerName: string; headerValue: string }> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No Anthropic API key configured');\n }\n\n const injection = this.getInjectionConfig();\n // Anthropic uses raw key without format transformation\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n headerName: injection.headerName,\n headerValue,\n };\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a new Anthropic API key handler.\n *\n * @param storage - The credential storage backend\n * @returns A new Anthropic API key handler instance\n */\nexport function createAnthropicApiKeyHandler(storage: IModelCredentialStorage): AnthropicApiKeyHandler {\n return new AnthropicApiKeyHandler(storage);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main orchestrator for OAuth authentication and model credentials.\n *\n * Coordinates providers, flows, storage, and token management.\n * Clearly separates user identity (OAuth/OIDC) from upstream model credentials (API keys).\n *\n * Requirements: 3.1, 4.1, 7b.3, 10.3, 11.4\n *\n * @module auth-manager\n */\n\nimport type { AgentApiKeys } from '../config/api-keys.js';\nimport type { ICredentialStore } from './storage/types.js';\nimport type { ITokenManager } from './token-manager.js';\nimport type { IAuthProvider } from './providers/types.js';\nimport { getProvider, hasProvider, getRegisteredProviders } from './providers/index.js';\nimport { AgentAuthFlow } from './flows/agent-auth-flow.js';\nimport { TerminalAuthFlow } from './flows/terminal-auth-flow.js';\nimport type {\n AuthProviderId,\n AuthResult,\n AuthStatusMap,\n AuthStatusEntry,\n AgentAuthOptions,\n AuthMethodType,\n AuthMethodPrecedenceConfig,\n} from './types.js';\nimport {\n isValidProviderId,\n VALID_PROVIDER_IDS,\n DEFAULT_AUTH_METHOD_PRECEDENCE,\n isValidAuthMethodType,\n} from './types.js';\nimport type {\n ModelProviderId,\n ModelCredentialResult,\n ModelCredentialStatusMap,\n} from './model-credentials/index.js';\nimport {\n isValidModelProviderId,\n VALID_MODEL_PROVIDER_IDS,\n MODEL_CREDENTIAL_INJECTION_CONFIG,\n OpenAIApiKeyHandler,\n AnthropicApiKeyHandler,\n} from './model-credentials/index.js';\nimport type { IModelCredentialStorage } from './model-credentials/openai-api-key.js';\n\n/**\n * Marker token used to indicate client credentials are configured but not authenticated.\n * This token should NEVER be sent in actual requests.\n */\nexport const CLIENT_CREDENTIALS_MARKER = '__CLIENT_CREDENTIALS_CONFIGURED__';\n\n/**\n * Check if a token is the client credentials marker (not a real token).\n * @param token - The token to check\n * @returns True if the token is the marker\n */\nexport function isMarkerToken(token: string | null | undefined): boolean {\n return token === CLIENT_CREDENTIALS_MARKER;\n}\n\n/**\n * Options for creating an AuthManager.\n */\nexport interface AuthManagerOptions {\n /** Credential store for persisting OAuth credentials */\n credentialStore: ICredentialStore;\n /** Token manager for token lifecycle management */\n tokenManager: ITokenManager;\n /** Legacy API keys from api-keys.json */\n legacyApiKeys: Record<string, AgentApiKeys>;\n /** Optional custom provider resolver (for testing) */\n providerResolver?: (providerId: AuthProviderId) => IAuthProvider;\n /**\n * Authentication method precedence configuration.\n * Controls which auth method is preferred when multiple are available.\n * Default: oauth2 > api-key (OAuth preferred when available)\n *\n * Requirements: 3.1, 10.3\n */\n methodPrecedence?: Partial<AuthMethodPrecedenceConfig>;\n /**\n * Optional model credential storage for API key management.\n * When provided, enables getModelCredential() and related methods.\n *\n * Requirements: 7b.3, 7b.4\n */\n modelCredentialStorage?: IModelCredentialStorage;\n}\n\n/**\n * Result of authentication method selection.\n */\nexport interface AuthMethodSelectionResult {\n /** The selected authentication method type */\n methodType: AuthMethodType;\n /** The provider ID to use (for oauth2) */\n providerId?: AuthProviderId;\n /** Whether a valid credential was found */\n hasCredential: boolean;\n /** Error message if selection failed */\n error?: string;\n}\n\n/**\n * Error thrown when authentication method selection fails.\n */\nexport class AuthMethodSelectionError extends Error {\n constructor(\n message: string,\n public readonly code: 'UNSUPPORTED_METHOD' | 'AMBIGUOUS_PROVIDER' | 'NO_CREDENTIALS',\n public readonly details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'AuthMethodSelectionError';\n }\n}\n\n/**\n * Main orchestrator for OAuth authentication and model credentials.\n * Coordinates providers, flows, storage, and token management.\n *\n * This class clearly separates:\n * - User identity (OAuth/OIDC): getTokenForAgent(), authenticateAgent()\n * - Model API access (API Keys): getModelCredential(), injectModelAuth()\n *\n * Responsibilities:\n * - Orchestrate agent auth flow (browser-based OAuth 2.1 with PKCE)\n * - Orchestrate terminal auth flow (interactive CLI setup)\n * - Manage credential precedence (OAuth over legacy api-keys.json)\n * - Inject authentication into agent requests\n * - Report authentication status\n * - Handle logout operations\n * - Manage model API credentials (OpenAI, Anthropic)\n *\n * Method Precedence Strategy (Requirements 3.1, 10.3):\n * - Default precedence: oauth2 > api-key (OAuth preferred when available)\n * - Configurable via AuthConfig.methodPrecedence\n * - Fail-fast on unsupported or ambiguous providerId (configurable)\n *\n * Requirements: 3.1, 4.1, 7b.3, 10.3, 11.4\n */\nexport class AuthManager {\n private readonly credentialStore: ICredentialStore;\n private readonly tokenManager: ITokenManager;\n private readonly legacyApiKeys: Record<string, AgentApiKeys>;\n private readonly providerResolver: (providerId: AuthProviderId) => IAuthProvider;\n private readonly methodPrecedenceConfig: AuthMethodPrecedenceConfig;\n\n /**\n * Model credential handlers for API key management.\n * These are separate from OAuth providers - they handle API keys for model providers.\n *\n * Requirements: 7b.3\n */\n private readonly openAIHandler?: OpenAIApiKeyHandler;\n private readonly anthropicHandler?: AnthropicApiKeyHandler;\n\n /**\n * Tracks in-flight authentication flows per provider.\n * Used to implement single-flight pattern: concurrent auth requests for the same\n * provider share the same Promise and receive the same result.\n *\n * Requirements: 3.1, 6.5\n */\n private readonly inFlightAuthFlows: Map<AuthProviderId, Promise<AuthResult>> = new Map();\n\n /**\n * Create a new AuthManager.\n *\n * @param options - Configuration options\n */\n constructor(options: AuthManagerOptions);\n /**\n * Create a new AuthManager (legacy constructor signature).\n *\n * @param credentialStore - Credential store for persisting OAuth credentials\n * @param tokenManager - Token manager for token lifecycle management\n * @param legacyApiKeys - Legacy API keys from api-keys.json\n */\n constructor(\n credentialStore: ICredentialStore,\n tokenManager: ITokenManager,\n legacyApiKeys: Record<string, AgentApiKeys>\n );\n constructor(\n optionsOrCredentialStore: AuthManagerOptions | ICredentialStore,\n tokenManager?: ITokenManager,\n legacyApiKeys?: Record<string, AgentApiKeys>\n ) {\n if (this.isAuthManagerOptions(optionsOrCredentialStore)) {\n // New options-based constructor\n this.credentialStore = optionsOrCredentialStore.credentialStore;\n this.tokenManager = optionsOrCredentialStore.tokenManager;\n this.legacyApiKeys = optionsOrCredentialStore.legacyApiKeys;\n this.providerResolver = optionsOrCredentialStore.providerResolver ?? getProvider;\n // Merge user-provided precedence config with defaults\n this.methodPrecedenceConfig = {\n ...DEFAULT_AUTH_METHOD_PRECEDENCE,\n ...optionsOrCredentialStore.methodPrecedence,\n };\n // Initialize model credential handlers if storage is provided\n if (optionsOrCredentialStore.modelCredentialStorage) {\n this.openAIHandler = new OpenAIApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage);\n this.anthropicHandler = new AnthropicApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage);\n }\n } else {\n // Legacy constructor\n this.credentialStore = optionsOrCredentialStore;\n this.tokenManager = tokenManager!;\n this.legacyApiKeys = legacyApiKeys ?? {};\n this.providerResolver = getProvider;\n this.methodPrecedenceConfig = { ...DEFAULT_AUTH_METHOD_PRECEDENCE };\n // No model credential handlers in legacy mode\n }\n }\n\n /**\n * Type guard to check if the argument is AuthManagerOptions.\n */\n private isAuthManagerOptions(arg: unknown): arg is AuthManagerOptions {\n return (\n typeof arg === 'object' &&\n arg !== null &&\n 'credentialStore' in arg &&\n 'tokenManager' in arg &&\n 'legacyApiKeys' in arg\n );\n }\n\n /**\n * Authenticate with a provider using agent auth flow.\n *\n * Initiates the OAuth 2.1 Authorization Code flow with PKCE.\n * Opens the system browser for user authentication.\n *\n * Implements single-flight pattern: if an auth flow is already in progress\n * for the same provider, subsequent callers wait for and share the same result.\n * This prevents multiple simultaneous browser flows for the same provider.\n *\n * Requirement 3.1: Initiate OAuth 2.1 Authorization Code flow with PKCE\n * Requirement 6.5: Concurrent auth requests share the same flow\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n async authenticateAgent(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n // Validate provider ID (fast-fail before checking in-flight flows)\n if (!isValidProviderId(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not supported.`,\n details: { supportedProviders: [...VALID_PROVIDER_IDS] },\n },\n };\n }\n\n // Check if provider is registered (fast-fail before checking in-flight flows)\n if (!hasProvider(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not registered.`,\n details: { registeredProviders: getRegisteredProviders() },\n },\n };\n }\n\n // Check for existing in-flight flow for this provider (single-flight pattern)\n const existingFlow = this.inFlightAuthFlows.get(providerId);\n if (existingFlow) {\n console.error(`[AuthManager] Auth flow already in progress for ${providerId}, waiting for result...`);\n return existingFlow;\n }\n\n // Create and track new flow\n const flowPromise = this.executeAuthFlow(providerId, options);\n this.inFlightAuthFlows.set(providerId, flowPromise);\n\n try {\n return await flowPromise;\n } finally {\n // Only delete if this is still our promise (race protection)\n if (this.inFlightAuthFlows.get(providerId) === flowPromise) {\n this.inFlightAuthFlows.delete(providerId);\n }\n }\n }\n\n /**\n * Execute the actual OAuth authentication flow.\n *\n * This is the internal implementation that performs the browser-based\n * OAuth 2.1 Authorization Code flow with PKCE.\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n private async executeAuthFlow(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n try {\n // Create the agent auth flow\n const agentAuthFlow = new AgentAuthFlow({\n getProvider: this.providerResolver,\n storeTokens: async (pid, tokens) => {\n await this.tokenManager.storeTokens(pid, tokens);\n },\n });\n\n // Execute the flow\n console.error(`[AuthManager] Starting agent auth flow for ${providerId}`);\n const result = await agentAuthFlow.execute(providerId, options);\n\n if (result.success) {\n console.error(`[AuthManager] Agent auth flow completed successfully for ${providerId}`);\n } else {\n console.error(`[AuthManager] Agent auth flow failed for ${providerId}: ${result.error?.message}`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[AuthManager] Agent auth flow error for ${providerId}: ${errorMessage}`);\n\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Authentication failed: ${errorMessage}`,\n },\n };\n }\n }\n\n /**\n * Run interactive terminal setup for a provider.\n *\n * Starts the Setup_Wizard interactive flow for configuring\n * OAuth credentials in headless environments.\n *\n * Requirement 4.1: Start Setup_Wizard interactive flow\n *\n * @param providerId - The provider to set up\n * @returns Authentication result indicating success or failure\n */\n async setupTerminal(providerId: AuthProviderId): Promise<AuthResult> {\n // Validate provider ID\n if (!isValidProviderId(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not supported.`,\n details: { supportedProviders: [...VALID_PROVIDER_IDS] },\n },\n };\n }\n\n try {\n // Create the terminal auth flow\n const terminalAuthFlow = new TerminalAuthFlow({\n credentialStore: this.credentialStore,\n validateCredentials: async (pid, credentials) => {\n return this.validateTerminalCredentials(pid, credentials);\n },\n });\n\n // Execute the flow\n console.error(`[AuthManager] Starting terminal setup for ${providerId}`);\n const flowResult = await terminalAuthFlow.execute(providerId);\n\n // Check if user selected browser OAuth flow\n if (flowResult.useBrowserOAuth) {\n console.error(`[AuthManager] User selected browser OAuth for ${providerId}, launching browser flow`);\n return this.authenticateAgent(flowResult.providerId);\n }\n\n // Manual credential flow completed\n const result = flowResult.authResult;\n if (result.success) {\n console.error(`[AuthManager] Terminal setup completed successfully for ${providerId}`);\n } else {\n console.error(`[AuthManager] Terminal setup failed for ${providerId}: ${result.error?.message}`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[AuthManager] Terminal setup error for ${providerId}: ${errorMessage}`);\n\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Terminal setup failed: ${errorMessage}`,\n },\n };\n }\n }\n\n /**\n * Validate credentials collected during terminal auth flow.\n *\n * Note: Terminal auth flow stores client credentials for later use.\n * The actual token exchange happens when the credentials are used.\n * This validation ensures the credentials are properly formatted.\n *\n * @param providerId - The provider to validate against\n * @param credentials - The collected credentials\n * @returns Validation result with status indicator\n */\n private async validateTerminalCredentials(\n providerId: AuthProviderId,\n credentials: { clientId: string; clientSecret?: string }\n ): Promise<{ valid: boolean; error?: string; accessToken?: string }> {\n try {\n // Validate client ID format\n if (!credentials.clientId || credentials.clientId.trim().length === 0) {\n return { valid: false, error: 'Client ID is required' };\n }\n\n // Basic format validation for client ID (alphanumeric with common separators)\n if (!/^[a-zA-Z0-9._-]+$/.test(credentials.clientId.trim())) {\n return { valid: false, error: 'Client ID contains invalid characters' };\n }\n\n // Validate provider is available\n if (!isValidProviderId(providerId)) {\n return { valid: false, error: `Provider '${providerId}' is not supported` };\n }\n\n try {\n const provider = this.providerResolver(providerId);\n if (!provider) {\n return { valid: false, error: `Provider '${providerId}' is not available` };\n }\n } catch {\n return { valid: false, error: `Provider '${providerId}' is not available` };\n }\n\n // Terminal auth stores client credentials, not access tokens\n // Return a placeholder token to indicate \"configured but not authenticated\"\n // The actual token will be obtained via OAuth flow when needed\n return {\n valid: true,\n // Use a special marker to indicate this is a client credential config, not an access token\n accessToken: CLIENT_CREDENTIALS_MARKER,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { valid: false, error: errorMessage };\n }\n }\n\n /**\n * Get access token for an agent, preferring OAuth over legacy.\n *\n * Requirement 10.3: Prefer OAuth credentials over legacy api-keys.json\n *\n * Security: When providerId is specified, ONLY that provider is used.\n * No fallback to other providers to prevent credential confusion.\n *\n * @param agentId - The agent identifier\n * @param providerId - Optional provider to get token from (strict binding when specified)\n * @returns Access token or null if not available\n */\n async getTokenForAgent(\n agentId: string,\n providerId?: AuthProviderId\n ): Promise<string | null> {\n // Step 1: If provider is specified, ONLY use that provider (strict binding)\n if (providerId) {\n // Validate provider ID at runtime\n if (!isValidProviderId(providerId)) {\n console.error(`[AuthManager] Invalid provider ID: ${providerId}`);\n return null;\n }\n\n const oauthToken = await this.tokenManager.getAccessToken(providerId);\n // Filter out marker tokens - they are not real access tokens\n if (oauthToken && !isMarkerToken(oauthToken)) {\n console.error(`[AuthManager] Using OAuth token for agent ${agentId} (provider: ${providerId})`);\n return oauthToken;\n }\n\n // Provider specified but no token available - do NOT fall back to other providers\n // This prevents credential confusion between different services\n console.error(`[AuthManager] No OAuth token available for specified provider ${providerId}`);\n\n // Only fall back to legacy if the legacy key is for the same provider\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n // Check if this agent is associated with the requested provider\n const agentProvider = this.getProviderForAgent(agentId);\n if (agentProvider === providerId) {\n console.error(`[AuthManager] Using legacy API key for agent ${agentId} (provider: ${providerId})`);\n return legacyKeys.apiKey;\n }\n }\n\n return null;\n }\n\n // Step 2: No provider specified - try to find appropriate provider for agent\n const agentProvider = this.getProviderForAgent(agentId);\n if (agentProvider) {\n const token = await this.tokenManager.getAccessToken(agentProvider);\n // Filter out marker tokens - they are not real access tokens\n if (token && !isMarkerToken(token)) {\n console.error(`[AuthManager] Using OAuth token for agent ${agentId} (auto-detected provider: ${agentProvider})`);\n return token;\n }\n }\n\n // Step 3: Fall back to legacy api-keys.json\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n console.error(`[AuthManager] Using legacy API key for agent ${agentId}`);\n return legacyKeys.apiKey;\n }\n\n // No credentials available\n console.error(`[AuthManager] No credentials available for agent ${agentId}`);\n return null;\n }\n\n /**\n * Inject authentication into an agent request.\n *\n * Requirement 11.4: Inject access token according to provider's token injection method\n *\n * Security: Uses strict provider binding based on agent ID to prevent\n * credential confusion between different services.\n *\n * @param agentId - The agent identifier\n * @param request - The request object to inject auth into\n * @returns The request object with authentication injected\n */\n async injectAuth(agentId: string, request: object): Promise<object> {\n // Determine the appropriate provider for this agent\n const agentProvider = this.getProviderForAgent(agentId);\n\n // Try OAuth token for the agent's provider\n if (agentProvider) {\n const token = await this.tokenManager.getAccessToken(agentProvider);\n // Filter out marker tokens - they are not real access tokens\n if (token && !isMarkerToken(token)) {\n try {\n const provider = this.providerResolver(agentProvider);\n const injection = provider.getTokenInjection();\n\n // Validate injection configuration\n const validationError = this.validateInjectionConfig(injection);\n if (validationError) {\n console.error(`[AuthManager] Invalid injection config for ${agentProvider}: ${validationError}`);\n // Fall through to legacy handling\n } else {\n const result = this.applyTokenInjection(request, token, injection);\n if (result !== null) {\n return result;\n }\n // Injection failed (e.g., control chars in token), fall through to legacy\n console.error(`[AuthManager] Token injection failed for ${agentProvider}, trying legacy fallback`);\n }\n } catch (error) {\n // Log the error but don't expose details\n console.error(`[AuthManager] Provider resolution failed for ${agentProvider}`);\n }\n }\n }\n\n // Fall back to legacy API key injection (Bearer header)\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n const result = this.applyTokenInjection(request, legacyKeys.apiKey, {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n });\n if (result !== null) {\n return result;\n }\n // Legacy key also has control chars - this is a security issue, return original request\n console.error(`[AuthManager] Legacy API key contains control characters, refusing to inject`);\n }\n\n // No auth to inject\n return request;\n }\n\n /**\n * Validate token injection configuration.\n * Prevents header injection attacks and unsafe configurations.\n *\n * @param injection - The injection configuration to validate\n * @returns Error message if invalid, null if valid\n */\n private validateInjectionConfig(\n injection: { type: 'header' | 'query' | 'body'; key: string; format?: string }\n ): string | null {\n // Validate key - must be alphanumeric with hyphens/underscores\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(injection.key)) {\n return 'Invalid injection key format';\n }\n\n // Prevent header injection via CR/LF\n if (injection.key.includes('\\r') || injection.key.includes('\\n')) {\n return 'Injection key contains invalid characters';\n }\n\n // Validate format if provided\n if (injection.format) {\n // Must contain {token} placeholder\n if (!injection.format.includes('{token}')) {\n return 'Injection format must contain {token} placeholder';\n }\n // Prevent CR/LF injection in format\n if (injection.format.includes('\\r') || injection.format.includes('\\n')) {\n return 'Injection format contains invalid characters';\n }\n }\n\n // Warn about query injection (tokens in URLs are risky)\n if (injection.type === 'query') {\n console.error('[AuthManager] Warning: Token injection via query parameter is less secure');\n }\n\n return null;\n }\n\n /**\n * Apply token injection to a request object.\n *\n * @param request - The request object\n * @param token - The access token\n * @param injection - The injection method\n * @returns The modified request object, or null if injection failed\n */\n private applyTokenInjection(\n request: object,\n token: string,\n injection: { type: 'header' | 'query' | 'body'; key: string; format?: string }\n ): object | null {\n const formattedToken = injection.format\n ? injection.format.replace('{token}', token)\n : token;\n\n // Validate formatted token for control characters (prevent header injection)\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(formattedToken)) {\n console.error('[AuthManager] Token contains control characters, refusing to inject');\n return null; // Signal injection failure\n }\n\n const result = { ...request } as Record<string, unknown>;\n\n switch (injection.type) {\n case 'header': {\n const headers = (result.headers as Record<string, string>) ?? {};\n result.headers = { ...headers, [injection.key]: formattedToken };\n break;\n }\n case 'query': {\n const query = (result.query as Record<string, string>) ?? {};\n result.query = { ...query, [injection.key]: formattedToken };\n break;\n }\n case 'body': {\n const body = (result.body as Record<string, string>) ?? {};\n result.body = { ...body, [injection.key]: formattedToken };\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Get authentication status for all providers.\n *\n * @returns Map of provider IDs to their authentication status\n */\n async getStatus(): Promise<AuthStatusMap> {\n const statusMap: AuthStatusMap = new Map();\n\n // Get token status from token manager\n const tokenStatus = await this.tokenManager.getStatus();\n\n // Get stored credentials for additional info\n const providers = await this.credentialStore.listProviders();\n\n for (const providerId of providers) {\n const status = tokenStatus.get(providerId) ?? 'not-configured';\n const credentials = await this.credentialStore.retrieve(providerId);\n\n const entry: AuthStatusEntry = {\n providerId,\n status,\n expiresAt: credentials?.expiresAt,\n scope: credentials?.scope,\n lastRefresh: credentials?.storedAt,\n };\n\n statusMap.set(providerId, entry);\n }\n\n // Add providers that are supported but not configured\n for (const providerId of VALID_PROVIDER_IDS) {\n if (!statusMap.has(providerId)) {\n statusMap.set(providerId, {\n providerId,\n status: 'not-configured',\n });\n }\n }\n\n return statusMap;\n }\n\n /**\n * Logout from a specific provider or all providers.\n *\n * Note: This clears OAuth credentials only. Legacy API keys from api-keys.json\n * are managed separately and are not affected by logout.\n *\n * @param providerId - Optional provider to logout from (all OAuth providers if not specified)\n * @throws Error if an invalid provider ID is specified\n */\n async logout(providerId?: AuthProviderId): Promise<void> {\n if (providerId) {\n // Validate provider ID at runtime\n if (!isValidProviderId(providerId)) {\n throw new Error(`Invalid provider ID for logout: ${providerId}`);\n }\n\n // Logout from specific provider\n console.error(`[AuthManager] Logging out from ${providerId}`);\n await this.tokenManager.clearTokens(providerId);\n await this.credentialStore.delete(providerId);\n } else {\n // Logout from all OAuth providers\n console.error(`[AuthManager] Logging out from all OAuth providers`);\n const providers = await this.credentialStore.listProviders();\n for (const pid of providers) {\n await this.tokenManager.clearTokens(pid);\n }\n await this.credentialStore.deleteAll();\n }\n }\n\n /**\n * Check if re-authentication is required for a provider.\n *\n * @param providerId - The provider to check\n * @returns True if re-authentication is required\n */\n async requiresReauth(providerId: AuthProviderId): Promise<boolean> {\n // Check if we have valid tokens\n const hasValid = await this.tokenManager.hasValidTokens(providerId);\n if (hasValid) {\n return false;\n }\n\n // Check if we have credentials at all\n const credentials = await this.credentialStore.retrieve(providerId);\n if (!credentials) {\n // No credentials stored, auth required\n return true;\n }\n\n // We have credentials but tokens are invalid\n // Try to refresh\n const refreshed = await this.tokenManager.forceRefresh(providerId);\n return refreshed === null;\n }\n\n /**\n * Get the provider for a given agent ID.\n *\n * Maps agent IDs to their OAuth providers based on keyword matching.\n *\n * WARNING: This is a heuristic-based mapping using keyword matching.\n * Agent IDs with ambiguous names (e.g., containing multiple provider keywords)\n * may be mapped to unexpected providers. For production use, consider\n * implementing explicit agent-to-provider configuration.\n *\n * @param agentId - The agent identifier\n * @returns The provider ID or undefined if not mapped\n */\n getProviderForAgent(agentId: string): AuthProviderId | undefined {\n // This is a simple mapping based on agent ID patterns\n // In a real implementation, this would come from configuration\n const agentLower = agentId.toLowerCase();\n\n // Check for provider keywords in order of specificity\n // More specific keywords first to avoid ambiguity\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n if (agentLower.includes('github') || agentLower.includes('copilot')) {\n return 'github';\n }\n if (agentLower.includes('google') || agentLower.includes('gemini')) {\n return 'google';\n }\n if (agentLower.includes('azure')) {\n return 'azure';\n }\n if (agentLower.includes('cognito') || agentLower.includes('aws')) {\n return 'cognito';\n }\n\n return undefined;\n }\n\n // ===========================================================================\n // Model Credential Methods (API Keys)\n // ===========================================================================\n // These methods handle upstream model provider credentials (OpenAI, Anthropic).\n // They are clearly separated from OAuth identity providers.\n // Requirements: 7b.3\n // ===========================================================================\n\n /**\n * Get API key credential for a model provider.\n *\n * This method is for retrieving API keys for upstream model providers\n * (OpenAI, Anthropic). These providers do NOT offer public OAuth IdP\n * for third-party login - they use API keys instead.\n *\n * This is clearly separated from getTokenForAgent() which handles\n * OAuth tokens for user identity providers.\n *\n * Requirements: 7b.1, 7b.3\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @returns The model credential result with API key if found\n */\n async getModelCredential(providerId: ModelProviderId): Promise<ModelCredentialResult> {\n // Validate provider ID\n if (!isValidModelProviderId(providerId)) {\n return {\n found: false,\n error: `Invalid model provider ID: ${providerId}. Valid providers: ${VALID_MODEL_PROVIDER_IDS.join(', ')}`,\n };\n }\n\n // Get the appropriate handler\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n return {\n found: false,\n error: `Model credential storage not configured. Initialize AuthManager with modelCredentialStorage option.`,\n };\n }\n\n // Retrieve the credential\n const result = await handler.retrieve();\n if (result.found) {\n console.error(`[AuthManager] Retrieved model credential for ${providerId}`);\n } else {\n console.error(`[AuthManager] No model credential found for ${providerId}`);\n }\n\n return result;\n }\n\n /**\n * Check if a model credential is configured for a provider.\n *\n * Requirements: 7b.3\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @returns True if an API key is configured for the provider\n */\n async hasModelCredential(providerId: ModelProviderId): Promise<boolean> {\n if (!isValidModelProviderId(providerId)) {\n return false;\n }\n\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n return false;\n }\n\n return handler.isConfigured();\n }\n\n /**\n * Get the status of all model credentials.\n *\n * Requirements: 7b.3\n *\n * @returns Map of model provider IDs to their credential status\n */\n async getModelCredentialStatus(): Promise<ModelCredentialStatusMap> {\n const statusMap: ModelCredentialStatusMap = new Map();\n\n for (const providerId of VALID_MODEL_PROVIDER_IDS) {\n const handler = this.getModelCredentialHandler(providerId);\n if (handler) {\n const status = await handler.getStatus();\n statusMap.set(providerId, status);\n } else {\n statusMap.set(providerId, {\n providerId,\n status: 'not-configured',\n });\n }\n }\n\n return statusMap;\n }\n\n /**\n * Inject model API key into a request.\n *\n * This method injects API keys for model providers (OpenAI, Anthropic)\n * according to their documented injection method:\n * - OpenAI: Authorization header with Bearer token\n * - Anthropic: x-api-key header with raw key\n *\n * This is clearly separated from injectAuth() which handles OAuth tokens.\n *\n * Requirements: 7b.3, 7b.5\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @param request - The request object to inject auth into\n * @returns The request object with API key injected, or original if not available\n */\n async injectModelAuth(providerId: ModelProviderId, request: object): Promise<object> {\n // Validate provider ID\n if (!isValidModelProviderId(providerId)) {\n console.error(`[AuthManager] Invalid model provider ID for injection: ${providerId}`);\n return request;\n }\n\n // Get the appropriate handler\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n console.error(`[AuthManager] Model credential storage not configured for ${providerId}`);\n return request;\n }\n\n // Get the credential\n const credentialResult = await handler.retrieve();\n if (!credentialResult.found || !credentialResult.credential) {\n console.error(`[AuthManager] No model credential available for ${providerId}`);\n return request;\n }\n\n // Get injection configuration\n const injection = MODEL_CREDENTIAL_INJECTION_CONFIG[providerId];\n const apiKey = credentialResult.credential.apiKey;\n\n // Format the header value\n const headerValue = injection.format\n ? injection.format.replace('{key}', apiKey)\n : apiKey;\n\n // Validate for control characters (prevent header injection)\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(headerValue)) {\n console.error(`[AuthManager] Model API key contains control characters, refusing to inject`);\n return request;\n }\n\n // Inject into headers\n const result = { ...request } as Record<string, unknown>;\n const headers = (result.headers as Record<string, string>) ?? {};\n result.headers = { ...headers, [injection.headerName]: headerValue };\n\n console.error(`[AuthManager] Injected model credential for ${providerId}`);\n return result;\n }\n\n /**\n * Get the model provider for a given agent ID.\n *\n * Maps agent IDs to their model providers based on keyword matching.\n * This is separate from getProviderForAgent() which maps to OAuth providers.\n *\n * Requirements: 7b.3\n *\n * @param agentId - The agent identifier\n * @returns The model provider ID or undefined if not mapped\n */\n getModelProviderForAgent(agentId: string): ModelProviderId | undefined {\n const agentLower = agentId.toLowerCase();\n\n // Check for model provider keywords\n // Note: These are NOT OAuth providers - they use API keys\n if (agentLower.includes('openai') || agentLower.includes('gpt') || agentLower.includes('chatgpt')) {\n return 'openai';\n }\n if (agentLower.includes('anthropic') || agentLower.includes('claude')) {\n return 'anthropic';\n }\n\n return undefined;\n }\n\n /**\n * Get the appropriate model credential handler for a provider.\n *\n * @param providerId - The model provider ID\n * @returns The handler or undefined if not available\n */\n private getModelCredentialHandler(\n providerId: ModelProviderId\n ): OpenAIApiKeyHandler | AnthropicApiKeyHandler | undefined {\n switch (providerId) {\n case 'openai':\n return this.openAIHandler;\n case 'anthropic':\n return this.anthropicHandler;\n default:\n return undefined;\n }\n }\n\n /**\n * Select the best authentication method for an agent based on precedence configuration.\n *\n * Method Precedence Strategy (Requirements 3.1, 10.3):\n * - Default precedence: oauth2 > api-key (OAuth preferred when available)\n * - Iterates through methods in precedence order\n * - Returns the first method with available credentials\n * - Fail-fast on unsupported or ambiguous providerId (configurable)\n *\n * @param agentId - The agent identifier\n * @param availableMethods - Optional list of methods the agent supports (from authMethods)\n * @param providerId - Optional explicit provider ID (strict binding when specified)\n * @returns Selection result with method type, provider, and credential availability\n * @throws AuthMethodSelectionError if fail-fast is enabled and an error occurs\n */\n async selectAuthMethod(\n agentId: string,\n availableMethods?: AuthMethodType[],\n providerId?: AuthProviderId\n ): Promise<AuthMethodSelectionResult> {\n const { methodPrecedence, failFastOnUnsupported, failFastOnAmbiguous } = this.methodPrecedenceConfig;\n\n // Validate explicit providerId if specified\n if (providerId !== undefined) {\n if (!isValidProviderId(providerId)) {\n const error = `Provider '${providerId}' is not supported. Valid providers: ${VALID_PROVIDER_IDS.join(', ')}`;\n if (failFastOnUnsupported) {\n throw new AuthMethodSelectionError(\n error,\n 'UNSUPPORTED_METHOD',\n { providerId, supportedProviders: [...VALID_PROVIDER_IDS] }\n );\n }\n console.error(`[AuthManager] ${error}`);\n return {\n methodType: 'api-key',\n hasCredential: false,\n error,\n };\n }\n }\n\n // Determine which methods to consider\n const methodsToTry = availableMethods\n ? methodPrecedence.filter(m => availableMethods.includes(m))\n : methodPrecedence;\n\n // Check for ambiguous provider mapping (multiple providers could match)\n if (!providerId && failFastOnAmbiguous) {\n const ambiguityCheck = this.checkProviderAmbiguity(agentId);\n if (ambiguityCheck.isAmbiguous) {\n throw new AuthMethodSelectionError(\n `Ambiguous provider mapping for agent '${agentId}'. Multiple providers could match: ${ambiguityCheck.matchingProviders.join(', ')}. Specify an explicit providerId.`,\n 'AMBIGUOUS_PROVIDER',\n { agentId, matchingProviders: ambiguityCheck.matchingProviders }\n );\n }\n }\n\n // Iterate through methods in precedence order\n for (const methodType of methodsToTry) {\n // Validate method type\n if (!isValidAuthMethodType(methodType)) {\n const error = `Unsupported authentication method: ${methodType}`;\n if (failFastOnUnsupported) {\n throw new AuthMethodSelectionError(\n error,\n 'UNSUPPORTED_METHOD',\n { methodType, supportedMethods: ['oauth2', 'api-key'] }\n );\n }\n console.error(`[AuthManager] ${error}, skipping...`);\n continue;\n }\n\n const result = await this.tryAuthMethod(agentId, methodType, providerId);\n if (result.hasCredential) {\n console.error(`[AuthManager] Selected auth method '${methodType}' for agent '${agentId}'`);\n return result;\n }\n }\n\n // No credentials found for any method\n console.error(`[AuthManager] No credentials available for agent '${agentId}'`);\n return {\n methodType: methodPrecedence[0] ?? 'oauth2',\n hasCredential: false,\n error: `No credentials available for agent '${agentId}'`,\n };\n }\n\n /**\n * Try a specific authentication method for an agent.\n *\n * @param agentId - The agent identifier\n * @param methodType - The authentication method to try\n * @param providerId - Optional explicit provider ID\n * @returns Selection result for this method\n */\n private async tryAuthMethod(\n agentId: string,\n methodType: AuthMethodType,\n providerId?: AuthProviderId\n ): Promise<AuthMethodSelectionResult> {\n switch (methodType) {\n case 'oauth2': {\n // Determine provider for OAuth\n const effectiveProviderId = providerId ?? this.getProviderForAgent(agentId);\n if (!effectiveProviderId) {\n return {\n methodType: 'oauth2',\n hasCredential: false,\n error: `No OAuth provider mapping for agent '${agentId}'`,\n };\n }\n\n // Check for OAuth token\n const token = await this.tokenManager.getAccessToken(effectiveProviderId);\n if (token && !isMarkerToken(token)) {\n return {\n methodType: 'oauth2',\n providerId: effectiveProviderId,\n hasCredential: true,\n };\n }\n\n return {\n methodType: 'oauth2',\n providerId: effectiveProviderId,\n hasCredential: false,\n error: `No OAuth token available for provider '${effectiveProviderId}'`,\n };\n }\n\n case 'api-key': {\n // Check for legacy API key\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n return {\n methodType: 'api-key',\n hasCredential: true,\n };\n }\n\n return {\n methodType: 'api-key',\n hasCredential: false,\n error: `No API key available for agent '${agentId}'`,\n };\n }\n\n default: {\n // This should never happen due to type checking, but handle gracefully\n return {\n methodType: methodType as AuthMethodType,\n hasCredential: false,\n error: `Unknown authentication method: ${methodType}`,\n };\n }\n }\n }\n\n /**\n * Check if an agent ID has ambiguous provider mapping.\n *\n * Ambiguity occurs when multiple provider keywords match the agent ID.\n * For example, \"azure-openai-agent\" matches both \"azure\" and \"openai\".\n *\n * @param agentId - The agent identifier\n * @returns Ambiguity check result\n */\n private checkProviderAmbiguity(agentId: string): { isAmbiguous: boolean; matchingProviders: AuthProviderId[] } {\n const agentLower = agentId.toLowerCase();\n const matchingProviders: AuthProviderId[] = [];\n\n // Check each provider's keywords\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n // Note: oidc is checked last as a fallback for generic OIDC providers\n const providerKeywords: Record<AuthProviderId, string[]> = {\n github: ['github', 'copilot'],\n google: ['google', 'gemini'],\n azure: ['azure'],\n cognito: ['cognito', 'aws'],\n oidc: ['oidc', 'openid', 'auth0', 'okta', 'keycloak', 'onelogin', 'ping'],\n };\n\n for (const [provider, keywords] of Object.entries(providerKeywords)) {\n if (keywords.some(keyword => agentLower.includes(keyword))) {\n matchingProviders.push(provider as AuthProviderId);\n }\n }\n\n return {\n isAmbiguous: matchingProviders.length > 1,\n matchingProviders,\n };\n }\n\n /**\n * Get the current method precedence configuration.\n *\n * @returns The current method precedence configuration\n */\n getMethodPrecedenceConfig(): AuthMethodPrecedenceConfig {\n return { ...this.methodPrecedenceConfig };\n }\n}\n\n/**\n * Create an AuthManager with the given options.\n *\n * @param options - Configuration options\n * @returns A new AuthManager instance\n */\nexport function createAuthManager(options: AuthManagerOptions): AuthManager {\n return new AuthManager(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --setup CLI command implementation.\n *\n * Starts the interactive authentication Setup_Wizard.\n *\n * Requirements: 9.1, 3.1\n *\n * @module cli/setup-command\n */\n\nimport { TerminalAuthFlow } from '../flows/terminal-auth-flow.js';\nimport { AgentAuthFlow } from '../flows/agent-auth-flow.js';\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { getProvider } from '../providers/index.js';\nimport { CLIENT_CREDENTIALS_MARKER } from '../auth-manager.js';\nimport type { AuthProviderId, TokenResponse } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Options for the setup command.\n */\nexport interface SetupCommandOptions {\n /** Optional pre-selected provider (skips provider selection) */\n providerId?: AuthProviderId;\n /** Custom input stream (for testing) */\n input?: NodeJS.ReadableStream;\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Run the setup command.\n *\n * Starts the interactive Setup_Wizard for configuring OAuth credentials.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.1: WHEN the `--setup` flag is provided, THE Registry_Launcher\n * SHALL start the interactive authentication Setup_Wizard.\n *\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runSetupCommand(options: SetupCommandOptions = {}): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Validate provider ID if specified\n if (options.providerId !== undefined && !isValidProviderId(options.providerId)) {\n output.write(`\\nError: Invalid provider '${options.providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n // Create credential store\n const credentialStore = new CredentialStore();\n\n // Create terminal auth flow\n const terminalAuthFlow = new TerminalAuthFlow({\n credentialStore,\n validateCredentials: async (_providerId, credentials) => {\n // Basic validation - check that required fields are present\n if (!credentials.clientId || credentials.clientId.trim().length === 0) {\n return { valid: false, error: 'This field is required' };\n }\n\n // For API key auth (no clientSecret), the clientId IS the API key\n // Return it directly as the access token\n if (!credentials.clientSecret) {\n // This is API key mode - the \"clientId\" is actually the API key\n return { valid: true, accessToken: credentials.clientId.trim() };\n }\n\n // For OAuth client credentials mode, return the marker token\n // The actual OAuth token will be obtained when the credentials are used\n return { valid: true, accessToken: CLIENT_CREDENTIALS_MARKER };\n },\n input: options.input,\n output,\n });\n\n // Execute the setup wizard\n const flowResult = await terminalAuthFlow.execute(options.providerId);\n\n // Check if user selected browser OAuth flow\n if (flowResult.useBrowserOAuth) {\n output.write('\\nLaunching browser for OAuth authentication...\\n');\n output.write('Please complete the authentication in your browser.\\n\\n');\n\n // Create token manager for storing tokens\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n\n // Create and execute browser OAuth flow\n const agentAuthFlow = new AgentAuthFlow({\n getProvider,\n storeTokens: async (providerId: AuthProviderId, tokens: TokenResponse) => {\n await tokenManager.storeTokens(providerId, tokens);\n },\n });\n\n const browserResult = await agentAuthFlow.execute(flowResult.providerId);\n\n if (browserResult.success) {\n output.write(`\\n${flowResult.providerId} authentication completed successfully!\\n\\n`);\n return 0;\n } else {\n output.write(`\\nBrowser authentication failed: ${browserResult.error?.message}\\n`);\n return 1;\n }\n }\n\n // Manual credential flow completed\n const result = flowResult.authResult;\n if (result.success) {\n return 0;\n } else {\n // Error message already displayed by terminal auth flow\n return 1;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nSetup failed: ${errorMessage}\\n`);\n console.error(`[SetupCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --auth-status CLI command implementation.\n *\n * Displays the current authentication status for all configured providers.\n *\n * Requirements: 9.2\n *\n * @module cli/status-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport type { AuthStatusEntry, TokenStatus } from '../types.js';\nimport { VALID_PROVIDER_IDS } from '../types.js';\nimport type { ModelCredentialStatusEntry, StoredModelCredential } from '../model-credentials/index.js';\nimport { VALID_MODEL_PROVIDER_IDS } from '../model-credentials/index.js';\nimport type { IModelCredentialStorage } from '../model-credentials/openai-api-key.js';\n\n/**\n * Options for the status command.\n */\nexport interface StatusCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Format a timestamp as a human-readable date string.\n *\n * @param timestamp - Unix timestamp in milliseconds\n * @returns Formatted date string\n */\nfunction formatTimestamp(timestamp: number | undefined): string {\n if (!timestamp) {\n return 'N/A';\n }\n return new Date(timestamp).toLocaleString();\n}\n\n/**\n * Get a human-readable status label with color indicator.\n *\n * @param status - Token status\n * @returns Status label with indicator\n */\nfunction getStatusLabel(status: TokenStatus): string {\n switch (status) {\n case 'authenticated':\n return '\u2713 Authenticated';\n case 'expired':\n return '\u26A0 Expired (refresh available)';\n case 'refresh-failed':\n return '\u2717 Refresh Failed (re-auth required)';\n case 'not-configured':\n return '\u25CB Not Configured';\n default:\n return '? Unknown';\n }\n}\n\n/**\n * Sanitize a string for safe terminal output.\n * Removes control characters and ANSI escape sequences.\n *\n * @param value - The string to sanitize\n * @returns Sanitized string safe for terminal output\n */\nfunction sanitizeForOutput(value: string): string {\n // Remove ANSI escape sequences\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\x00-\\x1f\\x7f]|\\x1b\\[[0-9;]*[a-zA-Z]/g, '');\n}\n\n/**\n * Format a single provider status entry for display.\n *\n * @param entry - Auth status entry\n * @returns Formatted status lines\n */\nfunction formatProviderStatus(entry: AuthStatusEntry): string[] {\n const lines: string[] = [];\n const providerName = entry.providerId.charAt(0).toUpperCase() + entry.providerId.slice(1);\n\n lines.push(` ${providerName}:`);\n lines.push(` Status: ${getStatusLabel(entry.status)}`);\n\n if (entry.status !== 'not-configured') {\n if (entry.expiresAt) {\n const now = Date.now();\n const isExpired = entry.expiresAt <= now;\n const expiresLabel = isExpired ? 'Expired at' : 'Expires at';\n lines.push(` ${expiresLabel}: ${formatTimestamp(entry.expiresAt)}`);\n }\n\n if (entry.scope) {\n // Sanitize scope to prevent terminal output injection\n lines.push(` Scope: ${sanitizeForOutput(entry.scope)}`);\n }\n\n if (entry.lastRefresh) {\n lines.push(` Last Updated: ${formatTimestamp(entry.lastRefresh)}`);\n }\n }\n\n return lines;\n}\n\n/**\n * Format a model credential status entry for display.\n *\n * @param entry - Model credential status entry\n * @returns Formatted status lines\n */\nfunction formatModelCredentialStatus(entry: ModelCredentialStatusEntry): string[] {\n const lines: string[] = [];\n const providerName = entry.providerId.charAt(0).toUpperCase() + entry.providerId.slice(1);\n\n lines.push(` ${providerName}:`);\n\n switch (entry.status) {\n case 'configured':\n lines.push(` Status: \u2713 Configured`);\n if (entry.label) {\n lines.push(` Label: ${sanitizeForOutput(entry.label)}`);\n }\n if (entry.storedAt) {\n lines.push(` Stored: ${formatTimestamp(entry.storedAt)}`);\n }\n break;\n case 'expired':\n lines.push(` Status: \u26A0 Expired`);\n break;\n case 'not-configured':\n lines.push(` Status: \u25CB Not Configured`);\n break;\n }\n\n return lines;\n}\n\n/**\n * Adapter to use CredentialStore as IModelCredentialStorage.\n *\n * This adapter wraps the CredentialStore to provide the IModelCredentialStorage\n * interface needed by model credential handlers.\n */\nclass CredentialStoreAdapter implements IModelCredentialStorage {\n private readonly storage: Map<string, StoredModelCredential> = new Map();\n\n async store(key: string, credential: StoredModelCredential): Promise<void> {\n this.storage.set(key, { ...credential });\n }\n\n async retrieve(key: string): Promise<StoredModelCredential | null> {\n const cred = this.storage.get(key);\n return cred ? { ...cred } : null;\n }\n\n async delete(key: string): Promise<void> {\n this.storage.delete(key);\n }\n\n async exists(key: string): Promise<boolean> {\n return this.storage.has(key);\n }\n}\n\n/**\n * Run the auth-status command.\n *\n * Displays the current authentication status for all configured providers.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.2: WHEN the `--auth-status` flag is provided, THE Registry_Launcher\n * SHALL display the current authentication status for all configured providers\n * (authenticated, expired, not configured).\n *\n * @param options - Command options\n * @returns Exit code (0 for success)\n */\nexport async function runStatusCommand(options: StatusCommandOptions = {}): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: () => null, // Not needed for status check\n });\n\n // Create model credential storage adapter\n const modelCredentialStorage = new CredentialStoreAdapter();\n\n // Create auth manager with model credential storage\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n modelCredentialStorage,\n });\n\n // Get status for all OAuth providers\n const statusMap = await authManager.getStatus();\n\n // Get status for all model API keys\n const modelStatusMap = await authManager.getModelCredentialStatus();\n\n // Display OAuth header\n output.write('\\n=== OAuth Authentication Status ===\\n\\n');\n\n // Count OAuth providers by status\n let authenticatedCount = 0;\n let expiredCount = 0;\n let notConfiguredCount = 0;\n\n // Display status for each OAuth provider\n for (const providerId of VALID_PROVIDER_IDS) {\n const entry = statusMap.get(providerId);\n\n if (entry) {\n const lines = formatProviderStatus(entry);\n for (const line of lines) {\n output.write(line + '\\n');\n }\n output.write('\\n');\n\n // Update counts\n switch (entry.status) {\n case 'authenticated':\n authenticatedCount++;\n break;\n case 'expired':\n case 'refresh-failed':\n expiredCount++;\n break;\n case 'not-configured':\n notConfiguredCount++;\n break;\n }\n }\n }\n\n // Display Model API Keys header\n output.write('=== Model API Keys ===\\n\\n');\n\n // Count model keys by status\n let modelConfiguredCount = 0;\n let modelNotConfiguredCount = 0;\n\n // Display status for each model provider\n for (const providerId of VALID_MODEL_PROVIDER_IDS) {\n const entry = modelStatusMap.get(providerId);\n\n if (entry) {\n const lines = formatModelCredentialStatus(entry);\n for (const line of lines) {\n output.write(line + '\\n');\n }\n output.write('\\n');\n\n // Update counts\n switch (entry.status) {\n case 'configured':\n modelConfiguredCount++;\n break;\n case 'expired':\n case 'not-configured':\n modelNotConfiguredCount++;\n break;\n }\n }\n }\n\n // Display summary\n output.write('--- Summary ---\\n');\n output.write(` OAuth Authenticated: ${authenticatedCount}\\n`);\n output.write(` OAuth Expired/Failed: ${expiredCount}\\n`);\n output.write(` OAuth Not Configured: ${notConfiguredCount}\\n`);\n output.write(` Model Keys Configured: ${modelConfiguredCount}\\n`);\n output.write(` Model Keys Not Configured: ${modelNotConfiguredCount}\\n`);\n output.write('\\n');\n\n // Provide helpful hints\n if (notConfiguredCount === VALID_PROVIDER_IDS.length && modelNotConfiguredCount === VALID_MODEL_PROVIDER_IDS.length) {\n output.write('Tip: Run with --setup to configure authentication.\\n\\n');\n } else if (expiredCount > 0) {\n output.write('Tip: Run with --setup to re-authenticate expired providers.\\n\\n');\n }\n\n return 0;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nFailed to get auth status: ${errorMessage}\\n`);\n console.error(`[StatusCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --logout CLI command implementation.\n *\n * Removes stored credentials from the Credential_Store.\n *\n * Requirements: 9.3, 9.4\n *\n * @module cli/logout-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Options for the logout command.\n */\nexport interface LogoutCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Run the logout command.\n *\n * Removes stored credentials from the Credential_Store.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.3: WHEN the `--logout` flag is provided, THE Registry_Launcher\n * SHALL remove all stored credentials from the Credential_Store.\n *\n * Requirement 9.4: WHEN the `--logout` flag is provided with a provider name,\n * THE Registry_Launcher SHALL remove only the credentials for that specific provider.\n *\n * @param providerId - Optional provider to logout from (all if not specified)\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runLogoutCommand(\n providerId?: AuthProviderId,\n options: LogoutCommandOptions = {}\n): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Validate provider ID if specified\n if (providerId !== undefined && !isValidProviderId(providerId)) {\n output.write(`\\nError: Invalid provider '${providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: () => null, // Not needed for logout\n });\n\n // Create auth manager\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n });\n\n // Get list of configured providers before logout\n const configuredProviders = await credentialStore.listProviders();\n\n if (providerId) {\n // Logout from specific provider (Requirement 9.4)\n if (!configuredProviders.includes(providerId)) {\n output.write(`\\nNo credentials found for provider '${providerId}'.\\n\\n`);\n return 0; // Not an error, just nothing to do\n }\n\n await authManager.logout(providerId);\n\n const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);\n output.write(`\\nSuccessfully logged out from ${providerName}.\\n\\n`);\n } else {\n // Logout from all providers (Requirement 9.3)\n if (configuredProviders.length === 0) {\n output.write('\\nNo credentials found. Nothing to logout.\\n\\n');\n return 0; // Not an error, just nothing to do\n }\n\n await authManager.logout();\n\n output.write(`\\nSuccessfully logged out from all providers.\\n`);\n output.write(`Removed credentials for: ${configuredProviders.join(', ')}\\n\\n`);\n }\n\n return 0;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nLogout failed: ${errorMessage}\\n`);\n console.error(`[LogoutCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --login CLI command implementation.\n *\n * Starts the browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirements: 3.1, 3.2, 9.5\n *\n * @module cli/login-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport { getProvider } from '../providers/index.js';\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Default timeout for browser OAuth flow (5 minutes).\n */\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Options for the login command.\n */\nexport interface LoginCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n /** Custom timeout in milliseconds (default: 5 minutes) */\n timeoutMs?: number;\n}\n\n/**\n * Run the login command.\n *\n * Starts the browser-based OAuth 2.1 Authorization Code flow with PKCE\n * for the specified provider.\n *\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 3.1: WHEN an agent requires OAuth authentication with `type: \"agent\"`,\n * THE Auth_Module SHALL initiate the OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirement 3.2: WHEN initiating the authorization flow, THE Auth_Module SHALL\n * open the system default browser to the provider's authorization URL.\n *\n * Requirement 9.5: THE Registry_Launcher SHALL exit with code 0 after successfully\n * completing any auth CLI command.\n *\n * @param providerId - The provider to authenticate with\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runLoginCommand(\n providerId: AuthProviderId,\n options: LoginCommandOptions = {}\n): Promise<number> {\n const output = options.output ?? process.stderr;\n\n // Validate and sanitize timeout\n let timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n timeoutMs = DEFAULT_TIMEOUT_MS;\n }\n // Clamp to reasonable bounds (1 second to 30 minutes)\n timeoutMs = Math.max(1000, Math.min(timeoutMs, 30 * 60 * 1000));\n\n // Validate provider ID\n if (!isValidProviderId(providerId)) {\n output.write(`\\nError: Invalid provider '${providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n try {\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n\n // Create auth manager\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n });\n\n // Get provider display name\n const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);\n const timeoutMinutes = Math.round(timeoutMs / 60000);\n\n // Output user feedback\n output.write(`\\nOpening browser for ${providerName} authentication...\\n`);\n output.write(`Waiting for authorization (timeout: ${timeoutMinutes} minutes)...\\n\\n`);\n\n // Start the browser OAuth flow\n const result = await authManager.authenticateAgent(providerId, { timeoutMs });\n\n if (result.success) {\n output.write(`\\n\u2713 Successfully authenticated with ${providerName}.\\n\\n`);\n return 0;\n } else {\n // Handle specific error cases\n const error = result.error;\n\n if (error.code === 'TIMEOUT') {\n output.write(`\\n\u2717 Authentication timed out.\\n`);\n output.write(`The browser authorization flow did not complete within ${timeoutMinutes} minutes.\\n`);\n output.write(`Please try again and complete the authorization in your browser.\\n\\n`);\n } else if (error.code === 'INVALID_STATE') {\n output.write(`\\n\u2717 Authentication failed: Security validation error.\\n`);\n output.write(`The authorization response could not be verified. Please try again.\\n\\n`);\n } else if (error.code === 'CALLBACK_ERROR') {\n output.write(`\\n\u2717 Authentication cancelled or failed.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n } else if (error.code === 'PROVIDER_ERROR') {\n output.write(`\\n\u2717 ${providerName} returned an error.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n } else if (error.code === 'UNSUPPORTED_PROVIDER') {\n output.write(`\\n\u2717 Provider '${providerId}' is not supported.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n } else {\n // Generic error handling\n output.write(`\\n\u2717 Authentication failed.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n }\n\n // Log error details for debugging (to stderr)\n console.error(`[LoginCommand] Authentication failed for ${providerId}: ${error.code} - ${error.message}`);\n\n return 1;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\n\u2717 Login failed: ${errorMessage}\\n\\n`);\n console.error(`[LoginCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "#!/usr/bin/env node\n\n/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main entry point for the Registry Launcher Worker.\n *\n * The Registry Launcher is a stdio Bus worker that:\n * 1. Fetches and parses the ACP Registry on startup\n * 2. Receives NDJSON messages from stdio Bus via stdin\n * 3. Routes messages to appropriate agent processes based on agentId\n * 4. Forwards agent responses back to stdout unchanged\n *\n * @module registry-launcher\n */\n\nimport { loadConfig } from './config/config.js';\nimport { loadApiKeys } from './config/api-keys.js';\nimport { RegistryFetchError, RegistryIndex, RegistryParseError, CustomAgentsLoadError, loadCustomAgents } from './registry/index.js';\nimport { NDJSONHandler } from './stream/ndjson-handler.js';\nimport { AgentRuntimeManager } from './runtime/manager.js';\nimport { MessageRouter } from './router/message-router.js';\nimport { logError, logExit, logInfo, logWarn } from './log.js';\nimport { runSetupCommand, runStatusCommand, runLogoutCommand, runLoginCommand } from './auth/cli/index.js';\nimport type { AuthProviderId } from './auth/types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from './auth/types.js';\nimport { AuthManager } from './auth/auth-manager.js';\nimport { TokenManager } from './auth/token-manager.js';\nimport { CredentialStore } from './auth/storage/credential-store.js';\nimport { getProvider } from './auth/providers/index.js';\n\n/**\n * Exit codes for the Registry Launcher.\n */\nconst ExitCodes = {\n /** Successful graceful shutdown */\n SUCCESS: 0,\n /** Fatal error during startup or operation */\n FATAL_ERROR: 1,\n} as const;\n\n/**\n * Flag to track if shutdown is in progress.\n */\nlet isShuttingDown = false;\n\n/**\n * Parsed command-line arguments.\n */\ninterface ParsedArgs {\n /** Path to the config file (positional argument) */\n configPath?: string;\n /** Path to the custom agents JSON file (--custom-agents <path>) */\n customAgentsPath?: string;\n /** Run the --setup auth command */\n setup?: boolean;\n /** Run the --auth-status command */\n authStatus?: boolean;\n /** Run the --logout command */\n logout?: boolean;\n /** Provider ID for --logout (optional) */\n logoutProvider?: AuthProviderId;\n /** Run the --login command */\n login?: boolean;\n /** Provider ID for --login (required) */\n loginProvider?: string;\n}\n\n/**\n * Parse command-line arguments.\n *\n * Usage: node index.js [config-path] [--custom-agents <path>] [--setup] [--auth-status] [--logout [provider]] [--login <provider>]\n *\n * @returns Parsed arguments\n */\nfunction parseArgs(): ParsedArgs {\n // argv[0] is node, argv[1] is the script path\n const args = process.argv.slice(2);\n const result: ParsedArgs = {};\n\n let i = 0;\n while (i < args.length) {\n const arg = args[i];\n\n if (arg === '--custom-agents') {\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.customAgentsPath = nextArg;\n i += 2;\n continue;\n }\n // --custom-agents without value: log warning and skip\n logWarn('--custom-agents requires a file path argument, ignoring');\n i += 1;\n continue;\n }\n\n // Auth CLI flags (Requirement 9.1, 9.2, 9.3)\n if (arg === '--setup') {\n result.setup = true;\n i += 1;\n continue;\n }\n\n if (arg === '--auth-status') {\n result.authStatus = true;\n i += 1;\n continue;\n }\n\n if (arg === '--logout') {\n result.logout = true;\n // Check if next arg is a provider ID (not a flag)\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.logoutProvider = nextArg as AuthProviderId;\n i += 2;\n continue;\n }\n i += 1;\n continue;\n }\n\n // --login [provider] flag (Requirement 3.1, 9.1)\n if (arg === '--login') {\n result.login = true;\n // Check if next arg is a provider ID (not a flag)\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.loginProvider = nextArg;\n i += 2;\n continue;\n }\n i += 1;\n continue;\n }\n\n // First non-flag argument is the config path\n if (!arg.startsWith('-') && !result.configPath) {\n result.configPath = arg;\n }\n\n i += 1;\n }\n\n return result;\n}\n\n/**\n * Set up SIGTERM handler for graceful shutdown.\n *\n * @param runtimeManager - The runtime manager to terminate agents\n * @param shutdownTimeoutMs - Timeout in milliseconds for graceful shutdown\n * @returns A function to trigger shutdown programmatically\n */\nfunction setupSignalHandlers(\n runtimeManager: AgentRuntimeManager,\n shutdownTimeoutMs: number,\n): () => Promise<void> {\n const shutdown = async (): Promise<void> => {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n logInfo('Received shutdown signal, initiating graceful shutdown');\n\n try {\n // Terminate all agent processes\n await runtimeManager.terminateAll(shutdownTimeoutMs);\n logInfo('All agent processes terminated');\n\n // Exit with success code\n process.exit(ExitCodes.SUCCESS);\n } catch (error) {\n logError(`Error during shutdown: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n };\n\n // Handle SIGTERM\n process.on('SIGTERM', () => {\n void shutdown();\n });\n\n // Also handle SIGINT for development convenience\n process.on('SIGINT', () => {\n void shutdown();\n });\n\n return shutdown;\n}\n\n/**\n * Set up stdin NDJSON handler and wire up message routing.\n *\n * @param router - The message router for handling incoming messages\n * @param ndjsonHandler - The NDJSON handler for stdin/stdout\n */\nfunction setupStdinHandler(router: MessageRouter, ndjsonHandler: NDJSONHandler): void {\n // Handle parsed messages from stdin\n ndjsonHandler.onMessage(async (message: object) => {\n try {\n const errorResponse = await router.route(message);\n\n // If routing returned an error, write it to stdout\n if (errorResponse) {\n ndjsonHandler.write(errorResponse);\n }\n } catch (error) {\n logError(`Unexpected error routing message: ${(error as Error).message}`);\n }\n });\n\n // Handle parse errors\n ndjsonHandler.onError((error: Error, line: string) => {\n logError(`Failed to parse NDJSON: ${error.message} - Line: ${line.slice(0, 100)}`);\n });\n\n // Process stdin data\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: Buffer | string) => {\n const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n ndjsonHandler.processChunk(buffer);\n });\n\n // Handle stdin close\n process.stdin.on('end', () => {\n logInfo('stdin closed');\n });\n\n // Handle stdin errors\n process.stdin.on('error', (error: Error) => {\n logError(`stdin error: ${error.message}`);\n });\n}\n\n/**\n * Set up agent response handling.\n *\n * Wires up agent stdout to the message router for forwarding responses.\n *\n * @param runtimeManager - The runtime manager\n * @param router - The message router\n */\nfunction setupAgentResponseHandling(\n runtimeManager: AgentRuntimeManager,\n router: MessageRouter,\n): void {\n // Handle agent exit events\n runtimeManager.onAgentExit((agentId: string, code: number | null) => {\n logExit(agentId, code);\n });\n\n // Hook into runtime manager to set up stdout handling for new agents\n const originalGetOrSpawn = runtimeManager.getOrSpawn.bind(runtimeManager);\n runtimeManager.getOrSpawn = async function (agentId: string, spawnCommand: import('./registry/types.js').SpawnCommand) {\n const runtime = await originalGetOrSpawn(agentId, spawnCommand);\n\n // Set up stdout handler for this agent if not already done\n const proc = runtime.process;\n if (proc.stdout && !proc.stdout.listenerCount('data')) {\n let buffer = '';\n\n proc.stdout.on('data', (chunk: string) => {\n buffer += chunk;\n\n // Process complete NDJSON lines\n let newlineIndex;\n while ((newlineIndex = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newlineIndex);\n buffer = buffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n try {\n const response = JSON.parse(line);\n router.handleAgentResponse(agentId, response);\n } catch (err) {\n logError(`Failed to parse agent ${agentId} response: ${(err as Error).message}`);\n }\n }\n }\n });\n }\n\n // Log stderr from agent\n if (proc.stderr && !proc.stderr.listenerCount('data')) {\n proc.stderr.on('data', (chunk: string) => {\n // Log agent stderr to our stderr\n process.stderr.write(`[agent:${agentId}] ${chunk}`);\n });\n }\n\n return runtime;\n };\n}\n\n/**\n * Main entry point for the Registry Launcher.\n *\n * 1. Load configuration from command-line argument\n * 2. Handle auth CLI commands (--setup, --auth-status, --logout) if present\n * 3. Fetch and parse registry on startup\n * 4. Set up stdin NDJSON handler\n * 5. Wire up message router with registry and runtime manager\n * 6. Handle SIGTERM for graceful shutdown\n * 7. Exit with appropriate codes\n */\nasync function main(): Promise<void> {\n logInfo('Registry Launcher starting');\n\n // Parse command-line arguments\n const parsedArgs = parseArgs();\n\n // Handle auth CLI commands (Requirement 9.1, 9.2, 9.3)\n // These commands exit after completion and don't start the worker\n if (parsedArgs.setup) {\n logInfo('Running --setup command');\n const exitCode = await runSetupCommand();\n process.exit(exitCode);\n }\n\n if (parsedArgs.authStatus) {\n logInfo('Running --auth-status command');\n const exitCode = await runStatusCommand();\n process.exit(exitCode);\n }\n\n if (parsedArgs.logout) {\n logInfo('Running --logout command');\n const exitCode = await runLogoutCommand(parsedArgs.logoutProvider);\n process.exit(exitCode);\n }\n\n // Handle --login command (Requirement 3.1, 9.1)\n if (parsedArgs.login) {\n logInfo('Running --login command');\n\n // Validate that provider is specified\n if (!parsedArgs.loginProvider) {\n logError('Error: --login requires a provider argument.');\n logError(`Usage: --login <provider>`);\n logError(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n // Validate provider ID\n if (!isValidProviderId(parsedArgs.loginProvider)) {\n logError(`Error: Invalid provider '${parsedArgs.loginProvider}'.`);\n logError(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n const exitCode = await runLoginCommand(parsedArgs.loginProvider);\n process.exit(exitCode);\n }\n\n if (parsedArgs.configPath) {\n logInfo(`Loading configuration from: ${parsedArgs.configPath}`);\n }\n\n // Load configuration\n const config = loadConfig(parsedArgs.configPath);\n\n // CLI --custom-agents takes precedence over config file and env\n if (parsedArgs.customAgentsPath) {\n config.customAgentsPath = parsedArgs.customAgentsPath;\n }\n\n logInfo(`Configuration loaded: registryUrl=${config.registryUrl}, apiKeysPath=${config.apiKeysPath}, shutdownTimeoutSec=${config.shutdownTimeoutSec}`);\n\n // Load API keys\n const apiKeys = loadApiKeys(config.apiKeysPath);\n\n // Create registry index\n const registry = new RegistryIndex(config.registryUrl);\n\n // Fetch and parse registry on startup\n try {\n await registry.fetch();\n } catch (error) {\n if (error instanceof RegistryFetchError) {\n logError(`Failed to fetch registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n if (error instanceof RegistryParseError) {\n logError(`Failed to parse registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n logError(`Unexpected error fetching registry: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n // Load and merge custom agents if --custom-agents was provided\n if (config.customAgentsPath) {\n try {\n logInfo(`Loading custom agents from: ${config.customAgentsPath}`);\n const customAgents = loadCustomAgents(config.customAgentsPath);\n registry.mergeCustomAgents(customAgents);\n } catch (error) {\n if (error instanceof CustomAgentsLoadError) {\n logError(`Failed to load custom agents: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n if (error instanceof RegistryParseError) {\n logError(`Invalid custom agents file: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n logError(`Unexpected error loading custom agents: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n }\n\n // Create runtime manager\n const runtimeManager = new AgentRuntimeManager();\n\n // Create NDJSON handler for stdin/stdout\n const ndjsonHandler = new NDJSONHandler(process.stdout);\n\n // Create OAuth authentication components (Requirement 3.1, 10.3)\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: apiKeys,\n });\n logInfo('OAuth authentication manager initialized');\n\n // Create message router with AuthManager for OAuth support\n const router = new MessageRouter(\n registry,\n runtimeManager,\n (message: object) => ndjsonHandler.write(message),\n apiKeys,\n authManager,\n );\n\n // Set up signal handlers for graceful shutdown\n const shutdownTimeoutMs = config.shutdownTimeoutSec * 1000;\n setupSignalHandlers(runtimeManager, shutdownTimeoutMs);\n\n // Set up agent response handling\n setupAgentResponseHandling(runtimeManager, router);\n\n // Set up stdin handler and wire up routing\n setupStdinHandler(router, ndjsonHandler);\n\n logInfo('Registry Launcher ready, waiting for messages');\n}\n\n// Run main and handle any uncaught errors\nmain().catch((error: Error) => {\n logError(`Fatal error: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n});\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration loading for the Registry Launcher.\n *\n * Handles loading configuration from JSON files with support for:\n * - Environment variable overrides (ACP_REGISTRY_URL)\n * - Default values for missing fields\n * - Warning logs for missing or malformed config files\n *\n * @module config\n */\n\nimport { readFileSync } from 'node:fs';\nimport { DEFAULT_CONFIG, LauncherConfig } from './types.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Environment variable name for API keys path override.\n */\nconst ENV_API_KEYS_PATH = 'ACP_API_KEYS_PATH';\n\n/**\n * Environment variable name for custom agents file path override.\n */\nconst ENV_CUSTOM_AGENTS_PATH = 'ACP_CUSTOM_AGENTS_PATH';\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [config] ${message}`);\n}\n\n/**\n * Validate that a value is a non-empty string.\n * @param value - Value to validate\n * @returns True if value is a non-empty string\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a positive number.\n * @param value - Value to validate\n * @returns True if value is a positive number\n */\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && value > 0 && Number.isFinite(value);\n}\n\n/**\n * Parse and validate a configuration object.\n * Applies default values for missing or invalid fields.\n *\n * @param obj - Parsed JSON object\n * @returns Validated LauncherConfig with defaults applied\n */\nfunction parseConfigObject(obj: unknown): LauncherConfig {\n const config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (obj === null || typeof obj !== 'object') {\n logWarning('Config file does not contain a valid object, using defaults');\n return config;\n }\n\n const rawConfig = obj as Record<string, unknown>;\n\n // Parse registryUrl\n if ('registryUrl' in rawConfig) {\n if (isNonEmptyString(rawConfig.registryUrl)) {\n config.registryUrl = rawConfig.registryUrl;\n } else {\n logWarning('Config field \"registryUrl\" is not a valid string, using default');\n }\n }\n\n // Parse apiKeysPath\n if ('apiKeysPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.apiKeysPath)) {\n config.apiKeysPath = rawConfig.apiKeysPath;\n } else {\n logWarning('Config field \"apiKeysPath\" is not a valid string, using default');\n }\n }\n\n // Parse shutdownTimeoutSec\n if ('shutdownTimeoutSec' in rawConfig) {\n if (isPositiveNumber(rawConfig.shutdownTimeoutSec)) {\n config.shutdownTimeoutSec = rawConfig.shutdownTimeoutSec;\n } else {\n logWarning('Config field \"shutdownTimeoutSec\" is not a valid positive number, using default');\n }\n }\n\n // Parse customAgentsPath\n if ('customAgentsPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.customAgentsPath)) {\n config.customAgentsPath = rawConfig.customAgentsPath;\n } else {\n logWarning('Config field \"customAgentsPath\" is not a valid string, ignoring');\n }\n }\n\n return config;\n}\n\n/**\n * Apply environment variable overrides to the configuration.\n * Environment variables take precedence over config file values.\n *\n * @param config - Configuration to apply overrides to\n * @returns Configuration with environment overrides applied\n */\nfunction applyEnvironmentOverrides(config: LauncherConfig): LauncherConfig {\n const envRegistryUrl = process.env[ENV_REGISTRY_URL];\n const envApiKeysPath = process.env[ENV_API_KEYS_PATH];\n const envCustomAgentsPath = process.env[ENV_CUSTOM_AGENTS_PATH];\n\n const overrides: Partial<LauncherConfig> = {};\n\n if (isNonEmptyString(envRegistryUrl)) {\n overrides.registryUrl = envRegistryUrl;\n }\n\n if (isNonEmptyString(envApiKeysPath)) {\n overrides.apiKeysPath = envApiKeysPath;\n }\n\n if (isNonEmptyString(envCustomAgentsPath)) {\n overrides.customAgentsPath = envCustomAgentsPath;\n }\n\n return {\n ...config,\n ...overrides,\n };\n}\n\n/**\n * Load configuration from a JSON file.\n *\n * This function:\n * 1. Reads the config file from the specified path\n * 2. Parses it as JSON\n * 3. Validates and applies default values for missing fields\n * 4. Applies environment variable overrides\n *\n * If the config file is missing or malformed, default values are used\n * and a warning is logged to stderr.\n *\n * @param configPath - Path to the JSON config file (optional)\n * @returns Loaded and validated configuration\n */\nexport function loadConfig(configPath?: string): LauncherConfig {\n let config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (configPath) {\n try {\n const fileContent = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(fileContent);\n config = parseConfigObject(parsed);\n } catch (error) {\n if (error instanceof SyntaxError) {\n logWarning(`Config file \"${configPath}\" contains malformed JSON, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`Config file \"${configPath}\" not found, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n logWarning(`Config file \"${configPath}\" is not readable, using defaults`);\n } else {\n logWarning(`Failed to read config file \"${configPath}\": ${(error as Error).message}, using defaults`);\n }\n }\n }\n\n // Apply environment variable overrides\n config = applyEnvironmentOverrides(config);\n\n return config;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration for the Registry Launcher.\n */\nexport interface LauncherConfig {\n /** Registry URL (default: official ACP Registry) */\n registryUrl: string;\n /** Path to API keys JSON file (default: ./api-keys.json) */\n apiKeysPath: string;\n /** Agent shutdown timeout in seconds (default: 5) */\n shutdownTimeoutSec: number;\n /** Path to custom agents JSON file (optional, loaded via --custom-agents CLI arg) */\n customAgentsPath?: string;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: LauncherConfig = {\n registryUrl: 'https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json',\n apiKeysPath: './api-keys.json',\n shutdownTimeoutSec: 5,\n};\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * API Keys loading for the Registry Launcher.\n *\n * Handles loading API keys from JSON files for agent authentication.\n *\n * @module config/api-keys\n */\n\nimport { readFileSync } from 'node:fs';\n\n/**\n * API keys structure for a single agent.\n */\nexport interface AgentApiKeys {\n /** API key for the agent */\n apiKey: string;\n /** Environment variables to pass to the agent */\n env: Record<string, string>;\n}\n\n/**\n * API keys file structure.\n */\nexport interface ApiKeysFile {\n /** Map of agent ID to API keys */\n agents: Record<string, AgentApiKeys>;\n /** Version of the API keys file format */\n version: string;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [api-keys] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [api-keys] ${message}`);\n}\n\n/**\n * Load API keys from a JSON file.\n *\n * @param apiKeysPath - Path to the API keys JSON file\n * @returns Loaded API keys or empty object if file is missing/malformed\n */\nexport function loadApiKeys(apiKeysPath: string): Record<string, AgentApiKeys> {\n try {\n const fileContent = readFileSync(apiKeysPath, 'utf-8');\n const parsed = JSON.parse(fileContent) as ApiKeysFile;\n\n if (!parsed.agents || typeof parsed.agents !== 'object') {\n logWarning(`API keys file \"${apiKeysPath}\" does not contain valid \"agents\" object`);\n return {};\n }\n\n // Count agents with non-empty API keys\n const agentsWithKeys = Object.entries(parsed.agents).filter(\n ([_, keys]) => keys.apiKey && keys.apiKey.length > 0,\n );\n\n logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from \"${apiKeysPath}\"`);\n\n return parsed.agents;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`API keys file \"${apiKeysPath}\" not found, agents will not be authenticated`);\n } else if (error instanceof SyntaxError) {\n logWarning(`API keys file \"${apiKeysPath}\" contains malformed JSON`);\n } else {\n logWarning(`Failed to read API keys file \"${apiKeysPath}\": ${(error as Error).message}`);\n }\n return {};\n }\n}\n\n/**\n * Get API key for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get key for\n * @returns API key or undefined if not found\n */\nexport function getAgentApiKey(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): string | undefined {\n const keys = apiKeys[agentId];\n if (!keys || !keys.apiKey || keys.apiKey.length === 0) {\n return undefined;\n }\n return keys.apiKey;\n}\n\n/**\n * Get environment variables for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get env vars for\n * @returns Environment variables or empty object if not found\n */\nexport function getAgentEnv(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): Record<string, string> {\n const keys = apiKeys[agentId];\n if (!keys || !keys.env) {\n return {};\n }\n return keys.env;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Distribution Resolver for the ACP Registry.\n *\n * Resolves distribution metadata to spawn commands based on distribution type.\n * Supports binary (platform-specific), npx (npm), and uvx (Python) distributions.\n *\n * @module registry/resolver\n */\n\nimport {\n BinaryDistribution,\n BinaryTarget,\n Distribution,\n NpxDistribution,\n Platform,\n SpawnCommand,\n UvxDistribution,\n} from './types.js';\n\n/**\n * Error thrown when the current platform is not supported by a binary distribution.\n */\nexport class PlatformNotSupportedError extends Error {\n constructor(\n public readonly agentId: string,\n public readonly platform: Platform,\n ) {\n super(`Platform not supported: ${platform} for agent ${agentId}`);\n this.name = 'PlatformNotSupportedError';\n }\n}\n\n/**\n * Error thrown when no supported distribution type is available.\n */\nexport class NoDistributionError extends Error {\n constructor(public readonly agentId: string) {\n super(`No supported distribution type for agent ${agentId}`);\n this.name = 'NoDistributionError';\n }\n}\n\n/**\n * Get the current platform identifier.\n * Maps Node.js platform/arch to ACP Registry platform format.\n *\n * @returns Platform identifier for the current OS and architecture\n */\nexport function getCurrentPlatform(): Platform {\n const platform = process.platform;\n const arch = process.arch;\n\n if (platform === 'darwin' && arch === 'arm64') return 'darwin-aarch64';\n if (platform === 'darwin' && arch === 'x64') return 'darwin-x86_64';\n if (platform === 'linux' && arch === 'arm64') return 'linux-aarch64';\n if (platform === 'linux' && arch === 'x64') return 'linux-x86_64';\n if (platform === 'win32' && arch === 'arm64') return 'windows-aarch64';\n if (platform === 'win32' && arch === 'x64') return 'windows-x86_64';\n\n // Default to linux-x86_64 for unsupported platforms\n return 'linux-x86_64';\n}\n\n/**\n * Resolve a binary distribution to a spawn command.\n *\n * @param distribution - Binary distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if current platform is not supported\n */\nexport function resolveBinary(\n distribution: BinaryDistribution,\n agentId: string,\n): SpawnCommand {\n const currentPlatform = getCurrentPlatform();\n const target: BinaryTarget | undefined = distribution[currentPlatform];\n\n if (!target) {\n throw new PlatformNotSupportedError(agentId, currentPlatform);\n }\n\n return {\n command: target.cmd,\n args: target.args ?? [],\n env: target.env,\n };\n}\n\n/**\n * Resolve an npx distribution to a spawn command.\n *\n * @param distribution - NPX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveNpx(distribution: NpxDistribution): SpawnCommand {\n return {\n command: 'npx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a uvx distribution to a spawn command.\n *\n * @param distribution - UVX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveUvx(distribution: UvxDistribution): SpawnCommand {\n return {\n command: 'uvx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a distribution to a spawn command.\n *\n * Priority order: npx > uvx > binary\n * (npx/uvx are preferred as they don't require platform-specific handling)\n *\n * @param distribution - Distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n * @throws NoDistributionError if no distribution type is available\n */\nexport function resolve(\n distribution: Distribution,\n agentId: string,\n): SpawnCommand {\n // Prefer npx (most common, cross-platform)\n if (distribution.npx) {\n return resolveNpx(distribution.npx);\n }\n\n // Then uvx (Python packages)\n if (distribution.uvx) {\n return resolveUvx(distribution.uvx);\n }\n\n // Finally binary (platform-specific)\n if (distribution.binary) {\n return resolveBinary(distribution.binary, agentId);\n }\n\n throw new NoDistributionError(agentId);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Registry Index for the ACP Registry.\n *\n * Handles fetching, parsing, and querying the ACP Registry.\n * Supports configurable registry URL via environment variable.\n *\n * @module registry/index\n */\n\nimport { AgentAuthMethod, Distribution, McpServerConfig, Registry, RegistryAgent, SpawnCommand } from './types.js';\nimport { resolve as resolveDistribution } from './resolver.js';\nimport { readFileSync } from 'node:fs';\n\n// Re-export types for external use\nexport type {\n Platform,\n BinaryDistribution,\n BinaryTarget,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n McpServerConfig,\n AgentAuthMethod,\n} from './types.js';\n\n// Re-export resolver functions and errors for external use\nexport { PlatformNotSupportedError, NoDistributionError, getCurrentPlatform, resolve } from './resolver.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Error thrown when registry fetch fails.\n */\nexport class RegistryFetchError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryFetchError';\n }\n}\n\n/**\n * Error thrown when registry JSON is malformed.\n */\nexport class RegistryParseError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryParseError';\n }\n}\n\n/**\n * Error thrown when an agent is not found in the registry.\n */\nexport class AgentNotFoundError extends Error {\n constructor(public readonly agentId: string) {\n super(`Agent not found: ${agentId}`);\n this.name = 'AgentNotFoundError';\n }\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [registry] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [registry] ${message}`);\n}\n\n/**\n * Cached auth requirements for an agent.\n *\n * Requirements: 11.2, 11.3\n */\nexport interface AgentAuthRequirements {\n /** Whether authentication is required */\n authRequired: boolean;\n /** Authentication methods supported/required by the agent */\n authMethods: AgentAuthMethod[];\n /** Primary OAuth provider ID (first oauth2 method's providerId) */\n primaryOAuthProviderId?: string;\n}\n\n/**\n * Interface for the RegistryIndex.\n */\nexport interface IRegistryIndex {\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n fetch(): Promise<void>;\n\n /**\n * Look up an agent by ID.\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined;\n\n /**\n * Resolve an agent ID to a spawn command.\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand;\n\n /**\n * Get auth requirements for an agent.\n *\n * Checks the agent definition in the registry for `authRequired` or `authMethods` fields.\n * Results are cached per agent for efficient repeated lookups.\n *\n * Requirements: 11.2, 11.3\n *\n * @param agentId - Agent ID to query\n * @returns Auth requirements or undefined if agent not found\n */\n getAuthRequirements(agentId: string): AgentAuthRequirements | undefined;\n}\n\n/**\n * Validate that a value is a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a valid distribution object.\n * Distribution must have at least one of: binary, npx, uvx\n */\nfunction isValidDistribution(value: unknown): value is Distribution {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n const dist = value as Record<string, unknown>;\n\n // Must have at least one distribution type\n const hasBinary = dist.binary !== undefined && typeof dist.binary === 'object';\n const hasNpx = dist.npx !== undefined && typeof dist.npx === 'object';\n const hasUvx = dist.uvx !== undefined && typeof dist.uvx === 'object';\n\n if (!hasBinary && !hasNpx && !hasUvx) {\n return false;\n }\n\n // Validate npx if present\n if (hasNpx) {\n const npx = dist.npx as Record<string, unknown>;\n if (!isNonEmptyString(npx.package)) {\n return false;\n }\n }\n\n // Validate uvx if present\n if (hasUvx) {\n const uvx = dist.uvx as Record<string, unknown>;\n if (!isNonEmptyString(uvx.package)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate and parse a single MCP server configuration.\n */\nfunction parseMcpServer(value: unknown, agentIndex: number, serverIndex: number): McpServerConfig | null {\n if (value === null || typeof value !== 'object') {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] is not an object, skipping`);\n return null;\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.name)) {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing \"name\" field, skipping`);\n return null;\n }\n\n if (!isNonEmptyString(raw.command)) {\n logWarning(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing \"command\" field, skipping`);\n return null;\n }\n\n const server: McpServerConfig = {\n name: raw.name,\n command: raw.command,\n };\n\n // Optional args\n if (Array.isArray(raw.args)) {\n server.args = raw.args.filter((a): a is string => typeof a === 'string');\n }\n\n // Optional env\n if (raw.env !== null && typeof raw.env === 'object' && !Array.isArray(raw.env)) {\n const env: Record<string, string> = {};\n for (const [key, val] of Object.entries(raw.env as Record<string, unknown>)) {\n if (typeof val === 'string') {\n env[key] = val;\n }\n }\n if (Object.keys(env).length > 0) {\n server.env = env;\n }\n }\n\n return server;\n}\n\n/**\n * Parse and validate mcpServers array for an agent.\n */\nfunction parseMcpServers(servers: unknown[], agentIndex: number): McpServerConfig[] {\n const result: McpServerConfig[] = [];\n\n for (let i = 0; i < servers.length; i++) {\n const server = parseMcpServer(servers[i], agentIndex, i);\n if (server !== null) {\n result.push(server);\n }\n }\n\n return result;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [registry] ${message}`);\n}\n\n/**\n * Valid auth method types for validation.\n */\nconst VALID_AUTH_METHOD_TYPES: readonly string[] = ['oauth2', 'api-key'];\n\n/**\n * Validate and parse a single auth method configuration.\n *\n * Requirements: 11.2, 11.3\n *\n * @param value - Raw auth method object\n * @param agentIndex - Index of the agent in the registry (for error messages)\n * @param methodIndex - Index of the auth method in the array (for error messages)\n * @returns Parsed AgentAuthMethod or null if invalid\n */\nfunction parseAuthMethod(value: unknown, agentIndex: number, methodIndex: number): AgentAuthMethod | null {\n if (value === null || typeof value !== 'object') {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] is not an object, skipping`);\n return null;\n }\n\n const raw = value as Record<string, unknown>;\n\n // Validate required 'id' field\n if (!isNonEmptyString(raw.id)) {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing \"id\" field, skipping`);\n return null;\n }\n\n // Validate required 'type' field\n if (!isNonEmptyString(raw.type) || !VALID_AUTH_METHOD_TYPES.includes(raw.type)) {\n logWarning(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing \"type\" field (must be 'oauth2' or 'api-key'), skipping`);\n return null;\n }\n\n const method: AgentAuthMethod = {\n id: raw.id,\n type: raw.type as 'oauth2' | 'api-key',\n };\n\n // Optional providerId field\n if (isNonEmptyString(raw.providerId)) {\n method.providerId = raw.providerId;\n }\n\n return method;\n}\n\n/**\n * Parse and validate authMethods array for an agent.\n *\n * Requirements: 11.2, 11.3\n *\n * @param methods - Raw auth methods array\n * @param agentIndex - Index of the agent in the registry (for error messages)\n * @returns Array of validated AgentAuthMethod entries\n */\nfunction parseAuthMethods(methods: unknown[], agentIndex: number): AgentAuthMethod[] {\n const result: AgentAuthMethod[] = [];\n\n for (let i = 0; i < methods.length; i++) {\n const method = parseAuthMethod(methods[i], agentIndex, i);\n if (method !== null) {\n result.push(method);\n }\n }\n\n return result;\n}\n\n/**\n * Validate and parse a registry agent entry.\n */\nfunction parseAgent(value: unknown, index: number): RegistryAgent {\n if (value === null || typeof value !== 'object') {\n throw new RegistryParseError(`Agent at index ${index} is not an object`);\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.id)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"id\" field`);\n }\n\n if (!isNonEmptyString(raw.name)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"name\" field`);\n }\n\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"version\" field`);\n }\n\n if (!isValidDistribution(raw.distribution)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"distribution\" field`);\n }\n\n const agent: RegistryAgent = {\n id: raw.id,\n name: raw.name,\n version: raw.version,\n distribution: raw.distribution as Distribution,\n };\n\n // Optional fields\n if (typeof raw.description === 'string') {\n agent.description = raw.description;\n }\n\n if (typeof raw.repository === 'string') {\n agent.repository = raw.repository;\n }\n\n if (Array.isArray(raw.authors)) {\n agent.authors = raw.authors.filter((a): a is string => typeof a === 'string');\n }\n\n if (typeof raw.license === 'string') {\n agent.license = raw.license;\n }\n\n if (typeof raw.icon === 'string') {\n agent.icon = raw.icon;\n }\n\n // Parse mcpServers if present\n if (Array.isArray(raw.mcpServers)) {\n const mcpServers = parseMcpServers(raw.mcpServers, index);\n if (mcpServers.length > 0) {\n agent.mcpServers = mcpServers;\n }\n }\n\n // Parse authRequired if present (Requirements: 11.2, 11.3)\n if (typeof raw.authRequired === 'boolean') {\n agent.authRequired = raw.authRequired;\n }\n\n // Parse authMethods if present (Requirements: 11.2, 11.3)\n if (Array.isArray(raw.authMethods)) {\n const authMethods = parseAuthMethods(raw.authMethods, index);\n if (authMethods.length > 0) {\n agent.authMethods = authMethods;\n }\n }\n\n return agent;\n}\n\n/**\n * Parse and validate registry JSON data.\n *\n * @param data - Raw JSON data to parse\n * @returns Parsed and validated Registry object\n * @throws RegistryParseError if JSON is malformed or invalid\n */\nexport function parseRegistry(data: unknown): Registry {\n if (data === null || typeof data !== 'object') {\n throw new RegistryParseError('Registry data is not an object');\n }\n\n const raw = data as Record<string, unknown>;\n\n // Validate version field\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError('Registry has invalid or missing \"version\" field');\n }\n\n // Validate agents array\n if (!Array.isArray(raw.agents)) {\n throw new RegistryParseError('Registry has invalid or missing \"agents\" field');\n }\n\n // Parse each agent\n const agents: RegistryAgent[] = [];\n for (let i = 0; i < raw.agents.length; i++) {\n agents.push(parseAgent(raw.agents[i], i));\n }\n\n return {\n version: raw.version,\n agents,\n };\n}\n\n/**\n * Registry Index implementation.\n *\n * Fetches, parses, and provides lookup functionality for the ACP Registry.\n */\nexport class RegistryIndex implements IRegistryIndex {\n /** Configured registry URL */\n private readonly registryUrl: string;\n\n /** Parsed registry data (null until fetch() is called) */\n private registry: Registry | null = null;\n\n /** Map of agent ID to agent entry for fast lookup */\n private agentMap: Map<string, RegistryAgent> = new Map();\n\n /**\n * Cache of auth requirements per agent.\n *\n * Requirements: 11.2, 11.3\n */\n private authRequirementsCache: Map<string, AgentAuthRequirements> = new Map();\n\n /**\n * Create a new RegistryIndex.\n *\n * @param registryUrl - URL to fetch the registry from (can be overridden by ACP_REGISTRY_URL env var)\n */\n constructor(registryUrl: string) {\n // Environment variable takes precedence\n const envUrl = process.env[ENV_REGISTRY_URL];\n this.registryUrl = isNonEmptyString(envUrl) ? envUrl : registryUrl;\n }\n\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n *\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n async fetch(): Promise<void> {\n logInfo(`Fetching registry from ${this.registryUrl}`);\n\n let response: Response;\n try {\n response = await fetch(this.registryUrl);\n } catch (error) {\n const message = `Failed to fetch registry from ${this.registryUrl}: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n if (!response.ok) {\n const message = `Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;\n logError(message);\n throw new RegistryFetchError(message);\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch (error) {\n const message = `Failed to read registry response body: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch (error) {\n const message = `Failed to parse registry JSON: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n try {\n this.registry = parseRegistry(data);\n } catch (error) {\n if (error instanceof RegistryParseError) {\n logError(error.message);\n throw error;\n }\n const message = `Failed to validate registry data: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n // Build the agent lookup map\n this.agentMap.clear();\n this.authRequirementsCache.clear();\n for (const agent of this.registry.agents) {\n this.agentMap.set(agent.id, agent);\n }\n\n logInfo(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`);\n }\n\n /**\n * Look up an agent by ID.\n *\n * @param agentId - Agent ID to look up\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined {\n return this.agentMap.get(agentId);\n }\n\n /**\n * Resolve an agent ID to a spawn command.\n *\n * @param agentId - Agent ID to resolve\n * @returns Resolved spawn command\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand {\n const agent = this.lookup(agentId);\n if (!agent) {\n throw new AgentNotFoundError(agentId);\n }\n\n return resolveDistribution(agent.distribution, agentId);\n }\n\n /**\n * Get the parsed registry data.\n * @returns The parsed registry or null if not yet fetched\n */\n getRegistry(): Registry | null {\n return this.registry;\n }\n\n /**\n * Get auth requirements for an agent.\n *\n * Checks the agent definition in the registry for `authRequired` or `authMethods` fields.\n * Results are cached per agent for efficient repeated lookups.\n *\n * Requirements: 11.2, 11.3\n *\n * @param agentId - Agent ID to query\n * @returns Auth requirements or undefined if agent not found\n */\n getAuthRequirements(agentId: string): AgentAuthRequirements | undefined {\n // Check cache first\n const cached = this.authRequirementsCache.get(agentId);\n if (cached !== undefined) {\n return cached;\n }\n\n // Look up agent\n const agent = this.lookup(agentId);\n if (!agent) {\n return undefined;\n }\n\n // Build auth requirements from agent definition\n const authMethods = agent.authMethods ?? [];\n\n // Determine if auth is required:\n // - Explicitly set via authRequired field\n // - Implicitly required if authMethods contains oauth2 methods\n const hasOAuthMethods = authMethods.some(m => m.type === 'oauth2');\n const authRequired = agent.authRequired ?? hasOAuthMethods;\n\n // Find primary OAuth provider ID (first oauth2 method with providerId)\n let primaryOAuthProviderId: string | undefined;\n for (const method of authMethods) {\n if (method.type === 'oauth2' && method.providerId) {\n primaryOAuthProviderId = method.providerId;\n break;\n }\n }\n\n const requirements: AgentAuthRequirements = {\n authRequired,\n authMethods,\n primaryOAuthProviderId,\n };\n\n // Cache the result\n this.authRequirementsCache.set(agentId, requirements);\n\n if (authRequired) {\n logInfo(`Agent \"${agentId}\" requires authentication${primaryOAuthProviderId ? ` (OAuth provider: ${primaryOAuthProviderId})` : ''}`);\n }\n\n return requirements;\n }\n\n /**\n * Clear the auth requirements cache for a specific agent or all agents.\n *\n * @param agentId - Optional agent ID to clear. If not provided, clears all cached requirements.\n */\n clearAuthRequirementsCache(agentId?: string): void {\n if (agentId) {\n this.authRequirementsCache.delete(agentId);\n logInfo(`Cleared auth requirements cache for agent \"${agentId}\"`);\n } else {\n this.authRequirementsCache.clear();\n logInfo('Cleared all auth requirements cache');\n }\n }\n\n /**\n * Merge custom agents into the registry.\n *\n * Custom agents take precedence over remote registry agents with the same ID.\n * This allows users to override or extend the official ACP Registry with\n * locally-defined agents (e.g., AWS Bedrock, custom internal agents).\n *\n * @param agents - Array of custom RegistryAgent entries to merge\n */\n mergeCustomAgents(agents: RegistryAgent[]): void {\n if (agents.length === 0) {\n return;\n }\n\n // Initialize registry if fetch() hasn't been called yet\n if (!this.registry) {\n this.registry = { version: 'custom', agents: [] };\n }\n\n for (const agent of agents) {\n // Custom agents override remote agents with the same ID\n const existingIndex = this.registry.agents.findIndex((a) => a.id === agent.id);\n if (existingIndex !== -1) {\n this.registry.agents[existingIndex] = agent;\n logInfo(`Custom agent \"${agent.id}\" overrides remote registry entry`);\n } else {\n this.registry.agents.push(agent);\n logInfo(`Custom agent \"${agent.id}\" added to registry`);\n }\n\n this.agentMap.set(agent.id, agent);\n\n // Clear cached auth requirements for this agent (may have changed)\n this.authRequirementsCache.delete(agent.id);\n }\n\n logInfo(`Registry now contains ${this.registry.agents.length} agents (${agents.length} custom)`);\n }\n}\n\n/**\n * Error thrown when custom agents file cannot be loaded.\n */\nexport class CustomAgentsLoadError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'CustomAgentsLoadError';\n }\n}\n\n/**\n * Load and validate custom agents from a JSON file.\n *\n * The file must contain a JSON object with an \"agents\" array field.\n * Each agent entry is validated using the same rules as the remote registry.\n *\n * Expected file format:\n * ```json\n * {\n * \"agents\": [\n * {\n * \"id\": \"my-custom-agent\",\n * \"name\": \"My Custom Agent\",\n * \"version\": \"1.0.0\",\n * \"distribution\": {\n * \"npx\": { \"package\": \"@my-org/my-agent@latest\" }\n * }\n * }\n * ]\n * }\n * ```\n *\n * @param filePath - Path to the custom agents JSON file\n * @returns Array of validated RegistryAgent entries\n * @throws CustomAgentsLoadError if file cannot be read or parsed\n * @throws RegistryParseError if agent entries are malformed\n */\nexport function loadCustomAgents(filePath: string): RegistryAgent[] {\n let fileContent: string;\n try {\n fileContent = readFileSync(filePath, 'utf-8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CustomAgentsLoadError(`Custom agents file not found: ${filePath}`);\n }\n if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n throw new CustomAgentsLoadError(`Custom agents file not readable: ${filePath}`);\n }\n throw new CustomAgentsLoadError(\n `Failed to read custom agents file \"${filePath}\": ${(error as Error).message}`,\n error as Error,\n );\n }\n\n let data: unknown;\n try {\n data = JSON.parse(fileContent);\n } catch (error) {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" contains malformed JSON: ${(error as Error).message}`,\n error as Error,\n );\n }\n\n if (data === null || typeof data !== 'object') {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" does not contain a valid object`,\n );\n }\n\n const raw = data as Record<string, unknown>;\n\n if (!Array.isArray(raw.agents)) {\n throw new CustomAgentsLoadError(\n `Custom agents file \"${filePath}\" does not contain a valid \"agents\" array`,\n );\n }\n\n // Reuse parseRegistry for validation \u2014 wrap in registry structure\n const registryData = {\n version: 'custom',\n agents: raw.agents,\n };\n\n const parsed = parseRegistry(registryData);\n return parsed.agents;\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * NDJSON (Newline-Delimited JSON) stream handler for the Registry Launcher.\n *\n * Handles buffering and parsing of NDJSON messages from stdin and writing\n * NDJSON messages to stdout. Supports partial reads across multiple chunks.\n *\n * @module stream/ndjson-handler\n */\n\nimport { Writable } from 'node:stream';\n\n/**\n * Callback type for parsed messages.\n */\nexport type MessageCallback = (message: object) => void;\n\n/**\n * Callback type for parse errors.\n */\nexport type ErrorCallback = (error: Error, line: string) => void;\n\n/**\n * Interface for NDJSON stream handling.\n */\nexport interface INDJSONHandler {\n /**\n * Register a callback for parsed messages.\n */\n onMessage(callback: MessageCallback): void;\n\n /**\n * Register a callback for parse errors.\n */\n onError(callback: ErrorCallback): void;\n\n /**\n * Write a message to the output stream.\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean;\n\n /**\n * Process incoming data chunk (call from stdin 'data' event).\n */\n processChunk(chunk: Buffer): void;\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`);\n}\n\n/**\n * NDJSON stream handler implementation.\n *\n * This class handles:\n * - Buffering incoming data until complete newline-delimited messages are received\n * - Handling partial JSON messages that span multiple read operations\n * - Appending newline characters after each JSON message when writing\n * - Splitting on newline boundaries when reading\n * - Logging errors and skipping malformed lines\n */\nexport class NDJSONHandler implements INDJSONHandler {\n /** Buffer for accumulating partial data */\n private buffer: string = '';\n\n /** Output stream for writing messages */\n private readonly output: Writable;\n\n /** Registered message callback */\n private messageCallback: MessageCallback | null = null;\n\n /** Registered error callback */\n private errorCallback: ErrorCallback | null = null;\n\n /**\n * Create a new NDJSONHandler.\n * @param output - Writable stream for output (typically process.stdout)\n */\n constructor(output: Writable) {\n this.output = output;\n }\n\n /**\n * Register a callback for parsed messages.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with each parsed message\n */\n onMessage(callback: MessageCallback): void {\n this.messageCallback = callback;\n }\n\n /**\n * Register a callback for parse errors.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with parse errors\n */\n onError(callback: ErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /**\n * Write a message to the output stream.\n *\n * Serializes the message as JSON and appends a newline character.\n *\n * @param message - Object to serialize and write\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean {\n if (!this.output.writable) {\n return false;\n }\n\n try {\n const json = JSON.stringify(message);\n this.output.write(json + '\\n');\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Process incoming data chunk.\n *\n * Buffers data and emits complete messages when newline boundaries are found.\n * Handles partial messages that span multiple chunks.\n *\n * @param chunk - Buffer containing incoming data\n */\n processChunk(chunk: Buffer): void {\n // Append chunk to buffer\n this.buffer += chunk.toString('utf-8');\n\n // Process complete lines\n this.processBuffer();\n }\n\n /**\n * Process the internal buffer, extracting and parsing complete lines.\n *\n * Splits on newline boundaries and parses each complete line as JSON.\n * Incomplete lines remain in the buffer for the next chunk.\n */\n private processBuffer(): void {\n let newlineIndex: number;\n\n // Process all complete lines in the buffer\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n // Extract the line (without the newline)\n const line = this.buffer.slice(0, newlineIndex);\n\n // Remove the processed line and newline from buffer\n this.buffer = this.buffer.slice(newlineIndex + 1);\n\n // Skip empty lines\n if (line.trim().length === 0) {\n continue;\n }\n\n // Parse and emit the message\n this.parseLine(line);\n }\n }\n\n /**\n * Parse a single line as JSON and emit the message.\n *\n * If parsing fails, logs the error and invokes the error callback.\n * Malformed lines are skipped.\n *\n * @param line - Line to parse as JSON\n */\n private parseLine(line: string): void {\n try {\n const message = JSON.parse(line);\n\n // Ensure the parsed value is an object (not a primitive)\n if (message === null || typeof message !== 'object') {\n const error = new Error('Parsed JSON is not an object');\n logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);\n this.errorCallback?.(error, line);\n return;\n }\n\n // Emit the parsed message\n this.messageCallback?.(message);\n } catch (error) {\n // Log the error and skip the malformed line\n logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);\n this.errorCallback?.(error as Error, line);\n }\n }\n\n /**\n * Truncate a line for logging purposes.\n * @param line - Line to truncate\n * @param maxLength - Maximum length (default: 100)\n * @returns Truncated line with ellipsis if needed\n */\n private truncateLine(line: string, maxLength: number = 100): string {\n if (line.length <= maxLength) {\n return line;\n }\n return line.slice(0, maxLength) + '...';\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport type { AgentRuntime, RuntimeState } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_TERMINATE_TIMEOUT_MS = 5000;\n\n/**\n * Implementation of AgentRuntime that manages a spawned agent process.\n *\n * Handles process spawning with non-blocking file descriptors,\n * message writing to stdin, and graceful termination with SIGTERM/SIGKILL.\n */\nexport class AgentRuntimeImpl implements AgentRuntime {\n public readonly agentId: string;\n public state: RuntimeState;\n public readonly process: ChildProcess;\n\n private readonly onExitCallback?: (code: number | null, signal: string | null) => void;\n\n /**\n * Create a new AgentRuntime by spawning a process.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command\n * @param onExit - Optional callback when process exits\n */\n private constructor(\n agentId: string,\n process: ChildProcess,\n onExit?: (code: number | null, signal: string | null) => void,\n ) {\n this.agentId = agentId;\n this.process = process;\n this.state = 'starting';\n this.onExitCallback = onExit;\n\n this.setupProcessHandlers();\n }\n\n /**\n * Spawn a new agent process and create an AgentRuntime instance.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command with command, args, and optional env\n * @param onExit - Optional callback when process exits\n * @returns A new AgentRuntime instance\n */\n public static spawn(\n agentId: string,\n spawnCommand: SpawnCommand,\n onExit?: (code: number | null, signal: string | null) => void,\n ): AgentRuntimeImpl {\n const childProcess = spawn(spawnCommand.command, spawnCommand.args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n ...spawnCommand.env,\n },\n // Detach from parent's controlling terminal to avoid signal propagation issues\n detached: false,\n });\n\n // Set file descriptors to non-blocking mode\n // Node.js streams are already non-blocking by default when using 'pipe',\n // but we ensure the streams are in flowing mode for proper async handling\n if (childProcess.stdout) {\n childProcess.stdout.setEncoding('utf8');\n }\n if (childProcess.stderr) {\n childProcess.stderr.setEncoding('utf8');\n }\n\n return new AgentRuntimeImpl(agentId, childProcess, onExit);\n }\n\n /**\n * Set up event handlers for the child process.\n */\n private setupProcessHandlers(): void {\n // Handle process spawn success\n this.process.on('spawn', () => {\n if (this.state === 'starting') {\n this.state = 'running';\n }\n });\n\n // Handle process errors (e.g., spawn failure)\n this.process.on('error', (error: Error) => {\n this.state = 'stopped';\n // Log to stderr as per requirements\n process.stderr.write(\n `[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}\\n`,\n );\n });\n\n // Handle process exit\n this.process.on('exit', (code: number | null, signal: string | null) => {\n this.state = 'stopped';\n if (this.onExitCallback) {\n this.onExitCallback(code, signal);\n }\n });\n\n // Handle stdin errors (e.g., broken pipe)\n if (this.process.stdin) {\n this.process.stdin.on('error', (error: Error) => {\n // Log stdin write errors but don't change state\n process.stderr.write(\n `[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}\\n`,\n );\n });\n }\n }\n\n /**\n * Write a message to the agent's stdin as NDJSON.\n *\n * @param message - The message object to send\n * @returns true if the write was accepted, false if backpressure or error\n */\n public write(message: object): boolean {\n if (this.state !== 'running' && this.state !== 'starting') {\n return false;\n }\n\n if (!this.process.stdin || this.process.stdin.destroyed) {\n return false;\n }\n\n try {\n const ndjsonLine = JSON.stringify(message) + '\\n';\n return this.process.stdin.write(ndjsonLine);\n } catch {\n return false;\n }\n }\n\n /**\n * Terminate the agent process gracefully.\n *\n * Sends SIGTERM first, then SIGKILL after timeout if process doesn't exit.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n * @returns Promise that resolves when process has exited\n */\n public async terminate(timeout: number = DEFAULT_TERMINATE_TIMEOUT_MS): Promise<void> {\n // Already stopped or stopping\n if (this.state === 'stopped') {\n return;\n }\n\n if (this.state === 'stopping') {\n // Wait for existing termination to complete\n return this.waitForExit();\n }\n\n this.state = 'stopping';\n\n // Close stdin to signal no more input\n if (this.process.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.end();\n }\n\n // Send SIGTERM for graceful shutdown\n this.process.kill('SIGTERM');\n\n // Wait for exit with timeout\n const exitPromise = this.waitForExit();\n const timeoutPromise = new Promise<'timeout'>((resolve) => {\n setTimeout(() => resolve('timeout'), timeout);\n });\n\n const result = await Promise.race([exitPromise, timeoutPromise]);\n\n if (result === 'timeout' && !this.process.killed && this.process.exitCode === null) {\n // Force kill if still running after timeout\n this.process.kill('SIGKILL');\n await this.waitForExit();\n }\n }\n\n /**\n * Wait for the process to exit.\n *\n * @returns Promise that resolves when process exits\n */\n private waitForExit(): Promise<void> {\n if (this.state === 'stopped') {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.process.once('exit', () => {\n resolve();\n });\n });\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AgentRuntime } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\nimport { AgentRuntimeImpl } from './agent-runtime.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;\n\n/**\n * Callback type for agent exit events.\n */\nexport type AgentExitCallback = (agentId: string, code: number | null) => void;\n\n/**\n * Manager for agent runtime lifecycle.\n *\n * Handles spawning, tracking, and terminating agent processes.\n * Stores runtimes in a Map keyed by agentId.\n */\nexport class AgentRuntimeManager {\n /** Map of agentId to AgentRuntime instances */\n private readonly runtimes: Map<string, AgentRuntime> = new Map();\n\n /** Registered callbacks for agent exit events */\n private readonly exitCallbacks: AgentExitCallback[] = [];\n\n /**\n * Get an existing runtime or spawn a new one for the given agentId.\n *\n * If a runtime already exists for the agentId, returns it.\n * Otherwise, spawns a new agent process using the provided spawn command.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command for spawning a new process\n * @returns The existing or newly spawned AgentRuntime\n */\n public async getOrSpawn(agentId: string, spawnCommand: SpawnCommand): Promise<AgentRuntime> {\n // Return existing runtime if available\n const existing = this.runtimes.get(agentId);\n if (existing && existing.state !== 'stopped') {\n return existing;\n }\n\n // Spawn new runtime\n const runtime = AgentRuntimeImpl.spawn(\n agentId,\n spawnCommand,\n (code: number | null, _signal: string | null) => {\n this.handleAgentExit(agentId, code);\n },\n );\n\n // Store in map\n this.runtimes.set(agentId, runtime);\n\n return runtime;\n }\n\n /**\n * Get an existing runtime without spawning.\n *\n * @param agentId - The agent identifier\n * @returns The AgentRuntime if it exists, undefined otherwise\n */\n public get(agentId: string): AgentRuntime | undefined {\n return this.runtimes.get(agentId);\n }\n\n /**\n * Terminate a specific agent runtime.\n *\n * Sends SIGTERM to the agent process and waits for it to exit.\n * If the process doesn't exit within the timeout, sends SIGKILL.\n *\n * @param agentId - The agent identifier\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminate(agentId: string, timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const runtime = this.runtimes.get(agentId);\n if (!runtime) {\n return;\n }\n\n await runtime.terminate(timeout);\n this.runtimes.delete(agentId);\n }\n\n /**\n * Terminate all agent runtimes for graceful shutdown.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminateAll(timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const terminatePromises: Promise<void>[] = [];\n\n for (const [agentId, runtime] of this.runtimes) {\n if (runtime.state !== 'stopped') {\n terminatePromises.push(\n runtime.terminate(timeout).then(() => {\n this.runtimes.delete(agentId);\n }),\n );\n }\n }\n\n await Promise.all(terminatePromises);\n }\n\n /**\n * Register a callback for agent exit events.\n *\n * The callback is invoked when an agent process exits unexpectedly.\n *\n * @param callback - Function to call when an agent exits\n */\n public onAgentExit(callback: AgentExitCallback): void {\n this.exitCallbacks.push(callback);\n }\n\n /**\n * Handle agent process exit.\n *\n * Removes the runtime from the map and notifies registered callbacks.\n *\n * @param agentId - The agent identifier\n * @param code - The exit code (null if terminated by signal)\n */\n private handleAgentExit(agentId: string, code: number | null): void {\n // Remove from map\n this.runtimes.delete(agentId);\n\n // Notify all registered callbacks\n for (const callback of this.exitCallbacks) {\n try {\n callback(agentId, code);\n } catch {\n // Ignore callback errors to prevent one failing callback from affecting others\n }\n }\n }\n\n /**\n * Get the number of active runtimes.\n *\n * @returns The count of runtimes currently in the map\n */\n public get size(): number {\n return this.runtimes.size;\n }\n\n /**\n * Check if a runtime exists for the given agentId.\n *\n * @param agentId - The agent identifier\n * @returns true if a runtime exists, false otherwise\n */\n public has(agentId: string): boolean {\n return this.runtimes.has(agentId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Message Router for the Registry Launcher.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles agentId extraction, message transformation, and error response generation.\n * Integrates with AuthManager for OAuth authentication (Requirements 11.2, 11.4).\n *\n * @module router/message-router\n */\n\nimport { spawn } from 'node:child_process';\nimport type { IRegistryIndex } from '../registry/index.js';\nimport { AgentNotFoundError, PlatformNotSupportedError } from '../registry/index.js';\nimport type { AgentRuntimeManager } from '../runtime/manager.js';\nimport type { AgentRuntime } from '../runtime/types.js';\nimport { getAgentApiKey, getAgentEnv } from '../config/api-keys.js';\nimport type { AuthManager } from '../auth/auth-manager.js';\nimport type { AcpAuthMethod, AuthProviderId } from '../auth/types.js';\nimport { isValidProviderId } from '../auth/types.js';\n\n/**\n * JSON-RPC error codes for routing errors.\n */\nexport const RoutingErrorCodes = {\n /** Missing agentId in request */\n MISSING_AGENT_ID: -32600,\n /** Agent not found in registry */\n AGENT_NOT_FOUND: -32001,\n /** Platform not supported for binary distribution */\n PLATFORM_NOT_SUPPORTED: -32002,\n /** Agent spawn failed */\n SPAWN_FAILED: -32003,\n /** Authentication required (Requirement 11.2) */\n AUTH_REQUIRED: -32004,\n} as const;\n\n// =============================================================================\n// Auth Method Parsing Types and Constants (Task 21.1)\n// =============================================================================\n\n/**\n * Valid auth method types from agent responses.\n * - 'oauth2': Standard OAuth 2.1 flow (client handles OAuth)\n * - 'agent': Agent handles OAuth internally (ACP-compliant, default)\n * - 'terminal': Interactive terminal auth (TUI)\n * - 'api-key': API key authentication\n */\nexport type AuthMethodType = 'oauth2' | 'agent' | 'terminal' | 'api-key';\n\n/**\n * Parsed auth method with validated fields.\n * Discriminated union for type-safe handling.\n */\nexport type ParsedAuthMethod =\n | { kind: 'oauth2'; id: string; providerId: AuthProviderId }\n | { kind: 'agent'; id: string; providerId?: AuthProviderId }\n | { kind: 'terminal'; id: string; args?: string[]; env?: Record<string, string> }\n | { kind: 'api-key'; id: string; providerId?: AuthProviderId };\n\n/**\n * Explicit mapping from auth method IDs to provider IDs.\n * Security: Uses explicit allowlist mapping, no substring heuristics.\n *\n * Requirement 3.1: Support OAuth authentication with type \"agent\" or \"oauth2\"\n * Requirement 11.2: Map authMethod.id to AuthProviderId\n */\nexport const AUTH_METHOD_ID_TO_PROVIDER: Readonly<Record<string, AuthProviderId>> = {\n // OAuth2 method IDs\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n 'oauth2-github': 'github',\n 'oauth2-google': 'google',\n 'oauth2-cognito': 'cognito',\n 'oauth2-azure': 'azure',\n // Agent auth method IDs (legacy format)\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n 'agent-github': 'github',\n 'agent-google': 'google',\n 'agent-cognito': 'cognito',\n 'agent-azure': 'azure',\n // API key method IDs - these map to providers that support API key auth\n // Note: OpenAI and Anthropic API key mappings will be handled by model-credentials module\n 'github-api-key': 'github',\n 'google-api-key': 'google',\n 'azure-api-key': 'azure',\n 'cognito-api-key': 'cognito',\n} as const;\n\n/**\n * Maximum number of auth methods to process (DoS protection).\n */\nconst MAX_AUTH_METHODS = 50;\n\n/**\n * Maximum length for auth method ID strings.\n */\nconst MAX_METHOD_ID_LENGTH = 128;\n\n/**\n * Valid auth method types allowlist.\n */\nconst VALID_AUTH_METHOD_TYPES: readonly string[] = ['oauth2', 'agent', 'terminal', 'api-key'];\n\n/**\n * Parse and validate auth methods from agent initialize response.\n *\n * Extracts type and providerId from each auth method, using explicit mapping\n * for id-to-provider resolution. Validates all fields and rejects invalid methods.\n *\n * Security considerations:\n * - Uses explicit allowlist for method types\n * - Uses explicit mapping for id-to-provider (no substring heuristics)\n * - Validates providerId against known providers\n * - Limits number of methods processed (DoS protection)\n * - Deduplicates by method ID\n *\n * Requirement 3.1: Identify methods with type \"oauth2\" or \"agent\"\n * Requirement 11.2: Map authMethod.id to AuthProviderId using explicit mapping\n *\n * @param raw - Raw auth methods array from agent response (untrusted input)\n * @returns Array of validated and parsed auth methods\n */\nexport function parseAuthMethods(raw: unknown): ParsedAuthMethod[] {\n // Validate input is an array\n if (!Array.isArray(raw)) {\n logError('authMethods is not an array, skipping parsing');\n return [];\n }\n\n // Limit number of methods (DoS protection)\n const methods = raw.slice(0, MAX_AUTH_METHODS);\n const parsed: ParsedAuthMethod[] = [];\n const seenIds = new Set<string>();\n\n for (const method of methods) {\n const result = parseAuthMethod(method, seenIds);\n if (result) {\n parsed.push(result);\n seenIds.add(result.id);\n }\n }\n\n logInfo(`Parsed ${parsed.length} valid auth methods from ${methods.length} raw methods`);\n return parsed;\n}\n\n/**\n * Parse a single auth method entry.\n *\n * @param method - Raw method object from agent response\n * @param seenIds - Set of already processed method IDs (for deduplication)\n * @returns Parsed auth method or null if invalid\n */\nfunction parseAuthMethod(method: unknown, seenIds: Set<string>): ParsedAuthMethod | null {\n // Validate method is an object\n if (method === null || typeof method !== 'object') {\n return null;\n }\n\n const obj = method as Record<string, unknown>;\n\n // Extract and validate id\n const id = obj.id;\n if (typeof id !== 'string' || id.length === 0 || id.length > MAX_METHOD_ID_LENGTH) {\n logError(`Invalid auth method id: ${typeof id === 'string' ? id.substring(0, 50) : typeof id}`);\n return null;\n }\n\n // Deduplicate by id\n if (seenIds.has(id)) {\n logInfo(`Skipping duplicate auth method id: ${id}`);\n return null;\n }\n\n // Extract and validate type\n const type = obj.type;\n if (typeof type !== 'string' || !VALID_AUTH_METHOD_TYPES.includes(type)) {\n logError(`Invalid auth method type for id ${id}: ${type}`);\n return null;\n }\n\n // Extract providerId from method object (if present)\n const rawProviderId = obj.providerId;\n let providerId: AuthProviderId | undefined;\n\n // Validate providerId if present in the method object\n if (rawProviderId !== undefined) {\n if (isValidProviderId(rawProviderId)) {\n providerId = rawProviderId;\n } else {\n logError(`Invalid providerId in auth method ${id}: ${rawProviderId}`);\n // Don't reject the method, try to resolve from id mapping\n }\n }\n\n // Resolve providerId from explicit id mapping\n const mappedProviderId = AUTH_METHOD_ID_TO_PROVIDER[id];\n\n // Check for conflicts between mapped and explicit providerId\n if (providerId && mappedProviderId && providerId !== mappedProviderId) {\n logError(`Conflict: auth method ${id} has providerId ${providerId} but maps to ${mappedProviderId}, rejecting`);\n return null;\n }\n\n // Use mapped providerId if explicit one not available\n const resolvedProviderId = providerId ?? mappedProviderId;\n\n // Build parsed method based on type\n if (type === 'oauth2') {\n // OAuth methods require a valid providerId\n if (!resolvedProviderId) {\n logError(`OAuth auth method ${id} has no valid providerId, skipping`);\n return null;\n }\n return {\n kind: 'oauth2',\n id,\n providerId: resolvedProviderId,\n };\n }\n\n if (type === 'agent') {\n // Agent auth: agent handles OAuth internally (ACP-compliant)\n // AUTH_REQUIREMENTS.md: When type is not specified, \"agent\" is assumed as default\n return {\n kind: 'agent',\n id,\n providerId: resolvedProviderId, // Optional for agent auth\n };\n }\n\n if (type === 'terminal') {\n // Terminal auth: interactive TUI setup\n // Extract args and env from the method object\n const args = Array.isArray(obj.args) ? obj.args.filter((a): a is string => typeof a === 'string') : undefined;\n const env = obj.env && typeof obj.env === 'object' && !Array.isArray(obj.env)\n ? Object.fromEntries(\n Object.entries(obj.env as Record<string, unknown>)\n .filter(([, v]) => typeof v === 'string')\n ) as Record<string, string>\n : undefined;\n\n return {\n kind: 'terminal',\n id,\n args,\n env,\n };\n }\n\n if (type === 'api-key') {\n return {\n kind: 'api-key',\n id,\n providerId: resolvedProviderId, // Optional for api-key\n };\n }\n\n // Should not reach here due to type validation above\n return null;\n}\n\n/**\n * Filter parsed auth methods to get only OAuth methods.\n *\n * Requirement 3.1: Identify methods with type \"oauth2\" or \"agent\"\n *\n * @param methods - Parsed auth methods\n * @returns Only OAuth methods (kind: 'oauth2')\n */\nexport function getOAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'oauth2' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'oauth2' } => m.kind === 'oauth2');\n}\n\n/**\n * Filter parsed auth methods to get only Agent Auth methods.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth is the default authentication method\n * where the agent manages the entire OAuth flow independently.\n *\n * @param methods - Parsed auth methods\n * @returns Only Agent Auth methods (kind: 'agent')\n */\nexport function getAgentAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'agent' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'agent' } => m.kind === 'agent');\n}\n\n/**\n * Filter parsed auth methods to get only Terminal Auth methods.\n *\n * AUTH_REQUIREMENTS.md: Terminal Auth enables agents to run an interactive\n * setup experience within a terminal environment.\n *\n * @param methods - Parsed auth methods\n * @returns Only Terminal Auth methods (kind: 'terminal')\n */\nexport function getTerminalAuthMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'terminal' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'terminal' } => m.kind === 'terminal');\n}\n\n/**\n * Filter parsed auth methods to get only API key methods.\n *\n * @param methods - Parsed auth methods\n * @returns Only API key methods (kind: 'api-key')\n */\nexport function getApiKeyMethods(methods: ParsedAuthMethod[]): Array<ParsedAuthMethod & { kind: 'api-key' }> {\n return methods.filter((m): m is ParsedAuthMethod & { kind: 'api-key' } => m.kind === 'api-key');\n}\n\n/**\n * JSON-RPC error response structure.\n */\nexport interface ErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\n/**\n * Pending request tracking structure.\n */\ninterface PendingRequest {\n id: string | number;\n agentId: string;\n timestamp: number;\n /** The JSON-RPC method name (for response correlation) */\n method?: string;\n /** Client session ID for session mapping */\n clientSessionId?: string;\n}\n\n// =============================================================================\n// Auth State Machine Types (Task 21.3)\n// =============================================================================\n\n/**\n * Authentication state for an agent.\n *\n * State transitions:\n * - none \u2192 pending: OAuth flow initiated\n * - pending \u2192 authenticated: OAuth flow succeeded\n * - pending \u2192 failed: OAuth flow failed or timed out\n * - failed \u2192 pending: Retry OAuth flow\n * - authenticated \u2192 none: Logout or token invalidation\n *\n * Requirement 3.1: Track auth state during OAuth 2.1 Authorization Code flow\n * Requirement 3.5: Handle timeout transitions to failed state\n */\nexport type AuthState = 'none' | 'pending' | 'authenticated' | 'failed';\n\n/**\n * Queued request structure for requests waiting on OAuth authentication.\n *\n * When an OAuth flow is pending for an agent, incoming requests are queued\n * and resumed after successful authentication.\n *\n * Requirement 3.1: Queue requests while OAuth flow is in progress\n */\nexport interface QueuedRequest {\n /** The original message to be routed */\n message: object;\n /** Timestamp when the request was queued */\n queuedAt: number;\n /** Resolve function to signal completion */\n resolve: (result: ErrorResponse | undefined) => void;\n}\n\n/**\n * Pending authenticate request tracking structure.\n *\n * Tracks authenticate JSON-RPC requests sent to agents for Agent Auth flow.\n * Used to correlate authenticate responses with the original auth flow.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n */\nexport interface PendingAuthenticateRequest {\n /** The authenticate request ID */\n requestId: string;\n /** The agent ID */\n agentId: string;\n /** The auth method ID from authMethods */\n authMethodId: string;\n /** Timestamp when the request was sent */\n sentAt: number;\n /** Resolve function to signal completion */\n resolve: (success: boolean, error?: string) => void;\n}\n\n/**\n * Default timeout for Agent Auth authenticate requests in milliseconds (5 minutes).\n * Matches the OAuth flow timeout from AUTH_REQUIREMENTS.md.\n */\nconst AGENT_AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Default timeout for Terminal Auth setup process in milliseconds (10 minutes).\n * Terminal Auth may require user interaction, so we allow more time.\n */\nconst TERMINAL_AUTH_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * Default timeout for queued requests in milliseconds (5 minutes).\n * Matches the OAuth flow timeout from Requirement 3.5.\n */\nconst QUEUED_REQUEST_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Callback type for writing responses to stdout.\n */\nexport type WriteCallback = (message: object) => boolean;\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [router] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [router] ${message}`);\n}\n\n\n/**\n * Create a JSON-RPC error response.\n *\n * @param id - Request ID (null for notifications or unknown)\n * @param code - Error code\n * @param message - Error message\n * @param data - Optional additional error data\n * @returns Error response object\n */\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): ErrorResponse {\n const response: ErrorResponse = {\n jsonrpc: '2.0',\n id,\n error: { code, message },\n };\n\n if (data !== undefined) {\n response.error.data = data;\n }\n\n return response;\n}\n\n/**\n * Extract the agentId field from a message.\n *\n * @param message - The message object to extract from\n * @returns The agentId string or undefined if not present\n */\nexport function extractAgentId(message: object): string | undefined {\n const msg = message as Record<string, unknown>;\n const agentId = msg.agentId;\n\n if (typeof agentId === 'string' && agentId.length > 0) {\n return agentId;\n }\n\n return undefined;\n}\n\n/**\n * Extract the JSON-RPC id field from a message.\n *\n * @param message - The message object to extract from\n * @returns The id (string, number, or null)\n */\nexport function extractId(message: object): string | number | null {\n const msg = message as Record<string, unknown>;\n const id = msg.id;\n\n if (typeof id === 'string' || typeof id === 'number') {\n return id;\n }\n\n return null;\n}\n\n/**\n * Transform a message for forwarding to an agent.\n *\n * Removes the agentId field while preserving all other fields.\n *\n * @param message - The original message\n * @returns A new message object without the agentId field\n */\nexport function transformMessage(message: object): object {\n const { agentId: _, ...rest } = message as Record<string, unknown>;\n return rest;\n}\n\n\n/**\n * Spawn function type for dependency injection in tests.\n */\nexport type SpawnFn = typeof spawn;\n\n/**\n * Optional dependencies for MessageRouter (for testing).\n */\nexport interface MessageRouterDeps {\n /** Custom spawn function (default: child_process.spawn) */\n spawnFn?: SpawnFn;\n /** Custom function to check if stdin is TTY (default: process.stdin.isTTY) */\n isStdinTTY?: () => boolean;\n /** Custom function to check if stdout is TTY (default: process.stdout.isTTY) */\n isStdoutTTY?: () => boolean;\n}\n\n/**\n * Message Router implementation.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles message transformation, error generation, and request correlation.\n * Integrates with AuthManager for OAuth authentication (Requirements 11.2, 11.4).\n * Implements auth state machine for pending OAuth flows (Task 21.3).\n */\nexport class MessageRouter {\n /** Registry index for agent lookup and resolution */\n private readonly registry: IRegistryIndex;\n\n /** Runtime manager for agent process lifecycle */\n private readonly runtimeManager: AgentRuntimeManager;\n\n /** Callback for writing responses to stdout */\n private readonly writeCallback: WriteCallback;\n\n /** API keys for agent authentication */\n private readonly apiKeys: Record<string, any>;\n\n /** Spawn function for Terminal Auth (injectable for testing) */\n private readonly spawnFn: SpawnFn;\n\n /** Function to check if stdin is TTY (injectable for testing) */\n private readonly isStdinTTY: () => boolean;\n\n /** Function to check if stdout is TTY (injectable for testing) */\n private readonly isStdoutTTY: () => boolean;\n /** Optional AuthManager for OAuth authentication (Requirements 11.2, 11.4) */\n private readonly authManager?: AuthManager;\n\n /** Map of request ID to pending request info for correlation */\n private readonly pendingRequests: Map<string | number, PendingRequest> = new Map();\n\n /**\n * Map of agent ID to authentication state.\n *\n * State machine (Task 21.3):\n * - none: No authentication in progress\n * - pending: OAuth flow in progress, requests are queued\n * - authenticated: OAuth flow completed successfully\n * - failed: OAuth flow failed or timed out\n *\n * Requirement 3.1: Track auth state during OAuth 2.1 Authorization Code flow\n */\n private readonly authState: Map<string, AuthState> = new Map();\n\n /**\n * Map of agent ID to required OAuth provider ID.\n *\n * Tracks which agents require OAuth authentication and with which provider.\n * This is populated when we receive an initialize response with authMethods\n * containing OAuth methods.\n *\n * Requirement 11.2: Track auth requirements to block requests when OAuth\n * is required but credentials are not available.\n */\n private readonly agentOAuthRequirements: Map<string, AuthProviderId> = new Map();\n\n /**\n * Map of agent ID to queued requests waiting for OAuth authentication.\n *\n * When an OAuth flow is pending for an agent, incoming requests are queued\n * here and processed after successful authentication.\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n */\n private readonly requestQueue: Map<string, QueuedRequest[]> = new Map();\n\n /**\n * Map of authenticate request ID to pending authenticate request info.\n *\n * Tracks authenticate JSON-RPC requests sent to agents for Agent Auth flow.\n * Used to correlate authenticate responses with the original auth flow.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n */\n private readonly pendingAuthenticateRequests: Map<string, PendingAuthenticateRequest> = new Map();\n\n /** Map of agent sessionId to client sessionId for notification routing */\n private readonly sessionIdMap: Map<string, string> = new Map();\n\n /**\n * Whether to automatically trigger OAuth browser flow when agent requires it.\n * When false, returns AUTH_REQUIRED error instead of opening browser.\n * Controlled by AUTH_AUTO_OAUTH environment variable (default: false for safety).\n */\n private readonly autoOAuth: boolean;\n\n /**\n * Create a new MessageRouter.\n *\n * @param registry - Registry index for agent lookup\n * @param runtimeManager - Runtime manager for agent processes\n * @param writeCallback - Callback for writing responses to stdout\n * @param apiKeys - API keys for agent authentication (optional)\n * @param authManager - AuthManager for OAuth authentication (optional, Requirements 11.2, 11.4)\n * @param autoOAuth - Whether to auto-trigger OAuth browser flow (default: from AUTH_AUTO_OAUTH env, or false)\n * @param deps - Optional dependencies for testing (spawnFn, TTY checks)\n */\n constructor(\n registry: IRegistryIndex,\n runtimeManager: AgentRuntimeManager,\n writeCallback: WriteCallback,\n apiKeys: Record<string, any> = {},\n authManager?: AuthManager,\n autoOAuth?: boolean,\n deps?: MessageRouterDeps,\n ) {\n this.registry = registry;\n this.runtimeManager = runtimeManager;\n this.writeCallback = writeCallback;\n this.apiKeys = apiKeys;\n this.authManager = authManager;\n // Default to false for safety - existing deployments won't suddenly open browsers\n // Can be enabled via AUTH_AUTO_OAUTH=true environment variable\n this.autoOAuth = autoOAuth ?? this.getAutoOAuthFromEnv();\n // Injectable dependencies for testing\n this.spawnFn = deps?.spawnFn ?? spawn;\n this.isStdinTTY = deps?.isStdinTTY ?? (() => process.stdin.isTTY ?? false);\n this.isStdoutTTY = deps?.isStdoutTTY ?? (() => process.stdout.isTTY ?? false);\n }\n\n /**\n * Get auto-OAuth setting from environment variable.\n * AUTH_AUTO_OAUTH=true enables auto-OAuth, any other value or unset disables it.\n */\n private getAutoOAuthFromEnv(): boolean {\n const envValue = process.env.AUTH_AUTO_OAUTH;\n return envValue === 'true' || envValue === '1' || envValue === 'yes';\n }\n\n /**\n * Get supported authentication methods for ACP initialize response.\n *\n * Requirement 11.1: WHEN responding to an initialize request, THE Registry_Launcher\n * SHALL include an `authMethods` array listing supported authentication methods.\n *\n * @returns Array of supported authentication methods\n */\n getSupportedAuthMethods(): AcpAuthMethod[] {\n const methods: AcpAuthMethod[] = [\n // Legacy API key authentication\n // Note: OpenAI and Anthropic API key support will be handled by model-credentials module\n { id: 'api-key', type: 'api-key' },\n ];\n\n // Add OAuth and ACP-compliant auth methods if AuthManager is available\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n if (this.authManager) {\n // ACP Registry requires type:\"agent\" or type:\"terminal\" for auth-check validation\n // (verify_agents.py --auth-check \u2192 client.py \u2192 validate_auth_methods)\n //\n // AUTHENTICATION.md: Agent Auth \u2014 agent handles OAuth flow independently\n // Our Registry Launcher supports --login <provider> which triggers browser OAuth\n methods.push(\n { id: 'agent-oauth', type: 'agent' },\n );\n\n // Terminal Auth \u2014 interactive setup via --setup flag\n methods.push(\n { id: 'terminal-setup', type: 'terminal', args: ['--setup'] },\n );\n\n // OAuth2 methods for browser-based authentication\n methods.push(\n { id: 'oauth2-github', type: 'oauth2', providerId: 'github' },\n { id: 'oauth2-google', type: 'oauth2', providerId: 'google' },\n { id: 'oauth2-cognito', type: 'oauth2', providerId: 'cognito' },\n { id: 'oauth2-azure', type: 'oauth2', providerId: 'azure' },\n { id: 'oauth2-oidc', type: 'oauth2', providerId: 'oidc' },\n );\n }\n\n return methods;\n }\n\n /**\n * Check if authentication is available for an agent.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response.\n *\n * @param agentId - The agent identifier\n * @returns True if authentication is available (OAuth or legacy API key)\n */\n async hasAuthenticationForAgent(agentId: string): Promise<boolean> {\n // Check OAuth credentials first (Requirement 10.3)\n if (this.authManager) {\n const token = await this.authManager.getTokenForAgent(agentId);\n if (token) {\n return true;\n }\n }\n\n // Fall back to legacy API key\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n return apiKey !== undefined;\n }\n\n /**\n * Check if api-key credentials are available for an agent.\n * This is a synchronous check for api-keys.json credentials.\n *\n * @param agentId - The agent identifier\n * @returns True if api-key credentials are available\n */\n hasCredentialsForAgent(agentId: string): boolean {\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n return apiKey !== undefined;\n }\n\n /**\n * Create an AUTH_REQUIRED error response.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response with the required\n * authentication method specified.\n *\n * @param id - The request ID\n * @param agentId - The agent identifier\n * @param requiredMethod - The required authentication method\n * @returns AUTH_REQUIRED error response\n */\n createAuthRequiredError(\n id: string | number | null,\n agentId: string,\n requiredMethod?: string\n ): ErrorResponse {\n // Build remediation instructions for the user\n // Use npx command (works without global install) and stdiobus (after global install)\n const remediation: Record<string, unknown> = {\n type: 'login_required',\n commands: [\n 'npx @stdiobus/workers-registry acp-registry --setup',\n 'stdiobus acp-registry --setup',\n ],\n hint: 'Run: npx @stdiobus/workers-registry acp-registry --setup',\n docsUrl: 'https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md'\n };\n\n return createErrorResponse(\n id,\n RoutingErrorCodes.AUTH_REQUIRED,\n 'Authentication required',\n {\n agentId,\n requiredMethod: requiredMethod ?? 'api-key',\n supportedMethods: this.getSupportedAuthMethods().map(m => m.id),\n remediation,\n }\n );\n }\n\n /**\n * Inject authentication into a request using AuthManager.\n *\n * Requirement 11.4: WHEN authentication is successful, THE Auth_Module SHALL inject\n * the access token into agent requests according to the provider's token injection method.\n *\n * @param agentId - The agent identifier\n * @param message - The message to inject auth into\n * @returns The message with authentication injected\n */\n async injectAuthentication(agentId: string, message: object): Promise<object> {\n if (this.authManager) {\n return this.authManager.injectAuth(agentId, message);\n }\n return message;\n }\n\n /**\n * Inject mcpServers from registry into session/new request params.\n *\n * If the agent has mcpServers configured in the registry, they are merged\n * with any mcpServers already present in the request params.\n * Registry servers are added first, then request servers (request takes precedence for duplicates).\n *\n * @param message - The transformed message (without agentId)\n * @param agentId - The agent ID to look up in registry\n * @returns Message with mcpServers injected into params\n */\n private injectMcpServers(message: object, agentId: string): object {\n const agent = this.registry.lookup(agentId);\n if (!agent?.mcpServers || agent.mcpServers.length === 0) {\n return message;\n }\n\n const msg = message as Record<string, unknown>;\n const params = (msg.params as Record<string, unknown>) || {};\n const existingServers = Array.isArray(params.mcpServers) ? params.mcpServers : [];\n\n // Convert registry McpServerConfig to ACP McpServer format\n const registryServers = agent.mcpServers.map((server) => ({\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env ? Object.entries(server.env).map(([name, value]) => ({ name, value })) : undefined,\n }));\n\n // Merge: registry servers first, then existing (existing can override by name)\n const existingNames = new Set(\n existingServers\n .filter((s): s is Record<string, unknown> => s !== null && typeof s === 'object')\n .map((s) => s.name)\n .filter((n): n is string => typeof n === 'string'),\n );\n\n const mergedServers = [\n ...registryServers.filter((s) => !existingNames.has(s.name)),\n ...existingServers,\n ];\n\n logInfo(`Injecting ${registryServers.length} MCP servers from registry for agent ${agentId}`);\n\n return {\n ...msg,\n params: {\n ...params,\n mcpServers: mergedServers,\n },\n };\n }\n\n /**\n * Route an incoming message to the appropriate agent.\n *\n * Extracts agentId, resolves spawn command, and forwards message.\n * If OAuth authentication is pending for the agent, queues the request\n * and resumes it after successful authentication (Task 21.3).\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n * Requirement 11.2: Block requests when OAuth required but not authenticated\n *\n * @param message - The incoming JSON-RPC message\n * @returns Error response if routing fails, undefined on success\n */\n async route(message: object): Promise<ErrorResponse | undefined> {\n const id = extractId(message);\n const agentId = extractAgentId(message);\n\n // Return error if agentId is missing\n if (agentId === undefined) {\n logError('Missing agentId in request');\n return createErrorResponse(id, RoutingErrorCodes.MISSING_AGENT_ID, 'Missing agentId');\n }\n\n // Check auth state for this agent (Task 21.3)\n const currentAuthState = this.getAuthState(agentId);\n\n // If OAuth flow is pending, queue the request (Requirement 3.1)\n if (currentAuthState === 'pending') {\n logInfo(`OAuth flow pending for agent ${agentId}, queueing request (id=${id})`);\n return this.queueRequest(agentId, message);\n }\n\n // If auth previously failed, return AUTH_REQUIRED error\n // This allows the client to retry or handle the failure\n if (currentAuthState === 'failed') {\n logError(`Authentication failed for agent ${agentId}, returning AUTH_REQUIRED`);\n const requiredProviderId = this.agentOAuthRequirements.get(agentId);\n return this.createAuthRequiredErrorWithProvider(id, agentId, requiredProviderId);\n }\n\n // Task 23.1: Block requests when OAuth required but not authenticated\n // Requirement 11.2: WHEN an agent requires authentication and credentials\n // are not available, THE Registry_Launcher SHALL return an AUTH_REQUIRED error\n const requiredProviderId = this.agentOAuthRequirements.get(agentId);\n if (requiredProviderId && currentAuthState !== 'authenticated') {\n // Agent requires OAuth - check if credentials are available\n const hasCredentials = await this.hasOAuthCredentialsForAgent(agentId, requiredProviderId);\n if (!hasCredentials) {\n logError(`Agent ${agentId} requires OAuth (provider: ${requiredProviderId}) but credentials not available`);\n return this.createAuthRequiredErrorWithProvider(id, agentId, requiredProviderId);\n }\n }\n\n // Proceed with normal routing\n return this.routeInternal(message, agentId, id);\n }\n\n /**\n * Check if OAuth credentials are available for an agent.\n *\n * Requirement 11.2: Check if credentials are available before routing.\n *\n * @param agentId - The agent identifier\n * @param providerId - The OAuth provider ID\n * @returns True if OAuth credentials are available\n */\n private async hasOAuthCredentialsForAgent(agentId: string, providerId: AuthProviderId): Promise<boolean> {\n if (!this.authManager) {\n return false;\n }\n\n // Check if we have a valid token for this agent/provider\n const token = await this.authManager.getTokenForAgent(agentId, providerId);\n return token !== null && token !== undefined;\n }\n\n /**\n * Create an AUTH_REQUIRED error response with provider information.\n *\n * Requirement 11.2: WHEN an agent requires authentication and credentials are not available,\n * THE Registry_Launcher SHALL return an AUTH_REQUIRED error response with the required\n * authentication method specified.\n *\n * @param id - The request ID\n * @param agentId - The agent identifier\n * @param providerId - The required OAuth provider ID (optional)\n * @returns AUTH_REQUIRED error response with requiredMethod, supportedMethods, providerId\n */\n private createAuthRequiredErrorWithProvider(\n id: string | number | null,\n agentId: string,\n providerId?: AuthProviderId\n ): ErrorResponse {\n const supportedMethods = this.getSupportedAuthMethods();\n\n // Build remediation instructions for the user\n // Use npx command (works without global install) and stdiobus (after global install)\n const remediation: Record<string, unknown> = {\n type: 'login_required',\n provider: providerId || 'unknown',\n commands: providerId\n ? [\n `npx @stdiobus/workers-registry acp-registry --login ${providerId}`,\n `stdiobus acp-registry --login ${providerId}`,\n ]\n : [\n 'npx @stdiobus/workers-registry acp-registry --setup',\n 'stdiobus acp-registry --setup',\n ],\n hint: providerId\n ? `Run: npx @stdiobus/workers-registry acp-registry --login ${providerId}`\n : 'Run: npx @stdiobus/workers-registry acp-registry --setup',\n docsUrl: 'https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md'\n };\n\n return createErrorResponse(\n id,\n RoutingErrorCodes.AUTH_REQUIRED,\n 'Authentication required',\n {\n agentId,\n requiredMethod: providerId ? `oauth2-${providerId}` : 'oauth2',\n supportedMethods: supportedMethods.map(m => m.id),\n providerId: providerId,\n remediation,\n }\n );\n }\n\n /**\n * Internal routing logic after auth state checks.\n *\n * @param message - The incoming JSON-RPC message\n * @param agentId - The agent identifier\n * @param id - The request ID\n * @returns Error response if routing fails, undefined on success\n */\n private async routeInternal(\n message: object,\n agentId: string,\n id: string | number | null,\n ): Promise<ErrorResponse | undefined> {\n // Resolve agent to spawn command\n let spawnCommand;\n try {\n spawnCommand = this.registry.resolve(agentId);\n } catch (error) {\n if (error instanceof AgentNotFoundError) {\n logError(`Agent not found: ${agentId}`);\n return createErrorResponse(id, RoutingErrorCodes.AGENT_NOT_FOUND, 'Agent not found', {\n agentId,\n });\n }\n if (error instanceof PlatformNotSupportedError) {\n logError(`Platform not supported for agent: ${agentId}`);\n return createErrorResponse(\n id,\n RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,\n 'Platform not supported',\n { agentId, platform: (error as PlatformNotSupportedError).platform },\n );\n }\n throw error;\n }\n\n // Merge env from api-keys.json into spawn command\n // This ensures credentials are passed to the agent process\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: {\n ...spawnCommand.env,\n ...agentEnv,\n },\n };\n logInfo(`Injected ${Object.keys(agentEnv).length} env vars from api-keys.json for agent ${agentId}`);\n }\n\n // Get or spawn agent runtime\n let runtime: AgentRuntime;\n try {\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to spawn agent ${agentId}: ${(error as Error).message}`);\n return createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Agent spawn failed', {\n agentId,\n error: (error as Error).message,\n });\n }\n\n // Track pending request for correlation\n if (id !== null) {\n const msg = message as Record<string, unknown>;\n const clientSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : undefined;\n const method = typeof msg.method === 'string' ? msg.method : undefined;\n\n this.pendingRequests.set(id, {\n id,\n agentId,\n timestamp: Date.now(),\n method,\n clientSessionId,\n });\n }\n\n // Transform message (remove agentId) and forward to agent\n let transformedMessage = transformMessage(message);\n\n // For session/new requests, inject mcpServers from registry if agent has them configured\n const msg = message as Record<string, unknown>;\n if (msg.method === 'session/new') {\n transformedMessage = this.injectMcpServers(transformedMessage, agentId);\n }\n\n // Inject authentication into the message (Requirement 11.4)\n // This adds OAuth tokens or legacy API keys to the request\n transformedMessage = await this.injectAuthentication(agentId, transformedMessage);\n\n const success = runtime.write(transformedMessage);\n\n if (!success) {\n logError(`Failed to write to agent ${agentId}`);\n // Remove from pending if write failed\n if (id !== null) {\n this.pendingRequests.delete(id);\n }\n } else {\n logInfo(`Routed message to agent ${agentId}`);\n }\n\n return undefined;\n }\n\n // =============================================================================\n // Auth State Machine Methods (Task 21.3)\n // =============================================================================\n\n /**\n * Get the current authentication state for an agent.\n *\n * @param agentId - The agent identifier\n * @returns The current auth state (defaults to 'none')\n */\n getAuthState(agentId: string): AuthState {\n return this.authState.get(agentId) ?? 'none';\n }\n\n /**\n * Set the authentication state for an agent.\n *\n * Handles state transitions and triggers appropriate actions:\n * - none \u2192 pending: OAuth flow started\n * - pending \u2192 authenticated: Resume queued requests\n * - pending \u2192 failed: Reject queued requests with AUTH_REQUIRED\n *\n * Requirement 3.1: Track auth state during OAuth flow\n * Requirement 3.5: Handle timeout transitions to failed state\n *\n * @param agentId - The agent identifier\n * @param newState - The new auth state\n */\n setAuthState(agentId: string, newState: AuthState): void {\n const oldState = this.getAuthState(agentId);\n\n if (oldState === newState) {\n return; // No state change\n }\n\n logInfo(`Auth state transition for ${agentId}: ${oldState} \u2192 ${newState}`);\n this.authState.set(agentId, newState);\n\n // Handle state transition side effects\n if (newState === 'authenticated' && oldState === 'pending') {\n // Resume queued requests after successful authentication\n void this.processQueuedRequests(agentId);\n } else if (newState === 'failed' && oldState === 'pending') {\n // Reject queued requests with AUTH_REQUIRED error\n void this.rejectQueuedRequests(agentId);\n }\n }\n\n /**\n * Queue a request while OAuth authentication is pending.\n *\n * Returns a promise that resolves when the request is processed\n * (either routed successfully or rejected with an error).\n *\n * Requirement 3.1: Queue incoming requests while OAuth flow is pending\n *\n * @param agentId - The agent identifier\n * @param message - The message to queue\n * @returns Promise that resolves with the routing result\n */\n private queueRequest(agentId: string, message: object): Promise<ErrorResponse | undefined> {\n return new Promise((resolve) => {\n const queuedRequest: QueuedRequest = {\n message,\n queuedAt: Date.now(),\n resolve,\n };\n\n // Get or create queue for this agent\n let queue = this.requestQueue.get(agentId);\n if (!queue) {\n queue = [];\n this.requestQueue.set(agentId, queue);\n }\n\n queue.push(queuedRequest);\n logInfo(`Queued request for agent ${agentId}, queue size: ${queue.length}`);\n\n // Set up timeout for this request (Requirement 3.5)\n setTimeout(() => {\n this.handleQueuedRequestTimeout(agentId, queuedRequest);\n }, QUEUED_REQUEST_TIMEOUT_MS);\n });\n }\n\n /**\n * Handle timeout for a queued request.\n *\n * If the request is still in the queue when timeout fires,\n * remove it and resolve with a timeout error.\n *\n * Requirement 3.5: Handle timeout for queued requests\n *\n * @param agentId - The agent identifier\n * @param queuedRequest - The queued request that timed out\n */\n private handleQueuedRequestTimeout(agentId: string, queuedRequest: QueuedRequest): void {\n const queue = this.requestQueue.get(agentId);\n if (!queue) {\n return; // Queue already cleared\n }\n\n const index = queue.indexOf(queuedRequest);\n if (index === -1) {\n return; // Request already processed\n }\n\n // Remove from queue\n queue.splice(index, 1);\n logError(`Queued request timed out for agent ${agentId}`);\n\n // Resolve with timeout error\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.AUTH_REQUIRED, 'Authentication timeout', {\n agentId,\n reason: 'OAuth flow timed out while request was queued',\n })\n );\n }\n\n /**\n * Process queued requests after successful OAuth authentication.\n *\n * Routes all queued requests for the agent now that authentication\n * is complete.\n *\n * Requirement 3.1: Resume queued requests after successful authentication\n *\n * @param agentId - The agent identifier\n */\n private async processQueuedRequests(agentId: string): Promise<void> {\n const queue = this.requestQueue.get(agentId);\n if (!queue || queue.length === 0) {\n return;\n }\n\n logInfo(`Processing ${queue.length} queued requests for agent ${agentId}`);\n\n // Clear the queue before processing to prevent re-queueing\n this.requestQueue.delete(agentId);\n\n // Process each queued request\n for (const queuedRequest of queue) {\n try {\n const id = extractId(queuedRequest.message);\n const result = await this.routeInternal(queuedRequest.message, agentId, id);\n queuedRequest.resolve(result);\n } catch (error) {\n const id = extractId(queuedRequest.message);\n logError(`Error processing queued request for ${agentId}: ${(error as Error).message}`);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Failed to process queued request', {\n agentId,\n error: (error as Error).message,\n })\n );\n }\n }\n\n logInfo(`Completed processing queued requests for agent ${agentId}`);\n }\n\n /**\n * Reject all queued requests after OAuth authentication failure.\n *\n * Returns AUTH_REQUIRED error for all queued requests.\n *\n * Requirement 3.5: Handle failed authentication for queued requests\n *\n * @param agentId - The agent identifier\n */\n private async rejectQueuedRequests(agentId: string): Promise<void> {\n const queue = this.requestQueue.get(agentId);\n if (!queue || queue.length === 0) {\n return;\n }\n\n logInfo(`Rejecting ${queue.length} queued requests for agent ${agentId} due to auth failure`);\n\n // Clear the queue\n this.requestQueue.delete(agentId);\n\n // Reject each queued request with AUTH_REQUIRED error\n for (const queuedRequest of queue) {\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n this.createAuthRequiredError(id, agentId, 'oauth2')\n );\n }\n }\n\n /**\n * Get the number of queued requests for an agent.\n *\n * @param agentId - The agent identifier\n * @returns The number of queued requests\n */\n getQueuedRequestCount(agentId: string): number {\n const queue = this.requestQueue.get(agentId);\n return queue?.length ?? 0;\n }\n\n /**\n * Get the total number of queued requests across all agents.\n *\n * @returns The total number of queued requests\n */\n getTotalQueuedRequestCount(): number {\n let total = 0;\n for (const queue of this.requestQueue.values()) {\n total += queue.length;\n }\n return total;\n }\n\n /**\n * Handle a response from an agent process.\n *\n * Intercepts initialize responses to trigger automatic authentication and\n * inject authMethods (Requirement 11.1).\n * Handles agent-to-client requests (like session/request_permission) by\n * auto-responding when they cannot be forwarded to the client.\n * Tracks sessionId mapping for proper notification routing.\n * Handles authenticate responses for Agent Auth flow (Task 35.2).\n * Forwards all responses to stdout.\n *\n * @param agentId - The agent that sent the response\n * @param response - The response object from the agent\n */\n handleAgentResponse(agentId: string, response: object): void {\n const id = extractId(response);\n let msg = response as Record<string, unknown>;\n const method = typeof msg.method === 'string' ? msg.method : undefined;\n\n // Task 35.2: Handle authenticate response for Agent Auth flow\n // Check if this is a response to a pending authenticate request\n if (id !== null && typeof id === 'string') {\n const pendingAuth = this.pendingAuthenticateRequests.get(id);\n if (pendingAuth && pendingAuth.agentId === agentId) {\n this.handleAuthenticateResponse(pendingAuth, msg);\n return; // Don't forward authenticate responses to client\n }\n }\n\n // Handle agent-to-client requests (messages with both id and method).\n // These are requests FROM the agent TO the client (e.g., session/request_permission).\n // Since we are a headless launcher we cannot forward them to a human,\n // so we auto-respond to keep the agent unblocked.\n if (id !== null && method) {\n this.handleAgentRequest(agentId, id, method, msg);\n return;\n }\n\n // Check if this is a response to a tracked request (has id, no method)\n if (id !== null) {\n const pending = this.pendingRequests.get(id);\n if (pending && pending.agentId === agentId) {\n const result = msg.result as Record<string, unknown> | undefined;\n\n // Check if this is an initialize response - inject our authMethods (Requirement 11.1)\n // Use tracked method from pending request for reliable detection\n const isInitializeResponse = pending.method === 'initialize' && result !== undefined;\n if (isInitializeResponse) {\n const ourAuthMethods = this.getSupportedAuthMethods();\n const existingAuthMethods = Array.isArray(result.authMethods) ? result.authMethods : [];\n\n // Merge our auth methods with agent's auth methods (ours first)\n const mergedAuthMethods = [\n ...ourAuthMethods,\n ...existingAuthMethods.filter((m: any) =>\n !ourAuthMethods.some(our => our.id === m.id)\n ),\n ];\n\n // Create enriched response with authMethods\n msg = {\n ...msg,\n result: {\n ...result,\n authMethods: mergedAuthMethods,\n },\n };\n\n logInfo(`Injected ${ourAuthMethods.length} auth methods into initialize response for ${agentId}`);\n }\n\n // Check if this is an initialize response with authMethods (from agent)\n // Only trigger auto-auth on initialize responses to reduce attack surface\n if (isInitializeResponse && result && Array.isArray(result.authMethods) && result.authMethods.length > 0) {\n // Parse and validate auth methods using explicit mapping (Task 21.1)\n const parsedMethods = parseAuthMethods(result.authMethods);\n if (parsedMethods.length > 0) {\n // Task 23.1: Track OAuth requirements for this agent\n // Requirement 11.2: Cache auth requirements per agent\n const oauthMethods = getOAuthMethods(parsedMethods);\n const apiKeyMethods = getApiKeyMethods(parsedMethods);\n\n // Check if agent has api-key credentials available\n // If api-key is supported AND credentials exist, don't require OAuth\n const hasApiKeyCredentials = this.hasCredentialsForAgent(agentId);\n\n if (oauthMethods.length > 0 && !(apiKeyMethods.length > 0 && hasApiKeyCredentials)) {\n // Store the first OAuth provider as the required provider\n // Only if api-key fallback is not available\n const requiredProviderId = oauthMethods[0].providerId;\n this.agentOAuthRequirements.set(agentId, requiredProviderId);\n logInfo(`Agent ${agentId} requires OAuth authentication with provider: ${requiredProviderId}`);\n } else if (apiKeyMethods.length > 0 && hasApiKeyCredentials) {\n logInfo(`Agent ${agentId} supports OAuth but api-key credentials available, using api-key`);\n }\n\n // Task 33.2: Only auto-trigger OAuth if AUTH_AUTO_OAUTH is enabled\n // Default is false for backward compatibility - existing deployments won't suddenly open browsers\n if (this.autoOAuth) {\n logInfo(`Agent ${agentId} requires authentication, attempting auto-auth with ${parsedMethods.length} valid methods`);\n this.setAuthState(agentId, 'pending');\n void this.attemptAuthentication(agentId, parsedMethods);\n } else {\n logInfo(`Agent ${agentId} requires authentication but AUTH_AUTO_OAUTH is disabled. Use --login to authenticate.`);\n // Don't set auth state to pending - let requests fail with AUTH_REQUIRED\n // This allows users to explicitly authenticate via --login command\n }\n } else {\n logError(`Agent ${agentId} has authMethods but none are valid after parsing`);\n this.setAuthState(agentId, 'none');\n }\n }\n\n // Check if this is a session/new response - track sessionId mapping\n // session/new responses have sessionId in result, not in params\n if (result && typeof result.sessionId === 'string') {\n const agentSessionId = result.sessionId;\n const clientSessionId = pending.clientSessionId;\n if (clientSessionId) {\n this.sessionIdMap.set(agentSessionId, clientSessionId);\n logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`);\n }\n }\n\n this.pendingRequests.delete(id);\n }\n }\n\n // Handle notifications (no id) - map sessionId from agent to client\n if (id === null && method) {\n logInfo(`Received notification: ${method}`);\n const params = msg.params as Record<string, unknown> | undefined;\n if (params && typeof params.sessionId === 'string') {\n const agentSessionId = params.sessionId;\n const clientSessionId = this.sessionIdMap.get(agentSessionId);\n\n if (clientSessionId) {\n // Replace agent sessionId with client sessionId for stdio Bus routing\n const enriched = {\n ...msg,\n sessionId: clientSessionId,\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for agent context\n },\n };\n logInfo(`Forwarding notification with mapped sessionId: ${clientSessionId}`);\n this.writeCallback(enriched);\n return;\n } else {\n // CRITICAL FIX: If no mapping exists, use default sessionId for routing\n // Cannot forward with unmapped agentSessionId as stdio_bus won't recognize it\n logError(`Notification with unmapped agentSessionId: ${agentSessionId}, using default sessionId`);\n const enriched = {\n ...msg,\n sessionId: 'global-notifications',\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for context\n },\n };\n this.writeCallback(enriched);\n return;\n }\n } else {\n // CRITICAL FIX: Handle notifications without sessionId in params\n // Check if sessionId is at top level or if this is a global notification\n const topLevelSessionId = msg.sessionId as string | undefined;\n if (topLevelSessionId) {\n // sessionId already at top level, forward as-is\n this.writeCallback(response);\n return;\n } else {\n // Global notification without sessionId - add a default sessionId for routing\n logError(`Notification without sessionId: ${method}, adding default sessionId for routing`);\n const enriched = {\n ...msg,\n sessionId: 'global-notifications', // Default sessionId for stdio_bus routing\n };\n this.writeCallback(enriched);\n return;\n }\n }\n }\n\n // Forward response unchanged\n this.writeCallback(msg);\n }\n\n /**\n * Handle a request from an agent to the client.\n *\n * Agent-to-client requests (JSON-RPC messages with both `id` and `method`)\n * require a response. Since the Registry Launcher is headless and cannot\n * forward these to a human, we auto-respond to keep the agent unblocked.\n *\n * Known methods:\n * - session/request_permission: Auto-approve with the first \"allow\" option\n *\n * Unknown methods get a generic success response so the agent continues.\n *\n * @param agentId - The agent that sent the request\n * @param id - The JSON-RPC request id\n * @param method - The JSON-RPC method name\n * @param msg - The full message object\n */\n private handleAgentRequest(\n agentId: string,\n id: string | number,\n method: string,\n msg: Record<string, unknown>,\n ): void {\n logInfo(`Agent ${agentId} sent request: ${method} (id=${id}), auto-responding`);\n\n let result: Record<string, unknown>;\n\n if (method === 'session/request_permission') {\n result = this.buildPermissionResponse(msg);\n } else {\n // Fallback: generic success so the agent is never stuck waiting\n logInfo(`Unknown agent request method: ${method}, sending generic success`);\n result = {};\n }\n\n const response = {\n jsonrpc: '2.0' as const,\n id,\n result,\n };\n\n // Send directly to the agent, not to stdout\n this.sendToAgent(agentId, response);\n }\n\n /**\n * Build an auto-approve result for session/request_permission.\n *\n * Picks the first \"allow\" option from the request, preferring\n * allow_always > allow_once > first option as fallback.\n *\n * @param msg - The request_permission message\n * @returns The result object for the response\n */\n private buildPermissionResponse(msg: Record<string, unknown>): Record<string, unknown> {\n const params = msg.params as Record<string, unknown> | undefined;\n const options = params?.options as Array<Record<string, unknown>> | undefined;\n\n if (!options || options.length === 0) {\n return { optionId: 'approved' };\n }\n\n // Prefer allow_always, then allow_once, then first option\n const allowAlways = options.find(o => o.kind === 'allow_always');\n if (allowAlways && typeof allowAlways.optionId === 'string') {\n logInfo(`Auto-approving permission with option: ${allowAlways.optionId} (allow_always)`);\n return { optionId: allowAlways.optionId };\n }\n\n const allowOnce = options.find(o => o.kind === 'allow_once');\n if (allowOnce && typeof allowOnce.optionId === 'string') {\n logInfo(`Auto-approving permission with option: ${allowOnce.optionId} (allow_once)`);\n return { optionId: allowOnce.optionId };\n }\n\n // Fallback to first option\n const firstOption = options[0];\n const optionId = typeof firstOption.optionId === 'string' ? firstOption.optionId : 'approved';\n logInfo(`Auto-approving permission with fallback option: ${optionId}`);\n return { optionId };\n }\n\n /**\n * Handle an authenticate response from an agent.\n *\n * Task 35.2: Handle authenticate response\n * - On success: resolve the pending authenticate request with success\n * - On error: resolve with failure and log the error\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - after agent completes OAuth flow,\n * it responds to the authenticate request.\n *\n * @param pendingAuth - The pending authenticate request\n * @param response - The response from the agent\n */\n private handleAuthenticateResponse(\n pendingAuth: PendingAuthenticateRequest,\n response: Record<string, unknown>,\n ): void {\n const { agentId, authMethodId, requestId } = pendingAuth;\n\n // Check for error response\n if (response.error) {\n const error = response.error as Record<string, unknown>;\n const errorCode = error.code ?? 'UNKNOWN';\n const errorMessage = typeof error.message === 'string' ? error.message : 'Unknown error';\n logError(`Agent Auth failed for ${agentId}: [${errorCode}] ${errorMessage}`);\n pendingAuth.resolve(false, errorMessage);\n return;\n }\n\n // Check for success response\n if (response.result !== undefined) {\n logInfo(`Agent Auth succeeded for ${agentId} (method: ${authMethodId}, request: ${requestId})`);\n pendingAuth.resolve(true);\n return;\n }\n\n // Unexpected response format\n logError(`Unexpected authenticate response format for ${agentId}: ${JSON.stringify(response)}`);\n pendingAuth.resolve(false, 'Unexpected response format');\n }\n\n /**\n * Send a JSON-RPC message directly to an agent process.\n *\n * @param agentId - The agent to send to\n * @param message - The message to send\n */\n private sendToAgent(agentId: string, message: object): void {\n let runtime: AgentRuntime | undefined;\n try {\n runtime = this.runtimeManager.get(agentId);\n } catch {\n logError(`Failed to get runtime for agent ${agentId} to send response`);\n return;\n }\n\n if (!runtime) {\n logError(`No runtime found for agent ${agentId}, cannot send response`);\n return;\n }\n\n const success = runtime.write(message);\n if (!success) {\n logError(`Failed to write response to agent ${agentId}`);\n } else {\n logInfo(`Sent auto-response to agent ${agentId}`);\n }\n }\n\n /**\n * Attempt automatic authentication for an agent.\n *\n * Selects the best authentication method and initiates authentication.\n * Uses parsed auth methods with validated types and provider IDs.\n *\n * Authentication method precedence (AUTH_REQUIREMENTS.md):\n * 1. Agent Auth (type: \"agent\" or no type) - agent handles OAuth internally\n * 2. OAuth methods (type: \"oauth2\") - client handles browser-based flow\n * 3. API key methods - only if no OAuth methods are present\n *\n * AUTH_REQUIREMENTS.md: Agent Auth is the default authentication method\n * where the agent manages the entire OAuth flow independently.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Parsed and validated authentication methods (Task 21.1)\n */\n private async attemptAuthentication(\n agentId: string,\n authMethods: ParsedAuthMethod[],\n ): Promise<void> {\n // Check for Agent Auth methods first (AUTH_REQUIREMENTS.md: Agent Auth is default)\n const agentAuthMethods = getAgentAuthMethods(authMethods);\n\n if (agentAuthMethods.length > 0) {\n // Agent Auth: call authenticate method on agent, agent handles OAuth internally\n await this.attemptAgentAuthentication(agentId, agentAuthMethods);\n return;\n }\n\n // Check for Terminal Auth methods (Task 36: type: \"terminal\")\n // Terminal Auth: spawn agent with args/env for interactive TUI setup\n const terminalAuthMethods = getTerminalAuthMethods(authMethods);\n\n if (terminalAuthMethods.length > 0) {\n // Terminal Auth: spawn interactive setup process\n await this.attemptTerminalAuthentication(agentId, terminalAuthMethods);\n return;\n }\n\n // Check for OAuth methods (type: \"oauth2\") - client handles browser flow\n const oauthMethods = getOAuthMethods(authMethods);\n\n if (oauthMethods.length > 0) {\n // OAuth methods present - initiate OAuth flow (Requirement 3.1, 3.2)\n // Do NOT fall back to API key when agent explicitly requires OAuth\n await this.attemptOAuthAuthentication(agentId, oauthMethods);\n return;\n }\n\n // No OAuth methods - try API key authentication\n await this.attemptApiKeyAuthentication(agentId, authMethods);\n }\n\n /**\n * Attempt Agent Auth authentication for an agent.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls `authenticate` method on agent,\n * agent handles: HTTP server, browser launch, OAuth callback, token storage.\n *\n * Task 35.1: Call `authenticate` JSON-RPC method on agent\n * - Send: { jsonrpc: \"2.0\", method: \"authenticate\", params: { id: authMethod.id }, id: requestId }\n * - Wait for response from agent\n *\n * Task 35.2: Handle authenticate response\n * - On success: retry original request (session/new)\n * - On error: return error to client\n *\n * @param agentId - The agent to authenticate\n * @param agentAuthMethods - Agent Auth methods from agent's authMethods\n */\n private async attemptAgentAuthentication(\n agentId: string,\n agentAuthMethods: Array<ParsedAuthMethod & { kind: 'agent' }>,\n ): Promise<void> {\n // Select the first Agent Auth method\n const selectedMethod = agentAuthMethods[0];\n\n logInfo(`Agent ${agentId} requires Agent Auth with method: ${selectedMethod.id}`);\n logInfo(`Calling authenticate method on agent - agent will handle OAuth flow internally`);\n\n // Set auth state to pending while agent handles OAuth\n this.setAuthState(agentId, 'pending');\n\n try {\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for Agent Auth: ${(error as Error).message}`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Call authenticate method on agent and wait for response\n const success = await this.callAgentAuthenticate(agentId, selectedMethod.id, runtime);\n\n if (success) {\n logInfo(`Agent Auth successful for agent ${agentId}`);\n this.setAuthState(agentId, 'authenticated');\n } else {\n logError(`Agent Auth failed for agent ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Agent Auth error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Call the `authenticate` JSON-RPC method on an agent.\n *\n * AUTH_REQUIREMENTS.md: Agent Auth - client calls authenticate method on agent\n * Send: { jsonrpc: \"2.0\", method: \"authenticate\", params: { id: authMethod.id }, id: requestId }\n *\n * Task 35.1: Call `authenticate` JSON-RPC method on agent\n *\n * @param agentId - The agent to authenticate\n * @param authMethodId - The auth method ID from authMethods\n * @param runtime - The agent runtime\n * @returns Promise that resolves to true on success, false on failure\n */\n private callAgentAuthenticate(\n agentId: string,\n authMethodId: string,\n runtime: AgentRuntime,\n ): Promise<boolean> {\n return new Promise((resolve) => {\n const requestId = `agent-auth-${agentId}-${Date.now()}`;\n\n // Build authenticate request per AUTH_REQUIREMENTS.md\n const authenticateRequest = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'authenticate',\n params: {\n id: authMethodId,\n },\n };\n\n // Track the pending authenticate request\n const pendingRequest: PendingAuthenticateRequest = {\n requestId,\n agentId,\n authMethodId,\n sentAt: Date.now(),\n resolve: (success: boolean, error?: string) => {\n // Clean up the pending request\n this.pendingAuthenticateRequests.delete(requestId);\n if (error) {\n logError(`Agent Auth response error: ${error}`);\n }\n resolve(success);\n },\n };\n\n this.pendingAuthenticateRequests.set(requestId, pendingRequest);\n\n // Set up timeout for the authenticate request\n setTimeout(() => {\n const pending = this.pendingAuthenticateRequests.get(requestId);\n if (pending) {\n logError(`Agent Auth timeout for agent ${agentId} (method: ${authMethodId})`);\n this.pendingAuthenticateRequests.delete(requestId);\n resolve(false);\n }\n }, AGENT_AUTH_TIMEOUT_MS);\n\n // Send authenticate request to agent\n const success = runtime.write(authenticateRequest);\n\n if (!success) {\n logError(`Failed to send authenticate request to agent ${agentId}`);\n this.pendingAuthenticateRequests.delete(requestId);\n resolve(false);\n } else {\n logInfo(`Sent authenticate request to agent ${agentId} (id: ${requestId}, method: ${authMethodId})`);\n }\n });\n }\n\n /**\n * Attempt Terminal Auth authentication for an agent.\n *\n * AUTH_REQUIREMENTS.md: Terminal Auth - client spawns agent binary with args/env\n * from authMethod for interactive TUI setup.\n *\n * Task 36.1: Parse Terminal Auth from authMethods\n * Task 36.2: Launch agent binary with args/env\n * Task 36.3: Retry after Terminal Auth\n *\n * Flow:\n * 1. Stop current agent runtime (if running)\n * 2. Spawn agent with args/env from authMethod (stdio: 'inherit' for TUI)\n * 3. Wait for process exit\n * 4. On exit code 0: restart normal runtime and verify auth\n * 5. On non-zero exit: mark as failed\n *\n * @param agentId - The agent to authenticate\n * @param terminalAuthMethods - Terminal Auth methods from agent's authMethods\n */\n private async attemptTerminalAuthentication(\n agentId: string,\n terminalAuthMethods: Array<ParsedAuthMethod & { kind: 'terminal' }>,\n ): Promise<void> {\n // Select the first Terminal Auth method\n const selectedMethod = terminalAuthMethods[0];\n\n logInfo(`Agent ${agentId} requires Terminal Auth with method: ${selectedMethod.id}`);\n\n // Check if we're in a TTY environment (required for interactive TUI)\n if (!this.isStdinTTY() || !this.isStdoutTTY()) {\n logError(`Terminal Auth requires interactive terminal (TTY). Run in a terminal with stdin/stdout connected.`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Set auth state to pending\n this.setAuthState(agentId, 'pending');\n\n try {\n // Step 1: Stop current agent runtime if running\n const existingRuntime = this.runtimeManager.get(agentId);\n if (existingRuntime) {\n logInfo(`Stopping existing runtime for agent ${agentId} before Terminal Auth`);\n await this.runtimeManager.terminate(agentId);\n }\n\n // Step 2: Get spawn command for the agent\n const baseSpawnCommand = this.registry.resolve(agentId);\n\n // Build Terminal Auth spawn command using args/env from authMethod (replacement, not merge)\n const terminalArgs = selectedMethod.args ?? [];\n const terminalEnv = {\n ...process.env, // Inherit current environment\n ...(selectedMethod.env ?? {}), // Override with authMethod env\n };\n\n logInfo(`Launching Terminal Auth for ${agentId}: ${baseSpawnCommand.command} ${terminalArgs.join(' ')}`);\n\n // Step 3: Spawn interactive process with inherited stdio\n const exitCode = await this.runTerminalAuthProcess(\n baseSpawnCommand.command,\n terminalArgs,\n terminalEnv,\n );\n\n // Step 4: Handle exit code\n if (exitCode === 0) {\n logInfo(`Terminal Auth process exited successfully for ${agentId}`);\n\n // Restart normal runtime and verify auth\n const authVerified = await this.verifyTerminalAuthSuccess(agentId);\n\n if (authVerified) {\n logInfo(`Terminal Auth verified for ${agentId}`);\n this.setAuthState(agentId, 'authenticated');\n } else {\n logError(`Terminal Auth completed but verification failed for ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } else {\n logError(`Terminal Auth process exited with code ${exitCode} for ${agentId}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Terminal Auth error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Run the Terminal Auth process with inherited stdio for interactive TUI.\n *\n * @param command - The command to execute\n * @param args - Command-line arguments\n * @param env - Environment variables\n * @returns Promise that resolves to the exit code\n */\n private runTerminalAuthProcess(\n command: string,\n args: string[],\n env: Record<string, string | undefined>,\n ): Promise<number> {\n return new Promise((resolve, reject) => {\n logInfo(`Spawning Terminal Auth process: ${command} ${args.join(' ')}`);\n\n const child = this.spawnFn(command, args, {\n env: env as NodeJS.ProcessEnv,\n stdio: 'inherit', // Inherit stdin/stdout/stderr for interactive TUI\n shell: false,\n });\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n logError(`Terminal Auth process timed out after ${TERMINAL_AUTH_TIMEOUT_MS}ms`);\n child.kill('SIGTERM');\n // Give it a moment to terminate gracefully, then SIGKILL\n setTimeout(() => {\n if (!child.killed) {\n child.kill('SIGKILL');\n }\n }, 5000);\n }, TERMINAL_AUTH_TIMEOUT_MS);\n\n child.on('error', (error) => {\n clearTimeout(timeoutId);\n reject(error);\n });\n\n child.on('exit', (code, signal) => {\n clearTimeout(timeoutId);\n if (signal) {\n logError(`Terminal Auth process killed by signal: ${signal}`);\n resolve(1); // Treat signal termination as failure\n } else {\n resolve(code ?? 1);\n }\n });\n });\n }\n\n /**\n * Verify that Terminal Auth was successful by restarting the agent\n * and checking if authentication is now available.\n *\n * @param agentId - The agent to verify\n * @returns true if auth is now available, false otherwise\n */\n private async verifyTerminalAuthSuccess(agentId: string): Promise<boolean> {\n try {\n // Restart the agent runtime\n let spawnCommand = this.registry.resolve(agentId);\n\n // Merge env from api-keys.json (credentials may have been stored by Terminal Auth)\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n\n const runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n\n // For now, we trust that Terminal Auth stored credentials properly\n // A more robust verification would send an initialize request and check\n // if AUTH_REQUIRED is still returned, but that adds complexity.\n // The next actual request will verify auth status.\n return runtime.state === 'running';\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`Failed to verify Terminal Auth for ${agentId}: ${errorMessage}`);\n return false;\n }\n }\n\n /**\n * Attempt OAuth authentication for an agent using browser-based flow.\n *\n * Requirement 3.1: WHEN an agent requires OAuth authentication with `type: \"agent\"`,\n * THE Auth_Module SHALL initiate the OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirement 3.2: WHEN initiating the authorization flow, THE Auth_Module SHALL\n * open the system default browser to the provider's authorization URL.\n *\n * @param agentId - The agent to authenticate\n * @param oauthMethods - OAuth methods from agent's authMethods (already validated)\n */\n private async attemptOAuthAuthentication(\n agentId: string,\n oauthMethods: Array<ParsedAuthMethod & { kind: 'oauth2' }>,\n ): Promise<void> {\n // Check if AuthManager is available for OAuth\n if (!this.authManager) {\n logError(`OAuth authentication required for agent ${agentId}, but AuthManager not available`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Select the first OAuth method (could be enhanced with user preference later)\n const selectedMethod = oauthMethods[0];\n const providerId = selectedMethod.providerId;\n\n logInfo(`Agent ${agentId} requires OAuth authentication with provider: ${providerId}`);\n logInfo(`Initiating OAuth 2.1 Authorization Code flow with PKCE for ${providerId}`);\n\n // Set auth state to pending while browser flow is in progress\n this.setAuthState(agentId, 'pending');\n\n try {\n // Call AuthManager.authenticateAgent to start the browser-based OAuth flow\n // This opens the system default browser and waits for the callback\n const result = await this.authManager.authenticateAgent(providerId);\n\n if (result.success) {\n logInfo(`OAuth authentication successful for agent ${agentId} with provider ${providerId}`);\n this.setAuthState(agentId, 'authenticated');\n\n // After successful OAuth, send authenticate request to agent with the token\n await this.sendOAuthCredentialsToAgent(agentId, selectedMethod);\n } else {\n const errorMsg = result.error?.message ?? 'Unknown error';\n const errorCode = result.error?.code ?? 'UNKNOWN';\n logError(`OAuth authentication failed for agent ${agentId}: [${errorCode}] ${errorMsg}`);\n this.setAuthState(agentId, 'failed');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logError(`OAuth authentication error for agent ${agentId}: ${errorMessage}`);\n this.setAuthState(agentId, 'failed');\n }\n }\n\n /**\n * Send OAuth credentials to agent after successful browser-based authentication.\n *\n * After the OAuth flow completes successfully, this method retrieves the token\n * from AuthManager and sends an authenticate request to the agent.\n *\n * @param agentId - The agent to send credentials to\n * @param method - The OAuth method used for authentication\n */\n private async sendOAuthCredentialsToAgent(\n agentId: string,\n method: ParsedAuthMethod & { kind: 'oauth2' },\n ): Promise<void> {\n if (!this.authManager) {\n logError(`Cannot send OAuth credentials: AuthManager not available`);\n return;\n }\n\n // Get the access token from AuthManager\n const token = await this.authManager.getTokenForAgent(agentId, method.providerId);\n if (!token) {\n logError(`No OAuth token available for agent ${agentId} after successful auth`);\n return;\n }\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for OAuth credential injection: ${(error as Error).message}`);\n return;\n }\n\n // Build authenticate request with OAuth token\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: method.id,\n credentials: {\n accessToken: token,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send OAuth authenticate request to ${agentId}: ${error.message}`);\n } else {\n logInfo(`Sent OAuth authenticate request to agent ${agentId}`);\n }\n });\n }\n }\n\n /**\n * Attempt API key authentication for an agent.\n *\n * This is the fallback authentication method when no OAuth methods are present.\n * Uses the legacy api-keys.json configuration.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Parsed authentication methods (already validated)\n */\n private async attemptApiKeyAuthentication(\n agentId: string,\n authMethods: ParsedAuthMethod[],\n ): Promise<void> {\n // Get API key for this agent\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n\n if (!apiKey) {\n logError(`No API key found for agent ${agentId}, authentication will fail`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Get API key methods from parsed methods (already validated)\n const apiKeyMethods = getApiKeyMethods(authMethods);\n\n // Allowlist of safe method IDs for API key authentication\n // Only send API keys to methods we explicitly trust\n // Note: OpenAI and Anthropic API key methods will be handled by model-credentials module\n const SAFE_API_KEY_METHODS = [\n 'api-key',\n 'openai-api-key',\n 'github-api-key',\n 'google-api-key',\n 'azure-api-key',\n 'cognito-api-key',\n ];\n\n // Select authentication method from allowlist only (security: don't send API key to arbitrary methods)\n const selectedMethod = apiKeyMethods.find(m => SAFE_API_KEY_METHODS.includes(m.id));\n\n if (!selectedMethod) {\n // No safe API key method available - do not fall back to arbitrary methods\n logError(`No safe API key method available for agent ${agentId}, skipping auto-auth`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n logInfo(`Authenticating agent ${agentId} with API key method: ${selectedMethod.id} (providerId: ${selectedMethod.providerId ?? 'none'})`);\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n let spawnCommand = this.registry.resolve(agentId);\n // Merge env from api-keys.json\n const agentEnv = getAgentEnv(this.apiKeys, agentId);\n if (Object.keys(agentEnv).length > 0) {\n spawnCommand = {\n ...spawnCommand,\n env: { ...spawnCommand.env, ...agentEnv },\n };\n }\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for authentication: ${(error as Error).message}`);\n this.setAuthState(agentId, 'failed');\n return;\n }\n\n // Build authenticate request\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: selectedMethod.id,\n credentials: {\n apiKey: apiKey,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send authenticate request to ${agentId}: ${error.message}`);\n this.setAuthState(agentId, 'failed');\n } else {\n logInfo(`Sent authenticate request to agent ${agentId}`);\n // Mark as authenticated (optimistic)\n this.setAuthState(agentId, 'authenticated');\n }\n });\n }\n }\n\n /**\n * Get the number of pending requests.\n *\n * @returns The count of pending requests\n */\n get pendingCount(): number {\n return this.pendingRequests.size;\n }\n\n /**\n * Check if a request ID is pending.\n *\n * @param id - The request ID to check\n * @returns true if the request is pending, false otherwise\n */\n isPending(id: string | number): boolean {\n return this.pendingRequests.has(id);\n }\n\n /**\n * Clear all pending requests.\n * Useful for cleanup during shutdown.\n */\n clearPending(): void {\n this.pendingRequests.clear();\n }\n\n /**\n * Clear all queued requests and auth state.\n * Useful for cleanup during shutdown.\n *\n * Rejects all queued requests with a shutdown error.\n */\n clearQueues(): void {\n // Reject all queued requests\n for (const [agentId, queue] of this.requestQueue.entries()) {\n for (const queuedRequest of queue) {\n const id = extractId(queuedRequest.message);\n queuedRequest.resolve(\n createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Router shutdown', {\n agentId,\n reason: 'Router is shutting down',\n })\n );\n }\n }\n\n this.requestQueue.clear();\n this.authState.clear();\n this.agentOAuthRequirements.clear();\n logInfo('Cleared all request queues, auth state, and OAuth requirements');\n }\n\n /**\n * Reset auth state for an agent.\n * Useful for retry scenarios or logout.\n *\n * @param agentId - The agent identifier\n */\n resetAuthState(agentId: string): void {\n this.setAuthState(agentId, 'none');\n }\n\n /**\n * Get the OAuth requirement for an agent.\n *\n * Requirement 11.2: Check agent auth requirements.\n *\n * @param agentId - The agent identifier\n * @returns The required OAuth provider ID, or undefined if no OAuth required\n */\n getAgentOAuthRequirement(agentId: string): AuthProviderId | undefined {\n return this.agentOAuthRequirements.get(agentId);\n }\n\n /**\n * Set the OAuth requirement for an agent.\n *\n * Requirement 11.2: Cache auth requirements per agent.\n *\n * @param agentId - The agent identifier\n * @param providerId - The required OAuth provider ID\n */\n setAgentOAuthRequirement(agentId: string, providerId: AuthProviderId): void {\n this.agentOAuthRequirements.set(agentId, providerId);\n logInfo(`Set OAuth requirement for agent ${agentId}: provider ${providerId}`);\n }\n\n /**\n * Clear the OAuth requirement for an agent.\n *\n * @param agentId - The agent identifier\n */\n clearAgentOAuthRequirement(agentId: string): void {\n this.agentOAuthRequirements.delete(agentId);\n logInfo(`Cleared OAuth requirement for agent ${agentId}`);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Core type definitions for OAuth 2.1 authentication.\n *\n * @module types\n */\n\n// =============================================================================\n// Provider and Status Types\n// =============================================================================\n\n/**\n * Supported OAuth/OIDC provider identifiers for user identity.\n *\n * Note: OpenAI and Anthropic are NOT public OAuth IdPs for third-party login.\n * They use API keys instead. See ModelProviderId and model-credentials module.\n *\n * Requirements: 7.1, 7a.1\n */\nexport type AuthProviderId =\n | 'google'\n | 'azure'\n | 'cognito'\n | 'github'\n | 'oidc';\n\n/**\n * Model API providers that use API keys (NOT OAuth).\n *\n * These providers do not offer public OAuth IdP for third-party login.\n * Use API key authentication instead.\n *\n * Requirements: 7b.1, 7b.2\n */\nexport type ModelProviderId =\n | 'openai'\n | 'anthropic';\n\n/**\n * Storage backend types.\n */\nexport type StorageBackendType = 'keychain' | 'encrypted-file' | 'memory';\n\n/**\n * Token status for a provider.\n */\nexport type TokenStatus =\n | 'authenticated' // Valid tokens available\n | 'expired' // Tokens expired, refresh needed\n | 'refresh-failed' // Refresh attempted but failed\n | 'not-configured'; // No credentials stored\n\n/**\n * Authentication error codes.\n */\nexport type AuthErrorCode =\n | 'INVALID_STATE'\n | 'TIMEOUT'\n | 'NETWORK_ERROR'\n | 'INVALID_CREDENTIALS'\n | 'STORAGE_ERROR'\n | 'PROVIDER_ERROR'\n | 'UNSUPPORTED_PROVIDER'\n | 'CALLBACK_ERROR'\n | 'TOKEN_REFRESH_FAILED'\n | 'HEADLESS_ENVIRONMENT';\n\n// =============================================================================\n// Token and Credential Types\n// =============================================================================\n\n/**\n * OAuth token response from provider.\n */\nexport interface TokenResponse {\n accessToken: string;\n tokenType: string;\n expiresIn?: number; // Seconds until expiration\n refreshToken?: string;\n scope?: string;\n idToken?: string;\n}\n\n/**\n * Token injection method for agent requests.\n */\nexport interface TokenInjectionMethod {\n type: 'header' | 'query' | 'body';\n key: string; // e.g., 'Authorization', 'x-api-key', 'access_token'\n format?: string; // e.g., 'Bearer {token}'\n}\n\n/**\n * Custom provider endpoints (for Cognito/Azure).\n */\nexport interface ProviderEndpoints {\n authorizationEndpoint: string;\n tokenEndpoint: string;\n userInfoEndpoint?: string;\n}\n\n/**\n * Stored credentials in credential store.\n */\nexport interface StoredCredentials {\n providerId: AuthProviderId;\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number; // Unix timestamp\n scope?: string;\n clientId?: string; // For terminal auth\n clientSecret?: string; // For terminal auth (encrypted)\n customEndpoints?: ProviderEndpoints; // For Cognito/Azure\n storedAt: number; // Unix timestamp\n}\n\n// =============================================================================\n// Authorization Flow Types\n// =============================================================================\n\n/**\n * Authorization parameters for building auth URL.\n */\nexport interface AuthorizationParams {\n clientId: string;\n redirectUri: string;\n scope: string;\n state: string;\n codeChallenge: string;\n codeChallengeMethod: 'S256';\n responseType: 'code';\n additionalParams?: Record<string, string>;\n}\n\n/**\n * Successful callback result from OAuth redirect.\n */\nexport interface CallbackSuccess {\n success: true;\n code: string;\n state: string;\n}\n\n/**\n * Error callback result from OAuth redirect.\n */\nexport interface CallbackErrorResult {\n success: false;\n error: string;\n errorDescription?: string;\n state?: string; // State may be present in error callbacks\n}\n\n/**\n * Callback result from OAuth redirect (discriminated union).\n * OAuth error redirects may not include 'code', only 'error'.\n */\nexport type CallbackResult = CallbackSuccess | CallbackErrorResult;\n\n/**\n * Agent auth flow options.\n */\nexport interface AgentAuthOptions {\n timeoutMs?: number; // Default: 300000 (5 minutes)\n scopes?: string[]; // Override default scopes\n clientId?: string; // Override default client ID\n}\n\n// =============================================================================\n// Result and Error Types\n// =============================================================================\n\n/**\n * Authentication error with details.\n */\nexport interface AuthError {\n code: AuthErrorCode;\n message: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Successful authentication result.\n */\nexport interface AuthResultSuccess {\n success: true;\n providerId: AuthProviderId;\n}\n\n/**\n * Failed authentication result.\n */\nexport interface AuthResultFailure {\n success: false;\n providerId: AuthProviderId;\n error: AuthError;\n}\n\n/**\n * Authentication result (discriminated union).\n */\nexport type AuthResult = AuthResultSuccess | AuthResultFailure;\n\n/**\n * Auth status for display.\n */\nexport interface AuthStatusEntry {\n providerId: AuthProviderId;\n status: TokenStatus;\n expiresAt?: number;\n scope?: string;\n lastRefresh?: number;\n}\n\n/**\n * Map of provider IDs to their auth status.\n */\nexport type AuthStatusMap = Map<AuthProviderId, AuthStatusEntry>;\n\n// =============================================================================\n// Configuration Types\n// =============================================================================\n\n/**\n * Provider configuration.\n */\nexport interface ProviderConfig {\n clientId: string;\n clientSecret?: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n defaultScopes: string[];\n tokenInjection: TokenInjectionMethod;\n}\n\n/**\n * Authentication method types.\n * Used for method precedence selection.\n */\nexport type AuthMethodType = 'oauth2' | 'api-key';\n\n/**\n * Valid authentication method types for runtime validation.\n */\nexport const VALID_AUTH_METHOD_TYPES: readonly AuthMethodType[] = [\n 'oauth2',\n 'api-key',\n] as const;\n\n/**\n * Type guard to check if a value is a valid AuthMethodType.\n * @param value - The value to check\n * @returns True if the value is a valid AuthMethodType\n */\nexport function isValidAuthMethodType(value: unknown): value is AuthMethodType {\n return typeof value === 'string' && VALID_AUTH_METHOD_TYPES.includes(value as AuthMethodType);\n}\n\n/**\n * Configuration for authentication method precedence.\n *\n * Defines the order in which authentication methods are attempted.\n * Default precedence: oauth2 > api-key (OAuth preferred when available)\n *\n * Requirements: 3.1, 10.3\n */\nexport interface AuthMethodPrecedenceConfig {\n /**\n * Ordered list of authentication methods by preference.\n * First method in the list has highest priority.\n * Default: ['oauth2', 'api-key']\n */\n methodPrecedence: AuthMethodType[];\n\n /**\n * Whether to fail fast when an unsupported method is encountered.\n * If true, throws an error immediately.\n * If false, skips the unsupported method and tries the next one.\n * Default: true\n */\n failFastOnUnsupported: boolean;\n\n /**\n * Whether to fail fast when provider ID is ambiguous.\n * Ambiguity occurs when multiple providers could match an agent.\n * If true, throws an error immediately.\n * If false, uses the first matching provider.\n * Default: true\n */\n failFastOnAmbiguous: boolean;\n}\n\n/**\n * Default authentication method precedence configuration.\n *\n * OAuth2 is preferred over API keys when both are available.\n * This aligns with Requirement 10.3: prefer OAuth credentials.\n */\nexport const DEFAULT_AUTH_METHOD_PRECEDENCE: AuthMethodPrecedenceConfig = {\n methodPrecedence: ['oauth2', 'api-key'],\n failFastOnUnsupported: true,\n failFastOnAmbiguous: true,\n};\n\n/**\n * Extended launcher config with auth settings.\n */\nexport interface AuthConfig {\n /** Default auth timeout in seconds */\n authTimeoutSec: number;\n /** Proactive token refresh threshold in seconds */\n tokenRefreshThresholdSec: number;\n /** Preferred storage backend */\n preferredStorageBackend?: StorageBackendType;\n /**\n * Authentication method precedence configuration.\n * Controls which auth method is preferred when multiple are available.\n * Default: oauth2 > api-key\n */\n methodPrecedence?: Partial<AuthMethodPrecedenceConfig>;\n}\n\n// =============================================================================\n// ACP Protocol Types\n// =============================================================================\n\n/**\n * ACP protocol auth method advertisement.\n *\n * Supports all ACP Registry auth types:\n * - 'oauth2': Browser-based OAuth 2.1 flow\n * - 'api-key': Legacy API key authentication\n * - 'agent': Agent handles OAuth internally (ACP default)\n * - 'terminal': Interactive terminal setup (AUTHENTICATION.md)\n */\nexport interface AcpAuthMethod {\n id: string;\n type: 'oauth2' | 'api-key' | 'agent' | 'terminal';\n providerId?: AuthProviderId;\n /** CLI args for terminal auth (e.g. ['--setup']) */\n args?: string[];\n /** Environment variables for terminal auth */\n env?: Record<string, string>;\n}\n\n// =============================================================================\n// Type Guards and Validation Functions\n// =============================================================================\n\n/**\n * Valid provider IDs for runtime validation.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n */\nexport const VALID_PROVIDER_IDS: readonly AuthProviderId[] = [\n 'github',\n 'google',\n 'cognito',\n 'azure',\n 'oidc',\n] as const;\n\n/**\n * Valid storage backend types for runtime validation.\n */\nexport const VALID_STORAGE_BACKENDS: readonly StorageBackendType[] = [\n 'keychain',\n 'encrypted-file',\n 'memory',\n] as const;\n\n/**\n * Valid token status values for runtime validation.\n */\nexport const VALID_TOKEN_STATUSES: readonly TokenStatus[] = [\n 'authenticated',\n 'expired',\n 'refresh-failed',\n 'not-configured',\n] as const;\n\n/**\n * Valid auth error codes for runtime validation.\n */\nexport const VALID_ERROR_CODES: readonly AuthErrorCode[] = [\n 'INVALID_STATE',\n 'TIMEOUT',\n 'NETWORK_ERROR',\n 'INVALID_CREDENTIALS',\n 'STORAGE_ERROR',\n 'PROVIDER_ERROR',\n 'UNSUPPORTED_PROVIDER',\n 'CALLBACK_ERROR',\n 'TOKEN_REFRESH_FAILED',\n 'HEADLESS_ENVIRONMENT',\n] as const;\n\n/**\n * Type guard to check if a value is a valid AuthProviderId.\n * @param value - The value to check\n * @returns True if the value is a valid AuthProviderId\n */\nexport function isValidProviderId(value: unknown): value is AuthProviderId {\n return typeof value === 'string' && VALID_PROVIDER_IDS.includes(value as AuthProviderId);\n}\n\n/**\n * Type guard to check if a value is a valid StorageBackendType.\n * @param value - The value to check\n * @returns True if the value is a valid StorageBackendType\n */\nexport function isValidStorageBackend(value: unknown): value is StorageBackendType {\n return typeof value === 'string' && VALID_STORAGE_BACKENDS.includes(value as StorageBackendType);\n}\n\n/**\n * Type guard to check if a value is a valid TokenStatus.\n * @param value - The value to check\n * @returns True if the value is a valid TokenStatus\n */\nexport function isValidTokenStatus(value: unknown): value is TokenStatus {\n return typeof value === 'string' && VALID_TOKEN_STATUSES.includes(value as TokenStatus);\n}\n\n/**\n * Type guard to check if a value is a valid AuthErrorCode.\n * @param value - The value to check\n * @returns True if the value is a valid AuthErrorCode\n */\nexport function isValidErrorCode(value: unknown): value is AuthErrorCode {\n return typeof value === 'string' && VALID_ERROR_CODES.includes(value as AuthErrorCode);\n}\n\n// =============================================================================\n// AuthMethod ID to Provider ID Mapping\n// =============================================================================\n\n/**\n * Explicit mapping from authMethod.id to AuthProviderId.\n *\n * This mapping table provides a secure, explicit translation from\n * ACP authMethod identifiers to internal provider IDs.\n *\n * SECURITY: No substring or heuristic matching is used.\n * Only exact matches in this table are accepted.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n *\n * Requirements: 7.1, 13.4\n */\nexport const AUTH_METHOD_ID_TO_PROVIDER_ID: Readonly<Record<string, AuthProviderId>> = {\n // OAuth2 method IDs\n 'oauth2-github': 'github',\n 'oauth2-google': 'google',\n 'oauth2-cognito': 'cognito',\n 'oauth2-azure': 'azure',\n 'oauth2-oidc': 'oidc',\n\n // Direct provider IDs (for backward compatibility)\n 'github': 'github',\n 'google': 'google',\n 'cognito': 'cognito',\n 'azure': 'azure',\n 'oidc': 'oidc',\n} as const;\n\n/**\n * Valid authMethod.id values for runtime validation.\n */\nexport const VALID_AUTH_METHOD_IDS: readonly string[] = Object.keys(AUTH_METHOD_ID_TO_PROVIDER_ID);\n\n/**\n * Error thrown when an unknown authMethod.id is encountered.\n *\n * Requirements: 13.4\n */\nexport class UnknownAuthMethodIdError extends Error {\n public readonly code = 'UNSUPPORTED_PROVIDER' as const;\n public readonly unknownMethodId: string;\n public readonly supportedMethodIds: readonly string[];\n public readonly supportedProviders: readonly AuthProviderId[];\n\n constructor(unknownMethodId: string) {\n const supportedMethodIds = VALID_AUTH_METHOD_IDS;\n const supportedProviders = VALID_PROVIDER_IDS;\n\n super(\n `Unknown authMethod.id: \"${unknownMethodId}\". ` +\n `Supported method IDs: ${supportedMethodIds.join(', ')}. ` +\n `Supported providers: ${supportedProviders.join(', ')}.`\n );\n\n this.name = 'UnknownAuthMethodIdError';\n this.unknownMethodId = unknownMethodId;\n this.supportedMethodIds = supportedMethodIds;\n this.supportedProviders = supportedProviders;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UnknownAuthMethodIdError);\n }\n }\n}\n\n/**\n * Type guard to check if a value is a valid authMethod.id.\n *\n * @param value - The value to check\n * @returns True if the value is a valid authMethod.id\n */\nexport function isValidAuthMethodId(value: unknown): value is string {\n return typeof value === 'string' && value in AUTH_METHOD_ID_TO_PROVIDER_ID;\n}\n\n/**\n * Resolves an authMethod.id to its corresponding AuthProviderId.\n *\n * This function uses explicit mapping only - no substring matching,\n * no heuristics, no fuzzy matching. This is a security requirement\n * to prevent provider confusion attacks.\n *\n * @param authMethodId - The authMethod.id to resolve\n * @returns The corresponding AuthProviderId\n * @throws UnknownAuthMethodIdError if the authMethod.id is not recognized\n *\n * Requirements: 7.1, 13.4\n *\n * @example\n * ```typescript\n * // Valid mappings\n * resolveAuthMethodIdToProviderId('oauth2-openai'); // returns 'openai'\n * resolveAuthMethodIdToProviderId('oauth2-github'); // returns 'github'\n * resolveAuthMethodIdToProviderId('github'); // returns 'github' (direct)\n *\n * // Invalid - throws UnknownAuthMethodIdError\n * resolveAuthMethodIdToProviderId('oauth2-unknown');\n * resolveAuthMethodIdToProviderId('openai-oauth2'); // wrong format\n * resolveAuthMethodIdToProviderId('OPENAI'); // case sensitive\n * ```\n */\nexport function resolveAuthMethodIdToProviderId(authMethodId: string): AuthProviderId {\n const providerId = AUTH_METHOD_ID_TO_PROVIDER_ID[authMethodId];\n\n if (providerId === undefined) {\n throw new UnknownAuthMethodIdError(authMethodId);\n }\n\n return providerId;\n}\n\n/**\n * Safely resolves an authMethod.id to its corresponding AuthProviderId.\n *\n * Unlike `resolveAuthMethodIdToProviderId`, this function returns null\n * instead of throwing an error for unknown method IDs.\n *\n * @param authMethodId - The authMethod.id to resolve\n * @returns The corresponding AuthProviderId, or null if not recognized\n *\n * Requirements: 7.1, 13.4\n */\nexport function tryResolveAuthMethodIdToProviderId(authMethodId: string): AuthProviderId | null {\n const providerId = AUTH_METHOD_ID_TO_PROVIDER_ID[authMethodId];\n return providerId ?? null;\n}\n", "/**\n * Logging utilities for the Registry Launcher.\n *\n * Provides structured logging to stderr with ISO 8601 timestamps and severity levels.\n * All logs go to stderr only - stdout is reserved for NDJSON protocol messages.\n *\n * @module log\n */\n\n/**\n * Log severity levels.\n */\nexport type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';\n\n/**\n * Log context identifier for the registry launcher.\n */\nconst LOG_CONTEXT = 'registry-launcher';\n\n/**\n * Format a log message with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n * @returns Formatted log line\n */\nfunction formatLogMessage(level: LogLevel, message: string, context: string = LOG_CONTEXT): string {\n const timestamp = new Date().toISOString();\n return `[${timestamp}] [${level}] [${context}] ${message}`;\n}\n\n/**\n * Log a message to stderr with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level (DEBUG, INFO, WARN, ERROR)\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n */\nexport function log(level: LogLevel, message: string, context?: string): void {\n const formatted = formatLogMessage(level, message, context);\n console.error(formatted);\n}\n\n/**\n * Log an agent spawn event.\n *\n * @param agentId - The agent identifier\n * @param command - The spawn command\n * @param args - The spawn command arguments\n */\nexport function logSpawn(agentId: string, command: string, args: string[]): void {\n const fullCommand = [command, ...args].join(' ');\n log('INFO', `Spawning agent \"${agentId}\": ${fullCommand}`);\n}\n\n/**\n * Log an agent exit event.\n *\n * @param agentId - The agent identifier\n * @param exitCode - The exit code (null if terminated by signal)\n * @param signal - The signal that terminated the process (null if exited normally)\n */\nexport function logExit(agentId: string, exitCode: number | null, signal?: string | null): void {\n if (signal) {\n log('INFO', `Agent \"${agentId}\" exited with signal ${signal}`);\n } else if (exitCode !== null) {\n log('INFO', `Agent \"${agentId}\" exited with code ${exitCode}`);\n } else {\n log('INFO', `Agent \"${agentId}\" exited`);\n }\n}\n\n/**\n * Log a backpressure warning when writing to an agent process fails.\n *\n * @param agentId - The agent identifier\n */\nexport function logBackpressure(agentId: string): void {\n log('WARN', `Write to agent \"${agentId}\" failed due to backpressure`);\n}\n\n/**\n * Log an error when an agent's stdin closes unexpectedly.\n *\n * @param agentId - The agent identifier\n * @param error - The error that occurred\n */\nexport function logStdinClosed(agentId: string, error?: Error): void {\n const errorMessage = error ? `: ${error.message}` : '';\n log('ERROR', `Agent \"${agentId}\" stdin closed unexpectedly${errorMessage}`);\n}\n\n/**\n * Convenience function for logging debug messages.\n *\n * @param message - Debug message\n * @param context - Optional context identifier\n */\nexport function logDebug(message: string, context?: string): void {\n log('DEBUG', message, context);\n}\n\n/**\n * Convenience function for logging info messages.\n *\n * @param message - Info message\n * @param context - Optional context identifier\n */\nexport function logInfo(message: string, context?: string): void {\n log('INFO', message, context);\n}\n\n/**\n * Convenience function for logging warning messages.\n *\n * @param message - Warning message\n * @param context - Optional context identifier\n */\nexport function logWarn(message: string, context?: string): void {\n log('WARN', message, context);\n}\n\n/**\n * Convenience function for logging error messages.\n *\n * @param message - Error message\n * @param context - Optional context identifier\n */\nexport function logError(message: string, context?: string): void {\n log('ERROR', message, context);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Interactive CLI setup flow for headless/manual credential configuration.\n *\n * Implements the terminal auth flow for configuring OAuth credentials\n * in headless environments without browser access.\n *\n * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6\n *\n * @module flows/terminal-auth-flow\n */\n\nimport * as readline from 'readline';\nimport type {\n AuthProviderId,\n AuthResult,\n StoredCredentials,\n ProviderEndpoints,\n} from '../types.js';\nimport { VALID_PROVIDER_IDS } from '../types.js';\nimport type { ICredentialStore } from '../storage/types.js';\n\n/**\n * Authentication mode selected by the user.\n * Requirements: 3.1, 4.2\n */\nexport type AuthenticationMode = 'browser-oauth' | 'manual-api-key';\n\n/**\n * Result indicating browser OAuth flow should be used.\n * This is returned when the user selects \"Browser OAuth\" mode.\n */\nexport interface BrowserOAuthResult {\n /** Indicates browser OAuth flow should be used */\n useBrowserOAuth: true;\n /** The selected provider ID */\n providerId: AuthProviderId;\n}\n\n/**\n * Result indicating manual credential flow completed.\n */\nexport interface ManualCredentialResult {\n /** Indicates manual credential flow was used */\n useBrowserOAuth: false;\n /** The authentication result from manual flow */\n authResult: AuthResult;\n}\n\n/**\n * Combined result type for terminal auth flow execution.\n */\nexport type TerminalAuthFlowResult = BrowserOAuthResult | ManualCredentialResult;\n\n/**\n * Provider display information for the selection menu.\n */\ninterface ProviderInfo {\n id: AuthProviderId;\n name: string;\n requiresClientSecret: boolean;\n requiresCustomEndpoints: boolean;\n /** Whether this provider supports simple API key authentication */\n supportsApiKey: boolean;\n /** Whether this provider supports browser-based OAuth flow */\n supportsOAuth: boolean;\n /** Label for the API key (e.g., \"API Key\", \"Personal Access Token\") */\n apiKeyLabel?: string;\n /** Environment variable name for the API key */\n apiKeyEnvVar?: string;\n}\n\n/**\n * Provider information for display and configuration.\n * Per ACP Registry spec: GitHub supports Personal Access Token alternative.\n * All providers support browser-based OAuth flow.\n *\n * Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys only.\n * OpenAI and Anthropic API key support is handled by the model-credentials module.\n *\n * Requirements: 7.1, 7b.1\n */\nconst PROVIDER_INFO: readonly ProviderInfo[] = [\n { id: 'github', name: 'GitHub', requiresClientSecret: true, requiresCustomEndpoints: false, supportsApiKey: true, supportsOAuth: true, apiKeyLabel: 'Personal Access Token', apiKeyEnvVar: 'GITHUB_TOKEN' },\n { id: 'google', name: 'Google', requiresClientSecret: true, requiresCustomEndpoints: false, supportsApiKey: false, supportsOAuth: true },\n { id: 'cognito', name: 'AWS Cognito', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n { id: 'azure', name: 'Microsoft Entra ID', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n { id: 'oidc', name: 'Generic OIDC', requiresClientSecret: true, requiresCustomEndpoints: true, supportsApiKey: false, supportsOAuth: true },\n] as const;\n\n\n/**\n * Collected credentials from user input.\n */\nexport interface CollectedCredentials {\n clientId: string;\n clientSecret?: string;\n customEndpoints?: ProviderEndpoints;\n}\n\n/**\n * Dependencies for the terminal auth flow.\n */\nexport interface TerminalAuthFlowDependencies {\n /** Credential store for persisting credentials */\n credentialStore: ICredentialStore;\n /** Function to validate credentials (attempts token request) */\n validateCredentials: (\n providerId: AuthProviderId,\n credentials: CollectedCredentials\n ) => Promise<{ valid: boolean; error?: string; accessToken?: string }>;\n /** Optional custom input/output streams (for testing) */\n input?: NodeJS.ReadableStream;\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Terminal auth flow - interactive CLI setup.\n *\n * Provides an interactive terminal interface for configuring OAuth credentials\n * in headless environments. The flow:\n * 1. Prompts user to select a provider\n * 2. Prompts for required credentials\n * 3. Validates credentials by attempting a token request\n * 4. Stores credentials securely on success\n * 5. Prompts for re-entry on validation failure\n */\nexport class TerminalAuthFlow {\n private readonly credentialStore: ICredentialStore;\n private readonly validateCredentials: TerminalAuthFlowDependencies['validateCredentials'];\n private readonly input: NodeJS.ReadableStream;\n private readonly output: NodeJS.WritableStream;\n private rl: readline.Interface | null = null;\n\n /**\n * Create a new terminal auth flow.\n *\n * @param dependencies - Flow dependencies\n */\n constructor(dependencies: TerminalAuthFlowDependencies) {\n this.credentialStore = dependencies.credentialStore;\n this.validateCredentials = dependencies.validateCredentials;\n this.input = dependencies.input ?? process.stdin;\n this.output = dependencies.output ?? process.stderr;\n }\n\n /**\n * Execute the terminal auth flow.\n *\n * Runs the interactive setup wizard to configure OAuth credentials.\n * For providers supporting OAuth, offers a choice between browser OAuth\n * and manual API key entry.\n *\n * Requirements: 3.1, 4.2\n *\n * @param providerId - Optional pre-selected provider (skips provider selection)\n * @returns Terminal auth flow result indicating mode selection and outcome\n */\n async execute(providerId?: AuthProviderId): Promise<TerminalAuthFlowResult> {\n this.rl = readline.createInterface({\n input: this.input,\n output: this.output,\n });\n\n try {\n this.writeLine('\\n=== OAuth Authentication Setup ===\\n');\n\n // Step 1: Select provider (Requirement 4.2)\n const selectedProvider = providerId ?? await this.selectProvider();\n const providerInfo = PROVIDER_INFO.find(p => p.id === selectedProvider);\n\n if (!providerInfo) {\n return {\n useBrowserOAuth: false,\n authResult: {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${selectedProvider}' is not supported.`,\n details: { supportedProviders: VALID_PROVIDER_IDS },\n },\n },\n };\n }\n\n this.writeLine(`\\nConfiguring ${providerInfo.name}...\\n`);\n\n // Step 2: Select authentication mode (Requirement 3.1, 4.2)\n // For providers supporting OAuth, offer choice between browser OAuth and manual\n if (providerInfo.supportsOAuth) {\n const authMode = await this.selectAuthenticationMode(providerInfo);\n\n if (authMode === 'browser-oauth') {\n // Return indicator that browser OAuth flow should be used\n this.writeLine('\\nBrowser OAuth selected. Launching browser authentication flow...\\n');\n return {\n useBrowserOAuth: true,\n providerId: selectedProvider,\n };\n }\n }\n\n // Step 3-5: Collect and validate credentials with retry loop (manual flow)\n const result = await this.collectAndValidateWithRetry(selectedProvider, providerInfo);\n return {\n useBrowserOAuth: false,\n authResult: result,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[TerminalAuthFlow] Error: ${errorMessage}`);\n\n // Use the provided providerId or default to 'github' for error reporting\n // Note: OpenAI is NOT an OAuth provider - it uses API keys\n const errorProviderId = providerId || 'github';\n return {\n useBrowserOAuth: false,\n authResult: {\n success: false,\n providerId: errorProviderId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Terminal auth flow failed: ${errorMessage}`,\n },\n },\n };\n } finally {\n this.cleanup();\n }\n }\n\n /**\n * Select authentication mode for providers supporting OAuth.\n * Offers choice between browser OAuth (recommended) and manual API key.\n *\n * Requirements: 3.1, 4.2\n *\n * @param providerInfo - Provider information\n * @returns Selected authentication mode\n */\n private async selectAuthenticationMode(providerInfo: ProviderInfo): Promise<AuthenticationMode> {\n this.writeLine(`${providerInfo.name} supports multiple authentication methods:\\n`);\n this.writeLine(' 1. Browser OAuth (recommended) - Opens browser for secure authentication');\n this.writeLine(' 2. Manual API Key - Enter credentials directly in terminal\\n');\n\n const selection = await this.promptSelection('Select authentication method (1-2) [default: 1]: ', 1, 2, 1);\n\n return selection === 1 ? 'browser-oauth' : 'manual-api-key';\n }\n\n\n /**\n * Collect and validate credentials with retry loop.\n * Requirements: 4.3, 4.4, 4.5, 4.6\n *\n * Note: When this method is called, the user has already selected \"Manual API Key\"\n * in the authentication mode selection. For providers that support simple API key\n * authentication (OpenAI, Anthropic, GitHub), we collect the API key directly.\n * For providers that don't support simple API key (Google, Cognito, Azure),\n * we collect OAuth client credentials.\n */\n private async collectAndValidateWithRetry(\n selectedProvider: AuthProviderId,\n providerInfo: ProviderInfo\n ): Promise<AuthResult> {\n let credentials: CollectedCredentials | null = null;\n let validationResult: { valid: boolean; error?: string; accessToken?: string } | null = null;\n let attempts = 0;\n const maxAttempts = 3;\n\n // For providers that support simple API key, use API key mode directly\n // (user already selected \"Manual API Key\" in auth mode selection)\n const useApiKey = providerInfo.supportsApiKey;\n\n while (attempts < maxAttempts) {\n attempts++;\n\n // Collect credentials (Requirement 4.3)\n if (useApiKey) {\n credentials = await this.collectApiKeyCredentials(providerInfo);\n } else {\n credentials = await this.collectCredentials(providerInfo);\n }\n\n // Validate credentials (Requirement 4.4)\n this.writeLine('\\nValidating credentials...');\n validationResult = await this.validateCredentials(selectedProvider, credentials);\n\n if (validationResult.valid) {\n break;\n }\n\n // Display error and prompt for re-entry (Requirement 4.6)\n this.writeLine(`\\nValidation failed: ${validationResult.error || 'Unknown error'}`);\n\n if (attempts < maxAttempts) {\n const retry = await this.promptYesNo('Would you like to try again?');\n if (!retry) {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: 'Credential validation failed and user cancelled retry.',\n details: { validationError: validationResult.error },\n },\n };\n }\n this.writeLine('');\n }\n }\n\n if (!validationResult?.valid || !credentials) {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: `Credential validation failed after ${maxAttempts} attempts.`,\n details: { validationError: validationResult?.error },\n },\n };\n }\n\n // Enforce invariant: valid credentials must have a non-empty access token\n if (!validationResult.accessToken || validationResult.accessToken.trim() === '') {\n return {\n success: false,\n providerId: selectedProvider,\n error: {\n code: 'INVALID_CREDENTIALS',\n message: 'Credential validation succeeded but no access token was returned.',\n },\n };\n }\n\n // Step 5: Store credentials securely (Requirement 4.5)\n const storedCredentials: StoredCredentials = {\n providerId: selectedProvider,\n accessToken: validationResult.accessToken,\n clientId: credentials.clientId,\n clientSecret: credentials.clientSecret,\n customEndpoints: credentials.customEndpoints,\n storedAt: Date.now(),\n };\n\n await this.credentialStore.store(selectedProvider, storedCredentials);\n\n this.writeLine(`\\n${providerInfo.name} credentials configured successfully!\\n`);\n\n return {\n success: true,\n providerId: selectedProvider,\n };\n }\n\n /**\n * Prompt for a numeric selection within a range.\n * Supports an optional default value that is used when user presses Enter without input.\n *\n * @param message - The prompt message\n * @param min - Minimum valid selection\n * @param max - Maximum valid selection\n * @param defaultValue - Optional default value used when input is empty\n * @returns The selected number\n */\n private async promptSelection(message: string, min: number, max: number, defaultValue?: number): Promise<number> {\n while (true) {\n const input = await this.prompt(message);\n const trimmed = input.trim();\n\n // If input is empty and we have a default, use it\n if (trimmed === '' && defaultValue !== undefined) {\n return defaultValue;\n }\n\n const selection = parseInt(trimmed, 10);\n\n if (selection >= min && selection <= max) {\n return selection;\n }\n\n this.writeLine(`Invalid selection. Please enter a number between ${min} and ${max}.`);\n }\n }\n\n /**\n * Collect API key credentials (simple mode for OpenAI, Anthropic, GitHub).\n */\n private async collectApiKeyCredentials(providerInfo: ProviderInfo): Promise<CollectedCredentials> {\n const label = providerInfo.apiKeyLabel || 'API Key';\n const envVar = providerInfo.apiKeyEnvVar;\n\n if (envVar) {\n this.writeLine(`(You can also set this via ${envVar} environment variable)\\n`);\n }\n\n const apiKey = await this.promptSecret(`${label}: `);\n\n // For API key auth, we store the API key as the clientId\n // The actual token will be the API key itself\n return {\n clientId: apiKey,\n // No clientSecret needed for API key auth\n };\n }\n\n /**\n * Prompt user to select a provider from the supported list.\n * Requirement 4.2\n */\n private async selectProvider(): Promise<AuthProviderId> {\n this.writeLine('Select an OAuth provider:\\n');\n\n PROVIDER_INFO.forEach((provider, index) => {\n this.writeLine(` ${index + 1}. ${provider.name}`);\n });\n\n this.writeLine('');\n\n while (true) {\n const input = await this.prompt(`Enter selection (1-${PROVIDER_INFO.length}): `);\n const selection = parseInt(input.trim(), 10);\n\n if (selection >= 1 && selection <= PROVIDER_INFO.length) {\n return PROVIDER_INFO[selection - 1].id;\n }\n\n this.writeLine(`Invalid selection. Please enter a number between 1 and ${PROVIDER_INFO.length}.`);\n }\n }\n\n\n /**\n * Collect credentials from user input.\n * Requirement 4.3\n */\n private async collectCredentials(providerInfo: ProviderInfo): Promise<CollectedCredentials> {\n const credentials: CollectedCredentials = {\n clientId: '',\n };\n\n // Always prompt for client ID\n credentials.clientId = await this.promptRequired('Client ID: ');\n\n // Prompt for client secret if required\n if (providerInfo.requiresClientSecret) {\n credentials.clientSecret = await this.promptSecret('Client Secret: ');\n }\n\n // Prompt for custom endpoints if required (Cognito/Azure)\n if (providerInfo.requiresCustomEndpoints) {\n credentials.customEndpoints = await this.collectCustomEndpoints(providerInfo);\n }\n\n return credentials;\n }\n\n /**\n * Collect custom endpoints for providers that require them (Cognito/Azure/OIDC).\n * Validates all endpoints to ensure HTTPS and no embedded credentials.\n */\n private async collectCustomEndpoints(providerInfo: ProviderInfo): Promise<ProviderEndpoints> {\n this.writeLine(`\\n${providerInfo.name} requires custom endpoint configuration:\\n`);\n\n if (providerInfo.id === 'cognito') {\n return this.collectCognitoEndpoints();\n } else if (providerInfo.id === 'azure') {\n return this.collectAzureEndpoints();\n } else if (providerInfo.id === 'oidc') {\n return this.collectOidcEndpoints();\n }\n\n // Generic custom endpoints with HTTPS validation\n const authEndpoint = await this.promptValidatedUrl('Authorization Endpoint URL: ');\n const tokenEndpoint = await this.promptValidatedUrl('Token Endpoint URL: ');\n\n return {\n authorizationEndpoint: authEndpoint,\n tokenEndpoint: tokenEndpoint,\n };\n }\n\n /**\n * Prompt for a validated HTTPS URL.\n * Ensures the URL is valid, uses HTTPS, and has no embedded credentials.\n */\n private async promptValidatedUrl(message: string): Promise<string> {\n while (true) {\n const input = await this.promptRequired(message);\n const error = this.validateHttpsUrl(input);\n if (error === null) {\n return input;\n }\n this.writeLine(`Error: ${error}`);\n }\n }\n\n /**\n * Validate that a URL is a valid HTTPS URL without embedded credentials.\n */\n private validateHttpsUrl(value: string): string | null {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return 'Invalid URL format.';\n }\n if (url.protocol !== 'https:') {\n return 'URL must use HTTPS protocol for security.';\n }\n if (url.username || url.password) {\n return 'URL must not contain embedded credentials.';\n }\n return null;\n }\n\n /**\n * Collect Cognito-specific endpoint configuration.\n * Validates input to prevent URL injection attacks.\n */\n private async collectCognitoEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your Cognito User Pool details:\\n');\n\n const userPoolDomain = await this.promptValidated(\n 'User Pool Domain (e.g., my-app): ',\n this.validateCognitoDomain.bind(this)\n );\n const region = await this.promptValidated(\n 'AWS Region (e.g., us-east-1): ',\n this.validateAwsRegion.bind(this)\n );\n\n const baseUrl = `https://${userPoolDomain}.auth.${region}.amazoncognito.com`;\n\n return {\n authorizationEndpoint: `${baseUrl}/oauth2/authorize`,\n tokenEndpoint: `${baseUrl}/oauth2/token`,\n };\n }\n\n /**\n * Collect Azure AD-specific endpoint configuration.\n * Validates input to prevent URL injection attacks.\n */\n private async collectAzureEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your Microsoft Entra ID details:\\n');\n\n const tenantId = await this.promptValidated(\n 'Tenant ID (or \"common\" for multi-tenant): ',\n this.validateAzureTenantId.bind(this)\n );\n\n const baseUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0`;\n\n return {\n authorizationEndpoint: `${baseUrl}/authorize`,\n tokenEndpoint: `${baseUrl}/token`,\n };\n }\n\n /**\n * Collect Generic OIDC endpoint configuration.\n * Supports issuer-based discovery or manual endpoint entry.\n * Validates input to prevent URL injection attacks.\n *\n * Requirements: 7a.1, 7a.2\n */\n private async collectOidcEndpoints(): Promise<ProviderEndpoints> {\n this.writeLine('Enter your OIDC provider details:\\n');\n this.writeLine('You can provide an issuer URL for automatic discovery,');\n this.writeLine('or manually enter the authorization and token endpoints.\\n');\n\n const useDiscovery = await this.promptYesNo('Use OIDC Discovery (recommended)?');\n\n if (useDiscovery) {\n const issuerUrl = await this.promptValidatedUrl('Issuer URL (e.g., https://auth.example.com): ');\n\n // Build discovery URL from issuer\n const discoveryUrl = issuerUrl.endsWith('/')\n ? `${issuerUrl}.well-known/openid-configuration`\n : `${issuerUrl}/.well-known/openid-configuration`;\n\n this.writeLine(`\\nOIDC Discovery URL: ${discoveryUrl}`);\n this.writeLine('The authorization and token endpoints will be fetched automatically.\\n');\n\n // For OIDC with discovery, we store the issuer URL\n // The actual endpoints will be discovered at runtime\n return {\n authorizationEndpoint: issuerUrl, // Store issuer URL, discovery happens at runtime\n tokenEndpoint: discoveryUrl, // Store discovery URL for reference\n };\n }\n\n // Manual endpoint entry\n this.writeLine('\\nEnter the endpoints manually:\\n');\n const authEndpoint = await this.promptValidatedUrl('Authorization Endpoint URL: ');\n const tokenEndpoint = await this.promptValidatedUrl('Token Endpoint URL: ');\n\n return {\n authorizationEndpoint: authEndpoint,\n tokenEndpoint: tokenEndpoint,\n };\n }\n\n /**\n * Validate Cognito user pool domain.\n * Must be alphanumeric with hyphens, no URL injection characters.\n */\n private validateCognitoDomain(value: string): string | null {\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(value)) {\n return 'Invalid domain format. Must be alphanumeric with hyphens, no leading/trailing hyphens.';\n }\n if (value.length > 63) {\n return 'Domain must be 63 characters or less.';\n }\n if (/[/:?#@\\s]/.test(value)) {\n return 'Domain contains invalid characters (/, :, ?, #, @, or whitespace).';\n }\n return null;\n }\n\n /**\n * Validate AWS region format.\n * Must match pattern like us-east-1, eu-west-2.\n */\n private validateAwsRegion(value: string): string | null {\n if (!/^[a-z]{2}-[a-z]+-\\d+$/.test(value)) {\n return 'Invalid AWS region format. Expected format: us-east-1, eu-west-2, etc.';\n }\n return null;\n }\n\n /**\n * Validate Azure tenant ID.\n * Must be 'common', 'organizations', 'consumers', a valid GUID, or a domain name.\n */\n private validateAzureTenantId(value: string): string | null {\n const wellKnown = ['common', 'organizations', 'consumers'];\n if (wellKnown.includes(value.toLowerCase())) {\n return null;\n }\n // GUID pattern\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {\n return null;\n }\n // Domain pattern (no URL injection chars)\n if (/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(value)) {\n return null;\n }\n if (/[/:?#@\\s]/.test(value)) {\n return 'Tenant ID contains invalid characters (/, :, ?, #, @, or whitespace).';\n }\n return \"Invalid tenant ID. Must be 'common', 'organizations', 'consumers', a valid GUID, or a domain name.\";\n }\n\n /**\n * Prompt for input with validation.\n */\n private async promptValidated(\n message: string,\n validator: (value: string) => string | null\n ): Promise<string> {\n while (true) {\n const input = await this.promptRequired(message);\n const error = validator(input);\n if (error === null) {\n return input;\n }\n this.writeLine(`Error: ${error}`);\n }\n }\n\n\n /**\n * Prompt for required input (non-empty).\n */\n private async promptRequired(message: string): Promise<string> {\n while (true) {\n const input = await this.prompt(message);\n const trimmed = input.trim();\n\n if (trimmed.length > 0) {\n return trimmed;\n }\n\n this.writeLine('This field is required. Please enter a value.');\n }\n }\n\n /**\n * Prompt for secret input (hidden if possible).\n * Note: In a real implementation, this would hide input.\n * For headless environments, we accept visible input.\n */\n private async promptSecret(message: string): Promise<string> {\n // In headless environments, we can't easily hide input\n // The user should be aware they're entering sensitive data\n this.writeLine('(Note: Input will be visible in terminal)');\n return this.promptRequired(message);\n }\n\n /**\n * Prompt for yes/no confirmation.\n */\n private async promptYesNo(message: string): Promise<boolean> {\n while (true) {\n const input = await this.prompt(`${message} (y/n): `);\n const normalized = input.trim().toLowerCase();\n\n if (normalized === 'y' || normalized === 'yes') {\n return true;\n }\n if (normalized === 'n' || normalized === 'no') {\n return false;\n }\n\n this.writeLine('Please enter \"y\" or \"n\".');\n }\n }\n\n /**\n * Prompt for user input.\n */\n private prompt(message: string): Promise<string> {\n return new Promise((resolve) => {\n if (!this.rl) {\n resolve('');\n return;\n }\n\n this.rl.question(message, (answer) => {\n resolve(answer);\n });\n });\n }\n\n /**\n * Write a line to output.\n */\n private writeLine(message: string): void {\n this.output.write(message + '\\n');\n }\n\n /**\n * Clean up resources.\n */\n private cleanup(): void {\n if (this.rl) {\n this.rl.close();\n this.rl = null;\n }\n }\n}\n\n/**\n * Create a terminal auth flow with the given dependencies.\n *\n * @param dependencies - Flow dependencies\n * @returns A new TerminalAuthFlow instance\n */\nexport function createTerminalAuthFlow(\n dependencies: TerminalAuthFlowDependencies\n): TerminalAuthFlow {\n return new TerminalAuthFlow(dependencies);\n}\n\n/**\n * Get provider information by ID.\n *\n * @param providerId - The provider identifier\n * @returns Provider info or undefined if not found\n */\nexport function getProviderInfo(providerId: AuthProviderId): ProviderInfo | undefined {\n return PROVIDER_INFO.find(p => p.id === providerId);\n}\n\n/**\n * Get all supported provider information.\n *\n * @returns Array of provider information\n */\nexport function getAllProviderInfo(): readonly ProviderInfo[] {\n return PROVIDER_INFO;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Auth session management for OAuth 2.1 authorization flows.\n *\n * Tracks in-progress OAuth authorization sessions including PKCE parameters,\n * state for CSRF protection, and timeout handling.\n *\n * @module session\n */\n\nimport { randomUUID } from 'crypto';\nimport type { AuthProviderId } from './types.js';\nimport { validateState, generateState } from './state.js';\nimport { generatePKCEPair } from './pkce.js';\n\n/**\n * Represents an in-progress OAuth authorization flow.\n */\nexport interface IAuthSession {\n /** Unique session identifier */\n readonly sessionId: string;\n\n /** Provider being authenticated */\n readonly providerId: AuthProviderId;\n\n /** PKCE code verifier (kept secret) */\n readonly codeVerifier: string;\n\n /** PKCE code challenge (sent to provider) */\n readonly codeChallenge: string;\n\n /** State parameter for CSRF protection */\n readonly state: string;\n\n /** Session start timestamp */\n readonly startedAt: number;\n\n /** Session timeout in milliseconds */\n readonly timeoutMs: number;\n\n /** Check if session has expired */\n isExpired(): boolean;\n\n /** Get remaining time in milliseconds */\n remainingTime(): number;\n\n /** Validate returned state parameter */\n validateState(returnedState: string): boolean;\n}\n\n/**\n * Default session timeout in milliseconds (5 minutes).\n */\nexport const DEFAULT_SESSION_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Maximum allowed session timeout in milliseconds (1 hour).\n */\nexport const MAX_SESSION_TIMEOUT_MS = 60 * 60 * 1000;\n\n/**\n * Validate and normalize a timeout value.\n *\n * Ensures the timeout is a finite positive number within allowed bounds.\n * Returns the default timeout for invalid values (NaN, Infinity, negative, zero).\n *\n * @param timeoutMs - The timeout value to validate\n * @returns A valid timeout value within bounds\n */\nexport function validateTimeout(timeoutMs: number): number {\n // Check for NaN, Infinity, or non-finite values\n if (!Number.isFinite(timeoutMs)) {\n return DEFAULT_SESSION_TIMEOUT_MS;\n }\n\n // Check for non-positive values\n if (timeoutMs <= 0) {\n return DEFAULT_SESSION_TIMEOUT_MS;\n }\n\n // Clamp to maximum allowed\n if (timeoutMs > MAX_SESSION_TIMEOUT_MS) {\n return MAX_SESSION_TIMEOUT_MS;\n }\n\n // Round to integer (floor to avoid extending timeout)\n return Math.floor(timeoutMs);\n}\n\n/**\n * Represents an in-progress OAuth authorization flow.\n *\n * Implements the IAuthSession interface from the design document.\n * Tracks all PKCE and state parameters needed for a secure OAuth 2.1 flow.\n */\nexport class AuthSession implements IAuthSession {\n /** Unique session identifier */\n readonly sessionId: string;\n\n /** Provider being authenticated */\n readonly providerId: AuthProviderId;\n\n /** PKCE code verifier (kept secret) */\n readonly codeVerifier: string;\n\n /** PKCE code challenge (sent to provider) */\n readonly codeChallenge: string;\n\n /** State parameter for CSRF protection */\n readonly state: string;\n\n /** Session start timestamp (Unix milliseconds) */\n readonly startedAt: number;\n\n /** Session timeout in milliseconds */\n readonly timeoutMs: number;\n\n /**\n * Create a new auth session.\n *\n * @param providerId - The OAuth provider being authenticated\n * @param codeVerifier - PKCE code verifier (kept secret)\n * @param codeChallenge - PKCE code challenge (sent to provider)\n * @param state - State parameter for CSRF protection\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n */\n constructor(\n providerId: AuthProviderId,\n codeVerifier: string,\n codeChallenge: string,\n state: string,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n ) {\n this.sessionId = randomUUID();\n this.providerId = providerId;\n this.codeVerifier = codeVerifier;\n this.codeChallenge = codeChallenge;\n this.state = state;\n this.startedAt = Date.now();\n this.timeoutMs = validateTimeout(timeoutMs);\n }\n\n /**\n * Check if the session has expired.\n *\n * A session is expired if the current time exceeds startedAt + timeoutMs.\n *\n * @returns True if the session has expired, false otherwise\n */\n isExpired(): boolean {\n return this.remainingTime() <= 0;\n }\n\n /**\n * Get the remaining time until session expiration.\n *\n * Returns the number of milliseconds until the session expires.\n * Returns 0 if the session has already expired.\n *\n * @returns Remaining time in milliseconds (0 if expired)\n */\n remainingTime(): number {\n const elapsed = Date.now() - this.startedAt;\n const remaining = this.timeoutMs - elapsed;\n return Math.max(0, remaining);\n }\n\n /**\n * Validate a returned state parameter against this session's state.\n *\n * Uses constant-time comparison via the validateState function\n * to prevent timing attacks.\n *\n * @param returnedState - The state parameter from the OAuth callback\n * @returns True if the state matches, false otherwise\n */\n validateState(returnedState: string): boolean {\n return validateState(this.state, returnedState);\n }\n}\n\n/**\n * Factory function to create a new auth session.\n *\n * Generates PKCE parameters and state, then creates a new AuthSession.\n * This is a convenience function that handles all the cryptographic\n * parameter generation.\n *\n * @param providerId - The OAuth provider to authenticate with\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n * @returns A new AuthSession with generated PKCE and state parameters\n */\nexport function createSession(\n providerId: AuthProviderId,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n): AuthSession {\n const { verifier, challenge } = generatePKCEPair();\n const state = generateState();\n\n return new AuthSession(providerId, verifier, challenge, state, validateTimeout(timeoutMs));\n}\n\n/**\n * Session manager for tracking and cleaning up OAuth authorization sessions.\n *\n * Provides centralized management of active auth sessions including:\n * - Session storage and retrieval by session ID or state parameter\n * - Automatic cleanup of expired sessions\n * - Session lifecycle management (create, get, remove, list)\n *\n * The manager uses a configurable cleanup interval to periodically remove\n * expired sessions, preventing memory leaks in long-running processes.\n */\nexport class SessionManager {\n /** Map of session ID to AuthSession */\n private readonly sessions: Map<string, AuthSession> = new Map();\n\n /** Map of state parameter to session ID for quick lookup */\n private readonly stateToSessionId: Map<string, string> = new Map();\n\n /** Cleanup interval timer reference */\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n /** Default cleanup interval in milliseconds (1 minute) */\n static readonly DEFAULT_CLEANUP_INTERVAL_MS = 60 * 1000;\n\n /**\n * Create a new SessionManager.\n *\n * @param cleanupIntervalMs - Interval for automatic cleanup (default: 1 minute)\n * @param autoStartCleanup - Whether to start automatic cleanup immediately (default: true)\n */\n constructor(\n private readonly cleanupIntervalMs: number = SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,\n autoStartCleanup: boolean = true,\n ) {\n // Validate cleanup interval - use default for invalid values\n if (!Number.isFinite(this.cleanupIntervalMs) || this.cleanupIntervalMs <= 0) {\n // TypeScript doesn't allow reassigning readonly in constructor after initial assignment,\n // so we use Object.defineProperty to override\n Object.defineProperty(this, 'cleanupIntervalMs', {\n value: SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,\n writable: false,\n });\n }\n\n if (autoStartCleanup) {\n this.startCleanup();\n }\n }\n\n /**\n * Create and register a new auth session.\n *\n * Generates PKCE parameters and state, creates a new AuthSession,\n * and registers it with the manager for tracking.\n *\n * @param providerId - The OAuth provider to authenticate with\n * @param timeoutMs - Session timeout in milliseconds (default: 5 minutes)\n * @returns The newly created and registered AuthSession\n */\n create(\n providerId: AuthProviderId,\n timeoutMs: number = DEFAULT_SESSION_TIMEOUT_MS,\n ): AuthSession {\n const session = createSession(providerId, validateTimeout(timeoutMs));\n this.sessions.set(session.sessionId, session);\n this.stateToSessionId.set(session.state, session.sessionId);\n return session;\n }\n\n /**\n * Get a session by its session ID.\n *\n * @param sessionId - The unique session identifier\n * @returns The session if found and not expired, undefined otherwise\n */\n get(sessionId: string): AuthSession | undefined {\n const session = this.sessions.get(sessionId);\n if (session && session.isExpired()) {\n this.remove(sessionId);\n return undefined;\n }\n return session;\n }\n\n /**\n * Get a session by its state parameter.\n *\n * Useful for looking up sessions during OAuth callback handling.\n *\n * @param state - The state parameter from the OAuth callback\n * @returns The session if found and not expired, undefined otherwise\n */\n getByState(state: string): AuthSession | undefined {\n const sessionId = this.stateToSessionId.get(state);\n if (!sessionId) {\n return undefined;\n }\n return this.get(sessionId);\n }\n\n /**\n * Remove a session by its session ID.\n *\n * Cleans up both the session and its state parameter mapping.\n *\n * @param sessionId - The unique session identifier\n * @returns True if the session was removed, false if it didn't exist\n */\n remove(sessionId: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return false;\n }\n\n this.stateToSessionId.delete(session.state);\n this.sessions.delete(sessionId);\n return true;\n }\n\n /**\n * Remove a session by its state parameter.\n *\n * @param state - The state parameter\n * @returns True if the session was removed, false if it didn't exist\n */\n removeByState(state: string): boolean {\n const sessionId = this.stateToSessionId.get(state);\n if (!sessionId) {\n return false;\n }\n return this.remove(sessionId);\n }\n\n /**\n * List all active (non-expired) sessions.\n *\n * This method also performs cleanup of any expired sessions found.\n *\n * @returns Array of active AuthSession objects\n */\n list(): AuthSession[] {\n const activeSessions: AuthSession[] = [];\n const expiredSessionIds: string[] = [];\n\n for (const [sessionId, session] of this.sessions) {\n if (session.isExpired()) {\n expiredSessionIds.push(sessionId);\n } else {\n activeSessions.push(session);\n }\n }\n\n // Clean up expired sessions found during listing\n for (const sessionId of expiredSessionIds) {\n this.remove(sessionId);\n }\n\n return activeSessions;\n }\n\n /**\n * Get the count of active sessions.\n *\n * Note: This may include sessions that have expired but not yet been cleaned up.\n * Use list().length for an accurate count of non-expired sessions.\n *\n * @returns The number of tracked sessions\n */\n size(): number {\n return this.sessions.size;\n }\n\n /**\n * Check if a session exists by session ID.\n *\n * @param sessionId - The unique session identifier\n * @returns True if the session exists and is not expired\n */\n has(sessionId: string): boolean {\n return this.get(sessionId) !== undefined;\n }\n\n /**\n * Check if a session exists by state parameter.\n *\n * @param state - The state parameter\n * @returns True if a session with this state exists and is not expired\n */\n hasByState(state: string): boolean {\n return this.getByState(state) !== undefined;\n }\n\n /**\n * Remove all expired sessions.\n *\n * This is called automatically by the cleanup timer, but can also\n * be called manually to force immediate cleanup.\n *\n * @returns The number of expired sessions that were removed\n */\n cleanup(): number {\n const expiredSessionIds: string[] = [];\n\n for (const [sessionId, session] of this.sessions) {\n if (session.isExpired()) {\n expiredSessionIds.push(sessionId);\n }\n }\n\n for (const sessionId of expiredSessionIds) {\n this.remove(sessionId);\n }\n\n return expiredSessionIds.length;\n }\n\n /**\n * Start the automatic cleanup timer.\n *\n * If cleanup is already running, this method does nothing.\n */\n startCleanup(): void {\n if (this.cleanupTimer !== null) {\n return;\n }\n\n this.cleanupTimer = setInterval(() => {\n this.cleanup();\n }, this.cleanupIntervalMs);\n\n // Ensure the timer doesn't prevent Node.js from exiting\n if (this.cleanupTimer.unref) {\n this.cleanupTimer.unref();\n }\n }\n\n /**\n * Stop the automatic cleanup timer.\n *\n * Call this method when shutting down to clean up resources.\n */\n stopCleanup(): void {\n if (this.cleanupTimer !== null) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n /**\n * Clear all sessions and stop cleanup.\n *\n * Use this for cleanup during shutdown or testing.\n */\n clear(): void {\n this.stopCleanup();\n this.sessions.clear();\n this.stateToSessionId.clear();\n }\n\n /**\n * Check if automatic cleanup is running.\n *\n * @returns True if the cleanup timer is active\n */\n isCleanupRunning(): boolean {\n return this.cleanupTimer !== null;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * State parameter generation and validation for OAuth 2.1 CSRF protection.\n *\n * @module state\n */\n\nimport { randomBytes, timingSafeEqual } from 'crypto';\n\n/**\n * Minimum number of random bytes for state parameter.\n * 32 bytes provides 256 bits of entropy for CSRF protection.\n */\nexport const STATE_MIN_BYTES = 32;\n\n/**\n * Generate a cryptographically secure state parameter.\n *\n * Generates at least 32 bytes of cryptographic randomness using\n * Node.js crypto.randomBytes, then encodes as base64url without padding.\n *\n * @returns Base64url-encoded random bytes (at least 32 bytes)\n */\nexport function generateState(): string {\n // Generate 32 cryptographically random bytes\n const randomBuffer = randomBytes(STATE_MIN_BYTES);\n\n // Convert to base64url encoding without padding\n // base64url: replace + with -, / with _, remove = padding\n return randomBuffer\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Validate a returned state parameter against the expected value.\n *\n * Uses constant-time comparison to prevent timing attacks.\n * Returns false for missing, empty, or mismatched state parameters.\n * Never throws exceptions - always returns boolean.\n *\n * @param expected - The original state parameter\n * @param received - The state parameter from the callback\n * @returns True if the states match exactly, false otherwise\n */\nexport function validateState(expected: string | null | undefined, received: string | null | undefined): boolean {\n // Return false for missing or empty state parameters\n if (!expected || !received) {\n return false;\n }\n\n // Convert strings to Buffers first to compare byte lengths\n // This handles Unicode characters correctly\n const expectedBuffer = Buffer.from(expected, 'utf8');\n const receivedBuffer = Buffer.from(received, 'utf8');\n\n // Return false if byte lengths don't match\n // This is safe to do before timingSafeEqual since length comparison\n // doesn't leak information about the content\n if (expectedBuffer.length !== receivedBuffer.length) {\n return false;\n }\n\n // Use constant-time comparison to prevent timing attacks\n try {\n return timingSafeEqual(expectedBuffer, receivedBuffer);\n } catch {\n // Safety net: if timingSafeEqual throws for any reason, return false\n // This should never happen with the byte length check above, but\n // provides defense in depth\n return false;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * PKCE (Proof Key for Code Exchange) implementation.\n *\n * Provides code verifier and challenge generation for OAuth 2.1 PKCE flow.\n * Implements RFC 7636 with S256 challenge method as required by OAuth 2.1.\n *\n * @module pkce\n */\n\nimport { randomBytes, createHash } from 'crypto';\n\n/**\n * Minimum length for PKCE code verifier per RFC 7636.\n */\nexport const PKCE_VERIFIER_MIN_LENGTH = 43;\n\n/**\n * Maximum length for PKCE code verifier per RFC 7636.\n */\nexport const PKCE_VERIFIER_MAX_LENGTH = 128;\n\n/**\n * Default length for PKCE code verifier.\n * Using 64 characters provides good entropy while staying well within limits.\n */\nconst DEFAULT_VERIFIER_LENGTH = 64;\n\n/**\n * PKCE code challenge method.\n * OAuth 2.1 requires S256 (SHA-256) method.\n */\nexport const PKCE_CODE_CHALLENGE_METHOD = 'S256' as const;\n\n/**\n * Unreserved URI characters allowed in PKCE code verifier per RFC 7636.\n * Characters: A-Z, a-z, 0-9, hyphen (-), period (.), underscore (_), tilde (~)\n */\nconst UNRESERVED_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\n/**\n * Regex pattern for validating PKCE code verifier format.\n * Only unreserved URI characters are allowed per RFC 7636.\n */\nconst UNRESERVED_CHARS_REGEX = /^[A-Za-z0-9\\-._~]+$/;\n\n/**\n * Generate a cryptographically secure PKCE code verifier.\n *\n * The verifier is generated using crypto.randomBytes for cryptographic randomness,\n * then encoded using only unreserved URI characters as specified in RFC 7636.\n * Uses rejection sampling to avoid modulo bias.\n *\n * @param length - Optional length of the verifier (default: 64, must be 43-128)\n * @returns A random string between 43-128 characters using unreserved URI characters\n * @throws Error if length is outside the valid range (43-128) or not a valid integer\n */\nexport function generateCodeVerifier(length: number = DEFAULT_VERIFIER_LENGTH): string {\n // Validate length is a valid integer\n if (!Number.isInteger(length) || !Number.isFinite(length)) {\n throw new Error(\n `PKCE code verifier length must be a valid integer, got ${length}`,\n );\n }\n\n if (length < PKCE_VERIFIER_MIN_LENGTH || length > PKCE_VERIFIER_MAX_LENGTH) {\n throw new Error(\n `PKCE code verifier length must be between ${PKCE_VERIFIER_MIN_LENGTH} and ${PKCE_VERIFIER_MAX_LENGTH}, got ${length}`,\n );\n }\n\n const charsetLength = UNRESERVED_CHARS.length; // 66 characters\n\n // Calculate the largest multiple of charsetLength that fits in a byte (256)\n // This is used for rejection sampling to avoid modulo bias\n // For 66 chars: 256 - (256 % 66) = 256 - 58 = 198\n const maxValidByte = 256 - (256 % charsetLength);\n\n let verifier = '';\n let bytesNeeded = length;\n\n while (verifier.length < length) {\n // Generate more random bytes than needed to account for rejections\n // On average, we reject about 22.6% of bytes (58/256), so request ~30% extra\n const randomBuffer = randomBytes(Math.ceil(bytesNeeded * 1.4));\n\n for (let i = 0; i < randomBuffer.length && verifier.length < length; i++) {\n const byte = randomBuffer[i];\n\n // Rejection sampling: only use bytes that don't cause modulo bias\n if (byte < maxValidByte) {\n verifier += UNRESERVED_CHARS[byte % charsetLength];\n }\n }\n\n bytesNeeded = length - verifier.length;\n }\n\n return verifier;\n}\n\n/**\n * Validate a PKCE code verifier format.\n *\n * Checks that the verifier meets RFC 7636 requirements:\n * - Length between 43 and 128 characters\n * - Contains only unreserved URI characters\n *\n * @param verifier - The code verifier to validate\n * @returns True if the verifier is valid, false otherwise\n */\nexport function validateCodeVerifier(verifier: string): boolean {\n if (typeof verifier !== 'string') {\n return false;\n }\n\n if (verifier.length < PKCE_VERIFIER_MIN_LENGTH || verifier.length > PKCE_VERIFIER_MAX_LENGTH) {\n return false;\n }\n\n return UNRESERVED_CHARS_REGEX.test(verifier);\n}\n\n/**\n * Generate a PKCE code challenge from a code verifier.\n *\n * Computes the SHA-256 hash of the verifier and encodes it as base64url\n * without padding, as required by RFC 7636 S256 method.\n *\n * @param verifier - The code verifier to hash\n * @param strict - If true, validates verifier format (default: false for backward compatibility)\n * @returns Base64url-encoded SHA-256 hash of the verifier (without padding)\n * @throws Error if strict mode is enabled and verifier format is invalid\n */\nexport function generateCodeChallenge(verifier: string, strict: boolean = false): string {\n if (strict && !validateCodeVerifier(verifier)) {\n throw new Error(\n `Invalid PKCE code verifier format. Must be ${PKCE_VERIFIER_MIN_LENGTH}-${PKCE_VERIFIER_MAX_LENGTH} characters using only unreserved URI characters.`,\n );\n }\n\n // Compute SHA-256 hash of the verifier\n const hash = createHash('sha256').update(verifier, 'ascii').digest();\n\n // Convert to base64url encoding without padding\n // base64url: replace + with -, / with _, remove = padding\n return hash\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a PKCE pair (verifier and challenge).\n *\n * Creates a cryptographically secure code verifier and computes\n * the corresponding S256 code challenge.\n *\n * @param length - Optional length of the verifier (default: 64, must be 43-128)\n * @returns Object containing both the verifier and challenge\n */\nexport function generatePKCEPair(length?: number): { verifier: string; challenge: string } {\n const verifier = generateCodeVerifier(length);\n const challenge = generateCodeChallenge(verifier);\n\n return { verifier, challenge };\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Loopback HTTP server for OAuth callbacks.\n *\n * @module flows/callback-server\n */\n\nimport * as http from 'node:http';\nimport { URL } from 'node:url';\nimport type { CallbackResult } from '../types.js';\n\n/**\n * Loopback HTTP server for OAuth callbacks.\n */\nexport interface ICallbackServer {\n /** Start the server and return the redirect URI */\n start(): Promise<string>;\n\n /** Wait for the authorization callback */\n waitForCallback(timeoutMs: number): Promise<CallbackResult>;\n\n /** Stop the server and clean up resources */\n stop(): Promise<void>;\n\n /** Get the current server port (0 if not started) */\n getPort(): number;\n\n /** Check if server is running */\n isRunning(): boolean;\n}\n\n/**\n * Default callback path for OAuth redirects.\n */\nconst DEFAULT_CALLBACK_PATH = '/callback';\n\n/**\n * Loopback address for binding the server (IPv4).\n */\nconst LOOPBACK_HOST_IPV4 = '127.0.0.1';\n\n/**\n * Maximum URL length to prevent memory abuse (8KB).\n */\nconst MAX_URL_LENGTH = 8192;\n\n/**\n * Maximum header size to prevent memory abuse (8KB).\n */\nconst MAX_HEADER_SIZE = 8192;\n\n/**\n * Security response headers for browser-facing responses.\n */\nconst SECURITY_HEADERS: Record<string, string> = {\n 'Cache-Control': 'no-store',\n 'Pragma': 'no-cache',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Content-Security-Policy': \"default-src 'none'; style-src 'unsafe-inline'\",\n};\n\n/**\n * Check if an address is a loopback address.\n * Supports both IPv4 (127.x.x.x) and IPv6 (::1) loopback addresses.\n *\n * @param address - The IP address to check\n * @returns True if the address is a loopback address\n */\nexport function isLoopbackAddress(address: string | undefined): boolean {\n if (!address) {\n return false;\n }\n\n // IPv4 loopback: 127.0.0.0/8 (any address starting with 127.)\n if (address.startsWith('127.')) {\n return true;\n }\n\n // IPv6 loopback: ::1\n if (address === '::1') {\n return true;\n }\n\n // IPv4-mapped IPv6 loopback: ::ffff:127.x.x.x\n if (address.startsWith('::ffff:127.')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Validate the Host header against allowed loopback hosts.\n * Prevents DNS rebinding and host confusion attacks.\n *\n * @param hostHeader - The Host header value from the request\n * @param expectedPort - The expected port number\n * @returns True if the Host header is valid\n */\nexport function isValidHostHeader(hostHeader: string | undefined, expectedPort: number): boolean {\n if (!hostHeader) {\n return false;\n }\n\n // Allowed host patterns for loopback\n const allowedHosts = [\n `127.0.0.1:${expectedPort}`,\n `localhost:${expectedPort}`,\n `[::1]:${expectedPort}`,\n ];\n\n return allowedHosts.includes(hostHeader.toLowerCase());\n}\n\n/**\n * Check if a query parameter appears multiple times (potential injection).\n *\n * @param url - The parsed URL object\n * @param paramName - The parameter name to check\n * @returns True if the parameter appears more than once\n */\nfunction hasDuplicateParam(url: URL, paramName: string): boolean {\n return url.searchParams.getAll(paramName).length > 1;\n}\n\n\n/**\n * Callback server implementation.\n * Creates an HTTP server on a loopback address with dynamic port allocation\n * to receive OAuth authorization callbacks.\n *\n * @implements {ICallbackServer}\n */\nexport class CallbackServer implements ICallbackServer {\n private server: http.Server | null = null;\n private port = 0;\n private running = false;\n private callbackPath: string;\n private callbackPromise: Promise<CallbackResult> | null = null;\n private callbackResolve: ((result: CallbackResult) => void) | null = null;\n private callbackReject: ((error: Error) => void) | null = null;\n private timeoutId: NodeJS.Timeout | null = null;\n private callbackHandled = false; // One-shot guard to prevent multiple callbacks\n\n /** Minimum timeout in milliseconds (1 second) */\n private static readonly MIN_TIMEOUT_MS = 1000;\n /** Maximum timeout in milliseconds (10 minutes) */\n private static readonly MAX_TIMEOUT_MS = 600000;\n\n /**\n * Creates a new CallbackServer instance.\n * @param callbackPath - The path to listen for callbacks (default: '/callback')\n */\n constructor(callbackPath: string = DEFAULT_CALLBACK_PATH) {\n this.callbackPath = callbackPath;\n }\n\n /**\n * Start the server and return the redirect URI.\n * The server binds to a loopback address (127.0.0.1) with a dynamically allocated port.\n *\n * @returns The redirect URI to use for OAuth callbacks\n * @throws Error if the server is already running or fails to start\n */\n async start(): Promise<string> {\n if (this.running) {\n throw new Error('Callback server is already running');\n }\n\n return new Promise<string>((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n // Handle server errors\n this.server.on('error', (error) => {\n this.running = false;\n reject(new Error(`Failed to start callback server: ${error.message}`));\n });\n\n // Bind to loopback address with dynamic port (port 0)\n this.server.listen(0, LOOPBACK_HOST_IPV4, () => {\n const address = this.server?.address();\n if (address && typeof address === 'object') {\n this.port = address.port;\n this.running = true;\n const redirectUri = `http://${LOOPBACK_HOST_IPV4}:${this.port}${this.callbackPath}`;\n resolve(redirectUri);\n } else {\n reject(new Error('Failed to get server address'));\n }\n });\n });\n }\n\n /**\n * Wait for the authorization callback.\n * Returns when a callback is received or the timeout is reached.\n *\n * @param timeoutMs - Maximum time to wait for the callback in milliseconds\n * @returns The callback result containing the authorization code and state\n * @throws Error if the server is not running, timeout is reached, or callback fails\n */\n async waitForCallback(timeoutMs: number): Promise<CallbackResult> {\n if (!this.running) {\n throw new Error('Callback server is not running');\n }\n\n if (this.callbackPromise) {\n throw new Error('Already waiting for callback');\n }\n\n // Validate timeout parameter\n if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs)) {\n throw new Error('Timeout must be a finite number');\n }\n if (timeoutMs < CallbackServer.MIN_TIMEOUT_MS) {\n throw new Error(`Timeout must be at least ${CallbackServer.MIN_TIMEOUT_MS}ms`);\n }\n if (timeoutMs > CallbackServer.MAX_TIMEOUT_MS) {\n throw new Error(`Timeout must not exceed ${CallbackServer.MAX_TIMEOUT_MS}ms`);\n }\n\n // Reset one-shot guard for new wait\n this.callbackHandled = false;\n\n this.callbackPromise = new Promise<CallbackResult>((resolve, reject) => {\n this.callbackResolve = resolve;\n this.callbackReject = reject;\n\n // Set up timeout\n this.timeoutId = setTimeout(() => {\n this.callbackReject?.(new Error('Callback timeout exceeded'));\n this.cleanup();\n }, timeoutMs);\n });\n\n try {\n return await this.callbackPromise;\n } finally {\n this.callbackPromise = null;\n }\n }\n\n /**\n * Stop the server and clean up resources.\n */\n async stop(): Promise<void> {\n this.cleanup();\n\n if (this.server) {\n return new Promise<void>((resolve, reject) => {\n this.server?.close((error) => {\n // Ignore \"Server is not running\" errors if already closed by stopAcceptingConnections\n if (error && !error.message.includes('Server is not running')) {\n reject(new Error(`Failed to stop callback server: ${error.message}`));\n } else {\n this.server = null;\n this.port = 0;\n this.running = false;\n resolve();\n }\n });\n });\n }\n }\n\n /**\n * Get the current server port.\n * @returns The port number, or 0 if the server is not started\n */\n getPort(): number {\n return this.port;\n }\n\n /**\n * Check if the server is running.\n * @returns True if the server is running\n */\n isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Handle incoming HTTP requests.\n * Parses the callback URL and extracts the authorization code and state.\n * Rejects connections from non-loopback addresses for security.\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n // Security: Only accept GET requests (Requirement 8.2)\n if (req.method !== 'GET') {\n this.sendErrorResponse(res, 405, 'Method Not Allowed', 'Only GET requests are accepted');\n return;\n }\n\n // Security: Reject connections from non-loopback addresses (Requirement 8.2)\n const remoteAddress = req.socket.remoteAddress;\n if (!isLoopbackAddress(remoteAddress)) {\n this.sendErrorResponse(res, 403, 'Forbidden', 'Only loopback connections are allowed');\n return;\n }\n\n // Security: Validate Host header to prevent DNS rebinding attacks (Requirement 8.2)\n const hostHeader = req.headers.host;\n if (!isValidHostHeader(hostHeader, this.port)) {\n this.sendErrorResponse(res, 400, 'Bad Request', 'Invalid Host header');\n return;\n }\n\n // Security: Enforce max URL length to prevent memory abuse\n const rawUrl = req.url || '/';\n if (rawUrl.length > MAX_URL_LENGTH) {\n this.sendErrorResponse(res, 414, 'URI Too Long', 'Request URL exceeds maximum length');\n return;\n }\n\n // Security: Check total header size\n const headerSize = Object.entries(req.headers).reduce(\n (sum, [key, value]) => sum + key.length + (Array.isArray(value) ? value.join('').length : (value?.length || 0)),\n 0\n );\n if (headerSize > MAX_HEADER_SIZE) {\n this.sendErrorResponse(res, 431, 'Request Header Fields Too Large', 'Headers exceed maximum size');\n return;\n }\n\n let url: URL;\n try {\n url = new URL(rawUrl, `http://${LOOPBACK_HOST_IPV4}:${this.port}`);\n } catch {\n this.sendErrorResponse(res, 400, 'Bad Request', 'Invalid URL format');\n return;\n }\n\n // Only handle requests to the callback path\n if (url.pathname !== this.callbackPath) {\n this.sendErrorResponse(res, 404, 'Not Found', 'Invalid callback path');\n return;\n }\n\n // One-shot guard: reject duplicate callbacks (Requirement 8.3)\n if (this.callbackHandled) {\n this.sendErrorResponse(res, 409, 'Conflict', 'Callback already processed');\n return;\n }\n\n // Security: Reject duplicate query parameters (potential injection)\n const sensitiveParams = ['code', 'state', 'error', 'error_description'];\n for (const param of sensitiveParams) {\n if (hasDuplicateParam(url, param)) {\n this.sendErrorResponse(res, 400, 'Bad Request', `Duplicate parameter: ${param}`);\n return;\n }\n }\n\n // Mark callback as handled atomically before any processing\n this.callbackHandled = true;\n\n // Parse query parameters\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Build callback result based on whether it's success or error\n let result: CallbackResult;\n\n try {\n // Send response to browser\n if (error) {\n // OAuth error response - state is optional in error responses\n result = {\n success: false,\n error: error,\n errorDescription: errorDescription || undefined,\n state: state || undefined,\n };\n this.sendHtmlResponse(res, 400, this.buildErrorPage(error, errorDescription));\n } else if (code && state) {\n // Successful authorization - both code and state are required\n result = {\n success: true,\n code: code,\n state: state,\n };\n this.sendHtmlResponse(res, 200, this.buildSuccessPage());\n } else {\n // Missing required parameters\n result = {\n success: false,\n error: 'missing_params',\n errorDescription: 'Missing code or state parameter',\n state: state || undefined,\n };\n this.sendHtmlResponse(res, 400, this.buildErrorPage('missing_params', 'Missing code or state parameter'));\n }\n\n // Resolve the callback promise and stop accepting new connections (Requirement 8.3)\n if (this.callbackResolve) {\n this.callbackResolve(result);\n }\n } finally {\n // Always cleanup and stop accepting connections, even on exceptions\n this.cleanup();\n this.stopAcceptingConnections();\n }\n }\n\n /**\n * Send an error response with security headers.\n */\n private sendErrorResponse(res: http.ServerResponse, statusCode: number, _statusMessage: string, body: string): void {\n res.writeHead(statusCode, {\n 'Content-Type': 'text/plain',\n ...SECURITY_HEADERS,\n });\n res.end(body);\n }\n\n /**\n * Send an HTML response with security headers.\n */\n private sendHtmlResponse(res: http.ServerResponse, statusCode: number, html: string): void {\n res.writeHead(statusCode, {\n 'Content-Type': 'text/html; charset=utf-8',\n ...SECURITY_HEADERS,\n });\n res.end(html);\n }\n\n /**\n * Stop accepting new connections without fully closing the server.\n * This ensures the server stops accepting new requests after processing the callback\n * while allowing the current response to complete.\n * \n * @remarks\n * This implements Requirement 8.3: The Callback_Server SHALL immediately close\n * after processing the single expected request.\n */\n private stopAcceptingConnections(): void {\n if (this.server) {\n // Close the server to stop accepting new connections\n // This allows existing connections to finish but rejects new ones\n this.server.close();\n this.running = false;\n }\n }\n\n /**\n * Clean up timeout and callback state.\n */\n private cleanup(): void {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId);\n this.timeoutId = null;\n }\n this.callbackResolve = null;\n this.callbackReject = null;\n }\n\n /**\n * Build a success HTML page to display in the browser.\n */\n private buildSuccessPage(): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <title>Authorization Successful</title>\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; \n display: flex; justify-content: center; align-items: center; height: 100vh; \n margin: 0; background: #f5f5f5; }\n .container { text-align: center; padding: 40px; background: white; \n border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }\n h1 { color: #22c55e; margin-bottom: 16px; }\n p { color: #666; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>\u2713 Authorization Successful</h1>\n <p>You can close this window and return to the application.</p>\n </div>\n</body>\n</html>`;\n }\n\n /**\n * Build an error HTML page to display in the browser.\n */\n private buildErrorPage(error: string, description?: string | null): string {\n const safeError = this.escapeHtml(error);\n const safeDescription = description ? this.escapeHtml(description) : 'An error occurred during authorization.';\n\n return `<!DOCTYPE html>\n<html>\n<head>\n <title>Authorization Failed</title>\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; \n display: flex; justify-content: center; align-items: center; height: 100vh; \n margin: 0; background: #f5f5f5; }\n .container { text-align: center; padding: 40px; background: white; \n border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }\n h1 { color: #ef4444; margin-bottom: 16px; }\n p { color: #666; }\n .error-code { font-family: monospace; background: #f5f5f5; padding: 4px 8px; \n border-radius: 4px; color: #333; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>\u2717 Authorization Failed</h1>\n <p>${safeDescription}</p>\n <p>Error code: <span class=\"error-code\">${safeError}</span></p>\n </div>\n</body>\n</html>`;\n }\n\n /**\n * Escape HTML special characters to prevent XSS.\n */\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Orchestrates the complete agent authentication flow:\n * 1. Generate PKCE code verifier and challenge\n * 2. Generate state parameter for CSRF protection\n * 3. Start the callback server on loopback address\n * 4. Build the authorization URL with all required parameters\n * 5. Launch the system default browser to the authorization URL\n * 6. Wait for the callback with the authorization code\n * 7. Validate the state parameter\n * 8. Exchange the authorization code for tokens\n * 9. Return the authentication result\n *\n * Requirements: 3.1, 3.2, 3.3, 3.4\n *\n * @module flows/agent-auth-flow\n */\n\nimport type {\n AuthProviderId,\n AuthResult,\n AgentAuthOptions,\n AuthorizationParams,\n TokenResponse,\n} from '../types.js';\nimport { createSession, DEFAULT_SESSION_TIMEOUT_MS } from '../session.js';\nimport type { AuthSession } from '../session.js';\nimport { CallbackServer } from './callback-server.js';\nimport type { IAuthProvider } from '../providers/types.js';\n\n/**\n * Default timeout for the agent auth flow in milliseconds (5 minutes).\n */\nexport const DEFAULT_AUTH_TIMEOUT_MS = DEFAULT_SESSION_TIMEOUT_MS;\n\n/**\n * List of environment variables that indicate a CI/headless environment.\n * These are commonly set by CI systems and indicate that browser-based\n * authentication should not be attempted.\n */\nconst CI_ENVIRONMENT_VARIABLES = [\n 'CI', // Generic CI indicator (GitHub Actions, GitLab CI, etc.)\n 'CONTINUOUS_INTEGRATION', // Generic CI indicator\n 'GITHUB_ACTIONS', // GitHub Actions\n 'GITLAB_CI', // GitLab CI\n 'JENKINS', // Jenkins\n 'JENKINS_URL', // Jenkins (alternative)\n 'TRAVIS', // Travis CI\n 'CIRCLECI', // CircleCI\n 'BUILDKITE', // Buildkite\n 'DRONE', // Drone CI\n 'TEAMCITY_VERSION', // TeamCity\n 'TF_BUILD', // Azure Pipelines\n 'CODEBUILD_BUILD_ID', // AWS CodeBuild\n 'BITBUCKET_BUILD_NUMBER', // Bitbucket Pipelines\n 'HEROKU_TEST_RUN_ID', // Heroku CI\n 'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI', // Azure DevOps\n] as const;\n\n/**\n * Detect if the current environment is headless (no display/browser available).\n *\n * A headless environment is detected when any of the following conditions are true:\n * 1. CI environment variables are set (CI, GITHUB_ACTIONS, GITLAB_CI, JENKINS, etc.)\n * 2. HEADLESS environment variable is set to a truthy value\n * 3. SSH_TTY environment variable is set (indicates SSH session without display)\n * 4. stdout or stderr are not TTY (indicates non-interactive environment)\n *\n * This detection is used to prevent browser launch in environments where\n * it would fail or be inappropriate (CI pipelines, SSH sessions, etc.).\n *\n * Requirements: 3.1, 4.1\n *\n * @returns true if running in a headless environment, false otherwise\n */\nexport function isHeadlessEnvironment(): boolean {\n // Check for CI environment variables\n for (const envVar of CI_ENVIRONMENT_VARIABLES) {\n const value = process.env[envVar];\n if (value !== undefined && value !== '' && value !== '0' && value.toLowerCase() !== 'false') {\n return true;\n }\n }\n\n // Check for explicit HEADLESS environment variable\n const headlessEnv = process.env['HEADLESS'];\n if (headlessEnv !== undefined && headlessEnv !== '' && headlessEnv !== '0' && headlessEnv.toLowerCase() !== 'false') {\n return true;\n }\n\n // Check for SSH_TTY (indicates SSH session without display)\n if (process.env['SSH_TTY'] !== undefined && process.env['SSH_TTY'] !== '') {\n return true;\n }\n\n // Check if stdout or stderr are not TTY (non-interactive environment)\n // Note: In a headless environment, typically neither stdout nor stderr is a TTY\n if (!process.stdout.isTTY || !process.stderr.isTTY) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Dependencies for the agent auth flow.\n */\nexport interface AgentAuthFlowDependencies {\n /** Function to get a provider by ID */\n getProvider: (providerId: AuthProviderId) => IAuthProvider;\n /** Function to store tokens after successful authentication */\n storeTokens: (providerId: AuthProviderId, tokens: TokenResponse) => Promise<void>;\n /** Optional custom browser launcher (for testing) */\n launchBrowser?: (url: string) => Promise<void>;\n}\n\n/**\n * Agent auth flow - browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * This class orchestrates the complete OAuth 2.1 agent authentication flow,\n * handling PKCE generation, browser launch, callback handling, and token exchange.\n */\nexport class AgentAuthFlow {\n private readonly getProvider: (providerId: AuthProviderId) => IAuthProvider;\n private readonly storeTokens: (providerId: AuthProviderId, tokens: TokenResponse) => Promise<void>;\n private readonly launchBrowser: (url: string) => Promise<void>;\n\n /**\n * Create a new agent auth flow.\n *\n * @param dependencies - Flow dependencies including provider resolver and token storage\n */\n constructor(dependencies: AgentAuthFlowDependencies) {\n this.getProvider = dependencies.getProvider;\n this.storeTokens = dependencies.storeTokens;\n this.launchBrowser = dependencies.launchBrowser ?? openSystemBrowser;\n }\n\n /**\n * Execute the agent auth flow.\n *\n * Performs the complete OAuth 2.1 Authorization Code flow with PKCE:\n * 1. Checks for headless environment (fails fast if detected)\n * 2. Creates an auth session with PKCE and state parameters\n * 3. Starts a callback server on loopback address\n * 4. Builds and opens the authorization URL in the system browser\n * 5. Waits for the OAuth callback\n * 6. Validates the state parameter\n * 7. Exchanges the authorization code for tokens\n * 8. Stores the tokens and returns the result\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n async execute(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n // Step 0: Check for headless environment FIRST (fail-fast)\n // Requirements: 3.1, 13.2\n if (isHeadlessEnvironment()) {\n console.error(`[AgentAuthFlow] Headless environment detected, cannot launch browser for ${providerId}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'HEADLESS_ENVIRONMENT',\n message: 'Browser OAuth not available in headless environment',\n details: {\n suggestion: 'Use --setup for manual credential configuration',\n },\n },\n };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_AUTH_TIMEOUT_MS;\n let callbackServer: CallbackServer | null = null;\n let session: AuthSession | null = null;\n\n try {\n // Step 1: Get the provider\n const provider = this.getProvider(providerId);\n\n // Step 1.5: Validate provider configuration (Requirement 7.1, 7.5)\n const clientId = options?.clientId ?? this.getDefaultClientId(providerId);\n const configValidation = this.validateProviderConfig(providerId, provider, clientId);\n if (!configValidation.valid) {\n console.error(`[AgentAuthFlow] Provider configuration invalid: ${configValidation.error}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: configValidation.error!,\n details: configValidation.details,\n },\n };\n }\n\n // Step 2: Create auth session with PKCE and state parameters\n // (Requirement 3.1: Initiate OAuth 2.1 Authorization Code flow with PKCE)\n session = createSession(providerId, timeoutMs);\n console.error(`[AgentAuthFlow] Created auth session ${session.sessionId} for ${providerId}`);\n\n // Step 3: Start the callback server on loopback address\n // (Requirement 3.3: Start Callback_Server on loopback address)\n callbackServer = new CallbackServer();\n const redirectUri = await callbackServer.start();\n console.error(`[AgentAuthFlow] Callback server started at ${redirectUri}`);\n\n // Step 4: Build the authorization URL with all required parameters\n // (Requirement 3.2: Open browser with required OAuth parameters)\n const scopes = options?.scopes ?? [...provider.defaultScopes];\n\n const authParams: AuthorizationParams = {\n clientId,\n redirectUri,\n scope: scopes.join(' '),\n state: session.state,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: 'S256',\n responseType: 'code',\n };\n\n const authorizationUrl = provider.buildAuthorizationUrl(authParams);\n console.error(`[AgentAuthFlow] Authorization URL built for ${providerId}`);\n\n // Step 5: Launch the system default browser to the authorization URL\n // (Requirement 3.2: Open system default browser)\n await this.launchBrowser(authorizationUrl);\n console.error(`[AgentAuthFlow] Browser launched for ${providerId} authentication`);\n\n // Step 6: Wait for the callback with the authorization code\n // (Requirement 3.4: Receive authorization code via callback)\n const callbackResult = await callbackServer.waitForCallback(session.remainingTime());\n\n // Check for OAuth error in callback (discriminated union check)\n if (!callbackResult.success) {\n console.error(`[AgentAuthFlow] OAuth error: ${callbackResult.error} - ${callbackResult.errorDescription}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: callbackResult.errorDescription || callbackResult.error,\n details: {\n oauthError: callbackResult.error,\n oauthErrorDescription: callbackResult.errorDescription,\n },\n },\n };\n }\n\n // Step 7: Validate the state parameter\n // (Requirement 2.2, 2.3: Validate state parameter)\n if (!session.validateState(callbackResult.state)) {\n console.error(`[AgentAuthFlow] State validation failed for ${providerId}`);\n return {\n success: false,\n providerId,\n error: {\n code: 'INVALID_STATE',\n message: 'State parameter validation failed. The authorization response may have been tampered with.',\n },\n };\n }\n\n // Step 8: Exchange the authorization code for tokens\n // (Requirement 3.4: Exchange code for tokens with code verifier)\n // Note: callbackResult.code is guaranteed to exist here due to discriminated union\n console.error(`[AgentAuthFlow] Exchanging authorization code for tokens`);\n const tokenResponse = await provider.exchangeCode(\n callbackResult.code,\n session.codeVerifier,\n redirectUri\n );\n\n // Step 9: Store the tokens\n await this.storeTokens(providerId, tokenResponse);\n console.error(`[AgentAuthFlow] Tokens stored successfully for ${providerId}`);\n\n return {\n success: true,\n providerId,\n };\n } catch (error) {\n console.error(`[AgentAuthFlow] Authentication failed for ${providerId}: ${error}`);\n\n // Determine error type\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Check for timeout\n if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {\n return {\n success: false,\n providerId,\n error: {\n code: 'TIMEOUT',\n message: 'Authentication flow timed out. Please try again.',\n },\n };\n }\n\n // Check for network errors\n if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND') || errorMessage.includes('fetch')) {\n return {\n success: false,\n providerId,\n error: {\n code: 'NETWORK_ERROR',\n message: `Network error during authentication: ${errorMessage}`,\n },\n };\n }\n\n // Generic error\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: errorMessage,\n },\n };\n } finally {\n // Clean up: Stop the callback server\n if (callbackServer) {\n try {\n await callbackServer.stop();\n console.error(`[AgentAuthFlow] Callback server stopped`);\n } catch (stopError) {\n console.error(`[AgentAuthFlow] Error stopping callback server: ${stopError}`);\n }\n }\n }\n }\n\n /**\n * Validate provider configuration before starting OAuth flow.\n *\n * Checks:\n * - Client ID is present and valid\n * - Provider endpoints are HTTPS (via provider.validateConfig())\n * - Provider supports PKCE with S256\n *\n * @param providerId - The provider identifier\n * @param provider - The provider instance\n * @param clientId - The client ID to validate\n * @returns Validation result with error details if invalid\n */\n private validateProviderConfig(\n providerId: AuthProviderId,\n provider: IAuthProvider,\n clientId: string\n ): { valid: boolean; error?: string; details?: Record<string, unknown> } {\n // Validate client ID\n if (!clientId || typeof clientId !== 'string') {\n return {\n valid: false,\n error: `Client ID is required for ${providerId}. Set OAUTH_${providerId.toUpperCase()}_CLIENT_ID environment variable.`,\n details: { providerId, suggestion: 'Configure client_id via environment variable or --setup' },\n };\n }\n\n const trimmedClientId = clientId.trim();\n if (trimmedClientId.length === 0) {\n return {\n valid: false,\n error: `Client ID cannot be empty for ${providerId}.`,\n details: { providerId },\n };\n }\n\n // Check for control characters in client ID\n if (containsControlCharacters(trimmedClientId)) {\n return {\n valid: false,\n error: `Client ID contains invalid characters for ${providerId}.`,\n details: { providerId },\n };\n }\n\n // Validate provider configuration (HTTPS endpoints, etc.)\n try {\n provider.validateConfig();\n } catch (error) {\n return {\n valid: false,\n error: `Provider configuration invalid for ${providerId}: ${(error as Error).message}`,\n details: { providerId },\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Get the default client ID for a provider.\n *\n * This is a placeholder that should be overridden with actual client IDs\n * from configuration or environment variables.\n *\n * @param providerId - The provider identifier\n * @returns The default client ID for the provider\n */\n private getDefaultClientId(providerId: AuthProviderId): string {\n // In a real implementation, these would come from configuration\n // For now, return a placeholder that indicates configuration is needed\n const envKey = `OAUTH_${providerId.toUpperCase()}_CLIENT_ID`;\n const clientId = process.env[envKey];\n\n if (!clientId) {\n throw new Error(\n `No client ID configured for ${providerId}. ` +\n `Set the ${envKey} environment variable or provide clientId in options.`\n );\n }\n\n return clientId;\n }\n}\n\n/**\n * Sensitive URL parameters that should be redacted in logs.\n * These parameters may contain security-sensitive values.\n */\nconst SENSITIVE_URL_PARAMS = [\n 'state',\n 'code_challenge',\n 'code',\n 'access_token',\n 'refresh_token',\n 'id_token',\n 'client_secret',\n] as const;\n\n/**\n * Redact sensitive parameters from a URL for safe logging.\n *\n * @param url - The URL to redact\n * @returns URL string with sensitive parameters replaced with [REDACTED]\n */\nexport function redactUrlForLogging(url: string): string {\n try {\n const parsedUrl = new URL(url);\n for (const param of SENSITIVE_URL_PARAMS) {\n if (parsedUrl.searchParams.has(param)) {\n parsedUrl.searchParams.set(param, '[REDACTED]');\n }\n }\n return parsedUrl.toString();\n } catch {\n // If URL parsing fails, return a generic redacted message\n return '[INVALID URL - REDACTED]';\n }\n}\n\n/**\n * Check if a string contains control characters (ASCII 0-31 except tab, newline, carriage return).\n *\n * @param str - The string to check\n * @returns true if control characters are found, false otherwise\n */\nfunction containsControlCharacters(str: string): boolean {\n // Match control characters except tab (0x09), newline (0x0A), carriage return (0x0D)\n // eslint-disable-next-line no-control-regex\n const controlCharRegex = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/;\n return controlCharRegex.test(str);\n}\n\n/**\n * Open a URL in the system default browser.\n *\n * Uses platform-specific commands to launch the browser:\n * - macOS: `open`\n * - Windows: `start`\n * - Linux: `xdg-open`\n *\n * Security measures:\n * - Uses execFile with argument arrays to prevent command injection\n * - Validates URL is HTTPS only (OAuth authorization URLs must use HTTPS)\n * - Disallows userinfo (username:password) in URL\n * - Validates no control characters in URL (checked before URL parsing)\n * - Logs redacted URL for security\n *\n * Requirements: 3.6, 8.1\n *\n * @param url - The URL to open\n * @throws Error if the browser cannot be launched or URL is invalid\n */\nexport async function openSystemBrowser(url: string): Promise<void> {\n // Security: Check for control characters in the original URL string BEFORE parsing\n // This catches attempts to inject control characters that might be normalized by URL class\n if (containsControlCharacters(url)) {\n throw new Error('URL contains invalid control characters');\n }\n\n // Validate URL to prevent command injection and ensure it's a valid HTTPS URL\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new Error('Invalid URL format');\n }\n\n // Security: Only allow HTTPS protocol for OAuth authorization URLs\n // This is a security requirement for OAuth 2.1 - authorization endpoints must use HTTPS\n // Requirements: 3.6, 8.1\n if (parsedUrl.protocol !== 'https:') {\n throw new Error('OAuth authorization URL must use HTTPS');\n }\n\n // Security: Disallow userinfo (username:password) in URL\n // URLs like https://user:pass@auth.example.com/ are suspicious and should be rejected\n if (parsedUrl.username !== '' || parsedUrl.password !== '') {\n throw new Error('URL must not contain credentials');\n }\n\n // Use the parsed URL's toString() for the final URL (properly encoded)\n const finalUrl = parsedUrl.toString();\n\n // Log redacted URL for debugging (security: sensitive params are redacted)\n const redactedUrl = redactUrlForLogging(finalUrl);\n console.error(`[openSystemBrowser] Opening browser: ${redactedUrl}`);\n\n const platform = process.platform;\n\n // Use execFile with argument arrays to prevent shell injection\n // This is safer than exec() with string interpolation\n const { execFile } = await import('child_process');\n const { promisify } = await import('util');\n const execFileAsync = promisify(execFile);\n\n try {\n switch (platform) {\n case 'darwin':\n // macOS: open command with URL as argument\n await execFileAsync('open', [finalUrl]);\n break;\n case 'win32':\n // Windows: use cmd.exe /c start with proper escaping\n // Note: Windows requires special handling for URLs with special chars\n await execFileAsync('cmd.exe', ['/c', 'start', '', finalUrl]);\n break;\n default:\n // Linux and others: xdg-open with URL as argument\n await execFileAsync('xdg-open', [finalUrl]);\n break;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to open browser: ${errorMessage}`);\n }\n}\n\n/**\n * Create an agent auth flow with the given dependencies.\n *\n * @param dependencies - Flow dependencies\n * @returns A new AgentAuthFlow instance\n */\nexport function createAgentAuthFlow(dependencies: AgentAuthFlowDependencies): AgentAuthFlow {\n return new AgentAuthFlow(dependencies);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * OS keychain storage backend.\n *\n * Uses macOS Keychain, Windows Credential Manager, or Linux Secret Service\n * via the keytar package for secure credential storage.\n *\n * @module storage/keychain-backend\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport { isValidProviderId } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/** Service name for keychain entries */\nconst SERVICE_NAME = 'stdio-bus-registry-launcher';\n\n/** Account prefix for provider entries */\nconst ACCOUNT_PREFIX = 'oauth-';\n\n/** Special account for tracking stored providers */\nconst PROVIDERS_LIST_ACCOUNT = '__providers_list__';\n\n/**\n * Keytar module interface (subset of keytar API we use).\n */\nexport interface KeytarModule {\n setPassword(service: string, account: string, password: string): Promise<void>;\n getPassword(service: string, account: string): Promise<string | null>;\n deletePassword(service: string, account: string): Promise<boolean>;\n findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;\n}\n\n/**\n * Keychain storage backend implementation.\n *\n * Uses the system keychain for secure credential storage:\n * - macOS: Keychain Access\n * - Windows: Credential Manager\n * - Linux: Secret Service (libsecret)\n *\n * Handles keychain unavailability gracefully by returning false from isAvailable().\n */\nexport class KeychainBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'keychain';\n\n private keytar: KeytarModule | null = null;\n private keytarLoadAttempted = false;\n private keytarLoadError: Error | null = null;\n\n /**\n * Lazily load keytar module.\n * Uses dynamic import to handle cases where keytar is not installed.\n */\n private async loadKeytar(): Promise<KeytarModule | null> {\n if (this.keytarLoadAttempted) {\n return this.keytar;\n }\n\n this.keytarLoadAttempted = true;\n\n try {\n // Dynamic import to handle missing keytar gracefully\n // Use a variable to prevent TypeScript from resolving the module at compile time\n const moduleName = 'keytar';\n const keytarModule = await import(/* webpackIgnore: true */ moduleName) as { default?: KeytarModule } & KeytarModule;\n this.keytar = keytarModule.default || keytarModule;\n return this.keytar;\n } catch (error) {\n this.keytarLoadError = error instanceof Error ? error : new Error(String(error));\n console.error(`[KeychainBackend] Failed to load keytar: ${this.keytarLoadError.message}`);\n return null;\n }\n }\n\n /**\n * Get account name for a provider.\n */\n private getAccountName(providerId: AuthProviderId): string {\n return `${ACCOUNT_PREFIX}${providerId}`;\n }\n\n /**\n * Check if the keychain backend is available on this system.\n * Returns false if keytar is not installed or keychain access fails.\n */\n async isAvailable(): Promise<boolean> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return false;\n }\n\n try {\n // Test keychain access by attempting to read a non-existent entry\n await keytar.getPassword(SERVICE_NAME, '__availability_check__');\n return true;\n } catch (error) {\n console.error(`[KeychainBackend] Keychain access check failed: ${error}`);\n return false;\n }\n }\n\n /**\n * Store credentials for a provider in the system keychain.\n */\n async store(providerId: AuthProviderId, credentials: StoredCredentials): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n throw new Error('Keychain backend is not available');\n }\n\n const account = this.getAccountName(providerId);\n const serialized = JSON.stringify(credentials);\n\n try {\n await keytar.setPassword(SERVICE_NAME, account, serialized);\n // Update providers list\n await this.addToProvidersList(providerId);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to store credentials in keychain: ${message}`);\n }\n }\n\n /**\n * Retrieve credentials for a provider from the system keychain.\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return null;\n }\n\n const account = this.getAccountName(providerId);\n\n try {\n const serialized = await keytar.getPassword(SERVICE_NAME, account);\n if (!serialized) {\n return null;\n }\n\n const credentials = JSON.parse(serialized) as StoredCredentials;\n return credentials;\n } catch (error) {\n console.error(`[KeychainBackend] Failed to retrieve credentials: ${error}`);\n return null;\n }\n }\n\n /**\n * Delete credentials for a provider from the system keychain.\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return;\n }\n\n const account = this.getAccountName(providerId);\n\n try {\n await keytar.deletePassword(SERVICE_NAME, account);\n // Update providers list\n await this.removeFromProvidersList(providerId);\n } catch (error) {\n // Ignore errors when deleting (entry might not exist)\n console.error(`[KeychainBackend] Failed to delete credentials: ${error}`);\n }\n }\n\n /**\n * Delete all stored credentials from the system keychain.\n */\n async deleteAll(): Promise<void> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return;\n }\n\n try {\n const credentials = await keytar.findCredentials(SERVICE_NAME);\n for (const cred of credentials) {\n await keytar.deletePassword(SERVICE_NAME, cred.account);\n }\n } catch (error) {\n console.error(`[KeychainBackend] Failed to delete all credentials: ${error}`);\n }\n }\n\n /**\n * List all providers with stored credentials.\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const keytar = await this.loadKeytar();\n if (!keytar) {\n return [];\n }\n\n try {\n const credentials = await keytar.findCredentials(SERVICE_NAME);\n const providers: AuthProviderId[] = [];\n\n for (const cred of credentials) {\n if (cred.account.startsWith(ACCOUNT_PREFIX)) {\n const candidateId = cred.account.slice(ACCOUNT_PREFIX.length);\n // Validate that the extracted ID is a valid provider ID\n if (isValidProviderId(candidateId)) {\n providers.push(candidateId);\n }\n }\n }\n\n return providers;\n } catch (error) {\n console.error(`[KeychainBackend] Failed to list providers: ${error}`);\n return [];\n }\n }\n\n /**\n * Add a provider to the internal providers list.\n */\n private async addToProvidersList(providerId: AuthProviderId): Promise<void> {\n const keytar = this.keytar;\n if (!keytar) return;\n\n try {\n const existing = await keytar.getPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT);\n let providers: AuthProviderId[] = [];\n\n if (existing) {\n const parsed = JSON.parse(existing);\n // Validate that parsed is an array and filter to valid provider IDs only\n if (Array.isArray(parsed)) {\n providers = parsed.filter((p): p is AuthProviderId => isValidProviderId(p));\n }\n }\n\n if (!providers.includes(providerId)) {\n providers.push(providerId);\n await keytar.setPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT, JSON.stringify(providers));\n }\n } catch {\n // Ignore errors updating providers list\n }\n }\n\n /**\n * Remove a provider from the internal providers list.\n */\n private async removeFromProvidersList(providerId: AuthProviderId): Promise<void> {\n const keytar = this.keytar;\n if (!keytar) return;\n\n try {\n const existing = await keytar.getPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT);\n if (existing) {\n const parsed = JSON.parse(existing);\n // Validate that parsed is an array and filter to valid provider IDs only\n if (Array.isArray(parsed)) {\n const providers = parsed.filter((p): p is AuthProviderId => isValidProviderId(p));\n const filtered = providers.filter(p => p !== providerId);\n await keytar.setPassword(SERVICE_NAME, PROVIDERS_LIST_ACCOUNT, JSON.stringify(filtered));\n }\n }\n } catch {\n // Ignore errors updating providers list\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Encrypted file storage backend.\n *\n * Uses AES-256-GCM encryption with random salt + machine-specific key derivation.\n * Stores credentials in ~/.stdio-bus/auth-credentials.enc\n *\n * File format v2: Salt (32 bytes) + IV (12 bytes) + Auth Tag (16 bytes) + Encrypted JSON data\n *\n * @module storage/encrypted-file-backend\n */\n\nimport * as crypto from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/** AES-256-GCM configuration constants */\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // 96 bits for GCM\nconst AUTH_TAG_LENGTH = 16; // 128 bits\nconst KEY_LENGTH = 32; // 256 bits\nconst SALT_LENGTH = 32; // 256 bits random salt\nconst PBKDF2_ITERATIONS = 100000;\nconst PBKDF2_DIGEST = 'sha256';\n\n/** File permission mode: owner read/write only (0600) */\nconst FILE_PERMISSION_MODE = 0o600;\n\n/** Default storage directory and file names */\nconst CONFIG_DIR_NAME = '.stdio-bus';\nconst CREDENTIALS_FILE_NAME = 'auth-credentials.enc';\n\n/**\n * Custom error for credential store corruption.\n */\nexport class CredentialStoreCorruptedError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'CredentialStoreCorruptedError';\n }\n}\n\n/**\n * Internal storage format for credentials map.\n */\ninterface CredentialsStore {\n version: number;\n credentials: Record<string, StoredCredentials>;\n}\n\n\n/**\n * Encrypted file storage backend implementation.\n *\n * Uses AES-256-GCM encryption with a key derived from:\n * - Random salt (stored with file, unique per file)\n * - Machine-specific entropy (hostname, username)\n *\n * This provides both uniqueness (random salt) and machine binding (entropy).\n *\n * Requirements: 5.2, 5.3\n */\nexport class EncryptedFileBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'encrypted-file';\n\n /** Cached encryption key (derived once per instance, per salt) */\n private encryptionKey: Buffer | null = null;\n\n /** Cached salt from current file */\n private currentSalt: Buffer | null = null;\n\n /** Path to the credentials file */\n private readonly filePath: string;\n\n /** Path to the config directory */\n private readonly configDir: string;\n\n /**\n * Create a new EncryptedFileBackend instance.\n * @param customPath - Optional custom path for the credentials file (for testing)\n */\n constructor(customPath?: string) {\n if (customPath) {\n this.filePath = customPath;\n this.configDir = path.dirname(customPath);\n } else {\n this.configDir = path.join(os.homedir(), CONFIG_DIR_NAME);\n this.filePath = path.join(this.configDir, CREDENTIALS_FILE_NAME);\n }\n }\n\n /**\n * Check if the backend is available.\n * Tests if the config directory is writable.\n * @returns True if the file system is writable\n */\n async isAvailable(): Promise<boolean> {\n try {\n // Ensure config directory exists\n await fs.mkdir(this.configDir, { recursive: true });\n\n // Test write access by creating a temporary file\n const testFile = path.join(this.configDir, `.write-test-${Date.now()}`);\n await fs.writeFile(testFile, 'test');\n await fs.unlink(testFile);\n\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<void> {\n const store = await this.loadStore();\n store.credentials[providerId] = credentials;\n await this.saveStore(store);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const store = await this.loadStore();\n return store.credentials[providerId] ?? null;\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const store = await this.loadStore();\n delete store.credentials[providerId];\n await this.saveStore(store);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n try {\n await fs.unlink(this.filePath);\n // Clear cached key and salt\n this.encryptionKey = null;\n this.currentSalt = null;\n } catch (error) {\n // Ignore if file doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider IDs that have stored credentials\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const store = await this.loadStore();\n return Object.keys(store.credentials) as AuthProviderId[];\n }\n\n /**\n * Derive the encryption key from salt and machine-specific entropy.\n * Uses PBKDF2 with random salt + hostname + username.\n * @param salt - The random salt (32 bytes)\n * @returns The derived 256-bit encryption key\n */\n private async deriveKey(salt: Buffer): Promise<Buffer> {\n // Check if we can use cached key (same salt)\n if (this.encryptionKey && this.currentSalt && salt.equals(this.currentSalt)) {\n return this.encryptionKey;\n }\n\n // Gather machine-specific entropy\n const hostname = os.hostname();\n const username = os.userInfo().username;\n const machineEntropy = `${hostname}:${username}`;\n\n // Combine random salt with machine entropy for key derivation\n const combinedSalt = Buffer.concat([salt, Buffer.from(machineEntropy, 'utf8')]);\n\n // Derive key using PBKDF2\n const derivedKey = await new Promise<Buffer>((resolve, reject) => {\n crypto.pbkdf2(\n machineEntropy,\n combinedSalt,\n PBKDF2_ITERATIONS,\n KEY_LENGTH,\n PBKDF2_DIGEST,\n (err, key) => {\n if (err) {\n reject(err);\n } else {\n resolve(key);\n }\n }\n );\n });\n\n // Cache the key and salt\n this.encryptionKey = derivedKey;\n this.currentSalt = salt;\n\n return derivedKey;\n }\n\n /**\n * Encrypt data using AES-256-GCM.\n * @param plaintext - The data to encrypt\n * @param salt - The salt to use for key derivation\n * @returns Buffer containing Salt + IV + Auth Tag + Ciphertext\n */\n private async encrypt(plaintext: string, salt: Buffer): Promise<Buffer> {\n const key = await this.deriveKey(salt);\n const iv = crypto.randomBytes(IV_LENGTH);\n\n const cipher = crypto.createCipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n\n const authTag = cipher.getAuthTag();\n\n // Format: Salt (32 bytes) + IV (12 bytes) + Auth Tag (16 bytes) + Encrypted data\n return Buffer.concat([salt, iv, authTag, encrypted]);\n }\n\n /**\n * Decrypt data using AES-256-GCM.\n * @param data - Buffer containing Salt + IV + Auth Tag + Ciphertext\n * @returns The decrypted plaintext\n * @throws CredentialStoreCorruptedError if decryption fails\n */\n private async decrypt(data: Buffer): Promise<string> {\n const minLength = SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH;\n if (data.length < minLength) {\n throw new CredentialStoreCorruptedError(\n `Invalid encrypted data: too short (${data.length} bytes, minimum ${minLength})`\n );\n }\n\n // Extract components\n const salt = data.subarray(0, SALT_LENGTH);\n const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const authTag = data.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = data.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\n const key = await this.deriveKey(salt);\n\n try {\n const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n\n return decrypted.toString('utf8');\n } catch (error) {\n throw new CredentialStoreCorruptedError(\n 'Failed to decrypt credential store: authentication failed or data corrupted',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n\n /**\n * Load the credentials store from the encrypted file.\n * @returns The decrypted credentials store\n * @throws CredentialStoreCorruptedError if the file exists but cannot be decrypted\n */\n private async loadStore(): Promise<CredentialsStore> {\n try {\n const encryptedData = await fs.readFile(this.filePath);\n const jsonData = await this.decrypt(encryptedData);\n\n let store: CredentialsStore;\n try {\n store = JSON.parse(jsonData) as CredentialsStore;\n } catch (parseError) {\n throw new CredentialStoreCorruptedError(\n 'Failed to parse credential store: invalid JSON',\n parseError instanceof Error ? parseError : undefined\n );\n }\n\n // Validate store structure\n if (typeof store.version !== 'number' || typeof store.credentials !== 'object') {\n throw new CredentialStoreCorruptedError(\n 'Invalid credential store format: missing version or credentials'\n );\n }\n\n return store;\n } catch (error) {\n // Return empty store only if file doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { version: 1, credentials: {} };\n }\n\n // Re-throw CredentialStoreCorruptedError as-is\n if (error instanceof CredentialStoreCorruptedError) {\n throw error;\n }\n\n // Wrap other errors\n throw new CredentialStoreCorruptedError(\n 'Failed to load credential store',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Save the credentials store to the encrypted file.\n * Sets restrictive file permissions (0600 - owner read/write only).\n * @param store - The credentials store to save\n */\n private async saveStore(store: CredentialsStore): Promise<void> {\n // Ensure config directory exists\n await fs.mkdir(this.configDir, { recursive: true });\n\n // Generate new random salt for each save (provides forward secrecy)\n const salt = crypto.randomBytes(SALT_LENGTH);\n\n const jsonData = JSON.stringify(store);\n const encryptedData = await this.encrypt(jsonData, salt);\n\n // Write atomically using a temporary file\n const tempFile = `${this.filePath}.tmp`;\n await fs.writeFile(tempFile, encryptedData, { mode: FILE_PERMISSION_MODE });\n\n // Rename atomically\n await fs.rename(tempFile, this.filePath);\n\n // Ensure final file has correct permissions (rename may not preserve mode on all systems)\n try {\n await fs.chmod(this.filePath, FILE_PERMISSION_MODE);\n } catch {\n // Ignore chmod errors on systems that don't support it (e.g., Windows)\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * In-memory storage backend for testing.\n *\n * This backend stores credentials in a Map and is primarily intended\n * for testing purposes. All operations are synchronous but return\n * Promises for interface compatibility with other backends.\n *\n * @module storage/memory-backend\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { IStorageBackend } from './types.js';\n\n/**\n * In-memory storage backend implementation.\n *\n * Uses a Map<AuthProviderId, StoredCredentials> for storage.\n * This backend is always available and is primarily used for testing.\n */\nexport class MemoryBackend implements IStorageBackend {\n readonly type: StorageBackendType = 'memory';\n\n /** Internal storage map */\n private readonly storage: Map<AuthProviderId, StoredCredentials> = new Map();\n\n /**\n * Check if the backend is available.\n * Memory backend is always available.\n * @returns Always resolves to true\n */\n async isAvailable(): Promise<boolean> {\n return true;\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<void> {\n this.storage.set(providerId, credentials);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n return this.storage.get(providerId) ?? null;\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n this.storage.delete(providerId);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n this.storage.clear();\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider IDs that have stored credentials\n */\n async listProviders(): Promise<AuthProviderId[]> {\n return Array.from(this.storage.keys());\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main credential store facade.\n *\n * Selects the best available storage backend (keychain > encrypted file > memory).\n * Provides a unified interface for credential storage regardless of the underlying backend.\n *\n * @module storage/credential-store\n */\n\nimport type { AuthProviderId, StorageBackendType, StoredCredentials } from '../types.js';\nimport type { ICredentialStore, IStorageBackend } from './types.js';\nimport { KeychainBackend } from './keychain-backend.js';\nimport { EncryptedFileBackend } from './encrypted-file-backend.js';\nimport { MemoryBackend } from './memory-backend.js';\n\n/**\n * Options for creating a credential store.\n */\nexport interface CredentialStoreOptions {\n /** Preferred storage backend (overrides automatic selection) */\n preferredBackend?: StorageBackendType;\n /** Custom path for encrypted file storage */\n encryptedFilePath?: string;\n}\n\n/**\n * Credential store implementation.\n *\n * Automatically selects the best available storage backend:\n * 1. Keychain (most secure, OS-level protection)\n * 2. Encrypted file (secure, portable)\n * 3. Memory (testing only, not persistent)\n *\n * The backend selection happens lazily on first use.\n */\nexport class CredentialStore implements ICredentialStore {\n private backend: IStorageBackend | null = null;\n private backendInitialized = false;\n private readonly options: CredentialStoreOptions;\n\n /**\n * Create a new credential store.\n * @param options - Configuration options\n */\n constructor(options: CredentialStoreOptions = {}) {\n this.options = options;\n }\n\n /**\n * Initialize the storage backend.\n * Selects the best available backend based on system capabilities.\n */\n private async initializeBackend(): Promise<IStorageBackend> {\n if (this.backendInitialized && this.backend) {\n return this.backend;\n }\n\n this.backendInitialized = true;\n\n // If a preferred backend is specified, try to use it\n if (this.options.preferredBackend) {\n const preferred = await this.createBackend(this.options.preferredBackend);\n if (preferred && await preferred.isAvailable()) {\n this.backend = preferred;\n console.error(`[CredentialStore] Using preferred backend: ${preferred.type}`);\n return this.backend;\n }\n console.error(`[CredentialStore] Preferred backend ${this.options.preferredBackend} not available, falling back`);\n }\n\n // Try backends in order of preference\n const backends: IStorageBackend[] = [\n new KeychainBackend(),\n new EncryptedFileBackend(this.options.encryptedFilePath),\n new MemoryBackend(),\n ];\n\n for (const backend of backends) {\n try {\n if (await backend.isAvailable()) {\n this.backend = backend;\n console.error(`[CredentialStore] Using backend: ${backend.type}`);\n return this.backend;\n }\n } catch (error) {\n console.error(`[CredentialStore] Backend ${backend.type} check failed: ${error}`);\n }\n }\n\n // Fallback to memory backend (always available)\n this.backend = new MemoryBackend();\n console.error(`[CredentialStore] Falling back to memory backend`);\n return this.backend;\n }\n\n /**\n * Create a specific backend by type.\n */\n private async createBackend(type: StorageBackendType): Promise<IStorageBackend | null> {\n switch (type) {\n case 'keychain':\n return new KeychainBackend();\n case 'encrypted-file':\n return new EncryptedFileBackend(this.options.encryptedFilePath);\n case 'memory':\n return new MemoryBackend();\n default:\n return null;\n }\n }\n\n /**\n * Get the initialized backend.\n */\n private async getBackend(): Promise<IStorageBackend> {\n return this.initializeBackend();\n }\n\n /**\n * Store credentials for a provider.\n * @param providerId - The provider identifier\n * @param credentials - The credentials to store\n */\n async store(providerId: AuthProviderId, credentials: StoredCredentials): Promise<void> {\n const backend = await this.getBackend();\n await backend.store(providerId, credentials);\n }\n\n /**\n * Retrieve credentials for a provider.\n * @param providerId - The provider identifier\n * @returns The stored credentials or null if not found\n */\n async retrieve(providerId: AuthProviderId): Promise<StoredCredentials | null> {\n const backend = await this.getBackend();\n return backend.retrieve(providerId);\n }\n\n /**\n * Delete credentials for a provider.\n * @param providerId - The provider identifier\n */\n async delete(providerId: AuthProviderId): Promise<void> {\n const backend = await this.getBackend();\n await backend.delete(providerId);\n }\n\n /**\n * Delete all stored credentials.\n */\n async deleteAll(): Promise<void> {\n const backend = await this.getBackend();\n await backend.deleteAll();\n }\n\n /**\n * List all providers with stored credentials.\n * @returns Array of provider identifiers\n */\n async listProviders(): Promise<AuthProviderId[]> {\n const backend = await this.getBackend();\n return backend.listProviders();\n }\n\n /**\n * Get the active storage backend type.\n * @returns The backend type currently in use\n */\n getBackendType(): StorageBackendType {\n if (!this.backend) {\n // Return 'memory' as default before initialization\n return 'memory';\n }\n return this.backend.type;\n }\n\n /**\n * Check if the store has been initialized.\n * @returns True if a backend has been selected\n */\n isInitialized(): boolean {\n return this.backendInitialized;\n }\n\n /**\n * Force re-initialization of the backend.\n * Useful for testing or when system capabilities change.\n */\n async reinitialize(): Promise<void> {\n this.backend = null;\n this.backendInitialized = false;\n await this.initializeBackend();\n }\n}\n\n/**\n * Create a credential store with default options.\n * @returns A new credential store instance\n */\nexport function createCredentialStore(options?: CredentialStoreOptions): CredentialStore {\n return new CredentialStore(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Token lifecycle management.\n *\n * Handles token storage, refresh, and expiration.\n * Implements proactive token refresh and concurrent refresh serialization.\n *\n * @module token-manager\n */\n\nimport type { AuthProviderId, TokenResponse, TokenStatus, StoredCredentials } from './types.js';\nimport type { ICredentialStore } from './storage/types.js';\nimport type { IAuthProvider } from './providers/types.js';\n\n/**\n * Default token refresh threshold in milliseconds (5 minutes).\n * Tokens will be proactively refreshed when they expire within this threshold.\n */\nexport const DEFAULT_REFRESH_THRESHOLD_MS = 5 * 60 * 1000;\n\n/**\n * Token lifecycle management interface.\n */\nexport interface ITokenManager {\n /** Get a valid access token, refreshing if necessary */\n getAccessToken(providerId: AuthProviderId): Promise<string | null>;\n\n /** Store new tokens from an OAuth response */\n storeTokens(providerId: AuthProviderId, tokens: TokenResponse): Promise<void>;\n\n /** Check if tokens exist and are valid for a provider */\n hasValidTokens(providerId: AuthProviderId): Promise<boolean>;\n\n /** Force refresh tokens for a provider */\n forceRefresh(providerId: AuthProviderId): Promise<string | null>;\n\n /** Clear tokens for a provider (triggers re-auth) */\n clearTokens(providerId: AuthProviderId): Promise<void>;\n\n /** Get token status for all providers */\n getStatus(): Promise<Map<AuthProviderId, TokenStatus>>;\n}\n\n/**\n * Provider resolver function type.\n * Used to get provider instances for token refresh operations.\n */\nexport type ProviderResolver = (providerId: AuthProviderId) => IAuthProvider | null;\n\n/**\n * Options for creating a token manager.\n */\nexport interface TokenManagerOptions {\n /** Credential store for persisting tokens */\n credentialStore: ICredentialStore;\n /** Function to resolve provider instances */\n providerResolver: ProviderResolver;\n /** Token refresh threshold in milliseconds (default: 5 minutes) */\n refreshThresholdMs?: number;\n}\n\n/**\n * Token manager implementation.\n *\n * Manages token lifecycle including:\n * - Proactive token refresh when tokens are near expiration\n * - Concurrent refresh serialization (only one refresh per provider at a time)\n * - Automatic credential cleanup on refresh failure\n * - Refresh token rotation handling\n */\nexport class TokenManager implements ITokenManager {\n private readonly credentialStore: ICredentialStore;\n private readonly providerResolver: ProviderResolver;\n private readonly refreshThresholdMs: number;\n\n /**\n * Map of provider IDs to pending refresh promises.\n * Used to serialize concurrent refresh requests.\n */\n private readonly pendingRefreshes = new Map<AuthProviderId, Promise<string | null>>();\n\n /**\n * Create a new token manager.\n * @param options - Configuration options\n */\n constructor(options: TokenManagerOptions) {\n this.credentialStore = options.credentialStore;\n this.providerResolver = options.providerResolver;\n this.refreshThresholdMs = options.refreshThresholdMs ?? DEFAULT_REFRESH_THRESHOLD_MS;\n }\n\n /**\n * Get a valid access token, refreshing if necessary.\n *\n * If the token is within the refresh threshold of expiration, it will be\n * proactively refreshed before returning.\n *\n * @param providerId - The provider identifier\n * @returns The access token or null if not available\n */\n async getAccessToken(providerId: AuthProviderId): Promise<string | null> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return null;\n }\n\n // Check if token needs refresh\n if (this.shouldRefresh(credentials)) {\n // Attempt proactive refresh\n const refreshedToken = await this.refreshTokenInternal(providerId, credentials);\n if (refreshedToken !== null) {\n return refreshedToken;\n }\n // If refresh failed but we still have a valid token, return it\n if (!this.isExpired(credentials)) {\n return credentials.accessToken;\n }\n return null;\n }\n\n return credentials.accessToken;\n }\n\n /**\n * Store new tokens from an OAuth response.\n *\n * Converts the token response to stored credentials format and persists them.\n * Preserves the existing refresh token if the new response doesn't include one\n * (some providers only return refresh tokens on initial auth, not on refresh).\n *\n * @param providerId - The provider identifier\n * @param tokens - The token response from the OAuth provider\n */\n async storeTokens(providerId: AuthProviderId, tokens: TokenResponse): Promise<void> {\n const now = Date.now();\n\n // Validate token response\n if (!tokens.accessToken || typeof tokens.accessToken !== 'string') {\n throw new Error('Invalid token response: missing or invalid accessToken');\n }\n if (tokens.expiresIn !== undefined) {\n if (typeof tokens.expiresIn !== 'number' || !Number.isFinite(tokens.expiresIn) || tokens.expiresIn < 0) {\n throw new Error('Invalid token response: expiresIn must be a non-negative finite number');\n }\n }\n\n // Calculate expiration timestamp if expiresIn is provided\n const expiresAt = tokens.expiresIn\n ? now + tokens.expiresIn * 1000\n : undefined;\n\n // Get existing credentials to preserve client info and refresh token\n const existing = await this.credentialStore.retrieve(providerId);\n\n // Preserve existing refresh token if new response doesn't include one\n // (some providers only return refresh tokens on initial auth, not on refresh)\n const refreshToken = tokens.refreshToken ?? existing?.refreshToken;\n\n const credentials: StoredCredentials = {\n providerId,\n accessToken: tokens.accessToken,\n refreshToken,\n expiresAt,\n scope: tokens.scope,\n // Preserve client info from existing credentials\n clientId: existing?.clientId,\n clientSecret: existing?.clientSecret,\n customEndpoints: existing?.customEndpoints,\n storedAt: now,\n };\n\n await this.credentialStore.store(providerId, credentials);\n }\n\n /**\n * Check if tokens exist and are valid for a provider.\n *\n * A token is considered valid if:\n * - Credentials exist for the provider\n * - The access token is not expired\n *\n * @param providerId - The provider identifier\n * @returns True if valid tokens exist\n */\n async hasValidTokens(providerId: AuthProviderId): Promise<boolean> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return false;\n }\n\n return !this.isExpired(credentials);\n }\n\n /**\n * Force refresh tokens for a provider.\n *\n * Unlike getAccessToken, this always attempts a refresh regardless of\n * the current token's expiration status.\n *\n * @param providerId - The provider identifier\n * @returns The new access token or null if refresh failed\n */\n async forceRefresh(providerId: AuthProviderId): Promise<string | null> {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n return null;\n }\n\n return this.refreshTokenInternal(providerId, credentials);\n }\n\n /**\n * Clear tokens for a provider.\n *\n * This triggers re-authentication on the next token request.\n *\n * @param providerId - The provider identifier\n */\n async clearTokens(providerId: AuthProviderId): Promise<void> {\n await this.credentialStore.delete(providerId);\n }\n\n /**\n * Get token status for all providers.\n *\n * @returns Map of provider IDs to their token status\n */\n async getStatus(): Promise<Map<AuthProviderId, TokenStatus>> {\n const providers = await this.credentialStore.listProviders();\n const statusMap = new Map<AuthProviderId, TokenStatus>();\n\n for (const providerId of providers) {\n const credentials = await this.credentialStore.retrieve(providerId);\n\n if (!credentials) {\n statusMap.set(providerId, 'not-configured');\n continue;\n }\n\n if (this.isExpired(credentials)) {\n // Check if we have a refresh token to potentially recover\n if (credentials.refreshToken) {\n statusMap.set(providerId, 'expired');\n } else {\n statusMap.set(providerId, 'refresh-failed');\n }\n } else {\n statusMap.set(providerId, 'authenticated');\n }\n }\n\n return statusMap;\n }\n\n /**\n * Check if credentials are expired.\n *\n * @param credentials - The stored credentials\n * @returns True if the token is expired\n */\n private isExpired(credentials: StoredCredentials): boolean {\n if (!credentials.expiresAt) {\n // No expiration info, assume valid\n return false;\n }\n\n return Date.now() >= credentials.expiresAt;\n }\n\n /**\n * Check if credentials should be proactively refreshed.\n *\n * Returns true if the token will expire within the refresh threshold.\n *\n * @param credentials - The stored credentials\n * @returns True if the token should be refreshed\n */\n private shouldRefresh(credentials: StoredCredentials): boolean {\n if (!credentials.expiresAt) {\n // No expiration info, don't refresh\n return false;\n }\n\n if (!credentials.refreshToken) {\n // No refresh token available\n return false;\n }\n\n const timeUntilExpiry = credentials.expiresAt - Date.now();\n return timeUntilExpiry <= this.refreshThresholdMs;\n }\n\n /**\n * Internal token refresh implementation with concurrent request serialization.\n *\n * Ensures only one refresh operation occurs at a time per provider.\n * If a refresh is already in progress, returns the pending promise.\n *\n * @param providerId - The provider identifier\n * @param credentials - The current stored credentials\n * @returns The new access token or null if refresh failed\n */\n private async refreshTokenInternal(\n providerId: AuthProviderId,\n credentials: StoredCredentials\n ): Promise<string | null> {\n // Check for pending refresh\n const pending = this.pendingRefreshes.get(providerId);\n if (pending) {\n return pending;\n }\n\n // No refresh token available\n if (!credentials.refreshToken) {\n console.error(`[TokenManager] No refresh token available for ${providerId}`);\n return null;\n }\n\n // Create and store the refresh promise\n const refreshPromise = this.executeRefresh(providerId, credentials.refreshToken);\n this.pendingRefreshes.set(providerId, refreshPromise);\n\n try {\n return await refreshPromise;\n } finally {\n // Clean up pending refresh\n this.pendingRefreshes.delete(providerId);\n }\n }\n\n /**\n * Execute the actual token refresh operation.\n *\n * @param providerId - The provider identifier\n * @param refreshToken - The refresh token to use\n * @returns The new access token or null if refresh failed\n */\n private async executeRefresh(\n providerId: AuthProviderId,\n refreshToken: string\n ): Promise<string | null> {\n const provider = this.providerResolver(providerId);\n\n if (!provider) {\n console.error(`[TokenManager] Provider not found for ${providerId}`);\n return null;\n }\n\n try {\n console.error(`[TokenManager] Refreshing token for ${providerId}`);\n\n const tokenResponse = await provider.refreshToken(refreshToken);\n\n // Store the new tokens (handles refresh token rotation)\n await this.storeTokens(providerId, tokenResponse);\n\n console.error(`[TokenManager] Token refreshed successfully for ${providerId}`);\n return tokenResponse.accessToken;\n } catch (error) {\n // Sanitize error logging to avoid leaking sensitive information\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n // Remove any potential tokens or secrets from error message\n const sanitizedMessage = errorMessage\n .replace(/[A-Za-z0-9_-]{20,}/g, '[REDACTED]')\n .replace(/Bearer\\s+\\S+/gi, 'Bearer [REDACTED]');\n console.error(`[TokenManager] Token refresh failed for ${providerId}: ${sanitizedMessage}`);\n\n // Clear credentials on refresh failure (Requirement 6.3)\n // This signals that re-authentication is required\n await this.credentialStore.delete(providerId);\n\n return null;\n }\n }\n}\n\n/**\n * Create a token manager with the given options.\n *\n * @param options - Configuration options\n * @returns A new token manager instance\n */\nexport function createTokenManager(options: TokenManagerOptions): TokenManager {\n return new TokenManager(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Base OAuth 2.1 provider implementation.\n *\n * Provides common OAuth 2.1 URL building logic and HTTPS endpoint validation.\n *\n * @module providers/base-provider\n */\n\nimport type {\n AuthProviderId,\n AuthorizationParams,\n TokenResponse,\n TokenInjectionMethod,\n ProviderEndpoints,\n} from '../types.js';\nimport type { IAuthProvider } from './types.js';\n\n/**\n * Configuration for a base auth provider.\n */\nexport interface BaseProviderConfig {\n /** Unique provider identifier */\n id: AuthProviderId;\n /** Human-readable provider name */\n name: string;\n /** OAuth authorization endpoint URL */\n authorizationEndpoint: string;\n /** OAuth token endpoint URL */\n tokenEndpoint: string;\n /** Default scopes for this provider */\n defaultScopes: string[];\n /** Token injection method for agent requests */\n tokenInjection: TokenInjectionMethod;\n /** Client ID for OAuth flow */\n clientId?: string;\n /** Client secret for OAuth flow (optional, for confidential clients) */\n clientSecret?: string;\n}\n\n/**\n * Abstract base class for OAuth 2.1 providers.\n *\n * Implements common OAuth 2.1 functionality:\n * - Authorization URL building with required parameters\n * - HTTPS endpoint validation\n * - Token exchange and refresh\n *\n * Requirements: 3.2, 3.6, 7.5\n */\nexport abstract class BaseAuthProvider implements IAuthProvider {\n readonly id: AuthProviderId;\n readonly name: string;\n readonly defaultScopes: readonly string[];\n\n protected readonly authorizationEndpoint: string;\n protected readonly tokenEndpoint: string;\n protected readonly tokenInjection: TokenInjectionMethod;\n protected clientId?: string;\n protected clientSecret?: string;\n\n /** Default timeout for HTTP requests in milliseconds (30 seconds) */\n protected static readonly DEFAULT_REQUEST_TIMEOUT_MS = 30000;\n\n /**\n * Security-critical OAuth parameters that cannot be overridden by additionalParams.\n * These parameters are set by the OAuth flow and must not be tampered with.\n */\n private static readonly PROTECTED_PARAMS = new Set([\n 'client_id',\n 'redirect_uri',\n 'response_type',\n 'scope',\n 'state',\n 'code_challenge',\n 'code_challenge_method',\n 'code',\n 'code_verifier',\n 'grant_type',\n 'refresh_token',\n 'client_secret',\n ]);\n\n /**\n * Create a new base auth provider.\n * @param config - Provider configuration\n * @throws Error if endpoints are not HTTPS or contain embedded credentials\n */\n constructor(config: BaseProviderConfig) {\n this.id = config.id;\n this.name = config.name;\n this.authorizationEndpoint = config.authorizationEndpoint;\n this.tokenEndpoint = config.tokenEndpoint;\n this.defaultScopes = Object.freeze([...config.defaultScopes]);\n this.tokenInjection = config.tokenInjection;\n this.clientId = config.clientId;\n this.clientSecret = config.clientSecret;\n\n // Validate HTTPS endpoints at construction time\n this.validateConfig();\n }\n\n /**\n * Build the authorization URL for the OAuth flow.\n *\n * Includes all required OAuth 2.1 parameters:\n * - client_id\n * - redirect_uri\n * - response_type=code\n * - scope\n * - state\n * - code_challenge\n * - code_challenge_method=S256\n *\n * @param params - Authorization parameters\n * @returns The complete authorization URL\n * @throws Error if additionalParams attempts to override protected parameters\n */\n buildAuthorizationUrl(params: AuthorizationParams): string {\n const url = new URL(this.authorizationEndpoint);\n\n // Required OAuth 2.1 parameters\n url.searchParams.set('client_id', params.clientId);\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', params.responseType);\n url.searchParams.set('scope', params.scope);\n url.searchParams.set('state', params.state);\n\n // PKCE parameters (required for OAuth 2.1)\n url.searchParams.set('code_challenge', params.codeChallenge);\n url.searchParams.set('code_challenge_method', params.codeChallengeMethod);\n\n // Add any additional provider-specific parameters\n // Security: Reject attempts to override protected OAuth parameters\n if (params.additionalParams) {\n for (const [key, value] of Object.entries(params.additionalParams)) {\n if (BaseAuthProvider.PROTECTED_PARAMS.has(key.toLowerCase())) {\n throw new Error(\n `Security violation: additionalParams cannot override protected OAuth parameter '${key}'`\n );\n }\n url.searchParams.set(key, value);\n }\n }\n\n return url.toString();\n }\n\n /**\n * Exchange authorization code for tokens.\n *\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n * @param redirectUri - The redirect URI used in the authorization request\n * @returns The token response\n */\n async exchangeCode(\n code: string,\n codeVerifier: string,\n redirectUri: string\n ): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n });\n\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n\n // Use AbortController for timeout and prevent redirect following\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error', // Prevent following redirects for security\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token exchange timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token\n * @returns The new token response\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n });\n\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n\n // Use AbortController for timeout and prevent redirect following\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error', // Prevent following redirects for security\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token refresh timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Validate provider configuration.\n *\n * Ensures all endpoints use HTTPS (required for OAuth 2.1).\n *\n * @throws Error if configuration is invalid\n */\n validateConfig(): void {\n this.validateHttpsEndpoint(this.authorizationEndpoint, 'authorization');\n this.validateHttpsEndpoint(this.tokenEndpoint, 'token');\n }\n\n /**\n * Get token injection method for agent requests.\n *\n * @returns The token injection configuration\n */\n getTokenInjection(): TokenInjectionMethod {\n return this.tokenInjection;\n }\n\n /**\n * Get the provider endpoints.\n *\n * @returns The provider endpoint URLs\n */\n getEndpoints(): ProviderEndpoints {\n return {\n authorizationEndpoint: this.authorizationEndpoint,\n tokenEndpoint: this.tokenEndpoint,\n };\n }\n\n /**\n * Set the client credentials.\n *\n * @param clientId - The OAuth client ID\n * @param clientSecret - The OAuth client secret (optional)\n */\n setClientCredentials(clientId: string, clientSecret?: string): void {\n this.clientId = clientId;\n this.clientSecret = clientSecret;\n }\n\n /**\n * Validate that an endpoint uses HTTPS and has no embedded credentials.\n *\n * @param endpoint - The endpoint URL to validate\n * @param name - The name of the endpoint (for error messages)\n * @throws Error if the endpoint does not use HTTPS or contains embedded credentials\n */\n protected validateHttpsEndpoint(endpoint: string, name: string): void {\n const url = new URL(endpoint);\n if (url.protocol !== 'https:') {\n throw new Error(\n `${this.name} ${name} endpoint must use HTTPS: ${endpoint}`\n );\n }\n // Reject URLs with embedded credentials (username:password@host)\n if (url.username || url.password) {\n throw new Error(\n `${this.name} ${name} endpoint must not contain embedded credentials: ${endpoint}`\n );\n }\n }\n\n /**\n * Parse a token response from the provider.\n *\n * @param data - The raw response data\n * @returns The parsed token response\n */\n protected parseTokenResponse(data: Record<string, unknown>): TokenResponse {\n const accessToken = data.access_token;\n if (typeof accessToken !== 'string') {\n throw new Error('Invalid token response: missing access_token');\n }\n\n const tokenType = data.token_type;\n if (typeof tokenType !== 'string') {\n throw new Error('Invalid token response: missing token_type');\n }\n\n const response: TokenResponse = {\n accessToken,\n tokenType,\n };\n\n // Optional fields\n if (typeof data.expires_in === 'number') {\n response.expiresIn = data.expires_in;\n }\n\n if (typeof data.refresh_token === 'string') {\n response.refreshToken = data.refresh_token;\n }\n\n if (typeof data.scope === 'string') {\n response.scope = data.scope;\n }\n\n if (typeof data.id_token === 'string') {\n response.idToken = data.id_token;\n }\n\n return response;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * GitHub OAuth 2.1 provider implementation.\n *\n * @module providers/github-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * GitHub OAuth provider.\n *\n * Endpoints:\n * - Authorization: https://github.com/login/oauth/authorize\n * - Token: https://github.com/login/oauth/access_token\n *\n * Default scopes: read:user\n * Token injection: Bearer header\n */\nexport class GitHubProvider extends BaseAuthProvider {\n constructor(clientId?: string, clientSecret?: string) {\n super({\n id: 'github',\n name: 'GitHub',\n authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n tokenEndpoint: 'https://github.com/login/oauth/access_token',\n defaultScopes: ['read:user'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId,\n clientSecret,\n });\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Google OAuth 2.1 provider implementation.\n *\n * @module providers/google-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Google OAuth provider.\n *\n * Endpoints:\n * - Authorization: https://accounts.google.com/o/oauth2/v2/auth\n * - Token: https://oauth2.googleapis.com/token\n *\n * Default scopes: openid, profile, email\n * Token injection: Bearer header\n */\nexport class GoogleProvider extends BaseAuthProvider {\n constructor(clientId?: string, clientSecret?: string) {\n super({\n id: 'google',\n name: 'Google',\n authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenEndpoint: 'https://oauth2.googleapis.com/token',\n defaultScopes: ['openid', 'profile', 'email'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId,\n clientSecret,\n });\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * AWS Cognito OAuth 2.1 provider implementation.\n *\n * @module providers/cognito-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Configuration options for Cognito provider.\n */\nexport interface CognitoProviderConfig {\n /** Cognito user pool domain (e.g., 'my-app' for my-app.auth.us-east-1.amazoncognito.com) */\n userPoolDomain: string;\n /** AWS region (e.g., 'us-east-1') */\n region: string;\n /** OAuth client ID */\n clientId?: string;\n /** OAuth client secret (optional) */\n clientSecret?: string;\n}\n\n/**\n * Pattern for valid Cognito user pool domain names.\n * Must be alphanumeric with hyphens, no leading/trailing hyphens.\n */\nconst VALID_DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i;\n\n/**\n * Pattern for valid AWS region names.\n * Format: xx-xxxx-N (e.g., us-east-1, eu-west-2)\n */\nconst VALID_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\\d+$/;\n\n/**\n * AWS Cognito OAuth provider.\n *\n * Endpoints are dynamically constructed based on user pool domain and region:\n * - Authorization: https://{domain}.auth.{region}.amazoncognito.com/oauth2/authorize\n * - Token: https://{domain}.auth.{region}.amazoncognito.com/oauth2/token\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n */\nexport class CognitoProvider extends BaseAuthProvider {\n constructor(config: CognitoProviderConfig) {\n // Validate userPoolDomain to prevent URL injection\n CognitoProvider.validateUserPoolDomain(config.userPoolDomain);\n // Validate region to prevent URL injection\n CognitoProvider.validateRegion(config.region);\n\n const baseUrl = `https://${config.userPoolDomain}.auth.${config.region}.amazoncognito.com`;\n\n super({\n id: 'cognito',\n name: 'AWS Cognito',\n authorizationEndpoint: `${baseUrl}/oauth2/authorize`,\n tokenEndpoint: `${baseUrl}/oauth2/token`,\n defaultScopes: ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n }\n\n /**\n * Validate Cognito user pool domain name.\n * @param domain - The user pool domain to validate\n * @throws Error if domain is invalid or contains injection characters\n */\n private static validateUserPoolDomain(domain: string): void {\n if (!domain || typeof domain !== 'string') {\n throw new Error('Cognito userPoolDomain is required');\n }\n\n const trimmed = domain.trim();\n if (trimmed !== domain) {\n throw new Error('Cognito userPoolDomain must not contain leading/trailing whitespace');\n }\n\n if (domain.length === 0) {\n throw new Error('Cognito userPoolDomain cannot be empty');\n }\n\n if (domain.length > 63) {\n throw new Error('Cognito userPoolDomain must be 63 characters or less');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(domain)) {\n throw new Error('Cognito userPoolDomain contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n if (!VALID_DOMAIN_PATTERN.test(domain)) {\n throw new Error(\n 'Cognito userPoolDomain must be alphanumeric with hyphens, no leading/trailing hyphens'\n );\n }\n }\n\n /**\n * Validate AWS region name.\n * @param region - The AWS region to validate\n * @throws Error if region is invalid or contains injection characters\n */\n private static validateRegion(region: string): void {\n if (!region || typeof region !== 'string') {\n throw new Error('Cognito region is required');\n }\n\n const trimmed = region.trim();\n if (trimmed !== region) {\n throw new Error('Cognito region must not contain leading/trailing whitespace');\n }\n\n if (region.length === 0) {\n throw new Error('Cognito region cannot be empty');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(region)) {\n throw new Error('Cognito region contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n if (!VALID_REGION_PATTERN.test(region)) {\n throw new Error(\n 'Cognito region must be a valid AWS region format (e.g., us-east-1, eu-west-2)'\n );\n }\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Microsoft Entra ID (formerly Azure AD) OAuth 2.1 provider implementation.\n *\n * @module providers/entra-provider\n */\n\nimport { BaseAuthProvider } from './base-provider.js';\n\n/**\n * Configuration options for Microsoft Entra ID provider.\n * \n * @remarks\n * Microsoft renamed Azure AD to Microsoft Entra ID in 2023.\n * This provider supports both single-tenant and multi-tenant configurations.\n */\nexport interface EntraProviderConfig {\n /** Microsoft Entra ID tenant ID or 'common' for multi-tenant */\n tenantId: string;\n /** OAuth client ID */\n clientId?: string;\n /** OAuth client secret (optional) */\n clientSecret?: string;\n}\n\n/**\n * @deprecated Use EntraProviderConfig instead. Kept for backward compatibility.\n */\nexport type AzureProviderConfig = EntraProviderConfig;\n\n/**\n * Well-known Microsoft Entra ID tenant values for multi-tenant scenarios.\n */\nconst WELL_KNOWN_TENANTS = new Set(['common', 'organizations', 'consumers']);\n\n/**\n * Pattern for valid Microsoft Entra ID tenant GUIDs.\n * Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n */\nconst GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Pattern for valid Microsoft Entra ID verified domain names.\n * Must be a valid domain format without URL injection characters.\n */\nconst DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;\n\n/**\n * Microsoft Entra ID (formerly Azure AD) OAuth provider.\n *\n * Endpoints are dynamically constructed based on tenant ID:\n * - Authorization: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize\n * - Token: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token\n *\n * Use 'common' for multi-tenant applications.\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n * \n * @remarks\n * The provider ID remains 'azure' for backward compatibility with existing\n * configurations and the ACP Registry.\n */\nexport class EntraIdProvider extends BaseAuthProvider {\n constructor(config: EntraProviderConfig) {\n // Validate tenantId to prevent URL injection\n EntraIdProvider.validateTenantId(config.tenantId);\n\n const baseUrl = `https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0`;\n\n super({\n id: 'azure', // Keep 'azure' as provider ID for backward compatibility\n name: 'Microsoft Entra ID',\n authorizationEndpoint: `${baseUrl}/authorize`,\n tokenEndpoint: `${baseUrl}/token`,\n defaultScopes: ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n }\n\n /**\n * Validate Microsoft Entra ID tenant ID.\n * @param tenantId - The tenant ID to validate\n * @throws Error if tenantId is invalid or contains injection characters\n */\n private static validateTenantId(tenantId: string): void {\n if (!tenantId || typeof tenantId !== 'string') {\n throw new Error('Entra ID tenantId is required');\n }\n\n const trimmed = tenantId.trim();\n if (trimmed !== tenantId) {\n throw new Error('Entra ID tenantId must not contain leading/trailing whitespace');\n }\n\n if (tenantId.length === 0) {\n throw new Error('Entra ID tenantId cannot be empty');\n }\n\n // Check for URL injection characters\n if (/[/:?#@\\s]/.test(tenantId)) {\n throw new Error('Entra ID tenantId contains invalid characters (/, :, ?, #, @, or whitespace)');\n }\n\n // Accept well-known tenant values\n if (WELL_KNOWN_TENANTS.has(tenantId.toLowerCase())) {\n return;\n }\n\n // Accept valid GUIDs\n if (GUID_PATTERN.test(tenantId)) {\n return;\n }\n\n // Accept valid domain names (verified domains)\n if (DOMAIN_PATTERN.test(tenantId)) {\n return;\n }\n\n throw new Error(\n `Entra ID tenantId must be 'common', 'organizations', 'consumers', a valid GUID, or a verified domain name`\n );\n }\n}\n\n/**\n * @deprecated Use EntraIdProvider instead. Kept for backward compatibility.\n */\nexport const AzureProvider = EntraIdProvider;\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Generic OIDC Discovery provider implementation.\n *\n * Supports any OIDC-compliant provider via issuer-based discovery\n * (.well-known/openid-configuration) with manual endpoint override fallback.\n *\n * Requirements: 7a.1, 7a.2, 7a.3, 7a.5, 7a.6\n *\n * @module providers/oidc-provider\n */\n\nimport * as crypto from 'crypto';\nimport { BaseAuthProvider } from './base-provider.js';\nimport type { TokenResponse } from '../types.js';\n\n// =============================================================================\n// JWKS Types\n// =============================================================================\n\n/**\n * JSON Web Key (JWK) structure for RSA keys.\n */\nexport interface JWK {\n /** Key type (e.g., 'RSA') */\n kty: string;\n /** Key ID - used to match keys in JWKS */\n kid?: string;\n /** Algorithm (e.g., 'RS256') */\n alg?: string;\n /** Key use (e.g., 'sig' for signature) */\n use?: string;\n /** RSA modulus (base64url encoded) */\n n?: string;\n /** RSA exponent (base64url encoded) */\n e?: string;\n /** X.509 certificate chain */\n x5c?: string[];\n}\n\n/**\n * JSON Web Key Set (JWKS) structure.\n */\nexport interface JWKS {\n /** Array of JSON Web Keys */\n keys: JWK[];\n}\n\n/**\n * Cached JWKS with metadata.\n */\nexport interface CachedJWKS {\n /** The JWKS data */\n jwks: JWKS;\n /** Timestamp when the JWKS was fetched */\n fetchedAt: number;\n /** TTL in milliseconds */\n ttlMs: number;\n}\n\n/**\n * Decoded JWT header.\n */\nexport interface JWTHeader {\n /** Algorithm used for signing */\n alg: string;\n /** Token type (usually 'JWT') */\n typ?: string;\n /** Key ID - used to find the signing key in JWKS */\n kid?: string;\n}\n\n/**\n * ID Token claims structure.\n * Requirements: 7a.5\n */\nexport interface IDTokenClaims {\n /** Issuer - must match the configured issuer */\n iss: string;\n /** Subject - unique identifier for the user */\n sub: string;\n /** Audience - must contain the client_id */\n aud: string | string[];\n /** Expiration time (Unix timestamp) */\n exp: number;\n /** Issued at time (Unix timestamp) */\n iat: number;\n /** Nonce - if provided in auth request, must match */\n nonce?: string;\n /** Authentication time */\n auth_time?: number;\n /** Access token hash */\n at_hash?: string;\n /** Additional claims */\n [key: string]: unknown;\n}\n\n/**\n * Result of ID token validation.\n */\nexport interface IDTokenValidationResult {\n /** Whether validation was successful */\n valid: boolean;\n /** The decoded claims if valid */\n claims?: IDTokenClaims;\n /** Error message if validation failed */\n error?: string;\n}\n\n/**\n * Options for ID token validation.\n */\nexport interface IDTokenValidationOptions {\n /** Expected audience (client_id) */\n audience: string;\n /** Expected nonce (if used in auth request) */\n nonce?: string;\n /** Clock skew tolerance in seconds (default: 60) */\n clockSkewSeconds?: number;\n}\n\n/**\n * OIDC Discovery document structure.\n * Contains the endpoints and capabilities advertised by the OIDC provider.\n */\nexport interface OIDCDiscoveryDocument {\n /** The issuer identifier (must match the issuer URL) */\n issuer: string;\n /** URL of the authorization endpoint */\n authorization_endpoint: string;\n /** URL of the token endpoint */\n token_endpoint: string;\n /** URL of the JWKS endpoint for token validation */\n jwks_uri?: string;\n /** URL of the userinfo endpoint */\n userinfo_endpoint?: string;\n /** Supported response types */\n response_types_supported?: string[];\n /** Supported grant types */\n grant_types_supported?: string[];\n /** Supported scopes */\n scopes_supported?: string[];\n /** Supported token endpoint authentication methods */\n token_endpoint_auth_methods_supported?: string[];\n /** Supported code challenge methods for PKCE */\n code_challenge_methods_supported?: string[];\n}\n\n/**\n * Configuration options for OIDC provider.\n */\nexport interface OIDCProviderConfig {\n /**\n * The OIDC issuer URL (e.g., 'https://auth.example.com').\n * Used for discovery via {issuer}/.well-known/openid-configuration.\n */\n issuer: string;\n\n /**\n * Manual override for authorization endpoint.\n * Used when discovery is unavailable.\n * Requirements: 7a.2\n */\n authorizationEndpoint?: string;\n\n /**\n * Manual override for token endpoint.\n * Used when discovery is unavailable.\n * Requirements: 7a.2\n */\n tokenEndpoint?: string;\n\n /**\n * Manual override for JWKS URI.\n * Used for token validation when discovery is unavailable.\n */\n jwksUri?: string;\n\n /** OAuth client ID */\n clientId?: string;\n\n /** OAuth client secret (optional, for confidential clients) */\n clientSecret?: string;\n\n /**\n * Token endpoint authentication method.\n * Supported: 'client_secret_post', 'client_secret_basic'\n * Default: 'client_secret_post'\n * Requirements: 7a.7\n */\n tokenEndpointAuthMethod?: 'client_secret_post' | 'client_secret_basic';\n\n /**\n * Custom scopes to use instead of defaults.\n * Default: ['openid', 'profile']\n */\n scopes?: string[];\n\n /**\n * Whether to skip discovery and use manual endpoints only.\n * Default: false (discovery is attempted first)\n */\n skipDiscovery?: boolean;\n\n /**\n * Timeout for discovery request in milliseconds.\n * Default: 10000 (10 seconds)\n */\n discoveryTimeoutMs?: number;\n}\n\n/**\n * Result of OIDC discovery operation.\n */\nexport interface OIDCDiscoveryResult {\n /** Whether discovery was successful */\n success: boolean;\n /** The discovery document if successful */\n document?: OIDCDiscoveryDocument;\n /** Error message if discovery failed */\n error?: string;\n}\n\n/**\n * Generic OIDC Discovery provider.\n *\n * Supports any OIDC-compliant provider (Auth0, Okta, Keycloak, etc.)\n * via issuer-based discovery with manual endpoint override fallback.\n *\n * Features:\n * - Automatic discovery via .well-known/openid-configuration\n * - Manual endpoint override when discovery unavailable\n * - PKCE S256 enforcement (per Requirement 7a.3)\n * - Cached discovery document\n * - Support for client_secret_post and client_secret_basic auth methods\n *\n * Default scopes: openid, profile\n * Token injection: Bearer header\n *\n * Requirements: 7a.1, 7a.2, 7a.3, 7a.7\n */\nexport class OIDCProvider extends BaseAuthProvider {\n private readonly issuer: string;\n private readonly tokenEndpointAuthMethod: 'client_secret_post' | 'client_secret_basic';\n private readonly discoveryTimeoutMs: number;\n private readonly skipDiscovery: boolean;\n private readonly manualJwksUri?: string;\n\n /** Discovered authorization endpoint (overrides base class endpoint after discovery) */\n private discoveredAuthorizationEndpoint?: string;\n\n /** Discovered token endpoint (overrides base class endpoint after discovery) */\n private discoveredTokenEndpoint?: string;\n\n /** Cached discovery document */\n private discoveryDocument?: OIDCDiscoveryDocument;\n\n /** Whether discovery has been attempted */\n private discoveryAttempted = false;\n\n /** Cached JWKS for token validation */\n private cachedJWKS?: CachedJWKS;\n\n /** Default timeout for discovery requests (10 seconds) */\n private static readonly DEFAULT_DISCOVERY_TIMEOUT_MS = 10000;\n\n /** Default JWKS cache TTL (1 hour) */\n private static readonly DEFAULT_JWKS_CACHE_TTL_MS = 60 * 60 * 1000;\n\n /** Default clock skew tolerance for token validation (60 seconds) */\n private static readonly DEFAULT_CLOCK_SKEW_SECONDS = 60;\n\n constructor(config: OIDCProviderConfig) {\n // Validate issuer URL\n OIDCProvider.validateIssuer(config.issuer);\n\n // Determine initial endpoints - use manual overrides or placeholders for discovery\n const authorizationEndpoint = config.authorizationEndpoint ||\n `${config.issuer}/authorize`;\n const tokenEndpoint = config.tokenEndpoint ||\n `${config.issuer}/oauth/token`;\n\n // Validate manual endpoints if provided\n if (config.authorizationEndpoint) {\n OIDCProvider.validateEndpoint(config.authorizationEndpoint, 'authorization');\n }\n if (config.tokenEndpoint) {\n OIDCProvider.validateEndpoint(config.tokenEndpoint, 'token');\n }\n if (config.jwksUri) {\n OIDCProvider.validateEndpoint(config.jwksUri, 'jwks');\n }\n\n super({\n id: 'oidc',\n name: 'Generic OIDC',\n authorizationEndpoint,\n tokenEndpoint,\n defaultScopes: config.scopes || ['openid', 'profile'],\n tokenInjection: {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n },\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n });\n\n this.issuer = config.issuer;\n this.tokenEndpointAuthMethod = config.tokenEndpointAuthMethod || 'client_secret_post';\n this.discoveryTimeoutMs = config.discoveryTimeoutMs || OIDCProvider.DEFAULT_DISCOVERY_TIMEOUT_MS;\n this.skipDiscovery = config.skipDiscovery || false;\n this.manualJwksUri = config.jwksUri;\n\n // If manual endpoints are provided, mark discovery as not needed\n if (config.authorizationEndpoint && config.tokenEndpoint) {\n this.discoveryAttempted = true;\n }\n }\n\n /**\n * Validate the issuer URL.\n * @param issuer - The issuer URL to validate\n * @throws Error if issuer is invalid\n */\n private static validateIssuer(issuer: string): void {\n if (!issuer || typeof issuer !== 'string') {\n throw new Error('OIDC issuer is required');\n }\n\n const trimmed = issuer.trim();\n if (trimmed !== issuer) {\n throw new Error('OIDC issuer must not contain leading/trailing whitespace');\n }\n\n if (issuer.length === 0) {\n throw new Error('OIDC issuer cannot be empty');\n }\n\n // Parse and validate URL\n let url: URL;\n try {\n url = new URL(issuer);\n } catch {\n throw new Error(`OIDC issuer must be a valid URL: ${issuer}`);\n }\n\n // Must be HTTPS\n if (url.protocol !== 'https:') {\n throw new Error(`OIDC issuer must use HTTPS: ${issuer}`);\n }\n\n // Must not have embedded credentials\n if (url.username || url.password) {\n throw new Error('OIDC issuer must not contain embedded credentials');\n }\n\n // Must not have query string or fragment\n if (url.search || url.hash) {\n throw new Error('OIDC issuer must not contain query string or fragment');\n }\n }\n\n /**\n * Validate an endpoint URL.\n * @param endpoint - The endpoint URL to validate\n * @param name - The name of the endpoint for error messages\n * @throws Error if endpoint is invalid\n */\n private static validateEndpoint(endpoint: string, name: string): void {\n if (!endpoint || typeof endpoint !== 'string') {\n throw new Error(`OIDC ${name} endpoint is required`);\n }\n\n let url: URL;\n try {\n url = new URL(endpoint);\n } catch {\n throw new Error(`OIDC ${name} endpoint must be a valid URL: ${endpoint}`);\n }\n\n if (url.protocol !== 'https:') {\n throw new Error(`OIDC ${name} endpoint must use HTTPS: ${endpoint}`);\n }\n\n if (url.username || url.password) {\n throw new Error(`OIDC ${name} endpoint must not contain embedded credentials`);\n }\n }\n\n /**\n * Get the issuer URL.\n * @returns The issuer URL\n */\n getIssuer(): string {\n return this.issuer;\n }\n\n /**\n * Get the JWKS URI for token validation.\n * Returns the discovered or manually configured JWKS URI.\n * @returns The JWKS URI or undefined if not available\n */\n getJwksUri(): string | undefined {\n return this.discoveryDocument?.jwks_uri || this.manualJwksUri;\n }\n\n /**\n * Get the cached discovery document.\n * @returns The discovery document or undefined if not discovered\n */\n getDiscoveryDocument(): OIDCDiscoveryDocument | undefined {\n return this.discoveryDocument;\n }\n\n /**\n * Check if discovery has been performed.\n * @returns True if discovery was attempted\n */\n isDiscoveryAttempted(): boolean {\n return this.discoveryAttempted;\n }\n\n /**\n * Get the effective authorization endpoint.\n * Returns discovered endpoint if available, otherwise the initial endpoint.\n * @returns The authorization endpoint URL\n */\n getAuthorizationEndpoint(): string {\n return this.discoveredAuthorizationEndpoint || this.getEndpoints().authorizationEndpoint;\n }\n\n /**\n * Get the effective token endpoint.\n * Returns discovered endpoint if available, otherwise the initial endpoint.\n * @returns The token endpoint URL\n */\n getTokenEndpoint(): string {\n return this.discoveredTokenEndpoint || this.getEndpoints().tokenEndpoint;\n }\n\n /**\n * Perform OIDC discovery by fetching the .well-known/openid-configuration.\n *\n * This method fetches and parses the discovery document, updating the\n * provider's endpoints if successful.\n *\n * Requirements: 7a.1\n *\n * @returns The discovery result\n */\n async discover(): Promise<OIDCDiscoveryResult> {\n if (this.skipDiscovery) {\n return {\n success: false,\n error: 'Discovery skipped by configuration',\n };\n }\n\n const discoveryUrl = `${this.issuer}/.well-known/openid-configuration`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.discoveryTimeoutMs);\n\n try {\n const response = await fetch(discoveryUrl, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n signal: controller.signal,\n redirect: 'error', // Don't follow redirects for security\n });\n\n if (!response.ok) {\n this.discoveryAttempted = true;\n return {\n success: false,\n error: `Discovery failed: HTTP ${response.status} ${response.statusText}`,\n };\n }\n\n const document = await response.json() as OIDCDiscoveryDocument;\n\n // Validate required fields\n const validationError = this.validateDiscoveryDocument(document);\n if (validationError) {\n this.discoveryAttempted = true;\n return {\n success: false,\n error: validationError,\n };\n }\n\n // Cache the discovery document\n this.discoveryDocument = document;\n this.discoveryAttempted = true;\n\n // Store discovered endpoints\n this.discoveredAuthorizationEndpoint = document.authorization_endpoint;\n this.discoveredTokenEndpoint = document.token_endpoint;\n\n return {\n success: true,\n document,\n };\n } catch (error) {\n this.discoveryAttempted = true;\n\n if (error instanceof Error && error.name === 'AbortError') {\n return {\n success: false,\n error: `Discovery timed out after ${this.discoveryTimeoutMs}ms`,\n };\n }\n\n return {\n success: false,\n error: `Discovery failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Validate the discovery document.\n * @param document - The discovery document to validate\n * @returns Error message if invalid, undefined if valid\n */\n private validateDiscoveryDocument(document: OIDCDiscoveryDocument): string | undefined {\n if (!document.issuer) {\n return 'Discovery document missing required field: issuer';\n }\n\n if (!document.authorization_endpoint) {\n return 'Discovery document missing required field: authorization_endpoint';\n }\n\n if (!document.token_endpoint) {\n return 'Discovery document missing required field: token_endpoint';\n }\n\n // Validate issuer matches\n // Note: Some providers may have trailing slash differences\n const normalizedDocIssuer = document.issuer.replace(/\\/$/, '');\n const normalizedConfigIssuer = this.issuer.replace(/\\/$/, '');\n if (normalizedDocIssuer !== normalizedConfigIssuer) {\n return `Discovery document issuer mismatch: expected ${this.issuer}, got ${document.issuer}`;\n }\n\n // Validate endpoints are HTTPS\n try {\n OIDCProvider.validateEndpoint(document.authorization_endpoint, 'authorization');\n OIDCProvider.validateEndpoint(document.token_endpoint, 'token');\n if (document.jwks_uri) {\n OIDCProvider.validateEndpoint(document.jwks_uri, 'jwks');\n }\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n\n return undefined;\n }\n\n /**\n * Ensure discovery has been performed before operations that need endpoints.\n * If discovery hasn't been attempted and manual endpoints weren't provided,\n * this will perform discovery.\n */\n async ensureDiscovered(): Promise<void> {\n if (!this.discoveryAttempted && !this.skipDiscovery) {\n await this.discover();\n }\n }\n\n /**\n * Exchange authorization code for tokens.\n *\n * Overrides base implementation to support different token endpoint\n * authentication methods (client_secret_post, client_secret_basic).\n *\n * Requirements: 7a.7\n *\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n * @param redirectUri - The redirect URI used in the authorization request\n * @returns The token response\n */\n override async exchangeCode(\n code: string,\n codeVerifier: string,\n redirectUri: string\n ): Promise<TokenResponse> {\n // Ensure discovery has been performed\n await this.ensureDiscovered();\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n };\n\n // Handle different token endpoint auth methods\n if (this.tokenEndpointAuthMethod === 'client_secret_basic') {\n // client_secret_basic: credentials in Authorization header\n if (this.clientId && this.clientSecret) {\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');\n headers['Authorization'] = `Basic ${credentials}`;\n }\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n } else {\n // client_secret_post: credentials in request body (default)\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n // Use discovered endpoint if available\n const tokenEndpoint = this.getTokenEndpoint();\n\n try {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers,\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token exchange timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * Overrides base implementation to support different token endpoint\n * authentication methods.\n *\n * @param refreshToken - The refresh token\n * @returns The new token response\n */\n override async refreshToken(refreshToken: string): Promise<TokenResponse> {\n // Ensure discovery has been performed\n await this.ensureDiscovered();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Accept': 'application/json',\n };\n\n // Handle different token endpoint auth methods\n if (this.tokenEndpointAuthMethod === 'client_secret_basic') {\n if (this.clientId && this.clientSecret) {\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');\n headers['Authorization'] = `Basic ${credentials}`;\n }\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n } else {\n if (this.clientId) {\n body.set('client_id', this.clientId);\n }\n if (this.clientSecret) {\n body.set('client_secret', this.clientSecret);\n }\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS\n );\n\n // Use discovered endpoint if available\n const tokenEndpoint = this.getTokenEndpoint();\n\n try {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers,\n body: body.toString(),\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorBody}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return this.parseTokenResponse(data);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Token refresh timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // =============================================================================\n // JWKS Retrieval and Caching (Requirements: 7a.6)\n // =============================================================================\n\n /**\n * Fetch JWKS from the jwks_uri endpoint.\n *\n * Requirements: 7a.6\n *\n * @param forceRefresh - If true, bypasses cache and fetches fresh JWKS\n * @returns The JWKS or null if unavailable\n */\n async fetchJWKS(forceRefresh = false): Promise<JWKS | null> {\n // Check cache first (unless force refresh)\n if (!forceRefresh && this.cachedJWKS) {\n const now = Date.now();\n const cacheAge = now - this.cachedJWKS.fetchedAt;\n if (cacheAge < this.cachedJWKS.ttlMs) {\n return this.cachedJWKS.jwks;\n }\n }\n\n // Ensure discovery has been performed to get jwks_uri\n await this.ensureDiscovered();\n\n const jwksUri = this.getJwksUri();\n if (!jwksUri) {\n return null;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.discoveryTimeoutMs);\n\n try {\n const response = await fetch(jwksUri, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n },\n signal: controller.signal,\n redirect: 'error',\n });\n\n if (!response.ok) {\n throw new Error(`JWKS fetch failed: HTTP ${response.status} ${response.statusText}`);\n }\n\n const jwks = await response.json() as JWKS;\n\n // Validate JWKS structure\n if (!jwks.keys || !Array.isArray(jwks.keys)) {\n throw new Error('Invalid JWKS: missing or invalid keys array');\n }\n\n // Cache the JWKS\n this.cachedJWKS = {\n jwks,\n fetchedAt: Date.now(),\n ttlMs: OIDCProvider.DEFAULT_JWKS_CACHE_TTL_MS,\n };\n\n return jwks;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`JWKS fetch timed out after ${this.discoveryTimeoutMs}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Find a key in the JWKS by key ID (kid).\n *\n * If the key is not found in the cache, attempts to refresh the JWKS\n * to handle key rotation.\n *\n * Requirements: 7a.6 (key rotation handling)\n *\n * @param kid - The key ID to find\n * @returns The JWK or null if not found\n */\n async findKey(kid: string): Promise<JWK | null> {\n // First, try to find in cached JWKS\n let jwks = await this.fetchJWKS(false);\n if (jwks) {\n const key = jwks.keys.find(k => k.kid === kid);\n if (key) {\n return key;\n }\n }\n\n // Key not found - might be key rotation, refresh JWKS\n jwks = await this.fetchJWKS(true);\n if (jwks) {\n const key = jwks.keys.find(k => k.kid === kid);\n if (key) {\n return key;\n }\n }\n\n return null;\n }\n\n /**\n * Clear the JWKS cache.\n * Useful for testing or when key rotation is detected.\n */\n clearJWKSCache(): void {\n this.cachedJWKS = undefined;\n }\n\n /**\n * Get the cached JWKS if available.\n * @returns The cached JWKS or undefined\n */\n getCachedJWKS(): CachedJWKS | undefined {\n return this.cachedJWKS;\n }\n\n // =============================================================================\n // ID Token Validation (Requirements: 7a.5)\n // =============================================================================\n\n /**\n * Validate an ID token.\n *\n * Validates the following claims per OIDC Core spec:\n * - iss: Must match the configured issuer\n * - aud: Must contain the client_id\n * - exp: Must not be expired\n * - iat: Must be present and reasonable\n *\n * Also validates the JWT signature using JWKS.\n *\n * Requirements: 7a.5, 7a.6\n *\n * @param idToken - The ID token to validate\n * @param options - Validation options\n * @returns The validation result\n */\n async validateIdToken(\n idToken: string,\n options: IDTokenValidationOptions\n ): Promise<IDTokenValidationResult> {\n try {\n // Split the JWT into parts\n const parts = idToken.split('.');\n if (parts.length !== 3) {\n return { valid: false, error: 'Invalid JWT format: expected 3 parts' };\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode header\n let header: JWTHeader;\n try {\n header = JSON.parse(OIDCProvider.base64UrlDecode(headerB64));\n } catch {\n return { valid: false, error: 'Invalid JWT header: failed to decode' };\n }\n\n // Decode payload (claims)\n let claims: IDTokenClaims;\n try {\n claims = JSON.parse(OIDCProvider.base64UrlDecode(payloadB64));\n } catch {\n return { valid: false, error: 'Invalid JWT payload: failed to decode' };\n }\n\n // Validate signature\n const signatureValid = await this.validateJWTSignature(\n headerB64,\n payloadB64,\n signatureB64,\n header\n );\n if (!signatureValid) {\n return { valid: false, error: 'Invalid JWT signature' };\n }\n\n // Validate claims\n const claimsValidation = this.validateIDTokenClaims(claims, options);\n if (!claimsValidation.valid) {\n return claimsValidation;\n }\n\n return { valid: true, claims };\n } catch (error) {\n return {\n valid: false,\n error: `Token validation failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n /**\n * Validate the JWT signature using JWKS.\n *\n * Requirements: 7a.6\n *\n * @param headerB64 - Base64url encoded header\n * @param payloadB64 - Base64url encoded payload\n * @param signatureB64 - Base64url encoded signature\n * @param header - Decoded JWT header\n * @returns True if signature is valid\n */\n private async validateJWTSignature(\n headerB64: string,\n payloadB64: string,\n signatureB64: string,\n header: JWTHeader\n ): Promise<boolean> {\n // Only support RS256 for now (most common)\n if (header.alg !== 'RS256') {\n throw new Error(`Unsupported algorithm: ${header.alg}. Only RS256 is supported.`);\n }\n\n // Find the signing key\n if (!header.kid) {\n throw new Error('JWT header missing kid (key ID)');\n }\n\n const key = await this.findKey(header.kid);\n if (!key) {\n throw new Error(`Key not found in JWKS: ${header.kid}`);\n }\n\n // Verify the key is RSA\n if (key.kty !== 'RSA') {\n throw new Error(`Unsupported key type: ${key.kty}. Only RSA is supported.`);\n }\n\n if (!key.n || !key.e) {\n throw new Error('Invalid RSA key: missing n or e');\n }\n\n // Convert JWK to PEM format for Node.js crypto\n const publicKey = OIDCProvider.jwkToPem(key);\n\n // Verify signature\n const signedData = `${headerB64}.${payloadB64}`;\n const signature = OIDCProvider.base64UrlToBuffer(signatureB64);\n\n const verifier = crypto.createVerify('RSA-SHA256');\n verifier.update(signedData);\n\n return verifier.verify(publicKey, signature);\n }\n\n /**\n * Validate ID token claims.\n *\n * Requirements: 7a.5\n *\n * @param claims - The decoded claims\n * @param options - Validation options\n * @returns The validation result\n */\n private validateIDTokenClaims(\n claims: IDTokenClaims,\n options: IDTokenValidationOptions\n ): IDTokenValidationResult {\n const clockSkew = options.clockSkewSeconds ?? OIDCProvider.DEFAULT_CLOCK_SKEW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\n\n // Validate iss (issuer)\n // Normalize trailing slashes for comparison\n const normalizedClaimsIss = claims.iss?.replace(/\\/$/, '');\n const normalizedIssuer = this.issuer.replace(/\\/$/, '');\n if (!claims.iss || normalizedClaimsIss !== normalizedIssuer) {\n return {\n valid: false,\n error: `Invalid issuer: expected ${this.issuer}, got ${claims.iss}`,\n };\n }\n\n // Validate aud (audience)\n const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];\n if (!audiences.includes(options.audience)) {\n return {\n valid: false,\n error: `Invalid audience: expected ${options.audience}, got ${claims.aud}`,\n };\n }\n\n // Validate exp (expiration)\n if (typeof claims.exp !== 'number') {\n return { valid: false, error: 'Missing or invalid exp claim' };\n }\n if (claims.exp + clockSkew < now) {\n return { valid: false, error: 'Token has expired' };\n }\n\n // Validate iat (issued at)\n if (typeof claims.iat !== 'number') {\n return { valid: false, error: 'Missing or invalid iat claim' };\n }\n // iat should not be in the future (with clock skew tolerance)\n if (claims.iat - clockSkew > now) {\n return { valid: false, error: 'Token issued in the future' };\n }\n\n // Validate nonce if provided\n if (options.nonce !== undefined) {\n if (claims.nonce !== options.nonce) {\n return {\n valid: false,\n error: `Invalid nonce: expected ${options.nonce}, got ${claims.nonce}`,\n };\n }\n }\n\n return { valid: true, claims };\n }\n\n // =============================================================================\n // Utility Methods\n // =============================================================================\n\n /**\n * Decode a base64url encoded string to UTF-8.\n *\n * @param input - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\n private static base64UrlDecode(input: string): string {\n // Replace base64url characters with base64 characters\n let base64 = input.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary\n const padding = base64.length % 4;\n if (padding) {\n base64 += '='.repeat(4 - padding);\n }\n\n return Buffer.from(base64, 'base64').toString('utf-8');\n }\n\n /**\n * Convert a base64url encoded string to a Buffer.\n *\n * @param input - Base64url encoded string\n * @returns Buffer\n */\n private static base64UrlToBuffer(input: string): Buffer {\n // Replace base64url characters with base64 characters\n let base64 = input.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary\n const padding = base64.length % 4;\n if (padding) {\n base64 += '='.repeat(4 - padding);\n }\n\n return Buffer.from(base64, 'base64');\n }\n\n /**\n * Convert a JWK RSA public key to PEM format.\n *\n * @param jwk - The JWK to convert\n * @returns PEM formatted public key\n */\n private static jwkToPem(jwk: JWK): string {\n if (!jwk.n || !jwk.e) {\n throw new Error('Invalid JWK: missing n or e');\n }\n\n // Use Node.js crypto to create the key from JWK\n const keyObject = crypto.createPublicKey({\n key: {\n kty: jwk.kty,\n n: jwk.n,\n e: jwk.e,\n },\n format: 'jwk',\n });\n\n return keyObject.export({ type: 'spki', format: 'pem' }) as string;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Provider registry and factory.\n *\n * Provides access to OAuth provider implementations.\n *\n * @module providers\n */\n\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId as isValidProviderIdFromTypes } from '../types.js';\nimport type { IAuthProvider } from './types.js';\n\n/**\n * Provider factory function type.\n */\nexport type ProviderFactory = () => IAuthProvider;\n\n/**\n * Internal registry of provider factories.\n */\nconst providerRegistry = new Map<AuthProviderId, ProviderFactory>();\n\n/**\n * List of supported OAuth provider IDs.\n *\n * Note: OpenAI and Anthropic are NOT included - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n */\nexport const SUPPORTED_PROVIDERS: readonly AuthProviderId[] = [\n 'github',\n 'google',\n 'cognito',\n 'azure',\n 'oidc',\n] as const;\n\n/**\n * Register a provider factory.\n *\n * @param providerId - The provider identifier\n * @param factory - Factory function that creates the provider\n * @throws Error if providerId is not a valid supported provider ID\n */\nexport function registerProvider(providerId: AuthProviderId, factory: ProviderFactory): void {\n // Runtime validation to ensure only valid provider IDs are registered\n if (!isValidProviderIdFromTypes(providerId)) {\n throw new Error(\n `Invalid provider ID: '${providerId}'. Supported providers: ${SUPPORTED_PROVIDERS.join(', ')}`\n );\n }\n providerRegistry.set(providerId, factory);\n}\n\n/**\n * Unregister a provider.\n *\n * @param providerId - The provider identifier to unregister\n * @returns True if the provider was unregistered\n */\nexport function unregisterProvider(providerId: AuthProviderId): boolean {\n return providerRegistry.delete(providerId);\n}\n\n/**\n * Clear all registered providers.\n * Useful for testing.\n */\nexport function clearProviders(): void {\n providerRegistry.clear();\n}\n\n/**\n * Get a provider implementation by ID.\n *\n * @param providerId - The provider identifier\n * @returns The provider implementation\n * @throws Error if provider is not registered\n */\nexport function getProvider(providerId: AuthProviderId): IAuthProvider {\n const factory = providerRegistry.get(providerId);\n if (!factory) {\n const supported = getRegisteredProviders().join(', ') || 'none';\n throw new Error(\n `Provider '${providerId}' is not registered. Registered providers: ${supported}`\n );\n }\n return factory();\n}\n\n/**\n * Check if a provider is registered.\n *\n * @param providerId - The provider identifier\n * @returns True if the provider is registered\n */\nexport function hasProvider(providerId: AuthProviderId): boolean {\n return providerRegistry.has(providerId);\n}\n\n/**\n * Get the list of registered provider IDs.\n *\n * @returns Array of registered provider identifiers\n */\nexport function getRegisteredProviders(): AuthProviderId[] {\n return Array.from(providerRegistry.keys());\n}\n\n/**\n * Get the list of supported provider IDs.\n *\n * @returns Array of supported provider identifiers\n */\nexport function getSupportedProviders(): readonly AuthProviderId[] {\n return SUPPORTED_PROVIDERS;\n}\n\n/**\n * Check if a provider ID is valid (supported).\n * Re-exports the centralized type guard from types.ts.\n *\n * @param providerId - The provider identifier to check\n * @returns True if the provider is supported\n */\nexport function isValidProviderId(providerId: unknown): providerId is AuthProviderId {\n return isValidProviderIdFromTypes(providerId);\n}\n\n/**\n * Check if a provider ID is registered and available.\n *\n * @param providerId - The provider identifier to check\n * @returns True if the provider is registered\n */\nexport function isProviderAvailable(providerId: string): boolean {\n return isValidProviderId(providerId) && hasProvider(providerId as AuthProviderId);\n}\n\n// Re-export types\nexport type { IAuthProvider } from './types.js';\nexport { BaseAuthProvider } from './base-provider.js';\nexport type { BaseProviderConfig } from './base-provider.js';\n\n// Re-export concrete providers\nexport { GitHubProvider } from './github-provider.js';\nexport { GoogleProvider } from './google-provider.js';\nexport { CognitoProvider } from './cognito-provider.js';\nexport type { CognitoProviderConfig } from './cognito-provider.js';\n// Microsoft Entra ID (formerly Azure AD) - export both new and legacy names\nexport { EntraIdProvider, AzureProvider } from './entra-provider.js';\nexport type { EntraProviderConfig, AzureProviderConfig } from './entra-provider.js';\nexport { OIDCProvider } from './oidc-provider.js';\nexport type { OIDCProviderConfig, OIDCDiscoveryDocument, OIDCDiscoveryResult } from './oidc-provider.js';\n\n// Import providers for registration\nimport { GitHubProvider } from './github-provider.js';\nimport { GoogleProvider } from './google-provider.js';\nimport { CognitoProvider } from './cognito-provider.js';\nimport { EntraIdProvider } from './entra-provider.js';\nimport { OIDCProvider } from './oidc-provider.js';\n\n/**\n * Initialize all OAuth providers.\n * Must be called before using AuthManager.\n *\n * This registers all supported OAuth provider implementations\n * in the provider registry.\n *\n * Note: OpenAI and Anthropic are NOT registered here - they use API keys, not OAuth.\n * See model-credentials module for API key handling.\n *\n * Note: Cognito, Azure, and OIDC require environment-specific configuration\n * and are only registered if their config is available via env vars.\n */\nexport function initializeProviders(): void {\n // Only register if not already registered (idempotent)\n if (!providerRegistry.has('github')) {\n registerProvider('github', () => new GitHubProvider());\n }\n if (!providerRegistry.has('google')) {\n registerProvider('google', () => new GoogleProvider());\n }\n\n // Cognito requires userPoolDomain and region from environment\n const cognitoUserPoolDomain = process.env.COGNITO_USER_POOL_DOMAIN;\n const cognitoRegion = process.env.COGNITO_REGION || 'us-east-1';\n if (cognitoUserPoolDomain && !providerRegistry.has('cognito')) {\n registerProvider('cognito', () => new CognitoProvider({\n userPoolDomain: cognitoUserPoolDomain,\n region: cognitoRegion,\n }));\n }\n\n // Microsoft Entra ID (formerly Azure AD) requires tenantId from environment\n const azureTenantId = process.env.AZURE_TENANT_ID;\n if (azureTenantId && !providerRegistry.has('azure')) {\n registerProvider('azure', () => new EntraIdProvider({\n tenantId: azureTenantId,\n }));\n }\n\n // OIDC requires issuer from environment\n // Manual endpoint overrides are optional (discovery is used by default)\n const oidcIssuer = process.env.OIDC_ISSUER;\n if (oidcIssuer && !providerRegistry.has('oidc')) {\n registerProvider('oidc', () => new OIDCProvider({\n issuer: oidcIssuer,\n authorizationEndpoint: process.env.OIDC_AUTHORIZATION_ENDPOINT,\n tokenEndpoint: process.env.OIDC_TOKEN_ENDPOINT,\n jwksUri: process.env.OIDC_JWKS_URI,\n clientId: process.env.OIDC_CLIENT_ID,\n clientSecret: process.env.OIDC_CLIENT_SECRET,\n tokenEndpointAuthMethod: process.env.OIDC_TOKEN_ENDPOINT_AUTH_METHOD as 'client_secret_post' | 'client_secret_basic' | undefined,\n }));\n }\n}\n\n// Auto-initialize providers on module load\ninitializeProviders();\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Type definitions for model API credentials.\n *\n * This module defines types for upstream model provider credentials (API keys).\n * These providers (OpenAI, Anthropic) do NOT offer public OAuth IdP for\n * third-party login - they use API keys instead.\n *\n * This separation clearly distinguishes:\n * - User identity (OAuth/OIDC): Google, Microsoft Entra ID, AWS Cognito, GitHub, Generic OIDC\n * - Model API access (API Keys): OpenAI, Anthropic\n *\n * Requirements: 7b.1, 7b.3\n *\n * @module model-credentials/types\n */\n\n// =============================================================================\n// Model Provider Types\n// =============================================================================\n\n/**\n * Model API provider identifiers.\n *\n * These providers use API keys for authentication, NOT OAuth.\n * They do not offer public OAuth IdP for third-party login.\n *\n * Requirements: 7b.1, 7b.2\n */\nexport type ModelProviderId = 'openai' | 'anthropic';\n\n/**\n * Valid model provider IDs for runtime validation.\n */\nexport const VALID_MODEL_PROVIDER_IDS: readonly ModelProviderId[] = [\n 'openai',\n 'anthropic',\n] as const;\n\n/**\n * Type guard to check if a value is a valid ModelProviderId.\n *\n * @param value - The value to check\n * @returns True if the value is a valid ModelProviderId\n */\nexport function isValidModelProviderId(value: unknown): value is ModelProviderId {\n return typeof value === 'string' && VALID_MODEL_PROVIDER_IDS.includes(value as ModelProviderId);\n}\n\n// =============================================================================\n// Model Credential Types\n// =============================================================================\n\n/**\n * Model credential for storing API keys.\n *\n * Represents an API key credential for a model provider.\n * These credentials are stored securely in the Credential_Store.\n *\n * Requirements: 7b.1, 7b.4\n */\nexport interface ModelCredential {\n /**\n * The model provider this credential is for.\n */\n providerId: ModelProviderId;\n\n /**\n * The API key value.\n * This is stored encrypted in the Credential_Store.\n */\n apiKey: string;\n\n /**\n * Optional human-readable label for this credential.\n * Useful when multiple keys are stored for the same provider.\n */\n label?: string;\n\n /**\n * Unix timestamp when this credential was stored.\n */\n storedAt: number;\n\n /**\n * Optional Unix timestamp when this credential expires.\n * Most API keys don't expire, but some providers may issue\n * time-limited keys.\n */\n expiresAt?: number;\n}\n\n// =============================================================================\n// Model Credential Injection Types\n// =============================================================================\n\n/**\n * Header injection type for model credentials.\n */\nexport interface HeaderInjection {\n type: 'header';\n /**\n * The header name to use.\n * e.g., 'Authorization' for OpenAI, 'x-api-key' for Anthropic\n */\n headerName: string;\n /**\n * Optional format string for the header value.\n * Use '{key}' as placeholder for the API key.\n * e.g., 'Bearer {key}' for OpenAI\n * If not provided, the API key is used directly.\n */\n format?: string;\n}\n\n/**\n * Model credential injection configuration.\n *\n * Defines how API keys should be injected into requests\n * for each model provider.\n *\n * Requirements: 7b.5\n */\nexport type ModelCredentialInjection = HeaderInjection;\n\n/**\n * Provider-specific injection configurations.\n *\n * OpenAI: Authorization header with Bearer token\n * Anthropic: x-api-key header with raw key\n *\n * Requirements: 7b.5\n */\nexport const MODEL_CREDENTIAL_INJECTION_CONFIG: Readonly<\n Record<ModelProviderId, ModelCredentialInjection>\n> = {\n openai: {\n type: 'header',\n headerName: 'Authorization',\n format: 'Bearer {key}',\n },\n anthropic: {\n type: 'header',\n headerName: 'x-api-key',\n // No format - raw key is used directly\n },\n} as const;\n\n// =============================================================================\n// Stored Model Credential Types\n// =============================================================================\n\n/**\n * Stored model credentials in the credential store.\n *\n * This is the format used when persisting model credentials\n * to the Credential_Store.\n *\n * Requirements: 7b.4\n */\nexport interface StoredModelCredential {\n /**\n * The model provider this credential is for.\n */\n providerId: ModelProviderId;\n\n /**\n * The encrypted API key value.\n * Encryption is handled by the storage backend.\n */\n apiKey: string;\n\n /**\n * Optional human-readable label.\n */\n label?: string;\n\n /**\n * Unix timestamp when this credential was stored.\n */\n storedAt: number;\n\n /**\n * Optional Unix timestamp when this credential expires.\n */\n expiresAt?: number;\n}\n\n// =============================================================================\n// Model Credential Result Types\n// =============================================================================\n\n/**\n * Result of a model credential retrieval operation.\n */\nexport interface ModelCredentialResult {\n /**\n * Whether the credential was found.\n */\n found: boolean;\n\n /**\n * The credential, if found.\n */\n credential?: ModelCredential;\n\n /**\n * Error message if retrieval failed.\n */\n error?: string;\n}\n\n// =============================================================================\n// Model Credential Status Types\n// =============================================================================\n\n/**\n * Status of a model credential.\n */\nexport type ModelCredentialStatus =\n | 'configured' // API key is stored and available\n | 'not-configured' // No API key stored\n | 'expired'; // API key has expired (if expiration is set)\n\n/**\n * Model credential status entry for display.\n */\nexport interface ModelCredentialStatusEntry {\n /**\n * The model provider.\n */\n providerId: ModelProviderId;\n\n /**\n * Current status of the credential.\n */\n status: ModelCredentialStatus;\n\n /**\n * Optional label for the credential.\n */\n label?: string;\n\n /**\n * Unix timestamp when the credential was stored.\n */\n storedAt?: number;\n\n /**\n * Unix timestamp when the credential expires.\n */\n expiresAt?: number;\n}\n\n/**\n * Map of model provider IDs to their credential status.\n */\nexport type ModelCredentialStatusMap = Map<ModelProviderId, ModelCredentialStatusEntry>;\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * OpenAI API Key Handler.\n *\n * Handles storage, retrieval, validation, and injection of OpenAI API keys.\n * OpenAI uses the Authorization header with Bearer token format.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n *\n * @module model-credentials/openai-api-key\n */\n\nimport type {\n ModelCredential,\n ModelProviderId,\n StoredModelCredential,\n ModelCredentialResult,\n ModelCredentialStatusEntry,\n HeaderInjection,\n} from './types.js';\nimport { MODEL_CREDENTIAL_INJECTION_CONFIG } from './types.js';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * The provider ID for OpenAI.\n */\nexport const OPENAI_PROVIDER_ID: ModelProviderId = 'openai';\n\n/**\n * OpenAI API key prefix for validation.\n * OpenAI API keys typically start with 'sk-'.\n */\nexport const OPENAI_API_KEY_PREFIX = 'sk-';\n\n/**\n * Minimum length for OpenAI API keys.\n * OpenAI keys are typically 51+ characters.\n */\nexport const OPENAI_API_KEY_MIN_LENGTH = 20;\n\n/**\n * Storage key prefix for OpenAI credentials.\n */\nexport const OPENAI_STORAGE_KEY = 'model-credential:openai';\n\n// =============================================================================\n// Storage Interface\n// =============================================================================\n\n/**\n * Interface for credential storage operations.\n * This allows the handler to work with any storage backend.\n */\nexport interface IModelCredentialStorage {\n /**\n * Store a model credential.\n * @param key - The storage key\n * @param credential - The credential to store\n */\n store(key: string, credential: StoredModelCredential): Promise<void>;\n\n /**\n * Retrieve a model credential.\n * @param key - The storage key\n * @returns The stored credential or null if not found\n */\n retrieve(key: string): Promise<StoredModelCredential | null>;\n\n /**\n * Delete a model credential.\n * @param key - The storage key\n */\n delete(key: string): Promise<void>;\n\n /**\n * Check if a credential exists.\n * @param key - The storage key\n * @returns True if the credential exists\n */\n exists(key: string): Promise<boolean>;\n}\n\n// =============================================================================\n// OpenAI API Key Handler\n// =============================================================================\n\n/**\n * OpenAI API Key Handler.\n *\n * Provides methods for storing, retrieving, validating, and injecting\n * OpenAI API keys. Integrates with the Credential_Store for secure storage.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n */\nexport class OpenAIApiKeyHandler {\n private readonly storage: IModelCredentialStorage;\n\n /**\n * Create a new OpenAI API key handler.\n * @param storage - The credential storage backend\n */\n constructor(storage: IModelCredentialStorage) {\n this.storage = storage;\n }\n\n /**\n * Get the provider ID for this handler.\n * @returns The OpenAI provider ID\n */\n getProviderId(): ModelProviderId {\n return OPENAI_PROVIDER_ID;\n }\n\n /**\n * Get the injection configuration for OpenAI.\n *\n * OpenAI uses the Authorization header with Bearer token format:\n * Authorization: Bearer {key}\n *\n * Requirements: 7b.5\n *\n * @returns The header injection configuration\n */\n getInjectionConfig(): HeaderInjection {\n return MODEL_CREDENTIAL_INJECTION_CONFIG.openai;\n }\n\n /**\n * Validate an OpenAI API key format.\n *\n * Performs basic format validation:\n * - Must be a non-empty string\n * - Must meet minimum length requirement\n * - Optionally checks for 'sk-' prefix (warning only)\n *\n * Note: This does not validate the key against OpenAI's API.\n * Use validateWithApi() for full validation.\n *\n * @param apiKey - The API key to validate\n * @returns Validation result with success flag and optional warning\n */\n validateFormat(apiKey: string): { valid: boolean; warning?: string } {\n if (!apiKey || typeof apiKey !== 'string') {\n return { valid: false };\n }\n\n const trimmedKey = apiKey.trim();\n\n if (trimmedKey.length < OPENAI_API_KEY_MIN_LENGTH) {\n return { valid: false };\n }\n\n // Check for expected prefix (warning only, not a hard requirement)\n if (!trimmedKey.startsWith(OPENAI_API_KEY_PREFIX)) {\n return {\n valid: true,\n warning: `API key does not start with expected prefix '${OPENAI_API_KEY_PREFIX}'`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Store an OpenAI API key in the credential store.\n *\n * The key is stored with encryption handled by the storage backend.\n *\n * Requirements: 7b.4\n *\n * @param apiKey - The API key to store\n * @param label - Optional human-readable label\n * @returns Promise that resolves when stored\n * @throws Error if the API key format is invalid\n */\n async store(apiKey: string, label?: string): Promise<void> {\n const validation = this.validateFormat(apiKey);\n if (!validation.valid) {\n throw new Error('Invalid OpenAI API key format');\n }\n\n const credential: StoredModelCredential = {\n providerId: OPENAI_PROVIDER_ID,\n apiKey: apiKey.trim(),\n label,\n storedAt: Date.now(),\n };\n\n await this.storage.store(OPENAI_STORAGE_KEY, credential);\n }\n\n /**\n * Retrieve the stored OpenAI API key.\n *\n * Requirements: 7b.4\n *\n * @returns The credential result with the API key if found\n */\n async retrieve(): Promise<ModelCredentialResult> {\n try {\n const stored = await this.storage.retrieve(OPENAI_STORAGE_KEY);\n\n if (!stored) {\n return { found: false };\n }\n\n // Check for expiration if set\n if (stored.expiresAt && stored.expiresAt < Date.now()) {\n return {\n found: false,\n error: 'API key has expired',\n };\n }\n\n const credential: ModelCredential = {\n providerId: stored.providerId,\n apiKey: stored.apiKey,\n label: stored.label,\n storedAt: stored.storedAt,\n expiresAt: stored.expiresAt,\n };\n\n return { found: true, credential };\n } catch (error) {\n return {\n found: false,\n error: error instanceof Error ? error.message : 'Failed to retrieve credential',\n };\n }\n }\n\n /**\n * Delete the stored OpenAI API key.\n *\n * @returns Promise that resolves when deleted\n */\n async delete(): Promise<void> {\n await this.storage.delete(OPENAI_STORAGE_KEY);\n }\n\n /**\n * Check if an OpenAI API key is configured.\n *\n * @returns True if a valid API key is stored\n */\n async isConfigured(): Promise<boolean> {\n const result = await this.retrieve();\n return result.found;\n }\n\n /**\n * Get the status of the OpenAI API key credential.\n *\n * @returns The credential status entry\n */\n async getStatus(): Promise<ModelCredentialStatusEntry> {\n const result = await this.retrieve();\n\n if (!result.found) {\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'not-configured',\n };\n }\n\n const credential = result.credential!;\n\n // Check for expiration\n if (credential.expiresAt && credential.expiresAt < Date.now()) {\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'expired',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n return {\n providerId: OPENAI_PROVIDER_ID,\n status: 'configured',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n /**\n * Inject the OpenAI API key into request headers.\n *\n * Creates the Authorization header with Bearer token format:\n * Authorization: Bearer {key}\n *\n * Requirements: 7b.5\n *\n * @param headers - Existing headers object (will be modified)\n * @returns The headers object with the Authorization header added\n * @throws Error if no API key is configured\n */\n async injectHeader(headers: Record<string, string> = {}): Promise<Record<string, string>> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No OpenAI API key configured');\n }\n\n const injection = this.getInjectionConfig();\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n ...headers,\n [injection.headerName]: headerValue,\n };\n }\n\n /**\n * Get the header injection for a request.\n *\n * Returns the header name and value for injecting the API key.\n * This is useful when you need the header separately from the request.\n *\n * Requirements: 7b.5\n *\n * @returns Object with headerName and headerValue\n * @throws Error if no API key is configured\n */\n async getHeaderInjection(): Promise<{ headerName: string; headerValue: string }> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No OpenAI API key configured');\n }\n\n const injection = this.getInjectionConfig();\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n headerName: injection.headerName,\n headerValue,\n };\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a new OpenAI API key handler.\n *\n * @param storage - The credential storage backend\n * @returns A new OpenAI API key handler instance\n */\nexport function createOpenAIApiKeyHandler(storage: IModelCredentialStorage): OpenAIApiKeyHandler {\n return new OpenAIApiKeyHandler(storage);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Anthropic API Key Handler.\n *\n * Handles storage, retrieval, validation, and injection of Anthropic API keys.\n * Anthropic uses the x-api-key header with the raw key (no Bearer prefix).\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n *\n * @module model-credentials/anthropic-api-key\n */\n\nimport type {\n ModelCredential,\n ModelProviderId,\n StoredModelCredential,\n ModelCredentialResult,\n ModelCredentialStatusEntry,\n HeaderInjection,\n} from './types.js';\nimport { MODEL_CREDENTIAL_INJECTION_CONFIG } from './types.js';\nimport type { IModelCredentialStorage } from './openai-api-key.js';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * The provider ID for Anthropic.\n */\nexport const ANTHROPIC_PROVIDER_ID: ModelProviderId = 'anthropic';\n\n/**\n * Anthropic API key prefix for validation.\n * Anthropic API keys typically start with 'sk-ant-'.\n */\nexport const ANTHROPIC_API_KEY_PREFIX = 'sk-ant-';\n\n/**\n * Minimum length for Anthropic API keys.\n * Anthropic keys are typically 40+ characters.\n */\nexport const ANTHROPIC_API_KEY_MIN_LENGTH = 20;\n\n/**\n * Storage key prefix for Anthropic credentials.\n */\nexport const ANTHROPIC_STORAGE_KEY = 'model-credential:anthropic';\n\n// =============================================================================\n// Anthropic API Key Handler\n// =============================================================================\n\n/**\n * Anthropic API Key Handler.\n *\n * Provides methods for storing, retrieving, validating, and injecting\n * Anthropic API keys. Integrates with the Credential_Store for secure storage.\n *\n * Requirements: 7b.1, 7b.4, 7b.5\n */\nexport class AnthropicApiKeyHandler {\n private readonly storage: IModelCredentialStorage;\n\n /**\n * Create a new Anthropic API key handler.\n * @param storage - The credential storage backend\n */\n constructor(storage: IModelCredentialStorage) {\n this.storage = storage;\n }\n\n /**\n * Get the provider ID for this handler.\n * @returns The Anthropic provider ID\n */\n getProviderId(): ModelProviderId {\n return ANTHROPIC_PROVIDER_ID;\n }\n\n /**\n * Get the injection configuration for Anthropic.\n *\n * Anthropic uses the x-api-key header with the raw key:\n * x-api-key: {key}\n *\n * Requirements: 7b.5\n *\n * @returns The header injection configuration\n */\n getInjectionConfig(): HeaderInjection {\n return MODEL_CREDENTIAL_INJECTION_CONFIG.anthropic;\n }\n\n /**\n * Validate an Anthropic API key format.\n *\n * Performs basic format validation:\n * - Must be a non-empty string\n * - Must meet minimum length requirement\n * - Optionally checks for 'sk-ant-' prefix (warning only)\n *\n * Note: This does not validate the key against Anthropic's API.\n * Use validateWithApi() for full validation.\n *\n * @param apiKey - The API key to validate\n * @returns Validation result with success flag and optional warning\n */\n validateFormat(apiKey: string): { valid: boolean; warning?: string } {\n if (!apiKey || typeof apiKey !== 'string') {\n return { valid: false };\n }\n\n const trimmedKey = apiKey.trim();\n\n if (trimmedKey.length < ANTHROPIC_API_KEY_MIN_LENGTH) {\n return { valid: false };\n }\n\n // Check for expected prefix (warning only, not a hard requirement)\n if (!trimmedKey.startsWith(ANTHROPIC_API_KEY_PREFIX)) {\n return {\n valid: true,\n warning: `API key does not start with expected prefix '${ANTHROPIC_API_KEY_PREFIX}'`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Store an Anthropic API key in the credential store.\n *\n * The key is stored with encryption handled by the storage backend.\n *\n * Requirements: 7b.4\n *\n * @param apiKey - The API key to store\n * @param label - Optional human-readable label\n * @returns Promise that resolves when stored\n * @throws Error if the API key format is invalid\n */\n async store(apiKey: string, label?: string): Promise<void> {\n const validation = this.validateFormat(apiKey);\n if (!validation.valid) {\n throw new Error('Invalid Anthropic API key format');\n }\n\n const credential: StoredModelCredential = {\n providerId: ANTHROPIC_PROVIDER_ID,\n apiKey: apiKey.trim(),\n label,\n storedAt: Date.now(),\n };\n\n await this.storage.store(ANTHROPIC_STORAGE_KEY, credential);\n }\n\n /**\n * Retrieve the stored Anthropic API key.\n *\n * Requirements: 7b.4\n *\n * @returns The credential result with the API key if found\n */\n async retrieve(): Promise<ModelCredentialResult> {\n try {\n const stored = await this.storage.retrieve(ANTHROPIC_STORAGE_KEY);\n\n if (!stored) {\n return { found: false };\n }\n\n // Check for expiration if set\n if (stored.expiresAt && stored.expiresAt < Date.now()) {\n return {\n found: false,\n error: 'API key has expired',\n };\n }\n\n const credential: ModelCredential = {\n providerId: stored.providerId,\n apiKey: stored.apiKey,\n label: stored.label,\n storedAt: stored.storedAt,\n expiresAt: stored.expiresAt,\n };\n\n return { found: true, credential };\n } catch (error) {\n return {\n found: false,\n error: error instanceof Error ? error.message : 'Failed to retrieve credential',\n };\n }\n }\n\n /**\n * Delete the stored Anthropic API key.\n *\n * @returns Promise that resolves when deleted\n */\n async delete(): Promise<void> {\n await this.storage.delete(ANTHROPIC_STORAGE_KEY);\n }\n\n /**\n * Check if an Anthropic API key is configured.\n *\n * @returns True if a valid API key is stored\n */\n async isConfigured(): Promise<boolean> {\n const result = await this.retrieve();\n return result.found;\n }\n\n /**\n * Get the status of the Anthropic API key credential.\n *\n * @returns The credential status entry\n */\n async getStatus(): Promise<ModelCredentialStatusEntry> {\n const result = await this.retrieve();\n\n if (!result.found) {\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'not-configured',\n };\n }\n\n const credential = result.credential!;\n\n // Check for expiration\n if (credential.expiresAt && credential.expiresAt < Date.now()) {\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'expired',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n return {\n providerId: ANTHROPIC_PROVIDER_ID,\n status: 'configured',\n label: credential.label,\n storedAt: credential.storedAt,\n expiresAt: credential.expiresAt,\n };\n }\n\n /**\n * Inject the Anthropic API key into request headers.\n *\n * Creates the x-api-key header with the raw key:\n * x-api-key: {key}\n *\n * Requirements: 7b.5\n *\n * @param headers - Existing headers object (will be modified)\n * @returns The headers object with the x-api-key header added\n * @throws Error if no API key is configured\n */\n async injectHeader(headers: Record<string, string> = {}): Promise<Record<string, string>> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No Anthropic API key configured');\n }\n\n const injection = this.getInjectionConfig();\n // Anthropic uses raw key without format transformation\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n ...headers,\n [injection.headerName]: headerValue,\n };\n }\n\n /**\n * Get the header injection for a request.\n *\n * Returns the header name and value for injecting the API key.\n * This is useful when you need the header separately from the request.\n *\n * Requirements: 7b.5\n *\n * @returns Object with headerName and headerValue\n * @throws Error if no API key is configured\n */\n async getHeaderInjection(): Promise<{ headerName: string; headerValue: string }> {\n const result = await this.retrieve();\n\n if (!result.found || !result.credential) {\n throw new Error('No Anthropic API key configured');\n }\n\n const injection = this.getInjectionConfig();\n // Anthropic uses raw key without format transformation\n const headerValue = injection.format\n ? injection.format.replace('{key}', result.credential.apiKey)\n : result.credential.apiKey;\n\n return {\n headerName: injection.headerName,\n headerValue,\n };\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a new Anthropic API key handler.\n *\n * @param storage - The credential storage backend\n * @returns A new Anthropic API key handler instance\n */\nexport function createAnthropicApiKeyHandler(storage: IModelCredentialStorage): AnthropicApiKeyHandler {\n return new AnthropicApiKeyHandler(storage);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main orchestrator for OAuth authentication and model credentials.\n *\n * Coordinates providers, flows, storage, and token management.\n * Clearly separates user identity (OAuth/OIDC) from upstream model credentials (API keys).\n *\n * Requirements: 3.1, 4.1, 7b.3, 10.3, 11.4\n *\n * @module auth-manager\n */\n\nimport type { AgentApiKeys } from '../config/api-keys.js';\nimport type { ICredentialStore } from './storage/types.js';\nimport type { ITokenManager } from './token-manager.js';\nimport type { IAuthProvider } from './providers/types.js';\nimport { getProvider, hasProvider, getRegisteredProviders } from './providers/index.js';\nimport { AgentAuthFlow } from './flows/agent-auth-flow.js';\nimport { TerminalAuthFlow } from './flows/terminal-auth-flow.js';\nimport type {\n AuthProviderId,\n AuthResult,\n AuthStatusMap,\n AuthStatusEntry,\n AgentAuthOptions,\n AuthMethodType,\n AuthMethodPrecedenceConfig,\n} from './types.js';\nimport {\n isValidProviderId,\n VALID_PROVIDER_IDS,\n DEFAULT_AUTH_METHOD_PRECEDENCE,\n isValidAuthMethodType,\n} from './types.js';\nimport type {\n ModelProviderId,\n ModelCredentialResult,\n ModelCredentialStatusMap,\n} from './model-credentials/index.js';\nimport {\n isValidModelProviderId,\n VALID_MODEL_PROVIDER_IDS,\n MODEL_CREDENTIAL_INJECTION_CONFIG,\n OpenAIApiKeyHandler,\n AnthropicApiKeyHandler,\n} from './model-credentials/index.js';\nimport type { IModelCredentialStorage } from './model-credentials/openai-api-key.js';\n\n/**\n * Marker token used to indicate client credentials are configured but not authenticated.\n * This token should NEVER be sent in actual requests.\n */\nexport const CLIENT_CREDENTIALS_MARKER = '__CLIENT_CREDENTIALS_CONFIGURED__';\n\n/**\n * Check if a token is the client credentials marker (not a real token).\n * @param token - The token to check\n * @returns True if the token is the marker\n */\nexport function isMarkerToken(token: string | null | undefined): boolean {\n return token === CLIENT_CREDENTIALS_MARKER;\n}\n\n/**\n * Options for creating an AuthManager.\n */\nexport interface AuthManagerOptions {\n /** Credential store for persisting OAuth credentials */\n credentialStore: ICredentialStore;\n /** Token manager for token lifecycle management */\n tokenManager: ITokenManager;\n /** Legacy API keys from api-keys.json */\n legacyApiKeys: Record<string, AgentApiKeys>;\n /** Optional custom provider resolver (for testing) */\n providerResolver?: (providerId: AuthProviderId) => IAuthProvider;\n /**\n * Authentication method precedence configuration.\n * Controls which auth method is preferred when multiple are available.\n * Default: oauth2 > api-key (OAuth preferred when available)\n *\n * Requirements: 3.1, 10.3\n */\n methodPrecedence?: Partial<AuthMethodPrecedenceConfig>;\n /**\n * Optional model credential storage for API key management.\n * When provided, enables getModelCredential() and related methods.\n *\n * Requirements: 7b.3, 7b.4\n */\n modelCredentialStorage?: IModelCredentialStorage;\n}\n\n/**\n * Result of authentication method selection.\n */\nexport interface AuthMethodSelectionResult {\n /** The selected authentication method type */\n methodType: AuthMethodType;\n /** The provider ID to use (for oauth2) */\n providerId?: AuthProviderId;\n /** Whether a valid credential was found */\n hasCredential: boolean;\n /** Error message if selection failed */\n error?: string;\n}\n\n/**\n * Error thrown when authentication method selection fails.\n */\nexport class AuthMethodSelectionError extends Error {\n constructor(\n message: string,\n public readonly code: 'UNSUPPORTED_METHOD' | 'AMBIGUOUS_PROVIDER' | 'NO_CREDENTIALS',\n public readonly details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'AuthMethodSelectionError';\n }\n}\n\n/**\n * Main orchestrator for OAuth authentication and model credentials.\n * Coordinates providers, flows, storage, and token management.\n *\n * This class clearly separates:\n * - User identity (OAuth/OIDC): getTokenForAgent(), authenticateAgent()\n * - Model API access (API Keys): getModelCredential(), injectModelAuth()\n *\n * Responsibilities:\n * - Orchestrate agent auth flow (browser-based OAuth 2.1 with PKCE)\n * - Orchestrate terminal auth flow (interactive CLI setup)\n * - Manage credential precedence (OAuth over legacy api-keys.json)\n * - Inject authentication into agent requests\n * - Report authentication status\n * - Handle logout operations\n * - Manage model API credentials (OpenAI, Anthropic)\n *\n * Method Precedence Strategy (Requirements 3.1, 10.3):\n * - Default precedence: oauth2 > api-key (OAuth preferred when available)\n * - Configurable via AuthConfig.methodPrecedence\n * - Fail-fast on unsupported or ambiguous providerId (configurable)\n *\n * Requirements: 3.1, 4.1, 7b.3, 10.3, 11.4\n */\nexport class AuthManager {\n private readonly credentialStore: ICredentialStore;\n private readonly tokenManager: ITokenManager;\n private readonly legacyApiKeys: Record<string, AgentApiKeys>;\n private readonly providerResolver: (providerId: AuthProviderId) => IAuthProvider;\n private readonly methodPrecedenceConfig: AuthMethodPrecedenceConfig;\n\n /**\n * Model credential handlers for API key management.\n * These are separate from OAuth providers - they handle API keys for model providers.\n *\n * Requirements: 7b.3\n */\n private readonly openAIHandler?: OpenAIApiKeyHandler;\n private readonly anthropicHandler?: AnthropicApiKeyHandler;\n\n /**\n * Tracks in-flight authentication flows per provider.\n * Used to implement single-flight pattern: concurrent auth requests for the same\n * provider share the same Promise and receive the same result.\n *\n * Requirements: 3.1, 6.5\n */\n private readonly inFlightAuthFlows: Map<AuthProviderId, Promise<AuthResult>> = new Map();\n\n /**\n * Create a new AuthManager.\n *\n * @param options - Configuration options\n */\n constructor(options: AuthManagerOptions);\n /**\n * Create a new AuthManager (legacy constructor signature).\n *\n * @param credentialStore - Credential store for persisting OAuth credentials\n * @param tokenManager - Token manager for token lifecycle management\n * @param legacyApiKeys - Legacy API keys from api-keys.json\n */\n constructor(\n credentialStore: ICredentialStore,\n tokenManager: ITokenManager,\n legacyApiKeys: Record<string, AgentApiKeys>\n );\n constructor(\n optionsOrCredentialStore: AuthManagerOptions | ICredentialStore,\n tokenManager?: ITokenManager,\n legacyApiKeys?: Record<string, AgentApiKeys>\n ) {\n if (this.isAuthManagerOptions(optionsOrCredentialStore)) {\n // New options-based constructor\n this.credentialStore = optionsOrCredentialStore.credentialStore;\n this.tokenManager = optionsOrCredentialStore.tokenManager;\n this.legacyApiKeys = optionsOrCredentialStore.legacyApiKeys;\n this.providerResolver = optionsOrCredentialStore.providerResolver ?? getProvider;\n // Merge user-provided precedence config with defaults\n this.methodPrecedenceConfig = {\n ...DEFAULT_AUTH_METHOD_PRECEDENCE,\n ...optionsOrCredentialStore.methodPrecedence,\n };\n // Initialize model credential handlers if storage is provided\n if (optionsOrCredentialStore.modelCredentialStorage) {\n this.openAIHandler = new OpenAIApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage);\n this.anthropicHandler = new AnthropicApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage);\n }\n } else {\n // Legacy constructor\n this.credentialStore = optionsOrCredentialStore;\n this.tokenManager = tokenManager!;\n this.legacyApiKeys = legacyApiKeys ?? {};\n this.providerResolver = getProvider;\n this.methodPrecedenceConfig = { ...DEFAULT_AUTH_METHOD_PRECEDENCE };\n // No model credential handlers in legacy mode\n }\n }\n\n /**\n * Type guard to check if the argument is AuthManagerOptions.\n */\n private isAuthManagerOptions(arg: unknown): arg is AuthManagerOptions {\n return (\n typeof arg === 'object' &&\n arg !== null &&\n 'credentialStore' in arg &&\n 'tokenManager' in arg &&\n 'legacyApiKeys' in arg\n );\n }\n\n /**\n * Authenticate with a provider using agent auth flow.\n *\n * Initiates the OAuth 2.1 Authorization Code flow with PKCE.\n * Opens the system browser for user authentication.\n *\n * Implements single-flight pattern: if an auth flow is already in progress\n * for the same provider, subsequent callers wait for and share the same result.\n * This prevents multiple simultaneous browser flows for the same provider.\n *\n * Requirement 3.1: Initiate OAuth 2.1 Authorization Code flow with PKCE\n * Requirement 6.5: Concurrent auth requests share the same flow\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n async authenticateAgent(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n // Validate provider ID (fast-fail before checking in-flight flows)\n if (!isValidProviderId(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not supported.`,\n details: { supportedProviders: [...VALID_PROVIDER_IDS] },\n },\n };\n }\n\n // Check if provider is registered (fast-fail before checking in-flight flows)\n if (!hasProvider(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not registered.`,\n details: { registeredProviders: getRegisteredProviders() },\n },\n };\n }\n\n // Check for existing in-flight flow for this provider (single-flight pattern)\n const existingFlow = this.inFlightAuthFlows.get(providerId);\n if (existingFlow) {\n console.error(`[AuthManager] Auth flow already in progress for ${providerId}, waiting for result...`);\n return existingFlow;\n }\n\n // Create and track new flow\n const flowPromise = this.executeAuthFlow(providerId, options);\n this.inFlightAuthFlows.set(providerId, flowPromise);\n\n try {\n return await flowPromise;\n } finally {\n // Only delete if this is still our promise (race protection)\n if (this.inFlightAuthFlows.get(providerId) === flowPromise) {\n this.inFlightAuthFlows.delete(providerId);\n }\n }\n }\n\n /**\n * Execute the actual OAuth authentication flow.\n *\n * This is the internal implementation that performs the browser-based\n * OAuth 2.1 Authorization Code flow with PKCE.\n *\n * @param providerId - The provider to authenticate with\n * @param options - Optional flow configuration\n * @returns Authentication result indicating success or failure\n */\n private async executeAuthFlow(\n providerId: AuthProviderId,\n options?: AgentAuthOptions\n ): Promise<AuthResult> {\n try {\n // Create the agent auth flow\n const agentAuthFlow = new AgentAuthFlow({\n getProvider: this.providerResolver,\n storeTokens: async (pid, tokens) => {\n await this.tokenManager.storeTokens(pid, tokens);\n },\n });\n\n // Execute the flow\n console.error(`[AuthManager] Starting agent auth flow for ${providerId}`);\n const result = await agentAuthFlow.execute(providerId, options);\n\n if (result.success) {\n console.error(`[AuthManager] Agent auth flow completed successfully for ${providerId}`);\n } else {\n console.error(`[AuthManager] Agent auth flow failed for ${providerId}: ${result.error?.message}`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[AuthManager] Agent auth flow error for ${providerId}: ${errorMessage}`);\n\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Authentication failed: ${errorMessage}`,\n },\n };\n }\n }\n\n /**\n * Run interactive terminal setup for a provider.\n *\n * Starts the Setup_Wizard interactive flow for configuring\n * OAuth credentials in headless environments.\n *\n * Requirement 4.1: Start Setup_Wizard interactive flow\n *\n * @param providerId - The provider to set up\n * @returns Authentication result indicating success or failure\n */\n async setupTerminal(providerId: AuthProviderId): Promise<AuthResult> {\n // Validate provider ID\n if (!isValidProviderId(providerId)) {\n return {\n success: false,\n providerId,\n error: {\n code: 'UNSUPPORTED_PROVIDER',\n message: `Provider '${providerId}' is not supported.`,\n details: { supportedProviders: [...VALID_PROVIDER_IDS] },\n },\n };\n }\n\n try {\n // Create the terminal auth flow\n const terminalAuthFlow = new TerminalAuthFlow({\n credentialStore: this.credentialStore,\n validateCredentials: async (pid, credentials) => {\n return this.validateTerminalCredentials(pid, credentials);\n },\n });\n\n // Execute the flow\n console.error(`[AuthManager] Starting terminal setup for ${providerId}`);\n const flowResult = await terminalAuthFlow.execute(providerId);\n\n // Check if user selected browser OAuth flow\n if (flowResult.useBrowserOAuth) {\n console.error(`[AuthManager] User selected browser OAuth for ${providerId}, launching browser flow`);\n return this.authenticateAgent(flowResult.providerId);\n }\n\n // Manual credential flow completed\n const result = flowResult.authResult;\n if (result.success) {\n console.error(`[AuthManager] Terminal setup completed successfully for ${providerId}`);\n } else {\n console.error(`[AuthManager] Terminal setup failed for ${providerId}: ${result.error?.message}`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[AuthManager] Terminal setup error for ${providerId}: ${errorMessage}`);\n\n return {\n success: false,\n providerId,\n error: {\n code: 'PROVIDER_ERROR',\n message: `Terminal setup failed: ${errorMessage}`,\n },\n };\n }\n }\n\n /**\n * Validate credentials collected during terminal auth flow.\n *\n * Note: Terminal auth flow stores client credentials for later use.\n * The actual token exchange happens when the credentials are used.\n * This validation ensures the credentials are properly formatted.\n *\n * @param providerId - The provider to validate against\n * @param credentials - The collected credentials\n * @returns Validation result with status indicator\n */\n private async validateTerminalCredentials(\n providerId: AuthProviderId,\n credentials: { clientId: string; clientSecret?: string }\n ): Promise<{ valid: boolean; error?: string; accessToken?: string }> {\n try {\n // Validate client ID format\n if (!credentials.clientId || credentials.clientId.trim().length === 0) {\n return { valid: false, error: 'Client ID is required' };\n }\n\n // Basic format validation for client ID (alphanumeric with common separators)\n if (!/^[a-zA-Z0-9._-]+$/.test(credentials.clientId.trim())) {\n return { valid: false, error: 'Client ID contains invalid characters' };\n }\n\n // Validate provider is available\n if (!isValidProviderId(providerId)) {\n return { valid: false, error: `Provider '${providerId}' is not supported` };\n }\n\n try {\n const provider = this.providerResolver(providerId);\n if (!provider) {\n return { valid: false, error: `Provider '${providerId}' is not available` };\n }\n } catch {\n return { valid: false, error: `Provider '${providerId}' is not available` };\n }\n\n // Terminal auth stores client credentials, not access tokens\n // Return a placeholder token to indicate \"configured but not authenticated\"\n // The actual token will be obtained via OAuth flow when needed\n return {\n valid: true,\n // Use a special marker to indicate this is a client credential config, not an access token\n accessToken: CLIENT_CREDENTIALS_MARKER,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { valid: false, error: errorMessage };\n }\n }\n\n /**\n * Get access token for an agent, preferring OAuth over legacy.\n *\n * Requirement 10.3: Prefer OAuth credentials over legacy api-keys.json\n *\n * Security: When providerId is specified, ONLY that provider is used.\n * No fallback to other providers to prevent credential confusion.\n *\n * @param agentId - The agent identifier\n * @param providerId - Optional provider to get token from (strict binding when specified)\n * @returns Access token or null if not available\n */\n async getTokenForAgent(\n agentId: string,\n providerId?: AuthProviderId\n ): Promise<string | null> {\n // Step 1: If provider is specified, ONLY use that provider (strict binding)\n if (providerId) {\n // Validate provider ID at runtime\n if (!isValidProviderId(providerId)) {\n console.error(`[AuthManager] Invalid provider ID: ${providerId}`);\n return null;\n }\n\n const oauthToken = await this.tokenManager.getAccessToken(providerId);\n // Filter out marker tokens - they are not real access tokens\n if (oauthToken && !isMarkerToken(oauthToken)) {\n console.error(`[AuthManager] Using OAuth token for agent ${agentId} (provider: ${providerId})`);\n return oauthToken;\n }\n\n // Provider specified but no token available - do NOT fall back to other providers\n // This prevents credential confusion between different services\n console.error(`[AuthManager] No OAuth token available for specified provider ${providerId}`);\n\n // Only fall back to legacy if the legacy key is for the same provider\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n // Check if this agent is associated with the requested provider\n const agentProvider = this.getProviderForAgent(agentId);\n if (agentProvider === providerId) {\n console.error(`[AuthManager] Using legacy API key for agent ${agentId} (provider: ${providerId})`);\n return legacyKeys.apiKey;\n }\n }\n\n return null;\n }\n\n // Step 2: No provider specified - try to find appropriate provider for agent\n const agentProvider = this.getProviderForAgent(agentId);\n if (agentProvider) {\n const token = await this.tokenManager.getAccessToken(agentProvider);\n // Filter out marker tokens - they are not real access tokens\n if (token && !isMarkerToken(token)) {\n console.error(`[AuthManager] Using OAuth token for agent ${agentId} (auto-detected provider: ${agentProvider})`);\n return token;\n }\n }\n\n // Step 3: Fall back to legacy api-keys.json\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n console.error(`[AuthManager] Using legacy API key for agent ${agentId}`);\n return legacyKeys.apiKey;\n }\n\n // No credentials available\n console.error(`[AuthManager] No credentials available for agent ${agentId}`);\n return null;\n }\n\n /**\n * Inject authentication into an agent request.\n *\n * Requirement 11.4: Inject access token according to provider's token injection method\n *\n * Security: Uses strict provider binding based on agent ID to prevent\n * credential confusion between different services.\n *\n * @param agentId - The agent identifier\n * @param request - The request object to inject auth into\n * @returns The request object with authentication injected\n */\n async injectAuth(agentId: string, request: object): Promise<object> {\n // Determine the appropriate provider for this agent\n const agentProvider = this.getProviderForAgent(agentId);\n\n // Try OAuth token for the agent's provider\n if (agentProvider) {\n const token = await this.tokenManager.getAccessToken(agentProvider);\n // Filter out marker tokens - they are not real access tokens\n if (token && !isMarkerToken(token)) {\n try {\n const provider = this.providerResolver(agentProvider);\n const injection = provider.getTokenInjection();\n\n // Validate injection configuration\n const validationError = this.validateInjectionConfig(injection);\n if (validationError) {\n console.error(`[AuthManager] Invalid injection config for ${agentProvider}: ${validationError}`);\n // Fall through to legacy handling\n } else {\n const result = this.applyTokenInjection(request, token, injection);\n if (result !== null) {\n return result;\n }\n // Injection failed (e.g., control chars in token), fall through to legacy\n console.error(`[AuthManager] Token injection failed for ${agentProvider}, trying legacy fallback`);\n }\n } catch (error) {\n // Log the error but don't expose details\n console.error(`[AuthManager] Provider resolution failed for ${agentProvider}`);\n }\n }\n }\n\n // Fall back to legacy API key injection (Bearer header)\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n const result = this.applyTokenInjection(request, legacyKeys.apiKey, {\n type: 'header',\n key: 'Authorization',\n format: 'Bearer {token}',\n });\n if (result !== null) {\n return result;\n }\n // Legacy key also has control chars - this is a security issue, return original request\n console.error(`[AuthManager] Legacy API key contains control characters, refusing to inject`);\n }\n\n // No auth to inject\n return request;\n }\n\n /**\n * Validate token injection configuration.\n * Prevents header injection attacks and unsafe configurations.\n *\n * @param injection - The injection configuration to validate\n * @returns Error message if invalid, null if valid\n */\n private validateInjectionConfig(\n injection: { type: 'header' | 'query' | 'body'; key: string; format?: string }\n ): string | null {\n // Validate key - must be alphanumeric with hyphens/underscores\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(injection.key)) {\n return 'Invalid injection key format';\n }\n\n // Prevent header injection via CR/LF\n if (injection.key.includes('\\r') || injection.key.includes('\\n')) {\n return 'Injection key contains invalid characters';\n }\n\n // Validate format if provided\n if (injection.format) {\n // Must contain {token} placeholder\n if (!injection.format.includes('{token}')) {\n return 'Injection format must contain {token} placeholder';\n }\n // Prevent CR/LF injection in format\n if (injection.format.includes('\\r') || injection.format.includes('\\n')) {\n return 'Injection format contains invalid characters';\n }\n }\n\n // Warn about query injection (tokens in URLs are risky)\n if (injection.type === 'query') {\n console.error('[AuthManager] Warning: Token injection via query parameter is less secure');\n }\n\n return null;\n }\n\n /**\n * Apply token injection to a request object.\n *\n * @param request - The request object\n * @param token - The access token\n * @param injection - The injection method\n * @returns The modified request object, or null if injection failed\n */\n private applyTokenInjection(\n request: object,\n token: string,\n injection: { type: 'header' | 'query' | 'body'; key: string; format?: string }\n ): object | null {\n const formattedToken = injection.format\n ? injection.format.replace('{token}', token)\n : token;\n\n // Validate formatted token for control characters (prevent header injection)\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(formattedToken)) {\n console.error('[AuthManager] Token contains control characters, refusing to inject');\n return null; // Signal injection failure\n }\n\n const result = { ...request } as Record<string, unknown>;\n\n switch (injection.type) {\n case 'header': {\n const headers = (result.headers as Record<string, string>) ?? {};\n result.headers = { ...headers, [injection.key]: formattedToken };\n break;\n }\n case 'query': {\n const query = (result.query as Record<string, string>) ?? {};\n result.query = { ...query, [injection.key]: formattedToken };\n break;\n }\n case 'body': {\n const body = (result.body as Record<string, string>) ?? {};\n result.body = { ...body, [injection.key]: formattedToken };\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Get authentication status for all providers.\n *\n * @returns Map of provider IDs to their authentication status\n */\n async getStatus(): Promise<AuthStatusMap> {\n const statusMap: AuthStatusMap = new Map();\n\n // Get token status from token manager\n const tokenStatus = await this.tokenManager.getStatus();\n\n // Get stored credentials for additional info\n const providers = await this.credentialStore.listProviders();\n\n for (const providerId of providers) {\n const status = tokenStatus.get(providerId) ?? 'not-configured';\n const credentials = await this.credentialStore.retrieve(providerId);\n\n const entry: AuthStatusEntry = {\n providerId,\n status,\n expiresAt: credentials?.expiresAt,\n scope: credentials?.scope,\n lastRefresh: credentials?.storedAt,\n };\n\n statusMap.set(providerId, entry);\n }\n\n // Add providers that are supported but not configured\n for (const providerId of VALID_PROVIDER_IDS) {\n if (!statusMap.has(providerId)) {\n statusMap.set(providerId, {\n providerId,\n status: 'not-configured',\n });\n }\n }\n\n return statusMap;\n }\n\n /**\n * Logout from a specific provider or all providers.\n *\n * Note: This clears OAuth credentials only. Legacy API keys from api-keys.json\n * are managed separately and are not affected by logout.\n *\n * @param providerId - Optional provider to logout from (all OAuth providers if not specified)\n * @throws Error if an invalid provider ID is specified\n */\n async logout(providerId?: AuthProviderId): Promise<void> {\n if (providerId) {\n // Validate provider ID at runtime\n if (!isValidProviderId(providerId)) {\n throw new Error(`Invalid provider ID for logout: ${providerId}`);\n }\n\n // Logout from specific provider\n console.error(`[AuthManager] Logging out from ${providerId}`);\n await this.tokenManager.clearTokens(providerId);\n await this.credentialStore.delete(providerId);\n } else {\n // Logout from all OAuth providers\n console.error(`[AuthManager] Logging out from all OAuth providers`);\n const providers = await this.credentialStore.listProviders();\n for (const pid of providers) {\n await this.tokenManager.clearTokens(pid);\n }\n await this.credentialStore.deleteAll();\n }\n }\n\n /**\n * Check if re-authentication is required for a provider.\n *\n * @param providerId - The provider to check\n * @returns True if re-authentication is required\n */\n async requiresReauth(providerId: AuthProviderId): Promise<boolean> {\n // Check if we have valid tokens\n const hasValid = await this.tokenManager.hasValidTokens(providerId);\n if (hasValid) {\n return false;\n }\n\n // Check if we have credentials at all\n const credentials = await this.credentialStore.retrieve(providerId);\n if (!credentials) {\n // No credentials stored, auth required\n return true;\n }\n\n // We have credentials but tokens are invalid\n // Try to refresh\n const refreshed = await this.tokenManager.forceRefresh(providerId);\n return refreshed === null;\n }\n\n /**\n * Get the provider for a given agent ID.\n *\n * Maps agent IDs to their OAuth providers based on keyword matching.\n *\n * WARNING: This is a heuristic-based mapping using keyword matching.\n * Agent IDs with ambiguous names (e.g., containing multiple provider keywords)\n * may be mapped to unexpected providers. For production use, consider\n * implementing explicit agent-to-provider configuration.\n *\n * @param agentId - The agent identifier\n * @returns The provider ID or undefined if not mapped\n */\n getProviderForAgent(agentId: string): AuthProviderId | undefined {\n // This is a simple mapping based on agent ID patterns\n // In a real implementation, this would come from configuration\n const agentLower = agentId.toLowerCase();\n\n // Check for provider keywords in order of specificity\n // More specific keywords first to avoid ambiguity\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n if (agentLower.includes('github') || agentLower.includes('copilot')) {\n return 'github';\n }\n if (agentLower.includes('google') || agentLower.includes('gemini')) {\n return 'google';\n }\n if (agentLower.includes('azure')) {\n return 'azure';\n }\n if (agentLower.includes('cognito') || agentLower.includes('aws')) {\n return 'cognito';\n }\n\n return undefined;\n }\n\n // ===========================================================================\n // Model Credential Methods (API Keys)\n // ===========================================================================\n // These methods handle upstream model provider credentials (OpenAI, Anthropic).\n // They are clearly separated from OAuth identity providers.\n // Requirements: 7b.3\n // ===========================================================================\n\n /**\n * Get API key credential for a model provider.\n *\n * This method is for retrieving API keys for upstream model providers\n * (OpenAI, Anthropic). These providers do NOT offer public OAuth IdP\n * for third-party login - they use API keys instead.\n *\n * This is clearly separated from getTokenForAgent() which handles\n * OAuth tokens for user identity providers.\n *\n * Requirements: 7b.1, 7b.3\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @returns The model credential result with API key if found\n */\n async getModelCredential(providerId: ModelProviderId): Promise<ModelCredentialResult> {\n // Validate provider ID\n if (!isValidModelProviderId(providerId)) {\n return {\n found: false,\n error: `Invalid model provider ID: ${providerId}. Valid providers: ${VALID_MODEL_PROVIDER_IDS.join(', ')}`,\n };\n }\n\n // Get the appropriate handler\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n return {\n found: false,\n error: `Model credential storage not configured. Initialize AuthManager with modelCredentialStorage option.`,\n };\n }\n\n // Retrieve the credential\n const result = await handler.retrieve();\n if (result.found) {\n console.error(`[AuthManager] Retrieved model credential for ${providerId}`);\n } else {\n console.error(`[AuthManager] No model credential found for ${providerId}`);\n }\n\n return result;\n }\n\n /**\n * Check if a model credential is configured for a provider.\n *\n * Requirements: 7b.3\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @returns True if an API key is configured for the provider\n */\n async hasModelCredential(providerId: ModelProviderId): Promise<boolean> {\n if (!isValidModelProviderId(providerId)) {\n return false;\n }\n\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n return false;\n }\n\n return handler.isConfigured();\n }\n\n /**\n * Get the status of all model credentials.\n *\n * Requirements: 7b.3\n *\n * @returns Map of model provider IDs to their credential status\n */\n async getModelCredentialStatus(): Promise<ModelCredentialStatusMap> {\n const statusMap: ModelCredentialStatusMap = new Map();\n\n for (const providerId of VALID_MODEL_PROVIDER_IDS) {\n const handler = this.getModelCredentialHandler(providerId);\n if (handler) {\n const status = await handler.getStatus();\n statusMap.set(providerId, status);\n } else {\n statusMap.set(providerId, {\n providerId,\n status: 'not-configured',\n });\n }\n }\n\n return statusMap;\n }\n\n /**\n * Inject model API key into a request.\n *\n * This method injects API keys for model providers (OpenAI, Anthropic)\n * according to their documented injection method:\n * - OpenAI: Authorization header with Bearer token\n * - Anthropic: x-api-key header with raw key\n *\n * This is clearly separated from injectAuth() which handles OAuth tokens.\n *\n * Requirements: 7b.3, 7b.5\n *\n * @param providerId - The model provider ID ('openai' or 'anthropic')\n * @param request - The request object to inject auth into\n * @returns The request object with API key injected, or original if not available\n */\n async injectModelAuth(providerId: ModelProviderId, request: object): Promise<object> {\n // Validate provider ID\n if (!isValidModelProviderId(providerId)) {\n console.error(`[AuthManager] Invalid model provider ID for injection: ${providerId}`);\n return request;\n }\n\n // Get the appropriate handler\n const handler = this.getModelCredentialHandler(providerId);\n if (!handler) {\n console.error(`[AuthManager] Model credential storage not configured for ${providerId}`);\n return request;\n }\n\n // Get the credential\n const credentialResult = await handler.retrieve();\n if (!credentialResult.found || !credentialResult.credential) {\n console.error(`[AuthManager] No model credential available for ${providerId}`);\n return request;\n }\n\n // Get injection configuration\n const injection = MODEL_CREDENTIAL_INJECTION_CONFIG[providerId];\n const apiKey = credentialResult.credential.apiKey;\n\n // Format the header value\n const headerValue = injection.format\n ? injection.format.replace('{key}', apiKey)\n : apiKey;\n\n // Validate for control characters (prevent header injection)\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(headerValue)) {\n console.error(`[AuthManager] Model API key contains control characters, refusing to inject`);\n return request;\n }\n\n // Inject into headers\n const result = { ...request } as Record<string, unknown>;\n const headers = (result.headers as Record<string, string>) ?? {};\n result.headers = { ...headers, [injection.headerName]: headerValue };\n\n console.error(`[AuthManager] Injected model credential for ${providerId}`);\n return result;\n }\n\n /**\n * Get the model provider for a given agent ID.\n *\n * Maps agent IDs to their model providers based on keyword matching.\n * This is separate from getProviderForAgent() which maps to OAuth providers.\n *\n * Requirements: 7b.3\n *\n * @param agentId - The agent identifier\n * @returns The model provider ID or undefined if not mapped\n */\n getModelProviderForAgent(agentId: string): ModelProviderId | undefined {\n const agentLower = agentId.toLowerCase();\n\n // Check for model provider keywords\n // Note: These are NOT OAuth providers - they use API keys\n if (agentLower.includes('openai') || agentLower.includes('gpt') || agentLower.includes('chatgpt')) {\n return 'openai';\n }\n if (agentLower.includes('anthropic') || agentLower.includes('claude')) {\n return 'anthropic';\n }\n\n return undefined;\n }\n\n /**\n * Get the appropriate model credential handler for a provider.\n *\n * @param providerId - The model provider ID\n * @returns The handler or undefined if not available\n */\n private getModelCredentialHandler(\n providerId: ModelProviderId\n ): OpenAIApiKeyHandler | AnthropicApiKeyHandler | undefined {\n switch (providerId) {\n case 'openai':\n return this.openAIHandler;\n case 'anthropic':\n return this.anthropicHandler;\n default:\n return undefined;\n }\n }\n\n /**\n * Select the best authentication method for an agent based on precedence configuration.\n *\n * Method Precedence Strategy (Requirements 3.1, 10.3):\n * - Default precedence: oauth2 > api-key (OAuth preferred when available)\n * - Iterates through methods in precedence order\n * - Returns the first method with available credentials\n * - Fail-fast on unsupported or ambiguous providerId (configurable)\n *\n * @param agentId - The agent identifier\n * @param availableMethods - Optional list of methods the agent supports (from authMethods)\n * @param providerId - Optional explicit provider ID (strict binding when specified)\n * @returns Selection result with method type, provider, and credential availability\n * @throws AuthMethodSelectionError if fail-fast is enabled and an error occurs\n */\n async selectAuthMethod(\n agentId: string,\n availableMethods?: AuthMethodType[],\n providerId?: AuthProviderId\n ): Promise<AuthMethodSelectionResult> {\n const { methodPrecedence, failFastOnUnsupported, failFastOnAmbiguous } = this.methodPrecedenceConfig;\n\n // Validate explicit providerId if specified\n if (providerId !== undefined) {\n if (!isValidProviderId(providerId)) {\n const error = `Provider '${providerId}' is not supported. Valid providers: ${VALID_PROVIDER_IDS.join(', ')}`;\n if (failFastOnUnsupported) {\n throw new AuthMethodSelectionError(\n error,\n 'UNSUPPORTED_METHOD',\n { providerId, supportedProviders: [...VALID_PROVIDER_IDS] }\n );\n }\n console.error(`[AuthManager] ${error}`);\n return {\n methodType: 'api-key',\n hasCredential: false,\n error,\n };\n }\n }\n\n // Determine which methods to consider\n const methodsToTry = availableMethods\n ? methodPrecedence.filter(m => availableMethods.includes(m))\n : methodPrecedence;\n\n // Check for ambiguous provider mapping (multiple providers could match)\n if (!providerId && failFastOnAmbiguous) {\n const ambiguityCheck = this.checkProviderAmbiguity(agentId);\n if (ambiguityCheck.isAmbiguous) {\n throw new AuthMethodSelectionError(\n `Ambiguous provider mapping for agent '${agentId}'. Multiple providers could match: ${ambiguityCheck.matchingProviders.join(', ')}. Specify an explicit providerId.`,\n 'AMBIGUOUS_PROVIDER',\n { agentId, matchingProviders: ambiguityCheck.matchingProviders }\n );\n }\n }\n\n // Iterate through methods in precedence order\n for (const methodType of methodsToTry) {\n // Validate method type\n if (!isValidAuthMethodType(methodType)) {\n const error = `Unsupported authentication method: ${methodType}`;\n if (failFastOnUnsupported) {\n throw new AuthMethodSelectionError(\n error,\n 'UNSUPPORTED_METHOD',\n { methodType, supportedMethods: ['oauth2', 'api-key'] }\n );\n }\n console.error(`[AuthManager] ${error}, skipping...`);\n continue;\n }\n\n const result = await this.tryAuthMethod(agentId, methodType, providerId);\n if (result.hasCredential) {\n console.error(`[AuthManager] Selected auth method '${methodType}' for agent '${agentId}'`);\n return result;\n }\n }\n\n // No credentials found for any method\n console.error(`[AuthManager] No credentials available for agent '${agentId}'`);\n return {\n methodType: methodPrecedence[0] ?? 'oauth2',\n hasCredential: false,\n error: `No credentials available for agent '${agentId}'`,\n };\n }\n\n /**\n * Try a specific authentication method for an agent.\n *\n * @param agentId - The agent identifier\n * @param methodType - The authentication method to try\n * @param providerId - Optional explicit provider ID\n * @returns Selection result for this method\n */\n private async tryAuthMethod(\n agentId: string,\n methodType: AuthMethodType,\n providerId?: AuthProviderId\n ): Promise<AuthMethodSelectionResult> {\n switch (methodType) {\n case 'oauth2': {\n // Determine provider for OAuth\n const effectiveProviderId = providerId ?? this.getProviderForAgent(agentId);\n if (!effectiveProviderId) {\n return {\n methodType: 'oauth2',\n hasCredential: false,\n error: `No OAuth provider mapping for agent '${agentId}'`,\n };\n }\n\n // Check for OAuth token\n const token = await this.tokenManager.getAccessToken(effectiveProviderId);\n if (token && !isMarkerToken(token)) {\n return {\n methodType: 'oauth2',\n providerId: effectiveProviderId,\n hasCredential: true,\n };\n }\n\n return {\n methodType: 'oauth2',\n providerId: effectiveProviderId,\n hasCredential: false,\n error: `No OAuth token available for provider '${effectiveProviderId}'`,\n };\n }\n\n case 'api-key': {\n // Check for legacy API key\n const legacyKeys = this.legacyApiKeys[agentId];\n if (legacyKeys?.apiKey) {\n return {\n methodType: 'api-key',\n hasCredential: true,\n };\n }\n\n return {\n methodType: 'api-key',\n hasCredential: false,\n error: `No API key available for agent '${agentId}'`,\n };\n }\n\n default: {\n // This should never happen due to type checking, but handle gracefully\n return {\n methodType: methodType as AuthMethodType,\n hasCredential: false,\n error: `Unknown authentication method: ${methodType}`,\n };\n }\n }\n }\n\n /**\n * Check if an agent ID has ambiguous provider mapping.\n *\n * Ambiguity occurs when multiple provider keywords match the agent ID.\n * For example, \"azure-openai-agent\" matches both \"azure\" and \"openai\".\n *\n * @param agentId - The agent identifier\n * @returns Ambiguity check result\n */\n private checkProviderAmbiguity(agentId: string): { isAmbiguous: boolean; matchingProviders: AuthProviderId[] } {\n const agentLower = agentId.toLowerCase();\n const matchingProviders: AuthProviderId[] = [];\n\n // Check each provider's keywords\n // Note: OpenAI and Anthropic are NOT OAuth providers - they use API keys\n // Note: oidc is checked last as a fallback for generic OIDC providers\n const providerKeywords: Record<AuthProviderId, string[]> = {\n github: ['github', 'copilot'],\n google: ['google', 'gemini'],\n azure: ['azure'],\n cognito: ['cognito', 'aws'],\n oidc: ['oidc', 'openid', 'auth0', 'okta', 'keycloak', 'onelogin', 'ping'],\n };\n\n for (const [provider, keywords] of Object.entries(providerKeywords)) {\n if (keywords.some(keyword => agentLower.includes(keyword))) {\n matchingProviders.push(provider as AuthProviderId);\n }\n }\n\n return {\n isAmbiguous: matchingProviders.length > 1,\n matchingProviders,\n };\n }\n\n /**\n * Get the current method precedence configuration.\n *\n * @returns The current method precedence configuration\n */\n getMethodPrecedenceConfig(): AuthMethodPrecedenceConfig {\n return { ...this.methodPrecedenceConfig };\n }\n}\n\n/**\n * Create an AuthManager with the given options.\n *\n * @param options - Configuration options\n * @returns A new AuthManager instance\n */\nexport function createAuthManager(options: AuthManagerOptions): AuthManager {\n return new AuthManager(options);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --setup CLI command implementation.\n *\n * Starts the interactive authentication Setup_Wizard.\n *\n * Requirements: 9.1, 3.1\n *\n * @module cli/setup-command\n */\n\nimport { TerminalAuthFlow } from '../flows/terminal-auth-flow.js';\nimport { AgentAuthFlow } from '../flows/agent-auth-flow.js';\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { getProvider } from '../providers/index.js';\nimport { CLIENT_CREDENTIALS_MARKER } from '../auth-manager.js';\nimport type { AuthProviderId, TokenResponse } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Options for the setup command.\n */\nexport interface SetupCommandOptions {\n /** Optional pre-selected provider (skips provider selection) */\n providerId?: AuthProviderId;\n /** Custom input stream (for testing) */\n input?: NodeJS.ReadableStream;\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Run the setup command.\n *\n * Starts the interactive Setup_Wizard for configuring OAuth credentials.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.1: WHEN the `--setup` flag is provided, THE Registry_Launcher\n * SHALL start the interactive authentication Setup_Wizard.\n *\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runSetupCommand(options: SetupCommandOptions = {}): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Validate provider ID if specified\n if (options.providerId !== undefined && !isValidProviderId(options.providerId)) {\n output.write(`\\nError: Invalid provider '${options.providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n // Create credential store\n const credentialStore = new CredentialStore();\n\n // Create terminal auth flow\n const terminalAuthFlow = new TerminalAuthFlow({\n credentialStore,\n validateCredentials: async (_providerId, credentials) => {\n // Basic validation - check that required fields are present\n if (!credentials.clientId || credentials.clientId.trim().length === 0) {\n return { valid: false, error: 'This field is required' };\n }\n\n // For API key auth (no clientSecret), the clientId IS the API key\n // Return it directly as the access token\n if (!credentials.clientSecret) {\n // This is API key mode - the \"clientId\" is actually the API key\n return { valid: true, accessToken: credentials.clientId.trim() };\n }\n\n // For OAuth client credentials mode, return the marker token\n // The actual OAuth token will be obtained when the credentials are used\n return { valid: true, accessToken: CLIENT_CREDENTIALS_MARKER };\n },\n input: options.input,\n output,\n });\n\n // Execute the setup wizard\n const flowResult = await terminalAuthFlow.execute(options.providerId);\n\n // Check if user selected browser OAuth flow\n if (flowResult.useBrowserOAuth) {\n output.write('\\nLaunching browser for OAuth authentication...\\n');\n output.write('Please complete the authentication in your browser.\\n\\n');\n\n // Create token manager for storing tokens\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n\n // Create and execute browser OAuth flow\n const agentAuthFlow = new AgentAuthFlow({\n getProvider,\n storeTokens: async (providerId: AuthProviderId, tokens: TokenResponse) => {\n await tokenManager.storeTokens(providerId, tokens);\n },\n });\n\n const browserResult = await agentAuthFlow.execute(flowResult.providerId);\n\n if (browserResult.success) {\n output.write(`\\n${flowResult.providerId} authentication completed successfully!\\n\\n`);\n return 0;\n } else {\n output.write(`\\nBrowser authentication failed: ${browserResult.error?.message}\\n`);\n return 1;\n }\n }\n\n // Manual credential flow completed\n const result = flowResult.authResult;\n if (result.success) {\n return 0;\n } else {\n // Error message already displayed by terminal auth flow\n return 1;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nSetup failed: ${errorMessage}\\n`);\n console.error(`[SetupCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --auth-status CLI command implementation.\n *\n * Displays the current authentication status for all configured providers.\n *\n * Requirements: 9.2\n *\n * @module cli/status-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport type { AuthStatusEntry, TokenStatus } from '../types.js';\nimport { VALID_PROVIDER_IDS } from '../types.js';\nimport type { ModelCredentialStatusEntry, StoredModelCredential } from '../model-credentials/index.js';\nimport { VALID_MODEL_PROVIDER_IDS } from '../model-credentials/index.js';\nimport type { IModelCredentialStorage } from '../model-credentials/openai-api-key.js';\n\n/**\n * Options for the status command.\n */\nexport interface StatusCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Format a timestamp as a human-readable date string.\n *\n * @param timestamp - Unix timestamp in milliseconds\n * @returns Formatted date string\n */\nfunction formatTimestamp(timestamp: number | undefined): string {\n if (!timestamp) {\n return 'N/A';\n }\n return new Date(timestamp).toLocaleString();\n}\n\n/**\n * Get a human-readable status label with color indicator.\n *\n * @param status - Token status\n * @returns Status label with indicator\n */\nfunction getStatusLabel(status: TokenStatus): string {\n switch (status) {\n case 'authenticated':\n return '\u2713 Authenticated';\n case 'expired':\n return '\u26A0 Expired (refresh available)';\n case 'refresh-failed':\n return '\u2717 Refresh Failed (re-auth required)';\n case 'not-configured':\n return '\u25CB Not Configured';\n default:\n return '? Unknown';\n }\n}\n\n/**\n * Sanitize a string for safe terminal output.\n * Removes control characters and ANSI escape sequences.\n *\n * @param value - The string to sanitize\n * @returns Sanitized string safe for terminal output\n */\nfunction sanitizeForOutput(value: string): string {\n // Remove ANSI escape sequences\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\x00-\\x1f\\x7f]|\\x1b\\[[0-9;]*[a-zA-Z]/g, '');\n}\n\n/**\n * Format a single provider status entry for display.\n *\n * @param entry - Auth status entry\n * @returns Formatted status lines\n */\nfunction formatProviderStatus(entry: AuthStatusEntry): string[] {\n const lines: string[] = [];\n const providerName = entry.providerId.charAt(0).toUpperCase() + entry.providerId.slice(1);\n\n lines.push(` ${providerName}:`);\n lines.push(` Status: ${getStatusLabel(entry.status)}`);\n\n if (entry.status !== 'not-configured') {\n if (entry.expiresAt) {\n const now = Date.now();\n const isExpired = entry.expiresAt <= now;\n const expiresLabel = isExpired ? 'Expired at' : 'Expires at';\n lines.push(` ${expiresLabel}: ${formatTimestamp(entry.expiresAt)}`);\n }\n\n if (entry.scope) {\n // Sanitize scope to prevent terminal output injection\n lines.push(` Scope: ${sanitizeForOutput(entry.scope)}`);\n }\n\n if (entry.lastRefresh) {\n lines.push(` Last Updated: ${formatTimestamp(entry.lastRefresh)}`);\n }\n }\n\n return lines;\n}\n\n/**\n * Format a model credential status entry for display.\n *\n * @param entry - Model credential status entry\n * @returns Formatted status lines\n */\nfunction formatModelCredentialStatus(entry: ModelCredentialStatusEntry): string[] {\n const lines: string[] = [];\n const providerName = entry.providerId.charAt(0).toUpperCase() + entry.providerId.slice(1);\n\n lines.push(` ${providerName}:`);\n\n switch (entry.status) {\n case 'configured':\n lines.push(` Status: \u2713 Configured`);\n if (entry.label) {\n lines.push(` Label: ${sanitizeForOutput(entry.label)}`);\n }\n if (entry.storedAt) {\n lines.push(` Stored: ${formatTimestamp(entry.storedAt)}`);\n }\n break;\n case 'expired':\n lines.push(` Status: \u26A0 Expired`);\n break;\n case 'not-configured':\n lines.push(` Status: \u25CB Not Configured`);\n break;\n }\n\n return lines;\n}\n\n/**\n * Adapter to use CredentialStore as IModelCredentialStorage.\n *\n * This adapter wraps the CredentialStore to provide the IModelCredentialStorage\n * interface needed by model credential handlers.\n */\nclass CredentialStoreAdapter implements IModelCredentialStorage {\n private readonly storage: Map<string, StoredModelCredential> = new Map();\n\n async store(key: string, credential: StoredModelCredential): Promise<void> {\n this.storage.set(key, { ...credential });\n }\n\n async retrieve(key: string): Promise<StoredModelCredential | null> {\n const cred = this.storage.get(key);\n return cred ? { ...cred } : null;\n }\n\n async delete(key: string): Promise<void> {\n this.storage.delete(key);\n }\n\n async exists(key: string): Promise<boolean> {\n return this.storage.has(key);\n }\n}\n\n/**\n * Run the auth-status command.\n *\n * Displays the current authentication status for all configured providers.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.2: WHEN the `--auth-status` flag is provided, THE Registry_Launcher\n * SHALL display the current authentication status for all configured providers\n * (authenticated, expired, not configured).\n *\n * @param options - Command options\n * @returns Exit code (0 for success)\n */\nexport async function runStatusCommand(options: StatusCommandOptions = {}): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: () => null, // Not needed for status check\n });\n\n // Create model credential storage adapter\n const modelCredentialStorage = new CredentialStoreAdapter();\n\n // Create auth manager with model credential storage\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n modelCredentialStorage,\n });\n\n // Get status for all OAuth providers\n const statusMap = await authManager.getStatus();\n\n // Get status for all model API keys\n const modelStatusMap = await authManager.getModelCredentialStatus();\n\n // Display OAuth header\n output.write('\\n=== OAuth Authentication Status ===\\n\\n');\n\n // Count OAuth providers by status\n let authenticatedCount = 0;\n let expiredCount = 0;\n let notConfiguredCount = 0;\n\n // Display status for each OAuth provider\n for (const providerId of VALID_PROVIDER_IDS) {\n const entry = statusMap.get(providerId);\n\n if (entry) {\n const lines = formatProviderStatus(entry);\n for (const line of lines) {\n output.write(line + '\\n');\n }\n output.write('\\n');\n\n // Update counts\n switch (entry.status) {\n case 'authenticated':\n authenticatedCount++;\n break;\n case 'expired':\n case 'refresh-failed':\n expiredCount++;\n break;\n case 'not-configured':\n notConfiguredCount++;\n break;\n }\n }\n }\n\n // Display Model API Keys header\n output.write('=== Model API Keys ===\\n\\n');\n\n // Count model keys by status\n let modelConfiguredCount = 0;\n let modelNotConfiguredCount = 0;\n\n // Display status for each model provider\n for (const providerId of VALID_MODEL_PROVIDER_IDS) {\n const entry = modelStatusMap.get(providerId);\n\n if (entry) {\n const lines = formatModelCredentialStatus(entry);\n for (const line of lines) {\n output.write(line + '\\n');\n }\n output.write('\\n');\n\n // Update counts\n switch (entry.status) {\n case 'configured':\n modelConfiguredCount++;\n break;\n case 'expired':\n case 'not-configured':\n modelNotConfiguredCount++;\n break;\n }\n }\n }\n\n // Display summary\n output.write('--- Summary ---\\n');\n output.write(` OAuth Authenticated: ${authenticatedCount}\\n`);\n output.write(` OAuth Expired/Failed: ${expiredCount}\\n`);\n output.write(` OAuth Not Configured: ${notConfiguredCount}\\n`);\n output.write(` Model Keys Configured: ${modelConfiguredCount}\\n`);\n output.write(` Model Keys Not Configured: ${modelNotConfiguredCount}\\n`);\n output.write('\\n');\n\n // Provide helpful hints\n if (notConfiguredCount === VALID_PROVIDER_IDS.length && modelNotConfiguredCount === VALID_MODEL_PROVIDER_IDS.length) {\n output.write('Tip: Run with --setup to configure authentication.\\n\\n');\n } else if (expiredCount > 0) {\n output.write('Tip: Run with --setup to re-authenticate expired providers.\\n\\n');\n }\n\n return 0;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nFailed to get auth status: ${errorMessage}\\n`);\n console.error(`[StatusCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --logout CLI command implementation.\n *\n * Removes stored credentials from the Credential_Store.\n *\n * Requirements: 9.3, 9.4\n *\n * @module cli/logout-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Options for the logout command.\n */\nexport interface LogoutCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Run the logout command.\n *\n * Removes stored credentials from the Credential_Store.\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 9.3: WHEN the `--logout` flag is provided, THE Registry_Launcher\n * SHALL remove all stored credentials from the Credential_Store.\n *\n * Requirement 9.4: WHEN the `--logout` flag is provided with a provider name,\n * THE Registry_Launcher SHALL remove only the credentials for that specific provider.\n *\n * @param providerId - Optional provider to logout from (all if not specified)\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runLogoutCommand(\n providerId?: AuthProviderId,\n options: LogoutCommandOptions = {}\n): Promise<number> {\n const output = options.output ?? process.stderr;\n\n try {\n // Validate provider ID if specified\n if (providerId !== undefined && !isValidProviderId(providerId)) {\n output.write(`\\nError: Invalid provider '${providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: () => null, // Not needed for logout\n });\n\n // Create auth manager\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n });\n\n // Get list of configured providers before logout\n const configuredProviders = await credentialStore.listProviders();\n\n if (providerId) {\n // Logout from specific provider (Requirement 9.4)\n if (!configuredProviders.includes(providerId)) {\n output.write(`\\nNo credentials found for provider '${providerId}'.\\n\\n`);\n return 0; // Not an error, just nothing to do\n }\n\n await authManager.logout(providerId);\n\n const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);\n output.write(`\\nSuccessfully logged out from ${providerName}.\\n\\n`);\n } else {\n // Logout from all providers (Requirement 9.3)\n if (configuredProviders.length === 0) {\n output.write('\\nNo credentials found. Nothing to logout.\\n\\n');\n return 0; // Not an error, just nothing to do\n }\n\n await authManager.logout();\n\n output.write(`\\nSuccessfully logged out from all providers.\\n`);\n output.write(`Removed credentials for: ${configuredProviders.join(', ')}\\n\\n`);\n }\n\n return 0;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\nLogout failed: ${errorMessage}\\n`);\n console.error(`[LogoutCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * --login CLI command implementation.\n *\n * Starts the browser-based OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirements: 3.1, 3.2, 9.5\n *\n * @module cli/login-command\n */\n\nimport { CredentialStore } from '../storage/credential-store.js';\nimport { TokenManager } from '../token-manager.js';\nimport { AuthManager } from '../auth-manager.js';\nimport { getProvider } from '../providers/index.js';\nimport type { AuthProviderId } from '../types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from '../types.js';\n\n/**\n * Default timeout for browser OAuth flow (5 minutes).\n */\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Options for the login command.\n */\nexport interface LoginCommandOptions {\n /** Custom output stream (for testing) */\n output?: NodeJS.WritableStream;\n /** Custom timeout in milliseconds (default: 5 minutes) */\n timeoutMs?: number;\n}\n\n/**\n * Run the login command.\n *\n * Starts the browser-based OAuth 2.1 Authorization Code flow with PKCE\n * for the specified provider.\n *\n * All output goes to stderr to comply with NDJSON protocol requirements.\n *\n * Requirement 3.1: WHEN an agent requires OAuth authentication with `type: \"agent\"`,\n * THE Auth_Module SHALL initiate the OAuth 2.1 Authorization Code flow with PKCE.\n *\n * Requirement 3.2: WHEN initiating the authorization flow, THE Auth_Module SHALL\n * open the system default browser to the provider's authorization URL.\n *\n * Requirement 9.5: THE Registry_Launcher SHALL exit with code 0 after successfully\n * completing any auth CLI command.\n *\n * @param providerId - The provider to authenticate with\n * @param options - Command options\n * @returns Exit code (0 for success, 1 for failure)\n */\nexport async function runLoginCommand(\n providerId: AuthProviderId,\n options: LoginCommandOptions = {}\n): Promise<number> {\n const output = options.output ?? process.stderr;\n\n // Validate and sanitize timeout\n let timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n timeoutMs = DEFAULT_TIMEOUT_MS;\n }\n // Clamp to reasonable bounds (1 second to 30 minutes)\n timeoutMs = Math.max(1000, Math.min(timeoutMs, 30 * 60 * 1000));\n\n // Validate provider ID\n if (!isValidProviderId(providerId)) {\n output.write(`\\nError: Invalid provider '${providerId}'.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n return 1;\n }\n\n try {\n // Create credential store and token manager\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n\n // Create auth manager\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: {},\n });\n\n // Get provider display name\n const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);\n const timeoutMinutes = Math.round(timeoutMs / 60000);\n\n // Output user feedback\n output.write(`\\nOpening browser for ${providerName} authentication...\\n`);\n output.write(`Waiting for authorization (timeout: ${timeoutMinutes} minutes)...\\n\\n`);\n\n // Start the browser OAuth flow\n const result = await authManager.authenticateAgent(providerId, { timeoutMs });\n\n if (result.success) {\n output.write(`\\n\u2713 Successfully authenticated with ${providerName}.\\n\\n`);\n return 0;\n } else {\n // Handle specific error cases\n const error = result.error;\n\n if (error.code === 'TIMEOUT') {\n output.write(`\\n\u2717 Authentication timed out.\\n`);\n output.write(`The browser authorization flow did not complete within ${timeoutMinutes} minutes.\\n`);\n output.write(`Please try again and complete the authorization in your browser.\\n\\n`);\n } else if (error.code === 'INVALID_STATE') {\n output.write(`\\n\u2717 Authentication failed: Security validation error.\\n`);\n output.write(`The authorization response could not be verified. Please try again.\\n\\n`);\n } else if (error.code === 'CALLBACK_ERROR') {\n output.write(`\\n\u2717 Authentication cancelled or failed.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n } else if (error.code === 'PROVIDER_ERROR') {\n output.write(`\\n\u2717 ${providerName} returned an error.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n } else if (error.code === 'UNSUPPORTED_PROVIDER') {\n output.write(`\\n\u2717 Provider '${providerId}' is not supported.\\n`);\n output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}\\n\\n`);\n } else {\n // Generic error handling\n output.write(`\\n\u2717 Authentication failed.\\n`);\n if (error.message) {\n output.write(`${error.message}\\n`);\n }\n output.write(`\\n`);\n }\n\n // Log error details for debugging (to stderr)\n console.error(`[LoginCommand] Authentication failed for ${providerId}: ${error.code} - ${error.message}`);\n\n return 1;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n output.write(`\\n\u2717 Login failed: ${errorMessage}\\n\\n`);\n console.error(`[LoginCommand] Error: ${errorMessage}`);\n return 1;\n }\n}\n", "#!/usr/bin/env node\n\n/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Main entry point for the Registry Launcher Worker.\n *\n * The Registry Launcher is a stdio Bus worker that:\n * 1. Fetches and parses the ACP Registry on startup\n * 2. Receives NDJSON messages from stdio Bus via stdin\n * 3. Routes messages to appropriate agent processes based on agentId\n * 4. Forwards agent responses back to stdout unchanged\n *\n * @module registry-launcher\n */\n\nimport { loadConfig } from './config/config.js';\nimport { loadApiKeys } from './config/api-keys.js';\nimport { RegistryFetchError, RegistryIndex, RegistryParseError, CustomAgentsLoadError, loadCustomAgents } from './registry/index.js';\nimport { NDJSONHandler } from './stream/ndjson-handler.js';\nimport { AgentRuntimeManager } from './runtime/manager.js';\nimport { MessageRouter } from './router/message-router.js';\nimport { logError, logExit, logInfo, logWarn } from './log.js';\nimport { runSetupCommand, runStatusCommand, runLogoutCommand, runLoginCommand } from './auth/cli/index.js';\nimport type { AuthProviderId } from './auth/types.js';\nimport { isValidProviderId, VALID_PROVIDER_IDS } from './auth/types.js';\nimport { AuthManager } from './auth/auth-manager.js';\nimport { TokenManager } from './auth/token-manager.js';\nimport { CredentialStore } from './auth/storage/credential-store.js';\nimport { getProvider } from './auth/providers/index.js';\n\n/**\n * Exit codes for the Registry Launcher.\n */\nconst ExitCodes = {\n /** Successful graceful shutdown */\n SUCCESS: 0,\n /** Fatal error during startup or operation */\n FATAL_ERROR: 1,\n} as const;\n\n/**\n * Flag to track if shutdown is in progress.\n */\nlet isShuttingDown = false;\n\n/**\n * Parsed command-line arguments.\n */\ninterface ParsedArgs {\n /** Path to the config file (positional argument) */\n configPath?: string;\n /** Path to the custom agents JSON file (--custom-agents <path>) */\n customAgentsPath?: string;\n /** Run the --setup auth command */\n setup?: boolean;\n /** Run the --auth-status command */\n authStatus?: boolean;\n /** Run the --logout command */\n logout?: boolean;\n /** Provider ID for --logout (optional) */\n logoutProvider?: AuthProviderId;\n /** Run the --login command */\n login?: boolean;\n /** Provider ID for --login (required) */\n loginProvider?: string;\n}\n\n/**\n * Parse command-line arguments.\n *\n * Usage: node index.js [config-path] [--custom-agents <path>] [--setup] [--auth-status] [--logout [provider]] [--login <provider>]\n *\n * @returns Parsed arguments\n */\nfunction parseArgs(): ParsedArgs {\n // argv[0] is node, argv[1] is the script path\n const args = process.argv.slice(2);\n const result: ParsedArgs = {};\n\n let i = 0;\n while (i < args.length) {\n const arg = args[i];\n\n if (arg === '--custom-agents') {\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.customAgentsPath = nextArg;\n i += 2;\n continue;\n }\n // --custom-agents without value: log warning and skip\n logWarn('--custom-agents requires a file path argument, ignoring');\n i += 1;\n continue;\n }\n\n // Auth CLI flags (Requirement 9.1, 9.2, 9.3)\n if (arg === '--setup') {\n result.setup = true;\n i += 1;\n continue;\n }\n\n if (arg === '--auth-status') {\n result.authStatus = true;\n i += 1;\n continue;\n }\n\n if (arg === '--logout') {\n result.logout = true;\n // Check if next arg is a provider ID (not a flag)\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.logoutProvider = nextArg as AuthProviderId;\n i += 2;\n continue;\n }\n i += 1;\n continue;\n }\n\n // --login [provider] flag (Requirement 3.1, 9.1)\n if (arg === '--login') {\n result.login = true;\n // Check if next arg is a provider ID (not a flag)\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith('-')) {\n result.loginProvider = nextArg;\n i += 2;\n continue;\n }\n i += 1;\n continue;\n }\n\n // First non-flag argument is the config path\n if (!arg.startsWith('-') && !result.configPath) {\n result.configPath = arg;\n }\n\n i += 1;\n }\n\n return result;\n}\n\n/**\n * Set up SIGTERM handler for graceful shutdown.\n *\n * @param runtimeManager - The runtime manager to terminate agents\n * @param shutdownTimeoutMs - Timeout in milliseconds for graceful shutdown\n * @returns A function to trigger shutdown programmatically\n */\nfunction setupSignalHandlers(\n runtimeManager: AgentRuntimeManager,\n shutdownTimeoutMs: number,\n): () => Promise<void> {\n const shutdown = async (): Promise<void> => {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n logInfo('Received shutdown signal, initiating graceful shutdown');\n\n try {\n // Terminate all agent processes\n await runtimeManager.terminateAll(shutdownTimeoutMs);\n logInfo('All agent processes terminated');\n\n // Exit with success code\n process.exit(ExitCodes.SUCCESS);\n } catch (error) {\n logError(`Error during shutdown: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n };\n\n // Handle SIGTERM\n process.on('SIGTERM', () => {\n void shutdown();\n });\n\n // Also handle SIGINT for development convenience\n process.on('SIGINT', () => {\n void shutdown();\n });\n\n return shutdown;\n}\n\n/**\n * Set up stdin NDJSON handler and wire up message routing.\n *\n * @param router - The message router for handling incoming messages\n * @param ndjsonHandler - The NDJSON handler for stdin/stdout\n */\nfunction setupStdinHandler(router: MessageRouter, ndjsonHandler: NDJSONHandler): void {\n // Handle parsed messages from stdin\n ndjsonHandler.onMessage(async (message: object) => {\n try {\n const errorResponse = await router.route(message);\n\n // If routing returned an error, write it to stdout\n if (errorResponse) {\n ndjsonHandler.write(errorResponse);\n }\n } catch (error) {\n logError(`Unexpected error routing message: ${(error as Error).message}`);\n }\n });\n\n // Handle parse errors\n ndjsonHandler.onError((error: Error, line: string) => {\n logError(`Failed to parse NDJSON: ${error.message} - Line: ${line.slice(0, 100)}`);\n });\n\n // Process stdin data\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: Buffer | string) => {\n const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n ndjsonHandler.processChunk(buffer);\n });\n\n // Handle stdin close\n process.stdin.on('end', () => {\n logInfo('stdin closed');\n });\n\n // Handle stdin errors\n process.stdin.on('error', (error: Error) => {\n logError(`stdin error: ${error.message}`);\n });\n}\n\n/**\n * Set up agent response handling.\n *\n * Wires up agent stdout to the message router for forwarding responses.\n *\n * @param runtimeManager - The runtime manager\n * @param router - The message router\n */\nfunction setupAgentResponseHandling(\n runtimeManager: AgentRuntimeManager,\n router: MessageRouter,\n): void {\n // Handle agent exit events\n runtimeManager.onAgentExit((agentId: string, code: number | null) => {\n logExit(agentId, code);\n });\n\n // Hook into runtime manager to set up stdout handling for new agents\n const originalGetOrSpawn = runtimeManager.getOrSpawn.bind(runtimeManager);\n runtimeManager.getOrSpawn = async function (agentId: string, spawnCommand: import('./registry/types.js').SpawnCommand) {\n const runtime = await originalGetOrSpawn(agentId, spawnCommand);\n\n // Set up stdout handler for this agent if not already done\n const proc = runtime.process;\n if (proc.stdout && !proc.stdout.listenerCount('data')) {\n let buffer = '';\n\n proc.stdout.on('data', (chunk: string) => {\n buffer += chunk;\n\n // Process complete NDJSON lines\n let newlineIndex;\n while ((newlineIndex = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newlineIndex);\n buffer = buffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n try {\n const response = JSON.parse(line);\n router.handleAgentResponse(agentId, response);\n } catch (err) {\n logError(`Failed to parse agent ${agentId} response: ${(err as Error).message}`);\n }\n }\n }\n });\n }\n\n // Log stderr from agent\n if (proc.stderr && !proc.stderr.listenerCount('data')) {\n proc.stderr.on('data', (chunk: string) => {\n // Log agent stderr to our stderr\n process.stderr.write(`[agent:${agentId}] ${chunk}`);\n });\n }\n\n return runtime;\n };\n}\n\n/**\n * Main entry point for the Registry Launcher.\n *\n * 1. Load configuration from command-line argument\n * 2. Handle auth CLI commands (--setup, --auth-status, --logout) if present\n * 3. Fetch and parse registry on startup\n * 4. Set up stdin NDJSON handler\n * 5. Wire up message router with registry and runtime manager\n * 6. Handle SIGTERM for graceful shutdown\n * 7. Exit with appropriate codes\n */\nasync function main(): Promise<void> {\n logInfo('Registry Launcher starting');\n\n // Parse command-line arguments\n const parsedArgs = parseArgs();\n\n // Handle auth CLI commands (Requirement 9.1, 9.2, 9.3)\n // These commands exit after completion and don't start the worker\n if (parsedArgs.setup) {\n logInfo('Running --setup command');\n const exitCode = await runSetupCommand();\n process.exit(exitCode);\n }\n\n if (parsedArgs.authStatus) {\n logInfo('Running --auth-status command');\n const exitCode = await runStatusCommand();\n process.exit(exitCode);\n }\n\n if (parsedArgs.logout) {\n logInfo('Running --logout command');\n const exitCode = await runLogoutCommand(parsedArgs.logoutProvider);\n process.exit(exitCode);\n }\n\n // Handle --login command (Requirement 3.1, 9.1)\n if (parsedArgs.login) {\n logInfo('Running --login command');\n\n // Validate that provider is specified\n if (!parsedArgs.loginProvider) {\n logError('Error: --login requires a provider argument.');\n logError(`Usage: --login <provider>`);\n logError(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n // Validate provider ID\n if (!isValidProviderId(parsedArgs.loginProvider)) {\n logError(`Error: Invalid provider '${parsedArgs.loginProvider}'.`);\n logError(`Supported providers: ${VALID_PROVIDER_IDS.join(', ')}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n const exitCode = await runLoginCommand(parsedArgs.loginProvider);\n process.exit(exitCode);\n }\n\n if (parsedArgs.configPath) {\n logInfo(`Loading configuration from: ${parsedArgs.configPath}`);\n }\n\n // Load configuration\n const config = loadConfig(parsedArgs.configPath);\n\n // CLI --custom-agents takes precedence over config file and env\n if (parsedArgs.customAgentsPath) {\n config.customAgentsPath = parsedArgs.customAgentsPath;\n }\n\n logInfo(`Configuration loaded: registryUrl=${config.registryUrl}, apiKeysPath=${config.apiKeysPath}, shutdownTimeoutSec=${config.shutdownTimeoutSec}`);\n\n // Load API keys\n const apiKeys = loadApiKeys(config.apiKeysPath);\n\n // Create registry index\n const registry = new RegistryIndex(config.registryUrl);\n\n // Fetch and parse registry on startup\n try {\n await registry.fetch();\n } catch (error) {\n if (error instanceof RegistryFetchError) {\n logError(`Failed to fetch registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n if (error instanceof RegistryParseError) {\n logError(`Failed to parse registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n logError(`Unexpected error fetching registry: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n // Load and merge custom agents if --custom-agents was provided\n if (config.customAgentsPath) {\n try {\n logInfo(`Loading custom agents from: ${config.customAgentsPath}`);\n const customAgents = loadCustomAgents(config.customAgentsPath);\n registry.mergeCustomAgents(customAgents);\n } catch (error) {\n if (error instanceof CustomAgentsLoadError) {\n logError(`Failed to load custom agents: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n if (error instanceof RegistryParseError) {\n logError(`Invalid custom agents file: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n logError(`Unexpected error loading custom agents: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n }\n\n // Create runtime manager\n const runtimeManager = new AgentRuntimeManager();\n\n // Create NDJSON handler for stdin/stdout\n const ndjsonHandler = new NDJSONHandler(process.stdout);\n\n // Create OAuth authentication components (Requirement 3.1, 10.3)\n const credentialStore = new CredentialStore();\n const tokenManager = new TokenManager({\n credentialStore,\n providerResolver: getProvider,\n });\n const authManager = new AuthManager({\n credentialStore,\n tokenManager,\n legacyApiKeys: apiKeys,\n });\n logInfo('OAuth authentication manager initialized');\n\n // Create message router with AuthManager for OAuth support\n const router = new MessageRouter(\n registry,\n runtimeManager,\n (message: object) => ndjsonHandler.write(message),\n apiKeys,\n authManager,\n );\n\n // Set up signal handlers for graceful shutdown\n const shutdownTimeoutMs = config.shutdownTimeoutSec * 1000;\n setupSignalHandlers(runtimeManager, shutdownTimeoutMs);\n\n // Set up agent response handling\n setupAgentResponseHandling(runtimeManager, router);\n\n // Set up stdin handler and wire up routing\n setupStdinHandler(router, ndjsonHandler);\n\n logInfo('Registry Launcher ready, waiting for messages');\n}\n\n// Run main and handle any uncaught errors\nmain().catch((error: Error) => {\n logError(`Fatal error: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n});\n"],
5
+ "mappings": "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",
6
6
  "names": ["readFileSync", "logWarning", "readFileSync", "ENV_REGISTRY_URL", "logInfo", "isNonEmptyString", "logWarning", "logError", "process", "resolve", "spawn", "VALID_AUTH_METHOD_TYPES", "VALID_AUTH_METHOD_TYPES", "parseAuthMethods", "logError", "parseAuthMethod", "logInfo", "spawn", "requiredProviderId", "msg", "resolve", "success", "logInfo", "logError", "resolve", "randomBytes", "randomBytes", "URL", "resolve", "hostname", "resolve", "crypto", "legacyKeys", "agentProvider", "logInfo", "logError"]
7
7
  }