sessix-server 0.3.9 → 0.4.1
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 +551 -46
- package/dist/server.js +545 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,10 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
27
|
+
var import_node_os9 = require("os");
|
|
28
28
|
var import_node_fs4 = require("fs");
|
|
29
|
-
var
|
|
30
|
-
var
|
|
29
|
+
var import_node_path8 = require("path");
|
|
30
|
+
var import_node_child_process11 = require("child_process");
|
|
31
31
|
|
|
32
32
|
// src/i18n/locales/zh.ts
|
|
33
33
|
var zh = {
|
|
@@ -302,12 +302,12 @@ function t(key, params) {
|
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
// src/server.ts
|
|
305
|
-
var
|
|
306
|
-
var
|
|
307
|
-
var
|
|
308
|
-
var
|
|
309
|
-
var
|
|
310
|
-
var
|
|
305
|
+
var import_uuid7 = require("uuid");
|
|
306
|
+
var import_promises5 = require("fs/promises");
|
|
307
|
+
var import_node_os8 = require("os");
|
|
308
|
+
var import_node_path7 = require("path");
|
|
309
|
+
var import_node_child_process10 = require("child_process");
|
|
310
|
+
var import_node_util2 = require("util");
|
|
311
311
|
|
|
312
312
|
// src/providers/ProcessProvider.ts
|
|
313
313
|
var import_child_process = require("child_process");
|
|
@@ -1650,7 +1650,14 @@ var SessionManager = class {
|
|
|
1650
1650
|
runningStartedAt = /* @__PURE__ */ new Map();
|
|
1651
1651
|
/** assistant 事件合并缓冲区(30ms 窗口内的 assistant 事件合并为一次发送) */
|
|
1652
1652
|
pendingAssistantEvents = /* @__PURE__ */ new Map();
|
|
1653
|
-
/**
|
|
1653
|
+
/**
|
|
1654
|
+
* 标记哪些会话的缓冲区不能代表完整历史,需要从 JSONL 补全。
|
|
1655
|
+
* 两种情况会被标记:
|
|
1656
|
+
* 1. 缓冲区溢出过 BUFFER_MAX(旧事件被丢弃)
|
|
1657
|
+
* 2. 会话是通过 --resume 启动的(缓冲区只有恢复后的新事件,完整历史在 JSONL 中)
|
|
1658
|
+
* 例如:服务器重启后用户继续聊天,sendMessage 走 resume 路径再次创建会话,
|
|
1659
|
+
* 此时 buffer 只有 system init + 少量新事件,不能用它替换手机端已加载的完整 turns。
|
|
1660
|
+
*/
|
|
1654
1661
|
bufferTruncated = /* @__PURE__ */ new Set();
|
|
1655
1662
|
/** sessionId → projectPath 映射,用于截断时从 JSONL 补全历史 */
|
|
1656
1663
|
sessionProjectPaths = /* @__PURE__ */ new Map();
|
|
@@ -1708,6 +1715,9 @@ var SessionManager = class {
|
|
|
1708
1715
|
this.sessionAgentType.set(session.id, resolvedAgentType);
|
|
1709
1716
|
this.lastBroadcastStatus.set(session.id, session.status);
|
|
1710
1717
|
this.sessionProjectPaths.set(session.id, projectPath);
|
|
1718
|
+
if (resumeSessionId) {
|
|
1719
|
+
this.bufferTruncated.add(session.id);
|
|
1720
|
+
}
|
|
1711
1721
|
this.unsubscribeSession(session.id);
|
|
1712
1722
|
this.subscribeToSession(session.id);
|
|
1713
1723
|
console.log(`[SessionManager] Session created: ${session.id} (project: ${projectPath})`);
|
|
@@ -3126,9 +3136,26 @@ process.stdin.on('end', async () => {
|
|
|
3126
3136
|
signal: AbortSignal.timeout(320000),
|
|
3127
3137
|
})
|
|
3128
3138
|
const data = await res.json()
|
|
3129
|
-
|
|
3139
|
+
const decision = data.decision === 'deny' ? 'deny' : 'allow'
|
|
3140
|
+
const output = {
|
|
3141
|
+
hookSpecificOutput: {
|
|
3142
|
+
hookEventName: 'PreToolUse',
|
|
3143
|
+
permissionDecision: decision,
|
|
3144
|
+
},
|
|
3145
|
+
}
|
|
3146
|
+
if (decision === 'deny' && data.reason) {
|
|
3147
|
+
output.hookSpecificOutput.permissionDecisionReason = String(data.reason)
|
|
3148
|
+
}
|
|
3149
|
+
process.stdout.write(JSON.stringify(output))
|
|
3150
|
+
process.exit(0)
|
|
3130
3151
|
} catch {
|
|
3131
|
-
// Sessix \u670D\u52A1\u5668\u4E0D\u53EF\u7528\uFF0C\u9ED8\u8BA4\u653E\u884C
|
|
3152
|
+
// Sessix \u670D\u52A1\u5668\u4E0D\u53EF\u7528\uFF0C\u9ED8\u8BA4\u653E\u884C\uFF08\u8F93\u51FA\u663E\u5F0F allow\uFF0C\u907F\u514D\u843D\u5230\u7EC8\u7AEF\u63D0\u793A\uFF09
|
|
3153
|
+
process.stdout.write(JSON.stringify({
|
|
3154
|
+
hookSpecificOutput: {
|
|
3155
|
+
hookEventName: 'PreToolUse',
|
|
3156
|
+
permissionDecision: 'allow',
|
|
3157
|
+
},
|
|
3158
|
+
}))
|
|
3132
3159
|
process.exit(0)
|
|
3133
3160
|
}
|
|
3134
3161
|
})
|
|
@@ -3171,15 +3198,19 @@ var HookInstaller = class {
|
|
|
3171
3198
|
console.log("[HookInstaller] Hook uninstalled");
|
|
3172
3199
|
}
|
|
3173
3200
|
/**
|
|
3174
|
-
* 检查 hook
|
|
3175
|
-
*
|
|
3201
|
+
* 检查 hook 是否已安装且为最新版本
|
|
3202
|
+
*
|
|
3203
|
+
* 必须同时满足:
|
|
3204
|
+
* 1. 两个脚本文件都存在
|
|
3205
|
+
* 2. settings.json 中有 Sessix hook 配置
|
|
3206
|
+
* 3. approval-hook.js 包含最新的 permissionDecision 输出协议
|
|
3207
|
+
* (旧版仅用 exit code,会导致 ExitPlanMode 等工具卡住)
|
|
3176
3208
|
*/
|
|
3177
3209
|
async isInstalled() {
|
|
3178
|
-
let
|
|
3210
|
+
let approvalScriptContent = "";
|
|
3179
3211
|
let permissionScriptExists = false;
|
|
3180
3212
|
try {
|
|
3181
|
-
await (0, import_promises2.
|
|
3182
|
-
approvalScriptExists = true;
|
|
3213
|
+
approvalScriptContent = await (0, import_promises2.readFile)(HOOK_SCRIPT_PATH, "utf-8");
|
|
3183
3214
|
} catch {
|
|
3184
3215
|
}
|
|
3185
3216
|
try {
|
|
@@ -3187,9 +3218,10 @@ var HookInstaller = class {
|
|
|
3187
3218
|
permissionScriptExists = true;
|
|
3188
3219
|
} catch {
|
|
3189
3220
|
}
|
|
3221
|
+
const isLatestVersion = approvalScriptContent.includes("permissionDecision");
|
|
3190
3222
|
const settings = await this.readClaudeSettings();
|
|
3191
3223
|
const configExists = this.hasHookConfig(settings);
|
|
3192
|
-
return
|
|
3224
|
+
return isLatestVersion && permissionScriptExists && configExists;
|
|
3193
3225
|
}
|
|
3194
3226
|
// ============================================
|
|
3195
3227
|
// 内部方法
|
|
@@ -4363,7 +4395,7 @@ var AuthManager = class extends import_events3.EventEmitter {
|
|
|
4363
4395
|
};
|
|
4364
4396
|
|
|
4365
4397
|
// src/server.ts
|
|
4366
|
-
var
|
|
4398
|
+
var import_promises6 = require("fs/promises");
|
|
4367
4399
|
|
|
4368
4400
|
// src/terminal/TerminalExecutor.ts
|
|
4369
4401
|
var import_node_child_process7 = require("child_process");
|
|
@@ -4452,8 +4484,422 @@ var TerminalExecutor = class {
|
|
|
4452
4484
|
}
|
|
4453
4485
|
};
|
|
4454
4486
|
|
|
4455
|
-
// src/
|
|
4487
|
+
// src/xcode/XcodeBuildExecutor.ts
|
|
4456
4488
|
var import_node_child_process8 = require("child_process");
|
|
4489
|
+
var import_node_util = require("util");
|
|
4490
|
+
var import_promises4 = require("fs/promises");
|
|
4491
|
+
var import_node_path6 = require("path");
|
|
4492
|
+
var import_node_os7 = require("os");
|
|
4493
|
+
var import_uuid6 = require("uuid");
|
|
4494
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process8.exec);
|
|
4495
|
+
var BUILD_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4496
|
+
var INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4497
|
+
var CONFIG_FILE = (0, import_node_path6.join)((0, import_node_os7.homedir)(), ".sessix", "xcode-config.json");
|
|
4498
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
4499
|
+
"node_modules",
|
|
4500
|
+
".git",
|
|
4501
|
+
"DerivedData",
|
|
4502
|
+
"Pods",
|
|
4503
|
+
".build",
|
|
4504
|
+
"build",
|
|
4505
|
+
"dist",
|
|
4506
|
+
"__pycache__",
|
|
4507
|
+
".next",
|
|
4508
|
+
"vendor",
|
|
4509
|
+
".expo",
|
|
4510
|
+
"android",
|
|
4511
|
+
".gradle",
|
|
4512
|
+
"Carthage",
|
|
4513
|
+
"xcarchive"
|
|
4514
|
+
]);
|
|
4515
|
+
var MAX_SCAN_DEPTH = 4;
|
|
4516
|
+
var XcodeBuildExecutor = class {
|
|
4517
|
+
builds = /* @__PURE__ */ new Map();
|
|
4518
|
+
installs = /* @__PURE__ */ new Map();
|
|
4519
|
+
eventCallbacks = [];
|
|
4520
|
+
configCache = null;
|
|
4521
|
+
onEvent(callback) {
|
|
4522
|
+
this.eventCallbacks.push(callback);
|
|
4523
|
+
return () => {
|
|
4524
|
+
const idx = this.eventCallbacks.indexOf(callback);
|
|
4525
|
+
if (idx !== -1) this.eventCallbacks.splice(idx, 1);
|
|
4526
|
+
};
|
|
4527
|
+
}
|
|
4528
|
+
emit(event) {
|
|
4529
|
+
for (const cb of this.eventCallbacks) {
|
|
4530
|
+
try {
|
|
4531
|
+
cb(event);
|
|
4532
|
+
} catch (err) {
|
|
4533
|
+
console.error("[XcodeBuildExecutor] Event callback error:", err);
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
// ============================================
|
|
4538
|
+
// 配置持久化
|
|
4539
|
+
// ============================================
|
|
4540
|
+
async loadConfigs() {
|
|
4541
|
+
if (this.configCache) return this.configCache;
|
|
4542
|
+
try {
|
|
4543
|
+
const raw = await (0, import_promises4.readFile)(CONFIG_FILE, "utf8");
|
|
4544
|
+
this.configCache = JSON.parse(raw);
|
|
4545
|
+
} catch {
|
|
4546
|
+
this.configCache = {};
|
|
4547
|
+
}
|
|
4548
|
+
return this.configCache;
|
|
4549
|
+
}
|
|
4550
|
+
async writeConfigs(store) {
|
|
4551
|
+
await (0, import_promises4.mkdir)((0, import_node_path6.join)((0, import_node_os7.homedir)(), ".sessix"), { recursive: true });
|
|
4552
|
+
await (0, import_promises4.writeFile)(CONFIG_FILE, JSON.stringify(store, null, 2), "utf8");
|
|
4553
|
+
this.configCache = store;
|
|
4554
|
+
}
|
|
4555
|
+
async getSavedConfig(projectPath) {
|
|
4556
|
+
const store = await this.loadConfigs();
|
|
4557
|
+
return store[projectPath];
|
|
4558
|
+
}
|
|
4559
|
+
async saveConfig(projectPath, config) {
|
|
4560
|
+
const store = await this.loadConfigs();
|
|
4561
|
+
store[projectPath] = config;
|
|
4562
|
+
await this.writeConfigs(store);
|
|
4563
|
+
}
|
|
4564
|
+
// ============================================
|
|
4565
|
+
// 递归扫描 Xcode 工程
|
|
4566
|
+
// ============================================
|
|
4567
|
+
async findAllContainers(projectPath) {
|
|
4568
|
+
const results = [];
|
|
4569
|
+
await this.scanDir(projectPath, projectPath, 0, results);
|
|
4570
|
+
results.sort((a, b) => a.path.split("/").length - b.path.split("/").length);
|
|
4571
|
+
return results;
|
|
4572
|
+
}
|
|
4573
|
+
async scanDir(rootPath, currentPath, depth, results) {
|
|
4574
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
4575
|
+
let entries;
|
|
4576
|
+
try {
|
|
4577
|
+
entries = await (0, import_promises4.readdir)(currentPath);
|
|
4578
|
+
} catch {
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
let foundWorkspace;
|
|
4582
|
+
let foundProject;
|
|
4583
|
+
for (const name of entries) {
|
|
4584
|
+
if (name.endsWith(".xcworkspace") && !name.endsWith("project.xcworkspace")) {
|
|
4585
|
+
foundWorkspace = foundWorkspace ?? name;
|
|
4586
|
+
} else if (name.endsWith(".xcodeproj")) {
|
|
4587
|
+
foundProject = foundProject ?? name;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
if (foundWorkspace || foundProject) {
|
|
4591
|
+
const relDir = currentPath === rootPath ? "" : currentPath.slice(rootPath.length + 1);
|
|
4592
|
+
const containerName = foundWorkspace ? foundWorkspace.replace(/\.xcworkspace$/, "") : foundProject.replace(/\.xcodeproj$/, "");
|
|
4593
|
+
results.push({
|
|
4594
|
+
path: relDir,
|
|
4595
|
+
name: containerName,
|
|
4596
|
+
workspace: foundWorkspace ? relDir ? `${relDir}/${foundWorkspace}` : foundWorkspace : void 0,
|
|
4597
|
+
project: foundProject ? relDir ? `${relDir}/${foundProject}` : foundProject : void 0
|
|
4598
|
+
});
|
|
4599
|
+
}
|
|
4600
|
+
for (const name of entries) {
|
|
4601
|
+
if (SKIP_DIRS.has(name)) continue;
|
|
4602
|
+
if (name.startsWith(".")) continue;
|
|
4603
|
+
if (name.endsWith(".xcodeproj") || name.endsWith(".xcworkspace")) continue;
|
|
4604
|
+
const childPath = (0, import_node_path6.join)(currentPath, name);
|
|
4605
|
+
await this.scanDir(rootPath, childPath, depth + 1, results);
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
// ============================================
|
|
4609
|
+
// 探测 schemes(针对指定 container)
|
|
4610
|
+
// ============================================
|
|
4611
|
+
async detect(projectPath) {
|
|
4612
|
+
if (process.platform !== "darwin") {
|
|
4613
|
+
return { available: false, error: "Xcode \u6784\u5EFA\u53EA\u652F\u6301 macOS", containers: [], schemes: [] };
|
|
4614
|
+
}
|
|
4615
|
+
const containers = await this.findAllContainers(projectPath);
|
|
4616
|
+
if (containers.length === 0) {
|
|
4617
|
+
return { available: false, error: "\u672A\u627E\u5230 .xcworkspace \u6216 .xcodeproj", containers: [], schemes: [] };
|
|
4618
|
+
}
|
|
4619
|
+
const firstContainer = containers[0];
|
|
4620
|
+
const schemes = await this.getSchemesForContainer(projectPath, firstContainer);
|
|
4621
|
+
const saved = await this.getSavedConfig(projectPath);
|
|
4622
|
+
return {
|
|
4623
|
+
available: true,
|
|
4624
|
+
containers,
|
|
4625
|
+
schemes,
|
|
4626
|
+
saved
|
|
4627
|
+
};
|
|
4628
|
+
}
|
|
4629
|
+
async getSchemesForContainer(projectPath, container) {
|
|
4630
|
+
const args = container.workspace ? ["-workspace", container.workspace, "-list", "-json"] : ["-project", container.project, "-list", "-json"];
|
|
4631
|
+
try {
|
|
4632
|
+
const { stdout } = await execAsync(
|
|
4633
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4634
|
+
{ cwd: projectPath, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4635
|
+
);
|
|
4636
|
+
const parsed = JSON.parse(stdout);
|
|
4637
|
+
return parsed.workspace?.schemes ?? parsed.project?.schemes ?? [];
|
|
4638
|
+
} catch {
|
|
4639
|
+
return [];
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
// ============================================
|
|
4643
|
+
// 列举 destinations
|
|
4644
|
+
// ============================================
|
|
4645
|
+
async listDestinations(projectPath, scheme, container) {
|
|
4646
|
+
if (process.platform !== "darwin") return [];
|
|
4647
|
+
const args = [
|
|
4648
|
+
...container.workspace ? ["-workspace", container.workspace] : ["-project", container.project],
|
|
4649
|
+
"-scheme",
|
|
4650
|
+
scheme,
|
|
4651
|
+
"-showdestinations"
|
|
4652
|
+
];
|
|
4653
|
+
try {
|
|
4654
|
+
const { stdout, stderr } = await execAsync(
|
|
4655
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4656
|
+
{ cwd: projectPath, timeout: 6e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4657
|
+
);
|
|
4658
|
+
return parseDestinations(stdout + "\n" + stderr);
|
|
4659
|
+
} catch (err) {
|
|
4660
|
+
const e = err;
|
|
4661
|
+
const parsed = parseDestinations(`${e.stdout ?? ""}
|
|
4662
|
+
${e.stderr ?? ""}`);
|
|
4663
|
+
if (parsed.length > 0) return parsed;
|
|
4664
|
+
throw err;
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
// ============================================
|
|
4668
|
+
// 构建
|
|
4669
|
+
// ============================================
|
|
4670
|
+
async build(sessionId, projectPath, override) {
|
|
4671
|
+
if (process.platform !== "darwin") {
|
|
4672
|
+
this.emitBuildError(sessionId, "", "Xcode \u6784\u5EFA\u4EC5\u652F\u6301 macOS\n");
|
|
4673
|
+
return null;
|
|
4674
|
+
}
|
|
4675
|
+
const config = override ?? await this.getSavedConfig(projectPath);
|
|
4676
|
+
if (!config) {
|
|
4677
|
+
this.emitBuildError(sessionId, "", "\u672A\u914D\u7F6E Xcode \u6784\u5EFA\u53C2\u6570\uFF0C\u8BF7\u5148\u9009\u62E9 scheme \u4E0E\u76EE\u6807\u8BBE\u5907\n");
|
|
4678
|
+
return null;
|
|
4679
|
+
}
|
|
4680
|
+
if (override) await this.saveConfig(projectPath, override);
|
|
4681
|
+
const buildId = (0, import_uuid6.v4)();
|
|
4682
|
+
const args = buildArgs(config);
|
|
4683
|
+
const proc = (0, import_node_child_process8.spawn)("xcodebuild", args, {
|
|
4684
|
+
cwd: projectPath,
|
|
4685
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4686
|
+
env: { ...process.env, NSUnbufferedIO: "YES" }
|
|
4687
|
+
});
|
|
4688
|
+
this.builds.set(buildId, proc);
|
|
4689
|
+
this.emit({
|
|
4690
|
+
type: "xcode_build_output",
|
|
4691
|
+
sessionId,
|
|
4692
|
+
buildId,
|
|
4693
|
+
stream: "stdout",
|
|
4694
|
+
data: `$ xcodebuild ${args.map((a) => a.includes(" ") ? `"${a}"` : a).join(" ")}
|
|
4695
|
+
cwd: ${projectPath}
|
|
4696
|
+
destination: ${config.destinationName}
|
|
4697
|
+
|
|
4698
|
+
`
|
|
4699
|
+
});
|
|
4700
|
+
proc.stdout?.on("data", (chunk) => {
|
|
4701
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stdout", data: chunk.toString() });
|
|
4702
|
+
});
|
|
4703
|
+
proc.stderr?.on("data", (chunk) => {
|
|
4704
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: chunk.toString() });
|
|
4705
|
+
});
|
|
4706
|
+
proc.on("error", (err) => {
|
|
4707
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: `[spawn error] ${err.message}
|
|
4708
|
+
` });
|
|
4709
|
+
});
|
|
4710
|
+
const timer = setTimeout(() => {
|
|
4711
|
+
if (this.builds.has(buildId)) killProcessCrossPlatform(proc);
|
|
4712
|
+
}, BUILD_TIMEOUT_MS);
|
|
4713
|
+
proc.on("exit", (code, signal) => {
|
|
4714
|
+
clearTimeout(timer);
|
|
4715
|
+
this.builds.delete(buildId);
|
|
4716
|
+
this.emit({ type: "xcode_build_exit", sessionId, buildId, code, signal });
|
|
4717
|
+
});
|
|
4718
|
+
console.log(`[XcodeBuildExecutor] build ${buildId} scheme=${config.scheme} dest=${config.destinationName}`);
|
|
4719
|
+
return buildId;
|
|
4720
|
+
}
|
|
4721
|
+
killBuild(buildId) {
|
|
4722
|
+
const proc = this.builds.get(buildId);
|
|
4723
|
+
if (proc) {
|
|
4724
|
+
killProcessCrossPlatform(proc);
|
|
4725
|
+
console.log(`[XcodeBuildExecutor] kill build ${buildId}`);
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
emitBuildError(sessionId, buildId, msg) {
|
|
4729
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: msg });
|
|
4730
|
+
this.emit({ type: "xcode_build_exit", sessionId, buildId, code: 1, signal: null });
|
|
4731
|
+
}
|
|
4732
|
+
// ============================================
|
|
4733
|
+
// 安装
|
|
4734
|
+
// ============================================
|
|
4735
|
+
async install(sessionId, projectPath) {
|
|
4736
|
+
if (process.platform !== "darwin") {
|
|
4737
|
+
this.emitInstallError(sessionId, "", "Xcode \u5B89\u88C5\u4EC5\u652F\u6301 macOS\n");
|
|
4738
|
+
return null;
|
|
4739
|
+
}
|
|
4740
|
+
const config = await this.getSavedConfig(projectPath);
|
|
4741
|
+
if (!config) {
|
|
4742
|
+
this.emitInstallError(sessionId, "", "\u672A\u627E\u5230\u6784\u5EFA\u914D\u7F6E\uFF0C\u8BF7\u5148\u6784\u5EFA\u4E00\u6B21\n");
|
|
4743
|
+
return null;
|
|
4744
|
+
}
|
|
4745
|
+
const installId = (0, import_uuid6.v4)();
|
|
4746
|
+
let appPath;
|
|
4747
|
+
try {
|
|
4748
|
+
appPath = await this.getAppPath(projectPath, config);
|
|
4749
|
+
} catch (err) {
|
|
4750
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4751
|
+
this.emitInstallError(sessionId, installId, `\u65E0\u6CD5\u5B9A\u4F4D\u6784\u5EFA\u4EA7\u7269: ${msg}
|
|
4752
|
+
`);
|
|
4753
|
+
return null;
|
|
4754
|
+
}
|
|
4755
|
+
const { destinationKind, destinationId, destinationName } = config;
|
|
4756
|
+
let installCmd;
|
|
4757
|
+
if (destinationKind === "simulator") {
|
|
4758
|
+
installCmd = ["xcrun", "simctl", "install", destinationId, appPath];
|
|
4759
|
+
} else if (destinationKind === "device") {
|
|
4760
|
+
installCmd = ["xcrun", "devicectl", "device", "install", "app", "--device", destinationId, appPath];
|
|
4761
|
+
} else if (destinationKind === "mac") {
|
|
4762
|
+
installCmd = ["open", appPath];
|
|
4763
|
+
} else {
|
|
4764
|
+
this.emitInstallError(sessionId, installId, `\u672A\u77E5\u76EE\u6807\u7C7B\u578B\uFF0C\u65E0\u6CD5\u81EA\u52A8\u5B89\u88C5
|
|
4765
|
+
`);
|
|
4766
|
+
return null;
|
|
4767
|
+
}
|
|
4768
|
+
this.emit({
|
|
4769
|
+
type: "xcode_install_output",
|
|
4770
|
+
sessionId,
|
|
4771
|
+
installId,
|
|
4772
|
+
stream: "stdout",
|
|
4773
|
+
data: `$ ${installCmd.join(" ")}
|
|
4774
|
+
destination: ${destinationName}
|
|
4775
|
+
app: ${appPath}
|
|
4776
|
+
|
|
4777
|
+
`
|
|
4778
|
+
});
|
|
4779
|
+
const proc = (0, import_node_child_process8.spawn)(installCmd[0], installCmd.slice(1), {
|
|
4780
|
+
cwd: projectPath,
|
|
4781
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4782
|
+
});
|
|
4783
|
+
this.installs.set(installId, proc);
|
|
4784
|
+
proc.stdout?.on("data", (chunk) => {
|
|
4785
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stdout", data: chunk.toString() });
|
|
4786
|
+
});
|
|
4787
|
+
proc.stderr?.on("data", (chunk) => {
|
|
4788
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: chunk.toString() });
|
|
4789
|
+
});
|
|
4790
|
+
proc.on("error", (err) => {
|
|
4791
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: `[spawn error] ${err.message}
|
|
4792
|
+
` });
|
|
4793
|
+
});
|
|
4794
|
+
const timer = setTimeout(() => {
|
|
4795
|
+
if (this.installs.has(installId)) killProcessCrossPlatform(proc);
|
|
4796
|
+
}, INSTALL_TIMEOUT_MS);
|
|
4797
|
+
proc.on("exit", (code, signal) => {
|
|
4798
|
+
clearTimeout(timer);
|
|
4799
|
+
this.installs.delete(installId);
|
|
4800
|
+
this.emit({ type: "xcode_install_exit", sessionId, installId, code, signal });
|
|
4801
|
+
});
|
|
4802
|
+
console.log(`[XcodeBuildExecutor] install ${installId} dest=${destinationName} kind=${destinationKind}`);
|
|
4803
|
+
return installId;
|
|
4804
|
+
}
|
|
4805
|
+
killInstall(installId) {
|
|
4806
|
+
const proc = this.installs.get(installId);
|
|
4807
|
+
if (proc) {
|
|
4808
|
+
killProcessCrossPlatform(proc);
|
|
4809
|
+
console.log(`[XcodeBuildExecutor] kill install ${installId}`);
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
emitInstallError(sessionId, installId, msg) {
|
|
4813
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: msg });
|
|
4814
|
+
this.emit({ type: "xcode_install_exit", sessionId, installId, code: 1, signal: null });
|
|
4815
|
+
}
|
|
4816
|
+
/** 通过 xcodebuild -showBuildSettings 定位 .app 路径 */
|
|
4817
|
+
async getAppPath(projectPath, config) {
|
|
4818
|
+
const args = [
|
|
4819
|
+
...buildArgs(config).filter((a) => a !== "build"),
|
|
4820
|
+
"-showBuildSettings"
|
|
4821
|
+
];
|
|
4822
|
+
const { stdout } = await execAsync(
|
|
4823
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4824
|
+
{ cwd: projectPath, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4825
|
+
);
|
|
4826
|
+
const builtDir = extractBuildSetting(stdout, "BUILT_PRODUCTS_DIR");
|
|
4827
|
+
const productName = extractBuildSetting(stdout, "FULL_PRODUCT_NAME");
|
|
4828
|
+
if (!builtDir || !productName) {
|
|
4829
|
+
throw new Error("\u65E0\u6CD5\u4ECE -showBuildSettings \u4E2D\u8BFB\u53D6 BUILT_PRODUCTS_DIR / FULL_PRODUCT_NAME");
|
|
4830
|
+
}
|
|
4831
|
+
return (0, import_node_path6.join)(builtDir, productName);
|
|
4832
|
+
}
|
|
4833
|
+
// ============================================
|
|
4834
|
+
// 清理
|
|
4835
|
+
// ============================================
|
|
4836
|
+
destroy() {
|
|
4837
|
+
for (const [id, proc] of this.builds) {
|
|
4838
|
+
killProcessCrossPlatform(proc);
|
|
4839
|
+
console.log(`[XcodeBuildExecutor] cleanup build ${id}`);
|
|
4840
|
+
}
|
|
4841
|
+
for (const [id, proc] of this.installs) {
|
|
4842
|
+
killProcessCrossPlatform(proc);
|
|
4843
|
+
console.log(`[XcodeBuildExecutor] cleanup install ${id}`);
|
|
4844
|
+
}
|
|
4845
|
+
this.builds.clear();
|
|
4846
|
+
this.installs.clear();
|
|
4847
|
+
this.eventCallbacks.length = 0;
|
|
4848
|
+
}
|
|
4849
|
+
};
|
|
4850
|
+
function shellQuote(s) {
|
|
4851
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
4852
|
+
}
|
|
4853
|
+
function buildArgs(config) {
|
|
4854
|
+
const container = config.workspace ? ["-workspace", config.workspace] : config.project ? ["-project", config.project] : [];
|
|
4855
|
+
return [
|
|
4856
|
+
...container,
|
|
4857
|
+
"-scheme",
|
|
4858
|
+
config.scheme,
|
|
4859
|
+
"-destination",
|
|
4860
|
+
`id=${config.destinationId}`,
|
|
4861
|
+
"-configuration",
|
|
4862
|
+
config.configuration ?? "Debug",
|
|
4863
|
+
"build"
|
|
4864
|
+
];
|
|
4865
|
+
}
|
|
4866
|
+
function extractBuildSetting(output, key) {
|
|
4867
|
+
const match = new RegExp(`^\\s*${key}\\s*=\\s*(.+)$`, "m").exec(output);
|
|
4868
|
+
return match?.[1]?.trim();
|
|
4869
|
+
}
|
|
4870
|
+
function parseDestinations(text) {
|
|
4871
|
+
const results = [];
|
|
4872
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4873
|
+
const lineRegex = /\{\s*([^{}]+)\s*\}/g;
|
|
4874
|
+
let match;
|
|
4875
|
+
while ((match = lineRegex.exec(text)) !== null) {
|
|
4876
|
+
const fields = {};
|
|
4877
|
+
for (const part of match[1].split(",")) {
|
|
4878
|
+
const colon = part.indexOf(":");
|
|
4879
|
+
if (colon === -1) continue;
|
|
4880
|
+
const key = part.slice(0, colon).trim();
|
|
4881
|
+
const value = part.slice(colon + 1).trim();
|
|
4882
|
+
if (key) fields[key] = value;
|
|
4883
|
+
}
|
|
4884
|
+
const { id, name, platform: platform2 } = fields;
|
|
4885
|
+
if (!id || !name || !platform2) continue;
|
|
4886
|
+
if (seen.has(id)) continue;
|
|
4887
|
+
seen.add(id);
|
|
4888
|
+
let kind = "unknown";
|
|
4889
|
+
if (platform2.includes("Simulator")) kind = "simulator";
|
|
4890
|
+
else if (platform2 === "iOS" || platform2 === "watchOS" || platform2 === "tvOS" || platform2 === "visionOS") kind = "device";
|
|
4891
|
+
else if (platform2 === "macOS") kind = "mac";
|
|
4892
|
+
results.push({ id, name, platform: platform2, os: fields.OS, kind });
|
|
4893
|
+
}
|
|
4894
|
+
results.sort((a, b) => kindOrder(a.kind) - kindOrder(b.kind));
|
|
4895
|
+
return results;
|
|
4896
|
+
}
|
|
4897
|
+
function kindOrder(k) {
|
|
4898
|
+
return k === "device" ? 0 : k === "simulator" ? 1 : k === "mac" ? 2 : 3;
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
// src/utils/cliCapabilities.ts
|
|
4902
|
+
var import_node_child_process9 = require("child_process");
|
|
4457
4903
|
var DEFAULT_CAPABILITIES = {
|
|
4458
4904
|
effortLevels: ["low", "medium", "high", "xhigh", "max"]
|
|
4459
4905
|
};
|
|
@@ -4481,7 +4927,7 @@ async function parseCliCapabilities() {
|
|
|
4481
4927
|
}
|
|
4482
4928
|
function runCli(path2, args) {
|
|
4483
4929
|
return new Promise((resolve) => {
|
|
4484
|
-
(0,
|
|
4930
|
+
(0, import_node_child_process9.execFile)(path2, args, { timeout: 5e3 }, (err, stdout) => {
|
|
4485
4931
|
if (err) {
|
|
4486
4932
|
console.warn(`[CliCapabilities] Failed to run ${path2} ${args.join(" ")}:`, err.message);
|
|
4487
4933
|
resolve(null);
|
|
@@ -4495,11 +4941,11 @@ function runCli(path2, args) {
|
|
|
4495
4941
|
// src/server.ts
|
|
4496
4942
|
var WS_PORT = 3745;
|
|
4497
4943
|
var HTTP_PORT = 3746;
|
|
4498
|
-
var
|
|
4944
|
+
var execAsync2 = (0, import_node_util2.promisify)(import_node_child_process10.exec);
|
|
4499
4945
|
async function killPortProcess(port) {
|
|
4500
4946
|
try {
|
|
4501
4947
|
if (isWindows) {
|
|
4502
|
-
const { stdout } = await
|
|
4948
|
+
const { stdout } = await execAsync2(
|
|
4503
4949
|
`netstat -ano | findstr :${port} | findstr LISTENING`
|
|
4504
4950
|
);
|
|
4505
4951
|
const pids = /* @__PURE__ */ new Set();
|
|
@@ -4509,14 +4955,14 @@ async function killPortProcess(port) {
|
|
|
4509
4955
|
if (pid && /^\d+$/.test(pid) && pid !== "0") pids.add(pid);
|
|
4510
4956
|
}
|
|
4511
4957
|
for (const pid of pids) {
|
|
4512
|
-
await
|
|
4958
|
+
await execAsync2(`taskkill /PID ${pid} /F`).catch(() => {
|
|
4513
4959
|
});
|
|
4514
4960
|
}
|
|
4515
4961
|
} else {
|
|
4516
|
-
const { stdout } = await
|
|
4962
|
+
const { stdout } = await execAsync2(`lsof -ti :${port}`);
|
|
4517
4963
|
const pids = stdout.trim().split("\n").filter((p) => p && /^\d+$/.test(p));
|
|
4518
4964
|
if (pids.length > 0) {
|
|
4519
|
-
await
|
|
4965
|
+
await execAsync2(`kill -9 ${pids.join(" ")}`);
|
|
4520
4966
|
}
|
|
4521
4967
|
}
|
|
4522
4968
|
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
@@ -4537,8 +4983,8 @@ async function createWithRetry(label, port, factory) {
|
|
|
4537
4983
|
}
|
|
4538
4984
|
}
|
|
4539
4985
|
async function start(opts = {}) {
|
|
4540
|
-
const configDir = (0,
|
|
4541
|
-
const tokenFile = (0,
|
|
4986
|
+
const configDir = (0, import_node_path7.join)((0, import_node_os8.homedir)(), ".sessix");
|
|
4987
|
+
const tokenFile = (0, import_node_path7.join)(configDir, "token");
|
|
4542
4988
|
let token;
|
|
4543
4989
|
if (opts.token !== void 0) {
|
|
4544
4990
|
token = opts.token;
|
|
@@ -4548,17 +4994,23 @@ async function start(opts = {}) {
|
|
|
4548
4994
|
token = envToken;
|
|
4549
4995
|
} else {
|
|
4550
4996
|
try {
|
|
4551
|
-
token = (await (0,
|
|
4997
|
+
token = (await (0, import_promises5.readFile)(tokenFile, "utf8")).trim();
|
|
4552
4998
|
} catch {
|
|
4553
|
-
token = (0,
|
|
4554
|
-
await (0,
|
|
4555
|
-
await (0,
|
|
4999
|
+
token = (0, import_uuid7.v4)();
|
|
5000
|
+
await (0, import_promises5.mkdir)(configDir, { recursive: true });
|
|
5001
|
+
await (0, import_promises5.writeFile)(tokenFile, token, "utf8");
|
|
4556
5002
|
}
|
|
4557
5003
|
}
|
|
4558
5004
|
}
|
|
4559
5005
|
const providerFactory = new ProviderFactory();
|
|
4560
5006
|
const sessionManager = new SessionManager(providerFactory);
|
|
4561
5007
|
const terminalExecutor = new TerminalExecutor();
|
|
5008
|
+
const xcodeBuildExecutor = new XcodeBuildExecutor();
|
|
5009
|
+
const approvalProxy = await createWithRetry(
|
|
5010
|
+
"ApprovalProxy",
|
|
5011
|
+
HTTP_PORT,
|
|
5012
|
+
() => ApprovalProxy.create({ port: HTTP_PORT, token })
|
|
5013
|
+
);
|
|
4562
5014
|
const wsBridge = await createWithRetry(
|
|
4563
5015
|
"WsBridge",
|
|
4564
5016
|
WS_PORT,
|
|
@@ -4589,15 +5041,10 @@ async function start(opts = {}) {
|
|
|
4589
5041
|
const sessionFileWatcher = new SessionFileWatcher((event) => {
|
|
4590
5042
|
wsBridge.broadcast(event);
|
|
4591
5043
|
});
|
|
4592
|
-
const approvalProxy = await createWithRetry(
|
|
4593
|
-
"ApprovalProxy",
|
|
4594
|
-
HTTP_PORT,
|
|
4595
|
-
() => ApprovalProxy.create({ port: HTTP_PORT, token })
|
|
4596
|
-
);
|
|
4597
5044
|
let mdnsService = null;
|
|
4598
5045
|
const pairingManager = new PairingManager({
|
|
4599
5046
|
token,
|
|
4600
|
-
serverName: (0,
|
|
5047
|
+
serverName: (0, import_node_os8.hostname)(),
|
|
4601
5048
|
version: "0.2.0",
|
|
4602
5049
|
onStateChange: (state) => mdnsService?.updatePairingState(state)
|
|
4603
5050
|
});
|
|
@@ -4651,7 +5098,7 @@ async function start(opts = {}) {
|
|
|
4651
5098
|
try {
|
|
4652
5099
|
switch (event.type) {
|
|
4653
5100
|
case "create_session": {
|
|
4654
|
-
await (0,
|
|
5101
|
+
await (0, import_promises5.mkdir)(event.projectPath, { recursive: true });
|
|
4655
5102
|
const resumeId = event.resumeSessionId ?? event.newSessionId;
|
|
4656
5103
|
if (resumeId) sessionFileWatcher.unwatch(resumeId);
|
|
4657
5104
|
await sessionManager.createSession(
|
|
@@ -4839,7 +5286,7 @@ async function start(opts = {}) {
|
|
|
4839
5286
|
if (!isStreaming) {
|
|
4840
5287
|
const filePath = getSessionFilePath(event.projectPath, event.sessionId);
|
|
4841
5288
|
try {
|
|
4842
|
-
const fileStat = await (0,
|
|
5289
|
+
const fileStat = await (0, import_promises6.stat)(filePath);
|
|
4843
5290
|
sessionFileWatcher.watch(event.sessionId, filePath, fileStat.size);
|
|
4844
5291
|
} catch {
|
|
4845
5292
|
}
|
|
@@ -4948,6 +5395,60 @@ async function start(opts = {}) {
|
|
|
4948
5395
|
wsBridge.send(ws, { type: "agent_list", agents });
|
|
4949
5396
|
break;
|
|
4950
5397
|
}
|
|
5398
|
+
case "xcode_detect": {
|
|
5399
|
+
const info = await xcodeBuildExecutor.detect(event.projectPath);
|
|
5400
|
+
wsBridge.send(ws, { type: "xcode_info", sessionId: event.sessionId, info });
|
|
5401
|
+
break;
|
|
5402
|
+
}
|
|
5403
|
+
case "xcode_list_schemes": {
|
|
5404
|
+
const schemes = await xcodeBuildExecutor.getSchemesForContainer(event.projectPath, event.container);
|
|
5405
|
+
wsBridge.send(ws, {
|
|
5406
|
+
type: "xcode_info",
|
|
5407
|
+
sessionId: event.sessionId,
|
|
5408
|
+
info: {
|
|
5409
|
+
available: schemes.length > 0,
|
|
5410
|
+
containers: [event.container],
|
|
5411
|
+
schemes
|
|
5412
|
+
}
|
|
5413
|
+
});
|
|
5414
|
+
break;
|
|
5415
|
+
}
|
|
5416
|
+
case "xcode_list_destinations": {
|
|
5417
|
+
try {
|
|
5418
|
+
const destinations = await xcodeBuildExecutor.listDestinations(event.projectPath, event.scheme, event.container);
|
|
5419
|
+
wsBridge.send(ws, { type: "xcode_destinations", sessionId: event.sessionId, scheme: event.scheme, destinations });
|
|
5420
|
+
} catch (err) {
|
|
5421
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5422
|
+
wsBridge.send(ws, {
|
|
5423
|
+
type: "xcode_destinations",
|
|
5424
|
+
sessionId: event.sessionId,
|
|
5425
|
+
scheme: event.scheme,
|
|
5426
|
+
destinations: [],
|
|
5427
|
+
error: message.split("\n")[0]
|
|
5428
|
+
});
|
|
5429
|
+
}
|
|
5430
|
+
break;
|
|
5431
|
+
}
|
|
5432
|
+
case "xcode_save_config": {
|
|
5433
|
+
await xcodeBuildExecutor.saveConfig(event.projectPath, event.config);
|
|
5434
|
+
break;
|
|
5435
|
+
}
|
|
5436
|
+
case "xcode_build": {
|
|
5437
|
+
await xcodeBuildExecutor.build(event.sessionId, event.projectPath, event.config);
|
|
5438
|
+
break;
|
|
5439
|
+
}
|
|
5440
|
+
case "xcode_build_kill": {
|
|
5441
|
+
xcodeBuildExecutor.killBuild(event.buildId);
|
|
5442
|
+
break;
|
|
5443
|
+
}
|
|
5444
|
+
case "xcode_install": {
|
|
5445
|
+
await xcodeBuildExecutor.install(event.sessionId, event.projectPath);
|
|
5446
|
+
break;
|
|
5447
|
+
}
|
|
5448
|
+
case "xcode_install_kill": {
|
|
5449
|
+
xcodeBuildExecutor.killInstall(event.installId);
|
|
5450
|
+
break;
|
|
5451
|
+
}
|
|
4951
5452
|
default: {
|
|
4952
5453
|
wsBridge.send(ws, {
|
|
4953
5454
|
type: "error",
|
|
@@ -4983,6 +5484,9 @@ async function start(opts = {}) {
|
|
|
4983
5484
|
terminalExecutor.onEvent((event) => {
|
|
4984
5485
|
wsBridge.broadcast(event);
|
|
4985
5486
|
});
|
|
5487
|
+
xcodeBuildExecutor.onEvent((event) => {
|
|
5488
|
+
wsBridge.broadcast(event);
|
|
5489
|
+
});
|
|
4986
5490
|
wsBridge.onDisconnect(() => {
|
|
4987
5491
|
if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
|
|
4988
5492
|
approvalProxy.approveAll(t("server.phoneDisconnected"));
|
|
@@ -5090,6 +5594,7 @@ async function start(opts = {}) {
|
|
|
5090
5594
|
await attempt(() => approvalProxy.close(), "ApprovalProxy");
|
|
5091
5595
|
await attempt(() => sessionManager.destroy(), "SessionManager");
|
|
5092
5596
|
await attempt(() => terminalExecutor.destroy(), "TerminalExecutor");
|
|
5597
|
+
await attempt(() => xcodeBuildExecutor.destroy(), "XcodeBuildExecutor");
|
|
5093
5598
|
await attempt(() => notificationService.destroy(), "NotificationService");
|
|
5094
5599
|
await attempt(() => sessionFileWatcher.destroy(), "SessionFileWatcher");
|
|
5095
5600
|
if (errors.length > 0) {
|
|
@@ -5118,9 +5623,9 @@ async function start(opts = {}) {
|
|
|
5118
5623
|
openPairing: (duration) => pairingManager.open(duration),
|
|
5119
5624
|
closePairing: () => pairingManager.close(),
|
|
5120
5625
|
regenerateToken: async () => {
|
|
5121
|
-
const newToken = (0,
|
|
5122
|
-
await (0,
|
|
5123
|
-
await (0,
|
|
5626
|
+
const newToken = (0, import_uuid7.v4)();
|
|
5627
|
+
await (0, import_promises5.mkdir)(configDir, { recursive: true });
|
|
5628
|
+
await (0, import_promises5.writeFile)(tokenFile, newToken, "utf8");
|
|
5124
5629
|
instance.token = newToken;
|
|
5125
5630
|
wsBridge.updateToken(newToken);
|
|
5126
5631
|
approvalProxy.updateToken(newToken);
|
|
@@ -5137,7 +5642,7 @@ async function start(opts = {}) {
|
|
|
5137
5642
|
var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
5138
5643
|
function getPackageVersion() {
|
|
5139
5644
|
try {
|
|
5140
|
-
const pkg = JSON.parse((0, import_node_fs4.readFileSync)((0,
|
|
5645
|
+
const pkg = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path8.join)(__dirname, "..", "package.json"), "utf8"));
|
|
5141
5646
|
return pkg.version ?? "0.0.0";
|
|
5142
5647
|
} catch {
|
|
5143
5648
|
return "0.0.0";
|
|
@@ -5175,7 +5680,7 @@ async function autoUpdateIfNeeded() {
|
|
|
5175
5680
|
console.log(` \u{1F4E6} ${t("startup.autoUpdating", { current: PKG_VERSION, latest })}`);
|
|
5176
5681
|
console.log();
|
|
5177
5682
|
try {
|
|
5178
|
-
(0,
|
|
5683
|
+
(0, import_node_child_process11.execFileSync)("npx", [`sessix-server@${latest}`], {
|
|
5179
5684
|
stdio: "inherit",
|
|
5180
5685
|
env: { ...process.env, __SESSIX_UPDATED: "1" }
|
|
5181
5686
|
});
|
|
@@ -5281,7 +5786,7 @@ ${t("startup.pairingReopened")}`);
|
|
|
5281
5786
|
}
|
|
5282
5787
|
}
|
|
5283
5788
|
function getLocalIp() {
|
|
5284
|
-
const interfaces = (0,
|
|
5789
|
+
const interfaces = (0, import_node_os9.networkInterfaces)();
|
|
5285
5790
|
for (const iface of Object.values(interfaces)) {
|
|
5286
5791
|
for (const addr of iface ?? []) {
|
|
5287
5792
|
if (addr.family === "IPv4" && !addr.internal) {
|