deskbuddy 0.2.0-beta.32 → 0.2.0-beta.34
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/{chunk-4ICQ3BAL.js → chunk-VSS27ECH.js} +168 -13
- package/dist/cli.js +1918 -332
- package/dist/deskbuddy-dsh.mjs +79 -1
- package/dist/{enable-SIY4AEF7.js → enable-OC46BDQD.js} +1 -1
- package/dist/hook.cjs +12 -2
- package/dist/opencode.mjs +257 -2
- package/hook/deskbuddy-dsh.d.mts +3 -0
- package/hook/deskbuddy-dsh.mjs +79 -1
- package/hook/hook-debug.log +36 -0
- package/hook/hook.cjs +12 -2
- package/hook/opencode.d.mts +16 -1
- package/hook/opencode.mjs +257 -2
- package/package.json +1 -1
|
@@ -148,6 +148,7 @@ function loadConfig() {
|
|
|
148
148
|
approvalPolicy: parsed.approvalPolicy === "all" ? "all" : parsed.approvalPolicy === "commands" ? "commands" : void 0,
|
|
149
149
|
codexAppServer: typeof parsed.codexAppServer === "string" ? parsed.codexAppServer : void 0,
|
|
150
150
|
autoUpdate: typeof parsed.autoUpdate === "boolean" ? parsed.autoUpdate : void 0,
|
|
151
|
+
transcript: parsed.transcript === false ? false : void 0,
|
|
151
152
|
disabledTools: Array.isArray(parsed.disabledTools) ? parsed.disabledTools.filter((t) => typeof t === "string") : void 0,
|
|
152
153
|
workbuddySetup: parseWorkbuddySetup(parsed.workbuddySetup),
|
|
153
154
|
workbuddyAppApproval: typeof parsed.workbuddyAppApproval === "boolean" ? parsed.workbuddyAppApproval : void 0,
|
|
@@ -161,6 +162,7 @@ function loadConfig() {
|
|
|
161
162
|
dumateCdpPort: portOrUndefined(parsed.dumateCdpPort),
|
|
162
163
|
dumateShortcuts: typeof parsed.dumateShortcuts === "boolean" ? parsed.dumateShortcuts : void 0,
|
|
163
164
|
qoderShortcuts: typeof parsed.qoderShortcuts === "boolean" ? parsed.qoderShortcuts : void 0,
|
|
165
|
+
qoderLaunchPort: typeof parsed.qoderLaunchPort === "boolean" ? parsed.qoderLaunchPort : void 0,
|
|
164
166
|
cozeApp: typeof parsed.cozeApp === "boolean" ? parsed.cozeApp : void 0,
|
|
165
167
|
cozeCdpPort: portOrUndefined(parsed.cozeCdpPort)
|
|
166
168
|
};
|
|
@@ -1022,6 +1024,8 @@ var SERVICES = {
|
|
|
1022
1024
|
var DIRS_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyclientdirsreportapi";
|
|
1023
1025
|
var INSTALL_REDEEM_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyinstallredeemapi";
|
|
1024
1026
|
var CMD_RESULT_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddycommandresultapi";
|
|
1027
|
+
var TRANSCRIPT_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddytranscriptappendapi";
|
|
1028
|
+
var CODE_NOT_SUPPORT = "5001";
|
|
1025
1029
|
var COMMAND_OUTPUT_MAX = 8e3;
|
|
1026
1030
|
var APPROVAL_SERVICES = {
|
|
1027
1031
|
create: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalcreateapi",
|
|
@@ -1167,7 +1171,8 @@ var api = {
|
|
|
1167
1171
|
dirsReport: (payload) => call(DIRS_SERVICE, payload),
|
|
1168
1172
|
installRedeem: (installCode, clientId, hostName) => call(INSTALL_REDEEM_SERVICE, { installCode, clientId, hostName, ...machineInfo() }),
|
|
1169
1173
|
// 2 秒超时、失败不重试(spec §4.1):回报丢了只是设备少一句提示,任务状态仍由会话快照负责
|
|
1170
|
-
commandResult: (payload, timeoutMs = 2e3) => call(CMD_RESULT_SERVICE, payload, timeoutMs)
|
|
1174
|
+
commandResult: (payload, timeoutMs = 2e3) => call(CMD_RESULT_SERVICE, payload, timeoutMs),
|
|
1175
|
+
transcriptAppend: (payload, timeoutMs = 15e3) => call(TRANSCRIPT_SERVICE, payload, timeoutMs)
|
|
1171
1176
|
};
|
|
1172
1177
|
|
|
1173
1178
|
// src/core/registry.ts
|
|
@@ -2566,8 +2571,11 @@ function renderAmendmentQuoted(v) {
|
|
|
2566
2571
|
if (!Array.isArray(v) || v.length === 0 || v.some((x) => typeof x !== "string" || x === "")) return null;
|
|
2567
2572
|
const out = [];
|
|
2568
2573
|
for (const x of v) {
|
|
2569
|
-
if (/[
|
|
2570
|
-
|
|
2574
|
+
if (/[\u0000-\u001f\u007f-\u009f---]/.test(x)) return null;
|
|
2575
|
+
if (/^[A-Za-z0-9_@%+=,./-]+$/.test(x)) out.push(x);
|
|
2576
|
+
else if (!x.includes('"')) out.push(`"${x}"`);
|
|
2577
|
+
else if (!x.includes("'")) out.push(`'${x}'`);
|
|
2578
|
+
else out.push(`"${x.replace(/"/g, '\\"')}"`);
|
|
2571
2579
|
}
|
|
2572
2580
|
return out.join(" ");
|
|
2573
2581
|
}
|
|
@@ -5997,6 +6005,10 @@ var CdpKeeper = class {
|
|
|
5997
6005
|
#hung = new HungTracker();
|
|
5998
6006
|
/** 这一段卡死里「暂不重启」的原因记过哪条(同一条只记一次) */
|
|
5999
6007
|
#hungNote = "";
|
|
6008
|
+
/** 这一段端口不通里「不重启」的原因记过哪条(同一条只记一次;端口通了 / 转成卡死时清掉。258:原先这些分支一行不记) */
|
|
6009
|
+
#downNote = "";
|
|
6010
|
+
/** 最近一轮端口不通的细分(日志 / 设备任务的等待原因用):fail = 没在监听或回包不是 WorkBuddy;timeout = 超时且监听者不是本机回环的 WorkBuddy */
|
|
6011
|
+
#downWhy = "";
|
|
6000
6012
|
constructor(deps) {
|
|
6001
6013
|
this.#d = deps;
|
|
6002
6014
|
}
|
|
@@ -6137,6 +6149,16 @@ var CdpKeeper = class {
|
|
|
6137
6149
|
this.#hung.clear();
|
|
6138
6150
|
this.#hungNote = "";
|
|
6139
6151
|
}
|
|
6152
|
+
/** 最近一轮端口不通的细分(没不通过返回 '') */
|
|
6153
|
+
get downWhy() {
|
|
6154
|
+
return this.#downWhy;
|
|
6155
|
+
}
|
|
6156
|
+
/** 端口不通期间不重启的原因:同一段里同一条只记一次 */
|
|
6157
|
+
#noteDown(why) {
|
|
6158
|
+
if (this.#downNote === why) return;
|
|
6159
|
+
this.#downNote = why;
|
|
6160
|
+
this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3 ${this.#d.port()} \u4E0D\u901A\uFF0C${why}`);
|
|
6161
|
+
}
|
|
6140
6162
|
/** 卡死期间暂不重启的原因:同一段卡死里同一条只记一次 */
|
|
6141
6163
|
#noteHung(why) {
|
|
6142
6164
|
if (this.#hungNote === why) return;
|
|
@@ -6182,6 +6204,8 @@ var CdpKeeper = class {
|
|
|
6182
6204
|
if (this.#hung.since) this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3 ${this.#d.port()} \u6062\u590D\u54CD\u5E94`);
|
|
6183
6205
|
this.#markHung(false, 0);
|
|
6184
6206
|
this.#downRounds = 0;
|
|
6207
|
+
this.#downNote = "";
|
|
6208
|
+
this.#downWhy = "";
|
|
6185
6209
|
const ok = await this.ownerOk().catch(() => false);
|
|
6186
6210
|
if (ok) await this.#rememberExe().catch(() => {
|
|
6187
6211
|
});
|
|
@@ -6191,6 +6215,8 @@ var CdpKeeper = class {
|
|
|
6191
6215
|
const now = this.#now();
|
|
6192
6216
|
this.#markHung(true, now);
|
|
6193
6217
|
this.#downRounds = 0;
|
|
6218
|
+
this.#downNote = "";
|
|
6219
|
+
this.#downWhy = "";
|
|
6194
6220
|
if (this.#hung.confirmed(now)) {
|
|
6195
6221
|
await this.#maybeRestart(true).catch((e) => this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3\uFF1A\u91CD\u542F\u51FA\u9519 ${e?.message ?? e}`));
|
|
6196
6222
|
}
|
|
@@ -6198,6 +6224,7 @@ var CdpKeeper = class {
|
|
|
6198
6224
|
}
|
|
6199
6225
|
this.#markHung(false, 0);
|
|
6200
6226
|
this.#downRounds++;
|
|
6227
|
+
this.#downWhy = pr === "timeout" ? "timeout" : "fail";
|
|
6201
6228
|
await this.#maybeRestart().catch((e) => this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3\uFF1A\u91CD\u542F\u51FA\u9519 ${e?.message ?? e}`));
|
|
6202
6229
|
return "down";
|
|
6203
6230
|
}
|
|
@@ -6218,18 +6245,27 @@ var CdpKeeper = class {
|
|
|
6218
6245
|
}
|
|
6219
6246
|
if (this.#restarts >= RESTART_DAILY_MAX) {
|
|
6220
6247
|
if (hung) this.#noteHung(`\u4ECA\u5929\u5DF2\u91CD\u542F ${RESTART_DAILY_MAX} \u6B21\uFF0C\u4E0D\u518D\u91CD\u542F\uFF1B\u8BF7\u5728\u7535\u8111\u4E0A\u91CD\u5F00 WorkBuddy`);
|
|
6248
|
+
else this.#noteDown(`\u4ECA\u5929\u5DF2\u91CD\u542F ${RESTART_DAILY_MAX} \u6B21\uFF0C\u4E0D\u518D\u5E26\u7AEF\u53E3\u91CD\u542F`);
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
if (this.#lastRestart && now - this.#lastRestart < RESTART_GAP_MS) {
|
|
6252
|
+
if (!hung) this.#noteDown("30 \u5206\u949F\u5185\u521A\u91CD\u542F\u8FC7\uFF0C\u5148\u4E0D\u518D\u91CD\u542F");
|
|
6221
6253
|
return;
|
|
6222
6254
|
}
|
|
6223
|
-
if (this.#lastRestart && now - this.#lastRestart < RESTART_GAP_MS) return;
|
|
6224
6255
|
if (this.#lastWakeLaunch && now - this.#lastWakeLaunch < WAKE_LAUNCH_GAP_MS) return;
|
|
6225
6256
|
const running = await (this.#d.appRunning ?? defaultAppRunning)();
|
|
6226
|
-
if (!running)
|
|
6257
|
+
if (!running) {
|
|
6258
|
+
if (!hung) this.#noteDown("WorkBuddy App \u6CA1\u5728\u8FD0\u884C");
|
|
6259
|
+
return;
|
|
6260
|
+
}
|
|
6227
6261
|
if (this.#isBusy(now)) {
|
|
6228
6262
|
if (hung) this.#noteHung("\u4E14\u6709\u4EFB\u52A1\u5728\u8DD1 / \u6709\u7B49\u8BBE\u5907\u7684\u5361\uFF0C\u4E0D\u91CD\u542F\uFF1B\u8BF7\u5728\u7535\u8111\u4E0A\u91CD\u5F00 WorkBuddy");
|
|
6263
|
+
else this.#noteDown("App \u5728\u8FD0\u884C\u4F46\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF08\u53EF\u80FD\u662F\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF\u4E4B\u524D\u6253\u5F00\u7684\uFF09\uFF0C\u4E14\u6709\u4EFB\u52A1\u5728\u8DD1 / \u6709\u7B49\u8BBE\u5907\u7684\u5361\uFF0C\u4E0D\u91CD\u542F");
|
|
6229
6264
|
return;
|
|
6230
6265
|
}
|
|
6231
6266
|
if (!await this.#userAway()) {
|
|
6232
6267
|
if (hung) this.#noteHung("\u7528\u6237\u5728\u7535\u8111\u524D\uFF0C\u4E0D\u6253\u65AD\uFF1B\u8BF7\u5728\u7535\u8111\u4E0A\u91CD\u5F00 WorkBuddy");
|
|
6268
|
+
else this.#noteDown("App \u5728\u8FD0\u884C\u4F46\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF08\u53EF\u80FD\u662F\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF\u4E4B\u524D\u6253\u5F00\u7684\uFF09\uFF0C\u7528\u6237\u5728\u7535\u8111\u524D\uFF0C\u4E0D\u91CD\u542F");
|
|
6233
6269
|
return;
|
|
6234
6270
|
}
|
|
6235
6271
|
await this.#doRestart(true, hung);
|
|
@@ -7703,6 +7739,17 @@ var DumateWatch = class {
|
|
|
7703
7739
|
* 任务总结的读回(spec 2026-10-11 summary-all-tools):GET /session/:id/message + /session/status,按标记认本轮(messagesVerdict)。
|
|
7704
7740
|
* 连不上 / 读不出 → 等(总超时由调用方判);不唤起 App
|
|
7705
7741
|
*/
|
|
7742
|
+
/**
|
|
7743
|
+
* 会话完整对话(spec 2026-10-11 transcript-sync §3):GET /session/:id/message 全量(消息只追加,序号稳定)。
|
|
7744
|
+
* 连不上 / 读不出 → null(游标不动,下次可读再补);不唤起 App
|
|
7745
|
+
*/
|
|
7746
|
+
async transcriptMessages(sessionId) {
|
|
7747
|
+
const t = this.#target ?? this.#discover(false);
|
|
7748
|
+
if (!t) return null;
|
|
7749
|
+
const dir2 = this.#sessionDir.get(sessionId) ?? "";
|
|
7750
|
+
const r = await this.#http(t)(t, { method: "GET", path: `/session/${encodeURIComponent(sessionId)}/message`, ...dir2 ? { directory: dir2 } : {} });
|
|
7751
|
+
return r.status === 200 && Array.isArray(r.body) ? r.body : null;
|
|
7752
|
+
}
|
|
7706
7753
|
async summaryCheck(sessionId, instructionId) {
|
|
7707
7754
|
const wait = { kind: "wait" };
|
|
7708
7755
|
const t = this.#target ?? this.#discover(false);
|
|
@@ -8262,6 +8309,10 @@ var RESTART_DAILY_MAX2 = 3;
|
|
|
8262
8309
|
var BUSY_WINDOW_MS2 = 30 * 60 * 1e3;
|
|
8263
8310
|
var QUIT_WAIT_MS2 = 20 * 1e3;
|
|
8264
8311
|
var USER_IDLE_MS = 5 * 60 * 1e3;
|
|
8312
|
+
var LAUNCH_FRESH_MS = 10 * 1e3;
|
|
8313
|
+
var LAUNCH_PORT_WINDOW_MS = 10 * 60 * 1e3;
|
|
8314
|
+
var LAUNCH_PORT_MAX = 2;
|
|
8315
|
+
var LAUNCH_SEEN_MAX = 32;
|
|
8265
8316
|
function winBrandMainProc(procs, exe) {
|
|
8266
8317
|
const want = exe.toLowerCase();
|
|
8267
8318
|
for (const p of procs) {
|
|
@@ -8313,6 +8364,24 @@ function parseLsappBundle(out) {
|
|
|
8313
8364
|
const m = /"CFBundleIdentifier"\s*=\s*"([^"]+)"/.exec(out);
|
|
8314
8365
|
return m ? m[1] : null;
|
|
8315
8366
|
}
|
|
8367
|
+
function parseEtime(s) {
|
|
8368
|
+
const m = /^(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)$/.exec(s.trim());
|
|
8369
|
+
if (!m) return null;
|
|
8370
|
+
const [d, h, mi, se] = [m[1], m[2], m[3], m[4]].map((x) => Number(x ?? 0));
|
|
8371
|
+
return (((d * 24 + h) * 60 + mi) * 60 + se) * 1e3;
|
|
8372
|
+
}
|
|
8373
|
+
function hasPortArg(args) {
|
|
8374
|
+
return /(^|\s|")--remote-debugging-port(=|\s|"|$)/.test(args);
|
|
8375
|
+
}
|
|
8376
|
+
function macBrandMains(psOut, macExe) {
|
|
8377
|
+
const out = [];
|
|
8378
|
+
for (const line of psOut.split(/\r?\n/)) {
|
|
8379
|
+
const m = /^\s*(\d+)\s+(\S+)\s+(.*)$/.exec(line);
|
|
8380
|
+
if (!m || !isMainArgs(m[3], macExe)) continue;
|
|
8381
|
+
out.push({ pid: Number(m[1]), ageMs: parseEtime(m[2]), hasPort: hasPortArg(m[3].slice(macExe.length)) });
|
|
8382
|
+
}
|
|
8383
|
+
return out;
|
|
8384
|
+
}
|
|
8316
8385
|
function toolBusy(tool, now = Date.now(), dir2 = stateDir(), alive = isAlive) {
|
|
8317
8386
|
if (!existsSync20(dir2)) return false;
|
|
8318
8387
|
for (const f of readdirSync12(dir2)) {
|
|
@@ -8343,6 +8412,8 @@ var QoderCdpKeeper = class {
|
|
|
8343
8412
|
#hung = new HungTracker();
|
|
8344
8413
|
/** 这一段卡死里「暂不重启」的原因记过哪条(同一条只记一次) */
|
|
8345
8414
|
#hungNote = "";
|
|
8415
|
+
/** 启动瞬间补端口处理过的主进程号(每个启动只做一次) */
|
|
8416
|
+
#launchSeen = [];
|
|
8346
8417
|
constructor(deps) {
|
|
8347
8418
|
this.#d = deps;
|
|
8348
8419
|
}
|
|
@@ -8425,6 +8496,7 @@ var QoderCdpKeeper = class {
|
|
|
8425
8496
|
const t = all?.[this.#d.brand.tool] ?? {};
|
|
8426
8497
|
const out = { lastLaunch: Number(t.lastLaunch) || 0, lastRestart: Number(t.lastRestart) || 0, day: typeof t.day === "string" ? t.day : "", restarts: Number(t.restarts) || 0 };
|
|
8427
8498
|
if (this.#d.brand.win && isWinAppExe(t.appExe, this.#d.brand.win.exe)) out.appExe = t.appExe;
|
|
8499
|
+
if (Array.isArray(t.launchPorts)) out.launchPorts = t.launchPorts.map(Number).filter((x) => Number.isFinite(x) && x > 0);
|
|
8428
8500
|
return out;
|
|
8429
8501
|
} catch {
|
|
8430
8502
|
return { lastLaunch: 0, lastRestart: 0, day: "", restarts: 0 };
|
|
@@ -8594,6 +8666,87 @@ var QoderCdpKeeper = class {
|
|
|
8594
8666
|
const r = await this.#exec()("ioreg", ["-c", "IOHIDSystem", "-d", "4"]);
|
|
8595
8667
|
return r.code === 0 ? parseHidIdleMs2(r.stdout) : null;
|
|
8596
8668
|
}
|
|
8669
|
+
/** 主进程(macOS;只认唯一一个;没有 / 认不出 / 多个 → null) */
|
|
8670
|
+
async #brandMain() {
|
|
8671
|
+
const r = await this.#exec()("ps", ["-axww", "-o", "pid=,etime=,args="]);
|
|
8672
|
+
if (r.code !== 0) return null;
|
|
8673
|
+
const mains = macBrandMains(r.stdout, this.#d.brand.macExe);
|
|
8674
|
+
return mains.length === 1 ? mains[0] : null;
|
|
8675
|
+
}
|
|
8676
|
+
/**
|
|
8677
|
+
* 启动瞬间补端口(spec 2026-10-12 qoder-launch-port):主进程刚起(不到 LAUNCH_FRESH_MS)、命令行没带调试端口、没有会话在跑 / 待答题 →
|
|
8678
|
+
* 不管用户在不在,正常退出并带端口拉起。每个启动(主进程号)只看一次:带了端口 / 已超过 10 秒 / 处理过(成败都算)都不再动;
|
|
8679
|
+
* 单独限流 LAUNCH_PORT_MAX 次 / LAUNCH_PORT_WINDOW_MS(落盘),不占每天 3 次的重启额度。
|
|
8680
|
+
* 🔴 macOS 只正常退出(osascript quit),20 秒没退就放弃、绝不强杀;Windows 关闭按钮只会缩到托盘,按原有规则结束这一个主进程树(刚起、没会话)
|
|
8681
|
+
*/
|
|
8682
|
+
async fixFreshLaunch() {
|
|
8683
|
+
if (this.#platform() !== "darwin" || this.#skip() || this.#d.noRestart) return "skipped";
|
|
8684
|
+
if (this.#restarting || this.#waking || this.#launchChecking) return "skipped";
|
|
8685
|
+
this.#launchChecking = true;
|
|
8686
|
+
try {
|
|
8687
|
+
return await this.#fixFresh();
|
|
8688
|
+
} finally {
|
|
8689
|
+
this.#launchChecking = false;
|
|
8690
|
+
if (this.#launchRestarting) {
|
|
8691
|
+
this.#launchRestarting = false;
|
|
8692
|
+
this.#restarting = false;
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
/** 补端口检测进行中(只看进程表,不挡唤起) */
|
|
8697
|
+
#launchChecking = false;
|
|
8698
|
+
/** 这次 #restarting 是补端口占的(finally 里释放) */
|
|
8699
|
+
#launchRestarting = false;
|
|
8700
|
+
/** 正在启动补端口 / 重启(常开据此跳过) */
|
|
8701
|
+
get restarting() {
|
|
8702
|
+
return this.#restarting;
|
|
8703
|
+
}
|
|
8704
|
+
async #fixFresh() {
|
|
8705
|
+
const main = await this.#brandMain();
|
|
8706
|
+
if (!main) return "none";
|
|
8707
|
+
if (this.#launchSeen.includes(main.pid)) return "seen";
|
|
8708
|
+
this.#launchSeen.push(main.pid);
|
|
8709
|
+
if (this.#launchSeen.length > LAUNCH_SEEN_MAX) this.#launchSeen.shift();
|
|
8710
|
+
if (main.hasPort) return "has_port";
|
|
8711
|
+
if (main.ageMs === null || main.ageMs < 0 || main.ageMs >= LAUNCH_FRESH_MS) return "old";
|
|
8712
|
+
const b = this.#d.brand;
|
|
8713
|
+
const now = this.#now();
|
|
8714
|
+
if ((this.#d.busy ?? (() => toolBusy(b.tool, now)))()) {
|
|
8715
|
+
this.#d.log(`${b.label} \u521A\u542F\u52A8\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF0C\u4F46\u6709\u4F1A\u8BDD\u5728\u8DD1\u6216\u6709\u5F85\u7B54\u9898\uFF0C\u4E0D\u91CD\u5F00`);
|
|
8716
|
+
return "busy";
|
|
8717
|
+
}
|
|
8718
|
+
const t = this.#loadThrottle();
|
|
8719
|
+
const recent = (t.launchPorts ?? []).filter((x) => now - x < LAUNCH_PORT_WINDOW_MS && x <= now);
|
|
8720
|
+
if (recent.length >= LAUNCH_PORT_MAX) {
|
|
8721
|
+
this.#d.log(`${b.label} \u521A\u542F\u52A8\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF0C\u4F46 ${LAUNCH_PORT_WINDOW_MS / 6e4} \u5206\u949F\u5185\u5DF2\u91CD\u5F00 ${recent.length} \u6B21\uFF0C\u8FD9\u6B21\u4E0D\u91CD\u5F00`);
|
|
8722
|
+
return "limited";
|
|
8723
|
+
}
|
|
8724
|
+
const app = this.appPath();
|
|
8725
|
+
if (!app) return "failed";
|
|
8726
|
+
if (this.#restarting || this.#waking) return "skipped";
|
|
8727
|
+
this.#restarting = true;
|
|
8728
|
+
this.#launchRestarting = true;
|
|
8729
|
+
this.#saveThrottle({ ...t, launchPorts: [...recent, now], lastLaunch: now });
|
|
8730
|
+
await this.#exec()("osascript", ["-e", `quit app ${JSON.stringify(b.appName)}`], 15e3);
|
|
8731
|
+
let gone = false;
|
|
8732
|
+
for (let waited = 0; waited < QUIT_WAIT_MS2; waited += 1e3) {
|
|
8733
|
+
if (!await this.appRunning()) {
|
|
8734
|
+
gone = true;
|
|
8735
|
+
break;
|
|
8736
|
+
}
|
|
8737
|
+
await this.#sleep(1e3);
|
|
8738
|
+
}
|
|
8739
|
+
if (!gone) {
|
|
8740
|
+
this.#d.log(`${b.label} \u521A\u542F\u52A8\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF0C${QUIT_WAIT_MS2 / 1e3} \u79D2\u5185\u6CA1\u6709\u9000\u51FA\uFF0C\u653E\u5F03\u91CD\u5F00\uFF08\u4E0D\u5F3A\u5236\u7ED3\u675F\uFF09`);
|
|
8741
|
+
return "failed";
|
|
8742
|
+
}
|
|
8743
|
+
const port = this.#d.port();
|
|
8744
|
+
const launch = this.#d.launch ?? defaultLaunch;
|
|
8745
|
+
const env = (this.#d.launchEnv ?? cleanLaunchEnv)();
|
|
8746
|
+
const ok = await launch("open", ["-a", app, "--args", `--remote-debugging-port=${port}`], env);
|
|
8747
|
+
this.#d.log(ok ? `${b.label} \u521A\u542F\u52A8\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF0C\u5DF2\u5E26\u7AEF\u53E3\u91CD\u5F00\uFF08\u7AEF\u53E3 ${port}\uFF09` : `${b.label} \u521A\u542F\u52A8\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\uFF0C\u5DF2\u9000\u51FA\u4F46\u5E26\u7AEF\u53E3\u62C9\u8D77\u5931\u8D25\uFF08\u4E0D\u518D\u91CD\u8BD5\uFF09`);
|
|
8748
|
+
return ok ? "reopened" : "failed";
|
|
8749
|
+
}
|
|
8597
8750
|
/**
|
|
8598
8751
|
* 设备任务到达、App 不可用时唤起(spec §3.1)。不等端口起来(调用方等连上):
|
|
8599
8752
|
* - 端口已活 → running;
|
|
@@ -8601,9 +8754,9 @@ var QoderCdpKeeper = class {
|
|
|
8601
8754
|
* - App 开着没带端口:用户在场 → present、正在跑任务 → busy(都不动 App);用户不在场且空闲 → 退出重启 → restarted;
|
|
8602
8755
|
* - 没装 → not_installed;当天重启次数用完 / 拉不起来 → failed;非 macOS / 测试兜底 → skipped
|
|
8603
8756
|
*/
|
|
8604
|
-
async wakeForTask() {
|
|
8757
|
+
async wakeForTask(source = "task") {
|
|
8605
8758
|
if (this.#waking) return this.#waking;
|
|
8606
|
-
const p = this.#wake();
|
|
8759
|
+
const p = this.#wake(source);
|
|
8607
8760
|
this.#waking = p;
|
|
8608
8761
|
try {
|
|
8609
8762
|
return await p;
|
|
@@ -8611,7 +8764,7 @@ var QoderCdpKeeper = class {
|
|
|
8611
8764
|
if (this.#waking === p) this.#waking = null;
|
|
8612
8765
|
}
|
|
8613
8766
|
}
|
|
8614
|
-
async #wake() {
|
|
8767
|
+
async #wake(source) {
|
|
8615
8768
|
if (this.#skip() || !this.#canManage() || this.#d.noRestart) return "skipped";
|
|
8616
8769
|
if (this.#restarting) return "restarted";
|
|
8617
8770
|
const pr = await this.#probe();
|
|
@@ -8642,7 +8795,7 @@ var QoderCdpKeeper = class {
|
|
|
8642
8795
|
if (restarts >= RESTART_DAILY_MAX2) return "failed";
|
|
8643
8796
|
const app = this.appPath();
|
|
8644
8797
|
if (!app) return "not_installed";
|
|
8645
|
-
return await this.#restart(app, t, now, restarts, false) ? "restarted" : "failed";
|
|
8798
|
+
return await this.#restart(app, t, now, restarts, false, source) ? "restarted" : "failed";
|
|
8646
8799
|
}
|
|
8647
8800
|
/**
|
|
8648
8801
|
* 设备任务到达、端口在监听但无响应:空闲、用户不在场、已连续无响应满 HUNG_RESTART_MS、没超节流 → 结束并带端口重启(restarted);
|
|
@@ -8678,7 +8831,7 @@ var QoderCdpKeeper = class {
|
|
|
8678
8831
|
* hung=false(没带端口):正常退出 20 秒没反应就放弃——🔴 可能有确认弹窗,不强杀;
|
|
8679
8832
|
* hung=true(端口无响应、App 卡死):正常退出没反应时结束端口监听者(确认是这个 App 的主进程才动)
|
|
8680
8833
|
*/
|
|
8681
|
-
async #restart(app, t, now, restarts, hung) {
|
|
8834
|
+
async #restart(app, t, now, restarts, hung, source = "task") {
|
|
8682
8835
|
if (this.#restarting) return false;
|
|
8683
8836
|
this.#restarting = true;
|
|
8684
8837
|
const b = this.#d.brand;
|
|
@@ -8686,7 +8839,7 @@ var QoderCdpKeeper = class {
|
|
|
8686
8839
|
try {
|
|
8687
8840
|
this.#saveThrottle({ ...this.#loadThrottle(), lastRestart: now, day: localDay2(now), restarts: restarts + 1 });
|
|
8688
8841
|
this.#d.log(
|
|
8689
|
-
hung ? `${b.label} \u8C03\u8BD5\u7AEF\u53E3 ${port} \u65E0\u54CD\u5E94 ${this.#hung.minutes(now)} \u5206\u949F\u3001App \u7A7A\u95F2\u3001\u7528\u6237\u4E0D\u5728\u573A\uFF1A\u9000\u51FA\u5E76\u5E26\u7AEF\u53E3\u91CD\u542F\uFF08\u4ECA\u5929\u7B2C ${restarts + 1}/${RESTART_DAILY_MAX2} \u6B21\uFF09` : `\u8BBE\u5907\u4EFB\u52A1\u5230\u8FBE\uFF1A${b.label} \u5F00\u7740\u4F46\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\u3001\u7528\u6237\u4E0D\u5728\u573A\uFF1A\u9000\u51FA\u5E76\u5E26\u7AEF\u53E3\u91CD\u542F\uFF08\u4ECA\u5929\u7B2C ${restarts + 1}/${RESTART_DAILY_MAX2} \u6B21\uFF09`
|
|
8842
|
+
hung ? `${b.label} \u8C03\u8BD5\u7AEF\u53E3 ${port} \u65E0\u54CD\u5E94 ${this.#hung.minutes(now)} \u5206\u949F\u3001App \u7A7A\u95F2\u3001\u7528\u6237\u4E0D\u5728\u573A\uFF1A\u9000\u51FA\u5E76\u5E26\u7AEF\u53E3\u91CD\u542F\uFF08\u4ECA\u5929\u7B2C ${restarts + 1}/${RESTART_DAILY_MAX2} \u6B21\uFF09` : source === "keep_open" ? `${b.label} \u7AEF\u53E3\u4E0D\u5728\u3001\u7528\u6237\u4E0D\u5728\u4E14\u7A7A\u95F2\uFF0C\u5E26\u7AEF\u53E3\u91CD\u5F00\uFF08\u5E38\u5F00\uFF0C\u4ECA\u5929\u7B2C ${restarts + 1}/${RESTART_DAILY_MAX2} \u6B21\uFF09` : `\u8BBE\u5907\u4EFB\u52A1\u5230\u8FBE\uFF1A${b.label} \u5F00\u7740\u4F46\u6CA1\u5E26\u8C03\u8BD5\u7AEF\u53E3\u3001\u7528\u6237\u4E0D\u5728\u573A\uFF1A\u9000\u51FA\u5E76\u5E26\u7AEF\u53E3\u91CD\u542F\uFF08\u4ECA\u5929\u7B2C ${restarts + 1}/${RESTART_DAILY_MAX2} \u6B21\uFF09`
|
|
8690
8843
|
);
|
|
8691
8844
|
const exec = this.#exec();
|
|
8692
8845
|
if (this.#isWin()) return await this.#restartWin(app, hung);
|
|
@@ -10495,7 +10648,7 @@ var defaultLoginOpenDeps = () => ({
|
|
|
10495
10648
|
});
|
|
10496
10649
|
async function installCodebuddyHooks(log) {
|
|
10497
10650
|
try {
|
|
10498
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
10651
|
+
const { installTools: installTools2 } = await import("./enable-OC46BDQD.js");
|
|
10499
10652
|
installTools2(["workbuddy"], () => {
|
|
10500
10653
|
}, null, { auto: true });
|
|
10501
10654
|
} catch (e) {
|
|
@@ -11009,7 +11162,7 @@ function defaultOfferDeps(log) {
|
|
|
11009
11162
|
}),
|
|
11010
11163
|
openLogin: () => defaultOpenLogin(platform),
|
|
11011
11164
|
installHooks: async () => {
|
|
11012
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
11165
|
+
const { installTools: installTools2 } = await import("./enable-OC46BDQD.js");
|
|
11013
11166
|
installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
|
|
11014
11167
|
},
|
|
11015
11168
|
log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|
|
@@ -11263,6 +11416,7 @@ export {
|
|
|
11263
11416
|
qwenworkDataHome,
|
|
11264
11417
|
qoderSettingsPath,
|
|
11265
11418
|
opencodePluginPath,
|
|
11419
|
+
codexHome,
|
|
11266
11420
|
installedHookPath,
|
|
11267
11421
|
loadConfig,
|
|
11268
11422
|
saveConfig,
|
|
@@ -11274,6 +11428,7 @@ export {
|
|
|
11274
11428
|
packageVersion,
|
|
11275
11429
|
installedHookVersion,
|
|
11276
11430
|
syncHookFile,
|
|
11431
|
+
CODE_NOT_SUPPORT,
|
|
11277
11432
|
APP_ID,
|
|
11278
11433
|
APP_SECRET,
|
|
11279
11434
|
sign,
|