@shgroup/dsh-serenity-hooks 1.28.0 → 1.28.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/dsh.plugin.json +1 -1
- package/lib/client.js +12 -37
- package/lib/config-ops.d.ts +13 -0
- package/lib/gateway-dsh-auth.d.ts +47 -0
- package/lib/gateway-proxy.d.ts +18 -1
- package/lib/gateway.d.ts +2 -2
- package/lib/index.d.ts +2 -1
- package/lib/index.js +233 -74
- package/lib/seams/bootstrap.d.ts +9 -0
- package/lib/session-ops.d.ts +11 -0
- package/lib/settings-section-BfVxgCZy.js +91 -0
- package/lib/settings-section.d.ts +5 -1
- package/lib/{skiff-debug-DIf_Uenr.js → skiff-debug-CBU6T2F_.js} +14 -3
- package/package.json +18 -16
- package/lib/settings-section-DnX9TJxt.js +0 -2124
package/lib/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$2, f as readHandymanConfig, i as findGitRoot, l as pathInside, m as resolveInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-CX48YNUL.js";
|
|
3
3
|
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, l as systemPromptSource, r as isSkiffSessionId, s as roleToolWhitelist } from "./skiff-role-BTdBHyOQ.js";
|
|
4
|
-
import { C as splitModel, E as skiffRoleFor, S as requireWhitelistedModel, T as writeProgress, _ as buildRoundPrompt, a as startSkiffDebugServer, b as newStopToken, c as askSkiff, d as skiffMsmGate, f as skiffSessionInfo, g as HANDYMAN_GUIDE, h as unregisterSkiffSession, l as createSkiffAgent, m as skiffTrajectoryEnabled, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionSnapshot, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as getSkiffAgent, v as handymanProgressPaths, w as writeFailedStatus, x as readProgress, y as listActiveHandymen } from "./skiff-debug-
|
|
4
|
+
import { C as splitModel, E as skiffRoleFor, S as requireWhitelistedModel, T as writeProgress, _ as buildRoundPrompt, a as startSkiffDebugServer, b as newStopToken, c as askSkiff, d as skiffMsmGate, f as skiffSessionInfo, g as HANDYMAN_GUIDE, h as unregisterSkiffSession, l as createSkiffAgent, m as skiffTrajectoryEnabled, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionSnapshot, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as getSkiffAgent, v as handymanProgressPaths, w as writeFailedStatus, x as readProgress, y as listActiveHandymen } from "./skiff-debug-CBU6T2F_.js";
|
|
5
5
|
import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, h as runLocalStore, i as matchWeixinRoute, l as weixinSessionIdFor, m as readGitTrack, n as extractWeixinText, o as readWeixinSettings, p as localstorePath, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-DFkRf9ou.js";
|
|
6
|
-
import { n as registerSettingsSection, t as readSimpleSettings } from "./settings-section-
|
|
6
|
+
import { n as registerSettingsSection, t as readSimpleSettings } from "./settings-section-BfVxgCZy.js";
|
|
7
7
|
import { a as markdownToPlainText, c as sniffImageExt, i as getUpdates, n as downloadMedia, o as sendTextMessage, r as getConfig, s as sendTyping, t as TypingStatus } from "./weixin-api-OUCuw0Ws.js";
|
|
8
8
|
import z from "@deepseek-ai/schemastery";
|
|
9
9
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -18,6 +18,7 @@ import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
|
18
18
|
import { createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
19
19
|
import { deriveEventMessage } from "@deepseek-ai/dsh-session";
|
|
20
20
|
import { createServer, request } from "node:http";
|
|
21
|
+
import * as zlib from "node:zlib";
|
|
21
22
|
//#region src/fs-ops.ts
|
|
22
23
|
/**
|
|
23
24
|
* fs-ops.ts — cc_fs 纯操作层(零 DSH 依赖,可独立单测)
|
|
@@ -2301,7 +2302,8 @@ function waitIdle(ctx, agent) {
|
|
|
2301
2302
|
}
|
|
2302
2303
|
/** 读取会话最后一个 assistant/message 文本 */
|
|
2303
2304
|
function lastAssistantText$1(agent) {
|
|
2304
|
-
const
|
|
2305
|
+
const s = agent.session;
|
|
2306
|
+
const events = typeof s.snapshotEvents === "function" ? s.snapshotEvents() : s.events ?? [];
|
|
2305
2307
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
2306
2308
|
const e = events[i];
|
|
2307
2309
|
if (e && e.type === "assistant/message") {
|
|
@@ -2567,6 +2569,24 @@ const SESSION_ACTIONS = [
|
|
|
2567
2569
|
"summary",
|
|
2568
2570
|
"hook-develop-guide"
|
|
2569
2571
|
];
|
|
2572
|
+
/**
|
|
2573
|
+
* 读取 Session 事件序列(v1.28.1 适配 0.1.2-rc.1 补齐):rc.1 起官方 Session 类
|
|
2574
|
+
* 移除 `.events` 属性 → `snapshotEvents()` 方法(dsh-session/src/session.ts:
|
|
2575
|
+
* `snapshotEvents(fromSeq, toSeqExclusive)`)。插件早期代码多处裸读 `.events`
|
|
2576
|
+
* (经 `as unknown as { events? }` 断言绕过 typecheck),运行时静默 undefined——
|
|
2577
|
+
* 造成 first-anchor 每轮重插 / SESSION 激活恢复失效 / rebuild 定位错乱。
|
|
2578
|
+
* 统一收敛到本 helper:snapshotEvents() 优先(rc.1 真实形态),`.events` 兜底
|
|
2579
|
+
* (测试替身/旧运行时)。所有消费方一律经此读取,禁止再裸读 `.events`。
|
|
2580
|
+
* 泛型 T:调用方按需声明事件形状(如 `SessionEvent`),unknown 默认。
|
|
2581
|
+
*/
|
|
2582
|
+
function sessionEvents(session) {
|
|
2583
|
+
const s = session;
|
|
2584
|
+
if (!s) return [];
|
|
2585
|
+
if (typeof s.snapshotEvents === "function") try {
|
|
2586
|
+
return s.snapshotEvents() ?? [];
|
|
2587
|
+
} catch {}
|
|
2588
|
+
return s.events ?? [];
|
|
2589
|
+
}
|
|
2570
2590
|
const SESSION_MD = "SESSION.md";
|
|
2571
2591
|
const ARCHIVE_DIR_NAME = "_archived";
|
|
2572
2592
|
const HEALTH_STALE_DAYS = 7;
|
|
@@ -3524,6 +3544,17 @@ const DEFAULT_COMPACTION_TOOLS = [
|
|
|
3524
3544
|
*/
|
|
3525
3545
|
const DEFAULT_ANCHOR_MESSAGES = ["You are the operator agent of Serenity — a cognitive container governed by the Abstract Cognitive Container (ACC) protocol.\nWe operate under the Explicit Abstraction Principle (EAP): the functional value of a thought equals its external reconstructability — every output we produce must be explicit (E↑), reconstructable (R↓), and stable (S↑).\nThe personal pronoun is us/we.\nWe anchor first, then act: the abstract layer precedes the concrete.\nPlease simply reply \"acknowledge\" — no action needed.", "Before we proceed, align on how we work:\n1. We read before we write — every decision grounds in what already exists in the container.\n2. Every output records its reasoning (R↓): decisions carry reasons and alternatives.\n3. We never jump levels — abstract layer first, then specifics.\n4. Every artifact we create is a durable cognitive anchor for the work that follows.\n5. We keep the container's state coherent (SESSION.md) as we advance.\nPlease simply reply \"acknowledge\" — no action needed."];
|
|
3526
3546
|
/**
|
|
3547
|
+
* 根会话锚定重入判定(v1.28.1 提取导出以便单测):会话是否已有真实用户消息。
|
|
3548
|
+
* resume/续跑的会话已有对话历史 → **不重锚**(first-anchor 只注入一次)。
|
|
3549
|
+
*
|
|
3550
|
+
* 0.1.2-rc.1 适配教训:裸读 `session.events`(经类型断言)恒 undefined →
|
|
3551
|
+
* `!undefined` 恒 true → 有历史也永不跳过 → 任何情况发消息都重插 first-anchor。
|
|
3552
|
+
* 统一经 sessionEvents() 读取(snapshotEvents() 优先,.events 兜底)。
|
|
3553
|
+
*/
|
|
3554
|
+
function hasUserMessageHistory(session) {
|
|
3555
|
+
return sessionEvents(session).some((event) => event.type === "user/message");
|
|
3556
|
+
}
|
|
3557
|
+
/**
|
|
3527
3558
|
* 构建一个 epoch 感知晋升跟踪器(纯逻辑,可单测)。
|
|
3528
3559
|
* requiredSignals:晋升所需信号数(boundary 后累计;默认 1)。
|
|
3529
3560
|
* 多轮锚定(v4):两轮递进锚定时 requiredSignals = 锚定轮数——
|
|
@@ -3552,8 +3583,8 @@ function createEpochPromotion(promoteEvents, requiredSignals = 1, maxRoundsFallb
|
|
|
3552
3583
|
let boundary = -1;
|
|
3553
3584
|
let signalCount = 0;
|
|
3554
3585
|
let rounds = 0;
|
|
3555
|
-
const events = session
|
|
3556
|
-
if (
|
|
3586
|
+
const events = sessionEvents(session);
|
|
3587
|
+
if (events.length > 0) for (const event of events) {
|
|
3557
3588
|
const e = event;
|
|
3558
3589
|
const seq = typeof e.seq === "number" ? e.seq : 0;
|
|
3559
3590
|
if (e.type === "compaction/end") {
|
|
@@ -3695,7 +3726,7 @@ function registerBootstrap(ctx) {
|
|
|
3695
3726
|
const sid = typeof session?.id === "string" ? session.id : void 0;
|
|
3696
3727
|
if (sid !== void 0 && (sid.startsWith("handyman-") || isSkiffSessionId(sid))) return;
|
|
3697
3728
|
if (depth === 0) {
|
|
3698
|
-
if (session
|
|
3729
|
+
if (hasUserMessageHistory(session)) return;
|
|
3699
3730
|
} else if (sid !== void 0 && anchoredSessions.has(sid)) return;
|
|
3700
3731
|
if (message?.source?.kind === "plugin") return;
|
|
3701
3732
|
const inbox = agent.inbox;
|
|
@@ -3834,8 +3865,8 @@ function parseAnchorMdPath(session) {
|
|
|
3834
3865
|
try {
|
|
3835
3866
|
const nodes = [...session.surface.nodes];
|
|
3836
3867
|
if (nodes.length === 0) return null;
|
|
3837
|
-
const events = session
|
|
3838
|
-
if (
|
|
3868
|
+
const events = sessionEvents(session);
|
|
3869
|
+
if (events.length === 0) return null;
|
|
3839
3870
|
const event = events[nodes[0]];
|
|
3840
3871
|
if (!event) return null;
|
|
3841
3872
|
const message = deriveEventMessage(event);
|
|
@@ -3860,8 +3891,8 @@ function sessionNameFromMdPath(mdPath) {
|
|
|
3860
3891
|
function resolveSessionMdPath(root, scope, session) {
|
|
3861
3892
|
const candidates = [];
|
|
3862
3893
|
candidates.push(getActiveSessionInfo(scope)?.mdPath ?? null);
|
|
3863
|
-
const events = session
|
|
3864
|
-
if (
|
|
3894
|
+
const events = sessionEvents(session);
|
|
3895
|
+
if (events.length > 0) candidates.push(parseSessionContextFromEvents(events)?.mdPath ?? null);
|
|
3865
3896
|
candidates.push(parseAnchorMdPath(session));
|
|
3866
3897
|
candidates.push(findLatestActiveSessionMd(root));
|
|
3867
3898
|
for (const c of candidates) {
|
|
@@ -3953,8 +3984,9 @@ function performRebuild(session, pending, meter) {
|
|
|
3953
3984
|
if (nodes.length === 0) return false;
|
|
3954
3985
|
if (meter) {
|
|
3955
3986
|
let shadowedTokenCount = 0;
|
|
3987
|
+
const events = sessionEvents(session);
|
|
3956
3988
|
for (const seq of nodes) {
|
|
3957
|
-
const event =
|
|
3989
|
+
const event = events[seq];
|
|
3958
3990
|
if (!event) continue;
|
|
3959
3991
|
const message = deriveEventMessage(event);
|
|
3960
3992
|
if (message) shadowedTokenCount += meter.estimateMessage(message);
|
|
@@ -4765,8 +4797,7 @@ function resolveTargetAgent(ctx, mdPath) {
|
|
|
4765
4797
|
*/
|
|
4766
4798
|
function readSessionTitle(session) {
|
|
4767
4799
|
try {
|
|
4768
|
-
const events = session
|
|
4769
|
-
if (!Array.isArray(events)) return null;
|
|
4800
|
+
const events = sessionEvents(session);
|
|
4770
4801
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
4771
4802
|
const e = events[i];
|
|
4772
4803
|
if (e?.type === "session/title" && typeof e.data?.title === "string" && e.data.title.trim() !== "") return e.data.title.trim();
|
|
@@ -5603,6 +5634,18 @@ function toWire(settings) {
|
|
|
5603
5634
|
};
|
|
5604
5635
|
}
|
|
5605
5636
|
/**
|
|
5637
|
+
* 已知工作区投影(v1.28.0 适配 0.1.2-rc.1 A2 方案 A′):
|
|
5638
|
+
* rc.1 workspace.list unary 删除 → AccountsEditor 白名单下拉的数据源改走 gateway 自有
|
|
5639
|
+
* /serenity/config 的 knownWorkspaces(host workspaceRegistry.list() 投影,白名单过滤)。
|
|
5640
|
+
* 纯函数可单测。allowPrefixes 空 = 全部放行(向后兼容默认)。
|
|
5641
|
+
*/
|
|
5642
|
+
function projectKnownWorkspaces(workspaces, allowPrefixes) {
|
|
5643
|
+
return workspaces.filter((w) => typeof w.path === "string" && w.path !== "").filter((w) => allowPrefixes.length === 0 || allowPrefixes.some((p) => w.path.startsWith(p))).map((w) => ({
|
|
5644
|
+
path: w.path,
|
|
5645
|
+
title: typeof w.title === "string" && w.title !== "" ? w.title : w.path
|
|
5646
|
+
}));
|
|
5647
|
+
}
|
|
5648
|
+
/**
|
|
5606
5649
|
* wire → 持久化(面板 PUT 用):
|
|
5607
5650
|
* accounts 元素可选带 `pass`:非空 → 重新 hash;空/缺省 → 保留现有 hash(按 id 匹配)。
|
|
5608
5651
|
* 新账号(id 不在现有)必须带非空 pass,否则抛错(无法生成 hash)。
|
|
@@ -6264,8 +6307,7 @@ function shouldAutoRestore(agent) {
|
|
|
6264
6307
|
*/
|
|
6265
6308
|
function shouldRestoreActive(agent) {
|
|
6266
6309
|
if (!shouldAutoRestore(agent)) return false;
|
|
6267
|
-
|
|
6268
|
-
return Array.isArray(events) && events.length > 0;
|
|
6310
|
+
return sessionEvents(agent.session).length > 0;
|
|
6269
6311
|
}
|
|
6270
6312
|
function registerContext(ctx, opts = {}) {
|
|
6271
6313
|
const configPaths = opts.configPaths ?? DEFAULT_SERENITY_CONFIG_PATHS;
|
|
@@ -6280,7 +6322,7 @@ function registerContext(ctx, opts = {}) {
|
|
|
6280
6322
|
const scope = agentScope(agent);
|
|
6281
6323
|
if (shouldRestoreActive(agent) && getActiveSessionInfo(scope) === null) try {
|
|
6282
6324
|
if (loadSerenityConfig(root, configPaths).hooks?.autoRestoreSession ?? true) {
|
|
6283
|
-
const info = parseSessionContextFromEvents(agent.session
|
|
6325
|
+
const info = parseSessionContextFromEvents(sessionEvents(agent.session));
|
|
6284
6326
|
if (info) {
|
|
6285
6327
|
const abs = info.mdPath.startsWith(root) ? info.mdPath : resolve(root, info.mdPath);
|
|
6286
6328
|
if (existsSync(abs)) {
|
|
@@ -6635,7 +6677,16 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6635
6677
|
return;
|
|
6636
6678
|
}
|
|
6637
6679
|
if (req.method === "GET") {
|
|
6638
|
-
|
|
6680
|
+
const settings = readAdvancedSettings();
|
|
6681
|
+
let known = [];
|
|
6682
|
+
try {
|
|
6683
|
+
const registry = ctx.get?.("workspaceRegistry");
|
|
6684
|
+
known = projectKnownWorkspaces(registry?.list?.() ?? [], settings.gateway.workspaces);
|
|
6685
|
+
} catch {}
|
|
6686
|
+
sendJson$1(res, 200, {
|
|
6687
|
+
config: toWire(settings),
|
|
6688
|
+
knownWorkspaces: known
|
|
6689
|
+
});
|
|
6639
6690
|
return;
|
|
6640
6691
|
}
|
|
6641
6692
|
if (req.method === "PUT") {
|
|
@@ -6668,7 +6719,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6668
6719
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
6669
6720
|
});
|
|
6670
6721
|
const root = findSerenityRoot(workspace) ?? "";
|
|
6671
|
-
const { discoverCccs } = await import("./skiff-debug-
|
|
6722
|
+
const { discoverCccs } = await import("./skiff-debug-CBU6T2F_.js").then((n) => n.i);
|
|
6672
6723
|
sendJson$1(res, 200, { cccs: await discoverCccs(ctx, root) });
|
|
6673
6724
|
} catch (err) {
|
|
6674
6725
|
sendJson$1(res, 400, { error: err.message ?? String(err) });
|
|
@@ -6698,7 +6749,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6698
6749
|
return;
|
|
6699
6750
|
}
|
|
6700
6751
|
const settings = readAdvancedSettings();
|
|
6701
|
-
const { readSimpleSettings } = await import("./settings-section-
|
|
6752
|
+
const { readSimpleSettings } = await import("./settings-section-BfVxgCZy.js").then((n) => n.r);
|
|
6702
6753
|
const simple = readSimpleSettings();
|
|
6703
6754
|
const allowed = settings.publicAsk.allowed;
|
|
6704
6755
|
const port = simple.acpHttpPort ?? 3100;
|
|
@@ -7381,25 +7432,6 @@ const RANDOM_UUID_POLYFILL = `<script>
|
|
|
7381
7432
|
/** 注入标记(幂等:已注入的 HTML 不重复注入) */
|
|
7382
7433
|
const POLYFILL_MARKER = "data-sp-randomuuid-polyfill";
|
|
7383
7434
|
/**
|
|
7384
|
-
* workspace.list 响应过滤(v1.22 白名单):
|
|
7385
|
-
* DSH client→server RPC 全部走 HTTP JSON(`POST /api/workspace.list`,WS 仅下行推送)。
|
|
7386
|
-
* 白名单(workspaces 路径前缀)非空 → 只保留匹配前缀的 items;
|
|
7387
|
-
* 空 = 全部允许(默认,向后兼容)。
|
|
7388
|
-
*/
|
|
7389
|
-
function filterWorkspaceList(body, allowPrefixes) {
|
|
7390
|
-
if (allowPrefixes.length === 0) return body;
|
|
7391
|
-
try {
|
|
7392
|
-
const parsed = JSON.parse(body);
|
|
7393
|
-
const value = parsed?.result?.value;
|
|
7394
|
-
if (parsed?.result?.ok !== true || !value || !Array.isArray(value.items)) return body;
|
|
7395
|
-
const keep = (path) => typeof path === "string" && allowPrefixes.some((p) => path.startsWith(p));
|
|
7396
|
-
value.items = value.items.filter((item) => keep(item.path));
|
|
7397
|
-
return JSON.stringify(parsed);
|
|
7398
|
-
} catch {
|
|
7399
|
-
return body;
|
|
7400
|
-
}
|
|
7401
|
-
}
|
|
7402
|
-
/**
|
|
7403
7435
|
* 校验 workspace.create 请求路径是否在白名单内(v1.22):
|
|
7404
7436
|
* 白名单非空且路径不匹配 → 拒绝(由调用方构造 403 RPC 响应)。
|
|
7405
7437
|
*/
|
|
@@ -7422,7 +7454,9 @@ function workspaceDenyResponse(rpcId) {
|
|
|
7422
7454
|
}
|
|
7423
7455
|
});
|
|
7424
7456
|
}
|
|
7425
|
-
/**
|
|
7457
|
+
/**
|
|
7458
|
+
* 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
|
|
7459
|
+
*/
|
|
7426
7460
|
function injectPolyfillHtml(html) {
|
|
7427
7461
|
if (html.includes(POLYFILL_MARKER)) return html;
|
|
7428
7462
|
const head = RANDOM_UUID_POLYFILL.replace("<script>", `<script ${POLYFILL_MARKER}="1">`);
|
|
@@ -7430,6 +7464,37 @@ function injectPolyfillHtml(html) {
|
|
|
7430
7464
|
return `${head}\n${html}`;
|
|
7431
7465
|
}
|
|
7432
7466
|
/**
|
|
7467
|
+
* 反代 HTML 响应注入变换(v1.28.2 修复,S142 白屏根因):
|
|
7468
|
+
* 上游按 Accept-Encoding 可能返回 gzip/br/deflate 压缩的 HTML——注入必须在**明文**上进行,
|
|
7469
|
+
* 把压缩字节 toString 当文本注入会破坏压缩流(找不到 </head> → polyfill 前置 → HTML 损坏
|
|
7470
|
+
* → 浏览器白屏,用户实测 https://dsh.notfoundhome.cc 登录后白屏)。
|
|
7471
|
+
*
|
|
7472
|
+
* @param raw - 上游响应完整 body(可能压缩)
|
|
7473
|
+
* @param upstreamHeaders - 上游响应头(node IncomingHttpHeaders 形态)
|
|
7474
|
+
* @returns { body, headers } 注入后的明文 body + 修正后的响应头(去 content-encoding/transfer-encoding、
|
|
7475
|
+
* 重算 content-length);解压失败返回 null(调用方应原样透传避免破坏)
|
|
7476
|
+
*/
|
|
7477
|
+
function transformHtmlForProxy(raw, upstreamHeaders) {
|
|
7478
|
+
let body = raw;
|
|
7479
|
+
const enc = String(upstreamHeaders["content-encoding"] ?? "").toLowerCase().trim();
|
|
7480
|
+
const outHeaders = { ...upstreamHeaders };
|
|
7481
|
+
if (enc === "gzip" || enc === "x-gzip" || enc === "deflate" || enc === "br") try {
|
|
7482
|
+
if (enc === "br") body = zlib.brotliDecompressSync(body);
|
|
7483
|
+
else if (enc === "deflate") body = zlib.inflateSync(body);
|
|
7484
|
+
else body = zlib.gunzipSync(body);
|
|
7485
|
+
delete outHeaders["content-encoding"];
|
|
7486
|
+
} catch {
|
|
7487
|
+
return null;
|
|
7488
|
+
}
|
|
7489
|
+
const transformed = injectPolyfillHtml(body.toString("utf-8"));
|
|
7490
|
+
outHeaders["content-length"] = Buffer.byteLength(transformed);
|
|
7491
|
+
delete outHeaders["transfer-encoding"];
|
|
7492
|
+
return {
|
|
7493
|
+
body: transformed,
|
|
7494
|
+
headers: outHeaders
|
|
7495
|
+
};
|
|
7496
|
+
}
|
|
7497
|
+
/**
|
|
7433
7498
|
* 反代请求头构造(v1.22.1 信任栅栏修复,纯逻辑可测):
|
|
7434
7499
|
* DSH isTrustedApiRequest 要求 Origin.host === Host.host——Host 改写为 loopback 后
|
|
7435
7500
|
* Origin 必须同步改写(浏览器 POST 必带 Origin,透传外部地址 → 403)。
|
|
@@ -7444,6 +7509,69 @@ function buildProxyHeaders(reqHeaders, mainPort, bodyOverride) {
|
|
|
7444
7509
|
return headers;
|
|
7445
7510
|
}
|
|
7446
7511
|
//#endregion
|
|
7512
|
+
//#region src/gateway-dsh-auth.ts
|
|
7513
|
+
/** 从一组 set-cookie 头提取 `dsh-auth-*` cookie(browser-auth sessionCookie 首段) */
|
|
7514
|
+
function pickDshCookie(setCookieHeader) {
|
|
7515
|
+
const entries = Array.isArray(setCookieHeader) ? setCookieHeader : setCookieHeader === void 0 ? [] : [setCookieHeader];
|
|
7516
|
+
for (const entry of entries) {
|
|
7517
|
+
const first = entry.split(";", 1)[0].trim();
|
|
7518
|
+
if (first.startsWith("dsh-auth-") && first.includes("=")) return first;
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
/**
|
|
7522
|
+
* 把 dsh browser cookie 合并进现有 Cookie 头(保持外部既有 cookie——serenity_session 等)。
|
|
7523
|
+
* 现有头可为 string / string[](node 头形态);无现有 → 只返回 dsh cookie。
|
|
7524
|
+
*/
|
|
7525
|
+
function mergeCookieHeader(existing, dshCookie) {
|
|
7526
|
+
const existingStr = Array.isArray(existing) ? existing.join("; ") : typeof existing === "string" && existing !== "" ? existing : void 0;
|
|
7527
|
+
return existingStr !== void 0 ? `${existingStr}; ${dshCookie}` : dshCookie;
|
|
7528
|
+
}
|
|
7529
|
+
/**
|
|
7530
|
+
* 内存换取 authority 绑定的 dsh browser cookie(S142 拍板方案核心)。
|
|
7531
|
+
* 用官方通道在进程内完成 token→cookie 交换:构造一个对 `http://<authority>/?token=…`
|
|
7532
|
+
* 的 index 请求 → connection.authorizeIndex 校验 token → 写 set-cookie → 提取 cookie。
|
|
7533
|
+
* @returns cookie 头值(如 `dsh-auth-xxx=v1.…`);authority/connection 异常 → undefined
|
|
7534
|
+
*/
|
|
7535
|
+
function exchangeDshCookie(connection, authority) {
|
|
7536
|
+
try {
|
|
7537
|
+
const url = new URL(connection.authenticatedUrl(`http://${authority}`));
|
|
7538
|
+
if (url.searchParams.get("token") === null) return void 0;
|
|
7539
|
+
const headers = { host: authority };
|
|
7540
|
+
const request = {
|
|
7541
|
+
url: `${url.pathname}${url.search}`,
|
|
7542
|
+
method: "GET",
|
|
7543
|
+
headers
|
|
7544
|
+
};
|
|
7545
|
+
let captured;
|
|
7546
|
+
const response = {
|
|
7547
|
+
writeHead(_status, headers) {
|
|
7548
|
+
if (headers !== void 0) captured = headers["set-cookie"];
|
|
7549
|
+
return response;
|
|
7550
|
+
},
|
|
7551
|
+
end: () => response
|
|
7552
|
+
};
|
|
7553
|
+
connection.authorizeIndex(request, response);
|
|
7554
|
+
return pickDshCookie(captured);
|
|
7555
|
+
} catch {
|
|
7556
|
+
return;
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
/**
|
|
7560
|
+
* 构建一个带内存缓存的 dsh cookie 提供者(S142 拍板:内存缓存,不落盘)。
|
|
7561
|
+
* 惰性换取:首次调用才向 connection 换;失败返回 undefined(不缓存失败——下次重试)。
|
|
7562
|
+
* 调用方可周期性失效缓存重换(cookie 30 天有效;进程重启 token 仍在 → 换一次长期可用)。
|
|
7563
|
+
*/
|
|
7564
|
+
function createDshCookieProvider(connection, authority) {
|
|
7565
|
+
let cached;
|
|
7566
|
+
return () => {
|
|
7567
|
+
if (connection === void 0) return void 0;
|
|
7568
|
+
if (cached !== void 0) return cached;
|
|
7569
|
+
const cookie = exchangeDshCookie(connection, authority);
|
|
7570
|
+
if (cookie !== void 0) cached = cookie;
|
|
7571
|
+
return cookie;
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
//#endregion
|
|
7447
7575
|
//#region src/gateway.ts
|
|
7448
7576
|
/** 读取请求体(≤ maxBytes;超限 reject) */
|
|
7449
7577
|
function readBody$1(req, maxBytes) {
|
|
@@ -7466,7 +7594,7 @@ function readBody$1(req, maxBytes) {
|
|
|
7466
7594
|
* @param config - 监听/代理配置。
|
|
7467
7595
|
* @param getAccounts - 运行时读取账号列表(plugin 全局文件;gateway.enabled 开关在调用方判)。
|
|
7468
7596
|
*/
|
|
7469
|
-
function startGateway(config, getAccounts) {
|
|
7597
|
+
function startGateway(config, getAccounts, getDshCookie) {
|
|
7470
7598
|
const { host, port, cookieName, mainPort, loginDelayMs } = config;
|
|
7471
7599
|
const allowWorkspaces = config.allowWorkspaces ?? [];
|
|
7472
7600
|
const cookieSecure = config.cookieSecure === true;
|
|
@@ -7475,6 +7603,18 @@ function startGateway(config, getAccounts) {
|
|
|
7475
7603
|
/** 校验请求 Cookie 是否含有效 token(v1.22.4:滑动过期 + 返回会话供审计) */
|
|
7476
7604
|
const authed = (req) => validateToken(cookieValue(req.headers.cookie, cookieName));
|
|
7477
7605
|
/**
|
|
7606
|
+
* 构造上游请求头(buildProxyHeaders 基准 + dsh browser cookie 注入)。
|
|
7607
|
+
* v1.28.2(0.1.2-rc.1 BrowserAuth 适配):主端口 /api + index 需 dsh browser cookie。
|
|
7608
|
+
* 反代 Host 已改写 loopback(过信任栅栏)→ 只缺 cookie → 注入内存换取 cookie
|
|
7609
|
+
*(createDshCookieProvider 官方通道,零落盘)。无 cookie(旧 dsh/换取失败)→ 不注入向后兼容。
|
|
7610
|
+
*/
|
|
7611
|
+
const upstreamHeaders = (req, bodyOverride) => {
|
|
7612
|
+
const headers = buildProxyHeaders(req.headers, mainPort, bodyOverride);
|
|
7613
|
+
const dshCookie = getDshCookie?.();
|
|
7614
|
+
if (dshCookie !== void 0) headers.cookie = mergeCookieHeader(headers.cookie, dshCookie);
|
|
7615
|
+
return headers;
|
|
7616
|
+
};
|
|
7617
|
+
/**
|
|
7478
7618
|
* 反向代理:改写 Host + Origin 头 → 主端口(loopback 过信任栅栏)。
|
|
7479
7619
|
* v1.22.1 修复:DSH 信任栅栏(api-request-trust.ts isTrustedApiRequest)要求
|
|
7480
7620
|
* **Origin.host === Host.host**——仅改写 Host(127.0.0.1:3080)而 Origin 透传
|
|
@@ -7485,9 +7625,7 @@ function startGateway(config, getAccounts) {
|
|
|
7485
7625
|
* @param bodyOverride - workspace.create 已读 body 时的重放(白名单检查后转发)
|
|
7486
7626
|
*/
|
|
7487
7627
|
const proxy = (req, res, bodyOverride) => {
|
|
7488
|
-
const
|
|
7489
|
-
const method = req.method === "POST" && url.pathname.startsWith("/api/") ? url.pathname.slice(5) : null;
|
|
7490
|
-
const headers = buildProxyHeaders(req.headers, mainPort, bodyOverride);
|
|
7628
|
+
const headers = upstreamHeaders(req, bodyOverride);
|
|
7491
7629
|
const target = request({
|
|
7492
7630
|
host: "127.0.0.1",
|
|
7493
7631
|
port: mainPort,
|
|
@@ -7501,32 +7639,15 @@ function startGateway(config, getAccounts) {
|
|
|
7501
7639
|
const chunks = [];
|
|
7502
7640
|
upstream.on("data", (c) => chunks.push(c));
|
|
7503
7641
|
upstream.on("end", () => {
|
|
7504
|
-
|
|
7505
|
-
const
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
try {
|
|
7514
|
-
res.destroy();
|
|
7515
|
-
} catch {}
|
|
7516
|
-
});
|
|
7517
|
-
return;
|
|
7518
|
-
}
|
|
7519
|
-
if (method === "workspace.list" && status === 200 && ct.includes("application/json")) {
|
|
7520
|
-
const chunks = [];
|
|
7521
|
-
upstream.on("data", (c) => chunks.push(c));
|
|
7522
|
-
upstream.on("end", () => {
|
|
7523
|
-
const transformed = filterWorkspaceList(Buffer.concat(chunks).toString("utf-8"), allowWorkspaces);
|
|
7524
|
-
const out = {
|
|
7525
|
-
...upstream.headers,
|
|
7526
|
-
"content-length": Buffer.byteLength(transformed)
|
|
7527
|
-
};
|
|
7528
|
-
res.writeHead(status, out);
|
|
7529
|
-
res.end(transformed);
|
|
7642
|
+
let raw = Buffer.concat(chunks);
|
|
7643
|
+
const transformedRes = transformHtmlForProxy(raw, upstream.headers);
|
|
7644
|
+
if (transformedRes === null) {
|
|
7645
|
+
res.writeHead(status, upstream.headers);
|
|
7646
|
+
res.end(raw);
|
|
7647
|
+
return;
|
|
7648
|
+
}
|
|
7649
|
+
res.writeHead(status, transformedRes.headers);
|
|
7650
|
+
res.end(transformedRes.body);
|
|
7530
7651
|
});
|
|
7531
7652
|
upstream.on("error", () => {
|
|
7532
7653
|
try {
|
|
@@ -7757,7 +7878,7 @@ function startGateway(config, getAccounts) {
|
|
|
7757
7878
|
port: mainPort,
|
|
7758
7879
|
path: req.url ?? "/",
|
|
7759
7880
|
method: req.method,
|
|
7760
|
-
headers:
|
|
7881
|
+
headers: upstreamHeaders(req)
|
|
7761
7882
|
});
|
|
7762
7883
|
upstream.on("upgrade", (ures, usock, uhead) => {
|
|
7763
7884
|
usocket = usock;
|
|
@@ -7831,6 +7952,42 @@ function startGateway(config, getAccounts) {
|
|
|
7831
7952
|
}
|
|
7832
7953
|
};
|
|
7833
7954
|
}
|
|
7955
|
+
/**
|
|
7956
|
+
* 构造 dsh browser cookie 提供者(v1.28.2 0.1.2-rc.1 BrowserAuth 适配):
|
|
7957
|
+
* 经 ctx.connection(HostConnectionHandle)官方通道在内存换取 authority=127.0.0.1:<mainPort>
|
|
7958
|
+
* 的 cookie 并缓存(createDshCookieProvider 惰性换取)。connection 缺失(旧 dsh/非 web 装配)
|
|
7959
|
+
* → provider 返回 undefined → 反代不注入(旧形态无 BrowserAuth,天然兼容)。
|
|
7960
|
+
*
|
|
7961
|
+
* v1.28.2 修复:**connection 每次调用现取**(不绑定 sync 时点)——gateway sync 在 apply
|
|
7962
|
+
* 早期跑,此时 connection 服务可能尚未装配(web-app 的 URL 打印在其后);首次反代请求
|
|
7963
|
+
* 到达时 connection 必已就绪。cachedProvider 缓存换取结果(同一 connection 实例)。
|
|
7964
|
+
*/
|
|
7965
|
+
function buildDshCookieProvider(ctx, mainPort) {
|
|
7966
|
+
const authority = `127.0.0.1:${mainPort}`;
|
|
7967
|
+
let cachedProvider;
|
|
7968
|
+
let diagnosed = false;
|
|
7969
|
+
return () => {
|
|
7970
|
+
try {
|
|
7971
|
+
const connection = ctx.get?.("connection");
|
|
7972
|
+
if (!connection || typeof connection.authenticatedUrl !== "function" || typeof connection.authorizeIndex !== "function") {
|
|
7973
|
+
if (!diagnosed) {
|
|
7974
|
+
diagnosed = true;
|
|
7975
|
+
console.log("[serenity-hooks] dsh browser-auth 适配: connection ✗ 不可取(不注入 dsh cookie——旧 dsh/非 web 装配兼容)");
|
|
7976
|
+
}
|
|
7977
|
+
return;
|
|
7978
|
+
}
|
|
7979
|
+
if (cachedProvider === void 0) cachedProvider = createDshCookieProvider(connection, authority);
|
|
7980
|
+
const cookie = cachedProvider();
|
|
7981
|
+
if (!diagnosed) {
|
|
7982
|
+
diagnosed = true;
|
|
7983
|
+
console.log(`[serenity-hooks] dsh browser-auth 适配: connection ✓ 可取 + cookie ${cookie === void 0 ? "✗ 换取失败(上游 401 时检查)" : "✓ 已内存换取(注入反代)"}`);
|
|
7984
|
+
}
|
|
7985
|
+
return cookie;
|
|
7986
|
+
} catch {
|
|
7987
|
+
return;
|
|
7988
|
+
}
|
|
7989
|
+
};
|
|
7990
|
+
}
|
|
7834
7991
|
/** 注册 gateway(index.ts apply 调用)。
|
|
7835
7992
|
* 归属原则(v1.22):gateway 是 plugin 全局能力——enabled 开关读 DSH settings
|
|
7836
7993
|
* (readSimpleSettings().gatewayEnabled),host/port/accounts 读 plugin 全局文件
|
|
@@ -7868,7 +8025,7 @@ function registerGateway(ctx) {
|
|
|
7868
8025
|
cookieSecure: settings.gateway.cookieSecure === true,
|
|
7869
8026
|
allowWorkspaceCreate: settings.gateway.allowWorkspaceCreate !== false,
|
|
7870
8027
|
totpEnabled: settings.gateway.totpEnabled === true
|
|
7871
|
-
}, () => readAdvancedSettings().gateway.accounts);
|
|
8028
|
+
}, () => readAdvancedSettings().gateway.accounts, buildDshCookieProvider(ctx, webServer.port));
|
|
7872
8029
|
const accounts = settings.gateway.accounts.length;
|
|
7873
8030
|
const wsNote = settings.gateway.workspaces.length === 0 ? "" : `;工作区白名单 ${settings.gateway.workspaces.length} 条`;
|
|
7874
8031
|
console.log(`[serenity-hooks] gateway 已启动: http://${settings.gateway.host}:${settings.gateway.port} → 127.0.0.1:${webServer.port}` + (accounts === 0 ? "(⚠️ 未配置账号,登录页将提示)" : `(${accounts} 个账号)`) + wsNote);
|
|
@@ -8055,7 +8212,7 @@ function isExternalFaceSession(sessionId) {
|
|
|
8055
8212
|
* 否则 think 内提及内部机制词(思考过程必然推演机制)会误打回。复用 stripThink
|
|
8056
8213
|
* (v1.26.8 状态机,弃正则——同 v1.27.1 微信桥回复链路)。 */
|
|
8057
8214
|
function lastAssistantText(agent) {
|
|
8058
|
-
const events = agent.session
|
|
8215
|
+
const events = sessionEvents(agent.session);
|
|
8059
8216
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
8060
8217
|
const e = events[i];
|
|
8061
8218
|
if (e && e.type === "assistant/message") {
|
|
@@ -9332,7 +9489,8 @@ function registerWeixinBridge(ctx) {
|
|
|
9332
9489
|
//#endregion
|
|
9333
9490
|
//#region src/index.ts
|
|
9334
9491
|
const name = "dsh-serenity-hooks";
|
|
9335
|
-
/** 主动调用的服务;其余(agent 事件)随 harness 装配必然存在
|
|
9492
|
+
/** 主动调用的服务;其余(agent 事件)随 harness 装配必然存在
|
|
9493
|
+
* (v1.28.0 适配 0.1.2-rc.1:+ 'settings'——B4 settings 服务由 provider 插件加载后才有) */
|
|
9336
9494
|
const inject = [
|
|
9337
9495
|
"tools",
|
|
9338
9496
|
"webServer",
|
|
@@ -9342,7 +9500,8 @@ const inject = [
|
|
|
9342
9500
|
"agentLoop",
|
|
9343
9501
|
"agents",
|
|
9344
9502
|
"systemPrompt",
|
|
9345
|
-
"sessionProjections"
|
|
9503
|
+
"sessionProjections",
|
|
9504
|
+
"settings"
|
|
9346
9505
|
];
|
|
9347
9506
|
const Config = z.object({
|
|
9348
9507
|
serenityConfigPaths: z.array(z.string()).default([...DEFAULT_SERENITY_CONFIG_PATHS]),
|
package/lib/seams/bootstrap.d.ts
CHANGED
|
@@ -50,6 +50,15 @@ export declare const DEFAULT_COMPACTION_TOOLS: string[];
|
|
|
50
50
|
* 文本 = 原 home-serenity CCC 配置(anchorMessages),随零配置化固化进代码。
|
|
51
51
|
*/
|
|
52
52
|
export declare const DEFAULT_ANCHOR_MESSAGES: string[];
|
|
53
|
+
/**
|
|
54
|
+
* 根会话锚定重入判定(v1.28.1 提取导出以便单测):会话是否已有真实用户消息。
|
|
55
|
+
* resume/续跑的会话已有对话历史 → **不重锚**(first-anchor 只注入一次)。
|
|
56
|
+
*
|
|
57
|
+
* 0.1.2-rc.1 适配教训:裸读 `session.events`(经类型断言)恒 undefined →
|
|
58
|
+
* `!undefined` 恒 true → 有历史也永不跳过 → 任何情况发消息都重插 first-anchor。
|
|
59
|
+
* 统一经 sessionEvents() 读取(snapshotEvents() 优先,.events 兜底)。
|
|
60
|
+
*/
|
|
61
|
+
export declare function hasUserMessageHistory(session: unknown): boolean;
|
|
53
62
|
export interface PromotionStatus {
|
|
54
63
|
/** 最后一次 compaction/end 的 seq(-1 = 未压缩过) */
|
|
55
64
|
boundary: number;
|
package/lib/session-ops.d.ts
CHANGED
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
import type { JsonValue } from './json.js';
|
|
13
13
|
export type SessionAction = 'list' | 'show' | 'create' | 'use' | 'close' | 'health' | 'qa' | 'archive' | 'summary' | 'hook-develop-guide';
|
|
14
14
|
export declare const SESSION_ACTIONS: readonly SessionAction[];
|
|
15
|
+
/**
|
|
16
|
+
* 读取 Session 事件序列(v1.28.1 适配 0.1.2-rc.1 补齐):rc.1 起官方 Session 类
|
|
17
|
+
* 移除 `.events` 属性 → `snapshotEvents()` 方法(dsh-session/src/session.ts:
|
|
18
|
+
* `snapshotEvents(fromSeq, toSeqExclusive)`)。插件早期代码多处裸读 `.events`
|
|
19
|
+
* (经 `as unknown as { events? }` 断言绕过 typecheck),运行时静默 undefined——
|
|
20
|
+
* 造成 first-anchor 每轮重插 / SESSION 激活恢复失效 / rebuild 定位错乱。
|
|
21
|
+
* 统一收敛到本 helper:snapshotEvents() 优先(rc.1 真实形态),`.events` 兜底
|
|
22
|
+
* (测试替身/旧运行时)。所有消费方一律经此读取,禁止再裸读 `.events`。
|
|
23
|
+
* 泛型 T:调用方按需声明事件形状(如 `SessionEvent`),unknown 默认。
|
|
24
|
+
*/
|
|
25
|
+
export declare function sessionEvents<T = unknown>(session: unknown): readonly T[];
|
|
15
26
|
export declare function sessionsRoot(root: string): string;
|
|
16
27
|
interface SessionStatus {
|
|
17
28
|
hasSessionMd: boolean;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
//#region src/settings-section.ts
|
|
4
|
+
var settings_section_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
SERENITY_SETTINGS_NS: () => SERENITY_SETTINGS_NS,
|
|
6
|
+
defaultSimpleSettings: () => defaultSimpleSettings,
|
|
7
|
+
entryDefaults: () => entryDefaults,
|
|
8
|
+
readSimpleSettings: () => readSimpleSettings,
|
|
9
|
+
registerSettingsSection: () => registerSettingsSection,
|
|
10
|
+
simpleSettingsSchema: () => simpleSettingsSchema
|
|
11
|
+
});
|
|
12
|
+
/** 简单配置命名空间(dsh 设置面板的 section id / settings.yaml section) */
|
|
13
|
+
const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
14
|
+
/** schemastery schema(与 DSH 各插件 Config 同款) */
|
|
15
|
+
const simpleSettingsSchema = z.object({
|
|
16
|
+
gatewayEnabled: z.boolean().default(false),
|
|
17
|
+
rebuildEnabled: z.boolean().default(true),
|
|
18
|
+
rebuildThresholdK: z.number().min(50).max(4e3).default(400),
|
|
19
|
+
skiffEnabled: z.boolean().default(false),
|
|
20
|
+
skiffDebugPort: z.number().min(1024).max(65535).default(3099),
|
|
21
|
+
acpEnabled: z.boolean().default(false),
|
|
22
|
+
acpHttpPort: z.number().min(1024).max(65535).default(3100),
|
|
23
|
+
publicAskEnabled: z.boolean().default(false),
|
|
24
|
+
autopilotEnabled: z.boolean().default(false)
|
|
25
|
+
});
|
|
26
|
+
/** 从插件 Config 提取 entry 默认(settings base 层) */
|
|
27
|
+
function entryDefaults(config) {
|
|
28
|
+
return {
|
|
29
|
+
gatewayEnabled: config.gateway?.enabled ?? false,
|
|
30
|
+
rebuildEnabled: config.rebuild?.enabled ?? true,
|
|
31
|
+
rebuildThresholdK: config.rebuild?.thresholdK ?? 400,
|
|
32
|
+
skiffEnabled: config.skiff?.enabled ?? false,
|
|
33
|
+
skiffDebugPort: config.skiff?.debugPort ?? 3099,
|
|
34
|
+
acpEnabled: config.acp?.enabled ?? false,
|
|
35
|
+
acpHttpPort: config.acp?.httpPort ?? 3100,
|
|
36
|
+
publicAskEnabled: config.publicAsk?.enabled ?? false,
|
|
37
|
+
autopilotEnabled: false
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** 运行时源(installSettingsSection 注入:settings scope 或 entry fallback) */
|
|
41
|
+
let simpleSource = null;
|
|
42
|
+
/** 进程级默认(无 settings 服务时的兜底) */
|
|
43
|
+
function defaultSimpleSettings() {
|
|
44
|
+
return {
|
|
45
|
+
gatewayEnabled: false,
|
|
46
|
+
rebuildEnabled: true,
|
|
47
|
+
rebuildThresholdK: 400,
|
|
48
|
+
skiffEnabled: false,
|
|
49
|
+
skiffDebugPort: 3099,
|
|
50
|
+
acpEnabled: false,
|
|
51
|
+
acpHttpPort: 3100,
|
|
52
|
+
publicAskEnabled: false,
|
|
53
|
+
autopilotEnabled: false
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 读取当前简单配置(settings 解析值;无 provider/未注册 → entry 默认)。
|
|
58
|
+
* 各功能(gateway/rebuild/naming)启动与运行时判断开关都经此函数。
|
|
59
|
+
*/
|
|
60
|
+
function readSimpleSettings() {
|
|
61
|
+
return simpleSource ? simpleSource() : defaultSimpleSettings();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 注册简单配置到 DSH settings(零改 DSH;settings.yaml 持久化 + 原生面板渲染)。
|
|
65
|
+
* 注:插件 Config(cordis.yml 组合层)作为 base;用户文档层叠加其上。
|
|
66
|
+
* 运行时读取简单配置统一经 `readSimpleSettings()`。
|
|
67
|
+
* v1.28.0 适配 0.1.2-rc.1(B4):`installSettingsSection`/`settingsNamespace` 便捷函数在
|
|
68
|
+
* rc.1 消失 → 改 `SettingsProvider.installSection(owner, ns, schema, entry, hooks)`
|
|
69
|
+
* (owner = consumer 插件 ctx;ns 直接字符串)。
|
|
70
|
+
*/
|
|
71
|
+
function registerSettingsSection(ctx, config) {
|
|
72
|
+
const hooks = {
|
|
73
|
+
setSource: (get) => {
|
|
74
|
+
simpleSource = get;
|
|
75
|
+
},
|
|
76
|
+
onChange: () => {
|
|
77
|
+
try {
|
|
78
|
+
ctx.emit?.("serenity/settings-changed");
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const settingsAny = ctx.settings;
|
|
83
|
+
if (settingsAny) {
|
|
84
|
+
settingsAny.installSection.call(settingsAny, ctx, SERENITY_SETTINGS_NS, simpleSettingsSchema, entryDefaults(config), hooks);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
hooks.setSource(() => ({}));
|
|
88
|
+
hooks.onChange();
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { registerSettingsSection as n, settings_section_exports as r, readSimpleSettings as t };
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* 分层决策(S142):**简单配置(开关/阈值)→ dsh 原生设置面板**;
|
|
5
5
|
* **复杂配置(账号列表)→ 宁静号高级面板**(localstore + /serenity/config)。
|
|
6
6
|
*
|
|
7
|
-
* 本模块承载简单配置层:`
|
|
7
|
+
* 本模块承载简单配置层:`SettingsProvider.installSection(ctx, 'serenity-hooks', schema, entry)`
|
|
8
8
|
* 注册 `serenity-hooks` namespace——三功能总开关 + F2 阈值。
|
|
9
|
+
* (v1.28.0 适配 0.1.2-rc.1:installSettingsSection/settingsNamespace 便捷函数消失 → 方法调用)
|
|
9
10
|
*
|
|
10
11
|
* 运行时降级守卫(版本鲁棒性):旧 RC(staging 架构)api-proxy 有
|
|
11
12
|
* `WEB_SETTINGS_NAMESPACES` 静态白名单,第三方 ns 会收到 settings-not-exposed
|
|
@@ -107,5 +108,8 @@ export declare function __setSimpleSourceForTest(source: (() => SerenitySimpleSe
|
|
|
107
108
|
* 注册简单配置到 DSH settings(零改 DSH;settings.yaml 持久化 + 原生面板渲染)。
|
|
108
109
|
* 注:插件 Config(cordis.yml 组合层)作为 base;用户文档层叠加其上。
|
|
109
110
|
* 运行时读取简单配置统一经 `readSimpleSettings()`。
|
|
111
|
+
* v1.28.0 适配 0.1.2-rc.1(B4):`installSettingsSection`/`settingsNamespace` 便捷函数在
|
|
112
|
+
* rc.1 消失 → 改 `SettingsProvider.installSection(owner, ns, schema, entry, hooks)`
|
|
113
|
+
* (owner = consumer 插件 ctx;ns 直接字符串)。
|
|
110
114
|
*/
|
|
111
115
|
export declare function registerSettingsSection(ctx: Context, config: SimpleConfigFragment): void;
|