@bitkyc08/opencodex 2.7.25 → 2.7.26
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-Dq3eZ1cU.css → index-BnrJO9Wz.css} +1 -1
- package/gui/dist/assets/{index-QyUBi3W_.js → index-BvQ5spEX.js} +3 -3
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/cli/index.ts +7 -2
- package/src/config.ts +60 -2
- package/src/lib/errors.ts +51 -12
- package/src/lib/process-control.ts +1 -1
- package/src/lib/redact.ts +8 -0
- package/src/lib/windows-secret-acl.ts +217 -74
- package/src/oauth/github-copilot.ts +427 -0
- package/src/oauth/index.ts +41 -4
- package/src/oauth/kimi.ts +52 -1
- package/src/oauth/store.ts +20 -3
- package/src/oauth/types.ts +5 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/quota.ts +111 -59
- package/src/providers/registry.ts +56 -2
- package/src/providers/xai-transport.ts +5 -0
- package/src/server/ports.ts +44 -2
- package/src/server/proxy-liveness.ts +26 -4
- package/src/server/request-log.ts +20 -6
- package/src/server/responses.ts +14 -5
- package/src/update/job.ts +59 -11
package/src/update/job.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { atomicWriteFile, getConfigDir, readPid } from "../config";
|
|
5
|
+
import { atomicWriteFile, getConfigDir, loadConfig, readPid, readRuntimePort } from "../config";
|
|
6
6
|
import { killProxy } from "../lib/process-control";
|
|
7
|
+
import { waitForPortAvailable } from "../server/ports";
|
|
7
8
|
import { isServiceInstalled } from "../service";
|
|
8
9
|
import {
|
|
9
10
|
type Channel,
|
|
@@ -158,18 +159,23 @@ export function restartCommand(
|
|
|
158
159
|
serviceInstalled: boolean,
|
|
159
160
|
installer: Installer,
|
|
160
161
|
launcher = packageLauncherPath(),
|
|
162
|
+
port?: number,
|
|
161
163
|
): { mode: "service" | "proxy"; bin: string; args: string[]; display: string } {
|
|
162
164
|
const mode = serviceInstalled ? "service" : "proxy";
|
|
165
|
+
const pinPort = !serviceInstalled && typeof port === "number" && Number.isFinite(port) && port > 0;
|
|
166
|
+
const startArgs = pinPort
|
|
167
|
+
? [launcher, "start", "--port", String(Math.trunc(port))]
|
|
168
|
+
: [launcher, "start"];
|
|
163
169
|
if (installer === "npm") {
|
|
164
170
|
const bin = nodeBin();
|
|
165
|
-
const args = serviceInstalled ? [launcher, "service", "install"] :
|
|
171
|
+
const args = serviceInstalled ? [launcher, "service", "install"] : startArgs;
|
|
166
172
|
return { mode, bin, args, display: formatCommand(bin, args) };
|
|
167
173
|
}
|
|
168
174
|
// bun/source installs: restart via the current runtime executable + package launcher (both real
|
|
169
175
|
// .exe files), NOT the `ocx.cmd` shim. Spawning a `.cmd` shell-less throws EINVAL on Windows
|
|
170
176
|
// Node/Bun ≥18.20/20.12 (CVE-2024-27980 hardening) — the same class the npm path (nodeBin) avoids.
|
|
171
177
|
const bin = process.execPath;
|
|
172
|
-
const args = serviceInstalled ? [launcher, "service", "install"] :
|
|
178
|
+
const args = serviceInstalled ? [launcher, "service", "install"] : startArgs;
|
|
173
179
|
return { mode, bin, args, display: formatCommand(bin, args) };
|
|
174
180
|
}
|
|
175
181
|
|
|
@@ -264,8 +270,8 @@ function runLoggedCommand(job: UpdateJobState, bin: string, args: string[], time
|
|
|
264
270
|
return { status: result.status, signal: result.signal };
|
|
265
271
|
}
|
|
266
272
|
|
|
267
|
-
function spawnDetachedStart(job: UpdateJobState, installer: Installer): void {
|
|
268
|
-
const cmd = restartCommand(false, installer);
|
|
273
|
+
function spawnDetachedStart(job: UpdateJobState, installer: Installer, port?: number): void {
|
|
274
|
+
const cmd = restartCommand(false, installer, packageLauncherPath(), port);
|
|
269
275
|
const env = { ...process.env };
|
|
270
276
|
delete env.OCX_SERVICE;
|
|
271
277
|
updateJob(job, {}, `$ ${cmd.display}`);
|
|
@@ -278,9 +284,26 @@ function spawnDetachedStart(job: UpdateJobState, installer: Installer): void {
|
|
|
278
284
|
child.unref();
|
|
279
285
|
}
|
|
280
286
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
287
|
+
/** Test seam: the wait/spawn pair is injectable so the restart path is verifiable. */
|
|
288
|
+
export interface RestartIo {
|
|
289
|
+
waitForPort?: typeof waitForPortAvailable;
|
|
290
|
+
spawnStart?: (job: UpdateJobState, installer: Installer, port?: number) => void;
|
|
291
|
+
serviceInstalledFn?: () => boolean;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function restartAfterUpdate(
|
|
295
|
+
job: UpdateJobState,
|
|
296
|
+
captured?: { port: number; hostname: string },
|
|
297
|
+
io: RestartIo = {},
|
|
298
|
+
): Promise<void> {
|
|
299
|
+
const serviceInstalled = (io.serviceInstalledFn ?? isServiceInstalled)();
|
|
300
|
+
const config = loadConfig();
|
|
301
|
+
// The stop-first update flow has already cleared pid/runtime state by the time we run,
|
|
302
|
+
// so the pre-update capture (taken before the update command) is the authoritative
|
|
303
|
+
// port to wait on; config is only the cold-start fallback.
|
|
304
|
+
const port = captured?.port ?? config.port ?? 10100;
|
|
305
|
+
const hostname = captured?.hostname ?? config.hostname ?? "127.0.0.1";
|
|
306
|
+
const cmd = restartCommand(serviceInstalled, job.installer, packageLauncherPath(), port);
|
|
284
307
|
if (serviceInstalled) {
|
|
285
308
|
const result = runLoggedCommand(job, cmd.bin, cmd.args, RESTART_TIMEOUT_MS);
|
|
286
309
|
if (result.status !== 0) {
|
|
@@ -294,13 +317,38 @@ function restartAfterUpdate(job: UpdateJobState): void {
|
|
|
294
317
|
updateJob(job, {}, `Stopping current proxy PID ${pid}.`);
|
|
295
318
|
killProxy(pid);
|
|
296
319
|
}
|
|
297
|
-
|
|
320
|
+
// The old socket can stay busy briefly after stop (Windows taskkill drain, or the
|
|
321
|
+
// stop-first update path that already killed the proxy before we got here) — wait
|
|
322
|
+
// unconditionally on the captured port so the pinned start does not race the drain.
|
|
323
|
+
const waitFn = io.waitForPort ?? waitForPortAvailable;
|
|
324
|
+
const freed = await waitFn(port, hostname, { timeoutMs: 2000, intervalMs: 25 });
|
|
325
|
+
if (!freed) {
|
|
326
|
+
updateJob(job, {}, `Port ${port} still busy after stop; starting with --port ${port} anyway.`);
|
|
327
|
+
}
|
|
328
|
+
(io.spawnStart ?? spawnDetachedStart)(job, job.installer, port);
|
|
298
329
|
}
|
|
299
330
|
|
|
300
|
-
|
|
331
|
+
/** Exposed for tests: drives the non-service restart path with injected io. */
|
|
332
|
+
export function restartAfterUpdateForTests(
|
|
333
|
+
job: UpdateJobState,
|
|
334
|
+
captured: { port: number; hostname: string },
|
|
335
|
+
io: RestartIo,
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
return restartAfterUpdate(job, captured, io);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function runGuiUpdateWorker(jobId: string, channel: Channel, restart: boolean): Promise<void> {
|
|
301
341
|
let job = readUpdateJob(jobId);
|
|
302
342
|
const check = checkForUpdate(channel);
|
|
303
343
|
const now = new Date().toISOString();
|
|
344
|
+
// Capture the live listen target BEFORE the update command runs: the stop-first update
|
|
345
|
+
// flow clears pid/runtime state, so this is the last moment the real port is knowable.
|
|
346
|
+
const rt = readRuntimePort();
|
|
347
|
+
const preUpdateConfig = loadConfig();
|
|
348
|
+
const captured = {
|
|
349
|
+
port: rt?.port ?? preUpdateConfig.port ?? 10100,
|
|
350
|
+
hostname: rt?.hostname ?? preUpdateConfig.hostname ?? "127.0.0.1",
|
|
351
|
+
};
|
|
304
352
|
if (!job) {
|
|
305
353
|
job = {
|
|
306
354
|
id: jobId,
|
|
@@ -362,7 +410,7 @@ export function runGuiUpdateWorker(jobId: string, channel: Channel, restart: boo
|
|
|
362
410
|
|
|
363
411
|
if (restart) {
|
|
364
412
|
job = updateJob(job, { status: "restarting" }, "Update installed. Restarting proxy...");
|
|
365
|
-
restartAfterUpdate(job);
|
|
413
|
+
await restartAfterUpdate(job, captured);
|
|
366
414
|
updateJob(job, { status: "succeeded", restarted: true }, "Restart requested.");
|
|
367
415
|
return;
|
|
368
416
|
}
|