From a2a196dba7a1cdb44bb5721521337b8df5d3669b Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Thu, 27 Aug 2026 23:32:39 -0700 Subject: [PATCH 1/3] Preserve settings provenance through onboarding --- src/config.test.ts | 4 + src/config/index.ts | 12 +++ src/tui/onboarding.test.ts | 213 +++++++++++++++++++++++++++++++++++++ src/tui/onboarding.ts | 16 ++- 4 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 src/tui/onboarding.test.ts diff --git a/src/config.test.ts b/src/config.test.ts index 28a62a082..82afb48ca 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -227,6 +227,8 @@ describe("loadConfig", () => { expect(result.task).toBe("do it"); expect(result.providerError).toMatch(/missing/); expect(result.globalSettingsPath).toBe(NO_SETTINGS); + expect(result.cliConfigPath).toBeUndefined(); + expect(result.settingsSource).toBe("programmatic"); } } finally { await rm(cwd, { recursive: true, force: true }); @@ -267,6 +269,8 @@ describe("loadConfig", () => { expect(result.configured).toBe(false); if (result.configured === false) { expect(result.globalSettingsPath).toBe(configPath); + expect(result.cliConfigPath).toBe(configPath); + expect(result.settingsSource).toBe("cli"); } } finally { await rm(cwd, { recursive: true, force: true }); diff --git a/src/config/index.ts b/src/config/index.ts index 3a44057be..0fdfe1301 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -422,6 +422,10 @@ export interface UnconfiguredConfig { director?: DirectorId; // Path where the onboarding flow should write the new settings. globalSettingsPath: string; + /** Original CLI path, present only when --config selected the write target. */ + cliConfigPath?: string; + /** Provenance needed to preserve OAuth composition during onboarding reload. */ + settingsSource: "default" | "cli" | "programmatic"; // The original error message, used for non-TUI (exec) error output. providerError: string; /** @@ -717,6 +721,12 @@ export async function loadConfig( // same file, not the global default. Prefer configPath, then the caller // override, then the real global default. const effectiveSettingsPath = configPath ?? options.globalSettingsPath ?? globalSettingsPath(); + const settingsSource = + configPath !== undefined + ? "cli" + : options.globalSettingsPath !== undefined + ? "programmatic" + : "default"; const task = positional.join(" ").trim(); let resolved: ResolvedProvider; @@ -739,6 +749,8 @@ export async function loadConfig( command, ...(director !== undefined ? { director } : {}), globalSettingsPath: effectiveSettingsPath, + ...(configPath !== undefined ? { cliConfigPath: configPath } : {}), + settingsSource, providerError: err instanceof Error ? err.message : String(err), // Keep diagnostics even when provider setup fails early so junk local // files still reach stderr (exec) / banner (TUI after onboarding). diff --git a/src/tui/onboarding.test.ts b/src/tui/onboarding.test.ts new file mode 100644 index 000000000..267264ef9 --- /dev/null +++ b/src/tui/onboarding.test.ts @@ -0,0 +1,213 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import type { Config, UnconfiguredConfig } from "../config/index.js"; +import type { ProviderSetupConfig } from "./provider-setup.js"; +import { withMockedModule } from "../../tests/helpers/mock-module.js"; + +let testHome = ""; +let setup: (config: ProviderSetupConfig) => Promise = async () => {}; +let tuiConfig: Config | undefined; + +await withMockedModule(import.meta.resolve("node:os"), (real: typeof import("node:os")) => ({ + ...real, + homedir: () => testHome, +})); +await withMockedModule( + import.meta.resolve("./provider-setup.js"), + (real: typeof import("./provider-setup.js")) => ({ + ...real, + runProviderSetup: async (config: ProviderSetupConfig) => { + await setup(config); + return true; + }, + }), +); +await withMockedModule( + import.meta.resolve("./runner.js"), + (real: typeof import("./runner.js")) => ({ + ...real, + runTUI: async (config: Config) => { + tuiConfig = config; + return 0; + }, + }), +); + +const { loadConfig } = await import("../config/index.js"); +const { runOnboarding } = await import("./onboarding.js"); + +async function unconfiguredCLIConfig(cwd: string, configPath: string): Promise { + const config = await loadConfig(["--cwd", cwd, "--config", configPath], { + allowUnconfigured: true, + }); + if (config.configured) throw new Error("Expected onboarding config"); + return config; +} + +async function unconfiguredProgrammaticConfig( + cwd: string, + configPath: string, +): Promise { + const config = await loadConfig(["--cwd", cwd], { + globalSettingsPath: configPath, + allowUnconfigured: true, + }); + if (config.configured) throw new Error("Expected onboarding config"); + return config; +} + +afterEach(() => { + setup = async () => {}; + tuiConfig = undefined; +}); + +describe("runOnboarding settings source", () => { + test("reloads CLI --config with the selected OAuth profile projection", async () => { + testHome = await mkdtemp(join(tmpdir(), "corbits-onboarding-oauth-home-")); + const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-oauth-cwd-")); + const configPath = join(cwd, "custom-settings.json"); + try { + await mkdir(join(testHome, ".corbits"), { recursive: true }); + await writeFile(configPath, JSON.stringify({ providers: {} })); + const config = await unconfiguredCLIConfig(cwd, configPath); + + setup = async ({ onSubmit }) => { + await writeFile( + join(testHome, ".corbits", "xai-auth.json"), + JSON.stringify({ + profiles: { + work: { + name: "work", + tokens: { + access: "test-access-token", + refresh: "test-refresh-token", + expiresAt: Date.now() + 3_600_000, + }, + createdAt: Date.now(), + }, + }, + }), + ); + await onSubmit( + { + name: "xai/work", + baseURL: "https://api.x.ai/v1", + apiKey: "", + model: "grok-4", + oauthProfile: "work", + }, + () => {}, + { + skipValidation: true, + oauth: { kind: "xai", profile: "work", providerName: "xai/work" }, + }, + ); + }; + + expect(await runOnboarding(config)).toBe(0); + expect(tuiConfig?.providerName).toBe("xai/work"); + expect(tuiConfig?.model).toBe("grok-4"); + expect(tuiConfig?.globalSettingsPath).toBe(configPath); + expect(tuiConfig?.providers.some((provider) => provider.name === "xai/work")).toBe(true); + + const persisted = JSON.parse(await readFile(configPath, "utf8")) as { + defaultProvider?: string; + providers?: Record; + }; + expect(persisted.defaultProvider).toBe("xai/work"); + expect(persisted.providers).toEqual({}); + } finally { + await rm(testHome, { recursive: true, force: true }); + await rm(cwd, { recursive: true, force: true }); + } + }); + + test("keeps API-key onboarding writes and reloads on CLI --config", async () => { + testHome = await mkdtemp(join(tmpdir(), "corbits-onboarding-key-home-")); + const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-key-cwd-")); + const configPath = join(cwd, "custom-settings.json"); + try { + await writeFile(configPath, JSON.stringify({ providers: {} })); + const config = await unconfiguredCLIConfig(cwd, configPath); + + setup = async ({ onSubmit }) => { + await onSubmit( + { + name: "custom", + baseURL: "https://provider.example.com/v1", + apiKey: "test-key", + model: "test-model", + oauthProfile: "", + }, + () => {}, + { skipValidation: true }, + ); + }; + + expect(await runOnboarding(config)).toBe(0); + expect(tuiConfig?.providerName).toBe("custom"); + expect(tuiConfig?.model).toBe("test-model"); + expect(tuiConfig?.globalSettingsPath).toBe(configPath); + + const persisted = JSON.parse(await readFile(configPath, "utf8")) as { + providers?: Record; + }; + expect(persisted.providers).toHaveProperty("custom"); + } finally { + await rm(testHome, { recursive: true, force: true }); + await rm(cwd, { recursive: true, force: true }); + } + }); + + test("keeps programmatic settings isolated from home OAuth profiles after reload", async () => { + testHome = await mkdtemp(join(tmpdir(), "corbits-onboarding-isolated-home-")); + const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-isolated-cwd-")); + const configPath = join(cwd, "isolated-settings.json"); + try { + await mkdir(join(testHome, ".corbits"), { recursive: true }); + await writeFile( + join(testHome, ".corbits", "xai-auth.json"), + JSON.stringify({ + profiles: { + hidden: { + name: "hidden", + tokens: { + access: "hidden-access-token", + refresh: "hidden-refresh-token", + expiresAt: Date.now() + 3_600_000, + }, + createdAt: Date.now(), + }, + }, + }), + ); + await writeFile(configPath, JSON.stringify({ providers: {} })); + const config = await unconfiguredProgrammaticConfig(cwd, configPath); + + setup = async ({ onSubmit }) => { + await onSubmit( + { + name: "isolated", + baseURL: "https://isolated.example.com/v1", + apiKey: "isolated-key", + model: "isolated-model", + oauthProfile: "", + }, + () => {}, + { skipValidation: true }, + ); + }; + + expect(await runOnboarding(config)).toBe(0); + expect(tuiConfig?.providerName).toBe("isolated"); + expect(tuiConfig?.providers.map((provider) => provider.name)).toEqual(["isolated"]); + expect(tuiConfig?.globalSettingsPath).toBe(configPath); + } finally { + await rm(testHome, { recursive: true, force: true }); + await rm(cwd, { recursive: true, force: true }); + } + }); +}); diff --git a/src/tui/onboarding.ts b/src/tui/onboarding.ts index 61a46394e..06cccbd60 100644 --- a/src/tui/onboarding.ts +++ b/src/tui/onboarding.ts @@ -37,18 +37,16 @@ export async function runOnboarding(config: UnconfiguredConfig): Promise } const argv: string[] = ["--cwd", config.cwd]; + if (config.cliConfigPath !== undefined) argv.push("--config", config.cliConfigPath); if (config.dangerouslySkipPermissions) argv.push("--dangerously-skip-permissions"); if (config.force) argv.push("--force"); if (config.task.length > 0) argv.push(config.task); - // An explicit globalSettingsPath tells loadConfig it is on a controlled - // settings source and suppresses the home-level OAuth profile projection. - // Passing the default path would therefore hide a provider the operator just - // signed into, so it is only forwarded when it really is an override. - const overridesSettingsPath = settingsPath !== globalSettingsPath(); - const newConfig = await loadConfig( - argv, - overridesSettingsPath ? { globalSettingsPath: settingsPath } : {}, - ); + // Recreate the original source rather than comparing paths: CLI --config + // composes with home OAuth profiles, while a programmatic override is an + // isolated settings source even if it names the default settings path. + const loadOptions = + config.settingsSource === "programmatic" ? { globalSettingsPath: settingsPath } : {}; + const newConfig = await loadConfig(argv, loadOptions); return runTUI(newConfig); } From 4ca260ce73d55195e8aa13979ea47d2dda0b1567 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Thu, 27 Aug 2026 23:56:48 -0700 Subject: [PATCH 2/3] Preserve OAuth isolation when config sources overlap --- src/config.test.ts | 2 ++ src/config/index.ts | 5 ++- src/tui/onboarding.test.ts | 74 ++++++++++++++++++++++++++++++++++++-- src/tui/onboarding.ts | 8 ++--- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index 82afb48ca..079f894ed 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -229,6 +229,7 @@ describe("loadConfig", () => { expect(result.globalSettingsPath).toBe(NO_SETTINGS); expect(result.cliConfigPath).toBeUndefined(); expect(result.settingsSource).toBe("programmatic"); + expect(result.programmaticSettingsPath).toBe(true); } } finally { await rm(cwd, { recursive: true, force: true }); @@ -271,6 +272,7 @@ describe("loadConfig", () => { expect(result.globalSettingsPath).toBe(configPath); expect(result.cliConfigPath).toBe(configPath); expect(result.settingsSource).toBe("cli"); + expect(result.programmaticSettingsPath).toBe(false); } } finally { await rm(cwd, { recursive: true, force: true }); diff --git a/src/config/index.ts b/src/config/index.ts index 0fdfe1301..03a83e482 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -424,8 +424,10 @@ export interface UnconfiguredConfig { globalSettingsPath: string; /** Original CLI path, present only when --config selected the write target. */ cliConfigPath?: string; - /** Provenance needed to preserve OAuth composition during onboarding reload. */ + /** Source that selected the settings write target. */ settingsSource: "default" | "cli" | "programmatic"; + /** Whether the caller requested an OAuth-isolated programmatic settings load. */ + programmaticSettingsPath: boolean; // The original error message, used for non-TUI (exec) error output. providerError: string; /** @@ -751,6 +753,7 @@ export async function loadConfig( globalSettingsPath: effectiveSettingsPath, ...(configPath !== undefined ? { cliConfigPath: configPath } : {}), settingsSource, + programmaticSettingsPath: options.globalSettingsPath !== undefined, providerError: err instanceof Error ? err.message : String(err), // Keep diagnostics even when provider setup fails early so junk local // files still reach stderr (exec) / banner (TUI after onboarding). diff --git a/src/tui/onboarding.test.ts b/src/tui/onboarding.test.ts index 267264ef9..dba2fa07d 100644 --- a/src/tui/onboarding.test.ts +++ b/src/tui/onboarding.test.ts @@ -59,6 +59,19 @@ async function unconfiguredProgrammaticConfig( return config; } +async function unconfiguredCLIAndProgrammaticConfig( + cwd: string, + cliConfigPath: string, + programmaticConfigPath: string, +): Promise { + const config = await loadConfig(["--cwd", cwd, "--config", cliConfigPath], { + globalSettingsPath: programmaticConfigPath, + allowUnconfigured: true, + }); + if (config.configured) throw new Error("Expected onboarding config"); + return config; +} + afterEach(() => { setup = async () => {}; tuiConfig = undefined; @@ -162,10 +175,67 @@ describe("runOnboarding settings source", () => { } }); - test("keeps programmatic settings isolated from home OAuth profiles after reload", async () => { + test("keeps OAuth profiles isolated when CLI and programmatic paths are both supplied", async () => { + testHome = await mkdtemp(join(tmpdir(), "corbits-onboarding-both-home-")); + const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-both-cwd-")); + const cliConfigPath = join(cwd, "cli-settings.json"); + const programmaticConfigPath = join(cwd, "programmatic-settings.json"); + try { + await mkdir(join(testHome, ".corbits"), { recursive: true }); + await writeFile( + join(testHome, ".corbits", "xai-auth.json"), + JSON.stringify({ + profiles: { + hidden: { + name: "hidden", + tokens: { + access: "hidden-access-token", + refresh: "hidden-refresh-token", + expiresAt: Date.now() + 3_600_000, + }, + createdAt: Date.now(), + }, + }, + }), + ); + await writeFile(cliConfigPath, JSON.stringify({ providers: {} })); + await writeFile(programmaticConfigPath, JSON.stringify({ providers: {} })); + const config = await unconfiguredCLIAndProgrammaticConfig( + cwd, + cliConfigPath, + programmaticConfigPath, + ); + expect(config.settingsSource).toBe("cli"); + expect(config.programmaticSettingsPath).toBe(true); + + setup = async ({ onSubmit }) => { + await onSubmit( + { + name: "isolated", + baseURL: "https://isolated.example.com/v1", + apiKey: "isolated-key", + model: "isolated-model", + oauthProfile: "", + }, + () => {}, + { skipValidation: true }, + ); + }; + + expect(await runOnboarding(config)).toBe(0); + expect(tuiConfig?.providerName).toBe("isolated"); + expect(tuiConfig?.providers.map((provider) => provider.name)).toEqual(["isolated"]); + expect(tuiConfig?.globalSettingsPath).toBe(cliConfigPath); + } finally { + await rm(testHome, { recursive: true, force: true }); + await rm(cwd, { recursive: true, force: true }); + } + }); + + test("keeps a default-path programmatic override isolated after reload", async () => { testHome = await mkdtemp(join(tmpdir(), "corbits-onboarding-isolated-home-")); const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-isolated-cwd-")); - const configPath = join(cwd, "isolated-settings.json"); + const configPath = join(testHome, ".corbits", "settings.json"); try { await mkdir(join(testHome, ".corbits"), { recursive: true }); await writeFile( diff --git a/src/tui/onboarding.ts b/src/tui/onboarding.ts index 06cccbd60..9d3416672 100644 --- a/src/tui/onboarding.ts +++ b/src/tui/onboarding.ts @@ -42,11 +42,9 @@ export async function runOnboarding(config: UnconfiguredConfig): Promise if (config.force) argv.push("--force"); if (config.task.length > 0) argv.push(config.task); - // Recreate the original source rather than comparing paths: CLI --config - // composes with home OAuth profiles, while a programmatic override is an - // isolated settings source even if it names the default settings path. - const loadOptions = - config.settingsSource === "programmatic" ? { globalSettingsPath: settingsPath } : {}; + // Preserve programmatic isolation independently of the path that won settings + // precedence; CLI --config remains the write and reload target when both exist. + const loadOptions = config.programmaticSettingsPath ? { globalSettingsPath: settingsPath } : {}; const newConfig = await loadConfig(argv, loadOptions); return runTUI(newConfig); } From 50b541f69c53bd22b1f8d7305b518eb6c4f90af1 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Fri, 28 Aug 2026 09:20:24 -0700 Subject: [PATCH 3/3] Remove unused settings source plumbing --- src/config.test.ts | 2 - src/config/index.ts | 9 --- src/tui/onboarding.test.ts | 117 ++++++++++++------------------------- 3 files changed, 36 insertions(+), 92 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index 079f894ed..a48173fbe 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -228,7 +228,6 @@ describe("loadConfig", () => { expect(result.providerError).toMatch(/missing/); expect(result.globalSettingsPath).toBe(NO_SETTINGS); expect(result.cliConfigPath).toBeUndefined(); - expect(result.settingsSource).toBe("programmatic"); expect(result.programmaticSettingsPath).toBe(true); } } finally { @@ -271,7 +270,6 @@ describe("loadConfig", () => { if (result.configured === false) { expect(result.globalSettingsPath).toBe(configPath); expect(result.cliConfigPath).toBe(configPath); - expect(result.settingsSource).toBe("cli"); expect(result.programmaticSettingsPath).toBe(false); } } finally { diff --git a/src/config/index.ts b/src/config/index.ts index 03a83e482..cc78dcf9a 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -424,8 +424,6 @@ export interface UnconfiguredConfig { globalSettingsPath: string; /** Original CLI path, present only when --config selected the write target. */ cliConfigPath?: string; - /** Source that selected the settings write target. */ - settingsSource: "default" | "cli" | "programmatic"; /** Whether the caller requested an OAuth-isolated programmatic settings load. */ programmaticSettingsPath: boolean; // The original error message, used for non-TUI (exec) error output. @@ -723,12 +721,6 @@ export async function loadConfig( // same file, not the global default. Prefer configPath, then the caller // override, then the real global default. const effectiveSettingsPath = configPath ?? options.globalSettingsPath ?? globalSettingsPath(); - const settingsSource = - configPath !== undefined - ? "cli" - : options.globalSettingsPath !== undefined - ? "programmatic" - : "default"; const task = positional.join(" ").trim(); let resolved: ResolvedProvider; @@ -752,7 +744,6 @@ export async function loadConfig( ...(director !== undefined ? { director } : {}), globalSettingsPath: effectiveSettingsPath, ...(configPath !== undefined ? { cliConfigPath: configPath } : {}), - settingsSource, programmaticSettingsPath: options.globalSettingsPath !== undefined, providerError: err instanceof Error ? err.message : String(err), // Keep diagnostics even when provider setup fails early so junk local diff --git a/src/tui/onboarding.test.ts b/src/tui/onboarding.test.ts index dba2fa07d..af7435cb3 100644 --- a/src/tui/onboarding.test.ts +++ b/src/tui/onboarding.test.ts @@ -39,37 +39,41 @@ await withMockedModule( const { loadConfig } = await import("../config/index.js"); const { runOnboarding } = await import("./onboarding.js"); -async function unconfiguredCLIConfig(cwd: string, configPath: string): Promise { - const config = await loadConfig(["--cwd", cwd, "--config", configPath], { - allowUnconfigured: true, - }); - if (config.configured) throw new Error("Expected onboarding config"); - return config; -} - -async function unconfiguredProgrammaticConfig( +async function unconfiguredConfig( cwd: string, - configPath: string, + paths: { cliConfigPath?: string; programmaticConfigPath?: string }, ): Promise { - const config = await loadConfig(["--cwd", cwd], { - globalSettingsPath: configPath, + const argv = ["--cwd", cwd]; + if (paths.cliConfigPath !== undefined) argv.push("--config", paths.cliConfigPath); + + const config = await loadConfig(argv, { + ...(paths.programmaticConfigPath !== undefined + ? { globalSettingsPath: paths.programmaticConfigPath } + : {}), allowUnconfigured: true, }); if (config.configured) throw new Error("Expected onboarding config"); return config; } -async function unconfiguredCLIAndProgrammaticConfig( - cwd: string, - cliConfigPath: string, - programmaticConfigPath: string, -): Promise { - const config = await loadConfig(["--cwd", cwd, "--config", cliConfigPath], { - globalSettingsPath: programmaticConfigPath, - allowUnconfigured: true, - }); - if (config.configured) throw new Error("Expected onboarding config"); - return config; +async function writeXAIAuthProfile(home: string, profile: string): Promise { + await mkdir(join(home, ".corbits"), { recursive: true }); + await writeFile( + join(home, ".corbits", "xai-auth.json"), + JSON.stringify({ + profiles: { + [profile]: { + name: profile, + tokens: { + access: `${profile}-access-token`, + refresh: `${profile}-refresh-token`, + expiresAt: Date.now() + 3_600_000, + }, + createdAt: Date.now(), + }, + }, + }), + ); } afterEach(() => { @@ -83,27 +87,11 @@ describe("runOnboarding settings source", () => { const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-oauth-cwd-")); const configPath = join(cwd, "custom-settings.json"); try { - await mkdir(join(testHome, ".corbits"), { recursive: true }); await writeFile(configPath, JSON.stringify({ providers: {} })); - const config = await unconfiguredCLIConfig(cwd, configPath); + const config = await unconfiguredConfig(cwd, { cliConfigPath: configPath }); setup = async ({ onSubmit }) => { - await writeFile( - join(testHome, ".corbits", "xai-auth.json"), - JSON.stringify({ - profiles: { - work: { - name: "work", - tokens: { - access: "test-access-token", - refresh: "test-refresh-token", - expiresAt: Date.now() + 3_600_000, - }, - createdAt: Date.now(), - }, - }, - }), - ); + await writeXAIAuthProfile(testHome, "work"); await onSubmit( { name: "xai/work", @@ -144,7 +132,7 @@ describe("runOnboarding settings source", () => { const configPath = join(cwd, "custom-settings.json"); try { await writeFile(configPath, JSON.stringify({ providers: {} })); - const config = await unconfiguredCLIConfig(cwd, configPath); + const config = await unconfiguredConfig(cwd, { cliConfigPath: configPath }); setup = async ({ onSubmit }) => { await onSubmit( @@ -181,31 +169,14 @@ describe("runOnboarding settings source", () => { const cliConfigPath = join(cwd, "cli-settings.json"); const programmaticConfigPath = join(cwd, "programmatic-settings.json"); try { - await mkdir(join(testHome, ".corbits"), { recursive: true }); - await writeFile( - join(testHome, ".corbits", "xai-auth.json"), - JSON.stringify({ - profiles: { - hidden: { - name: "hidden", - tokens: { - access: "hidden-access-token", - refresh: "hidden-refresh-token", - expiresAt: Date.now() + 3_600_000, - }, - createdAt: Date.now(), - }, - }, - }), - ); + await writeXAIAuthProfile(testHome, "hidden"); await writeFile(cliConfigPath, JSON.stringify({ providers: {} })); await writeFile(programmaticConfigPath, JSON.stringify({ providers: {} })); - const config = await unconfiguredCLIAndProgrammaticConfig( - cwd, + const config = await unconfiguredConfig(cwd, { cliConfigPath, programmaticConfigPath, - ); - expect(config.settingsSource).toBe("cli"); + }); + expect(config.cliConfigPath).toBe(cliConfigPath); expect(config.programmaticSettingsPath).toBe(true); setup = async ({ onSubmit }) => { @@ -237,25 +208,9 @@ describe("runOnboarding settings source", () => { const cwd = await mkdtemp(join(tmpdir(), "corbits-onboarding-isolated-cwd-")); const configPath = join(testHome, ".corbits", "settings.json"); try { - await mkdir(join(testHome, ".corbits"), { recursive: true }); - await writeFile( - join(testHome, ".corbits", "xai-auth.json"), - JSON.stringify({ - profiles: { - hidden: { - name: "hidden", - tokens: { - access: "hidden-access-token", - refresh: "hidden-refresh-token", - expiresAt: Date.now() + 3_600_000, - }, - createdAt: Date.now(), - }, - }, - }), - ); + await writeXAIAuthProfile(testHome, "hidden"); await writeFile(configPath, JSON.stringify({ providers: {} })); - const config = await unconfiguredProgrammaticConfig(cwd, configPath); + const config = await unconfiguredConfig(cwd, { programmaticConfigPath: configPath }); setup = async ({ onSubmit }) => { await onSubmit(