chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -1,9 +1,6 @@
import type { Command } from "commander";
import {
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
} from "../agents/agent-scope.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import type { ClawdbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging.js";
@@ -12,15 +9,9 @@ import type { PluginLogger } from "./types.js";
const log = createSubsystemLogger("plugins");
export function registerPluginCliCommands(
program: Command,
cfg?: ClawdbotConfig,
) {
export function registerPluginCliCommands(program: Command, cfg?: ClawdbotConfig) {
const config = cfg ?? loadConfig();
const workspaceDir = resolveAgentWorkspaceDir(
config,
resolveDefaultAgentId(config),
);
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
const logger: PluginLogger = {
info: (msg: string) => log.info(msg),
warn: (msg: string) => log.warn(msg),
@@ -43,15 +34,11 @@ export function registerPluginCliCommands(
});
if (result && typeof (result as Promise<void>).then === "function") {
void (result as Promise<void>).catch((err) => {
log.warn(
`plugin CLI register failed (${entry.pluginId}): ${String(err)}`,
);
log.warn(`plugin CLI register failed (${entry.pluginId}): ${String(err)}`);
});
}
} catch (err) {
log.warn(
`plugin CLI register failed (${entry.pluginId}): ${String(err)}`,
);
log.warn(`plugin CLI register failed (${entry.pluginId}): ${String(err)}`);
}
}
}

View File

@@ -46,19 +46,11 @@ describe("discoverClawdbotPlugins", () => {
const globalExt = path.join(stateDir, "extensions");
fs.mkdirSync(globalExt, { recursive: true });
fs.writeFileSync(
path.join(globalExt, "alpha.ts"),
"export default function () {}",
"utf-8",
);
fs.writeFileSync(path.join(globalExt, "alpha.ts"), "export default function () {}", "utf-8");
const workspaceExt = path.join(workspaceDir, ".clawdbot", "extensions");
fs.mkdirSync(workspaceExt, { recursive: true });
fs.writeFileSync(
path.join(workspaceExt, "beta.ts"),
"export default function () {}",
"utf-8",
);
fs.writeFileSync(path.join(workspaceExt, "beta.ts"), "export default function () {}", "utf-8");
const { candidates } = await withStateDir(stateDir, async () => {
const { discoverClawdbotPlugins } = await import("./discovery.js");
@@ -145,11 +137,7 @@ describe("discoverClawdbotPlugins", () => {
}),
"utf-8",
);
fs.writeFileSync(
path.join(packDir, "index.js"),
"module.exports = {}",
"utf-8",
);
fs.writeFileSync(path.join(packDir, "index.js"), "module.exports = {}", "utf-8");
const { candidates } = await withStateDir(stateDir, async () => {
const { discoverClawdbotPlugins } = await import("./discovery.js");

View File

@@ -50,9 +50,7 @@ function readPackageManifest(dir: string): PackageManifest | null {
function resolvePackageExtensions(manifest: PackageManifest): string[] {
const raw = manifest.clawdbot?.extensions;
if (!Array.isArray(raw)) return [];
return raw
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
.filter(Boolean);
return raw.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
}
function deriveIdHint(params: {

View File

@@ -16,10 +16,7 @@ function makeTempDir() {
function resolveNpmCliJs() {
const fromEnv = process.env.npm_execpath;
if (
fromEnv?.includes(`${path.sep}npm${path.sep}`) &&
fromEnv?.endsWith("npm-cli.js")
) {
if (fromEnv?.includes(`${path.sep}npm${path.sep}`) && fromEnv?.endsWith("npm-cli.js")) {
return fromEnv ?? null;
}
@@ -64,20 +61,12 @@ function packToArchive({
const res = spawnSync(cmd, args, { encoding: "utf-8" });
expect(res.status).toBe(0);
if (res.status !== 0) {
throw new Error(
`npm pack failed: ${res.stderr || res.stdout || "<no output>"}`,
);
throw new Error(`npm pack failed: ${res.stderr || res.stdout || "<no output>"}`);
}
const packed = (res.stdout || "")
.trim()
.split(/\r?\n/)
.filter(Boolean)
.at(-1);
const packed = (res.stdout || "").trim().split(/\r?\n/).filter(Boolean).at(-1);
if (!packed) {
throw new Error(
`npm pack did not output a filename: ${res.stdout || "<no stdout>"}`,
);
throw new Error(`npm pack did not output a filename: ${res.stdout || "<no stdout>"}`);
}
const src = path.join(outDir, packed);
@@ -128,11 +117,7 @@ describe("installPluginFromArchive", () => {
}),
"utf-8",
);
fs.writeFileSync(
path.join(pkgDir, "dist", "index.js"),
"export {};",
"utf-8",
);
fs.writeFileSync(path.join(pkgDir, "dist", "index.js"), "export {};", "utf-8");
const archivePath = packToArchive({
pkgDir,
@@ -147,15 +132,9 @@ describe("installPluginFromArchive", () => {
expect(result.ok).toBe(true);
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,
);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(
true,
);
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "voice-call"));
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(true);
});
it("rejects installing when plugin already exists", async () => {
@@ -172,11 +151,7 @@ describe("installPluginFromArchive", () => {
}),
"utf-8",
);
fs.writeFileSync(
path.join(pkgDir, "dist", "index.js"),
"export {};",
"utf-8",
);
fs.writeFileSync(path.join(pkgDir, "dist", "index.js"), "export {};", "utf-8");
const archivePath = packToArchive({
pkgDir,

View File

@@ -31,9 +31,7 @@ const defaultLogger: PluginInstallLogger = {};
function unscopedPackageName(name: string): string {
const trimmed = name.trim();
if (!trimmed) return trimmed;
return trimmed.includes("/")
? (trimmed.split("/").pop() ?? trimmed)
: trimmed;
return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
}
function safeDirName(input: string): string {
@@ -77,20 +75,14 @@ async function ensureClawdbotExtensions(manifest: PackageManifest) {
if (!Array.isArray(extensions)) {
throw new Error("package.json missing clawdbot.extensions");
}
const list = extensions
.map((e) => (typeof e === "string" ? e.trim() : ""))
.filter(Boolean);
const list = extensions.map((e) => (typeof e === "string" ? e.trim() : "")).filter(Boolean);
if (list.length === 0) {
throw new Error("package.json clawdbot.extensions is empty");
}
return list;
}
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
label: string,
): Promise<T> {
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
@@ -132,11 +124,7 @@ export async function installPluginFromArchive(params: {
logger.info?.(`Extracting ${archivePath}`);
try {
await withTimeout(
tar.x({ file: archivePath, cwd: extractDir }),
timeoutMs,
"extract archive",
);
await withTimeout(tar.x({ file: archivePath, cwd: extractDir }), timeoutMs, "extract archive");
} catch (err) {
return { ok: false, error: `failed to extract archive: ${String(err)}` };
}
@@ -192,10 +180,10 @@ export async function installPluginFromArchive(params: {
const hasDeps = Object.keys(deps).length > 0;
if (hasDeps) {
logger.info?.("Installing plugin dependencies…");
const npmRes = await runCommandWithTimeout(
["npm", "install", "--omit=dev", "--silent"],
{ timeoutMs: Math.max(timeoutMs, 300_000), cwd: targetDir },
);
const npmRes = await runCommandWithTimeout(["npm", "install", "--omit=dev", "--silent"], {
timeoutMs: Math.max(timeoutMs, 300_000),
cwd: targetDir,
});
if (npmRes.code !== 0) {
return {
ok: false,

View File

@@ -5,11 +5,7 @@ import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
import { createSubsystemLogger } from "../logging.js";
import { resolveUserPath } from "../utils.js";
import { discoverClawdbotPlugins } from "./discovery.js";
import {
createPluginRegistry,
type PluginRecord,
type PluginRegistry,
} from "./registry.js";
import { createPluginRegistry, type PluginRecord, type PluginRegistry } from "./registry.js";
import type {
ClawdbotPluginConfigSchema,
ClawdbotPluginDefinition,
@@ -34,10 +30,7 @@ type NormalizedPluginsConfig = {
allow: string[];
deny: string[];
loadPaths: string[];
entries: Record<
string,
{ enabled?: boolean; config?: Record<string, unknown> }
>;
entries: Record<string, { enabled?: boolean; config?: Record<string, unknown> }>;
};
const registryCache = new Map<string, PluginRegistry>();
@@ -46,14 +39,10 @@ const defaultLogger = () => createSubsystemLogger("plugins");
const normalizeList = (value: unknown): string[] => {
if (!Array.isArray(value)) return [];
return value
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
.filter(Boolean);
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
};
const normalizePluginEntries = (
entries: unknown,
): NormalizedPluginsConfig["entries"] => {
const normalizePluginEntries = (entries: unknown): NormalizedPluginsConfig["entries"] => {
if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
return {};
}
@@ -68,9 +57,7 @@ const normalizePluginEntries = (
normalized[key] = {
enabled: typeof entry.enabled === "boolean" ? entry.enabled : undefined,
config:
entry.config &&
typeof entry.config === "object" &&
!Array.isArray(entry.config)
entry.config && typeof entry.config === "object" && !Array.isArray(entry.config)
? (entry.config as Record<string, unknown>)
: undefined,
};
@@ -78,9 +65,7 @@ const normalizePluginEntries = (
return normalized;
};
const normalizePluginsConfig = (
config?: ClawdbotConfig["plugins"],
): NormalizedPluginsConfig => {
const normalizePluginsConfig = (config?: ClawdbotConfig["plugins"]): NormalizedPluginsConfig => {
return {
enabled: config?.enabled !== false,
allow: normalizeList(config?.allow),
@@ -94,9 +79,7 @@ function buildCacheKey(params: {
workspaceDir?: string;
plugins: NormalizedPluginsConfig;
}): string {
const workspaceKey = params.workspaceDir
? resolveUserPath(params.workspaceDir)
: "";
const workspaceKey = params.workspaceDir ? resolveUserPath(params.workspaceDir) : "";
return `${workspaceKey}::${JSON.stringify(params.plugins)}`;
}
@@ -213,16 +196,11 @@ function createPluginRecord(params: {
};
}
function pushDiagnostics(
diagnostics: PluginDiagnostic[],
append: PluginDiagnostic[],
) {
function pushDiagnostics(diagnostics: PluginDiagnostic[], append: PluginDiagnostic[]) {
diagnostics.push(...append);
}
export function loadClawdbotPlugins(
options: PluginLoadOptions = {},
): PluginRegistry {
export function loadClawdbotPlugins(options: PluginLoadOptions = {}): PluginRegistry {
const cfg = options.config ?? {};
const logger = options.logger ?? defaultLogger();
const normalized = normalizePluginsConfig(cfg.plugins);
@@ -238,10 +216,7 @@ export function loadClawdbotPlugins(
const { registry, createApi } = createPluginRegistry({
logger,
coreGatewayHandlers: options.coreGatewayHandlers as Record<
string,
GatewayRequestHandler
>,
coreGatewayHandlers: options.coreGatewayHandlers as Record<string, GatewayRequestHandler>,
});
const discovery = discoverClawdbotPlugins({
@@ -313,8 +288,7 @@ export function loadClawdbotPlugins(
definition?.configSchema &&
typeof definition.configSchema === "object" &&
(definition.configSchema as { uiHints?: unknown }).uiHints &&
typeof (definition.configSchema as { uiHints?: unknown }).uiHints ===
"object" &&
typeof (definition.configSchema as { uiHints?: unknown }).uiHints === "object" &&
!Array.isArray((definition.configSchema as { uiHints?: unknown }).uiHints)
? ((definition.configSchema as { uiHints?: unknown }).uiHints as Record<
string,
@@ -365,8 +339,7 @@ export function loadClawdbotPlugins(
level: "warn",
pluginId: record.id,
source: record.source,
message:
"plugin register returned a promise; async registration is ignored",
message: "plugin register returned a promise; async registration is ignored",
});
}
registry.plugins.push(record);

View File

@@ -78,9 +78,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
services: [],
diagnostics: [],
};
const coreGatewayMethods = new Set(
Object.keys(registryParams.coreGatewayHandlers ?? {}),
);
const coreGatewayMethods = new Set(Object.keys(registryParams.coreGatewayHandlers ?? {}));
const pushDiagnostic = (diag: PluginDiagnostic) => {
registry.diagnostics.push(diag);
@@ -93,9 +91,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
) => {
const names = opts?.names ?? (opts?.name ? [opts.name] : []);
const factory: ClawdbotPluginToolFactory =
typeof tool === "function"
? tool
: (_ctx: ClawdbotPluginToolContext) => tool;
typeof tool === "function" ? tool : (_ctx: ClawdbotPluginToolContext) => tool;
if (typeof tool !== "function") {
names.push(tool.name);
@@ -138,9 +134,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
registrar: ClawdbotPluginCliRegistrar,
opts?: { commands?: string[] },
) => {
const commands = (opts?.commands ?? [])
.map((cmd) => cmd.trim())
.filter(Boolean);
const commands = (opts?.commands ?? []).map((cmd) => cmd.trim()).filter(Boolean);
record.cliCommands.push(...commands);
registry.cliRegistrars.push({
pluginId: record.id,
@@ -150,10 +144,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
});
};
const registerService = (
record: PluginRecord,
service: ClawdbotPluginService,
) => {
const registerService = (record: PluginRecord, service: ClawdbotPluginService) => {
const id = service.id.trim();
if (!id) return;
record.services.push(id);
@@ -188,8 +179,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
pluginConfig: params.pluginConfig,
logger: normalizeLogger(registryParams.logger),
registerTool: (tool, opts) => registerTool(record, tool, opts),
registerGatewayMethod: (method, handler) =>
registerGatewayMethod(record, method, handler),
registerGatewayMethod: (method, handler) => registerGatewayMethod(record, method, handler),
registerCli: (registrar, opts) => registerCli(record, registrar, opts),
registerService: (service) => registerService(record, service),
resolvePath: (input: string) => resolveUserPath(input),

View File

@@ -1,7 +1,4 @@
import {
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
} from "../agents/agent-scope.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js";
import { loadConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging.js";

View File

@@ -63,9 +63,7 @@ export type ClawdbotPluginCliContext = {
logger: PluginLogger;
};
export type ClawdbotPluginCliRegistrar = (
ctx: ClawdbotPluginCliContext,
) => void | Promise<void>;
export type ClawdbotPluginCliRegistrar = (ctx: ClawdbotPluginCliContext) => void | Promise<void>;
export type ClawdbotPluginServiceContext = {
config: ClawdbotConfig;
@@ -107,14 +105,8 @@ export type ClawdbotPluginApi = {
tool: AnyAgentTool | ClawdbotPluginToolFactory,
opts?: { name?: string; names?: string[] },
) => void;
registerGatewayMethod: (
method: string,
handler: GatewayRequestHandler,
) => void;
registerCli: (
registrar: ClawdbotPluginCliRegistrar,
opts?: { commands?: string[] },
) => void;
registerGatewayMethod: (method: string, handler: GatewayRequestHandler) => void;
registerCli: (registrar: ClawdbotPluginCliRegistrar, opts?: { commands?: string[] }) => void;
registerService: (service: ClawdbotPluginService) => void;
resolvePath: (input: string) => string;
};

View File

@@ -65,9 +65,7 @@ describe("voice-call plugin", () => {
})),
speak: vi.fn(async () => ({ success: true })),
endCall: vi.fn(async () => ({ success: true })),
getCall: vi.fn((id: string) =>
id === "call-1" ? { callId: "call-1" } : undefined,
),
getCall: vi.fn((id: string) => (id === "call-1" ? { callId: "call-1" } : undefined)),
getCallByProviderCallId: vi.fn(() => undefined),
},
stop: vi.fn(async () => {}),
@@ -165,10 +163,9 @@ describe("voice-call plugin", () => {
resolvePath: (p: string) => p,
});
await program.parseAsync(
["voicecall", "start", "--to", "+1", "--message", "Hello"],
{ from: "user" },
);
await program.parseAsync(["voicecall", "start", "--to", "+1", "--message", "Hello"], {
from: "user",
});
expect(logSpy).toHaveBeenCalled();
logSpy.mockRestore();
});