diff --git a/src/lib/installer-core.spec.ts b/src/lib/installer-core.spec.ts index 90dfe97..8c62dd7 100644 --- a/src/lib/installer-core.spec.ts +++ b/src/lib/installer-core.spec.ts @@ -10,6 +10,8 @@ import type { InstallerMachineContext, BranchCheckOutput, } from './installer-core.types.js'; +import type { EnvFileInfo } from './credential-discovery.js'; +import type { StagingCredentials } from './staging-api.js'; // Shared mock actors for reuse across tests const baseMockActors = { @@ -378,5 +380,54 @@ describe('InstallerCore State Machine', () => { expect(actor.getSnapshot().value).toBe('complete'); actor.stop(); }); + + it('skips device auth when checkStoredAuth returns true (unclaimed env)', async () => { + const emitter = createInstallerEventEmitter(); + const options: InstallerOptions = { + debug: false, + forceInstall: false, + installDir: '/test/project', + default: false, + local: true, + ci: false, + skipAuth: true, + dashboard: false, + emitter, + // No CLI credentials — forces credential gathering flow + }; + + let deviceAuthStarted = false; + + const machine = installerMachine.provide({ + actors: { + ...baseMockActors, + detectEnvFiles: fromPromise(async () => ({ + found: false, + })), + checkStoredAuth: fromPromise(async () => true), + runDeviceAuth: fromPromise(async () => { + deviceAuthStarted = true; + throw new Error('device auth should not be called'); + }), + fetchStagingCredentials: fromPromise(async () => ({ + clientId: 'client_unclaimed', + apiKey: 'sk_test_unclaimed', + })), + }, + }); + + const actor = createActor(machine, { + input: { emitter, options }, + }); + + actor.start(); + actor.send({ type: 'START' }); + + await new Promise((r) => setTimeout(r, 200)); + + expect(deviceAuthStarted).toBe(false); + expect(actor.getSnapshot().value).toBe('complete'); + actor.stop(); + }); }); }); diff --git a/src/lib/run-with-core.ts b/src/lib/run-with-core.ts index 79c106e..ef14508 100644 --- a/src/lib/run-with-core.ts +++ b/src/lib/run-with-core.ts @@ -27,7 +27,7 @@ import { getStagingCredentials, saveStagingCredentials, } from './credentials.js'; -import { getConfig, saveConfig, getActiveEnvironment } from './config-store.js'; +import { getConfig, saveConfig, getActiveEnvironment, isUnclaimedEnvironment } from './config-store.js'; import { checkForEnvFiles, discoverCredentials } from './credential-discovery.js'; import { requestDeviceCode, pollForToken } from './device-auth.js'; import { fetchStagingCredentials as fetchStagingCredentialsApi } from './staging-api.js'; @@ -321,6 +321,11 @@ export async function runWithCore(options: InstallerOptions): Promise { }), checkStoredAuth: fromPromise(async () => { + const activeEnv = getActiveEnvironment(); + if (activeEnv?.apiKey && isUnclaimedEnvironment(activeEnv)) { + return true; + } + const token = getAccessToken(); return token !== null; }),