@dsh-sup/dsh-core-linux-x64 0.1.6-BETA.13 → 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 +2461 -1416
- package/package.json +1 -1
- package/ui-react/assets/{InstancesPage-DM0dMQHh.js → InstancesPage-Ce6M33Ba.js} +1 -1
- package/ui-react/assets/{LanPage-B5oThF8O.js → LanPage-wpx7g9la.js} +2 -2
- package/ui-react/assets/OverviewPage-81_tl-DM.js +2 -0
- package/ui-react/assets/{PluginsPage-C1llxTDk.js → PluginsPage-BiGqLhAM.js} +1 -1
- package/ui-react/assets/RouterPage-BGuTrkVi.js +1 -0
- package/ui-react/assets/{SettingsPage-BZG-EvR3.js → SettingsPage-Dtzd41KX.js} +1 -1
- package/ui-react/assets/{TasksPage-haZMZbBh.js → TasksPage-B1eWMGNq.js} +1 -1
- package/ui-react/assets/openExternal-CCkI5Gcp.js +1 -0
- package/ui-react/assets/{refresh-cw-B-VAKpDt.js → refresh-cw-DL1X7yMA.js} +1 -1
- package/ui-react/assets/{rocket-CLAmwatt.js → rocket-USfrjyOj.js} +1 -1
- package/ui-react/assets/{shield-check-CMRwmPpk.js → shield-check-T6r2Ozdx.js} +1 -1
- package/ui-react/assets/{supervisor-BCZW2z-5.js → supervisor-B8Hi4QZf.js} +3 -3
- package/ui-react/assets/supervisor-C1zJELaq.css +2 -0
- package/ui-react/assets/{supervisor-BbSavtNN.js → supervisor-DsqHrafU.js} +1 -1
- package/ui-react/assets/{useSupervisorAction-q0YJmhui.js → useSupervisorAction-bKiRxMdz.js} +1 -1
- package/ui-react/assets/{widgets-BbRkZm-Z.js → widgets-CKp8QAOl.js} +1 -1
- package/ui-react/supervisor.html +3 -3
- package/ui-react/assets/OverviewPage-DH8PtnPI.js +0 -2
- package/ui-react/assets/RouterPage-CUKMV2Q9.js +0 -1
- package/ui-react/assets/openExternal-CzqErnSy.js +0 -1
- 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.
|
|
954
|
+
if (true) return String("0.1.6-BETA.15");
|
|
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
|
-
|
|
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
|
|
|
@@ -4831,1218 +4855,2020 @@ var require_capability_profile = __commonJS({
|
|
|
4831
4855
|
}
|
|
4832
4856
|
});
|
|
4833
4857
|
|
|
4834
|
-
// src/platform/
|
|
4835
|
-
var
|
|
4836
|
-
"src/platform/
|
|
4858
|
+
// src/platform/util/redact.js
|
|
4859
|
+
var require_redact = __commonJS({
|
|
4860
|
+
"src/platform/util/redact.js"(exports2, module2) {
|
|
4837
4861
|
"use strict";
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
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 [];
|
|
4848
4913
|
}
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
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);
|
|
4856
4922
|
}
|
|
4923
|
+
note(key, names.length ? names.length + " \u9879" : "\u65E0\u5B50\u952E");
|
|
4924
|
+
return names;
|
|
4857
4925
|
}
|
|
4858
|
-
function
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
if (isWindows) {
|
|
4864
|
-
ex2.runAsync("taskkill", ["/PID", String(pid), "/T", "/F"], { timeoutMs: 1e4 }).then((r) => {
|
|
4865
|
-
if (cb) cb(r.ok ? null : new Error(r.error || "taskkill failed"));
|
|
4866
|
-
});
|
|
4867
|
-
return;
|
|
4926
|
+
function regValueTargets(runner, note, key) {
|
|
4927
|
+
const out = runner("reg.exe", ["query", key]);
|
|
4928
|
+
if (!out) {
|
|
4929
|
+
note(key, "unreadable");
|
|
4930
|
+
return [];
|
|
4868
4931
|
}
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
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);
|
|
4876
4940
|
}
|
|
4877
|
-
|
|
4941
|
+
note(key, paths.length ? paths.length + " \u4E2A\u80FD\u529B\u8DEF\u5F84" : "\u65E0\u503C");
|
|
4942
|
+
return paths;
|
|
4878
4943
|
}
|
|
4879
|
-
module2.exports = {
|
|
4944
|
+
module2.exports = {
|
|
4945
|
+
regValueOf,
|
|
4946
|
+
regDwordOf,
|
|
4947
|
+
expandEnvVars,
|
|
4948
|
+
safeRegKeyPart,
|
|
4949
|
+
REG_HIVE_ALIAS,
|
|
4950
|
+
regKeyFull,
|
|
4951
|
+
regValue,
|
|
4952
|
+
regSubkeys,
|
|
4953
|
+
regValueTargets
|
|
4954
|
+
};
|
|
4880
4955
|
}
|
|
4881
4956
|
});
|
|
4882
4957
|
|
|
4883
|
-
// src/platform/os/
|
|
4884
|
-
var
|
|
4885
|
-
"src/platform/os/
|
|
4958
|
+
// src/platform/os/browser-inventory.js
|
|
4959
|
+
var require_browser_inventory = __commonJS({
|
|
4960
|
+
"src/platform/os/browser-inventory.js"(exports2, module2) {
|
|
4886
4961
|
"use strict";
|
|
4887
4962
|
var fs2 = require("node:fs");
|
|
4888
|
-
var os2 = require("node:os");
|
|
4889
4963
|
var path2 = require("node:path");
|
|
4890
|
-
var
|
|
4891
|
-
var
|
|
4892
|
-
var
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4964
|
+
var os2 = require("node:os");
|
|
4965
|
+
var exec = require_exec();
|
|
4966
|
+
var { isExecutableFile } = require_exec_path();
|
|
4967
|
+
var registry = require_registry();
|
|
4968
|
+
var { expandEnvVars, safeRegKeyPart, regValue, regSubkeys, regValueTargets } = registry;
|
|
4969
|
+
var PROBE_TIMEOUT_MS = 1500;
|
|
4970
|
+
function engineOf(bin) {
|
|
4971
|
+
const base = String(bin || "").toLowerCase().split(/[\\/]/).pop();
|
|
4972
|
+
if (/(chrome|chromium|msedge|edge|brave|vivaldi|opera|thorium)/.test(base)) return "chromium";
|
|
4973
|
+
if (/(firefox|librewolf|waterfox)/.test(base)) return "firefox";
|
|
4974
|
+
return "other";
|
|
4898
4975
|
}
|
|
4899
|
-
function
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4976
|
+
function tokenizeExec(line) {
|
|
4977
|
+
const toks = [];
|
|
4978
|
+
let cur = "";
|
|
4979
|
+
let q = null;
|
|
4980
|
+
let esc = false;
|
|
4981
|
+
let has = false;
|
|
4982
|
+
for (const ch of String(line)) {
|
|
4983
|
+
if (esc) {
|
|
4984
|
+
cur += ch;
|
|
4985
|
+
esc = false;
|
|
4986
|
+
continue;
|
|
4905
4987
|
}
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
try {
|
|
4911
|
-
fs2.chmodSync(file, 384);
|
|
4912
|
-
return { ok: true, mode: "posix-0600" };
|
|
4913
|
-
} catch (e) {
|
|
4914
|
-
return { ok: false, mode: "posix-0600", reason: e.message };
|
|
4988
|
+
if (ch === "\\") {
|
|
4989
|
+
esc = true;
|
|
4990
|
+
has = true;
|
|
4991
|
+
continue;
|
|
4915
4992
|
}
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
const r = ex2.runDetail("icacls", [file, "/inheritance:r", "/grant:r", user + ":F"], { stdio: "ignore", timeoutMs: 5e3 });
|
|
4921
|
-
return r.ok ? { ok: true, mode: "icacls-file" } : { ok: false, mode: "icacls-file", reason: r.error || "\u9000\u51FA\u7801 " + r.code };
|
|
4922
|
-
}
|
|
4923
|
-
function protectDir(dir) {
|
|
4924
|
-
if (!IS_WINDOWS2) {
|
|
4925
|
-
try {
|
|
4926
|
-
fs2.chmodSync(dir, 448);
|
|
4927
|
-
return { ok: true, mode: "posix-0700" };
|
|
4928
|
-
} catch (e) {
|
|
4929
|
-
return { ok: false, mode: "posix-0700", reason: e.message };
|
|
4993
|
+
if (q) {
|
|
4994
|
+
if (ch === q) q = null;
|
|
4995
|
+
else cur += ch;
|
|
4996
|
+
continue;
|
|
4930
4997
|
}
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
const r = ex2.runDetail("icacls", [dir, "/inheritance:r", "/grant:r", user + ":(OI)(CI)F"], { stdio: "ignore", timeoutMs: 1e4 });
|
|
4936
|
-
return r.ok ? { ok: true, mode: "icacls-dir" } : { ok: false, mode: "icacls-dir", reason: r.error || "\u9000\u51FA\u7801 " + r.code };
|
|
4937
|
-
}
|
|
4938
|
-
function ensurePrivateDir(dir) {
|
|
4939
|
-
try {
|
|
4940
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
4941
|
-
} catch (e) {
|
|
4942
|
-
return { ok: false, mode: "mkdir", reason: e.message };
|
|
4943
|
-
}
|
|
4944
|
-
return protectDir(dir);
|
|
4945
|
-
}
|
|
4946
|
-
function writePrivate(file, data) {
|
|
4947
|
-
try {
|
|
4948
|
-
const dir = path2.dirname(file);
|
|
4949
|
-
try {
|
|
4950
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
4951
|
-
} catch {
|
|
4998
|
+
if (ch === "'" || ch === '"') {
|
|
4999
|
+
q = ch;
|
|
5000
|
+
has = true;
|
|
5001
|
+
continue;
|
|
4952
5002
|
}
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
fs2.rmSync(tmp, { force: true });
|
|
4959
|
-
} catch {
|
|
5003
|
+
if (/\s/.test(ch)) {
|
|
5004
|
+
if (has) {
|
|
5005
|
+
toks.push(cur);
|
|
5006
|
+
cur = "";
|
|
5007
|
+
has = false;
|
|
4960
5008
|
}
|
|
4961
|
-
|
|
5009
|
+
continue;
|
|
4962
5010
|
}
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
if (p2 && p2.ok === false) return { ok: false, reason: "protect(file): " + (p2.reason || p2.mode), mode: p2.mode };
|
|
4966
|
-
return { ok: true };
|
|
4967
|
-
} catch (e) {
|
|
4968
|
-
return { ok: false, reason: e.message };
|
|
5011
|
+
cur += ch;
|
|
5012
|
+
has = true;
|
|
4969
5013
|
}
|
|
5014
|
+
if (has) toks.push(cur);
|
|
5015
|
+
return toks;
|
|
4970
5016
|
}
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
"src/platform/util/input.js"(exports2, module2) {
|
|
4978
|
-
"use strict";
|
|
4979
|
-
var PKG_NAME_RE = /^(@[a-zA-Z0-9._-]+\/)?[a-zA-Z0-9._-]+$/;
|
|
4980
|
-
var ARGV_UNSAFE_RE = /[\s;|&<>`'"$(){}\\*?~#]/;
|
|
4981
|
-
var WIN_ABS_PATH_RE = /^[A-Za-z]:\\[^;|&<>`'"$*?~#\s]*$/;
|
|
4982
|
-
var UNIT_NAME_RE = /^[A-Za-z0-9:@._-]{1,128}$/;
|
|
4983
|
-
var UNSAFE_LEDGER_KEYS = ["__proto__", "prototype", "constructor"];
|
|
4984
|
-
var LEDGER_UNSAFE_RE = /[\s\u0000-\u001f\u007f]/;
|
|
4985
|
-
function argvViolation(arg) {
|
|
4986
|
-
const s = String(arg == null ? "" : arg);
|
|
4987
|
-
if (!s) return "\u7A7A\u7684 argv \u9879";
|
|
4988
|
-
if (ARGV_UNSAFE_RE.test(s) && !WIN_ABS_PATH_RE.test(s)) return "argv \u9879\u542B\u7981\u7528\u5B57\u7B26\uFF08\u7A7A\u767D/shell \u5143\u5B57\u7B26\uFF09: " + s.slice(0, 80);
|
|
4989
|
-
return null;
|
|
4990
|
-
}
|
|
4991
|
-
var CONTROL_RE = /[\u0000-\u001f\u007f]/;
|
|
4992
|
-
function isAbsoluteForm(p) {
|
|
4993
|
-
const s = String(p == null ? "" : p);
|
|
4994
|
-
return s.charCodeAt(0) === 47 || /^[A-Za-z]:[\\/]/.test(s) || s.charCodeAt(0) === 92 && s.charCodeAt(1) === 92;
|
|
4995
|
-
}
|
|
4996
|
-
function prefixViolation(prefix) {
|
|
4997
|
-
const s = String(prefix == null ? "" : prefix);
|
|
4998
|
-
if (!s.trim()) return "\u7A7A\u7684\u5B89\u88C5\u524D\u7F00";
|
|
4999
|
-
if (CONTROL_RE.test(s)) return "\u5B89\u88C5\u524D\u7F00\u542B\u63A7\u5236\u7B26: " + s.slice(0, 80);
|
|
5000
|
-
if (s.length > 4096) return "\u5B89\u88C5\u524D\u7F00\u8D85\u957F: " + s.slice(0, 60) + "...";
|
|
5001
|
-
if (s[0] === "-") return "\u5B89\u88C5\u524D\u7F00\u4EE5 - \u5F00\u5934\uFF08\u4F1A\u88AB\u5F53\u4F5C npm \u9009\u9879\uFF09: " + s.slice(0, 80);
|
|
5002
|
-
if (!isAbsoluteForm(s)) return "\u5B89\u88C5\u524D\u7F00\u987B\u4E3A\u7EDD\u5BF9\u8DEF\u5F84\uFF08/ \u5F00\u5934\u3001win \u76D8\u7B26\u6216 UNC\uFF09: " + s.slice(0, 80);
|
|
5003
|
-
return null;
|
|
5004
|
-
}
|
|
5005
|
-
function pkgNameViolation(pkg) {
|
|
5006
|
-
const s = String(pkg == null ? "" : pkg);
|
|
5007
|
-
return PKG_NAME_RE.test(s) ? null : "\u975E\u6CD5\u5305\u540D\uFF08\u5B57\u7B26\u96C6\u767D\u540D\u5355\u4E0D\u901A\u8FC7\uFF09: " + s.slice(0, 80);
|
|
5008
|
-
}
|
|
5009
|
-
function unitNameViolation(unit) {
|
|
5010
|
-
const s = String(unit == null ? "" : unit);
|
|
5011
|
-
if (!UNIT_NAME_RE.test(s)) return "\u975E\u6CD5\u5355\u5143\u540D\uFF08\u5B57\u7B26\u96C6/\u957F\u5EA6\u767D\u540D\u5355\u4E0D\u901A\u8FC7\uFF09: " + s.slice(0, 80);
|
|
5012
|
-
const dot = s.indexOf(".");
|
|
5013
|
-
if (dot >= 0 && !/\.service$/.test(s)) return "\u5355\u5143\u540D\u540E\u7F00\u4E0D\u53D7\u652F\u6301\uFF08\u4EC5\u5141\u8BB8 .service \u6216\u65E0\u540E\u7F00\uFF09: " + s.slice(0, 80);
|
|
5014
|
-
return null;
|
|
5015
|
-
}
|
|
5016
|
-
function ledgerKey(raw, opts) {
|
|
5017
|
-
const o = opts || {};
|
|
5018
|
-
const max = typeof o.max === "number" ? o.max : 128;
|
|
5019
|
-
const empty = o.empty == null ? "unknown" : o.empty;
|
|
5020
|
-
const unsafe = o.unsafe == null ? "(other)" : o.unsafe;
|
|
5021
|
-
if (typeof raw !== "string" || !raw) return empty;
|
|
5022
|
-
const s = raw.length > max ? raw.slice(0, max) : raw;
|
|
5023
|
-
if (UNSAFE_LEDGER_KEYS.indexOf(s) >= 0 || LEDGER_UNSAFE_RE.test(s)) return unsafe;
|
|
5024
|
-
return s;
|
|
5025
|
-
}
|
|
5026
|
-
module2.exports = {
|
|
5027
|
-
PKG_NAME_RE,
|
|
5028
|
-
ARGV_UNSAFE_RE,
|
|
5029
|
-
WIN_ABS_PATH_RE,
|
|
5030
|
-
UNIT_NAME_RE,
|
|
5031
|
-
UNSAFE_LEDGER_KEYS,
|
|
5032
|
-
LEDGER_UNSAFE_KEYS_RE: LEDGER_UNSAFE_RE,
|
|
5033
|
-
argvViolation,
|
|
5034
|
-
pkgNameViolation,
|
|
5035
|
-
unitNameViolation,
|
|
5036
|
-
ledgerKey,
|
|
5037
|
-
CONTROL_RE,
|
|
5038
|
-
isAbsoluteForm,
|
|
5039
|
-
prefixViolation
|
|
5040
|
-
};
|
|
5041
|
-
}
|
|
5042
|
-
});
|
|
5043
|
-
|
|
5044
|
-
// src/platform/os/spawn.js
|
|
5045
|
-
var require_spawn = __commonJS({
|
|
5046
|
-
"src/platform/os/spawn.js"(exports2, module2) {
|
|
5047
|
-
"use strict";
|
|
5048
|
-
var { spawn } = require("node:child_process");
|
|
5049
|
-
function detached(cmd2, args, opts) {
|
|
5050
|
-
const o = opts || {};
|
|
5051
|
-
return spawn(cmd2, args, Object.assign({}, o, {
|
|
5052
|
-
detached: true,
|
|
5053
|
-
windowsHide: true,
|
|
5054
|
-
// 默认 ignore;显式 undefined 也回落 ignore,避免 stdio:undefined 的歧义。
|
|
5055
|
-
stdio: o.stdio === void 0 ? "ignore" : o.stdio
|
|
5056
|
-
}));
|
|
5057
|
-
}
|
|
5058
|
-
function piped(cmd2, args, opts) {
|
|
5059
|
-
const o = opts || {};
|
|
5060
|
-
return spawn(cmd2, args, Object.assign({}, o, {
|
|
5061
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
5062
|
-
windowsHide: true,
|
|
5063
|
-
detached: o.detached === true
|
|
5064
|
-
}));
|
|
5065
|
-
}
|
|
5066
|
-
function detachedIgnored(cmd2, args, opts) {
|
|
5067
|
-
const o = opts || {};
|
|
5068
|
-
return spawn(cmd2, args, Object.assign({}, o, {
|
|
5069
|
-
detached: true,
|
|
5070
|
-
windowsHide: true,
|
|
5071
|
-
stdio: "ignore"
|
|
5072
|
-
}));
|
|
5073
|
-
}
|
|
5074
|
-
module2.exports = { detached, piped, detachedIgnored };
|
|
5075
|
-
}
|
|
5076
|
-
});
|
|
5077
|
-
|
|
5078
|
-
// src/platform/os/portable.js
|
|
5079
|
-
var require_portable = __commonJS({
|
|
5080
|
-
"src/platform/os/portable.js"(exports2, module2) {
|
|
5081
|
-
"use strict";
|
|
5082
|
-
var fs2 = require("node:fs");
|
|
5083
|
-
var pidlookup = require_pidlookup();
|
|
5084
|
-
var spawner = require_spawn();
|
|
5085
|
-
var procOS = require_process();
|
|
5086
|
-
var STOP_WAIT_CAP_MS = 3e3;
|
|
5087
|
-
var NAP_MS = 150;
|
|
5088
|
-
function nap(ms) {
|
|
5089
|
-
try {
|
|
5090
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
5091
|
-
} catch {
|
|
5092
|
-
}
|
|
5093
|
-
}
|
|
5094
|
-
function readPidFile(pidFile) {
|
|
5095
|
-
if (!pidFile) return null;
|
|
5096
|
-
try {
|
|
5097
|
-
const n = parseInt(String(fs2.readFileSync(pidFile, "utf8")).trim(), 10);
|
|
5098
|
-
return Number.isInteger(n) && n > 0 ? n : null;
|
|
5099
|
-
} catch {
|
|
5100
|
-
return null;
|
|
5017
|
+
function parseExecLine(line) {
|
|
5018
|
+
let toks = tokenizeExec(line).filter((t) => t !== "%%" && !/^%[a-zA-Z]$/.test(t));
|
|
5019
|
+
if (toks[0] === "env") {
|
|
5020
|
+
let i = 1;
|
|
5021
|
+
while (i < toks.length && /=/.test(toks[i])) i++;
|
|
5022
|
+
toks = toks.slice(i);
|
|
5101
5023
|
}
|
|
5024
|
+
if (!toks.length) return null;
|
|
5025
|
+
return { bin: toks[0], baseArgs: toks.slice(1) };
|
|
5102
5026
|
}
|
|
5103
|
-
function
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
if (
|
|
5107
|
-
|
|
5027
|
+
function exeFromCmdLine(cmdLine) {
|
|
5028
|
+
const s = String(cmdLine || "");
|
|
5029
|
+
let m = s.match(/^\s*"([^"]+\.exe)"/i);
|
|
5030
|
+
if (m) return m[1];
|
|
5031
|
+
m = s.match(/^\s*(.*?\.exe)/i);
|
|
5032
|
+
return m ? m[1] : null;
|
|
5108
5033
|
}
|
|
5109
|
-
var
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
const anchors = anchorsOf(o);
|
|
5117
|
-
const fp = readPidFile(pidFileOf(o));
|
|
5118
|
-
if (fp !== null && pidlookup.isAlive(fp) && matchesAnchors(fp, anchors)) return { pid: fp, ownGroup: true };
|
|
5119
|
-
const port = portOf(o);
|
|
5120
|
-
if (port) {
|
|
5121
|
-
const q = pidlookup.findListeningPid(port);
|
|
5122
|
-
if (q !== null && q !== fp && matchesAnchors(q, anchors)) return { pid: q, ownGroup: false };
|
|
5034
|
+
var WIN_APP_PATHS = ["msedge.exe", "chrome.exe", "firefox.exe", "brave.exe", "opera.exe", "vivaldi.exe", "chromium.exe", "thorium.exe", "librewolf.exe"];
|
|
5035
|
+
function winExeOfProgId(runner, note, progId, env) {
|
|
5036
|
+
if (!safeRegKeyPart(progId)) return null;
|
|
5037
|
+
for (const root of ["HKCU\\Software\\Classes", "HKLM\\Software\\Classes"]) {
|
|
5038
|
+
const cmd2 = regValue(runner, note, root + "\\" + progId + "\\shell\\open\\command");
|
|
5039
|
+
const exe = cmd2 ? exeFromCmdLine(expandEnvVars(cmd2, env)) : null;
|
|
5040
|
+
if (exe) return exe;
|
|
5123
5041
|
}
|
|
5124
5042
|
return null;
|
|
5125
5043
|
}
|
|
5126
|
-
function
|
|
5127
|
-
const
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5044
|
+
function probeWin(d) {
|
|
5045
|
+
const { runOut, env, canExec } = d;
|
|
5046
|
+
const notes = [];
|
|
5047
|
+
const note = (source, detail) => {
|
|
5048
|
+
notes.push({ source, detail });
|
|
5049
|
+
};
|
|
5050
|
+
const found = /* @__PURE__ */ new Map();
|
|
5051
|
+
const keyOf = (exe) => String(exe || "").toLowerCase().replace(/[\\/]/g, "\\");
|
|
5052
|
+
const push = (exe, how) => {
|
|
5053
|
+
const full = expandEnvVars(exe, env).trim();
|
|
5054
|
+
if (!/\.exe$/i.test(full)) {
|
|
5055
|
+
note(how, "\u6765\u6E90\u7ED9\u51FA\u7684\u4E0D\u662F exe \u8DEF\u5F84: " + full);
|
|
5056
|
+
return null;
|
|
5132
5057
|
}
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
supportsTransient: true,
|
|
5139
|
-
// 无操作 = 成功(与 systemd 的动词返回形态一致,调用方无需按 provider 分支)。
|
|
5140
|
-
daemonReload() {
|
|
5141
|
-
return true;
|
|
5142
|
-
},
|
|
5143
|
-
resetFailed() {
|
|
5144
|
-
return true;
|
|
5145
|
-
},
|
|
5146
|
-
/** 拉起:spawn(detached)——POSIX 自成进程组(kill(-pid) 语义的前提)、win32 windowsHide +
|
|
5147
|
-
* CREATE_NEW_PROCESS_GROUP(NO-CONSOLE-WINDOW 纪律收口在 platform/os/spawn.js)。
|
|
5148
|
-
* 调用方传来的 props(cgroup 语义)在此如实忽略:本档无内核强制可施加。
|
|
5149
|
-
* @param {{cmd:string[], env?:object, workingDir?:string, pidFile?:string, port?:number, anchors?:string[]}} o */
|
|
5150
|
-
startTransient(o) {
|
|
5151
|
-
const opts = o || {};
|
|
5152
|
-
const cmd2 = opts.cmd || [];
|
|
5153
|
-
if (!cmd2.length) throw new Error("portable \u62C9\u8D77\u62D2\u7EDD\uFF1A\u7A7A\u547D\u4EE4");
|
|
5154
|
-
const child = spawner.detached(cmd2[0], cmd2.slice(1), {
|
|
5155
|
-
cwd: opts.workingDir || void 0,
|
|
5156
|
-
env: Object.assign({}, process.env, opts.env || {})
|
|
5157
|
-
});
|
|
5158
|
-
child.on("error", () => {
|
|
5159
|
-
});
|
|
5160
|
-
if (!child.pid) {
|
|
5161
|
-
try {
|
|
5162
|
-
child.kill();
|
|
5163
|
-
} catch {
|
|
5164
|
-
}
|
|
5165
|
-
throw new Error("portable \u62C9\u8D77\u5931\u8D25\uFF1Aspawn \u672A\u4EA7\u751F\u8FDB\u7A0B");
|
|
5058
|
+
const k = keyOf(full);
|
|
5059
|
+
const hit = found.get(k);
|
|
5060
|
+
if (hit) {
|
|
5061
|
+
if (!hit.sources.includes(how)) hit.sources.push(how);
|
|
5062
|
+
return hit;
|
|
5166
5063
|
}
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5064
|
+
if (!canExec(full)) {
|
|
5065
|
+
note(how, "\u6307\u5411\u7684\u6587\u4EF6\u4E0D\u53EF\u6267\u884C: " + full);
|
|
5066
|
+
return null;
|
|
5170
5067
|
}
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5068
|
+
const item = { id: k, name: path2.basename(full).replace(/\.exe$/i, ""), engine: engineOf(full), bin: full, source: how, sources: [how] };
|
|
5069
|
+
found.set(k, item);
|
|
5070
|
+
note(how, item.name);
|
|
5071
|
+
return item;
|
|
5072
|
+
};
|
|
5073
|
+
const progIds = /* @__PURE__ */ new Map();
|
|
5074
|
+
const addProgId = (pid, how) => {
|
|
5075
|
+
const s = String(pid || "").trim();
|
|
5076
|
+
if (!safeRegKeyPart(s)) return;
|
|
5077
|
+
if (progIds.get(s) === "userchoice") return;
|
|
5078
|
+
progIds.set(s, how);
|
|
5079
|
+
};
|
|
5080
|
+
let defaultId = null;
|
|
5081
|
+
let defaultSource = null;
|
|
5082
|
+
const DEF_RANK = { userchoice: 1, "scheme-association": 2, "only-installed": 9 };
|
|
5083
|
+
let defRank = 99;
|
|
5084
|
+
const setDefault = (item, how) => {
|
|
5085
|
+
const r = DEF_RANK[how] === void 0 ? 99 : DEF_RANK[how];
|
|
5086
|
+
if (item && r < defRank) {
|
|
5087
|
+
defRank = r;
|
|
5088
|
+
defaultId = item.id;
|
|
5089
|
+
defaultSource = how;
|
|
5176
5090
|
}
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
const
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
const
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
}
|
|
5199
|
-
if (Date.now() - t0 >= budget) break;
|
|
5200
|
-
nap(NAP_MS);
|
|
5091
|
+
};
|
|
5092
|
+
const cmdToExe = (raw) => raw ? exeFromCmdLine(expandEnvVars(raw, env)) : null;
|
|
5093
|
+
const uc = regValue(runOut, note, "HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice", "ProgId");
|
|
5094
|
+
if (uc) addProgId(uc, "userchoice");
|
|
5095
|
+
for (const root of ["HKCU\\Software\\Classes", "HKLM\\Software\\Classes"]) {
|
|
5096
|
+
const pid = regValue(runOut, note, root + "\\https");
|
|
5097
|
+
if (pid) addProgId(pid, "scheme-association");
|
|
5098
|
+
const exe = cmdToExe(regValue(runOut, note, root + "\\https\\shell\\open\\command"));
|
|
5099
|
+
if (exe) setDefault(push(exe, "scheme-association"), "scheme-association");
|
|
5100
|
+
}
|
|
5101
|
+
for (const name of regSubkeys(runOut, note, "HKLM\\SOFTWARE\\Clients\\StartMenuInternet")) {
|
|
5102
|
+
addProgId(name, "startmenu-catalog");
|
|
5103
|
+
const exe = cmdToExe(regValue(runOut, note, "HKLM\\SOFTWARE\\Clients\\StartMenuInternet\\" + name + "\\shell\\open\\command")) || winExeOfProgId(runOut, note, name, env);
|
|
5104
|
+
if (exe) push(exe, "startmenu-catalog");
|
|
5105
|
+
}
|
|
5106
|
+
for (const root of ["HKLM\\SOFTWARE", "HKCU\\SOFTWARE"]) {
|
|
5107
|
+
for (const capPath of regValueTargets(runOut, note, root + "\\RegisteredApplications")) {
|
|
5108
|
+
const https = regValue(runOut, note, capPath + "\\UrlAssociations\\https");
|
|
5109
|
+
const sid = regValue(runOut, note, capPath, "StartMenuInternet");
|
|
5110
|
+
const pid = https || sid;
|
|
5111
|
+
if (pid) addProgId(pid, "registered-apps");
|
|
5201
5112
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5113
|
+
}
|
|
5114
|
+
for (const exe of WIN_APP_PATHS) {
|
|
5115
|
+
for (const root of ["HKCU", "HKLM"]) {
|
|
5116
|
+
const v = regValue(runOut, note, root + "\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + exe);
|
|
5117
|
+
if (v && push(v, "app-paths")) break;
|
|
5205
5118
|
}
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5119
|
+
}
|
|
5120
|
+
for (const [pid, how] of progIds) {
|
|
5121
|
+
const exe = winExeOfProgId(runOut, note, pid, env);
|
|
5122
|
+
if (!exe) continue;
|
|
5123
|
+
const item = push(exe, how);
|
|
5124
|
+
if (how === "userchoice") setDefault(item, "userchoice");
|
|
5125
|
+
}
|
|
5126
|
+
if (found.size === 1) setDefault([...found.values()][0], "only-installed");
|
|
5127
|
+
return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
|
|
5128
|
+
}
|
|
5129
|
+
var MAC_JXA_LIST = [
|
|
5130
|
+
"ObjC.import('Foundation');",
|
|
5131
|
+
"var ws=$.NSWorkspace.sharedWorkspace;",
|
|
5132
|
+
"var u=$.NSURL.URLWithString('https://dsh.local/probe');",
|
|
5133
|
+
"var out=[];var def='';",
|
|
5134
|
+
"try{var d=ws.URLForApplicationToOpenURL(u); if(!d.isNil()) def=ObjC.unwrap(d.path)||'';}catch(e){}",
|
|
5135
|
+
"var list=null;",
|
|
5136
|
+
"try{list=ws.urlsForApplicationsToOpenURL(u);}catch(e){}",
|
|
5137
|
+
"if(list===null||typeof list.count!=='number'){if(def){out.push(def+'\\t\\t1');}}else{",
|
|
5138
|
+
"var n=Number(list.count);",
|
|
5139
|
+
"for(var i=0;i<n;i++){",
|
|
5140
|
+
"var p=ObjC.unwrap(list.objectAtIndex(i).path)||''; if(!p) continue;",
|
|
5141
|
+
"var exe='';var bid='';",
|
|
5142
|
+
"var b=$.NSBundle.bundleWithPath(p);",
|
|
5143
|
+
"if(!b.isNil()){exe=ObjC.unwrap(b.executablePath)||'';bid=ObjC.unwrap(b.bundleIdentifier)||'';}",
|
|
5144
|
+
"out.push((exe||p)+'\\t'+bid+'\\t'+(p&&def&&p===def?1:0));}",
|
|
5145
|
+
"}",
|
|
5146
|
+
// 末句必须是裸表达式:osascript 取的是最后一条**语句**的值,if/else 语句不产结果,
|
|
5147
|
+
// 写在分支里会让整段查询输出空字符串(表现为「系统未报任何可用应用」)。
|
|
5148
|
+
"out.join('\\n');"
|
|
5149
|
+
].join("");
|
|
5150
|
+
function probeMac(d) {
|
|
5151
|
+
const { runOut, canExec } = d;
|
|
5152
|
+
const notes = [];
|
|
5153
|
+
const note = (source, detail) => {
|
|
5154
|
+
notes.push({ source, detail });
|
|
5155
|
+
};
|
|
5156
|
+
const out = runOut("osascript", ["-l", "JavaScript", "-e", MAC_JXA_LIST]);
|
|
5157
|
+
if (!out || !out.trim()) {
|
|
5158
|
+
note("launchservices", "\u67E5\u8BE2\u65E0\u8F93\u51FA\uFF08\u65E7\u7CFB\u7EDF\u6216\u88AB\u6743\u9650\u62E6\u622A\uFF09");
|
|
5159
|
+
return { browsers: [], defaultId: null, defaultSource: null, probed: notes };
|
|
5160
|
+
}
|
|
5161
|
+
const keyOf = (exe) => String(exe || "").toLowerCase();
|
|
5162
|
+
const found = /* @__PURE__ */ new Map();
|
|
5163
|
+
let defaultId = null;
|
|
5164
|
+
let defaultSource = null;
|
|
5165
|
+
for (const line of out.trim().split("\n")) {
|
|
5166
|
+
const parts = line.split(" ");
|
|
5167
|
+
const bin = (parts[0] || "").trim();
|
|
5168
|
+
if (!bin) continue;
|
|
5169
|
+
if (!canExec(bin)) {
|
|
5170
|
+
note("launchservices", "\u4E0D\u53EF\u6267\u884C: " + bin);
|
|
5171
|
+
continue;
|
|
5210
5172
|
}
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
const opts = o || {};
|
|
5219
|
-
if (findOurs(opts)) return true;
|
|
5220
|
-
const port = portOf(opts);
|
|
5221
|
-
const pidFile = pidFileOf(opts);
|
|
5222
|
-
const anchors = anchorsOf(opts);
|
|
5223
|
-
if (!port && !pidFile) return null;
|
|
5224
|
-
if (!anchors.length) {
|
|
5225
|
-
const fp2 = readPidFile(pidFile);
|
|
5226
|
-
if (fp2 !== null && pidlookup.isAlive(fp2)) return true;
|
|
5227
|
-
if (port) {
|
|
5228
|
-
const q = pidlookup.findListeningPid(port);
|
|
5229
|
-
if (q !== null) return true;
|
|
5230
|
-
if (fp2 !== null) return false;
|
|
5231
|
-
return null;
|
|
5173
|
+
const k = keyOf(bin);
|
|
5174
|
+
if (!found.has(k)) found.set(k, { id: k, name: path2.basename(bin), engine: engineOf(bin), bin, source: "launchservices", sources: ["launchservices"], isDefault: false });
|
|
5175
|
+
if (parts[2] === "1") {
|
|
5176
|
+
found.get(k).isDefault = true;
|
|
5177
|
+
if (!defaultId) {
|
|
5178
|
+
defaultId = k;
|
|
5179
|
+
defaultSource = "launchservices";
|
|
5232
5180
|
}
|
|
5233
|
-
return fp2 !== null ? false : null;
|
|
5234
5181
|
}
|
|
5235
|
-
const fp = readPidFile(pidFile);
|
|
5236
|
-
if (fp !== null && pidlookup.isAlive(fp) && !pidlookup.readCmdline(fp)) return null;
|
|
5237
|
-
return false;
|
|
5238
|
-
},
|
|
5239
|
-
// portable 无 systemd transient 单元文件;run.pid 的清理归 cleanTransient。
|
|
5240
|
-
transientUnitFile() {
|
|
5241
|
-
return null;
|
|
5242
|
-
},
|
|
5243
|
-
/** 清理同名残留:先停掉仍活着的旧进程(重启自愈路径:旧进程未监听端口时端口探测拦不住),再清 run.pid。 */
|
|
5244
|
-
cleanTransient(unit, o) {
|
|
5245
|
-
const opts = o || {};
|
|
5246
|
-
const errors = [];
|
|
5247
|
-
if (this.stopUnit(unit, Object.assign({}, opts, { timeoutMs: 1500 })) === false) errors.push("stop-unconfirmed");
|
|
5248
|
-
removePidFile(opts);
|
|
5249
|
-
return { ok: errors.length === 0, errors };
|
|
5250
|
-
},
|
|
5251
|
-
/** 返回值只表示「是否改动了内核强制」;governor 不因 false 走降级分支 —— 违规处置链本就平台无关。 */
|
|
5252
|
-
setLimits() {
|
|
5253
|
-
return false;
|
|
5254
5182
|
}
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
// src/platform/os/service.js
|
|
5261
|
-
var require_service = __commonJS({
|
|
5262
|
-
"src/platform/os/service.js"(exports2, module2) {
|
|
5263
|
-
"use strict";
|
|
5264
|
-
var fs2 = require("node:fs");
|
|
5265
|
-
var os2 = require("node:os");
|
|
5266
|
-
var path2 = require("node:path");
|
|
5267
|
-
var exec = require_exec();
|
|
5268
|
-
var input = require_input();
|
|
5269
|
-
var execPath = require_exec_path();
|
|
5270
|
-
var { portable } = require_portable();
|
|
5271
|
-
var CapabilityError = class extends Error {
|
|
5272
|
-
constructor(msg) {
|
|
5273
|
-
super(msg);
|
|
5274
|
-
this.name = "CapabilityError";
|
|
5275
|
-
this.code = "CAPABILITY_UNSUPPORTED";
|
|
5183
|
+
note("launchservices", found.size ? found.size + " \u4E2A\u53EF\u6253\u5F00 https \u7684\u5E94\u7528" : "\u7CFB\u7EDF\u672A\u62A5\u4EFB\u4F55\u53EF\u7528\u5E94\u7528");
|
|
5184
|
+
if (!defaultId && found.size === 1) {
|
|
5185
|
+
defaultId = [...found.keys()][0];
|
|
5186
|
+
defaultSource = "only-installed";
|
|
5276
5187
|
}
|
|
5277
|
-
|
|
5278
|
-
var PLATFORM = process.platform;
|
|
5279
|
-
function run(cmd2, args, opts) {
|
|
5280
|
-
return exec.run(cmd2, args, opts || {});
|
|
5188
|
+
return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
|
|
5281
5189
|
}
|
|
5282
|
-
var
|
|
5283
|
-
function
|
|
5284
|
-
|
|
5190
|
+
var PJ = path2.posix.join;
|
|
5191
|
+
function desktopDirs(env, home) {
|
|
5192
|
+
const dataHome = env.XDG_DATA_HOME || PJ(home, ".local", "share");
|
|
5193
|
+
const dataDirs = String(env.XDG_DATA_DIRS || "/usr/local/share:/usr/share").split(":").filter(Boolean);
|
|
5194
|
+
const dirs = [[dataHome, "xdg"], ...dataDirs.map((p) => [p, "xdg"])];
|
|
5195
|
+
dirs.push([PJ(home, ".local", "share"), "xdg"]);
|
|
5196
|
+
dirs.push(["/var/lib/flatpak/exports/share", "flatpak"]);
|
|
5197
|
+
dirs.push([PJ(home, ".local", "share", "flatpak", "exports", "share"), "flatpak"]);
|
|
5198
|
+
dirs.push(["/var/lib/snapd/desktop", "snap"]);
|
|
5199
|
+
const seen = [];
|
|
5200
|
+
return dirs.map(([root, kind]) => ({ dir: PJ(root, "applications"), kind })).filter((x) => {
|
|
5201
|
+
if (seen.includes(x.dir)) return false;
|
|
5202
|
+
seen.push(x.dir);
|
|
5203
|
+
return true;
|
|
5204
|
+
});
|
|
5285
5205
|
}
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
} catch {
|
|
5294
|
-
return false;
|
|
5295
|
-
}
|
|
5296
|
-
},
|
|
5297
|
-
stopUnit(unit, opts) {
|
|
5298
|
-
if (unitNameViolation(unit)) return false;
|
|
5299
|
-
const o = opts || {};
|
|
5300
|
-
try {
|
|
5301
|
-
return run("systemctl", ["--user", "stop", unit], { timeoutMs: o.timeoutMs || 15e3 }) !== null;
|
|
5302
|
-
} catch {
|
|
5303
|
-
return false;
|
|
5304
|
-
}
|
|
5305
|
-
},
|
|
5306
|
-
// run() 失败返回 null 而不抛,try/catch 包裹会恒返回 true;如实回传成败。
|
|
5307
|
-
resetFailed(unit) {
|
|
5308
|
-
if (unitNameViolation(unit)) return false;
|
|
5309
|
-
return run("systemctl", ["--user", "reset-failed", unit], { timeoutMs: 1e4 }) !== null;
|
|
5310
|
-
},
|
|
5311
|
-
/** 单元活跃判定(三态):确认 active -> true;确认不活跃 -> false;查询未完成 -> null(未知)。
|
|
5312
|
-
* run() 失败只返回 null,「查询未完成」不可折成 false:instance/ops.js 删除数据目录前
|
|
5313
|
-
* 按「!== false 才放行删除」消费本函数,折假即超时窗口内误删沙箱数据。 */
|
|
5314
|
-
isUnitActive(unit) {
|
|
5315
|
-
if (!unit) return true;
|
|
5316
|
-
if (unitNameViolation(unit)) return false;
|
|
5317
|
-
try {
|
|
5318
|
-
const r = exec.runDetail("systemctl", ["--user", "is-active", unit], { timeoutMs: 8e3 });
|
|
5319
|
-
if (r.timedOut) return null;
|
|
5320
|
-
const state = String(r.stdout || "").trim();
|
|
5321
|
-
if (state === "active") return true;
|
|
5322
|
-
if (state || r.code !== null) return false;
|
|
5323
|
-
return null;
|
|
5324
|
-
} catch {
|
|
5325
|
-
return null;
|
|
5326
|
-
}
|
|
5327
|
-
},
|
|
5328
|
-
transientUnitFile(unit) {
|
|
5329
|
-
if (unitNameViolation(unit)) return null;
|
|
5330
|
-
let uid = 0;
|
|
5331
|
-
try {
|
|
5332
|
-
uid = os2.userInfo().uid;
|
|
5333
|
-
} catch {
|
|
5206
|
+
function browserFromDesktop(text, kind, file) {
|
|
5207
|
+
let inMain = false;
|
|
5208
|
+
const got = {};
|
|
5209
|
+
for (const line of String(text).split(/\r?\n/)) {
|
|
5210
|
+
if (/^\[Desktop Entry\]\s*$/.test(line)) {
|
|
5211
|
+
inMain = true;
|
|
5212
|
+
continue;
|
|
5334
5213
|
}
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
* 硬失败只有删单元文件与 daemon-reload(run() 失败返回 null 不抛,成败须如实回传)。 */
|
|
5342
|
-
cleanTransient(unit) {
|
|
5343
|
-
const errors = [];
|
|
5344
|
-
const bad = unitNameViolation(unit);
|
|
5345
|
-
if (bad) return { ok: false, errors: [bad] };
|
|
5346
|
-
exec.runDetail("systemctl", ["--user", "stop", unit + ".service"], { timeoutMs: 1e4 });
|
|
5347
|
-
exec.runDetail("systemctl", ["--user", "reset-failed", unit + ".service"], { timeoutMs: 1e4 });
|
|
5348
|
-
let unlinkOk = true;
|
|
5349
|
-
try {
|
|
5350
|
-
const f = this.transientUnitFile(unit);
|
|
5351
|
-
if (f) fs2.unlinkSync(f);
|
|
5352
|
-
} catch {
|
|
5353
|
-
unlinkOk = false;
|
|
5354
|
-
errors.push("unlink " + unit + ".service");
|
|
5355
|
-
}
|
|
5356
|
-
if (!exec.runDetail("systemctl", ["--user", "daemon-reload"], { timeoutMs: 15e3 }).ok) errors.push("daemon-reload");
|
|
5357
|
-
return { ok: unlinkOk && errors.length === 0, errors };
|
|
5358
|
-
},
|
|
5359
|
-
/** 以 transient 单元启动(独立 cgroup;每实例隔离)。
|
|
5360
|
-
* @param {{unit:string, cmd:string[], env?:object, props?:string[], workingDir?:string, description?:string, timeoutMs?:number}} o
|
|
5361
|
-
* props 为 systemd 属性(如 KillMode=process、MemoryMax=8G),平台层只拼装成 --property,不解释语义。 */
|
|
5362
|
-
startTransient(o) {
|
|
5363
|
-
const opts = o || {};
|
|
5364
|
-
const bad = unitNameViolation(opts.unit);
|
|
5365
|
-
if (bad) throw new Error("systemd-run \u62D2\u7EDD: " + bad);
|
|
5366
|
-
const args = ["--user", "--unit=" + opts.unit];
|
|
5367
|
-
if (opts.description) args.push("--description=" + opts.description);
|
|
5368
|
-
for (const p of opts.props || []) args.push("--property=" + p);
|
|
5369
|
-
for (const [k, v] of Object.entries(opts.env || {})) args.push("--setenv=" + k + "=" + v);
|
|
5370
|
-
if (opts.workingDir) args.push("--working-directory=" + opts.workingDir);
|
|
5371
|
-
args.push("--", ...opts.cmd || []);
|
|
5372
|
-
const r = exec.runDetail("systemd-run", args, { timeoutMs: opts.timeoutMs || 2e4 });
|
|
5373
|
-
if (!r.ok) throw new Error("systemd-run \u5931\u8D25: " + (r.error || "unknown") + (r.stderr ? " | " + String(r.stderr).trim() : ""));
|
|
5374
|
-
return true;
|
|
5375
|
-
},
|
|
5376
|
-
/** 运行期改限额(W3 动态化):systemctl --user set-property 立即生效,无需重启单元。
|
|
5377
|
-
* 必带 --runtime:transient 单元本不落盘,不带它会把 drop-in 写进用户配置目录,陈旧下限永久黏住。
|
|
5378
|
-
* 值只来自 sandbox.unitProps 同源的 alloc(平台层翻译语义、不决定数额)。 */
|
|
5379
|
-
setLimits(unit, alloc) {
|
|
5380
|
-
if (unitNameViolation(unit)) return false;
|
|
5381
|
-
const a = alloc || {};
|
|
5382
|
-
const props = [];
|
|
5383
|
-
if (a.memoryMax) props.push("MemoryMax=" + a.memoryMax);
|
|
5384
|
-
if (a.memoryHigh) props.push("MemoryHigh=" + a.memoryHigh);
|
|
5385
|
-
if (a.cpuQuota) props.push("CPUQuota=" + a.cpuQuota);
|
|
5386
|
-
if (!props.length) return false;
|
|
5387
|
-
return exec.runDetail(
|
|
5388
|
-
"systemctl",
|
|
5389
|
-
["--user", "set-property", "--runtime", unit].concat(props),
|
|
5390
|
-
{ timeoutMs: 1e4 }
|
|
5391
|
-
).ok;
|
|
5214
|
+
if (/^\[/.test(line)) break;
|
|
5215
|
+
if (!inMain) continue;
|
|
5216
|
+
const m = line.match(/^(Exec|Name|TryExec|Categories)=(.*)$/);
|
|
5217
|
+
if (!m) continue;
|
|
5218
|
+
if (m[1] === "Name" && got.Name) continue;
|
|
5219
|
+
got[m[1]] = m[2].trim();
|
|
5392
5220
|
}
|
|
5393
|
-
|
|
5394
|
-
|
|
5221
|
+
if (!got.Exec) return null;
|
|
5222
|
+
const categories = String(got.Categories || "");
|
|
5223
|
+
const parsed = parseExecLine(got.Exec);
|
|
5224
|
+
if (!parsed) return null;
|
|
5225
|
+
const engine = engineOf(parsed.bin);
|
|
5226
|
+
const claimsBrowser = /(^|;)WebBrowser(;|$)/.test(categories);
|
|
5227
|
+
if (!claimsBrowser && engine === "other") return null;
|
|
5228
|
+
if (engine === "other") return null;
|
|
5395
5229
|
return {
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
},
|
|
5405
|
-
resetFailed() {
|
|
5406
|
-
return false;
|
|
5407
|
-
},
|
|
5408
|
-
isUnitActive(unit) {
|
|
5409
|
-
return unit ? false : true;
|
|
5410
|
-
},
|
|
5411
|
-
transientUnitFile() {
|
|
5412
|
-
return null;
|
|
5413
|
-
},
|
|
5414
|
-
// 无 transient 单元可清 = 成功;返回形态与 systemd 一致,调用方无需分支。
|
|
5415
|
-
cleanTransient() {
|
|
5416
|
-
return { ok: true, errors: [] };
|
|
5417
|
-
},
|
|
5418
|
-
startTransient() {
|
|
5419
|
-
throw new CapabilityError(label + "\uFF1A\u4E0D\u652F\u6301\u62C9\u8D77\u5B9E\u4F8B\u8231\uFF08portable \u6863\u672A\u542F\u7528\uFF09");
|
|
5420
|
-
},
|
|
5421
|
-
setLimits() {
|
|
5422
|
-
return false;
|
|
5423
|
-
}
|
|
5230
|
+
// id 恒为归一后的可执行文件路径:defaultId 靠它在清单里定位条目,三平台同一契约
|
|
5231
|
+
id: parsed.bin.toLowerCase(),
|
|
5232
|
+
name: got.Name || path2.basename(file, ".desktop"),
|
|
5233
|
+
engine,
|
|
5234
|
+
bin: parsed.bin,
|
|
5235
|
+
baseArgs: parsed.baseArgs,
|
|
5236
|
+
source: kind,
|
|
5237
|
+
desktopFile: file
|
|
5424
5238
|
};
|
|
5425
5239
|
}
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
if (
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
if (PLATFORM === "linux") return hasSystemdRun() ? systemd : portable;
|
|
5436
|
-
if (PLATFORM === "darwin" || PLATFORM === "win32") return portable;
|
|
5437
|
-
return NONE;
|
|
5438
|
-
}
|
|
5439
|
-
module2.exports = { current, CapabilityError, kind: () => current().kind, PLATFORM, UNIT_NAME_RE, unitNameViolation, _testProviders: { systemd, portable, NONE } };
|
|
5440
|
-
}
|
|
5441
|
-
});
|
|
5442
|
-
|
|
5443
|
-
// src/platform/os/notify.js
|
|
5444
|
-
var require_notify = __commonJS({
|
|
5445
|
-
"src/platform/os/notify.js"(exports2, module2) {
|
|
5446
|
-
"use strict";
|
|
5447
|
-
var spawnOS = require_spawn();
|
|
5448
|
-
function appleScriptString(s) {
|
|
5449
|
-
return JSON.stringify(String(s));
|
|
5450
|
-
}
|
|
5451
|
-
function powerShellString(s) {
|
|
5452
|
-
return "'" + String(s).replace(/'/g, "''") + "'";
|
|
5453
|
-
}
|
|
5454
|
-
function notifyCommand(platform, title, body) {
|
|
5455
|
-
const pl = platform || process.platform;
|
|
5456
|
-
const t = String(title == null ? "" : title);
|
|
5457
|
-
const b = String(body == null ? "" : body);
|
|
5458
|
-
if (pl === "linux") {
|
|
5459
|
-
return { cmd: "notify-send", args: ["-a", "dsh-supervisor", t, b] };
|
|
5460
|
-
}
|
|
5461
|
-
if (pl === "darwin") {
|
|
5462
|
-
const script = "display notification " + appleScriptString(b) + " with title " + appleScriptString(t);
|
|
5463
|
-
return { cmd: "osascript", args: ["-e", script] };
|
|
5464
|
-
}
|
|
5465
|
-
if (pl === "win32") {
|
|
5466
|
-
const ps = [
|
|
5467
|
-
'[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null',
|
|
5468
|
-
'[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null',
|
|
5469
|
-
"$n = New-Object System.Windows.Forms.NotifyIcon",
|
|
5470
|
-
"$n.Icon = [System.Drawing.SystemIcons]::Information",
|
|
5471
|
-
"$n.Visible = $true",
|
|
5472
|
-
"$n.ShowBalloonTip(4000, " + powerShellString(t) + ", " + powerShellString(b) + ", [System.Windows.Forms.ToolTipIcon]::None)",
|
|
5473
|
-
"Start-Sleep -Milliseconds 4200",
|
|
5474
|
-
"$n.Dispose(); $n.Visible = $false"
|
|
5475
|
-
].join("; ");
|
|
5476
|
-
return { cmd: "powershell", args: ["-NoProfile", "-NonInteractive", "-Command", ps] };
|
|
5240
|
+
function resolveLinuxBin(bin, canExec, env) {
|
|
5241
|
+
const b = String(bin || "").trim();
|
|
5242
|
+
if (!b) return null;
|
|
5243
|
+
if (b.startsWith("/")) return canExec(b) ? b : null;
|
|
5244
|
+
if (b.includes("/")) return null;
|
|
5245
|
+
for (const dir of String(env.PATH || "").split(":")) {
|
|
5246
|
+
if (!dir) continue;
|
|
5247
|
+
const p = PJ(dir.replace(/\/+$/, ""), b);
|
|
5248
|
+
if (canExec(p)) return p;
|
|
5477
5249
|
}
|
|
5478
5250
|
return null;
|
|
5479
5251
|
}
|
|
5480
|
-
function
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
return false;
|
|
5493
|
-
}
|
|
5494
|
-
}
|
|
5495
|
-
module2.exports = { notify, notifyCommand, appleScriptString, powerShellString };
|
|
5496
|
-
}
|
|
5497
|
-
});
|
|
5498
|
-
|
|
5499
|
-
// src/platform/os/browser-inventory.js
|
|
5500
|
-
var require_browser_inventory = __commonJS({
|
|
5501
|
-
"src/platform/os/browser-inventory.js"(exports2, module2) {
|
|
5502
|
-
"use strict";
|
|
5503
|
-
var fs2 = require("node:fs");
|
|
5504
|
-
var path2 = require("node:path");
|
|
5505
|
-
var os2 = require("node:os");
|
|
5506
|
-
var exec = require_exec();
|
|
5507
|
-
var { isExecutableFile } = require_exec_path();
|
|
5508
|
-
var PROBE_TIMEOUT_MS = 1500;
|
|
5509
|
-
function engineOf(bin) {
|
|
5510
|
-
const base = String(bin || "").toLowerCase().split(/[\\/]/).pop();
|
|
5511
|
-
if (/(chrome|chromium|msedge|edge|brave|vivaldi|opera|thorium)/.test(base)) return "chromium";
|
|
5512
|
-
if (/(firefox|librewolf|waterfox)/.test(base)) return "firefox";
|
|
5513
|
-
return "other";
|
|
5514
|
-
}
|
|
5515
|
-
function tokenizeExec(line) {
|
|
5516
|
-
const toks = [];
|
|
5517
|
-
let cur = "";
|
|
5518
|
-
let q = null;
|
|
5519
|
-
let esc = false;
|
|
5520
|
-
let has = false;
|
|
5521
|
-
for (const ch of String(line)) {
|
|
5522
|
-
if (esc) {
|
|
5523
|
-
cur += ch;
|
|
5524
|
-
esc = false;
|
|
5525
|
-
continue;
|
|
5526
|
-
}
|
|
5527
|
-
if (ch === "\\") {
|
|
5528
|
-
esc = true;
|
|
5529
|
-
has = true;
|
|
5530
|
-
continue;
|
|
5252
|
+
function probeLinux(d) {
|
|
5253
|
+
const { runOut, readFile, exists, listDir, canExec, env, home } = d;
|
|
5254
|
+
const notes = [];
|
|
5255
|
+
const note = (source, detail) => {
|
|
5256
|
+
notes.push({ source, detail });
|
|
5257
|
+
};
|
|
5258
|
+
const found = /* @__PURE__ */ new Map();
|
|
5259
|
+
const usable = (item) => {
|
|
5260
|
+
const resolved = resolveLinuxBin(item.bin, canExec, env);
|
|
5261
|
+
if (!resolved) {
|
|
5262
|
+
note(item.source, "\u4E0D\u53EF\u6267\u884C: " + item.bin);
|
|
5263
|
+
return null;
|
|
5531
5264
|
}
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5265
|
+
const k = resolved.toLowerCase();
|
|
5266
|
+
const hit = found.get(k);
|
|
5267
|
+
if (hit) {
|
|
5268
|
+
if (!hit.sources.includes(item.source)) hit.sources.push(item.source);
|
|
5269
|
+
return hit;
|
|
5536
5270
|
}
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5271
|
+
const v = Object.assign({}, item, { id: k, bin: resolved, sources: [item.source] });
|
|
5272
|
+
found.set(k, v);
|
|
5273
|
+
return v;
|
|
5274
|
+
};
|
|
5275
|
+
for (const { dir, kind } of desktopDirs(env, home)) {
|
|
5276
|
+
let names = [];
|
|
5277
|
+
try {
|
|
5278
|
+
names = listDir(dir).filter((f) => f.endsWith(".desktop"));
|
|
5279
|
+
} catch {
|
|
5540
5280
|
continue;
|
|
5541
5281
|
}
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5282
|
+
for (const f of names) {
|
|
5283
|
+
let text = null;
|
|
5284
|
+
try {
|
|
5285
|
+
text = readFile(PJ(dir, f));
|
|
5286
|
+
} catch {
|
|
5287
|
+
continue;
|
|
5288
|
+
}
|
|
5289
|
+
const item = browserFromDesktop(text, kind, f);
|
|
5290
|
+
if (item) usable(item);
|
|
5549
5291
|
}
|
|
5550
|
-
cur += ch;
|
|
5551
|
-
has = true;
|
|
5552
5292
|
}
|
|
5553
|
-
|
|
5554
|
-
|
|
5293
|
+
let defaultId = null;
|
|
5294
|
+
let defaultSource = null;
|
|
5295
|
+
const fromMime = linuxDefaultFromMimeApps(readFile, exists, env, home, note);
|
|
5296
|
+
if (fromMime) {
|
|
5297
|
+
const item = usable(fromMime);
|
|
5298
|
+
if (item) {
|
|
5299
|
+
defaultId = item.id;
|
|
5300
|
+
defaultSource = "mimeapps";
|
|
5301
|
+
}
|
|
5302
|
+
}
|
|
5303
|
+
const id = runOut("xdg-settings", ["get", "default-web-browser"]);
|
|
5304
|
+
const desktopId = id && String(id).trim();
|
|
5305
|
+
if (!defaultId && desktopId && /^[A-Za-z0-9._-]+\.desktop$/.test(desktopId)) {
|
|
5306
|
+
for (const { dir } of desktopDirs(env, home)) {
|
|
5307
|
+
const p = PJ(dir, desktopId);
|
|
5308
|
+
let text = null;
|
|
5309
|
+
try {
|
|
5310
|
+
if (exists(p)) text = readFile(p);
|
|
5311
|
+
} catch {
|
|
5312
|
+
continue;
|
|
5313
|
+
}
|
|
5314
|
+
if (text === null) continue;
|
|
5315
|
+
const item = browserFromDesktop(text, "xdg-settings", desktopId);
|
|
5316
|
+
const used = item && usable(item);
|
|
5317
|
+
if (used) {
|
|
5318
|
+
defaultId = used.id;
|
|
5319
|
+
defaultSource = "xdg-settings";
|
|
5320
|
+
break;
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
5324
|
+
if (!desktopId) note("xdg-settings", "\u65E0\u8F93\u51FA");
|
|
5325
|
+
else if (defaultSource === "xdg-settings") note("xdg-settings", "\u9ED8\u8BA4=" + defaultId);
|
|
5326
|
+
else note("xdg-settings", "\u62A5\u51FA " + desktopId + (defaultId ? "\uFF08\u9ED8\u8BA4\u5DF2\u7531 " + defaultSource + " \u5B9A\u51FA\uFF09" : " \u4F46\u4E0D\u53EF\u7528"));
|
|
5327
|
+
if (!defaultId && found.size === 1) {
|
|
5328
|
+
defaultId = [...found.keys()][0];
|
|
5329
|
+
defaultSource = "only-installed";
|
|
5330
|
+
}
|
|
5331
|
+
return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
|
|
5555
5332
|
}
|
|
5556
|
-
function
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5333
|
+
function linuxDefaultFromMimeApps(readFile, exists, env, home, note) {
|
|
5334
|
+
const cfgHome = env.XDG_CONFIG_HOME || PJ(home, ".config");
|
|
5335
|
+
const cfgDirs = String(env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").filter(Boolean);
|
|
5336
|
+
const dataDirs = String(env.XDG_DATA_DIRS || "/usr/local/share:/usr/share").split(":").filter(Boolean);
|
|
5337
|
+
const files = [
|
|
5338
|
+
PJ(cfgHome, "mimeapps.list"),
|
|
5339
|
+
...cfgDirs.map((d) => PJ(d, "mimeapps.list")),
|
|
5340
|
+
...dataDirs.map((d) => PJ(d, "applications", "mimeapps.list"))
|
|
5341
|
+
];
|
|
5342
|
+
for (const f of files) {
|
|
5343
|
+
if (!exists(f)) continue;
|
|
5344
|
+
let text = null;
|
|
5345
|
+
try {
|
|
5346
|
+
text = readFile(f);
|
|
5347
|
+
} catch {
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
const id = mimeAppsDefault(text);
|
|
5351
|
+
if (!id) continue;
|
|
5352
|
+
note("mimeapps", path2.basename(f) + " -> " + id);
|
|
5353
|
+
for (const { dir, kind } of desktopDirs(env, home)) {
|
|
5354
|
+
const p = PJ(dir, id);
|
|
5355
|
+
if (!exists(p)) continue;
|
|
5356
|
+
let dt = null;
|
|
5357
|
+
try {
|
|
5358
|
+
dt = readFile(p);
|
|
5359
|
+
} catch {
|
|
5360
|
+
continue;
|
|
5361
|
+
}
|
|
5362
|
+
const item = browserFromDesktop(dt, kind, id);
|
|
5363
|
+
if (item) return item;
|
|
5364
|
+
}
|
|
5365
|
+
return null;
|
|
5562
5366
|
}
|
|
5563
|
-
|
|
5564
|
-
return
|
|
5367
|
+
note("mimeapps", "\u65E0\u9ED8\u8BA4\u9879");
|
|
5368
|
+
return null;
|
|
5565
5369
|
}
|
|
5566
|
-
function
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5370
|
+
function mimeAppsDefault(text) {
|
|
5371
|
+
let inDefault = false;
|
|
5372
|
+
for (const line of String(text || "").split(/\r?\n/)) {
|
|
5373
|
+
if (/^\s*\[/.test(line)) {
|
|
5374
|
+
inDefault = /^\s*\[Default Applications\]\s*$/.test(line);
|
|
5375
|
+
continue;
|
|
5376
|
+
}
|
|
5377
|
+
if (!inDefault) continue;
|
|
5378
|
+
const m = line.match(/^\s*x-scheme-handler\/https?\s*=\s*(.*)$/i);
|
|
5379
|
+
if (!m) continue;
|
|
5380
|
+
const ids = m[1].split(";").map((s) => s.trim()).filter((s) => /^[A-Za-z0-9._-]+\.desktop$/.test(s));
|
|
5381
|
+
if (ids.length) return ids[0];
|
|
5382
|
+
}
|
|
5383
|
+
return null;
|
|
5570
5384
|
}
|
|
5571
|
-
function
|
|
5572
|
-
const
|
|
5573
|
-
|
|
5385
|
+
function probe(platform, d) {
|
|
5386
|
+
const pl = platform || process.platform;
|
|
5387
|
+
const dd = Object.assign({}, d, { canExec: d.canExec || ((p) => isExecutableFile(p, pl)) });
|
|
5388
|
+
if (pl === "win32") return probeWin(dd);
|
|
5389
|
+
if (pl === "darwin") return probeMac(dd);
|
|
5390
|
+
return probeLinux(dd);
|
|
5574
5391
|
}
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5392
|
+
var CACHE = /* @__PURE__ */ Object.create(null);
|
|
5393
|
+
function inventory(platform, o) {
|
|
5394
|
+
const ov = o || {};
|
|
5395
|
+
const pl = platform || process.platform;
|
|
5396
|
+
const now = ov.now || (() => Date.now());
|
|
5397
|
+
const ttl = ov.ttlMs === void 0 ? 6e4 : ov.ttlMs;
|
|
5398
|
+
const cached = CACHE[pl];
|
|
5399
|
+
if (!ov.force && cached && now() - cached.at < ttl) return Object.assign({ cached: true }, cached.value);
|
|
5400
|
+
const runOut = ov.runOut || ((bin, args) => exec.runOut(bin, args, { timeoutMs: PROBE_TIMEOUT_MS }));
|
|
5401
|
+
const readFile = ov.readFile || ((p) => fs2.readFileSync(p, "utf8"));
|
|
5402
|
+
const exists = ov.exists || ((p) => fs2.existsSync(p));
|
|
5403
|
+
const listDir = ov.listDir || ((p) => fs2.readdirSync(p));
|
|
5404
|
+
let value;
|
|
5405
|
+
try {
|
|
5406
|
+
value = probe(pl, { runOut, readFile, exists, listDir, canExec: ov.canExec, env: ov.env || process.env, home: ov.home || os2.homedir() });
|
|
5407
|
+
} catch (e) {
|
|
5408
|
+
value = { browsers: [], defaultId: null, defaultSource: null, probed: [{ source: "probe-error", detail: String(e && e.message || e) }] };
|
|
5409
|
+
}
|
|
5410
|
+
value.at = now();
|
|
5411
|
+
value.platform = pl;
|
|
5412
|
+
value.browsers.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
5413
|
+
CACHE[pl] = { at: now(), value };
|
|
5414
|
+
return Object.assign({ cached: false }, value);
|
|
5581
5415
|
}
|
|
5582
|
-
function
|
|
5583
|
-
|
|
5416
|
+
function invalidate(platform) {
|
|
5417
|
+
if (platform) delete CACHE[platform];
|
|
5418
|
+
else for (const k of Object.keys(CACHE)) delete CACHE[k];
|
|
5584
5419
|
}
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5420
|
+
module2.exports = {
|
|
5421
|
+
inventory,
|
|
5422
|
+
invalidate,
|
|
5423
|
+
probe,
|
|
5424
|
+
probeWin,
|
|
5425
|
+
probeMac,
|
|
5426
|
+
probeLinux,
|
|
5427
|
+
engineOf,
|
|
5428
|
+
tokenizeExec,
|
|
5429
|
+
parseExecLine,
|
|
5430
|
+
exeFromCmdLine,
|
|
5431
|
+
// 注册表原语住在 ./registry.js(单一实现),此处原样转出:既有门禁与消费方的取用口不变。
|
|
5432
|
+
regValueOf: registry.regValueOf,
|
|
5433
|
+
expandEnvVars,
|
|
5434
|
+
safeRegKeyPart,
|
|
5435
|
+
regKeyFull: registry.regKeyFull,
|
|
5436
|
+
regValue,
|
|
5437
|
+
regSubkeys,
|
|
5438
|
+
regValueTargets,
|
|
5439
|
+
browserFromDesktop,
|
|
5440
|
+
desktopDirs,
|
|
5441
|
+
resolveLinuxBin,
|
|
5442
|
+
mimeAppsDefault,
|
|
5443
|
+
linuxDefaultFromMimeApps,
|
|
5444
|
+
WIN_APP_PATHS,
|
|
5445
|
+
PROBE_TIMEOUT_MS
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5448
|
+
});
|
|
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
|
+
}
|
|
5591
5466
|
}
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
const
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
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
|
+
});
|
|
5598
5482
|
}
|
|
5599
|
-
function
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
const
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
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
|
+
});
|
|
5616
5505
|
}
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
}
|
|
5625
|
-
return null;
|
|
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" };
|
|
5626
5513
|
}
|
|
5627
|
-
function
|
|
5628
|
-
|
|
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
|
+
}
|
|
5629
5527
|
const notes = [];
|
|
5630
5528
|
const note = (source, detail) => {
|
|
5631
5529
|
notes.push({ source, detail });
|
|
5632
5530
|
};
|
|
5633
|
-
const
|
|
5634
|
-
const
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
if (
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
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
|
|
5655
5713
|
};
|
|
5656
|
-
const
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
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
|
+
|
|
5761
|
+
// src/platform/os/environment.js
|
|
5762
|
+
var require_environment = __commonJS({
|
|
5763
|
+
"src/platform/os/environment.js"(exports2, module2) {
|
|
5764
|
+
"use strict";
|
|
5765
|
+
var fs2 = require("node:fs");
|
|
5766
|
+
var os2 = require("node:os");
|
|
5767
|
+
var path2 = require("node:path");
|
|
5768
|
+
var stateRoot2 = require_state_root();
|
|
5769
|
+
var { writeAtomic } = require_fs();
|
|
5770
|
+
var { maskProxyServer, maskProxySecrets } = require_redact();
|
|
5771
|
+
var desktop = require_desktop();
|
|
5772
|
+
var detector = require_browser_inventory();
|
|
5773
|
+
var egress = require_egress();
|
|
5774
|
+
var shellReport = require_shell_report();
|
|
5775
|
+
var { engineOf } = detector;
|
|
5776
|
+
var SCHEMA = 2;
|
|
5777
|
+
var FILE_NAME = "environment.json";
|
|
5778
|
+
var FORM_TTL_MS = 6e4;
|
|
5779
|
+
var SECTION_TTL_MS = 6e4;
|
|
5780
|
+
var SHELL_REPORT_TTL_MS = 1e4;
|
|
5781
|
+
var _sources = {};
|
|
5782
|
+
function bind(sources) {
|
|
5783
|
+
_sources = Object.assign({}, _sources, sources || {});
|
|
5784
|
+
return _sources;
|
|
5785
|
+
}
|
|
5786
|
+
function preferenceId() {
|
|
5787
|
+
const f = _sources.preference;
|
|
5788
|
+
if (typeof f !== "function") return null;
|
|
5789
|
+
const v = f();
|
|
5790
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
5791
|
+
}
|
|
5792
|
+
function capabilities() {
|
|
5793
|
+
const f = _sources.capabilities;
|
|
5794
|
+
if (typeof f !== "function") return null;
|
|
5795
|
+
return f() || null;
|
|
5796
|
+
}
|
|
5797
|
+
function identity() {
|
|
5798
|
+
let user = null;
|
|
5799
|
+
try {
|
|
5800
|
+
user = os2.userInfo().username;
|
|
5801
|
+
} catch {
|
|
5802
|
+
user = process.env.USER || process.env.USERNAME || null;
|
|
5803
|
+
}
|
|
5804
|
+
return {
|
|
5805
|
+
platform: process.platform,
|
|
5806
|
+
arch: process.arch,
|
|
5807
|
+
hostname: os2.hostname(),
|
|
5808
|
+
user: user || null,
|
|
5809
|
+
home: os2.homedir(),
|
|
5810
|
+
node: process.version
|
|
5662
5811
|
};
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5812
|
+
}
|
|
5813
|
+
function paths() {
|
|
5814
|
+
return { root: stateRoot2.root(), supervisor: stateRoot2.supervisorDir(), shell: stateRoot2.shellDir() };
|
|
5815
|
+
}
|
|
5816
|
+
function normalizeInventory(inv, platform) {
|
|
5817
|
+
const list = inv && Array.isArray(inv.browsers) ? inv.browsers : [];
|
|
5818
|
+
const defaultId = inv && inv.defaultId ? inv.defaultId : null;
|
|
5819
|
+
return {
|
|
5820
|
+
platform: inv && inv.platform || platform || process.platform,
|
|
5821
|
+
cached: !!(inv && inv.cached),
|
|
5822
|
+
at: inv && inv.at !== void 0 ? inv.at : null,
|
|
5823
|
+
defaultId,
|
|
5824
|
+
defaultSource: inv && inv.defaultSource || null,
|
|
5825
|
+
browsers: list.map((b) => ({
|
|
5826
|
+
id: b.id,
|
|
5827
|
+
name: b.name,
|
|
5828
|
+
bin: b.bin,
|
|
5829
|
+
engine: b.engine || engineOf(b.bin),
|
|
5830
|
+
baseArgs: b.baseArgs || [],
|
|
5831
|
+
sources: b.sources && b.sources.length ? b.sources : [b.source || "unknown"],
|
|
5832
|
+
isDefault: !!defaultId && b.id === defaultId
|
|
5833
|
+
})),
|
|
5834
|
+
probed: inv && inv.probed || []
|
|
5674
5835
|
};
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5836
|
+
}
|
|
5837
|
+
function browsers(o) {
|
|
5838
|
+
const ov = o || {};
|
|
5839
|
+
const pl = ov.platform || process.platform;
|
|
5840
|
+
if (ov.inventory) return normalizeInventory(ov.inventory, pl);
|
|
5841
|
+
const resolve = typeof ov.resolveInventory === "function" ? ov.resolveInventory : ((platform, deps) => detector.inventory(platform, deps));
|
|
5842
|
+
return normalizeInventory(resolve(pl, ov.resolveDeps || {}), pl);
|
|
5843
|
+
}
|
|
5844
|
+
var ENGINE_RANK = { chromium: 0, firefox: 1, other: 2 };
|
|
5845
|
+
function rankCandidates(list) {
|
|
5846
|
+
const rankOf = (b) => {
|
|
5847
|
+
const r = ENGINE_RANK[b && b.engine];
|
|
5848
|
+
return r === void 0 ? ENGINE_RANK.other : r;
|
|
5849
|
+
};
|
|
5850
|
+
return (Array.isArray(list) ? list : []).slice().sort((a, b) => {
|
|
5851
|
+
if (rankOf(a) !== rankOf(b)) return rankOf(a) - rankOf(b);
|
|
5852
|
+
const ia = String(a && a.id || "");
|
|
5853
|
+
const ib = String(b && b.id || "");
|
|
5854
|
+
return ia < ib ? -1 : ia > ib ? 1 : 0;
|
|
5855
|
+
});
|
|
5856
|
+
}
|
|
5857
|
+
function pickLauncher(platform, inv, preference) {
|
|
5858
|
+
const list = inv && Array.isArray(inv.browsers) ? inv.browsers : [];
|
|
5859
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5860
|
+
for (const b of list) byId.set(b.id, b);
|
|
5861
|
+
const wanted = preference === void 0 ? preferenceId() : typeof preference === "string" && preference ? preference : null;
|
|
5862
|
+
const stale = wanted !== null && !byId.has(wanted);
|
|
5863
|
+
const out = (browser, how) => ({ browser, how, wanted, stale });
|
|
5864
|
+
if (wanted !== null && byId.has(wanted)) return out(byId.get(wanted), "user-preference");
|
|
5865
|
+
const sys = inv && inv.defaultId ? byId.get(inv.defaultId) : null;
|
|
5866
|
+
if (sys) return out(sys, inv.defaultSource || "default");
|
|
5867
|
+
if (list.length === 1) return out(list[0], "only-installed");
|
|
5868
|
+
if (list.length > 1) return out(rankCandidates(list)[0], "candidate-rank");
|
|
5869
|
+
return out(null, "none-found");
|
|
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" });
|
|
5683
5910
|
}
|
|
5684
|
-
for (const
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
if (exe) push(exe, "startmenu-catalog");
|
|
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 });
|
|
5688
5914
|
}
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
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));
|
|
5696
5931
|
}
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
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");
|
|
5701
5981
|
}
|
|
5702
5982
|
}
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
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" };
|
|
5708
5997
|
}
|
|
5709
|
-
if (
|
|
5710
|
-
|
|
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
|
+
};
|
|
5711
6012
|
}
|
|
5712
|
-
function
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
6013
|
+
function snapshotPath() {
|
|
6014
|
+
return path2.join(stateRoot2.supervisorDir(), FILE_NAME);
|
|
6015
|
+
}
|
|
6016
|
+
function readSnapshot() {
|
|
6017
|
+
try {
|
|
6018
|
+
const doc = JSON.parse(fs2.readFileSync(snapshotPath(), "utf8"));
|
|
6019
|
+
return doc && doc.schema === SCHEMA ? doc : null;
|
|
6020
|
+
} catch {
|
|
6021
|
+
return null;
|
|
5717
6022
|
}
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
const
|
|
5725
|
-
|
|
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 };
|
|
5726
6031
|
}
|
|
5727
|
-
|
|
5728
|
-
|
|
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
|
+
};
|
|
5729
6040
|
}
|
|
5730
|
-
var
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
"
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
].join("");
|
|
5751
|
-
function probeMac(d) {
|
|
5752
|
-
const { runOut, canExec } = d;
|
|
5753
|
-
const notes = [];
|
|
5754
|
-
const note = (source, detail) => {
|
|
5755
|
-
notes.push({ source, detail });
|
|
6041
|
+
var _formCache = null;
|
|
6042
|
+
function form(o) {
|
|
6043
|
+
const ov = o || {};
|
|
6044
|
+
const now = typeof ov.now === "function" ? ov.now : (() => Date.now());
|
|
6045
|
+
const stamp = now();
|
|
6046
|
+
const ttl = ov.ttlMs === void 0 ? FORM_TTL_MS : ov.ttlMs;
|
|
6047
|
+
if (!ov.force && _formCache && stamp - _formCache.at < ttl) {
|
|
6048
|
+
return Object.assign({}, _formCache.value, { cached: true });
|
|
6049
|
+
}
|
|
6050
|
+
const inv = browsers(ov);
|
|
6051
|
+
const caps = capabilities();
|
|
6052
|
+
const session = desktop.describe();
|
|
6053
|
+
const prefId = preferenceId();
|
|
6054
|
+
const prefEntry = prefId ? inv.browsers.find((b) => b.id === prefId) || null : null;
|
|
6055
|
+
const preference = {
|
|
6056
|
+
id: prefId,
|
|
6057
|
+
configured: !!prefId,
|
|
6058
|
+
matched: !!prefEntry,
|
|
6059
|
+
browser: prefEntry ? { id: prefEntry.id, name: prefEntry.name, engine: prefEntry.engine } : null,
|
|
6060
|
+
reason: !prefId ? "not-set" : prefEntry ? "matched" : "stale"
|
|
5756
6061
|
};
|
|
5757
|
-
const
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
6062
|
+
const pick = pickLauncher(inv.platform, inv, prefId);
|
|
6063
|
+
const probed = inv.probed.map((p) => ({ section: "browsers", source: p.source, detail: p.detail }));
|
|
6064
|
+
probed.push({ section: "session", source: "desktop", detail: session.reason + (session.available ? "\uFF08\u53EF\u7528\uFF09" : "\uFF08\u4E0D\u53EF\u7528\uFF09") });
|
|
6065
|
+
probed.push({
|
|
6066
|
+
section: "capabilities",
|
|
6067
|
+
source: "profile",
|
|
6068
|
+
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"
|
|
6069
|
+
});
|
|
6070
|
+
probed.push({
|
|
6071
|
+
section: "preference",
|
|
6072
|
+
source: "config",
|
|
6073
|
+
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
|
|
6074
|
+
});
|
|
6075
|
+
probed.push({
|
|
6076
|
+
section: "pick",
|
|
6077
|
+
source: "form",
|
|
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" : "")
|
|
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
|
+
});
|
|
5761
6118
|
}
|
|
5762
|
-
const
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
}
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
6119
|
+
const value = {
|
|
6120
|
+
schema: SCHEMA,
|
|
6121
|
+
at: stamp,
|
|
6122
|
+
platform: inv.platform,
|
|
6123
|
+
identity: identity(),
|
|
6124
|
+
paths: paths(),
|
|
6125
|
+
session,
|
|
6126
|
+
capabilities: caps,
|
|
6127
|
+
preference,
|
|
6128
|
+
default: inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource } : null,
|
|
6129
|
+
browsers: inv.browsers,
|
|
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,
|
|
6132
|
+
probed
|
|
6133
|
+
};
|
|
6134
|
+
const snapshot = { path: snapshotPath(), written: false, error: null };
|
|
6135
|
+
if (ov.persist === true) {
|
|
6136
|
+
try {
|
|
6137
|
+
writeAtomic(snapshot.path, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
|
|
6138
|
+
snapshot.written = true;
|
|
6139
|
+
} catch (e) {
|
|
6140
|
+
snapshot.error = String(e && e.message || e);
|
|
5782
6141
|
}
|
|
6142
|
+
probed.push({
|
|
6143
|
+
section: "snapshot",
|
|
6144
|
+
source: FILE_NAME,
|
|
6145
|
+
detail: snapshot.written ? "\u5DF2\u843D\u76D8 " + snapshot.path : "\u843D\u76D8\u5931\u8D25: " + snapshot.error
|
|
6146
|
+
});
|
|
5783
6147
|
}
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
6148
|
+
value.snapshot = snapshot;
|
|
6149
|
+
_formCache = { at: stamp, value };
|
|
6150
|
+
return Object.assign({ cached: false }, value);
|
|
6151
|
+
}
|
|
6152
|
+
function invalidate(platform, only) {
|
|
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;
|
|
5788
6159
|
}
|
|
5789
|
-
return
|
|
6160
|
+
return detector.invalidate(platform);
|
|
5790
6161
|
}
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
const
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
});
|
|
6162
|
+
function checkPreference(value, form2) {
|
|
6163
|
+
const candidates = (form2 && form2.browsers || []).map((b) => ({
|
|
6164
|
+
id: b.id,
|
|
6165
|
+
name: b.name,
|
|
6166
|
+
engine: b.engine,
|
|
6167
|
+
isDefault: b.isDefault === true
|
|
6168
|
+
}));
|
|
6169
|
+
const id = typeof value === "string" ? value.trim() : "";
|
|
6170
|
+
if (!id) return { ok: true, id: null, browser: null, candidates };
|
|
6171
|
+
const hit = candidates.find((c) => c.id === id);
|
|
6172
|
+
if (!hit) {
|
|
6173
|
+
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" };
|
|
6174
|
+
}
|
|
6175
|
+
return { ok: true, id: hit.id, browser: hit, candidates };
|
|
5806
6176
|
}
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
6177
|
+
module2.exports = {
|
|
6178
|
+
SCHEMA,
|
|
6179
|
+
FILE_NAME,
|
|
6180
|
+
FORM_TTL_MS,
|
|
6181
|
+
SECTION_TTL_MS,
|
|
6182
|
+
SECTION_ORDER,
|
|
6183
|
+
SYNC_DIMS,
|
|
6184
|
+
bind,
|
|
6185
|
+
preferenceId,
|
|
6186
|
+
capabilities,
|
|
6187
|
+
identity,
|
|
6188
|
+
paths,
|
|
6189
|
+
browsers,
|
|
6190
|
+
normalizeInventory,
|
|
6191
|
+
rankCandidates,
|
|
6192
|
+
pickLauncher,
|
|
6193
|
+
checkPreference,
|
|
6194
|
+
registerSection,
|
|
6195
|
+
unregisterSection,
|
|
6196
|
+
section,
|
|
6197
|
+
sectionData,
|
|
6198
|
+
refresh,
|
|
6199
|
+
coldProfileViable,
|
|
6200
|
+
checkEgress,
|
|
6201
|
+
form,
|
|
6202
|
+
snapshotPath,
|
|
6203
|
+
readSnapshot,
|
|
6204
|
+
lastSnapshot,
|
|
6205
|
+
invalidate
|
|
6206
|
+
};
|
|
6207
|
+
}
|
|
6208
|
+
});
|
|
6209
|
+
|
|
6210
|
+
// src/platform/os/process.js
|
|
6211
|
+
var require_process = __commonJS({
|
|
6212
|
+
"src/platform/os/process.js"(exports2, module2) {
|
|
6213
|
+
"use strict";
|
|
6214
|
+
var ex2 = require_exec();
|
|
6215
|
+
var isWindows = process.platform === "win32";
|
|
6216
|
+
function signalProcess(pid, sig) {
|
|
6217
|
+
if (!Number.isInteger(pid) || pid <= 0) return;
|
|
6218
|
+
if (isWindows) {
|
|
6219
|
+
try {
|
|
6220
|
+
process.kill(pid, sig);
|
|
6221
|
+
} catch {
|
|
6222
|
+
}
|
|
6223
|
+
return;
|
|
6224
|
+
}
|
|
6225
|
+
try {
|
|
6226
|
+
process.kill(-pid, sig);
|
|
6227
|
+
} catch {
|
|
6228
|
+
try {
|
|
6229
|
+
process.kill(pid, sig);
|
|
6230
|
+
} catch {
|
|
5814
6231
|
}
|
|
5815
|
-
if (/^\[/.test(line)) break;
|
|
5816
|
-
if (!inMain) continue;
|
|
5817
|
-
const m = line.match(/^(Exec|Name|TryExec|Categories)=(.*)$/);
|
|
5818
|
-
if (!m) continue;
|
|
5819
|
-
if (m[1] === "Name" && got.Name) continue;
|
|
5820
|
-
got[m[1]] = m[2].trim();
|
|
5821
6232
|
}
|
|
5822
|
-
if (!got.Exec) return null;
|
|
5823
|
-
const categories = String(got.Categories || "");
|
|
5824
|
-
const parsed = parseExecLine(got.Exec);
|
|
5825
|
-
if (!parsed) return null;
|
|
5826
|
-
const engine = engineOf(parsed.bin);
|
|
5827
|
-
const claimsBrowser = /(^|;)WebBrowser(;|$)/.test(categories);
|
|
5828
|
-
if (!claimsBrowser && engine === "other") return null;
|
|
5829
|
-
if (engine === "other") return null;
|
|
5830
|
-
return {
|
|
5831
|
-
// id 恒为归一后的可执行文件路径:defaultId 靠它在清单里定位条目,三平台同一契约
|
|
5832
|
-
id: parsed.bin.toLowerCase(),
|
|
5833
|
-
name: got.Name || path2.basename(file, ".desktop"),
|
|
5834
|
-
engine,
|
|
5835
|
-
bin: parsed.bin,
|
|
5836
|
-
baseArgs: parsed.baseArgs,
|
|
5837
|
-
source: kind,
|
|
5838
|
-
desktopFile: file
|
|
5839
|
-
};
|
|
5840
6233
|
}
|
|
5841
|
-
function
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
if (b.includes("/")) return null;
|
|
5846
|
-
for (const dir of String(env.PATH || "").split(":")) {
|
|
5847
|
-
if (!dir) continue;
|
|
5848
|
-
const p = PJ(dir.replace(/\/+$/, ""), b);
|
|
5849
|
-
if (canExec(p)) return p;
|
|
6234
|
+
function killTree(pid, sig, cb, opts) {
|
|
6235
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
6236
|
+
if (cb) cb(new Error("invalid pid"));
|
|
6237
|
+
return;
|
|
5850
6238
|
}
|
|
5851
|
-
|
|
6239
|
+
if (isWindows) {
|
|
6240
|
+
ex2.runAsync("taskkill", ["/PID", String(pid), "/T", "/F"], { timeoutMs: 1e4 }).then((r) => {
|
|
6241
|
+
if (cb) cb(r.ok ? null : new Error(r.error || "taskkill failed"));
|
|
6242
|
+
});
|
|
6243
|
+
return;
|
|
6244
|
+
}
|
|
6245
|
+
if (opts && opts.ownGroup === true) {
|
|
6246
|
+
signalProcess(pid, sig || "SIGTERM");
|
|
6247
|
+
} else {
|
|
6248
|
+
try {
|
|
6249
|
+
process.kill(pid, sig || "SIGTERM");
|
|
6250
|
+
} catch {
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
if (cb) process.nextTick(cb, null);
|
|
5852
6254
|
}
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
6255
|
+
module2.exports = { signalProcess, killTree };
|
|
6256
|
+
}
|
|
6257
|
+
});
|
|
6258
|
+
|
|
6259
|
+
// src/platform/os/file-protect.js
|
|
6260
|
+
var require_file_protect = __commonJS({
|
|
6261
|
+
"src/platform/os/file-protect.js"(exports2, module2) {
|
|
6262
|
+
"use strict";
|
|
6263
|
+
var fs2 = require("node:fs");
|
|
6264
|
+
var os2 = require("node:os");
|
|
6265
|
+
var path2 = require("node:path");
|
|
6266
|
+
var ex2 = require_exec();
|
|
6267
|
+
var IS_WINDOWS2 = process.platform === "win32";
|
|
6268
|
+
var _icacls = null;
|
|
6269
|
+
function hasIcacls(platform) {
|
|
6270
|
+
if ((platform || process.platform) !== "win32") return false;
|
|
6271
|
+
if (_icacls !== null) return _icacls;
|
|
6272
|
+
_icacls = ex2.runOut("icacls", ["/?"], { timeoutMs: 3e3 }) !== null;
|
|
6273
|
+
return _icacls;
|
|
6274
|
+
}
|
|
6275
|
+
function currentUser() {
|
|
6276
|
+
return process.env.USERNAME || process.env.USER || (() => {
|
|
6277
|
+
try {
|
|
6278
|
+
return os2.userInfo().username;
|
|
6279
|
+
} catch {
|
|
5864
6280
|
return null;
|
|
5865
6281
|
}
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
6282
|
+
})();
|
|
6283
|
+
}
|
|
6284
|
+
function protectFile(file) {
|
|
6285
|
+
if (!IS_WINDOWS2) {
|
|
6286
|
+
try {
|
|
6287
|
+
fs2.chmodSync(file, 384);
|
|
6288
|
+
return { ok: true, mode: "posix-0600" };
|
|
6289
|
+
} catch (e) {
|
|
6290
|
+
return { ok: false, mode: "posix-0600", reason: e.message };
|
|
5871
6291
|
}
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
};
|
|
5876
|
-
|
|
5877
|
-
|
|
6292
|
+
}
|
|
6293
|
+
if (!hasIcacls()) return { ok: false, mode: "none", reason: "icacls \u4E0D\u53EF\u7528" };
|
|
6294
|
+
const user = currentUser();
|
|
6295
|
+
if (!user) return { ok: false, mode: "icacls", reason: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u7528\u6237" };
|
|
6296
|
+
const r = ex2.runDetail("icacls", [file, "/inheritance:r", "/grant:r", user + ":F"], { stdio: "ignore", timeoutMs: 5e3 });
|
|
6297
|
+
return r.ok ? { ok: true, mode: "icacls-file" } : { ok: false, mode: "icacls-file", reason: r.error || "\u9000\u51FA\u7801 " + r.code };
|
|
6298
|
+
}
|
|
6299
|
+
function protectDir(dir) {
|
|
6300
|
+
if (!IS_WINDOWS2) {
|
|
6301
|
+
try {
|
|
6302
|
+
fs2.chmodSync(dir, 448);
|
|
6303
|
+
return { ok: true, mode: "posix-0700" };
|
|
6304
|
+
} catch (e) {
|
|
6305
|
+
return { ok: false, mode: "posix-0700", reason: e.message };
|
|
6306
|
+
}
|
|
6307
|
+
}
|
|
6308
|
+
if (!hasIcacls()) return { ok: false, mode: "none", reason: "icacls \u4E0D\u53EF\u7528" };
|
|
6309
|
+
const user = currentUser();
|
|
6310
|
+
if (!user) return { ok: false, mode: "icacls", reason: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u7528\u6237" };
|
|
6311
|
+
const r = ex2.runDetail("icacls", [dir, "/inheritance:r", "/grant:r", user + ":(OI)(CI)F"], { stdio: "ignore", timeoutMs: 1e4 });
|
|
6312
|
+
return r.ok ? { ok: true, mode: "icacls-dir" } : { ok: false, mode: "icacls-dir", reason: r.error || "\u9000\u51FA\u7801 " + r.code };
|
|
6313
|
+
}
|
|
6314
|
+
function ensurePrivateDir(dir) {
|
|
6315
|
+
try {
|
|
6316
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
6317
|
+
} catch (e) {
|
|
6318
|
+
return { ok: false, mode: "mkdir", reason: e.message };
|
|
6319
|
+
}
|
|
6320
|
+
return protectDir(dir);
|
|
6321
|
+
}
|
|
6322
|
+
function writePrivate(file, data) {
|
|
6323
|
+
try {
|
|
6324
|
+
const dir = path2.dirname(file);
|
|
6325
|
+
try {
|
|
6326
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
6327
|
+
} catch {
|
|
6328
|
+
}
|
|
6329
|
+
const tmp = file + ".tmp" + process.pid;
|
|
6330
|
+
fs2.writeFileSync(tmp, data, { mode: 384 });
|
|
6331
|
+
const p1 = protectFile(tmp);
|
|
6332
|
+
if (p1 && p1.ok === false) {
|
|
6333
|
+
try {
|
|
6334
|
+
fs2.rmSync(tmp, { force: true });
|
|
6335
|
+
} catch {
|
|
6336
|
+
}
|
|
6337
|
+
return { ok: false, reason: "protect(tmp): " + (p1.reason || p1.mode), mode: p1.mode };
|
|
6338
|
+
}
|
|
6339
|
+
fs2.renameSync(tmp, file);
|
|
6340
|
+
const p2 = protectFile(file);
|
|
6341
|
+
if (p2 && p2.ok === false) return { ok: false, reason: "protect(file): " + (p2.reason || p2.mode), mode: p2.mode };
|
|
6342
|
+
return { ok: true };
|
|
6343
|
+
} catch (e) {
|
|
6344
|
+
return { ok: false, reason: e.message };
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
module2.exports = { protectFile, protectDir, ensurePrivateDir, writePrivate, hasIcacls, currentUser, IS_WINDOWS: IS_WINDOWS2 };
|
|
6348
|
+
}
|
|
6349
|
+
});
|
|
6350
|
+
|
|
6351
|
+
// src/platform/util/input.js
|
|
6352
|
+
var require_input = __commonJS({
|
|
6353
|
+
"src/platform/util/input.js"(exports2, module2) {
|
|
6354
|
+
"use strict";
|
|
6355
|
+
var PKG_NAME_RE = /^(@[a-zA-Z0-9._-]+\/)?[a-zA-Z0-9._-]+$/;
|
|
6356
|
+
var ARGV_UNSAFE_RE = /[\s;|&<>`'"$(){}\\*?~#]/;
|
|
6357
|
+
var WIN_ABS_PATH_RE = /^[A-Za-z]:\\[^;|&<>`'"$*?~#\s]*$/;
|
|
6358
|
+
var UNIT_NAME_RE = /^[A-Za-z0-9:@._-]{1,128}$/;
|
|
6359
|
+
var UNSAFE_LEDGER_KEYS = ["__proto__", "prototype", "constructor"];
|
|
6360
|
+
var LEDGER_UNSAFE_RE = /[\s\u0000-\u001f\u007f]/;
|
|
6361
|
+
function argvViolation(arg) {
|
|
6362
|
+
const s = String(arg == null ? "" : arg);
|
|
6363
|
+
if (!s) return "\u7A7A\u7684 argv \u9879";
|
|
6364
|
+
if (ARGV_UNSAFE_RE.test(s) && !WIN_ABS_PATH_RE.test(s)) return "argv \u9879\u542B\u7981\u7528\u5B57\u7B26\uFF08\u7A7A\u767D/shell \u5143\u5B57\u7B26\uFF09: " + s.slice(0, 80);
|
|
6365
|
+
return null;
|
|
6366
|
+
}
|
|
6367
|
+
var CONTROL_RE = /[\u0000-\u001f\u007f]/;
|
|
6368
|
+
function isAbsoluteForm(p) {
|
|
6369
|
+
const s = String(p == null ? "" : p);
|
|
6370
|
+
return s.charCodeAt(0) === 47 || /^[A-Za-z]:[\\/]/.test(s) || s.charCodeAt(0) === 92 && s.charCodeAt(1) === 92;
|
|
6371
|
+
}
|
|
6372
|
+
function prefixViolation(prefix) {
|
|
6373
|
+
const s = String(prefix == null ? "" : prefix);
|
|
6374
|
+
if (!s.trim()) return "\u7A7A\u7684\u5B89\u88C5\u524D\u7F00";
|
|
6375
|
+
if (CONTROL_RE.test(s)) return "\u5B89\u88C5\u524D\u7F00\u542B\u63A7\u5236\u7B26: " + s.slice(0, 80);
|
|
6376
|
+
if (s.length > 4096) return "\u5B89\u88C5\u524D\u7F00\u8D85\u957F: " + s.slice(0, 60) + "...";
|
|
6377
|
+
if (s[0] === "-") return "\u5B89\u88C5\u524D\u7F00\u4EE5 - \u5F00\u5934\uFF08\u4F1A\u88AB\u5F53\u4F5C npm \u9009\u9879\uFF09: " + s.slice(0, 80);
|
|
6378
|
+
if (!isAbsoluteForm(s)) return "\u5B89\u88C5\u524D\u7F00\u987B\u4E3A\u7EDD\u5BF9\u8DEF\u5F84\uFF08/ \u5F00\u5934\u3001win \u76D8\u7B26\u6216 UNC\uFF09: " + s.slice(0, 80);
|
|
6379
|
+
return null;
|
|
6380
|
+
}
|
|
6381
|
+
function pkgNameViolation(pkg) {
|
|
6382
|
+
const s = String(pkg == null ? "" : pkg);
|
|
6383
|
+
return PKG_NAME_RE.test(s) ? null : "\u975E\u6CD5\u5305\u540D\uFF08\u5B57\u7B26\u96C6\u767D\u540D\u5355\u4E0D\u901A\u8FC7\uFF09: " + s.slice(0, 80);
|
|
6384
|
+
}
|
|
6385
|
+
function unitNameViolation(unit) {
|
|
6386
|
+
const s = String(unit == null ? "" : unit);
|
|
6387
|
+
if (!UNIT_NAME_RE.test(s)) return "\u975E\u6CD5\u5355\u5143\u540D\uFF08\u5B57\u7B26\u96C6/\u957F\u5EA6\u767D\u540D\u5355\u4E0D\u901A\u8FC7\uFF09: " + s.slice(0, 80);
|
|
6388
|
+
const dot = s.indexOf(".");
|
|
6389
|
+
if (dot >= 0 && !/\.service$/.test(s)) return "\u5355\u5143\u540D\u540E\u7F00\u4E0D\u53D7\u652F\u6301\uFF08\u4EC5\u5141\u8BB8 .service \u6216\u65E0\u540E\u7F00\uFF09: " + s.slice(0, 80);
|
|
6390
|
+
return null;
|
|
6391
|
+
}
|
|
6392
|
+
function ledgerKey(raw, opts) {
|
|
6393
|
+
const o = opts || {};
|
|
6394
|
+
const max = typeof o.max === "number" ? o.max : 128;
|
|
6395
|
+
const empty = o.empty == null ? "unknown" : o.empty;
|
|
6396
|
+
const unsafe = o.unsafe == null ? "(other)" : o.unsafe;
|
|
6397
|
+
if (typeof raw !== "string" || !raw) return empty;
|
|
6398
|
+
const s = raw.length > max ? raw.slice(0, max) : raw;
|
|
6399
|
+
if (UNSAFE_LEDGER_KEYS.indexOf(s) >= 0 || LEDGER_UNSAFE_RE.test(s)) return unsafe;
|
|
6400
|
+
return s;
|
|
6401
|
+
}
|
|
6402
|
+
module2.exports = {
|
|
6403
|
+
PKG_NAME_RE,
|
|
6404
|
+
ARGV_UNSAFE_RE,
|
|
6405
|
+
WIN_ABS_PATH_RE,
|
|
6406
|
+
UNIT_NAME_RE,
|
|
6407
|
+
UNSAFE_LEDGER_KEYS,
|
|
6408
|
+
LEDGER_UNSAFE_KEYS_RE: LEDGER_UNSAFE_RE,
|
|
6409
|
+
argvViolation,
|
|
6410
|
+
pkgNameViolation,
|
|
6411
|
+
unitNameViolation,
|
|
6412
|
+
ledgerKey,
|
|
6413
|
+
CONTROL_RE,
|
|
6414
|
+
isAbsoluteForm,
|
|
6415
|
+
prefixViolation
|
|
6416
|
+
};
|
|
6417
|
+
}
|
|
6418
|
+
});
|
|
6419
|
+
|
|
6420
|
+
// src/platform/os/spawn.js
|
|
6421
|
+
var require_spawn = __commonJS({
|
|
6422
|
+
"src/platform/os/spawn.js"(exports2, module2) {
|
|
6423
|
+
"use strict";
|
|
6424
|
+
var { spawn } = require("node:child_process");
|
|
6425
|
+
function detached(cmd2, args, opts) {
|
|
6426
|
+
const o = opts || {};
|
|
6427
|
+
return spawn(cmd2, args, Object.assign({}, o, {
|
|
6428
|
+
detached: true,
|
|
6429
|
+
windowsHide: true,
|
|
6430
|
+
// 默认 ignore;显式 undefined 也回落 ignore,避免 stdio:undefined 的歧义。
|
|
6431
|
+
stdio: o.stdio === void 0 ? "ignore" : o.stdio
|
|
6432
|
+
}));
|
|
6433
|
+
}
|
|
6434
|
+
function piped(cmd2, args, opts) {
|
|
6435
|
+
const o = opts || {};
|
|
6436
|
+
return spawn(cmd2, args, Object.assign({}, o, {
|
|
6437
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6438
|
+
windowsHide: true,
|
|
6439
|
+
detached: o.detached === true
|
|
6440
|
+
}));
|
|
6441
|
+
}
|
|
6442
|
+
function detachedIgnored(cmd2, args, opts) {
|
|
6443
|
+
const o = opts || {};
|
|
6444
|
+
return spawn(cmd2, args, Object.assign({}, o, {
|
|
6445
|
+
detached: true,
|
|
6446
|
+
windowsHide: true,
|
|
6447
|
+
stdio: "ignore"
|
|
6448
|
+
}));
|
|
6449
|
+
}
|
|
6450
|
+
module2.exports = { detached, piped, detachedIgnored };
|
|
6451
|
+
}
|
|
6452
|
+
});
|
|
6453
|
+
|
|
6454
|
+
// src/platform/os/portable.js
|
|
6455
|
+
var require_portable = __commonJS({
|
|
6456
|
+
"src/platform/os/portable.js"(exports2, module2) {
|
|
6457
|
+
"use strict";
|
|
6458
|
+
var fs2 = require("node:fs");
|
|
6459
|
+
var pidlookup = require_pidlookup();
|
|
6460
|
+
var spawner = require_spawn();
|
|
6461
|
+
var procOS = require_process();
|
|
6462
|
+
var STOP_WAIT_CAP_MS = 3e3;
|
|
6463
|
+
var NAP_MS = 150;
|
|
6464
|
+
function nap(ms) {
|
|
6465
|
+
try {
|
|
6466
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
6467
|
+
} catch {
|
|
6468
|
+
}
|
|
6469
|
+
}
|
|
6470
|
+
function readPidFile(pidFile) {
|
|
6471
|
+
if (!pidFile) return null;
|
|
6472
|
+
try {
|
|
6473
|
+
const n = parseInt(String(fs2.readFileSync(pidFile, "utf8")).trim(), 10);
|
|
6474
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
6475
|
+
} catch {
|
|
6476
|
+
return null;
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
function matchesAnchors(pid, anchors) {
|
|
6480
|
+
if (!pid || !Array.isArray(anchors) || !anchors.length) return false;
|
|
6481
|
+
const cmd2 = pidlookup.readCmdline(pid);
|
|
6482
|
+
if (!cmd2) return false;
|
|
6483
|
+
return anchors.some((a) => a && cmd2.indexOf(String(a)) !== -1);
|
|
6484
|
+
}
|
|
6485
|
+
var portOf = (o) => {
|
|
6486
|
+
const p = Number(o && o.port);
|
|
6487
|
+
return Number.isInteger(p) && p > 0 ? p : 0;
|
|
6488
|
+
};
|
|
6489
|
+
var pidFileOf = (o) => o && o.pidFile || null;
|
|
6490
|
+
var anchorsOf = (o) => Array.isArray(o && o.anchors) ? o.anchors : [];
|
|
6491
|
+
function findOurs(o) {
|
|
6492
|
+
const anchors = anchorsOf(o);
|
|
6493
|
+
const fp = readPidFile(pidFileOf(o));
|
|
6494
|
+
if (fp !== null && pidlookup.isAlive(fp) && matchesAnchors(fp, anchors)) return { pid: fp, ownGroup: true };
|
|
6495
|
+
const port = portOf(o);
|
|
6496
|
+
if (port) {
|
|
6497
|
+
const q = pidlookup.findListeningPid(port);
|
|
6498
|
+
if (q !== null && q !== fp && matchesAnchors(q, anchors)) return { pid: q, ownGroup: false };
|
|
6499
|
+
}
|
|
6500
|
+
return null;
|
|
6501
|
+
}
|
|
6502
|
+
function removePidFile(o) {
|
|
6503
|
+
const f = pidFileOf(o);
|
|
6504
|
+
if (f) {
|
|
6505
|
+
try {
|
|
6506
|
+
fs2.unlinkSync(f);
|
|
6507
|
+
} catch {
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
var portable = {
|
|
6512
|
+
kind: "portable",
|
|
6513
|
+
supportsUnits: false,
|
|
6514
|
+
supportsTransient: true,
|
|
6515
|
+
// 无操作 = 成功(与 systemd 的动词返回形态一致,调用方无需按 provider 分支)。
|
|
6516
|
+
daemonReload() {
|
|
6517
|
+
return true;
|
|
6518
|
+
},
|
|
6519
|
+
resetFailed() {
|
|
6520
|
+
return true;
|
|
6521
|
+
},
|
|
6522
|
+
/** 拉起:spawn(detached)——POSIX 自成进程组(kill(-pid) 语义的前提)、win32 windowsHide +
|
|
6523
|
+
* CREATE_NEW_PROCESS_GROUP(NO-CONSOLE-WINDOW 纪律收口在 platform/os/spawn.js)。
|
|
6524
|
+
* 调用方传来的 props(cgroup 语义)在此如实忽略:本档无内核强制可施加。
|
|
6525
|
+
* @param {{cmd:string[], env?:object, workingDir?:string, pidFile?:string, port?:number, anchors?:string[]}} o */
|
|
6526
|
+
startTransient(o) {
|
|
6527
|
+
const opts = o || {};
|
|
6528
|
+
const cmd2 = opts.cmd || [];
|
|
6529
|
+
if (!cmd2.length) throw new Error("portable \u62C9\u8D77\u62D2\u7EDD\uFF1A\u7A7A\u547D\u4EE4");
|
|
6530
|
+
const child = spawner.detached(cmd2[0], cmd2.slice(1), {
|
|
6531
|
+
cwd: opts.workingDir || void 0,
|
|
6532
|
+
env: Object.assign({}, process.env, opts.env || {})
|
|
6533
|
+
});
|
|
6534
|
+
child.on("error", () => {
|
|
6535
|
+
});
|
|
6536
|
+
if (!child.pid) {
|
|
6537
|
+
try {
|
|
6538
|
+
child.kill();
|
|
6539
|
+
} catch {
|
|
6540
|
+
}
|
|
6541
|
+
throw new Error("portable \u62C9\u8D77\u5931\u8D25\uFF1Aspawn \u672A\u4EA7\u751F\u8FDB\u7A0B");
|
|
6542
|
+
}
|
|
6543
|
+
try {
|
|
6544
|
+
child.unref();
|
|
6545
|
+
} catch {
|
|
6546
|
+
}
|
|
6547
|
+
if (opts.pidFile) {
|
|
6548
|
+
try {
|
|
6549
|
+
fs2.writeFileSync(opts.pidFile, String(child.pid));
|
|
6550
|
+
} catch {
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
return true;
|
|
6554
|
+
},
|
|
6555
|
+
/** 停止(有界同步复核):终止我们的进程树,确认「端口锚点与 run.pid 锚点双双消失」才 true。
|
|
6556
|
+
* 无可杀对象(含 run.pid 为陈旧/被 PID 复用)-> 清文件后如实 true(幂等)。 */
|
|
6557
|
+
stopUnit(unit, o) {
|
|
6558
|
+
const opts = o || {};
|
|
6559
|
+
const ours = findOurs(opts);
|
|
6560
|
+
if (!ours) {
|
|
6561
|
+
removePidFile(opts);
|
|
6562
|
+
return true;
|
|
6563
|
+
}
|
|
6564
|
+
try {
|
|
6565
|
+
procOS.killTree(ours.pid, "SIGTERM", void 0, { ownGroup: ours.ownGroup });
|
|
6566
|
+
} catch {
|
|
6567
|
+
}
|
|
6568
|
+
const budget = Math.max(0, Math.min(typeof opts.timeoutMs === "number" ? opts.timeoutMs : STOP_WAIT_CAP_MS, STOP_WAIT_CAP_MS));
|
|
6569
|
+
const t0 = Date.now();
|
|
6570
|
+
for (; ; ) {
|
|
6571
|
+
if (!findOurs(opts)) {
|
|
6572
|
+
removePidFile(opts);
|
|
6573
|
+
return true;
|
|
6574
|
+
}
|
|
6575
|
+
if (Date.now() - t0 >= budget) break;
|
|
6576
|
+
nap(NAP_MS);
|
|
6577
|
+
}
|
|
6578
|
+
try {
|
|
6579
|
+
procOS.killTree(ours.pid, "SIGKILL", void 0, { ownGroup: ours.ownGroup });
|
|
6580
|
+
} catch {
|
|
6581
|
+
}
|
|
6582
|
+
nap(NAP_MS);
|
|
6583
|
+
if (!findOurs(opts)) {
|
|
6584
|
+
removePidFile(opts);
|
|
6585
|
+
return true;
|
|
6586
|
+
}
|
|
6587
|
+
return false;
|
|
6588
|
+
},
|
|
6589
|
+
/** 活跃判定(三态):port 与 pidFile 都缺 = 无从查询 -> null,删除保护路径不得把它当「已停止」。
|
|
6590
|
+
* anchors 为空时降级为「端口有监听即活跃」(monitor.probeInstance 同口径,该调用方本就以此判在线);
|
|
6591
|
+
* 杀进程(stopUnit)仍要求锚点命中 —— 判定可宽,动手必严。
|
|
6592
|
+
* 存活 pid 的 cmdline 读不到 = 分不清「他人复用」与「查询失败」-> null。 */
|
|
6593
|
+
isUnitActive(unit, o) {
|
|
6594
|
+
const opts = o || {};
|
|
6595
|
+
if (findOurs(opts)) return true;
|
|
6596
|
+
const port = portOf(opts);
|
|
6597
|
+
const pidFile = pidFileOf(opts);
|
|
6598
|
+
const anchors = anchorsOf(opts);
|
|
6599
|
+
if (!port && !pidFile) return null;
|
|
6600
|
+
if (!anchors.length) {
|
|
6601
|
+
const fp2 = readPidFile(pidFile);
|
|
6602
|
+
if (fp2 !== null && pidlookup.isAlive(fp2)) return true;
|
|
6603
|
+
if (port) {
|
|
6604
|
+
const q = pidlookup.findListeningPid(port);
|
|
6605
|
+
if (q !== null) return true;
|
|
6606
|
+
if (fp2 !== null) return false;
|
|
6607
|
+
return null;
|
|
6608
|
+
}
|
|
6609
|
+
return fp2 !== null ? false : null;
|
|
6610
|
+
}
|
|
6611
|
+
const fp = readPidFile(pidFile);
|
|
6612
|
+
if (fp !== null && pidlookup.isAlive(fp) && !pidlookup.readCmdline(fp)) return null;
|
|
6613
|
+
return false;
|
|
6614
|
+
},
|
|
6615
|
+
// portable 无 systemd transient 单元文件;run.pid 的清理归 cleanTransient。
|
|
6616
|
+
transientUnitFile() {
|
|
6617
|
+
return null;
|
|
6618
|
+
},
|
|
6619
|
+
/** 清理同名残留:先停掉仍活着的旧进程(重启自愈路径:旧进程未监听端口时端口探测拦不住),再清 run.pid。 */
|
|
6620
|
+
cleanTransient(unit, o) {
|
|
6621
|
+
const opts = o || {};
|
|
6622
|
+
const errors = [];
|
|
6623
|
+
if (this.stopUnit(unit, Object.assign({}, opts, { timeoutMs: 1500 })) === false) errors.push("stop-unconfirmed");
|
|
6624
|
+
removePidFile(opts);
|
|
6625
|
+
return { ok: errors.length === 0, errors };
|
|
6626
|
+
},
|
|
6627
|
+
/** 返回值只表示「是否改动了内核强制」;governor 不因 false 走降级分支 —— 违规处置链本就平台无关。 */
|
|
6628
|
+
setLimits() {
|
|
6629
|
+
return false;
|
|
6630
|
+
}
|
|
6631
|
+
};
|
|
6632
|
+
module2.exports = { portable, findOurs, matchesAnchors, _test: { readPidFile, matchesAnchors, findOurs } };
|
|
6633
|
+
}
|
|
6634
|
+
});
|
|
6635
|
+
|
|
6636
|
+
// src/platform/os/service.js
|
|
6637
|
+
var require_service = __commonJS({
|
|
6638
|
+
"src/platform/os/service.js"(exports2, module2) {
|
|
6639
|
+
"use strict";
|
|
6640
|
+
var fs2 = require("node:fs");
|
|
6641
|
+
var os2 = require("node:os");
|
|
6642
|
+
var path2 = require("node:path");
|
|
6643
|
+
var exec = require_exec();
|
|
6644
|
+
var input = require_input();
|
|
6645
|
+
var execPath = require_exec_path();
|
|
6646
|
+
var { portable } = require_portable();
|
|
6647
|
+
var CapabilityError = class extends Error {
|
|
6648
|
+
constructor(msg) {
|
|
6649
|
+
super(msg);
|
|
6650
|
+
this.name = "CapabilityError";
|
|
6651
|
+
this.code = "CAPABILITY_UNSUPPORTED";
|
|
6652
|
+
}
|
|
6653
|
+
};
|
|
6654
|
+
var PLATFORM = process.platform;
|
|
6655
|
+
function run(cmd2, args, opts) {
|
|
6656
|
+
return exec.run(cmd2, args, opts || {});
|
|
6657
|
+
}
|
|
6658
|
+
var UNIT_NAME_RE = input.UNIT_NAME_RE;
|
|
6659
|
+
function unitNameViolation(unit) {
|
|
6660
|
+
return input.unitNameViolation(unit);
|
|
6661
|
+
}
|
|
6662
|
+
var systemd = {
|
|
6663
|
+
kind: "systemd",
|
|
6664
|
+
supportsUnits: true,
|
|
6665
|
+
supportsTransient: true,
|
|
6666
|
+
daemonReload() {
|
|
5878
6667
|
try {
|
|
5879
|
-
|
|
6668
|
+
return run("systemctl", ["--user", "daemon-reload"], { timeoutMs: 15e3 }) !== null;
|
|
5880
6669
|
} catch {
|
|
5881
|
-
|
|
5882
|
-
}
|
|
5883
|
-
for (const f of names) {
|
|
5884
|
-
let text = null;
|
|
5885
|
-
try {
|
|
5886
|
-
text = readFile(PJ(dir, f));
|
|
5887
|
-
} catch {
|
|
5888
|
-
continue;
|
|
5889
|
-
}
|
|
5890
|
-
const item = browserFromDesktop(text, kind, f);
|
|
5891
|
-
if (item) usable(item);
|
|
6670
|
+
return false;
|
|
5892
6671
|
}
|
|
5893
|
-
}
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
defaultSource = "mimeapps";
|
|
6672
|
+
},
|
|
6673
|
+
stopUnit(unit, opts) {
|
|
6674
|
+
if (unitNameViolation(unit)) return false;
|
|
6675
|
+
const o = opts || {};
|
|
6676
|
+
try {
|
|
6677
|
+
return run("systemctl", ["--user", "stop", unit], { timeoutMs: o.timeoutMs || 15e3 }) !== null;
|
|
6678
|
+
} catch {
|
|
6679
|
+
return false;
|
|
5902
6680
|
}
|
|
5903
|
-
}
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
const
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
6681
|
+
},
|
|
6682
|
+
// run() 失败返回 null 而不抛,try/catch 包裹会恒返回 true;如实回传成败。
|
|
6683
|
+
resetFailed(unit) {
|
|
6684
|
+
if (unitNameViolation(unit)) return false;
|
|
6685
|
+
return run("systemctl", ["--user", "reset-failed", unit], { timeoutMs: 1e4 }) !== null;
|
|
6686
|
+
},
|
|
6687
|
+
/** 单元活跃判定(三态):确认 active -> true;确认不活跃 -> false;查询未完成 -> null(未知)。
|
|
6688
|
+
* run() 失败只返回 null,「查询未完成」不可折成 false:instance/ops.js 删除数据目录前
|
|
6689
|
+
* 按「!== false 才放行删除」消费本函数,折假即超时窗口内误删沙箱数据。 */
|
|
6690
|
+
isUnitActive(unit) {
|
|
6691
|
+
if (!unit) return true;
|
|
6692
|
+
if (unitNameViolation(unit)) return false;
|
|
6693
|
+
try {
|
|
6694
|
+
const r = exec.runDetail("systemctl", ["--user", "is-active", unit], { timeoutMs: 8e3 });
|
|
6695
|
+
if (r.timedOut) return null;
|
|
6696
|
+
const state = String(r.stdout || "").trim();
|
|
6697
|
+
if (state === "active") return true;
|
|
6698
|
+
if (state || r.code !== null) return false;
|
|
6699
|
+
return null;
|
|
6700
|
+
} catch {
|
|
6701
|
+
return null;
|
|
5923
6702
|
}
|
|
5924
|
-
}
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
if (!defaultId && found.size === 1) {
|
|
5929
|
-
defaultId = [...found.keys()][0];
|
|
5930
|
-
defaultSource = "only-installed";
|
|
5931
|
-
}
|
|
5932
|
-
return { browsers: [...found.values()], defaultId, defaultSource, probed: notes };
|
|
5933
|
-
}
|
|
5934
|
-
function linuxDefaultFromMimeApps(readFile, exists, env, home, note) {
|
|
5935
|
-
const cfgHome = env.XDG_CONFIG_HOME || PJ(home, ".config");
|
|
5936
|
-
const cfgDirs = String(env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").filter(Boolean);
|
|
5937
|
-
const dataDirs = String(env.XDG_DATA_DIRS || "/usr/local/share:/usr/share").split(":").filter(Boolean);
|
|
5938
|
-
const files = [
|
|
5939
|
-
PJ(cfgHome, "mimeapps.list"),
|
|
5940
|
-
...cfgDirs.map((d) => PJ(d, "mimeapps.list")),
|
|
5941
|
-
...dataDirs.map((d) => PJ(d, "applications", "mimeapps.list"))
|
|
5942
|
-
];
|
|
5943
|
-
for (const f of files) {
|
|
5944
|
-
if (!exists(f)) continue;
|
|
5945
|
-
let text = null;
|
|
6703
|
+
},
|
|
6704
|
+
transientUnitFile(unit) {
|
|
6705
|
+
if (unitNameViolation(unit)) return null;
|
|
6706
|
+
let uid = 0;
|
|
5946
6707
|
try {
|
|
5947
|
-
|
|
6708
|
+
uid = os2.userInfo().uid;
|
|
5948
6709
|
} catch {
|
|
5949
|
-
continue;
|
|
5950
6710
|
}
|
|
5951
|
-
const
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
6711
|
+
const rt = process.env.XDG_RUNTIME_DIR || "/run/user/" + uid;
|
|
6712
|
+
return path2.join(rt, "systemd", "transient", unit + ".service");
|
|
6713
|
+
},
|
|
6714
|
+
/** 清理 stale transient 单元:stop/reset-failed/删单元文件/daemon-reload。
|
|
6715
|
+
* 必须 reload:删除文件后 systemd 仍缓存该单元为 loaded,否则 systemd-run 拒绝重建同名单元。
|
|
6716
|
+
* 返回 {ok, errors}:stop/reset-failed 对「从未加载的单元」非零退出属正常,不计入 ok;
|
|
6717
|
+
* 硬失败只有删单元文件与 daemon-reload(run() 失败返回 null 不抛,成败须如实回传)。 */
|
|
6718
|
+
cleanTransient(unit) {
|
|
6719
|
+
const errors = [];
|
|
6720
|
+
const bad = unitNameViolation(unit);
|
|
6721
|
+
if (bad) return { ok: false, errors: [bad] };
|
|
6722
|
+
exec.runDetail("systemctl", ["--user", "stop", unit + ".service"], { timeoutMs: 1e4 });
|
|
6723
|
+
exec.runDetail("systemctl", ["--user", "reset-failed", unit + ".service"], { timeoutMs: 1e4 });
|
|
6724
|
+
let unlinkOk = true;
|
|
6725
|
+
try {
|
|
6726
|
+
const f = this.transientUnitFile(unit);
|
|
6727
|
+
if (f) fs2.unlinkSync(f);
|
|
6728
|
+
} catch {
|
|
6729
|
+
unlinkOk = false;
|
|
6730
|
+
errors.push("unlink " + unit + ".service");
|
|
5965
6731
|
}
|
|
5966
|
-
|
|
6732
|
+
if (!exec.runDetail("systemctl", ["--user", "daemon-reload"], { timeoutMs: 15e3 }).ok) errors.push("daemon-reload");
|
|
6733
|
+
return { ok: unlinkOk && errors.length === 0, errors };
|
|
6734
|
+
},
|
|
6735
|
+
/** 以 transient 单元启动(独立 cgroup;每实例隔离)。
|
|
6736
|
+
* @param {{unit:string, cmd:string[], env?:object, props?:string[], workingDir?:string, description?:string, timeoutMs?:number}} o
|
|
6737
|
+
* props 为 systemd 属性(如 KillMode=process、MemoryMax=8G),平台层只拼装成 --property,不解释语义。 */
|
|
6738
|
+
startTransient(o) {
|
|
6739
|
+
const opts = o || {};
|
|
6740
|
+
const bad = unitNameViolation(opts.unit);
|
|
6741
|
+
if (bad) throw new Error("systemd-run \u62D2\u7EDD: " + bad);
|
|
6742
|
+
const args = ["--user", "--unit=" + opts.unit];
|
|
6743
|
+
if (opts.description) args.push("--description=" + opts.description);
|
|
6744
|
+
for (const p of opts.props || []) args.push("--property=" + p);
|
|
6745
|
+
for (const [k, v] of Object.entries(opts.env || {})) args.push("--setenv=" + k + "=" + v);
|
|
6746
|
+
if (opts.workingDir) args.push("--working-directory=" + opts.workingDir);
|
|
6747
|
+
args.push("--", ...opts.cmd || []);
|
|
6748
|
+
const r = exec.runDetail("systemd-run", args, { timeoutMs: opts.timeoutMs || 2e4 });
|
|
6749
|
+
if (!r.ok) throw new Error("systemd-run \u5931\u8D25: " + (r.error || "unknown") + (r.stderr ? " | " + String(r.stderr).trim() : ""));
|
|
6750
|
+
return true;
|
|
6751
|
+
},
|
|
6752
|
+
/** 运行期改限额(W3 动态化):systemctl --user set-property 立即生效,无需重启单元。
|
|
6753
|
+
* 必带 --runtime:transient 单元本不落盘,不带它会把 drop-in 写进用户配置目录,陈旧下限永久黏住。
|
|
6754
|
+
* 值只来自 sandbox.unitProps 同源的 alloc(平台层翻译语义、不决定数额)。 */
|
|
6755
|
+
setLimits(unit, alloc) {
|
|
6756
|
+
if (unitNameViolation(unit)) return false;
|
|
6757
|
+
const a = alloc || {};
|
|
6758
|
+
const props = [];
|
|
6759
|
+
if (a.memoryMax) props.push("MemoryMax=" + a.memoryMax);
|
|
6760
|
+
if (a.memoryHigh) props.push("MemoryHigh=" + a.memoryHigh);
|
|
6761
|
+
if (a.cpuQuota) props.push("CPUQuota=" + a.cpuQuota);
|
|
6762
|
+
if (!props.length) return false;
|
|
6763
|
+
return exec.runDetail(
|
|
6764
|
+
"systemctl",
|
|
6765
|
+
["--user", "set-property", "--runtime", unit].concat(props),
|
|
6766
|
+
{ timeoutMs: 1e4 }
|
|
6767
|
+
).ok;
|
|
5967
6768
|
}
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
6769
|
+
};
|
|
6770
|
+
function makeUnsupported(kind, label) {
|
|
6771
|
+
return {
|
|
6772
|
+
kind,
|
|
6773
|
+
supportsUnits: false,
|
|
6774
|
+
supportsTransient: false,
|
|
6775
|
+
daemonReload() {
|
|
6776
|
+
return false;
|
|
6777
|
+
},
|
|
6778
|
+
stopUnit() {
|
|
6779
|
+
throw new CapabilityError(label + "\uFF1A\u4E0D\u652F\u6301\u4EE5\u7528\u6237\u5355\u5143\u65B9\u5F0F\u7BA1\u7406\u88AB\u7BA1\u5B9E\u4F8B");
|
|
6780
|
+
},
|
|
6781
|
+
resetFailed() {
|
|
6782
|
+
return false;
|
|
6783
|
+
},
|
|
6784
|
+
isUnitActive(unit) {
|
|
6785
|
+
return unit ? false : true;
|
|
6786
|
+
},
|
|
6787
|
+
transientUnitFile() {
|
|
6788
|
+
return null;
|
|
6789
|
+
},
|
|
6790
|
+
// 无 transient 单元可清 = 成功;返回形态与 systemd 一致,调用方无需分支。
|
|
6791
|
+
cleanTransient() {
|
|
6792
|
+
return { ok: true, errors: [] };
|
|
6793
|
+
},
|
|
6794
|
+
startTransient() {
|
|
6795
|
+
throw new CapabilityError(label + "\uFF1A\u4E0D\u652F\u6301\u62C9\u8D77\u5B9E\u4F8B\u8231\uFF08portable \u6863\u672A\u542F\u7528\uFF09");
|
|
6796
|
+
},
|
|
6797
|
+
setLimits() {
|
|
6798
|
+
return false;
|
|
5977
6799
|
}
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
6800
|
+
};
|
|
6801
|
+
}
|
|
6802
|
+
var NONE = makeUnsupported("none", "\u5F53\u524D\u5E73\u53F0\u65E0\u670D\u52A1\u7BA1\u7406\u5668");
|
|
6803
|
+
var _systemdRun = null;
|
|
6804
|
+
function hasSystemdRun() {
|
|
6805
|
+
if (_systemdRun === null) {
|
|
6806
|
+
_systemdRun = !!execPath.resolveExecutable("systemd-run") || exec.runOut("systemd-run", ["--version"], { timeoutMs: 3e3 }) !== null;
|
|
5983
6807
|
}
|
|
5984
|
-
return
|
|
6808
|
+
return _systemdRun;
|
|
6809
|
+
}
|
|
6810
|
+
function current() {
|
|
6811
|
+
if (PLATFORM === "linux") return hasSystemdRun() ? systemd : portable;
|
|
6812
|
+
if (PLATFORM === "darwin" || PLATFORM === "win32") return portable;
|
|
6813
|
+
return NONE;
|
|
6814
|
+
}
|
|
6815
|
+
module2.exports = { current, CapabilityError, kind: () => current().kind, PLATFORM, UNIT_NAME_RE, unitNameViolation, _testProviders: { systemd, portable, NONE } };
|
|
6816
|
+
}
|
|
6817
|
+
});
|
|
6818
|
+
|
|
6819
|
+
// src/platform/os/notify.js
|
|
6820
|
+
var require_notify = __commonJS({
|
|
6821
|
+
"src/platform/os/notify.js"(exports2, module2) {
|
|
6822
|
+
"use strict";
|
|
6823
|
+
var spawnOS = require_spawn();
|
|
6824
|
+
function appleScriptString(s) {
|
|
6825
|
+
return JSON.stringify(String(s));
|
|
6826
|
+
}
|
|
6827
|
+
function powerShellString(s) {
|
|
6828
|
+
return "'" + String(s).replace(/'/g, "''") + "'";
|
|
5985
6829
|
}
|
|
5986
|
-
function
|
|
6830
|
+
function notifyCommand(platform, title, body) {
|
|
5987
6831
|
const pl = platform || process.platform;
|
|
5988
|
-
const
|
|
5989
|
-
|
|
5990
|
-
if (pl === "
|
|
5991
|
-
|
|
6832
|
+
const t = String(title == null ? "" : title);
|
|
6833
|
+
const b = String(body == null ? "" : body);
|
|
6834
|
+
if (pl === "linux") {
|
|
6835
|
+
return { cmd: "notify-send", args: ["-a", "dsh-supervisor", t, b] };
|
|
6836
|
+
}
|
|
6837
|
+
if (pl === "darwin") {
|
|
6838
|
+
const script = "display notification " + appleScriptString(b) + " with title " + appleScriptString(t);
|
|
6839
|
+
return { cmd: "osascript", args: ["-e", script] };
|
|
6840
|
+
}
|
|
6841
|
+
if (pl === "win32") {
|
|
6842
|
+
const ps = [
|
|
6843
|
+
'[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null',
|
|
6844
|
+
'[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null',
|
|
6845
|
+
"$n = New-Object System.Windows.Forms.NotifyIcon",
|
|
6846
|
+
"$n.Icon = [System.Drawing.SystemIcons]::Information",
|
|
6847
|
+
"$n.Visible = $true",
|
|
6848
|
+
"$n.ShowBalloonTip(4000, " + powerShellString(t) + ", " + powerShellString(b) + ", [System.Windows.Forms.ToolTipIcon]::None)",
|
|
6849
|
+
"Start-Sleep -Milliseconds 4200",
|
|
6850
|
+
"$n.Dispose(); $n.Visible = $false"
|
|
6851
|
+
].join("; ");
|
|
6852
|
+
return { cmd: "powershell", args: ["-NoProfile", "-NonInteractive", "-Command", ps] };
|
|
6853
|
+
}
|
|
6854
|
+
return null;
|
|
5992
6855
|
}
|
|
5993
|
-
|
|
5994
|
-
function inventory(platform, o) {
|
|
5995
|
-
const ov = o || {};
|
|
5996
|
-
const pl = platform || process.platform;
|
|
5997
|
-
const now = ov.now || (() => Date.now());
|
|
5998
|
-
const ttl = ov.ttlMs === void 0 ? 6e4 : ov.ttlMs;
|
|
5999
|
-
const cached = CACHE[pl];
|
|
6000
|
-
if (!ov.force && cached && now() - cached.at < ttl) return Object.assign({ cached: true }, cached.value);
|
|
6001
|
-
const runOut = ov.runOut || ((bin, args) => exec.runOut(bin, args, { timeoutMs: PROBE_TIMEOUT_MS }));
|
|
6002
|
-
const readFile = ov.readFile || ((p) => fs2.readFileSync(p, "utf8"));
|
|
6003
|
-
const exists = ov.exists || ((p) => fs2.existsSync(p));
|
|
6004
|
-
const listDir = ov.listDir || ((p) => fs2.readdirSync(p));
|
|
6005
|
-
let value;
|
|
6856
|
+
function notify(title, body, onError) {
|
|
6006
6857
|
try {
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6858
|
+
const plan = notifyCommand(process.platform, title, body);
|
|
6859
|
+
if (!plan) return false;
|
|
6860
|
+
const c = spawnOS.detachedIgnored(plan.cmd, plan.args);
|
|
6861
|
+
c.on("error", () => {
|
|
6862
|
+
if (onError) onError();
|
|
6863
|
+
});
|
|
6864
|
+
c.unref();
|
|
6865
|
+
return true;
|
|
6866
|
+
} catch {
|
|
6867
|
+
if (onError) onError();
|
|
6868
|
+
return false;
|
|
6010
6869
|
}
|
|
6011
|
-
value.at = now();
|
|
6012
|
-
value.platform = pl;
|
|
6013
|
-
value.browsers.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
6014
|
-
CACHE[pl] = { at: now(), value };
|
|
6015
|
-
return Object.assign({ cached: false }, value);
|
|
6016
|
-
}
|
|
6017
|
-
function invalidate(platform) {
|
|
6018
|
-
if (platform) delete CACHE[platform];
|
|
6019
|
-
else for (const k of Object.keys(CACHE)) delete CACHE[k];
|
|
6020
6870
|
}
|
|
6021
|
-
module2.exports = {
|
|
6022
|
-
inventory,
|
|
6023
|
-
invalidate,
|
|
6024
|
-
probe,
|
|
6025
|
-
probeWin,
|
|
6026
|
-
probeMac,
|
|
6027
|
-
probeLinux,
|
|
6028
|
-
engineOf,
|
|
6029
|
-
tokenizeExec,
|
|
6030
|
-
parseExecLine,
|
|
6031
|
-
regValueOf,
|
|
6032
|
-
expandEnvVars,
|
|
6033
|
-
exeFromCmdLine,
|
|
6034
|
-
regSubkeys,
|
|
6035
|
-
regKeyFull,
|
|
6036
|
-
regValueTargets,
|
|
6037
|
-
safeRegKeyPart,
|
|
6038
|
-
browserFromDesktop,
|
|
6039
|
-
desktopDirs,
|
|
6040
|
-
resolveLinuxBin,
|
|
6041
|
-
mimeAppsDefault,
|
|
6042
|
-
linuxDefaultFromMimeApps,
|
|
6043
|
-
WIN_APP_PATHS,
|
|
6044
|
-
PROBE_TIMEOUT_MS
|
|
6045
|
-
};
|
|
6871
|
+
module2.exports = { notify, notifyCommand, appleScriptString, powerShellString };
|
|
6046
6872
|
}
|
|
6047
6873
|
});
|
|
6048
6874
|
|
|
@@ -6055,8 +6881,10 @@ var require_browser = __commonJS({
|
|
|
6055
6881
|
var desktop = require_desktop();
|
|
6056
6882
|
var CAPABILITY_PROFILES = require_capability_profile();
|
|
6057
6883
|
var { resolveExecutable, isExecutableFile } = require_exec_path();
|
|
6884
|
+
var environment = require_environment();
|
|
6058
6885
|
var detector = require_browser_inventory();
|
|
6059
6886
|
var { engineOf } = detector;
|
|
6887
|
+
var { allocTempDir, removeTreeDeferred } = require_fs();
|
|
6060
6888
|
function isSafeHttpUrl(url) {
|
|
6061
6889
|
try {
|
|
6062
6890
|
const u = new URL(String(url));
|
|
@@ -6092,7 +6920,7 @@ var require_browser = __commonJS({
|
|
|
6092
6920
|
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";
|
|
6093
6921
|
var FAILURE_TEXT = {
|
|
6094
6922
|
"unsafe-url": "\u5730\u5740\u4E0D\u662F http(s) \u7EDD\u5BF9 URL\uFF0C\u5DF2\u62D2\u7EDD\u4EA4\u7ED9\u6D4F\u89C8\u5668",
|
|
6095
|
-
"no-launcher": "\u672A\
|
|
6923
|
+
"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",
|
|
6096
6924
|
"spawn-failed": "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF08\u7CFB\u7EDF\u62D2\u7EDD\u4E86\u8BE5\u547D\u4EE4\uFF09",
|
|
6097
6925
|
"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",
|
|
6098
6926
|
"killed-by-signal": "\u6D4F\u89C8\u5668\u542F\u52A8\u547D\u4EE4\u88AB\u7CFB\u7EDF\u7EC8\u6B62\uFF0C\u7A97\u53E3\u672A\u51FA\u73B0",
|
|
@@ -6100,17 +6928,10 @@ var require_browser = __commonJS({
|
|
|
6100
6928
|
"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"
|
|
6101
6929
|
};
|
|
6102
6930
|
function ownsItsWindow(via, platform) {
|
|
6931
|
+
if (via === "isolated") return true;
|
|
6103
6932
|
if (via !== "dispatcher") return false;
|
|
6104
6933
|
return platform !== "win32";
|
|
6105
6934
|
}
|
|
6106
|
-
function pickLauncher(platform, inv) {
|
|
6107
|
-
const list = inv && Array.isArray(inv.browsers) ? inv.browsers : [];
|
|
6108
|
-
const id = inv && inv.defaultId;
|
|
6109
|
-
const hit = id ? list.find((b) => b.id === id) : null;
|
|
6110
|
-
if (hit) return { browser: hit, how: inv && inv.defaultSource || "default" };
|
|
6111
|
-
if (list.length === 1) return { browser: list[0], how: "only-installed" };
|
|
6112
|
-
return { browser: null, how: list.length ? "no-default" : "none-found" };
|
|
6113
|
-
}
|
|
6114
6935
|
function formOfBin(pl, b) {
|
|
6115
6936
|
const engine = b && b.bin ? engineOf(b.bin) : "other";
|
|
6116
6937
|
return { engine, direct: !!b && (engine !== "other" || pl === "win32") };
|
|
@@ -6118,30 +6939,118 @@ var require_browser = __commonJS({
|
|
|
6118
6939
|
function openPlan(platform, url, opts) {
|
|
6119
6940
|
const o = opts || {};
|
|
6120
6941
|
const pl = platform || process.platform;
|
|
6121
|
-
const picked = o.pick || pickLauncher(pl, o.inventory);
|
|
6942
|
+
const picked = o.pick || environment.pickLauncher(pl, o.inventory, o.preference);
|
|
6122
6943
|
const b = picked.browser;
|
|
6123
6944
|
const form = formOfBin(pl, b);
|
|
6124
6945
|
if (form.direct) {
|
|
6125
6946
|
return {
|
|
6126
6947
|
bin: b.bin,
|
|
6948
|
+
args: (b.baseArgs || []).concat([url]),
|
|
6127
6949
|
engine: form.engine,
|
|
6128
6950
|
via: "browser",
|
|
6951
|
+
isolated: false,
|
|
6952
|
+
watch: false,
|
|
6953
|
+
envKind: "sys",
|
|
6129
6954
|
pick: picked.how,
|
|
6130
|
-
|
|
6955
|
+
stale: picked.stale === true,
|
|
6131
6956
|
exitIsEvidence: ownsItsWindow("browser", pl)
|
|
6132
6957
|
};
|
|
6133
6958
|
}
|
|
6134
6959
|
const c = openCommand(pl, url);
|
|
6135
|
-
if (!c) return {
|
|
6960
|
+
if (!c) return {
|
|
6961
|
+
bin: null,
|
|
6962
|
+
args: [url],
|
|
6963
|
+
engine: form.engine,
|
|
6964
|
+
via: "none",
|
|
6965
|
+
isolated: false,
|
|
6966
|
+
watch: false,
|
|
6967
|
+
envKind: "sys",
|
|
6968
|
+
pick: picked.how,
|
|
6969
|
+
stale: picked.stale === true,
|
|
6970
|
+
exitIsEvidence: false
|
|
6971
|
+
};
|
|
6136
6972
|
return {
|
|
6137
6973
|
bin: c.cmd,
|
|
6974
|
+
args: c.args,
|
|
6138
6975
|
engine: "other",
|
|
6139
6976
|
via: "dispatcher",
|
|
6977
|
+
isolated: false,
|
|
6978
|
+
watch: false,
|
|
6979
|
+
envKind: "sys",
|
|
6140
6980
|
pick: picked.how,
|
|
6141
|
-
|
|
6981
|
+
stale: picked.stale === true,
|
|
6142
6982
|
exitIsEvidence: ownsItsWindow("dispatcher", pl)
|
|
6143
6983
|
};
|
|
6144
6984
|
}
|
|
6985
|
+
function isolatedPlan(platform, url, opts) {
|
|
6986
|
+
const o = opts || {};
|
|
6987
|
+
const pl = platform || process.platform;
|
|
6988
|
+
const picked = o.pick || environment.pickLauncher(pl, o.inventory, o.preference);
|
|
6989
|
+
const b = picked.browser;
|
|
6990
|
+
const form = formOfBin(pl, b);
|
|
6991
|
+
const baseArgs = b && b.baseArgs || [];
|
|
6992
|
+
const common = { pick: picked.how, stale: picked.stale === true, engine: form.engine };
|
|
6993
|
+
const plain = (bin, label) => Object.assign(common, {
|
|
6994
|
+
bin,
|
|
6995
|
+
args: baseArgs.concat([url]),
|
|
6996
|
+
via: "browser",
|
|
6997
|
+
isolated: false,
|
|
6998
|
+
watch: false,
|
|
6999
|
+
envKind: "sys",
|
|
7000
|
+
label,
|
|
7001
|
+
exitIsEvidence: ownsItsWindow("browser", pl)
|
|
7002
|
+
});
|
|
7003
|
+
if (form.direct && form.engine === "chromium" && o.profileDir) {
|
|
7004
|
+
const args = ["--incognito", "--user-data-dir=" + o.profileDir];
|
|
7005
|
+
if (Array.isArray(o.size) && o.size.length === 2) args.push("--window-size=" + o.size[0] + "," + o.size[1]);
|
|
7006
|
+
if (o.lang) args.push("--lang=" + o.lang);
|
|
7007
|
+
args.push("--no-first-run", "--no-default-browser-check", "--disable-session-crashed-bubble");
|
|
7008
|
+
return Object.assign(common, {
|
|
7009
|
+
bin: b.bin,
|
|
7010
|
+
args: [...baseArgs, ...args, url],
|
|
7011
|
+
via: "isolated",
|
|
7012
|
+
isolated: true,
|
|
7013
|
+
watch: true,
|
|
7014
|
+
envKind: "anti",
|
|
7015
|
+
label: "chromium",
|
|
7016
|
+
exitIsEvidence: ownsItsWindow("isolated", pl)
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
7019
|
+
if (form.direct && form.engine === "firefox" && o.profileDir) {
|
|
7020
|
+
return Object.assign(common, {
|
|
7021
|
+
bin: b.bin,
|
|
7022
|
+
args: [...baseArgs, "--no-remote", "--profile", o.profileDir, "-private-window", url],
|
|
7023
|
+
via: "isolated",
|
|
7024
|
+
isolated: true,
|
|
7025
|
+
watch: true,
|
|
7026
|
+
envKind: "anti",
|
|
7027
|
+
label: "firefox",
|
|
7028
|
+
exitIsEvidence: ownsItsWindow("isolated", pl)
|
|
7029
|
+
});
|
|
7030
|
+
}
|
|
7031
|
+
if (form.direct) return plain(b.bin, path2.basename(b.bin));
|
|
7032
|
+
const c = openCommand(pl, url);
|
|
7033
|
+
if (!c) return Object.assign(common, {
|
|
7034
|
+
bin: null,
|
|
7035
|
+
args: [url],
|
|
7036
|
+
via: "none",
|
|
7037
|
+
isolated: false,
|
|
7038
|
+
watch: false,
|
|
7039
|
+
envKind: "sys",
|
|
7040
|
+
label: null,
|
|
7041
|
+
exitIsEvidence: false
|
|
7042
|
+
});
|
|
7043
|
+
return Object.assign(common, {
|
|
7044
|
+
bin: c.cmd,
|
|
7045
|
+
args: c.args,
|
|
7046
|
+
via: "dispatcher",
|
|
7047
|
+
isolated: false,
|
|
7048
|
+
watch: false,
|
|
7049
|
+
envKind: "sys",
|
|
7050
|
+
label: c.cmd,
|
|
7051
|
+
exitIsEvidence: ownsItsWindow("dispatcher", pl)
|
|
7052
|
+
});
|
|
7053
|
+
}
|
|
6145
7054
|
function observeSpawn(child, windowMs, setTimeoutFn) {
|
|
6146
7055
|
return new Promise((resolve) => {
|
|
6147
7056
|
let settled = false;
|
|
@@ -6159,105 +7068,32 @@ var require_browser = __commonJS({
|
|
|
6159
7068
|
});
|
|
6160
7069
|
}
|
|
6161
7070
|
var OPEN_OBSERVE_MS = 1500;
|
|
6162
|
-
|
|
7071
|
+
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"];
|
|
7072
|
+
var LOGIN_LANG_POOL = ["zh-CN", "en-US", "en-GB", "ja-JP", "ko-KR", "de-DE", "fr-FR", "zh-TW"];
|
|
7073
|
+
var LOGIN_SIZE_POOL = [[1280, 800], [1366, 768], [1440, 900], [1536, 864], [1600, 900], [1680, 1050], [1920, 1080], [1024, 768], [1152, 864], [1280, 720]];
|
|
7074
|
+
function loginEnv(rand) {
|
|
7075
|
+
const pick = (arr) => arr[Math.floor((typeof rand === "function" ? rand() : Math.random()) * arr.length)];
|
|
7076
|
+
const size = pick(LOGIN_SIZE_POOL);
|
|
7077
|
+
const lang = pick(LOGIN_LANG_POOL);
|
|
7078
|
+
const tz = pick(LOGIN_TZ_POOL);
|
|
7079
|
+
const sysEnv = Object.assign({}, process.env, desktop.sessionEnv());
|
|
7080
|
+
return { size, lang, sysEnv, antiEnv: Object.assign({}, sysEnv, { TZ: tz, LANG: lang }) };
|
|
7081
|
+
}
|
|
7082
|
+
function launchDiagnostics(inv, plan, picked) {
|
|
6163
7083
|
const found = (inv && inv.browsers || []).map((b) => ({
|
|
6164
7084
|
name: b.name,
|
|
6165
7085
|
engine: b.engine || engineOf(b.bin),
|
|
6166
7086
|
via: (b.sources && b.sources.length ? b.sources : [b.source || "unknown"]).join("+")
|
|
6167
7087
|
}));
|
|
7088
|
+
const pk = picked || {};
|
|
6168
7089
|
return {
|
|
6169
|
-
|
|
6170
|
-
pick: how || plan && plan.pick || "none",
|
|
6171
|
-
bin: plan && plan.bin || null,
|
|
7090
|
+
pick: pk.how || plan && plan.pick || "none",
|
|
6172
7091
|
default: inv && inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource || null } : null,
|
|
7092
|
+
preference: pk.wanted ? { id: pk.wanted, matched: pk.stale !== true } : null,
|
|
6173
7093
|
found,
|
|
6174
7094
|
probed: inv && inv.probed || []
|
|
6175
7095
|
};
|
|
6176
7096
|
}
|
|
6177
|
-
async function openBrowser(url, o) {
|
|
6178
|
-
const opts = o || {};
|
|
6179
|
-
const pl = opts.platform || process.platform;
|
|
6180
|
-
if (!isSafeHttpUrl(url)) {
|
|
6181
|
-
return outcome({ ok: false, reason: "unsafe-url", url: String(url || ""), evidence: null });
|
|
6182
|
-
}
|
|
6183
|
-
if (!SUPPORTED_OPEN_PLATFORMS.includes(pl)) {
|
|
6184
|
-
return outcome({ ok: false, reason: "unsupported-platform", url, evidence: { platform: pl } });
|
|
6185
|
-
}
|
|
6186
|
-
const observeMs = opts.observeMs === void 0 ? OPEN_OBSERVE_MS : opts.observeMs;
|
|
6187
|
-
const spawnWith = typeof opts.spawn === "function" ? opts.spawn : _spawnDetachedIgnored;
|
|
6188
|
-
const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
|
|
6189
|
-
const observe = typeof opts.observe === "function" ? opts.observe : observeSpawn;
|
|
6190
|
-
const resolveInv = typeof opts.resolveInventory === "function" ? opts.resolveInventory : ((platform, deps) => detector.inventory(platform, deps));
|
|
6191
|
-
const inv = "inventory" in opts ? opts.inventory : resolveInv(pl, opts.resolveDeps || {});
|
|
6192
|
-
const plan = openPlan(pl, url, { inventory: inv, pick: opts.pick });
|
|
6193
|
-
const diagnostics = launchDiagnostics(inv, plan, plan.pick);
|
|
6194
|
-
const evidence = {
|
|
6195
|
-
bin: plan.bin,
|
|
6196
|
-
engine: plan.engine,
|
|
6197
|
-
via: plan.via,
|
|
6198
|
-
ownsWindow: plan.exitIsEvidence === true,
|
|
6199
|
-
exitCode: null,
|
|
6200
|
-
exitSignal: null,
|
|
6201
|
-
error: null,
|
|
6202
|
-
diagnostics
|
|
6203
|
-
};
|
|
6204
|
-
const desktopAvailable = typeof opts.desktopAvailable === "function" ? opts.desktopAvailable : desktop.sessionAvailable;
|
|
6205
|
-
if (pl === "linux" && !desktopAvailable()) {
|
|
6206
|
-
return outcome({ ok: false, reason: "no-desktop-session", url, evidence });
|
|
6207
|
-
}
|
|
6208
|
-
if (!plan.bin) {
|
|
6209
|
-
return outcome({ ok: false, reason: "no-launcher", url, evidence });
|
|
6210
|
-
}
|
|
6211
|
-
if (!avail(plan.bin)) {
|
|
6212
|
-
return outcome({ ok: false, reason: "no-launcher", url, evidence });
|
|
6213
|
-
}
|
|
6214
|
-
let child;
|
|
6215
|
-
try {
|
|
6216
|
-
child = spawnWith(plan.bin, plan.baseArgs);
|
|
6217
|
-
} catch (e) {
|
|
6218
|
-
evidence.error = e && e.code || String(e);
|
|
6219
|
-
return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
6220
|
-
}
|
|
6221
|
-
if (!child) return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
6222
|
-
const seen = await observe(child, observeMs, opts.setTimeout);
|
|
6223
|
-
evidence.error = seen.stage === "error" ? String(seen.code) : null;
|
|
6224
|
-
evidence.exitCode = seen.code === void 0 ? null : seen.code;
|
|
6225
|
-
evidence.exitSignal = seen.signal === void 0 ? null : seen.signal;
|
|
6226
|
-
if (seen.stage === "error") return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
6227
|
-
const exitDecides = seen.stage === "exit" && plan.exitIsEvidence === true;
|
|
6228
|
-
if (exitDecides && (seen.code !== 0 || seen.signal)) {
|
|
6229
|
-
return outcome({
|
|
6230
|
-
ok: false,
|
|
6231
|
-
reason: seen.signal ? "killed-by-signal" : "exit-nonzero",
|
|
6232
|
-
url,
|
|
6233
|
-
evidence,
|
|
6234
|
-
error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
|
|
6235
|
-
});
|
|
6236
|
-
}
|
|
6237
|
-
return outcome({ ok: true, confirmed: exitDecides, handedOff: !exitDecides, url, evidence });
|
|
6238
|
-
}
|
|
6239
|
-
function listBrowsers(o) {
|
|
6240
|
-
const ov = o || {};
|
|
6241
|
-
const inv = detector.inventory(ov.platform, { force: ov.force === true });
|
|
6242
|
-
return {
|
|
6243
|
-
ok: true,
|
|
6244
|
-
platform: inv.platform,
|
|
6245
|
-
cached: inv.cached === true,
|
|
6246
|
-
default: inv.defaultId ? { id: inv.defaultId, source: inv.defaultSource || null } : null,
|
|
6247
|
-
browsers: (inv.browsers || []).map((b) => ({
|
|
6248
|
-
id: b.id,
|
|
6249
|
-
name: b.name,
|
|
6250
|
-
engine: b.engine || engineOf(b.bin),
|
|
6251
|
-
bin: b.bin,
|
|
6252
|
-
sources: b.sources && b.sources.length ? b.sources : [b.source],
|
|
6253
|
-
isDefault: b.id === inv.defaultId
|
|
6254
|
-
})),
|
|
6255
|
-
probed: inv.probed || []
|
|
6256
|
-
};
|
|
6257
|
-
}
|
|
6258
|
-
function invalidateBrowsers(platform) {
|
|
6259
|
-
return detector.invalidate(platform);
|
|
6260
|
-
}
|
|
6261
7097
|
function _spawnDetachedIgnored(bin, args) {
|
|
6262
7098
|
try {
|
|
6263
7099
|
const p = spawnOS.detachedIgnored(bin, args);
|
|
@@ -6287,86 +7123,120 @@ var require_browser = __commonJS({
|
|
|
6287
7123
|
child.unref();
|
|
6288
7124
|
return child;
|
|
6289
7125
|
}
|
|
6290
|
-
function isolatedPlan(platform, url, opts) {
|
|
6291
|
-
const o = opts || {};
|
|
6292
|
-
const pl = platform || process.platform;
|
|
6293
|
-
const picked = o.pick || pickLauncher(pl, o.inventory);
|
|
6294
|
-
const b = picked.browser;
|
|
6295
|
-
const form = formOfBin(pl, b);
|
|
6296
|
-
const baseArgs = b && b.baseArgs || [];
|
|
6297
|
-
if (form.direct && form.engine === "chromium" && o.profileDir) {
|
|
6298
|
-
const args = ["--incognito", "--user-data-dir=" + o.profileDir];
|
|
6299
|
-
if (Array.isArray(o.size) && o.size.length === 2) args.push("--window-size=" + o.size[0] + "," + o.size[1]);
|
|
6300
|
-
if (o.lang) args.push("--lang=" + o.lang);
|
|
6301
|
-
args.push("--no-first-run", "--no-default-browser-check", "--disable-session-crashed-bubble");
|
|
6302
|
-
return { bin: b.bin, args: [...baseArgs, ...args, url], isolated: true, watch: true, envKind: "anti", label: "chromium", pick: picked.how };
|
|
6303
|
-
}
|
|
6304
|
-
if (form.direct && form.engine === "firefox" && o.profileDir) {
|
|
6305
|
-
return {
|
|
6306
|
-
bin: b.bin,
|
|
6307
|
-
args: [...baseArgs, "--no-remote", "--profile", o.profileDir, "-private-window", url],
|
|
6308
|
-
isolated: true,
|
|
6309
|
-
watch: true,
|
|
6310
|
-
envKind: "anti",
|
|
6311
|
-
label: "firefox",
|
|
6312
|
-
pick: picked.how
|
|
6313
|
-
};
|
|
6314
|
-
}
|
|
6315
|
-
if (form.direct) {
|
|
6316
|
-
return { bin: b.bin, args: baseArgs.concat([url]), isolated: false, watch: false, envKind: "sys", label: path2.basename(b.bin), pick: picked.how };
|
|
6317
|
-
}
|
|
6318
|
-
const c = openCommand(pl, url);
|
|
6319
|
-
if (!c) return { bin: null, args: [url], isolated: false, watch: false, envKind: "sys", label: null, pick: picked.how };
|
|
6320
|
-
return { bin: c.cmd, args: c.args, isolated: false, watch: false, envKind: "sys", label: c.cmd, pick: picked.how };
|
|
6321
|
-
}
|
|
6322
7126
|
function binAvailable(bin) {
|
|
6323
7127
|
if (!bin) return false;
|
|
6324
7128
|
if (bin.includes("/") || bin.includes("\\") || /^[A-Za-z]:[\\/]/.test(bin)) return isExecutableFile(bin);
|
|
6325
7129
|
return resolveExecutable(bin) !== null;
|
|
6326
7130
|
}
|
|
6327
|
-
function
|
|
7131
|
+
async function openBrowser(url, o) {
|
|
6328
7132
|
const opts = o || {};
|
|
6329
|
-
|
|
6330
|
-
const
|
|
6331
|
-
|
|
6332
|
-
|
|
7133
|
+
const pl = opts.platform || process.platform;
|
|
7134
|
+
const intent = opts.intent === "isolated-login" ? "isolated-login" : "plain";
|
|
7135
|
+
if (!isSafeHttpUrl(url)) {
|
|
7136
|
+
return outcome({ ok: false, reason: "unsafe-url", url: String(url || ""), evidence: null });
|
|
7137
|
+
}
|
|
7138
|
+
if (!SUPPORTED_OPEN_PLATFORMS.includes(pl)) {
|
|
7139
|
+
return outcome({ ok: false, reason: "unsupported-platform", url, evidence: { platform: pl } });
|
|
7140
|
+
}
|
|
7141
|
+
const observeMs = opts.observeMs === void 0 ? OPEN_OBSERVE_MS : opts.observeMs;
|
|
7142
|
+
const spawnWith = typeof opts.spawn === "function" ? opts.spawn : intent === "isolated-login" ? _spawnDetached : _spawnDetachedIgnored;
|
|
6333
7143
|
const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
|
|
6334
|
-
const
|
|
6335
|
-
const
|
|
7144
|
+
const observe = typeof opts.observe === "function" ? opts.observe : observeSpawn;
|
|
7145
|
+
const resolveInv = typeof opts.resolveInventory === "function" ? opts.resolveInventory : ((platform, deps) => environment.browsers(Object.assign({ platform }, deps)));
|
|
7146
|
+
const inv = "inventory" in opts ? opts.inventory : resolveInv(pl, opts.resolveDeps || {});
|
|
7147
|
+
const pref = "preference" in opts ? opts.preference : void 0;
|
|
7148
|
+
const login = intent === "isolated-login" ? loginEnv(opts.rand) : null;
|
|
7149
|
+
const picked = opts.pick || environment.pickLauncher(pl, inv, pref);
|
|
7150
|
+
const form = formOfBin(pl, picked.browser);
|
|
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);
|
|
7154
|
+
let profile = null;
|
|
7155
|
+
if (isolate) {
|
|
7156
|
+
const alloc = typeof opts.allocProfile === "function" ? opts.allocProfile : (() => allocTempDir("dsh-login-"));
|
|
7157
|
+
try {
|
|
7158
|
+
profile = opts.profileDir || alloc();
|
|
7159
|
+
} catch {
|
|
7160
|
+
profile = null;
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
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 });
|
|
7164
|
+
const diagnostics = launchDiagnostics(inv, plan, picked);
|
|
7165
|
+
const evidence = {
|
|
7166
|
+
bin: plan.bin,
|
|
7167
|
+
engine: plan.engine,
|
|
7168
|
+
via: plan.via,
|
|
7169
|
+
ownsWindow: plan.exitIsEvidence === true,
|
|
7170
|
+
isolated: plan.isolated === true,
|
|
7171
|
+
profile: plan.isolated ? profile : null,
|
|
7172
|
+
watch: plan.watch === true,
|
|
7173
|
+
exitCode: null,
|
|
7174
|
+
exitSignal: null,
|
|
7175
|
+
error: null,
|
|
7176
|
+
diagnostics,
|
|
7177
|
+
egress: cold
|
|
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;
|
|
7181
|
+
const deferredRemove = typeof opts.rmTree === "function" ? opts.rmTree : removeTreeDeferred;
|
|
7182
|
+
const fail = (reason, patch) => {
|
|
7183
|
+
if (profile) deferredRemove(profile, 0);
|
|
7184
|
+
return outcome(Object.assign({ ok: false, reason, url, evidence }, patch));
|
|
7185
|
+
};
|
|
7186
|
+
const desktopAvailable = typeof opts.desktopAvailable === "function" ? opts.desktopAvailable : desktop.sessionAvailable;
|
|
7187
|
+
if (pl === "linux" && !desktopAvailable()) {
|
|
7188
|
+
return fail("no-desktop-session");
|
|
7189
|
+
}
|
|
7190
|
+
if (!plan.bin) return fail("no-launcher");
|
|
7191
|
+
if (!avail(plan.bin)) return fail("no-launcher");
|
|
7192
|
+
if (isolate && !profile) {
|
|
7193
|
+
evidence.error = "\u4E34\u65F6 profile \u76EE\u5F55\u5206\u914D\u5931\u8D25";
|
|
7194
|
+
return fail("spawn-failed");
|
|
7195
|
+
}
|
|
7196
|
+
let child;
|
|
6336
7197
|
try {
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
7198
|
+
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);
|
|
7199
|
+
} catch (e) {
|
|
7200
|
+
evidence.error = e && e.code || String(e);
|
|
7201
|
+
return fail("spawn-failed");
|
|
7202
|
+
}
|
|
7203
|
+
if (!child) return fail("spawn-failed");
|
|
7204
|
+
if (evidence.profile) deferredRemove(evidence.profile, opts.profileMs === void 0 ? 30 * 60 * 1e3 : opts.profileMs);
|
|
7205
|
+
const seen = await observe(child, observeMs, opts.setTimeout);
|
|
7206
|
+
evidence.error = seen.stage === "error" ? String(seen.code) : null;
|
|
7207
|
+
evidence.exitCode = seen.code === void 0 ? null : seen.code;
|
|
7208
|
+
evidence.exitSignal = seen.signal === void 0 ? null : seen.signal;
|
|
7209
|
+
if (seen.stage === "error") return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
7210
|
+
const exitDecides = seen.stage === "exit" && plan.exitIsEvidence === true;
|
|
7211
|
+
if (exitDecides && (seen.code !== 0 || seen.signal)) {
|
|
7212
|
+
return outcome({
|
|
7213
|
+
ok: false,
|
|
7214
|
+
reason: seen.signal ? "killed-by-signal" : "exit-nonzero",
|
|
7215
|
+
url,
|
|
7216
|
+
evidence,
|
|
7217
|
+
error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
|
|
6345
7218
|
});
|
|
6346
|
-
const evidence = { bin: plan.bin, engine: plan.label, via: plan.isolated ? "browser" : "dispatcher", diagnostics: launchDiagnostics(inv, plan, plan.pick) };
|
|
6347
|
-
if (!avail(plan.bin)) return fail("no-launcher", { evidence });
|
|
6348
|
-
const env = plan.envKind === "anti" ? antiEnv : sysEnv;
|
|
6349
|
-
const p = spawnWith(plan.bin, plan.args, env, plan.watch ? onExit : void 0);
|
|
6350
|
-
if (!p) return fail("spawn-failed", { evidence });
|
|
6351
|
-
return Object.assign(outcome({ ok: true, confirmed: false, handedOff: true, url, evidence }), { bin: plan.label, isolated: plan.isolated });
|
|
6352
|
-
} catch (e) {
|
|
6353
|
-
return fail("spawn-failed", { error: FAILURE_TEXT["spawn-failed"] + "\uFF08" + (e && e.code || e) + "\uFF09" });
|
|
6354
7219
|
}
|
|
7220
|
+
return outcome({
|
|
7221
|
+
ok: true,
|
|
7222
|
+
confirmed: exitDecides,
|
|
7223
|
+
handedOff: !exitDecides,
|
|
7224
|
+
url,
|
|
7225
|
+
evidence,
|
|
7226
|
+
message: downgradeText || void 0
|
|
7227
|
+
});
|
|
6355
7228
|
}
|
|
6356
7229
|
module2.exports = {
|
|
6357
7230
|
openBrowser,
|
|
6358
|
-
launchIsolated,
|
|
6359
7231
|
openCommand,
|
|
6360
7232
|
openPlan,
|
|
6361
7233
|
isolatedPlan,
|
|
6362
|
-
pickLauncher,
|
|
6363
7234
|
ownsItsWindow,
|
|
6364
7235
|
observeSpawn,
|
|
6365
7236
|
isSafeHttpUrl,
|
|
6366
7237
|
binAvailable,
|
|
6367
|
-
listBrowsers,
|
|
6368
|
-
invalidateBrowsers,
|
|
6369
7238
|
launchDiagnostics,
|
|
7239
|
+
loginEnv,
|
|
6370
7240
|
// 探测层的解析原语经此转出(唯一实现处在 ./browser-inventory.js):门禁与消费方按同一出口取用,
|
|
6371
7241
|
// 不在别处再写第二份平台解析。
|
|
6372
7242
|
engineOf,
|
|
@@ -6723,6 +7593,7 @@ var require_os = __commonJS({
|
|
|
6723
7593
|
var execPath = require_exec_path();
|
|
6724
7594
|
var desktop = require_desktop();
|
|
6725
7595
|
var CAPABILITY_PROFILES = require_capability_profile();
|
|
7596
|
+
var environment = require_environment();
|
|
6726
7597
|
var PLATFORM = process.platform;
|
|
6727
7598
|
var ARCH = process.arch;
|
|
6728
7599
|
var isLinux = PLATFORM === "linux";
|
|
@@ -6777,6 +7648,7 @@ var require_os = __commonJS({
|
|
|
6777
7648
|
}
|
|
6778
7649
|
return p;
|
|
6779
7650
|
}
|
|
7651
|
+
environment.bind({ capabilities });
|
|
6780
7652
|
module2.exports = {
|
|
6781
7653
|
PLATFORM,
|
|
6782
7654
|
ARCH,
|
|
@@ -6800,6 +7672,8 @@ var require_os = __commonJS({
|
|
|
6800
7672
|
// 导出模块对象会抛 platform.notify is not a function,把升级终态误判成失败。
|
|
6801
7673
|
notify: require_notify().notify,
|
|
6802
7674
|
browser: require_browser(),
|
|
7675
|
+
environment,
|
|
7676
|
+
// 环境表单(外部打开链路的事实底座 + 选路依据)
|
|
6803
7677
|
desktop,
|
|
6804
7678
|
// 图形会话可用性(Linux 需实测 socket)
|
|
6805
7679
|
autostart: require_autostart()
|
|
@@ -6818,6 +7692,7 @@ var require_bootstrap = __commonJS({
|
|
|
6818
7692
|
var { createShellWatchdog } = require_watchdog();
|
|
6819
7693
|
var pidlook = require_pidlookup();
|
|
6820
7694
|
var platform = require_os();
|
|
7695
|
+
var ENV_FORM_STARTUP_DELAY_MS = 3e3;
|
|
6821
7696
|
function _bootstrap(host2) {
|
|
6822
7697
|
try {
|
|
6823
7698
|
host2.events.append("guard_started", {
|
|
@@ -6833,6 +7708,15 @@ var require_bootstrap = __commonJS({
|
|
|
6833
7708
|
} catch {
|
|
6834
7709
|
}
|
|
6835
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
|
+
};
|
|
6836
7720
|
host2.tick();
|
|
6837
7721
|
host2._timer = host2.managedObjects ? null : setInterval(() => host2.tick(), host2.config.probeIntervalMs);
|
|
6838
7722
|
host2._lastHeartbeatAt = Date.now();
|
|
@@ -6888,6 +7772,7 @@ var require_bootstrap = __commonJS({
|
|
|
6888
7772
|
}
|
|
6889
7773
|
if (host2._routerDaemonActive()) host2._writeRouterDaemonLock();
|
|
6890
7774
|
const rt = host2._ensureRouterRuntime(true);
|
|
7775
|
+
host2._startupFacts.routerMode = rt.mode;
|
|
6891
7776
|
if (rt.mode === "daemon") {
|
|
6892
7777
|
host2._disableRouterPersist();
|
|
6893
7778
|
if (rt.spawned) {
|
|
@@ -6928,13 +7813,20 @@ var require_bootstrap = __commonJS({
|
|
|
6928
7813
|
rlc._monitoring = false;
|
|
6929
7814
|
}
|
|
6930
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
|
+
};
|
|
6931
7823
|
if (host2.config.updateCheckEnabled !== false) {
|
|
6932
7824
|
host2._initialCheckTimer = setTimeout(() => {
|
|
6933
7825
|
host2.nativeManager.checkUpdate();
|
|
6934
|
-
},
|
|
7826
|
+
}, checkDelayMs);
|
|
6935
7827
|
host2._upgradeTimer = setInterval(() => {
|
|
6936
7828
|
host2.nativeManager.checkUpdate();
|
|
6937
|
-
},
|
|
7829
|
+
}, checkIntervalMs);
|
|
6938
7830
|
}
|
|
6939
7831
|
try {
|
|
6940
7832
|
host2._startShellWatchdog();
|
|
@@ -6943,6 +7835,50 @@ var require_bootstrap = __commonJS({
|
|
|
6943
7835
|
host2.logger.warn("[shell-watchdog] \u542F\u52A8\u5F02\u5E38\uFF08\u4E0D\u5F71\u54CD\u5B88\u536B\u4E3B\u5FAA\u73AF\uFF09: " + (e && e.message || e));
|
|
6944
7836
|
}
|
|
6945
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
|
+
});
|
|
6946
7882
|
}
|
|
6947
7883
|
function _startShellWatchdog(host2) {
|
|
6948
7884
|
if (host2.config.shellWatchdog === false) {
|
|
@@ -7012,7 +7948,7 @@ var require_bootstrap = __commonJS({
|
|
|
7012
7948
|
}
|
|
7013
7949
|
}
|
|
7014
7950
|
}
|
|
7015
|
-
module2.exports = { _bootstrap, _startShellWatchdog, _registerFixedPorts, _bindNativeDshCommand };
|
|
7951
|
+
module2.exports = { _bootstrap, _startShellWatchdog, _registerFixedPorts, _bindNativeDshCommand, _refreshEnvironmentForm };
|
|
7016
7952
|
}
|
|
7017
7953
|
});
|
|
7018
7954
|
|
|
@@ -7542,7 +8478,7 @@ var require_release = __commonJS({
|
|
|
7542
8478
|
});
|
|
7543
8479
|
|
|
7544
8480
|
// src/platform/contract/registry.js
|
|
7545
|
-
var
|
|
8481
|
+
var require_registry2 = __commonJS({
|
|
7546
8482
|
"src/platform/contract/registry.js"(exports2, module2) {
|
|
7547
8483
|
"use strict";
|
|
7548
8484
|
var fs2 = require("node:fs");
|
|
@@ -7658,7 +8594,7 @@ var require_registry_config = __commonJS({
|
|
|
7658
8594
|
"use strict";
|
|
7659
8595
|
var fs2 = require("node:fs");
|
|
7660
8596
|
var path2 = require("node:path");
|
|
7661
|
-
var registryContract =
|
|
8597
|
+
var registryContract = require_registry2();
|
|
7662
8598
|
var { writeAtomic } = require_fs();
|
|
7663
8599
|
var policies = require_policies();
|
|
7664
8600
|
var CONTRACT_TTL_MS = 60 * 1e3;
|
|
@@ -7816,7 +8752,7 @@ var require_matrix = __commonJS({
|
|
|
7816
8752
|
});
|
|
7817
8753
|
|
|
7818
8754
|
// src/platform/distribution/registry.js
|
|
7819
|
-
var
|
|
8755
|
+
var require_registry3 = __commonJS({
|
|
7820
8756
|
"src/platform/distribution/registry.js"(exports2, module2) {
|
|
7821
8757
|
"use strict";
|
|
7822
8758
|
var matrix = require_matrix();
|
|
@@ -8037,7 +8973,7 @@ var require_version_check = __commonJS({
|
|
|
8037
8973
|
var { VERSION_RE } = require_version2();
|
|
8038
8974
|
var input = require_input();
|
|
8039
8975
|
var policies = require_policies();
|
|
8040
|
-
var registry =
|
|
8976
|
+
var registry = require_registry3();
|
|
8041
8977
|
var ref = require_registry_ref();
|
|
8042
8978
|
var release = require_release();
|
|
8043
8979
|
var PKG_NAME_RE = input.PKG_NAME_RE;
|
|
@@ -8404,7 +9340,7 @@ var require_distribution = __commonJS({
|
|
|
8404
9340
|
var policies = require_policies();
|
|
8405
9341
|
var release = require_release();
|
|
8406
9342
|
var registryConfig = require_registry_config();
|
|
8407
|
-
var registry =
|
|
9343
|
+
var registry = require_registry3();
|
|
8408
9344
|
var versionCheck = require_version_check();
|
|
8409
9345
|
var install = require_install();
|
|
8410
9346
|
var { semverCompare, VERSION_RE } = require_version2();
|
|
@@ -12918,6 +13854,60 @@ var require_access = __commonJS({
|
|
|
12918
13854
|
}
|
|
12919
13855
|
});
|
|
12920
13856
|
|
|
13857
|
+
// src/app/settings/browser.js
|
|
13858
|
+
var require_browser2 = __commonJS({
|
|
13859
|
+
"src/app/settings/browser.js"(exports2, module2) {
|
|
13860
|
+
"use strict";
|
|
13861
|
+
var platform = require_os();
|
|
13862
|
+
var { verifyPersisted } = require_access();
|
|
13863
|
+
module2.exports = {
|
|
13864
|
+
methods: {
|
|
13865
|
+
/** 当前偏好 + 候选清单 + 这一拍的分发依据(面板据此渲染选择器并说明「现在实际会用谁」)。 */
|
|
13866
|
+
externalBrowserStatus() {
|
|
13867
|
+
try {
|
|
13868
|
+
const form = platform.environment.form();
|
|
13869
|
+
const v = platform.environment.checkPreference(platform.environment.preferenceId(), form);
|
|
13870
|
+
return {
|
|
13871
|
+
ok: true,
|
|
13872
|
+
configured: !!form.preference && form.preference.configured === true,
|
|
13873
|
+
value: form.preference && form.preference.id || null,
|
|
13874
|
+
// stale=true:偏好所指已不在候选清单(被卸载/路径失效),本次分发已回落,需用户重选。
|
|
13875
|
+
stale: !!(form.pick && form.pick.stale),
|
|
13876
|
+
browser: v.browser,
|
|
13877
|
+
candidates: v.candidates,
|
|
13878
|
+
pick: form.pick || null,
|
|
13879
|
+
platform: form.platform
|
|
13880
|
+
};
|
|
13881
|
+
} catch (e) {
|
|
13882
|
+
return { ok: false, error: e.message };
|
|
13883
|
+
}
|
|
13884
|
+
},
|
|
13885
|
+
/** 设置/清除浏览器偏好(空串=清除,回到「按系统默认或候选次序分发」)。 */
|
|
13886
|
+
setExternalBrowser(id) {
|
|
13887
|
+
try {
|
|
13888
|
+
const form = platform.environment.form();
|
|
13889
|
+
const v = platform.environment.checkPreference(id, form);
|
|
13890
|
+
if (!v.ok) return { ok: false, error: v.error };
|
|
13891
|
+
const cfg = this.config || {};
|
|
13892
|
+
cfg.externalBrowser = v.id;
|
|
13893
|
+
const patch = { externalBrowser: v.id };
|
|
13894
|
+
if (this.configPath) {
|
|
13895
|
+
this.state.persistConfigPatch(patch);
|
|
13896
|
+
const persistError = verifyPersisted(this.configPath, patch);
|
|
13897
|
+
if (persistError) return { ok: false, error: persistError };
|
|
13898
|
+
}
|
|
13899
|
+
platform.environment.invalidate();
|
|
13900
|
+
if (this.events) this.events.append("external_browser_changed", { id: v.id, name: v.browser ? v.browser.name : null });
|
|
13901
|
+
return { ok: true, configured: !!v.id, value: v.id, browser: v.browser };
|
|
13902
|
+
} catch (e) {
|
|
13903
|
+
return { ok: false, error: e.message };
|
|
13904
|
+
}
|
|
13905
|
+
}
|
|
13906
|
+
}
|
|
13907
|
+
};
|
|
13908
|
+
}
|
|
13909
|
+
});
|
|
13910
|
+
|
|
12921
13911
|
// src/platform/os/netinfo.js
|
|
12922
13912
|
var require_netinfo = __commonJS({
|
|
12923
13913
|
"src/platform/os/netinfo.js"(exports2, module2) {
|
|
@@ -13135,6 +14125,7 @@ var require_facets = __commonJS({
|
|
|
13135
14125
|
{ name: "settings/node-lts", mod: require_node_lts() },
|
|
13136
14126
|
{ name: "settings/versions", mod: require_versions() },
|
|
13137
14127
|
{ name: "settings/access", mod: require_access() },
|
|
14128
|
+
{ name: "settings/browser", mod: require_browser2() },
|
|
13138
14129
|
{ name: "settings/lan-panel", mod: require_lan_panel() }
|
|
13139
14130
|
];
|
|
13140
14131
|
function installMethods(host2, methods) {
|
|
@@ -14016,6 +15007,77 @@ var require_token = __commonJS({
|
|
|
14016
15007
|
}
|
|
14017
15008
|
});
|
|
14018
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
|
+
|
|
14019
15081
|
// src/domains/shell/journal.js
|
|
14020
15082
|
var require_journal = __commonJS({
|
|
14021
15083
|
"src/domains/shell/journal.js"(exports2, module2) {
|
|
@@ -14668,6 +15730,9 @@ var require_core5 = __commonJS({
|
|
|
14668
15730
|
var { extension: domainConfigExtension2 } = require_domain_config();
|
|
14669
15731
|
var logcore = require_logcore();
|
|
14670
15732
|
var { DshTokenService } = require_token();
|
|
15733
|
+
var { EnvCatalog } = require_env_catalog();
|
|
15734
|
+
var npm = require_npm();
|
|
15735
|
+
var FORM_NPM_ROOT_TIMEOUT_MS = 4e3;
|
|
14671
15736
|
var { DistributionManager } = require_distribution();
|
|
14672
15737
|
var shellDomain = require_shell();
|
|
14673
15738
|
var { TaskRegistry } = require_tasks();
|
|
@@ -14677,6 +15742,69 @@ var require_core5 = __commonJS({
|
|
|
14677
15742
|
function composeCore(host2, rawConfig, configPath) {
|
|
14678
15743
|
host2.config = normalize(rawConfig, domainConfigExtension2());
|
|
14679
15744
|
host2.configPath = typeof configPath === "string" ? configPath : null;
|
|
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
|
+
});
|
|
14680
15808
|
host2._fileProtectStatus = null;
|
|
14681
15809
|
try {
|
|
14682
15810
|
const fp = platform.fileProtect;
|
|
@@ -18211,58 +19339,13 @@ var require_forward_core = __commonJS({
|
|
|
18211
19339
|
}
|
|
18212
19340
|
});
|
|
18213
19341
|
|
|
18214
|
-
// src/domains/router/ops/browser.js
|
|
18215
|
-
var require_browser2 = __commonJS({
|
|
18216
|
-
"src/domains/router/ops/browser.js"(exports2, module2) {
|
|
18217
|
-
"use strict";
|
|
18218
|
-
var platform = require_os();
|
|
18219
|
-
var desktop = require_desktop();
|
|
18220
|
-
var fs2 = require("node:fs");
|
|
18221
|
-
var os2 = require("node:os");
|
|
18222
|
-
var path2 = require("node:path");
|
|
18223
|
-
var crypto = require("node:crypto");
|
|
18224
|
-
function graphicalEnv() {
|
|
18225
|
-
return desktop.sessionEnv();
|
|
18226
|
-
}
|
|
18227
|
-
function openInBrowser(url, onExit) {
|
|
18228
|
-
try {
|
|
18229
|
-
const tmpProfile = path2.join(os2.tmpdir(), "dsh-oauth-" + crypto.randomBytes(8).toString("hex"));
|
|
18230
|
-
const sysEnv = Object.assign({}, process.env, graphicalEnv());
|
|
18231
|
-
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"];
|
|
18232
|
-
const LANG_POOL = ["zh-CN", "en-US", "en-GB", "ja-JP", "ko-KR", "de-DE", "fr-FR", "zh-TW"];
|
|
18233
|
-
const SIZE_POOL = [[1280, 800], [1366, 768], [1440, 900], [1536, 864], [1600, 900], [1680, 1050], [1920, 1080], [1024, 768], [1152, 864], [1280, 720]];
|
|
18234
|
-
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
18235
|
-
const size = pick(SIZE_POOL);
|
|
18236
|
-
const lang = pick(LANG_POOL);
|
|
18237
|
-
const tz = pick(TZ_POOL);
|
|
18238
|
-
const antiEnv = Object.assign({}, sysEnv, { TZ: tz, LANG: lang });
|
|
18239
|
-
const r = platform.browser.launchIsolated(url, { profileDir: tmpProfile, size, lang, antiEnv, sysEnv, onExit });
|
|
18240
|
-
if (!r || !r.ok) return { profile: null, result: r || { ok: false, reason: "spawn-failed" } };
|
|
18241
|
-
if (r.isolated) {
|
|
18242
|
-
const t30 = setTimeout(() => {
|
|
18243
|
-
try {
|
|
18244
|
-
fs2.rmSync(tmpProfile, { recursive: true, force: true });
|
|
18245
|
-
} catch {
|
|
18246
|
-
}
|
|
18247
|
-
}, 30 * 60 * 1e3);
|
|
18248
|
-
if (t30.unref) t30.unref();
|
|
18249
|
-
}
|
|
18250
|
-
return { profile: tmpProfile, result: r };
|
|
18251
|
-
} catch (e) {
|
|
18252
|
-
return { profile: null, result: { ok: false, reason: "spawn-failed", error: "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF1A" + (e && e.message || e) } };
|
|
18253
|
-
}
|
|
18254
|
-
}
|
|
18255
|
-
module2.exports = { graphicalEnv, openInBrowser };
|
|
18256
|
-
}
|
|
18257
|
-
});
|
|
18258
|
-
|
|
18259
19342
|
// src/domains/router/ops/oauth.js
|
|
18260
19343
|
var require_oauth = __commonJS({
|
|
18261
19344
|
"src/domains/router/ops/oauth.js"(exports2, module2) {
|
|
18262
19345
|
"use strict";
|
|
18263
|
-
var fs2 = require("node:fs");
|
|
18264
19346
|
var crypto = require("node:crypto");
|
|
18265
19347
|
var { createServer } = require("node:http");
|
|
19348
|
+
var { removeTreeDeferred } = require_fs();
|
|
18266
19349
|
function createOAuthOps(deps) {
|
|
18267
19350
|
const d = deps || {};
|
|
18268
19351
|
const ports = d.ports;
|
|
@@ -18427,20 +19510,21 @@ var require_oauth = __commonJS({
|
|
|
18427
19510
|
promise.catch(() => {
|
|
18428
19511
|
});
|
|
18429
19512
|
st._ccLoginPromise = promise;
|
|
18430
|
-
const opened = openInBrowser(authUrl, () => {
|
|
19513
|
+
const opened = await openInBrowser(authUrl, () => {
|
|
18431
19514
|
if (st._ccLoginRound !== roundId) return;
|
|
18432
19515
|
if (st._ccLoginReject) {
|
|
18433
|
-
const
|
|
19516
|
+
const r2 = st._ccLoginReject;
|
|
18434
19517
|
st._ccLoginReject = null;
|
|
18435
19518
|
st._ccLoginResolve = null;
|
|
18436
19519
|
try {
|
|
18437
|
-
|
|
19520
|
+
r2(new Error("\u6D4F\u89C8\u5668\u5DF2\u5173\u95ED\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88"));
|
|
18438
19521
|
} catch {
|
|
18439
19522
|
}
|
|
18440
19523
|
}
|
|
18441
19524
|
});
|
|
18442
|
-
const
|
|
18443
|
-
|
|
19525
|
+
const r = opened || { ok: false, reason: "spawn-failed" };
|
|
19526
|
+
const ev = r.evidence || {};
|
|
19527
|
+
if (!r.ok) {
|
|
18444
19528
|
st._ccLoginPromise = null;
|
|
18445
19529
|
st._ccLoginResolve = null;
|
|
18446
19530
|
st._ccLoginReject = null;
|
|
@@ -18460,13 +19544,13 @@ var require_oauth = __commonJS({
|
|
|
18460
19544
|
opened: false,
|
|
18461
19545
|
confirmed: false,
|
|
18462
19546
|
handedOff: false,
|
|
18463
|
-
reason:
|
|
18464
|
-
evidence:
|
|
18465
|
-
error: (
|
|
19547
|
+
reason: r.reason || "no-launcher",
|
|
19548
|
+
evidence: r.evidence || null,
|
|
19549
|
+
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"
|
|
18466
19550
|
};
|
|
18467
19551
|
}
|
|
18468
|
-
st._ccLogin = { state, port, server, tmpProfile:
|
|
18469
|
-
return Object.assign({},
|
|
19552
|
+
st._ccLogin = { state, port, server, tmpProfile: ev.profile || null };
|
|
19553
|
+
return Object.assign({}, r, {
|
|
18470
19554
|
ok: true,
|
|
18471
19555
|
authUrl,
|
|
18472
19556
|
url: authUrl,
|
|
@@ -18474,8 +19558,13 @@ var require_oauth = __commonJS({
|
|
|
18474
19558
|
port,
|
|
18475
19559
|
waitMs: 18e4,
|
|
18476
19560
|
opened: true,
|
|
18477
|
-
// 非隔离引擎(Safari
|
|
18478
|
-
isolated:
|
|
19561
|
+
// 非隔离引擎(Safari/打包器包装)由平台层降级:账号隔离不成立,换账号只能靠超时重发或手动窗口。
|
|
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
|
|
18479
19568
|
});
|
|
18480
19569
|
}
|
|
18481
19570
|
async function commandcodeLoginWait(timeoutMs) {
|
|
@@ -18521,15 +19610,7 @@ var require_oauth = __commonJS({
|
|
|
18521
19610
|
st._ccLoginResolve = st._ccLoginReject = null;
|
|
18522
19611
|
return { ok: false, error: e.message };
|
|
18523
19612
|
} finally {
|
|
18524
|
-
if (tmpProfile)
|
|
18525
|
-
const t60 = setTimeout(() => {
|
|
18526
|
-
try {
|
|
18527
|
-
fs2.rmSync(tmpProfile, { recursive: true, force: true });
|
|
18528
|
-
} catch {
|
|
18529
|
-
}
|
|
18530
|
-
}, 60 * 1e3);
|
|
18531
|
-
if (t60.unref) t60.unref();
|
|
18532
|
-
}
|
|
19613
|
+
if (tmpProfile) removeTreeDeferred(tmpProfile, 60 * 1e3);
|
|
18533
19614
|
}
|
|
18534
19615
|
}
|
|
18535
19616
|
return { commandcodeLoginStart, commandcodeLoginWait };
|
|
@@ -18983,8 +20064,8 @@ var require_router_ops = __commonJS({
|
|
|
18983
20064
|
"use strict";
|
|
18984
20065
|
require_port_segments();
|
|
18985
20066
|
var ports = require_ports().shared;
|
|
20067
|
+
var platform = require_os();
|
|
18986
20068
|
var { maskKey } = require_base();
|
|
18987
|
-
var { openInBrowser } = require_browser2();
|
|
18988
20069
|
var { createOAuthOps } = require_oauth();
|
|
18989
20070
|
var { createAppsRegistryOps } = require_apps_registry();
|
|
18990
20071
|
var { createQuotaSyncOps } = require_quotasync();
|
|
@@ -18992,6 +20073,7 @@ var require_router_ops = __commonJS({
|
|
|
18992
20073
|
function createAuxCore(deps) {
|
|
18993
20074
|
const d = deps || {};
|
|
18994
20075
|
const getProviders = d.getProviders || (() => []);
|
|
20076
|
+
const openInBrowser = (url, onExit) => platform.browser.openBrowser(url, { intent: "isolated-login", onExit });
|
|
18995
20077
|
const oauth = createOAuthOps({ ports, openInBrowser });
|
|
18996
20078
|
const apps = createAppsRegistryOps({
|
|
18997
20079
|
getProviders,
|
|
@@ -23843,7 +24925,7 @@ var require_heartbeat = __commonJS({
|
|
|
23843
24925
|
});
|
|
23844
24926
|
|
|
23845
24927
|
// src/app/control/registry.js
|
|
23846
|
-
var
|
|
24928
|
+
var require_registry4 = __commonJS({
|
|
23847
24929
|
"src/app/control/registry.js"(exports2, module2) {
|
|
23848
24930
|
"use strict";
|
|
23849
24931
|
var fs2 = require("node:fs");
|
|
@@ -24414,77 +25496,6 @@ var require_manifest = __commonJS({
|
|
|
24414
25496
|
}
|
|
24415
25497
|
});
|
|
24416
25498
|
|
|
24417
|
-
// src/app/native/npm.js
|
|
24418
|
-
var require_npm = __commonJS({
|
|
24419
|
-
"src/app/native/npm.js"(exports2, module2) {
|
|
24420
|
-
"use strict";
|
|
24421
|
-
var ex2 = require_exec();
|
|
24422
|
-
var runtimeContract = require_runtime();
|
|
24423
|
-
function npmLaunch(host2) {
|
|
24424
|
-
const h = host2 || {};
|
|
24425
|
-
if (h._npmBin) {
|
|
24426
|
-
return { program: h._npmBin, args: Array.isArray(h._npmBinArgs) ? h._npmBinArgs.slice() : [] };
|
|
24427
|
-
}
|
|
24428
|
-
const l = runtimeContract.npmLauncher();
|
|
24429
|
-
return { program: l.program, args: l.args };
|
|
24430
|
-
}
|
|
24431
|
-
async function resolveNpmRoot(host2) {
|
|
24432
|
-
if (host2.npmRoot) return host2.npmRoot;
|
|
24433
|
-
const l = npmLaunch(host2);
|
|
24434
|
-
const r = await ex2.runOutAsync(l.program, l.args.concat(["root", "-g"]));
|
|
24435
|
-
return r ? r.trim() : null;
|
|
24436
|
-
}
|
|
24437
|
-
async function checkEnvironment(host2) {
|
|
24438
|
-
const errors = [];
|
|
24439
|
-
const l = npmLaunch(host2);
|
|
24440
|
-
const [nv, npmv, npmRoot] = await Promise.all([
|
|
24441
|
-
ex2.runOutAsync("node", ["--version"]),
|
|
24442
|
-
ex2.runOutAsync(l.program, l.args.concat(["--version"])),
|
|
24443
|
-
resolveNpmRoot(host2)
|
|
24444
|
-
]);
|
|
24445
|
-
if (!nv || !nv.trim()) errors.push("node \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
|
|
24446
|
-
if (!npmv || !npmv.trim()) errors.push("npm \u672A\u5B89\u88C5\u6216\u4E0D\u53EF\u6267\u884C");
|
|
24447
|
-
return { ok: errors.length === 0, errors, npmRoot };
|
|
24448
|
-
}
|
|
24449
|
-
async function latestVersion(host2) {
|
|
24450
|
-
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");
|
|
24451
|
-
const channel = host2.config.releaseChannel || "npm";
|
|
24452
|
-
return host2.dist.fetchVersionInfo(host2.config.packageName, channel);
|
|
24453
|
-
}
|
|
24454
|
-
async function selectRegistry(host2) {
|
|
24455
|
-
if (!host2.dist) return null;
|
|
24456
|
-
try {
|
|
24457
|
-
return await host2.dist.registryOrigin(true);
|
|
24458
|
-
} catch {
|
|
24459
|
-
return null;
|
|
24460
|
-
}
|
|
24461
|
-
}
|
|
24462
|
-
function runNpm(host2, opts) {
|
|
24463
|
-
const a = opts && opts.action || "install";
|
|
24464
|
-
const verb = a === "uninstall" ? "\u5378\u8F7D" : "\u5B89\u88C5";
|
|
24465
|
-
if (!host2.dist) return Promise.resolve({ ok: false, error: "dist \u5206\u53D1\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5" + verb, output: [] });
|
|
24466
|
-
const tpl = a === "install" ? host2.config.installCommandTemplate : null;
|
|
24467
|
-
return host2.dist.runNpmInstall({
|
|
24468
|
-
action: a,
|
|
24469
|
-
pkg: host2.config.packageName || "@deepseek-ai/dsh",
|
|
24470
|
-
version: opts && opts.version,
|
|
24471
|
-
// 前缀单源:装与卸必须落在同一个全局前缀,否则「卸载成功」而包还在原地。
|
|
24472
|
-
prefix: host2.npmRoot || null,
|
|
24473
|
-
registry: opts && opts.registry,
|
|
24474
|
-
// 只在显式注入过 npm 形态时接管启动形态;生产留空,由执行器按运行期契约解析一次。
|
|
24475
|
-
launcher: host2._npmBin ? npmLaunch(host2) : null,
|
|
24476
|
-
commandTemplate: Array.isArray(tpl) && tpl.length ? tpl : null,
|
|
24477
|
-
timeoutMs: a === "uninstall" ? host2.config.uninstallTimeoutMs : host2.config.upgradeTimeoutMs,
|
|
24478
|
-
onLine: (l) => {
|
|
24479
|
-
host2._appendUpgradeLog(l);
|
|
24480
|
-
if (host2.installing) host2._appendInstallLog(l);
|
|
24481
|
-
}
|
|
24482
|
-
});
|
|
24483
|
-
}
|
|
24484
|
-
module2.exports = { npmLaunch, resolveNpmRoot, checkEnvironment, latestVersion, selectRegistry, runNpm };
|
|
24485
|
-
}
|
|
24486
|
-
});
|
|
24487
|
-
|
|
24488
25499
|
// src/app/native/ops.js
|
|
24489
25500
|
var require_ops4 = __commonJS({
|
|
24490
25501
|
"src/app/native/ops.js"(exports2, module2) {
|
|
@@ -25129,7 +26140,7 @@ var require_installer = __commonJS({
|
|
|
25129
26140
|
var require_entry = __commonJS({
|
|
25130
26141
|
"src/app/control/entry.js"(exports2, module2) {
|
|
25131
26142
|
"use strict";
|
|
25132
|
-
var { PHASES } =
|
|
26143
|
+
var { PHASES } = require_registry4();
|
|
25133
26144
|
var ManagedLifecycle = class {
|
|
25134
26145
|
constructor(opts) {
|
|
25135
26146
|
this.id = opts.id || "lc-" + Math.random().toString(36).slice(2, 8);
|
|
@@ -25383,7 +26394,7 @@ var require_domains = __commonJS({
|
|
|
25383
26394
|
var { InstanceManager } = require_instance();
|
|
25384
26395
|
var { PluginMarket } = require_market();
|
|
25385
26396
|
var { PluginManager } = require_plugin();
|
|
25386
|
-
var { ManagedRegistry } =
|
|
26397
|
+
var { ManagedRegistry } = require_registry4();
|
|
25387
26398
|
var { guardVersion } = require_version();
|
|
25388
26399
|
var { Lifecycle } = require_lifecycle2();
|
|
25389
26400
|
var { Health } = require_health();
|
|
@@ -25510,7 +26521,7 @@ var require_adapters = __commonJS({
|
|
|
25510
26521
|
"src/app/control/adapters.js"(exports2, module2) {
|
|
25511
26522
|
"use strict";
|
|
25512
26523
|
var { ManagedLifecycle } = require_entry();
|
|
25513
|
-
var { kindMeta } =
|
|
26524
|
+
var { kindMeta } = require_registry4();
|
|
25514
26525
|
function capsOf(objectKind) {
|
|
25515
26526
|
const m = kindMeta(objectKind);
|
|
25516
26527
|
if (!m) return { startable: true, guardable: true };
|
|
@@ -26009,7 +27020,7 @@ var require_guard = __commonJS({
|
|
|
26009
27020
|
var fs2 = require("node:fs");
|
|
26010
27021
|
var path2 = require("node:path");
|
|
26011
27022
|
function owns(pathname) {
|
|
26012
|
-
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/
|
|
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";
|
|
26013
27024
|
}
|
|
26014
27025
|
function fetchDshChangelog(res, sup) {
|
|
26015
27026
|
const v = sup && sup.nativeManager ? sup.nativeManager.versionInfo() : {};
|
|
@@ -26021,7 +27032,7 @@ var require_guard = __commonJS({
|
|
|
26021
27032
|
return res.end(md);
|
|
26022
27033
|
}
|
|
26023
27034
|
function handle(ctx) {
|
|
26024
|
-
const { sup, req, res, pathname, identity, send, collectBody, originAllowed, browser } = ctx;
|
|
27035
|
+
const { sup, req, res, pathname, identity, send, collectBody, originAllowed, browser, environment } = ctx;
|
|
26025
27036
|
if (req.method === "GET" && pathname === "/changelog") {
|
|
26026
27037
|
return fetchDshChangelog(res, sup);
|
|
26027
27038
|
}
|
|
@@ -26127,6 +27138,31 @@ var require_guard = __commonJS({
|
|
|
26127
27138
|
});
|
|
26128
27139
|
return;
|
|
26129
27140
|
}
|
|
27141
|
+
if (req.method === "GET" && pathname === "/settings/external-browser") {
|
|
27142
|
+
return send(200, sup.externalBrowserStatus());
|
|
27143
|
+
}
|
|
27144
|
+
if (req.method === "POST" && pathname === "/settings/external-browser") {
|
|
27145
|
+
if (!originAllowed(req, sup.config.apiPort)) {
|
|
27146
|
+
req.resume();
|
|
27147
|
+
return send(403, {});
|
|
27148
|
+
}
|
|
27149
|
+
collectBody(req, res, 2048, (body) => {
|
|
27150
|
+
let id = null;
|
|
27151
|
+
let given = false;
|
|
27152
|
+
try {
|
|
27153
|
+
const j = body ? JSON.parse(body) : {};
|
|
27154
|
+
if (typeof j.id === "string") {
|
|
27155
|
+
id = j.id;
|
|
27156
|
+
given = true;
|
|
27157
|
+
}
|
|
27158
|
+
} catch {
|
|
27159
|
+
}
|
|
27160
|
+
if (!given) return send(400, { ok: false, error: '\u9700\u8981 {"id":"<\u5019\u9009 id>"}\uFF08\u7A7A\u4E32\u6E05\u9664\u504F\u597D\uFF09' });
|
|
27161
|
+
const r = sup.setExternalBrowser(id);
|
|
27162
|
+
return send(r.ok ? 200 : 500, r);
|
|
27163
|
+
});
|
|
27164
|
+
return;
|
|
27165
|
+
}
|
|
26130
27166
|
if (req.method === "POST" && pathname === "/shutdown") {
|
|
26131
27167
|
if (!originAllowed(req, sup.config.apiPort)) {
|
|
26132
27168
|
req.resume();
|
|
@@ -26185,13 +27221,20 @@ var require_guard = __commonJS({
|
|
|
26185
27221
|
if (req.method === "GET" && pathname === "/env/node-lts") {
|
|
26186
27222
|
return sup.nodeLtsStatus().then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
|
|
26187
27223
|
}
|
|
26188
|
-
if (req.method === "GET" && pathname === "/env/
|
|
27224
|
+
if (req.method === "GET" && pathname === "/env/environment") {
|
|
27225
|
+
if (!originAllowed(req, sup.config.apiPort)) {
|
|
27226
|
+
req.resume();
|
|
27227
|
+
return send(403, { ok: false, error: "cross-origin request rejected" });
|
|
27228
|
+
}
|
|
27229
|
+
const force = /[?&]force=1/.test(String(req.url || ""));
|
|
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") {
|
|
26189
27233
|
if (!originAllowed(req, sup.config.apiPort)) {
|
|
26190
27234
|
req.resume();
|
|
26191
27235
|
return send(403, { ok: false, error: "cross-origin request rejected" });
|
|
26192
27236
|
}
|
|
26193
|
-
|
|
26194
|
-
return Promise.resolve(browser.listBrowsers({ force })).then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: "\u6D4F\u89C8\u5668\u63A2\u6D4B\u5931\u8D25\uFF1A" + (e && e.message || e) }));
|
|
27237
|
+
return send(200, environment.lastSnapshot());
|
|
26195
27238
|
}
|
|
26196
27239
|
if (req.method === "POST" && pathname === "/env/open-url") {
|
|
26197
27240
|
if (!identity.loopback) {
|
|
@@ -27328,6 +28371,7 @@ var require_server = __commonJS({
|
|
|
27328
28371
|
var { identify } = require_identity3();
|
|
27329
28372
|
var { originAllowed, requestHasAccessKey, isShellOrigin } = require_security();
|
|
27330
28373
|
var browserExit = require_browser();
|
|
28374
|
+
var environmentExit = require_environment();
|
|
27331
28375
|
function safeFail(res, err, where) {
|
|
27332
28376
|
try {
|
|
27333
28377
|
const body = JSON.stringify({ ok: false, error: err && err.message || String(err) });
|
|
@@ -27347,6 +28391,7 @@ var require_server = __commonJS({
|
|
|
27347
28391
|
}
|
|
27348
28392
|
function createServer(sup, deps) {
|
|
27349
28393
|
const browser = deps && deps.browser || browserExit;
|
|
28394
|
+
const environment = deps && deps.environment || environmentExit;
|
|
27350
28395
|
return http2.createServer((req, res) => {
|
|
27351
28396
|
const shellOrigin = (() => {
|
|
27352
28397
|
const o = req.headers.origin;
|
|
@@ -27414,7 +28459,7 @@ var require_server = __commonJS({
|
|
|
27414
28459
|
} catch (e) {
|
|
27415
28460
|
return send(400, { error: "bad request encoding" });
|
|
27416
28461
|
}
|
|
27417
|
-
const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf, browser };
|
|
28462
|
+
const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf, browser, environment };
|
|
27418
28463
|
for (const d of API_DOMAINS) {
|
|
27419
28464
|
if (d.owns(pathname)) {
|
|
27420
28465
|
try {
|