@stdiobus/workers-registry 1.5.5 → 1.5.6
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.
|
@@ -54,7 +54,7 @@ OIDC Discovery URL: ${discoveryUrl}`);this.writeLine("The authorization and toke
|
|
|
54
54
|
<p>Error code: <span class="error-code">${safeError}</span></p>
|
|
55
55
|
</div>
|
|
56
56
|
</body>
|
|
57
|
-
</html>`}escapeHtml(text){return text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}};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(`
|
|
57
|
+
</html>`}escapeHtml(text){return text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}};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] keytar not available, falling back to encrypted-file storage`);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
58
|
Error: Invalid provider '${options.providerId}'.
|
|
59
59
|
`);output.write(`Supported providers: ${VALID_PROVIDER_IDS.join(", ")}
|
|
60
60
|
|