@oatelauser/crosschat 1.0.0 → 1.1.0
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/CHANGELOG.md +53 -22
- package/LICENSE +25 -25
- package/README.md +254 -244
- package/dist/cli.js +38 -18
- package/dist/cli.js.map +1 -1
- package/dist/codex/deliver.js +20 -9
- package/dist/codex/deliver.js.map +1 -1
- package/dist/codex/rollout-meta.js +6 -0
- package/dist/codex/rollout-meta.js.map +1 -1
- package/dist/codex/transport.js +19 -1
- package/dist/codex/transport.js.map +1 -1
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/send.js +56 -13
- package/dist/commands/send.js.map +1 -1
- package/dist/outbox.js +204 -52
- package/dist/outbox.js.map +1 -1
- package/dist/send-log.js +74 -0
- package/dist/send-log.js.map +1 -0
- package/dist/watchdog.js +104 -0
- package/dist/watchdog.js.map +1 -0
- package/package.json +46 -47
- package/skills/crosschat/SKILL.md +65 -65
- package/dist/codex/queue.js +0 -71
- package/dist/codex/queue.js.map +0 -1
package/dist/outbox.js
CHANGED
|
@@ -1,43 +1,81 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, writeSync, } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import process from 'node:process';
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
5
6
|
import { MultichatError } from './errors.js';
|
|
6
7
|
import { checkAndRecord, rateKey } from './rate-limit.js';
|
|
7
8
|
/**
|
|
8
|
-
* Opportunistic outbox (B10): when a codex delivery fails
|
|
9
|
-
* CODEX_THREAD_BUSY_TIMEOUT / CODEX_THREAD_LOCKED, the fully composed
|
|
10
|
-
* is parked on disk (one JSON file per thread under
|
|
9
|
+
* Opportunistic outbox (B10/B11, reworked B12): when a codex delivery fails
|
|
10
|
+
* with CODEX_THREAD_BUSY_TIMEOUT / CODEX_THREAD_LOCKED, the fully composed
|
|
11
|
+
* envelope is parked on disk (one JSON file per thread under
|
|
11
12
|
* %LOCALAPPDATA%/crosschat/outbox/) and re-delivered verbatim by the next
|
|
12
|
-
* send/status invocation
|
|
13
|
-
*
|
|
13
|
+
* send/status invocation or by the detached watchdog loop (src/watchdog.ts).
|
|
14
|
+
*
|
|
15
|
+
* B12 invariants, learned from the 2026-10-02 incident:
|
|
16
|
+
* - busy/locked is a THREAD-level state: one failing attempt aborts the whole
|
|
17
|
+
* thread for this round instead of burning the budget on guaranteed failures
|
|
18
|
+
* (the old loop starved queue tails to attempts=0 for 12h).
|
|
19
|
+
* - new sends join the queue instead of jumping it (enforced in send.ts).
|
|
20
|
+
* - every parked/delivered/dropped item is mirrored into a human-readable
|
|
21
|
+
* mailbox file so stuck content is always readable.
|
|
22
|
+
* - park/drain mutations take a per-thread lockfile so the watchdog and CLI
|
|
23
|
+
* drains cannot lose items by racing read-modify-write cycles.
|
|
14
24
|
*/
|
|
15
|
-
export const OUTBOX_MAX_PER_THREAD =
|
|
25
|
+
export const OUTBOX_MAX_PER_THREAD = 200;
|
|
16
26
|
/** Short wait for drains: opportunistic relief must not block the real command. */
|
|
17
27
|
const DEFAULT_DRAIN_BUSY_TIMEOUT_MS = 5_000;
|
|
18
28
|
/** B11: a drain round re-delivers at most this many items (rest waits for the next round). */
|
|
19
29
|
const DEFAULT_DRAIN_MAX_ITEMS = 5;
|
|
20
30
|
/** B11: wall-clock budget for one drain round, judged on the injected clock. */
|
|
21
31
|
const DEFAULT_DRAIN_BUDGET_MS = 15_000;
|
|
32
|
+
/** Per-thread lockfile staleness; a crashed holder is stolen after this. */
|
|
33
|
+
const LOCK_STALE_MS = 60_000;
|
|
22
34
|
export function defaultOutboxDir() {
|
|
23
35
|
return join(process.env.LOCALAPPDATA ?? homedir(), 'crosschat', 'outbox');
|
|
24
36
|
}
|
|
37
|
+
/** Mailbox mirror lives next to the outbox root (sibling "mailbox" directory). */
|
|
38
|
+
export function mailboxDirFor(outboxDir) {
|
|
39
|
+
return join(outboxDir, '..', 'mailbox');
|
|
40
|
+
}
|
|
41
|
+
export function mailboxFileFor(outboxDir, threadId) {
|
|
42
|
+
return join(mailboxDirFor(outboxDir), `${threadId}.md`);
|
|
43
|
+
}
|
|
44
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
|
+
function genId(nowMs) {
|
|
46
|
+
return `${nowMs.toString(36)}-${randomBytes(3).toString('hex')}`;
|
|
47
|
+
}
|
|
48
|
+
function isoOf(nowMs) {
|
|
49
|
+
return new Date(nowMs).toISOString();
|
|
50
|
+
}
|
|
25
51
|
/** Append one parked item (FIFO); refuses honestly past the per-thread cap. */
|
|
26
|
-
export function park(dir, threadId, item, nowMs = Date.now()) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
52
|
+
export function park(dir, threadId, item, nowMs = Date.now(), mailboxDir = mailboxDirFor(dir)) {
|
|
53
|
+
// Best-effort lock: a contended park falls through lockless (ms-scale window,
|
|
54
|
+
// same risk as pre-B12) rather than blocking the sending command.
|
|
55
|
+
const locked = acquireThreadLock(dir, threadId, Date.now());
|
|
56
|
+
try {
|
|
57
|
+
const items = readItems(dir, threadId) ?? []; // missing/corrupt: self-heal as empty
|
|
58
|
+
if (items.length >= OUTBOX_MAX_PER_THREAD) {
|
|
59
|
+
throw new MultichatError('OUTBOX_FULL', `Outbox full for codex thread ${threadId} (${OUTBOX_MAX_PER_THREAD} parked). ` +
|
|
60
|
+
`对方线程持续忙或已死:读 ${join(mailboxDir, `${threadId}.md`)} 取回内容,` +
|
|
61
|
+
`或确认线程仍存在后重发。`);
|
|
62
|
+
}
|
|
63
|
+
const parked = { ...item, id: genId(nowMs), parkedAt: nowMs, attempts: 0 };
|
|
64
|
+
writeItems(dir, threadId, [...items, parked]);
|
|
65
|
+
appendMailbox(mailboxDir, threadId, `\n## ${parked.id} 寄存 ${isoOf(nowMs)}(队列第 ${items.length + 1} 位)\n${parked.envelope}\n`);
|
|
66
|
+
return parked;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (locked)
|
|
70
|
+
releaseThreadLock(dir, threadId);
|
|
31
71
|
}
|
|
32
|
-
const parked = { ...item, parkedAt: nowMs, attempts: 0 };
|
|
33
|
-
writeItems(dir, threadId, [...items, parked]);
|
|
34
|
-
return parked;
|
|
35
72
|
}
|
|
36
73
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
74
|
+
* Deliver parked items head-first, one at a time: each delivery re-reads the
|
|
75
|
+
* file under the thread lock and removes exactly the delivered id, so a
|
|
76
|
+
* concurrent park is never lost. A busy/locked failure aborts the thread for
|
|
77
|
+
* this round (the state is thread-level); CODEX_THREAD_NOT_FOUND dead-letters
|
|
78
|
+
* the whole thread; any other error skips just that item for this round.
|
|
41
79
|
* Never throws (corrupt files are skipped, a missing dir is a no-op), so it
|
|
42
80
|
* can run before any command without failing it.
|
|
43
81
|
*/
|
|
@@ -45,6 +83,7 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
45
83
|
const busyTimeoutMs = opts.busyTimeoutMs ?? DEFAULT_DRAIN_BUSY_TIMEOUT_MS;
|
|
46
84
|
const maxItems = opts.maxItems ?? DEFAULT_DRAIN_MAX_ITEMS;
|
|
47
85
|
const budgetMs = opts.budgetMs ?? DEFAULT_DRAIN_BUDGET_MS;
|
|
86
|
+
const mailboxDir = opts.mailboxDir ?? mailboxDirFor(dir);
|
|
48
87
|
const now = opts.now ?? Date.now;
|
|
49
88
|
const deadline = now() + budgetMs;
|
|
50
89
|
let allowance = maxItems;
|
|
@@ -60,25 +99,32 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
60
99
|
if (!file.endsWith('.json'))
|
|
61
100
|
continue;
|
|
62
101
|
const threadId = file.slice(0, -'.json'.length);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const remaining = [];
|
|
102
|
+
// Items to ignore for the rest of this round (rate-window full, unknown
|
|
103
|
+
// delivery error): kept verbatim, retried by the next round.
|
|
104
|
+
const skipIds = new Set();
|
|
67
105
|
let delivered = 0;
|
|
68
106
|
let dropped = 0;
|
|
69
|
-
let
|
|
70
|
-
for (
|
|
71
|
-
if (allowance <= 0 || now() >= deadline)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
let toName = '';
|
|
108
|
+
for (;;) {
|
|
109
|
+
if (allowance <= 0 || now() >= deadline)
|
|
110
|
+
break;
|
|
111
|
+
const head = await withThreadLock(dir, threadId, 2_000, () => {
|
|
112
|
+
const items = readItems(dir, threadId);
|
|
113
|
+
if (items === undefined || items.length === 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
const item = items.find((candidate) => !skipIds.has(candidate.id));
|
|
116
|
+
return item === undefined ? undefined : { item };
|
|
117
|
+
});
|
|
118
|
+
if (head === undefined)
|
|
119
|
+
break;
|
|
120
|
+
toName = head.item.toName;
|
|
75
121
|
if (opts.rateDir !== undefined) {
|
|
76
122
|
try {
|
|
77
|
-
checkAndRecord(opts.rateDir, rateKey(item.callerKey ?? 'human', `codex:${threadId}`), now());
|
|
123
|
+
checkAndRecord(opts.rateDir, rateKey(head.item.callerKey ?? 'human', `codex:${threadId}`), now());
|
|
78
124
|
}
|
|
79
125
|
catch (err) {
|
|
80
126
|
if (err instanceof MultichatError && err.code === 'RATE_LIMITED') {
|
|
81
|
-
|
|
127
|
+
skipIds.add(head.item.id); // window full: skip this round, retry next drain
|
|
82
128
|
continue;
|
|
83
129
|
}
|
|
84
130
|
throw err;
|
|
@@ -86,44 +132,51 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
86
132
|
}
|
|
87
133
|
allowance -= 1;
|
|
88
134
|
try {
|
|
89
|
-
await deliverFn(threadId, item.envelope, busyTimeoutMs);
|
|
135
|
+
await deliverFn(threadId, head.item.envelope, busyTimeoutMs);
|
|
90
136
|
delivered++;
|
|
91
|
-
|
|
137
|
+
appendMailbox(mailboxDir, threadId, `> ${head.item.id} 送达 ${isoOf(now())}\n`);
|
|
138
|
+
await withThreadLock(dir, threadId, 2_000, () => {
|
|
139
|
+
removeById(dir, threadId, head.item.id);
|
|
140
|
+
});
|
|
92
141
|
}
|
|
93
142
|
catch (err) {
|
|
94
143
|
if (err instanceof MultichatError && err.code === 'CODEX_THREAD_NOT_FOUND') {
|
|
95
144
|
// Dead letter: the thread is gone, so every parked item for it is
|
|
96
145
|
// unreachable — discard all of them, not just this one.
|
|
97
|
-
|
|
98
|
-
|
|
146
|
+
const removed = (await withThreadLock(dir, threadId, 2_000, () => {
|
|
147
|
+
const items = readItems(dir, threadId) ?? [];
|
|
148
|
+
writeItems(dir, threadId, []);
|
|
149
|
+
return items.length;
|
|
150
|
+
})) ?? 0;
|
|
151
|
+
dropped += removed;
|
|
152
|
+
appendMailbox(mailboxDir, threadId, `> 线程已不存在,丢弃 ${removed} 条(${isoOf(now())})\n`);
|
|
99
153
|
break;
|
|
100
154
|
}
|
|
101
155
|
if (err instanceof MultichatError &&
|
|
102
156
|
(err.code === 'CODEX_THREAD_BUSY_TIMEOUT' || err.code === 'CODEX_THREAD_LOCKED')) {
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
157
|
+
// Thread-level state: everything after the head would fail too.
|
|
158
|
+
await withThreadLock(dir, threadId, 2_000, () => {
|
|
159
|
+
bumpAttempts(dir, threadId, head.item.id);
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
108
162
|
}
|
|
163
|
+
skipIds.add(head.item.id); // unknown state: keep, try again next drain
|
|
109
164
|
}
|
|
110
165
|
}
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
writeItems(dir, threadId, remaining);
|
|
114
|
-
if (delivered > 0 || remaining.length > 0 || dropped > 0) {
|
|
166
|
+
const remaining = countItems(dir, threadId);
|
|
167
|
+
if (delivered > 0 || remaining > 0 || dropped > 0) {
|
|
115
168
|
results.push({
|
|
116
169
|
threadId,
|
|
117
|
-
toName:
|
|
170
|
+
toName: toName || shortThread(threadId),
|
|
118
171
|
delivered,
|
|
119
|
-
remaining
|
|
172
|
+
remaining,
|
|
120
173
|
dropped,
|
|
121
174
|
});
|
|
122
175
|
}
|
|
123
176
|
}
|
|
124
177
|
return results;
|
|
125
178
|
}
|
|
126
|
-
/** Parked item count per thread (doctor display); corrupt
|
|
179
|
+
/** Parked item count + oldest age per thread (doctor display); corrupt files are skipped. */
|
|
127
180
|
export function outboxSummary(dir) {
|
|
128
181
|
let files;
|
|
129
182
|
try {
|
|
@@ -138,18 +191,36 @@ export function outboxSummary(dir) {
|
|
|
138
191
|
continue;
|
|
139
192
|
const threadId = file.slice(0, -'.json'.length);
|
|
140
193
|
const items = readItems(dir, threadId);
|
|
141
|
-
if (items !== undefined && items.length > 0)
|
|
142
|
-
summary.push({
|
|
194
|
+
if (items !== undefined && items.length > 0) {
|
|
195
|
+
summary.push({
|
|
196
|
+
threadId,
|
|
197
|
+
count: items.length,
|
|
198
|
+
oldestParkedAt: Math.min(...items.map((item) => item.parkedAt)),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
143
201
|
}
|
|
144
202
|
return summary;
|
|
145
203
|
}
|
|
204
|
+
/** Queue depth for one thread (0 when nothing is parked). */
|
|
205
|
+
export function outboxCount(dir, threadId) {
|
|
206
|
+
return readItems(dir, threadId)?.length ?? 0;
|
|
207
|
+
}
|
|
208
|
+
function shortThread(threadId) {
|
|
209
|
+
return `codex/${threadId.slice(0, 8)}`;
|
|
210
|
+
}
|
|
146
211
|
function threadFile(dir, threadId) {
|
|
147
212
|
return join(dir, `${threadId}.json`);
|
|
148
213
|
}
|
|
214
|
+
function lockFile(dir, threadId) {
|
|
215
|
+
return join(dir, `${threadId}.lock`);
|
|
216
|
+
}
|
|
149
217
|
function readItems(dir, threadId) {
|
|
150
218
|
try {
|
|
151
219
|
const parsed = JSON.parse(readFileSync(threadFile(dir, threadId), 'utf8'));
|
|
152
|
-
|
|
220
|
+
if (!Array.isArray(parsed.items))
|
|
221
|
+
return undefined;
|
|
222
|
+
// Pre-B12 files carry no id: synthesize a stable one from parkedAt+index.
|
|
223
|
+
return parsed.items.map((item, index) => item.id === undefined ? { ...item, id: `${item.parkedAt.toString(36)}-${index.toString(36)}` } : item);
|
|
153
224
|
}
|
|
154
225
|
catch {
|
|
155
226
|
return undefined;
|
|
@@ -163,10 +234,91 @@ function writeItems(dir, threadId, items) {
|
|
|
163
234
|
return;
|
|
164
235
|
}
|
|
165
236
|
mkdirSync(dir, { recursive: true });
|
|
166
|
-
// ponytail: no cross-process locking; two concurrent drains can double-deliver
|
|
167
|
-
// one item — rare and downstream-idempotent enough; add a lockfile if it bites.
|
|
168
237
|
const tmp = join(dir, `${threadId}.${process.pid}.${Date.now()}.tmp`);
|
|
169
238
|
writeFileSync(tmp, JSON.stringify({ items }), 'utf8');
|
|
170
239
|
renameSync(tmp, file);
|
|
171
240
|
}
|
|
241
|
+
function removeById(dir, threadId, id) {
|
|
242
|
+
const items = readItems(dir, threadId);
|
|
243
|
+
if (items === undefined)
|
|
244
|
+
return;
|
|
245
|
+
writeItems(dir, threadId, items.filter((item) => item.id !== id));
|
|
246
|
+
}
|
|
247
|
+
function bumpAttempts(dir, threadId, id) {
|
|
248
|
+
const items = readItems(dir, threadId);
|
|
249
|
+
if (items === undefined)
|
|
250
|
+
return;
|
|
251
|
+
writeItems(dir, threadId, items.map((item) => (item.id === id ? { ...item, attempts: item.attempts + 1 } : item)));
|
|
252
|
+
}
|
|
253
|
+
function countItems(dir, threadId) {
|
|
254
|
+
return readItems(dir, threadId)?.length ?? 0;
|
|
255
|
+
}
|
|
256
|
+
function appendMailbox(mailboxDir, threadId, text) {
|
|
257
|
+
try {
|
|
258
|
+
mkdirSync(mailboxDir, { recursive: true });
|
|
259
|
+
appendFileSync(join(mailboxDir, `${threadId}.md`), text, 'utf8');
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// ponytail: the mirror is best-effort observability; outbox json stays authoritative.
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function acquireThreadLock(dir, threadId, nowMs) {
|
|
266
|
+
const file = lockFile(dir, threadId);
|
|
267
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
268
|
+
try {
|
|
269
|
+
const fd = openExclusive(file);
|
|
270
|
+
writeSyncClose(fd, `${process.pid} ${nowMs}`);
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
if (err.code !== 'EEXIST')
|
|
275
|
+
return false;
|
|
276
|
+
try {
|
|
277
|
+
const [, ts] = readFileSync(file, 'utf8').trim().split(/\s+/);
|
|
278
|
+
const written = Number(ts);
|
|
279
|
+
if (Number.isFinite(written) && nowMs - written > LOCK_STALE_MS) {
|
|
280
|
+
rmSync(file, { force: true }); // steal a crashed holder's lock
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
function releaseThreadLock(dir, threadId) {
|
|
293
|
+
rmSync(lockFile(dir, threadId), { force: true });
|
|
294
|
+
}
|
|
295
|
+
/** Run fn under the per-thread lock, waiting up to waitMs; undefined = never ran. */
|
|
296
|
+
async function withThreadLock(dir, threadId, waitMs, fn) {
|
|
297
|
+
const deadline = Date.now() + waitMs;
|
|
298
|
+
for (;;) {
|
|
299
|
+
if (acquireThreadLock(dir, threadId, Date.now())) {
|
|
300
|
+
try {
|
|
301
|
+
return fn();
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
releaseThreadLock(dir, threadId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (Date.now() >= deadline)
|
|
308
|
+
return undefined;
|
|
309
|
+
await sleep(25);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// fs.open with "wx" lives behind openSync's flag form; keep it tiny and local.
|
|
313
|
+
function openExclusive(file) {
|
|
314
|
+
return openSync(file, 'wx');
|
|
315
|
+
}
|
|
316
|
+
function writeSyncClose(fd, text) {
|
|
317
|
+
try {
|
|
318
|
+
writeSync(fd, text, null, 'utf8');
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
closeSync(fd);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
172
324
|
//# sourceMappingURL=outbox.js.map
|
package/dist/outbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"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"}
|
package/dist/send-log.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { findRolloutFile } from './codex/rollout-meta.js';
|
|
6
|
+
/**
|
|
7
|
+
* Sender-side send log (B14, F4): every send's final outcome is appended as
|
|
8
|
+
* one JSON line so a delivery whose CLI invocation already returned (tool
|
|
9
|
+
* timeout → background continuation) stays observable after the fact, and a
|
|
10
|
+
* codex delivery is confirmed against the target's rollout file — turning
|
|
11
|
+
* "delivered" from "the server accepted the turn" into "the message is
|
|
12
|
+
* verifiably in the recipient's conversation history". Metadata only; no
|
|
13
|
+
* message bodies are ever written here.
|
|
14
|
+
*/
|
|
15
|
+
export function defaultSendLogFile() {
|
|
16
|
+
return join(process.env.LOCALAPPDATA ?? homedir(), 'crosschat', 'send-log.jsonl');
|
|
17
|
+
}
|
|
18
|
+
export function appendSendLog(file, entry) {
|
|
19
|
+
try {
|
|
20
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
21
|
+
appendFileSync(file, `${JSON.stringify(entry)}\n`, 'utf8');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// ponytail: observability must never break the send itself.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
+
/**
|
|
29
|
+
* Poll the recipient thread's rollout file until the marker (the unique
|
|
30
|
+
* reply-ref embedded in every envelope) shows up — the receipt. The rollout
|
|
31
|
+
* write can lag turn/start acceptance by a moment, hence the short retry.
|
|
32
|
+
* Never throws; 'unconfirmed' means "not seen within the budget", not "lost".
|
|
33
|
+
*/
|
|
34
|
+
export async function confirmInRollout(codexHome, threadId, marker, opts = {}) {
|
|
35
|
+
const tries = opts.tries ?? 5;
|
|
36
|
+
const delayMs = opts.delayMs ?? 300;
|
|
37
|
+
for (let attempt = 0; attempt < tries; attempt++) {
|
|
38
|
+
const file = findRolloutFile(codexHome, threadId);
|
|
39
|
+
if (file !== undefined && fileContains(file, marker))
|
|
40
|
+
return 'confirmed';
|
|
41
|
+
await sleep(delayMs);
|
|
42
|
+
}
|
|
43
|
+
return 'unconfirmed';
|
|
44
|
+
}
|
|
45
|
+
/** Chunked substring scan; carries a tail so a marker spanning chunks is found. */
|
|
46
|
+
function fileContains(path, marker) {
|
|
47
|
+
let fd;
|
|
48
|
+
try {
|
|
49
|
+
fd = openSync(path, 'r');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
56
|
+
let carry = '';
|
|
57
|
+
for (;;) {
|
|
58
|
+
const n = readSync(fd, buf, 0, buf.length, null);
|
|
59
|
+
if (n <= 0)
|
|
60
|
+
return false;
|
|
61
|
+
const text = carry + buf.subarray(0, n).toString('utf8');
|
|
62
|
+
if (text.includes(marker))
|
|
63
|
+
return true;
|
|
64
|
+
carry = text.slice(Math.max(0, text.length - marker.length));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
closeSync(fd);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=send-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-log.js","sourceRoot":"","sources":["../src/send-log.ts"],"names":[],"mappings":"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"}
|
package/dist/watchdog.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { drain, outboxSummary } from './outbox.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detached outbox watchdog (B12): the 2026-10-02 incident showed parked
|
|
8
|
+
* messages starve unless some CLI call happens to run while the target thread
|
|
9
|
+
* is idle. After any park, send spawns `crosschat __drain --watch` detached;
|
|
10
|
+
* it retries with backoff until the outbox empties, then exits. Single
|
|
11
|
+
* instance via a pid+timestamp lockfile in the outbox dir (no resident
|
|
12
|
+
* daemon: the process only lives while work is pending).
|
|
13
|
+
*/
|
|
14
|
+
export const WATCHDOG_BACKOFF_STEPS_MS = [30_000, 60_000, 120_000, 300_000];
|
|
15
|
+
/** Backoff after N consecutive empty-handed rounds; any delivery resets it. */
|
|
16
|
+
export function nextBackoffMs(consecutiveEmptyRounds) {
|
|
17
|
+
const index = Math.min(Math.max(consecutiveEmptyRounds, 0), WATCHDOG_BACKOFF_STEPS_MS.length - 1);
|
|
18
|
+
return WATCHDOG_BACKOFF_STEPS_MS[index];
|
|
19
|
+
}
|
|
20
|
+
const WATCHDOG_LOCK = 'watchdog.lock';
|
|
21
|
+
/** Heartbeat freshness; a holder without a heartbeat is considered dead. */
|
|
22
|
+
const WATCHDOG_STALE_MS = 90_000;
|
|
23
|
+
export function watchdogStatus(outboxDir, nowMs = Date.now()) {
|
|
24
|
+
try {
|
|
25
|
+
const [pidText, tsText] = readFileSync(join(outboxDir, WATCHDOG_LOCK), 'utf8').trim().split(/\s+/);
|
|
26
|
+
const pid = Number(pidText);
|
|
27
|
+
const ts = Number(tsText);
|
|
28
|
+
if (!Number.isFinite(ts))
|
|
29
|
+
return { running: false };
|
|
30
|
+
if (nowMs - ts < WATCHDOG_STALE_MS)
|
|
31
|
+
return { running: true, pid: Number.isFinite(pid) ? pid : undefined };
|
|
32
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
33
|
+
try {
|
|
34
|
+
process.kill(pid, 0); // throws when the pid is gone
|
|
35
|
+
return { running: true, pid };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* stale and dead */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* no lock file */
|
|
44
|
+
}
|
|
45
|
+
return { running: false };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Best-effort spawn of the detached watchdog loop; never throws and never
|
|
49
|
+
* spawns a second instance while one is alive. No-op under vitest so tests
|
|
50
|
+
* cannot leak background processes.
|
|
51
|
+
*/
|
|
52
|
+
export function spawnWatchdog(outboxDir) {
|
|
53
|
+
if (watchdogStatus(outboxDir).running)
|
|
54
|
+
return;
|
|
55
|
+
const entry = process.argv[1];
|
|
56
|
+
if (entry === undefined || entry.includes('vitest'))
|
|
57
|
+
return;
|
|
58
|
+
try {
|
|
59
|
+
const child = spawn(process.execPath, [entry, '__drain', '--watch'], {
|
|
60
|
+
detached: true,
|
|
61
|
+
stdio: 'ignore',
|
|
62
|
+
windowsHide: true,
|
|
63
|
+
});
|
|
64
|
+
child.unref();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// ponytail: watchdog is liveness relief, not correctness; the next send retries the spawn.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** The watchdog loop itself; returns when the outbox is empty. Never throws. */
|
|
71
|
+
export async function runWatchdog(outboxDir, rateDir, deps) {
|
|
72
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
73
|
+
try {
|
|
74
|
+
mkdirSync(outboxDir, { recursive: true });
|
|
75
|
+
writeFileSync(join(outboxDir, WATCHDOG_LOCK), `${process.pid} ${Date.now()}`, 'utf8');
|
|
76
|
+
let emptyRounds = 0;
|
|
77
|
+
for (;;) {
|
|
78
|
+
const results = await drain(outboxDir, (threadId, content, busyTimeoutMs) => deps.deliverCodex(threadId, content, busyTimeoutMs), { rateDir });
|
|
79
|
+
const delivered = results.reduce((total, result) => total + result.delivered, 0);
|
|
80
|
+
const dropped = results.reduce((total, result) => total + result.dropped, 0);
|
|
81
|
+
if (delivered > 0)
|
|
82
|
+
deps.err(`watchdog: 补投 ${delivered} 条`);
|
|
83
|
+
if (dropped > 0)
|
|
84
|
+
deps.err(`watchdog: 线程已不存在,丢弃 ${dropped} 条`);
|
|
85
|
+
if (outboxSummary(outboxDir).every((thread) => thread.count === 0))
|
|
86
|
+
break;
|
|
87
|
+
writeFileSync(join(outboxDir, WATCHDOG_LOCK), `${process.pid} ${Date.now()}`, 'utf8'); // heartbeat
|
|
88
|
+
await sleep(nextBackoffMs(emptyRounds));
|
|
89
|
+
emptyRounds = delivered > 0 ? 0 : emptyRounds + 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Never throw out of the detached loop; the next park re-spawns a fresh watchdog.
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
try {
|
|
97
|
+
rmSync(join(outboxDir, WATCHDOG_LOCK), { force: true });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* already gone */
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=watchdog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchdog.js","sourceRoot":"","sources":["../src/watchdog.ts"],"names":[],"mappings":"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"}
|