The cli tests that launch pi deleted only the session login names, so pi's preflight passed or refused by whatever provider keys the host exported. A shared helper now deletes every credential pi's preflight reads and gives pi launches one made-up key. The refusal itself is unchanged. pi bundle() takes a default argument again, so a call with none returns the non-proxy bundle, as before P3. pi.bundle leaves out a launch.models entry whose provider has no proxy route instead of failing every pi launch (F2); malformed proxy entries still refuse. The invalid session proxy context BundleError gets the code invalid-proxy-context. New tests cover F2, the code, and the three proxy account branches in the launcher (F3).
77 lines
3.7 KiB
JavaScript
77 lines
3.7 KiB
JavaScript
// P3: run real harness exports through the plug-in loader, with made-up session keys only.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { createLoader } from '../src/loader.mjs';
|
|
import { sessionEnv } from '../../seat/src/session.mjs';
|
|
|
|
async function harness(name) {
|
|
const loader = createLoader({ log() {} });
|
|
const result = await loader.load({ harness: [name] });
|
|
assert.deepEqual(result.refused, []);
|
|
return loader.modules('harness')[0];
|
|
}
|
|
function scratch(t) {
|
|
const root = mkdtempSync(join(tmpdir(), 'p332-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
return root;
|
|
}
|
|
const policy = { tools: [], typed: [] };
|
|
const session = { model: 'claude-fixture', thinking: 'low' };
|
|
|
|
test('P3 Claude env receives the session proxy URL and key, without another agent or host credential', async () => {
|
|
const claude = await harness('claude-code');
|
|
const host = { CLAUDE_CODE_OAUTH_TOKEN: 'fx332-host', OPENAI_API_KEY: 'fx332-upstream' };
|
|
for (const [run, key] of [['r332a', 'fx332-agent-a'], ['r332b', 'fx332-agent-b']]) {
|
|
const proxy = { baseUrl: 'http://127.0.0.1:12345', key };
|
|
const env = { ...sessionEnv(host, run, proxy), ...claude.env({ files: {}, policy, session, proxy }) };
|
|
assert.equal(env.ANTHROPIC_BASE_URL, 'http://127.0.0.1:12345');
|
|
assert.equal(env.ANTHROPIC_AUTH_TOKEN, key);
|
|
assert.equal(Object.hasOwn(env, 'CLAUDE_CODE_OAUTH_TOKEN'), false);
|
|
assert.equal(Object.hasOwn(env, 'OPENAI_API_KEY'), false);
|
|
const other = run === 'r332a' ? 'fx332-agent-b' : 'fx332-agent-a';
|
|
assert.ok(!Object.values(env).includes(other));
|
|
}
|
|
});
|
|
|
|
test('P3 Claude preflight accepts a proxy key without a host provider credential', async t => {
|
|
const claude = await harness('claude-code');
|
|
const root = scratch(t), binary = join(root, 'claude');
|
|
writeFileSync(binary, '#!/bin/sh\nexit 0\n');
|
|
chmodSync(binary, 0o700);
|
|
assert.equal(claude.preflight({ env: { MOSAIC_CLAUDE_BIN: binary }, proxy: {
|
|
baseUrl: 'http://127.0.0.1:12345', key: 'fx332-agent-a',
|
|
} }), null);
|
|
});
|
|
|
|
test('P3 pi refuses no-session-credential with no provider key', async () => {
|
|
const pi = await harness('pi');
|
|
assert.equal(pi.preflight({ env: {} })?.code, 'no-session-credential');
|
|
});
|
|
|
|
test('P3 pi refuses no-session-credential with only a Claude OAuth token', async () => {
|
|
const pi = await harness('pi');
|
|
assert.equal(pi.preflight({ env: { CLAUDE_CODE_OAUTH_TOKEN: 'fx332-oauth' } })?.code, 'no-session-credential');
|
|
});
|
|
|
|
test('P3 pi accepts its own provider key or a session proxy key', async () => {
|
|
const pi = await harness('pi');
|
|
for (const name of ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'ZAI_API_KEY']) {
|
|
assert.equal(pi.preflight({ env: { [name]: 'fx332-own' } }), null, name);
|
|
}
|
|
assert.equal(pi.preflight({ env: {}, proxy: { baseUrl: 'http://127.0.0.1:12345', key: 'fx332-agent-a' } }), null);
|
|
});
|
|
|
|
// Row 332 R2 (#1830): callers that only want pi's bundle sources call bundle() with no argument,
|
|
// as they could before P3 (packages/cli/tests/stack-copy.test.mjs). It returns the non-proxy bundle.
|
|
test('row 332 R2 pi bundle() with no argument returns the non-proxy bundle it returned before P3', async () => {
|
|
const pi = await harness('pi');
|
|
const base = pi.bundle();
|
|
assert.deepEqual(base.files, {});
|
|
assert.deepEqual(base.digests, []);
|
|
assert.deepEqual(Object.keys(base.sources).sort(), ['piExtension', 'unslopCheck', 'unslopExtension', 'unslopLists']);
|
|
assert.deepEqual(pi.bundle({ dir: '/nonexistent/bundle', models: {} }), base, 'same as a call with no proxy context');
|
|
});
|