@shgroup/dsh-serenity-hooks 1.56.10 → 1.57.1

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.
Files changed (46) hide show
  1. package/README.md +25 -11
  2. package/dsh.plugin.json +3 -3
  3. package/lib/agent-idle.d.ts +2 -2
  4. package/lib/api.d.ts +0 -1
  5. package/lib/{ccc-DXky9UZu.js → ccc-B0ZGL9lx.js} +30 -13
  6. package/lib/{ccc-roots-CDW2dO0n.js → ccc-roots-GIJ3A_tw.js} +82 -5
  7. package/lib/ccc-roots.d.ts +3 -2
  8. package/lib/ccc.d.ts +23 -10
  9. package/lib/cheap-worker-ops.d.ts +49 -0
  10. package/lib/cheap-worker-preset-inherit.d.ts +35 -0
  11. package/lib/client.js +47 -471
  12. package/lib/config-ops.d.ts +3 -34
  13. package/lib/cro-guide.d.ts +1 -1
  14. package/lib/face-host.d.ts +7 -5
  15. package/lib/host/access.d.ts +2 -2
  16. package/lib/index.d.ts +0 -31
  17. package/lib/index.js +280 -1599
  18. package/lib/invariant.d.ts +1 -1
  19. package/lib/invariant.js +1 -1
  20. package/lib/localstore-ops.d.ts +1 -1
  21. package/lib/{message-source-CQNfQaTx.js → message-source-B4P3Uw6D.js} +1 -1
  22. package/lib/output-guard-seam.d.ts +9 -2
  23. package/lib/ports.d.ts +6 -6
  24. package/lib/seams/context.d.ts +2 -2
  25. package/lib/seams/lifecycle.d.ts +3 -3
  26. package/lib/{settings-section-xHABl8nk.js → settings-section-DQIVsTqE.js} +45 -30
  27. package/lib/settings-section.d.ts +3 -16
  28. package/lib/skiff-core.d.ts +6 -5
  29. package/lib/skiff-debug.d.ts +5 -4
  30. package/lib/{skiff-role-Det66X16.js → skiff-role-B615L9MV.js} +2 -2
  31. package/lib/skiff-role.d.ts +2 -2
  32. package/lib/status.d.ts +2 -2
  33. package/lib/tools/cheap-worker.d.ts +21 -0
  34. package/lib/tools/skiff-admin.d.ts +2 -2
  35. package/lib/unattended-ops.d.ts +6 -4
  36. package/lib/weixin-bridge.d.ts +9 -2
  37. package/lib/weixin-output-guard.d.ts +1 -1
  38. package/lib/{weixin-route-CxCciqQl.js → weixin-route-B9gU1YXz.js} +5 -5
  39. package/package.json +2 -2
  40. package/lib/access-CNccNtgf.js +0 -79
  41. package/lib/acp-core.d.ts +0 -80
  42. package/lib/acp-http.d.ts +0 -37
  43. package/lib/handyman-ops.d.ts +0 -97
  44. package/lib/handyman-preset-inherit.d.ts +0 -35
  45. package/lib/skiff-registry-BjIX_KD1.js +0 -43
  46. package/lib/tools/handyman.d.ts +0 -24
package/lib/client.js CHANGED
@@ -8,7 +8,7 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region \0sp-css:src/client/SafeModePanel
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";
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/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\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");
@@ -24,7 +24,6 @@ window.__ModuleLoader__.load({
24
24
  /** 尚未落位时的屏外坐标(`useLayoutEffect` 在绘制前就会覆写,肉眼看不到) */
25
25
  const POP_OFFSCREEN = -9999;
26
26
  const API = "/serenity/status";
27
- const HANDYMEN_API = "/serenity/handymen";
28
27
  /** 盾牌 SVG(v1.24.5 校准:fill=none 纯描边;ON=绿 emerald / OFF=灰 label-secondary 由状态类控制) */
29
28
  const SHIELD_SVG = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
30
29
  viewBox: "0 0 16 16",
@@ -54,8 +53,6 @@ window.__ModuleLoader__.load({
54
53
  const [status, setStatus] = (0, react.useState)(null);
55
54
  const [busy, setBusy] = (0, react.useState)(false);
56
55
  const [open, setOpen] = (0, react.useState)(false);
57
- const [handymen, setHandymen] = (0, react.useState)([]);
58
- const [handymenOpen, setHandymenOpen] = (0, react.useState)(false);
59
56
  const rootRef = (0, react.useRef)(null);
60
57
  const popRef = (0, react.useRef)(null);
61
58
  const [popPos, setPopPos] = (0, react.useState)(null);
@@ -72,25 +69,6 @@ window.__ModuleLoader__.load({
72
69
  (0, react.useEffect)(() => {
73
70
  refresh();
74
71
  }, [refresh]);
75
- (0, react.useEffect)(() => {
76
- if (!open) return;
77
- let alive = true;
78
- const qs = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
79
- const tick = async () => {
80
- try {
81
- const data = await (await fetch(`${HANDYMEN_API}${qs}`, { headers: { accept: "application/json" } })).json();
82
- if (alive) setHandymen(data.handymen ?? []);
83
- } catch {
84
- if (alive) setHandymen([]);
85
- }
86
- };
87
- tick();
88
- const timer = setInterval(() => void tick(), 3e3);
89
- return () => {
90
- alive = false;
91
- clearInterval(timer);
92
- };
93
- }, [open, sessionId]);
94
72
  (0, react.useEffect)(() => {
95
73
  if (!open) return;
96
74
  const onDocMouseDown = (e) => {
@@ -164,7 +142,6 @@ window.__ModuleLoader__.load({
164
142
  })]
165
143
  });
166
144
  const inCcc = status.root !== null;
167
- const runningHandymen = handymen.filter((l) => !l.done);
168
145
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
169
146
  className: cx("sp-root"),
170
147
  ref: rootRef,
@@ -251,11 +228,11 @@ window.__ModuleLoader__.load({
251
228
  className: cx("sp-row"),
252
229
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
253
230
  className: cx("sp-label"),
254
- children: "Handyman 模型"
231
+ children: "CheapWorker 模型"
255
232
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
256
233
  className: cx("sp-value"),
257
- title: status.handymanModel ? `handyman.models: ${status.handymanModel}` : void 0,
258
- children: status.handymanModel ?? "未配置"
234
+ title: status.cheapWorkerModel ? `cheap-worker.models: ${status.cheapWorkerModel}` : void 0,
235
+ children: status.cheapWorkerModel ?? "未配置"
259
236
  })]
260
237
  }),
261
238
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -302,71 +279,6 @@ window.__ModuleLoader__.load({
302
279
  })]
303
280
  })]
304
281
  }),
305
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
306
- className: cx("sp-group"),
307
- children: [
308
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
309
- className: cx("sp-groupHead"),
310
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
311
- className: cx("sp-groupTitle"),
312
- children: "Handyman 运行"
313
- }), handymen.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
314
- type: "button",
315
- className: cx("sp-handymenToggle"),
316
- onClick: () => setHandymenOpen((v) => !v),
317
- children: handymenOpen ? "收起" : "详情"
318
- })]
319
- }),
320
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
321
- className: cx("sp-rows"),
322
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
323
- className: cx("sp-row"),
324
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
325
- className: cx("sp-label"),
326
- children: "状态"
327
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
328
- className: cx("sp-value"),
329
- children: runningHandymen.length > 0 ? `运行中 ${runningHandymen.length}${handymen.length > 0 ? ` / 共 ${handymen.length}` : ""}` : "无运行中 handyman"
330
- })]
331
- })
332
- }),
333
- handymenOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
334
- className: cx("sp-handymenList"),
335
- children: [handymen.slice(0, 3).map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
336
- className: cx("sp-handymanItem"),
337
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
338
- className: cx("sp-handymanHead"),
339
- children: [
340
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
341
- className: cx("sp-handymanLabel"),
342
- title: l.label,
343
- children: l.label
344
- }),
345
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
346
- className: cx("sp-handymanRound"),
347
- children: l.done ? "✓ 完成" : `R${l.round}`
348
- }),
349
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
350
- className: cx("sp-handymanTime"),
351
- children: new Date(l.updated).toLocaleTimeString()
352
- })
353
- ]
354
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
355
- className: cx("sp-handymanResp"),
356
- title: l.lastResponse,
357
- children: l.lastResponse.slice(0, 80) || (l.done ? "已完成" : "(等待首轮响应)")
358
- })]
359
- }, l.label)), handymen.length > 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
360
- className: cx("sp-handymenMore"),
361
- children: [
362
- "… 共 ",
363
- handymen.length,
364
- " 个 handyman(其余见等待界面)"
365
- ]
366
- })]
367
- })
368
- ]
369
- }),
370
282
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
371
283
  className: cx("sp-hint"),
372
284
  children: [
@@ -3295,7 +3207,7 @@ window.__ModuleLoader__.load({
3295
3207
  knownWorkspaces: body.knownWorkspaces ?? []
3296
3208
  };
3297
3209
  }
3298
- /** PUT 配置(账号 patch + 工作区白名单 + persona 彩蛋 + 开放容器白名单)→ 返回保存后的 wire */
3210
+ /** PUT 配置(账号 patch + 工作区白名单 + persona 彩蛋)→ 返回保存后的 wire */
3299
3211
  async function saveConfig(patch) {
3300
3212
  const res = await fetch(CONFIG_PATH, {
3301
3213
  method: "PUT",
@@ -3887,286 +3799,6 @@ Instruction-following style:
3887
3799
  });
3888
3800
  }
3889
3801
  //#endregion
3890
- //#region src/client/PublicAskEditor.tsx
3891
- /**
3892
- * PublicAskEditor.tsx — 建议问答页配置(v1.26.2,S142 用户需求)
3893
- *
3894
- * 功能:
3895
- * - **开放容器白名单**:从 /serenity/cccs 拉取候选容器,chips 选择哪些开放;
3896
- * 空白名单 = 全部容器开放(向后兼容 v1.26.1 全局开放语义)。
3897
- * - **key + 地址展示**:经 /serenity/public-ask 获取访问 key 与各开放容器 URL
3898
- * (用户:配置处需能获取 key 和地址——管理员复制分享给使用者)。
3899
- *
3900
- * 数据通道(plugin 全局):
3901
- * - GET /serenity/cccs 候选容器(node half listCccs)
3902
- * - GET /serenity/public-ask key + 地址(x-serenity-ui 头)
3903
- * - PUT /serenity/config publicAsk.allowed 白名单保存(accounts-api saveConfig)
3904
- */
3905
- function PublicAskEditor(props) {
3906
- const { publicAskOn } = props;
3907
- const [candidates, setCandidates] = (0, react.useState)([]);
3908
- const [allowed, setAllowed] = (0, react.useState)(null);
3909
- const [info, setInfo] = (0, react.useState)(null);
3910
- const [busy, setBusy] = (0, react.useState)(false);
3911
- const [error, setError] = (0, react.useState)(null);
3912
- const [saved, setSaved] = (0, react.useState)(false);
3913
- const [rotating, setRotating] = (0, react.useState)(false);
3914
- const rotateKey = async () => {
3915
- if (!window.confirm("重新生成访问 Key?旧 Key 将立即失效,需要重新分享给使用者。")) return;
3916
- setRotating(true);
3917
- setError(null);
3918
- try {
3919
- const res = await fetch("/serenity/public-ask", {
3920
- method: "PUT",
3921
- headers: {
3922
- "content-type": "application/json",
3923
- "x-serenity-ui": "1"
3924
- },
3925
- body: JSON.stringify({ action: "rotate" })
3926
- });
3927
- if (!res.ok) throw new Error(`轮换失败(${res.status})`);
3928
- const body = await res.json();
3929
- setInfo((cur) => cur ? {
3930
- ...cur,
3931
- key: body.key
3932
- } : cur);
3933
- setSaved(true);
3934
- setTimeout(() => setSaved(false), 2500);
3935
- } catch (err) {
3936
- setError(err instanceof Error ? err.message : String(err));
3937
- } finally {
3938
- setRotating(false);
3939
- }
3940
- };
3941
- (0, react.useEffect)(() => {
3942
- let alive = true;
3943
- (async () => {
3944
- try {
3945
- const cfg = await fetchConfig();
3946
- if (!alive) return;
3947
- setAllowed(cfg?.config?.publicAsk?.allowed ?? []);
3948
- } catch {}
3949
- })();
3950
- return () => {
3951
- alive = false;
3952
- };
3953
- }, []);
3954
- (0, react.useEffect)(() => {
3955
- let alive = true;
3956
- (async () => {
3957
- try {
3958
- const res = await fetch("/serenity/cccs", { headers: { accept: "application/json" } });
3959
- if (!res.ok) return;
3960
- const body = await res.json();
3961
- if (alive) setCandidates(body?.cccs ?? []);
3962
- } catch {}
3963
- })();
3964
- return () => {
3965
- alive = false;
3966
- };
3967
- }, []);
3968
- (0, react.useEffect)(() => {
3969
- if (!publicAskOn) return;
3970
- let alive = true;
3971
- (async () => {
3972
- try {
3973
- const res = await fetch("/serenity/public-ask", { headers: {
3974
- accept: "application/json",
3975
- "x-serenity-ui": "1"
3976
- } });
3977
- if (!res.ok) return;
3978
- const body = await res.json();
3979
- if (alive) setInfo(body);
3980
- } catch {}
3981
- })();
3982
- return () => {
3983
- alive = false;
3984
- };
3985
- }, [publicAskOn]);
3986
- const toggleAllowed = (name) => {
3987
- setAllowed((cur) => {
3988
- const base = cur ?? [];
3989
- return base.includes(name) ? base.filter((n) => n !== name) : [...base, name];
3990
- });
3991
- };
3992
- const save = async () => {
3993
- if (allowed === null) return;
3994
- setBusy(true);
3995
- setError(null);
3996
- setSaved(false);
3997
- try {
3998
- const cfg = await saveConfig({ publicAsk: { allowed } });
3999
- if (cfg) {
4000
- setAllowed(cfg.publicAsk.allowed ?? []);
4001
- setSaved(true);
4002
- setTimeout(() => setSaved(false), 2e3);
4003
- }
4004
- } catch (err) {
4005
- setError(err instanceof Error ? err.message : String(err));
4006
- } finally {
4007
- setBusy(false);
4008
- }
4009
- };
4010
- const allOpen = (allowed ?? []).length === 0;
4011
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4012
- className: "ae-root",
4013
- children: [
4014
- !publicAskOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4015
- className: "ae-warn",
4016
- children: "⚠️ Skiff 问答页当前关闭——先开启上方「Skiff 问答页」开关,外部访问才生效。"
4017
- }),
4018
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4019
- className: "ae-block",
4020
- children: [
4021
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
4022
- className: "ae-title",
4023
- children: "开放容器"
4024
- }),
4025
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4026
- className: "ae-desc",
4027
- children: [
4028
- "允许外部问答的认知容器(URL:/c/<容器名>)。",
4029
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "未选择 = 全部容器开放" }),
4030
- "; 选择后仅白名单内容器可访问。"
4031
- ]
4032
- }),
4033
- candidates.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4034
- className: "ae-note",
4035
- children: "暂无可选容器(/serenity/cccs 未返回条目——先在工作区打开一个 CCC)"
4036
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4037
- className: "ae-chips",
4038
- children: candidates.map((c) => {
4039
- const on = (allowed ?? []).includes(c.name);
4040
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4041
- type: "button",
4042
- className: `ae-chip${on ? " ae-chipOn" : ""}`,
4043
- onClick: () => toggleAllowed(c.name),
4044
- title: `${c.root}(${c.roles.length} 角色)`,
4045
- children: [c.name, on ? " ✓" : ""]
4046
- }, c.name);
4047
- })
4048
- }),
4049
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4050
- className: "ae-actions",
4051
- children: [
4052
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4053
- type: "button",
4054
- className: "ae-save",
4055
- disabled: busy || allowed === null,
4056
- onClick: () => void save(),
4057
- children: busy ? "保存中…" : "保存白名单"
4058
- }),
4059
- saved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4060
- className: "ae-saved",
4061
- children: "✓ 已保存"
4062
- }),
4063
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4064
- className: "ae-err",
4065
- children: error
4066
- })
4067
- ]
4068
- }),
4069
- allOpen && allowed !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4070
- className: "ae-note",
4071
- children: "当前为「全部开放」——所有发现到的认知容器都可问答。"
4072
- })
4073
- ]
4074
- }),
4075
- publicAskOn && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4076
- className: "ae-block",
4077
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
4078
- className: "ae-title",
4079
- children: "访问 Key 与地址"
4080
- }), info ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4081
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4082
- className: "ae-desc",
4083
- children: "把下面的 key 与地址分享给使用者;key 已自动保存在其浏览器(localStorage)。"
4084
- }),
4085
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4086
- className: "ae-row",
4087
- children: [
4088
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4089
- className: "ae-label",
4090
- children: "访问 Key"
4091
- }),
4092
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4093
- className: "ae-key",
4094
- children: info.key
4095
- }),
4096
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4097
- type: "button",
4098
- className: "ae-copy",
4099
- onClick: () => {
4100
- navigator.clipboard?.writeText(info.key);
4101
- },
4102
- children: "复制"
4103
- }),
4104
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4105
- type: "button",
4106
- className: "ae-copy ae-rotate",
4107
- disabled: rotating,
4108
- onClick: () => void rotateKey(),
4109
- children: rotating ? "生成中…" : "重新生成"
4110
- })
4111
- ]
4112
- }),
4113
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4114
- className: "ae-row",
4115
- children: [
4116
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4117
- className: "ae-label",
4118
- children: "容器列表页"
4119
- }),
4120
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4121
- className: "ae-key",
4122
- children: info.listUrl
4123
- }),
4124
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4125
- type: "button",
4126
- className: "ae-copy",
4127
- onClick: () => {
4128
- navigator.clipboard?.writeText(info.listUrl);
4129
- },
4130
- children: "复制"
4131
- })
4132
- ]
4133
- }),
4134
- info.urls.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4135
- className: "ae-rows",
4136
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4137
- className: "ae-label",
4138
- children: "各容器地址"
4139
- }), info.urls.map((u) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4140
- className: "ae-row",
4141
- children: [
4142
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4143
- className: "ae-label ae-labelDim",
4144
- children: u.name
4145
- }),
4146
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4147
- className: "ae-key",
4148
- children: u.url
4149
- }),
4150
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4151
- type: "button",
4152
- className: "ae-copy",
4153
- onClick: () => {
4154
- navigator.clipboard?.writeText(u.url);
4155
- },
4156
- children: "复制"
4157
- })
4158
- ]
4159
- }, u.name))]
4160
- })
4161
- ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4162
- className: "ae-note",
4163
- children: "开启后此处显示访问 key 与地址…"
4164
- })]
4165
- })
4166
- ]
4167
- });
4168
- }
4169
- //#endregion
4170
3802
  //#region src/client/WeixinBridgeEditor.tsx
4171
3803
  /**
4172
3804
  * WeixinBridgeEditor.tsx — 「微信桥」配置区块(F4c-3,v1.27.0 实验性)
@@ -4800,24 +4432,16 @@ Instruction-following style:
4800
4432
  const setSkiffPort = (v) => {
4801
4433
  set("skiffDebugPort", Math.min(65535, Math.max(1024, Math.round(v))));
4802
4434
  };
4803
- const setAcpPort = (v) => {
4804
- set("acpHttpPort", Math.min(65535, Math.max(1024, Math.round(v))));
4805
- };
4806
4435
  const gatewayOn = value?.gatewayEnabled ?? false;
4807
4436
  const thresholdK = value?.rebuildThresholdK ?? 400;
4808
4437
  const skiffOn = value?.skiffEnabled ?? false;
4809
4438
  const skiffPort = value?.skiffDebugPort ?? 3099;
4810
- const acpOn = value?.acpEnabled ?? false;
4811
- const acpPort = value?.acpHttpPort ?? 3100;
4812
- const publicAskOn = value?.publicAskEnabled ?? false;
4813
4439
  const croOn = value?.croEnabled ?? true;
4814
4440
  const unattendedOn = value?.unattendedEnabled ?? false;
4815
4441
  const diagramOn = value?.diagramEnabled ?? false;
4816
4442
  const [openGateway, setOpenGateway] = (0, react.useState)(false);
4817
4443
  const [openRebuild, setOpenRebuild] = (0, react.useState)(false);
4818
4444
  const [openSkiff, setOpenSkiff] = (0, react.useState)(false);
4819
- const [openAcp, setOpenAcp] = (0, react.useState)(false);
4820
- const [openPublicAsk, setOpenPublicAsk] = (0, react.useState)(false);
4821
4445
  const [openCro, setOpenCro] = (0, react.useState)(false);
4822
4446
  const [openUnattended, setOpenUnattended] = (0, react.useState)(false);
4823
4447
  const [openDiagram, setOpenDiagram] = (0, react.useState)(false);
@@ -4909,98 +4533,50 @@ Instruction-following style:
4909
4533
  }),
4910
4534
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Group, {
4911
4535
  title: "外部能力",
4912
- children: [
4913
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4914
- title: "Skiff 调试",
4915
- desc: "Skiff 认知子集问答页(端口 3099,实验性,127.0.0.1)",
4916
- expandable: true,
4917
- open: openSkiff,
4918
- onToggle: setOpenSkiff,
4919
- control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4920
- className: "ss-switchRow",
4921
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4922
- checked: skiffOn,
4923
- onChange: (on) => toggle("skiffEnabled", on)
4924
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4925
- className: "ss-portInput",
4926
- type: "number",
4927
- min: 1024,
4928
- max: 65535,
4929
- value: skiffPort,
4930
- disabled: !skiffOn,
4931
- title: "Skiff 调试端口(默认 3099)",
4932
- onChange: (e) => setSkiffPort(Number(e.target.value))
4933
- })]
4934
- }),
4935
- detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4936
- className: "ss-detailIntro",
4937
- children: "Skiff 认知子集角色调试服务(端口 3099):\n· 概念:Skiff = 完整宁静号 trajectory 的任意子集(认知子集角色)\n· 服务:角色问答页(调试用),仅监听 127.0.0.1(本地)\n· 使用:配置 CCC 的 skiff.roles 角色后,可经此页以角色身份问答\n· 实验性:人工启停,默认关闭;对外问答请用「Skiff 问答页」"
4938
- })
4939
- }) }),
4940
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4941
- title: "ACP JSON-RPC",
4942
- desc: "程序化调用 Skiff 角色(端口 3100,实验性,127.0.0.1)",
4943
- expandable: true,
4944
- open: openAcp,
4945
- onToggle: setOpenAcp,
4946
- control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4947
- className: "ss-switchRow",
4948
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4949
- checked: acpOn,
4950
- onChange: (on) => toggle("acpEnabled", on)
4951
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4952
- className: "ss-portInput",
4953
- type: "number",
4954
- min: 1024,
4955
- max: 65535,
4956
- value: acpPort,
4957
- disabled: !acpOn && !publicAskOn,
4958
- title: "ACP + Skiff 问答共用端口(默认 3100)",
4959
- onChange: (e) => setAcpPort(Number(e.target.value))
4960
- })]
4961
- }),
4962
- detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4536
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4537
+ title: "Skiff 调试",
4538
+ desc: "Skiff 认知子集问答页(端口 3099,实验性,127.0.0.1)",
4539
+ expandable: true,
4540
+ open: openSkiff,
4541
+ onToggle: setOpenSkiff,
4542
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4543
+ className: "ss-switchRow",
4544
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4545
+ checked: skiffOn,
4546
+ onChange: (on) => toggle("skiffEnabled", on)
4547
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4548
+ className: "ss-portInput",
4549
+ type: "number",
4550
+ min: 1024,
4551
+ max: 65535,
4552
+ value: skiffPort,
4553
+ disabled: !skiffOn,
4554
+ title: "Skiff 调试端口(默认 3099)",
4555
+ onChange: (e) => setSkiffPort(Number(e.target.value))
4556
+ })]
4557
+ }),
4558
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4559
+ className: "ss-detailIntro",
4560
+ children: "Skiff 认知子集角色调试服务(端口 3099):\n· 概念:Skiff = 完整宁静号 trajectory 的任意子集(认知子集角色)\n· 服务:角色问答页(调试用),仅监听 127.0.0.1(本地)\n· 使用:配置 CCC 的 skiff.roles 角色后,可经此页以角色身份问答\n· 实验性:人工启停,默认关闭;对外问答请用「Skiff 问答页」"
4561
+ })
4562
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4563
+ title: "CRO(轨迹自编程唤起)",
4564
+ desc: "让轨迹用自己的一段程序决定何时唤起(全局开关,默认开)",
4565
+ expandable: true,
4566
+ open: openCro,
4567
+ onToggle: setOpenCro,
4568
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4569
+ checked: croOn,
4570
+ onChange: (on) => toggle("croEnabled", on)
4571
+ }),
4572
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4573
+ className: "ss-detailStack",
4574
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4963
4575
  className: "ss-detailIntro",
4964
- children: "ACP(Agent Client Protocol)HTTP JSON-RPC 端点(端口 3100):\n· 作用:程序化调用 Skiff 角色(指定 CCC + 角色 + 会话)\n· 形态:HTTP JSON-RPC 服务,仅监听 127.0.0.1(本地)\n· 场景:IM 桥(如微信桥)、脚本、自动化接线\n· 实验性:人工启停,默认关闭;与「Skiff 问答页」共用端口"
4576
+ children: "CRO = Continuous Re-Occurrence(轨迹自编程唤起)。\n· 作用:轨迹目录下放一个 continuous-re-occurrence.ts,ACC 的 5min tick 会 spawn 它,\n 由这段程序自己决定“要不要唤、何时唤、唤起时说什么”\n· 怎么启用:程序文件在即启用(无需在此配置);本开关是**总闸**\n· 关掉它的后果:**只有 CRO 阶段停跑** —— 定时唤醒(send-later)、即时投递(send-now)\n 与唤醒表投递**照常工作**\n· 默认开:已在生产稳定运行,且“没有程序文件的轨迹”本来就不参与(默认存在感为零)\n· 与它同时废止的开关:原“唤醒调度器”闸(2026-09-21 所有者令砍掉)——\n 唤醒调度器现**恒开**,不再有可被误关的开关"
4965
4577
  })
4966
- }) }),
4967
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4968
- title: "Skiff 问答页",
4969
- desc: "按认知容器暴露问答页(端口 3100,需 key)",
4970
- expandable: true,
4971
- open: openPublicAsk,
4972
- onToggle: setOpenPublicAsk,
4973
- control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4974
- checked: publicAskOn,
4975
- onChange: (on) => toggle("publicAskEnabled", on)
4976
- }),
4977
- detail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4978
- className: "ss-detailStack",
4979
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4980
- className: "ss-detailIntro",
4981
- children: "Skiff 问答页(对外问答,端口 3100,key 认证):\n· 作用:让外部用户在浏览器中与指定 CCC 的 Skiff 角色对话\n· 认证:访问 key(首次开启自动生成)\n· 授权:开放容器白名单(未选 = 全部开放)\n· 外部接入:需经部署方自选方案暴露(隧道/反代/端口映射)\n· 安全:key 可轮换(旧 key 立即失效)"
4982
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PublicAskEditor, { publicAskOn })]
4983
- })
4984
- }) }),
4985
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4986
- title: "CRO(轨迹自编程唤起)",
4987
- desc: "让轨迹用自己的一段程序决定何时唤起(全局开关,默认开)",
4988
- expandable: true,
4989
- open: openCro,
4990
- onToggle: setOpenCro,
4991
- control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4992
- checked: croOn,
4993
- onChange: (on) => toggle("croEnabled", on)
4994
- }),
4995
- detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4996
- className: "ss-detailStack",
4997
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4998
- className: "ss-detailIntro",
4999
- children: "CRO = Continuous Re-Occurrence(轨迹自编程唤起)。\n· 作用:轨迹目录下放一个 continuous-re-occurrence.ts,ACC 的 5min tick 会 spawn 它,\n 由这段程序自己决定“要不要唤、何时唤、唤起时说什么”\n· 怎么启用:程序文件在即启用(无需在此配置);本开关是**总闸**\n· 关掉它的后果:**只有 CRO 阶段停跑** —— 定时唤醒(send-later)、即时投递(send-now)\n 与唤醒表投递**照常工作**\n· 默认开:已在生产稳定运行,且“没有程序文件的轨迹”本来就不参与(默认存在感为零)\n· 与它同时废止的开关:原“唤醒调度器”闸(2026-09-21 所有者令砍掉)——\n 唤醒调度器现**恒开**,不再有可被误关的开关"
5000
- })
5001
- })
5002
- }) })
5003
- ]
4578
+ })
4579
+ }) })]
5004
4580
  }),
5005
4581
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Group, {
5006
4582
  title: "工具",