mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 10:41:25 +00:00
fix(ui): fix web UI after tsdown migration and typing changes
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
type SkillInstallSpec,
|
||||
type SkillsInstallPreferences,
|
||||
} from "./skills.js";
|
||||
import { resolveBundledSkillsContext } from "./skills/bundled-context.js";
|
||||
|
||||
export type SkillStatusConfigCheck = {
|
||||
path: string;
|
||||
@@ -33,6 +34,7 @@ export type SkillStatusEntry = {
|
||||
name: string;
|
||||
description: string;
|
||||
source: string;
|
||||
bundled: boolean;
|
||||
filePath: string;
|
||||
baseDir: string;
|
||||
skillKey: string;
|
||||
@@ -167,6 +169,7 @@ function buildSkillStatus(
|
||||
config?: OpenClawConfig,
|
||||
prefs?: SkillsInstallPreferences,
|
||||
eligibility?: SkillEligibilityContext,
|
||||
bundledNames?: Set<string>,
|
||||
): SkillStatusEntry {
|
||||
const skillKey = resolveSkillKey(entry);
|
||||
const skillConfig = resolveSkillConfig(config, skillKey);
|
||||
@@ -181,6 +184,10 @@ function buildSkillStatus(
|
||||
entry.frontmatter.website ??
|
||||
entry.frontmatter.url;
|
||||
const homepage = homepageRaw?.trim() ? homepageRaw.trim() : undefined;
|
||||
const bundled =
|
||||
bundledNames && bundledNames.size > 0
|
||||
? bundledNames.has(entry.skill.name)
|
||||
: entry.skill.source === "openclaw-bundled";
|
||||
|
||||
const requiredBins = entry.metadata?.requires?.bins ?? [];
|
||||
const requiredAnyBins = entry.metadata?.requires?.anyBins ?? [];
|
||||
@@ -256,6 +263,7 @@ function buildSkillStatus(
|
||||
name: entry.skill.name,
|
||||
description: entry.skill.description,
|
||||
source: entry.skill.source,
|
||||
bundled,
|
||||
filePath: entry.skill.filePath,
|
||||
baseDir: entry.skill.baseDir,
|
||||
skillKey,
|
||||
@@ -289,13 +297,20 @@ export function buildWorkspaceSkillStatus(
|
||||
},
|
||||
): SkillStatusReport {
|
||||
const managedSkillsDir = opts?.managedSkillsDir ?? path.join(CONFIG_DIR, "skills");
|
||||
const skillEntries = opts?.entries ?? loadWorkspaceSkillEntries(workspaceDir, opts);
|
||||
const bundledContext = resolveBundledSkillsContext();
|
||||
const skillEntries =
|
||||
opts?.entries ??
|
||||
loadWorkspaceSkillEntries(workspaceDir, {
|
||||
config: opts?.config,
|
||||
managedSkillsDir,
|
||||
bundledSkillsDir: bundledContext.dir,
|
||||
});
|
||||
const prefs = resolveSkillsInstallPreferences(opts?.config);
|
||||
return {
|
||||
workspaceDir,
|
||||
managedSkillsDir,
|
||||
skills: skillEntries.map((entry) =>
|
||||
buildSkillStatus(entry, opts?.config, prefs, opts?.eligibility),
|
||||
buildSkillStatus(entry, opts?.config, prefs, opts?.eligibility, bundledContext.names),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
24
src/agents/skills/bundled-context.ts
Normal file
24
src/agents/skills/bundled-context.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { loadSkillsFromDir } from "@mariozechner/pi-coding-agent";
|
||||
import { resolveBundledSkillsDir, type BundledSkillsResolveOptions } from "./bundled-dir.js";
|
||||
|
||||
export type BundledSkillsContext = {
|
||||
dir?: string;
|
||||
names: Set<string>;
|
||||
};
|
||||
|
||||
export function resolveBundledSkillsContext(
|
||||
opts: BundledSkillsResolveOptions = {},
|
||||
): BundledSkillsContext {
|
||||
const dir = resolveBundledSkillsDir(opts);
|
||||
const names = new Set<string>();
|
||||
if (!dir) {
|
||||
return { dir, names };
|
||||
}
|
||||
const result = loadSkillsFromDir({ dir, source: "openclaw-bundled" });
|
||||
for (const skill of result.skills) {
|
||||
if (skill.name.trim()) {
|
||||
names.add(skill.name);
|
||||
}
|
||||
}
|
||||
return { dir, names };
|
||||
}
|
||||
54
src/agents/skills/bundled-dir.test.ts
Normal file
54
src/agents/skills/bundled-dir.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { resolveBundledSkillsDir } from "./bundled-dir.js";
|
||||
|
||||
async function writeSkill(dir: string, name: string) {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(dir, "SKILL.md"),
|
||||
`---\nname: ${name}\ndescription: ${name}\n---\n\n# ${name}\n`,
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
describe("resolveBundledSkillsDir", () => {
|
||||
const originalOverride = process.env.OPENCLAW_BUNDLED_SKILLS_DIR;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalOverride === undefined) {
|
||||
delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_BUNDLED_SKILLS_DIR = originalOverride;
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves bundled skills under a flattened dist layout", async () => {
|
||||
delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR;
|
||||
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-bundled-"));
|
||||
await fs.writeFile(path.join(root, "package.json"), JSON.stringify({ name: "openclaw" }));
|
||||
|
||||
await writeSkill(path.join(root, "skills", "peekaboo"), "peekaboo");
|
||||
|
||||
const distDir = path.join(root, "dist");
|
||||
await fs.mkdir(distDir, { recursive: true });
|
||||
const argv1 = path.join(distDir, "index.js");
|
||||
await fs.writeFile(argv1, "// stub", "utf-8");
|
||||
|
||||
const moduleUrl = pathToFileURL(path.join(distDir, "skills.js")).href;
|
||||
const execPath = path.join(root, "bin", "node");
|
||||
await fs.mkdir(path.dirname(execPath), { recursive: true });
|
||||
|
||||
const resolved = resolveBundledSkillsDir({
|
||||
argv1,
|
||||
moduleUrl,
|
||||
cwd: distDir,
|
||||
execPath,
|
||||
});
|
||||
|
||||
expect(resolved).toBe(path.join(root, "skills"));
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,41 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveOpenClawPackageRootSync } from "../../infra/openclaw-root.js";
|
||||
|
||||
export function resolveBundledSkillsDir(): string | undefined {
|
||||
function looksLikeSkillsDir(dir: string): boolean {
|
||||
try {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isFile() && entry.name.endsWith(".md")) {
|
||||
return true;
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
if (fs.existsSync(path.join(fullPath, "SKILL.md"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export type BundledSkillsResolveOptions = {
|
||||
argv1?: string;
|
||||
moduleUrl?: string;
|
||||
cwd?: string;
|
||||
execPath?: string;
|
||||
};
|
||||
|
||||
export function resolveBundledSkillsDir(
|
||||
opts: BundledSkillsResolveOptions = {},
|
||||
): string | undefined {
|
||||
const override = process.env.OPENCLAW_BUNDLED_SKILLS_DIR?.trim();
|
||||
if (override) {
|
||||
return override;
|
||||
@@ -10,7 +43,8 @@ export function resolveBundledSkillsDir(): string | undefined {
|
||||
|
||||
// bun --compile: ship a sibling `skills/` next to the executable.
|
||||
try {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const execPath = opts.execPath ?? process.execPath;
|
||||
const execDir = path.dirname(execPath);
|
||||
const sibling = path.join(execDir, "skills");
|
||||
if (fs.existsSync(sibling)) {
|
||||
return sibling;
|
||||
@@ -21,11 +55,32 @@ export function resolveBundledSkillsDir(): string | undefined {
|
||||
|
||||
// npm/dev: resolve `<packageRoot>/skills` relative to this module.
|
||||
try {
|
||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(moduleDir, "..", "..", "..");
|
||||
const candidate = path.join(root, "skills");
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
const moduleUrl = opts.moduleUrl ?? import.meta.url;
|
||||
const moduleDir = path.dirname(fileURLToPath(moduleUrl));
|
||||
const argv1 = opts.argv1 ?? process.argv[1];
|
||||
const cwd = opts.cwd ?? process.cwd();
|
||||
const packageRoot = resolveOpenClawPackageRootSync({
|
||||
argv1,
|
||||
moduleUrl,
|
||||
cwd,
|
||||
});
|
||||
if (packageRoot) {
|
||||
const candidate = path.join(packageRoot, "skills");
|
||||
if (looksLikeSkillsDir(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
let current = moduleDir;
|
||||
for (let depth = 0; depth < 6; depth += 1) {
|
||||
const candidate = path.join(current, "skills");
|
||||
if (looksLikeSkillsDir(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
const next = path.dirname(current);
|
||||
if (next === current) {
|
||||
break;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
|
||||
Reference in New Issue
Block a user