@agentuity/cli 0.0.77 → 0.0.79
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/dist/cmd/build/ast.d.ts +2 -2
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +191 -22
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +10 -11
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +29 -31
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/build/route-registry.d.ts +2 -0
- package/dist/cmd/build/route-registry.d.ts.map +1 -1
- package/dist/cmd/build/route-registry.js +5 -1
- package/dist/cmd/build/route-registry.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +6 -2
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/legacy-check.d.ts.map +1 -1
- package/dist/legacy-check.js +2 -2
- package/dist/legacy-check.js.map +1 -1
- package/package.json +3 -3
- package/src/cmd/build/ast.ts +230 -29
- package/src/cmd/build/bundler.ts +10 -11
- package/src/cmd/build/plugin.ts +45 -41
- package/src/cmd/build/route-registry.ts +7 -1
- package/src/cmd/cloud/deploy.ts +8 -1
- package/src/legacy-check.ts +4 -2
package/src/cmd/build/ast.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as acornLoose from 'acorn-loose';
|
|
2
|
-
import { dirname, relative } from 'node:path';
|
|
2
|
+
import { dirname, relative, join, basename } from 'node:path';
|
|
3
3
|
import { parse as parseCronExpression } from '@datasert/cronjs-parser';
|
|
4
4
|
import { generate } from 'astring';
|
|
5
5
|
import type { BuildMetadata } from '../../types';
|
|
@@ -7,7 +7,7 @@ import { createLogger } from '@agentuity/server';
|
|
|
7
7
|
import * as ts from 'typescript';
|
|
8
8
|
import { StructuredError, type WorkbenchConfig } from '@agentuity/core';
|
|
9
9
|
import type { LogLevel } from '../../types';
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
12
12
|
import JSON5 from 'json5';
|
|
13
13
|
import { formatSchemaCode } from './format-schema';
|
|
@@ -48,7 +48,7 @@ interface ASTObjectExpression extends ASTNode {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
interface ASTLiteral extends ASTNode {
|
|
51
|
-
value: string;
|
|
51
|
+
value: string | number | boolean | null;
|
|
52
52
|
raw?: string;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -75,7 +75,7 @@ function parseObjectExpressionToMap(expr: ASTObjectExpression): Map<string, stri
|
|
|
75
75
|
switch (prop.value.type) {
|
|
76
76
|
case 'Literal': {
|
|
77
77
|
const value = prop.value as unknown as ASTLiteral;
|
|
78
|
-
result.set(prop.key.name, value.value);
|
|
78
|
+
result.set(prop.key.name, String(value.value));
|
|
79
79
|
break;
|
|
80
80
|
}
|
|
81
81
|
default: {
|
|
@@ -152,7 +152,7 @@ function getEvalId(
|
|
|
152
152
|
name: string,
|
|
153
153
|
version: string
|
|
154
154
|
): string {
|
|
155
|
-
return `
|
|
155
|
+
return `evalid_${hashSHA1(projectId, deploymentId, filename, name, version)}`;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
function generateRouteId(
|
|
@@ -172,7 +172,7 @@ function generateStableAgentId(projectId: string, name: string): string {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
function generateStableEvalId(projectId: string, agentId: string, name: string): string {
|
|
175
|
-
return `
|
|
175
|
+
return `eval_${hashSHA1(projectId, agentId, name)}`.substring(0, 64);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/**
|
|
@@ -255,7 +255,8 @@ function augmentAgentMetadataNode(
|
|
|
255
255
|
}
|
|
256
256
|
const name = metadata.get('name')!;
|
|
257
257
|
const descriptionNode = propvalue.properties.find((x) => x.key.name === 'description')?.value;
|
|
258
|
-
const
|
|
258
|
+
const descriptionValue = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
|
|
259
|
+
const description = typeof descriptionValue === 'string' ? descriptionValue : '';
|
|
259
260
|
const agentId = generateStableAgentId(projectId, name);
|
|
260
261
|
metadata.set('version', version);
|
|
261
262
|
metadata.set('filename', rel);
|
|
@@ -327,14 +328,118 @@ function createAgentMetadataNode(
|
|
|
327
328
|
|
|
328
329
|
const DuplicateNameError = StructuredError('DuplicateNameError')<{ filename: string }>();
|
|
329
330
|
|
|
330
|
-
|
|
331
|
+
function injectEvalMetadata(
|
|
332
|
+
configObj: ASTObjectExpression,
|
|
333
|
+
evalId: string,
|
|
334
|
+
stableEvalId: string,
|
|
335
|
+
version: string,
|
|
336
|
+
filename: string,
|
|
337
|
+
agentId?: string
|
|
338
|
+
): void {
|
|
339
|
+
// Create metadata object with eval IDs and version
|
|
340
|
+
const properties = [
|
|
341
|
+
createObjectPropertyNode('id', evalId),
|
|
342
|
+
createObjectPropertyNode('evalId', stableEvalId),
|
|
343
|
+
createObjectPropertyNode('version', version),
|
|
344
|
+
createObjectPropertyNode('filename', filename),
|
|
345
|
+
];
|
|
346
|
+
|
|
347
|
+
// Add agentId if available
|
|
348
|
+
if (agentId) {
|
|
349
|
+
properties.push(createObjectPropertyNode('agentId', agentId));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const metadataObj: ASTPropertyNode = {
|
|
353
|
+
type: 'Property',
|
|
354
|
+
kind: 'init',
|
|
355
|
+
key: {
|
|
356
|
+
type: 'Identifier',
|
|
357
|
+
name: 'metadata',
|
|
358
|
+
},
|
|
359
|
+
value: {
|
|
360
|
+
type: 'ObjectExpression',
|
|
361
|
+
properties,
|
|
362
|
+
} as ASTObjectExpression,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// Add metadata to the config object
|
|
366
|
+
configObj.properties.push(metadataObj);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function findAgentVariableAndImport(ast: ASTProgram): { varName: string; importPath: string } | undefined {
|
|
370
|
+
// First, find what variable is being used in agent.createEval() calls
|
|
371
|
+
let agentVarName: string | undefined;
|
|
372
|
+
|
|
373
|
+
for (const node of ast.body) {
|
|
374
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
375
|
+
const exportDecl = node as {
|
|
376
|
+
declaration?: { type: string; declarations?: Array<ASTVariableDeclarator> };
|
|
377
|
+
};
|
|
378
|
+
if (exportDecl.declaration?.type === 'VariableDeclaration') {
|
|
379
|
+
const variableDeclaration = exportDecl.declaration as {
|
|
380
|
+
declarations: Array<ASTVariableDeclarator>;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
for (const vardecl of variableDeclaration.declarations) {
|
|
384
|
+
if (vardecl.type === 'VariableDeclarator' && vardecl.init?.type === 'CallExpression') {
|
|
385
|
+
const call = vardecl.init as ASTCallExpression;
|
|
386
|
+
if (call.callee.type === 'MemberExpression') {
|
|
387
|
+
const memberExpr = call.callee as ASTMemberExpression;
|
|
388
|
+
const object = memberExpr.object as ASTNodeIdentifier;
|
|
389
|
+
const property = memberExpr.property as ASTNodeIdentifier;
|
|
390
|
+
if (
|
|
391
|
+
object.type === 'Identifier' &&
|
|
392
|
+
property.type === 'Identifier' &&
|
|
393
|
+
property.name === 'createEval'
|
|
394
|
+
) {
|
|
395
|
+
agentVarName = object.name;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (agentVarName) break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (!agentVarName) return undefined;
|
|
407
|
+
|
|
408
|
+
// Now find the import for this variable
|
|
409
|
+
for (const node of ast.body) {
|
|
410
|
+
if (node.type === 'ImportDeclaration') {
|
|
411
|
+
const importDecl = node as unknown as {
|
|
412
|
+
source: ASTLiteral;
|
|
413
|
+
specifiers: Array<{
|
|
414
|
+
type: string;
|
|
415
|
+
local: ASTNodeIdentifier;
|
|
416
|
+
}>;
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Find default import specifier that matches our variable
|
|
420
|
+
for (const spec of importDecl.specifiers) {
|
|
421
|
+
if (spec.type === 'ImportDefaultSpecifier' && spec.local.name === agentVarName) {
|
|
422
|
+
const importPath = importDecl.source.value;
|
|
423
|
+
if (typeof importPath === 'string') {
|
|
424
|
+
return { varName: agentVarName, importPath };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export async function parseEvalMetadata(
|
|
331
435
|
rootDir: string,
|
|
332
436
|
filename: string,
|
|
333
437
|
contents: string,
|
|
334
438
|
projectId: string,
|
|
335
439
|
deploymentId: string,
|
|
336
|
-
agentId?: string
|
|
337
|
-
|
|
440
|
+
agentId?: string,
|
|
441
|
+
agentMetadata?: Map<string, Map<string, string>>
|
|
442
|
+
): Promise<[
|
|
338
443
|
string,
|
|
339
444
|
Array<{
|
|
340
445
|
filename: string;
|
|
@@ -344,7 +449,7 @@ export function parseEvalMetadata(
|
|
|
344
449
|
evalId: string;
|
|
345
450
|
description?: string;
|
|
346
451
|
}>,
|
|
347
|
-
] {
|
|
452
|
+
]> {
|
|
348
453
|
const logLevel = (process.env.AGENTUITY_LOG_LEVEL || 'info') as
|
|
349
454
|
| 'trace'
|
|
350
455
|
| 'debug'
|
|
@@ -369,6 +474,41 @@ export function parseEvalMetadata(
|
|
|
369
474
|
description?: string;
|
|
370
475
|
}> = [];
|
|
371
476
|
|
|
477
|
+
// Try to find the corresponding agent to get the agentId
|
|
478
|
+
let resolvedAgentId = agentId;
|
|
479
|
+
if (!resolvedAgentId && agentMetadata) {
|
|
480
|
+
const agentInfo = findAgentVariableAndImport(ast);
|
|
481
|
+
if (agentInfo) {
|
|
482
|
+
logger.trace(`[EVAL METADATA] Found agent variable '${agentInfo.varName}' imported from '${agentInfo.importPath}'`);
|
|
483
|
+
|
|
484
|
+
// Resolve the import path to actual file path
|
|
485
|
+
let resolvedPath = agentInfo.importPath;
|
|
486
|
+
if (resolvedPath.startsWith('./') || resolvedPath.startsWith('../')) {
|
|
487
|
+
// Convert relative path to match the format in agentMetadata
|
|
488
|
+
const baseDir = dirname(filename);
|
|
489
|
+
resolvedPath = join(baseDir, resolvedPath);
|
|
490
|
+
// Normalize and ensure .ts extension
|
|
491
|
+
if (!resolvedPath.endsWith('.ts')) {
|
|
492
|
+
resolvedPath += '.ts';
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Find the agent metadata from the passed agentMetadata map
|
|
497
|
+
for (const [agentFile, metadata] of agentMetadata) {
|
|
498
|
+
// Check if this agent file matches the resolved import path
|
|
499
|
+
if (agentFile.includes(basename(resolvedPath)) && metadata.has('agentId')) {
|
|
500
|
+
resolvedAgentId = metadata.get('agentId');
|
|
501
|
+
logger.trace(`[EVAL METADATA] Resolved agentId from agent metadata: ${resolvedAgentId} (file: ${agentFile})`);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (!resolvedAgentId) {
|
|
507
|
+
logger.warn(`[EVAL METADATA] Could not find agent metadata for import path: ${resolvedPath}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
372
512
|
// Find all exported agent.createEval() calls
|
|
373
513
|
for (const body of ast.body) {
|
|
374
514
|
let variableDeclaration: { declarations: Array<ASTVariableDeclarator> } | undefined;
|
|
@@ -414,7 +554,7 @@ export function parseEvalMetadata(
|
|
|
414
554
|
firstArg.type === 'Literal' &&
|
|
415
555
|
typeof (firstArg as ASTLiteral).value === 'string'
|
|
416
556
|
) {
|
|
417
|
-
evalName = (firstArg as ASTLiteral).value;
|
|
557
|
+
evalName = (firstArg as ASTLiteral).value as string;
|
|
418
558
|
} else {
|
|
419
559
|
throw new MetadataError({
|
|
420
560
|
filename,
|
|
@@ -435,7 +575,9 @@ export function parseEvalMetadata(
|
|
|
435
575
|
prop.key.name === 'description'
|
|
436
576
|
) {
|
|
437
577
|
if (prop.value.type === 'Literal') {
|
|
438
|
-
|
|
578
|
+
const literalValue = (prop.value as ASTLiteral).value;
|
|
579
|
+
evalDescription =
|
|
580
|
+
typeof literalValue === 'string' ? literalValue : undefined;
|
|
439
581
|
}
|
|
440
582
|
}
|
|
441
583
|
}
|
|
@@ -448,16 +590,18 @@ export function parseEvalMetadata(
|
|
|
448
590
|
);
|
|
449
591
|
const evalId = getEvalId(projectId, deploymentId, rel, finalName, version);
|
|
450
592
|
|
|
451
|
-
// Generate stable evalId
|
|
452
|
-
const effectiveAgentId =
|
|
593
|
+
// Generate stable evalId using resolved agentId
|
|
594
|
+
const effectiveAgentId = resolvedAgentId || '';
|
|
453
595
|
const stableEvalId = generateStableEvalId(
|
|
454
596
|
projectId,
|
|
455
597
|
effectiveAgentId,
|
|
456
598
|
finalName
|
|
457
599
|
);
|
|
458
600
|
|
|
459
|
-
//
|
|
460
|
-
|
|
601
|
+
// Inject eval metadata into the AST (same pattern as agents)
|
|
602
|
+
if (configObj) {
|
|
603
|
+
injectEvalMetadata(configObj, evalId, stableEvalId, version, rel, resolvedAgentId);
|
|
604
|
+
}
|
|
461
605
|
|
|
462
606
|
evals.push({
|
|
463
607
|
filename: rel,
|
|
@@ -737,13 +881,14 @@ export async function parseAgentMetadata(
|
|
|
737
881
|
const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
|
|
738
882
|
const evalsContents = transpiler.transformSync(evalsSource);
|
|
739
883
|
const agentId = result[1].get('agentId') || '';
|
|
740
|
-
const [, evals] = parseEvalMetadata(
|
|
884
|
+
const [, evals] = await parseEvalMetadata(
|
|
741
885
|
rootDir,
|
|
742
886
|
evalsPath,
|
|
743
887
|
evalsContents,
|
|
744
888
|
projectId,
|
|
745
889
|
deploymentId,
|
|
746
|
-
agentId
|
|
890
|
+
agentId,
|
|
891
|
+
new Map() // Empty map since we already have agentId
|
|
747
892
|
);
|
|
748
893
|
if (evals.length > 0) {
|
|
749
894
|
logger.trace(`Adding ${evals.length} eval(s) to agent metadata for ${name}`);
|
|
@@ -777,6 +922,7 @@ interface ValidatorInfo {
|
|
|
777
922
|
agentVariable?: string;
|
|
778
923
|
inputSchemaVariable?: string;
|
|
779
924
|
outputSchemaVariable?: string;
|
|
925
|
+
stream?: boolean;
|
|
780
926
|
}
|
|
781
927
|
|
|
782
928
|
function hasValidatorCall(args: unknown[]): ValidatorInfo {
|
|
@@ -826,14 +972,16 @@ function hasValidatorCall(args: unknown[]): ValidatorInfo {
|
|
|
826
972
|
}
|
|
827
973
|
|
|
828
974
|
/**
|
|
829
|
-
* Extract schema variable names from validator() call arguments
|
|
830
|
-
* Example: validator({ input: myInputSchema, output: myOutputSchema })
|
|
975
|
+
* Extract schema variable names and stream flag from validator() call arguments
|
|
976
|
+
* Example: validator({ input: myInputSchema, output: myOutputSchema, stream: true })
|
|
831
977
|
*/
|
|
832
978
|
function extractValidatorSchemas(callExpr: ASTCallExpression): {
|
|
833
979
|
inputSchemaVariable?: string;
|
|
834
980
|
outputSchemaVariable?: string;
|
|
981
|
+
stream?: boolean;
|
|
835
982
|
} {
|
|
836
|
-
const result: { inputSchemaVariable?: string; outputSchemaVariable?: string } =
|
|
983
|
+
const result: { inputSchemaVariable?: string; outputSchemaVariable?: string; stream?: boolean } =
|
|
984
|
+
{};
|
|
837
985
|
|
|
838
986
|
// Check if validator has arguments
|
|
839
987
|
if (!callExpr.arguments || callExpr.arguments.length === 0) {
|
|
@@ -848,7 +996,17 @@ function extractValidatorSchemas(callExpr: ASTCallExpression): {
|
|
|
848
996
|
|
|
849
997
|
const objExpr = firstArg as ASTObjectExpression;
|
|
850
998
|
for (const prop of objExpr.properties) {
|
|
851
|
-
|
|
999
|
+
// Extract key name defensively - could be Identifier or Literal
|
|
1000
|
+
let keyName: string | undefined;
|
|
1001
|
+
const propKey = prop.key as { type: string; name?: string; value?: unknown };
|
|
1002
|
+
if (propKey.type === 'Identifier') {
|
|
1003
|
+
keyName = propKey.name;
|
|
1004
|
+
} else if (propKey.type === 'Literal') {
|
|
1005
|
+
keyName = String(propKey.value);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (!keyName) continue;
|
|
1009
|
+
|
|
852
1010
|
if ((keyName === 'input' || keyName === 'output') && prop.value.type === 'Identifier') {
|
|
853
1011
|
const valueName = (prop.value as ASTNodeIdentifier).name;
|
|
854
1012
|
if (keyName === 'input') {
|
|
@@ -857,6 +1015,46 @@ function extractValidatorSchemas(callExpr: ASTCallExpression): {
|
|
|
857
1015
|
result.outputSchemaVariable = valueName;
|
|
858
1016
|
}
|
|
859
1017
|
}
|
|
1018
|
+
// Extract stream flag - can be Literal, Identifier, or UnaryExpression (!0 or !1)
|
|
1019
|
+
if (keyName === 'stream') {
|
|
1020
|
+
if (prop.value.type === 'Literal') {
|
|
1021
|
+
const literal = prop.value as ASTLiteral;
|
|
1022
|
+
if (typeof literal.value === 'boolean') {
|
|
1023
|
+
result.stream = literal.value;
|
|
1024
|
+
}
|
|
1025
|
+
} else if (prop.value.type === 'Identifier') {
|
|
1026
|
+
const identifier = prop.value as ASTNodeIdentifier;
|
|
1027
|
+
// Handle stream: true or stream: false as identifiers
|
|
1028
|
+
if (identifier.name === 'true') {
|
|
1029
|
+
result.stream = true;
|
|
1030
|
+
} else if (identifier.name === 'false') {
|
|
1031
|
+
result.stream = false;
|
|
1032
|
+
}
|
|
1033
|
+
} else if (prop.value.type === 'UnaryExpression') {
|
|
1034
|
+
// Handle !0 (true) or !1 (false) - acorn-loose transpiles booleans this way
|
|
1035
|
+
const unary = prop.value as { type: string; operator?: string; argument?: ASTNode };
|
|
1036
|
+
if (unary.argument?.type === 'Literal') {
|
|
1037
|
+
const literal = unary.argument as ASTLiteral;
|
|
1038
|
+
// Numeric literal: !0 = true, !1 = false
|
|
1039
|
+
if (typeof literal.value === 'number') {
|
|
1040
|
+
if (unary.operator === '!') {
|
|
1041
|
+
result.stream = literal.value === 0;
|
|
1042
|
+
}
|
|
1043
|
+
} else if (typeof literal.value === 'boolean') {
|
|
1044
|
+
result.stream = unary.operator === '!' ? !literal.value : literal.value;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
// Handle true/false as identifiers
|
|
1048
|
+
if (unary.argument?.type === 'Identifier') {
|
|
1049
|
+
const identifier = unary.argument as ASTNodeIdentifier;
|
|
1050
|
+
if (identifier.name === 'true') {
|
|
1051
|
+
result.stream = unary.operator === '!' ? false : true;
|
|
1052
|
+
} else if (identifier.name === 'false') {
|
|
1053
|
+
result.stream = unary.operator === '!' ? true : false;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
860
1058
|
}
|
|
861
1059
|
|
|
862
1060
|
return result;
|
|
@@ -883,7 +1081,7 @@ function extractZValidatorSchema(callExpr: ASTCallExpression): {
|
|
|
883
1081
|
if (targetArg.type === 'Literal') {
|
|
884
1082
|
const targetValue = (targetArg as ASTLiteral).value;
|
|
885
1083
|
// Only extract schemas for JSON body validation
|
|
886
|
-
if (targetValue !== 'json') {
|
|
1084
|
+
if (typeof targetValue === 'string' && targetValue !== 'json') {
|
|
887
1085
|
return result;
|
|
888
1086
|
}
|
|
889
1087
|
} else {
|
|
@@ -1051,7 +1249,7 @@ export async function parseRoute(
|
|
|
1051
1249
|
case 'patch':
|
|
1052
1250
|
case 'delete': {
|
|
1053
1251
|
if (action && (action as ASTLiteral).type === 'Literal') {
|
|
1054
|
-
suffix = (action as ASTLiteral).value;
|
|
1252
|
+
suffix = String((action as ASTLiteral).value);
|
|
1055
1253
|
} else {
|
|
1056
1254
|
throw new InvalidRouterConfigError({
|
|
1057
1255
|
filename,
|
|
@@ -1068,7 +1266,7 @@ export async function parseRoute(
|
|
|
1068
1266
|
method = 'post';
|
|
1069
1267
|
const theaction = action as ASTLiteral;
|
|
1070
1268
|
if (theaction.type === 'Literal') {
|
|
1071
|
-
suffix = theaction.value;
|
|
1269
|
+
suffix = String(theaction.value);
|
|
1072
1270
|
break;
|
|
1073
1271
|
}
|
|
1074
1272
|
break;
|
|
@@ -1088,7 +1286,7 @@ export async function parseRoute(
|
|
|
1088
1286
|
const number = theaction.properties.find((p) => p.key.name === 'number');
|
|
1089
1287
|
if (number && number.value.type === 'Literal') {
|
|
1090
1288
|
const phoneNumber = number.value as ASTLiteral;
|
|
1091
|
-
suffix = hash(phoneNumber.value);
|
|
1289
|
+
suffix = hash(String(phoneNumber.value));
|
|
1092
1290
|
break;
|
|
1093
1291
|
}
|
|
1094
1292
|
}
|
|
@@ -1099,7 +1297,7 @@ export async function parseRoute(
|
|
|
1099
1297
|
method = 'post';
|
|
1100
1298
|
const theaction = action as ASTLiteral;
|
|
1101
1299
|
if (theaction.type === 'Literal') {
|
|
1102
|
-
const email = theaction.value;
|
|
1300
|
+
const email = String(theaction.value);
|
|
1103
1301
|
suffix = hash(email);
|
|
1104
1302
|
break;
|
|
1105
1303
|
}
|
|
@@ -1110,7 +1308,7 @@ export async function parseRoute(
|
|
|
1110
1308
|
method = 'post';
|
|
1111
1309
|
const theaction = action as ASTLiteral;
|
|
1112
1310
|
if (theaction.type === 'Literal') {
|
|
1113
|
-
const expression = theaction.value;
|
|
1311
|
+
const expression = String(theaction.value);
|
|
1114
1312
|
try {
|
|
1115
1313
|
parseCronExpression(expression, { hasSeconds: false });
|
|
1116
1314
|
} catch (ex) {
|
|
@@ -1169,6 +1367,9 @@ export async function parseRoute(
|
|
|
1169
1367
|
if (validatorInfo.outputSchemaVariable) {
|
|
1170
1368
|
routeConfig.outputSchemaVariable = validatorInfo.outputSchemaVariable;
|
|
1171
1369
|
}
|
|
1370
|
+
if (validatorInfo.stream !== undefined) {
|
|
1371
|
+
routeConfig.stream = validatorInfo.stream;
|
|
1372
|
+
}
|
|
1172
1373
|
}
|
|
1173
1374
|
|
|
1174
1375
|
routes.push({
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -305,10 +305,12 @@ export async function bundle({
|
|
|
305
305
|
format: 'esm',
|
|
306
306
|
banner: `// Generated file. DO NOT EDIT`,
|
|
307
307
|
// Disable minify for server bundle (keep code readable for debugging)
|
|
308
|
-
|
|
309
|
-
minify: false,
|
|
308
|
+
minify: !dev,
|
|
310
309
|
drop: isProd ? ['debugger'] : undefined,
|
|
311
|
-
splitting
|
|
310
|
+
// Disable splitting - causes module initialization issues with externalized packages
|
|
311
|
+
// The chunk helper functions (__commonJS, __esm, etc.) don't properly handle
|
|
312
|
+
// CommonJS packages in node_modules that require() other modules
|
|
313
|
+
splitting: false,
|
|
312
314
|
conditions: [isProd ? 'production' : 'development', 'bun'],
|
|
313
315
|
external,
|
|
314
316
|
naming: {
|
|
@@ -322,9 +324,6 @@ export async function bundle({
|
|
|
322
324
|
if (!buildResult.success) {
|
|
323
325
|
handleBuildFailure(buildResult);
|
|
324
326
|
}
|
|
325
|
-
// Fix duplicate exports caused by Bun splitting bug
|
|
326
|
-
// See: https://github.com/oven-sh/bun/issues/5344
|
|
327
|
-
await fixDuplicateExportsInDirectory(outDir, false);
|
|
328
327
|
})();
|
|
329
328
|
|
|
330
329
|
const buildmetadata = getBuildMetadata();
|
|
@@ -486,19 +485,19 @@ export async function bundle({
|
|
|
486
485
|
await Bun.write(workbenchIndexFile, generateWorkbenchIndexHtml());
|
|
487
486
|
|
|
488
487
|
// Bundle workbench using generated files
|
|
489
|
-
//
|
|
490
|
-
// Bun needs to resolve @agentuity/* packages from the project's node_modules
|
|
488
|
+
// Use same strategy as web bundle - fully bundle all dependencies to avoid cross-bundle chunk conflicts
|
|
491
489
|
const workbenchBuildConfig: Bun.BuildConfig = {
|
|
492
490
|
entrypoints: [workbenchIndexFile],
|
|
493
491
|
outdir: join(outDir, 'workbench'),
|
|
494
492
|
sourcemap: dev ? 'inline' : 'linked',
|
|
495
|
-
plugins: [AgentuityBundler], // i dont think we need this plugin here
|
|
496
493
|
target: 'browser',
|
|
497
494
|
format: 'esm',
|
|
498
495
|
banner: `// Generated file. DO NOT EDIT`,
|
|
499
|
-
minify:
|
|
500
|
-
|
|
496
|
+
minify: true,
|
|
497
|
+
drop: isProd ? ['debugger'] : undefined,
|
|
498
|
+
splitting: true,
|
|
501
499
|
packages: 'bundle',
|
|
500
|
+
conditions: ['browser', 'import', 'default'],
|
|
502
501
|
naming: {
|
|
503
502
|
entry: '[dir]/[name].[ext]',
|
|
504
503
|
chunk: 'workbench/chunk/[name]-[hash].[ext]',
|
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -236,38 +236,55 @@ const AgentuityBundler: BunPlugin = {
|
|
|
236
236
|
let newsource = await Bun.file(args.path).text();
|
|
237
237
|
if (args.path.startsWith(srcDir)) {
|
|
238
238
|
const contents = transpiler.transformSync(newsource);
|
|
239
|
-
const result = await parseAgentMetadata(
|
|
240
|
-
rootDir,
|
|
241
|
-
args.path,
|
|
242
|
-
contents,
|
|
243
|
-
projectId,
|
|
244
|
-
deploymentId
|
|
245
|
-
);
|
|
246
239
|
|
|
247
|
-
//
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
240
|
+
// Check if this is an eval file (eval.ts)
|
|
241
|
+
if (args.path.endsWith('/eval.ts')) {
|
|
242
|
+
// parseEvalMetadata will find the agent from the import statement
|
|
243
|
+
const [ns] = await parseEvalMetadata(
|
|
244
|
+
rootDir,
|
|
245
|
+
args.path,
|
|
246
|
+
contents,
|
|
247
|
+
projectId,
|
|
248
|
+
deploymentId,
|
|
249
|
+
undefined, // No agentId - will be resolved from import
|
|
250
|
+
agentMetadata
|
|
251
|
+
);
|
|
252
|
+
newsource = ns;
|
|
253
|
+
} else {
|
|
254
|
+
// Handle regular agent files
|
|
255
|
+
const result = await parseAgentMetadata(
|
|
256
|
+
rootDir,
|
|
257
|
+
args.path,
|
|
258
|
+
contents,
|
|
259
|
+
projectId,
|
|
260
|
+
deploymentId
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// Skip files that don't have a createAgent export
|
|
264
|
+
if (result === undefined) {
|
|
265
|
+
return {
|
|
266
|
+
contents: newsource,
|
|
267
|
+
loader: 'ts',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
254
270
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
271
|
+
const [ns, md] = result;
|
|
272
|
+
newsource = ns;
|
|
273
|
+
|
|
274
|
+
// Only process files that actually export an agent
|
|
275
|
+
if (md.has('name')) {
|
|
276
|
+
const newAgentName = md.get('name');
|
|
277
|
+
for (const [, kv] of agentMetadata) {
|
|
278
|
+
const found = kv.get('name');
|
|
279
|
+
if (newAgentName === found) {
|
|
280
|
+
throw new AgentNameDuplicateError({
|
|
281
|
+
message: `The agent in ${kv.get('filename')} and the agent in ${md.get('filename')} have the same name (${found}). Agent Names must be unique within a project.`,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
267
284
|
}
|
|
268
|
-
}
|
|
269
285
|
|
|
270
|
-
|
|
286
|
+
agentMetadata.set(md.get('name')!, md);
|
|
287
|
+
}
|
|
271
288
|
}
|
|
272
289
|
}
|
|
273
290
|
return {
|
|
@@ -276,19 +293,6 @@ const AgentuityBundler: BunPlugin = {
|
|
|
276
293
|
};
|
|
277
294
|
});
|
|
278
295
|
|
|
279
|
-
build.onLoad({ filter: /\/eval\.ts$/, namespace: 'file' }, async (args) => {
|
|
280
|
-
let newsource = await Bun.file(args.path).text();
|
|
281
|
-
if (args.path.startsWith(srcDir)) {
|
|
282
|
-
const contents = transpiler.transformSync(newsource);
|
|
283
|
-
const [ns] = parseEvalMetadata(rootDir, args.path, contents, projectId, deploymentId);
|
|
284
|
-
newsource = ns;
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
287
|
-
contents: newsource,
|
|
288
|
-
loader: 'ts',
|
|
289
|
-
};
|
|
290
|
-
});
|
|
291
|
-
|
|
292
296
|
const patches = generatePatches();
|
|
293
297
|
for (const [, patch] of patches) {
|
|
294
298
|
let modulePath = join('node_modules', patch.module, '.*');
|
|
@@ -25,6 +25,8 @@ export interface RouteInfo {
|
|
|
25
25
|
inputSchemaVariable?: string;
|
|
26
26
|
/** Output schema variable name if using validator({ output }) */
|
|
27
27
|
outputSchemaVariable?: string;
|
|
28
|
+
/** Whether this route is a streaming route (from validator({ stream: true })) */
|
|
29
|
+
stream?: boolean;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -38,7 +40,8 @@ export interface RouteInfo {
|
|
|
38
40
|
*/
|
|
39
41
|
export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void {
|
|
40
42
|
// Filter routes by type (include ALL routes, not just those with validators)
|
|
41
|
-
|
|
43
|
+
// Note: 'stream' routes are HTTP routes that return ReadableStream, so include them with API routes
|
|
44
|
+
const apiRoutes = routes.filter((r) => r.routeType === 'api' || r.routeType === 'stream');
|
|
42
45
|
const websocketRoutes = routes.filter((r) => r.routeType === 'websocket');
|
|
43
46
|
const sseRoutes = routes.filter((r) => r.routeType === 'sse');
|
|
44
47
|
|
|
@@ -142,6 +145,7 @@ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void
|
|
|
142
145
|
return ` '${routeKey}': {
|
|
143
146
|
inputSchema: typeof ${importName} extends { inputSchema?: infer I } ? I : never;
|
|
144
147
|
outputSchema: typeof ${importName} extends { outputSchema?: infer O } ? O : never;
|
|
148
|
+
stream: typeof ${importName} extends { stream?: infer S } ? S : false;
|
|
145
149
|
};`;
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -153,9 +157,11 @@ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void
|
|
|
153
157
|
const outputType = route.outputSchemaVariable
|
|
154
158
|
? `typeof ${route.outputSchemaVariable}`
|
|
155
159
|
: 'never';
|
|
160
|
+
const streamValue = route.stream === true ? 'true' : 'false';
|
|
156
161
|
return ` '${routeKey}': {
|
|
157
162
|
inputSchema: ${inputType};
|
|
158
163
|
outputSchema: ${outputType};
|
|
164
|
+
stream: ${streamValue};
|
|
159
165
|
};`;
|
|
160
166
|
}
|
|
161
167
|
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type DeploymentInstructions,
|
|
28
28
|
type DeploymentComplete,
|
|
29
29
|
type DeploymentStatusResult,
|
|
30
|
+
getAppBaseURL,
|
|
30
31
|
} from '@agentuity/server';
|
|
31
32
|
import {
|
|
32
33
|
findEnvFile,
|
|
@@ -51,6 +52,7 @@ const DeployResponseSchema = z.object({
|
|
|
51
52
|
deployment: z.string().describe('Deployment-specific URL'),
|
|
52
53
|
latest: z.string().describe('Latest/active deployment URL'),
|
|
53
54
|
custom: z.array(z.string()).optional().describe('Custom domain URLs'),
|
|
55
|
+
dashboard: z.string().describe('The dashboard URL for the deployment'),
|
|
54
56
|
})
|
|
55
57
|
.optional()
|
|
56
58
|
.describe('Deployment URLs'),
|
|
@@ -551,9 +553,12 @@ export const deploySubcommand = createSubcommand({
|
|
|
551
553
|
tui.success('Your project was deployed!');
|
|
552
554
|
}
|
|
553
555
|
|
|
556
|
+
const appUrl = getAppBaseURL(config?.name, config?.overrides);
|
|
557
|
+
|
|
558
|
+
const dashboard = `${appUrl}/r/${deployment.id}`;
|
|
559
|
+
|
|
554
560
|
// Show deployment URLs
|
|
555
561
|
if (complete?.publicUrls) {
|
|
556
|
-
tui.arrow(tui.bold(tui.padRight('Deployment ID:', 17)) + tui.link(deployment.id));
|
|
557
562
|
if (complete.publicUrls.custom?.length) {
|
|
558
563
|
for (const url of complete.publicUrls.custom) {
|
|
559
564
|
tui.arrow(tui.bold(tui.padRight('Deployment URL:', 17)) + tui.link(url));
|
|
@@ -566,6 +571,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
566
571
|
tui.arrow(
|
|
567
572
|
tui.bold(tui.padRight('Project URL:', 17)) + tui.link(complete.publicUrls.latest)
|
|
568
573
|
);
|
|
574
|
+
tui.arrow(tui.bold(tui.padRight('Dashboard URL:', 17)) + tui.link(dashboard));
|
|
569
575
|
}
|
|
570
576
|
}
|
|
571
577
|
|
|
@@ -579,6 +585,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
579
585
|
deployment: complete.publicUrls.deployment,
|
|
580
586
|
latest: complete.publicUrls.latest,
|
|
581
587
|
custom: complete.publicUrls.custom,
|
|
588
|
+
dashboard,
|
|
582
589
|
}
|
|
583
590
|
: undefined,
|
|
584
591
|
};
|