138 lines
4.1 KiB
JavaScript
138 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const {
|
|
dispatchTask,
|
|
parseArgs,
|
|
readConfig,
|
|
summarizeCliResult,
|
|
} = require('../tools/erp_task_dispatcher');
|
|
const { validateTaskEnvelope } = require('../tools/erp_task_contract');
|
|
|
|
function resolvePath(filePath, cwd = process.cwd()) {
|
|
if (!filePath) return '';
|
|
return path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
|
|
}
|
|
|
|
function readTaskFile(taskPath) {
|
|
const resolved = resolvePath(taskPath);
|
|
const text = fs.readFileSync(resolved, 'utf8').replace(/^\uFEFF/, '');
|
|
return JSON.parse(text);
|
|
}
|
|
|
|
function validationResult(validation, expectedOperation = '') {
|
|
const task = validation.task || {};
|
|
const operation = task.operation || (validation.envelope && validation.envelope.operation) || null;
|
|
const route = task.route || (validation.envelope && validation.envelope.route) || null;
|
|
const violations = [...(validation.violations || [])];
|
|
if (expectedOperation && operation && operation !== expectedOperation) {
|
|
violations.push(`expected operation ${expectedOperation}, received ${operation}`);
|
|
}
|
|
|
|
const status = violations.length
|
|
? 'invalid_task'
|
|
: (validation.status || 'invalid_task');
|
|
return {
|
|
status,
|
|
reason: status === 'needs_clarification' ? 'adapter_not_ready' : 'invalid_task',
|
|
operation,
|
|
route,
|
|
violations,
|
|
customerMessage: status === 'needs_clarification'
|
|
? 'Task needs clarification before ERP execution.'
|
|
: 'The ERP task JSON is invalid and was not sent to an executor.',
|
|
};
|
|
}
|
|
|
|
async function runTaskFile(taskPath, options = {}) {
|
|
const input = readTaskFile(taskPath);
|
|
const validation = validateTaskEnvelope(input);
|
|
if (!validation.valid) return validationResult(validation, options.expectedOperation);
|
|
|
|
const expectedOperation = options.expectedOperation || '';
|
|
if (expectedOperation && validation.task.operation !== expectedOperation) {
|
|
return validationResult(validation, expectedOperation);
|
|
}
|
|
|
|
const result = await dispatchTask(validation.envelope, {
|
|
mode: options.mode || 'dry-run',
|
|
config: options.config || {},
|
|
auditDir: options.auditDir,
|
|
source: options.source,
|
|
sender: options.sender,
|
|
senderId: options.senderId,
|
|
senderName: options.senderName,
|
|
chatId: options.chatId,
|
|
channelId: options.channelId,
|
|
handlers: options.handlers,
|
|
registryPath: options.registryPath,
|
|
taskLock: options.taskLock,
|
|
});
|
|
return summarizeCliResult(result);
|
|
}
|
|
|
|
function helpText() {
|
|
return [
|
|
'Usage:',
|
|
' node scripts/run_erp_task.js --task task.json [--mode dry-run|execute] [--config config.json] [--audit-dir dir] [--json]',
|
|
'',
|
|
'Runs a validated ERP task JSON through the dispatcher. The default mode is dry-run.',
|
|
].join('\n');
|
|
}
|
|
|
|
function exitCodeForResult(result) {
|
|
if (!result || result.status === 'blocked' || result.status === 'invalid_task'
|
|
|| result.status === 'needs_clarification') return 2;
|
|
return 0;
|
|
}
|
|
|
|
async function main(argv = process.argv.slice(2)) {
|
|
const args = parseArgs(argv);
|
|
if (args.help || !args.task) {
|
|
console.log(helpText());
|
|
process.exitCode = args.help ? 0 : 1;
|
|
return;
|
|
}
|
|
|
|
const result = await runTaskFile(args.task, {
|
|
mode: args.mode,
|
|
config: readConfig(args.config),
|
|
auditDir: args.auditDir ? resolvePath(args.auditDir) : undefined,
|
|
source: {
|
|
sender: args.sender,
|
|
senderId: args.senderId,
|
|
senderName: args.senderName,
|
|
chatId: args.chatId,
|
|
channelId: args.channelId,
|
|
},
|
|
});
|
|
|
|
if (args.json) {
|
|
console.log(JSON.stringify(result, null, 2));
|
|
} else {
|
|
console.log(result.customerMessage || result.status);
|
|
if (result.auditPath) console.log(`audit: ${result.auditPath}`);
|
|
if (Array.isArray(result.violations) && result.violations.length) {
|
|
console.log(`violations: ${result.violations.join('; ')}`);
|
|
}
|
|
}
|
|
process.exitCode = exitCodeForResult(result);
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch((error) => {
|
|
console.error(error.stack || error.message);
|
|
process.exitCode = 1;
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
resolvePath,
|
|
readTaskFile,
|
|
validationResult,
|
|
runTaskFile,
|
|
exitCodeForResult,
|
|
main,
|
|
};
|