@marsken/dsh-studio 0.1.4 → 0.1.6
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/README.md +1 -1
- package/client.js +48 -48
- package/dist/client.js +48 -48
- package/docs/compatibility.md +1 -1
- package/docs/publish.md +4 -1
- package/package.json +1 -1
- package/src/host/dispatcher.js +2 -0
- package/src/host/executor.js +8 -1
- package/src/host/routes/tasks.js +2 -0
- package/src/host/webhooks.js +1 -1
package/docs/compatibility.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
| 项 | 值 |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| dsh-studio | `package.json` `version: 0.1.
|
|
9
|
+
| dsh-studio | `package.json` `version: 0.1.6` |
|
|
10
10
|
| DeepSeek Harness | `0.2.2-rc.1`(`dsh --version`) |
|
|
11
11
|
| Node | `v24.22.2`(Linux 目标机);本机 Windows `v24.19.0` |
|
|
12
12
|
| 目标系统 | Ubuntu 24.04,`dsh web` 监听 `18080`,nginx `:80` 反代并注入 `dsh-auth-*` cookie |
|
package/docs/publish.md
CHANGED
|
@@ -127,6 +127,8 @@ token 只写进临时 npmrc(放在仓库外或 `tmp/` 且不提交),发完
|
|
|
127
127
|
| --- | --- | --- |
|
|
128
128
|
| `0.1.0` | 2026-11-13 | 首个公开版:看板 / 需求 / 用例回归 / 效能 / 设计评审四期 |
|
|
129
129
|
| `0.1.1` | 2026-11-13 | 补三张 README 截图(npm 页可看图);默认值与文档去掉公司内网信息 |
|
|
130
|
+
| `0.1.6` | 2026-11-14 | 回归跑完也发事件(regression/finished);数据库用例可指定断言的列 |
|
|
131
|
+
| `0.1.5` | 2026-11-14 | agent/failed 事件、控制面板(执行开关 / 上限 / webhook 按事件订阅)、抽屉里加数据库用例入口 |
|
|
130
132
|
| `0.1.4` | 2026-11-14 | SQLite 用例、出站 webhook、可配置上限、执行器注册表 |
|
|
131
133
|
| `0.1.3` | 2026-11-13 | shell 用例(命令 + 退出码/输出断言,默认关闭) |
|
|
132
134
|
| `0.1.2` | 2026-11-13 | 修 5 个缺陷:卡片挂不上 MR、换列清空负责人、三处静默截断、还原度无历史、规范体检不定位元素 |
|
|
@@ -134,4 +136,5 @@ token 只写进临时 npmrc(放在仓库外或 `tmp/` 且不提交),发完
|
|
|
134
136
|
两条硬规矩,每次发版都过一遍:
|
|
135
137
|
|
|
136
138
|
1. `npm run check` + `pnpm build` + `cmp -s dist/client.js client.js`(产物必须逐字节一致);
|
|
137
|
-
2. 发完**在临时 profile 里装一遍**并跑 `dsh --profile <tmp> --dump-config | grep dsh-studio`,确认宿主真认到了,然后把 profile
|
|
139
|
+
2. 发完**在临时 profile 里装一遍**并跑 `dsh --profile <tmp> --dump-config | grep dsh-studio`,确认宿主真认到了,然后把 profile 删掉;
|
|
140
|
+
3. 部署脚本退出码 0 **不等于服务活着**——它打印的 `state=` 要自己看一眼,再单独 `curl -o /dev/null -w '%{http_code}' <host>/studio-api/state` 确认 200(2026-11-13 有过一次 `state=404` 被当成成功,线上挂了十分钟)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marsken/dsh-studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DSH Studio: role-based multi-agent workspace (product / architect / developer / QA) with a live work board, GitLab + Feishu connection settings, all configurable from the web GUI.",
|
|
6
6
|
"main": "index.js",
|
package/src/host/dispatcher.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// ------------------------------------------------------------------ dispatcher
|
|
2
2
|
import { DEFAULT_ROLES, FALLBACK_AGENT_MODEL, ROLE_IDS, SANDBOX_MODES, STATUSES, codingSummary, normalizeStrategy, worktreeRoot } from './domain.js'
|
|
3
|
+
import { emitEvent } from './events.js'
|
|
3
4
|
import { loadTeamMemory, teamMemoryBlock } from './team-memory.js'
|
|
4
5
|
import { mkdir } from 'node:fs/promises'
|
|
5
6
|
import { randomBytes } from 'node:crypto'
|
|
@@ -345,6 +346,7 @@ export async function noteAgentFailure(store, taskId, error, by = '系统', opts
|
|
|
345
346
|
host.INTEGRATION.lastPushError = String(cause?.message ?? cause).slice(0, 300)
|
|
346
347
|
})
|
|
347
348
|
}
|
|
349
|
+
emitEvent('agent/failed', { taskId, attempts: next, exhausted: plan.exhausted, error: message, by })
|
|
348
350
|
return { failures: next, ...plan, message }
|
|
349
351
|
}
|
|
350
352
|
|
package/src/host/executor.js
CHANGED
|
@@ -333,6 +333,9 @@ export async function runTaskCases(store, taskId, options = {}) {
|
|
|
333
333
|
for (const item of targets) {
|
|
334
334
|
const outcome = await runOneCase(item, env, config)
|
|
335
335
|
results.push({ id: item.id, title: item.title, ...outcome })
|
|
336
|
+
if (outcome.ok !== true && item.flaky !== true) {
|
|
337
|
+
emitEvent('case/failed', { taskId: task.id, code: task.code ?? '', caseId: item.id, title: item.title, error: String(outcome.error ?? '').slice(0, 200) })
|
|
338
|
+
}
|
|
336
339
|
await store.mutate(draft => {
|
|
337
340
|
const card = draft.tasks.find(entry => entry.id === task.id)
|
|
338
341
|
if (card === undefined) return
|
|
@@ -1265,6 +1268,7 @@ export async function updateTask(store, id, patch) {
|
|
|
1265
1268
|
const simulated = { ...current, ...normalized, blocked: current.blocked }
|
|
1266
1269
|
const gate = taskExitGate(store.snapshot(), simulated)
|
|
1267
1270
|
if (!gate.ready) {
|
|
1271
|
+
emitEvent('gate/blocked', { taskId: id, gate: 'exit', blockers: gate.blockers.slice(0, 3) })
|
|
1268
1272
|
throw new Error(`准出没过:${gate.blockers.join(';')}。确实要收口就在推进时填 override 写明原因。`)
|
|
1269
1273
|
}
|
|
1270
1274
|
}
|
|
@@ -1274,7 +1278,10 @@ export async function updateTask(store, id, patch) {
|
|
|
1274
1278
|
const current = store.snapshot().tasks.find(entry => entry.id === id)
|
|
1275
1279
|
if (current !== undefined) {
|
|
1276
1280
|
const gate = designGate(store.snapshot().config, current, normalized.status)
|
|
1277
|
-
if (gate.needed === true && gate.ready !== true)
|
|
1281
|
+
if (gate.needed === true && gate.ready !== true) {
|
|
1282
|
+
emitEvent('gate/blocked', { taskId: id, gate: 'design', blockers: gate.blockers.slice(0, 3) })
|
|
1283
|
+
throw new Error(`进开发被拦:${gate.blockers.join(';')}`)
|
|
1284
|
+
}
|
|
1278
1285
|
}
|
|
1279
1286
|
}
|
|
1280
1287
|
let result
|
package/src/host/routes/tasks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
2
|
import { applyFidelityItem, applySpecItem, pushDiffHistory, sanitizeDesign } from '../design.js'
|
|
3
|
+
import { emitEvent } from '../events.js'
|
|
3
4
|
import { collectCases, groupCaseKeys, loadSuites, resolveSuiteKeys, sanitizeSuite, saveSuites, summarizeRun } from '../cases.js'
|
|
4
5
|
import { clearTeamMemory, loadTeamMemory } from '../team-memory.js'
|
|
5
6
|
import {
|
|
@@ -84,6 +85,7 @@ async function runCaseKeys(store, keys, options = {}) {
|
|
|
84
85
|
} catch {
|
|
85
86
|
/* 记不下来不影响这次跑的结果 */
|
|
86
87
|
}
|
|
88
|
+
emitEvent('regression/finished', { by: record.by, cards: record.cards, total: record.total, passed: record.passed, failed: record.failed, bugs: record.bugs ?? 0 })
|
|
87
89
|
return { results, regression: record }
|
|
88
90
|
}
|
|
89
91
|
|
package/src/host/webhooks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// webhook 订阅者:把事件 POST 给配置里的地址。失败只记健康,不重试、不阻塞。
|
|
2
2
|
import { emitEvent, subscribe } from './events.js'
|
|
3
3
|
|
|
4
|
-
export const WEBHOOK_EVENTS = ['task/moved', 'case/failed', 'agent/failed']
|
|
4
|
+
export const WEBHOOK_EVENTS = ['task/moved', 'case/failed', 'gate/blocked', 'agent/failed', 'regression/finished']
|
|
5
5
|
|
|
6
6
|
export function sanitizeWebhooks(raw) {
|
|
7
7
|
if (!Array.isArray(raw)) return []
|