sessix-server 0.2.4 → 0.2.6
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/approval/ApprovalProxy.d.ts +0 -10
- package/dist/approval/ApprovalProxy.d.ts.map +1 -1
- package/dist/approval/ApprovalProxy.js +6 -44
- package/dist/approval/ApprovalProxy.js.map +1 -1
- package/dist/hooks/HookInstaller.d.ts +7 -11
- package/dist/hooks/HookInstaller.d.ts.map +1 -1
- package/dist/hooks/HookInstaller.js +49 -96
- package/dist/hooks/HookInstaller.js.map +1 -1
- package/dist/index.js +205 -145
- package/dist/notification/ExpoNotificationChannel.d.ts.map +1 -1
- package/dist/notification/ExpoNotificationChannel.js +1 -9
- package/dist/notification/ExpoNotificationChannel.js.map +1 -1
- package/dist/notification/NotificationService.d.ts +1 -1
- package/dist/notification/NotificationService.d.ts.map +1 -1
- package/dist/notification/NotificationService.js +5 -13
- package/dist/notification/NotificationService.js.map +1 -1
- package/dist/providers/ExecutionProvider.d.ts +2 -8
- package/dist/providers/ExecutionProvider.d.ts.map +1 -1
- package/dist/providers/ProcessProvider.d.ts +2 -2
- package/dist/providers/ProcessProvider.d.ts.map +1 -1
- package/dist/providers/ProcessProvider.js +15 -49
- package/dist/providers/ProcessProvider.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +198 -139
- package/dist/server.js.map +1 -1
- package/dist/session/ProjectReader.js +1 -1
- package/dist/session/ProjectReader.js.map +1 -1
- package/dist/session/SessionManager.d.ts +3 -7
- package/dist/session/SessionManager.d.ts.map +1 -1
- package/dist/session/SessionManager.js +5 -45
- package/dist/session/SessionManager.js.map +1 -1
- package/dist/ws/WsBridge.js +4 -4
- package/dist/ws/WsBridge.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var import_node_os7 = require("os");
|
|
28
|
+
var import_node_fs3 = require("fs");
|
|
29
|
+
var import_node_path6 = require("path");
|
|
30
30
|
|
|
31
31
|
// src/i18n/locales/zh.ts
|
|
32
32
|
var zh = {
|
|
@@ -289,38 +289,87 @@ function t(key, params) {
|
|
|
289
289
|
// src/server.ts
|
|
290
290
|
var import_uuid4 = require("uuid");
|
|
291
291
|
var import_promises4 = require("fs/promises");
|
|
292
|
-
var
|
|
293
|
-
var
|
|
294
|
-
var
|
|
292
|
+
var import_node_os6 = require("os");
|
|
293
|
+
var import_node_path5 = require("path");
|
|
294
|
+
var import_node_child_process4 = require("child_process");
|
|
295
295
|
var import_node_util = require("util");
|
|
296
296
|
|
|
297
297
|
// src/providers/ProcessProvider.ts
|
|
298
|
-
var
|
|
298
|
+
var import_child_process = require("child_process");
|
|
299
299
|
var import_readline = require("readline");
|
|
300
300
|
var import_events = require("events");
|
|
301
|
-
var
|
|
301
|
+
var import_node_os2 = require("os");
|
|
302
302
|
var import_uuid = require("uuid");
|
|
303
303
|
|
|
304
304
|
// src/utils/claudePath.ts
|
|
305
|
-
var
|
|
305
|
+
var import_node_child_process2 = require("child_process");
|
|
306
|
+
var import_node_fs = require("fs");
|
|
307
|
+
var import_node_path = require("path");
|
|
308
|
+
var import_node_os = require("os");
|
|
309
|
+
|
|
310
|
+
// src/utils/platform.ts
|
|
311
|
+
var import_node_child_process = require("child_process");
|
|
312
|
+
var isWindows = process.platform === "win32";
|
|
313
|
+
function killProcessCrossPlatform(proc, timeoutMs = 3e3) {
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
if (proc.exitCode !== null || proc.signalCode !== null) {
|
|
316
|
+
resolve();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const onExit = () => {
|
|
320
|
+
clearTimeout(timer);
|
|
321
|
+
resolve();
|
|
322
|
+
};
|
|
323
|
+
proc.once("exit", onExit);
|
|
324
|
+
if (isWindows) {
|
|
325
|
+
if (proc.pid) {
|
|
326
|
+
(0, import_node_child_process.spawn)("taskkill", ["/PID", String(proc.pid), "/T", "/F"], { stdio: "ignore" });
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
proc.kill("SIGTERM");
|
|
330
|
+
}
|
|
331
|
+
const timer = setTimeout(() => {
|
|
332
|
+
if (proc.exitCode === null && proc.signalCode === null) {
|
|
333
|
+
if (!isWindows) {
|
|
334
|
+
proc.kill("SIGKILL");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
resolve();
|
|
338
|
+
}, timeoutMs);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function isNormalExit(code, signal) {
|
|
342
|
+
if (code === 0) return true;
|
|
343
|
+
if (isWindows) {
|
|
344
|
+
return code === 1;
|
|
345
|
+
}
|
|
346
|
+
return code === 143 || signal === "SIGTERM";
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/utils/claudePath.ts
|
|
306
350
|
function findClaudePath() {
|
|
307
351
|
try {
|
|
308
|
-
|
|
352
|
+
const cmd = isWindows ? "where claude" : "which claude";
|
|
353
|
+
return (0, import_node_child_process2.execSync)(cmd, { encoding: "utf-8" }).trim().split("\n")[0];
|
|
309
354
|
} catch {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
355
|
+
}
|
|
356
|
+
const candidates = isWindows ? [
|
|
357
|
+
(0, import_node_path.join)(process.env.LOCALAPPDATA ?? "", "Programs", "claude", "claude.exe"),
|
|
358
|
+
(0, import_node_path.join)((0, import_node_os.homedir)(), "AppData", "Local", "Programs", "claude", "claude.exe"),
|
|
359
|
+
(0, import_node_path.join)((0, import_node_os.homedir)(), ".claude", "local", "claude.exe")
|
|
360
|
+
] : [
|
|
361
|
+
(0, import_node_path.join)((0, import_node_os.homedir)(), ".local", "bin", "claude"),
|
|
362
|
+
"/usr/local/bin/claude",
|
|
363
|
+
"/opt/homebrew/bin/claude"
|
|
364
|
+
];
|
|
365
|
+
for (const candidate of candidates) {
|
|
366
|
+
try {
|
|
367
|
+
(0, import_node_fs.accessSync)(candidate, import_node_fs.constants.X_OK);
|
|
368
|
+
return candidate;
|
|
369
|
+
} catch {
|
|
321
370
|
}
|
|
322
|
-
return "claude";
|
|
323
371
|
}
|
|
372
|
+
return "claude";
|
|
324
373
|
}
|
|
325
374
|
|
|
326
375
|
// src/providers/ProcessProvider.ts
|
|
@@ -393,19 +442,7 @@ var ProcessProvider = class {
|
|
|
393
442
|
entry.process.stdin?.end();
|
|
394
443
|
} catch {
|
|
395
444
|
}
|
|
396
|
-
entry.process
|
|
397
|
-
await new Promise((resolve) => {
|
|
398
|
-
const timeout = setTimeout(() => {
|
|
399
|
-
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
400
|
-
entry.process.kill("SIGKILL");
|
|
401
|
-
}
|
|
402
|
-
resolve();
|
|
403
|
-
}, 3e3);
|
|
404
|
-
entry.process.once("exit", () => {
|
|
405
|
-
clearTimeout(timeout);
|
|
406
|
-
resolve();
|
|
407
|
-
});
|
|
408
|
-
});
|
|
445
|
+
await killProcessCrossPlatform(entry.process);
|
|
409
446
|
}
|
|
410
447
|
this.emittedQuestionToolUseIds.delete(sessionId);
|
|
411
448
|
this.activeSessions.delete(sessionId);
|
|
@@ -435,7 +472,7 @@ var ProcessProvider = class {
|
|
|
435
472
|
entry.process.stdin?.end();
|
|
436
473
|
} catch {
|
|
437
474
|
}
|
|
438
|
-
entry.process
|
|
475
|
+
killProcessCrossPlatform(entry.process);
|
|
439
476
|
}
|
|
440
477
|
} else {
|
|
441
478
|
console.log(`[ProcessProvider] Session ${sessionId}: process exited, respawning`);
|
|
@@ -517,7 +554,7 @@ var ProcessProvider = class {
|
|
|
517
554
|
}
|
|
518
555
|
const env = { ...process.env, SESSIX_SESSION_ID: sessionId };
|
|
519
556
|
delete env.CLAUDECODE;
|
|
520
|
-
const proc = (0,
|
|
557
|
+
const proc = (0, import_child_process.spawn)(CLAUDE_PATH, args, {
|
|
521
558
|
cwd: projectPath,
|
|
522
559
|
env,
|
|
523
560
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -663,7 +700,7 @@ var ProcessProvider = class {
|
|
|
663
700
|
entry.session.lastActiveAt = Date.now();
|
|
664
701
|
const alreadyHasResult = entry.session.status === "idle" || entry.session.status === "error";
|
|
665
702
|
if (alreadyHasResult) return;
|
|
666
|
-
const isNormal = code
|
|
703
|
+
const isNormal = isNormalExit(code, signal);
|
|
667
704
|
entry.session.status = isNormal ? "idle" : "error";
|
|
668
705
|
if (!isNormal) {
|
|
669
706
|
console.error(
|
|
@@ -729,8 +766,8 @@ ${context}`;
|
|
|
729
766
|
return new Promise((resolve, reject) => {
|
|
730
767
|
const env = { ...process.env };
|
|
731
768
|
delete env.CLAUDECODE;
|
|
732
|
-
const proc = (0,
|
|
733
|
-
cwd: (0,
|
|
769
|
+
const proc = (0, import_child_process.spawn)(CLAUDE_PATH, ["-p", prompt, "--output-format", "text"], {
|
|
770
|
+
cwd: (0, import_node_os2.homedir)(),
|
|
734
771
|
env,
|
|
735
772
|
stdio: ["pipe", "pipe", "pipe"]
|
|
736
773
|
});
|
|
@@ -1622,15 +1659,15 @@ var WsBridge = class _WsBridge {
|
|
|
1622
1659
|
|
|
1623
1660
|
// src/approval/ApprovalProxy.ts
|
|
1624
1661
|
var import_node_http = __toESM(require("http"));
|
|
1625
|
-
var
|
|
1626
|
-
var
|
|
1627
|
-
var
|
|
1662
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
1663
|
+
var import_node_path2 = __toESM(require("path"));
|
|
1664
|
+
var import_node_os3 = __toESM(require("os"));
|
|
1628
1665
|
var import_uuid3 = require("uuid");
|
|
1629
1666
|
var ApprovalProxy = class _ApprovalProxy {
|
|
1630
1667
|
server;
|
|
1631
1668
|
token;
|
|
1632
1669
|
port;
|
|
1633
|
-
settingsPath =
|
|
1670
|
+
settingsPath = import_node_path2.default.join(import_node_os3.default.homedir(), ".claude", "settings.json");
|
|
1634
1671
|
/** 待处理的审批请求:requestId -> { resolve, timer, request } */
|
|
1635
1672
|
pendingApprovals = /* @__PURE__ */ new Map();
|
|
1636
1673
|
/** 审批请求回调(通知外部推送到手机) */
|
|
@@ -1735,7 +1772,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1735
1772
|
isToolInClaudeSettings(toolName, projectPath) {
|
|
1736
1773
|
const checkPath = (filepath) => {
|
|
1737
1774
|
try {
|
|
1738
|
-
const raw =
|
|
1775
|
+
const raw = import_node_fs2.default.readFileSync(filepath, "utf-8");
|
|
1739
1776
|
const settings = JSON.parse(raw);
|
|
1740
1777
|
const allow = settings?.permissions?.allow ?? [];
|
|
1741
1778
|
return allow.some((entry) => {
|
|
@@ -1749,24 +1786,24 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1749
1786
|
}
|
|
1750
1787
|
};
|
|
1751
1788
|
if (projectPath) {
|
|
1752
|
-
const projectSettingsPath =
|
|
1789
|
+
const projectSettingsPath = import_node_path2.default.join(projectPath, ".claude", "settings.json");
|
|
1753
1790
|
if (checkPath(projectSettingsPath)) return true;
|
|
1754
1791
|
}
|
|
1755
1792
|
return checkPath(this.settingsPath);
|
|
1756
1793
|
}
|
|
1757
1794
|
/** 将工具写入 settings.json permissions.allow(项目级或全局) */
|
|
1758
1795
|
addToClaudeSettings(projectPath, toolName) {
|
|
1759
|
-
const targetPath = projectPath ?
|
|
1796
|
+
const targetPath = projectPath ? import_node_path2.default.join(projectPath, ".claude", "settings.json") : this.settingsPath;
|
|
1760
1797
|
try {
|
|
1761
1798
|
if (projectPath) {
|
|
1762
|
-
const dir =
|
|
1763
|
-
if (!
|
|
1764
|
-
|
|
1799
|
+
const dir = import_node_path2.default.dirname(targetPath);
|
|
1800
|
+
if (!import_node_fs2.default.existsSync(dir)) {
|
|
1801
|
+
import_node_fs2.default.mkdirSync(dir, { recursive: true });
|
|
1765
1802
|
}
|
|
1766
1803
|
}
|
|
1767
1804
|
let settings = {};
|
|
1768
1805
|
try {
|
|
1769
|
-
settings = JSON.parse(
|
|
1806
|
+
settings = JSON.parse(import_node_fs2.default.readFileSync(targetPath, "utf-8"));
|
|
1770
1807
|
} catch {
|
|
1771
1808
|
}
|
|
1772
1809
|
if (!settings.permissions) {
|
|
@@ -1780,7 +1817,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
1780
1817
|
const entry = `${toolName}(*)`;
|
|
1781
1818
|
if (!allow.includes(entry)) {
|
|
1782
1819
|
allow.push(entry);
|
|
1783
|
-
|
|
1820
|
+
import_node_fs2.default.writeFileSync(targetPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
1784
1821
|
const label = projectPath ? `${projectPath}/.claude/settings.json` : "~/.claude/settings.json";
|
|
1785
1822
|
console.log(`[ApprovalProxy] ${t("approval.alwaysAllowWritten", { entry, label })}`);
|
|
1786
1823
|
}
|
|
@@ -2004,11 +2041,12 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2004
2041
|
|
|
2005
2042
|
// src/mdns/MdnsService.ts
|
|
2006
2043
|
var import_bonjour_service = __toESM(require("bonjour-service"));
|
|
2007
|
-
var
|
|
2044
|
+
var import_node_os4 = require("os");
|
|
2008
2045
|
function getLanAddresses() {
|
|
2009
2046
|
const results = [];
|
|
2010
|
-
for (const [name, addrs] of Object.entries((0,
|
|
2011
|
-
if (name.startsWith("utun") || name
|
|
2047
|
+
for (const [name, addrs] of Object.entries((0, import_node_os4.networkInterfaces)())) {
|
|
2048
|
+
if (name.startsWith("utun") || name === "lo") continue;
|
|
2049
|
+
if (isWindows && (name.startsWith("vEthernet") || name.includes("Loopback"))) continue;
|
|
2012
2050
|
for (const addr of addrs ?? []) {
|
|
2013
2051
|
if (addr.family === "IPv4" && !addr.internal) {
|
|
2014
2052
|
results.push(addr.address);
|
|
@@ -2039,7 +2077,13 @@ var MdnsService = class {
|
|
|
2039
2077
|
return;
|
|
2040
2078
|
}
|
|
2041
2079
|
const lanAddrs = getLanAddresses();
|
|
2042
|
-
|
|
2080
|
+
const onMdnsError = (err) => {
|
|
2081
|
+
console.warn(`[MdnsService] mDNS error (non-fatal): ${err.message}`);
|
|
2082
|
+
this.stop();
|
|
2083
|
+
};
|
|
2084
|
+
const opts = lanAddrs.length > 0 ? { interface: lanAddrs[0] } : {};
|
|
2085
|
+
this.bonjour = new import_bonjour_service.default(opts, onMdnsError);
|
|
2086
|
+
this.bonjour.server?.mdns?.on("error", onMdnsError);
|
|
2043
2087
|
if (lanAddrs.length > 0) {
|
|
2044
2088
|
console.log(`[MdnsService] ${t("mdns.boundInterface", { ip: lanAddrs[0] })}`);
|
|
2045
2089
|
}
|
|
@@ -2104,72 +2148,62 @@ var MdnsService = class {
|
|
|
2104
2148
|
|
|
2105
2149
|
// src/hooks/HookInstaller.ts
|
|
2106
2150
|
var import_promises2 = require("fs/promises");
|
|
2107
|
-
var
|
|
2108
|
-
var
|
|
2109
|
-
var SESSIX_HOOKS_DIR = (0,
|
|
2110
|
-
var HOOK_SCRIPT_PATH = (0,
|
|
2111
|
-
var PERMISSION_ACCEPT_PATH = (0,
|
|
2112
|
-
var CLAUDE_SETTINGS_PATH = (0,
|
|
2113
|
-
var HOOK_COMMAND = "~/.sessix/hooks/approval-hook.
|
|
2114
|
-
var PERMISSION_ACCEPT_COMMAND = "~/.sessix/hooks/permission-accept.
|
|
2115
|
-
var
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
# \u4ECE stdin \u8BFB\u53D6 hook payload
|
|
2124
|
-
PAYLOAD=$(cat)
|
|
2125
|
-
|
|
2126
|
-
# \u83B7\u53D6\u9879\u76EE\u8DEF\u5F84\uFF08\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF09
|
|
2127
|
-
PROJECT_PATH="$PWD"
|
|
2128
|
-
|
|
2129
|
-
# \u53D1\u9001\u5BA1\u6279\u8BF7\u6C42\u5230 Sessix \u670D\u52A1\u5668\uFF08\u957F\u8F6E\u8BE2\uFF0C\u8D85\u65F6\u65F6\u95F4 > 300s\uFF09
|
|
2130
|
-
RESPONSE=$(curl -s -X POST "http://localhost:3746/hook/approval" \\
|
|
2131
|
-
-H "Content-Type: application/json" \\
|
|
2132
|
-
-d "{\\"sessionId\\": \\"$SESSIX_SESSION_ID\\", \\"projectPath\\": \\"$PROJECT_PATH\\", \\"payload\\": $PAYLOAD}" \\
|
|
2133
|
-
--max-time 320 \\
|
|
2134
|
-
2>/dev/null)
|
|
2135
|
-
|
|
2136
|
-
if [ $? -ne 0 ] || [ -z "$RESPONSE" ]; then
|
|
2137
|
-
# \u5982\u679C Sessix \u670D\u52A1\u5668\u4E0D\u53EF\u7528\uFF0C\u9ED8\u8BA4\u653E\u884C\uFF08exit 0 = \u6279\u51C6\uFF09
|
|
2138
|
-
exit 0
|
|
2139
|
-
fi
|
|
2151
|
+
var import_node_path3 = require("path");
|
|
2152
|
+
var import_node_os5 = require("os");
|
|
2153
|
+
var SESSIX_HOOKS_DIR = (0, import_node_path3.join)((0, import_node_os5.homedir)(), ".sessix", "hooks");
|
|
2154
|
+
var HOOK_SCRIPT_PATH = (0, import_node_path3.join)(SESSIX_HOOKS_DIR, "approval-hook.js");
|
|
2155
|
+
var PERMISSION_ACCEPT_PATH = (0, import_node_path3.join)(SESSIX_HOOKS_DIR, "permission-accept.js");
|
|
2156
|
+
var CLAUDE_SETTINGS_PATH = (0, import_node_path3.join)((0, import_node_os5.homedir)(), ".claude", "settings.json");
|
|
2157
|
+
var HOOK_COMMAND = "node ~/.sessix/hooks/approval-hook.js";
|
|
2158
|
+
var PERMISSION_ACCEPT_COMMAND = "node ~/.sessix/hooks/permission-accept.js";
|
|
2159
|
+
var LEGACY_HOOK_COMMANDS = [
|
|
2160
|
+
"~/.sessix/hooks/approval-hook.sh",
|
|
2161
|
+
"~/.sessix/hooks/permission-accept.sh"
|
|
2162
|
+
];
|
|
2163
|
+
var HOOK_SCRIPT_TEMPLATE = `#!/usr/bin/env node
|
|
2164
|
+
// Sessix Approval Hook
|
|
2165
|
+
// \u4EC5\u5728 Sessix \u7BA1\u7406\u7684\u4F1A\u8BDD\u4E2D\u6FC0\u6D3B
|
|
2140
2166
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2167
|
+
const sessionId = process.env.SESSIX_SESSION_ID
|
|
2168
|
+
if (!sessionId) process.exit(0)
|
|
2143
2169
|
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2170
|
+
let payload = ''
|
|
2171
|
+
process.stdin.on('data', (chunk) => { payload += chunk })
|
|
2172
|
+
process.stdin.on('end', async () => {
|
|
2173
|
+
try {
|
|
2174
|
+
const res = await fetch('http://localhost:3746/hook/approval', {
|
|
2175
|
+
method: 'POST',
|
|
2176
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2177
|
+
body: JSON.stringify({
|
|
2178
|
+
sessionId,
|
|
2179
|
+
projectPath: process.cwd(),
|
|
2180
|
+
payload: JSON.parse(payload),
|
|
2181
|
+
}),
|
|
2182
|
+
signal: AbortSignal.timeout(320000),
|
|
2183
|
+
})
|
|
2184
|
+
const data = await res.json()
|
|
2185
|
+
process.exit(data.decision === 'deny' ? 1 : 0)
|
|
2186
|
+
} catch {
|
|
2187
|
+
// Sessix \u670D\u52A1\u5668\u4E0D\u53EF\u7528\uFF0C\u9ED8\u8BA4\u653E\u884C
|
|
2188
|
+
process.exit(0)
|
|
2189
|
+
}
|
|
2190
|
+
})
|
|
2154
2191
|
`;
|
|
2155
|
-
var PERMISSION_ACCEPT_TEMPLATE = `#!/bin/
|
|
2156
|
-
|
|
2157
|
-
|
|
2192
|
+
var PERMISSION_ACCEPT_TEMPLATE = `#!/usr/bin/env node
|
|
2193
|
+
// Sessix PermissionRequest \u515C\u5E95
|
|
2194
|
+
// \u81EA\u52A8\u63A5\u53D7\u6743\u9650\u8BF7\u6C42\uFF0C\u907F\u514D Sessix \u4F1A\u8BDD\u963B\u585E
|
|
2158
2195
|
|
|
2159
|
-
if
|
|
2160
|
-
exit 0
|
|
2161
|
-
fi
|
|
2196
|
+
if (!process.env.SESSIX_SESSION_ID) process.exit(0)
|
|
2162
2197
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
exit 0
|
|
2198
|
+
process.stdout.write('{"decision":"allow"}\\n')
|
|
2199
|
+
process.exit(0)
|
|
2166
2200
|
`;
|
|
2167
2201
|
var HookInstaller = class {
|
|
2168
2202
|
/**
|
|
2169
2203
|
* 安装 hook
|
|
2170
2204
|
*
|
|
2171
2205
|
* 1. 创建 ~/.sessix/hooks/ 目录
|
|
2172
|
-
* 2. 写入 approval-hook.
|
|
2206
|
+
* 2. 写入 approval-hook.js 脚本
|
|
2173
2207
|
* 3. 赋予执行权限
|
|
2174
2208
|
* 4. 更新 Claude Code settings.json 添加 hook 配置
|
|
2175
2209
|
*/
|
|
@@ -2222,6 +2256,10 @@ var HookInstaller = class {
|
|
|
2222
2256
|
async addHookToSettings() {
|
|
2223
2257
|
let settings = await this.readClaudeSettings();
|
|
2224
2258
|
let changed = false;
|
|
2259
|
+
for (const cmd of LEGACY_HOOK_COMMANDS) {
|
|
2260
|
+
this.removeHookCommand(settings, "PreToolUse", cmd);
|
|
2261
|
+
this.removeHookCommand(settings, "PermissionRequest", cmd);
|
|
2262
|
+
}
|
|
2225
2263
|
if (!settings.hooks) {
|
|
2226
2264
|
settings.hooks = {};
|
|
2227
2265
|
}
|
|
@@ -2289,7 +2327,7 @@ var HookInstaller = class {
|
|
|
2289
2327
|
* 写入 Claude Code settings.json
|
|
2290
2328
|
*/
|
|
2291
2329
|
async writeClaudeSettings(settings) {
|
|
2292
|
-
await (0, import_promises2.mkdir)((0,
|
|
2330
|
+
await (0, import_promises2.mkdir)((0, import_node_path3.join)((0, import_node_os5.homedir)(), ".claude"), { recursive: true });
|
|
2293
2331
|
await (0, import_promises2.writeFile)(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
2294
2332
|
}
|
|
2295
2333
|
/**
|
|
@@ -2298,11 +2336,11 @@ var HookInstaller = class {
|
|
|
2298
2336
|
hasHookConfig(settings) {
|
|
2299
2337
|
return this.hasPreToolUseConfig(settings) && this.hasPermissionRequestConfig(settings);
|
|
2300
2338
|
}
|
|
2301
|
-
/** 检查 PreToolUse 中是否有 approval-hook.
|
|
2339
|
+
/** 检查 PreToolUse 中是否有 approval-hook.js */
|
|
2302
2340
|
hasPreToolUseConfig(settings) {
|
|
2303
2341
|
return this.hasHookEntry(settings?.hooks?.PreToolUse, HOOK_COMMAND);
|
|
2304
2342
|
}
|
|
2305
|
-
/** 检查 PermissionRequest 中是否有 permission-accept.
|
|
2343
|
+
/** 检查 PermissionRequest 中是否有 permission-accept.js */
|
|
2306
2344
|
hasPermissionRequestConfig(settings) {
|
|
2307
2345
|
return this.hasHookEntry(settings?.hooks?.PermissionRequest, PERMISSION_ACCEPT_COMMAND);
|
|
2308
2346
|
}
|
|
@@ -2316,7 +2354,7 @@ var HookInstaller = class {
|
|
|
2316
2354
|
};
|
|
2317
2355
|
|
|
2318
2356
|
// src/notification/NotificationService.ts
|
|
2319
|
-
var
|
|
2357
|
+
var import_node_path4 = require("path");
|
|
2320
2358
|
var NotificationService = class {
|
|
2321
2359
|
constructor(sessionManager, expoChannel = null) {
|
|
2322
2360
|
this.sessionManager = sessionManager;
|
|
@@ -2412,7 +2450,7 @@ var NotificationService = class {
|
|
|
2412
2450
|
const dangerLevel = this.getDangerLevel(request.toolName);
|
|
2413
2451
|
const isDangerous = dangerLevel === "danger" || dangerLevel === "write";
|
|
2414
2452
|
const categoryId = isDangerous ? "APPROVAL_DANGEROUS" : "APPROVAL_NORMAL";
|
|
2415
|
-
const projectName = (0,
|
|
2453
|
+
const projectName = (0, import_node_path4.basename)(
|
|
2416
2454
|
this.sessionManager.getActiveSessions().find((s) => s.id === request.sessionId)?.projectPath ?? ""
|
|
2417
2455
|
);
|
|
2418
2456
|
const pushTitle = isDangerous ? `\u26A0\uFE0F ${title}` : title;
|
|
@@ -2468,7 +2506,7 @@ var NotificationService = class {
|
|
|
2468
2506
|
/** 从审批请求中提取操作目标的简短描述 */
|
|
2469
2507
|
extractTarget(request) {
|
|
2470
2508
|
const input = request.toolInput;
|
|
2471
|
-
if (input.file_path) return (0,
|
|
2509
|
+
if (input.file_path) return (0, import_node_path4.basename)(String(input.file_path));
|
|
2472
2510
|
if (input.command) return String(input.command).slice(0, 40);
|
|
2473
2511
|
return request.description.slice(0, 40);
|
|
2474
2512
|
}
|
|
@@ -2571,7 +2609,7 @@ var NotificationService = class {
|
|
|
2571
2609
|
getSessionTitle(sessionId) {
|
|
2572
2610
|
const session = this.sessionManager.getActiveSessions().find((s) => s.id === sessionId);
|
|
2573
2611
|
if (!session) return "Unknown";
|
|
2574
|
-
return session.summary ?? (0,
|
|
2612
|
+
return session.summary ?? (0, import_node_path4.basename)(session.projectPath);
|
|
2575
2613
|
}
|
|
2576
2614
|
/** 获取会话的 YOLO 模式状态 */
|
|
2577
2615
|
getYoloMode(sessionId) {
|
|
@@ -2579,9 +2617,9 @@ var NotificationService = class {
|
|
|
2579
2617
|
}
|
|
2580
2618
|
};
|
|
2581
2619
|
|
|
2582
|
-
// src/notification/
|
|
2583
|
-
var
|
|
2584
|
-
var
|
|
2620
|
+
// src/notification/DesktopNotificationChannel.ts
|
|
2621
|
+
var import_node_child_process3 = require("child_process");
|
|
2622
|
+
var DesktopNotificationChannel = class {
|
|
2585
2623
|
isAvailable() {
|
|
2586
2624
|
return process.platform === "darwin";
|
|
2587
2625
|
}
|
|
@@ -2592,9 +2630,9 @@ var MacNotificationChannel = class {
|
|
|
2592
2630
|
const sound = payload.sound ?? "Ping";
|
|
2593
2631
|
const script = `display notification "${body}" with title "${title}" sound name "${sound}"`;
|
|
2594
2632
|
return new Promise((resolve) => {
|
|
2595
|
-
(0,
|
|
2633
|
+
(0, import_node_child_process3.execFile)("osascript", ["-e", script], (err) => {
|
|
2596
2634
|
if (err) {
|
|
2597
|
-
console.warn("[
|
|
2635
|
+
console.warn("[DesktopNotificationChannel] Send notification failed:", err.message);
|
|
2598
2636
|
}
|
|
2599
2637
|
resolve();
|
|
2600
2638
|
});
|
|
@@ -3220,11 +3258,11 @@ var PairingManager = class {
|
|
|
3220
3258
|
};
|
|
3221
3259
|
|
|
3222
3260
|
// src/auth/AuthManager.ts
|
|
3261
|
+
var import_child_process2 = require("child_process");
|
|
3223
3262
|
var import_child_process3 = require("child_process");
|
|
3224
|
-
var import_child_process4 = require("child_process");
|
|
3225
3263
|
var import_util = require("util");
|
|
3226
3264
|
var import_events2 = require("events");
|
|
3227
|
-
var execFileAsync = (0, import_util.promisify)(
|
|
3265
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
|
|
3228
3266
|
var CLAUDE_PATH2 = findClaudePath();
|
|
3229
3267
|
var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3230
3268
|
var AuthManager = class extends import_events2.EventEmitter {
|
|
@@ -3255,7 +3293,7 @@ var AuthManager = class extends import_events2.EventEmitter {
|
|
|
3255
3293
|
}
|
|
3256
3294
|
this.clearLoginTimeout();
|
|
3257
3295
|
this.urlSent = false;
|
|
3258
|
-
const proc = (0,
|
|
3296
|
+
const proc = (0, import_child_process2.spawn)(CLAUDE_PATH2, ["auth", "login"], {
|
|
3259
3297
|
env: { ...process.env, BROWSER: "echo" },
|
|
3260
3298
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3261
3299
|
});
|
|
@@ -3341,15 +3379,31 @@ var AuthManager = class extends import_events2.EventEmitter {
|
|
|
3341
3379
|
var import_promises5 = require("fs/promises");
|
|
3342
3380
|
var WS_PORT = 3745;
|
|
3343
3381
|
var HTTP_PORT = 3746;
|
|
3344
|
-
var execAsync = (0, import_node_util.promisify)(
|
|
3382
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process4.exec);
|
|
3345
3383
|
async function killPortProcess(port) {
|
|
3346
3384
|
try {
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3385
|
+
if (isWindows) {
|
|
3386
|
+
const { stdout } = await execAsync(
|
|
3387
|
+
`netstat -ano | findstr :${port} | findstr LISTENING`
|
|
3388
|
+
);
|
|
3389
|
+
const pids = /* @__PURE__ */ new Set();
|
|
3390
|
+
for (const line of stdout.trim().split("\n")) {
|
|
3391
|
+
const parts = line.trim().split(/\s+/);
|
|
3392
|
+
const pid = parts[parts.length - 1];
|
|
3393
|
+
if (pid && /^\d+$/.test(pid) && pid !== "0") pids.add(pid);
|
|
3394
|
+
}
|
|
3395
|
+
for (const pid of pids) {
|
|
3396
|
+
await execAsync(`taskkill /PID ${pid} /F`).catch(() => {
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
} else {
|
|
3400
|
+
const { stdout } = await execAsync(`lsof -ti :${port}`);
|
|
3401
|
+
const pids = stdout.trim().split("\n").filter((p) => p && /^\d+$/.test(p));
|
|
3402
|
+
if (pids.length > 0) {
|
|
3403
|
+
await execAsync(`kill -9 ${pids.join(" ")}`);
|
|
3404
|
+
}
|
|
3352
3405
|
}
|
|
3406
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
3353
3407
|
} catch {
|
|
3354
3408
|
}
|
|
3355
3409
|
}
|
|
@@ -3367,8 +3421,8 @@ async function createWithRetry(label, port, factory) {
|
|
|
3367
3421
|
}
|
|
3368
3422
|
}
|
|
3369
3423
|
async function start(opts = {}) {
|
|
3370
|
-
const configDir = (0,
|
|
3371
|
-
const tokenFile = (0,
|
|
3424
|
+
const configDir = (0, import_node_path5.join)((0, import_node_os6.homedir)(), ".sessix");
|
|
3425
|
+
const tokenFile = (0, import_node_path5.join)(configDir, "token");
|
|
3372
3426
|
let token;
|
|
3373
3427
|
if (opts.token !== void 0) {
|
|
3374
3428
|
token = opts.token;
|
|
@@ -3391,7 +3445,7 @@ async function start(opts = {}) {
|
|
|
3391
3445
|
const expoChannel = new ExpoNotificationChannel();
|
|
3392
3446
|
const notificationService = new NotificationService(sessionManager, expoChannel);
|
|
3393
3447
|
notificationService.addChannel("expo", expoChannel, opts.enableExpoPush !== false);
|
|
3394
|
-
notificationService.addChannel("mac", new
|
|
3448
|
+
notificationService.addChannel("mac", new DesktopNotificationChannel(), opts.enableMacNotification !== false);
|
|
3395
3449
|
if (opts.activityPush) {
|
|
3396
3450
|
try {
|
|
3397
3451
|
const activityChannel = new ActivityPushChannel(opts.activityPush);
|
|
@@ -3418,7 +3472,7 @@ async function start(opts = {}) {
|
|
|
3418
3472
|
let mdnsService = null;
|
|
3419
3473
|
const pairingManager = new PairingManager({
|
|
3420
3474
|
token,
|
|
3421
|
-
serverName: (0,
|
|
3475
|
+
serverName: (0, import_node_os6.hostname)(),
|
|
3422
3476
|
version: "0.2.0",
|
|
3423
3477
|
onStateChange: (state) => mdnsService?.updatePairingState(state)
|
|
3424
3478
|
});
|
|
@@ -3788,12 +3842,17 @@ async function start(opts = {}) {
|
|
|
3788
3842
|
}));
|
|
3789
3843
|
const startMdns = () => {
|
|
3790
3844
|
if (mdnsService) return;
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3845
|
+
try {
|
|
3846
|
+
mdnsService = new MdnsService({
|
|
3847
|
+
wsPort: WS_PORT,
|
|
3848
|
+
httpPort: HTTP_PORT,
|
|
3849
|
+
pairing: pairingManager.state
|
|
3850
|
+
});
|
|
3851
|
+
mdnsService.start();
|
|
3852
|
+
} catch (err) {
|
|
3853
|
+
console.warn(`[Server] mDNS failed to start (non-fatal): ${err.message}`);
|
|
3854
|
+
mdnsService = null;
|
|
3855
|
+
}
|
|
3797
3856
|
};
|
|
3798
3857
|
const stopMdns = () => {
|
|
3799
3858
|
if (!mdnsService) return;
|
|
@@ -3870,7 +3929,7 @@ async function start(opts = {}) {
|
|
|
3870
3929
|
var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
3871
3930
|
function getPackageVersion() {
|
|
3872
3931
|
try {
|
|
3873
|
-
const pkg = JSON.parse((0,
|
|
3932
|
+
const pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path6.join)(__dirname, "..", "package.json"), "utf8"));
|
|
3874
3933
|
return pkg.version ?? "0.0.0";
|
|
3875
3934
|
} catch {
|
|
3876
3935
|
return "0.0.0";
|
|
@@ -3972,10 +4031,11 @@ ${t("startup.pairingReopened")}`);
|
|
|
3972
4031
|
} else {
|
|
3973
4032
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
3974
4033
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
4034
|
+
process.on("SIGHUP", () => shutdown("SIGHUP"));
|
|
3975
4035
|
}
|
|
3976
4036
|
}
|
|
3977
4037
|
function getLocalIp() {
|
|
3978
|
-
const interfaces = (0,
|
|
4038
|
+
const interfaces = (0, import_node_os7.networkInterfaces)();
|
|
3979
4039
|
for (const iface of Object.values(interfaces)) {
|
|
3980
4040
|
for (const addr of iface ?? []) {
|
|
3981
4041
|
if (addr.family === "IPv4" && !addr.internal) {
|
|
@@ -3983,7 +4043,7 @@ function getLocalIp() {
|
|
|
3983
4043
|
}
|
|
3984
4044
|
}
|
|
3985
4045
|
}
|
|
3986
|
-
return "<your-
|
|
4046
|
+
return "<your-ip>";
|
|
3987
4047
|
}
|
|
3988
4048
|
function buildQrUrl(ip, wsPort, token) {
|
|
3989
4049
|
const base = `sessix://${ip}:${wsPort}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNotificationChannel.d.ts","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAI3F;;;;;;;GAOG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,MAAM,CAAyB;IAEvC,WAAW,IAAI,OAAO;IAItB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoNotificationChannel.d.ts","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAI3F;;;;;;;GAOG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,MAAM,CAAyB;IAEvC,WAAW,IAAI,OAAO;IAItB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CA6BxD"}
|
|
@@ -45,15 +45,7 @@ class ExpoNotificationChannel {
|
|
|
45
45
|
console.warn('[ExpoNotificationChannel] Expo Push API 返回错误:', res.status, JSON.stringify(body));
|
|
46
46
|
}
|
|
47
47
|
else {
|
|
48
|
-
|
|
49
|
-
const data = body.data;
|
|
50
|
-
if (Array.isArray(data)) {
|
|
51
|
-
for (const ticket of data) {
|
|
52
|
-
if (ticket.status === 'error') {
|
|
53
|
-
console.error(`[ExpoNotificationChannel] 推送失败: ${ticket.message} (${ticket.details?.error ?? 'unknown'})`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
48
|
+
console.log('[ExpoNotificationChannel] Expo Push API 响应:', JSON.stringify(body));
|
|
57
49
|
}
|
|
58
50
|
}
|
|
59
51
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNotificationChannel.js","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":";;;AAEA,MAAM,aAAa,GAAG,sCAAsC,CAAA;AAE5D;;;;;;;GAOG;AACH,MAAa,uBAAuB;IAC1B,MAAM,GAAgB,IAAI,GAAG,EAAE,CAAA;IAEvC,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACtB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA4B;QACrC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,OAAM;QAElC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE;YACF,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;SACzB,CAAC,CAAC,CAAA;QAEH,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,wCAAwC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;YAC9E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE;gBACrC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBAC3E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aAC/B,CAAC,CAAA;YAEF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YACjG,CAAC;iBAAM,CAAC;gBACN,
|
|
1
|
+
{"version":3,"file":"ExpoNotificationChannel.js","sourceRoot":"","sources":["../../src/notification/ExpoNotificationChannel.ts"],"names":[],"mappings":";;;AAEA,MAAM,aAAa,GAAG,sCAAsC,CAAA;AAE5D;;;;;;;GAOG;AACH,MAAa,uBAAuB;IAC1B,MAAM,GAAgB,IAAI,GAAG,EAAE,CAAA;IAEvC,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACtB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,OAAO,CAAC,GAAG,CAAC,mDAAmD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA4B;QACrC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,OAAM;QAElC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE;YACF,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;SACzB,CAAC,CAAC,CAAA;QAEH,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,wCAAwC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;YAC9E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE;gBACrC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBAC3E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aAC/B,CAAC,CAAA;YAEF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YACjG,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,6CAA6C,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YAClF,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;CACF;AA9CD,0DA8CC"}
|