weapp-ide-cli 6.3.0 → 6.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.
@@ -1,4 +1,4 @@
1
- import { E as withPowerShellUtf8Output, T as inspectWechatDevtoolsInstallation, g as resolveWechatInspectionTimeout, h as resolveWechatDevtoolsInstallationRoot, m as assertWechatDevtoolsPort, p as assertWechatDevtoolsHost, u as resolveWechatDevtoolsTarget } from "./automatorProject-DjYLqMj6.js";
1
+ import { E as withPowerShellUtf8Output, T as inspectWechatDevtoolsInstallation, g as resolveWechatInspectionTimeout, h as resolveWechatDevtoolsInstallationRoot, m as assertWechatDevtoolsPort, p as assertWechatDevtoolsHost, u as resolveWechatDevtoolsTarget } from "./automatorProject-DPU-pEF0.js";
2
2
  import path from "node:path";
3
3
  import { TextDecoder, debuglog, isDeepStrictEqual, stripVTControlCharacters } from "node:util";
4
4
  import { assertMachineE2ELeaseRecoveryScope, mutateLease, withMachineE2ELease, withMachineE2ELeaseRecoveryOperation } from "@weapp-vite/devtools-runtime";
@@ -11,7 +11,7 @@ import net from "node:net";
11
11
  import { setTimeout } from "node:timers/promises";
12
12
  import { z } from "zod";
13
13
  import { performance as performance$1 } from "node:perf_hooks";
14
- //#region src/cli/agentStart.ts
14
+ // src/cli/agentStart.ts
15
15
  /** 提取独立的完整 JSON 对象,普通日志和嵌套对象不能替代命令响应。 */
16
16
  function readResponseObjects(stdout) {
17
17
  const objects = [];
@@ -116,8 +116,7 @@ async function startWechatIdeAgent(options) {
116
116
  }
117
117
  });
118
118
  }
119
- //#endregion
120
- //#region src/devtoolsProjectOwnership/installationExit/darwinKernelPaths.ts
119
+ // src/devtoolsProjectOwnership/installationExit/darwinKernelPaths.ts
121
120
  const inspectionTimeoutMs$1 = 1e4;
122
121
  const kernelPathsScript = `
123
122
  ObjC.import('Foundation');
@@ -174,66 +173,94 @@ async function readDarwinKernelPaths(pids, timeoutMs = inspectionTimeoutMs$1) {
174
173
  if (entries.size !== pids.length || parsed.length !== pids.length || pids.some((pid) => !entries.has(pid))) throw new Error("Installation-exit recovery received incomplete kernel process identities.");
175
174
  return entries;
176
175
  }
177
- //#endregion
178
- //#region src/devtoolsProjectOwnership/darwinProcessIdentity.ts
176
+ // src/devtoolsProjectOwnership/darwinProcessIdentity.ts
179
177
  const inspectionTimeoutMs = 3e3;
180
- function invalidIdentity$2() {
181
- return /* @__PURE__ */ new Error("Managed DevTools process identity could not be verified; no project was closed.");
182
- }
183
- async function readStarted(pids, remaining) {
178
+ function invalidIdentity$2(stage, reason, details = {}, cause) {
179
+ return new Error(`Managed DevTools process identity could not be verified; no project was closed. ${JSON.stringify({
180
+ stage,
181
+ reason,
182
+ ...details
183
+ })}`, { cause });
184
+ }
185
+ async function readStarted(pids, remaining, stage) {
184
186
  const result = await execa("/bin/ps", [
185
187
  "-p",
186
188
  pids.join(","),
187
189
  "-o",
188
- "pid=,lstart="
190
+ "pid=,stat=,lstart="
189
191
  ], {
190
192
  timeout: remaining(),
191
193
  reject: false,
192
194
  env: { LC_ALL: "C" }
193
195
  });
194
196
  remaining();
195
- if (result.timedOut || result.signal || result.stderr?.trim() || result.exitCode === 0 && !result.stdout.trim() || result.exitCode !== 0 && !(result.exitCode === 1 && !result.stdout.trim())) throw invalidIdentity$2();
197
+ if (result.timedOut || result.signal || result.stderr?.trim() || result.exitCode === 0 && !result.stdout.trim() || result.exitCode !== 0 && !(result.exitCode === 1 && !result.stdout.trim())) throw invalidIdentity$2(stage, "birth-query-failed", { requestedCount: pids.length });
196
198
  const started = /* @__PURE__ */ new Map();
197
199
  for (const line of result.stdout.split(/\r?\n/).filter((line) => line.trim())) {
198
- const match = /^\s*(\d+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s*$/.exec(line);
200
+ const match = /^\s*(\d+)\s+(\S+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s*$/.exec(line);
199
201
  const pid = Number(match?.[1]);
200
- if (!match || !pids.includes(pid) || started.has(pid)) throw invalidIdentity$2();
201
- started.set(pid, match[2].replace(/\s+/g, " "));
202
+ if (!match || !pids.includes(pid) || started.has(pid)) throw invalidIdentity$2(stage, "invalid-birth-inventory", { requestedCount: pids.length });
203
+ started.set(pid, {
204
+ started: match[3].replace(/\s+/g, " "),
205
+ zombie: match[2].startsWith("Z")
206
+ });
202
207
  }
203
208
  return started;
204
209
  }
205
210
  /** 批量读取内核路径,并以前后启动时间绑定 PID;进程标题变化不改变身份。 */
206
211
  async function readDarwinManagedProcessIdentities(pids) {
207
- if (pids.some((pid) => !Number.isSafeInteger(pid) || pid <= 0)) throw invalidIdentity$2();
212
+ if (pids.some((pid) => !Number.isSafeInteger(pid) || pid <= 0)) throw invalidIdentity$2("request", "invalid-pid");
208
213
  const requested = [...new Set(pids)];
209
214
  const identities = new Map(requested.map((pid) => [pid, void 0]));
210
215
  if (!requested.length) return identities;
211
216
  const deadline = performance.now() + inspectionTimeoutMs;
217
+ let stage = "before";
212
218
  const remaining = () => {
213
219
  const timeout = Math.floor(deadline - performance.now());
214
- if (timeout <= 0) throw invalidIdentity$2();
220
+ if (timeout <= 0) throw invalidIdentity$2(stage, "deadline-exceeded", { requestedCount: requested.length });
215
221
  return timeout;
216
222
  };
217
- const before = await readStarted(requested, remaining);
223
+ const before = await readStarted(requested, remaining, "before");
218
224
  if (!before.size) return identities;
219
- const paths = await readDarwinKernelPaths([...before.keys()], remaining());
220
- const after = await readStarted(requested, remaining);
225
+ stage = "kernel";
226
+ const paths = await readDarwinKernelPaths([...before.keys()], remaining()).catch((cause) => {
227
+ throw invalidIdentity$2("kernel", "kernel-query-failed", { requestedCount: before.size }, cause);
228
+ });
229
+ stage = "after";
230
+ const after = await readStarted(requested, remaining, "after");
221
231
  for (const pid of requested) {
222
- const started = after.get(pid);
223
- if (!started) continue;
232
+ const current = after.get(pid);
233
+ if (!current) continue;
224
234
  const executable = paths.get(pid);
225
- if (before.get(pid) !== started || !executable?.startsWith("/")) throw invalidIdentity$2();
235
+ const previous = before.get(pid);
236
+ if (previous?.started !== current.started) throw invalidIdentity$2("after", "birth-changed", {
237
+ pid,
238
+ beforePresent: before.has(pid),
239
+ afterPresent: true
240
+ });
241
+ if (previous.zombie && current.zombie) continue;
242
+ if (previous.zombie !== current.zombie) throw invalidIdentity$2("after", "terminal-state-changed", {
243
+ pid,
244
+ beforeZombie: previous.zombie,
245
+ afterZombie: current.zombie
246
+ });
247
+ if (!executable?.startsWith("/")) throw invalidIdentity$2("after", "kernel-path-unavailable", {
248
+ pid,
249
+ beforePresent: true,
250
+ afterPresent: true,
251
+ kernelPath: executable ? "non-absolute" : "empty"
252
+ });
226
253
  identities.set(pid, {
227
254
  pid,
228
- started,
255
+ started: current.started,
229
256
  executable
230
257
  });
231
258
  }
259
+ stage = "complete";
232
260
  remaining();
233
261
  return identities;
234
262
  }
235
- //#endregion
236
- //#region src/devtoolsProjectOwnership/processIdentity.ts
263
+ // src/devtoolsProjectOwnership/processIdentity.ts
237
264
  const inspectionOptions$2 = {
238
265
  timeout: 3e3,
239
266
  reject: false,
@@ -308,8 +335,7 @@ async function readManagedProcessIdentity(pid, platform = process.platform) {
308
335
  function sameManagedProcess(first, second) {
309
336
  return first.pid === second.pid && first.started === second.started && first.executable === second.executable;
310
337
  }
311
- //#endregion
312
- //#region src/devtoolsProjectOwnership/host.ts
338
+ // src/devtoolsProjectOwnership/host.ts
313
339
  const inspectionOptions$1 = {
314
340
  timeout: 3e3,
315
341
  reject: false,
@@ -377,8 +403,7 @@ async function waitForManagedPortClosed(port, timeoutMs = 1e4) {
377
403
  } while (Date.now() < deadline);
378
404
  throw new Error("Managed DevTools project close returned but its automator port remained open.");
379
405
  }
380
- //#endregion
381
- //#region src/devtoolsProjectOwnership/installationExit/schema.ts
406
+ // src/devtoolsProjectOwnership/installationExit/schema.ts
382
407
  const installationExitRecoverySchema = z.object({
383
408
  protocol: z.literal("wechat-devtools-installation-exit-v1"),
384
409
  recoveredAt: z.string().datetime(),
@@ -412,8 +437,7 @@ const installationExitRecoverySchema = z.object({
412
437
  selectedProcessCount: z.literal(0)
413
438
  }).strict()
414
439
  }).strict();
415
- //#endregion
416
- //#region src/devtoolsProjectOwnership/journal/scope.ts
440
+ // src/devtoolsProjectOwnership/journal/scope.ts
417
441
  const markerName = ".ownership-scope";
418
442
  const scopeSchema = z.object({
419
443
  schemaVersion: z.literal(1),
@@ -466,8 +490,7 @@ async function resolveManagedJournalScope(directory) {
466
490
  if (path.resolve(rootPath, relative) !== currentPath) throw new Error("Managed DevTools journal path changed from its registered task children.");
467
491
  return scope;
468
492
  }
469
- //#endregion
470
- //#region src/devtoolsProjectOwnership/journal/windowsSelfIdentity/wire.ts
493
+ // src/devtoolsProjectOwnership/journal/windowsSelfIdentity/wire.ts
471
494
  const SELF_IDENTITY_WIRE = "WEAPP_JOURNAL_SELF_V1";
472
495
  const epochTicks = 621355968000000000n;
473
496
  const maximumTicks = 3155378975999999999n;
@@ -501,8 +524,7 @@ function parseWindowsSelfIdentity(stdout, expectedPid) {
501
524
  started
502
525
  };
503
526
  }
504
- //#endregion
505
- //#region src/devtoolsProjectOwnership/journal/windowsSelfIdentity/command.ts
527
+ // src/devtoolsProjectOwnership/journal/windowsSelfIdentity/command.ts
506
528
  /** 当前 writer 的 PID 由 Node 进程提供;脚本只读查询,不执行任何进程或 IDE 清理。 */
507
529
  function windowsSelfIdentityCommand(pid) {
508
530
  if (!Number.isSafeInteger(pid) || pid <= 0) throw new TypeError("A positive writer PID is required.");
@@ -542,8 +564,7 @@ finally {
542
564
  exit $writerExitCode
543
565
  `;
544
566
  }
545
- //#endregion
546
- //#region src/devtoolsProjectOwnership/journal/windowsSelfIdentity/trace.ts
567
+ // src/devtoolsProjectOwnership/journal/windowsSelfIdentity/trace.ts
547
568
  const marker = `${SELF_IDENTITY_WIRE}:phase:`;
548
569
  const statements = /* @__PURE__ */ new Map([
549
570
  ["$writerFirst=[System.Diagnostics.Process]", "lookup-before"],
@@ -590,8 +611,7 @@ function observeWindowsSelfIdentityPhases(report) {
590
611
  function stripWindowsSelfIdentityPhases(stderr) {
591
612
  return stderr.replace(phaseLines, "");
592
613
  }
593
- //#endregion
594
- //#region src/devtoolsProjectOwnership/journal/windowsSelfIdentity/index.ts
614
+ // src/devtoolsProjectOwnership/journal/windowsSelfIdentity/index.ts
595
615
  const debug = debuglog("weapp-ide-journal-writer");
596
616
  /** 仅核验当前 Node 写入进程;不能用于任意宿主、旧锁存活判断或进程清理。 */
597
617
  async function readWindowsJournalWriterIdentity() {
@@ -626,8 +646,7 @@ async function readWindowsJournalWriterIdentity() {
626
646
  }
627
647
  return parseWindowsSelfIdentity(result.stdout, process.pid);
628
648
  }
629
- //#endregion
630
- //#region src/devtoolsProjectOwnership/journal/writerIdentity.ts
649
+ // src/devtoolsProjectOwnership/journal/writerIdentity.ts
631
650
  let currentIdentity$1;
632
651
  /** 共享在途与成功身份;失败仍拒绝当前调用,仅允许下一次显式操作重新核验。 */
633
652
  function readManagedJournalWriterIdentity() {
@@ -643,8 +662,7 @@ function readManagedJournalWriterIdentity() {
643
662
  }
644
663
  return currentIdentity$1;
645
664
  }
646
- //#endregion
647
- //#region src/devtoolsProjectOwnership/snapshot/schema.ts
665
+ // src/devtoolsProjectOwnership/snapshot/schema.ts
648
666
  const managedSnapshotSchema = z.object({
649
667
  protocol: z.literal("wechat-devtools-project-snapshot-v1"),
650
668
  sourceProjectPath: z.string().min(1),
@@ -663,8 +681,7 @@ const managedSnapshotSchema = z.object({
663
681
  removedAt: z.string().datetime().optional(),
664
682
  cleanupError: z.string().optional()
665
683
  }).strict();
666
- //#endregion
667
- //#region src/devtoolsProjectOwnership/windowClose/schema.ts
684
+ // src/devtoolsProjectOwnership/windowClose/schema.ts
668
685
  const text = z.string().min(1);
669
686
  const cursorSchema = z.object({
670
687
  name: z.string().regex(/^[^/\\]+\.log$/),
@@ -705,8 +722,7 @@ const managedWindowCloseSchema = z.object({
705
722
  cursors: z.array(cursorSchema).min(1)
706
723
  }).optional()
707
724
  });
708
- //#endregion
709
- //#region src/devtoolsProjectOwnership/journal.ts
725
+ // src/devtoolsProjectOwnership/journal.ts
710
726
  const managedProcessToken = randomUUID();
711
727
  const idSchema = z.string().uuid();
712
728
  const hostSchema = z.object({
@@ -790,16 +806,15 @@ async function writeManagedRecord(record) {
790
806
  await fs.rm(temporary, { force: true });
791
807
  }
792
808
  }
793
- /** 仅递归当前任务明确传递的 children;不扫描用户缓存,不跟随符号链接。 */
794
- async function readManagedWechatProjectRecords(journalPath) {
795
- const directory = resolveManagedJournal(journalPath);
796
- if (!directory) return [];
809
+ async function readManagedJournalDirectory(directory, parentScope) {
810
+ if (parentScope && !(await fs.lstat(directory)).isDirectory()) throw new Error("Managed DevTools journal scope must use regular directories.");
797
811
  const entries = await fs.readdir(directory, { withFileTypes: true }).catch((error) => {
798
- if (error.code === "ENOENT") return;
812
+ if (error.code === "ENOENT" && !parentScope) return;
799
813
  throw error;
800
814
  });
801
815
  if (!entries) return [];
802
816
  const scope = await resolveManagedJournalScope(directory);
817
+ if (parentScope && (parentScope.scopeId !== void 0 || scope.scopeId !== void 0) && (parentScope.scopeId !== scope.scopeId || parentScope.rootPath !== scope.rootPath)) throw new Error("Managed DevTools journal scope changed inside its registered children; refusing another task tree.");
803
818
  const records = [];
804
819
  for (const entry of entries) if (entry.name.endsWith(".json")) records.push(await readManagedRecord(directory, entry.name.slice(0, -5)));
805
820
  else if (entry.name === "children") {
@@ -808,13 +823,17 @@ async function readManagedWechatProjectRecords(journalPath) {
808
823
  for (const child of children) {
809
824
  if (!child.isDirectory()) throw new Error("Managed DevTools child journal is not a regular directory.");
810
825
  const childPath = path.join(directory, "children", child.name);
811
- const childScope = await resolveManagedJournalScope(childPath);
812
- if ((scope.scopeId !== void 0 || childScope.scopeId !== void 0) && (scope.scopeId !== childScope.scopeId || scope.rootPath !== childScope.rootPath)) throw new Error("Managed DevTools journal scope changed inside its registered children; refusing another task tree.");
813
- records.push(...await readManagedWechatProjectRecords(childPath));
826
+ records.push(...await readManagedJournalDirectory(childPath, scope));
814
827
  }
815
828
  }
816
829
  return records;
817
830
  }
831
+ /** 仅递归当前任务明确传递的 children;不扫描用户缓存,不跟随符号链接。 */
832
+ async function readManagedWechatProjectRecords(journalPath) {
833
+ const directory = resolveManagedJournal(journalPath);
834
+ if (!directory) return [];
835
+ return await readManagedJournalDirectory(directory);
836
+ }
818
837
  const lockOwnerSchema = z.object({
819
838
  token: idSchema,
820
839
  identity: hostSchema
@@ -890,8 +909,7 @@ async function withManagedJournalLock(directory, run) {
890
909
  return await run(scope.rootPath);
891
910
  });
892
911
  }
893
- //#endregion
894
- //#region src/devtoolsProjectOwnership/snapshot/index.ts
912
+ // src/devtoolsProjectOwnership/snapshot/index.ts
895
913
  const MARKER = ".weapp-ide-snapshot-owner";
896
914
  function managedSnapshotPath(journalPath, id) {
897
915
  return path.join(journalPath, "snapshots", id);
@@ -994,8 +1012,7 @@ async function finalizeManagedProjectSnapshot(record) {
994
1012
  throw error;
995
1013
  }
996
1014
  }
997
- //#endregion
998
- //#region src/devtoolsProjectOwnership/windowBudget.ts
1015
+ // src/devtoolsProjectOwnership/windowBudget.ts
999
1016
  function maximumOwnedWindows(options) {
1000
1017
  const selected = options.maxOwnedWindows ?? process.env["WEAPP_IDE_MANAGED_PROJECT_MAX_WINDOWS"] ?? 1;
1001
1018
  if (selected !== 1 && selected !== 2 && selected !== "1" && selected !== "2") throw new Error("Managed DevTools maximum owned windows must be 1 or 2.");
@@ -1012,8 +1029,7 @@ function checkManagedWindowBudget(records, options) {
1012
1029
  const owned = records.filter((record) => record.state === "owned").length;
1013
1030
  if (owned >= maximum) throw new Error(`Managed DevTools window budget exhausted (${owned}/${maximum}); close the previous owned project before another start.`);
1014
1031
  }
1015
- //#endregion
1016
- //#region src/devtoolsProjectOwnership/windowClose/logCursor.ts
1032
+ // src/devtoolsProjectOwnership/windowClose/logCursor.ts
1017
1033
  const readLimit = 2097152;
1018
1034
  const scanLimit = 33554432;
1019
1035
  const scanChunkSize = 65536;
@@ -1217,8 +1233,7 @@ async function consumeFreshLogLines(directory, cursors, consume) {
1217
1233
  await Promise.all(streams.map(({ file }) => file.close()));
1218
1234
  }
1219
1235
  }
1220
- //#endregion
1221
- //#region src/devtoolsProjectOwnership/windowClose/activeLog.ts
1236
+ // src/devtoolsProjectOwnership/windowClose/activeLog.ts
1222
1237
  const inspectionOptions = {
1223
1238
  timeout: 3e3,
1224
1239
  reject: false,
@@ -1377,8 +1392,7 @@ async function captureActiveMainLogCursors(record, directory, platform = process
1377
1392
  host: main
1378
1393
  };
1379
1394
  }
1380
- //#endregion
1381
- //#region src/devtoolsProjectOwnership/windowClose/protocol.ts
1395
+ // src/devtoolsProjectOwnership/windowClose/protocol.ts
1382
1396
  function decodeString(value) {
1383
1397
  const quote = value[0];
1384
1398
  if (!quote || ![
@@ -1519,8 +1533,7 @@ function consumeWindowCloseTrace(evidence, projectPath, input) {
1519
1533
  }
1520
1534
  }
1521
1535
  }
1522
- //#endregion
1523
- //#region src/devtoolsProjectOwnership/windowClose.ts
1536
+ // src/devtoolsProjectOwnership/windowClose.ts
1524
1537
  /** 只捕获本次显式安装的日志,必须先于关闭命令持久化。 */
1525
1538
  async function captureManagedWindowClose(record) {
1526
1539
  if (!record.target.version) throw new Error("Managed DevTools window-close evidence requires the selected product version.");
@@ -1581,8 +1594,7 @@ async function waitForManagedWindowClosed(record, persist, options = {}) {
1581
1594
  } while (true);
1582
1595
  throw new Error("Managed DevTools project close has no complete native-window destruction evidence; stop the acceptance lane.");
1583
1596
  }
1584
- //#endregion
1585
- //#region src/devtoolsProjectOwnership/installationExit/darwin.ts
1597
+ // src/devtoolsProjectOwnership/installationExit/darwin.ts
1586
1598
  const options = {
1587
1599
  timeout: 1e4,
1588
1600
  reject: false,
@@ -1611,11 +1623,15 @@ function decodeTextImagePath(wire) {
1611
1623
  return textImageDecoder.decode(bytes.subarray(0, length));
1612
1624
  }
1613
1625
  /** ps 仅用于 PID、启动时间和僵尸状态,不能用可修改的 comm 代替实际可执行文件。 */
1614
- async function readDarwinProcessStates() {
1615
- const inspection = execa("/bin/ps", ["-axo", "pid=,stat=,lstart="], options);
1626
+ async function readDarwinProcessStates(timeoutMs = options.timeout) {
1627
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new Error("Installation-exit recovery cannot inspect the process inventory without a positive timeout.");
1628
+ const inspection = execa("/bin/ps", ["-axo", "pid=,stat=,lstart="], {
1629
+ ...options,
1630
+ timeout: Math.min(options.timeout, timeoutMs)
1631
+ });
1616
1632
  const inspectorPid = inspection.pid;
1617
1633
  const result = await inspection;
1618
- if (result.exitCode !== 0 || result.stderr?.trim() || !result.stdout.trim()) throw new Error("Installation-exit recovery could not read the complete process inventory.");
1634
+ if (result.exitCode !== 0 || result.timedOut || result.signal || result.stderr?.trim() || !result.stdout.trim()) throw new Error("Installation-exit recovery could not read the complete process inventory.");
1619
1635
  const records = result.stdout.trim().split(/\r?\n/).map((line) => {
1620
1636
  const match = /^\s*(\d+)\s+(\S+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s*$/.exec(line);
1621
1637
  const pid = Number(match?.[1]);
@@ -1657,24 +1673,37 @@ async function readDarwinTextImages(pid, timeoutMs = options.timeout) {
1657
1673
  }
1658
1674
  return paths;
1659
1675
  }
1660
- //#endregion
1661
- //#region src/devtoolsProjectOwnership/installationExit/processes.ts
1676
+ // src/devtoolsProjectOwnership/installationExit/processes.ts
1662
1677
  /** 仅支持已验证的 macOS 全安装清单;未知平台、权限错误或不完整映像均拒绝推断退出。 */
1663
- async function inspectExitedWechatInstallation(target, platform = process.platform) {
1678
+ async function inspectExitedWechatInstallation(target, platform = process.platform, options = {}) {
1664
1679
  if (platform !== "darwin") throw new Error("Installation-exit recovery requires the verified macOS process inventory.");
1680
+ const timeoutMs = options.timeoutMs ?? 3e4;
1681
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new Error("Installation-exit recovery requires a positive process inventory timeout.");
1682
+ const deadline = performance.now() + Math.min(timeoutMs, 3e4);
1683
+ const remaining = () => {
1684
+ const timeout = Math.floor(deadline - performance.now());
1685
+ if (timeout <= 0) throw new Error("Installation-exit recovery process inventory deadline expired; continuing new process activity or incomplete inspection prevents proving exit.");
1686
+ return timeout;
1687
+ };
1688
+ const readStates = async () => {
1689
+ const states = await readDarwinProcessStates(remaining());
1690
+ remaining();
1691
+ return states;
1692
+ };
1665
1693
  const selectedRoot = resolveWechatDevtoolsInstallationRoot(target, platform);
1666
1694
  if (!selectedRoot || !path.isAbsolute(selectedRoot) || !selectedRoot.endsWith(".app") || !target.appPath.includes("/Contents/")) throw new Error("Installation-exit recovery cannot resolve the selected application bundle.");
1667
1695
  const installationRoot = await fs.realpath(selectedRoot);
1668
- let pending = await readDarwinProcessStates();
1696
+ let pending = await readStates();
1669
1697
  const verified = /* @__PURE__ */ new Map();
1670
1698
  let kernelPathProcessCount = 0;
1671
1699
  let textImageProcessCount = 0;
1672
1700
  let exitedProcessCount = 0;
1673
1701
  let zombieProcessCount = 0;
1674
- for (let round = 0; round < 3; round++) {
1675
- const kernelPaths = await readDarwinKernelPaths(pending.map((entry) => entry.pid));
1676
- const after = new Map((await readDarwinProcessStates()).map((entry) => [entry.pid, entry]));
1702
+ while (true) {
1703
+ const kernelPaths = await readDarwinKernelPaths(pending.map((entry) => entry.pid), remaining());
1704
+ const after = new Map((await readStates()).map((entry) => [entry.pid, entry]));
1677
1705
  for (const entry of pending) {
1706
+ remaining();
1678
1707
  const current = after.get(entry.pid);
1679
1708
  if (!current) {
1680
1709
  exitedProcessCount++;
@@ -1689,9 +1718,9 @@ async function inspectExitedWechatInstallation(target, platform = process.platfo
1689
1718
  const executable = kernelPaths.get(entry.pid);
1690
1719
  let images;
1691
1720
  try {
1692
- images = executable ? [executable] : await readDarwinTextImages(entry.pid);
1721
+ images = executable ? [executable] : await readDarwinTextImages(entry.pid, remaining());
1693
1722
  } catch (error) {
1694
- const latest = await readDarwinProcessStates().then((states) => states.find((process) => process.pid === entry.pid), (confirmationError) => {
1723
+ const latest = await readStates().then((states) => states.find((process) => process.pid === entry.pid), (confirmationError) => {
1695
1724
  throw new AggregateError([error, confirmationError], "Installation-exit recovery cannot independently confirm process exit after failed image inspection.");
1696
1725
  });
1697
1726
  if (!latest) {
@@ -1701,6 +1730,7 @@ async function inspectExitedWechatInstallation(target, platform = process.platfo
1701
1730
  if (latest.started !== entry.started) throw new Error("Installation-exit recovery process identity changed during inspection.", { cause: error });
1702
1731
  throw error;
1703
1732
  }
1733
+ remaining();
1704
1734
  if (!images.length) throw new Error("Installation-exit recovery received no verifiable executable text images.");
1705
1735
  for (const image of images) {
1706
1736
  if (!path.isAbsolute(image) || /[\r\n\0]/.test(image)) throw new Error("Installation-exit recovery received an unverifiable executable image path.");
@@ -1714,7 +1744,7 @@ async function inspectExitedWechatInstallation(target, platform = process.platfo
1714
1744
  zombie: false
1715
1745
  });
1716
1746
  }
1717
- const final = await readDarwinProcessStates();
1747
+ const final = await readStates();
1718
1748
  for (const entry of final) {
1719
1749
  const previous = verified.get(entry.pid);
1720
1750
  if (previous && (previous.started !== entry.started || previous.zombie && !entry.zombie)) throw new Error("Installation-exit recovery process identity changed during inspection.");
@@ -1722,7 +1752,7 @@ async function inspectExitedWechatInstallation(target, platform = process.platfo
1722
1752
  pending = final.filter((entry) => !verified.has(entry.pid));
1723
1753
  if (!pending.length) break;
1724
1754
  }
1725
- if (pending.length) throw new Error("Installation-exit recovery found continuing new process activity; retry after it becomes idle.");
1755
+ remaining();
1726
1756
  return {
1727
1757
  platform,
1728
1758
  installationRoot,
@@ -1735,8 +1765,7 @@ async function inspectExitedWechatInstallation(target, platform = process.platfo
1735
1765
  selectedProcessCount: 0
1736
1766
  };
1737
1767
  }
1738
- //#endregion
1739
- //#region src/devtoolsProjectOwnership/installationExit/index.ts
1768
+ // src/devtoolsProjectOwnership/installationExit/index.ts
1740
1769
  async function fingerprint(record) {
1741
1770
  const file = managedRecordPath(record.journalPath, record.id);
1742
1771
  const stat = await fs.lstat(file);
@@ -1827,8 +1856,7 @@ async function recoverAfterExit(options) {
1827
1856
  function recoverManagedWechatProjectsAfterInstallationExit(options) {
1828
1857
  return withMachineE2ELeaseRecoveryOperation(options.recoveryScope, () => recoverAfterExit(options));
1829
1858
  }
1830
- //#endregion
1831
- //#region src/devtoolsProjectOwnership/index.ts
1859
+ // src/devtoolsProjectOwnership/index.ts
1832
1860
  const pendingClosures = /* @__PURE__ */ new Map();
1833
1861
  function describe(error) {
1834
1862
  return error instanceof Error ? error.message : String(error);
@@ -2018,5 +2046,4 @@ async function cleanupManagedWechatProjects(options = {}) {
2018
2046
  }
2019
2047
  if (errors.length) throw new AggregateError(errors, "Managed DevTools project cleanup remains incomplete; stop the acceptance lane.");
2020
2048
  }
2021
- //#endregion
2022
2049
  export { isManagedWechatProjectFullyReleased as a, resolveManagedJournal as c, assertManagedInstallation as d, inspectManagedProjectHost as f, recoverManagedWechatProjectsAfterInstallationExit as i, withManagedJournalLock as l, startWechatIdeAgent as m, cleanupManagedWechatProjects as n, readManagedRecord as o, sameManagedProcess as p, closeManagedWechatProject as r, readManagedWechatProjectRecords as s, beginManagedWechatProject as t, writeManagedRecord as u };