@shgroup/dsh-serenity-hooks 1.51.0 → 1.51.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/README.md +1 -1
- package/assets/diagram/DejaVuSans.ttf +0 -0
- package/assets/diagram/LICENSE-DejaVu.txt +78 -0
- package/assets/diagram/LICENSE-OFL-1.1.txt +155 -0
- package/assets/diagram/NotoSans-Regular.ttf +0 -0
- package/assets/diagram/PROVENANCE.md +80 -21
- package/dsh.plugin.json +1 -1
- package/lib/client.js +220 -8
- package/lib/diagram/fonts.d.ts +40 -0
- package/lib/diagram/rasterize.d.ts +13 -6
- package/lib/index.js +222 -98
- package/lib/{settings-section-cq0eUSd-.js → settings-section-xHABl8nk.js} +5 -1
- package/package.json +6 -1
package/lib/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSe
|
|
|
6
6
|
import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, g as runLocalStore, h as readStore, 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-7BtOiy4X.js";
|
|
7
7
|
import { a as unregisterSkiffSession$1, i as skiffSessionSnapshot$1, n as skiffRoleFor$1, o as ACC_VERSION, r as skiffSessionInfo$1, t as registerSkiffSession$1 } from "./skiff-registry-BjIX_KD1.js";
|
|
8
8
|
import { a as readSkiffRoles, c as roleMsmWhitelist, d as systemPromptSource, f as trajectorySubset, i as isSkiffSessionId, l as roleToolWhitelist, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-DQZ6eR51.js";
|
|
9
|
-
import { a as
|
|
9
|
+
import { a as ACP_HTTP_PORT, c as MAIN_WEB_PORT, d as WEIXIN_SEND_PORT, l as MECHANISM_PORTS, n as readSimpleSettings, o as FACE_PORTS, r as registerSettingsSection, s as GATEWAY_PORT, u as SKIFF_DEBUG_PORT } from "./settings-section-xHABl8nk.js";
|
|
10
10
|
import { a as markdownToPlainText, c as sendTyping, i as getUpdates, l as sniffImageExt, n as downloadMedia, o as sendFileMessage, r as getConfig, s as sendTextMessage, t as TypingStatus } from "./weixin-api-BrRcPiQs.js";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import z from "@deepseek-ai/schemastery";
|
|
@@ -10358,7 +10358,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10358
10358
|
return;
|
|
10359
10359
|
}
|
|
10360
10360
|
const settings = readAdvancedSettings();
|
|
10361
|
-
const { readSimpleSettings } = await import("./settings-section-
|
|
10361
|
+
const { readSimpleSettings } = await import("./settings-section-xHABl8nk.js").then((n) => n.i);
|
|
10362
10362
|
const simple = readSimpleSettings();
|
|
10363
10363
|
const allowed = settings.publicAsk.allowed;
|
|
10364
10364
|
const port = simple.acpHttpPort ?? ACP_HTTP_PORT;
|
|
@@ -19330,13 +19330,26 @@ function lineAt(src, idx) {
|
|
|
19330
19330
|
function parseDsl(source) {
|
|
19331
19331
|
const src = stripComments(source);
|
|
19332
19332
|
const errors = [];
|
|
19333
|
+
/**
|
|
19334
|
+
* 🔴 **默认坐标系 = 画布像素、y 向下**(v1.51.1 修的真缺陷,见下)。
|
|
19335
|
+
*
|
|
19336
|
+
* v1.51.0 的默认值是从旧 mini-SVG 移植时**原样带过来**的:`0~100 × −19~72 且 y 向上`
|
|
19337
|
+
* —— 而工具描述里**只写了 `box="x0,y0,x1,y1"`,没写量程与单位**。
|
|
19338
|
+
* 模型按常识写画布像素(`box="100,100,400,200"`)⇒ 渲染器把它塞进那个 100 宽的域里
|
|
19339
|
+
* ⇒ **所有坐标飞出画布**(实测 SVG:`x=990 y=-919 w=2940`),而 `errors`/`lint` **全空**
|
|
19340
|
+
* ⇒ 图是空白却一声不吭,模型只能反复试错(owner 实测「别的 CCC 根本用不起来」的根因)。
|
|
19341
|
+
*
|
|
19342
|
+
* ⇒ 现在:**默认域 = `0,0,width,height`**(`width/height` 未写时 1000×780)⇒ 1 单位 ≈ 1 像素量级,
|
|
19343
|
+
* `y` 越大越靠下(`dsl-render.ts` 的 `py()` 同批改成 y 向下)。
|
|
19344
|
+
* `domain="x0,y0,x1,y1"` 仍然保留 —— 需要"数学式坐标窗口"时显式写它(**y 也向下**,口径只有一种)。
|
|
19345
|
+
*/
|
|
19333
19346
|
const model = {
|
|
19334
19347
|
W: 1e3,
|
|
19335
19348
|
H: 780,
|
|
19336
19349
|
x0: 0,
|
|
19337
|
-
y0:
|
|
19338
|
-
x1:
|
|
19339
|
-
y1:
|
|
19350
|
+
y0: 0,
|
|
19351
|
+
x1: 1e3,
|
|
19352
|
+
y1: 780,
|
|
19340
19353
|
pad: 10,
|
|
19341
19354
|
title: "",
|
|
19342
19355
|
subtitle: "",
|
|
@@ -19421,6 +19434,11 @@ function parseDsl(source) {
|
|
|
19421
19434
|
model.x1 = d.x1;
|
|
19422
19435
|
model.y1 = d.y1;
|
|
19423
19436
|
} else fail(tagIndex, ln, `domain 不是 4 个数字:${attrs.domain}(已忽略)`);
|
|
19437
|
+
} else {
|
|
19438
|
+
model.x0 = 0;
|
|
19439
|
+
model.y0 = 0;
|
|
19440
|
+
model.x1 = model.W;
|
|
19441
|
+
model.y1 = model.H;
|
|
19424
19442
|
}
|
|
19425
19443
|
continue;
|
|
19426
19444
|
}
|
|
@@ -19459,7 +19477,7 @@ function parseDsl(source) {
|
|
|
19459
19477
|
continue;
|
|
19460
19478
|
}
|
|
19461
19479
|
const label = attrs.label;
|
|
19462
|
-
const id = attrs.id || label || `rect${rectOrdinal}`;
|
|
19480
|
+
const id = attrs.id || attrs.name || label || `rect${rectOrdinal}`;
|
|
19463
19481
|
if (ids.has(id)) {
|
|
19464
19482
|
fail(tagIndex, ln, `rect id 重复:${id}`);
|
|
19465
19483
|
continue;
|
|
@@ -19511,7 +19529,7 @@ function parseDsl(source) {
|
|
|
19511
19529
|
x,
|
|
19512
19530
|
y,
|
|
19513
19531
|
text: content,
|
|
19514
|
-
align: al === "center" || al === "
|
|
19532
|
+
align: al === "center" || al === "middle" ? "center" : al === "right" || al === "end" ? "right" : "left",
|
|
19515
19533
|
size: num(attrs.size) ?? 12.5,
|
|
19516
19534
|
ln
|
|
19517
19535
|
};
|
|
@@ -19527,6 +19545,44 @@ function parseDsl(source) {
|
|
|
19527
19545
|
};
|
|
19528
19546
|
}
|
|
19529
19547
|
//#endregion
|
|
19548
|
+
//#region src/diagram/fonts.ts
|
|
19549
|
+
/**
|
|
19550
|
+
* diagram 字体事实源(**唯一真相源**:资产路径 + 家族名 + CSS 家族栈)
|
|
19551
|
+
*
|
|
19552
|
+
* 为什么单独成模块(R↓):两处都要它,而它们的依赖面**必须**保持不同 ——
|
|
19553
|
+
* · `dsl-render.ts` 只生成 SVG 字符串(**纯函数、零 fs**),它需要在 SVG 里写 `font-family` 栈;
|
|
19554
|
+
* · `rasterize.ts` 要读字体文件喂给 wasm,并设 `defaultFontFamily`。
|
|
19555
|
+
* ⇒ 谁都不该从对方 import(前者会被拖进 `node:fs`/wasm,后者不该定义 SVG 措辞)。
|
|
19556
|
+
*
|
|
19557
|
+
* ## 为什么是**三个**字型(每一个都由一张实测图证明,不是偏好,也不是"多带点保险")
|
|
19558
|
+
*
|
|
19559
|
+
* | 字型 | 覆盖 | 它修好了什么(实测) |
|
|
19560
|
+
* |---|---|---|
|
|
19561
|
+
* | `DroidSansFallbackFull.ttf` | 汉字 | 没有它 ⇒ 中文全是方框(docker 实测:wasm 对系统字体**完全不可见**) |
|
|
19562
|
+
* | `NotoSans-Regular.ttf` | 拉丁 | 没有它 ⇒ `diagram` / `parseDsl` 全变方框(Droid 是 **CJK 兜底字型**,**不含拉丁**) |
|
|
19563
|
+
* | `DejaVuSans.ttf` | 符号 | 没有它 ⇒ `① ② ③` / `⇒ ⇒` / `→` 仍是方框(**前两个都没有这些字形**,实测对照图见 `docs/diagram-tool-feasibility.md`) |
|
|
19564
|
+
*
|
|
19565
|
+
* 🔴 **上一版的判据漏洞**(v1.51.1 修的):那条"有字 vs 无字逐字节不同"只证明**有墨**,
|
|
19566
|
+
* 没证明**墨是那个字** —— 方框也是墨 ⇒ 拉丁全变方框时它**照样绿**。
|
|
19567
|
+
* ⇒ 现在每条覆盖声明都配一条**点名字符集**的差分用例:同长度不同字形 ⇒ 必须出不同墨。
|
|
19568
|
+
*
|
|
19569
|
+
* ## 家族名是**量出来的**,不是猜的
|
|
19570
|
+
* 下表取自各文件 `name` 表 `nameID=1`(平台 3 / UTF-16BE):
|
|
19571
|
+
* `Droid Sans Fallback` / `Noto Sans` / `DejaVu Sans`。
|
|
19572
|
+
* 写错名字时 resvg 的自动回退仍可能把字画出来(**图上看不出来**)⇒ 名字正确与否只能这样证明。
|
|
19573
|
+
*/
|
|
19574
|
+
const DIAGRAM_CJK_FONT_ASSET = "assets/diagram/DroidSansFallbackFull.ttf";
|
|
19575
|
+
const DIAGRAM_LATIN_FONT_ASSET = "assets/diagram/NotoSans-Regular.ttf";
|
|
19576
|
+
const DIAGRAM_SYMBOL_FONT_ASSET = "assets/diagram/DejaVuSans.ttf";
|
|
19577
|
+
/** 汉字字型的家族名(**字体文件自报的族名**,写错就取不到) */
|
|
19578
|
+
const DIAGRAM_CJK_FONT_FAMILY = "Droid Sans Fallback";
|
|
19579
|
+
/**
|
|
19580
|
+
* SVG 里写的家族栈(**顺序即回退顺序**):拉丁优先取 Noto,符号落 DejaVu,其余字形落到 Droid。
|
|
19581
|
+
* ⚠️ 末尾的通用名(`sans-serif`)只是兜底惯例:本部署里 resvg **不带系统字体**
|
|
19582
|
+
* (`loadSystemFonts: false`)⇒ 它取不到任何东西,真正的兜底是上面三个内置字型。
|
|
19583
|
+
*/
|
|
19584
|
+
const DIAGRAM_FONT_STACK = `'Noto Sans', 'DejaVu Sans', '${DIAGRAM_CJK_FONT_FAMILY}', sans-serif`;
|
|
19585
|
+
//#endregion
|
|
19530
19586
|
//#region src/diagram/dsl-render.ts
|
|
19531
19587
|
const TITLE_BAND = 68;
|
|
19532
19588
|
const EPS = .35;
|
|
@@ -19669,7 +19725,12 @@ function renderDsl(model) {
|
|
|
19669
19725
|
const scaleX = (W - 2 * pad) / (x1 - x0 || 1);
|
|
19670
19726
|
const scaleY = (H - TITLE_BAND - pad) / (y1 - y0 || 1);
|
|
19671
19727
|
const px = (x) => r2(pad + (x - x0) * scaleX);
|
|
19672
|
-
const py = (y) => r2(TITLE_BAND + (
|
|
19728
|
+
const py = (y) => r2(TITLE_BAND + (y - y0) * scaleY);
|
|
19729
|
+
/** 绘图窗口 = 声明的域(默认就是画布)。出窗口的图元**报错并跳过**,不静默画到看不见的地方。 */
|
|
19730
|
+
const tolX = Math.abs(x1 - x0) * .01;
|
|
19731
|
+
const tolY = Math.abs(y1 - y0) * .01;
|
|
19732
|
+
const outside = (x, y) => x < x0 - tolX || x > x1 + tolX || y < y0 - tolY || y > y1 + tolY;
|
|
19733
|
+
const windowText = `坐标窗口 x ${r2(x0)}..${r2(x1)} / y ${r2(y0)}..${r2(y1)}(y 向下;不写 domain 时=画布)`;
|
|
19673
19734
|
const resolve = (token) => {
|
|
19674
19735
|
const t = String(token ?? "").trim();
|
|
19675
19736
|
const np = NUM_PAIR_RE.exec(t);
|
|
@@ -19689,15 +19750,15 @@ function renderDsl(model) {
|
|
|
19689
19750
|
const cx = (rx0 + rx1) / 2;
|
|
19690
19751
|
const cy = (ry0 + ry1) / 2;
|
|
19691
19752
|
const base = {
|
|
19692
|
-
n: [cx,
|
|
19693
|
-
s: [cx,
|
|
19753
|
+
n: [cx, ry0],
|
|
19754
|
+
s: [cx, ry1],
|
|
19694
19755
|
e: [rx1, cy],
|
|
19695
19756
|
w: [rx0, cy],
|
|
19696
19757
|
c: [cx, cy],
|
|
19697
|
-
ne: [rx1,
|
|
19698
|
-
nw: [rx0,
|
|
19699
|
-
se: [rx1,
|
|
19700
|
-
sw: [rx0,
|
|
19758
|
+
ne: [rx1, ry0],
|
|
19759
|
+
nw: [rx0, ry0],
|
|
19760
|
+
se: [rx1, ry1],
|
|
19761
|
+
sw: [rx0, ry1]
|
|
19701
19762
|
}[mt[2] ?? ""];
|
|
19702
19763
|
if (!base) return null;
|
|
19703
19764
|
const dx = mt[3] === void 0 ? 0 : Number(mt[3]);
|
|
@@ -19724,7 +19785,7 @@ function renderDsl(model) {
|
|
|
19724
19785
|
const headEls = [], rectEls = [], segEls = [];
|
|
19725
19786
|
const textEls = [], legendEls = [], l3 = [], l4 = [];
|
|
19726
19787
|
let marks = 0;
|
|
19727
|
-
headEls.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" font-family="
|
|
19788
|
+
headEls.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" font-family="${DIAGRAM_FONT_STACK}">`);
|
|
19728
19789
|
headEls.push(`<rect width="${W}" height="${H}" fill="#fbfaf7"/>`);
|
|
19729
19790
|
marks += 2;
|
|
19730
19791
|
if (m.title) headEls.push(`<text x="10" y="34" font-size="21" font-weight="bold" fill="#111">${esc(m.title)}</text>`);
|
|
@@ -19732,10 +19793,14 @@ function renderDsl(model) {
|
|
|
19732
19793
|
if (m.subtitle) headEls.push(`<text x="10" y="58" font-size="13" fill="#666">${esc(m.subtitle)}</text>`);
|
|
19733
19794
|
if (m.subtitle) marks += 1;
|
|
19734
19795
|
for (const r of rects) {
|
|
19796
|
+
if (outside(n(r.x0, 0), n(r.y0, 0)) || outside(n(r.x1, 0), n(r.y1, 0))) {
|
|
19797
|
+
errors.push(`矩形「${r.label ?? r.id}」在绘图窗口外:box=${r2(n(r.x0, 0))},${r2(n(r.y0, 0))},${r2(n(r.x1, 0))},${r2(n(r.y1, 0))} / ${windowText} ⇒ 该图元已跳过`);
|
|
19798
|
+
continue;
|
|
19799
|
+
}
|
|
19735
19800
|
const X = px(n(r.x0, 0));
|
|
19736
|
-
const Y = py(n(r.
|
|
19801
|
+
const Y = py(n(r.y0, 0));
|
|
19737
19802
|
const bw = r2(px(n(r.x1, 0)) - X);
|
|
19738
|
-
const bh = r2(py(n(r.
|
|
19803
|
+
const bh = r2(py(n(r.y1, 0)) - Y);
|
|
19739
19804
|
rectEls.push(`<rect x="${X}" y="${Y}" width="${bw}" height="${bh}" rx="2" fill="${esc(col(r.color, DEF_FILL))}" stroke="#ffffff" stroke-width="1.5"/>`);
|
|
19740
19805
|
marks += 1;
|
|
19741
19806
|
if (!r.label) continue;
|
|
@@ -19751,7 +19816,12 @@ function renderDsl(model) {
|
|
|
19751
19816
|
const b = resolve(s.to);
|
|
19752
19817
|
if (!a || !b) {
|
|
19753
19818
|
const bad = !a ? s.from : s.to;
|
|
19754
|
-
|
|
19819
|
+
const shown = ids.slice(0, 8).join("、");
|
|
19820
|
+
errors.push(`第 ${lineNo(s, i)} 行 ${kind}:锚点引用解析不了(${esc(bad)});已知 id = ${shown}${ids.length > 8 ? ` …(共 ${ids.length} 个)` : ""};rect 要能被引用请写 id="…"(name="…" 亦可)⇒ 该图元已跳过`);
|
|
19821
|
+
continue;
|
|
19822
|
+
}
|
|
19823
|
+
if (outside(a.x, a.y) || outside(b.x, b.y)) {
|
|
19824
|
+
errors.push(`第 ${lineNo(s, i)} 行 ${kind}:端点在绘图窗口外 (${r2(a.x)},${r2(a.y)})→(${r2(b.x)},${r2(b.y)}) / ${windowText} ⇒ 该图元已跳过`);
|
|
19755
19825
|
continue;
|
|
19756
19826
|
}
|
|
19757
19827
|
const X0 = px(a.x);
|
|
@@ -19794,19 +19864,21 @@ function renderDsl(model) {
|
|
|
19794
19864
|
}
|
|
19795
19865
|
}
|
|
19796
19866
|
for (const t of texts) {
|
|
19797
|
-
|
|
19867
|
+
if (outside(n(t.x, 0), n(t.y, 0))) {
|
|
19868
|
+
errors.push(`文字「${t.text}」在绘图窗口外:(x ${r2(n(t.x, 0))}, y ${r2(n(t.y, 0))}) / ${windowText} ⇒ 该图元已跳过`);
|
|
19869
|
+
continue;
|
|
19870
|
+
}
|
|
19871
|
+
const anchor = t.anchor === "left" || t.anchor === "start" ? "start" : t.anchor === "right" || t.anchor === "end" ? "end" : "middle";
|
|
19798
19872
|
textEls.push(`<text x="${px(n(t.x, 0))}" y="${py(n(t.y, 0))}" font-size="${n(t.size, DEF_SIZE)}" fill="${esc(col(t.color, "#4a4a4a"))}" text-anchor="${anchor}" dominant-baseline="middle">${esc(t.text)}</text>`);
|
|
19799
19873
|
marks += 1;
|
|
19800
19874
|
}
|
|
19801
19875
|
if (m.legend) {
|
|
19802
19876
|
const size = n(m.legend.size, DEF_SIZE);
|
|
19803
|
-
const LX =
|
|
19804
|
-
const LY =
|
|
19805
|
-
legendEls.push(`<text x="${LX}" y="${LY}" font-size="${size + 1}" font-weight="bold" fill="#333">阵营</text>`);
|
|
19806
|
-
marks += 1;
|
|
19877
|
+
const LX = px(n(m.legend.x, 0));
|
|
19878
|
+
const LY = py(n(m.legend.y, 0));
|
|
19807
19879
|
const entries = Object.entries(colors);
|
|
19808
19880
|
for (let i = 0; i < entries.length; i++) {
|
|
19809
|
-
const row = LY +
|
|
19881
|
+
const row = LY + i * (size + 6);
|
|
19810
19882
|
legendEls.push(`<rect x="${LX}" y="${r2(row - 8)}" width="12" height="12" rx="2" fill="${esc(entries[i][1])}"/>`);
|
|
19811
19883
|
legendEls.push(`<text x="${r2(LX + 18)}" y="${r2(row)}" font-size="${size}" fill="#333" dominant-baseline="middle">${esc(entries[i][0])}</text>`);
|
|
19812
19884
|
marks += 2;
|
|
@@ -19841,7 +19913,8 @@ function renderDsl(model) {
|
|
|
19841
19913
|
* ⇒ **wasm 对系统字体完全不可见**。只有 `fontBuffers` 才画得出字。
|
|
19842
19914
|
* ⇒ 顺带白拿一个好处:**输出与机器无关**(同一份内置字体 ⇒ 逐字节可复现,S↑ 稳定)。
|
|
19843
19915
|
*
|
|
19844
|
-
*
|
|
19916
|
+
* 字体资产随包分发(三个,各有各的覆盖,见 `fonts.ts` 的表):汉字 3.8 MB(Apache-2.0)/
|
|
19917
|
+
* 拉丁 0.5 MB(OFL-1.1)/符号 0.75 MB(DejaVu 许可证),各自带许可证与出处。
|
|
19845
19918
|
*/
|
|
19846
19919
|
/**
|
|
19847
19920
|
* 包根 = **向上找到含 `package.json` 的那一级**。
|
|
@@ -19861,20 +19934,27 @@ function packageRootPath() {
|
|
|
19861
19934
|
}
|
|
19862
19935
|
return join(start, "..");
|
|
19863
19936
|
}
|
|
19864
|
-
/**
|
|
19865
|
-
const DIAGRAM_FONT_ASSET = "assets/diagram/DroidSansFallbackFull.ttf";
|
|
19866
|
-
/** 字体绝对路径(导出以便测试与 pack-check 共用同一判据来源) */
|
|
19937
|
+
/** 汉字字型绝对路径(导出以便测试与 pack-check 共用同一判据来源) */
|
|
19867
19938
|
function diagramFontPath() {
|
|
19868
|
-
return join(packageRootPath(),
|
|
19939
|
+
return join(packageRootPath(), DIAGRAM_CJK_FONT_ASSET);
|
|
19940
|
+
}
|
|
19941
|
+
/** 拉丁字型绝对路径 */
|
|
19942
|
+
function diagramLatinFontPath() {
|
|
19943
|
+
return join(packageRootPath(), DIAGRAM_LATIN_FONT_ASSET);
|
|
19944
|
+
}
|
|
19945
|
+
/** 符号字型绝对路径 */
|
|
19946
|
+
function diagramSymbolFontPath() {
|
|
19947
|
+
return join(packageRootPath(), DIAGRAM_SYMBOL_FONT_ASSET);
|
|
19869
19948
|
}
|
|
19870
19949
|
/**
|
|
19871
|
-
*
|
|
19872
|
-
*
|
|
19873
|
-
* ⇒ 光栅化时必须由这里兜底,否则中文会退化成缺字方框。
|
|
19950
|
+
* 兜底字型的族名 = **汉字字型**(本工具的图以中文为主)。
|
|
19951
|
+
* 两个内置字型的家族名与 CSS 家族栈统一定义在 `fonts.ts`(单一真相源),此处只透出兜底那一个。
|
|
19874
19952
|
*/
|
|
19875
|
-
const DIAGRAM_FONT_FAMILY =
|
|
19953
|
+
const DIAGRAM_FONT_FAMILY = DIAGRAM_CJK_FONT_FAMILY;
|
|
19876
19954
|
let wasmReady = null;
|
|
19877
19955
|
let fontBuf = null;
|
|
19956
|
+
let latinBuf = null;
|
|
19957
|
+
let symbolBuf = null;
|
|
19878
19958
|
/**
|
|
19879
19959
|
* wasm 模块路径:**先解析 JS 入口再取同目录的 `.wasm`**。
|
|
19880
19960
|
* 不直接 resolve `.wasm` 的原因:包的 `exports` 表未必暴露该子路径(解析失败会抛,
|
|
@@ -19891,11 +19971,19 @@ function ensureReady() {
|
|
|
19891
19971
|
});
|
|
19892
19972
|
return wasmReady;
|
|
19893
19973
|
}
|
|
19894
|
-
/**
|
|
19974
|
+
/** 三个字型各只读一次(3.8 MB + 0.5 MB + 0.75 MB,进程内缓存) */
|
|
19895
19975
|
function fontBuffer() {
|
|
19896
19976
|
if (fontBuf === null) fontBuf = readFileSync(diagramFontPath());
|
|
19897
19977
|
return fontBuf;
|
|
19898
19978
|
}
|
|
19979
|
+
function latinFontBuffer() {
|
|
19980
|
+
if (latinBuf === null) latinBuf = readFileSync(diagramLatinFontPath());
|
|
19981
|
+
return latinBuf;
|
|
19982
|
+
}
|
|
19983
|
+
function symbolFontBuffer() {
|
|
19984
|
+
if (symbolBuf === null) symbolBuf = readFileSync(diagramSymbolFontPath());
|
|
19985
|
+
return symbolBuf;
|
|
19986
|
+
}
|
|
19899
19987
|
/**
|
|
19900
19988
|
* SVG → PNG。
|
|
19901
19989
|
* @param width 输出像素宽(矢量无损放大;高度按 SVG 自身比例推出)
|
|
@@ -19909,7 +19997,11 @@ async function rasterizeSvg(svg, width) {
|
|
|
19909
19997
|
value: width
|
|
19910
19998
|
},
|
|
19911
19999
|
font: {
|
|
19912
|
-
fontBuffers: [
|
|
20000
|
+
fontBuffers: [
|
|
20001
|
+
latinFontBuffer(),
|
|
20002
|
+
symbolFontBuffer(),
|
|
20003
|
+
fontBuffer()
|
|
20004
|
+
],
|
|
19913
20005
|
loadSystemFonts: false,
|
|
19914
20006
|
defaultFontFamily: DIAGRAM_FONT_FAMILY
|
|
19915
20007
|
}
|
|
@@ -19931,27 +20023,30 @@ async function rasterizeSvg(svg, width) {
|
|
|
19931
20023
|
* (注意**不借助浏览器生成截图**)」。权威设计 = 插件仓 `docs/diagram-tool-feasibility.md`。
|
|
19932
20024
|
*
|
|
19933
20025
|
* 链路(全部进程内,**不起浏览器**):标签源码 → `parseDsl` → `renderDsl`(SVG + 四项体检)
|
|
19934
|
-
* → `rasterizeSvg`(resvg-wasm +
|
|
20026
|
+
* → `rasterizeSvg`(resvg-wasm + **内置字型**)→ PNG → **图片块进对话**。
|
|
20027
|
+
*
|
|
20028
|
+
* 🔴 **不落文件**(owner 2026-09-29 明确):「这个 diagram 生成文件干啥,没有任何意义啊,
|
|
20029
|
+
* 直接生成给用户看就好了」⇒ 正常路径**一份文件都不写**(图走附件的 durable 引用直进对话记录,
|
|
20030
|
+
* 可回看、可被后续轮次引用、可转微信 —— 这才是"给人看"的落点)。
|
|
20031
|
+
* ⚠️ **唯一的例外**:图**贴不进对话**时(附件服务不在/部署不收 png/当前路由不声明图像输入/
|
|
20032
|
+
* 存字节失败),才把 PNG 落到 `<CCC>/_tmp/diagram/` 并**在回执里给出路径与原因** ——
|
|
20033
|
+
* 否则那一次就真的什么都没有了("降级不吞回执"同一条纪律)。
|
|
19935
20034
|
*
|
|
19936
20035
|
* 🔴 **为什么 PNG 是必需的**:宿主的对话内图片通路**白名单只有光栅格式**
|
|
19937
|
-
* (`image/png|jpeg|webp|gif`,见 `dsh-attachment/types.d.ts`)⇒ **SVG
|
|
19938
|
-
* "产出 SVG 就自动显示"确定不成立。右侧栏走 SVG 是二期方案。
|
|
20036
|
+
* (`image/png|jpeg|webp|gif`,见 `dsh-attachment/types.d.ts`)⇒ **SVG 进不去**。
|
|
19939
20037
|
*
|
|
19940
|
-
* 🔴 **开关**:由 `index.ts`
|
|
19941
|
-
* 不占 token、不会被误调)—— 同 `skiff`/`acp` 的"未开启零资源占用"口径。
|
|
20038
|
+
* 🔴 **开关**:由 `index.ts` 门控(**关时不注册** ⇒ 工具根本不进模型工具清单),同 skiff/acp 口径。
|
|
19942
20039
|
*
|
|
19943
20040
|
* ## 🔴 两条**宿主契约**(2026-09-29 现场取证;第 ① 条是本文件第一版写错的地方)
|
|
19944
20041
|
*
|
|
19945
20042
|
* ① **`output.render` 是「同步投影」,宿主不 await 它**:`@deepseek-ai/dsh-tools` 的
|
|
19946
20043
|
* `createSuccessResult()` 直接 `rendered = tool.output.render(args, value)`,紧接着
|
|
19947
20044
|
* `snapshotJsonValue(rendered)` —— 返回一个 Promise 会被判「**非 lossless JSON**」⇒
|
|
19948
|
-
*
|
|
19949
|
-
* 于是"拿不到图片块仍返回文字回执"那段自兜底**根本走不到**(它以为的失败模式不是真失败模式)。
|
|
20045
|
+
* **整条工具调用失败**(图片与文字回执**一起**丢掉)。
|
|
19950
20046
|
* ② **图片块带的 `attachment` 必须是附件服务的 durable 引用**(`ImageAttachmentRef`),
|
|
19951
20047
|
* 而存字节是 async ⇒ **只能分两段**:`execute` 里 `saveImage`(async,把引用拼进**纯 JSON** 的返回值),
|
|
19952
20048
|
* `render` 里**同步**装配 `{ type: 'image', attachment: … }`。
|
|
19953
|
-
* 这正是平台自家 `read_image` 的形态(本机 live 安装 `dsh-tool-fs`
|
|
19954
|
-
* `render: (_args, value) => imageReadContent(value)`),本文件照该形态实现。
|
|
20049
|
+
* 这正是平台自家 `read_image` 的形态(本机 live 安装 `dsh-tool-fs` 取证),本文件照该形态实现。
|
|
19955
20050
|
*
|
|
19956
20051
|
* ⚠️ 因此 render 里那个 `as unknown as ContentBlock` 是**必要的**(本包不引 `@deepseek-ai/dsh-attachment`,
|
|
19957
20052
|
* 它不是 peerDep ⇒ 拿不到 `AttachmentId` 的 brand),而它**同时让类型检查对字段名变瞎**(判据 ㉜)⇒
|
|
@@ -19959,7 +20054,7 @@ async function rasterizeSvg(svg, width) {
|
|
|
19959
20054
|
*/
|
|
19960
20055
|
/** 输出宽度缺省(矢量无损放大;高度按图自身比例推出) */
|
|
19961
20056
|
const DEFAULT_WIDTH = 1200;
|
|
19962
|
-
/** 时间戳:ISO → `YYYYMMDD-HHMMSS
|
|
20057
|
+
/** 时间戳:ISO → `YYYYMMDD-HHMMSS`(仅降级落盘的文件名用) */
|
|
19963
20058
|
function stamp() {
|
|
19964
20059
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace("T", "-").slice(0, 15);
|
|
19965
20060
|
}
|
|
@@ -19968,7 +20063,7 @@ function stamp() {
|
|
|
19968
20063
|
*
|
|
19969
20064
|
* 🔴 为什么这道门必须过(而不是"先附上试试"):图片块进的是**工具结果内容**,
|
|
19970
20065
|
* 若路由不收图,最坏情形是**整轮请求**被装配层拒掉 —— 比"没图"坏得多。
|
|
19971
|
-
* ⚠️ 与 `read_image`
|
|
20066
|
+
* ⚠️ 与 `read_image` 唯一的差别:**这里不抛错**(改走"降级落盘 + 如实说明")。
|
|
19972
20067
|
*/
|
|
19973
20068
|
async function routeDeclaresImage(ctx, exec) {
|
|
19974
20069
|
const agent = exec.agent;
|
|
@@ -19981,39 +20076,46 @@ async function routeDeclaresImage(ctx, exec) {
|
|
|
19981
20076
|
const modes = (await llm.resolveModelInfo(provider, model, signal))?.inputModalities;
|
|
19982
20077
|
return Array.isArray(modes) && modes.includes("image");
|
|
19983
20078
|
}
|
|
20079
|
+
/** 降级落盘(**只在"贴不进对话"时走**):PNG 落 `<CCC>/_tmp/diagram/`,返回相对路径或 `null` */
|
|
20080
|
+
function saveFallbackPng(exec, png, base) {
|
|
20081
|
+
const root = cccRootForExec(exec);
|
|
20082
|
+
if (root === null) return null;
|
|
20083
|
+
try {
|
|
20084
|
+
const rel = join("_tmp", "diagram");
|
|
20085
|
+
const dir = join(root, rel);
|
|
20086
|
+
mkdirSync(dir, { recursive: true });
|
|
20087
|
+
writeFileSync(join(dir, `${base}.png`), png);
|
|
20088
|
+
return join(rel, `${base}.png`);
|
|
20089
|
+
} catch {
|
|
20090
|
+
return null;
|
|
20091
|
+
}
|
|
20092
|
+
}
|
|
19984
20093
|
/**
|
|
19985
|
-
* 落成**可进对话的图片块**所需的 durable 引用(async 段 —— 见文件头契约 ①)。
|
|
20094
|
+
* 把 PNG 落成**可进对话的图片块**所需的 durable 引用(async 段 —— 见文件头契约 ①)。
|
|
19986
20095
|
*
|
|
19987
|
-
*
|
|
19988
|
-
*
|
|
20096
|
+
* 四条降级**都不抛错**(图的价值不允许"附不上图"把整条回执一起带走),且都**如实带回原因**;
|
|
20097
|
+
* 降级时顺手把 PNG 落盘一次(见文件头"唯一的例外"):
|
|
19989
20098
|
* ① 附件服务不在(未挂载) ② 部署不收 `image/png` ③ 当前路由未声明图像输入 ④ `saveImage` 自身报错。
|
|
19990
20099
|
*/
|
|
19991
|
-
async function
|
|
19992
|
-
const
|
|
19993
|
-
if (attachments === void 0 || typeof attachments.saveImage !== "function") return {
|
|
19994
|
-
image: null,
|
|
19995
|
-
imageSkip: "附件服务未挂载(ctx.attachments 不在)"
|
|
19996
|
-
};
|
|
19997
|
-
if (!attachments.imageLimits.mediaTypes.includes("image/png")) return {
|
|
20100
|
+
async function resolveImage(ctx, exec, png, base) {
|
|
20101
|
+
const skip = (reason) => ({
|
|
19998
20102
|
image: null,
|
|
19999
|
-
imageSkip:
|
|
20000
|
-
|
|
20103
|
+
imageSkip: reason,
|
|
20104
|
+
fallbackRel: saveFallbackPng(exec, png, base)
|
|
20105
|
+
});
|
|
20106
|
+
const attachments = ctx.get?.("attachments");
|
|
20107
|
+
if (attachments === void 0 || typeof attachments.saveImage !== "function") return skip("附件服务未挂载(ctx.attachments 不在)");
|
|
20108
|
+
if (!attachments.imageLimits.mediaTypes.includes("image/png")) return skip("本部署不收 image/png");
|
|
20001
20109
|
try {
|
|
20002
|
-
if (!await routeDeclaresImage(ctx, exec)) return
|
|
20003
|
-
image: null,
|
|
20004
|
-
imageSkip: "当前模型路由未声明图像输入(同 read_image 的门)"
|
|
20005
|
-
};
|
|
20110
|
+
if (!await routeDeclaresImage(ctx, exec)) return skip("当前模型路由未声明图像输入(同 read_image 的门)");
|
|
20006
20111
|
} catch (err) {
|
|
20007
|
-
return {
|
|
20008
|
-
image: null,
|
|
20009
|
-
imageSkip: `模型路由未能解析:${String(err?.message ?? err)}`
|
|
20010
|
-
};
|
|
20112
|
+
return skip(`模型路由未能解析:${String(err?.message ?? err)}`);
|
|
20011
20113
|
}
|
|
20012
20114
|
try {
|
|
20013
20115
|
const ref = await attachments.saveImage({
|
|
20014
20116
|
data: png,
|
|
20015
20117
|
mediaType: "image/png",
|
|
20016
|
-
name: `${
|
|
20118
|
+
name: `${base}.png`
|
|
20017
20119
|
});
|
|
20018
20120
|
return {
|
|
20019
20121
|
image: {
|
|
@@ -20022,21 +20124,19 @@ async function attachPng(ctx, exec, png, name) {
|
|
|
20022
20124
|
bytes: ref.bytes,
|
|
20023
20125
|
width: ref.width,
|
|
20024
20126
|
height: ref.height,
|
|
20025
|
-
name: ref.name ?? `${
|
|
20127
|
+
name: ref.name ?? `${base}.png`
|
|
20026
20128
|
},
|
|
20027
|
-
imageSkip: null
|
|
20129
|
+
imageSkip: null,
|
|
20130
|
+
fallbackRel: null
|
|
20028
20131
|
};
|
|
20029
20132
|
} catch (err) {
|
|
20030
|
-
return {
|
|
20031
|
-
image: null,
|
|
20032
|
-
imageSkip: `附件服务拒收:${String(err?.message ?? err)}`
|
|
20033
|
-
};
|
|
20133
|
+
return skip(`附件服务拒收:${String(err?.message ?? err)}`);
|
|
20034
20134
|
}
|
|
20035
20135
|
}
|
|
20036
20136
|
function createDiagramTool(ctx) {
|
|
20037
20137
|
return defineTool({
|
|
20038
20138
|
name: "diagram",
|
|
20039
|
-
description: "
|
|
20139
|
+
description: "Draw a diagram and show it to the user IN THE CONVERSATION (in-process, no browser, no files written). Pass the source directly as tag format. Geometry: coordinates are canvas pixels with y growing DOWNWARD; the drawing window defaults to the canvas (0,0 to width,height) — only pass domain=\"x0,y0,x1,y1\" if you want your own window. Tags: <rect id=\"A\" box=\"x0,y0,x1,y1\" label=\"...\" fill=\"#hex|colorName\"/> (name= works as an alias of id), <arrow from=\"A.e\" to=\"B.w\"/> or <line>, endpoints use anchors n/s/e/w/ne/nw/se/sw/c and may be offset like A.n+3,0, or a raw \"x,y\" pair; <text x=\"..\" y=\"..\" align=\"left|center|right\">text</text>, <color name=\"x\" value=\"#hex\"/>, <legend x=\"..\" y=\"..\"/>, <title>/<subtitle>. Returns the image itself plus any authoring errors and four layout warnings (overlapping boxes / label overflow / dangling endpoints / a line crossing a label). Elements whose coordinates fall outside the drawing window are reported as errors and skipped; the rest still render. Example:\n<diagram width=\"1000\" height=\"600\" title=\"三层\">\n <rect id=\"a\" box=\"100,60,400,140\" label=\"接入层\" fill=\"#dbe7f6\"/>\n <rect id=\"b\" box=\"100,200,400,280\" label=\"服务层\" fill=\"#dbe7f6\"/>\n <arrow from=\"a.s\" to=\"b.n\"/>\n</diagram>",
|
|
20040
20140
|
parameters: {
|
|
20041
20141
|
source: {
|
|
20042
20142
|
type: "string",
|
|
@@ -20049,7 +20149,7 @@ function createDiagramTool(ctx) {
|
|
|
20049
20149
|
},
|
|
20050
20150
|
name: {
|
|
20051
20151
|
type: "string",
|
|
20052
|
-
description: "Optional short name used
|
|
20152
|
+
description: "Optional short name (attachment label; also used if a degraded fallback file is written)"
|
|
20053
20153
|
}
|
|
20054
20154
|
},
|
|
20055
20155
|
output: {
|
|
@@ -20057,9 +20157,10 @@ function createDiagramTool(ctx) {
|
|
|
20057
20157
|
render: (_args, value) => {
|
|
20058
20158
|
const v = value;
|
|
20059
20159
|
const lines = [`图已生成(${v.marks} 个图元 / ${v.pxW}×${v.pxH} px / ${v.bytes} B PNG / 光栅化 ${v.ms} ms)`];
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20160
|
+
if (v.imageSkip !== null) {
|
|
20161
|
+
lines.push(`⚠️ 未能把图贴进对话:${v.imageSkip}`);
|
|
20162
|
+
lines.push(v.fallbackRel === null ? "(本会话解析不到 CCC 根,连降级落盘也没做 ⇒ 这一次没有可看的东西)" : `已降级落盘:${v.fallbackRel}(可直接打开,或用 read_image 读它)`);
|
|
20163
|
+
}
|
|
20063
20164
|
if (v.errors.length > 0) {
|
|
20064
20165
|
lines.push(`✗ 源码错误 ${v.errors.length} 条(图已尽力渲染;下列图元已跳过)`);
|
|
20065
20166
|
for (const e of v.errors) lines.push(` [err] ${e}`);
|
|
@@ -20078,25 +20179,16 @@ function createDiagramTool(ctx) {
|
|
|
20078
20179
|
}
|
|
20079
20180
|
},
|
|
20080
20181
|
async execute(args, exec) {
|
|
20081
|
-
const root = cccRootForExec(exec);
|
|
20082
|
-
if (root === null) throw new Error("diagram: 找不到 CCC 根(本会话缺 header.cwd)—— 产物必须落在容器内");
|
|
20083
20182
|
const src = typeof args.source === "string" ? args.source : "";
|
|
20084
20183
|
const name = typeof args.name === "string" && args.name.trim() !== "" ? args.name.trim() : "diagram";
|
|
20085
20184
|
const width = typeof args.width === "number" && Number.isFinite(args.width) && args.width > 0 ? Math.round(args.width) : DEFAULT_WIDTH;
|
|
20086
20185
|
const parsed = parseDsl(src);
|
|
20087
20186
|
const rendered = renderDsl(parsed.model);
|
|
20088
20187
|
const { png, ms, width: pxW, height: pxH } = await rasterizeSvg(rendered.svg, width);
|
|
20089
|
-
const rel = join("_tmp", "diagram");
|
|
20090
|
-
const dir = join(root, rel);
|
|
20091
|
-
mkdirSync(dir, { recursive: true });
|
|
20092
20188
|
const base = `${stamp()}-${name.replace(/[^\w\u4e00-\u9fa5-]/g, "_")}`;
|
|
20093
|
-
|
|
20094
|
-
writeFileSync(join(dir, `${base}.png`), png);
|
|
20095
|
-
const { image, imageSkip } = await attachPng(ctx, exec, png, base);
|
|
20189
|
+
const { image, imageSkip, fallbackRel } = await resolveImage(ctx, exec, png, base);
|
|
20096
20190
|
return {
|
|
20097
20191
|
name: base,
|
|
20098
|
-
svgRel: join(rel, `${base}.svg`),
|
|
20099
|
-
pngRel: join(rel, `${base}.png`),
|
|
20100
20192
|
lint: rendered.lint,
|
|
20101
20193
|
errors: [...parsed.errors, ...rendered.errors],
|
|
20102
20194
|
marks: rendered.marks,
|
|
@@ -20105,7 +20197,8 @@ function createDiagramTool(ctx) {
|
|
|
20105
20197
|
pxW,
|
|
20106
20198
|
pxH,
|
|
20107
20199
|
image,
|
|
20108
|
-
imageSkip
|
|
20200
|
+
imageSkip,
|
|
20201
|
+
fallbackRel
|
|
20109
20202
|
};
|
|
20110
20203
|
}
|
|
20111
20204
|
});
|
|
@@ -20414,19 +20507,50 @@ function apply(ctx, config) {
|
|
|
20414
20507
|
* 归属与边界(E↑):**机制归 ACC**(工具面 + 开关 + 光栅化),**图的语义归调用方**
|
|
20415
20508
|
* (标签语法与四项体检在 `src/diagram/`,权威设计 = 插件仓 `docs/diagram-tool-feasibility.md`)。
|
|
20416
20509
|
*
|
|
20417
|
-
* 🔴
|
|
20418
|
-
*
|
|
20419
|
-
*
|
|
20420
|
-
*
|
|
20510
|
+
* 🔴 **v1.51.1 修的真缺陷(owner 实测:「我把开关开了,工具没出来」)**:
|
|
20511
|
+
* 面板拨开关**只改配置、不重跑 `apply`**(铁证 = 插件重启才刷新"武装于";`cordis.patch.yml` 里
|
|
20512
|
+
* `diagramEnabled: true` 已落盘而工具没注册)⇒ 旧实现"apply 时判一次"等于**开关要重启才生效**。
|
|
20513
|
+
* ⇒ 现在改成**反应式**:
|
|
20514
|
+
* ① 听宿主的**真事件** `settings/document-updated`(ns = 我们那张设置页)——顺带说明为什么不用
|
|
20515
|
+
* 我们自己的 `serenity/settings-changed`:那个事件**全仓没有任何 `emit` 点**(只有 `ctx.on` 侧
|
|
20516
|
+
* 在用)=**死通道**,照着它接线等于接了个永远不响的铃;
|
|
20517
|
+
* ② 兜底再挂 `agent/created` / `session/created`(与 skiff/acp 同款做法,那两条在实测里确实会 fire)
|
|
20518
|
+
* —— 宿主事件面若换名,至少还有这一条能自愈;
|
|
20519
|
+
* ③ 启动时同步一次(apply 时按当前配置定初始态)。
|
|
20520
|
+
* 🔴 **关的时候要注销**(不是"留着但不响应"):与"关时不注册"同一口径 —— 工具不出现在模型清单里。
|
|
20421
20521
|
* @param ctx - 插件上下文
|
|
20422
20522
|
*/
|
|
20423
20523
|
function registerDiagramTool(ctx) {
|
|
20424
|
-
|
|
20524
|
+
/** 当前注册的 disposer(`null` = 未注册)。宿主 `ctx.tools.register` **返回注销器**。 */
|
|
20525
|
+
let dispose = null;
|
|
20526
|
+
const sync = () => {
|
|
20527
|
+
const on = readSimpleSettings().diagramEnabled;
|
|
20528
|
+
if (on && dispose === null) {
|
|
20529
|
+
try {
|
|
20530
|
+
dispose = ctx.tools.register(createDiagramTool(ctx));
|
|
20531
|
+
} catch (err) {
|
|
20532
|
+
console.error(`[serenity-hooks] ✗ diagram 工具注册失败: ${String(err?.message ?? err)}`);
|
|
20533
|
+
}
|
|
20534
|
+
return;
|
|
20535
|
+
}
|
|
20536
|
+
if (!on && dispose !== null) {
|
|
20537
|
+
try {
|
|
20538
|
+
dispose();
|
|
20539
|
+
} catch {}
|
|
20540
|
+
dispose = null;
|
|
20541
|
+
}
|
|
20542
|
+
};
|
|
20425
20543
|
try {
|
|
20426
|
-
ctx.
|
|
20427
|
-
|
|
20428
|
-
|
|
20429
|
-
}
|
|
20544
|
+
ctx.on("settings/document-updated", (ns) => {
|
|
20545
|
+
if (String(ns) === "serenity-hooks") sync();
|
|
20546
|
+
});
|
|
20547
|
+
} catch {}
|
|
20548
|
+
for (const eventName of ["agent/created", "session/created"]) try {
|
|
20549
|
+
ctx.on(eventName, () => {
|
|
20550
|
+
sync();
|
|
20551
|
+
});
|
|
20552
|
+
} catch {}
|
|
20553
|
+
sync();
|
|
20430
20554
|
}
|
|
20431
20555
|
/**
|
|
20432
20556
|
* F4 Skiff 调试服务装配:启停 = 人工(设置面板 Skiff 区块开关,settings 持久化)。
|
|
@@ -68,6 +68,10 @@ var settings_section_exports = /* @__PURE__ */ __exportAll({
|
|
|
68
68
|
simpleSettingsFromConfig: () => simpleSettingsFromConfig,
|
|
69
69
|
unwrapVolatile: () => unwrapVolatile
|
|
70
70
|
});
|
|
71
|
+
/**
|
|
72
|
+
* 设置面板命名空间 = **profile 条目 id**(v0.1.7 起;与旧 `settings.yaml` 段名逐字相同)。
|
|
73
|
+
*/
|
|
74
|
+
const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
71
75
|
/** 进程级默认(无 Config 可读时的兜底;与 Config 各字段的缺省一致) */
|
|
72
76
|
function defaultSimpleSettings() {
|
|
73
77
|
return {
|
|
@@ -218,4 +222,4 @@ function registerSettingsSection(ctx, config) {
|
|
|
218
222
|
} catch {}
|
|
219
223
|
}
|
|
220
224
|
//#endregion
|
|
221
|
-
export {
|
|
225
|
+
export { ACP_HTTP_PORT as a, MAIN_WEB_PORT as c, WEIXIN_SEND_PORT as d, settings_section_exports as i, MECHANISM_PORTS as l, readSimpleSettings as n, FACE_PORTS as o, registerSettingsSection as r, GATEWAY_PORT as s, SERENITY_SETTINGS_NS as t, SKIFF_DEBUG_PORT as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.51.
|
|
3
|
+
"version": "1.51.2",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:12 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/im-bridge/acc-diag/diagram)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"assets/README.md",
|
|
34
34
|
"assets/diagram/DroidSansFallbackFull.ttf",
|
|
35
35
|
"assets/diagram/LICENSE-Apache-2.0.txt",
|
|
36
|
+
"assets/diagram/NotoSans-Regular.ttf",
|
|
37
|
+
"assets/diagram/LICENSE-OFL-1.1.txt",
|
|
38
|
+
"assets/diagram/DejaVuSans.ttf",
|
|
39
|
+
"assets/diagram/LICENSE-DejaVu.txt",
|
|
36
40
|
"assets/diagram/PROVENANCE.md",
|
|
37
41
|
"cordis.patch.yml",
|
|
38
42
|
"dsh.plugin.json",
|
|
@@ -115,6 +119,7 @@
|
|
|
115
119
|
"@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.1",
|
|
116
120
|
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
|
|
117
121
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
|
|
122
|
+
"@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.1",
|
|
118
123
|
"@deepseek-ai/dsh-compaction": "0.1.7-rc.1",
|
|
119
124
|
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
|
|
120
125
|
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|