@bitkyc08/opencodex 2.6.19 → 2.6.20
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/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-iWo2gxQ2.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-DDcEW0Cm.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -444,15 +444,22 @@ async function handleResponses(
|
|
|
444
444
|
linkAbortSignal(upstream, turnAc.signal);
|
|
445
445
|
registerTurn(turnAc);
|
|
446
446
|
if (recordTerminalOutcomes) {
|
|
447
|
+
// A real terminal was parsed from the (teed) inspection stream — record it as the outcome
|
|
448
|
+
// even if the client has already disconnected: the turn genuinely reached that terminal, so
|
|
449
|
+
// it must log as completed/failed, not be dropped or downgraded to a cancel (#44). A pure
|
|
450
|
+
// client-cancel (no terminal seen) is finalized separately via consumeForInspection's onCancel.
|
|
447
451
|
const reportNativeTerminal = (status: ResponsesTerminalStatus) => {
|
|
448
|
-
if (options.abortSignal?.aborted) {
|
|
449
|
-
options.onNativePassthroughCancel?.();
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
452
|
terminalRecorder?.(status);
|
|
453
453
|
options.onNativePassthroughTerminal?.(status);
|
|
454
454
|
};
|
|
455
|
-
consumeForInspection(
|
|
455
|
+
consumeForInspection(
|
|
456
|
+
inspectBody,
|
|
457
|
+
reportNativeTerminal,
|
|
458
|
+
turnAc.signal,
|
|
459
|
+
() => unregisterTurn(turnAc),
|
|
460
|
+
logCtx,
|
|
461
|
+
() => options.onNativePassthroughCancel?.(),
|
|
462
|
+
);
|
|
456
463
|
} else {
|
|
457
464
|
consumeForResponseLogMetadata(inspectBody, logCtx, turnAc.signal, () => unregisterTurn(turnAc));
|
|
458
465
|
}
|
|
@@ -1302,12 +1309,13 @@ export function relaySseWithHeartbeat(
|
|
|
1302
1309
|
* Background-consume an SSE stream purely for terminal-outcome inspection (quota tracking).
|
|
1303
1310
|
* Does not produce output; safe to ignore errors (the client-facing stream is separate).
|
|
1304
1311
|
*/
|
|
1305
|
-
function consumeForInspection(
|
|
1312
|
+
export function consumeForInspection(
|
|
1306
1313
|
body: ReadableStream<Uint8Array>,
|
|
1307
1314
|
onTerminal: (status: ResponsesTerminalStatus) => void,
|
|
1308
1315
|
signal?: AbortSignal,
|
|
1309
1316
|
onDone?: () => void,
|
|
1310
1317
|
logCtx?: RequestLogContext,
|
|
1318
|
+
onCancel?: () => void,
|
|
1311
1319
|
): void {
|
|
1312
1320
|
const reader = body.getReader();
|
|
1313
1321
|
const decoder = new TextDecoder();
|
|
@@ -1316,13 +1324,21 @@ function consumeForInspection(
|
|
|
1316
1324
|
let cancelled = false;
|
|
1317
1325
|
if (signal) {
|
|
1318
1326
|
if (signal.aborted) {
|
|
1327
|
+
// Aborted before we could read anything (Codex disconnects the instant it finishes reading).
|
|
1328
|
+
// Finalize as a client-cancel and release the turn — the early return skips pump()'s finally,
|
|
1329
|
+
// so onDone/onCancel must run here or the entry is silently dropped (#44).
|
|
1319
1330
|
cancelled = true;
|
|
1320
1331
|
reader.cancel(signal.reason).catch(() => {});
|
|
1332
|
+
onCancel?.();
|
|
1333
|
+
onDone?.();
|
|
1321
1334
|
return;
|
|
1322
1335
|
}
|
|
1323
1336
|
signal.addEventListener("abort", () => {
|
|
1337
|
+
// Mid-drain disconnect: record a client-cancel entry (idempotent downstream) instead of the
|
|
1338
|
+
// suppressed onTerminal path. onDone still fires via pump()'s finally after the read rejects.
|
|
1324
1339
|
cancelled = true;
|
|
1325
1340
|
reader.cancel(signal.reason).catch(() => {});
|
|
1341
|
+
onCancel?.();
|
|
1326
1342
|
}, { once: true });
|
|
1327
1343
|
}
|
|
1328
1344
|
const pump = async () => {
|
|
@@ -2102,9 +2118,15 @@ export function startServer(port?: number) {
|
|
|
2102
2118
|
const listenPort = port ?? config.port ?? 10100;
|
|
2103
2119
|
setCorsOrigin(listenPort);
|
|
2104
2120
|
|
|
2121
|
+
// Canonicalize an explicit "localhost" bind to IPv4 so it matches the injected base_url (which
|
|
2122
|
+
// resolves localhost→127.0.0.1): on Windows `localhost` resolves ::1-first, but the injected URL
|
|
2123
|
+
// is 127.0.0.1, so binding literal "localhost" would reintroduce the F4 refusal. Wildcards
|
|
2124
|
+
// (0.0.0.0/::) and specific hosts are left untouched so intentional exposure is preserved.
|
|
2125
|
+
const bindHost = /^localhost$/i.test(config.hostname ?? "") ? "127.0.0.1" : (config.hostname ?? "127.0.0.1");
|
|
2126
|
+
|
|
2105
2127
|
const server: Server<WsData> = Bun.serve<WsData>({
|
|
2106
2128
|
port: listenPort,
|
|
2107
|
-
hostname:
|
|
2129
|
+
hostname: bindHost,
|
|
2108
2130
|
idleTimeout: 255,
|
|
2109
2131
|
async fetch(req, requestServer): Promise<Response> {
|
|
2110
2132
|
const url = new URL(req.url);
|
package/src/service.ts
CHANGED
|
@@ -493,12 +493,41 @@ WantedBy=default.target
|
|
|
493
493
|
`;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
+
/** The per-user runtime dir systemd creates (holds the user-bus socket), or null. */
|
|
497
|
+
function userRuntimeDir(): string | null {
|
|
498
|
+
const fromEnv = process.env.XDG_RUNTIME_DIR;
|
|
499
|
+
if (fromEnv && existsSync(fromEnv)) return fromEnv;
|
|
500
|
+
if (typeof process.getuid === "function") {
|
|
501
|
+
const candidate = `/run/user/${process.getuid()}`;
|
|
502
|
+
if (existsSync(candidate)) return candidate;
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* SSH sessions frequently start without `XDG_RUNTIME_DIR`/`DBUS_SESSION_BUS_ADDRESS`, so
|
|
509
|
+
* `systemctl --user` can't find the user bus even when systemd is running. Point `XDG_RUNTIME_DIR`
|
|
510
|
+
* at the per-user runtime dir when it exists so the `--user` probe and install commands reach the
|
|
511
|
+
* bus. No-op when already set or when no runtime dir exists (e.g. genuinely non-systemd hosts).
|
|
512
|
+
*/
|
|
513
|
+
function ensureUserBusEnv(): void {
|
|
514
|
+
if (process.env.XDG_RUNTIME_DIR) return;
|
|
515
|
+
const dir = userRuntimeDir();
|
|
516
|
+
if (dir) process.env.XDG_RUNTIME_DIR = dir;
|
|
517
|
+
}
|
|
518
|
+
|
|
496
519
|
function isSystemd(): boolean {
|
|
497
520
|
try { execSync("systemctl --version", { stdio: "pipe" }); } catch { return false; }
|
|
498
|
-
|
|
521
|
+
ensureUserBusEnv();
|
|
522
|
+
// Prefer the user-bus probe; but an SSH session without a user D-Bus fails it even when systemd
|
|
523
|
+
// is present (F9). Fall back to the per-user runtime dir existing — a strong signal the user
|
|
524
|
+
// systemd instance is available — so a first-time `ocx service install` isn't wrongly refused.
|
|
525
|
+
try { execSync("systemctl --user show-environment", { stdio: "pipe" }); return true; } catch { /* no user bus in this session */ }
|
|
526
|
+
return userRuntimeDir() !== null;
|
|
499
527
|
}
|
|
500
528
|
|
|
501
529
|
function installSystemd(): void {
|
|
530
|
+
ensureUserBusEnv(); // reach the user bus over a bare SSH session (F9)
|
|
502
531
|
const dir = unitDir();
|
|
503
532
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
504
533
|
if (!existsSync(getConfigDir())) mkdirSync(getConfigDir(), { recursive: true });
|
package/src/update-job.ts
CHANGED
|
@@ -78,10 +78,6 @@ function nodeBin(): string {
|
|
|
78
78
|
return process.platform === "win32" ? "node.exe" : "node";
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function ocxBin(): string {
|
|
82
|
-
return process.platform === "win32" ? "ocx.cmd" : "ocx";
|
|
83
|
-
}
|
|
84
|
-
|
|
85
81
|
function packageLauncherPath(): string {
|
|
86
82
|
return join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "ocx.mjs");
|
|
87
83
|
}
|
|
@@ -163,8 +159,11 @@ export function restartCommand(
|
|
|
163
159
|
const args = serviceInstalled ? [launcher, "service", "install"] : [launcher, "start"];
|
|
164
160
|
return { mode, bin, args, display: formatCommand(bin, args) };
|
|
165
161
|
}
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
// bun/source installs: restart via the current runtime executable + package launcher (both real
|
|
163
|
+
// .exe files), NOT the `ocx.cmd` shim. Spawning a `.cmd` shell-less throws EINVAL on Windows
|
|
164
|
+
// Node/Bun ≥18.20/20.12 (CVE-2024-27980 hardening) — the same class the npm path (nodeBin) avoids.
|
|
165
|
+
const bin = process.execPath;
|
|
166
|
+
const args = serviceInstalled ? [launcher, "service", "install"] : [launcher, "start"];
|
|
168
167
|
return { mode, bin, args, display: formatCommand(bin, args) };
|
|
169
168
|
}
|
|
170
169
|
|