merge: integrate origin/master (v0.5.50) into gitea/new_feature
- Resolve conflicts in chatCore handlers: keep apiKey/streamErrorPatterns from the details-filters feature, adopt origin's stripContinuityFields, customToolNames, cache-inclusive usage accounting, and Responses-API SSE→JSON conversion - Adopt origin's provider usage handlers (codebuddy-intl, qoder creds) and modality detection (audio/video inputs) - Keep requestDetails apiKey column (schema v2) + masked key persistence Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* Singleton cache for real Claude Code client headers.
|
||||
* Captures headers from authentic Claude Code requests and makes them available
|
||||
* for forwarding to api.anthropic.com, replacing static hardcoded values.
|
||||
*/
|
||||
|
||||
const CLAUDE_IDENTITY_HEADERS = [
|
||||
"user-agent",
|
||||
"anthropic-beta",
|
||||
"anthropic-version",
|
||||
"anthropic-dangerous-direct-browser-access",
|
||||
"x-app",
|
||||
"x-stainless-helper-method",
|
||||
"x-stainless-retry-count",
|
||||
"x-stainless-runtime-version",
|
||||
"x-stainless-package-version",
|
||||
"x-stainless-runtime",
|
||||
"x-stainless-lang",
|
||||
"x-stainless-arch",
|
||||
"x-stainless-os",
|
||||
"x-stainless-timeout",
|
||||
"x-claude-code-session-id",
|
||||
"package-version",
|
||||
"runtime-version",
|
||||
"os",
|
||||
"arch",
|
||||
];
|
||||
|
||||
let cachedHeaders = null;
|
||||
|
||||
/**
|
||||
* Detect if request headers look like a real Claude Code client.
|
||||
* @param {object} headers - Lowercase header key/value object
|
||||
*/
|
||||
function isClaudeCodeClient(headers) {
|
||||
const ua = (headers["user-agent"] || "").toLowerCase();
|
||||
const xApp = (headers["x-app"] || "").toLowerCase();
|
||||
return ua.includes("claude-cli") || ua.includes("claude-code") || xApp === "cli";
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Claude Code identity headers if this looks like a real client request.
|
||||
* Called at the entry point before any translation/forwarding.
|
||||
* @param {object} headers - Lowercase header key/value object (from request.headers.entries())
|
||||
*/
|
||||
export function cacheClaudeHeaders(headers) {
|
||||
if (!headers || typeof headers !== "object") return;
|
||||
if (!isClaudeCodeClient(headers)) return;
|
||||
|
||||
const captured = {};
|
||||
for (const key of CLAUDE_IDENTITY_HEADERS) {
|
||||
if (headers[key] !== undefined && headers[key] !== null) {
|
||||
captured[key] = headers[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(captured).length > 0) {
|
||||
cachedHeaders = captured;
|
||||
console.log(`[ClaudeHeaders] Cached ${Object.keys(captured).length} identity headers from Claude Code client`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently cached Claude Code identity headers.
|
||||
* Returns null if no authentic client request has been seen yet (cold start).
|
||||
* @returns {object|null}
|
||||
*/
|
||||
export function getCachedClaudeHeaders() {
|
||||
return cachedHeaders;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export function detectClientTool(headers = {}, body = {}) {
|
||||
const xApp = (headers["x-app"] || "").toLowerCase();
|
||||
const openaiIntent = (headers["openai-intent"] || "").toLowerCase();
|
||||
const initiator = (headers["x-initiator"] || headers["X-Initiator"] || "").toLowerCase();
|
||||
const originator = (headers["originator"] || "").toLowerCase();
|
||||
|
||||
// Antigravity: detected via body field (not header)
|
||||
if (body.userAgent === "antigravity") return "antigravity";
|
||||
@@ -37,8 +38,10 @@ export function detectClientTool(headers = {}, body = {}) {
|
||||
// Gemini CLI
|
||||
if (ua.includes("gemini-cli")) return "gemini-cli";
|
||||
|
||||
// Codex CLI
|
||||
if (ua.includes("codex-cli")) return "codex";
|
||||
// Codex CLI/Desktop — codex-tui is the current Rust CLI, codex-cli/codex_cli_rs legacy;
|
||||
// Codex Desktop identifies via UA "Codex Desktop" or originator "codex_work_desktop"
|
||||
if (ua.includes("codex-tui") || ua.includes("codex-cli") || ua.includes("codex_cli_rs") ||
|
||||
ua.includes("codex desktop") || originator.startsWith("codex_")) return "codex";
|
||||
|
||||
// DeepSeek TUI
|
||||
if (ua.includes("deepseek-tui")) return "deepseek-tui";
|
||||
|
||||
@@ -44,6 +44,7 @@ export function createSSEStream(options = {}) {
|
||||
provider = null,
|
||||
reqLogger = null,
|
||||
toolNameMap = null,
|
||||
customToolNames = null,
|
||||
model = null,
|
||||
connectionId = null,
|
||||
body = null,
|
||||
@@ -57,7 +58,9 @@ export function createSSEStream(options = {}) {
|
||||
// Per-stream decoder with stream:true to correctly handle multi-byte chars split across chunks
|
||||
const decoder = new TextDecoder("utf-8", { fatal: false });
|
||||
|
||||
const state = mode === STREAM_MODE.TRANSLATE ? { ...initState(sourceFormat), provider, toolNameMap, model } : null;
|
||||
const state = mode === STREAM_MODE.TRANSLATE
|
||||
? { ...initState(sourceFormat), provider, toolNameMap, customToolNames: new Set(customToolNames || []), model }
|
||||
: null;
|
||||
|
||||
let totalContentLength = 0;
|
||||
let accumulatedContent = "";
|
||||
@@ -464,7 +467,7 @@ export function createSSEStream(options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, provider = null, reqLogger = null, toolNameMap = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null) {
|
||||
export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, provider = null, reqLogger = null, toolNameMap = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null, customToolNames = null) {
|
||||
return createSSEStream({
|
||||
mode: STREAM_MODE.TRANSLATE,
|
||||
targetFormat,
|
||||
@@ -472,6 +475,7 @@ export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, p
|
||||
provider,
|
||||
reqLogger,
|
||||
toolNameMap,
|
||||
customToolNames,
|
||||
model,
|
||||
connectionId,
|
||||
body,
|
||||
|
||||
Reference in New Issue
Block a user