dsh-any-agent 0.6.2 → 0.6.3
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 +60 -1
- package/README.md +47 -6
- package/README.zh.md +48 -7
- package/SECURITY.md +46 -0
- package/USAGE-NOTES.en.md +6 -2
- package/USAGE-NOTES.md +10 -2
- package/bin/dsh-any-agent.mjs +2 -1
- package/docs/en/architecture.md +12 -0
- package/docs/en/limits.md +6 -0
- package/docs/en/live-view-design.md +177 -0
- package/docs/en/reference.md +1 -0
- package/docs/en/release.md +21 -14
- package/docs/en/tests.md +6 -5
- package/docs/en/usage.md +2 -1
- package/docs/zh/architecture.md +12 -0
- package/docs/zh/limits.md +6 -0
- package/docs/zh/live-view-design.md +11 -6
- package/docs/zh/reference.md +1 -0
- package/docs/zh/release.md +20 -11
- package/docs/zh/tests.md +6 -5
- package/docs/zh/usage.md +2 -1
- package/package.json +6 -5
- package/src/doctor.mjs +63 -2
- package/src/live-cli.mjs +52 -15
- package/src/live-dashboard.html +191 -79
- package/src/live-view.mjs +111 -24
- package/DESIGN.md +0 -484
- package/docs/brand/README.md +0 -58
- package/docs/brand/header.jpg +0 -0
- package/scripts/check-release.mjs +0 -73
- package/scripts/commit.mjs +0 -122
- package/scripts/detect-dsh.mjs +0 -55
- package/scripts/lint.mjs +0 -595
- package/scripts/sync-test-counts.mjs +0 -699
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Release gate: the package version, CHANGELOG and docs must agree.
|
|
3
|
-
*
|
|
4
|
-
* Catches the two drifts that actually happened in this repo:
|
|
5
|
-
* - publishing a version whose CHANGELOG section was never written;
|
|
6
|
-
* - splitting docs and leaving relative links that 404 on GitHub.
|
|
7
|
-
*
|
|
8
|
-
* No network, no DSH, no tokens. Run before `npm publish`.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import path from 'node:path';
|
|
13
|
-
import { fileURLToPath } from 'node:url';
|
|
14
|
-
|
|
15
|
-
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const ROOT = path.resolve(HERE, '..');
|
|
17
|
-
const read = (p) => fs.readFileSync(path.join(ROOT, p), 'utf8');
|
|
18
|
-
|
|
19
|
-
let failed = 0;
|
|
20
|
-
const ok = (msg) => console.log(` ✓ ${msg}`);
|
|
21
|
-
const bad = (msg) => {
|
|
22
|
-
failed += 1;
|
|
23
|
-
console.log(` ✗ ${msg}`);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const pkg = JSON.parse(read('package.json'));
|
|
27
|
-
const version = pkg.version;
|
|
28
|
-
const changelog = read('CHANGELOG.md');
|
|
29
|
-
|
|
30
|
-
console.log(`\nrelease check: dsh-any-agent ${version}`);
|
|
31
|
-
|
|
32
|
-
if (changelog.includes(`## [${version}]`)) ok(`CHANGELOG has a ${version} section`);
|
|
33
|
-
else bad(`CHANGELOG.md is missing "## [${version}]"`);
|
|
34
|
-
|
|
35
|
-
if (changelog.includes(`## [${version}]`) && version !== '0.0.0') {
|
|
36
|
-
const section = changelog.split(`## [${version}]`)[1] ?? '';
|
|
37
|
-
if (section.trim().length > 0) ok('CHANGELOG section is not empty');
|
|
38
|
-
else bad(`CHANGELOG section for ${version} is empty`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const docFiles = [
|
|
42
|
-
'README.zh.md',
|
|
43
|
-
'README.md',
|
|
44
|
-
...fs.readdirSync(path.join(ROOT, 'docs/zh')).map((f) => `docs/zh/${f}`),
|
|
45
|
-
...fs.readdirSync(path.join(ROOT, 'docs/en')).map((f) => `docs/en/${f}`),
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
let links = 0;
|
|
49
|
-
for (const file of docFiles) {
|
|
50
|
-
const text = read(file);
|
|
51
|
-
for (const m of text.matchAll(/\]\(([^)]+)\)/g)) {
|
|
52
|
-
const link = m[1];
|
|
53
|
-
if (/^(https?:|#|mailto:)/.test(link)) continue;
|
|
54
|
-
const [target] = link.split('#');
|
|
55
|
-
if (!target) continue;
|
|
56
|
-
links += 1;
|
|
57
|
-
if (!fs.existsSync(path.resolve(ROOT, path.dirname(file), target))) {
|
|
58
|
-
bad(`${file}: missing link target ${link}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (links > 0) ok(`${links} relative links resolve`);
|
|
63
|
-
|
|
64
|
-
for (const required of ['docs/zh/team.md', 'docs/en/team.md', 'docs/zh/compatibility.md', 'docs/en/compatibility.md']) {
|
|
65
|
-
if (fs.existsSync(path.join(ROOT, required))) ok(`${required} exists`);
|
|
66
|
-
else bad(`${required} is missing`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (pkg.files?.includes('docs/')) ok('package.json ships docs/');
|
|
70
|
-
else bad('package.json "files" does not include docs/');
|
|
71
|
-
|
|
72
|
-
console.log(`\n${failed === 0 ? 'release check: 通过 ✅' : `release check: ${failed} 项失败 ❌`}`);
|
|
73
|
-
process.exit(failed === 0 ? 0 : 1);
|
package/scripts/commit.mjs
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* 提交/打标签,绝不写临时文件。
|
|
4
|
-
*
|
|
5
|
-
* 为什么要有这个脚本:过去几次发布都把 _cm.txt、_msg.cjs 这类临时文件
|
|
6
|
-
* 一起提交进了仓库 —— 因为生成多行提交信息的老办法是「先写文件,再
|
|
7
|
-
* git commit -F 那个文件」。文件一旦落盘,只要有一条 git add -A 就会顺手
|
|
8
|
-
* 把它加进索引,事后还得补一个清理提交。
|
|
9
|
-
*
|
|
10
|
-
* 正确做法是根本不让它落盘:提交信息走 stdin。
|
|
11
|
-
*
|
|
12
|
-
* node scripts/commit.mjs -m "标题" -m "正文第一段" ...
|
|
13
|
-
* node scripts/commit.mjs --file - 从 stdin 读
|
|
14
|
-
* node scripts/commit.mjs --tag v1.2.3 -m "..." 打附注标签
|
|
15
|
-
*
|
|
16
|
-
* 安全阀:提交前检查索引里有没有下划线开头的临时文件或 *.tmp-dsh-* 残留,
|
|
17
|
-
* 有就直接失败并列出它们 —— 宁可中断,也不要再制造一个「发布提交里混进
|
|
18
|
-
* 垃圾文件」的历史。
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { spawnSync } from 'node:child_process';
|
|
22
|
-
import { readFileSync } from 'node:fs';
|
|
23
|
-
|
|
24
|
-
const argv = process.argv.slice(2);
|
|
25
|
-
const has = (f) => argv.includes(f);
|
|
26
|
-
const val = (f, d) => {
|
|
27
|
-
const i = argv.indexOf(f);
|
|
28
|
-
return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : d;
|
|
29
|
-
};
|
|
30
|
-
const vals = (f) => {
|
|
31
|
-
const out = [];
|
|
32
|
-
for (let i = 0; i < argv.length; i++) {
|
|
33
|
-
if (argv[i] === f && argv[i + 1] && !argv[i + 1].startsWith('--')) out.push(argv[i + 1]);
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
if (has('--help') || has('-h') || argv.length === 0) {
|
|
39
|
-
process.stdout.write(
|
|
40
|
-
'commit.mjs -- commit or tag without ever writing a message file\n\n' +
|
|
41
|
-
' node scripts/commit.mjs -m "subject" -m "body paragraph"\n' +
|
|
42
|
-
' node scripts/commit.mjs --tag v1.2.3 -m "release notes"\n' +
|
|
43
|
-
' node scripts/commit.mjs --file - < msg.txt\n\n' +
|
|
44
|
-
'Messages go to git over stdin (git commit -F -), so no temp file exists\n' +
|
|
45
|
-
'and no git add -A can sweep one into the tree. Refuses to run if the\n' +
|
|
46
|
-
'index already contains scratch files (leading underscore) or *.tmp-dsh-*.\n',
|
|
47
|
-
);
|
|
48
|
-
process.exit(has('--help') || has('-h') ? 0 : 1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const DRY = has('--dry-run');
|
|
52
|
-
const TAG = val('--tag');
|
|
53
|
-
const ALLOW_TEMP = has('--allow-staged-temp');
|
|
54
|
-
|
|
55
|
-
/** 组装提交信息:每段之间一个空行。 */
|
|
56
|
-
function buildMessage() {
|
|
57
|
-
const fileArg = val('--file');
|
|
58
|
-
if (fileArg) {
|
|
59
|
-
const text = fileArg === '-' ? readFileSync(0, 'utf8') : readFileSync(fileArg, 'utf8');
|
|
60
|
-
// 统一成 LF,并且去掉结尾的空白行 —— git 会把它们当正文保留。
|
|
61
|
-
return text.replace(/\r\n/g, '\n').replace(/[ \t]+$/gm, '').replace(/\n+$/, '');
|
|
62
|
-
}
|
|
63
|
-
const paras = vals('-m');
|
|
64
|
-
if (!paras.length) {
|
|
65
|
-
process.stderr.write('需要至少一个 -m,或 --file <path|->。用 --help 看用法。\n');
|
|
66
|
-
process.exit(2);
|
|
67
|
-
}
|
|
68
|
-
// 每段各自去掉首尾空白:残留的空行会被 git 原样保留进提交信息。
|
|
69
|
-
return paras.map((s) => s.trim()).filter(Boolean).join('\n\n');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function git(args, input) {
|
|
73
|
-
const r = spawnSync('git', args, {
|
|
74
|
-
// 三种 stdio 都继承,git 自己的报错才看得见;只有 stdin 接管道。
|
|
75
|
-
stdio: input === undefined ? 'inherit' : ['pipe', 'inherit', 'inherit'],
|
|
76
|
-
input,
|
|
77
|
-
encoding: 'utf8',
|
|
78
|
-
});
|
|
79
|
-
return r.status ?? 1;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** 索引里不该有的东西。宁可在这一步失败。 */
|
|
83
|
-
function scratchInIndex() {
|
|
84
|
-
const r = spawnSync('git', ['diff', '--cached', '--name-only'], { encoding: 'utf8' });
|
|
85
|
-
if (r.status !== 0) return [];
|
|
86
|
-
return r.stdout
|
|
87
|
-
.split('\n')
|
|
88
|
-
.map((s) => s.trim())
|
|
89
|
-
.filter(Boolean)
|
|
90
|
-
.filter((p) => {
|
|
91
|
-
const base = p.split('/').pop();
|
|
92
|
-
return base.startsWith('_') || /\.tmp-dsh-/.test(base) || /\.bak-dsh-/.test(base);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const message = buildMessage();
|
|
97
|
-
if (!message.trim()) {
|
|
98
|
-
process.stderr.write('提交信息为空,拒绝执行。\n');
|
|
99
|
-
process.exit(2);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!ALLOW_TEMP) {
|
|
103
|
-
const scratch = scratchInIndex();
|
|
104
|
-
if (scratch.length) {
|
|
105
|
-
process.stderr.write('索引里有临时/备份文件,拒绝提交:\n');
|
|
106
|
-
for (const p of scratch) process.stderr.write(' ' + p + '\n');
|
|
107
|
-
process.stderr.write('先 git restore --staged 它们,或确认无误后加 --allow-staged-temp。\n');
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const args = TAG ? ['tag', '-a', TAG, '-F', '-'] : ['commit', '-F', '-'];
|
|
113
|
-
|
|
114
|
-
if (DRY) {
|
|
115
|
-
process.stdout.write('[dry-run] git ' + args.join(' ') + ' <<< 信息走 stdin,不落盘\n');
|
|
116
|
-
process.stdout.write('----\n' + message + '\n----\n');
|
|
117
|
-
process.exit(0);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 关键一行:信息从 stdin 进 git,磁盘上不存在任何信息文件。
|
|
121
|
-
const code = git(args, message);
|
|
122
|
-
process.exit(code);
|
package/scripts/detect-dsh.mjs
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CI 用:问项目自己"这台机器上找得到 DSH 吗"。
|
|
4
|
-
*
|
|
5
|
-
* 为什么不在 workflow 里写 `command -v dsh`:服务自己并不靠 PATH 上的 `dsh` 找 DSH,
|
|
6
|
-
* 而是用 src/dsh-locate.mjs 的 resolveDsh()(桌面版注册表 / 默认安装路径、npm 全局、
|
|
7
|
-
* npx 缓存,`DSH_BIN` 可显式覆盖)。只装了桌面版、PATH 里没有 `dsh` 的自托管 runner
|
|
8
|
-
* 用旧写法会被误判成"没有 DSH",模型套件整个跳过。这里直接复用同一套逻辑,
|
|
9
|
-
* CI 判定与服务实际行为就不会再分叉。
|
|
10
|
-
*
|
|
11
|
-
* 写成脚本而不是内联 `node -e`:workflow 的 shell 在 Linux(bash)与 Windows(pwsh)上
|
|
12
|
-
* 对引号的处理不同,`node scripts/detect-dsh.mjs` 在两边写法一致。
|
|
13
|
-
*
|
|
14
|
-
* 输出:
|
|
15
|
-
* - 标准输出一行人类可读的结果;
|
|
16
|
-
* - 在 GitHub Actions 里(有 GITHUB_OUTPUT)再写 ok=1|0、source、version,
|
|
17
|
-
* 后续步骤用 steps.<id>.outputs.ok 判断;
|
|
18
|
-
* - 找不到时退出码仍是 0(这是"跳过",不是失败),由 workflow 决定后续步骤。
|
|
19
|
-
*
|
|
20
|
-
* 只读:不拉起 DSH,只读安装目录 / asar 里的版本信息(以及 Windows 上的 `reg export`)。
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { appendFileSync } from 'node:fs';
|
|
24
|
-
import { SOURCE_LABEL, resolveDsh } from '../src/dsh-locate.mjs';
|
|
25
|
-
|
|
26
|
-
let ok = false;
|
|
27
|
-
let source = '';
|
|
28
|
-
let version = '';
|
|
29
|
-
let detail = '';
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const dsh = resolveDsh({ fresh: true });
|
|
33
|
-
ok = true;
|
|
34
|
-
source = dsh.source;
|
|
35
|
-
version = dsh.version ?? '';
|
|
36
|
-
detail = `${SOURCE_LABEL[dsh.source] ?? dsh.source} / DSH ${dsh.version ?? '未知版本'} / ${dsh.bin}`;
|
|
37
|
-
console.log(`DSH found: ${detail}`);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
// resolveDsh 的报错是多行的;GitHub 注解里换行需要转义,这里只取第一行
|
|
40
|
-
detail = String(e.message).split('\n')[0];
|
|
41
|
-
console.log(`DSH not found: ${detail}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (process.env.GITHUB_OUTPUT) {
|
|
45
|
-
appendFileSync(process.env.GITHUB_OUTPUT, `ok=${ok ? '1' : '0'}\nsource=${source}\nversion=${version}\n`);
|
|
46
|
-
}
|
|
47
|
-
if (process.env.GITHUB_ACTIONS === 'true') {
|
|
48
|
-
if (ok) console.log(`::notice title=DSH detected::${detail}`);
|
|
49
|
-
else {
|
|
50
|
-
console.log(
|
|
51
|
-
`::warning title=Model suites skipped::${detail} -- use a self-hosted runner with DeepSeek Harness ` +
|
|
52
|
-
'(desktop app, npm global or npx cache; or set the DSH_BIN secret), a provider and credentials.',
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|