@dsh-sup/dsh-core-linux-x64 0.1.6-BETA.3 → 0.1.6-BETA.5

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.
Files changed (2) hide show
  1. package/core.cjs +134 -55
  2. package/package.json +1 -1
package/core.cjs CHANGED
@@ -929,7 +929,7 @@ var require_version = __commonJS({
929
929
  var fs2 = require("node:fs");
930
930
  var path2 = require("node:path");
931
931
  function guardVersion() {
932
- if (true) return String("0.1.6-BETA.3");
932
+ if (true) return String("0.1.6-BETA.5");
933
933
  try {
934
934
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
935
935
  } catch {
@@ -2038,8 +2038,6 @@ var require_specs = __commonJS({
2038
2038
  }
2039
2039
  function sandboxSpec(inst) {
2040
2040
  if (!inst || !inst.id) return null;
2041
- const phase = inst.state && inst.state.phase;
2042
- const running = phase === "RUNNING" || phase === "STARTING" || phase === "INSTALLING";
2043
2041
  let rootPath = null;
2044
2042
  const im = instances();
2045
2043
  try {
@@ -2050,7 +2048,7 @@ var require_specs = __commonJS({
2050
2048
  kind: "sandbox-instance",
2051
2049
  id: inst.id,
2052
2050
  name: String(inst.name || inst.id),
2053
- desired: running ? "running" : "stopped",
2051
+ desired: inst.state && inst.state.desired === "stopped" ? "stopped" : "running",
2054
2052
  guardian: inst.guardian === true,
2055
2053
  ownership: {
2056
2054
  ports: [{ role: "inst", port: Number(inst.port) }],
@@ -2060,13 +2058,12 @@ var require_specs = __commonJS({
2060
2058
  }
2061
2059
  };
2062
2060
  }
2063
- function upsert(spec, opts) {
2061
+ function upsert(spec) {
2064
2062
  const m = reg();
2065
2063
  if (!m || !spec) return;
2066
- const keepDesired = !!(opts && opts.keepDesired);
2067
2064
  try {
2068
2065
  const existing = m.get(spec.id);
2069
- if (existing) m.update(spec.id, { desired: keepDesired ? void 0 : spec.desired, guardian: spec.guardian, name: spec.name, ownership: spec.ownership });
2066
+ if (existing) m.update(spec.id, { desired: spec.desired, guardian: spec.guardian, name: spec.name, ownership: spec.ownership });
2070
2067
  else m.register(spec);
2071
2068
  } catch (e) {
2072
2069
  const l = logger();
@@ -2092,7 +2089,7 @@ var require_specs = __commonJS({
2092
2089
  const sandboxes = _m && typeof _m.all === "function" && _m.all() || [];
2093
2090
  for (const inst of sandboxes) {
2094
2091
  if (inst.id === "main" || inst.domain === "native") continue;
2095
- upsert(sandboxSpec(inst), { keepDesired: true });
2092
+ upsert(sandboxSpec(inst));
2096
2093
  }
2097
2094
  const c = ctl();
2098
2095
  upsert({
@@ -3726,6 +3723,18 @@ var require_pool = __commonJS({
3726
3723
  this._save();
3727
3724
  return p;
3728
3725
  }
3726
+ /** 登记固定端口,并保证该 role 在全表唯一(先清除同 role 的其它端口记录再登记)。
3727
+ * 登记表以端口号为键,避让/重绑后新端口是**追加**记录;只 release 旧端口不足以立住不变式
3728
+ * (该调用在内核侧被 catch{} 包住且忽略返回值),残留两条同 role 记录时任何「按 role 取号」
3729
+ * 的读法(本表 get、桌面壳读 ports.json)都可能拿到一个没人监听的端口。
3730
+ * @returns {number} port */
3731
+ registerSole(role, port) {
3732
+ const p = Number(port);
3733
+ for (const [existing, r] of [...this._records]) {
3734
+ if (r.role === role && existing !== p) this._records.delete(existing);
3735
+ }
3736
+ return this.register(role, p);
3737
+ }
3729
3738
  /** 登记用户配置端口(实例内部端口等);冲突(固定/保留池/已占)抛错。 */
3730
3739
  registerUser(port, owner) {
3731
3740
  const p = Number(port);
@@ -3760,10 +3769,15 @@ var require_pool = __commonJS({
3760
3769
  return true;
3761
3770
  }
3762
3771
  /* 查询 */
3763
- /** 按 role 取端口(固定端口)。 */
3772
+ /** 按 role 取端口(固定端口)。同 role 有多条(老版本避让留下的残留记录)时取**最新登记**:
3773
+ * 桌面壳读 ports.json 用的是同一判据,两侧不许对「哪个端口是当前的」给出不同答案。 */
3764
3774
  get(role) {
3765
- for (const r of this._records.values()) if (r.role === role) return r.port;
3766
- return null;
3775
+ let best = null;
3776
+ for (const r of this._records.values()) {
3777
+ if (r.role !== role) continue;
3778
+ if (!best || (r.createdAt || 0) >= (best.createdAt || 0)) best = r;
3779
+ }
3780
+ return best ? best.port : null;
3767
3781
  }
3768
3782
  isRegistered(port) {
3769
3783
  return this._records.has(Number(port));
@@ -3872,8 +3886,8 @@ var require_orphan_scan = __commonJS({
3872
3886
  const issues = [];
3873
3887
  try {
3874
3888
  const daemons = [
3875
- { kind: "router-daemon", port: g.getCtl().routerPort(), active: () => g.getDaemons().routerActive(), managed: () => g.getDaemons().managed(), want: () => g.getConfig().routerAutostart === true || !!(reg && reg.get("router-daemon") && reg.get("router-daemon").desired === "running") },
3876
- { kind: "lan-daemon", port: g.getCtl().lanPort(), active: () => g.getDaemons().lanActive(), managed: () => g.getDaemons().lanManaged(), want: () => g.getDaemons().enabled() || !!(reg && reg.get("lan-daemon") && reg.get("lan-daemon").desired === "running") }
3889
+ { kind: "router-daemon", port: g.getCtl().routerPort(), active: () => g.getDaemons().routerActive(), managed: () => g.getDaemons().managed(), want: () => g.getConfig().routerAutostart === true },
3890
+ { kind: "lan-daemon", port: g.getCtl().lanPort(), active: () => g.getDaemons().lanActive(), managed: () => g.getDaemons().lanManaged(), want: () => g.getDaemons().enabled() }
3877
3891
  ];
3878
3892
  for (const d of daemons) {
3879
3893
  if (!d.active()) continue;
@@ -6281,7 +6295,7 @@ var require_bootstrap = __commonJS({
6281
6295
  }
6282
6296
  function _registerFixedPorts(host2) {
6283
6297
  ports.register("dsh-main", host2.config.targetPort);
6284
- ports.register("supervisor-api", host2.config.apiPort);
6298
+ ports.registerSole("supervisor-api", host2.config.apiPort);
6285
6299
  }
6286
6300
  function _bindNativeDshCommand(host2) {
6287
6301
  try {
@@ -6365,9 +6379,9 @@ var require_api_rebind = __commonJS({
6365
6379
  bind._tries = 0;
6366
6380
  host2.api = server;
6367
6381
  try {
6368
- portsShared.register("supervisor-api", host2.config.apiPort);
6382
+ portsShared.registerSole("supervisor-api", host2.config.apiPort);
6369
6383
  } catch (e) {
6370
- host2.logger.warn("ports.register(supervisor-api) \u5931\u8D25: " + (e && e.message || e));
6384
+ host2.logger.warn("ports.registerSole(supervisor-api) \u5931\u8D25: " + (e && e.message || e));
6371
6385
  }
6372
6386
  host2.events.append("api_listening", { host: host2.config.apiHost, port: host2.config.apiPort });
6373
6387
  host2.logger.info("api listening on " + host2.config.apiHost + ":" + host2.config.apiPort);
@@ -6395,17 +6409,13 @@ var require_api_rebind = __commonJS({
6395
6409
  host2.api = server;
6396
6410
  const prev = host2.config.apiPort;
6397
6411
  if (port !== prev) {
6398
- try {
6399
- portsShared.release(prev, "system:supervisor-api");
6400
- } catch {
6401
- }
6402
6412
  host2.config.apiPort = port;
6403
6413
  if (host2.configPath) host2.persistConfigPatch({ apiPort: port });
6404
6414
  }
6405
6415
  try {
6406
- portsShared.register("supervisor-api", port);
6416
+ portsShared.registerSole("supervisor-api", port);
6407
6417
  } catch (e) {
6408
- host2.logger.warn("ports.register(actual) \u5931\u8D25: " + e.message);
6418
+ host2.logger.warn("ports.registerSole(supervisor-api) \u5931\u8D25: " + e.message);
6409
6419
  }
6410
6420
  host2.events.append("api_listening", { host: host2.config.apiHost, port });
6411
6421
  host2.logger.info("api listening on " + host2.config.apiHost + ":" + port);
@@ -7807,8 +7817,8 @@ var require_instance_adapter = __commonJS({
7807
7817
  return { ok: running, error: running ? null : "\u6C99\u7BB1\u5B9E\u4F8B\u672A\u8FD0\u884C" };
7808
7818
  },
7809
7819
  /** 目录项 <- 实例域状态对齐(监督拍后调用):实例已删 -> 注销(防死登记);存在 -> 经
7810
- * sandboxSpec 同步 name/guardian/ownership + phase 落目录词表。desired 不同步(keepDesired,
7811
- * 契约 M-1:观测推导不得写回应然,崩溃进 BACKOFF 不等于「用户想停」)。 */
7820
+ * sandboxSpec 同步 name/guardian/ownership + phase 落目录词表。desired 走的是实例意图字段的
7821
+ * 投影(不是 phase 推导),所以观测路径写它不会改写任何意图(契约 M-1)。 */
7812
7822
  _syncSandboxRegistryEntry(entry) {
7813
7823
  const d = depsOf(this);
7814
7824
  if (!entry || !d.managedObjects() || !d.instances()) return;
@@ -7819,7 +7829,7 @@ var require_instance_adapter = __commonJS({
7819
7829
  return;
7820
7830
  }
7821
7831
  try {
7822
- d.control().upsert(d.control().sandboxSpec(inst), { keepDesired: true });
7832
+ d.control().upsert(d.control().sandboxSpec(inst));
7823
7833
  } catch (e) {
7824
7834
  d.logger() && d.logger().warn && d.logger().warn("sandbox upsert: " + (e && e.message || e));
7825
7835
  }
@@ -9479,8 +9489,8 @@ var require_supervise = __commonJS({
9479
9489
  if (d.exitIntended()) return { ok: false, error: "exit intended" };
9480
9490
  try {
9481
9491
  if (kind === "router") {
9492
+ const wantRunning = d.config().routerAutostart === true;
9482
9493
  const rlc = d.lifecycleManager() ? d.lifecycleManager().get("router") : null;
9483
- const wantRunning = d.config().routerAutostart === true || rlc && rlc.desired === "running";
9484
9494
  if (!wantRunning) return { ok: d.daemons().routerActive() };
9485
9495
  if (!d.daemons().managed()) return { ok: d.daemons().routerActive() };
9486
9496
  const rlcx = d.daemons().lifecycle("router");
@@ -10204,7 +10214,7 @@ var require_identity = __commonJS({
10204
10214
  return null;
10205
10215
  }
10206
10216
  }
10207
- function pidAlive(pid) {
10217
+ function pidAlive2(pid) {
10208
10218
  if (!Number.isInteger(pid) || pid <= 0) return false;
10209
10219
  try {
10210
10220
  process.kill(pid, 0);
@@ -10236,7 +10246,7 @@ var require_identity = __commonJS({
10236
10246
  if (attempt()) return true;
10237
10247
  const holder = lockPid(p);
10238
10248
  if (holder === process.pid) return true;
10239
- if (holder !== null && pidAlive(holder)) return false;
10249
+ if (holder !== null && pidAlive2(holder)) return false;
10240
10250
  try {
10241
10251
  fs2.unlinkSync(p);
10242
10252
  } catch {
@@ -10274,7 +10284,7 @@ var require_identity = __commonJS({
10274
10284
  }
10275
10285
  module2.exports = {
10276
10286
  // 测试缝:锁的取/放/读主是纯 fs + kill(0) 语义,直接导出给回归用。
10277
- _lockPrimitives: { acquireLock: acquireLock2, releaseLock: releaseLock2, lockPid, pidAlive },
10287
+ _lockPrimitives: { acquireLock: acquireLock2, releaseLock: releaseLock2, lockPid, pidAlive: pidAlive2 },
10278
10288
  methods: {
10279
10289
  _lanLockPath() {
10280
10290
  const d = depsOf(this);
@@ -10897,7 +10907,7 @@ var require_router2 = __commonJS({
10897
10907
  config.routerAutostart = false;
10898
10908
  state.persistConfigPatch({ routerAutostart: false });
10899
10909
  if (rlc) {
10900
- rlc.desired = "stopped";
10910
+ rlc.wantStopped();
10901
10911
  rlc._monitoring = false;
10902
10912
  rlc._setPhase("stopped");
10903
10913
  rlc.healthy = false;
@@ -10908,7 +10918,7 @@ var require_router2 = __commonJS({
10908
10918
  config.routerAutostart = false;
10909
10919
  state.persistConfigPatch({ routerAutostart: false });
10910
10920
  if (rlc) {
10911
- rlc.desired = "stopped";
10921
+ rlc.wantStopped();
10912
10922
  rlc._monitoring = false;
10913
10923
  rlc._setPhase("stopped");
10914
10924
  rlc.healthy = false;
@@ -19237,6 +19247,10 @@ var require_model3 = __commonJS({
19237
19247
  inst.state.lastError = null;
19238
19248
  }
19239
19249
  if (inst.state) inst.state.phase = inst.state.phase || "STOPPED";
19250
+ if (inst.state && inst.state.desired === void 0) {
19251
+ const p = inst.state.phase;
19252
+ inst.state.desired = p === "RUNNING" || p === "STARTING" || p === "INSTALLING" ? "running" : "stopped";
19253
+ }
19240
19254
  return inst;
19241
19255
  }
19242
19256
  function createRecord(payload, id) {
@@ -19261,7 +19275,7 @@ var require_model3 = __commonJS({
19261
19275
  protectHome: payload.protectHome === void 0 ? false : !!payload.protectHome
19262
19276
  // 资源配额不接收户输入:启动时由 governor 按机器预算与活跃实例数推导。
19263
19277
  },
19264
- state: { phase: "STOPPED", restartCount: 0, backoffLevel: 0, lastProbeOk: null },
19278
+ state: { phase: "STOPPED", desired: "stopped", restartCount: 0, backoffLevel: 0, lastProbeOk: null },
19265
19279
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
19266
19280
  };
19267
19281
  }
@@ -19603,6 +19617,10 @@ var require_lifecycle = __commonJS({
19603
19617
  return { ok: false, error: adm.error };
19604
19618
  }
19605
19619
  }
19620
+ if (inst.state.desired !== "running") {
19621
+ inst.state.desired = "running";
19622
+ store.save();
19623
+ }
19606
19624
  store.ensureDirs(inst);
19607
19625
  const dshEntry = sandbox.dshEntry(instancesRoot, inst);
19608
19626
  if (!fs2.existsSync(dshEntry)) {
@@ -19613,7 +19631,8 @@ var require_lifecycle = __commonJS({
19613
19631
  }
19614
19632
  return _systemdStart(inst);
19615
19633
  }
19616
- function stop(id) {
19634
+ function stop(id, opts) {
19635
+ const transient = !!(opts && opts.intent === "transient");
19617
19636
  const inst = store.instances.find((i) => i.id === id);
19618
19637
  if (!inst) return { ok: false, error: "\u5B9E\u4F8B\u4E0D\u5B58\u5728" };
19619
19638
  if (!isSandboxSupported()) return { ok: false, error: "\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u6C99\u7BB1\u5B9E\u4F8B\uFF08\u80FD\u529B\u77E9\u9635\u89C1 GET /env/status \u7684 capabilities.sandboxLaunch\uFF1B\u9650\u989D\u6267\u884C\u6863\u4F4D\u89C1 capabilities.sandboxEnforcement\uFF09" };
@@ -19635,6 +19654,7 @@ var require_lifecycle = __commonJS({
19635
19654
  }
19636
19655
  inst.state.phase = "STOPPED";
19637
19656
  inst.state.usage = null;
19657
+ if (!transient) inst.state.desired = "stopped";
19638
19658
  runtime.delete(inst.id);
19639
19659
  store.save();
19640
19660
  if (inst.port && hooks.onInstanceStop) hooks.onInstanceStop(inst);
@@ -20138,7 +20158,7 @@ var require_upgrade = __commonJS({
20138
20158
  tasks.stepState(task.id, tasks.get(task.id).steps.indexOf(s), "running");
20139
20159
  }
20140
20160
  try {
20141
- await lifecycle.stop(id);
20161
+ await lifecycle.stop(id, { intent: "transient" });
20142
20162
  } catch {
20143
20163
  }
20144
20164
  if (task) {
@@ -20186,7 +20206,7 @@ var require_upgrade = __commonJS({
20186
20206
  tasks.log(task.id, "\u81EA\u52A8\u56DE\u6EDA\u5230 " + oldVersion + "\u2026");
20187
20207
  }
20188
20208
  try {
20189
- const rs = await lifecycle.stop(id);
20209
+ const rs = await lifecycle.stop(id, { intent: "transient" });
20190
20210
  if (rs && rs.ok === false && task) tasks.log(task.id, "\u56DE\u6EDA\u524D\u505C\u6B62\u5931\u8D25\uFF08\u7EE7\u7EED\u56DE\u88C5\u65E7\u7248\uFF09\uFF1A" + (rs.error || ""));
20191
20211
  } catch (e) {
20192
20212
  if (task) tasks.log(task.id, "\u56DE\u6EDA\u524D\u505C\u6B62\u5F02\u5E38\uFF08\u7EE7\u7EED\u56DE\u88C5\u65E7\u7248\uFF09\uFF1A" + (e && e.message || e));
@@ -20765,8 +20785,8 @@ var require_instance = __commonJS({
20765
20785
  startInstance(id, opts) {
20766
20786
  return this._lifecycle.start(id, opts);
20767
20787
  }
20768
- stopInstance(id) {
20769
- return this._lifecycle.stop(id);
20788
+ stopInstance(id, opts) {
20789
+ return this._lifecycle.stop(id, opts);
20770
20790
  }
20771
20791
  supervise(id) {
20772
20792
  return this._lifecycle.supervise(id);
@@ -21831,7 +21851,7 @@ var require_restart3 = __commonJS({
21831
21851
  log("\u91CD\u542F\u5B9E\u4F8B\u300C" + (target.name || target.id) + "\u300D\u4F7F\u63D2\u4EF6\u53D8\u66F4\u751F\u6548\u2026");
21832
21852
  if (ctx.events) ctx.events.append("plugin_restart_started", { name: target.name || target.id, target: target.id, kind });
21833
21853
  try {
21834
- ctx.instances.stopInstance(target.id);
21854
+ ctx.instances.stopInstance(target.id, { intent: "transient" });
21835
21855
  } catch (e) {
21836
21856
  log("\u505C\u6B62\u5B9E\u4F8B\u5931\u8D25: " + e.message);
21837
21857
  }
@@ -24066,6 +24086,7 @@ var require_entry = __commonJS({
24066
24086
  } catch (e) {
24067
24087
  this.error = e && e.message || String(e);
24068
24088
  this._setPhase("stopped");
24089
+ this.desired = "stopped";
24069
24090
  if (this.logger && this.logger.warn) this.logger.warn("[lifecycle] " + this.id + " start \u5931\u8D25: " + this.error);
24070
24091
  return { ok: false, error: this.error };
24071
24092
  }
@@ -24375,11 +24396,11 @@ var require_adapters = __commonJS({
24375
24396
  kind: "router",
24376
24397
  name: "\u667A\u80FD\u8DEF\u7531",
24377
24398
  logger,
24378
- start: async () => sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(true) : router.start(),
24399
+ start: async () => sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(true) : { ok: false, error: "\u7F3A setRouterRunning \u5199\u53E3\uFF1A\u542F\u505C\u4E0D\u5F97\u7ED5\u8FC7 config \u6301\u4E45\u5316" },
24379
24400
  // 守卫自身 shutdown(_stopping=true)不得停独立 daemon:守卫退出不影响被管模块,仅显式用户停止才停。
24380
24401
  stop: async () => {
24381
24402
  if (sup && sup._stopping) return { ok: true, already: true, reason: "guard-shutdown \u4E0D\u505C daemon" };
24382
- return sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(false) : router.stop();
24403
+ return sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(false) : { ok: false, error: "\u7F3A setRouterRunning \u5199\u53E3\uFF1A\u505C\u6B62\u987B\u540C\u65F6\u6E05 config \u6301\u4E45\u5316" };
24383
24404
  },
24384
24405
  // detail 走守卫真实 router 状态视图(daemon 模式经 ctl 取实时态)
24385
24406
  status: () => sup && typeof sup.routerStatus === "function" ? sup.routerStatus() : router.status ? router.status() : null
@@ -25987,7 +26008,8 @@ var require_static = __commonJS({
25987
26008
  ".png": "image/png",
25988
26009
  ".ico": "image/x-icon"
25989
26010
  };
25990
- var CSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'";
26011
+ var FRAME_ANCESTORS = "tauri://localhost http://tauri.localhost https://tauri.localhost";
26012
+ var CSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors " + FRAME_ANCESTORS;
25991
26013
  function serveStatic(res, file, corsOrigin) {
25992
26014
  if (!UI_DIR) {
25993
26015
  res.writeHead(503, { "Content-Type": "text/plain; charset=utf-8" });
@@ -27938,6 +27960,41 @@ function readLock() {
27938
27960
  function isOwnGuardEntry(cmdline) {
27939
27961
  return typeof cmdline === "string" && cmdline.includes("dsh-supervisor");
27940
27962
  }
27963
+ var LOCK_HEARTBEAT_MS = 2e3;
27964
+ var LOCK_STALE_MS = LOCK_HEARTBEAT_MS * 15;
27965
+ function lockSilentMs() {
27966
+ try {
27967
+ return Math.max(0, Date.now() - fs.statSync(LOCK_FILE).mtimeMs);
27968
+ } catch {
27969
+ return null;
27970
+ }
27971
+ }
27972
+ function pidAlive(pid) {
27973
+ try {
27974
+ process.kill(pid, 0);
27975
+ return true;
27976
+ } catch (e) {
27977
+ return e.code === "EPERM";
27978
+ }
27979
+ }
27980
+ var LOCK_TAKE_RETRY_MS = 8e3;
27981
+ var LOCK_TAKE_NAP_MS = 250;
27982
+ function napSync(ms) {
27983
+ try {
27984
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
27985
+ } catch {
27986
+ }
27987
+ }
27988
+ function startLockHeartbeat() {
27989
+ const t = setInterval(() => {
27990
+ try {
27991
+ const d = /* @__PURE__ */ new Date();
27992
+ fs.utimesSync(LOCK_FILE, d, d);
27993
+ } catch {
27994
+ }
27995
+ }, LOCK_HEARTBEAT_MS);
27996
+ if (t.unref) t.unref();
27997
+ }
27941
27998
  function acquireLock() {
27942
27999
  fs.mkdirSync(SUPERVISOR_DIR, { recursive: true });
27943
28000
  const tryCreate = () => {
@@ -27952,26 +28009,47 @@ function acquireLock() {
27952
28009
  process.exit(1);
27953
28010
  }
27954
28011
  };
27955
- if (tryCreate()) return true;
28012
+ const take = () => {
28013
+ if (!tryCreate()) return null;
28014
+ startLockHeartbeat();
28015
+ return true;
28016
+ };
28017
+ const first = take();
28018
+ if (first) return true;
27956
28019
  const held = readLock();
27957
28020
  if (held) {
27958
- let alive = false;
27959
- try {
27960
- process.kill(held.pid, 0);
27961
- alive = true;
27962
- } catch (err) {
27963
- alive = err.code === "EPERM";
28021
+ if (!pidAlive(held.pid)) {
28022
+ console.log("[supervisor] \u6E05\u7406\u9648\u65E7\u5B88\u536B\u9501\uFF08\u6301\u6709 pid " + held.pid + " \u5DF2\u9000\u51FA\uFF09");
28023
+ } else {
28024
+ const cmdline = readCmdline(held.pid);
28025
+ if (cmdline && isOwnGuardEntry(cmdline)) {
28026
+ const deadline = Date.now() + LOCK_TAKE_RETRY_MS;
28027
+ while (pidAlive(held.pid)) {
28028
+ if (Date.now() >= deadline) {
28029
+ return "\u6301\u6709 pid " + held.pid + " \u5B58\u6D3B\u3001\u547D\u4EE4\u884C\u662F\u672C\u4EA7\u54C1\u7684\u5B88\u536B\u5165\u53E3\uFF0C\u6709\u754C\u7B49\u5F85 " + Math.round(LOCK_TAKE_RETRY_MS / 1e3) + "s \u540E\u4ECD\u672A\u8BA9\u51FA";
28030
+ }
28031
+ napSync(LOCK_TAKE_NAP_MS);
28032
+ }
28033
+ console.log("[supervisor] \u524D\u4EFB\u5B88\u536B pid " + held.pid + " \u5728\u7B49\u5F85\u4E2D\u9000\u51FA -> \u63A5\u7BA1\u5B88\u536B\u9501");
28034
+ } else if (cmdline) {
28035
+ console.log("[supervisor] \u6E05\u7406\u9648\u65E7\u5B88\u536B\u9501\uFF08\u6301\u6709 pid " + held.pid + " \u5B58\u6D3B\u4F46\u547D\u4EE4\u884C\u4E0D\u662F\u672C\u4EA7\u54C1\u7684\u5B88\u536B\u5165\u53E3\uFF1A" + cmdline + "\uFF09");
28036
+ } else {
28037
+ const silent = lockSilentMs();
28038
+ if (silent !== null && silent >= LOCK_STALE_MS) {
28039
+ console.log("[supervisor] \u6E05\u7406\u9648\u65E7\u5B88\u536B\u9501\uFF08\u6301\u6709 pid " + held.pid + " \u5B58\u6D3B\u4F46\u547D\u4EE4\u884C\u8BFB\u4E0D\u5230\uFF0C\u4E14\u9501\u5DF2 " + Math.round(silent / 1e3) + "s \u672A\u7EED\u7EA6 -> \u5224\u5B9A\u539F\u6301\u6709\u8005\u5DF2\u6B7B\uFF09");
28040
+ } else {
28041
+ return "\u6301\u6709 pid " + held.pid + " \u5B58\u6D3B\u3001\u547D\u4EE4\u884C\u8BFB\u4E0D\u5230\uFF0C" + (silent === null ? "\u9501\u72B6\u6001\u4E0D\u53EF\u7EDF\u8BA1\uFF08\u4FDD\u5B88\u8BA9\u4F4D\uFF09" : "\u9501 " + Math.round(silent / 1e3) + "s \u524D\u4ECD\u5728\u7EED\u7EA6\uFF08\u4FDD\u5B88\u8BA9\u4F4D\uFF09");
28042
+ }
28043
+ }
27964
28044
  }
27965
- const cmdline = alive ? readCmdline(held.pid) : null;
27966
- if (alive && !cmdline) return false;
27967
- if (alive && isOwnGuardEntry(cmdline)) return false;
27968
- console.log("[supervisor] \u6E05\u7406\u9648\u65E7\u5B88\u536B\u9501\uFF08\u6301\u6709 pid " + held.pid + (alive ? " \u5B58\u6D3B\u4F46\u547D\u4EE4\u884C\u4E0D\u662F\u672C\u4EA7\u54C1\u7684\u5B88\u536B\u5165\u53E3\uFF1A" + cmdline : " \u5DF2\u9000\u51FA") + "\uFF09");
27969
28045
  }
27970
28046
  try {
27971
28047
  fs.unlinkSync(LOCK_FILE);
27972
28048
  } catch {
27973
28049
  }
27974
- return tryCreate();
28050
+ const again = take();
28051
+ if (again) return true;
28052
+ return "\u6E05\u7406\u9648\u65E7\u9501\u540E\u4ECD\u521B\u5EFA\u4E0D\u4E86\uFF08" + LOCK_FILE + " \u4E0D\u53EF\u5199\uFF1F\uFF09";
27975
28053
  }
27976
28054
  function releaseLock() {
27977
28055
  try {
@@ -27983,8 +28061,9 @@ function releaseLock() {
27983
28061
  function cmdDaemon() {
27984
28062
  const moved = stateRoot.migrateLegacy();
27985
28063
  for (const m of moved) console.log("[migrate] \u72B6\u6001\u76EE\u5F55\u8FC1\u79FB: " + m);
27986
- if (!acquireLock()) {
27987
- console.error("[supervisor] \u5DF2\u6709\u5B88\u536B\u5B9E\u4F8B\u5728\u8FD0\u884C\uFF08\u9501 " + LOCK_FILE + "\uFF09\uFF0C\u672C\u8FDB\u7A0B\u9000\u51FA\u3002");
28064
+ const lock = acquireLock();
28065
+ if (lock !== true) {
28066
+ console.error("[supervisor] \u672C\u8FDB\u7A0B\u9000\u51FA\uFF1A\u672A\u53D6\u5F97\u5B88\u536B\u9501\uFF08\u9501 " + LOCK_FILE + "\uFF09\u2014\u2014 " + lock);
27988
28067
  process.exit(1);
27989
28068
  }
27990
28069
  process.on("exit", releaseLock);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-sup/dsh-core-linux-x64",
3
- "version": "0.1.6-BETA.3",
3
+ "version": "0.1.6-BETA.5",
4
4
  "description": "DSH lifecycle guard core (Node launcher) for linux-x64 — requires Node >=18.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {