@yansigit/opencodex 2.36.1 → 2.36.2-dev.20260829.53
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 +9 -4
- package/gui/dist/assets/{ApiKeys-CsmNyf4I.js → ApiKeys-BhwqQvO8.js} +1 -1
- package/gui/dist/assets/{Claude-B4sHsGTD.js → Claude-DYNY8SHC.js} +1 -1
- package/gui/dist/assets/{CodexSet-BuAQ4YQn.js → CodexSet-tSN1aWmM.js} +1 -1
- package/gui/dist/assets/{FileIntegrationPage-BC65sEaS.js → FileIntegrationPage-B68tn2JT.js} +1 -1
- package/gui/dist/assets/{Grok-DNJWlISf.js → Grok-Dr6FN8SD.js} +1 -1
- package/gui/dist/assets/{Integrations-vGJshFZK.js → Integrations-jvqYofMm.js} +2 -2
- package/gui/dist/assets/{IntegrationsOverview-DYMkDFHE.js → IntegrationsOverview-Bjc7JSgw.js} +1 -1
- package/gui/dist/assets/{Logs-Coc7_RPP.js → Logs-C23CdgmP.js} +1 -1
- package/gui/dist/assets/{Models-CKVT8Meb.js → Models-Ci4wK3HO.js} +1 -1
- package/gui/dist/assets/{NumberStepper-Dug4wR8n.js → NumberStepper-nggECFok.js} +1 -1
- package/gui/dist/assets/Providers-DasIL4ng.js +1 -0
- package/gui/dist/assets/{RestoreDialog-BpuvVU2w.js → RestoreDialog-CW7LbS-w.js} +1 -1
- package/gui/dist/assets/{Startup-DAUQqNEN.js → Startup-Cx3dZre0.js} +1 -1
- package/gui/dist/assets/{Storage-DfP95SlJ.js → Storage-D0iUYQED.js} +1 -1
- package/gui/dist/assets/{Subagents-CMNsZLfk.js → Subagents-phLFffyl.js} +1 -1
- package/gui/dist/assets/{Usage-ESFRfBMO.js → Usage-NvYxG04o.js} +1 -1
- package/gui/dist/assets/{codex-stale-banner-W4FhikN7.js → codex-stale-banner-B5VQPO3t.js} +1 -1
- package/gui/dist/assets/{data-surface-B8PX8rjm.js → data-surface-B0APtUxm.js} +1 -1
- package/gui/dist/assets/{data-surface-CETFXqiA.js → data-surface-OiQUZok8.js} +1 -1
- package/gui/dist/assets/index-C8flH881.css +1 -0
- package/gui/dist/assets/{index-DHQHBRhb.js → index-CZ1jyQCF.js} +3 -3
- package/gui/dist/assets/{model-display-OBTJhQwT.js → model-display-Dlrzica2.js} +1 -1
- package/gui/dist/assets/{provider-payload-CJRJUn7w.js → provider-payload-Ca3Ff8oP.js} +1 -1
- package/gui/dist/assets/{section-tabs-B8sLMiz6.js → section-tabs-Bpm5lFcZ.js} +1 -1
- package/gui/dist/assets/shared-BHpPx-nZ.js +69 -0
- package/gui/dist/index.html +3 -3
- package/package.json +2 -1
- package/src/adapters/base.ts +2 -0
- package/src/adapters/command-code.ts +2 -1
- package/src/adapters/google-http.ts +34 -14
- package/src/adapters/google.ts +4 -1
- package/src/cli/dispatch.ts +2 -2
- package/src/cli/help.ts +2 -2
- package/src/cli/index.ts +9 -1
- package/src/cli/init.ts +52 -3
- package/src/cli/registry.ts +2 -2
- package/src/codex/account-lifecycle.ts +4 -8
- package/src/codex/inject.ts +14 -9
- package/src/config/process-state.ts +15 -0
- package/src/config.ts +17 -0
- package/src/generated/compatibility-version.json +67 -47
- package/src/lib/bun-stream-caps.ts +2 -1
- package/src/lib/pinned-http.ts +7 -1
- package/src/lib/process-control.ts +3 -2
- package/src/lib/provider-outbound.ts +77 -27
- package/src/lib/provider-runtime-fetch.ts +23 -0
- package/src/lib/provider-tls-profile.ts +97 -11
- package/src/lib/server-tls.ts +47 -0
- package/src/lib/test-provider-fetch.ts +6 -0
- package/src/lib/test-server-start.ts +5 -0
- package/src/lib/upstream-retry.ts +39 -25
- package/src/oauth/anthropic.ts +4 -3
- package/src/oauth/chatgpt.ts +7 -3
- package/src/oauth/command-code.ts +3 -2
- package/src/oauth/cursor.ts +3 -2
- package/src/oauth/github-copilot.ts +7 -5
- package/src/oauth/google-antigravity.ts +5 -4
- package/src/oauth/key-providers.ts +11 -13
- package/src/oauth/kimi.ts +7 -6
- package/src/oauth/kiro.ts +3 -2
- package/src/oauth/nous.ts +5 -5
- package/src/oauth/transport.ts +77 -0
- package/src/oauth/xai.ts +5 -4
- package/src/providers/replit/probe.ts +8 -3
- package/src/providers/xai-transport.ts +16 -2
- package/src/server/auth-cors.ts +15 -1
- package/src/server/chat-native.ts +9 -3
- package/src/server/index.ts +63 -23
- package/src/server/management/config-routes.ts +117 -15
- package/src/server/management/context.ts +4 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/responses/compact.ts +5 -2
- package/src/server/responses/core.ts +16 -19
- package/src/server/responses/fetch-helpers.ts +15 -7
- package/src/server/ws-bridge.ts +33 -4
- package/src/service.ts +10 -1
- package/src/types/config.ts +9 -0
- package/src/types/provider.ts +2 -0
- package/src/types.ts +1 -0
- package/gui/dist/assets/Providers-DyaCHRWL.js +0 -1
- package/gui/dist/assets/index-pLho_LBF.css +0 -1
- package/gui/dist/assets/shared-DI18-uF7.js +0 -69
|
@@ -11,6 +11,8 @@ import type { WsData } from "../ws-bridge";
|
|
|
11
11
|
import { waitForProviderRequestSlot } from "../../providers/request-pacing";
|
|
12
12
|
import { withUpstreamHttpVersion } from "../../lib/upstream-http-version";
|
|
13
13
|
import { providerTlsFetch } from "../../lib/provider-tls-profile";
|
|
14
|
+
import { testProviderFetch } from "../../lib/test-provider-fetch";
|
|
15
|
+
import { runtimeProviderFetch } from "../../lib/provider-runtime-fetch";
|
|
14
16
|
|
|
15
17
|
export { withUpstreamHttpVersion };
|
|
16
18
|
|
|
@@ -58,6 +60,8 @@ export interface ProviderFetchOptions {
|
|
|
58
60
|
modelId?: string;
|
|
59
61
|
/** One pacing slot was acquired immediately before this fetch wrapper was created. */
|
|
60
62
|
pacingSlotAcquired?: boolean;
|
|
63
|
+
/** Explicit test/integration executor; never read from serialized provider config. */
|
|
64
|
+
fetch?: typeof globalThis.fetch;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export function providerFetch(
|
|
@@ -65,7 +69,7 @@ export function providerFetch(
|
|
|
65
69
|
runtime: BunRuntimeGateInput = currentBunRuntimeIdentity(),
|
|
66
70
|
options: ProviderFetchOptions = {},
|
|
67
71
|
): ProviderFetch {
|
|
68
|
-
const base = (provider
|
|
72
|
+
const base = options.fetch ?? testProviderFetch(provider) ?? runtimeProviderFetch(provider, options.providerName) ?? globalThis.fetch;
|
|
69
73
|
const preconnect = (...args: Parameters<typeof globalThis.fetch.preconnect>): void => {
|
|
70
74
|
base.preconnect?.(...args);
|
|
71
75
|
};
|
|
@@ -126,7 +130,7 @@ export async function fetchWithHeaderTimeout(
|
|
|
126
130
|
timeoutMs: number,
|
|
127
131
|
preferIdentityEncoding = false,
|
|
128
132
|
executor: typeof globalThis.fetch = globalThis.fetch,
|
|
129
|
-
|
|
133
|
+
_manualRedirect = true,
|
|
130
134
|
): Promise<Response> {
|
|
131
135
|
const pacing = executor as ProviderFetch;
|
|
132
136
|
await pacing.waitForPacing?.(abortSignal);
|
|
@@ -142,16 +146,20 @@ export async function fetchWithHeaderTimeout(
|
|
|
142
146
|
headers.set("accept-encoding", "identity");
|
|
143
147
|
}
|
|
144
148
|
try {
|
|
145
|
-
|
|
149
|
+
const response = await fetchExecutor(url, {
|
|
146
150
|
...init,
|
|
147
151
|
headers,
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
...(manualRedirect ? { redirect: "manual" as const } : {}),
|
|
152
|
+
// Upstream URLs are configuration, not navigation. Refuse every redirect
|
|
153
|
+
// so POST bodies and provider headers are never replayed to another hop.
|
|
154
|
+
redirect: "manual",
|
|
152
155
|
signal: AbortSignal.any([abortSignal, timeout.signal]),
|
|
153
156
|
timeout: 0,
|
|
154
157
|
});
|
|
158
|
+
if (response.status >= 300 && response.status < 400) {
|
|
159
|
+
try { await response.body?.cancel(); } catch { /* ignore cancellation failures */ }
|
|
160
|
+
throw new Error(`upstream returned ${response.status} redirect; configure the final upstream URL directly`);
|
|
161
|
+
}
|
|
162
|
+
return response;
|
|
155
163
|
} finally {
|
|
156
164
|
clearTimeout(timer);
|
|
157
165
|
}
|
package/src/server/ws-bridge.ts
CHANGED
|
@@ -54,6 +54,8 @@ export interface WsData {
|
|
|
54
54
|
/** Turn/account ownership retained for the complete sideband socket lifetime. */
|
|
55
55
|
liveTurnAdmissionLease?: AdmissionLease;
|
|
56
56
|
admissionLease?: AdmissionReservation<ServerWebSocket<WsData>>;
|
|
57
|
+
wsDrain?: () => void;
|
|
58
|
+
wsDrainWaiter?: { resolve: () => void };
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
/**
|
|
@@ -155,6 +157,18 @@ export function sendTextFrame(ws: ServerWebSocket<WsData>, payload: string): voi
|
|
|
155
157
|
// Bun returns -1 when queued with backpressure. That is accepted; a later 0 is the hard failure.
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
async function sendTextFrameWithDrain(ws: ServerWebSocket<WsData>, payload: string): Promise<void> {
|
|
161
|
+
if (ws.readyState !== OPEN) throw new WsSendDroppedError();
|
|
162
|
+
const result = ws.send(payload);
|
|
163
|
+
if (result === 0) throw new WsSendDroppedError();
|
|
164
|
+
if (result === -1) {
|
|
165
|
+
await new Promise<void>(resolve => {
|
|
166
|
+
ws.data.wsDrainWaiter = { resolve };
|
|
167
|
+
});
|
|
168
|
+
if (ws.readyState !== OPEN) throw new WsSendDroppedError();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
export function sendJsonFrame(ws: ServerWebSocket<WsData>, payload: Record<string, unknown>): void {
|
|
159
173
|
sendTextFrame(ws, JSON.stringify(payload));
|
|
160
174
|
}
|
|
@@ -222,6 +236,7 @@ export async function pumpResponsesSseToWebSocket(
|
|
|
222
236
|
sseStream: ReadableStream<Uint8Array>,
|
|
223
237
|
options: {
|
|
224
238
|
isCurrent?: () => boolean;
|
|
239
|
+
abortSignal?: AbortSignal;
|
|
225
240
|
onTerminal?: ResponsesTerminalReporter;
|
|
226
241
|
onSsePayload?: ResponsesPayloadObserver;
|
|
227
242
|
} = {},
|
|
@@ -237,15 +252,22 @@ export async function pumpResponsesSseToWebSocket(
|
|
|
237
252
|
};
|
|
238
253
|
const cancel = () => {
|
|
239
254
|
clientCancelled = true;
|
|
255
|
+
ws.data.wsDrain?.();
|
|
240
256
|
void reader.cancel().catch(() => {});
|
|
241
257
|
};
|
|
258
|
+
const drain = () => {
|
|
259
|
+
ws.data.wsDrainWaiter?.resolve();
|
|
260
|
+
ws.data.wsDrainWaiter = undefined;
|
|
261
|
+
};
|
|
262
|
+
ws.data.wsDrain = drain;
|
|
242
263
|
ws.data.cancel = cancel;
|
|
264
|
+
options.abortSignal?.addEventListener("abort", cancel, { once: true });
|
|
243
265
|
|
|
244
266
|
const decoder = new TextDecoder();
|
|
245
267
|
const framer = new BoundedSseFrameBuffer();
|
|
246
268
|
let terminalSeen = false;
|
|
247
269
|
|
|
248
|
-
const handlePayload = (payload: string): boolean => {
|
|
270
|
+
const handlePayload = async (payload: string): Promise<boolean> => {
|
|
249
271
|
if (!isCurrent()) return true;
|
|
250
272
|
if (payload === "[DONE]") return false;
|
|
251
273
|
try {
|
|
@@ -262,7 +284,7 @@ export async function pumpResponsesSseToWebSocket(
|
|
|
262
284
|
return true;
|
|
263
285
|
}
|
|
264
286
|
if (terminalSeen) return true;
|
|
265
|
-
|
|
287
|
+
await sendTextFrameWithDrain(ws, payload);
|
|
266
288
|
const terminalStatus = terminalStatusFromType(type);
|
|
267
289
|
if (terminalStatus) {
|
|
268
290
|
reportTerminal(terminalStatus);
|
|
@@ -279,13 +301,13 @@ export async function pumpResponsesSseToWebSocket(
|
|
|
279
301
|
if (done) break;
|
|
280
302
|
for (const frame of framer.feed(value)) {
|
|
281
303
|
const payload = parseSseBlock(decoder.decode(frame.block));
|
|
282
|
-
if (payload && handlePayload(payload)) break;
|
|
304
|
+
if (payload && await handlePayload(payload)) break;
|
|
283
305
|
}
|
|
284
306
|
}
|
|
285
307
|
const tail = framer.finish();
|
|
286
308
|
if (!terminalSeen && tail.byteLength > 0) {
|
|
287
309
|
const payload = parseSseBlock(decoder.decode(tail));
|
|
288
|
-
if (payload) handlePayload(payload);
|
|
310
|
+
if (payload) await handlePayload(payload);
|
|
289
311
|
}
|
|
290
312
|
if (!terminalSeen && isCurrent() && !clientCancelled) {
|
|
291
313
|
reportTerminal("incomplete");
|
|
@@ -314,6 +336,10 @@ export async function pumpResponsesSseToWebSocket(
|
|
|
314
336
|
// release the reader, even when terminal/send paths already cancelled it.
|
|
315
337
|
void reader.cancel().catch(() => {});
|
|
316
338
|
if (ws.data.cancel === cancel) ws.data.cancel = undefined;
|
|
339
|
+
if (ws.data.wsDrain === drain) ws.data.wsDrain = undefined;
|
|
340
|
+
options.abortSignal?.removeEventListener("abort", cancel);
|
|
341
|
+
ws.data.wsDrainWaiter?.resolve();
|
|
342
|
+
ws.data.wsDrainWaiter = undefined;
|
|
317
343
|
}
|
|
318
344
|
}
|
|
319
345
|
|
|
@@ -363,6 +389,7 @@ export async function sendResponseToWebSocket(
|
|
|
363
389
|
options: {
|
|
364
390
|
onTerminal?: ResponsesTerminalReporter;
|
|
365
391
|
onSsePayload?: ResponsesPayloadObserver;
|
|
392
|
+
abortSignal?: AbortSignal;
|
|
366
393
|
} = {},
|
|
367
394
|
): Promise<void> {
|
|
368
395
|
if (!isCurrent()) {
|
|
@@ -391,6 +418,7 @@ export async function sendResponseToWebSocket(
|
|
|
391
418
|
if (contentType.includes("text/event-stream")) {
|
|
392
419
|
await pumpResponsesSseToWebSocket(ws, response.body, {
|
|
393
420
|
isCurrent,
|
|
421
|
+
abortSignal: options.abortSignal,
|
|
394
422
|
onTerminal: options.onTerminal,
|
|
395
423
|
onSsePayload: options.onSsePayload,
|
|
396
424
|
});
|
|
@@ -413,6 +441,7 @@ export async function sendResponseToWebSocket(
|
|
|
413
441
|
if (looksLikeSse(prefix)) {
|
|
414
442
|
await pumpResponsesSseToWebSocket(ws, stream, {
|
|
415
443
|
isCurrent,
|
|
444
|
+
abortSignal: options.abortSignal,
|
|
416
445
|
onTerminal: options.onTerminal,
|
|
417
446
|
onSsePayload: options.onSsePayload,
|
|
418
447
|
});
|
package/src/service.ts
CHANGED
|
@@ -3400,6 +3400,9 @@ export type ServiceCommandPlan =
|
|
|
3400
3400
|
| { ok: true; parsed: ParsedServiceArgs; command: string }
|
|
3401
3401
|
| { ok: false; message: string };
|
|
3402
3402
|
|
|
3403
|
+
const SERVICE_SUBCOMMANDS = new Set(["install", "repair", "start", "stop", "status", "uninstall", "remove"]);
|
|
3404
|
+
const SERVICE_USAGE = "Usage: ocx service [install|repair|restart|start|stop|status|uninstall|remove] [--native|--scheduler]";
|
|
3405
|
+
|
|
3403
3406
|
export function planServiceCommand(
|
|
3404
3407
|
args: string[],
|
|
3405
3408
|
options: { platform?: NodeJS.Platform; probeInstallation?: () => ServiceInstallationProbe } = {},
|
|
@@ -3408,6 +3411,12 @@ export function planServiceCommand(
|
|
|
3408
3411
|
if (parsed.invalid.length > 0) {
|
|
3409
3412
|
return { ok: false, message: `Unknown service option: ${parsed.invalid.join(" ")}` };
|
|
3410
3413
|
}
|
|
3414
|
+
// Validate the requested action before touching platform probes. In a Linux
|
|
3415
|
+
// test container (and on a host without systemd), invalid CLI input must
|
|
3416
|
+
// report usage rather than an unrelated service-manager capability error.
|
|
3417
|
+
if (!SERVICE_SUBCOMMANDS.has(parsed.sub)) {
|
|
3418
|
+
return { ok: false, message: SERVICE_USAGE };
|
|
3419
|
+
}
|
|
3411
3420
|
if (parsed.backend && parsed.sub !== "install") {
|
|
3412
3421
|
return { ok: false, message: "--native/--scheduler apply to `ocx service install` only; other subcommands use the installed backend." };
|
|
3413
3422
|
}
|
|
@@ -3603,7 +3612,7 @@ export async function serviceCommand(...args: (string | undefined)[]): Promise<v
|
|
|
3603
3612
|
console.log("✅ service uninstalled.");
|
|
3604
3613
|
break;
|
|
3605
3614
|
default:
|
|
3606
|
-
console.error(
|
|
3615
|
+
console.error(SERVICE_USAGE);
|
|
3607
3616
|
console.error(" With no subcommand, installs when absent or repairs/restarts an existing service.");
|
|
3608
3617
|
console.error(" repair: refresh assets and restart an already-installed service (no admin re-prompt).");
|
|
3609
3618
|
console.error(" restart: alias of repair.");
|
package/src/types/config.ts
CHANGED
|
@@ -260,6 +260,13 @@ export interface OcxConfigRebaseProvenance {
|
|
|
260
260
|
deletedTopLevelKeys: string[];
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
export interface OcxServerTlsConfig {
|
|
264
|
+
certFile: string;
|
|
265
|
+
keyFile: string;
|
|
266
|
+
/** Exact externally reachable HTTPS origin; paths, query strings, fragments, and credentials are rejected. */
|
|
267
|
+
publicOrigin: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
263
270
|
export interface OcxConfig {
|
|
264
271
|
port: number;
|
|
265
272
|
autonomousRemediation?: {
|
|
@@ -512,6 +519,8 @@ export interface OcxConfig {
|
|
|
512
519
|
contextCapValue?: number;
|
|
513
520
|
/** Bind hostname. Default "127.0.0.1" (loopback only). Set "0.0.0.0" to expose on all interfaces. */
|
|
514
521
|
hostname?: string;
|
|
522
|
+
/** Native Bun TLS for the public listener. Required for non-loopback binds. */
|
|
523
|
+
tls?: OcxServerTlsConfig;
|
|
515
524
|
/**
|
|
516
525
|
* Optional second listener bound to 127.0.0.1 that admits data-plane requests without a
|
|
517
526
|
* credential (issue #1102).
|
package/src/types/provider.ts
CHANGED
|
@@ -568,6 +568,8 @@ export interface OcxProviderConfig {
|
|
|
568
568
|
* before any response bytes are relayed, so the replay is lossless.
|
|
569
569
|
*/
|
|
570
570
|
retryOn429?: RateLimitRetryPolicy;
|
|
571
|
+
/** Opt in to replaying transient upstream 5xx responses (at most three total sends). */
|
|
572
|
+
replayTransientFailures?: boolean;
|
|
571
573
|
/**
|
|
572
574
|
* Model ids whose OpenAI-compatible chat endpoint accepts `reasoning_split: true` and returns
|
|
573
575
|
* thinking separately in `reasoning_content` / `reasoning_details` instead of visible content.
|