@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.
- package/cli.js +59 -34
- 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 "✅
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
*
|
|
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 = [
|
|
121
|
+
const resolvers = [];
|
|
122
|
+
|
|
123
|
+
// Only add builtInMathResolver if allowed
|
|
124
|
+
if (allowedResolvers.has('builtInMathResolver')) {
|
|
125
|
+
resolvers.push(builtInMathResolver);
|
|
126
|
+
}
|
|
125
127
|
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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('❌
|
|
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('📄
|
|
192
|
+
console.log(' 📄 Parsed Workflow:', JSON.stringify(workflow, null, 2));
|
|
176
193
|
}
|
|
177
194
|
|
|
178
|
-
const
|
|
195
|
+
const allowedSet = new Set(workflow.allowedResolvers.map(r => r.trim()));
|
|
196
|
+
const resolver = loadResolverChain(options.resolver, options.verbose, allowedSet);
|
|
179
197
|
|
|
180
|
-
|
|
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('❌
|
|
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