dsh-model 0.23.2 → 0.23.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/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/commands/engine.js +2 -2
- package/dist/commands/setup.js +2 -2
- package/dist/engine/install.js +23 -14
- package/dist/util/download.js +69 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -163,6 +163,7 @@ See the [design doc](docs/DESIGN.md) (Chinese).
|
|
|
163
163
|
- Meant for personal use: one account of your own per provider; do not use it to serve others.
|
|
164
164
|
- Using subscription and WorkBuddy credentials outside the official clients may break the providers' terms and put the account at risk. Decide for yourself.
|
|
165
165
|
- Windows 10 / 11 (x64, ARM64), macOS and Linux, Node ≥ 20.
|
|
166
|
+
- Downloading the engine needs GitHub: setup uses the system proxy (Windows, macOS) or `HTTPS_PROXY` automatically; if it still cannot connect it says why, and you can pass `--proxy <url>` or download the file yourself and set `DSH_MODEL_ENGINE_ARCHIVE`.
|
|
166
167
|
- Windows: the background services start at sign-in with no window. If an antivirus blocks them (the program is gone or will not start), trust `%USERPROFILE%\.dsh-model` and run `dsh-model repair`.
|
|
167
168
|
|
|
168
169
|
## License
|
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ dsh · 编辑器 · 脚本 ──Bearer key──▶ 引擎 127.0.0.1:8317/v1(
|
|
|
163
163
|
- 适合个人使用:每家用自己的一个账号,不要拿来对外提供服务。
|
|
164
164
|
- 订阅和 WorkBuddy 的凭据在官方客户端之外使用,可能不符合服务商的条款,账号有风险,请自行判断。
|
|
165
165
|
- 支持 Windows 10 / 11(x64、ARM64)、macOS、Linux,需要 Node ≥ 20。
|
|
166
|
+
- 下载引擎要连 GitHub:setup 会自动用上系统代理(Windows、macOS)或 `HTTPS_PROXY`;连不上时会说明原因,可以用 `--proxy <地址>` 指定代理,或手动下载后设置 `DSH_MODEL_ENGINE_ARCHIVE`。
|
|
166
167
|
- Windows:后台服务登录后自动运行、不弹窗口。如果被杀毒软件拦下(程序不见了或启动不了),把 `%USERPROFILE%\.dsh-model` 加入信任,再执行 `dsh-model repair`。
|
|
167
168
|
|
|
168
169
|
## License
|
package/dist/commands/engine.js
CHANGED
|
@@ -45,9 +45,9 @@ async function upgrade(ctx, requested) {
|
|
|
45
45
|
let asset = version === manifest.version ? manifest.assets[target] : undefined;
|
|
46
46
|
if (!asset) {
|
|
47
47
|
warn(L(`v${version} 不在本版 dsh-model 的锁定清单里,校验值取自上游发布的 checksums.txt`, `v${version} is not pinned by this dsh-model; using the checksum from the upstream checksums.txt`));
|
|
48
|
-
asset = await fetchUpstreamAsset(manifest, version, target);
|
|
48
|
+
asset = await fetchUpstreamAsset(manifest, version, target, { proxy: all.config.proxy });
|
|
49
49
|
}
|
|
50
|
-
await installVersion(ctx, version, asset);
|
|
50
|
+
await installVersion(ctx, version, asset, { proxy: all.config.proxy });
|
|
51
51
|
ok(L(`v${version} 已下载并校验`, `v${version} downloaded and verified`));
|
|
52
52
|
await trialRun(ctx, all, versionBinary(ctx, version));
|
|
53
53
|
ok(L('试运行通过', 'Trial run passed'));
|
package/dist/commands/setup.js
CHANGED
|
@@ -164,10 +164,10 @@ export async function ensureEngine(ctx, all, opts = {}) {
|
|
|
164
164
|
const manifest = await loadManifest();
|
|
165
165
|
const target = platformTarget();
|
|
166
166
|
const version = config.engine.version ?? manifest.version;
|
|
167
|
-
const asset = version === manifest.version ? manifest.assets[target] : await fetchUpstreamAsset(manifest, version, target);
|
|
167
|
+
const asset = version === manifest.version ? manifest.assets[target] : await fetchUpstreamAsset(manifest, version, target, { proxy: config.proxy });
|
|
168
168
|
if (!asset)
|
|
169
169
|
throw new DshModelError('unsupported_platform', L(`引擎没有 ${target} 的发布包`, `No engine build for ${target}`));
|
|
170
|
-
const installed = await installVersion(ctx, version, asset);
|
|
170
|
+
const installed = await installVersion(ctx, version, asset, { proxy: config.proxy });
|
|
171
171
|
if ((await currentVersion(ctx)) !== version)
|
|
172
172
|
await activateVersion(ctx, version);
|
|
173
173
|
config.engine.version = version;
|
package/dist/engine/install.js
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
// 只信任仓库里 engine-manifest.json 的 sha256;校验不过直接失败。
|
|
4
4
|
// 版本目录 engine/versions/<v>/,current 软链指向在用版本,升级与回滚只切软链。
|
|
5
5
|
// Windows:发布包是 zip、程序叫 cli-proxy-api.exe;建软链要管理员,所以 current 用目录联接(junction),不需要权限。
|
|
6
|
-
import { createWriteStream } from 'node:fs';
|
|
7
6
|
import { chmod, mkdir, readFile, readlink, rename, rm, symlink, unlink } from 'node:fs/promises';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
8
|
import { join, resolve } from 'node:path';
|
|
9
|
-
import {
|
|
10
|
-
import { pipeline } from 'node:stream/promises';
|
|
11
|
-
import { DshModelError } from '../errors.js';
|
|
9
|
+
import { DshModelError, isDshModelError } from '../errors.js';
|
|
12
10
|
import { L } from '../i18n.js';
|
|
11
|
+
import { downloadFile, downloadText } from '../util/download.js';
|
|
13
12
|
import { run } from '../util/exec.js';
|
|
14
13
|
import { ensureDir, exists, sha256 } from '../util/fs.js';
|
|
15
14
|
import { PKG_ROOT } from '../util/pkg.js';
|
|
@@ -44,13 +43,19 @@ export async function currentVersion(ctx) {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
/** 从上游发布的 checksums.txt 取某版本的资产(engine upgrade <ver> 指定了清单外版本时用) */
|
|
47
|
-
export async function fetchUpstreamAsset(manifest, version, target) {
|
|
46
|
+
export async function fetchUpstreamAsset(manifest, version, target, opts = {}) {
|
|
48
47
|
const base = `https://github.com/${manifest.repo}/releases/download/v${version}`;
|
|
49
48
|
const file = assetFileName(version, target);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
let text;
|
|
50
|
+
try {
|
|
51
|
+
text = await downloadText(`${base}/checksums.txt`, join(tmpdir(), `dsh-model-checksums-${process.pid}-${Date.now()}.txt`), opts);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (isDshModelError(error) && error.code === 'download_not_found')
|
|
55
|
+
throw new DshModelError('engine_version_not_found', L(`找不到引擎版本 v${version}`, `Engine version v${version} not found`));
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
const line = text.split('\n').find((l) => l.trim().endsWith(file));
|
|
54
59
|
const sha = line?.trim().split(/\s+/)[0];
|
|
55
60
|
if (!sha || !/^[0-9a-f]{64}$/.test(sha))
|
|
56
61
|
throw new DshModelError('engine_checksum_missing', L(`v${version} 的 checksums.txt 里没有 ${file}`, `${file} is missing from v${version} checksums.txt`));
|
|
@@ -60,7 +65,7 @@ export async function fetchUpstreamAsset(manifest, version, target) {
|
|
|
60
65
|
* 安装某版本到 versions/<v>/(已装就跳过)。返回是否真的装了。
|
|
61
66
|
* DSH_MODEL_ENGINE_ARCHIVE 可指向本地 tar.gz(离线 / 测试),同样要过 sha256 校验。
|
|
62
67
|
*/
|
|
63
|
-
export async function installVersion(ctx, version, asset) {
|
|
68
|
+
export async function installVersion(ctx, version, asset, opts = {}) {
|
|
64
69
|
const bin = versionBinary(ctx, version);
|
|
65
70
|
if (await exists(bin))
|
|
66
71
|
return false;
|
|
@@ -75,10 +80,14 @@ export async function installVersion(ctx, version, asset) {
|
|
|
75
80
|
data = await readFile(local);
|
|
76
81
|
}
|
|
77
82
|
else {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
try {
|
|
84
|
+
await downloadFile(asset.url, archive, opts);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (isDshModelError(error))
|
|
88
|
+
throw new DshModelError('engine_download_failed', L(`下载引擎失败:${error.message.replace(/^下载失败:|^Download failed: /, '')}`, `Engine download failed: ${error.message.replace(/^下载失败:|^Download failed: /, '')}`), error.hint);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
82
91
|
data = await readFile(archive);
|
|
83
92
|
}
|
|
84
93
|
const actual = sha256(data);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// util/download.ts — 下载文件(引擎发布包、checksums.txt)
|
|
2
|
+
//
|
|
3
|
+
// Node 的 fetch 不走系统代理,也不认 HTTPS_PROXY。国内直连 GitHub 的下载地址经常不通,
|
|
4
|
+
// Windows 上 setup 只报一句 "fetch failed"(实测)。所以设了代理时改用系统自带的 curl 走代理
|
|
5
|
+
// (macOS、Linux 都有;Windows 10 1803 起自带 curl.exe),没有 curl 再退回 fetch。
|
|
6
|
+
// 下载的内容一律由调用方按清单里的 sha256 校验,走哪条路都一样可信。
|
|
7
|
+
import { createWriteStream } from 'node:fs';
|
|
8
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
9
|
+
import { Readable } from 'node:stream';
|
|
10
|
+
import { pipeline } from 'node:stream/promises';
|
|
11
|
+
import { DshModelError } from '../errors.js';
|
|
12
|
+
import { L } from '../i18n.js';
|
|
13
|
+
import { run, which } from './exec.js';
|
|
14
|
+
import { redactProxy } from './proxy.js';
|
|
15
|
+
/** fetch 抛的错里真正的原因("fetch failed" 的 cause:ENOTFOUND、ECONNRESET、UND_ERR_CONNECT_TIMEOUT…) */
|
|
16
|
+
export function fetchCause(error) {
|
|
17
|
+
const e = error;
|
|
18
|
+
const cause = e?.cause?.code ?? e?.cause?.message;
|
|
19
|
+
return cause ? `${e.message ?? 'fetch failed'}(${cause})` : String(e?.message ?? error);
|
|
20
|
+
}
|
|
21
|
+
/** 连不上时怎么办:开代理、指定代理,或手动下载后用本地文件 */
|
|
22
|
+
export function downloadHint(url, proxy) {
|
|
23
|
+
return proxy
|
|
24
|
+
? L(`经代理 ${redactProxy(proxy)} 也没下载成功:确认代理软件开着、能打开 GitHub;或换一个代理(dsh-model setup --proxy <地址>)。也可以手动下载 ${url},把环境变量 DSH_MODEL_ENGINE_ARCHIVE 设成下载好的文件路径,再执行 dsh-model setup`, `Still failed through the proxy ${redactProxy(proxy)}: check the proxy app is running and can reach GitHub, or use another one (dsh-model setup --proxy <url>). You can also download ${url} yourself, set the environment variable DSH_MODEL_ENGINE_ARCHIVE to the file's path, and run dsh-model setup again`)
|
|
25
|
+
: L(`连不上 GitHub(下载引擎要用)。用代理的话:打开代理软件的"系统代理",或执行 dsh-model setup --proxy http://127.0.0.1:7890(换成你的代理端口)。也可以手动下载 ${url},把环境变量 DSH_MODEL_ENGINE_ARCHIVE 设成下载好的文件路径,再执行 dsh-model setup`, `Cannot reach GitHub (needed to download the engine). If you use a proxy: turn on its "system proxy", or run dsh-model setup --proxy http://127.0.0.1:7890 (your proxy's port). You can also download ${url} yourself, set the environment variable DSH_MODEL_ENGINE_ARCHIVE to the file's path, and run dsh-model setup again`);
|
|
26
|
+
}
|
|
27
|
+
/** 下载到 dest。HTTP 404 抛 code=download_not_found,其余失败抛 download_failed(带原因和建议) */
|
|
28
|
+
export async function downloadFile(url, dest, opts = {}) {
|
|
29
|
+
const curl = opts.proxy ? await which('curl') : null;
|
|
30
|
+
if (opts.proxy && curl) {
|
|
31
|
+
// --noproxy '':只用这里指定的代理,不受环境里 NO_PROXY 的影响
|
|
32
|
+
const r = await run(curl, ['-fsSL', '--retry', '2', '--connect-timeout', '20', '--proxy', opts.proxy, '--noproxy', '', '-o', dest, url], { timeoutMs: 10 * 60_000 });
|
|
33
|
+
if (r.code === 0)
|
|
34
|
+
return;
|
|
35
|
+
await rm(dest, { force: true });
|
|
36
|
+
// curl -f:HTTP 4xx/5xx 退出码 22,stderr 里有 "The requested URL returned error: 404"
|
|
37
|
+
if (/\b404\b/.test(r.stderr))
|
|
38
|
+
throw new DshModelError('download_not_found', L(`没有这个文件:${url}`, `No such file: ${url}`));
|
|
39
|
+
throw new DshModelError('download_failed', L(`下载失败:${r.stderr.trim() || `curl 退出码 ${r.code}`}`, `Download failed: ${r.stderr.trim() || `curl exit code ${r.code}`}`), downloadHint(url, opts.proxy));
|
|
40
|
+
}
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await fetch(url, { signal: AbortSignal.timeout(10 * 60_000) });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new DshModelError('download_failed', L(`下载失败:${fetchCause(error)}`, `Download failed: ${fetchCause(error)}`), downloadHint(url, opts.proxy));
|
|
47
|
+
}
|
|
48
|
+
if (res.status === 404)
|
|
49
|
+
throw new DshModelError('download_not_found', L(`没有这个文件:${url}`, `No such file: ${url}`));
|
|
50
|
+
if (!res.ok || !res.body)
|
|
51
|
+
throw new DshModelError('download_failed', L(`下载失败:HTTP ${res.status}`, `Download failed: HTTP ${res.status}`), downloadHint(url, opts.proxy));
|
|
52
|
+
try {
|
|
53
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(dest, { mode: 0o600 }));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
await rm(dest, { force: true });
|
|
57
|
+
throw new DshModelError('download_failed', L(`下载中断:${fetchCause(error)}`, `Download interrupted: ${fetchCause(error)}`), downloadHint(url, opts.proxy));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** 下载一个小文本文件(checksums.txt) */
|
|
61
|
+
export async function downloadText(url, tmpFile, opts = {}) {
|
|
62
|
+
try {
|
|
63
|
+
await downloadFile(url, tmpFile, opts);
|
|
64
|
+
return await readFile(tmpFile, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await rm(tmpFile, { force: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.3",
|
|
4
4
|
"description": "Daily check-in credits claimed for you (Trae, Qoder, WorkBuddy China), one OpenAI-compatible address that relays every model, and free or subscription model sources (SiliconFlow, Gemini, Codex, Kimi, Ollama…). For DeepSeek Harness (dsh) and any OpenAI-compatible tool. 每天自动签到领积分 · 一个地址中转所有模型 · 免费和订阅模型随你接。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"siliconflow",
|