(cwd: string)
| 181 | }, |
| 182 | |
| 183 | async readAiConfig(cwd: string): Promise<WorkspaceAiCred | null> { |
| 184 | const raw = await readWorkspaceFile(cwd, OPENCODE_CONFIG_PATH); |
| 185 | if (raw === null) return null; |
| 186 | let parsed: Record<string, unknown>; |
| 187 | try { |
| 188 | parsed = JSON.parse(raw) as Record<string, unknown>; |
| 189 | } catch { |
| 190 | return null; |
| 191 | } |
| 192 | const provider = (parsed['provider'] ?? {}) as Record<string, unknown>; |
| 193 | const ws = (provider[OPENCODE_PROVIDER_NAME] ?? {}) as Record<string, unknown>; |
| 194 | const options = (ws['options'] ?? {}) as Record<string, unknown>; |
| 195 | const baseUrl = typeof options['baseURL'] === 'string' ? (options['baseURL'] as string) : null; |
| 196 | const apiKey = typeof options['apiKey'] === 'string' ? (options['apiKey'] as string) : null; |
| 197 | // Top-level model is "<provider>/<id>"; surface just the id back to the modal. |
| 198 | let model: string | null = null; |
| 199 | const top = parsed['model']; |
| 200 | if (typeof top === 'string') { |
| 201 | const slash = top.indexOf('/'); |
| 202 | model = slash >= 0 ? top.slice(slash + 1) : top; |
| 203 | } |
| 204 | const models = (ws['models'] ?? {}) as Record<string, Record<string, unknown>>; |
| 205 | const modelConfig = model ? models[model] : undefined; |
| 206 | const limit = (modelConfig?.['limit'] ?? {}) as Record<string, unknown>; |
| 207 | const contextWindow = positiveNumber(limit['context'] as number | null | undefined); |
| 208 | if (baseUrl === null && apiKey === null && model === null) return null; |
| 209 | // Reverse the npm package back to the wire shape. |
| 210 | const npm = typeof ws['npm'] === 'string' ? (ws['npm'] as string) : ''; |
| 211 | const wireShape = npm === '@ai-sdk/anthropic' ? 'anthropic' as const |
| 212 | : npm === '@ai-sdk/openai' ? 'openai-responses' as const |
| 213 | : 'openai-chat' as const; |
| 214 | return { baseUrl, apiKey, model, wireShape, ...(contextWindow ? { contextWindow } : {}) }; |
| 215 | }, |
| 216 | |
| 217 | /** |
| 218 | * List opencode sessions for THIS workspace cwd, for the watcher's post-spawn |
nothing calls this directly
no test coverage detected