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/server.js CHANGED
@@ -305,7 +305,7 @@ var import_uuid5 = require("uuid");
305
305
  var import_promises4 = require("fs/promises");
306
306
  var import_node_os6 = require("os");
307
307
  var import_node_path5 = require("path");
308
- var import_node_child_process5 = require("child_process");
308
+ var import_node_child_process6 = require("child_process");
309
309
  var import_node_util = require("util");
310
310
 
311
311
  // src/providers/ProcessProvider.ts
@@ -2065,79 +2065,120 @@ var ApprovalProxy = class _ApprovalProxy {
2065
2065
  };
2066
2066
 
2067
2067
  // src/mdns/MdnsService.ts
2068
- var import_bonjour_service = __toESM(require("bonjour-service"));
2068
+ var import_node_child_process3 = require("child_process");
2069
2069
  var import_node_os4 = require("os");
2070
- function getLanAddresses() {
2071
- const results = [];
2072
- for (const [name, addrs] of Object.entries((0, import_node_os4.networkInterfaces)())) {
2073
- if (name.startsWith("utun") || name === "lo") continue;
2074
- if (isWindows && (name.startsWith("vEthernet") || name.includes("Loopback"))) continue;
2075
- for (const addr of addrs ?? []) {
2076
- if (addr.family === "IPv4" && !addr.internal) {
2077
- results.push(addr.address);
2078
- }
2079
- }
2080
- }
2081
- return results;
2070
+ function buildTxtArgs(txt) {
2071
+ return Object.entries(txt).map(([k, v]) => `${k}=${v}`);
2082
2072
  }
2083
2073
  var MdnsService = class {
2084
- bonjour = null;
2085
- service = null;
2074
+ proc = null;
2075
+ bonjourInstance = null;
2076
+ bonjourService = null;
2086
2077
  wsPort;
2087
2078
  httpPort;
2088
2079
  version;
2089
2080
  pairing;
2081
+ useDnsSd;
2090
2082
  constructor(options) {
2091
2083
  this.wsPort = options.wsPort;
2092
2084
  this.httpPort = options.httpPort;
2093
2085
  this.version = options.version ?? "0.1.0";
2094
2086
  this.pairing = options.pairing ?? "closed";
2087
+ this.useDnsSd = (0, import_node_os4.platform)() === "darwin";
2088
+ }
2089
+ getTxt() {
2090
+ return {
2091
+ version: this.version,
2092
+ httpPort: String(this.httpPort),
2093
+ wsPort: String(this.wsPort),
2094
+ pairing: this.pairing
2095
+ };
2095
2096
  }
2096
2097
  /**
2097
2098
  * 启动 mDNS 广播
2098
2099
  */
2099
2100
  start() {
2100
- if (this.bonjour) {
2101
+ if (this.useDnsSd) {
2102
+ this.startDnsSd();
2103
+ } else {
2104
+ this.startBonjour();
2105
+ }
2106
+ }
2107
+ startDnsSd() {
2108
+ if (this.proc) {
2101
2109
  console.warn(`[MdnsService] ${t("mdns.alreadyRunning")}`);
2102
2110
  return;
2103
2111
  }
2104
- const lanAddrs = getLanAddresses();
2105
- const onMdnsError = (err) => {
2106
- console.warn(`[MdnsService] mDNS error (non-fatal): ${err.message}`);
2107
- this.stop();
2108
- };
2109
- const opts = lanAddrs.length > 0 ? { interface: lanAddrs[0] } : {};
2110
- this.bonjour = new import_bonjour_service.default(opts, onMdnsError);
2111
- this.bonjour.server?.mdns?.on("error", onMdnsError);
2112
- if (lanAddrs.length > 0) {
2113
- console.log(`[MdnsService] ${t("mdns.boundInterface", { ip: lanAddrs[0] })}`);
2114
- }
2115
- this.service = this.bonjour.publish({
2116
- name: "Sessix",
2117
- type: "sessix",
2118
- port: this.wsPort,
2119
- txt: {
2120
- version: this.version,
2121
- httpPort: String(this.httpPort),
2122
- wsPort: String(this.wsPort),
2123
- pairing: this.pairing
2112
+ const args = [
2113
+ "-R",
2114
+ "Sessix",
2115
+ "_sessix._tcp",
2116
+ "local",
2117
+ String(this.wsPort),
2118
+ ...buildTxtArgs(this.getTxt())
2119
+ ];
2120
+ this.proc = (0, import_node_child_process3.spawn)("dns-sd", args, { stdio: "ignore" });
2121
+ this.proc.on("error", (err) => {
2122
+ console.warn(`[MdnsService] dns-sd failed, falling back to bonjour-service: ${err.message}`);
2123
+ this.proc = null;
2124
+ this.useDnsSd = false;
2125
+ this.startBonjour();
2126
+ });
2127
+ this.proc.on("exit", (code) => {
2128
+ if (code !== null && code !== 0) {
2129
+ console.warn(`[MdnsService] dns-sd exited with code ${code}`);
2124
2130
  }
2131
+ this.proc = null;
2125
2132
  });
2126
- console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })}`);
2133
+ console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })} (dns-sd)`);
2134
+ }
2135
+ async startBonjour() {
2136
+ if (this.bonjourInstance) {
2137
+ console.warn(`[MdnsService] ${t("mdns.alreadyRunning")}`);
2138
+ return;
2139
+ }
2140
+ try {
2141
+ const { default: Bonjour } = await import("bonjour-service");
2142
+ const { networkInterfaces } = await import("os");
2143
+ const lanAddrs = getLanAddresses(networkInterfaces);
2144
+ const opts = lanAddrs.length > 0 ? { interface: lanAddrs[0] } : {};
2145
+ const onError = (err) => {
2146
+ if (err.code === "EADDRINUSE") return;
2147
+ console.warn(`[MdnsService] mDNS error (non-fatal): ${err.message}`);
2148
+ };
2149
+ this.bonjourInstance = new Bonjour(opts, onError);
2150
+ this.bonjourInstance.server?.mdns?.on("error", onError);
2151
+ if (lanAddrs.length > 0) {
2152
+ console.log(`[MdnsService] ${t("mdns.boundInterface", { ip: lanAddrs[0] })}`);
2153
+ }
2154
+ this.bonjourService = this.bonjourInstance.publish({
2155
+ name: "Sessix",
2156
+ type: "sessix",
2157
+ port: this.wsPort,
2158
+ txt: this.getTxt()
2159
+ });
2160
+ console.log(`[MdnsService] ${t("mdns.started", { port: this.wsPort })} (bonjour-service)`);
2161
+ } catch (err) {
2162
+ console.warn(`[MdnsService] bonjour-service failed: ${err.message}`);
2163
+ }
2127
2164
  }
2128
2165
  /**
2129
2166
  * 停止 mDNS 广播
2130
2167
  */
2131
2168
  stop() {
2132
- if (this.service) {
2133
- this.service.stop?.(() => {
2169
+ if (this.proc) {
2170
+ this.proc.kill();
2171
+ this.proc = null;
2172
+ }
2173
+ if (this.bonjourService) {
2174
+ this.bonjourService.stop?.(() => {
2134
2175
  console.log(`[MdnsService] ${t("mdns.stopped")}`);
2135
2176
  });
2136
- this.service = null;
2177
+ this.bonjourService = null;
2137
2178
  }
2138
- if (this.bonjour) {
2139
- this.bonjour.destroy();
2140
- this.bonjour = null;
2179
+ if (this.bonjourInstance) {
2180
+ this.bonjourInstance.destroy();
2181
+ this.bonjourInstance = null;
2141
2182
  }
2142
2183
  console.log(`[MdnsService] ${t("mdns.closed")}`);
2143
2184
  }
@@ -2146,30 +2187,45 @@ var MdnsService = class {
2146
2187
  */
2147
2188
  updatePairingState(state) {
2148
2189
  this.pairing = state;
2149
- if (!this.bonjour) return;
2190
+ if (this.useDnsSd) {
2191
+ if (this.proc) {
2192
+ this.proc.kill();
2193
+ this.proc = null;
2194
+ }
2195
+ this.startDnsSd();
2196
+ return;
2197
+ }
2198
+ if (!this.bonjourInstance) return;
2150
2199
  const republish = () => {
2151
- if (!this.bonjour) return;
2152
- this.service = this.bonjour.publish({
2200
+ if (!this.bonjourInstance) return;
2201
+ this.bonjourService = this.bonjourInstance.publish({
2153
2202
  name: "Sessix",
2154
2203
  type: "sessix",
2155
2204
  port: this.wsPort,
2156
- txt: {
2157
- version: this.version,
2158
- httpPort: String(this.httpPort),
2159
- wsPort: String(this.wsPort),
2160
- pairing: state
2161
- }
2205
+ txt: this.getTxt()
2162
2206
  });
2163
2207
  };
2164
- if (this.service) {
2165
- const old = this.service;
2166
- this.service = null;
2208
+ if (this.bonjourService) {
2209
+ const old = this.bonjourService;
2210
+ this.bonjourService = null;
2167
2211
  old.stop?.(() => republish());
2168
2212
  } else {
2169
2213
  republish();
2170
2214
  }
2171
2215
  }
2172
2216
  };
2217
+ function getLanAddresses(networkInterfacesFn) {
2218
+ const results = [];
2219
+ for (const [name, addrs] of Object.entries(networkInterfacesFn())) {
2220
+ if (name.startsWith("utun") || name === "lo") continue;
2221
+ for (const addr of addrs ?? []) {
2222
+ if (addr.family === "IPv4" && !addr.internal) {
2223
+ results.push(addr.address);
2224
+ }
2225
+ }
2226
+ }
2227
+ return results;
2228
+ }
2173
2229
 
2174
2230
  // src/hooks/HookInstaller.ts
2175
2231
  var import_promises2 = require("fs/promises");
@@ -2643,7 +2699,7 @@ var NotificationService = class {
2643
2699
  };
2644
2700
 
2645
2701
  // src/notification/DesktopNotificationChannel.ts
2646
- var import_node_child_process3 = require("child_process");
2702
+ var import_node_child_process4 = require("child_process");
2647
2703
  var DesktopNotificationChannel = class {
2648
2704
  isAvailable() {
2649
2705
  return process.platform === "darwin";
@@ -2655,7 +2711,7 @@ var DesktopNotificationChannel = class {
2655
2711
  const sound = payload.sound ?? "Ping";
2656
2712
  const script = `display notification "${body}" with title "${title}" sound name "${sound}"`;
2657
2713
  return new Promise((resolve) => {
2658
- (0, import_node_child_process3.execFile)("osascript", ["-e", script], (err) => {
2714
+ (0, import_node_child_process4.execFile)("osascript", ["-e", script], (err) => {
2659
2715
  if (err) {
2660
2716
  console.warn("[DesktopNotificationChannel] Send notification failed:", err.message);
2661
2717
  }
@@ -2981,16 +3037,23 @@ async function getHistoricalSessions(projectPath) {
2981
3037
  const entries = await (0, import_promises3.readdir)(projectDir, { withFileTypes: true });
2982
3038
  const jsonlFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
2983
3039
  const mtimeMap = /* @__PURE__ */ new Map();
2984
- for (const entry of jsonlFiles) {
2985
- const sessionId = entry.name.slice(0, -6);
2986
- const filePath = (0, import_path.join)(projectDir, entry.name);
2987
- try {
2988
- const fileStat = await (0, import_promises3.stat)(filePath);
2989
- mtimeMap.set(sessionId, fileStat.mtimeMs);
2990
- } catch {
2991
- mtimeMap.set(sessionId, 0);
2992
- }
2993
- }
3040
+ await Promise.all(
3041
+ jsonlFiles.map(async (entry) => {
3042
+ const sessionId = entry.name.slice(0, -6);
3043
+ const filePath = (0, import_path.join)(projectDir, entry.name);
3044
+ try {
3045
+ const contentTs = await extractLastTimestamp(filePath);
3046
+ if (contentTs) {
3047
+ mtimeMap.set(sessionId, contentTs);
3048
+ } else {
3049
+ const fileStat = await (0, import_promises3.stat)(filePath);
3050
+ mtimeMap.set(sessionId, fileStat.mtimeMs);
3051
+ }
3052
+ } catch {
3053
+ mtimeMap.set(sessionId, 0);
3054
+ }
3055
+ })
3056
+ );
2994
3057
  const uuidDirs = entries.filter(
2995
3058
  (e) => e.isDirectory() && UUID_RE.test(e.name) && !mtimeMap.has(e.name)
2996
3059
  );
@@ -3141,6 +3204,32 @@ async function getSessionHistory(projectPath, sessionId) {
3141
3204
  };
3142
3205
  }
3143
3206
  }
3207
+ async function extractLastTimestamp(filePath) {
3208
+ let fileHandle;
3209
+ try {
3210
+ fileHandle = await (0, import_promises3.open)(filePath, "r");
3211
+ const fileStat = await fileHandle.stat();
3212
+ const readSize = Math.min(fileStat.size, 8192);
3213
+ const buffer = Buffer.alloc(readSize);
3214
+ await fileHandle.read(buffer, 0, readSize, fileStat.size - readSize);
3215
+ const tail = buffer.toString("utf-8");
3216
+ const lines = tail.split("\n").filter((l) => l.trim());
3217
+ for (let i = lines.length - 1; i >= 0; i--) {
3218
+ try {
3219
+ const obj = JSON.parse(lines[i]);
3220
+ if (obj.timestamp) {
3221
+ const ts = new Date(obj.timestamp).getTime();
3222
+ if (!isNaN(ts)) return ts;
3223
+ }
3224
+ } catch {
3225
+ }
3226
+ }
3227
+ } catch {
3228
+ } finally {
3229
+ await fileHandle?.close();
3230
+ }
3231
+ return void 0;
3232
+ }
3144
3233
  async function extractFirstPrompt(filePath) {
3145
3234
  let fileHandle;
3146
3235
  try {
@@ -3209,17 +3298,24 @@ async function countJsonlFilesWithMtime(dirPath) {
3209
3298
  (e) => e.isDirectory() && UUID_RE.test(e.name) && !jsonlNames.has(e.name)
3210
3299
  );
3211
3300
  let latestMtime = 0;
3212
- const allEntries = [
3213
- ...entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")),
3214
- ...uuidDirs
3215
- ];
3216
- for (const entry of allEntries) {
3217
- try {
3218
- const fileStat = await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name));
3219
- if (fileStat.mtimeMs > latestMtime) latestMtime = fileStat.mtimeMs;
3220
- } catch {
3221
- }
3222
- }
3301
+ const jsonlEntries = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
3302
+ await Promise.all([
3303
+ ...jsonlEntries.map(async (entry) => {
3304
+ try {
3305
+ const contentTs = await extractLastTimestamp((0, import_path.join)(dirPath, entry.name));
3306
+ const ts = contentTs ?? (await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name))).mtimeMs;
3307
+ if (ts > latestMtime) latestMtime = ts;
3308
+ } catch {
3309
+ }
3310
+ }),
3311
+ ...uuidDirs.map(async (entry) => {
3312
+ try {
3313
+ const fileStat = await (0, import_promises3.stat)((0, import_path.join)(dirPath, entry.name));
3314
+ if (fileStat.mtimeMs > latestMtime) latestMtime = fileStat.mtimeMs;
3315
+ } catch {
3316
+ }
3317
+ })
3318
+ ]);
3223
3319
  return { count: jsonlNames.size + uuidDirs.length, latestMtime };
3224
3320
  } catch {
3225
3321
  return { count: 0, latestMtime: 0 };
@@ -3407,7 +3503,7 @@ var AuthManager = class extends import_events2.EventEmitter {
3407
3503
  var import_promises5 = require("fs/promises");
3408
3504
 
3409
3505
  // src/terminal/TerminalExecutor.ts
3410
- var import_node_child_process4 = require("child_process");
3506
+ var import_node_child_process5 = require("child_process");
3411
3507
  var import_uuid4 = require("uuid");
3412
3508
  var EXEC_TIMEOUT_MS = 5 * 60 * 1e3;
3413
3509
  var TerminalExecutor = class {
@@ -3433,7 +3529,7 @@ var TerminalExecutor = class {
3433
3529
  const execId = (0, import_uuid4.v4)();
3434
3530
  const shell = isWindows ? "powershell" : "bash";
3435
3531
  const args = isWindows ? ["-Command", command] : ["-c", command];
3436
- const proc = (0, import_node_child_process4.spawn)(shell, args, {
3532
+ const proc = (0, import_node_child_process5.spawn)(shell, args, {
3437
3533
  cwd,
3438
3534
  stdio: ["ignore", "pipe", "pipe"],
3439
3535
  env: { ...process.env }
@@ -3496,7 +3592,7 @@ var TerminalExecutor = class {
3496
3592
  // src/server.ts
3497
3593
  var WS_PORT = 3745;
3498
3594
  var HTTP_PORT = 3746;
3499
- var execAsync = (0, import_node_util.promisify)(import_node_child_process5.exec);
3595
+ var execAsync = (0, import_node_util.promisify)(import_node_child_process6.exec);
3500
3596
  async function killPortProcess(port) {
3501
3597
  try {
3502
3598
  if (isWindows) {
@@ -3853,9 +3949,9 @@ async function start(opts = {}) {
3853
3949
  }
3854
3950
  case "terminal_exec": {
3855
3951
  const activeSession = sessionManager.getActiveSessions().find((s) => s.id === event.sessionId);
3856
- const cwd = activeSession?.projectPath ?? sessionManager.getSessionProjectPath(event.sessionId);
3952
+ const cwd = activeSession?.projectPath ?? sessionManager.getSessionProjectPath(event.sessionId) ?? event.projectPath;
3857
3953
  if (!cwd) {
3858
- wsBridge.send(ws, { type: "error", code: "TERMINAL_EXEC_ERROR", message: "Session not found or no project path", sessionId: event.sessionId });
3954
+ wsBridge.send(ws, { type: "error", code: "TERMINAL_EXEC_ERROR", message: `Session not found (id: ${event.sessionId.slice(0, 8)}\u2026)`, sessionId: event.sessionId });
3859
3955
  break;
3860
3956
  }
3861
3957
  terminalExecutor.exec(event.sessionId, event.command, cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sessix-server",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "bin": {
5
5
  "sessix-server": "./dist/index.js"
6
6
  },
@@ -17,6 +17,15 @@
17
17
  "files": [
18
18
  "dist"
19
19
  ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ampere1988/Sessix-Server.git"
23
+ },
24
+ "homepage": "https://github.com/ampere1988/Sessix-Server#readme",
25
+ "bugs": {
26
+ "url": "https://github.com/ampere1988/Sessix-Server/issues"
27
+ },
28
+ "license": "MIT",
20
29
  "engines": {
21
30
  "node": ">=22.0.0"
22
31
  },
@@ -1,86 +0,0 @@
1
- import type { ApprovalRequest, ApprovalDecision } from '@sessix/shared';
2
- /** ApprovalProxy 配置 */
3
- interface ApprovalProxyOptions {
4
- port: number;
5
- token: string;
6
- }
7
- /** 外部注入的连接信息回调(用于 /health 端点) */
8
- interface StatusInfo {
9
- connections: number;
10
- activeSessions: number;
11
- }
12
- /**
13
- * 审批代理 HTTP 服务
14
- *
15
- * 接收 Claude Code hook 发来的工具审批请求,通过长轮询机制
16
- * hold 住响应,等待手机端用户做出审批决策后再返回。
17
- */
18
- export declare class ApprovalProxy {
19
- private server;
20
- private token;
21
- private port;
22
- private settingsPath;
23
- /** 待处理的审批请求:requestId -> { resolve, timer, request } */
24
- private pendingApprovals;
25
- /** 审批请求回调(通知外部推送到手机) */
26
- private approvalRequestCallbacks;
27
- /** 获取状态信息的回调(由外部注入) */
28
- private statusInfoProvider;
29
- constructor(options: ApprovalProxyOptions);
30
- /**
31
- * 异步工厂方法:等待端口监听成功后 resolve,端口占用等错误时 reject。
32
- */
33
- static create(options: ApprovalProxyOptions): Promise<ApprovalProxy>;
34
- /** 注册审批请求回调(当有新的审批请求时触发) */
35
- onApprovalRequest(callback: (request: ApprovalRequest) => void): void;
36
- /** 设置状态信息提供者(用于 /health 端点) */
37
- setStatusInfoProvider(provider: () => StatusInfo): void;
38
- /**
39
- * 注入审批结果
40
- *
41
- * 从 pendingApprovals 中取出对应请求,resolve promise,
42
- * 让长轮询的 HTTP 响应返回审批结果给 Claude Code hook。
43
- */
44
- resolveApproval(requestId: string, decision: ApprovalDecision): boolean;
45
- /** 获取当前待处理的审批数量 */
46
- getPendingCount(): number;
47
- /** 检查指定审批请求是否仍在等待用户决策 */
48
- isPending(requestId: string): boolean;
49
- /** 检查工具是否已在 settings.json permissions.allow 中(检查项目级和全局) */
50
- private isToolInClaudeSettings;
51
- /** 将工具写入 settings.json permissions.allow(项目级或全局) */
52
- addToClaudeSettings(projectPath: string | undefined, toolName: string): void;
53
- /** 获取指定会话的所有 pending approval requests(用于 subscribe 重发) */
54
- getPendingRequestsForSession(sessionId: string): ApprovalRequest[];
55
- /**
56
- * 批量允许所有待处理的审批请求(手机端断线时调用)
57
- */
58
- approveAll(reason?: string): void;
59
- /** 优雅关闭 HTTP 服务 */
60
- close(): Promise<void>;
61
- /** 路由请求 */
62
- private handleRequest;
63
- /**
64
- * 核心端点:处理 Claude Code hook 的审批请求
65
- *
66
- * 长轮询实现:
67
- * 1. 解析请求 body
68
- * 2. 创建 ApprovalRequest 对象
69
- * 3. 通知外部(推到手机)
70
- * 4. 创建 Promise 并 hold 住 response
71
- * 5. 等待 resolveApproval() 被调用或超时
72
- */
73
- private handleApprovalHook;
74
- /** 健康检查端点 */
75
- private handleHealth;
76
- /** 返回连接 token(仅本机访问) */
77
- private handleToken;
78
- /** 通知所有注册的审批请求回调 */
79
- private notifyApprovalRequest;
80
- /** 手动解析请求的 JSON body(限制最大 1MB 防止滥用) */
81
- private parseJsonBody;
82
- /** 发送 JSON 响应的辅助方法 */
83
- private sendJson;
84
- }
85
- export {};
86
- //# sourceMappingURL=ApprovalProxy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ApprovalProxy.d.ts","sourceRoot":"","sources":["../../src/approval/ApprovalProxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEvE,uBAAuB;AACvB,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,iCAAiC;AACjC,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,YAAY,CAAsD;IAE1E,wDAAwD;IACxD,OAAO,CAAC,gBAAgB,CAIpB;IAEJ,wBAAwB;IACxB,OAAO,CAAC,wBAAwB,CAAgD;IAEhF,uBAAuB;IACvB,OAAO,CAAC,kBAAkB,CAAkC;gBAEhD,OAAO,EAAE,oBAAoB;IAazC;;OAEG;WACU,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAe1E,4BAA4B;IAC5B,iBAAiB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAIrE,+BAA+B;IAC/B,qBAAqB,CAAC,QAAQ,EAAE,MAAM,UAAU,GAAG,IAAI;IAIvD;;;;;OAKG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO;IAkBvE,mBAAmB;IACnB,eAAe,IAAI,MAAM;IAIzB,yBAAyB;IACzB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIrC,2DAA2D;IAC3D,OAAO,CAAC,sBAAsB;IAsB9B,oDAAoD;IACpD,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAwC5E,2DAA2D;IAC3D,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE;IAUlE;;OAEG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAUjC,mBAAmB;IACnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,WAAW;IACX,OAAO,CAAC,aAAa;IA2BrB;;;;;;;;;OASG;YACW,kBAAkB;IAyDhC,aAAa;IACb,OAAO,CAAC,YAAY;IASpB,wBAAwB;IACxB,OAAO,CAAC,WAAW;IAenB,oBAAoB;IACpB,OAAO,CAAC,qBAAqB;IAU7B,uCAAuC;IACvC,OAAO,CAAC,aAAa;IAgCrB,sBAAsB;IACtB,OAAO,CAAC,QAAQ;CAQjB"}