Merge remote-tracking branch 'origin/dev' into codex/fix-cliproxy-v3-snapshot-migration-issue

# Conflicts:
#	src/web-server/usage/cliproxy-usage-transformer.ts
#	tests/unit/web-server/cliproxy-usage-transformer.test.ts
This commit is contained in:
Tam Nhu Tran committed 2026-05-30 16:13:49 -04:00
commit ced9317565
210 files changed
+5627 -1065

No files matched your search

@@ -313,6 +313,12 @@ describe('web-server claude-extension-routes', () => {
it('creates a binding and applies managed settings to shared + IDE targets', async () => {
const ideSettingsPath = path.join(tempHome, 'ide', 'vscode', 'settings.json');
const sharedSettingsPath = path.join(tempHome, '.claude', 'settings.json');
fs.mkdirSync(path.dirname(ideSettingsPath), { recursive: true });
fs.mkdirSync(path.dirname(sharedSettingsPath), { recursive: true });
fs.writeFileSync(sharedSettingsPath, JSON.stringify({ env: {} }, null, 2) + '\n', { mode: 0o600 });
fs.writeFileSync(ideSettingsPath, JSON.stringify({}, null, 2) + '\n', { mode: 0o600 });
const createResponse = await fetch(`${baseUrl}/api/claude-extension/bindings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -352,7 +358,6 @@ describe('web-server claude-extension-routes', () => {
expect(applied.sharedSettings.state).toBe('applied');
expect(applied.ideSettings.state).toBe('applied');
const sharedSettingsPath = path.join(tempHome, '.claude', 'settings.json');
const sharedSettings = JSON.parse(fs.readFileSync(sharedSettingsPath, 'utf8')) as {
env?: Record<string, string>;
};
@@ -370,6 +375,9 @@ describe('web-server claude-extension-routes', () => {
)
).toBe(true);
expect(ideSettings['claudeCode.disableLoginPrompt']).toBe(true);
expect(fs.statSync(sharedSettingsPath).mode & 0o777).toBe(0o600);
expect(fs.statSync(ideSettingsPath).mode & 0o777).toBe(0o600);
});
it('resets only managed keys and preserves unrelated shared + IDE settings', async () => {
@@ -6,6 +6,7 @@ import * as path from 'path';
import * as http from 'http';
import type { Server } from 'http';
import cliproxyAuthRoutes from '../../../src/web-server/routes/cliproxy-auth-routes';
import { registerAccountFromToken } from '../../../src/cliproxy/auth/token-manager';
import {
clearQuotaCache,
getCachedQuota,
@@ -593,6 +594,61 @@ describe('cliproxy-auth-routes manual callback nickname persistence', () => {
}
});
it('reauthenticates a targeted existing Codex account when its token file is rewritten', async () => {
const tokenDir = path.join(tempHome, '.ccs', 'cliproxy', 'auth');
fs.mkdirSync(tokenDir, { recursive: true });
const tokenPath = path.join(tokenDir, 'codex-existing@example.com.json');
fs.writeFileSync(
tokenPath,
JSON.stringify({ type: 'codex', email: 'existing@example.com', version: 1 }),
'utf8'
);
const initialAccount = registerAccountFromToken('codex', tokenDir, 'work');
expect(initialAccount?.id).toBe('existing@example.com');
mockFetch([
{
url: /\/v0\/management\/codex-auth-url\?is_webui=true$/,
response: {
auth_url: 'https://auth.example.com/authorize?state=state-targeted-rewrite',
state: 'state-targeted-rewrite',
},
},
{
url: /\/v0\/management\/get-auth-status\?state=state-targeted-rewrite$/,
response: { status: 'ok' },
},
]);
const startResponse = await postJson('/api/cliproxy/auth/codex/start-url', {
accountId: 'existing@example.com',
});
expect(startResponse.status).toBe(200);
fs.writeFileSync(
tokenPath,
JSON.stringify({ type: 'codex', email: 'existing@example.com', version: 2 }),
'utf8'
);
const statusResponse = await getJson(
'/api/cliproxy/auth/codex/status?state=state-targeted-rewrite'
);
expect(statusResponse.status).toBe(200);
expect(statusResponse.body).toEqual({
status: 'ok',
account: {
id: 'existing@example.com',
email: 'existing@example.com',
nickname: 'work',
provider: 'codex',
isDefault: true,
},
});
});
it('registers the new account before reporting polled auth success', async () => {
mockFetch([
{
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'bun:test';
import {
getKiroStartIDCValidationError,
getReauthAccountTarget,
getStartAuthFailureMessage,
getStartAuthNicknameError,
getStartUrlUnsupportedReason,
@@ -17,6 +18,7 @@ describe('cliproxy-auth-routes start-url guard', () => {
"Provider 'codebuddy' uses Device Code flow"
);
expect(getStartUrlUnsupportedReason('kilo')).toContain("Provider 'kilo' uses Device Code flow");
expect(getStartUrlUnsupportedReason('qoder')).toContain("Provider 'qoder' uses Device Code flow");
});
it('allows Cursor browser URL auth on start-url', () => {
@@ -142,3 +144,27 @@ describe('cliproxy-auth-routes nickname validation', () => {
).toBeNull();
});
});
describe('cliproxy-auth-routes reauth account targeting', () => {
const existingAccounts = [
{ id: 'codex-user@example.com', nickname: 'work' },
{ id: 'codex-personal@example.com', nickname: 'personal' },
];
it('does not require a target for normal add-account auth', () => {
expect(getReauthAccountTarget(undefined, existingAccounts)).toEqual({});
expect(getReauthAccountTarget('', existingAccounts)).toEqual({});
});
it('resolves an existing account target for reauth', () => {
expect(getReauthAccountTarget('codex-user@example.com', existingAccounts)).toEqual({
account: { id: 'codex-user@example.com', nickname: 'work' },
});
});
it('rejects unknown account targets instead of falling back to ambiguous registration', () => {
expect(getReauthAccountTarget('missing', existingAccounts)).toEqual({
error: "Account 'missing' not found for this provider",
});
});
});
@@ -112,6 +112,21 @@ describe('cliproxy usage syncer', () => {
const snapshotPath = path.join(ccsDir, 'cache', 'cliproxy-usage', 'latest.json');
expect(fs.existsSync(snapshotPath)).toBe(true);
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8')) as {
details: Array<Record<string, unknown>>;
};
expect(snapshot.details[0]).not.toHaveProperty('source');
expect(snapshot.details[0]).not.toHaveProperty('authIndex');
if (process.platform !== 'win32') {
const cacheDir = path.join(ccsDir, 'cache');
const cliproxyCacheDir = path.dirname(snapshotPath);
expect(fs.statSync(ccsDir).mode & 0o777).toBe(0o700);
expect(fs.statSync(cacheDir).mode & 0o777).toBe(0o700);
expect(fs.statSync(cliproxyCacheDir).mode & 0o777).toBe(0o700);
expect(fs.statSync(snapshotPath).mode & 0o777).toBe(0o600);
}
const cached = await runWithScopedConfigDir(ccsDir, async () => {
return await loadCachedCliproxyData();
});
@@ -110,6 +110,8 @@ describe('cliproxy usage transformer', () => {
const flat = extractCliproxyUsageHistoryDetails(sampleResponse);
expect(flat).toHaveLength(4);
expect(flat[0].provider).toBe('google');
expect(flat[0]).not.toHaveProperty('source');
expect(flat[0]).not.toHaveProperty('authIndex');
expect(
flat.some(
(entry) => entry.failed === true && entry.inputTokens === 40 && entry.outputTokens === 10
@@ -160,8 +162,8 @@ describe('cliproxy usage transformer', () => {
const legacyDetail = normalizeCliproxyUsageHistoryDetail({
model: incoming.model,
timestamp: incoming.timestamp,
source: incoming.source,
authIndex: incoming.authIndex,
source: 'account-a',
authIndex: '0',
inputTokens: incoming.inputTokens,
outputTokens: incoming.outputTokens,
cacheReadTokens: incoming.cacheReadTokens,
@@ -170,6 +172,8 @@ describe('cliproxy usage transformer', () => {
expect(legacyDetail?.requestCount).toBe(1);
expect(Number.isFinite(legacyDetail?.cost)).toBe(true);
expect(legacyDetail).not.toHaveProperty('source');
expect(legacyDetail).not.toHaveProperty('authIndex');
const merged = mergeCliproxyUsageHistoryDetails([legacyDetail!], [incoming]);
expect(merged).toHaveLength(1);
@@ -177,6 +181,19 @@ describe('cliproxy usage transformer', () => {
expect(merged[0].cost).toBe(incoming.cost);
});
it('strips legacy account identifiers when merging persisted history', () => {
const details = extractCliproxyUsageHistoryDetails(sampleResponse);
const legacyDetail = {
...details[0],
source: 'user@example.com',
authIndex: 'auth-file-7',
};
const merged = mergeCliproxyUsageHistoryDetails([legacyDetail], []);
expect(merged[0]).not.toHaveProperty('source');
expect(merged[0]).not.toHaveProperty('authIndex');
});
it('preserves legitimate duplicate requests when the incoming batch has more occurrences', () => {
const details = extractCliproxyUsageHistoryDetails(sampleResponse);
const repeated = [details[0], { ...details[0] }];
@@ -311,6 +311,28 @@ describe('codex native usage collector', () => {
expect(entries).toHaveLength(2);
});
it('writes native usage caches with owner-only permissions', async () => {
if (process.platform === 'win32') return;
writeCodexRollout(tempRoot);
const cacheDir = getCacheDir();
const previousUmask = process.umask(0o022);
try {
await scanCodexNativeUsageEntries({
env: { CODEX_HOME: tempRoot },
homeDir: tempRoot,
cacheDir,
});
} finally {
process.umask(previousUmask);
}
const cachePath = path.join(cacheDir, 'codex-native-usage-v1.json');
expect(fs.statSync(cacheDir).mode & 0o777).toBe(0o700);
expect(fs.statSync(cachePath).mode & 0o777).toBe(0o600);
});
it('keeps default and include-cliproxy cache entries separate', async () => {
writeCodexRollout(tempRoot, { modelProvider: 'cliproxy' });
const cacheDir = getCacheDir();
@@ -510,6 +510,17 @@ describe('Cursor Routes Logic', () => {
expect(json.current).toBe('gpt-5.3-codex');
});
it('POST /api/cursor/probe rejects cross-origin requests', async () => {
const res = await fetch(`${baseUrl}/api/cursor/probe`, {
method: 'POST',
headers: { Origin: 'https://attacker.example' },
});
expect(res.status).toBe(403);
const json = (await res.json()) as { error?: string };
expect(json.error).toContain('Cross-origin probe requests are not allowed.');
});
it('POST /api/cursor/probe returns auth failure when credentials are missing', async () => {
const res = await fetch(`${baseUrl}/api/cursor/probe`, { method: 'POST' });
expect(res.status).toBe(401);
@@ -340,4 +340,31 @@ describe('image-analysis routes', () => {
error: 'Profile mapping for "codexProfile" references an unknown backend.',
});
});
it('rejects unsupported provider backends and preserves existing configuration', async () => {
const putResponse = await fetch(`${baseUrl}/api/image-analysis`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
providerModels: {
gemini: 'gemini-3-flash-preview',
evil: 'vision-model',
},
fallbackBackend: 'gemini',
}),
});
expect(putResponse.status).toBe(400);
expect(await putResponse.json()).toEqual({
error: 'Unsupported provider backend "evil".',
});
const getResponse = await fetch(`${baseUrl}/api/image-analysis`);
expect(getResponse.status).toBe(200);
const payload = await getResponse.json();
expect(payload.config.providerModels).toMatchObject({
gemini: 'gemini-3-flash-preview',
ghcp: 'claude-haiku-4.5',
});
});
});
@@ -6,11 +6,20 @@ import profileRoutes from '../../../src/web-server/routes/profile-routes';
describe('profile-routes local runtime readiness', () => {
let server: Server;
let baseUrl = '';
let forcedRemoteAddress = '127.0.0.1';
let originalDashboardAuthEnabled: string | undefined;
const originalFetch = globalThis.fetch;
beforeAll(async () => {
const app = express();
app.use(express.json());
app.use((req, _res, next) => {
Object.defineProperty(req.socket, 'remoteAddress', {
value: forcedRemoteAddress,
configurable: true,
});
next();
});
app.use('/api/profiles', profileRoutes);
await new Promise<void>((resolve, reject) => {
@@ -36,6 +45,10 @@ describe('profile-routes local runtime readiness', () => {
});
beforeEach(() => {
originalDashboardAuthEnabled = process.env.CCS_DASHBOARD_AUTH_ENABLED;
process.env.CCS_DASHBOARD_AUTH_ENABLED = 'false';
forcedRemoteAddress = '127.0.0.1';
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
@@ -73,6 +86,12 @@ describe('profile-routes local runtime readiness', () => {
afterEach(() => {
globalThis.fetch = originalFetch;
if (originalDashboardAuthEnabled !== undefined) {
process.env.CCS_DASHBOARD_AUTH_ENABLED = originalDashboardAuthEnabled;
} else {
delete process.env.CCS_DASHBOARD_AUTH_ENABLED;
}
});
it('reports local runtimes as ready when their endpoints respond with models', async () => {
@@ -103,6 +122,30 @@ describe('profile-routes local runtime readiness', () => {
);
});
it('blocks remote readiness probes when dashboard auth is disabled', async () => {
let probeCount = 0;
forcedRemoteAddress = '10.10.0.24';
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
if (url.startsWith(baseUrl)) {
return originalFetch(input);
}
probeCount += 1;
return new Response(JSON.stringify({ models: [{ name: 'gemma4:e4b' }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
const response = await fetch(`${baseUrl}/api/profiles/local-runtime-readiness`);
expect(response.status).toBe(403);
expect(await response.json()).toEqual({
error: 'Local runtime readiness requires localhost access when dashboard auth is disabled.',
});
expect(probeCount).toBe(0);
});
it('reports setup guidance when local endpoints are unavailable', async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
@@ -7,16 +7,16 @@ describe('route target parsing', () => {
it('accepts valid target values', () => {
expect(parseProfileTarget('claude')).toBe('claude');
expect(parseProfileTarget('DROID')).toBe('droid');
expect(parseProfileTarget('codex')).toBe('codex');
expect(parseVariantTarget(' claude ')).toBe('claude');
expect(parseVariantTarget('droid')).toBe('droid');
expect(parseVariantTarget(' codex ')).toBe('codex');
});
it('returns null for invalid target values', () => {
expect(parseProfileTarget('glm')).toBeNull();
expect(parseProfileTarget('codex')).toBeNull();
expect(parseProfileTarget('')).toBeNull();
expect(parseVariantTarget('factory')).toBeNull();
expect(parseVariantTarget('codex')).toBeNull();
expect(parseVariantTarget(' ')).toBeNull();
});
@@ -83,11 +83,7 @@ function writeAssistantEntriesToDir(baseClaudeDir: string, entries: AssistantFix
},
});
fs.writeFileSync(
path.join(projectDir, `${entry.sessionId}.jsonl`),
`${line}\n`,
'utf-8'
);
fs.writeFileSync(path.join(projectDir, `${entry.sessionId}.jsonl`), `${line}\n`, 'utf-8');
}
}
@@ -222,7 +218,10 @@ describe('usage handlers semantics', () => {
res as never
);
const payload = res.payload as { success: boolean; data: Array<{ hour: string; requests: number }> };
const payload = res.payload as {
success: boolean;
data: Array<{ hour: string; requests: number }>;
};
const targetHour = payload.data.find((row) => row.hour === '2026-03-02 10:00');
expect(targetHour?.requests).toBe(3);
@@ -415,6 +414,19 @@ describe('usage handlers semantics', () => {
});
});
it('rejects non-string profile filters as validation errors', async () => {
for (const profile of [['work', 'default'], { name: 'work' }]) {
const res = createMockResponse();
await handlers.handleSummary({ query: { profile } } as never, res as never);
expect(res.statusCode).toBe(400);
expect(res.payload).toMatchObject({
success: false,
error: 'Invalid profile filter',
});
}
});
it('rejects reversed date ranges before computing summary totals', async () => {
const res = createMockResponse();