@nowcrew/daemon 0.6.107-beta.1 → 0.6.107-beta.2

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.
@@ -311,6 +311,19 @@ export async function acquireProfileSaveLock(profilesDirectory, options = {}) {
311
311
  catch (error) {
312
312
  if (installed)
313
313
  throw error;
314
+ if (codeOf(error) === "EINVAL") {
315
+ // A competing release can remove the destination parent during rename.
316
+ // Retry only that observed race; preserve genuine invalid-rename errors.
317
+ const parentMissing = await fileSystem.stat(lockDirectory).then(() => false, (inspectionError) => {
318
+ if (codeOf(inspectionError) === "ENOENT")
319
+ return true;
320
+ throw inspectionError;
321
+ });
322
+ if (parentMissing) {
323
+ await pause();
324
+ continue;
325
+ }
326
+ }
314
327
  if (codeOf(error) === "ENOENT") {
315
328
  await pause();
316
329
  continue;
@@ -3,15 +3,28 @@ import { setTimeout as delay } from "node:timers/promises";
3
3
  import { MACHINE_ENVIRONMENT_MAX_BYTES, MachineEnvironmentCaptureError } from "./machine-environment-contract.js";
4
4
  /** 只接收本模块 detached spawn 所创建的进程组,绝不复用 supervisor 的 PID 清理接口。 */
5
5
  async function cleanupOwnedGroup(pid) {
6
- const exists = () => {
7
- try {
8
- process.kill(-pid, 0);
9
- return true;
10
- }
11
- catch (error) {
12
- if (error.code === "ESRCH")
13
- return false;
14
- throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
6
+ const exists = async () => {
7
+ const until = Date.now() + 1_000;
8
+ let inspectionDenied = false;
9
+ for (;;) {
10
+ try {
11
+ process.kill(-pid, 0);
12
+ if (!inspectionDenied)
13
+ return true;
14
+ }
15
+ catch (error) {
16
+ const code = error.code;
17
+ if (code === "ESRCH")
18
+ return false;
19
+ if (code !== "EPERM")
20
+ throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
21
+ inspectionDenied = true;
22
+ }
23
+ // After denied inspection, wait only for absence. Never signal a group
24
+ // whose identity may have changed, even if it becomes queryable again.
25
+ if (Date.now() >= until)
26
+ throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
27
+ await delay(20);
15
28
  }
16
29
  };
17
30
  const signal = (value) => {
@@ -23,14 +36,14 @@ async function cleanupOwnedGroup(pid) {
23
36
  throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
24
37
  }
25
38
  };
26
- if (!exists())
39
+ if (!await exists())
27
40
  return;
28
41
  for (const value of ["SIGTERM", "SIGKILL"]) {
29
42
  signal(value);
30
43
  const until = Date.now() + 1_000;
31
- while (exists() && Date.now() < until)
44
+ while (Date.now() < until && await exists())
32
45
  await delay(20);
33
- if (!exists())
46
+ if (!await exists())
34
47
  return;
35
48
  }
36
49
  throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.107-beta.1",
3
+ "version": "0.6.107-beta.2",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",