@agentlang/cli 0.6.6
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/.pnpmrc +10 -0
- package/LICENSE +83 -0
- package/README.md +690 -0
- package/bin/cli.js +4 -0
- package/out/docs.js +296 -0
- package/out/docs.js.map +1 -0
- package/out/main.js +544 -0
- package/out/main.js.map +1 -0
- package/out/repl.js +785 -0
- package/out/repl.js.map +1 -0
- package/out/ui-generator/specFinder.js +50 -0
- package/out/ui-generator/specFinder.js.map +1 -0
- package/out/ui-generator/specLoader.js +30 -0
- package/out/ui-generator/specLoader.js.map +1 -0
- package/out/ui-generator/uiGenerator.js +1859 -0
- package/out/ui-generator/uiGenerator.js.map +1 -0
- package/package.json +97 -0
package/out/repl.js
ADDED
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
import * as readline from 'node:readline';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as chokidar from 'chokidar';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { load, loadRawConfig, parseAndIntern } from 'agentlang/out/runtime/loader.js';
|
|
6
|
+
import { addModule, getActiveModuleName, fetchModule, getUserModuleNames, getEntity, addEntity, removeEntity, getRecord, addRecord, removeRecord, getRelationship, addRelationship, removeRelationship, getWorkflow, addWorkflow, removeWorkflow, getEvent, addEvent, removeEvent, removeModule, } from 'agentlang/out/runtime/module.js';
|
|
7
|
+
import { addFromDef, addRelationshipFromDef, addWorkflowFromDef } from 'agentlang/out/runtime/loader.js';
|
|
8
|
+
import { parseModule } from 'agentlang/out/language/parser.js';
|
|
9
|
+
import { isEntityDefinition, isEventDefinition, isRecordDefinition, isRelationshipDefinition, isWorkflowDefinition, } from 'agentlang/out/language/generated/ast.js';
|
|
10
|
+
import { setAppConfig } from 'agentlang/out/runtime/state.js';
|
|
11
|
+
import { runPreInitTasks, runPostInitTasks } from './main.js';
|
|
12
|
+
import { lookupAllInstances, parseAndEvaluateStatement } from 'agentlang/out/runtime/interpreter.js';
|
|
13
|
+
// Global REPL state
|
|
14
|
+
let replState = null;
|
|
15
|
+
// Core AgentLang processing function
|
|
16
|
+
async function processAgentlang(code) {
|
|
17
|
+
let currentModule = getActiveModuleName();
|
|
18
|
+
if (!currentModule && (replState === null || replState === void 0 ? void 0 : replState.appSpec) && 'name' in replState.appSpec) {
|
|
19
|
+
currentModule = replState.appSpec.name;
|
|
20
|
+
}
|
|
21
|
+
if (!currentModule) {
|
|
22
|
+
throw new Error('No active module found. Please ensure the application is loaded.');
|
|
23
|
+
}
|
|
24
|
+
// For individual definitions, use a different approach to avoid module replacement
|
|
25
|
+
const trimmedCode = code.trim();
|
|
26
|
+
// Check if it's a simple entity, record, event, or relationship definition
|
|
27
|
+
if (trimmedCode.startsWith('entity ') ||
|
|
28
|
+
trimmedCode.startsWith('record ') ||
|
|
29
|
+
trimmedCode.startsWith('event ') ||
|
|
30
|
+
trimmedCode.startsWith('relationship ') ||
|
|
31
|
+
trimmedCode.startsWith('workflow ')) {
|
|
32
|
+
try {
|
|
33
|
+
// Parse the definition in a temporary module context to get the AST
|
|
34
|
+
const tempModuleName = `__temp_${Date.now()}`;
|
|
35
|
+
const wrappedCode = `module ${tempModuleName}\n\n${code}`;
|
|
36
|
+
const parsedModule = await parseModule(wrappedCode);
|
|
37
|
+
// Extract the definition from the parsed module
|
|
38
|
+
if (parsedModule.defs && parsedModule.defs.length > 0) {
|
|
39
|
+
const def = parsedModule.defs[0];
|
|
40
|
+
// Use the appropriate specific function based on the definition type
|
|
41
|
+
if (isEntityDefinition(def) || isEventDefinition(def) || isRecordDefinition(def)) {
|
|
42
|
+
await addFromDef(def, currentModule);
|
|
43
|
+
}
|
|
44
|
+
else if (isRelationshipDefinition(def)) {
|
|
45
|
+
addRelationshipFromDef(def, currentModule);
|
|
46
|
+
}
|
|
47
|
+
else if (isWorkflowDefinition(def)) {
|
|
48
|
+
addWorkflowFromDef(def, currentModule);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Fall back to the general addFromDef for other types
|
|
52
|
+
await addFromDef(def, currentModule);
|
|
53
|
+
}
|
|
54
|
+
return '✓ AgentLang code processed successfully';
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error('No definitions found in parsed code');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// If the custom approach fails, fall back to the original method
|
|
62
|
+
// eslint-disable-next-line no-console
|
|
63
|
+
console.warn('Custom parsing failed, falling back to original method:', error);
|
|
64
|
+
await parseAndIntern(code, currentModule);
|
|
65
|
+
return '✓ AgentLang code processed successfully';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// For complex code or module-level statements, use the original approach
|
|
70
|
+
await parseAndIntern(code, currentModule);
|
|
71
|
+
return '✓ AgentLang code processed successfully';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// REPL Helper Functions - Multiple syntax styles like original Deno REPL
|
|
75
|
+
function createReplHelpers() {
|
|
76
|
+
// Template literal and function syntax for AgentLang code
|
|
77
|
+
const al = function (strings, ...values) {
|
|
78
|
+
if (Array.isArray(strings) && 'raw' in strings) {
|
|
79
|
+
// Template literal: al`entity User { name String }`
|
|
80
|
+
const code = strings.reduce((acc, str, i) => {
|
|
81
|
+
const value = values[i];
|
|
82
|
+
let valueStr = '';
|
|
83
|
+
if (value !== undefined) {
|
|
84
|
+
if (typeof value === 'object' && value !== null) {
|
|
85
|
+
valueStr = JSON.stringify(value);
|
|
86
|
+
}
|
|
87
|
+
else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
88
|
+
valueStr = String(value);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
valueStr = '[object]';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return acc + str + valueStr;
|
|
95
|
+
}, '');
|
|
96
|
+
return processAgentlang(code);
|
|
97
|
+
}
|
|
98
|
+
else if (typeof strings === 'string') {
|
|
99
|
+
// Function call: al("entity User { name String }")
|
|
100
|
+
return processAgentlang(strings);
|
|
101
|
+
}
|
|
102
|
+
throw new Error("Invalid usage of al(). Use al`code` or al('code')");
|
|
103
|
+
};
|
|
104
|
+
// Enhanced entity function - multiple syntax styles
|
|
105
|
+
const e = function (name, definition) {
|
|
106
|
+
if (typeof name === 'string' && typeof definition === 'object') {
|
|
107
|
+
// Object style: e("User", { name: "String", age: "Int" })
|
|
108
|
+
const fields = Object.entries(definition)
|
|
109
|
+
.map(([key, type]) => ` ${key} ${type}`)
|
|
110
|
+
.join('\n');
|
|
111
|
+
const code = `entity ${name} {\n${fields}\n}`;
|
|
112
|
+
return processAgentlang(code);
|
|
113
|
+
}
|
|
114
|
+
else if (typeof name === 'string' && typeof definition === 'string') {
|
|
115
|
+
// String style: e("User", "{ id String @id, name String }")
|
|
116
|
+
const cleanDef = definition.trim();
|
|
117
|
+
const fieldsContent = cleanDef.startsWith('{') && cleanDef.endsWith('}') ? cleanDef.slice(1, -1).trim() : cleanDef;
|
|
118
|
+
const code = `entity ${name} { ${fieldsContent} }`;
|
|
119
|
+
return processAgentlang(code);
|
|
120
|
+
}
|
|
121
|
+
else if (typeof name === 'string' && !definition) {
|
|
122
|
+
// Simple style: e("User")
|
|
123
|
+
const code = `entity ${name} {}`;
|
|
124
|
+
return processAgentlang(code);
|
|
125
|
+
}
|
|
126
|
+
throw new Error("Invalid usage of e(). Use e('Name', {fields}) or e('Name', 'fields') or e('Name')");
|
|
127
|
+
};
|
|
128
|
+
// Record function
|
|
129
|
+
const r = function (name, definition) {
|
|
130
|
+
if (typeof name === 'string' && typeof definition === 'object') {
|
|
131
|
+
const fields = Object.entries(definition)
|
|
132
|
+
.map(([key, type]) => ` ${key} ${type}`)
|
|
133
|
+
.join('\n');
|
|
134
|
+
const code = `record ${name} {\n${fields}\n}`;
|
|
135
|
+
return processAgentlang(code);
|
|
136
|
+
}
|
|
137
|
+
else if (typeof name === 'string' && typeof definition === 'string') {
|
|
138
|
+
const cleanDef = definition.trim();
|
|
139
|
+
const fieldsContent = cleanDef.startsWith('{') && cleanDef.endsWith('}') ? cleanDef.slice(1, -1).trim() : cleanDef;
|
|
140
|
+
const code = `record ${name} { ${fieldsContent} }`;
|
|
141
|
+
return processAgentlang(code);
|
|
142
|
+
}
|
|
143
|
+
else if (typeof name === 'string' && !definition) {
|
|
144
|
+
const code = `record ${name} {}`;
|
|
145
|
+
return processAgentlang(code);
|
|
146
|
+
}
|
|
147
|
+
throw new Error("Invalid usage of r(). Use r('Name', {fields}) or r('Name', 'fields') or r('Name')");
|
|
148
|
+
};
|
|
149
|
+
// Event function
|
|
150
|
+
const ev = function (name, definition) {
|
|
151
|
+
if (typeof name === 'string' && typeof definition === 'object') {
|
|
152
|
+
const fields = Object.entries(definition)
|
|
153
|
+
.map(([key, type]) => ` ${key} ${type}`)
|
|
154
|
+
.join('\n');
|
|
155
|
+
const code = `event ${name} {\n${fields}\n}`;
|
|
156
|
+
return processAgentlang(code);
|
|
157
|
+
}
|
|
158
|
+
else if (typeof name === 'string' && typeof definition === 'string') {
|
|
159
|
+
const cleanDef = definition.trim();
|
|
160
|
+
const fieldsContent = cleanDef.startsWith('{') && cleanDef.endsWith('}') ? cleanDef.slice(1, -1).trim() : cleanDef;
|
|
161
|
+
const code = `event ${name} { ${fieldsContent} }`;
|
|
162
|
+
return processAgentlang(code);
|
|
163
|
+
}
|
|
164
|
+
else if (typeof name === 'string' && !definition) {
|
|
165
|
+
const code = `event ${name} {}`;
|
|
166
|
+
return processAgentlang(code);
|
|
167
|
+
}
|
|
168
|
+
throw new Error("Invalid usage of ev(). Use ev('Name', {fields}) or ev('Name', 'fields') or ev('Name')");
|
|
169
|
+
};
|
|
170
|
+
// Relationship function
|
|
171
|
+
const rel = function (name, type, nodes) {
|
|
172
|
+
if (nodes.length !== 2) {
|
|
173
|
+
throw new Error('Relationship requires exactly 2 nodes');
|
|
174
|
+
}
|
|
175
|
+
const code = `relationship ${name} { ${type} [${nodes.join(', ')}] }`;
|
|
176
|
+
return processAgentlang(code);
|
|
177
|
+
};
|
|
178
|
+
// Workflow function
|
|
179
|
+
const w = function (name, statements) {
|
|
180
|
+
const stmts = statements ? statements.join('\n ') : '';
|
|
181
|
+
const code = `workflow ${name} {\n ${stmts}\n}`;
|
|
182
|
+
return processAgentlang(code);
|
|
183
|
+
};
|
|
184
|
+
// Instance creation helper
|
|
185
|
+
const inst = async function (entityName, attributes) {
|
|
186
|
+
const currentModule = getActiveModuleName();
|
|
187
|
+
if (!currentModule) {
|
|
188
|
+
throw new Error('No active module found');
|
|
189
|
+
}
|
|
190
|
+
// Helper to format values for AgentLang syntax
|
|
191
|
+
const formatValue = (value) => {
|
|
192
|
+
var _a;
|
|
193
|
+
if (typeof value === 'string') {
|
|
194
|
+
return `"${value}"`;
|
|
195
|
+
}
|
|
196
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
197
|
+
return String(value);
|
|
198
|
+
}
|
|
199
|
+
else if (Array.isArray(value)) {
|
|
200
|
+
return `[${value.map(formatValue).join(', ')}]`;
|
|
201
|
+
}
|
|
202
|
+
else if (value === null || value === undefined) {
|
|
203
|
+
return 'nil';
|
|
204
|
+
}
|
|
205
|
+
else if (typeof value === 'object') {
|
|
206
|
+
const entries = Object.entries(value)
|
|
207
|
+
.map(([k, v]) => `${k} ${formatValue(v)}`)
|
|
208
|
+
.join(', ');
|
|
209
|
+
return `{${entries}}`;
|
|
210
|
+
}
|
|
211
|
+
// For unsupported types, use JSON.stringify as fallback
|
|
212
|
+
return (_a = JSON.stringify(value)) !== null && _a !== void 0 ? _a : 'nil';
|
|
213
|
+
};
|
|
214
|
+
// Build the statement string
|
|
215
|
+
const fields = Object.entries(attributes)
|
|
216
|
+
.map(([key, value]) => `${key} ${formatValue(value)}`)
|
|
217
|
+
.join(', ');
|
|
218
|
+
const statement = `{${currentModule}/${entityName} {${fields}}}`;
|
|
219
|
+
// Parse and evaluate the statement
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
221
|
+
const result = await parseAndEvaluateStatement(statement);
|
|
222
|
+
return result;
|
|
223
|
+
};
|
|
224
|
+
// Module management object
|
|
225
|
+
const m = {
|
|
226
|
+
list: () => getUserModuleNames(),
|
|
227
|
+
get: (name) => fetchModule(name),
|
|
228
|
+
active: () => getActiveModuleName(),
|
|
229
|
+
add: (name) => addModule(name),
|
|
230
|
+
remove: (name) => removeModule(name),
|
|
231
|
+
};
|
|
232
|
+
// Inspection utilities
|
|
233
|
+
const inspect = {
|
|
234
|
+
modules: () => {
|
|
235
|
+
const modules = getUserModuleNames();
|
|
236
|
+
// eslint-disable-next-line no-console
|
|
237
|
+
console.log(chalk.blue('📦 Available Modules:'));
|
|
238
|
+
// eslint-disable-next-line no-console
|
|
239
|
+
modules.forEach(mod => console.log(` • ${mod}`));
|
|
240
|
+
return modules;
|
|
241
|
+
},
|
|
242
|
+
entities: (moduleName) => {
|
|
243
|
+
const mod = moduleName ? fetchModule(moduleName) : fetchModule(getActiveModuleName());
|
|
244
|
+
const entities = mod.getEntityNames();
|
|
245
|
+
// eslint-disable-next-line no-console
|
|
246
|
+
console.log(chalk.green(`🏗️ Entities in ${mod.name}:`));
|
|
247
|
+
// eslint-disable-next-line no-console
|
|
248
|
+
entities.forEach(ent => console.log(` • ${ent}`));
|
|
249
|
+
return entities;
|
|
250
|
+
},
|
|
251
|
+
events: (moduleName) => {
|
|
252
|
+
const mod = moduleName ? fetchModule(moduleName) : fetchModule(getActiveModuleName());
|
|
253
|
+
const events = mod.getEventNames();
|
|
254
|
+
// eslint-disable-next-line no-console
|
|
255
|
+
console.log(chalk.yellow(`⚡ Events in ${mod.name}:`));
|
|
256
|
+
// eslint-disable-next-line no-console
|
|
257
|
+
events.forEach(evt => console.log(` • ${evt}`));
|
|
258
|
+
return events;
|
|
259
|
+
},
|
|
260
|
+
relationships: (moduleName) => {
|
|
261
|
+
const mod = moduleName ? fetchModule(moduleName) : fetchModule(getActiveModuleName());
|
|
262
|
+
const rels = mod.getRelationshipNames();
|
|
263
|
+
// eslint-disable-next-line no-console
|
|
264
|
+
console.log(chalk.magenta(`🔗 Relationships in ${mod.name}:`));
|
|
265
|
+
// eslint-disable-next-line no-console
|
|
266
|
+
rels.forEach(rel => console.log(` • ${rel}`));
|
|
267
|
+
return rels;
|
|
268
|
+
},
|
|
269
|
+
instances: async (entityName) => {
|
|
270
|
+
if (!entityName) {
|
|
271
|
+
throw new Error('entityName is required');
|
|
272
|
+
}
|
|
273
|
+
const instances = await lookupAllInstances(entityName);
|
|
274
|
+
// eslint-disable-next-line no-console
|
|
275
|
+
console.log(chalk.cyan(`🏭 Instances for ${entityName}:`));
|
|
276
|
+
return instances;
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
// Utility functions
|
|
280
|
+
const utils = {
|
|
281
|
+
help: () => {
|
|
282
|
+
/* eslint-disable no-console */
|
|
283
|
+
console.log(chalk.blue.bold('\n🚀 AgentLang REPL - Comprehensive Guide\n'));
|
|
284
|
+
console.log(chalk.green.bold('📋 Basic Commands:'));
|
|
285
|
+
console.log(' help, ? // Show this help');
|
|
286
|
+
console.log(' exit, quit // Exit REPL');
|
|
287
|
+
console.log(' clear // Clear screen');
|
|
288
|
+
console.log(' restart // Restart REPL');
|
|
289
|
+
console.log(chalk.cyan.bold('\n🏗️ Entity Creation:'));
|
|
290
|
+
console.log(' e("User") // Empty entity');
|
|
291
|
+
console.log(' e("User", {name: "String"}) // Object syntax');
|
|
292
|
+
console.log(' e("User", "name String, age Int") // String syntax');
|
|
293
|
+
console.log(' entity("User", {id: "String @id"}) // Alias for e()');
|
|
294
|
+
console.log(chalk.magenta.bold('\n📄 Record Creation:'));
|
|
295
|
+
console.log(' r("Config") // Empty record');
|
|
296
|
+
console.log(' r("Config", {key: "String"}) // Object syntax');
|
|
297
|
+
console.log(' r("Config", "key String, val Any") // String syntax');
|
|
298
|
+
console.log(' record("Config", {settings: "Map"}) // Alias for r()');
|
|
299
|
+
console.log(chalk.yellow.bold('\n⚡ Event Creation:'));
|
|
300
|
+
console.log(' ev("UserCreated") // Empty event');
|
|
301
|
+
console.log(' ev("UserCreated", {id: "String"}) // Object syntax');
|
|
302
|
+
console.log(' ev("UserCreated", "id String") // String syntax');
|
|
303
|
+
console.log(' event("UserCreated", {data: "Map"}) // Alias for ev()');
|
|
304
|
+
console.log(chalk.red.bold('\n🔗 Relationship Creation:'));
|
|
305
|
+
console.log(' rel("UserPosts", "contains", ["User", "Post"])');
|
|
306
|
+
console.log(' rel("Friendship", "between", ["User", "User"])');
|
|
307
|
+
console.log(' relationship("Owns", "contains", ["User", "Asset"])');
|
|
308
|
+
console.log(chalk.blue.bold('\n🔄 Workflow Creation:'));
|
|
309
|
+
console.log(' w("ProcessUser") // Empty workflow');
|
|
310
|
+
console.log(' w("ProcessUser", ["step1", "step2"]) // With steps');
|
|
311
|
+
console.log(' workflow("HandleOrder", ["validate", "process"])');
|
|
312
|
+
console.log(chalk.green.bold('\n🏭 Instance Creation:'));
|
|
313
|
+
console.log(' inst("User", {name: "John", age: 30})');
|
|
314
|
+
console.log(' instance("Post", {title: "Hello", content: "World"})');
|
|
315
|
+
console.log(chalk.cyan.bold('\n📝 Template Literal Usage:'));
|
|
316
|
+
console.log(' al`entity User { name String }` // AgentLang code');
|
|
317
|
+
console.log(' al`record Config { key String }` // Multi-line supported');
|
|
318
|
+
console.log(' al("entity User { name String }") // Function syntax');
|
|
319
|
+
console.log(' ag`event Created { id String }` // Alias for al');
|
|
320
|
+
console.log(chalk.magenta.bold('\n📦 Module Management (m.*):'));
|
|
321
|
+
console.log(' m.active() // Get active module name');
|
|
322
|
+
console.log(' m.list() // List all user modules');
|
|
323
|
+
console.log(' m.get("MyApp") // Get specific module');
|
|
324
|
+
console.log(' m.add("NewMod") // Add new module');
|
|
325
|
+
console.log(' m.remove("Mod") // Remove module');
|
|
326
|
+
console.log(' modules.active() // Alias for m.active()');
|
|
327
|
+
console.log(chalk.yellow.bold('\n🔍 Inspection Commands (inspect.*):'));
|
|
328
|
+
console.log(' inspect.modules() // List all modules');
|
|
329
|
+
console.log(' inspect.entities() // List entities in active module');
|
|
330
|
+
console.log(' inspect.entities("MyApp") // List entities in specific module');
|
|
331
|
+
console.log(' inspect.events() // List events in active module');
|
|
332
|
+
console.log(' inspect.events("MyApp") // List events in specific module');
|
|
333
|
+
console.log(' inspect.relationships() // List relationships in active module');
|
|
334
|
+
console.log(' inspect.relationships("MyApp") // List relationships in specific module');
|
|
335
|
+
console.log(' inspect.instances("MyApp/EntityName") // List instances created for an entity');
|
|
336
|
+
console.log(chalk.red.bold('\n🛠️ Direct Runtime Functions (requires full qualified names in string: "<ModuleName>/<Name>"):'));
|
|
337
|
+
console.log(chalk.white(' Entity Management:'));
|
|
338
|
+
console.log(' addEntity(name, definition) // Add entity to runtime');
|
|
339
|
+
console.log(' removeEntity(name) // Remove entity from runtime');
|
|
340
|
+
console.log(' getEntity(name) // Get entity definition');
|
|
341
|
+
console.log(chalk.white(' Record Management:'));
|
|
342
|
+
console.log(' addRecord(name, definition) // Add record to runtime');
|
|
343
|
+
console.log(' removeRecord(name) // Remove record from runtime');
|
|
344
|
+
console.log(' getRecord(name) // Get record definition');
|
|
345
|
+
console.log(chalk.white(' Event Management:'));
|
|
346
|
+
console.log(' addEvent(name, definition) // Add event to runtime');
|
|
347
|
+
console.log(' removeEvent(name) // Remove event from runtime');
|
|
348
|
+
console.log(' getEvent(name) // Get event definition');
|
|
349
|
+
console.log(chalk.white(' Relationship Management:'));
|
|
350
|
+
console.log(' addRelationship(name, def) // Add relationship to runtime');
|
|
351
|
+
console.log(' removeRelationship(name) // Remove relationship from runtime');
|
|
352
|
+
console.log(' getRelationship(name) // Get relationship definition');
|
|
353
|
+
console.log(chalk.white(' Workflow Management:'));
|
|
354
|
+
console.log(' addWorkflow(name, definition) // Add workflow to runtime');
|
|
355
|
+
console.log(' removeWorkflow(name) // Remove workflow from runtime');
|
|
356
|
+
console.log(' getWorkflow(name) // Get workflow definition');
|
|
357
|
+
console.log(chalk.white(' Core Processing:'));
|
|
358
|
+
console.log(' processAgentlang(code) // Process raw AgentLang code');
|
|
359
|
+
console.log(' parseAndEvaluateStatement(stmt) // Parse and evaluate AgentLang statement');
|
|
360
|
+
console.log(' // Example: parseAndEvaluateStatement("{MyApp/User {id 1, name \\"Alice\\"}}");');
|
|
361
|
+
console.log(chalk.gray.bold('\n🛠️ Utility Commands (utils.*):'));
|
|
362
|
+
console.log(' utils.help() // Show this help');
|
|
363
|
+
console.log(' utils.clear() // Clear screen');
|
|
364
|
+
console.log(' utils.restart() // Restart REPL');
|
|
365
|
+
console.log(' utils.exit() // Exit REPL');
|
|
366
|
+
console.log(chalk.gray.bold('\n💡 Tips:'));
|
|
367
|
+
console.log(' • Use tab completion for commands');
|
|
368
|
+
console.log(' • Template literals support multi-line code');
|
|
369
|
+
console.log(' • All functions return promises - use await if needed');
|
|
370
|
+
console.log(' • File watching auto-restarts on changes (if enabled)');
|
|
371
|
+
console.log(' • Use inspect.* commands to explore your application');
|
|
372
|
+
console.log(chalk.blue('\n📚 Examples:'));
|
|
373
|
+
console.log(' al`entity User { id String @id, name String }`');
|
|
374
|
+
console.log(' inst("User", {id: "123", name: "Alice"})');
|
|
375
|
+
console.log(' inspect.entities()');
|
|
376
|
+
console.log(' inspect.instances(MyApp/EntityName)');
|
|
377
|
+
console.log(' m.active()');
|
|
378
|
+
/* eslint-enable no-console */
|
|
379
|
+
return '';
|
|
380
|
+
},
|
|
381
|
+
clear: () => {
|
|
382
|
+
// eslint-disable-next-line no-console
|
|
383
|
+
console.log('\x1b[2J\x1b[0f');
|
|
384
|
+
return '';
|
|
385
|
+
},
|
|
386
|
+
restart: async () => {
|
|
387
|
+
// eslint-disable-next-line no-console
|
|
388
|
+
console.log(chalk.yellow('🔄 Restarting REPL...'));
|
|
389
|
+
await restartRepl();
|
|
390
|
+
return '';
|
|
391
|
+
},
|
|
392
|
+
exit: () => {
|
|
393
|
+
// eslint-disable-next-line no-console
|
|
394
|
+
console.log(chalk.yellow('\n👋 Goodbye!'));
|
|
395
|
+
cleanup();
|
|
396
|
+
process.exit(0);
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
return {
|
|
400
|
+
al,
|
|
401
|
+
ag: al, // Alias
|
|
402
|
+
e,
|
|
403
|
+
entity: e, // Alias
|
|
404
|
+
r,
|
|
405
|
+
record: r, // Alias
|
|
406
|
+
ev,
|
|
407
|
+
event: ev, // Alias
|
|
408
|
+
rel,
|
|
409
|
+
relationship: rel, // Alias
|
|
410
|
+
w,
|
|
411
|
+
workflow: w, // Alias
|
|
412
|
+
inst,
|
|
413
|
+
instance: inst, // Alias
|
|
414
|
+
m,
|
|
415
|
+
modules: m, // Alias
|
|
416
|
+
inspect,
|
|
417
|
+
utils,
|
|
418
|
+
// Direct access to runtime functions
|
|
419
|
+
addEntity,
|
|
420
|
+
removeEntity,
|
|
421
|
+
getEntity,
|
|
422
|
+
addRecord,
|
|
423
|
+
removeRecord,
|
|
424
|
+
getRecord,
|
|
425
|
+
addEvent,
|
|
426
|
+
removeEvent,
|
|
427
|
+
getEvent,
|
|
428
|
+
addRelationship,
|
|
429
|
+
removeRelationship,
|
|
430
|
+
getRelationship,
|
|
431
|
+
addWorkflow,
|
|
432
|
+
removeWorkflow,
|
|
433
|
+
getWorkflow,
|
|
434
|
+
processAgentlang,
|
|
435
|
+
parseAndEvaluateStatement,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
// Setup file watcher for app directory
|
|
439
|
+
function setupFileWatcher(appDir, options) {
|
|
440
|
+
const debounceMs = options.debounceMs || 1000;
|
|
441
|
+
let restartTimeout;
|
|
442
|
+
let isWatcherReady = false;
|
|
443
|
+
const debouncedRestart = () => {
|
|
444
|
+
// Only restart if watcher is ready and not in initial startup phase
|
|
445
|
+
if (!isWatcherReady || !replState || replState.isRestarting || replState.isInitializing)
|
|
446
|
+
return;
|
|
447
|
+
if (restartTimeout)
|
|
448
|
+
clearTimeout(restartTimeout);
|
|
449
|
+
restartTimeout = setTimeout(() => {
|
|
450
|
+
if (!(replState === null || replState === void 0 ? void 0 : replState.isRestarting) && !(replState === null || replState === void 0 ? void 0 : replState.isInitializing)) {
|
|
451
|
+
void restartRepl();
|
|
452
|
+
}
|
|
453
|
+
}, debounceMs);
|
|
454
|
+
};
|
|
455
|
+
const watcher = chokidar.watch([path.join(appDir, '**/*.al'), path.join(appDir, '**/*.json'), path.join(appDir, 'app.config.json')], {
|
|
456
|
+
ignored: ['**/node_modules/**', '**/.git/**', '**/out/**', '**/dist/**'],
|
|
457
|
+
persistent: true,
|
|
458
|
+
ignoreInitial: true, // Ignore initial add events for existing files
|
|
459
|
+
});
|
|
460
|
+
watcher
|
|
461
|
+
.on('ready', () => {
|
|
462
|
+
// Mark watcher as ready after initial scan is complete
|
|
463
|
+
isWatcherReady = true;
|
|
464
|
+
})
|
|
465
|
+
.on('change', filePath => {
|
|
466
|
+
if (!options.quiet && isWatcherReady) {
|
|
467
|
+
// eslint-disable-next-line no-console
|
|
468
|
+
console.log(chalk.blue(`\n📁 File changed: ${path.relative(appDir, filePath)}`));
|
|
469
|
+
}
|
|
470
|
+
debouncedRestart();
|
|
471
|
+
})
|
|
472
|
+
.on('add', filePath => {
|
|
473
|
+
if (!options.quiet && isWatcherReady) {
|
|
474
|
+
// eslint-disable-next-line no-console
|
|
475
|
+
console.log(chalk.green(`\n📁 File added: ${path.relative(appDir, filePath)}`));
|
|
476
|
+
}
|
|
477
|
+
debouncedRestart();
|
|
478
|
+
})
|
|
479
|
+
.on('unlink', filePath => {
|
|
480
|
+
if (!options.quiet && isWatcherReady) {
|
|
481
|
+
// eslint-disable-next-line no-console
|
|
482
|
+
console.log(chalk.red(`\n📁 File removed: ${path.relative(appDir, filePath)}`));
|
|
483
|
+
}
|
|
484
|
+
debouncedRestart();
|
|
485
|
+
})
|
|
486
|
+
.on('error', (error) => {
|
|
487
|
+
// eslint-disable-next-line no-console
|
|
488
|
+
console.error(chalk.red(`Watcher error: ${String(error)}`));
|
|
489
|
+
});
|
|
490
|
+
return watcher;
|
|
491
|
+
}
|
|
492
|
+
// Restart REPL functionality
|
|
493
|
+
async function restartRepl() {
|
|
494
|
+
if (!replState || replState.isRestarting)
|
|
495
|
+
return;
|
|
496
|
+
replState.isRestarting = true;
|
|
497
|
+
try {
|
|
498
|
+
// eslint-disable-next-line no-console
|
|
499
|
+
console.log(chalk.yellow('\n🔄 Restarting AgentLang REPL...'));
|
|
500
|
+
// Reload the application
|
|
501
|
+
if (replState.appDir) {
|
|
502
|
+
await loadApplication(replState.appDir);
|
|
503
|
+
}
|
|
504
|
+
// eslint-disable-next-line no-console
|
|
505
|
+
console.log(chalk.green('✅ REPL restarted successfully'));
|
|
506
|
+
// eslint-disable-next-line no-console
|
|
507
|
+
console.log(chalk.blue('💬 Ready for input\n'));
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
// eslint-disable-next-line no-console
|
|
511
|
+
console.error(chalk.red(`❌ Failed to restart: ${String(error)}`));
|
|
512
|
+
}
|
|
513
|
+
finally {
|
|
514
|
+
replState.isRestarting = false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Load AgentLang application
|
|
518
|
+
async function loadApplication(appDir) {
|
|
519
|
+
if (!replState)
|
|
520
|
+
return;
|
|
521
|
+
// Load configuration if available
|
|
522
|
+
try {
|
|
523
|
+
const configPath = path.join(appDir, 'app.config.json');
|
|
524
|
+
const rawConfig = (await loadRawConfig(configPath));
|
|
525
|
+
replState.config = setAppConfig(rawConfig);
|
|
526
|
+
// eslint-disable-next-line no-console
|
|
527
|
+
console.log(chalk.blue(`📋 Loaded config from ${configPath}`));
|
|
528
|
+
}
|
|
529
|
+
catch (_a) {
|
|
530
|
+
// Config is optional
|
|
531
|
+
if (!replState.options.quiet) {
|
|
532
|
+
// eslint-disable-next-line no-console
|
|
533
|
+
console.log(chalk.yellow('⚠️ No app.config.json found, using defaults'));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Load the application
|
|
537
|
+
// eslint-disable-next-line no-console
|
|
538
|
+
console.log(chalk.blue(`📂 Loading application from: ${appDir}`));
|
|
539
|
+
await load(appDir, undefined, async (appSpec) => {
|
|
540
|
+
if (replState) {
|
|
541
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
542
|
+
replState.appSpec = appSpec;
|
|
543
|
+
if (appSpec && 'name' in appSpec) {
|
|
544
|
+
// eslint-disable-next-line no-console
|
|
545
|
+
console.log(chalk.green(`✅ Loaded application: ${appSpec.name}`));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
await runPostInitTasks(appSpec, replState === null || replState === void 0 ? void 0 : replState.config);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
// Setup signal handlers
|
|
552
|
+
function setupSignalHandlers() {
|
|
553
|
+
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
554
|
+
signals.forEach(signal => {
|
|
555
|
+
process.on(signal, () => {
|
|
556
|
+
// eslint-disable-next-line no-console
|
|
557
|
+
console.log(chalk.yellow(`\n\n🛑 Received ${signal}, shutting down gracefully...`));
|
|
558
|
+
cleanup();
|
|
559
|
+
process.exit(0);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
// Cleanup function
|
|
564
|
+
function cleanup() {
|
|
565
|
+
if (replState) {
|
|
566
|
+
// Close readline interface
|
|
567
|
+
replState.rl.close();
|
|
568
|
+
// Stop file watcher
|
|
569
|
+
if (replState.watcher) {
|
|
570
|
+
void replState.watcher.close();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Main REPL function
|
|
575
|
+
export async function startRepl(appDir = '.', options = {}) {
|
|
576
|
+
// eslint-disable-next-line no-console
|
|
577
|
+
console.log(chalk.blue.bold('🚀 Starting AgentLang REPL...\n'));
|
|
578
|
+
// Setup signal handlers
|
|
579
|
+
setupSignalHandlers();
|
|
580
|
+
// Resolve app directory
|
|
581
|
+
const resolvedAppDir = path.resolve(process.cwd(), appDir);
|
|
582
|
+
// Initialize REPL state
|
|
583
|
+
replState = {
|
|
584
|
+
appDir: resolvedAppDir,
|
|
585
|
+
options,
|
|
586
|
+
rl: readline.createInterface({
|
|
587
|
+
input: process.stdin,
|
|
588
|
+
output: process.stdout,
|
|
589
|
+
prompt: chalk.cyan('agentlang> '),
|
|
590
|
+
completer: (line) => {
|
|
591
|
+
const completions = [
|
|
592
|
+
'help',
|
|
593
|
+
'?',
|
|
594
|
+
'exit',
|
|
595
|
+
'quit',
|
|
596
|
+
'clear',
|
|
597
|
+
'restart',
|
|
598
|
+
'al`',
|
|
599
|
+
'al(',
|
|
600
|
+
'e(',
|
|
601
|
+
'r(',
|
|
602
|
+
'ev(',
|
|
603
|
+
'rel(',
|
|
604
|
+
'w(',
|
|
605
|
+
'inst(',
|
|
606
|
+
'm.list()',
|
|
607
|
+
'm.active()',
|
|
608
|
+
'm.get(',
|
|
609
|
+
'm.add(',
|
|
610
|
+
'm.remove(',
|
|
611
|
+
'inspect.modules()',
|
|
612
|
+
'inspect.entities()',
|
|
613
|
+
'inspect.events()',
|
|
614
|
+
'inspect.relationships(',
|
|
615
|
+
'inspect.instances(',
|
|
616
|
+
'utils.help()',
|
|
617
|
+
'utils.clear()',
|
|
618
|
+
'utils.restart()',
|
|
619
|
+
'utils.exit()',
|
|
620
|
+
'addEntity(',
|
|
621
|
+
'removeEntity(',
|
|
622
|
+
'getEntity(',
|
|
623
|
+
'addRecord(',
|
|
624
|
+
'removeRecord(',
|
|
625
|
+
'getRecord(',
|
|
626
|
+
'updateRecord(',
|
|
627
|
+
'queryRecords(',
|
|
628
|
+
'deleteRecord(',
|
|
629
|
+
'listRecords(',
|
|
630
|
+
'countRecords(',
|
|
631
|
+
'existsRecord(',
|
|
632
|
+
'findRecord(',
|
|
633
|
+
'findRecords(',
|
|
634
|
+
'createRecord(',
|
|
635
|
+
'upsertRecord(',
|
|
636
|
+
'bulkInsert(',
|
|
637
|
+
'bulkUpdate(',
|
|
638
|
+
'bulkDelete(',
|
|
639
|
+
'transaction(',
|
|
640
|
+
'parseAndEvaluateStatement(',
|
|
641
|
+
'processAgentlang(',
|
|
642
|
+
];
|
|
643
|
+
const hits = completions.filter(c => c.startsWith(line));
|
|
644
|
+
return [hits.length ? hits : completions, line];
|
|
645
|
+
},
|
|
646
|
+
}),
|
|
647
|
+
isRestarting: false,
|
|
648
|
+
isInitializing: true,
|
|
649
|
+
};
|
|
650
|
+
try {
|
|
651
|
+
// Initialize AgentLang runtime
|
|
652
|
+
const success = await runPreInitTasks();
|
|
653
|
+
if (!success) {
|
|
654
|
+
throw new Error('Failed to initialize runtime');
|
|
655
|
+
}
|
|
656
|
+
// Load the application if directory is specified
|
|
657
|
+
if (appDir && appDir.trim() && appDir !== '.') {
|
|
658
|
+
await loadApplication(resolvedAppDir);
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
// Try to load from current directory
|
|
662
|
+
try {
|
|
663
|
+
await loadApplication(process.cwd());
|
|
664
|
+
}
|
|
665
|
+
catch (_a) {
|
|
666
|
+
// eslint-disable-next-line no-console
|
|
667
|
+
console.log(chalk.blue('📂 Starting REPL without loading an application'));
|
|
668
|
+
await runPostInitTasks();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// eslint-disable-next-line no-console
|
|
672
|
+
console.log(chalk.green('✅ AgentLang runtime initialized'));
|
|
673
|
+
// Setup file watcher AFTER initial load to prevent immediate restart
|
|
674
|
+
if (options.watch && appDir !== '') {
|
|
675
|
+
// Give the initial load time to complete before starting watcher
|
|
676
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
677
|
+
replState.watcher = setupFileWatcher(resolvedAppDir, options);
|
|
678
|
+
// eslint-disable-next-line no-console
|
|
679
|
+
console.log(chalk.green('👀 File watching enabled'));
|
|
680
|
+
}
|
|
681
|
+
// Mark initialization as complete
|
|
682
|
+
replState.isInitializing = false;
|
|
683
|
+
// Give any async startup messages time to complete
|
|
684
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
685
|
+
// eslint-disable-next-line no-console
|
|
686
|
+
console.log(chalk.blue('💬 REPL ready - type "help" for help'));
|
|
687
|
+
// eslint-disable-next-line no-console
|
|
688
|
+
console.log(); // Extra newline for clean prompt appearance
|
|
689
|
+
// Create and expose helper functions globally
|
|
690
|
+
const helpers = createReplHelpers();
|
|
691
|
+
Object.assign(global, helpers);
|
|
692
|
+
// Start REPL loop
|
|
693
|
+
replState.rl.prompt();
|
|
694
|
+
replState.rl.on('line', (input) => {
|
|
695
|
+
void (async () => {
|
|
696
|
+
const trimmed = input.trim();
|
|
697
|
+
if (!trimmed) {
|
|
698
|
+
replState === null || replState === void 0 ? void 0 : replState.rl.prompt();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
// Handle special commands without parentheses
|
|
703
|
+
if (trimmed === 'help' || trimmed === '?') {
|
|
704
|
+
helpers.utils.help();
|
|
705
|
+
replState === null || replState === void 0 ? void 0 : replState.rl.prompt();
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (trimmed === 'exit' || trimmed === 'quit') {
|
|
709
|
+
helpers.utils.exit();
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (trimmed === 'clear') {
|
|
713
|
+
helpers.utils.clear();
|
|
714
|
+
replState === null || replState === void 0 ? void 0 : replState.rl.prompt();
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (trimmed === 'restart') {
|
|
718
|
+
await helpers.utils.restart();
|
|
719
|
+
replState === null || replState === void 0 ? void 0 : replState.rl.prompt();
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
// Evaluate the input in the global context with helpers
|
|
723
|
+
const result = (await eval(trimmed));
|
|
724
|
+
// Handle promises
|
|
725
|
+
if (result && typeof result.then === 'function') {
|
|
726
|
+
try {
|
|
727
|
+
const resolved = await result;
|
|
728
|
+
if (resolved !== undefined && resolved !== '') {
|
|
729
|
+
// eslint-disable-next-line no-console
|
|
730
|
+
console.log(chalk.green('→'), resolved);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
// eslint-disable-next-line no-console
|
|
735
|
+
console.error(chalk.red('Promise rejected:'), error);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
else if (result !== undefined && result !== '') {
|
|
739
|
+
// eslint-disable-next-line no-console
|
|
740
|
+
console.log(chalk.green('→'), result);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
// eslint-disable-next-line no-console
|
|
745
|
+
console.error(chalk.red('Error:'), error);
|
|
746
|
+
}
|
|
747
|
+
replState === null || replState === void 0 ? void 0 : replState.rl.prompt();
|
|
748
|
+
})();
|
|
749
|
+
});
|
|
750
|
+
replState.rl.on('close', () => {
|
|
751
|
+
cleanup();
|
|
752
|
+
process.exit(0);
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
// eslint-disable-next-line no-console
|
|
757
|
+
console.error(chalk.red('❌ Failed to start REPL:'));
|
|
758
|
+
if (error instanceof Error) {
|
|
759
|
+
const nodeError = error;
|
|
760
|
+
if (nodeError.code === 'ENOENT') {
|
|
761
|
+
// eslint-disable-next-line no-console
|
|
762
|
+
console.error(chalk.red('File or directory not found:'), nodeError.path || 'unknown path');
|
|
763
|
+
// eslint-disable-next-line no-console
|
|
764
|
+
console.error(chalk.yellow('💡 Tip: Make sure the directory exists and contains a valid AgentLang application'));
|
|
765
|
+
}
|
|
766
|
+
else if (error.message.includes('app.config.json') || error.message.includes('package.json')) {
|
|
767
|
+
// eslint-disable-next-line no-console
|
|
768
|
+
console.error(chalk.red('Could not find required configuration files in the specified directory'));
|
|
769
|
+
// eslint-disable-next-line no-console
|
|
770
|
+
console.error(chalk.yellow('💡 Tip: Make sure you are pointing to a valid AgentLang application directory'));
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
// eslint-disable-next-line no-console
|
|
774
|
+
console.error(chalk.red('Error:'), error.message);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// eslint-disable-next-line no-console
|
|
779
|
+
console.error(chalk.red('Unknown error:'), error);
|
|
780
|
+
}
|
|
781
|
+
cleanup();
|
|
782
|
+
throw error;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
//# sourceMappingURL=repl.js.map
|