@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.
- package/LICENSE +21 -0
- package/README.md +223 -0
- package/bin/dreamux +31 -0
- package/bin/server +35 -0
- package/bin/server-ctl +28 -0
- package/db/migrations/0001_init.sql +49 -0
- package/dist/admin/methods.js +103 -0
- package/dist/admin/methods.js.map +1 -0
- package/dist/admin/protocol.js +15 -0
- package/dist/admin/protocol.js.map +1 -0
- package/dist/admin/socket.js +251 -0
- package/dist/admin/socket.js.map +1 -0
- package/dist/cli/dreamux.js +105 -0
- package/dist/cli/dreamux.js.map +1 -0
- package/dist/cli/server-ctl.js +172 -0
- package/dist/cli/server-ctl.js.map +1 -0
- package/dist/cli/server.js +88 -0
- package/dist/cli/server.js.map +1 -0
- package/dist/codex/events.js +82 -0
- package/dist/codex/events.js.map +1 -0
- package/dist/codex/handshake.js +85 -0
- package/dist/codex/handshake.js.map +1 -0
- package/dist/codex/rpc.js +200 -0
- package/dist/codex/rpc.js.map +1 -0
- package/dist/codex/supervisor.js +184 -0
- package/dist/codex/supervisor.js.map +1 -0
- package/dist/codex/types.js +10 -0
- package/dist/codex/types.js.map +1 -0
- package/dist/db/repository.js +207 -0
- package/dist/db/repository.js.map +1 -0
- package/dist/db/schema.js +29 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/dispatcher/approval.js +43 -0
- package/dist/dispatcher/approval.js.map +1 -0
- package/dist/dispatcher/runtime.js +262 -0
- package/dist/dispatcher/runtime.js.map +1 -0
- package/dist/dispatcher/turn-manager.js +167 -0
- package/dist/dispatcher/turn-manager.js.map +1 -0
- package/dist/feishu/bot.js +137 -0
- package/dist/feishu/bot.js.map +1 -0
- package/dist/feishu/content.js +108 -0
- package/dist/feishu/content.js.map +1 -0
- package/dist/feishu/render.js +600 -0
- package/dist/feishu/render.js.map +1 -0
- package/dist/feishu/types.js +9 -0
- package/dist/feishu/types.js.map +1 -0
- package/dist/runtime/codex-args.js +92 -0
- package/dist/runtime/codex-args.js.map +1 -0
- package/dist/runtime/config.js +351 -0
- package/dist/runtime/config.js.map +1 -0
- package/dist/runtime/paths.js +77 -0
- package/dist/runtime/paths.js.map +1 -0
- package/dist/runtime/secrets.js +18 -0
- package/dist/runtime/secrets.js.map +1 -0
- package/dist/server.js +234 -0
- package/dist/server.js.map +1 -0
- 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"}
|