@excitedjs/dreamux 0.1.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/bin/dreamux +31 -0
  4. package/bin/server +35 -0
  5. package/bin/server-ctl +28 -0
  6. package/db/migrations/0001_init.sql +49 -0
  7. package/dist/admin/methods.js +103 -0
  8. package/dist/admin/methods.js.map +1 -0
  9. package/dist/admin/protocol.js +15 -0
  10. package/dist/admin/protocol.js.map +1 -0
  11. package/dist/admin/socket.js +251 -0
  12. package/dist/admin/socket.js.map +1 -0
  13. package/dist/cli/dreamux.js +105 -0
  14. package/dist/cli/dreamux.js.map +1 -0
  15. package/dist/cli/server-ctl.js +172 -0
  16. package/dist/cli/server-ctl.js.map +1 -0
  17. package/dist/cli/server.js +88 -0
  18. package/dist/cli/server.js.map +1 -0
  19. package/dist/codex/events.js +82 -0
  20. package/dist/codex/events.js.map +1 -0
  21. package/dist/codex/handshake.js +85 -0
  22. package/dist/codex/handshake.js.map +1 -0
  23. package/dist/codex/rpc.js +200 -0
  24. package/dist/codex/rpc.js.map +1 -0
  25. package/dist/codex/supervisor.js +184 -0
  26. package/dist/codex/supervisor.js.map +1 -0
  27. package/dist/codex/types.js +10 -0
  28. package/dist/codex/types.js.map +1 -0
  29. package/dist/db/repository.js +207 -0
  30. package/dist/db/repository.js.map +1 -0
  31. package/dist/db/schema.js +29 -0
  32. package/dist/db/schema.js.map +1 -0
  33. package/dist/db/types.js +2 -0
  34. package/dist/db/types.js.map +1 -0
  35. package/dist/dispatcher/approval.js +43 -0
  36. package/dist/dispatcher/approval.js.map +1 -0
  37. package/dist/dispatcher/runtime.js +262 -0
  38. package/dist/dispatcher/runtime.js.map +1 -0
  39. package/dist/dispatcher/turn-manager.js +167 -0
  40. package/dist/dispatcher/turn-manager.js.map +1 -0
  41. package/dist/feishu/bot.js +137 -0
  42. package/dist/feishu/bot.js.map +1 -0
  43. package/dist/feishu/content.js +108 -0
  44. package/dist/feishu/content.js.map +1 -0
  45. package/dist/feishu/render.js +600 -0
  46. package/dist/feishu/render.js.map +1 -0
  47. package/dist/feishu/types.js +9 -0
  48. package/dist/feishu/types.js.map +1 -0
  49. package/dist/runtime/codex-args.js +92 -0
  50. package/dist/runtime/codex-args.js.map +1 -0
  51. package/dist/runtime/config.js +351 -0
  52. package/dist/runtime/config.js.map +1 -0
  53. package/dist/runtime/paths.js +77 -0
  54. package/dist/runtime/paths.js.map +1 -0
  55. package/dist/runtime/secrets.js +18 -0
  56. package/dist/runtime/secrets.js.map +1 -0
  57. package/dist/server.js +234 -0
  58. package/dist/server.js.map +1 -0
  59. package/package.json +43 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * The admin Unix-socket server.
3
+ *
4
+ * One client gets one line-delimited NDJSON stream of requests; we reply
5
+ * with one line per request. Permissions on the socket are 0600 to keep
6
+ * other local users out (issue #2 §"管理接口").
7
+ */
8
+ import { createServer } from 'node:net';
9
+ import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
10
+ import { AdminError, } from './protocol.js';
11
+ import { adminMethods } from './methods.js';
12
+ /**
13
+ * Max attempts to reclaim a stale pidfile before yielding to a competitor.
14
+ * Mirrors claudemux's instance-lock policy.
15
+ */
16
+ const RECLAIM_ATTEMPTS = 3;
17
+ export function createAdminSocketServer(server, socketPath, options = {}) {
18
+ const chmod = options.chmodFn ?? chmodSync;
19
+ const isAlive = options.isPidAlive ?? defaultIsPidAlive;
20
+ const myPid = options.selfPid ?? process.pid;
21
+ const lockPath = `${socketPath}.lock`;
22
+ let netServer = null;
23
+ let holdLock = false;
24
+ return {
25
+ socketPath,
26
+ async start() {
27
+ // PR #3 review #3 (r2): the previous probe-then-unlink had a TOCTOU
28
+ // window — two competing startups could both observe a stale socket
29
+ // as not-live, then one bind successfully and the other unlink it
30
+ // out from under the first. We resolve this by gating *every* path
31
+ // (probe, cleanup, bind) behind a pidfile that's created with the
32
+ // exclusive `wx` flag — atomic at the filesystem level. Once we
33
+ // hold it, nobody else can be inside this start() concurrently.
34
+ // Stale pidfiles (dead holder) are reclaimed up to RECLAIM_ATTEMPTS
35
+ // times; a live holder always loses the race.
36
+ acquirePidLock(lockPath, myPid, isAlive);
37
+ holdLock = true;
38
+ try {
39
+ // Lock is held — stale socket cleanup is now race-free.
40
+ if (existsSync(socketPath)) {
41
+ rmSync(socketPath, { force: true });
42
+ }
43
+ netServer = createServer((sock) => handleConnection(server, sock));
44
+ await new Promise((res, rej) => {
45
+ netServer.once('error', rej);
46
+ netServer.listen(socketPath, () => res());
47
+ });
48
+ // PR #3 review #2: chmod is a hard requirement, not best-effort —
49
+ // a 0666 admin socket exposes server-ctl methods to every local user.
50
+ try {
51
+ chmod(socketPath, 0o600);
52
+ }
53
+ catch (e) {
54
+ const chmodErr = e instanceof Error ? e.message : String(e);
55
+ throw new Error(`admin socket ${socketPath} could not be locked down to 0600 (${chmodErr}); refusing to expose it on a permissive mode`);
56
+ }
57
+ }
58
+ catch (err) {
59
+ // Unwind whatever partial state we set up — bound server, socket
60
+ // file, and the pidfile lock — so a retry doesn't trip over our
61
+ // own leftovers.
62
+ if (netServer !== null) {
63
+ const closing = netServer;
64
+ netServer = null;
65
+ await new Promise((res) => closing.close(() => res()));
66
+ }
67
+ try {
68
+ rmSync(socketPath, { force: true });
69
+ }
70
+ catch {
71
+ /* best-effort */
72
+ }
73
+ releasePidLock(lockPath, myPid);
74
+ holdLock = false;
75
+ throw err;
76
+ }
77
+ },
78
+ async close() {
79
+ if (netServer !== null) {
80
+ await new Promise((res) => netServer.close(() => res()));
81
+ netServer = null;
82
+ try {
83
+ rmSync(socketPath, { force: true });
84
+ }
85
+ catch {
86
+ /* */
87
+ }
88
+ }
89
+ if (holdLock) {
90
+ releasePidLock(lockPath, myPid);
91
+ holdLock = false;
92
+ }
93
+ },
94
+ };
95
+ }
96
+ /**
97
+ * Acquire the single-instance pidfile lock.
98
+ *
99
+ * Atomic `wx` create races safely: two competing startups both attempt the
100
+ * same call; one wins, one gets EEXIST. The loser then reads the holder's
101
+ * PID and decides:
102
+ * - alive holder → throw (split-brain prevention)
103
+ * - dead holder → remove the stale file and retry the `wx` create
104
+ *
105
+ * RECLAIM_ATTEMPTS bounds the retry so a pathologically broken filesystem
106
+ * doesn't spin forever.
107
+ */
108
+ function acquirePidLock(lockPath, myPid, isAlive) {
109
+ for (let attempt = 0; attempt < RECLAIM_ATTEMPTS; attempt++) {
110
+ try {
111
+ writeFileSync(lockPath, `${myPid}\n`, { flag: 'wx', mode: 0o600 });
112
+ return;
113
+ }
114
+ catch (err) {
115
+ if (err.code !== 'EEXIST')
116
+ throw err;
117
+ }
118
+ const holder = readPidFile(lockPath);
119
+ if (holder === myPid) {
120
+ // Re-entrant — shouldn't happen in normal use, but treat as held.
121
+ return;
122
+ }
123
+ if (holder !== null && isAlive(holder)) {
124
+ throw new Error(`admin socket lockfile ${lockPath} is held by another live dreamux-server process (pid ${holder}). ` +
125
+ 'Refusing to bind to avoid split-brain admin control. ' +
126
+ 'Stop the other instance, or set CODEX_HOST_ADMIN_SOCKET to a different path.');
127
+ }
128
+ // Stale lock (unreadable PID, or PID belongs to a dead process).
129
+ // Remove and retry the exclusive create. A competitor reclaiming the
130
+ // same stale file simply wins this round of `wx`, and we'll see *their*
131
+ // live PID on the next iteration and bail out.
132
+ try {
133
+ rmSync(lockPath, { force: true });
134
+ }
135
+ catch {
136
+ /* concurrent reclaim — retry the wx open */
137
+ }
138
+ }
139
+ throw new Error(`admin socket lockfile ${lockPath} could not be acquired after ${RECLAIM_ATTEMPTS} reclaim attempts; ` +
140
+ 'a competitor is racing us. Retry, or set CODEX_HOST_ADMIN_SOCKET to a different path.');
141
+ }
142
+ /**
143
+ * Release the pidfile lock — but only if it still names us. A holder whose
144
+ * file was already reclaimed by a competitor (e.g. we were paused long
145
+ * enough for our PID to look dead) must not delete the new holder's lock.
146
+ */
147
+ function releasePidLock(lockPath, myPid) {
148
+ if (readPidFile(lockPath) !== myPid)
149
+ return;
150
+ try {
151
+ rmSync(lockPath, { force: true });
152
+ }
153
+ catch {
154
+ /* best-effort */
155
+ }
156
+ }
157
+ function readPidFile(path) {
158
+ let txt;
159
+ try {
160
+ txt = readFileSync(path, 'utf8').trim();
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ if (txt === '')
166
+ return null;
167
+ const n = Number.parseInt(txt, 10);
168
+ return Number.isInteger(n) && n > 0 ? n : null;
169
+ }
170
+ function defaultIsPidAlive(pid) {
171
+ if (!Number.isInteger(pid) || pid <= 0)
172
+ return false;
173
+ try {
174
+ process.kill(pid, 0);
175
+ return true;
176
+ }
177
+ catch (e) {
178
+ // EPERM means the process exists but we can't signal it (still alive).
179
+ return e.code === 'EPERM';
180
+ }
181
+ }
182
+ function handleConnection(server, sock) {
183
+ let buf = '';
184
+ sock.setEncoding('utf8');
185
+ sock.on('data', (chunk) => {
186
+ buf += chunk;
187
+ let idx;
188
+ while ((idx = buf.indexOf('\n')) !== -1) {
189
+ const line = buf.slice(0, idx).trim();
190
+ buf = buf.slice(idx + 1);
191
+ if (line === '')
192
+ continue;
193
+ void processLine(server, sock, line);
194
+ }
195
+ });
196
+ sock.on('error', () => {
197
+ /* client closed unexpectedly — nothing more to do */
198
+ });
199
+ }
200
+ async function processLine(server, sock, line) {
201
+ let req;
202
+ try {
203
+ req = JSON.parse(line);
204
+ if (typeof req !== 'object' || req === null || typeof req.id !== 'string') {
205
+ throw new Error('bad request envelope');
206
+ }
207
+ }
208
+ catch (err) {
209
+ const msg = err instanceof Error ? err.message : String(err);
210
+ write(sock, { id: '?', ok: false, error: { code: 'BAD_REQUEST', message: msg } });
211
+ return;
212
+ }
213
+ const handler = adminMethods[req.method];
214
+ if (handler === undefined) {
215
+ write(sock, {
216
+ id: req.id,
217
+ ok: false,
218
+ error: { code: 'UNKNOWN_METHOD', message: `unknown method '${req.method}'` },
219
+ });
220
+ return;
221
+ }
222
+ try {
223
+ const result = await handler(server, req.params);
224
+ write(sock, { id: req.id, ok: true, result });
225
+ }
226
+ catch (err) {
227
+ if (err instanceof AdminError) {
228
+ write(sock, {
229
+ id: req.id,
230
+ ok: false,
231
+ error: { code: err.code, message: err.message },
232
+ });
233
+ return;
234
+ }
235
+ const msg = err instanceof Error ? err.message : String(err);
236
+ write(sock, {
237
+ id: req.id,
238
+ ok: false,
239
+ error: { code: 'INTERNAL', message: msg },
240
+ });
241
+ }
242
+ }
243
+ function write(sock, response) {
244
+ try {
245
+ sock.write(`${JSON.stringify(response)}\n`);
246
+ }
247
+ catch {
248
+ /* client gone */
249
+ }
250
+ }
251
+ //# sourceMappingURL=socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/admin/socket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `dreamux` — the unified top-level CLI (issue #4).
3
+ *
4
+ * Subcommands:
5
+ * dreamux server start # start the long-running server
6
+ * dreamux server status # admin-socket query
7
+ * dreamux dispatcher add ... # configure a dispatcher
8
+ * dreamux dispatcher remove --id X
9
+ * dreamux dispatcher list
10
+ * dreamux dispatcher status --id X
11
+ * dreamux dispatcher start --id X
12
+ * dreamux dispatcher stop --id X
13
+ *
14
+ * Implementation note: this binary is a thin router. `server start` delegates
15
+ * to the same entrypoint as the legacy `dreamux-server` (`./server.js`);
16
+ * everything else is forwarded to the legacy `server-ctl` flow
17
+ * (`./server-ctl.js`). Both legacy binaries are kept as aliases for users
18
+ * with stale PATH entries (PR #6 shipped them; reverting would break local
19
+ * setups).
20
+ */
21
+ import { spawn } from 'node:child_process';
22
+ import { dirname, join } from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+ const HERE = dirname(fileURLToPath(import.meta.url));
25
+ const SERVER_ENTRY = join(HERE, 'server.js');
26
+ const SERVER_CTL_ENTRY = join(HERE, 'server-ctl.js');
27
+ function printRootHelp() {
28
+ console.log(`dreamux — Codex-host MVP unified CLI (excitedjs/dreamux#4)
29
+
30
+ Usage:
31
+ dreamux server start
32
+ dreamux server status
33
+ dreamux dispatcher list
34
+ dreamux dispatcher add --id <ID> --bot-app-id <APP_ID> \\
35
+ --bot-secret-ref env:<VAR> [--codex-args-json <JSON>] [--codex-cwd <PATH>]
36
+ dreamux dispatcher status --id <ID>
37
+ dreamux dispatcher start --id <ID>
38
+ dreamux dispatcher stop --id <ID>
39
+ dreamux dispatcher remove --id <ID>
40
+
41
+ Environment:
42
+ CODEX_HOST_RUNTIME_DIR Root dir (default: ~/.codex-host)
43
+ CODEX_HOST_ADMIN_SOCKET Admin Unix socket path
44
+ CODEX_HOST_CODEX_BIN Codex binary (default: 'codex' on PATH)
45
+ BOT_SECRET_<NAME> Each dispatcher's bot secret (referenced via
46
+ bot_secret_ref=env:BOT_SECRET_<NAME>)
47
+
48
+ The legacy 'dreamux-server' and 'server-ctl' binaries remain available as
49
+ aliases for compatibility; new tooling should call 'dreamux'.
50
+ `);
51
+ }
52
+ function fail(msg, code = 2) {
53
+ console.error(`dreamux: ${msg}\n`);
54
+ printRootHelp();
55
+ process.exit(code);
56
+ }
57
+ async function execEntry(entry, argv) {
58
+ // Re-exec node on the target so each subcommand keeps its own argv / process
59
+ // environment; no shared state to leak between subcommands.
60
+ const child = spawn(process.execPath, [entry, ...argv], {
61
+ stdio: 'inherit',
62
+ });
63
+ await new Promise((res, rej) => {
64
+ child.once('error', rej);
65
+ child.once('exit', (code, signal) => {
66
+ if (signal !== null)
67
+ process.kill(process.pid, signal);
68
+ process.exit(code ?? 0);
69
+ res();
70
+ });
71
+ });
72
+ process.exit(0); // unreachable; satisfies TS never
73
+ }
74
+ async function main() {
75
+ const argv = process.argv.slice(2);
76
+ if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
77
+ printRootHelp();
78
+ return;
79
+ }
80
+ const [topic, sub, ...rest] = argv;
81
+ if (topic === 'server') {
82
+ if (sub === 'start') {
83
+ await execEntry(SERVER_ENTRY, rest);
84
+ return;
85
+ }
86
+ if (sub === 'status') {
87
+ // server status is an admin-socket query (server-ctl `server status`).
88
+ await execEntry(SERVER_CTL_ENTRY, ['server', 'status', ...rest]);
89
+ return;
90
+ }
91
+ fail(`unknown 'server' subcommand: ${sub ?? '(missing)'}`);
92
+ }
93
+ if (topic === 'dispatcher') {
94
+ if (sub === undefined)
95
+ fail("missing 'dispatcher' subcommand");
96
+ await execEntry(SERVER_CTL_ENTRY, ['dispatcher', sub, ...rest]);
97
+ return;
98
+ }
99
+ fail(`unknown command: ${topic ?? ''}`);
100
+ }
101
+ main().catch((err) => {
102
+ console.error(`dreamux: ${err instanceof Error ? err.message : err}`);
103
+ process.exit(1);
104
+ });
105
+ //# sourceMappingURL=dreamux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dreamux.js","sourceRoot":"","sources":["../../src/cli/dreamux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * `server-ctl` — admin CLI that talks to the server via Unix socket.
3
+ *
4
+ * Connects to the admin socket (CODEX_HOST_ADMIN_SOCKET or default), sends a
5
+ * single NDJSON request, prints the response, exits.
6
+ *
7
+ * Usage:
8
+ * server-ctl server status
9
+ * server-ctl dispatcher list
10
+ * server-ctl dispatcher add --id flow --bot-app-id cli_aaa --bot-secret-ref env:BOT_SECRET_FLOW
11
+ * server-ctl dispatcher status --id flow
12
+ * server-ctl dispatcher start --id flow
13
+ * server-ctl dispatcher stop --id flow
14
+ * server-ctl dispatcher remove --id flow
15
+ */
16
+ import { connect } from 'node:net';
17
+ import { adminSocketPath } from '../runtime/paths.js';
18
+ function parseArgs(argv) {
19
+ const flags = {};
20
+ const positional = [];
21
+ for (let i = 0; i < argv.length; i++) {
22
+ const a = argv[i];
23
+ if (a.startsWith('--')) {
24
+ const key = a.slice(2);
25
+ const next = argv[i + 1];
26
+ if (next === undefined || next.startsWith('--')) {
27
+ flags[key] = 'true';
28
+ }
29
+ else {
30
+ flags[key] = next;
31
+ i++;
32
+ }
33
+ }
34
+ else {
35
+ positional.push(a);
36
+ }
37
+ }
38
+ return { flags, positional };
39
+ }
40
+ async function main() {
41
+ const argv = process.argv.slice(2);
42
+ if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
43
+ printHelp();
44
+ return;
45
+ }
46
+ const { flags, positional } = parseArgs(argv);
47
+ const [obj, verb] = positional;
48
+ const method = resolveMethod(obj, verb);
49
+ if (method === null) {
50
+ console.error(`unknown command: ${obj ?? ''} ${verb ?? ''}\n`);
51
+ printHelp();
52
+ process.exit(2);
53
+ }
54
+ const params = flagsToParams(method, flags);
55
+ const request = { id: cryptoRandomId(), method, params };
56
+ const response = await sendOne(adminSocketPath(), request);
57
+ if (response.ok) {
58
+ console.log(JSON.stringify(response.result, null, 2));
59
+ }
60
+ else {
61
+ console.error(`error: [${response.error.code}] ${response.error.message}`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ function resolveMethod(obj, verb) {
66
+ const o = obj ?? '';
67
+ const v = verb ?? '';
68
+ if (o === 'server' && v === 'status')
69
+ return 'server.status';
70
+ if (o === 'dispatcher') {
71
+ switch (v) {
72
+ case 'add': return 'dispatcher.add';
73
+ case 'remove': return 'dispatcher.remove';
74
+ case 'list': return 'dispatcher.list';
75
+ case 'status': return 'dispatcher.status';
76
+ case 'start': return 'dispatcher.start';
77
+ case 'stop': return 'dispatcher.stop';
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ const FLAG_TO_PARAM = {
83
+ id: 'dispatcher_id',
84
+ 'bot-app-id': 'bot_app_id',
85
+ 'bot-secret-ref': 'bot_secret_ref',
86
+ 'codex-args-json': 'codex_args_json',
87
+ 'codex-cwd': 'codex_cwd',
88
+ };
89
+ function flagsToParams(_method, flags) {
90
+ const params = {};
91
+ for (const [k, v] of Object.entries(flags)) {
92
+ const key = FLAG_TO_PARAM[k] ?? k.replace(/-/g, '_');
93
+ params[key] = v;
94
+ }
95
+ return params;
96
+ }
97
+ function sendOne(socketPath, request) {
98
+ return new Promise((resolve, reject) => {
99
+ let buf = '';
100
+ let settled = false;
101
+ let sock;
102
+ try {
103
+ sock = connect(socketPath);
104
+ }
105
+ catch (err) {
106
+ reject(err);
107
+ return;
108
+ }
109
+ sock.setEncoding('utf8');
110
+ sock.on('connect', () => {
111
+ sock.write(`${JSON.stringify(request)}\n`);
112
+ });
113
+ sock.on('data', (chunk) => {
114
+ buf += chunk;
115
+ const nl = buf.indexOf('\n');
116
+ if (nl !== -1 && !settled) {
117
+ settled = true;
118
+ const line = buf.slice(0, nl).trim();
119
+ try {
120
+ resolve(JSON.parse(line));
121
+ }
122
+ catch (err) {
123
+ reject(err);
124
+ }
125
+ sock.end();
126
+ }
127
+ });
128
+ sock.on('error', (err) => {
129
+ if (settled)
130
+ return;
131
+ settled = true;
132
+ const code = err.code;
133
+ if (code === 'ENOENT' || code === 'ECONNREFUSED') {
134
+ reject(new Error(`cannot reach admin socket at ${socketPath} — is the server running?`));
135
+ }
136
+ else {
137
+ reject(err);
138
+ }
139
+ });
140
+ sock.on('close', () => {
141
+ if (settled)
142
+ return;
143
+ settled = true;
144
+ reject(new Error('admin socket closed without a response'));
145
+ });
146
+ });
147
+ }
148
+ function cryptoRandomId() {
149
+ return `cli-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
150
+ }
151
+ function printHelp() {
152
+ console.log(`server-ctl — admin CLI for the dreamux server
153
+
154
+ Usage:
155
+ server-ctl server status
156
+ server-ctl dispatcher list
157
+ server-ctl dispatcher add --id <ID> --bot-app-id <APP_ID> \\
158
+ --bot-secret-ref env:<VAR> [--codex-args-json <JSON>] [--codex-cwd <PATH>]
159
+ server-ctl dispatcher status --id <ID>
160
+ server-ctl dispatcher start --id <ID>
161
+ server-ctl dispatcher stop --id <ID>
162
+ server-ctl dispatcher remove --id <ID>
163
+
164
+ Environment:
165
+ CODEX_HOST_ADMIN_SOCKET override the admin socket path (default: ~/.codex-host/admin.sock)
166
+ `);
167
+ }
168
+ main().catch((err) => {
169
+ console.error(`server-ctl: ${err instanceof Error ? err.message : err}`);
170
+ process.exit(1);
171
+ });
172
+ //# sourceMappingURL=server-ctl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-ctl.js","sourceRoot":"","sources":["../../src/cli/server-ctl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `dreamux-server` — the long-running server entry point.
3
+ *
4
+ * Usage:
5
+ * dreamux-server # run in foreground; logs to stderr
6
+ * dreamux-server --help
7
+ *
8
+ * Configuration sources (highest precedence first):
9
+ * 1. environment variables (CODEX_HOST_RUNTIME_DIR, CODEX_HOST_ADMIN_SOCKET,
10
+ * CODEX_HOST_CODEX_BIN) — escape hatch for CI / one-off debug runs
11
+ * 2. per-dispatcher fields in SQLite (codex_args_json: approvalPolicy, extraArgs)
12
+ * 3. ~/.dreamux/config.toml — user-editable global defaults; auto-created
13
+ * with sensible defaults on first boot (see src/runtime/config.ts)
14
+ * 4. built-in defaults compiled into the binary
15
+ *
16
+ * Per-dispatcher secrets stay in env (bot_secret_ref=env:VAR_NAME); they
17
+ * deliberately do not flow through the config file (issue #2 Q9).
18
+ */
19
+ import { mkdirSync } from 'node:fs';
20
+ import { dirname } from 'node:path';
21
+ import { Server } from '../server.js';
22
+ import { loadOrInitConfig } from '../runtime/config.js';
23
+ import { adminSocketPath, databasePath, runtimeRoot } from '../runtime/paths.js';
24
+ async function main() {
25
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
26
+ printHelp();
27
+ return;
28
+ }
29
+ // Load (or create on first boot) ~/.dreamux/config.toml *before* anything
30
+ // else looks at runtime paths — paths.* consults the active config for
31
+ // its non-env defaults. A parse error here fails-fast with a file:line
32
+ // pointer; the operator fixes the file and restarts.
33
+ const { config, configFile, createdOnThisBoot } = loadOrInitConfig();
34
+ if (createdOnThisBoot) {
35
+ console.error(`[server] created ${configFile} with default settings — edit and restart to change`);
36
+ }
37
+ else {
38
+ console.error(`[server] loaded global config from ${configFile}`);
39
+ }
40
+ mkdirSync(runtimeRoot(), { recursive: true });
41
+ mkdirSync(dirname(databasePath()), { recursive: true });
42
+ const server = new Server({ config });
43
+ await server.start();
44
+ console.error(`[server] up; admin socket: ${adminSocketPath()}`);
45
+ const shutdown = async (signal) => {
46
+ console.error(`[server] received ${signal}`);
47
+ await server.shutdown();
48
+ process.exit(0);
49
+ };
50
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
51
+ process.on('SIGINT', () => void shutdown('SIGINT'));
52
+ }
53
+ function printHelp() {
54
+ console.log(`dreamux-server — Codex-host MVP server (excitedjs/dreamux#2)
55
+
56
+ Usage:
57
+ dreamux-server [--help]
58
+
59
+ Global config:
60
+ ~/.dreamux/config.toml Auto-created on first boot. Override with the
61
+ DREAMUX_CONFIG_DIR env var. Edit and restart to
62
+ apply. Holds defaults for codex.bin,
63
+ approval_policy, runtime_dir, outbound retries,
64
+ etc. See the file's own comments for keys.
65
+
66
+ Runtime data (kept separate from config):
67
+ ~/.codex-host/ SQLite, admin socket, per-dispatcher logs.
68
+ Override via 'runtime_dir' in config, or
69
+ CODEX_HOST_RUNTIME_DIR env (env wins).
70
+
71
+ Environment overrides (highest precedence):
72
+ CODEX_HOST_RUNTIME_DIR Overrides config.runtime_dir
73
+ CODEX_HOST_ADMIN_SOCKET Overrides config.admin_socket
74
+ CODEX_HOST_CODEX_BIN Overrides config.codex.bin
75
+ DREAMUX_CONFIG_DIR Overrides ~/.dreamux (where config.toml lives)
76
+ BOT_SECRET_<NAME> Each dispatcher's bot secret (referenced via
77
+ bot_secret_ref=env:BOT_SECRET_<NAME>)
78
+
79
+ Add dispatchers via server-ctl:
80
+ dreamux dispatcher add --id flow --bot-app-id cli_aaa \\
81
+ --bot-secret-ref env:BOT_SECRET_FLOW
82
+ `);
83
+ }
84
+ main().catch((err) => {
85
+ console.error('[server] fatal:', err);
86
+ process.exit(1);
87
+ });
88
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/cli/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEjF,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,0EAA0E;IAC1E,uEAAuE;IACvE,uEAAuE;IACvE,qDAAqD;IACrD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACrE,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CACX,oBAAoB,UAAU,qDAAqD,CACpF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,sCAAsC,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,SAAS,CAAC,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,OAAO,CAAC,KAAK,CAAC,8BAA8B,eAAe,EAAE,EAAE,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;QACvD,OAAO,CAAC,KAAK,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Bb,CAAC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}