zen-gitsync 2.17.6 → 2.17.7
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 +22 -2
- package/package.json +1 -1
- package/scripts/release.js +139 -48
- package/src/cli/ai/agent.js +104 -324
- package/src/cli/ai/cli.integration.test.js +99 -0
- package/src/cli/ai/context.js +90 -0
- package/src/cli/ai/modelSetup.js +38 -0
- package/src/cli/ai/runtime.test.js +152 -0
- package/src/cli/ai/safety.js +27 -4
- package/src/cli/ai/safety.test.js +13 -0
- package/src/cli/ai/telemetry.js +43 -0
- package/src/cli/ai/termui.js +154 -41
- package/src/cli/ai/termui.test.js +63 -14
- package/src/cli/ai/tools.js +27 -5
- package/src/cli/ai/tools.test.js +23 -0
- package/src/cli/ai/transport.js +89 -0
- package/src/cli/ai/turn.js +126 -0
- package/src/config.js +34 -4
- package/src/ui/public/assets/{AgentView-BC3F0F6m.js → AgentView-CZc191Xm.js} +1 -1
- package/src/ui/public/assets/{AppVersionBadge-DV6KuLqb.js → AppVersionBadge-Gs5lUl7P.js} +2 -2
- package/src/ui/public/assets/{BranchSelector-DGXDAOkR.js → BranchSelector-IjaFk0S-.js} +1 -1
- package/src/ui/public/assets/{CommitForm-BYBj7UUa.js → CommitForm-C-sowOXH.js} +3 -3
- package/src/ui/public/assets/{CommonDialog-DWMCeujE.js → CommonDialog-QyptFD1R.js} +1 -1
- package/src/ui/public/assets/{EditorView-D-TEQZso.js → EditorView-CvvluFCY.js} +1 -1
- package/src/ui/public/assets/{FileDiffViewer-C4ZANajI.js → FileDiffViewer-BXVsdCxE.js} +1 -1
- package/src/ui/public/assets/{FlowExecutionViewer-DUv7mBuG.js → FlowExecutionViewer-CRxipXru.js} +1 -1
- package/src/ui/public/assets/{FlowOrchestrationWorkspace-BiSuvzL7.js → FlowOrchestrationWorkspace-AE1ZHTKF.js} +1 -1
- package/src/ui/public/assets/{IconButton-BltPKVbV.js → IconButton-BmSueF_H.js} +1 -1
- package/src/ui/public/assets/{LogList-BY1Haq9r.js → LogList-iaVA-_aC.js} +1 -1
- package/src/ui/public/assets/{MindmapView-BKsvCnYv.js → MindmapView-DcG4jpCh.js} +1 -1
- package/src/ui/public/assets/{MonitorView-B3_NaJRy.js → MonitorView-DO59syvl.js} +1 -1
- package/src/ui/public/assets/{ProjectStartupButton-DP3eDVRp.js → ProjectStartupButton-DYppceyw.js} +1 -1
- package/src/ui/public/assets/{RemoteManagerDialog-Bl9b_iRv.js → RemoteManagerDialog-CDbxG6pZ.js} +1 -1
- package/src/ui/public/assets/{RemoteRepoCard-TQ3DxDnq.js → RemoteRepoCard-Cko2KB8w.js} +1 -1
- package/src/ui/public/assets/{SourceMapView-BF-Mzv3V.js → SourceMapView-CwWPVdz-.js} +1 -1
- package/src/ui/public/assets/{SvgIcon-DxazW7dI.js → SvgIcon-Cf7nOKc8.js} +1 -1
- package/src/ui/public/assets/{TemplateManager-BN6gWFWX.js → TemplateManager-DWPDjWxM.js} +1 -1
- package/src/ui/public/assets/{UserInputNode-CPwvIfP6.js → UserInputNode-YOziN-5A.js} +1 -1
- package/src/ui/public/assets/WorkbenchView-DNnEpIYy.js +13 -0
- package/src/ui/public/assets/WorkbenchView-v3QFn6Ln.css +1 -0
- package/src/ui/public/assets/_plugin-vue_export-helper-CQjg8MXm.js +19 -0
- package/src/ui/public/assets/configStore-C7HOx5S1.js +1 -0
- package/src/ui/public/assets/{gitStore-CYrdjXPU.js → gitStore-DIsBd6n4.js} +1 -1
- package/src/ui/public/assets/index-FOe1cHkh.css +1 -0
- package/src/ui/public/assets/index-MmcnT2Dn.js +24 -0
- package/src/ui/public/assets/{previewSandbox-D_j5PWX1.js → previewSandbox-TKucqLxt.js} +0 -0
- package/src/ui/public/index.html +11 -11
- package/src/ui/server/routes/config.js +8 -3
- package/src/ui/server/routes/fileOpen.js +48 -2
- package/src/ui/server/routes/gAiLauncher.test.js +70 -0
- package/src/ui/server/routes/workbench/index.js +37 -6
- package/src/ui/server/routes/workbench/jobStore.js +4 -0
- package/src/ui/server/routes/workbench/taskRunner.js +276 -96
- package/src/ui/server/routes/workbench/taskRunner.opencode.test.js +126 -0
- package/src/ui/public/assets/WorkbenchView-CDu61WWs.js +0 -13
- package/src/ui/public/assets/WorkbenchView-DEejub9Z.css +0 -1
- package/src/ui/public/assets/_plugin-vue_export-helper-B7A-NgBp.js +0 -19
- package/src/ui/public/assets/configStore-Dx84lvTX.js +0 -1
- package/src/ui/public/assets/index-DNHOYMCg.js +0 -24
- package/src/ui/public/assets/index-Dsfmp6w8.css +0 -1
package/README.md
CHANGED
|
@@ -453,7 +453,18 @@ selection + Enter to confirm** (typing a number also jumps directly; `0` selects
|
|
|
453
453
|
"custom / manual input" entry). Non-TTY environments (CI, piped input) automatically fall back
|
|
454
454
|
to numeric input. `Esc` or `Ctrl+C` cancels the wizard cleanly.
|
|
455
455
|
|
|
456
|
-
In-session commands: `/help`, `/model`, `/addmodel`, `/cd <path>`, `/image [path]`, `/think`, `/clear`, `/exit`.
|
|
456
|
+
In-session commands: `/help`, `/model`, `/addmodel`, `/cd <path>`, `/image [path]`, `/think`, `/tools`, `/stats`, `/new`, `/resume`, `/clear`, `/exit`.
|
|
457
|
+
|
|
458
|
+
Reasoning, tool calls and answers have separate visual sections. Reasoning defaults to a short preview;
|
|
459
|
+
`/think full` shows subsequent reasoning in full, `/think off` hides it, and `/think compact` restores the preview.
|
|
460
|
+
Tool output defaults to a few head/tail lines; `/tools full` shows subsequent tool results in full and
|
|
461
|
+
`/tools compact` restores compact output. These display settings do not reduce model token usage.
|
|
462
|
+
|
|
463
|
+
Each turn ends with completion time, total duration, first-token latency (including reasoning), first-answer
|
|
464
|
+
latency, model/tool durations and provider-reported input/output token usage across all model calls.
|
|
465
|
+
Cache and reasoning tokens are shown as subsets when reported. Missing or partial usage is labelled explicitly;
|
|
466
|
+
`/stats` also shows session totals. `Ctrl+C` stops an active task while keeping the conversation open.
|
|
467
|
+
Progress is saved after each tool result; `/resume` restores the working directory and reported usage.
|
|
457
468
|
|
|
458
469
|
Tool-call budget: one message may trigger up to N tool calls in a row before the turn is
|
|
459
470
|
force-ended with a "max tool iterations reached" notice (send another message to continue).
|
|
@@ -1044,7 +1055,16 @@ $ g ai --model=2 # 使用第 2 个已配置的模型(序号或
|
|
|
1044
1055
|
直接输入数字跳转,`0` = 列表底部的"自定义 / 手动输入");非 TTY 环境下自动回退为数字输入。
|
|
1045
1056
|
`Esc` 或 `Ctrl+C` 一键取消整个向导。
|
|
1046
1057
|
|
|
1047
|
-
会话内命令:`/help`、`/model`、`/addmodel`、`/cd <路径>`、`/image [路径]`、`/think`、`/clear`、`/exit`。
|
|
1058
|
+
会话内命令:`/help`、`/model`、`/addmodel`、`/cd <路径>`、`/image [路径]`、`/think`、`/tools`、`/stats`、`/new`、`/resume`、`/clear`、`/exit`。
|
|
1059
|
+
|
|
1060
|
+
思考、工具调用和回答分区展示。默认只显示简短思考预览,`/think full` 显示后续完整思考,
|
|
1061
|
+
`/think off` 隐藏思考,`/think compact` 恢复预览。工具结果默认保留头尾几行,
|
|
1062
|
+
`/tools full` 显示后续完整工具结果,`/tools compact` 恢复精简。这些显示设置不会减少模型的 Token 消耗。
|
|
1063
|
+
|
|
1064
|
+
每轮结束显示完成时间、总耗时、首响应(含思考首字)、正文等待、模型与工具耗时,以及本轮所有模型调用的
|
|
1065
|
+
输入 / 输出 Token 总量;服务端提供时还显示其中的缓存与推理 Token。用量来自服务端真实返回,
|
|
1066
|
+
缺失或不完整时明确标注;`/stats` 还可查看会话累计用量。执行中按 `Ctrl+C` 停止当前任务并保留会话。
|
|
1067
|
+
每个工具结果后保存进度,`/resume` 同时恢复工作目录和用量统计。
|
|
1048
1068
|
|
|
1049
1069
|
工具调用预算:一条消息内智能体最多连续调用 N 次工具,触顶后本轮被强制结束并提示
|
|
1050
1070
|
"已达单轮最大工具调用次数",再发一条消息即可继续。N 默认 **200**,可在
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zen-gitsync",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.7",
|
|
4
4
|
"description": "Auto commit, scheduled sync, and visual GUI for Git. Run `g` in any repo for one-key commit & push, AI commit messages, scheduled background sync, and a drag-and-drop workflow builder.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
package/scripts/release.js
CHANGED
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
* npm run release -- --dry-run # 只打印计划,不真正改 package.json / commit / publish
|
|
29
29
|
* npm run release -- --poll-interval=20 --poll-timeout=600 # 调自更新重试节奏(秒)
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* 原因见 tarballUrl() / selfUpdateGlobal() 处注释 —— publish 成功不等于
|
|
31
|
+
* 发布后自更新:每轮先探 tarball 能不能取,能取到才调 npm;两条路(`pkg@<版本>` /
|
|
32
|
+
* tarball URL 直连)都试,装上后校验全局版本,失败把原因打出来。
|
|
33
|
+
* 原因见 tarballUrl() / selfUpdateGlobal() 处注释 —— publish 成功不等于
|
|
34
|
+
* packument 立即可见,也不等于 tarball 立即可取(两种先后顺序都实测出现过)。
|
|
34
35
|
*/
|
|
35
36
|
|
|
36
37
|
import fs from 'node:fs'
|
|
@@ -74,19 +75,81 @@ function readNumberArg(name, fallback) {
|
|
|
74
75
|
// `npm view dist-tags` 和 `npm install pkg@ver` 读的是**同一份 packument**:
|
|
75
76
|
// 等它、用它,走的是同一条被缓存的路。只把超时 600s 往上加,只是把失败往后推。
|
|
76
77
|
//
|
|
77
|
-
// 兜底:tarball URL
|
|
78
|
-
//
|
|
79
|
-
//
|
|
78
|
+
// 兜底:tarball URL 是不可变资源,且不经过 packument 那层缓存(见 tarballUrl 注释)。
|
|
79
|
+
// 所以自更新不再把安装卡在 dist-tags 后面,而是"反复尝试安装 + 校验全局版本",
|
|
80
|
+
// 精确版本号解析不到时自动改用 tarball 直连。
|
|
81
|
+
//
|
|
82
|
+
// 又实测(2026-09-20, v2.17.6):tarball 也可能**晚于** packument 就绪 ——
|
|
83
|
+
// dist-tags.latest 已经是 2.17.6,而那个 tarball 的 Last-Modified 晚了 2 分钟,
|
|
84
|
+
// 这 2 分钟里两条路一起失败(对象还不存在),之后又有几分钟边缘缓存回 404。
|
|
85
|
+
// 于是 600s 眼看要耗尽、终端上 25 轮只有"→ npm install"没有任何原因。
|
|
86
|
+
// 对策两条:① 每轮先自己探一次 tarball(isTarballFetchable,GET 而非 HEAD),
|
|
87
|
+
// 取不到就跳过这一轮的 npm 调用并把状态打出来;② 失败原因压一行打出来。
|
|
88
|
+
// 另外单次 npm 调用必须有时间上限(见 INSTALL_TIMEOUT_MS),否则一次网络卡死
|
|
89
|
+
// 就能吃掉整个预算,外面那个 15s 一轮的循环根本没机会重试。
|
|
90
|
+
//
|
|
80
91
|
// 默认 15s 一轮、上限 600s;可用 `--poll-interval=<秒>` / `--poll-timeout=<秒>` 调。
|
|
81
92
|
const POLL_INTERVAL_MS = readNumberArg('--poll-interval', 15) * 1000
|
|
82
93
|
const POLL_TIMEOUT_MS = readNumberArg('--poll-timeout', 600) * 1000
|
|
83
94
|
|
|
84
|
-
// 发布物 tarball 的地址。
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
// 发布物 tarball 的地址。
|
|
96
|
+
//
|
|
97
|
+
// 为什么值得单独走这条路:tarball 按 URL 寻址、内容不可变,不像 packument 那样被
|
|
98
|
+
// max-age 挂在旧内容上 —— "新版本已发布、但 packument 还是旧的"时,它是唯一能立刻装上的路径。
|
|
99
|
+
//
|
|
100
|
+
// 但**两种先后顺序都实测出现过**,别押任何一边:
|
|
101
|
+
// - v2.17.4:packument 滞后 >600s,tarball 早就可取;
|
|
102
|
+
// - v2.17.6:反过来 —— dist-tags 已经是 2.17.6 了,tarball 的 Last-Modified 却晚 2 分钟,
|
|
103
|
+
// 那 2 分钟里 `npm install -g pkg@2.17.6` 和直连 tarball 一起失败(取不到那个对象)。
|
|
104
|
+
// 所以自更新每轮**两条路都试**,并且用 isTarballFetchable() 自己先探一次。
|
|
105
|
+
//
|
|
106
|
+
// `cacheBust=true` 时带一个时间戳查询串:目标 URL 在被发布出来的头几分钟里,
|
|
107
|
+
// CDN 边缘可能还挂着"这个路径 404"的负缓存(实测同一分钟内 HEAD 404 / GET 200),
|
|
108
|
+
// 换一个 URL 就等于绕开那份缓存。
|
|
109
|
+
function tarballUrl(version, cacheBust = false) {
|
|
110
|
+
const base = `${NPM_REGISTRY.replace(/\/$/, '')}/${PKG_NAME}/-/${PKG_NAME}-${version}.tgz`
|
|
111
|
+
return cacheBust ? `${base}?t=${Date.now()}` : base
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 探一次 tarball 到底能不能取到 —— 这比"packument 里有没有这个版本"更接近真相。
|
|
115
|
+
//
|
|
116
|
+
// 三个刻意的选择:
|
|
117
|
+
// ① 用 GET 而不是 HEAD:实测同一个 URL 在同一分钟内 HEAD 返回 404、GET 返回 200,
|
|
118
|
+
// npm 真正下载用的是 GET,所以只认 GET 的结果。
|
|
119
|
+
// ② 带 `Range: bytes=0-0`,只取 1 个字节(服务端支持 Accept-Ranges);万一服务端忽略 Range
|
|
120
|
+
// 回了整包,读完后 cancel() 掉,不会真把 7MB 拖下来。
|
|
121
|
+
// ③ 探不到不算致命:调用方据此跳过这一轮的 npm 调用(省下一次注定失败的 10~40s),
|
|
122
|
+
// 但拿不到 fetch(老 Node)时返回 unknown,让调用方照旧去试。
|
|
123
|
+
async function isTarballFetchable(version, cacheBust) {
|
|
124
|
+
if (typeof fetch !== 'function') return { ok: true, unknown: true }
|
|
125
|
+
try {
|
|
126
|
+
const res = await fetch(tarballUrl(version, cacheBust), {
|
|
127
|
+
headers: { Range: 'bytes=0-0' },
|
|
128
|
+
redirect: 'follow',
|
|
129
|
+
})
|
|
130
|
+
const status = res.status
|
|
131
|
+
try {
|
|
132
|
+
await res.body?.cancel()
|
|
133
|
+
} catch {
|
|
134
|
+
/* 流已结束,忽略 */
|
|
135
|
+
}
|
|
136
|
+
return { ok: status === 200 || status === 206, status }
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return { ok: false, status: 0, error: String(err?.message || err) }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 把 npm 的一大坨输出压成一行,用来在每次失败时给出"为什么",而不是只报"重试中"。
|
|
143
|
+
// 例:`E404 Not Found - GET https://... - Not found`
|
|
144
|
+
function summarizeInstallError(output) {
|
|
145
|
+
if (!output) return '未知错误'
|
|
146
|
+
const lines = String(output)
|
|
147
|
+
.split(/\r?\n/)
|
|
148
|
+
.map((l) => l.trim())
|
|
149
|
+
.filter((l) => l.startsWith('npm error') || l.startsWith('npm ERR!'))
|
|
150
|
+
.map((l) => l.replace(/^npm (error|ERR!)\s*/, ''))
|
|
151
|
+
if (lines.length === 0) return String(output).split(/\r?\n/)[0].slice(0, 160)
|
|
152
|
+
return lines.slice(0, 2).join(' / ').slice(0, 200)
|
|
90
153
|
}
|
|
91
154
|
|
|
92
155
|
// 跨平台 sleep:Node 原生,避免 `sleep N || ping` 的 Windows 兜底 hack
|
|
@@ -549,18 +612,33 @@ function readGlobalInstalledVersion() {
|
|
|
549
612
|
|
|
550
613
|
// 执行一次全局安装。退出码 0 才返回 ok。
|
|
551
614
|
// 输出先兜住不打印:重试期间每失败一次就刷一屏 `npm error` 太吵,
|
|
552
|
-
//
|
|
615
|
+
// 失败时由调用方压成一行打出来(summarizeInstallError),整轮失败才回放全文。
|
|
616
|
+
//
|
|
617
|
+
// 时间必须**有界**:npm 自己的 fetch-timeout 是 5 分钟、还要重试 2 次,
|
|
618
|
+
// 网络卡住时单次 `npm install` 能吃掉十几分钟 —— 实测挂在一个坏代理上,
|
|
619
|
+
// 一次调用就耗掉了整个 600s 预算,外面那个"每 15s 重试一轮"的循环根本没有机会跑第二轮。
|
|
620
|
+
// 所以这里把 fetch 重试收紧、再给 execSync 一个硬上限:让每次尝试**快速失败**,
|
|
621
|
+
// 把"重试"这件事交给外层循环(它本来就在做,而且间隔只有 15s)。
|
|
622
|
+
const INSTALL_TIMEOUT_MS = 180000
|
|
553
623
|
function tryInstallGlobal(spec) {
|
|
554
624
|
console.log(chalk.gray(` → npm install -g ${spec}`))
|
|
555
625
|
try {
|
|
556
|
-
execSync(
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
626
|
+
execSync(
|
|
627
|
+
`npm install -g ${spec} --registry=${NPM_REGISTRY} --prefer-online`
|
|
628
|
+
+ ' --fetch-retries=1 --fetch-retry-mintimeout=3000 --fetch-retry-maxtimeout=10000',
|
|
629
|
+
{
|
|
630
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
631
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
632
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
633
|
+
}
|
|
634
|
+
)
|
|
561
635
|
return { ok: true }
|
|
562
636
|
} catch (err) {
|
|
637
|
+
// 超时会被 execSync 杀掉(带 SIGTERM),stderr 可能是空的 —— 补一句可读的原因
|
|
563
638
|
const out = `${err.stdout ?? ''}${err.stderr ?? ''}`.trim()
|
|
639
|
+
if (!out && (err.killed || err.signal)) {
|
|
640
|
+
return { ok: false, output: `安装超时(${INSTALL_TIMEOUT_MS / 1000}s 到点被终止)` }
|
|
641
|
+
}
|
|
564
642
|
return { ok: false, output: out || String(err.message || err) }
|
|
565
643
|
}
|
|
566
644
|
}
|
|
@@ -572,13 +650,18 @@ function tryInstallGlobal(spec) {
|
|
|
572
650
|
// packument 被 CDN 缓存(max-age=300,实测还能更久)时两边一起滞后 ——
|
|
573
651
|
// 等待阶段白等 600s,超时后再装照样 ETARGET,等于什么都没解决。
|
|
574
652
|
//
|
|
575
|
-
// 所以顺序反过来:先"尝试装"
|
|
576
|
-
// ①
|
|
577
|
-
//
|
|
653
|
+
// 所以顺序反过来:先"尝试装",装不上才等。每轮:
|
|
654
|
+
// ① 先探 tarball 能不能取(isTarballFetchable)—— 取不到就别浪费一次 npm 调用,
|
|
655
|
+
// 并且把"还取不到 HTTP 404"如实打出来:这是"到底卡在哪"的唯一线索;
|
|
656
|
+
// ② 取得到就按两条路试,任一条装上且校验通过即结束:
|
|
657
|
+
// `pkg@<version>`(packument 已同步时的常规路径)/ tarball URL 直连;
|
|
658
|
+
// ③ 失败原因压成一行打出来(summarizeInstallError)。**别再默默重试**——
|
|
659
|
+
// v2.17.6 那次用户看到的就是"一直在执行安装命令"、25 轮里一句原因都没有,
|
|
660
|
+
// 而真实原因是那几分钟 tarball 还没对外可取(见 tarballUrl 注释)。
|
|
578
661
|
async function selfUpdateGlobal(version) {
|
|
579
662
|
console.log(chalk.blue('\n=== 发布后自更新全局版本 ==='))
|
|
580
|
-
console.log(chalk.gray('publish 成功 ≠ packument
|
|
581
|
-
console.log(chalk.gray('不空等 dist-tags
|
|
663
|
+
console.log(chalk.gray('publish 成功 ≠ packument 立即可见、也 ≠ tarball 立即可取(两者先后顺序随机)。'))
|
|
664
|
+
console.log(chalk.gray('不空等 dist-tags:每轮先探 tarball,能取到才调 npm,失败会打印原因。'))
|
|
582
665
|
console.log(chalk.gray(
|
|
583
666
|
`间隔 ${POLL_INTERVAL_MS / 1000}s,上限 ${POLL_TIMEOUT_MS / 1000}s`
|
|
584
667
|
+ '(可用 --poll-interval / --poll-timeout 调)。'
|
|
@@ -586,39 +669,47 @@ async function selfUpdateGlobal(version) {
|
|
|
586
669
|
|
|
587
670
|
const startedAt = Date.now()
|
|
588
671
|
const deadline = startedAt + POLL_TIMEOUT_MS
|
|
589
|
-
const tarball = tarballUrl(version)
|
|
590
672
|
let attempt = 0
|
|
591
673
|
let lastOutput = ''
|
|
592
674
|
|
|
593
675
|
for (;;) {
|
|
594
676
|
attempt += 1
|
|
595
677
|
const packumentSynced = readLatestDistTag() === version
|
|
596
|
-
//
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
678
|
+
// 第 2 轮起给 tarball 地址加时间戳,绕开 CDN 可能挂着的"这条路径 404"负缓存
|
|
679
|
+
const bust = attempt > 1
|
|
680
|
+
const tarball = tarballUrl(version, bust)
|
|
681
|
+
const probe = await isTarballFetchable(version, bust)
|
|
600
682
|
|
|
601
683
|
console.log(chalk.gray(
|
|
602
|
-
`第 ${attempt} 次尝试(dist-tags.latest${packumentSynced ? '已同步' : '仍是旧版本'}
|
|
684
|
+
`第 ${attempt} 次尝试(dist-tags.latest${packumentSynced ? '已同步' : '仍是旧版本'},`
|
|
685
|
+
+ ` tarball ${probe.ok ? '已可取' : `还取不到${probe.status ? ` HTTP ${probe.status}` : ''}`})...`
|
|
603
686
|
))
|
|
604
687
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
688
|
+
if (probe.ok) {
|
|
689
|
+
// packument 已同步 → 先走常规精确版本;还没同步 → tarball 优先,争取一轮命中
|
|
690
|
+
const targets = packumentSynced
|
|
691
|
+
? [`${PKG_NAME}@${version}`, tarball]
|
|
692
|
+
: [tarball, `${PKG_NAME}@${version}`]
|
|
693
|
+
|
|
694
|
+
for (const spec of targets) {
|
|
695
|
+
const res = tryInstallGlobal(spec)
|
|
696
|
+
if (!res.ok) {
|
|
697
|
+
lastOutput = res.output
|
|
698
|
+
console.log(chalk.yellow(` ✗ ${summarizeInstallError(res.output)}`))
|
|
699
|
+
continue
|
|
700
|
+
}
|
|
701
|
+
const installed = readGlobalInstalledVersion()
|
|
702
|
+
if (installed === version) {
|
|
703
|
+
const cost = ((Date.now() - startedAt) / 1000).toFixed(1)
|
|
704
|
+
console.log(chalk.green(
|
|
705
|
+
`全局已更新到 ${PKG_NAME}@${version}(第 ${attempt} 次尝试,耗时 ${cost}s)`
|
|
706
|
+
))
|
|
707
|
+
return
|
|
708
|
+
}
|
|
709
|
+
// 退出码 0 但版本不对(极少见):按失败处理,继续下一轮
|
|
710
|
+
lastOutput = `安装命令退出码 0,但全局版本读到 ${installed ?? '未知'}`
|
|
711
|
+
console.log(chalk.yellow(` 安装命令成功,但全局版本是 ${installed ?? '未知'},继续重试`))
|
|
618
712
|
}
|
|
619
|
-
// 退出码 0 但版本不对(极少见):按失败处理,继续下一轮
|
|
620
|
-
lastOutput = `安装命令退出码 0,但全局版本读到 ${installed ?? '未知'}`
|
|
621
|
-
console.log(chalk.yellow(` 安装命令成功,但全局版本是 ${installed ?? '未知'},继续重试`))
|
|
622
713
|
}
|
|
623
714
|
|
|
624
715
|
const remain = deadline - Date.now()
|
|
@@ -628,8 +719,8 @@ async function selfUpdateGlobal(version) {
|
|
|
628
719
|
console.error(chalk.gray(
|
|
629
720
|
'可稍后手动重试:\n'
|
|
630
721
|
+ ` npm install -g ${PKG_NAME}@${version}\n`
|
|
631
|
-
+ '
|
|
632
|
-
+ ` npm install -g ${
|
|
722
|
+
+ '若报 ETARGET / E404(registry 元数据与 tarball 还没对齐),直连 tarball:\n'
|
|
723
|
+
+ ` npm install -g ${tarballUrl(version)}`
|
|
633
724
|
))
|
|
634
725
|
return
|
|
635
726
|
}
|
|
@@ -646,7 +737,7 @@ async function publishToNpm(version) {
|
|
|
646
737
|
if (DRY_RUN) {
|
|
647
738
|
console.log(chalk.yellow(`[dry-run] npm publish --registry=${NPM_REGISTRY}`))
|
|
648
739
|
console.log(chalk.yellow(
|
|
649
|
-
`[dry-run]
|
|
740
|
+
`[dry-run] 反复探测并安装,直到全局 ${PKG_NAME}@${version} 校验通过(见 selfUpdateGlobal)`
|
|
650
741
|
))
|
|
651
742
|
return
|
|
652
743
|
}
|