#!/usr/bin/env node
"use strict";
// @ts-nocheck
Object.defineProperty(exports, "__esModule", { value: true });
const { mergeDeep, isPlainObject } = require('./merge');
const ToolError = require('../errors/ToolError');
function dslInvalid(message) {
return ToolError.invalidParams({
field: 'dsl',
message,
hint: 'DSL directives: runbook, description, step, tool, action, args, arg, when, foreach, continue_on_error.',
});
}
function parseValue(raw) {
const trimmed = String(raw || '').trim();
if (!trimmed) {
return '';
}
if (trimmed === 'true' || trimmed === 'false' || trimmed === 'null') {
return JSON.parse(trimmed);
}
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
return Number(trimmed);
}
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
(trimmed.startsWith('[') && trimmed.endsWith(']')) ||
(trimmed.startsWith('"') && trimmed.endsWith('"'))) {
try {
return JSON.parse(trimmed);
}
catch (error) {
return trimmed;
}
}
return trimmed;
}
function setPath(target, path, value) {
const parts = path.split('.').map((part) => part.trim()).filter(Boolean);
if (parts.length === 0) {
return;
}
let current = target;
for (let idx = 0; idx < parts.length - 1; idx += 1) {
const key = parts[idx];
if (!isPlainObject(current[key])) {
current[key] = {};
}
current = current[key];
}
current[parts[parts.length - 1]] = value;
}
function parseKeyValue(raw) {
const trimmed = String(raw || '').trim();
const eqIndex = trimmed.indexOf('=');
if (eqIndex === -1) {
throw dslInvalid('arg directive requires key=value');
}
const key = trimmed.slice(0, eqIndex).trim();
const valueRaw = trimmed.slice(eqIndex + 1).trim();
if (!key) {
throw dslInvalid('arg directive requires key=value');
}
return { key, value: parseValue(valueRaw) };
}
function parseRunbookDsl(dsl) {
const runbook = { steps: [] };
const lines = String(dsl || '').split(/\r?\n/);
let current = null;
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
const rawLine = lines[lineIndex];
const trimmed = rawLine.trim();
if (!trimmed) {
continue;
}
if (trimmed.startsWith('#') || trimmed.startsWith('//')) {
continue;
}
const spaceIndex = trimmed.indexOf(' ');
const directive = spaceIndex === -1 ? trimmed : trimmed.slice(0, spaceIndex);
const rest = spaceIndex === -1 ? '' : trimmed.slice(spaceIndex + 1).trim();
switch (directive) {
case 'runbook':
runbook.name = rest;
break;
case 'description':
runbook.description = rest;
break;
case 'step': {
const tokens = rest.split(/\s+/).filter(Boolean);
const id = tokens.shift();
const tool = tokens.shift();
const action = tokens.shift();
if (!id || !tool) {
throw dslInvalid(`step requires id and tool at line ${lineIndex + 1}`);
}
const step = { id, tool, args: {} };
if (action) {
step.args.action = action;
}
if (tokens.length > 0) {
const parsed = parseValue(tokens.join(' '));
if (isPlainObject(parsed)) {
step.args = mergeDeep(step.args, parsed);
}
}
runbook.steps.push(step);
current = step;
break;
}
case 'tool':
if (!current) {
throw dslInvalid(`tool directive before step at line ${lineIndex + 1}`);
}
current.tool = rest;
break;
case 'action':
if (!current) {
throw dslInvalid(`action directive before step at line ${lineIndex + 1}`);
}
current.args = current.args || {};
current.args.action = rest;
break;
case 'args': {
if (!current) {
throw dslInvalid(`args directive before step at line ${lineIndex + 1}`);
}
const parsed = parseValue(rest);
if (!isPlainObject(parsed)) {
throw dslInvalid(`args directive expects JSON object at line ${lineIndex + 1}`);
}
current.args = mergeDeep(current.args || {}, parsed);
break;
}
case 'arg': {
if (!current) {
throw dslInvalid(`arg directive before step at line ${lineIndex + 1}`);
}
const { key, value } = parseKeyValue(rest);
current.args = current.args || {};
setPath(current.args, key, value);
break;
}
case 'when': {
if (!current) {
throw dslInvalid(`when directive before step at line ${lineIndex + 1}`);
}
const parsed = parseValue(rest);
current.when = parsed;
break;
}
case 'foreach': {
if (!current) {
throw dslInvalid(`foreach directive before step at line ${lineIndex + 1}`);
}
const parsed = parseValue(rest);
current.foreach = parsed;
break;
}
case 'continue_on_error': {
if (!current) {
throw dslInvalid(`continue_on_error directive before step at line ${lineIndex + 1}`);
}
current.continue_on_error = parseValue(rest) === true;
break;
}
default:
throw dslInvalid(`Unknown DSL directive '${directive}' at line ${lineIndex + 1}`);
}
}
if (!Array.isArray(runbook.steps) || runbook.steps.length === 0) {
throw dslInvalid('runbook DSL must define at least one step');
}
return runbook;
}
module.exports = {
parseRunbookDsl,
};