fix(worker): codex prepareAuth skips auth.json for api_key profiles

This commit is contained in:
Gabriel Brown
2026-07-11 11:00:08 -04:00
parent c0f107c4cf
commit 0716a224ef
2 changed files with 107 additions and 2 deletions
@@ -1,9 +1,24 @@
import { describe, expect, test, vi } from 'vitest';
import { mkdir, mkdtemp, readFile, rm, stat } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, test, vi } from 'vitest';
import type { NormalizedAgentEvent } from '../../src/agent-events';
import type { ExecStreamFn } from '../../src/runtime/agent-runtime';
import type { AdapterWorkspace, Claim } from '../../src/runtime/provider';
const tempDirs: string[] = [];
const pathExists = async (filePath: string): Promise<boolean> => {
try {
await stat(filePath);
return true;
} catch {
return false;
}
};
const loadAdapter = async () => {
process.env.SPOON_WORKER_TOKEN = 'test-worker-token';
process.env.GITHUB_APP_ID = '123';
@@ -51,7 +66,88 @@ const makeWorkspace = (): AdapterWorkspace => ({
runtime: 'codex',
});
const makeAuthWorkspace = async (
profile: Record<string, unknown>,
): Promise<AdapterWorkspace> => {
const workdir = await mkdtemp(path.join(os.tmpdir(), 'spoon-codex-auth-'));
tempDirs.push(workdir);
const repoDir = path.join(workdir, 'Code', 'spoon');
await mkdir(repoDir, { recursive: true });
return {
...makeWorkspace(),
claim: { ...claim, aiProviderProfile: profile } as unknown as Claim,
workdir,
homeDir: workdir,
repoDir,
};
};
describe('CodexAdapter', () => {
afterEach(async () => {
await Promise.all(
tempDirs.map((dir) => rm(dir, { force: true, recursive: true })),
);
tempDirs.length = 0;
});
test('prepareAuth skips auth.json for an api_key profile', async () => {
const { createCodexAdapter } = await loadAdapter();
const adapter = createCodexAdapter();
const workspace = await makeAuthWorkspace({
id: 'p',
name: 'OpenAI',
provider: 'openai',
authType: 'api_key',
model: 'gpt-5',
// A raw API key, which is deliberately NOT valid JSON.
secret: 'sk-test',
});
await expect(adapter.prepareAuth?.(workspace)).resolves.toBeUndefined();
const codexAuthPath = path.join(workspace.workdir, '.codex', 'auth.json');
const openCodeAuthPath = path.join(
workspace.workdir,
'.local',
'share',
'opencode',
'auth.json',
);
await expect(pathExists(codexAuthPath)).resolves.toBe(false);
await expect(pathExists(openCodeAuthPath)).resolves.toBe(false);
});
test('prepareAuth writes auth.json for a ChatGPT-login profile', async () => {
const { createCodexAdapter } = await loadAdapter();
const adapter = createCodexAdapter();
const secret = JSON.stringify({ tokens: { access: 'abc' } });
const workspace = await makeAuthWorkspace({
id: 'p',
name: 'Codex',
provider: 'opencode_openai_login',
authType: 'opencode_auth_json',
model: 'gpt-5',
secret,
});
await adapter.prepareAuth?.(workspace);
const codexAuthPath = path.join(workspace.workdir, '.codex', 'auth.json');
await expect(readFile(codexAuthPath, 'utf8')).resolves.toBe(
`${JSON.stringify(JSON.parse(secret), null, 2)}\n`,
);
const openCodeAuthPath = path.join(
workspace.workdir,
'.local',
'share',
'opencode',
'auth.json',
);
await expect(readFile(openCodeAuthPath, 'utf8')).resolves.toBe(
`${JSON.stringify(JSON.parse(secret), null, 2)}\n`,
);
});
test('streams normalized events and marks the turn via buildMarkedCommand', async () => {
const { createCodexAdapter } = await loadAdapter();
const captured: unknown[] = [];