@nchappell/codex-web-ui 1.0.6 → 1.0.8
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +1 -1
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/thread/[threadId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/threads/page_client-reference-manifest.js +1 -1
- package/.next/server/app/threads.html +1 -1
- package/.next/server/app/threads.rsc +2 -2
- package/.next/server/app/threads.segments/_full.segment.rsc +2 -2
- package/.next/server/app/threads.segments/_head.segment.rsc +1 -1
- package/.next/server/app/threads.segments/_index.segment.rsc +1 -1
- package/.next/server/app/threads.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/threads.segments/threads/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/threads.segments/threads.segment.rsc +1 -1
- package/.next/server/chunks/[root-of-the-server]__0pym12l._.js +3 -3
- package/.next/server/chunks/[root-of-the-server]__0pym12l._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0h7ed3q._.js +6 -6
- package/.next/server/chunks/ssr/[root-of-the-server]__0h7ed3q._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/{0k4c4eedynfr0.js → 0g7lwnm9wxo3~.js} +7 -7
- package/README.md +17 -14
- package/bin/codex-web-ui.js +70 -33
- package/bin/docker-entrypoint.sh +5 -0
- package/client/src/App.tsx +1 -19
- package/client/src/api.ts +0 -5
- package/package.json +3 -2
- package/server/appApi.ts +0 -25
- /package/.next/static/{Kq7OCztahzPSRmlZKubC1 → QMdoD-vxQDoRuGNGiCAPJ}/_buildManifest.js +0 -0
- /package/.next/static/{Kq7OCztahzPSRmlZKubC1 → QMdoD-vxQDoRuGNGiCAPJ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Kq7OCztahzPSRmlZKubC1 → QMdoD-vxQDoRuGNGiCAPJ}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -23,17 +23,18 @@ The app is a Next.js App Router application with Tailwind, shadcn/ui, and AI Ele
|
|
|
23
23
|
```bash
|
|
24
24
|
npm install -g @nchappell/codex-web-ui
|
|
25
25
|
codex-web-ui init
|
|
26
|
+
codex-web-ui app-server start
|
|
26
27
|
codex-web-ui doctor
|
|
27
28
|
codex-web-ui
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
Open `http://127.0.0.1:4545`.
|
|
31
32
|
|
|
32
|
-
The normal startup command starts the Next.js server
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
app-server socket.
|
|
33
|
+
The normal startup command starts only the Next.js server. The detached
|
|
34
|
+
`codex app-server` sidecar must already be running and websocket-ready on the
|
|
35
|
+
configured Unix socket. Run `codex-web-ui doctor` after install or upgrade to
|
|
36
|
+
check the production build, Codex CLI availability, login status, auth setup,
|
|
37
|
+
writable data directories, permission settings, and app-server socket.
|
|
37
38
|
|
|
38
39
|
For source or git installs, run `codex-web-ui --build` once if the package does
|
|
39
40
|
not include a `.next` production build.
|
|
@@ -81,8 +82,9 @@ npm run test:docker
|
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
The Docker smoke builds `codex-web-ui:smoke`, starts it on port `4555` with a
|
|
84
|
-
temporary data directory
|
|
85
|
-
|
|
85
|
+
temporary data directory, verifies `/threads`, `/api/auth`, and password login,
|
|
86
|
+
then removes the container and temp volumes. The container entrypoint starts the
|
|
87
|
+
sidecar before launching the web process.
|
|
86
88
|
By default it builds with `@nchappell/codex-web-ui@<package.json version>` from
|
|
87
89
|
npm. Set `CODEX_WEB_UI_DOCKER_NPM_SPEC=@nchappell/codex-web-ui@latest` or
|
|
88
90
|
another npm package spec to test a different published package,
|
|
@@ -152,10 +154,10 @@ codex-web-ui \
|
|
|
152
154
|
--data-dir "$PWD/data"
|
|
153
155
|
```
|
|
154
156
|
|
|
155
|
-
The main command
|
|
156
|
-
|
|
157
|
-
`--app-server-socket
|
|
158
|
-
not
|
|
157
|
+
The main command does not manage the app-server sidecar. Start it explicitly
|
|
158
|
+
with `codex-web-ui app-server start`, or run the official app-server yourself
|
|
159
|
+
and point `--app-server-socket` at that Unix socket. Startup fails fast if the
|
|
160
|
+
socket is not websocket-ready.
|
|
159
161
|
|
|
160
162
|
Default permissions are intentionally conservative: `on-request` approval and
|
|
161
163
|
`workspace-write` sandbox. `danger-full-access`, `on-failure`, and `never`
|
|
@@ -345,9 +347,10 @@ npm run app-server:recover
|
|
|
345
347
|
npm run app-server:status
|
|
346
348
|
```
|
|
347
349
|
|
|
348
|
-
`app-server:recover` treats a live PID without a
|
|
349
|
-
degraded, restarts the sidecar, and waits until the Unix socket accepts
|
|
350
|
-
|
|
350
|
+
`app-server:recover` treats a live PID without a websocket-ready socket as
|
|
351
|
+
degraded, restarts the sidecar, and waits until the Unix socket accepts the
|
|
352
|
+
app-server WebSocket upgrade. Use it when the UI reports `connect ENOENT
|
|
353
|
+
...codex-app-server.sock` or `Unix socket closed before WebSocket upgrade`.
|
|
351
354
|
|
|
352
355
|
For MCP OAuth from a phone or another machine, Codex Web UI relays OAuth
|
|
353
356
|
callbacks through the currently used Web UI origin. When an MCP OAuth login
|
package/bin/codex-web-ui.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { accessSync, chmodSync, closeSync, constants as fsConstants, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { randomBytes } from "node:crypto";
|
|
5
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
6
6
|
import { createConnection } from "node:net";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -123,15 +123,21 @@ if (!existsSync(join(packageRoot, ".next", "BUILD_ID"))) {
|
|
|
123
123
|
mkdirSync(dataDir, { recursive: true });
|
|
124
124
|
mkdirSync(uploadDir, { recursive: true });
|
|
125
125
|
|
|
126
|
-
if (!
|
|
127
|
-
|
|
126
|
+
if (!await isSocketWebSocketReady(appServerSocket, 1_000)) {
|
|
127
|
+
fail([
|
|
128
|
+
`Codex app-server socket is not ready: ${appServerSocket}`,
|
|
129
|
+
"Start it before launching the Web UI:",
|
|
130
|
+
` codex-web-ui app-server start --socket ${appServerSocket}`,
|
|
131
|
+
"",
|
|
132
|
+
"If you run the official app-server yourself, make sure it listens on that Unix socket."
|
|
133
|
+
].join("\n"));
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
console.log(`codex-web-ui listening on http://${host}:${port}`);
|
|
131
137
|
console.log(`codex-web-ui app cwd: ${packageRoot}`);
|
|
132
138
|
console.log(`codex default cwd: ${env.CODEX_CWD}`);
|
|
133
139
|
console.log(`runtime data dir: ${env.CODEX_WEB_UI_DATA_DIR}`);
|
|
134
|
-
console.log(`codex app-server socket: ${env.CODEX_APP_SERVER_SOCKET}
|
|
140
|
+
console.log(`codex app-server socket: ${env.CODEX_APP_SERVER_SOCKET}`);
|
|
135
141
|
console.log(`permissions: approval=${env.CODEX_WEB_UI_APPROVAL_POLICY}, sandbox=${env.CODEX_WEB_UI_SANDBOX}${env.CODEX_WEB_UI_LOCK_PERMISSIONS === "1" ? ", locked" : ""}${unsafePermissions ? ", unsafe enabled" : ""}`);
|
|
136
142
|
if (configPath) {
|
|
137
143
|
console.log(`config file: ${configPath}`);
|
|
@@ -373,11 +379,11 @@ async function runDoctorCommand(options) {
|
|
|
373
379
|
}
|
|
374
380
|
|
|
375
381
|
const socketPresent = existsSync(options.appServerSocket);
|
|
376
|
-
const
|
|
382
|
+
const socketReady = socketPresent ? await isSocketWebSocketReady(options.appServerSocket, 500) : false;
|
|
377
383
|
checks.push([
|
|
378
384
|
"app-server socket",
|
|
379
|
-
`${options.appServerSocket}${
|
|
380
|
-
|
|
385
|
+
`${options.appServerSocket}${socketReady ? " (websocket ready)" : socketPresent ? " (present, not websocket-ready)" : " (missing)"}`,
|
|
386
|
+
socketReady
|
|
381
387
|
]);
|
|
382
388
|
|
|
383
389
|
console.log("Codex Web UI doctor");
|
|
@@ -385,8 +391,8 @@ async function runDoctorCommand(options) {
|
|
|
385
391
|
console.log(`${ok ? "ok " : "fail"} ${name}: ${detail}`);
|
|
386
392
|
}
|
|
387
393
|
console.log("");
|
|
388
|
-
if (!
|
|
389
|
-
console.log("
|
|
394
|
+
if (!socketReady) {
|
|
395
|
+
console.log("Start the sidecar first with `codex-web-ui app-server start`, then run `codex-web-ui`.");
|
|
390
396
|
}
|
|
391
397
|
if (!options.password && isLoopbackHost(options.host)) {
|
|
392
398
|
console.log("No password is configured; `codex-web-ui` will print a temporary local password on each start.");
|
|
@@ -410,12 +416,12 @@ function appServerControlOptions({ command, logFile, pidFile, socket }) {
|
|
|
410
416
|
async function startAppServer(options) {
|
|
411
417
|
const existingPid = readPid(options.pidFile);
|
|
412
418
|
if (existingPid && isPidRunning(existingPid)) {
|
|
413
|
-
if (await
|
|
419
|
+
if (await isSocketWebSocketReady(options.socket, 750)) {
|
|
414
420
|
console.log(`codex app-server already running: pid=${existingPid}`);
|
|
415
|
-
console.log(`socket: ${options.socket} (
|
|
421
|
+
console.log(`socket: ${options.socket} (websocket ready)`);
|
|
416
422
|
return;
|
|
417
423
|
}
|
|
418
|
-
console.warn(`codex app-server pid ${existingPid} is present, but the socket is not
|
|
424
|
+
console.warn(`codex app-server pid ${existingPid} is present, but the socket is not websocket-ready; restarting`);
|
|
419
425
|
await stopAppServer(options, { quiet: true });
|
|
420
426
|
}
|
|
421
427
|
mkdirSync(dirname(options.socket), { recursive: true });
|
|
@@ -442,7 +448,7 @@ async function startAppServer(options) {
|
|
|
442
448
|
fail(`Timed out waiting for codex app-server socket: ${options.socket}. Check ${options.logFile}`);
|
|
443
449
|
}
|
|
444
450
|
console.log(`codex app-server started: pid=${child.pid}`);
|
|
445
|
-
console.log(`socket: ${options.socket} (
|
|
451
|
+
console.log(`socket: ${options.socket} (websocket ready)`);
|
|
446
452
|
console.log(`log: ${options.logFile}`);
|
|
447
453
|
}
|
|
448
454
|
|
|
@@ -473,9 +479,9 @@ async function stopAppServer(options, { quiet = false } = {}) {
|
|
|
473
479
|
|
|
474
480
|
async function recoverAppServer(options) {
|
|
475
481
|
const pid = readPid(options.pidFile);
|
|
476
|
-
if (pid && isPidRunning(pid) && await
|
|
482
|
+
if (pid && isPidRunning(pid) && await isSocketWebSocketReady(options.socket, 750)) {
|
|
477
483
|
console.log(`codex app-server healthy: pid=${pid}`);
|
|
478
|
-
console.log(`socket: ${options.socket} (
|
|
484
|
+
console.log(`socket: ${options.socket} (websocket ready)`);
|
|
479
485
|
return;
|
|
480
486
|
}
|
|
481
487
|
await stopAppServer(options, { quiet: true });
|
|
@@ -487,8 +493,8 @@ async function printAppServerStatus(options) {
|
|
|
487
493
|
const pid = readPid(options.pidFile);
|
|
488
494
|
const running = Boolean(pid && isPidRunning(pid));
|
|
489
495
|
const socketPresent = existsSync(options.socket);
|
|
490
|
-
const
|
|
491
|
-
const state = running &&
|
|
496
|
+
const socketReady = socketPresent ? await isSocketWebSocketReady(options.socket, 500) : false;
|
|
497
|
+
const state = running && socketReady
|
|
492
498
|
? "running"
|
|
493
499
|
: running
|
|
494
500
|
? "degraded"
|
|
@@ -496,7 +502,7 @@ async function printAppServerStatus(options) {
|
|
|
496
502
|
? "stopped with stale socket"
|
|
497
503
|
: "stopped";
|
|
498
504
|
console.log(`codex app-server ${state}${pid ? `: pid=${pid}` : ""}`);
|
|
499
|
-
console.log(`socket: ${options.socket}${
|
|
505
|
+
console.log(`socket: ${options.socket}${socketReady ? " (websocket ready)" : socketPresent ? " (present, not websocket-ready)" : " (missing)"}`);
|
|
500
506
|
console.log(`pid file: ${options.pidFile}`);
|
|
501
507
|
console.log(`log: ${options.logFile}`);
|
|
502
508
|
}
|
|
@@ -546,7 +552,7 @@ function waitForSocket(socket, timeoutMs) {
|
|
|
546
552
|
const startedAt = Date.now();
|
|
547
553
|
return new Promise((resolve) => {
|
|
548
554
|
const tick = async () => {
|
|
549
|
-
if (await
|
|
555
|
+
if (await isSocketWebSocketReady(socket, 250)) {
|
|
550
556
|
resolve(true);
|
|
551
557
|
return;
|
|
552
558
|
}
|
|
@@ -560,25 +566,56 @@ function waitForSocket(socket, timeoutMs) {
|
|
|
560
566
|
});
|
|
561
567
|
}
|
|
562
568
|
|
|
563
|
-
function
|
|
569
|
+
function isSocketWebSocketReady(socket, timeoutMs) {
|
|
564
570
|
if (!existsSync(socket)) {
|
|
565
571
|
return Promise.resolve(false);
|
|
566
572
|
}
|
|
567
573
|
return new Promise((resolve) => {
|
|
568
574
|
const client = createConnection(socket);
|
|
569
|
-
const
|
|
575
|
+
const key = randomBytes(16).toString("base64");
|
|
576
|
+
const expectedAccept = createHash("sha1")
|
|
577
|
+
.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
|
|
578
|
+
.digest("base64");
|
|
579
|
+
let buffer = Buffer.alloc(0);
|
|
580
|
+
let settled = false;
|
|
581
|
+
const settle = (ready) => {
|
|
582
|
+
if (settled) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
settled = true;
|
|
586
|
+
clearTimeout(timer);
|
|
570
587
|
client.destroy();
|
|
571
|
-
resolve(
|
|
572
|
-
}
|
|
588
|
+
resolve(ready);
|
|
589
|
+
};
|
|
590
|
+
const timer = setTimeout(() => settle(false), timeoutMs);
|
|
573
591
|
client.once("connect", () => {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
592
|
+
client.write([
|
|
593
|
+
"GET / HTTP/1.1",
|
|
594
|
+
"Host: localhost",
|
|
595
|
+
"Upgrade: websocket",
|
|
596
|
+
"Connection: Upgrade",
|
|
597
|
+
`Sec-WebSocket-Key: ${key}`,
|
|
598
|
+
"Sec-WebSocket-Version: 13",
|
|
599
|
+
"",
|
|
600
|
+
""
|
|
601
|
+
].join("\r\n"));
|
|
577
602
|
});
|
|
578
|
-
client.
|
|
579
|
-
|
|
580
|
-
|
|
603
|
+
client.on("data", (chunk) => {
|
|
604
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
605
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
606
|
+
if (headerEnd === -1) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const headerText = buffer.subarray(0, headerEnd).toString("utf8");
|
|
610
|
+
const lines = headerText.split("\r\n");
|
|
611
|
+
const headers = Object.fromEntries(lines.slice(1).map((line) => {
|
|
612
|
+
const index = line.indexOf(":");
|
|
613
|
+
return index === -1 ? ["", ""] : [line.slice(0, index).trim().toLowerCase(), line.slice(index + 1).trim()];
|
|
614
|
+
}).filter(([name]) => name));
|
|
615
|
+
settle(lines[0]?.startsWith("HTTP/1.1 101") && headers["sec-websocket-accept"] === expectedAccept);
|
|
581
616
|
});
|
|
617
|
+
client.once("error", () => settle(false));
|
|
618
|
+
client.once("close", () => settle(false));
|
|
582
619
|
});
|
|
583
620
|
}
|
|
584
621
|
|
|
@@ -803,8 +840,8 @@ function printHelp() {
|
|
|
803
840
|
codex-web-ui doctor [options]
|
|
804
841
|
codex-web-ui app-server <start|stop|restart|recover|status> [options]
|
|
805
842
|
|
|
806
|
-
Starts the Codex Web UI using Next.js production mode.
|
|
807
|
-
|
|
843
|
+
Starts the Codex Web UI using Next.js production mode. The codex app-server
|
|
844
|
+
sidecar must already be running.
|
|
808
845
|
|
|
809
846
|
Options:
|
|
810
847
|
-c, --config <path> JSON config file. Default search:
|
|
@@ -818,8 +855,8 @@ Options:
|
|
|
818
855
|
--allowed-origins <csv> Set CODEX_WEB_UI_ALLOWED_ORIGINS
|
|
819
856
|
--app-server-socket <path> Codex app-server Unix socket. Default:
|
|
820
857
|
~/.codex-webgui/codex-app-server.sock
|
|
821
|
-
--external-app-server
|
|
822
|
-
|
|
858
|
+
--external-app-server Deprecated no-op; app-server is always external
|
|
859
|
+
to the web process
|
|
823
860
|
--codex-command <command> Codex command. Default: codex
|
|
824
861
|
--cwd <path> Default Codex working directory
|
|
825
862
|
--model <model> Default Codex model. Default: gpt-5.5
|
package/client/src/App.tsx
CHANGED
|
@@ -85,7 +85,6 @@ import {
|
|
|
85
85
|
logout,
|
|
86
86
|
openEventStream,
|
|
87
87
|
readReferencedFile,
|
|
88
|
-
recoverAppServer,
|
|
89
88
|
reloadMcpServers,
|
|
90
89
|
respondClientRequest,
|
|
91
90
|
restartServer,
|
|
@@ -896,7 +895,6 @@ export default function App({ initialThreadId = null }: AppProps) {
|
|
|
896
895
|
rateLimits={rateLimits}
|
|
897
896
|
status={serverStatus}
|
|
898
897
|
onClose={() => setStatusOpen(false)}
|
|
899
|
-
onRecover={recoverAppServerFromUi}
|
|
900
898
|
onRefreshMcp={reloadMcpServerStatus}
|
|
901
899
|
onLoginMcpServer={handleLoginMcpServer}
|
|
902
900
|
statusRefreshing={statusRefreshing}
|
|
@@ -1002,17 +1000,6 @@ export default function App({ initialThreadId = null }: AppProps) {
|
|
|
1002
1000
|
}
|
|
1003
1001
|
}
|
|
1004
1002
|
|
|
1005
|
-
async function recoverAppServerFromUi() {
|
|
1006
|
-
try {
|
|
1007
|
-
const result = await recoverAppServer();
|
|
1008
|
-
setServerStatus(result.status);
|
|
1009
|
-
showToast(result.output || "App server recovered");
|
|
1010
|
-
await loadSessions();
|
|
1011
|
-
} catch (error) {
|
|
1012
|
-
showToast(error);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
1003
|
async function refreshStatus() {
|
|
1017
1004
|
setStatusRefreshing(true);
|
|
1018
1005
|
try {
|
|
@@ -2563,7 +2550,6 @@ function StatusModal({
|
|
|
2563
2550
|
statusRefreshing,
|
|
2564
2551
|
onClose,
|
|
2565
2552
|
onLoginMcpServer,
|
|
2566
|
-
onRecover,
|
|
2567
2553
|
onRefresh,
|
|
2568
2554
|
onRefreshMcp,
|
|
2569
2555
|
onSaveMcpServer
|
|
@@ -2576,7 +2562,6 @@ function StatusModal({
|
|
|
2576
2562
|
statusRefreshing: boolean;
|
|
2577
2563
|
onClose: () => void;
|
|
2578
2564
|
onLoginMcpServer: (name: string) => Promise<string>;
|
|
2579
|
-
onRecover: () => Promise<void>;
|
|
2580
2565
|
onRefresh: () => Promise<void>;
|
|
2581
2566
|
onRefreshMcp: () => Promise<void>;
|
|
2582
2567
|
onSaveMcpServer: (input: { name: string; url: string }) => Promise<void>;
|
|
@@ -2703,11 +2688,8 @@ function StatusModal({
|
|
|
2703
2688
|
</div>
|
|
2704
2689
|
</section>
|
|
2705
2690
|
)}
|
|
2706
|
-
<p className="muted">Rate limits use the app-server account quota snapshot.
|
|
2691
|
+
<p className="muted">Rate limits use the app-server account quota snapshot. Start or recover the sidecar from the CLI, then reconnect this UI.</p>
|
|
2707
2692
|
<footer className="modal-actions">
|
|
2708
|
-
<button className="secondary-button" type="button" onClick={() => void onRecover()}>
|
|
2709
|
-
<RefreshCw size={16} /> Recover app-server
|
|
2710
|
-
</button>
|
|
2711
2693
|
<button className="primary-button" type="button" onClick={() => void onRefresh()} disabled={statusRefreshing}>
|
|
2712
2694
|
<RefreshCw size={16} className={statusRefreshing ? "spin-icon" : ""} /> Refresh
|
|
2713
2695
|
</button>
|
package/client/src/api.ts
CHANGED
|
@@ -29,11 +29,6 @@ export async function restartServer(): Promise<ServerStatus> {
|
|
|
29
29
|
return body.status;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export async function recoverAppServer(): Promise<{ output: string; status: ServerStatus }> {
|
|
33
|
-
const body = await postJson<{ output: string; status: ServerStatus }>("/api/app-server/recover", {});
|
|
34
|
-
return body;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
32
|
export async function rpc<T = unknown>(method: string, params: JsonValue = {}): Promise<T> {
|
|
38
33
|
const body = await postJson<{ result: T }>("/api/rpc", { method, params });
|
|
39
34
|
return body.result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nchappell/codex-web-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Mobile-friendly web UI client for the official Codex app-server.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
],
|
|
23
23
|
"type": "module",
|
|
24
24
|
"bin": {
|
|
25
|
-
"codex-web-ui": "bin/codex-web-ui.js"
|
|
25
|
+
"codex-web-ui": "bin/codex-web-ui.js",
|
|
26
|
+
"codex-web-ui-docker-entrypoint": "bin/docker-entrypoint.sh"
|
|
26
27
|
},
|
|
27
28
|
"publishConfig": {
|
|
28
29
|
"access": "public"
|
package/server/appApi.ts
CHANGED
|
@@ -231,12 +231,6 @@ async function dispatchApiRequest(request: Request, url: URL, cors: Headers): Pr
|
|
|
231
231
|
return json({ ok: true, status: bridge.summary() }, 200, cors);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
if (pathname === "/api/app-server/recover" && request.method === "POST") {
|
|
235
|
-
const output = await recoverAppServerSidecar();
|
|
236
|
-
await bridge.restart();
|
|
237
|
-
return json({ ok: true, output, status: bridge.summary() }, 200, cors);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
234
|
if (pathname === "/api/server/stop" && request.method === "POST") {
|
|
241
235
|
await bridge.stop();
|
|
242
236
|
return json({ ok: true, status: bridge.summary() }, 200, cors);
|
|
@@ -828,25 +822,6 @@ function json(payload: unknown, status = 200, cors?: Headers): Response {
|
|
|
828
822
|
);
|
|
829
823
|
}
|
|
830
824
|
|
|
831
|
-
async function recoverAppServerSidecar(): Promise<string> {
|
|
832
|
-
const socket = process.env.CODEX_APP_SERVER_SOCKET;
|
|
833
|
-
if (!socket) {
|
|
834
|
-
throw new Error("CODEX_APP_SERVER_SOCKET is not configured; this server owns its app-server connection.");
|
|
835
|
-
}
|
|
836
|
-
const binPath = path.join(projectRoot, "bin", "codex-web-ui.js");
|
|
837
|
-
return new Promise((resolve, reject) => {
|
|
838
|
-
execFile(process.execPath, [binPath, "app-server", "recover", "--socket", socket], { cwd: projectRoot, env: process.env, timeout: 12_000 }, (error, stdout, stderr) => {
|
|
839
|
-
const output = [stdout, stderr].filter(Boolean).join("\n").trim();
|
|
840
|
-
if (error) {
|
|
841
|
-
const message = output || error.message;
|
|
842
|
-
reject(new Error(message));
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
resolve(output);
|
|
846
|
-
});
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
|
|
850
825
|
function noStoreHeaders(): Headers {
|
|
851
826
|
return new Headers({
|
|
852
827
|
"Cache-Control": "no-store"
|
|
File without changes
|
/package/.next/static/{Kq7OCztahzPSRmlZKubC1 → QMdoD-vxQDoRuGNGiCAPJ}/_clientMiddlewareManifest.js
RENAMED
|
File without changes
|
|
File without changes
|