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/README.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
<div align="center">
|
|
3
2
|
|
|
4
3
|
<p>
|
|
@@ -25,18 +24,20 @@
|
|
|
25
24
|
<a href="https://github.com/agentlang-ai/agentlang/tree/main/example"><img src="https://img.shields.io/badge/Examples-Page-yellow?logo=homepage&logoColor=yellow&style=for-the-badge"></a>
|
|
26
25
|
|
|
27
26
|
[](https://nodejs.org) [](https://github.com/agentlang-ai/agentlang/actions/workflows/ci.yml) [](https://www.npmjs.com/package/agentlang)
|
|
27
|
+
|
|
28
28
|
<hr>
|
|
29
29
|
|
|
30
30
|
## Agentlang - Team as Code
|
|
31
|
+
|
|
31
32
|
</div>
|
|
32
33
|
|
|
33
34
|
Agentlang is a declarative DSL (built on TypeScript) for creating AI Agents and full-stack Agentic Apps. With Agentlang, you define, version, run, mentor, and monitor teams of AI agents, along with the app infrastructure they need: data model, workflows, RBAC, integrations, and UI. We refer to this approach - bringing together AI agent development and App development into a single coherent discipline - as Team-as-Code (our riff on IaC).
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
- **For Devs and Non-Devs:** Code and vibe-code in your IDE, focusing on the business-logic of your app, not wiring. Alternatively, you can build, run, mentor and monitor your AI Team in Studio - our visual-builder (coming soon). Switch back-and-forth between the two modes seamlessly.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
- **Robust Integrations:** The Agentlang runtime ships with native integrations for LLMs, databases, vector DBs, and auth providers. Our connector architecture is built for the enterprise, with a rapidly growing catalog for systems like Salesforce, ServiceNow, HubSpot, Snowflake, and more. Also, because Agentlang compiles to Node.js (and runs in the browser), you can use any existing JavaScript library out of the box.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
- **Production-grade**: Under the hood, it’s all modern TypeScript—strong typing, tooling, testing, and CI/CD-friendly workflows—built for enterprise-class reliability, governance, and scale.
|
|
40
41
|
|
|
41
42
|
Agentlang introduces two foundational innovations: [Agentic Reliability Modeling](#-agentic-reliability-modeling) and [AgentLang Ontology](#agentlang-ontology)
|
|
42
43
|
|
|
@@ -132,7 +133,6 @@ workflow ticketInProgress {
|
|
|
132
133
|
}
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
|
|
136
136
|
### ✨ First-class AI Agents
|
|
137
137
|
|
|
138
138
|
Agents and many concepts agents use are built-in language constructs.
|
|
@@ -328,8 +328,8 @@ What makes this model special is how seamlessly an agent can interact with it
|
|
|
328
328
|
|
|
329
329
|
To get started with Agentlang Ontology, please see the [Agentlang Tutorial](https://docs.fractl.io/app) or explore the following example applications:
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
- [Car Dealership](https://github.com/agentlang-ai/agentlang/tree/main/example/car_dealership)
|
|
332
|
+
- [Customer Support System](https://github.com/agentlang-ai/agentlang/tree/main/example/customer_support_system)
|
|
333
333
|
|
|
334
334
|
## 🚀 Getting Started
|
|
335
335
|
|
|
@@ -363,14 +363,7 @@ For contributors who want to build and develop Agentlang itself:
|
|
|
363
363
|
```shell
|
|
364
364
|
# Install dependencies
|
|
365
365
|
npm install
|
|
366
|
-
|
|
367
|
-
OR
|
|
368
|
-
|
|
369
|
-
# Install pnpm: https://pnpm.io/installation
|
|
370
|
-
# Use pnpm
|
|
371
|
-
pnpm install
|
|
372
366
|
```
|
|
373
|
-
**Note**: If pnpm shows build script warnings, run `pnpm approve-builds` and approve esbuild and sqlite3.
|
|
374
367
|
|
|
375
368
|
### ⚡ Build
|
|
376
369
|
|
package/out/api/http.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { InstanceAttributes } from '../runtime/module.js';
|
|
1
3
|
import { ApplicationSpec } from '../runtime/loader.js';
|
|
2
4
|
import { Config } from '../runtime/state.js';
|
|
5
|
+
export declare function createApp(appSpec: ApplicationSpec, config?: Config): Promise<Express>;
|
|
3
6
|
export declare function startServer(appSpec: ApplicationSpec, port: number, host?: string, config?: Config): Promise<void>;
|
|
7
|
+
export declare function patternFromAttributes(moduleName: string, recName: string, attrs: InstanceAttributes, entityFqName?: string): string;
|
|
4
8
|
//# sourceMappingURL=http.d.ts.map
|
package/out/api/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAE9D,OAAO,EAKL,kBAAkB,EAcnB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AA8BvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAc7C,wBAAsB,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqU3F;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,iBAyBhB;AAuDD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,kBAAkB,EACzB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CA8BR"}
|
package/out/api/http.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import { getAllChildRelationships, getAllEntityNames, getAllEventNames, Instance, isBetweenRelationship, makeInstance, objectAsInstanceAttributes, fetchModule, getModuleNames, Record, fetchRefTarget, getAttributeNames, getAllBetweenRelationshipNames, linkInstancesEvent, } from '../runtime/module.js';
|
|
4
|
+
import { getAllChildRelationships, getAllEntityNames, getAllEventNames, Instance, isBetweenRelationship, getRelationship, makeInstance, objectAsInstanceAttributes, fetchModule, getModuleNames, Record, fetchRefTarget, getAttributeNames, getAllBetweenRelationshipNames, linkInstancesEvent, } from '../runtime/module.js';
|
|
5
5
|
import { isNodeEnv } from '../utils/runtime.js';
|
|
6
6
|
import { parseAndEvaluateStatement } from '../runtime/interpreter.js';
|
|
7
7
|
import { logger } from '../runtime/logger.js';
|
|
@@ -11,7 +11,11 @@ import { escapeFqName, forceAsEscapedName, forceAsFqName, isString, isStringNume
|
|
|
11
11
|
import { BadRequestError, isPathAttribute, PathAttributeNameQuery, setEntityEndpointsUpdater, setEventEndpointsUpdater, setRelationshipEndpointsUpdater, UnauthorisedError, } from '../runtime/defs.js';
|
|
12
12
|
import { evaluate } from '../runtime/interpreter.js';
|
|
13
13
|
import { findFileByFilename, createFileRecord, deleteFileRecord, } from '../runtime/modules/files.js';
|
|
14
|
-
|
|
14
|
+
import { getOAuthAuthorizeUrl, exchangeOAuthCode, getIntegrationAccessToken, } from '../runtime/integration-client.js';
|
|
15
|
+
import * as XLSX from 'xlsx';
|
|
16
|
+
import { objectToQueryPattern } from '../language/parser.js';
|
|
17
|
+
export async function createApp(appSpec, config) {
|
|
18
|
+
var _a;
|
|
15
19
|
const app = express();
|
|
16
20
|
app.use(express.json());
|
|
17
21
|
// Add CORS middleware
|
|
@@ -123,6 +127,85 @@ export async function startServer(appSpec, port, host, config) {
|
|
|
123
127
|
app.get('/meta', (req, res) => {
|
|
124
128
|
handleMetaGet(req, res);
|
|
125
129
|
});
|
|
130
|
+
app.post('/agentlang/QueryAsCsv', (req, res) => {
|
|
131
|
+
handleQueryAsCsvPost(req, res);
|
|
132
|
+
});
|
|
133
|
+
app.post('/agentlang/QueryAsXlsx', (req, res) => {
|
|
134
|
+
handleQueryAsXlsxPost(req, res);
|
|
135
|
+
});
|
|
136
|
+
// --- Built-in OAuth proxy ---
|
|
137
|
+
if ((_a = config === null || config === void 0 ? void 0 : config.integrations) === null || _a === void 0 ? void 0 : _a.oauth) {
|
|
138
|
+
const connections = config.integrations.connections;
|
|
139
|
+
// Resolve provider key (e.g. 'google_drive') to integration entity name
|
|
140
|
+
// (e.g. 'google-drive') by extracting from the config path.
|
|
141
|
+
function resolveIntegrationName(provider) {
|
|
142
|
+
const entry = connections[provider];
|
|
143
|
+
if (!entry)
|
|
144
|
+
return provider;
|
|
145
|
+
const configPath = typeof entry === 'string' ? entry : entry.config;
|
|
146
|
+
if (!configPath)
|
|
147
|
+
return provider;
|
|
148
|
+
return configPath.split('/')[0];
|
|
149
|
+
}
|
|
150
|
+
app.get('/agentlang/oauth/authorize-url', async (req, res) => {
|
|
151
|
+
try {
|
|
152
|
+
const provider = req.query.provider;
|
|
153
|
+
const redirectUri = req.query.redirectUri;
|
|
154
|
+
if (!provider || !redirectUri) {
|
|
155
|
+
res.status(400).json({ error: 'provider and redirectUri query params are required' });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!connections[provider]) {
|
|
159
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const result = await getOAuthAuthorizeUrl(resolveIntegrationName(provider), redirectUri);
|
|
163
|
+
res.json(result);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
logger.error(`OAuth authorize-url error: ${err}`);
|
|
167
|
+
res.status(500).json({ error: err.message || 'Failed to get authorization URL' });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
app.post('/agentlang/oauth/exchange', async (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
const { provider, code, state } = req.body;
|
|
173
|
+
if (!provider || !code || !state) {
|
|
174
|
+
res.status(400).json({ error: 'provider, code, and state are required in request body' });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!connections[provider]) {
|
|
178
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const result = await exchangeOAuthCode(resolveIntegrationName(provider), code, state);
|
|
182
|
+
res.json(result);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
logger.error(`OAuth exchange error: ${err}`);
|
|
186
|
+
res.status(500).json({ error: err.message || 'Failed to exchange authorization code' });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
app.get('/agentlang/oauth/access-token', async (req, res) => {
|
|
190
|
+
try {
|
|
191
|
+
const provider = req.query.provider;
|
|
192
|
+
if (!provider) {
|
|
193
|
+
res.status(400).json({ error: 'provider query param is required' });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!connections[provider]) {
|
|
197
|
+
res.status(400).json({ error: `Unknown provider: ${provider}` });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const result = await getIntegrationAccessToken(resolveIntegrationName(provider));
|
|
201
|
+
res.json(result);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
logger.error(`OAuth access-token error: ${err}`);
|
|
205
|
+
res.status(500).json({ error: err.message || 'Failed to get access token' });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
126
209
|
if (isNodeEnv && upload && uploadDir) {
|
|
127
210
|
app.post('/uploadFile', upload.single('file'), (req, res) => {
|
|
128
211
|
handleFileUpload(req, res, config);
|
|
@@ -201,15 +284,6 @@ export async function startServer(appSpec, port, host, config) {
|
|
|
201
284
|
addBetweenHandlers(moduleName, n);
|
|
202
285
|
});
|
|
203
286
|
});
|
|
204
|
-
const cb = () => {
|
|
205
|
-
console.log(chalk.green(`Application ${chalk.bold(appName + ' version ' + appVersion)} started on port ${chalk.bold(port)}`));
|
|
206
|
-
};
|
|
207
|
-
if (host) {
|
|
208
|
-
app.listen(port, host, cb);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
app.listen(port, cb);
|
|
212
|
-
}
|
|
213
287
|
setEventEndpointsUpdater((moduleName) => {
|
|
214
288
|
const m = fetchModule(moduleName);
|
|
215
289
|
const eventNames = m.getEventNames();
|
|
@@ -231,6 +305,27 @@ export async function startServer(appSpec, port, host, config) {
|
|
|
231
305
|
addBetweenHandlers(moduleName, n);
|
|
232
306
|
});
|
|
233
307
|
});
|
|
308
|
+
return app;
|
|
309
|
+
}
|
|
310
|
+
export async function startServer(appSpec, port, host, config) {
|
|
311
|
+
const app = await createApp(appSpec, config);
|
|
312
|
+
const appName = appSpec.name;
|
|
313
|
+
const appVersion = appSpec.version;
|
|
314
|
+
// Expose port and host on globalThis so resolver code (e.g. integration-manager's
|
|
315
|
+
// auth-resolver) can make self-referencing HTTP calls to the correct address.
|
|
316
|
+
globalThis.__agentlang_port = port;
|
|
317
|
+
if (host) {
|
|
318
|
+
globalThis.__agentlang_host = host;
|
|
319
|
+
}
|
|
320
|
+
const cb = () => {
|
|
321
|
+
console.log(chalk.green(`Application ${chalk.bold(appName + ' version ' + appVersion)} started on port ${chalk.bold(port)}`));
|
|
322
|
+
};
|
|
323
|
+
if (host) {
|
|
324
|
+
app.listen(port, host, cb);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
app.listen(port, cb);
|
|
328
|
+
}
|
|
234
329
|
}
|
|
235
330
|
function ok(res) {
|
|
236
331
|
return (value) => {
|
|
@@ -256,19 +351,62 @@ function internalError(res) {
|
|
|
256
351
|
res.status(statusFromErrorType(reason)).send(reason.message);
|
|
257
352
|
};
|
|
258
353
|
}
|
|
259
|
-
function
|
|
354
|
+
function formatAttrValue(v, n) {
|
|
355
|
+
let av = isString(v) ? `"${v}"` : v;
|
|
356
|
+
if (av instanceof Object) {
|
|
357
|
+
av = JSON.stringify(av);
|
|
358
|
+
}
|
|
359
|
+
if (isPathAttribute(n)) {
|
|
360
|
+
av = escapeSepInPath(av);
|
|
361
|
+
}
|
|
362
|
+
return `${n} ${av}`;
|
|
363
|
+
}
|
|
364
|
+
function buildChildPattern(otherFqName, childObj) {
|
|
365
|
+
const childAttrs = Object.entries(childObj)
|
|
366
|
+
.map(([k, v]) => {
|
|
367
|
+
let av;
|
|
368
|
+
if (isString(v)) {
|
|
369
|
+
av = `"${v}"`;
|
|
370
|
+
}
|
|
371
|
+
else if (v instanceof Object) {
|
|
372
|
+
av = JSON.stringify(v);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
av = v;
|
|
376
|
+
}
|
|
377
|
+
return `${k} ${av}`;
|
|
378
|
+
})
|
|
379
|
+
.join(', ');
|
|
380
|
+
return `{${otherFqName} {${childAttrs}}, @upsert}`;
|
|
381
|
+
}
|
|
382
|
+
export function patternFromAttributes(moduleName, recName, attrs, entityFqName) {
|
|
260
383
|
const attrsStrs = new Array();
|
|
384
|
+
const relPatterns = new Array();
|
|
261
385
|
attrs.forEach((v, n) => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
386
|
+
if (entityFqName) {
|
|
387
|
+
try {
|
|
388
|
+
if (isBetweenRelationship(n, moduleName)) {
|
|
389
|
+
const rel = getRelationship(n, moduleName);
|
|
390
|
+
const otherFqName = rel.getParentFqName() === entityFqName ? rel.getChildFqName() : rel.getParentFqName();
|
|
391
|
+
const children = Array.isArray(v) ? v : [v];
|
|
392
|
+
const childPatterns = children
|
|
393
|
+
.map((child) => buildChildPattern(otherFqName, child))
|
|
394
|
+
.join(', ');
|
|
395
|
+
relPatterns.push(`${n} [${childPatterns}]`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch (_a) {
|
|
400
|
+
// Not a relationship — fall through to normal attribute handling
|
|
401
|
+
}
|
|
268
402
|
}
|
|
269
|
-
attrsStrs.push(
|
|
403
|
+
attrsStrs.push(formatAttrValue(v, n));
|
|
270
404
|
});
|
|
271
|
-
|
|
405
|
+
const entityPat = `{${moduleName}/${recName} {${attrsStrs.join(',\n')}}`;
|
|
406
|
+
if (relPatterns.length > 0) {
|
|
407
|
+
return `${entityPat}, ${relPatterns.join(', ')}}`;
|
|
408
|
+
}
|
|
409
|
+
return `${entityPat}}`;
|
|
272
410
|
}
|
|
273
411
|
function normalizeRequestPath(path, moduleName) {
|
|
274
412
|
if (path.length <= 1) {
|
|
@@ -318,7 +456,7 @@ async function handleEventPost(moduleName, eventName, req, res) {
|
|
|
318
456
|
return;
|
|
319
457
|
}
|
|
320
458
|
const inst = makeInstance(moduleName, eventName, objectAsInstanceAttributes(req.body)).setAuthContext(sessionInfo);
|
|
321
|
-
evaluate(inst
|
|
459
|
+
evaluate(inst).then(ok(res)).catch(internalError(res));
|
|
322
460
|
}
|
|
323
461
|
catch (err) {
|
|
324
462
|
logger.error(err);
|
|
@@ -332,9 +470,10 @@ async function handleEntityPost(moduleName, entityName, req, res) {
|
|
|
332
470
|
res.status(401).send('Authorization required');
|
|
333
471
|
return;
|
|
334
472
|
}
|
|
473
|
+
const entityFqName = makeFqName(moduleName, entityName);
|
|
335
474
|
const pattern = req.params.path
|
|
336
475
|
? createChildPattern(moduleName, entityName, req)
|
|
337
|
-
: patternFromAttributes(moduleName, entityName, objectAsInstanceAttributes(req.body));
|
|
476
|
+
: patternFromAttributes(moduleName, entityName, objectAsInstanceAttributes(req.body), entityFqName);
|
|
338
477
|
parseAndEvaluateStatement(pattern, sessionInfo.userId).then(ok(res)).catch(internalError(res));
|
|
339
478
|
}
|
|
340
479
|
catch (err) {
|
|
@@ -368,16 +507,24 @@ const joinTags = new Map()
|
|
|
368
507
|
.set('@joinOn', '@join')
|
|
369
508
|
.set('@leftJoinOn', '@left_join')
|
|
370
509
|
.set('@rightJoinOn', '@right_join');
|
|
510
|
+
const paginationTags = new Set(['@limit', '@offset']);
|
|
371
511
|
function objectAsAttributesPattern(entityFqName, obj) {
|
|
372
512
|
const attrs = new Array();
|
|
373
513
|
let joinType;
|
|
374
514
|
let joinOnAttr;
|
|
515
|
+
const paginationParts = new Array();
|
|
375
516
|
Object.keys(obj).forEach(key => {
|
|
376
517
|
const s = obj[key];
|
|
377
518
|
if (joinTags.has(key)) {
|
|
378
519
|
joinType = joinTags.get(key);
|
|
379
520
|
joinOnAttr = s;
|
|
380
521
|
}
|
|
522
|
+
else if (paginationTags.has(key)) {
|
|
523
|
+
const n = parseInt(s, 10);
|
|
524
|
+
if (!Number.isNaN(n) && Number.isInteger(n) && n >= 0) {
|
|
525
|
+
paginationParts.push(`${key}(${n})`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
381
528
|
else {
|
|
382
529
|
let v = s;
|
|
383
530
|
if (!s.startsWith('"')) {
|
|
@@ -405,13 +552,19 @@ function objectAsAttributesPattern(entityFqName, obj) {
|
|
|
405
552
|
});
|
|
406
553
|
const intoPat = `@into {${intoSpec.join(', ')}}`;
|
|
407
554
|
joinOnAttr = reverseJoin ? splitRefs(joinOnAttr)[1] : joinOnAttr;
|
|
555
|
+
const paginationStr = paginationParts.length > 0 ? `,\n${paginationParts.join(',\n')}` : '';
|
|
408
556
|
return [
|
|
409
|
-
`${pat},\n${joinType} ${targetEntity} {${targetAttr}? ${entityFqName}.${joinOnAttr}}, \n${intoPat}`,
|
|
557
|
+
`${pat},\n${joinType} ${targetEntity} {${targetAttr}? ${entityFqName}.${joinOnAttr}}, \n${intoPat}${paginationStr}`,
|
|
410
558
|
hasQueryAttrs,
|
|
559
|
+
'',
|
|
411
560
|
];
|
|
412
561
|
}
|
|
413
562
|
else {
|
|
414
|
-
return [
|
|
563
|
+
return [
|
|
564
|
+
pat,
|
|
565
|
+
hasQueryAttrs,
|
|
566
|
+
paginationParts.length > 0 ? `,\n${paginationParts.join(',\n')}` : '',
|
|
567
|
+
];
|
|
415
568
|
}
|
|
416
569
|
}
|
|
417
570
|
function queryPatternFromPath(path, req) {
|
|
@@ -423,9 +576,9 @@ function queryPatternFromPath(path, req) {
|
|
|
423
576
|
const fqName = `${moduleName}/${entityName}`;
|
|
424
577
|
if (parts.length == 2 && id === undefined) {
|
|
425
578
|
if (req.query && Object.keys(req.query).length > 0) {
|
|
426
|
-
const [pat, hasQueryAttrs] = objectAsAttributesPattern(fqName, req.query);
|
|
579
|
+
const [pat, hasQueryAttrs, paginationPat] = objectAsAttributesPattern(fqName, req.query);
|
|
427
580
|
const n = hasQueryAttrs ? fqName : `${fqName}?`;
|
|
428
|
-
return `{${n} ${pat}}`;
|
|
581
|
+
return `{${n} ${pat}${paginationPat}}`;
|
|
429
582
|
}
|
|
430
583
|
else {
|
|
431
584
|
return `{${fqName}? {}}`;
|
|
@@ -578,6 +731,66 @@ function normalizedResult(r) {
|
|
|
578
731
|
return r;
|
|
579
732
|
}
|
|
580
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* Converts eval result to an array of flat row objects for CSV/XLSX.
|
|
736
|
+
* Handles:
|
|
737
|
+
* - [{ "EntityName": { "attr": "attrval" } }, ...] -> one row per item, columns = Entity + attrs
|
|
738
|
+
* - [{ "attr": "attrval" }, ...] -> one row per item, columns = attrs
|
|
739
|
+
* - [[ { "EntityName": { ... } }, ... ]] -> unwrap single outer array, then one row per inner item
|
|
740
|
+
*/
|
|
741
|
+
function evalResultToRows(data) {
|
|
742
|
+
let items;
|
|
743
|
+
if (Array.isArray(data) && data.length === 1 && Array.isArray(data[0])) {
|
|
744
|
+
items = data[0];
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
items = Array.isArray(data) ? data : [data];
|
|
748
|
+
}
|
|
749
|
+
return items.map((item) => {
|
|
750
|
+
if (item === null || typeof item !== 'object') {
|
|
751
|
+
return { value: item };
|
|
752
|
+
}
|
|
753
|
+
if (Array.isArray(item)) {
|
|
754
|
+
return { value: item };
|
|
755
|
+
}
|
|
756
|
+
const obj = item;
|
|
757
|
+
const keys = Object.keys(obj);
|
|
758
|
+
if (keys.length === 1) {
|
|
759
|
+
const [k] = keys;
|
|
760
|
+
const v = obj[k];
|
|
761
|
+
if (v !== null &&
|
|
762
|
+
typeof v === 'object' &&
|
|
763
|
+
!Array.isArray(v) &&
|
|
764
|
+
Object.prototype.toString.call(v) === '[object Object]') {
|
|
765
|
+
return Object.assign({}, v);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return Object.assign({}, obj);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Ensures cell value is stringifiable for CSV/XLSX (nested objects/arrays become JSON string).
|
|
773
|
+
*/
|
|
774
|
+
function cellValue(val) {
|
|
775
|
+
if (val === null || val === undefined)
|
|
776
|
+
return '';
|
|
777
|
+
if (typeof val === 'object')
|
|
778
|
+
return JSON.stringify(val);
|
|
779
|
+
return val;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Parses column names from the first instance and returns an array-of-arrays
|
|
783
|
+
* for the sheet: first row = column names, following rows = data. This ensures
|
|
784
|
+
* CSV/XLSX output has proper headers (col1, col2, ...) not 0, 1, 2, ...
|
|
785
|
+
*/
|
|
786
|
+
function rowsToSheetAoa(rows) {
|
|
787
|
+
if (rows.length === 0)
|
|
788
|
+
return [];
|
|
789
|
+
const columns = Object.keys(rows[0]);
|
|
790
|
+
const headerRow = columns;
|
|
791
|
+
const dataRows = rows.map(row => columns.map(col => cellValue(row[col])));
|
|
792
|
+
return [headerRow, ...dataRows];
|
|
793
|
+
}
|
|
581
794
|
async function handleMetaGet(req, res) {
|
|
582
795
|
try {
|
|
583
796
|
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
@@ -781,6 +994,74 @@ async function handleMetaGet(req, res) {
|
|
|
781
994
|
res.status(500).send(err.toString());
|
|
782
995
|
}
|
|
783
996
|
}
|
|
997
|
+
async function handleQueryAsCsvPost(req, res) {
|
|
998
|
+
var _a, _b;
|
|
999
|
+
try {
|
|
1000
|
+
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
1001
|
+
if (isNoSession(sessionInfo)) {
|
|
1002
|
+
res.status(401).send('Authorization required');
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
const q = (_a = req.body) === null || _a === void 0 ? void 0 : _a.q;
|
|
1006
|
+
if (q === undefined) {
|
|
1007
|
+
res.status(400).send('Missing or invalid pattern');
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const qs = objectToQueryPattern(q);
|
|
1011
|
+
const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
|
|
1012
|
+
const normalized = normalizedResult(result);
|
|
1013
|
+
const rows = evalResultToRows(normalized);
|
|
1014
|
+
const aoa = rowsToSheetAoa(rows);
|
|
1015
|
+
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
|
1016
|
+
const workbook = XLSX.utils.book_new();
|
|
1017
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
1018
|
+
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'csv' });
|
|
1019
|
+
const filename = `eval-${Date.now()}.csv`;
|
|
1020
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
1021
|
+
res.contentType('text/csv');
|
|
1022
|
+
res.send(buffer);
|
|
1023
|
+
}
|
|
1024
|
+
catch (err) {
|
|
1025
|
+
logger.error(err);
|
|
1026
|
+
if (!res.headersSent) {
|
|
1027
|
+
res.status(statusFromErrorType(err)).send((_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err === null || err === void 0 ? void 0 : err.toString());
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
async function handleQueryAsXlsxPost(req, res) {
|
|
1032
|
+
var _a, _b;
|
|
1033
|
+
try {
|
|
1034
|
+
const sessionInfo = await verifyAuth('', '', req.headers.authorization);
|
|
1035
|
+
if (isNoSession(sessionInfo)) {
|
|
1036
|
+
res.status(401).send('Authorization required');
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const q = (_a = req.body) === null || _a === void 0 ? void 0 : _a.q;
|
|
1040
|
+
if (q === undefined) {
|
|
1041
|
+
res.status(400).send('Missing or invalid pattern');
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const qs = objectToQueryPattern(q);
|
|
1045
|
+
const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
|
|
1046
|
+
const normalized = normalizedResult(result);
|
|
1047
|
+
const rows = evalResultToRows(normalized);
|
|
1048
|
+
const aoa = rowsToSheetAoa(rows);
|
|
1049
|
+
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
|
1050
|
+
const workbook = XLSX.utils.book_new();
|
|
1051
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
1052
|
+
const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
|
|
1053
|
+
const filename = `eval-${Date.now()}.xlsx`;
|
|
1054
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
1055
|
+
res.contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
1056
|
+
res.send(buffer);
|
|
1057
|
+
}
|
|
1058
|
+
catch (err) {
|
|
1059
|
+
logger.error(err);
|
|
1060
|
+
if (!res.headersSent) {
|
|
1061
|
+
res.status(statusFromErrorType(err)).send((_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err === null || err === void 0 ? void 0 : err.toString());
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
784
1065
|
async function handleFileUpload(req, res, config) {
|
|
785
1066
|
var _a;
|
|
786
1067
|
try {
|