sessix-server 0.3.4 → 0.3.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/index.js +80 -9
- package/dist/server.js +78 -7
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var import_node_os8 = require("os");
|
|
28
28
|
var import_node_fs4 = require("fs");
|
|
29
29
|
var import_node_path7 = require("path");
|
|
30
|
-
var
|
|
30
|
+
var import_node_child_process10 = require("child_process");
|
|
31
31
|
|
|
32
32
|
// src/i18n/locales/zh.ts
|
|
33
33
|
var zh = {
|
|
@@ -306,7 +306,7 @@ var import_uuid6 = require("uuid");
|
|
|
306
306
|
var import_promises4 = require("fs/promises");
|
|
307
307
|
var import_node_os7 = require("os");
|
|
308
308
|
var import_node_path6 = require("path");
|
|
309
|
-
var
|
|
309
|
+
var import_node_child_process9 = require("child_process");
|
|
310
310
|
var import_node_util = require("util");
|
|
311
311
|
|
|
312
312
|
// src/providers/ProcessProvider.ts
|
|
@@ -2335,7 +2335,7 @@ var WsBridge = class _WsBridge {
|
|
|
2335
2335
|
onConnection(callback) {
|
|
2336
2336
|
this.connectionCallbacks.push(callback);
|
|
2337
2337
|
}
|
|
2338
|
-
/**
|
|
2338
|
+
/** 注册断开连接回调(任意客户端断开时触发,传入断开的 ws 对象) */
|
|
2339
2339
|
onDisconnect(callback) {
|
|
2340
2340
|
this.disconnectCallbacks.push(callback);
|
|
2341
2341
|
}
|
|
@@ -2449,7 +2449,7 @@ var WsBridge = class _WsBridge {
|
|
|
2449
2449
|
console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
|
|
2450
2450
|
for (const cb of this.disconnectCallbacks) {
|
|
2451
2451
|
try {
|
|
2452
|
-
cb();
|
|
2452
|
+
cb(ws);
|
|
2453
2453
|
} catch (err) {
|
|
2454
2454
|
console.error("[WsBridge] Disconnect callback error:", err);
|
|
2455
2455
|
}
|
|
@@ -3275,6 +3275,8 @@ var NotificationService = class {
|
|
|
3275
3275
|
latestAssistantText = /* @__PURE__ */ new Map();
|
|
3276
3276
|
/** 获取全局待审批总数的回调(跨所有会话) */
|
|
3277
3277
|
globalPendingCountProvider = null;
|
|
3278
|
+
/** 获取当前 WS 连接数的回调(用于在线抑制推送) */
|
|
3279
|
+
connectionCountProvider = null;
|
|
3278
3280
|
/** 添加通知渠道(id 唯一,可用于后续动态开关) */
|
|
3279
3281
|
addChannel(id, channel, enabled = true) {
|
|
3280
3282
|
this.channelMap.set(id, { channel, enabled });
|
|
@@ -3312,6 +3314,14 @@ var NotificationService = class {
|
|
|
3312
3314
|
setGlobalPendingCountProvider(provider) {
|
|
3313
3315
|
this.globalPendingCountProvider = provider;
|
|
3314
3316
|
}
|
|
3317
|
+
/** 设置 WS 连接数提供者(用于 status_change 通知的在线抑制) */
|
|
3318
|
+
setConnectionCountProvider(provider) {
|
|
3319
|
+
this.connectionCountProvider = provider;
|
|
3320
|
+
}
|
|
3321
|
+
/** 移除指定 WebSocket 关联的所有 push token */
|
|
3322
|
+
removeTokensByWs(ws) {
|
|
3323
|
+
this.expoChannel?.removeTokensByWs(ws);
|
|
3324
|
+
}
|
|
3315
3325
|
/** 获取全局待审批总数 */
|
|
3316
3326
|
getGlobalPendingCount() {
|
|
3317
3327
|
return this.globalPendingCountProvider?.() ?? 0;
|
|
@@ -3454,7 +3464,7 @@ var NotificationService = class {
|
|
|
3454
3464
|
isYoloMode,
|
|
3455
3465
|
updatedAt: Date.now()
|
|
3456
3466
|
});
|
|
3457
|
-
} else {
|
|
3467
|
+
} else if ((this.connectionCountProvider?.() ?? 0) === 0) {
|
|
3458
3468
|
this.notify({
|
|
3459
3469
|
title: sessionTitle,
|
|
3460
3470
|
body,
|
|
@@ -3476,7 +3486,7 @@ var NotificationService = class {
|
|
|
3476
3486
|
isYoloMode,
|
|
3477
3487
|
updatedAt: Date.now()
|
|
3478
3488
|
});
|
|
3479
|
-
} else {
|
|
3489
|
+
} else if ((this.connectionCountProvider?.() ?? 0) === 0) {
|
|
3480
3490
|
this.notify({
|
|
3481
3491
|
title: sessionTitle,
|
|
3482
3492
|
body,
|
|
@@ -3564,6 +3574,17 @@ var ExpoNotificationChannel = class {
|
|
|
3564
3574
|
this.soundPreferences.delete(token);
|
|
3565
3575
|
console.log(`[ExpoNotificationChannel] ${t("notification.tokenRemoved", { count: this.tokens.size })}`);
|
|
3566
3576
|
}
|
|
3577
|
+
/** 移除指定 WebSocket 关联的所有 token(设备断开时调用) */
|
|
3578
|
+
removeTokensByWs(ws) {
|
|
3579
|
+
for (const [token, tokenWs] of this.tokenWsMap) {
|
|
3580
|
+
if (tokenWs === ws) {
|
|
3581
|
+
this.tokens.delete(token);
|
|
3582
|
+
this.tokenWsMap.delete(token);
|
|
3583
|
+
this.soundPreferences.delete(token);
|
|
3584
|
+
console.log(`[ExpoNotificationChannel] Token removed on WS disconnect, remaining: ${this.tokens.size}`);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3567
3588
|
/** 更新某个 token 的音效偏好 */
|
|
3568
3589
|
setSoundPreferences(prefs) {
|
|
3569
3590
|
for (const token of this.tokens) {
|
|
@@ -4410,10 +4431,50 @@ var TerminalExecutor = class {
|
|
|
4410
4431
|
}
|
|
4411
4432
|
};
|
|
4412
4433
|
|
|
4434
|
+
// src/utils/cliCapabilities.ts
|
|
4435
|
+
var import_node_child_process8 = require("child_process");
|
|
4436
|
+
var DEFAULT_CAPABILITIES = {
|
|
4437
|
+
effortLevels: ["low", "medium", "high", "max"]
|
|
4438
|
+
};
|
|
4439
|
+
async function parseCliCapabilities() {
|
|
4440
|
+
const claudePath = findClaudePath();
|
|
4441
|
+
const [helpText, version] = await Promise.all([
|
|
4442
|
+
runCli(claudePath, ["--help"]),
|
|
4443
|
+
runCli(claudePath, ["--version"])
|
|
4444
|
+
]);
|
|
4445
|
+
const capabilities = { ...DEFAULT_CAPABILITIES };
|
|
4446
|
+
if (version) {
|
|
4447
|
+
capabilities.version = version.trim();
|
|
4448
|
+
}
|
|
4449
|
+
if (helpText) {
|
|
4450
|
+
const effortMatch = helpText.match(/--effort\s.*?\(([^)]+)\)/);
|
|
4451
|
+
if (effortMatch) {
|
|
4452
|
+
const levels = effortMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
4453
|
+
if (levels.length > 0) {
|
|
4454
|
+
capabilities.effortLevels = levels;
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
console.log(`[CliCapabilities] version=${capabilities.version ?? "unknown"}, effort=[${capabilities.effortLevels.join(", ")}]`);
|
|
4459
|
+
return capabilities;
|
|
4460
|
+
}
|
|
4461
|
+
function runCli(path2, args) {
|
|
4462
|
+
return new Promise((resolve) => {
|
|
4463
|
+
(0, import_node_child_process8.execFile)(path2, args, { timeout: 5e3 }, (err, stdout) => {
|
|
4464
|
+
if (err) {
|
|
4465
|
+
console.warn(`[CliCapabilities] Failed to run ${path2} ${args.join(" ")}:`, err.message);
|
|
4466
|
+
resolve(null);
|
|
4467
|
+
} else {
|
|
4468
|
+
resolve(stdout);
|
|
4469
|
+
}
|
|
4470
|
+
});
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4413
4474
|
// src/server.ts
|
|
4414
4475
|
var WS_PORT = 3745;
|
|
4415
4476
|
var HTTP_PORT = 3746;
|
|
4416
|
-
var execAsync = (0, import_node_util.promisify)(
|
|
4477
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process9.exec);
|
|
4417
4478
|
async function killPortProcess(port) {
|
|
4418
4479
|
try {
|
|
4419
4480
|
if (isWindows) {
|
|
@@ -4535,6 +4596,12 @@ async function start(opts = {}) {
|
|
|
4535
4596
|
notificationService.setGlobalPendingCountProvider(
|
|
4536
4597
|
() => approvalProxy.getPendingCount() + sessionManager.getAllPendingQuestions().length + unreadSessionIds.size
|
|
4537
4598
|
);
|
|
4599
|
+
notificationService.setConnectionCountProvider(() => wsBridge.getConnectionCount());
|
|
4600
|
+
let cliCapabilities = null;
|
|
4601
|
+
parseCliCapabilities().then((caps) => {
|
|
4602
|
+
cliCapabilities = caps;
|
|
4603
|
+
}).catch(() => {
|
|
4604
|
+
});
|
|
4538
4605
|
const broadcastUnreadSessions = () => {
|
|
4539
4606
|
wsBridge.broadcast({ type: "unread_sessions", sessionIds: Array.from(unreadSessionIds) });
|
|
4540
4607
|
};
|
|
@@ -4556,6 +4623,9 @@ async function start(opts = {}) {
|
|
|
4556
4623
|
if (unreadSessionIds.size > 0) {
|
|
4557
4624
|
wsBridge.send(ws, { type: "unread_sessions", sessionIds: Array.from(unreadSessionIds) });
|
|
4558
4625
|
}
|
|
4626
|
+
if (cliCapabilities) {
|
|
4627
|
+
wsBridge.send(ws, { type: "cli_info", effortLevels: cliCapabilities.effortLevels, version: cliCapabilities.version });
|
|
4628
|
+
}
|
|
4559
4629
|
});
|
|
4560
4630
|
wsBridge.onClientEvent(async (event, ws) => {
|
|
4561
4631
|
try {
|
|
@@ -4891,7 +4961,8 @@ async function start(opts = {}) {
|
|
|
4891
4961
|
terminalExecutor.onEvent((event) => {
|
|
4892
4962
|
wsBridge.broadcast(event);
|
|
4893
4963
|
});
|
|
4894
|
-
wsBridge.onDisconnect(() => {
|
|
4964
|
+
wsBridge.onDisconnect((ws) => {
|
|
4965
|
+
notificationService.removeTokensByWs(ws);
|
|
4895
4966
|
if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
|
|
4896
4967
|
approvalProxy.approveAll(t("server.phoneDisconnected"));
|
|
4897
4968
|
}
|
|
@@ -5076,7 +5147,7 @@ async function autoUpdateIfNeeded() {
|
|
|
5076
5147
|
console.log(` \u{1F4E6} ${t("startup.autoUpdating", { current: PKG_VERSION, latest })}`);
|
|
5077
5148
|
console.log();
|
|
5078
5149
|
try {
|
|
5079
|
-
(0,
|
|
5150
|
+
(0, import_node_child_process10.execFileSync)("npx", [`sessix-server@${latest}`], {
|
|
5080
5151
|
stdio: "inherit",
|
|
5081
5152
|
env: { ...process.env, __SESSIX_UPDATED: "1" }
|
|
5082
5153
|
});
|
package/dist/server.js
CHANGED
|
@@ -311,7 +311,7 @@ var import_uuid6 = require("uuid");
|
|
|
311
311
|
var import_promises4 = require("fs/promises");
|
|
312
312
|
var import_node_os7 = require("os");
|
|
313
313
|
var import_node_path6 = require("path");
|
|
314
|
-
var
|
|
314
|
+
var import_node_child_process9 = require("child_process");
|
|
315
315
|
var import_node_util = require("util");
|
|
316
316
|
|
|
317
317
|
// src/providers/ProcessProvider.ts
|
|
@@ -2340,7 +2340,7 @@ var WsBridge = class _WsBridge {
|
|
|
2340
2340
|
onConnection(callback) {
|
|
2341
2341
|
this.connectionCallbacks.push(callback);
|
|
2342
2342
|
}
|
|
2343
|
-
/**
|
|
2343
|
+
/** 注册断开连接回调(任意客户端断开时触发,传入断开的 ws 对象) */
|
|
2344
2344
|
onDisconnect(callback) {
|
|
2345
2345
|
this.disconnectCallbacks.push(callback);
|
|
2346
2346
|
}
|
|
@@ -2454,7 +2454,7 @@ var WsBridge = class _WsBridge {
|
|
|
2454
2454
|
console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
|
|
2455
2455
|
for (const cb of this.disconnectCallbacks) {
|
|
2456
2456
|
try {
|
|
2457
|
-
cb();
|
|
2457
|
+
cb(ws);
|
|
2458
2458
|
} catch (err) {
|
|
2459
2459
|
console.error("[WsBridge] Disconnect callback error:", err);
|
|
2460
2460
|
}
|
|
@@ -3280,6 +3280,8 @@ var NotificationService = class {
|
|
|
3280
3280
|
latestAssistantText = /* @__PURE__ */ new Map();
|
|
3281
3281
|
/** 获取全局待审批总数的回调(跨所有会话) */
|
|
3282
3282
|
globalPendingCountProvider = null;
|
|
3283
|
+
/** 获取当前 WS 连接数的回调(用于在线抑制推送) */
|
|
3284
|
+
connectionCountProvider = null;
|
|
3283
3285
|
/** 添加通知渠道(id 唯一,可用于后续动态开关) */
|
|
3284
3286
|
addChannel(id, channel, enabled = true) {
|
|
3285
3287
|
this.channelMap.set(id, { channel, enabled });
|
|
@@ -3317,6 +3319,14 @@ var NotificationService = class {
|
|
|
3317
3319
|
setGlobalPendingCountProvider(provider) {
|
|
3318
3320
|
this.globalPendingCountProvider = provider;
|
|
3319
3321
|
}
|
|
3322
|
+
/** 设置 WS 连接数提供者(用于 status_change 通知的在线抑制) */
|
|
3323
|
+
setConnectionCountProvider(provider) {
|
|
3324
|
+
this.connectionCountProvider = provider;
|
|
3325
|
+
}
|
|
3326
|
+
/** 移除指定 WebSocket 关联的所有 push token */
|
|
3327
|
+
removeTokensByWs(ws) {
|
|
3328
|
+
this.expoChannel?.removeTokensByWs(ws);
|
|
3329
|
+
}
|
|
3320
3330
|
/** 获取全局待审批总数 */
|
|
3321
3331
|
getGlobalPendingCount() {
|
|
3322
3332
|
return this.globalPendingCountProvider?.() ?? 0;
|
|
@@ -3459,7 +3469,7 @@ var NotificationService = class {
|
|
|
3459
3469
|
isYoloMode,
|
|
3460
3470
|
updatedAt: Date.now()
|
|
3461
3471
|
});
|
|
3462
|
-
} else {
|
|
3472
|
+
} else if ((this.connectionCountProvider?.() ?? 0) === 0) {
|
|
3463
3473
|
this.notify({
|
|
3464
3474
|
title: sessionTitle,
|
|
3465
3475
|
body,
|
|
@@ -3481,7 +3491,7 @@ var NotificationService = class {
|
|
|
3481
3491
|
isYoloMode,
|
|
3482
3492
|
updatedAt: Date.now()
|
|
3483
3493
|
});
|
|
3484
|
-
} else {
|
|
3494
|
+
} else if ((this.connectionCountProvider?.() ?? 0) === 0) {
|
|
3485
3495
|
this.notify({
|
|
3486
3496
|
title: sessionTitle,
|
|
3487
3497
|
body,
|
|
@@ -3569,6 +3579,17 @@ var ExpoNotificationChannel = class {
|
|
|
3569
3579
|
this.soundPreferences.delete(token);
|
|
3570
3580
|
console.log(`[ExpoNotificationChannel] ${t("notification.tokenRemoved", { count: this.tokens.size })}`);
|
|
3571
3581
|
}
|
|
3582
|
+
/** 移除指定 WebSocket 关联的所有 token(设备断开时调用) */
|
|
3583
|
+
removeTokensByWs(ws) {
|
|
3584
|
+
for (const [token, tokenWs] of this.tokenWsMap) {
|
|
3585
|
+
if (tokenWs === ws) {
|
|
3586
|
+
this.tokens.delete(token);
|
|
3587
|
+
this.tokenWsMap.delete(token);
|
|
3588
|
+
this.soundPreferences.delete(token);
|
|
3589
|
+
console.log(`[ExpoNotificationChannel] Token removed on WS disconnect, remaining: ${this.tokens.size}`);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3572
3593
|
/** 更新某个 token 的音效偏好 */
|
|
3573
3594
|
setSoundPreferences(prefs) {
|
|
3574
3595
|
for (const token of this.tokens) {
|
|
@@ -4415,10 +4436,50 @@ var TerminalExecutor = class {
|
|
|
4415
4436
|
}
|
|
4416
4437
|
};
|
|
4417
4438
|
|
|
4439
|
+
// src/utils/cliCapabilities.ts
|
|
4440
|
+
var import_node_child_process8 = require("child_process");
|
|
4441
|
+
var DEFAULT_CAPABILITIES = {
|
|
4442
|
+
effortLevels: ["low", "medium", "high", "max"]
|
|
4443
|
+
};
|
|
4444
|
+
async function parseCliCapabilities() {
|
|
4445
|
+
const claudePath = findClaudePath();
|
|
4446
|
+
const [helpText, version] = await Promise.all([
|
|
4447
|
+
runCli(claudePath, ["--help"]),
|
|
4448
|
+
runCli(claudePath, ["--version"])
|
|
4449
|
+
]);
|
|
4450
|
+
const capabilities = { ...DEFAULT_CAPABILITIES };
|
|
4451
|
+
if (version) {
|
|
4452
|
+
capabilities.version = version.trim();
|
|
4453
|
+
}
|
|
4454
|
+
if (helpText) {
|
|
4455
|
+
const effortMatch = helpText.match(/--effort\s.*?\(([^)]+)\)/);
|
|
4456
|
+
if (effortMatch) {
|
|
4457
|
+
const levels = effortMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
4458
|
+
if (levels.length > 0) {
|
|
4459
|
+
capabilities.effortLevels = levels;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
console.log(`[CliCapabilities] version=${capabilities.version ?? "unknown"}, effort=[${capabilities.effortLevels.join(", ")}]`);
|
|
4464
|
+
return capabilities;
|
|
4465
|
+
}
|
|
4466
|
+
function runCli(path2, args) {
|
|
4467
|
+
return new Promise((resolve) => {
|
|
4468
|
+
(0, import_node_child_process8.execFile)(path2, args, { timeout: 5e3 }, (err, stdout) => {
|
|
4469
|
+
if (err) {
|
|
4470
|
+
console.warn(`[CliCapabilities] Failed to run ${path2} ${args.join(" ")}:`, err.message);
|
|
4471
|
+
resolve(null);
|
|
4472
|
+
} else {
|
|
4473
|
+
resolve(stdout);
|
|
4474
|
+
}
|
|
4475
|
+
});
|
|
4476
|
+
});
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4418
4479
|
// src/server.ts
|
|
4419
4480
|
var WS_PORT = 3745;
|
|
4420
4481
|
var HTTP_PORT = 3746;
|
|
4421
|
-
var execAsync = (0, import_node_util.promisify)(
|
|
4482
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process9.exec);
|
|
4422
4483
|
async function killPortProcess(port) {
|
|
4423
4484
|
try {
|
|
4424
4485
|
if (isWindows) {
|
|
@@ -4540,6 +4601,12 @@ async function start(opts = {}) {
|
|
|
4540
4601
|
notificationService.setGlobalPendingCountProvider(
|
|
4541
4602
|
() => approvalProxy.getPendingCount() + sessionManager.getAllPendingQuestions().length + unreadSessionIds.size
|
|
4542
4603
|
);
|
|
4604
|
+
notificationService.setConnectionCountProvider(() => wsBridge.getConnectionCount());
|
|
4605
|
+
let cliCapabilities = null;
|
|
4606
|
+
parseCliCapabilities().then((caps) => {
|
|
4607
|
+
cliCapabilities = caps;
|
|
4608
|
+
}).catch(() => {
|
|
4609
|
+
});
|
|
4543
4610
|
const broadcastUnreadSessions = () => {
|
|
4544
4611
|
wsBridge.broadcast({ type: "unread_sessions", sessionIds: Array.from(unreadSessionIds) });
|
|
4545
4612
|
};
|
|
@@ -4561,6 +4628,9 @@ async function start(opts = {}) {
|
|
|
4561
4628
|
if (unreadSessionIds.size > 0) {
|
|
4562
4629
|
wsBridge.send(ws, { type: "unread_sessions", sessionIds: Array.from(unreadSessionIds) });
|
|
4563
4630
|
}
|
|
4631
|
+
if (cliCapabilities) {
|
|
4632
|
+
wsBridge.send(ws, { type: "cli_info", effortLevels: cliCapabilities.effortLevels, version: cliCapabilities.version });
|
|
4633
|
+
}
|
|
4564
4634
|
});
|
|
4565
4635
|
wsBridge.onClientEvent(async (event, ws) => {
|
|
4566
4636
|
try {
|
|
@@ -4896,7 +4966,8 @@ async function start(opts = {}) {
|
|
|
4896
4966
|
terminalExecutor.onEvent((event) => {
|
|
4897
4967
|
wsBridge.broadcast(event);
|
|
4898
4968
|
});
|
|
4899
|
-
wsBridge.onDisconnect(() => {
|
|
4969
|
+
wsBridge.onDisconnect((ws) => {
|
|
4970
|
+
notificationService.removeTokensByWs(ws);
|
|
4900
4971
|
if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
|
|
4901
4972
|
approvalProxy.approveAll(t("server.phoneDisconnected"));
|
|
4902
4973
|
}
|