@shgroup/dsh-serenity-hooks 1.20.5 → 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 +430 -53
- 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,8 +7,262 @@ 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$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.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");
|
|
@@ -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
|
});
|
|
@@ -244,11 +512,15 @@ window.__ModuleLoader__.load({
|
|
|
244
512
|
/** 图片落盘接口路径(node half api.ts,client 专属 x-serenity-ui 头) */
|
|
245
513
|
const UPLOAD_PATH = "/serenity/image-upload";
|
|
246
514
|
/**
|
|
247
|
-
*
|
|
248
|
-
*
|
|
515
|
+
* 图片提示消息模板(协议固有,S142 用户迭代):
|
|
516
|
+
* - v1.20.5 目录级提示(无文件名)→ 用户反馈 agent 还要猜
|
|
517
|
+
* - v1.20.6 恢复具体路径(对话里一定写名图片路径,agent 直接可用,无需猜)
|
|
518
|
+
* 单图:用户提供了一张图片(路径:...);多图:每张一行路径
|
|
249
519
|
*/
|
|
250
|
-
|
|
251
|
-
|
|
520
|
+
function imageNoteTemplate(paths) {
|
|
521
|
+
if (paths.length === 1) return `用户提供了一张图片(路径:${paths[0]})`;
|
|
522
|
+
return `用户提供了 ${paths.length} 张图片:\n${paths.map((p) => `- ${p}`).join("\n")}`;
|
|
523
|
+
}
|
|
252
524
|
/** 浏览器 File → base64(与 ui-conversation serializeImages 等价的最小实现) */
|
|
253
525
|
function fileToBase64(file) {
|
|
254
526
|
return file.arrayBuffer().then((buffer) => {
|
|
@@ -309,17 +581,8 @@ window.__ModuleLoader__.load({
|
|
|
309
581
|
if (!result.ok) throw new Error(`serenity image fallback resend failed: ${result.error?.code}: ${result.error?.message}`);
|
|
310
582
|
}
|
|
311
583
|
//#endregion
|
|
312
|
-
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/ImageFallbackDock
|
|
313
|
-
const css = "/* ImageFallbackDock — 图片自动落盘兜底状态条(S142) */\n\n.serenity-image-fallback {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n line-height: 1.5;\n padding: 2px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-surface-muted, rgba(127, 127, 127, 0.1));\n color: var(--dsw-alias-text-secondary, currentColor);\n}\n\n.serenity-image-fallback[data-state='busy'] {\n color: var(--dsw-alias-text-secondary, currentColor);\n}\n\n.serenity-image-fallback[data-state='done'] {\n color: var(--dsw-alias-text-success, currentColor);\n}\n\n.serenity-image-fallback[data-state='error'] {\n color: var(--dsw-alias-text-danger, currentColor);\n}\n\n.serenity-image-fallback code {\n font-family: var(--dsw-alias-font-mono, monospace);\n font-size: 11px;\n background: var(--dsw-alias-surface-muted, rgba(127, 127, 127, 0.12));\n border-radius: 4px;\n padding: 0 4px;\n}\n";
|
|
314
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
|
|
315
|
-
const tag = document.createElement("style");
|
|
316
|
-
tag.setAttribute("data-sp-css", "1");
|
|
317
|
-
tag.textContent = css;
|
|
318
|
-
document.head.appendChild(tag);
|
|
319
|
-
}
|
|
320
|
-
//#endregion
|
|
321
584
|
//#region src/client/ImageFallbackDock.tsx
|
|
322
|
-
/** input.dock
|
|
585
|
+
/** input.dock 条目(静默):图片发送失败自动落盘 + 文本重发,无 UI */
|
|
323
586
|
function ImageFallbackDock(props) {
|
|
324
587
|
const zone = props;
|
|
325
588
|
const session = zone.session;
|
|
@@ -327,9 +590,6 @@ window.__ModuleLoader__.load({
|
|
|
327
590
|
const sessionId = props.sessionId;
|
|
328
591
|
const { uploadImage, getDraftFiles, resendText } = props;
|
|
329
592
|
const inputActions = props.inputActions;
|
|
330
|
-
const [state, setState] = (0, react.useState)("idle");
|
|
331
|
-
const [paths, setPaths] = (0, react.useState)([]);
|
|
332
|
-
const [errorText, setErrorText] = (0, react.useState)(null);
|
|
333
593
|
const handlingRef = (0, react.useRef)(false);
|
|
334
594
|
const promptError = session?.promptError?.error;
|
|
335
595
|
const imageIds = input?.imageIds ?? [];
|
|
@@ -338,14 +598,13 @@ window.__ModuleLoader__.load({
|
|
|
338
598
|
if (promptError.details?.reason !== "MODEL_DOES_NOT_SUPPORT_IMAGES") return;
|
|
339
599
|
if (handlingRef.current || imageIds.length === 0) return;
|
|
340
600
|
handlingRef.current = true;
|
|
341
|
-
setState("uploading");
|
|
342
601
|
(async () => {
|
|
343
602
|
try {
|
|
344
603
|
const files = await getDraftFiles(String(sessionId), imageIds);
|
|
345
604
|
if (files.length === 0) throw new Error("no draft image files");
|
|
346
605
|
const saved = [];
|
|
347
606
|
for (const file of files) saved.push(await uploadImage(file, String(sessionId)));
|
|
348
|
-
const note =
|
|
607
|
+
const note = imageNoteTemplate(saved);
|
|
349
608
|
const draft = input?.draft ?? "";
|
|
350
609
|
const text = draft === "" ? note : `${draft}\n${note}`;
|
|
351
610
|
if (inputActions?.removeImage !== void 0 && inputActions.setDraft !== void 0 && inputActions.submit !== void 0) {
|
|
@@ -353,13 +612,8 @@ window.__ModuleLoader__.load({
|
|
|
353
612
|
inputActions.setDraft(text);
|
|
354
613
|
inputActions.submit();
|
|
355
614
|
} else await resendText(String(sessionId), text);
|
|
356
|
-
setPaths(saved);
|
|
357
|
-
setState("done");
|
|
358
615
|
} catch (err) {
|
|
359
|
-
|
|
360
|
-
console.warn(`[serenity] image fallback failed: ${message}`);
|
|
361
|
-
setErrorText(message);
|
|
362
|
-
setState("error");
|
|
616
|
+
console.warn(`[serenity] image fallback failed: ${String(err?.message ?? err)}`);
|
|
363
617
|
} finally {
|
|
364
618
|
handlingRef.current = false;
|
|
365
619
|
}
|
|
@@ -374,24 +628,132 @@ window.__ModuleLoader__.load({
|
|
|
374
628
|
input?.draft,
|
|
375
629
|
inputActions
|
|
376
630
|
]);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
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 })
|
|
390
664
|
});
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
+
]
|
|
395
757
|
});
|
|
396
758
|
}
|
|
397
759
|
//#endregion
|
|
@@ -399,13 +761,20 @@ window.__ModuleLoader__.load({
|
|
|
399
761
|
const inject = [
|
|
400
762
|
"slots",
|
|
401
763
|
"conversation",
|
|
402
|
-
"sessions"
|
|
764
|
+
"sessions",
|
|
765
|
+
"settingsScope"
|
|
403
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
|
+
};
|
|
404
772
|
function apply(ctx) {
|
|
405
773
|
ctx.inject([
|
|
406
774
|
"slots",
|
|
407
775
|
"conversation",
|
|
408
|
-
"sessions"
|
|
776
|
+
"sessions",
|
|
777
|
+
"settingsScope"
|
|
409
778
|
], (scope) => {
|
|
410
779
|
scope.effect(() => scope.slots.register({
|
|
411
780
|
name: "conversation.session.header.actions",
|
|
@@ -422,6 +791,14 @@ window.__ModuleLoader__.load({
|
|
|
422
791
|
resendText: (sessionId, text) => resendText(scope, sessionId, text)
|
|
423
792
|
})
|
|
424
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");
|
|
425
802
|
});
|
|
426
803
|
}
|
|
427
804
|
//#endregion
|