deskbuddy 0.2.0-beta.33 → 0.2.0-beta.35
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.
|
@@ -162,6 +162,7 @@ function loadConfig() {
|
|
|
162
162
|
dumateCdpPort: portOrUndefined(parsed.dumateCdpPort),
|
|
163
163
|
dumateShortcuts: typeof parsed.dumateShortcuts === "boolean" ? parsed.dumateShortcuts : void 0,
|
|
164
164
|
qoderShortcuts: typeof parsed.qoderShortcuts === "boolean" ? parsed.qoderShortcuts : void 0,
|
|
165
|
+
qoderLaunchPort: typeof parsed.qoderLaunchPort === "boolean" ? parsed.qoderLaunchPort : void 0,
|
|
165
166
|
cozeApp: typeof parsed.cozeApp === "boolean" ? parsed.cozeApp : void 0,
|
|
166
167
|
cozeCdpPort: portOrUndefined(parsed.cozeCdpPort)
|
|
167
168
|
};
|
|
@@ -2570,8 +2571,11 @@ function renderAmendmentQuoted(v) {
|
|
|
2570
2571
|
if (!Array.isArray(v) || v.length === 0 || v.some((x) => typeof x !== "string" || x === "")) return null;
|
|
2571
2572
|
const out = [];
|
|
2572
2573
|
for (const x of v) {
|
|
2573
|
-
if (/[
|
|
2574
|
-
|
|
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, '\\"')}"`);
|
|
2575
2579
|
}
|
|
2576
2580
|
return out.join(" ");
|
|
2577
2581
|
}
|
|
@@ -5604,10 +5608,13 @@ var defaultFetchJson = async (url, timeoutMs) => {
|
|
|
5604
5608
|
clearTimeout(t);
|
|
5605
5609
|
}
|
|
5606
5610
|
};
|
|
5607
|
-
function
|
|
5608
|
-
if (!Array.isArray(list)) return
|
|
5611
|
+
function mainTargets(list) {
|
|
5612
|
+
if (!Array.isArray(list)) return [];
|
|
5609
5613
|
const pages = list.filter((t) => t && t.type === "page" && typeof t.webSocketDebuggerUrl === "string" && !/^devtools:/.test(t.url ?? ""));
|
|
5610
|
-
return pages.
|
|
5614
|
+
return pages.filter((t) => /renderer\/index\.html/.test(t.url ?? ""));
|
|
5615
|
+
}
|
|
5616
|
+
function pickMainTarget(list) {
|
|
5617
|
+
return mainTargets(list)[0] ?? null;
|
|
5611
5618
|
}
|
|
5612
5619
|
function loopbackWsUrl(wsUrl, port) {
|
|
5613
5620
|
try {
|
|
@@ -5620,16 +5627,18 @@ function loopbackWsUrl(wsUrl, port) {
|
|
|
5620
5627
|
return null;
|
|
5621
5628
|
}
|
|
5622
5629
|
}
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5630
|
+
var HAS_SDK_EXPR = "!!(globalThis.wb && globalThis.wb.conversations)";
|
|
5631
|
+
function pageName(url) {
|
|
5632
|
+
return String(url || "").split(/[?#]/)[0].split(/[\\/]/).slice(-2).join("/");
|
|
5633
|
+
}
|
|
5634
|
+
function openWs(target, port, d, info) {
|
|
5635
|
+
if (!target.webSocketDebuggerUrl) return Promise.reject(new Error("\u627E\u4E0D\u5230 App \u4E3B\u7A97\u53E3"));
|
|
5627
5636
|
const url = loopbackWsUrl(target.webSocketDebuggerUrl, port);
|
|
5628
|
-
if (!url)
|
|
5637
|
+
if (!url) return Promise.reject(new Error("\u4E3B\u7A97\u53E3\u7684\u8C03\u8BD5\u5730\u5740\u4E0D\u5BF9"));
|
|
5629
5638
|
const ws = (d.ws ?? ((u) => new WebSocket(u, { handshakeTimeout: 5e3 })))(url);
|
|
5630
|
-
return
|
|
5639
|
+
return new Promise((resolve6, reject) => {
|
|
5631
5640
|
let opened = false;
|
|
5632
|
-
const client = new WsCdpClient(ws);
|
|
5641
|
+
const client = new WsCdpClient(ws, info);
|
|
5633
5642
|
ws.on("open", () => {
|
|
5634
5643
|
opened = true;
|
|
5635
5644
|
resolve6(client);
|
|
@@ -5644,13 +5653,53 @@ async function connectCdp(port, d = {}) {
|
|
|
5644
5653
|
ws.on("message", (data) => client.onMessage(data));
|
|
5645
5654
|
});
|
|
5646
5655
|
}
|
|
5656
|
+
async function connectCdp(port, d = {}) {
|
|
5657
|
+
const fetchJson = d.fetchJson ?? defaultFetchJson;
|
|
5658
|
+
const list = await fetchJson(`http://127.0.0.1:${port}/json/list`, 3e3);
|
|
5659
|
+
if (!d.pickSdk) {
|
|
5660
|
+
const target = (d.pick ?? pickMainTarget)(list);
|
|
5661
|
+
if (!target?.webSocketDebuggerUrl) throw new Error("\u627E\u4E0D\u5230 App \u4E3B\u7A97\u53E3");
|
|
5662
|
+
return openWs(target, port, d);
|
|
5663
|
+
}
|
|
5664
|
+
const all = mainTargets(list);
|
|
5665
|
+
if (all.length === 0) throw new Error("\u627E\u4E0D\u5230 App \u4E3B\u7A97\u53E3");
|
|
5666
|
+
let ua = "";
|
|
5667
|
+
try {
|
|
5668
|
+
const v = await fetchJson(`http://127.0.0.1:${port}/json/version`, 2e3);
|
|
5669
|
+
const m = typeof v?.["User-Agent"] === "string" ? /\bWorkBuddy\/[\w.-]+/.exec(v["User-Agent"]) : null;
|
|
5670
|
+
ua = [m?.[0], typeof v?.Browser === "string" ? v.Browser : ""].filter(Boolean).join(" ");
|
|
5671
|
+
} catch {
|
|
5672
|
+
ua = "";
|
|
5673
|
+
}
|
|
5674
|
+
const infoOf = (i, sdk) => ({ url: pageName(String(all[i].url ?? "")), title: "", candidates: all.length, index: i, sdk, ...ua ? { ua } : {} });
|
|
5675
|
+
if (all.length === 1) return openWs(all[0], port, d, infoOf(0, null));
|
|
5676
|
+
for (let i = 0; i < all.length; i++) {
|
|
5677
|
+
let c;
|
|
5678
|
+
try {
|
|
5679
|
+
c = await openWs(all[i], port, d, infoOf(i, true));
|
|
5680
|
+
} catch {
|
|
5681
|
+
continue;
|
|
5682
|
+
}
|
|
5683
|
+
let has = false;
|
|
5684
|
+
try {
|
|
5685
|
+
has = await c.evaluate(HAS_SDK_EXPR, 2e3) === true;
|
|
5686
|
+
} catch {
|
|
5687
|
+
has = false;
|
|
5688
|
+
}
|
|
5689
|
+
if (has) return c;
|
|
5690
|
+
c.close();
|
|
5691
|
+
}
|
|
5692
|
+
return openWs(all[0], port, d, infoOf(0, false));
|
|
5693
|
+
}
|
|
5647
5694
|
var WsCdpClient = class {
|
|
5648
5695
|
#ws;
|
|
5696
|
+
target;
|
|
5649
5697
|
#id = 0;
|
|
5650
5698
|
#closed = false;
|
|
5651
5699
|
#waiting = /* @__PURE__ */ new Map();
|
|
5652
|
-
constructor(ws) {
|
|
5700
|
+
constructor(ws, target) {
|
|
5653
5701
|
this.#ws = ws;
|
|
5702
|
+
if (target) this.target = target;
|
|
5654
5703
|
}
|
|
5655
5704
|
get closed() {
|
|
5656
5705
|
return this.#closed;
|
|
@@ -5854,6 +5903,7 @@ var HungTracker = class {
|
|
|
5854
5903
|
}
|
|
5855
5904
|
};
|
|
5856
5905
|
var APP_PORT_HUNG_REASON = "app_port_unresponsive";
|
|
5906
|
+
var STARTUP_GRACE_MS = 90 * 1e3;
|
|
5857
5907
|
var FORCE_QUIT_WAIT_MS = 10 * 1e3;
|
|
5858
5908
|
function isTimeoutError(e) {
|
|
5859
5909
|
const err = e;
|
|
@@ -6001,6 +6051,16 @@ var CdpKeeper = class {
|
|
|
6001
6051
|
#hung = new HungTracker();
|
|
6002
6052
|
/** 这一段卡死里「暂不重启」的原因记过哪条(同一条只记一次) */
|
|
6003
6053
|
#hungNote = "";
|
|
6054
|
+
/** 启动宽限期到什么时候(0 = 不在宽限期) */
|
|
6055
|
+
#graceUntil = 0;
|
|
6056
|
+
/** 宽限期内超时记过一行没有(每段宽限只记一次) */
|
|
6057
|
+
#graceNoted = false;
|
|
6058
|
+
/** 上一轮端口检查的结果(判「刚从不通变成通」) */
|
|
6059
|
+
#lastPort = null;
|
|
6060
|
+
/** 这一段端口不通里「不重启」的原因记过哪条(同一条只记一次;端口通了 / 转成卡死时清掉。258:原先这些分支一行不记) */
|
|
6061
|
+
#downNote = "";
|
|
6062
|
+
/** 最近一轮端口不通的细分(日志 / 设备任务的等待原因用):fail = 没在监听或回包不是 WorkBuddy;timeout = 超时且监听者不是本机回环的 WorkBuddy */
|
|
6063
|
+
#downWhy = "";
|
|
6004
6064
|
constructor(deps) {
|
|
6005
6065
|
this.#d = deps;
|
|
6006
6066
|
}
|
|
@@ -6141,6 +6201,30 @@ var CdpKeeper = class {
|
|
|
6141
6201
|
this.#hung.clear();
|
|
6142
6202
|
this.#hungNote = "";
|
|
6143
6203
|
}
|
|
6204
|
+
/** 进入启动宽限期(拉起 / 重启 / 端口刚从不通变成通) */
|
|
6205
|
+
#startGrace(now) {
|
|
6206
|
+
this.#graceUntil = now + STARTUP_GRACE_MS;
|
|
6207
|
+
this.#graceNoted = false;
|
|
6208
|
+
}
|
|
6209
|
+
#inGrace(now) {
|
|
6210
|
+
return this.#graceUntil > 0 && now < this.#graceUntil;
|
|
6211
|
+
}
|
|
6212
|
+
/** 宽限期内超时:记一行(每段一次) */
|
|
6213
|
+
#noteGrace() {
|
|
6214
|
+
if (this.#graceNoted) return;
|
|
6215
|
+
this.#graceNoted = true;
|
|
6216
|
+
this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3 ${this.#d.port()} \u8D85\u65F6\uFF0C\u4F46 App \u521A\u542F\u52A8 / \u521A\u6062\u590D\uFF08${STARTUP_GRACE_MS / 1e3} \u79D2\u5BBD\u9650\u671F\u5185\uFF09\uFF0C\u5148\u4E0D\u5224\u65E0\u54CD\u5E94`);
|
|
6217
|
+
}
|
|
6218
|
+
/** 最近一轮端口不通的细分(没不通过返回 '') */
|
|
6219
|
+
get downWhy() {
|
|
6220
|
+
return this.#downWhy;
|
|
6221
|
+
}
|
|
6222
|
+
/** 端口不通期间不重启的原因:同一段里同一条只记一次 */
|
|
6223
|
+
#noteDown(why) {
|
|
6224
|
+
if (this.#downNote === why) return;
|
|
6225
|
+
this.#downNote = why;
|
|
6226
|
+
this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3 ${this.#d.port()} \u4E0D\u901A\uFF0C${why}`);
|
|
6227
|
+
}
|
|
6144
6228
|
/** 卡死期间暂不重启的原因:同一段卡死里同一条只记一次 */
|
|
6145
6229
|
#noteHung(why) {
|
|
6146
6230
|
if (this.#hungNote === why) return;
|
|
@@ -6182,19 +6266,34 @@ var CdpKeeper = class {
|
|
|
6182
6266
|
await this.ensureEnv().catch(() => {
|
|
6183
6267
|
});
|
|
6184
6268
|
const pr = await this.#probe();
|
|
6269
|
+
const st = await this.#tickInner(pr);
|
|
6270
|
+
this.#lastPort = st;
|
|
6271
|
+
return st;
|
|
6272
|
+
}
|
|
6273
|
+
async #tickInner(pr) {
|
|
6185
6274
|
if (pr === "ok") {
|
|
6275
|
+
if (this.#lastPort === "down") this.#startGrace(this.#now());
|
|
6186
6276
|
if (this.#hung.since) this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3 ${this.#d.port()} \u6062\u590D\u54CD\u5E94`);
|
|
6187
6277
|
this.#markHung(false, 0);
|
|
6188
6278
|
this.#downRounds = 0;
|
|
6279
|
+
this.#downNote = "";
|
|
6280
|
+
this.#downWhy = "";
|
|
6189
6281
|
const ok = await this.ownerOk().catch(() => false);
|
|
6190
6282
|
if (ok) await this.#rememberExe().catch(() => {
|
|
6191
6283
|
});
|
|
6192
6284
|
return ok ? "up" : "untrusted";
|
|
6193
6285
|
}
|
|
6286
|
+
if (pr === "timeout" && this.#inGrace(this.#now()) && await this.ownerOk().catch(() => false)) {
|
|
6287
|
+
this.#noteGrace();
|
|
6288
|
+
this.#downRounds = 0;
|
|
6289
|
+
return this.#lastPort === "up" ? "up" : "down";
|
|
6290
|
+
}
|
|
6194
6291
|
if (await this.#isHung(pr)) {
|
|
6195
6292
|
const now = this.#now();
|
|
6196
6293
|
this.#markHung(true, now);
|
|
6197
6294
|
this.#downRounds = 0;
|
|
6295
|
+
this.#downNote = "";
|
|
6296
|
+
this.#downWhy = "";
|
|
6198
6297
|
if (this.#hung.confirmed(now)) {
|
|
6199
6298
|
await this.#maybeRestart(true).catch((e) => this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3\uFF1A\u91CD\u542F\u51FA\u9519 ${e?.message ?? e}`));
|
|
6200
6299
|
}
|
|
@@ -6202,6 +6301,7 @@ var CdpKeeper = class {
|
|
|
6202
6301
|
}
|
|
6203
6302
|
this.#markHung(false, 0);
|
|
6204
6303
|
this.#downRounds++;
|
|
6304
|
+
this.#downWhy = pr === "timeout" ? "timeout" : "fail";
|
|
6205
6305
|
await this.#maybeRestart().catch((e) => this.#d.log(`WorkBuddy \u8C03\u8BD5\u7AEF\u53E3\uFF1A\u91CD\u542F\u51FA\u9519 ${e?.message ?? e}`));
|
|
6206
6306
|
return "down";
|
|
6207
6307
|
}
|
|
@@ -6222,18 +6322,27 @@ var CdpKeeper = class {
|
|
|
6222
6322
|
}
|
|
6223
6323
|
if (this.#restarts >= RESTART_DAILY_MAX) {
|
|
6224
6324
|
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`);
|
|
6325
|
+
else this.#noteDown(`\u4ECA\u5929\u5DF2\u91CD\u542F ${RESTART_DAILY_MAX} \u6B21\uFF0C\u4E0D\u518D\u5E26\u7AEF\u53E3\u91CD\u542F`);
|
|
6326
|
+
return;
|
|
6327
|
+
}
|
|
6328
|
+
if (this.#lastRestart && now - this.#lastRestart < RESTART_GAP_MS) {
|
|
6329
|
+
if (!hung) this.#noteDown("30 \u5206\u949F\u5185\u521A\u91CD\u542F\u8FC7\uFF0C\u5148\u4E0D\u518D\u91CD\u542F");
|
|
6225
6330
|
return;
|
|
6226
6331
|
}
|
|
6227
|
-
if (this.#lastRestart && now - this.#lastRestart < RESTART_GAP_MS) return;
|
|
6228
6332
|
if (this.#lastWakeLaunch && now - this.#lastWakeLaunch < WAKE_LAUNCH_GAP_MS) return;
|
|
6229
6333
|
const running = await (this.#d.appRunning ?? defaultAppRunning)();
|
|
6230
|
-
if (!running)
|
|
6334
|
+
if (!running) {
|
|
6335
|
+
if (!hung) this.#noteDown("WorkBuddy App \u6CA1\u5728\u8FD0\u884C");
|
|
6336
|
+
return;
|
|
6337
|
+
}
|
|
6231
6338
|
if (this.#isBusy(now)) {
|
|
6232
6339
|
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");
|
|
6340
|
+
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");
|
|
6233
6341
|
return;
|
|
6234
6342
|
}
|
|
6235
6343
|
if (!await this.#userAway()) {
|
|
6236
6344
|
if (hung) this.#noteHung("\u7528\u6237\u5728\u7535\u8111\u524D\uFF0C\u4E0D\u6253\u65AD\uFF1B\u8BF7\u5728\u7535\u8111\u4E0A\u91CD\u5F00 WorkBuddy");
|
|
6345
|
+
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");
|
|
6237
6346
|
return;
|
|
6238
6347
|
}
|
|
6239
6348
|
await this.#doRestart(true, hung);
|
|
@@ -6245,6 +6354,7 @@ var CdpKeeper = class {
|
|
|
6245
6354
|
async #doRestart(verify, hung = false) {
|
|
6246
6355
|
this.#restarting = true;
|
|
6247
6356
|
this.#lastRestart = this.#now();
|
|
6357
|
+
this.#startGrace(this.#lastRestart);
|
|
6248
6358
|
this.#restarts++;
|
|
6249
6359
|
this.#saveThrottle();
|
|
6250
6360
|
try {
|
|
@@ -6336,6 +6446,10 @@ var CdpKeeper = class {
|
|
|
6336
6446
|
this.#d.log(`\u5524\u8D77 WorkBuddy\uFF1A${Math.round((now - this.#lastWakeLaunch) / 1e3)} \u79D2\u524D\u521A\u62C9\u8D77\u8FC7\uFF08\u8282\u6D41 60 \u79D2\uFF09\uFF0C\u4E0D\u518D\u62C9\u8D77\uFF0C\u7B49\u5B83\u8D77\u6765`);
|
|
6337
6447
|
return "launched";
|
|
6338
6448
|
}
|
|
6449
|
+
if (pr === "timeout" && this.#inGrace(now) && await this.ownerOk().catch(() => false)) {
|
|
6450
|
+
this.#noteGrace();
|
|
6451
|
+
return "running";
|
|
6452
|
+
}
|
|
6339
6453
|
if (await this.#isHung(pr)) return this.#wakeHung(now);
|
|
6340
6454
|
if (this.#lastRestart && now - this.#lastRestart < RESTART_GAP_MS) {
|
|
6341
6455
|
this.#d.log(`\u5524\u8D77 WorkBuddy\uFF1A${Math.round((now - this.#lastRestart) / 6e4)} \u5206\u949F\u524D\u521A\u91CD\u542F\u8FC7\uFF08\u8282\u6D41 30 \u5206\u949F\uFF09\uFF0C\u4E0D\u518D\u91CD\u542F\uFF0C\u7B49\u7AEF\u53E3\u8D77\u6765`);
|
|
@@ -6352,6 +6466,7 @@ var CdpKeeper = class {
|
|
|
6352
6466
|
return "not_installed";
|
|
6353
6467
|
}
|
|
6354
6468
|
this.#lastWakeLaunch = now;
|
|
6469
|
+
this.#startGrace(now);
|
|
6355
6470
|
this.#saveThrottle();
|
|
6356
6471
|
await this.ensureEnv().catch(() => {
|
|
6357
6472
|
});
|
|
@@ -8277,6 +8392,10 @@ var RESTART_DAILY_MAX2 = 3;
|
|
|
8277
8392
|
var BUSY_WINDOW_MS2 = 30 * 60 * 1e3;
|
|
8278
8393
|
var QUIT_WAIT_MS2 = 20 * 1e3;
|
|
8279
8394
|
var USER_IDLE_MS = 5 * 60 * 1e3;
|
|
8395
|
+
var LAUNCH_FRESH_MS = 10 * 1e3;
|
|
8396
|
+
var LAUNCH_PORT_WINDOW_MS = 10 * 60 * 1e3;
|
|
8397
|
+
var LAUNCH_PORT_MAX = 2;
|
|
8398
|
+
var LAUNCH_SEEN_MAX = 32;
|
|
8280
8399
|
function winBrandMainProc(procs, exe) {
|
|
8281
8400
|
const want = exe.toLowerCase();
|
|
8282
8401
|
for (const p of procs) {
|
|
@@ -8328,6 +8447,24 @@ function parseLsappBundle(out) {
|
|
|
8328
8447
|
const m = /"CFBundleIdentifier"\s*=\s*"([^"]+)"/.exec(out);
|
|
8329
8448
|
return m ? m[1] : null;
|
|
8330
8449
|
}
|
|
8450
|
+
function parseEtime(s) {
|
|
8451
|
+
const m = /^(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)$/.exec(s.trim());
|
|
8452
|
+
if (!m) return null;
|
|
8453
|
+
const [d, h, mi, se] = [m[1], m[2], m[3], m[4]].map((x) => Number(x ?? 0));
|
|
8454
|
+
return (((d * 24 + h) * 60 + mi) * 60 + se) * 1e3;
|
|
8455
|
+
}
|
|
8456
|
+
function hasPortArg(args) {
|
|
8457
|
+
return /(^|\s|")--remote-debugging-port(=|\s|"|$)/.test(args);
|
|
8458
|
+
}
|
|
8459
|
+
function macBrandMains(psOut, macExe) {
|
|
8460
|
+
const out = [];
|
|
8461
|
+
for (const line of psOut.split(/\r?\n/)) {
|
|
8462
|
+
const m = /^\s*(\d+)\s+(\S+)\s+(.*)$/.exec(line);
|
|
8463
|
+
if (!m || !isMainArgs(m[3], macExe)) continue;
|
|
8464
|
+
out.push({ pid: Number(m[1]), ageMs: parseEtime(m[2]), hasPort: hasPortArg(m[3].slice(macExe.length)) });
|
|
8465
|
+
}
|
|
8466
|
+
return out;
|
|
8467
|
+
}
|
|
8331
8468
|
function toolBusy(tool, now = Date.now(), dir2 = stateDir(), alive = isAlive) {
|
|
8332
8469
|
if (!existsSync20(dir2)) return false;
|
|
8333
8470
|
for (const f of readdirSync12(dir2)) {
|
|
@@ -8358,6 +8495,8 @@ var QoderCdpKeeper = class {
|
|
|
8358
8495
|
#hung = new HungTracker();
|
|
8359
8496
|
/** 这一段卡死里「暂不重启」的原因记过哪条(同一条只记一次) */
|
|
8360
8497
|
#hungNote = "";
|
|
8498
|
+
/** 启动瞬间补端口处理过的主进程号(每个启动只做一次) */
|
|
8499
|
+
#launchSeen = [];
|
|
8361
8500
|
constructor(deps) {
|
|
8362
8501
|
this.#d = deps;
|
|
8363
8502
|
}
|
|
@@ -8440,6 +8579,7 @@ var QoderCdpKeeper = class {
|
|
|
8440
8579
|
const t = all?.[this.#d.brand.tool] ?? {};
|
|
8441
8580
|
const out = { lastLaunch: Number(t.lastLaunch) || 0, lastRestart: Number(t.lastRestart) || 0, day: typeof t.day === "string" ? t.day : "", restarts: Number(t.restarts) || 0 };
|
|
8442
8581
|
if (this.#d.brand.win && isWinAppExe(t.appExe, this.#d.brand.win.exe)) out.appExe = t.appExe;
|
|
8582
|
+
if (Array.isArray(t.launchPorts)) out.launchPorts = t.launchPorts.map(Number).filter((x) => Number.isFinite(x) && x > 0);
|
|
8443
8583
|
return out;
|
|
8444
8584
|
} catch {
|
|
8445
8585
|
return { lastLaunch: 0, lastRestart: 0, day: "", restarts: 0 };
|
|
@@ -8609,6 +8749,87 @@ var QoderCdpKeeper = class {
|
|
|
8609
8749
|
const r = await this.#exec()("ioreg", ["-c", "IOHIDSystem", "-d", "4"]);
|
|
8610
8750
|
return r.code === 0 ? parseHidIdleMs2(r.stdout) : null;
|
|
8611
8751
|
}
|
|
8752
|
+
/** 主进程(macOS;只认唯一一个;没有 / 认不出 / 多个 → null) */
|
|
8753
|
+
async #brandMain() {
|
|
8754
|
+
const r = await this.#exec()("ps", ["-axww", "-o", "pid=,etime=,args="]);
|
|
8755
|
+
if (r.code !== 0) return null;
|
|
8756
|
+
const mains = macBrandMains(r.stdout, this.#d.brand.macExe);
|
|
8757
|
+
return mains.length === 1 ? mains[0] : null;
|
|
8758
|
+
}
|
|
8759
|
+
/**
|
|
8760
|
+
* 启动瞬间补端口(spec 2026-10-12 qoder-launch-port):主进程刚起(不到 LAUNCH_FRESH_MS)、命令行没带调试端口、没有会话在跑 / 待答题 →
|
|
8761
|
+
* 不管用户在不在,正常退出并带端口拉起。每个启动(主进程号)只看一次:带了端口 / 已超过 10 秒 / 处理过(成败都算)都不再动;
|
|
8762
|
+
* 单独限流 LAUNCH_PORT_MAX 次 / LAUNCH_PORT_WINDOW_MS(落盘),不占每天 3 次的重启额度。
|
|
8763
|
+
* 🔴 macOS 只正常退出(osascript quit),20 秒没退就放弃、绝不强杀;Windows 关闭按钮只会缩到托盘,按原有规则结束这一个主进程树(刚起、没会话)
|
|
8764
|
+
*/
|
|
8765
|
+
async fixFreshLaunch() {
|
|
8766
|
+
if (this.#platform() !== "darwin" || this.#skip() || this.#d.noRestart) return "skipped";
|
|
8767
|
+
if (this.#restarting || this.#waking || this.#launchChecking) return "skipped";
|
|
8768
|
+
this.#launchChecking = true;
|
|
8769
|
+
try {
|
|
8770
|
+
return await this.#fixFresh();
|
|
8771
|
+
} finally {
|
|
8772
|
+
this.#launchChecking = false;
|
|
8773
|
+
if (this.#launchRestarting) {
|
|
8774
|
+
this.#launchRestarting = false;
|
|
8775
|
+
this.#restarting = false;
|
|
8776
|
+
}
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
/** 补端口检测进行中(只看进程表,不挡唤起) */
|
|
8780
|
+
#launchChecking = false;
|
|
8781
|
+
/** 这次 #restarting 是补端口占的(finally 里释放) */
|
|
8782
|
+
#launchRestarting = false;
|
|
8783
|
+
/** 正在启动补端口 / 重启(常开据此跳过) */
|
|
8784
|
+
get restarting() {
|
|
8785
|
+
return this.#restarting;
|
|
8786
|
+
}
|
|
8787
|
+
async #fixFresh() {
|
|
8788
|
+
const main = await this.#brandMain();
|
|
8789
|
+
if (!main) return "none";
|
|
8790
|
+
if (this.#launchSeen.includes(main.pid)) return "seen";
|
|
8791
|
+
this.#launchSeen.push(main.pid);
|
|
8792
|
+
if (this.#launchSeen.length > LAUNCH_SEEN_MAX) this.#launchSeen.shift();
|
|
8793
|
+
if (main.hasPort) return "has_port";
|
|
8794
|
+
if (main.ageMs === null || main.ageMs < 0 || main.ageMs >= LAUNCH_FRESH_MS) return "old";
|
|
8795
|
+
const b = this.#d.brand;
|
|
8796
|
+
const now = this.#now();
|
|
8797
|
+
if ((this.#d.busy ?? (() => toolBusy(b.tool, now)))()) {
|
|
8798
|
+
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`);
|
|
8799
|
+
return "busy";
|
|
8800
|
+
}
|
|
8801
|
+
const t = this.#loadThrottle();
|
|
8802
|
+
const recent = (t.launchPorts ?? []).filter((x) => now - x < LAUNCH_PORT_WINDOW_MS && x <= now);
|
|
8803
|
+
if (recent.length >= LAUNCH_PORT_MAX) {
|
|
8804
|
+
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`);
|
|
8805
|
+
return "limited";
|
|
8806
|
+
}
|
|
8807
|
+
const app = this.appPath();
|
|
8808
|
+
if (!app) return "failed";
|
|
8809
|
+
if (this.#restarting || this.#waking) return "skipped";
|
|
8810
|
+
this.#restarting = true;
|
|
8811
|
+
this.#launchRestarting = true;
|
|
8812
|
+
this.#saveThrottle({ ...t, launchPorts: [...recent, now], lastLaunch: now });
|
|
8813
|
+
await this.#exec()("osascript", ["-e", `quit app ${JSON.stringify(b.appName)}`], 15e3);
|
|
8814
|
+
let gone = false;
|
|
8815
|
+
for (let waited = 0; waited < QUIT_WAIT_MS2; waited += 1e3) {
|
|
8816
|
+
if (!await this.appRunning()) {
|
|
8817
|
+
gone = true;
|
|
8818
|
+
break;
|
|
8819
|
+
}
|
|
8820
|
+
await this.#sleep(1e3);
|
|
8821
|
+
}
|
|
8822
|
+
if (!gone) {
|
|
8823
|
+
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`);
|
|
8824
|
+
return "failed";
|
|
8825
|
+
}
|
|
8826
|
+
const port = this.#d.port();
|
|
8827
|
+
const launch = this.#d.launch ?? defaultLaunch;
|
|
8828
|
+
const env = (this.#d.launchEnv ?? cleanLaunchEnv)();
|
|
8829
|
+
const ok = await launch("open", ["-a", app, "--args", `--remote-debugging-port=${port}`], env);
|
|
8830
|
+
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`);
|
|
8831
|
+
return ok ? "reopened" : "failed";
|
|
8832
|
+
}
|
|
8612
8833
|
/**
|
|
8613
8834
|
* 设备任务到达、App 不可用时唤起(spec §3.1)。不等端口起来(调用方等连上):
|
|
8614
8835
|
* - 端口已活 → running;
|
|
@@ -8616,9 +8837,9 @@ var QoderCdpKeeper = class {
|
|
|
8616
8837
|
* - App 开着没带端口:用户在场 → present、正在跑任务 → busy(都不动 App);用户不在场且空闲 → 退出重启 → restarted;
|
|
8617
8838
|
* - 没装 → not_installed;当天重启次数用完 / 拉不起来 → failed;非 macOS / 测试兜底 → skipped
|
|
8618
8839
|
*/
|
|
8619
|
-
async wakeForTask() {
|
|
8840
|
+
async wakeForTask(source = "task") {
|
|
8620
8841
|
if (this.#waking) return this.#waking;
|
|
8621
|
-
const p = this.#wake();
|
|
8842
|
+
const p = this.#wake(source);
|
|
8622
8843
|
this.#waking = p;
|
|
8623
8844
|
try {
|
|
8624
8845
|
return await p;
|
|
@@ -8626,7 +8847,7 @@ var QoderCdpKeeper = class {
|
|
|
8626
8847
|
if (this.#waking === p) this.#waking = null;
|
|
8627
8848
|
}
|
|
8628
8849
|
}
|
|
8629
|
-
async #wake() {
|
|
8850
|
+
async #wake(source) {
|
|
8630
8851
|
if (this.#skip() || !this.#canManage() || this.#d.noRestart) return "skipped";
|
|
8631
8852
|
if (this.#restarting) return "restarted";
|
|
8632
8853
|
const pr = await this.#probe();
|
|
@@ -8657,7 +8878,7 @@ var QoderCdpKeeper = class {
|
|
|
8657
8878
|
if (restarts >= RESTART_DAILY_MAX2) return "failed";
|
|
8658
8879
|
const app = this.appPath();
|
|
8659
8880
|
if (!app) return "not_installed";
|
|
8660
|
-
return await this.#restart(app, t, now, restarts, false) ? "restarted" : "failed";
|
|
8881
|
+
return await this.#restart(app, t, now, restarts, false, source) ? "restarted" : "failed";
|
|
8661
8882
|
}
|
|
8662
8883
|
/**
|
|
8663
8884
|
* 设备任务到达、端口在监听但无响应:空闲、用户不在场、已连续无响应满 HUNG_RESTART_MS、没超节流 → 结束并带端口重启(restarted);
|
|
@@ -8693,7 +8914,7 @@ var QoderCdpKeeper = class {
|
|
|
8693
8914
|
* hung=false(没带端口):正常退出 20 秒没反应就放弃——🔴 可能有确认弹窗,不强杀;
|
|
8694
8915
|
* hung=true(端口无响应、App 卡死):正常退出没反应时结束端口监听者(确认是这个 App 的主进程才动)
|
|
8695
8916
|
*/
|
|
8696
|
-
async #restart(app, t, now, restarts, hung) {
|
|
8917
|
+
async #restart(app, t, now, restarts, hung, source = "task") {
|
|
8697
8918
|
if (this.#restarting) return false;
|
|
8698
8919
|
this.#restarting = true;
|
|
8699
8920
|
const b = this.#d.brand;
|
|
@@ -8701,7 +8922,7 @@ var QoderCdpKeeper = class {
|
|
|
8701
8922
|
try {
|
|
8702
8923
|
this.#saveThrottle({ ...this.#loadThrottle(), lastRestart: now, day: localDay2(now), restarts: restarts + 1 });
|
|
8703
8924
|
this.#d.log(
|
|
8704
|
-
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`
|
|
8925
|
+
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`
|
|
8705
8926
|
);
|
|
8706
8927
|
const exec = this.#exec();
|
|
8707
8928
|
if (this.#isWin()) return await this.#restartWin(app, hung);
|
|
@@ -10510,7 +10731,7 @@ var defaultLoginOpenDeps = () => ({
|
|
|
10510
10731
|
});
|
|
10511
10732
|
async function installCodebuddyHooks(log) {
|
|
10512
10733
|
try {
|
|
10513
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
10734
|
+
const { installTools: installTools2 } = await import("./enable-UNMUBPWP.js");
|
|
10514
10735
|
installTools2(["workbuddy"], () => {
|
|
10515
10736
|
}, null, { auto: true });
|
|
10516
10737
|
} catch (e) {
|
|
@@ -11024,7 +11245,7 @@ function defaultOfferDeps(log) {
|
|
|
11024
11245
|
}),
|
|
11025
11246
|
openLogin: () => defaultOpenLogin(platform),
|
|
11026
11247
|
installHooks: async () => {
|
|
11027
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
11248
|
+
const { installTools: installTools2 } = await import("./enable-UNMUBPWP.js");
|
|
11028
11249
|
installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
|
|
11029
11250
|
},
|
|
11030
11251
|
log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|