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.
- package/dist/index.js +180 -84
- package/dist/server.js +180 -84
- package/package.json +10 -1
- package/dist/approval/ApprovalProxy.d.ts +0 -86
- package/dist/approval/ApprovalProxy.d.ts.map +0 -1
- package/dist/approval/ApprovalProxy.js +0 -363
- package/dist/approval/ApprovalProxy.js.map +0 -1
- package/dist/hooks/HookInstaller.d.ts +0 -55
- package/dist/hooks/HookInstaller.d.ts.map +0 -1
- package/dist/hooks/HookInstaller.js +0 -215
- package/dist/hooks/HookInstaller.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mdns/MdnsService.d.ts +0 -36
- package/dist/mdns/MdnsService.d.ts.map +0 -1
- package/dist/mdns/MdnsService.js +0 -66
- package/dist/mdns/MdnsService.js.map +0 -1
- package/dist/notification/ActivityPushChannel.d.ts +0 -54
- package/dist/notification/ActivityPushChannel.d.ts.map +0 -1
- package/dist/notification/ActivityPushChannel.js +0 -235
- package/dist/notification/ActivityPushChannel.js.map +0 -1
- package/dist/notification/ExpoNotificationChannel.d.ts +0 -17
- package/dist/notification/ExpoNotificationChannel.d.ts.map +0 -1
- package/dist/notification/ExpoNotificationChannel.js +0 -57
- package/dist/notification/ExpoNotificationChannel.js.map +0 -1
- package/dist/notification/MacNotificationChannel.d.ts +0 -22
- package/dist/notification/MacNotificationChannel.d.ts.map +0 -1
- package/dist/notification/MacNotificationChannel.js +0 -33
- package/dist/notification/MacNotificationChannel.js.map +0 -1
- package/dist/notification/NotificationService.d.ts +0 -50
- package/dist/notification/NotificationService.d.ts.map +0 -1
- package/dist/notification/NotificationService.js +0 -177
- package/dist/notification/NotificationService.js.map +0 -1
- package/dist/providers/ExecutionProvider.d.ts +0 -60
- package/dist/providers/ExecutionProvider.d.ts.map +0 -1
- package/dist/providers/ExecutionProvider.js +0 -3
- package/dist/providers/ExecutionProvider.js.map +0 -1
- package/dist/providers/ProcessProvider.d.ts +0 -117
- package/dist/providers/ProcessProvider.d.ts.map +0 -1
- package/dist/providers/ProcessProvider.js +0 -507
- package/dist/providers/ProcessProvider.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/session/ProjectReader.d.ts +0 -44
- package/dist/session/ProjectReader.d.ts.map +0 -1
- package/dist/session/ProjectReader.js +0 -471
- package/dist/session/ProjectReader.js.map +0 -1
- package/dist/session/SessionFileWatcher.d.ts +0 -35
- package/dist/session/SessionFileWatcher.d.ts.map +0 -1
- package/dist/session/SessionFileWatcher.js +0 -207
- package/dist/session/SessionFileWatcher.js.map +0 -1
- package/dist/session/SessionManager.d.ts +0 -114
- package/dist/session/SessionManager.d.ts.map +0 -1
- package/dist/session/SessionManager.js +0 -356
- package/dist/session/SessionManager.js.map +0 -1
- package/dist/ws/WsBridge.d.ts +0 -55
- package/dist/ws/WsBridge.d.ts.map +0 -1
- package/dist/ws/WsBridge.js +0 -220
- 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
|
|
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
|
|
2068
|
+
var import_node_child_process3 = require("child_process");
|
|
2069
2069
|
var import_node_os4 = require("os");
|
|
2070
|
-
function
|
|
2071
|
-
|
|
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
|
-
|
|
2085
|
-
|
|
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.
|
|
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
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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.
|
|
2133
|
-
this.
|
|
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.
|
|
2177
|
+
this.bonjourService = null;
|
|
2137
2178
|
}
|
|
2138
|
-
if (this.
|
|
2139
|
-
this.
|
|
2140
|
-
this.
|
|
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 (
|
|
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.
|
|
2152
|
-
this.
|
|
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.
|
|
2165
|
-
const old = this.
|
|
2166
|
-
this.
|
|
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
|
|
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,
|
|
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
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
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
|
|
3213
|
-
|
|
3214
|
-
...
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
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
|
|
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,
|
|
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)(
|
|
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:
|
|
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.
|
|
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"}
|