dsh-pocket 2.6.0 → 2.6.1
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 -1
- package/README.md +1 -1
- package/lib/tunnel.mjs +71 -32
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -194,7 +194,7 @@ Such tools take over all traffic and often cut cloudflared's tunnel-edge connect
|
|
|
194
194
|
```sh
|
|
195
195
|
npm install
|
|
196
196
|
node client/build.mjs # rebuild after editing client/
|
|
197
|
-
npm test # proxy / auth / compression / tunnel / service / RPC / settings (
|
|
197
|
+
npm test # proxy / auth / compression / tunnel / service / RPC / settings (108 tests)
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
**Want to try your changes locally without publishing?** Point the installed plugin at your local checkout with a symlink and restart dsh web. Full steps (including switching back to the npm release) are in [LOCAL-DEV.md](./LOCAL-DEV.md).
|
package/README.md
CHANGED
|
@@ -195,7 +195,7 @@ npx @deepseek-ai/dsh web
|
|
|
195
195
|
```sh
|
|
196
196
|
npm install
|
|
197
197
|
node client/build.mjs # 改 client/ 后重新打包
|
|
198
|
-
npm test # 代理 / 认证 / 压缩 / 隧道 / 服务 / RPC / 设置(
|
|
198
|
+
npm test # 代理 / 认证 / 压缩 / 隧道 / 服务 / RPC / 设置(108 测试)
|
|
199
199
|
```
|
|
200
200
|
|
|
201
201
|
**改完想在本机先试?** 不用发版:把插件换成指向本地仓库的软链,重启 dsh web 就是本地代码。完整步骤(含怎么换回 npm 官方版本)见 [LOCAL-DEV.md](./LOCAL-DEV.md)。
|
package/lib/tunnel.mjs
CHANGED
|
@@ -23,12 +23,37 @@ import { createWriteStream } from 'node:fs';
|
|
|
23
23
|
export const QUICK_TUNNEL_URL_RE = /https:\/\/(?!api\.)[a-z0-9-]+\.trycloudflare\.com/i;
|
|
24
24
|
|
|
25
25
|
function platformBinary() {
|
|
26
|
-
const archMap = { x64: 'amd64', arm64: 'arm64' };
|
|
26
|
+
const archMap = { x64: 'amd64', arm64: 'arm64', ia32: '386', arm: 'arm' };
|
|
27
27
|
const a = archMap[process.arch] ?? process.arch;
|
|
28
28
|
const os = process.platform === 'darwin' ? 'darwin' : process.platform === 'win32' ? 'windows' : 'linux';
|
|
29
29
|
return { os, a, ext: os === 'windows' ? '.exe' : '' };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* 候选发布资产名,按优先级排列(issue #45)。
|
|
34
|
+
*
|
|
35
|
+
* cloudflared 现在的发布布局是**按平台分**:
|
|
36
|
+
* - linux:只有裸二进制 `cloudflared-linux-amd64`(没有 .tgz)
|
|
37
|
+
* - darwin:只有 `cloudflared-darwin-<arch>.tgz`
|
|
38
|
+
* - windows:只有 `cloudflared-windows-<arch>.exe`
|
|
39
|
+
*
|
|
40
|
+
* 我们以前给 linux 拼的是 `cloudflared-linux-amd64.tgz`,而它**根本不存在**
|
|
41
|
+
* (GitHub 返回 404),五个镜像全指向同一个 404 地址,于是必然"所有源都不通"
|
|
42
|
+
* —— 表现就是 Linux 服务器上开了公网访问一直报"无法安装",而 macOS/Windows
|
|
43
|
+
* 一切正常。Linux 下裸二进制还有个额外好处:不用解压,也就不再依赖系统装了
|
|
44
|
+
* `tar`(Alpine / slim 镜像 / 容器里常常没有)。
|
|
45
|
+
*
|
|
46
|
+
* linux 仍把 .tgz 留在候选里作为回退,万一上游改回打包方式也不会直接躺平。
|
|
47
|
+
*
|
|
48
|
+
* @returns {string[]} 资产名列表,越靠前越优先。
|
|
49
|
+
*/
|
|
50
|
+
export function platformAssets() {
|
|
51
|
+
const { os, a } = platformBinary();
|
|
52
|
+
if (os === 'windows') return [`cloudflared-windows-${a}.exe`];
|
|
53
|
+
if (os === 'darwin') return [`cloudflared-darwin-${a}.tgz`];
|
|
54
|
+
return [`cloudflared-linux-${a}`, `cloudflared-linux-${a}.tgz`];
|
|
55
|
+
}
|
|
56
|
+
|
|
32
57
|
/**
|
|
33
58
|
* cloudflared 下载源。
|
|
34
59
|
* 优先:清华 TUNA 镜像的 Homebrew bottle(国内 CDN,实测 ~3MB/s)——仅 macOS/Linux
|
|
@@ -189,51 +214,59 @@ async function downloadCloudflared(binPath, signal) {
|
|
|
189
214
|
const dir = dirname(binPath);
|
|
190
215
|
const tmpFile = join(dir, `cloudflared.download`);
|
|
191
216
|
const isWindows = os === 'windows';
|
|
192
|
-
// 发布资产:Windows 是 .exe(下载即二进制),macOS/Linux 是 .tgz(需解压)
|
|
193
|
-
const asset = isWindows ? `cloudflared-windows-${a}.exe` : `cloudflared-${os}-${a}.tgz`;
|
|
194
217
|
const fetchSignal = signal
|
|
195
218
|
? AbortSignal.any([signal, AbortSignal.timeout(120_000)])
|
|
196
219
|
: AbortSignal.timeout(120_000);
|
|
197
220
|
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
if (!isWindows) {
|
|
201
|
-
const tua = await tsinghuaBottleUrl({ os, a }).catch(() => null);
|
|
202
|
-
if (tua) sources.push({ url: tua, host: 'mirrors.tuna.tsinghua.edu.cn' });
|
|
203
|
-
}
|
|
204
|
-
for (const m of CLOUDFLARED_MIRRORS) sources.push({ url: m(asset), host: hostOf(m(asset)) });
|
|
221
|
+
// 清华 bottle(仅 macOS/Linux 且有对应 bottle);与资产名无关,只算一次
|
|
222
|
+
const bottle = isWindows ? null : await tsinghuaBottleUrl({ os, a }).catch(() => null);
|
|
205
223
|
|
|
224
|
+
// 逐个资产试(linux 首选裸二进制,.tgz 只是回退),每个资产再逐个源试
|
|
225
|
+
const assets = platformAssets();
|
|
206
226
|
let lastErr = null;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
let usedAsset = null;
|
|
228
|
+
|
|
229
|
+
for (let ai = 0; ai < assets.length && usedAsset === null; ai++) {
|
|
230
|
+
const asset = assets[ai];
|
|
231
|
+
const sources = [];
|
|
232
|
+
if (bottle && asset.endsWith('.tgz')) {
|
|
233
|
+
sources.push({ url: bottle, host: 'mirrors.tuna.tsinghua.edu.cn' });
|
|
234
|
+
}
|
|
235
|
+
for (const m of CLOUDFLARED_MIRRORS) sources.push({ url: m(asset), host: hostOf(m(asset)) });
|
|
236
|
+
|
|
237
|
+
for (let i = 0; i < sources.length; i++) {
|
|
238
|
+
const { url, host } = sources[i];
|
|
239
|
+
console.log(`⬇️ 下载 cloudflared(${asset},源 ${i + 1}/${sources.length}:${host})…`);
|
|
240
|
+
try {
|
|
241
|
+
// 多线程分块(官方 GitHub 支持 Range,Windows 50MB 从几分钟降到几十秒);
|
|
242
|
+
// 不支持 Range 的源自动回退单线程
|
|
243
|
+
await downloadFile(url, tmpFile, { signal: fetchSignal });
|
|
244
|
+
// 简单校验:空文件/极小文件视为下载失败(可能是镜像返回了错误页)
|
|
245
|
+
const st = await stat(tmpFile);
|
|
246
|
+
if (st.size < 1024 * 1024) throw new Error(`文件异常小(${st.size} 字节),疑似镜像错误页`);
|
|
247
|
+
usedAsset = asset;
|
|
248
|
+
lastErr = null;
|
|
249
|
+
break; // 下载成功
|
|
250
|
+
} catch (err) {
|
|
251
|
+
lastErr = err;
|
|
252
|
+
await rm(tmpFile, { force: true }).catch(() => {}); // 清掉半截文件
|
|
253
|
+
console.warn(` ⚠️ 源 ${i + 1} 失败:${err?.message ?? err},尝试下一个…`);
|
|
254
|
+
}
|
|
223
255
|
}
|
|
224
256
|
}
|
|
225
|
-
if (
|
|
257
|
+
if (usedAsset === null) {
|
|
226
258
|
throw new Error(
|
|
227
259
|
`cloudflared 下载失败:所有源都不通(最后错误:${lastErr?.message ?? lastErr})。`
|
|
228
260
|
+ (isWindows
|
|
229
|
-
? `Windows 可手动安装后重试:winget install cloudflared;或下载 ${
|
|
230
|
-
:
|
|
261
|
+
? `Windows 可手动安装后重试:winget install cloudflared;或下载 ${assets[0]} 放到 ${dir} 目录 | download failed — try: winget install cloudflared, or put the exe into ${dir}`
|
|
262
|
+
: `也可以自己装好后在 settings.json 写 "cloudflaredPath": "/path/to/cloudflared" 跳过下载(或用环境变量 DSH_POCKET_CLOUDFLARED);或用包管理器安装:apt/dnf install cloudflared | all mirrors failed — install cloudflared manually and set "cloudflaredPath" in settings.json, or: apt/dnf install cloudflared`),
|
|
231
263
|
);
|
|
232
264
|
}
|
|
233
265
|
|
|
234
266
|
let extracted = join(dir, `cloudflared${ext}`);
|
|
235
|
-
|
|
236
|
-
|
|
267
|
+
// 只有 .tgz 才需要解压:Windows 的 .exe 和 linux 的裸二进制本身就可直接执行
|
|
268
|
+
// (linux 用裸二进制还有个好处——不依赖系统装了 tar,见 platformAssets 的说明)
|
|
269
|
+
if (!usedAsset.endsWith('.tgz')) {
|
|
237
270
|
await rename(tmpFile, extracted).catch(async () => {
|
|
238
271
|
await cp(tmpFile, extracted).catch(() => {});
|
|
239
272
|
});
|
|
@@ -246,7 +279,13 @@ async function downloadCloudflared(binPath, signal) {
|
|
|
246
279
|
await new Promise((resolve, reject) => {
|
|
247
280
|
const child = spawn('tar', ['-xzf', tmpFile, '-C', extractDir], { stdio: 'ignore' });
|
|
248
281
|
child.once('exit', (code) => code === 0 ? resolve() : reject(new Error(`cloudflared 解压失败(code=${code})`)));
|
|
249
|
-
|
|
282
|
+
// spawn 失败(Alpine / slim 镜像 / 容器里常见:根本没有 tar)时 err.code 是
|
|
283
|
+
// ENOENT,直接抛出来只有一句 "spawn tar ENOENT",看不出该怎么办
|
|
284
|
+
child.once('error', (err) => reject(
|
|
285
|
+
err?.code === 'ENOENT'
|
|
286
|
+
? new Error(`系统里没有 tar 命令,无法解压 ${usedAsset} —— 可改用 linux 裸二进制(默认已如此)或手动安装后设置 cloudflaredPath | no tar on this system`)
|
|
287
|
+
: err,
|
|
288
|
+
));
|
|
250
289
|
});
|
|
251
290
|
// 找真实的二进制**文件**(排除目录):
|
|
252
291
|
// - GitHub tgz:extractDir/cloudflared
|
package/package.json
CHANGED