@coseung2/opencodex 2.8.0-cs.13 → 2.8.0-cs.14
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.
- package/gui/dist/assets/index-MUpaVatk.js +67 -0
- package/gui/dist/index.html +1 -1
- package/package.json +3 -3
- package/packages/ocx-notch/README.md +2 -1
- package/src/adapters/cursor/discovery.ts +6 -2
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/google-antigravity-replay.ts +24 -0
- package/src/adapters/google.ts +16 -11
- package/src/chat/inbound.ts +5 -11
- package/src/cli/account-api.ts +9 -1
- package/src/cli/account-extended.ts +4 -1
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +12 -1
- package/src/codex/account-namespaces.ts +21 -0
- package/src/codex/account-priority.ts +49 -0
- package/src/codex/account-store.ts +2 -1
- package/src/codex/auth-api.ts +108 -17
- package/src/codex/auth-context.ts +61 -16
- package/src/codex/catalog/metadata.ts +34 -12
- package/src/codex/catalog/parsing.ts +8 -1
- package/src/codex/catalog/provider-fetch.ts +24 -6
- package/src/codex/catalog.ts +1 -1
- package/src/codex/pool-rotation.ts +51 -4
- package/src/codex/quota.ts +154 -35
- package/src/codex/routing.ts +133 -33
- package/src/codex/warmup.ts +193 -85
- package/src/config.ts +84 -1
- package/src/lib/bounded-body.ts +13 -6
- package/src/lib/bun-stream-caps.ts +5 -6
- package/src/lib/redact.ts +13 -0
- package/src/oauth/index.ts +79 -12
- package/src/oauth/log.ts +3 -1
- package/src/oauth/store.ts +31 -8
- package/src/providers/antigravity-models.ts +53 -24
- package/src/providers/codex-capacity.ts +303 -0
- package/src/providers/model-rename-migration.ts +147 -0
- package/src/providers/model-rename-startup.ts +29 -0
- package/src/providers/quota.ts +126 -16
- package/src/providers/registry.ts +258 -38
- package/src/responses/parser.ts +19 -12
- package/src/responses/spill-store.ts +14 -1
- package/src/responses/state.ts +108 -14
- package/src/server/index.ts +9 -1
- package/src/server/management/logs-usage-routes.ts +1 -0
- package/src/server/management/oauth-account-routes.ts +8 -1
- package/src/server/relay.ts +10 -42
- package/src/server/request-log.ts +42 -1
- package/src/server/responses/compact.ts +16 -4
- package/src/server/responses/core.ts +217 -59
- package/src/server/responses/empty-completion-guard.ts +275 -0
- package/src/server/responses/encrypted-payload.ts +54 -39
- package/src/server/responses/fetch-helpers.ts +24 -3
- package/src/server/responses/ws-upstream.ts +318 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/ws-bridge.ts +17 -11
- package/src/types.ts +8 -0
- package/src/usage/log.ts +24 -0
- package/src/usage/summary.ts +152 -2
- package/vendor/ocx-notch/win32-x64/ocx-notch.exe +0 -0
- package/gui/dist/assets/index-BucjyD4I.js +0 -67
package/src/types.ts
CHANGED
|
@@ -606,6 +606,8 @@ export interface OcxConfig {
|
|
|
606
606
|
* See src/lib/bun-stream-caps.ts.
|
|
607
607
|
*/
|
|
608
608
|
streamMode?: "auto" | "legacy-tee" | "eager-relay";
|
|
609
|
+
/** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
|
|
610
|
+
emptyCompletionRetry?: boolean;
|
|
609
611
|
/**
|
|
610
612
|
* Custom override for the injected multi-agent guidance body (the text inside the
|
|
611
613
|
* <multi_agent_mode> tags). When set, it replaces the built-in prompt on whichever
|
|
@@ -731,12 +733,18 @@ export interface OcxConfig {
|
|
|
731
733
|
* first non-paused account (or clears the active selection when none remain).
|
|
732
734
|
*/
|
|
733
735
|
pausedOauthAccountIds?: Record<string, string[]>;
|
|
736
|
+
/** Selection order per Codex account id; higher values are considered first. */
|
|
737
|
+
codexAccountPriorities?: Record<string, number>;
|
|
738
|
+
/** Account most recently selected manually; released when it drains or becomes unavailable. */
|
|
739
|
+
activeCodexAccountPinned?: string;
|
|
734
740
|
/**
|
|
735
741
|
* Public model-selector namespaces bound to one Codex account. Values are stored account ids;
|
|
736
742
|
* `"@main"` selects the Codex Desktop/main auth.json account. Account display aliases
|
|
737
743
|
* are intentionally separate from these selectors.
|
|
738
744
|
*/
|
|
739
745
|
codexAccountNamespaces?: Record<string, string>;
|
|
746
|
+
/** Picker visibility override; existing non-empty maps remain enabled when omitted. */
|
|
747
|
+
codexAccountPickerEnabled?: boolean;
|
|
740
748
|
/** Active pool account id for next session. undefined = main (passthrough as-is). */
|
|
741
749
|
activeCodexAccountId?: string;
|
|
742
750
|
/** Auto-switch threshold (0-100). Default 80. 0 = disabled. */
|
package/src/usage/log.ts
CHANGED
|
@@ -2,13 +2,21 @@ import { chmodSync, closeSync, existsSync, fstatSync, mkdirSync, openSync, readF
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getConfigDir } from "../config";
|
|
4
4
|
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
5
|
+
import { CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
5
6
|
import { usageDisplayTotalTokens } from "./totals";
|
|
6
7
|
import type { OcxUsage } from "../types";
|
|
8
|
+
import { sanitizeLogMetadataString } from "../lib/redact";
|
|
7
9
|
|
|
8
10
|
export type UsageStatus = "reported" | "unreported" | "unsupported" | "estimated";
|
|
11
|
+
export type CodexUsageAccountLogLabel = "main" | `p${string}`;
|
|
12
|
+
|
|
13
|
+
export function isCodexUsageAccountLogLabel(value: unknown): value is CodexUsageAccountLogLabel {
|
|
14
|
+
return value === "main" || (typeof value === "string" && CODEX_ACCOUNT_LOG_LABEL_RE.test(value));
|
|
15
|
+
}
|
|
9
16
|
|
|
10
17
|
export type AttemptRecoveryKind =
|
|
11
18
|
| "transient-5xx"
|
|
19
|
+
| "empty-completion"
|
|
12
20
|
| "connection-reset"
|
|
13
21
|
| "oauth-401"
|
|
14
22
|
| "key-429"
|
|
@@ -19,6 +27,8 @@ export interface PersistedUsageAttempt {
|
|
|
19
27
|
ordinal: number;
|
|
20
28
|
provider: string;
|
|
21
29
|
model: string;
|
|
30
|
+
/** Stable non-PII Codex Pool account identity for this serving attempt. */
|
|
31
|
+
accountLogLabel?: CodexUsageAccountLogLabel;
|
|
22
32
|
adapter: string;
|
|
23
33
|
status: number;
|
|
24
34
|
durationMs: number;
|
|
@@ -27,6 +37,7 @@ export interface PersistedUsageAttempt {
|
|
|
27
37
|
sendCount: number;
|
|
28
38
|
recoveryKinds: AttemptRecoveryKind[];
|
|
29
39
|
usageStatus: UsageStatus;
|
|
40
|
+
streamAborted?: true;
|
|
30
41
|
inputTokenEstimate?: number;
|
|
31
42
|
usage?: OcxUsage;
|
|
32
43
|
totalTokens?: number;
|
|
@@ -43,6 +54,8 @@ export interface PersistedUsageEntry {
|
|
|
43
54
|
timestamp: number;
|
|
44
55
|
provider: string;
|
|
45
56
|
model: string;
|
|
57
|
+
/** Stable non-PII Codex Pool account identity for the serving account. */
|
|
58
|
+
accountLogLabel?: CodexUsageAccountLogLabel;
|
|
46
59
|
surface?: "claude" | "claude-desktop" | "grok";
|
|
47
60
|
/** Matched configured key id; absent for environment/loopback admissions and
|
|
48
61
|
* for every row written before attribution existed. */
|
|
@@ -54,6 +67,8 @@ export interface PersistedUsageEntry {
|
|
|
54
67
|
conversationId?: string;
|
|
55
68
|
resolvedModel?: string;
|
|
56
69
|
requestedModel?: string;
|
|
70
|
+
/** Original helper model when shadow-call interception rewrote the request. */
|
|
71
|
+
shadowCallRewrittenFrom?: string;
|
|
57
72
|
/** Reasoning effort / service-tier metadata for GUI Logs after restart. */
|
|
58
73
|
requestedEffort?: string;
|
|
59
74
|
/** Adapter-normalized tier and exact upstream parameter emitted for this request. */
|
|
@@ -243,6 +258,9 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null {
|
|
|
243
258
|
provider: attempt.provider,
|
|
244
259
|
model: attempt.model,
|
|
245
260
|
adapter: attempt.adapter,
|
|
261
|
+
...(isCodexUsageAccountLogLabel(attempt.accountLogLabel)
|
|
262
|
+
? { accountLogLabel: attempt.accountLogLabel }
|
|
263
|
+
: {}),
|
|
246
264
|
status: attempt.status,
|
|
247
265
|
durationMs: attempt.durationMs,
|
|
248
266
|
...(isNonNegativeFiniteNumber(attempt.firstOutputMs)
|
|
@@ -251,6 +269,7 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null {
|
|
|
251
269
|
sendCount: attempt.sendCount as number,
|
|
252
270
|
recoveryKinds,
|
|
253
271
|
usageStatus: attempt.usageStatus as UsageStatus,
|
|
272
|
+
...(attempt.streamAborted === true ? { streamAborted: true as const } : {}),
|
|
254
273
|
...(isNonNegativeFiniteNumber(attempt.inputTokenEstimate)
|
|
255
274
|
? { inputTokenEstimate: attempt.inputTokenEstimate }
|
|
256
275
|
: {}),
|
|
@@ -294,11 +313,15 @@ export function normalizeUsageEntryForTest(entry: PersistedUsageEntry): Persiste
|
|
|
294
313
|
|
|
295
314
|
function normalizeUsageEntry(entry: PersistedUsageEntry): PersistedUsageEntry {
|
|
296
315
|
const attempts = normalizedAttempts(entry.attempts);
|
|
316
|
+
const shadowCallRewrittenFrom = sanitizeLogMetadataString(entry.shadowCallRewrittenFrom);
|
|
297
317
|
return {
|
|
298
318
|
requestId: entry.requestId,
|
|
299
319
|
timestamp: entry.timestamp,
|
|
300
320
|
provider: entry.provider,
|
|
301
321
|
model: entry.model,
|
|
322
|
+
...(isCodexUsageAccountLogLabel(entry.accountLogLabel)
|
|
323
|
+
? { accountLogLabel: entry.accountLogLabel }
|
|
324
|
+
: {}),
|
|
302
325
|
...(isKnownUsageSurface(entry.surface) ? { surface: entry.surface } : {}),
|
|
303
326
|
...(typeof entry.apiKeyId === "string" && entry.apiKeyId.trim()
|
|
304
327
|
// Deliberately NOT capped. `capMetadataString` protects free-form metadata
|
|
@@ -314,6 +337,7 @@ function normalizeUsageEntry(entry: PersistedUsageEntry): PersistedUsageEntry {
|
|
|
314
337
|
: {}),
|
|
315
338
|
...(entry.resolvedModel ? { resolvedModel: entry.resolvedModel } : {}),
|
|
316
339
|
...(entry.requestedModel ? { requestedModel: entry.requestedModel } : {}),
|
|
340
|
+
...(shadowCallRewrittenFrom ? { shadowCallRewrittenFrom } : {}),
|
|
317
341
|
...(typeof entry.requestedEffort === "string" && entry.requestedEffort
|
|
318
342
|
? { requestedEffort: capMetadataString(entry.requestedEffort) }
|
|
319
343
|
: {}),
|
package/src/usage/summary.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { baseProviderLabel } from "../providers/label";
|
|
2
2
|
import { canonicalAntigravityUsageModel } from "../providers/antigravity-models";
|
|
3
3
|
import { usageDisplayTotalTokens } from "./totals";
|
|
4
|
-
import type
|
|
5
|
-
import { estimateComboCost, estimateRequestCost, effectiveServiceTier } from "./cost";
|
|
4
|
+
import { isCodexUsageAccountLogLabel, type PersistedUsageEntry, type UsageStatus } from "./log";
|
|
5
|
+
import { estimateAttemptCost, estimateComboCost, estimateRequestCost, effectiveServiceTier } from "./cost";
|
|
6
6
|
|
|
7
7
|
export type UsageRange = "7d" | "30d" | "all";
|
|
8
8
|
export type UsageSurface = "all" | "codex" | "claude" | "grok";
|
|
@@ -79,6 +79,28 @@ export interface UsageProvider {
|
|
|
79
79
|
estimatedCostUsd?: number;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export interface UsageAccount {
|
|
83
|
+
accountLogLabel: string;
|
|
84
|
+
ambiguous: boolean;
|
|
85
|
+
requests: number;
|
|
86
|
+
attemptCount: number;
|
|
87
|
+
measuredAttempts: number;
|
|
88
|
+
reportedAttempts: number;
|
|
89
|
+
estimatedAttempts: number;
|
|
90
|
+
unmeteredAttempts: number;
|
|
91
|
+
inputTokens: number;
|
|
92
|
+
outputTokens: number;
|
|
93
|
+
cacheReadInputTokens: number;
|
|
94
|
+
cacheCreationInputTokens: number;
|
|
95
|
+
reasoningOutputTokens: number;
|
|
96
|
+
totalTokens: number;
|
|
97
|
+
usageCoverageRatio: number;
|
|
98
|
+
estimatedCostUsd?: number;
|
|
99
|
+
pricedAttempts: number;
|
|
100
|
+
unpricedAttempts: number;
|
|
101
|
+
priceCoverageRatio: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
export interface UsageSummary {
|
|
83
105
|
range: UsageRange;
|
|
84
106
|
surface: UsageSurface;
|
|
@@ -88,6 +110,7 @@ export interface UsageSummary {
|
|
|
88
110
|
days: UsageDay[];
|
|
89
111
|
models: UsageModel[];
|
|
90
112
|
providers: UsageProvider[];
|
|
113
|
+
accounts: UsageAccount[];
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
const DAY_MS = 86_400_000;
|
|
@@ -547,6 +570,132 @@ function buildProviders(entries: PersistedUsageEntry[], totalTokens: number): Us
|
|
|
547
570
|
return providers.sort((a, b) => b.requests - a.requests);
|
|
548
571
|
}
|
|
549
572
|
|
|
573
|
+
const LEGACY_AMBIGUOUS_ACCOUNT_LABEL = "legacy-ambiguous";
|
|
574
|
+
|
|
575
|
+
function legacyCodexAccountLabel(provider: string): string | null {
|
|
576
|
+
if (baseProviderLabel(provider) !== "openai") return null;
|
|
577
|
+
return provider.match(/-(main|p[a-f0-9]{6})$/)?.[1] ?? LEGACY_AMBIGUOUS_ACCOUNT_LABEL;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function accountLabelForAttribution(provider: string, explicit: unknown): string | null {
|
|
581
|
+
return isCodexUsageAccountLogLabel(explicit) ? explicit : legacyCodexAccountLabel(provider);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function buildAccounts(entries: PersistedUsageEntry[]): UsageAccount[] {
|
|
585
|
+
const byLabel = new Map<string, UsageAccount>();
|
|
586
|
+
const requestIds = new Map<string, Set<string>>();
|
|
587
|
+
|
|
588
|
+
const add = (input: {
|
|
589
|
+
requestId: string;
|
|
590
|
+
provider: string;
|
|
591
|
+
accountLogLabel?: string;
|
|
592
|
+
usageStatus: UsageStatus;
|
|
593
|
+
usage?: PersistedUsageEntry["usage"];
|
|
594
|
+
totalTokens?: number;
|
|
595
|
+
estimate: ReturnType<typeof estimateRequestCost>;
|
|
596
|
+
}): void => {
|
|
597
|
+
const label = accountLabelForAttribution(input.provider, input.accountLogLabel);
|
|
598
|
+
if (!label) return;
|
|
599
|
+
let row = byLabel.get(label);
|
|
600
|
+
if (!row) {
|
|
601
|
+
row = {
|
|
602
|
+
accountLogLabel: label,
|
|
603
|
+
ambiguous: label === LEGACY_AMBIGUOUS_ACCOUNT_LABEL,
|
|
604
|
+
requests: 0,
|
|
605
|
+
attemptCount: 0,
|
|
606
|
+
measuredAttempts: 0,
|
|
607
|
+
reportedAttempts: 0,
|
|
608
|
+
estimatedAttempts: 0,
|
|
609
|
+
unmeteredAttempts: 0,
|
|
610
|
+
inputTokens: 0,
|
|
611
|
+
outputTokens: 0,
|
|
612
|
+
cacheReadInputTokens: 0,
|
|
613
|
+
cacheCreationInputTokens: 0,
|
|
614
|
+
reasoningOutputTokens: 0,
|
|
615
|
+
totalTokens: 0,
|
|
616
|
+
usageCoverageRatio: 0,
|
|
617
|
+
pricedAttempts: 0,
|
|
618
|
+
unpricedAttempts: 0,
|
|
619
|
+
priceCoverageRatio: 0,
|
|
620
|
+
};
|
|
621
|
+
byLabel.set(label, row);
|
|
622
|
+
requestIds.set(label, new Set());
|
|
623
|
+
}
|
|
624
|
+
const ids = requestIds.get(label)!;
|
|
625
|
+
ids.add(input.requestId);
|
|
626
|
+
row.requests = ids.size;
|
|
627
|
+
row.attemptCount += 1;
|
|
628
|
+
const measured = input.usage !== undefined && isMeasuredStatus(input.usageStatus);
|
|
629
|
+
if (!measured) {
|
|
630
|
+
row.unmeteredAttempts += 1;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
row.measuredAttempts += 1;
|
|
635
|
+
if (input.usageStatus === "reported") row.reportedAttempts += 1;
|
|
636
|
+
else if (input.usageStatus === "estimated") row.estimatedAttempts += 1;
|
|
637
|
+
row.inputTokens += input.usage!.inputTokens;
|
|
638
|
+
row.outputTokens += input.usage!.outputTokens;
|
|
639
|
+
const creation = input.usage!.cacheCreationInputTokens;
|
|
640
|
+
const read = typeof input.usage!.cacheReadInputTokens === "number"
|
|
641
|
+
? input.usage!.cacheReadInputTokens
|
|
642
|
+
: typeof input.usage!.cachedInputTokens === "number" && typeof creation === "number"
|
|
643
|
+
? Math.max(0, input.usage!.cachedInputTokens - creation)
|
|
644
|
+
: input.usage!.cachedInputTokens;
|
|
645
|
+
if (typeof read === "number") row.cacheReadInputTokens += read;
|
|
646
|
+
if (typeof creation === "number") row.cacheCreationInputTokens += creation;
|
|
647
|
+
if (typeof input.usage!.reasoningOutputTokens === "number") {
|
|
648
|
+
row.reasoningOutputTokens += input.usage!.reasoningOutputTokens;
|
|
649
|
+
}
|
|
650
|
+
row.totalTokens += usageDisplayTotalTokens(input.usage, input.totalTokens) ?? 0;
|
|
651
|
+
if (input.estimate) {
|
|
652
|
+
row.pricedAttempts += 1;
|
|
653
|
+
row.estimatedCostUsd = (row.estimatedCostUsd ?? 0) + input.estimate.cost.total;
|
|
654
|
+
} else {
|
|
655
|
+
row.unpricedAttempts += 1;
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
for (const entry of entries) {
|
|
660
|
+
const tier = effectiveServiceTier(entry);
|
|
661
|
+
if (entry.attempts?.length) {
|
|
662
|
+
for (const attempt of entry.attempts) {
|
|
663
|
+
add({
|
|
664
|
+
requestId: entry.requestId,
|
|
665
|
+
provider: attempt.provider,
|
|
666
|
+
...(attempt.accountLogLabel ? { accountLogLabel: attempt.accountLogLabel } : {}),
|
|
667
|
+
usageStatus: attempt.usageStatus,
|
|
668
|
+
...(attempt.usage ? { usage: attempt.usage } : {}),
|
|
669
|
+
...(attempt.totalTokens !== undefined ? { totalTokens: attempt.totalTokens } : {}),
|
|
670
|
+
estimate: estimateAttemptCost(attempt, undefined, tier),
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
add({
|
|
676
|
+
requestId: entry.requestId,
|
|
677
|
+
provider: entry.provider,
|
|
678
|
+
...(entry.accountLogLabel ? { accountLogLabel: entry.accountLogLabel } : {}),
|
|
679
|
+
usageStatus: entry.usageStatus,
|
|
680
|
+
...(entry.usage ? { usage: entry.usage } : {}),
|
|
681
|
+
...(entry.totalTokens !== undefined ? { totalTokens: entry.totalTokens } : {}),
|
|
682
|
+
estimate: estimateRequestCost({
|
|
683
|
+
provider: entry.provider,
|
|
684
|
+
model: entry.model,
|
|
685
|
+
usage: entry.usage,
|
|
686
|
+
usageStatus: entry.usageStatus,
|
|
687
|
+
serviceTier: tier,
|
|
688
|
+
}),
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
for (const row of byLabel.values()) {
|
|
693
|
+
row.usageCoverageRatio = row.attemptCount === 0 ? 0 : row.measuredAttempts / row.attemptCount;
|
|
694
|
+
row.priceCoverageRatio = row.measuredAttempts === 0 ? 0 : row.pricedAttempts / row.measuredAttempts;
|
|
695
|
+
}
|
|
696
|
+
return [...byLabel.values()].sort((a, b) => b.totalTokens - a.totalTokens);
|
|
697
|
+
}
|
|
698
|
+
|
|
550
699
|
export function summarizeUsage(
|
|
551
700
|
entries: PersistedUsageEntry[],
|
|
552
701
|
range: UsageRange,
|
|
@@ -581,5 +730,6 @@ export function summarizeUsage(
|
|
|
581
730
|
days: buildDayGrid(range, since, now, filteredEntries),
|
|
582
731
|
models: buildModels(filteredEntries, totals.totalTokens),
|
|
583
732
|
providers: buildProviders(filteredEntries, totals.totalTokens),
|
|
733
|
+
accounts: buildAccounts(filteredEntries),
|
|
584
734
|
};
|
|
585
735
|
}
|
|
Binary file
|