dsh-quote-annotate-pro 0.0.0-stage → 0.2.0
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/CHANGELOG.md +37 -0
- package/LICENSE +22 -0
- package/README.en.md +78 -0
- package/README.md +148 -2
- package/SUMMARY.md +143 -0
- package/cordis.patch.yml +4 -0
- package/lib/client.js +1275 -0
- package/lib/index.js +6 -0
- package/package.json +51 -4
- package/tools/check-manifest.mjs +61 -0
- package/tools/diag-sink.mjs +56 -0
package/lib/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-quote-annotate-pro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "选区引用与锚点批注(增强 fork):划词 → 批注 → 带精确锚点的引用 chip,点击跳回原文;提交时把轮次/角色/位置写入引用块,让模型精确知道你指的是哪一处。",
|
|
5
|
+
"keywords": ["dsh", "dsh-plugin", "deepseek-harness", "quote", "annotate", "selection", "reference", "anchor"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": "./lib/client.js",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": ["lib", "tools", "cordis.patch.yml", "README.md", "SUMMARY.md", "CHANGELOG.md", "LICENSE"],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/zhao-guang-ai/dsh-quote-annotate-pro.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/zhao-guang-ai/dsh-quote-annotate-pro#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/zhao-guang-ai/dsh-quote-annotate-pro/issues"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"check": "node tools/check-manifest.mjs",
|
|
27
|
+
"diag": "node tools/diag-sink.mjs"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"registry": "https://registry.npmjs.org/"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.2.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"react": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"dsh": {
|
|
45
|
+
"bundle": {
|
|
46
|
+
"patch": "./cordis.patch.yml"
|
|
47
|
+
},
|
|
48
|
+
"client": {
|
|
49
|
+
"platform": "web",
|
|
50
|
+
"inject": []
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 清单一致性自检(CI 用,无外部依赖):
|
|
3
|
+
* 1. package.json 必需字段与 dsh 声明;
|
|
4
|
+
* 2. exports['./client'] 指向的文件存在,且 bundle 的 `id` 与包名一致;
|
|
5
|
+
* 3. cordis.patch.yml 确实 insert 了本包名。
|
|
6
|
+
* 用法:node tools/check-manifest.mjs
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
9
|
+
import { dirname, resolve } from 'node:path'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
|
|
12
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
13
|
+
const fail = (msg) => { console.error(`✗ ${msg}`); process.exitCode = 1 }
|
|
14
|
+
const ok = (msg) => console.log(`✓ ${msg}`)
|
|
15
|
+
|
|
16
|
+
const pkgPath = resolve(root, 'package.json')
|
|
17
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
18
|
+
|
|
19
|
+
if (typeof pkg.name === 'string' && pkg.name !== '') ok(`name = ${pkg.name}`)
|
|
20
|
+
else fail('package.json 缺少 name')
|
|
21
|
+
|
|
22
|
+
if (typeof pkg.version === 'string' && /^\d+\.\d+\.\d+/.test(pkg.version)) ok(`version = ${pkg.version}`)
|
|
23
|
+
else fail('package.json 缺少合法的 version')
|
|
24
|
+
|
|
25
|
+
if (pkg.type !== 'module') fail('package.json 应为 "type": "module"')
|
|
26
|
+
else ok('type = module')
|
|
27
|
+
|
|
28
|
+
const clientDecl = pkg.dsh && pkg.dsh.client
|
|
29
|
+
if (!clientDecl) fail('缺少 dsh.client 声明')
|
|
30
|
+
else if (clientDecl.platform !== 'web') fail(`dsh.client.platform 应为 web,实际 ${clientDecl.platform}`)
|
|
31
|
+
else ok('dsh.client.platform = web')
|
|
32
|
+
|
|
33
|
+
const patchRel = pkg.dsh && pkg.dsh.bundle && pkg.dsh.bundle.patch
|
|
34
|
+
if (!patchRel) fail('缺少 dsh.bundle.patch')
|
|
35
|
+
else if (!existsSync(resolve(root, patchRel))) fail(`patch 文件不存在:${patchRel}`)
|
|
36
|
+
else ok(`dsh.bundle.patch = ${patchRel}`)
|
|
37
|
+
|
|
38
|
+
const clientRel = pkg.exports && pkg.exports['./client']
|
|
39
|
+
if (!clientRel) fail("缺少 exports['./client']")
|
|
40
|
+
else {
|
|
41
|
+
const clientPath = resolve(root, clientRel)
|
|
42
|
+
if (!existsSync(clientPath)) fail(`客户端 bundle 不存在:${clientRel}`)
|
|
43
|
+
else {
|
|
44
|
+
const head = readFileSync(clientPath, 'utf8').slice(0, 4000)
|
|
45
|
+
const idMatch = /__ModuleLoader__\s*\.\s*load\s*\(\s*\{\s*id:\s*'([^']+)'/.exec(head)
|
|
46
|
+
if (!idMatch) fail(`bundle 未以 window.__ModuleLoader__.load({ id, factory }) 形式注册:${clientRel}`)
|
|
47
|
+
else if (idMatch[1] !== pkg.name) fail(`bundle 注册的 id (${idMatch[1]}) 与包名 (${pkg.name}) 不一致`)
|
|
48
|
+
else ok(`bundle id = ${idMatch[1]}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (patchRel && existsSync(resolve(root, patchRel))) {
|
|
53
|
+
const patch = readFileSync(resolve(root, patchRel), 'utf8')
|
|
54
|
+
if (patch.includes(pkg.name)) ok(`patch 引用了 ${pkg.name}`)
|
|
55
|
+
else fail(`patch 未引用包名 ${pkg.name}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (pkg.main && !existsSync(resolve(root, pkg.main))) fail(`main 不存在:${pkg.main}`)
|
|
59
|
+
else if (pkg.main) ok(`main = ${pkg.main}`)
|
|
60
|
+
|
|
61
|
+
console.log(process.exitCode === 1 ? '\n自检未通过' : '\n自检通过')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地诊断接收端(开发期可选)。
|
|
3
|
+
*
|
|
4
|
+
* 插件(lib/client.js)在启动时会向 `http://127.0.0.1:47821/dsh-quote-pro` POST 一次 `/ping`
|
|
5
|
+
* 探测接收端:探测不到就本次运行静默,因此**不开这个脚本插件也能正常工作**。
|
|
6
|
+
*
|
|
7
|
+
* 用法:
|
|
8
|
+
* node tools/diag-sink.mjs
|
|
9
|
+
* # 自定义端口 / 日志文件:
|
|
10
|
+
* DIAG_PORT=47821 DIAG_LOG=./diag-sink.log node tools/diag-sink.mjs
|
|
11
|
+
*
|
|
12
|
+
* 之后在 DSH 里操作插件,终端会实时打出全链路事件:
|
|
13
|
+
* module:materialized / apply:start / register:ok … 插件加载与槽位注册
|
|
14
|
+
* selection:show / selection:stage 划词与「批注」
|
|
15
|
+
* insert:chip / insert:fallback-plaintext 引用插入(chip 或纯文本回退)
|
|
16
|
+
* dock:rows / dock:click / jump:ok / toast 导航条与跳回原文
|
|
17
|
+
* turnTail:render 回合尾部按钮取文
|
|
18
|
+
*/
|
|
19
|
+
import { createServer } from 'node:http'
|
|
20
|
+
import { appendFileSync, mkdirSync } from 'node:fs'
|
|
21
|
+
import { dirname, resolve } from 'node:path'
|
|
22
|
+
|
|
23
|
+
const PORT = Number(process.env.DIAG_PORT ?? 47821)
|
|
24
|
+
const LOG = resolve(process.env.DIAG_LOG ?? 'diag-sink.log')
|
|
25
|
+
|
|
26
|
+
mkdirSync(dirname(LOG), { recursive: true })
|
|
27
|
+
|
|
28
|
+
const cors = {
|
|
29
|
+
'Access-Control-Allow-Origin': '*',
|
|
30
|
+
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
31
|
+
'Access-Control-Allow-Headers': '*',
|
|
32
|
+
'Access-Control-Max-Age': '600',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
createServer((req, res) => {
|
|
36
|
+
if (req.method === 'OPTIONS') {
|
|
37
|
+
res.writeHead(204, cors)
|
|
38
|
+
res.end()
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
let body = ''
|
|
42
|
+
req.on('data', (chunk) => {
|
|
43
|
+
body += chunk
|
|
44
|
+
if (body.length > 200000) req.destroy()
|
|
45
|
+
})
|
|
46
|
+
req.on('end', () => {
|
|
47
|
+
const line = `${new Date().toISOString()}\t${req.method}\t${req.url}\t${body.replace(/[\r\n]+/g, '\\n')}\n`
|
|
48
|
+
try { appendFileSync(LOG, line) } catch (error) { console.error('append failed', error) }
|
|
49
|
+
if (req.url !== '/ping') process.stdout.write(line)
|
|
50
|
+
res.writeHead(200, { ...cors, 'Content-Type': 'text/plain' })
|
|
51
|
+
res.end('ok')
|
|
52
|
+
})
|
|
53
|
+
}).listen(PORT, '127.0.0.1', () => {
|
|
54
|
+
console.log(`diag sink listening on http://127.0.0.1:${PORT}/ -> ${LOG}`)
|
|
55
|
+
console.log('插件里对应的常量:const DIAG_URL = `http://127.0.0.1:${PORT}/dsh-quote-pro`')
|
|
56
|
+
})
|