@jaralabs/codex-bridge 0.4.3 → 0.4.5
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/README.md +10 -0
- package/lib/resilience.mjs +31 -0
- package/lib/server.mjs +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @jaralabs/codex-bridge
|
|
2
2
|
|
|
3
|
+
> **Disclaimer.** This project is not affiliated with, endorsed by, or
|
|
4
|
+
> sponsored by OpenAI. It is an independent tool that reuses the OAuth
|
|
5
|
+
> session written by the official Codex CLI to `~/.codex/auth.json` and
|
|
6
|
+
> speaks the same upstream endpoint (`chatgpt.com/backend-api/codex/responses`)
|
|
7
|
+
> that the CLI itself uses. The endpoint is not part of any published OpenAI
|
|
8
|
+
> API, so the bridge can stop working the day OpenAI rotates it. Use it as
|
|
9
|
+
> a developer convenience for your own account, not as a service you sell
|
|
10
|
+
> or redistribute. The authors and contributors are not responsible for
|
|
11
|
+
> account actions taken by OpenAI as a result of how you use this tool.
|
|
12
|
+
|
|
3
13
|
> Use your ChatGPT Business / Team / Plus subscription from OpenCode instead
|
|
4
14
|
> of paying for the OpenAI API.
|
|
5
15
|
|
package/lib/resilience.mjs
CHANGED
|
@@ -31,6 +31,15 @@ const state = {
|
|
|
31
31
|
lastError: null,
|
|
32
32
|
lastSuccessAt: 0,
|
|
33
33
|
lastFailureAt: 0,
|
|
34
|
+
// token usage accumulated since process start. The bridge reads
|
|
35
|
+
// `usage` (input_tokens / output_tokens / total_tokens) out of every
|
|
36
|
+
// successful Responses API response and sums it here. This is the
|
|
37
|
+
// closest thing the bridge has to a "credits used" counter: the
|
|
38
|
+
// ChatGPT subscription does not expose a per-account quota, so
|
|
39
|
+
// the only observable usage is what flows through the proxy.
|
|
40
|
+
promptTokens: 0,
|
|
41
|
+
completionTokens: 0,
|
|
42
|
+
totalTokens: 0,
|
|
34
43
|
// circuit breaker
|
|
35
44
|
failureTimes: [], // timestamps of recent failures
|
|
36
45
|
circuitOpenedAt: 0, // 0 = closed; >0 = open since this timestamp
|
|
@@ -116,11 +125,33 @@ export function metrics() {
|
|
|
116
125
|
lastError: state.lastError,
|
|
117
126
|
lastSuccessAt: state.lastSuccessAt,
|
|
118
127
|
lastFailureAt: state.lastFailureAt,
|
|
128
|
+
tokens: {
|
|
129
|
+
prompt: state.promptTokens,
|
|
130
|
+
completion: state.completionTokens,
|
|
131
|
+
total: state.totalTokens,
|
|
132
|
+
},
|
|
119
133
|
circuit: circuitStatus(),
|
|
120
134
|
circuitFailuresInWindow: state.failureTimes.length,
|
|
121
135
|
};
|
|
122
136
|
}
|
|
123
137
|
|
|
138
|
+
// Sum the token counts from a successful Responses API response
|
|
139
|
+
// into the in-memory counters. The wire shape is
|
|
140
|
+
// `{ input_tokens, output_tokens, total_tokens }`; missing fields
|
|
141
|
+
// default to 0. Idempotent and safe to call multiple times for the
|
|
142
|
+
// same response (callers should only call once per success).
|
|
143
|
+
export function recordUsage(usage) {
|
|
144
|
+
if (!usage || typeof usage !== "object") return;
|
|
145
|
+
const prompt = Number.isFinite(usage.input_tokens) ? usage.input_tokens : 0;
|
|
146
|
+
const completion = Number.isFinite(usage.output_tokens) ? usage.output_tokens : 0;
|
|
147
|
+
const total = Number.isFinite(usage.total_tokens)
|
|
148
|
+
? usage.total_tokens
|
|
149
|
+
: prompt + completion;
|
|
150
|
+
state.promptTokens += prompt;
|
|
151
|
+
state.completionTokens += completion;
|
|
152
|
+
state.totalTokens += total;
|
|
153
|
+
}
|
|
154
|
+
|
|
124
155
|
// Wrap an async function with retry + circuit breaker semantics.
|
|
125
156
|
// fn() must throw an Error with .status attached when the response
|
|
126
157
|
// is a retryable HTTP error. onRetry(attempt, delayMs, err) is
|
package/lib/server.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
withResilience,
|
|
32
32
|
metrics as resilienceMetrics,
|
|
33
|
+
recordUsage,
|
|
33
34
|
} from "./resilience.mjs";
|
|
34
35
|
import { probeCapabilities } from "./capabilities.mjs";
|
|
35
36
|
|
|
@@ -440,6 +441,13 @@ async function handleChatCompletions(payload, req, res) {
|
|
|
440
441
|
const id = genId("chatcmpl");
|
|
441
442
|
const created = now();
|
|
442
443
|
const usage = toUsage(result.usage);
|
|
444
|
+
// Sum the token usage of this successful turn into the in-memory
|
|
445
|
+
// counters that /v1/health exposes. We record once per turn (here,
|
|
446
|
+
// after the upstream Responses API returned a `response.completed`
|
|
447
|
+
// event with a `usage` block), so streaming and JSON responses are
|
|
448
|
+
// counted identically. Multi-turn tool-call loops in the same
|
|
449
|
+
// request accumulate multiple `recordUsage` calls.
|
|
450
|
+
if (result.usage) recordUsage(result.usage);
|
|
443
451
|
const hasToolCalls = result.toolCalls && result.toolCalls.length > 0;
|
|
444
452
|
const finishReason = hasToolCalls ? "tool_calls" : "stop";
|
|
445
453
|
const message = { role: "assistant", content: result.text || "" };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaralabs/codex-bridge",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenAI-compatible HTTP bridge that reuses a local ChatGPT Business / Team / Plus session via codex, so OpenCode can list and call those models without paying for the OpenAI API.",
|
|
6
6
|
"type": "module",
|