fix(install-e2e): put the mock endpoint in config.yaml where the desktop reads it

The smoke writer fed the endpoint to `model.provider: custom` through .env
OPENAI_BASE_URL, which runtime_provider's bare-`custom` trust path never reads
on v2026.8.31 -- that tree's own comment: "OPENAI_BASE_URL env var is no longer
consulted -- config.yaml is the single source of truth for endpoint URLs". So
the app's readiness check (setup.runtime_check) resolved `custom` to no endpoint
and booted behind its onboarding overlay ("No usable credentials found for
custom. setup.status reports configured credentials, but runtime resolution
still failed"), which is why every installer-script+desktop leg failed at its
OLD checkpoint and why the desktop smoke's trial click never landed.

Reproduced against the real ladder before changing anything: with the writer's
config, resolve_runtime_provider(requested='custom') raises
"provider 'custom' resolved without credentials (no endpoint or API key
configured)"; adding
model.base_url makes it resolve to the mock URL (api_key no-key-required, the
loopback bypass). A custom_providers entry alone is not enough, so the endpoint
now goes where both vintages read it. The named/custom:<name> entry and the .env
pair stay for the consumers that use them.
This commit is contained in:
ethernet
2026-09-17 15:46:38 -04:00
parent a91a8b0f62
commit 5265ebed78
2 changed files with 17 additions and 13 deletions

View File

@@ -52,11 +52,11 @@ test('provider reconfiguration preserves feed, plugins, history and explicit fix
writeEnvFile(home)
expect(yaml.load(fs.readFileSync(path.join(home, 'config.yaml'), 'utf8'))).toMatchObject({
updates: feed, plugins: { witness: true },
model: { provider: 'custom', temperature: 0.7, context_length: 12000 },
model: { provider: 'custom', base_url: 'http://127.0.0.1:9001/v1', temperature: 0.7, context_length: 12000 },
providers: {},
// Exactly one entry, repointed at the second URL: a bare `custom` resolves
// from this list on every vintage, and reconfiguring must not stack
// duplicates.
// Exactly one entry, repointed at the second URL: reconfiguring must not
// stack duplicates. The readiness ladder reads model.base_url (above),
// not this list; the entry names the same endpoint for `custom:<name>`.
custom_providers: [{ name: 'Mock', base_url: 'http://127.0.0.1:9001/v1', key_env: 'OPENAI_API_KEY' }],
auxiliary: { title_generation: { enabled: false } }, approvals: { mode: 'smart' }, display: { interim_assistant_messages: true },
})

View File

@@ -44,15 +44,19 @@ export function writeMockProviderConfig(
const display = section.parse(extraDisplayConfig ? yaml.load(extraDisplayConfig) ?? {} : {})
const merged = {
...config,
model: { ...config.model, default: 'mock-model', provider: 'custom', context_length: modelContextLength ?? 64000 },
// Two shapes, one endpoint. `providers:` stays untouched (a provider named
// 'mock' only resolved on trees that had registered such a profile, so
// older refs died with "Unknown provider 'mock'"). A bare `custom` resolves
// from `custom_providers:` on every vintage -- v2026.8.31's desktop
// readiness check reports "No usable credentials found for custom ...
// runtime resolution still failed" without an entry here -- while the .env
// OPENAI_BASE_URL/OPENAI_API_KEY pair (writeEnvFile) is how trees that
// resolve the endpoint from the environment reach the same URL.
// The endpoint must live in config.yaml: runtime_provider's bare-`custom`
// trust path reads the CONFIG base_url, and v2026.8.31 says so outright --
// "OPENAI_BASE_URL env var is no longer consulted -- config.yaml is the
// single source of truth for endpoint URLs". Without it `custom` resolves to
// no endpoint and the app boots on its onboarding overlay ("No usable
// credentials found for custom"), which is what every +desktop smoke leg
// hit. The `custom_providers` entry names the same endpoint for the
// named/`custom:<name>` form; the .env OPENAI_BASE_URL/OPENAI_API_KEY pair
// (writeEnvFile) stays for the trees that resolve the endpoint from the
// environment. `providers:` stays untouched (a provider named 'mock' only
// resolved on trees that had registered such a profile, so older refs died
// with "Unknown provider 'mock'").
model: { ...config.model, default: 'mock-model', provider: 'custom', base_url: `${url}/v1`, context_length: modelContextLength ?? 64000 },
providers: { ...config.providers },
custom_providers: [
...(config.custom_providers ?? []).filter(entry => entry?.name !== MOCK_PROVIDER_NAME),