@marsken/dsh-studio 0.1.9 → 0.1.11
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/AGENTS.md +4 -1
- package/README.md +11 -0
- package/client.js +48 -48
- package/dist/client.js +48 -48
- package/docs/compatibility.md +1 -1
- package/docs/publish.md +2 -0
- package/package.json +4 -2
- package/src/host/cases.js +4 -0
- package/src/host/impact.js +4 -2
- package/src/host/quality.js +17 -1
- package/src/host/routes/requirements.js +20 -1
- package/src/host/routes/tasks.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.11` |
|
|
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.11` | 2026-11-15 | 需求变更影响面(接口 + 面板)、可跑用例口径修正、成本口径文档 |
|
|
131
|
+
| `0.1.10` | 2026-11-15 | shell/db 用例可跑、GitLab 核对结果常驻显示、事件带 truncated、角色归并 |
|
|
130
132
|
| `0.1.9` | 2026-11-14 | 准出门禁提示「代码事实核对不通过」(只提醒,不拦) |
|
|
131
133
|
| `0.1.8` | 2026-11-14 | 卡进「待评审」时自动核对代码事实,对不上就写进 history 并记 gate/blocked |
|
|
132
134
|
| `0.1.7` | 2026-11-14 | webhook 投递结果进「集成健康」(地址写错了看得见) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marsken/dsh-studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"test": "node --test tests/repo.test.mjs tests/store-boot.test.mjs tests/client-units.test.mjs tests/notify.test.mjs",
|
|
13
13
|
"check": "node --check index.js && node --check client.src.js && node --test tests/repo.test.mjs tests/store-boot.test.mjs tests/client-units.test.mjs tests/notify.test.mjs",
|
|
14
|
-
"smoke:store": "node scripts/smoke-store.mjs"
|
|
14
|
+
"smoke:store": "node scripts/smoke-store.mjs",
|
|
15
|
+
"mock:gitlab": "node tools/mock-gitlab.mjs",
|
|
16
|
+
"verify:gitlab": "node tools/verify-gitlab-facts.mjs"
|
|
15
17
|
},
|
|
16
18
|
"exports": {
|
|
17
19
|
".": "./index.js",
|
package/src/host/cases.js
CHANGED
|
@@ -49,6 +49,10 @@ export function collectCases(state) {
|
|
|
49
49
|
priority: item.priority ?? 'p2',
|
|
50
50
|
pointId: item.pointId ?? '',
|
|
51
51
|
http: item.http !== null && item.http !== undefined,
|
|
52
|
+
shell: item.shell !== null && item.shell !== undefined,
|
|
53
|
+
db: item.db !== null && item.db !== undefined,
|
|
54
|
+
// 能跑的三种:接口 / shell / 数据库。以前界面只认 http,另外两种建得出来却跑不了。
|
|
55
|
+
runnable: item.http != null || item.shell != null || item.db != null,
|
|
52
56
|
lastRunAt: item.lastRun?.at ?? 0,
|
|
53
57
|
// 失败时最想看的三样:错在哪、返回了什么、上一次的结论文案。
|
|
54
58
|
lastError: String(item.lastRun?.error ?? '').slice(0, 300),
|
package/src/host/impact.js
CHANGED
|
@@ -13,7 +13,9 @@ export function casesOfTasks(tasks, limit = 40) {
|
|
|
13
13
|
key: `${task.id}:${item.id}`,
|
|
14
14
|
title: String(item.title ?? '').slice(0, 160),
|
|
15
15
|
result: String(item.result ?? 'pending'),
|
|
16
|
-
|
|
16
|
+
// 能自动跑的判断要和执行器一致:接口 / shell / 数据库任一都算。
|
|
17
|
+
http: item.http !== null && item.http !== undefined,
|
|
18
|
+
runnable: item.http != null || item.shell != null || item.db != null,
|
|
17
19
|
})
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -26,7 +28,7 @@ export function impactSummary(tasks) {
|
|
|
26
28
|
return {
|
|
27
29
|
cards: (Array.isArray(tasks) ? tasks : []).length,
|
|
28
30
|
cases: cases.length,
|
|
29
|
-
runnable: cases.filter(item => item.
|
|
31
|
+
runnable: cases.filter(item => item.runnable === true).length,
|
|
30
32
|
failing: cases.filter(item => item.result === 'fail').length,
|
|
31
33
|
}
|
|
32
34
|
}
|
package/src/host/quality.js
CHANGED
|
@@ -56,7 +56,23 @@ export function buildQuality(state, runs) {
|
|
|
56
56
|
if (rework) row.rework += 1
|
|
57
57
|
if (done && !rework) row.firstPass += 1
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
// 老的派单日志记的是「角色位」(dev),卡上记的是专家 id。一个角色只有一位专家时直接归并,
|
|
60
|
+
// 否则同一个人会在表里出现两行(一行有派单没卡、一行有卡没派单)。
|
|
61
|
+
const byRole = new Map()
|
|
62
|
+
for (const expert of state?.config?.experts ?? []) {
|
|
63
|
+
const role = String(expert.role ?? '')
|
|
64
|
+
if (role === '') continue
|
|
65
|
+
byRole.set(role, [...(byRole.get(role) ?? []), String(expert.id ?? '')])
|
|
66
|
+
}
|
|
67
|
+
const runRows = (runs?.byExpert ?? []).map(item => {
|
|
68
|
+
const key = String(item.key ?? '')
|
|
69
|
+
const holders = byRole.get(key) ?? []
|
|
70
|
+
return {
|
|
71
|
+
name: holders.length === 1 ? holders[0] : key,
|
|
72
|
+
runs: Number(item.runs) || 0,
|
|
73
|
+
ms: Number(item.ms) || 0,
|
|
74
|
+
}
|
|
75
|
+
})
|
|
60
76
|
const runByName = new Map(runRows.map(item => [item.name.toLowerCase(), item]))
|
|
61
77
|
const runById = new Map(runRows.map(item => [item.name, item]))
|
|
62
78
|
const experts = [...perExpert.values()].map(row => {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
sanitizeRequirementPool,
|
|
8
8
|
text,
|
|
9
9
|
} from '../domain.js'
|
|
10
|
+
import { casesOfTasks, impactSummary } from '../impact.js'
|
|
10
11
|
import {
|
|
11
12
|
ensureReqFolder,
|
|
12
13
|
mutateReqDoc,
|
|
@@ -405,7 +406,25 @@ export async function handleRequirementRoute({ method, route, req, res, store, u
|
|
|
405
406
|
host.sendJson(res, 200, { ok: true, requirement: result, state: host.publicState(store) })
|
|
406
407
|
return true
|
|
407
408
|
}
|
|
408
|
-
if (method === 'POST' && route === '/requirement/
|
|
409
|
+
if (method === 'POST' && route === '/requirement/impact') {
|
|
410
|
+
// 需求一改,这条需求下面的卡和用例就要重看。这里只算、不改数据。
|
|
411
|
+
const body = await host.readJsonBody(req)
|
|
412
|
+
const id = String(body.id ?? '')
|
|
413
|
+
const snapshot = store.snapshot()
|
|
414
|
+
const requirement = (snapshot.requirements ?? []).find(item => item.id === id)
|
|
415
|
+
if (requirement === undefined) throw new Error(`no requirement ${id}`)
|
|
416
|
+
const tasks = (snapshot.tasks ?? []).filter(task => task.requirementId === id)
|
|
417
|
+
const summary = impactSummary(tasks)
|
|
418
|
+
const cases = casesOfTasks(tasks, 60)
|
|
419
|
+
host.sendJson(res, 200, {
|
|
420
|
+
ok: true,
|
|
421
|
+
summary,
|
|
422
|
+
cases,
|
|
423
|
+
cards: tasks.map(task => ({ id: task.id, code: task.code ?? '', title: task.title ?? '', status: task.status ?? '' })),
|
|
424
|
+
})
|
|
425
|
+
return true
|
|
426
|
+
}
|
|
427
|
+
if (method === 'POST' && route === '/requirement/pull') {
|
|
409
428
|
const body = await host.readJsonBody(req)
|
|
410
429
|
const result = await pullFeishuDoc(body.url ?? body.docUrl ?? '')
|
|
411
430
|
host.sendJson(res, 200, { ok: true, ...result })
|
package/src/host/routes/tasks.js
CHANGED
|
@@ -85,7 +85,7 @@ async function runCaseKeys(store, keys, options = {}) {
|
|
|
85
85
|
} catch {
|
|
86
86
|
/* 记不下来不影响这次跑的结果 */
|
|
87
87
|
}
|
|
88
|
-
emitEvent('regression/finished', { by: record.by, cards: record.cards, total: record.total, passed: record.passed, failed: record.failed, bugs: record.bugs ?? 0 })
|
|
88
|
+
emitEvent('regression/finished', { by: record.by, cards: record.cards, total: record.total, passed: record.passed, failed: record.failed, bugs: record.bugs ?? 0, truncated: options.truncated ?? null })
|
|
89
89
|
return { results, regression: record }
|
|
90
90
|
}
|
|
91
91
|
|