refactor: keep downstream client metadata on cli profiles
This commit is contained in:
@@ -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 || {}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user