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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +181 -21
  2. package/out/dist/workers-registry/acp-registry/index.js +128 -2
  3. package/out/dist/workers-registry/acp-registry/index.js.map +4 -4
  4. package/out/dist/workers-registry/acp-worker/index.js +1 -3
  5. package/out/dist/workers-registry/acp-worker/index.js.map +4 -4
  6. package/out/dist/workers-registry/index.d.ts +1 -0
  7. package/out/dist/workers-registry/index.js +6 -0
  8. package/out/dist/workers-registry/openai-agent/index.js +1 -1
  9. package/out/dist/workers-registry/openai-agent/index.js.map +2 -2
  10. package/out/dist/workers-registry/registry-launcher/index.js +131 -0
  11. package/out/dist/workers-registry/registry-launcher/index.js.map +7 -0
  12. package/out/tsc/workers-registry/acp-worker/src/index.d.ts +0 -10
  13. package/out/tsc/workers-registry/registry-launcher/src/auth/auth-manager.d.ts +392 -0
  14. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/cli.property.test.d.ts +22 -0
  15. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/index.d.ts +9 -0
  16. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/login-command.d.ts +32 -0
  17. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/logout-command.d.ts +25 -0
  18. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/setup-command.d.ts +25 -0
  19. package/out/tsc/workers-registry/registry-launcher/src/auth/cli/status-command.d.ts +21 -0
  20. package/out/tsc/workers-registry/registry-launcher/src/auth/errors.d.ts +190 -0
  21. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/agent-auth-flow.d.ts +146 -0
  22. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/callback-server.d.ts +131 -0
  23. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/callback-server.test.d.ts +1 -0
  24. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/index.d.ts +11 -0
  25. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/terminal-auth-flow.d.ts +252 -0
  26. package/out/tsc/workers-registry/registry-launcher/src/auth/flows/terminal-auth-flow.test.d.ts +1 -0
  27. package/out/tsc/workers-registry/registry-launcher/src/auth/index.d.ts +33 -0
  28. package/out/tsc/workers-registry/registry-launcher/src/auth/integration.test.d.ts +1 -0
  29. package/out/tsc/workers-registry/registry-launcher/src/auth/model-credentials/anthropic-api-key.d.ts +154 -0
  30. package/out/tsc/workers-registry/registry-launcher/src/auth/model-credentials/index.d.ts +20 -0
  31. package/out/tsc/workers-registry/registry-launcher/src/auth/model-credentials/model-credentials.test.d.ts +1 -0
  32. package/out/tsc/workers-registry/registry-launcher/src/auth/model-credentials/openai-api-key.d.ts +182 -0
  33. package/out/tsc/workers-registry/registry-launcher/src/auth/model-credentials/types.d.ts +186 -0
  34. package/out/tsc/workers-registry/registry-launcher/src/auth/pkce.d.ts +61 -0
  35. package/out/tsc/workers-registry/registry-launcher/src/auth/pkce.property.test.d.ts +1 -0
  36. package/out/tsc/workers-registry/registry-launcher/src/auth/pkce.test.d.ts +1 -0
  37. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/base-provider.d.ts +138 -0
  38. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/base-provider.test.d.ts +1 -0
  39. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/cognito-provider.d.ts +44 -0
  40. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/concrete-providers.test.d.ts +1 -0
  41. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/entra-provider.d.ts +54 -0
  42. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/github-provider.d.ts +19 -0
  43. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/google-provider.d.ts +19 -0
  44. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/index.d.ts +107 -0
  45. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/index.test.d.ts +1 -0
  46. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/oidc-provider.d.ts +413 -0
  47. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/oidc-provider.property.test.d.ts +1 -0
  48. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/oidc-provider.test.d.ts +1 -0
  49. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/providers.property.test.d.ts +1 -0
  50. package/out/tsc/workers-registry/registry-launcher/src/auth/providers/types.d.ts +28 -0
  51. package/out/tsc/workers-registry/registry-launcher/src/auth/session.d.ts +251 -0
  52. package/out/tsc/workers-registry/registry-launcher/src/auth/session.property.test.d.ts +1 -0
  53. package/out/tsc/workers-registry/registry-launcher/src/auth/session.test.d.ts +1 -0
  54. package/out/tsc/workers-registry/registry-launcher/src/auth/state.d.ts +26 -0
  55. package/out/tsc/workers-registry/registry-launcher/src/auth/state.property.test.d.ts +1 -0
  56. package/out/tsc/workers-registry/registry-launcher/src/auth/state.test.d.ts +1 -0
  57. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/credential-store.d.ts +98 -0
  58. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/credential-store.test.d.ts +1 -0
  59. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/encrypted-file-backend.d.ts +101 -0
  60. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/encrypted-file-backend.test.d.ts +1 -0
  61. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/index.d.ts +12 -0
  62. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/keychain-backend.d.ts +80 -0
  63. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/keychain-backend.test.d.ts +1 -0
  64. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/memory-backend.d.ts +54 -0
  65. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/storage.property.test.d.ts +1 -0
  66. package/out/tsc/workers-registry/registry-launcher/src/auth/storage/types.d.ts +44 -0
  67. package/out/tsc/workers-registry/registry-launcher/src/auth/token-manager.d.ts +171 -0
  68. package/out/tsc/workers-registry/registry-launcher/src/auth/token-manager.property.test.d.ts +1 -0
  69. package/out/tsc/workers-registry/registry-launcher/src/auth/token-manager.test.d.ts +1 -0
  70. package/out/tsc/workers-registry/registry-launcher/src/auth/types.d.ts +369 -0
  71. package/out/tsc/workers-registry/registry-launcher/src/auth/types.test.d.ts +1 -0
  72. package/out/tsc/workers-registry/registry-launcher/src/config/config.property.test.d.ts +1 -0
  73. package/out/tsc/workers-registry/registry-launcher/src/config/config.test.d.ts +1 -0
  74. package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/registry/index.d.ts +51 -2
  75. package/out/tsc/workers-registry/registry-launcher/src/registry/index.property.test.d.ts +1 -0
  76. package/out/tsc/workers-registry/registry-launcher/src/registry/index.test.d.ts +1 -0
  77. package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/registry/types.d.ts +24 -0
  78. package/out/tsc/workers-registry/registry-launcher/src/router/message-router.d.ts +770 -0
  79. package/out/tsc/workers-registry/registry-launcher/src/router/message-router.property.test.d.ts +1 -0
  80. package/out/tsc/workers-registry/registry-launcher/src/router/message-router.test.d.ts +1 -0
  81. package/out/tsc/workers-registry/registry-launcher/src/runtime/manager.property.test.d.ts +1 -0
  82. package/out/tsc/workers-registry/registry-launcher/src/runtime/manager.test.d.ts +1 -0
  83. package/out/tsc/workers-registry/registry-launcher/src/stream/ndjson-handler.property.test.d.ts +1 -0
  84. package/out/tsc/workers-registry/registry-launcher/src/stream/ndjson-handler.test.d.ts +1 -0
  85. package/out/tsc/workers-registry/registry-launcher/tests/e2e/auth-flow.e2e.test.d.ts +1 -0
  86. package/out/tsc/workers-registry/registry-launcher/tests/e2e/auth-required-flow.e2e.test.d.ts +1 -0
  87. package/out/tsc/workers-registry/registry-launcher/tests/e2e/helpers/api-keys.d.ts +32 -0
  88. package/out/tsc/workers-registry/registry-launcher/tests/e2e/helpers/index.d.ts +17 -0
  89. package/out/tsc/workers-registry/registry-launcher/tests/e2e/helpers/launcher-harness.d.ts +101 -0
  90. package/out/tsc/workers-registry/registry-launcher/tests/e2e/helpers/registry-server.d.ts +46 -0
  91. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-agent-auth.e2e.test.d.ts +1 -0
  92. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-api-keys.e2e.test.d.ts +1 -0
  93. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-auth-required.e2e.test.d.ts +1 -0
  94. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-cli.e2e.test.d.ts +1 -0
  95. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-concurrent.e2e.test.d.ts +1 -0
  96. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-lifecycle.e2e.test.d.ts +1 -0
  97. package/out/tsc/workers-registry/registry-launcher/tests/e2e/production-terminal-auth.e2e.test.d.ts +1 -0
  98. package/out/tsc/workers-registry/registry-launcher/tests/integration/agent-auth.integration.test.d.ts +9 -0
  99. package/out/tsc/workers-registry/registry-launcher/tests/integration/registry-launcher.test.d.ts +1 -0
  100. package/out/tsc/workers-registry/registry-launcher/tests/integration/terminal-auth.integration.test.d.ts +12 -0
  101. package/package.json +16 -11
  102. package/out/tsc/workers-registry/acp-worker/src/registry-launcher/router/message-router.d.ts +0 -199
  103. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/config/config.property.test.d.ts → registry-launcher/src/auth/auth-manager.property.test.d.ts} +0 -0
  104. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/config/config.test.d.ts → registry-launcher/src/auth/auth-manager.test.d.ts} +0 -0
  105. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/registry/index.property.test.d.ts → registry-launcher/src/auth/cli/cli.test.d.ts} +0 -0
  106. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/registry/index.test.d.ts → registry-launcher/src/auth/cli/login-command.test.d.ts} +0 -0
  107. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/router/message-router.property.test.d.ts → registry-launcher/src/auth/cli/provider-config.test.d.ts} +0 -0
  108. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/router/message-router.test.d.ts → registry-launcher/src/auth/cli/setup-command.test.d.ts} +0 -0
  109. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/runtime/manager.property.test.d.ts → registry-launcher/src/auth/cli/status-command.test.d.ts} +0 -0
  110. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/runtime/manager.test.d.ts → registry-launcher/src/auth/errors.property.test.d.ts} +0 -0
  111. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/stream/ndjson-handler.property.test.d.ts → registry-launcher/src/auth/errors.test.d.ts} +0 -0
  112. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher/stream/ndjson-handler.test.d.ts → registry-launcher/src/auth/flows/agent-auth-flow.test.d.ts} +0 -0
  113. /package/out/tsc/workers-registry/{acp-worker/tests/integration/registry-launcher.test.d.ts → registry-launcher/src/auth/flows/callback-server.property.test.d.ts} +0 -0
  114. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/config/api-keys.d.ts +0 -0
  115. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/config/config.d.ts +0 -0
  116. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/config/index.d.ts +0 -0
  117. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/config/types.d.ts +0 -0
  118. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/index.d.ts +0 -0
  119. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/log.d.ts +0 -0
  120. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/log.test.d.ts +0 -0
  121. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/registry/resolver.d.ts +0 -0
  122. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/router/index.d.ts +0 -0
  123. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/runtime/agent-runtime.d.ts +0 -0
  124. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/runtime/index.d.ts +0 -0
  125. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/runtime/manager.d.ts +0 -0
  126. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/runtime/types.d.ts +0 -0
  127. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/stream/index.d.ts +0 -0
  128. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/stream/ndjson-handler.d.ts +0 -0
  129. /package/out/tsc/workers-registry/{acp-worker/src/registry-launcher → registry-launcher/src}/test-utils/index.d.ts +0 -0
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ import{readFileSync}from"node:fs";var DEFAULT_CONFIG={registryUrl:"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",apiKeysPath:"./api-keys.json",shutdownTimeoutSec:5};var ENV_REGISTRY_URL="ACP_REGISTRY_URL";var ENV_API_KEYS_PATH="ACP_API_KEYS_PATH";var ENV_CUSTOM_AGENTS_PATH="ACP_CUSTOM_AGENTS_PATH";function logWarning(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [config] ${message}`)}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isPositiveNumber(value){return typeof value==="number"&&value>0&&Number.isFinite(value)}function parseConfigObject(obj){const config={...DEFAULT_CONFIG};if(obj===null||typeof obj!=="object"){logWarning("Config file does not contain a valid object, using defaults");return config}const rawConfig=obj;if("registryUrl"in rawConfig){if(isNonEmptyString(rawConfig.registryUrl)){config.registryUrl=rawConfig.registryUrl}else{logWarning('Config field "registryUrl" is not a valid string, using default')}}if("apiKeysPath"in rawConfig){if(isNonEmptyString(rawConfig.apiKeysPath)){config.apiKeysPath=rawConfig.apiKeysPath}else{logWarning('Config field "apiKeysPath" is not a valid string, using default')}}if("shutdownTimeoutSec"in rawConfig){if(isPositiveNumber(rawConfig.shutdownTimeoutSec)){config.shutdownTimeoutSec=rawConfig.shutdownTimeoutSec}else{logWarning('Config field "shutdownTimeoutSec" is not a valid positive number, using default')}}if("customAgentsPath"in rawConfig){if(isNonEmptyString(rawConfig.customAgentsPath)){config.customAgentsPath=rawConfig.customAgentsPath}else{logWarning('Config field "customAgentsPath" is not a valid string, ignoring')}}return config}function applyEnvironmentOverrides(config){const envRegistryUrl=process.env[ENV_REGISTRY_URL];const envApiKeysPath=process.env[ENV_API_KEYS_PATH];const envCustomAgentsPath=process.env[ENV_CUSTOM_AGENTS_PATH];const overrides={};if(isNonEmptyString(envRegistryUrl)){overrides.registryUrl=envRegistryUrl}if(isNonEmptyString(envApiKeysPath)){overrides.apiKeysPath=envApiKeysPath}if(isNonEmptyString(envCustomAgentsPath)){overrides.customAgentsPath=envCustomAgentsPath}return{...config,...overrides}}function loadConfig(configPath){let config={...DEFAULT_CONFIG};if(configPath){try{const fileContent=readFileSync(configPath,"utf-8");const parsed=JSON.parse(fileContent);config=parseConfigObject(parsed)}catch(error){if(error instanceof SyntaxError){logWarning(`Config file "${configPath}" contains malformed JSON, using defaults`)}else if(error.code==="ENOENT"){logWarning(`Config file "${configPath}" not found, using defaults`)}else if(error.code==="EACCES"){logWarning(`Config file "${configPath}" is not readable, using defaults`)}else{logWarning(`Failed to read config file "${configPath}": ${error.message}, using defaults`)}}}config=applyEnvironmentOverrides(config);return config}import{readFileSync as readFileSync2}from"node:fs";function logWarning2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [api-keys] ${message}`)}function logInfo(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [api-keys] ${message}`)}function loadApiKeys(apiKeysPath){try{const fileContent=readFileSync2(apiKeysPath,"utf-8");const parsed=JSON.parse(fileContent);if(!parsed.agents||typeof parsed.agents!=="object"){logWarning2(`API keys file "${apiKeysPath}" does not contain valid "agents" object`);return{}}const agentsWithKeys=Object.entries(parsed.agents).filter(([_,keys])=>keys.apiKey&&keys.apiKey.length>0);logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from "${apiKeysPath}"`);return parsed.agents}catch(error){if(error.code==="ENOENT"){logWarning2(`API keys file "${apiKeysPath}" not found, agents will not be authenticated`)}else if(error instanceof SyntaxError){logWarning2(`API keys file "${apiKeysPath}" contains malformed JSON`)}else{logWarning2(`Failed to read API keys file "${apiKeysPath}": ${error.message}`)}return{}}}function getAgentApiKey(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.apiKey||keys.apiKey.length===0){return void 0}return keys.apiKey}function getAgentEnv(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.env){return{}}return keys.env}var PlatformNotSupportedError=class extends Error{constructor(agentId,platform){super(`Platform not supported: ${platform} for agent ${agentId}`);this.agentId=agentId;this.platform=platform;this.name="PlatformNotSupportedError"}};var NoDistributionError=class extends Error{constructor(agentId){super(`No supported distribution type for agent ${agentId}`);this.agentId=agentId;this.name="NoDistributionError"}};function getCurrentPlatform(){const platform=process.platform;const arch=process.arch;if(platform==="darwin"&&arch==="arm64")return"darwin-aarch64";if(platform==="darwin"&&arch==="x64")return"darwin-x86_64";if(platform==="linux"&&arch==="arm64")return"linux-aarch64";if(platform==="linux"&&arch==="x64")return"linux-x86_64";if(platform==="win32"&&arch==="arm64")return"windows-aarch64";if(platform==="win32"&&arch==="x64")return"windows-x86_64";return"linux-x86_64"}function resolveBinary(distribution,agentId){const currentPlatform=getCurrentPlatform();const target=distribution[currentPlatform];if(!target){throw new PlatformNotSupportedError(agentId,currentPlatform)}return{command:target.cmd,args:target.args??[],env:target.env}}function resolveNpx(distribution){return{command:"npx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolveUvx(distribution){return{command:"uvx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolve(distribution,agentId){if(distribution.npx){return resolveNpx(distribution.npx)}if(distribution.uvx){return resolveUvx(distribution.uvx)}if(distribution.binary){return resolveBinary(distribution.binary,agentId)}throw new NoDistributionError(agentId)}import{readFileSync as readFileSync3}from"node:fs";var ENV_REGISTRY_URL2="ACP_REGISTRY_URL";var RegistryFetchError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="RegistryFetchError"}};var RegistryParseError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="RegistryParseError"}};var AgentNotFoundError=class extends Error{constructor(agentId){super(`Agent not found: ${agentId}`);this.agentId=agentId;this.name="AgentNotFoundError"}};function logError(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [registry] ${message}`)}function logInfo2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [registry] ${message}`)}function isNonEmptyString2(value){return typeof value==="string"&&value.length>0}function isValidDistribution(value){if(value===null||typeof value!=="object"){return false}const dist=value;const hasBinary=dist.binary!==void 0&&typeof dist.binary==="object";const hasNpx=dist.npx!==void 0&&typeof dist.npx==="object";const hasUvx=dist.uvx!==void 0&&typeof dist.uvx==="object";if(!hasBinary&&!hasNpx&&!hasUvx){return false}if(hasNpx){const npx=dist.npx;if(!isNonEmptyString2(npx.package)){return false}}if(hasUvx){const uvx=dist.uvx;if(!isNonEmptyString2(uvx.package)){return false}}return true}function parseMcpServer(value,agentIndex,serverIndex){if(value===null||typeof value!=="object"){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] is not an object, skipping`);return null}const raw=value;if(!isNonEmptyString2(raw.name)){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing "name" field, skipping`);return null}if(!isNonEmptyString2(raw.command)){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing "command" field, skipping`);return null}const server={name:raw.name,command:raw.command};if(Array.isArray(raw.args)){server.args=raw.args.filter(a=>typeof a==="string")}if(raw.env!==null&&typeof raw.env==="object"&&!Array.isArray(raw.env)){const env={};for(const[key,val]of Object.entries(raw.env)){if(typeof val==="string"){env[key]=val}}if(Object.keys(env).length>0){server.env=env}}return server}function parseMcpServers(servers,agentIndex){const result=[];for(let i=0;i<servers.length;i++){const server=parseMcpServer(servers[i],agentIndex,i);if(server!==null){result.push(server)}}return result}function logWarning3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [registry] ${message}`)}var VALID_AUTH_METHOD_TYPES=["oauth2","api-key"];function parseAuthMethod(value,agentIndex,methodIndex){if(value===null||typeof value!=="object"){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] is not an object, skipping`);return null}const raw=value;if(!isNonEmptyString2(raw.id)){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing "id" field, skipping`);return null}if(!isNonEmptyString2(raw.type)||!VALID_AUTH_METHOD_TYPES.includes(raw.type)){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing "type" field (must be 'oauth2' or 'api-key'), skipping`);return null}const method={id:raw.id,type:raw.type};if(isNonEmptyString2(raw.providerId)){method.providerId=raw.providerId}return method}function parseAuthMethods(methods,agentIndex){const result=[];for(let i=0;i<methods.length;i++){const method=parseAuthMethod(methods[i],agentIndex,i);if(method!==null){result.push(method)}}return result}function parseAgent(value,index){if(value===null||typeof value!=="object"){throw new RegistryParseError(`Agent at index ${index} is not an object`)}const raw=value;if(!isNonEmptyString2(raw.id)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "id" field`)}if(!isNonEmptyString2(raw.name)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "name" field`)}if(!isNonEmptyString2(raw.version)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "version" field`)}if(!isValidDistribution(raw.distribution)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "distribution" field`)}const agent={id:raw.id,name:raw.name,version:raw.version,distribution:raw.distribution};if(typeof raw.description==="string"){agent.description=raw.description}if(typeof raw.repository==="string"){agent.repository=raw.repository}if(Array.isArray(raw.authors)){agent.authors=raw.authors.filter(a=>typeof a==="string")}if(typeof raw.license==="string"){agent.license=raw.license}if(typeof raw.icon==="string"){agent.icon=raw.icon}if(Array.isArray(raw.mcpServers)){const mcpServers=parseMcpServers(raw.mcpServers,index);if(mcpServers.length>0){agent.mcpServers=mcpServers}}if(typeof raw.authRequired==="boolean"){agent.authRequired=raw.authRequired}if(Array.isArray(raw.authMethods)){const authMethods=parseAuthMethods(raw.authMethods,index);if(authMethods.length>0){agent.authMethods=authMethods}}return agent}function parseRegistry(data){if(data===null||typeof data!=="object"){throw new RegistryParseError("Registry data is not an object")}const raw=data;if(!isNonEmptyString2(raw.version)){throw new RegistryParseError('Registry has invalid or missing "version" field')}if(!Array.isArray(raw.agents)){throw new RegistryParseError('Registry has invalid or missing "agents" field')}const agents=[];for(let i=0;i<raw.agents.length;i++){agents.push(parseAgent(raw.agents[i],i))}return{version:raw.version,agents}}var RegistryIndex=class{registryUrl;registry=null;agentMap=new Map;authRequirementsCache=new Map;constructor(registryUrl){const envUrl=process.env[ENV_REGISTRY_URL2];this.registryUrl=isNonEmptyString2(envUrl)?envUrl:registryUrl}async fetch(){logInfo2(`Fetching registry from ${this.registryUrl}`);let response;try{response=await fetch(this.registryUrl)}catch(error){const message=`Failed to fetch registry from ${this.registryUrl}: ${error.message}`;logError(message);throw new RegistryFetchError(message,error)}if(!response.ok){const message=`Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;logError(message);throw new RegistryFetchError(message)}let text;try{text=await response.text()}catch(error){const message=`Failed to read registry response body: ${error.message}`;logError(message);throw new RegistryFetchError(message,error)}let data;try{data=JSON.parse(text)}catch(error){const message=`Failed to parse registry JSON: ${error.message}`;logError(message);throw new RegistryParseError(message,error)}try{this.registry=parseRegistry(data)}catch(error){if(error instanceof RegistryParseError){logError(error.message);throw error}const message=`Failed to validate registry data: ${error.message}`;logError(message);throw new RegistryParseError(message,error)}this.agentMap.clear();this.authRequirementsCache.clear();for(const agent of this.registry.agents){this.agentMap.set(agent.id,agent)}logInfo2(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`)}lookup(agentId){return this.agentMap.get(agentId)}resolve(agentId){const agent=this.lookup(agentId);if(!agent){throw new AgentNotFoundError(agentId)}return resolve(agent.distribution,agentId)}getRegistry(){return this.registry}getAuthRequirements(agentId){const cached=this.authRequirementsCache.get(agentId);if(cached!==void 0){return cached}const agent=this.lookup(agentId);if(!agent){return void 0}const authMethods=agent.authMethods??[];const hasOAuthMethods=authMethods.some(m=>m.type==="oauth2");const authRequired=agent.authRequired??hasOAuthMethods;let primaryOAuthProviderId;for(const method of authMethods){if(method.type==="oauth2"&&method.providerId){primaryOAuthProviderId=method.providerId;break}}const requirements={authRequired,authMethods,primaryOAuthProviderId};this.authRequirementsCache.set(agentId,requirements);if(authRequired){logInfo2(`Agent "${agentId}" requires authentication${primaryOAuthProviderId?` (OAuth provider: ${primaryOAuthProviderId})`:""}`)}return requirements}clearAuthRequirementsCache(agentId){if(agentId){this.authRequirementsCache.delete(agentId);logInfo2(`Cleared auth requirements cache for agent "${agentId}"`)}else{this.authRequirementsCache.clear();logInfo2("Cleared all auth requirements cache")}}mergeCustomAgents(agents){if(agents.length===0){return}if(!this.registry){this.registry={version:"custom",agents:[]}}for(const agent of agents){const existingIndex=this.registry.agents.findIndex(a=>a.id===agent.id);if(existingIndex!==-1){this.registry.agents[existingIndex]=agent;logInfo2(`Custom agent "${agent.id}" overrides remote registry entry`)}else{this.registry.agents.push(agent);logInfo2(`Custom agent "${agent.id}" added to registry`)}this.agentMap.set(agent.id,agent);this.authRequirementsCache.delete(agent.id)}logInfo2(`Registry now contains ${this.registry.agents.length} agents (${agents.length} custom)`)}};var CustomAgentsLoadError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="CustomAgentsLoadError"}};function loadCustomAgents(filePath){let fileContent;try{fileContent=readFileSync3(filePath,"utf-8")}catch(error){if(error.code==="ENOENT"){throw new CustomAgentsLoadError(`Custom agents file not found: ${filePath}`)}if(error.code==="EACCES"){throw new CustomAgentsLoadError(`Custom agents file not readable: ${filePath}`)}throw new CustomAgentsLoadError(`Failed to read custom agents file "${filePath}": ${error.message}`,error)}let data;try{data=JSON.parse(fileContent)}catch(error){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" contains malformed JSON: ${error.message}`,error)}if(data===null||typeof data!=="object"){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" does not contain a valid object`)}const raw=data;if(!Array.isArray(raw.agents)){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" does not contain a valid "agents" array`)}const registryData={version:"custom",agents:raw.agents};const parsed=parseRegistry(registryData);return parsed.agents}function logError2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`)}var NDJSONHandler=class{buffer="";output;messageCallback=null;errorCallback=null;constructor(output){this.output=output}onMessage(callback){this.messageCallback=callback}onError(callback){this.errorCallback=callback}write(message){if(!this.output.writable){return false}try{const json=JSON.stringify(message);this.output.write(json+"\n");return true}catch{return false}}processChunk(chunk){this.buffer+=chunk.toString("utf-8");this.processBuffer()}processBuffer(){let newlineIndex;while((newlineIndex=this.buffer.indexOf("\n"))!==-1){const line=this.buffer.slice(0,newlineIndex);this.buffer=this.buffer.slice(newlineIndex+1);if(line.trim().length===0){continue}this.parseLine(line)}}parseLine(line){try{const message=JSON.parse(line);if(message===null||typeof message!=="object"){const error=new Error("Parsed JSON is not an object");logError2(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);this.errorCallback?.(error,line);return}this.messageCallback?.(message)}catch(error){logError2(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);this.errorCallback?.(error,line)}}truncateLine(line,maxLength=100){if(line.length<=maxLength){return line}return line.slice(0,maxLength)+"..."}};import{spawn}from"child_process";var DEFAULT_TERMINATE_TIMEOUT_MS=5e3;var AgentRuntimeImpl=class _AgentRuntimeImpl{agentId;state;process;onExitCallback;constructor(agentId,process2,onExit){this.agentId=agentId;this.process=process2;this.state="starting";this.onExitCallback=onExit;this.setupProcessHandlers()}static spawn(agentId,spawnCommand,onExit){const childProcess=spawn(spawnCommand.command,spawnCommand.args,{stdio:["pipe","pipe","pipe"],env:{...process.env,...spawnCommand.env},detached:false});if(childProcess.stdout){childProcess.stdout.setEncoding("utf8")}if(childProcess.stderr){childProcess.stderr.setEncoding("utf8")}return new _AgentRuntimeImpl(agentId,childProcess,onExit)}setupProcessHandlers(){this.process.on("spawn",()=>{if(this.state==="starting"){this.state="running"}});this.process.on("error",error=>{this.state="stopped";process.stderr.write(`[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}
3
+ `)});this.process.on("exit",(code,signal)=>{this.state="stopped";if(this.onExitCallback){this.onExitCallback(code,signal)}});if(this.process.stdin){this.process.stdin.on("error",error=>{process.stderr.write(`[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}
4
+ `)})}}write(message){if(this.state!=="running"&&this.state!=="starting"){return false}if(!this.process.stdin||this.process.stdin.destroyed){return false}try{const ndjsonLine=JSON.stringify(message)+"\n";return this.process.stdin.write(ndjsonLine)}catch{return false}}async terminate(timeout=DEFAULT_TERMINATE_TIMEOUT_MS){if(this.state==="stopped"){return}if(this.state==="stopping"){return this.waitForExit()}this.state="stopping";if(this.process.stdin&&!this.process.stdin.destroyed){this.process.stdin.end()}this.process.kill("SIGTERM");const exitPromise=this.waitForExit();const timeoutPromise=new Promise(resolve2=>{setTimeout(()=>resolve2("timeout"),timeout)});const result=await Promise.race([exitPromise,timeoutPromise]);if(result==="timeout"&&!this.process.killed&&this.process.exitCode===null){this.process.kill("SIGKILL");await this.waitForExit()}}waitForExit(){if(this.state==="stopped"){return Promise.resolve()}return new Promise(resolve2=>{this.process.once("exit",()=>{resolve2()})})}};var DEFAULT_SHUTDOWN_TIMEOUT_MS=5e3;var AgentRuntimeManager=class{runtimes=new Map;exitCallbacks=[];async getOrSpawn(agentId,spawnCommand){const existing=this.runtimes.get(agentId);if(existing&&existing.state!=="stopped"){return existing}const runtime=AgentRuntimeImpl.spawn(agentId,spawnCommand,(code,_signal)=>{this.handleAgentExit(agentId,code)});this.runtimes.set(agentId,runtime);return runtime}get(agentId){return this.runtimes.get(agentId)}async terminate(agentId,timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const runtime=this.runtimes.get(agentId);if(!runtime){return}await runtime.terminate(timeout);this.runtimes.delete(agentId)}async terminateAll(timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const terminatePromises=[];for(const[agentId,runtime]of this.runtimes){if(runtime.state!=="stopped"){terminatePromises.push(runtime.terminate(timeout).then(()=>{this.runtimes.delete(agentId)}))}}await Promise.all(terminatePromises)}onAgentExit(callback){this.exitCallbacks.push(callback)}handleAgentExit(agentId,code){this.runtimes.delete(agentId);for(const callback of this.exitCallbacks){try{callback(agentId,code)}catch{}}}get size(){return this.runtimes.size}has(agentId){return this.runtimes.has(agentId)}};import{spawn as spawn2}from"node:child_process";var VALID_AUTH_METHOD_TYPES2=["oauth2","api-key"];function isValidAuthMethodType(value){return typeof value==="string"&&VALID_AUTH_METHOD_TYPES2.includes(value)}var DEFAULT_AUTH_METHOD_PRECEDENCE={methodPrecedence:["oauth2","api-key"],failFastOnUnsupported:true,failFastOnAmbiguous:true};var VALID_PROVIDER_IDS=["github","google","cognito","azure","oidc"];function isValidProviderId(value){return typeof value==="string"&&VALID_PROVIDER_IDS.includes(value)}var AUTH_METHOD_ID_TO_PROVIDER_ID={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","oauth2-oidc":"oidc","github":"github","google":"google","cognito":"cognito","azure":"azure","oidc":"oidc"};var VALID_AUTH_METHOD_IDS=Object.keys(AUTH_METHOD_ID_TO_PROVIDER_ID);var RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003,AUTH_REQUIRED:-32004};var AUTH_METHOD_ID_TO_PROVIDER={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","agent-github":"github","agent-google":"google","agent-cognito":"cognito","agent-azure":"azure","github-api-key":"github","google-api-key":"google","azure-api-key":"azure","cognito-api-key":"cognito"};var MAX_AUTH_METHODS=50;var MAX_METHOD_ID_LENGTH=128;var VALID_AUTH_METHOD_TYPES3=["oauth2","agent","terminal","api-key"];function parseAuthMethods2(raw){if(!Array.isArray(raw)){logError3("authMethods is not an array, skipping parsing");return[]}const methods=raw.slice(0,MAX_AUTH_METHODS);const parsed=[];const seenIds=new Set;for(const method of methods){const result=parseAuthMethod2(method,seenIds);if(result){parsed.push(result);seenIds.add(result.id)}}logInfo3(`Parsed ${parsed.length} valid auth methods from ${methods.length} raw methods`);return parsed}function parseAuthMethod2(method,seenIds){if(method===null||typeof method!=="object"){return null}const obj=method;const id=obj.id;if(typeof id!=="string"||id.length===0||id.length>MAX_METHOD_ID_LENGTH){logError3(`Invalid auth method id: ${typeof id==="string"?id.substring(0,50):typeof id}`);return null}if(seenIds.has(id)){logInfo3(`Skipping duplicate auth method id: ${id}`);return null}const type=obj.type;if(typeof type!=="string"||!VALID_AUTH_METHOD_TYPES3.includes(type)){logError3(`Invalid auth method type for id ${id}: ${type}`);return null}const rawProviderId=obj.providerId;let providerId;if(rawProviderId!==void 0){if(isValidProviderId(rawProviderId)){providerId=rawProviderId}else{logError3(`Invalid providerId in auth method ${id}: ${rawProviderId}`)}}const mappedProviderId=AUTH_METHOD_ID_TO_PROVIDER[id];if(providerId&&mappedProviderId&&providerId!==mappedProviderId){logError3(`Conflict: auth method ${id} has providerId ${providerId} but maps to ${mappedProviderId}, rejecting`);return null}const resolvedProviderId=providerId??mappedProviderId;if(type==="oauth2"){if(!resolvedProviderId){logError3(`OAuth auth method ${id} has no valid providerId, skipping`);return null}return{kind:"oauth2",id,providerId:resolvedProviderId}}if(type==="agent"){return{kind:"agent",id,providerId:resolvedProviderId}}if(type==="terminal"){const args=Array.isArray(obj.args)?obj.args.filter(a=>typeof a==="string"):void 0;const env=obj.env&&typeof obj.env==="object"&&!Array.isArray(obj.env)?Object.fromEntries(Object.entries(obj.env).filter(([,v])=>typeof v==="string")):void 0;return{kind:"terminal",id,args,env}}if(type==="api-key"){return{kind:"api-key",id,providerId:resolvedProviderId}}return null}function getOAuthMethods(methods){return methods.filter(m=>m.kind==="oauth2")}function getAgentAuthMethods(methods){return methods.filter(m=>m.kind==="agent")}function getTerminalAuthMethods(methods){return methods.filter(m=>m.kind==="terminal")}function getApiKeyMethods(methods){return methods.filter(m=>m.kind==="api-key")}var AGENT_AUTH_TIMEOUT_MS=5*60*1e3;var TERMINAL_AUTH_TIMEOUT_MS=10*60*1e3;var QUEUED_REQUEST_TIMEOUT_MS=5*60*1e3;function logError3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [router] ${message}`)}function logInfo3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [router] ${message}`)}function createErrorResponse(id,code,message,data){const response={jsonrpc:"2.0",id,error:{code,message}};if(data!==void 0){response.error.data=data}return response}function extractAgentId(message){const msg=message;const agentId=msg.agentId;if(typeof agentId==="string"&&agentId.length>0){return agentId}return void 0}function extractId(message){const msg=message;const id=msg.id;if(typeof id==="string"||typeof id==="number"){return id}return null}function transformMessage(message){const{agentId:_,...rest}=message;return rest}var MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;spawnFn;isStdinTTY;isStdoutTTY;authManager;pendingRequests=new Map;authState=new Map;agentOAuthRequirements=new Map;requestQueue=new Map;pendingAuthenticateRequests=new Map;sessionIdMap=new Map;autoOAuth;constructor(registry,runtimeManager,writeCallback,apiKeys={},authManager,autoOAuth,deps){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys;this.authManager=authManager;this.autoOAuth=autoOAuth??this.getAutoOAuthFromEnv();this.spawnFn=deps?.spawnFn??spawn2;this.isStdinTTY=deps?.isStdinTTY??(()=>process.stdin.isTTY??false);this.isStdoutTTY=deps?.isStdoutTTY??(()=>process.stdout.isTTY??false)}getAutoOAuthFromEnv(){const envValue=process.env.AUTH_AUTO_OAUTH;return envValue==="true"||envValue==="1"||envValue==="yes"}getSupportedAuthMethods(){const methods=[{id:"api-key",type:"api-key"}];if(this.authManager){methods.push({id:"oauth2-github",type:"oauth2",providerId:"github"},{id:"oauth2-google",type:"oauth2",providerId:"google"},{id:"oauth2-cognito",type:"oauth2",providerId:"cognito"},{id:"oauth2-azure",type:"oauth2",providerId:"azure"},{id:"oauth2-oidc",type:"oauth2",providerId:"oidc"})}return methods}async hasAuthenticationForAgent(agentId){if(this.authManager){const token=await this.authManager.getTokenForAgent(agentId);if(token){return true}}const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}hasCredentialsForAgent(agentId){const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}createAuthRequiredError(id,agentId,requiredMethod){const remediation={type:"login_required",commands:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:requiredMethod??"api-key",supportedMethods:this.getSupportedAuthMethods().map(m=>m.id),remediation})}async injectAuthentication(agentId,message){if(this.authManager){return this.authManager.injectAuth(agentId,message)}return message}injectMcpServers(message,agentId){const agent=this.registry.lookup(agentId);if(!agent?.mcpServers||agent.mcpServers.length===0){return message}const msg=message;const params=msg.params||{};const existingServers=Array.isArray(params.mcpServers)?params.mcpServers:[];const registryServers=agent.mcpServers.map(server=>({name:server.name,command:server.command,args:server.args,env:server.env?Object.entries(server.env).map(([name,value])=>({name,value})):void 0}));const existingNames=new Set(existingServers.filter(s=>s!==null&&typeof s==="object").map(s=>s.name).filter(n=>typeof n==="string"));const mergedServers=[...registryServers.filter(s=>!existingNames.has(s.name)),...existingServers];logInfo3(`Injecting ${registryServers.length} MCP servers from registry for agent ${agentId}`);return{...msg,params:{...params,mcpServers:mergedServers}}}async route(message){const id=extractId(message);const agentId=extractAgentId(message);if(agentId===void 0){logError3("Missing agentId in request");return createErrorResponse(id,RoutingErrorCodes.MISSING_AGENT_ID,"Missing agentId")}const currentAuthState=this.getAuthState(agentId);if(currentAuthState==="pending"){logInfo3(`OAuth flow pending for agent ${agentId}, queueing request (id=${id})`);return this.queueRequest(agentId,message)}if(currentAuthState==="failed"){logError3(`Authentication failed for agent ${agentId}, returning AUTH_REQUIRED`);const requiredProviderId2=this.agentOAuthRequirements.get(agentId);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId2)}const requiredProviderId=this.agentOAuthRequirements.get(agentId);if(requiredProviderId&&currentAuthState!=="authenticated"){const hasCredentials=await this.hasOAuthCredentialsForAgent(agentId,requiredProviderId);if(!hasCredentials){logError3(`Agent ${agentId} requires OAuth (provider: ${requiredProviderId}) but credentials not available`);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId)}}return this.routeInternal(message,agentId,id)}async hasOAuthCredentialsForAgent(agentId,providerId){if(!this.authManager){return false}const token=await this.authManager.getTokenForAgent(agentId,providerId);return token!==null&&token!==void 0}createAuthRequiredErrorWithProvider(id,agentId,providerId){const supportedMethods=this.getSupportedAuthMethods();const remediation={type:"login_required",provider:providerId||"unknown",commands:providerId?[`npx @stdiobus/workers-registry acp-registry --login ${providerId}`,`stdiobus acp-registry --login ${providerId}`]:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:providerId?`Run: npx @stdiobus/workers-registry acp-registry --login ${providerId}`:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:providerId?`oauth2-${providerId}`:"oauth2",supportedMethods:supportedMethods.map(m=>m.id),providerId,remediation})}async routeInternal(message,agentId,id){let spawnCommand;try{spawnCommand=this.registry.resolve(agentId)}catch(error){if(error instanceof AgentNotFoundError){logError3(`Agent not found: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.AGENT_NOT_FOUND,"Agent not found",{agentId})}if(error instanceof PlatformNotSupportedError){logError3(`Platform not supported for agent: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,"Platform not supported",{agentId,platform:error.platform})}throw error}const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}};logInfo3(`Injected ${Object.keys(agentEnv).length} env vars from api-keys.json for agent ${agentId}`)}let runtime;try{runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to spawn agent ${agentId}: ${error.message}`);return createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Agent spawn failed",{agentId,error:error.message})}if(id!==null){const msg2=message;const clientSessionId=typeof msg2.sessionId==="string"?msg2.sessionId:void 0;const method=typeof msg2.method==="string"?msg2.method:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),method,clientSessionId})}let transformedMessage=transformMessage(message);const msg=message;if(msg.method==="session/new"){transformedMessage=this.injectMcpServers(transformedMessage,agentId)}transformedMessage=await this.injectAuthentication(agentId,transformedMessage);const success=runtime.write(transformedMessage);if(!success){logError3(`Failed to write to agent ${agentId}`);if(id!==null){this.pendingRequests.delete(id)}}else{logInfo3(`Routed message to agent ${agentId}`)}return void 0}getAuthState(agentId){return this.authState.get(agentId)??"none"}setAuthState(agentId,newState){const oldState=this.getAuthState(agentId);if(oldState===newState){return}logInfo3(`Auth state transition for ${agentId}: ${oldState} \u2192 ${newState}`);this.authState.set(agentId,newState);if(newState==="authenticated"&&oldState==="pending"){void this.processQueuedRequests(agentId)}else if(newState==="failed"&&oldState==="pending"){void this.rejectQueuedRequests(agentId)}}queueRequest(agentId,message){return new Promise(resolve2=>{const queuedRequest={message,queuedAt:Date.now(),resolve:resolve2};let queue=this.requestQueue.get(agentId);if(!queue){queue=[];this.requestQueue.set(agentId,queue)}queue.push(queuedRequest);logInfo3(`Queued request for agent ${agentId}, queue size: ${queue.length}`);setTimeout(()=>{this.handleQueuedRequestTimeout(agentId,queuedRequest)},QUEUED_REQUEST_TIMEOUT_MS)})}handleQueuedRequestTimeout(agentId,queuedRequest){const queue=this.requestQueue.get(agentId);if(!queue){return}const index=queue.indexOf(queuedRequest);if(index===-1){return}queue.splice(index,1);logError3(`Queued request timed out for agent ${agentId}`);const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication timeout",{agentId,reason:"OAuth flow timed out while request was queued"}))}async processQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Processing ${queue.length} queued requests for agent ${agentId}`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){try{const id=extractId(queuedRequest.message);const result=await this.routeInternal(queuedRequest.message,agentId,id);queuedRequest.resolve(result)}catch(error){const id=extractId(queuedRequest.message);logError3(`Error processing queued request for ${agentId}: ${error.message}`);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Failed to process queued request",{agentId,error:error.message}))}}logInfo3(`Completed processing queued requests for agent ${agentId}`)}async rejectQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Rejecting ${queue.length} queued requests for agent ${agentId} due to auth failure`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(this.createAuthRequiredError(id,agentId,"oauth2"))}}getQueuedRequestCount(agentId){const queue=this.requestQueue.get(agentId);return queue?.length??0}getTotalQueuedRequestCount(){let total=0;for(const queue of this.requestQueue.values()){total+=queue.length}return total}handleAgentResponse(agentId,response){const id=extractId(response);let msg=response;const method=typeof msg.method==="string"?msg.method:void 0;if(id!==null&&typeof id==="string"){const pendingAuth=this.pendingAuthenticateRequests.get(id);if(pendingAuth&&pendingAuth.agentId===agentId){this.handleAuthenticateResponse(pendingAuth,msg);return}}if(id!==null&&method){this.handleAgentRequest(agentId,id,method,msg);return}if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;const isInitializeResponse=pending.method==="initialize"&&result!==void 0;if(isInitializeResponse){const ourAuthMethods=this.getSupportedAuthMethods();const existingAuthMethods=Array.isArray(result.authMethods)?result.authMethods:[];const mergedAuthMethods=[...ourAuthMethods,...existingAuthMethods.filter(m=>!ourAuthMethods.some(our=>our.id===m.id))];msg={...msg,result:{...result,authMethods:mergedAuthMethods}};logInfo3(`Injected ${ourAuthMethods.length} auth methods into initialize response for ${agentId}`)}if(isInitializeResponse&&result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){const parsedMethods=parseAuthMethods2(result.authMethods);if(parsedMethods.length>0){const oauthMethods=getOAuthMethods(parsedMethods);const apiKeyMethods=getApiKeyMethods(parsedMethods);const hasApiKeyCredentials=this.hasCredentialsForAgent(agentId);if(oauthMethods.length>0&&!(apiKeyMethods.length>0&&hasApiKeyCredentials)){const requiredProviderId=oauthMethods[0].providerId;this.agentOAuthRequirements.set(agentId,requiredProviderId);logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${requiredProviderId}`)}else if(apiKeyMethods.length>0&&hasApiKeyCredentials){logInfo3(`Agent ${agentId} supports OAuth but api-key credentials available, using api-key`)}if(this.autoOAuth){logInfo3(`Agent ${agentId} requires authentication, attempting auto-auth with ${parsedMethods.length} valid methods`);this.setAuthState(agentId,"pending");void this.attemptAuthentication(agentId,parsedMethods)}else{logInfo3(`Agent ${agentId} requires authentication but AUTH_AUTO_OAUTH is disabled. Use --login to authenticate.`)}}else{logError3(`Agent ${agentId} has authMethods but none are valid after parsing`);this.setAuthState(agentId,"none")}}if(result&&typeof result.sessionId==="string"){const agentSessionId=result.sessionId;const clientSessionId=pending.clientSessionId;if(clientSessionId){this.sessionIdMap.set(agentSessionId,clientSessionId);logInfo3(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`)}}this.pendingRequests.delete(id)}}if(id===null&&method){logInfo3(`Received notification: ${method}`);const params=msg.params;if(params&&typeof params.sessionId==="string"){const agentSessionId=params.sessionId;const clientSessionId=this.sessionIdMap.get(agentSessionId);if(clientSessionId){const enriched={...msg,sessionId:clientSessionId,params:{...params,sessionId:agentSessionId}};logInfo3(`Forwarding notification with mapped sessionId: ${clientSessionId}`);this.writeCallback(enriched);return}else{logError3(`Notification with unmapped agentSessionId: ${agentSessionId}, using default sessionId`);const enriched={...msg,sessionId:"global-notifications",params:{...params,sessionId:agentSessionId}};this.writeCallback(enriched);return}}else{const topLevelSessionId=msg.sessionId;if(topLevelSessionId){this.writeCallback(response);return}else{logError3(`Notification without sessionId: ${method}, adding default sessionId for routing`);const enriched={...msg,sessionId:"global-notifications"};this.writeCallback(enriched);return}}}this.writeCallback(msg)}handleAgentRequest(agentId,id,method,msg){logInfo3(`Agent ${agentId} sent request: ${method} (id=${id}), auto-responding`);let result;if(method==="session/request_permission"){result=this.buildPermissionResponse(msg)}else{logInfo3(`Unknown agent request method: ${method}, sending generic success`);result={}}const response={jsonrpc:"2.0",id,result};this.sendToAgent(agentId,response)}buildPermissionResponse(msg){const params=msg.params;const options=params?.options;if(!options||options.length===0){return{optionId:"approved"}}const allowAlways=options.find(o=>o.kind==="allow_always");if(allowAlways&&typeof allowAlways.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowAlways.optionId} (allow_always)`);return{optionId:allowAlways.optionId}}const allowOnce=options.find(o=>o.kind==="allow_once");if(allowOnce&&typeof allowOnce.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowOnce.optionId} (allow_once)`);return{optionId:allowOnce.optionId}}const firstOption=options[0];const optionId=typeof firstOption.optionId==="string"?firstOption.optionId:"approved";logInfo3(`Auto-approving permission with fallback option: ${optionId}`);return{optionId}}handleAuthenticateResponse(pendingAuth,response){const{agentId,authMethodId,requestId}=pendingAuth;if(response.error){const error=response.error;const errorCode=error.code??"UNKNOWN";const errorMessage=typeof error.message==="string"?error.message:"Unknown error";logError3(`Agent Auth failed for ${agentId}: [${errorCode}] ${errorMessage}`);pendingAuth.resolve(false,errorMessage);return}if(response.result!==void 0){logInfo3(`Agent Auth succeeded for ${agentId} (method: ${authMethodId}, request: ${requestId})`);pendingAuth.resolve(true);return}logError3(`Unexpected authenticate response format for ${agentId}: ${JSON.stringify(response)}`);pendingAuth.resolve(false,"Unexpected response format")}sendToAgent(agentId,message){let runtime;try{runtime=this.runtimeManager.get(agentId)}catch{logError3(`Failed to get runtime for agent ${agentId} to send response`);return}if(!runtime){logError3(`No runtime found for agent ${agentId}, cannot send response`);return}const success=runtime.write(message);if(!success){logError3(`Failed to write response to agent ${agentId}`)}else{logInfo3(`Sent auto-response to agent ${agentId}`)}}async attemptAuthentication(agentId,authMethods){const agentAuthMethods=getAgentAuthMethods(authMethods);if(agentAuthMethods.length>0){await this.attemptAgentAuthentication(agentId,agentAuthMethods);return}const terminalAuthMethods=getTerminalAuthMethods(authMethods);if(terminalAuthMethods.length>0){await this.attemptTerminalAuthentication(agentId,terminalAuthMethods);return}const oauthMethods=getOAuthMethods(authMethods);if(oauthMethods.length>0){await this.attemptOAuthAuthentication(agentId,oauthMethods);return}await this.attemptApiKeyAuthentication(agentId,authMethods)}async attemptAgentAuthentication(agentId,agentAuthMethods){const selectedMethod=agentAuthMethods[0];logInfo3(`Agent ${agentId} requires Agent Auth with method: ${selectedMethod.id}`);logInfo3(`Calling authenticate method on agent - agent will handle OAuth flow internally`);this.setAuthState(agentId,"pending");try{let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for Agent Auth: ${error.message}`);this.setAuthState(agentId,"failed");return}const success=await this.callAgentAuthenticate(agentId,selectedMethod.id,runtime);if(success){logInfo3(`Agent Auth successful for agent ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Agent Auth failed for agent ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Agent Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}callAgentAuthenticate(agentId,authMethodId,runtime){return new Promise(resolve2=>{const requestId=`agent-auth-${agentId}-${Date.now()}`;const authenticateRequest={jsonrpc:"2.0",id:requestId,method:"authenticate",params:{id:authMethodId}};const pendingRequest={requestId,agentId,authMethodId,sentAt:Date.now(),resolve:(success2,error)=>{this.pendingAuthenticateRequests.delete(requestId);if(error){logError3(`Agent Auth response error: ${error}`)}resolve2(success2)}};this.pendingAuthenticateRequests.set(requestId,pendingRequest);setTimeout(()=>{const pending=this.pendingAuthenticateRequests.get(requestId);if(pending){logError3(`Agent Auth timeout for agent ${agentId} (method: ${authMethodId})`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}},AGENT_AUTH_TIMEOUT_MS);const success=runtime.write(authenticateRequest);if(!success){logError3(`Failed to send authenticate request to agent ${agentId}`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}else{logInfo3(`Sent authenticate request to agent ${agentId} (id: ${requestId}, method: ${authMethodId})`)}})}async attemptTerminalAuthentication(agentId,terminalAuthMethods){const selectedMethod=terminalAuthMethods[0];logInfo3(`Agent ${agentId} requires Terminal Auth with method: ${selectedMethod.id}`);if(!this.isStdinTTY()||!this.isStdoutTTY()){logError3(`Terminal Auth requires interactive terminal (TTY). Run in a terminal with stdin/stdout connected.`);this.setAuthState(agentId,"failed");return}this.setAuthState(agentId,"pending");try{const existingRuntime=this.runtimeManager.get(agentId);if(existingRuntime){logInfo3(`Stopping existing runtime for agent ${agentId} before Terminal Auth`);await this.runtimeManager.terminate(agentId)}const baseSpawnCommand=this.registry.resolve(agentId);const terminalArgs=selectedMethod.args??[];const terminalEnv={...process.env,...selectedMethod.env??{}};logInfo3(`Launching Terminal Auth for ${agentId}: ${baseSpawnCommand.command} ${terminalArgs.join(" ")}`);const exitCode=await this.runTerminalAuthProcess(baseSpawnCommand.command,terminalArgs,terminalEnv);if(exitCode===0){logInfo3(`Terminal Auth process exited successfully for ${agentId}`);const authVerified=await this.verifyTerminalAuthSuccess(agentId);if(authVerified){logInfo3(`Terminal Auth verified for ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Terminal Auth completed but verification failed for ${agentId}`);this.setAuthState(agentId,"failed")}}else{logError3(`Terminal Auth process exited with code ${exitCode} for ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Terminal Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}runTerminalAuthProcess(command,args,env){return new Promise((resolve2,reject)=>{logInfo3(`Spawning Terminal Auth process: ${command} ${args.join(" ")}`);const child=this.spawnFn(command,args,{env,stdio:"inherit",shell:false});const timeoutId=setTimeout(()=>{logError3(`Terminal Auth process timed out after ${TERMINAL_AUTH_TIMEOUT_MS}ms`);child.kill("SIGTERM");setTimeout(()=>{if(!child.killed){child.kill("SIGKILL")}},5e3)},TERMINAL_AUTH_TIMEOUT_MS);child.on("error",error=>{clearTimeout(timeoutId);reject(error)});child.on("exit",(code,signal)=>{clearTimeout(timeoutId);if(signal){logError3(`Terminal Auth process killed by signal: ${signal}`);resolve2(1)}else{resolve2(code??1)}})})}async verifyTerminalAuthSuccess(agentId){try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}const runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand);return runtime.state==="running"}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Failed to verify Terminal Auth for ${agentId}: ${errorMessage}`);return false}}async attemptOAuthAuthentication(agentId,oauthMethods){if(!this.authManager){logError3(`OAuth authentication required for agent ${agentId}, but AuthManager not available`);this.setAuthState(agentId,"failed");return}const selectedMethod=oauthMethods[0];const providerId=selectedMethod.providerId;logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${providerId}`);logInfo3(`Initiating OAuth 2.1 Authorization Code flow with PKCE for ${providerId}`);this.setAuthState(agentId,"pending");try{const result=await this.authManager.authenticateAgent(providerId);if(result.success){logInfo3(`OAuth authentication successful for agent ${agentId} with provider ${providerId}`);this.setAuthState(agentId,"authenticated");await this.sendOAuthCredentialsToAgent(agentId,selectedMethod)}else{const errorMsg=result.error?.message??"Unknown error";const errorCode=result.error?.code??"UNKNOWN";logError3(`OAuth authentication failed for agent ${agentId}: [${errorCode}] ${errorMsg}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`OAuth authentication error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}async sendOAuthCredentialsToAgent(agentId,method){if(!this.authManager){logError3(`Cannot send OAuth credentials: AuthManager not available`);return}const token=await this.authManager.getTokenForAgent(agentId,method.providerId);if(!token){logError3(`No OAuth token available for agent ${agentId} after successful auth`);return}let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for OAuth credential injection: ${error.message}`);return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:method.id,credentials:{accessToken:token}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send OAuth authenticate request to ${agentId}: ${error.message}`)}else{logInfo3(`Sent OAuth authenticate request to agent ${agentId}`)}})}}async attemptApiKeyAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError3(`No API key found for agent ${agentId}, authentication will fail`);this.setAuthState(agentId,"failed");return}const apiKeyMethods=getApiKeyMethods(authMethods);const SAFE_API_KEY_METHODS=["api-key","openai-api-key","github-api-key","google-api-key","azure-api-key","cognito-api-key"];const selectedMethod=apiKeyMethods.find(m=>SAFE_API_KEY_METHODS.includes(m.id));if(!selectedMethod){logError3(`No safe API key method available for agent ${agentId}, skipping auto-auth`);this.setAuthState(agentId,"failed");return}logInfo3(`Authenticating agent ${agentId} with API key method: ${selectedMethod.id} (providerId: ${selectedMethod.providerId??"none"})`);let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for authentication: ${error.message}`);this.setAuthState(agentId,"failed");return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:selectedMethod.id,credentials:{apiKey}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send authenticate request to ${agentId}: ${error.message}`);this.setAuthState(agentId,"failed")}else{logInfo3(`Sent authenticate request to agent ${agentId}`);this.setAuthState(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}clearQueues(){for(const[agentId,queue]of this.requestQueue.entries()){for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Router shutdown",{agentId,reason:"Router is shutting down"}))}}this.requestQueue.clear();this.authState.clear();this.agentOAuthRequirements.clear();logInfo3("Cleared all request queues, auth state, and OAuth requirements")}resetAuthState(agentId){this.setAuthState(agentId,"none")}getAgentOAuthRequirement(agentId){return this.agentOAuthRequirements.get(agentId)}setAgentOAuthRequirement(agentId,providerId){this.agentOAuthRequirements.set(agentId,providerId);logInfo3(`Set OAuth requirement for agent ${agentId}: provider ${providerId}`)}clearAgentOAuthRequirement(agentId){this.agentOAuthRequirements.delete(agentId);logInfo3(`Cleared OAuth requirement for agent ${agentId}`)}};var LOG_CONTEXT="registry-launcher";function formatLogMessage(level,message,context=LOG_CONTEXT){const timestamp=new Date().toISOString();return`[${timestamp}] [${level}] [${context}] ${message}`}function log(level,message,context){const formatted=formatLogMessage(level,message,context);console.error(formatted)}function logExit(agentId,exitCode,signal){if(signal){log("INFO",`Agent "${agentId}" exited with signal ${signal}`)}else if(exitCode!==null){log("INFO",`Agent "${agentId}" exited with code ${exitCode}`)}else{log("INFO",`Agent "${agentId}" exited`)}}function logInfo4(message,context){log("INFO",message,context)}function logWarn(message,context){log("WARN",message,context)}function logError4(message,context){log("ERROR",message,context)}import*as readline from"readline";var PROVIDER_INFO=[{id:"github",name:"GitHub",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:true,supportsOAuth:true,apiKeyLabel:"Personal Access Token",apiKeyEnvVar:"GITHUB_TOKEN"},{id:"google",name:"Google",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:false,supportsOAuth:true},{id:"cognito",name:"AWS Cognito",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"azure",name:"Microsoft Entra ID",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"oidc",name:"Generic OIDC",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true}];var TerminalAuthFlow=class{credentialStore;validateCredentials;input;output;rl=null;constructor(dependencies){this.credentialStore=dependencies.credentialStore;this.validateCredentials=dependencies.validateCredentials;this.input=dependencies.input??process.stdin;this.output=dependencies.output??process.stderr}async execute(providerId){this.rl=readline.createInterface({input:this.input,output:this.output});try{this.writeLine("\n=== OAuth Authentication Setup ===\n");const selectedProvider=providerId??await this.selectProvider();const providerInfo=PROVIDER_INFO.find(p=>p.id===selectedProvider);if(!providerInfo){return{useBrowserOAuth:false,authResult:{success:false,providerId:selectedProvider,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${selectedProvider}' is not supported.`,details:{supportedProviders:VALID_PROVIDER_IDS}}}}}this.writeLine(`
5
+ Configuring ${providerInfo.name}...
6
+ `);if(providerInfo.supportsOAuth){const authMode=await this.selectAuthenticationMode(providerInfo);if(authMode==="browser-oauth"){this.writeLine("\nBrowser OAuth selected. Launching browser authentication flow...\n");return{useBrowserOAuth:true,providerId:selectedProvider}}}const result=await this.collectAndValidateWithRetry(selectedProvider,providerInfo);return{useBrowserOAuth:false,authResult:result}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[TerminalAuthFlow] Error: ${errorMessage}`);const errorProviderId=providerId||"github";return{useBrowserOAuth:false,authResult:{success:false,providerId:errorProviderId,error:{code:"PROVIDER_ERROR",message:`Terminal auth flow failed: ${errorMessage}`}}}}finally{this.cleanup()}}async selectAuthenticationMode(providerInfo){this.writeLine(`${providerInfo.name} supports multiple authentication methods:
7
+ `);this.writeLine(" 1. Browser OAuth (recommended) - Opens browser for secure authentication");this.writeLine(" 2. Manual API Key - Enter credentials directly in terminal\n");const selection=await this.promptSelection("Select authentication method (1-2) [default: 1]: ",1,2,1);return selection===1?"browser-oauth":"manual-api-key"}async collectAndValidateWithRetry(selectedProvider,providerInfo){let credentials=null;let validationResult=null;let attempts=0;const maxAttempts=3;const useApiKey=providerInfo.supportsApiKey;while(attempts<maxAttempts){attempts++;if(useApiKey){credentials=await this.collectApiKeyCredentials(providerInfo)}else{credentials=await this.collectCredentials(providerInfo)}this.writeLine("\nValidating credentials...");validationResult=await this.validateCredentials(selectedProvider,credentials);if(validationResult.valid){break}this.writeLine(`
8
+ Validation failed: ${validationResult.error||"Unknown error"}`);if(attempts<maxAttempts){const retry=await this.promptYesNo("Would you like to try again?");if(!retry){return{success:false,providerId:selectedProvider,error:{code:"INVALID_CREDENTIALS",message:"Credential validation failed and user cancelled retry.",details:{validationError:validationResult.error}}}}this.writeLine("")}}if(!validationResult?.valid||!credentials){return{success:false,providerId:selectedProvider,error:{code:"INVALID_CREDENTIALS",message:`Credential validation failed after ${maxAttempts} attempts.`,details:{validationError:validationResult?.error}}}}if(!validationResult.accessToken||validationResult.accessToken.trim()===""){return{success:false,providerId:selectedProvider,error:{code:"INVALID_CREDENTIALS",message:"Credential validation succeeded but no access token was returned."}}}const storedCredentials={providerId:selectedProvider,accessToken:validationResult.accessToken,clientId:credentials.clientId,clientSecret:credentials.clientSecret,customEndpoints:credentials.customEndpoints,storedAt:Date.now()};await this.credentialStore.store(selectedProvider,storedCredentials);this.writeLine(`
9
+ ${providerInfo.name} credentials configured successfully!
10
+ `);return{success:true,providerId:selectedProvider}}async promptSelection(message,min,max,defaultValue){while(true){const input=await this.prompt(message);const trimmed=input.trim();if(trimmed===""&&defaultValue!==void 0){return defaultValue}const selection=parseInt(trimmed,10);if(selection>=min&&selection<=max){return selection}this.writeLine(`Invalid selection. Please enter a number between ${min} and ${max}.`)}}async collectApiKeyCredentials(providerInfo){const label=providerInfo.apiKeyLabel||"API Key";const envVar=providerInfo.apiKeyEnvVar;if(envVar){this.writeLine(`(You can also set this via ${envVar} environment variable)
11
+ `)}const apiKey=await this.promptSecret(`${label}: `);return{clientId:apiKey}}async selectProvider(){this.writeLine("Select an OAuth provider:\n");PROVIDER_INFO.forEach((provider,index)=>{this.writeLine(` ${index+1}. ${provider.name}`)});this.writeLine("");while(true){const input=await this.prompt(`Enter selection (1-${PROVIDER_INFO.length}): `);const selection=parseInt(input.trim(),10);if(selection>=1&&selection<=PROVIDER_INFO.length){return PROVIDER_INFO[selection-1].id}this.writeLine(`Invalid selection. Please enter a number between 1 and ${PROVIDER_INFO.length}.`)}}async collectCredentials(providerInfo){const credentials={clientId:""};credentials.clientId=await this.promptRequired("Client ID: ");if(providerInfo.requiresClientSecret){credentials.clientSecret=await this.promptSecret("Client Secret: ")}if(providerInfo.requiresCustomEndpoints){credentials.customEndpoints=await this.collectCustomEndpoints(providerInfo)}return credentials}async collectCustomEndpoints(providerInfo){this.writeLine(`
12
+ ${providerInfo.name} requires custom endpoint configuration:
13
+ `);if(providerInfo.id==="cognito"){return this.collectCognitoEndpoints()}else if(providerInfo.id==="azure"){return this.collectAzureEndpoints()}else if(providerInfo.id==="oidc"){return this.collectOidcEndpoints()}const authEndpoint=await this.promptValidatedUrl("Authorization Endpoint URL: ");const tokenEndpoint=await this.promptValidatedUrl("Token Endpoint URL: ");return{authorizationEndpoint:authEndpoint,tokenEndpoint}}async promptValidatedUrl(message){while(true){const input=await this.promptRequired(message);const error=this.validateHttpsUrl(input);if(error===null){return input}this.writeLine(`Error: ${error}`)}}validateHttpsUrl(value){let url;try{url=new URL(value)}catch{return"Invalid URL format."}if(url.protocol!=="https:"){return"URL must use HTTPS protocol for security."}if(url.username||url.password){return"URL must not contain embedded credentials."}return null}async collectCognitoEndpoints(){this.writeLine("Enter your Cognito User Pool details:\n");const userPoolDomain=await this.promptValidated("User Pool Domain (e.g., my-app): ",this.validateCognitoDomain.bind(this));const region=await this.promptValidated("AWS Region (e.g., us-east-1): ",this.validateAwsRegion.bind(this));const baseUrl=`https://${userPoolDomain}.auth.${region}.amazoncognito.com`;return{authorizationEndpoint:`${baseUrl}/oauth2/authorize`,tokenEndpoint:`${baseUrl}/oauth2/token`}}async collectAzureEndpoints(){this.writeLine("Enter your Microsoft Entra ID details:\n");const tenantId=await this.promptValidated('Tenant ID (or "common" for multi-tenant): ',this.validateAzureTenantId.bind(this));const baseUrl=`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0`;return{authorizationEndpoint:`${baseUrl}/authorize`,tokenEndpoint:`${baseUrl}/token`}}async collectOidcEndpoints(){this.writeLine("Enter your OIDC provider details:\n");this.writeLine("You can provide an issuer URL for automatic discovery,");this.writeLine("or manually enter the authorization and token endpoints.\n");const useDiscovery=await this.promptYesNo("Use OIDC Discovery (recommended)?");if(useDiscovery){const issuerUrl=await this.promptValidatedUrl("Issuer URL (e.g., https://auth.example.com): ");const discoveryUrl=issuerUrl.endsWith("/")?`${issuerUrl}.well-known/openid-configuration`:`${issuerUrl}/.well-known/openid-configuration`;this.writeLine(`
14
+ OIDC Discovery URL: ${discoveryUrl}`);this.writeLine("The authorization and token endpoints will be fetched automatically.\n");return{authorizationEndpoint:issuerUrl,tokenEndpoint:discoveryUrl}}this.writeLine("\nEnter the endpoints manually:\n");const authEndpoint=await this.promptValidatedUrl("Authorization Endpoint URL: ");const tokenEndpoint=await this.promptValidatedUrl("Token Endpoint URL: ");return{authorizationEndpoint:authEndpoint,tokenEndpoint}}validateCognitoDomain(value){if(!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(value)){return"Invalid domain format. Must be alphanumeric with hyphens, no leading/trailing hyphens."}if(value.length>63){return"Domain must be 63 characters or less."}if(/[/:?#@\s]/.test(value)){return"Domain contains invalid characters (/, :, ?, #, @, or whitespace)."}return null}validateAwsRegion(value){if(!/^[a-z]{2}-[a-z]+-\d+$/.test(value)){return"Invalid AWS region format. Expected format: us-east-1, eu-west-2, etc."}return null}validateAzureTenantId(value){const wellKnown=["common","organizations","consumers"];if(wellKnown.includes(value.toLowerCase())){return null}if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)){return null}if(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(value)){return null}if(/[/:?#@\s]/.test(value)){return"Tenant ID contains invalid characters (/, :, ?, #, @, or whitespace)."}return"Invalid tenant ID. Must be 'common', 'organizations', 'consumers', a valid GUID, or a domain name."}async promptValidated(message,validator){while(true){const input=await this.promptRequired(message);const error=validator(input);if(error===null){return input}this.writeLine(`Error: ${error}`)}}async promptRequired(message){while(true){const input=await this.prompt(message);const trimmed=input.trim();if(trimmed.length>0){return trimmed}this.writeLine("This field is required. Please enter a value.")}}async promptSecret(message){this.writeLine("(Note: Input will be visible in terminal)");return this.promptRequired(message)}async promptYesNo(message){while(true){const input=await this.prompt(`${message} (y/n): `);const normalized=input.trim().toLowerCase();if(normalized==="y"||normalized==="yes"){return true}if(normalized==="n"||normalized==="no"){return false}this.writeLine('Please enter "y" or "n".')}}prompt(message){return new Promise(resolve2=>{if(!this.rl){resolve2("");return}this.rl.question(message,answer=>{resolve2(answer)})})}writeLine(message){this.output.write(message+"\n")}cleanup(){if(this.rl){this.rl.close();this.rl=null}}};import{randomUUID}from"crypto";import{randomBytes,timingSafeEqual}from"crypto";var STATE_MIN_BYTES=32;function generateState(){const randomBuffer=randomBytes(STATE_MIN_BYTES);return randomBuffer.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function validateState(expected,received){if(!expected||!received){return false}const expectedBuffer=Buffer.from(expected,"utf8");const receivedBuffer=Buffer.from(received,"utf8");if(expectedBuffer.length!==receivedBuffer.length){return false}try{return timingSafeEqual(expectedBuffer,receivedBuffer)}catch{return false}}import{randomBytes as randomBytes2,createHash}from"crypto";var PKCE_VERIFIER_MIN_LENGTH=43;var PKCE_VERIFIER_MAX_LENGTH=128;var DEFAULT_VERIFIER_LENGTH=64;var UNRESERVED_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";var UNRESERVED_CHARS_REGEX=/^[A-Za-z0-9\-._~]+$/;function generateCodeVerifier(length=DEFAULT_VERIFIER_LENGTH){if(!Number.isInteger(length)||!Number.isFinite(length)){throw new Error(`PKCE code verifier length must be a valid integer, got ${length}`)}if(length<PKCE_VERIFIER_MIN_LENGTH||length>PKCE_VERIFIER_MAX_LENGTH){throw new Error(`PKCE code verifier length must be between ${PKCE_VERIFIER_MIN_LENGTH} and ${PKCE_VERIFIER_MAX_LENGTH}, got ${length}`)}const charsetLength=UNRESERVED_CHARS.length;const maxValidByte=256-256%charsetLength;let verifier="";let bytesNeeded=length;while(verifier.length<length){const randomBuffer=randomBytes2(Math.ceil(bytesNeeded*1.4));for(let i=0;i<randomBuffer.length&&verifier.length<length;i++){const byte=randomBuffer[i];if(byte<maxValidByte){verifier+=UNRESERVED_CHARS[byte%charsetLength]}}bytesNeeded=length-verifier.length}return verifier}function validateCodeVerifier(verifier){if(typeof verifier!=="string"){return false}if(verifier.length<PKCE_VERIFIER_MIN_LENGTH||verifier.length>PKCE_VERIFIER_MAX_LENGTH){return false}return UNRESERVED_CHARS_REGEX.test(verifier)}function generateCodeChallenge(verifier,strict=false){if(strict&&!validateCodeVerifier(verifier)){throw new Error(`Invalid PKCE code verifier format. Must be ${PKCE_VERIFIER_MIN_LENGTH}-${PKCE_VERIFIER_MAX_LENGTH} characters using only unreserved URI characters.`)}const hash=createHash("sha256").update(verifier,"ascii").digest();return hash.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function generatePKCEPair(length){const verifier=generateCodeVerifier(length);const challenge=generateCodeChallenge(verifier);return{verifier,challenge}}var DEFAULT_SESSION_TIMEOUT_MS=5*60*1e3;var MAX_SESSION_TIMEOUT_MS=60*60*1e3;function validateTimeout(timeoutMs){if(!Number.isFinite(timeoutMs)){return DEFAULT_SESSION_TIMEOUT_MS}if(timeoutMs<=0){return DEFAULT_SESSION_TIMEOUT_MS}if(timeoutMs>MAX_SESSION_TIMEOUT_MS){return MAX_SESSION_TIMEOUT_MS}return Math.floor(timeoutMs)}var AuthSession=class{sessionId;providerId;codeVerifier;codeChallenge;state;startedAt;timeoutMs;constructor(providerId,codeVerifier,codeChallenge,state,timeoutMs=DEFAULT_SESSION_TIMEOUT_MS){this.sessionId=randomUUID();this.providerId=providerId;this.codeVerifier=codeVerifier;this.codeChallenge=codeChallenge;this.state=state;this.startedAt=Date.now();this.timeoutMs=validateTimeout(timeoutMs)}isExpired(){return this.remainingTime()<=0}remainingTime(){const elapsed=Date.now()-this.startedAt;const remaining=this.timeoutMs-elapsed;return Math.max(0,remaining)}validateState(returnedState){return validateState(this.state,returnedState)}};function createSession(providerId,timeoutMs=DEFAULT_SESSION_TIMEOUT_MS){const{verifier,challenge}=generatePKCEPair();const state=generateState();return new AuthSession(providerId,verifier,challenge,state,validateTimeout(timeoutMs))}var SessionManager=class _SessionManager{constructor(cleanupIntervalMs=_SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,autoStartCleanup=true){this.cleanupIntervalMs=cleanupIntervalMs;if(!Number.isFinite(this.cleanupIntervalMs)||this.cleanupIntervalMs<=0){Object.defineProperty(this,"cleanupIntervalMs",{value:_SessionManager.DEFAULT_CLEANUP_INTERVAL_MS,writable:false})}if(autoStartCleanup){this.startCleanup()}}sessions=new Map;stateToSessionId=new Map;cleanupTimer=null;static DEFAULT_CLEANUP_INTERVAL_MS=60*1e3;create(providerId,timeoutMs=DEFAULT_SESSION_TIMEOUT_MS){const session=createSession(providerId,validateTimeout(timeoutMs));this.sessions.set(session.sessionId,session);this.stateToSessionId.set(session.state,session.sessionId);return session}get(sessionId){const session=this.sessions.get(sessionId);if(session&&session.isExpired()){this.remove(sessionId);return void 0}return session}getByState(state){const sessionId=this.stateToSessionId.get(state);if(!sessionId){return void 0}return this.get(sessionId)}remove(sessionId){const session=this.sessions.get(sessionId);if(!session){return false}this.stateToSessionId.delete(session.state);this.sessions.delete(sessionId);return true}removeByState(state){const sessionId=this.stateToSessionId.get(state);if(!sessionId){return false}return this.remove(sessionId)}list(){const activeSessions=[];const expiredSessionIds=[];for(const[sessionId,session]of this.sessions){if(session.isExpired()){expiredSessionIds.push(sessionId)}else{activeSessions.push(session)}}for(const sessionId of expiredSessionIds){this.remove(sessionId)}return activeSessions}size(){return this.sessions.size}has(sessionId){return this.get(sessionId)!==void 0}hasByState(state){return this.getByState(state)!==void 0}cleanup(){const expiredSessionIds=[];for(const[sessionId,session]of this.sessions){if(session.isExpired()){expiredSessionIds.push(sessionId)}}for(const sessionId of expiredSessionIds){this.remove(sessionId)}return expiredSessionIds.length}startCleanup(){if(this.cleanupTimer!==null){return}this.cleanupTimer=setInterval(()=>{this.cleanup()},this.cleanupIntervalMs);if(this.cleanupTimer.unref){this.cleanupTimer.unref()}}stopCleanup(){if(this.cleanupTimer!==null){clearInterval(this.cleanupTimer);this.cleanupTimer=null}}clear(){this.stopCleanup();this.sessions.clear();this.stateToSessionId.clear()}isCleanupRunning(){return this.cleanupTimer!==null}};import*as http from"node:http";import{URL as URL2}from"node:url";var DEFAULT_CALLBACK_PATH="/callback";var LOOPBACK_HOST_IPV4="127.0.0.1";var MAX_URL_LENGTH=8192;var MAX_HEADER_SIZE=8192;var SECURITY_HEADERS={"Cache-Control":"no-store","Pragma":"no-cache","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'"};function isLoopbackAddress(address){if(!address){return false}if(address.startsWith("127.")){return true}if(address==="::1"){return true}if(address.startsWith("::ffff:127.")){return true}return false}function isValidHostHeader(hostHeader,expectedPort){if(!hostHeader){return false}const allowedHosts=[`127.0.0.1:${expectedPort}`,`localhost:${expectedPort}`,`[::1]:${expectedPort}`];return allowedHosts.includes(hostHeader.toLowerCase())}function hasDuplicateParam(url,paramName){return url.searchParams.getAll(paramName).length>1}var CallbackServer=class _CallbackServer{server=null;port=0;running=false;callbackPath;callbackPromise=null;callbackResolve=null;callbackReject=null;timeoutId=null;callbackHandled=false;static MIN_TIMEOUT_MS=1e3;static MAX_TIMEOUT_MS=6e5;constructor(callbackPath=DEFAULT_CALLBACK_PATH){this.callbackPath=callbackPath}async start(){if(this.running){throw new Error("Callback server is already running")}return new Promise((resolve2,reject)=>{this.server=http.createServer((req,res)=>{this.handleRequest(req,res)});this.server.on("error",error=>{this.running=false;reject(new Error(`Failed to start callback server: ${error.message}`))});this.server.listen(0,LOOPBACK_HOST_IPV4,()=>{const address=this.server?.address();if(address&&typeof address==="object"){this.port=address.port;this.running=true;const redirectUri=`http://${LOOPBACK_HOST_IPV4}:${this.port}${this.callbackPath}`;resolve2(redirectUri)}else{reject(new Error("Failed to get server address"))}})})}async waitForCallback(timeoutMs){if(!this.running){throw new Error("Callback server is not running")}if(this.callbackPromise){throw new Error("Already waiting for callback")}if(typeof timeoutMs!=="number"||!Number.isFinite(timeoutMs)){throw new Error("Timeout must be a finite number")}if(timeoutMs<_CallbackServer.MIN_TIMEOUT_MS){throw new Error(`Timeout must be at least ${_CallbackServer.MIN_TIMEOUT_MS}ms`)}if(timeoutMs>_CallbackServer.MAX_TIMEOUT_MS){throw new Error(`Timeout must not exceed ${_CallbackServer.MAX_TIMEOUT_MS}ms`)}this.callbackHandled=false;this.callbackPromise=new Promise((resolve2,reject)=>{this.callbackResolve=resolve2;this.callbackReject=reject;this.timeoutId=setTimeout(()=>{this.callbackReject?.(new Error("Callback timeout exceeded"));this.cleanup()},timeoutMs)});try{return await this.callbackPromise}finally{this.callbackPromise=null}}async stop(){this.cleanup();if(this.server){return new Promise((resolve2,reject)=>{this.server?.close(error=>{if(error&&!error.message.includes("Server is not running")){reject(new Error(`Failed to stop callback server: ${error.message}`))}else{this.server=null;this.port=0;this.running=false;resolve2()}})})}}getPort(){return this.port}isRunning(){return this.running}handleRequest(req,res){if(req.method!=="GET"){this.sendErrorResponse(res,405,"Method Not Allowed","Only GET requests are accepted");return}const remoteAddress=req.socket.remoteAddress;if(!isLoopbackAddress(remoteAddress)){this.sendErrorResponse(res,403,"Forbidden","Only loopback connections are allowed");return}const hostHeader=req.headers.host;if(!isValidHostHeader(hostHeader,this.port)){this.sendErrorResponse(res,400,"Bad Request","Invalid Host header");return}const rawUrl=req.url||"/";if(rawUrl.length>MAX_URL_LENGTH){this.sendErrorResponse(res,414,"URI Too Long","Request URL exceeds maximum length");return}const headerSize=Object.entries(req.headers).reduce((sum,[key,value])=>sum+key.length+(Array.isArray(value)?value.join("").length:value?.length||0),0);if(headerSize>MAX_HEADER_SIZE){this.sendErrorResponse(res,431,"Request Header Fields Too Large","Headers exceed maximum size");return}let url;try{url=new URL2(rawUrl,`http://${LOOPBACK_HOST_IPV4}:${this.port}`)}catch{this.sendErrorResponse(res,400,"Bad Request","Invalid URL format");return}if(url.pathname!==this.callbackPath){this.sendErrorResponse(res,404,"Not Found","Invalid callback path");return}if(this.callbackHandled){this.sendErrorResponse(res,409,"Conflict","Callback already processed");return}const sensitiveParams=["code","state","error","error_description"];for(const param of sensitiveParams){if(hasDuplicateParam(url,param)){this.sendErrorResponse(res,400,"Bad Request",`Duplicate parameter: ${param}`);return}}this.callbackHandled=true;const code=url.searchParams.get("code");const state=url.searchParams.get("state");const error=url.searchParams.get("error");const errorDescription=url.searchParams.get("error_description");let result;try{if(error){result={success:false,error,errorDescription:errorDescription||void 0,state:state||void 0};this.sendHtmlResponse(res,400,this.buildErrorPage(error,errorDescription))}else if(code&&state){result={success:true,code,state};this.sendHtmlResponse(res,200,this.buildSuccessPage())}else{result={success:false,error:"missing_params",errorDescription:"Missing code or state parameter",state:state||void 0};this.sendHtmlResponse(res,400,this.buildErrorPage("missing_params","Missing code or state parameter"))}if(this.callbackResolve){this.callbackResolve(result)}}finally{this.cleanup();this.stopAcceptingConnections()}}sendErrorResponse(res,statusCode,_statusMessage,body){res.writeHead(statusCode,{"Content-Type":"text/plain",...SECURITY_HEADERS});res.end(body)}sendHtmlResponse(res,statusCode,html){res.writeHead(statusCode,{"Content-Type":"text/html; charset=utf-8",...SECURITY_HEADERS});res.end(html)}stopAcceptingConnections(){if(this.server){this.server.close();this.running=false}}cleanup(){if(this.timeoutId){clearTimeout(this.timeoutId);this.timeoutId=null}this.callbackResolve=null;this.callbackReject=null}buildSuccessPage(){return`<!DOCTYPE html>
15
+ <html>
16
+ <head>
17
+ <title>Authorization Successful</title>
18
+ <style>
19
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
20
+ display: flex; justify-content: center; align-items: center; height: 100vh;
21
+ margin: 0; background: #f5f5f5; }
22
+ .container { text-align: center; padding: 40px; background: white;
23
+ border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
24
+ h1 { color: #22c55e; margin-bottom: 16px; }
25
+ p { color: #666; }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <div class="container">
30
+ <h1>\u2713 Authorization Successful</h1>
31
+ <p>You can close this window and return to the application.</p>
32
+ </div>
33
+ </body>
34
+ </html>`}buildErrorPage(error,description){const safeError=this.escapeHtml(error);const safeDescription=description?this.escapeHtml(description):"An error occurred during authorization.";return`<!DOCTYPE html>
35
+ <html>
36
+ <head>
37
+ <title>Authorization Failed</title>
38
+ <style>
39
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
40
+ display: flex; justify-content: center; align-items: center; height: 100vh;
41
+ margin: 0; background: #f5f5f5; }
42
+ .container { text-align: center; padding: 40px; background: white;
43
+ border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
44
+ h1 { color: #ef4444; margin-bottom: 16px; }
45
+ p { color: #666; }
46
+ .error-code { font-family: monospace; background: #f5f5f5; padding: 4px 8px;
47
+ border-radius: 4px; color: #333; }
48
+ </style>
49
+ </head>
50
+ <body>
51
+ <div class="container">
52
+ <h1>\u2717 Authorization Failed</h1>
53
+ <p>${safeDescription}</p>
54
+ <p>Error code: <span class="error-code">${safeError}</span></p>
55
+ </div>
56
+ </body>
57
+ </html>`}escapeHtml(text){return text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}};var DEFAULT_AUTH_TIMEOUT_MS=DEFAULT_SESSION_TIMEOUT_MS;var CI_ENVIRONMENT_VARIABLES=["CI","CONTINUOUS_INTEGRATION","GITHUB_ACTIONS","GITLAB_CI","JENKINS","JENKINS_URL","TRAVIS","CIRCLECI","BUILDKITE","DRONE","TEAMCITY_VERSION","TF_BUILD","CODEBUILD_BUILD_ID","BITBUCKET_BUILD_NUMBER","HEROKU_TEST_RUN_ID","SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"];function isHeadlessEnvironment(){for(const envVar of CI_ENVIRONMENT_VARIABLES){const value=process.env[envVar];if(value!==void 0&&value!==""&&value!=="0"&&value.toLowerCase()!=="false"){return true}}const headlessEnv=process.env["HEADLESS"];if(headlessEnv!==void 0&&headlessEnv!==""&&headlessEnv!=="0"&&headlessEnv.toLowerCase()!=="false"){return true}if(process.env["SSH_TTY"]!==void 0&&process.env["SSH_TTY"]!==""){return true}if(!process.stdout.isTTY||!process.stderr.isTTY){return true}return false}var AgentAuthFlow=class{getProvider;storeTokens;launchBrowser;constructor(dependencies){this.getProvider=dependencies.getProvider;this.storeTokens=dependencies.storeTokens;this.launchBrowser=dependencies.launchBrowser??openSystemBrowser}async execute(providerId,options){if(isHeadlessEnvironment()){console.error(`[AgentAuthFlow] Headless environment detected, cannot launch browser for ${providerId}`);return{success:false,providerId,error:{code:"HEADLESS_ENVIRONMENT",message:"Browser OAuth not available in headless environment",details:{suggestion:"Use --setup for manual credential configuration"}}}}const timeoutMs=options?.timeoutMs??DEFAULT_AUTH_TIMEOUT_MS;let callbackServer=null;let session=null;try{const provider=this.getProvider(providerId);const clientId=options?.clientId??this.getDefaultClientId(providerId);const configValidation=this.validateProviderConfig(providerId,provider,clientId);if(!configValidation.valid){console.error(`[AgentAuthFlow] Provider configuration invalid: ${configValidation.error}`);return{success:false,providerId,error:{code:"PROVIDER_ERROR",message:configValidation.error,details:configValidation.details}}}session=createSession(providerId,timeoutMs);console.error(`[AgentAuthFlow] Created auth session ${session.sessionId} for ${providerId}`);callbackServer=new CallbackServer;const redirectUri=await callbackServer.start();console.error(`[AgentAuthFlow] Callback server started at ${redirectUri}`);const scopes=options?.scopes??[...provider.defaultScopes];const authParams={clientId,redirectUri,scope:scopes.join(" "),state:session.state,codeChallenge:session.codeChallenge,codeChallengeMethod:"S256",responseType:"code"};const authorizationUrl=provider.buildAuthorizationUrl(authParams);console.error(`[AgentAuthFlow] Authorization URL built for ${providerId}`);await this.launchBrowser(authorizationUrl);console.error(`[AgentAuthFlow] Browser launched for ${providerId} authentication`);const callbackResult=await callbackServer.waitForCallback(session.remainingTime());if(!callbackResult.success){console.error(`[AgentAuthFlow] OAuth error: ${callbackResult.error} - ${callbackResult.errorDescription}`);return{success:false,providerId,error:{code:"PROVIDER_ERROR",message:callbackResult.errorDescription||callbackResult.error,details:{oauthError:callbackResult.error,oauthErrorDescription:callbackResult.errorDescription}}}}if(!session.validateState(callbackResult.state)){console.error(`[AgentAuthFlow] State validation failed for ${providerId}`);return{success:false,providerId,error:{code:"INVALID_STATE",message:"State parameter validation failed. The authorization response may have been tampered with."}}}console.error(`[AgentAuthFlow] Exchanging authorization code for tokens`);const tokenResponse=await provider.exchangeCode(callbackResult.code,session.codeVerifier,redirectUri);await this.storeTokens(providerId,tokenResponse);console.error(`[AgentAuthFlow] Tokens stored successfully for ${providerId}`);return{success:true,providerId}}catch(error){console.error(`[AgentAuthFlow] Authentication failed for ${providerId}: ${error}`);const errorMessage=error instanceof Error?error.message:String(error);if(errorMessage.includes("timeout")||errorMessage.includes("Timeout")){return{success:false,providerId,error:{code:"TIMEOUT",message:"Authentication flow timed out. Please try again."}}}if(errorMessage.includes("ECONNREFUSED")||errorMessage.includes("ENOTFOUND")||errorMessage.includes("fetch")){return{success:false,providerId,error:{code:"NETWORK_ERROR",message:`Network error during authentication: ${errorMessage}`}}}return{success:false,providerId,error:{code:"PROVIDER_ERROR",message:errorMessage}}}finally{if(callbackServer){try{await callbackServer.stop();console.error(`[AgentAuthFlow] Callback server stopped`)}catch(stopError){console.error(`[AgentAuthFlow] Error stopping callback server: ${stopError}`)}}}}validateProviderConfig(providerId,provider,clientId){if(!clientId||typeof clientId!=="string"){return{valid:false,error:`Client ID is required for ${providerId}. Set OAUTH_${providerId.toUpperCase()}_CLIENT_ID environment variable.`,details:{providerId,suggestion:"Configure client_id via environment variable or --setup"}}}const trimmedClientId=clientId.trim();if(trimmedClientId.length===0){return{valid:false,error:`Client ID cannot be empty for ${providerId}.`,details:{providerId}}}if(containsControlCharacters(trimmedClientId)){return{valid:false,error:`Client ID contains invalid characters for ${providerId}.`,details:{providerId}}}try{provider.validateConfig()}catch(error){return{valid:false,error:`Provider configuration invalid for ${providerId}: ${error.message}`,details:{providerId}}}return{valid:true}}getDefaultClientId(providerId){const envKey=`OAUTH_${providerId.toUpperCase()}_CLIENT_ID`;const clientId=process.env[envKey];if(!clientId){throw new Error(`No client ID configured for ${providerId}. Set the ${envKey} environment variable or provide clientId in options.`)}return clientId}};var SENSITIVE_URL_PARAMS=["state","code_challenge","code","access_token","refresh_token","id_token","client_secret"];function redactUrlForLogging(url){try{const parsedUrl=new URL(url);for(const param of SENSITIVE_URL_PARAMS){if(parsedUrl.searchParams.has(param)){parsedUrl.searchParams.set(param,"[REDACTED]")}}return parsedUrl.toString()}catch{return"[INVALID URL - REDACTED]"}}function containsControlCharacters(str){const controlCharRegex=/[\x00-\x08\x0B\x0C\x0E-\x1F]/;return controlCharRegex.test(str)}async function openSystemBrowser(url){if(containsControlCharacters(url)){throw new Error("URL contains invalid control characters")}let parsedUrl;try{parsedUrl=new URL(url)}catch{throw new Error("Invalid URL format")}if(parsedUrl.protocol!=="https:"){throw new Error("OAuth authorization URL must use HTTPS")}if(parsedUrl.username!==""||parsedUrl.password!==""){throw new Error("URL must not contain credentials")}const finalUrl=parsedUrl.toString();const redactedUrl=redactUrlForLogging(finalUrl);console.error(`[openSystemBrowser] Opening browser: ${redactedUrl}`);const platform=process.platform;const{execFile}=await import("child_process");const{promisify}=await import("util");const execFileAsync=promisify(execFile);try{switch(platform){case"darwin":await execFileAsync("open",[finalUrl]);break;case"win32":await execFileAsync("cmd.exe",["/c","start","",finalUrl]);break;default:await execFileAsync("xdg-open",[finalUrl]);break}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);throw new Error(`Failed to open browser: ${errorMessage}`)}}var SERVICE_NAME="stdio-bus-registry-launcher";var ACCOUNT_PREFIX="oauth-";var PROVIDERS_LIST_ACCOUNT="__providers_list__";var KeychainBackend=class{type="keychain";keytar=null;keytarLoadAttempted=false;keytarLoadError=null;async loadKeytar(){if(this.keytarLoadAttempted){return this.keytar}this.keytarLoadAttempted=true;try{const moduleName="keytar";const keytarModule=await import(moduleName);this.keytar=keytarModule.default||keytarModule;return this.keytar}catch(error){this.keytarLoadError=error instanceof Error?error:new Error(String(error));console.error(`[KeychainBackend] Failed to load keytar: ${this.keytarLoadError.message}`);return null}}getAccountName(providerId){return`${ACCOUNT_PREFIX}${providerId}`}async isAvailable(){const keytar=await this.loadKeytar();if(!keytar){return false}try{await keytar.getPassword(SERVICE_NAME,"__availability_check__");return true}catch(error){console.error(`[KeychainBackend] Keychain access check failed: ${error}`);return false}}async store(providerId,credentials){const keytar=await this.loadKeytar();if(!keytar){throw new Error("Keychain backend is not available")}const account=this.getAccountName(providerId);const serialized=JSON.stringify(credentials);try{await keytar.setPassword(SERVICE_NAME,account,serialized);await this.addToProvidersList(providerId)}catch(error){const message=error instanceof Error?error.message:String(error);throw new Error(`Failed to store credentials in keychain: ${message}`)}}async retrieve(providerId){const keytar=await this.loadKeytar();if(!keytar){return null}const account=this.getAccountName(providerId);try{const serialized=await keytar.getPassword(SERVICE_NAME,account);if(!serialized){return null}const credentials=JSON.parse(serialized);return credentials}catch(error){console.error(`[KeychainBackend] Failed to retrieve credentials: ${error}`);return null}}async delete(providerId){const keytar=await this.loadKeytar();if(!keytar){return}const account=this.getAccountName(providerId);try{await keytar.deletePassword(SERVICE_NAME,account);await this.removeFromProvidersList(providerId)}catch(error){console.error(`[KeychainBackend] Failed to delete credentials: ${error}`)}}async deleteAll(){const keytar=await this.loadKeytar();if(!keytar){return}try{const credentials=await keytar.findCredentials(SERVICE_NAME);for(const cred of credentials){await keytar.deletePassword(SERVICE_NAME,cred.account)}}catch(error){console.error(`[KeychainBackend] Failed to delete all credentials: ${error}`)}}async listProviders(){const keytar=await this.loadKeytar();if(!keytar){return[]}try{const credentials=await keytar.findCredentials(SERVICE_NAME);const providers=[];for(const cred of credentials){if(cred.account.startsWith(ACCOUNT_PREFIX)){const candidateId=cred.account.slice(ACCOUNT_PREFIX.length);if(isValidProviderId(candidateId)){providers.push(candidateId)}}}return providers}catch(error){console.error(`[KeychainBackend] Failed to list providers: ${error}`);return[]}}async addToProvidersList(providerId){const keytar=this.keytar;if(!keytar)return;try{const existing=await keytar.getPassword(SERVICE_NAME,PROVIDERS_LIST_ACCOUNT);let providers=[];if(existing){const parsed=JSON.parse(existing);if(Array.isArray(parsed)){providers=parsed.filter(p=>isValidProviderId(p))}}if(!providers.includes(providerId)){providers.push(providerId);await keytar.setPassword(SERVICE_NAME,PROVIDERS_LIST_ACCOUNT,JSON.stringify(providers))}}catch{}}async removeFromProvidersList(providerId){const keytar=this.keytar;if(!keytar)return;try{const existing=await keytar.getPassword(SERVICE_NAME,PROVIDERS_LIST_ACCOUNT);if(existing){const parsed=JSON.parse(existing);if(Array.isArray(parsed)){const providers=parsed.filter(p=>isValidProviderId(p));const filtered=providers.filter(p=>p!==providerId);await keytar.setPassword(SERVICE_NAME,PROVIDERS_LIST_ACCOUNT,JSON.stringify(filtered))}}}catch{}}};import*as crypto from"node:crypto";import*as fs from"node:fs/promises";import*as os from"node:os";import*as path from"node:path";var ALGORITHM="aes-256-gcm";var IV_LENGTH=12;var AUTH_TAG_LENGTH=16;var KEY_LENGTH=32;var SALT_LENGTH=32;var PBKDF2_ITERATIONS=1e5;var PBKDF2_DIGEST="sha256";var FILE_PERMISSION_MODE=384;var CONFIG_DIR_NAME=".stdio-bus";var CREDENTIALS_FILE_NAME="auth-credentials.enc";var CredentialStoreCorruptedError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="CredentialStoreCorruptedError"}};var EncryptedFileBackend=class{type="encrypted-file";encryptionKey=null;currentSalt=null;filePath;configDir;constructor(customPath){if(customPath){this.filePath=customPath;this.configDir=path.dirname(customPath)}else{this.configDir=path.join(os.homedir(),CONFIG_DIR_NAME);this.filePath=path.join(this.configDir,CREDENTIALS_FILE_NAME)}}async isAvailable(){try{await fs.mkdir(this.configDir,{recursive:true});const testFile=path.join(this.configDir,`.write-test-${Date.now()}`);await fs.writeFile(testFile,"test");await fs.unlink(testFile);return true}catch{return false}}async store(providerId,credentials){const store=await this.loadStore();store.credentials[providerId]=credentials;await this.saveStore(store)}async retrieve(providerId){const store=await this.loadStore();return store.credentials[providerId]??null}async delete(providerId){const store=await this.loadStore();delete store.credentials[providerId];await this.saveStore(store)}async deleteAll(){try{await fs.unlink(this.filePath);this.encryptionKey=null;this.currentSalt=null}catch(error){if(error.code!=="ENOENT"){throw error}}}async listProviders(){const store=await this.loadStore();return Object.keys(store.credentials)}async deriveKey(salt){if(this.encryptionKey&&this.currentSalt&&salt.equals(this.currentSalt)){return this.encryptionKey}const hostname2=os.hostname();const username=os.userInfo().username;const machineEntropy=`${hostname2}:${username}`;const combinedSalt=Buffer.concat([salt,Buffer.from(machineEntropy,"utf8")]);const derivedKey=await new Promise((resolve2,reject)=>{crypto.pbkdf2(machineEntropy,combinedSalt,PBKDF2_ITERATIONS,KEY_LENGTH,PBKDF2_DIGEST,(err,key)=>{if(err){reject(err)}else{resolve2(key)}})});this.encryptionKey=derivedKey;this.currentSalt=salt;return derivedKey}async encrypt(plaintext,salt){const key=await this.deriveKey(salt);const iv=crypto.randomBytes(IV_LENGTH);const cipher=crypto.createCipheriv(ALGORITHM,key,iv,{authTagLength:AUTH_TAG_LENGTH});const encrypted=Buffer.concat([cipher.update(plaintext,"utf8"),cipher.final()]);const authTag=cipher.getAuthTag();return Buffer.concat([salt,iv,authTag,encrypted])}async decrypt(data){const minLength=SALT_LENGTH+IV_LENGTH+AUTH_TAG_LENGTH;if(data.length<minLength){throw new CredentialStoreCorruptedError(`Invalid encrypted data: too short (${data.length} bytes, minimum ${minLength})`)}const salt=data.subarray(0,SALT_LENGTH);const iv=data.subarray(SALT_LENGTH,SALT_LENGTH+IV_LENGTH);const authTag=data.subarray(SALT_LENGTH+IV_LENGTH,SALT_LENGTH+IV_LENGTH+AUTH_TAG_LENGTH);const ciphertext=data.subarray(SALT_LENGTH+IV_LENGTH+AUTH_TAG_LENGTH);const key=await this.deriveKey(salt);try{const decipher=crypto.createDecipheriv(ALGORITHM,key,iv,{authTagLength:AUTH_TAG_LENGTH});decipher.setAuthTag(authTag);const decrypted=Buffer.concat([decipher.update(ciphertext),decipher.final()]);return decrypted.toString("utf8")}catch(error){throw new CredentialStoreCorruptedError("Failed to decrypt credential store: authentication failed or data corrupted",error instanceof Error?error:void 0)}}async loadStore(){try{const encryptedData=await fs.readFile(this.filePath);const jsonData=await this.decrypt(encryptedData);let store;try{store=JSON.parse(jsonData)}catch(parseError){throw new CredentialStoreCorruptedError("Failed to parse credential store: invalid JSON",parseError instanceof Error?parseError:void 0)}if(typeof store.version!=="number"||typeof store.credentials!=="object"){throw new CredentialStoreCorruptedError("Invalid credential store format: missing version or credentials")}return store}catch(error){if(error.code==="ENOENT"){return{version:1,credentials:{}}}if(error instanceof CredentialStoreCorruptedError){throw error}throw new CredentialStoreCorruptedError("Failed to load credential store",error instanceof Error?error:void 0)}}async saveStore(store){await fs.mkdir(this.configDir,{recursive:true});const salt=crypto.randomBytes(SALT_LENGTH);const jsonData=JSON.stringify(store);const encryptedData=await this.encrypt(jsonData,salt);const tempFile=`${this.filePath}.tmp`;await fs.writeFile(tempFile,encryptedData,{mode:FILE_PERMISSION_MODE});await fs.rename(tempFile,this.filePath);try{await fs.chmod(this.filePath,FILE_PERMISSION_MODE)}catch{}}};var MemoryBackend=class{type="memory";storage=new Map;async isAvailable(){return true}async store(providerId,credentials){this.storage.set(providerId,credentials)}async retrieve(providerId){return this.storage.get(providerId)??null}async delete(providerId){this.storage.delete(providerId)}async deleteAll(){this.storage.clear()}async listProviders(){return Array.from(this.storage.keys())}};var CredentialStore=class{backend=null;backendInitialized=false;options;constructor(options={}){this.options=options}async initializeBackend(){if(this.backendInitialized&&this.backend){return this.backend}this.backendInitialized=true;if(this.options.preferredBackend){const preferred=await this.createBackend(this.options.preferredBackend);if(preferred&&await preferred.isAvailable()){this.backend=preferred;console.error(`[CredentialStore] Using preferred backend: ${preferred.type}`);return this.backend}console.error(`[CredentialStore] Preferred backend ${this.options.preferredBackend} not available, falling back`)}const backends=[new KeychainBackend,new EncryptedFileBackend(this.options.encryptedFilePath),new MemoryBackend];for(const backend of backends){try{if(await backend.isAvailable()){this.backend=backend;console.error(`[CredentialStore] Using backend: ${backend.type}`);return this.backend}}catch(error){console.error(`[CredentialStore] Backend ${backend.type} check failed: ${error}`)}}this.backend=new MemoryBackend;console.error(`[CredentialStore] Falling back to memory backend`);return this.backend}async createBackend(type){switch(type){case"keychain":return new KeychainBackend;case"encrypted-file":return new EncryptedFileBackend(this.options.encryptedFilePath);case"memory":return new MemoryBackend;default:return null}}async getBackend(){return this.initializeBackend()}async store(providerId,credentials){const backend=await this.getBackend();await backend.store(providerId,credentials)}async retrieve(providerId){const backend=await this.getBackend();return backend.retrieve(providerId)}async delete(providerId){const backend=await this.getBackend();await backend.delete(providerId)}async deleteAll(){const backend=await this.getBackend();await backend.deleteAll()}async listProviders(){const backend=await this.getBackend();return backend.listProviders()}getBackendType(){if(!this.backend){return"memory"}return this.backend.type}isInitialized(){return this.backendInitialized}async reinitialize(){this.backend=null;this.backendInitialized=false;await this.initializeBackend()}};var DEFAULT_REFRESH_THRESHOLD_MS=5*60*1e3;var TokenManager=class{credentialStore;providerResolver;refreshThresholdMs;pendingRefreshes=new Map;constructor(options){this.credentialStore=options.credentialStore;this.providerResolver=options.providerResolver;this.refreshThresholdMs=options.refreshThresholdMs??DEFAULT_REFRESH_THRESHOLD_MS}async getAccessToken(providerId){const credentials=await this.credentialStore.retrieve(providerId);if(!credentials){return null}if(this.shouldRefresh(credentials)){const refreshedToken=await this.refreshTokenInternal(providerId,credentials);if(refreshedToken!==null){return refreshedToken}if(!this.isExpired(credentials)){return credentials.accessToken}return null}return credentials.accessToken}async storeTokens(providerId,tokens){const now=Date.now();if(!tokens.accessToken||typeof tokens.accessToken!=="string"){throw new Error("Invalid token response: missing or invalid accessToken")}if(tokens.expiresIn!==void 0){if(typeof tokens.expiresIn!=="number"||!Number.isFinite(tokens.expiresIn)||tokens.expiresIn<0){throw new Error("Invalid token response: expiresIn must be a non-negative finite number")}}const expiresAt=tokens.expiresIn?now+tokens.expiresIn*1e3:void 0;const existing=await this.credentialStore.retrieve(providerId);const refreshToken=tokens.refreshToken??existing?.refreshToken;const credentials={providerId,accessToken:tokens.accessToken,refreshToken,expiresAt,scope:tokens.scope,clientId:existing?.clientId,clientSecret:existing?.clientSecret,customEndpoints:existing?.customEndpoints,storedAt:now};await this.credentialStore.store(providerId,credentials)}async hasValidTokens(providerId){const credentials=await this.credentialStore.retrieve(providerId);if(!credentials){return false}return!this.isExpired(credentials)}async forceRefresh(providerId){const credentials=await this.credentialStore.retrieve(providerId);if(!credentials){return null}return this.refreshTokenInternal(providerId,credentials)}async clearTokens(providerId){await this.credentialStore.delete(providerId)}async getStatus(){const providers=await this.credentialStore.listProviders();const statusMap=new Map;for(const providerId of providers){const credentials=await this.credentialStore.retrieve(providerId);if(!credentials){statusMap.set(providerId,"not-configured");continue}if(this.isExpired(credentials)){if(credentials.refreshToken){statusMap.set(providerId,"expired")}else{statusMap.set(providerId,"refresh-failed")}}else{statusMap.set(providerId,"authenticated")}}return statusMap}isExpired(credentials){if(!credentials.expiresAt){return false}return Date.now()>=credentials.expiresAt}shouldRefresh(credentials){if(!credentials.expiresAt){return false}if(!credentials.refreshToken){return false}const timeUntilExpiry=credentials.expiresAt-Date.now();return timeUntilExpiry<=this.refreshThresholdMs}async refreshTokenInternal(providerId,credentials){const pending=this.pendingRefreshes.get(providerId);if(pending){return pending}if(!credentials.refreshToken){console.error(`[TokenManager] No refresh token available for ${providerId}`);return null}const refreshPromise=this.executeRefresh(providerId,credentials.refreshToken);this.pendingRefreshes.set(providerId,refreshPromise);try{return await refreshPromise}finally{this.pendingRefreshes.delete(providerId)}}async executeRefresh(providerId,refreshToken){const provider=this.providerResolver(providerId);if(!provider){console.error(`[TokenManager] Provider not found for ${providerId}`);return null}try{console.error(`[TokenManager] Refreshing token for ${providerId}`);const tokenResponse=await provider.refreshToken(refreshToken);await this.storeTokens(providerId,tokenResponse);console.error(`[TokenManager] Token refreshed successfully for ${providerId}`);return tokenResponse.accessToken}catch(error){const errorMessage=error instanceof Error?error.message:"Unknown error";const sanitizedMessage=errorMessage.replace(/[A-Za-z0-9_-]{20,}/g,"[REDACTED]").replace(/Bearer\s+\S+/gi,"Bearer [REDACTED]");console.error(`[TokenManager] Token refresh failed for ${providerId}: ${sanitizedMessage}`);await this.credentialStore.delete(providerId);return null}}};var BaseAuthProvider=class _BaseAuthProvider{id;name;defaultScopes;authorizationEndpoint;tokenEndpoint;tokenInjection;clientId;clientSecret;static DEFAULT_REQUEST_TIMEOUT_MS=3e4;static PROTECTED_PARAMS=new Set(["client_id","redirect_uri","response_type","scope","state","code_challenge","code_challenge_method","code","code_verifier","grant_type","refresh_token","client_secret"]);constructor(config){this.id=config.id;this.name=config.name;this.authorizationEndpoint=config.authorizationEndpoint;this.tokenEndpoint=config.tokenEndpoint;this.defaultScopes=Object.freeze([...config.defaultScopes]);this.tokenInjection=config.tokenInjection;this.clientId=config.clientId;this.clientSecret=config.clientSecret;this.validateConfig()}buildAuthorizationUrl(params){const url=new URL(this.authorizationEndpoint);url.searchParams.set("client_id",params.clientId);url.searchParams.set("redirect_uri",params.redirectUri);url.searchParams.set("response_type",params.responseType);url.searchParams.set("scope",params.scope);url.searchParams.set("state",params.state);url.searchParams.set("code_challenge",params.codeChallenge);url.searchParams.set("code_challenge_method",params.codeChallengeMethod);if(params.additionalParams){for(const[key,value]of Object.entries(params.additionalParams)){if(_BaseAuthProvider.PROTECTED_PARAMS.has(key.toLowerCase())){throw new Error(`Security violation: additionalParams cannot override protected OAuth parameter '${key}'`)}url.searchParams.set(key,value)}}return url.toString()}async exchangeCode(code,codeVerifier,redirectUri){const body=new URLSearchParams({grant_type:"authorization_code",code,redirect_uri:redirectUri,code_verifier:codeVerifier});if(this.clientId){body.set("client_id",this.clientId)}if(this.clientSecret){body.set("client_secret",this.clientSecret)}const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),_BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS);try{const response=await fetch(this.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","Accept":"application/json"},body:body.toString(),signal:controller.signal,redirect:"error"});if(!response.ok){const errorBody=await response.text();throw new Error(`Token exchange failed: ${response.status} ${errorBody}`)}const data=await response.json();return this.parseTokenResponse(data)}catch(error){if(error instanceof Error&&error.name==="AbortError"){throw new Error(`Token exchange timed out after ${_BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`)}throw error}finally{clearTimeout(timeoutId)}}async refreshToken(refreshToken){const body=new URLSearchParams({grant_type:"refresh_token",refresh_token:refreshToken});if(this.clientId){body.set("client_id",this.clientId)}if(this.clientSecret){body.set("client_secret",this.clientSecret)}const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),_BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS);try{const response=await fetch(this.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","Accept":"application/json"},body:body.toString(),signal:controller.signal,redirect:"error"});if(!response.ok){const errorBody=await response.text();throw new Error(`Token refresh failed: ${response.status} ${errorBody}`)}const data=await response.json();return this.parseTokenResponse(data)}catch(error){if(error instanceof Error&&error.name==="AbortError"){throw new Error(`Token refresh timed out after ${_BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`)}throw error}finally{clearTimeout(timeoutId)}}validateConfig(){this.validateHttpsEndpoint(this.authorizationEndpoint,"authorization");this.validateHttpsEndpoint(this.tokenEndpoint,"token")}getTokenInjection(){return this.tokenInjection}getEndpoints(){return{authorizationEndpoint:this.authorizationEndpoint,tokenEndpoint:this.tokenEndpoint}}setClientCredentials(clientId,clientSecret){this.clientId=clientId;this.clientSecret=clientSecret}validateHttpsEndpoint(endpoint,name){const url=new URL(endpoint);if(url.protocol!=="https:"){throw new Error(`${this.name} ${name} endpoint must use HTTPS: ${endpoint}`)}if(url.username||url.password){throw new Error(`${this.name} ${name} endpoint must not contain embedded credentials: ${endpoint}`)}}parseTokenResponse(data){const accessToken=data.access_token;if(typeof accessToken!=="string"){throw new Error("Invalid token response: missing access_token")}const tokenType=data.token_type;if(typeof tokenType!=="string"){throw new Error("Invalid token response: missing token_type")}const response={accessToken,tokenType};if(typeof data.expires_in==="number"){response.expiresIn=data.expires_in}if(typeof data.refresh_token==="string"){response.refreshToken=data.refresh_token}if(typeof data.scope==="string"){response.scope=data.scope}if(typeof data.id_token==="string"){response.idToken=data.id_token}return response}};var GitHubProvider=class extends BaseAuthProvider{constructor(clientId,clientSecret){super({id:"github",name:"GitHub",authorizationEndpoint:"https://github.com/login/oauth/authorize",tokenEndpoint:"https://github.com/login/oauth/access_token",defaultScopes:["read:user"],tokenInjection:{type:"header",key:"Authorization",format:"Bearer {token}"},clientId,clientSecret})}};var GoogleProvider=class extends BaseAuthProvider{constructor(clientId,clientSecret){super({id:"google",name:"Google",authorizationEndpoint:"https://accounts.google.com/o/oauth2/v2/auth",tokenEndpoint:"https://oauth2.googleapis.com/token",defaultScopes:["openid","profile","email"],tokenInjection:{type:"header",key:"Authorization",format:"Bearer {token}"},clientId,clientSecret})}};var VALID_DOMAIN_PATTERN=/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i;var VALID_REGION_PATTERN=/^[a-z]{2}-[a-z]+-\d+$/;var CognitoProvider=class _CognitoProvider extends BaseAuthProvider{constructor(config){_CognitoProvider.validateUserPoolDomain(config.userPoolDomain);_CognitoProvider.validateRegion(config.region);const baseUrl=`https://${config.userPoolDomain}.auth.${config.region}.amazoncognito.com`;super({id:"cognito",name:"AWS Cognito",authorizationEndpoint:`${baseUrl}/oauth2/authorize`,tokenEndpoint:`${baseUrl}/oauth2/token`,defaultScopes:["openid","profile"],tokenInjection:{type:"header",key:"Authorization",format:"Bearer {token}"},clientId:config.clientId,clientSecret:config.clientSecret})}static validateUserPoolDomain(domain){if(!domain||typeof domain!=="string"){throw new Error("Cognito userPoolDomain is required")}const trimmed=domain.trim();if(trimmed!==domain){throw new Error("Cognito userPoolDomain must not contain leading/trailing whitespace")}if(domain.length===0){throw new Error("Cognito userPoolDomain cannot be empty")}if(domain.length>63){throw new Error("Cognito userPoolDomain must be 63 characters or less")}if(/[/:?#@\s]/.test(domain)){throw new Error("Cognito userPoolDomain contains invalid characters (/, :, ?, #, @, or whitespace)")}if(!VALID_DOMAIN_PATTERN.test(domain)){throw new Error("Cognito userPoolDomain must be alphanumeric with hyphens, no leading/trailing hyphens")}}static validateRegion(region){if(!region||typeof region!=="string"){throw new Error("Cognito region is required")}const trimmed=region.trim();if(trimmed!==region){throw new Error("Cognito region must not contain leading/trailing whitespace")}if(region.length===0){throw new Error("Cognito region cannot be empty")}if(/[/:?#@\s]/.test(region)){throw new Error("Cognito region contains invalid characters (/, :, ?, #, @, or whitespace)")}if(!VALID_REGION_PATTERN.test(region)){throw new Error("Cognito region must be a valid AWS region format (e.g., us-east-1, eu-west-2)")}}};var WELL_KNOWN_TENANTS=new Set(["common","organizations","consumers"]);var GUID_PATTERN=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;var DOMAIN_PATTERN=/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;var EntraIdProvider=class _EntraIdProvider extends BaseAuthProvider{constructor(config){_EntraIdProvider.validateTenantId(config.tenantId);const baseUrl=`https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0`;super({id:"azure",name:"Microsoft Entra ID",authorizationEndpoint:`${baseUrl}/authorize`,tokenEndpoint:`${baseUrl}/token`,defaultScopes:["openid","profile"],tokenInjection:{type:"header",key:"Authorization",format:"Bearer {token}"},clientId:config.clientId,clientSecret:config.clientSecret})}static validateTenantId(tenantId){if(!tenantId||typeof tenantId!=="string"){throw new Error("Entra ID tenantId is required")}const trimmed=tenantId.trim();if(trimmed!==tenantId){throw new Error("Entra ID tenantId must not contain leading/trailing whitespace")}if(tenantId.length===0){throw new Error("Entra ID tenantId cannot be empty")}if(/[/:?#@\s]/.test(tenantId)){throw new Error("Entra ID tenantId contains invalid characters (/, :, ?, #, @, or whitespace)")}if(WELL_KNOWN_TENANTS.has(tenantId.toLowerCase())){return}if(GUID_PATTERN.test(tenantId)){return}if(DOMAIN_PATTERN.test(tenantId)){return}throw new Error(`Entra ID tenantId must be 'common', 'organizations', 'consumers', a valid GUID, or a verified domain name`)}};import*as crypto2 from"crypto";var OIDCProvider=class _OIDCProvider extends BaseAuthProvider{issuer;tokenEndpointAuthMethod;discoveryTimeoutMs;skipDiscovery;manualJwksUri;discoveredAuthorizationEndpoint;discoveredTokenEndpoint;discoveryDocument;discoveryAttempted=false;cachedJWKS;static DEFAULT_DISCOVERY_TIMEOUT_MS=1e4;static DEFAULT_JWKS_CACHE_TTL_MS=60*60*1e3;static DEFAULT_CLOCK_SKEW_SECONDS=60;constructor(config){_OIDCProvider.validateIssuer(config.issuer);const authorizationEndpoint=config.authorizationEndpoint||`${config.issuer}/authorize`;const tokenEndpoint=config.tokenEndpoint||`${config.issuer}/oauth/token`;if(config.authorizationEndpoint){_OIDCProvider.validateEndpoint(config.authorizationEndpoint,"authorization")}if(config.tokenEndpoint){_OIDCProvider.validateEndpoint(config.tokenEndpoint,"token")}if(config.jwksUri){_OIDCProvider.validateEndpoint(config.jwksUri,"jwks")}super({id:"oidc",name:"Generic OIDC",authorizationEndpoint,tokenEndpoint,defaultScopes:config.scopes||["openid","profile"],tokenInjection:{type:"header",key:"Authorization",format:"Bearer {token}"},clientId:config.clientId,clientSecret:config.clientSecret});this.issuer=config.issuer;this.tokenEndpointAuthMethod=config.tokenEndpointAuthMethod||"client_secret_post";this.discoveryTimeoutMs=config.discoveryTimeoutMs||_OIDCProvider.DEFAULT_DISCOVERY_TIMEOUT_MS;this.skipDiscovery=config.skipDiscovery||false;this.manualJwksUri=config.jwksUri;if(config.authorizationEndpoint&&config.tokenEndpoint){this.discoveryAttempted=true}}static validateIssuer(issuer){if(!issuer||typeof issuer!=="string"){throw new Error("OIDC issuer is required")}const trimmed=issuer.trim();if(trimmed!==issuer){throw new Error("OIDC issuer must not contain leading/trailing whitespace")}if(issuer.length===0){throw new Error("OIDC issuer cannot be empty")}let url;try{url=new URL(issuer)}catch{throw new Error(`OIDC issuer must be a valid URL: ${issuer}`)}if(url.protocol!=="https:"){throw new Error(`OIDC issuer must use HTTPS: ${issuer}`)}if(url.username||url.password){throw new Error("OIDC issuer must not contain embedded credentials")}if(url.search||url.hash){throw new Error("OIDC issuer must not contain query string or fragment")}}static validateEndpoint(endpoint,name){if(!endpoint||typeof endpoint!=="string"){throw new Error(`OIDC ${name} endpoint is required`)}let url;try{url=new URL(endpoint)}catch{throw new Error(`OIDC ${name} endpoint must be a valid URL: ${endpoint}`)}if(url.protocol!=="https:"){throw new Error(`OIDC ${name} endpoint must use HTTPS: ${endpoint}`)}if(url.username||url.password){throw new Error(`OIDC ${name} endpoint must not contain embedded credentials`)}}getIssuer(){return this.issuer}getJwksUri(){return this.discoveryDocument?.jwks_uri||this.manualJwksUri}getDiscoveryDocument(){return this.discoveryDocument}isDiscoveryAttempted(){return this.discoveryAttempted}getAuthorizationEndpoint(){return this.discoveredAuthorizationEndpoint||this.getEndpoints().authorizationEndpoint}getTokenEndpoint(){return this.discoveredTokenEndpoint||this.getEndpoints().tokenEndpoint}async discover(){if(this.skipDiscovery){return{success:false,error:"Discovery skipped by configuration"}}const discoveryUrl=`${this.issuer}/.well-known/openid-configuration`;const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),this.discoveryTimeoutMs);try{const response=await fetch(discoveryUrl,{method:"GET",headers:{"Accept":"application/json"},signal:controller.signal,redirect:"error"});if(!response.ok){this.discoveryAttempted=true;return{success:false,error:`Discovery failed: HTTP ${response.status} ${response.statusText}`}}const document=await response.json();const validationError=this.validateDiscoveryDocument(document);if(validationError){this.discoveryAttempted=true;return{success:false,error:validationError}}this.discoveryDocument=document;this.discoveryAttempted=true;this.discoveredAuthorizationEndpoint=document.authorization_endpoint;this.discoveredTokenEndpoint=document.token_endpoint;return{success:true,document}}catch(error){this.discoveryAttempted=true;if(error instanceof Error&&error.name==="AbortError"){return{success:false,error:`Discovery timed out after ${this.discoveryTimeoutMs}ms`}}return{success:false,error:`Discovery failed: ${error instanceof Error?error.message:String(error)}`}}finally{clearTimeout(timeoutId)}}validateDiscoveryDocument(document){if(!document.issuer){return"Discovery document missing required field: issuer"}if(!document.authorization_endpoint){return"Discovery document missing required field: authorization_endpoint"}if(!document.token_endpoint){return"Discovery document missing required field: token_endpoint"}const normalizedDocIssuer=document.issuer.replace(/\/$/,"");const normalizedConfigIssuer=this.issuer.replace(/\/$/,"");if(normalizedDocIssuer!==normalizedConfigIssuer){return`Discovery document issuer mismatch: expected ${this.issuer}, got ${document.issuer}`}try{_OIDCProvider.validateEndpoint(document.authorization_endpoint,"authorization");_OIDCProvider.validateEndpoint(document.token_endpoint,"token");if(document.jwks_uri){_OIDCProvider.validateEndpoint(document.jwks_uri,"jwks")}}catch(error){return error instanceof Error?error.message:String(error)}return void 0}async ensureDiscovered(){if(!this.discoveryAttempted&&!this.skipDiscovery){await this.discover()}}async exchangeCode(code,codeVerifier,redirectUri){await this.ensureDiscovered();const body=new URLSearchParams({grant_type:"authorization_code",code,redirect_uri:redirectUri,code_verifier:codeVerifier});const headers={"Content-Type":"application/x-www-form-urlencoded","Accept":"application/json"};if(this.tokenEndpointAuthMethod==="client_secret_basic"){if(this.clientId&&this.clientSecret){const credentials=Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");headers["Authorization"]=`Basic ${credentials}`}if(this.clientId){body.set("client_id",this.clientId)}}else{if(this.clientId){body.set("client_id",this.clientId)}if(this.clientSecret){body.set("client_secret",this.clientSecret)}}const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS);const tokenEndpoint=this.getTokenEndpoint();try{const response=await fetch(tokenEndpoint,{method:"POST",headers,body:body.toString(),signal:controller.signal,redirect:"error"});if(!response.ok){const errorBody=await response.text();throw new Error(`Token exchange failed: ${response.status} ${errorBody}`)}const data=await response.json();return this.parseTokenResponse(data)}catch(error){if(error instanceof Error&&error.name==="AbortError"){throw new Error(`Token exchange timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`)}throw error}finally{clearTimeout(timeoutId)}}async refreshToken(refreshToken){await this.ensureDiscovered();const body=new URLSearchParams({grant_type:"refresh_token",refresh_token:refreshToken});const headers={"Content-Type":"application/x-www-form-urlencoded","Accept":"application/json"};if(this.tokenEndpointAuthMethod==="client_secret_basic"){if(this.clientId&&this.clientSecret){const credentials=Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");headers["Authorization"]=`Basic ${credentials}`}if(this.clientId){body.set("client_id",this.clientId)}}else{if(this.clientId){body.set("client_id",this.clientId)}if(this.clientSecret){body.set("client_secret",this.clientSecret)}}const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS);const tokenEndpoint=this.getTokenEndpoint();try{const response=await fetch(tokenEndpoint,{method:"POST",headers,body:body.toString(),signal:controller.signal,redirect:"error"});if(!response.ok){const errorBody=await response.text();throw new Error(`Token refresh failed: ${response.status} ${errorBody}`)}const data=await response.json();return this.parseTokenResponse(data)}catch(error){if(error instanceof Error&&error.name==="AbortError"){throw new Error(`Token refresh timed out after ${BaseAuthProvider.DEFAULT_REQUEST_TIMEOUT_MS}ms`)}throw error}finally{clearTimeout(timeoutId)}}async fetchJWKS(forceRefresh=false){if(!forceRefresh&&this.cachedJWKS){const now=Date.now();const cacheAge=now-this.cachedJWKS.fetchedAt;if(cacheAge<this.cachedJWKS.ttlMs){return this.cachedJWKS.jwks}}await this.ensureDiscovered();const jwksUri=this.getJwksUri();if(!jwksUri){return null}const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),this.discoveryTimeoutMs);try{const response=await fetch(jwksUri,{method:"GET",headers:{"Accept":"application/json"},signal:controller.signal,redirect:"error"});if(!response.ok){throw new Error(`JWKS fetch failed: HTTP ${response.status} ${response.statusText}`)}const jwks=await response.json();if(!jwks.keys||!Array.isArray(jwks.keys)){throw new Error("Invalid JWKS: missing or invalid keys array")}this.cachedJWKS={jwks,fetchedAt:Date.now(),ttlMs:_OIDCProvider.DEFAULT_JWKS_CACHE_TTL_MS};return jwks}catch(error){if(error instanceof Error&&error.name==="AbortError"){throw new Error(`JWKS fetch timed out after ${this.discoveryTimeoutMs}ms`)}throw error}finally{clearTimeout(timeoutId)}}async findKey(kid){let jwks=await this.fetchJWKS(false);if(jwks){const key=jwks.keys.find(k=>k.kid===kid);if(key){return key}}jwks=await this.fetchJWKS(true);if(jwks){const key=jwks.keys.find(k=>k.kid===kid);if(key){return key}}return null}clearJWKSCache(){this.cachedJWKS=void 0}getCachedJWKS(){return this.cachedJWKS}async validateIdToken(idToken,options){try{const parts=idToken.split(".");if(parts.length!==3){return{valid:false,error:"Invalid JWT format: expected 3 parts"}}const[headerB64,payloadB64,signatureB64]=parts;let header;try{header=JSON.parse(_OIDCProvider.base64UrlDecode(headerB64))}catch{return{valid:false,error:"Invalid JWT header: failed to decode"}}let claims;try{claims=JSON.parse(_OIDCProvider.base64UrlDecode(payloadB64))}catch{return{valid:false,error:"Invalid JWT payload: failed to decode"}}const signatureValid=await this.validateJWTSignature(headerB64,payloadB64,signatureB64,header);if(!signatureValid){return{valid:false,error:"Invalid JWT signature"}}const claimsValidation=this.validateIDTokenClaims(claims,options);if(!claimsValidation.valid){return claimsValidation}return{valid:true,claims}}catch(error){return{valid:false,error:`Token validation failed: ${error instanceof Error?error.message:String(error)}`}}}async validateJWTSignature(headerB64,payloadB64,signatureB64,header){if(header.alg!=="RS256"){throw new Error(`Unsupported algorithm: ${header.alg}. Only RS256 is supported.`)}if(!header.kid){throw new Error("JWT header missing kid (key ID)")}const key=await this.findKey(header.kid);if(!key){throw new Error(`Key not found in JWKS: ${header.kid}`)}if(key.kty!=="RSA"){throw new Error(`Unsupported key type: ${key.kty}. Only RSA is supported.`)}if(!key.n||!key.e){throw new Error("Invalid RSA key: missing n or e")}const publicKey=_OIDCProvider.jwkToPem(key);const signedData=`${headerB64}.${payloadB64}`;const signature=_OIDCProvider.base64UrlToBuffer(signatureB64);const verifier=crypto2.createVerify("RSA-SHA256");verifier.update(signedData);return verifier.verify(publicKey,signature)}validateIDTokenClaims(claims,options){const clockSkew=options.clockSkewSeconds??_OIDCProvider.DEFAULT_CLOCK_SKEW_SECONDS;const now=Math.floor(Date.now()/1e3);const normalizedClaimsIss=claims.iss?.replace(/\/$/,"");const normalizedIssuer=this.issuer.replace(/\/$/,"");if(!claims.iss||normalizedClaimsIss!==normalizedIssuer){return{valid:false,error:`Invalid issuer: expected ${this.issuer}, got ${claims.iss}`}}const audiences=Array.isArray(claims.aud)?claims.aud:[claims.aud];if(!audiences.includes(options.audience)){return{valid:false,error:`Invalid audience: expected ${options.audience}, got ${claims.aud}`}}if(typeof claims.exp!=="number"){return{valid:false,error:"Missing or invalid exp claim"}}if(claims.exp+clockSkew<now){return{valid:false,error:"Token has expired"}}if(typeof claims.iat!=="number"){return{valid:false,error:"Missing or invalid iat claim"}}if(claims.iat-clockSkew>now){return{valid:false,error:"Token issued in the future"}}if(options.nonce!==void 0){if(claims.nonce!==options.nonce){return{valid:false,error:`Invalid nonce: expected ${options.nonce}, got ${claims.nonce}`}}}return{valid:true,claims}}static base64UrlDecode(input){let base64=input.replace(/-/g,"+").replace(/_/g,"/");const padding=base64.length%4;if(padding){base64+="=".repeat(4-padding)}return Buffer.from(base64,"base64").toString("utf-8")}static base64UrlToBuffer(input){let base64=input.replace(/-/g,"+").replace(/_/g,"/");const padding=base64.length%4;if(padding){base64+="=".repeat(4-padding)}return Buffer.from(base64,"base64")}static jwkToPem(jwk){if(!jwk.n||!jwk.e){throw new Error("Invalid JWK: missing n or e")}const keyObject=crypto2.createPublicKey({key:{kty:jwk.kty,n:jwk.n,e:jwk.e},format:"jwk"});return keyObject.export({type:"spki",format:"pem"})}};var providerRegistry=new Map;var SUPPORTED_PROVIDERS=["github","google","cognito","azure","oidc"];function registerProvider(providerId,factory){if(!isValidProviderId(providerId)){throw new Error(`Invalid provider ID: '${providerId}'. Supported providers: ${SUPPORTED_PROVIDERS.join(", ")}`)}providerRegistry.set(providerId,factory)}function getProvider(providerId){const factory=providerRegistry.get(providerId);if(!factory){const supported=getRegisteredProviders().join(", ")||"none";throw new Error(`Provider '${providerId}' is not registered. Registered providers: ${supported}`)}return factory()}function hasProvider(providerId){return providerRegistry.has(providerId)}function getRegisteredProviders(){return Array.from(providerRegistry.keys())}function initializeProviders(){if(!providerRegistry.has("github")){registerProvider("github",()=>new GitHubProvider)}if(!providerRegistry.has("google")){registerProvider("google",()=>new GoogleProvider)}const cognitoUserPoolDomain=process.env.COGNITO_USER_POOL_DOMAIN;const cognitoRegion=process.env.COGNITO_REGION||"us-east-1";if(cognitoUserPoolDomain&&!providerRegistry.has("cognito")){registerProvider("cognito",()=>new CognitoProvider({userPoolDomain:cognitoUserPoolDomain,region:cognitoRegion}))}const azureTenantId=process.env.AZURE_TENANT_ID;if(azureTenantId&&!providerRegistry.has("azure")){registerProvider("azure",()=>new EntraIdProvider({tenantId:azureTenantId}))}const oidcIssuer=process.env.OIDC_ISSUER;if(oidcIssuer&&!providerRegistry.has("oidc")){registerProvider("oidc",()=>new OIDCProvider({issuer:oidcIssuer,authorizationEndpoint:process.env.OIDC_AUTHORIZATION_ENDPOINT,tokenEndpoint:process.env.OIDC_TOKEN_ENDPOINT,jwksUri:process.env.OIDC_JWKS_URI,clientId:process.env.OIDC_CLIENT_ID,clientSecret:process.env.OIDC_CLIENT_SECRET,tokenEndpointAuthMethod:process.env.OIDC_TOKEN_ENDPOINT_AUTH_METHOD}))}}initializeProviders();var VALID_MODEL_PROVIDER_IDS=["openai","anthropic"];function isValidModelProviderId(value){return typeof value==="string"&&VALID_MODEL_PROVIDER_IDS.includes(value)}var MODEL_CREDENTIAL_INJECTION_CONFIG={openai:{type:"header",headerName:"Authorization",format:"Bearer {key}"},anthropic:{type:"header",headerName:"x-api-key"}};var OPENAI_PROVIDER_ID="openai";var OPENAI_API_KEY_PREFIX="sk-";var OPENAI_API_KEY_MIN_LENGTH=20;var OPENAI_STORAGE_KEY="model-credential:openai";var OpenAIApiKeyHandler=class{storage;constructor(storage){this.storage=storage}getProviderId(){return OPENAI_PROVIDER_ID}getInjectionConfig(){return MODEL_CREDENTIAL_INJECTION_CONFIG.openai}validateFormat(apiKey){if(!apiKey||typeof apiKey!=="string"){return{valid:false}}const trimmedKey=apiKey.trim();if(trimmedKey.length<OPENAI_API_KEY_MIN_LENGTH){return{valid:false}}if(!trimmedKey.startsWith(OPENAI_API_KEY_PREFIX)){return{valid:true,warning:`API key does not start with expected prefix '${OPENAI_API_KEY_PREFIX}'`}}return{valid:true}}async store(apiKey,label){const validation=this.validateFormat(apiKey);if(!validation.valid){throw new Error("Invalid OpenAI API key format")}const credential={providerId:OPENAI_PROVIDER_ID,apiKey:apiKey.trim(),label,storedAt:Date.now()};await this.storage.store(OPENAI_STORAGE_KEY,credential)}async retrieve(){try{const stored=await this.storage.retrieve(OPENAI_STORAGE_KEY);if(!stored){return{found:false}}if(stored.expiresAt&&stored.expiresAt<Date.now()){return{found:false,error:"API key has expired"}}const credential={providerId:stored.providerId,apiKey:stored.apiKey,label:stored.label,storedAt:stored.storedAt,expiresAt:stored.expiresAt};return{found:true,credential}}catch(error){return{found:false,error:error instanceof Error?error.message:"Failed to retrieve credential"}}}async delete(){await this.storage.delete(OPENAI_STORAGE_KEY)}async isConfigured(){const result=await this.retrieve();return result.found}async getStatus(){const result=await this.retrieve();if(!result.found){return{providerId:OPENAI_PROVIDER_ID,status:"not-configured"}}const credential=result.credential;if(credential.expiresAt&&credential.expiresAt<Date.now()){return{providerId:OPENAI_PROVIDER_ID,status:"expired",label:credential.label,storedAt:credential.storedAt,expiresAt:credential.expiresAt}}return{providerId:OPENAI_PROVIDER_ID,status:"configured",label:credential.label,storedAt:credential.storedAt,expiresAt:credential.expiresAt}}async injectHeader(headers={}){const result=await this.retrieve();if(!result.found||!result.credential){throw new Error("No OpenAI API key configured")}const injection=this.getInjectionConfig();const headerValue=injection.format?injection.format.replace("{key}",result.credential.apiKey):result.credential.apiKey;return{...headers,[injection.headerName]:headerValue}}async getHeaderInjection(){const result=await this.retrieve();if(!result.found||!result.credential){throw new Error("No OpenAI API key configured")}const injection=this.getInjectionConfig();const headerValue=injection.format?injection.format.replace("{key}",result.credential.apiKey):result.credential.apiKey;return{headerName:injection.headerName,headerValue}}};var ANTHROPIC_PROVIDER_ID="anthropic";var ANTHROPIC_API_KEY_PREFIX="sk-ant-";var ANTHROPIC_API_KEY_MIN_LENGTH=20;var ANTHROPIC_STORAGE_KEY="model-credential:anthropic";var AnthropicApiKeyHandler=class{storage;constructor(storage){this.storage=storage}getProviderId(){return ANTHROPIC_PROVIDER_ID}getInjectionConfig(){return MODEL_CREDENTIAL_INJECTION_CONFIG.anthropic}validateFormat(apiKey){if(!apiKey||typeof apiKey!=="string"){return{valid:false}}const trimmedKey=apiKey.trim();if(trimmedKey.length<ANTHROPIC_API_KEY_MIN_LENGTH){return{valid:false}}if(!trimmedKey.startsWith(ANTHROPIC_API_KEY_PREFIX)){return{valid:true,warning:`API key does not start with expected prefix '${ANTHROPIC_API_KEY_PREFIX}'`}}return{valid:true}}async store(apiKey,label){const validation=this.validateFormat(apiKey);if(!validation.valid){throw new Error("Invalid Anthropic API key format")}const credential={providerId:ANTHROPIC_PROVIDER_ID,apiKey:apiKey.trim(),label,storedAt:Date.now()};await this.storage.store(ANTHROPIC_STORAGE_KEY,credential)}async retrieve(){try{const stored=await this.storage.retrieve(ANTHROPIC_STORAGE_KEY);if(!stored){return{found:false}}if(stored.expiresAt&&stored.expiresAt<Date.now()){return{found:false,error:"API key has expired"}}const credential={providerId:stored.providerId,apiKey:stored.apiKey,label:stored.label,storedAt:stored.storedAt,expiresAt:stored.expiresAt};return{found:true,credential}}catch(error){return{found:false,error:error instanceof Error?error.message:"Failed to retrieve credential"}}}async delete(){await this.storage.delete(ANTHROPIC_STORAGE_KEY)}async isConfigured(){const result=await this.retrieve();return result.found}async getStatus(){const result=await this.retrieve();if(!result.found){return{providerId:ANTHROPIC_PROVIDER_ID,status:"not-configured"}}const credential=result.credential;if(credential.expiresAt&&credential.expiresAt<Date.now()){return{providerId:ANTHROPIC_PROVIDER_ID,status:"expired",label:credential.label,storedAt:credential.storedAt,expiresAt:credential.expiresAt}}return{providerId:ANTHROPIC_PROVIDER_ID,status:"configured",label:credential.label,storedAt:credential.storedAt,expiresAt:credential.expiresAt}}async injectHeader(headers={}){const result=await this.retrieve();if(!result.found||!result.credential){throw new Error("No Anthropic API key configured")}const injection=this.getInjectionConfig();const headerValue=injection.format?injection.format.replace("{key}",result.credential.apiKey):result.credential.apiKey;return{...headers,[injection.headerName]:headerValue}}async getHeaderInjection(){const result=await this.retrieve();if(!result.found||!result.credential){throw new Error("No Anthropic API key configured")}const injection=this.getInjectionConfig();const headerValue=injection.format?injection.format.replace("{key}",result.credential.apiKey):result.credential.apiKey;return{headerName:injection.headerName,headerValue}}};var CLIENT_CREDENTIALS_MARKER="__CLIENT_CREDENTIALS_CONFIGURED__";function isMarkerToken(token){return token===CLIENT_CREDENTIALS_MARKER}var AuthMethodSelectionError=class extends Error{constructor(message,code,details){super(message);this.code=code;this.details=details;this.name="AuthMethodSelectionError"}};var AuthManager=class{credentialStore;tokenManager;legacyApiKeys;providerResolver;methodPrecedenceConfig;openAIHandler;anthropicHandler;inFlightAuthFlows=new Map;constructor(optionsOrCredentialStore,tokenManager,legacyApiKeys){if(this.isAuthManagerOptions(optionsOrCredentialStore)){this.credentialStore=optionsOrCredentialStore.credentialStore;this.tokenManager=optionsOrCredentialStore.tokenManager;this.legacyApiKeys=optionsOrCredentialStore.legacyApiKeys;this.providerResolver=optionsOrCredentialStore.providerResolver??getProvider;this.methodPrecedenceConfig={...DEFAULT_AUTH_METHOD_PRECEDENCE,...optionsOrCredentialStore.methodPrecedence};if(optionsOrCredentialStore.modelCredentialStorage){this.openAIHandler=new OpenAIApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage);this.anthropicHandler=new AnthropicApiKeyHandler(optionsOrCredentialStore.modelCredentialStorage)}}else{this.credentialStore=optionsOrCredentialStore;this.tokenManager=tokenManager;this.legacyApiKeys=legacyApiKeys??{};this.providerResolver=getProvider;this.methodPrecedenceConfig={...DEFAULT_AUTH_METHOD_PRECEDENCE}}}isAuthManagerOptions(arg){return typeof arg==="object"&&arg!==null&&"credentialStore"in arg&&"tokenManager"in arg&&"legacyApiKeys"in arg}async authenticateAgent(providerId,options){if(!isValidProviderId(providerId)){return{success:false,providerId,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${providerId}' is not supported.`,details:{supportedProviders:[...VALID_PROVIDER_IDS]}}}}if(!hasProvider(providerId)){return{success:false,providerId,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${providerId}' is not registered.`,details:{registeredProviders:getRegisteredProviders()}}}}const existingFlow=this.inFlightAuthFlows.get(providerId);if(existingFlow){console.error(`[AuthManager] Auth flow already in progress for ${providerId}, waiting for result...`);return existingFlow}const flowPromise=this.executeAuthFlow(providerId,options);this.inFlightAuthFlows.set(providerId,flowPromise);try{return await flowPromise}finally{if(this.inFlightAuthFlows.get(providerId)===flowPromise){this.inFlightAuthFlows.delete(providerId)}}}async executeAuthFlow(providerId,options){try{const agentAuthFlow=new AgentAuthFlow({getProvider:this.providerResolver,storeTokens:async(pid,tokens)=>{await this.tokenManager.storeTokens(pid,tokens)}});console.error(`[AuthManager] Starting agent auth flow for ${providerId}`);const result=await agentAuthFlow.execute(providerId,options);if(result.success){console.error(`[AuthManager] Agent auth flow completed successfully for ${providerId}`)}else{console.error(`[AuthManager] Agent auth flow failed for ${providerId}: ${result.error?.message}`)}return result}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[AuthManager] Agent auth flow error for ${providerId}: ${errorMessage}`);return{success:false,providerId,error:{code:"PROVIDER_ERROR",message:`Authentication failed: ${errorMessage}`}}}}async setupTerminal(providerId){if(!isValidProviderId(providerId)){return{success:false,providerId,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${providerId}' is not supported.`,details:{supportedProviders:[...VALID_PROVIDER_IDS]}}}}try{const terminalAuthFlow=new TerminalAuthFlow({credentialStore:this.credentialStore,validateCredentials:async(pid,credentials)=>{return this.validateTerminalCredentials(pid,credentials)}});console.error(`[AuthManager] Starting terminal setup for ${providerId}`);const flowResult=await terminalAuthFlow.execute(providerId);if(flowResult.useBrowserOAuth){console.error(`[AuthManager] User selected browser OAuth for ${providerId}, launching browser flow`);return this.authenticateAgent(flowResult.providerId)}const result=flowResult.authResult;if(result.success){console.error(`[AuthManager] Terminal setup completed successfully for ${providerId}`)}else{console.error(`[AuthManager] Terminal setup failed for ${providerId}: ${result.error?.message}`)}return result}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[AuthManager] Terminal setup error for ${providerId}: ${errorMessage}`);return{success:false,providerId,error:{code:"PROVIDER_ERROR",message:`Terminal setup failed: ${errorMessage}`}}}}async validateTerminalCredentials(providerId,credentials){try{if(!credentials.clientId||credentials.clientId.trim().length===0){return{valid:false,error:"Client ID is required"}}if(!/^[a-zA-Z0-9._-]+$/.test(credentials.clientId.trim())){return{valid:false,error:"Client ID contains invalid characters"}}if(!isValidProviderId(providerId)){return{valid:false,error:`Provider '${providerId}' is not supported`}}try{const provider=this.providerResolver(providerId);if(!provider){return{valid:false,error:`Provider '${providerId}' is not available`}}}catch{return{valid:false,error:`Provider '${providerId}' is not available`}}return{valid:true,accessToken:CLIENT_CREDENTIALS_MARKER}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);return{valid:false,error:errorMessage}}}async getTokenForAgent(agentId,providerId){if(providerId){if(!isValidProviderId(providerId)){console.error(`[AuthManager] Invalid provider ID: ${providerId}`);return null}const oauthToken=await this.tokenManager.getAccessToken(providerId);if(oauthToken&&!isMarkerToken(oauthToken)){console.error(`[AuthManager] Using OAuth token for agent ${agentId} (provider: ${providerId})`);return oauthToken}console.error(`[AuthManager] No OAuth token available for specified provider ${providerId}`);const legacyKeys2=this.legacyApiKeys[agentId];if(legacyKeys2?.apiKey){const agentProvider2=this.getProviderForAgent(agentId);if(agentProvider2===providerId){console.error(`[AuthManager] Using legacy API key for agent ${agentId} (provider: ${providerId})`);return legacyKeys2.apiKey}}return null}const agentProvider=this.getProviderForAgent(agentId);if(agentProvider){const token=await this.tokenManager.getAccessToken(agentProvider);if(token&&!isMarkerToken(token)){console.error(`[AuthManager] Using OAuth token for agent ${agentId} (auto-detected provider: ${agentProvider})`);return token}}const legacyKeys=this.legacyApiKeys[agentId];if(legacyKeys?.apiKey){console.error(`[AuthManager] Using legacy API key for agent ${agentId}`);return legacyKeys.apiKey}console.error(`[AuthManager] No credentials available for agent ${agentId}`);return null}async injectAuth(agentId,request){const agentProvider=this.getProviderForAgent(agentId);if(agentProvider){const token=await this.tokenManager.getAccessToken(agentProvider);if(token&&!isMarkerToken(token)){try{const provider=this.providerResolver(agentProvider);const injection=provider.getTokenInjection();const validationError=this.validateInjectionConfig(injection);if(validationError){console.error(`[AuthManager] Invalid injection config for ${agentProvider}: ${validationError}`)}else{const result=this.applyTokenInjection(request,token,injection);if(result!==null){return result}console.error(`[AuthManager] Token injection failed for ${agentProvider}, trying legacy fallback`)}}catch(error){console.error(`[AuthManager] Provider resolution failed for ${agentProvider}`)}}}const legacyKeys=this.legacyApiKeys[agentId];if(legacyKeys?.apiKey){const result=this.applyTokenInjection(request,legacyKeys.apiKey,{type:"header",key:"Authorization",format:"Bearer {token}"});if(result!==null){return result}console.error(`[AuthManager] Legacy API key contains control characters, refusing to inject`)}return request}validateInjectionConfig(injection){if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(injection.key)){return"Invalid injection key format"}if(injection.key.includes("\r")||injection.key.includes("\n")){return"Injection key contains invalid characters"}if(injection.format){if(!injection.format.includes("{token}")){return"Injection format must contain {token} placeholder"}if(injection.format.includes("\r")||injection.format.includes("\n")){return"Injection format contains invalid characters"}}if(injection.type==="query"){console.error("[AuthManager] Warning: Token injection via query parameter is less secure")}return null}applyTokenInjection(request,token,injection){const formattedToken=injection.format?injection.format.replace("{token}",token):token;if(/[\x00-\x1f\x7f]/.test(formattedToken)){console.error("[AuthManager] Token contains control characters, refusing to inject");return null}const result={...request};switch(injection.type){case"header":{const headers=result.headers??{};result.headers={...headers,[injection.key]:formattedToken};break}case"query":{const query=result.query??{};result.query={...query,[injection.key]:formattedToken};break}case"body":{const body=result.body??{};result.body={...body,[injection.key]:formattedToken};break}}return result}async getStatus(){const statusMap=new Map;const tokenStatus=await this.tokenManager.getStatus();const providers=await this.credentialStore.listProviders();for(const providerId of providers){const status=tokenStatus.get(providerId)??"not-configured";const credentials=await this.credentialStore.retrieve(providerId);const entry={providerId,status,expiresAt:credentials?.expiresAt,scope:credentials?.scope,lastRefresh:credentials?.storedAt};statusMap.set(providerId,entry)}for(const providerId of VALID_PROVIDER_IDS){if(!statusMap.has(providerId)){statusMap.set(providerId,{providerId,status:"not-configured"})}}return statusMap}async logout(providerId){if(providerId){if(!isValidProviderId(providerId)){throw new Error(`Invalid provider ID for logout: ${providerId}`)}console.error(`[AuthManager] Logging out from ${providerId}`);await this.tokenManager.clearTokens(providerId);await this.credentialStore.delete(providerId)}else{console.error(`[AuthManager] Logging out from all OAuth providers`);const providers=await this.credentialStore.listProviders();for(const pid of providers){await this.tokenManager.clearTokens(pid)}await this.credentialStore.deleteAll()}}async requiresReauth(providerId){const hasValid=await this.tokenManager.hasValidTokens(providerId);if(hasValid){return false}const credentials=await this.credentialStore.retrieve(providerId);if(!credentials){return true}const refreshed=await this.tokenManager.forceRefresh(providerId);return refreshed===null}getProviderForAgent(agentId){const agentLower=agentId.toLowerCase();if(agentLower.includes("github")||agentLower.includes("copilot")){return"github"}if(agentLower.includes("google")||agentLower.includes("gemini")){return"google"}if(agentLower.includes("azure")){return"azure"}if(agentLower.includes("cognito")||agentLower.includes("aws")){return"cognito"}return void 0}async getModelCredential(providerId){if(!isValidModelProviderId(providerId)){return{found:false,error:`Invalid model provider ID: ${providerId}. Valid providers: ${VALID_MODEL_PROVIDER_IDS.join(", ")}`}}const handler=this.getModelCredentialHandler(providerId);if(!handler){return{found:false,error:`Model credential storage not configured. Initialize AuthManager with modelCredentialStorage option.`}}const result=await handler.retrieve();if(result.found){console.error(`[AuthManager] Retrieved model credential for ${providerId}`)}else{console.error(`[AuthManager] No model credential found for ${providerId}`)}return result}async hasModelCredential(providerId){if(!isValidModelProviderId(providerId)){return false}const handler=this.getModelCredentialHandler(providerId);if(!handler){return false}return handler.isConfigured()}async getModelCredentialStatus(){const statusMap=new Map;for(const providerId of VALID_MODEL_PROVIDER_IDS){const handler=this.getModelCredentialHandler(providerId);if(handler){const status=await handler.getStatus();statusMap.set(providerId,status)}else{statusMap.set(providerId,{providerId,status:"not-configured"})}}return statusMap}async injectModelAuth(providerId,request){if(!isValidModelProviderId(providerId)){console.error(`[AuthManager] Invalid model provider ID for injection: ${providerId}`);return request}const handler=this.getModelCredentialHandler(providerId);if(!handler){console.error(`[AuthManager] Model credential storage not configured for ${providerId}`);return request}const credentialResult=await handler.retrieve();if(!credentialResult.found||!credentialResult.credential){console.error(`[AuthManager] No model credential available for ${providerId}`);return request}const injection=MODEL_CREDENTIAL_INJECTION_CONFIG[providerId];const apiKey=credentialResult.credential.apiKey;const headerValue=injection.format?injection.format.replace("{key}",apiKey):apiKey;if(/[\x00-\x1f\x7f]/.test(headerValue)){console.error(`[AuthManager] Model API key contains control characters, refusing to inject`);return request}const result={...request};const headers=result.headers??{};result.headers={...headers,[injection.headerName]:headerValue};console.error(`[AuthManager] Injected model credential for ${providerId}`);return result}getModelProviderForAgent(agentId){const agentLower=agentId.toLowerCase();if(agentLower.includes("openai")||agentLower.includes("gpt")||agentLower.includes("chatgpt")){return"openai"}if(agentLower.includes("anthropic")||agentLower.includes("claude")){return"anthropic"}return void 0}getModelCredentialHandler(providerId){switch(providerId){case"openai":return this.openAIHandler;case"anthropic":return this.anthropicHandler;default:return void 0}}async selectAuthMethod(agentId,availableMethods,providerId){const{methodPrecedence,failFastOnUnsupported,failFastOnAmbiguous}=this.methodPrecedenceConfig;if(providerId!==void 0){if(!isValidProviderId(providerId)){const error=`Provider '${providerId}' is not supported. Valid providers: ${VALID_PROVIDER_IDS.join(", ")}`;if(failFastOnUnsupported){throw new AuthMethodSelectionError(error,"UNSUPPORTED_METHOD",{providerId,supportedProviders:[...VALID_PROVIDER_IDS]})}console.error(`[AuthManager] ${error}`);return{methodType:"api-key",hasCredential:false,error}}}const methodsToTry=availableMethods?methodPrecedence.filter(m=>availableMethods.includes(m)):methodPrecedence;if(!providerId&&failFastOnAmbiguous){const ambiguityCheck=this.checkProviderAmbiguity(agentId);if(ambiguityCheck.isAmbiguous){throw new AuthMethodSelectionError(`Ambiguous provider mapping for agent '${agentId}'. Multiple providers could match: ${ambiguityCheck.matchingProviders.join(", ")}. Specify an explicit providerId.`,"AMBIGUOUS_PROVIDER",{agentId,matchingProviders:ambiguityCheck.matchingProviders})}}for(const methodType of methodsToTry){if(!isValidAuthMethodType(methodType)){const error=`Unsupported authentication method: ${methodType}`;if(failFastOnUnsupported){throw new AuthMethodSelectionError(error,"UNSUPPORTED_METHOD",{methodType,supportedMethods:["oauth2","api-key"]})}console.error(`[AuthManager] ${error}, skipping...`);continue}const result=await this.tryAuthMethod(agentId,methodType,providerId);if(result.hasCredential){console.error(`[AuthManager] Selected auth method '${methodType}' for agent '${agentId}'`);return result}}console.error(`[AuthManager] No credentials available for agent '${agentId}'`);return{methodType:methodPrecedence[0]??"oauth2",hasCredential:false,error:`No credentials available for agent '${agentId}'`}}async tryAuthMethod(agentId,methodType,providerId){switch(methodType){case"oauth2":{const effectiveProviderId=providerId??this.getProviderForAgent(agentId);if(!effectiveProviderId){return{methodType:"oauth2",hasCredential:false,error:`No OAuth provider mapping for agent '${agentId}'`}}const token=await this.tokenManager.getAccessToken(effectiveProviderId);if(token&&!isMarkerToken(token)){return{methodType:"oauth2",providerId:effectiveProviderId,hasCredential:true}}return{methodType:"oauth2",providerId:effectiveProviderId,hasCredential:false,error:`No OAuth token available for provider '${effectiveProviderId}'`}}case"api-key":{const legacyKeys=this.legacyApiKeys[agentId];if(legacyKeys?.apiKey){return{methodType:"api-key",hasCredential:true}}return{methodType:"api-key",hasCredential:false,error:`No API key available for agent '${agentId}'`}}default:{return{methodType,hasCredential:false,error:`Unknown authentication method: ${methodType}`}}}}checkProviderAmbiguity(agentId){const agentLower=agentId.toLowerCase();const matchingProviders=[];const providerKeywords={github:["github","copilot"],google:["google","gemini"],azure:["azure"],cognito:["cognito","aws"],oidc:["oidc","openid","auth0","okta","keycloak","onelogin","ping"]};for(const[provider,keywords]of Object.entries(providerKeywords)){if(keywords.some(keyword=>agentLower.includes(keyword))){matchingProviders.push(provider)}}return{isAmbiguous:matchingProviders.length>1,matchingProviders}}getMethodPrecedenceConfig(){return{...this.methodPrecedenceConfig}}};async function runSetupCommand(options={}){const output=options.output??process.stderr;try{if(options.providerId!==void 0&&!isValidProviderId(options.providerId)){output.write(`
58
+ Error: Invalid provider '${options.providerId}'.
59
+ `);output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}
60
+
61
+ `);return 1}const credentialStore=new CredentialStore;const terminalAuthFlow=new TerminalAuthFlow({credentialStore,validateCredentials:async(_providerId,credentials)=>{if(!credentials.clientId||credentials.clientId.trim().length===0){return{valid:false,error:"This field is required"}}if(!credentials.clientSecret){return{valid:true,accessToken:credentials.clientId.trim()}}return{valid:true,accessToken:CLIENT_CREDENTIALS_MARKER}},input:options.input,output});const flowResult=await terminalAuthFlow.execute(options.providerId);if(flowResult.useBrowserOAuth){output.write("\nLaunching browser for OAuth authentication...\n");output.write("Please complete the authentication in your browser.\n\n");const tokenManager=new TokenManager({credentialStore,providerResolver:getProvider});const agentAuthFlow=new AgentAuthFlow({getProvider,storeTokens:async(providerId,tokens)=>{await tokenManager.storeTokens(providerId,tokens)}});const browserResult=await agentAuthFlow.execute(flowResult.providerId);if(browserResult.success){output.write(`
62
+ ${flowResult.providerId} authentication completed successfully!
63
+
64
+ `);return 0}else{output.write(`
65
+ Browser authentication failed: ${browserResult.error?.message}
66
+ `);return 1}}const result=flowResult.authResult;if(result.success){return 0}else{return 1}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);output.write(`
67
+ Setup failed: ${errorMessage}
68
+ `);console.error(`[SetupCommand] Error: ${errorMessage}`);return 1}}function formatTimestamp(timestamp){if(!timestamp){return"N/A"}return new Date(timestamp).toLocaleString()}function getStatusLabel(status){switch(status){case"authenticated":return"\u2713 Authenticated";case"expired":return"\u26A0 Expired (refresh available)";case"refresh-failed":return"\u2717 Refresh Failed (re-auth required)";case"not-configured":return"\u25CB Not Configured";default:return"? Unknown"}}function sanitizeForOutput(value){return value.replace(/[\x00-\x1f\x7f]|\x1b\[[0-9;]*[a-zA-Z]/g,"")}function formatProviderStatus(entry){const lines=[];const providerName=entry.providerId.charAt(0).toUpperCase()+entry.providerId.slice(1);lines.push(` ${providerName}:`);lines.push(` Status: ${getStatusLabel(entry.status)}`);if(entry.status!=="not-configured"){if(entry.expiresAt){const now=Date.now();const isExpired=entry.expiresAt<=now;const expiresLabel=isExpired?"Expired at":"Expires at";lines.push(` ${expiresLabel}: ${formatTimestamp(entry.expiresAt)}`)}if(entry.scope){lines.push(` Scope: ${sanitizeForOutput(entry.scope)}`)}if(entry.lastRefresh){lines.push(` Last Updated: ${formatTimestamp(entry.lastRefresh)}`)}}return lines}function formatModelCredentialStatus(entry){const lines=[];const providerName=entry.providerId.charAt(0).toUpperCase()+entry.providerId.slice(1);lines.push(` ${providerName}:`);switch(entry.status){case"configured":lines.push(` Status: \u2713 Configured`);if(entry.label){lines.push(` Label: ${sanitizeForOutput(entry.label)}`)}if(entry.storedAt){lines.push(` Stored: ${formatTimestamp(entry.storedAt)}`)}break;case"expired":lines.push(` Status: \u26A0 Expired`);break;case"not-configured":lines.push(` Status: \u25CB Not Configured`);break}return lines}var CredentialStoreAdapter=class{storage=new Map;async store(key,credential){this.storage.set(key,{...credential})}async retrieve(key){const cred=this.storage.get(key);return cred?{...cred}:null}async delete(key){this.storage.delete(key)}async exists(key){return this.storage.has(key)}};async function runStatusCommand(options={}){const output=options.output??process.stderr;try{const credentialStore=new CredentialStore;const tokenManager=new TokenManager({credentialStore,providerResolver:()=>null});const modelCredentialStorage=new CredentialStoreAdapter;const authManager=new AuthManager({credentialStore,tokenManager,legacyApiKeys:{},modelCredentialStorage});const statusMap=await authManager.getStatus();const modelStatusMap=await authManager.getModelCredentialStatus();output.write("\n=== OAuth Authentication Status ===\n\n");let authenticatedCount=0;let expiredCount=0;let notConfiguredCount=0;for(const providerId of VALID_PROVIDER_IDS){const entry=statusMap.get(providerId);if(entry){const lines=formatProviderStatus(entry);for(const line of lines){output.write(line+"\n")}output.write("\n");switch(entry.status){case"authenticated":authenticatedCount++;break;case"expired":case"refresh-failed":expiredCount++;break;case"not-configured":notConfiguredCount++;break}}}output.write("=== Model API Keys ===\n\n");let modelConfiguredCount=0;let modelNotConfiguredCount=0;for(const providerId of VALID_MODEL_PROVIDER_IDS){const entry=modelStatusMap.get(providerId);if(entry){const lines=formatModelCredentialStatus(entry);for(const line of lines){output.write(line+"\n")}output.write("\n");switch(entry.status){case"configured":modelConfiguredCount++;break;case"expired":case"not-configured":modelNotConfiguredCount++;break}}}output.write("--- Summary ---\n");output.write(` OAuth Authenticated: ${authenticatedCount}
69
+ `);output.write(` OAuth Expired/Failed: ${expiredCount}
70
+ `);output.write(` OAuth Not Configured: ${notConfiguredCount}
71
+ `);output.write(` Model Keys Configured: ${modelConfiguredCount}
72
+ `);output.write(` Model Keys Not Configured: ${modelNotConfiguredCount}
73
+ `);output.write("\n");if(notConfiguredCount===VALID_PROVIDER_IDS.length&&modelNotConfiguredCount===VALID_MODEL_PROVIDER_IDS.length){output.write("Tip: Run with --setup to configure authentication.\n\n")}else if(expiredCount>0){output.write("Tip: Run with --setup to re-authenticate expired providers.\n\n")}return 0}catch(error){const errorMessage=error instanceof Error?error.message:String(error);output.write(`
74
+ Failed to get auth status: ${errorMessage}
75
+ `);console.error(`[StatusCommand] Error: ${errorMessage}`);return 1}}async function runLogoutCommand(providerId,options={}){const output=options.output??process.stderr;try{if(providerId!==void 0&&!isValidProviderId(providerId)){output.write(`
76
+ Error: Invalid provider '${providerId}'.
77
+ `);output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}
78
+
79
+ `);return 1}const credentialStore=new CredentialStore;const tokenManager=new TokenManager({credentialStore,providerResolver:()=>null});const authManager=new AuthManager({credentialStore,tokenManager,legacyApiKeys:{}});const configuredProviders=await credentialStore.listProviders();if(providerId){if(!configuredProviders.includes(providerId)){output.write(`
80
+ No credentials found for provider '${providerId}'.
81
+
82
+ `);return 0}await authManager.logout(providerId);const providerName=providerId.charAt(0).toUpperCase()+providerId.slice(1);output.write(`
83
+ Successfully logged out from ${providerName}.
84
+
85
+ `)}else{if(configuredProviders.length===0){output.write("\nNo credentials found. Nothing to logout.\n\n");return 0}await authManager.logout();output.write(`
86
+ Successfully logged out from all providers.
87
+ `);output.write(`Removed credentials for: ${configuredProviders.join(", ")}
88
+
89
+ `)}return 0}catch(error){const errorMessage=error instanceof Error?error.message:String(error);output.write(`
90
+ Logout failed: ${errorMessage}
91
+ `);console.error(`[LogoutCommand] Error: ${errorMessage}`);return 1}}var DEFAULT_TIMEOUT_MS=5*60*1e3;async function runLoginCommand(providerId,options={}){const output=options.output??process.stderr;let timeoutMs=options.timeoutMs??DEFAULT_TIMEOUT_MS;if(!Number.isFinite(timeoutMs)||timeoutMs<=0){timeoutMs=DEFAULT_TIMEOUT_MS}timeoutMs=Math.max(1e3,Math.min(timeoutMs,30*60*1e3));if(!isValidProviderId(providerId)){output.write(`
92
+ Error: Invalid provider '${providerId}'.
93
+ `);output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}
94
+
95
+ `);return 1}try{const credentialStore=new CredentialStore;const tokenManager=new TokenManager({credentialStore,providerResolver:getProvider});const authManager=new AuthManager({credentialStore,tokenManager,legacyApiKeys:{}});const providerName=providerId.charAt(0).toUpperCase()+providerId.slice(1);const timeoutMinutes=Math.round(timeoutMs/6e4);output.write(`
96
+ Opening browser for ${providerName} authentication...
97
+ `);output.write(`Waiting for authorization (timeout: ${timeoutMinutes} minutes)...
98
+
99
+ `);const result=await authManager.authenticateAgent(providerId,{timeoutMs});if(result.success){output.write(`
100
+ \u2713 Successfully authenticated with ${providerName}.
101
+
102
+ `);return 0}else{const error=result.error;if(error.code==="TIMEOUT"){output.write(`
103
+ \u2717 Authentication timed out.
104
+ `);output.write(`The browser authorization flow did not complete within ${timeoutMinutes} minutes.
105
+ `);output.write(`Please try again and complete the authorization in your browser.
106
+
107
+ `)}else if(error.code==="INVALID_STATE"){output.write(`
108
+ \u2717 Authentication failed: Security validation error.
109
+ `);output.write(`The authorization response could not be verified. Please try again.
110
+
111
+ `)}else if(error.code==="CALLBACK_ERROR"){output.write(`
112
+ \u2717 Authentication cancelled or failed.
113
+ `);if(error.message){output.write(`${error.message}
114
+ `)}output.write(`
115
+ `)}else if(error.code==="PROVIDER_ERROR"){output.write(`
116
+ \u2717 ${providerName} returned an error.
117
+ `);if(error.message){output.write(`${error.message}
118
+ `)}output.write(`
119
+ `)}else if(error.code==="UNSUPPORTED_PROVIDER"){output.write(`
120
+ \u2717 Provider '${providerId}' is not supported.
121
+ `);output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}
122
+
123
+ `)}else{output.write(`
124
+ \u2717 Authentication failed.
125
+ `);if(error.message){output.write(`${error.message}
126
+ `)}output.write(`
127
+ `)}console.error(`[LoginCommand] Authentication failed for ${providerId}: ${error.code} - ${error.message}`);return 1}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);output.write(`
128
+ \u2717 Login failed: ${errorMessage}
129
+
130
+ `);console.error(`[LoginCommand] Error: ${errorMessage}`);return 1}}var ExitCodes={SUCCESS:0,FATAL_ERROR:1};var isShuttingDown=false;function parseArgs(){const args=process.argv.slice(2);const result={};let i=0;while(i<args.length){const arg=args[i];if(arg==="--custom-agents"){const nextArg=args[i+1];if(nextArg&&!nextArg.startsWith("-")){result.customAgentsPath=nextArg;i+=2;continue}logWarn("--custom-agents requires a file path argument, ignoring");i+=1;continue}if(arg==="--setup"){result.setup=true;i+=1;continue}if(arg==="--auth-status"){result.authStatus=true;i+=1;continue}if(arg==="--logout"){result.logout=true;const nextArg=args[i+1];if(nextArg&&!nextArg.startsWith("-")){result.logoutProvider=nextArg;i+=2;continue}i+=1;continue}if(arg==="--login"){result.login=true;const nextArg=args[i+1];if(nextArg&&!nextArg.startsWith("-")){result.loginProvider=nextArg;i+=2;continue}i+=1;continue}if(!arg.startsWith("-")&&!result.configPath){result.configPath=arg}i+=1}return result}function setupSignalHandlers(runtimeManager,shutdownTimeoutMs){const shutdown=async()=>{if(isShuttingDown){return}isShuttingDown=true;logInfo4("Received shutdown signal, initiating graceful shutdown");try{await runtimeManager.terminateAll(shutdownTimeoutMs);logInfo4("All agent processes terminated");process.exit(ExitCodes.SUCCESS)}catch(error){logError4(`Error during shutdown: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}};process.on("SIGTERM",()=>{void shutdown()});process.on("SIGINT",()=>{void shutdown()});return shutdown}function setupStdinHandler(router,ndjsonHandler){ndjsonHandler.onMessage(async message=>{try{const errorResponse=await router.route(message);if(errorResponse){ndjsonHandler.write(errorResponse)}}catch(error){logError4(`Unexpected error routing message: ${error.message}`)}});ndjsonHandler.onError((error,line)=>{logError4(`Failed to parse NDJSON: ${error.message} - Line: ${line.slice(0,100)}`)});process.stdin.setEncoding("utf8");process.stdin.on("data",chunk=>{const buffer=typeof chunk==="string"?Buffer.from(chunk):chunk;ndjsonHandler.processChunk(buffer)});process.stdin.on("end",()=>{logInfo4("stdin closed")});process.stdin.on("error",error=>{logError4(`stdin error: ${error.message}`)})}function setupAgentResponseHandling(runtimeManager,router){runtimeManager.onAgentExit((agentId,code)=>{logExit(agentId,code)});const originalGetOrSpawn=runtimeManager.getOrSpawn.bind(runtimeManager);runtimeManager.getOrSpawn=async function(agentId,spawnCommand){const runtime=await originalGetOrSpawn(agentId,spawnCommand);const proc=runtime.process;if(proc.stdout&&!proc.stdout.listenerCount("data")){let buffer="";proc.stdout.on("data",chunk=>{buffer+=chunk;let newlineIndex;while((newlineIndex=buffer.indexOf("\n"))!==-1){const line=buffer.slice(0,newlineIndex);buffer=buffer.slice(newlineIndex+1);if(line.trim()){try{const response=JSON.parse(line);router.handleAgentResponse(agentId,response)}catch(err){logError4(`Failed to parse agent ${agentId} response: ${err.message}`)}}}})}if(proc.stderr&&!proc.stderr.listenerCount("data")){proc.stderr.on("data",chunk=>{process.stderr.write(`[agent:${agentId}] ${chunk}`)})}return runtime}}async function main(){logInfo4("Registry Launcher starting");const parsedArgs=parseArgs();if(parsedArgs.setup){logInfo4("Running --setup command");const exitCode=await runSetupCommand();process.exit(exitCode)}if(parsedArgs.authStatus){logInfo4("Running --auth-status command");const exitCode=await runStatusCommand();process.exit(exitCode)}if(parsedArgs.logout){logInfo4("Running --logout command");const exitCode=await runLogoutCommand(parsedArgs.logoutProvider);process.exit(exitCode)}if(parsedArgs.login){logInfo4("Running --login command");if(!parsedArgs.loginProvider){logError4("Error: --login requires a provider argument.");logError4(`Usage: --login <provider>`);logError4(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}`);process.exit(ExitCodes.FATAL_ERROR)}if(!isValidProviderId(parsedArgs.loginProvider)){logError4(`Error: Invalid provider '${parsedArgs.loginProvider}'.`);logError4(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}`);process.exit(ExitCodes.FATAL_ERROR)}const exitCode=await runLoginCommand(parsedArgs.loginProvider);process.exit(exitCode)}if(parsedArgs.configPath){logInfo4(`Loading configuration from: ${parsedArgs.configPath}`)}const config=loadConfig(parsedArgs.configPath);if(parsedArgs.customAgentsPath){config.customAgentsPath=parsedArgs.customAgentsPath}logInfo4(`Configuration loaded: registryUrl=${config.registryUrl}, apiKeysPath=${config.apiKeysPath}, shutdownTimeoutSec=${config.shutdownTimeoutSec}`);const apiKeys=loadApiKeys(config.apiKeysPath);const registry=new RegistryIndex(config.registryUrl);try{await registry.fetch()}catch(error){if(error instanceof RegistryFetchError){logError4(`Failed to fetch registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}if(error instanceof RegistryParseError){logError4(`Failed to parse registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}logError4(`Unexpected error fetching registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}if(config.customAgentsPath){try{logInfo4(`Loading custom agents from: ${config.customAgentsPath}`);const customAgents=loadCustomAgents(config.customAgentsPath);registry.mergeCustomAgents(customAgents)}catch(error){if(error instanceof CustomAgentsLoadError){logError4(`Failed to load custom agents: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}if(error instanceof RegistryParseError){logError4(`Invalid custom agents file: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}logError4(`Unexpected error loading custom agents: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}}const runtimeManager=new AgentRuntimeManager;const ndjsonHandler=new NDJSONHandler(process.stdout);const credentialStore=new CredentialStore;const tokenManager=new TokenManager({credentialStore,providerResolver:getProvider});const authManager=new AuthManager({credentialStore,tokenManager,legacyApiKeys:apiKeys});logInfo4("OAuth authentication manager initialized");const router=new MessageRouter(registry,runtimeManager,message=>ndjsonHandler.write(message),apiKeys,authManager);const shutdownTimeoutMs=config.shutdownTimeoutSec*1e3;setupSignalHandlers(runtimeManager,shutdownTimeoutMs);setupAgentResponseHandling(runtimeManager,router);setupStdinHandler(router,ndjsonHandler);logInfo4("Registry Launcher ready, waiting for messages")}main().catch(error=>{logError4(`Fatal error: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)});
131
+ //# sourceMappingURL=index.js.map