Files
16gagent/scripts/guard-tool-path.mjs
2026-06-06 10:40:48 +08:00

144 lines
5.0 KiB
JavaScript

#!/usr/bin/env node
/**
* Guard 3: Tool Path Guard
* Records current tool call entry points. Soft warning — no hard fail yet.
*
* → Architecture Freeze v2 (PR-1): docs/architecture-freeze-v2.md
* → Adapter §3.2: Tool Adapters
*
* ToolCore not yet implemented — this guard inventories the current state
* and will FAIL only if a NEW unknown tool path appears.
*
* Known tool paths:
* - native tool (dist/commands/*)
* - MCP tool (dist/mcp/*)
* - plugin tool (dist/extensions plugin.json contracts.tools)
* - codex tool (dist/extensions/codex-supervisor/*)
*/
import { readdirSync, readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
const OPENCLAW_HOME = process.env.OPENCLAW_HOME || "/opt/homebrew/lib/node_modules/openclaw";
const DIST_DIR = join(OPENCLAW_HOME, "dist");
const EXTENSIONS_DIR = join(DIST_DIR, "extensions");
function guardToolPaths() {
const issues = [];
const inventory = { native: [], mcp: [], plugin: [], codex: [], unknown: [] };
const knownToolPaths = new Set([
"native", "mcp", "plugin", "codex"
]);
// 1. Native tools — look for tool registrations in commands
const commandsDir = join(DIST_DIR, "commands");
if (existsSync(commandsDir)) {
try {
const entries = readdirSync(commandsDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
inventory.native.push(`commands/${entry.name}`);
}
}
} catch (e) {
issues.push({ level: "WARN", msg: `Could not scan commands dir: ${e.message}` });
}
}
// 2. MCP tools
const mcpDir = join(DIST_DIR, "mcp");
if (existsSync(mcpDir)) {
try {
const files = readdirSync(mcpDir).filter(f => f.endsWith(".js"));
inventory.mcp.push(...files.map(f => `mcp/${f}`));
} catch (e) {
issues.push({ level: "WARN", msg: `Could not scan mcp dir: ${e.message}` });
}
}
// 3. Plugin tools (from openclaw.plugin.json contracts)
if (existsSync(EXTENSIONS_DIR)) {
try {
const extDirs = readdirSync(EXTENSIONS_DIR, { withFileTypes: true })
.filter(d => d.isDirectory());
for (const extDir of extDirs) {
const pluginJsonPath = join(EXTENSIONS_DIR, extDir.name, "openclaw.plugin.json");
if (!existsSync(pluginJsonPath)) continue;
try {
const plugin = JSON.parse(readFileSync(pluginJsonPath, "utf8"));
const tools = plugin.contracts?.tools || [];
if (tools.length > 0) {
// Categorize
if (extDir.name.includes("codex") || extDir.name.includes("supervisor")) {
inventory.codex.push(`${extDir.name}: [${tools.join(", ")}]`);
} else {
inventory.plugin.push(`${extDir.name}: [${tools.join(", ")}]`);
}
}
} catch {}
}
} catch (e) {
issues.push({ level: "WARN", msg: `Could not scan extensions: ${e.message}` });
}
}
// 4. Check for unknown tool paths in key files
const keyFiles = readdirSync(DIST_DIR).filter(f =>
f.endsWith(".js") && (f.includes("tool") || f.includes("command") || f.includes("mcp"))
);
for (const f of keyFiles) {
const name = f.replace(/\.js$/, "").replace(/-[A-Za-z0-9]{8}$/, "");
// Categorize based on naming
if (name.includes("mcp") || name.includes("codex-mcp")) {
if (!inventory.mcp.some(t => t.includes(name.substring(0, 10)))) {
inventory.mcp.push(`dist/${f}`);
}
} else if (name.includes("tool")) {
if (!inventory.native.some(t => t.includes(name.substring(0, 10)))) {
inventory.native.push(`dist/${f}`);
}
}
}
// Report
console.log("");
console.log("═══════════════════════════════════════");
console.log(" Guard 3: Tool Path Guard");
console.log("═══════════════════════════════════════");
console.log("");
console.log(" Tool Path Inventory:");
console.log(` Native tools: ${inventory.native.length} entries`);
console.log(` MCP tools: ${inventory.mcp.length} entries`);
console.log(` Plugin tools: ${inventory.plugin.length} plugins`);
console.log(` Codex tools: ${inventory.codex.length} plugins`);
if (inventory.plugin.length > 0) {
console.log("\n Plugin tool contracts:");
for (const p of inventory.plugin) {
console.log(`${p}`);
}
}
if (inventory.codex.length > 0) {
console.log("\n Codex tool contracts:");
for (const p of inventory.codex) {
console.log(`${p}`);
}
}
// This guard is WARN-only for now (ToolCore not yet implemented)
console.log("");
console.log(" ⚠ WARN: ToolCore not yet enforced");
console.log(" This guard currently inventories tool paths.");
console.log(" After PR-8/PR-9 (ToolCore), it will enforce all tools go through ToolExecutor.");
// No hard fail — informational only at this stage
process.exit(0);
}
guardToolPaths();