mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 13:01:42 +03:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -4,13 +4,17 @@ import { fileURLToPath } from "node:url";
|
||||
|
||||
export function resolveBundledPluginsDir(): string | undefined {
|
||||
const override = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR?.trim();
|
||||
if (override) return override;
|
||||
if (override) {
|
||||
return override;
|
||||
}
|
||||
|
||||
// bun --compile: ship a sibling `extensions/` next to the executable.
|
||||
try {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const sibling = path.join(execDir, "extensions");
|
||||
if (fs.existsSync(sibling)) return sibling;
|
||||
if (fs.existsSync(sibling)) {
|
||||
return sibling;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -20,9 +24,13 @@ export function resolveBundledPluginsDir(): string | undefined {
|
||||
let cursor = path.dirname(fileURLToPath(import.meta.url));
|
||||
for (let i = 0; i < 6; i += 1) {
|
||||
const candidate = path.join(cursor, "extensions");
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
const parent = path.dirname(cursor);
|
||||
if (parent === cursor) break;
|
||||
if (parent === cursor) {
|
||||
break;
|
||||
}
|
||||
cursor = parent;
|
||||
}
|
||||
} catch {
|
||||
|
||||
+15
-5
@@ -168,7 +168,9 @@ export function matchPluginCommand(
|
||||
commandBody: string,
|
||||
): { command: RegisteredPluginCommand; args?: string } | null {
|
||||
const trimmed = commandBody.trim();
|
||||
if (!trimmed.startsWith("/")) return null;
|
||||
if (!trimmed.startsWith("/")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract command name and args
|
||||
const spaceIndex = trimmed.indexOf(" ");
|
||||
@@ -178,10 +180,14 @@ export function matchPluginCommand(
|
||||
const key = commandName.toLowerCase();
|
||||
const command = pluginCommands.get(key);
|
||||
|
||||
if (!command) return null;
|
||||
if (!command) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If command doesn't accept args but args were provided, don't match
|
||||
if (args && !command.acceptsArgs) return null;
|
||||
if (args && !command.acceptsArgs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { command, args: args || undefined };
|
||||
}
|
||||
@@ -191,7 +197,9 @@ export function matchPluginCommand(
|
||||
* Removes control characters and enforces length limits.
|
||||
*/
|
||||
function sanitizeArgs(args: string | undefined): string | undefined {
|
||||
if (!args) return undefined;
|
||||
if (!args) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Enforce length limit
|
||||
if (args.length > MAX_ARGS_LENGTH) {
|
||||
@@ -203,7 +211,9 @@ function sanitizeArgs(args: string | undefined): string | undefined {
|
||||
for (const char of args) {
|
||||
const code = char.charCodeAt(0);
|
||||
const isControl = (code <= 0x1f && code !== 0x09 && code !== 0x0a) || code === 0x7f;
|
||||
if (!isControl) sanitized += char;
|
||||
if (!isControl) {
|
||||
sanitized += char;
|
||||
}
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ function error(message: string): SafeParseResult {
|
||||
export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema {
|
||||
return {
|
||||
safeParse(value: unknown): SafeParseResult {
|
||||
if (value === undefined) return { success: true, data: undefined };
|
||||
if (value === undefined) {
|
||||
return { success: true, data: undefined };
|
||||
}
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return error("expected config object");
|
||||
}
|
||||
|
||||
+44
-15
@@ -16,15 +16,23 @@ export type NormalizedPluginsConfig = {
|
||||
export const BUNDLED_ENABLED_BY_DEFAULT = new Set<string>();
|
||||
|
||||
const normalizeList = (value: unknown): string[] => {
|
||||
if (!Array.isArray(value)) return [];
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
|
||||
};
|
||||
|
||||
const normalizeSlotValue = (value: unknown): string | null | undefined => {
|
||||
if (typeof value !== "string") return undefined;
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return undefined;
|
||||
if (trimmed.toLowerCase() === "none") return null;
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
if (trimmed.toLowerCase() === "none") {
|
||||
return null;
|
||||
}
|
||||
return trimmed;
|
||||
};
|
||||
|
||||
@@ -34,7 +42,9 @@ const normalizePluginEntries = (entries: unknown): NormalizedPluginsConfig["entr
|
||||
}
|
||||
const normalized: NormalizedPluginsConfig["entries"] = {};
|
||||
for (const [key, value] of Object.entries(entries)) {
|
||||
if (!key.trim()) continue;
|
||||
if (!key.trim()) {
|
||||
continue;
|
||||
}
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
normalized[key] = {};
|
||||
continue;
|
||||
@@ -71,14 +81,27 @@ const hasExplicitMemoryEntry = (plugins?: OpenClawConfig["plugins"]) =>
|
||||
Boolean(plugins?.entries && Object.prototype.hasOwnProperty.call(plugins.entries, "memory-core"));
|
||||
|
||||
const hasExplicitPluginConfig = (plugins?: OpenClawConfig["plugins"]) => {
|
||||
if (!plugins) return false;
|
||||
if (typeof plugins.enabled === "boolean") return true;
|
||||
if (Array.isArray(plugins.allow) && plugins.allow.length > 0) return true;
|
||||
if (Array.isArray(plugins.deny) && plugins.deny.length > 0) return true;
|
||||
if (plugins.load?.paths && Array.isArray(plugins.load.paths) && plugins.load.paths.length > 0)
|
||||
if (!plugins) {
|
||||
return false;
|
||||
}
|
||||
if (typeof plugins.enabled === "boolean") {
|
||||
return true;
|
||||
if (plugins.slots && Object.keys(plugins.slots).length > 0) return true;
|
||||
if (plugins.entries && Object.keys(plugins.entries).length > 0) return true;
|
||||
}
|
||||
if (Array.isArray(plugins.allow) && plugins.allow.length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(plugins.deny) && plugins.deny.length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (plugins.load?.paths && Array.isArray(plugins.load.paths) && plugins.load.paths.length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (plugins.slots && Object.keys(plugins.slots).length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (plugins.entries && Object.keys(plugins.entries).length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -86,7 +109,9 @@ export function applyTestPluginDefaults(
|
||||
cfg: OpenClawConfig,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): OpenClawConfig {
|
||||
if (!env.VITEST) return cfg;
|
||||
if (!env.VITEST) {
|
||||
return cfg;
|
||||
}
|
||||
const plugins = cfg.plugins;
|
||||
const explicitConfig = hasExplicitPluginConfig(plugins);
|
||||
if (explicitConfig) {
|
||||
@@ -122,7 +147,9 @@ export function isTestDefaultMemorySlotDisabled(
|
||||
cfg: OpenClawConfig,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): boolean {
|
||||
if (!env.VITEST) return false;
|
||||
if (!env.VITEST) {
|
||||
return false;
|
||||
}
|
||||
const plugins = cfg.plugins;
|
||||
if (hasExplicitMemorySlot(plugins) || hasExplicitMemoryEntry(plugins)) {
|
||||
return false;
|
||||
@@ -169,7 +196,9 @@ export function resolveMemorySlotDecision(params: {
|
||||
slot: string | null | undefined;
|
||||
selectedId: string | null;
|
||||
}): { enabled: boolean; reason?: string; selected?: boolean } {
|
||||
if (params.kind !== "memory") return { enabled: true };
|
||||
if (params.kind !== "memory") {
|
||||
return { enabled: true };
|
||||
}
|
||||
if (params.slot === null) {
|
||||
return { enabled: false, reason: "memory slot disabled" };
|
||||
}
|
||||
|
||||
+33
-11
@@ -32,13 +32,17 @@ export type PluginDiscoveryResult = {
|
||||
|
||||
function isExtensionFile(filePath: string): boolean {
|
||||
const ext = path.extname(filePath);
|
||||
if (!EXTENSION_EXTS.has(ext)) return false;
|
||||
if (!EXTENSION_EXTS.has(ext)) {
|
||||
return false;
|
||||
}
|
||||
return !filePath.endsWith(".d.ts");
|
||||
}
|
||||
|
||||
function readPackageManifest(dir: string): PackageManifest | null {
|
||||
const manifestPath = path.join(dir, "package.json");
|
||||
if (!fs.existsSync(manifestPath)) return null;
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const raw = fs.readFileSync(manifestPath, "utf-8");
|
||||
return JSON.parse(raw) as PackageManifest;
|
||||
@@ -49,7 +53,9 @@ function readPackageManifest(dir: string): PackageManifest | null {
|
||||
|
||||
function resolvePackageExtensions(manifest: PackageManifest): string[] {
|
||||
const raw = getPackageManifestMetadata(manifest)?.extensions;
|
||||
if (!Array.isArray(raw)) return [];
|
||||
if (!Array.isArray(raw)) {
|
||||
return [];
|
||||
}
|
||||
return raw.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -60,7 +66,9 @@ function deriveIdHint(params: {
|
||||
}): string {
|
||||
const base = path.basename(params.filePath, path.extname(params.filePath));
|
||||
const rawPackageName = params.packageName?.trim();
|
||||
if (!rawPackageName) return base;
|
||||
if (!rawPackageName) {
|
||||
return base;
|
||||
}
|
||||
|
||||
// Prefer the unscoped name so config keys stay stable even when the npm
|
||||
// package is scoped (example: @openclaw/voice-call -> voice-call).
|
||||
@@ -68,7 +76,9 @@ function deriveIdHint(params: {
|
||||
? (rawPackageName.split("/").pop() ?? rawPackageName)
|
||||
: rawPackageName;
|
||||
|
||||
if (!params.hasMultipleExtensions) return unscoped;
|
||||
if (!params.hasMultipleExtensions) {
|
||||
return unscoped;
|
||||
}
|
||||
return `${unscoped}/${base}`;
|
||||
}
|
||||
|
||||
@@ -84,7 +94,9 @@ function addCandidate(params: {
|
||||
packageDir?: string;
|
||||
}) {
|
||||
const resolved = path.resolve(params.source);
|
||||
if (params.seen.has(resolved)) return;
|
||||
if (params.seen.has(resolved)) {
|
||||
return;
|
||||
}
|
||||
params.seen.add(resolved);
|
||||
const manifest = params.manifest ?? null;
|
||||
params.candidates.push({
|
||||
@@ -109,7 +121,9 @@ function discoverInDirectory(params: {
|
||||
diagnostics: PluginDiagnostic[];
|
||||
seen: Set<string>;
|
||||
}) {
|
||||
if (!fs.existsSync(params.dir)) return;
|
||||
if (!fs.existsSync(params.dir)) {
|
||||
return;
|
||||
}
|
||||
let entries: fs.Dirent[] = [];
|
||||
try {
|
||||
entries = fs.readdirSync(params.dir, { withFileTypes: true });
|
||||
@@ -125,7 +139,9 @@ function discoverInDirectory(params: {
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(params.dir, entry.name);
|
||||
if (entry.isFile()) {
|
||||
if (!isExtensionFile(fullPath)) continue;
|
||||
if (!isExtensionFile(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
addCandidate({
|
||||
candidates: params.candidates,
|
||||
seen: params.seen,
|
||||
@@ -136,7 +152,9 @@ function discoverInDirectory(params: {
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
}
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (!entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const manifest = readPackageManifest(fullPath);
|
||||
const extensions = manifest ? resolvePackageExtensions(manifest) : [];
|
||||
@@ -292,9 +310,13 @@ export function discoverOpenClawPlugins(params: {
|
||||
|
||||
const extra = params.extraPaths ?? [];
|
||||
for (const extraPath of extra) {
|
||||
if (typeof extraPath !== "string") continue;
|
||||
if (typeof extraPath !== "string") {
|
||||
continue;
|
||||
}
|
||||
const trimmed = extraPath.trim();
|
||||
if (!trimmed) continue;
|
||||
if (!trimmed) {
|
||||
continue;
|
||||
}
|
||||
discoverFromPath({
|
||||
rawPath: trimmed,
|
||||
origin: "config",
|
||||
|
||||
@@ -8,7 +8,9 @@ export type PluginEnableResult = {
|
||||
|
||||
function ensureAllowlisted(cfg: OpenClawConfig, pluginId: string): OpenClawConfig {
|
||||
const allow = cfg.plugins?.allow;
|
||||
if (!Array.isArray(allow) || allow.includes(pluginId)) return cfg;
|
||||
if (!Array.isArray(allow) || allow.includes(pluginId)) {
|
||||
return cfg;
|
||||
}
|
||||
return {
|
||||
...cfg,
|
||||
plugins: {
|
||||
|
||||
+12
-4
@@ -104,7 +104,9 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
ctx: Parameters<NonNullable<PluginHookRegistration<K>["handler"]>>[1],
|
||||
): Promise<void> {
|
||||
const hooks = getHooksForName(registry, hookName);
|
||||
if (hooks.length === 0) return;
|
||||
if (hooks.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger?.debug?.(`[hooks] running ${hookName} (${hooks.length} handlers)`);
|
||||
|
||||
@@ -135,7 +137,9 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
mergeResults?: (accumulated: TResult | undefined, next: TResult) => TResult,
|
||||
): Promise<TResult | undefined> {
|
||||
const hooks = getHooksForName(registry, hookName);
|
||||
if (hooks.length === 0) return undefined;
|
||||
if (hooks.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
logger?.debug?.(`[hooks] running ${hookName} (${hooks.length} handlers, sequential)`);
|
||||
|
||||
@@ -323,7 +327,9 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
ctx: PluginHookToolResultPersistContext,
|
||||
): PluginHookToolResultPersistResult | undefined {
|
||||
const hooks = getHooksForName(registry, "tool_result_persist");
|
||||
if (hooks.length === 0) return undefined;
|
||||
if (hooks.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let current = event.message;
|
||||
|
||||
@@ -347,7 +353,9 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
}
|
||||
|
||||
const next = (out as PluginHookToolResultPersistResult | undefined)?.message;
|
||||
if (next) current = next;
|
||||
if (next) {
|
||||
current = next;
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `[hooks] tool_result_persist handler from ${hook.pluginId} failed: ${String(err)}`;
|
||||
if (catchErrors) {
|
||||
|
||||
@@ -5,7 +5,9 @@ export function normalizePluginHttpPath(
|
||||
const trimmed = path?.trim();
|
||||
if (!trimmed) {
|
||||
const fallbackTrimmed = fallback?.trim();
|
||||
if (!fallbackTrimmed) return null;
|
||||
if (!fallbackTrimmed) {
|
||||
return null;
|
||||
}
|
||||
return fallbackTrimmed.startsWith("/") ? fallbackTrimmed : `/${fallbackTrimmed}`;
|
||||
}
|
||||
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
||||
|
||||
@@ -28,7 +28,9 @@ function resolveNpmCliJs() {
|
||||
"bin",
|
||||
"npm-cli.js",
|
||||
);
|
||||
if (fs.existsSync(fromNodeDir)) return fromNodeDir;
|
||||
if (fs.existsSync(fromNodeDir)) {
|
||||
return fromNodeDir;
|
||||
}
|
||||
|
||||
const fromLibNodeModules = path.resolve(
|
||||
path.dirname(process.execPath),
|
||||
@@ -39,7 +41,9 @@ function resolveNpmCliJs() {
|
||||
"bin",
|
||||
"npm-cli.js",
|
||||
);
|
||||
if (fs.existsSync(fromLibNodeModules)) return fromLibNodeModules;
|
||||
if (fs.existsSync(fromLibNodeModules)) {
|
||||
return fromLibNodeModules;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -117,7 +121,9 @@ describe("installPluginFromArchive", () => {
|
||||
extensionsDir,
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.pluginId).toBe("voice-call");
|
||||
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "voice-call"));
|
||||
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
|
||||
@@ -159,7 +165,9 @@ describe("installPluginFromArchive", () => {
|
||||
|
||||
expect(first.ok).toBe(true);
|
||||
expect(second.ok).toBe(false);
|
||||
if (second.ok) return;
|
||||
if (second.ok) {
|
||||
return;
|
||||
}
|
||||
expect(second.error).toContain("already exists");
|
||||
});
|
||||
|
||||
@@ -189,7 +197,9 @@ describe("installPluginFromArchive", () => {
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.pluginId).toBe("zipper");
|
||||
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "zipper"));
|
||||
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
|
||||
@@ -249,7 +259,9 @@ describe("installPluginFromArchive", () => {
|
||||
|
||||
expect(first.ok).toBe(true);
|
||||
expect(second.ok).toBe(true);
|
||||
if (!second.ok) return;
|
||||
if (!second.ok) {
|
||||
return;
|
||||
}
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(path.join(second.targetDir, "package.json"), "utf-8"),
|
||||
) as { version?: string };
|
||||
@@ -280,7 +292,9 @@ describe("installPluginFromArchive", () => {
|
||||
extensionsDir,
|
||||
});
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
if (result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.error).toContain("openclaw.extensions");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,13 +38,17 @@ const defaultLogger: PluginInstallLogger = {};
|
||||
|
||||
function unscopedPackageName(name: string): string {
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return trimmed;
|
||||
if (!trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
|
||||
}
|
||||
|
||||
function safeDirName(input: string): string {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return trimmed;
|
||||
if (!trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.replaceAll("/", "__");
|
||||
}
|
||||
|
||||
@@ -348,7 +352,9 @@ export async function installPluginFromNpmSpec(params: {
|
||||
const dryRun = params.dryRun ?? false;
|
||||
const expectedPluginId = params.expectedPluginId;
|
||||
const spec = params.spec.trim();
|
||||
if (!spec) return { ok: false, error: "missing npm spec" };
|
||||
if (!spec) {
|
||||
return { ok: false, error: "missing npm spec" };
|
||||
}
|
||||
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-npm-pack-"));
|
||||
logger.info?.(`Downloading ${spec}…`);
|
||||
|
||||
@@ -56,10 +56,14 @@ const resolvePluginSdkAlias = (): string | null => {
|
||||
? [distCandidate, srcCandidate]
|
||||
: [srcCandidate, distCandidate];
|
||||
for (const candidate of orderedCandidates) {
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
const parent = path.dirname(cursor);
|
||||
if (parent === cursor) break;
|
||||
if (parent === cursor) {
|
||||
break;
|
||||
}
|
||||
cursor = parent;
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -37,16 +37,24 @@ const DEFAULT_MANIFEST_CACHE_MS = 200;
|
||||
|
||||
function resolveManifestCacheMs(env: NodeJS.ProcessEnv): number {
|
||||
const raw = env.OPENCLAW_PLUGIN_MANIFEST_CACHE_MS?.trim();
|
||||
if (raw === "" || raw === "0") return 0;
|
||||
if (!raw) return DEFAULT_MANIFEST_CACHE_MS;
|
||||
if (raw === "" || raw === "0") {
|
||||
return 0;
|
||||
}
|
||||
if (!raw) {
|
||||
return DEFAULT_MANIFEST_CACHE_MS;
|
||||
}
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
if (!Number.isFinite(parsed)) return DEFAULT_MANIFEST_CACHE_MS;
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return DEFAULT_MANIFEST_CACHE_MS;
|
||||
}
|
||||
return Math.max(0, parsed);
|
||||
}
|
||||
|
||||
function shouldUseManifestCache(env: NodeJS.ProcessEnv): boolean {
|
||||
const disabled = env.OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE?.trim();
|
||||
if (disabled) return false;
|
||||
if (disabled) {
|
||||
return false;
|
||||
}
|
||||
return resolveManifestCacheMs(env) > 0;
|
||||
}
|
||||
|
||||
@@ -114,7 +122,9 @@ export function loadPluginManifestRegistry(params: {
|
||||
const cacheEnabled = params.cache !== false && shouldUseManifestCache(env);
|
||||
if (cacheEnabled) {
|
||||
const cached = registryCache.get(cacheKey);
|
||||
if (cached && cached.expiresAt > Date.now()) return cached.registry;
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.registry;
|
||||
}
|
||||
}
|
||||
|
||||
const discovery = params.candidates
|
||||
|
||||
@@ -25,7 +25,9 @@ export type PluginManifestLoadResult =
|
||||
| { ok: false; error: string; manifestPath: string };
|
||||
|
||||
function normalizeStringList(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -36,7 +38,9 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
export function resolvePluginManifestPath(rootDir: string): string {
|
||||
for (const filename of PLUGIN_MANIFEST_FILENAMES) {
|
||||
const candidate = path.join(rootDir, filename);
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return path.join(rootDir, PLUGIN_MANIFEST_FILENAME);
|
||||
}
|
||||
@@ -144,6 +148,8 @@ export type PackageManifest = {
|
||||
export function getPackageManifestMetadata(
|
||||
manifest: PackageManifest | undefined,
|
||||
): OpenClawPackageManifest | undefined {
|
||||
if (!manifest) return undefined;
|
||||
if (!manifest) {
|
||||
return undefined;
|
||||
}
|
||||
return manifest[MANIFEST_KEY];
|
||||
}
|
||||
|
||||
@@ -268,7 +268,9 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
handler: GatewayRequestHandler,
|
||||
) => {
|
||||
const trimmed = method.trim();
|
||||
if (!trimmed) return;
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
if (coreGatewayMethods.has(trimmed) || registry.gatewayHandlers[trimmed]) {
|
||||
pushDiagnostic({
|
||||
level: "error",
|
||||
@@ -397,7 +399,9 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
|
||||
const registerService = (record: PluginRecord, service: OpenClawPluginService) => {
|
||||
const id = service.id.trim();
|
||||
if (!id) return;
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
record.services.push(id);
|
||||
registry.services.push({
|
||||
pluginId: record.id,
|
||||
|
||||
@@ -150,7 +150,9 @@ import type { PluginRuntime } from "./types.js";
|
||||
let cachedVersion: string | null = null;
|
||||
|
||||
function resolveVersion(): string {
|
||||
if (cachedVersion) return cachedVersion;
|
||||
if (cachedVersion) {
|
||||
return cachedVersion;
|
||||
}
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
const pkg = require("../../../package.json") as { version?: string };
|
||||
|
||||
@@ -14,7 +14,9 @@ type CachedValidator = {
|
||||
const schemaCache = new Map<string, CachedValidator>();
|
||||
|
||||
function formatAjvErrors(errors: ErrorObject[] | null | undefined): string[] {
|
||||
if (!errors || errors.length === 0) return ["invalid config"];
|
||||
if (!errors || errors.length === 0) {
|
||||
return ["invalid config"];
|
||||
}
|
||||
return errors.map((error) => {
|
||||
const path = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
|
||||
const message = error.message ?? "invalid";
|
||||
@@ -35,6 +37,8 @@ export function validateJsonSchemaValue(params: {
|
||||
}
|
||||
|
||||
const ok = cached.validate(params.value);
|
||||
if (ok) return { ok: true };
|
||||
if (ok) {
|
||||
return { ok: true };
|
||||
}
|
||||
return { ok: false, errors: formatAjvErrors(cached.validate.errors) };
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ export async function startPluginServices(params: {
|
||||
return {
|
||||
stop: async () => {
|
||||
for (const entry of running.toReversed()) {
|
||||
if (!entry.stop) continue;
|
||||
if (!entry.stop) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await entry.stop();
|
||||
} catch (err) {
|
||||
|
||||
@@ -18,7 +18,9 @@ const DEFAULT_SLOT_BY_KEY: Record<PluginSlotKey, string> = {
|
||||
};
|
||||
|
||||
export function slotKeyForPluginKind(kind?: PluginKind): PluginSlotKey | null {
|
||||
if (!kind) return null;
|
||||
if (!kind) {
|
||||
return null;
|
||||
}
|
||||
return SLOT_BY_KIND[kind] ?? null;
|
||||
}
|
||||
|
||||
@@ -62,8 +64,12 @@ export function applyExclusiveSlotSelection(params: {
|
||||
const disabledIds: string[] = [];
|
||||
if (params.registry) {
|
||||
for (const plugin of params.registry.plugins) {
|
||||
if (plugin.id === params.selectedId) continue;
|
||||
if (plugin.kind !== params.selectedKind) continue;
|
||||
if (plugin.id === params.selectedId) {
|
||||
continue;
|
||||
}
|
||||
if (plugin.kind !== params.selectedKind) {
|
||||
continue;
|
||||
}
|
||||
const entry = entries[plugin.id];
|
||||
if (!entry || entry.enabled !== false) {
|
||||
entries[plugin.id] = {
|
||||
|
||||
+18
-6
@@ -26,11 +26,17 @@ function isOptionalToolAllowed(params: {
|
||||
pluginId: string;
|
||||
allowlist: Set<string>;
|
||||
}): boolean {
|
||||
if (params.allowlist.size === 0) return false;
|
||||
if (params.allowlist.size === 0) {
|
||||
return false;
|
||||
}
|
||||
const toolName = normalizeToolName(params.toolName);
|
||||
if (params.allowlist.has(toolName)) return true;
|
||||
if (params.allowlist.has(toolName)) {
|
||||
return true;
|
||||
}
|
||||
const pluginKey = normalizeToolName(params.pluginId);
|
||||
if (params.allowlist.has(pluginKey)) return true;
|
||||
if (params.allowlist.has(pluginKey)) {
|
||||
return true;
|
||||
}
|
||||
return params.allowlist.has("group:plugins");
|
||||
}
|
||||
|
||||
@@ -57,7 +63,9 @@ export function resolvePluginTools(params: {
|
||||
const blockedPlugins = new Set<string>();
|
||||
|
||||
for (const entry of registry.tools) {
|
||||
if (blockedPlugins.has(entry.pluginId)) continue;
|
||||
if (blockedPlugins.has(entry.pluginId)) {
|
||||
continue;
|
||||
}
|
||||
const pluginIdKey = normalizeToolName(entry.pluginId);
|
||||
if (existingNormalized.has(pluginIdKey)) {
|
||||
const message = `plugin id conflicts with core tool name (${entry.pluginId})`;
|
||||
@@ -78,7 +86,9 @@ export function resolvePluginTools(params: {
|
||||
log.error(`plugin tool failed (${entry.pluginId}): ${String(err)}`);
|
||||
continue;
|
||||
}
|
||||
if (!resolved) continue;
|
||||
if (!resolved) {
|
||||
continue;
|
||||
}
|
||||
const listRaw = Array.isArray(resolved) ? resolved : [resolved];
|
||||
const list = entry.optional
|
||||
? listRaw.filter((tool) =>
|
||||
@@ -89,7 +99,9 @@ export function resolvePluginTools(params: {
|
||||
}),
|
||||
)
|
||||
: listRaw;
|
||||
if (list.length === 0) continue;
|
||||
if (list.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const nameSet = new Set<string>();
|
||||
for (const tool of list) {
|
||||
if (nameSet.has(tool.name) || existing.has(tool.name)) {
|
||||
|
||||
+33
-11
@@ -66,11 +66,17 @@ function resolveBundledPluginSources(params: {
|
||||
const bundled = new Map<string, BundledPluginSource>();
|
||||
|
||||
for (const candidate of discovery.candidates) {
|
||||
if (candidate.origin !== "bundled") continue;
|
||||
if (candidate.origin !== "bundled") {
|
||||
continue;
|
||||
}
|
||||
const manifest = loadPluginManifest(candidate.rootDir);
|
||||
if (!manifest.ok) continue;
|
||||
if (!manifest.ok) {
|
||||
continue;
|
||||
}
|
||||
const pluginId = manifest.manifest.id;
|
||||
if (bundled.has(pluginId)) continue;
|
||||
if (bundled.has(pluginId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const npmSpec =
|
||||
candidate.packageManifest?.install?.npmSpec?.trim() ||
|
||||
@@ -88,7 +94,9 @@ function resolveBundledPluginSources(params: {
|
||||
}
|
||||
|
||||
function pathsEqual(left?: string, right?: string): boolean {
|
||||
if (!left || !right) return false;
|
||||
if (!left || !right) {
|
||||
return false;
|
||||
}
|
||||
return resolveUserPath(left) === resolveUserPath(right);
|
||||
}
|
||||
|
||||
@@ -100,7 +108,9 @@ function buildLoadPathHelpers(existing: string[]) {
|
||||
|
||||
const addPath = (value: string) => {
|
||||
const normalized = resolveUserPath(value);
|
||||
if (resolved.has(normalized)) return;
|
||||
if (resolved.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
paths.push(value);
|
||||
resolved.add(normalized);
|
||||
changed = true;
|
||||
@@ -108,7 +118,9 @@ function buildLoadPathHelpers(existing: string[]) {
|
||||
|
||||
const removePath = (value: string) => {
|
||||
const normalized = resolveUserPath(value);
|
||||
if (!resolved.has(normalized)) return;
|
||||
if (!resolved.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
paths = paths.filter((entry) => resolveUserPath(entry) !== normalized);
|
||||
resolved = resolveSet();
|
||||
changed = true;
|
||||
@@ -314,13 +326,17 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
if (params.channel === "dev") {
|
||||
for (const [pluginId, record] of Object.entries(installs)) {
|
||||
const bundledInfo = bundled.get(pluginId);
|
||||
if (!bundledInfo) continue;
|
||||
if (!bundledInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loadHelpers.addPath(bundledInfo.localPath);
|
||||
|
||||
const alreadyBundled =
|
||||
record.source === "path" && pathsEqual(record.sourcePath, bundledInfo.localPath);
|
||||
if (alreadyBundled) continue;
|
||||
if (alreadyBundled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
next = recordPluginInstall(next, {
|
||||
pluginId,
|
||||
@@ -336,15 +352,21 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
} else {
|
||||
for (const [pluginId, record] of Object.entries(installs)) {
|
||||
const bundledInfo = bundled.get(pluginId);
|
||||
if (!bundledInfo) continue;
|
||||
if (!bundledInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record.source === "npm") {
|
||||
loadHelpers.removePath(bundledInfo.localPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record.source !== "path") continue;
|
||||
if (!pathsEqual(record.sourcePath, bundledInfo.localPath)) continue;
|
||||
if (record.source !== "path") {
|
||||
continue;
|
||||
}
|
||||
if (!pathsEqual(record.sourcePath, bundledInfo.localPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const spec = record.spec ?? bundledInfo.npmSpec;
|
||||
if (!spec) {
|
||||
|
||||
Reference in New Issue
Block a user