@dsh-plus/agent-preset-rea 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +363 -105
- package/lib/index.d.ts +127 -20
- package/lib/index.js +510 -196
- package/package.json +2 -2
- package/src/client/api.ts +7 -6
- package/src/client/card.tsx +8 -4
- package/src/client/client.ts +2 -2
- package/src/client/draft.ts +21 -2
- package/src/client/i18n.ts +42 -2
- package/src/client/install.tsx +157 -58
- package/src/client/toggles.tsx +57 -0
- package/src/config.ts +34 -5
- package/src/definition.ts +6 -6
- package/src/dto.ts +29 -3
- package/src/exec.ts +34 -12
- package/src/index.ts +21 -1
- package/src/manager.ts +14 -9
- package/src/reverse.ts +335 -0
- package/src/routes.ts +108 -66
- package/src/skill.ts +23 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/agent-preset-rea",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "dsh-plus service + UI plugin: 注册逆向工程 agent preset(id=rea)——官方 standard 全量行镜像 + 预设作用域的 REA MCP 客户端(mcp__rea__*)、随包 REA 官方 skill、提示词卡片与 REA/skill 的受管安装更新",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@dsh-plus/shared": "0.1.
|
|
35
|
+
"@dsh-plus/shared": "0.1.41"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.5-alpha.2",
|
package/src/client/api.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module agent-preset-rea/client/api
|
|
7
7
|
*/
|
|
8
8
|
import { getJson, postJson } from '@dsh-plus/shared/client'
|
|
9
|
-
import type { JobActionDto, JobDto, StatusDto } from '../dto.ts'
|
|
9
|
+
import type { JobActionDto, JobDto, JobKind, StatusDto } from '../dto.ts'
|
|
10
10
|
|
|
11
11
|
/** 载荷类型谓词(与 shared/client 的 fetch 同形;此处按本插件契约自持)。 */
|
|
12
12
|
type Guard<T> = (value: unknown) => value is T
|
|
@@ -34,6 +34,7 @@ function isStatus(value: unknown): value is StatusDto {
|
|
|
34
34
|
const install = body['install']
|
|
35
35
|
const skill = body['skill']
|
|
36
36
|
const mcp = body['mcp']
|
|
37
|
+
const reverse = body['reverse']
|
|
37
38
|
return (
|
|
38
39
|
typeof install === 'object' &&
|
|
39
40
|
install !== null &&
|
|
@@ -41,6 +42,8 @@ function isStatus(value: unknown): value is StatusDto {
|
|
|
41
42
|
skill !== null &&
|
|
42
43
|
typeof mcp === 'object' &&
|
|
43
44
|
mcp !== null &&
|
|
45
|
+
typeof reverse === 'object' &&
|
|
46
|
+
reverse !== null &&
|
|
44
47
|
typeof body['config'] === 'object' &&
|
|
45
48
|
body['config'] !== null
|
|
46
49
|
)
|
|
@@ -60,13 +63,11 @@ export async function fetchStatus(): Promise<StatusDto> {
|
|
|
60
63
|
|
|
61
64
|
/**
|
|
62
65
|
* 启动作业。
|
|
63
|
-
* @param kind - `install` = 安装/更新 REA 包;`skill` =
|
|
66
|
+
* @param kind - `install` = 安装/更新 REA 包;`skill` = 更新 REA skill 副本;
|
|
67
|
+
* `reverse` = 更新 reverse-skill(GitHub 源)。
|
|
64
68
|
* @param proxyPort - 草稿里当前填的代理端口(未保存也即时生效)。
|
|
65
69
|
*/
|
|
66
|
-
export async function startJob(
|
|
67
|
-
kind: 'install' | 'skill',
|
|
68
|
-
proxyPort: number,
|
|
69
|
-
): Promise<JobActionDto> {
|
|
70
|
+
export async function startJob(kind: JobKind, proxyPort: number): Promise<JobActionDto> {
|
|
70
71
|
return postJson<JobActionDto>(`${ROUTE_BASE}/${kind}`, { proxyPort }, isJobAction)
|
|
71
72
|
}
|
|
72
73
|
|
package/src/client/card.tsx
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* (CardChrome + useNamespaceDraft:staged 草稿、脏判定、revision fencing、
|
|
7
7
|
* Host 更新不覆盖在途编辑),安装维护区见 client/install.tsx。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* 五个活动字段(`Config` 里标 `.volatile()` 的)都在这里给出编辑器:
|
|
10
10
|
* - `personaPrefix`:system-prompt 的 persona 前缀段;
|
|
11
|
-
* - `proxyPort
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* - `proxyPort`:安装/更新时的本机代理端口;
|
|
12
|
+
* - `mcpEnabled` / `skillEnabled` / `reverseSkillEnabled`:三个能力开关(见 toggles.tsx)。
|
|
13
|
+
* 其余字段(受管目录、版本钉、skill 组、MCP 参数、超时、附加 skill 根)属部署方平面,
|
|
14
|
+
* 由 profile 的 cordis.patch.yml 行级配置覆盖,卡片只读展示关键事实。
|
|
14
15
|
*
|
|
15
16
|
* 安装维护为什么不放在注册期自动做:启动期 `npx`/npm 安装会跑依赖安装脚本
|
|
16
17
|
* (原生 prebuild 下载),卡住即吊死预设激活与整个名册;因此改成显式动作
|
|
@@ -33,6 +34,7 @@ import type { ReactElement } from 'react'
|
|
|
33
34
|
import { type ConfigValue, type Draft, draftFromValue, parseProxyPort, toPatch } from './draft.ts'
|
|
34
35
|
import type { Translate } from './i18n.ts'
|
|
35
36
|
import { InstallSection } from './install.tsx'
|
|
37
|
+
import { ToggleSection } from './toggles.tsx'
|
|
36
38
|
|
|
37
39
|
export interface CardProps extends PluginConfigViewProps {
|
|
38
40
|
t: Translate
|
|
@@ -139,6 +141,8 @@ export function ReaCard(props: CardProps): ReactElement | string | null {
|
|
|
139
141
|
onEdit={(text) => state.edit({ proxyPort: text })}
|
|
140
142
|
/>
|
|
141
143
|
|
|
144
|
+
<ToggleSection t={t} draft={draft} disabled={disabled} onEdit={state.edit} />
|
|
145
|
+
|
|
142
146
|
<InstallSection t={t} disabled={disabled} proxyPort={port} />
|
|
143
147
|
</CardChrome>
|
|
144
148
|
)
|
package/src/client/client.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* 构建产物为 window.__ModuleLoader__.load({id, factory}) 形式的 CJS factory
|
|
5
5
|
* (包装见 tsdown.config.ts);样式沿用官方 data-plugin-css 约定,HMR 据此卸载。
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* 卡片编辑五个活动字段(`personaPrefix`、`proxyPort` 与 mcp/skill/reverse 三个开关),
|
|
8
|
+
* 并驱动宿主半的受管安装/更新作业(同源自定义端点,见 client/api.ts);配置读写经共享层直连 `ctx.remote.settings`
|
|
9
9
|
* (官方 `settingsScope` 服务不存在、由 `configForms` 取代),与 notify-email 同机制。
|
|
10
10
|
* @module @dsh-plus/agent-preset-rea/client
|
|
11
11
|
*/
|
package/src/client/draft.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 卡片草稿的纯逻辑(无 JSX,node --test 可直接测):命名空间解析值 ⇄ 编辑草稿。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 卡片编辑五个活动字段(`personaPrefix` 提示词、`proxyPort` 安装代理端口、
|
|
5
|
+
* `mcpEnabled` / `skillEnabled` / `reverseSkillEnabled` 三个能力开关),其余字段
|
|
5
6
|
* 属部署方平面不进卡片;命名空间值是整行配置的脱敏视图,读取时按字段收窄:
|
|
6
|
-
* 值缺席或类型不符一律回落默认(提示词空串、代理 0)。
|
|
7
|
+
* 值缺席或类型不符一律回落默认(提示词空串、代理 0、开关 true)。
|
|
7
8
|
*
|
|
8
9
|
* 代理端口以文本承载:越界/非法不静默改写,标 `invalid` 并禁用保存(见 card.tsx),
|
|
9
10
|
* 避免「输入 99999 被悄悄存成 65535」这类静默数据损坏。
|
|
@@ -14,6 +15,9 @@
|
|
|
14
15
|
export interface ConfigValue {
|
|
15
16
|
personaPrefix?: unknown
|
|
16
17
|
proxyPort?: unknown
|
|
18
|
+
mcpEnabled?: unknown
|
|
19
|
+
skillEnabled?: unknown
|
|
20
|
+
reverseSkillEnabled?: unknown
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/** 编辑草稿。 */
|
|
@@ -21,6 +25,15 @@ export interface Draft {
|
|
|
21
25
|
personaPrefix: string
|
|
22
26
|
/** 代理端口文本(空串与 `'0'` 都表示不用代理)。 */
|
|
23
27
|
proxyPort: string
|
|
28
|
+
/** 三个能力开关(REA MCP 行 / REA skill / reverse-skill)。 */
|
|
29
|
+
mcpEnabled: boolean
|
|
30
|
+
skillEnabled: boolean
|
|
31
|
+
reverseSkillEnabled: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 布尔字段收窄(缺席或类型不符回落默认值)。 */
|
|
35
|
+
function boolOf(value: unknown, fallback: boolean): boolean {
|
|
36
|
+
return typeof value === 'boolean' ? value : fallback
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
/** 代理端口上限。 */
|
|
@@ -31,6 +44,9 @@ export function draftFromValue(value: ConfigValue): Draft {
|
|
|
31
44
|
return {
|
|
32
45
|
personaPrefix: typeof value.personaPrefix === 'string' ? value.personaPrefix : '',
|
|
33
46
|
proxyPort: typeof value.proxyPort === 'number' ? String(value.proxyPort) : '',
|
|
47
|
+
mcpEnabled: boolOf(value.mcpEnabled, true),
|
|
48
|
+
skillEnabled: boolOf(value.skillEnabled, true),
|
|
49
|
+
reverseSkillEnabled: boolOf(value.reverseSkillEnabled, true),
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
|
|
@@ -51,5 +67,8 @@ export function toPatch(draft: Draft): Record<string, unknown> {
|
|
|
51
67
|
return {
|
|
52
68
|
personaPrefix: draft.personaPrefix,
|
|
53
69
|
proxyPort: parseProxyPort(draft.proxyPort) ?? 0,
|
|
70
|
+
mcpEnabled: draft.mcpEnabled,
|
|
71
|
+
skillEnabled: draft.skillEnabled,
|
|
72
|
+
reverseSkillEnabled: draft.reverseSkillEnabled,
|
|
54
73
|
}
|
|
55
74
|
}
|
package/src/client/i18n.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const NS = 'dsh-plus-agent-preset-rea'
|
|
|
10
10
|
const ownZh = {
|
|
11
11
|
title: '逆向工程预设',
|
|
12
12
|
description: 'REA 提示词与安装维护:提示词留空即空 persona,工具指引与运行时上下文照常注入。',
|
|
13
|
-
summaryLine: '
|
|
13
|
+
summaryLine: '编辑「逆向工程」预设的提示词、开关 REA MCP 与两类 skill,并安装/更新',
|
|
14
14
|
prompt: '系统提示词(persona 前缀)',
|
|
15
15
|
promptHint:
|
|
16
16
|
'写入 system-prompt 的 persona 前缀段,仅对「逆向工程」预设的新会话生效;留空 = 不注入 persona 段(官方工具指引、项目指令与运行时上下文不受影响)。保存后新建会话即用新值。',
|
|
@@ -58,6 +58,26 @@ const ownZh = {
|
|
|
58
58
|
jobElapsed: '已用时',
|
|
59
59
|
jobLogEmpty: '(暂无输出)',
|
|
60
60
|
|
|
61
|
+
toggleSection: '能力开关',
|
|
62
|
+
toggleMcp: 'REA MCP 工具(mcp__rea__*)',
|
|
63
|
+
toggleMcpHint:
|
|
64
|
+
'关掉即从本预设摘掉 REA MCP 行,模型面看不到 mcp__rea__* 工具;REA 仍留在磁盘上,随时可开回来(保存后新建会话生效)。',
|
|
65
|
+
toggleSkill: 'REA 官方 skill',
|
|
66
|
+
toggleSkillHint:
|
|
67
|
+
'关掉即不挂 REA 官方 skill 根(reverse-engineer-anything),其余 skill 根不受影响。',
|
|
68
|
+
toggleReverse: 'reverse-skill 技能包',
|
|
69
|
+
toggleReverseHint:
|
|
70
|
+
'关掉即不挂 zhaoxuya520/reverse-skill 的技能组(默认挂主包根:路由技能 + 各模块技能);安装副本仍在,可从下面重新更新。',
|
|
71
|
+
enabled: '已挂载',
|
|
72
|
+
disabled: '已关闭',
|
|
73
|
+
reverseSource: 'reverse-skill',
|
|
74
|
+
reverseCommit: '提交',
|
|
75
|
+
reverseGroups: '技能组',
|
|
76
|
+
reverseGroupsHint: '树内可选组:',
|
|
77
|
+
reversePath: '技能包目录',
|
|
78
|
+
reverseAction: '更新 reverse-skill',
|
|
79
|
+
jobReverse: '更新 reverse-skill',
|
|
80
|
+
|
|
61
81
|
proxyPort: '安装代理端口',
|
|
62
82
|
proxyPortHint:
|
|
63
83
|
'安装/更新时供 npm 与原生依赖(node-pty)prebuild 下载使用的本机代理端口;0 或留空 = 直连。未保存也用于本次安装,保存后长期生效。',
|
|
@@ -68,7 +88,7 @@ const ownEn = {
|
|
|
68
88
|
title: 'Reverse-engineering preset',
|
|
69
89
|
description:
|
|
70
90
|
'REA prompt and installation: an empty prompt means a blank persona; tool guidance and runtime context stay in place.',
|
|
71
|
-
summaryLine: 'Edit the “逆向工程” prompt
|
|
91
|
+
summaryLine: 'Edit the “逆向工程” prompt, toggle REA MCP/skills, install and update them',
|
|
72
92
|
prompt: 'System prompt (persona prefix)',
|
|
73
93
|
promptHint:
|
|
74
94
|
'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.',
|
|
@@ -118,6 +138,26 @@ const ownEn = {
|
|
|
118
138
|
jobElapsed: 'Elapsed',
|
|
119
139
|
jobLogEmpty: '(no output yet)',
|
|
120
140
|
|
|
141
|
+
toggleSection: 'Capability switches',
|
|
142
|
+
toggleMcp: 'REA MCP tools (mcp__rea__*)',
|
|
143
|
+
toggleMcpHint:
|
|
144
|
+
'Turning this off drops the REA MCP row from this preset, so mcp__rea__* tools disappear; REA stays on disk and can be switched back on. New sessions pick up the change.',
|
|
145
|
+
toggleSkill: 'REA official skill',
|
|
146
|
+
toggleSkillHint:
|
|
147
|
+
'Turning this off unmounts the bundled REA skill root (reverse-engineer-anything); other skill roots are unaffected.',
|
|
148
|
+
toggleReverse: 'reverse-skill pack',
|
|
149
|
+
toggleReverseHint:
|
|
150
|
+
'Turning this off unmounts the skill groups of zhaoxuya520/reverse-skill (the main pack root is mounted by default: router skill plus module skills). The installed copy stays on disk and can be updated again below.',
|
|
151
|
+
enabled: 'Mounted',
|
|
152
|
+
disabled: 'Off',
|
|
153
|
+
reverseSource: 'reverse-skill',
|
|
154
|
+
reverseCommit: 'Commit',
|
|
155
|
+
reverseGroups: 'Skill groups',
|
|
156
|
+
reverseGroupsHint: 'Available in tree: ',
|
|
157
|
+
reversePath: 'Pack directory',
|
|
158
|
+
reverseAction: 'Update reverse-skill',
|
|
159
|
+
jobReverse: 'Update reverse-skill',
|
|
160
|
+
|
|
121
161
|
proxyPort: 'Install proxy port',
|
|
122
162
|
proxyPortHint:
|
|
123
163
|
'Local proxy port used by npm and by the native node-pty prebuild download during install/update; 0 or empty means direct. The unsaved value is used for the next install and applies permanently once saved.',
|
package/src/client/install.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 卡片的「REA 安装与更新」区:只读状态表 +
|
|
3
|
-
*
|
|
2
|
+
* 卡片的「REA 安装与更新」区:只读状态表 + 四个动作(安装/更新 REA、更新 REA skill、
|
|
3
|
+
* 更新 reverse-skill、取消作业)+ 运行中作业的阶段进度条、已用时与日志尾部。
|
|
4
4
|
*
|
|
5
5
|
* 状态与作业都来自宿主半的同源端点(见 client/api.ts 与宿主 routes.ts):这里只管
|
|
6
6
|
* 轮询与呈现。作业在跑时按 {@link PROGRESS_POLL_MS} 轮询——进度是宿主侧的真事实,
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* @module agent-preset-rea/client/install
|
|
9
9
|
*/
|
|
10
10
|
import { Button } from '@dsh-plus/shared/client'
|
|
11
|
-
import { type ReactElement, useCallback, useEffect, useRef, useState } from 'react'
|
|
11
|
+
import { type ReactElement, type ReactNode, useCallback, useEffect, useRef, useState } from 'react'
|
|
12
12
|
|
|
13
|
-
import type { JobDto, StatusDto } from '../dto.ts'
|
|
13
|
+
import type { JobDto, ReverseSkillDto, SkillDto, StatusDto } from '../dto.ts'
|
|
14
14
|
import { cancelJob, fetchStatus, startJob } from './api.ts'
|
|
15
15
|
import type { Translate } from './i18n.ts'
|
|
16
16
|
|
|
@@ -43,6 +43,14 @@ const STATE_CLASS = {
|
|
|
43
43
|
cancelled: 'Cancelled',
|
|
44
44
|
} as const
|
|
45
45
|
|
|
46
|
+
/** 作业种类 → 文案键。 */
|
|
47
|
+
const JOB_LABEL = { install: 'jobInstall', skill: 'jobSkill', reverse: 'jobReverse' } as const
|
|
48
|
+
|
|
49
|
+
/** 进度条百分比(防御性收窄,避免越界宽度)。 */
|
|
50
|
+
function widthOf(percent: number): string {
|
|
51
|
+
return `${Math.max(0, Math.min(100, percent))}%`
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
/** 作业进度块:状态徽标 + 阶段 + 进度条 + 日志尾部。 */
|
|
47
55
|
function JobPanel(props: { t: Translate; job: JobDto }): ReactElement {
|
|
48
56
|
const { t, job } = props
|
|
@@ -59,9 +67,7 @@ function JobPanel(props: { t: Translate; job: JobDto }): ReactElement {
|
|
|
59
67
|
<div className="apr-job">
|
|
60
68
|
<div className="apr-jobHead">
|
|
61
69
|
<span className={`apr-pill apr-pill${STATE_CLASS[job.state]}`}>{t(stateKey)}</span>
|
|
62
|
-
<span className="apr-jobTitle">
|
|
63
|
-
{t(job.kind === 'install' ? 'jobInstall' : 'jobSkill')}
|
|
64
|
-
</span>
|
|
70
|
+
<span className="apr-jobTitle">{t(JOB_LABEL[job.kind])}</span>
|
|
65
71
|
<span className="apr-hint">
|
|
66
72
|
{t('jobPhase')} {job.progress.step}/{job.progress.steps}·{job.progress.phase}
|
|
67
73
|
</span>
|
|
@@ -76,7 +82,7 @@ function JobPanel(props: { t: Translate; job: JobDto }): ReactElement {
|
|
|
76
82
|
aria-valuemax={100}
|
|
77
83
|
aria-valuenow={job.progress.percent}
|
|
78
84
|
>
|
|
79
|
-
<div className="apr-barFill" style={{ width:
|
|
85
|
+
<div className="apr-barFill" style={{ width: widthOf(job.progress.percent) }} />
|
|
80
86
|
</div>
|
|
81
87
|
{job.error === null ? null : <p className="apr-error">{job.error}</p>}
|
|
82
88
|
<details className="apr-logBox" open={job.state !== 'ok'}>
|
|
@@ -88,7 +94,7 @@ function JobPanel(props: { t: Translate; job: JobDto }): ReactElement {
|
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
/** 一行状态(键 + 值)。 */
|
|
91
|
-
function StatusRow(props: { label: string; children:
|
|
97
|
+
function StatusRow(props: { label: string; children: ReactNode }): ReactElement {
|
|
92
98
|
return (
|
|
93
99
|
<div className="apr-installRow">
|
|
94
100
|
<dt className="apr-installKey">{props.label}</dt>
|
|
@@ -97,10 +103,16 @@ function StatusRow(props: { label: string; children: React.ReactNode }): ReactEl
|
|
|
97
103
|
)
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
/**
|
|
101
|
-
function
|
|
106
|
+
/** 状态徽标(已就绪 / 未就绪 / 已关闭)。 */
|
|
107
|
+
function Pill(props: { ok: boolean; label: string }): ReactElement {
|
|
108
|
+
return (
|
|
109
|
+
<span className={`apr-pill ${props.ok ? 'apr-pillOk' : 'apr-pillMiss'}`}>{props.label}</span>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** REA 包与 MCP 行状态。 */
|
|
114
|
+
function InstallRows(props: { t: Translate; status: StatusDto }): ReactElement {
|
|
102
115
|
const { t, status } = props
|
|
103
|
-
const skillLabel = status.skill.source === 'updated' ? t('skillUpdated') : t('skillBundled')
|
|
104
116
|
const mcpLabel = !status.mcp.enabled
|
|
105
117
|
? status.config.mcpEnabled
|
|
106
118
|
? t('mcpMissing')
|
|
@@ -109,11 +121,12 @@ function StatusTable(props: { t: Translate; status: StatusDto }): ReactElement {
|
|
|
109
121
|
? t('mcpCustom')
|
|
110
122
|
: t('mcpInstalled')
|
|
111
123
|
return (
|
|
112
|
-
|
|
124
|
+
<>
|
|
113
125
|
<StatusRow label={t('installPackage')}>
|
|
114
|
-
<
|
|
115
|
-
{
|
|
116
|
-
|
|
126
|
+
<Pill
|
|
127
|
+
ok={status.install.installed}
|
|
128
|
+
label={t(status.install.installed ? 'installed' : 'notInstalled')}
|
|
129
|
+
/>
|
|
117
130
|
{status.install.version === null ? null : (
|
|
118
131
|
<span className="apr-mono">{status.install.version}</span>
|
|
119
132
|
)}
|
|
@@ -124,18 +137,6 @@ function StatusTable(props: { t: Translate; status: StatusDto }): ReactElement {
|
|
|
124
137
|
<StatusRow label={t('installPath')}>
|
|
125
138
|
<code className="apr-mono">{status.install.root}</code>
|
|
126
139
|
</StatusRow>
|
|
127
|
-
<StatusRow label={t('skillSource')}>
|
|
128
|
-
<span className="apr-mono">{skillLabel}</span>
|
|
129
|
-
<span className="apr-hint">
|
|
130
|
-
{t('skillFiles')} {status.skill.files}
|
|
131
|
-
{status.skill.version === null ? '' : ` · ${status.skill.version}`}
|
|
132
|
-
</span>
|
|
133
|
-
{status.skill.updatedAt === null ? null : (
|
|
134
|
-
<span className="apr-hint">
|
|
135
|
-
{t('updatedAt')} {status.skill.updatedAt}
|
|
136
|
-
</span>
|
|
137
|
-
)}
|
|
138
|
-
</StatusRow>
|
|
139
140
|
<StatusRow label={t('mcpRow')}>
|
|
140
141
|
<span className="apr-mono">{mcpLabel}</span>
|
|
141
142
|
{status.mcp.command === null ? null : (
|
|
@@ -144,10 +145,125 @@ function StatusTable(props: { t: Translate; status: StatusDto }): ReactElement {
|
|
|
144
145
|
</code>
|
|
145
146
|
)}
|
|
146
147
|
</StatusRow>
|
|
148
|
+
</>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** REA 官方 skill 状态。 */
|
|
153
|
+
function SkillRows(props: { t: Translate; skill: SkillDto; enabled: boolean }): ReactElement {
|
|
154
|
+
const { t, skill, enabled } = props
|
|
155
|
+
const version = skill.version === null ? '' : ` · ${skill.version}`
|
|
156
|
+
return (
|
|
157
|
+
<StatusRow label={t('skillSource')}>
|
|
158
|
+
<Pill ok={enabled} label={t(enabled ? 'enabled' : 'disabled')} />
|
|
159
|
+
<span className="apr-mono">
|
|
160
|
+
{t(skill.source === 'updated' ? 'skillUpdated' : 'skillBundled')}
|
|
161
|
+
</span>
|
|
162
|
+
<span className="apr-hint">
|
|
163
|
+
{t('skillFiles')} {skill.files}
|
|
164
|
+
{version}
|
|
165
|
+
</span>
|
|
166
|
+
{skill.updatedAt === null ? null : (
|
|
167
|
+
<span className="apr-hint">
|
|
168
|
+
{t('updatedAt')} {skill.updatedAt}
|
|
169
|
+
</span>
|
|
170
|
+
)}
|
|
171
|
+
</StatusRow>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** reverse-skill(GitHub 源)状态。 */
|
|
176
|
+
function ReverseRows(props: {
|
|
177
|
+
t: Translate
|
|
178
|
+
reverse: ReverseSkillDto
|
|
179
|
+
enabled: boolean
|
|
180
|
+
}): ReactElement {
|
|
181
|
+
const { t, reverse, enabled } = props
|
|
182
|
+
const active = reverse.activeGroups.length === 0 ? '—' : reverse.activeGroups.join(', ')
|
|
183
|
+
const all = reverse.groups.length === 0 ? '—' : reverse.groups.join(', ')
|
|
184
|
+
return (
|
|
185
|
+
<>
|
|
186
|
+
<StatusRow label={t('reverseSource')}>
|
|
187
|
+
<Pill ok={reverse.installed} label={t(reverse.installed ? 'installed' : 'notInstalled')} />
|
|
188
|
+
<span className="apr-mono">{reverse.repo}</span>
|
|
189
|
+
{reverse.sha === null ? null : (
|
|
190
|
+
<span className="apr-hint">
|
|
191
|
+
{t('reverseCommit')} {reverse.sha.slice(0, 12)}
|
|
192
|
+
{reverse.version === null ? '' : ` · v${reverse.version}`}
|
|
193
|
+
</span>
|
|
194
|
+
)}
|
|
195
|
+
</StatusRow>
|
|
196
|
+
<StatusRow label={t('reverseGroups')}>
|
|
197
|
+
<Pill ok={enabled} label={t(enabled ? 'enabled' : 'disabled')} />
|
|
198
|
+
<span className="apr-mono">{active}</span>
|
|
199
|
+
<span className="apr-hint">
|
|
200
|
+
{t('reverseGroupsHint')} {all}
|
|
201
|
+
</span>
|
|
202
|
+
</StatusRow>
|
|
203
|
+
<StatusRow label={t('reversePath')}>
|
|
204
|
+
<code className="apr-mono">{reverse.tree}</code>
|
|
205
|
+
{reverse.updatedAt === null ? null : (
|
|
206
|
+
<span className="apr-hint">
|
|
207
|
+
{t('updatedAt')} {reverse.updatedAt}
|
|
208
|
+
</span>
|
|
209
|
+
)}
|
|
210
|
+
</StatusRow>
|
|
211
|
+
</>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** 只读状态表:REA 包 / MCP 行 / REA skill / reverse-skill。 */
|
|
216
|
+
function StatusTable(props: { t: Translate; status: StatusDto }): ReactElement {
|
|
217
|
+
const { t, status } = props
|
|
218
|
+
return (
|
|
219
|
+
<dl className="apr-install">
|
|
220
|
+
<InstallRows t={t} status={status} />
|
|
221
|
+
<SkillRows t={t} skill={status.skill} enabled={status.config.skillEnabled} />
|
|
222
|
+
<ReverseRows t={t} reverse={status.reverse} enabled={status.config.reverseSkillEnabled} />
|
|
147
223
|
</dl>
|
|
148
224
|
)
|
|
149
225
|
}
|
|
150
226
|
|
|
227
|
+
/** 动作按钮行。 */
|
|
228
|
+
function ActionRow(props: {
|
|
229
|
+
t: Translate
|
|
230
|
+
busy: boolean
|
|
231
|
+
running: boolean
|
|
232
|
+
disabled: boolean
|
|
233
|
+
proxyPort: number | null
|
|
234
|
+
onAction(kind: 'install' | 'skill' | 'reverse'): void
|
|
235
|
+
onCancel(): void
|
|
236
|
+
}): ReactElement {
|
|
237
|
+
const { t, busy, running, disabled, proxyPort, onAction, onCancel } = props
|
|
238
|
+
const blocked = disabled || busy || running || proxyPort === null
|
|
239
|
+
return (
|
|
240
|
+
<div className="apr-btnRow">
|
|
241
|
+
<Button
|
|
242
|
+
prefix="apr"
|
|
243
|
+
variant="primary"
|
|
244
|
+
label={t('installAction')}
|
|
245
|
+
disabled={blocked}
|
|
246
|
+
onClick={() => onAction('install')}
|
|
247
|
+
/>
|
|
248
|
+
<Button
|
|
249
|
+
prefix="apr"
|
|
250
|
+
label={t('skillAction')}
|
|
251
|
+
disabled={blocked}
|
|
252
|
+
onClick={() => onAction('skill')}
|
|
253
|
+
/>
|
|
254
|
+
<Button
|
|
255
|
+
prefix="apr"
|
|
256
|
+
label={t('reverseAction')}
|
|
257
|
+
disabled={blocked}
|
|
258
|
+
onClick={() => onAction('reverse')}
|
|
259
|
+
/>
|
|
260
|
+
{running ? (
|
|
261
|
+
<Button prefix="apr" label={t('cancelAction')} disabled={busy} onClick={onCancel} />
|
|
262
|
+
) : null}
|
|
263
|
+
</div>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
151
267
|
export interface InstallSectionProps {
|
|
152
268
|
t: Translate
|
|
153
269
|
/** 设置只读(不可写)时禁用全部动作。 */
|
|
@@ -215,7 +331,6 @@ export function InstallSection(props: InstallSectionProps): ReactElement {
|
|
|
215
331
|
[load, t],
|
|
216
332
|
)
|
|
217
333
|
|
|
218
|
-
const blocked = disabled || busy || running === true
|
|
219
334
|
const job = status?.job ?? null
|
|
220
335
|
return (
|
|
221
336
|
<section className="apr-field">
|
|
@@ -235,35 +350,19 @@ export function InstallSection(props: InstallSectionProps): ReactElement {
|
|
|
235
350
|
) : (
|
|
236
351
|
<>
|
|
237
352
|
<StatusTable t={t} status={status} />
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
disabled={blocked || proxyPort === null}
|
|
252
|
-
onClick={() => {
|
|
253
|
-
runAction(() => startJob('skill', proxyPort ?? 0))
|
|
254
|
-
}}
|
|
255
|
-
/>
|
|
256
|
-
{running === true ? (
|
|
257
|
-
<Button
|
|
258
|
-
prefix="apr"
|
|
259
|
-
label={t('cancelAction')}
|
|
260
|
-
disabled={busy}
|
|
261
|
-
onClick={() => {
|
|
262
|
-
runAction(() => cancelJob())
|
|
263
|
-
}}
|
|
264
|
-
/>
|
|
265
|
-
) : null}
|
|
266
|
-
</div>
|
|
353
|
+
<ActionRow
|
|
354
|
+
t={t}
|
|
355
|
+
busy={busy}
|
|
356
|
+
running={running === true}
|
|
357
|
+
disabled={disabled}
|
|
358
|
+
proxyPort={proxyPort}
|
|
359
|
+
onAction={(kind) => {
|
|
360
|
+
runAction(() => startJob(kind, proxyPort ?? 0))
|
|
361
|
+
}}
|
|
362
|
+
onCancel={() => {
|
|
363
|
+
runAction(() => cancelJob())
|
|
364
|
+
}}
|
|
365
|
+
/>
|
|
267
366
|
</>
|
|
268
367
|
)}
|
|
269
368
|
{job === null ? null : <JobPanel t={t} job={job} />}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 卡片的「能力开关」区:三个一键开关(REA MCP 行 / REA 官方 skill / reverse-skill
|
|
3
|
+
* 技能包)。三者都是活动字段,保存后本预设换代注册——新建会话按新装配起(REA 工具
|
|
4
|
+
* 与 skill 根的有无),已存在会话保持既有装配。
|
|
5
|
+
*
|
|
6
|
+
* 开关只管「挂不挂」,不管「装没装」:关掉不会删磁盘上的安装副本(更新/回滚仍是
|
|
7
|
+
* 显式动作,见 install.tsx)。
|
|
8
|
+
* @module agent-preset-rea/client/toggles
|
|
9
|
+
*/
|
|
10
|
+
import { CheckRow } from '@dsh-plus/shared/client'
|
|
11
|
+
import type { ReactElement } from 'react'
|
|
12
|
+
|
|
13
|
+
import type { Draft } from './draft.ts'
|
|
14
|
+
import type { Translate } from './i18n.ts'
|
|
15
|
+
|
|
16
|
+
/** 三个开关的键与文案键。 */
|
|
17
|
+
const TOGGLES = [
|
|
18
|
+
{ field: 'mcpEnabled', id: 'apr-toggle-mcp', label: 'toggleMcp', hint: 'toggleMcpHint' },
|
|
19
|
+
{ field: 'skillEnabled', id: 'apr-toggle-skill', label: 'toggleSkill', hint: 'toggleSkillHint' },
|
|
20
|
+
{
|
|
21
|
+
field: 'reverseSkillEnabled',
|
|
22
|
+
id: 'apr-toggle-reverse',
|
|
23
|
+
label: 'toggleReverse',
|
|
24
|
+
hint: 'toggleReverseHint',
|
|
25
|
+
},
|
|
26
|
+
] as const
|
|
27
|
+
|
|
28
|
+
export interface ToggleSectionProps {
|
|
29
|
+
t: Translate
|
|
30
|
+
draft: Draft
|
|
31
|
+
disabled: boolean
|
|
32
|
+
onEdit(patch: Partial<Draft>): void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ToggleSection(props: ToggleSectionProps): ReactElement {
|
|
36
|
+
const { t, draft, disabled, onEdit } = props
|
|
37
|
+
return (
|
|
38
|
+
<section className="apr-field">
|
|
39
|
+
<div className="apr-head">
|
|
40
|
+
<span className="apr-label">{t('toggleSection')}</span>
|
|
41
|
+
</div>
|
|
42
|
+
{TOGGLES.map((toggle) => (
|
|
43
|
+
<div key={toggle.field}>
|
|
44
|
+
<CheckRow
|
|
45
|
+
prefix="apr"
|
|
46
|
+
id={toggle.id}
|
|
47
|
+
label={t(toggle.label)}
|
|
48
|
+
checked={draft[toggle.field]}
|
|
49
|
+
disabled={disabled}
|
|
50
|
+
onEdit={(checked) => onEdit({ [toggle.field]: checked })}
|
|
51
|
+
/>
|
|
52
|
+
<p className="apr-hint">{t(toggle.hint)}</p>
|
|
53
|
+
</div>
|
|
54
|
+
))}
|
|
55
|
+
</section>
|
|
56
|
+
)
|
|
57
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* 与 settings 用户层(GUI 配置卡片写入 profile 行级覆盖层,loader 原位提交活动
|
|
6
6
|
* 引用,消费端 `unwrapVolatile` 现取即热)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* **活动字段(`.volatile()`)共五个**,全部在配置卡片里有编辑器(见 src/client/card.tsx):
|
|
9
|
+
* `personaPrefix`(提示词)、`proxyPort`(安装代理)、`mcpEnabled` / `skillEnabled` /
|
|
10
|
+
* `reverseSkillEnabled`(三个能力开关)——保存即热更(预设换代,新建会话生效)。
|
|
11
|
+
* 其余字段是**部署方平面**(受管目录、版本钉、skill 组、超时等运维旋钮),由
|
|
12
|
+
* `cordis.patch.yml` 整体覆盖,GUI 不提供入口。
|
|
12
13
|
*
|
|
13
14
|
* **persona 默认为空串**:`dsh-persona` 的空模板照样遮蔽部署级 persona 段,
|
|
14
15
|
* 渲染为空后在提示词里消失;官方装配(harness 身份、工具指引、项目指令、
|
|
@@ -50,7 +51,35 @@ export const Config = z.object({
|
|
|
50
51
|
mcpEnabled: z
|
|
51
52
|
.boolean()
|
|
52
53
|
.description('是否挂 REA MCP 客户端行(false = 只保留预设其余能力)')
|
|
53
|
-
.default(true)
|
|
54
|
+
.default(true)
|
|
55
|
+
.volatile(),
|
|
56
|
+
skillEnabled: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.description('是否把 REA 官方 skill 根挂进本预设(false = 模型只看到其余 skill 根)')
|
|
59
|
+
.default(true)
|
|
60
|
+
.volatile(),
|
|
61
|
+
reverseSkillEnabled: z
|
|
62
|
+
.boolean()
|
|
63
|
+
.description(
|
|
64
|
+
'是否挂 zhaoxuya520/reverse-skill 技能包(false = 从本预设的 skill 根里摘掉,安装副本仍在)',
|
|
65
|
+
)
|
|
66
|
+
.default(true)
|
|
67
|
+
.volatile(),
|
|
68
|
+
reverseSkillRepo: z
|
|
69
|
+
.string()
|
|
70
|
+
.pattern(/^[\w.-]+\/[\w.-]+$/)
|
|
71
|
+
.description('reverse-skill 仓库(<owner>/<repo>;安装/手动更新都从这里拉)')
|
|
72
|
+
.default('zhaoxuya520/reverse-skill'),
|
|
73
|
+
reverseSkillRef: z
|
|
74
|
+
.string()
|
|
75
|
+
.description('reverse-skill 的 ref(分支/标签/提交;留空 = 仓库默认分支的最新提交)')
|
|
76
|
+
.default(''),
|
|
77
|
+
reverseSkillGroups: z
|
|
78
|
+
.array(z.string())
|
|
79
|
+
.description(
|
|
80
|
+
'reverse-skill 里挂进本预设的技能组(相对安装树根;默认只挂主包根 skills——含路由技能与全部模块技能,CTF 侧车等组按需追加)',
|
|
81
|
+
)
|
|
82
|
+
.default(['skills']),
|
|
54
83
|
mcpServerName: z
|
|
55
84
|
.string()
|
|
56
85
|
.pattern(/^[A-Za-z0-9_-]{1,32}$/)
|