@mariozechner/pi-coding-agent 0.27.9 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/README.md +17 -18
  3. package/dist/cli/list-models.d.ts +2 -2
  4. package/dist/cli/list-models.d.ts.map +1 -1
  5. package/dist/cli/list-models.js +2 -7
  6. package/dist/cli/list-models.js.map +1 -1
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +3 -3
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +6 -3
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +23 -25
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/auth-storage.d.ts +104 -0
  16. package/dist/core/auth-storage.d.ts.map +1 -0
  17. package/dist/core/auth-storage.js +232 -0
  18. package/dist/core/auth-storage.js.map +1 -0
  19. package/dist/core/custom-tools/types.d.ts +2 -2
  20. package/dist/core/custom-tools/types.d.ts.map +1 -1
  21. package/dist/core/custom-tools/types.js.map +1 -1
  22. package/dist/core/hooks/types.d.ts +3 -3
  23. package/dist/core/hooks/types.d.ts.map +1 -1
  24. package/dist/core/hooks/types.js.map +1 -1
  25. package/dist/core/model-registry.d.ts +50 -0
  26. package/dist/core/model-registry.d.ts.map +1 -0
  27. package/dist/core/model-registry.js +268 -0
  28. package/dist/core/model-registry.js.map +1 -0
  29. package/dist/core/model-resolver.d.ts +7 -7
  30. package/dist/core/model-resolver.d.ts.map +1 -1
  31. package/dist/core/model-resolver.js +12 -44
  32. package/dist/core/model-resolver.js.map +1 -1
  33. package/dist/core/sdk.d.ts +13 -26
  34. package/dist/core/sdk.d.ts.map +1 -1
  35. package/dist/core/sdk.js +24 -101
  36. package/dist/core/sdk.js.map +1 -1
  37. package/dist/core/settings-manager.d.ts +0 -5
  38. package/dist/core/settings-manager.d.ts.map +1 -1
  39. package/dist/core/settings-manager.js +0 -19
  40. package/dist/core/settings-manager.js.map +1 -1
  41. package/dist/core/skills.d.ts.map +1 -1
  42. package/dist/core/skills.js +15 -1
  43. package/dist/core/skills.js.map +1 -1
  44. package/dist/index.d.ts +3 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -8
  47. package/dist/index.js.map +1 -1
  48. package/dist/main.d.ts.map +1 -1
  49. package/dist/main.js +37 -22
  50. package/dist/main.js.map +1 -1
  51. package/dist/modes/interactive/components/footer.d.ts +3 -1
  52. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/footer.js +4 -3
  54. package/dist/modes/interactive/components/footer.js.map +1 -1
  55. package/dist/modes/interactive/components/model-selector.d.ts +3 -1
  56. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/model-selector.js +21 -14
  58. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  59. package/dist/modes/interactive/components/oauth-selector.d.ts +3 -1
  60. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  61. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  62. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  63. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  64. package/dist/modes/interactive/interactive-mode.js +56 -51
  65. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  66. package/docs/custom-tools.md +3 -3
  67. package/docs/hooks.md +9 -9
  68. package/docs/sdk.md +86 -61
  69. package/examples/custom-tools/hello/index.ts +15 -15
  70. package/examples/custom-tools/question/index.ts +3 -3
  71. package/examples/custom-tools/subagent/agents.ts +1 -2
  72. package/examples/custom-tools/subagent/index.ts +332 -125
  73. package/examples/custom-tools/todo/index.ts +30 -12
  74. package/examples/hooks/confirm-destructive.ts +6 -8
  75. package/examples/hooks/custom-compaction.ts +7 -7
  76. package/examples/hooks/dirty-repo-guard.ts +7 -15
  77. package/examples/hooks/permission-gate.ts +1 -5
  78. package/examples/sdk/02-custom-model.ts +20 -7
  79. package/examples/sdk/04-skills.ts +1 -1
  80. package/examples/sdk/05-tools.ts +11 -14
  81. package/examples/sdk/06-hooks.ts +1 -1
  82. package/examples/sdk/07-context-files.ts +1 -1
  83. package/examples/sdk/08-slash-commands.ts +3 -3
  84. package/examples/sdk/09-api-keys-and-oauth.ts +36 -26
  85. package/examples/sdk/10-settings.ts +2 -2
  86. package/examples/sdk/12-full-control.ts +19 -20
  87. package/examples/sdk/README.md +26 -13
  88. package/package.json +4 -5
  89. package/dist/core/model-config.d.ts +0 -58
  90. package/dist/core/model-config.d.ts.map +0 -1
  91. package/dist/core/model-config.js +0 -384
  92. package/dist/core/model-config.js.map +0 -1
  93. package/dist/core/oauth/index.d.ts +0 -41
  94. package/dist/core/oauth/index.d.ts.map +0 -1
  95. package/dist/core/oauth/index.js +0 -84
  96. package/dist/core/oauth/index.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-coding-agent",
3
- "version": "0.27.9",
3
+ "version": "0.29.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -34,14 +34,13 @@
34
34
  "build:binary": "npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
35
35
  "copy-assets": "mkdir -p dist/modes/interactive/theme && cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/",
36
36
  "copy-binary-assets": "cp package.json dist/ && cp README.md dist/ && cp CHANGELOG.md dist/ && mkdir -p dist/theme && cp src/modes/interactive/theme/*.json dist/theme/ && cp -r docs dist/ && cp -r examples dist/",
37
- "check": "tsgo --noEmit && tsgo -p tsconfig.examples.json",
38
37
  "test": "vitest --run",
39
38
  "prepublishOnly": "npm run clean && npm run build"
40
39
  },
41
40
  "dependencies": {
42
- "@mariozechner/pi-agent-core": "^0.27.9",
43
- "@mariozechner/pi-ai": "^0.27.9",
44
- "@mariozechner/pi-tui": "^0.27.9",
41
+ "@mariozechner/pi-agent-core": "^0.29.0",
42
+ "@mariozechner/pi-ai": "^0.29.0",
43
+ "@mariozechner/pi-tui": "^0.29.0",
45
44
  "chalk": "^5.5.0",
46
45
  "cli-highlight": "^2.1.11",
47
46
  "diff": "^8.0.2",
@@ -1,58 +0,0 @@
1
- import { type Api, type Model } from "@mariozechner/pi-ai";
2
- /**
3
- * Resolve an API key config value to an actual key.
4
- * First checks if it's an environment variable, then treats as literal.
5
- */
6
- export declare function resolveApiKey(keyConfig: string): string | undefined;
7
- /**
8
- * Get all models (built-in + custom), freshly loaded
9
- * Returns { models, error } - either models array or error message
10
- */
11
- export declare function loadAndMergeModels(agentDir?: string): {
12
- models: Model<Api>[];
13
- error: string | null;
14
- };
15
- /**
16
- * Get API key for a model (checks custom providers first, then built-in)
17
- * Now async to support OAuth token refresh.
18
- * Note: OAuth storage location is configured globally via setOAuthStorage.
19
- */
20
- export declare function getApiKeyForModel(model: Model<Api>): Promise<string | undefined>;
21
- /**
22
- * Get only models that have valid API keys available
23
- * Returns { models, error } - either models array or error message
24
- *
25
- * @param agentDir - Agent config directory
26
- * @param fallbackKeyResolver - Optional function to check for API keys not found by getApiKeyForModel
27
- * (e.g., keys from settings.json)
28
- */
29
- export declare function getAvailableModels(agentDir?: string, fallbackKeyResolver?: (provider: string) => string | undefined): Promise<{
30
- models: Model<Api>[];
31
- error: string | null;
32
- }>;
33
- /**
34
- * Find a specific model by provider and ID.
35
- *
36
- * Searches models from:
37
- * 1. Built-in models from @mariozechner/pi-ai
38
- * 2. Custom models defined in ~/.pi/agent/models.json
39
- *
40
- * Returns { model, error } - either the model or an error message.
41
- */
42
- export declare function findModel(provider: string, modelId: string, agentDir?: string): {
43
- model: Model<Api> | null;
44
- error: string | null;
45
- };
46
- /**
47
- * Invalidate the OAuth status cache.
48
- * Call this after login/logout operations.
49
- */
50
- export declare function invalidateOAuthCache(): void;
51
- /**
52
- * Check if a model is using OAuth credentials (subscription).
53
- * This checks if OAuth credentials exist and would be used for the model,
54
- * without actually fetching or refreshing the token.
55
- * Results are cached until invalidateOAuthCache() is called.
56
- */
57
- export declare function isModelUsingOAuth(model: Model<Api>): boolean;
58
- //# sourceMappingURL=model-config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAOR,KAAK,KAAK,EAKV,MAAM,qBAAqB,CAAC;AAsE7B;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOnE;AAyID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,MAAsB,GAAG;IAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA+BnH;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAkFtF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,GAAE,MAAsB,EAChC,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAC5D,OAAO,CAAC;IAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAoBzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAsB,GAC9B;IAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CASpD;AAgBD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAyB5D","sourcesContent":["import {\n\ttype Api,\n\tgetApiKey,\n\tgetGitHubCopilotBaseUrl,\n\tgetModels,\n\tgetProviders,\n\ttype KnownProvider,\n\tloadOAuthCredentials,\n\ttype Model,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n\tremoveOAuthCredentials,\n\tsaveOAuthCredentials,\n} from \"@mariozechner/pi-ai\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport AjvModule from \"ajv\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\nimport { getOAuthToken, type OAuthProvider, refreshToken } from \"./oauth/index.js\";\n\n// Handle both default and named exports\nconst Ajv = (AjvModule as any).default || AjvModule;\n\n// Schema for OpenAI compatibility settings\nconst OpenAICompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n});\n\n// Schema for custom model definition\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.String({ minLength: 1 }),\n\tapi: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai-completions\"),\n\t\t\tType.Literal(\"openai-responses\"),\n\t\t\tType.Literal(\"anthropic-messages\"),\n\t\t\tType.Literal(\"google-generative-ai\"),\n\t\t]),\n\t),\n\treasoning: Type.Boolean(),\n\tinput: Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")])),\n\tcost: Type.Object({\n\t\tinput: Type.Number(),\n\t\toutput: Type.Number(),\n\t\tcacheRead: Type.Number(),\n\t\tcacheWrite: Type.Number(),\n\t}),\n\tcontextWindow: Type.Number(),\n\tmaxTokens: Type.Number(),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(OpenAICompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tbaseUrl: Type.String({ minLength: 1 }),\n\tapiKey: Type.String({ minLength: 1 }),\n\tapi: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai-completions\"),\n\t\t\tType.Literal(\"openai-responses\"),\n\t\t\tType.Literal(\"anthropic-messages\"),\n\t\t\tType.Literal(\"google-generative-ai\"),\n\t\t]),\n\t),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Array(ModelDefinitionSchema),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\n\ntype ModelsConfig = Static<typeof ModelsConfigSchema>;\n\n// Custom provider API key mappings (provider name -> apiKey config)\nconst customProviderApiKeys: Map<string, string> = new Map();\n\n/**\n * Resolve an API key config value to an actual key.\n * First checks if it's an environment variable, then treats as literal.\n */\nexport function resolveApiKey(keyConfig: string): string | undefined {\n\t// First check if it's an env var name\n\tconst envValue = process.env[keyConfig];\n\tif (envValue) return envValue;\n\n\t// Otherwise treat as literal API key\n\treturn keyConfig;\n}\n\n/**\n * Load custom models from models.json in agent config dir\n * Returns { models, error } - either models array or error message\n */\nfunction loadCustomModels(agentDir: string = getAgentDir()): { models: Model<Api>[]; error: string | null } {\n\tconst configPath = join(agentDir, \"models.json\");\n\tif (!existsSync(configPath)) {\n\t\treturn { models: [], error: null };\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(configPath, \"utf-8\");\n\t\tconst config: ModelsConfig = JSON.parse(content);\n\n\t\t// Validate schema\n\t\tconst ajv = new Ajv();\n\t\tconst validate = ajv.compile(ModelsConfigSchema);\n\t\tif (!validate(config)) {\n\t\t\tconst errors =\n\t\t\t\tvalidate.errors?.map((e: any) => ` - ${e.instancePath || \"root\"}: ${e.message}`).join(\"\\n\") ||\n\t\t\t\t\"Unknown schema error\";\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Invalid models.json schema:\\n${errors}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\n\t\t// Additional validation\n\t\ttry {\n\t\t\tvalidateConfig(config);\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Invalid models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\n\t\t// Parse models\n\t\treturn { models: parseModels(config), error: null };\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Failed to parse models.json: ${error.message}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tmodels: [],\n\t\t\terror: `Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${configPath}`,\n\t\t};\n\t}\n}\n\n/**\n * Validate config structure and requirements\n */\nfunction validateConfig(config: ModelsConfig): void {\n\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\tconst hasProviderApi = !!providerConfig.api;\n\n\t\tfor (const modelDef of providerConfig.models) {\n\t\t\tconst hasModelApi = !!modelDef.api;\n\n\t\t\tif (!hasProviderApi && !hasModelApi) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Provider ${providerName}, model ${modelDef.id}: no \"api\" specified. ` +\n\t\t\t\t\t\t`Set at provider or model level.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Validate required fields\n\t\t\tif (!modelDef.id) throw new Error(`Provider ${providerName}: model missing \"id\"`);\n\t\t\tif (!modelDef.name) throw new Error(`Provider ${providerName}: model missing \"name\"`);\n\t\t\tif (modelDef.contextWindow <= 0)\n\t\t\t\tthrow new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);\n\t\t\tif (modelDef.maxTokens <= 0)\n\t\t\t\tthrow new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);\n\t\t}\n\t}\n}\n\n/**\n * Parse config into Model objects\n */\nfunction parseModels(config: ModelsConfig): Model<Api>[] {\n\tconst models: Model<Api>[] = [];\n\n\t// Clear and rebuild custom provider API key mappings\n\tcustomProviderApiKeys.clear();\n\n\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t// Store API key config for this provider\n\t\tcustomProviderApiKeys.set(providerName, providerConfig.apiKey);\n\n\t\tfor (const modelDef of providerConfig.models) {\n\t\t\t// Model-level api overrides provider-level api\n\t\t\tconst api = modelDef.api || providerConfig.api;\n\n\t\t\tif (!api) {\n\t\t\t\t// This should have been caught by validateConfig, but be safe\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Merge headers: provider headers are base, model headers override\n\t\t\tlet headers =\n\t\t\t\tproviderConfig.headers || modelDef.headers ? { ...providerConfig.headers, ...modelDef.headers } : undefined;\n\n\t\t\t// If authHeader is true, add Authorization header with resolved API key\n\t\t\tif (providerConfig.authHeader) {\n\t\t\t\tconst resolvedKey = resolveApiKey(providerConfig.apiKey);\n\t\t\t\tif (resolvedKey) {\n\t\t\t\t\theaders = { ...headers, Authorization: `Bearer ${resolvedKey}` };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmodels.push({\n\t\t\t\tid: modelDef.id,\n\t\t\t\tname: modelDef.name,\n\t\t\t\tapi: api as Api,\n\t\t\t\tprovider: providerName,\n\t\t\t\tbaseUrl: providerConfig.baseUrl,\n\t\t\t\treasoning: modelDef.reasoning,\n\t\t\t\tinput: modelDef.input as (\"text\" | \"image\")[],\n\t\t\t\tcost: modelDef.cost,\n\t\t\t\tcontextWindow: modelDef.contextWindow,\n\t\t\t\tmaxTokens: modelDef.maxTokens,\n\t\t\t\theaders,\n\t\t\t\tcompat: modelDef.compat,\n\t\t\t} as Model<Api>);\n\t\t}\n\t}\n\n\treturn models;\n}\n\n/**\n * Get all models (built-in + custom), freshly loaded\n * Returns { models, error } - either models array or error message\n */\nexport function loadAndMergeModels(agentDir: string = getAgentDir()): { models: Model<Api>[]; error: string | null } {\n\tconst builtInModels: Model<Api>[] = [];\n\tconst providers = getProviders();\n\n\t// Load all built-in models\n\tfor (const provider of providers) {\n\t\tconst providerModels = getModels(provider as KnownProvider);\n\t\tbuiltInModels.push(...(providerModels as Model<Api>[]));\n\t}\n\n\t// Load custom models\n\tconst { models: customModels, error } = loadCustomModels(agentDir);\n\n\tif (error) {\n\t\treturn { models: [], error };\n\t}\n\n\tconst combined = [...builtInModels, ...customModels];\n\n\t// Update github-copilot base URL based on OAuth token or enterprise domain\n\tconst copilotCreds = loadOAuthCredentials(\"github-copilot\");\n\tif (copilotCreds) {\n\t\tconst domain = copilotCreds.enterpriseUrl ? normalizeDomain(copilotCreds.enterpriseUrl) : undefined;\n\t\tconst baseUrl = getGitHubCopilotBaseUrl(copilotCreds.access, domain ?? undefined);\n\t\treturn {\n\t\t\tmodels: combined.map((m) => (m.provider === \"github-copilot\" ? { ...m, baseUrl } : m)),\n\t\t\terror: null,\n\t\t};\n\t}\n\n\treturn { models: combined, error: null };\n}\n\n/**\n * Get API key for a model (checks custom providers first, then built-in)\n * Now async to support OAuth token refresh.\n * Note: OAuth storage location is configured globally via setOAuthStorage.\n */\nexport async function getApiKeyForModel(model: Model<Api>): Promise<string | undefined> {\n\t// For custom providers, check their apiKey config\n\tconst customKeyConfig = customProviderApiKeys.get(model.provider);\n\tif (customKeyConfig) {\n\t\treturn resolveApiKey(customKeyConfig);\n\t}\n\n\t// For Anthropic, check OAuth first\n\tif (model.provider === \"anthropic\") {\n\t\t// 1. Check OAuth storage (auto-refresh if needed)\n\t\tconst oauthToken = await getOAuthToken(\"anthropic\");\n\t\tif (oauthToken) {\n\t\t\treturn oauthToken;\n\t\t}\n\n\t\t// 2. Check ANTHROPIC_OAUTH_TOKEN env var (manual OAuth token)\n\t\tconst oauthEnv = process.env.ANTHROPIC_OAUTH_TOKEN;\n\t\tif (oauthEnv) {\n\t\t\treturn oauthEnv;\n\t\t}\n\n\t\t// 3. Fall back to ANTHROPIC_API_KEY env var\n\t}\n\n\tif (model.provider === \"github-copilot\") {\n\t\t// 1. Check OAuth storage (from device flow login)\n\t\tconst oauthToken = await getOAuthToken(\"github-copilot\");\n\t\tif (oauthToken) {\n\t\t\treturn oauthToken;\n\t\t}\n\n\t\t// 2. Use GitHub token directly (works with copilot scope on github.com)\n\t\tconst githubToken = process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;\n\t\tif (!githubToken) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// 3. For enterprise, exchange token for short-lived Copilot token\n\t\tconst enterpriseDomain = process.env.COPILOT_ENTERPRISE_URL\n\t\t\t? normalizeDomain(process.env.COPILOT_ENTERPRISE_URL)\n\t\t\t: undefined;\n\n\t\tif (enterpriseDomain) {\n\t\t\tconst creds = await refreshGitHubCopilotToken(githubToken, enterpriseDomain);\n\t\t\tsaveOAuthCredentials(\"github-copilot\", creds);\n\t\t\treturn creds.access;\n\t\t}\n\n\t\t// 4. For github.com, use token directly\n\t\treturn githubToken;\n\t}\n\n\t// For Google Gemini CLI and Antigravity, check OAuth and encode projectId with token\n\tif (model.provider === \"google-gemini-cli\" || model.provider === \"google-antigravity\") {\n\t\tconst oauthProvider = model.provider as \"google-gemini-cli\" | \"google-antigravity\";\n\t\tconst credentials = loadOAuthCredentials(oauthProvider);\n\t\tif (!credentials) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Check if token is expired\n\t\tif (Date.now() >= credentials.expires) {\n\t\t\ttry {\n\t\t\t\tawait refreshToken(oauthProvider);\n\t\t\t\tconst refreshedCreds = loadOAuthCredentials(oauthProvider);\n\t\t\t\tif (refreshedCreds?.projectId) {\n\t\t\t\t\treturn JSON.stringify({ token: refreshedCreds.access, projectId: refreshedCreds.projectId });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tremoveOAuthCredentials(oauthProvider);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tif (credentials.projectId) {\n\t\t\treturn JSON.stringify({ token: credentials.access, projectId: credentials.projectId });\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// For built-in providers, use getApiKey from @mariozechner/pi-ai\n\treturn getApiKey(model.provider as KnownProvider);\n}\n\n/**\n * Get only models that have valid API keys available\n * Returns { models, error } - either models array or error message\n *\n * @param agentDir - Agent config directory\n * @param fallbackKeyResolver - Optional function to check for API keys not found by getApiKeyForModel\n * (e.g., keys from settings.json)\n */\nexport async function getAvailableModels(\n\tagentDir: string = getAgentDir(),\n\tfallbackKeyResolver?: (provider: string) => string | undefined,\n): Promise<{ models: Model<Api>[]; error: string | null }> {\n\tconst { models: allModels, error } = loadAndMergeModels(agentDir);\n\n\tif (error) {\n\t\treturn { models: [], error };\n\t}\n\n\tconst availableModels: Model<Api>[] = [];\n\tfor (const model of allModels) {\n\t\tlet apiKey = await getApiKeyForModel(model);\n\t\t// Check fallback resolver if primary lookup failed\n\t\tif (!apiKey && fallbackKeyResolver) {\n\t\t\tapiKey = fallbackKeyResolver(model.provider);\n\t\t}\n\t\tif (apiKey) {\n\t\t\tavailableModels.push(model);\n\t\t}\n\t}\n\n\treturn { models: availableModels, error: null };\n}\n\n/**\n * Find a specific model by provider and ID.\n *\n * Searches models from:\n * 1. Built-in models from @mariozechner/pi-ai\n * 2. Custom models defined in ~/.pi/agent/models.json\n *\n * Returns { model, error } - either the model or an error message.\n */\nexport function findModel(\n\tprovider: string,\n\tmodelId: string,\n\tagentDir: string = getAgentDir(),\n): { model: Model<Api> | null; error: string | null } {\n\tconst { models: allModels, error } = loadAndMergeModels(agentDir);\n\n\tif (error) {\n\t\treturn { model: null, error };\n\t}\n\n\tconst model = allModels.find((m) => m.provider === provider && m.id === modelId) || null;\n\treturn { model, error: null };\n}\n\n/**\n * Mapping from model provider to OAuth provider ID.\n * Only providers that support OAuth are listed here.\n */\nconst providerToOAuthProvider: Record<string, OAuthProvider> = {\n\tanthropic: \"anthropic\",\n\t\"github-copilot\": \"github-copilot\",\n\t\"google-gemini-cli\": \"google-gemini-cli\",\n\t\"google-antigravity\": \"google-antigravity\",\n};\n\n// Cache for OAuth status per provider (avoids file reads on every render)\nconst oauthStatusCache: Map<string, boolean> = new Map();\n\n/**\n * Invalidate the OAuth status cache.\n * Call this after login/logout operations.\n */\nexport function invalidateOAuthCache(): void {\n\toauthStatusCache.clear();\n}\n\n/**\n * Check if a model is using OAuth credentials (subscription).\n * This checks if OAuth credentials exist and would be used for the model,\n * without actually fetching or refreshing the token.\n * Results are cached until invalidateOAuthCache() is called.\n */\nexport function isModelUsingOAuth(model: Model<Api>): boolean {\n\tconst oauthProvider = providerToOAuthProvider[model.provider];\n\tif (!oauthProvider) {\n\t\treturn false;\n\t}\n\n\t// Check cache first\n\tif (oauthStatusCache.has(oauthProvider)) {\n\t\treturn oauthStatusCache.get(oauthProvider)!;\n\t}\n\n\t// Check if OAuth credentials exist for this provider\n\tlet usingOAuth = false;\n\tconst credentials = loadOAuthCredentials(oauthProvider);\n\tif (credentials) {\n\t\tusingOAuth = true;\n\t}\n\n\t// Also check for manual OAuth token env var (for Anthropic)\n\tif (!usingOAuth && model.provider === \"anthropic\" && process.env.ANTHROPIC_OAUTH_TOKEN) {\n\t\tusingOAuth = true;\n\t}\n\n\toauthStatusCache.set(oauthProvider, usingOAuth);\n\treturn usingOAuth;\n}\n"]}
@@ -1,384 +0,0 @@
1
- import { getApiKey, getGitHubCopilotBaseUrl, getModels, getProviders, loadOAuthCredentials, normalizeDomain, refreshGitHubCopilotToken, removeOAuthCredentials, saveOAuthCredentials, } from "@mariozechner/pi-ai";
2
- import { Type } from "@sinclair/typebox";
3
- import AjvModule from "ajv";
4
- import { existsSync, readFileSync } from "fs";
5
- import { join } from "path";
6
- import { getAgentDir } from "../config.js";
7
- import { getOAuthToken, refreshToken } from "./oauth/index.js";
8
- // Handle both default and named exports
9
- const Ajv = AjvModule.default || AjvModule;
10
- // Schema for OpenAI compatibility settings
11
- const OpenAICompatSchema = Type.Object({
12
- supportsStore: Type.Optional(Type.Boolean()),
13
- supportsDeveloperRole: Type.Optional(Type.Boolean()),
14
- supportsReasoningEffort: Type.Optional(Type.Boolean()),
15
- maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
16
- });
17
- // Schema for custom model definition
18
- const ModelDefinitionSchema = Type.Object({
19
- id: Type.String({ minLength: 1 }),
20
- name: Type.String({ minLength: 1 }),
21
- api: Type.Optional(Type.Union([
22
- Type.Literal("openai-completions"),
23
- Type.Literal("openai-responses"),
24
- Type.Literal("anthropic-messages"),
25
- Type.Literal("google-generative-ai"),
26
- ])),
27
- reasoning: Type.Boolean(),
28
- input: Type.Array(Type.Union([Type.Literal("text"), Type.Literal("image")])),
29
- cost: Type.Object({
30
- input: Type.Number(),
31
- output: Type.Number(),
32
- cacheRead: Type.Number(),
33
- cacheWrite: Type.Number(),
34
- }),
35
- contextWindow: Type.Number(),
36
- maxTokens: Type.Number(),
37
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
38
- compat: Type.Optional(OpenAICompatSchema),
39
- });
40
- const ProviderConfigSchema = Type.Object({
41
- baseUrl: Type.String({ minLength: 1 }),
42
- apiKey: Type.String({ minLength: 1 }),
43
- api: Type.Optional(Type.Union([
44
- Type.Literal("openai-completions"),
45
- Type.Literal("openai-responses"),
46
- Type.Literal("anthropic-messages"),
47
- Type.Literal("google-generative-ai"),
48
- ])),
49
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
50
- authHeader: Type.Optional(Type.Boolean()),
51
- models: Type.Array(ModelDefinitionSchema),
52
- });
53
- const ModelsConfigSchema = Type.Object({
54
- providers: Type.Record(Type.String(), ProviderConfigSchema),
55
- });
56
- // Custom provider API key mappings (provider name -> apiKey config)
57
- const customProviderApiKeys = new Map();
58
- /**
59
- * Resolve an API key config value to an actual key.
60
- * First checks if it's an environment variable, then treats as literal.
61
- */
62
- export function resolveApiKey(keyConfig) {
63
- // First check if it's an env var name
64
- const envValue = process.env[keyConfig];
65
- if (envValue)
66
- return envValue;
67
- // Otherwise treat as literal API key
68
- return keyConfig;
69
- }
70
- /**
71
- * Load custom models from models.json in agent config dir
72
- * Returns { models, error } - either models array or error message
73
- */
74
- function loadCustomModels(agentDir = getAgentDir()) {
75
- const configPath = join(agentDir, "models.json");
76
- if (!existsSync(configPath)) {
77
- return { models: [], error: null };
78
- }
79
- try {
80
- const content = readFileSync(configPath, "utf-8");
81
- const config = JSON.parse(content);
82
- // Validate schema
83
- const ajv = new Ajv();
84
- const validate = ajv.compile(ModelsConfigSchema);
85
- if (!validate(config)) {
86
- const errors = validate.errors?.map((e) => ` - ${e.instancePath || "root"}: ${e.message}`).join("\n") ||
87
- "Unknown schema error";
88
- return {
89
- models: [],
90
- error: `Invalid models.json schema:\n${errors}\n\nFile: ${configPath}`,
91
- };
92
- }
93
- // Additional validation
94
- try {
95
- validateConfig(config);
96
- }
97
- catch (error) {
98
- return {
99
- models: [],
100
- error: `Invalid models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${configPath}`,
101
- };
102
- }
103
- // Parse models
104
- return { models: parseModels(config), error: null };
105
- }
106
- catch (error) {
107
- if (error instanceof SyntaxError) {
108
- return {
109
- models: [],
110
- error: `Failed to parse models.json: ${error.message}\n\nFile: ${configPath}`,
111
- };
112
- }
113
- return {
114
- models: [],
115
- error: `Failed to load models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${configPath}`,
116
- };
117
- }
118
- }
119
- /**
120
- * Validate config structure and requirements
121
- */
122
- function validateConfig(config) {
123
- for (const [providerName, providerConfig] of Object.entries(config.providers)) {
124
- const hasProviderApi = !!providerConfig.api;
125
- for (const modelDef of providerConfig.models) {
126
- const hasModelApi = !!modelDef.api;
127
- if (!hasProviderApi && !hasModelApi) {
128
- throw new Error(`Provider ${providerName}, model ${modelDef.id}: no "api" specified. ` +
129
- `Set at provider or model level.`);
130
- }
131
- // Validate required fields
132
- if (!modelDef.id)
133
- throw new Error(`Provider ${providerName}: model missing "id"`);
134
- if (!modelDef.name)
135
- throw new Error(`Provider ${providerName}: model missing "name"`);
136
- if (modelDef.contextWindow <= 0)
137
- throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);
138
- if (modelDef.maxTokens <= 0)
139
- throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);
140
- }
141
- }
142
- }
143
- /**
144
- * Parse config into Model objects
145
- */
146
- function parseModels(config) {
147
- const models = [];
148
- // Clear and rebuild custom provider API key mappings
149
- customProviderApiKeys.clear();
150
- for (const [providerName, providerConfig] of Object.entries(config.providers)) {
151
- // Store API key config for this provider
152
- customProviderApiKeys.set(providerName, providerConfig.apiKey);
153
- for (const modelDef of providerConfig.models) {
154
- // Model-level api overrides provider-level api
155
- const api = modelDef.api || providerConfig.api;
156
- if (!api) {
157
- // This should have been caught by validateConfig, but be safe
158
- continue;
159
- }
160
- // Merge headers: provider headers are base, model headers override
161
- let headers = providerConfig.headers || modelDef.headers ? { ...providerConfig.headers, ...modelDef.headers } : undefined;
162
- // If authHeader is true, add Authorization header with resolved API key
163
- if (providerConfig.authHeader) {
164
- const resolvedKey = resolveApiKey(providerConfig.apiKey);
165
- if (resolvedKey) {
166
- headers = { ...headers, Authorization: `Bearer ${resolvedKey}` };
167
- }
168
- }
169
- models.push({
170
- id: modelDef.id,
171
- name: modelDef.name,
172
- api: api,
173
- provider: providerName,
174
- baseUrl: providerConfig.baseUrl,
175
- reasoning: modelDef.reasoning,
176
- input: modelDef.input,
177
- cost: modelDef.cost,
178
- contextWindow: modelDef.contextWindow,
179
- maxTokens: modelDef.maxTokens,
180
- headers,
181
- compat: modelDef.compat,
182
- });
183
- }
184
- }
185
- return models;
186
- }
187
- /**
188
- * Get all models (built-in + custom), freshly loaded
189
- * Returns { models, error } - either models array or error message
190
- */
191
- export function loadAndMergeModels(agentDir = getAgentDir()) {
192
- const builtInModels = [];
193
- const providers = getProviders();
194
- // Load all built-in models
195
- for (const provider of providers) {
196
- const providerModels = getModels(provider);
197
- builtInModels.push(...providerModels);
198
- }
199
- // Load custom models
200
- const { models: customModels, error } = loadCustomModels(agentDir);
201
- if (error) {
202
- return { models: [], error };
203
- }
204
- const combined = [...builtInModels, ...customModels];
205
- // Update github-copilot base URL based on OAuth token or enterprise domain
206
- const copilotCreds = loadOAuthCredentials("github-copilot");
207
- if (copilotCreds) {
208
- const domain = copilotCreds.enterpriseUrl ? normalizeDomain(copilotCreds.enterpriseUrl) : undefined;
209
- const baseUrl = getGitHubCopilotBaseUrl(copilotCreds.access, domain ?? undefined);
210
- return {
211
- models: combined.map((m) => (m.provider === "github-copilot" ? { ...m, baseUrl } : m)),
212
- error: null,
213
- };
214
- }
215
- return { models: combined, error: null };
216
- }
217
- /**
218
- * Get API key for a model (checks custom providers first, then built-in)
219
- * Now async to support OAuth token refresh.
220
- * Note: OAuth storage location is configured globally via setOAuthStorage.
221
- */
222
- export async function getApiKeyForModel(model) {
223
- // For custom providers, check their apiKey config
224
- const customKeyConfig = customProviderApiKeys.get(model.provider);
225
- if (customKeyConfig) {
226
- return resolveApiKey(customKeyConfig);
227
- }
228
- // For Anthropic, check OAuth first
229
- if (model.provider === "anthropic") {
230
- // 1. Check OAuth storage (auto-refresh if needed)
231
- const oauthToken = await getOAuthToken("anthropic");
232
- if (oauthToken) {
233
- return oauthToken;
234
- }
235
- // 2. Check ANTHROPIC_OAUTH_TOKEN env var (manual OAuth token)
236
- const oauthEnv = process.env.ANTHROPIC_OAUTH_TOKEN;
237
- if (oauthEnv) {
238
- return oauthEnv;
239
- }
240
- // 3. Fall back to ANTHROPIC_API_KEY env var
241
- }
242
- if (model.provider === "github-copilot") {
243
- // 1. Check OAuth storage (from device flow login)
244
- const oauthToken = await getOAuthToken("github-copilot");
245
- if (oauthToken) {
246
- return oauthToken;
247
- }
248
- // 2. Use GitHub token directly (works with copilot scope on github.com)
249
- const githubToken = process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
250
- if (!githubToken) {
251
- return undefined;
252
- }
253
- // 3. For enterprise, exchange token for short-lived Copilot token
254
- const enterpriseDomain = process.env.COPILOT_ENTERPRISE_URL
255
- ? normalizeDomain(process.env.COPILOT_ENTERPRISE_URL)
256
- : undefined;
257
- if (enterpriseDomain) {
258
- const creds = await refreshGitHubCopilotToken(githubToken, enterpriseDomain);
259
- saveOAuthCredentials("github-copilot", creds);
260
- return creds.access;
261
- }
262
- // 4. For github.com, use token directly
263
- return githubToken;
264
- }
265
- // For Google Gemini CLI and Antigravity, check OAuth and encode projectId with token
266
- if (model.provider === "google-gemini-cli" || model.provider === "google-antigravity") {
267
- const oauthProvider = model.provider;
268
- const credentials = loadOAuthCredentials(oauthProvider);
269
- if (!credentials) {
270
- return undefined;
271
- }
272
- // Check if token is expired
273
- if (Date.now() >= credentials.expires) {
274
- try {
275
- await refreshToken(oauthProvider);
276
- const refreshedCreds = loadOAuthCredentials(oauthProvider);
277
- if (refreshedCreds?.projectId) {
278
- return JSON.stringify({ token: refreshedCreds.access, projectId: refreshedCreds.projectId });
279
- }
280
- }
281
- catch {
282
- removeOAuthCredentials(oauthProvider);
283
- return undefined;
284
- }
285
- }
286
- if (credentials.projectId) {
287
- return JSON.stringify({ token: credentials.access, projectId: credentials.projectId });
288
- }
289
- return undefined;
290
- }
291
- // For built-in providers, use getApiKey from @mariozechner/pi-ai
292
- return getApiKey(model.provider);
293
- }
294
- /**
295
- * Get only models that have valid API keys available
296
- * Returns { models, error } - either models array or error message
297
- *
298
- * @param agentDir - Agent config directory
299
- * @param fallbackKeyResolver - Optional function to check for API keys not found by getApiKeyForModel
300
- * (e.g., keys from settings.json)
301
- */
302
- export async function getAvailableModels(agentDir = getAgentDir(), fallbackKeyResolver) {
303
- const { models: allModels, error } = loadAndMergeModels(agentDir);
304
- if (error) {
305
- return { models: [], error };
306
- }
307
- const availableModels = [];
308
- for (const model of allModels) {
309
- let apiKey = await getApiKeyForModel(model);
310
- // Check fallback resolver if primary lookup failed
311
- if (!apiKey && fallbackKeyResolver) {
312
- apiKey = fallbackKeyResolver(model.provider);
313
- }
314
- if (apiKey) {
315
- availableModels.push(model);
316
- }
317
- }
318
- return { models: availableModels, error: null };
319
- }
320
- /**
321
- * Find a specific model by provider and ID.
322
- *
323
- * Searches models from:
324
- * 1. Built-in models from @mariozechner/pi-ai
325
- * 2. Custom models defined in ~/.pi/agent/models.json
326
- *
327
- * Returns { model, error } - either the model or an error message.
328
- */
329
- export function findModel(provider, modelId, agentDir = getAgentDir()) {
330
- const { models: allModels, error } = loadAndMergeModels(agentDir);
331
- if (error) {
332
- return { model: null, error };
333
- }
334
- const model = allModels.find((m) => m.provider === provider && m.id === modelId) || null;
335
- return { model, error: null };
336
- }
337
- /**
338
- * Mapping from model provider to OAuth provider ID.
339
- * Only providers that support OAuth are listed here.
340
- */
341
- const providerToOAuthProvider = {
342
- anthropic: "anthropic",
343
- "github-copilot": "github-copilot",
344
- "google-gemini-cli": "google-gemini-cli",
345
- "google-antigravity": "google-antigravity",
346
- };
347
- // Cache for OAuth status per provider (avoids file reads on every render)
348
- const oauthStatusCache = new Map();
349
- /**
350
- * Invalidate the OAuth status cache.
351
- * Call this after login/logout operations.
352
- */
353
- export function invalidateOAuthCache() {
354
- oauthStatusCache.clear();
355
- }
356
- /**
357
- * Check if a model is using OAuth credentials (subscription).
358
- * This checks if OAuth credentials exist and would be used for the model,
359
- * without actually fetching or refreshing the token.
360
- * Results are cached until invalidateOAuthCache() is called.
361
- */
362
- export function isModelUsingOAuth(model) {
363
- const oauthProvider = providerToOAuthProvider[model.provider];
364
- if (!oauthProvider) {
365
- return false;
366
- }
367
- // Check cache first
368
- if (oauthStatusCache.has(oauthProvider)) {
369
- return oauthStatusCache.get(oauthProvider);
370
- }
371
- // Check if OAuth credentials exist for this provider
372
- let usingOAuth = false;
373
- const credentials = loadOAuthCredentials(oauthProvider);
374
- if (credentials) {
375
- usingOAuth = true;
376
- }
377
- // Also check for manual OAuth token env var (for Anthropic)
378
- if (!usingOAuth && model.provider === "anthropic" && process.env.ANTHROPIC_OAUTH_TOKEN) {
379
- usingOAuth = true;
380
- }
381
- oauthStatusCache.set(oauthProvider, usingOAuth);
382
- return usingOAuth;
383
- }
384
- //# sourceMappingURL=model-config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-config.js","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,SAAS,EACT,uBAAuB,EACvB,SAAS,EACT,YAAY,EAEZ,oBAAoB,EAEpB,eAAe,EACf,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAe,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,SAAS,MAAM,KAAK,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAsB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEnF,wCAAwC;AACxC,MAAM,GAAG,GAAI,SAAiB,CAAC,OAAO,IAAI,SAAS,CAAC;AAEpD,2CAA2C;AAC3C,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IACtC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IAC5C,qBAAqB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACpD,uBAAuB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACtD,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;CAC9G,CAAC,CAAC;AAEH,qCAAqC;AACrC,MAAM,qBAAqB,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACjC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC,QAAQ,CACjB,IAAI,CAAC,KAAK,CAAC;QACV,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC;KACpC,CAAC,CACF;IACD,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE;IACzB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5E,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;QACpB,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACrB,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE;KACzB,CAAC;IACF,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE;IAC5B,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE;IACxB,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CACzC,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACtC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACrC,GAAG,EAAE,IAAI,CAAC,QAAQ,CACjB,IAAI,CAAC,KAAK,CAAC;QACV,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC;KACpC,CAAC,CACF;IACD,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACzC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC;CACzC,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IACtC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC;CAC3D,CAAC,CAAC;AAIH,oEAAoE;AACpE,MAAM,qBAAqB,GAAwB,IAAI,GAAG,EAAE,CAAC;AAE7D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAsB;IACpE,sCAAsC;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,qCAAqC;IACrC,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,QAAQ,GAAW,WAAW,EAAE,EAAkD;IAC3G,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAAiB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEjD,kBAAkB;QAClB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GACX,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,YAAY,IAAI,MAAM,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC5F,sBAAsB,CAAC;YACxB,OAAO;gBACN,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,gCAAgC,MAAM,aAAa,UAAU,EAAE;aACtE,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,IAAI,CAAC;YACJ,cAAc,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO;gBACN,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,aAAa,UAAU,EAAE;aACtG,CAAC;QACH,CAAC;QAED,eAAe;QACf,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YAClC,OAAO;gBACN,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,gCAAgC,KAAK,CAAC,OAAO,aAAa,UAAU,EAAE;aAC7E,CAAC;QACH,CAAC;QACD,OAAO;YACN,MAAM,EAAE,EAAE;YACV,KAAK,EAAE,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,aAAa,UAAU,EAAE;SAC7G,CAAC;IACH,CAAC;AAAA,CACD;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,MAAoB,EAAQ;IACnD,KAAK,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/E,MAAM,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC;QAE5C,KAAK,MAAM,QAAQ,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAEnC,IAAI,CAAC,cAAc,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrC,MAAM,IAAI,KAAK,CACd,YAAY,YAAY,WAAW,QAAQ,CAAC,EAAE,wBAAwB;oBACrE,iCAAiC,CAClC,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,sBAAsB,CAAC,CAAC;YAClF,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,wBAAwB,CAAC,CAAC;YACtF,IAAI,QAAQ,CAAC,aAAa,IAAI,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,WAAW,QAAQ,CAAC,EAAE,yBAAyB,CAAC,CAAC;YAC1F,IAAI,QAAQ,CAAC,SAAS,IAAI,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,WAAW,QAAQ,CAAC,EAAE,qBAAqB,CAAC,CAAC;QACvF,CAAC;IACF,CAAC;AAAA,CACD;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,MAAoB,EAAgB;IACxD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,qDAAqD;IACrD,qBAAqB,CAAC,KAAK,EAAE,CAAC;IAE9B,KAAK,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/E,yCAAyC;QACzC,qBAAqB,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;QAE/D,KAAK,MAAM,QAAQ,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC;YAC9C,+CAA+C;YAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC;YAE/C,IAAI,CAAC,GAAG,EAAE,CAAC;gBACV,8DAA8D;gBAC9D,SAAS;YACV,CAAC;YAED,mEAAmE;YACnE,IAAI,OAAO,GACV,cAAc,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YAE7G,wEAAwE;YACxE,IAAI,cAAc,CAAC,UAAU,EAAE,CAAC;gBAC/B,MAAM,WAAW,GAAG,aAAa,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;gBACzD,IAAI,WAAW,EAAE,CAAC;oBACjB,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE,CAAC;gBAClE,CAAC;YACF,CAAC;YAED,MAAM,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,GAAG,EAAE,GAAU;gBACf,QAAQ,EAAE,YAAY;gBACtB,OAAO,EAAE,cAAc,CAAC,OAAO;gBAC/B,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,KAAK,EAAE,QAAQ,CAAC,KAA6B;gBAC7C,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,aAAa,EAAE,QAAQ,CAAC,aAAa;gBACrC,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,OAAO;gBACP,MAAM,EAAE,QAAQ,CAAC,MAAM;aACT,CAAC,CAAC;QAClB,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AAAA,CACd;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAQ,GAAW,WAAW,EAAE,EAAkD;IACpH,MAAM,aAAa,GAAiB,EAAE,CAAC;IACvC,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IAEjC,2BAA2B;IAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,MAAM,cAAc,GAAG,SAAS,CAAC,QAAyB,CAAC,CAAC;QAC5D,aAAa,CAAC,IAAI,CAAC,GAAI,cAA+B,CAAC,CAAC;IACzD,CAAC;IAED,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEnE,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,YAAY,CAAC,CAAC;IAErD,2EAA2E;IAC3E,MAAM,YAAY,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAC5D,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACpG,MAAM,OAAO,GAAG,uBAAuB,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;QAClF,OAAO;YACN,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,gBAAgB,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,KAAK,EAAE,IAAI;SACX,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAAA,CACzC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAiB,EAA+B;IACvF,kDAAkD;IAClD,MAAM,eAAe,GAAG,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,eAAe,EAAE,CAAC;QACrB,OAAO,aAAa,CAAC,eAAe,CAAC,CAAC;IACvC,CAAC;IAED,mCAAmC;IACnC,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,kDAAkD;QAClD,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QACpD,IAAI,UAAU,EAAE,CAAC;YAChB,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,8DAA8D;QAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QACnD,IAAI,QAAQ,EAAE,CAAC;YACd,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,4CAA4C;IAC7C,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QACzC,kDAAkD;QAClD,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,UAAU,EAAE,CAAC;YAChB,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,wEAAwE;QACxE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACzG,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,kEAAkE;QAClE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB;YAC1D,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;YACrD,CAAC,CAAC,SAAS,CAAC;QAEb,IAAI,gBAAgB,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,yBAAyB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;YAC7E,oBAAoB,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;YAC9C,OAAO,KAAK,CAAC,MAAM,CAAC;QACrB,CAAC;QAED,wCAAwC;QACxC,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,qFAAqF;IACrF,IAAI,KAAK,CAAC,QAAQ,KAAK,mBAAmB,IAAI,KAAK,CAAC,QAAQ,KAAK,oBAAoB,EAAE,CAAC;QACvF,MAAM,aAAa,GAAG,KAAK,CAAC,QAAsD,CAAC;QACnF,MAAM,WAAW,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,4BAA4B;QAC5B,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,CAAC;gBACJ,MAAM,YAAY,CAAC,aAAa,CAAC,CAAC;gBAClC,MAAM,cAAc,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;gBAC3D,IAAI,cAAc,EAAE,SAAS,EAAE,CAAC;oBAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC9F,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,sBAAsB,CAAC,aAAa,CAAC,CAAC;gBACtC,OAAO,SAAS,CAAC;YAClB,CAAC;QACF,CAAC;QAED,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC;QACxF,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,iEAAiE;IACjE,OAAO,SAAS,CAAC,KAAK,CAAC,QAAyB,CAAC,CAAC;AAAA,CAClD;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,QAAQ,GAAW,WAAW,EAAE,EAChC,mBAA8D,EACJ;IAC1D,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAElE,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,eAAe,GAAiB,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,MAAM,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAC5C,mDAAmD;QACnD,IAAI,CAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC;YACpC,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACZ,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAAA,CAChD;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CACxB,QAAgB,EAChB,OAAe,EACf,QAAQ,GAAW,WAAW,EAAE,EACqB;IACrD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAElE,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC;IACzF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAAA,CAC9B;AAED;;;GAGG;AACH,MAAM,uBAAuB,GAAkC;IAC9D,SAAS,EAAE,WAAW;IACtB,gBAAgB,EAAE,gBAAgB;IAClC,mBAAmB,EAAE,mBAAmB;IACxC,oBAAoB,EAAE,oBAAoB;CAC1C,CAAC;AAEF,0EAA0E;AAC1E,MAAM,gBAAgB,GAAyB,IAAI,GAAG,EAAE,CAAC;AAEzD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,GAAS;IAC5C,gBAAgB,CAAC,KAAK,EAAE,CAAC;AAAA,CACzB;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB,EAAW;IAC7D,MAAM,aAAa,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9D,IAAI,CAAC,aAAa,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,oBAAoB;IACpB,IAAI,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;QACzC,OAAO,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC;IAC7C,CAAC;IAED,qDAAqD;IACrD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,WAAW,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;IACxD,IAAI,WAAW,EAAE,CAAC;QACjB,UAAU,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,4DAA4D;IAC5D,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;QACxF,UAAU,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,gBAAgB,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAChD,OAAO,UAAU,CAAC;AAAA,CAClB","sourcesContent":["import {\n\ttype Api,\n\tgetApiKey,\n\tgetGitHubCopilotBaseUrl,\n\tgetModels,\n\tgetProviders,\n\ttype KnownProvider,\n\tloadOAuthCredentials,\n\ttype Model,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n\tremoveOAuthCredentials,\n\tsaveOAuthCredentials,\n} from \"@mariozechner/pi-ai\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport AjvModule from \"ajv\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\nimport { getOAuthToken, type OAuthProvider, refreshToken } from \"./oauth/index.js\";\n\n// Handle both default and named exports\nconst Ajv = (AjvModule as any).default || AjvModule;\n\n// Schema for OpenAI compatibility settings\nconst OpenAICompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n});\n\n// Schema for custom model definition\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.String({ minLength: 1 }),\n\tapi: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai-completions\"),\n\t\t\tType.Literal(\"openai-responses\"),\n\t\t\tType.Literal(\"anthropic-messages\"),\n\t\t\tType.Literal(\"google-generative-ai\"),\n\t\t]),\n\t),\n\treasoning: Type.Boolean(),\n\tinput: Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")])),\n\tcost: Type.Object({\n\t\tinput: Type.Number(),\n\t\toutput: Type.Number(),\n\t\tcacheRead: Type.Number(),\n\t\tcacheWrite: Type.Number(),\n\t}),\n\tcontextWindow: Type.Number(),\n\tmaxTokens: Type.Number(),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(OpenAICompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tbaseUrl: Type.String({ minLength: 1 }),\n\tapiKey: Type.String({ minLength: 1 }),\n\tapi: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai-completions\"),\n\t\t\tType.Literal(\"openai-responses\"),\n\t\t\tType.Literal(\"anthropic-messages\"),\n\t\t\tType.Literal(\"google-generative-ai\"),\n\t\t]),\n\t),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Array(ModelDefinitionSchema),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\n\ntype ModelsConfig = Static<typeof ModelsConfigSchema>;\n\n// Custom provider API key mappings (provider name -> apiKey config)\nconst customProviderApiKeys: Map<string, string> = new Map();\n\n/**\n * Resolve an API key config value to an actual key.\n * First checks if it's an environment variable, then treats as literal.\n */\nexport function resolveApiKey(keyConfig: string): string | undefined {\n\t// First check if it's an env var name\n\tconst envValue = process.env[keyConfig];\n\tif (envValue) return envValue;\n\n\t// Otherwise treat as literal API key\n\treturn keyConfig;\n}\n\n/**\n * Load custom models from models.json in agent config dir\n * Returns { models, error } - either models array or error message\n */\nfunction loadCustomModels(agentDir: string = getAgentDir()): { models: Model<Api>[]; error: string | null } {\n\tconst configPath = join(agentDir, \"models.json\");\n\tif (!existsSync(configPath)) {\n\t\treturn { models: [], error: null };\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(configPath, \"utf-8\");\n\t\tconst config: ModelsConfig = JSON.parse(content);\n\n\t\t// Validate schema\n\t\tconst ajv = new Ajv();\n\t\tconst validate = ajv.compile(ModelsConfigSchema);\n\t\tif (!validate(config)) {\n\t\t\tconst errors =\n\t\t\t\tvalidate.errors?.map((e: any) => ` - ${e.instancePath || \"root\"}: ${e.message}`).join(\"\\n\") ||\n\t\t\t\t\"Unknown schema error\";\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Invalid models.json schema:\\n${errors}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\n\t\t// Additional validation\n\t\ttry {\n\t\t\tvalidateConfig(config);\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Invalid models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\n\t\t// Parse models\n\t\treturn { models: parseModels(config), error: null };\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\treturn {\n\t\t\t\tmodels: [],\n\t\t\t\terror: `Failed to parse models.json: ${error.message}\\n\\nFile: ${configPath}`,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tmodels: [],\n\t\t\terror: `Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${configPath}`,\n\t\t};\n\t}\n}\n\n/**\n * Validate config structure and requirements\n */\nfunction validateConfig(config: ModelsConfig): void {\n\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\tconst hasProviderApi = !!providerConfig.api;\n\n\t\tfor (const modelDef of providerConfig.models) {\n\t\t\tconst hasModelApi = !!modelDef.api;\n\n\t\t\tif (!hasProviderApi && !hasModelApi) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Provider ${providerName}, model ${modelDef.id}: no \"api\" specified. ` +\n\t\t\t\t\t\t`Set at provider or model level.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Validate required fields\n\t\t\tif (!modelDef.id) throw new Error(`Provider ${providerName}: model missing \"id\"`);\n\t\t\tif (!modelDef.name) throw new Error(`Provider ${providerName}: model missing \"name\"`);\n\t\t\tif (modelDef.contextWindow <= 0)\n\t\t\t\tthrow new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);\n\t\t\tif (modelDef.maxTokens <= 0)\n\t\t\t\tthrow new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);\n\t\t}\n\t}\n}\n\n/**\n * Parse config into Model objects\n */\nfunction parseModels(config: ModelsConfig): Model<Api>[] {\n\tconst models: Model<Api>[] = [];\n\n\t// Clear and rebuild custom provider API key mappings\n\tcustomProviderApiKeys.clear();\n\n\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t// Store API key config for this provider\n\t\tcustomProviderApiKeys.set(providerName, providerConfig.apiKey);\n\n\t\tfor (const modelDef of providerConfig.models) {\n\t\t\t// Model-level api overrides provider-level api\n\t\t\tconst api = modelDef.api || providerConfig.api;\n\n\t\t\tif (!api) {\n\t\t\t\t// This should have been caught by validateConfig, but be safe\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Merge headers: provider headers are base, model headers override\n\t\t\tlet headers =\n\t\t\t\tproviderConfig.headers || modelDef.headers ? { ...providerConfig.headers, ...modelDef.headers } : undefined;\n\n\t\t\t// If authHeader is true, add Authorization header with resolved API key\n\t\t\tif (providerConfig.authHeader) {\n\t\t\t\tconst resolvedKey = resolveApiKey(providerConfig.apiKey);\n\t\t\t\tif (resolvedKey) {\n\t\t\t\t\theaders = { ...headers, Authorization: `Bearer ${resolvedKey}` };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmodels.push({\n\t\t\t\tid: modelDef.id,\n\t\t\t\tname: modelDef.name,\n\t\t\t\tapi: api as Api,\n\t\t\t\tprovider: providerName,\n\t\t\t\tbaseUrl: providerConfig.baseUrl,\n\t\t\t\treasoning: modelDef.reasoning,\n\t\t\t\tinput: modelDef.input as (\"text\" | \"image\")[],\n\t\t\t\tcost: modelDef.cost,\n\t\t\t\tcontextWindow: modelDef.contextWindow,\n\t\t\t\tmaxTokens: modelDef.maxTokens,\n\t\t\t\theaders,\n\t\t\t\tcompat: modelDef.compat,\n\t\t\t} as Model<Api>);\n\t\t}\n\t}\n\n\treturn models;\n}\n\n/**\n * Get all models (built-in + custom), freshly loaded\n * Returns { models, error } - either models array or error message\n */\nexport function loadAndMergeModels(agentDir: string = getAgentDir()): { models: Model<Api>[]; error: string | null } {\n\tconst builtInModels: Model<Api>[] = [];\n\tconst providers = getProviders();\n\n\t// Load all built-in models\n\tfor (const provider of providers) {\n\t\tconst providerModels = getModels(provider as KnownProvider);\n\t\tbuiltInModels.push(...(providerModels as Model<Api>[]));\n\t}\n\n\t// Load custom models\n\tconst { models: customModels, error } = loadCustomModels(agentDir);\n\n\tif (error) {\n\t\treturn { models: [], error };\n\t}\n\n\tconst combined = [...builtInModels, ...customModels];\n\n\t// Update github-copilot base URL based on OAuth token or enterprise domain\n\tconst copilotCreds = loadOAuthCredentials(\"github-copilot\");\n\tif (copilotCreds) {\n\t\tconst domain = copilotCreds.enterpriseUrl ? normalizeDomain(copilotCreds.enterpriseUrl) : undefined;\n\t\tconst baseUrl = getGitHubCopilotBaseUrl(copilotCreds.access, domain ?? undefined);\n\t\treturn {\n\t\t\tmodels: combined.map((m) => (m.provider === \"github-copilot\" ? { ...m, baseUrl } : m)),\n\t\t\terror: null,\n\t\t};\n\t}\n\n\treturn { models: combined, error: null };\n}\n\n/**\n * Get API key for a model (checks custom providers first, then built-in)\n * Now async to support OAuth token refresh.\n * Note: OAuth storage location is configured globally via setOAuthStorage.\n */\nexport async function getApiKeyForModel(model: Model<Api>): Promise<string | undefined> {\n\t// For custom providers, check their apiKey config\n\tconst customKeyConfig = customProviderApiKeys.get(model.provider);\n\tif (customKeyConfig) {\n\t\treturn resolveApiKey(customKeyConfig);\n\t}\n\n\t// For Anthropic, check OAuth first\n\tif (model.provider === \"anthropic\") {\n\t\t// 1. Check OAuth storage (auto-refresh if needed)\n\t\tconst oauthToken = await getOAuthToken(\"anthropic\");\n\t\tif (oauthToken) {\n\t\t\treturn oauthToken;\n\t\t}\n\n\t\t// 2. Check ANTHROPIC_OAUTH_TOKEN env var (manual OAuth token)\n\t\tconst oauthEnv = process.env.ANTHROPIC_OAUTH_TOKEN;\n\t\tif (oauthEnv) {\n\t\t\treturn oauthEnv;\n\t\t}\n\n\t\t// 3. Fall back to ANTHROPIC_API_KEY env var\n\t}\n\n\tif (model.provider === \"github-copilot\") {\n\t\t// 1. Check OAuth storage (from device flow login)\n\t\tconst oauthToken = await getOAuthToken(\"github-copilot\");\n\t\tif (oauthToken) {\n\t\t\treturn oauthToken;\n\t\t}\n\n\t\t// 2. Use GitHub token directly (works with copilot scope on github.com)\n\t\tconst githubToken = process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;\n\t\tif (!githubToken) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// 3. For enterprise, exchange token for short-lived Copilot token\n\t\tconst enterpriseDomain = process.env.COPILOT_ENTERPRISE_URL\n\t\t\t? normalizeDomain(process.env.COPILOT_ENTERPRISE_URL)\n\t\t\t: undefined;\n\n\t\tif (enterpriseDomain) {\n\t\t\tconst creds = await refreshGitHubCopilotToken(githubToken, enterpriseDomain);\n\t\t\tsaveOAuthCredentials(\"github-copilot\", creds);\n\t\t\treturn creds.access;\n\t\t}\n\n\t\t// 4. For github.com, use token directly\n\t\treturn githubToken;\n\t}\n\n\t// For Google Gemini CLI and Antigravity, check OAuth and encode projectId with token\n\tif (model.provider === \"google-gemini-cli\" || model.provider === \"google-antigravity\") {\n\t\tconst oauthProvider = model.provider as \"google-gemini-cli\" | \"google-antigravity\";\n\t\tconst credentials = loadOAuthCredentials(oauthProvider);\n\t\tif (!credentials) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Check if token is expired\n\t\tif (Date.now() >= credentials.expires) {\n\t\t\ttry {\n\t\t\t\tawait refreshToken(oauthProvider);\n\t\t\t\tconst refreshedCreds = loadOAuthCredentials(oauthProvider);\n\t\t\t\tif (refreshedCreds?.projectId) {\n\t\t\t\t\treturn JSON.stringify({ token: refreshedCreds.access, projectId: refreshedCreds.projectId });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tremoveOAuthCredentials(oauthProvider);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tif (credentials.projectId) {\n\t\t\treturn JSON.stringify({ token: credentials.access, projectId: credentials.projectId });\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// For built-in providers, use getApiKey from @mariozechner/pi-ai\n\treturn getApiKey(model.provider as KnownProvider);\n}\n\n/**\n * Get only models that have valid API keys available\n * Returns { models, error } - either models array or error message\n *\n * @param agentDir - Agent config directory\n * @param fallbackKeyResolver - Optional function to check for API keys not found by getApiKeyForModel\n * (e.g., keys from settings.json)\n */\nexport async function getAvailableModels(\n\tagentDir: string = getAgentDir(),\n\tfallbackKeyResolver?: (provider: string) => string | undefined,\n): Promise<{ models: Model<Api>[]; error: string | null }> {\n\tconst { models: allModels, error } = loadAndMergeModels(agentDir);\n\n\tif (error) {\n\t\treturn { models: [], error };\n\t}\n\n\tconst availableModels: Model<Api>[] = [];\n\tfor (const model of allModels) {\n\t\tlet apiKey = await getApiKeyForModel(model);\n\t\t// Check fallback resolver if primary lookup failed\n\t\tif (!apiKey && fallbackKeyResolver) {\n\t\t\tapiKey = fallbackKeyResolver(model.provider);\n\t\t}\n\t\tif (apiKey) {\n\t\t\tavailableModels.push(model);\n\t\t}\n\t}\n\n\treturn { models: availableModels, error: null };\n}\n\n/**\n * Find a specific model by provider and ID.\n *\n * Searches models from:\n * 1. Built-in models from @mariozechner/pi-ai\n * 2. Custom models defined in ~/.pi/agent/models.json\n *\n * Returns { model, error } - either the model or an error message.\n */\nexport function findModel(\n\tprovider: string,\n\tmodelId: string,\n\tagentDir: string = getAgentDir(),\n): { model: Model<Api> | null; error: string | null } {\n\tconst { models: allModels, error } = loadAndMergeModels(agentDir);\n\n\tif (error) {\n\t\treturn { model: null, error };\n\t}\n\n\tconst model = allModels.find((m) => m.provider === provider && m.id === modelId) || null;\n\treturn { model, error: null };\n}\n\n/**\n * Mapping from model provider to OAuth provider ID.\n * Only providers that support OAuth are listed here.\n */\nconst providerToOAuthProvider: Record<string, OAuthProvider> = {\n\tanthropic: \"anthropic\",\n\t\"github-copilot\": \"github-copilot\",\n\t\"google-gemini-cli\": \"google-gemini-cli\",\n\t\"google-antigravity\": \"google-antigravity\",\n};\n\n// Cache for OAuth status per provider (avoids file reads on every render)\nconst oauthStatusCache: Map<string, boolean> = new Map();\n\n/**\n * Invalidate the OAuth status cache.\n * Call this after login/logout operations.\n */\nexport function invalidateOAuthCache(): void {\n\toauthStatusCache.clear();\n}\n\n/**\n * Check if a model is using OAuth credentials (subscription).\n * This checks if OAuth credentials exist and would be used for the model,\n * without actually fetching or refreshing the token.\n * Results are cached until invalidateOAuthCache() is called.\n */\nexport function isModelUsingOAuth(model: Model<Api>): boolean {\n\tconst oauthProvider = providerToOAuthProvider[model.provider];\n\tif (!oauthProvider) {\n\t\treturn false;\n\t}\n\n\t// Check cache first\n\tif (oauthStatusCache.has(oauthProvider)) {\n\t\treturn oauthStatusCache.get(oauthProvider)!;\n\t}\n\n\t// Check if OAuth credentials exist for this provider\n\tlet usingOAuth = false;\n\tconst credentials = loadOAuthCredentials(oauthProvider);\n\tif (credentials) {\n\t\tusingOAuth = true;\n\t}\n\n\t// Also check for manual OAuth token env var (for Anthropic)\n\tif (!usingOAuth && model.provider === \"anthropic\" && process.env.ANTHROPIC_OAUTH_TOKEN) {\n\t\tusingOAuth = true;\n\t}\n\n\toauthStatusCache.set(oauthProvider, usingOAuth);\n\treturn usingOAuth;\n}\n"]}
@@ -1,41 +0,0 @@
1
- /**
2
- * OAuth management for coding-agent.
3
- * Re-exports from @mariozechner/pi-ai and adds convenience wrappers.
4
- */
5
- import { getOAuthApiKey, listOAuthProviders as listOAuthProvidersFromAi, loadOAuthCredentials, type OAuthCredentials, type OAuthProvider, type OAuthStorageBackend, removeOAuthCredentials, resetOAuthStorage, saveOAuthCredentials, setOAuthStorage } from "@mariozechner/pi-ai";
6
- export type { OAuthCredentials, OAuthProvider, OAuthStorageBackend };
7
- export { listOAuthProvidersFromAi as listOAuthProviders };
8
- export { getOAuthApiKey, loadOAuthCredentials, removeOAuthCredentials, resetOAuthStorage, saveOAuthCredentials, setOAuthStorage, };
9
- export interface OAuthAuthInfo {
10
- url: string;
11
- instructions?: string;
12
- }
13
- export interface OAuthPrompt {
14
- message: string;
15
- placeholder?: string;
16
- }
17
- export type OAuthProviderInfo = {
18
- id: OAuthProvider;
19
- name: string;
20
- description: string;
21
- available: boolean;
22
- };
23
- export declare function getOAuthProviders(): OAuthProviderInfo[];
24
- /**
25
- * Login with OAuth provider
26
- */
27
- export declare function login(provider: OAuthProvider, onAuth: (info: OAuthAuthInfo) => void, onPrompt: (prompt: OAuthPrompt) => Promise<string>, onProgress?: (message: string) => void): Promise<void>;
28
- /**
29
- * Logout from OAuth provider
30
- */
31
- export declare function logout(provider: OAuthProvider): Promise<void>;
32
- /**
33
- * Refresh OAuth token for provider.
34
- * Delegates to the ai package implementation.
35
- */
36
- export declare function refreshToken(provider: OAuthProvider): Promise<string>;
37
- /**
38
- * Get OAuth token for provider (auto-refreshes if expired).
39
- */
40
- export declare function getOAuthToken(provider: OAuthProvider): Promise<string | null>;
41
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/oauth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,cAAc,EACd,kBAAkB,IAAI,wBAAwB,EAC9C,oBAAoB,EAKpB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EAExB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AACrE,OAAO,EAAE,wBAAwB,IAAI,kBAAkB,EAAE,CAAC;AAC1D,OAAO,EACN,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,GACf,CAAC;AAGF,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,EAAE,EAAE,aAAa,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CA2BvD;AAED;;GAEG;AACH,wBAAsB,KAAK,CAC1B,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,EACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,EAClD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF","sourcesContent":["/**\n * OAuth management for coding-agent.\n * Re-exports from @mariozechner/pi-ai and adds convenience wrappers.\n */\n\nimport {\n\tgetOAuthApiKey,\n\tlistOAuthProviders as listOAuthProvidersFromAi,\n\tloadOAuthCredentials,\n\tloginAnthropic,\n\tloginAntigravity,\n\tloginGeminiCli,\n\tloginGitHubCopilot,\n\ttype OAuthCredentials,\n\ttype OAuthProvider,\n\ttype OAuthStorageBackend,\n\trefreshToken as refreshTokenFromAi,\n\tremoveOAuthCredentials,\n\tresetOAuthStorage,\n\tsaveOAuthCredentials,\n\tsetOAuthStorage,\n} from \"@mariozechner/pi-ai\";\n\n// Re-export types and functions\nexport type { OAuthCredentials, OAuthProvider, OAuthStorageBackend };\nexport { listOAuthProvidersFromAi as listOAuthProviders };\nexport {\n\tgetOAuthApiKey,\n\tloadOAuthCredentials,\n\tremoveOAuthCredentials,\n\tresetOAuthStorage,\n\tsaveOAuthCredentials,\n\tsetOAuthStorage,\n};\n\n// Types for OAuth flow\nexport interface OAuthAuthInfo {\n\turl: string;\n\tinstructions?: string;\n}\n\nexport interface OAuthPrompt {\n\tmessage: string;\n\tplaceholder?: string;\n}\n\nexport type OAuthProviderInfo = {\n\tid: OAuthProvider;\n\tname: string;\n\tdescription: string;\n\tavailable: boolean;\n};\n\nexport function getOAuthProviders(): OAuthProviderInfo[] {\n\treturn [\n\t\t{\n\t\t\tid: \"anthropic\",\n\t\t\tname: \"Anthropic (Claude Pro/Max)\",\n\t\t\tdescription: \"Use Claude with your Pro/Max subscription\",\n\t\t\tavailable: true,\n\t\t},\n\t\t{\n\t\t\tid: \"github-copilot\",\n\t\t\tname: \"GitHub Copilot\",\n\t\t\tdescription: \"Use models via GitHub Copilot subscription\",\n\t\t\tavailable: true,\n\t\t},\n\t\t{\n\t\t\tid: \"google-gemini-cli\",\n\t\t\tname: \"Google Gemini CLI\",\n\t\t\tdescription: \"Free Gemini 2.0/2.5 models via Google Cloud\",\n\t\t\tavailable: true,\n\t\t},\n\t\t{\n\t\t\tid: \"google-antigravity\",\n\t\t\tname: \"Antigravity\",\n\t\t\tdescription: \"Free Gemini 3, Claude, GPT-OSS via Google Cloud\",\n\t\t\tavailable: true,\n\t\t},\n\t];\n}\n\n/**\n * Login with OAuth provider\n */\nexport async function login(\n\tprovider: OAuthProvider,\n\tonAuth: (info: OAuthAuthInfo) => void,\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>,\n\tonProgress?: (message: string) => void,\n): Promise<void> {\n\tswitch (provider) {\n\t\tcase \"anthropic\":\n\t\t\tawait loginAnthropic(\n\t\t\t\t(url) => onAuth({ url }),\n\t\t\t\tasync () => onPrompt({ message: \"Paste the authorization code below:\" }),\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"github-copilot\": {\n\t\t\tconst creds = await loginGitHubCopilot({\n\t\t\t\tonAuth: (url, instructions) => onAuth({ url, instructions }),\n\t\t\t\tonPrompt,\n\t\t\t\tonProgress,\n\t\t\t});\n\t\t\tsaveOAuthCredentials(\"github-copilot\", creds);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"google-gemini-cli\": {\n\t\t\tawait loginGeminiCli((info) => onAuth({ url: info.url, instructions: info.instructions }), onProgress);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"google-antigravity\": {\n\t\t\tawait loginAntigravity((info) => onAuth({ url: info.url, instructions: info.instructions }), onProgress);\n\t\t\tbreak;\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown OAuth provider: ${provider}`);\n\t}\n}\n\n/**\n * Logout from OAuth provider\n */\nexport async function logout(provider: OAuthProvider): Promise<void> {\n\tremoveOAuthCredentials(provider);\n}\n\n/**\n * Refresh OAuth token for provider.\n * Delegates to the ai package implementation.\n */\nexport async function refreshToken(provider: OAuthProvider): Promise<string> {\n\treturn refreshTokenFromAi(provider);\n}\n\n/**\n * Get OAuth token for provider (auto-refreshes if expired).\n */\nexport async function getOAuthToken(provider: OAuthProvider): Promise<string | null> {\n\treturn getOAuthApiKey(provider);\n}\n"]}