@open-mercato/shared 0.4.11-develop.1951.423f883d78 → 0.4.11-develop.1952.2b282328ac
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/dist/lib/ai/opencode-provider.js +3 -3
- package/dist/lib/ai/opencode-provider.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/ai/__tests__/opencode-provider.test.ts +40 -0
- package/src/lib/ai/opencode-provider.ts +3 -3
|
@@ -3,19 +3,19 @@ const OPEN_CODE_PROVIDERS = {
|
|
|
3
3
|
anthropic: {
|
|
4
4
|
id: "anthropic",
|
|
5
5
|
name: "Anthropic",
|
|
6
|
-
envKeys: ["ANTHROPIC_API_KEY"],
|
|
6
|
+
envKeys: ["ANTHROPIC_API_KEY", "OPENCODE_ANTHROPIC_API_KEY"],
|
|
7
7
|
defaultModel: "claude-haiku-4-5-20251001"
|
|
8
8
|
},
|
|
9
9
|
openai: {
|
|
10
10
|
id: "openai",
|
|
11
11
|
name: "OpenAI",
|
|
12
|
-
envKeys: ["OPENAI_API_KEY"],
|
|
12
|
+
envKeys: ["OPENAI_API_KEY", "OPENCODE_OPENAI_API_KEY"],
|
|
13
13
|
defaultModel: "gpt-4o-mini"
|
|
14
14
|
},
|
|
15
15
|
google: {
|
|
16
16
|
id: "google",
|
|
17
17
|
name: "Google",
|
|
18
|
-
envKeys: ["GOOGLE_GENERATIVE_AI_API_KEY"],
|
|
18
|
+
envKeys: ["GOOGLE_GENERATIVE_AI_API_KEY", "OPENCODE_GOOGLE_API_KEY"],
|
|
19
19
|
defaultModel: "gemini-3-flash"
|
|
20
20
|
}
|
|
21
21
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/ai/opencode-provider.ts"],
|
|
4
|
-
"sourcesContent": ["type EnvLookup = Record<string, string | undefined>\n\nexport type OpenCodeProviderId = 'anthropic' | 'openai' | 'google'\n\nexport type OpenCodeProviderDefinition = {\n id: OpenCodeProviderId\n name: string\n envKeys: readonly string[]\n defaultModel: string\n}\n\nexport const OPEN_CODE_PROVIDER_IDS = ['anthropic', 'openai', 'google'] as const\n\nexport const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDefinition> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n envKeys: ['ANTHROPIC_API_KEY'],\n defaultModel: 'claude-haiku-4-5-20251001',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n envKeys: ['OPENAI_API_KEY'],\n defaultModel: 'gpt-4o-mini',\n },\n google: {\n id: 'google',\n name: 'Google',\n envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY'],\n defaultModel: 'gemini-3-flash',\n },\n}\n\nexport type OpenCodeModelResolution = {\n modelId: string\n modelWithProvider: string\n source: 'override' | 'opencode_model' | 'default'\n}\n\nfunction normalizeToken(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim()\n return normalized.length > 0 ? normalized : null\n}\n\nfunction parseModelToken(token: string): { providerPrefix: string | null; modelId: string } {\n const slashIndex = token.indexOf('/')\n if (slashIndex <= 0) {\n return {\n providerPrefix: null,\n modelId: token,\n }\n }\n\n return {\n providerPrefix: token.slice(0, slashIndex).trim().toLowerCase(),\n modelId: token.slice(slashIndex + 1).trim(),\n }\n}\n\nexport function isOpenCodeProviderId(value: string): value is OpenCodeProviderId {\n return (OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(value)\n}\n\nexport function resolveOpenCodeProviderId(\n providerId: string | null | undefined,\n fallback: OpenCodeProviderId = 'anthropic',\n): OpenCodeProviderId {\n const normalized = normalizeToken(providerId)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n\n return fallback\n}\n\nexport function resolveFirstConfiguredOpenCodeProvider(\n options?: {\n env?: EnvLookup\n order?: readonly OpenCodeProviderId[]\n },\n): OpenCodeProviderId | null {\n const env = options?.env ?? process.env\n const order = options?.order ?? OPEN_CODE_PROVIDER_IDS\n\n for (const providerId of order) {\n if (isOpenCodeProviderConfigured(providerId, env)) {\n return providerId\n }\n }\n\n return null\n}\n\nexport function resolveOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string | null {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n const value = normalizeToken(env[key])\n if (value) {\n return value\n }\n }\n\n return null\n}\n\nexport function getOpenCodeProviderConfiguredEnvKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n if (normalizeToken(env[key])) {\n return key\n }\n }\n\n return provider.envKeys[0]\n}\n\nexport function isOpenCodeProviderConfigured(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): boolean {\n return resolveOpenCodeProviderApiKey(providerId, env) !== null\n}\n\nexport function resolveOpenCodeModel(\n providerId: OpenCodeProviderId,\n options?: {\n overrideModel?: string | null | undefined\n env?: EnvLookup\n },\n): OpenCodeModelResolution {\n const env = options?.env ?? process.env\n const overrideModel = normalizeToken(options?.overrideModel)\n const opencodeModel = normalizeToken(env.OPENCODE_MODEL)\n\n let source: OpenCodeModelResolution['source'] = 'default'\n let selectedModel = OPEN_CODE_PROVIDERS[providerId].defaultModel\n\n if (opencodeModel) {\n selectedModel = opencodeModel\n source = 'opencode_model'\n }\n\n if (overrideModel) {\n selectedModel = overrideModel\n source = 'override'\n }\n\n const parsed = parseModelToken(selectedModel)\n if (parsed.providerPrefix && parsed.providerPrefix !== providerId) {\n throw new Error(\n `Model \"${selectedModel}\" does not match configured provider \"${providerId}\"`,\n )\n }\n\n const modelId = normalizeToken(parsed.modelId)\n if (!modelId) {\n throw new Error(`Model \"${selectedModel}\" is invalid`)\n }\n\n return {\n modelId,\n modelWithProvider: `${providerId}/${modelId}`,\n source,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAWO,MAAM,yBAAyB,CAAC,aAAa,UAAU,QAAQ;AAE/D,MAAM,sBAA8E;AAAA,EACzF,WAAW;AAAA,IACT,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,
|
|
4
|
+
"sourcesContent": ["type EnvLookup = Record<string, string | undefined>\n\nexport type OpenCodeProviderId = 'anthropic' | 'openai' | 'google'\n\nexport type OpenCodeProviderDefinition = {\n id: OpenCodeProviderId\n name: string\n envKeys: readonly string[]\n defaultModel: string\n}\n\nexport const OPEN_CODE_PROVIDER_IDS = ['anthropic', 'openai', 'google'] as const\n\nexport const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDefinition> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n envKeys: ['ANTHROPIC_API_KEY', 'OPENCODE_ANTHROPIC_API_KEY'],\n defaultModel: 'claude-haiku-4-5-20251001',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],\n defaultModel: 'gpt-4o-mini',\n },\n google: {\n id: 'google',\n name: 'Google',\n envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY', 'OPENCODE_GOOGLE_API_KEY'],\n defaultModel: 'gemini-3-flash',\n },\n}\n\nexport type OpenCodeModelResolution = {\n modelId: string\n modelWithProvider: string\n source: 'override' | 'opencode_model' | 'default'\n}\n\nfunction normalizeToken(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim()\n return normalized.length > 0 ? normalized : null\n}\n\nfunction parseModelToken(token: string): { providerPrefix: string | null; modelId: string } {\n const slashIndex = token.indexOf('/')\n if (slashIndex <= 0) {\n return {\n providerPrefix: null,\n modelId: token,\n }\n }\n\n return {\n providerPrefix: token.slice(0, slashIndex).trim().toLowerCase(),\n modelId: token.slice(slashIndex + 1).trim(),\n }\n}\n\nexport function isOpenCodeProviderId(value: string): value is OpenCodeProviderId {\n return (OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(value)\n}\n\nexport function resolveOpenCodeProviderId(\n providerId: string | null | undefined,\n fallback: OpenCodeProviderId = 'anthropic',\n): OpenCodeProviderId {\n const normalized = normalizeToken(providerId)?.toLowerCase()\n if (normalized && isOpenCodeProviderId(normalized)) {\n return normalized\n }\n\n return fallback\n}\n\nexport function resolveFirstConfiguredOpenCodeProvider(\n options?: {\n env?: EnvLookup\n order?: readonly OpenCodeProviderId[]\n },\n): OpenCodeProviderId | null {\n const env = options?.env ?? process.env\n const order = options?.order ?? OPEN_CODE_PROVIDER_IDS\n\n for (const providerId of order) {\n if (isOpenCodeProviderConfigured(providerId, env)) {\n return providerId\n }\n }\n\n return null\n}\n\nexport function resolveOpenCodeProviderApiKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string | null {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n const value = normalizeToken(env[key])\n if (value) {\n return value\n }\n }\n\n return null\n}\n\nexport function getOpenCodeProviderConfiguredEnvKey(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): string {\n const provider = OPEN_CODE_PROVIDERS[providerId]\n for (const key of provider.envKeys) {\n if (normalizeToken(env[key])) {\n return key\n }\n }\n\n return provider.envKeys[0]\n}\n\nexport function isOpenCodeProviderConfigured(\n providerId: OpenCodeProviderId,\n env: EnvLookup = process.env,\n): boolean {\n return resolveOpenCodeProviderApiKey(providerId, env) !== null\n}\n\nexport function resolveOpenCodeModel(\n providerId: OpenCodeProviderId,\n options?: {\n overrideModel?: string | null | undefined\n env?: EnvLookup\n },\n): OpenCodeModelResolution {\n const env = options?.env ?? process.env\n const overrideModel = normalizeToken(options?.overrideModel)\n const opencodeModel = normalizeToken(env.OPENCODE_MODEL)\n\n let source: OpenCodeModelResolution['source'] = 'default'\n let selectedModel = OPEN_CODE_PROVIDERS[providerId].defaultModel\n\n if (opencodeModel) {\n selectedModel = opencodeModel\n source = 'opencode_model'\n }\n\n if (overrideModel) {\n selectedModel = overrideModel\n source = 'override'\n }\n\n const parsed = parseModelToken(selectedModel)\n if (parsed.providerPrefix && parsed.providerPrefix !== providerId) {\n throw new Error(\n `Model \"${selectedModel}\" does not match configured provider \"${providerId}\"`,\n )\n }\n\n const modelId = normalizeToken(parsed.modelId)\n if (!modelId) {\n throw new Error(`Model \"${selectedModel}\" is invalid`)\n }\n\n return {\n modelId,\n modelWithProvider: `${providerId}/${modelId}`,\n source,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.11-develop.
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.11-develop.1952.2b282328ac'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -82,4 +82,44 @@ describe('opencode provider helpers', () => {
|
|
|
82
82
|
}),
|
|
83
83
|
).toThrow('does not match configured provider')
|
|
84
84
|
})
|
|
85
|
+
|
|
86
|
+
it('resolves API key from OPENCODE_ANTHROPIC_API_KEY fallback', () => {
|
|
87
|
+
const provider = resolveFirstConfiguredOpenCodeProvider({
|
|
88
|
+
env: {
|
|
89
|
+
ANTHROPIC_API_KEY: '',
|
|
90
|
+
OPENCODE_ANTHROPIC_API_KEY: 'opencode-anthropic-key',
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
expect(provider).toBe('anthropic')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('primary key takes precedence over OPENCODE_* fallback', () => {
|
|
97
|
+
const provider = resolveFirstConfiguredOpenCodeProvider({
|
|
98
|
+
env: {
|
|
99
|
+
ANTHROPIC_API_KEY: 'primary-key',
|
|
100
|
+
OPENCODE_ANTHROPIC_API_KEY: 'opencode-anthropic-key',
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
expect(provider).toBe('anthropic')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('OPENCODE_* fallback works for openai provider', () => {
|
|
107
|
+
const provider = resolveFirstConfiguredOpenCodeProvider({
|
|
108
|
+
env: {
|
|
109
|
+
OPENAI_API_KEY: '',
|
|
110
|
+
OPENCODE_OPENAI_API_KEY: 'opencode-openai-key',
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
expect(provider).toBe('openai')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('OPENCODE_* fallback works for google provider', () => {
|
|
117
|
+
const provider = resolveFirstConfiguredOpenCodeProvider({
|
|
118
|
+
env: {
|
|
119
|
+
GOOGLE_GENERATIVE_AI_API_KEY: '',
|
|
120
|
+
OPENCODE_GOOGLE_API_KEY: 'opencode-google-key',
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
expect(provider).toBe('google')
|
|
124
|
+
})
|
|
85
125
|
})
|
|
@@ -15,19 +15,19 @@ export const OPEN_CODE_PROVIDERS: Record<OpenCodeProviderId, OpenCodeProviderDef
|
|
|
15
15
|
anthropic: {
|
|
16
16
|
id: 'anthropic',
|
|
17
17
|
name: 'Anthropic',
|
|
18
|
-
envKeys: ['ANTHROPIC_API_KEY'],
|
|
18
|
+
envKeys: ['ANTHROPIC_API_KEY', 'OPENCODE_ANTHROPIC_API_KEY'],
|
|
19
19
|
defaultModel: 'claude-haiku-4-5-20251001',
|
|
20
20
|
},
|
|
21
21
|
openai: {
|
|
22
22
|
id: 'openai',
|
|
23
23
|
name: 'OpenAI',
|
|
24
|
-
envKeys: ['OPENAI_API_KEY'],
|
|
24
|
+
envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],
|
|
25
25
|
defaultModel: 'gpt-4o-mini',
|
|
26
26
|
},
|
|
27
27
|
google: {
|
|
28
28
|
id: 'google',
|
|
29
29
|
name: 'Google',
|
|
30
|
-
envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY'],
|
|
30
|
+
envKeys: ['GOOGLE_GENERATIVE_AI_API_KEY', 'OPENCODE_GOOGLE_API_KEY'],
|
|
31
31
|
defaultModel: 'gemini-3-flash',
|
|
32
32
|
},
|
|
33
33
|
}
|