openagentflow 0.1.0

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/index.js ADDED
@@ -0,0 +1,580 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * OpenAgentFlow CLI
5
+ *
6
+ * Usage:
7
+ * oaf parse <file> Parse an .oaf file and print the AST
8
+ * oaf validate <file> Validate an .oaf file (syntax + semantics)
9
+ * oaf compile <file> [--target T] [-o file] Compile an .oaf file to IR or runtime code
10
+ * oaf run <file> [--target T] Compile and execute via a runtime adapter
11
+ * oaf graph <file> Print the workflow graph in DOT format
12
+ * oaf --help Show this help message
13
+ * oaf --version Show version
14
+ *
15
+ * Targets:
16
+ * ir (default for compile) Output IR as JSON
17
+ * langgraph Generate executable LangGraph Python code
18
+ */
19
+
20
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
21
+ import { resolve, basename, join } from 'path';
22
+ import { execSync, spawn } from 'child_process';
23
+ import os from 'os';
24
+ import { Compiler } from '../compiler/compiler.js';
25
+ import { LangGraphAdapter } from '../adapters/langgraph/index.js';
26
+ import { resolveEnvHierarchy, setupAuth } from './env.js';
27
+
28
+ // ─── ANSI Colors ───────────────────────────────────────────────────────────────
29
+
30
+ const colors = {
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ red: '\x1b[31m',
34
+ green: '\x1b[32m',
35
+ yellow: '\x1b[33m',
36
+ blue: '\x1b[34m',
37
+ cyan: '\x1b[36m',
38
+ dim: '\x1b[2m',
39
+ };
40
+
41
+ // ─── Helpers ───────────────────────────────────────────────────────────────────
42
+
43
+ function printBanner() {
44
+ console.log(`${colors.cyan}${colors.bold}OpenAgentFlow${colors.reset} ${colors.dim}v0.1.0${colors.reset}`);
45
+ console.log();
46
+ }
47
+
48
+ function printUsage() {
49
+ printBanner();
50
+ console.log(`${colors.bold}Usage:${colors.reset}`);
51
+ console.log(` oaf parse <file.oaf> Parse and print the AST`);
52
+ console.log(` oaf validate <file.oaf> Validate syntax and semantics`);
53
+ console.log(` oaf compile <file.oaf> [--target T] [-o F] Compile to IR or runtime code`);
54
+ console.log(` oaf run <file.oaf> [--target T] Compile and execute workflow`);
55
+ console.log(` oaf graph <file.oaf> Output workflow graph (DOT format)`);
56
+ console.log(` oaf auth Set up API keys interactively`);
57
+ console.log();
58
+ console.log(`${colors.bold}Options:${colors.reset}`);
59
+ console.log(` --target, -t <target> Compilation target: ir (default), langgraph`);
60
+ console.log(` --input, -i <file> Path to JSON file containing initial workflow state`);
61
+ console.log(` -o <file> Output file (for compile command)`);
62
+ console.log(` --help, -h Show this help message`);
63
+ console.log(` --version, -v Show version`);
64
+ console.log();
65
+ console.log(`${colors.bold}Targets:${colors.reset}`);
66
+ console.log(` ir Output IR as JSON (default)`);
67
+ console.log(` langgraph Generate executable LangGraph Python code`);
68
+ console.log();
69
+ }
70
+
71
+ function readSource(filePath) {
72
+ const absPath = resolve(filePath);
73
+ try {
74
+ return { source: readFileSync(absPath, 'utf-8'), filename: basename(absPath) };
75
+ } catch (err) {
76
+ console.error(`${colors.red}Error:${colors.reset} Cannot read file: ${absPath}`);
77
+ console.error(` ${err.message}`);
78
+ process.exit(1);
79
+ }
80
+ }
81
+
82
+ function loadInputFile(filePath) {
83
+ if (!filePath) return null;
84
+ const absPath = resolve(filePath);
85
+ try {
86
+ const raw = readFileSync(absPath, 'utf-8');
87
+ const parsed = JSON.parse(raw);
88
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
89
+ console.error(`${colors.red}Error:${colors.reset} Input JSON file must contain a JSON object (dictionary): ${absPath}`);
90
+ process.exit(1);
91
+ }
92
+ return parsed;
93
+ } catch (err) {
94
+ console.error(`${colors.red}Error:${colors.reset} Cannot read or parse input JSON file: ${absPath}`);
95
+ console.error(` ${err.message}`);
96
+ process.exit(1);
97
+ }
98
+ }
99
+
100
+ function printDiagnostics(validation, filename) {
101
+ for (const diag of validation.diagnostics) {
102
+ const color = diag.severity === 'ERROR' ? colors.red : colors.yellow;
103
+ console.log(`${color}[${diag.severity}]${colors.reset} ${filename}:${diag.line}:${diag.column} — ${diag.message}`);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Parse CLI args to extract named flags.
109
+ * Returns { positional: string[], flags: Map<string, string> }
110
+ */
111
+ function parseArgs(args) {
112
+ const positional = [];
113
+ const flags = new Map();
114
+
115
+ for (let i = 0; i < args.length; i++) {
116
+ const arg = args[i];
117
+
118
+ if (arg === '--target' || arg === '-t') {
119
+ flags.set('target', args[++i] ?? '');
120
+ } else if (arg === '--runtime') {
121
+ flags.set('target', args[++i] ?? '');
122
+ } else if (arg === '--input' || arg === '-i') {
123
+ flags.set('input', args[++i] ?? '');
124
+ } else if (arg === '--output' || arg === '-o') {
125
+ flags.set('output', args[++i] ?? '');
126
+ } else if (arg === '--demo') {
127
+ flags.set('demo', 'true');
128
+ } else if (arg.startsWith('--target=')) {
129
+ flags.set('target', arg.split('=')[1]);
130
+ } else if (arg.startsWith('--runtime=')) {
131
+ flags.set('target', arg.split('=')[1]);
132
+ } else if (arg.startsWith('--input=')) {
133
+ flags.set('input', arg.split('=')[1]);
134
+ } else if (arg.startsWith('-i=')) {
135
+ flags.set('input', arg.split('=')[1]);
136
+ } else if (arg.startsWith('--output=')) {
137
+ flags.set('output', arg.split('=')[1]);
138
+ } else if (arg.startsWith('-o=')) {
139
+ flags.set('output', arg.split('=')[1]);
140
+ } else {
141
+ positional.push(arg);
142
+ }
143
+ }
144
+
145
+ return { positional, flags };
146
+ }
147
+
148
+ /**
149
+ * Compile the source and return the CompilationResult, exiting on failure.
150
+ */
151
+ function compileSource(filePath) {
152
+ const { source, filename } = readSource(filePath);
153
+ const compiler = new Compiler(source, filename);
154
+ const result = compiler.compile();
155
+
156
+ if (result.status !== 'success') {
157
+ if (result.error) {
158
+ console.error(`${colors.red}✗ Compilation failed:${colors.reset} ${result.error.message}`);
159
+ }
160
+ if (result.validation) {
161
+ printDiagnostics(result.validation, filename);
162
+ }
163
+ process.exit(1);
164
+ }
165
+
166
+ return { result, filename };
167
+ }
168
+
169
+ const SUPPORTED_TARGETS = ['ir', 'langgraph'];
170
+
171
+ // ─── Commands ──────────────────────────────────────────────────────────────────
172
+
173
+ /**
174
+ * oaf parse <file>
175
+ * Tokenize and parse, then print the AST as JSON.
176
+ */
177
+ function cmdParse(filePath) {
178
+ const { source, filename } = readSource(filePath);
179
+ const compiler = new Compiler(source, filename);
180
+
181
+ try {
182
+ const tokens = compiler.lex();
183
+ const ast = compiler.parse(tokens);
184
+
185
+ console.log(`${colors.green}✓${colors.reset} Parsed ${colors.bold}${filename}${colors.reset} successfully.`);
186
+ console.log();
187
+ console.log(JSON.stringify(ast, null, 2));
188
+ } catch (err) {
189
+ console.error(`${colors.red}✗ Parse failed:${colors.reset} ${err.message}`);
190
+ process.exit(1);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * oaf validate <file>
196
+ * Parse and run semantic validation.
197
+ */
198
+ function cmdValidate(filePath) {
199
+ resolveEnvHierarchy(filePath);
200
+ const { source, filename } = readSource(filePath);
201
+ const compiler = new Compiler(source, filename);
202
+
203
+ try {
204
+ const tokens = compiler.lex();
205
+ const ast = compiler.parse(tokens);
206
+ const validation = compiler.validate(ast);
207
+
208
+ if (validation.diagnostics.length > 0) {
209
+ printDiagnostics(validation, filename);
210
+ console.log();
211
+ }
212
+
213
+ if (validation.isValid) {
214
+ console.log(`${colors.green}✓${colors.reset} ${colors.bold}${filename}${colors.reset} is valid.`);
215
+ if (validation.warnings.length > 0) {
216
+ console.log(` ${colors.yellow}${validation.warnings.length} warning(s)${colors.reset}`);
217
+ }
218
+ } else {
219
+ console.log(`${colors.red}✗${colors.reset} ${colors.bold}${filename}${colors.reset} has ${validation.errors.length} error(s).`);
220
+ process.exit(1);
221
+ }
222
+ } catch (err) {
223
+ console.error(`${colors.red}✗ Validation failed:${colors.reset} ${err.message}`);
224
+ process.exit(1);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * oaf compile <file> [--target T] [-o output]
230
+ * Full pipeline: parse → validate → IR → (optionally) adapter.
231
+ *
232
+ * Targets:
233
+ * ir — Output IR as JSON (default)
234
+ * langgraph — Generate Python code via LangGraph adapter
235
+ */
236
+ function cmdCompile(filePath, flags, positional = []) {
237
+ resolveEnvHierarchy(filePath);
238
+ const target = flags.get('target') ?? 'ir';
239
+ const outputFile = flags.get('output');
240
+ const rawInputPath = flags.get('input') || positional[2] || null;
241
+ const inputData = loadInputFile(rawInputPath);
242
+
243
+ if (!SUPPORTED_TARGETS.includes(target)) {
244
+ console.error(`${colors.red}Error:${colors.reset} Unknown target "${target}". Supported: ${SUPPORTED_TARGETS.join(', ')}`);
245
+ process.exit(1);
246
+ }
247
+
248
+ const { result, filename } = compileSource(filePath);
249
+ let output;
250
+
251
+ if (target === 'ir') {
252
+ output = JSON.stringify(result.ir, null, 2);
253
+ } else if (target === 'langgraph') {
254
+ try {
255
+ const adapter = new LangGraphAdapter(result.ir, { input: inputData });
256
+ output = adapter.generate();
257
+ } catch (err) {
258
+ console.error(`${colors.red}✗ LangGraph generation failed:${colors.reset} ${err.message}`);
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ if (outputFile) {
264
+ const absOutput = resolve(outputFile);
265
+ try {
266
+ writeFileSync(absOutput, output, 'utf-8');
267
+ console.log(`${colors.green}✓${colors.reset} Compiled ${colors.bold}${filename}${colors.reset} → ${colors.bold}${basename(absOutput)}${colors.reset} (target: ${target})`);
268
+ } catch (err) {
269
+ console.error(`${colors.red}Error:${colors.reset} Cannot write to: ${absOutput}`);
270
+ console.error(` ${err.message}`);
271
+ process.exit(1);
272
+ }
273
+ } else {
274
+ console.log(output);
275
+ }
276
+ }
277
+
278
+ /**
279
+ * oaf run <file> [--target T] [--input F]
280
+ * Compile to a runtime target and execute via subprocess.
281
+ *
282
+ * Currently supports: langgraph (default for run command)
283
+ */
284
+ function cmdRun(filePath, flags, positional = []) {
285
+ resolveEnvHierarchy(filePath);
286
+ // --- DEMO HOOK (Cleanly removable) ---
287
+ const isDemoMode = flags.get('demo') === 'true' || process.env.OAF_DEMO === '1';
288
+ if (isDemoMode) process.env.OAF_DEMO = '1';
289
+ // -------------------------------------
290
+
291
+ const target = flags.get('target') ?? 'langgraph';
292
+ const rawInputPath = flags.get('input') || positional[2] || null;
293
+ const inputPath = rawInputPath ? resolve(rawInputPath) : null;
294
+ const inputData = loadInputFile(rawInputPath);
295
+
296
+ if (target === 'ir') {
297
+ console.error(`${colors.red}Error:${colors.reset} Cannot execute IR directly. Use --target langgraph`);
298
+ process.exit(1);
299
+ }
300
+
301
+ if (target !== 'langgraph') {
302
+ console.error(`${colors.red}Error:${colors.reset} Unknown runtime target "${target}". Supported: langgraph`);
303
+ process.exit(1);
304
+ }
305
+
306
+ const { result, filename } = compileSource(filePath);
307
+
308
+ // Pre-flight check 1: Python runtime existence
309
+ const pythonExe = getPythonCommand();
310
+ try {
311
+ execSync(`${pythonExe} --version`, { stdio: 'ignore' });
312
+ } catch (err) {
313
+ console.error(`${colors.red}Error:${colors.reset} Python runtime not found ("${pythonExe}"). Please install Python 3.8+ and ensure it is in your PATH.`);
314
+ process.exit(1);
315
+ }
316
+
317
+ // Pre-flight check 2: API Keys & OAF_DEFAULT_MODEL
318
+ if (!isDemoMode) {
319
+ const overrideModel = process.env.OAF_OVERRIDE_MODEL;
320
+ for (const agent of result.ir.agents) {
321
+ const targetModel = overrideModel || agent.model;
322
+ if ((targetModel === null || targetModel === undefined) && !process.env.OAF_DEFAULT_MODEL) {
323
+ console.error(`${colors.red}Error:${colors.reset} No model specified for agent "${agent.id}" and no default model configured. Please specify a 'model' property in your agent definition or set the OAF_DEFAULT_MODEL environment variable.`);
324
+ process.exit(1);
325
+ }
326
+
327
+ let provider = overrideModel ? null : agent.provider;
328
+ if (!provider && targetModel) {
329
+ if (targetModel.startsWith('claude-')) provider = 'anthropic';
330
+ else if (targetModel.startsWith('gpt-') || targetModel.startsWith('o1') || targetModel.startsWith('o3')) provider = 'openai';
331
+ else if (targetModel.startsWith('gemini-') || targetModel.startsWith('gemma-')) provider = 'gemini';
332
+ }
333
+
334
+ if (provider === 'anthropic' && !process.env.ANTHROPIC_API_KEY) {
335
+ console.error(`${colors.red}Error:${colors.reset} Missing required API key "ANTHROPIC_API_KEY" for agent "${agent.id}" (provider: anthropic).`);
336
+ console.error(`Looked in order of priority:`);
337
+ console.error(` 1. Inline CLI overrides`);
338
+ console.error(` 2. Local Project .env`);
339
+ console.error(` 3. System Environment Variables`);
340
+ console.error(` 4. Global OAF Store (~/.oaf/.env)`);
341
+ console.error(`Run \`oaf auth\` to set up your global credentials or create a local .env file.`);
342
+ process.exit(1);
343
+ } else if (provider === 'openai' && !process.env.OPENAI_API_KEY) {
344
+ console.error(`${colors.red}Error:${colors.reset} Missing required API key "OPENAI_API_KEY" for agent "${agent.id}" (provider: openai).`);
345
+ console.error(`Looked in order of priority:`);
346
+ console.error(` 1. Inline CLI overrides`);
347
+ console.error(` 2. Local Project .env`);
348
+ console.error(` 3. System Environment Variables`);
349
+ console.error(` 4. Global OAF Store (~/.oaf/.env)`);
350
+ console.error(`Run \`oaf auth\` to set up your global credentials or create a local .env file.`);
351
+ process.exit(1);
352
+ } else if (provider === 'gemini' && !process.env.GOOGLE_API_KEY) {
353
+ console.error(`${colors.red}Error:${colors.reset} Missing required API key "GOOGLE_API_KEY" for agent "${agent.id}" (provider: gemini).`);
354
+ console.error(`Looked in order of priority:`);
355
+ console.error(` 1. Inline CLI overrides`);
356
+ console.error(` 2. Local Project .env`);
357
+ console.error(` 3. System Environment Variables`);
358
+ console.error(` 4. Global OAF Store (~/.oaf/.env)`);
359
+ console.error(`Run \`oaf auth\` to set up your global credentials or create a local .env file.`);
360
+ process.exit(1);
361
+ }
362
+ }
363
+
364
+ if (!process.env.GOOGLE_API_KEY && !process.env.OPENAI_API_KEY && !process.env.ANTHROPIC_API_KEY && result.ir.agents.length > 0) {
365
+ console.error(`${colors.red}Error:${colors.reset} No LLM API key configured. Set GOOGLE_API_KEY (Gemini), OPENAI_API_KEY (OpenAI), or ANTHROPIC_API_KEY (Anthropic) to execute workflows.`);
366
+ console.error(`Looked in order of priority:`);
367
+ console.error(` 1. Inline CLI overrides`);
368
+ console.error(` 2. Local Project .env`);
369
+ console.error(` 3. System Environment Variables`);
370
+ console.error(` 4. Global OAF Store (~/.oaf/.env)`);
371
+ console.error(`Run \`oaf auth\` to set up your global credentials or create a local .env file.`);
372
+ process.exit(1);
373
+ }
374
+ }
375
+
376
+ // Generate Python code
377
+ let pythonCode;
378
+ try {
379
+ const adapter = new LangGraphAdapter(result.ir, { input: inputData });
380
+ pythonCode = adapter.generate();
381
+ } catch (err) {
382
+ console.error(`${colors.red}✗ LangGraph generation failed:${colors.reset} ${err.message}`);
383
+ process.exit(1);
384
+ }
385
+
386
+ console.log(`${colors.green}✓${colors.reset} Compiled ${colors.bold}${filename}${colors.reset} (target: ${target})`);
387
+ console.log(`${colors.cyan}▶${colors.reset} Executing workflow via Python subprocess...`);
388
+ console.log();
389
+
390
+ // Execute Python code using a clean temporary file inside os.tmpdir()
391
+ const tmpFile = join(os.tmpdir(), `oaf_run_${Date.now()}_${Math.random().toString(36).slice(2)}.py`);
392
+ try {
393
+ writeFileSync(tmpFile, pythonCode, 'utf-8');
394
+ const pyArgs = [tmpFile];
395
+ if (inputPath) {
396
+ pyArgs.push('--input', inputPath);
397
+ }
398
+ const child = spawn(pythonExe, pyArgs, {
399
+ stdio: ['inherit', 'pipe', 'pipe'],
400
+ env: { ...process.env },
401
+ });
402
+
403
+ let stdout = '';
404
+ let stderr = '';
405
+
406
+ const cleanup = () => {
407
+ try { if (existsSync(tmpFile)) unlinkSync(tmpFile); } catch (e) {}
408
+ };
409
+
410
+ child.stdout.on('data', (data) => {
411
+ const text = data.toString();
412
+ stdout += text;
413
+ process.stdout.write(text);
414
+ });
415
+
416
+ child.stderr.on('data', (data) => {
417
+ const text = data.toString();
418
+ stderr += text;
419
+ process.stderr.write(text);
420
+ });
421
+
422
+ child.on('close', (code) => {
423
+ cleanup();
424
+ console.log();
425
+ if (code === 0) {
426
+ console.log(`${colors.green}✓${colors.reset} Workflow execution completed successfully.`);
427
+ } else {
428
+ console.error(`${colors.red}✗ Workflow execution failed${colors.reset} (exit code: ${code})`);
429
+ if (stderr.includes('ModuleNotFoundError')) {
430
+ console.error();
431
+ console.error(`${colors.yellow}Hint:${colors.reset} Missing Python dependencies. Install with:`);
432
+ console.error(` pip install langgraph langchain-openai`);
433
+ }
434
+ if (stderr.includes('OPENAI_API_KEY')) {
435
+ console.error();
436
+ console.error(`${colors.yellow}Hint:${colors.reset} Set your OpenAI API key:`);
437
+ console.error(` export OPENAI_API_KEY='your-api-key-here'`);
438
+ }
439
+ process.exit(1);
440
+ }
441
+ });
442
+
443
+ child.on('error', (err) => {
444
+ cleanup();
445
+ if (err.code === 'ENOENT') {
446
+ console.error(`${colors.red}Error:${colors.reset} Python not found. Please install Python 3.8+ and ensure it is in your PATH.`);
447
+ } else {
448
+ console.error(`${colors.red}Error:${colors.reset} Failed to spawn Python process: ${err.message}`);
449
+ }
450
+ process.exit(1);
451
+ });
452
+ } catch (err) {
453
+ try { if (existsSync(tmpFile)) unlinkSync(tmpFile); } catch (e) {}
454
+ console.error(`${colors.red}✗ Failed to execute Python subprocess:${colors.reset} ${err.message}`);
455
+ process.exit(1);
456
+ }
457
+ }
458
+
459
+ function getPythonCommand() {
460
+ if (process.env.VIRTUAL_ENV) {
461
+ const venvWin = join(process.env.VIRTUAL_ENV, 'Scripts', 'python.exe');
462
+ const venvPosix = join(process.env.VIRTUAL_ENV, 'bin', 'python');
463
+ if (existsSync(venvWin)) return venvWin;
464
+ if (existsSync(venvPosix)) return venvPosix;
465
+ }
466
+ const localWin = join(process.cwd(), '.venv', 'Scripts', 'python.exe');
467
+ const localPosix = join(process.cwd(), '.venv', 'bin', 'python');
468
+ if (existsSync(localWin)) return localWin;
469
+ if (existsSync(localPosix)) return localPosix;
470
+ return 'python';
471
+ }
472
+
473
+ /**
474
+ * oaf auth
475
+ * Interactive prompt to save API keys to ~/.oaf/.env
476
+ */
477
+ function cmdAuth() {
478
+ setupAuth();
479
+ }
480
+
481
+ /**
482
+ * oaf graph <file>
483
+ * Compile and output the workflow graph in Graphviz DOT format.
484
+ */
485
+ function cmdGraph(filePath) {
486
+ const { result, filename } = compileSource(filePath);
487
+ const ir = result.ir;
488
+ const lines = [];
489
+ lines.push('digraph workflow {');
490
+ lines.push(' rankdir=TB;');
491
+ lines.push(' node [shape=box, style="rounded,filled", fillcolor="#e8f4f8", fontname="sans-serif"];');
492
+ lines.push(' edge [color="#555555"];');
493
+ lines.push('');
494
+ lines.push(` // Workflow: ${ir.workflow.name}`);
495
+ lines.push(' __start__ [label="START", shape=circle, fillcolor="#4CAF50", fontcolor=white, style=filled];');
496
+ lines.push(' __end__ [label="END", shape=doublecircle, fillcolor="#f44336", fontcolor=white, style=filled];');
497
+ lines.push('');
498
+
499
+ // Agent nodes
500
+ for (const agent of ir.agents) {
501
+ const label = agent.id;
502
+ lines.push(` ${agent.id} [label="${label}"];`);
503
+ }
504
+
505
+ lines.push('');
506
+
507
+ // Start → entrypoint
508
+ if (ir.graph.entrypoint) {
509
+ lines.push(` __start__ -> ${ir.graph.entrypoint};`);
510
+ }
511
+
512
+ // Agent-to-agent edges
513
+ for (const edge of ir.graph.edges) {
514
+ lines.push(` ${edge.source} -> ${edge.target};`);
515
+ }
516
+
517
+ // Terminal → end
518
+ for (const terminal of ir.graph.terminals) {
519
+ lines.push(` ${terminal} -> __end__;`);
520
+ }
521
+
522
+ lines.push('}');
523
+
524
+ console.log(lines.join('\n'));
525
+ }
526
+
527
+ // ─── Main ──────────────────────────────────────────────────────────────────────
528
+
529
+ function main() {
530
+ const rawArgs = process.argv.slice(2);
531
+
532
+ if (rawArgs.length === 0 || rawArgs.includes('--help') || rawArgs.includes('-h')) {
533
+ printUsage();
534
+ process.exit(0);
535
+ }
536
+
537
+ if (rawArgs.includes('--version') || rawArgs.includes('-v')) {
538
+ console.log('0.1.0');
539
+ process.exit(0);
540
+ }
541
+
542
+ const { positional, flags } = parseArgs(rawArgs);
543
+ const command = positional[0];
544
+ const filePath = positional[1];
545
+
546
+ if (command === 'auth') {
547
+ cmdAuth();
548
+ return;
549
+ }
550
+
551
+ if (!filePath && ['parse', 'validate', 'compile', 'run', 'graph'].includes(command)) {
552
+ console.error(`${colors.red}Error:${colors.reset} Missing file argument.`);
553
+ console.error(` Usage: oaf ${command} <file.oaf>`);
554
+ process.exit(1);
555
+ }
556
+
557
+ switch (command) {
558
+ case 'parse':
559
+ cmdParse(filePath);
560
+ break;
561
+ case 'validate':
562
+ cmdValidate(filePath);
563
+ break;
564
+ case 'compile':
565
+ cmdCompile(filePath, flags, positional);
566
+ break;
567
+ case 'run':
568
+ cmdRun(filePath, flags, positional);
569
+ break;
570
+ case 'graph':
571
+ cmdGraph(filePath);
572
+ break;
573
+ default:
574
+ console.error(`${colors.red}Error:${colors.reset} Unknown command "${command}".`);
575
+ printUsage();
576
+ process.exit(1);
577
+ }
578
+ }
579
+
580
+ main();