@shgroup/dsh-serenity-hooks 1.59.1 → 1.59.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/lib/acp-auth.d.ts +42 -1
- package/lib/acp-face.d.ts +41 -1
- package/lib/client.js +9 -3
- package/lib/index.js +183 -18
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.59.
|
|
3
|
+
"version": "1.59.2",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/acp-auth.d.ts
CHANGED
|
@@ -69,6 +69,34 @@ export declare function resetAcpToken(root: string, accountId: string): string;
|
|
|
69
69
|
export declare function safeEqual(a: string, b: string): boolean;
|
|
70
70
|
/** token 指纹(诊断面用;不可逆) */
|
|
71
71
|
export declare function tokenFingerprint(token: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* 该账户的**主会话**键(`IM_ACP_SESSION_<ACCOUNT_ID>`)。
|
|
74
|
+
*
|
|
75
|
+
* 🔴 为什么放在 **credential** 节而不是 config 节:它跟 token 一样是**每账户一值的键控数据**
|
|
76
|
+
* (键由 `accountId` 派生、随账户生命周期存亡)。放 config 节要另造一层 `section.key`
|
|
77
|
+
* 结构,而这一层的唯一读者就是本模块 ⇒ 多一层结构 = 多一处会漂的形状。
|
|
78
|
+
*
|
|
79
|
+
* ⚠️ 它**不是凭据**(会话 id 不是秘密)—— 共享 credential 节只是为了复用它既有的
|
|
80
|
+
* 「按 accountId 派生键 + 删账户时一并清理」这套语义,不引入第二套键规则。
|
|
81
|
+
*/
|
|
82
|
+
export declare function acpSessionKeyFor(accountId: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* 读该账户的主会话 id(不存在 ⇒ `null`)。
|
|
85
|
+
*
|
|
86
|
+
* 🔴 **读路径无副作用**(与 `readAcpToken` 同纪律):不存在的含义是「这个账户还没有主会话」,
|
|
87
|
+
* 而那时的正确行为是**让 App 去建**(`session/list` 回空数组),不是替它编一个。
|
|
88
|
+
*/
|
|
89
|
+
export declare function readAcpMainSession(root: string, accountId: string): string | null;
|
|
90
|
+
/**
|
|
91
|
+
* 记下该账户的主会话 id(**写操作**;由 `session/new` 的出站响应触发)。
|
|
92
|
+
*
|
|
93
|
+
* 🔴 **后写覆盖先写**(last-wins):owner 要的是"一条长期对话",而"再建一条"是用户
|
|
94
|
+
* 在 App 里显式点「新对话」的结果 ⇒ 那时他要的就是**新的那条**。
|
|
95
|
+
* ⛔ **被否的两种做法**:① 已有主会话就拒建(用户会看到"点了没反应");
|
|
96
|
+
* ② 把 `session/new` 改写成 `session/resume`(响应形状不同,要让客户端认一条
|
|
97
|
+
* **不存在于协议里**的响应 —— 那是拿协议兼容性换一个我们本来就有的语义)。
|
|
98
|
+
*/
|
|
99
|
+
export declare function writeAcpMainSession(root: string, accountId: string, sessionId: string): void;
|
|
72
100
|
/**
|
|
73
101
|
* 从一次 HTTP upgrade 请求里取出候选 token。
|
|
74
102
|
*
|
|
@@ -174,6 +202,13 @@ export interface AcpAccountStatus {
|
|
|
174
202
|
enabled: boolean;
|
|
175
203
|
hasToken: boolean;
|
|
176
204
|
tokenFingerprint: string | null;
|
|
205
|
+
/**
|
|
206
|
+
* 该账户的**主会话** id(owner「每账户固定一条长期对话」的那一条;`null` = 还没建)。
|
|
207
|
+
*
|
|
208
|
+
* 🔴 会话 id **不是秘密**,故这里给明文(与 token 不同)—— 它的用途是**可诊断**:
|
|
209
|
+
* 面板/排障时要能回答"这台手机此刻钉的是哪条对话",否则出问题时只能靠猜。
|
|
210
|
+
*/
|
|
211
|
+
mainSession: string | null;
|
|
177
212
|
/** 该账户此刻**能不能被认证闸接受**(三条判据全真) */
|
|
178
213
|
usable: boolean;
|
|
179
214
|
/** 不能用的原因(能用 ⇒ null)—— 直接指向该改哪个字段 */
|
|
@@ -228,5 +263,11 @@ export declare function normalizeAcpAccount(input: unknown): (ImAcpAccount & {
|
|
|
228
263
|
}) | null;
|
|
229
264
|
/** 增/改一条账户(按 accountId 定位;写入后返回全表) */
|
|
230
265
|
export declare function upsertAcpAccount(root: string, account: ImAcpAccount, paths?: string[]): ImAcpAccount[];
|
|
231
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* 删一条账户(**同时清掉它的 token + 主会话**,避免留死键)。
|
|
268
|
+
*
|
|
269
|
+
* 🔴 主会话键**必须一起清**:否则日后用同一个 `accountId` 重建账户时,它会**静默继承**
|
|
270
|
+
* 那条旧会话 —— 而那条会话的 cwd/角色都是上一代账户的。这个失效**没有任何信号**
|
|
271
|
+
* (列表上它看着就是一条正常会话)。
|
|
272
|
+
*/
|
|
232
273
|
export declare function removeAcpAccount(root: string, accountId: string, paths?: string[]): ImAcpAccount[];
|
package/lib/acp-face.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export declare function acpFacePort(): number | null;
|
|
|
80
80
|
*/
|
|
81
81
|
export declare function acpFaceEnabled(): boolean;
|
|
82
82
|
/**
|
|
83
|
-
* 把客户端给的 `cwd` 归一化成宿主要求的**绝对路径**(v1.59.
|
|
83
|
+
* 把客户端给的 `cwd` 归一化成宿主要求的**绝对路径**(v1.59.1)。
|
|
84
84
|
*
|
|
85
85
|
* ## 为什么需要
|
|
86
86
|
* 宿主 `dsh-acp` 在每次 `session/new` 都有这一行(`lib/index.js` 逐字):
|
|
@@ -107,6 +107,45 @@ export declare function acpFaceEnabled(): boolean;
|
|
|
107
107
|
* @param cccRoot 本连接归属的 CCC 根
|
|
108
108
|
*/
|
|
109
109
|
export declare function normalizeClientCwd(raw: unknown, cccRoot: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* 一条连接的**会话钉住**状态(owner「每账户固定一条」的执行单元)。
|
|
112
|
+
*
|
|
113
|
+
* 生命周期 = 一条 WS 连接。⚠️ **跨连接的真相在 localstore**(`IM_ACP_SESSION_<ACCOUNT_ID>`);
|
|
114
|
+
* 本结构只是"这条连接上,请求 id 对应哪个方法"的记账 + 那两个方法的处理口径。
|
|
115
|
+
*/
|
|
116
|
+
interface AcpSessionPin {
|
|
117
|
+
/** 本连接归属的 CCC 根(合成 `SessionInfo.cwd` 时用 —— 那也是我们当初钉的值) */
|
|
118
|
+
root: string;
|
|
119
|
+
/** 本连接归属的账户(主会话键由它派生) */
|
|
120
|
+
accountId: string;
|
|
121
|
+
/** 在飞的请求:`id` → 方法名(**出站改写唯一的方法来源**) */
|
|
122
|
+
pending: Map<string, string>;
|
|
123
|
+
}
|
|
124
|
+
/** 建一条连接的钉住状态 */
|
|
125
|
+
export declare function createAcpSessionPin(root: string, accountId: string): AcpSessionPin;
|
|
126
|
+
/**
|
|
127
|
+
* 入站记账:把"这条请求的方法"记住,供出站按 `id` 反查。
|
|
128
|
+
*
|
|
129
|
+
* 判据:**既要有 `id`,又要有 `method`** —— 只满足一个的是响应(`id` 无 `method`)
|
|
130
|
+
* 或通知(`method` 无 `id`)⇒ 与我们无关,不记。
|
|
131
|
+
*/
|
|
132
|
+
export declare function recordAcpPending(pin: AcpSessionPin, msg: unknown): void;
|
|
133
|
+
/**
|
|
134
|
+
* 出站改写(本功能的落点)。返回**要发给客户端的那条消息**。
|
|
135
|
+
*
|
|
136
|
+
* | 原响应 | 改写后 |
|
|
137
|
+
* |---|---|
|
|
138
|
+
* | `session/list` | `{sessions:[该账户那一条]}`;**没有主会话 ⇒ `{sessions:[]}`** |
|
|
139
|
+
* | `session/new` | 原样,**同时**把 `result.sessionId` 记为该账户的主会话 |
|
|
140
|
+
* | 其余(含所有 error 响应、所有通知) | 原样 |
|
|
141
|
+
*
|
|
142
|
+
* 🔴 **没有主会话时回空数组,绝不回退成全量列表** —— 退回去正是 owner 说
|
|
143
|
+
* 「23 个对我没什么用」的那个东西。空数组会让 App 引导用户新建,而新建即建立主会话。
|
|
144
|
+
*
|
|
145
|
+
* 🔴 `nextCursor` **一律丢掉**:收窄到一条之后,分页游标失去意义;
|
|
146
|
+
* 留着它会让客户端继续翻页(而后续页仍会被收窄成同一条 ⇒ 前端可能出现重复项)。
|
|
147
|
+
*/
|
|
148
|
+
export declare function rewriteAcpResponse(pin: AcpSessionPin, msg: unknown): unknown;
|
|
110
149
|
/** 面的运行统计(`dashboard` / 诊断面用;**只读快照**) */
|
|
111
150
|
export interface AcpFaceStatus {
|
|
112
151
|
active: boolean;
|
|
@@ -202,3 +241,4 @@ export declare function resolveAcpSelection(ctx: Context): {
|
|
|
202
241
|
* 绝不抛出(抛出会让整个插件加载失败,而它只是一个可选面)。
|
|
203
242
|
*/
|
|
204
243
|
export declare function registerAcpFace(ctx: Context): void;
|
|
244
|
+
export {};
|
package/lib/client.js
CHANGED
|
@@ -3643,7 +3643,7 @@ window.__ModuleLoader__.load({
|
|
|
3643
3643
|
//#endregion
|
|
3644
3644
|
//#region src/client/AcpAccountsEditor.tsx
|
|
3645
3645
|
/**
|
|
3646
|
-
* AcpAccountsEditor.tsx — 「ACP 账户台账」配置区块(v1.59.
|
|
3646
|
+
* AcpAccountsEditor.tsx — 「ACP 账户台账」配置区块(v1.59.1,owner 2026-10-17 令)
|
|
3647
3647
|
*
|
|
3648
3648
|
* ## 为什么要有它
|
|
3649
3649
|
* owner 原话:「**面板里只放个开关不放具体的条目吗?**」
|
|
@@ -3824,7 +3824,8 @@ window.__ModuleLoader__.load({
|
|
|
3824
3824
|
}),
|
|
3825
3825
|
status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3826
3826
|
className: "ss-detailIntro",
|
|
3827
|
-
children: `通道:${status.channel}${status.channel === "acp" ? "" : "(未选 ACP ⇒ 账户不生效)"} / 面开关:${status.enabled ? "开" : "关"}\n可用账户:${status.accounts.filter((a) => a.usable).length} / ${status.accounts.length}
|
|
3827
|
+
children: `通道:${status.channel}${status.channel === "acp" ? "" : "(未选 ACP ⇒ 账户不生效)"} / 面开关:${status.enabled ? "开" : "关"}\n可用账户:${status.accounts.filter((a) => a.usable).length} / ${status.accounts.length}
|
|
3828
|
+
每个可用账户在客户端里**只看到自己那一条对话**(第一次发消息时建立,之后一直用它)。`
|
|
3828
3829
|
}),
|
|
3829
3830
|
status && status.accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
3830
3831
|
className: "ss-table",
|
|
@@ -3832,12 +3833,17 @@ window.__ModuleLoader__.load({
|
|
|
3832
3833
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "账户" }),
|
|
3833
3834
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "角色" }),
|
|
3834
3835
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "状态" }),
|
|
3836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "对话" }),
|
|
3835
3837
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "token" }),
|
|
3836
3838
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {})
|
|
3837
3839
|
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: status.accounts.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
3838
3840
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: a.accountId }), a.name ? ` · ${a.name}` : ""] }),
|
|
3839
3841
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.role ?? "—" }),
|
|
3840
3842
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.usable ? "✅ 可用" : `⚠️ ${a.blockedBy ?? "不可用"}` }),
|
|
3843
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
3844
|
+
title: a.mainSession ?? void 0,
|
|
3845
|
+
children: a.mainSession ? `…${a.mainSession.slice(-8)}` : "(还没建,客户端发第一条消息时建立)"
|
|
3846
|
+
}),
|
|
3841
3847
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.hasToken ? `…${a.tokenFingerprint ?? ""}` : "(无)" }),
|
|
3842
3848
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3843
3849
|
type: "button",
|
|
@@ -4589,7 +4595,7 @@ Instruction-following style:
|
|
|
4589
4595
|
}
|
|
4590
4596
|
//#endregion
|
|
4591
4597
|
//#region \0sp-css:src/client/SettingsSection
|
|
4592
|
-
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.59.2 ACP 账户台账表(owner 2026-10-17:「面板里只放个开关不放具体的条目吗?」)\n *\n * 用**表格**而不是卡片列表:台账天生是\"一行一条、列对齐可比\"的数据\n * (账户 / 角色 / 状态 / token 指纹),卡片会把可比性拆散。\n * 窄屏靠横向滚动兜底——不折行,折行后\"哪一格对应哪一列\"就看不出来了。 */\n.ss-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n overflow-x: auto;\n display: block;\n}\n\n.ss-table th,\n.ss-table td {\n padding: 4px 8px;\n text-align: left;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n white-space: nowrap;\n}\n\n.ss-table th {\n color: var(--dsw-alias-label-tertiary);\n font-weight: 600;\n}\n\n.ss-table td code {\n font-size: 11px;\n padding: 1px 4px;\n border-radius: 3px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n}\n\n.ss-table button {\n margin-left: 4px;\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";
|
|
4598
|
+
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.59.1 ACP 账户台账表(owner 2026-10-17:「面板里只放个开关不放具体的条目吗?」)\n *\n * 用**表格**而不是卡片列表:台账天生是\"一行一条、列对齐可比\"的数据\n * (账户 / 角色 / 状态 / token 指纹),卡片会把可比性拆散。\n * 窄屏靠横向滚动兜底——不折行,折行后\"哪一格对应哪一列\"就看不出来了。 */\n.ss-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n overflow-x: auto;\n display: block;\n}\n\n.ss-table th,\n.ss-table td {\n padding: 4px 8px;\n text-align: left;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n white-space: nowrap;\n}\n\n.ss-table th {\n color: var(--dsw-alias-label-tertiary);\n font-weight: 600;\n}\n\n.ss-table td code {\n font-size: 11px;\n padding: 1px 4px;\n border-radius: 3px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n}\n\n.ss-table button {\n margin-left: 4px;\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";
|
|
4593
4599
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SettingsSection\"]") === null) {
|
|
4594
4600
|
const tag = document.createElement("style");
|
|
4595
4601
|
tag.setAttribute("data-sp-css", "SettingsSection");
|
package/lib/index.js
CHANGED
|
@@ -15005,7 +15005,7 @@ const FILE_UPLOAD_PATH = "/serenity/file-upload";
|
|
|
15005
15005
|
const CONFIG_PATH = "/serenity/config";
|
|
15006
15006
|
const CCCS_PATH = "/serenity/cccs";
|
|
15007
15007
|
const WEIXIN_PATH = "/serenity/weixin";
|
|
15008
|
-
/** 🔴 v1.59.
|
|
15008
|
+
/** 🔴 v1.59.1:ACP 账户台账(面板数据面;形状照 `/serenity/weixin`) */
|
|
15009
15009
|
const ACP_PATH = "/serenity/acp";
|
|
15010
15010
|
/** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
|
|
15011
15011
|
const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
|
|
@@ -21953,6 +21953,7 @@ function registerWeixinSendApi(ctx) {
|
|
|
21953
21953
|
var acp_auth_exports = /* @__PURE__ */ __exportAll({
|
|
21954
21954
|
acpChannelEnabled: () => acpChannelEnabled,
|
|
21955
21955
|
acpChannelStatus: () => acpChannelStatus,
|
|
21956
|
+
acpSessionKeyFor: () => acpSessionKeyFor,
|
|
21956
21957
|
acpTokenKeyFor: () => acpTokenKeyFor,
|
|
21957
21958
|
cwdInsideRoot: () => cwdInsideRoot,
|
|
21958
21959
|
ensureAcpChannelTokens: () => ensureAcpChannelTokens,
|
|
@@ -21961,6 +21962,7 @@ var acp_auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
21961
21962
|
extractToken: () => extractToken,
|
|
21962
21963
|
listAcpRoots: () => listAcpRoots,
|
|
21963
21964
|
normalizeAcpAccount: () => normalizeAcpAccount,
|
|
21965
|
+
readAcpMainSession: () => readAcpMainSession,
|
|
21964
21966
|
readAcpToken: () => readAcpToken,
|
|
21965
21967
|
readImSettings: () => readImSettings,
|
|
21966
21968
|
removeAcpAccount: () => removeAcpAccount,
|
|
@@ -21969,7 +21971,8 @@ var acp_auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
21969
21971
|
safeEqual: () => safeEqual,
|
|
21970
21972
|
saveAcpAccounts: () => saveAcpAccounts,
|
|
21971
21973
|
tokenFingerprint: () => tokenFingerprint,
|
|
21972
|
-
upsertAcpAccount: () => upsertAcpAccount
|
|
21974
|
+
upsertAcpAccount: () => upsertAcpAccount,
|
|
21975
|
+
writeAcpMainSession: () => writeAcpMainSession
|
|
21973
21976
|
});
|
|
21974
21977
|
/** 多账户凭据键前缀(`IM_ACP_TOKEN_<ACCOUNT_ID>`;与微信桥 `WEIXIN_<ID>_<FIELD>` 同构) */
|
|
21975
21978
|
const ACP_TOKEN_PREFIX = "IM_ACP_TOKEN_";
|
|
@@ -22045,6 +22048,49 @@ function safeEqual(a, b) {
|
|
|
22045
22048
|
function tokenFingerprint(token) {
|
|
22046
22049
|
return createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
22047
22050
|
}
|
|
22051
|
+
/** 主会话键前缀(`IM_ACP_SESSION_<ACCOUNT_ID>`;与 `IM_ACP_TOKEN_<ACCOUNT_ID>` 同族同规则) */
|
|
22052
|
+
const ACP_SESSION_PREFIX = "IM_ACP_SESSION_";
|
|
22053
|
+
/**
|
|
22054
|
+
* 该账户的**主会话**键(`IM_ACP_SESSION_<ACCOUNT_ID>`)。
|
|
22055
|
+
*
|
|
22056
|
+
* 🔴 为什么放在 **credential** 节而不是 config 节:它跟 token 一样是**每账户一值的键控数据**
|
|
22057
|
+
* (键由 `accountId` 派生、随账户生命周期存亡)。放 config 节要另造一层 `section.key`
|
|
22058
|
+
* 结构,而这一层的唯一读者就是本模块 ⇒ 多一层结构 = 多一处会漂的形状。
|
|
22059
|
+
*
|
|
22060
|
+
* ⚠️ 它**不是凭据**(会话 id 不是秘密)—— 共享 credential 节只是为了复用它既有的
|
|
22061
|
+
* 「按 accountId 派生键 + 删账户时一并清理」这套语义,不引入第二套键规则。
|
|
22062
|
+
*/
|
|
22063
|
+
function acpSessionKeyFor(accountId) {
|
|
22064
|
+
return `${ACP_SESSION_PREFIX}${accountKeyPart(accountId)}`;
|
|
22065
|
+
}
|
|
22066
|
+
/**
|
|
22067
|
+
* 读该账户的主会话 id(不存在 ⇒ `null`)。
|
|
22068
|
+
*
|
|
22069
|
+
* 🔴 **读路径无副作用**(与 `readAcpToken` 同纪律):不存在的含义是「这个账户还没有主会话」,
|
|
22070
|
+
* 而那时的正确行为是**让 App 去建**(`session/list` 回空数组),不是替它编一个。
|
|
22071
|
+
*/
|
|
22072
|
+
function readAcpMainSession(root, accountId) {
|
|
22073
|
+
try {
|
|
22074
|
+
const value = getEntry(root, "credential", acpSessionKeyFor(accountId));
|
|
22075
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
22076
|
+
} catch {
|
|
22077
|
+
return null;
|
|
22078
|
+
}
|
|
22079
|
+
}
|
|
22080
|
+
/**
|
|
22081
|
+
* 记下该账户的主会话 id(**写操作**;由 `session/new` 的出站响应触发)。
|
|
22082
|
+
*
|
|
22083
|
+
* 🔴 **后写覆盖先写**(last-wins):owner 要的是"一条长期对话",而"再建一条"是用户
|
|
22084
|
+
* 在 App 里显式点「新对话」的结果 ⇒ 那时他要的就是**新的那条**。
|
|
22085
|
+
* ⛔ **被否的两种做法**:① 已有主会话就拒建(用户会看到"点了没反应");
|
|
22086
|
+
* ② 把 `session/new` 改写成 `session/resume`(响应形状不同,要让客户端认一条
|
|
22087
|
+
* **不存在于协议里**的响应 —— 那是拿协议兼容性换一个我们本来就有的语义)。
|
|
22088
|
+
*/
|
|
22089
|
+
function writeAcpMainSession(root, accountId, sessionId) {
|
|
22090
|
+
const id = String(sessionId ?? "").trim();
|
|
22091
|
+
if (id === "") return;
|
|
22092
|
+
writeEntry(root, "credential", acpSessionKeyFor(accountId), id);
|
|
22093
|
+
}
|
|
22048
22094
|
/**
|
|
22049
22095
|
* 从一次 HTTP upgrade 请求里取出候选 token。
|
|
22050
22096
|
*
|
|
@@ -22223,6 +22269,7 @@ function acpChannelStatus(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
22223
22269
|
enabled,
|
|
22224
22270
|
hasToken: token !== null,
|
|
22225
22271
|
tokenFingerprint: token ? tokenFingerprint(token) : null,
|
|
22272
|
+
mainSession: readAcpMainSession(root, accountId),
|
|
22226
22273
|
usable: usable.has(accountId),
|
|
22227
22274
|
blockedBy
|
|
22228
22275
|
});
|
|
@@ -22304,11 +22351,17 @@ function upsertAcpAccount(root, account, paths = DEFAULT_SERENITY_CONFIG_PATHS)
|
|
|
22304
22351
|
else list.push(account);
|
|
22305
22352
|
return saveAcpAccounts(root, list, paths);
|
|
22306
22353
|
}
|
|
22307
|
-
/**
|
|
22354
|
+
/**
|
|
22355
|
+
* 删一条账户(**同时清掉它的 token + 主会话**,避免留死键)。
|
|
22356
|
+
*
|
|
22357
|
+
* 🔴 主会话键**必须一起清**:否则日后用同一个 `accountId` 重建账户时,它会**静默继承**
|
|
22358
|
+
* 那条旧会话 —— 而那条会话的 cwd/角色都是上一代账户的。这个失效**没有任何信号**
|
|
22359
|
+
* (列表上它看着就是一条正常会话)。
|
|
22360
|
+
*/
|
|
22308
22361
|
function removeAcpAccount(root, accountId, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
22309
22362
|
const next = saveAcpAccounts(root, (readImSettings(root, paths).acp.accounts ?? []).filter((a) => a.accountId !== accountId), paths);
|
|
22310
|
-
try {
|
|
22311
|
-
unsetEntry(root, "credential",
|
|
22363
|
+
for (const key of [acpTokenKeyFor(accountId), acpSessionKeyFor(accountId)]) try {
|
|
22364
|
+
unsetEntry(root, "credential", key);
|
|
22312
22365
|
} catch {}
|
|
22313
22366
|
return next;
|
|
22314
22367
|
}
|
|
@@ -22500,7 +22553,7 @@ function acpFaceEnabled() {
|
|
|
22500
22553
|
return readSimpleSettings().acpFaceEnabled;
|
|
22501
22554
|
}
|
|
22502
22555
|
/**
|
|
22503
|
-
* 把客户端给的 `cwd` 归一化成宿主要求的**绝对路径**(v1.59.
|
|
22556
|
+
* 把客户端给的 `cwd` 归一化成宿主要求的**绝对路径**(v1.59.1)。
|
|
22504
22557
|
*
|
|
22505
22558
|
* ## 为什么需要
|
|
22506
22559
|
* 宿主 `dsh-acp` 在每次 `session/new` 都有这一行(`lib/index.js` 逐字):
|
|
@@ -22538,13 +22591,20 @@ function normalizeClientCwd(raw, cccRoot) {
|
|
|
22538
22591
|
* 实测注意(P0′):`dsh-acp` 写出的每条消息**自带换行**,客户端同样 ⇒ 按行切分即正确。
|
|
22539
22592
|
* ⚠️ **空行要丢**;**非 JSON 帧要容忍**(桥/客户端可能插入控制帧)。
|
|
22540
22593
|
*
|
|
22541
|
-
* 🔴 v1.59.
|
|
22594
|
+
* 🔴 v1.59.1:**入站消息在这里做一次 `cwd` 归一化**(见 {@link normalizeClientCwd})。
|
|
22542
22595
|
* 为什么是这里:`session/new` 与 `session/resume` 都带 `cwd`,而这是**唯一**能同时
|
|
22543
22596
|
* 拦到两者、且属于我们自己的缝(再往下就进 `dsh-acp` 了,那里改了就是改宿主)。
|
|
22544
22597
|
*
|
|
22598
|
+
* 🔴 v1.59.2:这条缝同时承担 **owner「每账户固定一条长期对话」** 的双向改写 ——
|
|
22599
|
+
* 入站 `recordAcpPending`(记 id→method)+ 出站 `rewriteAcpResponse`(收窄 list / 记 new)。
|
|
22600
|
+
* ⚠️ 两件事都必须在**同一个** `ws` 上做:`pending` 是**每条连接一份**的,
|
|
22601
|
+
* 拆到两处会让它失去"这条连接上请求与响应配对"这个唯一用途。
|
|
22602
|
+
*
|
|
22545
22603
|
* @param cccRoot 本连接归属的 CCC 根(token 反查所得)
|
|
22604
|
+
* @param accountId 本连接归属的账户键(主会话键由它派生)
|
|
22546
22605
|
*/
|
|
22547
|
-
function wsToAcpStream(ws, onWarn, cccRoot) {
|
|
22606
|
+
function wsToAcpStream(ws, onWarn, cccRoot, accountId) {
|
|
22607
|
+
const pin = createAcpSessionPin(cccRoot, accountId);
|
|
22548
22608
|
return {
|
|
22549
22609
|
readable: new ReadableStream({ start(controller) {
|
|
22550
22610
|
let buf = "";
|
|
@@ -22562,6 +22622,7 @@ function wsToAcpStream(ws, onWarn, cccRoot) {
|
|
|
22562
22622
|
onWarn(line.slice(0, 120));
|
|
22563
22623
|
continue;
|
|
22564
22624
|
}
|
|
22625
|
+
recordAcpPending(pin, msg);
|
|
22565
22626
|
controller.enqueue(normalizeInboundCwd(msg, cccRoot));
|
|
22566
22627
|
}
|
|
22567
22628
|
});
|
|
@@ -22577,30 +22638,133 @@ function wsToAcpStream(ws, onWarn, cccRoot) {
|
|
|
22577
22638
|
});
|
|
22578
22639
|
} }),
|
|
22579
22640
|
writable: new WritableStream({ write(msg) {
|
|
22580
|
-
ws.send(`${JSON.stringify(msg)}\n`);
|
|
22641
|
+
ws.send(`${JSON.stringify(rewriteAcpResponse(pin, msg))}\n`);
|
|
22581
22642
|
} })
|
|
22582
22643
|
};
|
|
22583
22644
|
}
|
|
22584
22645
|
/**
|
|
22585
|
-
*
|
|
22646
|
+
* 需要把 `cwd` 钉到本连接 CCC 根的方法(**必须逐个列出**)。
|
|
22586
22647
|
*
|
|
22587
|
-
* 🔴
|
|
22588
|
-
*
|
|
22648
|
+
* 🔴 **v1.59.1 的第一版判据错了**:只判「params 里有没有 `cwd`」⇒ 漏掉 `session/list`。
|
|
22649
|
+
* 宿主 `listSessions` 的过滤是
|
|
22650
|
+
* `if (params.cwd !== void 0 && !sameDirectory(header.cwd, params.cwd)) return`
|
|
22651
|
+
* —— **不给 cwd 就返回全机器所有会话**。实测后果:手机 App 列表里冒出
|
|
22652
|
+
* 23 条别的 CCC 的会话,全部显示为 `UNKNOWN` / `New Chat`
|
|
22653
|
+
* (owner:「**我需要是指定CCC的,不然没办法用**」)。
|
|
22654
|
+
* ⇒ 🆕 **判据:凡"应当被 CCC 收窄"的读接口,都必须把 `cwd` 钉成 CCC 根,不能依赖客户端传** ——
|
|
22655
|
+
* token 已经定了 CCC,让客户端再声明一次既冗余、又可以被静默漏掉。
|
|
22656
|
+
*/
|
|
22657
|
+
const CWD_PINNED_METHODS = /* @__PURE__ */ new Set([
|
|
22658
|
+
"session/new",
|
|
22659
|
+
"session/resume",
|
|
22660
|
+
"session/list"
|
|
22661
|
+
]);
|
|
22662
|
+
/**
|
|
22663
|
+
* 把一条入站消息的 `cwd` 归一化(**含"没给就补上"**)。
|
|
22664
|
+
*
|
|
22665
|
+
* | 情形 | 结果 |
|
|
22666
|
+
* |---|---|
|
|
22667
|
+
* | 方法不在 {@link CWD_PINNED_METHODS} | 原样(不碰别的消息) |
|
|
22668
|
+
* | 给了非绝对路径(`serenity` / `a` / 空串) | → 本连接 CCC 根 |
|
|
22669
|
+
* | 给了绝对路径 | 原样(越界由 `cwdInsideRoot` 拒) |
|
|
22670
|
+
* | **压根没给**(`session/list` 常见) | **→ 本连接 CCC 根**(否则列出全机器) |
|
|
22589
22671
|
*/
|
|
22590
22672
|
function normalizeInboundCwd(msg, cccRoot) {
|
|
22591
22673
|
if (typeof msg !== "object" || msg === null) return msg;
|
|
22592
22674
|
const m = msg;
|
|
22593
|
-
if (typeof m.
|
|
22594
|
-
|
|
22595
|
-
const normalized = normalizeClientCwd(m.params.cwd, cccRoot);
|
|
22675
|
+
if (typeof m.method !== "string" || !CWD_PINNED_METHODS.has(m.method)) return msg;
|
|
22676
|
+
const params = typeof m.params === "object" && m.params !== null ? m.params : {};
|
|
22596
22677
|
return {
|
|
22597
22678
|
...msg,
|
|
22598
22679
|
params: {
|
|
22599
|
-
...
|
|
22600
|
-
cwd:
|
|
22680
|
+
...params,
|
|
22681
|
+
cwd: normalizeClientCwd(params.cwd, cccRoot)
|
|
22601
22682
|
}
|
|
22602
22683
|
};
|
|
22603
22684
|
}
|
|
22685
|
+
/** 请求 id 的 Map 键(区分 `1` 与 `"1"` —— JSON-RPC 允许两者,混用也不该串号) */
|
|
22686
|
+
function requestIdKey(id) {
|
|
22687
|
+
return `${typeof id}:${String(id)}`;
|
|
22688
|
+
}
|
|
22689
|
+
/** 记账上限(防"客户端发一堆请求又从不收响应"把内存撑大) */
|
|
22690
|
+
const MAX_PENDING = 256;
|
|
22691
|
+
/** 建一条连接的钉住状态 */
|
|
22692
|
+
function createAcpSessionPin(root, accountId) {
|
|
22693
|
+
return {
|
|
22694
|
+
root,
|
|
22695
|
+
accountId,
|
|
22696
|
+
pending: /* @__PURE__ */ new Map()
|
|
22697
|
+
};
|
|
22698
|
+
}
|
|
22699
|
+
/**
|
|
22700
|
+
* 入站记账:把"这条请求的方法"记住,供出站按 `id` 反查。
|
|
22701
|
+
*
|
|
22702
|
+
* 判据:**既要有 `id`,又要有 `method`** —— 只满足一个的是响应(`id` 无 `method`)
|
|
22703
|
+
* 或通知(`method` 无 `id`)⇒ 与我们无关,不记。
|
|
22704
|
+
*/
|
|
22705
|
+
function recordAcpPending(pin, msg) {
|
|
22706
|
+
if (typeof msg !== "object" || msg === null) return;
|
|
22707
|
+
const m = msg;
|
|
22708
|
+
if (m.id === void 0 || m.id === null) return;
|
|
22709
|
+
if (typeof m.method !== "string") return;
|
|
22710
|
+
const key = requestIdKey(m.id);
|
|
22711
|
+
pin.pending.set(key, m.method);
|
|
22712
|
+
while (pin.pending.size > MAX_PENDING) {
|
|
22713
|
+
const oldest = pin.pending.keys().next();
|
|
22714
|
+
if (oldest.done) break;
|
|
22715
|
+
pin.pending.delete(oldest.value);
|
|
22716
|
+
}
|
|
22717
|
+
}
|
|
22718
|
+
/**
|
|
22719
|
+
* 出站改写(本功能的落点)。返回**要发给客户端的那条消息**。
|
|
22720
|
+
*
|
|
22721
|
+
* | 原响应 | 改写后 |
|
|
22722
|
+
* |---|---|
|
|
22723
|
+
* | `session/list` | `{sessions:[该账户那一条]}`;**没有主会话 ⇒ `{sessions:[]}`** |
|
|
22724
|
+
* | `session/new` | 原样,**同时**把 `result.sessionId` 记为该账户的主会话 |
|
|
22725
|
+
* | 其余(含所有 error 响应、所有通知) | 原样 |
|
|
22726
|
+
*
|
|
22727
|
+
* 🔴 **没有主会话时回空数组,绝不回退成全量列表** —— 退回去正是 owner 说
|
|
22728
|
+
* 「23 个对我没什么用」的那个东西。空数组会让 App 引导用户新建,而新建即建立主会话。
|
|
22729
|
+
*
|
|
22730
|
+
* 🔴 `nextCursor` **一律丢掉**:收窄到一条之后,分页游标失去意义;
|
|
22731
|
+
* 留着它会让客户端继续翻页(而后续页仍会被收窄成同一条 ⇒ 前端可能出现重复项)。
|
|
22732
|
+
*/
|
|
22733
|
+
function rewriteAcpResponse(pin, msg) {
|
|
22734
|
+
if (typeof msg !== "object" || msg === null) return msg;
|
|
22735
|
+
const m = msg;
|
|
22736
|
+
if (m.method !== void 0) return msg;
|
|
22737
|
+
const method = m.id === void 0 || m.id === null ? void 0 : pin.pending.get(requestIdKey(m.id));
|
|
22738
|
+
if (method === void 0) return msg;
|
|
22739
|
+
if (m.error !== void 0) return msg;
|
|
22740
|
+
if (method === "session/new") {
|
|
22741
|
+
const sessionId = m.result?.sessionId;
|
|
22742
|
+
if (typeof sessionId === "string" && sessionId.trim() !== "") try {
|
|
22743
|
+
writeAcpMainSession(pin.root, pin.accountId, sessionId);
|
|
22744
|
+
} catch (err) {
|
|
22745
|
+
console.warn(`[serenity-hooks] ⚠ 面 E:记录主会话失败(CCC=${pin.root} 账户=${pin.accountId}): ${String(err?.message ?? err)}`);
|
|
22746
|
+
}
|
|
22747
|
+
return msg;
|
|
22748
|
+
}
|
|
22749
|
+
if (method === "session/list") {
|
|
22750
|
+
const pinned = readAcpMainSession(pin.root, pin.accountId);
|
|
22751
|
+
if (!pinned) return {
|
|
22752
|
+
...m,
|
|
22753
|
+
result: { sessions: [] }
|
|
22754
|
+
};
|
|
22755
|
+
const list = m.result?.sessions;
|
|
22756
|
+
const hit = Array.isArray(list) ? list.find((s) => s?.sessionId === pinned) : void 0;
|
|
22757
|
+
const sessions = hit !== void 0 ? [hit] : [{
|
|
22758
|
+
sessionId: pinned,
|
|
22759
|
+
cwd: pin.root
|
|
22760
|
+
}];
|
|
22761
|
+
return {
|
|
22762
|
+
...m,
|
|
22763
|
+
result: { sessions }
|
|
22764
|
+
};
|
|
22765
|
+
}
|
|
22766
|
+
return msg;
|
|
22767
|
+
}
|
|
22604
22768
|
function newState() {
|
|
22605
22769
|
return {
|
|
22606
22770
|
handle: null,
|
|
@@ -22681,9 +22845,10 @@ ACP 面:认证失败(${verdict.reason})。请在该 CCC 的 localstore 生
|
|
|
22681
22845
|
try {
|
|
22682
22846
|
const connRoot = verdict.root;
|
|
22683
22847
|
const connRole = verdict.role;
|
|
22848
|
+
const connAccount = verdict.accountId;
|
|
22684
22849
|
const stream = wsToAcpStream(ws, (line) => {
|
|
22685
22850
|
console.log(`[serenity-hooks] 面 E:丢弃一条非 JSON 帧(前 120 字): ${line}`);
|
|
22686
|
-
}, connRoot);
|
|
22851
|
+
}, connRoot, connAccount);
|
|
22687
22852
|
const fiber = ctx.plugin({
|
|
22688
22853
|
inject: ACP_REQUIRED_SERVICES,
|
|
22689
22854
|
apply: (scoped) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.59.
|
|
3
|
+
"version": "1.59.2",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|