@agentuity/cli 0.0.53 → 0.0.55
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/cli.d.ts.map +1 -1
- package/dist/cli.js +66 -8
- package/dist/cli.js.map +1 -1
- package/dist/cmd/auth/ssh/add.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/add.js +28 -13
- package/dist/cmd/auth/ssh/add.js.map +1 -1
- package/dist/cmd/auth/ssh/delete.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/delete.js +28 -18
- package/dist/cmd/auth/ssh/delete.js.map +1 -1
- package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/list.js +5 -6
- package/dist/cmd/auth/ssh/list.js.map +1 -1
- package/dist/cmd/build/ast.d.ts +34 -0
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +165 -5
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts +2 -1
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +78 -17
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +2 -0
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +75 -7
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/list.js +28 -23
- package/dist/cmd/cloud/deployment/list.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +50 -47
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/cloud/env/get.d.ts.map +1 -1
- package/dist/cmd/cloud/env/get.js +16 -14
- package/dist/cmd/cloud/env/get.js.map +1 -1
- package/dist/cmd/cloud/env/list.d.ts.map +1 -1
- package/dist/cmd/cloud/env/list.js +24 -20
- package/dist/cmd/cloud/env/list.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.js +18 -16
- package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.js +11 -11
- package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/list-namespaces.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/list-namespaces.js +11 -7
- package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.js +16 -17
- package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.js +38 -23
- package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
- package/dist/cmd/cloud/objectstore/get.d.ts.map +1 -1
- package/dist/cmd/cloud/objectstore/get.js +17 -15
- package/dist/cmd/cloud/objectstore/get.js.map +1 -1
- package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +1 -1
- package/dist/cmd/cloud/objectstore/list-buckets.js +12 -8
- package/dist/cmd/cloud/objectstore/list-buckets.js.map +1 -1
- package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +1 -1
- package/dist/cmd/cloud/objectstore/list-keys.js +13 -10
- package/dist/cmd/cloud/objectstore/list-keys.js.map +1 -1
- package/dist/cmd/cloud/resource/list.d.ts.map +1 -1
- package/dist/cmd/cloud/resource/list.js +38 -27
- package/dist/cmd/cloud/resource/list.js.map +1 -1
- package/dist/cmd/cloud/secret/get.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/get.js +17 -15
- package/dist/cmd/cloud/secret/get.js.map +1 -1
- package/dist/cmd/cloud/secret/list.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/list.js +24 -20
- package/dist/cmd/cloud/secret/list.js.map +1 -1
- package/dist/cmd/cloud/session/logs.d.ts.map +1 -1
- package/dist/cmd/cloud/session/logs.js +18 -15
- package/dist/cmd/cloud/session/logs.js.map +1 -1
- package/dist/cmd/dev/agents.d.ts.map +1 -1
- package/dist/cmd/dev/agents.js +55 -41
- package/dist/cmd/dev/agents.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +34 -14
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/profile/create.js +1 -1
- package/dist/cmd/profile/create.js.map +1 -1
- package/dist/cmd/profile/delete.d.ts.map +1 -1
- package/dist/cmd/profile/delete.js +1 -1
- package/dist/cmd/profile/delete.js.map +1 -1
- package/dist/cmd/profile/list.d.ts.map +1 -1
- package/dist/cmd/profile/list.js +29 -11
- package/dist/cmd/profile/list.js.map +1 -1
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/profile/show.js +7 -10
- package/dist/cmd/profile/show.js.map +1 -1
- package/dist/cmd/project/delete.js +1 -1
- package/dist/cmd/project/delete.js.map +1 -1
- package/dist/cmd/version/index.d.ts.map +1 -1
- package/dist/cmd/version/index.js +1 -1
- package/dist/cmd/version/index.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +3 -1
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +32 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/src/cli.ts +109 -8
- package/src/cmd/auth/ssh/add.ts +37 -17
- package/src/cmd/auth/ssh/delete.ts +36 -23
- package/src/cmd/auth/ssh/list.ts +8 -6
- package/src/cmd/build/ast.ts +209 -5
- package/src/cmd/build/bundler.ts +82 -16
- package/src/cmd/build/index.ts +2 -0
- package/src/cmd/build/plugin.ts +93 -7
- package/src/cmd/cloud/deployment/list.ts +30 -26
- package/src/cmd/cloud/deployment/show.ts +47 -42
- package/src/cmd/cloud/env/get.ts +14 -12
- package/src/cmd/cloud/env/list.ts +24 -22
- package/src/cmd/cloud/keyvalue/get.ts +19 -14
- package/src/cmd/cloud/keyvalue/keys.ts +10 -12
- package/src/cmd/cloud/keyvalue/list-namespaces.ts +10 -8
- package/src/cmd/cloud/keyvalue/search.ts +14 -17
- package/src/cmd/cloud/keyvalue/stats.ts +52 -28
- package/src/cmd/cloud/objectstore/get.ts +18 -13
- package/src/cmd/cloud/objectstore/list-buckets.ts +11 -9
- package/src/cmd/cloud/objectstore/list-keys.ts +12 -11
- package/src/cmd/cloud/resource/list.ts +33 -23
- package/src/cmd/cloud/secret/get.ts +15 -13
- package/src/cmd/cloud/secret/list.ts +24 -22
- package/src/cmd/cloud/session/logs.ts +18 -17
- package/src/cmd/dev/agents.ts +70 -50
- package/src/cmd/dev/index.ts +41 -14
- package/src/cmd/profile/create.ts +3 -3
- package/src/cmd/profile/delete.ts +5 -2
- package/src/cmd/profile/list.ts +31 -11
- package/src/cmd/profile/show.ts +15 -12
- package/src/cmd/project/delete.ts +1 -1
- package/src/cmd/version/index.ts +5 -1
- package/src/tui.ts +3 -1
- package/src/types.ts +32 -10
package/src/cmd/build/ast.ts
CHANGED
|
@@ -126,7 +126,7 @@ function getAgentId(
|
|
|
126
126
|
filename: string,
|
|
127
127
|
version: string
|
|
128
128
|
): string {
|
|
129
|
-
return `
|
|
129
|
+
return `agentid_${hashSHA1(projectId, deploymentId, filename, version)}`;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function getEvalId(
|
|
@@ -152,7 +152,7 @@ function generateRouteId(
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
function generateStableAgentId(projectId: string, name: string): string {
|
|
155
|
-
return `
|
|
155
|
+
return `agent_${hashSHA1(projectId, name)}`.substring(0, 64);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
function generateStableEvalId(projectId: string, agentId: string, name: string): string {
|
|
@@ -164,7 +164,7 @@ type AcornParseResultType = ReturnType<typeof acornLoose.parse>;
|
|
|
164
164
|
function augmentAgentMetadataNode(
|
|
165
165
|
projectId: string,
|
|
166
166
|
id: string,
|
|
167
|
-
|
|
167
|
+
identifier: string,
|
|
168
168
|
rel: string,
|
|
169
169
|
version: string,
|
|
170
170
|
ast: AcornParseResultType,
|
|
@@ -178,7 +178,8 @@ function augmentAgentMetadataNode(
|
|
|
178
178
|
`missing required metadata.name in ${filename}${location}. This Agent should have a unique and human readable name for this project.`
|
|
179
179
|
);
|
|
180
180
|
}
|
|
181
|
-
|
|
181
|
+
const name = metadata.get('name')!;
|
|
182
|
+
if (metadata.has('identifier') && identifier !== metadata.get('identifier')) {
|
|
182
183
|
const location = ast.loc?.start ? ` on line ${ast.loc.start}` : '';
|
|
183
184
|
throw new Error(
|
|
184
185
|
`metadata.identifier (${metadata.get('identifier')}) in ${filename}${location} is mismatched (${name}). This is an internal error.`
|
|
@@ -188,7 +189,7 @@ function augmentAgentMetadataNode(
|
|
|
188
189
|
const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
|
|
189
190
|
const agentId = generateStableAgentId(projectId, name);
|
|
190
191
|
metadata.set('version', version);
|
|
191
|
-
metadata.set('identifier',
|
|
192
|
+
metadata.set('identifier', identifier);
|
|
192
193
|
metadata.set('filename', rel);
|
|
193
194
|
metadata.set('id', id);
|
|
194
195
|
metadata.set('agentId', agentId);
|
|
@@ -897,3 +898,206 @@ export async function parseRoute(
|
|
|
897
898
|
}
|
|
898
899
|
return routes;
|
|
899
900
|
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Configuration extracted from createWorkbench call
|
|
904
|
+
*/
|
|
905
|
+
export interface WorkbenchConfig {
|
|
906
|
+
route: string;
|
|
907
|
+
headers?: Record<string, string>;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Result of workbench analysis
|
|
912
|
+
*/
|
|
913
|
+
export interface WorkbenchAnalysis {
|
|
914
|
+
hasWorkbench: boolean;
|
|
915
|
+
config: WorkbenchConfig | null;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Check if a TypeScript file actively uses a specific function
|
|
920
|
+
* (ignores comments and unused imports)
|
|
921
|
+
*
|
|
922
|
+
* @param content - The TypeScript source code
|
|
923
|
+
* @param functionName - The function name to check for (e.g., 'createWorkbench')
|
|
924
|
+
* @returns true if the function is both imported and called
|
|
925
|
+
*/
|
|
926
|
+
export async function checkFunctionUsage(content: string, functionName: string): Promise<boolean> {
|
|
927
|
+
try {
|
|
928
|
+
const ts = await import('typescript');
|
|
929
|
+
const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
|
|
930
|
+
|
|
931
|
+
let hasImport = false;
|
|
932
|
+
let hasUsage = false;
|
|
933
|
+
|
|
934
|
+
function visitNode(node: import('typescript').Node): void {
|
|
935
|
+
// Check for import declarations with the function
|
|
936
|
+
if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
|
|
937
|
+
if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
938
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
939
|
+
if (element.name.text === functionName) {
|
|
940
|
+
hasImport = true;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// Check for function calls
|
|
946
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
947
|
+
if (node.expression.text === functionName) {
|
|
948
|
+
hasUsage = true;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
// Recursively visit child nodes
|
|
952
|
+
ts.forEachChild(node, visitNode);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
visitNode(sourceFile);
|
|
956
|
+
// Only return true if both import and usage are present
|
|
957
|
+
return hasImport && hasUsage;
|
|
958
|
+
} catch (error) {
|
|
959
|
+
// Fallback to string check if AST parsing fails
|
|
960
|
+
console.warn(`AST parsing failed for ${functionName}, falling back to string check:`, error);
|
|
961
|
+
return content.includes(functionName);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Check if app.ts contains conflicting routes for a given endpoint
|
|
967
|
+
*/
|
|
968
|
+
export async function checkRouteConflicts(
|
|
969
|
+
content: string,
|
|
970
|
+
workbenchEndpoint: string
|
|
971
|
+
): Promise<boolean> {
|
|
972
|
+
try {
|
|
973
|
+
const ts = await import('typescript');
|
|
974
|
+
const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
|
|
975
|
+
|
|
976
|
+
let hasConflict = false;
|
|
977
|
+
|
|
978
|
+
function visitNode(node: import('typescript').Node): void {
|
|
979
|
+
// Check for router.get calls
|
|
980
|
+
if (
|
|
981
|
+
ts.isCallExpression(node) &&
|
|
982
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
983
|
+
ts.isIdentifier(node.expression.name) &&
|
|
984
|
+
node.expression.name.text === 'get'
|
|
985
|
+
) {
|
|
986
|
+
// Check if first argument is the workbench endpoint
|
|
987
|
+
if (node.arguments.length > 0 && ts.isStringLiteral(node.arguments[0])) {
|
|
988
|
+
if (node.arguments[0].text === workbenchEndpoint) {
|
|
989
|
+
hasConflict = true;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
ts.forEachChild(node, visitNode);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
visitNode(sourceFile);
|
|
998
|
+
return hasConflict;
|
|
999
|
+
} catch (_error) {
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Analyze workbench usage and extract configuration
|
|
1006
|
+
*
|
|
1007
|
+
* @param content - The TypeScript source code
|
|
1008
|
+
* @returns workbench analysis including usage and config
|
|
1009
|
+
*/
|
|
1010
|
+
export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalysis> {
|
|
1011
|
+
try {
|
|
1012
|
+
const ts = await import('typescript');
|
|
1013
|
+
const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
|
|
1014
|
+
|
|
1015
|
+
let hasImport = false;
|
|
1016
|
+
let hasUsage = false;
|
|
1017
|
+
let config: WorkbenchConfig | null = null;
|
|
1018
|
+
|
|
1019
|
+
function visitNode(node: import('typescript').Node): void {
|
|
1020
|
+
// Check for import declarations with createWorkbench
|
|
1021
|
+
if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
|
|
1022
|
+
if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
1023
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
1024
|
+
if (element.name.text === 'createWorkbench') {
|
|
1025
|
+
hasImport = true;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Check for createWorkbench function calls and extract config
|
|
1032
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
1033
|
+
if (node.expression.text === 'createWorkbench') {
|
|
1034
|
+
hasUsage = true;
|
|
1035
|
+
|
|
1036
|
+
// Extract configuration from the first argument (if any)
|
|
1037
|
+
if (node.arguments.length > 0) {
|
|
1038
|
+
const configArg = node.arguments[0];
|
|
1039
|
+
config = parseConfigObject(configArg, ts);
|
|
1040
|
+
} else {
|
|
1041
|
+
// Default config if no arguments provided
|
|
1042
|
+
config = { route: '/workbench' };
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Recursively visit child nodes
|
|
1048
|
+
ts.forEachChild(node, visitNode);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
visitNode(sourceFile);
|
|
1052
|
+
|
|
1053
|
+
// Set default config if workbench is used but no config was parsed
|
|
1054
|
+
if (hasImport && hasUsage && !config) {
|
|
1055
|
+
config = { route: '/workbench' };
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
return {
|
|
1059
|
+
hasWorkbench: hasImport && hasUsage,
|
|
1060
|
+
config: config,
|
|
1061
|
+
};
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
// Fallback to simple check if AST parsing fails
|
|
1064
|
+
console.warn('Workbench AST parsing failed, falling back to string check:', error);
|
|
1065
|
+
const hasWorkbench = content.includes('createWorkbench');
|
|
1066
|
+
return {
|
|
1067
|
+
hasWorkbench,
|
|
1068
|
+
config: hasWorkbench ? { route: '/workbench' } : null,
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Parse a TypeScript object literal to extract configuration
|
|
1075
|
+
*/
|
|
1076
|
+
function parseConfigObject(
|
|
1077
|
+
node: import('typescript').Node,
|
|
1078
|
+
ts: typeof import('typescript')
|
|
1079
|
+
): WorkbenchConfig | null {
|
|
1080
|
+
if (!ts.isObjectLiteralExpression(node)) {
|
|
1081
|
+
return { route: '/workbench' }; // Default config
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
const config: WorkbenchConfig = { route: '/workbench' };
|
|
1085
|
+
|
|
1086
|
+
for (const property of node.properties) {
|
|
1087
|
+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) {
|
|
1088
|
+
const propertyName = property.name.text;
|
|
1089
|
+
|
|
1090
|
+
if (propertyName === 'route' && ts.isStringLiteral(property.initializer)) {
|
|
1091
|
+
config.route = property.initializer.text;
|
|
1092
|
+
} else if (
|
|
1093
|
+
propertyName === 'headers' &&
|
|
1094
|
+
ts.isObjectLiteralExpression(property.initializer)
|
|
1095
|
+
) {
|
|
1096
|
+
// Parse headers object if needed (not implemented for now)
|
|
1097
|
+
config.headers = {};
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return config;
|
|
1103
|
+
}
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $ } from 'bun';
|
|
2
2
|
import { join, relative, resolve, dirname } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';
|
|
4
5
|
import gitParseUrl from 'git-url-parse';
|
|
5
6
|
import AgentuityBundler, { getBuildMetadata } from './plugin';
|
|
@@ -7,6 +8,8 @@ import { getFilesRecursively } from './file';
|
|
|
7
8
|
import { getVersion } from '../../version';
|
|
8
9
|
import type { Project } from '../../types';
|
|
9
10
|
import { fixDuplicateExportsInDirectory } from './fix-duplicate-exports';
|
|
11
|
+
import { createLogger } from '@agentuity/server';
|
|
12
|
+
import type { LogLevel } from '../../types';
|
|
10
13
|
|
|
11
14
|
export interface BundleOptions {
|
|
12
15
|
rootDir: string;
|
|
@@ -16,6 +19,7 @@ export interface BundleOptions {
|
|
|
16
19
|
projectId?: string;
|
|
17
20
|
deploymentId?: string;
|
|
18
21
|
project?: Project;
|
|
22
|
+
port?: number;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export async function bundle({
|
|
@@ -25,6 +29,7 @@ export async function bundle({
|
|
|
25
29
|
dev = false,
|
|
26
30
|
rootDir,
|
|
27
31
|
project,
|
|
32
|
+
port,
|
|
28
33
|
}: BundleOptions) {
|
|
29
34
|
const appFile = join(rootDir, 'app.ts');
|
|
30
35
|
if (!existsSync(appFile)) {
|
|
@@ -45,7 +50,7 @@ export async function bundle({
|
|
|
45
50
|
}
|
|
46
51
|
const files = await getFilesRecursively(dir);
|
|
47
52
|
for (const filename of files) {
|
|
48
|
-
if (/\.[jt]s?$/.test(filename)) {
|
|
53
|
+
if (/\.[jt]s?$/.test(filename) && !filename.includes('.generated.')) {
|
|
49
54
|
appEntrypoints.push(filename);
|
|
50
55
|
}
|
|
51
56
|
}
|
|
@@ -246,26 +251,87 @@ export async function bundle({
|
|
|
246
251
|
}
|
|
247
252
|
}
|
|
248
253
|
})();
|
|
254
|
+
}
|
|
249
255
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
256
|
+
// Bundle workbench app if detected via setupWorkbench
|
|
257
|
+
const { analyzeWorkbench } = await import('./ast');
|
|
258
|
+
if (existsSync(appFile)) {
|
|
259
|
+
const appContent = await Bun.file(appFile).text();
|
|
260
|
+
const analysis = await analyzeWorkbench(appContent);
|
|
261
|
+
|
|
262
|
+
if (analysis.hasWorkbench) {
|
|
263
|
+
// Encode workbench config for environment variable
|
|
264
|
+
const { encodeWorkbenchConfig } = await import('@agentuity/core');
|
|
265
|
+
const config = analysis.config || { route: '/workbench', headers: {} };
|
|
266
|
+
// Add port to config (defaults to 3500 if not provided)
|
|
267
|
+
const configWithPort = { ...config, port: port || 3500 };
|
|
268
|
+
const encodedConfig = encodeWorkbenchConfig(configWithPort);
|
|
269
|
+
const workbenchDefine = {
|
|
270
|
+
...define,
|
|
271
|
+
AGENTUITY_WORKBENCH_CONFIG_INLINE: JSON.stringify(encodedConfig),
|
|
272
|
+
};
|
|
273
|
+
const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
|
|
274
|
+
try {
|
|
275
|
+
const projectRequire = createRequire(resolve(rootDir, 'package.json'));
|
|
276
|
+
const workbenchPkgPath = projectRequire.resolve('@agentuity/workbench/package.json');
|
|
277
|
+
const workbenchAppDir = join(dirname(workbenchPkgPath), 'src', 'app');
|
|
278
|
+
|
|
279
|
+
if (existsSync(workbenchAppDir)) {
|
|
280
|
+
const workbenchIndexFile = join(workbenchAppDir, 'index.html');
|
|
281
|
+
if (existsSync(workbenchIndexFile)) {
|
|
282
|
+
// Bundle workbench using same config as main web app
|
|
283
|
+
const workbenchBuildConfig: Bun.BuildConfig = {
|
|
284
|
+
entrypoints: [workbenchIndexFile],
|
|
285
|
+
root: workbenchAppDir,
|
|
286
|
+
outdir: join(outDir, 'workbench'),
|
|
287
|
+
define: workbenchDefine,
|
|
288
|
+
sourcemap: dev ? 'inline' : 'linked',
|
|
289
|
+
plugins: [AgentuityBundler],
|
|
290
|
+
target: 'browser',
|
|
291
|
+
format: 'esm',
|
|
292
|
+
banner: `// Generated file. DO NOT EDIT`,
|
|
293
|
+
minify: true,
|
|
294
|
+
splitting: true,
|
|
295
|
+
packages: 'bundle',
|
|
296
|
+
naming: {
|
|
297
|
+
entry: '[dir]/[name].[ext]',
|
|
298
|
+
chunk: 'workbench/chunk/[name]-[hash].[ext]',
|
|
299
|
+
asset: 'workbench/asset/[name]-[hash].[ext]',
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const workbenchResult = await Bun.build(workbenchBuildConfig);
|
|
304
|
+
if (workbenchResult.success) {
|
|
305
|
+
logger.debug('Workbench bundled successfully');
|
|
306
|
+
} else {
|
|
307
|
+
logger.error('Workbench bundling failed:', workbenchResult.logs.join('\n'));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch (error) {
|
|
312
|
+
logger.error('Failed to bundle workbench:', error);
|
|
265
313
|
}
|
|
266
314
|
}
|
|
267
315
|
}
|
|
268
316
|
|
|
317
|
+
if (!dev && buildmetadata) {
|
|
318
|
+
const webPublicDir = join(webDir, 'public');
|
|
319
|
+
if (existsSync(webPublicDir)) {
|
|
320
|
+
const assets = buildmetadata.assets;
|
|
321
|
+
const webOutPublicDir = join(outDir, 'web', 'public');
|
|
322
|
+
cpSync(webPublicDir, webOutPublicDir, { recursive: true });
|
|
323
|
+
[...new Bun.Glob('**.*').scanSync(webOutPublicDir)].forEach((f) => {
|
|
324
|
+
const bf = Bun.file(join(webOutPublicDir, f));
|
|
325
|
+
assets.push({
|
|
326
|
+
filename: join('public', f),
|
|
327
|
+
kind: 'static',
|
|
328
|
+
contentType: bf.type,
|
|
329
|
+
size: bf.size,
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
269
335
|
if (!dev && Bun.which('git') && buildmetadata?.deployment) {
|
|
270
336
|
buildmetadata.deployment.git = {
|
|
271
337
|
commit: process.env.GIT_SHA || process.env.GITHUB_SHA,
|
package/src/cmd/build/index.ts
CHANGED
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -2,7 +2,14 @@ import type { BunPlugin } from 'bun';
|
|
|
2
2
|
import { dirname, basename, join, resolve } from 'node:path';
|
|
3
3
|
import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
4
4
|
import type { BuildMetadata } from '@agentuity/server';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
parseAgentMetadata,
|
|
7
|
+
parseRoute,
|
|
8
|
+
parseEvalMetadata,
|
|
9
|
+
analyzeWorkbench,
|
|
10
|
+
checkRouteConflicts,
|
|
11
|
+
type WorkbenchConfig,
|
|
12
|
+
} from './ast';
|
|
6
13
|
import { applyPatch, generatePatches } from './patch';
|
|
7
14
|
import { detectSubagent } from '../../utils/detectSubagent';
|
|
8
15
|
import { createLogger } from '@agentuity/server';
|
|
@@ -19,6 +26,50 @@ function toPascalCase(str: string): string {
|
|
|
19
26
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
20
27
|
}
|
|
21
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Setup workbench configuration by analyzing app.ts file
|
|
31
|
+
*/
|
|
32
|
+
async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
|
|
33
|
+
// Look for app.ts in both root and src directories
|
|
34
|
+
const rootAppFile = join(dirname(srcDir), 'app.ts');
|
|
35
|
+
const srcAppFile = join(srcDir, 'app.ts');
|
|
36
|
+
|
|
37
|
+
let appFile = '';
|
|
38
|
+
if (existsSync(rootAppFile)) {
|
|
39
|
+
appFile = rootAppFile;
|
|
40
|
+
} else if (existsSync(srcAppFile)) {
|
|
41
|
+
appFile = srcAppFile;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!appFile || !existsSync(appFile)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const appContent = await Bun.file(appFile).text();
|
|
49
|
+
const analysis = await analyzeWorkbench(appContent);
|
|
50
|
+
|
|
51
|
+
if (!analysis.hasWorkbench) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const workbenchConfig = analysis.config;
|
|
56
|
+
|
|
57
|
+
// Check for route conflicts if workbench is being used
|
|
58
|
+
if (workbenchConfig?.route) {
|
|
59
|
+
const hasConflict = await checkRouteConflicts(appContent, workbenchConfig.route);
|
|
60
|
+
if (hasConflict) {
|
|
61
|
+
const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
|
|
62
|
+
logger.error(`🚨 Route conflict detected!\n`);
|
|
63
|
+
logger.error(
|
|
64
|
+
` Workbench route '${workbenchConfig.route}' conflicts with existing application route`
|
|
65
|
+
);
|
|
66
|
+
logger.error(` Please use a different route or remove the conflicting route.\n`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return workbenchConfig;
|
|
71
|
+
}
|
|
72
|
+
|
|
22
73
|
function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, string>>) {
|
|
23
74
|
// Separate parent agents and subagents
|
|
24
75
|
const parentAgents = agentInfo.filter((a) => !a.parent);
|
|
@@ -226,7 +277,6 @@ ${typeExports}
|
|
|
226
277
|
// Augment @agentuity/runtime types with strongly-typed agents from this project
|
|
227
278
|
declare module "@agentuity/runtime" {
|
|
228
279
|
// Augment the AgentRegistry interface with project-specific strongly-typed agents
|
|
229
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
230
280
|
export interface AgentRegistry {
|
|
231
281
|
${runtimeAgentTypes}
|
|
232
282
|
}
|
|
@@ -291,6 +341,10 @@ const AgentuityBundler: BunPlugin = {
|
|
|
291
341
|
const deploymentId = build.config.define?.['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID']
|
|
292
342
|
? JSON.parse(build.config.define['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID'])
|
|
293
343
|
: '';
|
|
344
|
+
const isDevMode =
|
|
345
|
+
(build.config.define?.['process.env.NODE_ENV']
|
|
346
|
+
? JSON.parse(build.config.define['process.env.NODE_ENV'])
|
|
347
|
+
: 'production') === 'development';
|
|
294
348
|
const routes: Set<string> = new Set();
|
|
295
349
|
const agentInfo: Array<Record<string, string>> = [];
|
|
296
350
|
const agentMetadata: Map<string, Map<string, string>> = new Map<
|
|
@@ -493,11 +547,45 @@ const AgentuityBundler: BunPlugin = {
|
|
|
493
547
|
const indexFile = join(srcDir, 'web', 'index.html');
|
|
494
548
|
|
|
495
549
|
if (existsSync(indexFile)) {
|
|
550
|
+
// Setup workbench configuration - evaluate fresh each time during builds
|
|
551
|
+
const workbenchConfig = await setupWorkbench(srcDir);
|
|
552
|
+
|
|
496
553
|
inserts.unshift(`await (async () => {
|
|
497
554
|
const { serveStatic } = require('hono/bun');
|
|
498
|
-
const { getRouter } = await import('@agentuity/runtime');
|
|
555
|
+
const { getRouter, registerDevModeRoutes } = await import('@agentuity/runtime');
|
|
499
556
|
const router = getRouter()!;
|
|
500
|
-
|
|
557
|
+
|
|
558
|
+
// Setup workbench routes if workbench was bundled
|
|
559
|
+
const workbenchIndexPath = import.meta.dir + '/workbench/index.html';
|
|
560
|
+
if (await Bun.file(workbenchIndexPath).exists()) {
|
|
561
|
+
let workbenchIndex = await Bun.file(workbenchIndexPath).text();
|
|
562
|
+
|
|
563
|
+
// Always serve assets at /workbench/* regardless of HTML route
|
|
564
|
+
const workbenchStatic = serveStatic({ root: import.meta.dir + '/workbench' });
|
|
565
|
+
router.get('/workbench/*', workbenchStatic);
|
|
566
|
+
|
|
567
|
+
// Use the workbench config determined at build time
|
|
568
|
+
const route = ${JSON.stringify(workbenchConfig?.route || '/workbench')};
|
|
569
|
+
|
|
570
|
+
// If using custom route, update HTML to point to absolute /workbench/ paths
|
|
571
|
+
if (route !== '/workbench') {
|
|
572
|
+
workbenchIndex = workbenchIndex.replace(new RegExp('src="\\\\.\\\\/workbench\\\\/', 'g'), 'src="/workbench/');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Serve HTML at the configured route
|
|
576
|
+
router.get(route, (c) => c.html(workbenchIndex));
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
let index = await Bun.file(import.meta.dir + '/web/index.html').text();
|
|
580
|
+
if (${isDevMode}) {
|
|
581
|
+
const end = index.lastIndexOf('</html>');
|
|
582
|
+
const html = registerDevModeRoutes(router);
|
|
583
|
+
if (end > 0) {
|
|
584
|
+
index = index.substring(0,end) + html + index.substring(end);
|
|
585
|
+
} else {
|
|
586
|
+
index += html;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
501
589
|
const webstatic = serveStatic({ root: import.meta.dir + '/web' });
|
|
502
590
|
router.get('/', (c) => c.html(index));
|
|
503
591
|
router.get('/web/chunk/*', webstatic);
|
|
@@ -511,9 +599,7 @@ const AgentuityBundler: BunPlugin = {
|
|
|
511
599
|
// 1. Evals are already imported when agents are registered (see line 421-422)
|
|
512
600
|
// 2. The registry is for type definitions only, not runtime execution
|
|
513
601
|
// 3. Importing it causes bundler resolution issues since it's generated during build
|
|
514
|
-
|
|
515
|
-
generateAgentRegistry(srcDir, agentInfo);
|
|
516
|
-
}
|
|
602
|
+
generateAgentRegistry(srcDir, agentInfo);
|
|
517
603
|
|
|
518
604
|
// create the workbench routes
|
|
519
605
|
inserts.push(`await (async() => {
|
|
@@ -53,42 +53,46 @@ export const listSubcommand = createSubcommand({
|
|
|
53
53
|
},
|
|
54
54
|
async handler(ctx) {
|
|
55
55
|
const projectId = resolveProjectId(ctx, { projectId: ctx.opts['project-id'] });
|
|
56
|
-
const { apiClient, opts } = ctx;
|
|
56
|
+
const { apiClient, opts, options } = ctx;
|
|
57
57
|
|
|
58
58
|
try {
|
|
59
59
|
const deployments = await projectDeploymentList(apiClient, projectId, opts.count);
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
tui.info('No deployments found.');
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const tableData = deployments.map((d) => ({
|
|
67
|
-
ID: d.id,
|
|
68
|
-
State: d.state || 'unknown',
|
|
69
|
-
Active: d.active ? 'Yes' : '',
|
|
70
|
-
Created: new Date(d.createdAt).toLocaleString(),
|
|
71
|
-
Message: d.message || '',
|
|
72
|
-
Tags: d.tags.join(', '),
|
|
73
|
-
}));
|
|
74
|
-
|
|
75
|
-
tui.table(tableData, [
|
|
76
|
-
{ name: 'ID', alignment: 'left' },
|
|
77
|
-
{ name: 'State', alignment: 'left' },
|
|
78
|
-
{ name: 'Active', alignment: 'center' },
|
|
79
|
-
{ name: 'Created', alignment: 'left' },
|
|
80
|
-
{ name: 'Message', alignment: 'left' },
|
|
81
|
-
{ name: 'Tags', alignment: 'left' },
|
|
82
|
-
]);
|
|
83
|
-
|
|
84
|
-
return deployments.map((d) => ({
|
|
61
|
+
const result = deployments.map((d) => ({
|
|
85
62
|
id: d.id,
|
|
86
63
|
state: d.state,
|
|
87
64
|
active: d.active,
|
|
88
65
|
createdAt: d.createdAt,
|
|
89
|
-
message: d.message,
|
|
66
|
+
message: d.message ?? undefined,
|
|
90
67
|
tags: d.tags,
|
|
91
68
|
}));
|
|
69
|
+
|
|
70
|
+
// Skip TUI output in JSON mode
|
|
71
|
+
if (!options.json) {
|
|
72
|
+
if (deployments.length === 0) {
|
|
73
|
+
tui.info('No deployments found.');
|
|
74
|
+
} else {
|
|
75
|
+
const tableData = deployments.map((d) => ({
|
|
76
|
+
ID: d.id,
|
|
77
|
+
State: d.state || 'unknown',
|
|
78
|
+
Active: d.active ? 'Yes' : '',
|
|
79
|
+
Created: new Date(d.createdAt).toLocaleString(),
|
|
80
|
+
Message: d.message || '',
|
|
81
|
+
Tags: d.tags.join(', '),
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
tui.table(tableData, [
|
|
85
|
+
{ name: 'ID', alignment: 'left' },
|
|
86
|
+
{ name: 'State', alignment: 'left' },
|
|
87
|
+
{ name: 'Active', alignment: 'center' },
|
|
88
|
+
{ name: 'Created', alignment: 'left' },
|
|
89
|
+
{ name: 'Message', alignment: 'left' },
|
|
90
|
+
{ name: 'Tags', alignment: 'left' },
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return result;
|
|
92
96
|
} catch (ex) {
|
|
93
97
|
tui.fatal(
|
|
94
98
|
`Failed to list deployments: ${ex instanceof Error ? ex.message : String(ex)}`,
|