(cwd: string, cred: WorkspaceAiCred)
| 132 | }, |
| 133 | |
| 134 | async writeAiConfig(cwd: string, cred: WorkspaceAiCred): Promise<void> { |
| 135 | const hasProvider = !!(cred.baseUrl || cred.apiKey || cred.model); |
| 136 | if (!hasProvider) { |
| 137 | // Reset: drop the redirected agent dir → Pi falls back to global config. |
| 138 | await rm(join(cwd, PI_AGENT_DIR), { recursive: true, force: true }); |
| 139 | return; |
| 140 | } |
| 141 | |
| 142 | // Pi's `api` field is the wire shape: anthropic-messages / openai-responses / |
| 143 | // openai-completions (Chat Completions, the default for CN/local gateways). |
| 144 | const api = cred.wireShape === 'anthropic' ? 'anthropic-messages' |
| 145 | : cred.wireShape === 'openai-responses' ? 'openai-responses' |
| 146 | : 'openai-completions'; |
| 147 | const provider: Record<string, unknown> = { |
| 148 | name: 'OpenAlice workspace provider', |
| 149 | api, |
| 150 | }; |
| 151 | if (cred.baseUrl) provider['baseUrl'] = cred.baseUrl; |
| 152 | // Key written directly into the workspace file (same trust model as codex's |
| 153 | // .codex/env.json / opencode's opencode.json). |
| 154 | if (cred.apiKey) provider['apiKey'] = cred.apiKey; |
| 155 | // Pi's custom model registry otherwise falls back to 128k. OpenAlice writes |
| 156 | // the context window when known so long-context models do not compact early. |
| 157 | if (cred.model) { |
| 158 | const model: Record<string, unknown> = { id: cred.model }; |
| 159 | const contextWindow = positiveNumber(cred.contextWindow); |
| 160 | if (contextWindow !== null) model['contextWindow'] = contextWindow; |
| 161 | provider['models'] = [model]; |
| 162 | } |
| 163 | |
| 164 | await writeWorkspaceFile( |
| 165 | cwd, |
| 166 | PI_MODELS_PATH, |
| 167 | JSON.stringify({ providers: { [PI_PROVIDER_NAME]: provider } }, null, 2) + '\n', |
| 168 | ); |
| 169 | |
| 170 | // Pin the default provider/model so spawns use the workspace provider |
| 171 | // without --provider/--model. Lives in the SAME .pi-agent dir so reset |
| 172 | // (rm .pi-agent) tears both down together. |
| 173 | const settings: Record<string, unknown> = { defaultProvider: PI_PROVIDER_NAME }; |
| 174 | if (cred.model) settings['defaultModel'] = cred.model; |
| 175 | await writeWorkspaceFile(cwd, PI_SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n'); |
| 176 | }, |
| 177 | |
| 178 | async readAiConfig(cwd: string): Promise<WorkspaceAiCred | null> { |
| 179 | const raw = await readWorkspaceFile(cwd, PI_MODELS_PATH); |
nothing calls this directly
no test coverage detected