@shgroup/dsh-serenity-hooks 1.20.6 → 1.21.0
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 +3 -2
- package/lib/client.js +421 -12
- package/lib/index.js +5206 -2450
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
|
-
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/loop + 拦截缝机械约束(safe-mode
|
|
5
|
+
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/loop/session_rebuild + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
7
7
|
"dsh": ">=0.1.0-rc.5"
|
|
8
8
|
},
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"neat",
|
|
18
18
|
"cce",
|
|
19
19
|
"loop",
|
|
20
|
+
"session_rebuild",
|
|
20
21
|
"localstore"
|
|
21
22
|
],
|
|
22
23
|
"skills": []
|
package/lib/client.js
CHANGED
|
@@ -7,12 +7,266 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
//#region src/client/accounts-api.ts
|
|
11
|
+
/** wire 账号 → 本地编辑行 */
|
|
12
|
+
function accountDraftFromWire(a) {
|
|
13
|
+
return {
|
|
14
|
+
id: a.id,
|
|
15
|
+
user: a.user,
|
|
16
|
+
pass: "",
|
|
17
|
+
isNew: false,
|
|
18
|
+
hasPassword: a.hasPassword
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** 生成新账号 id(本地编辑用;提交时若为空由服务端拒绝) */
|
|
22
|
+
function newAccountId() {
|
|
23
|
+
return `a-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
24
|
+
}
|
|
25
|
+
/** 本地编辑行 → wire 账号(提交时;pass 字段携带) */
|
|
26
|
+
function accountToWire(d) {
|
|
27
|
+
return {
|
|
28
|
+
id: d.id,
|
|
29
|
+
user: d.user,
|
|
30
|
+
pass: d.pass
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** 校验本地编辑行:user 非空;新账号必须设密码 */
|
|
34
|
+
function validateDraft(d) {
|
|
35
|
+
if (d.user.trim() === "") return "用户名不能为空";
|
|
36
|
+
if (d.isNew && d.pass === "") return "新账号必须设置密码";
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const CONFIG_PATH = "/serenity/config";
|
|
40
|
+
/** GET 配置(含账号列表;无 hash) */
|
|
41
|
+
async function fetchConfig(sessionId) {
|
|
42
|
+
const qs = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
43
|
+
const res = await fetch(`${CONFIG_PATH}${qs}`, { headers: {
|
|
44
|
+
accept: "application/json",
|
|
45
|
+
"x-serenity-ui": "1"
|
|
46
|
+
} });
|
|
47
|
+
if (!res.ok) return null;
|
|
48
|
+
return (await res.json()).config ?? null;
|
|
49
|
+
}
|
|
50
|
+
/** PUT 配置(账号 patch)→ 返回保存后的 wire */
|
|
51
|
+
async function saveConfig(sessionId, patch) {
|
|
52
|
+
const res = await fetch(CONFIG_PATH, {
|
|
53
|
+
method: "PUT",
|
|
54
|
+
headers: {
|
|
55
|
+
"content-type": "application/json",
|
|
56
|
+
"x-serenity-ui": "1"
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
sessionId,
|
|
60
|
+
config: patch
|
|
61
|
+
})
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
const body = await res.json().catch(() => null);
|
|
65
|
+
throw new Error(body?.error ?? `保存失败(${res.status})`);
|
|
66
|
+
}
|
|
67
|
+
return (await res.json()).config ?? null;
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/AccountsTab
|
|
71
|
+
const css$2 = "/* AccountsTab — 高级面板账号 tab(v1.21;--dsw-alias-* 语义 token,明暗自适应) */\n\n.at-root {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 6px 2px;\n font-size: 13px;\n}\n\n.at-note {\n margin: 0;\n font-size: 12px;\n opacity: 0.65;\n}\n\n.at-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.at-label {\n font-weight: 600;\n white-space: nowrap;\n}\n\n.at-input {\n flex: 1;\n min-width: 0;\n padding: 5px 8px;\n border: 1px solid var(--dsw-alias-border, #333);\n border-radius: 6px;\n background: var(--dsw-alias-field, #222);\n color: var(--dsw-alias-text, #eee);\n font-size: 13px;\n}\n\n.at-input.at-port {\n flex: none;\n width: 72px;\n}\n\n.at-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.at-head {\n display: flex;\n gap: 8px;\n font-size: 12px;\n opacity: 0.6;\n padding: 0 2px;\n}\n\n.at-item {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.at-colUser { flex: 1.2; }\n.at-colPass { flex: 1.6; }\n.at-colOp { flex: none; width: 48px; }\n\n.at-del {\n flex: none;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border, #444);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-danger, #f87171);\n font-size: 12px;\n cursor: pointer;\n}\n\n.at-add {\n align-self: flex-start;\n padding: 5px 12px;\n border: 1px dashed var(--dsw-alias-border, #444);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-text, #ccc);\n font-size: 12px;\n cursor: pointer;\n}\n\n.at-error {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-danger, #f87171);\n}\n\n.at-actions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n}\n\n.at-save {\n padding: 6px 22px;\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-accent, #3b82f6);\n color: #fff;\n font-size: 13px;\n cursor: pointer;\n}\n\n.at-save:disabled {\n opacity: 0.6;\n cursor: default;\n}\n";
|
|
72
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
|
|
73
|
+
const tag = document.createElement("style");
|
|
74
|
+
tag.setAttribute("data-sp-css", "1");
|
|
75
|
+
tag.textContent = css$2;
|
|
76
|
+
document.head.appendChild(tag);
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/client/AccountsTab.tsx
|
|
80
|
+
/**
|
|
81
|
+
* AccountsTab.tsx — 高级面板「账号」tab(v1.21 复杂配置层)
|
|
82
|
+
*
|
|
83
|
+
* 承载复杂配置:gateway 监听 host/port + 账号列表 CRUD(含密码)。
|
|
84
|
+
* 简单配置(开关/阈值)在 dsh 原生设置面板(SettingsSection)——本 tab 仅提示引导。
|
|
85
|
+
* 数据:GET/PUT /serenity/config(accounts-api.ts);无滚动条约束(紧凑布局)。
|
|
86
|
+
*/
|
|
87
|
+
function AccountsTab(props) {
|
|
88
|
+
const { sessionId } = props;
|
|
89
|
+
const [config, setConfig] = (0, react.useState)(null);
|
|
90
|
+
const [drafts, setDrafts] = (0, react.useState)([]);
|
|
91
|
+
const [host, setHost] = (0, react.useState)("0.0.0.0");
|
|
92
|
+
const [port, setPort] = (0, react.useState)(3081);
|
|
93
|
+
const [error, setError] = (0, react.useState)(null);
|
|
94
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
95
|
+
const [loaded, setLoaded] = (0, react.useState)(false);
|
|
96
|
+
(0, react.useEffect)(() => {
|
|
97
|
+
let alive = true;
|
|
98
|
+
fetchConfig(sessionId).then((cfg) => {
|
|
99
|
+
if (!alive) return;
|
|
100
|
+
if (cfg) {
|
|
101
|
+
setConfig(cfg);
|
|
102
|
+
setHost(cfg.gateway.host);
|
|
103
|
+
setPort(cfg.gateway.port);
|
|
104
|
+
setDrafts(cfg.gateway.accounts.map(accountDraftFromWire));
|
|
105
|
+
}
|
|
106
|
+
setLoaded(true);
|
|
107
|
+
});
|
|
108
|
+
return () => {
|
|
109
|
+
alive = false;
|
|
110
|
+
};
|
|
111
|
+
}, [sessionId]);
|
|
112
|
+
const updateDraft = (id, patch) => {
|
|
113
|
+
setDrafts((ds) => ds.map((d) => d.id === id ? {
|
|
114
|
+
...d,
|
|
115
|
+
...patch
|
|
116
|
+
} : d));
|
|
117
|
+
};
|
|
118
|
+
const addRow = () => {
|
|
119
|
+
setDrafts((ds) => [...ds, {
|
|
120
|
+
id: newAccountId(),
|
|
121
|
+
user: "",
|
|
122
|
+
pass: "",
|
|
123
|
+
isNew: true,
|
|
124
|
+
hasPassword: false
|
|
125
|
+
}]);
|
|
126
|
+
};
|
|
127
|
+
const removeRow = (id) => {
|
|
128
|
+
setDrafts((ds) => ds.filter((d) => d.id !== id));
|
|
129
|
+
};
|
|
130
|
+
const save = async () => {
|
|
131
|
+
for (const d of drafts) {
|
|
132
|
+
const err = validateDraft(d);
|
|
133
|
+
if (err) {
|
|
134
|
+
setError(err);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
setBusy(true);
|
|
139
|
+
setError(null);
|
|
140
|
+
try {
|
|
141
|
+
const cfg = await saveConfig(sessionId, { gateway: {
|
|
142
|
+
host,
|
|
143
|
+
port,
|
|
144
|
+
accounts: drafts.map(accountToWire)
|
|
145
|
+
} });
|
|
146
|
+
if (cfg) {
|
|
147
|
+
setConfig(cfg);
|
|
148
|
+
setDrafts(cfg.gateway.accounts.map(accountDraftFromWire));
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
152
|
+
} finally {
|
|
153
|
+
setBusy(false);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
if (!loaded) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
157
|
+
className: "at-root",
|
|
158
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
159
|
+
className: "at-note",
|
|
160
|
+
children: "加载中…"
|
|
161
|
+
})
|
|
162
|
+
});
|
|
163
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
164
|
+
className: "at-root",
|
|
165
|
+
children: [
|
|
166
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
167
|
+
className: "at-note",
|
|
168
|
+
children: "复杂配置(账号密码)。开关/阈值等简单配置请到 dsh 设置面板「Serenity」页。"
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
171
|
+
className: "at-row",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
174
|
+
className: "at-label",
|
|
175
|
+
children: "监听地址"
|
|
176
|
+
}),
|
|
177
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
178
|
+
className: "at-input",
|
|
179
|
+
value: host,
|
|
180
|
+
onChange: (e) => setHost(e.target.value),
|
|
181
|
+
placeholder: "0.0.0.0"
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
184
|
+
className: "at-label",
|
|
185
|
+
children: "端口"
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
188
|
+
className: "at-input at-port",
|
|
189
|
+
type: "number",
|
|
190
|
+
value: port,
|
|
191
|
+
min: 1,
|
|
192
|
+
max: 65535,
|
|
193
|
+
onChange: (e) => setPort(Number(e.target.value) || 0)
|
|
194
|
+
})
|
|
195
|
+
]
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
198
|
+
className: "at-list",
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
201
|
+
className: "at-head",
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
204
|
+
className: "at-col at-colUser",
|
|
205
|
+
children: "用户名"
|
|
206
|
+
}),
|
|
207
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
208
|
+
className: "at-col at-colPass",
|
|
209
|
+
children: "密码"
|
|
210
|
+
}),
|
|
211
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "at-col at-colOp" })
|
|
212
|
+
]
|
|
213
|
+
}),
|
|
214
|
+
drafts.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
215
|
+
className: "at-item",
|
|
216
|
+
children: [
|
|
217
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
218
|
+
className: "at-input at-colUser",
|
|
219
|
+
value: d.user,
|
|
220
|
+
placeholder: "用户名",
|
|
221
|
+
onChange: (e) => updateDraft(d.id, { user: e.target.value })
|
|
222
|
+
}),
|
|
223
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
224
|
+
className: "at-input at-colPass",
|
|
225
|
+
type: "password",
|
|
226
|
+
value: d.pass,
|
|
227
|
+
placeholder: d.isNew ? "必填" : d.hasPassword ? "留空保留原密码" : "未设置",
|
|
228
|
+
onChange: (e) => updateDraft(d.id, { pass: e.target.value })
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
231
|
+
type: "button",
|
|
232
|
+
className: "at-del",
|
|
233
|
+
onClick: () => removeRow(d.id),
|
|
234
|
+
children: "删除"
|
|
235
|
+
})
|
|
236
|
+
]
|
|
237
|
+
}, d.id)),
|
|
238
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
239
|
+
type: "button",
|
|
240
|
+
className: "at-add",
|
|
241
|
+
onClick: addRow,
|
|
242
|
+
children: "+ 添加账号"
|
|
243
|
+
})
|
|
244
|
+
]
|
|
245
|
+
}),
|
|
246
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
247
|
+
className: "at-error",
|
|
248
|
+
children: error
|
|
249
|
+
}),
|
|
250
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
251
|
+
className: "at-actions",
|
|
252
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
253
|
+
type: "button",
|
|
254
|
+
className: "at-save",
|
|
255
|
+
disabled: busy,
|
|
256
|
+
onClick: () => void save(),
|
|
257
|
+
children: busy ? "保存中…" : "保存"
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
]
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
10
264
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SafeModePanel
|
|
11
|
-
const css = "/* SafeModePanel — 会话头部 Serenity 状态徽章(点击展开详情卡)。\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 尺寸与官方 header 标签一致;前缀 sp- 防冲突。 */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 4px 0 4px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 24px;\n white-space: nowrap;\n}\n\n.sp-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 22px;\n padding: 0 6px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: inherit;\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-badge:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-badge:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 18px;\n padding: 0 8px;\n border: none;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggle:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-toggle:disabled {\n opacity: 0.55;\n cursor: default;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.sp-toggleOn:hover:not(:disabled) {\n background: var(--dsw-alias-state-error-secondary);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleOff:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleLg {\n height: 26px;\n padding: 0 14px;\n font-size: 13px;\n line-height: 26px;\n border-radius: 6px;\n}\n\n/* 详情卡:相对徽章绝对定位(右对齐),卡片 token */\n.sp-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 20;\n width: 300px;\n box-sizing: border-box;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-specific-tip);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-popRow {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-popLabel {\n flex: none;\n width: 34px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-popValue {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-popActions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* loop 运行状态区块(WebUI 等待界面;并行任务各自一行) */\n.sp-popSection {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 44px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\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: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-state-info-primary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-loopResp {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sp-toggle,\n .sp-badge {\n transition: none;\n }\n}\n";
|
|
265
|
+
const css$1 = "/* SafeModePanel — 会话头部 Serenity 状态徽章(点击展开详情卡)。\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 尺寸与官方 header 标签一致;前缀 sp- 防冲突。 */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 4px 0 4px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 24px;\n white-space: nowrap;\n}\n\n.sp-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 22px;\n padding: 0 6px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: inherit;\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-badge:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-badge:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 18px;\n padding: 0 8px;\n border: none;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggle:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-toggle:disabled {\n opacity: 0.55;\n cursor: default;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.sp-toggleOn:hover:not(:disabled) {\n background: var(--dsw-alias-state-error-secondary);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleOff:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleLg {\n height: 26px;\n padding: 0 14px;\n font-size: 13px;\n line-height: 26px;\n border-radius: 6px;\n}\n\n/* 详情卡:相对徽章绝对定位(右对齐),卡片 token */\n.sp-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 20;\n width: 300px;\n box-sizing: border-box;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-specific-tip);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* v1.21 大面板:双 tab(状态/账号),无滚动条约束(内容在视口内完整容纳) */\n.sp-popLg {\n width: 460px;\n padding: 10px 14px 14px;\n}\n\n.sp-tabs {\n display: flex;\n gap: 4px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.sp-tab {\n flex: 1;\n padding: 6px 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.sp-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-tabOn {\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.2));\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n\n.sp-popRow {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-popLabel {\n flex: none;\n width: 34px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-popValue {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-popActions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* loop 运行状态区块(WebUI 等待界面;并行任务各自一行) */\n.sp-popSection {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 44px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\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: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-state-info-primary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-loopResp {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sp-toggle,\n .sp-badge {\n transition: none;\n }\n}\n";
|
|
12
266
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
|
|
13
267
|
const tag = document.createElement("style");
|
|
14
268
|
tag.setAttribute("data-sp-css", "1");
|
|
15
|
-
tag.textContent = css;
|
|
269
|
+
tag.textContent = css$1;
|
|
16
270
|
document.head.appendChild(tag);
|
|
17
271
|
}
|
|
18
272
|
//#endregion
|
|
@@ -23,11 +277,12 @@ window.__ModuleLoader__.load({
|
|
|
23
277
|
function cx(...parts) {
|
|
24
278
|
return parts.filter(Boolean).join(" ");
|
|
25
279
|
}
|
|
26
|
-
/** 会话头部:可点击徽章 + safe-mode
|
|
280
|
+
/** 会话头部:可点击徽章 + safe-mode 开关;点击徽章展开双 tab 大面板 */
|
|
27
281
|
function SafeModePanel(props) {
|
|
28
282
|
const [status, setStatus] = (0, react.useState)(null);
|
|
29
283
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
30
284
|
const [open, setOpen] = (0, react.useState)(false);
|
|
285
|
+
const [tab, setTab] = (0, react.useState)("status");
|
|
31
286
|
const [loops, setLoops] = (0, react.useState)([]);
|
|
32
287
|
const rootRef = (0, react.useRef)(null);
|
|
33
288
|
const sessionId = props.sessionId;
|
|
@@ -129,11 +384,24 @@ window.__ModuleLoader__.load({
|
|
|
129
384
|
status.safeModeOn ? "ON" : "OFF"
|
|
130
385
|
]
|
|
131
386
|
}),
|
|
132
|
-
open && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
133
|
-
className: cx("sp-pop"),
|
|
387
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
388
|
+
className: cx("sp-pop", "sp-popLg"),
|
|
134
389
|
role: "dialog",
|
|
135
|
-
"aria-label": "Serenity
|
|
136
|
-
children:
|
|
390
|
+
"aria-label": "Serenity 高级设定",
|
|
391
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
392
|
+
className: cx("sp-tabs"),
|
|
393
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
394
|
+
type: "button",
|
|
395
|
+
className: cx("sp-tab", tab === "status" && "sp-tabOn"),
|
|
396
|
+
onClick: () => setTab("status"),
|
|
397
|
+
children: "状态"
|
|
398
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
399
|
+
type: "button",
|
|
400
|
+
className: cx("sp-tab", tab === "accounts" && "sp-tabOn"),
|
|
401
|
+
onClick: () => setTab("accounts"),
|
|
402
|
+
children: "账号"
|
|
403
|
+
})]
|
|
404
|
+
}), tab === "accounts" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsTab, { sessionId: String(sessionId ?? "") }) : inCcc ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
137
405
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
138
406
|
className: cx("sp-popRow"),
|
|
139
407
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -199,7 +467,7 @@ window.__ModuleLoader__.load({
|
|
|
199
467
|
className: cx("sp-popValue"),
|
|
200
468
|
children: loops.filter((l) => !l.done).length > 0 ? `运行中 ${loops.filter((l) => !l.done).length}${loops.length > 0 ? ` / 共 ${loops.length}` : ""}` : "无运行中 loop"
|
|
201
469
|
})]
|
|
202
|
-
}), loops.slice(0,
|
|
470
|
+
}), loops.slice(0, 4).map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
203
471
|
className: cx("sp-loopItem"),
|
|
204
472
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
205
473
|
className: cx("sp-loopHead"),
|
|
@@ -221,7 +489,7 @@ window.__ModuleLoader__.load({
|
|
|
221
489
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
222
490
|
className: cx("sp-loopResp"),
|
|
223
491
|
title: l.lastResponse,
|
|
224
|
-
children: l.lastResponse.slice(0,
|
|
492
|
+
children: l.lastResponse.slice(0, 60) || (l.done ? "已完成" : "(等待首轮响应)")
|
|
225
493
|
})]
|
|
226
494
|
}, l.label))]
|
|
227
495
|
})
|
|
@@ -234,7 +502,7 @@ window.__ModuleLoader__.load({
|
|
|
234
502
|
className: cx("sp-popValue"),
|
|
235
503
|
children: "ACC 未激活(工作区不在 CCC 内)"
|
|
236
504
|
})]
|
|
237
|
-
})
|
|
505
|
+
})]
|
|
238
506
|
})
|
|
239
507
|
]
|
|
240
508
|
});
|
|
@@ -363,17 +631,150 @@ window.__ModuleLoader__.load({
|
|
|
363
631
|
return null;
|
|
364
632
|
}
|
|
365
633
|
//#endregion
|
|
634
|
+
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SettingsSection
|
|
635
|
+
const css = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n 沿用 --dsw-alias-* 语义 token(明暗自适应)+ 紧凑布局 */\n\n.ss-root {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 8px 0;\n}\n\n.ss-note {\n margin: 0 0 4px;\n font-size: 12px;\n opacity: 0.7;\n}\n\n.ss-row {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n}\n\n.ss-label {\n min-width: 88px;\n font-weight: 600;\n}\n\n.ss-desc {\n flex: 1;\n font-size: 12px;\n opacity: 0.65;\n}\n\n.ss-row input[type='checkbox'] {\n flex: none;\n}\n\n.ss-row input[type='range'] {\n flex: none;\n width: 140px;\n}\n\n.ss-value {\n min-width: 36px;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n opacity: 0.8;\n}\n\n.ss-degrade {\n padding: 12px 8px;\n font-size: 13px;\n}\n\n.ss-degrade p {\n margin: 0;\n}\n";
|
|
636
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
|
|
637
|
+
const tag = document.createElement("style");
|
|
638
|
+
tag.setAttribute("data-sp-css", "1");
|
|
639
|
+
tag.textContent = css;
|
|
640
|
+
document.head.appendChild(tag);
|
|
641
|
+
}
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/client/SettingsSection.tsx
|
|
644
|
+
/** 降级提示文案(旧 RC 白名单时引导去宁静号面板) */
|
|
645
|
+
const DEGRADE_NOTE = "当前运行版本未暴露 serenity-hooks 配置(旧 RC 白名单)。请在会话头部 Serenity 徽章的「高级设定」面板中管理,或升级 DSH。";
|
|
646
|
+
/** dsh 原生设置面板:serenity-hooks 简单配置页 */
|
|
647
|
+
function SettingsSection(props) {
|
|
648
|
+
const { scope } = props;
|
|
649
|
+
const [snapshot, setSnapshot] = (0, react.useState)(() => scope.getSnapshot());
|
|
650
|
+
(0, react.useEffect)(() => {
|
|
651
|
+
return scope.subscribe(() => setSnapshot(scope.getSnapshot()));
|
|
652
|
+
}, [scope]);
|
|
653
|
+
const value = snapshot.value;
|
|
654
|
+
const ready = snapshot.status === "ready";
|
|
655
|
+
const toggle = (field, on) => {
|
|
656
|
+
scope.set(field, on);
|
|
657
|
+
};
|
|
658
|
+
const setThreshold = (v) => {
|
|
659
|
+
scope.set("rebuildThreshold", Math.min(1, Math.max(.01, v)));
|
|
660
|
+
};
|
|
661
|
+
if (!ready) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
662
|
+
className: "ss-degrade",
|
|
663
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: DEGRADE_NOTE })
|
|
664
|
+
});
|
|
665
|
+
const gatewayOn = value?.gatewayEnabled ?? false;
|
|
666
|
+
const rebuildOn = value?.rebuildEnabled ?? true;
|
|
667
|
+
const threshold = value?.rebuildThreshold ?? .9;
|
|
668
|
+
const namingOn = value?.namingEnabled ?? true;
|
|
669
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
670
|
+
className: "ss-root",
|
|
671
|
+
children: [
|
|
672
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
673
|
+
className: "ss-note",
|
|
674
|
+
children: "简单配置(开关/阈值)。账号列表等复杂配置请到会话头部 Serenity 徽章「高级设定」面板。"
|
|
675
|
+
}),
|
|
676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
677
|
+
className: "ss-row",
|
|
678
|
+
children: [
|
|
679
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
680
|
+
className: "ss-label",
|
|
681
|
+
children: "F1 双端口网关"
|
|
682
|
+
}),
|
|
683
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
684
|
+
className: "ss-desc",
|
|
685
|
+
children: "额外监听端口 + 网页登录(外部访问)"
|
|
686
|
+
}),
|
|
687
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
688
|
+
type: "checkbox",
|
|
689
|
+
checked: gatewayOn,
|
|
690
|
+
onChange: (e) => toggle("gatewayEnabled", e.target.checked)
|
|
691
|
+
})
|
|
692
|
+
]
|
|
693
|
+
}),
|
|
694
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
695
|
+
className: "ss-row",
|
|
696
|
+
children: [
|
|
697
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
698
|
+
className: "ss-label",
|
|
699
|
+
children: "F2 超限重建"
|
|
700
|
+
}),
|
|
701
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
702
|
+
className: "ss-desc",
|
|
703
|
+
children: "上下文接近上限时提示 session_rebuild 清空重建"
|
|
704
|
+
}),
|
|
705
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
706
|
+
type: "checkbox",
|
|
707
|
+
checked: rebuildOn,
|
|
708
|
+
onChange: (e) => toggle("rebuildEnabled", e.target.checked)
|
|
709
|
+
})
|
|
710
|
+
]
|
|
711
|
+
}),
|
|
712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
713
|
+
className: "ss-row",
|
|
714
|
+
children: [
|
|
715
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
716
|
+
className: "ss-label",
|
|
717
|
+
children: "重建阈值"
|
|
718
|
+
}),
|
|
719
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
720
|
+
className: "ss-desc",
|
|
721
|
+
children: "contextPressure 触发比例(0.01~1)"
|
|
722
|
+
}),
|
|
723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
724
|
+
type: "range",
|
|
725
|
+
min: .1,
|
|
726
|
+
max: 1,
|
|
727
|
+
step: .05,
|
|
728
|
+
value: threshold,
|
|
729
|
+
disabled: !rebuildOn,
|
|
730
|
+
onChange: (e) => setThreshold(Number(e.target.value))
|
|
731
|
+
}),
|
|
732
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
733
|
+
className: "ss-value",
|
|
734
|
+
children: threshold.toFixed(2)
|
|
735
|
+
})
|
|
736
|
+
]
|
|
737
|
+
}),
|
|
738
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
739
|
+
className: "ss-row",
|
|
740
|
+
children: [
|
|
741
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
742
|
+
className: "ss-label",
|
|
743
|
+
children: "F3 会话命名"
|
|
744
|
+
}),
|
|
745
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
746
|
+
className: "ss-desc",
|
|
747
|
+
children: "dsh 会话创建时自动命名 S###-日期"
|
|
748
|
+
}),
|
|
749
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
750
|
+
type: "checkbox",
|
|
751
|
+
checked: namingOn,
|
|
752
|
+
onChange: (e) => toggle("namingEnabled", e.target.checked)
|
|
753
|
+
})
|
|
754
|
+
]
|
|
755
|
+
})
|
|
756
|
+
]
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
366
760
|
//#region src/client/index.ts
|
|
367
761
|
const inject = [
|
|
368
762
|
"slots",
|
|
369
763
|
"conversation",
|
|
370
|
-
"sessions"
|
|
764
|
+
"sessions",
|
|
765
|
+
"settingsScope"
|
|
371
766
|
];
|
|
767
|
+
/** serenity-hooks 简单配置 scope spec(与 host 侧 registerSettingsSection 对齐) */
|
|
768
|
+
const SERENITY_SCOPE_SPEC = {
|
|
769
|
+
namespace: "serenity-hooks",
|
|
770
|
+
decode: (section) => section !== null && typeof section === "object" ? section : void 0
|
|
771
|
+
};
|
|
372
772
|
function apply(ctx) {
|
|
373
773
|
ctx.inject([
|
|
374
774
|
"slots",
|
|
375
775
|
"conversation",
|
|
376
|
-
"sessions"
|
|
776
|
+
"sessions",
|
|
777
|
+
"settingsScope"
|
|
377
778
|
], (scope) => {
|
|
378
779
|
scope.effect(() => scope.slots.register({
|
|
379
780
|
name: "conversation.session.header.actions",
|
|
@@ -390,6 +791,14 @@ window.__ModuleLoader__.load({
|
|
|
390
791
|
resendText: (sessionId, text) => resendText(scope, sessionId, text)
|
|
391
792
|
})
|
|
392
793
|
}, ImageFallbackDock), "serenity: image fallback dock");
|
|
794
|
+
const serenityScope = scope.get("settingsScope").bind(SERENITY_SCOPE_SPEC);
|
|
795
|
+
scope.effect(() => scope.slots.register({
|
|
796
|
+
name: "settings.section",
|
|
797
|
+
id: "serenity-hooks",
|
|
798
|
+
order: 90,
|
|
799
|
+
label: () => "Serenity",
|
|
800
|
+
inject: () => ({ scope: serenityScope })
|
|
801
|
+
}, SettingsSection), "serenity: simple settings section");
|
|
393
802
|
});
|
|
394
803
|
}
|
|
395
804
|
//#endregion
|