fix(pi): resolve packaged proxy token lazily
This commit is contained in:
1 parent
fa510c4976
commit
f902edefd0
13 files changed
+934
-30
No files matched your search
@@ -16,9 +16,14 @@ import {
|
||||
createMemoryCodingProjectStorage,
|
||||
} from '../../electron/coding-projects/project-store';
|
||||
import { PiProcessError } from '../../electron/coding-runtime/pi/process-errors';
|
||||
import { PiProviderConfigError } from '../../electron/coding-runtime/pi/provider-config';
|
||||
import { PiConversationRuntime } from '../../electron/coding-runtime/pi/runtime';
|
||||
import { PiSessionRegistry } from '../../electron/coding-runtime/pi/session-registry';
|
||||
import { PiWorkerPool, type PiConversationWorker } from '../../electron/coding-runtime/pi/worker-pool';
|
||||
import {
|
||||
PiWorkerPool,
|
||||
type PiConversationWorker,
|
||||
type PiWorkerPoolOptions,
|
||||
} from '../../electron/coding-runtime/pi/worker-pool';
|
||||
import type {
|
||||
PiRpcCommand,
|
||||
PiRpcEvent,
|
||||
@@ -68,7 +73,13 @@ afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
async function setupAuthRuntime(refreshCredential: (accountId: string) => Promise<void>) {
|
||||
async function setupAuthRuntime(
|
||||
refreshCredential: (accountId: string) => Promise<void>,
|
||||
options: {
|
||||
openWorker?: PiWorkerPoolOptions['openWorker'];
|
||||
prepare?: boolean;
|
||||
} = {},
|
||||
) {
|
||||
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-pi-auth-'));
|
||||
roots.push(projectPath);
|
||||
const projectStore = createCodingProjectStore(createMemoryCodingProjectStorage(), {
|
||||
@@ -96,19 +107,23 @@ async function setupAuthRuntime(refreshCredential: (accountId: string) => Promis
|
||||
modelResolution: 'resolved',
|
||||
});
|
||||
const workers: AuthFailureWorker[] = [];
|
||||
const defaultOpenWorker: PiWorkerPoolOptions['openWorker'] = async ({
|
||||
conversation: input,
|
||||
existingSession,
|
||||
}) => {
|
||||
const worker = new AuthFailureWorker(`worker-${workers.length + 1}`);
|
||||
workers.push(worker);
|
||||
return {
|
||||
worker,
|
||||
session: existingSession ?? {
|
||||
piSessionId: `session-${input.conversationId}`,
|
||||
sessionKey: `key-${input.conversationId}`,
|
||||
},
|
||||
};
|
||||
};
|
||||
const pool = new PiWorkerPool({
|
||||
maxIdle: 2,
|
||||
openWorker: async ({ conversation: input, existingSession }) => {
|
||||
const worker = new AuthFailureWorker(`worker-${workers.length + 1}`);
|
||||
workers.push(worker);
|
||||
return {
|
||||
worker,
|
||||
session: existingSession ?? {
|
||||
piSessionId: `session-${input.conversationId}`,
|
||||
sessionKey: `key-${input.conversationId}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
openWorker: options.openWorker ?? defaultOpenWorker,
|
||||
});
|
||||
const runtime = new PiConversationRuntime({
|
||||
pool,
|
||||
@@ -118,17 +133,38 @@ async function setupAuthRuntime(refreshCredential: (accountId: string) => Promis
|
||||
isAuthenticationError: isCodingProviderAuthenticationError,
|
||||
createId: () => 'run-auth',
|
||||
});
|
||||
await runtime.prepare({
|
||||
const input = {
|
||||
conversationId: conversation.id,
|
||||
projectId: 'project-auth',
|
||||
agentId: 'agent-auth',
|
||||
title: conversation.title,
|
||||
model: { model: conversation.model, modelResolution: conversation.modelResolution },
|
||||
});
|
||||
return { conversation, pool, runtime, workers };
|
||||
};
|
||||
if (options.prepare !== false) await runtime.prepare(input);
|
||||
return { conversation, input, pool, runtime, workers };
|
||||
}
|
||||
|
||||
describe('Pi runtime Provider authentication recovery', () => {
|
||||
it('maps a missing proxy token to Provider auth-required after one refresh retry', async () => {
|
||||
const refreshCredential = vi.fn(async () => undefined);
|
||||
const openWorker = vi.fn<PiWorkerPoolOptions['openWorker']>(async () => {
|
||||
throw new PiProviderConfigError('PROVIDER_AUTH_REQUIRED', 'Provider credential is unavailable');
|
||||
});
|
||||
const { input, runtime } = await setupAuthRuntime(refreshCredential, {
|
||||
openWorker,
|
||||
prepare: false,
|
||||
});
|
||||
|
||||
await expect(runtime.prepare(input)).rejects.toMatchObject({
|
||||
publicError: {
|
||||
code: 'CODING_PROVIDER_AUTH_REQUIRED',
|
||||
recoverable: true,
|
||||
},
|
||||
});
|
||||
expect(refreshCredential).toHaveBeenCalledTimes(1);
|
||||
expect(openWorker).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('refreshes and reopens once, then exposes the second authentication failure without looping', async () => {
|
||||
const refreshCredential = vi.fn(async () => undefined);
|
||||
const { conversation, pool, runtime, workers } = await setupAuthRuntime(refreshCredential);
|
||||
|
||||
Reference in new issue
Block a user