agentlang 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -14
- package/out/api/http.d.ts +4 -0
- package/out/api/http.d.ts.map +1 -1
- package/out/api/http.js +307 -26
- package/out/api/http.js.map +1 -1
- package/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +3 -0
- package/out/cli/main.js.map +1 -1
- package/out/extension/main.cjs +250 -250
- package/out/extension/main.cjs.map +2 -2
- package/out/language/agentlang-validator.d.ts.map +1 -1
- package/out/language/agentlang-validator.js +4 -0
- package/out/language/agentlang-validator.js.map +1 -1
- package/out/language/error-reporter.d.ts +53 -0
- package/out/language/error-reporter.d.ts.map +1 -0
- package/out/language/error-reporter.js +879 -0
- package/out/language/error-reporter.js.map +1 -0
- package/out/language/generated/ast.d.ts +77 -2
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +60 -0
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +342 -206
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +901 -710
- package/out/language/main.cjs.map +3 -3
- package/out/language/parser.d.ts +4 -2
- package/out/language/parser.d.ts.map +1 -1
- package/out/language/parser.js +58 -99
- package/out/language/parser.js.map +1 -1
- package/out/language/syntax.d.ts +16 -0
- package/out/language/syntax.d.ts.map +1 -1
- package/out/language/syntax.js +66 -27
- package/out/language/syntax.js.map +1 -1
- package/out/runtime/api.d.ts +2 -0
- package/out/runtime/api.d.ts.map +1 -1
- package/out/runtime/api.js +25 -0
- package/out/runtime/api.js.map +1 -1
- package/out/runtime/datefns.d.ts +34 -0
- package/out/runtime/datefns.d.ts.map +1 -0
- package/out/runtime/datefns.js +82 -0
- package/out/runtime/datefns.js.map +1 -0
- package/out/runtime/defs.d.ts +1 -0
- package/out/runtime/defs.d.ts.map +1 -1
- package/out/runtime/defs.js +2 -1
- package/out/runtime/defs.js.map +1 -1
- package/out/runtime/document-retriever.d.ts +24 -0
- package/out/runtime/document-retriever.d.ts.map +1 -0
- package/out/runtime/document-retriever.js +258 -0
- package/out/runtime/document-retriever.js.map +1 -0
- package/out/runtime/embeddings/chunker.d.ts +18 -0
- package/out/runtime/embeddings/chunker.d.ts.map +1 -1
- package/out/runtime/embeddings/chunker.js +47 -15
- package/out/runtime/embeddings/chunker.js.map +1 -1
- package/out/runtime/embeddings/openai.d.ts.map +1 -1
- package/out/runtime/embeddings/openai.js +22 -9
- package/out/runtime/embeddings/openai.js.map +1 -1
- package/out/runtime/embeddings/provider.d.ts +1 -0
- package/out/runtime/embeddings/provider.d.ts.map +1 -1
- package/out/runtime/embeddings/provider.js +20 -1
- package/out/runtime/embeddings/provider.js.map +1 -1
- package/out/runtime/exec-graph.d.ts.map +1 -1
- package/out/runtime/exec-graph.js +22 -3
- package/out/runtime/exec-graph.js.map +1 -1
- package/out/runtime/integration-client.d.ts +21 -0
- package/out/runtime/integration-client.d.ts.map +1 -0
- package/out/runtime/integration-client.js +112 -0
- package/out/runtime/integration-client.js.map +1 -0
- package/out/runtime/integrations.d.ts.map +1 -1
- package/out/runtime/integrations.js +20 -9
- package/out/runtime/integrations.js.map +1 -1
- package/out/runtime/interpreter.d.ts +10 -0
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +221 -22
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +70 -7
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/logger.d.ts.map +1 -1
- package/out/runtime/logger.js +8 -1
- package/out/runtime/logger.js.map +1 -1
- package/out/runtime/module.d.ts +18 -0
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +91 -3
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +16 -5
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +286 -88
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/core.d.ts.map +1 -1
- package/out/runtime/modules/core.js +5 -1
- package/out/runtime/modules/core.js.map +1 -1
- package/out/runtime/monitor.d.ts +6 -0
- package/out/runtime/monitor.d.ts.map +1 -1
- package/out/runtime/monitor.js +21 -1
- package/out/runtime/monitor.js.map +1 -1
- package/out/runtime/relgraph.d.ts.map +1 -1
- package/out/runtime/relgraph.js +7 -3
- package/out/runtime/relgraph.js.map +1 -1
- package/out/runtime/resolvers/interface.d.ts +7 -2
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js +17 -3
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +2 -0
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +142 -126
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.js +25 -4
- package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +2 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +24 -7
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
- package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
- package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
- package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
- package/out/runtime/resolvers/vector/types.d.ts +32 -0
- package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
- package/out/runtime/resolvers/vector/types.js +2 -0
- package/out/runtime/resolvers/vector/types.js.map +1 -0
- package/out/runtime/services/documentFetcher.d.ts.map +1 -1
- package/out/runtime/services/documentFetcher.js +21 -6
- package/out/runtime/services/documentFetcher.js.map +1 -1
- package/out/runtime/state.d.ts +19 -1
- package/out/runtime/state.d.ts.map +1 -1
- package/out/runtime/state.js +36 -1
- package/out/runtime/state.js.map +1 -1
- package/out/runtime/util.d.ts +3 -2
- package/out/runtime/util.d.ts.map +1 -1
- package/out/runtime/util.js +13 -2
- package/out/runtime/util.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/out/test-harness.d.ts +36 -0
- package/out/test-harness.d.ts.map +1 -0
- package/out/test-harness.js +341 -0
- package/out/test-harness.js.map +1 -0
- package/package.json +22 -19
- package/src/api/http.ts +336 -38
- package/src/cli/main.ts +3 -0
- package/src/language/agentlang-validator.ts +3 -0
- package/src/language/agentlang.langium +6 -2
- package/src/language/error-reporter.ts +1028 -0
- package/src/language/generated/ast.ts +94 -1
- package/src/language/generated/grammar.ts +342 -206
- package/src/language/parser.ts +64 -101
- package/src/language/syntax.ts +79 -24
- package/src/runtime/api.ts +36 -0
- package/src/runtime/datefns.ts +112 -0
- package/src/runtime/defs.ts +2 -1
- package/src/runtime/document-retriever.ts +311 -0
- package/src/runtime/embeddings/chunker.ts +52 -14
- package/src/runtime/embeddings/openai.ts +27 -9
- package/src/runtime/embeddings/provider.ts +22 -1
- package/src/runtime/exec-graph.ts +23 -2
- package/src/runtime/integration-client.ts +158 -0
- package/src/runtime/integrations.ts +20 -11
- package/src/runtime/interpreter.ts +221 -15
- package/src/runtime/loader.ts +83 -5
- package/src/runtime/logger.ts +12 -1
- package/src/runtime/module.ts +104 -3
- package/src/runtime/modules/ai.ts +341 -107
- package/src/runtime/modules/core.ts +5 -1
- package/src/runtime/monitor.ts +27 -1
- package/src/runtime/relgraph.ts +7 -3
- package/src/runtime/resolvers/interface.ts +23 -3
- package/src/runtime/resolvers/sqldb/database.ts +158 -130
- package/src/runtime/resolvers/sqldb/dbutil.ts +28 -6
- package/src/runtime/resolvers/sqldb/impl.ts +25 -7
- package/src/runtime/resolvers/vector/lancedb-store.ts +187 -0
- package/src/runtime/resolvers/vector/types.ts +39 -0
- package/src/runtime/services/documentFetcher.ts +21 -6
- package/src/runtime/state.ts +40 -1
- package/src/runtime/util.ts +19 -2
- package/src/syntaxes/agentlang.monarch.ts +1 -1
- package/src/test-harness.ts +423 -0
package/src/api/http.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import express, { Request, Response } from 'express';
|
|
2
|
+
import express, { Express, Request, Response } from 'express';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import {
|
|
5
5
|
getAllChildRelationships,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Instance,
|
|
9
9
|
InstanceAttributes,
|
|
10
10
|
isBetweenRelationship,
|
|
11
|
+
getRelationship,
|
|
11
12
|
makeInstance,
|
|
12
13
|
objectAsInstanceAttributes,
|
|
13
14
|
Relationship,
|
|
@@ -58,13 +59,15 @@ import {
|
|
|
58
59
|
createFileRecord,
|
|
59
60
|
deleteFileRecord,
|
|
60
61
|
} from '../runtime/modules/files.js';
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
import {
|
|
63
|
+
getOAuthAuthorizeUrl,
|
|
64
|
+
exchangeOAuthCode,
|
|
65
|
+
getIntegrationAccessToken,
|
|
66
|
+
} from '../runtime/integration-client.js';
|
|
67
|
+
import * as XLSX from 'xlsx';
|
|
68
|
+
import { objectToQueryPattern } from '../language/parser.js';
|
|
69
|
+
|
|
70
|
+
export async function createApp(appSpec: ApplicationSpec, config?: Config): Promise<Express> {
|
|
68
71
|
const app = express();
|
|
69
72
|
app.use(express.json());
|
|
70
73
|
|
|
@@ -197,6 +200,87 @@ export async function startServer(
|
|
|
197
200
|
handleMetaGet(req, res);
|
|
198
201
|
});
|
|
199
202
|
|
|
203
|
+
app.post('/agentlang/QueryAsCsv', (req: Request, res: Response) => {
|
|
204
|
+
handleQueryAsCsvPost(req, res);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
app.post('/agentlang/QueryAsXlsx', (req: Request, res: Response) => {
|
|
208
|
+
handleQueryAsXlsxPost(req, res);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// --- Built-in OAuth proxy ---
|
|
212
|
+
if (config?.integrations?.oauth) {
|
|
213
|
+
const connections = config.integrations.connections;
|
|
214
|
+
|
|
215
|
+
// Resolve provider key (e.g. 'google_drive') to integration entity name
|
|
216
|
+
// (e.g. 'google-drive') by extracting from the config path.
|
|
217
|
+
function resolveIntegrationName(provider: string): string {
|
|
218
|
+
const entry = connections[provider];
|
|
219
|
+
if (!entry) return provider;
|
|
220
|
+
const configPath = typeof entry === 'string' ? entry : entry.config;
|
|
221
|
+
if (!configPath) return provider;
|
|
222
|
+
return configPath.split('/')[0];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
app.get('/agentlang/oauth/authorize-url', async (req: Request, res: Response) => {
|
|
226
|
+
try {
|
|
227
|
+
const provider = req.query.provider as string;
|
|
228
|
+
const redirectUri = req.query.redirectUri as string;
|
|
229
|
+
if (!provider || !redirectUri) {
|
|
230
|
+
res.status(400).json({ error: 'provider and redirectUri query params are required' });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!connections[provider]) {
|
|
234
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const result = await getOAuthAuthorizeUrl(resolveIntegrationName(provider), redirectUri);
|
|
238
|
+
res.json(result);
|
|
239
|
+
} catch (err: any) {
|
|
240
|
+
logger.error(`OAuth authorize-url error: ${err}`);
|
|
241
|
+
res.status(500).json({ error: err.message || 'Failed to get authorization URL' });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
app.post('/agentlang/oauth/exchange', async (req: Request, res: Response) => {
|
|
246
|
+
try {
|
|
247
|
+
const { provider, code, state } = req.body;
|
|
248
|
+
if (!provider || !code || !state) {
|
|
249
|
+
res.status(400).json({ error: 'provider, code, and state are required in request body' });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!connections[provider]) {
|
|
253
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const result = await exchangeOAuthCode(resolveIntegrationName(provider), code, state);
|
|
257
|
+
res.json(result);
|
|
258
|
+
} catch (err: any) {
|
|
259
|
+
logger.error(`OAuth exchange error: ${err}`);
|
|
260
|
+
res.status(500).json({ error: err.message || 'Failed to exchange authorization code' });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
app.get('/agentlang/oauth/access-token', async (req: Request, res: Response) => {
|
|
265
|
+
try {
|
|
266
|
+
const provider = req.query.provider as string;
|
|
267
|
+
if (!provider) {
|
|
268
|
+
res.status(400).json({ error: 'provider query param is required' });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!connections[provider]) {
|
|
272
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const result = await getIntegrationAccessToken(resolveIntegrationName(provider));
|
|
276
|
+
res.json(result);
|
|
277
|
+
} catch (err: any) {
|
|
278
|
+
logger.error(`OAuth access-token error: ${err}`);
|
|
279
|
+
res.status(500).json({ error: err.message || 'Failed to get access token' });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
200
284
|
if (isNodeEnv && upload && uploadDir) {
|
|
201
285
|
app.post('/uploadFile', upload.single('file'), (req: Request, res: Response) => {
|
|
202
286
|
handleFileUpload(req, res, config);
|
|
@@ -285,19 +369,6 @@ export async function startServer(
|
|
|
285
369
|
});
|
|
286
370
|
});
|
|
287
371
|
|
|
288
|
-
const cb = () => {
|
|
289
|
-
console.log(
|
|
290
|
-
chalk.green(
|
|
291
|
-
`Application ${chalk.bold(appName + ' version ' + appVersion)} started on port ${chalk.bold(port)}`
|
|
292
|
-
)
|
|
293
|
-
);
|
|
294
|
-
};
|
|
295
|
-
if (host) {
|
|
296
|
-
app.listen(port, host, cb);
|
|
297
|
-
} else {
|
|
298
|
-
app.listen(port, cb);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
372
|
setEventEndpointsUpdater((moduleName: string) => {
|
|
302
373
|
const m = fetchModule(moduleName);
|
|
303
374
|
const eventNames = m.getEventNames();
|
|
@@ -319,6 +390,39 @@ export async function startServer(
|
|
|
319
390
|
addBetweenHandlers(moduleName, n);
|
|
320
391
|
});
|
|
321
392
|
});
|
|
393
|
+
|
|
394
|
+
return app;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export async function startServer(
|
|
398
|
+
appSpec: ApplicationSpec,
|
|
399
|
+
port: number,
|
|
400
|
+
host?: string,
|
|
401
|
+
config?: Config
|
|
402
|
+
) {
|
|
403
|
+
const app = await createApp(appSpec, config);
|
|
404
|
+
const appName: string = appSpec.name;
|
|
405
|
+
const appVersion: string = appSpec.version;
|
|
406
|
+
|
|
407
|
+
// Expose port and host on globalThis so resolver code (e.g. integration-manager's
|
|
408
|
+
// auth-resolver) can make self-referencing HTTP calls to the correct address.
|
|
409
|
+
(globalThis as any).__agentlang_port = port;
|
|
410
|
+
if (host) {
|
|
411
|
+
(globalThis as any).__agentlang_host = host;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const cb = () => {
|
|
415
|
+
console.log(
|
|
416
|
+
chalk.green(
|
|
417
|
+
`Application ${chalk.bold(appName + ' version ' + appVersion)} started on port ${chalk.bold(port)}`
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
};
|
|
421
|
+
if (host) {
|
|
422
|
+
app.listen(port, host, cb);
|
|
423
|
+
} else {
|
|
424
|
+
app.listen(port, cb);
|
|
425
|
+
}
|
|
322
426
|
}
|
|
323
427
|
|
|
324
428
|
function ok(res: Response) {
|
|
@@ -346,23 +450,69 @@ function internalError(res: Response) {
|
|
|
346
450
|
};
|
|
347
451
|
}
|
|
348
452
|
|
|
349
|
-
function
|
|
453
|
+
function formatAttrValue(v: any, n: string): string {
|
|
454
|
+
let av = isString(v) ? `"${v}"` : v;
|
|
455
|
+
if (av instanceof Object) {
|
|
456
|
+
av = JSON.stringify(av);
|
|
457
|
+
}
|
|
458
|
+
if (isPathAttribute(n)) {
|
|
459
|
+
av = escapeSepInPath(av);
|
|
460
|
+
}
|
|
461
|
+
return `${n} ${av}`;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function buildChildPattern(otherFqName: string, childObj: { [key: string]: any }): string {
|
|
465
|
+
const childAttrs = Object.entries(childObj)
|
|
466
|
+
.map(([k, v]: [string, any]) => {
|
|
467
|
+
let av: any;
|
|
468
|
+
if (isString(v)) {
|
|
469
|
+
av = `"${v}"`;
|
|
470
|
+
} else if (v instanceof Object) {
|
|
471
|
+
av = JSON.stringify(v);
|
|
472
|
+
} else {
|
|
473
|
+
av = v;
|
|
474
|
+
}
|
|
475
|
+
return `${k} ${av}`;
|
|
476
|
+
})
|
|
477
|
+
.join(', ');
|
|
478
|
+
return `{${otherFqName} {${childAttrs}}, @upsert}`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function patternFromAttributes(
|
|
350
482
|
moduleName: string,
|
|
351
483
|
recName: string,
|
|
352
|
-
attrs: InstanceAttributes
|
|
484
|
+
attrs: InstanceAttributes,
|
|
485
|
+
entityFqName?: string
|
|
353
486
|
): string {
|
|
354
487
|
const attrsStrs = new Array<string>();
|
|
488
|
+
const relPatterns = new Array<string>();
|
|
489
|
+
|
|
355
490
|
attrs.forEach((v: any, n: string) => {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
491
|
+
if (entityFqName) {
|
|
492
|
+
try {
|
|
493
|
+
if (isBetweenRelationship(n, moduleName)) {
|
|
494
|
+
const rel = getRelationship(n, moduleName);
|
|
495
|
+
const otherFqName =
|
|
496
|
+
rel.getParentFqName() === entityFqName ? rel.getChildFqName() : rel.getParentFqName();
|
|
497
|
+
const children = Array.isArray(v) ? v : [v];
|
|
498
|
+
const childPatterns = children
|
|
499
|
+
.map((child: any) => buildChildPattern(otherFqName, child))
|
|
500
|
+
.join(', ');
|
|
501
|
+
relPatterns.push(`${n} [${childPatterns}]`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
} catch {
|
|
505
|
+
// Not a relationship — fall through to normal attribute handling
|
|
506
|
+
}
|
|
362
507
|
}
|
|
363
|
-
attrsStrs.push(
|
|
508
|
+
attrsStrs.push(formatAttrValue(v, n));
|
|
364
509
|
});
|
|
365
|
-
|
|
510
|
+
|
|
511
|
+
const entityPat = `{${moduleName}/${recName} {${attrsStrs.join(',\n')}}`;
|
|
512
|
+
if (relPatterns.length > 0) {
|
|
513
|
+
return `${entityPat}, ${relPatterns.join(', ')}}`;
|
|
514
|
+
}
|
|
515
|
+
return `${entityPat}}`;
|
|
366
516
|
}
|
|
367
517
|
|
|
368
518
|
function normalizeRequestPath(path: string[], moduleName: string): string[] {
|
|
@@ -423,7 +573,7 @@ async function handleEventPost(
|
|
|
423
573
|
eventName,
|
|
424
574
|
objectAsInstanceAttributes(req.body)
|
|
425
575
|
).setAuthContext(sessionInfo);
|
|
426
|
-
evaluate(inst
|
|
576
|
+
evaluate(inst).then(ok(res)).catch(internalError(res));
|
|
427
577
|
} catch (err: any) {
|
|
428
578
|
logger.error(err);
|
|
429
579
|
res.status(500).send(err.toString());
|
|
@@ -442,9 +592,15 @@ async function handleEntityPost(
|
|
|
442
592
|
res.status(401).send('Authorization required');
|
|
443
593
|
return;
|
|
444
594
|
}
|
|
595
|
+
const entityFqName = makeFqName(moduleName, entityName);
|
|
445
596
|
const pattern = req.params.path
|
|
446
597
|
? createChildPattern(moduleName, entityName, req)
|
|
447
|
-
: patternFromAttributes(
|
|
598
|
+
: patternFromAttributes(
|
|
599
|
+
moduleName,
|
|
600
|
+
entityName,
|
|
601
|
+
objectAsInstanceAttributes(req.body),
|
|
602
|
+
entityFqName
|
|
603
|
+
);
|
|
448
604
|
parseAndEvaluateStatement(pattern, sessionInfo.userId).then(ok(res)).catch(internalError(res));
|
|
449
605
|
} catch (err: any) {
|
|
450
606
|
logger.error(err);
|
|
@@ -483,15 +639,23 @@ const joinTags = new Map()
|
|
|
483
639
|
.set('@leftJoinOn', '@left_join')
|
|
484
640
|
.set('@rightJoinOn', '@right_join');
|
|
485
641
|
|
|
486
|
-
|
|
642
|
+
const paginationTags = new Set(['@limit', '@offset']);
|
|
643
|
+
|
|
644
|
+
function objectAsAttributesPattern(entityFqName: string, obj: object): [string, boolean, string] {
|
|
487
645
|
const attrs = new Array<string>();
|
|
488
646
|
let joinType: string | undefined;
|
|
489
647
|
let joinOnAttr: string | undefined;
|
|
648
|
+
const paginationParts = new Array<string>();
|
|
490
649
|
Object.keys(obj).forEach(key => {
|
|
491
650
|
const s: string = obj[key as keyof object];
|
|
492
651
|
if (joinTags.has(key)) {
|
|
493
652
|
joinType = joinTags.get(key);
|
|
494
653
|
joinOnAttr = s;
|
|
654
|
+
} else if (paginationTags.has(key)) {
|
|
655
|
+
const n = parseInt(s, 10);
|
|
656
|
+
if (!Number.isNaN(n) && Number.isInteger(n) && n >= 0) {
|
|
657
|
+
paginationParts.push(`${key}(${n})`);
|
|
658
|
+
}
|
|
495
659
|
} else {
|
|
496
660
|
let v = s;
|
|
497
661
|
if (!s.startsWith('"')) {
|
|
@@ -519,12 +683,18 @@ function objectAsAttributesPattern(entityFqName: string, obj: object): [string,
|
|
|
519
683
|
});
|
|
520
684
|
const intoPat = `@into {${intoSpec.join(', ')}}`;
|
|
521
685
|
joinOnAttr = reverseJoin ? splitRefs(joinOnAttr)[1] : joinOnAttr;
|
|
686
|
+
const paginationStr = paginationParts.length > 0 ? `,\n${paginationParts.join(',\n')}` : '';
|
|
522
687
|
return [
|
|
523
|
-
`${pat},\n${joinType} ${targetEntity} {${targetAttr}? ${entityFqName}.${joinOnAttr}}, \n${intoPat}`,
|
|
688
|
+
`${pat},\n${joinType} ${targetEntity} {${targetAttr}? ${entityFqName}.${joinOnAttr}}, \n${intoPat}${paginationStr}`,
|
|
524
689
|
hasQueryAttrs,
|
|
690
|
+
'',
|
|
525
691
|
];
|
|
526
692
|
} else {
|
|
527
|
-
return [
|
|
693
|
+
return [
|
|
694
|
+
pat,
|
|
695
|
+
hasQueryAttrs,
|
|
696
|
+
paginationParts.length > 0 ? `,\n${paginationParts.join(',\n')}` : '',
|
|
697
|
+
];
|
|
528
698
|
}
|
|
529
699
|
}
|
|
530
700
|
|
|
@@ -537,9 +707,9 @@ function queryPatternFromPath(path: string, req: Request): string {
|
|
|
537
707
|
const fqName = `${moduleName}/${entityName}`;
|
|
538
708
|
if (parts.length == 2 && id === undefined) {
|
|
539
709
|
if (req.query && Object.keys(req.query).length > 0) {
|
|
540
|
-
const [pat, hasQueryAttrs] = objectAsAttributesPattern(fqName, req.query);
|
|
710
|
+
const [pat, hasQueryAttrs, paginationPat] = objectAsAttributesPattern(fqName, req.query);
|
|
541
711
|
const n = hasQueryAttrs ? fqName : `${fqName}?`;
|
|
542
|
-
return `{${n} ${pat}}`;
|
|
712
|
+
return `{${n} ${pat}${paginationPat}}`;
|
|
543
713
|
} else {
|
|
544
714
|
return `{${fqName}? {}}`;
|
|
545
715
|
}
|
|
@@ -730,6 +900,69 @@ function normalizedResult(r: Result): Result {
|
|
|
730
900
|
}
|
|
731
901
|
}
|
|
732
902
|
|
|
903
|
+
/**
|
|
904
|
+
* Converts eval result to an array of flat row objects for CSV/XLSX.
|
|
905
|
+
* Handles:
|
|
906
|
+
* - [{ "EntityName": { "attr": "attrval" } }, ...] -> one row per item, columns = Entity + attrs
|
|
907
|
+
* - [{ "attr": "attrval" }, ...] -> one row per item, columns = attrs
|
|
908
|
+
* - [[ { "EntityName": { ... } }, ... ]] -> unwrap single outer array, then one row per inner item
|
|
909
|
+
*/
|
|
910
|
+
function evalResultToRows(data: Result): { [key: string]: unknown }[] {
|
|
911
|
+
let items: Result[];
|
|
912
|
+
if (Array.isArray(data) && data.length === 1 && Array.isArray(data[0])) {
|
|
913
|
+
items = data[0] as Result[];
|
|
914
|
+
} else {
|
|
915
|
+
items = Array.isArray(data) ? (data as Result[]) : [data];
|
|
916
|
+
}
|
|
917
|
+
return items.map((item: Result) => {
|
|
918
|
+
if (item === null || typeof item !== 'object') {
|
|
919
|
+
return { value: item };
|
|
920
|
+
}
|
|
921
|
+
if (Array.isArray(item)) {
|
|
922
|
+
return { value: item };
|
|
923
|
+
}
|
|
924
|
+
const obj = item as { [key: string]: unknown };
|
|
925
|
+
const keys = Object.keys(obj);
|
|
926
|
+
if (keys.length === 1) {
|
|
927
|
+
const [k] = keys;
|
|
928
|
+
const v = obj[k];
|
|
929
|
+
if (
|
|
930
|
+
v !== null &&
|
|
931
|
+
typeof v === 'object' &&
|
|
932
|
+
!Array.isArray(v) &&
|
|
933
|
+
Object.prototype.toString.call(v) === '[object Object]'
|
|
934
|
+
) {
|
|
935
|
+
return { ...(v as { [key: string]: unknown }) };
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return { ...obj };
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Ensures cell value is stringifiable for CSV/XLSX (nested objects/arrays become JSON string).
|
|
944
|
+
*/
|
|
945
|
+
function cellValue(val: unknown): string | number | boolean | null {
|
|
946
|
+
if (val === null || val === undefined) return '';
|
|
947
|
+
if (typeof val === 'object') return JSON.stringify(val);
|
|
948
|
+
return val as string | number | boolean;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Parses column names from the first instance and returns an array-of-arrays
|
|
953
|
+
* for the sheet: first row = column names, following rows = data. This ensures
|
|
954
|
+
* CSV/XLSX output has proper headers (col1, col2, ...) not 0, 1, 2, ...
|
|
955
|
+
*/
|
|
956
|
+
function rowsToSheetAoa(
|
|
957
|
+
rows: { [key: string]: unknown }[]
|
|
958
|
+
): (string | number | boolean | null)[][] {
|
|
959
|
+
if (rows.length === 0) return [];
|
|
960
|
+
const columns = Object.keys(rows[0] as object);
|
|
961
|
+
const headerRow = columns;
|
|
962
|
+
const dataRows = rows.map(row => columns.map(col => cellValue(row[col])));
|
|
963
|
+
return [headerRow, ...dataRows];
|
|
964
|
+
}
|
|
965
|
+
|
|
733
966
|
async function handleMetaGet(req: Request, res: Response): Promise<void> {
|
|
734
967
|
try {
|
|
735
968
|
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
@@ -957,6 +1190,71 @@ async function handleMetaGet(req: Request, res: Response): Promise<void> {
|
|
|
957
1190
|
}
|
|
958
1191
|
}
|
|
959
1192
|
|
|
1193
|
+
async function handleQueryAsCsvPost(req: Request, res: Response): Promise<void> {
|
|
1194
|
+
try {
|
|
1195
|
+
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
1196
|
+
if (isNoSession(sessionInfo)) {
|
|
1197
|
+
res.status(401).send('Authorization required');
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const q = req.body?.q;
|
|
1201
|
+
if (q === undefined) {
|
|
1202
|
+
res.status(400).send('Missing or invalid pattern');
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
const qs = objectToQueryPattern(q);
|
|
1206
|
+
const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
|
|
1207
|
+
const normalized = normalizedResult(result);
|
|
1208
|
+
const rows = evalResultToRows(normalized);
|
|
1209
|
+
const aoa = rowsToSheetAoa(rows);
|
|
1210
|
+
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
|
1211
|
+
const workbook = XLSX.utils.book_new();
|
|
1212
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
1213
|
+
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'csv' });
|
|
1214
|
+
const filename = `eval-${Date.now()}.csv`;
|
|
1215
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
1216
|
+
res.contentType('text/csv');
|
|
1217
|
+
res.send(buffer);
|
|
1218
|
+
} catch (err: any) {
|
|
1219
|
+
logger.error(err);
|
|
1220
|
+
if (!res.headersSent) {
|
|
1221
|
+
res.status(statusFromErrorType(err)).send(err?.message ?? err?.toString());
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
async function handleQueryAsXlsxPost(req: Request, res: Response): Promise<void> {
|
|
1227
|
+
try {
|
|
1228
|
+
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
1229
|
+
if (isNoSession(sessionInfo)) {
|
|
1230
|
+
res.status(401).send('Authorization required');
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
const q = req.body?.q;
|
|
1234
|
+
if (q === undefined) {
|
|
1235
|
+
res.status(400).send('Missing or invalid pattern');
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
const qs = objectToQueryPattern(q);
|
|
1239
|
+
const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
|
|
1240
|
+
const normalized = normalizedResult(result);
|
|
1241
|
+
const rows = evalResultToRows(normalized);
|
|
1242
|
+
const aoa = rowsToSheetAoa(rows);
|
|
1243
|
+
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
|
1244
|
+
const workbook = XLSX.utils.book_new();
|
|
1245
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
1246
|
+
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
|
|
1247
|
+
const filename = `eval-${Date.now()}.xlsx`;
|
|
1248
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
1249
|
+
res.contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
1250
|
+
res.send(buffer);
|
|
1251
|
+
} catch (err: any) {
|
|
1252
|
+
logger.error(err);
|
|
1253
|
+
if (!res.headersSent) {
|
|
1254
|
+
res.status(statusFromErrorType(err)).send(err?.message ?? err?.toString());
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
960
1258
|
async function handleFileUpload(
|
|
961
1259
|
req: Request & { file?: Express.Multer.File },
|
|
962
1260
|
res: Response,
|
package/src/cli/main.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { Instance, Module } from '../runtime/module.js';
|
|
|
21
21
|
import { ModuleDefinition } from '../language/generated/ast.js';
|
|
22
22
|
import { Config } from '../runtime/state.js';
|
|
23
23
|
import { prepareIntegrations } from '../runtime/integrations.js';
|
|
24
|
+
import { configureIntegrationClient } from '../runtime/integration-client.js';
|
|
24
25
|
import { isExecGraphEnabled, isNodeEnv } from '../utils/runtime.js';
|
|
25
26
|
import { OpenAPIClientAxios } from 'openapi-client-axios';
|
|
26
27
|
import { registerOpenApiModule } from '../runtime/openapi.js';
|
|
@@ -239,6 +240,8 @@ export const runModule = async (fileName: string, releaseDb: boolean = false): P
|
|
|
239
240
|
config.integrations.password,
|
|
240
241
|
config.integrations.connections
|
|
241
242
|
);
|
|
243
|
+
// Configure the thin HTTP client to talk to integration-manager for auth
|
|
244
|
+
configureIntegrationClient(config.integrations.host);
|
|
242
245
|
}
|
|
243
246
|
if (config.openapi) {
|
|
244
247
|
await loadOpenApiSpec(config.openapi);
|
|
@@ -25,6 +25,9 @@ export class AgentlangValidator {
|
|
|
25
25
|
if (def.$type === 'PublicEventDefinition') {
|
|
26
26
|
def = def.def;
|
|
27
27
|
}
|
|
28
|
+
if (!def?.schema?.attributes) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
28
31
|
def.schema.attributes.forEach((a: AttributeDefinition) => {
|
|
29
32
|
if (reported.has(a.name)) {
|
|
30
33
|
accept('error', `'${def.name} " - attribute has non-unique name '${a.name}'.`, {
|
|
@@ -115,7 +115,9 @@ Statement: pattern=Pattern hints+=RuntimeHint*;
|
|
|
115
115
|
|
|
116
116
|
IfWithAlias: '(' if=If ')';
|
|
117
117
|
|
|
118
|
-
RuntimeHint: aliasSpec=AliasSpec | catchSpec=CatchSpec | thenSpec=ThenSpec;
|
|
118
|
+
RuntimeHint: aliasSpec=AliasSpec | catchSpec=CatchSpec | emptySpec=EmptySpec | thenSpec=ThenSpec;
|
|
119
|
+
|
|
120
|
+
EmptySpec: '@empty' stmt=Statement;
|
|
119
121
|
|
|
120
122
|
AliasSpec: ('@as' (alias=ID | '[' ( aliases+=ID (',' aliases+=ID)*)+ ']'));
|
|
121
123
|
|
|
@@ -129,7 +131,7 @@ CrudMap: '{' (name=QueryId (':')? '{''}'
|
|
|
129
131
|
'}';
|
|
130
132
|
|
|
131
133
|
QueryOption: join=JoinSpec | into=SelectIntoSpec | where=WhereSpec | groupByClause=GroupByClause | orderByClause=OrderByClause
|
|
132
|
-
| upsert='@upsert' | distinct='@distinct';
|
|
134
|
+
| limitClause=LimitClause | offsetClause=OffsetClause | upsert='@upsert' | distinct='@distinct';
|
|
133
135
|
|
|
134
136
|
CrudMapBody: '{' (attributes+=SetAttribute (',' attributes+=SetAttribute)*)+ properties+=PropertyDefinition* '}';
|
|
135
137
|
|
|
@@ -146,6 +148,8 @@ WhereSpecClause: lhs=QueryId op=SqlComparisonOpr? rhs=Expr;
|
|
|
146
148
|
|
|
147
149
|
GroupByClause: '@groupBy' ('(' (colNames+=QualifiedName (',' colNames+=QualifiedName)*)+ ')');
|
|
148
150
|
OrderByClause: '@orderBy' ('(' (colNames+=QualifiedName (',' colNames+=QualifiedName)*)+ ')') (order=('@asc' | '@desc'))?;
|
|
151
|
+
LimitClause: '@limit' '(' value=INT ')';
|
|
152
|
+
OffsetClause: '@offset' '(' value=INT ')';
|
|
149
153
|
|
|
150
154
|
FullTextSearch: '{' name=QueryId query=Literal options=MapLiteral? '}';
|
|
151
155
|
|