refactor: keep downstream client metadata on cli profiles

This commit is contained in:
Cita
2026-03-23 12:02:44 +08:00
parent 3877f3a3f1
commit 50c34b8866
8 changed files with 72 additions and 65 deletions
@@ -48,6 +48,9 @@ export const claudeCodeCliProfile: CliProfileDefinition = {
id: 'claude_code',
sessionId,
traceHint: sessionId,
clientAppId: 'claude_code',
clientAppName: 'Claude Code',
clientConfidence: 'exact',
};
},
};
@@ -189,9 +189,21 @@ export const codexCliProfile: CliProfileDefinition = {
if (!isCodexRequest(input)) return null;
const sessionId = getCodexSessionId(input.headers) || undefined;
const clientApp = detectCodexOfficialClientApp(input.headers);
return {
id: 'codex',
...(sessionId ? { sessionId, traceHint: sessionId } : {}),
...(clientApp
? {
clientAppId: clientApp.clientAppId,
clientAppName: clientApp.clientAppName,
clientConfidence: 'exact' as const,
}
: {
clientAppId: 'codex',
clientAppName: 'Codex',
clientConfidence: 'heuristic' as const,
}),
};
},
};
@@ -32,6 +32,9 @@ export const geminiCliProfile: CliProfileDefinition = {
if (input.body !== undefined && !hasGeminiCliBodyShape(input.body)) return null;
return {
id: 'gemini_cli',
clientAppId: 'gemini_cli',
clientAppName: 'Gemini CLI',
clientConfidence: 'exact',
};
},
};
@@ -14,6 +14,9 @@ describe('detectCliProfile', () => {
id: 'codex',
sessionId: 'codex-session-123',
traceHint: 'codex-session-123',
clientAppId: 'codex_cli_rs',
clientAppName: 'Codex CLI',
clientConfidence: 'exact',
capabilities: {
supportsResponsesCompact: true,
supportsResponsesWebsocketIncremental: true,
@@ -79,6 +82,9 @@ describe('detectCliProfile', () => {
id: 'claude_code',
sessionId: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
traceHint: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
clientAppId: 'claude_code',
clientAppName: 'Claude Code',
clientConfidence: 'exact',
capabilities: {
supportsResponsesCompact: false,
supportsResponsesWebsocketIncremental: false,
@@ -98,6 +104,9 @@ describe('detectCliProfile', () => {
},
})).toEqual({
id: 'gemini_cli',
clientAppId: 'gemini_cli',
clientAppName: 'Gemini CLI',
clientConfidence: 'exact',
capabilities: {
supportsResponsesCompact: false,
supportsResponsesWebsocketIncremental: false,
@@ -12,6 +12,8 @@ export type CliProfileCapabilities = {
echoesTurnState: boolean;
};
export type CliProfileClientConfidence = 'exact' | 'heuristic';
export type DetectCliProfileInput = {
downstreamPath: string;
headers?: Record<string, unknown>;
@@ -22,6 +24,9 @@ export type DetectedCliProfile = {
id: CliProfileId;
sessionId?: string;
traceHint?: string;
clientAppId?: string;
clientAppName?: string;
clientConfidence?: CliProfileClientConfidence;
capabilities: CliProfileCapabilities;
};
@@ -0,0 +1,17 @@
import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
function readSource(relativePath: string): string {
return readFileSync(new URL(relativePath, import.meta.url), 'utf8');
}
describe('downstreamClientContext architecture boundaries', () => {
it('keeps protocol-level client metadata on cli profiles instead of importing individual profile helpers', () => {
const source = readSource('./downstreamClientContext.ts');
expect(source).toContain("from '../../proxy-core/cliProfiles/registry.js'");
expect(source).toContain("from '../../proxy-core/cliProfiles/types.js'");
expect(source).not.toContain("from '../../proxy-core/cliProfiles/codexProfile.js'");
expect(source).not.toContain("from '../../proxy-core/cliProfiles/claudeCodeProfile.js'");
});
});
@@ -1,9 +1,7 @@
import { describe, expect, it } from 'vitest';
import {
detectDownstreamClientContext,
extractClaudeCodeSessionId,
isCodexResponsesSurface,
} from './downstreamClientContext.js';
import { extractClaudeCodeSessionId } from '../../proxy-core/cliProfiles/claudeCodeProfile.js';
import { isCodexResponsesSurface } from '../../proxy-core/cliProfiles/codexProfile.js';
import { detectDownstreamClientContext } from './downstreamClientContext.js';
describe('extractClaudeCodeSessionId', () => {
it('extracts session uuid from axonhub-compatible Claude Code user ids', () => {
@@ -1,15 +1,11 @@
import { extractClaudeCodeSessionId as extractClaudeCodeSessionIdViaProfile } from '../../proxy-core/cliProfiles/claudeCodeProfile.js';
import {
detectCliProfile,
} from '../../proxy-core/cliProfiles/registry.js';
import {
detectCodexOfficialClientApp as detectCodexOfficialClientAppViaProfile,
isCodexResponsesSurface as isCodexResponsesSurfaceViaProfile,
} from '../../proxy-core/cliProfiles/codexProfile.js';
import type { CliProfileId } from '../../proxy-core/cliProfiles/types.js';
import { detectCliProfile } from '../../proxy-core/cliProfiles/registry.js';
import type {
CliProfileClientConfidence,
CliProfileId,
} from '../../proxy-core/cliProfiles/types.js';
export type DownstreamClientKind = CliProfileId;
export type DownstreamClientConfidence = 'exact' | 'heuristic';
export type DownstreamClientConfidence = CliProfileClientConfidence;
export type DownstreamClientContext = {
clientKind: DownstreamClientKind;
@@ -40,7 +36,7 @@ type DownstreamClientFingerprintRule = {
match(input: DownstreamClientFingerprintInput): DownstreamClientConfidence | null;
};
type DownstreamProtocolClientApp = {
type DownstreamResolvedClientApp = {
clientAppId: string;
clientAppName: string;
clientConfidence: DownstreamClientConfidence;
@@ -199,7 +195,7 @@ function detectDownstreamClientFingerprint(input: {
};
}
function detectExplicitClientSelfReport(headers: NormalizedClientHeaders): DownstreamProtocolClientApp | null {
function detectExplicitClientSelfReport(headers: NormalizedClientHeaders): DownstreamResolvedClientApp | null {
for (const value of headers['x-openai-client-user-agent'] || []) {
const clientAppName = parseExplicitClientSelfReportValue(value);
if (!clientAppName) continue;
@@ -223,49 +219,6 @@ function detectExplicitClientSelfReport(headers: NormalizedClientHeaders): Downs
return null;
}
function detectProtocolClientApp(input: {
clientKind: DownstreamClientKind;
headers?: Record<string, unknown>;
}): DownstreamProtocolClientApp | null {
switch (input.clientKind) {
case 'claude_code':
return {
clientAppId: 'claude_code',
clientAppName: 'Claude Code',
clientConfidence: 'exact',
};
case 'gemini_cli':
return {
clientAppId: 'gemini_cli',
clientAppName: 'Gemini CLI',
clientConfidence: 'exact',
};
case 'codex': {
const clientApp = detectCodexOfficialClientAppViaProfile(input.headers);
return clientApp
? {
...clientApp,
clientConfidence: 'exact' as const,
}
: {
clientAppId: 'codex',
clientAppName: 'Codex',
clientConfidence: 'heuristic',
};
}
default:
return null;
}
}
export function isCodexResponsesSurface(headers?: Record<string, unknown>): boolean {
return isCodexResponsesSurfaceViaProfile(headers);
}
export function extractClaudeCodeSessionId(userId: string): string | null {
return extractClaudeCodeSessionIdViaProfile(userId);
}
export function detectDownstreamClientContext(input: {
downstreamPath: string;
headers?: Record<string, unknown>;
@@ -275,14 +228,21 @@ export function detectDownstreamClientContext(input: {
const normalizedHeaders = normalizeHeaders(input.headers);
const explicitSelfReport = detectExplicitClientSelfReport(normalizedHeaders);
const fingerprint = detectDownstreamClientFingerprint(input);
const protocolClientApp = fingerprint || explicitSelfReport ? null : detectProtocolClientApp({
clientKind: detected.id,
headers: input.headers,
});
const profileClientApp = fingerprint || explicitSelfReport
? null
: (
detected.clientAppId && detected.clientAppName
? {
clientAppId: detected.clientAppId,
clientAppName: detected.clientAppName,
...(detected.clientConfidence ? { clientConfidence: detected.clientConfidence } : {}),
}
: null
);
return {
clientKind: detected.id,
...(detected.sessionId ? { sessionId: detected.sessionId } : {}),
...(detected.traceHint ? { traceHint: detected.traceHint } : {}),
...(explicitSelfReport || fingerprint || protocolClientApp || {}),
...(explicitSelfReport || fingerprint || profileClientApp || {}),
};
}