@bitkyc08/opencodex 2.6.15 → 2.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,7 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-4jSPBLbx.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-D3GNd8DI.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-DIBiVVC0.css">
21
21
  </head>
22
22
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkyc08/opencodex",
3
- "version": "2.6.15",
3
+ "version": "2.6.16",
4
4
  "description": "Universal provider proxy for OpenAI Codex — use any LLM with Codex CLI/App/SDK",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -47,6 +47,14 @@ function withPromptCache<T extends Record<string, unknown>>(block: T): T & { cac
47
47
  return { ...block, cache_control: EPHEMERAL_CACHE_CONTROL };
48
48
  }
49
49
 
50
+ function usesNativeAnthropicEndpoint(provider: OcxProviderConfig): boolean {
51
+ try {
52
+ return new URL(provider.baseUrl).hostname === "api.anthropic.com";
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+
50
58
  /** Map a Responses reasoning effort to an Anthropic extended-thinking budget (tokens, >= 1024). */
51
59
  function reasoningBudget(effort: string): number {
52
60
  switch (effort) {
@@ -66,7 +74,11 @@ function usageFromAnthropic(usage: Record<string, number> | undefined): OcxUsage
66
74
  return {
67
75
  inputTokens: usage.input_tokens ?? 0,
68
76
  outputTokens: usage.output_tokens ?? 0,
69
- ...(hasCache ? { cachedInputTokens: (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) } : {}),
77
+ ...(hasCache ? {
78
+ cachedInputTokens: (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0),
79
+ cacheReadInputTokens: usage.cache_read_input_tokens ?? 0,
80
+ cacheCreationInputTokens: usage.cache_creation_input_tokens ?? 0,
81
+ } : {}),
70
82
  };
71
83
  }
72
84
 
@@ -234,6 +246,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig): ProviderAda
234
246
  stream: parsed.stream,
235
247
  max_tokens: parsed.options.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
236
248
  };
249
+ if (usesNativeAnthropicEndpoint(provider)) body.cache_control = EPHEMERAL_CACHE_CONTROL;
237
250
  if (isOAuth) {
238
251
  // Claude OAuth (Pro/Max) requires the first system block to be the Claude Code identity.
239
252
  body.system = [
package/src/bridge.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AdapterEvent, OcxUsage } from "./types";
2
2
  import { classifyError, type OcxErrorPayload } from "./errors";
3
+ import { usageDisplayTotalTokens, usageInputTokensWithCacheDetail } from "./usage-totals";
3
4
 
4
5
  function uuid(): string {
5
6
  return crypto.randomUUID().replace(/-/g, "");
@@ -11,10 +12,11 @@ function sseEvent(name: string, data: Record<string, unknown>): string {
11
12
 
12
13
  function responsesUsage(usage: OcxUsage | undefined): Record<string, unknown> {
13
14
  if (!usage) return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
15
+ const inputTokens = usageInputTokensWithCacheDetail(usage);
14
16
  const out: Record<string, unknown> = {
15
- input_tokens: usage.inputTokens,
17
+ input_tokens: inputTokens,
16
18
  output_tokens: usage.outputTokens,
17
- total_tokens: usage.totalTokens ?? usage.inputTokens + usage.outputTokens,
19
+ total_tokens: usageDisplayTotalTokens(usage) ?? inputTokens + usage.outputTokens,
18
20
  };
19
21
  if (usage.cachedInputTokens !== undefined) {
20
22
  out.input_tokens_details = { cached_tokens: usage.cachedInputTokens };
package/src/types.ts CHANGED
@@ -209,6 +209,8 @@ export interface OcxUsage {
209
209
  outputTokens: number;
210
210
  totalTokens?: number;
211
211
  cachedInputTokens?: number;
212
+ cacheReadInputTokens?: number;
213
+ cacheCreationInputTokens?: number;
212
214
  reasoningOutputTokens?: number;
213
215
  estimated?: boolean;
214
216
  }
package/src/usage-log.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { chmodSync, existsSync, mkdirSync, readFileSync, appendFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { getConfigDir } from "./config";
4
+ import { usageDisplayTotalTokens } from "./usage-totals";
4
5
  import type { OcxUsage } from "./types";
5
6
 
6
7
  export type UsageStatus = "reported" | "unreported" | "unsupported" | "estimated";
@@ -23,8 +24,7 @@ export function usageLogPath(): string {
23
24
  }
24
25
 
25
26
  export function usageTotalTokens(usage: OcxUsage | undefined): number | undefined {
26
- if (!usage) return undefined;
27
- return usage.totalTokens ?? usage.inputTokens + usage.outputTokens;
27
+ return usageDisplayTotalTokens(usage);
28
28
  }
29
29
 
30
30
  function isKiroLogProvider(provider: string): boolean {
@@ -49,6 +49,8 @@ function normalizeUsageValue(usage: OcxUsage | undefined): OcxUsage | undefined
49
49
  outputTokens: usage.outputTokens,
50
50
  ...(typeof usage.totalTokens === "number" ? { totalTokens: usage.totalTokens } : {}),
51
51
  ...(typeof usage.cachedInputTokens === "number" ? { cachedInputTokens: usage.cachedInputTokens } : {}),
52
+ ...(typeof usage.cacheReadInputTokens === "number" ? { cacheReadInputTokens: usage.cacheReadInputTokens } : {}),
53
+ ...(typeof usage.cacheCreationInputTokens === "number" ? { cacheCreationInputTokens: usage.cacheCreationInputTokens } : {}),
52
54
  ...(typeof usage.reasoningOutputTokens === "number" ? { reasoningOutputTokens: usage.reasoningOutputTokens } : {}),
53
55
  ...(usage.estimated ? { estimated: true } : {}),
54
56
  };
@@ -1,4 +1,5 @@
1
1
  import { baseProviderLabel } from "./provider-label";
2
+ import { usageDisplayTotalTokens } from "./usage-totals";
2
3
  import type { PersistedUsageEntry, UsageStatus } from "./usage-log";
3
4
 
4
5
  export type UsageRange = "7d" | "30d" | "all";
@@ -132,8 +133,7 @@ function addTokens(totals: UsageSummaryTotals, entry: PersistedUsageEntry): void
132
133
  totals.outputTokens += entry.usage.outputTokens;
133
134
  if (typeof entry.usage.cachedInputTokens === "number") totals.cachedInputTokens += entry.usage.cachedInputTokens;
134
135
  if (typeof entry.usage.reasoningOutputTokens === "number") totals.reasoningOutputTokens += entry.usage.reasoningOutputTokens;
135
- if (typeof entry.totalTokens === "number") totals.totalTokens += entry.totalTokens;
136
- else totals.totalTokens += entry.usage.inputTokens + entry.usage.outputTokens;
136
+ totals.totalTokens += usageDisplayTotalTokens(entry.usage, entry.totalTokens) ?? 0;
137
137
  }
138
138
 
139
139
  function finalizeCoverage(totals: UsageSummaryTotals): void {
@@ -154,8 +154,7 @@ function buildDayGrid(range: UsageRange, since: number | null, now: number, entr
154
154
  let m = models.get(mKey);
155
155
  if (!m) { m = { model: entry.model, provider: entry.provider, requests: 0, totalTokens: 0 }; models.set(mKey, m); }
156
156
  m.requests += 1;
157
- if (typeof entry.totalTokens === "number") m.totalTokens += entry.totalTokens;
158
- else if (entry.usage) m.totalTokens += entry.usage.inputTokens + entry.usage.outputTokens;
157
+ m.totalTokens += usageDisplayTotalTokens(entry.usage, entry.totalTokens) ?? 0;
159
158
  };
160
159
  for (let i = days - 1; i >= 0; i--) {
161
160
  const key = localDateKey(now - i * DAY_MS);
@@ -171,8 +170,7 @@ function buildDayGrid(range: UsageRange, since: number | null, now: number, entr
171
170
  day.requests += 1;
172
171
  if (isMeasuredStatus(entry.usageStatus)) day.measuredRequests += 1;
173
172
  if (entry.usageStatus === "reported") day.reportedRequests += 1;
174
- if (typeof entry.totalTokens === "number") day.totalTokens += entry.totalTokens;
175
- else if (entry.usage) day.totalTokens += entry.usage.inputTokens + entry.usage.outputTokens;
173
+ day.totalTokens += usageDisplayTotalTokens(entry.usage, entry.totalTokens) ?? 0;
176
174
  bumpDayModel(key, entry);
177
175
  }
178
176
  void since;
@@ -216,8 +214,7 @@ function buildModels(entries: PersistedUsageEntry[], totalRequests: number): Usa
216
214
  if (entry.usage) {
217
215
  model.inputTokens += entry.usage.inputTokens;
218
216
  model.outputTokens += entry.usage.outputTokens;
219
- if (typeof entry.totalTokens === "number") model.totalTokens += entry.totalTokens;
220
- else model.totalTokens += entry.usage.inputTokens + entry.usage.outputTokens;
217
+ model.totalTokens += usageDisplayTotalTokens(entry.usage, entry.totalTokens) ?? 0;
221
218
  }
222
219
  }
223
220
  const models = [...byKey.values()];
@@ -247,8 +244,7 @@ function buildProviders(entries: PersistedUsageEntry[], totalRequests: number):
247
244
  if (entry.usageStatus === "reported") provider.reportedRequests += 1;
248
245
  else if (entry.usageStatus === "estimated") provider.estimatedRequests += 1;
249
246
  if (entry.usage) {
250
- if (typeof entry.totalTokens === "number") provider.totalTokens += entry.totalTokens;
251
- else provider.totalTokens += entry.usage.inputTokens + entry.usage.outputTokens;
247
+ provider.totalTokens += usageDisplayTotalTokens(entry.usage, entry.totalTokens) ?? 0;
252
248
  }
253
249
  }
254
250
  const providers = [...byKey.values()];
@@ -0,0 +1,25 @@
1
+ import type { OcxUsage } from "./types";
2
+
3
+ function cacheDetailTokens(usage: OcxUsage): number | undefined {
4
+ const hasRead = typeof usage.cacheReadInputTokens === "number";
5
+ const hasCreate = typeof usage.cacheCreationInputTokens === "number";
6
+ if (!hasRead && !hasCreate) return undefined;
7
+ return (usage.cacheReadInputTokens ?? 0) + (usage.cacheCreationInputTokens ?? 0);
8
+ }
9
+
10
+ export function usageInputTokensWithCacheDetail(usage: OcxUsage): number {
11
+ const cacheDetailTotal = cacheDetailTokens(usage);
12
+ return usage.inputTokens + (cacheDetailTotal ?? 0);
13
+ }
14
+
15
+ export function usageDisplayTotalTokens(usage: OcxUsage | undefined, storedTotal?: number): number | undefined {
16
+ if (!usage) return storedTotal;
17
+ const baseTotal = usage.inputTokens + usage.outputTokens;
18
+ const explicitTotal = usage.totalTokens ?? storedTotal;
19
+ const cacheDetailTotal = cacheDetailTokens(usage);
20
+ if (cacheDetailTotal !== undefined) {
21
+ const detailedTotal = baseTotal + cacheDetailTotal;
22
+ return typeof explicitTotal === "number" ? Math.max(explicitTotal, detailedTotal) : detailedTotal;
23
+ }
24
+ return explicitTotal ?? baseTotal;
25
+ }