@oatelauser/crosschat 1.4.0 → 1.5.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.
@@ -0,0 +1,211 @@
1
+ /**
2
+ * 文件通道(票B B2):接收侧 FileInbox——inbox 收 whole 文件、parts 暂存
3
+ * 分块与断点锚。发送侧的分块循环在 via-tcp.ts,这里只有两端共用的常量、
4
+ * 文件名安全化与接收状态机。路径约定:~/.crosschat/tcp/{inbox,parts}/。
5
+ */
6
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
7
+ import { createHash } from 'node:crypto';
8
+ import { join } from 'node:path';
9
+ import { Buffer } from 'node:buffer';
10
+ /** 单文件上限 64MiB(发送侧读文件时与 broker file-begin 预拒同用此值)。 */
11
+ export const FILE_MAX_BYTES = 64 * 1024 * 1024;
12
+ /** 分块大小 256KiB(base64 后约 350KB,留在一帧 2MiB 上限内)。 */
13
+ export const FILE_CHUNK_BYTES = 256 * 1024;
14
+ /** 懒删除:inbox/parts 落盘超 5 天视为残迹。 */
15
+ export const FILE_STALE_MS = 5 * 24 * 3_600_000;
16
+ /** 懒删除:inbox 总量超 512MiB 从最旧删起。 */
17
+ export const INBOX_MAX_BYTES = 512 * 1024 * 1024;
18
+ /** 原名剥离目录成分与 Windows 危险字符,空则兜底 'file'。 */
19
+ export function safeFileName(name) {
20
+ const base = name.split(/[\\/]/).pop() ?? '';
21
+ // eslint-disable-next-line no-control-regex -- 控制字符必须一并剥掉
22
+ const cleaned = base.replace(/[/\\:*?"<>|\u0000-\u001f]/g, '_').trim();
23
+ return cleaned === '' || cleaned === '.' || cleaned === '..' ? 'file' : cleaned;
24
+ }
25
+ /** 接收完成后的最终路径:inbox/<sha 前 8>-<安全化原名>。 */
26
+ export function inboxPathFor(inboxDir, sha256, name) {
27
+ return join(inboxDir, `${sha256.slice(0, 8)}-${safeFileName(name)}`);
28
+ }
29
+ /**
30
+ * 一次 file-begin..file-end 的接收状态机(多 id 并发各写各的 parts 文件)。
31
+ * 断点锚 parts/&lt;sha&gt;.part.json{nextSeq} 每块落盘后更新——传输中断后
32
+ * 重新 begin 会拿到 resumeFrom=nextSeq,发方从该块续传。
33
+ */
34
+ export class FileInbox {
35
+ dirs;
36
+ now;
37
+ transfers = new Map();
38
+ constructor(dirs, now = () => Date.now()) {
39
+ this.dirs = dirs;
40
+ this.now = now;
41
+ }
42
+ /** file-begin 应答:去重秒回 / 断点续传锚 / 全新开始(顺带懒删除)。 */
43
+ begin(frame) {
44
+ this.lazyDelete();
45
+ const { sha256, name, size } = frame;
46
+ const finalPath = inboxPathFor(this.dirs.inboxDir, sha256, name);
47
+ if (existsSync(finalPath)) {
48
+ return { op: 'file-result', id: frame.id, ok: true, path: finalPath, dedup: true };
49
+ }
50
+ this.transfers.set(frame.id, { sha256, name, size });
51
+ const anchor = this.readAnchor(sha256);
52
+ if (anchor !== undefined && anchor.nextSeq > 0) {
53
+ return { op: 'file-result', id: frame.id, ok: true, resumeFrom: anchor.nextSeq };
54
+ }
55
+ // 无锚或锚损坏:清掉可能存在的残片,从零开始(残片混写必致 sha 失败)
56
+ this.cleanup(sha256);
57
+ this.writeAnchor(sha256, 0);
58
+ return { op: 'file-result', id: frame.id, ok: true, resumeFrom: 0 };
59
+ }
60
+ /** 顺序落盘一块;seq 必须等于锚的 nextSeq(发方遵守 resumeFrom 时恒真)。 */
61
+ chunk(frame) {
62
+ const t = this.transfers.get(frame.id);
63
+ if (t === undefined)
64
+ return false;
65
+ const anchor = this.readAnchor(t.sha256);
66
+ if (anchor === undefined || anchor.nextSeq !== frame.seq)
67
+ return false;
68
+ let bytes;
69
+ try {
70
+ bytes = Buffer.from(frame.bytes, 'base64');
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ mkdirSync(this.dirs.partsDir, { recursive: true });
76
+ appendFileSync(this.partFile(t.sha256), bytes);
77
+ this.writeAnchor(t.sha256, frame.seq + 1);
78
+ return true;
79
+ }
80
+ /** file-end:sha256 校验 → 改名转正 → 清残片;失败删残片要求整档重传。 */
81
+ end(id) {
82
+ const t = this.transfers.get(id);
83
+ if (t === undefined) {
84
+ return { op: 'file-result', id, ok: false, error: '未知传输 id(可能已被清理)' };
85
+ }
86
+ this.transfers.delete(id);
87
+ const part = this.partFile(t.sha256);
88
+ let ok = false;
89
+ try {
90
+ ok = existsSync(part) && sha256File(part) === t.sha256;
91
+ }
92
+ catch {
93
+ ok = false;
94
+ }
95
+ if (!ok) {
96
+ this.cleanup(t.sha256);
97
+ return {
98
+ op: 'file-result',
99
+ id,
100
+ ok: false,
101
+ error: 'sha256 校验失败——残片已清理,请整档重传(重跑同一命令即可)',
102
+ };
103
+ }
104
+ const finalPath = inboxPathFor(this.dirs.inboxDir, t.sha256, t.name);
105
+ mkdirSync(this.dirs.inboxDir, { recursive: true });
106
+ // 同名不同 sha 不可能(sha 在文件名里);同 sha 同名已提前 dedup
107
+ renameSync(part, finalPath);
108
+ this.cleanup(t.sha256);
109
+ return { op: 'file-result', id, ok: true, path: finalPath };
110
+ }
111
+ partFile(sha) {
112
+ return join(this.dirs.partsDir, `${sha}.part`);
113
+ }
114
+ anchorFile(sha) {
115
+ return join(this.dirs.partsDir, `${sha}.part.json`);
116
+ }
117
+ /** 无锚/锚损坏统一 undefined(损坏时 begin 会连残片一起清掉重来)。 */
118
+ readAnchor(sha) {
119
+ try {
120
+ const parsed = JSON.parse(readFileSync(this.anchorFile(sha), 'utf8'));
121
+ return typeof parsed?.nextSeq === 'number' ? { nextSeq: parsed.nextSeq } : undefined;
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ }
127
+ writeAnchor(sha, nextSeq) {
128
+ mkdirSync(this.dirs.partsDir, { recursive: true });
129
+ writeFileSync(this.anchorFile(sha), `${JSON.stringify({ nextSeq })}\n`, 'utf8');
130
+ }
131
+ cleanup(sha) {
132
+ rmSync(this.partFile(sha), { force: true });
133
+ rmSync(this.anchorFile(sha), { force: true });
134
+ }
135
+ /**
136
+ * 懒删除(每次 file-begin 顺带执行):inbox 落盘 >5 天删;inbox 总量
137
+ * >512MiB 从最旧删起;parts 残片同 5 天规则。best-effort——单文件删除
138
+ * 失败不阻断传输。
139
+ */
140
+ lazyDelete() {
141
+ const cutoff = this.now() - FILE_STALE_MS;
142
+ const statOf = (file) => {
143
+ try {
144
+ const s = statSync(file);
145
+ return { mtime: s.mtimeMs, size: s.size };
146
+ }
147
+ catch {
148
+ return undefined;
149
+ }
150
+ };
151
+ try {
152
+ const entries = readdirSync(this.dirs.inboxDir).map((f) => ({
153
+ file: join(this.dirs.inboxDir, f),
154
+ ...statOf(join(this.dirs.inboxDir, f)),
155
+ }));
156
+ for (const e of entries) {
157
+ if (e.mtime !== undefined && e.mtime < cutoff) {
158
+ try {
159
+ rmSync(e.file, { force: true });
160
+ }
161
+ catch { /* best-effort */ }
162
+ }
163
+ }
164
+ const live = entries
165
+ .filter((e) => e.mtime !== undefined && e.mtime >= cutoff && existsSync(e.file))
166
+ .sort((a, b) => a.mtime - b.mtime);
167
+ let total = live.reduce((sum, e) => sum + (e.size ?? 0), 0);
168
+ for (const e of live) {
169
+ if (total <= INBOX_MAX_BYTES)
170
+ break;
171
+ try {
172
+ rmSync(e.file, { force: true });
173
+ total -= e.size ?? 0;
174
+ }
175
+ catch { /* best-effort */ }
176
+ }
177
+ }
178
+ catch {
179
+ // inbox 目录不存在等——首份文件会建目录
180
+ }
181
+ try {
182
+ for (const f of readdirSync(this.dirs.partsDir)) {
183
+ const file = join(this.dirs.partsDir, f);
184
+ const s = statOf(file);
185
+ if (s !== undefined && s.mtime < cutoff) {
186
+ try {
187
+ rmSync(file, { force: true });
188
+ }
189
+ catch { /* best-effort */ }
190
+ }
191
+ }
192
+ }
193
+ catch {
194
+ // parts 目录不存在——忽略
195
+ }
196
+ }
197
+ }
198
+ function sha256File(file) {
199
+ return createHash('sha256').update(readFileSync(file)).digest('hex');
200
+ }
201
+ /** 发送侧文件读取 + 摘要(64MiB 预检在调用方)。 */
202
+ export function readFileForSend(path) {
203
+ const bytes = readFileSync(path);
204
+ return {
205
+ bytes,
206
+ sha256: createHash('sha256').update(bytes).digest('hex'),
207
+ name: (path.split(/[\\/]/).pop() ?? 'file'),
208
+ size: bytes.length,
209
+ };
210
+ }
211
+ //# sourceMappingURL=file-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-channel.js","sourceRoot":"","sources":["../../src/serve/file-channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,340 @@
1
+ /**
2
+ * 节点客户端(票B B2):`crosschat serve --connect` 的常驻腿——连 broker、
3
+ * 收 deliver 帧进程内调 runSend(与 ssh 远端 CLI 同权:会话状态/限流/发件箱
4
+ * 全是本机既有行为)、收 file-* 帧落盘 inbox。断线指数退避 1s→2s→…封顶
5
+ * 60s 永不放弃;连续失败 ≥5 分钟进静音模式(日志每 10 次一条)。
6
+ */
7
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
8
+ import { dirname } from 'node:path';
9
+ import { WebSocket } from 'ws';
10
+ import { MultichatError } from '../errors.js';
11
+ import { encodeFrame, MAX_PAYLOAD_BYTES, parseFrame, PROTOCOL_VERSION } from './protocol.js';
12
+ import { runSend } from '../commands/send.js';
13
+ /** 会员凭证缺失/损坏的教学报错(send --via tcp: 与 --connect 同用)。 */
14
+ export const NOT_JOINED_MESSAGE = '本机未入网——向管理员索取 node.json 放到 ~/.crosschat/tcp/(broker 机上 crosschat serve --gen-key 生成)';
15
+ export function readNodeConfig(configFile) {
16
+ let raw;
17
+ try {
18
+ raw = readFileSync(configFile, 'utf8');
19
+ }
20
+ catch {
21
+ throw new MultichatError('USAGE', NOT_JOINED_MESSAGE);
22
+ }
23
+ try {
24
+ const parsed = JSON.parse(raw);
25
+ if (parsed === null || typeof parsed !== 'object' ||
26
+ typeof parsed.key !== 'string' || parsed.key === '' ||
27
+ typeof parsed.broker?.host !== 'string' || !Number.isInteger(parsed.broker.port)) {
28
+ throw new Error('bad shape');
29
+ }
30
+ return parsed;
31
+ }
32
+ catch {
33
+ throw new MultichatError('USAGE', `node.json 损坏(${configFile})——向管理员重新索取覆盖;${NOT_JOINED_MESSAGE}`);
34
+ }
35
+ }
36
+ /**
37
+ * deliver 帧 → 本机 runSend(broker 侧 deliverLocal 同用此桥):
38
+ * payload=正文,to/conversation/origin 透传;origin 是发送机的
39
+ * encodeOriginIdentity 编码(ssh 远端 CLI 的 --origin 同语义)。
40
+ */
41
+ export async function deliverViaRunSend(frame, deps) {
42
+ let output;
43
+ try {
44
+ output = await runSend({
45
+ to: frame.to,
46
+ conversation: frame.conversation,
47
+ bodyArg: frame.payload,
48
+ origin: frame.origin,
49
+ json: true,
50
+ }, deps);
51
+ }
52
+ catch (err) {
53
+ if (err instanceof MultichatError) {
54
+ return { op: 'result', id: frame.id, status: 'error', code: err.code, message: err.message };
55
+ }
56
+ return {
57
+ op: 'result',
58
+ id: frame.id,
59
+ status: 'error',
60
+ code: 'INTERNAL',
61
+ message: err instanceof Error ? err.message : String(err),
62
+ };
63
+ }
64
+ // runSend --json 输出:{status,to,turn,replyRef[,queue,mailbox]}
65
+ try {
66
+ const receipt = JSON.parse(output);
67
+ return {
68
+ op: 'result',
69
+ id: frame.id,
70
+ status: receipt.status,
71
+ turn: receipt.turn === undefined ? undefined : String(receipt.turn),
72
+ replyRef: receipt.replyRef,
73
+ output,
74
+ };
75
+ }
76
+ catch {
77
+ return { op: 'result', id: frame.id, status: 'error', code: 'INTERNAL', message: `本机回执不是 JSON: ${output.slice(0, 200)}` };
78
+ }
79
+ }
80
+ export class NodeClient {
81
+ d;
82
+ stopped = false;
83
+ ws;
84
+ state = 'connecting';
85
+ stateSince = 0;
86
+ frameWaiters = [];
87
+ /** 本连接已收帧(open 与 nextFrame 注册之间到达的帧先落此,注册时回查)。 */
88
+ frameLog = [];
89
+ stateWaiters = [];
90
+ minuteTimer;
91
+ mainLoop;
92
+ stopResolve;
93
+ constructor(deps) {
94
+ this.d = { backoffBaseMs: 1_000, quietAfterMs: 300_000, ...deps };
95
+ }
96
+ /** 阻塞跑直到 stop(serve --connect 前台姿态)。 */
97
+ async run() {
98
+ this.d.onSignal?.('SIGINT', () => void this.stop());
99
+ this.d.onSignal?.('SIGTERM', () => void this.stop());
100
+ this.mainLoop = this.loop();
101
+ await new Promise((resolve) => {
102
+ this.stopResolve = resolve;
103
+ });
104
+ await this.mainLoop;
105
+ }
106
+ /** 停主循环、关连接(幂等);打断退避睡眠立即返回。 */
107
+ async stop() {
108
+ if (this.stopped) {
109
+ await this.mainLoop?.catch(() => { });
110
+ return;
111
+ }
112
+ this.stopped = true;
113
+ this.clearFrameWaiters(new Error('stopped'));
114
+ try {
115
+ this.ws?.close(1000, 'node stopping');
116
+ }
117
+ catch { /* 竞态关闭忽略 */ }
118
+ clearInterval(this.minuteTimer);
119
+ this.stopResolve?.();
120
+ await this.mainLoop?.catch(() => { });
121
+ }
122
+ /** 等到指定状态(测试用);超时拒绝。 */
123
+ waitForState(state, timeoutMs = 5_000) {
124
+ if (this.state === state)
125
+ return Promise.resolve();
126
+ return new Promise((resolve, reject) => {
127
+ const waiter = {
128
+ state,
129
+ resolve,
130
+ timer: setTimeout(() => {
131
+ const i = this.stateWaiters.indexOf(waiter);
132
+ if (i >= 0)
133
+ this.stateWaiters.splice(i, 1);
134
+ reject(new Error(`waitForState(${state}) timeout`));
135
+ }, timeoutMs),
136
+ };
137
+ this.stateWaiters.push(waiter);
138
+ });
139
+ }
140
+ async loop() {
141
+ let attempt = 0;
142
+ let firstFailAt;
143
+ while (!this.stopped) {
144
+ this.setState('connecting');
145
+ const everOnline = await this.connectOnce();
146
+ if (this.stopped)
147
+ break;
148
+ attempt = everOnline ? 0 : attempt + 1;
149
+ if (attempt === 1)
150
+ firstFailAt = this.d.now();
151
+ const failedFor = firstFailAt === undefined ? 0 : this.d.now() - firstFailAt;
152
+ const quiet = failedFor >= this.d.quietAfterMs;
153
+ const delay = Math.min(this.d.backoffBaseMs * 2 ** Math.max(0, attempt - 1), 60_000);
154
+ if (!quiet || attempt % 10 === 1) {
155
+ this.log(`reconnect-wait 第 ${attempt} 次失败后 ${delay}ms 再试(连续失败 ${Math.round(failedFor / 1000)}s)`);
156
+ }
157
+ this.setState('reconnect-wait');
158
+ this.writeStatus(this.d.now() + delay);
159
+ await this.sleepInterruptible(delay);
160
+ }
161
+ }
162
+ /** 一次连接生命周期;resolve(false)=从未上线即断,resolve(true)=上线后断开。 */
163
+ async connectOnce() {
164
+ const cfg = this.d.config;
165
+ const url = `ws://${cfg.broker.host}:${cfg.broker.port}`;
166
+ this.frameLog = [];
167
+ const ws = new WebSocket(url, { maxPayload: MAX_PAYLOAD_BYTES });
168
+ this.ws = ws;
169
+ let everOnline = false;
170
+ let closed = () => { };
171
+ const closedPromise = new Promise((resolve) => {
172
+ closed = resolve;
173
+ });
174
+ ws.once('close', () => closed());
175
+ ws.once('error', () => closed());
176
+ ws.on('message', (data) => {
177
+ void this.onFrame(data.toString());
178
+ });
179
+ try {
180
+ await new Promise((resolve, reject) => {
181
+ const t = setTimeout(() => reject(new Error('connect timeout')), 15_000);
182
+ ws.once('open', () => { clearTimeout(t); resolve(); });
183
+ ws.once('error', (err) => { clearTimeout(t); reject(err); });
184
+ });
185
+ const info = await this.nextFrame((f) => f.op === 'info', 10_000);
186
+ if (info.op !== 'info' || info.proto !== PROTOCOL_VERSION) {
187
+ throw new Error(`协议不匹配:服务端 ${info.op === 'info' ? info.proto : '?'}`);
188
+ }
189
+ ws.send(encodeFrame({
190
+ op: 'connect',
191
+ proto: PROTOCOL_VERSION,
192
+ role: 'node',
193
+ machineId: this.d.machineId() ?? '',
194
+ auth: cfg.key,
195
+ ...(cfg.name === undefined ? {} : { name: cfg.name }),
196
+ }));
197
+ const verdict = await this.nextFrame((f) => f.op === 'connected' || f.op === 'error', 10_000);
198
+ if (verdict.op === 'error') {
199
+ throw new Error(`broker 拒绝: ${verdict.code} ${verdict.message}`);
200
+ }
201
+ everOnline = true;
202
+ this.setState('online');
203
+ this.log(`online(broker ${cfg.broker.alias ?? url})`);
204
+ this.minuteTimer = setInterval(() => {
205
+ if (this.state === 'online')
206
+ this.writeStatus(undefined);
207
+ }, 60_000);
208
+ await closedPromise;
209
+ this.log('offline(连接断开)');
210
+ }
211
+ catch (err) {
212
+ this.log(`connect 失败: ${err instanceof Error ? err.message : String(err)}`);
213
+ }
214
+ finally {
215
+ clearInterval(this.minuteTimer);
216
+ this.clearFrameWaiters(new Error('connection closed'));
217
+ try {
218
+ ws.close();
219
+ }
220
+ catch { /* 已断开 */ }
221
+ if (this.ws === ws)
222
+ this.ws = undefined;
223
+ }
224
+ return everOnline;
225
+ }
226
+ async onFrame(text) {
227
+ const frame = parseFrame(text);
228
+ if (frame === undefined)
229
+ return;
230
+ this.frameLog.push(frame);
231
+ for (let i = 0; i < this.frameWaiters.length; i++) {
232
+ const w = this.frameWaiters[i];
233
+ if (w.pred(frame)) {
234
+ clearTimeout(w.timer);
235
+ this.frameWaiters.splice(i, 1);
236
+ w.resolve(frame);
237
+ return;
238
+ }
239
+ }
240
+ if (frame.op === 'deliver') {
241
+ void this.onDeliver(frame);
242
+ return;
243
+ }
244
+ if (frame.op === 'file-begin' || frame.op === 'file-chunk' || frame.op === 'file-end') {
245
+ this.onFile(frame);
246
+ }
247
+ }
248
+ async onDeliver(frame) {
249
+ const result = await deliverViaRunSend(frame, this.d.makeSendDeps());
250
+ this.send(result);
251
+ this.log(`deliver ${frame.id} → ${result.status}${result.status === 'error' ? ` (${result.code})` : ''}`);
252
+ }
253
+ onFile(frame) {
254
+ if (frame.op === 'file-begin') {
255
+ this.send(this.d.fileInbox.begin(frame));
256
+ return;
257
+ }
258
+ if (frame.op === 'file-chunk') {
259
+ if (!this.d.fileInbox.chunk(frame)) {
260
+ this.send({ op: 'file-result', id: frame.id, ok: false, error: '分块失序或未知 id——请重新 begin 续传' });
261
+ }
262
+ return;
263
+ }
264
+ const res = this.d.fileInbox.end(frame.id);
265
+ this.send(res);
266
+ this.log(`file ${frame.id} ${res.ok ? `→ ${res.path}` : `失败: ${res.error}`}`);
267
+ }
268
+ nextFrame(pred, timeoutMs) {
269
+ const seen = this.frameLog.find(pred);
270
+ if (seen !== undefined)
271
+ return Promise.resolve(seen);
272
+ return new Promise((resolve, reject) => {
273
+ const waiter = {
274
+ pred,
275
+ resolve,
276
+ reject,
277
+ timer: setTimeout(() => {
278
+ const i = this.frameWaiters.indexOf(waiter);
279
+ if (i >= 0)
280
+ this.frameWaiters.splice(i, 1);
281
+ reject(new Error('frame timeout'));
282
+ }, timeoutMs),
283
+ };
284
+ this.frameWaiters.push(waiter);
285
+ });
286
+ }
287
+ clearFrameWaiters(err) {
288
+ for (const w of this.frameWaiters.splice(0)) {
289
+ clearTimeout(w.timer);
290
+ w.reject(err);
291
+ }
292
+ }
293
+ send(frame) {
294
+ if (this.ws !== undefined && this.ws.readyState === WebSocket.OPEN) {
295
+ this.ws.send(encodeFrame(frame));
296
+ }
297
+ }
298
+ setState(state) {
299
+ this.state = state;
300
+ this.stateSince = this.d.now();
301
+ this.writeStatus(undefined);
302
+ for (let i = this.stateWaiters.length - 1; i >= 0; i--) {
303
+ const w = this.stateWaiters[i];
304
+ if (w.state !== state)
305
+ continue;
306
+ clearTimeout(w.timer);
307
+ this.stateWaiters.splice(i, 1);
308
+ w.resolve();
309
+ }
310
+ }
311
+ writeStatus(nextAttemptAt) {
312
+ const status = {
313
+ state: this.state,
314
+ brokerAlias: this.d.config.broker.alias ?? `${this.d.config.broker.host}:${this.d.config.broker.port}`,
315
+ since: new Date(this.stateSince).toISOString(),
316
+ lastUpdated: new Date(this.d.now()).toISOString(),
317
+ ...(nextAttemptAt === undefined ? {} : { nextAttemptAt: new Date(nextAttemptAt).toISOString() }),
318
+ };
319
+ try {
320
+ mkdirSync(dirname(this.d.statusFile), { recursive: true });
321
+ writeFileSync(this.d.statusFile, `${JSON.stringify(status, null, 2)}\n`, 'utf8');
322
+ }
323
+ catch {
324
+ // 状态文件写失败不倒客户端
325
+ }
326
+ }
327
+ /** 退避睡眠可被 stop 打断(100ms 片轮询),避免 SIGINT 卡最长 60s。 */
328
+ async sleepInterruptible(ms) {
329
+ let left = ms;
330
+ while (left > 0 && !this.stopped) {
331
+ const slice = Math.min(left, 100);
332
+ await new Promise((resolve) => setTimeout(resolve, slice));
333
+ left -= slice;
334
+ }
335
+ }
336
+ log(event) {
337
+ this.d.log(`${new Date(this.d.now()).toISOString()} ${event}`);
338
+ }
339
+ }
340
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/serve/node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * tcp broker 协议层(票B B1):单文件、客户端服务端共用。JSON 单帧单 ws
3
+ * 文本消息。B2 才有行为实现的帧(file-* 等)类型先定义全。
4
+ */
5
+ export const PROTOCOL_VERSION = 1;
6
+ export const MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
7
+ /** 协议错误码(error.code),与各端报错文案共用。 */
8
+ export const ERR = {
9
+ BAD_AUTH: 'BAD_AUTH',
10
+ BAD_PROTO: 'BAD_PROTO',
11
+ UNKNOWN_MACHINE: 'UNKNOWN_MACHINE',
12
+ PEER_OFFLINE: 'PEER_OFFLINE',
13
+ MACHINE_NAME_COLLISION: 'MACHINE_NAME_COLLISION',
14
+ NOT_READY: 'NOT_READY',
15
+ /** B2 修订:file-begin 尺寸预拒(>64MiB)。 */
16
+ FILE_TOO_LARGE: 'FILE_TOO_LARGE',
17
+ };
18
+ const OPS = new Set([
19
+ 'info',
20
+ 'connect',
21
+ 'connected',
22
+ 'error',
23
+ 'send',
24
+ 'receipt',
25
+ 'deliver',
26
+ 'result',
27
+ 'file-begin',
28
+ 'file-chunk',
29
+ 'file-end',
30
+ 'file-result',
31
+ 'roster',
32
+ 'roster-result',
33
+ ]);
34
+ /** 解析一帧 ws 文本消息;JSON 损坏或 op 未知返回 undefined(调用方丢弃)。 */
35
+ export function parseFrame(text) {
36
+ let obj;
37
+ try {
38
+ obj = JSON.parse(text);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ if (typeof obj !== 'object' || obj === null)
44
+ return undefined;
45
+ const op = obj.op;
46
+ return typeof op === 'string' && OPS.has(op) ? obj : undefined;
47
+ }
48
+ export function encodeFrame(frame) {
49
+ return JSON.stringify(frame);
50
+ }
51
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/serve/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjD,mCAAmC;AACnC,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,eAAe,EAAE,iBAAiB;IAClC,YAAY,EAAE,cAAc;IAC5B,sBAAsB,EAAE,wBAAwB;IAChD,SAAS,EAAE,WAAW;IACtB,qCAAqC;IACrC,cAAc,EAAE,gBAAgB;CACxB,CAAC;AAgIX,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAClB,MAAM;IACN,SAAS;IACT,WAAW;IACX,OAAO;IACP,MAAM;IACN,SAAS;IACT,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,aAAa;IACb,QAAQ;IACR,eAAe;CAChB,CAAC,CAAC;AAEH,sDAAsD;AACtD,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,EAAE,GAAI,GAAwB,CAAC,EAAE,CAAC;IACxC,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,GAAa,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC"}