Skip to content

Commit c6127fd

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/memory
2 parents 6406a41 + 8bbbbc7 commit c6127fd

31 files changed

Lines changed: 928 additions & 101 deletions

File tree

‎.github/workflows/publish.yml‎

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,28 @@ jobs:
5959
- name: Ensure zip and tar (per-platform binary archives)
6060
run: sudo apt-get update && sudo apt-get install -y zip
6161

62+
# bun build --compile leaves an invalid darwin linker signature. rcodesign
63+
# re-signs those Mach-O files on the Linux runner (binary-codesign.mjs).
64+
- name: Install rcodesign (ad-hoc sign darwin binaries)
65+
run: |
66+
set -euo pipefail
67+
RCODESIGN_VERSION=0.29.0
68+
asset="apple-codesign-${RCODESIGN_VERSION}-x86_64-unknown-linux-musl.tar.gz"
69+
curl -fsSL -o "/tmp/${asset}" \
70+
"https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign/${RCODESIGN_VERSION}/${asset}"
71+
tar -xzf "/tmp/${asset}" -C /tmp
72+
sudo install -m 755 \
73+
"/tmp/apple-codesign-${RCODESIGN_VERSION}-x86_64-unknown-linux-musl/rcodesign" \
74+
/usr/local/bin/rcodesign
75+
rcodesign --version
76+
6277
- run: pnpm install --frozen-lockfile
6378

6479
# Binary compile uses `bun build --compile` CLI (not Bun.build API).
6580
# Keep this pin in sync with any local smoke tests of binary-compile.mjs.
6681
- uses: oven-sh/setup-bun@v2
6782
with:
68-
bun-version: "1.2.19"
83+
bun-version: "1.3.14"
6984

7085
- name: publish-stable
7186
env:
@@ -79,6 +94,8 @@ jobs:
7994
# Bucket / prefix / allowlists live in the FC function.
8095
FC_TRIGGER_URL: ${{ vars.FC_TRIGGER_URL }}
8196
FC_RELEASE_AUDIENCE: ${{ vars.FC_RELEASE_AUDIENCE }}
97+
# Script order: git tag, then binary GitHub Release + OSS, then npm.
98+
# OSS upload can fail on a network blip; npm must not succeed first.
8299
run: node tools/release/publish-stable.mjs ${{ inputs.package == 'knowledge-studio-cli' && '--knowledge' || '' }}
83100

84101
publish-channel:
@@ -117,13 +134,28 @@ jobs:
117134
- name: Ensure zip and tar (per-platform binary archives)
118135
run: sudo apt-get update && sudo apt-get install -y zip
119136

137+
# bun build --compile leaves an invalid darwin linker signature. rcodesign
138+
# re-signs those Mach-O files on the Linux runner (binary-codesign.mjs).
139+
- name: Install rcodesign (ad-hoc sign darwin binaries)
140+
run: |
141+
set -euo pipefail
142+
RCODESIGN_VERSION=0.29.0
143+
asset="apple-codesign-${RCODESIGN_VERSION}-x86_64-unknown-linux-musl.tar.gz"
144+
curl -fsSL -o "/tmp/${asset}" \
145+
"https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign/${RCODESIGN_VERSION}/${asset}"
146+
tar -xzf "/tmp/${asset}" -C /tmp
147+
sudo install -m 755 \
148+
"/tmp/apple-codesign-${RCODESIGN_VERSION}-x86_64-unknown-linux-musl/rcodesign" \
149+
/usr/local/bin/rcodesign
150+
rcodesign --version
151+
120152
- run: pnpm install --frozen-lockfile
121153

122154
# Binary compile uses `bun build --compile` CLI (not Bun.build API).
123155
# Keep this pin in sync with any local smoke tests of binary-compile.mjs.
124156
- uses: oven-sh/setup-bun@v2
125157
with:
126-
bun-version: "1.2.19"
158+
bun-version: "1.3.14"
127159

128160
- name: publish-channel
129161
env:
@@ -132,6 +164,8 @@ jobs:
132164
# credentials in this repo (see publish-stable env notes).
133165
FC_TRIGGER_URL: ${{ vars.FC_TRIGGER_URL }}
134166
FC_RELEASE_AUDIENCE: ${{ vars.FC_RELEASE_AUDIENCE }}
167+
# Script order: binary GitHub Release + OSS, then npm.
168+
# OSS upload can fail on a network blip; npm must not succeed first.
135169
run: node tools/release/publish-channel.mjs ${{ inputs.package == 'knowledge-studio-cli' && '--knowledge' || '' }} --channel "${{ inputs.channel }}"
136170

137171
# bailian-kb-dsh is the dsh plugin (downstream host adapter): independent version,

‎CHANGELOG.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
66

77
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
88

9+
## [2.1.0] - 2026-09-28
10+
11+
### Added
12+
13+
- **Text fine-tuning parameters** — `bl finetune text create` now accepts job name, priority, LoRA, evaluation, checkpoint, dataset-split, and data-augmentation parameters. `bl finetune get` returns expanded job metadata and hyperparameters.
14+
15+
### Fixed
16+
17+
- **Windows Skill installation** — Handle legacy path limits and update installed Skills in place when running agents keep directories open.
18+
19+
### Internal
20+
21+
- Publish binaries before npm `latest` and ad-hoc sign macOS release binaries.
22+
923
## [2.0.0] - 2026-09-22
1024

1125
### Added

‎CHANGELOG.zh.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@
66

77
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
88

9+
## [2.1.0] - 2026-09-28
10+
11+
### 新增
12+
13+
- **文本微调参数** —— `bl finetune text create` 现在支持任务名、优先级、LoRA、评估、Checkpoint、数据集切分和数据增强参数;`bl finetune get` 返回更完整的任务元数据与超参数。
14+
15+
### 修复
16+
17+
- **Windows Skill 安装** —— 处理传统路径长度限制,并可在运行中的 Agent 占用目录时原地更新已安装的 Skill。
18+
19+
### 内部
20+
21+
- 调整发布顺序,在 npm `latest` 前发布二进制,并对 macOS 发布二进制进行 ad-hoc 签名。
22+
923
## [2.0.0] - 2026-09-22
1024

1125
### 新增

‎docs/agents/publish.md‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515

1616
```text
1717
publish-stable.mjs / publish-channel.mjs ← 唯一发版入口
18-
├─ npm(pnpm publish)
19-
└─ binary(lib/binary-release
20-
→ binary-build
21-
→ gh-release
22-
→ oss-direct-upload → FC release 通道)
18+
├─ binary 先走(lib/binary-release
19+
│ → binary-build → gh-release
20+
│ → oss-direct-upload → FC release 通道)
21+
└─ OSS 成功后再 npm(pnpm publish)
2322
```
2423

24+
bailian-cli 的二进制(GitHub Release + OSS)在 npm 之前。OSS 上传受网络波动可能失败;若 npm 先成功,会出现 latest 已发出但 CDN 没有对应二进制。stable 仍要先推 `v<version>` tag,因为 GitHub Release 首次创建带 `--verify-tag`。`knowledge-studio-cli` / `--skip-binary` 没有二进制,仍只发 npm。
25+
2526
`tools/release/lib/binary-release.mjs` 等是实现,一般不要单独当发版入口(调试可用)。
2627

2728
### OSS 通道:FC 预签名上传(仓库不持有任何 OSS 凭据)
@@ -73,15 +74,15 @@ workflow 的 `channel` 输入**只决定 npm dist-tag**(如 `mcp` / `plugin` /
7374
1. 在 GitHub 触发 Publish workflow,mode 选 `channel`,channel 填 npm dist-tag 名:
7475
- **`bailian-cli`**:npm 发到该 tag;二进制同时刷新 CDN `sync-release.json`(与 tag 名无关)。本机验证:`BAILIAN_CHANNEL=sync-release`。**先发二进制(zip+tar.gz 上齐)再发静态仓 `install.sh`**,避免新脚本去拉还不存在的 `.tar.gz`。
7576
- **`knowledge-studio-cli`**:仅 npm(自动跳过 binary,不碰 `sync-release.json`)
76-
2. CI 自动:生成 `0.0.0-beta-<sha7>-<YYYYMMDDHHMM>`(UTC 到分钟;同 commit 同分钟重跑会覆盖同号)→ 临时 bump → 自检 → **npm 发到 dist-tag** →(bailian-cli)**Bun 编二进制 + GH prerelease + 覆盖 `sync-release.json`** → 还原 package.json
77+
2. CI 自动:生成 `0.0.0-beta-<sha7>-<YYYYMMDDHHMM>`(UTC 到分钟;同 commit 同分钟重跑会覆盖同号)→ 临时 bump → 自检 →(bailian-cli)**Bun 编二进制 + GH prerelease + 覆盖 `sync-release.json`** → **npm 发到 dist-tag** → 还原 package.json
7778
3. 对应脚本:`tools/release/publish-channel.mjs`
7879

7980
### stable 发布
8081

8182
1. 确保当前 release tooling 覆盖的包(`tools/release/lib/packages.mjs`)已升到目标版本且一致;当前基础集合为 `packages/core` / `packages/runtime` / `packages/commands` / `packages/cli`,`knowledge-studio-cli` 发布会额外包含 `packages/kscli`
8283
2. 在 GitHub 触发 Publish workflow,package 选目标包集合,mode 选 `stable`
8384
3. 需要 production environment 审批人批准
84-
4. CI 自动:自检 → **npm 发到 latest** → **推送 git tag `v<ver>`** → **Bun 编二进制并创建/更新 GitHub Release**(每平台 `.zip`,darwin/linux 额外 `.tar.gz`)→(bailian-cli)维护 CDN **`manifest.json`**(unix 资产含 `tar` / `tarSha256`,`file` 仍为 zip)→ 完成
85+
4. CI 自动:自检 → **推送 git tag `v<ver>`** → **Bun 编二进制并创建/更新 GitHub Release**(每平台 `.zip`,darwin/linux 额外 `.tar.gz`)→(bailian-cli)维护 CDN **`manifest.json`**(unix 资产含 `tar` / `tarSha256`,`file` 仍为 zip)→ **npm 发到 latest** → 完成
8586
5. 如果所选发布集合的当前版本已全部存在于 npm,stable 发布会失败并提示先升级版本号;如果只有部分包已发布,CI 会继续补发缺失包
8687
6. 对应脚本:`tools/release/publish-stable.mjs`
8788

@@ -114,6 +115,7 @@ node tools/release/publish-channel.mjs --channel test --knowledge --dry-run
114115
- **GitHub Release**:`contents: write` + `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}`(stable / channel 均需)
115116
- **Node 版本**:24(npm 11.5+ 才支持 OIDC token 交换)
116117
- **Bun**:`oven-sh/setup-bun`,版本钉死在 workflow 中
118+
- **darwin 签名**:编完后 `binary-codesign.mjs` 对 darwin Mach-O 做 ad-hoc 重签,并按 Apple 的方式重算每一页 SHA-256(末页不补零)。对不上就中止发布。macOS runner 额外跑 `codesign --verify --strict`。Linux runner 用 `rcodesign 0.29.0 sign`;不要用 `rcodesign verify`(会拒绝 Apple 已通过的 ad-hoc 签名)。漏签时 Apple Silicon 会在启动时 SIGKILL
117119
- **Actions 版本**:checkout/setup-node/pnpm-action 均为 v6(Node 24 兼容)
118120
- **npm 配置**:当前 release tooling 发布的包(`bailian-cli-core` / `bailian-cli-runtime` / `bailian-cli-commands` / `bailian-cli` / `knowledge-studio-cli`)的 Trusted Publisher 指向 `modelstudioai/cli` 的 `publish.yml`;新增发布包时同步 npm Trusted Publisher
119121

@@ -160,4 +162,6 @@ node tools/release/publish-channel.mjs --channel test --knowledge --dry-run
160162
| CI 用 Node 22(npm 10)跑 publish | npm 10 不支持 OIDC token 交换,publish 报 404 |
161163
| stable 发布前没有升级版本号 | 所选发布集合的版本已全部存在于 npm,CI 明确报错并要求先升级版本号 |
162164
| channel job 缺少 `contents: write` | `gh release create` 失败 |
165+
| darwin 二进制编完后没有 ad-hoc 重签 | Apple Silicon / macOS 27 对失效的 linker 签名直接 SIGKILL(`Killed: 9`) |
166+
| npm 先于二进制 / OSS 成功 | latest 已发出,CDN 上没有对应 zip/tar.gz;OSS 网络失败时无法回滚 npm |
163167
| stable 未先推 tag 就建 Release | `--verify-tag` 失败 |

‎packages/cli/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
55
"keywords": [
66
"agent",

‎packages/cli/postinstall.js‎

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
*/
2424
import { createHash } from "node:crypto";
2525
import {
26+
copyFileSync,
2627
createWriteStream,
2728
existsSync,
29+
lstatSync,
2830
mkdirSync,
2931
readdirSync,
3032
readFileSync,
@@ -33,7 +35,7 @@ import {
3335
writeFileSync,
3436
} from "node:fs";
3537
import { homedir } from "node:os";
36-
import { dirname, join } from "node:path";
38+
import { dirname, join, relative } from "node:path";
3739
import { Readable } from "node:stream";
3840
import { pipeline } from "node:stream/promises";
3941
import { createBrotliDecompress } from "node:zlib";
@@ -52,6 +54,7 @@ const OBJECT_FILE_RE = /^sha256-[0-9a-f]{64}\.tar\.br$/;
5254

5355
const INDEX_TIMEOUT_MS = 3000;
5456
const DOWNLOAD_TIMEOUT_MS = 30000;
57+
const WINDOWS_LEGACY_MAX_PATH = 259;
5558

5659
function getConfigDir() {
5760
if (process.env.BAILIAN_CONFIG_DIR) return process.env.BAILIAN_CONFIG_DIR;
@@ -170,24 +173,115 @@ function computeDirContentHash(dir) {
170173
return `sha256:${hash.digest("hex")}`;
171174
}
172175

176+
function listTreeEntries(rootDir) {
177+
const entries = [];
178+
const visit = (currentDir) => {
179+
for (const entry of readdirSync(currentDir, { withFileTypes: true }).sort((left, right) =>
180+
left.name.localeCompare(right.name),
181+
)) {
182+
const path = join(currentDir, entry.name);
183+
const relativePath = relative(rootDir, path);
184+
if (entry.isDirectory()) {
185+
entries.push({ relativePath, type: "directory" });
186+
visit(path);
187+
} else if (entry.isFile()) {
188+
entries.push({ relativePath, type: "file" });
189+
}
190+
}
191+
};
192+
visit(rootDir);
193+
return entries;
194+
}
195+
196+
function assertWindowsCompatiblePaths(sourceDir, projectedRoot) {
197+
if (process.platform !== "win32") return;
198+
for (const entry of listTreeEntries(sourceDir)) {
199+
const pathLength = join(projectedRoot, entry.relativePath).length;
200+
if (pathLength > WINDOWS_LEGACY_MAX_PATH) {
201+
throw new Error(
202+
`Windows-incompatible skill path (${pathLength} characters): ${entry.relativePath}. ` +
203+
"The skill package must shorten this path before it can be installed safely.",
204+
);
205+
}
206+
}
207+
}
208+
209+
/** Replace children without renaming a root directory held open by a Windows agent. */
210+
function reconcileDirectoryContents(sourceDir, destDir) {
211+
const sourceEntries = listTreeEntries(sourceDir);
212+
const expectedPaths = new Set(sourceEntries.map((entry) => entry.relativePath));
213+
mkdirSync(destDir, { recursive: true });
214+
215+
for (const entry of sourceEntries) {
216+
const sourcePath = join(sourceDir, entry.relativePath);
217+
const destPath = join(destDir, entry.relativePath);
218+
if (existsSync(destPath)) {
219+
const destStat = lstatSync(destPath);
220+
const typeMatches = entry.type === "directory" ? destStat.isDirectory() : destStat.isFile();
221+
if (!typeMatches || destStat.isSymbolicLink()) {
222+
rmSync(destPath, { recursive: true, force: true });
223+
}
224+
}
225+
if (entry.type === "directory") {
226+
mkdirSync(destPath, { recursive: true });
227+
} else {
228+
mkdirSync(dirname(destPath), { recursive: true });
229+
copyFileSync(sourcePath, destPath);
230+
}
231+
}
232+
233+
const staleEntries = listTreeEntries(destDir)
234+
.filter((entry) => !expectedPaths.has(entry.relativePath))
235+
.sort((left, right) => right.relativePath.length - left.relativePath.length);
236+
for (const entry of staleEntries) {
237+
rmSync(join(destDir, entry.relativePath), { recursive: true, force: true });
238+
}
239+
}
240+
241+
function cleanupBackup(backup) {
242+
try {
243+
if (existsSync(backup)) rmSync(backup, { recursive: true, force: true });
244+
} catch {
245+
/* keep the backup on disk rather than report a completed swap as failed */
246+
}
247+
}
248+
173249
/** Atomic swap: tmpDir (same volume) → catalogDir. */
174250
function atomicSwap(tmpDir, catalogDir) {
175251
mkdirSync(dirname(catalogDir), { recursive: true });
176252
const backup = `${catalogDir}.old-${Date.now()}`;
177-
if (existsSync(catalogDir)) renameSync(catalogDir, backup);
253+
if (existsSync(catalogDir)) {
254+
try {
255+
renameSync(catalogDir, backup);
256+
} catch (error) {
257+
if (process.platform !== "win32" || (error?.code !== "EPERM" && error?.code !== "EBUSY")) {
258+
throw error;
259+
}
260+
try {
261+
reconcileDirectoryContents(catalogDir, backup);
262+
reconcileDirectoryContents(tmpDir, catalogDir);
263+
cleanupBackup(backup);
264+
return;
265+
} catch (fallbackError) {
266+
try {
267+
if (existsSync(backup)) reconcileDirectoryContents(backup, catalogDir);
268+
} catch {
269+
/* retain backup for manual recovery if an open file also blocks rollback */
270+
}
271+
throw new Error(
272+
`Skill directory is in use and could not be updated in place. Close running agent hosts and retry. ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
273+
{ cause: fallbackError },
274+
);
275+
}
276+
}
277+
}
178278
try {
179279
renameSync(tmpDir, catalogDir);
180280
} catch (err) {
181281
if (existsSync(backup) && !existsSync(catalogDir)) renameSync(backup, catalogDir);
182282
throw err;
183283
}
184-
// Best-effort cleanup (symmetric with core skills/extract.ts): the swap already
185-
// succeeded, so a backup deletion failure must not fail the pre-download
186-
try {
187-
if (existsSync(backup)) rmSync(backup, { recursive: true, force: true });
188-
} catch {
189-
/* keep the backup on disk rather than report a completed swap as failed */
190-
}
284+
cleanupBackup(backup);
191285
}
192286

193287
async function main() {
@@ -208,6 +302,7 @@ async function main() {
208302
try {
209303
mkdirSync(tmpDir, { recursive: true });
210304
await extractTarBr(tarBuf, tmpDir);
305+
assertWindowsCompatiblePaths(tmpDir, catalogDir);
211306
// Symmetric with layer 2 (core installer): reject archive/index fingerprint mismatch
212307
// before touching the canonical dir
213308
if (entry.contentHash.startsWith("sha256:")) {

‎packages/commands/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli-commands",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {

0 commit comments

Comments
 (0)