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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/core.cjs +1216 -389
  2. package/package.json +1 -1
  3. package/ui-react/assets/{InstancesPage-DadqCqud.js → InstancesPage-DhR8E6q2.js} +1 -1
  4. package/ui-react/assets/{LanPage-DWzQ-VLz.js → LanPage-BymkZ5Lp.js} +1 -1
  5. package/ui-react/assets/OverviewPage-BwAeZq3_.js +2 -0
  6. package/ui-react/assets/{PluginsPage-BwLIhj7E.js → PluginsPage-D9nLksEp.js} +1 -1
  7. package/ui-react/assets/{RouterPage-BeMIxdSL.js → RouterPage-B_dPmJbu.js} +1 -1
  8. package/ui-react/assets/{SettingsPage-R7lkiQwq.js → SettingsPage-C-iKRaHC.js} +1 -1
  9. package/ui-react/assets/{TasksPage-A3SFro8F.js → TasksPage-CceWxOiG.js} +1 -1
  10. package/ui-react/assets/openExternal-RPwenHBw.js +1 -0
  11. package/ui-react/assets/{refresh-cw-B-VAKpDt.js → refresh-cw-qHR0pOo6.js} +1 -1
  12. package/ui-react/assets/{rocket-CLAmwatt.js → rocket-qAs1Npqn.js} +1 -1
  13. package/ui-react/assets/{shield-check-CMRwmPpk.js → shield-check-DdnUZAa0.js} +1 -1
  14. package/ui-react/assets/{supervisor-Btn_MIEc.js → supervisor-Af5v0BTw.js} +3 -3
  15. package/ui-react/assets/supervisor-C1zJELaq.css +2 -0
  16. package/ui-react/assets/{supervisor-BbSavtNN.js → supervisor-CWSt_-Ws.js} +1 -1
  17. package/ui-react/assets/{useSupervisorAction-q0YJmhui.js → useSupervisorAction-CeZGHqFZ.js} +1 -1
  18. package/ui-react/assets/{widgets-BbRkZm-Z.js → widgets-BhlrHuez.js} +1 -1
  19. package/ui-react/supervisor.html +3 -3
  20. package/ui-react/assets/OverviewPage-D9y-GeR2.js +0 -2
  21. package/ui-react/assets/openExternal-DOkxrLUq.js +0 -1
  22. package/ui-react/assets/supervisor-YWIsBeZU.css +0 -2
package/core.cjs CHANGED
@@ -833,7 +833,11 @@ var require_config = __commonJS({
833
833
  // 不配置 = LAN 受 RFC1918 白名单约束,FRP 暴露仍强制 remoteToken。
834
834
  apiAccessKey: null,
835
835
  // 关闭窗口行为(系统级):'hide' = 隐藏至托盘、服务常驻(默认);'exit' = 退出管家并停全部服务链。
836
- closeAction: "hide"
836
+ closeAction: "hide",
837
+ // 外部打开的浏览器偏好(系统级,本机配置):存环境表单候选清单里的 id,null = 未选。
838
+ // 与「系统默认浏览器」分两个字段是刻意的:前者是用户在本产品里的选择,后者是操作系统的事实;
839
+ // 系统说不出默认时按候选次序分发并如实标注,用户可在这里定一次覆盖它(见 platform/os/environment.js)。
840
+ externalBrowser: null
837
841
  };
838
842
  function buildDefaults2(ext) {
839
843
  const pending = normalizeExtension(ext).defaults.slice();
@@ -947,7 +951,7 @@ var require_version = __commonJS({
947
951
  var fs2 = require("node:fs");
948
952
  var path2 = require("node:path");
949
953
  function guardVersion() {
950
- if (true) return String("0.1.6-BETA.12");
954
+ if (true) return String("0.1.6-BETA.14");
951
955
  try {
952
956
  return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
953
957
  } catch {
@@ -1080,6 +1084,7 @@ var require_fs = __commonJS({
1080
1084
  "src/platform/util/fs.js"(exports2, module2) {
1081
1085
  "use strict";
1082
1086
  var fs2 = require("node:fs");
1087
+ var os2 = require("node:os");
1083
1088
  var path2 = require("node:path");
1084
1089
  function dirSizeBytes(root) {
1085
1090
  let total = 0;
@@ -1140,7 +1145,26 @@ var require_fs = __commonJS({
1140
1145
  throw e;
1141
1146
  }
1142
1147
  }
1143
- module2.exports = { dirSizeBytes, writeAtomic };
1148
+ function removeTreeDeferred(dir, ms) {
1149
+ if (!dir) return null;
1150
+ const t = setTimeout(() => {
1151
+ try {
1152
+ fs2.rmSync(dir, { recursive: true, force: true });
1153
+ } catch {
1154
+ }
1155
+ }, ms === void 0 ? 6e4 : ms);
1156
+ if (t.unref) t.unref();
1157
+ return t;
1158
+ }
1159
+ function allocTempDir(prefix) {
1160
+ const dir = fs2.mkdtempSync(path2.join(os2.tmpdir(), prefix || "dsh-"));
1161
+ try {
1162
+ fs2.chmodSync(dir, 448);
1163
+ } catch {
1164
+ }
1165
+ return dir;
1166
+ }
1167
+ module2.exports = { dirSizeBytes, writeAtomic, removeTreeDeferred, allocTempDir };
1144
1168
  }
1145
1169
  });
1146
1170
 
@@ -4639,23 +4663,66 @@ var require_desktop = __commonJS({
4639
4663
  "src/platform/os/desktop.js"(exports2, module2) {
4640
4664
  "use strict";
4641
4665
  var fs2 = require("node:fs");
4666
+ var os2 = require("node:os");
4667
+ var path2 = require("node:path");
4642
4668
  var PLATFORM = process.platform;
4643
- function hasX11Socket() {
4669
+ var X11_DIR = "/tmp/.X11-unix";
4670
+ function deps(o) {
4671
+ const ov = o || {};
4672
+ return {
4673
+ env: ov.env || process.env,
4674
+ readdir: ov.readdir || ((p) => fs2.readdirSync(p)),
4675
+ exists: ov.exists || ((p) => fs2.existsSync(p)),
4676
+ home: ov.home || os2.homedir(),
4677
+ uid: typeof process.getuid === "function" ? process.getuid() : 0
4678
+ };
4679
+ }
4680
+ function runtimeDir(env, uid) {
4681
+ return env.XDG_RUNTIME_DIR || "/run/user/" + uid;
4682
+ }
4683
+ function x11Displays(o) {
4684
+ const d = deps(o);
4644
4685
  try {
4645
- return fs2.readdirSync("/tmp/.X11-unix").some((f) => /^X\d+$/.test(f));
4686
+ return d.readdir(X11_DIR).filter((f) => /^X\d+$/.test(f)).map((f) => Number(f.slice(1))).sort((a, b) => a - b);
4646
4687
  } catch {
4647
- return false;
4688
+ return [];
4648
4689
  }
4649
4690
  }
4650
- function hasWaylandSocket() {
4651
- const uid = typeof process.getuid === "function" ? process.getuid() : 0;
4652
- const rt = process.env.XDG_RUNTIME_DIR || "/run/user/" + uid;
4691
+ function waylandSockets(o) {
4692
+ const d = deps(o);
4653
4693
  try {
4654
- return fs2.readdirSync(rt).some((f) => /^wayland-\d+$/.test(f));
4694
+ return d.readdir(runtimeDir(d.env, d.uid)).filter((f) => /^wayland-\d+$/.test(f)).sort();
4655
4695
  } catch {
4656
- return false;
4696
+ return [];
4657
4697
  }
4658
4698
  }
4699
+ function hasX11Socket(o) {
4700
+ return x11Displays(o).length > 0;
4701
+ }
4702
+ function hasWaylandSocket(o) {
4703
+ return waylandSockets(o).length > 0;
4704
+ }
4705
+ function sessionEnv(o) {
4706
+ const d = deps(o);
4707
+ const out = {};
4708
+ if (PLATFORM !== "linux") return out;
4709
+ if (!d.env.DISPLAY && !d.env.WAYLAND_DISPLAY) {
4710
+ const xs = x11Displays(d);
4711
+ if (xs.length) {
4712
+ out.DISPLAY = ":" + xs[0];
4713
+ const xauth = path2.join(d.home, ".Xauthority");
4714
+ if (d.exists(xauth)) out.XAUTHORITY = xauth;
4715
+ } else {
4716
+ const wl = waylandSockets(d);
4717
+ if (wl.length) out.WAYLAND_DISPLAY = wl[0];
4718
+ }
4719
+ }
4720
+ if (!d.env.DBUS_SESSION_BUS_ADDRESS) {
4721
+ const bus = path2.join(runtimeDir(d.env, d.uid), "bus");
4722
+ if (d.exists(bus)) out.DBUS_SESSION_BUS_ADDRESS = "unix:path=" + bus;
4723
+ }
4724
+ return out;
4725
+ }
4659
4726
  function sessionAvailable() {
4660
4727
  if (PLATFORM === "linux") {
4661
4728
  if (process.env.DISPLAY || process.env.WAYLAND_DISPLAY) return true;
@@ -4682,7 +4749,7 @@ var require_desktop = __commonJS({
4682
4749
  reason: "session-scoped-by-launcher"
4683
4750
  };
4684
4751
  }
4685
- module2.exports = { sessionAvailable, describe, PLATFORM };
4752
+ module2.exports = { sessionAvailable, sessionEnv, describe, x11Displays, waylandSockets, PLATFORM };
4686
4753
  }
4687
4754
  });
4688
4755
 
@@ -4755,8 +4822,9 @@ var require_capability_profile = __commonJS({
4755
4822
  autostart: true,
4756
4823
  // 期望 schtasks(实测覆写)
4757
4824
  frpExpose: true,
4758
- // 可打开,但 win32 的两种启动形态(explorer.exe 调度器 / 裸 URL 直启解析到的浏览器)退出码都不携带
4759
- // 窗口是否出现的信息,故每次调用最多到 handedOff(见 browser.js#ownsItsWindow)。能力位只声明「能交出去」。
4825
+ // 可打开,但 Windows 没有可信调度器(系统调度器只有 darwin 的 open 与 linux 的 xdg-open),
4826
+ // 只能直启探测层解析出的浏览器本体;浏览器已在运行时本次进程只转交地址,退出码与窗口出现与否无关,
4827
+ // 故该平台每次调用最多到 handedOff(判据见 browser.js#ownsItsWindow)。能力位只声明「能交出去」。
4760
4828
  openBrowser: true,
4761
4829
  hostService: "windows-service",
4762
4830
  guardAutostart: true,
@@ -4787,6 +4855,784 @@ var require_capability_profile = __commonJS({
4787
4855
  }
4788
4856
  });
4789
4857
 
4858
+ // src/platform/os/browser-inventory.js
4859
+ var require_browser_inventory = __commonJS({
4860
+ "src/platform/os/browser-inventory.js"(exports2, module2) {
4861
+ "use strict";
4862
+ var fs2 = require("node:fs");
4863
+ var path2 = require("node:path");
4864
+ var os2 = require("node:os");
4865
+ var exec = require_exec();
4866
+ var { isExecutableFile } = require_exec_path();
4867
+ var PROBE_TIMEOUT_MS = 1500;
4868
+ function engineOf(bin) {
4869
+ const base = String(bin || "").toLowerCase().split(/[\\/]/).pop();
4870
+ if (/(chrome|chromium|msedge|edge|brave|vivaldi|opera|thorium)/.test(base)) return "chromium";
4871
+ if (/(firefox|librewolf|waterfox)/.test(base)) return "firefox";
4872
+ return "other";
4873
+ }
4874
+ function tokenizeExec(line) {
4875
+ const toks = [];
4876
+ let cur = "";
4877
+ let q = null;
4878
+ let esc = false;
4879
+ let has = false;
4880
+ for (const ch of String(line)) {
4881
+ if (esc) {
4882
+ cur += ch;
4883
+ esc = false;
4884
+ continue;
4885
+ }
4886
+ if (ch === "\\") {
4887
+ esc = true;
4888
+ has = true;
4889
+ continue;
4890
+ }
4891
+ if (q) {
4892
+ if (ch === q) q = null;
4893
+ else cur += ch;
4894
+ continue;
4895
+ }
4896
+ if (ch === "'" || ch === '"') {
4897
+ q = ch;
4898
+ has = true;
4899
+ continue;
4900
+ }
4901
+ if (/\s/.test(ch)) {
4902
+ if (has) {
4903
+ toks.push(cur);
4904
+ cur = "";
4905
+ has = false;
4906
+ }
4907
+ continue;
4908
+ }
4909
+ cur += ch;
4910
+ has = true;
4911
+ }
4912
+ if (has) toks.push(cur);
4913
+ return toks;
4914
+ }
4915
+ function parseExecLine(line) {
4916
+ let toks = tokenizeExec(line).filter((t) => t !== "%%" && !/^%[a-zA-Z]$/.test(t));
4917
+ if (toks[0] === "env") {
4918
+ let i = 1;
4919
+ while (i < toks.length && /=/.test(toks[i])) i++;
4920
+ toks = toks.slice(i);
4921
+ }
4922
+ if (!toks.length) return null;
4923
+ return { bin: toks[0], baseArgs: toks.slice(1) };
4924
+ }
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
+ function exeFromCmdLine(cmdLine) {
4935
+ const s = String(cmdLine || "");
4936
+ let m = s.match(/^\s*"([^"]+\.exe)"/i);
4937
+ if (m) return m[1];
4938
+ m = s.match(/^\s*(.*?\.exe)/i);
4939
+ return m ? m[1] : null;
4940
+ }
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
+ var WIN_APP_PATHS = ["msedge.exe", "chrome.exe", "firefox.exe", "brave.exe", "opera.exe", "vivaldi.exe", "chromium.exe", "thorium.exe", "librewolf.exe"];
4977
+ function winExeOfProgId(runner, note, progId, env) {
4978
+ if (!safeRegKeyPart(progId)) return null;
4979
+ for (const root of ["HKCU\\Software\\Classes", "HKLM\\Software\\Classes"]) {
4980
+ const cmd2 = regValue(runner, note, root + "\\" + progId + "\\shell\\open\\command");
4981
+ const exe = cmd2 ? exeFromCmdLine(expandEnvVars(cmd2, env)) : null;
4982
+ if (exe) return exe;
4983
+ }
4984
+ return null;
4985
+ }
4986
+ function probeWin(d) {
4987
+ const { runOut, env, canExec } = d;
4988
+ const notes = [];
4989
+ const note = (source, detail) => {
4990
+ notes.push({ source, detail });
4991
+ };
4992
+ const found = /* @__PURE__ */ new Map();
4993
+ const keyOf = (exe) => String(exe || "").toLowerCase().replace(/[\\/]/g, "\\");
4994
+ const push = (exe, how) => {
4995
+ const full = expandEnvVars(exe, env).trim();
4996
+ if (!/\.exe$/i.test(full)) {
4997
+ note(how, "\u6765\u6E90\u7ED9\u51FA\u7684\u4E0D\u662F exe \u8DEF\u5F84: " + full);
4998
+ return null;
4999
+ }
5000
+ const k = keyOf(full);
5001
+ const hit = found.get(k);
5002
+ if (hit) {
5003
+ if (!hit.sources.includes(how)) hit.sources.push(how);
5004
+ return hit;
5005
+ }
5006
+ if (!canExec(full)) {
5007
+ note(how, "\u6307\u5411\u7684\u6587\u4EF6\u4E0D\u53EF\u6267\u884C: " + full);
5008
+ return null;
5009
+ }
5010
+ const item = { id: k, name: path2.basename(full).replace(/\.exe$/i, ""), engine: engineOf(full), bin: full, source: how, sources: [how] };
5011
+ found.set(k, item);
5012
+ note(how, item.name);
5013
+ return item;
5014
+ };
5015
+ const progIds = /* @__PURE__ */ new Map();
5016
+ const addProgId = (pid, how) => {
5017
+ const s = String(pid || "").trim();
5018
+ if (!safeRegKeyPart(s)) return;
5019
+ if (progIds.get(s) === "userchoice") return;
5020
+ progIds.set(s, how);
5021
+ };
5022
+ let defaultId = null;
5023
+ let defaultSource = null;
5024
+ const DEF_RANK = { userchoice: 1, "scheme-association": 2, "only-installed": 9 };
5025
+ let defRank = 99;
5026
+ const setDefault = (item, how) => {
5027
+ const r = DEF_RANK[how] === void 0 ? 99 : DEF_RANK[how];
5028
+ if (item && r < defRank) {
5029
+ defRank = r;
5030
+ defaultId = item.id;
5031
+ defaultSource = how;
5032
+ }
5033
+ };
5034
+ const cmdToExe = (raw) => raw ? exeFromCmdLine(expandEnvVars(raw, env)) : null;
5035
+ const uc = regValue(runOut, note, "HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice", "ProgId");
5036
+ if (uc) addProgId(uc, "userchoice");
5037
+ for (const root of ["HKCU\\Software\\Classes", "HKLM\\Software\\Classes"]) {
5038
+ const pid = regValue(runOut, note, root + "\\https");
5039
+ if (pid) addProgId(pid, "scheme-association");
5040
+ const exe = cmdToExe(regValue(runOut, note, root + "\\https\\shell\\open\\command"));
5041
+ if (exe) setDefault(push(exe, "scheme-association"), "scheme-association");
5042
+ }
5043
+ for (const name of regSubkeys(runOut, note, "HKLM\\SOFTWARE\\Clients\\StartMenuInternet")) {
5044
+ addProgId(name, "startmenu-catalog");
5045
+ const exe = cmdToExe(regValue(runOut, note, "HKLM\\SOFTWARE\\Clients\\StartMenuInternet\\" + name + "\\shell\\open\\command")) || winExeOfProgId(runOut, note, name, env);
5046
+ if (exe) push(exe, "startmenu-catalog");
5047
+ }
5048
+ for (const root of ["HKLM\\SOFTWARE", "HKCU\\SOFTWARE"]) {
5049
+ for (const capPath of regValueTargets(runOut, note, root + "\\RegisteredApplications")) {
5050
+ const https = regValue(runOut, note, capPath + "\\UrlAssociations\\https");
5051
+ const sid = regValue(runOut, note, capPath, "StartMenuInternet");
5052
+ const pid = https || sid;
5053
+ if (pid) addProgId(pid, "registered-apps");
5054
+ }
5055
+ }
5056
+ for (const exe of WIN_APP_PATHS) {
5057
+ for (const root of ["HKCU", "HKLM"]) {
5058
+ const v = regValue(runOut, note, root + "\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + exe);
5059
+ if (v && push(v, "app-paths")) break;
5060
+ }
5061
+ }
5062
+ for (const [pid, how] of progIds) {
5063
+ const exe = winExeOfProgId(runOut, note, pid, env);
5064
+ if (!exe) continue;
5065
+ const item = push(exe, how);
5066
+ if (how === "userchoice") setDefault(item, "userchoice");
5067
+ }
5068
+ if (found.size === 1) setDefault([...found.values()][0], "only-installed");
5069
+ return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
5070
+ }
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
+ var MAC_JXA_LIST = [
5090
+ "ObjC.import('Foundation');",
5091
+ "var ws=$.NSWorkspace.sharedWorkspace;",
5092
+ "var u=$.NSURL.URLWithString('https://dsh.local/probe');",
5093
+ "var out=[];var def='';",
5094
+ "try{var d=ws.URLForApplicationToOpenURL(u); if(!d.isNil()) def=ObjC.unwrap(d.path)||'';}catch(e){}",
5095
+ "var list=null;",
5096
+ "try{list=ws.urlsForApplicationsToOpenURL(u);}catch(e){}",
5097
+ "if(list===null||typeof list.count!=='number'){if(def){out.push(def+'\\t\\t1');}}else{",
5098
+ "var n=Number(list.count);",
5099
+ "for(var i=0;i<n;i++){",
5100
+ "var p=ObjC.unwrap(list.objectAtIndex(i).path)||''; if(!p) continue;",
5101
+ "var exe='';var bid='';",
5102
+ "var b=$.NSBundle.bundleWithPath(p);",
5103
+ "if(!b.isNil()){exe=ObjC.unwrap(b.executablePath)||'';bid=ObjC.unwrap(b.bundleIdentifier)||'';}",
5104
+ "out.push((exe||p)+'\\t'+bid+'\\t'+(p&&def&&p===def?1:0));}",
5105
+ "}",
5106
+ // 末句必须是裸表达式:osascript 取的是最后一条**语句**的值,if/else 语句不产结果,
5107
+ // 写在分支里会让整段查询输出空字符串(表现为「系统未报任何可用应用」)。
5108
+ "out.join('\\n');"
5109
+ ].join("");
5110
+ function probeMac(d) {
5111
+ const { runOut, canExec } = d;
5112
+ const notes = [];
5113
+ const note = (source, detail) => {
5114
+ notes.push({ source, detail });
5115
+ };
5116
+ const out = runOut("osascript", ["-l", "JavaScript", "-e", MAC_JXA_LIST]);
5117
+ if (!out || !out.trim()) {
5118
+ note("launchservices", "\u67E5\u8BE2\u65E0\u8F93\u51FA\uFF08\u65E7\u7CFB\u7EDF\u6216\u88AB\u6743\u9650\u62E6\u622A\uFF09");
5119
+ return { browsers: [], defaultId: null, defaultSource: null, probed: notes };
5120
+ }
5121
+ const keyOf = (exe) => String(exe || "").toLowerCase();
5122
+ const found = /* @__PURE__ */ new Map();
5123
+ let defaultId = null;
5124
+ let defaultSource = null;
5125
+ for (const line of out.trim().split("\n")) {
5126
+ const parts = line.split(" ");
5127
+ const bin = (parts[0] || "").trim();
5128
+ if (!bin) continue;
5129
+ if (!canExec(bin)) {
5130
+ note("launchservices", "\u4E0D\u53EF\u6267\u884C: " + bin);
5131
+ continue;
5132
+ }
5133
+ const k = keyOf(bin);
5134
+ if (!found.has(k)) found.set(k, { id: k, name: path2.basename(bin), engine: engineOf(bin), bin, source: "launchservices", sources: ["launchservices"], isDefault: false });
5135
+ if (parts[2] === "1") {
5136
+ found.get(k).isDefault = true;
5137
+ if (!defaultId) {
5138
+ defaultId = k;
5139
+ defaultSource = "launchservices";
5140
+ }
5141
+ }
5142
+ }
5143
+ note("launchservices", found.size ? found.size + " \u4E2A\u53EF\u6253\u5F00 https \u7684\u5E94\u7528" : "\u7CFB\u7EDF\u672A\u62A5\u4EFB\u4F55\u53EF\u7528\u5E94\u7528");
5144
+ if (!defaultId && found.size === 1) {
5145
+ defaultId = [...found.keys()][0];
5146
+ defaultSource = "only-installed";
5147
+ }
5148
+ return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
5149
+ }
5150
+ var PJ = path2.posix.join;
5151
+ function desktopDirs(env, home) {
5152
+ const dataHome = env.XDG_DATA_HOME || PJ(home, ".local", "share");
5153
+ const dataDirs = String(env.XDG_DATA_DIRS || "/usr/local/share:/usr/share").split(":").filter(Boolean);
5154
+ const dirs = [[dataHome, "xdg"], ...dataDirs.map((p) => [p, "xdg"])];
5155
+ dirs.push([PJ(home, ".local", "share"), "xdg"]);
5156
+ dirs.push(["/var/lib/flatpak/exports/share", "flatpak"]);
5157
+ dirs.push([PJ(home, ".local", "share", "flatpak", "exports", "share"), "flatpak"]);
5158
+ dirs.push(["/var/lib/snapd/desktop", "snap"]);
5159
+ const seen = [];
5160
+ return dirs.map(([root, kind]) => ({ dir: PJ(root, "applications"), kind })).filter((x) => {
5161
+ if (seen.includes(x.dir)) return false;
5162
+ seen.push(x.dir);
5163
+ return true;
5164
+ });
5165
+ }
5166
+ function browserFromDesktop(text, kind, file) {
5167
+ let inMain = false;
5168
+ const got = {};
5169
+ for (const line of String(text).split(/\r?\n/)) {
5170
+ if (/^\[Desktop Entry\]\s*$/.test(line)) {
5171
+ inMain = true;
5172
+ continue;
5173
+ }
5174
+ if (/^\[/.test(line)) break;
5175
+ if (!inMain) continue;
5176
+ const m = line.match(/^(Exec|Name|TryExec|Categories)=(.*)$/);
5177
+ if (!m) continue;
5178
+ if (m[1] === "Name" && got.Name) continue;
5179
+ got[m[1]] = m[2].trim();
5180
+ }
5181
+ if (!got.Exec) return null;
5182
+ const categories = String(got.Categories || "");
5183
+ const parsed = parseExecLine(got.Exec);
5184
+ if (!parsed) return null;
5185
+ const engine = engineOf(parsed.bin);
5186
+ const claimsBrowser = /(^|;)WebBrowser(;|$)/.test(categories);
5187
+ if (!claimsBrowser && engine === "other") return null;
5188
+ if (engine === "other") return null;
5189
+ return {
5190
+ // id 恒为归一后的可执行文件路径:defaultId 靠它在清单里定位条目,三平台同一契约
5191
+ id: parsed.bin.toLowerCase(),
5192
+ name: got.Name || path2.basename(file, ".desktop"),
5193
+ engine,
5194
+ bin: parsed.bin,
5195
+ baseArgs: parsed.baseArgs,
5196
+ source: kind,
5197
+ desktopFile: file
5198
+ };
5199
+ }
5200
+ function resolveLinuxBin(bin, canExec, env) {
5201
+ const b = String(bin || "").trim();
5202
+ if (!b) return null;
5203
+ if (b.startsWith("/")) return canExec(b) ? b : null;
5204
+ if (b.includes("/")) return null;
5205
+ for (const dir of String(env.PATH || "").split(":")) {
5206
+ if (!dir) continue;
5207
+ const p = PJ(dir.replace(/\/+$/, ""), b);
5208
+ if (canExec(p)) return p;
5209
+ }
5210
+ return null;
5211
+ }
5212
+ function probeLinux(d) {
5213
+ const { runOut, readFile, exists, listDir, canExec, env, home } = d;
5214
+ const notes = [];
5215
+ const note = (source, detail) => {
5216
+ notes.push({ source, detail });
5217
+ };
5218
+ const found = /* @__PURE__ */ new Map();
5219
+ const usable = (item) => {
5220
+ const resolved = resolveLinuxBin(item.bin, canExec, env);
5221
+ if (!resolved) {
5222
+ note(item.source, "\u4E0D\u53EF\u6267\u884C: " + item.bin);
5223
+ return null;
5224
+ }
5225
+ const k = resolved.toLowerCase();
5226
+ const hit = found.get(k);
5227
+ if (hit) {
5228
+ if (!hit.sources.includes(item.source)) hit.sources.push(item.source);
5229
+ return hit;
5230
+ }
5231
+ const v = Object.assign({}, item, { id: k, bin: resolved, sources: [item.source] });
5232
+ found.set(k, v);
5233
+ return v;
5234
+ };
5235
+ for (const { dir, kind } of desktopDirs(env, home)) {
5236
+ let names = [];
5237
+ try {
5238
+ names = listDir(dir).filter((f) => f.endsWith(".desktop"));
5239
+ } catch {
5240
+ continue;
5241
+ }
5242
+ for (const f of names) {
5243
+ let text = null;
5244
+ try {
5245
+ text = readFile(PJ(dir, f));
5246
+ } catch {
5247
+ continue;
5248
+ }
5249
+ const item = browserFromDesktop(text, kind, f);
5250
+ if (item) usable(item);
5251
+ }
5252
+ }
5253
+ let defaultId = null;
5254
+ let defaultSource = null;
5255
+ const fromMime = linuxDefaultFromMimeApps(readFile, exists, env, home, note);
5256
+ if (fromMime) {
5257
+ const item = usable(fromMime);
5258
+ if (item) {
5259
+ defaultId = item.id;
5260
+ defaultSource = "mimeapps";
5261
+ }
5262
+ }
5263
+ const id = runOut("xdg-settings", ["get", "default-web-browser"]);
5264
+ const desktopId = id && String(id).trim();
5265
+ if (!defaultId && desktopId && /^[A-Za-z0-9._-]+\.desktop$/.test(desktopId)) {
5266
+ for (const { dir } of desktopDirs(env, home)) {
5267
+ const p = PJ(dir, desktopId);
5268
+ let text = null;
5269
+ try {
5270
+ if (exists(p)) text = readFile(p);
5271
+ } catch {
5272
+ continue;
5273
+ }
5274
+ if (text === null) continue;
5275
+ const item = browserFromDesktop(text, "xdg-settings", desktopId);
5276
+ const used = item && usable(item);
5277
+ if (used) {
5278
+ defaultId = used.id;
5279
+ defaultSource = "xdg-settings";
5280
+ break;
5281
+ }
5282
+ }
5283
+ }
5284
+ if (!desktopId) note("xdg-settings", "\u65E0\u8F93\u51FA");
5285
+ else if (defaultSource === "xdg-settings") note("xdg-settings", "\u9ED8\u8BA4=" + defaultId);
5286
+ else note("xdg-settings", "\u62A5\u51FA " + desktopId + (defaultId ? "\uFF08\u9ED8\u8BA4\u5DF2\u7531 " + defaultSource + " \u5B9A\u51FA\uFF09" : " \u4F46\u4E0D\u53EF\u7528"));
5287
+ if (!defaultId && found.size === 1) {
5288
+ defaultId = [...found.keys()][0];
5289
+ defaultSource = "only-installed";
5290
+ }
5291
+ return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
5292
+ }
5293
+ function linuxDefaultFromMimeApps(readFile, exists, env, home, note) {
5294
+ const cfgHome = env.XDG_CONFIG_HOME || PJ(home, ".config");
5295
+ const cfgDirs = String(env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").filter(Boolean);
5296
+ const dataDirs = String(env.XDG_DATA_DIRS || "/usr/local/share:/usr/share").split(":").filter(Boolean);
5297
+ const files = [
5298
+ PJ(cfgHome, "mimeapps.list"),
5299
+ ...cfgDirs.map((d) => PJ(d, "mimeapps.list")),
5300
+ ...dataDirs.map((d) => PJ(d, "applications", "mimeapps.list"))
5301
+ ];
5302
+ for (const f of files) {
5303
+ if (!exists(f)) continue;
5304
+ let text = null;
5305
+ try {
5306
+ text = readFile(f);
5307
+ } catch {
5308
+ continue;
5309
+ }
5310
+ const id = mimeAppsDefault(text);
5311
+ if (!id) continue;
5312
+ note("mimeapps", path2.basename(f) + " -> " + id);
5313
+ for (const { dir, kind } of desktopDirs(env, home)) {
5314
+ const p = PJ(dir, id);
5315
+ if (!exists(p)) continue;
5316
+ let dt = null;
5317
+ try {
5318
+ dt = readFile(p);
5319
+ } catch {
5320
+ continue;
5321
+ }
5322
+ const item = browserFromDesktop(dt, kind, id);
5323
+ if (item) return item;
5324
+ }
5325
+ return null;
5326
+ }
5327
+ note("mimeapps", "\u65E0\u9ED8\u8BA4\u9879");
5328
+ return null;
5329
+ }
5330
+ function mimeAppsDefault(text) {
5331
+ let inDefault = false;
5332
+ for (const line of String(text || "").split(/\r?\n/)) {
5333
+ if (/^\s*\[/.test(line)) {
5334
+ inDefault = /^\s*\[Default Applications\]\s*$/.test(line);
5335
+ continue;
5336
+ }
5337
+ if (!inDefault) continue;
5338
+ const m = line.match(/^\s*x-scheme-handler\/https?\s*=\s*(.*)$/i);
5339
+ if (!m) continue;
5340
+ const ids = m[1].split(";").map((s) => s.trim()).filter((s) => /^[A-Za-z0-9._-]+\.desktop$/.test(s));
5341
+ if (ids.length) return ids[0];
5342
+ }
5343
+ return null;
5344
+ }
5345
+ function probe(platform, d) {
5346
+ const pl = platform || process.platform;
5347
+ const dd = Object.assign({}, d, { canExec: d.canExec || ((p) => isExecutableFile(p, pl)) });
5348
+ if (pl === "win32") return probeWin(dd);
5349
+ if (pl === "darwin") return probeMac(dd);
5350
+ return probeLinux(dd);
5351
+ }
5352
+ var CACHE = /* @__PURE__ */ Object.create(null);
5353
+ function inventory(platform, o) {
5354
+ const ov = o || {};
5355
+ const pl = platform || process.platform;
5356
+ const now = ov.now || (() => Date.now());
5357
+ const ttl = ov.ttlMs === void 0 ? 6e4 : ov.ttlMs;
5358
+ const cached = CACHE[pl];
5359
+ if (!ov.force && cached && now() - cached.at < ttl) return Object.assign({ cached: true }, cached.value);
5360
+ const runOut = ov.runOut || ((bin, args) => exec.runOut(bin, args, { timeoutMs: PROBE_TIMEOUT_MS }));
5361
+ const readFile = ov.readFile || ((p) => fs2.readFileSync(p, "utf8"));
5362
+ const exists = ov.exists || ((p) => fs2.existsSync(p));
5363
+ const listDir = ov.listDir || ((p) => fs2.readdirSync(p));
5364
+ let value;
5365
+ try {
5366
+ value = probe(pl, { runOut, readFile, exists, listDir, canExec: ov.canExec, env: ov.env || process.env, home: ov.home || os2.homedir() });
5367
+ } catch (e) {
5368
+ value = { browsers: [], defaultId: null, defaultSource: null, probed: [{ source: "probe-error", detail: String(e && e.message || e) }] };
5369
+ }
5370
+ value.at = now();
5371
+ value.platform = pl;
5372
+ value.browsers.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
5373
+ CACHE[pl] = { at: now(), value };
5374
+ return Object.assign({ cached: false }, value);
5375
+ }
5376
+ function invalidate(platform) {
5377
+ if (platform) delete CACHE[platform];
5378
+ else for (const k of Object.keys(CACHE)) delete CACHE[k];
5379
+ }
5380
+ module2.exports = {
5381
+ inventory,
5382
+ invalidate,
5383
+ probe,
5384
+ probeWin,
5385
+ probeMac,
5386
+ probeLinux,
5387
+ engineOf,
5388
+ tokenizeExec,
5389
+ parseExecLine,
5390
+ regValueOf,
5391
+ expandEnvVars,
5392
+ exeFromCmdLine,
5393
+ regSubkeys,
5394
+ regKeyFull,
5395
+ regValueTargets,
5396
+ safeRegKeyPart,
5397
+ browserFromDesktop,
5398
+ desktopDirs,
5399
+ resolveLinuxBin,
5400
+ mimeAppsDefault,
5401
+ linuxDefaultFromMimeApps,
5402
+ WIN_APP_PATHS,
5403
+ PROBE_TIMEOUT_MS
5404
+ };
5405
+ }
5406
+ });
5407
+
5408
+ // src/platform/os/environment.js
5409
+ var require_environment = __commonJS({
5410
+ "src/platform/os/environment.js"(exports2, module2) {
5411
+ "use strict";
5412
+ var fs2 = require("node:fs");
5413
+ var os2 = require("node:os");
5414
+ var path2 = require("node:path");
5415
+ var stateRoot2 = require_state_root();
5416
+ var { writeAtomic } = require_fs();
5417
+ var desktop = require_desktop();
5418
+ var detector = require_browser_inventory();
5419
+ var { engineOf } = detector;
5420
+ var SCHEMA = 1;
5421
+ var FILE_NAME = "environment.json";
5422
+ var FORM_TTL_MS = 6e4;
5423
+ var _sources = {};
5424
+ function bind(sources) {
5425
+ _sources = Object.assign({}, _sources, sources || {});
5426
+ return _sources;
5427
+ }
5428
+ function preferenceId() {
5429
+ const f = _sources.preference;
5430
+ if (typeof f !== "function") return null;
5431
+ const v = f();
5432
+ return typeof v === "string" && v.trim() ? v.trim() : null;
5433
+ }
5434
+ function capabilities() {
5435
+ const f = _sources.capabilities;
5436
+ if (typeof f !== "function") return null;
5437
+ return f() || null;
5438
+ }
5439
+ function identity() {
5440
+ let user = null;
5441
+ try {
5442
+ user = os2.userInfo().username;
5443
+ } catch {
5444
+ user = process.env.USER || process.env.USERNAME || null;
5445
+ }
5446
+ return {
5447
+ platform: process.platform,
5448
+ arch: process.arch,
5449
+ hostname: os2.hostname(),
5450
+ user: user || null,
5451
+ home: os2.homedir(),
5452
+ node: process.version
5453
+ };
5454
+ }
5455
+ function paths() {
5456
+ return { root: stateRoot2.root(), supervisor: stateRoot2.supervisorDir(), shell: stateRoot2.shellDir() };
5457
+ }
5458
+ function normalizeInventory(inv, platform) {
5459
+ const list = inv && Array.isArray(inv.browsers) ? inv.browsers : [];
5460
+ const defaultId = inv && inv.defaultId ? inv.defaultId : null;
5461
+ return {
5462
+ platform: inv && inv.platform || platform || process.platform,
5463
+ cached: !!(inv && inv.cached),
5464
+ at: inv && inv.at !== void 0 ? inv.at : null,
5465
+ defaultId,
5466
+ defaultSource: inv && inv.defaultSource || null,
5467
+ browsers: list.map((b) => ({
5468
+ id: b.id,
5469
+ name: b.name,
5470
+ bin: b.bin,
5471
+ engine: b.engine || engineOf(b.bin),
5472
+ baseArgs: b.baseArgs || [],
5473
+ sources: b.sources && b.sources.length ? b.sources : [b.source || "unknown"],
5474
+ isDefault: !!defaultId && b.id === defaultId
5475
+ })),
5476
+ probed: inv && inv.probed || []
5477
+ };
5478
+ }
5479
+ function browsers(o) {
5480
+ const ov = o || {};
5481
+ const pl = ov.platform || process.platform;
5482
+ if (ov.inventory) return normalizeInventory(ov.inventory, pl);
5483
+ const resolve = typeof ov.resolveInventory === "function" ? ov.resolveInventory : ((platform, deps) => detector.inventory(platform, deps));
5484
+ return normalizeInventory(resolve(pl, ov.resolveDeps || {}), pl);
5485
+ }
5486
+ var ENGINE_RANK = { chromium: 0, firefox: 1, other: 2 };
5487
+ function rankCandidates(list) {
5488
+ const rankOf = (b) => {
5489
+ const r = ENGINE_RANK[b && b.engine];
5490
+ return r === void 0 ? ENGINE_RANK.other : r;
5491
+ };
5492
+ return (Array.isArray(list) ? list : []).slice().sort((a, b) => {
5493
+ if (rankOf(a) !== rankOf(b)) return rankOf(a) - rankOf(b);
5494
+ const ia = String(a && a.id || "");
5495
+ const ib = String(b && b.id || "");
5496
+ return ia < ib ? -1 : ia > ib ? 1 : 0;
5497
+ });
5498
+ }
5499
+ function pickLauncher(platform, inv, preference) {
5500
+ const list = inv && Array.isArray(inv.browsers) ? inv.browsers : [];
5501
+ const byId = /* @__PURE__ */ new Map();
5502
+ for (const b of list) byId.set(b.id, b);
5503
+ const wanted = preference === void 0 ? preferenceId() : typeof preference === "string" && preference ? preference : null;
5504
+ const stale = wanted !== null && !byId.has(wanted);
5505
+ const out = (browser, how) => ({ browser, how, wanted, stale });
5506
+ if (wanted !== null && byId.has(wanted)) return out(byId.get(wanted), "user-preference");
5507
+ const sys = inv && inv.defaultId ? byId.get(inv.defaultId) : null;
5508
+ if (sys) return out(sys, inv.defaultSource || "default");
5509
+ if (list.length === 1) return out(list[0], "only-installed");
5510
+ if (list.length > 1) return out(rankCandidates(list)[0], "candidate-rank");
5511
+ return out(null, "none-found");
5512
+ }
5513
+ function snapshotPath() {
5514
+ return path2.join(stateRoot2.supervisorDir(), FILE_NAME);
5515
+ }
5516
+ function readSnapshot() {
5517
+ try {
5518
+ const doc = JSON.parse(fs2.readFileSync(snapshotPath(), "utf8"));
5519
+ return doc && doc.schema === SCHEMA ? doc : null;
5520
+ } catch {
5521
+ return null;
5522
+ }
5523
+ }
5524
+ var _formCache = null;
5525
+ function form(o) {
5526
+ const ov = o || {};
5527
+ const now = typeof ov.now === "function" ? ov.now : (() => Date.now());
5528
+ const stamp = now();
5529
+ const ttl = ov.ttlMs === void 0 ? FORM_TTL_MS : ov.ttlMs;
5530
+ if (!ov.force && _formCache && stamp - _formCache.at < ttl) {
5531
+ return Object.assign({}, _formCache.value, { cached: true });
5532
+ }
5533
+ const inv = browsers(ov);
5534
+ const caps = capabilities();
5535
+ const session = desktop.describe();
5536
+ const prefId = preferenceId();
5537
+ const prefEntry = prefId ? inv.browsers.find((b) => b.id === prefId) || null : null;
5538
+ const preference = {
5539
+ id: prefId,
5540
+ configured: !!prefId,
5541
+ matched: !!prefEntry,
5542
+ browser: prefEntry ? { id: prefEntry.id, name: prefEntry.name, engine: prefEntry.engine } : null,
5543
+ reason: !prefId ? "not-set" : prefEntry ? "matched" : "stale"
5544
+ };
5545
+ const pick = pickLauncher(inv.platform, inv, prefId);
5546
+ const probed = inv.probed.map((p) => ({ section: "browsers", source: p.source, detail: p.detail }));
5547
+ probed.push({ section: "session", source: "desktop", detail: session.reason + (session.available ? "\uFF08\u53EF\u7528\uFF09" : "\uFF08\u4E0D\u53EF\u7528\uFF09") });
5548
+ probed.push({
5549
+ section: "capabilities",
5550
+ source: "profile",
5551
+ detail: caps ? "openBrowser=" + (caps.openBrowser === true) : "\u672A\u7ED1\u5B9A\u80FD\u529B\u77E9\u9635\uFF08\u7531 platform/os/index.js \u88C5\u914D\u671F\u6CE8\u5165\uFF09"
5552
+ });
5553
+ probed.push({
5554
+ section: "preference",
5555
+ source: "config",
5556
+ detail: preference.reason === "not-set" ? "\u672A\u8BBE\u7F6E\uFF0C\u6309\u7CFB\u7EDF\u9ED8\u8BA4\u6216\u5019\u9009\u6B21\u5E8F\u5206\u53D1" : preference.reason === "matched" ? "\u5DF2\u9009 " + preference.browser.name : "\u504F\u597D\u6240\u6307\u5DF2\u4E0D\u5728\u5019\u9009\u6E05\u5355: " + prefId
5557
+ });
5558
+ probed.push({
5559
+ section: "pick",
5560
+ source: "form",
5561
+ 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
+ });
5563
+ const value = {
5564
+ schema: SCHEMA,
5565
+ at: stamp,
5566
+ platform: inv.platform,
5567
+ identity: identity(),
5568
+ paths: paths(),
5569
+ session,
5570
+ capabilities: caps,
5571
+ preference,
5572
+ default: inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource } : null,
5573
+ browsers: inv.browsers,
5574
+ pick: { how: pick.how, id: pick.browser ? pick.browser.id : null, name: pick.browser ? pick.browser.name : null, wanted: pick.wanted, stale: pick.stale },
5575
+ probed
5576
+ };
5577
+ const snapshot = { path: snapshotPath(), written: false, error: null };
5578
+ if (ov.persist === true) {
5579
+ try {
5580
+ writeAtomic(snapshot.path, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
5581
+ snapshot.written = true;
5582
+ } catch (e) {
5583
+ snapshot.error = String(e && e.message || e);
5584
+ }
5585
+ probed.push({
5586
+ section: "snapshot",
5587
+ source: FILE_NAME,
5588
+ detail: snapshot.written ? "\u5DF2\u843D\u76D8 " + snapshot.path : "\u843D\u76D8\u5931\u8D25: " + snapshot.error
5589
+ });
5590
+ }
5591
+ value.snapshot = snapshot;
5592
+ _formCache = { at: stamp, value };
5593
+ return Object.assign({ cached: false }, value);
5594
+ }
5595
+ function invalidate(platform) {
5596
+ _formCache = null;
5597
+ return detector.invalidate(platform);
5598
+ }
5599
+ function checkPreference(value, form2) {
5600
+ const candidates = (form2 && form2.browsers || []).map((b) => ({
5601
+ id: b.id,
5602
+ name: b.name,
5603
+ engine: b.engine,
5604
+ isDefault: b.isDefault === true
5605
+ }));
5606
+ const id = typeof value === "string" ? value.trim() : "";
5607
+ if (!id) return { ok: true, id: null, browser: null, candidates };
5608
+ const hit = candidates.find((c) => c.id === id);
5609
+ if (!hit) {
5610
+ return { ok: false, id: null, browser: null, candidates, error: "\u8BE5\u6D4F\u89C8\u5668\u4E0D\u5728\u672C\u673A\u5019\u9009\u6E05\u5355\u91CC\uFF08\u53EF\u80FD\u5DF2\u5378\u8F7D\u6216\u8DEF\u5F84\u5931\u6548\uFF09\uFF0C\u8BF7\u5148\u5237\u65B0\u73AF\u5883\u8868\u5355" };
5611
+ }
5612
+ return { ok: true, id: hit.id, browser: hit, candidates };
5613
+ }
5614
+ module2.exports = {
5615
+ SCHEMA,
5616
+ FILE_NAME,
5617
+ FORM_TTL_MS,
5618
+ bind,
5619
+ preferenceId,
5620
+ capabilities,
5621
+ identity,
5622
+ paths,
5623
+ browsers,
5624
+ normalizeInventory,
5625
+ rankCandidates,
5626
+ pickLauncher,
5627
+ checkPreference,
5628
+ form,
5629
+ snapshotPath,
5630
+ readSnapshot,
5631
+ invalidate
5632
+ };
5633
+ }
5634
+ });
5635
+
4790
5636
  // src/platform/os/process.js
4791
5637
  var require_process = __commonJS({
4792
5638
  "src/platform/os/process.js"(exports2, module2) {
@@ -5456,14 +6302,15 @@ var require_notify = __commonJS({
5456
6302
  var require_browser = __commonJS({
5457
6303
  "src/platform/os/browser.js"(exports2, module2) {
5458
6304
  "use strict";
5459
- var fs2 = require("node:fs");
5460
6305
  var path2 = require("node:path");
5461
- var os2 = require("node:os");
5462
6306
  var spawnOS = require_spawn();
5463
6307
  var desktop = require_desktop();
5464
6308
  var CAPABILITY_PROFILES = require_capability_profile();
5465
6309
  var { resolveExecutable, isExecutableFile } = require_exec_path();
5466
- var exec = require_exec();
6310
+ var environment = require_environment();
6311
+ var detector = require_browser_inventory();
6312
+ var { engineOf } = detector;
6313
+ var { allocTempDir, removeTreeDeferred } = require_fs();
5467
6314
  function isSafeHttpUrl(url) {
5468
6315
  try {
5469
6316
  const u = new URL(String(url));
@@ -5475,7 +6322,7 @@ var require_browser = __commonJS({
5475
6322
  function openCommand(platform, url) {
5476
6323
  const pl = platform || process.platform;
5477
6324
  if (pl === "darwin") return { cmd: "open", args: [url] };
5478
- if (pl === "win32") return { cmd: "explorer.exe", args: [url] };
6325
+ if (pl === "win32") return null;
5479
6326
  return { cmd: "xdg-open", args: [url] };
5480
6327
  }
5481
6328
  var SUPPORTED_OPEN_PLATFORMS = Object.keys(CAPABILITY_PROFILES).filter((k) => CAPABILITY_PROFILES[k].openBrowser === true);
@@ -5499,7 +6346,7 @@ var require_browser = __commonJS({
5499
6346
  var HANDEDOFF_TEXT = "\u5DF2\u628A\u5730\u5740\u4EA4\u7ED9\u7CFB\u7EDF\uFF0C\u4F46\u8FD9\u6B21\u542F\u52A8\u62FF\u4E0D\u5230\u7A97\u53E3\u662F\u5426\u51FA\u73B0\u7684\u8BC1\u636E";
5500
6347
  var FAILURE_TEXT = {
5501
6348
  "unsafe-url": "\u5730\u5740\u4E0D\u662F http(s) \u7EDD\u5BF9 URL\uFF0C\u5DF2\u62D2\u7EDD\u4EA4\u7ED9\u6D4F\u89C8\u5668",
5502
- "no-launcher": "\u672A\u627E\u5230\u53EF\u7528\u7684\u6D4F\u89C8\u5668\u542F\u52A8\u547D\u4EE4\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u8BE5\u5730\u5740",
6349
+ "no-launcher": "\u672C\u673A\u672A\u63A2\u5230\u53EF\u542F\u52A8\u7684\u6D4F\u89C8\u5668\uFF08\u6216\u6240\u9009\u6D4F\u89C8\u5668\u5DF2\u4E0D\u53EF\u6267\u884C\uFF09\uFF0C\u8BF7\u5728\u9762\u677F\u7684\u73AF\u5883\u8868\u5355\u91CC\u786E\u8BA4\uFF0C\u6216\u624B\u52A8\u6253\u5F00\u8BE5\u5730\u5740",
5503
6350
  "spawn-failed": "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF08\u7CFB\u7EDF\u62D2\u7EDD\u4E86\u8BE5\u547D\u4EE4\uFF09",
5504
6351
  "exit-nonzero": "\u7CFB\u7EDF\u62D2\u7EDD\u4E86\u8FD9\u4E2A\u5730\u5740\uFF08\u542F\u52A8\u547D\u4EE4\u975E 0 \u9000\u51FA\uFF09\uFF0C\u7A97\u53E3\u672A\u51FA\u73B0",
5505
6352
  "killed-by-signal": "\u6D4F\u89C8\u5668\u542F\u52A8\u547D\u4EE4\u88AB\u7CFB\u7EDF\u7EC8\u6B62\uFF0C\u7A97\u53E3\u672A\u51FA\u73B0",
@@ -5507,19 +6354,128 @@ var require_browser = __commonJS({
5507
6354
  "unsupported-platform": "\u5F53\u524D\u5E73\u53F0\u4E0D\u5728\u4EA7\u54C1\u652F\u6301\u7684\u684C\u9762\u5E73\u53F0\u5185\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u8BE5\u5730\u5740"
5508
6355
  };
5509
6356
  function ownsItsWindow(via, platform) {
6357
+ if (via === "isolated") return true;
5510
6358
  if (via !== "dispatcher") return false;
5511
6359
  return platform !== "win32";
5512
6360
  }
6361
+ function formOfBin(pl, b) {
6362
+ const engine = b && b.bin ? engineOf(b.bin) : "other";
6363
+ return { engine, direct: !!b && (engine !== "other" || pl === "win32") };
6364
+ }
5513
6365
  function openPlan(platform, url, opts) {
5514
6366
  const o = opts || {};
5515
6367
  const pl = platform || process.platform;
5516
- const db = o.defaultBrowser;
5517
- const engine = db && db.bin ? engineOf(db.bin) : "other";
5518
- if (engine === "chromium" || engine === "firefox") {
5519
- return { bin: db.bin, engine, via: "browser", baseArgs: (db.baseArgs || []).concat([url]), exitIsEvidence: ownsItsWindow("browser", pl) };
6368
+ const picked = o.pick || environment.pickLauncher(pl, o.inventory, o.preference);
6369
+ const b = picked.browser;
6370
+ const form = formOfBin(pl, b);
6371
+ if (form.direct) {
6372
+ return {
6373
+ bin: b.bin,
6374
+ args: (b.baseArgs || []).concat([url]),
6375
+ engine: form.engine,
6376
+ via: "browser",
6377
+ isolated: false,
6378
+ watch: false,
6379
+ envKind: "sys",
6380
+ pick: picked.how,
6381
+ stale: picked.stale === true,
6382
+ exitIsEvidence: ownsItsWindow("browser", pl)
6383
+ };
6384
+ }
6385
+ const c = openCommand(pl, url);
6386
+ if (!c) return {
6387
+ bin: null,
6388
+ args: [url],
6389
+ engine: form.engine,
6390
+ via: "none",
6391
+ isolated: false,
6392
+ watch: false,
6393
+ envKind: "sys",
6394
+ pick: picked.how,
6395
+ stale: picked.stale === true,
6396
+ exitIsEvidence: false
6397
+ };
6398
+ return {
6399
+ bin: c.cmd,
6400
+ args: c.args,
6401
+ engine: "other",
6402
+ via: "dispatcher",
6403
+ isolated: false,
6404
+ watch: false,
6405
+ envKind: "sys",
6406
+ pick: picked.how,
6407
+ stale: picked.stale === true,
6408
+ exitIsEvidence: ownsItsWindow("dispatcher", pl)
6409
+ };
6410
+ }
6411
+ function isolatedPlan(platform, url, opts) {
6412
+ const o = opts || {};
6413
+ const pl = platform || process.platform;
6414
+ const picked = o.pick || environment.pickLauncher(pl, o.inventory, o.preference);
6415
+ const b = picked.browser;
6416
+ const form = formOfBin(pl, b);
6417
+ const baseArgs = b && b.baseArgs || [];
6418
+ const common = { pick: picked.how, stale: picked.stale === true, engine: form.engine };
6419
+ const plain = (bin, label) => Object.assign(common, {
6420
+ bin,
6421
+ args: baseArgs.concat([url]),
6422
+ via: "browser",
6423
+ isolated: false,
6424
+ watch: false,
6425
+ envKind: "sys",
6426
+ label,
6427
+ exitIsEvidence: ownsItsWindow("browser", pl)
6428
+ });
6429
+ if (form.direct && form.engine === "chromium" && o.profileDir) {
6430
+ const args = ["--incognito", "--user-data-dir=" + o.profileDir];
6431
+ if (Array.isArray(o.size) && o.size.length === 2) args.push("--window-size=" + o.size[0] + "," + o.size[1]);
6432
+ if (o.lang) args.push("--lang=" + o.lang);
6433
+ args.push("--no-first-run", "--no-default-browser-check", "--disable-session-crashed-bubble");
6434
+ return Object.assign(common, {
6435
+ bin: b.bin,
6436
+ args: [...baseArgs, ...args, url],
6437
+ via: "isolated",
6438
+ isolated: true,
6439
+ watch: true,
6440
+ envKind: "anti",
6441
+ label: "chromium",
6442
+ exitIsEvidence: ownsItsWindow("isolated", pl)
6443
+ });
5520
6444
  }
6445
+ if (form.direct && form.engine === "firefox" && o.profileDir) {
6446
+ return Object.assign(common, {
6447
+ bin: b.bin,
6448
+ args: [...baseArgs, "--no-remote", "--profile", o.profileDir, "-private-window", url],
6449
+ via: "isolated",
6450
+ isolated: true,
6451
+ watch: true,
6452
+ envKind: "anti",
6453
+ label: "firefox",
6454
+ exitIsEvidence: ownsItsWindow("isolated", pl)
6455
+ });
6456
+ }
6457
+ if (form.direct) return plain(b.bin, path2.basename(b.bin));
5521
6458
  const c = openCommand(pl, url);
5522
- return { bin: c.cmd, engine: "other", via: "dispatcher", baseArgs: c.args, exitIsEvidence: ownsItsWindow("dispatcher", pl) };
6459
+ if (!c) return Object.assign(common, {
6460
+ bin: null,
6461
+ args: [url],
6462
+ via: "none",
6463
+ isolated: false,
6464
+ watch: false,
6465
+ envKind: "sys",
6466
+ label: null,
6467
+ exitIsEvidence: false
6468
+ });
6469
+ return Object.assign(common, {
6470
+ bin: c.cmd,
6471
+ args: c.args,
6472
+ via: "dispatcher",
6473
+ isolated: false,
6474
+ watch: false,
6475
+ envKind: "sys",
6476
+ label: c.cmd,
6477
+ exitIsEvidence: ownsItsWindow("dispatcher", pl)
6478
+ });
5523
6479
  }
5524
6480
  function observeSpawn(child, windowMs, setTimeoutFn) {
5525
6481
  return new Promise((resolve) => {
@@ -5531,63 +6487,40 @@ var require_browser = __commonJS({
5531
6487
  clearTimeout(timer);
5532
6488
  resolve(r);
5533
6489
  };
5534
- timer = (setTimeoutFn || setTimeout)(() => done({ stage: "alive" }), windowMs);
5535
- if (timer && timer.unref) timer.unref();
5536
- child.on("error", (e) => done({ stage: "error", code: e && e.code || String(e) }));
5537
- child.on("exit", (code, signal) => done({ stage: "exit", code, signal }));
5538
- });
5539
- }
5540
- var OPEN_OBSERVE_MS = 1500;
5541
- async function openBrowser(url, o) {
5542
- const opts = o || {};
5543
- const pl = opts.platform || process.platform;
5544
- if (!isSafeHttpUrl(url)) {
5545
- return outcome({ ok: false, reason: "unsafe-url", url: String(url || ""), evidence: null });
5546
- }
5547
- if (!SUPPORTED_OPEN_PLATFORMS.includes(pl)) {
5548
- return outcome({ ok: false, reason: "unsupported-platform", url, evidence: { platform: pl } });
5549
- }
5550
- const observeMs = opts.observeMs === void 0 ? OPEN_OBSERVE_MS : opts.observeMs;
5551
- const spawnWith = typeof opts.spawn === "function" ? opts.spawn : _spawnDetachedIgnored;
5552
- const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
5553
- const observe = typeof opts.observe === "function" ? opts.observe : observeSpawn;
5554
- let db;
5555
- if ("defaultBrowser" in opts) db = opts.defaultBrowser;
5556
- else if (typeof opts.resolveDefault === "function") db = opts.resolveDefault(pl, opts.resolveDeps || {});
5557
- else db = resolveDefaultBrowser(pl, opts.resolveDeps || {});
5558
- const plan = openPlan(pl, url, { defaultBrowser: db });
5559
- const evidence = { bin: plan.bin, engine: plan.engine, via: plan.via, ownsWindow: plan.exitIsEvidence === true, exitCode: null, exitSignal: null, error: null };
5560
- const desktopAvailable = typeof opts.desktopAvailable === "function" ? opts.desktopAvailable : desktop.sessionAvailable;
5561
- if (pl === "linux" && !desktopAvailable()) {
5562
- return outcome({ ok: false, reason: "no-desktop-session", url, evidence });
5563
- }
5564
- if (!avail(plan.bin)) {
5565
- return outcome({ ok: false, reason: "no-launcher", url, evidence });
5566
- }
5567
- let child;
5568
- try {
5569
- child = spawnWith(plan.bin, plan.baseArgs);
5570
- } catch (e) {
5571
- evidence.error = e && e.code || String(e);
5572
- return outcome({ ok: false, reason: "spawn-failed", url, evidence });
5573
- }
5574
- if (!child) return outcome({ ok: false, reason: "spawn-failed", url, evidence });
5575
- const seen = await observe(child, observeMs, opts.setTimeout);
5576
- evidence.error = seen.stage === "error" ? String(seen.code) : null;
5577
- evidence.exitCode = seen.code === void 0 ? null : seen.code;
5578
- evidence.exitSignal = seen.signal === void 0 ? null : seen.signal;
5579
- if (seen.stage === "error") return outcome({ ok: false, reason: "spawn-failed", url, evidence });
5580
- const exitDecides = seen.stage === "exit" && plan.exitIsEvidence === true;
5581
- if (exitDecides && (seen.code !== 0 || seen.signal)) {
5582
- return outcome({
5583
- ok: false,
5584
- reason: seen.signal ? "killed-by-signal" : "exit-nonzero",
5585
- url,
5586
- evidence,
5587
- error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
5588
- });
5589
- }
5590
- return outcome({ ok: true, confirmed: exitDecides, handedOff: !exitDecides, url, evidence });
6490
+ timer = (setTimeoutFn || setTimeout)(() => done({ stage: "alive" }), windowMs);
6491
+ if (timer && timer.unref) timer.unref();
6492
+ child.on("error", (e) => done({ stage: "error", code: e && e.code || String(e) }));
6493
+ child.on("exit", (code, signal) => done({ stage: "exit", code, signal }));
6494
+ });
6495
+ }
6496
+ var OPEN_OBSERVE_MS = 1500;
6497
+ var LOGIN_TZ_POOL = ["Asia/Shanghai", "Asia/Seoul", "Asia/Tokyo", "Asia/Singapore", "Europe/Berlin", "Europe/London", "Europe/Paris", "America/New_York", "America/Los_Angeles", "Australia/Sydney"];
6498
+ var LOGIN_LANG_POOL = ["zh-CN", "en-US", "en-GB", "ja-JP", "ko-KR", "de-DE", "fr-FR", "zh-TW"];
6499
+ var LOGIN_SIZE_POOL = [[1280, 800], [1366, 768], [1440, 900], [1536, 864], [1600, 900], [1680, 1050], [1920, 1080], [1024, 768], [1152, 864], [1280, 720]];
6500
+ function loginEnv(rand) {
6501
+ const pick = (arr) => arr[Math.floor((typeof rand === "function" ? rand() : Math.random()) * arr.length)];
6502
+ const size = pick(LOGIN_SIZE_POOL);
6503
+ const lang = pick(LOGIN_LANG_POOL);
6504
+ const tz = pick(LOGIN_TZ_POOL);
6505
+ const sysEnv = Object.assign({}, process.env, desktop.sessionEnv());
6506
+ return { size, lang, sysEnv, antiEnv: Object.assign({}, sysEnv, { TZ: tz, LANG: lang }) };
6507
+ }
6508
+ function launchDiagnostics(inv, plan, picked) {
6509
+ const found = (inv && inv.browsers || []).map((b) => ({
6510
+ name: b.name,
6511
+ engine: b.engine || engineOf(b.bin),
6512
+ via: (b.sources && b.sources.length ? b.sources : [b.source || "unknown"]).join("+")
6513
+ }));
6514
+ const pk = picked || {};
6515
+ return {
6516
+ platform: inv && inv.platform || null,
6517
+ pick: pk.how || plan && plan.pick || "none",
6518
+ bin: plan && plan.bin || null,
6519
+ default: inv && inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource || null } : null,
6520
+ preference: pk.wanted ? { id: pk.wanted, matched: pk.stale !== true } : null,
6521
+ found,
6522
+ probed: inv && inv.probed || []
6523
+ };
5591
6524
  }
5592
6525
  function _spawnDetachedIgnored(bin, args) {
5593
6526
  try {
@@ -5618,232 +6551,112 @@ var require_browser = __commonJS({
5618
6551
  child.unref();
5619
6552
  return child;
5620
6553
  }
5621
- function engineOf(bin) {
5622
- const base = String(bin || "").toLowerCase().split(/[\\/]/).pop();
5623
- if (/(chrome|chromium|msedge|edge|brave|vivaldi|opera|thorium)/.test(base)) return "chromium";
5624
- if (/(firefox|librewolf|waterfox)/.test(base)) return "firefox";
5625
- return "other";
5626
- }
5627
- function tokenizeExec(line) {
5628
- const toks = [];
5629
- let cur = "";
5630
- let q = null;
5631
- let esc = false;
5632
- let has = false;
5633
- for (const ch of String(line)) {
5634
- if (esc) {
5635
- cur += ch;
5636
- esc = false;
5637
- continue;
5638
- }
5639
- if (ch === "\\") {
5640
- esc = true;
5641
- has = true;
5642
- continue;
5643
- }
5644
- if (q) {
5645
- if (ch === q) q = null;
5646
- else cur += ch;
5647
- continue;
5648
- }
5649
- if (ch === "'" || ch === '"') {
5650
- q = ch;
5651
- has = true;
5652
- continue;
5653
- }
5654
- if (/\s/.test(ch)) {
5655
- if (has) {
5656
- toks.push(cur);
5657
- cur = "";
5658
- has = false;
5659
- }
5660
- continue;
5661
- }
5662
- cur += ch;
5663
- has = true;
5664
- }
5665
- if (has) toks.push(cur);
5666
- return toks;
5667
- }
5668
- function parseExecLine(line) {
5669
- let toks = tokenizeExec(line).filter((t) => t !== "%%" && !/^%[a-zA-Z]$/.test(t));
5670
- if (toks[0] === "env") {
5671
- let i = 1;
5672
- while (i < toks.length && /=/.test(toks[i])) i++;
5673
- toks = toks.slice(i);
5674
- }
5675
- if (!toks.length) return null;
5676
- return { bin: toks[0], baseArgs: toks.slice(1) };
5677
- }
5678
- function regValueOf(out) {
5679
- const m = String(out || "").match(/REG_SZ\s+(.*)/);
5680
- return m ? m[1].trim() : null;
5681
- }
5682
- function exeFromCmdLine(cmdLine) {
5683
- const s = String(cmdLine || "");
5684
- let m = s.match(/^\s*"([^"]+\.exe)"/i);
5685
- if (m) return m[1];
5686
- m = s.match(/^\s*(\S+\.exe)/i);
5687
- return m ? m[1] : null;
5688
- }
5689
- function resolveDefaultWin(runOut) {
5690
- const roots = ["HKCU\\Software\\Clients\\StartMenuInternet", "HKLM\\Software\\Clients\\StartMenuInternet"];
5691
- for (const root of roots) {
5692
- const progId = regValueOf(runOut("reg.exe", ["query", root, "/ve"]));
5693
- if (!progId || !/^[\x20-\x7e]+$/.test(progId)) continue;
5694
- for (const r2 of roots) {
5695
- const cmd2 = regValueOf(runOut("reg.exe", ["query", r2 + "\\" + progId + "\\shell\\open\\command", "/ve"]));
5696
- const bin = exeFromCmdLine(cmd2);
5697
- if (bin) return { bin, baseArgs: [] };
5698
- }
5699
- }
5700
- return null;
5701
- }
5702
- var MAC_JXA = [
5703
- "ObjC.import('CoreServices');ObjC.import('Foundation');",
5704
- "var b=ObjC.unwrap($.LSCopyDefaultHandlerForURLScheme(null,'https'))||ObjC.unwrap($.LSCopyDefaultHandlerForURLScheme(null,'http'));",
5705
- "if(!b){'EMPTY';}else{",
5706
- "var u=$.NSWorkspace.workspace.URLForApplicationWithBundleIdentifier(b);",
5707
- "if(u.isNil()){'EMPTY';}else{",
5708
- "var e=ObjC.unwrap($.NSBundle.bundleWithURL(u).objectForInfoDictionaryKey('CFBundleExecutable'));",
5709
- "b+'\\t'+ObjC.unwrap(u.path)+'\\t'+e;}}"
5710
- ].join("");
5711
- function resolveDefaultMac(runOut) {
5712
- const out = runOut("osascript", ["-l", "JavaScript", "-e", MAC_JXA]);
5713
- if (!out) return null;
5714
- const parts = out.trim().split(" ");
5715
- if (parts.length < 3 || parts[0] === "EMPTY" || !parts[2]) return null;
5716
- return { bin: path2.posix.join(parts[1], "Contents", "MacOS", parts[2]), baseArgs: [], bundleId: parts[0] };
5717
- }
5718
- function resolveDefaultLinux(runOut, readFile, exists, env, home) {
5719
- const id = runOut("xdg-settings", ["get", "default-web-browser"]);
5720
- const desktop2 = id && id.trim();
5721
- if (!desktop2 || !/^[A-Za-z0-9._-]+\.desktop$/.test(desktop2)) return null;
5722
- const dirs = [
5723
- env.XDG_DATA_HOME || path2.join(home, ".local", "share"),
5724
- "/usr/local/share",
5725
- "/usr/share",
5726
- "/var/lib/snapd/desktop"
5727
- ].map((d) => path2.join(d, "applications"));
5728
- for (const dir of dirs) {
5729
- const p = path2.join(dir, desktop2);
5730
- let text = null;
5731
- try {
5732
- if (exists(p)) text = readFile(p);
5733
- } catch {
5734
- continue;
5735
- }
5736
- if (text === null) continue;
5737
- let inMain = false;
5738
- for (const line of text.split(/\r?\n/)) {
5739
- if (/^\[Desktop Entry\]\s*$/.test(line)) {
5740
- inMain = true;
5741
- continue;
5742
- }
5743
- if (/^\[/.test(line)) break;
5744
- if (inMain) {
5745
- const m = line.match(/^Exec=(.+)/);
5746
- if (m) return parseExecLine(m[1]);
5747
- }
5748
- }
5749
- }
5750
- return null;
5751
- }
5752
- function resolveDefaultBrowser(platform, o) {
5753
- const ov = o || {};
5754
- const pl = platform || process.platform;
5755
- const runOut = ov.runOut || ((bin, args) => exec.runOut(bin, args, { timeoutMs: 5e3 }));
5756
- const readFile = ov.readFile || ((p) => fs2.readFileSync(p, "utf8"));
5757
- const exists = ov.exists || ((p) => fs2.existsSync(p));
5758
- const env = ov.env || process.env;
5759
- const home = ov.home || os2.homedir();
5760
- try {
5761
- if (pl === "win32") return resolveDefaultWin(runOut);
5762
- if (pl === "darwin") return resolveDefaultMac(runOut);
5763
- return resolveDefaultLinux(runOut, readFile, exists, env, home);
5764
- } catch {
5765
- return null;
5766
- }
5767
- }
5768
- function isolatedPlan(platform, url, opts) {
5769
- const o = opts || {};
5770
- const pl = platform || process.platform;
5771
- const db = o.defaultBrowser;
5772
- const engine = db && db.bin ? engineOf(db.bin) : "other";
5773
- const baseArgs = db && db.baseArgs || [];
5774
- if (engine === "chromium" && o.profileDir) {
5775
- const args = ["--incognito", "--user-data-dir=" + o.profileDir];
5776
- if (Array.isArray(o.size) && o.size.length === 2) args.push("--window-size=" + o.size[0] + "," + o.size[1]);
5777
- if (o.lang) args.push("--lang=" + o.lang);
5778
- args.push("--no-first-run", "--no-default-browser-check", "--disable-session-crashed-bubble");
5779
- return { bin: db.bin, args: [...baseArgs, ...args, url], isolated: true, watch: true, envKind: "anti", label: "chromium" };
5780
- }
5781
- if (engine === "firefox" && o.profileDir) {
5782
- return {
5783
- bin: db.bin,
5784
- args: [...baseArgs, "--no-remote", "--profile", o.profileDir, "-private-window", url],
5785
- isolated: true,
5786
- watch: true,
5787
- envKind: "anti",
5788
- label: "firefox"
5789
- };
5790
- }
5791
- const c = openCommand(pl, url);
5792
- return { bin: c.cmd, args: c.args, isolated: false, watch: false, envKind: "sys", label: c.cmd };
5793
- }
5794
6554
  function binAvailable(bin) {
5795
6555
  if (!bin) return false;
5796
6556
  if (bin.includes("/") || bin.includes("\\") || /^[A-Za-z]:[\\/]/.test(bin)) return isExecutableFile(bin);
5797
6557
  return resolveExecutable(bin) !== null;
5798
6558
  }
5799
- function launchIsolated(url, o) {
6559
+ async function openBrowser(url, o) {
5800
6560
  const opts = o || {};
5801
- if (!isSafeHttpUrl(url)) return Object.assign(outcome({ ok: false, reason: "unsafe-url", url: String(url || "") }), { bin: null, isolated: false });
5802
- const antiEnv = opts.antiEnv || opts.sysEnv || process.env;
5803
- const sysEnv = opts.sysEnv || process.env;
5804
- const onExit = opts.onExit;
6561
+ const pl = opts.platform || process.platform;
6562
+ const intent = opts.intent === "isolated-login" ? "isolated-login" : "plain";
6563
+ if (!isSafeHttpUrl(url)) {
6564
+ return outcome({ ok: false, reason: "unsafe-url", url: String(url || ""), evidence: null });
6565
+ }
6566
+ if (!SUPPORTED_OPEN_PLATFORMS.includes(pl)) {
6567
+ return outcome({ ok: false, reason: "unsupported-platform", url, evidence: { platform: pl } });
6568
+ }
6569
+ const observeMs = opts.observeMs === void 0 ? OPEN_OBSERVE_MS : opts.observeMs;
6570
+ const spawnWith = typeof opts.spawn === "function" ? opts.spawn : intent === "isolated-login" ? _spawnDetached : _spawnDetachedIgnored;
5805
6571
  const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
5806
- const spawnWith = typeof opts.spawn === "function" ? opts.spawn : _spawnDetached;
5807
- const fail = (reason, extra) => Object.assign(outcome(Object.assign({ ok: false, reason, url }, extra)), { bin: null, isolated: false });
6572
+ const observe = typeof opts.observe === "function" ? opts.observe : observeSpawn;
6573
+ const resolveInv = typeof opts.resolveInventory === "function" ? opts.resolveInventory : ((platform, deps) => environment.browsers(Object.assign({ platform }, deps)));
6574
+ const inv = "inventory" in opts ? opts.inventory : resolveInv(pl, opts.resolveDeps || {});
6575
+ const pref = "preference" in opts ? opts.preference : void 0;
6576
+ const login = intent === "isolated-login" ? loginEnv(opts.rand) : null;
6577
+ const picked = opts.pick || environment.pickLauncher(pl, inv, pref);
6578
+ const form = formOfBin(pl, picked.browser);
6579
+ const canIsolate = intent === "isolated-login" && form.direct && (form.engine === "chromium" || form.engine === "firefox");
6580
+ let profile = null;
6581
+ if (canIsolate) {
6582
+ const alloc = typeof opts.allocProfile === "function" ? opts.allocProfile : (() => allocTempDir("dsh-login-"));
6583
+ try {
6584
+ profile = opts.profileDir || alloc();
6585
+ } catch {
6586
+ profile = null;
6587
+ }
6588
+ }
6589
+ const plan = intent === "isolated-login" ? isolatedPlan(pl, url, { inventory: inv, pick: picked, profileDir: profile, size: login.size, lang: login.lang }) : openPlan(pl, url, { inventory: inv, pick: picked });
6590
+ const diagnostics = launchDiagnostics(inv, plan, picked);
6591
+ const evidence = {
6592
+ bin: plan.bin,
6593
+ engine: plan.engine,
6594
+ via: plan.via,
6595
+ ownsWindow: plan.exitIsEvidence === true,
6596
+ isolated: plan.isolated === true,
6597
+ profile: plan.isolated ? profile : null,
6598
+ watch: plan.watch === true,
6599
+ exitCode: null,
6600
+ exitSignal: null,
6601
+ error: null,
6602
+ diagnostics
6603
+ };
6604
+ const deferredRemove = typeof opts.rmTree === "function" ? opts.rmTree : removeTreeDeferred;
6605
+ const fail = (reason, patch) => {
6606
+ if (profile) deferredRemove(profile, 0);
6607
+ return outcome(Object.assign({ ok: false, reason, url, evidence }, patch));
6608
+ };
6609
+ const desktopAvailable = typeof opts.desktopAvailable === "function" ? opts.desktopAvailable : desktop.sessionAvailable;
6610
+ if (pl === "linux" && !desktopAvailable()) {
6611
+ return fail("no-desktop-session");
6612
+ }
6613
+ if (!plan.bin) return fail("no-launcher");
6614
+ if (!avail(plan.bin)) return fail("no-launcher");
6615
+ if (canIsolate && !profile) {
6616
+ evidence.error = "\u4E34\u65F6 profile \u76EE\u5F55\u5206\u914D\u5931\u8D25";
6617
+ return fail("spawn-failed");
6618
+ }
6619
+ let child;
5808
6620
  try {
5809
- let db;
5810
- if ("defaultBrowser" in opts) db = opts.defaultBrowser;
5811
- else if (typeof opts.resolveDefault === "function") db = opts.resolveDefault(process.platform, opts.resolveDeps || {});
5812
- else db = resolveDefaultBrowser(process.platform, opts.resolveDeps || {});
5813
- const plan = isolatedPlan(process.platform, url, {
5814
- defaultBrowser: db,
5815
- profileDir: opts.profileDir,
5816
- size: opts.size,
5817
- lang: opts.lang
5818
- });
5819
- if (!avail(plan.bin)) return fail("no-launcher", { evidence: { bin: plan.bin, engine: plan.label, via: plan.isolated ? "browser" : "dispatcher" } });
5820
- const env = plan.envKind === "anti" ? antiEnv : sysEnv;
5821
- const p = spawnWith(plan.bin, plan.args, env, plan.watch ? onExit : void 0);
5822
- if (!p) return fail("spawn-failed");
5823
- return Object.assign(outcome({ ok: true, confirmed: false, handedOff: true, url }), { bin: plan.label, isolated: plan.isolated });
6621
+ child = intent === "isolated-login" ? spawnWith(plan.bin, plan.args, plan.envKind === "anti" ? login.antiEnv : login.sysEnv, plan.watch ? opts.onExit : void 0) : spawnWith(plan.bin, plan.args);
5824
6622
  } catch (e) {
5825
- return fail("spawn-failed", { error: FAILURE_TEXT["spawn-failed"] + "\uFF08" + (e && e.code || e) + "\uFF09" });
6623
+ evidence.error = e && e.code || String(e);
6624
+ return fail("spawn-failed");
6625
+ }
6626
+ if (!child) return fail("spawn-failed");
6627
+ if (evidence.profile) deferredRemove(evidence.profile, opts.profileMs === void 0 ? 30 * 60 * 1e3 : opts.profileMs);
6628
+ const seen = await observe(child, observeMs, opts.setTimeout);
6629
+ evidence.error = seen.stage === "error" ? String(seen.code) : null;
6630
+ evidence.exitCode = seen.code === void 0 ? null : seen.code;
6631
+ evidence.exitSignal = seen.signal === void 0 ? null : seen.signal;
6632
+ if (seen.stage === "error") return outcome({ ok: false, reason: "spawn-failed", url, evidence });
6633
+ const exitDecides = seen.stage === "exit" && plan.exitIsEvidence === true;
6634
+ if (exitDecides && (seen.code !== 0 || seen.signal)) {
6635
+ return outcome({
6636
+ ok: false,
6637
+ reason: seen.signal ? "killed-by-signal" : "exit-nonzero",
6638
+ url,
6639
+ evidence,
6640
+ error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
6641
+ });
5826
6642
  }
6643
+ return outcome({ ok: true, confirmed: exitDecides, handedOff: !exitDecides, url, evidence });
5827
6644
  }
5828
6645
  module2.exports = {
5829
6646
  openBrowser,
5830
- launchIsolated,
5831
6647
  openCommand,
5832
6648
  openPlan,
5833
- ownsItsWindow,
5834
6649
  isolatedPlan,
6650
+ ownsItsWindow,
5835
6651
  observeSpawn,
5836
6652
  isSafeHttpUrl,
5837
- resolveDefaultBrowser,
5838
- resolveDefaultWin,
5839
- resolveDefaultMac,
5840
- resolveDefaultLinux,
6653
+ binAvailable,
6654
+ launchDiagnostics,
6655
+ loginEnv,
6656
+ // 探测层的解析原语经此转出(唯一实现处在 ./browser-inventory.js):门禁与消费方按同一出口取用,
6657
+ // 不在别处再写第二份平台解析。
5841
6658
  engineOf,
5842
- parseExecLine,
5843
- tokenizeExec,
5844
- regValueOf,
5845
- exeFromCmdLine,
5846
- binAvailable
6659
+ detector
5847
6660
  };
5848
6661
  }
5849
6662
  });
@@ -6196,6 +7009,7 @@ var require_os = __commonJS({
6196
7009
  var execPath = require_exec_path();
6197
7010
  var desktop = require_desktop();
6198
7011
  var CAPABILITY_PROFILES = require_capability_profile();
7012
+ var environment = require_environment();
6199
7013
  var PLATFORM = process.platform;
6200
7014
  var ARCH = process.arch;
6201
7015
  var isLinux = PLATFORM === "linux";
@@ -6250,6 +7064,7 @@ var require_os = __commonJS({
6250
7064
  }
6251
7065
  return p;
6252
7066
  }
7067
+ environment.bind({ capabilities });
6253
7068
  module2.exports = {
6254
7069
  PLATFORM,
6255
7070
  ARCH,
@@ -6273,6 +7088,8 @@ var require_os = __commonJS({
6273
7088
  // 导出模块对象会抛 platform.notify is not a function,把升级终态误判成失败。
6274
7089
  notify: require_notify().notify,
6275
7090
  browser: require_browser(),
7091
+ environment,
7092
+ // 环境表单(外部打开链路的事实底座 + 选路依据)
6276
7093
  desktop,
6277
7094
  // 图形会话可用性(Linux 需实测 socket)
6278
7095
  autostart: require_autostart()
@@ -12391,6 +13208,60 @@ var require_access = __commonJS({
12391
13208
  }
12392
13209
  });
12393
13210
 
13211
+ // src/app/settings/browser.js
13212
+ var require_browser2 = __commonJS({
13213
+ "src/app/settings/browser.js"(exports2, module2) {
13214
+ "use strict";
13215
+ var platform = require_os();
13216
+ var { verifyPersisted } = require_access();
13217
+ module2.exports = {
13218
+ methods: {
13219
+ /** 当前偏好 + 候选清单 + 这一拍的分发依据(面板据此渲染选择器并说明「现在实际会用谁」)。 */
13220
+ externalBrowserStatus() {
13221
+ try {
13222
+ const form = platform.environment.form();
13223
+ const v = platform.environment.checkPreference(platform.environment.preferenceId(), form);
13224
+ return {
13225
+ ok: true,
13226
+ configured: !!form.preference && form.preference.configured === true,
13227
+ value: form.preference && form.preference.id || null,
13228
+ // stale=true:偏好所指已不在候选清单(被卸载/路径失效),本次分发已回落,需用户重选。
13229
+ stale: !!(form.pick && form.pick.stale),
13230
+ browser: v.browser,
13231
+ candidates: v.candidates,
13232
+ pick: form.pick || null,
13233
+ platform: form.platform
13234
+ };
13235
+ } catch (e) {
13236
+ return { ok: false, error: e.message };
13237
+ }
13238
+ },
13239
+ /** 设置/清除浏览器偏好(空串=清除,回到「按系统默认或候选次序分发」)。 */
13240
+ setExternalBrowser(id) {
13241
+ try {
13242
+ const form = platform.environment.form();
13243
+ const v = platform.environment.checkPreference(id, form);
13244
+ if (!v.ok) return { ok: false, error: v.error };
13245
+ const cfg = this.config || {};
13246
+ cfg.externalBrowser = v.id;
13247
+ const patch = { externalBrowser: v.id };
13248
+ if (this.configPath) {
13249
+ this.state.persistConfigPatch(patch);
13250
+ const persistError = verifyPersisted(this.configPath, patch);
13251
+ if (persistError) return { ok: false, error: persistError };
13252
+ }
13253
+ platform.environment.invalidate();
13254
+ if (this.events) this.events.append("external_browser_changed", { id: v.id, name: v.browser ? v.browser.name : null });
13255
+ return { ok: true, configured: !!v.id, value: v.id, browser: v.browser };
13256
+ } catch (e) {
13257
+ return { ok: false, error: e.message };
13258
+ }
13259
+ }
13260
+ }
13261
+ };
13262
+ }
13263
+ });
13264
+
12394
13265
  // src/platform/os/netinfo.js
12395
13266
  var require_netinfo = __commonJS({
12396
13267
  "src/platform/os/netinfo.js"(exports2, module2) {
@@ -12608,6 +13479,7 @@ var require_facets = __commonJS({
12608
13479
  { name: "settings/node-lts", mod: require_node_lts() },
12609
13480
  { name: "settings/versions", mod: require_versions() },
12610
13481
  { name: "settings/access", mod: require_access() },
13482
+ { name: "settings/browser", mod: require_browser2() },
12611
13483
  { name: "settings/lan-panel", mod: require_lan_panel() }
12612
13484
  ];
12613
13485
  function installMethods(host2, methods) {
@@ -14150,6 +15022,7 @@ var require_core5 = __commonJS({
14150
15022
  function composeCore(host2, rawConfig, configPath) {
14151
15023
  host2.config = normalize(rawConfig, domainConfigExtension2());
14152
15024
  host2.configPath = typeof configPath === "string" ? configPath : null;
15025
+ platform.environment.bind({ preference: () => host2.config && host2.config.externalBrowser || null });
14153
15026
  host2._fileProtectStatus = null;
14154
15027
  try {
14155
15028
  const fp = platform.fileProtect;
@@ -17684,89 +18557,13 @@ var require_forward_core = __commonJS({
17684
18557
  }
17685
18558
  });
17686
18559
 
17687
- // src/domains/router/ops/browser.js
17688
- var require_browser2 = __commonJS({
17689
- "src/domains/router/ops/browser.js"(exports2, module2) {
17690
- "use strict";
17691
- var platform = require_os();
17692
- var fs2 = require("node:fs");
17693
- var os2 = require("node:os");
17694
- var path2 = require("node:path");
17695
- var crypto = require("node:crypto");
17696
- function graphicalEnv() {
17697
- const out = {};
17698
- try {
17699
- const uid = process.getuid ? String(process.getuid()) : "";
17700
- const xdgRun = process.env.XDG_RUNTIME_DIR || "/run/user/" + uid;
17701
- if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
17702
- const x11 = "/tmp/.X11-unix";
17703
- try {
17704
- if (fs2.existsSync(x11)) {
17705
- const socks = fs2.readdirSync(x11).filter((f) => /^X\d+$/.test(f)).map((f) => parseInt(f.slice(1), 10)).sort((a, b) => a - b);
17706
- if (socks.length > 0) {
17707
- out.DISPLAY = ":" + socks[0];
17708
- const xauth = path2.join(os2.homedir(), ".Xauthority");
17709
- if (fs2.existsSync(xauth)) out.XAUTHORITY = xauth;
17710
- }
17711
- }
17712
- } catch {
17713
- }
17714
- if (!out.DISPLAY) {
17715
- try {
17716
- if (fs2.existsSync(xdgRun)) {
17717
- const wl = fs2.readdirSync(xdgRun).filter((f) => f.startsWith("wayland-")).sort();
17718
- if (wl.length > 0) out.WAYLAND_DISPLAY = wl[0];
17719
- }
17720
- } catch {
17721
- }
17722
- }
17723
- }
17724
- if (!process.env.DBUS_SESSION_BUS_ADDRESS && fs2.existsSync(path2.join(xdgRun, "bus"))) {
17725
- out.DBUS_SESSION_BUS_ADDRESS = "unix:path=" + path2.join(xdgRun, "bus");
17726
- }
17727
- } catch {
17728
- }
17729
- return out;
17730
- }
17731
- function openInBrowser(url, onExit) {
17732
- try {
17733
- const tmpProfile = path2.join(os2.tmpdir(), "dsh-oauth-" + crypto.randomBytes(8).toString("hex"));
17734
- const sysEnv = Object.assign({}, process.env, graphicalEnv());
17735
- const TZ_POOL = ["Asia/Shanghai", "Asia/Seoul", "Asia/Tokyo", "Asia/Singapore", "Europe/Berlin", "Europe/London", "Europe/Paris", "America/New_York", "America/Los_Angeles", "Australia/Sydney"];
17736
- const LANG_POOL = ["zh-CN", "en-US", "en-GB", "ja-JP", "ko-KR", "de-DE", "fr-FR", "zh-TW"];
17737
- const SIZE_POOL = [[1280, 800], [1366, 768], [1440, 900], [1536, 864], [1600, 900], [1680, 1050], [1920, 1080], [1024, 768], [1152, 864], [1280, 720]];
17738
- const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
17739
- const size = pick(SIZE_POOL);
17740
- const lang = pick(LANG_POOL);
17741
- const tz = pick(TZ_POOL);
17742
- const antiEnv = Object.assign({}, sysEnv, { TZ: tz, LANG: lang });
17743
- const r = platform.browser.launchIsolated(url, { profileDir: tmpProfile, size, lang, antiEnv, sysEnv, onExit });
17744
- if (!r || !r.ok) return { profile: null, result: r || { ok: false, reason: "spawn-failed" } };
17745
- if (r.isolated) {
17746
- const t30 = setTimeout(() => {
17747
- try {
17748
- fs2.rmSync(tmpProfile, { recursive: true, force: true });
17749
- } catch {
17750
- }
17751
- }, 30 * 60 * 1e3);
17752
- if (t30.unref) t30.unref();
17753
- }
17754
- return { profile: tmpProfile, result: r };
17755
- } catch (e) {
17756
- return { profile: null, result: { ok: false, reason: "spawn-failed", error: "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF1A" + (e && e.message || e) } };
17757
- }
17758
- }
17759
- module2.exports = { graphicalEnv, openInBrowser };
17760
- }
17761
- });
17762
-
17763
18560
  // src/domains/router/ops/oauth.js
17764
18561
  var require_oauth = __commonJS({
17765
18562
  "src/domains/router/ops/oauth.js"(exports2, module2) {
17766
18563
  "use strict";
17767
- var fs2 = require("node:fs");
17768
18564
  var crypto = require("node:crypto");
17769
18565
  var { createServer } = require("node:http");
18566
+ var { removeTreeDeferred } = require_fs();
17770
18567
  function createOAuthOps(deps) {
17771
18568
  const d = deps || {};
17772
18569
  const ports = d.ports;
@@ -17931,20 +18728,21 @@ var require_oauth = __commonJS({
17931
18728
  promise.catch(() => {
17932
18729
  });
17933
18730
  st._ccLoginPromise = promise;
17934
- const opened = openInBrowser(authUrl, () => {
18731
+ const opened = await openInBrowser(authUrl, () => {
17935
18732
  if (st._ccLoginRound !== roundId) return;
17936
18733
  if (st._ccLoginReject) {
17937
- const r = st._ccLoginReject;
18734
+ const r2 = st._ccLoginReject;
17938
18735
  st._ccLoginReject = null;
17939
18736
  st._ccLoginResolve = null;
17940
18737
  try {
17941
- r(new Error("\u6D4F\u89C8\u5668\u5DF2\u5173\u95ED\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88"));
18738
+ r2(new Error("\u6D4F\u89C8\u5668\u5DF2\u5173\u95ED\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88"));
17942
18739
  } catch {
17943
18740
  }
17944
18741
  }
17945
18742
  });
17946
- const evidence = opened.result || {};
17947
- if (!opened.profile) {
18743
+ const r = opened || { ok: false, reason: "spawn-failed" };
18744
+ const ev = r.evidence || {};
18745
+ if (!r.ok) {
17948
18746
  st._ccLoginPromise = null;
17949
18747
  st._ccLoginResolve = null;
17950
18748
  st._ccLoginReject = null;
@@ -17964,12 +18762,13 @@ var require_oauth = __commonJS({
17964
18762
  opened: false,
17965
18763
  confirmed: false,
17966
18764
  handedOff: false,
17967
- reason: evidence.reason || "no-launcher",
17968
- error: (evidence.error || "\u65E0\u6CD5\u8C03\u8D77\u7CFB\u7EDF\u6D4F\u89C8\u5668") + "\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u4E0B\u65B9\u5730\u5740\u5B8C\u6210\u6388\u6743"
18765
+ reason: r.reason || "no-launcher",
18766
+ evidence: r.evidence || null,
18767
+ error: (r.error || "\u65E0\u6CD5\u8C03\u8D77\u7CFB\u7EDF\u6D4F\u89C8\u5668") + "\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u4E0B\u65B9\u5730\u5740\u5B8C\u6210\u6388\u6743"
17969
18768
  };
17970
18769
  }
17971
- st._ccLogin = { state, port, server, tmpProfile: opened.profile };
17972
- return Object.assign({}, evidence, {
18770
+ st._ccLogin = { state, port, server, tmpProfile: ev.profile || null };
18771
+ return Object.assign({}, r, {
17973
18772
  ok: true,
17974
18773
  authUrl,
17975
18774
  url: authUrl,
@@ -17977,8 +18776,8 @@ var require_oauth = __commonJS({
17977
18776
  port,
17978
18777
  waitMs: 18e4,
17979
18778
  opened: true,
17980
- // 非隔离引擎(Safari/snap 包装器)由平台层降级:账号隔离不成立,换账号只能靠超时重发或手动窗口。
17981
- isolated: evidence.isolated === true
18779
+ // 非隔离引擎(Safari/打包器包装)由平台层降级:账号隔离不成立,换账号只能靠超时重发或手动窗口。
18780
+ isolated: ev.isolated === true
17982
18781
  });
17983
18782
  }
17984
18783
  async function commandcodeLoginWait(timeoutMs) {
@@ -18024,15 +18823,7 @@ var require_oauth = __commonJS({
18024
18823
  st._ccLoginResolve = st._ccLoginReject = null;
18025
18824
  return { ok: false, error: e.message };
18026
18825
  } finally {
18027
- if (tmpProfile) {
18028
- const t60 = setTimeout(() => {
18029
- try {
18030
- fs2.rmSync(tmpProfile, { recursive: true, force: true });
18031
- } catch {
18032
- }
18033
- }, 60 * 1e3);
18034
- if (t60.unref) t60.unref();
18035
- }
18826
+ if (tmpProfile) removeTreeDeferred(tmpProfile, 60 * 1e3);
18036
18827
  }
18037
18828
  }
18038
18829
  return { commandcodeLoginStart, commandcodeLoginWait };
@@ -18486,8 +19277,8 @@ var require_router_ops = __commonJS({
18486
19277
  "use strict";
18487
19278
  require_port_segments();
18488
19279
  var ports = require_ports().shared;
19280
+ var platform = require_os();
18489
19281
  var { maskKey } = require_base();
18490
- var { openInBrowser } = require_browser2();
18491
19282
  var { createOAuthOps } = require_oauth();
18492
19283
  var { createAppsRegistryOps } = require_apps_registry();
18493
19284
  var { createQuotaSyncOps } = require_quotasync();
@@ -18495,6 +19286,7 @@ var require_router_ops = __commonJS({
18495
19286
  function createAuxCore(deps) {
18496
19287
  const d = deps || {};
18497
19288
  const getProviders = d.getProviders || (() => []);
19289
+ const openInBrowser = (url, onExit) => platform.browser.openBrowser(url, { intent: "isolated-login", onExit });
18498
19290
  const oauth = createOAuthOps({ ports, openInBrowser });
18499
19291
  const apps = createAppsRegistryOps({
18500
19292
  getProviders,
@@ -25512,7 +26304,7 @@ var require_guard = __commonJS({
25512
26304
  var fs2 = require("node:fs");
25513
26305
  var path2 = require("node:path");
25514
26306
  function owns(pathname) {
25515
- 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 === "/ports" || pathname === "/shutdown";
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";
25516
26308
  }
25517
26309
  function fetchDshChangelog(res, sup) {
25518
26310
  const v = sup && sup.nativeManager ? sup.nativeManager.versionInfo() : {};
@@ -25524,7 +26316,7 @@ var require_guard = __commonJS({
25524
26316
  return res.end(md);
25525
26317
  }
25526
26318
  function handle(ctx) {
25527
- const { sup, req, res, pathname, identity, send, collectBody, originAllowed, browser } = ctx;
26319
+ const { sup, req, res, pathname, identity, send, collectBody, originAllowed, browser, environment } = ctx;
25528
26320
  if (req.method === "GET" && pathname === "/changelog") {
25529
26321
  return fetchDshChangelog(res, sup);
25530
26322
  }
@@ -25630,6 +26422,31 @@ var require_guard = __commonJS({
25630
26422
  });
25631
26423
  return;
25632
26424
  }
26425
+ if (req.method === "GET" && pathname === "/settings/external-browser") {
26426
+ return send(200, sup.externalBrowserStatus());
26427
+ }
26428
+ if (req.method === "POST" && pathname === "/settings/external-browser") {
26429
+ if (!originAllowed(req, sup.config.apiPort)) {
26430
+ req.resume();
26431
+ return send(403, {});
26432
+ }
26433
+ collectBody(req, res, 2048, (body) => {
26434
+ let id = null;
26435
+ let given = false;
26436
+ try {
26437
+ const j = body ? JSON.parse(body) : {};
26438
+ if (typeof j.id === "string") {
26439
+ id = j.id;
26440
+ given = true;
26441
+ }
26442
+ } catch {
26443
+ }
26444
+ if (!given) return send(400, { ok: false, error: '\u9700\u8981 {"id":"<\u5019\u9009 id>"}\uFF08\u7A7A\u4E32\u6E05\u9664\u504F\u597D\uFF09' });
26445
+ const r = sup.setExternalBrowser(id);
26446
+ return send(r.ok ? 200 : 500, r);
26447
+ });
26448
+ return;
26449
+ }
25633
26450
  if (req.method === "POST" && pathname === "/shutdown") {
25634
26451
  if (!originAllowed(req, sup.config.apiPort)) {
25635
26452
  req.resume();
@@ -25688,6 +26505,14 @@ var require_guard = __commonJS({
25688
26505
  if (req.method === "GET" && pathname === "/env/node-lts") {
25689
26506
  return sup.nodeLtsStatus().then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
25690
26507
  }
26508
+ if (req.method === "GET" && pathname === "/env/environment") {
26509
+ if (!originAllowed(req, sup.config.apiPort)) {
26510
+ req.resume();
26511
+ return send(403, { ok: false, error: "cross-origin request rejected" });
26512
+ }
26513
+ 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) }));
26515
+ }
25691
26516
  if (req.method === "POST" && pathname === "/env/open-url") {
25692
26517
  if (!identity.loopback) {
25693
26518
  req.resume();
@@ -26823,6 +27648,7 @@ var require_server = __commonJS({
26823
27648
  var { identify } = require_identity3();
26824
27649
  var { originAllowed, requestHasAccessKey, isShellOrigin } = require_security();
26825
27650
  var browserExit = require_browser();
27651
+ var environmentExit = require_environment();
26826
27652
  function safeFail(res, err, where) {
26827
27653
  try {
26828
27654
  const body = JSON.stringify({ ok: false, error: err && err.message || String(err) });
@@ -26842,6 +27668,7 @@ var require_server = __commonJS({
26842
27668
  }
26843
27669
  function createServer(sup, deps) {
26844
27670
  const browser = deps && deps.browser || browserExit;
27671
+ const environment = deps && deps.environment || environmentExit;
26845
27672
  return http2.createServer((req, res) => {
26846
27673
  const shellOrigin = (() => {
26847
27674
  const o = req.headers.origin;
@@ -26909,7 +27736,7 @@ var require_server = __commonJS({
26909
27736
  } catch (e) {
26910
27737
  return send(400, { error: "bad request encoding" });
26911
27738
  }
26912
- const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf, browser };
27739
+ const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf, browser, environment };
26913
27740
  for (const d of API_DOMAINS) {
26914
27741
  if (d.owns(pathname)) {
26915
27742
  try {