sessix-server 0.2.7 → 0.2.9

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 (60) hide show
  1. package/dist/index.js +180 -84
  2. package/dist/server.js +180 -84
  3. package/package.json +10 -1
  4. package/dist/approval/ApprovalProxy.d.ts +0 -86
  5. package/dist/approval/ApprovalProxy.d.ts.map +0 -1
  6. package/dist/approval/ApprovalProxy.js +0 -363
  7. package/dist/approval/ApprovalProxy.js.map +0 -1
  8. package/dist/hooks/HookInstaller.d.ts +0 -55
  9. package/dist/hooks/HookInstaller.d.ts.map +0 -1
  10. package/dist/hooks/HookInstaller.js +0 -215
  11. package/dist/hooks/HookInstaller.js.map +0 -1
  12. package/dist/index.d.ts +0 -2
  13. package/dist/index.d.ts.map +0 -1
  14. package/dist/index.js.map +0 -1
  15. package/dist/mdns/MdnsService.d.ts +0 -36
  16. package/dist/mdns/MdnsService.d.ts.map +0 -1
  17. package/dist/mdns/MdnsService.js +0 -66
  18. package/dist/mdns/MdnsService.js.map +0 -1
  19. package/dist/notification/ActivityPushChannel.d.ts +0 -54
  20. package/dist/notification/ActivityPushChannel.d.ts.map +0 -1
  21. package/dist/notification/ActivityPushChannel.js +0 -235
  22. package/dist/notification/ActivityPushChannel.js.map +0 -1
  23. package/dist/notification/ExpoNotificationChannel.d.ts +0 -17
  24. package/dist/notification/ExpoNotificationChannel.d.ts.map +0 -1
  25. package/dist/notification/ExpoNotificationChannel.js +0 -57
  26. package/dist/notification/ExpoNotificationChannel.js.map +0 -1
  27. package/dist/notification/MacNotificationChannel.d.ts +0 -22
  28. package/dist/notification/MacNotificationChannel.d.ts.map +0 -1
  29. package/dist/notification/MacNotificationChannel.js +0 -33
  30. package/dist/notification/MacNotificationChannel.js.map +0 -1
  31. package/dist/notification/NotificationService.d.ts +0 -50
  32. package/dist/notification/NotificationService.d.ts.map +0 -1
  33. package/dist/notification/NotificationService.js +0 -177
  34. package/dist/notification/NotificationService.js.map +0 -1
  35. package/dist/providers/ExecutionProvider.d.ts +0 -60
  36. package/dist/providers/ExecutionProvider.d.ts.map +0 -1
  37. package/dist/providers/ExecutionProvider.js +0 -3
  38. package/dist/providers/ExecutionProvider.js.map +0 -1
  39. package/dist/providers/ProcessProvider.d.ts +0 -117
  40. package/dist/providers/ProcessProvider.d.ts.map +0 -1
  41. package/dist/providers/ProcessProvider.js +0 -507
  42. package/dist/providers/ProcessProvider.js.map +0 -1
  43. package/dist/server.d.ts.map +0 -1
  44. package/dist/server.js.map +0 -1
  45. package/dist/session/ProjectReader.d.ts +0 -44
  46. package/dist/session/ProjectReader.d.ts.map +0 -1
  47. package/dist/session/ProjectReader.js +0 -471
  48. package/dist/session/ProjectReader.js.map +0 -1
  49. package/dist/session/SessionFileWatcher.d.ts +0 -35
  50. package/dist/session/SessionFileWatcher.d.ts.map +0 -1
  51. package/dist/session/SessionFileWatcher.js +0 -207
  52. package/dist/session/SessionFileWatcher.js.map +0 -1
  53. package/dist/session/SessionManager.d.ts +0 -114
  54. package/dist/session/SessionManager.d.ts.map +0 -1
  55. package/dist/session/SessionManager.js +0 -356
  56. package/dist/session/SessionManager.js.map +0 -1
  57. package/dist/ws/WsBridge.d.ts +0 -55
  58. package/dist/ws/WsBridge.d.ts.map +0 -1
  59. package/dist/ws/WsBridge.js +0 -220
  60. package/dist/ws/WsBridge.js.map +0 -1
package/dist/index.js CHANGED
@@ -299,7 +299,7 @@ var import_uuid5 = require("uuid");
299
299
  var import_promises4 = require("fs/promises");
300
300
  var import_node_os6 = require("os");
301
301
  var import_node_path5 = require("path");
302
- var import_node_child_process5 = require("child_process");
302
+ var import_node_child_process6 = require("child_process");
303
303
  var import_node_util = require("util");
304
304
 
305
305
  // src/providers/ProcessProvider.ts
@@ -2059,79 +2059,120 @@ var ApprovalProxy = class _ApprovalProxy {
2059
2059
  };
2060
2060
 
2061
2061
  // src/mdns/MdnsService.ts
2062
- var import_bonjour_service = __toESM(require("bonjour-service"));
2062
+ var import_node_child_process3 = require("child_process");
2063
2063
  var import_node_os4 = require("os");
2064
- function getLanAddresses() {
2065
- const results = [];
2066
- for (const [name, addrs] of Object.entries((0, import_node_os4.networkInterfaces)())) {
2067
- if (name.startsWith("utun") || name === "lo") continue;
2068
- if (isWindows && (name.startsWith("vEthernet") || name.includes("Loopback"))) continue;
2069
- for (const addr of addrs ?? []) {
2070
- if (addr.family === "IPv4" && !addr.internal) {
2071
- results.push(addr.address);
2072
- }
2073
- }
2074
- }
2075
- return results;
2064
+ function buildTxtArgs(txt) {
2065
+ return Object.entries(txt).map(([k, v]) => `${k}=${v}`);
2076
2066
  }
2077
2067
  var MdnsService = class {
2078
- bonjour = null;
2079
- service = null;
2068
+ proc = null;
2069
+ bonjourInstance = null;
2070
+ bonjourService = null;
2080
2071
  wsPort;
2081
2072
  httpPort;
2082
2073
  version;
2083
2074
  pairing;
2075
+ useDnsSd;
2084
2076
  constructor(options) {
2085
2077
  this.wsPort = options.wsPort;
2086
2078
  this.httpPort = options.httpPort;
2087
2079
  this.version = options.version ?? "0.1.0";
2088
2080
  this.pairing = options.pairing ?? "closed";
2081
+ this.useDnsSd = (0, import_node_os4.platform)() === "darwin";
2082
+ }
2083
+ getTxt() {
2084
+ return {
2085
+ version: this.version,
2086
+ httpPort: String(this.httpPort),
2087
+ wsPort: String(this.wsPort),
2088
+ pairing: this.pairing
2089
+ };
2089
2090
  }
2090
2091
  /**
2091
2092
  * 启动 mDNS 广播
2092
2093
  */
2093
2094
  start() {
2094
- if (this.bonjour) {
2095
+ if (this.useDnsSd) {
2096
+ this.startDnsSd();
2097
+ } else {
2098
+ this.startBonjour();
2099
+ }
2100
+ }
2101
+ startDnsSd() {
2102
+ if (this.proc) {
2095
2103
  console.warn(`[MdnsService] ${t("mdns.alreadyRunning")}`);
2096
2104
  return;
2097
2105
  }
2098
- const lanAddrs = getLanAddresses();
2099
- const onMdnsError = (err) => {
2100
- console.warn(`[MdnsService] mDNS error (non-fatal): ${err.message}`);
2101
- this.stop();
2102
- };
2103
- const opts = lanAddrs.length > 0 ? { interface: lanAddrs[0] } : {};
2104
- this.bonjour = new import_bonjour_service.default(opts, onMdnsError);
2105
- this.bonjour.server?.mdns?.on("error", onMdnsError);
2106
- if (lanAddrs.length > 0) {
2107
- console.log(`[MdnsService] ${t("mdns.boundInterface", { ip: lanAddrs[0] })}`);
2108
- }
2109
- this.service = this.bonjour.publish({
2110
- name: "Sessix",
2111
- type: "sessix",
2112
- port: this.wsPort,
2113
- txt: {
2114
- version: this.version,
2115
- httpPort: String(this.httpPort),
2116
- wsPort: String(this.wsPort),
2117
- pairing: this.pairing
2106
+ const args = [
2107
+ "-R",
2108
+ "Sessix",
2109
+ "_sessix._tcp",
2110
+ "local",
2111
+ String(this.wsPort),
2112
+ ...buildTxtArgs(this.getTxt())
2113
+ ];
2114
+ this.proc = (0, import_node_child_process3.spawn)("dns-sd", args, { stdio: "ignore" });
2115
+ this.proc.on("error", (err) => {
2116
+ console.warn(`[MdnsService] dns-sd failed, falling back to bonjour-service: ${err.message}`);
2117
+ this.proc = null;
2118
+ this.useDnsSd = false;
2119
+ this.startBonjour();
2120
+ });
2121
+ this.proc.on("exit", (code) => {
2122
+ if (code !== null && code !== 0) {
2123
+ console.warn(`[MdnsService] dns-sd exited with code ${code}`);
2118
2124
  }
2125
+ this.proc = null;
2119
2126
  });
2120
- console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })}`);
2127
+ console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })} (dns-sd)`);
2128
+ }
2129
+ async startBonjour() {
2130
+ if (this.bonjourInstance) {
2131
+ console.warn(`[MdnsService] ${t("mdns.alreadyRunning")}`);
2132
+ return;
2133
+ }
2134
+ try {
2135
+ const { default: Bonjour } = await import("bonjour-service");
2136
+ const { networkInterfaces: networkInterfaces2 } = await import("os");
2137
+ const lanAddrs = getLanAddresses(networkInterfaces2);
2138
+ const opts = lanAddrs.length > 0 ? { interface: lanAddrs[0] } : {};
2139
+ const onError = (err) => {
2140
+ if (err.code === "EADDRINUSE") return;
2141
+ console.warn(`[MdnsService] mDNS error (non-fatal): ${err.message}`);
2142
+ };
2143
+ this.bonjourInstance = new Bonjour(opts, onError);
2144
+ this.bonjourInstance.server?.mdns?.on("error", onError);
2145
+ if (lanAddrs.length > 0) {
2146
+ console.log(`[MdnsService] ${t("mdns.boundInterface", { ip: lanAddrs[0] })}`);
2147
+ }
2148
+ this.bonjourService = this.bonjourInstance.publish({
2149
+ name: "Sessix",
2150
+ type: "sessix",
2151
+ port: this.wsPort,
2152
+ txt: this.getTxt()
2153
+ });
2154
+ console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })} (bonjour-service)`);
2155
+ } catch (err) {
2156
+ console.warn(`[MdnsService] bonjour-service failed: ${err.message}`);
2157
+ }
2121
2158
  }
2122
2159
  /**
2123
2160
  * 停止 mDNS 广播
2124
2161
  */
2125
2162
  stop() {
2126
- if (this.service) {
2127
- this.service.stop?.(() => {
2163
+ if (this.proc) {
2164
+ this.proc.kill();
2165
+ this.proc = null;
2166
+ }
2167
+ if (this.bonjourService) {
2168
+ this.bonjourService.stop?.(() => {
2128
2169
  console.log(`[MdnsService] ${t("mdns.stopped")}`);
2129
2170
  });
2130
- this.service = null;
2171
+ this.bonjourService = null;
2131
2172
  }
2132
- if (this.bonjour) {
2133
- this.bonjour.destroy();
2134
- this.bonjour = null;
2173
+ if (this.bonjourInstance) {
2174
+ this.bonjourInstance.destroy();
2175
+ this.bonjourInstance = null;
2135
2176
  }
2136
2177
  console.log(`[MdnsService] ${t("mdns.closed")}`);
2137
2178
  }
@@ -2140,30 +2181,45 @@ var MdnsService = class {
2140
2181
  */
2141
2182
  updatePairingState(state) {
2142
2183
  this.pairing = state;
2143
- if (!this.bonjour) return;
2184
+ if (this.useDnsSd) {
2185
+ if (this.proc) {
2186
+ this.proc.kill();
2187
+ this.proc = null;
2188
+ }
2189
+ this.startDnsSd();
2190
+ return;
2191
+ }
2192
+ if (!this.bonjourInstance) return;
2144
2193
  const republish = () => {
2145
- if (!this.bonjour) return;
2146
- this.service = this.bonjour.publish({
2194
+ if (!this.bonjourInstance) return;
2195
+ this.bonjourService = this.bonjourInstance.publish({
2147
2196
  name: "Sessix",
2148
2197
  type: "sessix",
2149
2198
  port: this.wsPort,
2150
- txt: {
2151
- version: this.version,
2152
- httpPort: String(this.httpPort),
2153
- wsPort: String(this.wsPort),
2154
- pairing: state
2155
- }
2199
+ txt: this.getTxt()
2156
2200
  });
2157
2201
  };
2158
- if (this.service) {
2159
- const old = this.service;
2160
- this.service = null;
2202
+ if (this.bonjourService) {
2203
+ const old = this.bonjourService;
2204
+ this.bonjourService = null;
2161
2205
  old.stop?.(() => republish());
2162
2206
  } else {
2163
2207
  republish();
2164
2208
  }
2165
2209
  }
2166
2210
  };
2211
+ function getLanAddresses(networkInterfacesFn) {
2212
+ const results = [];
2213
+ for (const [name, addrs] of Object.entries(networkInterfacesFn())) {
2214
+ if (name.startsWith("utun") || name === "lo") continue;
2215
+ for (const addr of addrs ?? []) {
2216
+ if (addr.family === "IPv4" && !addr.internal) {
2217
+ results.push(addr.address);
2218
+ }
2219
+ }
2220
+ }
2221
+ return results;
2222
+ }
2167
2223
 
2168
2224
  // src/hooks/HookInstaller.ts
2169
2225
  var import_promises2 = require("fs/promises");
@@ -2637,7 +2693,7 @@ var NotificationService = class {
2637
2693
  };
2638
2694
 
2639
2695
  // src/notification/DesktopNotificationChannel.ts
2640
- var import_node_child_process3 = require("child_process");
2696
+ var import_node_child_process4 = require("child_process");
2641
2697
  var DesktopNotificationChannel = class {
2642
2698
  isAvailable() {
2643
2699
  return process.platform === "darwin";
@@ -2649,7 +2705,7 @@ var DesktopNotificationChannel = class {
2649
2705
  const sound = payload.sound ?? "Ping";
2650
2706
  const script = `display notification "${body}" with title "${title}" sound name "${sound}"`;
2651
2707
  return new Promise((resolve) => {
2652
- (0, import_node_child_process3.execFile)("osascript", ["-e", script], (err) => {
2708
+ (0, import_node_child_process4.execFile)("osascript", ["-e", script], (err) => {
2653
2709
  if (err) {
2654
2710
  console.warn("[DesktopNotificationChannel] Send notification failed:", err.message);
2655
2711
  }
@@ -2975,16 +3031,23 @@ async function getHistoricalSessions(projectPath) {
2975
3031
  const entries = await (0, import_promises3.readdir)(projectDir, { withFileTypes: true });
2976
3032
  const jsonlFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
2977
3033
  const mtimeMap = /* @__PURE__ */ new Map();
2978
- for (const entry of jsonlFiles) {
2979
- const sessionId = entry.name.slice(0, -6);
2980
- const filePath = (0, import_path.join)(projectDir, entry.name);
2981
- try {
2982
- const fileStat = await (0, import_promises3.stat)(filePath);
2983
- mtimeMap.set(sessionId, fileStat.mtimeMs);
2984
- } catch {
2985
- mtimeMap.set(sessionId, 0);
2986
- }
2987
- }
3034
+ await Promise.all(
3035
+ jsonlFiles.map(async (entry) => {
3036
+ const sessionId = entry.name.slice(0, -6);
3037
+ const filePath = (0, import_path.join)(projectDir, entry.name);
3038
+ try {
3039
+ const contentTs = await extractLastTimestamp(filePath);
3040
+ if (contentTs) {
3041
+ mtimeMap.set(sessionId, contentTs);
3042
+ } else {
3043
+ const fileStat = await (0, import_promises3.stat)(filePath);
3044
+ mtimeMap.set(sessionId, fileStat.mtimeMs);
3045
+ }
3046
+ } catch {
3047
+ mtimeMap.set(sessionId, 0);
3048
+ }
3049
+ })
3050
+ );
2988
3051
  const uuidDirs = entries.filter(
2989
3052
  (e) => e.isDirectory() && UUID_RE.test(e.name) && !mtimeMap.has(e.name)
2990
3053
  );
@@ -3135,6 +3198,32 @@ async function getSessionHistory(projectPath, sessionId) {
3135
3198
  };
3136
3199
  }
3137
3200
  }
3201
+ async function extractLastTimestamp(filePath) {
3202
+ let fileHandle;
3203
+ try {
3204
+ fileHandle = await (0, import_promises3.open)(filePath, "r");
3205
+ const fileStat = await fileHandle.stat();
3206
+ const readSize = Math.min(fileStat.size, 8192);
3207
+ const buffer = Buffer.alloc(readSize);
3208
+ await fileHandle.read(buffer, 0, readSize, fileStat.size - readSize);
3209
+ const tail = buffer.toString("utf-8");
3210
+ const lines = tail.split("\n").filter((l) => l.trim());
3211
+ for (let i = lines.length - 1; i >= 0; i--) {
3212
+ try {
3213
+ const obj = JSON.parse(lines[i]);
3214
+ if (obj.timestamp) {
3215
+ const ts = new Date(obj.timestamp).getTime();
3216
+ if (!isNaN(ts)) return ts;
3217
+ }
3218
+ } catch {
3219
+ }
3220
+ }
3221
+ } catch {
3222
+ } finally {
3223
+ await fileHandle?.close();
3224
+ }
3225
+ return void 0;
3226
+ }
3138
3227
  async function extractFirstPrompt(filePath) {
3139
3228
  let fileHandle;
3140
3229
  try {
@@ -3203,17 +3292,24 @@ async function countJsonlFilesWithMtime(dirPath) {
3203
3292
  (e) => e.isDirectory() && UUID_RE.test(e.name) && !jsonlNames.has(e.name)
3204
3293
  );
3205
3294
  let latestMtime = 0;
3206
- const allEntries = [
3207
- ...entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")),
3208
- ...uuidDirs
3209
- ];
3210
- for (const entry of allEntries) {
3211
- try {
3212
- const fileStat = await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name));
3213
- if (fileStat.mtimeMs > latestMtime) latestMtime = fileStat.mtimeMs;
3214
- } catch {
3215
- }
3216
- }
3295
+ const jsonlEntries = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
3296
+ await Promise.all([
3297
+ ...jsonlEntries.map(async (entry) => {
3298
+ try {
3299
+ const contentTs = await extractLastTimestamp((0, import_path.join)(dirPath, entry.name));
3300
+ const ts = contentTs ?? (await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name))).mtimeMs;
3301
+ if (ts > latestMtime) latestMtime = ts;
3302
+ } catch {
3303
+ }
3304
+ }),
3305
+ ...uuidDirs.map(async (entry) => {
3306
+ try {
3307
+ const fileStat = await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name));
3308
+ if (fileStat.mtimeMs > latestMtime) latestMtime = fileStat.mtimeMs;
3309
+ } catch {
3310
+ }
3311
+ })
3312
+ ]);
3217
3313
  return { count: jsonlNames.size + uuidDirs.length, latestMtime };
3218
3314
  } catch {
3219
3315
  return { count: 0, latestMtime: 0 };
@@ -3401,7 +3497,7 @@ var AuthManager = class extends import_events2.EventEmitter {
3401
3497
  var import_promises5 = require("fs/promises");
3402
3498
 
3403
3499
  // src/terminal/TerminalExecutor.ts
3404
- var import_node_child_process4 = require("child_process");
3500
+ var import_node_child_process5 = require("child_process");
3405
3501
  var import_uuid4 = require("uuid");
3406
3502
  var EXEC_TIMEOUT_MS = 5 * 60 * 1e3;
3407
3503
  var TerminalExecutor = class {
@@ -3427,7 +3523,7 @@ var TerminalExecutor = class {
3427
3523
  const execId = (0, import_uuid4.v4)();
3428
3524
  const shell = isWindows ? "powershell" : "bash";
3429
3525
  const args = isWindows ? ["-Command", command] : ["-c", command];
3430
- const proc = (0, import_node_child_process4.spawn)(shell, args, {
3526
+ const proc = (0, import_node_child_process5.spawn)(shell, args, {
3431
3527
  cwd,
3432
3528
  stdio: ["ignore", "pipe", "pipe"],
3433
3529
  env: { ...process.env }
@@ -3490,7 +3586,7 @@ var TerminalExecutor = class {
3490
3586
  // src/server.ts
3491
3587
  var WS_PORT = 3745;
3492
3588
  var HTTP_PORT = 3746;
3493
- var execAsync = (0, import_node_util.promisify)(import_node_child_process5.exec);
3589
+ var execAsync = (0, import_node_util.promisify)(import_node_child_process6.exec);
3494
3590
  async function killPortProcess(port) {
3495
3591
  try {
3496
3592
  if (isWindows) {
@@ -3847,9 +3943,9 @@ async function start(opts = {}) {
3847
3943
  }
3848
3944
  case "terminal_exec": {
3849
3945
  const activeSession = sessionManager.getActiveSessions().find((s) => s.id === event.sessionId);
3850
- const cwd = activeSession?.projectPath ?? sessionManager.getSessionProjectPath(event.sessionId);
3946
+ const cwd = activeSession?.projectPath ?? sessionManager.getSessionProjectPath(event.sessionId) ?? event.projectPath;
3851
3947
  if (!cwd) {
3852
- wsBridge.send(ws, { type: "error", code: "TERMINAL_EXEC_ERROR", message: "Session not found or no project path", sessionId: event.sessionId });
3948
+ wsBridge.send(ws, { type: "error", code: "TERMINAL_EXEC_ERROR", message: `Session not found (id: ${event.sessionId.slice(0, 8)}\u2026)`, sessionId: event.sessionId });
3853
3949
  break;
3854
3950
  }
3855
3951
  terminalExecutor.exec(event.sessionId, event.command, cwd);