bailian-cli 1.13.1 → 1.14.0
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 +10 -3
- package/README.zh.md +11 -2
- package/dist/bailian.mjs +1 -1
- package/package.json +9 -6
- package/postinstall.js +213 -0
package/README.md
CHANGED
|
@@ -77,11 +77,17 @@ No timeline scrubbing. No frame-by-frame editing. Just one sentence → one vide
|
|
|
77
77
|
## Installation
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
|
+
# Recommended — no Node required
|
|
81
|
+
curl -fsSL https://bailian.aliyun.com/cli/install.sh | bash
|
|
82
|
+
|
|
83
|
+
# Windows (PowerShell)
|
|
84
|
+
irm https://bailian.aliyun.com/cli/install.ps1 | iex
|
|
85
|
+
|
|
86
|
+
# Node users / developers (Node.js >= 18.17)
|
|
80
87
|
npm install -g bailian-cli
|
|
81
|
-
npx skills add modelstudioai/cli --all -g
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
>
|
|
90
|
+
> Binary install does not require Node.js. `npm install -g` remains fully supported.
|
|
85
91
|
|
|
86
92
|
## Quick Start
|
|
87
93
|
|
|
@@ -210,8 +216,9 @@ bl config set --key base_url --value https://dashscope-us.aliyuncs.com
|
|
|
210
216
|
bl config set --key default_text_model --value qwen-turbo
|
|
211
217
|
bl config set --key timeout --value 600
|
|
212
218
|
|
|
213
|
-
# Self-update to latest version
|
|
219
|
+
# Self-update to latest or a specific version
|
|
214
220
|
bl update
|
|
221
|
+
bl update --to 0.1.14
|
|
215
222
|
```
|
|
216
223
|
|
|
217
224
|
Config file location: `~/.bailian/config.json`
|
package/README.zh.md
CHANGED
|
@@ -75,11 +75,17 @@ _专为 AI Agent 打造,每个命令均可作为结构化工具调用。_
|
|
|
75
75
|
## 安装
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
+
# 推荐 — 无需本机 Node.js
|
|
79
|
+
curl -fsSL https://bailian.aliyun.com/cli/install.sh | bash
|
|
80
|
+
|
|
81
|
+
# Windows(PowerShell)
|
|
82
|
+
irm https://bailian.aliyun.com/cli/install.ps1 | iex
|
|
83
|
+
|
|
84
|
+
# Node 用户 / 开发者(需要 Node.js >= 18.17)
|
|
78
85
|
npm install -g bailian-cli
|
|
79
|
-
npx skills add modelstudioai/cli --all -g
|
|
80
86
|
```
|
|
81
87
|
|
|
82
|
-
>
|
|
88
|
+
> 二进制安装不依赖 Node.js。`npm install -g` 长期保留。
|
|
83
89
|
|
|
84
90
|
## 快速开始
|
|
85
91
|
|
|
@@ -210,6 +216,9 @@ bl config set --key timeout --value 600
|
|
|
210
216
|
|
|
211
217
|
# 自更新到最新版本
|
|
212
218
|
bl update
|
|
219
|
+
|
|
220
|
+
# 安装指定版本
|
|
221
|
+
bl update --to 0.1.14
|
|
213
222
|
```
|
|
214
223
|
|
|
215
224
|
配置文件位置:`~/.bailian/config.json`
|
package/dist/bailian.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import{createCli as e}from"bailian-cli-runtime";import{advisorRecommend as t,appCall as n,appList as r,authGenerateAccessToken as i,authLogin as a,authLogout as o,authStatus as s,configAgent as c,configList as l,configSet as u,configShow as d,configUi as f,configUse as p,consoleCall as m,datasetDelete as h,datasetGet as g,datasetList as _,datasetUpload as v,datasetValidate as y,deployAudioCreate as b,deployDelete as x,deployGet as S,deployImageCreate as C,deployList as w,deployModels as T,deployScale as E,deployTextCreate as D,deployUpdate as O,fileUpload as k,finetuneAudioCreate as A,finetuneCancel as j,finetuneCapability as M,finetuneCheckpoints as N,finetuneDelete as P,finetuneExport as F,finetuneGet as I,finetuneImageCreate as L,finetuneList as R,finetuneLogs as z,finetuneTextCreate as B,finetuneWatch as V,imageEdit as H,imageGenerate as U,knowledgeChat as W,knowledgeRetrieve as G,knowledgeSearch as K,managedAgentApply as q,managedAgentDestroy as J,managedAgentInit as Y,managedAgentPlan as X,managedAgentSessionCreate as Z,managedAgentSessionDelete as Q,managedAgentSessionEvents as $,managedAgentSessionGet as ee,managedAgentSessionList as te,managedAgentSessionRun as ne,managedAgentSessionSend as re,managedAgentSkillList as ie,managedAgentStateImport as ae,managedAgentStateList as oe,managedAgentStateRm as se,managedAgentStateShow as ce,managedAgentValidate as le,mcpCall as ue,mcpList as de,mcpTools as fe,memoryAdd as pe,memoryDelete as me,memoryList as he,memoryProfileCreate as ge,memoryProfileGet as _e,memorySearch as ve,memoryUpdate as ye,modelList as be,pipelineRun as xe,pipelineValidate as Se,pluginInstall as Ce,pluginLink as we,pluginList as Te,pluginRemove as Ee,quotaCheck as De,quotaHistory as Oe,quotaList as ke,quotaRequest as Ae,searchWeb as je,
|
|
3
|
+
import{createCli as e}from"bailian-cli-runtime";import{advisorRecommend as t,appCall as n,appList as r,authGenerateAccessToken as i,authLogin as a,authLogout as o,authStatus as s,configAgent as c,configList as l,configSet as u,configShow as d,configUi as f,configUse as p,consoleCall as m,datasetDelete as h,datasetGet as g,datasetList as _,datasetUpload as v,datasetValidate as y,deployAudioCreate as b,deployDelete as x,deployGet as S,deployImageCreate as C,deployList as w,deployModels as T,deployScale as E,deployTextCreate as D,deployUpdate as O,fileUpload as k,finetuneAudioCreate as A,finetuneCancel as j,finetuneCapability as M,finetuneCheckpoints as N,finetuneDelete as P,finetuneExport as F,finetuneGet as I,finetuneImageCreate as L,finetuneList as R,finetuneLogs as z,finetuneTextCreate as B,finetuneWatch as V,imageEdit as H,imageGenerate as U,knowledgeChat as W,knowledgeRetrieve as G,knowledgeSearch as K,managedAgentApply as q,managedAgentDestroy as J,managedAgentInit as Y,managedAgentPlan as X,managedAgentSessionCreate as Z,managedAgentSessionDelete as Q,managedAgentSessionEvents as $,managedAgentSessionGet as ee,managedAgentSessionList as te,managedAgentSessionRun as ne,managedAgentSessionSend as re,managedAgentSkillList as ie,managedAgentStateImport as ae,managedAgentStateList as oe,managedAgentStateRm as se,managedAgentStateShow as ce,managedAgentValidate as le,mcpCall as ue,mcpList as de,mcpTools as fe,memoryAdd as pe,memoryDelete as me,memoryList as he,memoryProfileCreate as ge,memoryProfileGet as _e,memorySearch as ve,memoryUpdate as ye,modelList as be,pipelineRun as xe,pipelineValidate as Se,pluginInstall as Ce,pluginLink as we,pluginList as Te,pluginRemove as Ee,quotaCheck as De,quotaHistory as Oe,quotaList as ke,quotaRequest as Ae,searchWeb as je,skillAdd as Me,skillList as Ne,skillRemove as Pe,skillUpdate as Fe,speechRecognize as Ie,speechSynthesize as Le,textChat as Re,textOmni as ze,tokenPlanAddMember as Be,tokenPlanAssignSeats as Ve,tokenPlanCreateKey as He,tokenPlanListSeats as Ue,update as We,usageFree as Ge,usageFreetier as Ke,usageStats as qe,usageSummary as Je,videoDownload as Ye,videoEdit as Xe,videoGenerate as Ze,videoRef as Qe,videoTaskGet as $e,visionDescribe as et,workspaceInit as tt,workspaceList as nt}from"bailian-cli-commands";e({"auth login":a,"auth status":s,"auth logout":o,"auth generate-access-token":i,"text chat":Re,omni:ze,"image generate":U,"image edit":H,"video generate":Ze,"video edit":Xe,"video ref":Qe,"video task get":$e,"video download":Ye,"vision describe":et,"config show":d,"config set":u,"config list":l,"config use":p,"config ui":f,"config agent":c,update:We,"app call":n,"app list":r,"memory add":pe,"memory search":ve,"memory list":he,"memory update":ye,"memory delete":me,"memory profile create":ge,"memory profile get":_e,"knowledge retrieve":G,"knowledge search":K,"knowledge chat":W,"mcp call":ue,"mcp list":de,"mcp tools":fe,"search web":je,"speech synthesize":Le,"speech recognize":Ie,"file upload":k,"console call":m,"usage free":Ge,"usage freetier":Ke,"usage stats":qe,"usage summary":Je,"pipeline run":xe,"pipeline validate":Se,"advisor recommend":t,"model list":be,"workspace list":nt,"quota list":ke,"quota request":Ae,"quota history":Oe,"quota check":De,"dataset upload":v,"dataset list":_,"dataset get":g,"dataset delete":h,"dataset validate":y,"finetune text create":B,"finetune audio create":A,"finetune image create":L,"finetune list":R,"finetune get":I,"finetune cancel":j,"finetune delete":P,"finetune logs":z,"finetune checkpoints":N,"finetune export":F,"finetune watch":V,"finetune capability":M,"deploy text create":D,"deploy audio create":b,"deploy image create":C,"deploy list":w,"deploy get":S,"deploy models":T,"deploy scale":E,"deploy update":O,"deploy delete":x,"token-plan list-seats":Ue,"token-plan create-key":He,"token-plan assign-seats":Ve,"token-plan add-member":Be,"workspace init":tt,"plugin install":Ce,"plugin link":we,"plugin list":Te,"plugin remove":Ee,"skill add":Me,"skill update":Fe,"skill remove":Pe,"skill list":Ne,"managed-agent init":Y,"managed-agent validate":le,"managed-agent plan":X,"managed-agent apply":q,"managed-agent destroy":J,"managed-agent state list":oe,"managed-agent state show":ce,"managed-agent state rm":se,"managed-agent state import":ae,"managed-agent session create":Z,"managed-agent session list":te,"managed-agent session get":ee,"managed-agent session delete":Q,"managed-agent session run":ne,"managed-agent session send":re,"managed-agent session events":$,"managed-agent skill-list":ie},{binName:`bl`,version:`1.14.0`,clientName:`bailian-cli`,npmPackage:`bailian-cli`,quickStartTasks:[`Help me generate a set of Amazon e-commerce main images for baseball caps (white background + lifestyle shots + model wear shots)`,`Help me generate a 3-minute humorous crosstalk audio clip`,`Help me generate a Little Red Riding Hood picture-book PDF (with illustrations)`,`Help me analyze this video and write a Xiaohongshu-style post`],commandPacks:{supported:{"@ali/bailian-plugin-agent":{commandPrefixes:[`agent`],credentialAccess:[`apiKey`]},"@ali/bailian-plugin-inner-console-call":{commandPrefixes:[`inner-console`]}}}}).run();export{};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bailian-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
-
"README.zh.md"
|
|
28
|
+
"README.zh.md",
|
|
29
|
+
"postinstall.js"
|
|
29
30
|
],
|
|
30
31
|
"type": "module",
|
|
31
32
|
"exports": {
|
|
@@ -36,9 +37,10 @@
|
|
|
36
37
|
"registry": "https://registry.npmjs.org/"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"
|
|
40
|
-
"bailian-cli-
|
|
41
|
-
"bailian-cli-runtime": "1.
|
|
40
|
+
"tar-stream": "^3.2.0",
|
|
41
|
+
"bailian-cli-commands": "1.14.0",
|
|
42
|
+
"bailian-cli-runtime": "1.14.0",
|
|
43
|
+
"bailian-cli-core": "1.14.0"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@clack/prompts": "^0.7.0",
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
"build": "vp pack",
|
|
63
65
|
"dev": "tsx src/main.ts",
|
|
64
66
|
"test": "vp test",
|
|
65
|
-
"check": "vp check"
|
|
67
|
+
"check": "vp check",
|
|
68
|
+
"postinstall": "node postinstall.js"
|
|
66
69
|
}
|
|
67
70
|
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postinstall.js — Wiki data sync (layer 1: triggered by npm install)
|
|
3
|
+
*
|
|
4
|
+
* Runs automatically after npm/pnpm installs bailian-cli: unconditionally downloads the full Wiki data
|
|
5
|
+
* package and overwrites the local directory, ensuring data is in place the first time the user runs
|
|
6
|
+
* `bl advisor recommend`.
|
|
7
|
+
*
|
|
8
|
+
* Flow (unified skill publishing protocol: skills/index.json + one content-addressed object per skill):
|
|
9
|
+
* 1. Download skills/index.json from public-read OSS, get the bailian-docs-llm-wiki entry
|
|
10
|
+
* 2. Download skills/bailian-docs-llm-wiki/<entry.object> (sha256-<hex>.tar.br, brotli q6, ~2.3MB);
|
|
11
|
+
* legacy fallback to skill.tar.br when the entry has no valid object field
|
|
12
|
+
* 3. Node built-in brotli decompress + tar-stream extract (per-entry path safety check) to same-volume temp dir
|
|
13
|
+
* 4. renameSync atomic swap into ~/.bailian/skills/bailian-docs-llm-wiki/
|
|
14
|
+
* 5. Write ~/.bailian/wiki-sync-state.json
|
|
15
|
+
* 6. Write ~/.bailian/skills/skill-lock.json record (same ledger as bl skill)
|
|
16
|
+
*
|
|
17
|
+
* Design constraints:
|
|
18
|
+
* - Unconditional overwrite: every install fully replaces, no version comparison
|
|
19
|
+
* - Silent failure: any step failure → console.warn → process.exit(0), never blocks install
|
|
20
|
+
* - Standalone implementation: does not import bailian-cli-core, avoiding ESM path issues after bundling
|
|
21
|
+
* - Depends on Node built-in modules + tar-stream (consistent with sync.ts / publisher skills-publish.mjs)
|
|
22
|
+
*/
|
|
23
|
+
import {
|
|
24
|
+
createWriteStream,
|
|
25
|
+
existsSync,
|
|
26
|
+
mkdirSync,
|
|
27
|
+
readFileSync,
|
|
28
|
+
renameSync,
|
|
29
|
+
rmSync,
|
|
30
|
+
writeFileSync,
|
|
31
|
+
} from "node:fs";
|
|
32
|
+
import { homedir } from "node:os";
|
|
33
|
+
import { dirname, join } from "node:path";
|
|
34
|
+
import { Readable } from "node:stream";
|
|
35
|
+
import { pipeline } from "node:stream/promises";
|
|
36
|
+
import { createBrotliDecompress } from "node:zlib";
|
|
37
|
+
import tar from "tar-stream";
|
|
38
|
+
|
|
39
|
+
const REGISTRY_BASE_URL = "https://bailian-wiki.oss-cn-hangzhou.aliyuncs.com/skills";
|
|
40
|
+
const WIKI_SKILL_NAME = "bailian-docs-llm-wiki";
|
|
41
|
+
const CONFIG_DIR_NAME = ".bailian";
|
|
42
|
+
const SKILL_DIR_NAME = "skills/bailian-docs-llm-wiki";
|
|
43
|
+
const STATE_FILE_NAME = "wiki-sync-state.json";
|
|
44
|
+
const INDEX_KEY = "index.json";
|
|
45
|
+
/** Legacy fixed asset key (entries without a valid content-addressed object field) */
|
|
46
|
+
const LEGACY_ASSET_NAME = "skill.tar.br";
|
|
47
|
+
/** Same strict shape check as core registry.ts: only a valid object name may enter the URL */
|
|
48
|
+
const OBJECT_FILE_RE = /^sha256-[0-9a-f]{64}\.tar\.br$/;
|
|
49
|
+
|
|
50
|
+
const INDEX_TIMEOUT_MS = 3000;
|
|
51
|
+
const DOWNLOAD_TIMEOUT_MS = 30000;
|
|
52
|
+
|
|
53
|
+
function getConfigDir() {
|
|
54
|
+
if (process.env.BAILIAN_CONFIG_DIR) return process.env.BAILIAN_CONFIG_DIR;
|
|
55
|
+
return join(homedir(), CONFIG_DIR_NAME);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getCatalogDir() {
|
|
59
|
+
return join(getConfigDir(), SKILL_DIR_NAME);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getStatePath() {
|
|
63
|
+
return join(getConfigDir(), STATE_FILE_NAME);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getSkillLockPath() {
|
|
67
|
+
return join(getConfigDir(), "skills", "skill-lock.json");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Record this sync in skill-lock.json (same ledger as bl skill; list shows installed).
|
|
72
|
+
* Semantics aligned with upsertSkillLockEntry in core/src/skills/lock.ts: shallow-merge with the existing
|
|
73
|
+
* entry, preserving fields like links written by bl skill add; rebuild as empty table if lock is corrupted/unrecognized.
|
|
74
|
+
* best-effort: failure does not affect data sync results.
|
|
75
|
+
*/
|
|
76
|
+
function upsertSkillLock(name, entry) {
|
|
77
|
+
try {
|
|
78
|
+
let lock = { version: 1, skills: {} };
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(readFileSync(getSkillLockPath(), "utf-8"));
|
|
81
|
+
if (parsed?.version === 1 && parsed.skills && typeof parsed.skills === "object") {
|
|
82
|
+
lock = parsed;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
/* absent/corrupted → empty table */
|
|
86
|
+
}
|
|
87
|
+
lock.skills[name] = { ...lock.skills[name], ...entry };
|
|
88
|
+
mkdirSync(dirname(getSkillLockPath()), { recursive: true });
|
|
89
|
+
writeFileSync(getSkillLockPath(), JSON.stringify(lock, null, 2) + "\n");
|
|
90
|
+
} catch {
|
|
91
|
+
/* Bookkeeping failure does not block install; advisor-side sync will backfill */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function fetchJson(url, timeoutMs) {
|
|
96
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
97
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
98
|
+
return res.json();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function downloadBuffer(url) {
|
|
102
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
|
|
103
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
104
|
+
return Buffer.from(await res.arrayBuffer());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** tar 条目路径必须是相对路径且不含 ..,防止 tar-slip 逃逸解包目录 */
|
|
108
|
+
function isSafeEntryName(name) {
|
|
109
|
+
if (name.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(name)) return false;
|
|
110
|
+
return !name.split("/").includes("..");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Brotli decompress + tar-stream extract into destDir (symmetric with publisher tar.pack()). */
|
|
114
|
+
async function extractTarBr(tarBrBuffer, destDir) {
|
|
115
|
+
const extract = tar.extract();
|
|
116
|
+
|
|
117
|
+
extract.on("entry", (header, stream, next) => {
|
|
118
|
+
if (!isSafeEntryName(header.name)) {
|
|
119
|
+
// Same semantics as core skills/extract.ts: destroy so the pipeline rejects with this
|
|
120
|
+
// error; silence the entry stream to avoid its companion error becoming unhandled
|
|
121
|
+
stream.on("error", () => {});
|
|
122
|
+
stream.resume();
|
|
123
|
+
extract.destroy(new Error(`unsafe tar entry: ${header.name}`));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const filePath = join(destDir, header.name);
|
|
127
|
+
if (header.type === "directory") {
|
|
128
|
+
mkdirSync(filePath, { recursive: true });
|
|
129
|
+
stream.resume();
|
|
130
|
+
stream.on("end", next);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
134
|
+
const ws = createWriteStream(filePath);
|
|
135
|
+
stream.pipe(ws);
|
|
136
|
+
ws.on("finish", next);
|
|
137
|
+
ws.on("error", next);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await pipeline(Readable.from(tarBrBuffer), createBrotliDecompress(), extract);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Atomic swap: tmpDir (same volume) → catalogDir. */
|
|
144
|
+
function atomicSwap(tmpDir, catalogDir) {
|
|
145
|
+
mkdirSync(dirname(catalogDir), { recursive: true });
|
|
146
|
+
const backup = `${catalogDir}.old-${Date.now()}`;
|
|
147
|
+
if (existsSync(catalogDir)) renameSync(catalogDir, backup);
|
|
148
|
+
try {
|
|
149
|
+
renameSync(tmpDir, catalogDir);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (existsSync(backup) && !existsSync(catalogDir)) renameSync(backup, catalogDir);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
if (existsSync(backup)) rmSync(backup, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function main() {
|
|
158
|
+
// 1. Download skills/index.json and get the wiki entry
|
|
159
|
+
const index = await fetchJson(`${REGISTRY_BASE_URL}/${INDEX_KEY}`, INDEX_TIMEOUT_MS);
|
|
160
|
+
const entry = index?.skills?.[WIKI_SKILL_NAME];
|
|
161
|
+
if (!entry?.contentHash)
|
|
162
|
+
throw new Error("no bailian-docs-llm-wiki entry (or contentHash) in index.json");
|
|
163
|
+
|
|
164
|
+
// 2. Download the skill archive: content-addressed object first, legacy fixed key as fallback
|
|
165
|
+
const assetName =
|
|
166
|
+
entry.object && OBJECT_FILE_RE.test(entry.object) ? entry.object : LEGACY_ASSET_NAME;
|
|
167
|
+
const tarBuf = await downloadBuffer(`${REGISTRY_BASE_URL}/${WIKI_SKILL_NAME}/${assetName}`);
|
|
168
|
+
|
|
169
|
+
// 3. Extract to same-volume temp dir + atomic swap
|
|
170
|
+
const catalogDir = getCatalogDir();
|
|
171
|
+
const tmpDir = `${catalogDir}.tmp-${process.pid}-${Date.now()}`;
|
|
172
|
+
try {
|
|
173
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
174
|
+
await extractTarBr(tarBuf, tmpDir);
|
|
175
|
+
atomicSwap(tmpDir, catalogDir);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
if (existsSync(tmpDir)) rmSync(tmpDir, { recursive: true, force: true });
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 4. Write state
|
|
182
|
+
try {
|
|
183
|
+
writeFileSync(
|
|
184
|
+
getStatePath(),
|
|
185
|
+
JSON.stringify({ lastChecked: Date.now(), contentHash: entry.contentHash }),
|
|
186
|
+
);
|
|
187
|
+
} catch {
|
|
188
|
+
/* state write failure has no impact: first recommend will re-check */
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 5. skill-lock.json record: wiki shares the same ledger as bl skill
|
|
192
|
+
upsertSkillLock(WIKI_SKILL_NAME, {
|
|
193
|
+
contentHash: entry.contentHash,
|
|
194
|
+
...(entry.publishedAt ? { publishedAt: entry.publishedAt } : {}),
|
|
195
|
+
installedAt: new Date().toISOString(),
|
|
196
|
+
sourceType: "oss",
|
|
197
|
+
...(entry.description ? { description: entry.description } : {}),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
process.stdout.write(`bailian-cli: wiki data ready (${entry.publishedAt ?? "latest"})\n`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main().catch((err) => {
|
|
204
|
+
// Unconditional pass-through: install-time network/permission issues should not block npm install;
|
|
205
|
+
// sync.ts will fall back to syncing on the first `bl advisor recommend`.
|
|
206
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
207
|
+
process.stderr.write(
|
|
208
|
+
`bailian-cli: wiki data pre-download skipped (${msg}); will sync automatically on first use.\n`,
|
|
209
|
+
);
|
|
210
|
+
// Force a success exit code so a download failure never fails `npm install`.
|
|
211
|
+
// eslint-disable-next-line unicorn/no-process-exit
|
|
212
|
+
process.exit(0);
|
|
213
|
+
});
|