mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
feat: llm wiki sync
This commit is contained in:
+12
-20
@@ -5,12 +5,11 @@
|
||||
* 保证用户首次使用 `bl advisor recommend` 时数据已就位。
|
||||
*
|
||||
* 流程:
|
||||
* 1. fetch FC 签名函数 → 拿 OSS 预签名 URL
|
||||
* 2. 下载 manifest.json + wiki-doc-full.tar.br(~2.15MB)
|
||||
* 3. 校验 sha256
|
||||
* 4. Node 原生 brotli 解压 + tar-stream 解包到同盘临时目录
|
||||
* 5. renameSync 原子替换到 ~/.bailian/skills/bailian-docs-llm-wiki/
|
||||
* 6. 写 ~/.bailian/wiki-sync-state.json
|
||||
* 1. 从公共读 OSS 直链下载 manifest.json + wiki-doc-full.tar.br(~2.15MB)
|
||||
* 2. 校验 sha256
|
||||
* 3. Node 原生 brotli 解压 + tar-stream 解包到同盘临时目录
|
||||
* 4. renameSync 原子替换到 ~/.bailian/skills/bailian-docs-llm-wiki/
|
||||
* 5. 写 ~/.bailian/wiki-sync-state.json
|
||||
*
|
||||
* 设计约束:
|
||||
* - 无条件覆盖:每次 install 都全量替换,不比对已有版本
|
||||
@@ -34,7 +33,7 @@ import { pipeline } from "node:stream/promises";
|
||||
import { createBrotliDecompress } from "node:zlib";
|
||||
import tar from "tar-stream";
|
||||
|
||||
const FC_SIGN_URL = "https://signature-gmqkxxozrl.cn-hangzhou.fcapp.run";
|
||||
const OSS_BASE_URL = "https://bailian-wiki.oss-cn-hangzhou.aliyuncs.com/bailian-docs-llm-wiki";
|
||||
const CONFIG_DIR_NAME = ".bailian";
|
||||
const SKILL_DIR_NAME = "skills/bailian-docs-llm-wiki";
|
||||
const STATE_FILE_NAME = "wiki-sync-state.json";
|
||||
@@ -106,25 +105,18 @@ function atomicSwap(tmpDir, catalogDir) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// 1. 拿全部签名 URL
|
||||
const signResp = await fetchJson(FC_SIGN_URL, MANIFEST_TIMEOUT_MS);
|
||||
const urls = signResp?.urls;
|
||||
if (!urls?.[MANIFEST_KEY] || !urls?.[ASSET_KEY]) {
|
||||
throw new Error("签名函数未返回所需 URL");
|
||||
}
|
||||
|
||||
// 2. 下载 manifest
|
||||
const manifest = await fetchJson(urls[MANIFEST_KEY], MANIFEST_TIMEOUT_MS);
|
||||
// 1. 下载 manifest
|
||||
const manifest = await fetchJson(`${OSS_BASE_URL}/${MANIFEST_KEY}`, MANIFEST_TIMEOUT_MS);
|
||||
if (!manifest?.version) throw new Error("manifest 无 version");
|
||||
|
||||
// 3. 下载 tar.br + 校验
|
||||
const tarBuf = await downloadBuffer(urls[ASSET_KEY]);
|
||||
// 2. 下载 tar.br + 校验
|
||||
const tarBuf = await downloadBuffer(`${OSS_BASE_URL}/${ASSET_KEY}`);
|
||||
const sha256 = createHash("sha256").update(tarBuf).digest("hex");
|
||||
if (manifest.asset?.sha256 && sha256 !== manifest.asset.sha256) {
|
||||
throw new Error("sha256 校验失败");
|
||||
}
|
||||
|
||||
// 4. 解包到同盘临时目录 + 原子替换
|
||||
// 3. 解包到同盘临时目录 + 原子替换
|
||||
const catalogDir = getCatalogDir();
|
||||
const tmpDir = `${catalogDir}.tmp-${process.pid}-${Date.now()}`;
|
||||
try {
|
||||
@@ -136,7 +128,7 @@ async function main() {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// 5. 写 state
|
||||
// 4. 写 state
|
||||
try {
|
||||
writeFileSync(
|
||||
getStatePath(),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* `bl advisor recommend` 执行时调用 `maybeSyncWikiData()`:
|
||||
* 1. 12h throttle:距上次检查不足 12h 直接跳过
|
||||
* 2. 拉 FC 签名 URL → 下载 manifest.json 比对版本
|
||||
* 2. 从公共读 OSS 下载 manifest.json 比对版本
|
||||
* 3. 版本相同 → 仅刷新 lastChecked
|
||||
* 4. 版本不同 → 下载 tar.br → 校验 sha256 → brotli 解压 + tar-stream 解包
|
||||
* 到同盘临时目录 → renameSync 原子替换 → 写 state
|
||||
@@ -30,8 +30,8 @@ import { createBrotliDecompress } from "node:zlib";
|
||||
import tar from "tar-stream";
|
||||
import { getConfigDir } from "../config/paths.ts";
|
||||
|
||||
/** FC 匿名签名函数入口,返回 OSS 预签名 URL(硬编码,不走 env)。 */
|
||||
const FC_SIGN_URL = "https://signature-gmqkxxozrl.cn-hangzhou.fcapp.run";
|
||||
/** 公共读 OSS 目录,直链下载(硬编码,不走 env)。 */
|
||||
const OSS_BASE_URL = "https://bailian-wiki.oss-cn-hangzhou.aliyuncs.com/bailian-docs-llm-wiki";
|
||||
const SKILL_DIR_NAME = "skills/bailian-docs-llm-wiki";
|
||||
const STATE_FILE_NAME = "wiki-sync-state.json";
|
||||
const MODELS_FILE = "models.jsonl";
|
||||
@@ -93,20 +93,6 @@ function writeState(state: SyncState): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** 拉全部 OSS 预签名 URL:`{ urls: { "manifest.json": "...", "wiki-doc-full.tar.br": "..." } }` */
|
||||
async function fetchSignedUrls(): Promise<Record<string, string> | null> {
|
||||
try {
|
||||
const res = await fetch(FC_SIGN_URL, {
|
||||
signal: AbortSignal.timeout(MANIFEST_TIMEOUT_MS),
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const data = (await res.json()) as { urls?: Record<string, string> };
|
||||
return data.urls ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchManifest(url: string): Promise<Manifest | null> {
|
||||
try {
|
||||
const res = await fetch(url, { signal: AbortSignal.timeout(MANIFEST_TIMEOUT_MS) });
|
||||
@@ -182,25 +168,23 @@ export async function maybeSyncWikiData(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 拿签名 URL + manifest
|
||||
const urls = await fetchSignedUrls();
|
||||
if (!urls?.[MANIFEST_KEY]) return false; // 失败不写 lastChecked,下次重试
|
||||
|
||||
const manifest = await fetchManifest(urls[MANIFEST_KEY]);
|
||||
if (!manifest?.version) return false;
|
||||
// 2. 拉 manifest
|
||||
const manifest = await fetchManifest(`${OSS_BASE_URL}/${MANIFEST_KEY}`);
|
||||
if (!manifest?.version) return false; // 失败不写 lastChecked,下次重试
|
||||
|
||||
// 3. 版本相同且本地数据存在:仅刷新 lastChecked,无需重新下载。
|
||||
// 若数据缺失,即便版本相同也要继续走下载补齐(落到第 4 步)。
|
||||
if (state && manifest.version === state.version && catalogDataExists()) {
|
||||
writeState({ lastChecked: now, version: state.version });
|
||||
// 覆盖两种状态:(a) state.version === manifest.version → 直接命中;
|
||||
// (b) state 缺失但数据完好(用户或意外只删了 state)→ 用 manifest 版本
|
||||
// 写回 state,避免无谓的 2MB 下载+解压。
|
||||
// 数据缺失或版本落后时落到第 4 步全量下载补齐。
|
||||
const dataOk = catalogDataExists();
|
||||
if (dataOk && (!state || state.version === manifest.version)) {
|
||||
writeState({ lastChecked: now, version: manifest.version });
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 版本不同:下载 + 校验 + 解包 + 原子替换
|
||||
const assetUrl = urls[ASSET_KEY];
|
||||
if (!assetUrl) return false;
|
||||
|
||||
const tarBuf = await downloadBuffer(assetUrl);
|
||||
const tarBuf = await downloadBuffer(`${OSS_BASE_URL}/${ASSET_KEY}`);
|
||||
if (!tarBuf) return false;
|
||||
|
||||
// sha256 校验
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
{
|
||||
"version": 1,
|
||||
"skills": {
|
||||
"ask-matt": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/ask-matt/SKILL.md",
|
||||
"computedHash": "0f843160e34a24f5bd12cdc7de7d40951e77fbdc05ce8f891b37ca32eac2c44d"
|
||||
},
|
||||
"batch-grill-me": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/batch-grill-me/SKILL.md",
|
||||
"computedHash": "f448831b8f04518b1527408f1d5024384ad5f543bdbaac6fabb7aa053ad60489"
|
||||
},
|
||||
"claude-handoff": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/claude-handoff/SKILL.md",
|
||||
"computedHash": "40a7f4ec80b9826ad7d0c47b0778c99686d6d9d662112b3e175a816f3aca4c39"
|
||||
},
|
||||
"code-review": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/code-review/SKILL.md",
|
||||
"computedHash": "31d149a480eaa68c11e32f5ee77f0fd0b98a906834d531d881d502352edd0b8e"
|
||||
},
|
||||
"codebase-design": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/codebase-design/SKILL.md",
|
||||
"computedHash": "6d9d51d8caa01633fd00cf87089c5618c982321f942ac69f62565dc001c6b22f"
|
||||
},
|
||||
"design-an-interface": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/deprecated/design-an-interface/SKILL.md",
|
||||
"computedHash": "aef348a9af675b771635623b5aa7790da4d40adc1ee88b58b6fd116eda2047d8"
|
||||
},
|
||||
"diagnosing-bugs": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/diagnosing-bugs/SKILL.md",
|
||||
"computedHash": "fd6c99466b7ba43be624e6e66ed6d7af2796ded7218f24c83820823977819e22"
|
||||
},
|
||||
"domain-modeling": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/domain-modeling/SKILL.md",
|
||||
"computedHash": "363cb0f53b0b431e7c00086ad1f823500b7e1b70b5616ee969c979f0934e9e6e"
|
||||
},
|
||||
"edit-article": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/personal/edit-article/SKILL.md",
|
||||
"computedHash": "cb2a13b2acb6c8a0706eb3a973a2681f30fd74c00784c74ceaa98e12eb2b2fc3"
|
||||
},
|
||||
"git-guardrails-claude-code": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/misc/git-guardrails-claude-code/SKILL.md",
|
||||
"computedHash": "8c29552c621da0121427306afd92f8e02f47f446cdb0b55ceae01de6879fcb3a"
|
||||
},
|
||||
"grill-me": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/grill-me/SKILL.md",
|
||||
"computedHash": "f361db4e15e6bfd562a9282b1dccda513910a50061f9e838ce017be9c69dde3f"
|
||||
},
|
||||
"grill-with-docs": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/grill-with-docs/SKILL.md",
|
||||
"computedHash": "9c460cbd94fd3c63cdef967dbdb6e66ca687103cdc380cd37834e4d10b738f78"
|
||||
},
|
||||
"grilling": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/grilling/SKILL.md",
|
||||
"computedHash": "368d3dd7251247c69f3656d93dc83c8f1577792eacac2111f1e7981db2ece49b"
|
||||
},
|
||||
"handoff": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/handoff/SKILL.md",
|
||||
"computedHash": "ad03e8d4ea3cbbff66420eb7ba3cc375b5cbe1821a2449b53e863256cf5b5cde"
|
||||
},
|
||||
"implement": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/implement/SKILL.md",
|
||||
"computedHash": "2139cfedf24791adbc839aaab6019cff158af1e28bfead020ec6e0ce01b3e74d"
|
||||
},
|
||||
"improve-codebase-architecture": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/improve-codebase-architecture/SKILL.md",
|
||||
"computedHash": "66e8a50c83c3c724fcfe0769701b665c56cec220cc4f49cc1aee8bdfc07de94a"
|
||||
},
|
||||
"loop-me": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/loop-me/SKILL.md",
|
||||
"computedHash": "e1fcab9b531b338eb540c9d57ee089e2660e4bba1d7623ec566c6f32f77f2d26"
|
||||
},
|
||||
"migrate-to-shoehorn": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/misc/migrate-to-shoehorn/SKILL.md",
|
||||
"computedHash": "6397731ced114f3657aa88b55ed13d1344a56d77ca449c568e3200c21740fa99"
|
||||
},
|
||||
"obsidian-vault": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/personal/obsidian-vault/SKILL.md",
|
||||
"computedHash": "5c08eda96fd76a71793c0a8cb91bef41d518586654a14af4f2edf8f8fd0f96a7"
|
||||
},
|
||||
"prototype": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/prototype/SKILL.md",
|
||||
"computedHash": "faba901c53a6ca245174c4ba5db3929d14253232b3050fdbd46539720adf1ab8"
|
||||
},
|
||||
"qa": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/deprecated/qa/SKILL.md",
|
||||
"computedHash": "223de2b02aa9ab36cfa4fe93e80e527b56e912e7ae725f9d596f6d2457afab77"
|
||||
},
|
||||
"request-refactor-plan": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/deprecated/request-refactor-plan/SKILL.md",
|
||||
"computedHash": "08c06e587d0f979b8731f6a00c67fb8d00e563614daf80bf648d41148e553473"
|
||||
},
|
||||
"research": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/research/SKILL.md",
|
||||
"computedHash": "bd3e2c6826671d82c86ed0da3dac3370ebcf63b0fe847f91bb444e1fb7dac21b"
|
||||
},
|
||||
"resolving-merge-conflicts": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/resolving-merge-conflicts/SKILL.md",
|
||||
"computedHash": "28aad6f8b1b7025abc8892fa8890f68fe1533499b28a565f416b159131d22fad"
|
||||
},
|
||||
"scaffold-exercises": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/misc/scaffold-exercises/SKILL.md",
|
||||
"computedHash": "354c91f6dbc9b058632f30594aacb4edc6d25012596585abb00402af8d7ec5e9"
|
||||
},
|
||||
"setup-matt-pocock-skills": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/setup-matt-pocock-skills/SKILL.md",
|
||||
"computedHash": "74e894a3509e2676d4cdb771c8eace087092430635e845e02a9cc2f757c552a4"
|
||||
},
|
||||
"setup-pre-commit": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/misc/setup-pre-commit/SKILL.md",
|
||||
"computedHash": "1b140af948d0a6330c4cf52d46ca2e06a0e96fdc34d22ec8888d2b9751b99b62"
|
||||
},
|
||||
"setup-ts-deep-modules": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/setup-ts-deep-modules/SKILL.md",
|
||||
"computedHash": "bd5cbaa455454c2d6c27a1b734e0d820606da6c482bf3c79ec55fbc3f3e03ef3"
|
||||
},
|
||||
"tdd": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/tdd/SKILL.md",
|
||||
"computedHash": "81eca2a5b53a63f481c0849be7a663a8cd43d5cf53f32b644ec0a2f50cf91aa2"
|
||||
},
|
||||
"teach": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/teach/SKILL.md",
|
||||
"computedHash": "68999bb1a241384b2f921f3321d779b7d05eb6089077b70d88cee2aee3d75ccc"
|
||||
},
|
||||
"to-questionnaire": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/to-questionnaire/SKILL.md",
|
||||
"computedHash": "d8938509f3400d9977343e11e876fc6e1982b0b9c133a2f2e9d3d730a67a103e"
|
||||
},
|
||||
"to-spec": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/to-spec/SKILL.md",
|
||||
"computedHash": "0f544cd0c099c06f0dd0b7b9ee98b4237218e7e95fd3d3c02e791efbaf74bacb"
|
||||
},
|
||||
"to-tickets": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/to-tickets/SKILL.md",
|
||||
"computedHash": "5d79577541b5cf6dade61c69844432ea20ce7b12527cdda1dd4d6fe8d59a135d"
|
||||
},
|
||||
"triage": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/triage/SKILL.md",
|
||||
"computedHash": "7c923b6a677cfe689500721f08307e2a4c46797ff169dc55ef6c34a431a0d533"
|
||||
},
|
||||
"ubiquitous-language": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/deprecated/ubiquitous-language/SKILL.md",
|
||||
"computedHash": "0395170031b57ea63f2eb22561542894c03e3047430a2f1118a06b411054429b"
|
||||
},
|
||||
"wayfinder": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/wayfinder/SKILL.md",
|
||||
"computedHash": "c9e18cefd77b6b5b0ee35f59ce2fd96359aa2d2f02e3479e12c873c5402d9d43"
|
||||
},
|
||||
"wizard": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/wizard/SKILL.md",
|
||||
"computedHash": "dab67fb9fdcc70f3a06cf2facecf58f405f478504812103b53d4c5a84765254d"
|
||||
},
|
||||
"writing-beats": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/writing-beats/SKILL.md",
|
||||
"computedHash": "7581e49a991e45e4e486b128c6391b82bd112c143cea3cc4426c23530dd545af"
|
||||
},
|
||||
"writing-fragments": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/writing-fragments/SKILL.md",
|
||||
"computedHash": "972ab831c7a39dab971439fa4718b668114d3063d347d8ba29b2a19765b3f911"
|
||||
},
|
||||
"writing-great-skills": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/writing-great-skills/SKILL.md",
|
||||
"computedHash": "4deb21855fb4deeeb1b8217b041faff003fd0c915d24a22f636851c520bc9c5c"
|
||||
},
|
||||
"writing-shape": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/in-progress/writing-shape/SKILL.md",
|
||||
"computedHash": "90899c7b3853a80da0d9ef634844b22871cc3ea6aa1bc9e4299f40484ac05392"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user