dsh-sa-workbench 0.1.29 → 0.1.31
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 +3 -1
- package/client.js +1980 -672
- package/modules/dsh-pm-core/delivery-library.mjs +279 -0
- package/modules/dsh-pm-core/engine.js +1 -1
- package/modules/dsh-pm-core/index.js +41 -1
- package/modules/dsh-pm-core/knowledge-model.mjs +36 -0
- package/modules/dsh-pm-core/knowledge-workflow.mjs +291 -0
- package/modules/dsh-pm-core/product-context.mjs +74 -0
- package/modules/dsh-pm-core/requirement-deletion.mjs +276 -0
- package/modules/dsh-pm-ui/assets/reading-THIRD-PARTY-NOTICES.txt +6952 -0
- package/modules/dsh-pm-ui/assets/reading-manifest.json +29 -0
- package/modules/dsh-pm-ui/assets/reading-mermaid.js +4319 -0
- package/modules/dsh-pm-ui/client.js +1980 -672
- package/modules/dsh-pm-ui/index.js +28 -3
- package/modules/dsh-pm-ui/knowledge-model.mjs +36 -0
- package/modules/dsh-pm-ui/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {knowledgeCatalog} from './knowledge-model.mjs'
|
|
1
2
|
/**
|
|
2
3
|
* dsh-pm-ui — host half.
|
|
3
4
|
*
|
|
@@ -110,6 +111,7 @@ function statusFor(code) {
|
|
|
110
111
|
return 400
|
|
111
112
|
case 'stage_incomplete':
|
|
112
113
|
case 'stale_artifact':
|
|
114
|
+
case 'conflict':
|
|
113
115
|
case 'stale_requirement':
|
|
114
116
|
case 'archived':
|
|
115
117
|
case 'corrupt_state':
|
|
@@ -211,6 +213,21 @@ export function apply(ctx) {
|
|
|
211
213
|
|
|
212
214
|
const setup = pm.getSettings()
|
|
213
215
|
if ((setup.setupRequired || setup.restartRequired) && req.method !== 'GET' && route !== '/settings') return fail(res,409,'请先保存目录配置并完整重启 DSH。','setup_required')
|
|
216
|
+
if (route === '/knowledge-search' && req.method === 'GET') return json(res,200,{ok:true,...await pm.knowledgeWorkflow.search(url.searchParams.get('query') || '')})
|
|
217
|
+
if (route === '/knowledge-candidates' && req.method === 'GET') return json(res,200,{ok:true,...await pm.knowledgeWorkflow.load(url.searchParams.get('id') || '')})
|
|
218
|
+
if (route === '/knowledge-candidate-preview' && req.method === 'POST') return json(res,200,{ok:true,...await pm.knowledgeWorkflow.preview(await readBody(req))})
|
|
219
|
+
if (route === '/knowledge-candidate-approve' && req.method === 'POST') return json(res,200,{ok:true,receipt:await pm.knowledgeWorkflow.approve(await readBody(req))})
|
|
220
|
+
if (route === '/deliveries' && req.method === 'GET') return json(res,200,{ok:true,...await pm.deliveryLibrary.list(url.searchParams.get('query') || '')})
|
|
221
|
+
if (route === '/delivery' && req.method === 'GET') return json(res,200,{ok:true,...await pm.deliveryLibrary.detail(url.searchParams.get('id') || '')})
|
|
222
|
+
if (route === '/delivery-file' && req.method === 'GET') return json(res,200,{ok:true,...await pm.deliveryLibrary.read(url.searchParams.get('id') || '',url.searchParams.get('path') || '')})
|
|
223
|
+
if (route === '/delivery-capture' && req.method === 'POST') return json(res,200,{ok:true,entry:await pm.deliveryLibrary.capture(await readBody(req))})
|
|
224
|
+
if (route === '/delivery-assistant' && req.method === 'POST') return json(res,200,{ok:true,...await pm.deliveryLibrary.assistant((await readBody(req)).id)})
|
|
225
|
+
if (route === '/delivery-knowledge-candidates' && req.method === 'GET') return json(res,200,{ok:true,...await pm.deliveryLibrary.workflow.load(url.searchParams.get('id') || '')})
|
|
226
|
+
if (route === '/delivery-knowledge-candidate-preview' && req.method === 'POST') return json(res,200,{ok:true,...await pm.deliveryLibrary.workflow.preview(await readBody(req))})
|
|
227
|
+
if (route === '/delivery-knowledge-candidate-approve' && req.method === 'POST') return json(res,200,{ok:true,receipt:await pm.deliveryLibrary.workflow.approve(await readBody(req))})
|
|
228
|
+
if (route === '/product-context' && req.method === 'GET') return json(res,200,{ok:true,...await pm.productContext.read()})
|
|
229
|
+
if (route === '/product-context' && req.method === 'POST') return json(res,200,{ok:true,...await pm.productContext.save(await readBody(req))})
|
|
230
|
+
if (route === '/product-document' && req.method === 'GET') return json(res,200,{ok:true,...await pm.productContext.document(url.searchParams.get('path') || '')})
|
|
214
231
|
if (route === '/directories' && req.method === 'GET') {
|
|
215
232
|
return json(res, 200, { ok: true, ...await listDirectories(url.searchParams.get('path') ?? '') })
|
|
216
233
|
}
|
|
@@ -254,7 +271,8 @@ export function apply(ctx) {
|
|
|
254
271
|
}
|
|
255
272
|
|
|
256
273
|
if (route === '/state' && req.method === 'GET') {
|
|
257
|
-
const
|
|
274
|
+
const view = await pm.viewAll()
|
|
275
|
+
const requirements = await pm.deliveryLibrary.withDeliveryFacts(view.requirements)
|
|
258
276
|
return json(res, 200, {
|
|
259
277
|
ok: true,
|
|
260
278
|
workspace: {
|
|
@@ -283,6 +301,11 @@ export function apply(ctx) {
|
|
|
283
301
|
const bytes=await readFile(join(LIB_DIR,'assets','version-plan-template.xlsx'))
|
|
284
302
|
return json(res,200,{ok:true,name:'版本计划模板.xlsx',content:bytes.toString('base64')})
|
|
285
303
|
}
|
|
304
|
+
if (route === '/reading-mermaid' && req.method === 'GET') {
|
|
305
|
+
const bytes=await readFile(join(LIB_DIR,'assets','reading-mermaid.js'))
|
|
306
|
+
res.writeHead(200,{'content-type':'text/javascript; charset=utf-8','x-content-type-options':'nosniff','cache-control':'no-store'})
|
|
307
|
+
return res.end(bytes)
|
|
308
|
+
}
|
|
286
309
|
if (route === '/overview-font' && req.method === 'GET') {
|
|
287
310
|
const bytes=await readFile(join(LIB_DIR,'assets','outfit-latin-wght-normal.woff2'))
|
|
288
311
|
return json(res,200,{ok:true,content:bytes.toString('base64')})
|
|
@@ -362,9 +385,11 @@ export function apply(ctx) {
|
|
|
362
385
|
return json(res, 200, { ok: true, id: record.id, title: record.title })
|
|
363
386
|
}
|
|
364
387
|
|
|
388
|
+
if (route === '/requirement-delete-preview' && req.method === 'GET') return json(res,200,{ok:true,...await pm.requirementDeletion.preview(String(url.searchParams.get('id') || ''))})
|
|
389
|
+
|
|
365
390
|
if (route === '/requirement' && req.method === 'DELETE') {
|
|
366
391
|
const body = await readBody(req)
|
|
367
|
-
const result = await pm.deleteRequirement(String(body.id ?? ''))
|
|
392
|
+
const result = await pm.deleteRequirement(String(body.id ?? ''), body)
|
|
368
393
|
return json(res, 200, { ok: true, ...result })
|
|
369
394
|
}
|
|
370
395
|
|
|
@@ -474,7 +499,7 @@ export function apply(ctx) {
|
|
|
474
499
|
|
|
475
500
|
// 全局知识库:列表 + 单文件读取(只读;修改走会话或直接编辑文件)
|
|
476
501
|
if (route === '/knowledge' && req.method === 'GET') {
|
|
477
|
-
const files = await pm
|
|
502
|
+
const files = await knowledgeCatalog(pm)
|
|
478
503
|
return json(res, 200, { ok: true, root: pm.knowledgeDir, files })
|
|
479
504
|
}
|
|
480
505
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Deliberately restricted front matter: plain scalar fields and string lists only.
|
|
2
|
+
// Never infer business confirmation or domains from prose or filenames.
|
|
3
|
+
export function knowledgeRecord(file, text='') {
|
|
4
|
+
const fields={}, match=String(text).match(/^\uFEFF?---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)
|
|
5
|
+
const scalar=s=>{
|
|
6
|
+
const value=String(s).trim()
|
|
7
|
+
if(value.startsWith('"')){try{return String(JSON.parse(value))}catch{}}
|
|
8
|
+
return value.replace(/^(['"])(.*)\1$/,'$2')
|
|
9
|
+
}
|
|
10
|
+
if(match){let key='';for(const line of match[1].split(/\r?\n/)){
|
|
11
|
+
const pair=line.match(/^([a-z_]+):\s*(.*?)\s*$/)
|
|
12
|
+
if(pair){key=pair[1];fields[key]=pair[2] ? scalar(pair[2]) : []}
|
|
13
|
+
else {const item=line.match(/^\s+-\s+(.+)$/);if(item && Array.isArray(fields[key]))fields[key].push(scalar(item[1]))}
|
|
14
|
+
}}
|
|
15
|
+
const list=v=>Array.isArray(v)?v.map(scalar):typeof v==='string'?v.replace(/^\[|\]$/g,'').split(/[,,]/).map(scalar).filter(Boolean):[]
|
|
16
|
+
const body=match?text.slice(match[0].length):String(text)
|
|
17
|
+
return {...file,title:scalar(fields.title || body.match(/^#\s+(.+)$/m)?.[1] || file.path.replace(/.*\//,'').replace(/\.[^.]+$/,'')),
|
|
18
|
+
kind:scalar(fields.type || '未分类'),domains:list(fields.domains),status:scalar(fields.status || '未标注'),
|
|
19
|
+
summary:scalar(fields.summary || body.replace(/^#+\s+.*$/gm,'').replace(/[`*_>#\[\]]/g,'').trim().slice(0,160)),
|
|
20
|
+
source:scalar(fields.source || ''),owner:scalar(fields.owner || ''),version:scalar(fields.version || ''),
|
|
21
|
+
scope:scalar(fields.scope || ''),reviewedBy:scalar(fields.reviewed_by || ''),
|
|
22
|
+
sourceDeliveryId:scalar(fields.source_delivery_id || ''),sourceRequirementId:scalar(fields.source_requirement_id || ''),
|
|
23
|
+
related:list(fields.related),searchText:body.slice(0,262144),metadata:!!match}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function knowledgeCatalog(pm){
|
|
27
|
+
const files=await pm.listKnowledge(), records=[]
|
|
28
|
+
let budget=4*1024*1024
|
|
29
|
+
// Sequential, bounded reads avoid unbounded I/O on a large knowledge directory.
|
|
30
|
+
for(const file of files){
|
|
31
|
+
if(file.bytes>262144 || file.bytes>budget){records.push({...knowledgeRecord(file),previewLimited:true});continue}
|
|
32
|
+
try{const content=await pm.readKnowledge(file.path) || '';budget-=Math.max(file.bytes,content.length);records.push(knowledgeRecord(file,content))}
|
|
33
|
+
catch{records.push({...knowledgeRecord(file),metadataError:true})}
|
|
34
|
+
}
|
|
35
|
+
return records
|
|
36
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"0.1.
|
|
1
|
+
{"type":"module","version":"0.1.31"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sa-workbench",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"description": "SA 工作台:需求整理、方案、PRD、原型和交付归档,一个插件统一安装与更新。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -71,4 +71,4 @@
|
|
|
71
71
|
"registry": "https://registry.npmjs.org/",
|
|
72
72
|
"tag": "latest"
|
|
73
73
|
}
|
|
74
|
-
}
|
|
74
|
+
}
|