mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 19:10:45 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
303
openclaw/scripts/docs-sync-publish.mjs
Normal file
303
openclaw/scripts/docs-sync-publish.mjs
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(HERE, "..");
|
||||
const SOURCE_DOCS_DIR = path.join(ROOT, "docs");
|
||||
const SOURCE_CONFIG_PATH = path.join(SOURCE_DOCS_DIR, "docs.json");
|
||||
const GENERATED_LOCALES = [
|
||||
{
|
||||
language: "zh-Hans",
|
||||
dir: "zh-CN",
|
||||
navFile: "zh-Hans-navigation.json",
|
||||
tmFile: "zh-CN.tm.jsonl",
|
||||
navMode: "overlay",
|
||||
},
|
||||
{
|
||||
language: "ja",
|
||||
dir: "ja-JP",
|
||||
navFile: "ja-navigation.json",
|
||||
tmFile: "ja-JP.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "es",
|
||||
dir: "es",
|
||||
navFile: "es-navigation.json",
|
||||
tmFile: "es.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "pt-BR",
|
||||
dir: "pt-BR",
|
||||
navFile: "pt-BR-navigation.json",
|
||||
tmFile: "pt-BR.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "ko",
|
||||
dir: "ko",
|
||||
navFile: "ko-navigation.json",
|
||||
tmFile: "ko.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "de",
|
||||
dir: "de",
|
||||
navFile: "de-navigation.json",
|
||||
tmFile: "de.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "fr",
|
||||
dir: "fr",
|
||||
navFile: "fr-navigation.json",
|
||||
tmFile: "fr.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "ar",
|
||||
dir: "ar",
|
||||
navFile: "ar-navigation.json",
|
||||
tmFile: "ar.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "it",
|
||||
dir: "it",
|
||||
navFile: "it-navigation.json",
|
||||
tmFile: "it.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "tr",
|
||||
dir: "tr",
|
||||
navFile: "tr-navigation.json",
|
||||
tmFile: "tr.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "uk",
|
||||
dir: "uk",
|
||||
navFile: "uk-navigation.json",
|
||||
tmFile: "uk.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "id",
|
||||
dir: "id",
|
||||
navFile: "id-navigation.json",
|
||||
tmFile: "id.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
{
|
||||
language: "pl",
|
||||
dir: "pl",
|
||||
navFile: "pl-navigation.json",
|
||||
tmFile: "pl.tm.jsonl",
|
||||
navMode: "clone-en",
|
||||
},
|
||||
];
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
target: "",
|
||||
sourceRepo: "",
|
||||
sourceSha: "",
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const part = argv[index];
|
||||
switch (part) {
|
||||
case "--target":
|
||||
args.target = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
break;
|
||||
case "--source-repo":
|
||||
args.sourceRepo = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
break;
|
||||
case "--source-sha":
|
||||
args.sourceSha = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`unknown arg: ${part}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.target) {
|
||||
throw new Error("missing --target");
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function run(command, args, options = {}) {
|
||||
execFileSync(command, args, {
|
||||
cwd: ROOT,
|
||||
stdio: "inherit",
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
function ensureDir(dirPath) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
}
|
||||
|
||||
function writeJson(filePath, value) {
|
||||
ensureDir(path.dirname(filePath));
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
function prefixLocalePage(entry, localeDir) {
|
||||
if (typeof entry === "string") {
|
||||
return `${localeDir}/${entry}`;
|
||||
}
|
||||
if (Array.isArray(entry)) {
|
||||
return entry.map((item) => prefixLocalePage(item, localeDir));
|
||||
}
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return entry;
|
||||
}
|
||||
|
||||
const clone = { ...entry };
|
||||
if (typeof clone.page === "string") {
|
||||
clone.page = `${localeDir}/${clone.page}`;
|
||||
}
|
||||
if (Array.isArray(clone.pages)) {
|
||||
clone.pages = clone.pages.map((item) => prefixLocalePage(item, localeDir));
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
function cloneEnglishLanguageNav(englishNav, locale) {
|
||||
if (!englishNav) {
|
||||
throw new Error("docs/docs.json is missing navigation.languages.en");
|
||||
}
|
||||
return {
|
||||
...englishNav,
|
||||
language: locale.language,
|
||||
tabs: Array.isArray(englishNav.tabs)
|
||||
? englishNav.tabs.map((tab) => ({
|
||||
...tab,
|
||||
pages: Array.isArray(tab.pages)
|
||||
? tab.pages.map((entry) => prefixLocalePage(entry, locale.dir))
|
||||
: tab.pages,
|
||||
groups: Array.isArray(tab.groups)
|
||||
? tab.groups.map((group) => ({
|
||||
...group,
|
||||
pages: Array.isArray(group.pages)
|
||||
? group.pages.map((entry) => prefixLocalePage(entry, locale.dir))
|
||||
: group.pages,
|
||||
}))
|
||||
: tab.groups,
|
||||
}))
|
||||
: englishNav.tabs,
|
||||
};
|
||||
}
|
||||
|
||||
function composeLocaleNav(locale, englishNav) {
|
||||
if (locale.navMode === "clone-en") {
|
||||
return cloneEnglishLanguageNav(englishNav, locale);
|
||||
}
|
||||
return readJson(path.join(SOURCE_DOCS_DIR, ".i18n", locale.navFile));
|
||||
}
|
||||
|
||||
function composeDocsConfig() {
|
||||
const sourceConfig = readJson(SOURCE_CONFIG_PATH);
|
||||
const languages = sourceConfig?.navigation?.languages;
|
||||
|
||||
if (!Array.isArray(languages)) {
|
||||
throw new Error("docs/docs.json is missing navigation.languages");
|
||||
}
|
||||
|
||||
const englishNav = languages.find((entry) => entry?.language === "en");
|
||||
const generatedLanguageSet = new Set(GENERATED_LOCALES.map((entry) => entry.language));
|
||||
const withoutGenerated = languages.filter((entry) => !generatedLanguageSet.has(entry?.language));
|
||||
const enIndex = withoutGenerated.findIndex((entry) => entry?.language === "en");
|
||||
const generated = GENERATED_LOCALES.map((entry) => composeLocaleNav(entry, englishNav));
|
||||
if (enIndex === -1) {
|
||||
withoutGenerated.push(...generated);
|
||||
} else {
|
||||
withoutGenerated.splice(enIndex + 1, 0, ...generated);
|
||||
}
|
||||
|
||||
return {
|
||||
...sourceConfig,
|
||||
navigation: {
|
||||
...sourceConfig.navigation,
|
||||
languages: withoutGenerated,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function syncDocsTree(targetRoot) {
|
||||
const targetDocsDir = path.join(targetRoot, "docs");
|
||||
ensureDir(targetDocsDir);
|
||||
|
||||
const localeFilters = GENERATED_LOCALES.flatMap((entry) => [
|
||||
"--filter",
|
||||
`P ${entry.dir}/`,
|
||||
"--filter",
|
||||
`P .i18n/${entry.tmFile}`,
|
||||
"--exclude",
|
||||
`${entry.dir}/`,
|
||||
"--exclude",
|
||||
`.i18n/${entry.tmFile}`,
|
||||
]);
|
||||
|
||||
run("rsync", [
|
||||
"-a",
|
||||
"--delete",
|
||||
"--filter",
|
||||
"P .i18n/README.md",
|
||||
"--exclude",
|
||||
".i18n/README.md",
|
||||
...localeFilters,
|
||||
`${SOURCE_DOCS_DIR}/`,
|
||||
`${targetDocsDir}/`,
|
||||
]);
|
||||
|
||||
for (const locale of GENERATED_LOCALES) {
|
||||
const sourceTmPath = path.join(SOURCE_DOCS_DIR, ".i18n", locale.tmFile);
|
||||
const targetTmPath = path.join(targetDocsDir, ".i18n", locale.tmFile);
|
||||
if (!fs.existsSync(targetTmPath) && fs.existsSync(sourceTmPath)) {
|
||||
ensureDir(path.dirname(targetTmPath));
|
||||
fs.copyFileSync(sourceTmPath, targetTmPath);
|
||||
}
|
||||
}
|
||||
|
||||
writeJson(path.join(targetDocsDir, "docs.json"), composeDocsConfig());
|
||||
}
|
||||
|
||||
function writeSyncMetadata(targetRoot, args) {
|
||||
const metadata = {
|
||||
repository: args.sourceRepo || "",
|
||||
sha: args.sourceSha || "",
|
||||
syncedAt: new Date().toISOString(),
|
||||
};
|
||||
writeJson(path.join(targetRoot, ".openclaw-sync", "source.json"), metadata);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const targetRoot = path.resolve(args.target);
|
||||
|
||||
if (!fs.existsSync(targetRoot)) {
|
||||
throw new Error(`target does not exist: ${targetRoot}`);
|
||||
}
|
||||
|
||||
syncDocsTree(targetRoot);
|
||||
writeSyncMetadata(targetRoot, args);
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Add table
Add a link
Reference in a new issue