remcodex 0.1.0-beta.1

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +331 -0
  3. package/dist/server/src/app.js +186 -0
  4. package/dist/server/src/cli.js +270 -0
  5. package/dist/server/src/controllers/codex-options.controller.js +199 -0
  6. package/dist/server/src/controllers/message.controller.js +21 -0
  7. package/dist/server/src/controllers/project.controller.js +44 -0
  8. package/dist/server/src/controllers/session.controller.js +175 -0
  9. package/dist/server/src/db/client.js +10 -0
  10. package/dist/server/src/db/migrations.js +32 -0
  11. package/dist/server/src/gateways/ws.gateway.js +60 -0
  12. package/dist/server/src/services/codex-app-server-runner.js +363 -0
  13. package/dist/server/src/services/codex-exec-runner.js +147 -0
  14. package/dist/server/src/services/codex-rollout-sync.js +977 -0
  15. package/dist/server/src/services/codex-runner.js +11 -0
  16. package/dist/server/src/services/codex-stream-events.js +478 -0
  17. package/dist/server/src/services/event-store.js +328 -0
  18. package/dist/server/src/services/project-manager.js +130 -0
  19. package/dist/server/src/services/pty-runner.js +72 -0
  20. package/dist/server/src/services/session-manager.js +1586 -0
  21. package/dist/server/src/services/session-timeline-service.js +181 -0
  22. package/dist/server/src/types/codex-launch.js +2 -0
  23. package/dist/server/src/types/models.js +37 -0
  24. package/dist/server/src/utils/ansi.js +143 -0
  25. package/dist/server/src/utils/codex-launch.js +102 -0
  26. package/dist/server/src/utils/codex-quota.js +179 -0
  27. package/dist/server/src/utils/codex-status.js +163 -0
  28. package/dist/server/src/utils/codex-ui-options.js +114 -0
  29. package/dist/server/src/utils/command.js +46 -0
  30. package/dist/server/src/utils/errors.js +16 -0
  31. package/dist/server/src/utils/ids.js +7 -0
  32. package/dist/server/src/utils/node-pty.js +29 -0
  33. package/package.json +36 -0
  34. package/scripts/fix-node-pty-helper.js +36 -0
  35. package/web/api.js +175 -0
  36. package/web/app.js +8082 -0
  37. package/web/components/composer.js +627 -0
  38. package/web/components/session-workbench.js +173 -0
  39. package/web/i18n/index.js +171 -0
  40. package/web/i18n/locales/de.js +50 -0
  41. package/web/i18n/locales/en.js +320 -0
  42. package/web/i18n/locales/es.js +50 -0
  43. package/web/i18n/locales/fr.js +50 -0
  44. package/web/i18n/locales/ja.js +50 -0
  45. package/web/i18n/locales/ko.js +50 -0
  46. package/web/i18n/locales/pt-BR.js +50 -0
  47. package/web/i18n/locales/ru.js +50 -0
  48. package/web/i18n/locales/zh-CN.js +320 -0
  49. package/web/i18n/locales/zh-Hant.js +53 -0
  50. package/web/index.html +23 -0
  51. package/web/message-rich-text.js +218 -0
  52. package/web/session-command-activity.js +980 -0
  53. package/web/session-event-adapter.js +826 -0
  54. package/web/session-timeline-reducer.js +728 -0
  55. package/web/session-timeline-renderer.js +656 -0
  56. package/web/session-ws.js +31 -0
  57. package/web/styles.css +5665 -0
  58. package/web/vendor/markdown-it.js +6969 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RemCodex contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # RemCodex
2
+
3
+ > Control Codex from anywhere. Even on your phone.
4
+
5
+ RemCodex is a local-first web UI for running, reviewing, approving, and resuming Codex sessions from your browser.
6
+
7
+ It is built for the real workflow: long-running sessions, mobile check-ins, approval prompts, imported rollout history, and timeline-style execution flow.
8
+
9
+ ```mermaid
10
+ flowchart LR
11
+ subgraph D[Your devices]
12
+ P[Phone]
13
+ B[Browser]
14
+ end
15
+
16
+ subgraph M[Your work machine]
17
+ subgraph R[RemCodex]
18
+ UI[Web UI]
19
+ S[Server]
20
+ T[Timeline + approvals + sync]
21
+ end
22
+
23
+ subgraph C[Local Codex runtime]
24
+ X[Codex CLI / app-server]
25
+ F[Workspace files]
26
+ H[~/.codex sessions]
27
+ end
28
+ end
29
+
30
+ P --> UI
31
+ B --> UI
32
+ UI --> S
33
+ S --> T
34
+ S --> X
35
+ X --> F
36
+ X --> H
37
+ H --> S
38
+ T --> UI
39
+ ```
40
+
41
+ - Watch live Codex runs without staying in the terminal
42
+ - Approve sensitive actions from a cleaner UI
43
+ - Pick up the same session again after refresh, sleep, or reconnect
44
+
45
+ ## Status
46
+
47
+ This project is currently a **beta / developer preview**.
48
+
49
+ MIT licensed — free for personal and commercial use.
50
+
51
+ Cloud version coming soon.
52
+
53
+ It is already usable for local and internal workflows, but it is not yet packaged as a one-click desktop app.
54
+
55
+ ## Why People Use It
56
+
57
+ Codex is powerful in the terminal, but many real workflows need a better control surface:
58
+
59
+ - checking progress from your phone
60
+ - watching a long run without babysitting a terminal window
61
+ - approving writes from a cleaner interface
62
+ - reopening a session after refresh, sleep, or reconnect
63
+ - reviewing imported rollout history next to native sessions
64
+
65
+ RemCodex turns Codex's event stream into a browser-based workspace that is easier to follow, easier to resume, and easier to operate.
66
+
67
+ ## What It Does
68
+
69
+ - Run Codex sessions from a browser
70
+ - View sessions in a single-page workspace with a sidebar and execution timeline
71
+ - Follow streaming assistant output, commands, patches, and approvals
72
+ - Approve or reject file-system actions from the UI
73
+ - Import existing Codex rollout sessions from `~/.codex/sessions/...`
74
+ - Keep imported sessions in sync while they are still active
75
+ - Resume stale sessions after the page comes back from background
76
+ - Work well on desktop and on mobile
77
+
78
+ ## Screenshots
79
+
80
+ ![RemCodex desktop workspace](docs/assets/hero-desktop.png)
81
+
82
+ > Run and review Codex sessions in a single-page workspace.
83
+
84
+ ![RemCodex mobile session view](docs/assets/mobile-session.png)
85
+
86
+ > Follow a live Codex session from your phone.
87
+
88
+ ![RemCodex approval flow](docs/assets/approval-flow.png)
89
+
90
+ > Approve sensitive file-system actions from the UI.
91
+
92
+ ![RemCodex imported Codex session](docs/assets/imported-session.png)
93
+
94
+ > Bring imported Codex rollouts into the same workspace and keep them easy to review.
95
+
96
+ ## Who It Is For
97
+
98
+ - developers who already use Codex locally
99
+ - people who want a browser-based control surface instead of raw terminal watching
100
+ - teams who want to review or monitor runs from another device on the same network
101
+ - anyone who wants approvals, timeline view, and imported rollout history in one place
102
+
103
+ ## Screens It Aims To Replace
104
+
105
+ - terminal-only session watching
106
+ - ad-hoc mobile remote desktop checks
107
+ - raw log scrolling for approvals and command progress
108
+ - fragmented session history between local and imported rollouts
109
+
110
+ ## Current Product Shape
111
+
112
+ - Single-page workspace UI
113
+ - Left sidebar for session navigation
114
+ - Right-side timeline / execution flow for the active session
115
+ - Fixed composer at the bottom
116
+ - Semantic timeline rendering for:
117
+ - user messages
118
+ - assistant commentary / final messages
119
+ - thinking
120
+ - commands
121
+ - patches
122
+ - approvals
123
+ - system events
124
+
125
+ ## Tech Stack
126
+
127
+ - Backend: Node.js + TypeScript + Express + WebSocket
128
+ - Database: SQLite via `better-sqlite3`
129
+ - Terminal/runtime integration: `node-pty` + Codex app-server
130
+ - Frontend: zero-build static web app (`web/`)
131
+ - Markdown rendering: `markdown-it`
132
+
133
+ ## Requirements
134
+
135
+ Before running this project, you should have:
136
+
137
+ - Node.js installed
138
+ - Codex CLI installed and already working locally
139
+ - A machine where this app can access your local Codex data and working directories
140
+
141
+ This project is currently developed primarily around a local macOS workflow.
142
+
143
+ ## Quick Start
144
+
145
+ For the current developer preview, the recommended local install path is:
146
+
147
+ ```bash
148
+ npm install
149
+ npm run build
150
+ npm link
151
+ remcodex start
152
+ ```
153
+
154
+ Then open:
155
+
156
+ ```text
157
+ http://127.0.0.1:3000
158
+ ```
159
+
160
+ If you want to make it reachable from your phone, expose the local machine on your LAN and open the same URL with your host IP.
161
+
162
+ ## Local CLI
163
+
164
+ RemCodex already ships with a local CLI entrypoint, even though the npm package is not published yet.
165
+
166
+ If you do not want to run `npm link`, you can call the built CLI directly:
167
+
168
+ ```bash
169
+ node dist/server/src/cli.js start --no-open
170
+ ```
171
+
172
+ Useful commands:
173
+
174
+ ```bash
175
+ node dist/server/src/cli.js doctor
176
+ node dist/server/src/cli.js start --no-open
177
+ node dist/server/src/cli.js version
178
+ ```
179
+
180
+ Use a specific database:
181
+
182
+ ```bash
183
+ node dist/server/src/cli.js start --db ~/.remcodex/remcodex-alt.db --no-open
184
+ node dist/server/src/cli.js doctor --db ~/.remcodex/remcodex-alt.db
185
+ ```
186
+
187
+ Planned install target after the npm package is published:
188
+
189
+ ```bash
190
+ npx remcodex
191
+ ```
192
+
193
+ ## Development
194
+
195
+ ```bash
196
+ npm install
197
+ npm run dev
198
+ ```
199
+
200
+ ## How It Works
201
+
202
+ The app uses `codex app-server` as the primary runtime path.
203
+
204
+ At a high level:
205
+
206
+ 1. Codex emits semantic events
207
+ 2. The backend stores them in SQLite
208
+ 3. The frontend reads an aggregated timeline view for initial load
209
+ 4. Live updates continue over WebSocket with catch-up after refresh
210
+
211
+ This gives the UI:
212
+
213
+ - smooth streaming
214
+ - recoverable sessions
215
+ - imported rollout support
216
+ - a consistent execution timeline instead of raw terminal logs
217
+
218
+ ## Key Behaviors
219
+
220
+ ### Approvals
221
+
222
+ - Writes inside the working area usually pass directly
223
+ - Writes outside the working area trigger approval
224
+ - `Allow once` approves only the current request
225
+ - `Allow for this turn` expands writable roots for the active turn
226
+ - Historical approval records stay visible in the timeline
227
+ - Only live approvals stay actionable in the bottom approval bar
228
+
229
+ ### Imported Codex Sessions
230
+
231
+ - Existing Codex rollouts can be imported from local session history
232
+ - Imported sessions keep their own source metadata
233
+ - Imported sessions can continue syncing after you open them
234
+ - Native sessions are excluded from the import picker
235
+
236
+ ### Timeline and Execution Flow
237
+
238
+ - The UI renders semantic timeline items, not raw logs
239
+ - Commands and patches can be grouped into lighter activity summaries
240
+ - Running and failed commands remain visually important
241
+ - The final thinking placeholder appears only at the end of the active flow
242
+
243
+ ## Configuration
244
+
245
+ Supported environment variables:
246
+
247
+ - `PORT`
248
+ - `DATABASE_PATH`
249
+ - `PROJECT_ROOTS`
250
+ - `CODEX_COMMAND`
251
+ - `CODEX_MODE`
252
+ - `REMOTE_HOSTS`
253
+ - `ACTIVE_REMOTE_HOST`
254
+
255
+ Notes:
256
+
257
+ - The default runtime mode is `app-server`
258
+ - `exec-json` is kept only as a fallback compatibility path
259
+ - If `PROJECT_ROOTS` is not set, the app falls back to a broad local browsing root
260
+
261
+ ## Project Structure
262
+
263
+ ```text
264
+ server/
265
+ src/
266
+ app.ts
267
+ controllers/
268
+ db/
269
+ gateways/
270
+ services/
271
+ types/
272
+ utils/
273
+ web/
274
+ index.html
275
+ styles.css
276
+ api.js
277
+ session-ws.js
278
+ app.js
279
+ scripts/
280
+ fix-node-pty-helper.js
281
+ ```
282
+
283
+ ## Main Endpoints
284
+
285
+ - `GET /health`
286
+ - `GET /api/codex/mode`
287
+ - `GET /api/codex/status`
288
+ - `GET /api/codex/quota`
289
+ - `GET /api/sessions`
290
+ - `GET /api/sessions/:sessionId`
291
+ - `GET /api/sessions/:sessionId/timeline`
292
+ - `GET /api/sessions/:sessionId/events`
293
+ - `POST /api/sessions`
294
+ - `POST /api/sessions/:sessionId/messages`
295
+ - `POST /api/sessions/:sessionId/stop`
296
+ - `POST /api/sessions/:sessionId/approvals/:requestId`
297
+ - `WS /ws/sessions/:sessionId`
298
+
299
+ ## What Is Not Finished Yet
300
+
301
+ This is the honest list:
302
+
303
+ - no polished installer yet
304
+ - no desktop packaging yet
305
+ - no full automated test suite yet
306
+ - no production-grade auth / multi-user hardening yet
307
+ - no release pipeline yet
308
+
309
+ If you are comfortable cloning a repo and running a local Node app, you can use it today.
310
+
311
+ ## Roadmap
312
+
313
+ Near-term:
314
+
315
+ - improve onboarding and installation
316
+ - ship a cleaner public README and screenshots
317
+ - add stronger regression coverage
318
+ - harden long-running session recovery
319
+ - continue refining the execution timeline UI
320
+
321
+ Later:
322
+
323
+ - package for easier local install
324
+ - optional sync / multi-device helpers
325
+ - stronger sharing, auditing, and team workflows
326
+
327
+ ## License
328
+
329
+ No license has been added yet.
330
+
331
+ Until a license is added, assume this repository is **source-available for review only**, not open source for reuse.
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolvePackageRoot = resolvePackageRoot;
7
+ exports.resolveDefaultDatabasePath = resolveDefaultDatabasePath;
8
+ exports.startRemCodexServer = startRemCodexServer;
9
+ const node_fs_1 = require("node:fs");
10
+ const node_http_1 = __importDefault(require("node:http"));
11
+ const node_os_1 = require("node:os");
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const express_1 = __importDefault(require("express"));
14
+ const codex_options_controller_1 = require("./controllers/codex-options.controller");
15
+ const message_controller_1 = require("./controllers/message.controller");
16
+ const project_controller_1 = require("./controllers/project.controller");
17
+ const session_controller_1 = require("./controllers/session.controller");
18
+ const client_1 = require("./db/client");
19
+ const migrations_1 = require("./db/migrations");
20
+ const ws_gateway_1 = require("./gateways/ws.gateway");
21
+ const event_store_1 = require("./services/event-store");
22
+ const codex_rollout_sync_1 = require("./services/codex-rollout-sync");
23
+ const project_manager_1 = require("./services/project-manager");
24
+ const session_manager_1 = require("./services/session-manager");
25
+ const session_timeline_service_1 = require("./services/session-timeline-service");
26
+ const command_1 = require("./utils/command");
27
+ const errors_1 = require("./utils/errors");
28
+ function isPackageRoot(root) {
29
+ return ((0, node_fs_1.existsSync)(node_path_1.default.join(root, "package.json")) &&
30
+ (0, node_fs_1.existsSync)(node_path_1.default.join(root, "web", "index.html")));
31
+ }
32
+ function resolvePackageRoot(startDir = __dirname) {
33
+ let current = node_path_1.default.resolve(startDir);
34
+ while (true) {
35
+ if (isPackageRoot(current)) {
36
+ return current;
37
+ }
38
+ const parent = node_path_1.default.dirname(current);
39
+ if (parent === current) {
40
+ break;
41
+ }
42
+ current = parent;
43
+ }
44
+ return process.cwd();
45
+ }
46
+ function resolveDefaultDatabasePath() {
47
+ return node_path_1.default.join((0, node_os_1.homedir)(), ".remcodex", "remcodex.db");
48
+ }
49
+ function buildRemCodexServer(options = {}) {
50
+ const repoRoot = options.repoRoot ? node_path_1.default.resolve(options.repoRoot) : resolvePackageRoot();
51
+ const port = options.port ?? Number.parseInt(process.env.PORT ?? "3000", 10);
52
+ const databasePath = options.databasePath ??
53
+ process.env.DATABASE_PATH ??
54
+ resolveDefaultDatabasePath();
55
+ const codexCommand = (0, command_1.resolveExecutable)(options.codexCommand ?? process.env.CODEX_COMMAND ?? "codex");
56
+ const codexMode = options.codexMode ?? (process.env.CODEX_MODE === "exec-json" ? "exec-json" : "app-server");
57
+ const projectRootsEnv = options.projectRootsEnv ?? process.env.PROJECT_ROOTS;
58
+ (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(databasePath), { recursive: true });
59
+ const db = (0, client_1.createDatabase)(databasePath);
60
+ (0, migrations_1.runMigrations)(db);
61
+ const eventStore = new event_store_1.EventStore(db);
62
+ const sessionTimeline = new session_timeline_service_1.SessionTimelineService(eventStore);
63
+ const projectManager = new project_manager_1.ProjectManager(db, projectRootsEnv, repoRoot);
64
+ const codexRolloutSync = new codex_rollout_sync_1.CodexRolloutSyncService(db);
65
+ const sessionManager = new session_manager_1.SessionManager({
66
+ db,
67
+ eventStore,
68
+ projectManager,
69
+ codexCommand,
70
+ codexMode,
71
+ });
72
+ const app = (0, express_1.default)();
73
+ const server = node_http_1.default.createServer(app);
74
+ app.use(express_1.default.json({ limit: "1mb" }));
75
+ app.get("/health", (_request, response) => {
76
+ response.json({
77
+ ok: true,
78
+ codexMode,
79
+ codexCommand,
80
+ projectRoots: projectManager.listAllowedRoots(),
81
+ now: new Date().toISOString(),
82
+ });
83
+ });
84
+ app.use("/api/projects", (0, project_controller_1.createProjectRouter)(projectManager));
85
+ app.use("/api/codex", (0, codex_options_controller_1.createCodexOptionsRouter)({
86
+ sessionManager,
87
+ projectManager,
88
+ eventStore,
89
+ codexMode,
90
+ codexRolloutSync,
91
+ }));
92
+ app.use("/api/sessions", (0, session_controller_1.createSessionRouter)(sessionManager, eventStore, projectManager, codexRolloutSync, sessionTimeline));
93
+ app.use("/api/sessions/:sessionId/messages", (0, message_controller_1.createMessageRouter)(sessionManager));
94
+ const webRoot = node_path_1.default.join(repoRoot, "web");
95
+ app.use(express_1.default.static(webRoot));
96
+ app.get("/", (_request, response) => {
97
+ response.sendFile(node_path_1.default.join(webRoot, "index.html"));
98
+ });
99
+ app.use((error, _request, response, _next) => {
100
+ if ((0, errors_1.isAppError)(error)) {
101
+ response.status(error.statusCode).json({ error: error.message });
102
+ return;
103
+ }
104
+ const message = error instanceof Error ? error.message : "Internal server error";
105
+ response.status(500).json({
106
+ error: message,
107
+ });
108
+ });
109
+ (0, ws_gateway_1.registerSessionGateway)(server, {
110
+ eventStore,
111
+ sessionManager,
112
+ });
113
+ return {
114
+ app,
115
+ server,
116
+ closeDatabase: () => {
117
+ const closable = db;
118
+ closable.close?.();
119
+ },
120
+ port,
121
+ repoRoot,
122
+ databasePath,
123
+ codexCommand,
124
+ codexMode,
125
+ projectRoots: projectManager.listAllowedRoots(),
126
+ logStartup: options.logStartup ?? true,
127
+ };
128
+ }
129
+ async function startRemCodexServer(options = {}) {
130
+ const built = buildRemCodexServer(options);
131
+ await new Promise((resolve, reject) => {
132
+ const handleError = (error) => {
133
+ built.server.off("listening", handleListening);
134
+ reject(error);
135
+ };
136
+ const handleListening = () => {
137
+ built.server.off("error", handleError);
138
+ resolve();
139
+ };
140
+ built.server.once("error", handleError);
141
+ built.server.once("listening", handleListening);
142
+ built.server.listen(built.port);
143
+ });
144
+ if (built.logStartup) {
145
+ console.log(JSON.stringify({
146
+ message: "RemCodex listening",
147
+ port: built.port,
148
+ codexMode: built.codexMode,
149
+ databasePath: built.databasePath,
150
+ codexCommand: built.codexCommand,
151
+ }));
152
+ }
153
+ return {
154
+ app: built.app,
155
+ server: built.server,
156
+ port: built.port,
157
+ repoRoot: built.repoRoot,
158
+ databasePath: built.databasePath,
159
+ codexCommand: built.codexCommand,
160
+ codexMode: built.codexMode,
161
+ projectRoots: built.projectRoots,
162
+ stop: () => new Promise((resolve, reject) => {
163
+ built.server.close((error) => {
164
+ if (error) {
165
+ reject(error);
166
+ return;
167
+ }
168
+ built.closeDatabase();
169
+ resolve();
170
+ });
171
+ }),
172
+ };
173
+ }
174
+ async function main() {
175
+ await startRemCodexServer();
176
+ }
177
+ if (require.main === module) {
178
+ void main().catch((error) => {
179
+ const message = error instanceof Error ? error.message : String(error);
180
+ console.error(JSON.stringify({
181
+ message: "Failed to start RemCodex",
182
+ error: message,
183
+ }));
184
+ process.exitCode = 1;
185
+ });
186
+ }