| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const { spawnSync } = require('child_process');
- function parseArgs(argv) {
- const args = { toolArgs: [] };
- for (let i = 0; i < argv.length; i++) {
- const token = argv[i];
- if (token === '--') {
- args.toolArgs = argv.slice(i + 1);
- break;
- }
- if (token === '--tool') {
- args.tool = argv[++i];
- continue;
- }
- if (token === '--result-prefix') {
- args.resultPrefix = argv[++i];
- continue;
- }
- }
- return args;
- }
- function usage() {
- return [
- 'Usage:',
- ' node openclaw-tool-runner.js --tool <tool.js> --result-prefix <PREFIX> -- [tool args]',
- '',
- 'The runner resolves tools from ~/.openclaw/tools first and emits PREFIX=<json> after the child tool output.'
- ].join('\n');
- }
- function unique(values) {
- return Array.from(new Set(values.filter(Boolean)));
- }
- function resolveTool(tool) {
- if (!tool) return undefined;
- if (path.isAbsolute(tool) && fs.existsSync(tool)) return tool;
- const cwd = process.cwd();
- const toolRoot = __dirname;
- const projectRoot = path.resolve(__dirname, '..', '..');
- const candidates = unique([
- path.join(toolRoot, tool),
- path.join(cwd, tool),
- path.join(cwd, 'scripts', 'tools', tool),
- path.join(projectRoot, 'scripts', 'tools', tool),
- path.join(projectRoot, tool)
- ]);
- return candidates.find(candidate => fs.existsSync(candidate));
- }
- function extractLastJson(stdout) {
- const text = String(stdout || '').trim();
- if (!text) return undefined;
- for (let i = text.lastIndexOf('{'); i >= 0; i = text.lastIndexOf('{', i - 1)) {
- const candidate = text.slice(i).trim();
- try {
- return JSON.parse(candidate);
- } catch {
- continue;
- }
- }
- return undefined;
- }
- function isUnresolvedValue(value) {
- if (value === undefined || value === null) return true;
- const text = String(value).trim();
- return text === '' || /^\{\{[^}]+\}\}$/.test(text);
- }
- function sanitizeToolArgs(toolArgs) {
- const sanitized = [];
- for (let i = 0; i < toolArgs.length; i++) {
- const token = toolArgs[i];
- const next = toolArgs[i + 1];
- if (token.startsWith('--') && next !== undefined && !next.startsWith('--')) {
- if (!isUnresolvedValue(next)) {
- sanitized.push(token, next);
- }
- i++;
- continue;
- }
- if (!isUnresolvedValue(token)) {
- sanitized.push(token);
- }
- }
- return sanitized;
- }
- function main() {
- const args = parseArgs(process.argv.slice(2));
- if (!args.tool || !args.resultPrefix || args.help) {
- console.log(usage());
- process.exit(args.help ? 0 : 1);
- }
- const toolPath = resolveTool(args.tool);
- if (!toolPath) {
- const result = {
- status: 'error',
- message: `Tool not found: ${args.tool}`,
- searchedFrom: {
- cwd: process.cwd(),
- runnerDir: __dirname
- }
- };
- console.log(`${args.resultPrefix}=${JSON.stringify(result)}`);
- process.exit(1);
- }
- const child = spawnSync(process.execPath, [toolPath, ...sanitizeToolArgs(args.toolArgs)], {
- cwd: process.cwd(),
- encoding: 'utf8',
- maxBuffer: 1024 * 1024 * 100
- });
- if (child.stdout) process.stdout.write(child.stdout);
- if (child.stderr) process.stderr.write(child.stderr);
- const parsed = extractLastJson(child.stdout);
- const result = parsed || {
- status: child.status === 0 ? 'ok' : 'error',
- tool: args.tool,
- toolPath,
- message: parsed ? undefined : 'Tool did not emit parseable JSON on stdout'
- };
- console.log(`${args.resultPrefix}=${JSON.stringify(result)}`);
- if (child.error) {
- console.error(child.error.message);
- process.exit(1);
- }
- process.exit(child.status === null || child.status === undefined ? 1 : child.status);
- }
- main();
|