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.
- package/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/server/src/app.js +186 -0
- package/dist/server/src/cli.js +270 -0
- package/dist/server/src/controllers/codex-options.controller.js +199 -0
- package/dist/server/src/controllers/message.controller.js +21 -0
- package/dist/server/src/controllers/project.controller.js +44 -0
- package/dist/server/src/controllers/session.controller.js +175 -0
- package/dist/server/src/db/client.js +10 -0
- package/dist/server/src/db/migrations.js +32 -0
- package/dist/server/src/gateways/ws.gateway.js +60 -0
- package/dist/server/src/services/codex-app-server-runner.js +363 -0
- package/dist/server/src/services/codex-exec-runner.js +147 -0
- package/dist/server/src/services/codex-rollout-sync.js +977 -0
- package/dist/server/src/services/codex-runner.js +11 -0
- package/dist/server/src/services/codex-stream-events.js +478 -0
- package/dist/server/src/services/event-store.js +328 -0
- package/dist/server/src/services/project-manager.js +130 -0
- package/dist/server/src/services/pty-runner.js +72 -0
- package/dist/server/src/services/session-manager.js +1586 -0
- package/dist/server/src/services/session-timeline-service.js +181 -0
- package/dist/server/src/types/codex-launch.js +2 -0
- package/dist/server/src/types/models.js +37 -0
- package/dist/server/src/utils/ansi.js +143 -0
- package/dist/server/src/utils/codex-launch.js +102 -0
- package/dist/server/src/utils/codex-quota.js +179 -0
- package/dist/server/src/utils/codex-status.js +163 -0
- package/dist/server/src/utils/codex-ui-options.js +114 -0
- package/dist/server/src/utils/command.js +46 -0
- package/dist/server/src/utils/errors.js +16 -0
- package/dist/server/src/utils/ids.js +7 -0
- package/dist/server/src/utils/node-pty.js +29 -0
- package/package.json +36 -0
- package/scripts/fix-node-pty-helper.js +36 -0
- package/web/api.js +175 -0
- package/web/app.js +8082 -0
- package/web/components/composer.js +627 -0
- package/web/components/session-workbench.js +173 -0
- package/web/i18n/index.js +171 -0
- package/web/i18n/locales/de.js +50 -0
- package/web/i18n/locales/en.js +320 -0
- package/web/i18n/locales/es.js +50 -0
- package/web/i18n/locales/fr.js +50 -0
- package/web/i18n/locales/ja.js +50 -0
- package/web/i18n/locales/ko.js +50 -0
- package/web/i18n/locales/pt-BR.js +50 -0
- package/web/i18n/locales/ru.js +50 -0
- package/web/i18n/locales/zh-CN.js +320 -0
- package/web/i18n/locales/zh-Hant.js +53 -0
- package/web/index.html +23 -0
- package/web/message-rich-text.js +218 -0
- package/web/session-command-activity.js +980 -0
- package/web/session-event-adapter.js +826 -0
- package/web/session-timeline-reducer.js +728 -0
- package/web/session-timeline-renderer.js +656 -0
- package/web/session-ws.js +31 -0
- package/web/styles.css +5665 -0
- 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
|
+

|
|
81
|
+
|
|
82
|
+
> Run and review Codex sessions in a single-page workspace.
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
> Follow a live Codex session from your phone.
|
|
87
|
+
|
|
88
|
+

|
|
89
|
+
|
|
90
|
+
> Approve sensitive file-system actions from the UI.
|
|
91
|
+
|
|
92
|
+

|
|
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
|
+
}
|