@marsken/dsh-studio 0.1.17 → 0.1.18
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 +2 -1
- package/docs/compatibility.md +1 -1
- package/docs/publish.md +1 -0
- package/index.js +14 -3
- package/package.json +1 -1
- package/src/host/executor.js +8 -5
package/AGENTS.md
CHANGED
|
@@ -80,7 +80,8 @@ STUDIO_TOKEN=$(sudo journalctl -u dsh-web -n 50 | grep -o 'token=[A-Za-z0-9_-]*'
|
|
|
80
80
|
8. 真库自检:`npm run smoke:store -- ~/.dsh/dsh-studio.db`(把 `.db/-wal/-shm` 拷到临时目录起一遍真实 `StudioStore`,专抓「空库能起、真库就炸」的导入问题)
|
|
81
81
|
9. 成本数字可疑时跑 `npm run verify:usage [taskId]`:ssh 取原始会话日志现算一遍 token,与 `/costs` 对账(两条路径不同:一条走 session-usage 的 zstd 缓存,一条走 usage.js 直接解析)。
|
|
82
82
|
10. 发版后跑 `npm run verify:release`:比对 package.json 版本 / 本地 tag / origin main 与 tag / npm registry,不一致直接退出码 1。
|
|
83
|
-
11.
|
|
83
|
+
11. 全闭环自检:`STUDIO_BASE=http://<host>:18080 node tools/e2e-loop.mjs`——真建一条需求和卡,跑完需求评审→拆卡→各列→代码事实→用例→验收→版本发布。**会写入真实数据**,跑完记得清。
|
|
84
|
+
12. CI 在 `.github/workflows/ci.yml`:语法 → 契约 → `web/` 里 `pnpm install --frozen-lockfile && pnpm build` → `cmp dist/client.js client.js` → `git diff --exit-code`(产物必须可复现,改了前端忘记构建会直接红)
|
|
84
85
|
|
|
85
86
|
## 5. 禁止事项
|
|
86
87
|
|
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.18` |
|
|
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
|
@@ -151,6 +151,7 @@ token 只写进临时 npmrc(放在仓库外或 `tmp/` 且不提交),发完
|
|
|
151
151
|
3. **发版一致性用命令查,不靠记性**:`npm run verify:release`(= `node tools/verify-release.mjs`)比对五处——
|
|
152
152
|
package.json 版本、本地 tag、tag 里的 package.json、origin/main 与 origin 上的 tag、npm registry 的版本与 latest。
|
|
153
153
|
全绿输出「发版一致」,有不一致退出码 1。
|
|
154
|
+
**刚 publish 完那几分钟 registry 还看不到新版本**,这时用 `npm run verify:release -- --wait 180` 让它自己轮询等待,别把「还没同步」当成「没发」。
|
|
154
155
|
这条是因为 0.1.15/0.1.16 真的发到 npm 了、git 却没推上去:`git tag -f vX.Y.Z && git push ... | tail -1` 会把 push 的失败吞掉。
|
|
155
156
|
npm latest 滞后(刚发几分钟)只算 warn,不算失败。
|
|
156
157
|
4. 部署脚本退出码 0 **不等于服务活着**——它打印的 `state=` 要自己看一眼,再单独 `curl -o /dev/null -w '%{http_code}' <host>/studio-api/state` 确认 200(2026-11-13 有过一次 `state=404` 被当成成功,线上挂了十分钟)。
|
package/index.js
CHANGED
|
@@ -1555,7 +1555,10 @@ function serviceConflicts(state, task) {
|
|
|
1555
1555
|
|
|
1556
1556
|
/** 一卡一分支名:优先用卡号(DEV-001),没编号就用 id。 */
|
|
1557
1557
|
function worktreeBranch(task) {
|
|
1558
|
-
|
|
1558
|
+
// 卡号只在一条需求内唯一(两条需求都会拆出 REQ-001):带上 id 后半段,否则 git 不让同一分支挂两个 worktree
|
|
1559
|
+
const code = String(task?.code ?? '').trim(), id = String(task?.id ?? '').trim()
|
|
1560
|
+
const tail = id.replace(/[^a-z0-9]/gi, '').slice(-8)
|
|
1561
|
+
const label = code === '' ? id : `${code}-${tail}`
|
|
1559
1562
|
return `task/${label}`.slice(0, 120)
|
|
1560
1563
|
}
|
|
1561
1564
|
|
|
@@ -1582,9 +1585,17 @@ async function ensureWorktree(store, task, config) {
|
|
|
1582
1585
|
if (alive.ok && String(alive.stdout).includes(`worktree ${existing.path}`)) return existing
|
|
1583
1586
|
}
|
|
1584
1587
|
const branch = worktreeBranch(task)
|
|
1585
|
-
const path = join(worktreeRoot(config),
|
|
1588
|
+
const path = join(worktreeRoot(config), branch.replace(/^[a-z]+\//, ''))
|
|
1586
1589
|
await mkdir(dirname(path), { recursive: true })
|
|
1587
|
-
|
|
1590
|
+
// 基准分支没有显式配就用仓库当前检出的分支:默认写死 main 会在用 master 的仓库上直接失败
|
|
1591
|
+
// 配置里默认写的是 main(sanitizeProject 的默认值),但很多仓库还在用 master:
|
|
1592
|
+
// 先确认这个分支真的存在,不存在就退回仓库当前检出的分支。
|
|
1593
|
+
let base = String(service?.baseBranch ?? project.baseBranch ?? '').trim()
|
|
1594
|
+
const branchOk = async (name) => name !== '' && (await runCommand('git', ['-C', repo, 'rev-parse', '--verify', 'refs/heads/' + name], 20000)).ok
|
|
1595
|
+
if (!(await branchOk(base))) {
|
|
1596
|
+
const current = await runCommand('git', ['-C', repo, 'rev-parse', '--abbrev-ref', 'HEAD'], 20000)
|
|
1597
|
+
base = (current.ok ? String(current.stdout).trim() : '') || base || 'main'
|
|
1598
|
+
}
|
|
1588
1599
|
const branches = await runCommand('git', ['-C', repo, 'branch', '--list', branch], 20000)
|
|
1589
1600
|
const exists = branches.ok && String(branches.stdout).trim().length > 0
|
|
1590
1601
|
const added = exists
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marsken/dsh-studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
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/executor.js
CHANGED
|
@@ -342,7 +342,6 @@ export async function runTaskCases(store, taskId, options = {}) {
|
|
|
342
342
|
if (task === undefined) throw new Error('任务不存在')
|
|
343
343
|
const config = state.config
|
|
344
344
|
const envs = config.testing.envs
|
|
345
|
-
if (envs.length === 0) throw new Error('还没有配置测试环境(设置 → 测试执行)')
|
|
346
345
|
const envId = String(options.env ?? config.testing.defaultEnv ?? '')
|
|
347
346
|
const env = envs.find(item => item.id === envId) ?? envs[0]
|
|
348
347
|
const wanted = Array.isArray(options.caseIds) && options.caseIds.length > 0
|
|
@@ -350,11 +349,15 @@ export async function runTaskCases(store, taskId, options = {}) {
|
|
|
350
349
|
: null
|
|
351
350
|
const targets = (Array.isArray(task.cases) ? task.cases : [])
|
|
352
351
|
.filter(item => (wanted === null ? item.http != null || item.shell != null : wanted.has(item.id)))
|
|
353
|
-
if (targets.length === 0) throw new Error('
|
|
352
|
+
if (targets.length === 0) throw new Error('没有可执行的用例:接口用例要填 method + url(相对地址拼环境 baseUrl),shell / 数据库用例要填 command / sql')
|
|
354
353
|
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
|
|
354
|
+
// 只有接口用例才需要测试环境;纯 shell / 数据库用例不该被「环境不可达」挡住。
|
|
355
|
+
if (targets.some(item => item.http != null)) {
|
|
356
|
+
if (env === undefined) throw new Error('还没有配置测试环境(设置 → 测试执行)')
|
|
357
|
+
const probe = await probeEnv(env, config)
|
|
358
|
+
if (probe.ok !== true) {
|
|
359
|
+
throw new Error(`测试环境「${env.name || env.id}」不可达(${probe.error}):一条用例都没跑,先把 ${probe.baseUrl || 'baseUrl'} 起来`)
|
|
360
|
+
}
|
|
358
361
|
}
|
|
359
362
|
|
|
360
363
|
const by = String(options.by ?? '系统').slice(0, 40)
|