@jonathanleelx/meddle 0.5.0-beta.10 → 0.5.0-beta.11
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 +2 -0
- package/bin/commands/update.js +4 -0
- package/bin/lib/update-check.js +67 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ irm https://raw.githubusercontent.com/JonathanLee-LX/meddle/main/scripts/install
|
|
|
36
36
|
|
|
37
37
|
二进制产物随 [GitHub Release](https://github.com/JonathanLee-LX/meddle/releases) 发布,也可手动下载。安装目录默认 `~/.local/bin`(已有 `~/.meddle/bin` 旧安装时原地升级),可通过 `MEDDLE_BIN_DIR` 环境变量自定义。`meddle update` 始终更新到当前运行二进制所在目录。
|
|
38
38
|
|
|
39
|
+
**私有仓库:** `meddle update`(含 `meddle update --beta`)/ 安装脚本下载 Release 资产时会读取 `GH_TOKEN`(优先)或 `GITHUB_TOKEN`(`repo` 或 fine-grained `contents:read`)。私有仓安装后升级也需要设置 token;未设置时会表现为 404(checksum sidecar missing)。也可手动:`gh release download -R JonathanLee-LX/meddle -p 'meddle-*'`。
|
|
40
|
+
|
|
39
41
|
### 命令
|
|
40
42
|
|
|
41
43
|
```bash
|
package/bin/commands/update.js
CHANGED
|
@@ -77,6 +77,10 @@ meddle update - 检查并升级 meddle
|
|
|
77
77
|
meddle update --auto on|off 启用/禁用自动更新 (默认关闭)
|
|
78
78
|
meddle update status 查看安装方式、当前版本和自动更新开关
|
|
79
79
|
meddle update --help, -h 显示帮助
|
|
80
|
+
|
|
81
|
+
私有仓库二进制更新需要设置 GH_TOKEN 或 GITHUB_TOKEN(优先 GH_TOKEN;
|
|
82
|
+
权限:repo,或 fine-grained contents:read)。也可:
|
|
83
|
+
gh release download -R JonathanLee-LX/meddle -p 'meddle-*'
|
|
80
84
|
`)
|
|
81
85
|
}
|
|
82
86
|
|
package/bin/lib/update-check.js
CHANGED
|
@@ -27,6 +27,58 @@ const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000
|
|
|
27
27
|
const DEFAULT_TIMEOUT_MS = 5000
|
|
28
28
|
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 120 * 1000
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a GitHub token for private release downloads / latest redirects.
|
|
32
|
+
* Precedence: explicit argument → GH_TOKEN → GITHUB_TOKEN.
|
|
33
|
+
* @param {string} [explicit]
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function resolveGithubToken(explicit) {
|
|
37
|
+
if (typeof explicit === 'string' && explicit.trim()) return explicit.trim()
|
|
38
|
+
const gh = process.env.GH_TOKEN
|
|
39
|
+
if (typeof gh === 'string' && gh.trim()) return gh.trim()
|
|
40
|
+
const github = process.env.GITHUB_TOKEN
|
|
41
|
+
if (typeof github === 'string' && github.trim()) return github.trim()
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} token
|
|
47
|
+
* @returns {Record<string, string>}
|
|
48
|
+
*/
|
|
49
|
+
function githubAuthHeaders(token) {
|
|
50
|
+
if (!token) return {}
|
|
51
|
+
// GitHub accepts Bearer for PATs / fine-grained tokens on release asset URLs.
|
|
52
|
+
return { Authorization: `Bearer ${token}` }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function privateReleaseAuthHint() {
|
|
56
|
+
return (
|
|
57
|
+
'Private GitHub releases require GH_TOKEN or GITHUB_TOKEN '
|
|
58
|
+
+ '(scope: repo, or fine-grained contents:read). '
|
|
59
|
+
+ `Or: gh release download -R ${REPO} -p 'meddle-*'`
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {'checksum' | 'download'} kind
|
|
65
|
+
* @param {number} status
|
|
66
|
+
* @param {boolean} hadToken
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
function formatDownloadStatusError(kind, status, hadToken) {
|
|
70
|
+
const base = kind === 'checksum'
|
|
71
|
+
? `checksum sidecar missing (${status})`
|
|
72
|
+
: `download failed (${status})`
|
|
73
|
+
if ((status === 404 || status === 401 || status === 403) && !hadToken) {
|
|
74
|
+
return `${base}. ${privateReleaseAuthHint()}`
|
|
75
|
+
}
|
|
76
|
+
if ((status === 401 || status === 403) && hadToken) {
|
|
77
|
+
return `${base}. Token may lack repo/contents:read access to ${REPO}`
|
|
78
|
+
}
|
|
79
|
+
return base
|
|
80
|
+
}
|
|
81
|
+
|
|
30
82
|
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/
|
|
31
83
|
|
|
32
84
|
/**
|
|
@@ -120,12 +172,14 @@ async function getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs, distTag
|
|
|
120
172
|
* @param {{ fetchImpl?: Function, latestUrl?: string, timeoutMs?: number }} opts
|
|
121
173
|
* @returns {Promise<string>}
|
|
122
174
|
*/
|
|
123
|
-
async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs } = {}) {
|
|
175
|
+
async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs, githubToken } = {}) {
|
|
124
176
|
const doFetch = fetchImpl || fetch
|
|
125
177
|
const url = latestUrl || process.env.MEDDLE_GITHUB_LATEST_URL || DEFAULT_GITHUB_LATEST_URL
|
|
178
|
+
const token = resolveGithubToken(githubToken)
|
|
126
179
|
const response = await doFetch(url, {
|
|
127
180
|
redirect: 'manual',
|
|
128
181
|
signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS),
|
|
182
|
+
headers: githubAuthHeaders(token),
|
|
129
183
|
})
|
|
130
184
|
if (response.status < 300 || response.status >= 400) {
|
|
131
185
|
throw new Error(`GitHub latest release did not redirect (${response.status})`)
|
|
@@ -287,7 +341,7 @@ function sha256Hex(buffer) {
|
|
|
287
341
|
* baseUrl?: string, fetchImpl?: Function, timeoutMs?: number,
|
|
288
342
|
* retries?: number, retryDelayMs?: number,
|
|
289
343
|
* onProgress?: Function, onAttempt?: Function,
|
|
290
|
-
* fsImpl?: object }} opts
|
|
344
|
+
* githubToken?: string, fsImpl?: object }} opts
|
|
291
345
|
* @returns {Promise<{ installed: string, backup: string, version: string }>}
|
|
292
346
|
*/
|
|
293
347
|
async function downloadBinaryAsset(opts) {
|
|
@@ -300,6 +354,8 @@ async function downloadBinaryAsset(opts) {
|
|
|
300
354
|
const retryDelayMs = opts.retryDelayMs === undefined ? 1000 : opts.retryDelayMs
|
|
301
355
|
const onProgress = typeof opts.onProgress === 'function' ? opts.onProgress : () => {}
|
|
302
356
|
const onAttempt = typeof opts.onAttempt === 'function' ? opts.onAttempt : () => {}
|
|
357
|
+
const token = resolveGithubToken(opts.githubToken)
|
|
358
|
+
const authHeaders = githubAuthHeaders(token)
|
|
303
359
|
|
|
304
360
|
const asset = getAssetName({ platform, arch })
|
|
305
361
|
const assetUrl = `${baseUrl}/v${version}/${asset}`
|
|
@@ -327,8 +383,8 @@ async function downloadBinaryAsset(opts) {
|
|
|
327
383
|
}
|
|
328
384
|
resetIdle()
|
|
329
385
|
try {
|
|
330
|
-
const response = await doFetch(assetUrl, { signal: controller.signal })
|
|
331
|
-
if (!response.ok) throw new Error(
|
|
386
|
+
const response = await doFetch(assetUrl, { signal: controller.signal, headers: authHeaders })
|
|
387
|
+
if (!response.ok) throw new Error(formatDownloadStatusError('download', response.status, Boolean(token)))
|
|
332
388
|
const total = Number(response.headers.get('content-length')) || 0
|
|
333
389
|
const chunks = []
|
|
334
390
|
let received = 0
|
|
@@ -365,8 +421,11 @@ async function downloadBinaryAsset(opts) {
|
|
|
365
421
|
// download — otherwise the sidecar body stream aborts before it is read
|
|
366
422
|
// and the whole install fails with "operation aborted" right after the
|
|
367
423
|
// payload reached 100%.
|
|
368
|
-
const shaResponse = await doFetch(shaUrl, {
|
|
369
|
-
|
|
424
|
+
const shaResponse = await doFetch(shaUrl, {
|
|
425
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
426
|
+
headers: authHeaders,
|
|
427
|
+
})
|
|
428
|
+
if (!shaResponse.ok) throw new Error(formatDownloadStatusError('checksum', shaResponse.status, Boolean(token)))
|
|
370
429
|
const shaText = await shaResponse.text()
|
|
371
430
|
|
|
372
431
|
const payload = await fetchPayload()
|
|
@@ -559,5 +618,7 @@ module.exports = {
|
|
|
559
618
|
setAutoUpdate,
|
|
560
619
|
resolveUpdateBinDir,
|
|
561
620
|
runAsyncUpdateCheck,
|
|
621
|
+
resolveGithubToken,
|
|
622
|
+
githubAuthHeaders,
|
|
562
623
|
DEFAULT_DOWNLOAD_TIMEOUT_MS,
|
|
563
624
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonathanleelx/meddle",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.11",
|
|
4
4
|
"description": "A simple development proxy server with custom routing rules, mock responses, HTTPS interception, and a plugin system",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "index.js",
|