From 70ab6b324dd36e4abe5f7e78d3c17c17e64f156a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 13 Jul 2026 22:37:21 +0100 Subject: [PATCH] refactor(lobster): use canonical core export --- extensions/lobster/src/lobster-core.d.ts | 61 ----------------- extensions/lobster/src/lobster-runner.test.ts | 68 +++---------------- extensions/lobster/src/lobster-runner.ts | 54 +-------------- scripts/deadcode-exports.baseline.mjs | 1 - 4 files changed, 14 insertions(+), 170 deletions(-) delete mode 100644 extensions/lobster/src/lobster-core.d.ts diff --git a/extensions/lobster/src/lobster-core.d.ts b/extensions/lobster/src/lobster-core.d.ts deleted file mode 100644 index bcf8efe91be2..000000000000 --- a/extensions/lobster/src/lobster-core.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Lobster type declarations define plugin contracts. -declare module "@clawdbot/lobster/core" { - type LobsterApprovalRequest = { - type: "approval_request"; - prompt: string; - items: unknown[]; - resumeToken?: string; - approvalId?: string; - } | null; - - type LobsterToolContext = { - cwd?: string; - env?: Record; - stdin?: NodeJS.ReadableStream; - stdout?: NodeJS.WritableStream; - stderr?: NodeJS.WritableStream; - signal?: AbortSignal; - registry?: unknown; - llmAdapters?: Record; - }; - - type LobsterToolEnvelope = - | { - protocolVersion: 1; - ok: true; - status: "ok" | "needs_approval" | "needs_input" | "cancelled"; - output: unknown[]; - requiresApproval: LobsterApprovalRequest; - requiresInput?: { - prompt: string; - schema?: unknown; - items?: unknown[]; - resumeToken?: string; - approvalId?: string; - } | null; - } - | { - protocolVersion: 1; - ok: false; - error: { - type: string; - message: string; - }; - }; - - export function runToolRequest(params: { - pipeline?: string; - filePath?: string; - args?: Record; - ctx?: LobsterToolContext; - }): Promise; - - export function resumeToolRequest(params: { - token?: string; - approvalId?: string; - approved?: boolean; - response?: unknown; - cancel?: boolean; - ctx?: LobsterToolContext; - }): Promise; -} diff --git a/extensions/lobster/src/lobster-runner.test.ts b/extensions/lobster/src/lobster-runner.test.ts index a635f34224c6..1802ea902ebb 100644 --- a/extensions/lobster/src/lobster-runner.test.ts +++ b/extensions/lobster/src/lobster-runner.test.ts @@ -3,11 +3,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { - createEmbeddedLobsterRunner, - loadEmbeddedToolRuntimeFromPackage, - resolveLobsterCwd, -} from "./lobster-runner.js"; +import { createEmbeddedLobsterRunner, resolveLobsterCwd } from "./lobster-runner.js"; function requireRecord(value: unknown, label: string): Record { if (value === null || typeof value !== "object" || Array.isArray(value)) { @@ -495,58 +491,16 @@ describe("createEmbeddedLobsterRunner", () => { expect(loadRuntime).toHaveBeenCalledTimes(1); }); - it("falls back to the installed package core file when the core export is unavailable", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lobster-package-")); - const packageRoot = path.join(tempDir, "node_modules", "@clawdbot", "lobster"); - const packageEntryPath = path.join(packageRoot, "dist", "src", "sdk", "index.js"); - const packageCorePath = path.join(packageRoot, "dist", "src", "core", "index.js"); - - try { - await fs.mkdir(path.dirname(packageEntryPath), { recursive: true }); - await fs.mkdir(path.dirname(packageCorePath), { recursive: true }); - await fs.writeFile( - path.join(packageRoot, "package.json"), - JSON.stringify({ - name: "@clawdbot/lobster", - type: "module", - main: "./dist/src/sdk/index.js", - }), - "utf8", - ); - await fs.writeFile(packageEntryPath, "export {};\n", "utf8"); - await fs.writeFile( - packageCorePath, - [ - "export async function runToolRequest() {", - " return { ok: true, status: 'ok', output: [{ source: 'fallback' }], requiresApproval: null };", - "}", - "export async function resumeToolRequest() {", - " return { ok: true, status: 'cancelled', output: [], requiresApproval: null };", - "}", - "", - ].join("\n"), - "utf8", - ); - - const runtime = await loadEmbeddedToolRuntimeFromPackage({ - importModule: async (specifier) => { - if (specifier === "@clawdbot/lobster/core") { - throw new Error("package export missing"); - } - return (await import(`${specifier}?t=${Date.now()}`)) as object; - }, - resolvePackageEntry: () => packageEntryPath, - }); - - await expect(runtime.runToolRequest({ pipeline: "commands.list" })).resolves.toEqual({ - ok: true, - status: "ok", - output: [{ source: "fallback" }], - requiresApproval: null, - }); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + it("loads the published package core runtime", async () => { + await expect( + createEmbeddedLobsterRunner().run({ + action: "run", + pipeline: "commands.list", + cwd: process.cwd(), + timeoutMs: 2000, + maxStdoutBytes: 4096, + }), + ).resolves.toMatchObject({ ok: true, status: "ok" }); }); it("requires a pipeline for run", async () => { diff --git a/extensions/lobster/src/lobster-runner.ts b/extensions/lobster/src/lobster-runner.ts index fe6ae73ce1c6..1d2333faee5d 100644 --- a/extensions/lobster/src/lobster-runner.ts +++ b/extensions/lobster/src/lobster-runner.ts @@ -1,9 +1,7 @@ // Lobster plugin module implements lobster runner behavior. import { stat } from "node:fs/promises"; -import { createRequire } from "node:module"; import path from "node:path"; import { Readable, Writable } from "node:stream"; -import { pathToFileURL } from "node:url"; export type LobsterEnvelope = | { @@ -96,25 +94,8 @@ type EmbeddedToolRuntime = { type LoadEmbeddedToolRuntime = () => Promise; -type LoadEmbeddedToolRuntimeFromPackageOptions = { - importModule?: (specifier: string) => Promise>; - resolvePackageEntry?: (specifier: string) => string; -}; - -const lobsterRequire = createRequire(import.meta.url); const workflowExts = new Set([".lobster", ".yaml", ".yml", ".json"]); -function toEmbeddedToolRuntime( - moduleExports: Partial, - source: string, -): EmbeddedToolRuntime { - const { runToolRequest, resumeToolRequest } = moduleExports; - if (typeof runToolRequest === "function" && typeof resumeToolRequest === "function") { - return { runToolRequest, resumeToolRequest }; - } - throw new Error(`${source} does not export Lobster embedded runtime functions`); -} - function normalizeForCwdSandbox(p: string): string { const normalized = path.normalize(p); return process.platform === "win32" ? normalized.toLowerCase() : normalized; @@ -292,38 +273,9 @@ async function withTimeout( }); } -export async function loadEmbeddedToolRuntimeFromPackage( - options: LoadEmbeddedToolRuntimeFromPackageOptions = {}, -): Promise { - const importModule = - options.importModule ?? - (async (specifier: string) => (await import(specifier)) as Partial); - const resolvePackageEntry = - options.resolvePackageEntry ?? ((specifier: string) => lobsterRequire.resolve(specifier)); - const packageEntryPath = resolvePackageEntry("@clawdbot/lobster"); - - let coreLoadError: unknown; - try { - const coreSpecifier = ["@clawdbot", "lobster", "core"].join("/"); - return toEmbeddedToolRuntime(await importModule(coreSpecifier), "@clawdbot/lobster/core"); - } catch (error) { - coreLoadError = error; - } - - let fallbackLoadError: unknown; - try { - const coreRuntimeUrl = new URL("../core/index.js", pathToFileURL(packageEntryPath)).href; - return toEmbeddedToolRuntime(await importModule(coreRuntimeUrl), coreRuntimeUrl); - } catch (error) { - fallbackLoadError = error; - } - - throw new Error("Failed to load the Lobster embedded runtime", { - cause: new AggregateError( - [coreLoadError, fallbackLoadError], - "Both Lobster embedded runtime load paths failed", - ), - }); +async function loadEmbeddedToolRuntimeFromPackage(): Promise { + const coreSpecifier = ["@clawdbot", "lobster", "core"].join("/"); + return (await import(coreSpecifier)) as EmbeddedToolRuntime; } export function createEmbeddedLobsterRunner(options?: { diff --git a/scripts/deadcode-exports.baseline.mjs b/scripts/deadcode-exports.baseline.mjs index 5b027f0f89f8..903f843bcf39 100644 --- a/scripts/deadcode-exports.baseline.mjs +++ b/scripts/deadcode-exports.baseline.mjs @@ -175,7 +175,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "extensions/llama-cpp/src/embedding-provider.ts: DEFAULT_LLAMA_CPP_EMBEDDING_MODEL", "extensions/llama-cpp/src/embedding-provider.ts: formatLlamaCppSetupError", "extensions/lmstudio/src/stream.ts: resetLmstudioPreloadCooldownForTest", - "extensions/lobster/src/lobster-runner.ts: loadEmbeddedToolRuntimeFromPackage", "extensions/logbook/src/analyze.ts: clockToMs", "extensions/logbook/src/analyze.ts: extractJsonPayload", "extensions/matrix/src/approval-reactions.ts: clearMatrixApprovalReactionTargetsForTest",