sessix-server 0.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.
Files changed (61) hide show
  1. package/dist/approval/ApprovalProxy.d.ts +86 -0
  2. package/dist/approval/ApprovalProxy.d.ts.map +1 -0
  3. package/dist/approval/ApprovalProxy.js +363 -0
  4. package/dist/approval/ApprovalProxy.js.map +1 -0
  5. package/dist/hooks/HookInstaller.d.ts +55 -0
  6. package/dist/hooks/HookInstaller.d.ts.map +1 -0
  7. package/dist/hooks/HookInstaller.js +215 -0
  8. package/dist/hooks/HookInstaller.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +3115 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/mdns/MdnsService.d.ts +36 -0
  14. package/dist/mdns/MdnsService.d.ts.map +1 -0
  15. package/dist/mdns/MdnsService.js +66 -0
  16. package/dist/mdns/MdnsService.js.map +1 -0
  17. package/dist/notification/ActivityPushChannel.d.ts +54 -0
  18. package/dist/notification/ActivityPushChannel.d.ts.map +1 -0
  19. package/dist/notification/ActivityPushChannel.js +235 -0
  20. package/dist/notification/ActivityPushChannel.js.map +1 -0
  21. package/dist/notification/ExpoNotificationChannel.d.ts +17 -0
  22. package/dist/notification/ExpoNotificationChannel.d.ts.map +1 -0
  23. package/dist/notification/ExpoNotificationChannel.js +57 -0
  24. package/dist/notification/ExpoNotificationChannel.js.map +1 -0
  25. package/dist/notification/MacNotificationChannel.d.ts +22 -0
  26. package/dist/notification/MacNotificationChannel.d.ts.map +1 -0
  27. package/dist/notification/MacNotificationChannel.js +33 -0
  28. package/dist/notification/MacNotificationChannel.js.map +1 -0
  29. package/dist/notification/NotificationService.d.ts +50 -0
  30. package/dist/notification/NotificationService.d.ts.map +1 -0
  31. package/dist/notification/NotificationService.js +177 -0
  32. package/dist/notification/NotificationService.js.map +1 -0
  33. package/dist/providers/ExecutionProvider.d.ts +60 -0
  34. package/dist/providers/ExecutionProvider.d.ts.map +1 -0
  35. package/dist/providers/ExecutionProvider.js +3 -0
  36. package/dist/providers/ExecutionProvider.js.map +1 -0
  37. package/dist/providers/ProcessProvider.d.ts +117 -0
  38. package/dist/providers/ProcessProvider.d.ts.map +1 -0
  39. package/dist/providers/ProcessProvider.js +507 -0
  40. package/dist/providers/ProcessProvider.js.map +1 -0
  41. package/dist/server.d.ts +32 -0
  42. package/dist/server.d.ts.map +1 -0
  43. package/dist/server.js +3054 -0
  44. package/dist/server.js.map +1 -0
  45. package/dist/session/ProjectReader.d.ts +44 -0
  46. package/dist/session/ProjectReader.d.ts.map +1 -0
  47. package/dist/session/ProjectReader.js +471 -0
  48. package/dist/session/ProjectReader.js.map +1 -0
  49. package/dist/session/SessionFileWatcher.d.ts +35 -0
  50. package/dist/session/SessionFileWatcher.d.ts.map +1 -0
  51. package/dist/session/SessionFileWatcher.js +207 -0
  52. package/dist/session/SessionFileWatcher.js.map +1 -0
  53. package/dist/session/SessionManager.d.ts +114 -0
  54. package/dist/session/SessionManager.d.ts.map +1 -0
  55. package/dist/session/SessionManager.js +356 -0
  56. package/dist/session/SessionManager.js.map +1 -0
  57. package/dist/ws/WsBridge.d.ts +55 -0
  58. package/dist/ws/WsBridge.d.ts.map +1 -0
  59. package/dist/ws/WsBridge.js +220 -0
  60. package/dist/ws/WsBridge.js.map +1 -0
  61. package/package.json +38 -0
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SessionFileWatcher = void 0;
7
+ const chokidar_1 = __importDefault(require("chokidar"));
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_readline_1 = require("node:readline");
10
+ /**
11
+ * 监听 JSONL 会话文件的新增内容,推送到手机端。
12
+ *
13
+ * 使用场景:用户从 Sessix App 切到 VS Code(或其他客户端)继续会话后,
14
+ * Sessix 不再管理该进程,但可以通过监听 JSONL 文件实时获取新消息。
15
+ *
16
+ * 轻度监听策略:
17
+ * - 使用 chokidar 原生 FS 事件(非 polling),CPU 开销极低
18
+ * - awaitWriteFinish 300ms 防抖,避免每次字节写入都触发
19
+ * - 10 分钟无变化自动停止,不持续占用资源
20
+ * - 只跟踪"字节偏移量",每次仅读新增部分
21
+ */
22
+ class SessionFileWatcher {
23
+ watchers = new Map();
24
+ onEvent;
25
+ /** 文件无变化后自动停止监听的超时时间(10 分钟) */
26
+ IDLE_TIMEOUT_MS = 10 * 60 * 1000;
27
+ constructor(onEvent) {
28
+ this.onEvent = onEvent;
29
+ }
30
+ /**
31
+ * 开始监听指定会话的 JSONL 文件新增内容
32
+ *
33
+ * @param sessionId 会话 ID
34
+ * @param filePath JSONL 文件绝对路径
35
+ * @param byteOffset 已读到的字节位置(跳过历史内容,只推送新行)
36
+ */
37
+ watch(sessionId, filePath, byteOffset) {
38
+ // 同一会话不重复启动
39
+ if (this.watchers.has(sessionId))
40
+ return;
41
+ const watcher = chokidar_1.default.watch(filePath, {
42
+ persistent: false, // 不阻止进程退出
43
+ usePolling: false, // 使用原生 FS 事件,不轮询
44
+ awaitWriteFinish: {
45
+ stabilityThreshold: 300, // 写入停止 300ms 后才触发
46
+ pollInterval: 100,
47
+ },
48
+ });
49
+ const entry = {
50
+ filePath,
51
+ byteOffset,
52
+ idleTimer: null,
53
+ watcher,
54
+ };
55
+ watcher.on('change', () => {
56
+ this.readNewLines(sessionId).catch((err) => {
57
+ console.error(`[SessionFileWatcher] 读取异常 ${sessionId}:`, err);
58
+ });
59
+ this.resetIdleTimer(sessionId);
60
+ });
61
+ this.watchers.set(sessionId, entry);
62
+ this.resetIdleTimer(sessionId);
63
+ console.log(`[SessionFileWatcher] 开始监听: ${sessionId} (offset=${byteOffset})`);
64
+ }
65
+ /** 停止监听指定会话 */
66
+ unwatch(sessionId) {
67
+ const entry = this.watchers.get(sessionId);
68
+ if (!entry)
69
+ return;
70
+ if (entry.idleTimer)
71
+ clearTimeout(entry.idleTimer);
72
+ void entry.watcher.close();
73
+ this.watchers.delete(sessionId);
74
+ console.log(`[SessionFileWatcher] 停止监听: ${sessionId}`);
75
+ }
76
+ /** 停止所有监听(服务关闭时调用) */
77
+ destroy() {
78
+ for (const sessionId of [...this.watchers.keys()]) {
79
+ this.unwatch(sessionId);
80
+ }
81
+ }
82
+ // ============================================
83
+ // 内部方法
84
+ // ============================================
85
+ resetIdleTimer(sessionId) {
86
+ const entry = this.watchers.get(sessionId);
87
+ if (!entry)
88
+ return;
89
+ if (entry.idleTimer)
90
+ clearTimeout(entry.idleTimer);
91
+ entry.idleTimer = setTimeout(() => {
92
+ console.log(`[SessionFileWatcher] 空闲超时,停止监听: ${sessionId}`);
93
+ this.unwatch(sessionId);
94
+ }, this.IDLE_TIMEOUT_MS);
95
+ }
96
+ async readNewLines(sessionId) {
97
+ const entry = this.watchers.get(sessionId);
98
+ if (!entry)
99
+ return;
100
+ let fileHandle;
101
+ try {
102
+ fileHandle = await (0, promises_1.open)(entry.filePath, 'r');
103
+ const fileStat = await fileHandle.stat();
104
+ const newSize = fileStat.size;
105
+ // 文件没有变大(可能是 mtime 刷新但无新内容)
106
+ if (newSize <= entry.byteOffset)
107
+ return;
108
+ const rl = (0, node_readline_1.createInterface)({
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ input: fileHandle.createReadStream({ start: entry.byteOffset, encoding: 'utf-8' }),
111
+ crlfDelay: Infinity,
112
+ });
113
+ const newEvents = [];
114
+ let isCompleted = false;
115
+ let isError = false;
116
+ for await (const line of rl) {
117
+ if (!line.trim())
118
+ continue;
119
+ const parsed = parseJSONLLine(line, sessionId);
120
+ if (parsed.type === 'event' && parsed.event) {
121
+ newEvents.push(parsed.event);
122
+ }
123
+ else if (parsed.type === 'completed') {
124
+ isCompleted = true;
125
+ isError = parsed.isError;
126
+ }
127
+ }
128
+ // 更新偏移量(标记为已读)
129
+ entry.byteOffset = newSize;
130
+ // 推送新消息
131
+ for (const event of newEvents) {
132
+ this.onEvent({ type: 'claude_event', sessionId, event });
133
+ }
134
+ // 会话结束:推送状态变更并停止监听
135
+ if (isCompleted) {
136
+ this.onEvent({ type: 'status_change', sessionId, status: isError ? 'error' : 'idle' });
137
+ this.unwatch(sessionId);
138
+ }
139
+ }
140
+ finally {
141
+ await fileHandle?.close();
142
+ }
143
+ }
144
+ }
145
+ exports.SessionFileWatcher = SessionFileWatcher;
146
+ /**
147
+ * 解析一行 JSONL,转为 ClaudeStreamEvent(与 getSessionHistory 解析逻辑一致)
148
+ */
149
+ function parseJSONLLine(line, sessionId) {
150
+ try {
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ const obj = JSON.parse(line);
153
+ if (obj.type === 'user' && obj.message) {
154
+ const msgContent = obj.message.content;
155
+ // 过滤系统命令消息
156
+ if (typeof msgContent === 'string') {
157
+ if (msgContent.includes('<local-command') || msgContent.includes('<command-name>')) {
158
+ return { type: 'skip' };
159
+ }
160
+ }
161
+ const normalizedContent = typeof msgContent === 'string'
162
+ ? [{ type: 'text', text: msgContent }]
163
+ : Array.isArray(msgContent)
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
+ ? msgContent.filter((b) => b.type === 'text' && typeof b.text === 'string')
166
+ : [];
167
+ if (normalizedContent.length === 0)
168
+ return { type: 'skip' };
169
+ return {
170
+ type: 'event',
171
+ event: {
172
+ type: 'user',
173
+ message: { ...obj.message, content: normalizedContent },
174
+ session_id: sessionId,
175
+ },
176
+ };
177
+ }
178
+ if (obj.type === 'assistant' && obj.message) {
179
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
+ const content = (obj.message.content ?? []).filter((b) => b.type === 'text' || b.type === 'tool_use');
181
+ if (content.length === 0)
182
+ return { type: 'skip' };
183
+ return {
184
+ type: 'event',
185
+ event: {
186
+ type: 'assistant',
187
+ message: {
188
+ id: obj.message.id ?? obj.uuid ?? 'unknown',
189
+ model: obj.message.model ?? 'unknown',
190
+ role: 'assistant',
191
+ content,
192
+ stop_reason: obj.message.stop_reason,
193
+ },
194
+ session_id: sessionId,
195
+ },
196
+ };
197
+ }
198
+ if (obj.type === 'result') {
199
+ return { type: 'completed', isError: !!obj.is_error };
200
+ }
201
+ return { type: 'skip' };
202
+ }
203
+ catch {
204
+ return { type: 'skip' };
205
+ }
206
+ }
207
+ //# sourceMappingURL=SessionFileWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionFileWatcher.js","sourceRoot":"","sources":["../../src/session/SessionFileWatcher.ts"],"names":[],"mappings":";;;;;;AAAA,wDAAmD;AACnD,+CAAuC;AACvC,iDAA+C;AAG/C;;;;;;;;;;;GAWG;AACH,MAAa,kBAAkB;IACrB,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAA;IACxC,OAAO,CAA8B;IAE7C,+BAA+B;IACd,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IAEjD,YAAY,OAAqC;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAiB,EAAE,QAAgB,EAAE,UAAkB;QAC3D,YAAY;QACZ,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAM;QAExC,MAAM,OAAO,GAAG,kBAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE;YACvC,UAAU,EAAE,KAAK,EAAI,UAAU;YAC/B,UAAU,EAAE,KAAK,EAAI,iBAAiB;YACtC,gBAAgB,EAAE;gBAChB,kBAAkB,EAAE,GAAG,EAAG,kBAAkB;gBAC5C,YAAY,EAAE,GAAG;aAClB;SACF,CAAC,CAAA;QAEF,MAAM,KAAK,GAAe;YACxB,QAAQ;YACR,UAAU;YACV,SAAS,EAAE,IAAI;YACf,OAAO;SACR,CAAA;QAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACxB,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACzC,OAAO,CAAC,KAAK,CAAC,6BAA6B,SAAS,GAAG,EAAE,GAAG,CAAC,CAAA;YAC/D,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;QAE9B,OAAO,CAAC,GAAG,CAAC,8BAA8B,SAAS,YAAY,UAAU,GAAG,CAAC,CAAA;IAC/E,CAAC;IAED,eAAe;IACf,OAAO,CAAC,SAAiB;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,IAAI,KAAK,CAAC,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAClD,KAAK,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC1B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAE/B,OAAO,CAAC,GAAG,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAA;IACxD,CAAC;IAED,sBAAsB;IACtB,OAAO;QACL,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED,+CAA+C;IAC/C,OAAO;IACP,+CAA+C;IAEvC,cAAc,CAAC,SAAiB;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,IAAI,KAAK,CAAC,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAClD,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,OAAO,CAAC,GAAG,CAAC,mCAAmC,SAAS,EAAE,CAAC,CAAA;YAC3D,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACzB,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;IAC1B,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,SAAiB;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,IAAI,UAAU,CAAA;QACd,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,IAAA,eAAI,EAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;YAC5C,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAA;YACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAA;YAE7B,4BAA4B;YAC5B,IAAI,OAAO,IAAI,KAAK,CAAC,UAAU;gBAAE,OAAM;YAEvC,MAAM,EAAE,GAAG,IAAA,+BAAe,EAAC;gBACzB,8DAA8D;gBAC9D,KAAK,EAAE,UAAU,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAS,CAAC;gBACzF,SAAS,EAAE,QAAQ;aACpB,CAAC,CAAA;YAEF,MAAM,SAAS,GAAwB,EAAE,CAAA;YACzC,IAAI,WAAW,GAAG,KAAK,CAAA;YACvB,IAAI,OAAO,GAAG,KAAK,CAAA;YAEnB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;gBAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;oBAAE,SAAQ;gBAC1B,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;gBAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAC5C,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC9B,CAAC;qBAAM,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBACvC,WAAW,GAAG,IAAI,CAAA;oBAClB,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;gBAC1B,CAAC;YACH,CAAC;YAED,eAAe;YACf,KAAK,CAAC,UAAU,GAAG,OAAO,CAAA;YAE1B,QAAQ;YACR,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;gBAC9B,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;YAC1D,CAAC;YAED,mBAAmB;YACnB,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;gBACtF,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,UAAU,EAAE,KAAK,EAAE,CAAA;QAC3B,CAAC;IACH,CAAC;CACF;AAxID,gDAwIC;AAkBD;;GAEG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,SAAiB;IACrD,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAQ,CAAA;QAEnC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACvC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAA;YAEtC,WAAW;YACX,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACnC,IAAI,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;oBACnF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;gBACzB,CAAC;YACH,CAAC;YAED,MAAM,iBAAiB,GAAG,OAAO,UAAU,KAAK,QAAQ;gBACtD,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;gBAC/C,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;oBACzB,8DAA8D;oBAC9D,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;oBAChF,CAAC,CAAC,EAAE,CAAA;YAER,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;YAE3D,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE;oBACvD,UAAU,EAAE,SAAS;iBACtB;aACF,CAAA;QACH,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAC5C,8DAA8D;YAC9D,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;YAC1G,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;YAEjD,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,WAAW;oBACjB,OAAO,EAAE;wBACP,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,IAAI,SAAS;wBAC3C,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS;wBACrC,IAAI,EAAE,WAAW;wBACjB,OAAO;wBACP,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW;qBACrC;oBACD,UAAU,EAAE,SAAS;iBACtB;aACF,CAAA;QACH,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA;QACvD,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,114 @@
1
+ import type { ExecutionProvider } from '../providers/ExecutionProvider.js';
2
+ import type { Session, ClaudeStreamEvent, ServerEvent } from '@sessix/shared';
3
+ /**
4
+ * 会话管理器 — 后端核心协调模块
5
+ *
6
+ * 职责:
7
+ * - 创建 / 终止 / 发送消息到 Claude 会话
8
+ * - 监听 ExecutionProvider 的事件流,将 ClaudeStreamEvent 包装成 ServerEvent 转发
9
+ * - 检测 status 变化(system init → running,result → completed/error)
10
+ * - 维护 AskUserQuestion 问题映射,连接 ExecutionProvider 和 WsBridge
11
+ */
12
+ export declare class SessionManager {
13
+ private provider;
14
+ /** 事件回调列表(事件会被转发到 WsBridge) */
15
+ private eventCallbacks;
16
+ /** 每个会话的事件流取消订阅函数 */
17
+ private unsubscribeMap;
18
+ /** 每个会话的事件缓冲区(用于新订阅者重放)*/
19
+ private sessionEventBuffers;
20
+ /** AskUserQuestion 问题映射:requestId → resolve 回调 */
21
+ private pendingQuestions;
22
+ /**
23
+ * 会话状态缓存(用于追踪 status 变化,检测 oldStatus !== newStatus 时广播)
24
+ *
25
+ * 这是 status 变化的唯一检测源。ProcessProvider 的 session.status 是实际值,
26
+ * 这里只缓存上次广播的值,用于去重。
27
+ */
28
+ private lastBroadcastStatus;
29
+ /** 每个会话的服务器端累计统计 */
30
+ private sessionStats;
31
+ /** assistant 事件合并缓冲区(30ms 窗口内的 assistant 事件合并为一次发送) */
32
+ private pendingAssistantEvents;
33
+ constructor(provider: ExecutionProvider);
34
+ /**
35
+ * 创建新会话
36
+ *
37
+ * 调用 provider.startSession(),订阅事件流,
38
+ * 将 ClaudeStreamEvent 包装为 ServerEvent 转发。
39
+ */
40
+ createSession(projectPath: string, message: string, resumeSessionId?: string, newSessionId?: string, model?: string): Promise<Session>;
41
+ /**
42
+ * 发送消息到已有会话
43
+ */
44
+ sendMessage(sessionId: string, message: string): Promise<void>;
45
+ /**
46
+ * 终止会话
47
+ */
48
+ killSession(sessionId: string): Promise<void>;
49
+ /**
50
+ * 获取会话的缓冲事件(用于新订阅者重放)
51
+ */
52
+ getSessionEvents(sessionId: string): ClaudeStreamEvent[];
53
+ /**
54
+ * 处理 AskUserQuestion 回答(从手机端传来)
55
+ */
56
+ handleQuestionResponse(requestId: string, answer: string): void;
57
+ /**
58
+ * 获取所有活跃会话(含服务器端统计)
59
+ */
60
+ getActiveSessions(): Session[];
61
+ /**
62
+ * 注册事件回调(事件会被转发到 WsBridge)
63
+ *
64
+ * @returns 取消注册的函数
65
+ */
66
+ onEvent(callback: (event: ServerEvent) => void): () => void;
67
+ /**
68
+ * 清理所有资源
69
+ */
70
+ destroy(): void;
71
+ /**
72
+ * 订阅指定会话的事件流(包括 AskUserQuestion 问题事件)
73
+ */
74
+ private subscribeToSession;
75
+ /**
76
+ * 取消指定会话的事件订阅
77
+ */
78
+ private unsubscribeSession;
79
+ /**
80
+ * 处理来自 provider 的 Claude 事件
81
+ *
82
+ * - 包装为 ServerEvent 转发
83
+ * - assistant 事件在 30ms 窗口内合并后批量发送(减少 WebSocket 帧数)
84
+ * - 检测 status 变化
85
+ */
86
+ private handleClaudeEvent;
87
+ /**
88
+ * 缓冲 assistant 事件到 30ms 窗口
89
+ */
90
+ private bufferAssistantEvent;
91
+ /**
92
+ * 刷新缓冲的 assistant 事件,批量发送
93
+ */
94
+ private flushPendingAssistant;
95
+ /**
96
+ * 更新会话状态,如果状态发生变化则广播通知
97
+ *
98
+ * 使用 lastBroadcastStatus 去重,只在状态实际变化时广播。
99
+ */
100
+ private updateSessionStatus;
101
+ /**
102
+ * 处理 AskUserQuestion 事件:广播问题请求到手机,等待用户回答
103
+ */
104
+ private handleAskUserQuestion;
105
+ /**
106
+ * 清除指定会话的所有待回答问题
107
+ */
108
+ private clearPendingQuestions;
109
+ /**
110
+ * 发出 ServerEvent 到所有已注册的回调
111
+ */
112
+ private emit;
113
+ }
114
+ //# sourceMappingURL=SessionManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/session/SessionManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,KAAK,EACV,OAAO,EAIP,iBAAiB,EACjB,WAAW,EACZ,MAAM,gBAAgB,CAAA;AAIvB;;;;;;;;GAQG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAmB;IAEnC,+BAA+B;IAC/B,OAAO,CAAC,cAAc,CAA0C;IAEhE,qBAAqB;IACrB,OAAO,CAAC,cAAc,CAAgC;IAEtD,0BAA0B;IAC1B,OAAO,CAAC,mBAAmB,CAAyC;IAEpE,kDAAkD;IAClD,OAAO,CAAC,gBAAgB,CAIpB;IAEJ;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB,CAAmC;IAE9D,oBAAoB;IACpB,OAAO,CAAC,YAAY,CAAkC;IAEtD,uDAAuD;IACvD,OAAO,CAAC,sBAAsB,CAA2F;gBAE7G,QAAQ,EAAE,iBAAiB;IAQvC;;;;;OAKG;IACG,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,EACxB,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAmBnB;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBnD;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAIxD;;OAEG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgB/D;;OAEG;IACH,iBAAiB,IAAI,OAAO,EAAE;IAO9B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;IAW3D;;OAEG;IACH,OAAO,IAAI,IAAI;IA2Bf;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA6DzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAuC7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;OAEG;IACH,OAAO,CAAC,IAAI;CASb"}