dsh-gokifu-compact 1.11.1 → 1.11.2
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 +41 -0
- package/README.md +2 -2
- package/cordis.patch.yml +28 -0
- package/package.json +8 -2
- package/scripts/install.mjs +131 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 1.11.2 — 2026-10-30
|
|
4
|
+
|
|
5
|
+
### 本包改成 DSH bundle(UI 挂载不再需要用户手改 YAML)
|
|
6
|
+
|
|
7
|
+
**这是第一个 UI bundle 版。** 以前装完包要**手改 profile 的 `cordis.patch.yml`** 加一条
|
|
8
|
+
`- insert: [{id: gokifu-ui, name: 'dsh-gokifu-compact'}]` 才能挂上 UI。手改有两个后患:
|
|
9
|
+
|
|
10
|
+
1. **双挂载**:用户升级包后手工行还留着,和 bundle 通道同时挂 ⇒ 两个 host 半注册同一个 `/gokifu/api` 前缀 ⇒ **启动即失败**(duplicate prefix route);
|
|
11
|
+
2. **没挂载**:`dsh plugin add` 不会自己补挂载行 ⇒ 「装好了但没生效」,且**没有任何报错**。
|
|
12
|
+
|
|
13
|
+
**改法**
|
|
14
|
+
|
|
15
|
+
- 新增 **`cordis.patch.yml`(28 行)**:只做一件事 —— `- insert: [{id: gokifu-ui, name: 'dsh-gokifu-compact'}]`。
|
|
16
|
+
**引擎不在这里**:压缩后端必须挂在 **agent preset 的 compaction group 内部**,profile/bundle 级 patch 打不到 `preset.config.plugins`;那一行仍由 `install.mjs` 改 preset。
|
|
17
|
+
`id` 保持 `gokifu-ui`(与历史手工行一致)⇒ 从手工通道切过来时,日志/文档/doctor 口径都不用改。
|
|
18
|
+
- `package.json`:`dsh.bundle.patch = "./cordis.patch.yml"` · `dsh.manifestVersion = 1` · `files`/`exports` 补 `cordis.patch.yml`;`main` 与 `dsh.client` 不变。
|
|
19
|
+
- **`scripts/install.mjs` 改 bundle 优先**:
|
|
20
|
+
- `findManualUiRow` / `removeManualUiRow`:定位并(带备份)删除 profile 里的旧手工行
|
|
21
|
+
- `patchProfileBundles()`:把本包**追加**进 `dsh.profile.bundles`,保持既有顺序;写前备份 profile/`package.json`
|
|
22
|
+
- `ensureBundleChannel()` = **先加 bundle、再删手工行** —— 顺序保证任何中断都不会出现「两条都没有」的空窗
|
|
23
|
+
- `git`/`registry` 规格**不改写成 `link`**
|
|
24
|
+
- **`test/verify-install.mjs` 判据升级**:旧判据「bundles 里没有它」→ **「UI 挂载通道二选一,不双挂载」**:两条都在 = 红(双挂载)、都没有 = 红(没挂载);检查项总数仍 **16**。
|
|
25
|
+
- **`test/ws-install.test.mjs`**:健康夹具改走 bundle 通道;**新增 S11 四种态**(bundle-only 绿 / manual-only 绿 / both 红 / neither 红)。
|
|
26
|
+
- **`test/install-link.test.mjs`** 夹具同步。
|
|
27
|
+
- **新增 `test/bundle.test.mjs`(6 条)**:B-1 声明齐全 · B-2 patch 只插 UI 一行 · B-3 `findManualUiRow` 三态 · B-4 bundle 优先+备份+幂等 · B-5 dry-run 零写 · B-6 registry 规格不被改写。
|
|
28
|
+
- **变异自检(4 条,逐条实测红)**:删 `dsh.bundle` ⇒ B-1;patch 塞引擎行 ⇒ B-2;安装器不删手工行 ⇒ B-4;判据退回「bundle 里不许有」⇒ S11 + S10e-1。
|
|
29
|
+
|
|
30
|
+
### 验证
|
|
31
|
+
|
|
32
|
+
- 全量:**443 passed / 0 failed**(36 个测试文件;436 + 6 bundle + 1)
|
|
33
|
+
- `rooms` **15/0** · `ws-install` **14/0** · `install-link` **29/0** · `bundle` **6/0** · `verify-install` **16/16**
|
|
34
|
+
- `bench 2000`:**351 tokens** / 1.423 ms(81,260 / 386,945 字符、1.41 MB、2001 节点)
|
|
35
|
+
- `npm pack --dry-run --json`:**70 文件**(69 + `cordis.patch.yml`)/ 558,113 B;`test/`·`tmp/` 命中 0
|
|
36
|
+
- live 当前仍是**手工通道**(`bundle=无|手工行=有`)—— `verify-install` 按「二选一」判据通过
|
|
37
|
+
|
|
38
|
+
**未验证(必须知道)**
|
|
39
|
+
|
|
40
|
+
- **未跑 live install**:`--apply` 到真实 profile 的 bundle 切换没有执行过(只在 dry-run 与夹具上验证)。
|
|
41
|
+
- **未启 DSH**:bundle 通道在真实插件树里的加载行为未观测。
|
|
42
|
+
- 从手工通道切换到 bundle 的**中断恢复**只在单测里模拟(顺序保证 + 备份),真机未演练。
|
|
43
|
+
|
|
3
44
|
## 1.11.1 — 2026-10-30
|
|
4
45
|
|
|
5
46
|
### P6-C 落地:未落地尝试不再被当成残留(**热清单新增 OPTIONS 段**)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-GOkifu-compact
|
|
2
2
|
|
|
3
|
-
> 当前版本:**`1.11.
|
|
3
|
+
> 当前版本:**`1.11.2`** · npm [`dsh-gokifu-compact`](https://www.npmjs.com/package/dsh-gokifu-compact) · [GitHub Releases](https://github.com/MYCF711/dsh-GOkifu-compact/releases) · 更新记录见 [CHANGELOG.md](CHANGELOG.md)
|
|
4
4
|
|
|
5
5
|
**DSH 的棋谱式上下文压缩后端 —— 把「历史 → 一段摘要」换成「历史 → 可复盘的棋谱 DAG + 每步只有 323 tokens 的热清单」。**
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
热清单 323 tokens (2000 边压力测试;旧版全量渲染 28,800)
|
|
9
9
|
失败保留 8 / 8 (真实模型对照;官方 8 段模板 4.67 / 8)
|
|
10
10
|
清单开销 0.2% of prompt (v1.3.1 起;事故会话的旧版是 21.3%)
|
|
11
|
-
验证
|
|
11
|
+
验证 443 项单测 · 真实包 8/8 · 真实进程 fold 3~6/6
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
GO = 围棋,kifu = 棋谱,compact = 压缩。一局棋下完,棋谱上不只有落子顺序,还有每一手的评语。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# dsh-gokifu-compact bundle patch
|
|
2
|
+
#
|
|
3
|
+
# 这是本包的 `dsh.bundle.patch` 层(`package.json` 的 `dsh.bundle.patch` 指向本文件)。
|
|
4
|
+
# 它只做一件事:**把 UI 的 host 半插进插件树**(`/gokifu/api` + 详情页数据/写配置)。
|
|
5
|
+
#
|
|
6
|
+
# 为什么现在改成 bundle:以前靠 `dsh plugin add` 之后**手改 profile 的 cordis.patch.yml**
|
|
7
|
+
# 加一条 `- insert: [{id: gokifu-ui, name: 'dsh-gokifu-compact'}]`。手改有两个后患:
|
|
8
|
+
# ① 用户升级包之后,这条手工行留在 profile 里,和 bundle 通道**同时**挂载 ⇒ 两个 host 半
|
|
9
|
+
# 注册同一个 `/gokifu/api` 前缀 ⇒ 启动即失败(duplicate prefix route);
|
|
10
|
+
# ② `dsh plugin add` 不会自己补挂载行 ⇒ 「装好了但没生效」,且没有任何报错。
|
|
11
|
+
# 声明成本包的 patch 层之后,profile 只要把本包写进 `dsh.profile.bundles`,补丁会被自动合并,
|
|
12
|
+
# 与手工行**语义完全等价**(同样是 `- insert:` 一行),但不再需要用户改任何 YAML。
|
|
13
|
+
#
|
|
14
|
+
# ⚠ 两条通道**只能二选一**:
|
|
15
|
+
# · bundle 通道:`dsh.profile.bundles` 含 `dsh-gokifu-compact`,profile 自己的
|
|
16
|
+
# `cordis.patch.yml` 里**不得**再有 `id: gokifu-ui` 的手工行;
|
|
17
|
+
# · 手工通道:profile 自己的 `cordis.patch.yml` 有那一行,且 bundles 里**不得**含本包。
|
|
18
|
+
# `scripts/install.mjs` 切到 bundle 通道时会**备份并删除**旧手工行;
|
|
19
|
+
# `test/verify-install.mjs` 有「二选一、不双挂载」的机器判据(两条都在 = 红)。
|
|
20
|
+
#
|
|
21
|
+
# 引擎(压缩后端)不在这里:它必须挂在 **agent preset 的 compaction group 内部**,
|
|
22
|
+
# profile/bundle 级 patch 打不到 preset 的 `config.plugins`(见 docs/DSH-技术测绘.md §7.6)。
|
|
23
|
+
# 那一行由 `scripts/install.mjs` 改 preset 完成;本文件只管 UI。
|
|
24
|
+
#
|
|
25
|
+
# id 保持 `gokifu-ui`(与历史手工行一致):从手工通道切过来时,日志/文档/doctor 都不用改口径。
|
|
26
|
+
- insert:
|
|
27
|
+
- id: gokifu-ui
|
|
28
|
+
name: 'dsh-gokifu-compact'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gokifu-compact",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "DSH context-compression backend with a go game record (kifu) DAG: node = position, edge = attempted move + result. Incremental fold (each span summarized once), failure memory that survives compaction, transposition detection (chess-engine style), and a tiny hot ledger rebuilt every step.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"./engine": "./src/engine.mjs",
|
|
21
21
|
"./client": "./lib/client.js",
|
|
22
|
-
"./package.json": "./package.json"
|
|
22
|
+
"./package.json": "./package.json",
|
|
23
|
+
"./cordis.patch.yml": "./cordis.patch.yml"
|
|
23
24
|
},
|
|
24
25
|
"engines": {
|
|
25
26
|
"node": ">=20"
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"docs",
|
|
34
35
|
"evidence",
|
|
35
36
|
"scripts",
|
|
37
|
+
"cordis.patch.yml",
|
|
36
38
|
"CHANGELOG.md",
|
|
37
39
|
"QUICKSTART.md",
|
|
38
40
|
"LICENSE"
|
|
@@ -79,6 +81,10 @@
|
|
|
79
81
|
"dsh-gokifu-compact": "./scripts/cli.mjs"
|
|
80
82
|
},
|
|
81
83
|
"dsh": {
|
|
84
|
+
"bundle": {
|
|
85
|
+
"patch": "./cordis.patch.yml"
|
|
86
|
+
},
|
|
87
|
+
"manifestVersion": 1,
|
|
82
88
|
"client": {
|
|
83
89
|
"platform": "web",
|
|
84
90
|
"inject": []
|
package/scripts/install.mjs
CHANGED
|
@@ -158,6 +158,127 @@ export function patchProfileForUi(profileDir, log = () => {}, dryRun = false) {
|
|
|
158
158
|
return { changed: true, backup };
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
|
|
162
|
+
/* ─────────────── ②·b bundle 通道(task-118) ─────────────── */
|
|
163
|
+
|
|
164
|
+
/** 手工行的 id(历史值,保持兼容:日志/doctor/文档都用这个口径)。 */
|
|
165
|
+
const MANUAL_UI_ID = "gokifu-ui";
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 在 profile 的 cordis.patch.yml 里定位手工 UI 行(`- insert:` + `- id: gokifu-ui`)。
|
|
169
|
+
*
|
|
170
|
+
* 纯函数:只算行号,不改任何东西(方便测试与 dry-run)。找不到返回 null。
|
|
171
|
+
* 结构不认识时返回 `{ unsupported: true }` —— 调用方必须**拒绝改**,绝不能猜着删。
|
|
172
|
+
* @returns {{startLine:number,endLine:number,start:number,end:number,wholeBlock:boolean}|{unsupported:true,reason:string}|null}
|
|
173
|
+
*/
|
|
174
|
+
export function findManualUiRow(text) {
|
|
175
|
+
const lines = String(text).split("\n");
|
|
176
|
+
const childIdx = lines.findIndex((l) => new RegExp("^\\s*- id:\\s*" + MANUAL_UI_ID + "\\s*$").test(l));
|
|
177
|
+
if (childIdx < 0) return null;
|
|
178
|
+
// 往上找父级 `- insert:`(最多 12 行内;缩进必须比子行浅)
|
|
179
|
+
const childIndent = lines[childIdx].match(/^\s*/)[0].length;
|
|
180
|
+
let parentIdx = -1;
|
|
181
|
+
for (let i = childIdx - 1; i >= Math.max(0, childIdx - 12); i -= 1) {
|
|
182
|
+
const m = /^(\s*)- insert:\s*$/.exec(lines[i]);
|
|
183
|
+
if (m && m[1].length < childIndent) {
|
|
184
|
+
parentIdx = i;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (parentIdx < 0) return { unsupported: true, reason: "找不到 `- insert:` 父行" };
|
|
189
|
+
// 父块范围 = 从父行到下一条**缩进 ≤ 父缩进**的非空行
|
|
190
|
+
const parentIndent = lines[parentIdx].match(/^\s*/)[0].length;
|
|
191
|
+
let endIdx = lines.length;
|
|
192
|
+
for (let i = parentIdx + 1; i < lines.length; i += 1) {
|
|
193
|
+
if (lines[i].trim().length === 0) continue;
|
|
194
|
+
if (lines[i].match(/^\s*/)[0].length <= parentIndent && /^\s*- /.test(lines[i])) {
|
|
195
|
+
endIdx = i;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 这个 insert 块里还有别的子行吗?(有 ⇒ 只删本行;没有 ⇒ 整块删)
|
|
200
|
+
const otherChildren = lines
|
|
201
|
+
.slice(parentIdx + 1, endIdx)
|
|
202
|
+
.some((l) => l.match(/^\s*/)[0].length === childIndent && /^\s*- /.test(l) && !new RegExp("^\\s*- id:\\s*" + MANUAL_UI_ID + "\\s*$").test(l));
|
|
203
|
+
return { startLine: childIdx, endLine: childIdx, start: parentIdx, end: endIdx - 1, wholeBlock: !otherChildren };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 从 profile 的 cordis.patch.yml 删除手工 UI 行(**带备份**)。
|
|
208
|
+
* 只在 `wholeBlock` 时连注释一起删:注释是与那一行配套的说明,留着会变成误导性死注释。
|
|
209
|
+
*/
|
|
210
|
+
export function removeManualUiRow(profileDir, log = () => {}, dryRun = false) {
|
|
211
|
+
const file = nodePath.join(profileDir, "cordis.patch.yml");
|
|
212
|
+
if (!nodeFs.existsSync(file)) return { changed: false, reason: "没有 profile cordis.patch.yml" };
|
|
213
|
+
const text = nodeFs.readFileSync(file, "utf8");
|
|
214
|
+
const found = findManualUiRow(text);
|
|
215
|
+
if (found === null) return { changed: false, reason: "没有手工 UI 行" };
|
|
216
|
+
if (found.unsupported) return { changed: false, reason: `手工行结构不认识(${found.reason}),拒绝改` };
|
|
217
|
+
const lines = text.split("\n");
|
|
218
|
+
let start = found.start;
|
|
219
|
+
let end = found.end;
|
|
220
|
+
if (found.wholeBlock) {
|
|
221
|
+
// 连带删除紧贴其上、提到 gokifu 的连续注释行
|
|
222
|
+
while (start - 1 >= 0 && /^\s*#/.test(lines[start - 1]) && /gokifu|上下文压缩/i.test(lines[start - 1])) start -= 1;
|
|
223
|
+
} else {
|
|
224
|
+
start = found.startLine;
|
|
225
|
+
end = found.endLine;
|
|
226
|
+
}
|
|
227
|
+
let next = [...lines.slice(0, start), ...lines.slice(end + 1)].join("\n");
|
|
228
|
+
next = next.replace(/\n{3,}/g, "\n\n");
|
|
229
|
+
if (dryRun) {
|
|
230
|
+
log(" [dry] profile cordis.patch.yml 会删掉手工 gokifu-ui 行(改走 bundle 通道)");
|
|
231
|
+
return { changed: true, dryRun: true };
|
|
232
|
+
}
|
|
233
|
+
const backup = `${file}.bak-before-gokifu-ui-${Date.now()}`;
|
|
234
|
+
nodeFs.copyFileSync(file, backup);
|
|
235
|
+
nodeFs.writeFileSync(file, next, "utf8");
|
|
236
|
+
log(` ✓ profile cordis.patch.yml 已删手工 gokifu-ui 行(备份 ${nodePath.basename(backup)})`);
|
|
237
|
+
return { changed: true, backup };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 确保 profile 的 `dsh.profile.bundles` 含本包(**追加在末尾**,保持既有顺序)。
|
|
242
|
+
* 为什么这是主通道:bundle 声明了 patch 层 ⇒ 装载器自动合并本包的 cordis.patch.yml,
|
|
243
|
+
* 与手写那一行语义等价,但用户不用改 YAML,升级也不会漏。
|
|
244
|
+
*/
|
|
245
|
+
export function patchProfileBundles(profileDir, log = () => {}, dryRun = false) {
|
|
246
|
+
const file = nodePath.join(profileDir, "package.json");
|
|
247
|
+
if (!nodeFs.existsSync(file)) return { changed: false, reason: "没有 profile/package.json" };
|
|
248
|
+
let pkg;
|
|
249
|
+
try {
|
|
250
|
+
pkg = JSON.parse(nodeFs.readFileSync(file, "utf8"));
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return { changed: false, reason: `profile/package.json 不是合法 JSON(${error.message})` };
|
|
253
|
+
}
|
|
254
|
+
const dsh = pkg.dsh && typeof pkg.dsh === "object" ? pkg.dsh : {};
|
|
255
|
+
const profile = dsh.profile && typeof dsh.profile === "object" ? dsh.profile : {};
|
|
256
|
+
const bundles = Array.isArray(profile.bundles) ? profile.bundles : [];
|
|
257
|
+
if (bundles.includes(PKG_NAME)) return { changed: false, reason: "已在 dsh.profile.bundles" };
|
|
258
|
+
const next = [...bundles, PKG_NAME];
|
|
259
|
+
if (dryRun) {
|
|
260
|
+
log(` [dry] profile/package.json 的 dsh.profile.bundles 会追加 ${PKG_NAME}(现 ${bundles.length} 项)`);
|
|
261
|
+
return { changed: true, dryRun: true };
|
|
262
|
+
}
|
|
263
|
+
nodeFs.copyFileSync(file, `${file}.bak-before-gokifu-${Date.now()}`);
|
|
264
|
+
pkg.dsh = { ...dsh, profile: { ...profile, bundles: next } };
|
|
265
|
+
nodeFs.writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
266
|
+
log(` ✓ profile/package.json 的 dsh.profile.bundles 已追加 ${PKG_NAME}(${bundles.length} → ${next.length} 项)`);
|
|
267
|
+
return { changed: true, bundles: next };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* **bundle 优先**:先把本包写进 `dsh.profile.bundles`,再删掉旧的手工 gokifu-ui 行(带备份)。
|
|
272
|
+
* 顺序不能反:先加 bundle 再删手工行,任何一步中断都**不会**出现「两条通道都没有」的空窗。
|
|
273
|
+
*/
|
|
274
|
+
export function ensureBundleChannel(profileDir, log = () => {}, dryRun = false) {
|
|
275
|
+
const bundle = patchProfileBundles(profileDir, log, dryRun);
|
|
276
|
+
if (bundle.changed === false) log(` · UI(bundle 通道):${bundle.reason}`);
|
|
277
|
+
const manual = removeManualUiRow(profileDir, log, dryRun);
|
|
278
|
+
if (manual.changed === false && manual.reason !== "没有手工 UI 行") log(` · 手工行:${manual.reason}`);
|
|
279
|
+
return { bundle, manual };
|
|
280
|
+
}
|
|
281
|
+
|
|
161
282
|
export function run(argv = process.argv.slice(2)) {
|
|
162
283
|
const opts = parseArgs(argv);
|
|
163
284
|
const { dryRun, profile, dshHome, log } = opts;
|
|
@@ -234,6 +355,11 @@ export function run(argv = process.argv.slice(2)) {
|
|
|
234
355
|
samePath(spec.target.realPath, PROJECT_DIR);
|
|
235
356
|
if (correct) {
|
|
236
357
|
log(` · profile/package.json 的 ${PKG_NAME} 已指向本目录:${declared}`);
|
|
358
|
+
} else if (spec !== null && spec.kind === "other") {
|
|
359
|
+
// ★ task-118:用户把手上的依赖写成 registry / git / tarball 规格时**不许改写成 link**。
|
|
360
|
+
// 那是用户的选择(例如从 npm 装正式版),改写会让「装的是哪个」不可追溯;
|
|
361
|
+
// 本地调试请自己显式写 file:/link:,安装器不替用户做这个决定。
|
|
362
|
+
log(` · profile/package.json 的 ${PKG_NAME} 是 ${declared} 规格(非 file:/link:)—— 保持原样,不改写成 link`);
|
|
237
363
|
} else if (dryRun) {
|
|
238
364
|
log(` [dry] 会${declared === null ? "加" : "修"} profile/package.json 的 ${PKG_NAME}:${declared ?? "(无)"} → ${want}`);
|
|
239
365
|
} else {
|
|
@@ -299,9 +425,11 @@ export function run(argv = process.argv.slice(2)) {
|
|
|
299
425
|
log(` ✓ ${t.file} 已换后端(备份 ${nodePath.basename(backup)})`);
|
|
300
426
|
}
|
|
301
427
|
|
|
302
|
-
/* ③ 挂 UI 的 host
|
|
303
|
-
|
|
304
|
-
|
|
428
|
+
/* ③ 挂 UI 的 host 半:**bundle 通道优先**(本包自带 cordis.patch.yml,声明在 dsh.bundle.patch)。
|
|
429
|
+
旧做法是手改 profile 的 cordis.patch.yml 加 `- insert: {id: gokifu-ui}`;那条路已废弃 ——
|
|
430
|
+
升级后手工行与 bundle 会同时挂载,两个 host 半抢同一个 /gokifu/api 前缀,启动直接失败。
|
|
431
|
+
⇒ 这里把本包写进 dsh.profile.bundles,并**备份后删除**旧手工行,保证任何时候只有一条通道。 */
|
|
432
|
+
ensureBundleChannel(profileDir, (m) => log(m), dryRun);
|
|
305
433
|
|
|
306
434
|
if (dryRun) {
|
|
307
435
|
log("\n这是预演,什么都没改。真装去掉 --dry-run。");
|