mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 04:12:17 +03:00
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
This commit is contained in:
committed by
GitHub
parent
5ff85c6db6
commit
8939ccbae3
1
changelog.d/fixes/12491-codex-wreq-standalone-runtime.md
Normal file
1
changelog.d/fixes/12491-codex-wreq-standalone-runtime.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** Codex WebSocket transport (including the app-server) no longer fails to load in the Next.js standalone Docker runtime — the wreq-js loader now resolves its module name dynamically instead of a literal Turbopack could rewrite to an unreachable build-time symlink (#12491) — thanks @marshalfevzi
|
||||
@@ -43,6 +43,7 @@ import { errorResponse } from "../utils/error.ts";
|
||||
import { normalizeCodexResponsesInput } from "../utils/responsesInputNormalization.ts";
|
||||
import * as prl from "../utils/providerRequestLogging.ts";
|
||||
import { createRequire } from "module";
|
||||
import { loadDynamicModule } from "./codex/wreqLoader.ts";
|
||||
// Quota parsing/scheduling extracted to a pure leaf; re-exported for the
|
||||
// Codex account module and tests.
|
||||
export {
|
||||
@@ -90,7 +91,7 @@ function getCodexWebSocketTransport(): WebsocketFn | null {
|
||||
if (_wreqChecked) return _websocketFn;
|
||||
_wreqChecked = true;
|
||||
try {
|
||||
const mod = _wreqRequire("wreq-js") as { websocket?: WebsocketFn };
|
||||
const mod = loadDynamicModule(_wreqRequire, "wreq-js") as { websocket?: WebsocketFn };
|
||||
_websocketFn = typeof mod.websocket === "function" ? mod.websocket : null;
|
||||
} catch {
|
||||
console.warn("[codex] wreq-js import failed, websocket disabled");
|
||||
|
||||
9
open-sse/executors/codex/wreqLoader.ts
Normal file
9
open-sse/executors/codex/wreqLoader.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// #12491 — keep the module-name argument dynamic (never a literal string)
|
||||
// when calling a createRequire()-returned function. Turbopack statically
|
||||
// detects a literal specifier and rewrites the call to a hashed require()
|
||||
// target that resolves only via a `.next`-relative symlink generated at
|
||||
// build time, which is absent from the standalone Docker runtime. Mirrors
|
||||
// open-sse/utils/tlsClient.ts's loadRuntimeModule().
|
||||
export function loadDynamicModule(requireFn: NodeRequire, moduleName: string): unknown {
|
||||
return Reflect.apply(requireFn, undefined, [moduleName]);
|
||||
}
|
||||
@@ -5,7 +5,13 @@ import { getTlsClientTimeoutConfig } from "@/shared/utils/runtimeTimeouts";
|
||||
// import the first-byte watchdog alongside TlsClient without adding a line.
|
||||
export { guardTlsFirstByte } from "./tlsFirstByteWatchdog.ts";
|
||||
|
||||
const runtimeRequire = nodeModule.createRequire(import.meta.url);
|
||||
// #12491 — anchor on process.argv[1]||cwd() rather than import.meta.url: the
|
||||
// standalone Docker runtime re-lays-out files at a different relative depth
|
||||
// than the build, so an import.meta.url-relative resolution can miss even
|
||||
// though this loader already keeps the specifier itself dynamic (see
|
||||
// loadRuntimeModule() below). Matches src/lib/machineToken.ts and
|
||||
// src/lib/db/adapters/runtimeRequire.ts's established anchor pattern.
|
||||
const runtimeRequire = nodeModule.createRequire(process.argv[1] || process.cwd());
|
||||
|
||||
function loadRuntimeModule(moduleName: string): unknown {
|
||||
// Keep the specifier dynamic. Turbopack rewrites a literal createRequire call
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
||||
|
||||
/**
|
||||
* Regression guard for #12491: the Codex app-server WebSocket transport
|
||||
* ("Codex app-server websocket transport unavailable") never comes up inside
|
||||
* the Next.js standalone Docker runtime because open-sse/executors/codex.ts
|
||||
* resolves wreq-js with a LITERAL specifier:
|
||||
*
|
||||
* const _wreqRequire = createRequire(import.meta.url);
|
||||
* const mod = _wreqRequire("wreq-js");
|
||||
*
|
||||
* Turbopack statically detects the literal string argument and rewrites the
|
||||
* call into `require("wreq-js-<buildhash>")`, satisfied only by a symlink
|
||||
* Turbopack drops at `.next/node_modules/wreq-js-<buildhash> -> node_modules/wreq-js`
|
||||
* during the build. That symlink is a `.next`-relative build artifact; it is not
|
||||
* reachable through the same relative path once files are re-laid-out for a
|
||||
* standalone Docker image, so `require("wreq-js-<buildhash>")` throws
|
||||
* MODULE_NOT_FOUND at runtime — even though `node -e "require('wreq-js')"`
|
||||
* (the literal package name) succeeds fine in the very same container.
|
||||
*
|
||||
* Verified interactively against a real `next build --turbopack` (Next 16.3.3):
|
||||
* compiling `const _wreqRequire = createRequire(import.meta.url); _wreqRequire("wreq-js")`
|
||||
* emits `64301,(e,t,r)=>{t.exports=e.x("wreq-js-3b69dd5e46bd26d3",()=>require("wreq-js-3b69dd5e46bd26d3"))}`
|
||||
* in the compiled chunk, and the build directory gained
|
||||
* `.next/node_modules/wreq-js-3b69dd5e46bd26d3 -> ../../node_modules/wreq-js`.
|
||||
* `open-sse/utils/tlsClient.ts` (see its `loadRuntimeModule()`) was already
|
||||
* hardened against exactly this by keeping the specifier a runtime variable
|
||||
* (`Reflect.apply(runtimeRequire, undefined, [moduleName])`), which Turbopack
|
||||
* cannot statically rewrite — codex.ts's own wreq-js loader (the one that feeds
|
||||
* `getCodexAppServerWebsocketTransport()` / `CodexAppServerClient.connect()`)
|
||||
* never received the same treatment.
|
||||
*/
|
||||
|
||||
const CODEX_TS_PATH = join(ROOT, "open-sse", "executors", "codex.ts");
|
||||
|
||||
test("codex.ts must not pass a literal specifier to the wreq-js createRequire() loader", () => {
|
||||
const source = readFileSync(CODEX_TS_PATH, "utf8");
|
||||
|
||||
assert.match(
|
||||
source,
|
||||
/const _wreqRequire = createRequire\(import\.meta\.url\)/,
|
||||
"expected open-sse/executors/codex.ts to still define _wreqRequire via createRequire(import.meta.url) " +
|
||||
"— update this test's assumptions if the loader was restructured"
|
||||
);
|
||||
|
||||
const literalCallPattern = /_wreqRequire\(\s*["'`]wreq-js["'`]\s*\)/;
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
literalCallPattern,
|
||||
'open-sse/executors/codex.ts calls _wreqRequire("wreq-js") with a LITERAL specifier. ' +
|
||||
'Turbopack statically rewrites this into `require("wreq-js-<buildhash>")`, which only ' +
|
||||
"resolves via a `.next/node_modules/wreq-js-<buildhash>` symlink generated at build time — " +
|
||||
"not reachable in the standalone Docker runtime, so the Codex app-server WebSocket transport " +
|
||||
"(and the plain Codex WS transport sharing this loader) is permanently disabled in production " +
|
||||
"(#12491). Route the specifier through a variable the bundler cannot statically analyze, " +
|
||||
"e.g. Reflect.apply(_wreqRequire, undefined, [moduleName]) — the exact pattern already used by " +
|
||||
"open-sse/utils/tlsClient.ts's loadRuntimeModule()."
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user