agentlang 0.7.2 → 0.7.4

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.
Files changed (71) hide show
  1. package/out/api/http.d.ts.map +1 -1
  2. package/out/api/http.js +63 -1
  3. package/out/api/http.js.map +1 -1
  4. package/out/cli/main.d.ts.map +1 -1
  5. package/out/cli/main.js +10 -3
  6. package/out/cli/main.js.map +1 -1
  7. package/out/runtime/defs.d.ts +2 -0
  8. package/out/runtime/defs.d.ts.map +1 -1
  9. package/out/runtime/defs.js +11 -4
  10. package/out/runtime/defs.js.map +1 -1
  11. package/out/runtime/interpreter.js +3 -3
  12. package/out/runtime/interpreter.js.map +1 -1
  13. package/out/runtime/loader.d.ts +0 -1
  14. package/out/runtime/loader.d.ts.map +1 -1
  15. package/out/runtime/loader.js +61 -34
  16. package/out/runtime/loader.js.map +1 -1
  17. package/out/runtime/logger.d.ts +2 -0
  18. package/out/runtime/logger.d.ts.map +1 -1
  19. package/out/runtime/logger.js +46 -24
  20. package/out/runtime/logger.js.map +1 -1
  21. package/out/runtime/module.d.ts +3 -2
  22. package/out/runtime/module.d.ts.map +1 -1
  23. package/out/runtime/module.js +7 -3
  24. package/out/runtime/module.js.map +1 -1
  25. package/out/runtime/modules/core.d.ts +2 -1
  26. package/out/runtime/modules/core.d.ts.map +1 -1
  27. package/out/runtime/modules/core.js +69 -9
  28. package/out/runtime/modules/core.js.map +1 -1
  29. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  30. package/out/runtime/resolvers/interface.js +49 -8
  31. package/out/runtime/resolvers/interface.js.map +1 -1
  32. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  33. package/out/runtime/resolvers/sqldb/database.js +8 -4
  34. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  35. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  36. package/out/runtime/resolvers/sqldb/impl.js +3 -2
  37. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  38. package/out/runtime/state.d.ts +13 -0
  39. package/out/runtime/state.d.ts.map +1 -1
  40. package/out/runtime/state.js +5 -0
  41. package/out/runtime/state.js.map +1 -1
  42. package/out/runtime/util.d.ts +2 -0
  43. package/out/runtime/util.d.ts.map +1 -1
  44. package/out/runtime/util.js +11 -0
  45. package/out/runtime/util.js.map +1 -1
  46. package/package.json +185 -186
  47. package/src/api/http.ts +72 -0
  48. package/src/cli/main.ts +12 -1
  49. package/src/runtime/defs.ts +9 -0
  50. package/src/runtime/interpreter.ts +3 -3
  51. package/src/runtime/loader.ts +60 -31
  52. package/src/runtime/logger.ts +52 -27
  53. package/src/runtime/module.ts +12 -3
  54. package/src/runtime/modules/core.ts +78 -9
  55. package/src/runtime/resolvers/interface.ts +55 -7
  56. package/src/runtime/resolvers/sqldb/database.ts +9 -2
  57. package/src/runtime/resolvers/sqldb/impl.ts +3 -10
  58. package/src/runtime/state.ts +5 -0
  59. package/src/runtime/util.ts +13 -0
  60. package/out/setupClassic.d.ts +0 -98
  61. package/out/setupClassic.d.ts.map +0 -1
  62. package/out/setupClassic.js +0 -38
  63. package/out/setupClassic.js.map +0 -1
  64. package/out/setupCommon.d.ts +0 -2
  65. package/out/setupCommon.d.ts.map +0 -1
  66. package/out/setupCommon.js +0 -33
  67. package/out/setupCommon.js.map +0 -1
  68. package/out/setupExtended.d.ts +0 -40
  69. package/out/setupExtended.d.ts.map +0 -1
  70. package/out/setupExtended.js +0 -67
  71. package/out/setupExtended.js.map +0 -1
@@ -77,6 +77,7 @@ import {
77
77
  isFqName,
78
78
  makeFqName,
79
79
  maybeExtends,
80
+ objectAsString,
80
81
  preprocessRawConfig,
81
82
  registerInitFunction,
82
83
  rootRef,
@@ -342,6 +343,40 @@ export async function flushAllAndLoad(
342
343
  return await load(fileName, fsOptions, callback);
343
344
  }
344
345
 
346
+ async function evaluateConfigPatterns(cfgPats: string): Promise<any> {
347
+ const cfgWf = `workflow createConfig{\n${cfgPats}}`;
348
+ const wf = await parseWorkflow(cfgWf);
349
+ const cfgStmts = new Array<Statement>();
350
+ const initInsts = new Array<Statement>();
351
+ for (let i = 0; i < wf.statements.length; ++i) {
352
+ const stmt: Statement = wf.statements[i];
353
+ if (stmt.pattern.crudMap) {
354
+ initInsts.push(await makeDeleteAllConfigStatement(stmt.pattern.crudMap));
355
+ initInsts.push(stmt);
356
+ } else {
357
+ cfgStmts.push(stmt);
358
+ }
359
+ }
360
+ if (initInsts.length > 0) {
361
+ registerInitFunction(async () => {
362
+ const env = new Environment('config.insts.env');
363
+ try {
364
+ await evaluateStatements(initInsts, env);
365
+ await env.commitAllTransactions();
366
+ } catch (reason: any) {
367
+ await env.rollbackAllTransactions();
368
+ console.error(`Failed to initialize config instances: ${reason}`);
369
+ }
370
+ });
371
+ }
372
+ if (cfgStmts.length > 0) {
373
+ const env = new Environment('config.env');
374
+ await evaluateStatements(cfgStmts, env);
375
+ return env.getLastResult();
376
+ }
377
+ return undefined;
378
+ }
379
+
345
380
  export async function loadAppConfig(configDir: string): Promise<Config> {
346
381
  let cfgObj: any = undefined;
347
382
  const fs = await getFileSystem();
@@ -349,39 +384,12 @@ export async function loadAppConfig(configDir: string): Promise<Config> {
349
384
  if (await fs.exists(alCfgFile)) {
350
385
  const cfgPats = await fs.readFile(alCfgFile);
351
386
  if (canParse(cfgPats)) {
352
- const cfgWf = `workflow createConfig{\n${cfgPats}}`;
353
- const wf = await parseWorkflow(cfgWf);
354
- const env = new Environment('config.env');
355
- const cfgStmts = new Array<Statement>();
356
- const initInsts = new Array<Statement>();
357
- for (let i = 0; i < wf.statements.length; ++i) {
358
- const stmt: Statement = wf.statements[i];
359
- if (stmt.pattern.crudMap) {
360
- initInsts.push(await makeDeleteAllConfigStatement(stmt.pattern.crudMap));
361
- initInsts.push(stmt);
362
- } else {
363
- cfgStmts.push(stmt);
364
- }
365
- }
366
- if (initInsts.length > 0) {
367
- registerInitFunction(async () => {
368
- const env = new Environment('config.insts.env');
369
- try {
370
- await evaluateStatements(initInsts, env);
371
- await env.commitAllTransactions();
372
- } catch (reason: any) {
373
- await env.rollbackAllTransactions();
374
- console.error(`Failed to initialize config instances: ${reason}`);
375
- }
376
- });
377
- }
378
- await evaluateStatements(cfgStmts, env);
379
- cfgObj = env.getLastResult();
387
+ cfgObj = await evaluateConfigPatterns(cfgPats);
380
388
  }
381
389
  }
382
390
  try {
383
391
  const cfg = cfgObj
384
- ? configFromObject(cfgObj)
392
+ ? await configFromObject(cfgObj)
385
393
  : await loadRawConfig(`${configDir}${path.sep}app.config.json`);
386
394
  return setAppConfig(cfg);
387
395
  } catch (err: any) {
@@ -1178,10 +1186,31 @@ export async function loadRawConfig(
1178
1186
  }
1179
1187
  }
1180
1188
 
1181
- export function configFromObject(cfgObj: any, validate: boolean = true): any {
1189
+ function filterConfigEntityInstances(rawConfig: any): [any, Map<string, any>] {
1190
+ const cfg = new Map<string, any>();
1191
+ const insts = new Map<string, any>();
1192
+ Object.entries(rawConfig).forEach(([key, value]: [string, any]) => {
1193
+ if (isFqName(key)) {
1194
+ insts.set(key, value);
1195
+ } else {
1196
+ cfg.set(key, value);
1197
+ }
1198
+ });
1199
+ return [Object.fromEntries(cfg), insts];
1200
+ }
1201
+
1202
+ async function configFromObject(cfgObj: any, validate: boolean = true): Promise<any> {
1182
1203
  const rawConfig = preprocessRawConfig(cfgObj);
1183
1204
  if (validate) {
1184
- return ConfigSchema.parse(rawConfig);
1205
+ const [cfg, insts] = filterConfigEntityInstances(rawConfig);
1206
+ const pats = new Array<string>();
1207
+ insts.forEach((v: any, k: string) => {
1208
+ pats.push(`{${k} ${objectAsString(v)}}`);
1209
+ });
1210
+ if (pats.length > 0) {
1211
+ await evaluateConfigPatterns(pats.join('\n'));
1212
+ }
1213
+ return ConfigSchema.parse(cfg);
1185
1214
  }
1186
1215
  return rawConfig;
1187
1216
  }
@@ -1,4 +1,5 @@
1
1
  import { isNodeEnv } from '../utils/runtime.js';
2
+ import { AppConfig } from './state.js';
2
3
 
3
4
  let DailyRotateFile: any;
4
5
  let winston: any;
@@ -16,34 +17,58 @@ if (isNodeEnv) {
16
17
 
17
18
  export let logger: any;
18
19
 
19
- if (isNodeEnv) {
20
- const fileTransport = new DailyRotateFile({
21
- level: 'debug',
22
- filename: 'logs/app-%DATE%.log',
23
- datePattern: 'YYYY-MM-DD',
24
- maxSize: '20m',
25
- maxFiles: '7d',
26
- });
20
+ function getLogLevel(): string {
21
+ if (isNodeEnv && process.env && process.env.DEBUG) {
22
+ return 'debug';
23
+ }
24
+ return AppConfig?.logging?.level || 'info';
25
+ }
27
26
 
28
- logger = winston.createLogger({
29
- format: winston.format.combine(
30
- winston.format.timestamp(),
31
- winston.format.printf(({ timestamp, level, message }: any) => {
32
- return `[${timestamp}] ${level}: ${message}`;
33
- })
34
- ),
35
- transports: [fileTransport],
36
- });
37
- } else {
38
- function mkLogger(tag: string): Function {
39
- return (msg: string) => {
40
- console.log(`${tag}: ${msg}`);
27
+ export function initializeLogger() {
28
+ const logLevel = getLogLevel();
29
+
30
+ if (isNodeEnv) {
31
+ const fileTransport = new DailyRotateFile({
32
+ level: logLevel,
33
+ filename: 'logs/app-%DATE%.log',
34
+ datePattern: 'YYYY-MM-DD',
35
+ maxSize: '20m',
36
+ maxFiles: '7d',
37
+ });
38
+
39
+ logger = winston.createLogger({
40
+ level: logLevel,
41
+ format: winston.format.combine(
42
+ winston.format.timestamp(),
43
+ winston.format.printf(({ timestamp, level, message }: any) => {
44
+ return `[${timestamp}] ${level}: ${message}`;
45
+ })
46
+ ),
47
+ transports: [fileTransport],
48
+ });
49
+ } else {
50
+ function mkLogger(tag: string): Function {
51
+ return (msg: string) => {
52
+ console.log(`${tag}: ${msg}`);
53
+ };
54
+ }
55
+ logger = {
56
+ debug: mkLogger('DEBUG'),
57
+ info: mkLogger('INFO'),
58
+ warn: mkLogger('WARN'),
59
+ error: mkLogger('ERROR'),
41
60
  };
42
61
  }
43
- logger = {
44
- debug: mkLogger('DEBUG'),
45
- info: mkLogger('INFO'),
46
- warn: mkLogger('WARN'),
47
- error: mkLogger('ERROR'),
48
- };
62
+ }
63
+
64
+ initializeLogger();
65
+
66
+ export function updateLoggerFromConfig() {
67
+ if (isNodeEnv && logger) {
68
+ const logLevel = getLogLevel();
69
+ logger.level = logLevel;
70
+ if (logger.transports && logger.transports.length > 0) {
71
+ logger.transports[0].level = logLevel;
72
+ }
73
+ }
49
74
  }
@@ -1248,7 +1248,7 @@ export class Relationship extends Record {
1248
1248
 
1249
1249
  constructor(
1250
1250
  name: string,
1251
- typ: string,
1251
+ type: string,
1252
1252
  node1: RelationshipNode,
1253
1253
  node2: RelationshipNode,
1254
1254
  moduleName: string,
@@ -1256,7 +1256,7 @@ export class Relationship extends Record {
1256
1256
  props?: Map<string, any>
1257
1257
  ) {
1258
1258
  super(name, moduleName, scm);
1259
- if (typ == 'between') {
1259
+ if (type == 'between') {
1260
1260
  this.relType = RelType.BETWEEN;
1261
1261
  this.addMetaAttributes();
1262
1262
  }
@@ -3999,7 +3999,7 @@ export function getAttributeNames(entityFqName: string): Array<string> {
3999
3999
  return [...scm.keys()];
4000
4000
  }
4001
4001
 
4002
- export function maybeSetMetaAttributes(
4002
+ export function setMetaAttributes(
4003
4003
  attrs: InstanceAttributes,
4004
4004
  env: Environment,
4005
4005
  inUpdateMode: boolean = false
@@ -4010,3 +4010,12 @@ export function maybeSetMetaAttributes(
4010
4010
  attrs.set(SysAttr_CreatedBy, user);
4011
4011
  }
4012
4012
  }
4013
+
4014
+ export function setAllMetaAttributes(
4015
+ attrs: InstanceAttributes,
4016
+ env: Environment,
4017
+ inUpdateMode: boolean = false
4018
+ ) {
4019
+ attrs.set(SysAttr_Created, now());
4020
+ setMetaAttributes(attrs, env, inUpdateMode);
4021
+ }
@@ -28,14 +28,16 @@ import {
28
28
  import { logger } from '../logger.js';
29
29
  import { Statement } from '../../language/generated/ast.js';
30
30
  import { parseModule, parseStatements } from '../../language/parser.js';
31
- import { Resolver } from '../resolvers/interface.js';
31
+ import { GenericResolver, Resolver } from '../resolvers/interface.js';
32
32
  import {
33
33
  FlowSuspensionTag,
34
34
  ForceReadPermFlag,
35
35
  InternDynamicModule,
36
+ isRuntimeMode_dev,
36
37
  PathAttributeName,
37
38
  } from '../defs.js';
38
39
  import { getMonitor, getMonitorsForEvent, Monitor } from '../monitor.js';
40
+ import { registerResolver, setResolver } from '../resolvers/registry.js';
39
41
 
40
42
  const CoreModuleDefinition = `module ${DefaultModuleName}
41
43
 
@@ -146,11 +148,12 @@ entity Module {
146
148
  definition String
147
149
  }
148
150
 
149
- resolver moduleResolver [agentlang/Module] {
150
- create Core.createModule,
151
- update Core.updateModule,
152
- delete Core.deleteModule,
153
- query Core.getModule
151
+ entity PersistentModule extends Module {
152
+ }
153
+
154
+ workflow savePersistentModule {
155
+ purge {PersistentModule {name? savePersistentModule.name}}
156
+ {PersistentModule {name savePersistentModule.name, definition savePersistentModule.definition}}
154
157
  }
155
158
 
156
159
  entity Migration {
@@ -464,7 +467,21 @@ export async function internModuleHelper(
464
467
  }
465
468
 
466
469
  export async function createModule(_: Resolver, inst: Instance) {
467
- await internModuleHelper(inst.lookup('name'), inst.lookup('definition'));
470
+ const n = inst.lookup('name');
471
+ const d = inst.lookup('definition');
472
+ const env = new Environment('module-env');
473
+ try {
474
+ await parseAndEvaluateStatement(
475
+ `{agentlang/savePersistentModule {name "${n}", definition "${d}"}}`,
476
+ undefined,
477
+ env
478
+ );
479
+ await env.commitAllTransactions();
480
+ } catch (reason: any) {
481
+ await env.rollbackAllTransactions();
482
+ logger.error(`Failed to persist module ${n} - ${reason}`);
483
+ }
484
+ await internModuleHelper(n, d);
468
485
  return inst;
469
486
  }
470
487
 
@@ -473,7 +490,20 @@ export async function updateModule(r: Resolver, inst: Instance) {
473
490
  }
474
491
 
475
492
  export async function deleteModule(_: Resolver, inst: Instance) {
476
- removeModule(inst.lookup('name'));
493
+ const n = inst.lookup('name');
494
+ const env = new Environment('module-env');
495
+ try {
496
+ await parseAndEvaluateStatement(
497
+ `purge {agentlang/PersistentModule {name? "${n}"}}`,
498
+ undefined,
499
+ env
500
+ );
501
+ await env.commitAllTransactions();
502
+ } catch (reason: any) {
503
+ await env.rollbackAllTransactions();
504
+ logger.error(`Failed to purge persistent module ${n} - ${reason}`);
505
+ }
506
+ removeModule(n);
477
507
  return inst;
478
508
  }
479
509
 
@@ -489,7 +519,46 @@ export async function getModule(_: Resolver, inst: Instance) {
489
519
  return [makeInstance('agentlang', 'Module', attrs)];
490
520
  }
491
521
  }
492
- return null;
522
+ return [];
523
+ }
524
+
525
+ async function internPersistentModules() {
526
+ try {
527
+ const insts: Instance[] = await parseAndEvaluateStatement(`{agentlang/PersistentModule? {}}`);
528
+ for (let i = 0; i < insts.length; ++i) {
529
+ const inst = insts[i];
530
+ const n = inst.lookup('name');
531
+ if (!isModule(n)) await internModuleHelper(n, inst.lookup('definition'));
532
+ }
533
+ } catch (reason: any) {
534
+ logger.warn(`Failed to intern persistent modules: ${reason}`);
535
+ }
536
+ }
537
+
538
+ export function initCoreModuleManager() {
539
+ const ModuleResolverName = 'agentlang/moduleResolver';
540
+ const ModuleResolver = new GenericResolver(ModuleResolverName, {
541
+ create: createModule,
542
+ upsert: createModule,
543
+ update: updateModule,
544
+ query: getModule,
545
+ delete: deleteModule,
546
+ startTransaction: undefined,
547
+ commitTransaction: undefined,
548
+ rollbackTransaction: undefined,
549
+ });
550
+
551
+ registerResolver(ModuleResolverName, () => {
552
+ return ModuleResolver;
553
+ });
554
+
555
+ setResolver('agentlang/Module', ModuleResolverName);
556
+
557
+ if (isRuntimeMode_dev()) {
558
+ setInterval(() => {
559
+ internPersistentModules();
560
+ }, 10000);
561
+ }
493
562
  }
494
563
 
495
564
  const SqlSep = ';\n\n';
@@ -14,7 +14,7 @@ import {
14
14
  newInstanceAttributes,
15
15
  Relationship,
16
16
  } from '../module.js';
17
- import { CrudType, nameToPath } from '../util.js';
17
+ import { CrudType, nameToPath, generateLoggerCallId } from '../util.js';
18
18
  import { DefaultAuthInfo, ResolverAuthInfo } from './authinfo.js';
19
19
 
20
20
  export type JoinInfo = {
@@ -272,11 +272,22 @@ export class GenericResolver extends Resolver {
272
272
  }
273
273
 
274
274
  public override async createInstance(inst: Instance): Promise<any> {
275
+ const callId = generateLoggerCallId();
276
+ let attrVals;
277
+ if (inst.attributes) {
278
+ attrVals = JSON.stringify(Object.fromEntries(inst.attributes));
279
+ }
280
+ logger.debug(
281
+ `${callId}: Resolver createInstance called for ${inst.moduleName + '/' + inst.name} with values ${attrVals}`
282
+ );
283
+ let result;
275
284
  if (this.implementation?.create) {
276
- return await this.implementation.create(this, inst);
285
+ result = await this.implementation.create(this, inst);
277
286
  } else {
278
- return await super.createInstance(inst);
287
+ result = await super.createInstance(inst);
279
288
  }
289
+ logger.debug(`${callId}: Resolver createInstance response: ${JSON.stringify(result)}`);
290
+ return result;
280
291
  }
281
292
 
282
293
  public override async upsertInstance(inst: Instance): Promise<any> {
@@ -287,20 +298,57 @@ export class GenericResolver extends Resolver {
287
298
  }
288
299
 
289
300
  public override async updateInstance(inst: Instance, newAttrs: InstanceAttributes): Promise<any> {
301
+ const callId = generateLoggerCallId();
302
+
303
+ const newAttrsVals = JSON.stringify(Object.fromEntries(newAttrs));
304
+ logger.debug(
305
+ `${callId} Resolver updateInstance called for ${inst.moduleName + '/' + inst.name} with values ${newAttrsVals}`
306
+ );
307
+ if (inst.queryAttributes && inst.queryAttributeValues) {
308
+ const qattr = JSON.stringify(Object.fromEntries(inst.queryAttributes));
309
+ const qattrValues = JSON.stringify(Object.fromEntries(inst.queryAttributeValues));
310
+ logger.debug(`${callId}: Query attributes: ${qattr}, values ${qattrValues}`);
311
+ }
312
+
313
+ let result;
290
314
  if (this.implementation?.update) {
291
- return await this.implementation.update(this, inst, newAttrs);
315
+ result = await this.implementation.update(this, inst, newAttrs);
316
+ } else {
317
+ result = await super.updateInstance(inst, newAttrs);
292
318
  }
293
- return await super.updateInstance(inst, newAttrs);
319
+ logger.debug(`${callId}: Resolver updateInstance response: ${JSON.stringify(result)}`);
320
+ return result;
294
321
  }
295
322
 
296
323
  public override async queryInstances(inst: Instance, queryAll: boolean): Promise<any> {
324
+ const callId = generateLoggerCallId();
325
+ logger.debug(
326
+ `${callId}: Resolver queryInstances called for ${inst.moduleName + '/' + inst.name}`
327
+ );
328
+ if (inst.queryAttributes && inst.queryAttributeValues) {
329
+ const qattr = JSON.stringify(Object.fromEntries(inst.queryAttributes));
330
+ const qattrValues = JSON.stringify(Object.fromEntries(inst.queryAttributeValues));
331
+ logger.debug(`${callId}: Query attributes: ${qattr}, values ${qattrValues}`);
332
+ }
333
+ let result;
297
334
  if (this.implementation?.query) {
298
- return await this.implementation.query(this, inst, queryAll);
335
+ result = await this.implementation.query(this, inst, queryAll);
336
+ } else {
337
+ result = await super.queryInstances(inst, queryAll);
299
338
  }
300
- return await super.queryInstances(inst, queryAll);
339
+ logger.debug(`${callId}: Resolver queryInstances response: ${JSON.stringify(result)}`);
340
+ return result;
301
341
  }
302
342
 
303
343
  public override async deleteInstance(inst: Instance | Instance[], purge: boolean): Promise<any> {
344
+ if (inst instanceof Instance) {
345
+ if (inst.queryAttributes)
346
+ logger.debug(`Resolver deleteInstance called for ${inst.moduleName + '/' + inst.name}`);
347
+ } else {
348
+ logger.debug(
349
+ `Resolver deleteInstance called for ${inst.map(i => i.moduleName + '/' + i.name).join(', ')}`
350
+ );
351
+ }
304
352
  if (this.implementation?.delete) {
305
353
  return await this.implementation.delete(this, inst, purge);
306
354
  }
@@ -24,6 +24,7 @@ import {
24
24
  RbacPermissionFlag,
25
25
  RbacSpecification,
26
26
  Relationship,
27
+ setAllMetaAttributes,
27
28
  } from '../../module.js';
28
29
  import { isString } from '../../util.js';
29
30
  import {
@@ -33,6 +34,7 @@ import {
33
34
  isRuntimeMode_generate_migration,
34
35
  isRuntimeMode_init_schema,
35
36
  isRuntimeMode_migration,
37
+ isRuntimeMode_test,
36
38
  isRuntimeMode_undo_migration,
37
39
  PathAttributeName,
38
40
  UnauthorisedError,
@@ -170,11 +172,15 @@ function mkDbName(): string {
170
172
  return process.env.AGENTLANG_DB_NAME || `db-${Date.now()}`;
171
173
  }
172
174
 
175
+ function needSync(): boolean {
176
+ return isRuntimeMode_dev() || isRuntimeMode_test() || isRuntimeMode_init_schema();
177
+ }
178
+
173
179
  function makePostgresDataSource(
174
180
  entities: EntitySchema[],
175
181
  config: DatabaseConfig | undefined
176
182
  ): DataSource {
177
- const synchronize = isRuntimeMode_dev() || isRuntimeMode_init_schema();
183
+ const synchronize = needSync();
178
184
  //const runMigrations = isRuntimeMode_migration() || isRuntimeMode_undo_migration() || !synchronize;
179
185
  return new DataSource({
180
186
  type: 'postgres',
@@ -207,7 +213,7 @@ function makeSqliteDataSource(
207
213
  entities: EntitySchema[],
208
214
  config: DatabaseConfig | undefined
209
215
  ): DataSource {
210
- const synchronize = isRuntimeMode_dev() || isRuntimeMode_init_schema();
216
+ const synchronize = needSync();
211
217
  //const runMigrations = isRuntimeMode_migration() || isRuntimeMode_undo_migration() || !synchronize;
212
218
  return new DataSource({
213
219
  type: 'sqlite',
@@ -646,6 +652,7 @@ export async function insertBetweenRow(
646
652
  if (relEntry.isOneToMany()) {
647
653
  attrs.set(relEntry.joinNodesAttributeName(), `${p1}_${p2}`);
648
654
  }
655
+ setAllMetaAttributes(attrs, ctx.activeEnv);
649
656
  const row = Object.fromEntries(attrs);
650
657
  await insertRow(n, row, ctx.clone().setNeedAuthCheck(false), false);
651
658
  } else {
@@ -435,6 +435,7 @@ export class SqlDbResolver extends Resolver {
435
435
  const a1: string = relEntry.node1.alias;
436
436
  const a2: string = relEntry.node2.alias;
437
437
  const n1path: any = orUpdate ? firstNode.lookup(PathAttributeName) : undefined;
438
+ const ctx = this.getDbContext(relEntry.getFqName());
438
439
  if (relEntry.isOneToOne()) {
439
440
  await this.updateInstance(
440
441
  node1,
@@ -452,18 +453,10 @@ export class SqlDbResolver extends Resolver {
452
453
  [a1, n1path],
453
454
  [a2, secondNode.lookup(PathAttributeName)],
454
455
  ],
455
- this.getDbContext(relEntry.getFqName())
456
+ ctx
456
457
  );
457
458
  }
458
- await insertBetweenRow(
459
- n,
460
- a1,
461
- a2,
462
- firstNode,
463
- secondNode,
464
- relEntry,
465
- this.getDbContext(relEntry.getFqName())
466
- );
459
+ await insertBetweenRow(n, a1, a2, firstNode, secondNode, relEntry, ctx);
467
460
  }
468
461
  }
469
462
 
@@ -111,6 +111,11 @@ export const ConfigSchema = z.object({
111
111
  })
112
112
  )
113
113
  .optional(),
114
+ logging: z
115
+ .object({
116
+ level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
117
+ })
118
+ .optional(),
114
119
  });
115
120
 
116
121
  export type Config = z.infer<typeof ConfigSchema>;
@@ -612,6 +612,10 @@ export function generateUrlSafePassword(length: number = 8): string {
612
612
  return chars.join('');
613
613
  }
614
614
 
615
+ export function generateLoggerCallId() {
616
+ return Math.floor(10000 + Math.random() * 90000);
617
+ }
618
+
615
619
  const JS_PREFIX = '#js';
616
620
 
617
621
  export function preprocessRawConfig(rawConfig: any): any {
@@ -637,3 +641,12 @@ declare global {
637
641
  export function setScecretReader(f: ReadSecret) {
638
642
  globalThis.readSecret = f;
639
643
  }
644
+
645
+ export function objectAsString(obj: any) {
646
+ const entries = new Array<string>();
647
+ Object.entries(obj).forEach(([k, v]) => {
648
+ const vv = typeof v === 'string' ? `"${v}"` : v;
649
+ entries.push(`${k}: ${vv}`);
650
+ });
651
+ return `{${entries.join(', ')}}`;
652
+ }