@o-lang/olang 1.0.3 → 1.0.4

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.
Files changed (3) hide show
  1. package/cli.js +39 -4
  2. package/package.json +1 -1
  3. package/src/runtime.js +61 -3
package/cli.js CHANGED
@@ -46,6 +46,34 @@ async function defaultMockResolver(action, context) {
46
46
  return `[Unhandled: ${action}]`;
47
47
  }
48
48
 
49
+ /**
50
+ * Built-in Math Resolver
51
+ * Supports minimal math operations for workflows
52
+ */
53
+ async function builtInMathResolver(action, context) {
54
+ // Replace variables in context
55
+ action = action.replace(/\{([^\}]+)\}/g, (_, key) => {
56
+ const val = context[key.trim()];
57
+ return val !== undefined ? val : `{${key}}`;
58
+ });
59
+
60
+ let match;
61
+
62
+ match = action.match(/^add\(([^,]+),\s*([^)]+)\)$/);
63
+ if (match) return parseFloat(match[1]) + parseFloat(match[2]);
64
+
65
+ match = action.match(/^subtract\(([^,]+),\s*([^)]+)\)$/);
66
+ if (match) return parseFloat(match[1]) - parseFloat(match[2]);
67
+
68
+ match = action.match(/^multiply\(([^,]+),\s*([^)]+)\)$/);
69
+ if (match) return parseFloat(match[1]) * parseFloat(match[2]);
70
+
71
+ match = action.match(/^divide\(([^,]+),\s*([^)]+)\)$/);
72
+ if (match) return parseFloat(match[1]) / parseFloat(match[2]);
73
+
74
+ return null; // not handled
75
+ }
76
+
49
77
  /**
50
78
  * Resolver chaining mechanism
51
79
  */
@@ -90,13 +118,20 @@ function loadSingleResolver(specifier) {
90
118
  }
91
119
  }
92
120
 
121
+ /**
122
+ * Updated resolver chain
123
+ * Built-in math resolver is added first, then user resolvers, then default mock
124
+ */
93
125
  function loadResolverChain(specifiers) {
126
+ const userResolvers = specifiers?.map(loadSingleResolver) || [];
127
+ const resolvers = [builtInMathResolver, ...userResolvers, defaultMockResolver];
128
+
94
129
  if (!specifiers || specifiers.length === 0) {
95
- console.log('ℹ️ No resolver provided. Using default mock resolver.');
96
- return defaultMockResolver;
130
+ console.log('ℹ️ No resolver provided. Using built-in math + default mock resolver.');
131
+ } else {
132
+ console.log(`📦 Loaded user resolvers: ${specifiers.join(', ')}`);
97
133
  }
98
134
 
99
- const resolvers = specifiers.map(loadSingleResolver);
100
135
  return createResolverChain(resolvers);
101
136
  }
102
137
 
@@ -120,7 +155,7 @@ program
120
155
  'Input parameters',
121
156
  (val, acc = {}) => {
122
157
  const [k, v] = val.split('=');
123
- acc[k] = v;
158
+ acc[k] = isNaN(v) ? v : parseFloat(v);
124
159
  return acc;
125
160
  },
126
161
  {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "author": "Olalekan Ogundipe <info@workfily.com>",
5
5
  "description": "O-Lang: A governance language for user-directed, rule-enforced agent workflows",
6
6
  "main": "./src/index.js",
package/src/runtime.js CHANGED
@@ -24,9 +24,11 @@ class RuntimeAPI {
24
24
  evaluateCondition(cond, ctx) {
25
25
  cond = cond.trim();
26
26
  const eq = cond.match(/^\{(.+)\}\s+equals\s+"(.*)"$/);
27
- if (eq) return this.getNested(ctx, eq[1]) === eq[2];
27
+ if (eq) return this.getNested(ctx, eq[1]) == eq[2];
28
28
  const gt = cond.match(/^\{(.+)\}\s+greater than\s+(\d+\.?\d*)$/);
29
29
  if (gt) return parseFloat(this.getNested(ctx, gt[1])) > parseFloat(gt[2]);
30
+ const lt = cond.match(/^\{(.+)\}\s+less than\s+(\d+\.?\d*)$/);
31
+ if (lt) return parseFloat(this.getNested(ctx, lt[1])) < parseFloat(lt[2]);
30
32
  return Boolean(this.getNested(ctx, cond.replace(/\{|\}/g, '')));
31
33
  }
32
34
 
@@ -45,8 +47,66 @@ class RuntimeAPI {
45
47
  return null;
46
48
  }
47
49
 
50
+ // --------------------------
51
+ // Math helper functions
52
+ // --------------------------
53
+ mathFunctions = {
54
+ add: (a, b) => a + b,
55
+ subtract: (a, b) => a - b,
56
+ multiply: (a, b) => a * b,
57
+ divide: (a, b) => a / b,
58
+ equals: (a, b) => a === b,
59
+ greater: (a, b) => a > b,
60
+ less: (a, b) => a < b,
61
+ sum: arr => arr.reduce((acc, val) => acc + val, 0),
62
+ avg: arr => arr.reduce((acc, val) => acc + val, 0) / arr.length,
63
+ min: arr => Math.min(...arr),
64
+ max: arr => Math.max(...arr),
65
+ increment: a => a + 1,
66
+ decrement: a => a - 1,
67
+ round: a => Math.round(a),
68
+ floor: a => Math.floor(a),
69
+ ceil: a => Math.ceil(a),
70
+ abs: a => Math.abs(a)
71
+ };
72
+
73
+ evaluateMath(expr) {
74
+ // Replace context variables in curly braces
75
+ expr = expr.replace(/\{([^\}]+)\}/g, (_, path) => {
76
+ const value = this.getNested(this.context, path.trim());
77
+ return value !== undefined ? value : 0;
78
+ });
79
+
80
+ // Create a function for supported math functions only
81
+ const funcNames = Object.keys(this.mathFunctions);
82
+ const safeFunc = {};
83
+ funcNames.forEach(fn => {
84
+ safeFunc[fn] = this.mathFunctions[fn];
85
+ });
86
+
87
+ try {
88
+ // eslint-disable-next-line no-new-func
89
+ const f = new Function(...funcNames, `return ${expr};`);
90
+ return f(...funcNames.map(fn => safeFunc[fn]));
91
+ } catch (e) {
92
+ console.warn(`[O-Lang] Failed to evaluate math expression "${expr}": ${e.message}`);
93
+ return 0;
94
+ }
95
+ }
96
+
97
+ // --------------------------
98
+ // Execute workflow step
99
+ // --------------------------
48
100
  async executeStep(step, agentResolver) {
49
101
  switch (step.type) {
102
+ case 'calculate': {
103
+ // e.g., "add({x}, {y})"
104
+ const expr = step.expression || step.actionRaw;
105
+ const result = this.evaluateMath(expr);
106
+ if (step.saveAs) this.context[step.saveAs] = result;
107
+ break;
108
+ }
109
+
50
110
  case 'action': {
51
111
  const action = step.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
52
112
  const value = this.getNested(this.context, path.trim());
@@ -58,14 +118,12 @@ class RuntimeAPI {
58
118
  }
59
119
 
60
120
  case 'use': {
61
- // "Use <Tool>" step
62
121
  const res = await agentResolver(`Use ${step.tool}`, this.context);
63
122
  if (step.saveAs) this.context[step.saveAs] = res;
64
123
  break;
65
124
  }
66
125
 
67
126
  case 'ask': {
68
- // "Ask <Target>" step
69
127
  const res = await agentResolver(`Ask ${step.target}`, this.context);
70
128
  if (step.saveAs) this.context[step.saveAs] = res;
71
129
  break;