@dsh-sup/dsh-core-linux-x64 0.1.6-BETA.10 → 0.1.6-BETA.12
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 +389 -133
- package/package.json +1 -1
- package/ui-react/assets/InstancesPage-DadqCqud.js +3 -0
- package/ui-react/assets/LanPage-DWzQ-VLz.js +4 -0
- package/ui-react/assets/OverviewPage-D9y-GeR2.js +2 -0
- package/ui-react/assets/PluginsPage-BwLIhj7E.js +1 -0
- package/ui-react/assets/RouterPage-BeMIxdSL.js +1 -0
- package/ui-react/assets/SettingsPage-R7lkiQwq.js +1 -0
- package/ui-react/assets/{TasksPage-BsXiGMar.js → TasksPage-A3SFro8F.js} +1 -1
- package/ui-react/assets/openExternal-DOkxrLUq.js +1 -0
- package/ui-react/assets/{refresh-cw-uOv8eoMN.js → refresh-cw-B-VAKpDt.js} +1 -1
- package/ui-react/assets/{rocket-BYxAkKOI.js → rocket-CLAmwatt.js} +1 -1
- package/ui-react/assets/{shield-check-DskEWpz7.js → shield-check-CMRwmPpk.js} +1 -1
- package/ui-react/assets/supervisor-BbSavtNN.js +41 -0
- package/ui-react/assets/supervisor-Btn_MIEc.js +11 -0
- package/ui-react/assets/supervisor-YWIsBeZU.css +2 -0
- package/ui-react/assets/useSupervisorAction-q0YJmhui.js +1 -0
- package/ui-react/assets/widgets-BbRkZm-Z.js +1 -0
- package/ui-react/supervisor.html +3 -3
- package/ui-react/assets/InstancesPage-Bv6gx6Pj.js +0 -3
- package/ui-react/assets/LanPage-BLdWpYNX.js +0 -4
- package/ui-react/assets/OverviewPage-GLglcmt5.js +0 -2
- package/ui-react/assets/PluginsPage-D0w7WSyx.js +0 -5
- package/ui-react/assets/RouterPage-CXHKQf2P.js +0 -1
- package/ui-react/assets/SettingsPage-OuiVCipQ.js +0 -5
- package/ui-react/assets/external-link-CnOCgHhe.js +0 -1
- package/ui-react/assets/supervisor-BnUm3AVQ.js +0 -41
- package/ui-react/assets/supervisor-DJuPMCY8.css +0 -2
- package/ui-react/assets/supervisor-xQMSEUga.js +0 -11
- package/ui-react/assets/useSupervisorAction-CSk4q5cT.js +0 -1
- package/ui-react/assets/widgets-CcAMVR4H.js +0 -1
package/core.cjs
CHANGED
|
@@ -947,7 +947,7 @@ var require_version = __commonJS({
|
|
|
947
947
|
var fs2 = require("node:fs");
|
|
948
948
|
var path2 = require("node:path");
|
|
949
949
|
function guardVersion() {
|
|
950
|
-
if (true) return String("0.1.6-BETA.
|
|
950
|
+
if (true) return String("0.1.6-BETA.12");
|
|
951
951
|
try {
|
|
952
952
|
return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
|
|
953
953
|
} catch {
|
|
@@ -4634,6 +4634,58 @@ var require_watchdog = __commonJS({
|
|
|
4634
4634
|
}
|
|
4635
4635
|
});
|
|
4636
4636
|
|
|
4637
|
+
// src/platform/os/desktop.js
|
|
4638
|
+
var require_desktop = __commonJS({
|
|
4639
|
+
"src/platform/os/desktop.js"(exports2, module2) {
|
|
4640
|
+
"use strict";
|
|
4641
|
+
var fs2 = require("node:fs");
|
|
4642
|
+
var PLATFORM = process.platform;
|
|
4643
|
+
function hasX11Socket() {
|
|
4644
|
+
try {
|
|
4645
|
+
return fs2.readdirSync("/tmp/.X11-unix").some((f) => /^X\d+$/.test(f));
|
|
4646
|
+
} catch {
|
|
4647
|
+
return false;
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
function hasWaylandSocket() {
|
|
4651
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
4652
|
+
const rt = process.env.XDG_RUNTIME_DIR || "/run/user/" + uid;
|
|
4653
|
+
try {
|
|
4654
|
+
return fs2.readdirSync(rt).some((f) => /^wayland-\d+$/.test(f));
|
|
4655
|
+
} catch {
|
|
4656
|
+
return false;
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
function sessionAvailable() {
|
|
4660
|
+
if (PLATFORM === "linux") {
|
|
4661
|
+
if (process.env.DISPLAY || process.env.WAYLAND_DISPLAY) return true;
|
|
4662
|
+
return hasX11Socket() || hasWaylandSocket();
|
|
4663
|
+
}
|
|
4664
|
+
return PLATFORM === "darwin" || PLATFORM === "win32";
|
|
4665
|
+
}
|
|
4666
|
+
function describe() {
|
|
4667
|
+
if (PLATFORM === "linux") {
|
|
4668
|
+
const byEnv = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
4669
|
+
const byX = hasX11Socket();
|
|
4670
|
+
const byWl = hasWaylandSocket();
|
|
4671
|
+
return {
|
|
4672
|
+
platform: PLATFORM,
|
|
4673
|
+
available: byEnv || byX || byWl,
|
|
4674
|
+
reason: byEnv ? "env(DISPLAY/WAYLAND_DISPLAY)" : byX ? "x11-socket" : byWl ? "wayland-socket" : "none",
|
|
4675
|
+
display: process.env.DISPLAY || null,
|
|
4676
|
+
waylandDisplay: process.env.WAYLAND_DISPLAY || null
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
return {
|
|
4680
|
+
platform: PLATFORM,
|
|
4681
|
+
available: sessionAvailable(),
|
|
4682
|
+
reason: "session-scoped-by-launcher"
|
|
4683
|
+
};
|
|
4684
|
+
}
|
|
4685
|
+
module2.exports = { sessionAvailable, describe, PLATFORM };
|
|
4686
|
+
}
|
|
4687
|
+
});
|
|
4688
|
+
|
|
4637
4689
|
// src/platform/os/capability-profile.js
|
|
4638
4690
|
var require_capability_profile = __commonJS({
|
|
4639
4691
|
"src/platform/os/capability-profile.js"(exports2, module2) {
|
|
@@ -4651,6 +4703,8 @@ var require_capability_profile = __commonJS({
|
|
|
4651
4703
|
autostart: true,
|
|
4652
4704
|
// 期望 systemctl(实测覆写)
|
|
4653
4705
|
frpExpose: true,
|
|
4706
|
+
openBrowser: true,
|
|
4707
|
+
// xdg-open / 解析到的默认浏览器;无图形会话时实测覆写成 false
|
|
4654
4708
|
hostService: "systemd",
|
|
4655
4709
|
guardAutostart: true,
|
|
4656
4710
|
// systemd --user enable + linger
|
|
@@ -4674,6 +4728,8 @@ var require_capability_profile = __commonJS({
|
|
|
4674
4728
|
autostart: true,
|
|
4675
4729
|
// launchctl/LaunchAgent 恒在
|
|
4676
4730
|
frpExpose: true,
|
|
4731
|
+
openBrowser: true,
|
|
4732
|
+
// LaunchServices 解析 + open 直启
|
|
4677
4733
|
hostService: "launchd",
|
|
4678
4734
|
guardAutostart: true,
|
|
4679
4735
|
// LaunchAgent RunAtLoad + KeepAlive
|
|
@@ -4699,6 +4755,9 @@ var require_capability_profile = __commonJS({
|
|
|
4699
4755
|
autostart: true,
|
|
4700
4756
|
// 期望 schtasks(实测覆写)
|
|
4701
4757
|
frpExpose: true,
|
|
4758
|
+
// 可打开,但 win32 的两种启动形态(explorer.exe 调度器 / 裸 URL 直启解析到的浏览器)退出码都不携带
|
|
4759
|
+
// 窗口是否出现的信息,故每次调用最多到 handedOff(见 browser.js#ownsItsWindow)。能力位只声明「能交出去」。
|
|
4760
|
+
openBrowser: true,
|
|
4702
4761
|
hostService: "windows-service",
|
|
4703
4762
|
guardAutostart: true,
|
|
4704
4763
|
// schtasks DSH-Supervisor(ONLOGON,由壳建立)
|
|
@@ -4717,6 +4776,7 @@ var require_capability_profile = __commonJS({
|
|
|
4717
4776
|
desktopNotify: false,
|
|
4718
4777
|
autostart: false,
|
|
4719
4778
|
frpExpose: false,
|
|
4779
|
+
openBrowser: false,
|
|
4720
4780
|
hostService: "none",
|
|
4721
4781
|
guardAutostart: false,
|
|
4722
4782
|
guardSelfHeal: false,
|
|
@@ -5400,6 +5460,8 @@ var require_browser = __commonJS({
|
|
|
5400
5460
|
var path2 = require("node:path");
|
|
5401
5461
|
var os2 = require("node:os");
|
|
5402
5462
|
var spawnOS = require_spawn();
|
|
5463
|
+
var desktop = require_desktop();
|
|
5464
|
+
var CAPABILITY_PROFILES = require_capability_profile();
|
|
5403
5465
|
var { resolveExecutable, isExecutableFile } = require_exec_path();
|
|
5404
5466
|
var exec = require_exec();
|
|
5405
5467
|
function isSafeHttpUrl(url) {
|
|
@@ -5416,17 +5478,126 @@ var require_browser = __commonJS({
|
|
|
5416
5478
|
if (pl === "win32") return { cmd: "explorer.exe", args: [url] };
|
|
5417
5479
|
return { cmd: "xdg-open", args: [url] };
|
|
5418
5480
|
}
|
|
5419
|
-
|
|
5420
|
-
|
|
5481
|
+
var SUPPORTED_OPEN_PLATFORMS = Object.keys(CAPABILITY_PROFILES).filter((k) => CAPABILITY_PROFILES[k].openBrowser === true);
|
|
5482
|
+
function outcome(p) {
|
|
5483
|
+
const ok = !!p.ok;
|
|
5484
|
+
const confirmed = ok && p.confirmed === true;
|
|
5485
|
+
const handedOff = ok && !confirmed;
|
|
5486
|
+
const reason = ok ? null : p.reason || "failed";
|
|
5487
|
+
return {
|
|
5488
|
+
ok,
|
|
5489
|
+
confirmed,
|
|
5490
|
+
handedOff,
|
|
5491
|
+
reason,
|
|
5492
|
+
error: ok ? null : p.error || FAILURE_TEXT[reason] || "\u6253\u5F00\u5931\u8D25\uFF08" + reason + "\uFF09",
|
|
5493
|
+
message: ok ? confirmed ? CONFIRMED_TEXT : p.message || HANDEDOFF_TEXT : null,
|
|
5494
|
+
url: p.url === void 0 ? null : p.url,
|
|
5495
|
+
evidence: p.evidence || null
|
|
5496
|
+
};
|
|
5497
|
+
}
|
|
5498
|
+
var CONFIRMED_TEXT = "\u5DF2\u5728\u7CFB\u7EDF\u6D4F\u89C8\u5668\u6253\u5F00";
|
|
5499
|
+
var HANDEDOFF_TEXT = "\u5DF2\u628A\u5730\u5740\u4EA4\u7ED9\u7CFB\u7EDF\uFF0C\u4F46\u8FD9\u6B21\u542F\u52A8\u62FF\u4E0D\u5230\u7A97\u53E3\u662F\u5426\u51FA\u73B0\u7684\u8BC1\u636E";
|
|
5500
|
+
var FAILURE_TEXT = {
|
|
5501
|
+
"unsafe-url": "\u5730\u5740\u4E0D\u662F http(s) \u7EDD\u5BF9 URL\uFF0C\u5DF2\u62D2\u7EDD\u4EA4\u7ED9\u6D4F\u89C8\u5668",
|
|
5502
|
+
"no-launcher": "\u672A\u627E\u5230\u53EF\u7528\u7684\u6D4F\u89C8\u5668\u542F\u52A8\u547D\u4EE4\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u8BE5\u5730\u5740",
|
|
5503
|
+
"spawn-failed": "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF08\u7CFB\u7EDF\u62D2\u7EDD\u4E86\u8BE5\u547D\u4EE4\uFF09",
|
|
5504
|
+
"exit-nonzero": "\u7CFB\u7EDF\u62D2\u7EDD\u4E86\u8FD9\u4E2A\u5730\u5740\uFF08\u542F\u52A8\u547D\u4EE4\u975E 0 \u9000\u51FA\uFF09\uFF0C\u7A97\u53E3\u672A\u51FA\u73B0",
|
|
5505
|
+
"killed-by-signal": "\u6D4F\u89C8\u5668\u542F\u52A8\u547D\u4EE4\u88AB\u7CFB\u7EDF\u7EC8\u6B62\uFF0C\u7A97\u53E3\u672A\u51FA\u73B0",
|
|
5506
|
+
"no-desktop-session": "\u5F53\u524D\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u8C03\u8D77\u6D4F\u89C8\u5668",
|
|
5507
|
+
"unsupported-platform": "\u5F53\u524D\u5E73\u53F0\u4E0D\u5728\u4EA7\u54C1\u652F\u6301\u7684\u684C\u9762\u5E73\u53F0\u5185\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u8BE5\u5730\u5740"
|
|
5508
|
+
};
|
|
5509
|
+
function ownsItsWindow(via, platform) {
|
|
5510
|
+
if (via !== "dispatcher") return false;
|
|
5511
|
+
return platform !== "win32";
|
|
5512
|
+
}
|
|
5513
|
+
function openPlan(platform, url, opts) {
|
|
5514
|
+
const o = opts || {};
|
|
5515
|
+
const pl = platform || process.platform;
|
|
5516
|
+
const db = o.defaultBrowser;
|
|
5517
|
+
const engine = db && db.bin ? engineOf(db.bin) : "other";
|
|
5518
|
+
if (engine === "chromium" || engine === "firefox") {
|
|
5519
|
+
return { bin: db.bin, engine, via: "browser", baseArgs: (db.baseArgs || []).concat([url]), exitIsEvidence: ownsItsWindow("browser", pl) };
|
|
5520
|
+
}
|
|
5521
|
+
const c = openCommand(pl, url);
|
|
5522
|
+
return { bin: c.cmd, engine: "other", via: "dispatcher", baseArgs: c.args, exitIsEvidence: ownsItsWindow("dispatcher", pl) };
|
|
5523
|
+
}
|
|
5524
|
+
function observeSpawn(child, windowMs, setTimeoutFn) {
|
|
5525
|
+
return new Promise((resolve) => {
|
|
5526
|
+
let settled = false;
|
|
5527
|
+
let timer;
|
|
5528
|
+
const done = (r) => {
|
|
5529
|
+
if (settled) return;
|
|
5530
|
+
settled = true;
|
|
5531
|
+
clearTimeout(timer);
|
|
5532
|
+
resolve(r);
|
|
5533
|
+
};
|
|
5534
|
+
timer = (setTimeoutFn || setTimeout)(() => done({ stage: "alive" }), windowMs);
|
|
5535
|
+
if (timer && timer.unref) timer.unref();
|
|
5536
|
+
child.on("error", (e) => done({ stage: "error", code: e && e.code || String(e) }));
|
|
5537
|
+
child.on("exit", (code, signal) => done({ stage: "exit", code, signal }));
|
|
5538
|
+
});
|
|
5539
|
+
}
|
|
5540
|
+
var OPEN_OBSERVE_MS = 1500;
|
|
5541
|
+
async function openBrowser(url, o) {
|
|
5542
|
+
const opts = o || {};
|
|
5543
|
+
const pl = opts.platform || process.platform;
|
|
5544
|
+
if (!isSafeHttpUrl(url)) {
|
|
5545
|
+
return outcome({ ok: false, reason: "unsafe-url", url: String(url || ""), evidence: null });
|
|
5546
|
+
}
|
|
5547
|
+
if (!SUPPORTED_OPEN_PLATFORMS.includes(pl)) {
|
|
5548
|
+
return outcome({ ok: false, reason: "unsupported-platform", url, evidence: { platform: pl } });
|
|
5549
|
+
}
|
|
5550
|
+
const observeMs = opts.observeMs === void 0 ? OPEN_OBSERVE_MS : opts.observeMs;
|
|
5551
|
+
const spawnWith = typeof opts.spawn === "function" ? opts.spawn : _spawnDetachedIgnored;
|
|
5552
|
+
const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
|
|
5553
|
+
const observe = typeof opts.observe === "function" ? opts.observe : observeSpawn;
|
|
5554
|
+
let db;
|
|
5555
|
+
if ("defaultBrowser" in opts) db = opts.defaultBrowser;
|
|
5556
|
+
else if (typeof opts.resolveDefault === "function") db = opts.resolveDefault(pl, opts.resolveDeps || {});
|
|
5557
|
+
else db = resolveDefaultBrowser(pl, opts.resolveDeps || {});
|
|
5558
|
+
const plan = openPlan(pl, url, { defaultBrowser: db });
|
|
5559
|
+
const evidence = { bin: plan.bin, engine: plan.engine, via: plan.via, ownsWindow: plan.exitIsEvidence === true, exitCode: null, exitSignal: null, error: null };
|
|
5560
|
+
const desktopAvailable = typeof opts.desktopAvailable === "function" ? opts.desktopAvailable : desktop.sessionAvailable;
|
|
5561
|
+
if (pl === "linux" && !desktopAvailable()) {
|
|
5562
|
+
return outcome({ ok: false, reason: "no-desktop-session", url, evidence });
|
|
5563
|
+
}
|
|
5564
|
+
if (!avail(plan.bin)) {
|
|
5565
|
+
return outcome({ ok: false, reason: "no-launcher", url, evidence });
|
|
5566
|
+
}
|
|
5567
|
+
let child;
|
|
5568
|
+
try {
|
|
5569
|
+
child = spawnWith(plan.bin, plan.baseArgs);
|
|
5570
|
+
} catch (e) {
|
|
5571
|
+
evidence.error = e && e.code || String(e);
|
|
5572
|
+
return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
5573
|
+
}
|
|
5574
|
+
if (!child) return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
5575
|
+
const seen = await observe(child, observeMs, opts.setTimeout);
|
|
5576
|
+
evidence.error = seen.stage === "error" ? String(seen.code) : null;
|
|
5577
|
+
evidence.exitCode = seen.code === void 0 ? null : seen.code;
|
|
5578
|
+
evidence.exitSignal = seen.signal === void 0 ? null : seen.signal;
|
|
5579
|
+
if (seen.stage === "error") return outcome({ ok: false, reason: "spawn-failed", url, evidence });
|
|
5580
|
+
const exitDecides = seen.stage === "exit" && plan.exitIsEvidence === true;
|
|
5581
|
+
if (exitDecides && (seen.code !== 0 || seen.signal)) {
|
|
5582
|
+
return outcome({
|
|
5583
|
+
ok: false,
|
|
5584
|
+
reason: seen.signal ? "killed-by-signal" : "exit-nonzero",
|
|
5585
|
+
url,
|
|
5586
|
+
evidence,
|
|
5587
|
+
error: FAILURE_TEXT[seen.signal ? "killed-by-signal" : "exit-nonzero"] + "\uFF08" + (seen.signal || seen.code) + "\uFF09"
|
|
5588
|
+
});
|
|
5589
|
+
}
|
|
5590
|
+
return outcome({ ok: true, confirmed: exitDecides, handedOff: !exitDecides, url, evidence });
|
|
5591
|
+
}
|
|
5592
|
+
function _spawnDetachedIgnored(bin, args) {
|
|
5421
5593
|
try {
|
|
5422
|
-
const
|
|
5423
|
-
const p = spawnOS.detachedIgnored(c.cmd, c.args);
|
|
5594
|
+
const p = spawnOS.detachedIgnored(bin, args);
|
|
5424
5595
|
p.on("error", () => {
|
|
5425
5596
|
});
|
|
5426
5597
|
p.unref();
|
|
5427
|
-
return
|
|
5598
|
+
return p;
|
|
5428
5599
|
} catch {
|
|
5429
|
-
return
|
|
5600
|
+
return null;
|
|
5430
5601
|
}
|
|
5431
5602
|
}
|
|
5432
5603
|
function _spawnDetached(bin, args, env, onExit) {
|
|
@@ -5546,8 +5717,8 @@ var require_browser = __commonJS({
|
|
|
5546
5717
|
}
|
|
5547
5718
|
function resolveDefaultLinux(runOut, readFile, exists, env, home) {
|
|
5548
5719
|
const id = runOut("xdg-settings", ["get", "default-web-browser"]);
|
|
5549
|
-
const
|
|
5550
|
-
if (!
|
|
5720
|
+
const desktop2 = id && id.trim();
|
|
5721
|
+
if (!desktop2 || !/^[A-Za-z0-9._-]+\.desktop$/.test(desktop2)) return null;
|
|
5551
5722
|
const dirs = [
|
|
5552
5723
|
env.XDG_DATA_HOME || path2.join(home, ".local", "share"),
|
|
5553
5724
|
"/usr/local/share",
|
|
@@ -5555,7 +5726,7 @@ var require_browser = __commonJS({
|
|
|
5555
5726
|
"/var/lib/snapd/desktop"
|
|
5556
5727
|
].map((d) => path2.join(d, "applications"));
|
|
5557
5728
|
for (const dir of dirs) {
|
|
5558
|
-
const p = path2.join(dir,
|
|
5729
|
+
const p = path2.join(dir, desktop2);
|
|
5559
5730
|
let text = null;
|
|
5560
5731
|
try {
|
|
5561
5732
|
if (exists(p)) text = readFile(p);
|
|
@@ -5627,12 +5798,13 @@ var require_browser = __commonJS({
|
|
|
5627
5798
|
}
|
|
5628
5799
|
function launchIsolated(url, o) {
|
|
5629
5800
|
const opts = o || {};
|
|
5630
|
-
if (!isSafeHttpUrl(url)) return { ok: false, bin: null, isolated: false };
|
|
5801
|
+
if (!isSafeHttpUrl(url)) return Object.assign(outcome({ ok: false, reason: "unsafe-url", url: String(url || "") }), { bin: null, isolated: false });
|
|
5631
5802
|
const antiEnv = opts.antiEnv || opts.sysEnv || process.env;
|
|
5632
5803
|
const sysEnv = opts.sysEnv || process.env;
|
|
5633
5804
|
const onExit = opts.onExit;
|
|
5634
5805
|
const avail = typeof opts.binAvailable === "function" ? opts.binAvailable : binAvailable;
|
|
5635
5806
|
const spawnWith = typeof opts.spawn === "function" ? opts.spawn : _spawnDetached;
|
|
5807
|
+
const fail = (reason, extra) => Object.assign(outcome(Object.assign({ ok: false, reason, url }, extra)), { bin: null, isolated: false });
|
|
5636
5808
|
try {
|
|
5637
5809
|
let db;
|
|
5638
5810
|
if ("defaultBrowser" in opts) db = opts.defaultBrowser;
|
|
@@ -5644,19 +5816,23 @@ var require_browser = __commonJS({
|
|
|
5644
5816
|
size: opts.size,
|
|
5645
5817
|
lang: opts.lang
|
|
5646
5818
|
});
|
|
5647
|
-
if (!avail(plan.bin)) return {
|
|
5819
|
+
if (!avail(plan.bin)) return fail("no-launcher", { evidence: { bin: plan.bin, engine: plan.label, via: plan.isolated ? "browser" : "dispatcher" } });
|
|
5648
5820
|
const env = plan.envKind === "anti" ? antiEnv : sysEnv;
|
|
5649
5821
|
const p = spawnWith(plan.bin, plan.args, env, plan.watch ? onExit : void 0);
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5822
|
+
if (!p) return fail("spawn-failed");
|
|
5823
|
+
return Object.assign(outcome({ ok: true, confirmed: false, handedOff: true, url }), { bin: plan.label, isolated: plan.isolated });
|
|
5824
|
+
} catch (e) {
|
|
5825
|
+
return fail("spawn-failed", { error: FAILURE_TEXT["spawn-failed"] + "\uFF08" + (e && e.code || e) + "\uFF09" });
|
|
5653
5826
|
}
|
|
5654
5827
|
}
|
|
5655
5828
|
module2.exports = {
|
|
5656
|
-
|
|
5829
|
+
openBrowser,
|
|
5657
5830
|
launchIsolated,
|
|
5658
5831
|
openCommand,
|
|
5832
|
+
openPlan,
|
|
5833
|
+
ownsItsWindow,
|
|
5659
5834
|
isolatedPlan,
|
|
5835
|
+
observeSpawn,
|
|
5660
5836
|
isSafeHttpUrl,
|
|
5661
5837
|
resolveDefaultBrowser,
|
|
5662
5838
|
resolveDefaultWin,
|
|
@@ -5672,58 +5848,6 @@ var require_browser = __commonJS({
|
|
|
5672
5848
|
}
|
|
5673
5849
|
});
|
|
5674
5850
|
|
|
5675
|
-
// src/platform/os/desktop.js
|
|
5676
|
-
var require_desktop = __commonJS({
|
|
5677
|
-
"src/platform/os/desktop.js"(exports2, module2) {
|
|
5678
|
-
"use strict";
|
|
5679
|
-
var fs2 = require("node:fs");
|
|
5680
|
-
var PLATFORM = process.platform;
|
|
5681
|
-
function hasX11Socket() {
|
|
5682
|
-
try {
|
|
5683
|
-
return fs2.readdirSync("/tmp/.X11-unix").some((f) => /^X\d+$/.test(f));
|
|
5684
|
-
} catch {
|
|
5685
|
-
return false;
|
|
5686
|
-
}
|
|
5687
|
-
}
|
|
5688
|
-
function hasWaylandSocket() {
|
|
5689
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
5690
|
-
const rt = process.env.XDG_RUNTIME_DIR || "/run/user/" + uid;
|
|
5691
|
-
try {
|
|
5692
|
-
return fs2.readdirSync(rt).some((f) => /^wayland-\d+$/.test(f));
|
|
5693
|
-
} catch {
|
|
5694
|
-
return false;
|
|
5695
|
-
}
|
|
5696
|
-
}
|
|
5697
|
-
function sessionAvailable() {
|
|
5698
|
-
if (PLATFORM === "linux") {
|
|
5699
|
-
if (process.env.DISPLAY || process.env.WAYLAND_DISPLAY) return true;
|
|
5700
|
-
return hasX11Socket() || hasWaylandSocket();
|
|
5701
|
-
}
|
|
5702
|
-
return PLATFORM === "darwin" || PLATFORM === "win32";
|
|
5703
|
-
}
|
|
5704
|
-
function describe() {
|
|
5705
|
-
if (PLATFORM === "linux") {
|
|
5706
|
-
const byEnv = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
5707
|
-
const byX = hasX11Socket();
|
|
5708
|
-
const byWl = hasWaylandSocket();
|
|
5709
|
-
return {
|
|
5710
|
-
platform: PLATFORM,
|
|
5711
|
-
available: byEnv || byX || byWl,
|
|
5712
|
-
reason: byEnv ? "env(DISPLAY/WAYLAND_DISPLAY)" : byX ? "x11-socket" : byWl ? "wayland-socket" : "none",
|
|
5713
|
-
display: process.env.DISPLAY || null,
|
|
5714
|
-
waylandDisplay: process.env.WAYLAND_DISPLAY || null
|
|
5715
|
-
};
|
|
5716
|
-
}
|
|
5717
|
-
return {
|
|
5718
|
-
platform: PLATFORM,
|
|
5719
|
-
available: sessionAvailable(),
|
|
5720
|
-
reason: "session-scoped-by-launcher"
|
|
5721
|
-
};
|
|
5722
|
-
}
|
|
5723
|
-
module2.exports = { sessionAvailable, describe, PLATFORM };
|
|
5724
|
-
}
|
|
5725
|
-
});
|
|
5726
|
-
|
|
5727
5851
|
// src/platform/os/autostart/win32.js
|
|
5728
5852
|
var require_win32 = __commonJS({
|
|
5729
5853
|
"src/platform/os/autostart/win32.js"(exports2, module2) {
|
|
@@ -6070,6 +6194,7 @@ var require_os = __commonJS({
|
|
|
6070
6194
|
var path2 = require("node:path");
|
|
6071
6195
|
var ex2 = require_exec();
|
|
6072
6196
|
var execPath = require_exec_path();
|
|
6197
|
+
var desktop = require_desktop();
|
|
6073
6198
|
var CAPABILITY_PROFILES = require_capability_profile();
|
|
6074
6199
|
var PLATFORM = process.platform;
|
|
6075
6200
|
var ARCH = process.arch;
|
|
@@ -6115,6 +6240,7 @@ var require_os = __commonJS({
|
|
|
6115
6240
|
p.sandboxEnforcement = hasTool("systemd-run") ? "cgroup" : "supervise";
|
|
6116
6241
|
p.desktopNotify = hasTool("notify-send");
|
|
6117
6242
|
p.autostart = hasTool("systemctl");
|
|
6243
|
+
p.openBrowser = desktop.sessionAvailable();
|
|
6118
6244
|
} else if (pl === "darwin") {
|
|
6119
6245
|
p.desktopNotify = hasTool("osascript");
|
|
6120
6246
|
} else if (pl === "win32") {
|
|
@@ -6147,7 +6273,7 @@ var require_os = __commonJS({
|
|
|
6147
6273
|
// 导出模块对象会抛 platform.notify is not a function,把升级终态误判成失败。
|
|
6148
6274
|
notify: require_notify().notify,
|
|
6149
6275
|
browser: require_browser(),
|
|
6150
|
-
desktop
|
|
6276
|
+
desktop,
|
|
6151
6277
|
// 图形会话可用性(Linux 需实测 socket)
|
|
6152
6278
|
autostart: require_autostart()
|
|
6153
6279
|
};
|
|
@@ -10916,6 +11042,7 @@ var require_lan = __commonJS({
|
|
|
10916
11042
|
module2.exports = { methods: {
|
|
10917
11043
|
// 远程控制委托:全部转发给 LanManager;daemon 监督模式经 43108 ctl 委托(异步),本地模式走 LanManager(同步)。
|
|
10918
11044
|
// 令牌收敛:listLan 输出必须剔除 token/dshToken —— /lan-access 允许 LAN/私网 Host 访问,直出会把 DSH 会话令牌泄漏给局域网,权威仍在 DshTokenService。
|
|
11045
|
+
// 远程访问令牌的明文也不在此下发:它是实例意图字段,只随 /instances 的回环分支交给本机面板(api/domains/instances.js#decorate)。
|
|
10919
11046
|
// remote(projectRemoteView 产物)为后端单一推导视图 {mode,ready,accessUrl,reasons},不含机密,整段放行给 UI。返回形如 {items,addresses}。
|
|
10920
11047
|
listLan() {
|
|
10921
11048
|
const d = depsOf(this);
|
|
@@ -10928,7 +11055,7 @@ var require_lan = __commonJS({
|
|
|
10928
11055
|
dshPort: it.dshPort,
|
|
10929
11056
|
wanPort: it.wanPort,
|
|
10930
11057
|
running: !!it.running,
|
|
10931
|
-
// 令牌状态(布尔,不泄明文):
|
|
11058
|
+
// 令牌状态(布尔,不泄明文):UI 据此引导,明文的查看/修改走 /instances 的回环分支。
|
|
10932
11059
|
tokenSet: !!String(it.token || "").trim(),
|
|
10933
11060
|
remote: it.remote || null
|
|
10934
11061
|
};
|
|
@@ -11097,7 +11224,7 @@ var require_main = __commonJS({
|
|
|
11097
11224
|
guardian: m.guardian,
|
|
11098
11225
|
remoteMode: m.remoteMode,
|
|
11099
11226
|
remoteToken: m.remoteToken,
|
|
11100
|
-
// 进程内消费(LanManager mainOf);API
|
|
11227
|
+
// 进程内消费(LanManager mainOf);API 边界按回环判据决定去留(api/domains/instances.js#decorate)
|
|
11101
11228
|
unitName: null,
|
|
11102
11229
|
// systemd 托管已废弃:main 由守卫 spawn/观测
|
|
11103
11230
|
// 实时运行态:native 条目缺 state 会导致远程控制页误判「实例已停止」
|
|
@@ -11463,6 +11590,9 @@ if (typeof crypto.randomUUID !== 'function') {
|
|
|
11463
11590
|
}
|
|
11464
11591
|
return { ok: true };
|
|
11465
11592
|
}
|
|
11593
|
+
function generateRemoteToken() {
|
|
11594
|
+
return crypto.randomBytes(12).toString("base64url");
|
|
11595
|
+
}
|
|
11466
11596
|
function normalizeRemoteMode(v) {
|
|
11467
11597
|
return v === "lan" || v === "wan" ? v : "off";
|
|
11468
11598
|
}
|
|
@@ -11472,9 +11602,9 @@ if (typeof crypto.randomUUID !== 'function') {
|
|
|
11472
11602
|
if (mode === "off") return { mode, ready: false, accessUrl: null, reasons: [] };
|
|
11473
11603
|
const reasons = [];
|
|
11474
11604
|
if (!x.relayListening) reasons.push("\u8FDC\u7A0B\u670D\u52A1\u672A\u5C31\u7EEA\uFF08relay \u672A\u76D1\u542C\uFF09");
|
|
11475
|
-
if (!x.tokenSet) reasons.push("\u672A\u8BBE\u8BBF\u95EE\u4EE4\u724C");
|
|
11476
11605
|
else if (!x.cookieReady) reasons.push("\u6B63\u5728\u6CE8\u5165 DSH \u4F1A\u8BDD\u2026");
|
|
11477
11606
|
if (mode === "wan") {
|
|
11607
|
+
if (!x.tokenSet) reasons.push("\u672A\u8BBE\u8BBF\u95EE\u4EE4\u724C");
|
|
11478
11608
|
if (!String(x.serverAddr || "").trim()) reasons.push("\u672A\u914D\u7F6E frps \u670D\u52A1\u5668\u5730\u5740");
|
|
11479
11609
|
if (!x.frpcRunning) reasons.push("\u516C\u7F51\u96A7\u9053\u672A\u5EFA\u7ACB\uFF08frpc \u672A\u8FD0\u884C\uFF09");
|
|
11480
11610
|
}
|
|
@@ -11498,6 +11628,7 @@ if (typeof crypto.randomUUID !== 'function') {
|
|
|
11498
11628
|
buildFrpcToml,
|
|
11499
11629
|
normalizeFrpSettings,
|
|
11500
11630
|
normalizeRemoteMode,
|
|
11631
|
+
generateRemoteToken,
|
|
11501
11632
|
validateFrpServerSettings,
|
|
11502
11633
|
validateWanAccess,
|
|
11503
11634
|
projectRemoteView
|
|
@@ -11509,7 +11640,7 @@ if (typeof crypto.randomUUID !== 'function') {
|
|
|
11509
11640
|
var require_lan2 = __commonJS({
|
|
11510
11641
|
"src/app/domain-actions/lan.js"(exports2, module2) {
|
|
11511
11642
|
"use strict";
|
|
11512
|
-
var { validateWanAccess } = require_core4();
|
|
11643
|
+
var { validateWanAccess, generateRemoteToken } = require_core4();
|
|
11513
11644
|
var { remoteTokenStrength } = require_credential();
|
|
11514
11645
|
function lanModule(deps) {
|
|
11515
11646
|
const lm = typeof deps.getLifecycleManager === "function" ? deps.getLifecycleManager() : null;
|
|
@@ -11563,26 +11694,49 @@ var require_lan2 = __commonJS({
|
|
|
11563
11694
|
}
|
|
11564
11695
|
return {
|
|
11565
11696
|
/** 远程控制模式唯一写入口(off|lan|wan)。mode 必须显式给出——缺省归 'off' 会让
|
|
11566
|
-
* 漏字段的请求静默关闭远程控制。
|
|
11697
|
+
* 漏字段的请求静默关闭远程控制。
|
|
11698
|
+
* 开启(lan|wan)时若该实例压根没有访问令牌,就在这里分配一个并随模式一次落盘:
|
|
11699
|
+
* 「开启远程控制」是用户唯一的开远程动作,把「先去别处设令牌」留在流程里,产出的是开关已开、
|
|
11700
|
+
* 屏幕无二维码、用户也不知凭据为何的半截状态。已有令牌(含过弱的)一律不覆盖——静默改写
|
|
11701
|
+
* 用户自设凭据是另一类事故,弱令牌交给 wan 闸显式拒绝并把用户引到令牌框。
|
|
11702
|
+
* wan 前置闸按**补齐后**的令牌复判:分配在复判之前完成、落盘在复判之后发生,被拒仍零写入。 */
|
|
11567
11703
|
setRemoteMode(id, mode) {
|
|
11568
11704
|
if (mode !== "off" && mode !== "lan" && mode !== "wan") {
|
|
11569
11705
|
return { ok: false, error: "mode \u5FC5\u987B\u663E\u5F0F\u7ED9\u51FA\uFF08off|lan|wan\uFF09" };
|
|
11570
11706
|
}
|
|
11571
11707
|
const target = resolveTarget(id);
|
|
11572
11708
|
if (!target) return { ok: false, error: "\u5B9E\u4F8B\u4E0D\u5B58\u5728" };
|
|
11709
|
+
const allocate = mode !== "off" && !String(target.remoteToken || "").trim();
|
|
11710
|
+
const nextToken = allocate ? generateRemoteToken() : target.remoteToken;
|
|
11573
11711
|
if (mode === "wan") {
|
|
11574
|
-
const v = validateWanAccess({ remoteToken:
|
|
11712
|
+
const v = validateWanAccess({ remoteToken: nextToken });
|
|
11575
11713
|
if (!v.ok) return { ok: false, error: v.error };
|
|
11576
11714
|
}
|
|
11577
11715
|
if (target.kind === "main") {
|
|
11578
|
-
|
|
11579
|
-
|
|
11716
|
+
const patch = { remoteMode: mode };
|
|
11717
|
+
if (allocate) patch.remoteToken = nextToken;
|
|
11718
|
+
if (allocate || target.mode !== mode) {
|
|
11719
|
+
applyMainIntent(patch, { id: "main", name: "\u539F\u751F DSH", mode });
|
|
11720
|
+
if (allocate) {
|
|
11721
|
+
const events = g.getEvents();
|
|
11722
|
+
if (events) {
|
|
11723
|
+
try {
|
|
11724
|
+
events.append("dsh_remote_token_changed", { id: "main", tokenSet: true, autoAllocated: true });
|
|
11725
|
+
} catch {
|
|
11726
|
+
}
|
|
11727
|
+
}
|
|
11728
|
+
}
|
|
11729
|
+
}
|
|
11730
|
+
return { ok: true, tokenAutoAllocated: allocate };
|
|
11580
11731
|
}
|
|
11581
|
-
|
|
11732
|
+
const r = allocate ? g.getInstances().updateInstance(id, { remoteMode: mode, remoteToken: nextToken }) : g.getInstances().updateInstance(id, { remoteMode: mode });
|
|
11733
|
+
if (r && r.ok !== false) r.tokenAutoAllocated = allocate;
|
|
11734
|
+
return r;
|
|
11582
11735
|
},
|
|
11583
|
-
/**
|
|
11584
|
-
* 拒绝而非当作清除(漏 token 字段清掉访问凭据是事故,不是语义)。
|
|
11585
|
-
*
|
|
11736
|
+
/** 访问令牌唯一显式写入口。token 必须是字符串:空串=显式清除;缺字段/非字符串=请求方缺陷,
|
|
11737
|
+
* 拒绝而非当作清除(漏 token 字段清掉访问凭据是事故,不是语义)。
|
|
11738
|
+
* lan 模式清除后仍然可用(relay 空令牌放行),但下一次写入远程控制模式会由 setRemoteMode 重新分配;
|
|
11739
|
+
* wan 模式的守门由执行边界闸兜住(令牌清空后 syncFrpc 复判不过闸即停隧道)。 */
|
|
11586
11740
|
setRemoteToken(id, token) {
|
|
11587
11741
|
if (typeof token !== "string") {
|
|
11588
11742
|
return { ok: false, error: "token \u5FC5\u987B\u663E\u5F0F\u7ED9\u51FA\uFF08\u7A7A\u4E32=\u6E05\u9664\uFF09" };
|
|
@@ -17587,7 +17741,7 @@ var require_browser2 = __commonJS({
|
|
|
17587
17741
|
const tz = pick(TZ_POOL);
|
|
17588
17742
|
const antiEnv = Object.assign({}, sysEnv, { TZ: tz, LANG: lang });
|
|
17589
17743
|
const r = platform.browser.launchIsolated(url, { profileDir: tmpProfile, size, lang, antiEnv, sysEnv, onExit });
|
|
17590
|
-
if (!r || !r.ok) return null;
|
|
17744
|
+
if (!r || !r.ok) return { profile: null, result: r || { ok: false, reason: "spawn-failed" } };
|
|
17591
17745
|
if (r.isolated) {
|
|
17592
17746
|
const t30 = setTimeout(() => {
|
|
17593
17747
|
try {
|
|
@@ -17597,9 +17751,9 @@ var require_browser2 = __commonJS({
|
|
|
17597
17751
|
}, 30 * 60 * 1e3);
|
|
17598
17752
|
if (t30.unref) t30.unref();
|
|
17599
17753
|
}
|
|
17600
|
-
return tmpProfile;
|
|
17601
|
-
} catch {
|
|
17602
|
-
return null;
|
|
17754
|
+
return { profile: tmpProfile, result: r };
|
|
17755
|
+
} catch (e) {
|
|
17756
|
+
return { profile: null, result: { ok: false, reason: "spawn-failed", error: "\u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25\uFF1A" + (e && e.message || e) } };
|
|
17603
17757
|
}
|
|
17604
17758
|
}
|
|
17605
17759
|
module2.exports = { graphicalEnv, openInBrowser };
|
|
@@ -17777,7 +17931,7 @@ var require_oauth = __commonJS({
|
|
|
17777
17931
|
promise.catch(() => {
|
|
17778
17932
|
});
|
|
17779
17933
|
st._ccLoginPromise = promise;
|
|
17780
|
-
const
|
|
17934
|
+
const opened = openInBrowser(authUrl, () => {
|
|
17781
17935
|
if (st._ccLoginRound !== roundId) return;
|
|
17782
17936
|
if (st._ccLoginReject) {
|
|
17783
17937
|
const r = st._ccLoginReject;
|
|
@@ -17789,7 +17943,8 @@ var require_oauth = __commonJS({
|
|
|
17789
17943
|
}
|
|
17790
17944
|
}
|
|
17791
17945
|
});
|
|
17792
|
-
|
|
17946
|
+
const evidence = opened.result || {};
|
|
17947
|
+
if (!opened.profile) {
|
|
17793
17948
|
st._ccLoginPromise = null;
|
|
17794
17949
|
st._ccLoginResolve = null;
|
|
17795
17950
|
st._ccLoginReject = null;
|
|
@@ -17802,10 +17957,29 @@ var require_oauth = __commonJS({
|
|
|
17802
17957
|
} catch {
|
|
17803
17958
|
}
|
|
17804
17959
|
st._ccLogin = null;
|
|
17805
|
-
return {
|
|
17960
|
+
return {
|
|
17961
|
+
ok: false,
|
|
17962
|
+
authUrl,
|
|
17963
|
+
url: authUrl,
|
|
17964
|
+
opened: false,
|
|
17965
|
+
confirmed: false,
|
|
17966
|
+
handedOff: false,
|
|
17967
|
+
reason: evidence.reason || "no-launcher",
|
|
17968
|
+
error: (evidence.error || "\u65E0\u6CD5\u8C03\u8D77\u7CFB\u7EDF\u6D4F\u89C8\u5668") + "\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u4E0B\u65B9\u5730\u5740\u5B8C\u6210\u6388\u6743"
|
|
17969
|
+
};
|
|
17806
17970
|
}
|
|
17807
|
-
st._ccLogin = { state, port, server, tmpProfile };
|
|
17808
|
-
return {
|
|
17971
|
+
st._ccLogin = { state, port, server, tmpProfile: opened.profile };
|
|
17972
|
+
return Object.assign({}, evidence, {
|
|
17973
|
+
ok: true,
|
|
17974
|
+
authUrl,
|
|
17975
|
+
url: authUrl,
|
|
17976
|
+
state,
|
|
17977
|
+
port,
|
|
17978
|
+
waitMs: 18e4,
|
|
17979
|
+
opened: true,
|
|
17980
|
+
// 非隔离引擎(Safari/snap 包装器)由平台层降级:账号隔离不成立,换账号只能靠超时重发或手动窗口。
|
|
17981
|
+
isolated: evidence.isolated === true
|
|
17982
|
+
});
|
|
17809
17983
|
}
|
|
17810
17984
|
async function commandcodeLoginWait(timeoutMs) {
|
|
17811
17985
|
const p = st._ccLoginPromise;
|
|
@@ -21545,6 +21719,9 @@ var require_market = __commonJS({
|
|
|
21545
21719
|
this._cache = null;
|
|
21546
21720
|
this._ts = 0;
|
|
21547
21721
|
this._inFlight = null;
|
|
21722
|
+
this._lastError = null;
|
|
21723
|
+
this._nextAllowAt = 0;
|
|
21724
|
+
this.retryBackoffMs = opts.retryBackoffMs || 6e4;
|
|
21548
21725
|
this.buildBudgetMs = opts.buildBudgetMs || 24e4;
|
|
21549
21726
|
this.loadFromDisk();
|
|
21550
21727
|
}
|
|
@@ -21558,38 +21735,51 @@ var require_market = __commonJS({
|
|
|
21558
21735
|
saveToDisk() {
|
|
21559
21736
|
marketCache.saveIndex(this.cacheDir, this.indexFile, this._cache, this.logger);
|
|
21560
21737
|
}
|
|
21561
|
-
/**
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
|
|
21568
|
-
|
|
21738
|
+
/** 索引快照,耗时上限必须是「读内存」:构建(默认预算 4 分钟)交 _requestBuild 在后台跑。
|
|
21739
|
+
* 挂到请求上等构建,面板 15s 计时会先放弃而服务端仍在跑 —— 用户见失败、结论却是好的。
|
|
21740
|
+
* 无缓存且上次构建刚失败时按 retryBackoffMs 退避,免得每次读都点燃一轮 4 分钟构建;force 不受退避约束。 */
|
|
21741
|
+
getIndex(force = false) {
|
|
21742
|
+
const need = !this._cache || force || Date.now() - this._ts >= this.ttl;
|
|
21743
|
+
if (need && (force || Date.now() >= this._nextAllowAt)) this._requestBuild();
|
|
21744
|
+
return Promise.resolve(this._view());
|
|
21745
|
+
}
|
|
21746
|
+
/** 快照视图(无缓存也返回合法空索引,绝不为取数据而触网)。 */
|
|
21747
|
+
_view() {
|
|
21748
|
+
const c = this._cache;
|
|
21749
|
+
return {
|
|
21750
|
+
ok: true,
|
|
21751
|
+
indexedAt: c ? c.indexedAt : 0,
|
|
21752
|
+
sources: c ? c.sources : { npm: 0, github: 0, community: 0 },
|
|
21753
|
+
total: c ? c.total : 0,
|
|
21754
|
+
plugins: c ? c.plugins : [],
|
|
21755
|
+
building: !!this._inFlight,
|
|
21756
|
+
error: this._lastError
|
|
21757
|
+
};
|
|
21569
21758
|
}
|
|
21570
|
-
/**
|
|
21571
|
-
* (a)
|
|
21572
|
-
* (b)
|
|
21573
|
-
*
|
|
21574
|
-
|
|
21759
|
+
/** 启动一次构建(无消费者 await)并登记为在飞引用;两条不变量:
|
|
21760
|
+
* (a) 无人等待的失败不得成为进程级 unhandledRejection —— 给 raw 挂 no-op handler「标记已处理」;
|
|
21761
|
+
* (b) 去重:已有在飞构建就直接复用,force 连点不得叠加并发构建(M-i)。
|
|
21762
|
+
* 失败原因记入 _lastError 并由快照如实上报 —— 取不到不能显示成「没有插件」。 */
|
|
21763
|
+
_requestBuild() {
|
|
21764
|
+
if (this._inFlight) return this._inFlight;
|
|
21575
21765
|
const raw = this.buildIndex();
|
|
21576
21766
|
raw.catch(() => {
|
|
21577
21767
|
});
|
|
21578
21768
|
this._inFlight = raw;
|
|
21579
|
-
raw.then(
|
|
21580
|
-
|
|
21581
|
-
|
|
21769
|
+
raw.then(
|
|
21770
|
+
() => {
|
|
21771
|
+
this._lastError = null;
|
|
21772
|
+
},
|
|
21773
|
+
(e) => {
|
|
21774
|
+
this._lastError = e && e.message || String(e);
|
|
21775
|
+
if (!this._cache) this._nextAllowAt = Date.now() + this.retryBackoffMs;
|
|
21776
|
+
this.logger.warn && this.logger.warn("market: \u7D22\u5F15\u6784\u5EFA\u5931\u8D25\uFF08\u6CBF\u7528\u65E7\u7F13\u5B58\uFF09: " + this._lastError);
|
|
21777
|
+
}
|
|
21778
|
+
).then(() => {
|
|
21582
21779
|
if (this._inFlight === raw) this._inFlight = null;
|
|
21583
21780
|
});
|
|
21584
21781
|
return raw;
|
|
21585
21782
|
}
|
|
21586
|
-
_refreshIfStale() {
|
|
21587
|
-
if (Date.now() - this._ts < this.ttl) return;
|
|
21588
|
-
if (this._inFlight) return;
|
|
21589
|
-
this._startBuild().catch((e) => {
|
|
21590
|
-
this.logger.warn && this.logger.warn("market: \u540E\u53F0\u5237\u65B0\u7D22\u5F15\u5931\u8D25\uFF08\u6CBF\u7528\u65E7\u7F13\u5B58\uFF09: " + (e && e.message || e));
|
|
21591
|
-
});
|
|
21592
|
-
}
|
|
21593
21783
|
async buildIndex() {
|
|
21594
21784
|
const start = Date.now();
|
|
21595
21785
|
const bctx = { deadline: start + this.buildBudgetMs, truncated: /* @__PURE__ */ new Set() };
|
|
@@ -22753,22 +22943,57 @@ var require_updater = __commonJS({
|
|
|
22753
22943
|
if (latest !== null) ctx._updCache[name] = { latest, at: Date.now() };
|
|
22754
22944
|
return { latest, error };
|
|
22755
22945
|
}
|
|
22756
|
-
|
|
22946
|
+
function checkUpdates(ctx, force) {
|
|
22947
|
+
if (!ctx._updSnapshot || force) _requestCheck(ctx, force);
|
|
22948
|
+
const s = ctx._updSnapshot;
|
|
22949
|
+
return Promise.resolve({
|
|
22950
|
+
ok: true,
|
|
22951
|
+
checkedAt: s ? s.checkedAt : 0,
|
|
22952
|
+
plugins: s ? s.plugins : [],
|
|
22953
|
+
refreshing: !!ctx._updInFlight,
|
|
22954
|
+
error: s ? s.error : null
|
|
22955
|
+
});
|
|
22956
|
+
}
|
|
22957
|
+
function _requestCheck(ctx, force) {
|
|
22958
|
+
if (ctx._updInFlight) return ctx._updInFlight;
|
|
22959
|
+
const raw = refreshUpdates(ctx, force);
|
|
22960
|
+
raw.catch(() => {
|
|
22961
|
+
});
|
|
22962
|
+
ctx._updInFlight = raw;
|
|
22963
|
+
raw.catch((e) => {
|
|
22964
|
+
const msg = e && e.message || String(e);
|
|
22965
|
+
if (ctx.logger && ctx.logger.warn) ctx.logger.warn("plugin check-updates \u5931\u8D25: " + msg);
|
|
22966
|
+
if (!ctx._updSnapshot) ctx._updSnapshot = { checkedAt: 0, plugins: [], error: msg };
|
|
22967
|
+
}).then(() => {
|
|
22968
|
+
if (ctx._updInFlight === raw) ctx._updInFlight = null;
|
|
22969
|
+
});
|
|
22970
|
+
return raw;
|
|
22971
|
+
}
|
|
22972
|
+
async function refreshUpdates(ctx, force) {
|
|
22757
22973
|
const targets = [ctx._nativeTarget(), ...ctx._allSandboxTargets()];
|
|
22758
22974
|
const meta = /* @__PURE__ */ new Map();
|
|
22759
22975
|
for (const t of targets) {
|
|
22760
22976
|
for (const p of ctx.installedOn(t)) {
|
|
22761
22977
|
if (meta.has(p.name)) continue;
|
|
22762
|
-
|
|
22763
|
-
let latest = null;
|
|
22764
|
-
if (st === "npm" && ctx.dist) latest = (await latestCached(ctx, p.name, force)).latest;
|
|
22765
|
-
meta.set(p.name, { specType: st, latest });
|
|
22978
|
+
meta.set(p.name, { specType: specType(p.source), latest: null, error: null });
|
|
22766
22979
|
}
|
|
22767
22980
|
}
|
|
22981
|
+
const npmNames = [...meta.entries()].filter(([, m]) => m.specType === "npm" && ctx.dist).map(([name]) => name);
|
|
22982
|
+
const errors = [];
|
|
22983
|
+
for (let i = 0; i < npmNames.length; i += 6) {
|
|
22984
|
+
const slice = npmNames.slice(i, i + 6);
|
|
22985
|
+
await Promise.all(slice.map(async (name) => {
|
|
22986
|
+
const r = await latestCached(ctx, name, force);
|
|
22987
|
+
const m = meta.get(name);
|
|
22988
|
+
m.latest = r.latest;
|
|
22989
|
+
m.error = r.error;
|
|
22990
|
+
if (r.error) errors.push(name + ": " + r.error);
|
|
22991
|
+
}));
|
|
22992
|
+
}
|
|
22768
22993
|
const rows = [];
|
|
22769
22994
|
for (const t of targets) {
|
|
22770
22995
|
for (const p of ctx.installedOn(t)) {
|
|
22771
|
-
const m = meta.get(p.name) || { specType: "npm", latest: null };
|
|
22996
|
+
const m = meta.get(p.name) || { specType: "npm", latest: null, error: null };
|
|
22772
22997
|
const updateAvailable = m.specType === "npm" && isUpdateAvailable(m.latest, p.version);
|
|
22773
22998
|
rows.push({ name: p.name, target: t.id, targetName: t.name, installed: p.version || null, latest: m.latest || null, updateAvailable, specType: m.specType, bundle: p.bundle });
|
|
22774
22999
|
}
|
|
@@ -22777,13 +23002,15 @@ var require_updater = __commonJS({
|
|
|
22777
23002
|
for (const row of rows) {
|
|
22778
23003
|
let rec = byName.get(row.name);
|
|
22779
23004
|
if (!rec) {
|
|
22780
|
-
byName.set(row.name, { name: row.name, specType: row.specType, targets: [] });
|
|
23005
|
+
byName.set(row.name, { name: row.name, specType: row.specType, error: meta.get(row.name) ? meta.get(row.name).error : null, targets: [] });
|
|
22781
23006
|
rec = byName.get(row.name);
|
|
22782
23007
|
}
|
|
22783
23008
|
rec.targets.push({ id: row.target, name: row.targetName, installed: row.installed, latest: row.latest, updateAvailable: row.updateAvailable });
|
|
22784
23009
|
}
|
|
22785
|
-
const plugins = [...byName.values()].map((x) => ({ name: x.name, specType: x.specType, updateAvailable: x.targets.some((t) => t.updateAvailable), targets: x.targets })).sort((a, b) => a.name.localeCompare(b.name));
|
|
22786
|
-
|
|
23010
|
+
const plugins = [...byName.values()].map((x) => ({ name: x.name, specType: x.specType, error: x.error, updateAvailable: x.targets.some((t) => t.updateAvailable), targets: x.targets })).sort((a, b) => a.name.localeCompare(b.name));
|
|
23011
|
+
const snap = { checkedAt: Date.now(), plugins, error: errors.length ? errors.join("; ") : null };
|
|
23012
|
+
ctx._updSnapshot = snap;
|
|
23013
|
+
return snap;
|
|
22787
23014
|
}
|
|
22788
23015
|
async function update(ctx, name, targetStr) {
|
|
22789
23016
|
if (!name) return { ok: false, error: "missing plugin name" };
|
|
@@ -22885,6 +23112,8 @@ var require_plugin = __commonJS({
|
|
|
22885
23112
|
this.tasks = opts.tasks || null;
|
|
22886
23113
|
this._updCache = {};
|
|
22887
23114
|
this._updTTL = 6 * 3600 * 1e3;
|
|
23115
|
+
this._updSnapshot = null;
|
|
23116
|
+
this._updInFlight = null;
|
|
22888
23117
|
this.jobs = createJobs({ tasks: this.tasks });
|
|
22889
23118
|
this.layers = createLayers({ overlayFile: this.overlayFile, logger: this.logger });
|
|
22890
23119
|
this.store = new store.PluginStore({ getInventory: () => this.inventory() });
|
|
@@ -25283,7 +25512,7 @@ var require_guard = __commonJS({
|
|
|
25283
25512
|
var fs2 = require("node:fs");
|
|
25284
25513
|
var path2 = require("node:path");
|
|
25285
25514
|
function owns(pathname) {
|
|
25286
|
-
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 === "/ports" || pathname === "/shutdown";
|
|
25515
|
+
return pathname === "/changelog" || pathname.startsWith("/guard/") || pathname === "/autostart" || pathname.startsWith("/settings/") || pathname.startsWith("/self-update/") || pathname === "/env/dsh" || pathname === "/env/status" || pathname === "/env/node-lts" || pathname === "/env/open-url" || pathname === "/ports" || pathname === "/shutdown";
|
|
25287
25516
|
}
|
|
25288
25517
|
function fetchDshChangelog(res, sup) {
|
|
25289
25518
|
const v = sup && sup.nativeManager ? sup.nativeManager.versionInfo() : {};
|
|
@@ -25295,7 +25524,7 @@ var require_guard = __commonJS({
|
|
|
25295
25524
|
return res.end(md);
|
|
25296
25525
|
}
|
|
25297
25526
|
function handle(ctx) {
|
|
25298
|
-
const { sup, req, res, pathname, send, collectBody, originAllowed } = ctx;
|
|
25527
|
+
const { sup, req, res, pathname, identity, send, collectBody, originAllowed, browser } = ctx;
|
|
25299
25528
|
if (req.method === "GET" && pathname === "/changelog") {
|
|
25300
25529
|
return fetchDshChangelog(res, sup);
|
|
25301
25530
|
}
|
|
@@ -25459,6 +25688,27 @@ var require_guard = __commonJS({
|
|
|
25459
25688
|
if (req.method === "GET" && pathname === "/env/node-lts") {
|
|
25460
25689
|
return sup.nodeLtsStatus().then((r) => send(200, r)).catch((e) => send(500, { ok: false, error: e.message }));
|
|
25461
25690
|
}
|
|
25691
|
+
if (req.method === "POST" && pathname === "/env/open-url") {
|
|
25692
|
+
if (!identity.loopback) {
|
|
25693
|
+
req.resume();
|
|
25694
|
+
return send(403, { ok: false, error: "\u4EC5\u5185\u6838\u6240\u5728\u673A\u5668\u53EF\u8BF7\u5185\u6838\u8C03\u8D77\u6D4F\u89C8\u5668\uFF0C\u8BF7\u590D\u5236\u6216\u81EA\u884C\u6253\u5F00\u8BE5\u5730\u5740" });
|
|
25695
|
+
}
|
|
25696
|
+
if (!originAllowed(req, sup.config.apiPort)) {
|
|
25697
|
+
req.resume();
|
|
25698
|
+
return send(403, { ok: false, error: "cross-origin request rejected" });
|
|
25699
|
+
}
|
|
25700
|
+
collectBody(req, res, 4096, (body) => {
|
|
25701
|
+
let url = null;
|
|
25702
|
+
try {
|
|
25703
|
+
const j = body ? JSON.parse(body) : {};
|
|
25704
|
+
if (typeof j.url === "string") url = j.url;
|
|
25705
|
+
} catch {
|
|
25706
|
+
}
|
|
25707
|
+
if (!url) return send(400, { ok: false, error: '\u9700\u8981 {"url":"http(s)://\u2026"}' });
|
|
25708
|
+
return Promise.resolve(browser.openBrowser(url)).then((r) => send(r.ok ? 200 : 500, r)).catch((e) => send(500, { ok: false, reason: "spawn-failed", error: "\u6253\u5F00\u6D4F\u89C8\u5668\u5931\u8D25\uFF1A" + (e && e.message || e), url }));
|
|
25709
|
+
});
|
|
25710
|
+
return;
|
|
25711
|
+
}
|
|
25462
25712
|
if (req.method === "GET" && pathname === "/ports") {
|
|
25463
25713
|
return Promise.resolve(sup.listPorts()).then((r) => send(200, r)).catch((e) => send(500, { error: e && e.message }));
|
|
25464
25714
|
}
|
|
@@ -25953,7 +26203,6 @@ var require_exchange = __commonJS({
|
|
|
25953
26203
|
var require_instances = __commonJS({
|
|
25954
26204
|
"src/api/domains/instances.js"(exports2, module2) {
|
|
25955
26205
|
"use strict";
|
|
25956
|
-
var platform = require_os();
|
|
25957
26206
|
var execPath = require_exec_path();
|
|
25958
26207
|
var crypto = require("node:crypto");
|
|
25959
26208
|
var { bootstrapDshCookie } = require_exchange();
|
|
@@ -26055,17 +26304,18 @@ var require_instances = __commonJS({
|
|
|
26055
26304
|
return sharedErr + "\uFF1B" + FORMS + "\uFF1B\u9700\u8981\u5176\u5B83\u53EF\u6267\u884C\u8BF7\u8D70\u63D2\u4EF6\u5B89\u88C5\u901A\u9053";
|
|
26056
26305
|
}
|
|
26057
26306
|
function handle(ctx) {
|
|
26058
|
-
const { sup, req, res, pathname, identity, send, collectBody, originAllowed, tokOf } = ctx;
|
|
26307
|
+
const { sup, req, res, pathname, identity, send, collectBody, originAllowed, tokOf, browser } = ctx;
|
|
26059
26308
|
function openInSystemBrowser(url) {
|
|
26060
|
-
return
|
|
26309
|
+
return browser.openBrowser(url);
|
|
26061
26310
|
}
|
|
26062
26311
|
if (req.method === "GET" && pathname === "/open") return handleOpen(ctx);
|
|
26063
26312
|
if (req.method === "GET" && pathname === "/instances") {
|
|
26064
26313
|
const decorate = (it) => {
|
|
26065
26314
|
const tok = tokOf(it.id);
|
|
26066
26315
|
const out = Object.assign({}, it);
|
|
26067
|
-
delete out.remoteToken;
|
|
26068
26316
|
const loopback = identity.loopback;
|
|
26317
|
+
out.tokenSet = !!String(it.remoteToken || "").trim();
|
|
26318
|
+
if (!loopback) delete out.remoteToken;
|
|
26069
26319
|
out.authUrl = tok && loopback ? "http://127.0.0.1:" + it.port + "/?token=" + encodeURIComponent(tok) : "http://127.0.0.1:" + it.port + "/";
|
|
26070
26320
|
out.tokenPresent = loopback && !!tok;
|
|
26071
26321
|
return out;
|
|
@@ -26121,9 +26371,13 @@ var require_instances = __commonJS({
|
|
|
26121
26371
|
if (!(Number(it.port) > 0)) return send(400, { ok: false, error: "\u975E\u6CD5\u7AEF\u53E3" });
|
|
26122
26372
|
const code = issueOpenWebCode(j.id);
|
|
26123
26373
|
const url = "http://127.0.0.1:" + sup.config.apiPort + "/open?code=" + code;
|
|
26124
|
-
|
|
26125
|
-
|
|
26126
|
-
|
|
26374
|
+
return Promise.resolve(openInSystemBrowser(url)).then((r) => {
|
|
26375
|
+
if (!r.ok) dropOpenWebCode(code);
|
|
26376
|
+
return send(r.ok ? 200 : 500, r);
|
|
26377
|
+
}).catch((e) => {
|
|
26378
|
+
dropOpenWebCode(code);
|
|
26379
|
+
return send(500, { ok: false, reason: "spawn-failed", error: "\u6253\u5F00\u6D4F\u89C8\u5668\u5931\u8D25\uFF1A" + (e && e.message || e), url });
|
|
26380
|
+
});
|
|
26127
26381
|
} catch (e) {
|
|
26128
26382
|
return send(500, { ok: false, error: e.message });
|
|
26129
26383
|
}
|
|
@@ -26568,6 +26822,7 @@ var require_server = __commonJS({
|
|
|
26568
26822
|
var { serveStatic } = require_static();
|
|
26569
26823
|
var { identify } = require_identity3();
|
|
26570
26824
|
var { originAllowed, requestHasAccessKey, isShellOrigin } = require_security();
|
|
26825
|
+
var browserExit = require_browser();
|
|
26571
26826
|
function safeFail(res, err, where) {
|
|
26572
26827
|
try {
|
|
26573
26828
|
const body = JSON.stringify({ ok: false, error: err && err.message || String(err) });
|
|
@@ -26585,7 +26840,8 @@ var require_server = __commonJS({
|
|
|
26585
26840
|
} catch {
|
|
26586
26841
|
}
|
|
26587
26842
|
}
|
|
26588
|
-
function createServer(sup) {
|
|
26843
|
+
function createServer(sup, deps) {
|
|
26844
|
+
const browser = deps && deps.browser || browserExit;
|
|
26589
26845
|
return http2.createServer((req, res) => {
|
|
26590
26846
|
const shellOrigin = (() => {
|
|
26591
26847
|
const o = req.headers.origin;
|
|
@@ -26653,7 +26909,7 @@ var require_server = __commonJS({
|
|
|
26653
26909
|
} catch (e) {
|
|
26654
26910
|
return send(400, { error: "bad request encoding" });
|
|
26655
26911
|
}
|
|
26656
|
-
const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf };
|
|
26912
|
+
const ctx = { sup, req, res, pathname: decodedPathname, identity, send, collectBody, originAllowed, tokOf, browser };
|
|
26657
26913
|
for (const d of API_DOMAINS) {
|
|
26658
26914
|
if (d.owns(pathname)) {
|
|
26659
26915
|
try {
|