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.
Files changed (3) hide show
  1. package/dist/index.js +128 -84
  2. package/dist/server.js +113 -69
  3. 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] \u4F1A\u8BDD ${sessionId} \u8FDB\u7A0B\u9519\u8BEF:`, err.message);
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: `\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`,
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(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728\u6216\u5DF2\u7ED3\u675F`);
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] \u4F1A\u8BDD ${sessionId}: \u6743\u9650\u6A21\u5F0F\u5207\u6362 ${entry.permissionMode ?? "default"} \u2192 ${permissionMode}\uFF0Crespawn`);
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] \u4F1A\u8BDD ${sessionId}: \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0Crespawn \u91CD\u65B0\u542F\u52A8`);
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] \u4F1A\u8BDD ${sessionId} sendMessage \u8FDB\u7A0B\u9519\u8BEF:`, err.message);
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: `\u6D88\u606F\u53D1\u9001\u5931\u8D25: ${err.message}`,
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 \u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u4E22\u5931`);
532
+ console.error(`[ProcessProvider] stdin unavailable, message lost`);
489
533
  if (sessionId) {
490
- this.emitWriteError(sessionId, "\u8FDB\u7A0B stdin \u5DF2\u5173\u95ED\uFF0C\u6D88\u606F\u672A\u9001\u8FBE");
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] \u4F1A\u8BDD ${sessionId} stdin \u5199\u5165\u5931\u8D25:`, err.message);
497
- this.emitWriteError(sessionId, `\u6D88\u606F\u53D1\u9001\u5931\u8D25: ${err.message}`);
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] \u4F1A\u8BDD ${sessionId}: stdout \u4E0D\u53EF\u7528`);
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] \u4F1A\u8BDD ${sessionId}: \u65E0\u6CD5\u89E3\u6790\u884C: ${trimmed.substring(0, 100)}`
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] \u4F1A\u8BDD ${sessionId} stderr: ${text}`);
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] \u4F1A\u8BDD ${sessionId}: \u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA code=${code} signal=${signal}`
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 ? "" : `\u8FDB\u7A0B\u9000\u51FA code=${code} signal=${signal}`,
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 = `\u4F60\u662F\u4E00\u4E2A AI \u7F16\u7A0B\u52A9\u624B\u3002\u6839\u636E\u4EE5\u4E0B Claude Code \u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u5EFA\u8BAE\u7528\u6237\u4E0B\u4E00\u6B65\u6700\u6709\u4EF7\u503C\u7684\u4E00\u6761\u6307\u4EE4\uFF08\u76F4\u63A5\u7ED9\u51FA\u6307\u4EE4\u5185\u5BB9\uFF0C\u4E0D\u8981\u89E3\u91CA\uFF0C\u4E0D\u8981\u52A0\u5F15\u53F7\uFF09\uFF1A
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 \u8FDB\u7A0B\u9000\u51FA\u7801: ${code}`));
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(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728`);
738
+ throw new Error(`Session ${sessionId} not found`);
695
739
  }
696
740
  if (!entry.process.stdin || entry.process.stdin.destroyed) {
697
- throw new Error(`\u4F1A\u8BDD ${sessionId} stdin \u4E0D\u53EF\u7528`);
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] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u56DE\u7B54 (toolUseId=${toolUseId})`);
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] \u4F1A\u8BDD\u5DF2\u521B\u5EFA: ${session.id} (\u9879\u76EE: ${projectPath})`);
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] \u6D88\u606F\u5DF2\u53D1\u9001\u5230\u4F1A\u8BDD: ${sessionId}`);
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] \u4F1A\u8BDD\u5DF2\u7EC8\u6B62: ${sessionId}`);
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] \u672A\u627E\u5230\u95EE\u9898\u8BF7\u6C42: ${requestId}`);
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] \u95EE\u9898\u5DF2\u56DE\u7B54: ${requestId}`);
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] \u5DF2\u9500\u6BC1");
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] \u4F1A\u8BDD ${sessionId} \u72B6\u6001\u53D8\u5316: ${lastStatus ?? "(\u65E0)"} \u2192 ${newStatus}`);
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] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u66F4\u65B0 (requestId=${existingRequestId})`);
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 \u5931\u8D25 (${sessionId}):`, err);
1187
+ console.error(`[SessionManager] answerQuestion failed (${sessionId}):`, err);
1144
1188
  }
1145
1189
  }).catch((err) => console.error("[SessionManager] answerPromise rejected:", err));
1146
- console.log(`[SessionManager] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u63A8\u9001 (requestId=${requestId})`);
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] \u4E8B\u4EF6\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u7A7A\u95F2\u8D85\u65F6\uFF0C\u505C\u6B62\u76D1\u542C: ${sessionId}`);
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 \u670D\u52A1\u5DF2\u5173\u95ED");
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] \u65B0\u5BA2\u6237\u7AEF\u8FDE\u63A5\uFF0C\u5F53\u524D\u8FDE\u63A5\u6570: ${this.getConnectionCount()}`);
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] \u8FDE\u63A5\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u6D88\u606F\u89E3\u6790\u5931\u8D25:", err);
1533
+ console.error("[WsBridge] Message parse error:", err);
1490
1534
  this.send(ws, {
1491
1535
  type: "error",
1492
- message: "\u6D88\u606F\u683C\u5F0F\u65E0\u6548",
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] \u5BA2\u6237\u7AEF\u65AD\u5F00\uFF0C\u5F53\u524D\u8FDE\u63A5\u6570: ${this.getConnectionCount()}`);
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] \u65AD\u5F00\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u8FDE\u63A5\u9519\u8BEF:", err.message);
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] \u4E8B\u4EF6\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u68C0\u6D4B\u5230\u6B7B\u8FDE\u63A5\uFF0C\u4E3B\u52A8\u65AD\u5F00");
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} \u5DE5\u5177\u8C03\u7528\u8BF7\u6C42`),
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: "\u670D\u52A1\u5668\u5904\u7406\u8BF7\u6C42\u5931\u8D25" });
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] \u5BA1\u6279\u8BF7\u6C42\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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 \u5B89\u88C5\u5B8C\u6210");
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 \u5DF2\u5378\u8F7D");
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 \u914D\u7F6E\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7");
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] \u901A\u77E5\u53D1\u9001\u5931\u8D25:", err);
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] \u53D1\u9001\u901A\u77E5\u5931\u8D25:", err.message);
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] \u5DF2\u521D\u59CB\u5316 (${config.sandbox ? "\u6C99\u7BB1" : "\u751F\u4EA7"}\u6A21\u5F0F)`);
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 \u8FDE\u63A5\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u8BF7\u6C42\u65F6\u91CD\u5EFA:", err.message);
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] \u5DF2\u6CE8\u518C token: session=${sessionId}`);
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] \u66F4\u65B0\u5931\u8D25 session=${sessionId}:`, err);
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] \u5E26\u63D0\u9192\u66F4\u65B0\u5931\u8D25 session=${sessionId}:`, err);
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] \u7ED3\u675F\u5931\u8D25 session=${sessionId}:`, err);
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 \u8FD4\u56DE ${statusCode}: ${responseData}`));
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] \u7AEF\u53E3 ${port} \u88AB\u5360\u7528\uFF0C\u5C1D\u8BD5\u91CA\u653E\u65E7\u8FDB\u7A0B...`);
3051
+ console.warn(`[Server] ${t("server.portInUse", { port })}`);
3008
3052
  await killPortProcess(port);
3009
- console.log(`[Server] \u91CD\u65B0\u542F\u52A8 ${label}...`);
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("[Server] ActivityKit Push \u5DF2\u542F\u7528");
3089
+ console.log(`[Server] ${t("server.activityPushEnabled")}`);
3046
3090
  } catch (err) {
3047
- console.warn("[Server] ActivityKit Push \u521D\u59CB\u5316\u5931\u8D25:", err);
3048
- console.log("[Server] \u7EE7\u7EED\u542F\u52A8\uFF08Live Activity \u540E\u53F0\u63A8\u9001\u4E0D\u53EF\u7528\uFF09");
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("[Server] Hook \u5B89\u88C5\u5931\u8D25:", err);
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(` WebSocket \u7AEF\u53E3: ${server.wsPort}`);
3479
- console.log(` HTTP \u5BA1\u6279\u7AEF\u53E3: ${server.httpPort}`);
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(` \u8FDE\u63A5 Token: (\u5DF2\u7981\u7528\uFF0C\u5F00\u53D1\u6A21\u5F0F)`);
3525
+ console.log(t("startup.tokenDisabled"));
3482
3526
  console.log();
3483
- console.log(` WebSocket \u5730\u5740: ws://${localIp}:${server.wsPort}`);
3527
+ console.log(t("startup.wsAddress", { ip: localIp, port: server.wsPort }));
3484
3528
  } else {
3485
- console.log(` \u8FDE\u63A5 Token: ${server.token}`);
3529
+ console.log(t("startup.token", { token: server.token }));
3486
3530
  console.log();
3487
- console.log(` WebSocket \u5730\u5740: ws://${localIp}:${server.wsPort}?token=${server.token}`);
3531
+ console.log(t("startup.wsAddressWithToken", { ip: localIp, port: server.wsPort, token: server.token }));
3488
3532
  }
3489
- console.log(` \u5065\u5EB7\u68C0\u67E5: http://localhost:${server.httpPort}/health`);
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(" [\u5F00\u53D1\u6A21\u5F0F] \u65E0\u9700 Token\uFF0C\u624B\u673A\u7AEF\u53EA\u9700\u8F93\u5165 IP:\u7AEF\u53E3 \u5373\u53EF\u8FDE\u63A5");
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(` \u{1F4A1} \u81EA\u52A8\u53D1\u73B0\u5DF2\u542F\u7528\uFF0C\u540C\u7F51\u6BB5\u624B\u673A\u53EF\u81EA\u52A8\u8FDE\u63A5`);
3504
- console.log(` \u5982\u5728\u516C\u5171\u7F51\u7EDC\uFF0C\u5EFA\u8BAE\u5173\u95ED: SESSIX_AUTO_CONNECT=false npx sessix-server`);
3547
+ console.log(t("startup.autoDiscoveryOn"));
3548
+ console.log(t("startup.autoDiscoveryHint"));
3505
3549
  } else {
3506
- console.log(` \u2139\uFE0F \u81EA\u52A8\u53D1\u73B0\u5DF2\u5173\u95ED\uFF0C\u624B\u673A\u9700\u624B\u52A8\u8F93\u5165\u5730\u5740\u8FDE\u63A5`);
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] \u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
3557
+ [Main] ${t("startup.receivedSignal", { signal })}`);
3514
3558
  try {
3515
3559
  await server.stop();
3516
- console.log("[Main] \u6240\u6709\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u518D\u89C1\uFF01");
3560
+ console.log(`[Main] ${t("startup.goodbye")}`);
3517
3561
  process.exit(0);
3518
3562
  } catch (err) {
3519
- console.error("[Main] \u5173\u95ED\u8FC7\u7A0B\u51FA\u9519:", err);
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("[Main] \u542F\u52A8\u5931\u8D25:", err);
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] \u4F1A\u8BDD ${sessionId} \u8FDB\u7A0B\u9519\u8BEF:`, err.message);
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: `\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`,
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(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728\u6216\u5DF2\u7ED3\u675F`);
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] \u4F1A\u8BDD ${sessionId}: \u6743\u9650\u6A21\u5F0F\u5207\u6362 ${entry.permissionMode ?? "default"} \u2192 ${permissionMode}\uFF0Crespawn`);
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] \u4F1A\u8BDD ${sessionId}: \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0Crespawn \u91CD\u65B0\u542F\u52A8`);
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] \u4F1A\u8BDD ${sessionId} sendMessage \u8FDB\u7A0B\u9519\u8BEF:`, err.message);
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: `\u6D88\u606F\u53D1\u9001\u5931\u8D25: ${err.message}`,
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 \u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u4E22\u5931`);
540
+ console.error(`[ProcessProvider] stdin unavailable, message lost`);
497
541
  if (sessionId) {
498
- this.emitWriteError(sessionId, "\u8FDB\u7A0B stdin \u5DF2\u5173\u95ED\uFF0C\u6D88\u606F\u672A\u9001\u8FBE");
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] \u4F1A\u8BDD ${sessionId} stdin \u5199\u5165\u5931\u8D25:`, err.message);
505
- this.emitWriteError(sessionId, `\u6D88\u606F\u53D1\u9001\u5931\u8D25: ${err.message}`);
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] \u4F1A\u8BDD ${sessionId}: stdout \u4E0D\u53EF\u7528`);
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] \u4F1A\u8BDD ${sessionId}: \u65E0\u6CD5\u89E3\u6790\u884C: ${trimmed.substring(0, 100)}`
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] \u4F1A\u8BDD ${sessionId} stderr: ${text}`);
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] \u4F1A\u8BDD ${sessionId}: \u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA code=${code} signal=${signal}`
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 ? "" : `\u8FDB\u7A0B\u9000\u51FA code=${code} signal=${signal}`,
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 = `\u4F60\u662F\u4E00\u4E2A AI \u7F16\u7A0B\u52A9\u624B\u3002\u6839\u636E\u4EE5\u4E0B Claude Code \u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u5EFA\u8BAE\u7528\u6237\u4E0B\u4E00\u6B65\u6700\u6709\u4EF7\u503C\u7684\u4E00\u6761\u6307\u4EE4\uFF08\u76F4\u63A5\u7ED9\u51FA\u6307\u4EE4\u5185\u5BB9\uFF0C\u4E0D\u8981\u89E3\u91CA\uFF0C\u4E0D\u8981\u52A0\u5F15\u53F7\uFF09\uFF1A
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 \u8FDB\u7A0B\u9000\u51FA\u7801: ${code}`));
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(`\u4F1A\u8BDD ${sessionId} \u4E0D\u5B58\u5728`);
746
+ throw new Error(`Session ${sessionId} not found`);
703
747
  }
704
748
  if (!entry.process.stdin || entry.process.stdin.destroyed) {
705
- throw new Error(`\u4F1A\u8BDD ${sessionId} stdin \u4E0D\u53EF\u7528`);
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] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u56DE\u7B54 (toolUseId=${toolUseId})`);
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] \u4F1A\u8BDD\u5DF2\u521B\u5EFA: ${session.id} (\u9879\u76EE: ${projectPath})`);
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] \u6D88\u606F\u5DF2\u53D1\u9001\u5230\u4F1A\u8BDD: ${sessionId}`);
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] \u4F1A\u8BDD\u5DF2\u7EC8\u6B62: ${sessionId}`);
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] \u672A\u627E\u5230\u95EE\u9898\u8BF7\u6C42: ${requestId}`);
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] \u95EE\u9898\u5DF2\u56DE\u7B54: ${requestId}`);
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] \u5DF2\u9500\u6BC1");
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] \u4F1A\u8BDD ${sessionId} \u72B6\u6001\u53D8\u5316: ${lastStatus ?? "(\u65E0)"} \u2192 ${newStatus}`);
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] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u66F4\u65B0 (requestId=${existingRequestId})`);
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 \u5931\u8D25 (${sessionId}):`, err);
1195
+ console.error(`[SessionManager] answerQuestion failed (${sessionId}):`, err);
1152
1196
  }
1153
1197
  }).catch((err) => console.error("[SessionManager] answerPromise rejected:", err));
1154
- console.log(`[SessionManager] \u4F1A\u8BDD ${sessionId}: AskUserQuestion \u5DF2\u63A8\u9001 (requestId=${requestId})`);
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] \u4E8B\u4EF6\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u7A7A\u95F2\u8D85\u65F6\uFF0C\u505C\u6B62\u76D1\u542C: ${sessionId}`);
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 \u670D\u52A1\u5DF2\u5173\u95ED");
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] \u65B0\u5BA2\u6237\u7AEF\u8FDE\u63A5\uFF0C\u5F53\u524D\u8FDE\u63A5\u6570: ${this.getConnectionCount()}`);
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] \u8FDE\u63A5\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u6D88\u606F\u89E3\u6790\u5931\u8D25:", err);
1541
+ console.error("[WsBridge] Message parse error:", err);
1498
1542
  this.send(ws, {
1499
1543
  type: "error",
1500
- message: "\u6D88\u606F\u683C\u5F0F\u65E0\u6548",
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] \u5BA2\u6237\u7AEF\u65AD\u5F00\uFF0C\u5F53\u524D\u8FDE\u63A5\u6570: ${this.getConnectionCount()}`);
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] \u65AD\u5F00\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u8FDE\u63A5\u9519\u8BEF:", err.message);
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] \u4E8B\u4EF6\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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] \u68C0\u6D4B\u5230\u6B7B\u8FDE\u63A5\uFF0C\u4E3B\u52A8\u65AD\u5F00");
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} \u5DE5\u5177\u8C03\u7528\u8BF7\u6C42`),
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: "\u670D\u52A1\u5668\u5904\u7406\u8BF7\u6C42\u5931\u8D25" });
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] \u5BA1\u6279\u8BF7\u6C42\u56DE\u8C03\u6267\u884C\u5F02\u5E38:", err);
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 \u5B89\u88C5\u5B8C\u6210");
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 \u5DF2\u5378\u8F7D");
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 \u914D\u7F6E\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7");
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] \u901A\u77E5\u53D1\u9001\u5931\u8D25:", err);
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] \u53D1\u9001\u901A\u77E5\u5931\u8D25:", err.message);
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] \u5DF2\u521D\u59CB\u5316 (${config.sandbox ? "\u6C99\u7BB1" : "\u751F\u4EA7"}\u6A21\u5F0F)`);
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 \u8FDE\u63A5\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u8BF7\u6C42\u65F6\u91CD\u5EFA:", err.message);
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] \u5DF2\u6CE8\u518C token: session=${sessionId}`);
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] \u66F4\u65B0\u5931\u8D25 session=${sessionId}:`, err);
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] \u5E26\u63D0\u9192\u66F4\u65B0\u5931\u8D25 session=${sessionId}:`, err);
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] \u7ED3\u675F\u5931\u8D25 session=${sessionId}:`, err);
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 \u8FD4\u56DE ${statusCode}: ${responseData}`));
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] \u7AEF\u53E3 ${port} \u88AB\u5360\u7528\uFF0C\u5C1D\u8BD5\u91CA\u653E\u65E7\u8FDB\u7A0B...`);
3059
+ console.warn(`[Server] ${t("server.portInUse", { port })}`);
3016
3060
  await killPortProcess(port);
3017
- console.log(`[Server] \u91CD\u65B0\u542F\u52A8 ${label}...`);
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("[Server] ActivityKit Push \u5DF2\u542F\u7528");
3097
+ console.log(`[Server] ${t("server.activityPushEnabled")}`);
3054
3098
  } catch (err) {
3055
- console.warn("[Server] ActivityKit Push \u521D\u59CB\u5316\u5931\u8D25:", err);
3056
- console.log("[Server] \u7EE7\u7EED\u542F\u52A8\uFF08Live Activity \u540E\u53F0\u63A8\u9001\u4E0D\u53EF\u7528\uFF09");
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("[Server] Hook \u5B89\u88C5\u5931\u8D25:", err);
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.2",
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",