@orcalang/orca-lang 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/LICENSE +176 -0
- package/README.md +128 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/lock.d.ts +2 -0
- package/dist/auth/lock.d.ts.map +1 -0
- package/dist/auth/lock.js +59 -0
- package/dist/auth/lock.js.map +1 -0
- package/dist/auth/providers/anthropic.d.ts +14 -0
- package/dist/auth/providers/anthropic.d.ts.map +1 -0
- package/dist/auth/providers/anthropic.js +145 -0
- package/dist/auth/providers/anthropic.js.map +1 -0
- package/dist/auth/providers/index.d.ts +3 -0
- package/dist/auth/providers/index.d.ts.map +1 -0
- package/dist/auth/providers/index.js +3 -0
- package/dist/auth/providers/index.js.map +1 -0
- package/dist/auth/providers/minimax.d.ts +6 -0
- package/dist/auth/providers/minimax.d.ts.map +1 -0
- package/dist/auth/providers/minimax.js +65 -0
- package/dist/auth/providers/minimax.js.map +1 -0
- package/dist/auth/refresh.d.ts +8 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +104 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/auth/store.d.ts +11 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +63 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +51 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/compiler/mermaid.d.ts +3 -0
- package/dist/compiler/mermaid.d.ts.map +1 -0
- package/dist/compiler/mermaid.js +86 -0
- package/dist/compiler/mermaid.js.map +1 -0
- package/dist/compiler/xstate.d.ts +15 -0
- package/dist/compiler/xstate.d.ts.map +1 -0
- package/dist/compiler/xstate.js +542 -0
- package/dist/compiler/xstate.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +109 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +13 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +8 -0
- package/dist/config/types.js.map +1 -0
- package/dist/generators/index.d.ts +5 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +5 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/registry.d.ts +12 -0
- package/dist/generators/registry.d.ts.map +1 -0
- package/dist/generators/registry.js +15 -0
- package/dist/generators/registry.js.map +1 -0
- package/dist/generators/typescript.d.ts +9 -0
- package/dist/generators/typescript.d.ts.map +1 -0
- package/dist/generators/typescript.js +55 -0
- package/dist/generators/typescript.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +630 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic.d.ts +14 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +87 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/grok.d.ts +13 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +60 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/index.d.ts +11 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +23 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/ollama.d.ts +11 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +51 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +13 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +61 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/provider.d.ts +32 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +2 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/parser/ast-to-markdown.d.ts +3 -0
- package/dist/parser/ast-to-markdown.d.ts.map +1 -0
- package/dist/parser/ast-to-markdown.js +209 -0
- package/dist/parser/ast-to-markdown.js.map +1 -0
- package/dist/parser/ast.d.ts +183 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/ast.js +3 -0
- package/dist/parser/ast.js.map +1 -0
- package/dist/parser/markdown-parser.d.ts +8 -0
- package/dist/parser/markdown-parser.d.ts.map +1 -0
- package/dist/parser/markdown-parser.js +838 -0
- package/dist/parser/markdown-parser.js.map +1 -0
- package/dist/runtime/effects.d.ts +17 -0
- package/dist/runtime/effects.d.ts.map +1 -0
- package/dist/runtime/effects.js +28 -0
- package/dist/runtime/effects.js.map +1 -0
- package/dist/runtime/machine.d.ts +8 -0
- package/dist/runtime/machine.d.ts.map +1 -0
- package/dist/runtime/machine.js +158 -0
- package/dist/runtime/machine.js.map +1 -0
- package/dist/runtime/types.d.ts +37 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +3 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/skills.d.ts +114 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +1103 -0
- package/dist/skills.js.map +1 -0
- package/dist/tools.d.ts +18 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +124 -0
- package/dist/tools.js.map +1 -0
- package/dist/verifier/completeness.d.ts +4 -0
- package/dist/verifier/completeness.d.ts.map +1 -0
- package/dist/verifier/completeness.js +82 -0
- package/dist/verifier/completeness.js.map +1 -0
- package/dist/verifier/determinism.d.ts +17 -0
- package/dist/verifier/determinism.d.ts.map +1 -0
- package/dist/verifier/determinism.js +301 -0
- package/dist/verifier/determinism.js.map +1 -0
- package/dist/verifier/properties.d.ts +6 -0
- package/dist/verifier/properties.d.ts.map +1 -0
- package/dist/verifier/properties.js +404 -0
- package/dist/verifier/properties.js.map +1 -0
- package/dist/verifier/structural.d.ts +50 -0
- package/dist/verifier/structural.d.ts.map +1 -0
- package/dist/verifier/structural.js +692 -0
- package/dist/verifier/structural.js.map +1 -0
- package/dist/verifier/types.d.ts +40 -0
- package/dist/verifier/types.d.ts.map +1 -0
- package/dist/verifier/types.js +2 -0
- package/dist/verifier/types.js.map +1 -0
- package/package.json +49 -0
- package/src/auth/index.ts +5 -0
- package/src/auth/lock.ts +71 -0
- package/src/auth/providers/anthropic.ts +192 -0
- package/src/auth/providers/index.ts +17 -0
- package/src/auth/providers/minimax.ts +100 -0
- package/src/auth/refresh.ts +138 -0
- package/src/auth/store.ts +75 -0
- package/src/auth/types.ts +62 -0
- package/src/compiler/mermaid.ts +109 -0
- package/src/compiler/xstate.ts +615 -0
- package/src/config/index.ts +2 -0
- package/src/config/loader.ts +122 -0
- package/src/config/types.ts +21 -0
- package/src/generators/index.ts +6 -0
- package/src/generators/registry.ts +27 -0
- package/src/generators/typescript.ts +67 -0
- package/src/index.ts +671 -0
- package/src/llm/anthropic.ts +102 -0
- package/src/llm/grok.ts +73 -0
- package/src/llm/index.ts +29 -0
- package/src/llm/ollama.ts +62 -0
- package/src/llm/openai.ts +74 -0
- package/src/llm/provider.ts +35 -0
- package/src/parser/ast-to-markdown.ts +220 -0
- package/src/parser/ast.ts +236 -0
- package/src/parser/markdown-parser.ts +844 -0
- package/src/runtime/effects.ts +48 -0
- package/src/runtime/machine.ts +201 -0
- package/src/runtime/types.ts +44 -0
- package/src/skills.ts +1339 -0
- package/src/tools.ts +144 -0
- package/src/verifier/completeness.ts +89 -0
- package/src/verifier/determinism.ts +328 -0
- package/src/verifier/properties.ts +507 -0
- package/src/verifier/structural.ts +803 -0
- package/src/verifier/types.ts +45 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
4
|
+
import { join, basename } from 'path';
|
|
5
|
+
import { setAuthProfile, deleteAuthProfile, listAuthProfiles, getAuthProfile } from './auth/store.js';
|
|
6
|
+
import { getValidAccessToken } from './auth/refresh.js';
|
|
7
|
+
import {
|
|
8
|
+
buildAuthorizationUrl as buildAnthropicUrl,
|
|
9
|
+
getDeviceCode,
|
|
10
|
+
pollForToken,
|
|
11
|
+
promptForCode,
|
|
12
|
+
openBrowser,
|
|
13
|
+
anthropicOAuthProvider,
|
|
14
|
+
} from './auth/providers/anthropic.js';
|
|
15
|
+
import {
|
|
16
|
+
buildAuthorizationUrl as buildMiniMaxUrl,
|
|
17
|
+
minimaxOAuthProvider,
|
|
18
|
+
} from './auth/providers/minimax.js';
|
|
19
|
+
import { parseMarkdown } from './parser/markdown-parser.js';
|
|
20
|
+
import { machineToMarkdown } from './parser/ast-to-markdown.js';
|
|
21
|
+
import { checkStructural, analyzeFile } from './verifier/structural.js';
|
|
22
|
+
import { checkCompleteness } from './verifier/completeness.js';
|
|
23
|
+
import { checkDeterminism } from './verifier/determinism.js';
|
|
24
|
+
import { checkProperties } from './verifier/properties.js';
|
|
25
|
+
import { compileToXState, compileToXStateMachine } from './compiler/xstate.js';
|
|
26
|
+
import { compileToMermaid } from './compiler/mermaid.js';
|
|
27
|
+
import { verifySkill, compileSkill, generateActionsSkill, refineSkill, generateOrcaSkill, generateOrcaMultiSkill, parseSkill, type SkillInput } from './skills.js';
|
|
28
|
+
import { ORCA_TOOLS } from './tools.js';
|
|
29
|
+
import { createOrcaMachine } from './runtime/machine.js';
|
|
30
|
+
import type { OrcaMachine, OrcaMachineOptions, OrcaState } from './runtime/types.js';
|
|
31
|
+
|
|
32
|
+
// Re-export for use as a library
|
|
33
|
+
export { parseMarkdown } from './parser/markdown-parser.js';
|
|
34
|
+
export { machineToMarkdown } from './parser/ast-to-markdown.js';
|
|
35
|
+
export { compileToXState, compileToXStateMachine } from './compiler/xstate.js';
|
|
36
|
+
export { compileToMermaid } from './compiler/mermaid.js';
|
|
37
|
+
export { checkProperties } from './verifier/properties.js';
|
|
38
|
+
export { createOrcaMachine };
|
|
39
|
+
export type { OrcaMachine, OrcaMachineOptions, OrcaState, EffectHandlers, EffectResult, Effect } from './runtime/types.js';
|
|
40
|
+
|
|
41
|
+
import type { MachineDef } from './parser/ast.js';
|
|
42
|
+
|
|
43
|
+
// ── Stdin helpers ─────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
async function readStdin(): Promise<string> {
|
|
46
|
+
const chunks: Buffer[] = [];
|
|
47
|
+
for await (const chunk of process.stdin) {
|
|
48
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));
|
|
49
|
+
}
|
|
50
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Resolve a SkillInput from either a file path or stdin. Pass `-` as fileArg to force stdin. */
|
|
54
|
+
async function getInput(fileArg: string | undefined, useStdin: boolean): Promise<SkillInput> {
|
|
55
|
+
if (useStdin || fileArg === '-') return { source: await readStdin() };
|
|
56
|
+
if (fileArg) return { file: fileArg };
|
|
57
|
+
throw new Error('No input file specified. Provide a file path or use --stdin.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Read raw source from a SkillInput (used by functions that need the source string directly). */
|
|
61
|
+
function sourceFromInput(input: SkillInput): string {
|
|
62
|
+
if (input.source !== undefined) return input.source;
|
|
63
|
+
if (input.file !== undefined) return readFileSync(input.file, 'utf-8');
|
|
64
|
+
throw new Error('SkillInput requires either source or file');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Parse an Orca machine definition file (markdown format) */
|
|
68
|
+
function parseFile(filePath: string, source: string): MachineDef {
|
|
69
|
+
const { file } = parseMarkdown(source);
|
|
70
|
+
if (file.machines.length > 1) {
|
|
71
|
+
throw new Error(`File ${filePath} contains multiple machines. Use a command that supports multi-machine files.`);
|
|
72
|
+
}
|
|
73
|
+
return file.machines[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function login(provider: string, profileId: string = 'default'): Promise<void> {
|
|
77
|
+
console.log(`Logging in to ${provider}...`);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
if (provider === 'anthropic') {
|
|
81
|
+
// Use device code flow for simpler CLI experience
|
|
82
|
+
const deviceCodes = await getDeviceCode();
|
|
83
|
+
console.log(`Please visit: ${deviceCodes.verification_uri}`);
|
|
84
|
+
console.log(`And enter code: ${deviceCodes.user_code}`);
|
|
85
|
+
await openBrowser(deviceCodes.verification_uri);
|
|
86
|
+
|
|
87
|
+
const tokens = await pollForToken(deviceCodes.device_code);
|
|
88
|
+
|
|
89
|
+
setAuthProfile(profileId, {
|
|
90
|
+
mode: 'oauth',
|
|
91
|
+
provider: 'anthropic',
|
|
92
|
+
access: tokens.access,
|
|
93
|
+
refresh: tokens.refresh ?? '',
|
|
94
|
+
expires: tokens.expires,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log('Successfully logged in!');
|
|
98
|
+
} else if (provider === 'minimax') {
|
|
99
|
+
// Build authorization URL
|
|
100
|
+
const authUrl = buildMiniMaxUrl('orca-cli', 'http://localhost:9999/callback');
|
|
101
|
+
console.log(`Please visit: ${authUrl}`);
|
|
102
|
+
await openBrowser(authUrl);
|
|
103
|
+
|
|
104
|
+
console.log('After authorizing, you will be redirected to a callback URL.');
|
|
105
|
+
console.log('Copy the authorization code from the URL and paste it here:');
|
|
106
|
+
const code = await promptForCode();
|
|
107
|
+
|
|
108
|
+
// Note: In a full implementation, we'd exchange the code for tokens
|
|
109
|
+
// For now, this is a placeholder
|
|
110
|
+
console.log('MiniMax OAuth flow requires server callback - this is a placeholder.');
|
|
111
|
+
} else {
|
|
112
|
+
console.error(`Unsupported provider: ${provider}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error(`Login failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function logout(profileId?: string): Promise<void> {
|
|
122
|
+
const profiles = profileId ? [profileId] : listAuthProfiles();
|
|
123
|
+
|
|
124
|
+
if (profiles.length === 0) {
|
|
125
|
+
console.log('No auth profiles found.');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const id of profiles) {
|
|
130
|
+
if (deleteAuthProfile(id)) {
|
|
131
|
+
console.log(`Logged out: ${id}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function auth(showDoctor: boolean = false): Promise<void> {
|
|
137
|
+
const profiles = listAuthProfiles();
|
|
138
|
+
|
|
139
|
+
if (profiles.length === 0) {
|
|
140
|
+
console.log('No auth profiles configured.');
|
|
141
|
+
console.log('Run "orca login" to set up authentication.');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log('Auth profiles:');
|
|
146
|
+
for (const profileId of profiles) {
|
|
147
|
+
const profile = getAuthProfile(profileId);
|
|
148
|
+
if (!profile) continue;
|
|
149
|
+
|
|
150
|
+
const status = profile.mode === 'oauth' && profile.expires
|
|
151
|
+
? (Date.now() < profile.expires ? 'valid' : 'expired')
|
|
152
|
+
: 'valid';
|
|
153
|
+
|
|
154
|
+
console.log(` ${profileId}:`);
|
|
155
|
+
console.log(` provider: ${profile.provider}`);
|
|
156
|
+
console.log(` mode: ${profile.mode}`);
|
|
157
|
+
console.log(` status: ${status}`);
|
|
158
|
+
if (profile.email) {
|
|
159
|
+
console.log(` email: ${profile.email}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Try to get a valid token for the default profile
|
|
164
|
+
const defaultCreds = await getValidAccessToken('default');
|
|
165
|
+
if (defaultCreds) {
|
|
166
|
+
console.log('\nDefault profile is ready to use.');
|
|
167
|
+
} else if (profiles.includes('default')) {
|
|
168
|
+
console.log('\nDefault profile needs re-authentication. Run "orca login".');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function formatErrors(errors: { code: string; message: string; severity: string; suggestion?: string }[]): void {
|
|
173
|
+
for (const err of errors) {
|
|
174
|
+
const prefix = err.severity === 'error' ? 'ERROR' : 'WARN';
|
|
175
|
+
console.log(`[${prefix}] ${err.code}: ${err.message}`);
|
|
176
|
+
if (err.suggestion) {
|
|
177
|
+
console.log(` Suggestion: ${err.suggestion}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function stripCodeFence(code: string): string {
|
|
183
|
+
return code
|
|
184
|
+
.replace(/^```typescript\n/, '')
|
|
185
|
+
.replace(/^```\n/, '')
|
|
186
|
+
.replace(/\n```$/, '')
|
|
187
|
+
.trim();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function verify(input: SkillInput, json: boolean = false): Promise<void> {
|
|
191
|
+
if (json) {
|
|
192
|
+
const result = await verifySkill(input);
|
|
193
|
+
console.log(JSON.stringify(result, null, 2));
|
|
194
|
+
process.exit(result.status === 'valid' ? 0 : 1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const label = input.file ?? '<stdin>';
|
|
198
|
+
console.log(`Verifying ${label}...`);
|
|
199
|
+
const source = sourceFromInput(input);
|
|
200
|
+
const { file } = parseMarkdown(source);
|
|
201
|
+
|
|
202
|
+
if (file.machines.length > 1) {
|
|
203
|
+
// Multi-machine verification
|
|
204
|
+
const fileAnalysis = analyzeFile(file);
|
|
205
|
+
console.log(`Parsed ${file.machines.length} machines:`);
|
|
206
|
+
for (const machine of file.machines) {
|
|
207
|
+
console.log(` - ${machine.name}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const allErrors = [...fileAnalysis.errors];
|
|
211
|
+
const allWarnings = [...fileAnalysis.warnings];
|
|
212
|
+
|
|
213
|
+
if (allErrors.length > 0) {
|
|
214
|
+
const errorCount = allErrors.filter(e => e.severity === 'error').length;
|
|
215
|
+
const warningCount = allErrors.filter(e => e.severity === 'warning').length + allWarnings.length;
|
|
216
|
+
console.log(`\nFound ${errorCount} error(s)${warningCount > 0 ? ` and ${warningCount} warning(s)` : ''}:`);
|
|
217
|
+
formatErrors([...allErrors, ...allWarnings]);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
} else {
|
|
220
|
+
console.log('\nVerification passed!');
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Single-machine verification
|
|
226
|
+
const machine = file.machines[0];
|
|
227
|
+
console.log(`Parsed machine: ${machine.name}`);
|
|
228
|
+
console.log(` States: ${machine.states.length}`);
|
|
229
|
+
console.log(` Events: ${machine.events.length}`);
|
|
230
|
+
console.log(` Transitions: ${machine.transitions.length}`);
|
|
231
|
+
|
|
232
|
+
const structural = checkStructural(machine);
|
|
233
|
+
const completeness = checkCompleteness(machine);
|
|
234
|
+
const determinism = checkDeterminism(machine);
|
|
235
|
+
const properties = checkProperties(machine);
|
|
236
|
+
|
|
237
|
+
const allErrors = [
|
|
238
|
+
...structural.errors,
|
|
239
|
+
...completeness.errors,
|
|
240
|
+
...determinism.errors,
|
|
241
|
+
...properties.errors,
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
if (allErrors.length > 0) {
|
|
245
|
+
const errorCount = allErrors.filter(e => e.severity === 'error').length;
|
|
246
|
+
const warningCount = allErrors.filter(e => e.severity === 'warning').length;
|
|
247
|
+
if (errorCount > 0) {
|
|
248
|
+
console.log(`\nFound ${errorCount} error(s)${warningCount > 0 ? ` and ${warningCount} warning(s)` : ''}:`);
|
|
249
|
+
} else {
|
|
250
|
+
console.log(`\nFound ${warningCount} warning(s):`);
|
|
251
|
+
}
|
|
252
|
+
formatErrors(allErrors);
|
|
253
|
+
if (errorCount > 0) process.exit(1);
|
|
254
|
+
} else {
|
|
255
|
+
const propCount = machine.properties?.length ?? 0;
|
|
256
|
+
if (propCount > 0) {
|
|
257
|
+
console.log(`\nVerification passed! (${propCount} properties checked)`);
|
|
258
|
+
} else {
|
|
259
|
+
console.log('\nVerification passed!');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function compileXState(input: SkillInput, json: boolean = false): Promise<void> {
|
|
265
|
+
if (json) {
|
|
266
|
+
const result = await compileSkill(input, 'xstate');
|
|
267
|
+
console.log(JSON.stringify(result, null, 2));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const source = sourceFromInput(input);
|
|
271
|
+
const { file } = parseMarkdown(source);
|
|
272
|
+
if (file.machines.length > 1) {
|
|
273
|
+
console.error('Multi-machine XState compilation not yet fully implemented. Compiling first machine only.');
|
|
274
|
+
}
|
|
275
|
+
const machine = file.machines[0];
|
|
276
|
+
const output = compileToXState(machine);
|
|
277
|
+
console.log(output);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function compileMermaid(input: SkillInput, json: boolean = false): Promise<void> {
|
|
281
|
+
if (json) {
|
|
282
|
+
const result = await compileSkill(input, 'mermaid');
|
|
283
|
+
console.log(JSON.stringify(result, null, 2));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const source = sourceFromInput(input);
|
|
287
|
+
const machine = parseFile(input.file ?? '<stdin>', source);
|
|
288
|
+
const output = compileToMermaid(machine);
|
|
289
|
+
console.log(output);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function visualize(input: SkillInput): Promise<void> {
|
|
293
|
+
const source = sourceFromInput(input);
|
|
294
|
+
const machine = parseFile(input.file ?? '<stdin>', source);
|
|
295
|
+
const mermaid = compileToMermaid(machine);
|
|
296
|
+
console.log('Mermaid diagram:');
|
|
297
|
+
console.log(mermaid);
|
|
298
|
+
console.log('\nYou can render this at: https://mermaid.live');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function langExt(language: string): { src: string; test: string; testSuffix: string } {
|
|
302
|
+
switch (language) {
|
|
303
|
+
case 'python': return { src: '.py', test: '_test.py', testSuffix: '.py' };
|
|
304
|
+
case 'go': return { src: '.go', test: '_test.go', testSuffix: '.go' };
|
|
305
|
+
default: return { src: '.ts', test: '.test.ts', testSuffix: '.ts' };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function generateActions(input: SkillInput, language: string, json: boolean = false, useLLM: boolean = false, outputPath?: string, generateTests: boolean = false): Promise<void> {
|
|
310
|
+
const result = await generateActionsSkill(input, language, useLLM, undefined, generateTests);
|
|
311
|
+
const ext = langExt(language);
|
|
312
|
+
|
|
313
|
+
if (outputPath) {
|
|
314
|
+
// Write scaffolds to output directory or file
|
|
315
|
+
const isDir = outputPath.endsWith('/') || !outputPath.includes('.');
|
|
316
|
+
if (isDir) {
|
|
317
|
+
if (!existsSync(outputPath)) {
|
|
318
|
+
mkdirSync(outputPath, { recursive: true });
|
|
319
|
+
}
|
|
320
|
+
for (const [name, scaffold] of Object.entries(result.scaffolds)) {
|
|
321
|
+
const fileName = `${name}${ext.src}`;
|
|
322
|
+
const code = stripCodeFence(scaffold);
|
|
323
|
+
writeFileSync(join(outputPath, fileName), code);
|
|
324
|
+
console.log(`Wrote: ${join(outputPath, fileName)}`);
|
|
325
|
+
}
|
|
326
|
+
if (result.tests) {
|
|
327
|
+
for (const [name, test] of Object.entries(result.tests)) {
|
|
328
|
+
const fileName = `${name}${ext.test}`;
|
|
329
|
+
const code = stripCodeFence(test);
|
|
330
|
+
writeFileSync(join(outputPath, fileName), code);
|
|
331
|
+
console.log(`Wrote: ${join(outputPath, fileName)}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
// Combine all scaffolds into single file
|
|
336
|
+
const combined = Object.entries(result.scaffolds)
|
|
337
|
+
.map(([name, scaffold]) => stripCodeFence(scaffold))
|
|
338
|
+
.join('\n\n');
|
|
339
|
+
writeFileSync(outputPath, combined);
|
|
340
|
+
console.log(`Wrote: ${outputPath}`);
|
|
341
|
+
|
|
342
|
+
if (result.tests) {
|
|
343
|
+
const dotIdx = outputPath.lastIndexOf('.');
|
|
344
|
+
const testPath = dotIdx !== -1
|
|
345
|
+
? outputPath.slice(0, dotIdx) + ext.test
|
|
346
|
+
: outputPath + ext.test;
|
|
347
|
+
const testCombined = Object.entries(result.tests)
|
|
348
|
+
.map(([_, test]) => stripCodeFence(test))
|
|
349
|
+
.join('\n\n');
|
|
350
|
+
writeFileSync(testPath, testCombined);
|
|
351
|
+
console.log(`Wrote: ${testPath}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (json) {
|
|
358
|
+
console.log(JSON.stringify(result, null, 2));
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
console.log(`Generated action scaffolds for ${result.machine}:`);
|
|
363
|
+
for (const [name, scaffold] of Object.entries(result.scaffolds)) {
|
|
364
|
+
console.log(`\n--- ${name} ---`);
|
|
365
|
+
console.log(scaffold);
|
|
366
|
+
if (result.tests?.[name]) {
|
|
367
|
+
console.log(`\n--- ${name} Tests ---`);
|
|
368
|
+
console.log(result.tests[name]);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function refine(input: SkillInput, errorsJson: string, json: boolean = false): Promise<void> {
|
|
374
|
+
const errors = JSON.parse(errorsJson);
|
|
375
|
+
const result = await refineSkill(input, errors);
|
|
376
|
+
|
|
377
|
+
if (json) {
|
|
378
|
+
console.log(JSON.stringify(result, null, 2));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (result.status === 'requires_refinement') {
|
|
383
|
+
console.error(`Refinement incomplete after ${result.iterations} iteration(s). Remaining errors:`);
|
|
384
|
+
for (const e of result.verification?.errors ?? []) {
|
|
385
|
+
console.error(` [${e.severity.toUpperCase()}] ${e.code}: ${e.message}`);
|
|
386
|
+
}
|
|
387
|
+
console.log(result.corrected);
|
|
388
|
+
process.exit(1);
|
|
389
|
+
} else if (result.status === 'error') {
|
|
390
|
+
console.error(`Refinement failed: ${result.error}`);
|
|
391
|
+
process.exit(1);
|
|
392
|
+
} else {
|
|
393
|
+
console.log(result.corrected);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function main(): Promise<void> {
|
|
398
|
+
const args = process.argv.slice(2);
|
|
399
|
+
|
|
400
|
+
if (args.length === 0) {
|
|
401
|
+
console.log('Orca CLI');
|
|
402
|
+
console.log('Usage:');
|
|
403
|
+
console.log(' orca verify [--json] <file.orca> [--stdin] - Parse and verify a machine');
|
|
404
|
+
console.log(' orca compile [--json] xstate <file.orca> [--stdin] - Compile to XState v5');
|
|
405
|
+
console.log(' orca compile [--json] mermaid <file.orca> [--stdin] - Compile to Mermaid diagram');
|
|
406
|
+
console.log(' orca visualize <file.orca> [--stdin] - Compile and show Mermaid');
|
|
407
|
+
console.log(' orca actions [--json] [--lang <lang>] [--output <path>] [--tests] <file.orca> [--stdin]');
|
|
408
|
+
console.log(' orca --tools --json - List all tools as JSON');
|
|
409
|
+
console.log('');
|
|
410
|
+
console.log('Auth commands:');
|
|
411
|
+
console.log(' orca login [--provider <provider>] [--profile <id>] - Login to an LLM provider');
|
|
412
|
+
console.log(' orca logout [--profile <id>] - Remove auth credentials');
|
|
413
|
+
console.log(' orca auth [--doctor] - Show auth status');
|
|
414
|
+
console.log('');
|
|
415
|
+
console.log('Skills (LLM-friendly):');
|
|
416
|
+
console.log(' orca /parse-machine [<file.orca.md>] [--stdin] - Parse and return AST as JSON');
|
|
417
|
+
console.log(' orca /verify-orca [<file.orca>] [--stdin] - Structured JSON verification');
|
|
418
|
+
console.log(' orca /compile-orca [target] [<file.orca>] [--stdin] - Structured JSON compilation');
|
|
419
|
+
console.log(' orca /generate-orca "spec" [--output=<file.orca>] - Generate Orca from natural language');
|
|
420
|
+
console.log(' orca /generate-orca-multi "spec" [--output=<file.orca.md>] - Generate coordinated multi-machine Orca');
|
|
421
|
+
console.log(' orca /generate-actions [--use-llm] [--lang <lang>] [--output <path>] [--tests] [<file>] [--stdin]');
|
|
422
|
+
console.log(' orca /refine-orca [<file.orca>] [--stdin] - Fix verification errors (requires LLM)');
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Handle auth commands (before stdin/tools processing)
|
|
427
|
+
if (args[0] === 'login') {
|
|
428
|
+
let provider = 'anthropic';
|
|
429
|
+
let profileId = 'default';
|
|
430
|
+
for (let i = 1; i < args.length; i++) {
|
|
431
|
+
if (args[i] === '--provider' && args[i + 1]) provider = args[++i];
|
|
432
|
+
if (args[i] === '--profile' && args[i + 1]) profileId = args[++i];
|
|
433
|
+
}
|
|
434
|
+
await login(provider, profileId);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (args[0] === 'logout') {
|
|
439
|
+
let profileId: string | undefined;
|
|
440
|
+
for (let i = 1; i < args.length; i++) {
|
|
441
|
+
if (args[i] === '--profile' && args[i + 1]) profileId = args[++i];
|
|
442
|
+
}
|
|
443
|
+
await logout(profileId);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (args[0] === 'auth') {
|
|
448
|
+
await auth(args.includes('--doctor'));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// B3: tool discovery
|
|
453
|
+
if (args[0] === '--tools' && args[1] === '--json') {
|
|
454
|
+
console.log(JSON.stringify(ORCA_TOOLS, null, 2));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// B2: strip --stdin from arg list
|
|
459
|
+
const useStdin = args.includes('--stdin');
|
|
460
|
+
const cleanArgs = args.filter(a => a !== '--stdin');
|
|
461
|
+
|
|
462
|
+
// Check for skill invocations (starting with /)
|
|
463
|
+
if (cleanArgs[0].startsWith('/')) {
|
|
464
|
+
const skill = cleanArgs[0];
|
|
465
|
+
const skillArgs = cleanArgs.slice(1);
|
|
466
|
+
|
|
467
|
+
if (skill === '/parse-machine') {
|
|
468
|
+
const fileArg = skillArgs.find(a => !a.startsWith('-'));
|
|
469
|
+
const input = await getInput(fileArg, useStdin);
|
|
470
|
+
const result = parseSkill(input);
|
|
471
|
+
console.log(JSON.stringify(result, null, 2));
|
|
472
|
+
process.exit(result.status === 'success' ? 0 : 1);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (skill === '/verify-orca') {
|
|
476
|
+
const fileArg = skillArgs.find(a => !a.startsWith('-'));
|
|
477
|
+
const input = await getInput(fileArg, useStdin);
|
|
478
|
+
const result = await verifySkill(input);
|
|
479
|
+
console.log(JSON.stringify(result, null, 2));
|
|
480
|
+
process.exit(result.status === 'valid' ? 0 : 1);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (skill === '/compile-orca') {
|
|
484
|
+
const isTarget = (s: string) => s === 'xstate' || s === 'mermaid';
|
|
485
|
+
const target = (skillArgs.find(isTarget) as 'xstate' | 'mermaid') ?? 'xstate';
|
|
486
|
+
const fileArg = skillArgs.find(a => !a.startsWith('-') && !isTarget(a));
|
|
487
|
+
const input = await getInput(fileArg, useStdin);
|
|
488
|
+
const result = await compileSkill(input, target);
|
|
489
|
+
console.log(JSON.stringify(result, null, 2));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (skill === '/generate-actions') {
|
|
494
|
+
let useLLM = false;
|
|
495
|
+
let generateTests = false;
|
|
496
|
+
let lang = 'typescript';
|
|
497
|
+
let outputPath: string | undefined;
|
|
498
|
+
let fileArg: string | undefined;
|
|
499
|
+
|
|
500
|
+
for (let i = 0; i < skillArgs.length; i++) {
|
|
501
|
+
const arg = skillArgs[i];
|
|
502
|
+
if (arg === '--use-llm') useLLM = true;
|
|
503
|
+
if (arg === '--tests') generateTests = true;
|
|
504
|
+
if (arg === '--lang' && skillArgs[i + 1]) lang = skillArgs[++i];
|
|
505
|
+
if ((arg === '--output' || arg === '-o') && skillArgs[i + 1]) outputPath = skillArgs[++i];
|
|
506
|
+
if (arg?.endsWith('.orca') || arg?.endsWith('.orca.md')) fileArg = arg;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const input = await getInput(fileArg, useStdin);
|
|
510
|
+
await generateActions(input, lang, false, useLLM, outputPath, generateTests);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (skill === '/refine-orca') {
|
|
515
|
+
const fileArg = skillArgs.find(a => a.endsWith('.orca') || a.endsWith('.orca.md'));
|
|
516
|
+
const errorsJson = skillArgs.find(a => a.startsWith('[')) || '[]';
|
|
517
|
+
const input = await getInput(fileArg, useStdin);
|
|
518
|
+
const result = await refineSkill(input, JSON.parse(errorsJson));
|
|
519
|
+
console.log(JSON.stringify(result, null, 2));
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (skill === '/generate-orca') {
|
|
524
|
+
const spec = skillArgs[0] || skillArgs.find(a => !a.startsWith('--')) || '';
|
|
525
|
+
const outputPath = skillArgs.find(a => a.startsWith('--output='))?.replace('--output=', '') ||
|
|
526
|
+
skillArgs.find(a => a.startsWith('-o='))?.replace('-o=', '');
|
|
527
|
+
|
|
528
|
+
if (!spec) {
|
|
529
|
+
console.error('Usage: /generate-orca "natural language specification" [--output=<file.orca>]');
|
|
530
|
+
process.exit(1);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const result = await generateOrcaSkill(spec);
|
|
534
|
+
|
|
535
|
+
if (result.status === 'success' && result.orca) {
|
|
536
|
+
if (outputPath) {
|
|
537
|
+
writeFileSync(outputPath, result.orca);
|
|
538
|
+
console.log(`Generated: ${outputPath}`);
|
|
539
|
+
} else {
|
|
540
|
+
console.log(result.orca);
|
|
541
|
+
}
|
|
542
|
+
} else if (result.status === 'requires_refinement' && result.orca) {
|
|
543
|
+
console.log('Machine generated but verification found issues. Outputting for manual review:');
|
|
544
|
+
console.log(result.orca);
|
|
545
|
+
if (result.verification?.errors.length) {
|
|
546
|
+
console.log('\nVerification issues:');
|
|
547
|
+
for (const err of result.verification.errors) {
|
|
548
|
+
console.log(` [${err.severity.toUpperCase()}] ${err.code}: ${err.message}`);
|
|
549
|
+
if (err.suggestion) console.log(` Suggestion: ${err.suggestion}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
} else {
|
|
553
|
+
console.error(`Generation failed: ${result.error}`);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
}
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (skill === '/generate-orca-multi') {
|
|
560
|
+
const spec = skillArgs[0] || skillArgs.find(a => !a.startsWith('--')) || '';
|
|
561
|
+
const outputPath = skillArgs.find(a => a.startsWith('--output='))?.replace('--output=', '') ||
|
|
562
|
+
skillArgs.find(a => a.startsWith('-o='))?.replace('-o=', '');
|
|
563
|
+
|
|
564
|
+
if (!spec) {
|
|
565
|
+
console.error('Usage: /generate-orca-multi "natural language specification" [--output=<file.orca.md>]');
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const result = await generateOrcaMultiSkill(spec);
|
|
570
|
+
|
|
571
|
+
if (result.status === 'success' && result.orca) {
|
|
572
|
+
if (outputPath) {
|
|
573
|
+
writeFileSync(outputPath, result.orca);
|
|
574
|
+
console.log(`Generated: ${outputPath} (machines: ${result.machines?.join(', ')})`);
|
|
575
|
+
} else {
|
|
576
|
+
console.log(result.orca);
|
|
577
|
+
}
|
|
578
|
+
} else if (result.status === 'requires_refinement' && result.orca) {
|
|
579
|
+
console.log('Machines generated but verification found issues. Outputting for manual review:');
|
|
580
|
+
console.log(result.orca);
|
|
581
|
+
if (result.errors?.length) {
|
|
582
|
+
console.log('\nVerification issues:');
|
|
583
|
+
for (const err of result.errors) {
|
|
584
|
+
console.log(` [${err.severity.toUpperCase()}] ${err.code}: ${err.message}`);
|
|
585
|
+
if (err.suggestion) console.log(` Suggestion: ${err.suggestion}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
} else {
|
|
589
|
+
console.error(`Generation failed: ${result.error}`);
|
|
590
|
+
process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
console.error(`Unknown skill: ${skill}`);
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Standard commands
|
|
600
|
+
const command = cleanArgs[0];
|
|
601
|
+
|
|
602
|
+
// Check for --json flag
|
|
603
|
+
let json = false;
|
|
604
|
+
let filteredArgs = cleanArgs;
|
|
605
|
+
if (cleanArgs[1] === '--json') {
|
|
606
|
+
json = true;
|
|
607
|
+
filteredArgs = [cleanArgs[0], ...cleanArgs.slice(2)];
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
if (command === 'verify') {
|
|
612
|
+
const input = await getInput(filteredArgs[1], useStdin);
|
|
613
|
+
await verify(input, json);
|
|
614
|
+
} else if (command === 'compile' && filteredArgs[1] === 'xstate') {
|
|
615
|
+
const input = await getInput(filteredArgs[2], useStdin);
|
|
616
|
+
await compileXState(input, json);
|
|
617
|
+
} else if (command === 'compile' && filteredArgs[1] === 'mermaid') {
|
|
618
|
+
const input = await getInput(filteredArgs[2], useStdin);
|
|
619
|
+
await compileMermaid(input, json);
|
|
620
|
+
} else if (command === 'visualize') {
|
|
621
|
+
const input = await getInput(filteredArgs[1], useStdin);
|
|
622
|
+
await visualize(input);
|
|
623
|
+
} else if (command === 'convert') {
|
|
624
|
+
const inputPath = filteredArgs[1];
|
|
625
|
+
if (!inputPath) {
|
|
626
|
+
console.error('Usage: orca convert <input.orca> [-o <output.orca.md>]');
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
const outputIdx = filteredArgs.indexOf('-o');
|
|
630
|
+
const outputPath = outputIdx !== -1 ? filteredArgs[outputIdx + 1] : inputPath.replace(/\.orca$/, '.orca.md');
|
|
631
|
+
const source = readFileSync(inputPath, 'utf-8');
|
|
632
|
+
const machine = parseFile(inputPath, source);
|
|
633
|
+
const md = machineToMarkdown(machine);
|
|
634
|
+
writeFileSync(outputPath, md);
|
|
635
|
+
console.log(`Converted: ${inputPath} -> ${outputPath}`);
|
|
636
|
+
// Verify round-trip
|
|
637
|
+
const roundTrip = parseMarkdown(md).file.machines[0];
|
|
638
|
+
console.log(`Round-trip verification: ${JSON.stringify(machine) === JSON.stringify(roundTrip) ? 'PASS' : 'WARN: ASTs differ'}`);
|
|
639
|
+
} else if (command === 'actions') {
|
|
640
|
+
let lang = 'typescript';
|
|
641
|
+
let useLLM = false;
|
|
642
|
+
let generateTests = false;
|
|
643
|
+
let outputPath: string | undefined;
|
|
644
|
+
let fileArg: string | undefined;
|
|
645
|
+
for (let i = 1; i < filteredArgs.length; i++) {
|
|
646
|
+
if (filteredArgs[i] === '--lang' && filteredArgs[i + 1]) lang = filteredArgs[++i];
|
|
647
|
+
if ((filteredArgs[i] === '--output' || filteredArgs[i] === '-o') && filteredArgs[i + 1]) outputPath = filteredArgs[++i];
|
|
648
|
+
if (filteredArgs[i] === '--use-llm') useLLM = true;
|
|
649
|
+
if (filteredArgs[i] === '--tests') generateTests = true;
|
|
650
|
+
if (filteredArgs[i]?.endsWith('.orca') || filteredArgs[i]?.endsWith('.orca.md')) fileArg = filteredArgs[i];
|
|
651
|
+
}
|
|
652
|
+
if (!fileArg) fileArg = filteredArgs[filteredArgs.length - 1];
|
|
653
|
+
const input = await getInput(fileArg === command ? undefined : fileArg, useStdin);
|
|
654
|
+
await generateActions(input, lang, json, useLLM, outputPath, generateTests);
|
|
655
|
+
} else {
|
|
656
|
+
console.error(`Unknown command: ${command}`);
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
} catch (err) {
|
|
660
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Only run main() when this file is executed directly (not imported as a module)
|
|
666
|
+
// In ESM, we check if this module is the main entry point
|
|
667
|
+
import { fileURLToPath } from 'url';
|
|
668
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
669
|
+
if (process.argv[1] && process.argv[1].endsWith(__filename)) {
|
|
670
|
+
main();
|
|
671
|
+
}
|