import fs from "node:fs"; import path from "node:path"; const command = process.argv[2]; const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8")); function requireEnv(name) { const value = process.env[name]; if (!value) { throw new Error(`missing ${name}`); } return value; } function stateDir() { return process.env.OPENCLAW_STATE_DIR || path.join(process.env.HOME, ".openclaw"); } function configPath() { return process.env.OPENCLAW_CONFIG_PATH || path.join(stateDir(), "openclaw.json"); } function realPathMaybe(filePath) { try { return fs.realpathSync(filePath); } catch { return path.resolve(filePath); } } function assertPathInside(parentPath, childPath, label) { const parent = realPathMaybe(parentPath); const child = realPathMaybe(childPath); const relative = path.relative(parent, child); if (relative.startsWith("..") || path.isAbsolute(relative)) { throw new Error(`${label} resolved outside ${parentPath}: ${child}`); } } function writeJson(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`); } function installRecords() { const indexPath = path.join(stateDir(), "plugins", "installs.json"); const index = fs.existsSync(indexPath) ? readJson(indexPath) : {}; const cfg = fs.existsSync(configPath()) ? readJson(configPath()) : {}; return index.installRecords || index.records || cfg.plugins?.installs || {}; } function pluginInstallPath() { const pluginId = requireEnv("PLUGIN_ID"); const inspect = fs.existsSync("/tmp/openclaw-plugin-inspect.json") ? readJson("/tmp/openclaw-plugin-inspect.json") : {}; const record = installRecords()[pluginId] || inspect.install; if (!record) { throw new Error(`missing ${pluginId} install record`); } if (record.source !== "npm" || record.artifactKind !== "npm-pack") { throw new Error(`expected npm-pack install record: ${JSON.stringify(record)}`); } return String(record.installPath || "").replace(/^~(?=$|\/)/u, process.env.HOME); } function writeFixture() { const dir = process.argv[3]; if (!dir) { throw new Error("write-fixture requires output dir"); } const pluginId = requireEnv("PLUGIN_ID"); const pluginName = requireEnv("PLUGIN_NAME"); const version = requireEnv("PLUGIN_VERSION"); const toolName = requireEnv("TOOL_NAME"); const seed = requireEnv("SEED"); writeJson(path.join(dir, "package.json"), { name: pluginName, version, dependencies: { slugify: "^1.6.6" }, openclaw: { extensions: ["./index.js"] }, }); writeJson(path.join(dir, "openclaw.plugin.json"), { id: pluginId, name: "E2E Slug Tool", description: "Docker E2E plugin tool fixture", activation: { onStartup: true }, contracts: { tools: [toolName] }, configSchema: { type: "object", additionalProperties: false }, }); fs.writeFileSync( path.join(dir, "index.js"), `const slugify = require("slugify");\n` + `const value = slugify(${JSON.stringify(seed)}, { lower: true, strict: true });\n` + `module.exports = {\n` + ` id: ${JSON.stringify(pluginId)},\n` + ` name: "E2E Slug Tool",\n` + ` register(api) {\n` + ` api.registerTool({\n` + ` name: ${JSON.stringify(toolName)},\n` + ` description: "Return the hidden Docker E2E slug generated by the plugin dependency.",\n` + ` parameters: { type: "object", properties: {}, additionalProperties: false },\n` + ` async execute() {\n` + ` return { content: [{ type: "text", text: value }] };\n` + ` },\n` + ` });\n` + ` },\n` + `};\n`, ); } function configure() { const modelRef = requireEnv("MODEL_REF"); const pluginId = requireEnv("PLUGIN_ID"); const toolName = requireEnv("TOOL_NAME"); const cfgPath = configPath(); const cfg = fs.existsSync(cfgPath) ? readJson(cfgPath) : {}; const [providerId, modelId] = modelRef.split("/"); if (providerId !== "openai" || !modelId) { throw new Error(`live plugin tool E2E expects an openai/* model, got ${modelRef}`); } cfg.plugins = { ...cfg.plugins, enabled: true, allow: Array.from(new Set([...(cfg.plugins?.allow || []), "openai", pluginId])).toSorted( (left, right) => left.localeCompare(right), ), entries: { ...cfg.plugins?.entries, openai: { ...cfg.plugins?.entries?.openai, enabled: true }, [pluginId]: { ...cfg.plugins?.entries?.[pluginId], enabled: true }, }, }; cfg.tools = { ...cfg.tools, allow: [toolName], }; cfg.models = { ...cfg.models, mode: "merge", providers: { ...cfg.models?.providers, openai: { ...cfg.models?.providers?.openai, api: "openai-responses", baseUrl: (process.env.OPENAI_BASE_URL || "https://api.openai.com/v1").trim(), apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, agentRuntime: { id: "pi" }, timeoutSeconds: 300, models: [ { id: modelId, name: modelId, api: "openai-responses", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, contextTokens: 96000, maxTokens: 512, }, ], }, }, }; cfg.agents = { ...cfg.agents, defaults: { ...cfg.agents?.defaults, model: { primary: modelRef, fallbacks: [] }, models: { ...cfg.agents?.defaults?.models, [modelRef]: { ...cfg.agents?.defaults?.models?.[modelRef], agentRuntime: { id: "pi" }, params: { transport: "sse", openaiWsWarmup: false }, }, }, workspace: path.join(stateDir(), "workspace"), skipBootstrap: true, timeoutSeconds: 420, }, }; writeJson(cfgPath, cfg); } function findDependencyPackageJson(packageName) { const installPath = pluginInstallPath(); const npmRoot = path.join(stateDir(), "npm"); return [ path.join(installPath, "node_modules", packageName, "package.json"), path.join(npmRoot, "node_modules", packageName, "package.json"), ].find((candidate) => fs.existsSync(candidate)); } function assertInstalled() { const pluginId = requireEnv("PLUGIN_ID"); const pluginName = requireEnv("PLUGIN_NAME"); const toolName = requireEnv("TOOL_NAME"); const npmRoot = path.join(stateDir(), "npm"); const installPath = pluginInstallPath(); assertPathInside(npmRoot, installPath, "fixture plugin install path"); const packageJson = path.join(installPath, "package.json"); if (!fs.existsSync(packageJson)) { throw new Error(`missing fixture plugin package.json: ${packageJson}`); } const pkg = readJson(packageJson); if (pkg.name !== pluginName) { throw new Error(`unexpected fixture package name: ${pkg.name}`); } const slugifyPackageJson = findDependencyPackageJson("slugify"); if (!slugifyPackageJson) { throw new Error("missing slugify dependency installed by npm-pack plugin install"); } assertPathInside(npmRoot, slugifyPackageJson, "slugify dependency"); const list = readJson("/tmp/openclaw-plugins-list.json"); const plugin = (list.plugins || []).find((entry) => entry.id === pluginId); if (!plugin || plugin.enabled !== true || plugin.status !== "loaded") { throw new Error(`fixture plugin was not enabled+loaded: ${JSON.stringify(plugin)}`); } const inspect = readJson("/tmp/openclaw-plugin-inspect.json"); const toolNames = Array.isArray(inspect.tools) ? inspect.tools.flatMap((entry) => (Array.isArray(entry?.names) ? entry.names : [])) : []; if (!toolNames.includes(toolName)) { throw new Error(`fixture tool was not registered: ${JSON.stringify(inspect.tools)}`); } } function assertAgentTurn() { const expected = requireEnv("EXPECTED_SLUG"); const toolName = requireEnv("TOOL_NAME"); const stdout = fs.readFileSync("/tmp/openclaw-agent.json", "utf8"); const stderr = fs.existsSync("/tmp/openclaw-agent.err") ? fs.readFileSync("/tmp/openclaw-agent.err", "utf8") : ""; const response = JSON.parse(stdout); const text = (response.payloads || []).map((payload) => payload?.text || "").join("\n"); if (!text.includes(expected)) { throw new Error( `live agent reply did not contain tool slug ${expected}:\nstdout=${stdout}\nstderr=${stderr}`, ); } const sessionsDir = path.join(stateDir(), "agents", "main", "sessions"); const sessionFiles = fs .readdirSync(sessionsDir, { recursive: true }) .map((entry) => path.join(sessionsDir, String(entry))) .filter((entry) => entry.endsWith(".jsonl") && fs.existsSync(entry)); const transcript = sessionFiles.map((file) => fs.readFileSync(file, "utf8")).join("\n"); if (!transcript.includes(toolName) || !transcript.includes(expected)) { throw new Error( `session transcript did not show ${toolName} returning ${expected}; checked ${sessionFiles.join(", ")}`, ); } } const commands = { "write-fixture": writeFixture, configure, "assert-installed": assertInstalled, "assert-agent-turn": assertAgentTurn, }; const fn = commands[command]; if (!fn) { throw new Error(`unknown live plugin tool assertion command: ${command}`); } fn();