sessix-server 0.3.8 → 0.4.0
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 +579 -48
- package/dist/server.js +573 -42
- 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");
|
|
@@ -2523,6 +2523,8 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2523
2523
|
pendingApprovals = /* @__PURE__ */ new Map();
|
|
2524
2524
|
/** 审批请求回调(通知外部推送到手机) */
|
|
2525
2525
|
approvalRequestCallbacks = [];
|
|
2526
|
+
/** 审批 resolve 回调(任何来源的 resolve 都会触发,用于 WS 广播清理 */
|
|
2527
|
+
approvalResolvedCallbacks = [];
|
|
2526
2528
|
/** YOLO 模式状态:sessionId -> enabled */
|
|
2527
2529
|
yoloSessions = /* @__PURE__ */ new Map();
|
|
2528
2530
|
/** 内存缓存:已被"始终允许"的工具名(避免每次读 settings.json) */
|
|
@@ -2560,6 +2562,30 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2560
2562
|
onApprovalRequest(callback) {
|
|
2561
2563
|
this.approvalRequestCallbacks.push(callback);
|
|
2562
2564
|
}
|
|
2565
|
+
/**
|
|
2566
|
+
* 注册审批 resolve 回调
|
|
2567
|
+
*
|
|
2568
|
+
* 任何来源的 resolve 都会触发:
|
|
2569
|
+
* - resolveApproval(手机端 approve/reject)
|
|
2570
|
+
* - 5 分钟超时自动 allow
|
|
2571
|
+
* - clearPendingForSession(会话被 kill)
|
|
2572
|
+
* - approveAll(手机全断时自动 allow)
|
|
2573
|
+
*
|
|
2574
|
+
* 用于向所有客户端广播 approval_resolved,清理可能残留的审批 UI。
|
|
2575
|
+
*/
|
|
2576
|
+
onApprovalResolved(callback) {
|
|
2577
|
+
this.approvalResolvedCallbacks.push(callback);
|
|
2578
|
+
}
|
|
2579
|
+
/** 通知所有 resolve 回调(内部调用) */
|
|
2580
|
+
notifyApprovalResolved(requestId, decision) {
|
|
2581
|
+
for (const callback of this.approvalResolvedCallbacks) {
|
|
2582
|
+
try {
|
|
2583
|
+
callback(requestId, decision);
|
|
2584
|
+
} catch (err) {
|
|
2585
|
+
console.error("[ApprovalProxy] Approval resolved callback error:", err);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2563
2589
|
/** 设置状态信息提供者(用于 /health 端点) */
|
|
2564
2590
|
setStatusInfoProvider(provider) {
|
|
2565
2591
|
this.statusInfoProvider = provider;
|
|
@@ -2593,6 +2619,7 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2593
2619
|
pending.resolve(decision);
|
|
2594
2620
|
this.pendingApprovals.delete(requestId);
|
|
2595
2621
|
console.log(`[ApprovalProxy] ${t("approval.requestProcessed", { id: requestId })}: ${decision.decision}`);
|
|
2622
|
+
this.notifyApprovalResolved(requestId, decision);
|
|
2596
2623
|
return true;
|
|
2597
2624
|
}
|
|
2598
2625
|
/** 获取当前待处理的审批数量 */
|
|
@@ -2608,9 +2635,11 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2608
2635
|
for (const [requestId, pending] of this.pendingApprovals) {
|
|
2609
2636
|
if (pending.request.sessionId === sessionId) {
|
|
2610
2637
|
clearTimeout(pending.timer);
|
|
2611
|
-
|
|
2638
|
+
const decision = { decision: "allow" };
|
|
2639
|
+
pending.resolve(decision);
|
|
2612
2640
|
this.pendingApprovals.delete(requestId);
|
|
2613
2641
|
console.log(`[ApprovalProxy] Session ${sessionId} killed, auto-allowed pending approval ${requestId}`);
|
|
2642
|
+
this.notifyApprovalResolved(requestId, decision);
|
|
2614
2643
|
}
|
|
2615
2644
|
}
|
|
2616
2645
|
}
|
|
@@ -2698,9 +2727,11 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2698
2727
|
const entries = Array.from(this.pendingApprovals.entries());
|
|
2699
2728
|
for (const [requestId, pending] of entries) {
|
|
2700
2729
|
clearTimeout(pending.timer);
|
|
2701
|
-
|
|
2730
|
+
const decision = { decision: "allow" };
|
|
2731
|
+
pending.resolve(decision);
|
|
2702
2732
|
this.pendingApprovals.delete(requestId);
|
|
2703
2733
|
console.log(`[ApprovalProxy] ${t("approval.autoAllowed", { id: requestId, reason: reason ? `\uFF08${reason}\uFF09` : "" })}`);
|
|
2734
|
+
this.notifyApprovalResolved(requestId, decision);
|
|
2704
2735
|
}
|
|
2705
2736
|
}
|
|
2706
2737
|
/** 优雅关闭 HTTP 服务 */
|
|
@@ -2792,7 +2823,9 @@ var ApprovalProxy = class _ApprovalProxy {
|
|
|
2792
2823
|
const timer = setTimeout(() => {
|
|
2793
2824
|
console.log(`[ApprovalProxy] ${t("approval.timeout", { id: requestId })}`);
|
|
2794
2825
|
this.pendingApprovals.delete(requestId);
|
|
2795
|
-
|
|
2826
|
+
const autoDecision = { decision: "allow" };
|
|
2827
|
+
resolve(autoDecision);
|
|
2828
|
+
this.notifyApprovalResolved(requestId, autoDecision);
|
|
2796
2829
|
}, 325e3);
|
|
2797
2830
|
this.pendingApprovals.set(requestId, { resolve, timer, request: approvalRequest });
|
|
2798
2831
|
});
|
|
@@ -3093,9 +3126,26 @@ process.stdin.on('end', async () => {
|
|
|
3093
3126
|
signal: AbortSignal.timeout(320000),
|
|
3094
3127
|
})
|
|
3095
3128
|
const data = await res.json()
|
|
3096
|
-
|
|
3129
|
+
const decision = data.decision === 'deny' ? 'deny' : 'allow'
|
|
3130
|
+
const output = {
|
|
3131
|
+
hookSpecificOutput: {
|
|
3132
|
+
hookEventName: 'PreToolUse',
|
|
3133
|
+
permissionDecision: decision,
|
|
3134
|
+
},
|
|
3135
|
+
}
|
|
3136
|
+
if (decision === 'deny' && data.reason) {
|
|
3137
|
+
output.hookSpecificOutput.permissionDecisionReason = String(data.reason)
|
|
3138
|
+
}
|
|
3139
|
+
process.stdout.write(JSON.stringify(output))
|
|
3140
|
+
process.exit(0)
|
|
3097
3141
|
} catch {
|
|
3098
|
-
// Sessix \u670D\u52A1\u5668\u4E0D\u53EF\u7528\uFF0C\u9ED8\u8BA4\u653E\u884C
|
|
3142
|
+
// 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
|
|
3143
|
+
process.stdout.write(JSON.stringify({
|
|
3144
|
+
hookSpecificOutput: {
|
|
3145
|
+
hookEventName: 'PreToolUse',
|
|
3146
|
+
permissionDecision: 'allow',
|
|
3147
|
+
},
|
|
3148
|
+
}))
|
|
3099
3149
|
process.exit(0)
|
|
3100
3150
|
}
|
|
3101
3151
|
})
|
|
@@ -3138,15 +3188,19 @@ var HookInstaller = class {
|
|
|
3138
3188
|
console.log("[HookInstaller] Hook uninstalled");
|
|
3139
3189
|
}
|
|
3140
3190
|
/**
|
|
3141
|
-
* 检查 hook
|
|
3142
|
-
*
|
|
3191
|
+
* 检查 hook 是否已安装且为最新版本
|
|
3192
|
+
*
|
|
3193
|
+
* 必须同时满足:
|
|
3194
|
+
* 1. 两个脚本文件都存在
|
|
3195
|
+
* 2. settings.json 中有 Sessix hook 配置
|
|
3196
|
+
* 3. approval-hook.js 包含最新的 permissionDecision 输出协议
|
|
3197
|
+
* (旧版仅用 exit code,会导致 ExitPlanMode 等工具卡住)
|
|
3143
3198
|
*/
|
|
3144
3199
|
async isInstalled() {
|
|
3145
|
-
let
|
|
3200
|
+
let approvalScriptContent = "";
|
|
3146
3201
|
let permissionScriptExists = false;
|
|
3147
3202
|
try {
|
|
3148
|
-
await (0, import_promises2.
|
|
3149
|
-
approvalScriptExists = true;
|
|
3203
|
+
approvalScriptContent = await (0, import_promises2.readFile)(HOOK_SCRIPT_PATH, "utf-8");
|
|
3150
3204
|
} catch {
|
|
3151
3205
|
}
|
|
3152
3206
|
try {
|
|
@@ -3154,9 +3208,10 @@ var HookInstaller = class {
|
|
|
3154
3208
|
permissionScriptExists = true;
|
|
3155
3209
|
} catch {
|
|
3156
3210
|
}
|
|
3211
|
+
const isLatestVersion = approvalScriptContent.includes("permissionDecision");
|
|
3157
3212
|
const settings = await this.readClaudeSettings();
|
|
3158
3213
|
const configExists = this.hasHookConfig(settings);
|
|
3159
|
-
return
|
|
3214
|
+
return isLatestVersion && permissionScriptExists && configExists;
|
|
3160
3215
|
}
|
|
3161
3216
|
// ============================================
|
|
3162
3217
|
// 内部方法
|
|
@@ -4330,7 +4385,7 @@ var AuthManager = class extends import_events3.EventEmitter {
|
|
|
4330
4385
|
};
|
|
4331
4386
|
|
|
4332
4387
|
// src/server.ts
|
|
4333
|
-
var
|
|
4388
|
+
var import_promises6 = require("fs/promises");
|
|
4334
4389
|
|
|
4335
4390
|
// src/terminal/TerminalExecutor.ts
|
|
4336
4391
|
var import_node_child_process7 = require("child_process");
|
|
@@ -4419,8 +4474,422 @@ var TerminalExecutor = class {
|
|
|
4419
4474
|
}
|
|
4420
4475
|
};
|
|
4421
4476
|
|
|
4422
|
-
// src/
|
|
4477
|
+
// src/xcode/XcodeBuildExecutor.ts
|
|
4423
4478
|
var import_node_child_process8 = require("child_process");
|
|
4479
|
+
var import_node_util = require("util");
|
|
4480
|
+
var import_promises4 = require("fs/promises");
|
|
4481
|
+
var import_node_path6 = require("path");
|
|
4482
|
+
var import_node_os7 = require("os");
|
|
4483
|
+
var import_uuid6 = require("uuid");
|
|
4484
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process8.exec);
|
|
4485
|
+
var BUILD_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4486
|
+
var INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4487
|
+
var CONFIG_FILE = (0, import_node_path6.join)((0, import_node_os7.homedir)(), ".sessix", "xcode-config.json");
|
|
4488
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
4489
|
+
"node_modules",
|
|
4490
|
+
".git",
|
|
4491
|
+
"DerivedData",
|
|
4492
|
+
"Pods",
|
|
4493
|
+
".build",
|
|
4494
|
+
"build",
|
|
4495
|
+
"dist",
|
|
4496
|
+
"__pycache__",
|
|
4497
|
+
".next",
|
|
4498
|
+
"vendor",
|
|
4499
|
+
".expo",
|
|
4500
|
+
"android",
|
|
4501
|
+
".gradle",
|
|
4502
|
+
"Carthage",
|
|
4503
|
+
"xcarchive"
|
|
4504
|
+
]);
|
|
4505
|
+
var MAX_SCAN_DEPTH = 4;
|
|
4506
|
+
var XcodeBuildExecutor = class {
|
|
4507
|
+
builds = /* @__PURE__ */ new Map();
|
|
4508
|
+
installs = /* @__PURE__ */ new Map();
|
|
4509
|
+
eventCallbacks = [];
|
|
4510
|
+
configCache = null;
|
|
4511
|
+
onEvent(callback) {
|
|
4512
|
+
this.eventCallbacks.push(callback);
|
|
4513
|
+
return () => {
|
|
4514
|
+
const idx = this.eventCallbacks.indexOf(callback);
|
|
4515
|
+
if (idx !== -1) this.eventCallbacks.splice(idx, 1);
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
emit(event) {
|
|
4519
|
+
for (const cb of this.eventCallbacks) {
|
|
4520
|
+
try {
|
|
4521
|
+
cb(event);
|
|
4522
|
+
} catch (err) {
|
|
4523
|
+
console.error("[XcodeBuildExecutor] Event callback error:", err);
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
// ============================================
|
|
4528
|
+
// 配置持久化
|
|
4529
|
+
// ============================================
|
|
4530
|
+
async loadConfigs() {
|
|
4531
|
+
if (this.configCache) return this.configCache;
|
|
4532
|
+
try {
|
|
4533
|
+
const raw = await (0, import_promises4.readFile)(CONFIG_FILE, "utf8");
|
|
4534
|
+
this.configCache = JSON.parse(raw);
|
|
4535
|
+
} catch {
|
|
4536
|
+
this.configCache = {};
|
|
4537
|
+
}
|
|
4538
|
+
return this.configCache;
|
|
4539
|
+
}
|
|
4540
|
+
async writeConfigs(store) {
|
|
4541
|
+
await (0, import_promises4.mkdir)((0, import_node_path6.join)((0, import_node_os7.homedir)(), ".sessix"), { recursive: true });
|
|
4542
|
+
await (0, import_promises4.writeFile)(CONFIG_FILE, JSON.stringify(store, null, 2), "utf8");
|
|
4543
|
+
this.configCache = store;
|
|
4544
|
+
}
|
|
4545
|
+
async getSavedConfig(projectPath) {
|
|
4546
|
+
const store = await this.loadConfigs();
|
|
4547
|
+
return store[projectPath];
|
|
4548
|
+
}
|
|
4549
|
+
async saveConfig(projectPath, config) {
|
|
4550
|
+
const store = await this.loadConfigs();
|
|
4551
|
+
store[projectPath] = config;
|
|
4552
|
+
await this.writeConfigs(store);
|
|
4553
|
+
}
|
|
4554
|
+
// ============================================
|
|
4555
|
+
// 递归扫描 Xcode 工程
|
|
4556
|
+
// ============================================
|
|
4557
|
+
async findAllContainers(projectPath) {
|
|
4558
|
+
const results = [];
|
|
4559
|
+
await this.scanDir(projectPath, projectPath, 0, results);
|
|
4560
|
+
results.sort((a, b) => a.path.split("/").length - b.path.split("/").length);
|
|
4561
|
+
return results;
|
|
4562
|
+
}
|
|
4563
|
+
async scanDir(rootPath, currentPath, depth, results) {
|
|
4564
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
4565
|
+
let entries;
|
|
4566
|
+
try {
|
|
4567
|
+
entries = await (0, import_promises4.readdir)(currentPath);
|
|
4568
|
+
} catch {
|
|
4569
|
+
return;
|
|
4570
|
+
}
|
|
4571
|
+
let foundWorkspace;
|
|
4572
|
+
let foundProject;
|
|
4573
|
+
for (const name of entries) {
|
|
4574
|
+
if (name.endsWith(".xcworkspace") && !name.endsWith("project.xcworkspace")) {
|
|
4575
|
+
foundWorkspace = foundWorkspace ?? name;
|
|
4576
|
+
} else if (name.endsWith(".xcodeproj")) {
|
|
4577
|
+
foundProject = foundProject ?? name;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
if (foundWorkspace || foundProject) {
|
|
4581
|
+
const relDir = currentPath === rootPath ? "" : currentPath.slice(rootPath.length + 1);
|
|
4582
|
+
const containerName = foundWorkspace ? foundWorkspace.replace(/\.xcworkspace$/, "") : foundProject.replace(/\.xcodeproj$/, "");
|
|
4583
|
+
results.push({
|
|
4584
|
+
path: relDir,
|
|
4585
|
+
name: containerName,
|
|
4586
|
+
workspace: foundWorkspace ? relDir ? `${relDir}/${foundWorkspace}` : foundWorkspace : void 0,
|
|
4587
|
+
project: foundProject ? relDir ? `${relDir}/${foundProject}` : foundProject : void 0
|
|
4588
|
+
});
|
|
4589
|
+
}
|
|
4590
|
+
for (const name of entries) {
|
|
4591
|
+
if (SKIP_DIRS.has(name)) continue;
|
|
4592
|
+
if (name.startsWith(".")) continue;
|
|
4593
|
+
if (name.endsWith(".xcodeproj") || name.endsWith(".xcworkspace")) continue;
|
|
4594
|
+
const childPath = (0, import_node_path6.join)(currentPath, name);
|
|
4595
|
+
await this.scanDir(rootPath, childPath, depth + 1, results);
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
// ============================================
|
|
4599
|
+
// 探测 schemes(针对指定 container)
|
|
4600
|
+
// ============================================
|
|
4601
|
+
async detect(projectPath) {
|
|
4602
|
+
if (process.platform !== "darwin") {
|
|
4603
|
+
return { available: false, error: "Xcode \u6784\u5EFA\u53EA\u652F\u6301 macOS", containers: [], schemes: [] };
|
|
4604
|
+
}
|
|
4605
|
+
const containers = await this.findAllContainers(projectPath);
|
|
4606
|
+
if (containers.length === 0) {
|
|
4607
|
+
return { available: false, error: "\u672A\u627E\u5230 .xcworkspace \u6216 .xcodeproj", containers: [], schemes: [] };
|
|
4608
|
+
}
|
|
4609
|
+
const firstContainer = containers[0];
|
|
4610
|
+
const schemes = await this.getSchemesForContainer(projectPath, firstContainer);
|
|
4611
|
+
const saved = await this.getSavedConfig(projectPath);
|
|
4612
|
+
return {
|
|
4613
|
+
available: true,
|
|
4614
|
+
containers,
|
|
4615
|
+
schemes,
|
|
4616
|
+
saved
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
async getSchemesForContainer(projectPath, container) {
|
|
4620
|
+
const args = container.workspace ? ["-workspace", container.workspace, "-list", "-json"] : ["-project", container.project, "-list", "-json"];
|
|
4621
|
+
try {
|
|
4622
|
+
const { stdout } = await execAsync(
|
|
4623
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4624
|
+
{ cwd: projectPath, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4625
|
+
);
|
|
4626
|
+
const parsed = JSON.parse(stdout);
|
|
4627
|
+
return parsed.workspace?.schemes ?? parsed.project?.schemes ?? [];
|
|
4628
|
+
} catch {
|
|
4629
|
+
return [];
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
// ============================================
|
|
4633
|
+
// 列举 destinations
|
|
4634
|
+
// ============================================
|
|
4635
|
+
async listDestinations(projectPath, scheme, container) {
|
|
4636
|
+
if (process.platform !== "darwin") return [];
|
|
4637
|
+
const args = [
|
|
4638
|
+
...container.workspace ? ["-workspace", container.workspace] : ["-project", container.project],
|
|
4639
|
+
"-scheme",
|
|
4640
|
+
scheme,
|
|
4641
|
+
"-showdestinations"
|
|
4642
|
+
];
|
|
4643
|
+
try {
|
|
4644
|
+
const { stdout, stderr } = await execAsync(
|
|
4645
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4646
|
+
{ cwd: projectPath, timeout: 6e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4647
|
+
);
|
|
4648
|
+
return parseDestinations(stdout + "\n" + stderr);
|
|
4649
|
+
} catch (err) {
|
|
4650
|
+
const e = err;
|
|
4651
|
+
const parsed = parseDestinations(`${e.stdout ?? ""}
|
|
4652
|
+
${e.stderr ?? ""}`);
|
|
4653
|
+
if (parsed.length > 0) return parsed;
|
|
4654
|
+
throw err;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
// ============================================
|
|
4658
|
+
// 构建
|
|
4659
|
+
// ============================================
|
|
4660
|
+
async build(sessionId, projectPath, override) {
|
|
4661
|
+
if (process.platform !== "darwin") {
|
|
4662
|
+
this.emitBuildError(sessionId, "", "Xcode \u6784\u5EFA\u4EC5\u652F\u6301 macOS\n");
|
|
4663
|
+
return null;
|
|
4664
|
+
}
|
|
4665
|
+
const config = override ?? await this.getSavedConfig(projectPath);
|
|
4666
|
+
if (!config) {
|
|
4667
|
+
this.emitBuildError(sessionId, "", "\u672A\u914D\u7F6E Xcode \u6784\u5EFA\u53C2\u6570\uFF0C\u8BF7\u5148\u9009\u62E9 scheme \u4E0E\u76EE\u6807\u8BBE\u5907\n");
|
|
4668
|
+
return null;
|
|
4669
|
+
}
|
|
4670
|
+
if (override) await this.saveConfig(projectPath, override);
|
|
4671
|
+
const buildId = (0, import_uuid6.v4)();
|
|
4672
|
+
const args = buildArgs(config);
|
|
4673
|
+
const proc = (0, import_node_child_process8.spawn)("xcodebuild", args, {
|
|
4674
|
+
cwd: projectPath,
|
|
4675
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4676
|
+
env: { ...process.env, NSUnbufferedIO: "YES" }
|
|
4677
|
+
});
|
|
4678
|
+
this.builds.set(buildId, proc);
|
|
4679
|
+
this.emit({
|
|
4680
|
+
type: "xcode_build_output",
|
|
4681
|
+
sessionId,
|
|
4682
|
+
buildId,
|
|
4683
|
+
stream: "stdout",
|
|
4684
|
+
data: `$ xcodebuild ${args.map((a) => a.includes(" ") ? `"${a}"` : a).join(" ")}
|
|
4685
|
+
cwd: ${projectPath}
|
|
4686
|
+
destination: ${config.destinationName}
|
|
4687
|
+
|
|
4688
|
+
`
|
|
4689
|
+
});
|
|
4690
|
+
proc.stdout?.on("data", (chunk) => {
|
|
4691
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stdout", data: chunk.toString() });
|
|
4692
|
+
});
|
|
4693
|
+
proc.stderr?.on("data", (chunk) => {
|
|
4694
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: chunk.toString() });
|
|
4695
|
+
});
|
|
4696
|
+
proc.on("error", (err) => {
|
|
4697
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: `[spawn error] ${err.message}
|
|
4698
|
+
` });
|
|
4699
|
+
});
|
|
4700
|
+
const timer = setTimeout(() => {
|
|
4701
|
+
if (this.builds.has(buildId)) killProcessCrossPlatform(proc);
|
|
4702
|
+
}, BUILD_TIMEOUT_MS);
|
|
4703
|
+
proc.on("exit", (code, signal) => {
|
|
4704
|
+
clearTimeout(timer);
|
|
4705
|
+
this.builds.delete(buildId);
|
|
4706
|
+
this.emit({ type: "xcode_build_exit", sessionId, buildId, code, signal });
|
|
4707
|
+
});
|
|
4708
|
+
console.log(`[XcodeBuildExecutor] build ${buildId} scheme=${config.scheme} dest=${config.destinationName}`);
|
|
4709
|
+
return buildId;
|
|
4710
|
+
}
|
|
4711
|
+
killBuild(buildId) {
|
|
4712
|
+
const proc = this.builds.get(buildId);
|
|
4713
|
+
if (proc) {
|
|
4714
|
+
killProcessCrossPlatform(proc);
|
|
4715
|
+
console.log(`[XcodeBuildExecutor] kill build ${buildId}`);
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
emitBuildError(sessionId, buildId, msg) {
|
|
4719
|
+
this.emit({ type: "xcode_build_output", sessionId, buildId, stream: "stderr", data: msg });
|
|
4720
|
+
this.emit({ type: "xcode_build_exit", sessionId, buildId, code: 1, signal: null });
|
|
4721
|
+
}
|
|
4722
|
+
// ============================================
|
|
4723
|
+
// 安装
|
|
4724
|
+
// ============================================
|
|
4725
|
+
async install(sessionId, projectPath) {
|
|
4726
|
+
if (process.platform !== "darwin") {
|
|
4727
|
+
this.emitInstallError(sessionId, "", "Xcode \u5B89\u88C5\u4EC5\u652F\u6301 macOS\n");
|
|
4728
|
+
return null;
|
|
4729
|
+
}
|
|
4730
|
+
const config = await this.getSavedConfig(projectPath);
|
|
4731
|
+
if (!config) {
|
|
4732
|
+
this.emitInstallError(sessionId, "", "\u672A\u627E\u5230\u6784\u5EFA\u914D\u7F6E\uFF0C\u8BF7\u5148\u6784\u5EFA\u4E00\u6B21\n");
|
|
4733
|
+
return null;
|
|
4734
|
+
}
|
|
4735
|
+
const installId = (0, import_uuid6.v4)();
|
|
4736
|
+
let appPath;
|
|
4737
|
+
try {
|
|
4738
|
+
appPath = await this.getAppPath(projectPath, config);
|
|
4739
|
+
} catch (err) {
|
|
4740
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4741
|
+
this.emitInstallError(sessionId, installId, `\u65E0\u6CD5\u5B9A\u4F4D\u6784\u5EFA\u4EA7\u7269: ${msg}
|
|
4742
|
+
`);
|
|
4743
|
+
return null;
|
|
4744
|
+
}
|
|
4745
|
+
const { destinationKind, destinationId, destinationName } = config;
|
|
4746
|
+
let installCmd;
|
|
4747
|
+
if (destinationKind === "simulator") {
|
|
4748
|
+
installCmd = ["xcrun", "simctl", "install", destinationId, appPath];
|
|
4749
|
+
} else if (destinationKind === "device") {
|
|
4750
|
+
installCmd = ["xcrun", "devicectl", "device", "install", "app", "--device-id", destinationId, appPath];
|
|
4751
|
+
} else if (destinationKind === "mac") {
|
|
4752
|
+
installCmd = ["open", appPath];
|
|
4753
|
+
} else {
|
|
4754
|
+
this.emitInstallError(sessionId, installId, `\u672A\u77E5\u76EE\u6807\u7C7B\u578B\uFF0C\u65E0\u6CD5\u81EA\u52A8\u5B89\u88C5
|
|
4755
|
+
`);
|
|
4756
|
+
return null;
|
|
4757
|
+
}
|
|
4758
|
+
this.emit({
|
|
4759
|
+
type: "xcode_install_output",
|
|
4760
|
+
sessionId,
|
|
4761
|
+
installId,
|
|
4762
|
+
stream: "stdout",
|
|
4763
|
+
data: `$ ${installCmd.join(" ")}
|
|
4764
|
+
destination: ${destinationName}
|
|
4765
|
+
app: ${appPath}
|
|
4766
|
+
|
|
4767
|
+
`
|
|
4768
|
+
});
|
|
4769
|
+
const proc = (0, import_node_child_process8.spawn)(installCmd[0], installCmd.slice(1), {
|
|
4770
|
+
cwd: projectPath,
|
|
4771
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4772
|
+
});
|
|
4773
|
+
this.installs.set(installId, proc);
|
|
4774
|
+
proc.stdout?.on("data", (chunk) => {
|
|
4775
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stdout", data: chunk.toString() });
|
|
4776
|
+
});
|
|
4777
|
+
proc.stderr?.on("data", (chunk) => {
|
|
4778
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: chunk.toString() });
|
|
4779
|
+
});
|
|
4780
|
+
proc.on("error", (err) => {
|
|
4781
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: `[spawn error] ${err.message}
|
|
4782
|
+
` });
|
|
4783
|
+
});
|
|
4784
|
+
const timer = setTimeout(() => {
|
|
4785
|
+
if (this.installs.has(installId)) killProcessCrossPlatform(proc);
|
|
4786
|
+
}, INSTALL_TIMEOUT_MS);
|
|
4787
|
+
proc.on("exit", (code, signal) => {
|
|
4788
|
+
clearTimeout(timer);
|
|
4789
|
+
this.installs.delete(installId);
|
|
4790
|
+
this.emit({ type: "xcode_install_exit", sessionId, installId, code, signal });
|
|
4791
|
+
});
|
|
4792
|
+
console.log(`[XcodeBuildExecutor] install ${installId} dest=${destinationName} kind=${destinationKind}`);
|
|
4793
|
+
return installId;
|
|
4794
|
+
}
|
|
4795
|
+
killInstall(installId) {
|
|
4796
|
+
const proc = this.installs.get(installId);
|
|
4797
|
+
if (proc) {
|
|
4798
|
+
killProcessCrossPlatform(proc);
|
|
4799
|
+
console.log(`[XcodeBuildExecutor] kill install ${installId}`);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
emitInstallError(sessionId, installId, msg) {
|
|
4803
|
+
this.emit({ type: "xcode_install_output", sessionId, installId, stream: "stderr", data: msg });
|
|
4804
|
+
this.emit({ type: "xcode_install_exit", sessionId, installId, code: 1, signal: null });
|
|
4805
|
+
}
|
|
4806
|
+
/** 通过 xcodebuild -showBuildSettings 定位 .app 路径 */
|
|
4807
|
+
async getAppPath(projectPath, config) {
|
|
4808
|
+
const args = [
|
|
4809
|
+
...buildArgs(config).filter((a) => a !== "build"),
|
|
4810
|
+
"-showBuildSettings"
|
|
4811
|
+
];
|
|
4812
|
+
const { stdout } = await execAsync(
|
|
4813
|
+
`xcodebuild ${args.map(shellQuote).join(" ")}`,
|
|
4814
|
+
{ cwd: projectPath, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
4815
|
+
);
|
|
4816
|
+
const builtDir = extractBuildSetting(stdout, "BUILT_PRODUCTS_DIR");
|
|
4817
|
+
const productName = extractBuildSetting(stdout, "FULL_PRODUCT_NAME");
|
|
4818
|
+
if (!builtDir || !productName) {
|
|
4819
|
+
throw new Error("\u65E0\u6CD5\u4ECE -showBuildSettings \u4E2D\u8BFB\u53D6 BUILT_PRODUCTS_DIR / FULL_PRODUCT_NAME");
|
|
4820
|
+
}
|
|
4821
|
+
return (0, import_node_path6.join)(builtDir, productName);
|
|
4822
|
+
}
|
|
4823
|
+
// ============================================
|
|
4824
|
+
// 清理
|
|
4825
|
+
// ============================================
|
|
4826
|
+
destroy() {
|
|
4827
|
+
for (const [id, proc] of this.builds) {
|
|
4828
|
+
killProcessCrossPlatform(proc);
|
|
4829
|
+
console.log(`[XcodeBuildExecutor] cleanup build ${id}`);
|
|
4830
|
+
}
|
|
4831
|
+
for (const [id, proc] of this.installs) {
|
|
4832
|
+
killProcessCrossPlatform(proc);
|
|
4833
|
+
console.log(`[XcodeBuildExecutor] cleanup install ${id}`);
|
|
4834
|
+
}
|
|
4835
|
+
this.builds.clear();
|
|
4836
|
+
this.installs.clear();
|
|
4837
|
+
this.eventCallbacks.length = 0;
|
|
4838
|
+
}
|
|
4839
|
+
};
|
|
4840
|
+
function shellQuote(s) {
|
|
4841
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
4842
|
+
}
|
|
4843
|
+
function buildArgs(config) {
|
|
4844
|
+
const container = config.workspace ? ["-workspace", config.workspace] : config.project ? ["-project", config.project] : [];
|
|
4845
|
+
return [
|
|
4846
|
+
...container,
|
|
4847
|
+
"-scheme",
|
|
4848
|
+
config.scheme,
|
|
4849
|
+
"-destination",
|
|
4850
|
+
`id=${config.destinationId}`,
|
|
4851
|
+
"-configuration",
|
|
4852
|
+
config.configuration ?? "Debug",
|
|
4853
|
+
"build"
|
|
4854
|
+
];
|
|
4855
|
+
}
|
|
4856
|
+
function extractBuildSetting(output, key) {
|
|
4857
|
+
const match = new RegExp(`^\\s*${key}\\s*=\\s*(.+)$`, "m").exec(output);
|
|
4858
|
+
return match?.[1]?.trim();
|
|
4859
|
+
}
|
|
4860
|
+
function parseDestinations(text) {
|
|
4861
|
+
const results = [];
|
|
4862
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4863
|
+
const lineRegex = /\{\s*([^{}]+)\s*\}/g;
|
|
4864
|
+
let match;
|
|
4865
|
+
while ((match = lineRegex.exec(text)) !== null) {
|
|
4866
|
+
const fields = {};
|
|
4867
|
+
for (const part of match[1].split(",")) {
|
|
4868
|
+
const colon = part.indexOf(":");
|
|
4869
|
+
if (colon === -1) continue;
|
|
4870
|
+
const key = part.slice(0, colon).trim();
|
|
4871
|
+
const value = part.slice(colon + 1).trim();
|
|
4872
|
+
if (key) fields[key] = value;
|
|
4873
|
+
}
|
|
4874
|
+
const { id, name, platform: platform2 } = fields;
|
|
4875
|
+
if (!id || !name || !platform2) continue;
|
|
4876
|
+
if (seen.has(id)) continue;
|
|
4877
|
+
seen.add(id);
|
|
4878
|
+
let kind = "unknown";
|
|
4879
|
+
if (platform2.includes("Simulator")) kind = "simulator";
|
|
4880
|
+
else if (platform2 === "iOS" || platform2 === "watchOS" || platform2 === "tvOS" || platform2 === "visionOS") kind = "device";
|
|
4881
|
+
else if (platform2 === "macOS") kind = "mac";
|
|
4882
|
+
results.push({ id, name, platform: platform2, os: fields.OS, kind });
|
|
4883
|
+
}
|
|
4884
|
+
results.sort((a, b) => kindOrder(a.kind) - kindOrder(b.kind));
|
|
4885
|
+
return results;
|
|
4886
|
+
}
|
|
4887
|
+
function kindOrder(k) {
|
|
4888
|
+
return k === "device" ? 0 : k === "simulator" ? 1 : k === "mac" ? 2 : 3;
|
|
4889
|
+
}
|
|
4890
|
+
|
|
4891
|
+
// src/utils/cliCapabilities.ts
|
|
4892
|
+
var import_node_child_process9 = require("child_process");
|
|
4424
4893
|
var DEFAULT_CAPABILITIES = {
|
|
4425
4894
|
effortLevels: ["low", "medium", "high", "xhigh", "max"]
|
|
4426
4895
|
};
|
|
@@ -4448,7 +4917,7 @@ async function parseCliCapabilities() {
|
|
|
4448
4917
|
}
|
|
4449
4918
|
function runCli(path2, args) {
|
|
4450
4919
|
return new Promise((resolve) => {
|
|
4451
|
-
(0,
|
|
4920
|
+
(0, import_node_child_process9.execFile)(path2, args, { timeout: 5e3 }, (err, stdout) => {
|
|
4452
4921
|
if (err) {
|
|
4453
4922
|
console.warn(`[CliCapabilities] Failed to run ${path2} ${args.join(" ")}:`, err.message);
|
|
4454
4923
|
resolve(null);
|
|
@@ -4462,11 +4931,11 @@ function runCli(path2, args) {
|
|
|
4462
4931
|
// src/server.ts
|
|
4463
4932
|
var WS_PORT = 3745;
|
|
4464
4933
|
var HTTP_PORT = 3746;
|
|
4465
|
-
var
|
|
4934
|
+
var execAsync2 = (0, import_node_util2.promisify)(import_node_child_process10.exec);
|
|
4466
4935
|
async function killPortProcess(port) {
|
|
4467
4936
|
try {
|
|
4468
4937
|
if (isWindows) {
|
|
4469
|
-
const { stdout } = await
|
|
4938
|
+
const { stdout } = await execAsync2(
|
|
4470
4939
|
`netstat -ano | findstr :${port} | findstr LISTENING`
|
|
4471
4940
|
);
|
|
4472
4941
|
const pids = /* @__PURE__ */ new Set();
|
|
@@ -4476,14 +4945,14 @@ async function killPortProcess(port) {
|
|
|
4476
4945
|
if (pid && /^\d+$/.test(pid) && pid !== "0") pids.add(pid);
|
|
4477
4946
|
}
|
|
4478
4947
|
for (const pid of pids) {
|
|
4479
|
-
await
|
|
4948
|
+
await execAsync2(`taskkill /PID ${pid} /F`).catch(() => {
|
|
4480
4949
|
});
|
|
4481
4950
|
}
|
|
4482
4951
|
} else {
|
|
4483
|
-
const { stdout } = await
|
|
4952
|
+
const { stdout } = await execAsync2(`lsof -ti :${port}`);
|
|
4484
4953
|
const pids = stdout.trim().split("\n").filter((p) => p && /^\d+$/.test(p));
|
|
4485
4954
|
if (pids.length > 0) {
|
|
4486
|
-
await
|
|
4955
|
+
await execAsync2(`kill -9 ${pids.join(" ")}`);
|
|
4487
4956
|
}
|
|
4488
4957
|
}
|
|
4489
4958
|
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
@@ -4504,8 +4973,8 @@ async function createWithRetry(label, port, factory) {
|
|
|
4504
4973
|
}
|
|
4505
4974
|
}
|
|
4506
4975
|
async function start(opts = {}) {
|
|
4507
|
-
const configDir = (0,
|
|
4508
|
-
const tokenFile = (0,
|
|
4976
|
+
const configDir = (0, import_node_path7.join)((0, import_node_os8.homedir)(), ".sessix");
|
|
4977
|
+
const tokenFile = (0, import_node_path7.join)(configDir, "token");
|
|
4509
4978
|
let token;
|
|
4510
4979
|
if (opts.token !== void 0) {
|
|
4511
4980
|
token = opts.token;
|
|
@@ -4515,17 +4984,23 @@ async function start(opts = {}) {
|
|
|
4515
4984
|
token = envToken;
|
|
4516
4985
|
} else {
|
|
4517
4986
|
try {
|
|
4518
|
-
token = (await (0,
|
|
4987
|
+
token = (await (0, import_promises5.readFile)(tokenFile, "utf8")).trim();
|
|
4519
4988
|
} catch {
|
|
4520
|
-
token = (0,
|
|
4521
|
-
await (0,
|
|
4522
|
-
await (0,
|
|
4989
|
+
token = (0, import_uuid7.v4)();
|
|
4990
|
+
await (0, import_promises5.mkdir)(configDir, { recursive: true });
|
|
4991
|
+
await (0, import_promises5.writeFile)(tokenFile, token, "utf8");
|
|
4523
4992
|
}
|
|
4524
4993
|
}
|
|
4525
4994
|
}
|
|
4526
4995
|
const providerFactory = new ProviderFactory();
|
|
4527
4996
|
const sessionManager = new SessionManager(providerFactory);
|
|
4528
4997
|
const terminalExecutor = new TerminalExecutor();
|
|
4998
|
+
const xcodeBuildExecutor = new XcodeBuildExecutor();
|
|
4999
|
+
const approvalProxy = await createWithRetry(
|
|
5000
|
+
"ApprovalProxy",
|
|
5001
|
+
HTTP_PORT,
|
|
5002
|
+
() => ApprovalProxy.create({ port: HTTP_PORT, token })
|
|
5003
|
+
);
|
|
4529
5004
|
const wsBridge = await createWithRetry(
|
|
4530
5005
|
"WsBridge",
|
|
4531
5006
|
WS_PORT,
|
|
@@ -4556,15 +5031,10 @@ async function start(opts = {}) {
|
|
|
4556
5031
|
const sessionFileWatcher = new SessionFileWatcher((event) => {
|
|
4557
5032
|
wsBridge.broadcast(event);
|
|
4558
5033
|
});
|
|
4559
|
-
const approvalProxy = await createWithRetry(
|
|
4560
|
-
"ApprovalProxy",
|
|
4561
|
-
HTTP_PORT,
|
|
4562
|
-
() => ApprovalProxy.create({ port: HTTP_PORT, token })
|
|
4563
|
-
);
|
|
4564
5034
|
let mdnsService = null;
|
|
4565
5035
|
const pairingManager = new PairingManager({
|
|
4566
5036
|
token,
|
|
4567
|
-
serverName: (0,
|
|
5037
|
+
serverName: (0, import_node_os8.hostname)(),
|
|
4568
5038
|
version: "0.2.0",
|
|
4569
5039
|
onStateChange: (state) => mdnsService?.updatePairingState(state)
|
|
4570
5040
|
});
|
|
@@ -4618,7 +5088,7 @@ async function start(opts = {}) {
|
|
|
4618
5088
|
try {
|
|
4619
5089
|
switch (event.type) {
|
|
4620
5090
|
case "create_session": {
|
|
4621
|
-
await (0,
|
|
5091
|
+
await (0, import_promises5.mkdir)(event.projectPath, { recursive: true });
|
|
4622
5092
|
const resumeId = event.resumeSessionId ?? event.newSessionId;
|
|
4623
5093
|
if (resumeId) sessionFileWatcher.unwatch(resumeId);
|
|
4624
5094
|
await sessionManager.createSession(
|
|
@@ -4806,7 +5276,7 @@ async function start(opts = {}) {
|
|
|
4806
5276
|
if (!isStreaming) {
|
|
4807
5277
|
const filePath = getSessionFilePath(event.projectPath, event.sessionId);
|
|
4808
5278
|
try {
|
|
4809
|
-
const fileStat = await (0,
|
|
5279
|
+
const fileStat = await (0, import_promises6.stat)(filePath);
|
|
4810
5280
|
sessionFileWatcher.watch(event.sessionId, filePath, fileStat.size);
|
|
4811
5281
|
} catch {
|
|
4812
5282
|
}
|
|
@@ -4915,6 +5385,56 @@ async function start(opts = {}) {
|
|
|
4915
5385
|
wsBridge.send(ws, { type: "agent_list", agents });
|
|
4916
5386
|
break;
|
|
4917
5387
|
}
|
|
5388
|
+
case "xcode_detect": {
|
|
5389
|
+
const info = await xcodeBuildExecutor.detect(event.projectPath);
|
|
5390
|
+
wsBridge.send(ws, { type: "xcode_info", sessionId: event.sessionId, info });
|
|
5391
|
+
break;
|
|
5392
|
+
}
|
|
5393
|
+
case "xcode_list_schemes": {
|
|
5394
|
+
const schemes = await xcodeBuildExecutor.getSchemesForContainer(event.projectPath, event.container);
|
|
5395
|
+
wsBridge.send(ws, {
|
|
5396
|
+
type: "xcode_info",
|
|
5397
|
+
sessionId: event.sessionId,
|
|
5398
|
+
info: {
|
|
5399
|
+
available: schemes.length > 0,
|
|
5400
|
+
containers: [event.container],
|
|
5401
|
+
schemes
|
|
5402
|
+
}
|
|
5403
|
+
});
|
|
5404
|
+
break;
|
|
5405
|
+
}
|
|
5406
|
+
case "xcode_list_destinations": {
|
|
5407
|
+
try {
|
|
5408
|
+
const destinations = await xcodeBuildExecutor.listDestinations(event.projectPath, event.scheme, event.container);
|
|
5409
|
+
wsBridge.send(ws, { type: "xcode_destinations", sessionId: event.sessionId, scheme: event.scheme, destinations });
|
|
5410
|
+
} catch (err) {
|
|
5411
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5412
|
+
wsBridge.send(ws, {
|
|
5413
|
+
type: "xcode_destinations",
|
|
5414
|
+
sessionId: event.sessionId,
|
|
5415
|
+
scheme: event.scheme,
|
|
5416
|
+
destinations: [],
|
|
5417
|
+
error: message.split("\n")[0]
|
|
5418
|
+
});
|
|
5419
|
+
}
|
|
5420
|
+
break;
|
|
5421
|
+
}
|
|
5422
|
+
case "xcode_save_config": {
|
|
5423
|
+
await xcodeBuildExecutor.saveConfig(event.projectPath, event.config);
|
|
5424
|
+
break;
|
|
5425
|
+
}
|
|
5426
|
+
case "xcode_build": {
|
|
5427
|
+
await xcodeBuildExecutor.build(event.sessionId, event.projectPath, event.config);
|
|
5428
|
+
break;
|
|
5429
|
+
}
|
|
5430
|
+
case "xcode_build_kill": {
|
|
5431
|
+
xcodeBuildExecutor.killBuild(event.buildId);
|
|
5432
|
+
break;
|
|
5433
|
+
}
|
|
5434
|
+
case "xcode_install": {
|
|
5435
|
+
await xcodeBuildExecutor.install(event.sessionId, event.projectPath);
|
|
5436
|
+
break;
|
|
5437
|
+
}
|
|
4918
5438
|
default: {
|
|
4919
5439
|
wsBridge.send(ws, {
|
|
4920
5440
|
type: "error",
|
|
@@ -4950,11 +5470,21 @@ async function start(opts = {}) {
|
|
|
4950
5470
|
terminalExecutor.onEvent((event) => {
|
|
4951
5471
|
wsBridge.broadcast(event);
|
|
4952
5472
|
});
|
|
5473
|
+
xcodeBuildExecutor.onEvent((event) => {
|
|
5474
|
+
wsBridge.broadcast(event);
|
|
5475
|
+
});
|
|
4953
5476
|
wsBridge.onDisconnect(() => {
|
|
4954
5477
|
if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
|
|
4955
5478
|
approvalProxy.approveAll(t("server.phoneDisconnected"));
|
|
4956
5479
|
}
|
|
4957
5480
|
});
|
|
5481
|
+
approvalProxy.onApprovalResolved((requestId, decision) => {
|
|
5482
|
+
wsBridge.broadcast({
|
|
5483
|
+
type: "approval_resolved",
|
|
5484
|
+
requestId,
|
|
5485
|
+
decision: decision.decision
|
|
5486
|
+
});
|
|
5487
|
+
});
|
|
4958
5488
|
approvalProxy.onApprovalRequest((request) => {
|
|
4959
5489
|
wsBridge.broadcast({ type: "approval_request", request });
|
|
4960
5490
|
setTimeout(() => {
|
|
@@ -5050,6 +5580,7 @@ async function start(opts = {}) {
|
|
|
5050
5580
|
await attempt(() => approvalProxy.close(), "ApprovalProxy");
|
|
5051
5581
|
await attempt(() => sessionManager.destroy(), "SessionManager");
|
|
5052
5582
|
await attempt(() => terminalExecutor.destroy(), "TerminalExecutor");
|
|
5583
|
+
await attempt(() => xcodeBuildExecutor.destroy(), "XcodeBuildExecutor");
|
|
5053
5584
|
await attempt(() => notificationService.destroy(), "NotificationService");
|
|
5054
5585
|
await attempt(() => sessionFileWatcher.destroy(), "SessionFileWatcher");
|
|
5055
5586
|
if (errors.length > 0) {
|
|
@@ -5078,9 +5609,9 @@ async function start(opts = {}) {
|
|
|
5078
5609
|
openPairing: (duration) => pairingManager.open(duration),
|
|
5079
5610
|
closePairing: () => pairingManager.close(),
|
|
5080
5611
|
regenerateToken: async () => {
|
|
5081
|
-
const newToken = (0,
|
|
5082
|
-
await (0,
|
|
5083
|
-
await (0,
|
|
5612
|
+
const newToken = (0, import_uuid7.v4)();
|
|
5613
|
+
await (0, import_promises5.mkdir)(configDir, { recursive: true });
|
|
5614
|
+
await (0, import_promises5.writeFile)(tokenFile, newToken, "utf8");
|
|
5084
5615
|
instance.token = newToken;
|
|
5085
5616
|
wsBridge.updateToken(newToken);
|
|
5086
5617
|
approvalProxy.updateToken(newToken);
|
|
@@ -5097,7 +5628,7 @@ async function start(opts = {}) {
|
|
|
5097
5628
|
var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
5098
5629
|
function getPackageVersion() {
|
|
5099
5630
|
try {
|
|
5100
|
-
const pkg = JSON.parse((0, import_node_fs4.readFileSync)((0,
|
|
5631
|
+
const pkg = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path8.join)(__dirname, "..", "package.json"), "utf8"));
|
|
5101
5632
|
return pkg.version ?? "0.0.0";
|
|
5102
5633
|
} catch {
|
|
5103
5634
|
return "0.0.0";
|
|
@@ -5135,7 +5666,7 @@ async function autoUpdateIfNeeded() {
|
|
|
5135
5666
|
console.log(` \u{1F4E6} ${t("startup.autoUpdating", { current: PKG_VERSION, latest })}`);
|
|
5136
5667
|
console.log();
|
|
5137
5668
|
try {
|
|
5138
|
-
(0,
|
|
5669
|
+
(0, import_node_child_process11.execFileSync)("npx", [`sessix-server@${latest}`], {
|
|
5139
5670
|
stdio: "inherit",
|
|
5140
5671
|
env: { ...process.env, __SESSIX_UPDATED: "1" }
|
|
5141
5672
|
});
|
|
@@ -5241,7 +5772,7 @@ ${t("startup.pairingReopened")}`);
|
|
|
5241
5772
|
}
|
|
5242
5773
|
}
|
|
5243
5774
|
function getLocalIp() {
|
|
5244
|
-
const interfaces = (0,
|
|
5775
|
+
const interfaces = (0, import_node_os9.networkInterfaces)();
|
|
5245
5776
|
for (const iface of Object.values(interfaces)) {
|
|
5246
5777
|
for (const addr of iface ?? []) {
|
|
5247
5778
|
if (addr.family === "IPv4" && !addr.internal) {
|