@godv61/dsh-task-engine 0.22.3 → 0.22.5
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/.p0-test.mjs +57 -3
- package/README.md +18 -2
- package/docs/manual.html +7 -3
- package/lib/client.d.ts +6 -0
- package/lib/client.js +252 -24
- package/lib/client.js.map +3 -3
- package/lib/controller.d.ts +20 -0
- package/lib/controller.js +94 -3
- package/lib/controller.js.map +1 -1
- package/lib/dev-task.js +33 -12
- package/lib/dev-task.js.map +1 -1
- package/lib/index.d.ts +6 -0
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -1
- package/package.json +6 -2
package/.p0-test.mjs
CHANGED
|
@@ -33,6 +33,7 @@ async function assertThrows(fn, fragment, msg) {
|
|
|
33
33
|
|
|
34
34
|
function makeFs(initial = {}) {
|
|
35
35
|
const files = new Map(Object.entries(initial))
|
|
36
|
+
const versions = new Map()
|
|
36
37
|
return {
|
|
37
38
|
async resolve(relPath, opts) {
|
|
38
39
|
const base = opts && opts.cwd ? String(opts.cwd).replace(/\\/g, '/') : ''
|
|
@@ -42,8 +43,31 @@ function makeFs(initial = {}) {
|
|
|
42
43
|
async readText(target) {
|
|
43
44
|
return files.get(target.targetKey)
|
|
44
45
|
},
|
|
45
|
-
async
|
|
46
|
-
|
|
46
|
+
async lstat(path, opts) {
|
|
47
|
+
const base = opts && opts.cwd ? String(opts.cwd).replace(/\\/g, '/') : ''
|
|
48
|
+
const key = base ? `${base}/${path}` : path
|
|
49
|
+
if (!files.has(key)) return undefined
|
|
50
|
+
return { version: `v${versions.get(key) ?? 0}` }
|
|
51
|
+
},
|
|
52
|
+
async writeText(target, content, expected, signal, sandboxPolicy) {
|
|
53
|
+
const key = target.targetKey
|
|
54
|
+
if (expected !== undefined && expected !== null) {
|
|
55
|
+
if (expected.kind === 'createIfAbsent' && files.has(key)) {
|
|
56
|
+
const err = new Error('already exists')
|
|
57
|
+
err.code = 'FS_NOT_OBSERVED'
|
|
58
|
+
throw err
|
|
59
|
+
}
|
|
60
|
+
if (expected.kind === 'replaceIfVersion') {
|
|
61
|
+
const current = `v${versions.get(key) ?? 0}`
|
|
62
|
+
if (!files.has(key) || current !== expected.version) {
|
|
63
|
+
const err = new Error('stale version')
|
|
64
|
+
err.code = 'FS_STALE_VERSION'
|
|
65
|
+
throw err
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
files.set(key, content)
|
|
70
|
+
versions.set(key, (versions.get(key) ?? 0) + 1)
|
|
47
71
|
},
|
|
48
72
|
_files: files,
|
|
49
73
|
}
|
|
@@ -152,13 +176,25 @@ await assertThrows(
|
|
|
152
176
|
const exe = await registered(fs)
|
|
153
177
|
const prop10 = await exe({ operation: 'init', phase: 'propose', content: '# new' }, EXEC)
|
|
154
178
|
const hash10 = /content hash: ([0-9a-f]{64})/.exec(prop10)[1]
|
|
179
|
+
const existingHash10 = /existing hash: ([0-9a-f]{64})/.exec(prop10)[1]
|
|
180
|
+
assert(existingHash10 !== undefined, 'propose returns the existing-file hash when the file exists')
|
|
155
181
|
await assertThrows(
|
|
156
182
|
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10 }, EXEC),
|
|
183
|
+
'requires existing_hash',
|
|
184
|
+
'apply without existing_hash on an existing file is rejected',
|
|
185
|
+
)
|
|
186
|
+
await assertThrows(
|
|
187
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: '0'.repeat(64) }, EXEC),
|
|
188
|
+
'existing_hash mismatch',
|
|
189
|
+
'apply with a stale existing_hash is rejected (TOCTOU)',
|
|
190
|
+
)
|
|
191
|
+
await assertThrows(
|
|
192
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: existingHash10 }, EXEC),
|
|
157
193
|
'protected',
|
|
158
194
|
'apply without overwrite on an existing file is rejected',
|
|
159
195
|
)
|
|
160
196
|
await assertThrows(
|
|
161
|
-
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, overwrite: true }, EXEC),
|
|
197
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: existingHash10, overwrite: true }, EXEC),
|
|
162
198
|
'approval',
|
|
163
199
|
'overwrite requires an approval service',
|
|
164
200
|
)
|
|
@@ -667,6 +703,24 @@ function memProbe(files) {
|
|
|
667
703
|
ctl.registerWorkspace('D:/proj/one')
|
|
668
704
|
assert(ctl.checkedPath('D:/PROJ/one') === 'D:/PROJ/one', 'registered workspace passes under any case')
|
|
669
705
|
await assertThrows(() => ctl.checkedPath('D:/other-project'), 'not registered', 'unregistered workspace is rejected once the registry is active')
|
|
706
|
+
ctl.enableStrictWorkspaces()
|
|
707
|
+
await assertThrows(() => ctl.checkedPath('D:/still-unregistered'), 'not registered', 'strict mode keeps rejecting unregistered workspaces')
|
|
708
|
+
assert(ctl.checkedPath('D:/proj/one') === 'D:/proj/one', 'strict mode still admits registered workspaces')
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ── 36. 0.22.5: directory-bundle skill install Remote ──────────────────────
|
|
712
|
+
{
|
|
713
|
+
const controller = readFileSync('./lib/controller.js', 'utf8')
|
|
714
|
+
assert(controller.includes('installSkill'), 'installSkill Remote is registered')
|
|
715
|
+
assert(controller.includes('源目录必须是绝对路径'), 'install rejects a non-absolute source directory')
|
|
716
|
+
assert(controller.includes('SKILL.md'), 'install requires SKILL.md in the source directory')
|
|
717
|
+
assert(controller.includes('node_modules'), 'install skips dependency caches')
|
|
718
|
+
assert(controller.includes('内置技能,不可覆盖'), 'install refuses to shadow a bundled skill')
|
|
719
|
+
}
|
|
720
|
+
{
|
|
721
|
+
const client = readFileSync('./lib/client.js', 'utf8')
|
|
722
|
+
assert(client.includes('installSkill'), 'client declares the installSkill Remote contract')
|
|
723
|
+
assert(client.includes('sourceDir'), 'client schema carries the source directory')
|
|
670
724
|
}
|
|
671
725
|
|
|
672
726
|
console.log(`\nP0 acceptance: ${passed} checks passed`)
|
package/README.md
CHANGED
|
@@ -56,6 +56,20 @@ dsh plugin --profile <name> add github:<你的org>/dsh-task-engine
|
|
|
56
56
|
dsh plugin --profile <name> add .
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### host 集成 API(0.22.4,供 harness 层接入)
|
|
60
|
+
|
|
61
|
+
`./agent`(工具面)无需额外集成;工作台 Remote 的 workspace 边界可由宿主收紧:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { registerWorkspace, enableStrictWorkspaces } from '@godv61/dsh-task-engine'
|
|
65
|
+
// 每个会话工作区挂载时注册(未注册的路径被 Remote 拒绝):
|
|
66
|
+
registerWorkspace('/absolute/workspace/path')
|
|
67
|
+
// 生产模式:空 registry 不再回退放行(不调用则保持兼容的防误用校验):
|
|
68
|
+
enableStrictWorkspaces()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
根治性的 Typert 调用上下文(Remote 携带 session/workspace)与任务状态的 host 侧 HMAC 签名仍在 harness 层,本包已预留接入点并在「门禁边界」说明三层分工。
|
|
72
|
+
|
|
59
73
|
## 启用(点选即用)
|
|
60
74
|
|
|
61
75
|
装完 bundle、重启 web,预设列表里会**自动多出一个「工程化开发引擎」**——host 半在首次启动时把它对齐当前 `standard` 生成(换好工程人设 + 加好 agent 行),落在 `~/.dsh/.agent-presets/eng/`。
|
|
@@ -158,7 +172,7 @@ dsh plugin --profile <name> add .
|
|
|
158
172
|
|
|
159
173
|
## 状态与待办
|
|
160
174
|
|
|
161
|
-
-
|
|
175
|
+
- **当前验证基线(0.22.4)**:`npm test` 123 项验收全过(引擎状态机 / 守卫 / 快照 hash / 回执门 / 并发 CAS / Remote 路径 / 沙箱策略断言);`npm run typecheck` host + client 双面零错误;`npm run verify:package` 发布包黑盒 16 项(pack → 白名单 → 真实安装 → 主入口 import → client 注册 → 包内测试 → CLI);真实 git 临时仓库 e2e(合法放行 / 阶段·范围·快照篡改·敏感路径拦截);headless Chrome 实测侧栏入口与设置按钮逐像素对齐。发布前必跑 `npm test` + `npm run verify:package`。
|
|
162
176
|
- **真实模型端到端(0.4.1)**:headless + NewAPI DeepSeek 下让真模型走 `dev_task` 全流程,抓到并修掉一个真实 bug——`readText`/`writeText` 调 `fs.resolve()` 漏了 `await`,把 `Promise<FsTarget>` 当 `target` 传给了读写方法,导致 create/record/advance/commit 全部写不了任务文件、读永远返回 undefined。0.4.1 修复(`await fs.resolve(relPath)`),并把 `task_id` 在 create 也必填的说明补进工具 schema 与技能。修复后走真实 `ctx.fs` 路径冒烟全绿。
|
|
163
177
|
- **网页图形化配置界面(0.5.0)**:新增 `src/client/` 设置页「工程流程配置」+ Host 端 `task-engine` Remote 控制器;`@godv61/dsh-task-engine` 升级为双端包(`dsh.client` manifest + `./client` 出品,`exports` 暴露)。已实机验证:`dsh web` 起服务后 boot 数据里 `@godv61/dsh-task-engine` 以 `inject:["@deepseek-ai/dsh-api-gateway"]` 进入 application batch、`/plugins/…/client.js` 正常服务;headless 冒烟确认 boot + `dev_task` 未被新控制器破坏。浏览器点击级 e2e 留到发布后用真浏览器收尾。
|
|
164
178
|
- **skill/rule 挂载与渐进披露(0.6.0)**:`WorkflowConfig` 新增 `stage_bindings`(每阶段挂 skills/rules,可选字段),`dev_task` 的 `status`/`advance` 按当前阶段披露挂载的 skill 名 + rule 正文;内置 6 技能 + 3 规则库;Host 控制器新增 `listSkills`/`listRules`/`writeSkill`/`writeRule`;设置页新增「节点挂载」和「新建 skill / rule」两块,同时补上 `dev_task` 缺失的 `items` 操作(更新实施项状态)。引擎层 `stage_bindings` 校验(未知阶段/空名)与合并已单测通过。
|
|
@@ -186,4 +200,6 @@ dsh plugin --profile <name> add .
|
|
|
186
200
|
- **审计和发布质量(0.22.0)**:① 流程快照 hash——任务创建时固化 `config` 的 SHA-256,工具与提交钩子读任务记录时校验,被手改的快照一律拒绝继续;② 提交钩子完整性检测——新增 `dev_task verify_hook`,比对 `.git/hooks/commit-msg` 与内置门禁的 hash,被替换/篡改立即报错;③ 风险降级审批——新增 `set_risk`,`high_risk → standard` 必须人工批准并落 `risk_downgrades` 审计记录;④ 验证回执绑定项目根——receipt 记录 `root`,与任务 workspace 绑定;⑤ 内置规则指纹锁定——创建时固化内置规则内容指纹,包升级后 `status` 报 `bindings_drift` 而非静默换规则;⑥ 多项目 / 多语言 / 多任务测试矩阵补强。
|
|
187
201
|
- **沙箱写入修复(0.22.1)**:`dev_task` 的文件写入此前没有携带按调用传递的沙箱策略(`writeText` 的 `sandboxPolicy` 参数),在 DSH 文件沙箱下会把 workspace 内的任务记录写入误判为越界而拒绝(`file access denied under workspace-write mode`,且会话策略变化无法影响它);现在每次写入显式携带 `{ mode, workspaceRoot: 会话目录 }`,并新增 `sandbox_permissions` 参数(`workspace-write` / `danger-full-access`)作为被拒后的一次性升级路径。
|
|
188
202
|
- **质量修补(0.22.2,采纳 codex 评审五项)**:① client typecheck 修复——`project.ts` 不再依赖 `node:path`,浏览器面可完整类型检查;② sandbox 升级补审批——`sandbox_permissions` 必须与 `justification` 成对出现,`danger-full-access` 需人工一次批准,无审批服务即拒绝;③ Remote 任意路径设防——工作台 Remote 拒绝非绝对路径与系统级根目录;④ `set_risk` 升到 `high_risk` 时受流程能力门约束(`minimal`/`agile` 拒绝,不再绕过 `create` 的检查);⑤ `loadTask` 补齐旧记录缺失字段(items / verification / review / commits),损坏记录 fail-closed 而非引擎裸崩。
|
|
189
|
-
- **可信边界加固(0.22.3,采纳 GPT 评审)**:① 验证命令统一在任务记录的项目根运行(monorepo 子目录不再跑错目录),receipt.root 与任务根强校验;② 旧任务(无 frozen 快照)禁止升 `high_risk`——迁移或重建后才可;③ `init apply` 强制 `expected_hash`,新增 `existing_hash` 防审批期间文件被换(TOCTOU);④ 任务记录新增 `revision`,每次写入 compare-and-swap,并发覆盖直接报错(`changed concurrently`);⑤ 工作台 Remote 增加 host workspace registry(`registerWorkspace` 供 harness 集成,注册后未授权路径一律拒绝);⑥ sandbox 升级审批展示 workspace;⑦ 文档明确 hook(本地反馈)/ host(工具流约束)/ CI(最终可信门禁)三层职责边界。
|
|
203
|
+
- **可信边界加固(0.22.3,采纳 GPT 评审)**:① 验证命令统一在任务记录的项目根运行(monorepo 子目录不再跑错目录),receipt.root 与任务根强校验;② 旧任务(无 frozen 快照)禁止升 `high_risk`——迁移或重建后才可;③ `init apply` 强制 `expected_hash`,新增 `existing_hash` 防审批期间文件被换(TOCTOU);④ 任务记录新增 `revision`,每次写入 compare-and-swap,并发覆盖直接报错(`changed concurrently`);⑤ 工作台 Remote 增加 host workspace registry(`registerWorkspace` 供 harness 集成,注册后未授权路径一律拒绝);⑥ sandbox 升级审批展示 workspace;⑦ 文档明确 hook(本地反馈)/ host(工具流约束)/ CI(最终可信门禁)三层职责边界。
|
|
204
|
+
- **并发与发布闭环(0.22.4,采纳 REVIEW-0.22.3)**:① `init apply` 在目标文件存在时**强制** `existing_hash`(不再可选),propose→apply 之间文件被篡改直接 fail-closed;② 任务写入升级为真正的原子 CAS——逻辑 `revision` 检查 + dsh-fs 的 `lstat` 版本号 `replaceIfVersion` write intent,并发写入报 `FS_STALE_VERSION` 而非静默覆盖;③ workspace registry 增加 `enableStrictWorkspaces()` 生产模式(空 registry 不再回退放行);④ `exports["./client"]` 补 `types` 条件(`lib/client.d.ts` 随构建生成);⑤ 新增 `npm run verify:package` 发布包黑盒验证(pack → 白名单一致性 → 真实安装 → 主入口 import → client 注册 → 包内 123 checks → CLI 语法);⑥ 工作台 UI 优化——侧栏「工程流程」入口与设置按钮逐像素对齐(实测对齐 36 圆形 rail / 42 高 12 圆角展开行)、流程预设改卡片式选择器、工作台 header 品牌化、初始化预览容器化。REVIEW 指出的 Typert invocation context、host 可信状态签名、CI 最终门禁仍属 harness 层,集成点已在文档标注。
|
|
205
|
+
- **目录型 skill 安装(0.22.5)**:工作台「技能」页新增「安装 skill」——把本机已有的目录型 skill(`SKILL.md` + references / scripts / agents 等文件)一键装到<b>项目级</b>(工作区 `.dsh/skills`,团队共享)或<b>用户级</b>(`$DSH_HOME/skills`,个人所有项目可用);安装校验 SKILL.md frontmatter、拒绝覆盖内置同名、拒绝覆盖已装同名、自动排除 node_modules / .git / `__pycache__` 等缓存目录并限制 200 文件 / 20 MB;实机验证 `software-testing` 目录型 skill 从 UI 安装完整落盘(含 references + scripts)。
|
package/docs/manual.html
CHANGED
|
@@ -198,7 +198,7 @@ pnpm install && pnpm run build && pnpm dsh web</code></pre>
|
|
|
198
198
|
<li><strong>选流程预设</strong>:standard / agile / minimal 点一下即切。</li>
|
|
199
199
|
<li><strong>给节点挂 skill / rule</strong>:先选一个节点(如「开发」),再勾选它用什么 skill、守哪条 rule,保存。</li>
|
|
200
200
|
</ol>
|
|
201
|
-
<p
|
|
201
|
+
<p>「技能」页支持新建 / <b>安装</b> / 查看 / 编辑 / 删除:内置资源只读(正文以 markdown 富文本渲染);项目级 / 用户级资源可编辑、可删除(两步确认)。「安装 skill」把本机已有的<b>目录型 skill</b>(含 <code>SKILL.md</code>,可带 references / scripts 等文件)一键装到项目级(工作区 <code>.dsh/skills</code>,团队共享)或用户级(<code>$DSH_HOME/skills</code>,个人所有项目可用);自动排除 node_modules / <code>.git</code> / <code>__pycache__</code> 等缓存,同名内置或已装技能拒绝覆盖。</p>
|
|
202
202
|
<pre><code>// .dsh/eng.json 等价内容
|
|
203
203
|
{
|
|
204
204
|
"flow": "standard",
|
|
@@ -229,7 +229,7 @@ pnpm install && pnpm run build && pnpm dsh web</code></pre>
|
|
|
229
229
|
<ol>
|
|
230
230
|
<li><b>inspect</b>:对 AI 说「帮我初始化这个项目」;AI 读现有 <code>AGENTS.md</code>(若有)返回,没有则扫目录结构、技术栈、构建 / 运行命令、约定与红线。同时报告:项目根(自动从 workspace 向上发现)、语言栈(Node / Java / Python / Go / Rust)与默认验证命令、其它治理文件(<code>CLAUDE.md</code>、<code>.cursorrules</code>——init 只管理 AGENTS.md,不覆盖它们)、项目级 skill/rule 目录(<code>.dsh/rules/*.md</code>、<code>.dsh/skills/<name>/SKILL.md</code>)。</li>
|
|
231
231
|
<li><b>propose</b>:AI 写成 <code>AGENTS.md</code> 草稿,此步<b>只预览、不落盘</b>,同样硬校验<b>行数 ≤ 200 行</b>,并返回内容 hash。</li>
|
|
232
|
-
<li><b>apply</b
|
|
232
|
+
<li><b>apply</b>:落盘。<b>必须传回 propose 返回的 <code>expected_hash</code></b>(缺失或与内容不一致一律拒绝,防止预览与落盘之间内容被换);覆盖已有 <code>AGENTS.md</code> 时<b>还必须传回 <code>existing_hash</code></b>(证明审批期间现有文件未被他人改动)并带 <code>overwrite</code> 且<b>经人工批准</b>。写入目标必须在项目根内。</li>
|
|
233
233
|
</ol>
|
|
234
234
|
<pre><code>项目根/
|
|
235
235
|
├─ AGENTS.md ← init 生成,DSH 每会话自动注入
|
|
@@ -309,6 +309,7 @@ pnpm install && pnpm run build && pnpm dsh web</code></pre>
|
|
|
309
309
|
<li>无法联调外部系统时必须如实写「未联调」,不能把编译成功说成功能通过。</li>
|
|
310
310
|
<li><code>high_risk</code> 任务过验证门必须跑真实命令回执(引擎运行命令、取退出码,非自报通过)。</li>
|
|
311
311
|
<li><code>verify</code> 不带 <code>command</code> 时引擎自动选命令:<code>.dsh/eng.json</code> 的 <code>verify_command</code> 优先,其次按项目语言用默认(node → <code>npm test</code>、java → <code>mvn -q test</code>、python → <code>python -m pytest</code>、go → <code>go test ./...</code>、rust → <code>cargo test</code>);识别不出语言且未配置时保留自报路径。跑命令依赖宿主 shell 服务,未挂载会明确报错。</li>
|
|
312
|
+
<li><b>验证命令始终在任务记录的项目根运行</b>(任务创建时自动发现并固化 <code>root</code>),不是会话当前目录——monorepo 里会话停在子目录时,<code>npm test</code> 等也会在放清单文件的项目根执行;回执记录的 <code>root</code> 与任务根强校验,不一致直接拒绝。</li>
|
|
312
313
|
</ul>
|
|
313
314
|
<h3>评审</h3>
|
|
314
315
|
<p>开发阶段每项做「规格 + 质量」两阶段评审,都 pass 才标 done;缺评审或任一阶段 fail 会挡住「开发 → 交付」。</p>
|
|
@@ -370,8 +371,11 @@ risk_level: high_risk // 涉及鉴权,验证要加证据
|
|
|
370
371
|
<details><summary>任务文档要不要提交?</summary><div>要。<code>.dsh/task-*.json</code> 是团队共享、跨会话恢复的任务事实,随分支提交——引擎已豁免 <code>.dsh/task-*.json</code> 与 <code>.dsh/eng.json</code>,不受文件范围门拦截;忽略它别人只能看到代码,看不到确认内容和进度。</div></details>
|
|
371
372
|
<details><summary>一个会话能同时做两个需求吗?</summary><div>一个任务对应一份状态文档、一个分支。新需求保存当前进度后切新分支,切回原分支即可恢复。</div></details>
|
|
372
373
|
<details><summary>提交被拒是怎么回事?</summary><div>通常是四类:没建 dev_task 任务、阶段没到提交检查点、消息格式不符、提交文件不在任务范围内。按拒绝原因修正即可。</div></details>
|
|
374
|
+
<details><summary>git 钩子能防住所有绕过吗?</summary><div>不能,这是它的定位:<code>.git/hooks/commit-msg</code> 是<b>本地即时反馈</b>,拦常规 <code>git commit</code>(校验任务、阶段、范围、消息、快照 hash、敏感路径),但挡不住 <code>git commit --no-verify</code>、<code>core.hooksPath</code> 替换或直接手改 task 记录。<b>最终可信门禁在 CI</b>:CI 里重新校验 task id、快照 hash、staged 范围、提交消息、真实验证退出码、高风险回执,并检测钩子是否被绕过。三层分工:钩子 = 本地反馈,host 工具流 = 正常路径强约束,CI = 最终验证。</div></details>
|
|
375
|
+
<details><summary>任务记录被手改会怎样?</summary><div>引擎会拒绝:任务创建时把流程配置固化成 SHA-256 快照,工具和钩子每次读任务记录都校验——<b>被改过的快照一律拒绝继续</b>(提示修复或重建任务)。另外每次写入带 <code>revision</code> 版本号做 compare-and-swap,多个会话同时改同一任务会报「changed concurrently」而不是静默互相覆盖。</div></details>
|
|
376
|
+
<details><summary>dev_task 写文件被沙箱拒绝(file access denied)怎么办?</summary><div>沙箱按调用策略放行写入,偶发的越界误判可用一次性升级重试:同一操作加 <code>sandbox_permissions: "workspace-write"(或 "danger-full-access")</code> 并配 <code>justification</code>(一句话说明原因),升级需要<b>人工批准</b>;无审批服务时直接拒绝。日常任务记录写入默认已在会话工作区内放行,通常无需升级。</div></details>
|
|
373
377
|
<details><summary>「完成」等于上线了吗?</summary><div>不等于。<code>done</code> 只表示本地验证与必要评审通过;上线、合并、发布需人另行决定。</div></details>
|
|
374
|
-
<p class="stamp">当前结构基线:三套流程预设 · 工作台项目初始化(0.19.0)· 通用项目适配(0.21.0)· 审计与发布质量(0.22.0)· 最后更新:2026-09-
|
|
378
|
+
<p class="stamp">当前结构基线:三套流程预设 · 工作台项目初始化(0.19.0)· 通用项目适配(0.21.0)· 审计与发布质量(0.22.0)· 可信边界加固(0.22.3)· 并发与发布闭环(0.22.4)· 最后更新:2026-09-14</p>
|
|
375
379
|
</section>
|
|
376
380
|
</main>
|
|
377
381
|
</div>
|
package/lib/client.d.ts
ADDED
package/lib/client.js
CHANGED
|
@@ -4134,6 +4134,11 @@ var writeSkillRequestSchema = external_exports.object({
|
|
|
4134
4134
|
level: external_exports.enum(["project", "user"]),
|
|
4135
4135
|
path: external_exports.string().optional()
|
|
4136
4136
|
});
|
|
4137
|
+
var installSkillRequestSchema = external_exports.object({
|
|
4138
|
+
sourceDir: external_exports.string(),
|
|
4139
|
+
level: external_exports.enum(["project", "user"]),
|
|
4140
|
+
path: external_exports.string().optional()
|
|
4141
|
+
});
|
|
4137
4142
|
var writeRuleRequestSchema = external_exports.object({
|
|
4138
4143
|
name: external_exports.string(),
|
|
4139
4144
|
content: external_exports.string(),
|
|
@@ -4308,6 +4313,22 @@ var TYPERT_REMOTE = {
|
|
|
4308
4313
|
],
|
|
4309
4314
|
result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
|
|
4310
4315
|
},
|
|
4316
|
+
{
|
|
4317
|
+
id: `${PACKAGE}#task-engine/installSkill`,
|
|
4318
|
+
service: "taskEngineController",
|
|
4319
|
+
namespace: "task-engine",
|
|
4320
|
+
method: "installSkill",
|
|
4321
|
+
invocation: { kind: "direct" },
|
|
4322
|
+
parameters: [
|
|
4323
|
+
{
|
|
4324
|
+
name: "request",
|
|
4325
|
+
wire: "request",
|
|
4326
|
+
source: "json",
|
|
4327
|
+
codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#InstallSkillRequest`, schema: installSkillRequestSchema }
|
|
4328
|
+
}
|
|
4329
|
+
],
|
|
4330
|
+
result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
|
|
4331
|
+
},
|
|
4311
4332
|
{
|
|
4312
4333
|
id: `${PACKAGE}#task-engine/writeRule`,
|
|
4313
4334
|
service: "taskEngineController",
|
|
@@ -4469,6 +4490,7 @@ var surface = "var(--dsw-alias-bg-layer-2)";
|
|
|
4469
4490
|
var danger = "var(--dsw-alias-state-error-primary)";
|
|
4470
4491
|
var success = "var(--dsw-alias-state-success-primary)";
|
|
4471
4492
|
var warn = "var(--dsw-alias-state-warn-primary)";
|
|
4493
|
+
var brand = "var(--dsw-alias-state-business-primary)";
|
|
4472
4494
|
var styles = {
|
|
4473
4495
|
wrap: { display: "flex", flexDirection: "column", gap: 16, padding: 20, maxWidth: 900 },
|
|
4474
4496
|
head: { display: "flex", flexDirection: "column", gap: 4 },
|
|
@@ -4533,7 +4555,54 @@ var styles = {
|
|
|
4533
4555
|
noticeTitle: { color: warn, fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 },
|
|
4534
4556
|
okText: { color: success, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
|
|
4535
4557
|
status: { color: textTertiary, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
|
|
4536
|
-
actions: {
|
|
4558
|
+
actions: {
|
|
4559
|
+
display: "flex",
|
|
4560
|
+
alignItems: "center",
|
|
4561
|
+
gap: 12,
|
|
4562
|
+
marginTop: 4,
|
|
4563
|
+
paddingTop: 14,
|
|
4564
|
+
borderTop: `1px solid ${border}`
|
|
4565
|
+
},
|
|
4566
|
+
// ── flow preset picker ────────────────────────────────────────────────────
|
|
4567
|
+
flowGrid: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(230px, 1fr))", gap: 10 },
|
|
4568
|
+
flowOption: {
|
|
4569
|
+
display: "flex",
|
|
4570
|
+
flexDirection: "column",
|
|
4571
|
+
gap: 6,
|
|
4572
|
+
alignItems: "stretch",
|
|
4573
|
+
border: `1px solid ${border}`,
|
|
4574
|
+
borderRadius: 10,
|
|
4575
|
+
padding: "10px 12px",
|
|
4576
|
+
background: surface,
|
|
4577
|
+
cursor: "pointer",
|
|
4578
|
+
color: text,
|
|
4579
|
+
fontSize: 13,
|
|
4580
|
+
textAlign: "left",
|
|
4581
|
+
fontFamily: "inherit",
|
|
4582
|
+
transition: "border-color 120ms ease, box-shadow 120ms ease"
|
|
4583
|
+
},
|
|
4584
|
+
flowOptionActive: {
|
|
4585
|
+
borderColor: brand,
|
|
4586
|
+
boxShadow: `0 0 0 1px ${brand}`
|
|
4587
|
+
},
|
|
4588
|
+
flowOptionTitle: {
|
|
4589
|
+
display: "flex",
|
|
4590
|
+
alignItems: "center",
|
|
4591
|
+
gap: 7,
|
|
4592
|
+
fontSize: 13,
|
|
4593
|
+
fontWeight: 600,
|
|
4594
|
+
color: text
|
|
4595
|
+
},
|
|
4596
|
+
flowOptionDesc: { color: textTertiary, fontSize: 12, lineHeight: 1.45, margin: 0 },
|
|
4597
|
+
// ── init preview ──────────────────────────────────────────────────────────
|
|
4598
|
+
mdPreview: {
|
|
4599
|
+
border: `1px solid ${border}`,
|
|
4600
|
+
borderRadius: 8,
|
|
4601
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
4602
|
+
padding: "12px 14px",
|
|
4603
|
+
maxHeight: 420,
|
|
4604
|
+
overflowY: "auto"
|
|
4605
|
+
}
|
|
4537
4606
|
};
|
|
4538
4607
|
|
|
4539
4608
|
// src/client/TaskEngineSection.tsx
|
|
@@ -4722,8 +4791,8 @@ function deriveCapabilities(config) {
|
|
|
4722
4791
|
if (config.commit.file_scope) caps.push("file_scope");
|
|
4723
4792
|
return caps;
|
|
4724
4793
|
}
|
|
4725
|
-
function preset(id, version,
|
|
4726
|
-
return { id, version, label, description, capabilities: deriveCapabilities(config), config };
|
|
4794
|
+
function preset(id, version, label2, description, config) {
|
|
4795
|
+
return { id, version, label: label2, description, capabilities: deriveCapabilities(config), config };
|
|
4727
4796
|
}
|
|
4728
4797
|
var FLOW_PRESETS = {
|
|
4729
4798
|
standard: preset("standard", 1, "\u6807\u51C6\u7814\u53D1", "\u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838\uFF0C\u542B\u4EA7\u7269\u95E8 + \u786E\u8BA4\u95E8", STANDARD),
|
|
@@ -4873,9 +4942,28 @@ function TaskEngineSection(props) {
|
|
|
4873
4942
|
] }),
|
|
4874
4943
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
|
|
4875
4944
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6D41\u7A0B\u9884\u8BBE" }),
|
|
4876
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.
|
|
4877
|
-
|
|
4878
|
-
|
|
4945
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowGrid, children: FLOW_OPTIONS.map((option) => {
|
|
4946
|
+
const active = flow === option.id;
|
|
4947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
4948
|
+
"button",
|
|
4949
|
+
{
|
|
4950
|
+
type: "button",
|
|
4951
|
+
"aria-pressed": active,
|
|
4952
|
+
onClick: () => {
|
|
4953
|
+
selectFlow(option.id);
|
|
4954
|
+
},
|
|
4955
|
+
style: { ...styles.flowOption, ...active ? styles.flowOptionActive : {} },
|
|
4956
|
+
children: [
|
|
4957
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.flowOptionTitle, children: [
|
|
4958
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: active ? "done" : "ongoing", size: 8 }),
|
|
4959
|
+
option.label
|
|
4960
|
+
] }),
|
|
4961
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.flowOptionDesc, children: option.description })
|
|
4962
|
+
]
|
|
4963
|
+
},
|
|
4964
|
+
option.id
|
|
4965
|
+
);
|
|
4966
|
+
}) }),
|
|
4879
4967
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: FLOW_OPTIONS.find((option) => option.id === flow)?.description })
|
|
4880
4968
|
] }),
|
|
4881
4969
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u6D41\u7A0B\u8282\u70B9", hint: "\u8BE5\u6D41\u7A0B\u9884\u8BBE\u56FA\u5B9A\u7684\u9636\u6BB5\u987A\u5E8F\uFF1B\u6539\u53D8\u6D41\u7A0B\u8BF7\u5207\u6362\u4E0A\u9762\u7684\u9884\u8BBE\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: stages.map((stage) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: stage === (FLOW_PRESETS[flow]?.config.start_stage ?? ""), title: stage === FLOW_PRESETS[flow]?.config.start_stage ? "\u8D77\u59CB\u9636\u6BB5" : void 0, children: stage }, stage)) }) }),
|
|
@@ -5113,12 +5201,25 @@ function InitPanel({ workspace, remote }) {
|
|
|
5113
5201
|
{ style: styles.muted },
|
|
5114
5202
|
"\u9879\u76EE\u6839 AGENTS.md \u4F1A\u88AB DSH \u81EA\u52A8\u6CE8\u5165\u5230\u6BCF\u4E2A\u4F1A\u8BDD\u3002\u521D\u59CB\u5316\u4E00\u6B21\uFF0C\u4E4B\u540E\u6BCF\u6B21\u4EFB\u52A1\u5F00\u5DE5 AI \u90FD\u81EA\u5E26\u8FD9\u4EFD\u9879\u76EE\u8BA4\u77E5\uFF08\u9879\u76EE\u662F\u4EC0\u4E48 \u2192 \u600E\u4E48\u8DD1 \u2192 \u7ED3\u6784 \u2192 \u7EA6\u5B9A \u2192 \u5751\uFF09\u3002"
|
|
5115
5203
|
),
|
|
5116
|
-
exists && !editing && draft === null ? (0, import_react2.createElement)(
|
|
5204
|
+
exists && !editing && draft === null ? (0, import_react2.createElement)(
|
|
5205
|
+
"div",
|
|
5206
|
+
{ style: styles.mdPreview },
|
|
5207
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.MarkdownText, { text: view.content, labels: MD_LABELS })
|
|
5208
|
+
) : null,
|
|
5117
5209
|
draft !== null ? (0, import_react2.createElement)(
|
|
5118
5210
|
"div",
|
|
5119
5211
|
{ style: styles.section },
|
|
5120
|
-
(0, import_react2.createElement)(
|
|
5121
|
-
|
|
5212
|
+
(0, import_react2.createElement)(
|
|
5213
|
+
"div",
|
|
5214
|
+
{ style: styles.sourceRow },
|
|
5215
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.StateDot, { state: "ongoing", size: 8 }),
|
|
5216
|
+
(0, import_react2.createElement)("span", { style: styles.sourceBadge }, `AI \u8349\u7A3F \xB7 ${draft.lines} \u884C \xB7 \u5C1A\u672A\u5199\u5165`)
|
|
5217
|
+
),
|
|
5218
|
+
(0, import_react2.createElement)(
|
|
5219
|
+
"div",
|
|
5220
|
+
{ style: styles.mdPreview },
|
|
5221
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.MarkdownText, { text: draft.content, labels: MD_LABELS })
|
|
5222
|
+
)
|
|
5122
5223
|
) : null,
|
|
5123
5224
|
editing ? (0, import_react2.createElement)("textarea", {
|
|
5124
5225
|
style: { ...styles.textarea, minHeight: 300 },
|
|
@@ -5343,6 +5444,9 @@ function SkillManager({ workspace, remote }) {
|
|
|
5343
5444
|
const [whenToUse, setWhenToUse] = (0, import_react4.useState)("");
|
|
5344
5445
|
const [body2, setBody] = (0, import_react4.useState)("");
|
|
5345
5446
|
const [msg, setMsg] = (0, import_react4.useState)("");
|
|
5447
|
+
const [installing, setInstalling] = (0, import_react4.useState)(false);
|
|
5448
|
+
const [installDir, setInstallDir] = (0, import_react4.useState)("");
|
|
5449
|
+
const [installLevel, setInstallLevel] = (0, import_react4.useState)("project");
|
|
5346
5450
|
const refresh = (0, import_react4.useCallback)(() => {
|
|
5347
5451
|
void remote.listSkills(workspace).then((r) => {
|
|
5348
5452
|
if (r.ok) setSkills(r.value.skills);
|
|
@@ -5366,6 +5470,7 @@ function SkillManager({ workspace, remote }) {
|
|
|
5366
5470
|
setEditing(null);
|
|
5367
5471
|
setCreating(true);
|
|
5368
5472
|
setViewing(false);
|
|
5473
|
+
setInstalling(false);
|
|
5369
5474
|
setLevel("project");
|
|
5370
5475
|
setName("");
|
|
5371
5476
|
setDescription("");
|
|
@@ -5373,6 +5478,34 @@ function SkillManager({ workspace, remote }) {
|
|
|
5373
5478
|
setBody("");
|
|
5374
5479
|
setMsg("");
|
|
5375
5480
|
};
|
|
5481
|
+
const startInstall = () => {
|
|
5482
|
+
setEditing(null);
|
|
5483
|
+
setCreating(false);
|
|
5484
|
+
setViewing(false);
|
|
5485
|
+
setInstalling(true);
|
|
5486
|
+
setInstallDir("");
|
|
5487
|
+
setInstallLevel("project");
|
|
5488
|
+
setMsg("");
|
|
5489
|
+
};
|
|
5490
|
+
const submitInstall = async () => {
|
|
5491
|
+
setMsg("");
|
|
5492
|
+
const result = await remote.installSkill({
|
|
5493
|
+
sourceDir: installDir,
|
|
5494
|
+
level: installLevel,
|
|
5495
|
+
path: installLevel === "project" ? workspace : void 0
|
|
5496
|
+
});
|
|
5497
|
+
if (!result.ok) {
|
|
5498
|
+
setMsg("\u5B89\u88C5\u5931\u8D25\uFF1A" + describeError(result.error));
|
|
5499
|
+
return;
|
|
5500
|
+
}
|
|
5501
|
+
if (!result.value.ok) {
|
|
5502
|
+
setMsg("\u5B89\u88C5\u5931\u8D25\uFF1A" + (result.value.error ?? "\u672A\u77E5\u9519\u8BEF"));
|
|
5503
|
+
return;
|
|
5504
|
+
}
|
|
5505
|
+
setMsg(`\u5DF2\u5B89\u88C5 skill\u300C${result.value.name}\u300D\u2192 ${result.value.path}`);
|
|
5506
|
+
setInstalling(false);
|
|
5507
|
+
refresh();
|
|
5508
|
+
};
|
|
5376
5509
|
const loadSkill = (skillName, lvl) => {
|
|
5377
5510
|
setMsg("");
|
|
5378
5511
|
void remote.readSkill({ name: skillName, level: lvl, path: workspace }).then((r) => {
|
|
@@ -5444,7 +5577,7 @@ function SkillManager({ workspace, remote }) {
|
|
|
5444
5577
|
setConfirming(null);
|
|
5445
5578
|
refresh();
|
|
5446
5579
|
};
|
|
5447
|
-
const formOpen = editing !== null || creating;
|
|
5580
|
+
const formOpen = editing !== null || creating || installing;
|
|
5448
5581
|
const renderRow = (sk) => (0, import_react4.createElement)(
|
|
5449
5582
|
"div",
|
|
5450
5583
|
{
|
|
@@ -5534,6 +5667,53 @@ function SkillManager({ workspace, remote }) {
|
|
|
5534
5667
|
msg !== "" ? (0, import_react4.createElement)("p", { style: styles.status }, msg) : null
|
|
5535
5668
|
);
|
|
5536
5669
|
};
|
|
5670
|
+
const renderInstallForm = () => (0, import_react4.createElement)(
|
|
5671
|
+
ResourceModal,
|
|
5672
|
+
{
|
|
5673
|
+
title: "\u5B89\u88C5\u73B0\u6709 skill\uFF08\u76EE\u5F55\u578B\uFF09",
|
|
5674
|
+
description: "\u628A\u4E00\u4E2A\u672C\u673A\u5DF2\u6709\u7684 skill \u76EE\u5F55\uFF08\u542B SKILL.md\uFF0C\u53EF\u5E26 references / scripts \u7B49\u6587\u4EF6\uFF09\u5B89\u88C5\u5230\u9879\u76EE\u7EA7\u6216\u7528\u6237\u7EA7\u3002",
|
|
5675
|
+
onClose: () => {
|
|
5676
|
+
setInstalling(false);
|
|
5677
|
+
setMsg("");
|
|
5678
|
+
},
|
|
5679
|
+
footer: (0, import_react4.createElement)(
|
|
5680
|
+
"div",
|
|
5681
|
+
{ style: styles.row },
|
|
5682
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, {
|
|
5683
|
+
variant: "primary",
|
|
5684
|
+
size: "sm",
|
|
5685
|
+
disabled: installDir.trim() === "",
|
|
5686
|
+
onClick: () => {
|
|
5687
|
+
void submitInstall();
|
|
5688
|
+
}
|
|
5689
|
+
}, "\u5B89\u88C5"),
|
|
5690
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
5691
|
+
setInstalling(false);
|
|
5692
|
+
setMsg("");
|
|
5693
|
+
} }, "\u53D6\u6D88")
|
|
5694
|
+
)
|
|
5695
|
+
},
|
|
5696
|
+
(0, import_react4.createElement)(
|
|
5697
|
+
"div",
|
|
5698
|
+
{ style: styles.chips },
|
|
5699
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Pill, { active: installLevel === "project", onClick: () => {
|
|
5700
|
+
setInstallLevel("project");
|
|
5701
|
+
}, title: "\u88C5\u5230\u5DE5\u4F5C\u533A .dsh/skills\uFF0C\u56E2\u961F\u5171\u4EAB" }, "\u9879\u76EE\u7EA7"),
|
|
5702
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Pill, { active: installLevel === "user", onClick: () => {
|
|
5703
|
+
setInstallLevel("user");
|
|
5704
|
+
}, title: "\u88C5\u5230 $DSH_HOME/skills\uFF0C\u4E2A\u4EBA\u6240\u6709\u9879\u76EE\u53EF\u7528" }, "\u7528\u6237\u7EA7")
|
|
5705
|
+
),
|
|
5706
|
+
(0, import_react4.createElement)("input", {
|
|
5707
|
+
style: control,
|
|
5708
|
+
placeholder: "\u6E90\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u5982 D:\\yourong\\software-testing\uFF09",
|
|
5709
|
+
value: installDir,
|
|
5710
|
+
onChange: (ev) => {
|
|
5711
|
+
setInstallDir(ev.target.value);
|
|
5712
|
+
}
|
|
5713
|
+
}),
|
|
5714
|
+
(0, import_react4.createElement)("p", { style: styles.hint }, "\u8981\u6C42\uFF1A\u76EE\u5F55\u5185\u6709 SKILL.md\uFF08frontmatter \u542B name + description\uFF09\u3002\u5B89\u88C5\u4F1A\u590D\u5236\u6574\u4E2A\u76EE\u5F55\uFF08\u81EA\u52A8\u6392\u9664 node_modules / .git \u7B49\u7F13\u5B58\uFF09\uFF0C\u540C\u540D\u5185\u7F6E\u6280\u80FD\u62D2\u7EDD\u8986\u76D6\uFF0C\u540C\u540D\u5DF2\u88C5\u6280\u80FD\u9700\u5148\u5220\u9664\u3002"),
|
|
5715
|
+
msg !== "" ? (0, import_react4.createElement)("p", { style: styles.status }, msg) : null
|
|
5716
|
+
);
|
|
5537
5717
|
return (0, import_react4.createElement)(
|
|
5538
5718
|
"div",
|
|
5539
5719
|
{ style: styles.section },
|
|
@@ -5544,15 +5724,21 @@ function SkillManager({ workspace, remote }) {
|
|
|
5544
5724
|
"div",
|
|
5545
5725
|
{ style: styles.bindingStage },
|
|
5546
5726
|
(0, import_react4.createElement)("span", { style: styles.bindingLabel }, "\u5DF2\u5B89\u88C5\u7684 skill"),
|
|
5547
|
-
skills.length === 0 ? (0, import_react4.createElement)("p", { style: styles.muted }, "\u6682\u65E0 skill\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA skill\u300D\u521B\u5EFA\
|
|
5727
|
+
skills.length === 0 ? (0, import_react4.createElement)("p", { style: styles.muted }, "\u6682\u65E0 skill\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA skill\u300D\u521B\u5EFA\uFF0C\u6216\u300C\u5B89\u88C5 skill\u300D\u5BFC\u5165\u672C\u673A\u5DF2\u6709\u7684 skill \u76EE\u5F55\u3002") : (0, import_react4.createElement)(
|
|
5548
5728
|
"div",
|
|
5549
5729
|
{ style: styles.section },
|
|
5550
5730
|
...skills.map(renderRow)
|
|
5551
5731
|
)
|
|
5552
5732
|
),
|
|
5553
|
-
(0, import_react4.createElement)(
|
|
5733
|
+
(0, import_react4.createElement)(
|
|
5734
|
+
"div",
|
|
5735
|
+
{ style: styles.row },
|
|
5736
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "md", onClick: startCreate }, "\u65B0\u5EFA skill"),
|
|
5737
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "md", onClick: startInstall }, "\u5B89\u88C5 skill")
|
|
5738
|
+
)
|
|
5554
5739
|
),
|
|
5555
|
-
|
|
5740
|
+
installing ? renderInstallForm() : null,
|
|
5741
|
+
formOpen && !installing ? renderForm() : null
|
|
5556
5742
|
);
|
|
5557
5743
|
}
|
|
5558
5744
|
|
|
@@ -5946,6 +6132,10 @@ var header = {
|
|
|
5946
6132
|
padding: "12px 20px",
|
|
5947
6133
|
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
5948
6134
|
};
|
|
6135
|
+
var headerBrand = { display: "flex", alignItems: "center", gap: 10, minWidth: 0 };
|
|
6136
|
+
var headerText = { display: "flex", flexDirection: "column", gap: 1, minWidth: 0 };
|
|
6137
|
+
var headerTitle = { fontSize: 15, fontWeight: 600, color: "var(--dsw-alias-label-primary)" };
|
|
6138
|
+
var headerSub = { fontSize: 12, color: "var(--dsw-alias-label-tertiary)", margin: 0 };
|
|
5949
6139
|
var tabbar = {
|
|
5950
6140
|
display: "flex",
|
|
5951
6141
|
alignItems: "center",
|
|
@@ -5999,12 +6189,33 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
|
|
|
5999
6189
|
(0, import_react7.createElement)(
|
|
6000
6190
|
"div",
|
|
6001
6191
|
{ style: header },
|
|
6002
|
-
(0, import_react7.createElement)(
|
|
6192
|
+
(0, import_react7.createElement)(
|
|
6193
|
+
"div",
|
|
6194
|
+
{ style: headerBrand },
|
|
6195
|
+
(0, import_react7.createElement)(import_dsh_client_ui_primitives7.IconSettingsOutline16, { size: 18 }),
|
|
6196
|
+
(0, import_react7.createElement)(
|
|
6197
|
+
"div",
|
|
6198
|
+
{ style: headerText },
|
|
6199
|
+
(0, import_react7.createElement)("span", { style: headerTitle }, "\u5DE5\u7A0B\u6D41\u7A0B"),
|
|
6200
|
+
(0, import_react7.createElement)("p", { style: headerSub }, "\u5DE5\u7A0B\u5316\u4EA4\u4ED8\u5DE5\u4F5C\u53F0 \xB7 \u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838")
|
|
6201
|
+
)
|
|
6202
|
+
),
|
|
6003
6203
|
(0, import_react7.createElement)(
|
|
6004
6204
|
"button",
|
|
6005
|
-
{
|
|
6006
|
-
|
|
6007
|
-
|
|
6205
|
+
{
|
|
6206
|
+
type: "button",
|
|
6207
|
+
title: "\u5173\u95ED",
|
|
6208
|
+
onClick: () => {
|
|
6209
|
+
actions.close();
|
|
6210
|
+
},
|
|
6211
|
+
style: closeStyle,
|
|
6212
|
+
onMouseEnter: (event) => {
|
|
6213
|
+
event.currentTarget.style.background = "var(--dsw-alias-bg-layer-2)";
|
|
6214
|
+
},
|
|
6215
|
+
onMouseLeave: (event) => {
|
|
6216
|
+
event.currentTarget.style.background = "transparent";
|
|
6217
|
+
}
|
|
6218
|
+
},
|
|
6008
6219
|
(0, import_react7.createElement)(import_dsh_client_ui_primitives7.IconCloseOutline16, { size: 16 })
|
|
6009
6220
|
)
|
|
6010
6221
|
),
|
|
@@ -6033,6 +6244,8 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
|
|
|
6033
6244
|
// src/client/TriggerButton.tsx
|
|
6034
6245
|
var import_react8 = require("react");
|
|
6035
6246
|
var import_dsh_client_ui_primitives8 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
6247
|
+
var hover = "var(--dsw-alias-bg-layer-2)";
|
|
6248
|
+
var label = "var(--dsw-alias-label-primary)";
|
|
6036
6249
|
function TriggerButton({ wide, actions }) {
|
|
6037
6250
|
return (0, import_react8.createElement)(
|
|
6038
6251
|
"button",
|
|
@@ -6043,24 +6256,39 @@ function TriggerButton({ wide, actions }) {
|
|
|
6043
6256
|
onClick: () => {
|
|
6044
6257
|
actions.open();
|
|
6045
6258
|
},
|
|
6259
|
+
onMouseEnter: (event) => {
|
|
6260
|
+
event.currentTarget.style.background = hover;
|
|
6261
|
+
},
|
|
6262
|
+
onMouseLeave: (event) => {
|
|
6263
|
+
event.currentTarget.style.background = "transparent";
|
|
6264
|
+
},
|
|
6046
6265
|
style: {
|
|
6047
6266
|
display: "flex",
|
|
6048
6267
|
alignItems: "center",
|
|
6049
6268
|
justifyContent: wide ? "flex-start" : "center",
|
|
6050
6269
|
gap: wide ? 8 : 0,
|
|
6051
|
-
width: "100%",
|
|
6052
|
-
|
|
6270
|
+
width: wide ? "100%" : 36,
|
|
6271
|
+
minWidth: wide ? 0 : 36,
|
|
6272
|
+
height: wide ? 42 : 36,
|
|
6273
|
+
flex: wide ? "1 1 auto" : "none",
|
|
6053
6274
|
border: "none",
|
|
6054
6275
|
background: "transparent",
|
|
6055
6276
|
cursor: "pointer",
|
|
6056
|
-
color:
|
|
6057
|
-
borderRadius:
|
|
6058
|
-
padding: wide ? "0 10px" : 0,
|
|
6059
|
-
|
|
6277
|
+
color: label,
|
|
6278
|
+
borderRadius: wide ? 12 : "50%",
|
|
6279
|
+
padding: wide ? "0 10px 0 8px" : 0,
|
|
6280
|
+
// The native settings row bleeds 2px past its seat on both sides so its
|
|
6281
|
+
// hover chrome spans the full row; matching keeps both icon columns on
|
|
6282
|
+
// the same x.
|
|
6283
|
+
margin: wide ? "0 -2px" : "0 auto 2px",
|
|
6284
|
+
fontSize: wide ? 14 : void 0,
|
|
6285
|
+
lineHeight: wide ? "22px" : void 0,
|
|
6286
|
+
boxSizing: "border-box",
|
|
6287
|
+
transition: "background 120ms ease"
|
|
6060
6288
|
}
|
|
6061
6289
|
},
|
|
6062
6290
|
(0, import_react8.createElement)(import_dsh_client_ui_primitives8.IconSettingsOutline16, { size: wide ? 16 : 18 }),
|
|
6063
|
-
wide ? (0, import_react8.createElement)("span", { style: { fontSize:
|
|
6291
|
+
wide ? (0, import_react8.createElement)("span", { style: { fontSize: 14, lineHeight: "22px" } }, "\u5DE5\u7A0B\u6D41\u7A0B") : null
|
|
6064
6292
|
);
|
|
6065
6293
|
}
|
|
6066
6294
|
|