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

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 +404 -341
  2. package/package.json +1 -1
package/core.cjs CHANGED
@@ -60,11 +60,7 @@ var require_exec = __commonJS({
60
60
  function runAsync(bin, args, opts) {
61
61
  const o = opts || {};
62
62
  return new Promise((resolve) => {
63
- execFile(bin, args, Object.assign({}, options(o), { encoding: "utf8" }), (err, stdout, stderr) => {
64
- if (!err) {
65
- resolve({ ok: true, code: "0", stdout: String(stdout == null ? "" : stdout), stderr: String(stderr == null ? "" : stderr), timedOut: false, error: null });
66
- return;
67
- }
63
+ const fail = (err) => {
68
64
  if (o.logger && o.logger.warn) {
69
65
  try {
70
66
  o.logger.warn("[exec] (async) " + bin + " " + (args || []).join(" ").slice(0, 80) + " failed: " + (err && err.message || err));
@@ -75,12 +71,25 @@ var require_exec = __commonJS({
75
71
  resolve({
76
72
  ok: false,
77
73
  code: err.status != null ? String(err.status) : null,
78
- stdout: String(stdout == null ? err.stdout || "" : stdout),
79
- stderr: String(stderr == null ? err.stderr || "" : stderr),
74
+ stdout: String(err.stdout || ""),
75
+ stderr: String(err.stderr || ""),
80
76
  timedOut,
81
77
  error: err && err.message ? String(err.message) : String(err)
82
78
  });
83
- });
79
+ };
80
+ try {
81
+ execFile(bin, args, Object.assign({}, options(o), { encoding: "utf8" }), (err, stdout, stderr) => {
82
+ if (!err) {
83
+ resolve({ ok: true, code: "0", stdout: String(stdout == null ? "" : stdout), stderr: String(stderr == null ? "" : stderr), timedOut: false, error: null });
84
+ return;
85
+ }
86
+ err.stdout = stdout;
87
+ err.stderr = stderr;
88
+ fail(err);
89
+ });
90
+ } catch (e) {
91
+ fail(e);
92
+ }
84
93
  });
85
94
  }
86
95
  function runOutAsync(bin, args, opts) {
@@ -503,15 +512,21 @@ var require_probe = __commonJS({
503
512
  }
504
513
  return null;
505
514
  }
506
- function isAlive(pid) {
507
- if (!Number.isInteger(pid) || pid <= 0) return false;
515
+ function probeAlive(pid) {
516
+ if (!Number.isInteger(pid) || pid <= 0) return "dead";
508
517
  try {
509
518
  process.kill(pid, 0);
510
- return true;
519
+ return "alive";
511
520
  } catch (e) {
512
- return !!e && e.code === "EPERM";
521
+ const code = e && e.code;
522
+ if (code === "EPERM") return "alive";
523
+ if (code === "ESRCH") return "dead";
524
+ return "unknown";
513
525
  }
514
526
  }
527
+ function isAlive(pid) {
528
+ return probeAlive(pid) === "alive";
529
+ }
515
530
  function isZombie(pid) {
516
531
  if (!Number.isInteger(pid) || pid <= 0 || isWindows) return false;
517
532
  if (isLinux) {
@@ -611,6 +626,7 @@ var require_probe = __commonJS({
611
626
  readCmdline: readCmdline2,
612
627
  pgrepList,
613
628
  isAlive,
629
+ probeAlive,
614
630
  isZombie
615
631
  };
616
632
  }
@@ -637,6 +653,7 @@ var require_pidlookup = __commonJS({
637
653
  readCmdline: readCmdline2,
638
654
  pgrepList,
639
655
  isAlive,
656
+ probeAlive,
640
657
  isZombie
641
658
  } = require_probe();
642
659
  var isLinux = process.platform === "linux";
@@ -659,6 +676,7 @@ var require_pidlookup = __commonJS({
659
676
  module2.exports = {
660
677
  findListeningPid,
661
678
  isAlive,
679
+ probeAlive,
662
680
  isZombie,
663
681
  readCmdline: readCmdline2,
664
682
  normCmdline,
@@ -929,7 +947,7 @@ var require_version = __commonJS({
929
947
  var fs2 = require("node:fs");
930
948
  var path2 = require("node:path");
931
949
  function guardVersion() {
932
- if (true) return String("0.1.6-BETA.5");
950
+ if (true) return String("0.1.6-BETA.7");
933
951
  try {
934
952
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
935
953
  } catch {
@@ -949,6 +967,8 @@ var require_main_record = __commonJS({
949
967
  const reg = () => typeof g.getManagedObjects === "function" ? g.getManagedObjects() : null;
950
968
  const logger = () => typeof g.getLogger === "function" ? g.getLogger() : null;
951
969
  let fallback = null;
970
+ const DEFAULTS = { restartCount: 0, backoffLevel: 0, backoffUntil: null, crashWindowStart: null, crashWindowRestarts: 0 };
971
+ const buffered = /* @__PURE__ */ new Map();
952
972
  function entryOf() {
953
973
  const m = reg();
954
974
  if (!m || typeof m.get !== "function") return null;
@@ -964,8 +984,8 @@ var require_main_record = __commonJS({
964
984
  kind: "dsh",
965
985
  id: "main",
966
986
  name: "\u4E3B\u5B9E\u4F8B",
987
+ // 不带 guardian(B2-2/B2-3):目录 entry 形态已无该键,守护开关权威在 dsh-main.json。
967
988
  desired: "running",
968
- guardian: true,
969
989
  ownership: { ports: [], rootPath: null, unit: null, daemonScript: null, processMode: "spawn", meta: null },
970
990
  phase: "stopped",
971
991
  lastObserved: null,
@@ -994,11 +1014,26 @@ var require_main_record = __commonJS({
994
1014
  }
995
1015
  }
996
1016
  function storeOf() {
997
- return entryOf() || fallbackEntryOf();
1017
+ const e = entryOf();
1018
+ if (!e) return fallbackEntryOf();
1019
+ if (buffered.size) flushBuffered(e);
1020
+ return e;
1021
+ }
1022
+ function flushBuffered(e) {
1023
+ let changed = false;
1024
+ for (const [k, v] of buffered) {
1025
+ if (k in DEFAULTS && e[k] === DEFAULTS[k] && e[k] !== v) {
1026
+ e[k] = v;
1027
+ changed = true;
1028
+ }
1029
+ }
1030
+ buffered.clear();
1031
+ if (changed) persistCrashField();
998
1032
  }
999
1033
  function fieldOf(name, v, write) {
1000
1034
  const e = storeOf();
1001
1035
  if (write) {
1036
+ if (e === fallback && name in DEFAULTS) buffered.set(name, v);
1002
1037
  if (e[name] !== v) {
1003
1038
  e[name] = v;
1004
1039
  persistCrashField();
@@ -1172,7 +1207,7 @@ var require_main_store = __commonJS({
1172
1207
  if (!live) live = readDshMainFile();
1173
1208
  if (corrupt && !(typeof m.remoteToken === "string" && m.remoteToken)) {
1174
1209
  const l = logger();
1175
- if (l && l.warn) l.warn("_writeDshMain: \u6587\u4EF6\u635F\u574F\u6001\uFF0C\u62D2\u7EDD\u4EE5\u9ED8\u8BA4\u503C\u8986\u76D6\u5199\u56DE");
1210
+ if (l && l.warn) l.warn("writeDshMain: \u6587\u4EF6\u635F\u574F\u6001\uFF0C\u62D2\u7EDD\u4EE5\u9ED8\u8BA4\u503C\u8986\u76D6\u5199\u56DE");
1176
1211
  return;
1177
1212
  }
1178
1213
  corrupt = false;
@@ -1192,7 +1227,7 @@ var require_main_store = __commonJS({
1192
1227
  writeAtomic(f, body, { mode: 384 });
1193
1228
  } catch (e) {
1194
1229
  const l = logger();
1195
- if (l && l.warn) l.warn("_writeDshMain: " + (e && e.message || e));
1230
+ if (l && l.warn) l.warn("writeDshMain: " + (e && e.message || e));
1196
1231
  }
1197
1232
  }
1198
1233
  return { dshMainFile, registryFileName, readDshMain, readDshMainFile, writeDshMain };
@@ -1280,8 +1315,8 @@ var require_fields = __commonJS({
1280
1315
  try {
1281
1316
  if (m && typeof m.setPhase === "function" && record.entryOf() === e) {
1282
1317
  if (e.phase !== ph) m.setPhase("main", ph);
1283
- } else if (e.phase !== ph) {
1284
- e.phase = ph;
1318
+ } else {
1319
+ record.fieldOf("phase", ph, true);
1285
1320
  }
1286
1321
  } catch (e2) {
1287
1322
  const l = logger();
@@ -1308,8 +1343,8 @@ var require_fields = __commonJS({
1308
1343
  try {
1309
1344
  if (m && typeof m.update === "function" && record.entryOf() === e) {
1310
1345
  if (e.desired !== want) m.update("main", { desired: want });
1311
- } else if (e.desired !== want) {
1312
- e.desired = want;
1346
+ } else {
1347
+ record.fieldOf("desired", want, true);
1313
1348
  }
1314
1349
  } catch (e2) {
1315
1350
  const l = logger();
@@ -1511,6 +1546,7 @@ var require_desired = __commonJS({
1511
1546
  const intents = () => typeof g.getIntents === "function" ? g.getIntents() : null;
1512
1547
  const events = () => typeof g.getEvents === "function" ? g.getEvents() : null;
1513
1548
  const configPath = () => typeof g.getConfigPath === "function" ? g.getConfigPath() : null;
1549
+ const configAliases = () => typeof g.getConfigAliases === "function" ? g.getConfigAliases() || [] : [];
1514
1550
  const logger = () => typeof g.getLogger === "function" ? g.getLogger() : null;
1515
1551
  const setCrashHalted = typeof g.setCrashHalted === "function" ? g.setCrashHalted : () => {
1516
1552
  };
@@ -1592,7 +1628,9 @@ var require_desired = __commonJS({
1592
1628
  }
1593
1629
  }
1594
1630
  Object.assign(cur, patch);
1595
- delete cur.switcherAutoStart;
1631
+ for (const [from, to] of configAliases()) {
1632
+ if (cur[from] !== void 0 && cur[to] !== void 0) delete cur[from];
1633
+ }
1596
1634
  writeAtomic(p, JSON.stringify(cur, null, 2), { mode: 384 });
1597
1635
  return true;
1598
1636
  } catch (e) {
@@ -1741,6 +1779,7 @@ var require_collaborator = __commonJS({
1741
1779
  getIntents: g.getIntents,
1742
1780
  getEvents: g.getEvents,
1743
1781
  getConfigPath: g.getConfigPath,
1782
+ getConfigAliases: g.getConfigAliases,
1744
1783
  getLogger: logger,
1745
1784
  setCrashHalted: g.setCrashHalted,
1746
1785
  setManualRestart: g.setManualRestart,
@@ -2022,13 +2061,11 @@ var require_specs = __commonJS({
2022
2061
  const daemons = () => typeof g.getDaemons === "function" ? g.getDaemons() : null;
2023
2062
  const logger = () => typeof g.getLogger === "function" ? g.getLogger() : null;
2024
2063
  function mainSpec() {
2025
- const m = state().readMainMeta();
2026
2064
  return {
2027
2065
  kind: "dsh",
2028
2066
  id: "main",
2029
2067
  name: "\u4E3B\u5B9E\u4F8B",
2030
2068
  desired: state().desired() === "stopped" ? "stopped" : "running",
2031
- guardian: m.guardian === true,
2032
2069
  ownership: {
2033
2070
  ports: [{ role: "dsh-main", port: Number(config().targetPort || 3080) }],
2034
2071
  rootPath: path2.join(os2.homedir(), ".dsh"),
@@ -2048,8 +2085,6 @@ var require_specs = __commonJS({
2048
2085
  kind: "sandbox-instance",
2049
2086
  id: inst.id,
2050
2087
  name: String(inst.name || inst.id),
2051
- desired: inst.state && inst.state.desired === "stopped" ? "stopped" : "running",
2052
- guardian: inst.guardian === true,
2053
2088
  ownership: {
2054
2089
  ports: [{ role: "inst", port: Number(inst.port) }],
2055
2090
  rootPath,
@@ -2063,7 +2098,7 @@ var require_specs = __commonJS({
2063
2098
  if (!m || !spec) return;
2064
2099
  try {
2065
2100
  const existing = m.get(spec.id);
2066
- if (existing) m.update(spec.id, { desired: spec.desired, guardian: spec.guardian, name: spec.name, ownership: spec.ownership });
2101
+ if (existing) m.update(spec.id, { desired: spec.desired, name: spec.name, ownership: spec.ownership });
2067
2102
  else m.register(spec);
2068
2103
  } catch (e) {
2069
2104
  const l = logger();
@@ -2985,10 +3020,6 @@ var require_facades = __commonJS({
2985
3020
  _makeRouterFacade() {
2986
3021
  const client = createCtlClient({ getConfig: () => this.config });
2987
3022
  return createRouterCtlFacade({ getRouterPort: client.routerCtlPort, ctlCall: client.ctlCall });
2988
- },
2989
- _makeCtlFacade(port) {
2990
- const client = createCtlClient({ getConfig: () => this.config });
2991
- return createCtlFacade({ port, ctlCall: client.ctlCall });
2992
3023
  }
2993
3024
  };
2994
3025
  module2.exports = { createCtlFacade, createRouterCtlFacade, methods };
@@ -3172,6 +3203,14 @@ var require_store2 = __commonJS({
3172
3203
  }
3173
3204
  return out;
3174
3205
  }
3206
+ function fileStamp(file) {
3207
+ try {
3208
+ const st = fs2.statSync(file);
3209
+ return st.mtimeMs + ":" + st.size;
3210
+ } catch {
3211
+ return "0";
3212
+ }
3213
+ }
3175
3214
  function saveRecords(file, records) {
3176
3215
  try {
3177
3216
  fs2.mkdirSync(path2.dirname(file), { recursive: true });
@@ -3197,7 +3236,7 @@ var require_store2 = __commonJS({
3197
3236
  }
3198
3237
  return out;
3199
3238
  }
3200
- module2.exports = { loadRecords, saveRecords, extraRecords };
3239
+ module2.exports = { loadRecords, saveRecords, extraRecords, fileStamp };
3201
3240
  }
3202
3241
  });
3203
3242
 
@@ -3440,6 +3479,7 @@ var require_alloc = __commonJS({
3440
3479
  }
3441
3480
  r._allocLock = true;
3442
3481
  this._xrel = await this._acquireXLock();
3482
+ r._syncFromDisk();
3443
3483
  }
3444
3484
  _releaseAlloc() {
3445
3485
  this._registry._allocLock = false;
@@ -3694,6 +3734,14 @@ var require_pool = __commonJS({
3694
3734
  _load() {
3695
3735
  this._records = /* @__PURE__ */ new Map();
3696
3736
  for (const r of store.loadRecords(this._file)) this._records.set(r.port, r);
3737
+ this._diskStamp = store.fileStamp(this._file);
3738
+ }
3739
+ /** 跨进程对时(B2-5):ports.json 是多进程(守卫 + lan-daemon)共享事实源,各方全量
3740
+ * read-modify-write,陈旧内存快照会在 _save 时把他人新增整段覆盖丢失、或让分配器抢注
3741
+ * 他进程已登记的端口。指纹(mtime+size)变化即重载——所有写口与冲突判读口的入口。
3742
+ * 不动 _allocLock(复位会击穿本进程在飞分配的互斥)。 */
3743
+ _syncFromDisk() {
3744
+ if (store.fileStamp(this._file) !== this._diskStamp) this._load();
3697
3745
  }
3698
3746
  /** 重新从文件加载(读路径先 reload,以权威文件为准)。 */
3699
3747
  reload() {
@@ -3703,6 +3751,7 @@ var require_pool = __commonJS({
3703
3751
  }
3704
3752
  _save() {
3705
3753
  store.saveRecords(this._file, [...this._records.values()]);
3754
+ this._diskStamp = store.fileStamp(this._file);
3706
3755
  }
3707
3756
  /** 通用记录迁移:owner 命中任一前缀的记录 oldFile 到 newFile,并从旧文件清除。 */
3708
3757
  migrateByOwnerPrefix(oldFile, newFile, prefixes) {
@@ -3711,6 +3760,7 @@ var require_pool = __commonJS({
3711
3760
  /* 登记(固定 / 用户 / 动态) */
3712
3761
  /** 登记固定端口;同端口已被其它固定角色占用则报错;user/动态记录由固定权威覆盖。 */
3713
3762
  register(role, port) {
3763
+ this._syncFromDisk();
3714
3764
  const p = Number(port);
3715
3765
  if (!Number.isInteger(p) || p <= 0 || p > 65535) throw new Error("ports.register: \u975E\u6CD5\u7AEF\u53E3 " + port);
3716
3766
  const existing = this._records.get(p);
@@ -3737,6 +3787,7 @@ var require_pool = __commonJS({
3737
3787
  }
3738
3788
  /** 登记用户配置端口(实例内部端口等);冲突(固定/保留池/已占)抛错。 */
3739
3789
  registerUser(port, owner) {
3790
+ this._syncFromDisk();
3740
3791
  const p = Number(port);
3741
3792
  if (!Number.isInteger(p) || p <= 0 || p > 65535) throw new Error("ports.registerUser: \u975E\u6CD5\u7AEF\u53E3 " + port);
3742
3793
  if (this._records.has(p)) throw new Error("\u7AEF\u53E3 " + p + " \u5DF2\u88AB [" + this._records.get(p).role + "] \u5360\u7528");
@@ -3748,6 +3799,7 @@ var require_pool = __commonJS({
3748
3799
  }
3749
3800
  /** 按 owner 释放端口(对象删除/关闭时调用)。 */
3750
3801
  unregister(owner) {
3802
+ this._syncFromDisk();
3751
3803
  let removed = false;
3752
3804
  for (const [p, r] of this._records) {
3753
3805
  if (r.owner === owner) {
@@ -3760,6 +3812,7 @@ var require_pool = __commonJS({
3760
3812
  /** 释放端口:不传 ownerId 按端口号;传了则仅当登记 owner 匹配才释放。空值检查必须先于 owner 比较。
3761
3813
  * @returns {boolean} 是否真的释放了一条记录 */
3762
3814
  release(port, ownerId) {
3815
+ this._syncFromDisk();
3763
3816
  const p = Number(port);
3764
3817
  const rec = this._records.get(p);
3765
3818
  if (!rec) return false;
@@ -3772,6 +3825,7 @@ var require_pool = __commonJS({
3772
3825
  /** 按 role 取端口(固定端口)。同 role 有多条(老版本避让留下的残留记录)时取**最新登记**:
3773
3826
  * 桌面壳读 ports.json 用的是同一判据,两侧不许对「哪个端口是当前的」给出不同答案。 */
3774
3827
  get(role) {
3828
+ this._syncFromDisk();
3775
3829
  let best = null;
3776
3830
  for (const r of this._records.values()) {
3777
3831
  if (r.role !== role) continue;
@@ -3780,12 +3834,15 @@ var require_pool = __commonJS({
3780
3834
  return best ? best.port : null;
3781
3835
  }
3782
3836
  isRegistered(port) {
3837
+ this._syncFromDisk();
3783
3838
  return this._records.has(Number(port));
3784
3839
  }
3785
3840
  recordOf(port) {
3841
+ this._syncFromDisk();
3786
3842
  return this._records.get(Number(port)) || null;
3787
3843
  }
3788
3844
  byOwner(owner) {
3845
+ this._syncFromDisk();
3789
3846
  for (const r of this._records.values()) if (r.owner === owner) return r.port;
3790
3847
  return null;
3791
3848
  }
@@ -3798,6 +3855,7 @@ var require_pool = __commonJS({
3798
3855
  }
3799
3856
  /** 全部端口清单(按端口升序)。 */
3800
3857
  list() {
3858
+ this._syncFromDisk();
3801
3859
  return [...this._records.values()].sort((a, b) => a.port - b.port);
3802
3860
  }
3803
3861
  /** 只读聚合:本注册表 + 同目录下其它注册表文件(去重,本表优先)。
@@ -3822,6 +3880,7 @@ var require_pool = __commonJS({
3822
3880
  }
3823
3881
  /** 显式登记已分配端口(复用持久化端口时调用)。 */
3824
3882
  allocateMark(port, role, owner) {
3883
+ this._syncFromDisk();
3825
3884
  const p = Number(port);
3826
3885
  if (!this._records.has(p)) {
3827
3886
  this._records.set(p, { port: p, role: role || "dynamic", owner: owner || "dynamic", createdAt: Date.now() });
@@ -3943,27 +4002,7 @@ var require_orphan_scan = __commonJS({
3943
4002
  logger && logger.warn && logger.warn("[orphan] \u81EA\u68C0\u5F02\u5E38: " + (e && e.message || e));
3944
4003
  }
3945
4004
  }
3946
- function hostOrphanAudit() {
3947
- return orphanAudit({
3948
- getConfig: () => this.config,
3949
- getLogger: () => this.logger,
3950
- getEvents: () => this.events,
3951
- getInstances: () => this.instances,
3952
- getManagedObjects: () => this.managedObjects,
3953
- getCtl: () => this.ctl,
3954
- getDaemons: () => this.daemons,
3955
- getStopping: () => this._stopping,
3956
- getLastKey: () => this._lastOrphanKey,
3957
- setLastKey: (v) => {
3958
- this._lastOrphanKey = v;
3959
- },
3960
- getLastAt: () => this._lastOrphanAt,
3961
- setLastAt: (v) => {
3962
- this._lastOrphanAt = v;
3963
- }
3964
- });
3965
- }
3966
- module2.exports = { methods: { _orphanAudit: hostOrphanAudit }, orphanAudit };
4005
+ module2.exports = { orphanAudit };
3967
4006
  }
3968
4007
  });
3969
4008
 
@@ -4016,6 +4055,7 @@ var require_collaborators = __commonJS({
4016
4055
  var { createCtl } = require_collaborator3();
4017
4056
  var { createOrphanScan } = require_collaborator4();
4018
4057
  var { ENTRY_FIELDS, PROC_FIELDS } = require_field_tables();
4058
+ var { aliases: CONFIG_ALIASES } = require_domain_config();
4019
4059
  var THIN_SPEC = {
4020
4060
  ctl: {
4021
4061
  call: "_ctlCall",
@@ -4068,7 +4108,6 @@ var require_collaborators = __commonJS({
4068
4108
  routerStatus: "routerStatus",
4069
4109
  status: "statusSummary"
4070
4110
  },
4071
- audit: { orphan: "_orphanAudit" },
4072
4111
  ui: { notify: "notify" }
4073
4112
  };
4074
4113
  var THIN_NAMES = Object.keys(THIN_SPEC);
@@ -4105,6 +4144,7 @@ var require_collaborators = __commonJS({
4105
4144
  const state = createStateStore({
4106
4145
  getConfig: () => host2.config,
4107
4146
  getConfigPath: () => host2.configPath,
4147
+ getConfigAliases: () => CONFIG_ALIASES,
4108
4148
  getLogger: () => host2.logger,
4109
4149
  getEvents: () => host2.events,
4110
4150
  getManagedObjects: () => host2.managedObjects,
@@ -4150,7 +4190,6 @@ var require_collaborators = __commonJS({
4150
4190
  return host2;
4151
4191
  };
4152
4192
  host2._dshEntry = () => state.dshEntry();
4153
- host2._mainFallbackEntry = () => state.fallbackEntry();
4154
4193
  host2._persistCrashField = () => state.persistCrashField();
4155
4194
  host2._mStore = () => state.store();
4156
4195
  host2._mField = function(name, v) {
@@ -4163,7 +4202,6 @@ var require_collaborators = __commonJS({
4163
4202
  host2._registryFileName = () => state.registryFileName();
4164
4203
  host2._readDshMain = () => state.readMainMeta();
4165
4204
  host2._readDshMainFile = () => state.readMainMetaFile();
4166
- host2._writeDshMain = (meta) => state.writeMainMeta(meta);
4167
4205
  host2.writeState = (force) => state.write(force);
4168
4206
  host2.loadState = () => state.loadState();
4169
4207
  host2._migrateMainRecord = () => state.migrateMainRecord();
@@ -7817,8 +7855,8 @@ var require_instance_adapter = __commonJS({
7817
7855
  return { ok: running, error: running ? null : "\u6C99\u7BB1\u5B9E\u4F8B\u672A\u8FD0\u884C" };
7818
7856
  },
7819
7857
  /** 目录项 <- 实例域状态对齐(监督拍后调用):实例已删 -> 注销(防死登记);存在 -> 经
7820
- * sandboxSpec 同步 name/guardian/ownership + phase 落目录词表。desired 走的是实例意图字段的
7821
- * 投影(不是 phase 推导),所以观测路径写它不会改写任何意图(契约 M-1)。 */
7858
+ * sandboxSpec 同步 name/guardian/ownership + phase 落目录词表。沙箱不申报 desired
7859
+ * (运行意图无第二落点,B2-1),观测路径因此不可能改写任何意图。 */
7822
7860
  _syncSandboxRegistryEntry(entry) {
7823
7861
  const d = depsOf(this);
7824
7862
  if (!entry || !d.managedObjects() || !d.instances()) return;
@@ -7851,6 +7889,9 @@ var require_decide = __commonJS({
7851
7889
  "src/app/main/decide.js"(exports2, module2) {
7852
7890
  "use strict";
7853
7891
  var pidlook = require_pidlookup();
7892
+ function startDeadlinePassed(deadline, now) {
7893
+ return !!(deadline && now > deadline);
7894
+ }
7854
7895
  var DEPS = /* @__PURE__ */ new WeakMap();
7855
7896
  function depsOf(host2) {
7856
7897
  let d = DEPS.get(host2);
@@ -7938,7 +7979,7 @@ var require_decide = __commonJS({
7938
7979
  upgradeHold: d.upgradeHold() === true,
7939
7980
  manualRestart: d.manualRestart() === true,
7940
7981
  spawnBlocked: !!(d.mSpawnBlockedUntil() && now < d.mSpawnBlockedUntil()),
7941
- startDeadlinePassed: !!(d.mStartDeadline() && now > d.mStartDeadline()),
7982
+ startDeadlinePassed: startDeadlinePassed(d.mStartDeadline(), now),
7942
7983
  restartDue: d.mRestartAt() === null || now >= d.mRestartAt(),
7943
7984
  backoffDue: d.mBackoffUntil() === null || now >= d.mBackoffUntil(),
7944
7985
  // `_shouldRun()` 有两个否决位,快照必须建模(crashHalted/sessionHalting),否则影子每拍
@@ -8013,7 +8054,9 @@ var require_decide = __commonJS({
8013
8054
  _decideCrashRestart(reason) {
8014
8055
  return decideCrashRestart(reason);
8015
8056
  }
8016
- }
8057
+ },
8058
+ // 非 host 方法:纯谓词导出,controller 与本文件快照判据共用(facets 只安装 methods)。
8059
+ startDeadlinePassed
8017
8060
  };
8018
8061
  }
8019
8062
  });
@@ -8064,6 +8107,7 @@ var require_controller = __commonJS({
8064
8107
  "use strict";
8065
8108
  var pidlook = require_pidlookup();
8066
8109
  var monitor = require_monitor();
8110
+ var { startDeadlinePassed } = require_decide();
8067
8111
  var DEPS = /* @__PURE__ */ new WeakMap();
8068
8112
  function depsOf(host2) {
8069
8113
  let d = DEPS.get(host2);
@@ -8168,6 +8212,9 @@ var require_controller = __commonJS({
8168
8212
  mStartDeadline() {
8169
8213
  return host2._mStartDeadline();
8170
8214
  },
8215
+ mSetStartDeadline(v) {
8216
+ return host2._mSetStartDeadline(v);
8217
+ },
8171
8218
  mRestartAt() {
8172
8219
  return host2._mRestartAt();
8173
8220
  },
@@ -8315,7 +8362,9 @@ var require_controller = __commonJS({
8315
8362
  }
8316
8363
  case "STARTING": {
8317
8364
  if (portUp && healthOk) d.main().enterRunning();
8318
- else if (Date.now() > d.mStartDeadline()) d.main().beginRestart("start_timeout", { countCrash: true });
8365
+ else if (d.mStartDeadline() === null) {
8366
+ d.mSetStartDeadline(Date.now() + d.config().startTimeoutMs);
8367
+ } else if (startDeadlinePassed(d.mStartDeadline(), Date.now())) d.main().beginRestart("start_timeout", { countCrash: true });
8319
8368
  break;
8320
8369
  }
8321
8370
  case "RUNNING": {
@@ -9569,13 +9618,7 @@ var require_process_wait = __commonJS({
9569
9618
  async function waitProcessExit(pid, timeoutMs) {
9570
9619
  const deadline = Date.now() + timeoutMs;
9571
9620
  while (Date.now() < deadline) {
9572
- let alive = false;
9573
- try {
9574
- alive = pidlook.isAlive ? pidlook.isAlive(pid) : true;
9575
- } catch {
9576
- alive = false;
9577
- }
9578
- if (!alive) return true;
9621
+ if (!pidlook.isAlive(pid)) return true;
9579
9622
  await new Promise((r) => setTimeout(r, 200));
9580
9623
  }
9581
9624
  return false;
@@ -9693,13 +9736,15 @@ var require_process3 = __commonJS({
9693
9736
  } catch {
9694
9737
  }
9695
9738
  }
9739
+ /** 判活(platform/pidlookup.probeAlive 单源):unknown 不 fail-open——必须有第二条证据
9740
+ * (ctl 端口属主正是该 pid 且 cmdline 匹配本服务)才认活,否则按死走 reclaim/spawn。
9741
+ * fail-open 会让已死 daemon 被判活,此后既不接管也不拉起,永不自愈。 */
9696
9742
  _pidAlive(pid) {
9697
9743
  if (!pid) return false;
9698
- try {
9699
- return pidlook.isAlive ? !!pidlook.isAlive(pid) : true;
9700
- } catch {
9701
- return true;
9702
- }
9744
+ const st = pidlook.probeAlive(pid);
9745
+ if (st === "alive") return true;
9746
+ if (st === "dead") return false;
9747
+ return this._ctlOwnerPid() === pid;
9703
9748
  }
9704
9749
  /** ctl 端口的监听者是否就是本服务进程(cmdline 匹配)。 */
9705
9750
  _ctlOwnerPid() {
@@ -10205,6 +10250,7 @@ var require_identity = __commonJS({
10205
10250
  "use strict";
10206
10251
  var fs2 = require("node:fs");
10207
10252
  var path2 = require("node:path");
10253
+ var { isAlive } = require_pidlookup();
10208
10254
  function lockPid(p) {
10209
10255
  if (!p) return null;
10210
10256
  try {
@@ -10215,13 +10261,7 @@ var require_identity = __commonJS({
10215
10261
  }
10216
10262
  }
10217
10263
  function pidAlive2(pid) {
10218
- if (!Number.isInteger(pid) || pid <= 0) return false;
10219
- try {
10220
- process.kill(pid, 0);
10221
- return true;
10222
- } catch (e) {
10223
- return !!(e && e.code === "EPERM");
10224
- }
10264
+ return isAlive(pid);
10225
10265
  }
10226
10266
  function acquireLock2(p, onErr) {
10227
10267
  if (!p) return false;
@@ -10602,7 +10642,7 @@ var require_ports2 = __commonJS({
10602
10642
  "use strict";
10603
10643
  var probe = require_probe2();
10604
10644
  var ports = require_ports().shared;
10605
- var SIBLING_REGISTRIES = ["ports-lan.json", "ports-router.json"];
10645
+ var SIBLING_REGISTRIES = ["ports-router.json"];
10606
10646
  var DEPS = /* @__PURE__ */ new WeakMap();
10607
10647
  function depsOf(host2) {
10608
10648
  let d = DEPS.get(host2);
@@ -11198,24 +11238,32 @@ var require_lan2 = __commonJS({
11198
11238
  }
11199
11239
  }
11200
11240
  return {
11201
- /** 远程控制模式唯一写入口(off|lan|wan)。wan 前置闸:必须先有合规访问令牌。 */
11241
+ /** 远程控制模式唯一写入口(off|lan|wan)。mode 必须显式给出——缺省归 'off' 会让
11242
+ * 漏字段的请求静默关闭远程控制。wan 前置闸:必须先有合规访问令牌。 */
11202
11243
  setRemoteMode(id, mode) {
11203
- const want = mode === "lan" || mode === "wan" ? mode : "off";
11244
+ if (mode !== "off" && mode !== "lan" && mode !== "wan") {
11245
+ return { ok: false, error: "mode \u5FC5\u987B\u663E\u5F0F\u7ED9\u51FA\uFF08off|lan|wan\uFF09" };
11246
+ }
11204
11247
  const target = resolveTarget(id);
11205
11248
  if (!target) return { ok: false, error: "\u5B9E\u4F8B\u4E0D\u5B58\u5728" };
11206
- if (want === "wan") {
11249
+ if (mode === "wan") {
11207
11250
  const v = validateWanAccess({ remoteToken: target.remoteToken });
11208
11251
  if (!v.ok) return { ok: false, error: v.error };
11209
11252
  }
11210
11253
  if (target.kind === "main") {
11211
- if (target.mode !== want) applyMainIntent({ remoteMode: want }, { id: "main", name: "\u539F\u751F DSH", mode: want });
11254
+ if (target.mode !== mode) applyMainIntent({ remoteMode: mode }, { id: "main", name: "\u539F\u751F DSH", mode });
11212
11255
  return { ok: true };
11213
11256
  }
11214
- return g.getInstances().updateInstance(id, { remoteMode: want });
11257
+ return g.getInstances().updateInstance(id, { remoteMode: mode });
11215
11258
  },
11216
- /** 访问令牌唯一写入口(空串=清除;lan 模式可无令牌,wan 模式的守门由执行边界闸兜住)。 */
11259
+ /** 访问令牌唯一写入口。token 必须是字符串:空串=显式清除;缺字段/非字符串=请求方缺陷,
11260
+ * 拒绝而非当作清除(漏 token 字段清掉访问凭据是事故,不是语义)。lan 模式可无令牌,
11261
+ * wan 模式的守门由执行边界闸兜住。 */
11217
11262
  setRemoteToken(id, token) {
11218
- const next = String(token || "");
11263
+ if (typeof token !== "string") {
11264
+ return { ok: false, error: "token \u5FC5\u987B\u663E\u5F0F\u7ED9\u51FA\uFF08\u7A7A\u4E32=\u6E05\u9664\uFF09" };
11265
+ }
11266
+ const next = token;
11219
11267
  if (next && !remoteTokenStrength(next).ok) {
11220
11268
  return { ok: false, error: "\u8FDC\u7A0B\u8BBF\u95EE\u4EE4\u724C\uFF08remoteToken\uFF09\u81F3\u5C11 8 \u4F4D" };
11221
11269
  }
@@ -11299,16 +11347,17 @@ var require_env_catalog = __commonJS({
11299
11347
  const v = ex2.runOut(bin, (Array.isArray(args) ? args : []).concat(["--version"]), { timeoutMs: 3e3 });
11300
11348
  return v ? v.trim() || null : null;
11301
11349
  }
11350
+ function whichVersionAsync(bin, args) {
11351
+ return ex2.runOutAsync(bin, (Array.isArray(args) ? args : []).concat(["--version"]), { timeoutMs: 3e3 }).then((v) => v ? v.trim() || null : null);
11352
+ }
11302
11353
  var _verCache = /* @__PURE__ */ new Map();
11303
11354
  var CACHE_TTL = 1e4;
11304
- function cachedWhichVersion(bin, args) {
11355
+ function cacheKey(bin, args) {
11305
11356
  const a = Array.isArray(args) ? args : [];
11306
- const key = bin + "\0" + a.join("\0");
11307
- const hit = _verCache.get(key);
11308
- const now = Date.now();
11309
- if (hit && now - hit.at < CACHE_TTL) return hit.v;
11310
- const v = whichVersion(bin, a);
11311
- _verCache.set(key, { at: now, v });
11357
+ return bin + "\0" + a.join("\0");
11358
+ }
11359
+ function cacheSet(key, v) {
11360
+ _verCache.set(key, { at: Date.now(), v });
11312
11361
  if (_verCache.size > 16) {
11313
11362
  let oldest = null;
11314
11363
  for (const [k, e] of _verCache) if (!oldest || e.at < oldest.at) oldest = { k, at: e.at };
@@ -11316,6 +11365,19 @@ var require_env_catalog = __commonJS({
11316
11365
  }
11317
11366
  return v;
11318
11367
  }
11368
+ function cachedWhichVersion(bin, args) {
11369
+ const key = cacheKey(bin, args);
11370
+ const hit = _verCache.get(key);
11371
+ const now = Date.now();
11372
+ if (hit && now - hit.at < CACHE_TTL) return hit.v;
11373
+ return cacheSet(key, whichVersion(bin, args));
11374
+ }
11375
+ function cachedWhichVersionAsync(bin, args) {
11376
+ const key = cacheKey(bin, args);
11377
+ const hit = _verCache.get(key);
11378
+ if (hit && Date.now() - hit.at < CACHE_TTL) return Promise.resolve(hit.v);
11379
+ return whichVersionAsync(bin, args).then((v) => cacheSet(key, v));
11380
+ }
11319
11381
  var MIN_NODE_DEFAULT = "v22.12.0";
11320
11382
  var _runtimeMetaCache = null;
11321
11383
  var _runtimeMetaAt = 0;
@@ -11342,50 +11404,68 @@ var require_env_catalog = __commonJS({
11342
11404
  }
11343
11405
  return true;
11344
11406
  }
11345
- function probeNode() {
11346
- const v = cachedWhichVersion("node");
11407
+ function nodeVerdict(v) {
11347
11408
  if (!v) return null;
11348
11409
  const m = /v?(\d+\.\d+\.\d+)/.exec(String(v));
11349
11410
  const ver = m ? m[1] : String(v).trim();
11350
11411
  const min = String(runtimeMeta().minNode || MIN_NODE_DEFAULT);
11351
11412
  return { version: "v" + ver, min, meets: verAtLeast(ver, min) };
11352
11413
  }
11414
+ function probeNode() {
11415
+ return nodeVerdict(cachedWhichVersion("node"));
11416
+ }
11417
+ function probeNodeAsync() {
11418
+ return cachedWhichVersionAsync("node").then(nodeVerdict);
11419
+ }
11353
11420
  function probeNpm() {
11354
11421
  const l = runtime.npmLauncher();
11355
11422
  return cachedWhichVersion(l.program, l.args);
11356
11423
  }
11424
+ function probeNpmAsync() {
11425
+ const l = runtime.npmLauncher();
11426
+ return cachedWhichVersionAsync(l.program, l.args);
11427
+ }
11357
11428
  var SYSTEM_ENTRIES = {
11358
- node: { label: "Node.js", required: true, probe: probeNode },
11359
- npm: { label: "npm", required: true, probe: probeNpm },
11360
- git: { label: "git", required: false, probe: () => cachedWhichVersion("git") }
11429
+ node: { label: "Node.js", required: true, probe: probeNode, probeAsync: probeNodeAsync },
11430
+ npm: { label: "npm", required: true, probe: probeNpm, probeAsync: probeNpmAsync },
11431
+ git: { label: "git", required: false, probe: () => cachedWhichVersion("git"), probeAsync: () => cachedWhichVersionAsync("git") }
11361
11432
  };
11433
+ function entryView(id, e, v) {
11434
+ if (v && typeof v === "object" && typeof v.meets === "boolean") {
11435
+ return {
11436
+ label: e.label,
11437
+ required: e.required,
11438
+ state: v.meets ? "ok" : "outdated",
11439
+ version: v.version,
11440
+ min: v.min,
11441
+ meets: v.meets,
11442
+ detail: v.meets ? v.version : v.version + "\uFF08\u4F4E\u4E8E\u6700\u4F4E\u8981\u6C42 " + v.min + "\uFF09"
11443
+ };
11444
+ }
11445
+ return { label: e.label, required: e.required, state: v ? "ok" : "missing", detail: v };
11446
+ }
11362
11447
  var EnvCatalog = class {
11363
11448
  constructor(config) {
11364
11449
  this.config = config || {};
11365
11450
  }
11366
- /** 系统二进制条目探测:{ id: { label, required, state, detail } }。state 三态:
11367
- * ok = 存在且满足门槛(Node 需 >= 壳投放的 minNode);outdated = 存在但低于门槛;missing = 不存在。
11368
- * 兼容:detail 保持字符串,新增字段(version/min/meets)放 detail 之外,不破坏既有契约。 */
11451
+ /** 系统二进制条目探测(同步口径):{ id: 条目视图 }。仅限启动早期/CLI;HTTP 路径用 probeAsync。 */
11369
11452
  probe() {
11370
11453
  const out = {};
11371
11454
  for (const [id, e] of Object.entries(SYSTEM_ENTRIES)) {
11372
- const v = e.probe() || null;
11373
- if (v && typeof v === "object" && typeof v.meets === "boolean") {
11374
- out[id] = {
11375
- label: e.label,
11376
- required: e.required,
11377
- state: v.meets ? "ok" : "outdated",
11378
- version: v.version,
11379
- min: v.min,
11380
- meets: v.meets,
11381
- detail: v.meets ? v.version : v.version + "\uFF08\u4F4E\u4E8E\u6700\u4F4E\u8981\u6C42 " + v.min + "\uFF09"
11382
- };
11383
- } else {
11384
- out[id] = { label: e.label, required: e.required, state: v ? "ok" : "missing", detail: v };
11385
- }
11455
+ out[id] = entryView(id, e, e.probe() || null);
11386
11456
  }
11387
11457
  return out;
11388
11458
  }
11459
+ /** 异步口径:条目并行探测(最坏 3s x N 的串行冻结 -> 全程不阻塞事件循环)。 */
11460
+ async probeAsync() {
11461
+ const entries = Object.entries(SYSTEM_ENTRIES);
11462
+ const vals = await Promise.all(entries.map(([, e]) => e.probeAsync()));
11463
+ const out = {};
11464
+ entries.forEach(([id, e], i) => {
11465
+ out[id] = entryView(id, e, vals[i] || null);
11466
+ });
11467
+ return out;
11468
+ }
11389
11469
  /** 内核更新依赖条目(单写入者契约:安装/重启归桌面壳,守卫只读 corePackageName 查版本状态)。
11390
11470
  * id 仍为 selfUpdate 以兼容既有 /env/status 消费方。 */
11391
11471
  selfUpdateEntry() {
@@ -11429,20 +11509,22 @@ var require_env = __commonJS({
11429
11509
  var fs2 = require("node:fs");
11430
11510
  var { EnvCatalog } = require_env_catalog();
11431
11511
  var runtimeContract = require_runtime();
11432
- function envCatalogSummary(that) {
11512
+ async function envCatalogSummary(that) {
11433
11513
  const cat = new EnvCatalog(that.config);
11434
11514
  const extra = {};
11435
11515
  const d = that.dshenvStatus();
11436
11516
  extra.dsh = cat.dshEntry(d.binOk, d.installed, d.bin);
11437
11517
  extra.selfUpdate = cat.selfUpdateEntry();
11438
- return cat.summary(extra);
11518
+ return cat.summary(extra, await cat.probeAsync());
11439
11519
  }
11440
11520
  module2.exports = {
11441
11521
  methods: {
11442
- envStatus() {
11522
+ // 异步:全部子进程探测(EnvCatalog/契约回读)走异步口径——本方法挂在 /env/status 上,
11523
+ // 同步 execFileSync 会把守卫事件循环冻结在探测超时上(心跳/自愈停摆,B1-6 收口)。
11524
+ async envStatus() {
11443
11525
  const c = runtimeContract.read() || {};
11444
- const cat = new EnvCatalog(this.config).probe();
11445
- const en = this.nativeManager && typeof this.nativeManager.checkEnvironment === "function" ? this.nativeManager.checkEnvironment() : null;
11526
+ const cat = await new EnvCatalog(this.config).probeAsync();
11527
+ const en = this.nativeManager && typeof this.nativeManager.checkEnvironment === "function" ? await this.nativeManager.checkEnvironment() : null;
11446
11528
  return {
11447
11529
  node: { detected: cat.node.detail || null, runtime: c.nodeVersion || null, path: c.nodePath || null },
11448
11530
  // npm 与 node 同构三段:detected = 本机实跑版本;runtime = 壳实跑后投放的版本
@@ -11454,7 +11536,7 @@ var require_env = __commonJS({
11454
11536
  ok: cat.node.state === "ok" && cat.npm.state === "ok",
11455
11537
  npmRoot: en ? en.npmRoot : null,
11456
11538
  // EnvCatalog 声明式视图(面板环境卡用)
11457
- catalog: envCatalogSummary(this),
11539
+ catalog: await envCatalogSummary(this),
11458
11540
  // 平台能力矩阵:三平台静态档位 x 实际工具探测;前端据此做能力感知呈现与降级提示。
11459
11541
  capabilities: (() => {
11460
11542
  try {
@@ -11680,15 +11762,16 @@ var require_versions = __commonJS({
11680
11762
  return { ok: false, error: e.message };
11681
11763
  }
11682
11764
  },
11683
- /** 读磁盘上运行位的自报版本:spawn --version,解析版本行。
11765
+ /** 读磁盘上运行位的自报版本:spawn --version,解析版本行(异步:20s 上限的同步 exec
11766
+ * 在 HTTP 路径上会冻结守卫整条事件循环,判据不变)。
11684
11767
  * 条件是 updatable(sea-binary 或 launcher):launcher 的 bin 入口同样可执行,
11685
11768
  * 若只认 sea-binary 则发布态永远读不到磁盘实况,updatePending 恒 false。
11686
11769
  * source-shell 不支持:其 --version 报的是开发目录版本,与 npm 安装无关。 */
11687
- _readBinarySelfVersion() {
11770
+ async _readBinarySelfVersion() {
11688
11771
  const dep = deploy.detect();
11689
11772
  if (!dep.updatable || !dep.runningTarget) return null;
11690
11773
  try {
11691
- const out = ex2.runOut(dep.runningTarget, ["--version"], { timeoutMs: 2e4 });
11774
+ const out = await ex2.runOutAsync(dep.runningTarget, ["--version"], { timeoutMs: 2e4 });
11692
11775
  const m = /dsh-supervisor v([^\s]+)/.exec(out);
11693
11776
  return m ? m[1] : null;
11694
11777
  } catch {
@@ -11710,18 +11793,18 @@ var require_versions = __commonJS({
11710
11793
  }
11711
11794
  return dir;
11712
11795
  },
11713
- /** 本地视角(无网络 I/O,同步安全):commit + 是否配了 upstream。 */
11714
- guardVersionLocal() {
11796
+ /** 本地视角(无网络 I/O;git 子进程异步执行——同步 spawn 会冻结事件循环,
11797
+ * 消费方含 HTTP 路径,见 exec.js 同步仅限启动早期/CLI 的纪律)。 */
11798
+ async guardVersionLocal() {
11715
11799
  const d = depsOf(this);
11716
11800
  const root = d.vcsRoot();
11717
- let commit = null;
11718
- commit = (ex2.runOut("git", ["-C", root, "rev-parse", "--short", "HEAD"]) || "").trim() || null;
11801
+ const [rawCommit, rawUp] = await Promise.all([
11802
+ ex2.runOutAsync("git", ["-C", root, "rev-parse", "--short", "HEAD"]),
11803
+ ex2.runOutAsync("git", ["-C", root, "rev-parse", "--abbrev-ref", "@{u}"])
11804
+ ]);
11805
+ const commit = (rawCommit || "").trim() || null;
11719
11806
  let upstream = "local";
11720
- try {
11721
- const up = (ex2.runOut("git", ["-C", root, "rev-parse", "--abbrev-ref", "@{u}"]) || "").trim();
11722
- if (up) upstream = "git-repo";
11723
- } catch {
11724
- }
11807
+ if ((rawUp || "").trim()) upstream = "git-repo";
11725
11808
  return { version: d.guardVersion(), runningVersion: d.guardVersion(), commit, updateAvailable: false, upstream, latest: d.guardVersion() };
11726
11809
  },
11727
11810
  /** 完整版本检查(async):本地 commit + 远端 fetch 比对。
@@ -11729,20 +11812,17 @@ var require_versions = __commonJS({
11729
11812
  * fetch 失败/超时只降级为「本地视图」,不抛错。 */
11730
11813
  async guardVersionCheck() {
11731
11814
  const d = depsOf(this);
11732
- const base = d.guardVersionLocal();
11815
+ const base = await d.guardVersionLocal();
11733
11816
  if (base.upstream !== "git-repo") return base;
11734
11817
  const root = d.vcsRoot();
11735
11818
  const fetchOk = await ex2.runOutAsync("git", ["-C", root, "fetch", "--quiet"], { timeoutMs: 1e4 }) !== null;
11736
11819
  if (!fetchOk) return base;
11737
11820
  let updateAvailable = false;
11738
- try {
11739
- const ahead = (ex2.runOut("git", ["-C", root, "rev-list", "--count", "HEAD..@{u}"]) || "").trim();
11740
- updateAvailable = parseInt(ahead, 10) > 0;
11741
- } catch {
11742
- }
11821
+ const ahead = (await ex2.runOutAsync("git", ["-C", root, "rev-list", "--count", "HEAD..@{u}"], { timeoutMs: 1e4 }) || "").trim();
11822
+ updateAvailable = parseInt(ahead, 10) > 0;
11743
11823
  const dep = deploy.detect();
11744
11824
  let diskVersion = null;
11745
- if (dep.updatable) diskVersion = d.readBinarySelfVersion();
11825
+ if (dep.updatable) diskVersion = await d.readBinarySelfVersion();
11746
11826
  const updatePending = !!(diskVersion && diskVersion !== d.guardVersion());
11747
11827
  return { ...base, diskVersion, updatePending };
11748
11828
  }
@@ -11826,7 +11906,9 @@ var require_access = __commonJS({
11826
11906
  return { ok: false, error: e.message };
11827
11907
  }
11828
11908
  }
11829
- }
11909
+ },
11910
+ // settings 门面的写口核验件(B2-4):lan-panel 共用同一「写后读回」口径,不各写各的。
11911
+ verifyPersisted
11830
11912
  };
11831
11913
  }
11832
11914
  });
@@ -11933,9 +12015,8 @@ var require_netinfo = __commonJS({
11933
12015
  var require_lan_panel = __commonJS({
11934
12016
  "src/app/settings/lan-panel.js"(exports2, module2) {
11935
12017
  "use strict";
11936
- var fs2 = require("node:fs");
11937
12018
  var netInfo = require_netinfo();
11938
- var { writeAtomic } = require_fs();
12019
+ var { verifyPersisted } = require_access();
11939
12020
  var DEPS = /* @__PURE__ */ new WeakMap();
11940
12021
  function depsOf(host2) {
11941
12022
  let d = DEPS.get(host2);
@@ -11945,6 +12026,7 @@ var require_lan_panel = __commonJS({
11945
12026
  logger: () => host2.logger,
11946
12027
  events: () => host2.events,
11947
12028
  configPath: () => host2.configPath,
12029
+ state: () => host2.state,
11948
12030
  api: () => host2.api,
11949
12031
  lanPanelStatus: () => host2.lanPanelStatus(),
11950
12032
  apiRebind: () => host2._apiRebind()
@@ -11986,14 +12068,9 @@ var require_lan_panel = __commonJS({
11986
12068
  d.config().apiHost = host2;
11987
12069
  let persistError = null;
11988
12070
  if (d.configPath()) {
11989
- try {
11990
- const doc = JSON.parse(fs2.readFileSync(d.configPath(), "utf8"));
11991
- doc.apiHost = host2;
11992
- writeAtomic(d.configPath(), JSON.stringify(doc, null, 2), { mode: 384 });
11993
- } catch (e) {
11994
- persistError = "persist apiHost: " + e.message;
11995
- d.logger().error(persistError);
11996
- }
12071
+ d.state().persistConfigPatch({ apiHost: host2 });
12072
+ persistError = verifyPersisted(d.configPath(), { apiHost: host2 });
12073
+ if (persistError) d.logger().error(persistError);
11997
12074
  }
11998
12075
  if (changed && d.api() && typeof d.api().close === "function") d.apiRebind();
11999
12076
  if (d.events()) d.events().append("lan_panel_changed", { enabled: on });
@@ -12047,7 +12124,8 @@ var require_facets = __commonJS({
12047
12124
  { name: "domain-actions/router", mod: require_router2(), factory: "createRouterActions" },
12048
12125
  { name: "domain-actions/lan", mod: require_lan2(), factory: "createLanActions" },
12049
12126
  { name: "domain-actions/main", mod: require_main2(), factory: "createMainActions" },
12050
- { name: "audit/orphan-scan", mod: require_orphan_scan() },
12127
+ // audit/orphan-scan 不在本清单:它是真 ctor 工厂(assembly/collaborators.js 的 installAuditFactory
12128
+ // 构造 host.audit),实现体本身只是纯函数,不再需要 host 兼容外壳切面。
12051
12129
  { name: "settings/env", mod: require_env() },
12052
12130
  { name: "settings/node-lts", mod: require_node_lts() },
12053
12131
  { name: "settings/versions", mod: require_versions() },
@@ -12611,6 +12689,8 @@ var require_pool2 = __commonJS({
12611
12689
  this._bus = new FollowBus({ logger: this.logger });
12612
12690
  this._schedules = /* @__PURE__ */ new Map();
12613
12691
  this._seq = 0;
12692
+ this._attachGen = /* @__PURE__ */ new Map();
12693
+ this._journalFn = o.journal || capture.captureJournal;
12614
12694
  this._backfillAt = /* @__PURE__ */ new Map();
12615
12695
  this._poolFile = o.poolFile ? path2.resolve(o.poolFile) : null;
12616
12696
  this._loaded = false;
@@ -12635,6 +12715,7 @@ var require_pool2 = __commonJS({
12635
12715
  const unit = s.unit || prev && prev.unit || null;
12636
12716
  const file = s.file || prev && prev.file || null;
12637
12717
  this._sources.set(id, { kind, unit, file, lines: prev && prev.lines || [] });
12718
+ this._attachGen.set(id, (this._attachGen.get(id) || 0) + 1);
12638
12719
  const rec = this._records.get(id);
12639
12720
  if (rec) rec.kind = kind;
12640
12721
  return true;
@@ -12659,9 +12740,16 @@ var require_pool2 = __commonJS({
12659
12740
  return this._commit(id, hit.token, hit.source);
12660
12741
  }
12661
12742
  if (src.unit && kinds.isCaptured(src.kind)) {
12662
- Promise.resolve().then(() => capture.captureJournal(src.unit, { logger: this.logger })).then((j) => {
12743
+ const gen = this._attachGen.get(id) || 0;
12744
+ const fresh = () => (this._attachGen.get(id) || 0) === gen ? this._sources.get(id) : null;
12745
+ Promise.resolve().then(() => {
12746
+ const cur = fresh();
12747
+ return cur ? this._journalFn(cur.unit, { logger: this.logger }) : null;
12748
+ }).then((j) => {
12663
12749
  if (!j) return;
12664
- if (src.file) this._persistLine(id, src.file, j.line);
12750
+ const cur = fresh();
12751
+ if (!cur) return;
12752
+ if (cur.file) this._persistLine(id, cur.file, j.line);
12665
12753
  this._commit(id, j.token, j.source);
12666
12754
  }).catch(() => {
12667
12755
  });
@@ -12729,6 +12817,7 @@ var require_pool2 = __commonJS({
12729
12817
  this._backfillAt.delete(id);
12730
12818
  const src = this._sources.get(id);
12731
12819
  if (src && src.lines && src.lines.length) src.lines.length = 0;
12820
+ this._attachGen.set(id, (this._attachGen.get(id) || 0) + 1);
12732
12821
  this._records.delete(id);
12733
12822
  this._persistPool();
12734
12823
  this._bus.emit(id, null, null);
@@ -13659,7 +13748,6 @@ var require_core5 = __commonJS({
13659
13748
  host2._routerFacade = null;
13660
13749
  host2._lc = null;
13661
13750
  host2._dshMainLive = null;
13662
- host2._fallbackEntry = null;
13663
13751
  host2._lastStateBody = null;
13664
13752
  host2._shadowSeq = 0;
13665
13753
  host2._shadowConsistentBeats = 0;
@@ -13831,7 +13919,7 @@ var require_model = __commonJS({
13831
13919
  adapter: p.adapter || null,
13832
13920
  proxyAppId: p.proxyAppId || null,
13833
13921
  proxyRunning: p.proxyRunning || false,
13834
- selectedAccountKeyId: p.selectedAccountKeyId || p.selectedProxyKeyId || null,
13922
+ selectedAccountKeyId: p.selectedAccountKeyId || null,
13835
13923
  activeAccountKeyId: p.activeAccount && p.activeAccount.keyId || null,
13836
13924
  accounts: (p.accounts || []).map((a) => {
13837
13925
  p._normalizeConsistency(a);
@@ -14243,7 +14331,6 @@ var require_freeze = __commonJS({
14243
14331
  const usable = !!acc && acc.status === "ready" && (typeof provider.isAccountUsable !== "function" || provider.isAccountUsable(acc));
14244
14332
  if (!usable) {
14245
14333
  provider.selectedAccountKeyId = null;
14246
- if (provider.selectedProxyKeyId === lockedId) provider.selectedProxyKeyId = null;
14247
14334
  }
14248
14335
  }
14249
14336
  module2.exports = { setStatus, ensureLimit, previewLimit, setLimit, freezeLimited, markCreditsExhausted, markQuotaExhausted, markBanned, applyDetection, normalizeConsistency, reconcileLock };
@@ -17206,10 +17293,11 @@ var require_oauth = __commonJS({
17206
17293
  const d = deps || {};
17207
17294
  const ports = d.ports;
17208
17295
  const openInBrowser = d.openInBrowser;
17209
- const st = { _ccLogin: null, _ccLoginPromise: null, _ccLoginResolve: null, _ccLoginReject: null };
17296
+ const st = { _ccLogin: null, _ccLoginPromise: null, _ccLoginResolve: null, _ccLoginReject: null, _ccLoginRound: 0 };
17210
17297
  async function commandcodeLoginStart() {
17211
17298
  const STUDIO_BASE = "https://commandcode.ai";
17212
17299
  const state = crypto.randomBytes(32).toString("base64url");
17300
+ const roundId = ++st._ccLoginRound;
17213
17301
  if (st._ccLogin && st._ccLogin.server) {
17214
17302
  const oldState = st._ccLogin.state;
17215
17303
  try {
@@ -17271,6 +17359,11 @@ var require_oauth = __commonJS({
17271
17359
  if (b.length > 1e4) req.destroy();
17272
17360
  });
17273
17361
  req.on("end", () => {
17362
+ if (st._ccLoginRound !== roundId) {
17363
+ res.writeHead(410);
17364
+ res.end(callbackJson({ success: false, error: "Stale login round" }));
17365
+ return;
17366
+ }
17274
17367
  try {
17275
17368
  const j = JSON.parse(b || "{}");
17276
17369
  if (j && typeof j === "object" && "error" in j) {
@@ -17361,6 +17454,7 @@ var require_oauth = __commonJS({
17361
17454
  });
17362
17455
  st._ccLoginPromise = promise;
17363
17456
  const tmpProfile = openInBrowser(authUrl, () => {
17457
+ if (st._ccLoginRound !== roundId) return;
17364
17458
  if (st._ccLoginReject) {
17365
17459
  const r = st._ccLoginReject;
17366
17460
  st._ccLoginReject = null;
@@ -19247,10 +19341,7 @@ var require_model3 = __commonJS({
19247
19341
  inst.state.lastError = null;
19248
19342
  }
19249
19343
  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
- }
19344
+ if (inst.state) delete inst.state.desired;
19254
19345
  return inst;
19255
19346
  }
19256
19347
  function createRecord(payload, id) {
@@ -19275,7 +19366,7 @@ var require_model3 = __commonJS({
19275
19366
  protectHome: payload.protectHome === void 0 ? false : !!payload.protectHome
19276
19367
  // 资源配额不接收户输入:启动时由 governor 按机器预算与活跃实例数推导。
19277
19368
  },
19278
- state: { phase: "STOPPED", desired: "stopped", restartCount: 0, backoffLevel: 0, lastProbeOk: null },
19369
+ state: { phase: "STOPPED", restartCount: 0, backoffLevel: 0, lastProbeOk: null },
19279
19370
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
19280
19371
  };
19281
19372
  }
@@ -19515,6 +19606,7 @@ var require_lifecycle = __commonJS({
19515
19606
  "use strict";
19516
19607
  var fs2 = require("node:fs");
19517
19608
  var monitor = require_monitor();
19609
+ var ports = require_ports().shared;
19518
19610
  var guardian = require_guardian();
19519
19611
  var sandbox = require_sandbox();
19520
19612
  var governor = require_governor();
@@ -19557,7 +19649,7 @@ var require_lifecycle = __commonJS({
19557
19649
  logger.info && logger.info("cleaned stale transient unit: " + unit);
19558
19650
  }
19559
19651
  }
19560
- function _systemdStart(inst) {
19652
+ function _systemdStart(inst, opts) {
19561
19653
  try {
19562
19654
  const cmdArr = sandbox.effectiveCommand(instancesRoot, deps.dshBin, inst);
19563
19655
  if (!cmdArr || !cmdArr.length) return { ok: false, error: "\u5B9E\u4F8B\u672A\u914D\u7F6E\u542F\u52A8\u547D\u4EE4" };
@@ -19573,6 +19665,15 @@ var require_lifecycle = __commonJS({
19573
19665
  logger.warn && logger.warn("[" + inst.id + "] " + msg);
19574
19666
  return { ok: false, error: msg };
19575
19667
  }
19668
+ const takenBy = ports.recordOf(inst.port);
19669
+ if (takenBy && takenBy.owner !== "inst:" + inst.id) {
19670
+ const msg = "PORT_TAKEN:" + (takenBy.owner || takenBy.role);
19671
+ inst.state.lastError = msg;
19672
+ store.save();
19673
+ if (events) events.append("inst_start_refused", { id: inst.id, name: inst.name, error: msg });
19674
+ logger.warn && logger.warn("[" + inst.id + "] " + msg);
19675
+ return { ok: false, error: msg };
19676
+ }
19576
19677
  if (probe(inst).running) return { ok: false, error: "\u7AEF\u53E3 " + inst.port + " \u5DF2\u88AB\u5360\u7528" };
19577
19678
  const alloc = governor.currentAllocation(store.instances, inst.id, machineFactsNow());
19578
19679
  inst.state.allocation = alloc;
@@ -19592,6 +19693,12 @@ var require_lifecycle = __commonJS({
19592
19693
  inst.state.phase = "STARTING";
19593
19694
  inst.state.startAt = Date.now();
19594
19695
  inst.state.lastError = null;
19696
+ if (opts && opts.manual) {
19697
+ inst.state.restartCount = 0;
19698
+ inst.state.backoffLevel = 0;
19699
+ inst.state.backoffUntil = null;
19700
+ inst.state.lastFailAt = null;
19701
+ }
19595
19702
  store.save();
19596
19703
  if (inst.port && hooks.onInstanceStart) hooks.onInstanceStart(inst);
19597
19704
  if (events) events.append("inst_started", { id: inst.id, port: inst.port });
@@ -19617,10 +19724,6 @@ var require_lifecycle = __commonJS({
19617
19724
  return { ok: false, error: adm.error };
19618
19725
  }
19619
19726
  }
19620
- if (inst.state.desired !== "running") {
19621
- inst.state.desired = "running";
19622
- store.save();
19623
- }
19624
19727
  store.ensureDirs(inst);
19625
19728
  const dshEntry = sandbox.dshEntry(instancesRoot, inst);
19626
19729
  if (!fs2.existsSync(dshEntry)) {
@@ -19629,10 +19732,9 @@ var require_lifecycle = __commonJS({
19629
19732
  return { ok: true, installing: true };
19630
19733
  }
19631
19734
  }
19632
- return _systemdStart(inst);
19735
+ return _systemdStart(inst, opts);
19633
19736
  }
19634
- function stop(id, opts) {
19635
- const transient = !!(opts && opts.intent === "transient");
19737
+ function stop(id) {
19636
19738
  const inst = store.instances.find((i) => i.id === id);
19637
19739
  if (!inst) return { ok: false, error: "\u5B9E\u4F8B\u4E0D\u5B58\u5728" };
19638
19740
  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" };
@@ -19654,7 +19756,6 @@ var require_lifecycle = __commonJS({
19654
19756
  }
19655
19757
  inst.state.phase = "STOPPED";
19656
19758
  inst.state.usage = null;
19657
- if (!transient) inst.state.desired = "stopped";
19658
19759
  runtime.delete(inst.id);
19659
19760
  store.save();
19660
19761
  if (inst.port && hooks.onInstanceStop) hooks.onInstanceStop(inst);
@@ -19710,15 +19811,17 @@ var require_lifecycle = __commonJS({
19710
19811
  }).catch(() => {
19711
19812
  });
19712
19813
  }
19814
+ }
19815
+ function governSweep() {
19713
19816
  const roster = _sandboxRoster();
19714
- if (!roster.length) return;
19817
+ if (!roster.length) return { ok: true, entries: 0 };
19715
19818
  let plan;
19716
19819
  try {
19717
19820
  const f = machineFactsNow();
19718
19821
  plan = governor.decide({ totalMemBytes: f.totalMemBytes, cpuCount: f.cpuCount, roster });
19719
19822
  } catch (e) {
19720
- logger.warn && logger.warn("[" + inst.id + "] govern decide \u5931\u8D25: " + (e && e.message));
19721
- return;
19823
+ logger.warn && logger.warn("govern decide \u5931\u8D25: " + (e && e.message));
19824
+ return { ok: false, error: e && e.message || String(e) };
19722
19825
  }
19723
19826
  const now = Date.now();
19724
19827
  for (const entry of plan.entries) {
@@ -19743,19 +19846,21 @@ var require_lifecycle = __commonJS({
19743
19846
  }
19744
19847
  }
19745
19848
  }
19746
- if (!entry.violation || entry.id !== inst.id) continue;
19849
+ if (!entry.violation) continue;
19747
19850
  const v = entry.violation;
19748
19851
  const kindLabel = v.kind === "memory" ? "\u5185\u5B58" : "CPU";
19749
19852
  const reason = "\u8D44\u6E90\u8FDD\u89C4:" + kindLabel + "\u6301\u7EED\u8D85\u9650(\u5B9E\u9645 " + v.actual + "/\u9650\u989D " + v.target + ")";
19750
- if (events) events.append("inst_resource_violation", { id: inst.id, name: inst.name, kind: v.kind, actual: v.actual, target: v.target });
19751
- logger.warn && logger.warn("[" + inst.id + "] " + reason);
19853
+ if (events) events.append("inst_resource_violation", { id: target.id, name: target.name, kind: v.kind, actual: v.actual, target: v.target });
19854
+ logger.warn && logger.warn("[" + target.id + "] " + reason);
19752
19855
  try {
19753
- service.stopUnit("dsh-web@" + inst.id, Object.assign({ timeoutMs: 2e4 }, sandbox.launchCtx(instancesRoot, deps.dshBin, inst)));
19856
+ service.stopUnit("dsh-web@" + target.id, Object.assign({ timeoutMs: 2e4 }, sandbox.launchCtx(instancesRoot, deps.dshBin, target)));
19754
19857
  } catch (e) {
19755
- logger.warn && logger.warn("[" + inst.id + "] \u8FDD\u89C4\u505C\u5355\u5143\u5F02\u5E38: " + (e && e.message));
19858
+ logger.warn && logger.warn("[" + target.id + "] \u8FDD\u89C4\u505C\u5355\u5143\u5F02\u5E38: " + (e && e.message));
19756
19859
  }
19757
- stateMachine.restart(stateDeps(), inst, reason);
19860
+ stateMachine.restart(stateDeps(), target, reason);
19758
19861
  }
19862
+ store.save();
19863
+ return { ok: true, entries: plan.entries.length };
19759
19864
  }
19760
19865
  function supervise(id) {
19761
19866
  const inst = store.instances.find((i) => i.id === id);
@@ -19849,7 +19954,7 @@ var require_lifecycle = __commonJS({
19849
19954
  }
19850
19955
  return { ok: true };
19851
19956
  }
19852
- return { _prepareSystemd, start, stop, probe, probeInstance, supervise };
19957
+ return { _prepareSystemd, start, stop, probe, probeInstance, supervise, governSweep };
19853
19958
  }
19854
19959
  module2.exports = { createLifecycle };
19855
19960
  }
@@ -20158,7 +20263,7 @@ var require_upgrade = __commonJS({
20158
20263
  tasks.stepState(task.id, tasks.get(task.id).steps.indexOf(s), "running");
20159
20264
  }
20160
20265
  try {
20161
- await lifecycle.stop(id, { intent: "transient" });
20266
+ await lifecycle.stop(id);
20162
20267
  } catch {
20163
20268
  }
20164
20269
  if (task) {
@@ -20206,7 +20311,7 @@ var require_upgrade = __commonJS({
20206
20311
  tasks.log(task.id, "\u81EA\u52A8\u56DE\u6EDA\u5230 " + oldVersion + "\u2026");
20207
20312
  }
20208
20313
  try {
20209
- const rs = await lifecycle.stop(id, { intent: "transient" });
20314
+ const rs = await lifecycle.stop(id);
20210
20315
  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 || ""));
20211
20316
  } catch (e) {
20212
20317
  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));
@@ -20493,6 +20598,11 @@ var require_ops2 = __commonJS({
20493
20598
  } catch {
20494
20599
  }
20495
20600
  }
20601
+ try {
20602
+ lifecycle.governSweep();
20603
+ } catch (e) {
20604
+ logger.warn && logger.warn("governSweep: " + (e && e.message));
20605
+ }
20496
20606
  }, intervalMs || 5e3);
20497
20607
  }
20498
20608
  return { list, addInstance, removeInstance, updateInstance, startTimer };
@@ -20785,12 +20895,16 @@ var require_instance = __commonJS({
20785
20895
  startInstance(id, opts) {
20786
20896
  return this._lifecycle.start(id, opts);
20787
20897
  }
20788
- stopInstance(id, opts) {
20789
- return this._lifecycle.stop(id, opts);
20898
+ stopInstance(id) {
20899
+ return this._lifecycle.stop(id);
20790
20900
  }
20791
20901
  supervise(id) {
20792
20902
  return this._lifecycle.supervise(id);
20793
20903
  }
20904
+ /** 治理单拍(B2-6e):心跳拍末由 onBeatDone 调一次,全花名册 decide+下发+违规处置。 */
20905
+ governSweep() {
20906
+ return this._lifecycle.governSweep();
20907
+ }
20794
20908
  probeInstance(id) {
20795
20909
  return this._lifecycle.probeInstance(id);
20796
20910
  }
@@ -21112,8 +21226,6 @@ var require_market = __commonJS({
21112
21226
  this._ts = 0;
21113
21227
  this._inFlight = null;
21114
21228
  this.buildBudgetMs = opts.buildBudgetMs || 24e4;
21115
- this._deadline = 0;
21116
- this._truncatedSources = /* @__PURE__ */ new Set();
21117
21229
  this.loadFromDisk();
21118
21230
  }
21119
21231
  loadFromDisk() {
@@ -21160,19 +21272,18 @@ var require_market = __commonJS({
21160
21272
  }
21161
21273
  async buildIndex() {
21162
21274
  const start = Date.now();
21163
- this._deadline = Date.now() + this.buildBudgetMs;
21164
- this._truncatedSources = /* @__PURE__ */ new Set();
21275
+ const bctx = { deadline: start + this.buildBudgetMs, truncated: /* @__PURE__ */ new Set() };
21165
21276
  try {
21166
- return await this._buildIndexInner(start);
21277
+ return await this._buildIndexInner(start, bctx);
21167
21278
  } finally {
21168
- this._deadline = 0;
21279
+ bctx.deadline = 0;
21169
21280
  }
21170
21281
  }
21171
- /** 预算是否已耗尽(供各源的批次循环调用)。 */
21172
- _budgetExhausted() {
21173
- return this._deadline > 0 && Date.now() >= this._deadline;
21282
+ /** 预算是否已耗尽(供各源的批次循环调用;无 ctx = 单源直调,不设预算)。 */
21283
+ _budgetExhausted(bctx) {
21284
+ return !!bctx && bctx.deadline > 0 && Date.now() >= bctx.deadline;
21174
21285
  }
21175
- async _buildIndexInner(start) {
21286
+ async _buildIndexInner(start, bctx) {
21176
21287
  const plugins = [];
21177
21288
  const seen = /* @__PURE__ */ new Set();
21178
21289
  const add = (p) => {
@@ -21180,11 +21291,11 @@ var require_market = __commonJS({
21180
21291
  seen.add(p.name);
21181
21292
  plugins.push(p);
21182
21293
  };
21183
- const npm = await this.indexNpm();
21294
+ const npm = await this.indexNpm(bctx);
21184
21295
  npm.forEach(add);
21185
- const gh = await this.indexGithub();
21296
+ const gh = await this.indexGithub(bctx);
21186
21297
  gh.forEach(add);
21187
- const community = await this.indexCommunity();
21298
+ const community = await this.indexCommunity(bctx);
21188
21299
  community.forEach(add);
21189
21300
  for (const p of plugins) {
21190
21301
  p.category = p.category || classify(p);
@@ -21192,7 +21303,7 @@ var require_market = __commonJS({
21192
21303
  }
21193
21304
  plugins.sort((a, b) => (b.stars || 0) - (a.stars || 0));
21194
21305
  const prev = this._cache;
21195
- const truncated = this._truncatedSources || /* @__PURE__ */ new Set();
21306
+ const truncated = bctx.truncated;
21196
21307
  if (prev && prev.plugins && prev.plugins.length > 0 && truncated.size > 0) {
21197
21308
  const freshNames = new Set(plugins.map((pp) => pp.name));
21198
21309
  const kept = prev.plugins.filter((pp) => truncated.has(pp.source) && !freshNames.has(pp.name));
@@ -21227,8 +21338,8 @@ var require_market = __commonJS({
21227
21338
  this.logger.info && this.logger.info("market index built in " + (Date.now() - start) + "ms: " + plugins.length + " plugins (npm=" + npm.length + ", gh=" + gh.length + ", community=" + community.length + ")");
21228
21339
  return this._cache;
21229
21340
  }
21230
- /** npm 源:搜 deepseek-harness 受限 dsh,逐个检测 dsh.bundle。 */
21231
- async indexNpm() {
21341
+ /** npm 源:搜 deepseek-harness 受限 dsh,逐个检测 dsh.bundle。bctx 为本次构建的预算上下文(见 buildIndex)。 */
21342
+ async indexNpm(bctx) {
21232
21343
  const out = [];
21233
21344
  const queries = ["keywords:deepseek-harness", "keywords:dsh-bundle", "keywords:dsh-plugin"];
21234
21345
  const allNames = /* @__PURE__ */ new Set();
@@ -21255,8 +21366,8 @@ var require_market = __commonJS({
21255
21366
  this.logger.info && this.logger.info("npm candidates: " + names.length);
21256
21367
  const batch = 8;
21257
21368
  for (let i = 0; i < names.length; i += batch) {
21258
- if (this._budgetExhausted()) {
21259
- this._truncatedSources.add("npm");
21369
+ if (this._budgetExhausted(bctx)) {
21370
+ bctx.truncated.add("npm");
21260
21371
  this.logger.warn && this.logger.warn("market: npm \u6E90\u9884\u7B97\u8017\u5C3D\uFF0C\u5DF2\u5904\u7406 " + i + "/" + names.length + " \u4E2A\u5019\u9009");
21261
21372
  break;
21262
21373
  }
@@ -21286,7 +21397,7 @@ var require_market = __commonJS({
21286
21397
  return fetchLatest(await this._npmOrigin(), name);
21287
21398
  }
21288
21399
  /** GitHub 源:搜 topic:dsh-plugin + deepseek-harness,逐个验证 dsh.bundle。 */
21289
- async indexGithub() {
21400
+ async indexGithub(bctx) {
21290
21401
  const out = [];
21291
21402
  const topics = ["dsh-plugin", "deepseek-harness"];
21292
21403
  const seen = /* @__PURE__ */ new Set();
@@ -21316,7 +21427,7 @@ var require_market = __commonJS({
21316
21427
  return repoPkg(fullName);
21317
21428
  }
21318
21429
  /** 社区列表:抓 awesome-dsh-plugin README 白名单(官方社区维护的精选)。 */
21319
- async indexCommunity() {
21430
+ async indexCommunity(bctx) {
21320
21431
  const out = [];
21321
21432
  try {
21322
21433
  const md = await rawGet("awesome-dsh-plugin/awesome-dsh-plugin/main/README.md", false, 3e4);
@@ -21328,8 +21439,8 @@ var require_market = __commonJS({
21328
21439
  }
21329
21440
  const seenName = /* @__PURE__ */ new Set();
21330
21441
  for (let i = 0; i < links.length; i += 8) {
21331
- if (this._budgetExhausted()) {
21332
- this._truncatedSources.add("community");
21442
+ if (this._budgetExhausted(bctx)) {
21443
+ bctx.truncated.add("community");
21333
21444
  this.logger.warn && this.logger.warn("market: community \u6E90\u9884\u7B97\u8017\u5C3D\uFF0C\u5DF2\u5904\u7406 " + i + "/" + links.length + " \u4E2A\u5019\u9009");
21334
21445
  break;
21335
21446
  }
@@ -21851,7 +21962,7 @@ var require_restart3 = __commonJS({
21851
21962
  log("\u91CD\u542F\u5B9E\u4F8B\u300C" + (target.name || target.id) + "\u300D\u4F7F\u63D2\u4EF6\u53D8\u66F4\u751F\u6548\u2026");
21852
21963
  if (ctx.events) ctx.events.append("plugin_restart_started", { name: target.name || target.id, target: target.id, kind });
21853
21964
  try {
21854
- ctx.instances.stopInstance(target.id, { intent: "transient" });
21965
+ ctx.instances.stopInstance(target.id);
21855
21966
  } catch (e) {
21856
21967
  log("\u505C\u6B62\u5B9E\u4F8B\u5931\u8D25: " + e.message);
21857
21968
  }
@@ -22562,10 +22673,6 @@ var require_managed_object = __commonJS({
22562
22673
  function registerKind(kind, meta) {
22563
22674
  _customKinds[kind] = Object.assign({ label: kind, startable: false, guardable: false }, meta || {});
22564
22675
  }
22565
- var DOMAIN_A_KINDS = /* @__PURE__ */ new Set(["dsh", "sandbox-instance"]);
22566
- function isDomainA(kind) {
22567
- return DOMAIN_A_KINDS.has(kind);
22568
- }
22569
22676
  function createEntry(o) {
22570
22677
  const meta = kindMeta(o.kind);
22571
22678
  if (!meta) throw new Error("\u672A\u77E5\u53D7\u7BA1\u5BF9\u8C61\u7C7B\u578B: " + o.kind + "\uFF08\u5148 registerKind \u58F0\u660E\uFF09");
@@ -22576,8 +22683,9 @@ var require_managed_object = __commonJS({
22576
22683
  name: String(o.name || o.id),
22577
22684
  // desired 两域共用字段名但语义不同:域 A=用户意图;域 B=「当前业务是否需要它」的条件
22578
22685
  desired: o.desired === "stopped" ? "stopped" : "running",
22579
- // guardian 域 A 专有(见上);域 B 不物化,旧残留才清得掉
22580
- ...isDomainA(o.kind) ? { guardian: o.guardian === true } : {},
22686
+ // guardian 开关的权威在域记录本身(dsh-main.json / inst.guardian),消费者全部直读源;
22687
+ // 目录曾在域 A entry 上物化该字段但零读者(B2-2 收口)。createEntry 永不物化 guardian 键
22688
+ // = 老库残留的天然一次性清理口(load 经本函数重建即消失),无需迁移脚本。
22581
22689
  ownership: normalizeOwnership(o.ownership),
22582
22690
  // 初始 stopped;业务不得直接改,由 heartbeat 调谐循环写入
22583
22691
  phase: "stopped",
@@ -22612,7 +22720,7 @@ var require_managed_object = __commonJS({
22612
22720
  // 域备注(只读参考)
22613
22721
  };
22614
22722
  }
22615
- module2.exports = { DESIRED, MANAGED_KINDS, kindMeta, registerKind, isDomainA, createEntry, normalizeOwnership };
22723
+ module2.exports = { DESIRED, MANAGED_KINDS, kindMeta, registerKind, createEntry, normalizeOwnership };
22616
22724
  }
22617
22725
  });
22618
22726
 
@@ -22679,6 +22787,13 @@ var require_heartbeat = __commonJS({
22679
22787
  registry._log("warn", "heartbeat " + (ad.supervise ? "supervise" : "observe") + "(" + e.kind + ":" + e.id + "): " + (err && err.message || err));
22680
22788
  }
22681
22789
  }
22790
+ if (typeof registry.onBeatDone === "function") {
22791
+ try {
22792
+ await registry.onBeatDone({ observed, errors });
22793
+ } catch (err) {
22794
+ registry._log("warn", "heartbeat onBeatDone: " + (err && err.message || err));
22795
+ }
22796
+ }
22682
22797
  return { observed, errors };
22683
22798
  }
22684
22799
  module2.exports = { runHeartbeat, withTimeout, ADAPTER_TIMEOUT_TICKS };
@@ -22692,7 +22807,7 @@ var require_registry3 = __commonJS({
22692
22807
  var fs2 = require("node:fs");
22693
22808
  var path2 = require("node:path");
22694
22809
  var { writeAtomic } = require_fs();
22695
- var { DESIRED, MANAGED_KINDS, kindMeta, registerKind: registerManagedKind, isDomainA, createEntry, normalizeOwnership } = require_managed_object();
22810
+ var { DESIRED, MANAGED_KINDS, kindMeta, registerKind: registerManagedKind, createEntry, normalizeOwnership } = require_managed_object();
22696
22811
  var PHASES = ["stopped", "installing", "starting", "running", "draining", "backoff", "failed", "restarting"];
22697
22812
  var { runHeartbeat } = require_heartbeat();
22698
22813
  var ManagedRegistry = class {
@@ -22743,7 +22858,7 @@ var require_registry3 = __commonJS({
22743
22858
  for (const o of arr) {
22744
22859
  try {
22745
22860
  if (!o || !kindMeta(o.kind)) continue;
22746
- const e = createEntry({ kind: o.kind, id: o.id, name: o.name, desired: o.desired, guardian: isDomainA(o.kind) ? o.guardian : void 0, ownership: o.ownership });
22861
+ const e = createEntry({ kind: o.kind, id: o.id, name: o.name, desired: o.desired, ownership: o.ownership });
22747
22862
  if (PHASES.includes(o.phase)) e.phase = o.phase;
22748
22863
  if (Number.isInteger(o.backoffLevel)) e.backoffLevel = o.backoffLevel;
22749
22864
  if (typeof o.backoffUntil === "number" && o.backoffUntil > Date.now()) e.backoffUntil = o.backoffUntil;
@@ -22770,7 +22885,7 @@ var require_registry3 = __commonJS({
22770
22885
  kind: o.kind,
22771
22886
  id: o.id,
22772
22887
  name: o.name,
22773
- // desired 两域共用(语义不同,见 createEntry);guardian 仅域 A 落盘。
22888
+ // desired 两域共用(语义不同,见 createEntry);guardian 不落盘(B2-2,权威在域记录)。
22774
22889
  desired: o.desired,
22775
22890
  ownership: o.ownership,
22776
22891
  phase: o.phase,
@@ -22782,7 +22897,7 @@ var require_registry3 = __commonJS({
22782
22897
  startedAt: o.startedAt,
22783
22898
  createdAt: o.createdAt,
22784
22899
  updatedAt: o.updatedAt
22785
- }, isDomainA(o.kind) ? { guardian: o.guardian === true } : {}))
22900
+ }))
22786
22901
  }, null, 2);
22787
22902
  writeAtomic(this.file, body, { mode: 384 });
22788
22903
  } catch (e) {
@@ -22844,8 +22959,8 @@ var require_registry3 = __commonJS({
22844
22959
  this._event("managed_object_registered", { kind: e.kind, id: e.id, name: e.name });
22845
22960
  return e;
22846
22961
  }
22847
- /** 对象变更申报(desired/guardian/ownership/name)。guardian 是域 A 专有:
22848
- * 域 B 基础设施既不写入,还主动删键清除早期版本残留(下一次 update 即归一)。 */
22962
+ /** 对象变更申报(desired/ownership/name)。guardian 不接受申报(B2-2):createEntry 永不
22963
+ * 物化该键,patch 里带 guardian 一律忽略,老库残留由 load 重建时清理。 */
22849
22964
  update(id, patch) {
22850
22965
  const e = this.get(id);
22851
22966
  if (!e) return { ok: false, error: "\u672A\u6CE8\u518C: " + id };
@@ -22854,11 +22969,6 @@ var require_registry3 = __commonJS({
22854
22969
  if (!DESIRED.includes(p.desired)) return { ok: false, error: "\u975E\u6CD5 desired: " + p.desired };
22855
22970
  e.desired = p.desired;
22856
22971
  }
22857
- if (isDomainA(e.kind)) {
22858
- if (p.guardian !== void 0) e.guardian = p.guardian === true;
22859
- } else if ("guardian" in e) {
22860
- delete e.guardian;
22861
- }
22862
22972
  if (p.name !== void 0) e.name = String(p.name || e.id);
22863
22973
  if (p.ownership !== void 0) {
22864
22974
  const old = e.ownership.ports;
@@ -23276,20 +23386,23 @@ var require_npm = __commonJS({
23276
23386
  const l = runtimeContract.npmLauncher();
23277
23387
  return { program: l.program, args: l.args };
23278
23388
  }
23279
- function resolveNpmRoot(host2) {
23389
+ async function resolveNpmRoot(host2) {
23280
23390
  if (host2.npmRoot) return host2.npmRoot;
23281
23391
  const l = npmLaunch(host2);
23282
- const r = ex2.runOut(l.program, l.args.concat(["root", "-g"]));
23392
+ const r = await ex2.runOutAsync(l.program, l.args.concat(["root", "-g"]));
23283
23393
  return r ? r.trim() : null;
23284
23394
  }
23285
- function checkEnvironment(host2) {
23395
+ async function checkEnvironment(host2) {
23286
23396
  const errors = [];
23287
- const nv = ex2.runOut("node", ["--version"]);
23288
- if (!nv || !nv.trim()) errors.push("node \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
23289
23397
  const l = npmLaunch(host2);
23290
- const npmv = ex2.runOut(l.program, l.args.concat(["--version"]));
23398
+ const [nv, npmv, npmRoot] = await Promise.all([
23399
+ ex2.runOutAsync("node", ["--version"]),
23400
+ ex2.runOutAsync(l.program, l.args.concat(["--version"])),
23401
+ resolveNpmRoot(host2)
23402
+ ]);
23403
+ if (!nv || !nv.trim()) errors.push("node \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
23291
23404
  if (!npmv || !npmv.trim()) errors.push("npm \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
23292
- return { ok: errors.length === 0, errors, npmRoot: resolveNpmRoot(host2) };
23405
+ return { ok: errors.length === 0, errors, npmRoot };
23293
23406
  }
23294
23407
  async function latestVersion(host2) {
23295
23408
  if (!host2.dist || !host2.config.packageName) throw new Error("\u5206\u53D1\u670D\u52A1\u672A\u521D\u59CB\u5316\uFF0C\u65E0\u6CD5\u67E5\u8BE2\u6700\u65B0\u7248\u672C");
@@ -23388,13 +23501,14 @@ var require_ops4 = __commonJS({
23388
23501
  }
23389
23502
  async function install(host2, version) {
23390
23503
  if (!policies.isValidVersion(version)) return { ok: false, error: "\u975E\u6CD5\u7248\u672C\u53F7: " + version };
23391
- const env = host2.checkEnvironment();
23392
- if (!env.ok) return { ok: false, error: "\u73AF\u5883\u68C0\u67E5\u5931\u8D25: " + env.errors.join("; ") };
23393
23504
  host2.installing = true;
23394
23505
  host2.installLog = [];
23395
- const task = beginTask(host2, "install", { to: version || null, createdBy: "user" });
23396
- let target = version;
23506
+ let task = null;
23397
23507
  try {
23508
+ const env = await host2.checkEnvironment();
23509
+ if (!env.ok) return { ok: false, error: "\u73AF\u5883\u68C0\u67E5\u5931\u8D25: " + env.errors.join("; ") };
23510
+ task = beginTask(host2, "install", { to: version || null, createdBy: "user" });
23511
+ let target = version;
23398
23512
  if (!target) {
23399
23513
  target = await host2._latestVersion().catch(() => null);
23400
23514
  if (!target) {
@@ -23417,7 +23531,7 @@ var require_ops4 = __commonJS({
23417
23531
  return { ok: false, error: res.error, output: res.output };
23418
23532
  }
23419
23533
  const isFirstInstall = !host2._manifest();
23420
- host2._recordManifest(target, isFirstInstall ? host2._claimDataPaths() : void 0);
23534
+ await host2._recordManifest(target, isFirstInstall ? host2._claimDataPaths() : void 0);
23421
23535
  try {
23422
23536
  if (typeof host2._bindNativeDshCommand === "function") host2._bindNativeDshCommand();
23423
23537
  } catch (e) {
@@ -23442,8 +23556,6 @@ var require_ops4 = __commonJS({
23442
23556
  if (host2.uninstalling) return { ok: false, error: "\u5378\u8F7D\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u88C5" };
23443
23557
  if (policies.busy(host2)) return { ok: false, error: "\u5347\u7EA7\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u88C5\uFF08state=" + host2.upgradeState + "\uFF09" };
23444
23558
  if (!policies.isValidVersion(version)) return { ok: false, error: "\u975E\u6CD5\u7248\u672C\u53F7: " + version };
23445
- const env = host2.checkEnvironment();
23446
- if (!env.ok) return { ok: false, error: "\u73AF\u5883\u68C0\u67E5\u5931\u8D25: " + env.errors.join("; ") };
23447
23559
  install(host2, version).then(() => {
23448
23560
  }).catch((e) => {
23449
23561
  host2.installing = null;
@@ -23636,7 +23748,7 @@ var require_upgrade2 = __commonJS({
23636
23748
  if (!res.ok) throw new Error(res.error || "install failed");
23637
23749
  const newV = host2.installedVersion();
23638
23750
  if (newV !== target) throw new Error("\u5B89\u88C5\u540E\u7248\u672C\u6821\u9A8C\u5931\u8D25\uFF1A\u671F\u671B " + target + "\uFF0C\u5B9E\u9645 " + newV);
23639
- host2._recordManifest(newV || target);
23751
+ await host2._recordManifest(newV || target);
23640
23752
  if (host2.events) host2.events.append("upgrade_installed", { from: oldV, to: target });
23641
23753
  log(host2, "\u5B89\u88C5\u5B8C\u6210\uFF0C\u78C1\u76D8\u7248\u672C " + newV);
23642
23754
  if (task) {
@@ -23706,9 +23818,9 @@ var require_upgrade2 = __commonJS({
23706
23818
  async function rollbackAfterFailedVerify(host2, oldV, task, healthy) {
23707
23819
  log(host2, "\u5065\u5EB7\u9A8C\u8BC1\u5931\u8D25\uFF08" + healthy.reason + "\uFF09");
23708
23820
  if (task) host2.tasks.log(task.id, "\u5065\u5EB7\u9A8C\u8BC1\u5931\u8D25\uFF08" + healthy.reason + "\uFF09");
23709
- host2.rolledBack = true;
23710
23821
  host2.upgradeState = "rolling_back";
23711
23822
  const rb = await rollbackNative(host2, oldV, task);
23823
+ host2.rolledBack = rb.ok === true;
23712
23824
  host2.upgradeError = rb.ok ? "\u5347\u7EA7\u5931\u8D25\uFF0C\u5DF2\u56DE\u6EDA\u5230 " + oldV : "\u5347\u7EA7\u5931\u8D25\u4E14\u56DE\u6EDA\u5931\u8D25\uFF1A" + (rb.error || "");
23713
23825
  host2.upgradeState = "failed";
23714
23826
  host2.upgradeFinishedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -23740,7 +23852,7 @@ var require_upgrade2 = __commonJS({
23740
23852
  }
23741
23853
  tlog("\u56DE\u6EDA\u5B8C\u6210\uFF0C\u78C1\u76D8\u7248\u672C " + oldVersion);
23742
23854
  try {
23743
- host2._recordManifest(oldVersion);
23855
+ await host2._recordManifest(oldVersion);
23744
23856
  } catch (e2) {
23745
23857
  tlog("manifest \u66F4\u65B0\u5931\u8D25: " + e2.message);
23746
23858
  }
@@ -23755,7 +23867,6 @@ var require_upgrade2 = __commonJS({
23755
23867
  }
23756
23868
  async function rollbackAfterFailure(host2) {
23757
23869
  host2.upgradeState = "rolling_back";
23758
- host2.rolledBack = true;
23759
23870
  if (host2.events) host2.events.append("upgrade_rollback_started", { to: host2.oldVersion });
23760
23871
  log(host2, "\u56DE\u6EDA\u5230 " + host2.oldVersion + "\u2026");
23761
23872
  const registry = await host2._selectRegistry();
@@ -23776,8 +23887,9 @@ var require_upgrade2 = __commonJS({
23776
23887
  return { ok: false };
23777
23888
  }
23778
23889
  log(host2, "\u56DE\u6EDA\u5B8C\u6210\u3002");
23890
+ host2.rolledBack = true;
23779
23891
  try {
23780
- host2._recordManifest(host2.oldVersion);
23892
+ await host2._recordManifest(host2.oldVersion);
23781
23893
  } catch (e2) {
23782
23894
  log(host2, "manifest \u66F4\u65B0\u5931\u8D25: " + e2.message);
23783
23895
  }
@@ -23927,8 +24039,8 @@ var require_installer = __commonJS({
23927
24039
  _saveManifest(m) {
23928
24040
  return manifest.save(this, m);
23929
24041
  }
23930
- _recordManifest(version, dataPaths) {
23931
- return manifest.record(this, version, dataPaths, this.npmRoot || npm.resolveNpmRoot(this));
24042
+ async _recordManifest(version, dataPaths) {
24043
+ return manifest.record(this, version, dataPaths, this.npmRoot || await npm.resolveNpmRoot(this));
23932
24044
  }
23933
24045
  _claimDataPaths() {
23934
24046
  return manifest.claimDataPaths(this);
@@ -24310,6 +24422,7 @@ var require_domains = __commonJS({
24310
24422
  host2.managedObjects.registerAdapter("dsh", { supervise: () => host2._dshSuperviseOnce(), tickEvery: 1 });
24311
24423
  host2.managedObjects.registerAdapter("sandbox-instance", { supervise: (entry) => host2._sandboxSuperviseOnce(entry), tickEvery: 1 });
24312
24424
  }
24425
+ if (host2.managedObjects) host2.managedObjects.onBeatDone = () => host2.instances.governSweep();
24313
24426
  } catch (e) {
24314
24427
  host2.logger && host2.logger.warn && host2.logger.warn("managed registry init: " + (e && e.message));
24315
24428
  }
@@ -24904,7 +25017,7 @@ var require_guard = __commonJS({
24904
25017
  }
24905
25018
  }
24906
25019
  if (req.method === "GET" && pathname === "/guard/version") {
24907
- return send(200, sup.guardVersionLocal());
25020
+ return Promise.resolve(sup.guardVersionLocal()).then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
24908
25021
  }
24909
25022
  if (req.method === "POST" && pathname === "/guard/version/check") {
24910
25023
  req.resume();
@@ -25049,7 +25162,7 @@ var require_guard = __commonJS({
25049
25162
  req.resume();
25050
25163
  return send(403, {});
25051
25164
  }
25052
- return send(200, sup.envStatus());
25165
+ return Promise.resolve(sup.envStatus()).then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
25053
25166
  }
25054
25167
  if (req.method === "GET" && pathname === "/env/node-lts") {
25055
25168
  return sup.nodeLtsStatus().then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
@@ -25704,7 +25817,7 @@ var require_instances = __commonJS({
25704
25817
  const r = sup.instances.updateInstance(j.id, j);
25705
25818
  return send(r && r.ok ? 200 : 400, r);
25706
25819
  }
25707
- if (act === "start" && j.id) return Promise.resolve(sup.instances.startInstance(j.id)).then((r) => send(r && r.ok ? 200 : 400, r)).catch((e) => send(500, { ok: false, error: e.message }));
25820
+ if (act === "start" && j.id) return Promise.resolve(sup.instances.startInstance(j.id, { manual: true })).then((r) => send(r && r.ok ? 200 : 400, r)).catch((e) => send(500, { ok: false, error: e.message }));
25708
25821
  if (act === "stop" && j.id) {
25709
25822
  const r = sup.instances.stopInstance(j.id);
25710
25823
  return send(r && r.ok ? 200 : 400, r);
@@ -26772,10 +26885,7 @@ var require_managed = __commonJS({
26772
26885
  const main = typeof mainOf === "function" ? mainOf() : null;
26773
26886
  return main ? [...sandboxes, main] : sandboxes;
26774
26887
  }
26775
- function findManaged(list, id) {
26776
- return (list || []).find((x) => x.id === id) || null;
26777
- }
26778
- module2.exports = { localAddresses, allManaged, findManaged };
26888
+ module2.exports = { localAddresses, allManaged };
26779
26889
  }
26780
26890
  });
26781
26891
 
@@ -27481,7 +27591,6 @@ var require_ops5 = __commonJS({
27481
27591
  this.instances = opts.instances;
27482
27592
  this.configPath = opts.configPath || "";
27483
27593
  this.mainOf = opts.mainOf || null;
27484
- this.persist = opts.persist || null;
27485
27594
  this.frp = opts.frp || new FrpManager({ dir: opts.stateDir, logger: this.logger, events: this.events });
27486
27595
  this.lanInstances = [];
27487
27596
  this._reconcileInFlight = null;
@@ -27492,31 +27601,10 @@ var require_ops5 = __commonJS({
27492
27601
  localAddresses() {
27493
27602
  return managed.localAddresses();
27494
27603
  }
27495
- /** 持久化:优先注入的 persist 路由,否则回退沙箱 instances.save()。 */
27496
- _saveAll() {
27497
- if (typeof this.persist === "function") {
27498
- try {
27499
- this.persist();
27500
- } catch (e) {
27501
- this.logger && this.logger.warn && this.logger.warn("lan persist: " + (e && e.message));
27502
- }
27503
- return;
27504
- }
27505
- if (this.instances && typeof this.instances.save === "function") {
27506
- try {
27507
- this.instances.save();
27508
- } catch {
27509
- }
27510
- }
27511
- }
27512
27604
  /** 受管 DSH 合成清单(沙箱 + 原生主干 main)。 */
27513
27605
  _allManaged() {
27514
27606
  return managed.allManaged({ instances: this.instances, mainOf: this.mainOf });
27515
27607
  }
27516
- /** 合成查找:按 id 取首个匹配。 */
27517
- _findManaged(id) {
27518
- return managed.findManaged(this._allManaged(), id);
27519
- }
27520
27608
  /** frpc 子进程句柄只读访问器:不暴露 frp 私有对象,供 daemon 优雅停机等待其退出。 */
27521
27609
  frpChild() {
27522
27610
  return this.frp && this.frp.child || null;
@@ -27796,13 +27884,6 @@ var require_supervisor = __commonJS({
27796
27884
  m.kind = "native";
27797
27885
  return m;
27798
27886
  },
27799
- persist: () => {
27800
- try {
27801
- if (this.instances && this.instances.save) this.instances.save();
27802
- } catch {
27803
- }
27804
- this._writeDshMain({});
27805
- },
27806
27887
  tokenOf: (id) => this.tokenService.get(id)
27807
27888
  });
27808
27889
  }
@@ -27918,28 +27999,45 @@ function printSelfVersion() {
27918
27999
  const { guardVersion } = require_version();
27919
28000
  console.log("dsh-supervisor v" + guardVersion());
27920
28001
  }
27921
- function apiRequest(method, apiPath) {
28002
+ function apiRaw(method, apiPath, body) {
27922
28003
  return new Promise((resolve) => {
27923
28004
  const addr = readApiAddress();
27924
28005
  const req = http.request(
27925
- { hostname: addr.host, port: addr.port, path: apiPath, method, timeout: 5e3 },
28006
+ {
28007
+ hostname: addr.host,
28008
+ port: addr.port,
28009
+ path: apiPath,
28010
+ method,
28011
+ timeout: 5e3,
28012
+ headers: body ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) } : {}
28013
+ },
27926
28014
  (res) => {
27927
- let body = "";
27928
- res.on("data", (d) => body += d);
27929
- res.on("end", () => {
27930
- try {
27931
- resolve(JSON.parse(body));
27932
- } catch {
27933
- resolve({ error: "parse error", raw: body });
27934
- }
27935
- });
27936
- res.on("error", () => resolve({ error: "response error" }));
28015
+ let text = "";
28016
+ res.setEncoding("utf8");
28017
+ res.on("data", (d) => text += d);
28018
+ res.on("end", () => resolve({ status: res.statusCode, text }));
28019
+ res.on("error", (e) => resolve({ status: 0, text: "", error: e.message }));
27937
28020
  }
27938
28021
  );
27939
- req.on("error", () => resolve({ error: "daemon \u672A\u8FD0\u884C\u6216\u8FDE\u63A5\u5931\u8D25" }));
28022
+ req.on("timeout", () => req.destroy(new Error("api-timeout")));
28023
+ req.on(
28024
+ "error",
28025
+ (e) => resolve({ status: 0, text: "", error: e && e.message === "api-timeout" ? "daemon \u54CD\u5E94\u8D85\u65F6\uFF085s\uFF09" : "daemon \u672A\u8FD0\u884C\u6216\u8FDE\u63A5\u5931\u8D25" })
28026
+ );
28027
+ if (body) req.write(body);
27940
28028
  req.end();
27941
28029
  });
27942
28030
  }
28031
+ function apiRequest(method, apiPath) {
28032
+ return apiRaw(method, apiPath).then(({ status, text, error }) => {
28033
+ if (error) return { error };
28034
+ try {
28035
+ return JSON.parse(text);
28036
+ } catch {
28037
+ return { error: "parse error", raw: text };
28038
+ }
28039
+ });
28040
+ }
27943
28041
  var LOCK_FILE = process.env.DSH_SUPERVISOR_LOCK_FILE || path.join(SUPERVISOR_DIR, "guard.lock");
27944
28042
  var LOCK_OWNER = { pid: process.pid, started: Date.now(), entry: process.argv[1] || "" };
27945
28043
  function readLock() {
@@ -28258,26 +28356,9 @@ function cmdVersion() {
28258
28356
  });
28259
28357
  }
28260
28358
  function cmdSelfUpdate(action) {
28261
- const addr = readApiAddress();
28262
- const apiOne = (method, p, body) => new Promise((resolve) => {
28263
- const req = http.request({ hostname: addr.host, port: addr.port, path: p, method, timeout: 5e3, headers: body ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) } : {} }, (res) => {
28264
- let b = "";
28265
- res.on("data", (d) => b += d);
28266
- res.on("end", () => {
28267
- try {
28268
- resolve(JSON.parse(b));
28269
- } catch {
28270
- resolve({ error: "parse error", raw: b });
28271
- }
28272
- });
28273
- });
28274
- req.on("error", () => resolve({ error: "daemon \u672A\u8FD0\u884C\u6216\u8FDE\u63A5\u5931\u8D25" }));
28275
- if (body) req.write(body);
28276
- req.end();
28277
- });
28278
28359
  (async () => {
28279
28360
  if (action === "check" || !action) {
28280
- const s = await apiOne("GET", "/self-update/status");
28361
+ const s = await apiRequest("GET", "/self-update/status");
28281
28362
  if (s.error) return console.log("\u68C0\u67E5\u5931\u8D25: " + s.error);
28282
28363
  if (s.ok) {
28283
28364
  console.log("\u5B88\u536B\u5F53\u524D\u7248\u672C: " + s.installed);
@@ -28295,31 +28376,13 @@ function cmdSelfUpdate(action) {
28295
28376
  })();
28296
28377
  }
28297
28378
  function cmdUpgrade(requested) {
28298
- const post = () => new Promise((resolve) => {
28299
- const addr = readApiAddress();
28300
- const body = requested ? JSON.stringify({ version: requested }) : void 0;
28301
- const req = http.request(
28302
- {
28303
- hostname: addr.host,
28304
- port: addr.port,
28305
- path: "/native/upgrade",
28306
- method: "POST",
28307
- timeout: 5e3,
28308
- headers: body ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) } : {}
28309
- },
28310
- (res) => {
28311
- let b = "";
28312
- res.on("data", (d) => b += d);
28313
- res.on("end", () => resolve({ code: res.statusCode, body: b }));
28314
- }
28315
- );
28316
- req.on("error", () => resolve({ code: 0, body: "" }));
28317
- if (body) req.write(body);
28318
- req.end();
28319
- });
28320
- post().then(({ code, body }) => {
28379
+ const body = requested ? JSON.stringify({ version: requested }) : void 0;
28380
+ const post = () => apiRaw("POST", "/native/upgrade", body);
28381
+ post().then(({ status, text, error }) => {
28382
+ const code = error ? 0 : status;
28383
+ const outText = error ? "" : text;
28321
28384
  if (code !== 202) {
28322
- console.log("\u5347\u7EA7\u672A\u88AB\u63A5\u53D7:", body || "HTTP " + code);
28385
+ console.log("\u5347\u7EA7\u672A\u88AB\u63A5\u53D7:", outText || "HTTP " + code);
28323
28386
  process.exit(1);
28324
28387
  }
28325
28388
  console.log("\u5347\u7EA7\u5DF2\u5F00\u59CB\uFF0C\u8DDF\u8E2A\u8FDB\u5EA6\uFF08\u5148\u505C DSH \u2192 \u5B89\u88C5 \u2192 \u81EA\u52A8\u62C9\u8D77\uFF0C\u9700\u6570\u5206\u949F\uFF09\u2026");