sessix-server 0.1.2 → 0.1.3
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/dist/index.js +128 -84
- package/dist/server.js +113 -69
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -31,7 +31,22 @@ var zh = {
|
|
|
31
31
|
startup: {
|
|
32
32
|
banner: " Sessix \u2014 AI \u7F16\u7A0B\u79FB\u52A8\u6307\u6325\u4E2D\u5FC3",
|
|
33
33
|
scanToPair: " \u626B\u7801\u914D\u5BF9\uFF1A",
|
|
34
|
-
waitingConnection: " \u7B49\u5F85\u624B\u673A\u8FDE\u63A5..."
|
|
34
|
+
waitingConnection: " \u7B49\u5F85\u624B\u673A\u8FDE\u63A5...",
|
|
35
|
+
wsPort: " WebSocket \u7AEF\u53E3: {{port}}",
|
|
36
|
+
httpPort: " HTTP \u5BA1\u6279\u7AEF\u53E3: {{port}}",
|
|
37
|
+
tokenDisabled: " \u8FDE\u63A5 Token: (\u5DF2\u7981\u7528\uFF0C\u5F00\u53D1\u6A21\u5F0F)",
|
|
38
|
+
token: " \u8FDE\u63A5 Token: {{token}}",
|
|
39
|
+
wsAddress: " WebSocket \u5730\u5740: ws://{{ip}}:{{port}}",
|
|
40
|
+
wsAddressWithToken: " WebSocket \u5730\u5740: ws://{{ip}}:{{port}}?token={{token}}",
|
|
41
|
+
healthCheck: " \u5065\u5EB7\u68C0\u67E5: http://localhost:{{port}}/health",
|
|
42
|
+
devMode: " [\u5F00\u53D1\u6A21\u5F0F] \u65E0\u9700 Token\uFF0C\u624B\u673A\u7AEF\u53EA\u9700\u8F93\u5165 IP:\u7AEF\u53E3 \u5373\u53EF\u8FDE\u63A5",
|
|
43
|
+
autoDiscoveryOn: " \u{1F4A1} \u81EA\u52A8\u53D1\u73B0\u5DF2\u542F\u7528\uFF0C\u540C\u7F51\u6BB5\u624B\u673A\u53EF\u81EA\u52A8\u8FDE\u63A5",
|
|
44
|
+
autoDiscoveryHint: " \u5982\u5728\u516C\u5171\u7F51\u7EDC\uFF0C\u5EFA\u8BAE\u5173\u95ED: SESSIX_AUTO_CONNECT=false npx sessix-server",
|
|
45
|
+
autoDiscoveryOff: " \u2139\uFE0F \u81EA\u52A8\u53D1\u73B0\u5DF2\u5173\u95ED\uFF0C\u624B\u673A\u9700\u624B\u52A8\u8F93\u5165\u5730\u5740\u8FDE\u63A5",
|
|
46
|
+
receivedSignal: "\u6536\u5230 {{signal}}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...",
|
|
47
|
+
goodbye: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u518D\u89C1\uFF01",
|
|
48
|
+
shutdownError: "\u5173\u95ED\u8FC7\u7A0B\u51FA\u9519:",
|
|
49
|
+
startFailed: "\u542F\u52A8\u5931\u8D25:"
|
|
35
50
|
},
|
|
36
51
|
server: {
|
|
37
52
|
listProjectsFailed: "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25: {{error}}",
|
|
@@ -45,10 +60,16 @@ var zh = {
|
|
|
45
60
|
hookInstalled: "Sessix hook \u5DF2\u5B89\u88C5\u5230 Claude Code",
|
|
46
61
|
hookExists: "Sessix hook \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5B89\u88C5",
|
|
47
62
|
hookContinue: "\u7EE7\u7EED\u542F\u52A8\uFF08hook \u529F\u80FD\u53EF\u80FD\u4E0D\u53EF\u7528\uFF09",
|
|
63
|
+
hookInstallFailed: "Hook \u5B89\u88C5\u5931\u8D25:",
|
|
48
64
|
shuttingDown: "\u6B63\u5728\u4F18\u96C5\u5173\u95ED...",
|
|
49
65
|
shutdownComponentError: "\u5173\u95ED {{label}} \u51FA\u9519",
|
|
50
66
|
shutdownWithErrors: "\u5173\u95ED\u5B8C\u6210\uFF0C{{count}} \u4E2A\u9519\u8BEF",
|
|
51
|
-
shutdownComplete: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED"
|
|
67
|
+
shutdownComplete: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED",
|
|
68
|
+
portInUse: "\u7AEF\u53E3 {{port}} \u88AB\u5360\u7528\uFF0C\u5C1D\u8BD5\u91CA\u653E\u65E7\u8FDB\u7A0B...",
|
|
69
|
+
restarting: "\u91CD\u65B0\u542F\u52A8 {{label}}...",
|
|
70
|
+
activityPushEnabled: "ActivityKit Push \u5DF2\u542F\u7528",
|
|
71
|
+
activityPushFailed: "ActivityKit Push \u521D\u59CB\u5316\u5931\u8D25:",
|
|
72
|
+
activityPushContinue: "\u7EE7\u7EED\u542F\u52A8\uFF08Live Activity \u540E\u53F0\u63A8\u9001\u4E0D\u53EF\u7528\uFF09"
|
|
52
73
|
},
|
|
53
74
|
ws: {
|
|
54
75
|
started: "WebSocket \u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u7AEF\u53E3 {{port}}",
|
|
@@ -111,7 +132,22 @@ var en = {
|
|
|
111
132
|
startup: {
|
|
112
133
|
banner: " Sessix \u2014 AI Coding Mobile Command Center",
|
|
113
134
|
scanToPair: " Scan to pair:",
|
|
114
|
-
waitingConnection: " Waiting for phone connection..."
|
|
135
|
+
waitingConnection: " Waiting for phone connection...",
|
|
136
|
+
wsPort: " WebSocket port: {{port}}",
|
|
137
|
+
httpPort: " HTTP approval port: {{port}}",
|
|
138
|
+
tokenDisabled: " Token: (disabled, dev mode)",
|
|
139
|
+
token: " Token: {{token}}",
|
|
140
|
+
wsAddress: " WebSocket URL: ws://{{ip}}:{{port}}",
|
|
141
|
+
wsAddressWithToken: " WebSocket URL: ws://{{ip}}:{{port}}?token={{token}}",
|
|
142
|
+
healthCheck: " Health check: http://localhost:{{port}}/health",
|
|
143
|
+
devMode: " [Dev mode] No token required, just enter IP:port on your phone",
|
|
144
|
+
autoDiscoveryOn: " Auto-discovery enabled, phones on the same network can connect automatically",
|
|
145
|
+
autoDiscoveryHint: " On public networks, disable with: SESSIX_AUTO_CONNECT=false npx sessix-server",
|
|
146
|
+
autoDiscoveryOff: " Auto-discovery disabled, phone must enter address manually",
|
|
147
|
+
receivedSignal: "Received {{signal}}, graceful shutdown...",
|
|
148
|
+
goodbye: "All services closed, goodbye!",
|
|
149
|
+
shutdownError: "Shutdown error:",
|
|
150
|
+
startFailed: "Startup failed:"
|
|
115
151
|
},
|
|
116
152
|
server: {
|
|
117
153
|
listProjectsFailed: "Failed to list projects: {{error}}",
|
|
@@ -125,10 +161,16 @@ var en = {
|
|
|
125
161
|
hookInstalled: "Sessix hook installed to Claude Code",
|
|
126
162
|
hookExists: "Sessix hook already exists, skipping installation",
|
|
127
163
|
hookContinue: "Continuing startup (hook functionality may be unavailable)",
|
|
164
|
+
hookInstallFailed: "Hook installation failed:",
|
|
128
165
|
shuttingDown: "Graceful shutdown in progress...",
|
|
129
166
|
shutdownComponentError: "Error closing {{label}}",
|
|
130
167
|
shutdownWithErrors: "Shutdown complete, {{count}} error(s)",
|
|
131
|
-
shutdownComplete: "All services closed"
|
|
168
|
+
shutdownComplete: "All services closed",
|
|
169
|
+
portInUse: "Port {{port}} in use, attempting to release old process...",
|
|
170
|
+
restarting: "Restarting {{label}}...",
|
|
171
|
+
activityPushEnabled: "ActivityKit Push enabled",
|
|
172
|
+
activityPushFailed: "ActivityKit Push init failed:",
|
|
173
|
+
activityPushContinue: "Continuing startup (Live Activity background push unavailable)"
|
|
132
174
|
},
|
|
133
175
|
ws: {
|
|
134
176
|
started: "WebSocket server started on port {{port}}",
|
|
@@ -187,7 +229,10 @@ var en = {
|
|
|
187
229
|
};
|
|
188
230
|
|
|
189
231
|
// src/i18n/index.ts
|
|
232
|
+
var locales = { zh, en };
|
|
190
233
|
function detectLocale() {
|
|
234
|
+
const explicit = process.env.SESSIX_LANG;
|
|
235
|
+
if (explicit && explicit in locales) return explicit;
|
|
191
236
|
try {
|
|
192
237
|
const raw = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || "";
|
|
193
238
|
if (raw.startsWith("zh")) return "zh";
|
|
@@ -195,7 +240,6 @@ function detectLocale() {
|
|
|
195
240
|
}
|
|
196
241
|
return "en";
|
|
197
242
|
}
|
|
198
|
-
var locales = { zh, en };
|
|
199
243
|
var currentLocale = detectLocale();
|
|
200
244
|
var currentMessages = locales[currentLocale] ?? en;
|
|
201
245
|
function t(key, params) {
|
|
@@ -299,12 +343,12 @@ var ProcessProvider = class {
|
|
|
299
343
|
session.pid = proc.pid;
|
|
300
344
|
this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort });
|
|
301
345
|
proc.on("error", (err) => {
|
|
302
|
-
console.error(`[ProcessProvider]
|
|
346
|
+
console.error(`[ProcessProvider] Session ${sessionId} process error:`, err.message);
|
|
303
347
|
this.activeSessions.delete(sessionId);
|
|
304
348
|
const syntheticResult = {
|
|
305
349
|
type: "result",
|
|
306
350
|
subtype: "error",
|
|
307
|
-
result:
|
|
351
|
+
result: `Process spawn failed: ${err.message}`,
|
|
308
352
|
session_id: sessionId,
|
|
309
353
|
duration_ms: 0,
|
|
310
354
|
is_error: true,
|
|
@@ -357,7 +401,7 @@ var ProcessProvider = class {
|
|
|
357
401
|
async sendMessage(sessionId, message, permissionMode, images) {
|
|
358
402
|
const entry = this.activeSessions.get(sessionId);
|
|
359
403
|
if (!entry) {
|
|
360
|
-
throw new Error(
|
|
404
|
+
throw new Error(`Session ${sessionId} not found or already ended`);
|
|
361
405
|
}
|
|
362
406
|
const modeChanged = permissionMode != null && permissionMode !== (entry.permissionMode ?? "default");
|
|
363
407
|
if (!modeChanged && entry.process.exitCode === null && entry.process.signalCode === null && !entry.process.stdin?.destroyed) {
|
|
@@ -367,7 +411,7 @@ var ProcessProvider = class {
|
|
|
367
411
|
return;
|
|
368
412
|
}
|
|
369
413
|
if (modeChanged) {
|
|
370
|
-
console.log(`[ProcessProvider]
|
|
414
|
+
console.log(`[ProcessProvider] Session ${sessionId}: permission mode change ${entry.permissionMode ?? "default"} \u2192 ${permissionMode}, respawn`);
|
|
371
415
|
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
372
416
|
try {
|
|
373
417
|
entry.process.stdin?.end();
|
|
@@ -376,7 +420,7 @@ var ProcessProvider = class {
|
|
|
376
420
|
entry.process.kill("SIGTERM");
|
|
377
421
|
}
|
|
378
422
|
} else {
|
|
379
|
-
console.log(`[ProcessProvider]
|
|
423
|
+
console.log(`[ProcessProvider] Session ${sessionId}: process exited, respawning`);
|
|
380
424
|
}
|
|
381
425
|
const savedPendingQuestion = entry.pendingQuestion;
|
|
382
426
|
const newMode = permissionMode ?? entry.permissionMode;
|
|
@@ -389,12 +433,12 @@ var ProcessProvider = class {
|
|
|
389
433
|
entry.permissionMode = newMode;
|
|
390
434
|
entry.pendingQuestion = savedPendingQuestion;
|
|
391
435
|
proc.on("error", (err) => {
|
|
392
|
-
console.error(`[ProcessProvider]
|
|
436
|
+
console.error(`[ProcessProvider] Session ${sessionId} sendMessage process error:`, err.message);
|
|
393
437
|
this.activeSessions.delete(sessionId);
|
|
394
438
|
const syntheticResult = {
|
|
395
439
|
type: "result",
|
|
396
440
|
subtype: "error",
|
|
397
|
-
result:
|
|
441
|
+
result: `Failed to send message: ${err.message}`,
|
|
398
442
|
session_id: sessionId,
|
|
399
443
|
duration_ms: 0,
|
|
400
444
|
is_error: true,
|
|
@@ -485,16 +529,16 @@ var ProcessProvider = class {
|
|
|
485
529
|
parent_tool_use_id: null
|
|
486
530
|
});
|
|
487
531
|
if (!proc.stdin || proc.stdin.destroyed) {
|
|
488
|
-
console.error(`[ProcessProvider] stdin
|
|
532
|
+
console.error(`[ProcessProvider] stdin unavailable, message lost`);
|
|
489
533
|
if (sessionId) {
|
|
490
|
-
this.emitWriteError(sessionId, "
|
|
534
|
+
this.emitWriteError(sessionId, "Process stdin closed, message not delivered");
|
|
491
535
|
}
|
|
492
536
|
return;
|
|
493
537
|
}
|
|
494
538
|
proc.stdin.write(payload + "\n", (err) => {
|
|
495
539
|
if (err && sessionId) {
|
|
496
|
-
console.error(`[ProcessProvider]
|
|
497
|
-
this.emitWriteError(sessionId,
|
|
540
|
+
console.error(`[ProcessProvider] Session ${sessionId} stdin write failed:`, err.message);
|
|
541
|
+
this.emitWriteError(sessionId, `Failed to send message: ${err.message}`);
|
|
498
542
|
}
|
|
499
543
|
});
|
|
500
544
|
}
|
|
@@ -518,7 +562,7 @@ var ProcessProvider = class {
|
|
|
518
562
|
*/
|
|
519
563
|
attachStdoutListener(sessionId, proc) {
|
|
520
564
|
if (!proc.stdout) {
|
|
521
|
-
console.warn(`[ProcessProvider]
|
|
565
|
+
console.warn(`[ProcessProvider] Session ${sessionId}: stdout unavailable`);
|
|
522
566
|
return;
|
|
523
567
|
}
|
|
524
568
|
const rl = (0, import_readline.createInterface)({
|
|
@@ -556,7 +600,7 @@ var ProcessProvider = class {
|
|
|
556
600
|
this.emitter.emit(this.getEventName(sessionId), event);
|
|
557
601
|
} else {
|
|
558
602
|
console.warn(
|
|
559
|
-
`[ProcessProvider]
|
|
603
|
+
`[ProcessProvider] Session ${sessionId}: failed to parse line: ${trimmed.substring(0, 100)}`
|
|
560
604
|
);
|
|
561
605
|
}
|
|
562
606
|
});
|
|
@@ -569,7 +613,7 @@ var ProcessProvider = class {
|
|
|
569
613
|
proc.stderr.on("data", (data) => {
|
|
570
614
|
const text = data.toString().trim();
|
|
571
615
|
if (text) {
|
|
572
|
-
console.error(`[ProcessProvider]
|
|
616
|
+
console.error(`[ProcessProvider] Session ${sessionId} stderr: ${text}`);
|
|
573
617
|
}
|
|
574
618
|
});
|
|
575
619
|
}
|
|
@@ -600,7 +644,7 @@ var ProcessProvider = class {
|
|
|
600
644
|
entry.session.status = isNormal ? "idle" : "error";
|
|
601
645
|
if (!isNormal) {
|
|
602
646
|
console.error(
|
|
603
|
-
`[ProcessProvider]
|
|
647
|
+
`[ProcessProvider] Session ${sessionId}: process exited abnormally code=${code} signal=${signal}`
|
|
604
648
|
);
|
|
605
649
|
}
|
|
606
650
|
const syntheticResult = {
|
|
@@ -608,7 +652,7 @@ var ProcessProvider = class {
|
|
|
608
652
|
subtype: isNormal ? "success" : "error",
|
|
609
653
|
session_id: sessionId,
|
|
610
654
|
is_error: !isNormal,
|
|
611
|
-
result: isNormal ? "" :
|
|
655
|
+
result: isNormal ? "" : `Process exited code=${code} signal=${signal}`,
|
|
612
656
|
duration_ms: 0,
|
|
613
657
|
num_turns: 0
|
|
614
658
|
};
|
|
@@ -656,7 +700,7 @@ var ProcessProvider = class {
|
|
|
656
700
|
* 使用 --output-format text 做一次性调用,返回纯文本结果。
|
|
657
701
|
*/
|
|
658
702
|
async generateSuggestion(context) {
|
|
659
|
-
const prompt =
|
|
703
|
+
const prompt = `You are an AI coding assistant. Based on the following Claude Code conversation context, suggest the most valuable next instruction for the user (give the instruction directly, no explanation, no quotes):
|
|
660
704
|
|
|
661
705
|
${context}`;
|
|
662
706
|
return new Promise((resolve, reject) => {
|
|
@@ -676,7 +720,7 @@ ${context}`;
|
|
|
676
720
|
if (code === 0) {
|
|
677
721
|
resolve(output.trim());
|
|
678
722
|
} else {
|
|
679
|
-
reject(new Error(`generateSuggestion
|
|
723
|
+
reject(new Error(`generateSuggestion process exit code: ${code}`));
|
|
680
724
|
}
|
|
681
725
|
});
|
|
682
726
|
proc.once("error", reject);
|
|
@@ -691,10 +735,10 @@ ${context}`;
|
|
|
691
735
|
async answerQuestion(sessionId, toolUseId, answer) {
|
|
692
736
|
const entry = this.activeSessions.get(sessionId);
|
|
693
737
|
if (!entry) {
|
|
694
|
-
throw new Error(
|
|
738
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
695
739
|
}
|
|
696
740
|
if (!entry.process.stdin || entry.process.stdin.destroyed) {
|
|
697
|
-
throw new Error(
|
|
741
|
+
throw new Error(`Session ${sessionId} stdin unavailable`);
|
|
698
742
|
}
|
|
699
743
|
const toolResult = JSON.stringify({
|
|
700
744
|
type: "tool_result",
|
|
@@ -707,7 +751,7 @@ ${context}`;
|
|
|
707
751
|
else resolve();
|
|
708
752
|
});
|
|
709
753
|
});
|
|
710
|
-
console.log(`[ProcessProvider]
|
|
754
|
+
console.log(`[ProcessProvider] Session ${sessionId}: AskUserQuestion answered (toolUseId=${toolUseId})`);
|
|
711
755
|
}
|
|
712
756
|
/**
|
|
713
757
|
* 订阅指定会话的 AskUserQuestion 事件
|
|
@@ -792,7 +836,7 @@ var SessionManager = class {
|
|
|
792
836
|
this.sessionProjectPaths.set(session.id, projectPath);
|
|
793
837
|
this.unsubscribeSession(session.id);
|
|
794
838
|
this.subscribeToSession(session.id);
|
|
795
|
-
console.log(`[SessionManager]
|
|
839
|
+
console.log(`[SessionManager] Session created: ${session.id} (project: ${projectPath})`);
|
|
796
840
|
return session;
|
|
797
841
|
}
|
|
798
842
|
/**
|
|
@@ -801,7 +845,7 @@ var SessionManager = class {
|
|
|
801
845
|
async sendMessage(sessionId, message, permissionMode, images) {
|
|
802
846
|
await this.provider.sendMessage(sessionId, message, permissionMode, images);
|
|
803
847
|
this.updateSessionStatus(sessionId, "running");
|
|
804
|
-
console.log(`[SessionManager]
|
|
848
|
+
console.log(`[SessionManager] Message sent to session: ${sessionId}`);
|
|
805
849
|
}
|
|
806
850
|
/**
|
|
807
851
|
* 终止会话
|
|
@@ -820,7 +864,7 @@ var SessionManager = class {
|
|
|
820
864
|
this.pendingAssistantEvents.delete(sessionId);
|
|
821
865
|
}
|
|
822
866
|
await this.provider.killSession(sessionId);
|
|
823
|
-
console.log(`[SessionManager]
|
|
867
|
+
console.log(`[SessionManager] Session killed: ${sessionId}`);
|
|
824
868
|
}
|
|
825
869
|
/**
|
|
826
870
|
* 获取会话的缓冲事件(用于新订阅者重放)
|
|
@@ -846,13 +890,13 @@ var SessionManager = class {
|
|
|
846
890
|
handleQuestionResponse(requestId, answer) {
|
|
847
891
|
const pending = this.pendingQuestions.get(requestId);
|
|
848
892
|
if (!pending) {
|
|
849
|
-
console.warn(`[SessionManager]
|
|
893
|
+
console.warn(`[SessionManager] Question request not found: ${requestId}`);
|
|
850
894
|
return;
|
|
851
895
|
}
|
|
852
896
|
this.pendingQuestions.delete(requestId);
|
|
853
897
|
this.updateSessionStatus(pending.sessionId, "running");
|
|
854
898
|
pending.resolve(answer);
|
|
855
|
-
console.log(`[SessionManager]
|
|
899
|
+
console.log(`[SessionManager] Question answered: ${requestId}`);
|
|
856
900
|
}
|
|
857
901
|
/**
|
|
858
902
|
* 获取指定会话的所有待回答问题(用于重连时恢复)
|
|
@@ -936,7 +980,7 @@ var SessionManager = class {
|
|
|
936
980
|
this.pendingQuestions.clear();
|
|
937
981
|
this.lastBroadcastStatus.clear();
|
|
938
982
|
this.eventCallbacks.length = 0;
|
|
939
|
-
console.log("[SessionManager]
|
|
983
|
+
console.log("[SessionManager] Destroyed");
|
|
940
984
|
}
|
|
941
985
|
// ============================================
|
|
942
986
|
// 内部方法
|
|
@@ -1090,7 +1134,7 @@ var SessionManager = class {
|
|
|
1090
1134
|
status: newStatus,
|
|
1091
1135
|
stats
|
|
1092
1136
|
});
|
|
1093
|
-
console.log(`[SessionManager]
|
|
1137
|
+
console.log(`[SessionManager] Session ${sessionId} status change: ${lastStatus ?? "(none)"} \u2192 ${newStatus}`);
|
|
1094
1138
|
}
|
|
1095
1139
|
}
|
|
1096
1140
|
/** 获取会话统计(含 runningStartedAt) */
|
|
@@ -1119,7 +1163,7 @@ var SessionManager = class {
|
|
|
1119
1163
|
createdAt: Date.now()
|
|
1120
1164
|
};
|
|
1121
1165
|
this.emit({ type: "question_request", request: updatedRequest });
|
|
1122
|
-
console.log(`[SessionManager]
|
|
1166
|
+
console.log(`[SessionManager] Session ${sessionId}: AskUserQuestion updated (requestId=${existingRequestId})`);
|
|
1123
1167
|
return;
|
|
1124
1168
|
}
|
|
1125
1169
|
const requestId = (0, import_uuid2.v4)();
|
|
@@ -1140,10 +1184,10 @@ var SessionManager = class {
|
|
|
1140
1184
|
try {
|
|
1141
1185
|
await this.provider.answerQuestion(sessionId, toolUseId, answer);
|
|
1142
1186
|
} catch (err) {
|
|
1143
|
-
console.error(`[SessionManager] answerQuestion
|
|
1187
|
+
console.error(`[SessionManager] answerQuestion failed (${sessionId}):`, err);
|
|
1144
1188
|
}
|
|
1145
1189
|
}).catch((err) => console.error("[SessionManager] answerPromise rejected:", err));
|
|
1146
|
-
console.log(`[SessionManager]
|
|
1190
|
+
console.log(`[SessionManager] Session ${sessionId}: AskUserQuestion pushed (requestId=${requestId})`);
|
|
1147
1191
|
}
|
|
1148
1192
|
/**
|
|
1149
1193
|
* 清除指定会话的所有待回答问题
|
|
@@ -1167,7 +1211,7 @@ var SessionManager = class {
|
|
|
1167
1211
|
try {
|
|
1168
1212
|
callback(event);
|
|
1169
1213
|
} catch (err) {
|
|
1170
|
-
console.error("[SessionManager]
|
|
1214
|
+
console.error("[SessionManager] Event callback error:", err);
|
|
1171
1215
|
}
|
|
1172
1216
|
}
|
|
1173
1217
|
}
|
|
@@ -1244,7 +1288,7 @@ var SessionFileWatcher = class {
|
|
|
1244
1288
|
if (!entry) return;
|
|
1245
1289
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
1246
1290
|
entry.idleTimer = setTimeout(() => {
|
|
1247
|
-
console.log(`[SessionFileWatcher]
|
|
1291
|
+
console.log(`[SessionFileWatcher] Idle timeout, stop watching: ${sessionId}`);
|
|
1248
1292
|
this.unwatch(sessionId);
|
|
1249
1293
|
}, this.IDLE_TIMEOUT_MS);
|
|
1250
1294
|
}
|
|
@@ -1447,7 +1491,7 @@ var WsBridge = class _WsBridge {
|
|
|
1447
1491
|
if (err) {
|
|
1448
1492
|
reject(err);
|
|
1449
1493
|
} else {
|
|
1450
|
-
console.log("[WsBridge] WebSocket
|
|
1494
|
+
console.log("[WsBridge] WebSocket server closed");
|
|
1451
1495
|
resolve();
|
|
1452
1496
|
}
|
|
1453
1497
|
});
|
|
@@ -1470,12 +1514,12 @@ var WsBridge = class _WsBridge {
|
|
|
1470
1514
|
/** 处理新的 WebSocket 连接 */
|
|
1471
1515
|
handleConnection(ws) {
|
|
1472
1516
|
this.lastPongMap.set(ws, Date.now());
|
|
1473
|
-
console.log(`[WsBridge]
|
|
1517
|
+
console.log(`[WsBridge] New client connected, connections: ${this.getConnectionCount()}`);
|
|
1474
1518
|
for (const callback of this.connectionCallbacks) {
|
|
1475
1519
|
try {
|
|
1476
1520
|
callback(ws);
|
|
1477
1521
|
} catch (err) {
|
|
1478
|
-
console.error("[WsBridge]
|
|
1522
|
+
console.error("[WsBridge] Connection callback error:", err);
|
|
1479
1523
|
}
|
|
1480
1524
|
}
|
|
1481
1525
|
ws.on("pong", () => {
|
|
@@ -1486,10 +1530,10 @@ var WsBridge = class _WsBridge {
|
|
|
1486
1530
|
const event = JSON.parse(raw.toString());
|
|
1487
1531
|
this.dispatchClientEvent(event, ws);
|
|
1488
1532
|
} catch (err) {
|
|
1489
|
-
console.error("[WsBridge]
|
|
1533
|
+
console.error("[WsBridge] Message parse error:", err);
|
|
1490
1534
|
this.send(ws, {
|
|
1491
1535
|
type: "error",
|
|
1492
|
-
message: "
|
|
1536
|
+
message: "Invalid message format",
|
|
1493
1537
|
code: "INVALID_MESSAGE"
|
|
1494
1538
|
});
|
|
1495
1539
|
}
|
|
@@ -1499,18 +1543,18 @@ var WsBridge = class _WsBridge {
|
|
|
1499
1543
|
this.viewingSessions.delete(ws);
|
|
1500
1544
|
this.messageQueues.delete(ws);
|
|
1501
1545
|
setTimeout(() => {
|
|
1502
|
-
console.log(`[WsBridge]
|
|
1546
|
+
console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
|
|
1503
1547
|
for (const cb of this.disconnectCallbacks) {
|
|
1504
1548
|
try {
|
|
1505
1549
|
cb();
|
|
1506
1550
|
} catch (err) {
|
|
1507
|
-
console.error("[WsBridge]
|
|
1551
|
+
console.error("[WsBridge] Disconnect callback error:", err);
|
|
1508
1552
|
}
|
|
1509
1553
|
}
|
|
1510
1554
|
}, 0);
|
|
1511
1555
|
});
|
|
1512
1556
|
ws.on("error", (err) => {
|
|
1513
|
-
console.error("[WsBridge]
|
|
1557
|
+
console.error("[WsBridge] Connection error:", err.message);
|
|
1514
1558
|
});
|
|
1515
1559
|
}
|
|
1516
1560
|
/**
|
|
@@ -1526,7 +1570,7 @@ var WsBridge = class _WsBridge {
|
|
|
1526
1570
|
try {
|
|
1527
1571
|
await callback(event, ws);
|
|
1528
1572
|
} catch (err) {
|
|
1529
|
-
console.error("[WsBridge]
|
|
1573
|
+
console.error("[WsBridge] Event callback error:", err);
|
|
1530
1574
|
}
|
|
1531
1575
|
}
|
|
1532
1576
|
});
|
|
@@ -1540,7 +1584,7 @@ var WsBridge = class _WsBridge {
|
|
|
1540
1584
|
for (const ws of this.wss.clients) {
|
|
1541
1585
|
const lastPong = this.lastPongMap.get(ws) ?? 0;
|
|
1542
1586
|
if (now - lastPong > 45e3) {
|
|
1543
|
-
console.log("[WsBridge]
|
|
1587
|
+
console.log("[WsBridge] Dead connection detected, terminating");
|
|
1544
1588
|
ws.terminate();
|
|
1545
1589
|
continue;
|
|
1546
1590
|
}
|
|
@@ -1799,7 +1843,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1799
1843
|
projectPath,
|
|
1800
1844
|
toolName,
|
|
1801
1845
|
toolInput,
|
|
1802
|
-
description: String(payload.description ?? body.description ?? `${toolName}
|
|
1846
|
+
description: String(payload.description ?? body.description ?? `${toolName} tool call request`),
|
|
1803
1847
|
createdAt: Date.now()
|
|
1804
1848
|
};
|
|
1805
1849
|
console.log(`[ApprovalProxy] ${t("approval.received")}: ${requestId} (${approvalRequest.toolName})`);
|
|
@@ -1825,7 +1869,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1825
1869
|
this.sendJson(res, 200, decision);
|
|
1826
1870
|
} catch (err) {
|
|
1827
1871
|
console.error(`[ApprovalProxy] ${t("approval.processingFailed")}:`, err);
|
|
1828
|
-
this.sendJson(res, 200, { decision: "deny", reason: "
|
|
1872
|
+
this.sendJson(res, 200, { decision: "deny", reason: "Server failed to process request" });
|
|
1829
1873
|
}
|
|
1830
1874
|
}
|
|
1831
1875
|
/** 健康检查端点 */
|
|
@@ -1853,7 +1897,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1853
1897
|
try {
|
|
1854
1898
|
callback(request);
|
|
1855
1899
|
} catch (err) {
|
|
1856
|
-
console.error("[ApprovalProxy]
|
|
1900
|
+
console.error("[ApprovalProxy] Approval request callback error:", err);
|
|
1857
1901
|
}
|
|
1858
1902
|
}
|
|
1859
1903
|
}
|
|
@@ -2031,7 +2075,7 @@ var HookInstaller = class {
|
|
|
2031
2075
|
await (0, import_promises2.chmod)(HOOK_SCRIPT_PATH, 493);
|
|
2032
2076
|
await (0, import_promises2.chmod)(PERMISSION_ACCEPT_PATH, 493);
|
|
2033
2077
|
await this.addHookToSettings();
|
|
2034
|
-
console.log("[HookInstaller] Hook
|
|
2078
|
+
console.log("[HookInstaller] Hook installation complete");
|
|
2035
2079
|
}
|
|
2036
2080
|
/**
|
|
2037
2081
|
* 卸载 hook
|
|
@@ -2041,7 +2085,7 @@ var HookInstaller = class {
|
|
|
2041
2085
|
*/
|
|
2042
2086
|
async uninstall() {
|
|
2043
2087
|
await this.removeHookFromSettings();
|
|
2044
|
-
console.log("[HookInstaller] Hook
|
|
2088
|
+
console.log("[HookInstaller] Hook uninstalled");
|
|
2045
2089
|
}
|
|
2046
2090
|
/**
|
|
2047
2091
|
* 检查 hook 是否已安装
|
|
@@ -2099,7 +2143,7 @@ var HookInstaller = class {
|
|
|
2099
2143
|
if (changed) {
|
|
2100
2144
|
await this.writeClaudeSettings(settings);
|
|
2101
2145
|
} else {
|
|
2102
|
-
console.log("[HookInstaller] Hook
|
|
2146
|
+
console.log("[HookInstaller] Hook config already exists, skipping");
|
|
2103
2147
|
}
|
|
2104
2148
|
}
|
|
2105
2149
|
/**
|
|
@@ -2385,7 +2429,7 @@ var NotificationService = class {
|
|
|
2385
2429
|
for (const { channel, enabled } of this.channelMap.values()) {
|
|
2386
2430
|
if (!enabled) continue;
|
|
2387
2431
|
channel.send(payload).catch((err) => {
|
|
2388
|
-
console.error("[NotificationService]
|
|
2432
|
+
console.error("[NotificationService] Notification send failed:", err);
|
|
2389
2433
|
});
|
|
2390
2434
|
}
|
|
2391
2435
|
}
|
|
@@ -2425,7 +2469,7 @@ var MacNotificationChannel = class {
|
|
|
2425
2469
|
return new Promise((resolve) => {
|
|
2426
2470
|
(0, import_node_child_process.execFile)("osascript", ["-e", script], (err) => {
|
|
2427
2471
|
if (err) {
|
|
2428
|
-
console.warn("[MacNotificationChannel]
|
|
2472
|
+
console.warn("[MacNotificationChannel] Send notification failed:", err.message);
|
|
2429
2473
|
}
|
|
2430
2474
|
resolve();
|
|
2431
2475
|
});
|
|
@@ -2526,7 +2570,7 @@ var ActivityPushChannel = class {
|
|
|
2526
2570
|
this.keyId = config.keyId;
|
|
2527
2571
|
this.authKey = fs2.readFileSync(config.authKeyPath, "utf-8");
|
|
2528
2572
|
this.apnsHost = config.sandbox ? "api.sandbox.push.apple.com" : "api.push.apple.com";
|
|
2529
|
-
console.log(`[ActivityPushChannel]
|
|
2573
|
+
console.log(`[ActivityPushChannel] Initialized (${config.sandbox ? "sandbox" : "production"} mode)`);
|
|
2530
2574
|
}
|
|
2531
2575
|
/** 获取或新建 HTTP/2 长连接 */
|
|
2532
2576
|
getHttp2Client() {
|
|
@@ -2535,7 +2579,7 @@ var ActivityPushChannel = class {
|
|
|
2535
2579
|
}
|
|
2536
2580
|
this.http2Client = http2.connect(`https://${this.apnsHost}`);
|
|
2537
2581
|
this.http2Client.on("error", (err) => {
|
|
2538
|
-
console.warn("[ActivityPushChannel] HTTP/2
|
|
2582
|
+
console.warn("[ActivityPushChannel] HTTP/2 connection error, will reconnect on next request:", err.message);
|
|
2539
2583
|
this.http2Client?.destroy();
|
|
2540
2584
|
this.http2Client = null;
|
|
2541
2585
|
});
|
|
@@ -2547,7 +2591,7 @@ var ActivityPushChannel = class {
|
|
|
2547
2591
|
/** 注册 Activity push token */
|
|
2548
2592
|
addToken(sessionId, token) {
|
|
2549
2593
|
this.tokens.set(sessionId, token);
|
|
2550
|
-
console.log(`[ActivityPushChannel]
|
|
2594
|
+
console.log(`[ActivityPushChannel] Token registered: session=${sessionId}`);
|
|
2551
2595
|
}
|
|
2552
2596
|
/** 移除 Activity push token */
|
|
2553
2597
|
removeToken(sessionId) {
|
|
@@ -2567,7 +2611,7 @@ var ActivityPushChannel = class {
|
|
|
2567
2611
|
try {
|
|
2568
2612
|
await this.sendToAPNs(token, payload);
|
|
2569
2613
|
} catch (err) {
|
|
2570
|
-
console.warn(`[ActivityPushChannel]
|
|
2614
|
+
console.warn(`[ActivityPushChannel] Update failed session=${sessionId}:`, err);
|
|
2571
2615
|
}
|
|
2572
2616
|
}
|
|
2573
2617
|
/** 发送带通知的 content-state 更新(审批请求时使用) */
|
|
@@ -2586,7 +2630,7 @@ var ActivityPushChannel = class {
|
|
|
2586
2630
|
try {
|
|
2587
2631
|
await this.sendToAPNs(token, payload);
|
|
2588
2632
|
} catch (err) {
|
|
2589
|
-
console.warn(`[ActivityPushChannel]
|
|
2633
|
+
console.warn(`[ActivityPushChannel] Alert update failed session=${sessionId}:`, err);
|
|
2590
2634
|
}
|
|
2591
2635
|
}
|
|
2592
2636
|
/** 结束指定会话的 Live Activity */
|
|
@@ -2603,7 +2647,7 @@ var ActivityPushChannel = class {
|
|
|
2603
2647
|
try {
|
|
2604
2648
|
await this.sendToAPNs(token, payload);
|
|
2605
2649
|
} catch (err) {
|
|
2606
|
-
console.warn(`[ActivityPushChannel]
|
|
2650
|
+
console.warn(`[ActivityPushChannel] End failed session=${sessionId}:`, err);
|
|
2607
2651
|
}
|
|
2608
2652
|
this.tokens.delete(sessionId);
|
|
2609
2653
|
}
|
|
@@ -2650,7 +2694,7 @@ var ActivityPushChannel = class {
|
|
|
2650
2694
|
this.http2Client?.destroy();
|
|
2651
2695
|
this.http2Client = null;
|
|
2652
2696
|
}
|
|
2653
|
-
reject(new Error(`APNs
|
|
2697
|
+
reject(new Error(`APNs returned ${statusCode}: ${responseData}`));
|
|
2654
2698
|
}
|
|
2655
2699
|
});
|
|
2656
2700
|
req.on("error", (err) => {
|
|
@@ -3004,9 +3048,9 @@ async function createWithRetry(label, port, factory) {
|
|
|
3004
3048
|
return await factory();
|
|
3005
3049
|
} catch (err) {
|
|
3006
3050
|
if (err?.code === "EADDRINUSE") {
|
|
3007
|
-
console.warn(`[Server]
|
|
3051
|
+
console.warn(`[Server] ${t("server.portInUse", { port })}`);
|
|
3008
3052
|
await killPortProcess(port);
|
|
3009
|
-
console.log(`[Server]
|
|
3053
|
+
console.log(`[Server] ${t("server.restarting", { label })}`);
|
|
3010
3054
|
return await factory();
|
|
3011
3055
|
}
|
|
3012
3056
|
throw err;
|
|
@@ -3042,10 +3086,10 @@ async function start(opts = {}) {
|
|
|
3042
3086
|
try {
|
|
3043
3087
|
const activityChannel = new ActivityPushChannel(opts.activityPush);
|
|
3044
3088
|
notificationService.setActivityPushChannel(activityChannel);
|
|
3045
|
-
console.log(
|
|
3089
|
+
console.log(`[Server] ${t("server.activityPushEnabled")}`);
|
|
3046
3090
|
} catch (err) {
|
|
3047
|
-
console.warn(
|
|
3048
|
-
console.log(
|
|
3091
|
+
console.warn(`[Server] ${t("server.activityPushFailed")}`, err);
|
|
3092
|
+
console.log(`[Server] ${t("server.activityPushContinue")}`);
|
|
3049
3093
|
}
|
|
3050
3094
|
}
|
|
3051
3095
|
const wsBridge = await createWithRetry(
|
|
@@ -3418,7 +3462,7 @@ async function start(opts = {}) {
|
|
|
3418
3462
|
console.log(`[Server] ${t("server.hookExists")}`);
|
|
3419
3463
|
}
|
|
3420
3464
|
} catch (err) {
|
|
3421
|
-
console.error(
|
|
3465
|
+
console.error(`[Server] ${t("server.hookInstallFailed")}`, err);
|
|
3422
3466
|
console.log(`[Server] ${t("server.hookContinue")}`);
|
|
3423
3467
|
}
|
|
3424
3468
|
const stop = async () => {
|
|
@@ -3475,22 +3519,22 @@ async function main() {
|
|
|
3475
3519
|
const server = await start({ enableAutoConnect });
|
|
3476
3520
|
const localIp = getLocalIp();
|
|
3477
3521
|
console.log("-".repeat(50));
|
|
3478
|
-
console.log(
|
|
3479
|
-
console.log(
|
|
3522
|
+
console.log(t("startup.wsPort", { port: server.wsPort }));
|
|
3523
|
+
console.log(t("startup.httpPort", { port: server.httpPort }));
|
|
3480
3524
|
if (server.token === "") {
|
|
3481
|
-
console.log(
|
|
3525
|
+
console.log(t("startup.tokenDisabled"));
|
|
3482
3526
|
console.log();
|
|
3483
|
-
console.log(
|
|
3527
|
+
console.log(t("startup.wsAddress", { ip: localIp, port: server.wsPort }));
|
|
3484
3528
|
} else {
|
|
3485
|
-
console.log(
|
|
3529
|
+
console.log(t("startup.token", { token: server.token }));
|
|
3486
3530
|
console.log();
|
|
3487
|
-
console.log(
|
|
3531
|
+
console.log(t("startup.wsAddressWithToken", { ip: localIp, port: server.wsPort, token: server.token }));
|
|
3488
3532
|
}
|
|
3489
|
-
console.log(
|
|
3533
|
+
console.log(t("startup.healthCheck", { port: server.httpPort }));
|
|
3490
3534
|
console.log("-".repeat(50));
|
|
3491
3535
|
if (server.token === "") {
|
|
3492
3536
|
console.log();
|
|
3493
|
-
console.log("
|
|
3537
|
+
console.log(t("startup.devMode"));
|
|
3494
3538
|
}
|
|
3495
3539
|
console.log();
|
|
3496
3540
|
const qrUrl = buildQrUrl(localIp, server.wsPort, server.token);
|
|
@@ -3500,23 +3544,23 @@ async function main() {
|
|
|
3500
3544
|
});
|
|
3501
3545
|
console.log();
|
|
3502
3546
|
if (enableAutoConnect) {
|
|
3503
|
-
console.log(
|
|
3504
|
-
console.log(
|
|
3547
|
+
console.log(t("startup.autoDiscoveryOn"));
|
|
3548
|
+
console.log(t("startup.autoDiscoveryHint"));
|
|
3505
3549
|
} else {
|
|
3506
|
-
console.log(
|
|
3550
|
+
console.log(t("startup.autoDiscoveryOff"));
|
|
3507
3551
|
}
|
|
3508
3552
|
console.log();
|
|
3509
3553
|
console.log(t("startup.waitingConnection"));
|
|
3510
3554
|
console.log();
|
|
3511
3555
|
const shutdown = async (signal) => {
|
|
3512
3556
|
console.log(`
|
|
3513
|
-
[Main]
|
|
3557
|
+
[Main] ${t("startup.receivedSignal", { signal })}`);
|
|
3514
3558
|
try {
|
|
3515
3559
|
await server.stop();
|
|
3516
|
-
console.log(
|
|
3560
|
+
console.log(`[Main] ${t("startup.goodbye")}`);
|
|
3517
3561
|
process.exit(0);
|
|
3518
3562
|
} catch (err) {
|
|
3519
|
-
console.error(
|
|
3563
|
+
console.error(`[Main] ${t("startup.shutdownError")}`, err);
|
|
3520
3564
|
process.exit(1);
|
|
3521
3565
|
}
|
|
3522
3566
|
};
|
|
@@ -3539,6 +3583,6 @@ function buildQrUrl(ip, wsPort, token) {
|
|
|
3539
3583
|
return token ? `${base}?token=${token}` : base;
|
|
3540
3584
|
}
|
|
3541
3585
|
main().catch((err) => {
|
|
3542
|
-
console.error(
|
|
3586
|
+
console.error(`[Main] ${t("startup.startFailed")}`, err);
|
|
3543
3587
|
process.exit(1);
|
|
3544
3588
|
});
|
package/dist/server.js
CHANGED
|
@@ -39,7 +39,22 @@ var zh = {
|
|
|
39
39
|
startup: {
|
|
40
40
|
banner: " Sessix \u2014 AI \u7F16\u7A0B\u79FB\u52A8\u6307\u6325\u4E2D\u5FC3",
|
|
41
41
|
scanToPair: " \u626B\u7801\u914D\u5BF9\uFF1A",
|
|
42
|
-
waitingConnection: " \u7B49\u5F85\u624B\u673A\u8FDE\u63A5..."
|
|
42
|
+
waitingConnection: " \u7B49\u5F85\u624B\u673A\u8FDE\u63A5...",
|
|
43
|
+
wsPort: " WebSocket \u7AEF\u53E3: {{port}}",
|
|
44
|
+
httpPort: " HTTP \u5BA1\u6279\u7AEF\u53E3: {{port}}",
|
|
45
|
+
tokenDisabled: " \u8FDE\u63A5 Token: (\u5DF2\u7981\u7528\uFF0C\u5F00\u53D1\u6A21\u5F0F)",
|
|
46
|
+
token: " \u8FDE\u63A5 Token: {{token}}",
|
|
47
|
+
wsAddress: " WebSocket \u5730\u5740: ws://{{ip}}:{{port}}",
|
|
48
|
+
wsAddressWithToken: " WebSocket \u5730\u5740: ws://{{ip}}:{{port}}?token={{token}}",
|
|
49
|
+
healthCheck: " \u5065\u5EB7\u68C0\u67E5: http://localhost:{{port}}/health",
|
|
50
|
+
devMode: " [\u5F00\u53D1\u6A21\u5F0F] \u65E0\u9700 Token\uFF0C\u624B\u673A\u7AEF\u53EA\u9700\u8F93\u5165 IP:\u7AEF\u53E3 \u5373\u53EF\u8FDE\u63A5",
|
|
51
|
+
autoDiscoveryOn: " \u{1F4A1} \u81EA\u52A8\u53D1\u73B0\u5DF2\u542F\u7528\uFF0C\u540C\u7F51\u6BB5\u624B\u673A\u53EF\u81EA\u52A8\u8FDE\u63A5",
|
|
52
|
+
autoDiscoveryHint: " \u5982\u5728\u516C\u5171\u7F51\u7EDC\uFF0C\u5EFA\u8BAE\u5173\u95ED: SESSIX_AUTO_CONNECT=false npx sessix-server",
|
|
53
|
+
autoDiscoveryOff: " \u2139\uFE0F \u81EA\u52A8\u53D1\u73B0\u5DF2\u5173\u95ED\uFF0C\u624B\u673A\u9700\u624B\u52A8\u8F93\u5165\u5730\u5740\u8FDE\u63A5",
|
|
54
|
+
receivedSignal: "\u6536\u5230 {{signal}}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...",
|
|
55
|
+
goodbye: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u518D\u89C1\uFF01",
|
|
56
|
+
shutdownError: "\u5173\u95ED\u8FC7\u7A0B\u51FA\u9519:",
|
|
57
|
+
startFailed: "\u542F\u52A8\u5931\u8D25:"
|
|
43
58
|
},
|
|
44
59
|
server: {
|
|
45
60
|
listProjectsFailed: "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25: {{error}}",
|
|
@@ -53,10 +68,16 @@ var zh = {
|
|
|
53
68
|
hookInstalled: "Sessix hook \u5DF2\u5B89\u88C5\u5230 Claude Code",
|
|
54
69
|
hookExists: "Sessix hook \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5B89\u88C5",
|
|
55
70
|
hookContinue: "\u7EE7\u7EED\u542F\u52A8\uFF08hook \u529F\u80FD\u53EF\u80FD\u4E0D\u53EF\u7528\uFF09",
|
|
71
|
+
hookInstallFailed: "Hook \u5B89\u88C5\u5931\u8D25:",
|
|
56
72
|
shuttingDown: "\u6B63\u5728\u4F18\u96C5\u5173\u95ED...",
|
|
57
73
|
shutdownComponentError: "\u5173\u95ED {{label}} \u51FA\u9519",
|
|
58
74
|
shutdownWithErrors: "\u5173\u95ED\u5B8C\u6210\uFF0C{{count}} \u4E2A\u9519\u8BEF",
|
|
59
|
-
shutdownComplete: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED"
|
|
75
|
+
shutdownComplete: "\u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED",
|
|
76
|
+
portInUse: "\u7AEF\u53E3 {{port}} \u88AB\u5360\u7528\uFF0C\u5C1D\u8BD5\u91CA\u653E\u65E7\u8FDB\u7A0B...",
|
|
77
|
+
restarting: "\u91CD\u65B0\u542F\u52A8 {{label}}...",
|
|
78
|
+
activityPushEnabled: "ActivityKit Push \u5DF2\u542F\u7528",
|
|
79
|
+
activityPushFailed: "ActivityKit Push \u521D\u59CB\u5316\u5931\u8D25:",
|
|
80
|
+
activityPushContinue: "\u7EE7\u7EED\u542F\u52A8\uFF08Live Activity \u540E\u53F0\u63A8\u9001\u4E0D\u53EF\u7528\uFF09"
|
|
60
81
|
},
|
|
61
82
|
ws: {
|
|
62
83
|
started: "WebSocket \u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u7AEF\u53E3 {{port}}",
|
|
@@ -119,7 +140,22 @@ var en = {
|
|
|
119
140
|
startup: {
|
|
120
141
|
banner: " Sessix \u2014 AI Coding Mobile Command Center",
|
|
121
142
|
scanToPair: " Scan to pair:",
|
|
122
|
-
waitingConnection: " Waiting for phone connection..."
|
|
143
|
+
waitingConnection: " Waiting for phone connection...",
|
|
144
|
+
wsPort: " WebSocket port: {{port}}",
|
|
145
|
+
httpPort: " HTTP approval port: {{port}}",
|
|
146
|
+
tokenDisabled: " Token: (disabled, dev mode)",
|
|
147
|
+
token: " Token: {{token}}",
|
|
148
|
+
wsAddress: " WebSocket URL: ws://{{ip}}:{{port}}",
|
|
149
|
+
wsAddressWithToken: " WebSocket URL: ws://{{ip}}:{{port}}?token={{token}}",
|
|
150
|
+
healthCheck: " Health check: http://localhost:{{port}}/health",
|
|
151
|
+
devMode: " [Dev mode] No token required, just enter IP:port on your phone",
|
|
152
|
+
autoDiscoveryOn: " Auto-discovery enabled, phones on the same network can connect automatically",
|
|
153
|
+
autoDiscoveryHint: " On public networks, disable with: SESSIX_AUTO_CONNECT=false npx sessix-server",
|
|
154
|
+
autoDiscoveryOff: " Auto-discovery disabled, phone must enter address manually",
|
|
155
|
+
receivedSignal: "Received {{signal}}, graceful shutdown...",
|
|
156
|
+
goodbye: "All services closed, goodbye!",
|
|
157
|
+
shutdownError: "Shutdown error:",
|
|
158
|
+
startFailed: "Startup failed:"
|
|
123
159
|
},
|
|
124
160
|
server: {
|
|
125
161
|
listProjectsFailed: "Failed to list projects: {{error}}",
|
|
@@ -133,10 +169,16 @@ var en = {
|
|
|
133
169
|
hookInstalled: "Sessix hook installed to Claude Code",
|
|
134
170
|
hookExists: "Sessix hook already exists, skipping installation",
|
|
135
171
|
hookContinue: "Continuing startup (hook functionality may be unavailable)",
|
|
172
|
+
hookInstallFailed: "Hook installation failed:",
|
|
136
173
|
shuttingDown: "Graceful shutdown in progress...",
|
|
137
174
|
shutdownComponentError: "Error closing {{label}}",
|
|
138
175
|
shutdownWithErrors: "Shutdown complete, {{count}} error(s)",
|
|
139
|
-
shutdownComplete: "All services closed"
|
|
176
|
+
shutdownComplete: "All services closed",
|
|
177
|
+
portInUse: "Port {{port}} in use, attempting to release old process...",
|
|
178
|
+
restarting: "Restarting {{label}}...",
|
|
179
|
+
activityPushEnabled: "ActivityKit Push enabled",
|
|
180
|
+
activityPushFailed: "ActivityKit Push init failed:",
|
|
181
|
+
activityPushContinue: "Continuing startup (Live Activity background push unavailable)"
|
|
140
182
|
},
|
|
141
183
|
ws: {
|
|
142
184
|
started: "WebSocket server started on port {{port}}",
|
|
@@ -195,7 +237,10 @@ var en = {
|
|
|
195
237
|
};
|
|
196
238
|
|
|
197
239
|
// src/i18n/index.ts
|
|
240
|
+
var locales = { zh, en };
|
|
198
241
|
function detectLocale() {
|
|
242
|
+
const explicit = process.env.SESSIX_LANG;
|
|
243
|
+
if (explicit && explicit in locales) return explicit;
|
|
199
244
|
try {
|
|
200
245
|
const raw = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || "";
|
|
201
246
|
if (raw.startsWith("zh")) return "zh";
|
|
@@ -203,7 +248,6 @@ function detectLocale() {
|
|
|
203
248
|
}
|
|
204
249
|
return "en";
|
|
205
250
|
}
|
|
206
|
-
var locales = { zh, en };
|
|
207
251
|
var currentLocale = detectLocale();
|
|
208
252
|
var currentMessages = locales[currentLocale] ?? en;
|
|
209
253
|
function t(key, params) {
|
|
@@ -307,12 +351,12 @@ var ProcessProvider = class {
|
|
|
307
351
|
session.pid = proc.pid;
|
|
308
352
|
this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort });
|
|
309
353
|
proc.on("error", (err) => {
|
|
310
|
-
console.error(`[ProcessProvider]
|
|
354
|
+
console.error(`[ProcessProvider] Session ${sessionId} process error:`, err.message);
|
|
311
355
|
this.activeSessions.delete(sessionId);
|
|
312
356
|
const syntheticResult = {
|
|
313
357
|
type: "result",
|
|
314
358
|
subtype: "error",
|
|
315
|
-
result:
|
|
359
|
+
result: `Process spawn failed: ${err.message}`,
|
|
316
360
|
session_id: sessionId,
|
|
317
361
|
duration_ms: 0,
|
|
318
362
|
is_error: true,
|
|
@@ -365,7 +409,7 @@ var ProcessProvider = class {
|
|
|
365
409
|
async sendMessage(sessionId, message, permissionMode, images) {
|
|
366
410
|
const entry = this.activeSessions.get(sessionId);
|
|
367
411
|
if (!entry) {
|
|
368
|
-
throw new Error(
|
|
412
|
+
throw new Error(`Session ${sessionId} not found or already ended`);
|
|
369
413
|
}
|
|
370
414
|
const modeChanged = permissionMode != null && permissionMode !== (entry.permissionMode ?? "default");
|
|
371
415
|
if (!modeChanged && entry.process.exitCode === null && entry.process.signalCode === null && !entry.process.stdin?.destroyed) {
|
|
@@ -375,7 +419,7 @@ var ProcessProvider = class {
|
|
|
375
419
|
return;
|
|
376
420
|
}
|
|
377
421
|
if (modeChanged) {
|
|
378
|
-
console.log(`[ProcessProvider]
|
|
422
|
+
console.log(`[ProcessProvider] Session ${sessionId}: permission mode change ${entry.permissionMode ?? "default"} \u2192 ${permissionMode}, respawn`);
|
|
379
423
|
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
380
424
|
try {
|
|
381
425
|
entry.process.stdin?.end();
|
|
@@ -384,7 +428,7 @@ var ProcessProvider = class {
|
|
|
384
428
|
entry.process.kill("SIGTERM");
|
|
385
429
|
}
|
|
386
430
|
} else {
|
|
387
|
-
console.log(`[ProcessProvider]
|
|
431
|
+
console.log(`[ProcessProvider] Session ${sessionId}: process exited, respawning`);
|
|
388
432
|
}
|
|
389
433
|
const savedPendingQuestion = entry.pendingQuestion;
|
|
390
434
|
const newMode = permissionMode ?? entry.permissionMode;
|
|
@@ -397,12 +441,12 @@ var ProcessProvider = class {
|
|
|
397
441
|
entry.permissionMode = newMode;
|
|
398
442
|
entry.pendingQuestion = savedPendingQuestion;
|
|
399
443
|
proc.on("error", (err) => {
|
|
400
|
-
console.error(`[ProcessProvider]
|
|
444
|
+
console.error(`[ProcessProvider] Session ${sessionId} sendMessage process error:`, err.message);
|
|
401
445
|
this.activeSessions.delete(sessionId);
|
|
402
446
|
const syntheticResult = {
|
|
403
447
|
type: "result",
|
|
404
448
|
subtype: "error",
|
|
405
|
-
result:
|
|
449
|
+
result: `Failed to send message: ${err.message}`,
|
|
406
450
|
session_id: sessionId,
|
|
407
451
|
duration_ms: 0,
|
|
408
452
|
is_error: true,
|
|
@@ -493,16 +537,16 @@ var ProcessProvider = class {
|
|
|
493
537
|
parent_tool_use_id: null
|
|
494
538
|
});
|
|
495
539
|
if (!proc.stdin || proc.stdin.destroyed) {
|
|
496
|
-
console.error(`[ProcessProvider] stdin
|
|
540
|
+
console.error(`[ProcessProvider] stdin unavailable, message lost`);
|
|
497
541
|
if (sessionId) {
|
|
498
|
-
this.emitWriteError(sessionId, "
|
|
542
|
+
this.emitWriteError(sessionId, "Process stdin closed, message not delivered");
|
|
499
543
|
}
|
|
500
544
|
return;
|
|
501
545
|
}
|
|
502
546
|
proc.stdin.write(payload + "\n", (err) => {
|
|
503
547
|
if (err && sessionId) {
|
|
504
|
-
console.error(`[ProcessProvider]
|
|
505
|
-
this.emitWriteError(sessionId,
|
|
548
|
+
console.error(`[ProcessProvider] Session ${sessionId} stdin write failed:`, err.message);
|
|
549
|
+
this.emitWriteError(sessionId, `Failed to send message: ${err.message}`);
|
|
506
550
|
}
|
|
507
551
|
});
|
|
508
552
|
}
|
|
@@ -526,7 +570,7 @@ var ProcessProvider = class {
|
|
|
526
570
|
*/
|
|
527
571
|
attachStdoutListener(sessionId, proc) {
|
|
528
572
|
if (!proc.stdout) {
|
|
529
|
-
console.warn(`[ProcessProvider]
|
|
573
|
+
console.warn(`[ProcessProvider] Session ${sessionId}: stdout unavailable`);
|
|
530
574
|
return;
|
|
531
575
|
}
|
|
532
576
|
const rl = (0, import_readline.createInterface)({
|
|
@@ -564,7 +608,7 @@ var ProcessProvider = class {
|
|
|
564
608
|
this.emitter.emit(this.getEventName(sessionId), event);
|
|
565
609
|
} else {
|
|
566
610
|
console.warn(
|
|
567
|
-
`[ProcessProvider]
|
|
611
|
+
`[ProcessProvider] Session ${sessionId}: failed to parse line: ${trimmed.substring(0, 100)}`
|
|
568
612
|
);
|
|
569
613
|
}
|
|
570
614
|
});
|
|
@@ -577,7 +621,7 @@ var ProcessProvider = class {
|
|
|
577
621
|
proc.stderr.on("data", (data) => {
|
|
578
622
|
const text = data.toString().trim();
|
|
579
623
|
if (text) {
|
|
580
|
-
console.error(`[ProcessProvider]
|
|
624
|
+
console.error(`[ProcessProvider] Session ${sessionId} stderr: ${text}`);
|
|
581
625
|
}
|
|
582
626
|
});
|
|
583
627
|
}
|
|
@@ -608,7 +652,7 @@ var ProcessProvider = class {
|
|
|
608
652
|
entry.session.status = isNormal ? "idle" : "error";
|
|
609
653
|
if (!isNormal) {
|
|
610
654
|
console.error(
|
|
611
|
-
`[ProcessProvider]
|
|
655
|
+
`[ProcessProvider] Session ${sessionId}: process exited abnormally code=${code} signal=${signal}`
|
|
612
656
|
);
|
|
613
657
|
}
|
|
614
658
|
const syntheticResult = {
|
|
@@ -616,7 +660,7 @@ var ProcessProvider = class {
|
|
|
616
660
|
subtype: isNormal ? "success" : "error",
|
|
617
661
|
session_id: sessionId,
|
|
618
662
|
is_error: !isNormal,
|
|
619
|
-
result: isNormal ? "" :
|
|
663
|
+
result: isNormal ? "" : `Process exited code=${code} signal=${signal}`,
|
|
620
664
|
duration_ms: 0,
|
|
621
665
|
num_turns: 0
|
|
622
666
|
};
|
|
@@ -664,7 +708,7 @@ var ProcessProvider = class {
|
|
|
664
708
|
* 使用 --output-format text 做一次性调用,返回纯文本结果。
|
|
665
709
|
*/
|
|
666
710
|
async generateSuggestion(context) {
|
|
667
|
-
const prompt =
|
|
711
|
+
const prompt = `You are an AI coding assistant. Based on the following Claude Code conversation context, suggest the most valuable next instruction for the user (give the instruction directly, no explanation, no quotes):
|
|
668
712
|
|
|
669
713
|
${context}`;
|
|
670
714
|
return new Promise((resolve, reject) => {
|
|
@@ -684,7 +728,7 @@ ${context}`;
|
|
|
684
728
|
if (code === 0) {
|
|
685
729
|
resolve(output.trim());
|
|
686
730
|
} else {
|
|
687
|
-
reject(new Error(`generateSuggestion
|
|
731
|
+
reject(new Error(`generateSuggestion process exit code: ${code}`));
|
|
688
732
|
}
|
|
689
733
|
});
|
|
690
734
|
proc.once("error", reject);
|
|
@@ -699,10 +743,10 @@ ${context}`;
|
|
|
699
743
|
async answerQuestion(sessionId, toolUseId, answer) {
|
|
700
744
|
const entry = this.activeSessions.get(sessionId);
|
|
701
745
|
if (!entry) {
|
|
702
|
-
throw new Error(
|
|
746
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
703
747
|
}
|
|
704
748
|
if (!entry.process.stdin || entry.process.stdin.destroyed) {
|
|
705
|
-
throw new Error(
|
|
749
|
+
throw new Error(`Session ${sessionId} stdin unavailable`);
|
|
706
750
|
}
|
|
707
751
|
const toolResult = JSON.stringify({
|
|
708
752
|
type: "tool_result",
|
|
@@ -715,7 +759,7 @@ ${context}`;
|
|
|
715
759
|
else resolve();
|
|
716
760
|
});
|
|
717
761
|
});
|
|
718
|
-
console.log(`[ProcessProvider]
|
|
762
|
+
console.log(`[ProcessProvider] Session ${sessionId}: AskUserQuestion answered (toolUseId=${toolUseId})`);
|
|
719
763
|
}
|
|
720
764
|
/**
|
|
721
765
|
* 订阅指定会话的 AskUserQuestion 事件
|
|
@@ -800,7 +844,7 @@ var SessionManager = class {
|
|
|
800
844
|
this.sessionProjectPaths.set(session.id, projectPath);
|
|
801
845
|
this.unsubscribeSession(session.id);
|
|
802
846
|
this.subscribeToSession(session.id);
|
|
803
|
-
console.log(`[SessionManager]
|
|
847
|
+
console.log(`[SessionManager] Session created: ${session.id} (project: ${projectPath})`);
|
|
804
848
|
return session;
|
|
805
849
|
}
|
|
806
850
|
/**
|
|
@@ -809,7 +853,7 @@ var SessionManager = class {
|
|
|
809
853
|
async sendMessage(sessionId, message, permissionMode, images) {
|
|
810
854
|
await this.provider.sendMessage(sessionId, message, permissionMode, images);
|
|
811
855
|
this.updateSessionStatus(sessionId, "running");
|
|
812
|
-
console.log(`[SessionManager]
|
|
856
|
+
console.log(`[SessionManager] Message sent to session: ${sessionId}`);
|
|
813
857
|
}
|
|
814
858
|
/**
|
|
815
859
|
* 终止会话
|
|
@@ -828,7 +872,7 @@ var SessionManager = class {
|
|
|
828
872
|
this.pendingAssistantEvents.delete(sessionId);
|
|
829
873
|
}
|
|
830
874
|
await this.provider.killSession(sessionId);
|
|
831
|
-
console.log(`[SessionManager]
|
|
875
|
+
console.log(`[SessionManager] Session killed: ${sessionId}`);
|
|
832
876
|
}
|
|
833
877
|
/**
|
|
834
878
|
* 获取会话的缓冲事件(用于新订阅者重放)
|
|
@@ -854,13 +898,13 @@ var SessionManager = class {
|
|
|
854
898
|
handleQuestionResponse(requestId, answer) {
|
|
855
899
|
const pending = this.pendingQuestions.get(requestId);
|
|
856
900
|
if (!pending) {
|
|
857
|
-
console.warn(`[SessionManager]
|
|
901
|
+
console.warn(`[SessionManager] Question request not found: ${requestId}`);
|
|
858
902
|
return;
|
|
859
903
|
}
|
|
860
904
|
this.pendingQuestions.delete(requestId);
|
|
861
905
|
this.updateSessionStatus(pending.sessionId, "running");
|
|
862
906
|
pending.resolve(answer);
|
|
863
|
-
console.log(`[SessionManager]
|
|
907
|
+
console.log(`[SessionManager] Question answered: ${requestId}`);
|
|
864
908
|
}
|
|
865
909
|
/**
|
|
866
910
|
* 获取指定会话的所有待回答问题(用于重连时恢复)
|
|
@@ -944,7 +988,7 @@ var SessionManager = class {
|
|
|
944
988
|
this.pendingQuestions.clear();
|
|
945
989
|
this.lastBroadcastStatus.clear();
|
|
946
990
|
this.eventCallbacks.length = 0;
|
|
947
|
-
console.log("[SessionManager]
|
|
991
|
+
console.log("[SessionManager] Destroyed");
|
|
948
992
|
}
|
|
949
993
|
// ============================================
|
|
950
994
|
// 内部方法
|
|
@@ -1098,7 +1142,7 @@ var SessionManager = class {
|
|
|
1098
1142
|
status: newStatus,
|
|
1099
1143
|
stats
|
|
1100
1144
|
});
|
|
1101
|
-
console.log(`[SessionManager]
|
|
1145
|
+
console.log(`[SessionManager] Session ${sessionId} status change: ${lastStatus ?? "(none)"} \u2192 ${newStatus}`);
|
|
1102
1146
|
}
|
|
1103
1147
|
}
|
|
1104
1148
|
/** 获取会话统计(含 runningStartedAt) */
|
|
@@ -1127,7 +1171,7 @@ var SessionManager = class {
|
|
|
1127
1171
|
createdAt: Date.now()
|
|
1128
1172
|
};
|
|
1129
1173
|
this.emit({ type: "question_request", request: updatedRequest });
|
|
1130
|
-
console.log(`[SessionManager]
|
|
1174
|
+
console.log(`[SessionManager] Session ${sessionId}: AskUserQuestion updated (requestId=${existingRequestId})`);
|
|
1131
1175
|
return;
|
|
1132
1176
|
}
|
|
1133
1177
|
const requestId = (0, import_uuid2.v4)();
|
|
@@ -1148,10 +1192,10 @@ var SessionManager = class {
|
|
|
1148
1192
|
try {
|
|
1149
1193
|
await this.provider.answerQuestion(sessionId, toolUseId, answer);
|
|
1150
1194
|
} catch (err) {
|
|
1151
|
-
console.error(`[SessionManager] answerQuestion
|
|
1195
|
+
console.error(`[SessionManager] answerQuestion failed (${sessionId}):`, err);
|
|
1152
1196
|
}
|
|
1153
1197
|
}).catch((err) => console.error("[SessionManager] answerPromise rejected:", err));
|
|
1154
|
-
console.log(`[SessionManager]
|
|
1198
|
+
console.log(`[SessionManager] Session ${sessionId}: AskUserQuestion pushed (requestId=${requestId})`);
|
|
1155
1199
|
}
|
|
1156
1200
|
/**
|
|
1157
1201
|
* 清除指定会话的所有待回答问题
|
|
@@ -1175,7 +1219,7 @@ var SessionManager = class {
|
|
|
1175
1219
|
try {
|
|
1176
1220
|
callback(event);
|
|
1177
1221
|
} catch (err) {
|
|
1178
|
-
console.error("[SessionManager]
|
|
1222
|
+
console.error("[SessionManager] Event callback error:", err);
|
|
1179
1223
|
}
|
|
1180
1224
|
}
|
|
1181
1225
|
}
|
|
@@ -1252,7 +1296,7 @@ var SessionFileWatcher = class {
|
|
|
1252
1296
|
if (!entry) return;
|
|
1253
1297
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
1254
1298
|
entry.idleTimer = setTimeout(() => {
|
|
1255
|
-
console.log(`[SessionFileWatcher]
|
|
1299
|
+
console.log(`[SessionFileWatcher] Idle timeout, stop watching: ${sessionId}`);
|
|
1256
1300
|
this.unwatch(sessionId);
|
|
1257
1301
|
}, this.IDLE_TIMEOUT_MS);
|
|
1258
1302
|
}
|
|
@@ -1455,7 +1499,7 @@ var WsBridge = class _WsBridge {
|
|
|
1455
1499
|
if (err) {
|
|
1456
1500
|
reject(err);
|
|
1457
1501
|
} else {
|
|
1458
|
-
console.log("[WsBridge] WebSocket
|
|
1502
|
+
console.log("[WsBridge] WebSocket server closed");
|
|
1459
1503
|
resolve();
|
|
1460
1504
|
}
|
|
1461
1505
|
});
|
|
@@ -1478,12 +1522,12 @@ var WsBridge = class _WsBridge {
|
|
|
1478
1522
|
/** 处理新的 WebSocket 连接 */
|
|
1479
1523
|
handleConnection(ws) {
|
|
1480
1524
|
this.lastPongMap.set(ws, Date.now());
|
|
1481
|
-
console.log(`[WsBridge]
|
|
1525
|
+
console.log(`[WsBridge] New client connected, connections: ${this.getConnectionCount()}`);
|
|
1482
1526
|
for (const callback of this.connectionCallbacks) {
|
|
1483
1527
|
try {
|
|
1484
1528
|
callback(ws);
|
|
1485
1529
|
} catch (err) {
|
|
1486
|
-
console.error("[WsBridge]
|
|
1530
|
+
console.error("[WsBridge] Connection callback error:", err);
|
|
1487
1531
|
}
|
|
1488
1532
|
}
|
|
1489
1533
|
ws.on("pong", () => {
|
|
@@ -1494,10 +1538,10 @@ var WsBridge = class _WsBridge {
|
|
|
1494
1538
|
const event = JSON.parse(raw.toString());
|
|
1495
1539
|
this.dispatchClientEvent(event, ws);
|
|
1496
1540
|
} catch (err) {
|
|
1497
|
-
console.error("[WsBridge]
|
|
1541
|
+
console.error("[WsBridge] Message parse error:", err);
|
|
1498
1542
|
this.send(ws, {
|
|
1499
1543
|
type: "error",
|
|
1500
|
-
message: "
|
|
1544
|
+
message: "Invalid message format",
|
|
1501
1545
|
code: "INVALID_MESSAGE"
|
|
1502
1546
|
});
|
|
1503
1547
|
}
|
|
@@ -1507,18 +1551,18 @@ var WsBridge = class _WsBridge {
|
|
|
1507
1551
|
this.viewingSessions.delete(ws);
|
|
1508
1552
|
this.messageQueues.delete(ws);
|
|
1509
1553
|
setTimeout(() => {
|
|
1510
|
-
console.log(`[WsBridge]
|
|
1554
|
+
console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
|
|
1511
1555
|
for (const cb of this.disconnectCallbacks) {
|
|
1512
1556
|
try {
|
|
1513
1557
|
cb();
|
|
1514
1558
|
} catch (err) {
|
|
1515
|
-
console.error("[WsBridge]
|
|
1559
|
+
console.error("[WsBridge] Disconnect callback error:", err);
|
|
1516
1560
|
}
|
|
1517
1561
|
}
|
|
1518
1562
|
}, 0);
|
|
1519
1563
|
});
|
|
1520
1564
|
ws.on("error", (err) => {
|
|
1521
|
-
console.error("[WsBridge]
|
|
1565
|
+
console.error("[WsBridge] Connection error:", err.message);
|
|
1522
1566
|
});
|
|
1523
1567
|
}
|
|
1524
1568
|
/**
|
|
@@ -1534,7 +1578,7 @@ var WsBridge = class _WsBridge {
|
|
|
1534
1578
|
try {
|
|
1535
1579
|
await callback(event, ws);
|
|
1536
1580
|
} catch (err) {
|
|
1537
|
-
console.error("[WsBridge]
|
|
1581
|
+
console.error("[WsBridge] Event callback error:", err);
|
|
1538
1582
|
}
|
|
1539
1583
|
}
|
|
1540
1584
|
});
|
|
@@ -1548,7 +1592,7 @@ var WsBridge = class _WsBridge {
|
|
|
1548
1592
|
for (const ws of this.wss.clients) {
|
|
1549
1593
|
const lastPong = this.lastPongMap.get(ws) ?? 0;
|
|
1550
1594
|
if (now - lastPong > 45e3) {
|
|
1551
|
-
console.log("[WsBridge]
|
|
1595
|
+
console.log("[WsBridge] Dead connection detected, terminating");
|
|
1552
1596
|
ws.terminate();
|
|
1553
1597
|
continue;
|
|
1554
1598
|
}
|
|
@@ -1807,7 +1851,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1807
1851
|
projectPath,
|
|
1808
1852
|
toolName,
|
|
1809
1853
|
toolInput,
|
|
1810
|
-
description: String(payload.description ?? body.description ?? `${toolName}
|
|
1854
|
+
description: String(payload.description ?? body.description ?? `${toolName} tool call request`),
|
|
1811
1855
|
createdAt: Date.now()
|
|
1812
1856
|
};
|
|
1813
1857
|
console.log(`[ApprovalProxy] ${t("approval.received")}: ${requestId} (${approvalRequest.toolName})`);
|
|
@@ -1833,7 +1877,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1833
1877
|
this.sendJson(res, 200, decision);
|
|
1834
1878
|
} catch (err) {
|
|
1835
1879
|
console.error(`[ApprovalProxy] ${t("approval.processingFailed")}:`, err);
|
|
1836
|
-
this.sendJson(res, 200, { decision: "deny", reason: "
|
|
1880
|
+
this.sendJson(res, 200, { decision: "deny", reason: "Server failed to process request" });
|
|
1837
1881
|
}
|
|
1838
1882
|
}
|
|
1839
1883
|
/** 健康检查端点 */
|
|
@@ -1861,7 +1905,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1861
1905
|
try {
|
|
1862
1906
|
callback(request);
|
|
1863
1907
|
} catch (err) {
|
|
1864
|
-
console.error("[ApprovalProxy]
|
|
1908
|
+
console.error("[ApprovalProxy] Approval request callback error:", err);
|
|
1865
1909
|
}
|
|
1866
1910
|
}
|
|
1867
1911
|
}
|
|
@@ -2039,7 +2083,7 @@ var HookInstaller = class {
|
|
|
2039
2083
|
await (0, import_promises2.chmod)(HOOK_SCRIPT_PATH, 493);
|
|
2040
2084
|
await (0, import_promises2.chmod)(PERMISSION_ACCEPT_PATH, 493);
|
|
2041
2085
|
await this.addHookToSettings();
|
|
2042
|
-
console.log("[HookInstaller] Hook
|
|
2086
|
+
console.log("[HookInstaller] Hook installation complete");
|
|
2043
2087
|
}
|
|
2044
2088
|
/**
|
|
2045
2089
|
* 卸载 hook
|
|
@@ -2049,7 +2093,7 @@ var HookInstaller = class {
|
|
|
2049
2093
|
*/
|
|
2050
2094
|
async uninstall() {
|
|
2051
2095
|
await this.removeHookFromSettings();
|
|
2052
|
-
console.log("[HookInstaller] Hook
|
|
2096
|
+
console.log("[HookInstaller] Hook uninstalled");
|
|
2053
2097
|
}
|
|
2054
2098
|
/**
|
|
2055
2099
|
* 检查 hook 是否已安装
|
|
@@ -2107,7 +2151,7 @@ var HookInstaller = class {
|
|
|
2107
2151
|
if (changed) {
|
|
2108
2152
|
await this.writeClaudeSettings(settings);
|
|
2109
2153
|
} else {
|
|
2110
|
-
console.log("[HookInstaller] Hook
|
|
2154
|
+
console.log("[HookInstaller] Hook config already exists, skipping");
|
|
2111
2155
|
}
|
|
2112
2156
|
}
|
|
2113
2157
|
/**
|
|
@@ -2393,7 +2437,7 @@ var NotificationService = class {
|
|
|
2393
2437
|
for (const { channel, enabled } of this.channelMap.values()) {
|
|
2394
2438
|
if (!enabled) continue;
|
|
2395
2439
|
channel.send(payload).catch((err) => {
|
|
2396
|
-
console.error("[NotificationService]
|
|
2440
|
+
console.error("[NotificationService] Notification send failed:", err);
|
|
2397
2441
|
});
|
|
2398
2442
|
}
|
|
2399
2443
|
}
|
|
@@ -2433,7 +2477,7 @@ var MacNotificationChannel = class {
|
|
|
2433
2477
|
return new Promise((resolve) => {
|
|
2434
2478
|
(0, import_node_child_process.execFile)("osascript", ["-e", script], (err) => {
|
|
2435
2479
|
if (err) {
|
|
2436
|
-
console.warn("[MacNotificationChannel]
|
|
2480
|
+
console.warn("[MacNotificationChannel] Send notification failed:", err.message);
|
|
2437
2481
|
}
|
|
2438
2482
|
resolve();
|
|
2439
2483
|
});
|
|
@@ -2534,7 +2578,7 @@ var ActivityPushChannel = class {
|
|
|
2534
2578
|
this.keyId = config.keyId;
|
|
2535
2579
|
this.authKey = fs2.readFileSync(config.authKeyPath, "utf-8");
|
|
2536
2580
|
this.apnsHost = config.sandbox ? "api.sandbox.push.apple.com" : "api.push.apple.com";
|
|
2537
|
-
console.log(`[ActivityPushChannel]
|
|
2581
|
+
console.log(`[ActivityPushChannel] Initialized (${config.sandbox ? "sandbox" : "production"} mode)`);
|
|
2538
2582
|
}
|
|
2539
2583
|
/** 获取或新建 HTTP/2 长连接 */
|
|
2540
2584
|
getHttp2Client() {
|
|
@@ -2543,7 +2587,7 @@ var ActivityPushChannel = class {
|
|
|
2543
2587
|
}
|
|
2544
2588
|
this.http2Client = http2.connect(`https://${this.apnsHost}`);
|
|
2545
2589
|
this.http2Client.on("error", (err) => {
|
|
2546
|
-
console.warn("[ActivityPushChannel] HTTP/2
|
|
2590
|
+
console.warn("[ActivityPushChannel] HTTP/2 connection error, will reconnect on next request:", err.message);
|
|
2547
2591
|
this.http2Client?.destroy();
|
|
2548
2592
|
this.http2Client = null;
|
|
2549
2593
|
});
|
|
@@ -2555,7 +2599,7 @@ var ActivityPushChannel = class {
|
|
|
2555
2599
|
/** 注册 Activity push token */
|
|
2556
2600
|
addToken(sessionId, token) {
|
|
2557
2601
|
this.tokens.set(sessionId, token);
|
|
2558
|
-
console.log(`[ActivityPushChannel]
|
|
2602
|
+
console.log(`[ActivityPushChannel] Token registered: session=${sessionId}`);
|
|
2559
2603
|
}
|
|
2560
2604
|
/** 移除 Activity push token */
|
|
2561
2605
|
removeToken(sessionId) {
|
|
@@ -2575,7 +2619,7 @@ var ActivityPushChannel = class {
|
|
|
2575
2619
|
try {
|
|
2576
2620
|
await this.sendToAPNs(token, payload);
|
|
2577
2621
|
} catch (err) {
|
|
2578
|
-
console.warn(`[ActivityPushChannel]
|
|
2622
|
+
console.warn(`[ActivityPushChannel] Update failed session=${sessionId}:`, err);
|
|
2579
2623
|
}
|
|
2580
2624
|
}
|
|
2581
2625
|
/** 发送带通知的 content-state 更新(审批请求时使用) */
|
|
@@ -2594,7 +2638,7 @@ var ActivityPushChannel = class {
|
|
|
2594
2638
|
try {
|
|
2595
2639
|
await this.sendToAPNs(token, payload);
|
|
2596
2640
|
} catch (err) {
|
|
2597
|
-
console.warn(`[ActivityPushChannel]
|
|
2641
|
+
console.warn(`[ActivityPushChannel] Alert update failed session=${sessionId}:`, err);
|
|
2598
2642
|
}
|
|
2599
2643
|
}
|
|
2600
2644
|
/** 结束指定会话的 Live Activity */
|
|
@@ -2611,7 +2655,7 @@ var ActivityPushChannel = class {
|
|
|
2611
2655
|
try {
|
|
2612
2656
|
await this.sendToAPNs(token, payload);
|
|
2613
2657
|
} catch (err) {
|
|
2614
|
-
console.warn(`[ActivityPushChannel]
|
|
2658
|
+
console.warn(`[ActivityPushChannel] End failed session=${sessionId}:`, err);
|
|
2615
2659
|
}
|
|
2616
2660
|
this.tokens.delete(sessionId);
|
|
2617
2661
|
}
|
|
@@ -2658,7 +2702,7 @@ var ActivityPushChannel = class {
|
|
|
2658
2702
|
this.http2Client?.destroy();
|
|
2659
2703
|
this.http2Client = null;
|
|
2660
2704
|
}
|
|
2661
|
-
reject(new Error(`APNs
|
|
2705
|
+
reject(new Error(`APNs returned ${statusCode}: ${responseData}`));
|
|
2662
2706
|
}
|
|
2663
2707
|
});
|
|
2664
2708
|
req.on("error", (err) => {
|
|
@@ -3012,9 +3056,9 @@ async function createWithRetry(label, port, factory) {
|
|
|
3012
3056
|
return await factory();
|
|
3013
3057
|
} catch (err) {
|
|
3014
3058
|
if (err?.code === "EADDRINUSE") {
|
|
3015
|
-
console.warn(`[Server]
|
|
3059
|
+
console.warn(`[Server] ${t("server.portInUse", { port })}`);
|
|
3016
3060
|
await killPortProcess(port);
|
|
3017
|
-
console.log(`[Server]
|
|
3061
|
+
console.log(`[Server] ${t("server.restarting", { label })}`);
|
|
3018
3062
|
return await factory();
|
|
3019
3063
|
}
|
|
3020
3064
|
throw err;
|
|
@@ -3050,10 +3094,10 @@ async function start(opts = {}) {
|
|
|
3050
3094
|
try {
|
|
3051
3095
|
const activityChannel = new ActivityPushChannel(opts.activityPush);
|
|
3052
3096
|
notificationService.setActivityPushChannel(activityChannel);
|
|
3053
|
-
console.log(
|
|
3097
|
+
console.log(`[Server] ${t("server.activityPushEnabled")}`);
|
|
3054
3098
|
} catch (err) {
|
|
3055
|
-
console.warn(
|
|
3056
|
-
console.log(
|
|
3099
|
+
console.warn(`[Server] ${t("server.activityPushFailed")}`, err);
|
|
3100
|
+
console.log(`[Server] ${t("server.activityPushContinue")}`);
|
|
3057
3101
|
}
|
|
3058
3102
|
}
|
|
3059
3103
|
const wsBridge = await createWithRetry(
|
|
@@ -3426,7 +3470,7 @@ async function start(opts = {}) {
|
|
|
3426
3470
|
console.log(`[Server] ${t("server.hookExists")}`);
|
|
3427
3471
|
}
|
|
3428
3472
|
} catch (err) {
|
|
3429
|
-
console.error(
|
|
3473
|
+
console.error(`[Server] ${t("server.hookInstallFailed")}`, err);
|
|
3430
3474
|
console.log(`[Server] ${t("server.hookContinue")}`);
|
|
3431
3475
|
}
|
|
3432
3476
|
const stop = async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sessix-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"bin": {
|
|
5
5
|
"sessix-server": "./dist/index.js"
|
|
6
6
|
},
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"start": "node dist/index.js"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@sessix/shared": "*",
|
|
29
30
|
"ws": "^8.18.0",
|
|
30
31
|
"bonjour-service": "^1.3.0",
|
|
31
32
|
"chokidar": "^4.0.0",
|
|
@@ -33,7 +34,6 @@
|
|
|
33
34
|
"qrcode-terminal": "^0.12.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@sessix/shared": "*",
|
|
37
37
|
"@types/ws": "^8.5.0",
|
|
38
38
|
"@types/uuid": "^10.0.0",
|
|
39
39
|
"@types/qrcode-terminal": "^0.12.2",
|