@ponythewhite/base-context 1.0.2 → 1.0.5
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.
- package/CHANGELOG.md +19 -0
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/src/rlm/mcp.py +39 -17
- package/dist/base-context-runtime/test/test_mcp.py +87 -1
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/amazon-bedrock.js +2 -2
- package/dist/bundle/{bundled-modules-PWOGKUQQ.js → bundled-modules-UVCJPNFC.js} +4 -4
- package/dist/bundle/{chunk-IADCPVEI.js → chunk-6FZM46AU.js} +27 -12
- package/dist/bundle/{chunk-HKINIBP4.js → chunk-LFPWHI42.js} +1 -1
- package/dist/bundle/{chunk-27QCJ2AZ.js → chunk-UPVDERMD.js} +0 -12
- package/dist/bundle/{chunk-44PEHPOG.js → chunk-WFJRFQGG.js} +402 -277
- package/dist/bundle/{cli-main-4DS7Y3PI.js → cli-main-OYVMMJUO.js} +3 -3
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{main-SV7JLVEB.js → main-AVMYOTHF.js} +4 -4
- package/dist/cli/daemon-ps.d.ts.map +1 -1
- package/dist/cli/daemon-ps.js +1 -0
- package/dist/cli/daemon-ps.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +77 -19
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/kernel/bootstrap.d.ts.map +1 -1
- package/dist/core/kernel/bootstrap.js +5 -2
- package/dist/core/kernel/bootstrap.js.map +1 -1
- package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
- package/dist/core/mcp/mcp-manager.js +2 -2
- package/dist/core/mcp/mcp-manager.js.map +1 -1
- package/dist/installer.mjs +5 -2
- package/dist/modes/daemon/daemon-supervisor-ownership.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-supervisor-ownership.js +16 -16
- package/dist/modes/daemon/daemon-supervisor-ownership.js.map +1 -1
- package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
- package/dist/modes/interactive/auth-flows.js +7 -6
- package/dist/modes/interactive/auth-flows.js.map +1 -1
- package/dist/modes/interactive/components/brand-announcement.d.ts +5 -0
- package/dist/modes/interactive/components/brand-announcement.d.ts.map +1 -0
- package/dist/modes/interactive/components/brand-announcement.js +14 -0
- package/dist/modes/interactive/components/brand-announcement.js.map +1 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +3 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +3 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/menu-panel.d.ts +1 -0
- package/dist/modes/interactive/components/menu-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/menu-panel.js +3 -0
- package/dist/modes/interactive/components/menu-panel.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +9 -6
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +11 -9
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/shared/startup-notices.d.ts.map +1 -1
- package/dist/modes/shared/startup-notices.js +1 -1
- package/dist/modes/shared/startup-notices.js.map +1 -1
- package/dist/themes/synerise-logo.d.ts +3 -0
- package/dist/themes/synerise-logo.d.ts.map +1 -0
- package/dist/themes/synerise-logo.js +9 -0
- package/dist/themes/synerise-logo.js.map +1 -0
- package/package.json +6 -6
- package/dist/modes/interactive/assets/clankolas.png +0 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +0 -1
- package/dist/modes/interactive/components/earendil-announcement.js +0 -40
- package/dist/modes/interactive/components/earendil-announcement.js.map +0 -1
- package/dist/themes/prime-logo.d.ts +0 -9
- package/dist/themes/prime-logo.d.ts.map +0 -1
- package/dist/themes/prime-logo.js +0 -18
- package/dist/themes/prime-logo.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-flows.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAOlE,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAGzB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,oBAAoB,GAC7B;IACA,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC7B,GACD;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAExB,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD,eAAO,MAAM,mCAAmC,0MACwJ,CAAC;AAMzM,wBAAsB,mCAAmC,CACxD,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACrC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmB7B;AAID,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,EACrC,kBAAkB,GAAE,WAAW,CAAC,MAAM,CAA4B,GAChE,OAAO,CAQT;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0GAA0G;IAC1G,kBAAkB,IAAI,OAAO,CAAC,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACnE,oFAAoF;IACpF,aAAa,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,2EAA2E;IAC3E,gBAAgB,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,eAAe,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED,8FAA8F;AAC9F,qBAAa,iBAAiB;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,qBAAqB,EAAI;IAE5D;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAazE;IAED,QAAQ,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqC1E;IAED,aAAa,CAAC,cAAc,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CASjF;IAED,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA8ClC;IAED,uBAAuB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,oBAAoB,EAAE,CAkC9E;IAED,OAAO,CAAC,wBAAwB;YA4BlB,8BAA8B;YAyB9B,6BAA6B;YAe7B,0BAA0B;YAK1B,sBAAsB;YAgCtB,qBAAqB;IAmCnC,OAAO,CAAC,oBAAoB;YA4Bd,eAAe;CA6F7B","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@ponythewhite/base-context-ai\";\nimport type { OverlayHandle, TUI } from \"@ponythewhite/base-context-tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport { getProviderAuthContract } from \"../../core/provider-contracts.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\" || getProviderAuthContract(model.provider).oauth !== \"supported\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No supported OAuth providers are registered. Use /login to configure an API-key provider.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.logout(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst oauthProviders = authStorage.getOAuthProviders();\n\t\tconst oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t\t// MCP integrations (mcp:<server>) are services, not model providers.\n\t\t\t...(provider.id.startsWith(\"mcp:\") ? { category: \"service\" as const } : {}),\n\t\t}));\n\n\t\tconst modelProviders = new Set(this.host.modelRegistry.getAll().map((model) => model.provider));\n\t\tfor (const providerId of modelProviders) {\n\t\t\tif (!isApiKeyLoginProvider(providerId, oauthProviderIds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname: this.host.modelRegistry.getProviderDisplayName(providerId),\n\t\t\t\tauthType: \"api_key\",\n\t\t\t});\n\t\t}\n\n\t\t// Serper is a skill credential, not a model provider, so add it manually.\n\t\toptions.push({\n\t\t\tid: SERPER_CREDENTIAL_ID,\n\t\t\tname: SERPER_CREDENTIAL_NAME,\n\t\t\tauthType: \"api_key\",\n\t\t\tcategory: \"service\",\n\t\t});\n\n\t\tconst filteredOptions = authType ? options.filter((option) => option.authType === authType) : options;\n\t\treturn filteredOptions.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\")).trim();\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"API key cannot be empty.\");\n\t\t\t}\n\n\t\t\tthis.host.modelRegistry.authStorage.set(providerId, { type: \"api_key\", key: apiKey });\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(\n\t\t\t\tproviderId,\n\t\t\t\tproviderName,\n\t\t\t\t\"oauth\",\n\t\t\t\tkind === \"provider\" ? \"Use /model to select a model\" : undefined,\n\t\t\t\tkind,\n\t\t\t);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-flows.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAOlE,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAGzB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,oBAAoB,GAC7B;IACA,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC7B,GACD;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAExB,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD,eAAO,MAAM,mCAAmC,0MACwJ,CAAC;AAMzM,wBAAsB,mCAAmC,CACxD,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACrC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmB7B;AAID,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,EACrC,kBAAkB,GAAE,WAAW,CAAC,MAAM,CAA4B,GAChE,OAAO,CAQT;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0GAA0G;IAC1G,kBAAkB,IAAI,OAAO,CAAC,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACnE,oFAAoF;IACpF,aAAa,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,2EAA2E;IAC3E,gBAAgB,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,eAAe,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED,8FAA8F;AAC9F,qBAAa,iBAAiB;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,qBAAqB,EAAI;IAE5D;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAazE;IAED,QAAQ,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqC1E;IAED,aAAa,CAAC,cAAc,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CASjF;IAED,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA8ClC;IAED,uBAAuB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,oBAAoB,EAAE,CAkC9E;IAED,OAAO,CAAC,wBAAwB;YA4BlB,8BAA8B;YAyB9B,6BAA6B;YAe7B,0BAA0B;YAK1B,sBAAsB;YAgCtB,qBAAqB;IAsCnC,OAAO,CAAC,oBAAoB;YA4Bd,eAAe;CA6F7B","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@ponythewhite/base-context-ai\";\nimport type { OverlayHandle, TUI } from \"@ponythewhite/base-context-tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport { getProviderAuthContract } from \"../../core/provider-contracts.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\" || getProviderAuthContract(model.provider).oauth !== \"supported\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No supported OAuth providers are registered. Use /login to configure an API-key provider.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.removeVerified(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst oauthProviders = authStorage.getOAuthProviders();\n\t\tconst oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t\t// MCP integrations (mcp:<server>) are services, not model providers.\n\t\t\t...(provider.id.startsWith(\"mcp:\") ? { category: \"service\" as const } : {}),\n\t\t}));\n\n\t\tconst modelProviders = new Set(this.host.modelRegistry.getAll().map((model) => model.provider));\n\t\tfor (const providerId of modelProviders) {\n\t\t\tif (!isApiKeyLoginProvider(providerId, oauthProviderIds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname: this.host.modelRegistry.getProviderDisplayName(providerId),\n\t\t\t\tauthType: \"api_key\",\n\t\t\t});\n\t\t}\n\n\t\t// Serper is a skill credential, not a model provider, so add it manually.\n\t\toptions.push({\n\t\t\tid: SERPER_CREDENTIAL_ID,\n\t\t\tname: SERPER_CREDENTIAL_NAME,\n\t\t\tauthType: \"api_key\",\n\t\t\tcategory: \"service\",\n\t\t});\n\n\t\tconst filteredOptions = authType ? options.filter((option) => option.authType === authType) : options;\n\t\treturn filteredOptions.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.setFromLogin(\n\t\t\t\tproviderId,\n\t\t\t\tasync () => {\n\t\t\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\", undefined, { masked: true })).trim();\n\t\t\t\t\tif (!apiKey) throw new Error(\"API key cannot be empty.\");\n\t\t\t\t\treturn { type: \"api_key\", key: apiKey };\n\t\t\t\t},\n\t\t\t\tdialog.signal,\n\t\t\t);\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(\n\t\t\t\tproviderId,\n\t\t\t\tproviderName,\n\t\t\t\t\"oauth\",\n\t\t\t\tkind === \"provider\" ? \"Use /model to select a model\" : undefined,\n\t\t\t\tkind,\n\t\t\t);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
|
|
@@ -122,7 +122,7 @@ export class ProviderAuthFlows {
|
|
|
122
122
|
const selector = new OAuthSelectorComponent("logout", this.host.modelRegistry.authStorage, providerOptions, async (providerOption) => {
|
|
123
123
|
close();
|
|
124
124
|
try {
|
|
125
|
-
this.host.modelRegistry.authStorage.
|
|
125
|
+
this.host.modelRegistry.authStorage.removeVerified(providerOption.id);
|
|
126
126
|
this.host.modelRegistry.refresh();
|
|
127
127
|
await this.host.onAuthChanged?.();
|
|
128
128
|
const message = providerOption.authType === "oauth"
|
|
@@ -267,11 +267,12 @@ export class ProviderAuthFlows {
|
|
|
267
267
|
this.host.ui.requestRender();
|
|
268
268
|
};
|
|
269
269
|
try {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
await this.host.modelRegistry.authStorage.setFromLogin(providerId, async () => {
|
|
271
|
+
const apiKey = (await dialog.showPrompt("Enter API key:", undefined, { masked: true })).trim();
|
|
272
|
+
if (!apiKey)
|
|
273
|
+
throw new Error("API key cannot be empty.");
|
|
274
|
+
return { type: "api_key", key: apiKey };
|
|
275
|
+
}, dialog.signal);
|
|
275
276
|
closeDialog();
|
|
276
277
|
return await this.completeProviderAuthentication(providerId, providerName, "api_key", undefined, kind);
|
|
277
278
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-flows.js","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@ponythewhite/base-context-ai\";\nimport type { OverlayHandle, TUI } from \"@ponythewhite/base-context-tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport { getProviderAuthContract } from \"../../core/provider-contracts.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\" || getProviderAuthContract(model.provider).oauth !== \"supported\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No supported OAuth providers are registered. Use /login to configure an API-key provider.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.logout(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst oauthProviders = authStorage.getOAuthProviders();\n\t\tconst oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t\t// MCP integrations (mcp:<server>) are services, not model providers.\n\t\t\t...(provider.id.startsWith(\"mcp:\") ? { category: \"service\" as const } : {}),\n\t\t}));\n\n\t\tconst modelProviders = new Set(this.host.modelRegistry.getAll().map((model) => model.provider));\n\t\tfor (const providerId of modelProviders) {\n\t\t\tif (!isApiKeyLoginProvider(providerId, oauthProviderIds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname: this.host.modelRegistry.getProviderDisplayName(providerId),\n\t\t\t\tauthType: \"api_key\",\n\t\t\t});\n\t\t}\n\n\t\t// Serper is a skill credential, not a model provider, so add it manually.\n\t\toptions.push({\n\t\t\tid: SERPER_CREDENTIAL_ID,\n\t\t\tname: SERPER_CREDENTIAL_NAME,\n\t\t\tauthType: \"api_key\",\n\t\t\tcategory: \"service\",\n\t\t});\n\n\t\tconst filteredOptions = authType ? options.filter((option) => option.authType === authType) : options;\n\t\treturn filteredOptions.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\")).trim();\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"API key cannot be empty.\");\n\t\t\t}\n\n\t\t\tthis.host.modelRegistry.authStorage.set(providerId, { type: \"api_key\", key: apiKey });\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(\n\t\t\t\tproviderId,\n\t\t\t\tproviderName,\n\t\t\t\t\"oauth\",\n\t\t\t\tkind === \"provider\" ? \"Use /model to select a model\" : undefined,\n\t\t\t\tkind,\n\t\t\t);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-flows.js","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAgD,MAAM,+BAA+B,CAAC;AAE3G,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAGN,4BAA4B,EAC5B,sBAAsB,GACtB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAczC,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAEpD,MAAM,CAAC,MAAM,mCAAmC,GAC/C,uMAAuM,CAAC;AAEzM,SAAS,8BAA8B,CAAC,MAA0B,EAAW;IAC5E,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAAA,CACrE;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACxD,aAA4B,EAC5B,KAAuC,EACT;IAC9B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,IAAI,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QAC/G,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,gBAAgB,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACpE,IAAI,gBAAgB,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;QACxC,OAAO,mCAAmC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxE,IAAI,8BAA8B,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5C,OAAO,mCAAmC,CAAC;QAC5C,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,uDAAuD;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAS,YAAY,EAAE,CAAC,CAAC;AAEjE,MAAM,UAAU,qBAAqB,CACpC,UAAkB,EAClB,gBAAqC,EACrC,kBAAkB,GAAwB,wBAAwB,EACxD;IACV,IAAI,+BAA+B,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAAA,CACzC;AAoBD,8FAA8F;AAC9F,MAAM,OAAO,iBAAiB;IACA,IAAI;IAAjC,YAA6B,IAA2B,EAAE;oBAA7B,IAAI;IAA0B,CAAC;IAE5D;;;;;;OAMG;IACH,WAAW,CAAC,MAAc,EAAE,KAAc,EAAiC;QAC1E,MAAM,UAAU,GAAG,OAAO,MAAM,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QAC1G,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;YAC1D,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAAA,CAC3E;IAED,QAAQ,CAAC,OAAO,GAAyB,EAAE,EAAiC;QAC3E,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;QAC9C,MAAM,eAAe,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CACnB,QAAQ,KAAK,OAAO;gBACnB,CAAC,CAAC,2FAA2F;gBAC7F,CAAC,CAAC,QAAQ,KAAK,SAAS;oBACvB,CAAC,CAAC,iCAAiC;oBACnC,CAAC,CAAC,yBAAyB,CAC7B,CAAC;YACF,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/B,IAAI,MAAiC,CAAC;YACtC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;gBACnB,MAAM,EAAE,IAAI,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;YAAA,CAC7B,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,sBAAsB,CAC1C,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,EACnC,eAAe,EACf,KAAK,EAAE,cAAoC,EAAE,EAAE,CAAC;gBAC/C,KAAK,EAAE,CAAC;gBACR,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC;YAAA,CAClD,EACD,GAAG,EAAE,CAAC;gBACL,KAAK,EAAE,CAAC;gBACR,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAAA,CACjC,EACD,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,UAAU,CAAC,EACzE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,CAC9D,CAAC;YACF,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAAA,CACzD,CAAC,CAAC;IAAA,CACH;IAED,aAAa,CAAC,cAAoC,EAAiC;QAClF,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;QAC5E,IAAI,cAAc,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,cAAc,CAAC,EAAE,KAAK,mBAAmB,EAAE,CAAC;YAC/C,OAAO,IAAI,CAAC,sBAAsB,CAAC,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAAA,CAChF;IAED,SAAS,GAA2B;QACnC,MAAM,eAAe,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACxD,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CACnB,gJAAgJ,CAChJ,CAAC;YACF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/B,IAAI,MAAiC,CAAC;YACtC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;gBACnB,MAAM,EAAE,IAAI,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;YAAA,CAC7B,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,sBAAsB,CAC1C,QAAQ,EACR,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,EACnC,eAAe,EACf,KAAK,EAAE,cAAoC,EAAE,EAAE,CAAC;gBAC/C,KAAK,EAAE,CAAC;gBAER,IAAI,CAAC;oBACJ,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;oBACtE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;oBAClC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;oBAClC,MAAM,OAAO,GACZ,cAAc,CAAC,QAAQ,KAAK,OAAO;wBAClC,CAAC,CAAC,iBAAiB,cAAc,CAAC,IAAI,EAAE;wBACxC,CAAC,CAAC,8BAA8B,cAAc,CAAC,IAAI,+DAA+D,CAAC;oBACrH,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;oBAC9B,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;gBAC5B,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACzB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAChG,OAAO,CAAC,IAAI,CAAC,CAAC;gBACf,CAAC;YAAA,CACD,EACD,GAAG,EAAE,CAAC;gBACL,KAAK,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,CAAC;YAAA,CACd,EACD,SAAS,EACT,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAC;YACF,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAAA,CACzD,CAAC,CAAC;IAAA,CACH;IAED,uBAAuB,CAAC,QAA8B,EAA0B;QAC/E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;QACxD,MAAM,cAAc,GAAG,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAChF,MAAM,OAAO,GAA2B,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACzE,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,QAAQ,EAAE,OAAO;YACjB,qEAAqE;YACrE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E,CAAC,CAAC,CAAC;QAEJ,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChG,KAAK,MAAM,UAAU,IAAI,cAAc,EAAE,CAAC;YACzC,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;gBAC1D,SAAS;YACV,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,sBAAsB,CAAC,UAAU,CAAC;gBAChE,QAAQ,EAAE,SAAS;aACnB,CAAC,CAAC;QACJ,CAAC;QAED,0EAA0E;QAC1E,OAAO,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,oBAAoB;YACxB,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACtG,OAAO,eAAe,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAAA,CAC1D;IAEO,wBAAwB,GAA2B;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;QACxD,MAAM,OAAO,GAA2B,EAAE,CAAC;QAE3C,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1F,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7C,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;gBACjB,SAAS;YACV,CAAC;YACD,MAAM,QAAQ,GAAG,UAAU,KAAK,oBAAoB,CAAC;YACrD,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,IAAI,GAAG,QAAQ;gBACpB,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,KAAK;oBACN,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC/E,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,UAAU;gBACd,IAAI;gBACJ,QAAQ,EAAE,UAAU,CAAC,IAAI;gBACzB,QAAQ,EAAE,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU;aACpD,CAAC,CAAC;QACJ,CAAC;QAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAAA,CAC5D;IAEO,KAAK,CAAC,8BAA8B,CAC3C,UAAkB,EAClB,YAAoB,EACpB,QAA6B,EAC7B,YAAqB,EACrB,IAAI,GAA2B,UAAU,EACzC,cAAc,GAAW,WAAW,EAAE,EACN;QAChC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAElC,MAAM,WAAW,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,YAAY,EAAE,CAAC,CAAC,CAAC,qBAAqB,YAAY,EAAE,CAAC;QAChH,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CACnB,GAAG,WAAW,0BAA0B,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAClG,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC/B,OAAO;YACN,MAAM,EAAE,SAAS;YACjB,UAAU;YACV,YAAY;YACZ,QAAQ;YACR,IAAI;SACJ,CAAC;IAAA,CACF;IAEO,KAAK,CAAC,6BAA6B,CAC1C,UAAkB,EAClB,YAAoB,EACY;QAChC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,YAAY,oEAAoE,CAAC,CAAC;QAC1G,OAAO;YACN,MAAM,EAAE,SAAS;YACjB,UAAU;YACV,YAAY;YACZ,QAAQ,EAAE,SAAS;SACnB,CAAC;IAAA,CACF;IAEO,KAAK,CAAC,0BAA0B,CAAC,UAAkB,EAAoB;QAC9E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACpD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IAAA,CAC7D;IAEO,KAAK,CAAC,sBAAsB,CAAC,UAAkB,EAAE,YAAoB,EAAiC;QAC7G,MAAM,MAAM,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAAC,CAAC,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;QAClH,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;QAAA,CAC7B,CAAC;QAEF,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,gBAAgB,CAAC;gBAC7B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,kEAAkE,CAAC;gBACpF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,8EAA8E,CAAC;gBAChG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;gBACzB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC;aACnE,CAAC,CAAC;YACH,WAAW,EAAE,CAAC;YACd,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,YAAY,qEAAqE,CAAC,CAAC;gBAC3G,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YAChC,CAAC;YACD,OAAO,MAAM,IAAI,CAAC,6BAA6B,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACzB,WAAW,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxE,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,oBAAoB,YAAY,KAAK,QAAQ,EAAE,CAAC,CAAC;gBACrE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC7B,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAChC,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,qBAAqB,CAClC,UAAkB,EAClB,YAAoB,EACpB,IAAI,GAA2B,UAAU,EACT;QAChC,MAAM,MAAM,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QAE5G,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAE7D,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;QAAA,CAC7B,CAAC;QAEF,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,YAAY,CACrD,UAAU,EACV,KAAK,IAAI,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,gBAAgB,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC/F,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBACzD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;YAAA,CACxC,EACD,MAAM,CAAC,MAAM,CACb,CAAC;YAEF,WAAW,EAAE,CAAC;YACd,OAAO,MAAM,IAAI,CAAC,8BAA8B,CAAC,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACxG,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACzB,WAAW,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxE,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,8BAA8B,YAAY,KAAK,QAAQ,EAAE,CAAC,CAAC;gBAC/E,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC7B,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAChC,CAAC;IAAA,CACD;IAEO,oBAAoB,CAAC,YAA2B,EAAE,MAAyB,EAA+B;QACjH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/B,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,cAAyC,CAAC;YAC9C,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC;gBAC3B,cAAc,EAAE,IAAI,EAAE,CAAC;gBACvB,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBAC9B,YAAY,CAAC,KAAK,EAAE,CAAC;gBACrB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;YAAA,CAC7B,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,0BAA0B,CAC9C,MAAM,CAAC,OAAO,EACd,MAAM,EACN,CAAC,WAAW,EAAE,EAAE,CAAC;gBAChB,aAAa,EAAE,CAAC;gBAChB,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YAAA,CAC3E,EACD,GAAG,EAAE,CAAC;gBACL,aAAa,EAAE,CAAC;gBAChB,OAAO,CAAC,SAAS,CAAC,CAAC;YAAA,CACnB,EACD,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAC;YACF,cAAc,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAAA,CACjE,CAAC,CAAC;IAAA,CACH;IAEO,KAAK,CAAC,eAAe,CAC5B,UAAkB,EAClB,YAAoB,EACpB,IAAI,GAA2B,UAAU,EACT;QAChC,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC7B,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW;aACtD,iBAAiB,EAAE;aACnB,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QAEjD,MAAM,kBAAkB,GAAG,YAAY,EAAE,kBAAkB,IAAI,KAAK,CAAC;QAErE,MAAM,MAAM,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QAE5G,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE;YAC9D,eAAe,EAAE,EAAE;YACnB,sBAAsB,EAAE,IAAI;SAC5B,CAAC,CAAC;QAEH,IAAI,iBAAuD,CAAC;QAC5D,IAAI,gBAAoD,CAAC;QACzD,MAAM,iBAAiB,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YAClE,iBAAiB,GAAG,OAAO,CAAC;YAC5B,gBAAgB,GAAG,MAAM,CAAC;QAAA,CAC1B,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;QAAA,CAC7B,CAAC;QAEF,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,UAA6B,EAAE;gBAC9E,MAAM,EAAE,CAAC,IAA4C,EAAE,EAAE,CAAC;oBACzD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;oBAE7C,IAAI,kBAAkB,EAAE,CAAC;wBACxB,MAAM;6BACJ,eAAe,CAAC,yDAAyD,CAAC;6BAC1E,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;4BAChB,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAAC;gCAChC,iBAAiB,CAAC,KAAK,CAAC,CAAC;gCACzB,iBAAiB,GAAG,SAAS,CAAC;4BAC/B,CAAC;wBAAA,CACD,CAAC;6BACD,KAAK,CAAC,GAAG,EAAE,CAAC;4BACZ,IAAI,gBAAgB,EAAE,CAAC;gCACtB,gBAAgB,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;gCAC/C,gBAAgB,GAAG,SAAS,CAAC;4BAC9B,CAAC;wBAAA,CACD,CAAC,CAAC;oBACL,CAAC;yBAAM,IAAI,UAAU,KAAK,gBAAgB,EAAE,CAAC;wBAC5C,MAAM,CAAC,WAAW,CAAC,uCAAuC,CAAC,CAAC;oBAC7D,CAAC;gBAAA,CACD;gBAED,QAAQ,EAAE,KAAK,EAAE,MAAiD,EAAE,EAAE,CAAC;oBACtE,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;gBAAA,CAC7D;gBAED,UAAU,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC;oBAChC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBAAA,CAC7B;gBAED,QAAQ,EAAE,CAAC,MAAyB,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,YAAY,EAAE,MAAM,CAAC;gBAExF,iBAAiB,EAAE,GAAG,EAAE,CAAC,iBAAiB;gBAE1C,MAAM,EAAE,MAAM,CAAC,MAAM;aACrB,CAAC,CAAC;YAEH,WAAW,EAAE,CAAC;YACd,OAAO,MAAM,IAAI,CAAC,8BAA8B,CAC/C,UAAU,EACV,YAAY,EACZ,OAAO,EACP,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,SAAS,EAChE,IAAI,CACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACzB,WAAW,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxE,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,sBAAsB,YAAY,KAAK,QAAQ,EAAE,CAAC,CAAC;gBACvE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC7B,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAChC,CAAC;IAAA,CACD;CACD","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@ponythewhite/base-context-ai\";\nimport type { OverlayHandle, TUI } from \"@ponythewhite/base-context-tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport { getProviderAuthContract } from \"../../core/provider-contracts.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\" || getProviderAuthContract(model.provider).oauth !== \"supported\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No supported OAuth providers are registered. Use /login to configure an API-key provider.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.removeVerified(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst oauthProviders = authStorage.getOAuthProviders();\n\t\tconst oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t\t// MCP integrations (mcp:<server>) are services, not model providers.\n\t\t\t...(provider.id.startsWith(\"mcp:\") ? { category: \"service\" as const } : {}),\n\t\t}));\n\n\t\tconst modelProviders = new Set(this.host.modelRegistry.getAll().map((model) => model.provider));\n\t\tfor (const providerId of modelProviders) {\n\t\t\tif (!isApiKeyLoginProvider(providerId, oauthProviderIds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname: this.host.modelRegistry.getProviderDisplayName(providerId),\n\t\t\t\tauthType: \"api_key\",\n\t\t\t});\n\t\t}\n\n\t\t// Serper is a skill credential, not a model provider, so add it manually.\n\t\toptions.push({\n\t\t\tid: SERPER_CREDENTIAL_ID,\n\t\t\tname: SERPER_CREDENTIAL_NAME,\n\t\t\tauthType: \"api_key\",\n\t\t\tcategory: \"service\",\n\t\t});\n\n\t\tconst filteredOptions = authType ? options.filter((option) => option.authType === authType) : options;\n\t\treturn filteredOptions.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.setFromLogin(\n\t\t\t\tproviderId,\n\t\t\t\tasync () => {\n\t\t\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\", undefined, { masked: true })).trim();\n\t\t\t\t\tif (!apiKey) throw new Error(\"API key cannot be empty.\");\n\t\t\t\t\treturn { type: \"api_key\", key: apiKey };\n\t\t\t\t},\n\t\t\t\tdialog.signal,\n\t\t\t);\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst contract = getProviderAuthContract(providerId);\n\t\tif (contract.oauth !== \"supported\") {\n\t\t\tthis.host.showError(contract.guidance);\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(\n\t\t\t\tproviderId,\n\t\t\t\tproviderName,\n\t\t\t\t\"oauth\",\n\t\t\t\tkind === \"provider\" ? \"Use /model to select a model\" : undefined,\n\t\t\t\tkind,\n\t\t\t);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-announcement.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/brand-announcement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,MAAM,gCAAgC,CAAC;AAKzE,qBAAa,0BAA2B,SAAQ,SAAS;IACxD,cAMC;CACD","sourcesContent":["import { Container, Spacer, Text } from \"@ponythewhite/base-context-tui\";\nimport { SYNERISE_LOGO } from \"../../../themes/synerise-logo.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\nexport class BrandAnnouncementComponent extends Container {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.addChild(new DynamicBorder((text) => theme.fg(\"accent\", text)));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SYNERISE_LOGO), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((text) => theme.fg(\"accent\", text)));\n\t}\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Container, Spacer, Text } from "@ponythewhite/base-context-tui";
|
|
2
|
+
import { SYNERISE_LOGO } from "../../../themes/synerise-logo.js";
|
|
3
|
+
import { theme } from "../theme/theme.js";
|
|
4
|
+
import { DynamicBorder } from "./dynamic-border.js";
|
|
5
|
+
export class BrandAnnouncementComponent extends Container {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.addChild(new DynamicBorder((text) => theme.fg("accent", text)));
|
|
9
|
+
this.addChild(new Text(theme.fg("accent", SYNERISE_LOGO), 1, 0));
|
|
10
|
+
this.addChild(new Spacer(1));
|
|
11
|
+
this.addChild(new DynamicBorder((text) => theme.fg("accent", text)));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=brand-announcement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-announcement.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/brand-announcement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,OAAO,0BAA2B,SAAQ,SAAS;IACxD,cAAc;QACb,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACrE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAAA,CACrE;CACD","sourcesContent":["import { Container, Spacer, Text } from \"@ponythewhite/base-context-tui\";\nimport { SYNERISE_LOGO } from \"../../../themes/synerise-logo.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\nexport class BrandAnnouncementComponent extends Container {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.addChild(new DynamicBorder((text) => theme.fg(\"accent\", text)));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SYNERISE_LOGO), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((text) => theme.fg(\"accent\", text)));\n\t}\n}\n"]}
|
|
@@ -37,7 +37,9 @@ export declare class LoginDialogComponent extends Container implements Focusable
|
|
|
37
37
|
* Called by onPrompt callback - show prompt and wait for input
|
|
38
38
|
* Note: Does NOT clear content, appends to existing (preserves URL from showAuth)
|
|
39
39
|
*/
|
|
40
|
-
showPrompt(message: string, placeholder?: string
|
|
40
|
+
showPrompt(message: string, placeholder?: string, options?: {
|
|
41
|
+
masked?: boolean;
|
|
42
|
+
}): Promise<string>;
|
|
41
43
|
/**
|
|
42
44
|
* Show informational text without prompting for input.
|
|
43
45
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAKd,KAAK,GAAG,EACR,MAAM,gCAAgC,CAAC;AAkBxC;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IA6BtE,OAAO,CAAC,UAAU;IA5BnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAI/C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,gBAAgB,CAAC,CAAyB;IAClD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAAO;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,EAChE,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,MAAM,EA4BtB;IAED,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAed;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CA+BjD;IAED;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW/C;IAED;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAK9B;IAED;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBjE;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ9B;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/C;IAED;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKjC;IAED;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAOlC;IAED,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,kBAAkB;YAqBZ,WAAW;IAmBzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+B9B;CACD","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAKd,KAAK,GAAG,EACR,MAAM,gCAAgC,CAAC;AAkBxC;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IA6BtE,OAAO,CAAC,UAAU;IA5BnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAI/C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,gBAAgB,CAAC,CAAyB;IAClD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAAO;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,EAChE,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,MAAM,EA4BtB;IAED,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAed;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CA+BjD;IAED;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY/C;IAED;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAK9B;IAED;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBjG;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ9B;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/C;IAED;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKjC;IAED;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAOlC;IAED,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,kBAAkB;YAqBZ,WAAW;IAmBzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+B9B;CACD","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.input.setMasked(true);\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string, options?: { masked?: boolean }): Promise<string> {\n\t\tthis.input.setMasked(options?.masked === true);\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -120,6 +120,7 @@ export class LoginDialogComponent extends Container {
|
|
|
120
120
|
* Show input for manual code/URL entry (for callback server providers)
|
|
121
121
|
*/
|
|
122
122
|
showManualInput(prompt) {
|
|
123
|
+
this.input.setMasked(true);
|
|
123
124
|
this.addSectionSpacer();
|
|
124
125
|
this.addSectionTitle("Manual fallback");
|
|
125
126
|
this.addMutedText(prompt);
|
|
@@ -143,7 +144,8 @@ export class LoginDialogComponent extends Container {
|
|
|
143
144
|
* Called by onPrompt callback - show prompt and wait for input
|
|
144
145
|
* Note: Does NOT clear content, appends to existing (preserves URL from showAuth)
|
|
145
146
|
*/
|
|
146
|
-
showPrompt(message, placeholder) {
|
|
147
|
+
showPrompt(message, placeholder, options) {
|
|
148
|
+
this.input.setMasked(options?.masked === true);
|
|
147
149
|
this.addSectionSpacer();
|
|
148
150
|
this.addSectionTitle(message);
|
|
149
151
|
if (placeholder) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EACN,SAAS,EAET,eAAe,EACf,cAAc,EACd,MAAM,EACN,IAAI,GAEJ,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,SAAS,qBAAqB,CAAC,GAAW,EAAW;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC;AAAA,CAC3G;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAW;IAChD,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,MAAM,CAAC;AAAA,CAC3D;AAED;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,SAAS;IA6BzC,UAAU;IA5BX,gBAAgB,CAAY;IAC5B,KAAK,CAAkB;IACvB,GAAG,CAAM;IACT,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IACxC,aAAa,CAA2B;IACxC,aAAa,CAA0B;IAC/C,2EAA2E;IAC3E,0EAA0E;IAC1E,oEAAoE;IAC5D,YAAY,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAc;IAC9B,gBAAgB,CAA0B;IAC1C,OAAO,CAAU;IACjB,WAAW,CAAQ;IAE3B,2EAA2E;IACnE,QAAQ,GAAG,KAAK,CAAC;IACzB,IAAI,OAAO,GAAY;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC;IAAA,CACrB;IACD,IAAI,OAAO,CAAC,KAAc,EAAE;QAC3B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;IAAA,CAC3B;IAED,YACC,GAAQ,EACR,UAAkB,EACV,UAAwD,EAChE,oBAA6B,EAC7B,aAAsB,EACrB;QACD,KAAK,EAAE,CAAC;0BAJA,UAAU;QAKlB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,MAAM,YAAY,GAAG,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QAC1E,MAAM,YAAY,GAAG,oBAAoB,IAAI,YAAY,EAAE,IAAI,IAAI,UAAU,CAAC;QAC9E,MAAM,KAAK,GAAG,aAAa,IAAI,YAAY,YAAY,EAAE,CAAC;QAE1D,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC;YAC3B,KAAK;YACL,QAAQ,EAAE,uCAAuC;SACjD,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAErB,uBAAuB;QACvB,IAAI,CAAC,gBAAgB,GAAG,IAAI,SAAS,EAAE,CAAC;QACxC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAEtC,2CAA2C;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,eAAe,CAAC,aAAa,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC1C,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;gBAC/B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAChC,CAAC;QAAA,CACD,CAAC;IAAA,CACF;IAED,IAAI,MAAM,GAAgB;QACzB,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;IAAA,CACnC;IAEO,MAAM,GAAS;QACtB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAChC,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,IAAI,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACpD,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAClC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAAA,CAC1C;IAED;;OAEG;IACH,QAAQ,CAAC,GAAW,EAAE,YAAqB,EAAQ;QAClD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACnB,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,qFAAqF,CAAC,CAAC;QACzG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9F,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5E,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEjD,IAAI,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QACpC,CAAC;QAED,sBAAsB;QACtB,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GACvB,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC5B,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC;YACf,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC7B,CAAC,CAAC;oBACA,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,aAAa,EAAE,UAAU,EAAE,cAAc,CAAC;oBAC/E,6BAA6B;oBAC7B,GAAG;iBACH;gBACF,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;QAElC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAAA,CACzB;IAED;;OAEG;IACH,eAAe,CAAC,MAAc,EAAmB;QAChD,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAAA,CAC3B;IAED;;OAEG;IACH,YAAY,GAAoB;QAC/B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;YAC7B,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QAAA,CAC5B,CAAC,CAAC;IAAA,CACH;IAED;;;OAGG;IACH,UAAU,CAAC,OAAe,EAAE,WAAoB,EAAE,OAA8B,EAAmB;QAClG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,WAAW,EAAE,CAAC;YACjB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAC7B,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,QAAQ,CAAC,KAAK,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,EAAE,CAAC,EAC1G,CAAC,CACD,CACD,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAAA,CAC3B;IAED;;OAEG;IACH,QAAQ,CAAC,KAAe,EAAQ;QAC/B,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzG,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAAA,CACzB;IAED,gBAAgB,CAAC,KAAe,EAAiB;QAChD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAC7B,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CACP,OAAO,EACP,GAAG,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,KAAK,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,EAAE,CACzF,EACD,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;YAChC,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;QAAA,CAC/B,CAAC,CAAC;IAAA,CACH;IAED;;OAEG;IACH,WAAW,CAAC,OAAe,EAAQ;QAClC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5E,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAAA,CACzB;IAED;;OAEG;IACH,YAAY,CAAC,OAAe,EAAQ;QACnC,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,eAAe,CAAC,0BAA0B,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAAA,CACzB;IAEO,YAAY,GAAS;QAC5B,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,qDAAqD;QACrD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CAC9C;IAEO,gBAAgB,GAAS;QAChC,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CAC9C;IAEO,eAAe,CAAC,YAAoB,EAAQ;QACnD,MAAM,SAAS,GAAG,gCAAgC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;YACnC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3F,OAAO;QACR,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC3B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAAA,CAC/E;IAEO,eAAe,CAAC,IAAY,EAAQ;QAC3C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAAA,CACnF;IAEO,QAAQ,CAAC,IAAY,EAAQ;QACpC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAAA,CACxE;IAEO,YAAY,CAAC,IAAY,EAAQ;QACxC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAAA,CACxE;IAEO,kBAAkB,CAAC,MAA4B,EAAU;QAChE,MAAM,kBAAkB,GAAG,cAAc,EAAE,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY;YACjC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;YACjE,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClC,MAAM,QAAQ,GACb,QAAQ,CAAC,MAAM,GAAG,CAAC;YAClB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAChE,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,UAAU,GACf,MAAM,KAAK,QAAQ;YAClB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,CAAC;YAC5C,CAAC,CAAC,MAAM,KAAK,QAAQ;gBACpB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,6BAA6B,CAAC;gBAClD,CAAC,CAAC,SAAS,CAAC;QACf,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;aACnE,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC;aACpD,IAAI,CAAC,IAAI,CAAC,CAAC;IAAA,CACb;IAEO,KAAK,CAAC,WAAW,GAAkB;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;QACjC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO;YAAE,OAAO;QAE7B,IAAI,CAAC;YACJ,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;gBAC1D,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACnD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YAC1B,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;gBAC1D,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACnD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YAC1B,CAAC;QACF,CAAC;IAAA,CACD;IAED,WAAW,CAAC,IAAY,EAAQ;QAC/B,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;QAE5B,IACC,IAAI,CAAC,OAAO;YACZ,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,4BAA4B,CAAC;YAC9C,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAC9C,CAAC;YACF,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YACxB,OAAO;QACR,CAAC;QAED,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;QAC5E,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,IAAI,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC;YACtF,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QAED,IAAI,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC,EAAE,CAAC;YACrE,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC;YACtC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAClC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAClC,OAAO,EAAE,CAAC;YACV,OAAO;QACR,CAAC;QAED,gBAAgB;QAChB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAAA,CAC7B;CACD","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.input.setMasked(true);\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string, options?: { masked?: boolean }): Promise<string> {\n\t\tthis.input.setMasked(options?.masked === true);\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -47,6 +47,7 @@ export declare class MenuSearchInput implements Component, Focusable, FullWidthM
|
|
|
47
47
|
getValue(): string;
|
|
48
48
|
getCursor(): number;
|
|
49
49
|
setValue(value: string): void;
|
|
50
|
+
setMasked(masked: boolean): void;
|
|
50
51
|
handleInput(data: string): void;
|
|
51
52
|
invalidate(): void;
|
|
52
53
|
render(width: number): string[];
|