@shgroup/dsh-serenity-hooks 1.51.2 → 1.51.4
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/dsh.plugin.json +1 -1
- package/lib/{ccc-DG4Oc7I1.js → ccc-DXky9UZu.js} +26 -1
- package/lib/{ccc-roots-DtiMVRPo.js → ccc-roots-Bo0HKLlM.js} +2 -2
- package/lib/ccc.d.ts +30 -0
- package/lib/client.js +246 -14
- package/lib/index.js +104 -39
- package/lib/{message-source-LnJwIqLm.js → message-source-BExngUPX.js} +1 -1
- package/lib/seams/guards.d.ts +10 -2
- package/lib/{skiff-role-DQZ6eR51.js → skiff-role-BSg6Qu9k.js} +1 -1
- package/lib/status.d.ts +2 -0
- package/lib/{weixin-route-7BtOiy4X.js → weixin-route-BNyH5ghC.js} +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
|
|
|
80
80
|
| `container_admin` | 机务舱:管理子角色、管理小工具注册表、查看全部配置 | 定义"子角色"、注册新小工具、改配置时 |
|
|
81
81
|
| `im-bridge` | 给 IM 联系人发消息(目前是微信):发文本、发文件、查已配置联系人、查通道状态。每次成功发送自动进工作区的消息记录 | 想让 AI 主动给家人/同事发消息(见 §6.6)。**只在工作区配了微信桥时才出现**,且只能发本工作区的消息 |
|
|
82
82
|
| `acc-diag` | ACC 运行态诊断:一次调用出全报告——当前有多少会话活着、各自属于哪个工作区、唤醒时钟的武装态与 tick 次数、唤醒登记表的每一条(含状态 / 投递结果 / 补跑窗口) | **ACC 维护者专用**。**默认对所有工作区隐藏**,只有在工作区配置里点名(`exclusiveTools`)才出现 |
|
|
83
|
-
| `diagram` | 画图:AI 给一段**标签式源码**(`<diagram>` 根 + `<rect>` / `<arrow>` / `<text>` / `<color>` / `<legend>`),插件**在进程内**把它画成 PNG
|
|
83
|
+
| `diagram` | 画图:AI 给一段**标签式源码**(`<diagram>` 根 + `<rect>` / `<arrow>` / `<text>` / `<color>` / `<legend>`),插件**在进程内**把它画成 PNG 并**贴进对话**(**工具卡里内联一份,本轮回答的尾部再显示一份**——工具卡可折叠,尾部那份折不掉;两处用**同一份 durable 附件**,**不写文件、不占内存**。可回看、可被后续轮次引用)——**不启浏览器、不截图、正常路径不落文件**。坐标 = **画布像素、y 向下**(`domain="x0,y0,x1,y1"` 可选);写错的图元**逐条报错并跳过**,其余照画。自带四项体检(块重叠 / 标签溢出 / 悬空端点 / 线压字),**只告警不拦图** | 想把关系图/示意图直接放进对话时。**实验性 demo,默认关**:关着的时候这个工具**根本不注册**(AI 看不到它,不占上下文);开关在设置面板「工具」组,**拨动即时生效**(不必重启)。中英文与常用符号(`①` `⇒` `→` `★`)都能画,是因为**三个字型随插件内置**(汉字 + 拉丁 + 符号,合计约 5 MB) |
|
|
84
84
|
|
|
85
85
|
> **改过名**(旧名已彻底停用,没有兼容别名):下面每组的箭头链是**逐个发布版本**的名字,**末项才是今名**——`cc_fs` → `container_fs` · `cc_git` → `container_git` · `session`+`session_rebuild` → `logbook`(v1.30/1.31)→ `trajectory`(v1.32)→ **`container_trajectory`**(v1.34,今名) · `acc_kit` → `dashboard` · `acc_msm` → `msm`(执行)+ `container_admin`(管理)· `eap`/`neat`/`cce` → `praxis` · `skiff_admin` → `container_admin role` · `autopilot-trajectory` → `trajectory`(v1.32)→ **`container_trajectory`**(v1.34,今名)。老会话里看到旧名,**一律取所在那一组的末项**。
|
|
86
86
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.51.
|
|
3
|
+
"version": "1.51.4",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「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(deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
@@ -156,6 +156,31 @@ function readExclusiveTools(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
156
156
|
return list.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim());
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
159
|
+
* 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
|
|
160
|
+
*
|
|
161
|
+
* 语义(R↓):**标记文件存在 ⇒ 才生效**。deny = `['bash', ...本清单]`(并集去重);
|
|
162
|
+
* 标记消失 ⇒ 连同 `bash` 一起解除(调用点在 `syncSafeModeRestriction`,与基础表同一处)。
|
|
163
|
+
*
|
|
164
|
+
* 失败方向 = **fail-open**(与 `exclusiveTools` 的 fail-closed 相反,理由不同):
|
|
165
|
+
* 未配置 / 字段缺失 / 配置损坏 ⇒ **空数组** = 只隐藏基础表。本键是**附加**屏蔽,
|
|
166
|
+
* 读不到声明就等于"没有证据要求多藏";且**隐藏是危险方向**(藏掉 agent 真正要用的工具
|
|
167
|
+
* 会静默损害能力),而配置损坏本身已有 `loadSerenityConfig` 的响亮告警(不静默)。
|
|
168
|
+
*
|
|
169
|
+
* 🔴 名字在此**不做**校验——未注册的名字会被 DSH `restrict` 拒绝(宿主契约逐字:
|
|
170
|
+
* "Empty filters, unknown names, scope-local names, and reserved transport names **fail**")
|
|
171
|
+
* ⇒ **调用方必须按"失败退化为基础表"处理**(见 `syncSafeModeRestriction`),
|
|
172
|
+
* 否则 CCC 写错一个名字会连 `bash` 的隐藏一起丢掉。
|
|
173
|
+
*
|
|
174
|
+
* @param root CCC 根
|
|
175
|
+
* @param paths 候选配置相对路径
|
|
176
|
+
* @returns 去空白后的工具名列表(非字符串/空白项被丢弃)
|
|
177
|
+
*/
|
|
178
|
+
function readSafeModeHiddenTools(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
179
|
+
const list = loadSerenityConfig(root, paths).safeMode?.hiddenTools;
|
|
180
|
+
if (!Array.isArray(list)) return [];
|
|
181
|
+
return list.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim());
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
159
184
|
* 读取 CCC 声明的「轨迹默认 skill」清单(`trajectory.skills`,v1.44.0)。
|
|
160
185
|
*
|
|
161
186
|
* 语义与失败方向(R↓):
|
|
@@ -214,4 +239,4 @@ function matchBlacklist(relPath, rules) {
|
|
|
214
239
|
return null;
|
|
215
240
|
}
|
|
216
241
|
//#endregion
|
|
217
|
-
export { findSerenityRoot as a, matchBlacklist as c, readCccName as d, readExclusiveTools as f,
|
|
242
|
+
export { resolveInside as _, findSerenityRoot as a, matchBlacklist as c, readCccName as d, readExclusiveTools as f, readUtf8 as g, readTrajectorySkills as h, findGitRoot as i, pathInside as l, readSafeModeHiddenTools as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readHandymanConfig as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { a as hostSessions, i as hostService } from "./access-CNccNtgf.js";
|
|
3
|
-
import { a as findSerenityRoot } from "./ccc-
|
|
3
|
+
import { a as findSerenityRoot } from "./ccc-DXky9UZu.js";
|
|
4
4
|
import { basename } from "node:path";
|
|
5
5
|
//#region src/ccc-roots.ts
|
|
6
6
|
/**
|
|
@@ -150,7 +150,7 @@ async function listCccs(ctx, opts = {}) {
|
|
|
150
150
|
if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
|
|
151
151
|
let rolesOf = null;
|
|
152
152
|
if (opts.withRoles === true) {
|
|
153
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
153
|
+
const { readSkiffRoles } = await import("./skiff-role-BSg6Qu9k.js").then((n) => n.u);
|
|
154
154
|
rolesOf = (root) => [...readSkiffRoles(root).keys()];
|
|
155
155
|
}
|
|
156
156
|
return c.roots.map((root) => ({
|
package/lib/ccc.d.ts
CHANGED
|
@@ -57,6 +57,15 @@ interface SerenityConfig {
|
|
|
57
57
|
};
|
|
58
58
|
safeMode?: {
|
|
59
59
|
blacklist?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* **安全模式开启期间**额外从模型工具清单隐藏的工具(v1.51.4,owner 2026-10-09 令)。
|
|
62
|
+
*
|
|
63
|
+
* 语义(与 `exclusiveTools` 的区别,判据是"条件"):本键不是无条件的可见性白名单,
|
|
64
|
+
* 而是**条件屏蔽**——`\.serenity-safe-on` 标记存在时才隐藏,标记消失即**全部恢复**
|
|
65
|
+
* (基础表恒隐 `bash` 不受影响)。机制在 ACC、声明在 CCC(D23):
|
|
66
|
+
* ACC 代码里不出现任何具体工具名。
|
|
67
|
+
*/
|
|
68
|
+
hiddenTools?: string[];
|
|
60
69
|
};
|
|
61
70
|
/** localstore git 提交策略(S134 重设计):allow 可提交 / deny 禁提交(缺省 deny) */
|
|
62
71
|
localstore?: {
|
|
@@ -311,6 +320,27 @@ export declare function loadSerenityConfig(root: string, paths?: string[]): Sere
|
|
|
311
320
|
* @returns 去空白后的工具名列表(非字符串项被丢弃)
|
|
312
321
|
*/
|
|
313
322
|
export declare function readExclusiveTools(root: string, paths?: string[]): string[];
|
|
323
|
+
/**
|
|
324
|
+
* 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
|
|
325
|
+
*
|
|
326
|
+
* 语义(R↓):**标记文件存在 ⇒ 才生效**。deny = `['bash', ...本清单]`(并集去重);
|
|
327
|
+
* 标记消失 ⇒ 连同 `bash` 一起解除(调用点在 `syncSafeModeRestriction`,与基础表同一处)。
|
|
328
|
+
*
|
|
329
|
+
* 失败方向 = **fail-open**(与 `exclusiveTools` 的 fail-closed 相反,理由不同):
|
|
330
|
+
* 未配置 / 字段缺失 / 配置损坏 ⇒ **空数组** = 只隐藏基础表。本键是**附加**屏蔽,
|
|
331
|
+
* 读不到声明就等于"没有证据要求多藏";且**隐藏是危险方向**(藏掉 agent 真正要用的工具
|
|
332
|
+
* 会静默损害能力),而配置损坏本身已有 `loadSerenityConfig` 的响亮告警(不静默)。
|
|
333
|
+
*
|
|
334
|
+
* 🔴 名字在此**不做**校验——未注册的名字会被 DSH `restrict` 拒绝(宿主契约逐字:
|
|
335
|
+
* "Empty filters, unknown names, scope-local names, and reserved transport names **fail**")
|
|
336
|
+
* ⇒ **调用方必须按"失败退化为基础表"处理**(见 `syncSafeModeRestriction`),
|
|
337
|
+
* 否则 CCC 写错一个名字会连 `bash` 的隐藏一起丢掉。
|
|
338
|
+
*
|
|
339
|
+
* @param root CCC 根
|
|
340
|
+
* @param paths 候选配置相对路径
|
|
341
|
+
* @returns 去空白后的工具名列表(非字符串/空白项被丢弃)
|
|
342
|
+
*/
|
|
343
|
+
export declare function readSafeModeHiddenTools(root: string, paths?: string[]): string[];
|
|
314
344
|
/**
|
|
315
345
|
* 读取 CCC 声明的「轨迹默认 skill」清单(`trajectory.skills`,v1.44.0)。
|
|
316
346
|
*
|
package/lib/client.js
CHANGED
|
@@ -8,11 +8,11 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SafeModePanel
|
|
11
|
-
const css$4 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n width: min(680px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n max-height: calc(100vh - 120px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n min-height: 260px; /* 动画的下限:信息很少时也不塌成一条 */\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(420px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n flex: none;\n width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* handyman 运行状态区块 */\n\n.sp-handymenToggle {\n flex: none;\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n cursor: pointer;\n}\n\n.sp-handymenToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-handymenList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 2px;\n}\n\n.sp-handymanItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n min-width: 0;\n}\n\n.sp-handymanHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-handymanLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-handymanRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanResp {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sp-handymenMore {\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n padding: 4px 12px;\n}\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
11
|
+
const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n width: min(680px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n max-height: calc(100vh - 120px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n min-height: 260px; /* 动画的下限:信息很少时也不塌成一条 */\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(420px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n flex: none;\n width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* handyman 运行状态区块 */\n\n.sp-handymenToggle {\n flex: none;\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n cursor: pointer;\n}\n\n.sp-handymenToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-handymenList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 2px;\n}\n\n.sp-handymanItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n min-width: 0;\n}\n\n.sp-handymanHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-handymanLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-handymanRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanResp {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sp-handymenMore {\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n padding: 4px 12px;\n}\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
12
12
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SafeModePanel\"]") === null) {
|
|
13
13
|
const tag = document.createElement("style");
|
|
14
14
|
tag.setAttribute("data-sp-css", "SafeModePanel");
|
|
15
|
-
tag.textContent = css$
|
|
15
|
+
tag.textContent = css$5;
|
|
16
16
|
document.head.appendChild(tag);
|
|
17
17
|
}
|
|
18
18
|
//#endregion
|
|
@@ -3313,11 +3313,11 @@ window.__ModuleLoader__.load({
|
|
|
3313
3313
|
}
|
|
3314
3314
|
//#endregion
|
|
3315
3315
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/AccountsEditor
|
|
3316
|
-
const css$
|
|
3316
|
+
const css$4 = "/* AccountsEditor — DSH 设置面板「外部访问」区块(v1.22)\n * 与 SettingsSection ss-* 同设计语言:--dsw-alias-* 语义 token,明暗自适应。 */\n\n.ae-root {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n font-size: 13px;\n}\n\n/* ── v1.27.5 紧凑化:折叠块(details/summary)── */\n.ae-collapse {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.ae-collapseHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n list-style: none;\n user-select: none;\n}\n\n.ae-collapseHead::-webkit-details-marker {\n display: none;\n}\n\n.ae-collapseHead::before {\n content: '▸';\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n transition: transform 0.15s ease;\n}\n\n.ae-collapse[open] > .ae-collapseHead::before {\n transform: rotate(90deg);\n}\n\n.ae-collapseHead:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.06));\n}\n\n.ae-collapseDesc {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.ae-collapseBody {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 12px 10px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n.ae-warn {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n}\n\n.ae-block {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.ae-title {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.ae-desc {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n.ae-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* ── 监听设置 / 白名单输入行 ── */\n\n.ae-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.ae-label {\n flex: none;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.ae-input {\n flex: 1;\n min-width: 0;\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n outline: none;\n}\n\n.ae-input:focus {\n border-color: var(--dsw-alias-button-ghost-active-border);\n}\n\n.ae-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.ae-input.ae-port {\n flex: none;\n width: 76px;\n}\n\n/* ── 账号列表 ── */\n\n.ae-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ae-head {\n display: flex;\n gap: 8px;\n font-size: 11px;\n font-weight: 500;\n color: var(--dsw-alias-label-dimmed);\n padding: 0 2px;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n\n.ae-item {\n display: flex;\n gap: 8px;\n align-items: center;\n padding: 6px 2px;\n}\n\n.ae-colUser { flex: 1.2; }\n.ae-colPass { flex: 1.6; }\n.ae-colTotp { flex: 2.2; min-width: 0; }\n.ae-colOp { flex: none; width: 48px; }\n\n/* ── v1.22.4 TOTP 第二因素 ── */\n\n.ae-totpBtn {\n flex: none;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.ae-totpBtn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.ae-totpPending {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: center;\n min-width: 0;\n}\n\n/* v1.24.6:TOTP 绑定二维码(扫码即录入;scalable SVG,白底保证扫码识别) */\n.ae-totpQr {\n flex-basis: 100%;\n display: inline-flex;\n justify-content: center;\n padding: 8px;\n background: #fff;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n}\n\n.ae-totpQr svg {\n width: 132px;\n height: 132px;\n display: block;\n}\n\n.ae-totpSecret {\n font-family: var(--ds-font-family-code);\n font-size: 11px;\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 4px;\n padding: 2px 6px;\n color: var(--dsw-alias-label-secondary);\n word-break: break-all;\n}\n\n.ae-totpUri {\n font-size: 11px;\n color: var(--dsw-alias-button-info-fill);\n text-decoration: none;\n}\n\n.ae-totpUri:hover {\n text-decoration: underline;\n}\n\n/* v1.24.7:绑定提示(去确认码后——扫码或手动录入,保存即绑定) */\n.ae-totpHint {\n flex-basis: 100%;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.ae-totpBadge {\n font-size: 12px;\n color: var(--dsw-alias-state-warn-label);\n}\n\n.ae-totpClear {\n display: flex;\n gap: 6px;\n align-items: center;\n}\n\n.ae-check {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n margin-top: 10px;\n cursor: pointer;\n}\n\n.ae-check input {\n accent-color: var(--dsw-alias-button-info-fill);\n}\n\n.ae-del {\n flex: none;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.ae-del:hover {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n.ae-add {\n align-self: flex-start;\n padding: 5px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.ae-add:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.ae-wsSelect {\n margin-top: 8px;\n max-width: 100%;\n cursor: pointer;\n}\n\n/* ── 工作区白名单 chips ── */\n\n.ae-wsList {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.ae-wsChip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.14));\n}\n\n.ae-wsPath {\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n}\n\n.ae-wsDel {\n flex: none;\n width: 18px;\n height: 18px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--dsw-alias-label-dimmed);\n font-size: 13px;\n line-height: 1;\n cursor: pointer;\n}\n\n.ae-wsDel:hover {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* ── 错误 / 操作区 ── */\n\n.ae-error {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n word-break: break-word;\n}\n\n.ae-actions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n.ae-save {\n padding: 7px 24px;\n border: 0;\n border-radius: 8px;\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n}\n\n.ae-save:hover:not(:disabled) {\n filter: brightness(1.08);\n}\n\n.ae-save:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* ── v1.26.2 建议问答页配置(PublicAskEditor:开放容器白名单 chips + key/地址展示)── */\n\n.ae-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 8px 0 4px;\n}\n\n.ae-chip {\n padding: 4px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n\n.ae-chip:hover {\n border-color: var(--dsw-alias-button-ghost-active-border);\n}\n\n.ae-chipOn {\n background: var(--dsw-alias-button-primary-fill);\n border-color: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.ae-saved {\n margin-left: 10px;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.ae-err {\n margin-left: 10px;\n font-size: 12px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.ae-key {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n padding: 2px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n word-break: break-all;\n user-select: all;\n}\n\n.ae-copy {\n flex: none;\n margin-left: 8px;\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.ae-copy:hover {\n border-color: var(--dsw-alias-button-ghost-active-border);\n color: var(--dsw-alias-label-primary);\n}\n\n/* v1.26.5 轮换 key(警示色:旧 key 立即失效) */\n.ae-rotate {\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, transparent);\n}\n\n.ae-rotate:hover {\n border-color: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-state-error-primary);\n}\n\n.ae-rotate:disabled {\n opacity: 0.6;\n cursor: wait;\n}\n\n.ae-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 8px;\n}\n\n.ae-labelDim {\n opacity: 0.65;\n min-width: 96px;\n}\n";
|
|
3317
3317
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"AccountsEditor\"]") === null) {
|
|
3318
3318
|
const tag = document.createElement("style");
|
|
3319
3319
|
tag.setAttribute("data-sp-css", "AccountsEditor");
|
|
3320
|
-
tag.textContent = css$
|
|
3320
|
+
tag.textContent = css$4;
|
|
3321
3321
|
document.head.appendChild(tag);
|
|
3322
3322
|
}
|
|
3323
3323
|
//#endregion
|
|
@@ -3730,11 +3730,11 @@ window.__ModuleLoader__.load({
|
|
|
3730
3730
|
}
|
|
3731
3731
|
//#endregion
|
|
3732
3732
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/PersonaEditor
|
|
3733
|
-
const css$
|
|
3733
|
+
const css$3 = "/* PersonaEditor.css — 彩蛋模式区块(v1.23.1,官方 settings 设计语言:rowCard 内分组)\n * token 合规(v1.27.14 修复):全部使用 design-platform.css 官方 --dsw-alias-* 词汇;\n * 弃用自造名(text-primary/accent/border/state-success 等)——未定义时深色 fallback\n * 在浅色主题下显示为黑框黑字(用户反馈白色主题通性问题)。 */\n\n.pe-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n width: 100%;\n padding: 2px 0 6px;\n}\n\n.pe-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.pe-label {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.pe-input,\n.pe-textarea {\n width: 100%;\n box-sizing: border-box;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font-size: 13px;\n padding: 8px 10px;\n outline: none;\n}\n\n.pe-input:focus,\n.pe-textarea:focus {\n border-color: var(--dsw-alias-button-ghost-active-border);\n}\n\n.pe-textarea {\n resize: vertical;\n min-height: 72px; /* v1.27.5 紧凑化:默认 4 行(原 120px 过高),可拖拽扩大 */\n line-height: 1.5;\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n}\n\n.pe-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n flex-wrap: wrap;\n}\n\n.pe-btn {\n border-radius: 6px;\n padding: 5px 12px;\n font-size: 12px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n transition: opacity 0.15s;\n}\n\n.pe-btn:hover:not(:disabled) {\n opacity: 0.85;\n}\n\n.pe-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.pe-btnGhost {\n border-color: var(--dsw-alias-border-l2);\n}\n\n.pe-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n border-color: transparent;\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.pe-hint {\n font-size: 12px;\n margin: 0;\n}\n\n.pe-hintOk {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.pe-hintErr {\n color: var(--dsw-alias-state-error-primary);\n}\n";
|
|
3734
3734
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"PersonaEditor\"]") === null) {
|
|
3735
3735
|
const tag = document.createElement("style");
|
|
3736
3736
|
tag.setAttribute("data-sp-css", "PersonaEditor");
|
|
3737
|
-
tag.textContent = css$
|
|
3737
|
+
tag.textContent = css$3;
|
|
3738
3738
|
document.head.appendChild(tag);
|
|
3739
3739
|
}
|
|
3740
3740
|
//#endregion
|
|
@@ -4663,11 +4663,11 @@ Instruction-following style:
|
|
|
4663
4663
|
}
|
|
4664
4664
|
//#endregion
|
|
4665
4665
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SettingsSection
|
|
4666
|
-
const css$1 = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* ── v1.29 需求②:可展开行(整行点击 → 行内详设)── */\n.ss-expandable {\n flex-wrap: wrap; /* detail 换行占满整行 */\n cursor: default;\n}\n.ss-expandable .ss-rowText {\n cursor: pointer;\n}\n.ss-expandable .ss-rowText:hover .ss-rowName {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expanded {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expandMark {\n display: inline-block;\n margin-left: 6px;\n font-size: 10px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n.ss-expanded .ss-expandMark {\n transform: rotate(90deg);\n}\n.ss-rowDetail {\n flex-basis: 100%;\n width: 100%;\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 10px;\n padding-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n}\n.ss-detailStack {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.ss-detailIntro {\n margin: 0;\n font-size: 12px;\n line-height: 19px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.06));\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary;v1.27.5 紧凑化:默认单行截断,hover title 看全文) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-border-l3);\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-brand-primary);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 错误提示(v1.29 会话清理等) */\n.ss-error {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary, #ef4444);\n background: rgba(239, 68, 68, 0.08);\n border-radius: 8px;\n padding: 8px 10px;\n white-space: pre-line;\n word-break: break-word;\n}\n\n/* ── v1.27.5 紧凑化:折叠组(details/summary)── */\n.ss-collapse {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.ss-collapseHead {\n display: flex;\n align-items: baseline;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n list-style: none;\n user-select: none;\n}\n\n.ss-collapseHead::-webkit-details-marker {\n display: none;\n}\n\n.ss-collapseHead::before {\n content: '▸';\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n\n.ss-collapse[open] > .ss-collapseHead::before {\n transform: rotate(90deg);\n}\n\n.ss-collapseHead:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.06));\n}\n\n.ss-collapseTitle {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.ss-collapseDesc {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.ss-collapseBody {\n padding: 10px 14px 14px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── v1.27.5 紧凑化:只读定义列表(dl/dt/dd 2 列网格)── */\n.ss-defList {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px 20px;\n margin: 0;\n width: 100%;\n min-width: 0;\n}\n\n.ss-defItem {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.ss-defTerm {\n font-size: 11px;\n line-height: 16px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.ss-defValue {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ── v1.27.5 紧凑化:开关 + 端口内联行 ── */\n.ss-switchRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* ── v1.27.6 「?」帮助浮层(hover/焦点显示三行以上严谨说明)── */\n.ss-help {\n position: relative;\n display: inline-flex;\n align-items: center;\n margin-top: 2px;\n}\n\n.ss-helpMark {\n flex: none;\n width: 16px;\n height: 16px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 50%;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n cursor: help;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease, color 0.15s ease;\n}\n\n.ss-helpMark:hover,\n.ss-helpMark:focus-visible {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n outline: none;\n}\n\n.ss-helpTip {\n position: absolute;\n left: 0;\n top: calc(100% + 6px);\n z-index: 30;\n width: 300px;\n max-width: 60vw;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);\n font-size: 12px;\n line-height: 19px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n white-space: pre-line;\n word-break: break-word;\n opacity: 0;\n pointer-events: none;\n transform: translateY(-4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.ss-help:hover .ss-helpTip,\n.ss-helpMark:focus-visible + .ss-helpTip {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n}\n\n/* 折叠组内帮助浮层定位修正(左侧行卡不会溢出面板) */\n.ss-rowCard .ss-helpTip {\n left: 0;\n}\n\n/* ── 微信桥(F4c-3,v1.27.0 实验性)── */\n\n/* CCC 选择器(下拉) */\n.ss-select {\n max-width: 280px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* 扫码绑定按钮(复用 wakeBtn 语言) */\n.ss-loginBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-loginBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-loginBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 移除账号按钮(危险语义:红边框) */\n.ss-removeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-removeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary, #ef4444);\n color: var(--dsw-alias-state-error-primary, #ef4444);\n}\n\n/* 二维码展示行 */\n.ss-qrRow {\n align-items: center;\n}\n\n.ss-qrSvg {\n flex: none;\n width: 120px; /* v1.27.5 紧凑化:160 → 120 */\n height: 120px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: #fff; /* 二维码需要白底才能被微信扫出 */\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ss-qrSvg svg {\n width: 100%;\n height: 100%;\n}\n\n/* 路由表 JSON 编辑框 */\n.ss-routesEditor {\n width: 100%;\n box-sizing: border-box;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-family: var(--ds-font-family-code);\n resize: vertical;\n}\n";
|
|
4666
|
+
const css$2 = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* ── v1.29 需求②:可展开行(整行点击 → 行内详设)── */\n.ss-expandable {\n flex-wrap: wrap; /* detail 换行占满整行 */\n cursor: default;\n}\n.ss-expandable .ss-rowText {\n cursor: pointer;\n}\n.ss-expandable .ss-rowText:hover .ss-rowName {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expanded {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expandMark {\n display: inline-block;\n margin-left: 6px;\n font-size: 10px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n.ss-expanded .ss-expandMark {\n transform: rotate(90deg);\n}\n.ss-rowDetail {\n flex-basis: 100%;\n width: 100%;\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 10px;\n padding-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n}\n.ss-detailStack {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.ss-detailIntro {\n margin: 0;\n font-size: 12px;\n line-height: 19px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.06));\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary;v1.27.5 紧凑化:默认单行截断,hover title 看全文) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-border-l3);\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-brand-primary);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 错误提示(v1.29 会话清理等) */\n.ss-error {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary, #ef4444);\n background: rgba(239, 68, 68, 0.08);\n border-radius: 8px;\n padding: 8px 10px;\n white-space: pre-line;\n word-break: break-word;\n}\n\n/* ── v1.27.5 紧凑化:折叠组(details/summary)── */\n.ss-collapse {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.ss-collapseHead {\n display: flex;\n align-items: baseline;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n list-style: none;\n user-select: none;\n}\n\n.ss-collapseHead::-webkit-details-marker {\n display: none;\n}\n\n.ss-collapseHead::before {\n content: '▸';\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n\n.ss-collapse[open] > .ss-collapseHead::before {\n transform: rotate(90deg);\n}\n\n.ss-collapseHead:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.06));\n}\n\n.ss-collapseTitle {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.ss-collapseDesc {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.ss-collapseBody {\n padding: 10px 14px 14px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── v1.27.5 紧凑化:只读定义列表(dl/dt/dd 2 列网格)── */\n.ss-defList {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px 20px;\n margin: 0;\n width: 100%;\n min-width: 0;\n}\n\n.ss-defItem {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.ss-defTerm {\n font-size: 11px;\n line-height: 16px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.ss-defValue {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ── v1.27.5 紧凑化:开关 + 端口内联行 ── */\n.ss-switchRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* ── v1.27.6 「?」帮助浮层(hover/焦点显示三行以上严谨说明)── */\n.ss-help {\n position: relative;\n display: inline-flex;\n align-items: center;\n margin-top: 2px;\n}\n\n.ss-helpMark {\n flex: none;\n width: 16px;\n height: 16px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 50%;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n cursor: help;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease, color 0.15s ease;\n}\n\n.ss-helpMark:hover,\n.ss-helpMark:focus-visible {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n outline: none;\n}\n\n.ss-helpTip {\n position: absolute;\n left: 0;\n top: calc(100% + 6px);\n z-index: 30;\n width: 300px;\n max-width: 60vw;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);\n font-size: 12px;\n line-height: 19px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n white-space: pre-line;\n word-break: break-word;\n opacity: 0;\n pointer-events: none;\n transform: translateY(-4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.ss-help:hover .ss-helpTip,\n.ss-helpMark:focus-visible + .ss-helpTip {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n}\n\n/* 折叠组内帮助浮层定位修正(左侧行卡不会溢出面板) */\n.ss-rowCard .ss-helpTip {\n left: 0;\n}\n\n/* ── 微信桥(F4c-3,v1.27.0 实验性)── */\n\n/* CCC 选择器(下拉) */\n.ss-select {\n max-width: 280px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* 扫码绑定按钮(复用 wakeBtn 语言) */\n.ss-loginBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-loginBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-loginBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 移除账号按钮(危险语义:红边框) */\n.ss-removeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-removeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary, #ef4444);\n color: var(--dsw-alias-state-error-primary, #ef4444);\n}\n\n/* 二维码展示行 */\n.ss-qrRow {\n align-items: center;\n}\n\n.ss-qrSvg {\n flex: none;\n width: 120px; /* v1.27.5 紧凑化:160 → 120 */\n height: 120px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: #fff; /* 二维码需要白底才能被微信扫出 */\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ss-qrSvg svg {\n width: 100%;\n height: 100%;\n}\n\n/* 路由表 JSON 编辑框 */\n.ss-routesEditor {\n width: 100%;\n box-sizing: border-box;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-family: var(--ds-font-family-code);\n resize: vertical;\n}\n";
|
|
4667
4667
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SettingsSection\"]") === null) {
|
|
4668
4668
|
const tag = document.createElement("style");
|
|
4669
4669
|
tag.setAttribute("data-sp-css", "SettingsSection");
|
|
4670
|
-
tag.textContent = css$
|
|
4670
|
+
tag.textContent = css$2;
|
|
4671
4671
|
document.head.appendChild(tag);
|
|
4672
4672
|
}
|
|
4673
4673
|
//#endregion
|
|
@@ -5180,6 +5180,11 @@ Instruction-following style:
|
|
|
5180
5180
|
"image/webp",
|
|
5181
5181
|
"image/gif"
|
|
5182
5182
|
];
|
|
5183
|
+
/**
|
|
5184
|
+
* 本插件认领的 wire Tool 名(= 客户端两处的唯一真相源,v1.51.3 起):
|
|
5185
|
+
* 工具卡行(`tool.call.toolview` 的 key)与轮尾行(从 Chat 快照里挑图)都读它,**不再各写一份字面量**。
|
|
5186
|
+
*/
|
|
5187
|
+
const DIAGRAM_TOOL_NAME = "diagram";
|
|
5183
5188
|
/** 非空对象视图(`null` = 不是对象)。 */
|
|
5184
5189
|
function recordOf(value) {
|
|
5185
5190
|
return typeof value === "object" && value !== null ? value : null;
|
|
@@ -5253,17 +5258,15 @@ Instruction-following style:
|
|
|
5253
5258
|
}
|
|
5254
5259
|
//#endregion
|
|
5255
5260
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/DiagramToolView
|
|
5256
|
-
const css = "/*\n * DiagramToolView.css — `diagram` 工具行(Chat 视图)\n *\n * 🔴 只用 design-platform 的**官方** token(`--dsw-alias-*`)。\n * 自造 token(如 --sp-color / --text-primary)在浅色主题下 var() 永不解析 ⇒ 永远走深色 fallback\n * ⇒ 黑块 / 黑字(2026-09-04 S142 用户反馈的成因)。`tests/client-css-tokens.test.ts` 机械守卫此约束。\n * 本文件用到的 5 个 token 均在官方词汇表内:bg-layer-1 / border-l1 /\n * label-primary / label-secondary / state-error-primary。\n */\n\n.sp-dgRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.sp-dgTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n}\n\n.sp-dgHint {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 图自身有像素尺寸;这里只兜住容器宽度(窄屏不溢出)与圆角/描边 */\n.sp-dgImage {\n display: block;\n max-width: 100%;\n height: auto;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n/* 信封文字:结果原文里的换行是有意排版的(一行一个图元/lint 项)⇒ 保留换行 */\n.sp-dgText {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.sp-dgError {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n";
|
|
5261
|
+
const css$1 = "/*\n * DiagramToolView.css — `diagram` 工具行(Chat 视图)\n *\n * 🔴 只用 design-platform 的**官方** token(`--dsw-alias-*`)。\n * 自造 token(如 --sp-color / --text-primary)在浅色主题下 var() 永不解析 ⇒ 永远走深色 fallback\n * ⇒ 黑块 / 黑字(2026-09-04 S142 用户反馈的成因)。`tests/client-css-tokens.test.ts` 机械守卫此约束。\n * 本文件用到的 5 个 token 均在官方词汇表内:bg-layer-1 / border-l1 /\n * label-primary / label-secondary / state-error-primary。\n */\n\n.sp-dgRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.sp-dgTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n}\n\n.sp-dgHint {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 图自身有像素尺寸;这里只兜住容器宽度(窄屏不溢出)与圆角/描边 */\n.sp-dgImage {\n display: block;\n max-width: 100%;\n height: auto;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n/* 信封文字:结果原文里的换行是有意排版的(一行一个图元/lint 项)⇒ 保留换行 */\n.sp-dgText {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.sp-dgError {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n";
|
|
5257
5262
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"DiagramToolView\"]") === null) {
|
|
5258
5263
|
const tag = document.createElement("style");
|
|
5259
5264
|
tag.setAttribute("data-sp-css", "DiagramToolView");
|
|
5260
|
-
tag.textContent = css;
|
|
5265
|
+
tag.textContent = css$1;
|
|
5261
5266
|
document.head.appendChild(tag);
|
|
5262
5267
|
}
|
|
5263
5268
|
//#endregion
|
|
5264
5269
|
//#region src/client/DiagramToolView.tsx
|
|
5265
|
-
/** 本行认领的 wire Tool 名(= 注册键;拼错则永不派发)。 */
|
|
5266
|
-
const DIAGRAM_TOOL_NAME = "diagram";
|
|
5267
5270
|
/**
|
|
5268
5271
|
* 用会话授权的 loader 把 durable 引用换成浏览器 URL。
|
|
5269
5272
|
*
|
|
@@ -5376,12 +5379,235 @@ Instruction-following style:
|
|
|
5376
5379
|
});
|
|
5377
5380
|
}
|
|
5378
5381
|
//#endregion
|
|
5382
|
+
//#region src/client/diagram-tail.ts
|
|
5383
|
+
/**
|
|
5384
|
+
* 递归上限:宿主自己的 `MAX_TOOL_CALL_TREE_DEPTH` 是 256,这里只需要"别被畸形/超深树拖住",
|
|
5385
|
+
* 取一个小得多的值 —— 超过就**停止下探**(已经拿到的照常返回,不抛)。
|
|
5386
|
+
*/
|
|
5387
|
+
const MAX_DEPTH = 32;
|
|
5388
|
+
/** 单轮里最多看多少个 tool-call 根(防一次超长轮把渲染拖住)。 */
|
|
5389
|
+
const MAX_ROOTS = 512;
|
|
5390
|
+
/**
|
|
5391
|
+
* 该块是不是**已结算**的工具结果。
|
|
5392
|
+
*
|
|
5393
|
+
* `'kind' in block` 先把联合类型收窄到**唯一带 `kind` 的成员**(运行中的两个只有 `phase`),
|
|
5394
|
+
* 再比字面量值 —— 全程无 cast(理由见文件头)。
|
|
5395
|
+
* @param block - 候选块。
|
|
5396
|
+
* @returns 是否为已结算结果(`true` 时类型收窄为 {@link ToolResultNode})。
|
|
5397
|
+
*/
|
|
5398
|
+
function isSettled(block) {
|
|
5399
|
+
return "kind" in block && block.kind === "tool-result";
|
|
5400
|
+
}
|
|
5401
|
+
/**
|
|
5402
|
+
* 深度优先收集一个工具调用子树里的 diagram 图(**发现顺序** = 该轮的调用顺序)。
|
|
5403
|
+
* @param block - 工具调用块(只有**已结算且工具名匹配**的才可能带图;运行中有子调用也照常下探)。
|
|
5404
|
+
* @param out - 收集器。
|
|
5405
|
+
* @param depth - 当前深度(超过 {@link MAX_DEPTH} 即停)。
|
|
5406
|
+
*/
|
|
5407
|
+
function collect(block, out, depth) {
|
|
5408
|
+
if (depth > MAX_DEPTH || block === null || typeof block !== "object") return;
|
|
5409
|
+
if (isSettled(block) && block.call?.name === "diagram") {
|
|
5410
|
+
const image = diagramResultModel(block).image;
|
|
5411
|
+
if (image !== null) out.push({
|
|
5412
|
+
callId: block.callId,
|
|
5413
|
+
seq: block.seq,
|
|
5414
|
+
image
|
|
5415
|
+
});
|
|
5416
|
+
}
|
|
5417
|
+
const children = Array.isArray(block.subCalls) ? block.subCalls : [];
|
|
5418
|
+
for (const child of children) collect(child, out, depth + 1);
|
|
5419
|
+
}
|
|
5420
|
+
/**
|
|
5421
|
+
* 挑出**某一轮**里 `diagram` 产出的图。
|
|
5422
|
+
*
|
|
5423
|
+
* 输入 = 该 Turn 的 tool-call **根**列表(调用方用宿主的
|
|
5424
|
+
* `chat.nodes.turnDataSource(turn, 'tool-call')` 取,**含被隐藏的节点** ⇒ 折叠与否都不影响结果)。
|
|
5425
|
+
* @param roots - 本轮的工具调用根(顺序 = 锚点顺序)。
|
|
5426
|
+
* @returns 按发现顺序排列的图(无图 ⇒ 空数组;任何畸形输入都不抛)。
|
|
5427
|
+
*/
|
|
5428
|
+
function diagramImagesOfRoots(roots) {
|
|
5429
|
+
if (!Array.isArray(roots)) return [];
|
|
5430
|
+
const out = [];
|
|
5431
|
+
let seen = 0;
|
|
5432
|
+
for (const root of roots) {
|
|
5433
|
+
if (seen >= MAX_ROOTS) break;
|
|
5434
|
+
seen += 1;
|
|
5435
|
+
collect(root, out, 0);
|
|
5436
|
+
}
|
|
5437
|
+
return out;
|
|
5438
|
+
}
|
|
5439
|
+
//#endregion
|
|
5440
|
+
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/DiagramImageTail
|
|
5441
|
+
const css = "/**\n * DiagramImageTail.css — 轮尾图行的样式(v1.51.3)\n *\n * 与 `DiagramToolView.css` 同规矩:**只用官方 `--dsw-alias-*` 词汇表里的令牌**,\n * 圆角/尺寸用字面量(`--dsw-radius-*` **不在**官方 alias 词汇表里,`client-css-tokens.test.ts`\n * 会把它当自造 token 拦下 —— 浅色主题下会变成黑块)。\n */\n.sp-dtRoot {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin-top: 12px;\n}\n\n.sp-dtImage {\n max-width: 100%;\n height: auto;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n/* 字节还在读(或读数被拒)时的占位 —— 与图的左边缘对齐,避免行高跳动 */\n.sp-dtPending {\n width: 160px;\n height: 100px;\n border-radius: 6px;\n background: var(--dsw-alias-interactive-bg-hover);\n}\n";
|
|
5442
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"DiagramImageTail\"]") === null) {
|
|
5443
|
+
const tag = document.createElement("style");
|
|
5444
|
+
tag.setAttribute("data-sp-css", "DiagramImageTail");
|
|
5445
|
+
tag.textContent = css;
|
|
5446
|
+
document.head.appendChild(tag);
|
|
5447
|
+
}
|
|
5448
|
+
//#endregion
|
|
5449
|
+
//#region src/client/DiagramImageTail.tsx
|
|
5450
|
+
/**
|
|
5451
|
+
* client/DiagramImageTail.tsx — **本轮回答尾部**的 `diagram` 图(v1.51.3)
|
|
5452
|
+
*
|
|
5453
|
+
* ## 它修的是什么
|
|
5454
|
+
*
|
|
5455
|
+
* v1.51.2 让图出现在**工具卡**里 —— 但工具卡是**可折叠**的宿主外壳,折起来图就没了。
|
|
5456
|
+
* owner 的诉求:**图要出现在「回答」区域,且仍然不落盘**。
|
|
5457
|
+
*
|
|
5458
|
+
* ## 为什么是"槽"而不是"markdown 里的地址"(两条路都量过,2026-09-30)
|
|
5459
|
+
*
|
|
5460
|
+
* - **markdown 内联图**:宿主契约逐字写着 *"Images additionally require absolute HTTP(S)"*,
|
|
5461
|
+
* 且渲染器的 DOM 被 fixtures **逐字钉死**(`md-render.d.ts` 首行)—— 那是宿主包,我们**零改 DSH**;
|
|
5462
|
+
* 那条"把自定义目的地解成可显示 URL"的缝(`pathImages`)在宿主组件里用 `useMemo` 写死成
|
|
5463
|
+
* 本地文件路径解析、**不查任何服务** ⇒ 插件塞不进去。⇒ **"包装地址"这条路不成**。
|
|
5464
|
+
* - **`conversation.chat.turnTail`**:宿主声明的 **list 槽**,专用于"在一轮完成后的尾部追加功能行"
|
|
5465
|
+
* (官方 `ui-deliverables` 就是这么挂交付物的)⇒ 图的渲染**归我们**,于是既不需要地址、
|
|
5466
|
+
* 也不需要新的 HTTP 路由。
|
|
5467
|
+
*
|
|
5468
|
+
* ## 数据与 URL 从哪来(**都不新增存储**)
|
|
5469
|
+
*
|
|
5470
|
+
* - **图**:宿主自己的 Chat 快照 —— `ctx.uiConversation.binding(sessionId).target('chat')`,
|
|
5471
|
+
* 再用 `chat.nodes.turnDataSource(turn, 'tool-call')` 取**本轮**的工具调用根
|
|
5472
|
+
* (契约原话:*"Observe one Turn's data for a single Node kind, **including hidden Nodes**, in anchor order"*
|
|
5473
|
+
* ⇒ 工具卡折叠与否都不影响取数)→ 交给纯模块 `diagram-tail.ts` 挑图。
|
|
5474
|
+
* - **URL**:宿主自己的 per-session 授权 URL 缓存 `ctx.uiConversation.imageUrl(sessionId, ref)`
|
|
5475
|
+
* (先问 `peekImageUrl`,命中则**不发起读取**)。字节仍在**宿主的 durable 附件库**里
|
|
5476
|
+
* (`src/tools/diagram.ts` 的 `attachments.saveImage` 产出的那个引用)
|
|
5477
|
+
* ⇒ **不落工作区文件、不常驻内存**。🔴 因此**不做**任何"自己存一份图"的设计
|
|
5478
|
+
* (owner 2026-09-30 明确否掉过 loop/内存常驻:那会长期占内存)。
|
|
5479
|
+
*
|
|
5480
|
+
* ## 两个"被契约纠正"的实现细节(实测 0.1.7-rc.1)
|
|
5481
|
+
*
|
|
5482
|
+
* 1. 🔴 **本槽需要 `inject` 面**(与线上 0.2.0-rc.1 的 `TurnTailOwnerProps` 契约不同)——
|
|
5483
|
+
* tsc 直接报 *"Property 'inject' is missing … but required"*。故按**官方同款**走 `inject`,
|
|
5484
|
+
* 组件用 `InjectFace<DiagramTailInjected>` 声明自己的面(`ui-deliverables` 亦如此)。
|
|
5485
|
+
* 2. 🔴 **`sessionId` 是品牌类型 `SessionId`,不是 `string`**(`string` 赋不进去)。
|
|
5486
|
+
* 这里不引入 `@deepseek-ai/dsh-session/types` 新依赖,直接从本行 props 上取类型。
|
|
5487
|
+
*/
|
|
5488
|
+
/** 没有可用目标快照时的稳定空订阅(`useSyncExternalStore` 要求引用稳定)。 */
|
|
5489
|
+
const NO_SUBSCRIBE = () => () => {};
|
|
5490
|
+
/** 没有可用目标快照时的稳定空读数。 */
|
|
5491
|
+
const NO_SNAPSHOT = () => void 0;
|
|
5492
|
+
/**
|
|
5493
|
+
* 取该会话的 Chat 目标快照源。
|
|
5494
|
+
*
|
|
5495
|
+
* 宿主契约写明 `binding()` 在**会话未知或绑定已换代**时**抛错** ⇒ 这里按"本轮不显示"处理
|
|
5496
|
+
* (不是错误态:换会话/竞态都会走到这里)。
|
|
5497
|
+
* @param scope - 客户端 ctx(含 `uiConversation`)。
|
|
5498
|
+
* @param sessionId - 槽的 session 作用域。
|
|
5499
|
+
* @returns 目标快照源,或 `null`(拿不到 ⇒ 不渲染)。
|
|
5500
|
+
*/
|
|
5501
|
+
function chatTargetOf(scope, sessionId) {
|
|
5502
|
+
try {
|
|
5503
|
+
return scope.uiConversation.binding(sessionId).target("chat");
|
|
5504
|
+
} catch {
|
|
5505
|
+
return null;
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* 造一行的注入面(在 `index.ts` 注册时由 `scope` 闭包)。
|
|
5510
|
+
* @param scope - 客户端 ctx。
|
|
5511
|
+
* @returns 该槽的注入面。
|
|
5512
|
+
*/
|
|
5513
|
+
function diagramTailInjected(scope) {
|
|
5514
|
+
return {
|
|
5515
|
+
chatTarget: (sessionId) => chatTargetOf(scope, sessionId),
|
|
5516
|
+
peekImageUrl: (sessionId, ref) => {
|
|
5517
|
+
try {
|
|
5518
|
+
return scope.uiConversation.peekImageUrl(sessionId, ref) ?? null;
|
|
5519
|
+
} catch {
|
|
5520
|
+
return null;
|
|
5521
|
+
}
|
|
5522
|
+
},
|
|
5523
|
+
loadImageUrl: (sessionId, ref) => {
|
|
5524
|
+
try {
|
|
5525
|
+
return scope.uiConversation.imageUrl(sessionId, ref).catch(() => null);
|
|
5526
|
+
} catch {
|
|
5527
|
+
return Promise.resolve(null);
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
};
|
|
5531
|
+
}
|
|
5532
|
+
/**
|
|
5533
|
+
* 把每张图的 durable 引用解成**浏览器可直接用的 URL**(先查缓存,未命中才发起读取)。
|
|
5534
|
+
* @param injected - 本行的注入面。
|
|
5535
|
+
* @param sessionId - 授权作用域。
|
|
5536
|
+
* @param images - 本轮挑出的图(顺序即渲染顺序)。
|
|
5537
|
+
* @returns 与 `images` 等长的 URL 数组;未就绪或读取失败的位置为 `null`。
|
|
5538
|
+
*/
|
|
5539
|
+
function useTailUrls(injected, sessionId, images) {
|
|
5540
|
+
const [urls, setUrls] = (0, react.useState)([]);
|
|
5541
|
+
(0, react.useEffect)(() => {
|
|
5542
|
+
let live = true;
|
|
5543
|
+
const initial = images.map((entry) => injected.peekImageUrl(sessionId, entry.image));
|
|
5544
|
+
setUrls(initial);
|
|
5545
|
+
images.forEach((entry, index) => {
|
|
5546
|
+
if (initial[index] !== null) return;
|
|
5547
|
+
injected.loadImageUrl(sessionId, entry.image).then((url) => {
|
|
5548
|
+
if (!live || url === null) return;
|
|
5549
|
+
setUrls((prev) => prev.map((value, at) => at === index ? url : value));
|
|
5550
|
+
});
|
|
5551
|
+
});
|
|
5552
|
+
return () => {
|
|
5553
|
+
live = false;
|
|
5554
|
+
};
|
|
5555
|
+
}, [
|
|
5556
|
+
injected,
|
|
5557
|
+
sessionId,
|
|
5558
|
+
images
|
|
5559
|
+
]);
|
|
5560
|
+
return urls;
|
|
5561
|
+
}
|
|
5562
|
+
/**
|
|
5563
|
+
* `diagram` 的**轮尾图行**:注册进 `conversation.chat.turnTail`(`kind: 'list'`,入口 id `serenity-diagram-tail`)。
|
|
5564
|
+
*
|
|
5565
|
+
* 本轮没有图 ⇒ 返回 `null`("entries without content return null",不占位)。
|
|
5566
|
+
* @param props - 框架合成的槽 props。
|
|
5567
|
+
*/
|
|
5568
|
+
function DiagramImageTail(props) {
|
|
5569
|
+
const { sessionId, chatTarget, peekImageUrl, loadImageUrl } = props;
|
|
5570
|
+
const injected = (0, react.useMemo)(() => ({
|
|
5571
|
+
chatTarget,
|
|
5572
|
+
peekImageUrl,
|
|
5573
|
+
loadImageUrl
|
|
5574
|
+
}), [
|
|
5575
|
+
chatTarget,
|
|
5576
|
+
peekImageUrl,
|
|
5577
|
+
loadImageUrl
|
|
5578
|
+
]);
|
|
5579
|
+
const turn = props.turn?.turn;
|
|
5580
|
+
const source = (0, react.useMemo)(() => injected.chatTarget(sessionId), [injected, sessionId]);
|
|
5581
|
+
const chat = (0, react.useSyncExternalStore)((0, react.useMemo)(() => source === null ? NO_SUBSCRIBE : (notify) => source.subscribe(notify), [source]), (0, react.useMemo)(() => source === null ? NO_SNAPSHOT : () => source.getSnapshot(), [source]));
|
|
5582
|
+
const images = (0, react.useMemo)(() => {
|
|
5583
|
+
if (chat === void 0 || turn === void 0) return [];
|
|
5584
|
+
return diagramImagesOfRoots(chat.nodes.turnDataSource(turn, "tool-call").getSnapshot().map((data) => data.root));
|
|
5585
|
+
}, [chat, turn]);
|
|
5586
|
+
const urls = useTailUrls(injected, sessionId, images);
|
|
5587
|
+
if (images.length === 0) return null;
|
|
5588
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5589
|
+
className: "sp-dtRoot",
|
|
5590
|
+
"data-count": images.length,
|
|
5591
|
+
children: images.map((entry, index) => {
|
|
5592
|
+
const url = urls[index] ?? null;
|
|
5593
|
+
return url === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "sp-dtPending" }, entry.callId) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
5594
|
+
className: "sp-dtImage",
|
|
5595
|
+
src: url,
|
|
5596
|
+
alt: entry.image.name ?? "diagram",
|
|
5597
|
+
width: entry.image.width,
|
|
5598
|
+
height: entry.image.height
|
|
5599
|
+
}, entry.callId);
|
|
5600
|
+
})
|
|
5601
|
+
});
|
|
5602
|
+
}
|
|
5603
|
+
//#endregion
|
|
5379
5604
|
//#region src/client/index.ts
|
|
5380
5605
|
const inject = [
|
|
5381
5606
|
"slots",
|
|
5382
5607
|
"conversation",
|
|
5383
5608
|
"sessions",
|
|
5384
|
-
"configForms"
|
|
5609
|
+
"configForms",
|
|
5610
|
+
"uiConversation"
|
|
5385
5611
|
];
|
|
5386
5612
|
/** 设置页命名空间 = profile 条目 id(与 host 侧 `SERENITY_SETTINGS_NS` 同值;单一真相源在 host 侧常量,此处只作字面量引用) */
|
|
5387
5613
|
const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
@@ -5390,7 +5616,8 @@ Instruction-following style:
|
|
|
5390
5616
|
"slots",
|
|
5391
5617
|
"conversation",
|
|
5392
5618
|
"sessions",
|
|
5393
|
-
"configForms"
|
|
5619
|
+
"configForms",
|
|
5620
|
+
"uiConversation"
|
|
5394
5621
|
], (scope) => {
|
|
5395
5622
|
scope.effect(() => scope.slots.register({
|
|
5396
5623
|
name: "conversation.session.header.actions",
|
|
@@ -5430,6 +5657,11 @@ Instruction-following style:
|
|
|
5430
5657
|
name: "tool.call.toolview",
|
|
5431
5658
|
key: "diagram"
|
|
5432
5659
|
}, DiagramToolView)), "serenity: diagram tool view");
|
|
5660
|
+
scope.effect(() => scope.slots.inject("conversation.chat.turnTail", () => scope.slots.register({
|
|
5661
|
+
name: "conversation.chat.turnTail",
|
|
5662
|
+
id: "serenity-diagram-tail",
|
|
5663
|
+
inject: () => diagramTailInjected(scope)
|
|
5664
|
+
}, DiagramImageTail)), "serenity: diagram image tail");
|
|
5433
5665
|
});
|
|
5434
5666
|
}
|
|
5435
5667
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { a as hostSessions, c as hostWeb, i as hostService, l as hostWebServer, n as hostInjected, o as hostSettings, r as hostLlm, s as hostSubagents, t as hostAgents } from "./access-CNccNtgf.js";
|
|
3
|
-
import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$1, f as readExclusiveTools,
|
|
4
|
-
import { i as cccRootForExec, n as agentCwdFor, o as listCccs, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD } from "./ccc-roots-
|
|
5
|
-
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel, t as ACC_MESSAGE_KIND, u as TRAJECTORY_ACTIONS, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source-
|
|
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-
|
|
3
|
+
import { _ as resolveInside, a as findSerenityRoot, c as matchBlacklist, d as readCccName$1, f as readExclusiveTools, h as readTrajectorySkills, i as findGitRoot, l as pathInside, m as readSafeModeHiddenTools, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readHandymanConfig, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-DXky9UZu.js";
|
|
4
|
+
import { i as cccRootForExec, n as agentCwdFor, o as listCccs, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD } from "./ccc-roots-Bo0HKLlM.js";
|
|
5
|
+
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel, t as ACC_MESSAGE_KIND, u as TRAJECTORY_ACTIONS, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source-BExngUPX.js";
|
|
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-BNyH5ghC.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
|
-
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-
|
|
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-BSg6Qu9k.js";
|
|
9
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";
|
|
@@ -1091,6 +1091,7 @@ Directory of ACC capabilities — each area lists where to go for details (guide
|
|
|
1091
1091
|
⑦ 注册表与安全 → mech-registry.json 由 container_admin msm register/deregister 管理(写保护——不可直接编辑)
|
|
1092
1092
|
dashboard health 输出 registry 完整性检查;损坏恢复指引见 health registry.issues
|
|
1093
1093
|
CCC 级开关:exclusiveTools(声明专属工具)/ safeMode.blacklist(写路径黑名单)
|
|
1094
|
+
/ safeMode.hiddenTools(**安全模式开启期间**额外隐藏的工具,v1.51.4)
|
|
1094
1095
|
CCC 级 skill 供给:trajectory.skills(v1.44.0——**本 CCC 每条轨迹都带**,含 skiff 会话;与轨迹自己的 frontmatter 声明取并集)
|
|
1095
1096
|
|
|
1096
1097
|
每区:一句话定位 + 详细入口(工具名 / guide 子命令)。详情永远以对应工具的 guide/ccc-config 为单一真相源。
|
|
@@ -1205,11 +1206,21 @@ deny 时写入自动确保 .gitignore 含该文件(物理保证),container
|
|
|
1205
1206
|
Config:
|
|
1206
1207
|
{ "hooks": { "autoRestoreSession": false } }
|
|
1207
1208
|
|
|
1208
|
-
── 5. safeMode
|
|
1209
|
+
── 5. safeMode(黑名单 + 开启期间隐藏的工具)──
|
|
1209
1210
|
safe-mode 由 WebUI 开关控制(写 .serenity-safe-on 标记);黑名单路径受 guards seam 拦截。
|
|
1210
1211
|
|
|
1212
|
+
▸ blacklist:写路径黑名单(同下 Config)。
|
|
1213
|
+
▸ hiddenTools(v1.51.4,owner 2026-10-09 令):**仅在安全模式开启期间**额外从模型工具
|
|
1214
|
+
清单隐藏的工具(ACC 基础表恒隐 bash)。打开 safe-mode 生效、**关掉即全部恢复**;
|
|
1215
|
+
改本键**热生效**(下一个 pre-step 重算,无需重启)。声明在 CCC(D23)——ACC 代码里
|
|
1216
|
+
不出现任何具体工具名。
|
|
1217
|
+
⚠️ 写一个**本 CCC 未注册**的名字会让**整次 restrict 失败**(DSH 契约逐字:"unknown
|
|
1218
|
+
names … fail")⇒ 此时**退化为只隐藏基础表**并在日志响亮报错(绝不因一个错名丢掉
|
|
1219
|
+
safe-mode 的核心隐藏)。诊断:\`AGENT_SESSIONS/.restrict-diag.json\` 的 \`denyByKey\`。
|
|
1220
|
+
⚠️ 隐藏 = **从 schema 移除**(agent 根本看不到该工具)⇒ 别把 skill/文档还在教的工具写进来。
|
|
1221
|
+
|
|
1211
1222
|
Config:
|
|
1212
|
-
{ "safeMode": { "blacklist": [".secrets/"] } }
|
|
1223
|
+
{ "safeMode": { "blacklist": [".secrets/"], "hiddenTools": ["workflow", "job_kill"] } }
|
|
1213
1224
|
|
|
1214
1225
|
── 6. skiff.roles(F4 认知子集角色)──
|
|
1215
1226
|
Skiff 角色 = 全知全能 trajectory 的任意子集(CCC 定义)。每角色独立模型 + 双白名单
|
|
@@ -8713,21 +8724,25 @@ function decideGuard(input) {
|
|
|
8713
8724
|
function resolveAgentCwd(exec) {
|
|
8714
8725
|
return exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
8715
8726
|
}
|
|
8716
|
-
/**
|
|
8727
|
+
/**
|
|
8728
|
+
* 安全模式开启时从模型工具列表隐藏的工具 —— **基础表**(只隐藏 bash;write/edit 保留)。
|
|
8729
|
+
* 它**恒隐**、与 CCC 配置无关;CCC 声明的 `safeMode.hiddenTools` 是**附加**项(见下)。
|
|
8730
|
+
*/
|
|
8717
8731
|
const SAFE_MODE_DENY_TOOLS = ["bash"];
|
|
8718
|
-
/** agent key → restrict disposer(安全模式工具隐藏状态) */
|
|
8719
8732
|
const safeModeRestrictions = /* @__PURE__ */ new Map();
|
|
8720
8733
|
const restrictDiag = {
|
|
8721
8734
|
lastKey: null,
|
|
8722
8735
|
lastAttemptAt: null,
|
|
8723
8736
|
lastSuccess: null,
|
|
8724
8737
|
lastError: null,
|
|
8725
|
-
activeKeys: []
|
|
8738
|
+
activeKeys: [],
|
|
8739
|
+
denyByKey: {}
|
|
8726
8740
|
};
|
|
8727
8741
|
function getRestrictDiagnostics() {
|
|
8728
8742
|
return {
|
|
8729
8743
|
...restrictDiag,
|
|
8730
|
-
activeKeys: [...safeModeRestrictions.keys()]
|
|
8744
|
+
activeKeys: [...safeModeRestrictions.keys()],
|
|
8745
|
+
denyByKey: Object.fromEntries([...safeModeRestrictions].map(([k, v]) => [k, v.deny]))
|
|
8731
8746
|
};
|
|
8732
8747
|
}
|
|
8733
8748
|
/** 诊断落盘:AGENT_SESSIONS/.restrict-diag.json(文件通道,避免 HTTP 自锁) */
|
|
@@ -8742,35 +8757,85 @@ function writeRestrictDiag(root) {
|
|
|
8742
8757
|
} catch {}
|
|
8743
8758
|
}
|
|
8744
8759
|
/**
|
|
8745
|
-
* 同步安全模式工具隐藏:标记存在 → agent.ctx.tools.restrict deny
|
|
8746
|
-
*
|
|
8760
|
+
* 同步安全模式工具隐藏:标记存在 → `agent.ctx.tools.restrict deny` 隐藏
|
|
8761
|
+
* **基础表(bash)∪ CCC 声明的 `safeMode.hiddenTools`**;标记消失 → 全部解除。
|
|
8762
|
+
* pre-step 每步调用 ⇒ 开/关/**改声明**三种变化都实时生效(v1.51.4 起含"改声明")。
|
|
8763
|
+
*
|
|
8764
|
+
* 与 `readSafeModeHiddenTools` 的分工(R↓):本函数负责**形状与失败退化**,reader 只管取值。
|
|
8765
|
+
* 🔴 退化规则:DSH 契约规定 **未注册的工具名会让整次 restrict 失败**("unknown names … fail")
|
|
8766
|
+
* ⇒ 绝不能让 CCC 写错一个名字就丢掉 safe-mode 的核心隐藏(bash)。失败时退化为**只隐藏基础表**,
|
|
8767
|
+
* 并在日志响亮报错;退化态**按期望签名记账**,不会每步重试刷屏。
|
|
8747
8768
|
*/
|
|
8748
8769
|
function syncSafeModeRestriction(agent, root) {
|
|
8749
8770
|
const key = agent.session.id ?? "global";
|
|
8750
8771
|
const on = isSafeModeOn(root);
|
|
8751
8772
|
const existing = safeModeRestrictions.get(key);
|
|
8752
|
-
if (on
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8773
|
+
if (!on) {
|
|
8774
|
+
if (existing !== void 0) {
|
|
8775
|
+
try {
|
|
8776
|
+
existing.dispose();
|
|
8777
|
+
} catch (e) {
|
|
8778
|
+
console.error(`[serenity-hooks] restrict 解除失败 (key=${key}):`, e.message);
|
|
8779
|
+
}
|
|
8780
|
+
safeModeRestrictions.delete(key);
|
|
8781
|
+
}
|
|
8782
|
+
writeRestrictDiag(root);
|
|
8783
|
+
return;
|
|
8784
|
+
}
|
|
8785
|
+
let declared = [];
|
|
8786
|
+
try {
|
|
8787
|
+
declared = readSafeModeHiddenTools(root);
|
|
8788
|
+
} catch (e) {
|
|
8789
|
+
console.error(`[serenity-hooks] safeMode.hiddenTools 判据读取失败 (key=${key}):`, e.message);
|
|
8790
|
+
}
|
|
8791
|
+
const deny = [.../* @__PURE__ */ new Set([...SAFE_MODE_DENY_TOOLS, ...declared])];
|
|
8792
|
+
const signature = deny.join(",");
|
|
8793
|
+
if (existing !== void 0 && existing.signature === signature) {
|
|
8794
|
+
writeRestrictDiag(root);
|
|
8795
|
+
return;
|
|
8796
|
+
}
|
|
8797
|
+
if (existing !== void 0) {
|
|
8798
|
+
try {
|
|
8799
|
+
existing.dispose();
|
|
8800
|
+
} catch (e) {
|
|
8801
|
+
console.error(`[serenity-hooks] restrict 重算前解除失败 (key=${key}):`, e.message);
|
|
8802
|
+
}
|
|
8803
|
+
safeModeRestrictions.delete(key);
|
|
8804
|
+
}
|
|
8805
|
+
let applied = deny;
|
|
8806
|
+
let dispose;
|
|
8807
|
+
try {
|
|
8808
|
+
dispose = agent.ctx.tools.restrict({ deny: [...deny] });
|
|
8759
8809
|
} catch (e) {
|
|
8760
8810
|
restrictDiag.lastKey = key;
|
|
8761
8811
|
restrictDiag.lastAttemptAt = isoLocal();
|
|
8762
8812
|
restrictDiag.lastSuccess = false;
|
|
8763
8813
|
restrictDiag.lastError = e.message;
|
|
8764
|
-
console.error(`[serenity-hooks] restrict
|
|
8765
|
-
|
|
8766
|
-
else if (!on && existing) {
|
|
8814
|
+
console.error(`[serenity-hooks] restrict 失败,已退化为基础表(多为 safeMode.hiddenTools 里有本 CCC **未注册**的工具名)(key=${key}):`, e.message);
|
|
8815
|
+
applied = [...SAFE_MODE_DENY_TOOLS];
|
|
8767
8816
|
try {
|
|
8768
|
-
|
|
8769
|
-
} catch (
|
|
8770
|
-
|
|
8817
|
+
dispose = agent.ctx.tools.restrict({ deny: applied });
|
|
8818
|
+
} catch (e2) {
|
|
8819
|
+
restrictDiag.lastError = `${e.message} / 退化亦失败: ${e2.message}`;
|
|
8820
|
+
console.error(`[serenity-hooks] restrict 退化失败(守卫仍兜底拦截)(key=${key}):`, e2.message);
|
|
8821
|
+
safeModeRestrictions.set(key, {
|
|
8822
|
+
signature,
|
|
8823
|
+
deny: [],
|
|
8824
|
+
dispose: () => {}
|
|
8825
|
+
});
|
|
8826
|
+
writeRestrictDiag(root);
|
|
8827
|
+
return;
|
|
8771
8828
|
}
|
|
8772
|
-
safeModeRestrictions.delete(key);
|
|
8773
8829
|
}
|
|
8830
|
+
restrictDiag.lastKey = key;
|
|
8831
|
+
restrictDiag.lastAttemptAt = isoLocal();
|
|
8832
|
+
restrictDiag.lastSuccess = true;
|
|
8833
|
+
restrictDiag.lastError = null;
|
|
8834
|
+
safeModeRestrictions.set(key, {
|
|
8835
|
+
signature,
|
|
8836
|
+
deny: applied,
|
|
8837
|
+
dispose
|
|
8838
|
+
});
|
|
8774
8839
|
writeRestrictDiag(root);
|
|
8775
8840
|
}
|
|
8776
8841
|
/** 条件可见的 IM 工具名(与 tools/im-bridge.ts 的注册名一致) */
|
|
@@ -10393,11 +10458,11 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10393
10458
|
sendJson$4(res, 400, { error: chk.error });
|
|
10394
10459
|
return;
|
|
10395
10460
|
}
|
|
10396
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
10461
|
+
const { readWeixinSettings } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10397
10462
|
const { weixinBridgeStatus } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
10398
10463
|
const settings = readWeixinSettings(chk.root);
|
|
10399
10464
|
const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
|
|
10400
|
-
const { readWeixinCredential } = await import("./weixin-route-
|
|
10465
|
+
const { readWeixinCredential } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10401
10466
|
const accounts = (settings.accounts ?? []).map((a) => ({
|
|
10402
10467
|
accountId: a.accountId,
|
|
10403
10468
|
name: a.name ?? void 0,
|
|
@@ -10429,7 +10494,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10429
10494
|
const { fetchQRCode } = await import("./weixin-api-BrRcPiQs.js").then((n) => n.u);
|
|
10430
10495
|
const { randomUUID } = await import("node:crypto");
|
|
10431
10496
|
for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
|
|
10432
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
10497
|
+
const { readWeixinSettings } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10433
10498
|
const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
|
|
10434
10499
|
const loginKey = randomUUID();
|
|
10435
10500
|
weixinLogins.set(loginKey, {
|
|
@@ -10450,7 +10515,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10450
10515
|
sendJson$4(res, 400, { error: "missing accountId" });
|
|
10451
10516
|
return;
|
|
10452
10517
|
}
|
|
10453
|
-
const { removeWeixinAccount } = await import("./weixin-route-
|
|
10518
|
+
const { removeWeixinAccount } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10454
10519
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
10455
10520
|
removeWeixinAccount(root, body.accountId);
|
|
10456
10521
|
syncCccBridge(ctx, root);
|
|
@@ -10463,8 +10528,8 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10463
10528
|
sendJson$4(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
|
|
10464
10529
|
return;
|
|
10465
10530
|
}
|
|
10466
|
-
const { saveWeixinRoutes } = await import("./weixin-route-
|
|
10467
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
10531
|
+
const { saveWeixinRoutes } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10532
|
+
const { readSkiffRoles } = await import("./skiff-role-BSg6Qu9k.js").then((n) => n.u);
|
|
10468
10533
|
const roles = readSkiffRoles(root);
|
|
10469
10534
|
for (const r of routes) if (!roles.has(r.role)) {
|
|
10470
10535
|
sendJson$4(res, 400, { error: `unknown role: ${r.role} (not in ${root} skiff.roles)` });
|
|
@@ -10476,7 +10541,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10476
10541
|
}
|
|
10477
10542
|
if (body.action === "set-enabled") {
|
|
10478
10543
|
const enabled = body.enabled === true;
|
|
10479
|
-
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-
|
|
10544
|
+
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10480
10545
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
10481
10546
|
const settings = readWeixinSettings(root);
|
|
10482
10547
|
if (enabled && (settings.accounts ?? []).length === 0) {
|
|
@@ -10520,7 +10585,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10520
10585
|
return;
|
|
10521
10586
|
}
|
|
10522
10587
|
const { pollQRStatus } = await import("./weixin-api-BrRcPiQs.js").then((n) => n.u);
|
|
10523
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
10588
|
+
const { readWeixinSettings } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10524
10589
|
const settings = readWeixinSettings(login.root);
|
|
10525
10590
|
const status = await pollQRStatus({
|
|
10526
10591
|
baseUrl: void 0,
|
|
@@ -10535,7 +10600,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
10535
10600
|
});
|
|
10536
10601
|
return;
|
|
10537
10602
|
}
|
|
10538
|
-
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-
|
|
10603
|
+
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-BNyH5ghC.js").then((n) => n.u);
|
|
10539
10604
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
10540
10605
|
const accountId = nextWeixinAccountId(settings);
|
|
10541
10606
|
upsertWeixinAccount(login.root, {
|
|
@@ -16688,7 +16753,7 @@ function manualOutputFallbackNeeded(input) {
|
|
|
16688
16753
|
*/
|
|
16689
16754
|
async function resolveCccRootByAlias(ctx, alias) {
|
|
16690
16755
|
try {
|
|
16691
|
-
const { listCccs } = await import("./ccc-roots-
|
|
16756
|
+
const { listCccs } = await import("./ccc-roots-Bo0HKLlM.js").then((n) => n.a);
|
|
16692
16757
|
const entries = await listCccs(ctx);
|
|
16693
16758
|
const byName = entries.find((e) => e.name === alias);
|
|
16694
16759
|
if (byName) return byName.root;
|
|
@@ -17187,7 +17252,7 @@ function syncHumanChannel(ctx) {
|
|
|
17187
17252
|
(async () => {
|
|
17188
17253
|
try {
|
|
17189
17254
|
stopMachineLoops();
|
|
17190
|
-
const { listCccs } = await import("./ccc-roots-
|
|
17255
|
+
const { listCccs } = await import("./ccc-roots-Bo0HKLlM.js").then((n) => n.a);
|
|
17191
17256
|
for (const entry of await listCccs(ctx)) syncCccBridge(ctx, entry.root);
|
|
17192
17257
|
} catch {}
|
|
17193
17258
|
})();
|
|
@@ -17423,7 +17488,7 @@ function matchCcc(input, candidates) {
|
|
|
17423
17488
|
}
|
|
17424
17489
|
/** 组装候选列表(`ccc-roots.listCccs` 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
|
|
17425
17490
|
async function collectCandidates(ctx) {
|
|
17426
|
-
const { listCccs } = await import("./ccc-roots-
|
|
17491
|
+
const { listCccs } = await import("./ccc-roots-Bo0HKLlM.js").then((n) => n.a);
|
|
17427
17492
|
const entries = await listCccs(ctx, { defaultRoot: process.cwd() });
|
|
17428
17493
|
const seen = /* @__PURE__ */ new Set();
|
|
17429
17494
|
const out = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as cccRootForCwd } from "./ccc-roots-
|
|
1
|
+
import { r as cccRootForCwd } from "./ccc-roots-Bo0HKLlM.js";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
//#region src/time.ts
|
package/lib/seams/guards.d.ts
CHANGED
|
@@ -39,11 +39,19 @@ interface RestrictDiagnostics {
|
|
|
39
39
|
lastSuccess: boolean | null;
|
|
40
40
|
lastError: string | null;
|
|
41
41
|
activeKeys: string[];
|
|
42
|
+
/** v1.51.4:每个 key **实际生效**的 deny 清单(排查"某工具为什么/为什么没被隐藏") */
|
|
43
|
+
denyByKey: Record<string, string[]>;
|
|
42
44
|
}
|
|
43
45
|
export declare function getRestrictDiagnostics(): RestrictDiagnostics;
|
|
44
46
|
/**
|
|
45
|
-
* 同步安全模式工具隐藏:标记存在 → agent.ctx.tools.restrict deny
|
|
46
|
-
*
|
|
47
|
+
* 同步安全模式工具隐藏:标记存在 → `agent.ctx.tools.restrict deny` 隐藏
|
|
48
|
+
* **基础表(bash)∪ CCC 声明的 `safeMode.hiddenTools`**;标记消失 → 全部解除。
|
|
49
|
+
* pre-step 每步调用 ⇒ 开/关/**改声明**三种变化都实时生效(v1.51.4 起含"改声明")。
|
|
50
|
+
*
|
|
51
|
+
* 与 `readSafeModeHiddenTools` 的分工(R↓):本函数负责**形状与失败退化**,reader 只管取值。
|
|
52
|
+
* 🔴 退化规则:DSH 契约规定 **未注册的工具名会让整次 restrict 失败**("unknown names … fail")
|
|
53
|
+
* ⇒ 绝不能让 CCC 写错一个名字就丢掉 safe-mode 的核心隐藏(bash)。失败时退化为**只隐藏基础表**,
|
|
54
|
+
* 并在日志响亮报错;退化态**按期望签名记账**,不会每步重试刷屏。
|
|
47
55
|
*/
|
|
48
56
|
export declare function syncSafeModeRestriction(agent: Agent, root: string): void;
|
|
49
57
|
/** im-bridge 隐藏诊断(排查"工具为什么不在清单里") */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import {
|
|
2
|
+
import { _ as resolveInside, g as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-DXky9UZu.js";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/skiff-role.ts
|
|
5
5
|
/**
|
package/lib/status.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ interface SerenityStatus {
|
|
|
56
56
|
lastSuccess: boolean | null;
|
|
57
57
|
lastError: string | null;
|
|
58
58
|
activeKeys: string[];
|
|
59
|
+
/** v1.51.4:每个 key **实际生效**的 deny 清单(含 safeMode.hiddenTools 的并集) */
|
|
60
|
+
denyByKey: Record<string, string[]>;
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
63
|
export declare function getStatus(cwd: string, configPaths?: string[]): SerenityStatus;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-DXky9UZu.js";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { createHash } from "node:crypto";
|
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.4",
|
|
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": {
|
|
@@ -111,8 +111,11 @@
|
|
|
111
111
|
"@deepseek-ai/dsh-agent-loop": "0.1.7-rc.1",
|
|
112
112
|
"@deepseek-ai/dsh-agent-preset-registry": "0.1.7-rc.1",
|
|
113
113
|
"@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.1",
|
|
114
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
|
|
115
|
+
"@deepseek-ai/dsh-brand": "0.1.7-rc.1",
|
|
114
116
|
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.1",
|
|
115
117
|
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
|
|
118
|
+
"@deepseek-ai/dsh-client-ui-chat": "0.1.7-rc.1",
|
|
116
119
|
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
|
|
117
120
|
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
|
|
118
121
|
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
|