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:
2026-08-06 09:54:31 +07:00
180 changed files with 8622 additions and 6233 deletions

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -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,