chore: stabilize Zhinian pilot delivery

This commit is contained in:
inman
2026-05-12 19:44:44 +08:00
parent 45389855e1
commit 20b5aff4ad
174 changed files with 41428 additions and 784 deletions

View File

@@ -5,7 +5,7 @@
*
* All file I/O uses async fs/promises to avoid blocking the main thread.
*/
import { readFile, writeFile, access, mkdir } from 'fs/promises';
import { readFile, writeFile, access, mkdir, readdir, rm } from 'fs/promises';
import { existsSync } from 'fs';
import { constants } from 'fs';
import { join } from 'path';
@@ -106,6 +106,31 @@ async function setSkillsEnabled(skillKeys: string[], enabled: boolean): Promise<
});
}
async function removeSkillConfigs(skillKeys: string[]): Promise<void> {
if (skillKeys.length === 0) {
return;
}
return withConfigLock(async () => {
const config = await readConfig();
const entries = config.skills?.entries;
if (!entries) {
return;
}
let changed = false;
for (const skillKey of skillKeys) {
if (entries[skillKey]) {
delete entries[skillKey];
changed = true;
}
}
if (changed) {
await writeConfig(config);
}
});
}
/**
* Get skill config
*/
@@ -306,6 +331,49 @@ async function tryReadMarker(markerPath: string): Promise<PreinstalledMarker | n
}
}
async function cleanupRemovedPreinstalledSkills(targetRoot: string, desiredSlugs: Set<string>): Promise<void> {
let entries;
try {
entries = await readdir(targetRoot, { withFileTypes: true });
} catch {
return;
}
const removedSlugs: string[] = [];
for (const entry of entries) {
if (!entry.isDirectory()) {
continue;
}
const slug = entry.name;
if (desiredSlugs.has(slug)) {
continue;
}
const targetDir = join(targetRoot, slug);
const marker = await tryReadMarker(join(targetDir, PREINSTALLED_MARKER_NAME));
if (marker?.source !== 'clawx-preinstalled') {
continue;
}
try {
await rm(targetDir, { recursive: true, force: true });
removedSlugs.push(slug);
logger.info(`Removed deprecated preinstalled skill: ${slug}`);
} catch (error) {
logger.warn(`Failed to remove deprecated preinstalled skill ${slug}:`, error);
}
}
if (removedSlugs.length > 0) {
try {
await removeSkillConfigs(removedSlugs);
} catch (error) {
logger.warn('Failed to remove deprecated preinstalled skill configs:', error);
}
}
}
/**
* Ensure third-party preinstalled skills (bundled in app resources) are
* deployed to ~/.openclaw/skills/<slug>/ as full directories.
@@ -314,7 +382,7 @@ async function tryReadMarker(markerPath: string): Promise<PreinstalledMarker | n
* - If skill is missing locally, install it.
* - If local skill exists without our marker, treat as user-managed and never overwrite.
* - If marker exists with same version, skip.
* - If marker exists with a different version, skip by default to avoid overwriting edits.
* - If marker exists with a different version, replace it from the app-managed bundle.
*/
export async function ensurePreinstalledSkillsInstalled(): Promise<void> {
const skills = await readPreinstalledManifest();
@@ -331,6 +399,7 @@ export async function ensurePreinstalledSkillsInstalled(): Promise<void> {
const targetRoot = join(homedir(), '.openclaw', 'skills');
await mkdir(targetRoot, { recursive: true });
await cleanupRemovedPreinstalledSkills(targetRoot, new Set(skills.map((skill) => skill.slug)));
const toEnable: string[] = [];
for (const spec of skills) {
@@ -350,14 +419,32 @@ export async function ensurePreinstalledSkillsInstalled(): Promise<void> {
const marker = await tryReadMarker(markerPath);
if (existsSync(targetManifest)) {
if (!marker) {
if (!marker || marker.source !== 'clawx-preinstalled') {
logger.info(`Skipping user-managed skill: ${spec.slug}`);
continue;
}
if (marker.version === desiredVersion) {
continue;
}
logger.info(`Skipping preinstalled skill update for ${spec.slug} (local marker version=${marker.version}, desired=${desiredVersion})`);
try {
await rm(targetDir, { recursive: true, force: true });
await mkdir(targetDir, { recursive: true });
await cpAsyncSafe(sourceDir, targetDir);
const markerPayload: PreinstalledMarker = {
source: 'clawx-preinstalled',
slug: spec.slug,
version: desiredVersion,
installedAt: new Date().toISOString(),
};
await writeFile(markerPath, `${JSON.stringify(markerPayload, null, 2)}\n`, 'utf-8');
if (spec.autoEnable) {
toEnable.push(spec.slug);
}
logger.info(`Updated preinstalled skill: ${spec.slug} (${marker.version} -> ${desiredVersion})`);
} catch (error) {
logger.warn(`Failed to update preinstalled skill ${spec.slug}:`, error);
}
continue;
}