( connection: ConnectionEffectConnection, apiKey: string, model: string | undefined, fetchFn: ConnectionEffectFetch | undefined, t0: number, timeoutMs = CONNECTION_TEST_TIMEOUT_MS, )
| 120 | } |
| 121 | |
| 122 | async function testConnectionStrict( |
| 123 | connection: ConnectionEffectConnection, |
| 124 | apiKey: string, |
| 125 | model: string | undefined, |
| 126 | fetchFn: ConnectionEffectFetch | undefined, |
| 127 | t0: number, |
| 128 | timeoutMs = CONNECTION_TEST_TIMEOUT_MS, |
| 129 | ): Promise<ConnectionTestResult> { |
| 130 | const defaults = PROVIDER_DEFAULTS[connection.providerType]; |
| 131 | // Unknown providerType → can't pick an auth path or fallback model. Return a |
| 132 | // clear failure rather than crashing. Mirrors `isFakeBackend`. |
| 133 | if (!defaults) { |
| 134 | return { ok: false, errorMessage: `Unknown provider type "${connection.providerType}"` }; |
| 135 | } |
| 136 | const auth = defaults.authKind; |
| 137 | const secret = auth === 'none' ? '' : apiKey; |
| 138 | const testModel = resolveConnectionTestModel(connection, model, defaults.fallbackModels); |
| 139 | |
| 140 | if (!testModel) { |
| 141 | return { ok: false, errorMessage: 'No model to test' }; |
| 142 | } |
| 143 | if (connection.providerType === 'opencode-free' && !model?.trim()) { |
| 144 | const candidates = [ |
| 145 | ...new Set([...connectionEnabledModelIds(connection), ...defaults.fallbackModels]), |
| 146 | ]; |
| 147 | let lastFailure: ConnectionTestResult | undefined; |
| 148 | for (let index = 0; index < candidates.length; index += 1) { |
| 149 | const candidate = candidates[index]!; |
| 150 | const remainingMs = timeoutMs - (Date.now() - t0); |
| 151 | if (remainingMs <= 0) { |
| 152 | return connectionTestFailure(new ConnectionEffectFetchError('timeout'), t0); |
| 153 | } |
| 154 | const remainingCandidates = candidates.length - index; |
| 155 | const attemptTimeoutMs = Math.max(1, Math.floor(remainingMs / remainingCandidates)); |
| 156 | try { |
| 157 | const result = await testConnectionModel( |
| 158 | connection, |
| 159 | secret, |
| 160 | candidate, |
| 161 | fetchFn, |
| 162 | t0, |
| 163 | attemptTimeoutMs, |
| 164 | ); |
| 165 | if (result.ok) return result; |
| 166 | lastFailure = result; |
| 167 | } catch (error) { |
| 168 | lastFailure = connectionTestFailure(error, t0, true); |
| 169 | } |
| 170 | } |
| 171 | return lastFailure ?? connectionTestFailure(new ConnectionEffectFetchError('timeout'), t0); |
| 172 | } |
| 173 | |
| 174 | return await testConnectionModel(connection, secret, testModel, fetchFn, t0, timeoutMs); |
| 175 | } |
| 176 | |
| 177 | async function testConnectionModel( |
| 178 | connection: ConnectionEffectConnection, |
no test coverage detected