mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 09:51:13 +00:00
refactor(lobster): use canonical core export
This commit is contained in:
61
extensions/lobster/src/lobster-core.d.ts
vendored
61
extensions/lobster/src/lobster-core.d.ts
vendored
@@ -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<string, string | undefined>;
|
||||
stdin?: NodeJS.ReadableStream;
|
||||
stdout?: NodeJS.WritableStream;
|
||||
stderr?: NodeJS.WritableStream;
|
||||
signal?: AbortSignal;
|
||||
registry?: unknown;
|
||||
llmAdapters?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
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<string, unknown>;
|
||||
ctx?: LobsterToolContext;
|
||||
}): Promise<LobsterToolEnvelope>;
|
||||
|
||||
export function resumeToolRequest(params: {
|
||||
token?: string;
|
||||
approvalId?: string;
|
||||
approved?: boolean;
|
||||
response?: unknown;
|
||||
cancel?: boolean;
|
||||
ctx?: LobsterToolContext;
|
||||
}): Promise<LobsterToolEnvelope>;
|
||||
}
|
||||
@@ -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<string, unknown> {
|
||||
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 () => {
|
||||
|
||||
@@ -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<EmbeddedToolRuntime>;
|
||||
|
||||
type LoadEmbeddedToolRuntimeFromPackageOptions = {
|
||||
importModule?: (specifier: string) => Promise<Partial<EmbeddedToolRuntime>>;
|
||||
resolvePackageEntry?: (specifier: string) => string;
|
||||
};
|
||||
|
||||
const lobsterRequire = createRequire(import.meta.url);
|
||||
const workflowExts = new Set([".lobster", ".yaml", ".yml", ".json"]);
|
||||
|
||||
function toEmbeddedToolRuntime(
|
||||
moduleExports: Partial<EmbeddedToolRuntime>,
|
||||
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<T>(
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadEmbeddedToolRuntimeFromPackage(
|
||||
options: LoadEmbeddedToolRuntimeFromPackageOptions = {},
|
||||
): Promise<EmbeddedToolRuntime> {
|
||||
const importModule =
|
||||
options.importModule ??
|
||||
(async (specifier: string) => (await import(specifier)) as Partial<EmbeddedToolRuntime>);
|
||||
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<EmbeddedToolRuntime> {
|
||||
const coreSpecifier = ["@clawdbot", "lobster", "core"].join("/");
|
||||
return (await import(coreSpecifier)) as EmbeddedToolRuntime;
|
||||
}
|
||||
|
||||
export function createEmbeddedLobsterRunner(options?: {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user