@dsh-plus/agent-preset-rea 0.0.0-stage → 0.1.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/lib/client.js +637 -0
- package/lib/index.d.ts +145 -0
- package/lib/index.js +422 -0
- package/package.json +60 -3
- package/skills/reverse-engineer-anything/LICENSE +21 -0
- package/skills/reverse-engineer-anything/SKILL.md +342 -0
- package/skills/reverse-engineer-anything/references/android-applications.md +34 -0
- package/skills/reverse-engineer-anything/references/connection-and-recovery.md +127 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +71 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +175 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +277 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +78 -0
- package/src/client/card.tsx +120 -0
- package/src/client/client.ts +51 -0
- package/src/client/draft.ts +28 -0
- package/src/client/i18n.ts +43 -0
- package/src/client/styles.ts +32 -0
- package/src/config.ts +110 -0
- package/src/definition.ts +188 -0
- package/src/index.ts +95 -0
- package/src/mcp.ts +42 -0
- package/src/ns.ts +9 -0
- package/src/prompt.ts +25 -0
- package/src/registration.ts +86 -0
- package/src/skill.ts +24 -0
- package/README.md +0 -3
package/lib/client.js
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@dsh-plus/agent-preset-rea",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
+
//#region ../shared/src/client/fields.tsx
|
|
11
|
+
/** 卡片按钮(与 CardChrome 页脚同一套类名与视觉)。 */
|
|
12
|
+
function Button(props) {
|
|
13
|
+
const p = props.prefix ?? "dshp";
|
|
14
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15
|
+
type: "button",
|
|
16
|
+
className: `${p}-btn ${props.variant === "primary" ? `${p}-btnPrimary` : `${p}-btnGhost`}`,
|
|
17
|
+
disabled: props.disabled === true,
|
|
18
|
+
onClick: props.onClick,
|
|
19
|
+
children: props.label
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region ../shared/src/client/card.tsx
|
|
24
|
+
const IDLE_STATUS = {
|
|
25
|
+
kind: "idle",
|
|
26
|
+
text: ""
|
|
27
|
+
};
|
|
28
|
+
function CardChrome(props) {
|
|
29
|
+
const p = props.prefix;
|
|
30
|
+
const badge = props.statusBadge;
|
|
31
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
32
|
+
className: `${p}-section`,
|
|
33
|
+
"aria-label": props.title,
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
36
|
+
className: `${p}-sectionHead`,
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
39
|
+
className: `${p}-sectionTitle`,
|
|
40
|
+
children: props.title
|
|
41
|
+
}),
|
|
42
|
+
badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
43
|
+
className: `${p}-sectionBadge ${badge.on ? `${p}-statusOn` : `${p}-statusOff`}`,
|
|
44
|
+
children: badge.text
|
|
45
|
+
}) : null,
|
|
46
|
+
props.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
47
|
+
className: `${p}-pending`,
|
|
48
|
+
children: props.dirtyLabel
|
|
49
|
+
}) : null
|
|
50
|
+
]
|
|
51
|
+
}),
|
|
52
|
+
props.description === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
53
|
+
className: `${p}-sectionDesc`,
|
|
54
|
+
children: props.description
|
|
55
|
+
}),
|
|
56
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57
|
+
className: `${p}-sectionBody`,
|
|
58
|
+
children: [
|
|
59
|
+
props.readOnlyNotice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
60
|
+
className: `${p}-readOnly`,
|
|
61
|
+
role: "status",
|
|
62
|
+
children: props.readOnlyNotice
|
|
63
|
+
}) : null,
|
|
64
|
+
props.children,
|
|
65
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
66
|
+
className: `${p}-sectionFooter`,
|
|
67
|
+
children: [props.status !== void 0 && props.status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
68
|
+
className: `${p}-status${props.status.kind === "error" ? ` ${p}-statusError` : ""}`,
|
|
69
|
+
role: "status",
|
|
70
|
+
children: props.status.text
|
|
71
|
+
}) : null, props.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
72
|
+
prefix: p,
|
|
73
|
+
label: action.label,
|
|
74
|
+
variant: action.variant,
|
|
75
|
+
disabled: action.disabled,
|
|
76
|
+
onClick: action.onClick
|
|
77
|
+
}, action.key))]
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** 取值未到达时的占位:与 CardChrome 同形态,避免宿主页里闪一次卡片外壳。 */
|
|
85
|
+
function CardLoading(props) {
|
|
86
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
87
|
+
className: `${props.prefix}-section`,
|
|
88
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
89
|
+
className: `${props.prefix}-readOnly`,
|
|
90
|
+
role: "status",
|
|
91
|
+
children: props.text
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region ../shared/src/client/config-slots.ts
|
|
97
|
+
/** dsh-plus 聚合 bundle 的包名(各插件 row 均经其补丁层插入 profile)。 */
|
|
98
|
+
const DSH_PLUS_BUNDLE = "@dsh-plus/bundle-main";
|
|
99
|
+
/**
|
|
100
|
+
* 把一张配置卡片注册进插件页的两个配置槽位。
|
|
101
|
+
* 两个槽位都由 0.2.1-alpha.2 的插件页声明,任一缺席即报错——不做版本探测。
|
|
102
|
+
*/
|
|
103
|
+
function injectPluginConfigCard(slots, reg) {
|
|
104
|
+
const base = {
|
|
105
|
+
locale: reg.ns,
|
|
106
|
+
inject: reg.inject
|
|
107
|
+
};
|
|
108
|
+
slots.inject("plugins.row.config", () => slots.register({
|
|
109
|
+
name: "plugins.row.config",
|
|
110
|
+
key: `${DSH_PLUS_BUNDLE}#${reg.rowId}`,
|
|
111
|
+
...base
|
|
112
|
+
}, reg.component));
|
|
113
|
+
slots.inject("plugins.bundle.config", () => slots.register({
|
|
114
|
+
name: "plugins.bundle.config",
|
|
115
|
+
key: reg.pkg,
|
|
116
|
+
...base
|
|
117
|
+
}, reg.component));
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region ../shared/src/client/draft.ts
|
|
121
|
+
/**
|
|
122
|
+
* 单命名空间 staged 草稿(简单配置卡片共用):读 scope 快照、播种草稿、
|
|
123
|
+
* 脏判定、保存(revision fencing + 重新装载)与状态行。
|
|
124
|
+
*
|
|
125
|
+
* 语义与官方 staged 表单一致:Host 更新不覆盖在途编辑,只有保存才写回;
|
|
126
|
+
* 保存成功后按 Host 解析值重置草稿。`from`/`patch` 参与 effect 与 useMemo
|
|
127
|
+
* 依赖,必须是模块级稳定函数(不要内联箭头函数)。
|
|
128
|
+
* @module @dsh-plus/shared/client/draft
|
|
129
|
+
*/
|
|
130
|
+
function useNamespaceDraft(options) {
|
|
131
|
+
const { scope, api, from, patch, saveFailedLabel } = options;
|
|
132
|
+
const snapshot = (0, react.useSyncExternalStore)((listener) => scope.subscribe(listener), () => scope.getSnapshot());
|
|
133
|
+
const value = snapshot.value;
|
|
134
|
+
const [draft, setDraftState] = (0, react.useState)(null);
|
|
135
|
+
const [saving, setSaving] = (0, react.useState)(false);
|
|
136
|
+
const [status, setStatus] = (0, react.useState)(IDLE_STATUS);
|
|
137
|
+
(0, react.useEffect)(() => {
|
|
138
|
+
if (value === void 0 || draft !== null) return;
|
|
139
|
+
setDraftState(from(value));
|
|
140
|
+
}, [
|
|
141
|
+
value,
|
|
142
|
+
draft,
|
|
143
|
+
from
|
|
144
|
+
]);
|
|
145
|
+
const dirty = (0, react.useMemo)(() => value !== void 0 && draft !== null && JSON.stringify(patch(draft)) !== JSON.stringify(patch(from(value))), [
|
|
146
|
+
value,
|
|
147
|
+
draft,
|
|
148
|
+
from,
|
|
149
|
+
patch
|
|
150
|
+
]);
|
|
151
|
+
const setDraft = (next) => {
|
|
152
|
+
setDraftState(next);
|
|
153
|
+
setStatus(IDLE_STATUS);
|
|
154
|
+
};
|
|
155
|
+
const edit = (part) => {
|
|
156
|
+
if (draft === null) return;
|
|
157
|
+
setDraft({
|
|
158
|
+
...draft,
|
|
159
|
+
...part
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
const reset = () => {
|
|
163
|
+
if (value === void 0) return;
|
|
164
|
+
setDraft(from(value));
|
|
165
|
+
};
|
|
166
|
+
const save = () => {
|
|
167
|
+
if (draft === null) return;
|
|
168
|
+
setSaving(true);
|
|
169
|
+
const revision = scope.getSnapshot().revision;
|
|
170
|
+
api.update(patch(draft), revision).then(async () => {
|
|
171
|
+
await scope.load();
|
|
172
|
+
const next = scope.getSnapshot().value;
|
|
173
|
+
if (next !== void 0) setDraftState(from(next));
|
|
174
|
+
setStatus(IDLE_STATUS);
|
|
175
|
+
}).catch((error) => {
|
|
176
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
177
|
+
setStatus({
|
|
178
|
+
kind: "error",
|
|
179
|
+
text: `${saveFailedLabel}${message}`
|
|
180
|
+
});
|
|
181
|
+
}).finally(() => setSaving(false));
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
value,
|
|
185
|
+
draft,
|
|
186
|
+
dirty,
|
|
187
|
+
saving,
|
|
188
|
+
status,
|
|
189
|
+
disabled: !snapshot.writable,
|
|
190
|
+
setDraft,
|
|
191
|
+
edit,
|
|
192
|
+
reset,
|
|
193
|
+
save
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region ../shared/src/client/i18n.ts
|
|
198
|
+
/** 卡片通用文案(save/discard/unsaved/loading 等四插件共有键)。 */
|
|
199
|
+
const commonZh = {
|
|
200
|
+
save: "保存",
|
|
201
|
+
saving: "保存中…",
|
|
202
|
+
discard: "放弃",
|
|
203
|
+
unsaved: "未保存",
|
|
204
|
+
loading: "加载中…",
|
|
205
|
+
unavailable: "配置服务不可用。",
|
|
206
|
+
readOnly: "当前部署无 settings provider,配置为只读;请在 profile 的 cordis.patch.yml 行级配置。",
|
|
207
|
+
invalidNumber: "请输入正整数。",
|
|
208
|
+
collapse: "收起",
|
|
209
|
+
expand: "展开",
|
|
210
|
+
enabledOn: "已启用",
|
|
211
|
+
enabledOff: "未启用",
|
|
212
|
+
saveFailed: "保存失败,请检查填写内容。",
|
|
213
|
+
confirm: "确认",
|
|
214
|
+
cancel: "取消"
|
|
215
|
+
};
|
|
216
|
+
const commonEn = {
|
|
217
|
+
save: "Save",
|
|
218
|
+
saving: "Saving…",
|
|
219
|
+
discard: "Discard",
|
|
220
|
+
unsaved: "Unsaved",
|
|
221
|
+
loading: "Loading…",
|
|
222
|
+
unavailable: "Settings service unavailable.",
|
|
223
|
+
readOnly: "No settings provider in this deployment; config is read-only. Configure it at the profile row in cordis.patch.yml.",
|
|
224
|
+
invalidNumber: "Enter a positive integer.",
|
|
225
|
+
collapse: "Collapse",
|
|
226
|
+
expand: "Expand",
|
|
227
|
+
enabledOn: "On",
|
|
228
|
+
enabledOff: "Off",
|
|
229
|
+
saveFailed: "Save failed; check the fields.",
|
|
230
|
+
confirm: "Confirm",
|
|
231
|
+
cancel: "Cancel"
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* own 覆盖同键合并(返回新对象,入参不可变)。
|
|
235
|
+
* 返回类型保留两侧键集合,调用方据此派生 DictKey。
|
|
236
|
+
*/
|
|
237
|
+
function mergeDict(base, own) {
|
|
238
|
+
return {
|
|
239
|
+
...base,
|
|
240
|
+
...own
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region ../shared/src/client/scope.ts
|
|
245
|
+
/**
|
|
246
|
+
* remote.settings 直连实现的命名空间 scope(官方配置页 tab 同款模式)。
|
|
247
|
+
* 读:describe 取命名空间视图(脱敏 value/revision + 顶层 writable);
|
|
248
|
+
* 刷新:remote `settings/document-updated`(按命名空间过滤)与
|
|
249
|
+
* `connection/reset`;generation 防旧读覆盖新发布。任何页面 origin
|
|
250
|
+
* (loopback 或 tailnet 信任域名)下行为一致。
|
|
251
|
+
* effect 归属调用方 fiber(label 需含插件名以便诊断)。
|
|
252
|
+
*/
|
|
253
|
+
function createSettingsScope(c, ns, label) {
|
|
254
|
+
const remote = c.get("remote");
|
|
255
|
+
let state = {
|
|
256
|
+
status: "loading",
|
|
257
|
+
value: void 0,
|
|
258
|
+
revision: void 0,
|
|
259
|
+
writable: false
|
|
260
|
+
};
|
|
261
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
262
|
+
let generation = 0;
|
|
263
|
+
const publish = (next) => {
|
|
264
|
+
state = next;
|
|
265
|
+
for (const listener of [...listeners]) listener();
|
|
266
|
+
};
|
|
267
|
+
const load = async () => {
|
|
268
|
+
const gen = ++generation;
|
|
269
|
+
let view;
|
|
270
|
+
try {
|
|
271
|
+
const response = await remote.settings.describe();
|
|
272
|
+
if (!response.ok) return;
|
|
273
|
+
view = response.value;
|
|
274
|
+
} catch {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (gen !== generation) return;
|
|
278
|
+
const namespace = view.namespaces.find((candidate) => candidate.ns === ns);
|
|
279
|
+
if (namespace === void 0) {
|
|
280
|
+
publish({
|
|
281
|
+
status: "unavailable",
|
|
282
|
+
value: void 0,
|
|
283
|
+
revision: void 0,
|
|
284
|
+
writable: view.writable
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
publish({
|
|
289
|
+
status: "ready",
|
|
290
|
+
value: namespace.value,
|
|
291
|
+
revision: namespace.revision,
|
|
292
|
+
writable: view.writable
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
const refresh = (namespace) => {
|
|
296
|
+
if (namespace !== void 0 && namespace !== ns) return;
|
|
297
|
+
load();
|
|
298
|
+
};
|
|
299
|
+
const disposers = [remote.$on("settings/document-updated", refresh), c.on("connection/reset", () => {
|
|
300
|
+
load();
|
|
301
|
+
})];
|
|
302
|
+
c.effect(() => () => {
|
|
303
|
+
for (const dispose of disposers) dispose();
|
|
304
|
+
}, label);
|
|
305
|
+
load();
|
|
306
|
+
return {
|
|
307
|
+
getSnapshot: () => state,
|
|
308
|
+
subscribe: (listener) => {
|
|
309
|
+
listeners.add(listener);
|
|
310
|
+
return () => {
|
|
311
|
+
listeners.delete(listener);
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
load
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/** 以 remote.settings 构造命名空间绑定的 api 面。 */
|
|
318
|
+
function createNamespaceApi(remote, ns) {
|
|
319
|
+
return {
|
|
320
|
+
update: async (patch, expectedRevision) => {
|
|
321
|
+
const response = await remote.update(ns, patch, expectedRevision);
|
|
322
|
+
if (!response.ok) throw new Error(response.error.message ?? "settings update failed");
|
|
323
|
+
},
|
|
324
|
+
describe: async () => {
|
|
325
|
+
const response = await remote.describe();
|
|
326
|
+
if (!response.ok) throw new Error(response.error.message ?? "settings describe failed");
|
|
327
|
+
return response.value;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region ../shared/src/client/styles.ts
|
|
333
|
+
/**
|
|
334
|
+
* 配置分节样式工厂(各插件 client 的公共收编版)。
|
|
335
|
+
*
|
|
336
|
+
* 目标宿主 dsh 0.2.1-alpha.2:插件页把配置视图放进已有页面级内边距的容器里
|
|
337
|
+
* (`div.detailSections[data-plugin-config]` 或 `section.detailSection[data-plugin-config]`),
|
|
338
|
+
* 外层无边框——故这里输出的是**无边框分节**(小节标题 13px/600、字段 12px 0、
|
|
339
|
+
* 横向零内边距、页脚非 sticky),度量对齐官方设置页
|
|
340
|
+
* (`@deepseek-ai/dsh-client-ui-primitives` 的 settings-form)。
|
|
341
|
+
*
|
|
342
|
+
* 移动端与可达性(767px 断点 + pointer:coarse,对齐 ui-mobile-fit 约定):
|
|
343
|
+
* - 窄屏 input/textarea 16px(防 iOS 聚焦缩放)、可点目标 ≥44px;
|
|
344
|
+
* - 页脚按钮等宽换行、状态文字独占一行。
|
|
345
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载),不覆盖上游选择器。
|
|
346
|
+
* @module @dsh-plus/shared/client/styles
|
|
347
|
+
*/
|
|
348
|
+
/** 生成全套分节 CSS;prefix 为插件类名前缀(如 'dne'),extra 为插件特有规则。 */
|
|
349
|
+
function cardCss(prefix, extra = "") {
|
|
350
|
+
return `
|
|
351
|
+
.${prefix}-section{box-sizing:border-box;flex-direction:column;width:100%;min-width:0;padding:16px 0;display:flex}
|
|
352
|
+
.${prefix}-sectionHead{align-items:center;gap:8px;min-width:0;display:flex}
|
|
353
|
+
.${prefix}-sectionTitle{min-width:0;color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:13px;font-weight:600;line-height:1.5}
|
|
354
|
+
.${prefix}-sectionBadge{white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
355
|
+
.${prefix}-sectionDesc{color:var(--dsw-alias-label-tertiary);margin:4px 0 0;font-size:12px;line-height:1.5}
|
|
356
|
+
.${prefix}-sectionBody{flex-direction:column;min-width:0;display:flex}
|
|
357
|
+
.${prefix}-sectionFooter{align-items:center;gap:8px;padding-top:16px;display:flex;flex-wrap:wrap}
|
|
358
|
+
.${prefix}-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
359
|
+
.${prefix}-statusOn{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
360
|
+
.${prefix}-statusOff{color:var(--dsw-alias-label-tertiary)}
|
|
361
|
+
.${prefix}-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
362
|
+
.${prefix}-field+.${prefix}-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
363
|
+
.${prefix}-head{align-items:center;gap:8px;display:flex}
|
|
364
|
+
.${prefix}-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
365
|
+
.${prefix}-badge{white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
366
|
+
.${prefix}-badgeSet{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
367
|
+
.${prefix}-badgeUnset{color:var(--dsw-alias-label-tertiary)}
|
|
368
|
+
.${prefix}-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;transition:border-color .18s,background .18s}
|
|
369
|
+
.${prefix}-input:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-label-dimmed)}
|
|
370
|
+
.${prefix}-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
371
|
+
.${prefix}-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
372
|
+
.${prefix}-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
373
|
+
.${prefix}-select{appearance:none;background:var(--dsw-alias-bg-module-platform);border:none;cursor:pointer;background-image:linear-gradient(45deg,transparent 50%,var(--dsw-alias-label-secondary) 50%),linear-gradient(135deg,var(--dsw-alias-label-secondary) 50%,transparent 50%);background-position:calc(100% - 16px) 50%,calc(100% - 11px) 50%;background-size:5px 5px;background-repeat:no-repeat;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;height:34px;box-sizing:border-box;border-radius:8px;padding:0 30px 0 12px;transition:background-color .18s}
|
|
374
|
+
.${prefix}-select:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover)}
|
|
375
|
+
.${prefix}-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
376
|
+
.${prefix}-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
377
|
+
.${prefix}-checkRow{align-items:center;gap:10px;display:flex;padding:6px 0}
|
|
378
|
+
.${prefix}-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer;flex:1;min-width:0}
|
|
379
|
+
/* 自绘圆角扁平开关(官方 UI 无可见原生 checkbox,选择类全走胶囊/菜单) */
|
|
380
|
+
.${prefix}-checkRow input{appearance:none;-webkit-appearance:none;position:relative;box-sizing:border-box;background:var(--dsw-alias-bg-module-platform);border:none;border-radius:999px;width:34px;height:20px;flex:none;cursor:pointer;transition:background .18s;margin:0}
|
|
381
|
+
.${prefix}-checkRow input::after{content:"";position:absolute;top:2px;left:2px;background:var(--dsw-alias-label-primary);border-radius:999px;width:16px;height:16px;transition:left .18s cubic-bezier(.4,0,.2,1)}
|
|
382
|
+
.${prefix}-checkRow input:checked{background:var(--dsw-alias-brand-primary)}
|
|
383
|
+
.${prefix}-checkRow input:checked::after{left:16px;background:var(--dsw-alias-bg-base)}
|
|
384
|
+
.${prefix}-checkRow input:disabled{opacity:.4;cursor:default}
|
|
385
|
+
.${prefix}-checkRow input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
386
|
+
.${prefix}-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:12px 0 2px;margin:0}
|
|
387
|
+
.${prefix}-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
388
|
+
.${prefix}-warn{color:var(--dsw-alias-label-error);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
389
|
+
.${prefix}-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
390
|
+
.${prefix}-statusError{color:var(--dsw-alias-label-error)}
|
|
391
|
+
.${prefix}-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
|
|
392
|
+
.${prefix}-btnGhost{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
393
|
+
.${prefix}-btnGhost:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
394
|
+
.${prefix}-btnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
395
|
+
.${prefix}-btn:disabled{opacity:.4;cursor:default}
|
|
396
|
+
.${prefix}-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
397
|
+
@media (max-width:767px){
|
|
398
|
+
.${prefix}-input,.${prefix}-select{font-size:16px;height:40px}
|
|
399
|
+
.${prefix}-checkRow{min-height:44px;padding:8px 0}
|
|
400
|
+
.${prefix}-status{flex-basis:100%;flex:none}
|
|
401
|
+
.${prefix}-btn{min-height:44px;flex:1;min-width:96px}
|
|
402
|
+
}
|
|
403
|
+
@media (pointer:coarse){
|
|
404
|
+
.${prefix}-btn{min-height:44px}
|
|
405
|
+
.${prefix}-checkRow{min-height:44px}
|
|
406
|
+
.${prefix}-input,.${prefix}-select{height:40px}
|
|
407
|
+
}
|
|
408
|
+
${extra}
|
|
409
|
+
`;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 幂等注入样式标签(官方 data-plugin-css 约定);返回新标签,
|
|
413
|
+
* 已存在或环境无 document 时返回 null(调用方 effect 卸载用)。
|
|
414
|
+
*/
|
|
415
|
+
function injectCardStyle(pluginId, css) {
|
|
416
|
+
if (typeof document === "undefined") return null;
|
|
417
|
+
const tagId = `${pluginId}/card.css`;
|
|
418
|
+
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(tagId)}]`) !== null) return null;
|
|
419
|
+
const tag = document.createElement("style");
|
|
420
|
+
tag.dataset.plugin = pluginId;
|
|
421
|
+
tag.dataset.pluginCss = tagId;
|
|
422
|
+
tag.textContent = css;
|
|
423
|
+
document.head.appendChild(tag);
|
|
424
|
+
return tag;
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/ns.ts
|
|
428
|
+
/**
|
|
429
|
+
* settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
|
|
430
|
+
*
|
|
431
|
+
* 服务端以字面量参与 `Config` 的活动字段(dsh-settings 对命名空间做语法校验),
|
|
432
|
+
* 浏览器半把它作为配置卡片的注册键(插件页配置卡片挂在 row/bundle config 槽位上,
|
|
433
|
+
* 与命名空间解耦,见 shared/client/config-slots);同一字面量两处共用,防止漂移。
|
|
434
|
+
* @module @dsh-plus/agent-preset-rea/ns
|
|
435
|
+
*/
|
|
436
|
+
const SETTINGS_NS = "dsh-plus-agent-preset-rea";
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/client/draft.ts
|
|
439
|
+
/** 解析值 → 草稿(非字符串一律视为空串)。 */
|
|
440
|
+
function draftFromValue(value) {
|
|
441
|
+
return { personaPrefix: typeof value.personaPrefix === "string" ? value.personaPrefix : "" };
|
|
442
|
+
}
|
|
443
|
+
/** 草稿 → 提交形状(只回写本卡片负责的字段)。 */
|
|
444
|
+
function toPatch(draft) {
|
|
445
|
+
return { personaPrefix: draft.personaPrefix };
|
|
446
|
+
}
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/client/card.tsx
|
|
449
|
+
/**
|
|
450
|
+
* 「逆向工程」预设配置卡片:唯一字段是 persona 提示词。
|
|
451
|
+
*
|
|
452
|
+
* 经 injectPluginConfigCard 注册(插件页 plugins.row.config / plugins.bundle.config,
|
|
453
|
+
* view 分发 summary/page);外壳与草稿语义走 @dsh-plus/shared/client 套件
|
|
454
|
+
* (CardChrome + useNamespaceDraft:staged 草稿、脏判定、revision fencing、
|
|
455
|
+
* Host 更新不覆盖在途编辑)。
|
|
456
|
+
*
|
|
457
|
+
* 为什么只有这一个字段:`Config` 里只有 `personaPrefix` 标了 `.volatile()`
|
|
458
|
+
* (对用户的承诺=必须给编辑器);MCP 参数、超时、附加 skill 根等属部署方平面,
|
|
459
|
+
* 由 profile 的 cordis.patch.yml 行级配置覆盖,因此卡片底部只作说明。
|
|
460
|
+
* 保存后 loader 原位提交活动引用并广播 `loader/volatile-update`,宿主半据此换代
|
|
461
|
+
* 注册预设声明——新建会话即用新提示词,已存在会话保持原装配。
|
|
462
|
+
* @module agent-preset-rea/client/card
|
|
463
|
+
*/
|
|
464
|
+
/** 卡片动作:放弃 / 保存(仅脏且可写时可用)。 */
|
|
465
|
+
function cardActions(options) {
|
|
466
|
+
return [{
|
|
467
|
+
key: "discard",
|
|
468
|
+
label: options.t("discard"),
|
|
469
|
+
disabled: !options.dirty || options.saving,
|
|
470
|
+
onClick: options.onDiscard
|
|
471
|
+
}, {
|
|
472
|
+
key: "save",
|
|
473
|
+
label: options.t(options.saving ? "saving" : "save"),
|
|
474
|
+
variant: "primary",
|
|
475
|
+
disabled: !options.dirty || options.saving || options.disabled,
|
|
476
|
+
onClick: options.onSave
|
|
477
|
+
}];
|
|
478
|
+
}
|
|
479
|
+
function ReaCard(props) {
|
|
480
|
+
const { t, scope, api } = props;
|
|
481
|
+
const state = useNamespaceDraft({
|
|
482
|
+
scope,
|
|
483
|
+
api,
|
|
484
|
+
from: draftFromValue,
|
|
485
|
+
patch: toPatch,
|
|
486
|
+
saveFailedLabel: t("saveFailed")
|
|
487
|
+
});
|
|
488
|
+
if (props.view === "summary") return t("summaryLine");
|
|
489
|
+
const draft = state.draft;
|
|
490
|
+
if (state.value === void 0 || draft === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardLoading, {
|
|
491
|
+
prefix: "apr",
|
|
492
|
+
text: t("loading")
|
|
493
|
+
});
|
|
494
|
+
const disabled = state.disabled;
|
|
495
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardChrome, {
|
|
496
|
+
prefix: "apr",
|
|
497
|
+
title: t("title"),
|
|
498
|
+
description: t("description"),
|
|
499
|
+
dirty: state.dirty,
|
|
500
|
+
dirtyLabel: t("unsaved"),
|
|
501
|
+
readOnlyNotice: disabled ? t("readOnly") : void 0,
|
|
502
|
+
status: state.status,
|
|
503
|
+
actions: cardActions({
|
|
504
|
+
t,
|
|
505
|
+
dirty: state.dirty,
|
|
506
|
+
saving: state.saving,
|
|
507
|
+
disabled,
|
|
508
|
+
onDiscard: state.reset,
|
|
509
|
+
onSave: state.save
|
|
510
|
+
}),
|
|
511
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
512
|
+
className: "apr-field",
|
|
513
|
+
children: [
|
|
514
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
515
|
+
className: "apr-head",
|
|
516
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
517
|
+
className: "apr-label",
|
|
518
|
+
htmlFor: "apr-prompt",
|
|
519
|
+
children: t("prompt")
|
|
520
|
+
})
|
|
521
|
+
}),
|
|
522
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
523
|
+
id: "apr-prompt",
|
|
524
|
+
className: "apr-textarea",
|
|
525
|
+
rows: 10,
|
|
526
|
+
spellCheck: false,
|
|
527
|
+
placeholder: t("promptPlaceholder"),
|
|
528
|
+
value: draft.personaPrefix,
|
|
529
|
+
disabled,
|
|
530
|
+
onChange: (event) => state.edit({ personaPrefix: event.target.value })
|
|
531
|
+
}),
|
|
532
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
533
|
+
className: "apr-hint",
|
|
534
|
+
children: t("promptHint")
|
|
535
|
+
}),
|
|
536
|
+
draft.personaPrefix.trim() === "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
537
|
+
className: "apr-hint",
|
|
538
|
+
children: t("promptEmpty")
|
|
539
|
+
}) : null
|
|
540
|
+
]
|
|
541
|
+
})
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/client/i18n.ts
|
|
546
|
+
/**
|
|
547
|
+
* 配置卡片文案(zh/en)。经 ctx.locale.register 注册、bind 取用,与官方卡片同机制。
|
|
548
|
+
* 公共键(save/discard/unsaved 等)来自 shared 的 common 字典,本文件只维护业务键。
|
|
549
|
+
* @module @dsh-plus/agent-preset-rea/client/i18n
|
|
550
|
+
*/
|
|
551
|
+
const NS = "dsh-plus-agent-preset-rea";
|
|
552
|
+
const ownZh = {
|
|
553
|
+
title: "逆向工程预设",
|
|
554
|
+
description: "REA 提示词:留空即空 persona,工具指引与运行时上下文照常由官方装配注入。",
|
|
555
|
+
summaryLine: "编辑「逆向工程」预设的 persona 提示词",
|
|
556
|
+
prompt: "系统提示词(persona 前缀)",
|
|
557
|
+
promptHint: "写入 system-prompt 的 persona 前缀段,仅对「逆向工程」预设的新会话生效;留空 = 不注入 persona 段(官方工具指引、项目指令与运行时上下文不受影响)。保存后新建会话即用新值。",
|
|
558
|
+
promptPlaceholder: "例如:先做证据分级,再动手改二进制;每一步给出可复核的证据链……",
|
|
559
|
+
promptEmpty: "当前为空:该预设不注入 persona 段。",
|
|
560
|
+
saveFailed: "保存失败:"
|
|
561
|
+
};
|
|
562
|
+
const ownEn = {
|
|
563
|
+
title: "Reverse-engineering preset",
|
|
564
|
+
description: "REA prompt: leave it empty for a blank persona; official tool guidance and runtime context stay in place.",
|
|
565
|
+
summaryLine: "Edit the persona prompt of the “逆向工程” preset",
|
|
566
|
+
prompt: "System prompt (persona prefix)",
|
|
567
|
+
promptHint: "Written to the persona-prefix section of the system prompt for new sessions of this preset only. Empty means no persona section (official tool guidance, project instructions and runtime context are unaffected). New sessions pick up the saved value.",
|
|
568
|
+
promptPlaceholder: "e.g. Grade the evidence first, then patch the binary; every step needs a checkable chain of evidence…",
|
|
569
|
+
promptEmpty: "Currently empty: this preset injects no persona section.",
|
|
570
|
+
saveFailed: "Save failed: "
|
|
571
|
+
};
|
|
572
|
+
const zh = mergeDict(commonZh, ownZh);
|
|
573
|
+
const en = mergeDict(commonEn, ownEn);
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/client/styles.ts
|
|
576
|
+
/**
|
|
577
|
+
* 配置卡片样式:基础规则走 @dsh-plus/shared/client 的 cardCss('apr'),
|
|
578
|
+
* 本文件只补插件特有规则(提示词多行输入框)。
|
|
579
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载)。
|
|
580
|
+
* @module agent-preset-rea/client/styles
|
|
581
|
+
*/
|
|
582
|
+
const PLUGIN_ID = "@dsh-plus/agent-preset-rea";
|
|
583
|
+
const cardCssAll = cardCss("apr", `
|
|
584
|
+
.apr-textarea{box-sizing:border-box;width:100%;min-height:180px;resize:vertical;font:inherit;font-size:13px;line-height:1.6;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px;transition:border-color .18s,background .18s}
|
|
585
|
+
.apr-textarea:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-label-dimmed)}
|
|
586
|
+
.apr-textarea:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
587
|
+
.apr-textarea:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
588
|
+
.apr-textarea::placeholder{color:var(--dsw-alias-label-tertiary)}
|
|
589
|
+
@media (max-width:767px){
|
|
590
|
+
.apr-textarea{font-size:16px;min-height:140px}
|
|
591
|
+
}
|
|
592
|
+
`);
|
|
593
|
+
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
|
594
|
+
function injectStyle() {
|
|
595
|
+
return injectCardStyle(PLUGIN_ID, cardCssAll);
|
|
596
|
+
}
|
|
597
|
+
//#endregion
|
|
598
|
+
//#region src/client/client.ts
|
|
599
|
+
const name = "dsh-plus-agent-preset-rea";
|
|
600
|
+
/** 浏览器半需要的 cordis 服务 key(loader 据此注入;package.json 的 dsh.client.inject 管包加载顺序)。 */
|
|
601
|
+
const inject = [
|
|
602
|
+
"slots",
|
|
603
|
+
"locale",
|
|
604
|
+
"remote",
|
|
605
|
+
"remote.settings"
|
|
606
|
+
];
|
|
607
|
+
function apply(ctx) {
|
|
608
|
+
const c = ctx;
|
|
609
|
+
const tag = injectStyle();
|
|
610
|
+
c.effect(() => () => {
|
|
611
|
+
tag?.remove();
|
|
612
|
+
}, "agent-preset-rea: style");
|
|
613
|
+
c.effect(() => c.locale.register(NS, {
|
|
614
|
+
zh,
|
|
615
|
+
en
|
|
616
|
+
}), "agent-preset-rea: locale");
|
|
617
|
+
const scope = createSettingsScope(c, SETTINGS_NS, "agent-preset-rea: settings scope");
|
|
618
|
+
const api = createNamespaceApi(c.get("remote").settings, SETTINGS_NS);
|
|
619
|
+
injectPluginConfigCard(c.slots, {
|
|
620
|
+
ns: SETTINGS_NS,
|
|
621
|
+
rowId: "dsh-plus-agent-preset-rea",
|
|
622
|
+
pkg: PLUGIN_ID,
|
|
623
|
+
component: ReaCard,
|
|
624
|
+
inject: () => ({
|
|
625
|
+
t: c.locale.bind(NS),
|
|
626
|
+
scope,
|
|
627
|
+
api
|
|
628
|
+
})
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
//#endregion
|
|
632
|
+
exports.apply = apply;
|
|
633
|
+
exports.inject = inject;
|
|
634
|
+
exports.name = name;
|
|
635
|
+
return module.exports;
|
|
636
|
+
}
|
|
637
|
+
});
|