@dsh-sup/dsh-core-linux-x64 0.1.6-BETA.1 → 0.1.6-BETA.2
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 +333 -550
- package/package.json +1 -1
package/core.cjs
CHANGED
|
@@ -15,10 +15,10 @@ var require_exec = __commonJS({
|
|
|
15
15
|
function options(opts) {
|
|
16
16
|
const o = opts || {};
|
|
17
17
|
return {
|
|
18
|
-
// timeout
|
|
18
|
+
// timeout 是历史别名(兼容旧调用点拼写,若不与 timeoutMs 归一会被静默忽略):
|
|
19
19
|
// 两种拼写都收,timeoutMs 优先;新调用一律用 timeoutMs。
|
|
20
20
|
timeout: o.timeoutMs || o.timeout || DEFAULT_TIMEOUT_MS,
|
|
21
|
-
// 必须 SIGKILL:SIGTERM
|
|
21
|
+
// 必须 SIGKILL:SIGTERM 对挂起或被停住的进程可能无效。
|
|
22
22
|
killSignal: o.killSignal || "SIGKILL",
|
|
23
23
|
maxBuffer: o.maxBuffer || DEFAULT_MAX_BUFFER,
|
|
24
24
|
stdio: o.stdio || ["ignore", "pipe", "pipe"],
|
|
@@ -195,9 +195,8 @@ var require_config = __commonJS({
|
|
|
195
195
|
}
|
|
196
196
|
var BASE_DEFAULTS = {
|
|
197
197
|
probeIntervalMs: 5e3,
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// 退化为端口在线即健康。
|
|
198
|
+
// 健康探测三层:L0 进程存活 + L1 端口监听 + L2 HTTP GET healthUrl。probeTimeoutMs = 单次探测超时;
|
|
199
|
+
// failThreshold = 连续失败判故障(防抖动);httpProbeEnabled=false 退化为端口在线即健康。
|
|
201
200
|
probeTimeoutMs: 3e3,
|
|
202
201
|
failThreshold: 2,
|
|
203
202
|
httpProbeEnabled: true,
|
|
@@ -208,18 +207,14 @@ var require_config = __commonJS({
|
|
|
208
207
|
crashBurst: 5,
|
|
209
208
|
backoff: [3e4, 6e4, 12e4, 3e5, 6e5],
|
|
210
209
|
apiHost: "127.0.0.1",
|
|
211
|
-
// API
|
|
210
|
+
// API 端口:高位不常用段起始(常用端口段易冲突);守卫启动被占则自动顺延并持久化。
|
|
212
211
|
apiPort: 36360,
|
|
213
|
-
// 注意:daemon
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// (Linux ip_local_port_range=32768-60999),落在 IANA User 段低位供监听池使用。
|
|
217
|
-
// managed = relay/proxyInstance/oauthCallback 共享池;providerApi = 智能路由供应商独立端点池。
|
|
218
|
-
// null = 内置默认池。
|
|
212
|
+
// 注意:daemon 控制通道端口属业务域知识,经注入声明提供(同上),原键名与值逐字保留。
|
|
213
|
+
// 动态端口池(范围是配置项,非编译期常量):null = 内置默认池,默认避开 OS 动态端口范围
|
|
214
|
+
// (Linux ip_local_port_range=32768-60999),落在 IANA User 段低位。额外池由域装配期申报(DS-G4)。
|
|
219
215
|
portPools: null,
|
|
220
216
|
stateFile: path2.join(SUP, "state.json"),
|
|
221
|
-
//
|
|
222
|
-
// 显式配置(既有 config.json / 测试)仍尊重用户给定路径,不强行改写。
|
|
217
|
+
// 日志目录布局:事件在 events/guard.events.log,分级日志在 log/;显式配置的路径尊重用户给定值,不强改。
|
|
223
218
|
logFile: path2.join(SUP, "events", "guard.events.log"),
|
|
224
219
|
eventsMaxBytes: 5 * 1024 * 1024,
|
|
225
220
|
supervisorLogFile: path2.join(SUP, "log", "guard.log"),
|
|
@@ -228,22 +223,17 @@ var require_config = __commonJS({
|
|
|
228
223
|
logLevel: "info",
|
|
229
224
|
logMaxBytes: 5 * 1024 * 1024,
|
|
230
225
|
notifyEnabled: true,
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
// 安装/升级由桌面壳执行(见 RELEASE-AND-UPDATE-MECHANISM.md)。
|
|
226
|
+
// 内核更新单写入者 = 桌面壳:corePackageName 仅为内核自身 npm 子包名,守卫只读它查版本状态,
|
|
227
|
+
// 安装/升级由壳执行。不得再引入 manifest 更新通道(残留键已删,config.json 中未知键加载时忽略)。
|
|
234
228
|
corePackageName: null,
|
|
235
|
-
// 旧 manifest 模式的残留键 selfUpdateManifestUrl / selfUpdateDir 已删除(全仓无赋值点)。
|
|
236
|
-
// 既有用户 config.json 若仍含这两键,加载时忽略即可(未知键不报错);新代码不得再引入 manifest 更新通道。
|
|
237
229
|
pluginsProfileName: "web",
|
|
238
230
|
packageName: "@deepseek-ai/dsh",
|
|
239
|
-
// 灰度名单(RELEASE-CHANNEL-CONTRACT
|
|
240
|
-
//
|
|
231
|
+
// 灰度名单(RELEASE-CHANNEL-CONTRACT):本机 canary:true 即灰度机,仅对内核包生效,对第三方包
|
|
232
|
+
// (packageName)无效;未置真即非灰度。
|
|
241
233
|
canary: false,
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
// (见 platform/contract/registry.js)。此处仅保留官方源(能上网)+ npmmirror(中国网络)两条,
|
|
246
|
-
// 覆盖契约不可用时也能跑这一底线(不变量 C2)。
|
|
234
|
+
// 最小兜底镜像源(不变量 C2):完整目录归壳管理,经 <产品状态根>/supervisor/registry.json 投放,
|
|
235
|
+
// 内核由 platform/distribution 读取(契约见 platform/contract/registry.js);
|
|
236
|
+
// 此处官方源 + npmmirror 两条兜底,保证契约不可用时仍可安装。
|
|
247
237
|
registries: [
|
|
248
238
|
"https://registry.npmjs.org",
|
|
249
239
|
"https://registry.npmmirror.com"
|
|
@@ -253,12 +243,11 @@ var require_config = __commonJS({
|
|
|
253
243
|
initialCheckDelayMs: 2e4,
|
|
254
244
|
upgradeTimeoutMs: 6e5,
|
|
255
245
|
installCommandTemplate: ["npm", "install", "-g", "{pkg}@{version}"],
|
|
256
|
-
// apiAccessKey
|
|
257
|
-
//
|
|
258
|
-
//
|
|
246
|
+
// apiAccessKey(可选)出回环访问密钥:配置后,0.0.0.0(局域网)与 FRP 公网通道的 API 请求必须携带
|
|
247
|
+
// Authorization: Bearer <key> 或 ?access_key=<key>,否则 401;回环豁免。
|
|
248
|
+
// 不配置 = LAN 受 RFC1918 白名单约束,FRP 暴露仍强制 remoteToken。
|
|
259
249
|
apiAccessKey: null,
|
|
260
|
-
// 关闭窗口行为(系统级):'hide' =
|
|
261
|
-
// 'exit' = 退出管家,结束壳并停止全部服务链。
|
|
250
|
+
// 关闭窗口行为(系统级):'hide' = 隐藏至托盘、服务常驻(默认);'exit' = 退出管家并停全部服务链。
|
|
262
251
|
closeAction: "hide"
|
|
263
252
|
};
|
|
264
253
|
function buildDefaults2(ext) {
|
|
@@ -337,7 +326,7 @@ var require_domain_config = __commonJS({
|
|
|
337
326
|
var defaults = [
|
|
338
327
|
{
|
|
339
328
|
at: "portPools",
|
|
340
|
-
// daemon
|
|
329
|
+
// daemon 控制通道端口:集中定义,杜绝散落硬编码。
|
|
341
330
|
// 这两个值同时是 app/ctl/client.js 与两个 daemon 的兜底端口,不得单独改动
|
|
342
331
|
// (改动即需同步 8 处 43107/43108 兜底常量)。
|
|
343
332
|
values: {
|
|
@@ -347,7 +336,7 @@ var require_domain_config = __commonJS({
|
|
|
347
336
|
},
|
|
348
337
|
{
|
|
349
338
|
at: "corePackageName",
|
|
350
|
-
//
|
|
339
|
+
// 智能路由启动开关。
|
|
351
340
|
values: {
|
|
352
341
|
routerAutostart: false
|
|
353
342
|
}
|
|
@@ -373,7 +362,7 @@ var require_version = __commonJS({
|
|
|
373
362
|
var fs2 = require("node:fs");
|
|
374
363
|
var path2 = require("node:path");
|
|
375
364
|
function guardVersion() {
|
|
376
|
-
if (true) return String("0.1.6-BETA.
|
|
365
|
+
if (true) return String("0.1.6-BETA.2");
|
|
377
366
|
try {
|
|
378
367
|
return JSON.parse(fs2.readFileSync(path2.join(__dirname, "..", "..", "package.json"), "utf8")).version || "unknown";
|
|
379
368
|
} catch {
|
|
@@ -2248,7 +2237,7 @@ var require_events = __commonJS({
|
|
|
2248
2237
|
}
|
|
2249
2238
|
return max;
|
|
2250
2239
|
}
|
|
2251
|
-
//
|
|
2240
|
+
// 文件实际最大 seq —— 只读尾部(append-only + seq 单调,末行即最大)。
|
|
2252
2241
|
// 末行不可解析(截断起点落在行中 / 崩溃残留半行)时从后往前找首条完整记录;
|
|
2253
2242
|
// 整段都解析不出(文件为空/刚轮转走)才回退 _maxSeq() 全扫描,含 .1 防与旧代重号。
|
|
2254
2243
|
_fileMaxSeq() {
|
|
@@ -2708,8 +2697,8 @@ var require_hub = __commonJS({
|
|
|
2708
2697
|
var { loadWatermark, saveWatermark } = require_watermark();
|
|
2709
2698
|
var EventHub = class {
|
|
2710
2699
|
// opts: stateDir 守卫状态目录(聚合流/水位落此);guardEvents 守卫 Events 实例(源之一);
|
|
2711
|
-
// guardLogFile
|
|
2712
|
-
// daemonLogs
|
|
2700
|
+
// guardLogFile/dshLogFile/upgradeLogFile 守卫侧运行日志(/logs/tail 用);
|
|
2701
|
+
// daemonLogs/ctlPorts 按装配短键索引;logger 可选。
|
|
2713
2702
|
constructor(opts) {
|
|
2714
2703
|
this.stateDir = opts.stateDir;
|
|
2715
2704
|
this.aggBase = opts.aggBase || "state";
|
|
@@ -2738,7 +2727,7 @@ var require_hub = __commonJS({
|
|
|
2738
2727
|
if (this.watermark[this._localSource] == null && this.guardEvents) this.watermark[this._localSource] = this.guardEvents.seq;
|
|
2739
2728
|
this._tickSeq = 0;
|
|
2740
2729
|
}
|
|
2741
|
-
//
|
|
2730
|
+
// 内部告警出口:经可选 logger,绝不抛(失败路径上的最后一环)。
|
|
2742
2731
|
_log(level, msg) {
|
|
2743
2732
|
try {
|
|
2744
2733
|
const lg = this.logger;
|
|
@@ -2787,7 +2776,7 @@ var require_hub = __commonJS({
|
|
|
2787
2776
|
}
|
|
2788
2777
|
return lastOkSeq;
|
|
2789
2778
|
}
|
|
2790
|
-
//
|
|
2779
|
+
// 推模式:守卫 Events.append 已同步调此,守卫事件零延迟入聚合流。
|
|
2791
2780
|
pushGuard(rec) {
|
|
2792
2781
|
if (!rec || typeof rec.seq !== "number") return;
|
|
2793
2782
|
if (rec.source === "guard-hub") return;
|
|
@@ -2878,7 +2867,7 @@ var require_hub = __commonJS({
|
|
|
2878
2867
|
get seq() {
|
|
2879
2868
|
return this.writer.seq;
|
|
2880
2869
|
}
|
|
2881
|
-
// /logs/tail
|
|
2870
|
+
// /logs/tail:dsh/upgrade 为预设流;守卫与业务流按注册源装配短键解析。
|
|
2882
2871
|
tailLog(stream, n) {
|
|
2883
2872
|
const fixed = { dsh: this.dshLogFile, upgrade: this.upgradeLogFile };
|
|
2884
2873
|
if (stream in fixed) return tailFile(fixed[stream], n);
|
|
@@ -3466,9 +3455,9 @@ var require_alloc = __commonJS({
|
|
|
3466
3455
|
}
|
|
3467
3456
|
}
|
|
3468
3457
|
}
|
|
3469
|
-
/** 跨进程分配锁(best-effort
|
|
3470
|
-
*
|
|
3471
|
-
*
|
|
3458
|
+
/** 跨进程分配锁(best-effort)。返回释放函数;获取超时返回 null 并继续(fail-open 有界:
|
|
3459
|
+
* 登记后的「二次确认、被抢即撤销」复检是第二层防线,且 ports.json 读路径 reload 使外部
|
|
3460
|
+
* 分配可见 —— 宁可退化到复检层,也不让锁 IO 故障冻结分配)。 */
|
|
3472
3461
|
async _acquireXLock() {
|
|
3473
3462
|
const f = String(this._registry._file || "") + ".alloc.lock";
|
|
3474
3463
|
const deadline = Date.now() + XLOCK_TIMEOUT_MS;
|
|
@@ -3511,7 +3500,7 @@ var require_alloc = __commonJS({
|
|
|
3511
3500
|
}
|
|
3512
3501
|
}
|
|
3513
3502
|
}
|
|
3514
|
-
/** 登记 + 有界复检(
|
|
3503
|
+
/** 登记 + 有界复检(TOCTOU):登记后端口若立刻被外部占用(bind 探测与登记间隙被抢),
|
|
3515
3504
|
* 撤销登记并返回 false —— 宁可让调用方换下一个端口,也不留下必然失败的占用登记。
|
|
3516
3505
|
* self-listening 等「自己就在监听」的登记走 _register,不经本复检。 */
|
|
3517
3506
|
async _confirmRegister(port, rangeKey, owner) {
|
|
@@ -3684,7 +3673,7 @@ var require_pool = __commonJS({
|
|
|
3684
3673
|
if (pools && typeof pools === "object") this._pools = Object.assign({}, core.DEFAULT_POOLS, pools);
|
|
3685
3674
|
return this._pools;
|
|
3686
3675
|
}
|
|
3687
|
-
/**
|
|
3676
|
+
/** 实例侧注册接口(委托 core 模块级函数);段名/池名是域知识,不在本平台模块硬编码(DS-G4)。 */
|
|
3688
3677
|
registerSegment(role, pool) {
|
|
3689
3678
|
core.registerSegment(role, pool);
|
|
3690
3679
|
return this;
|
|
@@ -3759,8 +3748,7 @@ var require_pool = __commonJS({
|
|
|
3759
3748
|
}
|
|
3760
3749
|
if (removed) this._save();
|
|
3761
3750
|
}
|
|
3762
|
-
/** 释放端口:不传 ownerId 按端口号;传了则仅当登记 owner
|
|
3763
|
-
* 注意空值检查必须在 owner 比较之前(旧实现顺序反了会抛 TypeError)。
|
|
3751
|
+
/** 释放端口:不传 ownerId 按端口号;传了则仅当登记 owner 匹配才释放。空值检查必须先于 owner 比较。
|
|
3764
3752
|
* @returns {boolean} 是否真的释放了一条记录 */
|
|
3765
3753
|
release(port, ownerId) {
|
|
3766
3754
|
const p = Number(port);
|
|
@@ -3787,8 +3775,8 @@ var require_pool = __commonJS({
|
|
|
3787
3775
|
for (const r of this._records.values()) if (r.owner === owner) return r.port;
|
|
3788
3776
|
return null;
|
|
3789
3777
|
}
|
|
3790
|
-
/** 端口是否被占用:已登记
|
|
3791
|
-
*
|
|
3778
|
+
/** 端口是否被占用:已登记 或 本机实际监听;excludeOwner 仅豁免 registry 登记。
|
|
3779
|
+
* 监听探测为双栈回环(127.0.0.1 并 ::1),不漏 IPv6-only 监听者。 */
|
|
3792
3780
|
async isTaken(port, excludeOwner) {
|
|
3793
3781
|
const rec = this._records.get(Number(port));
|
|
3794
3782
|
if (rec && (!excludeOwner || rec.owner !== excludeOwner)) return true;
|
|
@@ -4548,7 +4536,7 @@ var require_watchdog = __commonJS({
|
|
|
4548
4536
|
const r = await shell.restartShell({
|
|
4549
4537
|
exePath: exe,
|
|
4550
4538
|
procPattern,
|
|
4551
|
-
//
|
|
4539
|
+
// 在飞复判:杀旧壳与 spawn 之间有 ~8s 窗口,退出请求可能在窗口内到达。
|
|
4552
4540
|
shouldAbort: typeof o.halted === "function" ? () => o.halted() : void 0
|
|
4553
4541
|
});
|
|
4554
4542
|
if (r && r.ok) {
|
|
@@ -4599,10 +4587,9 @@ var require_capability_profile = __commonJS({
|
|
|
4599
4587
|
"src/platform/os/capability-profile.js"(exports2, module2) {
|
|
4600
4588
|
"use strict";
|
|
4601
4589
|
var linux = {
|
|
4602
|
-
//
|
|
4603
|
-
// sandboxLaunch=能否运行实例舱;sandboxEnforcement=限额由谁执行(cgroup|supervise|none)。
|
|
4590
|
+
// 两维拆分声明:sandboxLaunch = 能否运行实例舱;sandboxEnforcement = 限额由谁执行(cgroup|supervise|none)。
|
|
4604
4591
|
sandboxLaunch: true,
|
|
4605
|
-
// W3
|
|
4592
|
+
// W3:恒可跑舱;有 systemd-run 走 cgroup 硬档,无则落 portable 软档(容器/WSL1)
|
|
4606
4593
|
sandboxEnforcement: "cgroup",
|
|
4607
4594
|
// 期望 cgroup 硬限额;无 systemd-run 实测降 'supervise'(采样式,无内核强制)
|
|
4608
4595
|
pidAdoption: true,
|
|
@@ -4640,8 +4627,8 @@ var require_capability_profile = __commonJS({
|
|
|
4640
4627
|
// LaunchAgent RunAtLoad + KeepAlive
|
|
4641
4628
|
guardSelfHeal: true,
|
|
4642
4629
|
// KeepAlive
|
|
4643
|
-
// 独立 LaunchAgent com.dsh.supervisor.gui(RunAtLoad
|
|
4644
|
-
//
|
|
4630
|
+
// 壳自启 = 独立 LaunchAgent com.dsh.supervisor.gui(RunAtLoad),内核创建/删除;
|
|
4631
|
+
// 守卫 plist 归桌面壳建立,内核只 enable/disable + bootstrap/bootout。
|
|
4645
4632
|
shellAutostart: true,
|
|
4646
4633
|
shellSelfHeal: true
|
|
4647
4634
|
// 守卫看护(三平台一套机制)
|
|
@@ -4653,9 +4640,8 @@ var require_capability_profile = __commonJS({
|
|
|
4653
4640
|
// 采样式限额;Job Object 硬档一期不做不预留(届时以真机数据另立项)
|
|
4654
4641
|
pidAdoption: true,
|
|
4655
4642
|
// netstat
|
|
4656
|
-
// processTreeKill 已接入 _killTree(supervisor 的 SIGKILL 升级路径 + 接管实例路径),
|
|
4657
4643
|
processTreeKill: true,
|
|
4658
|
-
// taskkill /PID /T /F
|
|
4644
|
+
// taskkill /PID /T /F(hasTool 覆写;使用点见 app/main/signals.js 的 _killTree)
|
|
4659
4645
|
desktopNotify: true,
|
|
4660
4646
|
// 期望 powershell(实测覆写)
|
|
4661
4647
|
autostart: true,
|
|
@@ -4663,13 +4649,12 @@ var require_capability_profile = __commonJS({
|
|
|
4663
4649
|
frpExpose: true,
|
|
4664
4650
|
hostService: "windows-service",
|
|
4665
4651
|
guardAutostart: true,
|
|
4666
|
-
// schtasks DSH-Supervisor(ONLOGON
|
|
4652
|
+
// schtasks DSH-Supervisor(ONLOGON,由壳建立)
|
|
4667
4653
|
guardSelfHeal: true,
|
|
4668
|
-
// schtasks DSH-Supervisor-Watchdog 每 5
|
|
4654
|
+
// schtasks DSH-Supervisor-Watchdog 每 5 分钟(同样归壳,内核只查询存在性)
|
|
4669
4655
|
shellAutostart: true,
|
|
4670
|
-
// schtasks DSH-Supervisor-GUI(ONLOGON
|
|
4671
|
-
// watchdog
|
|
4672
|
-
// 前置条件:登录自启已启用(watchdog 任务由 setAutostart 建立),与 guardSelfHeal 同前提。
|
|
4656
|
+
// schtasks DSH-Supervisor-GUI(ONLOGON,内核 setAutostart 创建)
|
|
4657
|
+
// 壳的自愈依赖那条 watchdog 任务,故与 guardSelfHeal 同前提:登录自启未启用时无从检查。
|
|
4673
4658
|
shellSelfHeal: true
|
|
4674
4659
|
};
|
|
4675
4660
|
var unknown = {
|
|
@@ -5003,8 +4988,8 @@ var require_portable = __commonJS({
|
|
|
5003
4988
|
return true;
|
|
5004
4989
|
},
|
|
5005
4990
|
/** 拉起:spawn(detached)——POSIX 自成进程组(kill(-pid) 语义的前提)、win32 windowsHide +
|
|
5006
|
-
* CREATE_NEW_PROCESS_GROUP
|
|
5007
|
-
* props(cgroup
|
|
4991
|
+
* CREATE_NEW_PROCESS_GROUP(NO-CONSOLE-WINDOW 纪律收口在 platform/os/spawn.js)。
|
|
4992
|
+
* 调用方传来的 props(cgroup 语义)在此如实忽略:本档无内核强制可施加。
|
|
5008
4993
|
* @param {{cmd:string[], env?:object, workingDir?:string, pidFile?:string, port?:number, anchors?:string[]}} o */
|
|
5009
4994
|
startTransient(o) {
|
|
5010
4995
|
const opts = o || {};
|
|
@@ -5069,12 +5054,10 @@ var require_portable = __commonJS({
|
|
|
5069
5054
|
}
|
|
5070
5055
|
return false;
|
|
5071
5056
|
},
|
|
5072
|
-
/**
|
|
5073
|
-
*
|
|
5074
|
-
*
|
|
5075
|
-
*
|
|
5076
|
-
* 判定可宽,动手必严。存活 pid 的 cmdline 读取失败 = 无法区分「他人复用」与「查询失败」
|
|
5077
|
-
* -> null(与 FIX-5 同一保守方向)。 */
|
|
5057
|
+
/** 活跃判定(三态):port 与 pidFile 都缺 = 无从查询 -> null,删除保护路径不得把它当「已停止」。
|
|
5058
|
+
* anchors 为空时降级为「端口有监听即活跃」(monitor.probeInstance 同口径,该调用方本就以此判在线);
|
|
5059
|
+
* 杀进程(stopUnit)仍要求锚点命中 —— 判定可宽,动手必严。
|
|
5060
|
+
* 存活 pid 的 cmdline 读不到 = 分不清「他人复用」与「查询失败」-> null。 */
|
|
5078
5061
|
isUnitActive(unit, o) {
|
|
5079
5062
|
const opts = o || {};
|
|
5080
5063
|
if (findOurs(opts)) return true;
|
|
@@ -5101,8 +5084,7 @@ var require_portable = __commonJS({
|
|
|
5101
5084
|
transientUnitFile() {
|
|
5102
5085
|
return null;
|
|
5103
5086
|
},
|
|
5104
|
-
/**
|
|
5105
|
-
* 再清 run.pid。返回形态与 systemd 档一致({ok, errors}),调用方无需分支。 */
|
|
5087
|
+
/** 清理同名残留:先停掉仍活着的旧进程(重启自愈路径:旧进程未监听端口时端口探测拦不住),再清 run.pid。 */
|
|
5106
5088
|
cleanTransient(unit, o) {
|
|
5107
5089
|
const opts = o || {};
|
|
5108
5090
|
const errors = [];
|
|
@@ -5110,8 +5092,7 @@ var require_portable = __commonJS({
|
|
|
5110
5092
|
removePidFile(opts);
|
|
5111
5093
|
return { ok: errors.length === 0, errors };
|
|
5112
5094
|
},
|
|
5113
|
-
/**
|
|
5114
|
-
* governor 不因 false 走任何降级分支——违规处置链本就平台无关。 */
|
|
5095
|
+
/** 返回值只表示「是否改动了内核强制」;governor 不因 false 走降级分支 —— 违规处置链本就平台无关。 */
|
|
5115
5096
|
setLimits() {
|
|
5116
5097
|
return false;
|
|
5117
5098
|
}
|
|
@@ -5166,15 +5147,14 @@ var require_service = __commonJS({
|
|
|
5166
5147
|
return false;
|
|
5167
5148
|
}
|
|
5168
5149
|
},
|
|
5169
|
-
// run() 失败返回 null
|
|
5150
|
+
// run() 失败返回 null 而不抛,try/catch 包裹会恒返回 true;如实回传成败。
|
|
5170
5151
|
resetFailed(unit) {
|
|
5171
5152
|
if (unitNameViolation(unit)) return false;
|
|
5172
5153
|
return run("systemctl", ["--user", "reset-failed", unit], { timeoutMs: 1e4 }) !== null;
|
|
5173
5154
|
},
|
|
5174
5155
|
/** 单元活跃判定(三态):确认 active -> true;确认不活跃 -> false;查询未完成 -> null(未知)。
|
|
5175
|
-
*
|
|
5176
|
-
*
|
|
5177
|
-
* 沙箱数据(FIX-5 A 的根因)。调用方按「!== false 才放行删除」消费本函数。 */
|
|
5156
|
+
* run() 失败只返回 null,「查询未完成」不可折成 false:instance/ops.js 删除数据目录前
|
|
5157
|
+
* 按「!== false 才放行删除」消费本函数,折假即超时窗口内误删沙箱数据。 */
|
|
5178
5158
|
isUnitActive(unit) {
|
|
5179
5159
|
if (!unit) return true;
|
|
5180
5160
|
if (unitNameViolation(unit)) return false;
|
|
@@ -5201,9 +5181,8 @@ var require_service = __commonJS({
|
|
|
5201
5181
|
},
|
|
5202
5182
|
/** 清理 stale transient 单元:stop/reset-failed/删单元文件/daemon-reload。
|
|
5203
5183
|
* 必须 reload:删除文件后 systemd 仍缓存该单元为 loaded,否则 systemd-run 拒绝重建同名单元。
|
|
5204
|
-
* 返回 {ok, errors}
|
|
5205
|
-
*
|
|
5206
|
-
* 退出属正常,不计入 ok;真正的硬失败只有删单元文件与 daemon-reload。 */
|
|
5184
|
+
* 返回 {ok, errors}:stop/reset-failed 对「从未加载的单元」非零退出属正常,不计入 ok;
|
|
5185
|
+
* 硬失败只有删单元文件与 daemon-reload(run() 失败返回 null 不抛,成败须如实回传)。 */
|
|
5207
5186
|
cleanTransient(unit) {
|
|
5208
5187
|
const errors = [];
|
|
5209
5188
|
const bad = unitNameViolation(unit);
|
|
@@ -5239,8 +5218,7 @@ var require_service = __commonJS({
|
|
|
5239
5218
|
return true;
|
|
5240
5219
|
},
|
|
5241
5220
|
/** 运行期改限额(W3 动态化):systemctl --user set-property 立即生效,无需重启单元。
|
|
5242
|
-
* 必带 --runtime:transient 单元本不落盘,不带它会把 drop-in
|
|
5243
|
-
* 与「每次启动按当时拓扑重算」的 governor 语义失同步(陈旧下限永久黏住)。
|
|
5221
|
+
* 必带 --runtime:transient 单元本不落盘,不带它会把 drop-in 写进用户配置目录,陈旧下限永久黏住。
|
|
5244
5222
|
* 值只来自 sandbox.unitProps 同源的 alloc(平台层翻译语义、不决定数额)。 */
|
|
5245
5223
|
setLimits(unit, alloc) {
|
|
5246
5224
|
if (unitNameViolation(unit)) return false;
|
|
@@ -5277,7 +5255,7 @@ var require_service = __commonJS({
|
|
|
5277
5255
|
transientUnitFile() {
|
|
5278
5256
|
return null;
|
|
5279
5257
|
},
|
|
5280
|
-
// 无 transient 单元可清 = 成功;返回形态与 systemd
|
|
5258
|
+
// 无 transient 单元可清 = 成功;返回形态与 systemd 一致,调用方无需分支。
|
|
5281
5259
|
cleanTransient() {
|
|
5282
5260
|
return { ok: true, errors: [] };
|
|
5283
5261
|
},
|
|
@@ -6108,17 +6086,17 @@ var require_os = __commonJS({
|
|
|
6108
6086
|
processControl: require_process(),
|
|
6109
6087
|
pidlookup: require_pidlookup(),
|
|
6110
6088
|
execPath,
|
|
6111
|
-
//
|
|
6089
|
+
// 跨平台可执行解析
|
|
6112
6090
|
fileProtect: require_file_protect(),
|
|
6113
6091
|
// 跨平台文件保护(Unix chmod / Windows icacls)
|
|
6114
6092
|
service: require_service(),
|
|
6115
6093
|
// 服务管理器抽象(Provider 分派)
|
|
6116
|
-
// notify
|
|
6117
|
-
//
|
|
6094
|
+
// notify 必须是直接可调函数:调用方按 platform.notify(title, body, onError) 用,
|
|
6095
|
+
// 导出模块对象会抛 platform.notify is not a function,把升级终态误判成失败。
|
|
6118
6096
|
notify: require_notify().notify,
|
|
6119
6097
|
browser: require_browser(),
|
|
6120
6098
|
desktop: require_desktop(),
|
|
6121
|
-
// 图形会话可用性(Linux
|
|
6099
|
+
// 图形会话可用性(Linux 需实测 socket)
|
|
6122
6100
|
autostart: require_autostart()
|
|
6123
6101
|
};
|
|
6124
6102
|
}
|
|
@@ -6274,14 +6252,12 @@ var require_bootstrap = __commonJS({
|
|
|
6274
6252
|
logger: host2.logger,
|
|
6275
6253
|
events: host2.events,
|
|
6276
6254
|
config: host2.config,
|
|
6277
|
-
//
|
|
6278
|
-
//
|
|
6279
|
-
//
|
|
6280
|
-
// (9-18:退出管家后守卫重启不得把壳拉回);主 DSH 收敛用不含 shellHalted 的 _exitIntended。
|
|
6255
|
+
// 退出门下沉到看护域:tick() 的一切调用者受同一门约束,合取式收敛为单源谓词
|
|
6256
|
+
// host._shellExitIntended()(通用退出 或 持久 _shellHalted)。壳看护属桌面壳域,
|
|
6257
|
+
// 须含 shellHalted(退出管家后守卫重启不得把壳拉回);主 DSH 收敛用 _exitIntended。
|
|
6281
6258
|
halted: () => host2._shellExitIntended(),
|
|
6282
6259
|
// 壳已在线 = 用户重新打开了壳 -> 清除持久退出标记(否则自愈被永久抑制)。
|
|
6283
|
-
//
|
|
6284
|
-
// 持久标记被写成 false,守卫重启后看护又把壳拉回(本修的核心场景)。
|
|
6260
|
+
// 只在非退出中清:退出握手期间壳还会存活数百 ms,此时误清会让守卫重启后又把壳拉回。
|
|
6285
6261
|
onShellAlive: () => {
|
|
6286
6262
|
if (!host2._shellHalted) return;
|
|
6287
6263
|
if (host2._stopping) return;
|
|
@@ -7118,7 +7094,7 @@ var require_runtime = __commonJS({
|
|
|
7118
7094
|
npmPath: j.npmPath || npm.path || null,
|
|
7119
7095
|
// 外壳可只提供包内 JS(npmPath=node,npmArgs=[npm-cli.js]),消费者必须带上 args。
|
|
7120
7096
|
npmArgs: Array.isArray(j.npmArgs) ? j.npmArgs : Array.isArray(npm.args) ? npm.args : [],
|
|
7121
|
-
// npm
|
|
7097
|
+
// npm 版本由壳真实执行 npm --version 得到;旧壳无此键时为 null(未知就是未知)。
|
|
7122
7098
|
npmVersion: typeof npm.version === "string" && npm.version || null,
|
|
7123
7099
|
minNode: j.minNode || null,
|
|
7124
7100
|
writtenBy: j.writtenBy || null,
|
|
@@ -7483,15 +7459,14 @@ var require_distribution = __commonJS({
|
|
|
7483
7459
|
DistributionManager,
|
|
7484
7460
|
semverCompare,
|
|
7485
7461
|
VERSION_RE,
|
|
7486
|
-
// 选版算法(release.js 唯一实现)+ 包归属判定 + rollback 防降级下限常量(RC-7
|
|
7462
|
+
// 选版算法(release.js 唯一实现)+ 包归属判定 + rollback 防降级下限常量(RC-7)
|
|
7487
7463
|
pickReleaseVersion: release.pickReleaseVersion,
|
|
7488
7464
|
isOurReleasePackage: release.isOurReleasePackage,
|
|
7489
7465
|
OUR_RELEASE_SCOPE: release.OUR_RELEASE_SCOPE,
|
|
7490
7466
|
ROLLBACK_FLOOR_VERSION: release.ROLLBACK_FLOOR_VERSION,
|
|
7491
7467
|
ROLLBACK_MAX_AGE_DAYS: release.ROLLBACK_MAX_AGE_DAYS,
|
|
7492
|
-
// 在途 npm 中止出口(D-10):句柄登记在 install.js
|
|
7493
|
-
//
|
|
7494
|
-
// 的调用方(app/native/installer.js 经 host.dist 走门面,但测试与未来 daemon 可直用)。
|
|
7468
|
+
// 在途 npm 中止出口(D-10):句柄登记在 install.js 的模块级集合(跨实例、覆盖全部调用路径),
|
|
7469
|
+
// 故门面按静态导出而非实例方法——挂实例会漏掉直接 require('./install') 的调用方。
|
|
7495
7470
|
killInflightNpm: install.killInflightNpm,
|
|
7496
7471
|
inflightNpmCount: install.inflightNpmCount
|
|
7497
7472
|
};
|
|
@@ -7728,9 +7703,9 @@ var require_scheduler = __commonJS({
|
|
|
7728
7703
|
}
|
|
7729
7704
|
module2.exports = {
|
|
7730
7705
|
methods: {
|
|
7731
|
-
/** tick 保留为 _dshConverge
|
|
7732
|
-
*
|
|
7733
|
-
*
|
|
7706
|
+
/** tick 保留为 _dshConverge 别名:外部收敛触发点(start 首拍 / setDesired / requestRestart /
|
|
7707
|
+
* _exitUpgradeHold)调用;shadow 模式的定时器也驱动此别名。on 模式下 main 每拍收敛由
|
|
7708
|
+
* heartbeat 的 dsh supervise 直接调 _dshConverge(无独立 tick 定时器)。 */
|
|
7734
7709
|
async tick() {
|
|
7735
7710
|
const d = depsOf(this);
|
|
7736
7711
|
return d.main().converge();
|
|
@@ -7791,7 +7766,7 @@ var require_instance_adapter = __commonJS({
|
|
|
7791
7766
|
logger: () => host2.logger,
|
|
7792
7767
|
control: () => host2.control,
|
|
7793
7768
|
managedObjects: () => host2.managedObjects,
|
|
7794
|
-
// 同模块兄弟方法经 host 上的既有安装转发(等价于原经 this
|
|
7769
|
+
// 同模块兄弟方法经 host 上的既有安装转发(等价于原经 this 的调用)
|
|
7795
7770
|
syncSandboxRegistryEntry: (entry) => host2._syncSandboxRegistryEntry(entry)
|
|
7796
7771
|
};
|
|
7797
7772
|
DEPS.set(host2, d);
|
|
@@ -7800,11 +7775,10 @@ var require_instance_adapter = __commonJS({
|
|
|
7800
7775
|
}
|
|
7801
7776
|
module2.exports = {
|
|
7802
7777
|
methods: {
|
|
7803
|
-
/**
|
|
7804
|
-
*
|
|
7805
|
-
*
|
|
7806
|
-
*
|
|
7807
|
-
* @returns {ok:boolean} 实例当前在线(heartbeat 统一写目录 lastObserved) */
|
|
7778
|
+
/** 沙箱实例监督单拍:heartbeat 经 adapter 对每个沙箱实例跑 InstanceManager.supervise
|
|
7779
|
+
* (单实例状态机)并把目录项与实例域状态对齐(目录=真实视图,防 ghost/死登记)。
|
|
7780
|
+
* 域业务 CRUD/安装/装配/systemd/持久化保留在 InstanceManager,本方法只做心跳驱动+目录同步;
|
|
7781
|
+
* 返回 ok=实例当前在线(heartbeat 统一写目录 lastObserved)。 */
|
|
7808
7782
|
async _sandboxSuperviseOnce(entry) {
|
|
7809
7783
|
const d = depsOf(this);
|
|
7810
7784
|
if (d.stopping()) return { ok: false, error: "guard stopping" };
|
|
@@ -7832,10 +7806,9 @@ var require_instance_adapter = __commonJS({
|
|
|
7832
7806
|
}
|
|
7833
7807
|
return { ok: running, error: running ? null : "\u6C99\u7BB1\u5B9E\u4F8B\u672A\u8FD0\u884C" };
|
|
7834
7808
|
},
|
|
7835
|
-
/** 目录项 <-
|
|
7836
|
-
*
|
|
7837
|
-
*
|
|
7838
|
-
* 误判成「用户想停它」。意图只由 entry 的 start()/stop() 与动作路径改。 */
|
|
7809
|
+
/** 目录项 <- 实例域状态对齐(监督拍后调用):实例已删 -> 注销(防死登记);存在 -> 经
|
|
7810
|
+
* sandboxSpec 同步 name/guardian/ownership + phase 落目录词表。desired 不同步(keepDesired,
|
|
7811
|
+
* 契约 M-1:观测推导不得写回应然,崩溃进 BACKOFF 不等于「用户想停」)。 */
|
|
7839
7812
|
_syncSandboxRegistryEntry(entry) {
|
|
7840
7813
|
const d = depsOf(this);
|
|
7841
7814
|
if (!entry || !d.managedObjects() || !d.instances()) return;
|
|
@@ -7888,7 +7861,7 @@ var require_decide = __commonJS({
|
|
|
7888
7861
|
crashHalted() {
|
|
7889
7862
|
return host2._crashHalted;
|
|
7890
7863
|
},
|
|
7891
|
-
// 字段 helper 与状态读取经 host
|
|
7864
|
+
// 字段 helper 与状态读取经 host 既有安装转发。
|
|
7892
7865
|
mLastProbeOk() {
|
|
7893
7866
|
return host2._mLastProbeOk();
|
|
7894
7867
|
},
|
|
@@ -8169,7 +8142,7 @@ var require_controller = __commonJS({
|
|
|
8169
8142
|
sessionState() {
|
|
8170
8143
|
return host2._sessionState;
|
|
8171
8144
|
},
|
|
8172
|
-
// 字段 helper 经 host
|
|
8145
|
+
// 字段 helper 经 host 既有安装转发。
|
|
8173
8146
|
mChild() {
|
|
8174
8147
|
return host2._mChild();
|
|
8175
8148
|
},
|
|
@@ -8225,8 +8198,8 @@ var require_controller = __commonJS({
|
|
|
8225
8198
|
}
|
|
8226
8199
|
module2.exports = {
|
|
8227
8200
|
methods: {
|
|
8228
|
-
//
|
|
8229
|
-
//
|
|
8201
|
+
// 唯一心跳驱动的收敛段(外部操作仍可即时触发);单一状态机 STOPPED/STARTING/RUNNING/RESTARTING/BACKOFF。
|
|
8202
|
+
// main 由守卫统一 spawn/观测管理(不经 systemd 托管)。
|
|
8230
8203
|
async _dshConverge() {
|
|
8231
8204
|
const d = depsOf(this);
|
|
8232
8205
|
if (d.readTicking() || d.stopping()) return;
|
|
@@ -8444,7 +8417,7 @@ var require_shadow = __commonJS({
|
|
|
8444
8417
|
stopping() {
|
|
8445
8418
|
return host2._stopping;
|
|
8446
8419
|
},
|
|
8447
|
-
// 兄弟方法/字段 helper 经 host
|
|
8420
|
+
// 兄弟方法/字段 helper 经 host 既有安装转发。
|
|
8448
8421
|
mAdopted() {
|
|
8449
8422
|
return host2._mAdopted();
|
|
8450
8423
|
},
|
|
@@ -8673,7 +8646,7 @@ var require_log = __commonJS({
|
|
|
8673
8646
|
return 0;
|
|
8674
8647
|
}
|
|
8675
8648
|
}
|
|
8676
|
-
// 读取日志尾部至多 n
|
|
8649
|
+
// 读取日志尾部至多 n 行(空行省略;文件不存在返回空数组)。
|
|
8677
8650
|
tail(n) {
|
|
8678
8651
|
if (!this.file) return [];
|
|
8679
8652
|
try {
|
|
@@ -9157,7 +9130,7 @@ var require_signals = __commonJS({
|
|
|
9157
9130
|
logger() {
|
|
9158
9131
|
return host2.logger;
|
|
9159
9132
|
},
|
|
9160
|
-
// 兄弟方法经 host
|
|
9133
|
+
// 兄弟方法经 host 既有安装转发。
|
|
9161
9134
|
mainOwnerFile() {
|
|
9162
9135
|
return host2._mainOwnerFile();
|
|
9163
9136
|
},
|
|
@@ -9195,10 +9168,8 @@ var require_signals = __commonJS({
|
|
|
9195
9168
|
}
|
|
9196
9169
|
module2.exports = {
|
|
9197
9170
|
methods: {
|
|
9198
|
-
/** 主 DSH
|
|
9199
|
-
*
|
|
9200
|
-
* 相似(`_isManagedProcess`),于是**两个守卫会认领同一个 DSH**——彼此 stop/kill 对方刚
|
|
9201
|
-
* 接管的进程(审计原述「疑似双管家互杀」)。 */
|
|
9171
|
+
/** 主 DSH 归属凭据文件:与 lan/router daemon 的 *-daemon.identity.json 同范式、同址(stateFile 目录)。
|
|
9172
|
+
* 接管只凭 cmdline 子串相似会让两个守卫认领同一个 DSH、互 stop/kill 对方刚接管的进程,故落凭据声明归属。 */
|
|
9202
9173
|
_mainOwnerFile() {
|
|
9203
9174
|
const d = depsOf(this);
|
|
9204
9175
|
try {
|
|
@@ -9207,7 +9178,7 @@ var require_signals = __commonJS({
|
|
|
9207
9178
|
return null;
|
|
9208
9179
|
}
|
|
9209
9180
|
},
|
|
9210
|
-
/** 读归属凭据;缺失/损坏一律 null
|
|
9181
|
+
/** 读归属凭据;缺失/损坏一律 null(绝不因读失败而接管或否决,判定回落 cmdline)。 */
|
|
9211
9182
|
_readMainOwner() {
|
|
9212
9183
|
const d = depsOf(this);
|
|
9213
9184
|
try {
|
|
@@ -9219,10 +9190,8 @@ var require_signals = __commonJS({
|
|
|
9219
9190
|
return null;
|
|
9220
9191
|
}
|
|
9221
9192
|
},
|
|
9222
|
-
/**
|
|
9223
|
-
*
|
|
9224
|
-
* writeAtomic:tmp 名含 pid+时间戳,多实例并发不互相覆盖,rename 原子替换)+ 0600
|
|
9225
|
-
* ——与 daemon 身份文件同法。 */
|
|
9193
|
+
/** 落归属凭据:声明 pid=<dshPid> 的主 DSH 由本守卫(guardPid)负责;spawn 与 adopt 两条取得所有权的路线都要写。
|
|
9194
|
+
* 落盘走 writeAtomic 单源(tmp 名含 pid+时间戳,多实例并发不互相覆盖)+ 0600,与 daemon 身份文件同法。 */
|
|
9226
9195
|
_writeMainOwner(dshPid, port) {
|
|
9227
9196
|
const d = depsOf(this);
|
|
9228
9197
|
try {
|
|
@@ -9238,12 +9207,9 @@ var require_signals = __commonJS({
|
|
|
9238
9207
|
d.logger() && d.logger().warn && d.logger().warn("writeMainOwner: " + (e && e.message || e));
|
|
9239
9208
|
}
|
|
9240
9209
|
},
|
|
9241
|
-
/** 校验 pid
|
|
9242
|
-
* 精确匹配避免"路径碰巧含 dsh
|
|
9243
|
-
*
|
|
9244
|
-
* 先看**归属凭据**——但凭据只做**否决**(别的守卫活着且明确拥有这个 pid 时不接管),
|
|
9245
|
-
* 不单独放行:放行权威仍是 cmdline 特征。理由——陈旧凭据(pid 已被内核复用)若可单独放行,
|
|
9246
|
-
* 会把无关进程接管进来,那是比原缺陷更糟的失败方向。 */
|
|
9210
|
+
/** 校验 pid 是否属本守卫:cmdline 含配置启动 bin 或符合 DSH 特征(兼容外部手动起的标准 DSH);
|
|
9211
|
+
* 精确匹配避免"路径碰巧含 dsh 误接管"与"安装路径不含 dsh 漏接管"。
|
|
9212
|
+
* 归属凭据只做否决(别的存活守卫拥有此 pid 时不接管),不单独放行:陈旧凭据的 pid 可能已被内核复用,单独放行会误接管无关进程。 */
|
|
9247
9213
|
_isManagedProcess(pid) {
|
|
9248
9214
|
const d = depsOf(this);
|
|
9249
9215
|
const own = d.readMainOwner();
|
|
@@ -9259,22 +9225,14 @@ var require_signals = __commonJS({
|
|
|
9259
9225
|
if (typeof bin === "string" && bin && cmd2.includes(bin)) return true;
|
|
9260
9226
|
return pidlook.isDshCmdline(pid);
|
|
9261
9227
|
},
|
|
9262
|
-
/** 向进程组发信号(detached spawn
|
|
9263
|
-
* POSIX 组信号;Windows 无组语义则单进程信号,树语义由
|
|
9228
|
+
/** 向进程组发信号(detached spawn 的子进程是组长);组信号失败退回单进程。平台层封装:
|
|
9229
|
+
* POSIX 组信号;Windows 无组语义则单进程信号,树语义由 killTree 提供。 */
|
|
9264
9230
|
_signalChild(child, sig) {
|
|
9265
9231
|
platform.processControl.signalProcess(child.pid, sig);
|
|
9266
9232
|
},
|
|
9267
|
-
/**
|
|
9268
|
-
*
|
|
9269
|
-
*
|
|
9270
|
-
* 平台层提供 `killTree`(Windows = `taskkill /PID <pid> /T /F`,POSIX = 进程组信号),
|
|
9271
|
-
* 实际停止路径若只用 `signalProcess`,它在 Windows 上只 `process.kill(pid, sig)`(单进程语义由平台层注释自己写明)。
|
|
9272
|
-
* 于是 Windows 上停止 DSH 只杀父进程:其派生的子进程(node / 浏览器 / 子命令)成为**孤儿**,
|
|
9273
|
-
* 继续占端口、持文件锁;守卫重启后 adopt 复用即被楔死。
|
|
9274
|
-
*
|
|
9275
|
-
* POSIX 上 `killTree` 退化为组信号,与 `_signalChild` 等价(幂等,无害)。
|
|
9276
|
-
* child 一律是本守卫 detached 拉起(组长),显式 ownGroup:true 保留组信号。
|
|
9277
|
-
*/
|
|
9233
|
+
/** 整树终止:必须单独存在。平台层 killTree(Windows taskkill /PID <pid> /T /F,POSIX 进程组信号)
|
|
9234
|
+
* 与 signalProcess 不同,后者在 Windows 只杀单进程——停止 DSH 会遗留子进程成孤儿、继续占端口与文件锁,守卫重启 adopt 即被楔死。
|
|
9235
|
+
* child 恒为本守卫 detached 拉起的组长,故显式 ownGroup:true 走组信号(POSIX 退化为组信号,幂等无害)。 */
|
|
9278
9236
|
_killTree(child, sig) {
|
|
9279
9237
|
const d = depsOf(this);
|
|
9280
9238
|
const pc = platform.processControl;
|
|
@@ -9297,16 +9255,10 @@ var require_signals = __commonJS({
|
|
|
9297
9255
|
}
|
|
9298
9256
|
}, d.config().stopGraceMs));
|
|
9299
9257
|
},
|
|
9300
|
-
/** 杀无句柄的接管实例(仅知 pid
|
|
9301
|
-
*
|
|
9302
|
-
*
|
|
9303
|
-
*
|
|
9304
|
-
*
|
|
9305
|
-
* 代际(gen):本文件只有一个 _adoptKillTimer 槽位,后一次 kill 会覆盖前一次的句柄。
|
|
9306
|
-
* 故每次调用自增 _adoptKillGen,定时器只在「本代仍是当前代」时才把槽位置回 null,
|
|
9307
|
-
* 防止旧 timer 清掉新一次操作的句柄(清掉后 shutdown 就漏清它)。
|
|
9308
|
-
* 注意代际只保护共享槽位:每个 timer 仍按自己的 pid 完成升级与复核,
|
|
9309
|
-
* 不因换代而跳过,否则前一个 pid 的 SIGKILL 升级会被吞掉。 */
|
|
9258
|
+
/** 杀无句柄的接管实例(仅知 pid)。失败不再静默:SIGKILL 后另给复核窗口,仍存活才判「停止落空」发 stop_failed + warn。
|
|
9259
|
+
* 代际 gen:本文件只有一个 _adoptKillTimer 槽位,后一次 kill 会覆盖前一次句柄;故每次自增 _adoptKillGen,
|
|
9260
|
+
* 定时器只在「本代仍是当前代」时置空槽位,防旧 timer 清掉新句柄。
|
|
9261
|
+
* 代际只保护共享槽位:每个 timer 仍按自己的 pid 完成升级与复核,不因换代跳过(否则前一个 pid 的 SIGKILL 升级被吞)。 */
|
|
9310
9262
|
_killAdopted(pid) {
|
|
9311
9263
|
const d = depsOf(this);
|
|
9312
9264
|
const gen = (d.readAdoptKillGen() || 0) + 1;
|
|
@@ -9409,7 +9361,7 @@ var require_health_gate = __commonJS({
|
|
|
9409
9361
|
ui() {
|
|
9410
9362
|
return host2.ui;
|
|
9411
9363
|
},
|
|
9412
|
-
// 字段 helper 经 host
|
|
9364
|
+
// 字段 helper 经 host 既有安装转发。
|
|
9413
9365
|
mCrashWindowStart() {
|
|
9414
9366
|
return host2._mCrashWindowStart();
|
|
9415
9367
|
},
|
|
@@ -9466,13 +9418,9 @@ var require_health_gate = __commonJS({
|
|
|
9466
9418
|
d.ui().notify("DSH \u53CD\u590D\u5D29\u6E83", "\u5DF2\u8FDB\u5165\u7B2C " + dec.backoffLevel + " \u7EA7\u9000\u907F\uFF08" + Math.round(d.config().backoff[dec.backoffLevel] / 1e3) + "s\uFF09\uFF0C\u8BF7\u67E5\u770B dsh-supervisor \u9762\u677F");
|
|
9467
9419
|
}
|
|
9468
9420
|
},
|
|
9469
|
-
/**
|
|
9470
|
-
* 单次抖动不清零(failStreak 单调累积直到达到阈值或恢复健康),达到阈值即触发。
|
|
9421
|
+
/** 假死识别(健康维度):进程/端口在但 HTTP 连续 failThreshold 次不健康才判故障重启;单次抖动不清零(failStreak 单调累积至阈值或恢复)。
|
|
9471
9422
|
* httpProbeEnabled=false 时 healthOk 恒为 true(monitor.probe 已退化),此处天然不触发。
|
|
9472
|
-
*
|
|
9473
|
-
* 本方法只记账 + 返回决策,不直接调 main.beginRestart()(否则造成
|
|
9474
|
-
* health-gate -> process 反向边);执行由收敛器(main/controller.js)承担。
|
|
9475
|
-
* 依赖单向:controller/process -> health-gate。 */
|
|
9423
|
+
* 只记账 + 返回决策,不直接调 main.beginRestart(避免 health-gate -> process 反向边);执行由收敛器 controller 承担。 */
|
|
9476
9424
|
_applyHealthCheck(healthOk) {
|
|
9477
9425
|
const d = depsOf(this);
|
|
9478
9426
|
if (healthOk) {
|
|
@@ -9521,12 +9469,10 @@ var require_supervise = __commonJS({
|
|
|
9521
9469
|
}
|
|
9522
9470
|
module2.exports = {
|
|
9523
9471
|
methods: {
|
|
9524
|
-
/** 唯一心跳驱动的 daemon 保活单拍:
|
|
9525
|
-
*
|
|
9526
|
-
*
|
|
9527
|
-
*
|
|
9528
|
-
* 故两分支均不写 guardian_action / restartCount(G-1/G-2)。
|
|
9529
|
-
* @returns {ok:boolean} daemon 当前在线(heartbeat 统一写入目录实然)。 */
|
|
9472
|
+
/** 唯一心跳驱动的 daemon 保活单拍:router/lan-daemon「业务需要它 + 失联即拉起」,由 ManagedRegistry.heartbeat
|
|
9473
|
+
* 经 adapter 调用(节流约 30s);守卫重启不影响 daemon(进程独立)。
|
|
9474
|
+
* 契约 域 B:两 daemon 是基础设施(能力自愈),本方法是保活而非守护触发用户意图,两分支均不写
|
|
9475
|
+
* guardian_action / restartCount(G-1/G-2)。@returns {ok:boolean} daemon 当前在线(heartbeat 统一写入目录实然)。 */
|
|
9530
9476
|
async _daemonSuperviseOnce(kind) {
|
|
9531
9477
|
const d = depsOf(this);
|
|
9532
9478
|
if (d.stopping()) return { ok: false };
|
|
@@ -9685,16 +9631,9 @@ var require_process3 = __commonJS({
|
|
|
9685
9631
|
var { waitProcessExit, waitPortFree } = require_process_wait();
|
|
9686
9632
|
var { deriveCmdMarks } = require_process_marks();
|
|
9687
9633
|
var DaemonLifecycle = class {
|
|
9688
|
-
/**
|
|
9689
|
-
*
|
|
9690
|
-
*
|
|
9691
|
-
* - script / args: spawn 命令
|
|
9692
|
-
* - ctlPort: 就绪/换代仲裁端口
|
|
9693
|
-
* - cmdMark: cmdline 识别子串(防误接管异主)
|
|
9694
|
-
* - identityFile: JSON {guardPid, daemonPid, startedAt}
|
|
9695
|
-
* - spawnEnv(extra) / logger / events
|
|
9696
|
-
* - readyTimeoutMs / stopGraceMs / portReleaseTimeoutMs / spawnWindowMs
|
|
9697
|
-
*/
|
|
9634
|
+
/** @param o 配置:name('router' | 'lan')、script/args(spawn 命令)、ctlPort(就绪/换代仲裁端口)、
|
|
9635
|
+
* cmdMark(cmdline 识别子串,防误接管异主)、identityFile(JSON {guardPid, daemonPid, startedAt})、
|
|
9636
|
+
* spawnEnv(extra) / logger / events、readyTimeoutMs / stopGraceMs / portReleaseTimeoutMs / spawnWindowMs。 */
|
|
9698
9637
|
constructor(o) {
|
|
9699
9638
|
this.name = o.name;
|
|
9700
9639
|
this.script = o.script;
|
|
@@ -9768,9 +9707,9 @@ var require_process3 = __commonJS({
|
|
|
9768
9707
|
const id = this._readIdentity();
|
|
9769
9708
|
return id && id.daemonPid ? id.daemonPid : null;
|
|
9770
9709
|
}
|
|
9771
|
-
/** 回收非当前受管代际的旧代进程:YAMA 下 /proc fd
|
|
9772
|
-
*
|
|
9773
|
-
*
|
|
9710
|
+
/** 回收非当前受管代际的旧代进程:YAMA 下 /proc fd 对非祖先不可读、socket 到 pid 无法映射,
|
|
9711
|
+
* 故用 pgrep -af 读 cmdline(跨平台/YAMA 免疫)。命中本 daemon 特征且 pid 非当前身份 pid/自己/
|
|
9712
|
+
* ctl 属主者一律视为旧代残留,TERM 回收。@returns 回收数 */
|
|
9774
9713
|
reclaimOrphans() {
|
|
9775
9714
|
const args = this.args || [];
|
|
9776
9715
|
let cfg = "";
|
|
@@ -9820,11 +9759,8 @@ var require_process3 = __commonJS({
|
|
|
9820
9759
|
}
|
|
9821
9760
|
return killed;
|
|
9822
9761
|
}
|
|
9823
|
-
/**
|
|
9824
|
-
*
|
|
9825
|
-
* - 无进程 -> spawn(latch)-> {mode:'started', pid}
|
|
9826
|
-
* - 期望 pid 死或失联 -> reclaiming(停残留,短 latch 后下一轮 spawn)
|
|
9827
|
-
* - spawn latch 窗口内 -> {mode:'barrier'} */
|
|
9762
|
+
/** 换代/启动仲裁总入口:期望 pid 活 -> {mode:'adopted', pid};无进程 -> spawn(latch)-> {mode:'started', pid};
|
|
9763
|
+
* 期望 pid 死或失联 -> reclaiming(停残留,短 latch 后下一轮 spawn);spawn latch 窗口内 -> {mode:'barrier'}。 */
|
|
9828
9764
|
ensureRunning() {
|
|
9829
9765
|
if (this._stopping) return { mode: "stopping" };
|
|
9830
9766
|
try {
|
|
@@ -9880,16 +9816,9 @@ var require_process3 = __commonJS({
|
|
|
9880
9816
|
if (this.logger && this.logger.info) this.logger.info("[" + this.name + "] \u5DF2\u62C9\u8D77\u72EC\u7ACB daemon pid=" + child.pid + "\uFF08spawn \u7A97\u53E3\u81F3 " + new Date(this._spawnWindowUntil).toISOString() + "\uFF09");
|
|
9881
9817
|
return { mode: "started", pid: child.pid };
|
|
9882
9818
|
}
|
|
9883
|
-
/**
|
|
9884
|
-
*
|
|
9885
|
-
*
|
|
9886
|
-
* - external: ctl 被「异 cmdMark/异代际」进程占用(外部抢占,绝不接管)
|
|
9887
|
-
* - reclaiming: 期望代际已死但同 cmdMark 残留仍占 ctl(需换代)
|
|
9888
|
-
* - barrier: spawn latch 窗口内(等旧代退出)
|
|
9889
|
-
* - absent: 无进程、无残留(可 spawn)
|
|
9890
|
-
* - stopping: 已进入停止流程
|
|
9891
|
-
* 这是生产监督路径(_orphanAudit / _daemonSuperviseOnce)识别「异主 daemon」的唯一判据——
|
|
9892
|
-
* ensureRunning 只按 cmdline 判 active,无法区分「本守卫的 daemon」与「外部同名 daemon」。 */
|
|
9819
|
+
/** 无副作用分类(供监督/审计共用,绝不 spawn/stop):running 期望代际存活 / external ctl 被异 cmdMark 进程
|
|
9820
|
+
* 占用(绝不接管)/ reclaiming 期望已死且同 cmdMark 残留占 ctl / barrier latch 窗口内 / absent 可 spawn /
|
|
9821
|
+
* stopping 停止中。监督与停止路径据此识别「异主 daemon」——ensureRunning 只按 cmdline 判 active,分不清两者。 */
|
|
9893
9822
|
classify() {
|
|
9894
9823
|
if (this._stopping) return { mode: "stopping" };
|
|
9895
9824
|
const exp = this.expectedPid();
|
|
@@ -9903,8 +9832,8 @@ var require_process3 = __commonJS({
|
|
|
9903
9832
|
if (stale) return { mode: "reclaiming", stale };
|
|
9904
9833
|
return { mode: "absent" };
|
|
9905
9834
|
}
|
|
9906
|
-
/**
|
|
9907
|
-
*
|
|
9835
|
+
/** 周期监督:classify() 分类(无副作用)后再施加副作用(reclaim/spawn);期望进程失联则先验证
|
|
9836
|
+
* 死透再 replace(死透由 ensureRunning 的 stale/死 pid 分支处理)。 */
|
|
9908
9837
|
async superviseOnce() {
|
|
9909
9838
|
if (!this.expectedPid()) {
|
|
9910
9839
|
try {
|
|
@@ -10021,7 +9950,7 @@ var require_runtime2 = __commonJS({
|
|
|
10021
9950
|
router() {
|
|
10022
9951
|
return host2.router;
|
|
10023
9952
|
},
|
|
10024
|
-
// 同模块兄弟方法经 host
|
|
9953
|
+
// 同模块兄弟方法经 host 上的既有安装转发(外部覆写 host 方法仍生效)。
|
|
10025
9954
|
dshMainView() {
|
|
10026
9955
|
return host2.dshMainView();
|
|
10027
9956
|
},
|
|
@@ -10061,7 +9990,7 @@ var require_runtime2 = __commonJS({
|
|
|
10061
9990
|
module2.exports = {
|
|
10062
9991
|
methods: {
|
|
10063
9992
|
/** 统一受管进程生命周期实例(懒加载单例;lan/router 共用 DaemonLifecycle 核心)。
|
|
10064
|
-
* 身份文件(owner 连续:守卫重启=接管既有 daemon
|
|
9993
|
+
* 身份文件(owner 连续:守卫重启=接管既有 daemon)、spawn latch、换代停旧/等死/等端口释放都在核心内。 */
|
|
10065
9994
|
_daemonLifecycle(kind) {
|
|
10066
9995
|
const d = depsOf(this);
|
|
10067
9996
|
if (!d.configPath()) return null;
|
|
@@ -10331,7 +10260,7 @@ var require_identity = __commonJS({
|
|
|
10331
10260
|
config() {
|
|
10332
10261
|
return host2.config;
|
|
10333
10262
|
},
|
|
10334
|
-
// 同模块兄弟方法经 host
|
|
10263
|
+
// 同模块兄弟方法经 host 上的既有安装转发。
|
|
10335
10264
|
lanLockPath() {
|
|
10336
10265
|
return host2._lanLockPath();
|
|
10337
10266
|
},
|
|
@@ -10344,7 +10273,7 @@ var require_identity = __commonJS({
|
|
|
10344
10273
|
return d;
|
|
10345
10274
|
}
|
|
10346
10275
|
module2.exports = {
|
|
10347
|
-
//
|
|
10276
|
+
// 测试缝:锁的取/放/读主是纯 fs + kill(0) 语义,直接导出给回归用。
|
|
10348
10277
|
_lockPrimitives: { acquireLock: acquireLock2, releaseLock: releaseLock2, lockPid, pidAlive },
|
|
10349
10278
|
methods: {
|
|
10350
10279
|
_lanLockPath() {
|
|
@@ -10434,9 +10363,7 @@ var require_probe4 = __commonJS({
|
|
|
10434
10363
|
}
|
|
10435
10364
|
module2.exports = {
|
|
10436
10365
|
methods: {
|
|
10437
|
-
/** 独立 router-daemon 是否在运行(探测 ctl 端口监听者 cmdline
|
|
10438
|
-
* 守卫探测到 daemon 在跑就不再内嵌启动 router(避免双占 ctl 口),只做监督
|
|
10439
|
-
* (lifecycleManager 周期探活 ctl 口,异常时拉起 daemon)。 */
|
|
10366
|
+
/** 独立 router-daemon 是否在运行(探测 ctl 端口监听者 cmdline)。 */
|
|
10440
10367
|
_routerDaemonActive() {
|
|
10441
10368
|
const d = depsOf(this);
|
|
10442
10369
|
try {
|
|
@@ -10448,9 +10375,8 @@ var require_probe4 = __commonJS({
|
|
|
10448
10375
|
return false;
|
|
10449
10376
|
}
|
|
10450
10377
|
},
|
|
10451
|
-
/** lan-daemon
|
|
10452
|
-
*
|
|
10453
|
-
* 否则内嵌 LanManager。这是部署形态选择(壳写配置),不对用户暴露。 */
|
|
10378
|
+
/** lan-daemon 模式是否启用:config.lanDaemon 是结构性部署选择(壳写配置,无面板入口、不对用户
|
|
10379
|
+
* 暴露)——true 时 lan 由独立 daemon 承载(ctl 通道),否则内嵌 LanManager。 */
|
|
10454
10380
|
lanDaemonEnabled() {
|
|
10455
10381
|
const d = depsOf(this);
|
|
10456
10382
|
const cfg = d.config();
|
|
@@ -10499,7 +10425,7 @@ var require_router = __commonJS({
|
|
|
10499
10425
|
ctl() {
|
|
10500
10426
|
return host2.ctl;
|
|
10501
10427
|
},
|
|
10502
|
-
// 同模块兄弟方法经 host
|
|
10428
|
+
// 同模块兄弟方法经 host 上的既有安装转发(外部覆写 host 方法仍生效)。
|
|
10503
10429
|
routerDaemonActive() {
|
|
10504
10430
|
return host2.routerDaemonActive();
|
|
10505
10431
|
},
|
|
@@ -10584,12 +10510,10 @@ var require_router = __commonJS({
|
|
|
10584
10510
|
return { ok: false, source: "embedded", error: e2 && e2.message || String(e2) };
|
|
10585
10511
|
}
|
|
10586
10512
|
},
|
|
10587
|
-
//
|
|
10588
|
-
//
|
|
10589
|
-
//
|
|
10590
|
-
//
|
|
10591
|
-
// (POST /ctl {method,args},见 src/platform/ctl/server.js)——写即 daemon 生效、读即
|
|
10592
|
-
// daemon 最新;daemon 未跑则走守卫本地实例(内嵌回退路径)。
|
|
10513
|
+
// router 控制通道门面:守卫 API/视图统一从这里取。
|
|
10514
|
+
// 本方法在此文件以保证对 ctl 的依赖单向(ctl/facades 不调本文件的 routerDaemonActive)。
|
|
10515
|
+
// daemon 在跑则转发 ctl(POST /ctl {method,args})——写即 daemon 生效、读即 daemon 最新;
|
|
10516
|
+
// daemon 未跑则走守卫本地实例(内嵌回退路径)。
|
|
10593
10517
|
routerApi() {
|
|
10594
10518
|
const d = depsOf(this);
|
|
10595
10519
|
if (d.routerDaemonActive()) {
|
|
@@ -10616,12 +10540,9 @@ var require_lan = __commonJS({
|
|
|
10616
10540
|
return d;
|
|
10617
10541
|
}
|
|
10618
10542
|
module2.exports = { methods: {
|
|
10619
|
-
// 远程控制委托:全部转发给 LanManager
|
|
10620
|
-
//
|
|
10621
|
-
//
|
|
10622
|
-
// 直出 dshToken 会把 DSH 会话令牌泄漏给局域网;权威仍在 DshTokenService(relay 经
|
|
10623
|
-
// tokenOf 内部读取,无需经此透传)。remote(projectRemoteView 产物)为后端单一推导
|
|
10624
|
-
// 视图 {mode,ready,accessUrl,reasons},不含机密,整段放行给 UI 消费。返回形如 {items,addresses}。
|
|
10543
|
+
// 远程控制委托:全部转发给 LanManager;daemon 监督模式经 43108 ctl 委托(异步),本地模式走 LanManager(同步)。
|
|
10544
|
+
// 令牌收敛:listLan 输出必须剔除 token/dshToken —— /lan-access 允许 LAN/私网 Host 访问,直出会把 DSH 会话令牌泄漏给局域网,权威仍在 DshTokenService。
|
|
10545
|
+
// remote(projectRemoteView 产物)为后端单一推导视图 {mode,ready,accessUrl,reasons},不含机密,整段放行给 UI。返回形如 {items,addresses}。
|
|
10625
10546
|
listLan() {
|
|
10626
10547
|
const d = depsOf(this);
|
|
10627
10548
|
const sanitize = (r) => {
|
|
@@ -10680,7 +10601,6 @@ var require_ports2 = __commonJS({
|
|
|
10680
10601
|
logger() {
|
|
10681
10602
|
return host2.logger;
|
|
10682
10603
|
},
|
|
10683
|
-
// 同模块兄弟方法经 host 上的既有安装转发(等价于原经 this 的调用)。
|
|
10684
10604
|
portActives(list) {
|
|
10685
10605
|
return host2._portActives(list);
|
|
10686
10606
|
},
|
|
@@ -10696,8 +10616,8 @@ var require_ports2 = __commonJS({
|
|
|
10696
10616
|
return d;
|
|
10697
10617
|
}
|
|
10698
10618
|
module2.exports = { methods: {
|
|
10699
|
-
//
|
|
10700
|
-
//
|
|
10619
|
+
// 端口清单经 sup 接口暴露(presentation 不直连 infra)。每条记录必须补 active
|
|
10620
|
+
// (端口当前真实监听中),否则前端「状态」列全部显示停用。
|
|
10701
10621
|
// 探测按端口集合整批缓存 3s TTL,避免前端 2s 心跳每次触发全量同步扫 /proc 挤占事件循环。
|
|
10702
10622
|
async listPorts() {
|
|
10703
10623
|
const d = depsOf(this);
|
|
@@ -10736,11 +10656,9 @@ var require_ports2 = __commonJS({
|
|
|
10736
10656
|
}
|
|
10737
10657
|
return { records: out, snapshot: snap, capacity };
|
|
10738
10658
|
},
|
|
10739
|
-
/**
|
|
10740
|
-
*
|
|
10741
|
-
*
|
|
10742
|
-
* 导致端口在监听却恒报 inactive。TCP connect 与端口是否被监听直接等价(同 isTaken 判占用
|
|
10743
|
-
* 语义),无需 /proc 权限,三平台一致。 */
|
|
10659
|
+
/** 端口集合激活探测,active=true 表示该端口当前有进程在监听。判定用纯 TCP connect(probe.portListening):
|
|
10660
|
+
* findListeningPid 要读 /proc/<pid>/fd 反查 socket->pid,对守卫管理树外的孙进程(router-daemon 的反代子进程)
|
|
10661
|
+
* 常因读取权限返回 null,端口在监听却恒报 inactive;TCP connect 无需 /proc 权限、三平台一致。 */
|
|
10744
10662
|
async _portActives(portsList) {
|
|
10745
10663
|
const d = depsOf(this);
|
|
10746
10664
|
const now = Date.now();
|
|
@@ -10893,7 +10811,7 @@ var require_status = __commonJS({
|
|
|
10893
10811
|
return {
|
|
10894
10812
|
desired: host2._mDesired(),
|
|
10895
10813
|
phase: host2._mPhase(),
|
|
10896
|
-
//
|
|
10814
|
+
// 会话生命周期(INV-S4):与 phase 正交——phase 是 main 状态机相位,
|
|
10897
10815
|
// sessionState 是整个服务链的运行相位(前端/壳据此表达「退出中/已退出」)。
|
|
10898
10816
|
sessionState: host2._sessionState,
|
|
10899
10817
|
// 数据目录保护状态(Windows 无 icacls 时可观测降级)。
|
|
@@ -10938,7 +10856,6 @@ var require_router2 = __commonJS({
|
|
|
10938
10856
|
function createRouterActions(deps) {
|
|
10939
10857
|
const g = deps || {};
|
|
10940
10858
|
return {
|
|
10941
|
-
// 智能路由开关管理
|
|
10942
10859
|
async setRouterRunning(on) {
|
|
10943
10860
|
const lifecycleManager = g.getLifecycleManager();
|
|
10944
10861
|
const daemons = g.getDaemons();
|
|
@@ -11094,7 +11011,7 @@ var require_core4 = __commonJS({
|
|
|
11094
11011
|
return {
|
|
11095
11012
|
ok: false,
|
|
11096
11013
|
redirect: url.pathname,
|
|
11097
|
-
// 令牌条 5
|
|
11014
|
+
// 令牌条 5:种派生会话值,绝不是门卫令牌原文。
|
|
11098
11015
|
cookie: "dsh_lan_token=" + lanGateCookieValue(token, salt) + "; Path=/; HttpOnly; SameSite=Lax"
|
|
11099
11016
|
};
|
|
11100
11017
|
}
|
|
@@ -11436,10 +11353,9 @@ var require_env_catalog = __commonJS({
|
|
|
11436
11353
|
constructor(config) {
|
|
11437
11354
|
this.config = config || {};
|
|
11438
11355
|
}
|
|
11439
|
-
/** 系统二进制条目探测:{ id: { label, required, state, detail } }。
|
|
11440
|
-
*
|
|
11441
|
-
*
|
|
11442
|
-
* 兼容:detail 保持字符串,新增字段放 detail 之外(version/min/meets),不破坏既有契约。 */
|
|
11356
|
+
/** 系统二进制条目探测:{ id: { label, required, state, detail } }。state 三态:
|
|
11357
|
+
* ok = 存在且满足门槛(Node 需 >= 壳投放的 minNode);outdated = 存在但低于门槛;missing = 不存在。
|
|
11358
|
+
* 兼容:detail 保持字符串,新增字段(version/min/meets)放 detail 之外,不破坏既有契约。 */
|
|
11443
11359
|
probe() {
|
|
11444
11360
|
const out = {};
|
|
11445
11361
|
for (const [id, e] of Object.entries(SYSTEM_ENTRIES)) {
|
|
@@ -11460,9 +11376,8 @@ var require_env_catalog = __commonJS({
|
|
|
11460
11376
|
}
|
|
11461
11377
|
return out;
|
|
11462
11378
|
}
|
|
11463
|
-
/**
|
|
11464
|
-
*
|
|
11465
|
-
* 既有 /env/status 消费方。 */
|
|
11379
|
+
/** 内核更新依赖条目(单写入者契约:安装/重启归桌面壳,守卫只读 corePackageName 查版本状态)。
|
|
11380
|
+
* id 仍为 selfUpdate 以兼容既有 /env/status 消费方。 */
|
|
11466
11381
|
selfUpdateEntry() {
|
|
11467
11382
|
const pkg = this.config.corePackageName;
|
|
11468
11383
|
if (!pkg) {
|
|
@@ -11484,8 +11399,7 @@ var require_env_catalog = __commonJS({
|
|
|
11484
11399
|
detail: binOk ? installed || "\u5DF2\u88C5" : "bin \u4E0D\u5B58\u5728: " + (bin || "?")
|
|
11485
11400
|
};
|
|
11486
11401
|
}
|
|
11487
|
-
/**
|
|
11488
|
-
* 无 sys 时探测一次(有 10s TTL 缓存)。 */
|
|
11402
|
+
/** 汇总:全部必填项状态(供面板/守卫快速判定环境就绪);无 sys 时探测一次(有 10s TTL 缓存)。 */
|
|
11489
11403
|
summary(extra, sys) {
|
|
11490
11404
|
const s = sys || this.probe();
|
|
11491
11405
|
const items = { ...s, ...extra || {} };
|
|
@@ -11587,7 +11501,7 @@ var require_node_lts = __commonJS({
|
|
|
11587
11501
|
var path2 = require("node:path");
|
|
11588
11502
|
module2.exports = {
|
|
11589
11503
|
methods: {
|
|
11590
|
-
/**
|
|
11504
|
+
/** Node LTS 状态:本地判定 + 6h 可刷新缓存,不做远端查询(避免守卫启动依赖网络)。
|
|
11591
11505
|
* 失败返回 { ok:false, error } 由前端降级展示,绝不抛异常。 */
|
|
11592
11506
|
async nodeLtsStatus() {
|
|
11593
11507
|
try {
|
|
@@ -11713,7 +11627,6 @@ var require_versions = __commonJS({
|
|
|
11713
11627
|
dist: () => host2.dist,
|
|
11714
11628
|
events: () => host2.events,
|
|
11715
11629
|
guardVersion: () => host2.guardVersion,
|
|
11716
|
-
// 同模块兄弟方法经 host 上的既有安装转发(等价于原经 this 的调用)。
|
|
11717
11630
|
guardCorePkg: () => host2.guardCorePkg(),
|
|
11718
11631
|
readBinarySelfVersion: () => host2._readBinarySelfVersion(),
|
|
11719
11632
|
vcsRoot: () => host2._vcsRoot(),
|
|
@@ -11725,20 +11638,18 @@ var require_versions = __commonJS({
|
|
|
11725
11638
|
}
|
|
11726
11639
|
module2.exports = {
|
|
11727
11640
|
methods: {
|
|
11728
|
-
// ----
|
|
11729
|
-
// 内核 npm 包的唯一写入者是桌面壳(见 RELEASE-AND-UPDATE-MECHANISM.md);
|
|
11641
|
+
// ---- 内核更新(单写入者契约):内核 npm 包的唯一写入者是桌面壳;
|
|
11730
11642
|
// 守卫只保留只读的 guardSelfUpdateStatus,接口 /self-update/apply|restart-guard 返回 410。
|
|
11731
|
-
/** 内核 npm
|
|
11643
|
+
/** 内核 npm 子包名:corePackageName 可为显式常量或含 {os}/{arch} 占位的模板。 */
|
|
11732
11644
|
guardCorePkg() {
|
|
11733
11645
|
const d = depsOf(this);
|
|
11734
11646
|
const raw = d.config().corePackageName;
|
|
11735
11647
|
if (!raw) return null;
|
|
11736
11648
|
return String(raw).replace(/{os}/g, matrix.osTag()).replace(/{arch}/g, matrix.current().arch) || null;
|
|
11737
11649
|
},
|
|
11738
|
-
/**
|
|
11739
|
-
*
|
|
11740
|
-
*
|
|
11741
|
-
* 本方法不写任何东西:面板据此显示可更新,实际安装由桌面壳 kernel_update_apply 执行。 */
|
|
11650
|
+
/** 内核更新状态(只读,本方法不写任何东西;实际安装由桌面壳 kernel_update_apply 执行)。
|
|
11651
|
+
* 查内核子包在发布通道(RELEASE-CHANNEL-CONTRACT:rollback -> canary -> latest,
|
|
11652
|
+
* latest 缺失才回落最高)的版本,与本机 guardVersion 比较。 */
|
|
11742
11653
|
async guardSelfUpdateStatus() {
|
|
11743
11654
|
const d = depsOf(this);
|
|
11744
11655
|
const pkg = d.guardCorePkg();
|
|
@@ -11759,14 +11670,10 @@ var require_versions = __commonJS({
|
|
|
11759
11670
|
return { ok: false, error: e.message };
|
|
11760
11671
|
}
|
|
11761
11672
|
},
|
|
11762
|
-
/**
|
|
11763
|
-
*
|
|
11764
|
-
*
|
|
11765
|
-
*
|
|
11766
|
-
* (require('../../core.cjs')),spawn 它能得到同样的 --version 输出。若只认 sea-binary,
|
|
11767
|
-
* 真实用户永远读不到磁盘实况版本,updatePending 恒 false(「已装好待重启」永不显示)。
|
|
11768
|
-
* source-shell(源码形态)不支持:其 --version 报的是开发目录版本,与 npm 安装无关。
|
|
11769
|
-
*/
|
|
11673
|
+
/** 读磁盘上运行位的自报版本:spawn --version,解析版本行。
|
|
11674
|
+
* 条件是 updatable(sea-binary 或 launcher):launcher 的 bin 入口同样可执行,
|
|
11675
|
+
* 若只认 sea-binary 则发布态永远读不到磁盘实况,updatePending 恒 false。
|
|
11676
|
+
* source-shell 不支持:其 --version 报的是开发目录版本,与 npm 安装无关。 */
|
|
11770
11677
|
_readBinarySelfVersion() {
|
|
11771
11678
|
const dep = deploy.detect();
|
|
11772
11679
|
if (!dep.updatable || !dep.runningTarget) return null;
|
|
@@ -11779,9 +11686,9 @@ var require_versions = __commonJS({
|
|
|
11779
11686
|
}
|
|
11780
11687
|
},
|
|
11781
11688
|
// 管家自身版本检查(与 DSH 更新解耦):本地仓库 git 视角,配了远程才 fetch 比对。
|
|
11782
|
-
/** VCS
|
|
11783
|
-
*
|
|
11784
|
-
*
|
|
11689
|
+
/** VCS 根:从包根上溯找最近的外层 .git(排除自身嵌套仓——嵌套仓 HEAD 与外层仓脱节,
|
|
11690
|
+
* 会导致 UI 版本/commit 失真);找不到时回退包根。
|
|
11691
|
+
* 包根必须用 srcpath.resolvePackageRoot()(按 package.json 上溯),不能靠 __dirname 相对层数。 */
|
|
11785
11692
|
_vcsRoot() {
|
|
11786
11693
|
const dir = srcpath.resolvePackageRoot() || path2.resolve(__dirname, "..");
|
|
11787
11694
|
const innerGit = path2.join(dir, ".git");
|
|
@@ -11807,11 +11714,9 @@ var require_versions = __commonJS({
|
|
|
11807
11714
|
}
|
|
11808
11715
|
return { version: d.guardVersion(), runningVersion: d.guardVersion(), commit, updateAvailable: false, upstream, latest: d.guardVersion() };
|
|
11809
11716
|
},
|
|
11810
|
-
/**
|
|
11811
|
-
*
|
|
11812
|
-
*
|
|
11813
|
-
* 这里用 exec.runOutAsync(异步)+ 10s 超时;fetch 失败/超时只降级为「本地视图」,不抛错。
|
|
11814
|
-
*/
|
|
11717
|
+
/** 完整版本检查(async):本地 commit + 远端 fetch 比对。
|
|
11718
|
+
* git fetch 是网络 I/O,绝不能同步执行(会冻结事件循环,守卫假死且无法自愈);
|
|
11719
|
+
* fetch 失败/超时只降级为「本地视图」,不抛错。 */
|
|
11815
11720
|
async guardVersionCheck() {
|
|
11816
11721
|
const d = depsOf(this);
|
|
11817
11722
|
const base = d.guardVersionLocal();
|
|
@@ -11861,10 +11766,10 @@ var require_access = __commonJS({
|
|
|
11861
11766
|
const cfg = this.config || {};
|
|
11862
11767
|
return { configured: !!cfg.apiAccessKey, host: cfg.apiHost || void 0 };
|
|
11863
11768
|
},
|
|
11864
|
-
/**
|
|
11865
|
-
*
|
|
11866
|
-
*
|
|
11867
|
-
*
|
|
11769
|
+
/** 设置/清除出回环访问密钥(空串=清除)。
|
|
11770
|
+
* 清空密钥必须同时回关 LAN(apiHost -> 127.0.0.1 并持久化):lan-panel 的开 LAN
|
|
11771
|
+
* 前置条件是「已有 apiAccessKey」,只清 key 不动 apiHost 会留下「绑定 0.0.0.0 且零认证」
|
|
11772
|
+
* 的暴露窗口。监听 socket 的即时生效由 api 层 fail-closed 兜底,本层只保证配置自洽。 */
|
|
11868
11773
|
setAccessKey(key) {
|
|
11869
11774
|
try {
|
|
11870
11775
|
const cfg = this.config || {};
|
|
@@ -12031,7 +11936,6 @@ var require_lan_panel = __commonJS({
|
|
|
12031
11936
|
events: () => host2.events,
|
|
12032
11937
|
configPath: () => host2.configPath,
|
|
12033
11938
|
api: () => host2.api,
|
|
12034
|
-
// 同模块兄弟方法经 host 上的既有安装转发(等价于原经 this 的调用)。
|
|
12035
11939
|
lanPanelStatus: () => host2.lanPanelStatus(),
|
|
12036
11940
|
apiRebind: () => host2._apiRebind()
|
|
12037
11941
|
};
|
|
@@ -12104,9 +12008,7 @@ var require_facets = __commonJS({
|
|
|
12104
12008
|
var FACETS = [
|
|
12105
12009
|
{ name: "assembly/bootstrap", mod: require_bootstrap(), hostFirst: true },
|
|
12106
12010
|
{ name: "assembly/api-rebind", mod: require_api_rebind(), apiRebind: true },
|
|
12107
|
-
//
|
|
12108
|
-
// + control/{projection,specs} 已改为**真 ctor 工厂**,经 assembly/collaborators.js 装配,
|
|
12109
|
-
// 不再在此以 { methods }/{ hostFirst } 形态安装(见 collaborators.js)。
|
|
12011
|
+
// session/state/control 不在本清单:它们是真 ctor 工厂,由 assembly/collaborators.js 装配。
|
|
12110
12012
|
{ name: "session/shutdown", mod: require_shutdown(), hostFirst: true },
|
|
12111
12013
|
{ name: "self/notify", mod: require_notify2(), hostFirst: true },
|
|
12112
12014
|
{ name: "control/scheduler", mod: require_scheduler() },
|
|
@@ -12115,7 +12017,6 @@ var require_facets = __commonJS({
|
|
|
12115
12017
|
{ name: "main/controller", mod: require_controller() },
|
|
12116
12018
|
{ name: "main/shadow", mod: require_shadow() },
|
|
12117
12019
|
{ name: "main/process", mod: require_process2() },
|
|
12118
|
-
// R3 严值 DF-2:端口运行时再推导从 main/process.js 拆为独立切面(_findManagedDshPort/_applyMainPort)。
|
|
12119
12020
|
{ name: "main/port-rederive", mod: require_port_rederive() },
|
|
12120
12021
|
{ name: "main/signals", mod: require_signals() },
|
|
12121
12022
|
{ name: "main/health-gate", mod: require_health_gate() },
|
|
@@ -12130,10 +12031,9 @@ var require_facets = __commonJS({
|
|
|
12130
12031
|
{ name: "facade/ports", mod: require_ports2() },
|
|
12131
12032
|
{ name: "facade/main", mod: require_main() },
|
|
12132
12033
|
{ name: "facade/status", mod: require_status(), hostFirst: true },
|
|
12133
|
-
//
|
|
12134
|
-
//
|
|
12135
|
-
//
|
|
12136
|
-
// 保证 api 消费面(sup.setRouterRunning / patchDshMain / setRemoteMode …)名字逐个不变。
|
|
12034
|
+
// 域写动作在 app/domain-actions/(facade 侧只读):三者是真 ctor 工厂,实现体经显式 deps
|
|
12035
|
+
// 取事实、不直连 this;按 factory 名在装配期构造后把产物平铺安装到 host,
|
|
12036
|
+
// 平铺名必须与 api 消费面(setRouterRunning / patchDshMain / setRemoteMode 等)逐个一致。
|
|
12137
12037
|
{ name: "domain-actions/router", mod: require_router2(), factory: "createRouterActions" },
|
|
12138
12038
|
{ name: "domain-actions/lan", mod: require_lan2(), factory: "createLanActions" },
|
|
12139
12039
|
{ name: "domain-actions/main", mod: require_main2(), factory: "createMainActions" },
|
|
@@ -12706,9 +12606,8 @@ var require_pool2 = __commonJS({
|
|
|
12706
12606
|
this._loaded = false;
|
|
12707
12607
|
}
|
|
12708
12608
|
/* 来源登记 */
|
|
12709
|
-
/**
|
|
12710
|
-
*
|
|
12711
|
-
* 返回是否完成登记;kind 完全无法判定时返回 false。 */
|
|
12609
|
+
/** 登记或更新目标的令牌来源,重复 attach 幂等并保留已推送的 stdout 行;kind 缺省时按源形态推断(infer.js)。
|
|
12610
|
+
* kind 必须在 kinds.js 登记(契约1),完全无法判定时返回 false,避免幽灵令牌入池。 */
|
|
12712
12611
|
attach(id, src) {
|
|
12713
12612
|
if (!id) return false;
|
|
12714
12613
|
const s = src || {};
|
|
@@ -12739,9 +12638,8 @@ var require_pool2 = __commonJS({
|
|
|
12739
12638
|
this.clear(id);
|
|
12740
12639
|
}
|
|
12741
12640
|
/* 统一捕获(源无关) */
|
|
12742
|
-
/**
|
|
12743
|
-
|
|
12744
|
-
* 因为同步 journalctl 在守卫生命周期 tick 里会冻结事件循环最长 5s(心跳停摆)。 */
|
|
12641
|
+
/** 主动捕捉一次:按源优先级取“最新一条”URL 行的令牌,有变化则入库并广播。
|
|
12642
|
+
* stdout/文件档同步返回;journal 档非阻塞发射:同步 journalctl 在生命周期 tick 里会冻结事件循环(见 capture.js)。 */
|
|
12745
12643
|
capture(id) {
|
|
12746
12644
|
const src = this._sources.get(id);
|
|
12747
12645
|
if (!src) return null;
|
|
@@ -12813,9 +12711,9 @@ var require_pool2 = __commonJS({
|
|
|
12813
12711
|
this.capture(id);
|
|
12814
12712
|
}
|
|
12815
12713
|
/* 生命周期 */
|
|
12816
|
-
/** 清空某目标的令牌与调度;TK-8:必须广播 null
|
|
12817
|
-
* stdout
|
|
12818
|
-
* 以新 gen
|
|
12714
|
+
/** 清空某目标的令牌与调度;TK-8:必须广播 null(消费方立刻丢弃旧代令牌)。
|
|
12715
|
+
* stdout 行缓冲同属旧代状态必须一并清:残留行会被 ensureCaptured 再“捕获”,
|
|
12716
|
+
* 以新 gen 把已死令牌回灌进恢复文件。 */
|
|
12819
12717
|
clear(id) {
|
|
12820
12718
|
this._cancelSchedule(id);
|
|
12821
12719
|
this._backfillAt.delete(id);
|
|
@@ -12882,7 +12780,6 @@ var require_pool2 = __commonJS({
|
|
|
12882
12780
|
const rec = {
|
|
12883
12781
|
value,
|
|
12884
12782
|
gen: (prev ? prev.gen : 0) + 1,
|
|
12885
|
-
// 每次值变化加一,代号解决无版本问题
|
|
12886
12783
|
source: source || "unknown",
|
|
12887
12784
|
at: Date.now(),
|
|
12888
12785
|
kind: src && src.kind || prev && prev.kind || null
|
|
@@ -12899,7 +12796,7 @@ var require_pool2 = __commonJS({
|
|
|
12899
12796
|
this._bus.emit(id, rec.value, { value: rec.value, gen: rec.gen, source: rec.source, at: rec.at });
|
|
12900
12797
|
return value;
|
|
12901
12798
|
}
|
|
12902
|
-
/**
|
|
12799
|
+
/** 把命中令牌的原文行写入恢复文件(统一经 persist:脱敏 + 0600 + 轮转)。 */
|
|
12903
12800
|
_persistLine(id, file, line) {
|
|
12904
12801
|
const r = persist.appendByRotation(file, line, { maxBytes: persist.PERSIST_LIMITS.MAX_BYTES });
|
|
12905
12802
|
if (!r.ok) this.logger.warn && this.logger.warn("[token] persist file(" + id + ") failed: " + (r.reason || "unknown"));
|
|
@@ -12938,7 +12835,6 @@ var require_pool2 = __commonJS({
|
|
|
12938
12835
|
this._records.set(e.id, { value: e.value, gen: e.gen, source: e.source, at: e.at, kind: e.kind });
|
|
12939
12836
|
}
|
|
12940
12837
|
}
|
|
12941
|
-
// 兼容层已移除:TK-4 要求令牌池是唯一事实源,任何指向内部 Map 的旧访问器都会让消费方绕开 get/getRecord,成为各自缓存令牌的温床。
|
|
12942
12838
|
};
|
|
12943
12839
|
module2.exports = {
|
|
12944
12840
|
TokenPool,
|
|
@@ -12962,7 +12858,6 @@ var require_token = __commonJS({
|
|
|
12962
12858
|
this.events = o.events || null;
|
|
12963
12859
|
this.pool = new TokenPool({ logger: this.logger, events: this.events, poolFile: o.poolFile || null });
|
|
12964
12860
|
}
|
|
12965
|
-
/* 来源登记 */
|
|
12966
12861
|
/** 登记源。kind 必填(契约1);缺省时按源形态强推断(见 pool.inferKind)。 */
|
|
12967
12862
|
attach(id, src) {
|
|
12968
12863
|
return this.pool.attach(id, src);
|
|
@@ -12971,7 +12866,6 @@ var require_token = __commonJS({
|
|
|
12971
12866
|
detach(id) {
|
|
12972
12867
|
return this.pool.detach(id);
|
|
12973
12868
|
}
|
|
12974
|
-
/* 捕捉 */
|
|
12975
12869
|
/** 主动捕捉一次(journal 源)。返回当前令牌或 null。 */
|
|
12976
12870
|
capture(id) {
|
|
12977
12871
|
return this.pool.capture(id);
|
|
@@ -12988,7 +12882,6 @@ var require_token = __commonJS({
|
|
|
12988
12882
|
ensureCaptured(id) {
|
|
12989
12883
|
return this.pool.ensureCaptured(id);
|
|
12990
12884
|
}
|
|
12991
|
-
/* 变更 */
|
|
12992
12885
|
/** 清令牌,广播 null(TK-8)。 */
|
|
12993
12886
|
clear(id) {
|
|
12994
12887
|
return this.pool.clear(id);
|
|
@@ -13377,11 +13270,8 @@ var require_tasks = __commonJS({
|
|
|
13377
13270
|
this._log("info", "task " + task.id + " created (" + kind + "/" + action + " " + task.target.id + ")");
|
|
13378
13271
|
return task;
|
|
13379
13272
|
}
|
|
13380
|
-
/**
|
|
13381
|
-
*
|
|
13382
|
-
* 内建契约:finally 清 _current 索引;fn 内的任何异常都不再逃逸为 unhandledRejection /
|
|
13383
|
-
* 任务永久 running。
|
|
13384
|
-
*/
|
|
13273
|
+
/** 统一作业执行器:begin->start->fn->succeed/fail 封装,作业体异常一律落 failed;
|
|
13274
|
+
* finally 清 _current 索引,任务绝不永久占用。 */
|
|
13385
13275
|
async run(kind, targetId, action, opts, fn) {
|
|
13386
13276
|
if (this.isBusy(kind, targetId)) {
|
|
13387
13277
|
const cur = this.current(kind, targetId);
|
|
@@ -13421,7 +13311,6 @@ var require_tasks = __commonJS({
|
|
|
13421
13311
|
const t = this.tasks.find((x) => x.id === id);
|
|
13422
13312
|
return t && (t.state === "running" || t.state === "pending") ? t : null;
|
|
13423
13313
|
}
|
|
13424
|
-
/** 按 id 查任务。 */
|
|
13425
13314
|
get(taskId2) {
|
|
13426
13315
|
return this.tasks.find((t) => t.id === taskId2) || null;
|
|
13427
13316
|
}
|
|
@@ -13435,7 +13324,6 @@ var require_tasks = __commonJS({
|
|
|
13435
13324
|
return this.tasks.filter((t) => t.state === "running" || t.state === "pending");
|
|
13436
13325
|
}
|
|
13437
13326
|
/* 任务推进 */
|
|
13438
|
-
/** 标记任务运行中(从 pending 进入 running;首个 step 前调用)。 */
|
|
13439
13327
|
start(taskId2) {
|
|
13440
13328
|
const t = this.get(taskId2);
|
|
13441
13329
|
if (!t || t.state !== "pending") return null;
|
|
@@ -13444,7 +13332,6 @@ var require_tasks = __commonJS({
|
|
|
13444
13332
|
this._emit("task_state", { id: taskId2, state: "running" });
|
|
13445
13333
|
return t;
|
|
13446
13334
|
}
|
|
13447
|
-
/** 添加一个 step(追加到 steps 尾部)。 */
|
|
13448
13335
|
step(taskId2, name) {
|
|
13449
13336
|
const t = this.get(taskId2);
|
|
13450
13337
|
if (!t) return null;
|
|
@@ -13453,7 +13340,6 @@ var require_tasks = __commonJS({
|
|
|
13453
13340
|
this._save();
|
|
13454
13341
|
return s;
|
|
13455
13342
|
}
|
|
13456
|
-
/** 推进某 step 状态。 */
|
|
13457
13343
|
stepState(taskId2, index, state, extra) {
|
|
13458
13344
|
const t = this.get(taskId2);
|
|
13459
13345
|
if (!t || !t.steps[index]) return null;
|
|
@@ -13468,7 +13354,6 @@ var require_tasks = __commonJS({
|
|
|
13468
13354
|
this._emit("task_step", { id: taskId2, index, name: s.name, state });
|
|
13469
13355
|
return s;
|
|
13470
13356
|
}
|
|
13471
|
-
/** 记录任务日志(有界尾部)。 */
|
|
13472
13357
|
log(taskId2, line) {
|
|
13473
13358
|
const t = this.get(taskId2);
|
|
13474
13359
|
if (!t) return;
|
|
@@ -13477,11 +13362,9 @@ var require_tasks = __commonJS({
|
|
|
13477
13362
|
if (t.log.length > MAX_LOG_LINES) t.log.splice(0, t.log.length - MAX_LOG_LINES);
|
|
13478
13363
|
this._save();
|
|
13479
13364
|
}
|
|
13480
|
-
/** 任务成功。 */
|
|
13481
13365
|
succeed(taskId2, extra) {
|
|
13482
13366
|
return this._finish(taskId2, "succeeded", null, extra);
|
|
13483
13367
|
}
|
|
13484
|
-
/** 任务失败。 */
|
|
13485
13368
|
fail(taskId2, error, extra) {
|
|
13486
13369
|
return this._finish(taskId2, "failed", error, extra);
|
|
13487
13370
|
}
|
|
@@ -13588,7 +13471,7 @@ var require_token_kinds = __commonJS({
|
|
|
13588
13471
|
var kinds = require_kinds();
|
|
13589
13472
|
var pool = require_pool2();
|
|
13590
13473
|
var KINDS = {
|
|
13591
|
-
//
|
|
13474
|
+
// 原生令牌:DSH 进程生成、我方只能捕捉;每次 DSH 重启轮换。main 与实例同策略、仅源不同。
|
|
13592
13475
|
"dsh-main": {
|
|
13593
13476
|
side: "dsh",
|
|
13594
13477
|
strategy: "capture+persist",
|
|
@@ -13598,7 +13481,7 @@ var require_token_kinds = __commonJS({
|
|
|
13598
13481
|
unitBacked: false,
|
|
13599
13482
|
desc: "\u539F\u751F main \u4F1A\u8BDD\u4EE4\u724C\uFF08DSH \u8FDB\u7A0B\u751F\u6210\uFF1B\u6355\u6349+\u6301\u4E45\u5316+\u8DDF\u968F\uFF09"
|
|
13600
13483
|
},
|
|
13601
|
-
//
|
|
13484
|
+
// 沙箱实例会话令牌:源为 systemd 单元 journald。
|
|
13602
13485
|
"dsh-instance": {
|
|
13603
13486
|
side: "dsh",
|
|
13604
13487
|
strategy: "capture+persist",
|
|
@@ -13608,8 +13491,8 @@ var require_token_kinds = __commonJS({
|
|
|
13608
13491
|
unitBacked: true,
|
|
13609
13492
|
desc: "\u6C99\u7BB1\u5B9E\u4F8B\u4F1A\u8BDD\u4EE4\u724C\uFF08DSH \u8FDB\u7A0B\u751F\u6210\uFF1B\u6E90\u4E3A dsh-web@<id> \u5355\u5143\uFF09"
|
|
13610
13493
|
},
|
|
13611
|
-
//
|
|
13612
|
-
//
|
|
13494
|
+
// 浏览器会话 cookie:由上面两种经 HTTP 303 换取,随 dshToken 轮换重换。
|
|
13495
|
+
// 刻意为 persistent:false —— cookie 是派生缓存,落盘等于多一份会话凭据副本。
|
|
13613
13496
|
"dsh-auth": {
|
|
13614
13497
|
side: "dsh-derived",
|
|
13615
13498
|
strategy: "exchange",
|
|
@@ -13619,7 +13502,7 @@ var require_token_kinds = __commonJS({
|
|
|
13619
13502
|
unitBacked: false,
|
|
13620
13503
|
desc: "\u6D4F\u89C8\u5668\u4F1A\u8BDD cookie\uFF08dsh-auth-*\uFF0C\u7531 dsh-main/dsh-instance \u6362\u53D6\uFF1B\u53EA\u5B58\u8FDB\u7A0B\u5185\u5B58\uFF09"
|
|
13621
13504
|
},
|
|
13622
|
-
//
|
|
13505
|
+
// 用户配置类:不是 DSH 令牌。不捕捉、不进令牌池展示、不随 DSH 轮换。
|
|
13623
13506
|
"remote-token": {
|
|
13624
13507
|
side: "user",
|
|
13625
13508
|
strategy: "config",
|
|
@@ -13647,7 +13530,7 @@ var require_token_kinds = __commonJS({
|
|
|
13647
13530
|
unitBacked: false,
|
|
13648
13531
|
desc: "FRP \u8BA4\u8BC1\u4EE4\u724C\uFF08\u7528\u6237\u5728 UI \u586B\u5199\uFF0C\u900F\u4F20\u7ED9 frpc\uFF09"
|
|
13649
13532
|
},
|
|
13650
|
-
//
|
|
13533
|
+
// 我方签发的门卫会话 cookie。
|
|
13651
13534
|
"lan-gate": {
|
|
13652
13535
|
side: "self",
|
|
13653
13536
|
strategy: "issue+verify",
|
|
@@ -13819,7 +13702,7 @@ var require_core5 = __commonJS({
|
|
|
13819
13702
|
registryFile: path2.join(swDir, "registry.json"),
|
|
13820
13703
|
events: host2.events,
|
|
13821
13704
|
logger: host2.logger,
|
|
13822
|
-
//
|
|
13705
|
+
// 灰度事实由本机配置注入(canary:true);仅 @dsh-sup/* 包消费该开关。
|
|
13823
13706
|
canary: host2.config.canary === true
|
|
13824
13707
|
});
|
|
13825
13708
|
host2.shellDomain = shellDomain;
|
|
@@ -13840,7 +13723,7 @@ var require_port_segments = __commonJS({
|
|
|
13840
13723
|
var ports = require_ports();
|
|
13841
13724
|
var POOLS = {
|
|
13842
13725
|
providerApi: { base: 24e3, count: 2e3 }
|
|
13843
|
-
//
|
|
13726
|
+
// 每供应商独立 API 端点段(24000-25999)
|
|
13844
13727
|
};
|
|
13845
13728
|
var SEGMENTS = {
|
|
13846
13729
|
proxyInstance: { pool: "managed", anchor: 1e3 },
|
|
@@ -13966,7 +13849,6 @@ var require_store3 = __commonJS({
|
|
|
13966
13849
|
canPersist() {
|
|
13967
13850
|
return this._canPersist ? this._canPersist() !== false : true;
|
|
13968
13851
|
}
|
|
13969
|
-
/** 触发一次落盘(经统一写权闸)。 */
|
|
13970
13852
|
persist() {
|
|
13971
13853
|
if (!this._persistFn) return;
|
|
13972
13854
|
if (!this.canPersist()) return;
|
|
@@ -14399,12 +14281,9 @@ var require_base = __commonJS({
|
|
|
14399
14281
|
async detectAccount(acc) {
|
|
14400
14282
|
throw new Error("detectAccount must be implemented by subclass");
|
|
14401
14283
|
}
|
|
14402
|
-
// 能力契约声明(PG-1
|
|
14403
|
-
//
|
|
14404
|
-
//
|
|
14405
|
-
// accountOf/markInstanceProblem/_doStart)
|
|
14406
|
-
// 由 providers/process-pool.js 的 mixin 实现并并入 supports 词表——契约在能力方声明,
|
|
14407
|
-
// 基座不再携带实现不了的抛错占位;调用方一律以 supports(cap) 守卫。
|
|
14284
|
+
// 能力契约声明(PG-1):process-pool 能力面由 providers/process-pool.js 的 mixin 实现并
|
|
14285
|
+
// 并入 supports 词表——契约在能力方声明,基座不携带实现不了的抛错占位;
|
|
14286
|
+
// 调用方一律以 supports(cap) 守卫。
|
|
14408
14287
|
/** 本 provider 是否具备某项能力;缺省为无 process 能力(最保守)。 */
|
|
14409
14288
|
supports(_cap) {
|
|
14410
14289
|
return false;
|
|
@@ -14460,7 +14339,7 @@ var require_base = __commonJS({
|
|
|
14460
14339
|
classifyResponse(status, headers, bodyText) {
|
|
14461
14340
|
return quota.classifyUpstreamLimited(status, bodyText);
|
|
14462
14341
|
}
|
|
14463
|
-
/**
|
|
14342
|
+
/** 账号处置副作用契约:router 只发 signal,具体冻结/停实例/封号由 provider 执行。 */
|
|
14464
14343
|
effect(signal, acc, ctx) {
|
|
14465
14344
|
const c = ctx || {};
|
|
14466
14345
|
if (signal === "credits") {
|
|
@@ -14477,7 +14356,7 @@ var require_base = __commonJS({
|
|
|
14477
14356
|
}
|
|
14478
14357
|
return false;
|
|
14479
14358
|
}
|
|
14480
|
-
/**
|
|
14359
|
+
/** 唯一「时间窗额度用尽」判定。 */
|
|
14481
14360
|
_windowExhausted(acc) {
|
|
14482
14361
|
return quota.windowExhausted(acc);
|
|
14483
14362
|
}
|
|
@@ -14487,7 +14366,7 @@ var require_base = __commonJS({
|
|
|
14487
14366
|
_creditsRefilled(acc) {
|
|
14488
14367
|
return quota.creditsRefilled(acc);
|
|
14489
14368
|
}
|
|
14490
|
-
/**
|
|
14369
|
+
/** 账号可用性判定(唯一事实):ready 且未「预付余额不足」且窗口未满。 */
|
|
14491
14370
|
isAccountUsable(acc, opts) {
|
|
14492
14371
|
if (!acc || acc.status !== "ready") return false;
|
|
14493
14372
|
if (this._isCreditsLow(acc)) return false;
|
|
@@ -14501,7 +14380,7 @@ var require_base = __commonJS({
|
|
|
14501
14380
|
_ensureLimit(acc) {
|
|
14502
14381
|
return freeze.ensureLimit(acc);
|
|
14503
14382
|
}
|
|
14504
|
-
/** limit
|
|
14383
|
+
/** limit 纯只读预览(不赋值、不写盘):只读视图唯一入口。写版 _ensureLimit 保留(门禁/测试消费)。 */
|
|
14505
14384
|
_previewLimit(acc) {
|
|
14506
14385
|
return freeze.previewLimit(acc);
|
|
14507
14386
|
}
|
|
@@ -14520,7 +14399,7 @@ var require_base = __commonJS({
|
|
|
14520
14399
|
markBanned(acc, error) {
|
|
14521
14400
|
return freeze.markBanned(acc, error, this);
|
|
14522
14401
|
}
|
|
14523
|
-
//
|
|
14402
|
+
// applyDetection 是唯一状态投影入口(覆盖全部调用方):投影完成后补齐 limit(写版 ensureLimit)。
|
|
14524
14403
|
applyDetection(acc, det) {
|
|
14525
14404
|
const r = freeze.applyDetection(acc, det, this);
|
|
14526
14405
|
freeze.ensureLimit(acc);
|
|
@@ -14715,7 +14594,7 @@ var require_direct = __commonJS({
|
|
|
14715
14594
|
this.presetId = opts.presetId || null;
|
|
14716
14595
|
this.officialPricing = {};
|
|
14717
14596
|
}
|
|
14718
|
-
/** 能力声明:key-pool
|
|
14597
|
+
/** 能力声明:key-pool 无实例能力,显式声明「不支持」供调用方 supports() 守卫,差异可静态校验。 */
|
|
14719
14598
|
supports(_cap) {
|
|
14720
14599
|
return false;
|
|
14721
14600
|
}
|
|
@@ -14798,15 +14677,13 @@ var require_model2 = __commonJS({
|
|
|
14798
14677
|
toJSON() {
|
|
14799
14678
|
return serializeInstance(this);
|
|
14800
14679
|
}
|
|
14801
|
-
/**
|
|
14680
|
+
/** 实例是否可立即服务(切换策略唯一需要问的问题)。 */
|
|
14802
14681
|
isServable() {
|
|
14803
14682
|
return isServable(this);
|
|
14804
14683
|
}
|
|
14805
|
-
/** 实例是否占用资源(WARM/HOT/DEAD 都有进程)。 */
|
|
14806
14684
|
occupiesSlot() {
|
|
14807
14685
|
return occupiesSlot(this);
|
|
14808
14686
|
}
|
|
14809
|
-
/** 反序列化:进程态(pid)不落盘 -> 一律回到 COLD,由 reconcile 按期望集重新拉起。 */
|
|
14810
14687
|
static fromJSON(o) {
|
|
14811
14688
|
return deserializeInstance(o);
|
|
14812
14689
|
}
|
|
@@ -15727,11 +15604,10 @@ var require_process_pool = __commonJS({
|
|
|
15727
15604
|
}
|
|
15728
15605
|
};
|
|
15729
15606
|
}
|
|
15730
|
-
/** 能力声明 = 基座能力与 process-pool 能力的并集。 */
|
|
15731
15607
|
supports(cap) {
|
|
15732
15608
|
return POOL_CAPS.includes(cap) || super.supports(cap);
|
|
15733
15609
|
}
|
|
15734
|
-
/**
|
|
15610
|
+
/** 实例在场是池能力:在基座使用状态(in-use/idle)上覆写派生 warming。 */
|
|
15735
15611
|
usageOf(acc) {
|
|
15736
15612
|
const r = super.usageOf(acc);
|
|
15737
15613
|
if (r !== "idle") return r;
|
|
@@ -15741,7 +15617,7 @@ var require_process_pool = __commonJS({
|
|
|
15741
15617
|
accountOf(inst) {
|
|
15742
15618
|
return this.accounts.find((a) => a.key === inst.key) || null;
|
|
15743
15619
|
}
|
|
15744
|
-
/** 账号 ->
|
|
15620
|
+
/** 账号 -> 实例映射(一账号一实例硬规则)。 */
|
|
15745
15621
|
instanceOf(acc) {
|
|
15746
15622
|
if (!acc) return null;
|
|
15747
15623
|
return (this.instances || []).find((i) => i.keyId === acc.keyId) || acc.instance || null;
|
|
@@ -15769,19 +15645,17 @@ var require_process_pool = __commonJS({
|
|
|
15769
15645
|
})();
|
|
15770
15646
|
return inst.startingPromise;
|
|
15771
15647
|
}
|
|
15772
|
-
/** 启动实例底层治理(spawn/探活在 probe.js
|
|
15773
|
-
* 测试以 _doStart 打桩替换 spawn;留在 mixin 侧是为了 this 图单向(mixin 不回调消费方方法)。 */
|
|
15648
|
+
/** 启动实例底层治理(spawn/探活在 probe.js)。测试以 _doStart 打桩替换 spawn;留在 mixin 侧是为 this 图单向。 */
|
|
15774
15649
|
async _doStart(inst) {
|
|
15775
15650
|
return probe.spawnInstance(this, inst);
|
|
15776
15651
|
}
|
|
15777
|
-
/**
|
|
15652
|
+
/** 请求级熔断计数清零;独立于健康监测的 _monitorFails。 */
|
|
15778
15653
|
markRequestOk(inst) {
|
|
15779
15654
|
if (!inst) return;
|
|
15780
15655
|
inst._unhealthyCount = 0;
|
|
15781
15656
|
}
|
|
15782
|
-
/** 统一可服务化门面(LC 核心-1
|
|
15783
|
-
*
|
|
15784
|
-
* (显式切换的启动预算,裁决 1:超时诚实报错,绝不静默换号)。 */
|
|
15657
|
+
/** 统一可服务化门面(LC 核心-1):幂等启动 + 预算内同步等待,外部不裸调 start/kill。
|
|
15658
|
+
* budgetMs=null 表示等满探活周期(显式切换预算);超时诚实报错,绝不静默换号。 */
|
|
15785
15659
|
async ensureServable(acc, opts) {
|
|
15786
15660
|
const inst = this.instanceOf(acc);
|
|
15787
15661
|
if (!inst) return { ok: false, error: "\u5B9E\u4F8B\u4E0D\u5B58\u5728" };
|
|
@@ -15795,12 +15669,12 @@ var require_process_pool = __commonJS({
|
|
|
15795
15669
|
return { ok: false, warming: !!inst.pid, error: "\u5B9E\u4F8B\u672A\u5728\u7B49\u5F85\u671F\u5185\u5C31\u7EEA" };
|
|
15796
15670
|
}
|
|
15797
15671
|
/** 等待区回收唯一入口(LC 核心-3):force 终止 + 释放端口,账号进程层面同一轮零存在。
|
|
15798
|
-
*
|
|
15672
|
+
* 冻结零宽限:发不出请求的账号不该继续占进程;丢弃在途属预期语义。幂等。 */
|
|
15799
15673
|
reclaimAccount(acc) {
|
|
15800
15674
|
return life.reclaimAccount(this, acc);
|
|
15801
15675
|
}
|
|
15802
|
-
/**
|
|
15803
|
-
*
|
|
15676
|
+
/** 状态迁移事件表的进程侧接线(freeze.js setStatus 调用):进等待区立即回收;
|
|
15677
|
+
* 回 ready 立即重算期望集补缺口(LC 核心-5),不等周期对账。 */
|
|
15804
15678
|
_onStatusTransition(acc, prev, status) {
|
|
15805
15679
|
if (status === "frozen" || status === "banned" || status === "discarded") {
|
|
15806
15680
|
try {
|
|
@@ -15811,11 +15685,10 @@ var require_process_pool = __commonJS({
|
|
|
15811
15685
|
this.reconcileNow();
|
|
15812
15686
|
}
|
|
15813
15687
|
}
|
|
15814
|
-
/**
|
|
15688
|
+
/** 实例停止仲裁(在途/在用延后、force 跳过的语义在 instance-lifecycle.js)。 */
|
|
15815
15689
|
stopInstance(inst, force) {
|
|
15816
15690
|
return life.arbitrateStop(this, inst, force);
|
|
15817
15691
|
}
|
|
15818
|
-
/** 请求结束补刀(委托 instance-lifecycle.js)。 */
|
|
15819
15692
|
_retryPendingStop(acc) {
|
|
15820
15693
|
return life.retryPendingStop(this, acc);
|
|
15821
15694
|
}
|
|
@@ -15874,11 +15747,11 @@ var require_process_pool = __commonJS({
|
|
|
15874
15747
|
} catch {
|
|
15875
15748
|
}
|
|
15876
15749
|
}
|
|
15877
|
-
/**
|
|
15750
|
+
/** 兼容旧名别名。 */
|
|
15878
15751
|
markInstanceNetFail(instOrAcc) {
|
|
15879
15752
|
this.markInstanceProblem(instOrAcc, "net-error");
|
|
15880
15753
|
}
|
|
15881
|
-
/**
|
|
15754
|
+
/** 实例空闲后补做被延后的重启(_restartPending 的唯一消费点)。 */
|
|
15882
15755
|
flushRestartPending(inst) {
|
|
15883
15756
|
if (!inst || !inst._restartPending) return;
|
|
15884
15757
|
const acc = this.accountOf(inst);
|
|
@@ -15976,7 +15849,6 @@ var require_proxy = __commonJS({
|
|
|
15976
15849
|
this._stopping = false;
|
|
15977
15850
|
this._terminatingPids = /* @__PURE__ */ new Set();
|
|
15978
15851
|
}
|
|
15979
|
-
/** 能力声明:基座与 process-pool 的并集在 withProcessPool mixin 完成(process-pool.js)。 */
|
|
15980
15852
|
async ensureInstance(key) {
|
|
15981
15853
|
let inst = this.instances.find((i) => i.key === key);
|
|
15982
15854
|
if (inst) return inst;
|
|
@@ -15984,7 +15856,7 @@ var require_proxy = __commonJS({
|
|
|
15984
15856
|
this.instances.push(inst);
|
|
15985
15857
|
return inst;
|
|
15986
15858
|
}
|
|
15987
|
-
/** 解析启动命令(缓存优先 + fallback npx
|
|
15859
|
+
/** 解析启动命令(缓存优先 + fallback npx):拼装委托 command.js;凭证剔除留在本层(纪律)。 */
|
|
15988
15860
|
async _resolveLaunchCommand(app, port, key) {
|
|
15989
15861
|
const registry = this.dist ? await this.dist.selectRegistry(false).catch(() => null) : null;
|
|
15990
15862
|
const cachedBin = this._cachedPkgBin(app.pkg);
|
|
@@ -16008,23 +15880,18 @@ var require_proxy = __commonJS({
|
|
|
16008
15880
|
_ensurePkgCached(app) {
|
|
16009
15881
|
return probe.ensurePkgCached(this, app);
|
|
16010
15882
|
}
|
|
16011
|
-
/** 实例停止仲裁(委托 instance-lifecycle.js)。 */
|
|
16012
15883
|
_canStopInstance(acc) {
|
|
16013
15884
|
return life.canStopInstance(this, acc);
|
|
16014
15885
|
}
|
|
16015
|
-
/** 等待全部 SIGTERM 在途子进程真正退出(实现与 zombie 判定在 probe.js)。 */
|
|
16016
15886
|
async waitAllStopped(timeoutMs) {
|
|
16017
15887
|
return probe.waitAllStopped(this, timeoutMs);
|
|
16018
15888
|
}
|
|
16019
|
-
/** 实例探活(探活实现与计数分层在 probe.js,计数各归其主)。 */
|
|
16020
15889
|
async healthInstance(inst) {
|
|
16021
15890
|
return probe.healthInstance(this, inst);
|
|
16022
15891
|
}
|
|
16023
|
-
/** 实例生命周期监控(进程/端口/HTTP 卡死检测)。 */
|
|
16024
15892
|
async monitorLifecycle() {
|
|
16025
15893
|
return probe.monitorLifecycle(this);
|
|
16026
15894
|
}
|
|
16027
|
-
/** 模式级配额检测(策略注册与解析在 probe.js + quota-strategies.js)。 */
|
|
16028
15895
|
async detectInstanceQuota(inst) {
|
|
16029
15896
|
return probe.detectInstanceQuota(this, inst);
|
|
16030
15897
|
}
|
|
@@ -16034,14 +15901,14 @@ var require_proxy = __commonJS({
|
|
|
16034
15901
|
isAccountUsable(acc, opts) {
|
|
16035
15902
|
return life.isAccountUsable(this, acc, opts);
|
|
16036
15903
|
}
|
|
16037
|
-
/** 429/403
|
|
15904
|
+
/** 429/403 配额冻结:先立即回收进等待区(force + 释放端口,零宽限),再状态机,再对账补槽。 */
|
|
16038
15905
|
markQuotaExhausted(acc, cooldownMs) {
|
|
16039
15906
|
this.reclaimAccount(acc);
|
|
16040
15907
|
super.markQuotaExhausted(acc, cooldownMs);
|
|
16041
15908
|
this.reconcileNow();
|
|
16042
15909
|
this._probeAfterResponseFreeze(acc);
|
|
16043
15910
|
}
|
|
16044
|
-
/** credits
|
|
15911
|
+
/** credits 余额不足:与窗口同一处置(回收 + 冻结 + 补槽)。 */
|
|
16045
15912
|
markCreditsExhausted(acc) {
|
|
16046
15913
|
this.reclaimAccount(acc);
|
|
16047
15914
|
super.markCreditsExhausted(acc);
|
|
@@ -16072,7 +15939,7 @@ var require_proxy = __commonJS({
|
|
|
16072
15939
|
reconcileNow() {
|
|
16073
15940
|
return restart.reconcileNow(this);
|
|
16074
15941
|
}
|
|
16075
|
-
/**
|
|
15942
|
+
/** 封号:与配额冻结同一处置(先回收再走状态机)。 */
|
|
16076
15943
|
markBanned(acc, error) {
|
|
16077
15944
|
this.reclaimAccount(acc);
|
|
16078
15945
|
super.markBanned(acc, error);
|
|
@@ -16185,8 +16052,7 @@ var require_switch2 = __commonJS({
|
|
|
16185
16052
|
this.events = opts.events || null;
|
|
16186
16053
|
this.onPersist = opts.onPersist || null;
|
|
16187
16054
|
}
|
|
16188
|
-
/**
|
|
16189
|
-
* opts.excludeKeys=Set 时把本请求内瞬时故障(5xx/net-error)账号直接排除,强制轮换不粘滞。 */
|
|
16055
|
+
/** 在指定供应商池内选号(不跨池,见文件头)。opts.excludeKeys=Set 时排除本请求内瞬时故障账号,强制轮换不粘滞。 */
|
|
16190
16056
|
pickFor(provider, opts) {
|
|
16191
16057
|
if (!provider) return null;
|
|
16192
16058
|
return this._pickIn(provider, opts);
|
|
@@ -16223,11 +16089,8 @@ var require_switch2 = __commonJS({
|
|
|
16223
16089
|
if (this.events) this.events.append("router_pick", { provider: p.name, key: picked.maskedKey });
|
|
16224
16090
|
return picked;
|
|
16225
16091
|
}
|
|
16226
|
-
/** 上游失败反应(唯一编排点)。ctx = { status, headers, body, attempt, attempts, error? }
|
|
16227
|
-
*
|
|
16228
|
-
* 2) provider.effect 执行账号副作用(credits/window 冻结,proxy 会停实例并预热;banned 封号);
|
|
16229
|
-
* 3) 返回 action:'retry'(credits/window/transient,带 transient 标记供调用方定退避)或
|
|
16230
|
-
* 'passthrough'(banned/none/unknown 原样回状态/头/体,绝不误切)。 */
|
|
16092
|
+
/** 上游失败反应(唯一编排点)。ctx = { status, headers, body, attempt, attempts, error? };
|
|
16093
|
+
* action/retry 语义见 policies/failure.js decideFailure 契约。 */
|
|
16231
16094
|
reactToFailure(provider, acc, ctx) {
|
|
16232
16095
|
const c = ctx || {};
|
|
16233
16096
|
const status = c.status;
|
|
@@ -16296,7 +16159,7 @@ var require_store4 = __commonJS({
|
|
|
16296
16159
|
canPersist() {
|
|
16297
16160
|
return this._writable !== false && this.loadedOk === true;
|
|
16298
16161
|
}
|
|
16299
|
-
/**
|
|
16162
|
+
/** 设置状态文件写开关(true=本实例写;false=只读,由外部 router-daemon 独占写)。 */
|
|
16300
16163
|
setPersistEnabled(v) {
|
|
16301
16164
|
this._writable = v !== false;
|
|
16302
16165
|
}
|
|
@@ -16361,8 +16224,7 @@ var require_store4 = __commonJS({
|
|
|
16361
16224
|
key: a.key || null,
|
|
16362
16225
|
keyId: a.keyId,
|
|
16363
16226
|
maskedKey: a.maskedKey,
|
|
16364
|
-
// 单事实源:只读 status;
|
|
16365
|
-
// usage 不反序列化(纯派生,由 usageOf 从 activeAccount/实例实况算)。
|
|
16227
|
+
// 单事实源:只读 status;usage 不反序列化(纯派生,由 usageOf 计算)。
|
|
16366
16228
|
status: a.status || a.validity || "registered",
|
|
16367
16229
|
quota: a.quota || null,
|
|
16368
16230
|
registeredAt: a.registeredAt || Date.now(),
|
|
@@ -16398,32 +16260,27 @@ var require_proxy_apps = __commonJS({
|
|
|
16398
16260
|
id: "commandcode",
|
|
16399
16261
|
name: "Command Code Proxy",
|
|
16400
16262
|
pkg: "commandcode-api-proxy",
|
|
16401
|
-
// 官方运行方式(npx 一键)—— 完整独立发行版
|
|
16402
16263
|
command: ["npx", "--yes", "commandcode-api-proxy", "--host", "127.0.0.1", "--port", "{{port}}", "--api-key", "{{key}}"],
|
|
16403
16264
|
healthPath: "/health",
|
|
16404
16265
|
modelPath: "/v1/models",
|
|
16405
16266
|
upstream: "https://api.commandcode.ai",
|
|
16406
|
-
// 实例环境契约:keyEnv
|
|
16407
|
-
//
|
|
16408
|
-
// 加 CC_IDLE_TIMEOUT_MS=0 等自我干预曾导致回归,故保持纯净注入。
|
|
16267
|
+
// 实例环境契约:keyEnv 是密钥注入的唯一 env 名(绝不进 cmdline);app.env/超时等一律不注入,
|
|
16268
|
+
// 跑上游完全默认配置(作者设计值)——自我干预(如 CC_IDLE_TIMEOUT_MS=0)曾导致回归,保持纯净注入。
|
|
16409
16269
|
keyEnv: "CC_API_KEY",
|
|
16410
16270
|
repo: "thaolaptrinh/commandcode-api-proxy",
|
|
16411
16271
|
// 自动更新:最新版本从 npm registry 查(同一 package 名)。空则跳过版本检查。
|
|
16412
16272
|
registry: "commandcode-api-proxy",
|
|
16413
16273
|
versionRefreshMs: 6 * 3600 * 1e3,
|
|
16414
|
-
//
|
|
16415
|
-
//
|
|
16416
|
-
// credits 池,无 period 字段),以及 /alpha/billing/subscriptions 的 currentPeriodEnd(月额度随订阅
|
|
16417
|
-
// 续期重置的精确时刻;仅 credits-limited 账号按 6h 缓存低频取,防风控)。
|
|
16418
|
-
// windowMap: rolling=5h、weekly=周;monthly 由订阅面推导(见 quota-strategies 的 monthlyCapUsd)。
|
|
16274
|
+
// 每账号配额来源:官方 billing 面(解析逻辑见 quota-strategies.js detectCommandCodeBilling)。
|
|
16275
|
+
// windowMap 指 rolling/weekly 字段;monthly 由订阅面推导。
|
|
16419
16276
|
quota: {
|
|
16420
16277
|
type: "commandcode-billing",
|
|
16421
16278
|
apiBase: "https://api.commandcode.ai",
|
|
16422
16279
|
creditsPath: "/alpha/billing/credits",
|
|
16423
16280
|
subscriptionsPath: "/alpha/billing/subscriptions",
|
|
16424
16281
|
windowMap: { rolling: "fiveHour", weekly: "weekly", monthly: null },
|
|
16425
|
-
//
|
|
16426
|
-
//
|
|
16282
|
+
// 月度配额上限($/月):订阅含 $10/月总池,周窗口从池内扣(weekly.used + monthlyRemaining = 10),
|
|
16283
|
+
// 解析层据此推导 monthly.percent。
|
|
16427
16284
|
monthlyCapUsd: 10
|
|
16428
16285
|
},
|
|
16429
16286
|
real: true
|
|
@@ -17053,11 +16910,8 @@ var require_usage = __commonJS({
|
|
|
17053
16910
|
this._timer = null;
|
|
17054
16911
|
this._dirty = false;
|
|
17055
16912
|
}
|
|
17056
|
-
/**
|
|
17057
|
-
*
|
|
17058
|
-
* `__proto__` / `constructor` —— `t.byModel[key] = {...}` 走的是原型赋值,
|
|
17059
|
-
* 账本静默失真且污染面在 Object.prototype 上(空白/控制符键同理,面板也没法看)。
|
|
17060
|
-
* 违规值**折进 (other) 桶**而不是丢弃:B19 的上限语义与「不丢计数」都保持不变。 */
|
|
16913
|
+
/** model 键归一走 platform/util/input.ledgerKey(外部输入字符集单源闸):
|
|
16914
|
+
* __proto__/constructor/空白/控制符等违规值折进 (other) 桶——不丢计数,也不做对象键(防原型污染)。 */
|
|
17061
16915
|
_modelKey(model) {
|
|
17062
16916
|
return input.ledgerKey(model, { max: 128, empty: "unknown", unsafe: "(other)" });
|
|
17063
16917
|
}
|
|
@@ -17074,7 +16928,6 @@ var require_usage = __commonJS({
|
|
|
17074
16928
|
}
|
|
17075
16929
|
return bm;
|
|
17076
16930
|
}
|
|
17077
|
-
/** 记账:累计总量 + byModel + byKey,按快照单价估算费用,节流落盘。 */
|
|
17078
16931
|
recordUsage(entry) {
|
|
17079
16932
|
const t = this.load();
|
|
17080
16933
|
t.requests += 1;
|
|
@@ -17104,14 +16957,13 @@ var require_usage = __commonJS({
|
|
|
17104
16957
|
if (this.events) this.events.append("router_usage", { model: entry.model, tokens: entry.totalTokens });
|
|
17105
16958
|
return t;
|
|
17106
16959
|
}
|
|
17107
|
-
/** 错误计数(持久化)。 */
|
|
17108
16960
|
recordError() {
|
|
17109
16961
|
const t = this.load();
|
|
17110
16962
|
t.errors = (t.errors || 0) + 1;
|
|
17111
16963
|
this._scheduleWrite();
|
|
17112
16964
|
return t.errors;
|
|
17113
16965
|
}
|
|
17114
|
-
/** 脏标记 +
|
|
16966
|
+
/** 脏标记 + 尾部定时器合并落盘;delay=0 时每调用同步写(兼容既有测试语义)。 */
|
|
17115
16967
|
_scheduleWrite() {
|
|
17116
16968
|
this._dirty = true;
|
|
17117
16969
|
if (this._writeDelayMs <= 0) return this._writeTotals();
|
|
@@ -17131,9 +16983,8 @@ var require_usage = __commonJS({
|
|
|
17131
16983
|
if (!this._dirty) return;
|
|
17132
16984
|
this._writeTotals();
|
|
17133
16985
|
}
|
|
17134
|
-
/**
|
|
17135
|
-
*
|
|
17136
|
-
* 永久缓存会让面板冻结在进程启动时的旧快照。兼容旧格式补默认字段,防 undefined 崩溃。 */
|
|
16986
|
+
/** 读源纪律:写者以内存为准(节流窗口内盘落后于内存,重读丢在途账);
|
|
16987
|
+
* 只读实例(无写权)从不记账、盘上是别人的活账,必须每次新鲜读盘——永久缓存会冻结面板。 */
|
|
17137
16988
|
load() {
|
|
17138
16989
|
if (this.totals && this._canPersist()) return this.totals;
|
|
17139
16990
|
let t;
|
|
@@ -17145,13 +16996,12 @@ var require_usage = __commonJS({
|
|
|
17145
16996
|
this.totals = defaults(t);
|
|
17146
16997
|
return this.totals;
|
|
17147
16998
|
}
|
|
17148
|
-
/** 汇总视图(byModel 取 token 前 12)。 */
|
|
17149
16999
|
getUsage() {
|
|
17150
17000
|
const t = this.load();
|
|
17151
17001
|
const byModel = Object.entries(t.byModel || {}).map(([model, v]) => ({ model, ...v })).sort((a, b) => b.totalTokens - a.totalTokens).slice(0, 12);
|
|
17152
17002
|
return { requests: t.requests, promptTokens: t.promptTokens, completionTokens: t.completionTokens, totalTokens: t.totalTokens, costUsd: t.costUsd, errors: t.errors || 0, byModel, byKey: t.byKey || {} };
|
|
17153
17003
|
}
|
|
17154
|
-
/**
|
|
17004
|
+
/** 原子落盘:tmp+rename 防崩溃/断电损坏高频用量文件;写权单闸。 */
|
|
17155
17005
|
_writeTotals() {
|
|
17156
17006
|
try {
|
|
17157
17007
|
if (typeof this._canPersist === "function" && !this._canPersist()) return;
|
|
@@ -17183,13 +17033,10 @@ var require_inflight = __commonJS({
|
|
|
17183
17033
|
acc.inflight = (acc.inflight || 0) + 1;
|
|
17184
17034
|
return acc.inflight;
|
|
17185
17035
|
},
|
|
17186
|
-
/**
|
|
17187
|
-
*
|
|
17188
|
-
*
|
|
17189
|
-
*
|
|
17190
|
-
* @returns {{ zero:boolean, effects:Array<{kind:string,acc:object,prov:object,inst:object}> }}
|
|
17191
|
-
* effects 仅为**描述**,由调用方执行——两条结束路径共用此结果。
|
|
17192
|
-
*/
|
|
17036
|
+
/** 唯一在途递减入口。@param {object} acc 账号(在途计数挂在账号对象上)
|
|
17037
|
+
* @param {{prov?:object, inst?:object, lifecycle?:boolean}} [ctx]
|
|
17038
|
+
* @returns {{ zero:boolean, effects:Array<{kind:string,acc:object,prov:object,inst:object}> }}
|
|
17039
|
+
* effects 仅为描述,由调用方在所有结束路径统一执行。 */
|
|
17193
17040
|
end(acc, ctx) {
|
|
17194
17041
|
if (!acc) return { zero: false, effects: [] };
|
|
17195
17042
|
ended += 1;
|
|
@@ -18090,7 +17937,7 @@ var require_views = __commonJS({
|
|
|
18090
17937
|
});
|
|
18091
17938
|
return {
|
|
18092
17939
|
running: state.running === true,
|
|
18093
|
-
//
|
|
17940
|
+
// 服务运行标志(由 RouterService start/stop 翻转,不在 ctl 方法白名单内)
|
|
18094
17941
|
activatedProviders: (state.providers || []).filter((x) => x.activated).length,
|
|
18095
17942
|
// 已激活供应商数(独立端点在线数)
|
|
18096
17943
|
providers: provs,
|
|
@@ -18142,14 +17989,13 @@ var require_views = __commonJS({
|
|
|
18142
17989
|
keyId: a.keyId,
|
|
18143
17990
|
maskedKey: a.maskedKey,
|
|
18144
17991
|
status: a.status,
|
|
18145
|
-
// 单事实源
|
|
18146
17992
|
validity: a.status,
|
|
18147
17993
|
// 兼容字段(=status,避免旧前端读 undefined)
|
|
18148
17994
|
usage: usageOf,
|
|
18149
17995
|
// 纯派生(activeAccount/实例实况)
|
|
18150
17996
|
quota: a.quota || null,
|
|
18151
17997
|
limit: (p._previewLimit ? p._previewLimit(a) : a.limit) || null,
|
|
18152
|
-
// limitKind+recovery
|
|
17998
|
+
// limitKind+recovery(只读预览,无写副作用)
|
|
18153
17999
|
nextResetAt: a.nextResetAt || null,
|
|
18154
18000
|
registeredAt: a.registeredAt,
|
|
18155
18001
|
detectError: a.detectError || null,
|
|
@@ -18964,7 +18810,6 @@ var require_router3 = __commonJS({
|
|
|
18964
18810
|
_startActivatedProviders() {
|
|
18965
18811
|
return this._endpoint.startActivatedProviders();
|
|
18966
18812
|
}
|
|
18967
|
-
/* Q9 转发 */
|
|
18968
18813
|
proxyFor(prov, req, res) {
|
|
18969
18814
|
return this._forward.proxyFor(prov, req, res);
|
|
18970
18815
|
}
|
|
@@ -18977,7 +18822,7 @@ var require_router3 = __commonJS({
|
|
|
18977
18822
|
recordError() {
|
|
18978
18823
|
return this._forward.recordError();
|
|
18979
18824
|
}
|
|
18980
|
-
/*
|
|
18825
|
+
/* 注册表 + 生命周期 */
|
|
18981
18826
|
addDirectProvider(o) {
|
|
18982
18827
|
return this._ops.addDirectProvider(o);
|
|
18983
18828
|
}
|
|
@@ -18999,7 +18844,7 @@ var require_router3 = __commonJS({
|
|
|
18999
18844
|
stopAllInstances() {
|
|
19000
18845
|
return this._ops.stopAllInstances();
|
|
19001
18846
|
}
|
|
19002
|
-
/*
|
|
18847
|
+
/* 调度 */
|
|
19003
18848
|
_startMaintenance() {
|
|
19004
18849
|
return this._scheduler.start();
|
|
19005
18850
|
}
|
|
@@ -19400,7 +19245,6 @@ var require_model3 = __commonJS({
|
|
|
19400
19245
|
id,
|
|
19401
19246
|
name: String(payload.name || "\u5B9E\u4F8B:" + port).slice(0, 40),
|
|
19402
19247
|
port,
|
|
19403
|
-
// 新增实例:默认沙箱域(绝对隔离、与原生/彼此互不干扰)
|
|
19404
19248
|
domain: "sandbox",
|
|
19405
19249
|
kind: "sandbox",
|
|
19406
19250
|
autoRegistered: false,
|
|
@@ -19415,7 +19259,7 @@ var require_model3 = __commonJS({
|
|
|
19415
19259
|
privateTmp: true,
|
|
19416
19260
|
// node/dsh 位于 /home(nvm),ProtectHome=yes 会使其 exec 失败(203/EXEC);默认关闭,可用 payload.protectHome 覆盖
|
|
19417
19261
|
protectHome: payload.protectHome === void 0 ? false : !!payload.protectHome
|
|
19418
|
-
// 资源配额不接收户输入:启动时由 governor
|
|
19262
|
+
// 资源配额不接收户输入:启动时由 governor 按机器预算与活跃实例数推导。
|
|
19419
19263
|
},
|
|
19420
19264
|
state: { phase: "STOPPED", restartCount: 0, backoffLevel: 0, lastProbeOk: null },
|
|
19421
19265
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -19509,16 +19353,13 @@ var require_store5 = __commonJS({
|
|
|
19509
19353
|
this.syncPorts();
|
|
19510
19354
|
return this.instances;
|
|
19511
19355
|
}
|
|
19512
|
-
/** 清历史遗留的未声明 state
|
|
19513
|
-
*
|
|
19514
|
-
* state.version:阶段五已停止写入(upgrade.js 的两处赋值删除),且内核/测试/壳仓**零消费者**、
|
|
19515
|
-
* createRecord 声明的 state 形状不含它(version 显示走 readInstalledVersion 实时读盘)。
|
|
19516
|
-
* 同型先例:model.normalizeInstance 同样在加载期删遗留键 dshToken(有测试断言该行为)。 */
|
|
19356
|
+
/** 清历史遗留的未声明 state 字段。只在内存删、本方法不写盘;下次 save() 全量序列化自然落地,故幂等。
|
|
19357
|
+
* state.version:内核/测试/壳仓零消费者,版本显示走 readInstalledVersion 实时读盘,已从记录形状声明中移除。 */
|
|
19517
19358
|
_stripLegacyStateKeys(inst) {
|
|
19518
19359
|
if (inst.state && Object.prototype.hasOwnProperty.call(inst.state, "version")) delete inst.state.version;
|
|
19519
19360
|
}
|
|
19520
|
-
/** 损坏现场隔离:先告警,再把损坏文件改名为 .corrupt-<ts>
|
|
19521
|
-
* rename
|
|
19361
|
+
/** 损坏现场隔离:先告警,再把损坏文件改名为 .corrupt-<ts> 保留现场,最后清空内存——
|
|
19362
|
+
* rename 必须先于清空,后续 save 才不会覆盖原损坏文件。 */
|
|
19522
19363
|
_quarantineCorrupt(err) {
|
|
19523
19364
|
const bak = this.instancesFile + ".corrupt-" + Date.now();
|
|
19524
19365
|
this.logger && this.logger.warn && this.logger.warn("instances.json \u8BFB\u53D6/\u89E3\u6790\u5931\u8D25\uFF0C\u5907\u4EFD\u540E\u6E05\u7A7A: " + (err && err.message) + " -> " + bak);
|
|
@@ -19529,7 +19370,7 @@ var require_store5 = __commonJS({
|
|
|
19529
19370
|
}
|
|
19530
19371
|
this._replace([]);
|
|
19531
19372
|
}
|
|
19532
|
-
/**
|
|
19373
|
+
/** 原地替换数组内容(保持数组对象身份,外部引用不失效)。 */
|
|
19533
19374
|
_replace(list) {
|
|
19534
19375
|
if (list === this.instances) return;
|
|
19535
19376
|
this.instances.length = 0;
|
|
@@ -19549,11 +19390,10 @@ var require_store5 = __commonJS({
|
|
|
19549
19390
|
this.logger && this.logger.error && this.logger.error("instances.json \u6301\u4E45\u5316\u5931\u8D25: " + (e && e.message));
|
|
19550
19391
|
}
|
|
19551
19392
|
}
|
|
19552
|
-
/**
|
|
19553
|
-
*
|
|
19554
|
-
*
|
|
19555
|
-
*
|
|
19556
|
-
* registerUser——否则旧 inst:* 记录因「该 id 仍存在」而永久泄漏。 */
|
|
19393
|
+
/** 端口登记派生同步:真源是 instances.json 内存数组(用户配置值),registry 的 inst:* 记录只是派生投影,
|
|
19394
|
+
* 把配置端口纳入全局冲突视图(防动态分配段撞实例端口)。全量对账:内存有而 registry 缺则 registerUser,
|
|
19395
|
+
* registry 有 inst:* 而内存无该实例则 unregister;端口变更时先卸旧登记再按新端口注册,
|
|
19396
|
+
* 否则旧 inst:* 记录因「该 id 仍存在」永久泄漏。 */
|
|
19557
19397
|
syncPorts() {
|
|
19558
19398
|
for (const inst of this.instances) {
|
|
19559
19399
|
const id = String(inst.id || "");
|
|
@@ -19582,7 +19422,7 @@ var require_store5 = __commonJS({
|
|
|
19582
19422
|
}
|
|
19583
19423
|
}
|
|
19584
19424
|
/** 为沙箱实例建独立目录(根 + 数据 + 依赖 + 临时)。实例根一次性收紧权限:
|
|
19585
|
-
*
|
|
19425
|
+
* 同机他用户的跨舱互读是隔离清单里唯一未被目录布局本身挡住的一格。 */
|
|
19586
19426
|
ensureDirs(inst) {
|
|
19587
19427
|
const r = fileProtect.ensurePrivateDir(sandbox.root(this.instancesRoot, inst));
|
|
19588
19428
|
if (r && r.ok === false) this.logger && this.logger.warn && this.logger.warn("\u5B9E\u4F8B\u6839\u6743\u9650\u6536\u7D27\u5931\u8D25 " + r.mode + ": " + r.reason);
|
|
@@ -21275,14 +21115,10 @@ var require_market = __commonJS({
|
|
|
21275
21115
|
if (this._inFlight) return this._inFlight;
|
|
21276
21116
|
return this._startBuild();
|
|
21277
21117
|
}
|
|
21278
|
-
/**
|
|
21279
|
-
*
|
|
21280
|
-
*
|
|
21281
|
-
*
|
|
21282
|
-
* (b) 被消费者取走时仍须如实失败 —— raw 的 reject 语义不变(api/domains/plugins.js 的
|
|
21283
|
-
* GET /plugins/market 分支据此回答 500)。
|
|
21284
|
-
* 若改成「消化后存回 _inFlight」,则并发的 force 请求会取到这个已消化的 promise,
|
|
21285
|
-
* 失败时 resolve 成 undefined -> 200 + 空体,正是本仓最忌讳的「假成功」。 */
|
|
21118
|
+
/** 启动一次构建并登记为并发去重引用,返回原始 promise。两条不变量必须同时成立:
|
|
21119
|
+
* (a) 无消费者的后台刷新失败不得成为进程级 unhandledRejection —— 靠给 raw 挂 no-op handler「标记已处理」实现,而不是把 promise 消化掉;
|
|
21120
|
+
* (b) 被消费者取走时仍须如实失败 —— raw 的 reject 语义不变(api/domains/plugins.js 的 GET /plugins/market 据此回答 500)。
|
|
21121
|
+
* 若改成「消化后存回 _inFlight」,force 请求会取到它并在失败时 resolve 成 undefined -> 200 + 空体。 */
|
|
21286
21122
|
_startBuild() {
|
|
21287
21123
|
const raw = this.buildIndex();
|
|
21288
21124
|
raw.catch(() => {
|
|
@@ -21425,7 +21261,7 @@ var require_market = __commonJS({
|
|
|
21425
21261
|
}
|
|
21426
21262
|
return (origin || REGISTRY).replace(/\/+$/, "");
|
|
21427
21263
|
}
|
|
21428
|
-
/** npm
|
|
21264
|
+
/** npm 最新版元数据查询(镜像源选择见 _npmOrigin)。 */
|
|
21429
21265
|
async safeFetchLatest(name) {
|
|
21430
21266
|
return fetchLatest(await this._npmOrigin(), name);
|
|
21431
21267
|
}
|
|
@@ -22718,27 +22554,20 @@ var require_managed_object = __commonJS({
|
|
|
22718
22554
|
kind: o.kind,
|
|
22719
22555
|
id: o.id,
|
|
22720
22556
|
name: String(o.name || o.id),
|
|
22721
|
-
//
|
|
22722
|
-
// desired:两域共用同一字段名,但语义不同——
|
|
22723
|
-
// 域 A = 用户意图;域 B = 「当前业务是否需要它」的条件。
|
|
22557
|
+
// desired 两域共用字段名但语义不同:域 A=用户意图;域 B=「当前业务是否需要它」的条件
|
|
22724
22558
|
desired: o.desired === "stopped" ? "stopped" : "running",
|
|
22725
|
-
// guardian
|
|
22726
|
-
// 不是「置 false」,是「不存在」,这样旧残留才清得掉。
|
|
22559
|
+
// guardian 域 A 专有(见上);域 B 不物化,旧残留才清得掉
|
|
22727
22560
|
...isDomainA(o.kind) ? { guardian: o.guardian === true } : {},
|
|
22728
|
-
// 所有权(注册时申报;持久)
|
|
22729
22561
|
ownership: normalizeOwnership(o.ownership),
|
|
22730
|
-
//
|
|
22562
|
+
// 初始 stopped;业务不得直接改,由 heartbeat 调谐循环写入
|
|
22731
22563
|
phase: "stopped",
|
|
22732
|
-
// 观测缓存(实然;不持久化,由 heartbeat/adapter 写入)
|
|
22733
22564
|
lastObserved: null,
|
|
22734
|
-
// { ok, error, at }
|
|
22735
|
-
//
|
|
22736
|
-
// 形如 { child?, adoptedPid?, adopted, observedOnly, startDeadline?, spawnBlockedUntil? })
|
|
22565
|
+
// 实然缓存 { ok, error, at };不持久化,由 heartbeat/adapter 写入
|
|
22566
|
+
// 运行期引用(不持久化):main 状态机句柄,形如 { child?, adoptedPid?, adopted, observedOnly, startDeadline?, spawnBlockedUntil? }
|
|
22737
22567
|
process: null,
|
|
22738
|
-
//
|
|
22739
|
-
// 形如 { runState, providers, accounts, proxyInstances, resourcePorts, fetchedAt })
|
|
22568
|
+
// daemon 类黑盒经 ctl 呈报的紧凑摘要,只读缓存不持久化(形如 { runState, providers, accounts, proxyInstances, resourcePorts, fetchedAt })
|
|
22740
22569
|
domainSummary: null,
|
|
22741
|
-
//
|
|
22570
|
+
// 退避与崩溃窗口计数随目录持久化(见 registry _load/_save)
|
|
22742
22571
|
backoffLevel: 0,
|
|
22743
22572
|
backoffUntil: null,
|
|
22744
22573
|
crashWindowStart: null,
|
|
@@ -22940,8 +22769,8 @@ var require_registry3 = __commonJS({
|
|
|
22940
22769
|
this._log("warn", "managed-objects \u6301\u4E45\u5316\u5931\u8D25: " + (e && e.message));
|
|
22941
22770
|
}
|
|
22942
22771
|
}
|
|
22943
|
-
/**
|
|
22944
|
-
*
|
|
22772
|
+
/** 崩溃/退避字段变化的持久化入口:防抖 50ms 合并同拍多次变更避免写放大;
|
|
22773
|
+
* 由 supervisor._persistCrashField 在字段变更后调用。 */
|
|
22945
22774
|
persistCrashState() {
|
|
22946
22775
|
if (this._crashSaveTimer) return;
|
|
22947
22776
|
this._crashSaveTimer = setTimeout(() => {
|
|
@@ -22971,7 +22800,7 @@ var require_registry3 = __commonJS({
|
|
|
22971
22800
|
const i = this._objects.indexOf(e);
|
|
22972
22801
|
if (i >= 0) this._objects.splice(i, 1);
|
|
22973
22802
|
}
|
|
22974
|
-
/**
|
|
22803
|
+
/** 注册新类型能力(显式声明,非通用 CRD;实现与判据见 managed-object.js)。 */
|
|
22975
22804
|
registerKind(kind, meta) {
|
|
22976
22805
|
registerManagedKind(kind, meta);
|
|
22977
22806
|
return this;
|
|
@@ -22995,8 +22824,8 @@ var require_registry3 = __commonJS({
|
|
|
22995
22824
|
this._event("managed_object_registered", { kind: e.kind, id: e.id, name: e.name });
|
|
22996
22825
|
return e;
|
|
22997
22826
|
}
|
|
22998
|
-
/** 对象变更申报(desired/guardian/ownership/name)。
|
|
22999
|
-
*
|
|
22827
|
+
/** 对象变更申报(desired/guardian/ownership/name)。guardian 是域 A 专有:
|
|
22828
|
+
* 域 B 基础设施既不写入,还主动删键清除早期版本残留(下一次 update 即归一)。 */
|
|
23000
22829
|
update(id, patch) {
|
|
23001
22830
|
const e = this.get(id);
|
|
23002
22831
|
if (!e) return { ok: false, error: "\u672A\u6CE8\u518C: " + id };
|
|
@@ -23043,10 +22872,8 @@ var require_registry3 = __commonJS({
|
|
|
23043
22872
|
this._event("managed_object_removed", { kind: e.kind, id: e.id });
|
|
23044
22873
|
return { ok: true };
|
|
23045
22874
|
}
|
|
23046
|
-
/** 释放本对象持有的端口(按 owner
|
|
23047
|
-
*
|
|
23048
|
-
* (那会误删他人端口登记)。返回值仅用于日志——不匹配即 no-op 是期望行为,不是错误。
|
|
23049
|
-
*/
|
|
22875
|
+
/** 释放本对象持有的端口(按 owner)。绝不回退成无 owner 的释放——那会误删他人登记;
|
|
22876
|
+
* owner 不匹配时 release 返回失败是期望行为,不视为错误,仅日志。 */
|
|
23050
22877
|
_releasePort(port, ownerId) {
|
|
23051
22878
|
if (!this.ports || typeof this.ports.release !== "function") return;
|
|
23052
22879
|
try {
|
|
@@ -23077,11 +22904,8 @@ var require_registry3 = __commonJS({
|
|
|
23077
22904
|
count() {
|
|
23078
22905
|
return this._objects.length;
|
|
23079
22906
|
}
|
|
23080
|
-
/** 唯一心跳:薄委托 runHeartbeat(单拍调度、节流、单对象超时隔离在 control/heartbeat.js
|
|
23081
|
-
* 观测写入口仍是本实例的 applyObservation/setPhase。
|
|
23082
|
-
* @param {number} [intervalMs] 心跳拍宽(默认 5000)
|
|
23083
|
-
* @returns {Promise<{ observed: string[], errors: string[] }>}
|
|
23084
|
-
*/
|
|
22907
|
+
/** 唯一心跳:薄委托 runHeartbeat(单拍调度、节流、单对象超时隔离在 control/heartbeat.js);
|
|
22908
|
+
* 观测写入口仍是本实例的 applyObservation/setPhase。 */
|
|
23085
22909
|
heartbeat(intervalMs) {
|
|
23086
22910
|
return runHeartbeat(this, intervalMs);
|
|
23087
22911
|
}
|
|
@@ -24157,16 +23981,6 @@ var require_entry = __commonJS({
|
|
|
24157
23981
|
"use strict";
|
|
24158
23982
|
var { PHASES } = require_registry3();
|
|
24159
23983
|
var ManagedLifecycle = class {
|
|
24160
|
-
/**
|
|
24161
|
-
* @param {object} opts
|
|
24162
|
-
* - id: 模块唯一标识(如 'dsh' / 'router' / 'router.proxy.<keyId>' / 'inst.<id>' / 'lan')
|
|
24163
|
-
* - kind: 模块类别('dsh' | 'router' | 'proxy-instance' | 'instance' | 'lan' | 'plugin')
|
|
24164
|
-
* - name: 显示名
|
|
24165
|
-
* - logger / events:可选(日志与事件总线)
|
|
24166
|
-
* - start(ctx):async —— 启动该模块(由生命周期管理器调用)
|
|
24167
|
-
* - stop(ctx):async —— 停止该模块(守卫 shutdown 或用户启停时调用)
|
|
24168
|
-
* - status():返回模块自身细节状态(供面板展示,可选)
|
|
24169
|
-
*/
|
|
24170
23984
|
constructor(opts) {
|
|
24171
23985
|
this.id = opts.id || "lc-" + Math.random().toString(36).slice(2, 8);
|
|
24172
23986
|
this.kind = opts.kind || "module";
|
|
@@ -24202,19 +24016,16 @@ var require_entry = __commonJS({
|
|
|
24202
24016
|
lastProbeAt: this.lastProbeAt,
|
|
24203
24017
|
lastTransitionAt: this.lastTransitionAt,
|
|
24204
24018
|
error: this.error,
|
|
24205
|
-
// 无 restartCount(域 A 计数不在本对象上)
|
|
24206
24019
|
guardian: this.guardian === true,
|
|
24207
24020
|
startable: this.startable,
|
|
24208
|
-
//
|
|
24021
|
+
// UI 据此灰化启停入口
|
|
24209
24022
|
guardable: this.guardable,
|
|
24210
24023
|
monitoring: this._monitoring,
|
|
24211
24024
|
detail: this._status ? this._status() || null : null
|
|
24212
24025
|
};
|
|
24213
24026
|
}
|
|
24214
|
-
|
|
24215
|
-
|
|
24216
|
-
* phase 只能取 canonical 词表内的值,防止写入手写字符串造成新的分叉;
|
|
24217
|
-
* 代价是写错值时不报错,故词表必须引用唯一源,否则合法值会被无声拒绝。 */
|
|
24027
|
+
/** 状态迁移。非白名单值静默丢弃是执法不是 bug:phase 只取 canonical 词表,
|
|
24028
|
+
* 防止手写字符串造成分叉;代价是写错不报错,故词表必须引用唯一源。 */
|
|
24218
24029
|
_setPhase(p) {
|
|
24219
24030
|
if (!PHASES.includes(p)) return;
|
|
24220
24031
|
if (this.phase !== p) {
|
|
@@ -24223,23 +24034,16 @@ var require_entry = __commonJS({
|
|
|
24223
24034
|
}
|
|
24224
24035
|
}
|
|
24225
24036
|
/* 供 LifecycleManager 调用的统一操作 */
|
|
24226
|
-
/** 期望保持运行。 */
|
|
24227
24037
|
wantRunning() {
|
|
24228
24038
|
this.desired = "running";
|
|
24229
24039
|
this.error = null;
|
|
24230
24040
|
}
|
|
24231
|
-
/** 期望停止。 */
|
|
24232
24041
|
wantStopped() {
|
|
24233
24042
|
this.desired = "stopped";
|
|
24234
24043
|
}
|
|
24235
|
-
/**
|
|
24236
|
-
*
|
|
24237
|
-
*
|
|
24238
|
-
* (如 daemon 拉不起来时)。若不看 `r.ok` 就置 running/healthy,
|
|
24239
|
-
* 会让 `/lifecycle/status` 谎报成功,而模块实际没起来。
|
|
24240
|
-
*
|
|
24241
|
-
* 兼容性:`r.ok !== false` 视为成功,保留「回调只返回 undefined / 无 ok 字段」的既有语义。
|
|
24242
|
-
*/
|
|
24044
|
+
/** 启动(幂等:已在运行则 no-op)。必须尊重回调的显式失败:适配器 start 可能返回
|
|
24045
|
+
* {ok:false}(如 daemon 拉不起来),不看 r.ok 就置 running/healthy 会让
|
|
24046
|
+
* /lifecycle/status 谎报成功。r.ok !== false 视为成功(允许回调返回 undefined)。 */
|
|
24243
24047
|
async start() {
|
|
24244
24048
|
if (this.phase === "running" || this.phase === "starting") return { ok: true, already: true };
|
|
24245
24049
|
this.error = null;
|
|
@@ -24266,12 +24070,8 @@ var require_entry = __commonJS({
|
|
|
24266
24070
|
return { ok: false, error: this.error };
|
|
24267
24071
|
}
|
|
24268
24072
|
}
|
|
24269
|
-
/**
|
|
24270
|
-
*
|
|
24271
|
-
* 同样必须尊重回调的显式失败:stop 回调返回 `{ok:false}` 时不得置
|
|
24272
|
-
* phase=stopped/desired=stopped,否则面板显示「已停止」而进程可能还在跑;
|
|
24273
|
-
* 显式失败时保持运行态(与异常分支同一语义)并如实上报错误。
|
|
24274
|
-
*/
|
|
24073
|
+
/** 停止(守卫 shutdown 或用户显式停)。同样必须尊重回调显式失败:ok:false 时
|
|
24074
|
+
* 不得置 stopped,否则面板显示「已停止」而进程可能还在跑。 */
|
|
24275
24075
|
async stop(reason) {
|
|
24276
24076
|
if (this.phase === "stopped") {
|
|
24277
24077
|
this.desired = "stopped";
|
|
@@ -24298,12 +24098,8 @@ var require_entry = __commonJS({
|
|
|
24298
24098
|
return { ok: false, error: this.error };
|
|
24299
24099
|
}
|
|
24300
24100
|
}
|
|
24301
|
-
/** 重启(无 _restart 回调时退化为 stop -> start
|
|
24302
|
-
*
|
|
24303
|
-
* 回退路径必须尊重 stop/start 的显式失败:两个返回值都不可丢弃,
|
|
24304
|
-
* 否则「停不掉」或「起不来」时仍报成功,面板显示「已重启」而模块实际是死的。
|
|
24305
|
-
* 语义:stop 失败则模块仍在跑,重启未发生;start 失败则已停但未起。
|
|
24306
|
-
*/
|
|
24101
|
+
/** 重启(无 _restart 回调时退化为 stop -> start)。两步返回值都不得丢弃显式失败:
|
|
24102
|
+
* stop 失败=模块仍在跑、重启未发生;start 失败=已停但未起。 */
|
|
24307
24103
|
async restart() {
|
|
24308
24104
|
if (this._restart) {
|
|
24309
24105
|
const r = await this._restart();
|
|
@@ -24380,7 +24176,9 @@ var require_manager = __commonJS({
|
|
|
24380
24176
|
this._emit("lifecycle_stopped", { id: lc.id, kind: lc.kind, ok: r.ok, reason });
|
|
24381
24177
|
return { ok: r.ok !== false, error: r.error, already: r.already, ...lc.snapshot() };
|
|
24382
24178
|
}
|
|
24383
|
-
/**
|
|
24179
|
+
/** 重启(保持 desired 语义)。委托 lc.restart():_restart 回调优先(如 dsh 经 requestRestart
|
|
24180
|
+
* 停旧拉新),无回调才退化为 stop -> start;该回退逻辑由 ManagedLifecycle.restart() 唯一承担
|
|
24181
|
+
* (契约测试锁定),此处不留副本。 */
|
|
24384
24182
|
async restart(id) {
|
|
24385
24183
|
const lc = this.registrations.get(id);
|
|
24386
24184
|
if (!lc) return { ok: false, error: "\u672A\u6CE8\u518C\u6A21\u5757: " + id };
|
|
@@ -24392,12 +24190,9 @@ var require_manager = __commonJS({
|
|
|
24392
24190
|
statusAll() {
|
|
24393
24191
|
return this.all().map((l) => lc_status(l));
|
|
24394
24192
|
}
|
|
24395
|
-
/**
|
|
24396
|
-
*
|
|
24397
|
-
*
|
|
24398
|
-
* 「守卫退出不动 DSH」由调用方以 exclude:['dsh'] 保证(见 supervisor.js 的 stopAll 调用)。
|
|
24399
|
-
* @param {object} opts { exclude?: string[] } 额外豁免的模块 id(如独立 daemon 型 router)
|
|
24400
|
-
*/
|
|
24193
|
+
/** 守卫 shutdown:停全部 monitoring 的模块。契约 RC2:本管理器不内置 dsh 特例——
|
|
24194
|
+
* stopAll 会停掉所有纳管/期望运行项,「守卫退出不动 DSH」由调用方以 exclude:['dsh'] 保证
|
|
24195
|
+
* (见 supervisor.js 的调用)。exclude = 额外豁免的模块 id(如独立 daemon 型 router)。 */
|
|
24401
24196
|
async stopAll(reason, opts) {
|
|
24402
24197
|
const exclude = new Set(opts && opts.exclude || []);
|
|
24403
24198
|
for (const lc of this.all()) {
|
|
@@ -24581,13 +24376,12 @@ var require_adapters = __commonJS({
|
|
|
24581
24376
|
name: "\u667A\u80FD\u8DEF\u7531",
|
|
24582
24377
|
logger,
|
|
24583
24378
|
start: async () => sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(true) : router.start(),
|
|
24584
|
-
// 守卫自身 shutdown(_stopping=true
|
|
24585
|
-
// 守卫退出不影响被管模块(daemon 独立生命周期继续服务);仅显式用户停止才停 daemon。
|
|
24379
|
+
// 守卫自身 shutdown(_stopping=true)不得停独立 daemon:守卫退出不影响被管模块,仅显式用户停止才停。
|
|
24586
24380
|
stop: async () => {
|
|
24587
24381
|
if (sup && sup._stopping) return { ok: true, already: true, reason: "guard-shutdown \u4E0D\u505C daemon" };
|
|
24588
24382
|
return sup && typeof sup.setRouterRunning === "function" ? sup.setRouterRunning(false) : router.stop();
|
|
24589
24383
|
},
|
|
24590
|
-
// detail 走守卫真实 router 状态视图(daemon 模式经 ctl
|
|
24384
|
+
// detail 走守卫真实 router 状态视图(daemon 模式经 ctl 取实时态)
|
|
24591
24385
|
status: () => sup && typeof sup.routerStatus === "function" ? sup.routerStatus() : router.status ? router.status() : null
|
|
24592
24386
|
});
|
|
24593
24387
|
mgr.register(rlc);
|
|
@@ -24609,7 +24403,7 @@ var require_adapters = __commonJS({
|
|
|
24609
24403
|
return { ok: false, error: e.message };
|
|
24610
24404
|
}
|
|
24611
24405
|
},
|
|
24612
|
-
// 守卫 shutdown
|
|
24406
|
+
// 守卫 shutdown 同样不停 lan(relay 由独立 lan-daemon 承载;守卫内 LanManager 为门面)
|
|
24613
24407
|
stop: async () => {
|
|
24614
24408
|
if (deps && deps.supervisor && deps.supervisor._stopping) return { ok: true, already: true, reason: "guard-shutdown \u4E0D\u505C lan" };
|
|
24615
24409
|
try {
|
|
@@ -24628,16 +24422,14 @@ var require_adapters = __commonJS({
|
|
|
24628
24422
|
id: "instances",
|
|
24629
24423
|
kind: "instances",
|
|
24630
24424
|
name: "\u5B9E\u4F8B\u7BA1\u7406",
|
|
24631
|
-
//
|
|
24632
|
-
// 单个实例的启停走 /instances/{start|stop}
|
|
24425
|
+
// 聚合单元无全局进程:声明不可启停,避免「返回 ok 但无动作」的假成功;
|
|
24426
|
+
// 单个实例的启停走 /instances/{start|stop}。
|
|
24633
24427
|
startable: false,
|
|
24634
24428
|
guardable: false,
|
|
24635
24429
|
logger,
|
|
24636
24430
|
start: async () => ({ ok: true }),
|
|
24637
|
-
// 实例无全局进程;单个实例由各自启停
|
|
24638
24431
|
stop: async () => ({ ok: true }),
|
|
24639
|
-
// detail
|
|
24640
|
-
// phase/healthy 视图由守卫 _syncInstancesLifecycleView 每心跳真实刷新。
|
|
24432
|
+
// detail 用实例集真实统计;phase/healthy 视图由守卫 _syncInstancesLifecycleView 每心跳刷新。
|
|
24641
24433
|
status: () => {
|
|
24642
24434
|
try {
|
|
24643
24435
|
const arr = instances && typeof instances.all === "function" && instances.all() || [];
|
|
@@ -25780,8 +25572,7 @@ var require_instances = __commonJS({
|
|
|
25780
25572
|
return bootstrapDshCookie("127.0.0.1", it.port, tok).then((cookie) => {
|
|
25781
25573
|
if (!cookie) return deny(400, "\u4EE4\u724C\u6362\u53D6\u5931\u8D25");
|
|
25782
25574
|
res.writeHead(303, {
|
|
25783
|
-
//
|
|
25784
|
-
// 端口不参与 site 判定),但必须杜绝跨站导航/子资源携带(旧值缺省=Lax)。
|
|
25575
|
+
// 跨端口共享 cookie 是设计意图(回环同源,端口不参与 site 判定),故须 Strict 杜绝跨站携带。
|
|
25785
25576
|
"Set-Cookie": cookie + "; Path=/; HttpOnly; SameSite=Strict",
|
|
25786
25577
|
"Location": "http://127.0.0.1:" + it.port + "/"
|
|
25787
25578
|
});
|
|
@@ -26095,7 +25886,7 @@ var require_router_table = __commonJS({
|
|
|
26095
25886
|
require_instances(),
|
|
26096
25887
|
require_relay(),
|
|
26097
25888
|
require_shell2()
|
|
26098
|
-
// 桌面壳更新安全网(/shell
|
|
25889
|
+
// 桌面壳更新安全网(/shell/*)
|
|
26099
25890
|
];
|
|
26100
25891
|
module2.exports = { API_DOMAINS };
|
|
26101
25892
|
}
|
|
@@ -26995,8 +26786,8 @@ var require_ports3 = __commonJS({
|
|
|
26995
26786
|
bindingPreferred: !!o.bindingPreferred,
|
|
26996
26787
|
onBindingLost: o.onBindingLost,
|
|
26997
26788
|
reclaimCmdMark: "lan-daemon.js",
|
|
26998
|
-
// configPath 为空时 reclaimCfg='',probe.reclaimByCmdMark 按
|
|
26999
|
-
//
|
|
26789
|
+
// configPath 为空时 reclaimCfg='',probe.reclaimByCmdMark 按 fail-closed 不回收:
|
|
26790
|
+
// 宁可留占用走冲突分支,也不按 cmdMark 全量误杀同名进程。
|
|
27000
26791
|
reclaimCfg: o.configPath || "",
|
|
27001
26792
|
waitMs: 8e3
|
|
27002
26793
|
});
|
|
@@ -27217,8 +27008,8 @@ var require_frp = __commonJS({
|
|
|
27217
27008
|
this._sumCache = null;
|
|
27218
27009
|
this._hardenPermissions();
|
|
27219
27010
|
}
|
|
27220
|
-
/**
|
|
27221
|
-
*
|
|
27011
|
+
/** frpc.toml 含 auth.token 明文、frp.json 同含 token:新写入已用 0600,
|
|
27012
|
+
* 但历史遗留文件可能是早期以默认 umask 写出的 0644/0664,故启动时补加固。 */
|
|
27222
27013
|
_hardenPermissions() {
|
|
27223
27014
|
try {
|
|
27224
27015
|
const fp = platform.fileProtect;
|
|
@@ -27254,19 +27045,18 @@ var require_frp = __commonJS({
|
|
|
27254
27045
|
installed: fs2.existsSync(this.binPath),
|
|
27255
27046
|
running: !!(this.child && this.child.pid),
|
|
27256
27047
|
pid: this.child ? this.child.pid : null,
|
|
27257
|
-
// API 面绝不回显 authToken
|
|
27258
|
-
//
|
|
27259
|
-
//
|
|
27048
|
+
// API 面绝不回显 authToken 明文,只报 authTokenSet(与 access.js「只报 configured」同规)。
|
|
27049
|
+
// normalizeFrpSettings 是 patch 归并:字段缺省(undefined)= 保留现值,显式 '' = 清除;
|
|
27050
|
+
// 故 UI 想保留现值必须省略字段而不是留空。
|
|
27260
27051
|
settings: { serverAddr: s.serverAddr, serverPort: s.serverPort, user: s.user, authTokenSet: !!s.authToken },
|
|
27261
27052
|
logTail: this.logTail.slice(-20)
|
|
27262
27053
|
};
|
|
27263
27054
|
}
|
|
27264
|
-
/**
|
|
27055
|
+
/** wan 实例映射为远程端口;文本生成委托纯函数 core.buildFrpcToml。 */
|
|
27265
27056
|
buildConfig(settings, instances) {
|
|
27266
27057
|
return buildFrpcToml(settings, instances);
|
|
27267
27058
|
}
|
|
27268
|
-
/**
|
|
27269
|
-
* frpc 生命周期单一条件 = 是否存在 wan 隧道(count>0);无全局总闸。 */
|
|
27059
|
+
/** 受管清单变化时调用:重写配置,运行中则平滑重启(生命周期条件见类头:是否存在 wan 隧道)。 */
|
|
27270
27060
|
syncFromInstances(instances) {
|
|
27271
27061
|
const settings = this.loadSettings();
|
|
27272
27062
|
const { text, count } = this.buildConfig(settings, instances);
|
|
@@ -27335,7 +27125,6 @@ var require_frp = __commonJS({
|
|
|
27335
27125
|
this.logger.info && this.logger.info("frpc started pid=" + child.pid);
|
|
27336
27126
|
return { ok: true, pid: child.pid };
|
|
27337
27127
|
}
|
|
27338
|
-
/** 非预期退出后的有界退避重启。 */
|
|
27339
27128
|
_scheduleRestart() {
|
|
27340
27129
|
if (this._restartTimer) return;
|
|
27341
27130
|
if (!this._lastCount) return;
|
|
@@ -27419,7 +27208,6 @@ var require_frp = __commonJS({
|
|
|
27419
27208
|
_download(url, report) {
|
|
27420
27209
|
return download(url, report);
|
|
27421
27210
|
}
|
|
27422
|
-
/** 安装 frpc(下载 + 完整性校验 + 解压)。 */
|
|
27423
27211
|
async install(onProgress) {
|
|
27424
27212
|
const cache = this._sumCache || (this._sumCache = {});
|
|
27425
27213
|
return installFrpc({
|
|
@@ -27682,7 +27470,7 @@ var require_ops5 = __commonJS({
|
|
|
27682
27470
|
localAddresses() {
|
|
27683
27471
|
return managed.localAddresses();
|
|
27684
27472
|
}
|
|
27685
|
-
/**
|
|
27473
|
+
/** 持久化:优先注入的 persist 路由,否则回退沙箱 instances.save()。 */
|
|
27686
27474
|
_saveAll() {
|
|
27687
27475
|
if (typeof this.persist === "function") {
|
|
27688
27476
|
try {
|
|
@@ -27699,15 +27487,15 @@ var require_ops5 = __commonJS({
|
|
|
27699
27487
|
}
|
|
27700
27488
|
}
|
|
27701
27489
|
}
|
|
27702
|
-
/** 受管 DSH
|
|
27490
|
+
/** 受管 DSH 合成清单(沙箱 + 原生主干 main)。 */
|
|
27703
27491
|
_allManaged() {
|
|
27704
27492
|
return managed.allManaged({ instances: this.instances, mainOf: this.mainOf });
|
|
27705
27493
|
}
|
|
27706
|
-
/** 合成查找:按 id
|
|
27494
|
+
/** 合成查找:按 id 取首个匹配。 */
|
|
27707
27495
|
_findManaged(id) {
|
|
27708
27496
|
return managed.findManaged(this._allManaged(), id);
|
|
27709
27497
|
}
|
|
27710
|
-
/** frpc
|
|
27498
|
+
/** frpc 子进程句柄只读访问器:不暴露 frp 私有对象,供 daemon 优雅停机等待其退出。 */
|
|
27711
27499
|
frpChild() {
|
|
27712
27500
|
return this.frp && this.frp.child || null;
|
|
27713
27501
|
}
|
|
@@ -27751,7 +27539,7 @@ var require_ops5 = __commonJS({
|
|
|
27751
27539
|
wanPort: proxy ? proxy.wanPort : null,
|
|
27752
27540
|
token: inst.remoteToken || "",
|
|
27753
27541
|
dshToken: this.tokenOf(inst.id) || "",
|
|
27754
|
-
//
|
|
27542
|
+
// 令牌按需从令牌池读取
|
|
27755
27543
|
running: !!this._lanServers && !!this._lanServers[inst.id],
|
|
27756
27544
|
inject,
|
|
27757
27545
|
remote
|
|
@@ -27768,7 +27556,7 @@ var require_ops5 = __commonJS({
|
|
|
27768
27556
|
});
|
|
27769
27557
|
return st;
|
|
27770
27558
|
}
|
|
27771
|
-
/** frpc 操作门面(settings/install
|
|
27559
|
+
/** frpc 操作门面(settings/install 统一入口)。 */
|
|
27772
27560
|
async frpAction(action, body) {
|
|
27773
27561
|
const j = body || {};
|
|
27774
27562
|
if (action === "settings") {
|
|
@@ -27792,14 +27580,13 @@ var require_ops5 = __commonJS({
|
|
|
27792
27580
|
}
|
|
27793
27581
|
return { ok: false, error: "\u672A\u77E5 frp \u64CD\u4F5C: " + action };
|
|
27794
27582
|
}
|
|
27795
|
-
/**
|
|
27583
|
+
/** 受管清单是否有 wan 意图:含尚未建立代理的实例(磁盘态即算)。 */
|
|
27796
27584
|
_hasWanIntent() {
|
|
27797
27585
|
return this._allManaged().some((x) => normalizeRemoteMode(x.remoteMode) === "wan");
|
|
27798
27586
|
}
|
|
27799
27587
|
/** 实例变化后同步 frpc 配置与进程(尽力而为,不抛异常影响主流程)。
|
|
27800
|
-
*
|
|
27801
|
-
*
|
|
27802
|
-
* 边界重现公网零认证暴露。故建隧道前对每个 wan 项复用同一事实源复校,不过闸即跳过该隧道。 */
|
|
27588
|
+
* 建隧道前复校 wan 闸(core.validateWanAccess,与写入口同一实现):冷启动直接按磁盘态组装隧道,
|
|
27589
|
+
* 历史遗留「wan 态但令牌为空」会绕开写侧闸,在网络边界重现公网零认证暴露——不过闸即跳过该隧道。 */
|
|
27803
27590
|
syncFrpc() {
|
|
27804
27591
|
if (!this.frp) return;
|
|
27805
27592
|
try {
|
|
@@ -27821,9 +27608,8 @@ var require_ops5 = __commonJS({
|
|
|
27821
27608
|
}
|
|
27822
27609
|
}
|
|
27823
27610
|
/** 远程代理对账:注册只与「remoteMode!=off」绑定;relay 运行 = 目标存活;孤儿注册移除。
|
|
27824
|
-
*
|
|
27825
|
-
* 同刻只允许一轮在跑,后续调用复用同一在途 Promise
|
|
27826
|
-
* 刻意不加 async:async 会把返回值包一层新 Promise,单飞身份丢失。 */
|
|
27611
|
+
* 单飞:内部逐实例串行 await targetReachable(每个最多 600ms)而被 2s 节拍反复触发,
|
|
27612
|
+
* 同刻只允许一轮在跑,后续调用复用同一在途 Promise。刻意不加 async——包一层 Promise 会丢单飞身份。 */
|
|
27827
27613
|
reconcile() {
|
|
27828
27614
|
if (this._reconcileInFlight) return this._reconcileInFlight;
|
|
27829
27615
|
this._reconcileInFlight = this._reconcileOnce().catch((e) => {
|
|
@@ -27833,25 +27619,23 @@ var require_ops5 = __commonJS({
|
|
|
27833
27619
|
});
|
|
27834
27620
|
return this._reconcileInFlight;
|
|
27835
27621
|
}
|
|
27836
|
-
/** 对账主体(委托 ops/reconcile.js
|
|
27622
|
+
/** 对账主体(委托 ops/reconcile.js);方法名不可改:单飞包装与门禁按此名调用。 */
|
|
27837
27623
|
async _reconcileOnce() {
|
|
27838
27624
|
return reconcile.reconcileOnce(this);
|
|
27839
27625
|
}
|
|
27840
|
-
/** 目标实例是否可达(委托 ops/reconcile.js)。 */
|
|
27841
27626
|
targetReachable(inst) {
|
|
27842
27627
|
return reconcile.targetReachable(inst);
|
|
27843
27628
|
}
|
|
27844
|
-
/** 串行执行 syncProxy(队列)。 */
|
|
27845
27629
|
_syncProxyQueued(inst) {
|
|
27846
27630
|
return reconcile.syncProxyQueued(this, inst);
|
|
27847
27631
|
}
|
|
27848
|
-
/**
|
|
27632
|
+
/** 实例被删除时移除其局域网代理。 */
|
|
27849
27633
|
async removeProxyForInstance(instId) {
|
|
27850
27634
|
return reconcile.removeProxyForInstance(this, instId);
|
|
27851
27635
|
}
|
|
27852
27636
|
/** 同步某实例的远程访问代理:remoteMode!=off 且目标在监听时创建 relay,off 拆除。
|
|
27853
|
-
* wan
|
|
27854
|
-
* 端口绑定唯一权威是 relay
|
|
27637
|
+
* wan 不引入第二监听/第二端口(隧道口与 relay 口恒同号,见 core.buildFrpcToml);
|
|
27638
|
+
* 端口绑定唯一权威是 relay 槽位注册表,实例记录不存 wanPort 镜像。 */
|
|
27855
27639
|
async syncProxy(inst) {
|
|
27856
27640
|
if (!inst) return;
|
|
27857
27641
|
const mode = normalizeRemoteMode(inst.remoteMode);
|
|
@@ -27915,11 +27699,10 @@ var require_ops5 = __commonJS({
|
|
|
27915
27699
|
}
|
|
27916
27700
|
}
|
|
27917
27701
|
}
|
|
27918
|
-
/**
|
|
27702
|
+
/** 实例启动/停止时联动远程代理(委托 ops/reconcile.js:停止只停 relay,保留注册)。 */
|
|
27919
27703
|
async instanceStart(inst) {
|
|
27920
27704
|
return reconcile.instanceStart(this, inst);
|
|
27921
27705
|
}
|
|
27922
|
-
/** 实例停止时联动远程代理(委托 ops/reconcile.js)。 */
|
|
27923
27706
|
instanceStop(inst) {
|
|
27924
27707
|
return reconcile.instanceStop(this, inst);
|
|
27925
27708
|
}
|
|
@@ -27929,11 +27712,11 @@ var require_ops5 = __commonJS({
|
|
|
27929
27712
|
_stopLanServer(id) {
|
|
27930
27713
|
return lanServers.stopLanServer(this, id);
|
|
27931
27714
|
}
|
|
27932
|
-
/**
|
|
27715
|
+
/** 守卫优雅退出:停全部 relay 与 frpc。 */
|
|
27933
27716
|
shutdown() {
|
|
27934
27717
|
return lanServers.shutdown(this);
|
|
27935
27718
|
}
|
|
27936
|
-
/**
|
|
27719
|
+
/** 令牌变化下发到既有 relay(热换 DSH 会话 cookie);本域不持久化令牌。 */
|
|
27937
27720
|
applyToken(instId) {
|
|
27938
27721
|
return lanServers.applyToken(this, instId);
|
|
27939
27722
|
}
|