@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,207 @@
|
|
|
1
|
+
const DISPATCHER_COLUMNS = `
|
|
2
|
+
dispatcher_id, bot_app_id, bot_secret_ref, codex_args_json, codex_cwd,
|
|
3
|
+
thread_id, status, enabled, created_at, updated_at,
|
|
4
|
+
last_started_at, last_ready_at, last_error, last_lost_thread_id
|
|
5
|
+
`;
|
|
6
|
+
const INBOUND_COLUMNS = `
|
|
7
|
+
id, dispatcher_id, source_chat_id, source_message_id, sender_id,
|
|
8
|
+
feishu_event_json, parsed_text, state, codex_turn_id, assistant_text,
|
|
9
|
+
feishu_message_ids_json, outbound_error, received_at, started_at,
|
|
10
|
+
completed_at, failed_at, error
|
|
11
|
+
`;
|
|
12
|
+
export class DispatcherRepo {
|
|
13
|
+
db;
|
|
14
|
+
constructor(db) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
}
|
|
17
|
+
create(input) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
this.db
|
|
20
|
+
.prepare(`INSERT INTO dispatchers (
|
|
21
|
+
dispatcher_id, bot_app_id, bot_secret_ref, codex_args_json,
|
|
22
|
+
codex_cwd, status, enabled, created_at, updated_at
|
|
23
|
+
) VALUES (?, ?, ?, ?, ?, 'declared', 1, ?, ?)`)
|
|
24
|
+
.run(input.dispatcher_id, input.bot_app_id, input.bot_secret_ref, input.codex_args_json ?? '{}', input.codex_cwd ?? null, now, now);
|
|
25
|
+
return this.get(input.dispatcher_id);
|
|
26
|
+
}
|
|
27
|
+
get(id) {
|
|
28
|
+
const row = this.db
|
|
29
|
+
.prepare(`SELECT ${DISPATCHER_COLUMNS} FROM dispatchers WHERE dispatcher_id = ?`)
|
|
30
|
+
.get(id);
|
|
31
|
+
return row ?? null;
|
|
32
|
+
}
|
|
33
|
+
list() {
|
|
34
|
+
return this.db
|
|
35
|
+
.prepare(`SELECT ${DISPATCHER_COLUMNS} FROM dispatchers ORDER BY created_at ASC`)
|
|
36
|
+
.all();
|
|
37
|
+
}
|
|
38
|
+
listEnabled() {
|
|
39
|
+
return this.db
|
|
40
|
+
.prepare(`SELECT ${DISPATCHER_COLUMNS} FROM dispatchers WHERE enabled = 1 ORDER BY created_at ASC`)
|
|
41
|
+
.all();
|
|
42
|
+
}
|
|
43
|
+
remove(id) {
|
|
44
|
+
const tx = this.db.transaction(() => {
|
|
45
|
+
this.db
|
|
46
|
+
.prepare(`DELETE FROM inbound_buffer WHERE dispatcher_id = ?`)
|
|
47
|
+
.run(id);
|
|
48
|
+
this.db.prepare(`DELETE FROM dispatchers WHERE dispatcher_id = ?`).run(id);
|
|
49
|
+
});
|
|
50
|
+
tx();
|
|
51
|
+
}
|
|
52
|
+
setStatus(id, status, extras = {}) {
|
|
53
|
+
const fields = ['status = ?', 'updated_at = ?'];
|
|
54
|
+
const values = [status, Date.now()];
|
|
55
|
+
if ('last_error' in extras) {
|
|
56
|
+
fields.push('last_error = ?');
|
|
57
|
+
values.push(extras.last_error ?? null);
|
|
58
|
+
}
|
|
59
|
+
if (extras.last_started_at !== undefined) {
|
|
60
|
+
fields.push('last_started_at = ?');
|
|
61
|
+
values.push(extras.last_started_at);
|
|
62
|
+
}
|
|
63
|
+
if (extras.last_ready_at !== undefined) {
|
|
64
|
+
fields.push('last_ready_at = ?');
|
|
65
|
+
values.push(extras.last_ready_at);
|
|
66
|
+
}
|
|
67
|
+
values.push(id);
|
|
68
|
+
this.db
|
|
69
|
+
.prepare(`UPDATE dispatchers SET ${fields.join(', ')} WHERE dispatcher_id = ?`)
|
|
70
|
+
.run(...values);
|
|
71
|
+
}
|
|
72
|
+
setThreadId(id, threadId) {
|
|
73
|
+
this.db
|
|
74
|
+
.prepare(`UPDATE dispatchers SET thread_id = ?, updated_at = ? WHERE dispatcher_id = ?`)
|
|
75
|
+
.run(threadId, Date.now(), id);
|
|
76
|
+
}
|
|
77
|
+
recordLostThread(id, lostThreadId, newThreadId, error) {
|
|
78
|
+
this.db
|
|
79
|
+
.prepare(`UPDATE dispatchers
|
|
80
|
+
SET thread_id = ?, last_lost_thread_id = ?, last_error = ?, updated_at = ?
|
|
81
|
+
WHERE dispatcher_id = ?`)
|
|
82
|
+
.run(newThreadId, lostThreadId, error, Date.now(), id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class InboundRepo {
|
|
86
|
+
db;
|
|
87
|
+
constructor(db) {
|
|
88
|
+
this.db = db;
|
|
89
|
+
}
|
|
90
|
+
/** Returns null when this message was already buffered (dedupe). */
|
|
91
|
+
enqueue(input) {
|
|
92
|
+
try {
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const result = this.db
|
|
95
|
+
.prepare(`INSERT INTO inbound_buffer (
|
|
96
|
+
dispatcher_id, source_chat_id, source_message_id, sender_id,
|
|
97
|
+
feishu_event_json, parsed_text, state, received_at
|
|
98
|
+
) VALUES (?, ?, ?, ?, ?, ?, 'queued', ?)`)
|
|
99
|
+
.run(input.dispatcher_id, input.source_chat_id, input.source_message_id, input.sender_id, input.feishu_event_json, input.parsed_text, now);
|
|
100
|
+
return this.getById(Number(result.lastInsertRowid));
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (isUniqueViolation(err))
|
|
104
|
+
return null;
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
getById(id) {
|
|
109
|
+
const row = this.db
|
|
110
|
+
.prepare(`SELECT ${INBOUND_COLUMNS} FROM inbound_buffer WHERE id = ?`)
|
|
111
|
+
.get(id);
|
|
112
|
+
return row ?? null;
|
|
113
|
+
}
|
|
114
|
+
/** Pull the oldest queued row for a dispatcher; returns null if none. */
|
|
115
|
+
takeNextQueued(dispatcherId) {
|
|
116
|
+
const row = this.db
|
|
117
|
+
.prepare(`SELECT ${INBOUND_COLUMNS} FROM inbound_buffer
|
|
118
|
+
WHERE dispatcher_id = ? AND state = 'queued'
|
|
119
|
+
ORDER BY id ASC LIMIT 1`)
|
|
120
|
+
.get(dispatcherId);
|
|
121
|
+
return row ?? null;
|
|
122
|
+
}
|
|
123
|
+
markRunning(id, turnId) {
|
|
124
|
+
this.db
|
|
125
|
+
.prepare(`UPDATE inbound_buffer
|
|
126
|
+
SET state = 'running', started_at = ?, codex_turn_id = ?
|
|
127
|
+
WHERE id = ? AND state = 'queued'`)
|
|
128
|
+
.run(Date.now(), turnId, id);
|
|
129
|
+
}
|
|
130
|
+
markAwaitingOutbound(id, assistantText) {
|
|
131
|
+
this.db
|
|
132
|
+
.prepare(`UPDATE inbound_buffer
|
|
133
|
+
SET state = 'awaiting_outbound', assistant_text = ?
|
|
134
|
+
WHERE id = ?`)
|
|
135
|
+
.run(assistantText, id);
|
|
136
|
+
}
|
|
137
|
+
markCompleted(id, feishuMessageIds) {
|
|
138
|
+
this.db
|
|
139
|
+
.prepare(`UPDATE inbound_buffer
|
|
140
|
+
SET state = 'completed', feishu_message_ids_json = ?, completed_at = ?
|
|
141
|
+
WHERE id = ?`)
|
|
142
|
+
.run(JSON.stringify(feishuMessageIds), Date.now(), id);
|
|
143
|
+
}
|
|
144
|
+
markOutboundFailed(id, error) {
|
|
145
|
+
this.db
|
|
146
|
+
.prepare(`UPDATE inbound_buffer
|
|
147
|
+
SET state = 'outbound_failed', outbound_error = ?
|
|
148
|
+
WHERE id = ?`)
|
|
149
|
+
.run(error, id);
|
|
150
|
+
}
|
|
151
|
+
markFailed(id, error) {
|
|
152
|
+
this.db
|
|
153
|
+
.prepare(`UPDATE inbound_buffer
|
|
154
|
+
SET state = 'failed', error = ?, failed_at = ?
|
|
155
|
+
WHERE id = ?`)
|
|
156
|
+
.run(error, Date.now(), id);
|
|
157
|
+
}
|
|
158
|
+
/** Crash recovery: anything still 'running' from a previous server life. */
|
|
159
|
+
markRunningAsUnknown(dispatcherId) {
|
|
160
|
+
const rows = this.db
|
|
161
|
+
.prepare(`SELECT ${INBOUND_COLUMNS} FROM inbound_buffer
|
|
162
|
+
WHERE dispatcher_id = ? AND state = 'running'`)
|
|
163
|
+
.all(dispatcherId);
|
|
164
|
+
if (rows.length === 0)
|
|
165
|
+
return [];
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
this.db
|
|
168
|
+
.prepare(`UPDATE inbound_buffer
|
|
169
|
+
SET state = 'unknown', error = 'server restarted while turn was running', failed_at = ?
|
|
170
|
+
WHERE dispatcher_id = ? AND state = 'running'`)
|
|
171
|
+
.run(now, dispatcherId);
|
|
172
|
+
return rows;
|
|
173
|
+
}
|
|
174
|
+
/** Crash recovery: turn finished but outbound never completed (safe to retry). */
|
|
175
|
+
listAwaitingOrFailedOutbound(dispatcherId) {
|
|
176
|
+
return this.db
|
|
177
|
+
.prepare(`SELECT ${INBOUND_COLUMNS} FROM inbound_buffer
|
|
178
|
+
WHERE dispatcher_id = ? AND state IN ('awaiting_outbound','outbound_failed')
|
|
179
|
+
ORDER BY id ASC`)
|
|
180
|
+
.all(dispatcherId);
|
|
181
|
+
}
|
|
182
|
+
countByState(dispatcherId) {
|
|
183
|
+
const rows = this.db
|
|
184
|
+
.prepare(`SELECT state, COUNT(*) AS n FROM inbound_buffer
|
|
185
|
+
WHERE dispatcher_id = ? GROUP BY state`)
|
|
186
|
+
.all(dispatcherId);
|
|
187
|
+
const out = {
|
|
188
|
+
queued: 0,
|
|
189
|
+
running: 0,
|
|
190
|
+
awaiting_outbound: 0,
|
|
191
|
+
completed: 0,
|
|
192
|
+
outbound_failed: 0,
|
|
193
|
+
failed: 0,
|
|
194
|
+
unknown: 0,
|
|
195
|
+
};
|
|
196
|
+
for (const r of rows)
|
|
197
|
+
out[r.state] = r.n;
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function isUniqueViolation(err) {
|
|
202
|
+
if (!err || typeof err !== 'object')
|
|
203
|
+
return false;
|
|
204
|
+
const code = err.code;
|
|
205
|
+
return code === 'SQLITE_CONSTRAINT_UNIQUE' || code === 'SQLITE_CONSTRAINT_PRIMARYKEY';
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/db/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const CURRENT_USER_VERSION = 1;
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const migrationsDir = join(here, '..', '..', 'db', 'migrations');
|
|
8
|
+
export function openDatabase(opts) {
|
|
9
|
+
const db = new Database(opts.path);
|
|
10
|
+
db.pragma('journal_mode = WAL');
|
|
11
|
+
db.pragma('foreign_keys = ON');
|
|
12
|
+
db.pragma('busy_timeout = 5000');
|
|
13
|
+
migrateIfNeeded(db);
|
|
14
|
+
return db;
|
|
15
|
+
}
|
|
16
|
+
function migrateIfNeeded(db) {
|
|
17
|
+
const row = db.pragma('user_version', { simple: true });
|
|
18
|
+
if (row >= CURRENT_USER_VERSION)
|
|
19
|
+
return;
|
|
20
|
+
const tx = db.transaction(() => {
|
|
21
|
+
if (row < 1) {
|
|
22
|
+
const sql = readFileSync(join(migrationsDir, '0001_init.sql'), 'utf8');
|
|
23
|
+
db.exec(sql);
|
|
24
|
+
}
|
|
25
|
+
db.pragma(`user_version = ${CURRENT_USER_VERSION}`);
|
|
26
|
+
});
|
|
27
|
+
tx();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;AAMjE,MAAM,UAAU,YAAY,CAAC,IAAmB;IAC9C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACjC,eAAe,CAAC,EAAE,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,EAAqB;IAC5C,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAW,CAAC;IAClE,IAAI,GAAG,IAAI,oBAAoB;QAAE,OAAO;IAExC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QAC7B,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;YACvE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;QACD,EAAE,CAAC,MAAM,CAAC,kBAAkB,oBAAoB,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,EAAE,EAAE,CAAC;AACP,CAAC"}
|
package/dist/db/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/db/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval handler for Codex server→client requests.
|
|
3
|
+
*
|
|
4
|
+
* Issue #2 §"信任模型" + §"实现陷阱":
|
|
5
|
+
* - MVP runs Codex with approval-policy=never (or auto-approve).
|
|
6
|
+
* - If a server-request still arrives (e.g. because policy was misconfigured
|
|
7
|
+
* or codex escalates anyway), fail loudly — never return null. Silent null
|
|
8
|
+
* is the trap that hangs the daemon.
|
|
9
|
+
*
|
|
10
|
+
* We surface the rejection back to the inbound's source chat via `onReject`
|
|
11
|
+
* so the user sees "this version does not support approvals" rather than a
|
|
12
|
+
* mute timeout.
|
|
13
|
+
*/
|
|
14
|
+
/** A method name like `exec_command_approval`, `apply_patch_approval`, etc. */
|
|
15
|
+
const APPROVAL_METHOD_HINTS = ['approval', 'approve', 'confirm', 'review'];
|
|
16
|
+
export function looksLikeApprovalRequest(method) {
|
|
17
|
+
const m = method.toLowerCase();
|
|
18
|
+
return APPROVAL_METHOD_HINTS.some((h) => m.includes(h));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a fail-fast server-request handler.
|
|
22
|
+
*
|
|
23
|
+
* The handler always throws — i.e. every server-request becomes an `error`
|
|
24
|
+
* response on the wire — but it tags approval-related methods with a
|
|
25
|
+
* user-readable message and notifies `onReject`.
|
|
26
|
+
*/
|
|
27
|
+
export function createFailFastApprovalHandler(opts = {}) {
|
|
28
|
+
return async (req) => {
|
|
29
|
+
if (opts.onReject !== undefined) {
|
|
30
|
+
try {
|
|
31
|
+
await opts.onReject(req);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* observer hook, must not mask the rejection itself */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (looksLikeApprovalRequest(req.method)) {
|
|
38
|
+
throw new Error(`当前版本不支持审批(${req.method})。请配置 codex approval-policy=never 或将本 dispatcher 部署在 trusted-local 环境。`);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`dispatcher 收到未支持的 codex server-request:${req.method}(id=${req.id})`);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/dispatcher/approval.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,+EAA+E;AAC/E,MAAM,qBAAqB,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3E,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/B,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAC3C,OAA+B,EAAE;IAEjC,OAAO,KAAK,EAAE,GAAkB,EAAoB,EAAE;QACpD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;YACzD,CAAC;QACH,CAAC;QACD,IAAI,wBAAwB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,MAAM,wEAAwE,CAChG,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CACb,0CAA0C,GAAG,CAAC,MAAM,OAAO,GAAG,CAAC,EAAE,GAAG,CACrE,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DispatcherRuntime — one running dispatcher's in-memory state.
|
|
3
|
+
*
|
|
4
|
+
* Owns:
|
|
5
|
+
* - CodexProcess (child app-server)
|
|
6
|
+
* - CodexWsClient (WS connection)
|
|
7
|
+
* - thread_id (lazily created via thread/start or resumed)
|
|
8
|
+
* - TurnManager (FIFO worker for this dispatcher)
|
|
9
|
+
* - approval handler bound to the current "source chat" for hints
|
|
10
|
+
*
|
|
11
|
+
* Lifecycle: declared → starting → ready → (degraded) → stopping → stopped.
|
|
12
|
+
*
|
|
13
|
+
* Issue #2 §"崩溃与异常恢复":
|
|
14
|
+
* - On startup, mark stale `running` rows as `unknown` before opening
|
|
15
|
+
* inbound — at-most-once.
|
|
16
|
+
* - thread/resume failure does not degrade the whole dispatcher; we
|
|
17
|
+
* start a fresh thread, record the lost one in last_lost_thread_id,
|
|
18
|
+
* and post a visible warning to the next source chat.
|
|
19
|
+
*/
|
|
20
|
+
import { mkdirSync } from 'node:fs';
|
|
21
|
+
import { dirname } from 'node:path';
|
|
22
|
+
import { CodexProcess } from '../codex/supervisor.js';
|
|
23
|
+
import { CodexWsClient } from '../codex/rpc.js';
|
|
24
|
+
import { performInitializeHandshake } from '../codex/handshake.js';
|
|
25
|
+
import { TurnManager } from './turn-manager.js';
|
|
26
|
+
import { createFailFastApprovalHandler } from './approval.js';
|
|
27
|
+
import { dispatcherCodexCwd, dispatcherStdoutLog, dispatcherStderrLog, dispatcherSocketPath } from '../runtime/paths.js';
|
|
28
|
+
export class DispatcherRuntime {
|
|
29
|
+
row;
|
|
30
|
+
deps;
|
|
31
|
+
process = null;
|
|
32
|
+
client = null;
|
|
33
|
+
turnManager = null;
|
|
34
|
+
threadId = null;
|
|
35
|
+
currentInboundChatId = null;
|
|
36
|
+
status = 'declared';
|
|
37
|
+
log;
|
|
38
|
+
constructor(row, deps) {
|
|
39
|
+
this.row = row;
|
|
40
|
+
this.deps = deps;
|
|
41
|
+
this.log = deps.log ?? ((lvl, msg, err) => {
|
|
42
|
+
const prefix = `[dispatcher ${row.dispatcher_id}] ${lvl}`;
|
|
43
|
+
if (err !== undefined)
|
|
44
|
+
console.error(prefix, msg, err);
|
|
45
|
+
else
|
|
46
|
+
console.error(prefix, msg);
|
|
47
|
+
});
|
|
48
|
+
this.threadId = row.thread_id;
|
|
49
|
+
}
|
|
50
|
+
get dispatcherId() {
|
|
51
|
+
return this.row.dispatcher_id;
|
|
52
|
+
}
|
|
53
|
+
getStatus() {
|
|
54
|
+
return this.status;
|
|
55
|
+
}
|
|
56
|
+
/** Called by Feishu inbound right before enqueuing into inbound_buffer. */
|
|
57
|
+
setCurrentInboundChat(chatId) {
|
|
58
|
+
this.currentInboundChatId = chatId;
|
|
59
|
+
}
|
|
60
|
+
getThreadId() {
|
|
61
|
+
return this.threadId;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Bring the dispatcher up. Order:
|
|
65
|
+
* 1. crash recovery sweep on inbound_buffer (running → unknown)
|
|
66
|
+
* 2. spawn codex app-server child
|
|
67
|
+
* 3. open WS client
|
|
68
|
+
* 4. install fail-fast approval handler
|
|
69
|
+
* 5. thread/start (new) or thread/resume (existing)
|
|
70
|
+
* 6. install turn manager + retry pending outbound
|
|
71
|
+
* 7. status = ready
|
|
72
|
+
*/
|
|
73
|
+
async start() {
|
|
74
|
+
this.setStatus('starting');
|
|
75
|
+
this.deps.dispatchers.setStatus(this.dispatcherId, 'starting', {
|
|
76
|
+
last_started_at: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
try {
|
|
79
|
+
await this.recoverInboundOnStartup();
|
|
80
|
+
const cwd = this.row.codex_cwd ?? dispatcherCodexCwd(this.dispatcherId);
|
|
81
|
+
const socketPath = dispatcherSocketPath(this.dispatcherId);
|
|
82
|
+
const extraArgs = this.deps.resolveExtraArgs?.(this.row) ?? [];
|
|
83
|
+
const factory = this.deps.codexProcessFactory ?? ((o) => new CodexProcess(o));
|
|
84
|
+
this.process = factory({
|
|
85
|
+
socketPath,
|
|
86
|
+
cwd,
|
|
87
|
+
stdoutLogPath: dispatcherStdoutLog(this.dispatcherId),
|
|
88
|
+
stderrLogPath: dispatcherStderrLog(this.dispatcherId),
|
|
89
|
+
binPath: this.deps.codexBinPath,
|
|
90
|
+
extraArgs,
|
|
91
|
+
});
|
|
92
|
+
mkdirSync(dirname(socketPath), { recursive: true });
|
|
93
|
+
await this.process.start();
|
|
94
|
+
const clientFactory = this.deps.codexClientFactory ?? ((sock) => new CodexWsClient({ socketPath: sock }));
|
|
95
|
+
this.client = clientFactory(socketPath);
|
|
96
|
+
await this.client.ready();
|
|
97
|
+
const approvalHandler = createFailFastApprovalHandler({
|
|
98
|
+
onReject: async (req) => {
|
|
99
|
+
// Best-effort hint to the user, only if we know who is asking.
|
|
100
|
+
const chatId = this.currentInboundChatId;
|
|
101
|
+
if (chatId === null)
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
await this.deps.outbound.sendText(chatId, `Codex 请求了一次审批(${req.method}),但当前 dispatcher 不支持审批 —— 本轮将失败。`);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* nothing useful to do */
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
this.client.setServerRequestHandler(approvalHandler);
|
|
112
|
+
// codex 0.134+ LSP-style handshake — must precede thread/start or
|
|
113
|
+
// any other RPC, otherwise codex answers everything with
|
|
114
|
+
// `Not initialized` (see src/codex/handshake.ts).
|
|
115
|
+
const initResponse = await performInitializeHandshake(this.client, {
|
|
116
|
+
...(this.deps.handshakeTimeoutMs !== undefined
|
|
117
|
+
? { timeoutMs: this.deps.handshakeTimeoutMs }
|
|
118
|
+
: {}),
|
|
119
|
+
});
|
|
120
|
+
this.log('info', `codex initialized: ${initResponse.userAgent} (home=${initResponse.codexHome}, ${initResponse.platformOs})`);
|
|
121
|
+
await this.resolveThread();
|
|
122
|
+
this.turnManager = new TurnManager({
|
|
123
|
+
dispatcherId: this.dispatcherId,
|
|
124
|
+
inbound: this.deps.inbound,
|
|
125
|
+
getThreadId: () => this.threadId,
|
|
126
|
+
client: this.client,
|
|
127
|
+
outbound: this.deps.outbound,
|
|
128
|
+
log: this.log,
|
|
129
|
+
...(this.deps.outboundRetries !== undefined
|
|
130
|
+
? { outboundRetries: this.deps.outboundRetries }
|
|
131
|
+
: {}),
|
|
132
|
+
...(this.deps.outboundRetryDelayMs !== undefined
|
|
133
|
+
? { outboundRetryDelayMs: this.deps.outboundRetryDelayMs }
|
|
134
|
+
: {}),
|
|
135
|
+
});
|
|
136
|
+
await this.turnManager.retryPendingOutbound();
|
|
137
|
+
this.setStatus('ready');
|
|
138
|
+
this.deps.dispatchers.setStatus(this.dispatcherId, 'ready', {
|
|
139
|
+
last_ready_at: Date.now(),
|
|
140
|
+
last_error: null,
|
|
141
|
+
});
|
|
142
|
+
// Issue #2 + PR #3 review #1: the durable inbound buffer's contract
|
|
143
|
+
// says nothing is dropped across a server crash. retryPendingOutbound
|
|
144
|
+
// covers awaiting_outbound / outbound_failed rows; recoverInboundOnStartup
|
|
145
|
+
// covers running → unknown. But rows persisted in 'queued' before the
|
|
146
|
+
// crash are only drained when notify() is invoked, and at startup no
|
|
147
|
+
// new inbound has arrived yet. Kick the worker once so any
|
|
148
|
+
// already-queued backlog drains immediately instead of stalling until
|
|
149
|
+
// the next live message lands.
|
|
150
|
+
this.turnManager.notify();
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
154
|
+
this.log('error', `start failed: ${msg}`, err);
|
|
155
|
+
this.setStatus('degraded');
|
|
156
|
+
this.deps.dispatchers.setStatus(this.dispatcherId, 'degraded', {
|
|
157
|
+
last_error: msg,
|
|
158
|
+
});
|
|
159
|
+
await this.cleanupOnFailure();
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async resolveThread() {
|
|
164
|
+
if (this.client === null)
|
|
165
|
+
throw new Error('client not initialized');
|
|
166
|
+
const existing = this.row.thread_id;
|
|
167
|
+
if (existing === null) {
|
|
168
|
+
// Fresh thread.
|
|
169
|
+
const res = await this.client.request('thread/start', {});
|
|
170
|
+
this.threadId = res.thread.id;
|
|
171
|
+
this.deps.dispatchers.setThreadId(this.dispatcherId, this.threadId);
|
|
172
|
+
this.log('info', `started fresh thread ${this.threadId}`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
await this.client.request('thread/resume', {
|
|
177
|
+
threadId: existing,
|
|
178
|
+
});
|
|
179
|
+
this.threadId = existing;
|
|
180
|
+
this.log('info', `resumed thread ${this.threadId}`);
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// Visible degradation (issue #2 Q11): start a fresh thread, record loss.
|
|
184
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
185
|
+
this.log('warn', `thread/resume failed for ${existing}: ${msg}; starting fresh thread`);
|
|
186
|
+
const res = await this.client.request('thread/start', {});
|
|
187
|
+
this.threadId = res.thread.id;
|
|
188
|
+
this.deps.dispatchers.recordLostThread(this.dispatcherId, existing, this.threadId, `thread/resume failed: ${msg}`);
|
|
189
|
+
// Park a warning to be delivered with the next outbound — best-effort
|
|
190
|
+
// queue note. For MVP we just log; full user-visible delivery on next
|
|
191
|
+
// inbound is a follow-up (see PR review).
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Drain any inbound message arriving for this dispatcher. Called by the
|
|
196
|
+
* Feishu inbound layer. Returns the assigned inbound row id, or null if
|
|
197
|
+
* the message was a duplicate.
|
|
198
|
+
*/
|
|
199
|
+
enqueueInbound(input) {
|
|
200
|
+
const row = this.deps.inbound.enqueue({
|
|
201
|
+
dispatcher_id: this.dispatcherId,
|
|
202
|
+
...input,
|
|
203
|
+
});
|
|
204
|
+
if (row === null)
|
|
205
|
+
return null;
|
|
206
|
+
this.setCurrentInboundChat(input.source_chat_id);
|
|
207
|
+
this.turnManager?.notify();
|
|
208
|
+
return row.id;
|
|
209
|
+
}
|
|
210
|
+
async recoverInboundOnStartup() {
|
|
211
|
+
const stale = this.deps.inbound.markRunningAsUnknown(this.dispatcherId);
|
|
212
|
+
for (const row of stale) {
|
|
213
|
+
this.log('warn', `inbound ${row.id} was 'running' at restart — marked unknown (at-most-once); chat=${row.source_chat_id}`);
|
|
214
|
+
try {
|
|
215
|
+
await this.deps.outbound.sendText(row.source_chat_id, `上一次的执行结果未知(server 重启时正在进行)。请确认是否需要重新发送:\n> ${row.parsed_text.slice(0, 200)}`);
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
this.log('warn', `failed to notify chat about unknown inbound`, err);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Graceful stop: stop accepting work, reap codex child. */
|
|
223
|
+
async stop() {
|
|
224
|
+
this.setStatus('stopping');
|
|
225
|
+
this.deps.dispatchers.setStatus(this.dispatcherId, 'stopping');
|
|
226
|
+
if (this.turnManager !== null)
|
|
227
|
+
await this.turnManager.stop();
|
|
228
|
+
if (this.client !== null) {
|
|
229
|
+
try {
|
|
230
|
+
this.client.close();
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
/* best effort */
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (this.process !== null) {
|
|
237
|
+
await this.process.reap();
|
|
238
|
+
}
|
|
239
|
+
this.setStatus('stopped');
|
|
240
|
+
this.deps.dispatchers.setStatus(this.dispatcherId, 'stopped');
|
|
241
|
+
}
|
|
242
|
+
async cleanupOnFailure() {
|
|
243
|
+
if (this.client !== null) {
|
|
244
|
+
try {
|
|
245
|
+
this.client.close();
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
/* */
|
|
249
|
+
}
|
|
250
|
+
this.client = null;
|
|
251
|
+
}
|
|
252
|
+
if (this.process !== null) {
|
|
253
|
+
await this.process.reap();
|
|
254
|
+
this.process = null;
|
|
255
|
+
}
|
|
256
|
+
this.turnManager = null;
|
|
257
|
+
}
|
|
258
|
+
setStatus(s) {
|
|
259
|
+
this.status = s;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/dispatcher/runtime.ts"],"names":[],"mappings":"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"}
|