@dsh-sup/dsh-core-linux-x64 0.1.5-BETA.4 → 0.1.5-BETA.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core.cjs CHANGED
@@ -131,15 +131,13 @@ var require_config = __commonJS({
131
131
  notifyEnabled: true,
132
132
  routerAutostart: false,
133
133
  // 智能路由启动开关(旧键 switcherAutoStart 已迁移)
134
- // 守卫自更新(2026-09 收敛:npm 通道取代 manifest):corePackageName = 内核自身 npm 子包名
135
- //(形如 @dsh-sup/dsh-core-linux-x64,按平台/架构发布;null = 不启用自更新)。
134
+ // 内核更新(单写入者 = 桌面壳):corePackageName = 内核自身 npm 子包名
135
+ //(形如 @dsh-sup/dsh-core-linux-x64,按平台/架构发布)。守卫**只读**它来查询版本状态;
136
+ //安装/升级由桌面壳执行(见 RELEASE-AND-UPDATE-MECHANISM.md §6)。
136
137
  corePackageName: null,
137
- // ⚠ `selfUpdateManifestUrl` / `selfUpdateDir` 是**旧 manifest 模式的残留键**(2026-09-12 核实):
138
- // 全仓**无任何赋值点**(默认恒 null),其唯一消费方 `env-catalog.selfUpdateEntry()` 已改为
139
- // 按 `corePackageName` 判定。保留它们只为兼容既有用户 config.json(读到即忽略,不报错)。
140
- // 新代码**不得**再依赖这两个键。
141
- selfUpdateManifestUrl: null,
142
- selfUpdateDir: null,
138
+ // ⚠ 2026-09-15:旧 manifest 模式的残留键 `selfUpdateManifestUrl` / `selfUpdateDir` **已删除**
139
+ // (全仓无赋值点;其唯一消费方曾改为按 corePackageName 判定)。既有用户 config.json 若仍含
140
+ // 这两键,加载时忽略即可(未知键不报错)。新代码**不得**再引入 manifest 更新通道。
143
141
  pluginsProfileName: "web",
144
142
  packageName: "@deepseek-ai/dsh",
145
143
  // **最小兜底**镜像源(2026-09-11 契约化)。
@@ -225,7 +223,7 @@ var require_version = __commonJS({
225
223
  var fs2 = require("node:fs");
226
224
  var path2 = require("node:path");
227
225
  function guardVersion() {
228
- if (true) return String("0.1.5-BETA.4");
226
+ if (true) return String("0.1.5-BETA.5");
229
227
  try {
230
228
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
231
229
  } catch {
@@ -4721,6 +4719,60 @@ var require_proxy_apps = __commonJS({
4721
4719
  }
4722
4720
  });
4723
4721
 
4722
+ // src/platform/runtime-contract.js
4723
+ var require_runtime_contract = __commonJS({
4724
+ "src/platform/runtime-contract.js"(exports2, module2) {
4725
+ "use strict";
4726
+ var fs2 = require("node:fs");
4727
+ var os2 = require("node:os");
4728
+ var path2 = require("node:path");
4729
+ var SUPPORTED_SCHEMA = 2;
4730
+ function file() {
4731
+ return path2.join(os2.homedir(), ".dsh", "supervisor", "runtime.json");
4732
+ }
4733
+ function read() {
4734
+ let j;
4735
+ try {
4736
+ j = JSON.parse(fs2.readFileSync(file(), "utf8"));
4737
+ } catch {
4738
+ return null;
4739
+ }
4740
+ if (!j || typeof j !== "object" || Array.isArray(j)) return null;
4741
+ const node = j.node && typeof j.node === "object" ? j.node : {};
4742
+ const npm = j.npm && typeof j.npm === "object" ? j.npm : {};
4743
+ return {
4744
+ schema: Number(j.schema) || 1,
4745
+ nodePath: j.nodePath || node.path || null,
4746
+ nodeBinDir: j.nodeBinDir || node.binDir || null,
4747
+ npmPath: j.npmPath || npm.path || null,
4748
+ minNode: j.minNode || null,
4749
+ writtenBy: j.writtenBy || null,
4750
+ raw: j
4751
+ };
4752
+ }
4753
+ function npmBin(fallback) {
4754
+ const c = read();
4755
+ if (c && c.npmPath) {
4756
+ try {
4757
+ if (fs2.existsSync(c.npmPath)) return c.npmPath;
4758
+ } catch {
4759
+ }
4760
+ }
4761
+ return typeof fallback === "function" ? fallback() : fallback;
4762
+ }
4763
+ function withPath(env) {
4764
+ const e = Object.assign({}, env || {});
4765
+ const c = read();
4766
+ if (c && c.nodeBinDir) {
4767
+ const cur = e.PATH || e.Path || "";
4768
+ e.PATH = c.nodeBinDir + path2.delimiter + cur;
4769
+ }
4770
+ return e;
4771
+ }
4772
+ module2.exports = { SUPPORTED_SCHEMA, file, read, npmBin, withPath };
4773
+ }
4774
+ });
4775
+
4724
4776
  // src/platform/registry-contract.js
4725
4777
  var require_registry_contract = __commonJS({
4726
4778
  "src/platform/registry-contract.js"(exports2, module2) {
@@ -4832,6 +4884,7 @@ var require_dist = __commonJS({
4832
4884
  var matrix = require_matrix();
4833
4885
  var { spawn } = require("node:child_process");
4834
4886
  var { npmBin } = require_exec_path();
4887
+ var runtimeContract = require_runtime_contract();
4835
4888
  var registryContract = require_registry_contract();
4836
4889
  var CONTRACT_TTL_MS = 60 * 1e3;
4837
4890
  var service = require_service().current();
@@ -5227,17 +5280,17 @@ var require_dist = __commonJS({
5227
5280
  const pkg = o.pkg || "@deepseek-ai/dsh";
5228
5281
  if (!o.version) return Promise.resolve({ ok: false, error: "runNpmInstall: \u7F3A\u5C11 version\uFF08\u5FC5\u987B\u663E\u5F0F\u643A\u5E26\uFF09", output: [] });
5229
5282
  let argv;
5230
- let bin = npmBin();
5283
+ let bin = runtimeContract.npmBin(npmBin);
5231
5284
  if (Array.isArray(o.commandTemplate) && o.commandTemplate.length) {
5232
5285
  argv = o.commandTemplate.map((s) => String(s).replace(/{pkg}/g, pkg).replace(/{version}/g, o.version).replace(/{prefix}/g, o.prefix || ""));
5233
- bin = argv[0] === "npm" ? npmBin() : argv[0];
5286
+ bin = argv[0] === "npm" ? runtimeContract.npmBin(npmBin) : argv[0];
5234
5287
  argv = argv.slice(1);
5235
5288
  } else {
5236
5289
  argv = ["install", "-g", "--no-audit", "--no-fund"];
5237
5290
  if (o.prefix) argv.push("--prefix", o.prefix);
5238
5291
  argv.push(pkg + "@" + o.version);
5239
5292
  }
5240
- const envVars = Object.assign({}, process.env);
5293
+ const envVars = runtimeContract.withPath(process.env);
5241
5294
  if (o.registry) {
5242
5295
  envVars.npm_config_registry = o.registry;
5243
5296
  envVars.NPM_CONFIG_REGISTRY = o.registry;
@@ -10387,60 +10440,6 @@ var require_fs_utils = __commonJS({
10387
10440
  "use strict";
10388
10441
  var fs2 = require("node:fs");
10389
10442
  var path2 = require("node:path");
10390
- var zlib = require("node:zlib");
10391
- function extractTarGz(tgzBuf, destDir, opts) {
10392
- const strip = opts && opts.stripComponents !== void 0 ? opts.stripComponents : 1;
10393
- const tarData = zlib.gunzipSync(tgzBuf);
10394
- fs2.mkdirSync(destDir, { recursive: true });
10395
- const written = [];
10396
- const stripName = (name) => {
10397
- const parts = String(name).split("/").filter((p) => p !== "" && p !== ".");
10398
- const cut = Math.min(strip, parts.length - 1);
10399
- const rel = parts.slice(cut).join("/");
10400
- if (!rel || rel.split("/").some((s) => s === "..") || rel.startsWith("/")) return null;
10401
- return rel;
10402
- };
10403
- let offset = 0;
10404
- let pendingLong = null;
10405
- while (offset + 512 <= tarData.length) {
10406
- const header = tarData.slice(offset, offset + 512);
10407
- if (header.every((b) => b === 0)) break;
10408
- let name = header.slice(0, 100).toString("utf8").split("\0")[0];
10409
- const size = parseInt(header.slice(124, 136).toString("utf8").replace(/[\0 ]/g, ""), 8) || 0;
10410
- const typeFlag = String.fromCharCode(header[156] || 48);
10411
- const dataStart = offset + 512;
10412
- const data = tarData.slice(dataStart, dataStart + size);
10413
- offset = dataStart + Math.ceil(size / 512) * 512;
10414
- if (pendingLong !== null) {
10415
- name = pendingLong;
10416
- pendingLong = null;
10417
- }
10418
- if (typeFlag === "L") {
10419
- pendingLong = name;
10420
- continue;
10421
- }
10422
- const rel = stripName(name);
10423
- if (rel === null) continue;
10424
- const target = path2.join(destDir, rel);
10425
- if (typeFlag === "5") {
10426
- fs2.mkdirSync(target, { recursive: true });
10427
- continue;
10428
- }
10429
- if (typeFlag === "2") {
10430
- const linkTo = header.slice(157, 157 + 100).toString("utf8").split("\0")[0];
10431
- try {
10432
- fs2.unlinkSync(target);
10433
- } catch {
10434
- }
10435
- fs2.symlinkSync(linkTo, target);
10436
- continue;
10437
- }
10438
- fs2.mkdirSync(path2.dirname(target), { recursive: true });
10439
- fs2.writeFileSync(target, data);
10440
- written.push(rel);
10441
- }
10442
- return written;
10443
- }
10444
10443
  function dirSizeBytes(root) {
10445
10444
  let total = 0;
10446
10445
  let seen = 0;
@@ -10474,7 +10473,7 @@ var require_fs_utils = __commonJS({
10474
10473
  }
10475
10474
  return total;
10476
10475
  }
10477
- module2.exports = { dirSizeBytes, extractTarGz };
10476
+ module2.exports = { dirSizeBytes };
10478
10477
  }
10479
10478
  });
10480
10479
 
@@ -15624,20 +15623,22 @@ var require_guard = __commonJS({
15624
15623
  return;
15625
15624
  }
15626
15625
  if (req.method === "POST" && pathname === "/self-update/apply") {
15627
- if (!originAllowed(req, sup.config.apiPort)) {
15628
- req.resume();
15629
- return send(403, {});
15630
- }
15631
- Promise.resolve(sup.guardSelfUpdateApply()).then((r) => send(r.ok ? 200 : 400, r));
15632
- return;
15626
+ req.resume();
15627
+ return send(410, {
15628
+ ok: false,
15629
+ code: "KERNEL_UPDATE_SINGLE_WRITER",
15630
+ owner: "desktop-shell",
15631
+ error: "\u5185\u6838\u66F4\u65B0\u7531\u684C\u9762\u58F3\u6267\u884C\uFF08\u5355\u5199\u5165\u8005\u5951\u7EA6\uFF09\uFF1A\u8BF7\u5728\u684C\u9762\u58F3\u7684\u9762\u677F\u4E2D\u70B9\u300C\u66F4\u65B0\u300D\uFF0C\u6216\u5347\u7EA7/\u91CD\u88C5\u684C\u9762\u58F3\u3002"
15632
+ });
15633
15633
  }
15634
15634
  if (req.method === "POST" && pathname === "/self-update/restart-guard") {
15635
- if (!originAllowed(req, sup.config.apiPort)) {
15636
- req.resume();
15637
- return send(403, {});
15638
- }
15639
- const r = sup.guardSelfUpdateRestart();
15640
- return send(r.ok ? 200 : 400, r);
15635
+ req.resume();
15636
+ return send(410, {
15637
+ ok: false,
15638
+ code: "KERNEL_UPDATE_SINGLE_WRITER",
15639
+ owner: "desktop-shell",
15640
+ error: "\u5B88\u536B\u91CD\u542F\u7531\u684C\u9762\u58F3\u7ECF\u670D\u52A1\u7BA1\u7406\u5668\u6267\u884C\uFF08\u5B88\u536B\u4ECE\u4E0D\u91CD\u542F\u81EA\u5DF1\uFF09\uFF1A\u66F4\u65B0\u5185\u6838\u8BF7\u7528\u684C\u9762\u58F3\u3002"
15641
+ });
15641
15642
  }
15642
15643
  if (req.method === "GET" && pathname === "/env/dsh") {
15643
15644
  if (!originAllowed(req, sup.config.apiPort)) {
@@ -17460,15 +17461,8 @@ var require_env_catalog = __commonJS({
17460
17461
  function runtimeMeta() {
17461
17462
  const now = Date.now();
17462
17463
  if (_runtimeMetaCache && now - _runtimeMetaAt < 1e4) return _runtimeMetaCache;
17463
- let meta = {};
17464
- try {
17465
- const os2 = require("node:os");
17466
- const path2 = require("node:path");
17467
- const f = path2.join(os2.homedir(), ".dsh", "supervisor", "runtime.json");
17468
- meta = JSON.parse(fs2.readFileSync(f, "utf8")) || {};
17469
- } catch {
17470
- meta = {};
17471
- }
17464
+ const c = require_runtime_contract().read();
17465
+ const meta = c && c.raw || {};
17472
17466
  _runtimeMetaCache = meta;
17473
17467
  _runtimeMetaAt = now;
17474
17468
  return meta;
@@ -17535,27 +17529,24 @@ var require_env_catalog = __commonJS({
17535
17529
  }
17536
17530
  return out;
17537
17531
  }
17538
- /** 守卫自更新条目。
17532
+ /** 内核更新依赖条目(单写入者契约:安装/重启归桌面壳)。
17539
17533
  *
17540
- * ⚠ 2026-09-12 校正(P2):本条目原先依据 `selfUpdateManifestUrl` / `selfUpdateDir`,
17541
- * 但**真实机制早已换成 npm 通道**(`corePackageName` + `runNpmInstall`,见 settings-view 的
17542
- * `guardSelfUpdateStatus` / `guardCorePkg`)。那两个键**没有任何发行流程赋值**(默认恒为 null),
17543
- * 于是 `/env/status` 里的「守卫自更新」**恒报 unconfigured**,且把原因归到了无效键上 ——
17544
- * 用户与排障者据此查 `selfUpdateManifestUrl`,而真正该看的是 `corePackageName`。
17545
- *
17546
- * 现改为按 npm 通道的真实开关(`corePackageName`)判定,detail 也给出该键。
17534
+ * 2026-09-15(A 方案):守卫**不再自更新**,`corePackageName` 只用于查询内核版本状态;
17535
+ * 安装/升级由桌面壳执行(见 RELEASE-AND-UPDATE-MECHANISM.md §6)。
17536
+ * 本条目因此只报告「壳执行内核更新所需的包名」是否就位 —— 不再暗示守卫能自己装。
17537
+ * id 仍为 `selfUpdate` 以兼容既有 `/env/status` 消费方。
17547
17538
  */
17548
17539
  selfUpdateEntry() {
17549
17540
  const pkg = this.config.corePackageName;
17550
17541
  if (!pkg) {
17551
17542
  return {
17552
- label: "\u5B88\u536B\u81EA\u66F4\u65B0",
17543
+ label: "\u5185\u6838\u66F4\u65B0\uFF08\u684C\u9762\u58F3\u6267\u884C\uFF09",
17553
17544
  required: false,
17554
17545
  state: "unconfigured",
17555
- detail: "\u672A\u914D\u7F6E corePackageName\uFF08npm \u901A\u9053\uFF1B\u5F62\u5982 @dsh-sup/dsh-core-<os>-<arch>\uFF09"
17546
+ detail: "\u672A\u914D\u7F6E corePackageName\uFF08\u5F62\u5982 @dsh-sup/dsh-core-<os>-<arch>\uFF09"
17556
17547
  };
17557
17548
  }
17558
- return { label: "\u5B88\u536B\u81EA\u66F4\u65B0", required: false, state: "configured", detail: pkg };
17549
+ return { label: "\u5185\u6838\u66F4\u65B0\uFF08\u684C\u9762\u58F3\u6267\u884C\uFF09", required: false, state: "configured", detail: pkg };
17559
17550
  }
17560
17551
  /** DSH 本体条目(外传判定:bin 可执行 + 已装版本)。 */
17561
17552
  dshEntry(binOk, installed, bin) {
@@ -17723,7 +17714,6 @@ var require_settings_view = __commonJS({
17723
17714
  "use strict";
17724
17715
  var fs2 = require("node:fs");
17725
17716
  var path2 = require("node:path");
17726
- var os2 = require("node:os");
17727
17717
  var { spawn } = require("node:child_process");
17728
17718
  var { execFile } = require("node:child_process");
17729
17719
  var ex2 = require_exec();
@@ -17826,54 +17816,24 @@ var require_settings_view = __commonJS({
17826
17816
  return { ok: false, error: e.message };
17827
17817
  }
17828
17818
  }
17829
- /** 守卫自更新「重启生效」衔接:仅当配置 guardRestartAllowed=true 且存在 systemd 用户单元才执行;
17830
- * 否则返回明确指引(避免误杀/误起守卫)。 */
17831
- /** 自更新后的守卫重启(A2):能力按部署形态自动判定(打包态=systemd 重启闭环;源码形态=拒绝),
17832
- * 去掉 guardRestartAllowed 人工配置门槛。重启前落盘「预期版本」,重启后 /status 校验自报版本
17833
- * 达标才算更新成功——闭环可观测,不再出现"装了没生效"的静默失败。 */
17834
- guardSelfUpdateRestart() {
17835
- const dep = deploy.detect();
17836
- if (!dep.updatable) {
17837
- return { ok: false, error: dep.reason || "\u5F53\u524D\u90E8\u7F72\u5F62\u6001\u4E0D\u652F\u6301\u81EA\u52A8\u91CD\u542F", form: dep.form };
17838
- }
17839
- const unit = path2.join(os2.homedir(), ".config", "systemd", "user", "dsh-supervisor.service");
17840
- if (!fs2.existsSync(unit)) return { ok: false, error: "\u7CFB\u7EDF\u672A\u542F\u7528 systemd \u7528\u6237\u5355\u5143\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F\u5B88\u536B" };
17841
- const swDir = path2.dirname(this.config.stateFile);
17842
- try {
17843
- fs2.writeFileSync(path2.join(swDir, "self-update-expected.json"), JSON.stringify({ expectedVersion: this._selfUpdateExpectedVersion || null, at: Date.now() }), { mode: 384 });
17844
- } catch {
17845
- }
17846
- if (this.events) this.events.append("guard_self_update_restart", { expectedVersion: this._selfUpdateExpectedVersion || null, via: "exit+systemd-restart" });
17847
- this.logger && this.logger.info && this.logger.info("[self-update] \u5B88\u536B\u5C06\u9000\u51FA\uFF0C\u7531 systemd Restart=always \u62C9\u8D77\u65B0\u5185\u6838");
17848
- setTimeout(() => {
17849
- const bail = setTimeout(() => process.exit(0), 8e3);
17850
- if (bail.unref) bail.unref();
17851
- Promise.resolve().then(() => this.shutdown()).catch((e) => {
17852
- this.logger && this.logger.warn && this.logger.warn("[self-update] shutdown \u5F02\u5E38: " + (e && e.message || e));
17853
- }).then(() => {
17854
- clearTimeout(bail);
17855
- process.exit(0);
17856
- });
17857
- }, 500).unref();
17858
- return { ok: true, restarted: true, via: "exit+systemd-restart" };
17859
- }
17860
- // ---- 守卫自更新(2026-09 收敛:npm 通道取代 manifest/目录翻转)----
17819
+ // ---- 内核更新(单写入者契约:安装/重启归桌面壳)----
17861
17820
  //
17862
- // ⚠ 此处原有 `guardSelfUpdateDir()` —— 已于 2026-09-12 删除(P2 死代码):
17863
- // 全仓**零调用点**,且它所依赖的 `config.selfUpdateDir` 也从未被任何发行流程赋值
17864
- // (默认恒为 null)。保留它会造成「自更新仍走 manifest/目录翻转」的错误印象,
17865
- // 而真实机制是 npm 通道(`guardCorePkg()` + `runNpmInstall`)。
17821
+ // ⚠ 2026-09-15(A 方案):`guardSelfUpdateApply` 与 `guardSelfUpdateRestart` **已删除** ——
17822
+ // 内核 npm 包的唯一写入者是桌面壳(见 RELEASE-AND-UPDATE-MECHANISM.md §6);
17823
+ // 守卫只保留只读的 `guardSelfUpdateStatus`,接口 `/self-update/apply|restart-guard` 返回 410。
17824
+ // (此前 2026-09-12 已删除零调用点的 `guardSelfUpdateDir()`。)
17866
17825
  /** 内核 npm 子包名(按当前平台/架构)。corePackageName 可为显式常量或含 {os}/{arch} 占位的模板。 */
17867
17826
  guardCorePkg() {
17868
17827
  const raw = this.config.corePackageName;
17869
17828
  if (!raw) return null;
17870
17829
  return String(raw).replace(/{os}/g, matrix.osTag()).replace(/{arch}/g, matrix.current().arch) || null;
17871
17830
  }
17872
- /** 守卫自更新(2026-09 收敛:npm 通道)——查 @dsh-sup/dsh-core-<os>-<arch> 全 tag 最高版本(全更新:
17873
- * BETA/RC/正式都算更新,任一更高即提示可更新),对本机 guardVersion 比较。 */
17831
+ /** 内核更新状态(**只读**;安装/重启归桌面壳,单写入者契约)。
17832
+ * 查 @dsh-sup/dsh-core-<os>-<arch> 全 tag 最高版本(BETA/RC/正式都算更新),与本机 guardVersion 比较。
17833
+ * 本方法**不写任何东西**:面板据此显示「可更新」,实际安装由桌面壳 kernel_update_apply 执行。 */
17874
17834
  async guardSelfUpdateStatus() {
17875
17835
  const pkg = this.guardCorePkg();
17876
- if (!pkg) return { ok: false, error: "\u672A\u914D\u7F6E\u5185\u6838\u81EA\u66F4\u65B0\u5305\uFF08corePackageName\uFF09" };
17836
+ if (!pkg) return { ok: false, error: "\u672A\u914D\u7F6E\u5185\u6838\u5305\uFF08corePackageName\uFF09" };
17877
17837
  if (!this.dist || typeof this.dist.fetchLatestVersion !== "function") return { ok: false, error: "\u53D1\u5E03\u670D\u52A1\u672A\u521D\u59CB\u5316" };
17878
17838
  const dep = deploy.detect();
17879
17839
  if (!dep.updatable) {
@@ -17890,82 +17850,6 @@ var require_settings_view = __commonJS({
17890
17850
  return { ok: false, error: e.message };
17891
17851
  }
17892
17852
  }
17893
- /** 应用内核更新(npm 通道,全更新强制语义):`npm i -g <pkg>@<latest>`。
17894
- * 安装后由调用方(壳/面板)据 restartRequired 调 /self-update/restart-guard(或手动重启)生效。 */
17895
- async guardSelfUpdateApply() {
17896
- const pkg = this.guardCorePkg();
17897
- if (!pkg) return { ok: false, error: "\u672A\u914D\u7F6E\u5185\u6838\u81EA\u66F4\u65B0\u5305\uFF08corePackageName\uFF09" };
17898
- if (!this.dist || typeof this.dist.runNpmInstall !== "function") return { ok: false, error: "\u53D1\u5E03\u670D\u52A1\u672A\u521D\u59CB\u5316" };
17899
- if (!this.config.installCommandTemplate || !Array.isArray(this.config.installCommandTemplate)) return { ok: false, error: "\u672A\u914D\u7F6E\u5B89\u88C5\u547D\u4EE4\u6A21\u677F\uFF08installCommandTemplate\uFF09" };
17900
- const latest = await this.guardSelfUpdateStatus();
17901
- if (!latest.ok || !latest.latest) return { ok: false, error: latest && latest.error || "\u7248\u672C\u67E5\u8BE2\u5931\u8D25" };
17902
- if (latest.updatable === false) return { ok: false, error: latest.error || "\u5F53\u524D\u90E8\u7F72\u5F62\u6001\u4E0D\u652F\u6301\u81EA\u66F4\u65B0", form: latest.form };
17903
- if (!latest.updateAvailable) return { ok: true, upToDate: true, version: this.guardVersion };
17904
- const target = latest.latest;
17905
- try {
17906
- const registry = "https://registry.npmjs.org";
17907
- const r = await this.dist.runNpmInstall({
17908
- pkg,
17909
- version: target,
17910
- commandTemplate: this.config.installCommandTemplate,
17911
- registry,
17912
- onLine: (l) => {
17913
- if (this.logger && this.logger.info) this.logger.info("[self-update] " + l);
17914
- }
17915
- });
17916
- if (r && r.ok) {
17917
- let diskVersion = null;
17918
- try {
17919
- diskVersion = deploy.detect().runningTarget ? this._readBinarySelfVersion() : null;
17920
- } catch {
17921
- }
17922
- const verified = diskVersion === target;
17923
- this._selfUpdateExpectedVersion = verified ? target : null;
17924
- const restartRequired = true;
17925
- if (this.events) {
17926
- this.events.append("guard_self_update_applied", { from: this.guardVersion, version: target, pkg, diskVersion, verified });
17927
- }
17928
- if (!verified) {
17929
- return {
17930
- ok: true,
17931
- from: this.guardVersion,
17932
- version: target,
17933
- diskVersion,
17934
- verified: false,
17935
- restartRequired,
17936
- warn: "\u5DF2\u5B89\u88C5\u4F46\u78C1\u76D8\u7248\u672C\u6821\u9A8C\u672A\u901A\u8FC7\uFF08\u88C5\u5230\u975E\u8FD0\u884C\u4F4D/\u955C\u50CF stale\uFF09\uFF0C\u8BF7\u68C0\u67E5\u90E8\u7F72\u5F62\u6001"
17937
- };
17938
- }
17939
- return { ok: true, from: this.guardVersion, version: target, diskVersion, verified: true, restartRequired };
17940
- }
17941
- return { ok: false, error: r && r.error || "npm \u5B89\u88C5\u5931\u8D25" };
17942
- } catch (e) {
17943
- return { ok: false, error: e.message };
17944
- }
17945
- }
17946
- /** 自更新「已安装待重启生效」判定(A3):最近一次 apply 落盘的预期版本存在、
17947
- * 且进程运行版本仍低于它 → updatePending=true(面板显示重启提示;重启达标后自动清除)。 */
17948
- _selfUpdatePending() {
17949
- try {
17950
- const swDir = path2.dirname(this.config.stateFile);
17951
- const f = path2.join(swDir, "self-update-expected.json");
17952
- if (!fs2.existsSync(f)) return false;
17953
- const j = JSON.parse(fs2.readFileSync(f, "utf8"));
17954
- const expected = j && j.expectedVersion;
17955
- if (!expected) return false;
17956
- if (this.guardVersion === expected) {
17957
- try {
17958
- fs2.unlinkSync(f);
17959
- } catch {
17960
- }
17961
- if (this.events) this.events.append("guard_self_update_verified", { version: expected });
17962
- return false;
17963
- }
17964
- return semverCompare(expected, this.guardVersion) > 0;
17965
- } catch {
17966
- return false;
17967
- }
17968
- }
17969
17853
  /** 读磁盘上**运行位**的自报版本(A1 校验用):spawn `--version`,解析 guardVersion= 行。
17970
17854
  *
17971
17855
  * ⚠ 2026-09-12(P0 配套):条件从 `form === 'sea-binary'` 放宽为「**可更新的标准形态**」
@@ -19642,7 +19526,6 @@ var require_supervisor = __commonJS({
19642
19526
  this.manualRestart = false;
19643
19527
  this._ticking = false;
19644
19528
  this.intents = new IntentLedger();
19645
- this._selfUpdateExpectedVersion = null;
19646
19529
  this._stopping = false;
19647
19530
  this._sessionState = "starting";
19648
19531
  this._crashHalted = false;
@@ -20210,8 +20093,6 @@ var require_supervisor = __commonJS({
20210
20093
  backoffUntil: this._mBackoffUntil(),
20211
20094
  lastFailure: this._mLastFailure(),
20212
20095
  lastRestartAt: this._mLastRestartAt(),
20213
- updatePending: this._selfUpdatePending(),
20214
- // A3:更新已安装待重启生效
20215
20096
  upgradeHold: this._upgradeHold,
20216
20097
  commandMissing: !!(this._mSpawnBlockedUntil() && Date.now() < this._mSpawnBlockedUntil()),
20217
20098
  dshTokenCaptured: !!(this.tokenService && this.tokenService.get("main")),
@@ -21276,20 +21157,16 @@ function cmdSelfUpdate(action) {
21276
21157
  if (s.ok) {
21277
21158
  console.log("\u5B88\u536B\u5F53\u524D\u7248\u672C: " + s.installed);
21278
21159
  console.log("\u6700\u65B0\u7248\u672C: " + s.latest);
21279
- console.log(s.updateAvailable ? "\u2192 \u6709\u53EF\u7528\u66F4\u65B0\uFF01\u6267\u884C: dsh-supervisor self-update apply" : "\u5DF2\u662F\u6700\u65B0\u3002");
21160
+ console.log(s.updateAvailable ? "\u2192 \u6709\u53EF\u7528\u66F4\u65B0\uFF1A\u8BF7\u5728\u684C\u9762\u58F3\u9762\u677F\u300C\u5173\u4E8E\u300D\u4E2D\u70B9\u300C\u66F4\u65B0\u300D" : "\u5DF2\u662F\u6700\u65B0\u3002");
21280
21161
  }
21281
21162
  return;
21282
21163
  }
21283
21164
  if (action === "apply") {
21284
- const r = await apiOne("POST", "/self-update/apply");
21285
- if (r.error) return console.log("\u5E94\u7528\u66F4\u65B0\u5931\u8D25: " + r.error);
21286
- if (r.ok) {
21287
- console.log("\u5DF2\u66F4\u65B0\u5230 v" + r.version + (r.restartRequired ? "\uFF0C\u9700\u8981\u91CD\u542F\u5B88\u536B\u751F\u6548" : "\uFF08\u65E0\u53D8\u5316\uFF09"));
21288
- console.log("\u91CD\u542F: systemctl --user restart dsh-supervisor");
21289
- }
21290
- return;
21165
+ console.error("\u5185\u6838\u66F4\u65B0\u7531\u684C\u9762\u58F3\u6267\u884C\uFF08\u5355\u5199\u5165\u8005\u5951\u7EA6\uFF09\uFF1A\u8BF7\u5728\u684C\u9762\u58F3\u9762\u677F\u300C\u5173\u4E8E\u300D\u4E2D\u70B9\u300C\u66F4\u65B0\u300D\u3002");
21166
+ console.error("\u672C\u547D\u4EE4\u4E0D\u518D\u5B89\u88C5\u5185\u6838\u3002");
21167
+ process.exit(2);
21291
21168
  }
21292
- console.log("\u7528\u6CD5: dsh-supervisor self-update [check|apply]");
21169
+ console.log("\u7528\u6CD5: dsh-supervisor self-update [check]");
21293
21170
  })();
21294
21171
  }
21295
21172
  function cmdUpgrade(requested) {
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.4",
3
+ "version": "0.1.5-BETA.5",
4
4
  "description": "DSH lifecycle guard core (Node launcher) for linux-x64 — requires Node >=18.",
5
5
  "license": "UNLICENSED",
6
6
  "os": [
@@ -1,3 +1,3 @@
1
- import{A as e,E as t,L as n,O as r,P as i,T as a,_ as o,b as s,g as c,h as l,i as u,k as d,m as f,n as p,o as m,r as h,t as g,v as _,y as v,z as y}from"./supervisor-BVac7io-.js";import{t as b}from"./external-link-SYs4QOvn.js";import{n as x,t as ee}from"./rocket-Dt6NGQXE.js";import{t as te}from"./shield-check-B-D3XDKa.js";import{c as ne,f as re,o as S,s as C}from"./supervisor-DwJMunGH.js";import{t as w}from"./useSupervisorAction-B1Piyga4.js";import{c as T,i as E,o as D,r as O,t as k}from"./widgets-DMpjcPY5.js";var A=i(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),j=i(`rotate-cw`,[[`path`,{d:`M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8`,key:`1p45f6`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}]]),M=i(`square`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}]]),N=y(n(),1),P=r();function F({onRegisterActions:n}){let{snap:r}=g(),{busy:i,run:y}=w(),[F,I]=(0,N.useState)(!1),[L,R]=(0,N.useState)(null),[z,B]=(0,N.useState)(``),[V,H]=(0,N.useState)(``),[U,W]=(0,N.useState)(``),[G,K]=(0,N.useState)(`4G`),[q,J]=(0,N.useState)(`150%`),Y=r.instances?.instances??[],[X,ie]=(0,N.useState)(null);(0,N.useEffect)(()=>{let e=!0;return h.envStatus().then(t=>{e&&ie(t.capabilities??null)}).catch(()=>void 0),()=>{e=!1}},[]);let Z=X!==null&&X.multiInstance===!1;(0,N.useEffect)(()=>(n?.({onAdd:()=>I(!0)}),()=>n?.(null)),[n]);let Q=(e,t)=>y(e,t);async function ae(){if(Z){d.error(`当前平台不支持沙箱实例(需 Linux + systemd-run)`);return}let e=parseInt(V,10);if(!Number.isFinite(e)||e<=0){d.error(`请填写有效端口`);return}let t=U?U.split(/\n/).map(e=>e.trim()).filter(Boolean):[];try{let n=await h.instanceAdd({name:z.trim()||`沙箱 `+e,port:e,command:t,memoryMax:G||`4G`,cpuQuota:q||`150%`});if(n&&n.ok===!1){d.error(n.error||`添加失败`);return}d.success(`已添加实例`),I(!1),B(``),H(``),W(``),K(`4G`),J(`150%`),p.refresh()}catch(e){d.error(String(e))}}async function oe(e){await y(e.id,async()=>{let t=await h.instanceCheckUpdate(e.id);t.updateAvailable?(d.success(`发现新版 `+t.latest+`,可点「更新」升级`),ce(t=>new Map(t).set(e.id,!0))):t.latest?d.info(`已是最新 `+t.latest):d.error(t.error||`版本检测失败`)})}let[se,ce]=(0,N.useState)(new Map);async function le(e){confirm(`将升级该沙箱实例的 DSH 到最新版(实例会短暂重启,进行中的请求中断)。确定升级?`)&&await y(e.id,()=>h.instanceUpgrade(e.id),{success:`升级已开始…`})}let $=({it:n})=>{let r=n.state?.running??!1,o=n.state?.lifecyclePhase,s=C(o,r),c=n.updateJob,l=c?.state===`running`,u=n.sandbox?.memoryMax||`—`,d=n.sandbox?.cpuQuota||`—`,f=i===n.id;return(0,P.jsxs)(k,{className:`overflow-visible`,children:[(0,P.jsxs)(`div`,{className:`grid grid-cols-1 @min-[720px]:grid-cols-[minmax(0,5fr)_minmax(0,6fr)]`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-4 border-b border-border px-6 py-5 md:border-b-0 md:border-r`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,P.jsx)(`strong`,{className:`truncate text-lg font-semibold tracking-[-0.01em] text-foreground`,children:n.name}),n.version?(0,P.jsxs)(`span`,{className:`font-mono text-sm text-muted-foreground`,children:[`v`,n.version]}):null]}),se.get(n.id)?(0,P.jsxs)(a,{className:`text-primary-foreground`,onClick:()=>void le(n),size:`chip`,children:[(0,P.jsx)(j,{className:`size-3`}),`更新`]}):(0,P.jsxs)(a,{className:`bg-primary/10 text-primary hover:bg-primary/15`,disabled:f||l,onClick:()=>void oe(n),size:`chip`,variant:`ghost`,children:[(0,P.jsx)(j,{className:t(`size-3`,f&&`animate-spin`)}),`检测更新`]})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsx)(T,{tone:s.tone,ping:s.tone===`ok`}),(0,P.jsx)(`span`,{className:`text-base font-semibold leading-none text-foreground`,children:s.label})]}),l?(0,P.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(m,{className:`size-3`}),`正在升级 DeepSeek Harness(`,c?.step||``,`)…`]}):null,n.state?.installing?(0,P.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(m,{className:`size-3`}),`正在安装…`]}):null,o===`FAILED`?(0,P.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-destructive`,children:[(0,P.jsx)(e,{className:`size-3.5`}),(0,P.jsx)(`span`,{className:`truncate`,children:n.state?.lastError||`失败`})]}):null]}),(0,P.jsx)(`div`,{className:`flex items-center bg-[image:var(--panel-accent-gradient)] px-6 py-5`,children:(0,P.jsxs)(`div`,{className:`grid w-full grid-cols-2 gap-x-6 gap-y-4 sm:grid-cols-4`,children:[(0,P.jsx)(E,{icon:(0,P.jsx)(re,{className:`size-4`}),label:`端口`,value:String(n.port),mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(M,{className:`size-4`}),label:`PID`,value:n.state?.pid?String(n.state.pid):`—`,mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(j,{className:`size-4`}),label:`重启次数`,value:String(n.state?.restartCount??0),mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(e,{className:`size-4`}),label:`最近故障`,value:n.state?.lastFailure?S(n.state.lastFailure):`无`,warn:!!n.state?.lastFailure})]})})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3 border-t border-border/60 px-6 py-3`,children:[(0,P.jsxs)(`span`,{className:`hidden min-w-0 items-center lg:inline-flex gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(O,{domain:`sandbox`}),(0,P.jsx)(D,{tone:n.remoteEnabled?`boot`:`off`,children:n.remoteEnabled?`远程开启`:`远程关闭`}),(0,P.jsxs)(`span`,{className:`whitespace-nowrap`,children:[`内存 `,u,` · CPU `,d]})]}),(0,P.jsxs)(`div`,{className:`ml-auto flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(a,{disabled:!r,onClick:()=>void Q(n.id,()=>h.instanceOpenWeb(n.id)),size:`sm`,title:`打开 DSH Web(自动带认证连接)`,variant:`outline`,className:`hidden md:inline-flex`,children:[(0,P.jsx)(b,{className:`size-4`}),`DSH Web`]}),(0,P.jsx)(a,{disabled:l,onClick:()=>void Q(n.id,()=>r?h.instanceStop(n.id):h.instanceStart(n.id)),size:`sm`,variant:`outline`,children:r?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(x,{className:`size-4 text-status-error`}),`停止实例`]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(ee,{className:`size-4 text-primary`}),`启动实例`]})}),(0,P.jsx)(`span`,{"aria-hidden":`true`,className:`mx-1 h-5 w-px bg-border`}),(0,P.jsxs)(a,{disabled:f,onClick:()=>void Q(n.id,()=>h.instanceUpdate(n.id,{guardian:!n.guardian})),size:`sm`,variant:`outline`,children:[(0,P.jsx)(te,{className:t(`size-4`,n.guardian?`text-status-ok`:`text-muted-foreground`)}),n.guardian?`停止守护`:`启动守护`]}),(0,P.jsxs)(a,{className:`hidden h-[30px] md:inline-flex`,disabled:f,onClick:()=>R(n.id),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(ne,{className:`size-4`}),`删除`]})]})]})]})};return(0,P.jsxs)(`div`,{className:`grid content-start gap-4`,children:[Z?(0,P.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 px-3 py-2.5 text-xs text-muted-foreground`,children:[(0,P.jsx)(e,{className:`mt-0.5 size-3.5 shrink-0 text-amber-500`}),(0,P.jsxs)(`span`,{children:[`当前平台(`,X?.platform||`未知`,`)不支持沙箱实例:需 Linux + systemd-run(独立 cgroup 隔离)。 原生 DSH 主实例不受影响。`]})]}):null,Y.length?(0,P.jsx)(`div`,{className:`grid gap-3`,children:Y.map(e=>(0,P.jsx)($,{it:e},e.id))}):(0,P.jsx)(`div`,{className:`grid min-h-[220px] place-items-center rounded-lg border border-dashed border-border text-center`,children:(0,P.jsxs)(`div`,{children:[(0,P.jsx)(A,{className:`mx-auto mb-3 size-8 text-muted-foreground`}),(0,P.jsx)(`p`,{className:`text-sm font-medium text-foreground`,children:`尚未添加沙箱实例`}),(0,P.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:Z?`当前平台不支持沙箱实例(见上方说明)`:`点击顶部「添加实例」创建沙箱`})]})}),(0,P.jsx)(c,{open:F,onOpenChange:I,children:(0,P.jsxs)(o,{className:`max-w-[440px]`,children:[(0,P.jsx)(v,{children:(0,P.jsx)(s,{children:`添加 DSH 实例`})}),(0,P.jsxs)(`div`,{className:`grid gap-3`,children:[(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`实例名称`}),(0,P.jsx)(l,{placeholder:`如 沙箱二开`,value:z,onChange:e=>B(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`监听端口`}),(0,P.jsx)(l,{inputMode:`numeric`,placeholder:`如 3082`,value:V,onChange:e=>H(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`启动命令(每项一参数,可留空用默认)`}),(0,P.jsx)(u,{className:`min-h-[72px] font-mono text-sm`,placeholder:`node
1
+ import{A as e,E as t,L as n,O as r,P as i,T as a,_ as o,b as s,g as c,h as l,i as u,k as d,m as f,n as p,o as m,r as h,t as g,v as _,y as v,z as y}from"./supervisor-DdCExALH.js";import{t as b}from"./external-link-B3goz-0m.js";import{n as x,t as ee}from"./rocket-B9cBKQUY.js";import{t as te}from"./shield-check-UgQY0o2B.js";import{c as ne,f as re,o as S,s as C}from"./supervisor-4Q6BBx68.js";import{t as w}from"./useSupervisorAction-C0KJmCu7.js";import{c as T,i as E,o as D,r as O,t as k}from"./widgets-BXmNWJy5.js";var A=i(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),j=i(`rotate-cw`,[[`path`,{d:`M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8`,key:`1p45f6`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}]]),M=i(`square`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}]]),N=y(n(),1),P=r();function F({onRegisterActions:n}){let{snap:r}=g(),{busy:i,run:y}=w(),[F,I]=(0,N.useState)(!1),[L,R]=(0,N.useState)(null),[z,B]=(0,N.useState)(``),[V,H]=(0,N.useState)(``),[U,W]=(0,N.useState)(``),[G,K]=(0,N.useState)(`4G`),[q,J]=(0,N.useState)(`150%`),Y=r.instances?.instances??[],[X,ie]=(0,N.useState)(null);(0,N.useEffect)(()=>{let e=!0;return h.envStatus().then(t=>{e&&ie(t.capabilities??null)}).catch(()=>void 0),()=>{e=!1}},[]);let Z=X!==null&&X.multiInstance===!1;(0,N.useEffect)(()=>(n?.({onAdd:()=>I(!0)}),()=>n?.(null)),[n]);let Q=(e,t)=>y(e,t);async function ae(){if(Z){d.error(`当前平台不支持沙箱实例(需 Linux + systemd-run)`);return}let e=parseInt(V,10);if(!Number.isFinite(e)||e<=0){d.error(`请填写有效端口`);return}let t=U?U.split(/\n/).map(e=>e.trim()).filter(Boolean):[];try{let n=await h.instanceAdd({name:z.trim()||`沙箱 `+e,port:e,command:t,memoryMax:G||`4G`,cpuQuota:q||`150%`});if(n&&n.ok===!1){d.error(n.error||`添加失败`);return}d.success(`已添加实例`),I(!1),B(``),H(``),W(``),K(`4G`),J(`150%`),p.refresh()}catch(e){d.error(String(e))}}async function oe(e){await y(e.id,async()=>{let t=await h.instanceCheckUpdate(e.id);t.updateAvailable?(d.success(`发现新版 `+t.latest+`,可点「更新」升级`),ce(t=>new Map(t).set(e.id,!0))):t.latest?d.info(`已是最新 `+t.latest):d.error(t.error||`版本检测失败`)})}let[se,ce]=(0,N.useState)(new Map);async function le(e){confirm(`将升级该沙箱实例的 DSH 到最新版(实例会短暂重启,进行中的请求中断)。确定升级?`)&&await y(e.id,()=>h.instanceUpgrade(e.id),{success:`升级已开始…`})}let $=({it:n})=>{let r=n.state?.running??!1,o=n.state?.lifecyclePhase,s=C(o,r),c=n.updateJob,l=c?.state===`running`,u=n.sandbox?.memoryMax||`—`,d=n.sandbox?.cpuQuota||`—`,f=i===n.id;return(0,P.jsxs)(k,{className:`overflow-visible`,children:[(0,P.jsxs)(`div`,{className:`grid grid-cols-1 @min-[720px]:grid-cols-[minmax(0,5fr)_minmax(0,6fr)]`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-4 border-b border-border px-6 py-5 md:border-b-0 md:border-r`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,P.jsx)(`strong`,{className:`truncate text-lg font-semibold tracking-[-0.01em] text-foreground`,children:n.name}),n.version?(0,P.jsxs)(`span`,{className:`font-mono text-sm text-muted-foreground`,children:[`v`,n.version]}):null]}),se.get(n.id)?(0,P.jsxs)(a,{className:`text-primary-foreground`,onClick:()=>void le(n),size:`chip`,children:[(0,P.jsx)(j,{className:`size-3`}),`更新`]}):(0,P.jsxs)(a,{className:`bg-primary/10 text-primary hover:bg-primary/15`,disabled:f||l,onClick:()=>void oe(n),size:`chip`,variant:`ghost`,children:[(0,P.jsx)(j,{className:t(`size-3`,f&&`animate-spin`)}),`检测更新`]})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsx)(T,{tone:s.tone,ping:s.tone===`ok`}),(0,P.jsx)(`span`,{className:`text-base font-semibold leading-none text-foreground`,children:s.label})]}),l?(0,P.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(m,{className:`size-3`}),`正在升级 DeepSeek Harness(`,c?.step||``,`)…`]}):null,n.state?.installing?(0,P.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(m,{className:`size-3`}),`正在安装…`]}):null,o===`FAILED`?(0,P.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-destructive`,children:[(0,P.jsx)(e,{className:`size-3.5`}),(0,P.jsx)(`span`,{className:`truncate`,children:n.state?.lastError||`失败`})]}):null]}),(0,P.jsx)(`div`,{className:`flex items-center bg-[image:var(--panel-accent-gradient)] px-6 py-5`,children:(0,P.jsxs)(`div`,{className:`grid w-full grid-cols-2 gap-x-6 gap-y-4 sm:grid-cols-4`,children:[(0,P.jsx)(E,{icon:(0,P.jsx)(re,{className:`size-4`}),label:`端口`,value:String(n.port),mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(M,{className:`size-4`}),label:`PID`,value:n.state?.pid?String(n.state.pid):`—`,mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(j,{className:`size-4`}),label:`重启次数`,value:String(n.state?.restartCount??0),mono:!0}),(0,P.jsx)(E,{icon:(0,P.jsx)(e,{className:`size-4`}),label:`最近故障`,value:n.state?.lastFailure?S(n.state.lastFailure):`无`,warn:!!n.state?.lastFailure})]})})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3 border-t border-border/60 px-6 py-3`,children:[(0,P.jsxs)(`span`,{className:`hidden min-w-0 items-center lg:inline-flex gap-2 text-xs text-muted-foreground`,children:[(0,P.jsx)(O,{domain:`sandbox`}),(0,P.jsx)(D,{tone:n.remoteEnabled?`boot`:`off`,children:n.remoteEnabled?`远程开启`:`远程关闭`}),(0,P.jsxs)(`span`,{className:`whitespace-nowrap`,children:[`内存 `,u,` · CPU `,d]})]}),(0,P.jsxs)(`div`,{className:`ml-auto flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(a,{disabled:!r,onClick:()=>void Q(n.id,()=>h.instanceOpenWeb(n.id)),size:`sm`,title:`打开 DSH Web(自动带认证连接)`,variant:`outline`,className:`hidden md:inline-flex`,children:[(0,P.jsx)(b,{className:`size-4`}),`DSH Web`]}),(0,P.jsx)(a,{disabled:l,onClick:()=>void Q(n.id,()=>r?h.instanceStop(n.id):h.instanceStart(n.id)),size:`sm`,variant:`outline`,children:r?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(x,{className:`size-4 text-status-error`}),`停止实例`]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(ee,{className:`size-4 text-primary`}),`启动实例`]})}),(0,P.jsx)(`span`,{"aria-hidden":`true`,className:`mx-1 h-5 w-px bg-border`}),(0,P.jsxs)(a,{disabled:f,onClick:()=>void Q(n.id,()=>h.instanceUpdate(n.id,{guardian:!n.guardian})),size:`sm`,variant:`outline`,children:[(0,P.jsx)(te,{className:t(`size-4`,n.guardian?`text-status-ok`:`text-muted-foreground`)}),n.guardian?`停止守护`:`启动守护`]}),(0,P.jsxs)(a,{className:`hidden h-[30px] md:inline-flex`,disabled:f,onClick:()=>R(n.id),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(ne,{className:`size-4`}),`删除`]})]})]})]})};return(0,P.jsxs)(`div`,{className:`grid content-start gap-4`,children:[Z?(0,P.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 px-3 py-2.5 text-xs text-muted-foreground`,children:[(0,P.jsx)(e,{className:`mt-0.5 size-3.5 shrink-0 text-amber-500`}),(0,P.jsxs)(`span`,{children:[`当前平台(`,X?.platform||`未知`,`)不支持沙箱实例:需 Linux + systemd-run(独立 cgroup 隔离)。 原生 DSH 主实例不受影响。`]})]}):null,Y.length?(0,P.jsx)(`div`,{className:`grid gap-3`,children:Y.map(e=>(0,P.jsx)($,{it:e},e.id))}):(0,P.jsx)(`div`,{className:`grid min-h-[220px] place-items-center rounded-lg border border-dashed border-border text-center`,children:(0,P.jsxs)(`div`,{children:[(0,P.jsx)(A,{className:`mx-auto mb-3 size-8 text-muted-foreground`}),(0,P.jsx)(`p`,{className:`text-sm font-medium text-foreground`,children:`尚未添加沙箱实例`}),(0,P.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:Z?`当前平台不支持沙箱实例(见上方说明)`:`点击顶部「添加实例」创建沙箱`})]})}),(0,P.jsx)(c,{open:F,onOpenChange:I,children:(0,P.jsxs)(o,{className:`max-w-[440px]`,children:[(0,P.jsx)(v,{children:(0,P.jsx)(s,{children:`添加 DSH 实例`})}),(0,P.jsxs)(`div`,{className:`grid gap-3`,children:[(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`实例名称`}),(0,P.jsx)(l,{placeholder:`如 沙箱二开`,value:z,onChange:e=>B(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`监听端口`}),(0,P.jsx)(l,{inputMode:`numeric`,placeholder:`如 3082`,value:V,onChange:e=>H(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`启动命令(每项一参数,可留空用默认)`}),(0,P.jsx)(u,{className:`min-h-[72px] font-mono text-sm`,placeholder:`node
2
2
  /usr/local/bin/dsh
3
3
  web`,value:U,onChange:e=>W(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`内存限额`}),(0,P.jsx)(l,{placeholder:`如 4G`,value:G,onChange:e=>K(e.target.value)})]}),(0,P.jsxs)(`div`,{className:`grid gap-1.5`,children:[(0,P.jsx)(f,{children:`CPU 配额`}),(0,P.jsx)(l,{placeholder:`如 150%`,value:q,onChange:e=>J(e.target.value)})]})]})]}),(0,P.jsxs)(_,{children:[(0,P.jsx)(a,{onClick:()=>I(!1),variant:`outline`,children:`取消`}),(0,P.jsx)(a,{onClick:()=>void ae(),children:`添加`})]})]})}),(0,P.jsx)(c,{open:!!L,onOpenChange:e=>!e&&R(null),children:(0,P.jsxs)(o,{className:`max-w-[380px]`,children:[(0,P.jsx)(v,{children:(0,P.jsx)(s,{children:`删除实例?`})}),(0,P.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`将彻底删除该沙箱实例(含配置与运行时数据)。此操作不可恢复。`}),(0,P.jsxs)(_,{children:[(0,P.jsx)(a,{onClick:()=>R(null),variant:`outline`,children:`取消`}),(0,P.jsx)(a,{className:`h-[34px]`,onClick:()=>{let e=L;R(null),e&&y(e,async()=>{let t=await h.instanceRemove(e);t&&t.ok!==!1&&t.dataPreserved===!0?d.warning(`实例已移除,但因其单元仍在运行,数据目录已保留(未删除)——请先停止实例再删数据`):t&&t.ok===!1&&d.error(t.error||`删除失败`),p.refresh()})},variant:`destructive`,children:`删除`})]})]})})]})}export{F as InstancesPage};