@dsh-sup/dsh-core-linux-x64 0.1.6-BETA.14 → 0.1.6-BETA.15

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
@@ -951,7 +951,7 @@ var require_version = __commonJS({
951
951
  var fs2 = require("node:fs");
952
952
  var path2 = require("node:path");
953
953
  function guardVersion() {
954
- if (true) return String("0.1.6-BETA.14");
954
+ if (true) return String("0.1.6-BETA.15");
955
955
  try {
956
956
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
957
957
  } catch {
@@ -4855,6 +4855,106 @@ var require_capability_profile = __commonJS({
4855
4855
  }
4856
4856
  });
4857
4857
 
4858
+ // src/platform/util/redact.js
4859
+ var require_redact = __commonJS({
4860
+ "src/platform/util/redact.js"(exports2, module2) {
4861
+ "use strict";
4862
+ function maskProxyServer(server) {
4863
+ return server ? String(server).replace(/(\/\/)?([^\s/@:]+):([^\s/@]*)@/, "$1$2:***@") : null;
4864
+ }
4865
+ function maskProxySecrets(text) {
4866
+ return String(text == null ? "" : text).replace(/(\/\/)?([^\s/@:]+):([^\s/@]*)@/g, "$1$2:***@");
4867
+ }
4868
+ module2.exports = { maskProxyServer, maskProxySecrets };
4869
+ }
4870
+ });
4871
+
4872
+ // src/platform/os/registry.js
4873
+ var require_registry = __commonJS({
4874
+ "src/platform/os/registry.js"(exports2, module2) {
4875
+ "use strict";
4876
+ function regValueOf(out) {
4877
+ const m = String(out || "").match(/REG_(?:EXPAND_SZ|SZ)\s+(.*)/);
4878
+ if (!m) return null;
4879
+ return m[1].trim() || null;
4880
+ }
4881
+ function regDwordOf(out) {
4882
+ const m = String(out || "").match(/REG_DWORD\s+(0x[0-9a-fA-F]+|\d+)\b/);
4883
+ if (!m) return null;
4884
+ const n = Number.parseInt(m[1], m[1].startsWith("0x") ? 16 : 10);
4885
+ return Number.isNaN(n) ? null : n;
4886
+ }
4887
+ function expandEnvVars(value, env) {
4888
+ const e = env || process.env;
4889
+ return String(value).replace(/%([^%]+)%/g, (all, name) => e[name] === void 0 ? all : e[name]);
4890
+ }
4891
+ function safeRegKeyPart(s) {
4892
+ return typeof s === "string" && s.length > 0 && s.length <= 200 && /^[\x20-\x7e]+$/.test(s) && !/[;|&`$()<>^"'\r\n]/.test(s);
4893
+ }
4894
+ var REG_HIVE_ALIAS = { HKLM: "HKEY_LOCAL_MACHINE", HKCU: "HKEY_CURRENT_USER", HKCR: "HKEY_CLASSES_ROOT", HKU: "HKEY_USERS", HKCC: "HKEY_CURRENT_CONFIG" };
4895
+ function regKeyFull(key) {
4896
+ const s = String(key || "");
4897
+ const i = s.indexOf("\\");
4898
+ const root = i < 0 ? s : s.slice(0, i);
4899
+ return (REG_HIVE_ALIAS[root.toUpperCase()] || root) + (i < 0 ? "" : s.slice(i));
4900
+ }
4901
+ function regValue(runner, note, key, name) {
4902
+ const args = name ? ["query", key, "/v", name] : ["query", key, "/ve"];
4903
+ const out = runner("reg.exe", args);
4904
+ const v = regValueOf(out);
4905
+ note(name ? key + " /v " + name : key, v ? "ok" : "empty");
4906
+ return v;
4907
+ }
4908
+ function regSubkeys(runner, note, key) {
4909
+ const out = runner("reg.exe", ["query", key]);
4910
+ if (!out) {
4911
+ note(key, "unreadable");
4912
+ return [];
4913
+ }
4914
+ const prefix = regKeyFull(key) + "\\";
4915
+ const names = [];
4916
+ for (const line of String(out).split(/\r?\n/)) {
4917
+ const l = line.trim();
4918
+ if (!/^HKEY_/i.test(l) || l.length <= prefix.length) continue;
4919
+ if (l.slice(0, prefix.length).toUpperCase() !== prefix.toUpperCase()) continue;
4920
+ const rest = l.slice(prefix.length);
4921
+ if (rest && !rest.includes("\\") && safeRegKeyPart(rest)) names.push(rest);
4922
+ }
4923
+ note(key, names.length ? names.length + " \u9879" : "\u65E0\u5B50\u952E");
4924
+ return names;
4925
+ }
4926
+ function regValueTargets(runner, note, key) {
4927
+ const out = runner("reg.exe", ["query", key]);
4928
+ if (!out) {
4929
+ note(key, "unreadable");
4930
+ return [];
4931
+ }
4932
+ const paths = [];
4933
+ for (const line of String(out).split(/\r?\n/)) {
4934
+ const m = line.trim().match(/REG_SZ\s+(.*)/);
4935
+ if (!m) continue;
4936
+ const rel = m[1].trim();
4937
+ if (!safeRegKeyPart(rel)) continue;
4938
+ const root = key.slice(0, key.indexOf("\\"));
4939
+ paths.push(root + "\\" + rel);
4940
+ }
4941
+ note(key, paths.length ? paths.length + " \u4E2A\u80FD\u529B\u8DEF\u5F84" : "\u65E0\u503C");
4942
+ return paths;
4943
+ }
4944
+ module2.exports = {
4945
+ regValueOf,
4946
+ regDwordOf,
4947
+ expandEnvVars,
4948
+ safeRegKeyPart,
4949
+ REG_HIVE_ALIAS,
4950
+ regKeyFull,
4951
+ regValue,
4952
+ regSubkeys,
4953
+ regValueTargets
4954
+ };
4955
+ }
4956
+ });
4957
+
4858
4958
  // src/platform/os/browser-inventory.js
4859
4959
  var require_browser_inventory = __commonJS({
4860
4960
  "src/platform/os/browser-inventory.js"(exports2, module2) {
@@ -4864,6 +4964,8 @@ var require_browser_inventory = __commonJS({
4864
4964
  var os2 = require("node:os");
4865
4965
  var exec = require_exec();
4866
4966
  var { isExecutableFile } = require_exec_path();
4967
+ var registry = require_registry();
4968
+ var { expandEnvVars, safeRegKeyPart, regValue, regSubkeys, regValueTargets } = registry;
4867
4969
  var PROBE_TIMEOUT_MS = 1500;
4868
4970
  function engineOf(bin) {
4869
4971
  const base = String(bin || "").toLowerCase().split(/[\\/]/).pop();
@@ -4922,15 +5024,6 @@ var require_browser_inventory = __commonJS({
4922
5024
  if (!toks.length) return null;
4923
5025
  return { bin: toks[0], baseArgs: toks.slice(1) };
4924
5026
  }
4925
- function regValueOf(out) {
4926
- const m = String(out || "").match(/REG_(?:EXPAND_SZ|SZ)\s+(.*)/);
4927
- if (!m) return null;
4928
- return m[1].trim() || null;
4929
- }
4930
- function expandEnvVars(value, env) {
4931
- const e = env || process.env;
4932
- return String(value).replace(/%([^%]+)%/g, (all, name) => e[name] === void 0 ? all : e[name]);
4933
- }
4934
5027
  function exeFromCmdLine(cmdLine) {
4935
5028
  const s = String(cmdLine || "");
4936
5029
  let m = s.match(/^\s*"([^"]+\.exe)"/i);
@@ -4938,41 +5031,6 @@ var require_browser_inventory = __commonJS({
4938
5031
  m = s.match(/^\s*(.*?\.exe)/i);
4939
5032
  return m ? m[1] : null;
4940
5033
  }
4941
- function safeRegKeyPart(s) {
4942
- return typeof s === "string" && s.length > 0 && s.length <= 200 && /^[\x20-\x7e]+$/.test(s) && !/[;|&`$()<>^"'\r\n]/.test(s);
4943
- }
4944
- function regValue(runner, note, key, name) {
4945
- const args = name ? ["query", key, "/v", name] : ["query", key, "/ve"];
4946
- const out = runner("reg.exe", args);
4947
- const v = regValueOf(out);
4948
- note(name ? key + " /v " + name : key, v ? "ok" : "empty");
4949
- return v;
4950
- }
4951
- var REG_HIVE_ALIAS = { HKLM: "HKEY_LOCAL_MACHINE", HKCU: "HKEY_CURRENT_USER", HKCR: "HKEY_CLASSES_ROOT", HKU: "HKEY_USERS", HKCC: "HKEY_CURRENT_CONFIG" };
4952
- function regKeyFull(key) {
4953
- const s = String(key || "");
4954
- const i = s.indexOf("\\");
4955
- const root = i < 0 ? s : s.slice(0, i);
4956
- return (REG_HIVE_ALIAS[root.toUpperCase()] || root) + (i < 0 ? "" : s.slice(i));
4957
- }
4958
- function regSubkeys(runner, note, key) {
4959
- const out = runner("reg.exe", ["query", key]);
4960
- if (!out) {
4961
- note(key, "unreadable");
4962
- return [];
4963
- }
4964
- const prefix = regKeyFull(key) + "\\";
4965
- const names = [];
4966
- for (const line of String(out).split(/\r?\n/)) {
4967
- const l = line.trim();
4968
- if (!/^HKEY_/i.test(l) || l.length <= prefix.length) continue;
4969
- if (l.slice(0, prefix.length).toUpperCase() !== prefix.toUpperCase()) continue;
4970
- const rest = l.slice(prefix.length);
4971
- if (rest && !rest.includes("\\") && safeRegKeyPart(rest)) names.push(rest);
4972
- }
4973
- note(key, names.length ? names.length + " \u9879" : "\u65E0\u5B50\u952E");
4974
- return names;
4975
- }
4976
5034
  var WIN_APP_PATHS = ["msedge.exe", "chrome.exe", "firefox.exe", "brave.exe", "opera.exe", "vivaldi.exe", "chromium.exe", "thorium.exe", "librewolf.exe"];
4977
5035
  function winExeOfProgId(runner, note, progId, env) {
4978
5036
  if (!safeRegKeyPart(progId)) return null;
@@ -5068,24 +5126,6 @@ var require_browser_inventory = __commonJS({
5068
5126
  if (found.size === 1) setDefault([...found.values()][0], "only-installed");
5069
5127
  return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
5070
5128
  }
5071
- function regValueTargets(runOut, note, key) {
5072
- const out = runOut("reg.exe", ["query", key]);
5073
- if (!out) {
5074
- note(key, "unreadable");
5075
- return [];
5076
- }
5077
- const paths = [];
5078
- for (const line of String(out).split(/\r?\n/)) {
5079
- const m = line.trim().match(/REG_SZ\s+(.*)/);
5080
- if (!m) continue;
5081
- const rel = m[1].trim();
5082
- if (!safeRegKeyPart(rel)) continue;
5083
- const root = key.slice(0, key.indexOf("\\"));
5084
- paths.push(root + "\\" + rel);
5085
- }
5086
- note(key, paths.length ? paths.length + " \u4E2A\u80FD\u529B\u8DEF\u5F84" : "\u65E0\u503C");
5087
- return paths;
5088
- }
5089
5129
  var MAC_JXA_LIST = [
5090
5130
  "ObjC.import('Foundation');",
5091
5131
  "var ws=$.NSWorkspace.sharedWorkspace;",
@@ -5387,13 +5427,15 @@ var require_browser_inventory = __commonJS({
5387
5427
  engineOf,
5388
5428
  tokenizeExec,
5389
5429
  parseExecLine,
5390
- regValueOf,
5391
- expandEnvVars,
5392
5430
  exeFromCmdLine,
5431
+ // 注册表原语住在 ./registry.js(单一实现),此处原样转出:既有门禁与消费方的取用口不变。
5432
+ regValueOf: registry.regValueOf,
5433
+ expandEnvVars,
5434
+ safeRegKeyPart,
5435
+ regKeyFull: registry.regKeyFull,
5436
+ regValue,
5393
5437
  regSubkeys,
5394
- regKeyFull,
5395
5438
  regValueTargets,
5396
- safeRegKeyPart,
5397
5439
  browserFromDesktop,
5398
5440
  desktopDirs,
5399
5441
  resolveLinuxBin,
@@ -5405,6 +5447,317 @@ var require_browser_inventory = __commonJS({
5405
5447
  }
5406
5448
  });
5407
5449
 
5450
+ // src/platform/os/egress.js
5451
+ var require_egress = __commonJS({
5452
+ "src/platform/os/egress.js"(exports2, module2) {
5453
+ "use strict";
5454
+ var tls = require("node:tls");
5455
+ var dns = require("node:dns");
5456
+ var exec = require_exec();
5457
+ var registry = require_registry();
5458
+ var PROBE_TIMEOUT_MS = 2500;
5459
+ var READ_TTL_MS = 6e4;
5460
+ function hostOf(url) {
5461
+ try {
5462
+ return new URL(String(url)).hostname.toLowerCase() || null;
5463
+ } catch {
5464
+ return null;
5465
+ }
5466
+ }
5467
+ function proxyWin(runner, note) {
5468
+ const KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
5469
+ const read = (args) => Promise.resolve(runner("reg.exe", args));
5470
+ return Promise.all([
5471
+ read(["query", KEY, "/v", "ProxyEnable"]),
5472
+ read(["query", KEY, "/v", "ProxyServer"]),
5473
+ read(["query", KEY, "/v", "AutoConfigURL"])
5474
+ ]).then(([en, sv, pc]) => {
5475
+ const on = registry.regDwordOf(en);
5476
+ const server = registry.regValueOf(sv);
5477
+ const pac = registry.regValueOf(pc);
5478
+ const state = on === 1 ? "on" : on === 0 ? pac ? "on" : "off" : "unknown";
5479
+ note("registry:Internet Settings", "ProxyEnable=" + (on === null ? "unreadable" : on) + " server=" + (server || "-") + " pac=" + (pac ? "set" : "-"));
5480
+ return { state, server: server || null, pac: pac || null, source: "registry:Internet Settings" };
5481
+ });
5482
+ }
5483
+ function proxyMac(runOut, note) {
5484
+ return Promise.resolve(runOut("scutil", ["--proxy"])).then((out) => {
5485
+ if (!out) {
5486
+ note("scutil --proxy", "unreadable");
5487
+ return { state: "unknown", server: null, pac: null, source: "scutil" };
5488
+ }
5489
+ const kv = {};
5490
+ for (const line of String(out).split(/\r?\n/)) {
5491
+ const m = line.match(/^\s*([A-Za-z]+)\s*:\s*(.+?)\s*$/);
5492
+ if (m) kv[m[1]] = m[2];
5493
+ }
5494
+ const flagOn = (k) => kv[k] === "1";
5495
+ const anyManual = kv.HTTPEnable !== void 0 || kv.HTTPSEnable !== void 0 || kv.SOCKSEnable !== void 0 || kv.ProxyAutoConfigEnable !== void 0;
5496
+ const state = flagOn("HTTPEnable") || flagOn("HTTPSEnable") || flagOn("SOCKSEnable") || flagOn("ProxyAutoConfigEnable") ? "on" : anyManual ? "off" : "unknown";
5497
+ note("scutil --proxy", "state=" + state);
5498
+ return {
5499
+ state,
5500
+ server: kv.HTTPProxy || kv.HTTPSProxy || kv.SOCKSProxy || null,
5501
+ pac: kv.ProxyAutoConfigURLString || kv.ProxyAutoConfigURL || null,
5502
+ source: "scutil"
5503
+ };
5504
+ });
5505
+ }
5506
+ function proxyLinux(env, note) {
5507
+ const pick = (k) => typeof env[k] === "string" && env[k].trim() ? env[k].trim() : null;
5508
+ const server = pick("https_proxy") || pick("HTTPS_PROXY") || pick("http_proxy") || pick("HTTP_PROXY") || pick("ALL_PROXY") || pick("all_proxy");
5509
+ const noProxy = pick("no_proxy") || pick("NO_PROXY");
5510
+ const state = server ? "on" : "unknown";
5511
+ note("env proxy", "state=" + state);
5512
+ return { state, server, pac: pick("PAC_FILE") || null, noProxy, source: "env" };
5513
+ }
5514
+ function proxyUnknown(reason) {
5515
+ return { state: "unknown", server: null, pac: null, source: reason };
5516
+ }
5517
+ var _proxyCache = /* @__PURE__ */ new Map();
5518
+ function proxy(o) {
5519
+ const ov = o || {};
5520
+ const pl = ov.platform || process.platform;
5521
+ const now = typeof ov.now === "function" ? ov.now : Date.now;
5522
+ const ttl = ov.ttlMs === void 0 ? READ_TTL_MS : ov.ttlMs;
5523
+ const hit = _proxyCache.get(pl);
5524
+ if (!ov.force && ttl > 0 && hit && now() - hit.value.at < ttl) {
5525
+ return Promise.resolve(Object.assign({}, hit.value, { cached: true }));
5526
+ }
5527
+ const notes = [];
5528
+ const note = (source, detail) => {
5529
+ notes.push({ source, detail });
5530
+ };
5531
+ const runOut = ov.runOut || ((bin, args) => exec.runOutAsync(bin, args, { timeoutMs: PROBE_TIMEOUT_MS }));
5532
+ const out = Promise.resolve().then(() => {
5533
+ if (pl === "win32") return proxyWin(runOut, note);
5534
+ if (pl === "darwin") return proxyMac(runOut, note);
5535
+ if (pl === "linux") return proxyLinux(ov.env || process.env, note);
5536
+ return proxyUnknown("unsupported-platform");
5537
+ }).catch((e) => {
5538
+ note(pl + " proxy probe", "error: " + String(e && (e.code || e.message) || e));
5539
+ return proxyUnknown("probe-failed");
5540
+ }).then((v) => {
5541
+ const value = Object.assign({ platform: pl, at: now(), probed: notes }, v);
5542
+ _proxyCache.set(pl, { value });
5543
+ return value;
5544
+ });
5545
+ return out;
5546
+ }
5547
+ function proxyRead() {
5548
+ const hit = _proxyCache.get(process.platform);
5549
+ return hit ? hit.value : null;
5550
+ }
5551
+ function reachWith(deps, host2, port, timeoutMs) {
5552
+ const d = deps || {};
5553
+ const ms = timeoutMs || PROBE_TIMEOUT_MS;
5554
+ const lookup = d.lookup || ((h) => new Promise((resolve, reject) => dns.lookup(h, (err, addr) => err ? reject(err) : resolve(addr))));
5555
+ const connect = d.connect || ((opts) => new Promise((resolve, reject) => {
5556
+ let settled = false;
5557
+ const sock = tls.connect(opts);
5558
+ const fail = (e) => {
5559
+ if (settled) return;
5560
+ settled = true;
5561
+ try {
5562
+ sock.destroy();
5563
+ } catch {
5564
+ }
5565
+ reject(e);
5566
+ };
5567
+ sock.once("secureConnect", () => {
5568
+ if (settled) return;
5569
+ settled = true;
5570
+ try {
5571
+ sock.destroy();
5572
+ } catch {
5573
+ }
5574
+ resolve(true);
5575
+ });
5576
+ sock.once("error", fail);
5577
+ sock.setTimeout(ms, () => fail(Object.assign(new Error("tls-timeout"), { code: "ETIMEDOUT" })));
5578
+ }));
5579
+ const started = Date.now();
5580
+ const verdict = (ok, stage, detail) => ({ ok, stage, host: host2, port, detail, at: started });
5581
+ return lookup(host2).then((addr) => connect({ host: addr, port, servername: host2, timeout: ms })).then(() => verdict(true, "tls", "TLS \u63E1\u624B\u5B8C\u6210")).catch((e) => {
5582
+ const code = String(e && (e.code || e.message) || e);
5583
+ if (/ENOTFOUND/i.test(code)) return verdict(false, "dns", code);
5584
+ if (/ECONNREFUSED/i.test(code)) return verdict(false, "tcp", code);
5585
+ if (/EPROTO|handshake|ALPR|CERT|UNEXPECTED/i.test(code)) return verdict(false, "tls", code);
5586
+ if (/EAI_AGAIN|getaddrinfo|ESERVFAIL|ENODATA/i.test(code)) return verdict(null, "dns", code);
5587
+ if (/ETIMEDOUT|ECONNRESET|EHOSTUNREACH|ENETUNREACH|timeout/i.test(code)) return verdict(null, "tcp", code);
5588
+ return verdict(null, "tcp", code);
5589
+ });
5590
+ }
5591
+ var _reachCache = /* @__PURE__ */ new Map();
5592
+ function reach(host2, o) {
5593
+ const ov = o || {};
5594
+ const h = String(host2 || "").toLowerCase();
5595
+ if (!h) return Promise.resolve({ ok: null, stage: "not-attempted", host: host2 || null, port: null, detail: "\u6CA1\u6709\u53EF\u5224\u5B9A\u7684\u4E3B\u673A", at: Date.now() });
5596
+ const ttl = ov.ttlMs === void 0 ? READ_TTL_MS : ov.ttlMs;
5597
+ const hit = _reachCache.get(h);
5598
+ if (!ov.force && ttl > 0 && hit && Date.now() - hit.value.at < ttl) {
5599
+ return Promise.resolve(Object.assign({}, hit.value, { cached: true }));
5600
+ }
5601
+ const port = ov.port || 443;
5602
+ return reachWith(ov, h, port, ov.timeoutMs).then((v) => {
5603
+ _reachCache.set(h, { value: v });
5604
+ return v;
5605
+ });
5606
+ }
5607
+ function reachRead(host2) {
5608
+ const h = String(host2 || "").toLowerCase();
5609
+ const hit = _reachCache.get(h);
5610
+ return hit ? hit.value : null;
5611
+ }
5612
+ function hosts() {
5613
+ return [..._reachCache.keys()];
5614
+ }
5615
+ function invalidate(host2) {
5616
+ if (host2 === void 0 || host2 === null) {
5617
+ const n = _reachCache.size + _proxyCache.size;
5618
+ _reachCache.clear();
5619
+ _proxyCache.clear();
5620
+ return n;
5621
+ }
5622
+ return _reachCache.delete(String(host2).toLowerCase()) ? 1 : 0;
5623
+ }
5624
+ module2.exports = {
5625
+ PROBE_TIMEOUT_MS,
5626
+ READ_TTL_MS,
5627
+ hostOf,
5628
+ proxy,
5629
+ proxyRead,
5630
+ proxyWin,
5631
+ proxyMac,
5632
+ proxyLinux,
5633
+ reach,
5634
+ reachRead,
5635
+ reachWith,
5636
+ hosts,
5637
+ invalidate
5638
+ };
5639
+ }
5640
+ });
5641
+
5642
+ // src/platform/contract/shell-report.js
5643
+ var require_shell_report = __commonJS({
5644
+ "src/platform/contract/shell-report.js"(exports2, module2) {
5645
+ "use strict";
5646
+ var fs2 = require("node:fs");
5647
+ var path2 = require("node:path");
5648
+ var stateRoot2 = require_state_root();
5649
+ var { maskProxyServer, maskProxySecrets } = require_redact();
5650
+ var SUPPORTED_SCHEMA = 1;
5651
+ var FILE_NAME = "shell-report.json";
5652
+ var MAX_BYTES = 256 * 1024;
5653
+ var MAX_ROWS = 64;
5654
+ function file() {
5655
+ return path2.join(stateRoot2.supervisorDir(), FILE_NAME);
5656
+ }
5657
+ var str = (v) => typeof v === "string" && v.trim() ? maskProxySecrets(v.trim()) : null;
5658
+ var raw = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
5659
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
5660
+ var tri = (v) => v === true || v === false ? v : null;
5661
+ var list = (v) => Array.isArray(v) ? v : [];
5662
+ function nodeView(n) {
5663
+ const o = n && typeof n === "object" ? n : null;
5664
+ if (!o) return null;
5665
+ return { path: str(o.path), binDir: str(o.binDir), version: raw(o.version), min: raw(o.min), ok: tri(o.ok) };
5666
+ }
5667
+ function npmView(n) {
5668
+ const o = n && typeof n === "object" ? n : null;
5669
+ if (!o) return null;
5670
+ return { path: str(o.path), args: list(o.args).map(String), version: raw(o.version), ok: tri(o.ok) };
5671
+ }
5672
+ function prefixView(n) {
5673
+ const o = n && typeof n === "object" ? n : null;
5674
+ if (!o) return null;
5675
+ return { dir: str(o.dir), writable: tri(o.writable), why: str(o.why) };
5676
+ }
5677
+ function registryView(n) {
5678
+ const o = n && typeof n === "object" ? n : null;
5679
+ if (!o) return null;
5680
+ const rows = list(o.probes).slice(0, MAX_ROWS).map((p) => ({
5681
+ url: p && typeof p === "object" ? maskProxyServer(raw(p.url)) : null,
5682
+ ok: p && typeof p === "object" ? tri(p.ok) : null,
5683
+ latencyMs: p && typeof p === "object" ? num(p.latencyMs) : null
5684
+ }));
5685
+ return { best: maskProxyServer(raw(o.best)), latencyMs: num(o.latencyMs), probes: rows, probesTotal: list(o.probes).length };
5686
+ }
5687
+ function recordsView(recs) {
5688
+ const all = list(recs);
5689
+ const rows = all.slice(0, MAX_ROWS).map((r) => {
5690
+ const o = r && typeof r === "object" ? r : {};
5691
+ return {
5692
+ probe: raw(o.probe) || "unknown",
5693
+ source: str(o.source) || "",
5694
+ target: str(o.target) || "",
5695
+ ms: num(o.ms),
5696
+ ok: tri(o.ok),
5697
+ note: str(o.note) || ""
5698
+ };
5699
+ });
5700
+ return { rows, truncated: all.length > MAX_ROWS ? all.length - MAX_ROWS : 0 };
5701
+ }
5702
+ function bodyOf(j) {
5703
+ const recs = recordsView(j.records);
5704
+ const body = {
5705
+ writtenBy: raw(j.writtenBy),
5706
+ schema: num(j.schema),
5707
+ node: nodeView(j.node),
5708
+ npm: npmView(j.npm),
5709
+ prefix: prefixView(j.prefix),
5710
+ registry: registryView(j.registry),
5711
+ records: recs.rows,
5712
+ droppedRecords: recs.truncated
5713
+ };
5714
+ const seen = [body.node, body.npm, body.prefix, body.registry].some((x) => !!x) || body.records.length > 0;
5715
+ return seen ? body : null;
5716
+ }
5717
+ function envelope(p, at, ageMs, reason, body) {
5718
+ return Object.assign({ available: reason === "ok", path: p, at, ageMs, reason }, body || {
5719
+ writtenBy: null,
5720
+ schema: null,
5721
+ node: null,
5722
+ npm: null,
5723
+ prefix: null,
5724
+ registry: null,
5725
+ records: [],
5726
+ droppedRecords: 0
5727
+ });
5728
+ }
5729
+ function read(o) {
5730
+ const now = o && typeof o.now === "function" ? o.now : Date.now;
5731
+ const p = file();
5732
+ let text;
5733
+ try {
5734
+ const st = fs2.statSync(p);
5735
+ if (st.size > MAX_BYTES) return envelope(p, null, null, "unreadable-or-schema-mismatch", null);
5736
+ text = fs2.readFileSync(p, "utf8");
5737
+ } catch (e) {
5738
+ if (e && e.code === "ENOENT") return envelope(p, null, null, "never-written", null);
5739
+ return envelope(p, null, null, "unreadable-or-schema-mismatch", null);
5740
+ }
5741
+ let j;
5742
+ try {
5743
+ j = JSON.parse(text);
5744
+ } catch {
5745
+ return envelope(p, null, null, "unreadable-or-schema-mismatch", null);
5746
+ }
5747
+ if (!j || typeof j !== "object" || Array.isArray(j) || num(j.schema) !== SUPPORTED_SCHEMA) {
5748
+ return envelope(p, null, null, "unreadable-or-schema-mismatch", null);
5749
+ }
5750
+ const at = num(j.at);
5751
+ if (at === null) return envelope(p, null, null, "unreadable-or-schema-mismatch", null);
5752
+ const ageMs = Math.max(0, now() - at);
5753
+ const body = bodyOf(j);
5754
+ if (!body) return envelope(p, at, null, "unreadable-or-schema-mismatch", null);
5755
+ return envelope(p, at, ageMs, "ok", body);
5756
+ }
5757
+ module2.exports = { SUPPORTED_SCHEMA, FILE_NAME, MAX_BYTES, MAX_ROWS, file, read };
5758
+ }
5759
+ });
5760
+
5408
5761
  // src/platform/os/environment.js
5409
5762
  var require_environment = __commonJS({
5410
5763
  "src/platform/os/environment.js"(exports2, module2) {
@@ -5414,12 +5767,17 @@ var require_environment = __commonJS({
5414
5767
  var path2 = require("node:path");
5415
5768
  var stateRoot2 = require_state_root();
5416
5769
  var { writeAtomic } = require_fs();
5770
+ var { maskProxyServer, maskProxySecrets } = require_redact();
5417
5771
  var desktop = require_desktop();
5418
5772
  var detector = require_browser_inventory();
5773
+ var egress = require_egress();
5774
+ var shellReport = require_shell_report();
5419
5775
  var { engineOf } = detector;
5420
- var SCHEMA = 1;
5776
+ var SCHEMA = 2;
5421
5777
  var FILE_NAME = "environment.json";
5422
5778
  var FORM_TTL_MS = 6e4;
5779
+ var SECTION_TTL_MS = 6e4;
5780
+ var SHELL_REPORT_TTL_MS = 1e4;
5423
5781
  var _sources = {};
5424
5782
  function bind(sources) {
5425
5783
  _sources = Object.assign({}, _sources, sources || {});
@@ -5510,6 +5868,148 @@ var require_environment = __commonJS({
5510
5868
  if (list.length > 1) return out(rankCandidates(list)[0], "candidate-rank");
5511
5869
  return out(null, "none-found");
5512
5870
  }
5871
+ var _sections = /* @__PURE__ */ new Map();
5872
+ var _reads = /* @__PURE__ */ new Map();
5873
+ var SECTION_ORDER = ["runtime", "shell", "dsh", "browsers", "session", "egress", "capabilities", "preference", "pick", "startup"];
5874
+ var SYNC_DIMS = ["browsers", "session", "capabilities", "preference", "pick"];
5875
+ function registerSection(id, def) {
5876
+ if (!id || !def || typeof def.probe !== "function") throw new Error("registerSection(" + id + ") \u9700\u8981 probe \u51FD\u6570");
5877
+ if (SYNC_DIMS.includes(id)) throw new Error("\u7EF4\u5EA6 " + id + " \u7531\u8868\u5355\u6BCF\u62CD\u81EA\u88C5\uFF0C\u4E0D\u63A5\u6CE8\u518C");
5878
+ const d = { id, label: def.label || id, probe: def.probe, ttlMs: def.ttlMs === void 0 ? SECTION_TTL_MS : def.ttlMs, source: def.source || "registered" };
5879
+ _sections.set(id, d);
5880
+ _reads.delete(id);
5881
+ return d;
5882
+ }
5883
+ function unregisterSection(id) {
5884
+ _sections.delete(id);
5885
+ _reads.delete(id);
5886
+ }
5887
+ function section(id) {
5888
+ return _reads.get(id) || null;
5889
+ }
5890
+ function sectionData(id) {
5891
+ const r = _reads.get(id);
5892
+ return r ? r.data || null : null;
5893
+ }
5894
+ function proxyDataOf(p) {
5895
+ return p ? { state: p.state, server: maskProxyServer(p.server), pac: p.pac || null, source: p.source, cached: p.cached === true } : null;
5896
+ }
5897
+ function egressData(proxyReading) {
5898
+ const targets = {};
5899
+ for (const h of egress.hosts()) {
5900
+ const r = egress.reachRead(h);
5901
+ if (r) targets[h] = { ok: r.ok === null ? null : r.ok === true, stage: r.stage, detail: r.detail, at: r.at };
5902
+ }
5903
+ const p = proxyReading || egress.proxyRead();
5904
+ const probed = [];
5905
+ if (p) {
5906
+ for (const row of p.probed || []) probed.push({ source: row.source, detail: maskProxySecrets(String(row.detail || "")) });
5907
+ probed.push({ source: p.source, detail: "proxy=" + p.state + (p.server ? " (" + maskProxyServer(p.server) + ")" : "") + (p.cached ? " \u590D\u7528" : "") });
5908
+ } else {
5909
+ probed.push({ source: "egress", detail: "\u7CFB\u7EDF\u4EE3\u7406\u672A\u8BFB\uFF08\u672C\u7EF4\u5EA6\u5C1A\u672A\u5237\u65B0\uFF09" });
5910
+ }
5911
+ for (const h of Object.keys(targets)) {
5912
+ const t = targets[h];
5913
+ probed.push({ source: "reach:" + h, detail: (t.ok === null ? "unknown" : t.ok ? "ok" : "no-route") + "@" + t.stage + " " + t.detail });
5914
+ }
5915
+ return {
5916
+ at: p ? p.at : null,
5917
+ proxy: proxyDataOf(p),
5918
+ targets,
5919
+ probed
5920
+ };
5921
+ }
5922
+ registerSection("egress", {
5923
+ label: "\u51FA\u7F51\u6761\u4EF6",
5924
+ source: "self",
5925
+ probe: (o) => {
5926
+ const ov = o || {};
5927
+ const force = ov.force === true;
5928
+ const hosts = Array.isArray(ov.hosts) ? ov.hosts : egress.hosts();
5929
+ const reachOpts = { force, timeoutMs: ov.timeoutMs, lookup: ov.lookup, connect: ov.connect, ttlMs: ov.ttlMs };
5930
+ return Promise.all([egress.proxy(Object.assign({}, ov, { force }))].concat(hosts.map((h) => egress.reach(h, reachOpts)))).then(([p]) => egressData(p));
5931
+ }
5932
+ });
5933
+ registerSection("shell", {
5934
+ label: "\u684C\u9762\u58F3\u6240\u89C1\uFF08Node/npm/\u955C\u50CF\u6E90/\u5168\u5C40\u524D\u7F00\uFF09",
5935
+ source: "shell",
5936
+ ttlMs: SHELL_REPORT_TTL_MS,
5937
+ probe: () => shellReport.read()
5938
+ });
5939
+ async function probeSection(id, o) {
5940
+ const def = _sections.get(id);
5941
+ if (!def) return null;
5942
+ const ov = o || {};
5943
+ const now = typeof ov.now === "function" ? ov.now : Date.now;
5944
+ try {
5945
+ const data = await def.probe(ov);
5946
+ _reads.set(id, { at: now(), source: def.source, label: def.label, state: data ? "ok" : "empty", data: data || null, error: null });
5947
+ } catch (e) {
5948
+ _reads.set(id, { at: now(), source: def.source, label: def.label, state: "error", data: null, error: String(e && (e.code || e.message) || e) });
5949
+ }
5950
+ return _reads.get(id);
5951
+ }
5952
+ function staleOf(id, now, force) {
5953
+ if (force) return true;
5954
+ const def = _sections.get(id);
5955
+ const read = _reads.get(id);
5956
+ if (!def || !read) return true;
5957
+ return now - read.at >= def.ttlMs;
5958
+ }
5959
+ async function refresh(o) {
5960
+ const ov = o || {};
5961
+ const now = typeof ov.now === "function" ? ov.now : Date.now;
5962
+ const ids = Array.isArray(ov.only) && ov.only.length ? ov.only : [..._sections.keys()];
5963
+ await Promise.all(ids.filter((id) => _sections.has(id) && staleOf(id, now(), ov.force)).map((id) => probeSection(id, ov)));
5964
+ return form(Object.assign({}, ov, { force: true }));
5965
+ }
5966
+ async function checkEgress(url, o) {
5967
+ const ov = o || {};
5968
+ const host2 = egress.hostOf(url);
5969
+ let data = ov.egress === void 0 ? null : ov.egress;
5970
+ if (!data) {
5971
+ try {
5972
+ const hosts = new Set(egress.hosts());
5973
+ if (host2) {
5974
+ await egress.reach(host2, ov);
5975
+ hosts.add(host2);
5976
+ }
5977
+ const read = await probeSection("egress", Object.assign({}, ov, { hosts: [...hosts] }));
5978
+ data = read && read.data || sectionData("egress");
5979
+ } catch {
5980
+ data = sectionData("egress");
5981
+ }
5982
+ }
5983
+ const v = coldProfileViable(data, host2);
5984
+ return Object.assign({}, v, {
5985
+ host: host2,
5986
+ proxy: data && data.proxy ? data.proxy.state : "unknown",
5987
+ at: data ? data.at || null : null
5988
+ });
5989
+ }
5990
+ function coldProfileViable(eg, host2) {
5991
+ if (!eg) return { viable: null, basis: "egress-unprobed", detail: "\u51FA\u7F51\u6761\u4EF6\u5C1A\u672A\u63A2\u6D4B\uFF0C\u9694\u79BB\u7A97\u53E3\u6309\u539F\u6863\u6253\u5F00" };
5992
+ const t = host2 && eg.targets ? eg.targets[host2] : null;
5993
+ const reached = t ? t.ok : null;
5994
+ const proxyState = eg.proxy ? eg.proxy.state : "unknown";
5995
+ if (reached === true) {
5996
+ return { viable: true, basis: "target-reachable", detail: host2 + " \u76F4\u8FDE\u53EF\u8FBE\uFF08" + t.stage + "\uFF09" };
5997
+ }
5998
+ if (reached === null) {
5999
+ return { viable: null, basis: "egress-undetermined", detail: host2 + " \u7684\u901A\u8DEF\u5224\u5B9A\u6CA1\u6709\u7ED9\u51FA\u7B54\u6848\uFF08" + (t ? t.stage + " " + t.detail : "\u65E0\u8BFB\u6570") + "\uFF09" };
6000
+ }
6001
+ if (proxyState === "on") {
6002
+ return { viable: true, basis: "cold-profile-inherits-proxy", detail: host2 + " \u76F4\u8FDE\u4E0D\u901A\uFF08" + t.stage + "\uFF09\uFF0C\u7CFB\u7EDF\u7EA7\u4EE3\u7406\u5728\u7528\uFF0C\u51B7\u6863\u6848\u540C\u4E00\u6761\u8DEF" };
6003
+ }
6004
+ if (proxyState === "unknown") {
6005
+ return { viable: null, basis: "proxy-unreadable", detail: host2 + " \u76F4\u8FDE\u4E0D\u901A\uFF08" + t.stage + "\uFF09\uFF0C\u4F46\u4EE3\u7406\u8BFB\u6570\u53D6\u4E0D\u5230\uFF0C\u6309\u5224\u4E0D\u51FA\u5904\u7406" };
6006
+ }
6007
+ return {
6008
+ viable: false,
6009
+ basis: "cold-profile-blocked",
6010
+ detail: host2 + " \u76F4\u8FDE\u4E0D\u901A\uFF08" + t.stage + " " + t.detail + "\uFF09\u4E14\u7CFB\u7EDF\u6CA1\u6709\u5728\u7528\u4EE3\u7406\uFF0C\u51B7\u6863\u6848\u7A97\u53E3\u5FC5\u7136\u7A7A\u767D"
6011
+ };
6012
+ }
5513
6013
  function snapshotPath() {
5514
6014
  return path2.join(stateRoot2.supervisorDir(), FILE_NAME);
5515
6015
  }
@@ -5521,6 +6021,23 @@ var require_environment = __commonJS({
5521
6021
  return null;
5522
6022
  }
5523
6023
  }
6024
+ function lastSnapshot(o) {
6025
+ const now = o && typeof o.now === "function" ? o.now : Date.now;
6026
+ const p = snapshotPath();
6027
+ const doc = readSnapshot();
6028
+ if (doc) {
6029
+ const at = typeof doc.at === "number" ? doc.at : null;
6030
+ return { available: true, path: p, at, ageMs: at === null ? null : Math.max(0, now() - at), reason: "ok", data: doc };
6031
+ }
6032
+ return {
6033
+ available: false,
6034
+ path: p,
6035
+ at: null,
6036
+ ageMs: null,
6037
+ reason: fs2.existsSync(p) ? "unreadable-or-schema-mismatch" : "never-written",
6038
+ data: null
6039
+ };
6040
+ }
5524
6041
  var _formCache = null;
5525
6042
  function form(o) {
5526
6043
  const ov = o || {};
@@ -5560,6 +6077,45 @@ var require_environment = __commonJS({
5560
6077
  source: "form",
5561
6078
  detail: pick.how + (pick.browser ? "\uFF08" + pick.browser.name + "\uFF09" : "\uFF08\u65E0\u5019\u9009\uFF09") + (pick.stale ? "\uFF0C\u504F\u597D\u5DF2\u5931\u6548\u9700\u91CD\u9009" : "")
5562
6079
  });
6080
+ const sections = {};
6081
+ const syncDims = {
6082
+ browsers: { at: stamp, state: inv.browsers.length ? "ok" : "empty", count: inv.browsers.length, defaultSource: inv.defaultSource },
6083
+ session: { at: stamp, state: session.available ? "ok" : "missing", reason: session.reason },
6084
+ capabilities: { at: stamp, state: caps ? "ok" : "unbound", openBrowser: caps ? caps.openBrowser === true : null },
6085
+ preference: { at: stamp, state: preference.reason === "matched" ? "ok" : preference.configured ? "stale" : "unset", id: preference.id },
6086
+ pick: { at: stamp, state: pick.browser ? "ok" : "empty", how: pick.how, id: pick.browser ? pick.browser.id : null }
6087
+ };
6088
+ const dimOf = (id) => {
6089
+ const def = _sections.get(id);
6090
+ const read = _reads.get(id);
6091
+ if (read && !syncDims[id]) {
6092
+ return { label: def ? def.label : id, at: read.at, source: read.source, state: read.state, data: read.data, error: read.error || null };
6093
+ }
6094
+ const s = syncDims[id];
6095
+ if (s) return Object.assign({ label: id, source: "self" }, s);
6096
+ return { label: def ? def.label : id, at: null, source: def ? def.source : "registered", state: "pending", data: null, error: null };
6097
+ };
6098
+ const ids = SECTION_ORDER.concat([..._sections.keys()].filter((id) => !SECTION_ORDER.includes(id)));
6099
+ for (const id of ids) {
6100
+ if (sections[id]) continue;
6101
+ const entry = dimOf(id);
6102
+ sections[id] = entry;
6103
+ if (entry.state === "pending") {
6104
+ probed.push({ section: id, source: "form", detail: "\u672A\u5237\u65B0\uFF08\u7B49\u542F\u52A8\u88C5\u914D\u6216\u9762\u677F force \u5237\u65B0\u8865\u62CD\uFF09" });
6105
+ } else if (entry.state === "error") {
6106
+ probed.push({ section: id, source: "probe", detail: id + " \u63A2\u6D4B\u5931\u8D25: " + entry.error });
6107
+ }
6108
+ }
6109
+ const egressRows = ((sections.egress || {}).data || {}).probed || [];
6110
+ for (const row of egressRows) probed.push({ section: "egress", source: row.source, detail: row.detail });
6111
+ const sh = (sections.shell || {}).data;
6112
+ if (sh) {
6113
+ probed.push({
6114
+ section: "shell",
6115
+ source: "shell-report:" + (sh.reason || "?"),
6116
+ detail: sh.available === true ? "\u58F3\u5DF2\u4E0A\u62A5\uFF08\u5199\u5165\u8005 " + (sh.writtenBy || "\u672A\u7F72\u540D") + "\uFF0Cschema " + sh.schema + "\uFF0C\u8DDD\u4ECA " + (sh.ageMs === null ? "\u672A\u8BFB\u51FA" : sh.ageMs) + " \u6BEB\u79D2\uFF0C\u660E\u7EC6 " + (sh.records || []).length + " \u6761" + (sh.droppedRecords ? "\uFF0C\u622A\u65AD " + sh.droppedRecords + " \u6761" : "") + "\uFF09" : sh.reason === "never-written" ? "\u8FD9\u53F0\u673A\u5668\u7684\u58F3\u8FD8\u6CA1\u62A5\u8FC7\uFF08\u5185\u6838\u72EC\u7ACB\u8DD1\u6216\u8001\u7248\u672C\u58F3\u65F6\u662F\u5E38\u6001\uFF0C\u4E0D\u662F\u6545\u969C\uFF09" : "\u58F3\u62A5\u7684\u6587\u4EF6\u8BFB\u4E0D\u51FA\u6216\u7248\u672C\u4E0D\u7B26\uFF0C\u5F97\u67E5\uFF1A" + (sh.path || "?")
6117
+ });
6118
+ }
5563
6119
  const value = {
5564
6120
  schema: SCHEMA,
5565
6121
  at: stamp,
@@ -5572,6 +6128,7 @@ var require_environment = __commonJS({
5572
6128
  default: inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource } : null,
5573
6129
  browsers: inv.browsers,
5574
6130
  pick: { how: pick.how, id: pick.browser ? pick.browser.id : null, name: pick.browser ? pick.browser.name : null, wanted: pick.wanted, stale: pick.stale },
6131
+ sections,
5575
6132
  probed
5576
6133
  };
5577
6134
  const snapshot = { path: snapshotPath(), written: false, error: null };
@@ -5592,8 +6149,14 @@ var require_environment = __commonJS({
5592
6149
  _formCache = { at: stamp, value };
5593
6150
  return Object.assign({ cached: false }, value);
5594
6151
  }
5595
- function invalidate(platform) {
6152
+ function invalidate(platform, only) {
5596
6153
  _formCache = null;
6154
+ const list = only === void 0 ? null : Array.isArray(only) ? only : [only];
6155
+ if (!list || list.includes("egress")) egress.invalidate();
6156
+ for (const id of list || [..._reads.keys()]) {
6157
+ const r = _reads.get(id);
6158
+ if (r) r.at = 0;
6159
+ }
5597
6160
  return detector.invalidate(platform);
5598
6161
  }
5599
6162
  function checkPreference(value, form2) {
@@ -5615,6 +6178,9 @@ var require_environment = __commonJS({
5615
6178
  SCHEMA,
5616
6179
  FILE_NAME,
5617
6180
  FORM_TTL_MS,
6181
+ SECTION_TTL_MS,
6182
+ SECTION_ORDER,
6183
+ SYNC_DIMS,
5618
6184
  bind,
5619
6185
  preferenceId,
5620
6186
  capabilities,
@@ -5625,9 +6191,17 @@ var require_environment = __commonJS({
5625
6191
  rankCandidates,
5626
6192
  pickLauncher,
5627
6193
  checkPreference,
6194
+ registerSection,
6195
+ unregisterSection,
6196
+ section,
6197
+ sectionData,
6198
+ refresh,
6199
+ coldProfileViable,
6200
+ checkEgress,
5628
6201
  form,
5629
6202
  snapshotPath,
5630
6203
  readSnapshot,
6204
+ lastSnapshot,
5631
6205
  invalidate
5632
6206
  };
5633
6207
  }
@@ -6513,9 +7087,7 @@ var require_browser = __commonJS({
6513
7087
  }));
6514
7088
  const pk = picked || {};
6515
7089
  return {
6516
- platform: inv && inv.platform || null,
6517
7090
  pick: pk.how || plan && plan.pick || "none",
6518
- bin: plan && plan.bin || null,
6519
7091
  default: inv && inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource || null } : null,
6520
7092
  preference: pk.wanted ? { id: pk.wanted, matched: pk.stale !== true } : null,
6521
7093
  found,
@@ -6577,8 +7149,10 @@ var require_browser = __commonJS({
6577
7149
  const picked = opts.pick || environment.pickLauncher(pl, inv, pref);
6578
7150
  const form = formOfBin(pl, picked.browser);
6579
7151
  const canIsolate = intent === "isolated-login" && form.direct && (form.engine === "chromium" || form.engine === "firefox");
7152
+ const cold = canIsolate ? await environment.checkEgress(url, opts) : null;
7153
+ const isolate = canIsolate && (!cold || cold.viable !== false);
6580
7154
  let profile = null;
6581
- if (canIsolate) {
7155
+ if (isolate) {
6582
7156
  const alloc = typeof opts.allocProfile === "function" ? opts.allocProfile : (() => allocTempDir("dsh-login-"));
6583
7157
  try {
6584
7158
  profile = opts.profileDir || alloc();
@@ -6599,8 +7173,11 @@ var require_browser = __commonJS({
6599
7173
  exitCode: null,
6600
7174
  exitSignal: null,
6601
7175
  error: null,
6602
- diagnostics
7176
+ diagnostics,
7177
+ egress: cold
6603
7178
  };
7179
+ const downgraded = cold && cold.viable === false;
7180
+ const downgradeText = downgraded ? "\u672C\u673A\u76F4\u8FDE " + cold.host + " \u4E0D\u901A\u4E14\u6CA1\u6709\u5728\u7528\u7CFB\u7EDF\u4EE3\u7406\uFF0C\u9694\u79BB\u7A97\u53E3\u4F1A\u662F\u7A7A\u767D\u9875\uFF1B\u5DF2\u5728\u73B0\u6709\u6D4F\u89C8\u5668\u7A97\u53E3\u6253\u5F00\u8BE5\u5730\u5740\uFF08\u767B\u5F55\u5B8C\u6210\u540E\u8BF7\u624B\u52A8\u6E05\u7406\u8D26\u53F7\uFF0C\u6216\u5148\u5728\u7CFB\u7EDF\u91CC\u914D\u597D\u4EE3\u7406\uFF09" : null;
6604
7181
  const deferredRemove = typeof opts.rmTree === "function" ? opts.rmTree : removeTreeDeferred;
6605
7182
  const fail = (reason, patch) => {
6606
7183
  if (profile) deferredRemove(profile, 0);
@@ -6612,7 +7189,7 @@ var require_browser = __commonJS({
6612
7189
  }
6613
7190
  if (!plan.bin) return fail("no-launcher");
6614
7191
  if (!avail(plan.bin)) return fail("no-launcher");
6615
- if (canIsolate && !profile) {
7192
+ if (isolate && !profile) {
6616
7193
  evidence.error = "\u4E34\u65F6 profile \u76EE\u5F55\u5206\u914D\u5931\u8D25";
6617
7194
  return fail("spawn-failed");
6618
7195
  }
@@ -6640,7 +7217,14 @@ var require_browser = __commonJS({
6640
7217
  error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
6641
7218
  });
6642
7219
  }
6643
- return outcome({ ok: true, confirmed: exitDecides, handedOff: !exitDecides, url, evidence });
7220
+ return outcome({
7221
+ ok: true,
7222
+ confirmed: exitDecides,
7223
+ handedOff: !exitDecides,
7224
+ url,
7225
+ evidence,
7226
+ message: downgradeText || void 0
7227
+ });
6644
7228
  }
6645
7229
  module2.exports = {
6646
7230
  openBrowser,
@@ -7108,6 +7692,7 @@ var require_bootstrap = __commonJS({
7108
7692
  var { createShellWatchdog } = require_watchdog();
7109
7693
  var pidlook = require_pidlookup();
7110
7694
  var platform = require_os();
7695
+ var ENV_FORM_STARTUP_DELAY_MS = 3e3;
7111
7696
  function _bootstrap(host2) {
7112
7697
  try {
7113
7698
  host2.events.append("guard_started", {
@@ -7123,6 +7708,15 @@ var require_bootstrap = __commonJS({
7123
7708
  } catch {
7124
7709
  }
7125
7710
  host2.logger.info("guard started v" + host2.guardVersion + " pid=" + process.pid);
7711
+ host2._startupFacts = {
7712
+ bootAt: Date.now(),
7713
+ envDelayMs: ENV_FORM_STARTUP_DELAY_MS,
7714
+ routerAutostart: host2.config.routerAutostart === true,
7715
+ routerMode: null,
7716
+ updateCheck: null,
7717
+ shellWatchdog: null,
7718
+ lastRefresh: null
7719
+ };
7126
7720
  host2.tick();
7127
7721
  host2._timer = host2.managedObjects ? null : setInterval(() => host2.tick(), host2.config.probeIntervalMs);
7128
7722
  host2._lastHeartbeatAt = Date.now();
@@ -7178,6 +7772,7 @@ var require_bootstrap = __commonJS({
7178
7772
  }
7179
7773
  if (host2._routerDaemonActive()) host2._writeRouterDaemonLock();
7180
7774
  const rt = host2._ensureRouterRuntime(true);
7775
+ host2._startupFacts.routerMode = rt.mode;
7181
7776
  if (rt.mode === "daemon") {
7182
7777
  host2._disableRouterPersist();
7183
7778
  if (rt.spawned) {
@@ -7218,13 +7813,20 @@ var require_bootstrap = __commonJS({
7218
7813
  rlc._monitoring = false;
7219
7814
  }
7220
7815
  }
7816
+ const checkDelayMs = host2.config.initialCheckDelayMs || 2e4;
7817
+ const checkIntervalMs = host2.config.updateCheckIntervalMs || 36e5;
7818
+ host2._startupFacts.updateCheck = {
7819
+ enabled: host2.config.updateCheckEnabled !== false,
7820
+ initialDelayMs: checkDelayMs,
7821
+ intervalMs: checkIntervalMs
7822
+ };
7221
7823
  if (host2.config.updateCheckEnabled !== false) {
7222
7824
  host2._initialCheckTimer = setTimeout(() => {
7223
7825
  host2.nativeManager.checkUpdate();
7224
- }, host2.config.initialCheckDelayMs || 2e4);
7826
+ }, checkDelayMs);
7225
7827
  host2._upgradeTimer = setInterval(() => {
7226
7828
  host2.nativeManager.checkUpdate();
7227
- }, host2.config.updateCheckIntervalMs || 36e5);
7829
+ }, checkIntervalMs);
7228
7830
  }
7229
7831
  try {
7230
7832
  host2._startShellWatchdog();
@@ -7233,6 +7835,50 @@ var require_bootstrap = __commonJS({
7233
7835
  host2.logger.warn("[shell-watchdog] \u542F\u52A8\u5F02\u5E38\uFF08\u4E0D\u5F71\u54CD\u5B88\u536B\u4E3B\u5FAA\u73AF\uFF09: " + (e && e.message || e));
7234
7836
  }
7235
7837
  }
7838
+ host2._startupFacts.shellWatchdog = !!host2.shellWatchdog;
7839
+ const envTimer = setTimeout(() => {
7840
+ try {
7841
+ host2._refreshEnvironmentForm();
7842
+ } catch (e) {
7843
+ host2.logger.warn && host2.logger.warn("[environment] \u542F\u52A8\u5237\u65B0\u5F02\u5E38\uFF08\u4E0D\u5F71\u54CD\u5B88\u536B\u4E3B\u5FAA\u73AF\uFF09: " + (e && e.message || e));
7844
+ }
7845
+ }, ENV_FORM_STARTUP_DELAY_MS);
7846
+ if (envTimer && envTimer.unref) envTimer.unref();
7847
+ }
7848
+ function _refreshEnvironmentForm(host2) {
7849
+ const startedAt = Date.now();
7850
+ return platform.environment.refresh({ persist: true }).then((f) => {
7851
+ const secs = f && f.sections || {};
7852
+ const pending = Object.keys(secs).filter((id) => secs[id] && secs[id].state === "pending");
7853
+ const failed = Object.keys(secs).filter((id) => secs[id] && secs[id].state === "error");
7854
+ const snap = f && f.snapshot ? f.snapshot : {};
7855
+ if (host2._startupFacts) {
7856
+ const states = {};
7857
+ for (const id of Object.keys(secs)) states[id] = secs[id] ? secs[id].state : "missing";
7858
+ host2._startupFacts.lastRefresh = {
7859
+ at: f && f.at || null,
7860
+ tookMs: Date.now() - startedAt,
7861
+ dims: states,
7862
+ browsers: f && f.browsers && f.browsers.length || 0,
7863
+ pick: f && f.pick && f.pick.how || null,
7864
+ snapshotWritten: snap.written === true
7865
+ };
7866
+ }
7867
+ if (snap.written === false && snap.error) {
7868
+ host2.logger.warn && host2.logger.warn("[environment] \u5FEB\u7167\u672A\u843D\u76D8: " + snap.error);
7869
+ }
7870
+ if (failed.length) {
7871
+ host2.logger.warn && host2.logger.warn("[environment] \u7EF4\u5EA6\u63A2\u6D4B\u5931\u8D25: " + failed.map((id) => id + "(" + secs[id].error + ")").join("; "));
7872
+ }
7873
+ if (pending.length) {
7874
+ host2.logger.warn && host2.logger.warn("[environment] \u7EF4\u5EA6\u65E0\u63A2\u9488\uFF0C\u672C\u673A\u8BE5\u9879\u4E0D\u53EF\u5224: " + pending.join(","));
7875
+ }
7876
+ host2.logger.info && host2.logger.info("[environment] \u5DF2\u5237\u65B0\uFF1A" + Object.keys(secs).length + " \u4E2A\u7EF4\u5EA6\uFF0C\u6D4F\u89C8\u5668\u5019\u9009 " + (f && f.browsers && f.browsers.length || 0) + " \u4E2A\uFF0C\u5206\u53D1\u4F9D\u636E " + (f && f.pick && f.pick.how || "?"));
7877
+ return f;
7878
+ }).catch((e) => {
7879
+ host2.logger.warn && host2.logger.warn("[environment] \u5237\u65B0\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5B88\u536B\u4E3B\u5FAA\u73AF\uFF09: " + (e && e.message || e));
7880
+ return null;
7881
+ });
7236
7882
  }
7237
7883
  function _startShellWatchdog(host2) {
7238
7884
  if (host2.config.shellWatchdog === false) {
@@ -7302,7 +7948,7 @@ var require_bootstrap = __commonJS({
7302
7948
  }
7303
7949
  }
7304
7950
  }
7305
- module2.exports = { _bootstrap, _startShellWatchdog, _registerFixedPorts, _bindNativeDshCommand };
7951
+ module2.exports = { _bootstrap, _startShellWatchdog, _registerFixedPorts, _bindNativeDshCommand, _refreshEnvironmentForm };
7306
7952
  }
7307
7953
  });
7308
7954
 
@@ -7832,7 +8478,7 @@ var require_release = __commonJS({
7832
8478
  });
7833
8479
 
7834
8480
  // src/platform/contract/registry.js
7835
- var require_registry = __commonJS({
8481
+ var require_registry2 = __commonJS({
7836
8482
  "src/platform/contract/registry.js"(exports2, module2) {
7837
8483
  "use strict";
7838
8484
  var fs2 = require("node:fs");
@@ -7948,7 +8594,7 @@ var require_registry_config = __commonJS({
7948
8594
  "use strict";
7949
8595
  var fs2 = require("node:fs");
7950
8596
  var path2 = require("node:path");
7951
- var registryContract = require_registry();
8597
+ var registryContract = require_registry2();
7952
8598
  var { writeAtomic } = require_fs();
7953
8599
  var policies = require_policies();
7954
8600
  var CONTRACT_TTL_MS = 60 * 1e3;
@@ -8106,7 +8752,7 @@ var require_matrix = __commonJS({
8106
8752
  });
8107
8753
 
8108
8754
  // src/platform/distribution/registry.js
8109
- var require_registry2 = __commonJS({
8755
+ var require_registry3 = __commonJS({
8110
8756
  "src/platform/distribution/registry.js"(exports2, module2) {
8111
8757
  "use strict";
8112
8758
  var matrix = require_matrix();
@@ -8327,7 +8973,7 @@ var require_version_check = __commonJS({
8327
8973
  var { VERSION_RE } = require_version2();
8328
8974
  var input = require_input();
8329
8975
  var policies = require_policies();
8330
- var registry = require_registry2();
8976
+ var registry = require_registry3();
8331
8977
  var ref = require_registry_ref();
8332
8978
  var release = require_release();
8333
8979
  var PKG_NAME_RE = input.PKG_NAME_RE;
@@ -8694,7 +9340,7 @@ var require_distribution = __commonJS({
8694
9340
  var policies = require_policies();
8695
9341
  var release = require_release();
8696
9342
  var registryConfig = require_registry_config();
8697
- var registry = require_registry2();
9343
+ var registry = require_registry3();
8698
9344
  var versionCheck = require_version_check();
8699
9345
  var install = require_install();
8700
9346
  var { semverCompare, VERSION_RE } = require_version2();
@@ -14361,6 +15007,77 @@ var require_token = __commonJS({
14361
15007
  }
14362
15008
  });
14363
15009
 
15010
+ // src/app/native/npm.js
15011
+ var require_npm = __commonJS({
15012
+ "src/app/native/npm.js"(exports2, module2) {
15013
+ "use strict";
15014
+ var ex2 = require_exec();
15015
+ var runtimeContract = require_runtime();
15016
+ function npmLaunch(host2) {
15017
+ const h = host2 || {};
15018
+ if (h._npmBin) {
15019
+ return { program: h._npmBin, args: Array.isArray(h._npmBinArgs) ? h._npmBinArgs.slice() : [] };
15020
+ }
15021
+ const l = runtimeContract.npmLauncher();
15022
+ return { program: l.program, args: l.args };
15023
+ }
15024
+ async function resolveNpmRoot(host2, opts) {
15025
+ if (host2.npmRoot) return host2.npmRoot;
15026
+ const l = npmLaunch(host2);
15027
+ const r = await ex2.runOutAsync(l.program, l.args.concat(["root", "-g"]), opts || void 0);
15028
+ return r ? r.trim() : null;
15029
+ }
15030
+ async function checkEnvironment(host2) {
15031
+ const errors = [];
15032
+ const l = npmLaunch(host2);
15033
+ const [nv, npmv, npmRoot] = await Promise.all([
15034
+ ex2.runOutAsync("node", ["--version"]),
15035
+ ex2.runOutAsync(l.program, l.args.concat(["--version"])),
15036
+ resolveNpmRoot(host2)
15037
+ ]);
15038
+ if (!nv || !nv.trim()) errors.push("node \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
15039
+ if (!npmv || !npmv.trim()) errors.push("npm \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
15040
+ return { ok: errors.length === 0, errors, npmRoot };
15041
+ }
15042
+ async function latestVersion(host2) {
15043
+ 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");
15044
+ const channel = host2.config.releaseChannel || "npm";
15045
+ return host2.dist.fetchVersionInfo(host2.config.packageName, channel);
15046
+ }
15047
+ async function selectRegistry(host2) {
15048
+ if (!host2.dist) return null;
15049
+ try {
15050
+ return await host2.dist.registryOrigin(true);
15051
+ } catch {
15052
+ return null;
15053
+ }
15054
+ }
15055
+ function runNpm(host2, opts) {
15056
+ const a = opts && opts.action || "install";
15057
+ const verb = a === "uninstall" ? "\u5378\u8F7D" : "\u5B89\u88C5";
15058
+ if (!host2.dist) return Promise.resolve({ ok: false, error: "dist \u5206\u53D1\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5" + verb, output: [] });
15059
+ const tpl = a === "install" ? host2.config.installCommandTemplate : null;
15060
+ return host2.dist.runNpmInstall({
15061
+ action: a,
15062
+ pkg: host2.config.packageName || "@deepseek-ai/dsh",
15063
+ version: opts && opts.version,
15064
+ // 前缀单源:装与卸必须落在同一个全局前缀,否则「卸载成功」而包还在原地。
15065
+ prefix: host2.npmRoot || null,
15066
+ registry: opts && opts.registry,
15067
+ // 只在显式注入过 npm 形态时接管启动形态;生产留空,由执行器按运行期契约解析一次。
15068
+ launcher: host2._npmBin ? npmLaunch(host2) : null,
15069
+ commandTemplate: Array.isArray(tpl) && tpl.length ? tpl : null,
15070
+ timeoutMs: a === "uninstall" ? host2.config.uninstallTimeoutMs : host2.config.upgradeTimeoutMs,
15071
+ onLine: (l) => {
15072
+ host2._appendUpgradeLog(l);
15073
+ if (host2.installing) host2._appendInstallLog(l);
15074
+ }
15075
+ });
15076
+ }
15077
+ module2.exports = { npmLaunch, resolveNpmRoot, checkEnvironment, latestVersion, selectRegistry, runNpm };
15078
+ }
15079
+ });
15080
+
14364
15081
  // src/domains/shell/journal.js
14365
15082
  var require_journal = __commonJS({
14366
15083
  "src/domains/shell/journal.js"(exports2, module2) {
@@ -15013,6 +15730,9 @@ var require_core5 = __commonJS({
15013
15730
  var { extension: domainConfigExtension2 } = require_domain_config();
15014
15731
  var logcore = require_logcore();
15015
15732
  var { DshTokenService } = require_token();
15733
+ var { EnvCatalog } = require_env_catalog();
15734
+ var npm = require_npm();
15735
+ var FORM_NPM_ROOT_TIMEOUT_MS = 4e3;
15016
15736
  var { DistributionManager } = require_distribution();
15017
15737
  var shellDomain = require_shell();
15018
15738
  var { TaskRegistry } = require_tasks();
@@ -15023,6 +15743,68 @@ var require_core5 = __commonJS({
15023
15743
  host2.config = normalize(rawConfig, domainConfigExtension2());
15024
15744
  host2.configPath = typeof configPath === "string" ? configPath : null;
15025
15745
  platform.environment.bind({ preference: () => host2.config && host2.config.externalBrowser || null });
15746
+ platform.environment.registerSection("runtime", {
15747
+ label: "\u8FD0\u884C\u65F6\uFF08Node/npm/git/\u955C\u50CF\u6E90/\u5168\u5C40\u524D\u7F00\uFF09",
15748
+ probe: async () => {
15749
+ const cat = await new EnvCatalog(host2.config).probeAsync();
15750
+ const m = host2.nativeManager;
15751
+ const [reg, prefix] = await Promise.all([
15752
+ m && m.dist && typeof m.dist.registryInfo === "function" ? Promise.resolve(m.dist.registryInfo()).catch(() => null) : Promise.resolve(null),
15753
+ // 前缀实测就是安装/卸载用的那同一个解析口,只把超时收到表单预算内(默认 15s 会吃掉面板刷新)。
15754
+ m ? npm.resolveNpmRoot(m, { timeoutMs: FORM_NPM_ROOT_TIMEOUT_MS }).catch(() => null) : Promise.resolve(null)
15755
+ ]);
15756
+ return {
15757
+ node: cat.node,
15758
+ npm: cat.npm,
15759
+ git: cat.git,
15760
+ registry: reg ? {
15761
+ origin: reg.origin || null,
15762
+ mode: reg.mode || null,
15763
+ source: reg.source || null,
15764
+ manualOrigin: reg.manualOrigin || null,
15765
+ candidates: (reg.registries || []).map((r) => ({
15766
+ base: r.base,
15767
+ reachable: r.reachable === void 0 ? null : r.reachable,
15768
+ latencyMs: r.latencyMs === void 0 ? null : r.latencyMs,
15769
+ error: r.error || null
15770
+ }))
15771
+ } : null,
15772
+ // 全局前缀只来自 `npm root -g` 的实测,拿不到就是 null:装/卸落在哪个前缀是排障第一问,
15773
+ // 猜一个只会指错地方(壳侧的同名读数在 E4 上报接通后进这一维)。
15774
+ prefix: prefix || null
15775
+ };
15776
+ }
15777
+ });
15778
+ platform.environment.registerSection("dsh", {
15779
+ label: "DSH \u672C\u4F53\u4E0E\u5185\u6838\u66F4\u65B0",
15780
+ probe: async () => {
15781
+ const cat = new EnvCatalog(host2.config);
15782
+ const d = typeof host2.dshenvStatus === "function" ? host2.dshenvStatus() : null;
15783
+ if (!d) return null;
15784
+ return {
15785
+ dsh: cat.dshEntry(d.binOk, d.installed, d.bin),
15786
+ selfUpdate: cat.selfUpdateEntry(),
15787
+ managed: d.managed === true,
15788
+ phase: d.phase || null
15789
+ };
15790
+ }
15791
+ });
15792
+ platform.environment.registerSection("startup", {
15793
+ label: "\u542F\u52A8\u65E2\u6210\u4E8B\u5B9E\uFF08\u5B88\u536B\u8FD9\u4E00\u62CD\u8DD1\u8FC7\u4EC0\u4E48\uFF09",
15794
+ probe: () => {
15795
+ const s = host2._startupFacts;
15796
+ if (!s) return null;
15797
+ return {
15798
+ bootAt: s.bootAt,
15799
+ envDelayMs: s.envDelayMs,
15800
+ routerAutostart: s.routerAutostart,
15801
+ routerMode: s.routerMode,
15802
+ updateCheck: s.updateCheck,
15803
+ shellWatchdog: s.shellWatchdog,
15804
+ lastRefresh: s.lastRefresh
15805
+ };
15806
+ }
15807
+ });
15026
15808
  host2._fileProtectStatus = null;
15027
15809
  try {
15028
15810
  const fp = platform.fileProtect;
@@ -18777,7 +19559,12 @@ var require_oauth = __commonJS({
18777
19559
  waitMs: 18e4,
18778
19560
  opened: true,
18779
19561
  // 非隔离引擎(Safari/打包器包装)由平台层降级:账号隔离不成立,换账号只能靠超时重发或手动窗口。
18780
- isolated: ev.isolated === true
19562
+ isolated: ev.isolated === true,
19563
+ // 为什么隔离成立/不成立必须一起交出:面板上一句「未隔离」既没说原因也没法处置。
19564
+ // basis 来自环境表单的出网条件维度(cold-profile-blocked 即冷档案注定空白),
19565
+ // 与「引擎不支持隔离」是两种不同原因,分开才谈得上分开修(配代理 vs 换浏览器)。
19566
+ isolatedBasis: ev.egress ? ev.egress.basis : ev.isolated === true ? "isolated" : "engine-not-isolatable",
19567
+ isolatedDetail: ev.egress ? ev.egress.detail || null : null
18781
19568
  });
18782
19569
  }
18783
19570
  async function commandcodeLoginWait(timeoutMs) {
@@ -24138,7 +24925,7 @@ var require_heartbeat = __commonJS({
24138
24925
  });
24139
24926
 
24140
24927
  // src/app/control/registry.js
24141
- var require_registry3 = __commonJS({
24928
+ var require_registry4 = __commonJS({
24142
24929
  "src/app/control/registry.js"(exports2, module2) {
24143
24930
  "use strict";
24144
24931
  var fs2 = require("node:fs");
@@ -24709,77 +25496,6 @@ var require_manifest = __commonJS({
24709
25496
  }
24710
25497
  });
24711
25498
 
24712
- // src/app/native/npm.js
24713
- var require_npm = __commonJS({
24714
- "src/app/native/npm.js"(exports2, module2) {
24715
- "use strict";
24716
- var ex2 = require_exec();
24717
- var runtimeContract = require_runtime();
24718
- function npmLaunch(host2) {
24719
- const h = host2 || {};
24720
- if (h._npmBin) {
24721
- return { program: h._npmBin, args: Array.isArray(h._npmBinArgs) ? h._npmBinArgs.slice() : [] };
24722
- }
24723
- const l = runtimeContract.npmLauncher();
24724
- return { program: l.program, args: l.args };
24725
- }
24726
- async function resolveNpmRoot(host2) {
24727
- if (host2.npmRoot) return host2.npmRoot;
24728
- const l = npmLaunch(host2);
24729
- const r = await ex2.runOutAsync(l.program, l.args.concat(["root", "-g"]));
24730
- return r ? r.trim() : null;
24731
- }
24732
- async function checkEnvironment(host2) {
24733
- const errors = [];
24734
- const l = npmLaunch(host2);
24735
- const [nv, npmv, npmRoot] = await Promise.all([
24736
- ex2.runOutAsync("node", ["--version"]),
24737
- ex2.runOutAsync(l.program, l.args.concat(["--version"])),
24738
- resolveNpmRoot(host2)
24739
- ]);
24740
- if (!nv || !nv.trim()) errors.push("node \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
24741
- if (!npmv || !npmv.trim()) errors.push("npm \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
24742
- return { ok: errors.length === 0, errors, npmRoot };
24743
- }
24744
- async function latestVersion(host2) {
24745
- 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");
24746
- const channel = host2.config.releaseChannel || "npm";
24747
- return host2.dist.fetchVersionInfo(host2.config.packageName, channel);
24748
- }
24749
- async function selectRegistry(host2) {
24750
- if (!host2.dist) return null;
24751
- try {
24752
- return await host2.dist.registryOrigin(true);
24753
- } catch {
24754
- return null;
24755
- }
24756
- }
24757
- function runNpm(host2, opts) {
24758
- const a = opts && opts.action || "install";
24759
- const verb = a === "uninstall" ? "\u5378\u8F7D" : "\u5B89\u88C5";
24760
- if (!host2.dist) return Promise.resolve({ ok: false, error: "dist \u5206\u53D1\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5" + verb, output: [] });
24761
- const tpl = a === "install" ? host2.config.installCommandTemplate : null;
24762
- return host2.dist.runNpmInstall({
24763
- action: a,
24764
- pkg: host2.config.packageName || "@deepseek-ai/dsh",
24765
- version: opts && opts.version,
24766
- // 前缀单源:装与卸必须落在同一个全局前缀,否则「卸载成功」而包还在原地。
24767
- prefix: host2.npmRoot || null,
24768
- registry: opts && opts.registry,
24769
- // 只在显式注入过 npm 形态时接管启动形态;生产留空,由执行器按运行期契约解析一次。
24770
- launcher: host2._npmBin ? npmLaunch(host2) : null,
24771
- commandTemplate: Array.isArray(tpl) && tpl.length ? tpl : null,
24772
- timeoutMs: a === "uninstall" ? host2.config.uninstallTimeoutMs : host2.config.upgradeTimeoutMs,
24773
- onLine: (l) => {
24774
- host2._appendUpgradeLog(l);
24775
- if (host2.installing) host2._appendInstallLog(l);
24776
- }
24777
- });
24778
- }
24779
- module2.exports = { npmLaunch, resolveNpmRoot, checkEnvironment, latestVersion, selectRegistry, runNpm };
24780
- }
24781
- });
24782
-
24783
25499
  // src/app/native/ops.js
24784
25500
  var require_ops4 = __commonJS({
24785
25501
  "src/app/native/ops.js"(exports2, module2) {
@@ -25424,7 +26140,7 @@ var require_installer = __commonJS({
25424
26140
  var require_entry = __commonJS({
25425
26141
  "src/app/control/entry.js"(exports2, module2) {
25426
26142
  "use strict";
25427
- var { PHASES } = require_registry3();
26143
+ var { PHASES } = require_registry4();
25428
26144
  var ManagedLifecycle = class {
25429
26145
  constructor(opts) {
25430
26146
  this.id = opts.id || "lc-" + Math.random().toString(36).slice(2, 8);
@@ -25678,7 +26394,7 @@ var require_domains = __commonJS({
25678
26394
  var { InstanceManager } = require_instance();
25679
26395
  var { PluginMarket } = require_market();
25680
26396
  var { PluginManager } = require_plugin();
25681
- var { ManagedRegistry } = require_registry3();
26397
+ var { ManagedRegistry } = require_registry4();
25682
26398
  var { guardVersion } = require_version();
25683
26399
  var { Lifecycle } = require_lifecycle2();
25684
26400
  var { Health } = require_health();
@@ -25805,7 +26521,7 @@ var require_adapters = __commonJS({
25805
26521
  "src/app/control/adapters.js"(exports2, module2) {
25806
26522
  "use strict";
25807
26523
  var { ManagedLifecycle } = require_entry();
25808
- var { kindMeta } = require_registry3();
26524
+ var { kindMeta } = require_registry4();
25809
26525
  function capsOf(objectKind) {
25810
26526
  const m = kindMeta(objectKind);
25811
26527
  if (!m) return { startable: true, guardable: true };
@@ -26304,7 +27020,7 @@ var require_guard = __commonJS({
26304
27020
  var fs2 = require("node:fs");
26305
27021
  var path2 = require("node:path");
26306
27022
  function owns(pathname) {
26307
- return pathname === "/changelog" || pathname.startsWith("/guard/") || pathname === "/autostart" || pathname.startsWith("/settings/") || pathname.startsWith("/self-update/") || pathname === "/env/dsh" || pathname === "/env/status" || pathname === "/env/node-lts" || pathname === "/env/open-url" || pathname === "/env/environment" || pathname === "/ports" || pathname === "/shutdown";
27023
+ return pathname === "/changelog" || pathname.startsWith("/guard/") || pathname === "/autostart" || pathname.startsWith("/settings/") || pathname.startsWith("/self-update/") || pathname === "/env/dsh" || pathname === "/env/status" || pathname === "/env/node-lts" || pathname === "/env/open-url" || pathname === "/env/environment" || pathname === "/env/environment/last" || pathname === "/ports" || pathname === "/shutdown";
26308
27024
  }
26309
27025
  function fetchDshChangelog(res, sup) {
26310
27026
  const v = sup && sup.nativeManager ? sup.nativeManager.versionInfo() : {};
@@ -26511,7 +27227,14 @@ var require_guard = __commonJS({
26511
27227
  return send(403, { ok: false, error: "cross-origin request rejected" });
26512
27228
  }
26513
27229
  const force = /[?&]force=1/.test(String(req.url || ""));
26514
- return Promise.resolve(environment.form({ force, persist: force })).then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: "\u73AF\u5883\u8868\u5355\u88C5\u914D\u5931\u8D25\uFF1A" + (e && e.message || e) }));
27230
+ return Promise.resolve(force ? environment.refresh({ persist: true, force: true }) : environment.form()).then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: "\u73AF\u5883\u8868\u5355\u88C5\u914D\u5931\u8D25\uFF1A" + (e && e.message || e) }));
27231
+ }
27232
+ if (req.method === "GET" && pathname === "/env/environment/last") {
27233
+ if (!originAllowed(req, sup.config.apiPort)) {
27234
+ req.resume();
27235
+ return send(403, { ok: false, error: "cross-origin request rejected" });
27236
+ }
27237
+ return send(200, environment.lastSnapshot());
26515
27238
  }
26516
27239
  if (req.method === "POST" && pathname === "/env/open-url") {
26517
27240
  if (!identity.loopback) {