@o-lang/olang 1.0.16 → 1.0.17
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 +45 -33
- 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);
|
|
@@ -96,17 +88,16 @@ function createResolverChain(resolvers, verbose = false) {
|
|
|
96
88
|
|
|
97
89
|
function loadSingleResolver(specifier) {
|
|
98
90
|
if (!specifier) return defaultMockResolver;
|
|
99
|
-
|
|
100
91
|
try {
|
|
101
92
|
const resolver = require(specifier);
|
|
102
93
|
if (typeof resolver !== 'function') throw new Error(`Resolver must export a function`);
|
|
103
|
-
console.log(
|
|
94
|
+
console.log(` 📦 Loaded resolver: ${specifier}`);
|
|
104
95
|
return resolver;
|
|
105
96
|
} catch (e1) {
|
|
106
97
|
try {
|
|
107
98
|
const absolutePath = path.resolve(process.cwd(), specifier);
|
|
108
99
|
const resolver = require(absolutePath);
|
|
109
|
-
console.log(
|
|
100
|
+
console.log(` 📁 Loaded resolver: ${absolutePath}`);
|
|
110
101
|
return resolver;
|
|
111
102
|
} catch (e2) {
|
|
112
103
|
throw new Error(
|
|
@@ -117,16 +108,40 @@ function loadSingleResolver(specifier) {
|
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
/**
|
|
120
|
-
*
|
|
111
|
+
* ✅ POLICY-AWARE resolver loader: only includes resolvers in allowedResolvers
|
|
121
112
|
*/
|
|
122
|
-
function loadResolverChain(specifiers, verbose = false) {
|
|
113
|
+
function loadResolverChain(specifiers, verbose = false, allowedResolvers = new Set()) {
|
|
123
114
|
const userResolvers = specifiers?.map(loadSingleResolver) || [];
|
|
124
|
-
const resolvers = [
|
|
115
|
+
const resolvers = [];
|
|
125
116
|
|
|
126
|
-
|
|
127
|
-
|
|
117
|
+
// Only add builtInMathResolver if allowed
|
|
118
|
+
if (allowedResolvers.has('builtInMathResolver')) {
|
|
119
|
+
resolvers.push(builtInMathResolver);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Add user resolvers only if their name is allowed
|
|
123
|
+
for (const r of userResolvers) {
|
|
124
|
+
const name = r.resolverName || r.name || 'unknown';
|
|
125
|
+
if (allowedResolvers.has(name)) {
|
|
126
|
+
resolvers.push(r);
|
|
127
|
+
} else if (verbose) {
|
|
128
|
+
console.warn(` ⚠️ Skipping disallowed user resolver: ${name}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Only add defaultMockResolver if explicitly allowed
|
|
133
|
+
if (allowedResolvers.has('defaultMockResolver')) {
|
|
134
|
+
resolvers.push(defaultMockResolver);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (resolvers.length === 0) {
|
|
138
|
+
if (verbose) {
|
|
139
|
+
console.warn(' ⚠️ No allowed resolvers loaded. Actions may fail.');
|
|
140
|
+
}
|
|
128
141
|
} else {
|
|
129
|
-
|
|
142
|
+
if (verbose) {
|
|
143
|
+
console.log(` ℹ️ Loaded allowed resolvers: ${resolvers.map(r => r.resolverName || 'anonymous').join(', ')}`);
|
|
144
|
+
}
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
return createResolverChain(resolvers, verbose);
|
|
@@ -136,7 +151,6 @@ function loadResolverChain(specifiers, verbose = false) {
|
|
|
136
151
|
* CLI Setup
|
|
137
152
|
*/
|
|
138
153
|
const program = new Command();
|
|
139
|
-
|
|
140
154
|
program
|
|
141
155
|
.name('olang')
|
|
142
156
|
.description('O-Lang CLI: run .ol workflows with rule-enforced agent governance')
|
|
@@ -162,29 +176,27 @@ program
|
|
|
162
176
|
.action(async (file, options) => {
|
|
163
177
|
try {
|
|
164
178
|
ensureOlExtension(file);
|
|
165
|
-
|
|
166
179
|
const content = fs.readFileSync(file, 'utf8');
|
|
167
|
-
const workflow = parse(content);
|
|
168
|
-
|
|
180
|
+
const workflow = parse(content, file);
|
|
169
181
|
if (!workflow || typeof workflow !== 'object') {
|
|
170
|
-
console.error('❌
|
|
182
|
+
console.error(' ❌ Error: Parsed workflow is invalid or empty');
|
|
171
183
|
process.exit(1);
|
|
172
184
|
}
|
|
173
|
-
|
|
174
185
|
if (options.verbose) {
|
|
175
|
-
console.log('📄
|
|
186
|
+
console.log(' 📄 Parsed Workflow:', JSON.stringify(workflow, null, 2));
|
|
176
187
|
}
|
|
177
188
|
|
|
178
|
-
|
|
189
|
+
// ✅ Pass allowedResolvers to loadResolverChain
|
|
190
|
+
const allowedSet = new Set(workflow.allowedResolvers.map(r => r.trim()));
|
|
191
|
+
const resolver = loadResolverChain(options.resolver, options.verbose, allowedSet);
|
|
179
192
|
|
|
180
193
|
const result = await execute(workflow, options.input, resolver, options.verbose);
|
|
181
|
-
|
|
182
194
|
console.log('\n=== Workflow Result ===');
|
|
183
195
|
console.log(JSON.stringify(result, null, 2));
|
|
184
196
|
} catch (err) {
|
|
185
|
-
console.error('❌
|
|
197
|
+
console.error(' ❌ Error:', err.message);
|
|
186
198
|
process.exit(1);
|
|
187
199
|
}
|
|
188
200
|
});
|
|
189
201
|
|
|
190
|
-
program.parse(process.argv);
|
|
202
|
+
program.parse(process.argv);
|
package/package.json
CHANGED