chore: stabilize Zhinian pilot delivery
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user