From b65921e8c35abb4d6c62a83581685edceb0eae71 Mon Sep 17 00:00:00 2001 From: Juan Camilo Date: Thu, 2 Apr 2026 15:50:52 +0200 Subject: [PATCH 1/4] fix: deterministic prefix matching and correct Llama 3.x context windows Two fixes in openaiContextWindows.ts: 1. Sort lookup keys by length descending in lookupByModel() so the most specific prefix always wins. Without this, 'gpt-4-turbo-preview' could match 'gpt-4' (8k) instead of 'gpt-4-turbo' (128k) depending on V8's object key iteration order. 2. Update Llama 3.1/3.2/3.3 context windows from 8,192 to 128,000. These models support 128k context natively (Meta official specs). The previous 8k value was Ollama's default num_ctx, not the model's actual capability, causing premature auto-compact warnings. --- src/utils/model/openaiContextWindows.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/utils/model/openaiContextWindows.ts b/src/utils/model/openaiContextWindows.ts index 6cb12c37..66db3d35 100644 --- a/src/utils/model/openaiContextWindows.ts +++ b/src/utils/model/openaiContextWindows.ts @@ -50,9 +50,11 @@ const OPENAI_CONTEXT_WINDOWS: Record = { 'gemini-2.5-flash': 1_048_576, // Ollama local models - 'llama3.3:70b': 8_192, - 'llama3.1:8b': 8_192, - 'llama3.2:3b': 8_192, + // Llama 3.1+ models support 128k context natively (Meta official specs). + // Ollama defaults to num_ctx=8192 but users can configure higher values. + 'llama3.3:70b': 128_000, + 'llama3.1:8b': 128_000, + 'llama3.2:3b': 128_000, 'qwen2.5-coder:32b': 32_768, 'qwen2.5-coder:7b': 32_768, 'deepseek-coder-v2:16b': 163_840, @@ -122,7 +124,11 @@ const OPENAI_MAX_OUTPUT_TOKENS: Record = { function lookupByModel(table: Record, model: string): T | undefined { if (table[model] !== undefined) return table[model] - for (const key of Object.keys(table)) { + // Sort keys by length descending so the most specific prefix wins. + // Without this, 'gpt-4-turbo-preview' could match 'gpt-4' (8k) instead + // of 'gpt-4-turbo' (128k) depending on V8's key iteration order. + const sortedKeys = Object.keys(table).sort((a, b) => b.length - a.length) + for (const key of sortedKeys) { if (model.startsWith(key)) return table[key] } return undefined From 5d6443799acc1e1983eb70d447170e44c79c7b5c Mon Sep 17 00:00:00 2001 From: Juan Camilo Date: Thu, 2 Apr 2026 16:14:35 +0200 Subject: [PATCH 2/4] fix: crypto.randomUUID for IDs, Azure Foundry detection, safety filter visibility Three targeted fixes: 1. Replace Math.random() with crypto.randomUUID() for message and tool call IDs in both openaiShim.ts and codexShim.ts. Math.random() is not cryptographically secure and predictable in seeded environments. 2. Anchor Azure endpoint detection to parsed hostname instead of raw URL regex. Adds support for Azure AI Foundry (services.ai.azure.com) alongside existing cognitiveservices and openai Azure endpoints. Prevents SSRF-style bypass via path segments. 3. Surface content safety filter blocks to the user. When Gemini or Azure returns finish_reason 'content_filter' or 'safety', emit a visible text block '[Content blocked by provider safety filter]' instead of silently returning empty/truncated content with stop_reason 'end_turn'. Applied to both streaming and non-streaming. --- src/services/api/codexShim.ts | 2 +- src/services/api/openaiShim.ts | 37 +++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/services/api/codexShim.ts b/src/services/api/codexShim.ts index 26ae237e..6cc51f5b 100644 --- a/src/services/api/codexShim.ts +++ b/src/services/api/codexShim.ts @@ -85,7 +85,7 @@ function makeUsage(usage?: { } function makeMessageId(): string { - return `msg_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}` + return `msg_${crypto.randomUUID().replace(/-/g, '')}` } function normalizeToolUseId(toolUseId: string | undefined): { diff --git a/src/services/api/openaiShim.ts b/src/services/api/openaiShim.ts index 2e767f1b..c8153eb4 100644 --- a/src/services/api/openaiShim.ts +++ b/src/services/api/openaiShim.ts @@ -231,7 +231,7 @@ function convertMessages( input?: unknown extra_content?: Record }) => ({ - id: tu.id ?? `call_${Math.random().toString(36).slice(2)}`, + id: tu.id ?? `call_${crypto.randomUUID().replace(/-/g, '')}`, type: 'function' as const, function: { name: tu.name ?? 'unknown', @@ -389,7 +389,7 @@ interface OpenAIStreamChunk { } function makeMessageId(): string { - return `msg_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}` + return `msg_${crypto.randomUUID().replace(/-/g, '')}` } function convertChunkUsage( @@ -610,6 +610,23 @@ async function* openaiStreamToAnthropic( : choice.finish_reason === 'length' ? 'max_tokens' : 'end_turn' + if (choice.finish_reason === 'content_filter' || choice.finish_reason === 'safety') { + // Gemini/Azure content safety filter blocked the response. + // Emit a visible text block so the user knows why output was truncated. + if (!hasEmittedContentStart) { + yield { + type: 'content_block_start', + index: contentBlockIndex, + content_block: { type: 'text', text: '' }, + } + hasEmittedContentStart = true + } + yield { + type: 'content_block_delta', + index: contentBlockIndex, + delta: { type: 'text_delta', text: '\n\n[Content blocked by provider safety filter]' }, + } + } lastStopReason = stopReason yield { @@ -841,7 +858,14 @@ class OpenAIShimMessages { } const apiKey = process.env.OPENAI_API_KEY ?? '' - const isAzure = /cognitiveservices\.azure\.com|openai\.azure\.com/.test(request.baseUrl) + // Detect Azure endpoints by hostname (not raw URL) to prevent bypass via + // path segments like https://evil.com/cognitiveservices.azure.com/ + let isAzure = false + try { + const { hostname } = new URL(request.baseUrl) + isAzure = hostname.endsWith('.azure.com') && + (hostname.includes('cognitiveservices') || hostname.includes('openai') || hostname.includes('services.ai')) + } catch { /* malformed URL — not Azure */ } if (apiKey) { if (isAzure) { @@ -1003,6 +1027,13 @@ class OpenAIShimMessages { ? 'max_tokens' : 'end_turn' + if (choice?.finish_reason === 'content_filter' || choice?.finish_reason === 'safety') { + content.push({ + type: 'text', + text: '\n\n[Content blocked by provider safety filter]', + }) + } + return { id: data.id ?? makeMessageId(), type: 'message', From 84ac06bac9b7897f64b5cf6048a1e833bd3d217f Mon Sep 17 00:00:00 2001 From: erdemozyol <26572407+erdemozyol@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:28:34 +0300 Subject: [PATCH 3/4] fix: show display version in status --- src/components/Settings/Status.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Settings/Status.tsx b/src/components/Settings/Status.tsx index 95c62964..81c3cdd1 100644 --- a/src/components/Settings/Status.tsx +++ b/src/components/Settings/Status.tsx @@ -22,7 +22,7 @@ function buildPrimarySection(): Property[] { const nameValue = customTitle ?? /rename to add a name; return [{ label: 'Version', - value: MACRO.VERSION + value: MACRO.DISPLAY_VERSION ?? MACRO.VERSION }, { label: 'Session name', value: nameValue @@ -238,4 +238,4 @@ function Diagnostics(t0) { function _temp5(diagnostic, i) { return {figures.warning}{typeof diagnostic === "string" ? {diagnostic} : diagnostic}; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJTdXNwZW5zZSIsInVzZSIsImdldFNlc3Npb25JZCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJ1c2VJc0luc2lkZU1vZGFsIiwiQm94IiwiVGV4dCIsInVzZVRoZW1lIiwiQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZSIsImdldEN3ZCIsImdldEN1cnJlbnRTZXNzaW9uVGl0bGUiLCJidWlsZEFjY291bnRQcm9wZXJ0aWVzIiwiYnVpbGRBUElQcm92aWRlclByb3BlcnRpZXMiLCJidWlsZElERVByb3BlcnRpZXMiLCJidWlsZEluc3RhbGxhdGlvbkRpYWdub3N0aWNzIiwiYnVpbGRJbnN0YWxsYXRpb25IZWFsdGhEaWFnbm9zdGljcyIsImJ1aWxkTWNwUHJvcGVydGllcyIsImJ1aWxkTWVtb3J5RGlhZ25vc3RpY3MiLCJidWlsZFNhbmRib3hQcm9wZXJ0aWVzIiwiYnVpbGRTZXR0aW5nU291cmNlc1Byb3BlcnRpZXMiLCJEaWFnbm9zdGljIiwiZ2V0TW9kZWxEaXNwbGF5TGFiZWwiLCJQcm9wZXJ0eSIsIlRoZW1lTmFtZSIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIlByb3BzIiwiY29udGV4dCIsImRpYWdub3N0aWNzUHJvbWlzZSIsIlByb21pc2UiLCJidWlsZFByaW1hcnlTZWN0aW9uIiwic2Vzc2lvbklkIiwiY3VzdG9tVGl0bGUiLCJuYW1lVmFsdWUiLCJsYWJlbCIsInZhbHVlIiwiTUFDUk8iLCJWRVJTSU9OIiwiYnVpbGRTZWNvbmRhcnlTZWN0aW9uIiwibWFpbkxvb3BNb2RlbCIsIm1jcCIsInRoZW1lIiwibW9kZWxMYWJlbCIsImNsaWVudHMiLCJvcHRpb25zIiwiaWRlSW5zdGFsbGF0aW9uU3RhdHVzIiwiYnVpbGREaWFnbm9zdGljcyIsIlByb3BlcnR5VmFsdWUiLCJ0MCIsIiQiLCJfYyIsIkFycmF5IiwiaXNBcnJheSIsInQxIiwidDIiLCJsZW5ndGgiLCJpdGVtIiwiaSIsIm1hcCIsIlN0YXR1cyIsIl90ZW1wIiwiX3RlbXAyIiwiU3ltYm9sIiwiZm9yIiwidDMiLCJzZWN0aW9ucyIsImdyb3ciLCJ1bmRlZmluZWQiLCJ0NCIsIl90ZW1wNCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwicHJvcGVydGllcyIsIl90ZW1wMyIsImoiLCJzXzAiLCJzIiwiRGlhZ25vc3RpY3MiLCJwcm9taXNlIiwiZGlhZ25vc3RpY3MiLCJfdGVtcDUiLCJkaWFnbm9zdGljIiwid2FybmluZyJdLCJzb3VyY2VzIjpbIlN0YXR1cy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU3VzcGVuc2UsIHVzZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZ2V0U2Vzc2lvbklkIH0gZnJvbSAnLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyB1c2VJc0luc2lkZU1vZGFsIH0gZnJvbSAnLi4vLi4vY29udGV4dC9tb2RhbENvbnRleHQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZVRoZW1lIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdHlwZSBBcHBTdGF0ZSwgdXNlQXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJy4uLy4uL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IGdldEN1cnJlbnRTZXNzaW9uVGl0bGUgfSBmcm9tICcuLi8uLi91dGlscy9zZXNzaW9uU3RvcmFnZS5qcydcbmltcG9ydCB7XG4gIGJ1aWxkQWNjb3VudFByb3BlcnRpZXMsXG4gIGJ1aWxkQVBJUHJvdmlkZXJQcm9wZXJ0aWVzLFxuICBidWlsZElERVByb3BlcnRpZXMsXG4gIGJ1aWxkSW5zdGFsbGF0aW9uRGlhZ25vc3RpY3MsXG4gIGJ1aWxkSW5zdGFsbGF0aW9uSGVhbHRoRGlhZ25vc3RpY3MsXG4gIGJ1aWxkTWNwUHJvcGVydGllcyxcbiAgYnVpbGRNZW1vcnlEaWFnbm9zdGljcyxcbiAgYnVpbGRTYW5kYm94UHJvcGVydGllcyxcbiAgYnVpbGRTZXR0aW5nU291cmNlc1Byb3BlcnRpZXMsXG4gIHR5cGUgRGlhZ25vc3RpYyxcbiAgZ2V0TW9kZWxEaXNwbGF5TGFiZWwsXG4gIHR5cGUgUHJvcGVydHksXG59IGZyb20gJy4uLy4uL3V0aWxzL3N0YXR1cy5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWVOYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGhlbWUuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGNvbnRleHQ6IExvY2FsSlNYQ29tbWFuZENvbnRleHRcbiAgZGlhZ25vc3RpY3NQcm9taXNlOiBQcm9taXNlPERpYWdub3N0aWNbXT5cbn1cblxuZnVuY3Rpb24gYnVpbGRQcmltYXJ5U2VjdGlvbigpOiBQcm9wZXJ0eVtdIHtcbiAgY29uc3Qgc2Vzc2lvbklkID0gZ2V0U2Vzc2lvbklkKClcbiAgY29uc3QgY3VzdG9tVGl0bGUgPSBnZXRDdXJyZW50U2Vzc2lvblRpdGxlKHNlc3Npb25JZClcbiAgY29uc3QgbmFtZVZhbHVlID0gY3VzdG9tVGl0bGUgPz8gPFRleHQgZGltQ29sb3I+L3JlbmFtZSB0byBhZGQgYSBuYW1lPC9UZXh0PlxuXG4gIHJldHVybiBbXG4gICAgeyBsYWJlbDogJ1ZlcnNpb24nLCB2YWx1ZTogTUFDUk8uVkVSU0lPTiB9LFxuICAgIHsgbGFiZWw6ICdTZXNzaW9uIG5hbWUnLCB2YWx1ZTogbmFtZVZhbHVlIH0sXG4gICAgeyBsYWJlbDogJ1Nlc3Npb24gSUQnLCB2YWx1ZTogc2Vzc2lvbklkIH0sXG4gICAgeyBsYWJlbDogJ2N3ZCcsIHZhbHVlOiBnZXRDd2QoKSB9LFxuICAgIC4uLmJ1aWxkQWNjb3VudFByb3BlcnRpZXMoKSxcbiAgICAuLi5idWlsZEFQSVByb3ZpZGVyUHJvcGVydGllcygpLFxuICBdXG59XG5cbmZ1bmN0aW9uIGJ1aWxkU2Vjb25kYXJ5U2VjdGlvbih7XG4gIG1haW5Mb29wTW9kZWwsXG4gIG1jcCxcbiAgdGhlbWUsXG4gIGNvbnRleHQsXG59OiB7XG4gIG1haW5Mb29wTW9kZWw6IEFwcFN0YXRlWydtYWluTG9vcE1vZGVsJ11cbiAgbWNwOiBBcHBTdGF0ZVsnbWNwJ11cbiAgdGhlbWU6IFRoZW1lTmFtZVxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0XG59KTogUHJvcGVydHlbXSB7XG4gIGNvbnN0IG1vZGVsTGFiZWwgPSBnZXRNb2RlbERpc3BsYXlMYWJlbChtYWluTG9vcE1vZGVsKVxuXG4gIHJldHVybiBbXG4gICAgeyBsYWJlbDogJ01vZGVsJywgdmFsdWU6IG1vZGVsTGFiZWwgfSxcbiAgICAuLi5idWlsZElERVByb3BlcnRpZXMoXG4gICAgICBtY3AuY2xpZW50cyxcbiAgICAgIGNvbnRleHQub3B0aW9ucy5pZGVJbnN0YWxsYXRpb25TdGF0dXMsXG4gICAgICB0aGVtZSxcbiAgICApLFxuICAgIC4uLmJ1aWxkTWNwUHJvcGVydGllcyhtY3AuY2xpZW50cywgdGhlbWUpLFxuICAgIC4uLmJ1aWxkU2FuZGJveFByb3BlcnRpZXMoKSxcbiAgICAuLi5idWlsZFNldHRpbmdTb3VyY2VzUHJvcGVydGllcygpLFxuICBdXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBidWlsZERpYWdub3N0aWNzKCk6IFByb21pc2U8RGlhZ25vc3RpY1tdPiB7XG4gIHJldHVybiBbXG4gICAgLi4uKGF3YWl0IGJ1aWxkSW5zdGFsbGF0aW9uRGlhZ25vc3RpY3MoKSksXG4gICAgLi4uKGF3YWl0IGJ1aWxkSW5zdGFsbGF0aW9uSGVhbHRoRGlhZ25vc3RpY3MoKSksXG4gICAgLi4uKGF3YWl0IGJ1aWxkTWVtb3J5RGlhZ25vc3RpY3MoKSksXG4gIF1cbn1cblxuZnVuY3Rpb24gUHJvcGVydHlWYWx1ZSh7XG4gIHZhbHVlLFxufToge1xuICB2YWx1ZTogUHJvcGVydHlbJ3ZhbHVlJ11cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBjb2x1bW5HYXA9ezF9IGZsZXhTaHJpbms9ezk5fT5cbiAgICAgICAge3ZhbHVlLm1hcCgoaXRlbSwgaSkgPT4ge1xuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8VGV4dCBrZXk9e2l9PlxuICAgICAgICAgICAgICB7aXRlbX1cbiAgICAgICAgICAgICAge2kgPCB2YWx1ZS5sZW5ndGggLSAxID8gJywnIDogJyd9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIDxUZXh0Pnt2YWx1ZX08L1RleHQ+XG4gIH1cblxuICByZXR1cm4gdmFsdWVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFN0YXR1cyh7XG4gIGNvbnRleHQsXG4gIGRpYWdub3N0aWNzUHJvbWlzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgbWFpbkxvb3BNb2RlbCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsKVxuICBjb25zdCBtY3AgPSB1c2VBcHBTdGF0ZShzID0+IHMubWNwKVxuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuXG4gIC8vIFNlY3Rpb25zIGFyZSBzeW5jaHJvbm91cyDigJQgY29tcHV0ZSBpbiByZW5kZXIgc28gdGhleSdyZSBuZXZlciBlbXB0eS5cbiAgLy8gZGlhZ25vc3RpY3NQcm9taXNlIGlzIGNyZWF0ZWQgb25jZSBpbiBTZXR0aW5ncy50c3ggc28gaXQgcmVzb2x2ZXMgb25jZVxuICAvLyBwZXIgcGFuZSBpbnZvY2F0aW9uIGluc3RlYWQgb2YgcmUtZmV0Y2hpbmcgb24gZXZlcnkgdGFiIHN3aXRjaCAoVGFiXG4gIC8vIHVubW91bnRzIGNoaWxkcmVuIHdoZW4gbm90IHNlbGVjdGVkLCB3aGljaCB3YXMgY2F1c2luZyB0aGUgZmxhc2gpLlxuICBjb25zdCBzZWN0aW9ucyA9IFJlYWN0LnVzZU1lbW8oXG4gICAgKCkgPT4gW1xuICAgICAgYnVpbGRQcmltYXJ5U2VjdGlvbigpLFxuICAgICAgYnVpbGRTZWNvbmRhcnlTZWN0aW9uKHsgbWFpbkxvb3BNb2RlbCwgbWNwLCB0aGVtZSwgY29udGV4dCB9KSxcbiAgICBdLFxuICAgIFttYWluTG9vcE1vZGVsLCBtY3AsIHRoZW1lLCBjb250ZXh0XSxcbiAgKVxuXG4gIC8vIGZsZXhHcm93IHNvIHRoZSBcIkVzYyB0byBjYW5jZWxcIiBmb290ZXIgcGlucyB0byB0aGUgYm90dG9tIG9mIHRoZVxuICAvLyBNb2RhbCdzIGlubmVyIFNjcm9sbEJveCB3aGVuIGNvbnRlbnQgaXMgc2hvcnQuIFRoZSBTY3JvbGxCb3ggY29udGVudFxuICAvLyB3cmFwcGVyIGhhcyBmbGV4R3JvdzoxIChmaWxscyBhdCBsZWFzdCB0aGUgdmlld3BvcnQpLCBzbyB0aGlzIHN0cmV0Y2hlc1xuICAvLyB0byBtYXRjaC4gV2l0aG91dCBpdCwgc2hvcnQgU3RhdHVzIGNvbnRlbnQgZmxvYXRzIGF0IHRoZSB0b3AgYW5kIHRoZVxuICAvLyBmb290ZXIgc2l0cyBtaWQtbW9kYWwgd2l0aCAyLTMgdHJhaWxpbmcgYmxhbmsgcm93cyBiZWxvdy4gT3V0c2lkZSBhXG4gIC8vIE1vZGFsIChub24tZnVsbHNjcmVlbiksIGxlYXZlIGxheW91dCBhbG9uZSDigJQgbm8gU2Nyb2xsQm94IHRvIGZpbGwuXG4gIGNvbnN0IGdyb3cgPSB1c2VJc0luc2lkZU1vZGFsKCkgPyAxIDogdW5kZWZpbmVkXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4R3Jvdz17Z3Jvd30+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IGZsZXhHcm93PXtncm93fT5cbiAgICAgICAge3NlY3Rpb25zLm1hcChcbiAgICAgICAgICAocHJvcGVydGllcywgaSkgPT5cbiAgICAgICAgICAgIHByb3BlcnRpZXMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgICAgIDxCb3gga2V5PXtpfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICAgICAge3Byb3BlcnRpZXMubWFwKCh7IGxhYmVsLCB2YWx1ZSB9LCBqKSA9PiAoXG4gICAgICAgICAgICAgICAgICA8Qm94IGtleT17an0gZmxleERpcmVjdGlvbj1cInJvd1wiIGdhcD17MX0gZmxleFNocmluaz17MH0+XG4gICAgICAgICAgICAgICAgICAgIHtsYWJlbCAhPT0gdW5kZWZpbmVkICYmIDxUZXh0IGJvbGQ+e2xhYmVsfTo8L1RleHQ+fVxuICAgICAgICAgICAgICAgICAgICA8UHJvcGVydHlWYWx1ZSB2YWx1ZT17dmFsdWV9IC8+XG4gICAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICApKX1cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApLFxuICAgICAgICApfVxuXG4gICAgICAgIDxTdXNwZW5zZSBmYWxsYmFjaz17bnVsbH0+XG4gICAgICAgICAgPERpYWdub3N0aWNzIHByb21pc2U9e2RpYWdub3N0aWNzUHJvbWlzZX0gLz5cbiAgICAgICAgPC9TdXNwZW5zZT5cbiAgICAgIDwvQm94PlxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICBjb250ZXh0PVwiU2V0dGluZ3NcIlxuICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgIC8+XG4gICAgICA8L1RleHQ+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZnVuY3Rpb24gRGlhZ25vc3RpY3Moe1xuICBwcm9taXNlLFxufToge1xuICBwcm9taXNlOiBQcm9taXNlPERpYWdub3N0aWNbXT5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBkaWFnbm9zdGljcyA9IHVzZShwcm9taXNlKVxuICBpZiAoZGlhZ25vc3RpY3MubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdCb3R0b209ezF9PlxuICAgICAgPFRleHQgYm9sZD5TeXN0ZW0gRGlhZ25vc3RpY3M8L1RleHQ+XG4gICAgICB7ZGlhZ25vc3RpY3MubWFwKChkaWFnbm9zdGljLCBpKSA9PiAoXG4gICAgICAgIDxCb3gga2V5PXtpfSBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfSBwYWRkaW5nWD17MX0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntmaWd1cmVzLndhcm5pbmd9PC9UZXh0PlxuICAgICAgICAgIHt0eXBlb2YgZGlhZ25vc3RpYyA9PT0gJ3N0cmluZycgPyAoXG4gICAgICAgICAgICA8VGV4dCB3cmFwPVwid3JhcFwiPntkaWFnbm9zdGljfTwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgZGlhZ25vc3RpY1xuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKSl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsUUFBUSxFQUFFQyxHQUFHLFFBQVEsT0FBTztBQUNyQyxTQUFTQyxZQUFZLFFBQVEsMEJBQTBCO0FBQ3ZELGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMv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 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJTdXNwZW5zZSIsInVzZSIsImdldFNlc3Npb25JZCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJ1c2VJc0luc2lkZU1vZGFsIiwiQm94IiwiVGV4dCIsInVzZVRoZW1lIiwiQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZSIsImdldEN3ZCIsImdldEN1cnJlbnRTZXNzaW9uVGl0bGUiLCJidWlsZEFjY291bnRQcm9wZXJ0aWVzIiwiYnVpbGRBUElQcm92aWRlclByb3BlcnRpZXMiLCJidWlsZElERVByb3BlcnRpZXMiLCJidWlsZEluc3RhbGxhdGlvbkRpYWdub3N0aWNzIiwiYnVpbGRJbnN0YWxsYXRpb25IZWFsdGhEaWFnbm9zdGljcyIsImJ1aWxkTWNwUHJvcGVydGllcyIsImJ1aWxkTWVtb3J5RGlhZ25vc3RpY3MiLCJidWlsZFNhbmRib3hQcm9wZXJ0aWVzIiwiYnVpbGRTZXR0aW5nU291cmNlc1Byb3BlcnRpZXMiLCJEaWFnbm9zdGljIiwiZ2V0TW9kZWxEaXNwbGF5TGFiZWwiLCJQcm9wZXJ0eSIsIlRoZW1lTmFtZSIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIlByb3BzIiwiY29udGV4dCIsImRpYWdub3N0aWNzUHJvbWlzZSIsIlByb21pc2UiLCJidWlsZFByaW1hcnlTZWN0aW9uIiwic2Vzc2lvbklkIiwiY3VzdG9tVGl0bGUiLCJuYW1lVmFsdWUiLCJsYWJlbCIsInZhbHVlIiwiTUFDUk8iLCJWRVJTSU9OIiwiYnVpbGRTZWNvbmRhcnlTZWN0aW9uIiwibWFpbkxvb3BNb2RlbCIsIm1jcCIsInRoZW1lIiwibW9kZWxMYWJlbCIsImNsaWVudHMiLCJvcHRpb25zIiwiaWRlSW5zdGFsbGF0aW9uU3RhdHVzIiwiYnVpbGREaWFnbm9zdGljcyIsIlByb3BlcnR5VmFsdWUiLCJ0MCIsIiQiLCJfYyIsIkFycmF5IiwiaXNBcnJheSIsInQxIiwidDIiLCJsZW5ndGgiLCJpdGVtIiwiaSIsIm1hcCIsIlN0YXR1cyIsIl90ZW1wIiwiX3RlbXAyIiwiU3ltYm9sIiwiZm9yIiwidDMiLCJzZWN0aW9ucyIsImdyb3ciLCJ1bmRlZmluZWQiLCJ0NCIsIl90ZW1wNCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwicHJvcGVydGllcyIsIl90ZW1wMyIsImoiLCJzXzAiLCJzIiwiRGlhZ25vc3RpY3MiLCJwcm9taXNlIiwiZGlhZ25vc3RpY3MiLCJfdGVtcDUiLCJkaWFnbm9zdGljIiwid2FybmluZyJdLCJzb3VyY2VzIjpbIlN0YXR1cy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU3VzcGVuc2UsIHVzZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZ2V0U2Vzc2lvbklkIH0gZnJvbSAnLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyB1c2VJc0luc2lkZU1vZGFsIH0gZnJvbSAnLi4vLi4vY29udGV4dC9tb2RhbENvbnRleHQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZVRoZW1lIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdHlwZSBBcHBTdGF0ZSwgdXNlQXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJy4uLy4uL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IGdldEN1cnJlbnRTZXNzaW9uVGl0bGUgfSBmcm9tICcuLi8uLi91dGlscy9zZXNzaW9uU3RvcmFnZS5qcydcbmltcG9ydCB7XG4gIGJ1aWxkQWNjb3VudFByb3BlcnRpZXMsXG4gIGJ1aWxkQVBJUHJvdmlkZXJQcm9wZXJ0aWVzLFxuICBidWlsZElERVByb3BlcnRpZXMsXG4gIGJ1aWxkSW5zdGFsbGF0aW9uRGlhZ25vc3RpY3MsXG4gIGJ1aWxkSW5zdGFsbGF0aW9uSGVhbHRoRGlhZ25vc3RpY3MsXG4gIGJ1aWxkTWNwUHJvcGVydGllcyxcbiAgYnVpbGRNZW1vcnlEaWFnbm9zdGljcyxcbiAgYnVpbGRTYW5kYm94UHJvcGVydGllcyxcbiAgYnVpbGRTZXR0aW5nU291cmNlc1Byb3BlcnRpZXMsXG4gIHR5cGUgRGlhZ25vc3RpYyxcbiAgZ2V0TW9kZWxEaXNwbGF5TGFiZWwsXG4gIHR5cGUgUHJvcGVydHksXG59IGZyb20gJy4uLy4uL3V0aWxzL3N0YXR1cy5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWVOYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGhlbWUuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGNvbnRleHQ6IExvY2FsSlNYQ29tbWFuZENvbnRleHRcbiAgZGlhZ25vc3RpY3NQcm9taXNlOiBQcm9taXNlPERpYWdub3N0aWNbXT5cbn1cblxuZnVuY3Rpb24gYnVpbGRQcmltYXJ5U2VjdGlvbigpOiBQcm9wZXJ0eVtdIHtcbiAgY29uc3Qgc2Vzc2lvbklkID0gZ2V0U2Vzc2lvbklkKClcbiAgY29uc3QgY3VzdG9tVGl0bGUgPSBnZXRDdXJyZW50U2Vzc2lvblRpdGxlKHNlc3Npb25JZClcbiAgY29uc3QgbmFtZVZhbHVlID0gY3VzdG9tVGl0bGUgPz8gPFRleHQgZGltQ29sb3I+L3JlbmFtZSB0byBhZGQgYSBuYW1lPC9UZXh0PlxuXG4gIHJldHVybiBbXG4gICAgeyBsYWJlbDogJ1ZlcnNpb24nLCB2YWx1ZTogTUFDUk8uVkVSU0lPTiB9LFxuICAgIHsgbGFiZWw6ICdTZXNzaW9uIG5hbWUnLCB2YWx1ZTogbmFtZVZhbHVlIH0sXG4gICAgeyBsYWJlbDogJ1Nlc3Npb24gSUQnLCB2YWx1ZTogc2Vzc2lvbklkIH0sXG4gICAgeyBsYWJlbDogJ2N3ZCcsIHZhbHVlOiBnZXRDd2QoKSB9LFxuICAgIC4uLmJ1aWxkQWNjb3VudFByb3BlcnRpZXMoKSxcbiAgICAuLi5idWlsZEFQSVByb3ZpZGVyUHJvcGVydGllcygpLFxuICBdXG59XG5cbmZ1bmN0aW9uIGJ1aWxkU2Vjb25kYXJ5U2VjdGlvbih7XG4gIG1haW5Mb29wTW9kZWwsXG4gIG1jcCxcbiAgdGhlbWUsXG4gIGNvbnRleHQsXG59OiB7XG4gIG1haW5Mb29wTW9kZWw6IEFwcFN0YXRlWydtYWluTG9vcE1vZGVsJ11cbiAgbWNwOiBBcHBTdGF0ZVsnbWNwJ11cbiAgdGhlbWU6IFRoZW1lTmFtZVxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0XG59KTogUHJvcGVydHlbXSB7XG4gIGNvbnN0IG1vZGVsTGFiZWwgPSBnZXRNb2RlbERpc3BsYXlMYWJlbChtYWluTG9vcE1vZGVsKVxuXG4gIHJldHVybiBbXG4gICAgeyBsYWJlbDogJ01vZGVsJywgdmFsdWU6IG1vZGVsTGFiZWwgfSxcbiAgICAuLi5idWlsZElERVByb3BlcnRpZXMoXG4gICAgICBtY3AuY2xpZW50cyxcbiAgICAgIGNvbnRleHQub3B0aW9ucy5pZGVJbnN0YWxsYXRpb25TdGF0dXMsXG4gICAgICB0aGVtZSxcbiAgICApLFxuICAgIC4uLmJ1aWxkTWNwUHJvcGVydGllcyhtY3AuY2xpZW50cywgdGhlbWUpLFxuICAgIC4uLmJ1aWxkU2FuZGJveFByb3BlcnRpZXMoKSxcbiAgICAuLi5idWlsZFNldHRpbmdTb3VyY2VzUHJvcGVydGllcygpLFxuICBdXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBidWlsZERpYWdub3N0aWNzKCk6IFByb21pc2U8RGlhZ25vc3RpY1tdPiB7XG4gIHJldHVybiBbXG4gICAgLi4uKGF3YWl0IGJ1aWxkSW5zdGFsbGF0aW9uRGlhZ25vc3RpY3MoKSksXG4gICAgLi4uKGF3YWl0IGJ1aWxkSW5zdGFsbGF0aW9uSGVhbHRoRGlhZ25vc3RpY3MoKSksXG4gICAgLi4uKGF3YWl0IGJ1aWxkTWVtb3J5RGlhZ25vc3RpY3MoKSksXG4gIF1cbn1cblxuZnVuY3Rpb24gUHJvcGVydHlWYWx1ZSh7XG4gIHZhbHVlLFxufToge1xuICB2YWx1ZTogUHJvcGVydHlbJ3ZhbHVlJ11cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBjb2x1bW5HYXA9ezF9IGZsZXhTaHJpbms9ezk5fT5cbiAgICAgICAge3ZhbHVlLm1hcCgoaXRlbSwgaSkgPT4ge1xuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8VGV4dCBrZXk9e2l9PlxuICAgICAgICAgICAgICB7aXRlbX1cbiAgICAgICAgICAgICAge2kgPCB2YWx1ZS5sZW5ndGggLSAxID8gJywnIDogJyd9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIDxUZXh0Pnt2YWx1ZX08L1RleHQ+XG4gIH1cblxuICByZXR1cm4gdmFsdWVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFN0YXR1cyh7XG4gIGNvbnRleHQsXG4gIGRpYWdub3N0aWNzUHJvbWlzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgbWFpbkxvb3BNb2RlbCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsKVxuICBjb25zdCBtY3AgPSB1c2VBcHBTdGF0ZShzID0+IHMubWNwKVxuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuXG4gIC8vIFNlY3Rpb25zIGFyZSBzeW5jaHJvbm91cyDigJQgY29tcHV0ZSBpbiByZW5kZXIgc28gdGhleSdyZSBuZXZlciBlbXB0eS5cbiAgLy8gZGlhZ25vc3RpY3NQcm9taXNlIGlzIGNyZWF0ZWQgb25jZSBpbiBTZXR0aW5ncy50c3ggc28gaXQgcmVzb2x2ZXMgb25jZVxuICAvLyBwZXIgcGFuZSBpbnZvY2F0aW9uIGluc3RlYWQgb2YgcmUtZmV0Y2hpbmcgb24gZXZlcnkgdGFiIHN3aXRjaCAoVGFiXG4gIC8vIHVubW91bnRzIGNoaWxkcmVuIHdoZW4gbm90IHNlbGVjdGVkLCB3aGljaCB3YXMgY2F1c2luZyB0aGUgZmxhc2gpLlxuICBjb25zdCBzZWN0aW9ucyA9IFJlYWN0LnVzZU1lbW8oXG4gICAgKCkgPT4gW1xuICAgICAgYnVpbGRQcmltYXJ5U2VjdGlvbigpLFxuICAgICAgYnVpbGRTZWNvbmRhcnlTZWN0aW9uKHsgbWFpbkxvb3BNb2RlbCwgbWNwLCB0aGVtZSwgY29udGV4dCB9KSxcbiAgICBdLFxuICAgIFttYWluTG9vcE1vZGVsLCBtY3AsIHRoZW1lLCBjb250ZXh0XSxcbiAgKVxuXG4gIC8vIGZsZXhHcm93IHNvIHRoZSBcIkVzYyB0byBjYW5jZWxcIiBmb290ZXIgcGlucyB0byB0aGUgYm90dG9tIG9mIHRoZVxuICAvLyBNb2RhbCdzIGlubmVyIFNjcm9sbEJveCB3aGVuIGNvbnRlbnQgaXMgc2hvcnQuIFRoZSBTY3JvbGxCb3ggY29udGVudFxuICAvLyB3cmFwcGVyIGhhcyBmbGV4R3JvdzoxIChmaWxscyBhdCBsZWFzdCB0aGUgdmlld3BvcnQpLCBzbyB0aGlzIHN0cmV0Y2hlc1xuICAvLyB0byBtYXRjaC4gV2l0aG91dCBpdCwgc2hvcnQgU3RhdHVzIGNvbnRlbnQgZmxvYXRzIGF0IHRoZSB0b3AgYW5kIHRoZVxuICAvLyBmb290ZXIgc2l0cyBtaWQtbW9kYWwgd2l0aCAyLTMgdHJhaWxpbmcgYmxhbmsgcm93cyBiZWxvdy4gT3V0c2lkZSBhXG4gIC8vIE1vZGFsIChub24tZnVsbHNjcmVlbiksIGxlYXZlIGxheW91dCBhbG9uZSDigJQgbm8gU2Nyb2xsQm94IHRvIGZpbGwuXG4gIGNvbnN0IGdyb3cgPSB1c2VJc0luc2lkZU1vZGFsKCkgPyAxIDogdW5kZWZpbmVkXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4R3Jvdz17Z3Jvd30+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IGZsZXhHcm93PXtncm93fT5cbiAgICAgICAge3NlY3Rpb25zLm1hcChcbiAgICAgICAgICAocHJvcGVydGllcywgaSkgPT5cbiAgICAgICAgICAgIHByb3BlcnRpZXMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgICAgIDxCb3gga2V5PXtpfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICAgICAge3Byb3BlcnRpZXMubWFwKCh7IGxhYmVsLCB2YWx1ZSB9LCBqKSA9PiAoXG4gICAgICAgICAgICAgICAgICA8Qm94IGtleT17an0gZmxleERpcmVjdGlvbj1cInJvd1wiIGdhcD17MX0gZmxleFNocmluaz17MH0+XG4gICAgICAgICAgICAgICAgICAgIHtsYWJlbCAhPT0gdW5kZWZpbmVkICYmIDxUZXh0IGJvbGQ+e2xhYmVsfTo8L1RleHQ+fVxuICAgICAgICAgICAgICAgICAgICA8UHJvcGVydHlWYWx1ZSB2YWx1ZT17dmFsdWV9IC8+XG4gICAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICApKX1cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApLFxuICAgICAgICApfVxuXG4gICAgICAgIDxTdXNwZW5zZSBmYWxsYmFjaz17bnVsbH0+XG4gICAgICAgICAgPERpYWdub3N0aWNzIHByb21pc2U9e2RpYWdub3N0aWNzUHJvbWlzZX0gLz5cbiAgICAgICAgPC9TdXNwZW5zZT5cbiAgICAgIDwvQm94PlxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICBjb250ZXh0PVwiU2V0dGluZ3NcIlxuICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgIC8+XG4gICAgICA8L1RleHQ+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZnVuY3Rpb24gRGlhZ25vc3RpY3Moe1xuICBwcm9taXNlLFxufToge1xuICBwcm9taXNlOiBQcm9taXNlPERpYWdub3N0aWNbXT5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBkaWFnbm9zdGljcyA9IHVzZShwcm9taXNlKVxuICBpZiAoZGlhZ25vc3RpY3MubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdCb3R0b209ezF9PlxuICAgICAgPFRleHQgYm9sZD5TeXN0ZW0gRGlhZ25vc3RpY3M8L1RleHQ+XG4gICAgICB7ZGlhZ25vc3RpY3MubWFwKChkaWFnbm9zdGljLCBpKSA9PiAoXG4gICAgICAgIDxCb3gga2V5PXtpfSBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfSBwYWRkaW5nWD17MX0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntmaWd1cmVzLndhcm5pbmd9PC9UZXh0PlxuICAgICAgICAgIHt0eXBlb2YgZGlhZ25vc3RpYyA9PT0gJ3N0cmluZycgPyAoXG4gICAgICAgICAgICA8VGV4dCB3cmFwPVwid3JhcFwiPntkaWFnbm9zdGljfTwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgZGlhZ25vc3RpY1xuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKSl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsUUFBUSxFQUFFQyxHQUFHLFFBQVEsT0FBTztBQUNyQyxTQUFTQyxZQUFZLFFBQVEsMEJBQTBCO0FBQ3ZELGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMv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 From b4aa27183de8d95c44e3af841146d99088d06842 Mon Sep 17 00:00:00 2001 From: gnanam1990 Date: Thu, 2 Apr 2026 21:51:26 +0530 Subject: [PATCH 4/4] fix: route CLAUDE_CODE_USE_GEMINI through OpenAI-compatible shim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gemini provider uses Google's OpenAI-compatible endpoint (generativelanguage.googleapis.com/v1beta/openai) but the client routing condition in client.ts only checked CLAUDE_CODE_USE_OPENAI and CLAUDE_CODE_USE_GITHUB — CLAUDE_CODE_USE_GEMINI was missing. This caused every Gemini request to fall through to the Anthropic client path. Since ANTHROPIC_API_KEY is not set when using Gemini, the Anthropic SDK threw: "Could not resolve authentication method. Expected either apiKey or authToken to be set." Fix: add CLAUDE_CODE_USE_GEMINI to the OpenAI shim routing condition so Gemini requests correctly reach createOpenAIShimClient(), which maps GEMINI_API_KEY → OPENAI_API_KEY and sets OPENAI_BASE_URL to the Google endpoint. Closes #176 Co-Authored-By: Claude Sonnet 4.6 --- src/services/api/client.test.ts | 121 ++++++++++++++++++++++++++++++++ src/services/api/client.ts | 3 +- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/services/api/client.test.ts diff --git a/src/services/api/client.test.ts b/src/services/api/client.test.ts new file mode 100644 index 00000000..6d92be7b --- /dev/null +++ b/src/services/api/client.test.ts @@ -0,0 +1,121 @@ +import { afterEach, beforeEach, expect, test } from 'bun:test' +import { getAnthropicClient } from './client.js' + +type FetchType = typeof globalThis.fetch + +type ShimClient = { + beta: { + messages: { + create: (params: Record) => Promise + } + } +} + +const originalFetch = globalThis.fetch +const originalMacro = (globalThis as Record).MACRO +const originalEnv = { + CLAUDE_CODE_USE_GEMINI: process.env.CLAUDE_CODE_USE_GEMINI, + GEMINI_API_KEY: process.env.GEMINI_API_KEY, + GEMINI_MODEL: process.env.GEMINI_MODEL, + GEMINI_BASE_URL: process.env.GEMINI_BASE_URL, + GOOGLE_API_KEY: process.env.GOOGLE_API_KEY, + OPENAI_API_KEY: process.env.OPENAI_API_KEY, + OPENAI_BASE_URL: process.env.OPENAI_BASE_URL, + OPENAI_MODEL: process.env.OPENAI_MODEL, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN, +} + +beforeEach(() => { + ;(globalThis as Record).MACRO = { VERSION: 'test-version' } + process.env.CLAUDE_CODE_USE_GEMINI = '1' + process.env.GEMINI_API_KEY = 'gemini-test-key' + process.env.GEMINI_MODEL = 'gemini-2.0-flash' + process.env.GEMINI_BASE_URL = 'https://gemini.example/v1beta/openai' + + delete process.env.GOOGLE_API_KEY + delete process.env.OPENAI_API_KEY + delete process.env.OPENAI_BASE_URL + delete process.env.OPENAI_MODEL + delete process.env.ANTHROPIC_API_KEY + delete process.env.ANTHROPIC_AUTH_TOKEN +}) + +afterEach(() => { + ;(globalThis as Record).MACRO = originalMacro + process.env.CLAUDE_CODE_USE_GEMINI = originalEnv.CLAUDE_CODE_USE_GEMINI + process.env.GEMINI_API_KEY = originalEnv.GEMINI_API_KEY + process.env.GEMINI_MODEL = originalEnv.GEMINI_MODEL + process.env.GEMINI_BASE_URL = originalEnv.GEMINI_BASE_URL + process.env.GOOGLE_API_KEY = originalEnv.GOOGLE_API_KEY + process.env.OPENAI_API_KEY = originalEnv.OPENAI_API_KEY + process.env.OPENAI_BASE_URL = originalEnv.OPENAI_BASE_URL + process.env.OPENAI_MODEL = originalEnv.OPENAI_MODEL + process.env.ANTHROPIC_API_KEY = originalEnv.ANTHROPIC_API_KEY + process.env.ANTHROPIC_AUTH_TOKEN = originalEnv.ANTHROPIC_AUTH_TOKEN + globalThis.fetch = originalFetch +}) + +test('routes Gemini provider requests through the OpenAI-compatible shim', async () => { + let capturedUrl: string | undefined + let capturedHeaders: Headers | undefined + let capturedBody: Record | undefined + + globalThis.fetch = (async (input, init) => { + capturedUrl = + typeof input === 'string' + ? input + : input instanceof URL + ? input.toString() + : input.url + capturedHeaders = new Headers(init?.headers) + capturedBody = JSON.parse(String(init?.body)) as Record + + return new Response( + JSON.stringify({ + id: 'chatcmpl-gemini', + model: 'gemini-2.0-flash', + choices: [ + { + message: { + role: 'assistant', + content: 'gemini ok', + }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 8, + completion_tokens: 3, + total_tokens: 11, + }, + }), + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ) + }) as FetchType + + const client = (await getAnthropicClient({ + maxRetries: 0, + model: 'gemini-2.0-flash', + })) as unknown as ShimClient + + const response = await client.beta.messages.create({ + model: 'gemini-2.0-flash', + system: 'test system', + messages: [{ role: 'user', content: 'hello' }], + max_tokens: 64, + stream: false, + }) + + expect(capturedUrl).toBe('https://gemini.example/v1beta/openai/chat/completions') + expect(capturedHeaders?.get('authorization')).toBe('Bearer gemini-test-key') + expect(capturedBody?.model).toBe('gemini-2.0-flash') + expect(response).toMatchObject({ + role: 'assistant', + model: 'gemini-2.0-flash', + }) +}) diff --git a/src/services/api/client.ts b/src/services/api/client.ts index ee50e35c..a32e0779 100644 --- a/src/services/api/client.ts +++ b/src/services/api/client.ts @@ -156,7 +156,8 @@ export async function getAnthropicClient({ } if ( isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI) || - isEnvTruthy(process.env.CLAUDE_CODE_USE_GITHUB) + isEnvTruthy(process.env.CLAUDE_CODE_USE_GITHUB) || + isEnvTruthy(process.env.CLAUDE_CODE_USE_GEMINI) ) { const { createOpenAIShimClient } = await import('./openaiShim.js') return createOpenAIShimClient({