@o-lang/olang 1.0.16 → 1.0.18

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 (2) hide show
  1. package/cli.js +59 -34
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  const { Command } = require('commander');
3
3
  const { parse } = require('./src/parser');
4
4
  const { execute } = require('./src/runtime');
@@ -22,7 +22,6 @@ function ensureOlExtension(filename) {
22
22
  */
23
23
  async function defaultMockResolver(action, context) {
24
24
  if (!action || typeof action !== 'string') return `[Unhandled: ${String(action)}]`;
25
-
26
25
  if (action.startsWith('Search for ')) {
27
26
  return {
28
27
  title: "HR Policy 2025",
@@ -30,45 +29,38 @@ async function defaultMockResolver(action, context) {
30
29
  url: "mock://hr-policy"
31
30
  };
32
31
  }
33
-
34
32
  if (action.startsWith('Ask ')) {
35
- return "✅ [Mock] Summarized for demonstration.";
33
+ return " [Mock] Summarized for demonstration.";
36
34
  }
37
-
38
35
  if (action.startsWith('Notify ')) {
39
36
  const recipient = action.match(/Notify (\S+)/)?.[1] || 'user@example.com';
40
- return `📬 Notification sent to ${recipient}`;
37
+ return ` 📬 Notification sent to ${recipient}`;
41
38
  }
42
-
43
39
  if (action.startsWith('Debrief ') || action.startsWith('Evolve ')) {
44
40
  console.log(`[O-Lang] ${action}`);
45
41
  return 'Acknowledged';
46
42
  }
47
-
48
43
  return `[Unhandled: ${action}]`;
49
44
  }
45
+ defaultMockResolver.resolverName = 'defaultMockResolver';
50
46
 
51
47
  /**
52
48
  * Built-in Math Resolver
53
49
  */
54
50
  async function builtInMathResolver(action, context) {
55
51
  if (!action || typeof action !== 'string') return null;
56
-
57
52
  const a = action.replace(/\{([^\}]+)\}/g, (_, k) => {
58
53
  const v = context[k.trim()];
59
54
  return v !== undefined ? v : `{${k}}`;
60
55
  });
61
-
62
56
  let m;
63
57
  m = a.match(/^add\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) + parseFloat(m[2]);
64
58
  m = a.match(/^subtract\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) - parseFloat(m[2]);
65
59
  m = a.match(/^multiply\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) * parseFloat(m[2]);
66
60
  m = a.match(/^divide\(([^,]+),\s*([^)]+)\)$/i); if (m) return parseFloat(m[1]) / parseFloat(m[2]);
67
61
  m = a.match(/^sum\(\s*\[([^\]]+)\]\s*\)$/i); if (m) return m[1].split(',').map(s => parseFloat(s.trim())).reduce((s, v) => s + v, 0);
68
-
69
62
  return null;
70
63
  }
71
- // Add resolver metadata so workflow policy recognizes it
72
64
  builtInMathResolver.resolverName = 'builtInMathResolver';
73
65
 
74
66
  /**
@@ -84,7 +76,7 @@ function createResolverChain(resolvers, verbose = false) {
84
76
  context[`__resolver_${i}`] = res;
85
77
  lastResult = res;
86
78
  } catch (e) {
87
- console.error(`❌ Resolver ${i} failed for action "${action}":`, e.message);
79
+ console.error(` Resolver ${i} failed for action "${action}":`, e.message);
88
80
  }
89
81
  }
90
82
  if (verbose) console.log(`[Resolver Chain] action="${action}" lastResult=`, lastResult);
@@ -94,19 +86,24 @@ function createResolverChain(resolvers, verbose = false) {
94
86
  return wrapped;
95
87
  }
96
88
 
89
+ /**
90
+ * Load a single resolver — NO fallback to defaultMockResolver
91
+ */
97
92
  function loadSingleResolver(specifier) {
98
- if (!specifier) return defaultMockResolver;
99
-
93
+ // Remove fallback: do NOT return defaultMockResolver
94
+ if (!specifier) {
95
+ throw new Error('Empty resolver specifier provided');
96
+ }
100
97
  try {
101
98
  const resolver = require(specifier);
102
99
  if (typeof resolver !== 'function') throw new Error(`Resolver must export a function`);
103
- console.log(`📦 Loaded resolver: ${specifier}`);
100
+ console.log(` 📦 Loaded resolver: ${specifier}`);
104
101
  return resolver;
105
102
  } catch (e1) {
106
103
  try {
107
104
  const absolutePath = path.resolve(process.cwd(), specifier);
108
105
  const resolver = require(absolutePath);
109
- console.log(`📁 Loaded resolver: ${absolutePath}`);
106
+ console.log(` 📁 Loaded resolver: ${absolutePath}`);
110
107
  return resolver;
111
108
  } catch (e2) {
112
109
  throw new Error(
@@ -117,16 +114,40 @@ function loadSingleResolver(specifier) {
117
114
  }
118
115
 
119
116
  /**
120
- * loadResolverChain: include built-in math resolver first, then user resolvers, then default mock resolver
117
+ * POLICY-AWARE resolver loader: only includes resolvers in allowedResolvers
121
118
  */
122
- function loadResolverChain(specifiers, verbose = false) {
119
+ function loadResolverChain(specifiers, verbose = false, allowedResolvers = new Set()) {
123
120
  const userResolvers = specifiers?.map(loadSingleResolver) || [];
124
- const resolvers = [builtInMathResolver, ...userResolvers, defaultMockResolver];
121
+ const resolvers = [];
122
+
123
+ // Only add builtInMathResolver if allowed
124
+ if (allowedResolvers.has('builtInMathResolver')) {
125
+ resolvers.push(builtInMathResolver);
126
+ }
125
127
 
126
- if (!specifiers || specifiers.length === 0) {
127
- console.log('ℹ️ No resolver provided. Using built-in math + default mock resolver.');
128
+ // Add user resolvers only if their name is allowed
129
+ for (const r of userResolvers) {
130
+ const name = r.resolverName || r.name || 'unknown';
131
+ if (allowedResolvers.has(name)) {
132
+ resolvers.push(r);
133
+ } else if (verbose) {
134
+ console.warn(` ⚠️ Skipping disallowed user resolver: ${name}`);
135
+ }
136
+ }
137
+
138
+ // Only add defaultMockResolver if explicitly allowed
139
+ if (allowedResolvers.has('defaultMockResolver')) {
140
+ resolvers.push(defaultMockResolver);
141
+ }
142
+
143
+ if (resolvers.length === 0) {
144
+ if (verbose) {
145
+ console.warn(' ⚠️ No allowed resolvers loaded. Actions may fail.');
146
+ }
128
147
  } else {
129
- console.log(`📦 Loaded user resolvers: ${specifiers.join(', ')}`);
148
+ if (verbose) {
149
+ console.log(` ℹ️ Loaded allowed resolvers: ${resolvers.map(r => r.resolverName || 'anonymous').join(', ')}`);
150
+ }
130
151
  }
131
152
 
132
153
  return createResolverChain(resolvers, verbose);
@@ -136,7 +157,6 @@ function loadResolverChain(specifiers, verbose = false) {
136
157
  * CLI Setup
137
158
  */
138
159
  const program = new Command();
139
-
140
160
  program
141
161
  .name('olang')
142
162
  .description('O-Lang CLI: run .ol workflows with rule-enforced agent governance')
@@ -162,29 +182,34 @@ program
162
182
  .action(async (file, options) => {
163
183
  try {
164
184
  ensureOlExtension(file);
165
-
166
185
  const content = fs.readFileSync(file, 'utf8');
167
- const workflow = parse(content);
168
-
186
+ const workflow = parse(content, file);
169
187
  if (!workflow || typeof workflow !== 'object') {
170
- console.error('❌ Error: Parsed workflow is invalid or empty');
188
+ console.error(' Error: Parsed workflow is invalid or empty');
171
189
  process.exit(1);
172
190
  }
173
-
174
191
  if (options.verbose) {
175
- console.log('📄 Parsed Workflow:', JSON.stringify(workflow, null, 2));
192
+ console.log(' 📄 Parsed Workflow:', JSON.stringify(workflow, null, 2));
176
193
  }
177
194
 
178
- const resolver = loadResolverChain(options.resolver, options.verbose);
195
+ const allowedSet = new Set(workflow.allowedResolvers.map(r => r.trim()));
196
+ const resolver = loadResolverChain(options.resolver, options.verbose, allowedSet);
179
197
 
180
- const result = await execute(workflow, options.input, resolver, options.verbose);
198
+ // 🔔 Warn if allowed resolvers declared but none loaded
199
+ if (workflow.allowedResolvers.length > 0 && resolver._chain.length === 0) {
200
+ console.warn(
201
+ `\n⚠️ Warning: Workflow allows [${workflow.allowedResolvers.join(', ')}],\n` +
202
+ ` but no matching resolvers were loaded. Use -r <path> to provide them.\n`
203
+ );
204
+ }
181
205
 
206
+ const result = await execute(workflow, options.input, resolver, options.verbose);
182
207
  console.log('\n=== Workflow Result ===');
183
208
  console.log(JSON.stringify(result, null, 2));
184
209
  } catch (err) {
185
- console.error('❌ Error:', err.message);
210
+ console.error(' Error:', err.message);
186
211
  process.exit(1);
187
212
  }
188
213
  });
189
214
 
190
- program.parse(process.argv);
215
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
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",