forked from boos/BoosAPI
refactor: keep downstream client metadata on cli profiles
This commit is contained in:
@@ -48,6 +48,9 @@ export const claudeCodeCliProfile: CliProfileDefinition = {
|
|||||||
id: 'claude_code',
|
id: 'claude_code',
|
||||||
sessionId,
|
sessionId,
|
||||||
traceHint: sessionId,
|
traceHint: sessionId,
|
||||||
|
clientAppId: 'claude_code',
|
||||||
|
clientAppName: 'Claude Code',
|
||||||
|
clientConfidence: 'exact',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -189,9 +189,21 @@ export const codexCliProfile: CliProfileDefinition = {
|
|||||||
if (!isCodexRequest(input)) return null;
|
if (!isCodexRequest(input)) return null;
|
||||||
|
|
||||||
const sessionId = getCodexSessionId(input.headers) || undefined;
|
const sessionId = getCodexSessionId(input.headers) || undefined;
|
||||||
|
const clientApp = detectCodexOfficialClientApp(input.headers);
|
||||||
return {
|
return {
|
||||||
id: 'codex',
|
id: 'codex',
|
||||||
...(sessionId ? { sessionId, traceHint: sessionId } : {}),
|
...(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;
|
if (input.body !== undefined && !hasGeminiCliBodyShape(input.body)) return null;
|
||||||
return {
|
return {
|
||||||
id: 'gemini_cli',
|
id: 'gemini_cli',
|
||||||
|
clientAppId: 'gemini_cli',
|
||||||
|
clientAppName: 'Gemini CLI',
|
||||||
|
clientConfidence: 'exact',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ describe('detectCliProfile', () => {
|
|||||||
id: 'codex',
|
id: 'codex',
|
||||||
sessionId: 'codex-session-123',
|
sessionId: 'codex-session-123',
|
||||||
traceHint: 'codex-session-123',
|
traceHint: 'codex-session-123',
|
||||||
|
clientAppId: 'codex_cli_rs',
|
||||||
|
clientAppName: 'Codex CLI',
|
||||||
|
clientConfidence: 'exact',
|
||||||
capabilities: {
|
capabilities: {
|
||||||
supportsResponsesCompact: true,
|
supportsResponsesCompact: true,
|
||||||
supportsResponsesWebsocketIncremental: true,
|
supportsResponsesWebsocketIncremental: true,
|
||||||
@@ -79,6 +82,9 @@ describe('detectCliProfile', () => {
|
|||||||
id: 'claude_code',
|
id: 'claude_code',
|
||||||
sessionId: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
|
sessionId: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
|
||||||
traceHint: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
|
traceHint: 'f25958b8-e75c-455d-8b40-f006d87cc2a4',
|
||||||
|
clientAppId: 'claude_code',
|
||||||
|
clientAppName: 'Claude Code',
|
||||||
|
clientConfidence: 'exact',
|
||||||
capabilities: {
|
capabilities: {
|
||||||
supportsResponsesCompact: false,
|
supportsResponsesCompact: false,
|
||||||
supportsResponsesWebsocketIncremental: false,
|
supportsResponsesWebsocketIncremental: false,
|
||||||
@@ -98,6 +104,9 @@ describe('detectCliProfile', () => {
|
|||||||
},
|
},
|
||||||
})).toEqual({
|
})).toEqual({
|
||||||
id: 'gemini_cli',
|
id: 'gemini_cli',
|
||||||
|
clientAppId: 'gemini_cli',
|
||||||
|
clientAppName: 'Gemini CLI',
|
||||||
|
clientConfidence: 'exact',
|
||||||
capabilities: {
|
capabilities: {
|
||||||
supportsResponsesCompact: false,
|
supportsResponsesCompact: false,
|
||||||
supportsResponsesWebsocketIncremental: false,
|
supportsResponsesWebsocketIncremental: false,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export type CliProfileCapabilities = {
|
|||||||
echoesTurnState: boolean;
|
echoesTurnState: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CliProfileClientConfidence = 'exact' | 'heuristic';
|
||||||
|
|
||||||
export type DetectCliProfileInput = {
|
export type DetectCliProfileInput = {
|
||||||
downstreamPath: string;
|
downstreamPath: string;
|
||||||
headers?: Record<string, unknown>;
|
headers?: Record<string, unknown>;
|
||||||
@@ -22,6 +24,9 @@ export type DetectedCliProfile = {
|
|||||||
id: CliProfileId;
|
id: CliProfileId;
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
traceHint?: string;
|
traceHint?: string;
|
||||||
|
clientAppId?: string;
|
||||||
|
clientAppName?: string;
|
||||||
|
clientConfidence?: CliProfileClientConfidence;
|
||||||
capabilities: CliProfileCapabilities;
|
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 { describe, expect, it } from 'vitest';
|
||||||
import {
|
import { extractClaudeCodeSessionId } from '../../proxy-core/cliProfiles/claudeCodeProfile.js';
|
||||||
detectDownstreamClientContext,
|
import { isCodexResponsesSurface } from '../../proxy-core/cliProfiles/codexProfile.js';
|
||||||
extractClaudeCodeSessionId,
|
import { detectDownstreamClientContext } from './downstreamClientContext.js';
|
||||||
isCodexResponsesSurface,
|
|
||||||
} from './downstreamClientContext.js';
|
|
||||||
|
|
||||||
describe('extractClaudeCodeSessionId', () => {
|
describe('extractClaudeCodeSessionId', () => {
|
||||||
it('extracts session uuid from axonhub-compatible Claude Code user ids', () => {
|
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 {
|
import type {
|
||||||
detectCliProfile,
|
CliProfileClientConfidence,
|
||||||
} from '../../proxy-core/cliProfiles/registry.js';
|
CliProfileId,
|
||||||
import {
|
} from '../../proxy-core/cliProfiles/types.js';
|
||||||
detectCodexOfficialClientApp as detectCodexOfficialClientAppViaProfile,
|
|
||||||
isCodexResponsesSurface as isCodexResponsesSurfaceViaProfile,
|
|
||||||
} from '../../proxy-core/cliProfiles/codexProfile.js';
|
|
||||||
import type { CliProfileId } from '../../proxy-core/cliProfiles/types.js';
|
|
||||||
|
|
||||||
export type DownstreamClientKind = CliProfileId;
|
export type DownstreamClientKind = CliProfileId;
|
||||||
export type DownstreamClientConfidence = 'exact' | 'heuristic';
|
export type DownstreamClientConfidence = CliProfileClientConfidence;
|
||||||
|
|
||||||
export type DownstreamClientContext = {
|
export type DownstreamClientContext = {
|
||||||
clientKind: DownstreamClientKind;
|
clientKind: DownstreamClientKind;
|
||||||
@@ -40,7 +36,7 @@ type DownstreamClientFingerprintRule = {
|
|||||||
match(input: DownstreamClientFingerprintInput): DownstreamClientConfidence | null;
|
match(input: DownstreamClientFingerprintInput): DownstreamClientConfidence | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DownstreamProtocolClientApp = {
|
type DownstreamResolvedClientApp = {
|
||||||
clientAppId: string;
|
clientAppId: string;
|
||||||
clientAppName: string;
|
clientAppName: string;
|
||||||
clientConfidence: DownstreamClientConfidence;
|
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'] || []) {
|
for (const value of headers['x-openai-client-user-agent'] || []) {
|
||||||
const clientAppName = parseExplicitClientSelfReportValue(value);
|
const clientAppName = parseExplicitClientSelfReportValue(value);
|
||||||
if (!clientAppName) continue;
|
if (!clientAppName) continue;
|
||||||
@@ -223,49 +219,6 @@ function detectExplicitClientSelfReport(headers: NormalizedClientHeaders): Downs
|
|||||||
return null;
|
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: {
|
export function detectDownstreamClientContext(input: {
|
||||||
downstreamPath: string;
|
downstreamPath: string;
|
||||||
headers?: Record<string, unknown>;
|
headers?: Record<string, unknown>;
|
||||||
@@ -275,14 +228,21 @@ export function detectDownstreamClientContext(input: {
|
|||||||
const normalizedHeaders = normalizeHeaders(input.headers);
|
const normalizedHeaders = normalizeHeaders(input.headers);
|
||||||
const explicitSelfReport = detectExplicitClientSelfReport(normalizedHeaders);
|
const explicitSelfReport = detectExplicitClientSelfReport(normalizedHeaders);
|
||||||
const fingerprint = detectDownstreamClientFingerprint(input);
|
const fingerprint = detectDownstreamClientFingerprint(input);
|
||||||
const protocolClientApp = fingerprint || explicitSelfReport ? null : detectProtocolClientApp({
|
const profileClientApp = fingerprint || explicitSelfReport
|
||||||
clientKind: detected.id,
|
? null
|
||||||
headers: input.headers,
|
: (
|
||||||
});
|
detected.clientAppId && detected.clientAppName
|
||||||
|
? {
|
||||||
|
clientAppId: detected.clientAppId,
|
||||||
|
clientAppName: detected.clientAppName,
|
||||||
|
...(detected.clientConfidence ? { clientConfidence: detected.clientConfidence } : {}),
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
clientKind: detected.id,
|
clientKind: detected.id,
|
||||||
...(detected.sessionId ? { sessionId: detected.sessionId } : {}),
|
...(detected.sessionId ? { sessionId: detected.sessionId } : {}),
|
||||||
...(detected.traceHint ? { traceHint: detected.traceHint } : {}),
|
...(detected.traceHint ? { traceHint: detected.traceHint } : {}),
|
||||||
...(explicitSelfReport || fingerprint || protocolClientApp || {}),
|
...(explicitSelfReport || fingerprint || profileClientApp || {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user