@o-lang/olang 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +30 -46
- package/package.json +1 -1
- package/src/parser.js +1 -1
- package/src/runtime.js +40 -25
package/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { Command } = require('commander');
|
|
3
|
-
const { parse } = require('
|
|
4
|
-
const { execute } = require('
|
|
3
|
+
const { parse } = require('./src/parser');
|
|
4
|
+
const { execute } = require('./src/runtime');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
@@ -49,61 +49,47 @@ async function defaultMockResolver(action, context) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Built-in Math Resolver
|
|
53
|
-
* Supports action strings like: add(1,2), subtract(5,2), multiply(2,3), divide(6,3), sum([1,2,3])
|
|
54
|
-
* Note: runtime handles calculate steps; this resolver helps when parser emits actions with math strings.
|
|
52
|
+
* Built-in Math Resolver
|
|
55
53
|
*/
|
|
56
54
|
async function builtInMathResolver(action, context) {
|
|
57
55
|
if (!action || typeof action !== 'string') return null;
|
|
58
56
|
|
|
59
|
-
// Replace contextual placeholders {var}
|
|
60
57
|
const a = action.replace(/\{([^\}]+)\}/g, (_, k) => {
|
|
61
58
|
const v = context[k.trim()];
|
|
62
59
|
return v !== undefined ? v : `{${k}}`;
|
|
63
60
|
});
|
|
64
61
|
|
|
65
|
-
// simple function matches
|
|
66
62
|
let m;
|
|
67
|
-
m = a.match(/^add\(([^,]+),\s*([^)]+)\)$/i);
|
|
68
|
-
if (m) return parseFloat(m[1])
|
|
63
|
+
m = a.match(/^add\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) + parseFloat(m[2]);
|
|
64
|
+
m = a.match(/^subtract\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) - parseFloat(m[2]);
|
|
65
|
+
m = a.match(/^multiply\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) * parseFloat(m[2]);
|
|
66
|
+
m = a.match(/^divide\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) / parseFloat(m[2]);
|
|
67
|
+
m = a.match(/^sum\(\s*\[([^\]]+)\]\s*\)$/i); if (m) return m[1].split(',').map(s => parseFloat(s.trim())).reduce((s, v) => s + v, 0);
|
|
69
68
|
|
|
70
|
-
m = a.match(/^subtract\(([^,]+),\s*([^)]+)\)$/i);
|
|
71
|
-
if (m) return parseFloat(m[1]) - parseFloat(m[2]);
|
|
72
|
-
|
|
73
|
-
m = a.match(/^multiply\(([^,]+),\s*([^)]+)\)$/i);
|
|
74
|
-
if (m) return parseFloat(m[1]) * parseFloat(m[2]);
|
|
75
|
-
|
|
76
|
-
m = a.match(/^divide\(([^,]+),\s*([^)]+)\)$/i);
|
|
77
|
-
if (m) return parseFloat(m[1]) / parseFloat(m[2]);
|
|
78
|
-
|
|
79
|
-
m = a.match(/^sum\(\s*\[([^\]]+)\]\s*\)$/i);
|
|
80
|
-
if (m) {
|
|
81
|
-
const arr = m[1].split(',').map(s => parseFloat(s.trim()));
|
|
82
|
-
return arr.reduce((s, v) => s + v, 0);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// not a math action
|
|
86
69
|
return null;
|
|
87
70
|
}
|
|
88
71
|
|
|
89
72
|
/**
|
|
90
|
-
* Resolver chaining
|
|
73
|
+
* Resolver chaining with verbose + context logging
|
|
91
74
|
*/
|
|
92
|
-
function createResolverChain(resolvers) {
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
function createResolverChain(resolvers, verbose = false) {
|
|
76
|
+
const chain = resolvers.slice();
|
|
77
|
+
const wrapped = async (action, context) => {
|
|
78
|
+
let lastResult;
|
|
79
|
+
for (let i = 0; i < chain.length; i++) {
|
|
95
80
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
console.error(`❌ Resolver error for action "${action}":`, err.message);
|
|
102
|
-
throw err;
|
|
81
|
+
const res = await chain[i](action, context);
|
|
82
|
+
context[`__resolver_${i}`] = res;
|
|
83
|
+
lastResult = res;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error(`❌ Resolver ${i} failed for action "${action}":`, e.message);
|
|
103
86
|
}
|
|
104
87
|
}
|
|
105
|
-
|
|
88
|
+
if (verbose) console.log(`[Resolver Chain] action="${action}" lastResult=`, lastResult);
|
|
89
|
+
return lastResult;
|
|
106
90
|
};
|
|
91
|
+
wrapped._chain = chain;
|
|
92
|
+
return wrapped;
|
|
107
93
|
}
|
|
108
94
|
|
|
109
95
|
function loadSingleResolver(specifier) {
|
|
@@ -111,9 +97,7 @@ function loadSingleResolver(specifier) {
|
|
|
111
97
|
|
|
112
98
|
try {
|
|
113
99
|
const resolver = require(specifier);
|
|
114
|
-
if (typeof resolver !== 'function')
|
|
115
|
-
throw new Error(`Resolver must export a function`);
|
|
116
|
-
}
|
|
100
|
+
if (typeof resolver !== 'function') throw new Error(`Resolver must export a function`);
|
|
117
101
|
console.log(`📦 Loaded resolver: ${specifier}`);
|
|
118
102
|
return resolver;
|
|
119
103
|
} catch (e1) {
|
|
@@ -133,7 +117,7 @@ function loadSingleResolver(specifier) {
|
|
|
133
117
|
/**
|
|
134
118
|
* loadResolverChain: include built-in math resolver first, then user resolvers, then default mock resolver
|
|
135
119
|
*/
|
|
136
|
-
function loadResolverChain(specifiers) {
|
|
120
|
+
function loadResolverChain(specifiers, verbose = false) {
|
|
137
121
|
const userResolvers = specifiers?.map(loadSingleResolver) || [];
|
|
138
122
|
const resolvers = [builtInMathResolver, ...userResolvers, defaultMockResolver];
|
|
139
123
|
|
|
@@ -143,7 +127,7 @@ function loadResolverChain(specifiers) {
|
|
|
143
127
|
console.log(`📦 Loaded user resolvers: ${specifiers.join(', ')}`);
|
|
144
128
|
}
|
|
145
129
|
|
|
146
|
-
return createResolverChain(resolvers);
|
|
130
|
+
return createResolverChain(resolvers, verbose);
|
|
147
131
|
}
|
|
148
132
|
|
|
149
133
|
/**
|
|
@@ -157,7 +141,7 @@ program
|
|
|
157
141
|
.command('run <file>')
|
|
158
142
|
.option(
|
|
159
143
|
'-r, --resolver <specifier>',
|
|
160
|
-
'Resolver (npm package or local path). Can be used multiple times
|
|
144
|
+
'Resolver (npm package or local path). Can be used multiple times.',
|
|
161
145
|
(val, acc) => { acc.push(val); return acc; },
|
|
162
146
|
[]
|
|
163
147
|
)
|
|
@@ -166,13 +150,13 @@ program
|
|
|
166
150
|
'Input parameters',
|
|
167
151
|
(val, acc = {}) => {
|
|
168
152
|
const [k, v] = val.split('=');
|
|
169
|
-
// try to parse numbers, preserve strings otherwise
|
|
170
153
|
const parsed = v === undefined ? '' : (v === 'true' ? true : (v === 'false' ? false : (isNaN(v) ? v : parseFloat(v))));
|
|
171
154
|
acc[k] = parsed;
|
|
172
155
|
return acc;
|
|
173
156
|
},
|
|
174
157
|
{}
|
|
175
158
|
)
|
|
159
|
+
.option('-v, --verbose', 'Verbose mode: logs resolver outputs and context after each step')
|
|
176
160
|
.action(async (file, options) => {
|
|
177
161
|
try {
|
|
178
162
|
ensureOlExtension(file);
|
|
@@ -180,8 +164,8 @@ program
|
|
|
180
164
|
const content = fs.readFileSync(file, 'utf8');
|
|
181
165
|
const workflow = parse(content);
|
|
182
166
|
|
|
183
|
-
const resolver = loadResolverChain(options.resolver);
|
|
184
|
-
const result = await execute(workflow, options.input, resolver);
|
|
167
|
+
const resolver = loadResolverChain(options.resolver, options.verbose);
|
|
168
|
+
const result = await execute(workflow, options.input, resolver, options.verbose);
|
|
185
169
|
|
|
186
170
|
console.log('\n=== Workflow Result ===');
|
|
187
171
|
console.log(JSON.stringify(result, null, 2));
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
package/src/runtime.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// src/runtime.js
|
|
2
1
|
const fs = require('fs');
|
|
3
2
|
|
|
4
3
|
class RuntimeAPI {
|
|
5
|
-
constructor() {
|
|
4
|
+
constructor({ verbose = false } = {}) {
|
|
6
5
|
this.context = {};
|
|
7
6
|
this.resources = {};
|
|
8
7
|
this.agentMap = {};
|
|
9
8
|
this.events = {};
|
|
10
9
|
this.workflowSteps = []; // store for evolve lookup
|
|
10
|
+
this.verbose = verbose; // verbose logging flag
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
on(eventName, cb) {
|
|
@@ -47,9 +47,6 @@ class RuntimeAPI {
|
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// --------------------------
|
|
51
|
-
// Math helper functions
|
|
52
|
-
// --------------------------
|
|
53
50
|
mathFunctions = {
|
|
54
51
|
add: (a, b) => a + b,
|
|
55
52
|
subtract: (a, b) => a - b,
|
|
@@ -71,15 +68,12 @@ class RuntimeAPI {
|
|
|
71
68
|
};
|
|
72
69
|
|
|
73
70
|
evaluateMath(expr) {
|
|
74
|
-
// Replace context variables in curly braces
|
|
75
71
|
expr = expr.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
76
72
|
const value = this.getNested(this.context, path.trim());
|
|
77
|
-
// if value is string, wrap in quotes for functions that accept strings
|
|
78
73
|
if (typeof value === 'string') return `"${value.replace(/"/g, '\\"')}"`;
|
|
79
74
|
return value !== undefined ? value : 0;
|
|
80
75
|
});
|
|
81
76
|
|
|
82
|
-
// expose safe functions only
|
|
83
77
|
const funcNames = Object.keys(this.mathFunctions);
|
|
84
78
|
const safeFunc = {};
|
|
85
79
|
funcNames.forEach(fn => {
|
|
@@ -87,7 +81,6 @@ class RuntimeAPI {
|
|
|
87
81
|
});
|
|
88
82
|
|
|
89
83
|
try {
|
|
90
|
-
// eslint-disable-next-line no-new-func
|
|
91
84
|
const f = new Function(...funcNames, `return ${expr};`);
|
|
92
85
|
return f(...funcNames.map(fn => safeFunc[fn]));
|
|
93
86
|
} catch (e) {
|
|
@@ -100,9 +93,33 @@ class RuntimeAPI {
|
|
|
100
93
|
// Execute workflow step
|
|
101
94
|
// --------------------------
|
|
102
95
|
async executeStep(step, agentResolver) {
|
|
103
|
-
|
|
96
|
+
const stepType = step.type;
|
|
97
|
+
|
|
98
|
+
// Helper: execute all resolvers for this step action
|
|
99
|
+
const runAllResolvers = async (action) => {
|
|
100
|
+
const outputs = [];
|
|
101
|
+
if (agentResolver && Array.isArray(agentResolver._chain)) {
|
|
102
|
+
for (let idx = 0; idx < agentResolver._chain.length; idx++) {
|
|
103
|
+
const resolver = agentResolver._chain[idx];
|
|
104
|
+
try {
|
|
105
|
+
const out = await resolver(action, this.context);
|
|
106
|
+
outputs.push(out);
|
|
107
|
+
this.context[`__resolver_${idx}`] = out;
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.error(`❌ Resolver ${idx} error for action "${action}":`, e.message);
|
|
110
|
+
outputs.push(null);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
const out = await agentResolver(action, this.context);
|
|
115
|
+
outputs.push(out);
|
|
116
|
+
this.context['__resolver_0'] = out;
|
|
117
|
+
}
|
|
118
|
+
return outputs[outputs.length - 1]; // last result as primary
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
switch (stepType) {
|
|
104
122
|
case 'calculate': {
|
|
105
|
-
// step.expression or actionRaw can contain math expression strings
|
|
106
123
|
const expr = step.expression || step.actionRaw;
|
|
107
124
|
const result = this.evaluateMath(expr);
|
|
108
125
|
if (step.saveAs) this.context[step.saveAs] = result;
|
|
@@ -115,23 +132,16 @@ class RuntimeAPI {
|
|
|
115
132
|
return value !== undefined ? String(value) : `{${path}}`;
|
|
116
133
|
});
|
|
117
134
|
|
|
118
|
-
// Provide fallback math recognition for action lines too (e.g., add(1,2))
|
|
119
|
-
// Try simple math function calls in action form
|
|
120
135
|
const mathCall = action.match(/^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i);
|
|
121
136
|
if (mathCall) {
|
|
122
137
|
const fn = mathCall[1].toLowerCase();
|
|
123
138
|
const argsRaw = mathCall[2];
|
|
124
|
-
// simple split by comma (doesn't handle nested arrays/funcs) — fine for workflow math
|
|
125
139
|
const args = argsRaw.split(',').map(s => s.trim()).map(s => {
|
|
126
|
-
// if it's a quoted string
|
|
127
140
|
if (/^".*"$/.test(s) || /^'.*'$/.test(s)) return s.slice(1, -1);
|
|
128
|
-
// try number
|
|
129
141
|
if (!isNaN(s)) return parseFloat(s);
|
|
130
|
-
// variable lookup
|
|
131
142
|
const lookup = s.replace(/^\{|\}$/g, '').trim();
|
|
132
143
|
return this.getNested(this.context, lookup);
|
|
133
144
|
});
|
|
134
|
-
|
|
135
145
|
if (this.mathFunctions[fn]) {
|
|
136
146
|
const value = this.mathFunctions[fn](...args);
|
|
137
147
|
if (step.saveAs) this.context[step.saveAs] = value;
|
|
@@ -139,20 +149,19 @@ class RuntimeAPI {
|
|
|
139
149
|
}
|
|
140
150
|
}
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
const res = await agentResolver(action, this.context);
|
|
152
|
+
const res = await runAllResolvers(action);
|
|
144
153
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
145
154
|
break;
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
case 'use': {
|
|
149
|
-
const res = await
|
|
158
|
+
const res = await runAllResolvers(`Use ${step.tool}`);
|
|
150
159
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
151
160
|
break;
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
case 'ask': {
|
|
155
|
-
const res = await
|
|
164
|
+
const res = await runAllResolvers(`Ask ${step.target}`);
|
|
156
165
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
157
166
|
break;
|
|
158
167
|
}
|
|
@@ -211,7 +220,7 @@ class RuntimeAPI {
|
|
|
211
220
|
revisedAction = revisedAction.replace(/(")$/, `\n\n[IMPROVEMENT FEEDBACK: ${step.feedback}]$1`);
|
|
212
221
|
}
|
|
213
222
|
|
|
214
|
-
currentOutput = await
|
|
223
|
+
currentOutput = await runAllResolvers(revisedAction);
|
|
215
224
|
this.context[varName] = currentOutput;
|
|
216
225
|
|
|
217
226
|
this.emit('debrief', {
|
|
@@ -244,6 +253,12 @@ class RuntimeAPI {
|
|
|
244
253
|
break;
|
|
245
254
|
}
|
|
246
255
|
}
|
|
256
|
+
|
|
257
|
+
// Verbose logging of context after each step
|
|
258
|
+
if (this.verbose) {
|
|
259
|
+
console.log(`\n[Step: ${step.type} | saveAs: ${step.saveAs || 'N/A'}]`);
|
|
260
|
+
console.log(JSON.stringify(this.context, null, 2));
|
|
261
|
+
}
|
|
247
262
|
}
|
|
248
263
|
|
|
249
264
|
async getUserInput(question) {
|
|
@@ -273,8 +288,8 @@ class RuntimeAPI {
|
|
|
273
288
|
}
|
|
274
289
|
}
|
|
275
290
|
|
|
276
|
-
async function execute(workflow, inputs, agentResolver) {
|
|
277
|
-
const rt = new RuntimeAPI();
|
|
291
|
+
async function execute(workflow, inputs, agentResolver, verbose = false) {
|
|
292
|
+
const rt = new RuntimeAPI({ verbose });
|
|
278
293
|
return rt.executeWorkflow(workflow, inputs, agentResolver);
|
|
279
294
|
}
|
|
280
295
|
|