@o-lang/olang 1.0.2 → 1.0.3
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 +50 -19
- package/package.json +1 -1
- package/src/parser.js +12 -13
package/cli.js
CHANGED
|
@@ -5,6 +5,21 @@ const { execute } = require('./src/runtime');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Enforce .ol extension ONLY
|
|
10
|
+
*/
|
|
11
|
+
function ensureOlExtension(filename) {
|
|
12
|
+
if (!filename.endsWith('.ol')) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Invalid file: "${filename}".\n` +
|
|
15
|
+
`O-Lang workflows must use the ".ol" extension.`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default mock resolver (for demo use)
|
|
22
|
+
*/
|
|
8
23
|
async function defaultMockResolver(action, context) {
|
|
9
24
|
if (action.startsWith('Search for ')) {
|
|
10
25
|
return {
|
|
@@ -13,20 +28,27 @@ async function defaultMockResolver(action, context) {
|
|
|
13
28
|
url: "mock://hr-policy"
|
|
14
29
|
};
|
|
15
30
|
}
|
|
31
|
+
|
|
16
32
|
if (action.startsWith('Ask ')) {
|
|
17
|
-
return "✅ [Mock] Summarized for
|
|
33
|
+
return "✅ [Mock] Summarized for demonstration.";
|
|
18
34
|
}
|
|
35
|
+
|
|
19
36
|
if (action.startsWith('Notify ')) {
|
|
20
37
|
const recipient = action.match(/Notify (\S+)/)?.[1] || 'user@example.com';
|
|
21
38
|
return `📬 Notification sent to ${recipient}`;
|
|
22
39
|
}
|
|
40
|
+
|
|
23
41
|
if (action.startsWith('Debrief ') || action.startsWith('Evolve ')) {
|
|
24
42
|
console.log(`[O-Lang] ${action}`);
|
|
25
43
|
return 'Acknowledged';
|
|
26
44
|
}
|
|
45
|
+
|
|
27
46
|
return `[Unhandled: ${action}]`;
|
|
28
47
|
}
|
|
29
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Resolver chaining mechanism
|
|
51
|
+
*/
|
|
30
52
|
function createResolverChain(resolvers) {
|
|
31
53
|
return async (action, context) => {
|
|
32
54
|
for (const resolver of resolvers) {
|
|
@@ -61,7 +83,9 @@ function loadSingleResolver(specifier) {
|
|
|
61
83
|
console.log(`📁 Loaded resolver: ${absolutePath}`);
|
|
62
84
|
return resolver;
|
|
63
85
|
} catch (e2) {
|
|
64
|
-
throw new Error(
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Failed to load resolver '${specifier}':\n npm: ${e1.message}\n file: ${e2.message}`
|
|
88
|
+
);
|
|
65
89
|
}
|
|
66
90
|
}
|
|
67
91
|
}
|
|
@@ -76,27 +100,41 @@ function loadResolverChain(specifiers) {
|
|
|
76
100
|
return createResolverChain(resolvers);
|
|
77
101
|
}
|
|
78
102
|
|
|
103
|
+
/**
|
|
104
|
+
* CLI Setup
|
|
105
|
+
*/
|
|
79
106
|
const program = new Command();
|
|
80
107
|
|
|
81
108
|
program
|
|
82
109
|
.name('olang')
|
|
83
|
-
.description('O-Lang CLI: run .
|
|
110
|
+
.description('O-Lang CLI: run .ol workflows with rule-enforced agent governance')
|
|
84
111
|
.command('run <file>')
|
|
85
|
-
.option(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
.option(
|
|
113
|
+
'-r, --resolver <specifier>',
|
|
114
|
+
'Resolver (npm package or local path). Can be used multiple times.\nExample:\n -r @o-lang/llm-groq\n -r @o-lang/notify-telegram',
|
|
115
|
+
(val, acc) => { acc.push(val); return acc; },
|
|
116
|
+
[]
|
|
117
|
+
)
|
|
118
|
+
.option(
|
|
119
|
+
'-i, --input <k=v>',
|
|
120
|
+
'Input parameters',
|
|
121
|
+
(val, acc = {}) => {
|
|
122
|
+
const [k, v] = val.split('=');
|
|
123
|
+
acc[k] = v;
|
|
124
|
+
return acc;
|
|
125
|
+
},
|
|
126
|
+
{}
|
|
127
|
+
)
|
|
94
128
|
.action(async (file, options) => {
|
|
95
129
|
try {
|
|
130
|
+
ensureOlExtension(file);
|
|
131
|
+
|
|
96
132
|
const content = fs.readFileSync(file, 'utf8');
|
|
97
133
|
const workflow = parse(content);
|
|
134
|
+
|
|
98
135
|
const resolver = loadResolverChain(options.resolver);
|
|
99
136
|
const result = await execute(workflow, options.input, resolver);
|
|
137
|
+
|
|
100
138
|
console.log('\n=== Workflow Result ===');
|
|
101
139
|
console.log(JSON.stringify(result, null, 2));
|
|
102
140
|
} catch (err) {
|
|
@@ -106,10 +144,3 @@ program
|
|
|
106
144
|
});
|
|
107
145
|
|
|
108
146
|
program.parse(process.argv);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// src/parser.js
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
function parse(code, fileName = null) {
|
|
4
|
+
// --- New: Enforce .ol extension if filename provided ---
|
|
5
|
+
if (fileName && !fileName.endsWith(".ol")) {
|
|
6
|
+
throw new Error(`Expected .ol workflow, got: ${fileName}`);
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
const lines = code
|
|
4
10
|
.split(/\r?\n/)
|
|
5
11
|
.map(l => l.trim())
|
|
@@ -59,16 +65,11 @@ function parse(code) {
|
|
|
59
65
|
let key = eq[1].trim();
|
|
60
66
|
let value = eq[2].trim();
|
|
61
67
|
|
|
62
|
-
// Parse list: [a, b, c]
|
|
63
68
|
if (value.startsWith('[') && value.endsWith(']')) {
|
|
64
69
|
value = value.slice(1, -1).split(',').map(v => v.trim().replace(/^"/, '').replace(/"$/, ''));
|
|
65
|
-
}
|
|
66
|
-
// Parse number
|
|
67
|
-
else if (!isNaN(value)) {
|
|
70
|
+
} else if (!isNaN(value)) {
|
|
68
71
|
value = Number(value);
|
|
69
|
-
}
|
|
70
|
-
// Keep string (remove quotes if present)
|
|
71
|
-
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
72
|
+
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
72
73
|
value = value.slice(1, -1);
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -119,7 +120,7 @@ function parse(code) {
|
|
|
119
120
|
continue;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
// Agent
|
|
123
|
+
// Agent uses
|
|
123
124
|
const agentUseMatch = line.match(/^Agent\s+"([^"]+)"\s+uses\s+"([^"]+)"$/i);
|
|
124
125
|
if (agentUseMatch) {
|
|
125
126
|
workflow.steps.push({
|
|
@@ -199,7 +200,7 @@ function parse(code) {
|
|
|
199
200
|
continue;
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
//
|
|
203
|
+
// Use <Tool>
|
|
203
204
|
const useMatch = line.match(/^Use\s+(.+)$/i);
|
|
204
205
|
if (useMatch) {
|
|
205
206
|
workflow.steps.push({
|
|
@@ -212,7 +213,7 @@ function parse(code) {
|
|
|
212
213
|
continue;
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
//
|
|
216
|
+
// Ask <Target>
|
|
216
217
|
const askMatch = line.match(/^Ask\s+(.+)$/i);
|
|
217
218
|
if (askMatch) {
|
|
218
219
|
workflow.steps.push({
|
|
@@ -268,13 +269,11 @@ function parseBlock(lines) {
|
|
|
268
269
|
steps.push({ type: 'prompt', question: promptMatch[1], saveAs: null });
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
// New: Use <Tool>
|
|
272
272
|
const useMatch = line.match(/^Use\s+(.+)$/i);
|
|
273
273
|
if (useMatch) {
|
|
274
274
|
steps.push({ type: 'use', tool: useMatch[1].trim(), saveAs: null, constraints: {} });
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
// New: Ask <Target>
|
|
278
277
|
const askMatch = line.match(/^Ask\s+(.+)$/i);
|
|
279
278
|
if (askMatch) {
|
|
280
279
|
steps.push({ type: 'ask', target: askMatch[1].trim(), saveAs: null, constraints: {} });
|