@dsh-sup/dsh-core-linux-x64 0.1.5-BETA.5 → 0.1.5-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 +228 -112
  2. package/package.json +1 -1
package/core.cjs CHANGED
@@ -80,6 +80,71 @@ var require_exec = __commonJS({
80
80
  }
81
81
  });
82
82
 
83
+ // src/platform/state-root.js
84
+ var require_state_root = __commonJS({
85
+ "src/platform/state-root.js"(exports2, module2) {
86
+ "use strict";
87
+ var fs2 = require("node:fs");
88
+ var os2 = require("node:os");
89
+ var path2 = require("node:path");
90
+ var SCHEMA = 1;
91
+ function root() {
92
+ const override = process.env.DSH_SUPERVISOR_HOME;
93
+ if (override && String(override).trim()) return path2.resolve(String(override).trim());
94
+ if (process.platform === "win32") {
95
+ const local = process.env.LOCALAPPDATA || path2.join(os2.homedir(), "AppData", "Local");
96
+ return path2.join(local, "dsh-supervisor");
97
+ }
98
+ if (process.platform === "darwin") {
99
+ return path2.join(os2.homedir(), "Library", "Application Support", "dsh-supervisor");
100
+ }
101
+ const xdg = process.env.XDG_STATE_HOME;
102
+ return xdg && String(xdg).trim() ? path2.join(String(xdg).trim(), "dsh-supervisor") : path2.join(os2.homedir(), ".local", "state", "dsh-supervisor");
103
+ }
104
+ function supervisorDir() {
105
+ return path2.join(root(), "supervisor");
106
+ }
107
+ function shellDir() {
108
+ return path2.join(root(), "shell");
109
+ }
110
+ function legacySupervisorDir() {
111
+ return path2.join(os2.homedir(), ".dsh", "supervisor");
112
+ }
113
+ function legacyShellDir() {
114
+ return path2.join(os2.homedir(), ".dsh", "shell");
115
+ }
116
+ function migrateLegacy() {
117
+ const moved = [];
118
+ for (const [from, to] of [
119
+ [legacySupervisorDir(), supervisorDir()],
120
+ [legacyShellDir(), shellDir()]
121
+ ]) {
122
+ try {
123
+ if (!fs2.existsSync(from)) continue;
124
+ fs2.mkdirSync(to, { recursive: true });
125
+ for (const name of fs2.readdirSync(from)) {
126
+ const src = path2.join(from, name);
127
+ const dst = path2.join(to, name);
128
+ if (fs2.existsSync(dst)) continue;
129
+ try {
130
+ fs2.renameSync(src, dst);
131
+ moved.push(src + " -> " + dst);
132
+ } catch {
133
+ }
134
+ }
135
+ try {
136
+ if (fs2.readdirSync(from).length === 0) fs2.rmdirSync(from);
137
+ } catch {
138
+ }
139
+ } catch {
140
+ }
141
+ }
142
+ return moved;
143
+ }
144
+ module2.exports = { SCHEMA, root, supervisorDir, shellDir, legacySupervisorDir, legacyShellDir, migrateLegacy };
145
+ }
146
+ });
147
+
83
148
  // src/platform/config.js
84
149
  var require_config = __commonJS({
85
150
  "src/platform/config.js"(exports2, module2) {
@@ -92,6 +157,7 @@ var require_config = __commonJS({
92
157
  if (p.startsWith("~/")) return path2.join(os2.homedir(), p.slice(2));
93
158
  return p;
94
159
  }
160
+ var SUP = require_state_root().supervisorDir();
95
161
  var DEFAULTS = {
96
162
  probeIntervalMs: 5e3,
97
163
  // 健康探测(三层):L0 进程存活 + L1 端口监听 + L2 HTTP GET healthUrl。
@@ -117,15 +183,15 @@ var require_config = __commonJS({
117
183
  // managed = relay/proxyInstance/oauthCallback 共享池(K8s 单一范围思想,杜绝段碎片化);
118
184
  // providerApi = 智能路由供应商独立端点池(按供应商规模调大)。null = 用内置默认池。
119
185
  portPools: null,
120
- stateFile: "~/.dsh/supervisor/state.json",
186
+ stateFile: path2.join(SUP, "state.json"),
121
187
  // 系统日志框架目录布局:log/ 与 events/ 分目录;
122
188
  // 守卫(guard) 事件在 events/guard.events.log、分级日志在 log/guard.log(daemon 用 router/lan 同构文件)。
123
189
  // 显式配置(既有生产 config.json / 测试)仍尊重用户给定路径——不强行改写。
124
- logFile: "~/.dsh/supervisor/events/guard.events.log",
190
+ logFile: path2.join(SUP, "events", "guard.events.log"),
125
191
  eventsMaxBytes: 5 * 1024 * 1024,
126
- supervisorLogFile: "~/.dsh/supervisor/log/guard.log",
127
- dshLogFile: "~/.dsh/supervisor/log/dsh.log",
128
- upgradeLogFile: "~/.dsh/supervisor/log/upgrade.log",
192
+ supervisorLogFile: path2.join(SUP, "log", "guard.log"),
193
+ dshLogFile: path2.join(SUP, "log", "dsh.log"),
194
+ upgradeLogFile: path2.join(SUP, "log", "upgrade.log"),
129
195
  logLevel: "info",
130
196
  logMaxBytes: 5 * 1024 * 1024,
131
197
  notifyEnabled: true,
@@ -223,7 +289,7 @@ var require_version = __commonJS({
223
289
  var fs2 = require("node:fs");
224
290
  var path2 = require("node:path");
225
291
  function guardVersion() {
226
- if (true) return String("0.1.5-BETA.5");
292
+ if (true) return String("0.1.5-BETA.7");
227
293
  try {
228
294
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
229
295
  } catch {
@@ -1033,7 +1099,48 @@ var require_exec_path = __commonJS({
1033
1099
  if (resolved) return resolved;
1034
1100
  return "npm.cmd";
1035
1101
  }
1036
- module2.exports = { resolveExecutable, candidateNames, standardDirs, firstExecutable, npmBin, npxBin };
1102
+ var DSH_PKG = ["@deepseek-ai", "dsh"];
1103
+ function dshJsIn(prefix) {
1104
+ return path2.join(prefix, "node_modules", ...DSH_PKG, "lib", "bin.js");
1105
+ }
1106
+ function resolveDsh(opts) {
1107
+ const o = opts || {};
1108
+ const pl = o.platform || process.platform;
1109
+ const env = o.env || process.env;
1110
+ const isFile = (p) => {
1111
+ try {
1112
+ return fs2.statSync(p).isFile();
1113
+ } catch {
1114
+ return false;
1115
+ }
1116
+ };
1117
+ const asJs = (bin, launcher) => ({ runtime: process.execPath, bin, isJs: true, launcher: launcher || null });
1118
+ if (env.DSH_BIN) {
1119
+ try {
1120
+ const r = fs2.realpathSync(env.DSH_BIN);
1121
+ if (isFile(r)) return asJs(r, env.DSH_BIN);
1122
+ } catch {
1123
+ }
1124
+ }
1125
+ const hit = resolveExecutable("dsh", { platform: pl, env });
1126
+ if (hit) {
1127
+ try {
1128
+ const real = fs2.realpathSync(hit);
1129
+ if (isFile(real) && /\.(js|cjs|mjs)$/i.test(real)) return asJs(real, hit);
1130
+ } catch {
1131
+ }
1132
+ const js = dshJsIn(path2.dirname(hit));
1133
+ if (isFile(js)) return asJs(js, hit);
1134
+ if (isFile(hit) && !/\.(cmd|bat|exe)$/i.test(hit)) return asJs(hit, hit);
1135
+ return { runtime: null, bin: hit, isJs: false, launcher: hit };
1136
+ }
1137
+ if (o.npmRoot) {
1138
+ const js = dshJsIn(o.npmRoot);
1139
+ if (isFile(js)) return asJs(js, null);
1140
+ }
1141
+ return null;
1142
+ }
1143
+ module2.exports = { resolveExecutable, candidateNames, standardDirs, firstExecutable, npmBin, npxBin, resolveDsh, dshJsIn };
1037
1144
  }
1038
1145
  });
1039
1146
 
@@ -1551,7 +1658,7 @@ var require_autostart = __commonJS({
1551
1658
  return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1552
1659
  }
1553
1660
  function macGuiPlist(guiExe) {
1554
- const log = path2.join(os2.homedir(), ".dsh", "shell", "gui-stdio.log");
1661
+ const log = path2.join(require_state_root().shellDir(), "gui-stdio.log");
1555
1662
  return '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n <key>Label</key><string>' + xmlEscape(GUI_LABEL) + "</string>\n <key>ProgramArguments</key>\n <array><string>" + xmlEscape(guiExe) + "</string></array>\n <key>RunAtLoad</key><true/>\n <key>LimitLoadToSessionType</key><string>Aqua</string>\n <key>ProcessType</key><string>Interactive</string>\n <key>StandardOutPath</key><string>" + xmlEscape(log) + "</string>\n <key>StandardErrorPath</key><string>" + xmlEscape(log) + "</string>\n</dict></plist>\n";
1556
1663
  }
1557
1664
  function guiFile() {
@@ -1605,54 +1712,14 @@ var require_autostart = __commonJS({
1605
1712
  const errors = [];
1606
1713
  if (isWindows) {
1607
1714
  try {
1608
- const watchdogPs1 = path2.join(os2.homedir(), ".dsh", "supervisor", "watchdog.ps1");
1609
1715
  if (on) {
1610
- const apiPort = process.env.DSH_SUPERVISOR_API_PORT || "36361";
1611
- const daemon = daemonCommand();
1612
- const guiPath = guiCommand();
1613
- const ps = [
1614
- '$ErrorActionPreference = "SilentlyContinue"',
1615
- "$port = " + JSON.stringify(String(apiPort)),
1616
- "$daemon = " + JSON.stringify(String(daemon)),
1617
- "$gui = " + JSON.stringify(String(guiPath)),
1618
- "$up = Test-NetConnection -ComputerName 127.0.0.1 -Port $port -InformationLevel Quiet -WarningAction SilentlyContinue",
1619
- "# \u2500\u2500 \u5B88\u536B\u4FDD\u6D3B\uFF08\u4EC5\u5728\u5B88\u536B\u4E0D\u53EF\u8FBE\u65F6\uFF09\u2500\u2500",
1620
- "if (-not $up) {",
1621
- " $p = @(Get-Process -Name dsh-supervisor -ErrorAction SilentlyContinue)",
1622
- " if (-not $p) { Start-Process -FilePath $daemon -ArgumentList 'daemon' -WindowStyle Hidden }",
1623
- "}",
1624
- "# \u2500\u2500 \u58F3\u4FDD\u6D3B\uFF08\u26A0 \u5FC5\u987B**\u72EC\u7ACB\u4E8E\u5B88\u536B\u72B6\u6001**\uFF09\u2500\u2500",
1625
- "# 2026-09-11 \u5BA1\u8BA1\u4FEE\u590D\uFF1A\u65E7\u5B9E\u73B0\u628A\u58F3\u68C0\u67E5\u5D4C\u5728\u4E0A\u9762\u7684 if (-not $up) \u5185\uFF0C",
1626
- "# \u4E8E\u662F\u300C\u58F3\u5D29\u3001\u5B88\u536B\u6D3B\u300D\u65F6 $up \u4E3A\u771F \u2192 \u6574\u5757\u8DF3\u8FC7 \u2192 **\u58F3\u6C38\u8FDC\u4E0D\u4F1A\u88AB\u62C9\u8D77**\u3002",
1627
- "# \u800C\u90A3\u6070\u662F\u58F3\u81EA\u6108\u552F\u4E00\u9700\u8981\u751F\u6548\u7684\u573A\u666F\uFF08\u5B88\u536B\u7531\u670D\u52A1\u7BA1\u7406\u5668\u4FDD\u6D3B\uFF0C\u58F3\u65E0\u4EBA\u7BA1\uFF09\u3002",
1628
- "$g = @(Get-Process -Name dsh-supervisor-gui -ErrorAction SilentlyContinue)",
1629
- "if (-not $g -and (Test-Path $gui)) { Start-Process -FilePath $gui -WindowStyle Hidden }",
1630
- "exit 0"
1631
- ].join(String.fromCharCode(13, 10));
1632
- fs2.mkdirSync(path2.dirname(watchdogPs1), { recursive: true });
1633
- const atmp = watchdogPs1 + ".tmp";
1634
- fs2.writeFileSync(atmp, ps);
1635
- fs2.renameSync(atmp, watchdogPs1);
1636
- {
1637
- const r = ex2.runDetail("schtasks", ["/Create", "/TN", "DSH-Supervisor-GUI", "/SC", "ONLOGON", "/RL", "HIGHEST", "/F", "/TR", '"' + guiCommand() + '"']);
1638
- if (!r.ok) errors.push("schtasks gui: " + (r.error || "\u6267\u884C\u5931\u8D25"));
1639
- }
1640
- ex2.run("schtasks", ["/Change", "/TN", "DSH-Supervisor", "/ENABLE"]);
1641
- {
1642
- const r = ex2.runDetail("schtasks", ["/Create", "/TN", "DSH-Supervisor-Watchdog", "/SC", "MINUTE", "/MO", "5", "/RL", "HIGHEST", "/F", "/TR", 'powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "' + watchdogPs1 + '"']);
1643
- if (!r.ok) errors.push("schtasks watchdog: " + (r.error || "\u6267\u884C\u5931\u8D25"));
1644
- }
1716
+ const r = ex2.runDetail("schtasks", ["/Create", "/TN", "DSH-Supervisor-GUI", "/SC", "ONLOGON", "/RL", "HIGHEST", "/F", "/TR", '"' + guiCommand() + '"']);
1717
+ if (!r.ok) errors.push("schtasks gui: " + (r.error || "\u6267\u884C\u5931\u8D25"));
1645
1718
  } else {
1646
- ex2.run("schtasks", ["/Delete", "/TN", "DSH-Supervisor-Watchdog", "/F"]);
1647
1719
  ex2.run("schtasks", ["/Delete", "/TN", "DSH-Supervisor-GUI", "/F"]);
1648
- ex2.run("schtasks", ["/Change", "/TN", "DSH-Supervisor", "/DISABLE"]);
1649
- try {
1650
- fs2.unlinkSync(watchdogPs1);
1651
- } catch {
1652
- }
1653
1720
  }
1654
1721
  } catch (e) {
1655
- errors.push("watchdog setup: " + e.message);
1722
+ errors.push("gui autostart: " + e.message);
1656
1723
  }
1657
1724
  return { ok: errors.length === 0, errors, ...status() };
1658
1725
  }
@@ -1834,7 +1901,7 @@ var require_os = __commonJS({
1834
1901
  return path2.join(os2.homedir(), ".dsh");
1835
1902
  }
1836
1903
  function supervisorDir() {
1837
- return path2.join(dataDir(), "supervisor");
1904
+ return require_state_root().supervisorDir();
1838
1905
  }
1839
1906
  function capabilityProfile(platform, arch) {
1840
1907
  const pl = platform || PLATFORM;
@@ -2067,7 +2134,7 @@ var require_ports = __commonJS({
2067
2134
  };
2068
2135
  var PortRegistry = class {
2069
2136
  constructor(opts) {
2070
- this._file = opts && opts.file || path2.join(os2.homedir(), ".dsh", "supervisor", "ports.json");
2137
+ this._file = opts && opts.file || path2.join(require_state_root().supervisorDir(), "ports.json");
2071
2138
  this._records = /* @__PURE__ */ new Map();
2072
2139
  this._allocLock = false;
2073
2140
  this._pools = Object.assign({}, DEFAULT_POOLS, opts && opts.pools || {});
@@ -3391,6 +3458,7 @@ var require_proxy = __commonJS({
3391
3458
  this.kind = "proxy";
3392
3459
  this.proxyAppId = opts.proxyAppId;
3393
3460
  this.app = opts.app || null;
3461
+ this.stateDir = opts.stateDir || null;
3394
3462
  this.proxyRunning = false;
3395
3463
  this.instances = [];
3396
3464
  this.selectedAccountKeyId = null;
@@ -3624,7 +3692,8 @@ var require_proxy = __commonJS({
3624
3692
  const logFilter = /error|streaming|idle|timeout|ECONN|abort|socket|finish|truncat/i;
3625
3693
  let logStream = null;
3626
3694
  try {
3627
- const logDir = require("node:path").join(require("node:os").homedir(), ".dsh", "supervisor", "logs");
3695
+ const baseDir = this.stateDir || require("../../platform/state-root").supervisorDir();
3696
+ const logDir = require("node:path").join(baseDir, "logs");
3628
3697
  require("node:fs").mkdirSync(logDir, { recursive: true });
3629
3698
  logStream = require("node:fs").createWriteStream(require("node:path").join(logDir, "proxy-instance-" + this.proxyAppId + "-" + port + ".log"), { flags: "a" });
3630
3699
  } catch {
@@ -4728,7 +4797,7 @@ var require_runtime_contract = __commonJS({
4728
4797
  var path2 = require("node:path");
4729
4798
  var SUPPORTED_SCHEMA = 2;
4730
4799
  function file() {
4731
- return path2.join(os2.homedir(), ".dsh", "supervisor", "runtime.json");
4800
+ return path2.join(require_state_root().supervisorDir(), "runtime.json");
4732
4801
  }
4733
4802
  function read() {
4734
4803
  let j;
@@ -4745,6 +4814,8 @@ var require_runtime_contract = __commonJS({
4745
4814
  nodePath: j.nodePath || node.path || null,
4746
4815
  nodeBinDir: j.nodeBinDir || node.binDir || null,
4747
4816
  npmPath: j.npmPath || npm.path || null,
4817
+ // 外壳可只提供包内 JS(npmPath=node,npmArgs=[npm-cli.js])——消费者必须带上 args。
4818
+ npmArgs: Array.isArray(j.npmArgs) ? j.npmArgs : Array.isArray(npm.args) ? npm.args : [],
4748
4819
  minNode: j.minNode || null,
4749
4820
  writtenBy: j.writtenBy || null,
4750
4821
  raw: j
@@ -6752,7 +6823,7 @@ var require_router = __commonJS({
6752
6823
  }
6753
6824
  }
6754
6825
  _deserializeProvider(p) {
6755
- const common = { id: p.id, name: p.name, logger: this.logger, events: this.events, dist: this.dist, onPersist: () => this._save(), apiPort: p.apiPort || null, activated: p.activated === true };
6826
+ const common = { id: p.id, name: p.name, logger: this.logger, events: this.events, dist: this.dist, onPersist: () => this._save(), stateDir: this.config && this.config.stateFile ? path2.dirname(this.config.stateFile) : null, apiPort: p.apiPort || null, activated: p.activated === true };
6756
6827
  let prov;
6757
6828
  if (p.kind === "proxy") {
6758
6829
  prov = new ProxyProvider({ ...common, kind: "proxy", proxyAppId: p.proxyAppId, app: PROXY_APPS[p.proxyAppId] || null });
@@ -10521,6 +10592,8 @@ var require_plugins = __commonJS({
10521
10592
  name: "\u539F\u751F\u5B9E\u4F8B",
10522
10593
  kind: "native",
10523
10594
  bin: this.dshBin,
10595
+ // 绑定后的原生入口可能是包内 JS(`.../lib/bin.js`)——必须用 node 承载(Windows 更甚:.js 不可直接执行)。
10596
+ runtime: /\.(js|cjs|mjs)$/i.test(this.dshBin) ? process.execPath : null,
10524
10597
  profileDir: this.profileDir,
10525
10598
  profileName: this.profileName,
10526
10599
  env: { HOME: os2.homedir(), PATH: this._pathExtra() }
@@ -10537,6 +10610,8 @@ var require_plugins = __commonJS({
10537
10610
  name: inst.name || inst.id,
10538
10611
  kind: "sandbox",
10539
10612
  bin: path2.join(installDir, "lib", "node_modules", "@deepseek-ai", "dsh", "lib", "bin.js"),
10613
+ runtime: process.execPath,
10614
+ // 包内 JS 入口:显式 node 承载(跨平台一致)
10540
10615
  installDir,
10541
10616
  profileDir: path2.join(dataDir, ".dsh", "profiles", this.profileName),
10542
10617
  profileName: this.profileName,
@@ -10846,7 +10921,9 @@ var require_plugins = __commonJS({
10846
10921
  try {
10847
10922
  const cliArgs = ["plugin", "--profile", target.profileName];
10848
10923
  if (target.storeDir) cliArgs.push("--store-dir", target.storeDir);
10849
- child = spawn(target.bin, [...cliArgs, ...args], { env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true, detached: true });
10924
+ const argv0 = target.runtime || target.bin;
10925
+ const argvPrefix = target.runtime ? [target.bin] : [];
10926
+ child = spawn(argv0, [...argvPrefix, ...cliArgs, ...args], { env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true, detached: true });
10850
10927
  } catch (e) {
10851
10928
  return settle({ ok: false, error: e.message });
10852
10929
  }
@@ -11763,7 +11840,7 @@ var require_shell = __commonJS({
11763
11840
  var path2 = require("node:path");
11764
11841
  var { semverCompare } = require_dist();
11765
11842
  function shellDir() {
11766
- return path2.join(os2.homedir(), ".dsh", "shell");
11843
+ return require_state_root().shellDir();
11767
11844
  }
11768
11845
  function readJson(p) {
11769
11846
  try {
@@ -12404,11 +12481,30 @@ var require_manager2 = __commonJS({
12404
12481
  if (this.installLog.length > 60) this.installLog.splice(0, this.installLog.length - 60);
12405
12482
  }
12406
12483
  /* ═══════ 安装状态探测 ═══════ */
12484
+ /** 原生 DSH 真实安装(**唯一检测入口**,2026-09-16 架构修正):
12485
+ * 此前只读静态 `config.command[1]`(出厂默认裸名 'dsh')→ `fs.existsSync('dsh')` 恒 false →
12486
+ * 「已安装」永远判不出来,与「安装」分支形成两套相反逻辑(系统已装 DSH,守卫却报未安装)。
12487
+ * · 已绑定/用户显式指定且**真实存在**的绝对路径 → 尊重之;
12488
+ * · 否则跨平台解析:PATH/PATHEXT → 标准落点 → 包内 `lib/bin.js`(exec-path.resolveDsh)。 */
12489
+ detected() {
12490
+ const cmd2 = Array.isArray(this.config.command) ? this.config.command : [];
12491
+ const configured = cmd2[1];
12492
+ const isBare = !configured || configured === "dsh" || configured === "dsh.cmd" || !/[\\/]/.test(configured) && !String(configured).startsWith("~");
12493
+ if (!isBare) {
12494
+ return { bin: configured, runtime: cmd2[0] || null, isJs: /\.(js|cjs|mjs)$/i.test(configured) };
12495
+ }
12496
+ try {
12497
+ return execPath.resolveDsh({ npmRoot: this.npmRoot });
12498
+ } catch {
12499
+ return null;
12500
+ }
12501
+ }
12407
12502
  binPath() {
12503
+ const d = this.detected();
12504
+ if (d && d.bin) return d.bin;
12408
12505
  const bin = this.config.command && this.config.command[1];
12409
12506
  if (!bin) return null;
12410
- const p = bin === "~" ? os2.homedir() : bin.startsWith("~/") ? path2.join(os2.homedir(), bin.slice(2)) : bin;
12411
- return p;
12507
+ return bin === "~" ? os2.homedir() : bin.startsWith("~/") ? path2.join(os2.homedir(), bin.slice(2)) : bin;
12412
12508
  }
12413
12509
  /** 已安装版本:优先显式配置(installedPkgJsonPath),否则从 bin 所在目录向上找 package.json。未安装返回 null(唯一探测实现)。 */
12414
12510
  installedVersion() {
@@ -17489,9 +17585,21 @@ var require_env_catalog = __commonJS({
17489
17585
  const min = String(runtimeMeta().minNode || MIN_NODE_DEFAULT);
17490
17586
  return { version: "v" + ver, min, meets: verAtLeast(ver, min) };
17491
17587
  }
17588
+ function probeNpm() {
17589
+ try {
17590
+ const c = require_runtime_contract().read();
17591
+ if (c && c.npmPath) {
17592
+ const args = Array.isArray(c.npmArgs) ? c.npmArgs : [];
17593
+ const v = ex2.runOut(c.npmPath, [...args, "--version"], { timeoutMs: 3e3 });
17594
+ if (v && v.trim()) return v.trim();
17595
+ }
17596
+ } catch {
17597
+ }
17598
+ return cachedWhichVersion("npm");
17599
+ }
17492
17600
  var SYSTEM_ENTRIES = {
17493
17601
  node: { label: "Node.js", required: true, probe: probeNode },
17494
- npm: { label: "npm", required: true, probe: () => cachedWhichVersion("npm") },
17602
+ npm: { label: "npm", required: true, probe: probeNpm },
17495
17603
  git: { label: "git", required: false, probe: () => cachedWhichVersion("git") }
17496
17604
  };
17497
17605
  var EnvCatalog = class {
@@ -19631,6 +19739,7 @@ var require_supervisor = __commonJS({
19631
19739
  } catch (e) {
19632
19740
  this.logger.warn && this.logger.warn("ports pools configure: " + (e && e.message));
19633
19741
  }
19742
+ this._bindNativeDshCommand();
19634
19743
  this.instances = new InstanceManager({
19635
19744
  dir: path2.dirname(this.config.stateFile),
19636
19745
  logger: this.logger,
@@ -19715,7 +19824,7 @@ var require_supervisor = __commonJS({
19715
19824
  logger: this.logger
19716
19825
  });
19717
19826
  this.pluginManager = new PluginManager({
19718
- dshBin: "dsh",
19827
+ dshBin: this.config.command && this.config.command[1] ? this.config.command[1] : "dsh",
19719
19828
  profileName: this.config.pluginsProfileName || "web",
19720
19829
  profileDir: path2.join(os2.homedir(), ".dsh", "profiles", this.config.pluginsProfileName || "web"),
19721
19830
  overlayFile: path2.join(path2.dirname(this.config.stateFile), "plugin-states.patch.yml"),
@@ -19763,6 +19872,40 @@ var require_supervisor = __commonJS({
19763
19872
  });
19764
19873
  this._registerFixedPorts();
19765
19874
  }
19875
+ /** 原生 DSH 检测 → 绑定(2026-09-16 架构修正)。
19876
+ *
19877
+ * 原生 DSH 此前只被静态 `config.command[1]`(出厂默认裸名 'dsh')定义 —— `node dsh` 不做 PATH 解析、
19878
+ * Windows 裸名无扩展名 → 「已安装」永远判 false,与「安装」分支形成两套相反逻辑,
19879
+ * 且会去装第二个 DSH 顶替原生的那个。
19880
+ *
19881
+ * 这里在**任何消费者之前**把无法解析的 command[1] 解析为真实绝对入口:
19882
+ * 包内 JS → `['<node>', '<abs lib/bin.js>', ...rest]`;仅垫片 → `['<abs shim>', ...rest]`。
19883
+ * 用户显式给出且真实存在的路径**原样尊重**(不覆盖)。检测与接管由此同源:
19884
+ * 已装 → 绑定并接管;未装 → 由 NativeManager 安装后重新绑定。 */
19885
+ _bindNativeDshCommand() {
19886
+ try {
19887
+ const cmd2 = Array.isArray(this.config.command) ? this.config.command.slice() : [];
19888
+ const cur = cmd2[1];
19889
+ const isBare = !cur || cur === "dsh" || cur === "dsh.cmd" || !/[\\/]/.test(cur) && !String(cur).startsWith("~");
19890
+ if (!isBare) return;
19891
+ const d = require_exec_path().resolveDsh();
19892
+ if (!d || !d.bin) return;
19893
+ this.config.command = d.isJs ? [d.runtime || process.execPath, d.bin, ...cmd2.slice(2)] : [d.bin, ...cmd2.slice(2)];
19894
+ try {
19895
+ this.events && this.events.append("dsh_command_bound", { from: cur || null, to: this.config.command[1] });
19896
+ } catch {
19897
+ }
19898
+ try {
19899
+ this.logger.info && this.logger.info("\u539F\u751F DSH \u5DF2\u7ED1\u5B9A: " + this.config.command.join(" "));
19900
+ } catch {
19901
+ }
19902
+ } catch (e) {
19903
+ try {
19904
+ this.logger.warn && this.logger.warn("\u539F\u751F DSH \u7ED1\u5B9A\u5931\u8D25: " + (e && e.message));
19905
+ } catch {
19906
+ }
19907
+ }
19908
+ }
19766
19909
  /** 固定端口统一登记:主DSH / 守卫API / 中转服务。冲突即抛错(守卫启动失败,避免带病运行)。
19767
19910
  * 主程序端口动态注册:用户使用场景各异(可能先装 DSH 并自定义端口)——
19768
19911
  * 若配置端口无监听且检测到 DSH 进程,从进程实际参数解析端口并动态覆盖(绝不硬编码 3080)。 */
@@ -19835,10 +19978,20 @@ var require_supervisor = __commonJS({
19835
19978
  });
19836
19979
  server.listen(port, this.config.apiHost, () => {
19837
19980
  this.api = server;
19838
- if (port !== this.config.apiPort) {
19981
+ const prev = this.config.apiPort;
19982
+ if (port !== prev) {
19983
+ try {
19984
+ ports.release(prev, "system:supervisor-api");
19985
+ } catch {
19986
+ }
19839
19987
  this.config.apiPort = port;
19840
19988
  if (this.configPath) this.persistConfigPatch({ apiPort: port });
19841
19989
  }
19990
+ try {
19991
+ ports.register("supervisor-api", port);
19992
+ } catch (e) {
19993
+ this.logger.warn("ports.register(actual) \u5931\u8D25: " + e.message);
19994
+ }
19842
19995
  this.events.append("api_listening", { host: this.config.apiHost, port });
19843
19996
  this.logger.info("api listening on " + this.config.apiHost + ":" + port);
19844
19997
  });
@@ -20340,6 +20493,10 @@ var require_supervisor = __commonJS({
20340
20493
  server.listen(this.config.apiPort, this.config.apiHost, () => {
20341
20494
  bind._tries = 0;
20342
20495
  this.api = server;
20496
+ try {
20497
+ ports.register("supervisor-api", this.config.apiPort);
20498
+ } catch {
20499
+ }
20343
20500
  this.events.append("api_listening", { host: this.config.apiHost, port: this.config.apiPort });
20344
20501
  this.logger.info("api listening on " + this.config.apiHost + ":" + this.config.apiPort);
20345
20502
  });
@@ -20720,7 +20877,8 @@ function findPackageRoot(start) {
20720
20877
  return path.join(__dirname, "..");
20721
20878
  }
20722
20879
  var ROOT = findPackageRoot(__dirname);
20723
- var SUPERVISOR_DIR = path.join(os.homedir(), ".dsh", "supervisor");
20880
+ var stateRoot = require_state_root();
20881
+ var SUPERVISOR_DIR = stateRoot.supervisorDir();
20724
20882
  var USER_CONFIG = path.join(SUPERVISOR_DIR, "config.json");
20725
20883
  var DEFAULT_CONFIG = Object.assign(
20726
20884
  {},
@@ -20730,10 +20888,6 @@ var DEFAULT_CONFIG = Object.assign(
20730
20888
  var STATE_FILE = path.join(SUPERVISOR_DIR, "state.json");
20731
20889
  var EVENTS_FILE = path.join(SUPERVISOR_DIR, "events", "guard.events.log");
20732
20890
  var UNIT_PATH = path.join(os.homedir(), ".config", "systemd", "user", "dsh-supervisor.service");
20733
- var UNIT_TEMPLATE = path.join(ROOT, "systemd", "dsh-supervisor.service");
20734
- var DESKTOP_DIR = path.join(os.homedir(), ".local", "share", "applications");
20735
- var ICON_DIR = path.join(os.homedir(), ".local", "share", "icons");
20736
- var DESKTOP_TEMPLATE = path.join(ROOT, "desktop", "dsh-supervisor.desktop");
20737
20891
  var AUTOSTART_TEMPLATE = path.join(ROOT, "desktop", "dsh-supervisor-gui-autostart.desktop");
20738
20892
  var AUTOSTART_FILE = path.join(os.homedir(), ".config", "autostart", "dsh-supervisor-gui-autostart.desktop");
20739
20893
  var IS_WINDOWS = process.platform === "win32";
@@ -20866,6 +21020,8 @@ function releaseLock() {
20866
21020
  }
20867
21021
  }
20868
21022
  function cmdDaemon() {
21023
+ const moved = stateRoot.migrateLegacy();
21024
+ for (const m of moved) console.log("[migrate] \u72B6\u6001\u76EE\u5F55\u8FC1\u79FB: " + m);
20869
21025
  if (!acquireLock()) {
20870
21026
  console.error("[supervisor] \u5DF2\u6709\u5B88\u536B\u5B9E\u4F8B\u5728\u8FD0\u884C\uFF08\u9501 " + LOCK_FILE + "\uFF09\uFF0C\u672C\u8FDB\u7A0B\u9000\u51FA\u3002");
20871
21027
  process.exit(1);
@@ -20982,6 +21138,8 @@ function finishControl(r) {
20982
21138
  }
20983
21139
  }
20984
21140
  function cmdInstall() {
21141
+ const moved = stateRoot.migrateLegacy();
21142
+ for (const m of moved) console.log("[migrate] \u72B6\u6001\u76EE\u5F55\u8FC1\u79FB: " + m);
20985
21143
  fs.mkdirSync(SUPERVISOR_DIR, { recursive: true });
20986
21144
  if (!fs.existsSync(USER_CONFIG)) {
20987
21145
  fs.mkdirSync(SUPERVISOR_DIR, { recursive: true });
@@ -21004,50 +21162,8 @@ function cmdInstall() {
21004
21162
  fs.symlinkSync(path.join(ROOT, "bin", "dsh-supervisor"), BIN_PATH);
21005
21163
  console.log("[install] \u547D\u4EE4\u884C\u94FE\u63A5: " + BIN_PATH + " \u2192 " + __dirname + "/dsh-supervisor");
21006
21164
  }
21007
- if (!fs.existsSync(UNIT_TEMPLATE)) {
21008
- console.warn("[install] \u672A\u627E\u5230 systemd \u6A21\u677F\uFF08" + UNIT_TEMPLATE + "\uFF09\u2014\u2014npm \u5185\u6838\u5305\u5F62\u6001\uFF0C\u8DF3\u8FC7\u7CFB\u7EDF\u670D\u52A1\u90E8\u7F72\u3002");
21009
- console.warn("[install] \u5982\u9700 systemd \u5E38\u9A7B\uFF0C\u8BF7\u4ECE\u6E90\u7801\u4ED3\u5B89\u88C5\uFF08systemd/dsh-supervisor.service\uFF09\u6216\u624B\u52A8\u8FD0\u884C\uFF1Adsh-supervisor daemon");
21010
- } else {
21011
- let unit = fs.readFileSync(UNIT_TEMPLATE, "utf8");
21012
- unit = unit.replace("@BIN@", BIN_PATH);
21013
- const unitDir = path.dirname(UNIT_PATH);
21014
- fs.mkdirSync(unitDir, { recursive: true });
21015
- fs.writeFileSync(UNIT_PATH, unit);
21016
- console.log("[install] systemd unit: " + UNIT_PATH);
21017
- try {
21018
- execInherit("systemctl", ["--user", "daemon-reload"]);
21019
- execInherit("systemctl", ["--user", "enable", "dsh-supervisor.service"]);
21020
- } catch (e) {
21021
- console.warn("[install] systemctl \u6267\u884C\u5931\u8D25\uFF08\u53EF\u80FD\u4E0D\u5728 systemd \u4F1A\u8BDD\u4E2D\uFF09:", e.message);
21022
- }
21023
- try {
21024
- execInherit("loginctl", ["enable-linger", os.userInfo().username]);
21025
- console.log("[install] loginctl enable-linger \u5DF2\u542F\u7528");
21026
- } catch {
21027
- console.warn("[install] enable-linger \u5931\u8D25\uFF0C\u5F00\u673A\u81EA\u542F\u53EF\u80FD\u4E0D\u751F\u6548");
21028
- }
21029
- installDesktopEntry();
21030
- console.log("\n\u5B89\u88C5\u5B8C\u6210\u3002\u542F\u52A8\u5B88\u536B\uFF1A\n systemctl --user start dsh-supervisor");
21031
- console.log("\u67E5\u770B\u72B6\u6001\uFF1A\n dsh-supervisor status");
21032
- }
21033
- }
21034
- function installDesktopEntry() {
21035
- try {
21036
- if (!fs.existsSync(DESKTOP_TEMPLATE)) return;
21037
- fs.mkdirSync(ICON_DIR, { recursive: true });
21038
- const shellDir = process.env.DSH_SHELL_DIR || null;
21039
- const iconSrc = shellDir ? path.join(shellDir, "src-tauri", "icons", "128x128.png") : null;
21040
- const iconDst = path.join(ICON_DIR, "dsh-supervisor.png");
21041
- if (iconSrc && fs.existsSync(iconSrc)) fs.copyFileSync(iconSrc, iconDst);
21042
- else console.warn("[install] \u672A\u627E\u5230\u58F3\u56FE\u6807\uFF08\u53EF\u8BBE DSH_SHELL_DIR \u6307\u5411\u58F3 checkout\uFF09\uFF0C\u684C\u9762\u56FE\u6807\u7F3A\u5931\uFF1B\u4E0D\u5F71\u54CD\u529F\u80FD\u3002");
21043
- fs.mkdirSync(DESKTOP_DIR, { recursive: true });
21044
- const entry = fs.readFileSync(DESKTOP_TEMPLATE, "utf8").replace(/@HOME@/g, os.homedir());
21045
- const dst = path.join(DESKTOP_DIR, "dsh-supervisor.desktop");
21046
- fs.writeFileSync(dst, entry);
21047
- console.log("[install] \u684C\u9762\u83DC\u5355\u5165\u53E3: " + dst);
21048
- } catch (e) {
21049
- console.warn("[install] \u684C\u9762\u5165\u53E3\u90E8\u7F72\u5931\u8D25:", e.message);
21050
- }
21165
+ console.log("[install] \u670D\u52A1\u5B9A\u4E49/\u5F00\u673A\u81EA\u542F/\u684C\u9762\u5165\u53E3\u7531\u684C\u9762\u58F3\u8D1F\u8D23\uFF08\u672C\u547D\u4EE4\u4E0D\u518D\u90E8\u7F72\uFF09\u3002");
21166
+ console.log("[install] \u5185\u6838\u672C\u4F53\u5DF2\u5C31\u7EEA\u3002\u542F\u52A8\u7531\u684C\u9762\u58F3\u6216\u670D\u52A1\u7BA1\u7406\u5668\u53D1\u8D77\uFF1Adsh-supervisor daemon");
21051
21167
  }
21052
21168
  function cmdGuiAutostart(onoff) {
21053
21169
  if (onoff === "on") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-sup/dsh-core-linux-x64",
3
- "version": "0.1.5-BETA.5",
3
+ "version": "0.1.5-BETA.7",
4
4
  "description": "DSH lifecycle guard core (Node launcher) for linux-x64 — requires Node >=18.",
5
5
  "license": "UNLICENSED",
6
6
  "os": [