@agentuity/cli 0.0.52 → 0.0.54
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 +159 -0
- 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 +77 -16
- 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 +3 -1
- 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 +158 -63
- 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 +2 -0
- 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 +203 -0
- package/src/cmd/build/bundler.ts +81 -15
- package/src/cmd/build/index.ts +3 -1
- package/src/cmd/build/plugin.ts +186 -68
- 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 +2 -0
- 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
|
@@ -897,3 +897,206 @@ export async function parseRoute(
|
|
|
897
897
|
}
|
|
898
898
|
return routes;
|
|
899
899
|
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Configuration extracted from createWorkbench call
|
|
903
|
+
*/
|
|
904
|
+
export interface WorkbenchConfig {
|
|
905
|
+
route: string;
|
|
906
|
+
headers?: Record<string, string>;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Result of workbench analysis
|
|
911
|
+
*/
|
|
912
|
+
export interface WorkbenchAnalysis {
|
|
913
|
+
hasWorkbench: boolean;
|
|
914
|
+
config: WorkbenchConfig | null;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Check if a TypeScript file actively uses a specific function
|
|
919
|
+
* (ignores comments and unused imports)
|
|
920
|
+
*
|
|
921
|
+
* @param content - The TypeScript source code
|
|
922
|
+
* @param functionName - The function name to check for (e.g., 'createWorkbench')
|
|
923
|
+
* @returns true if the function is both imported and called
|
|
924
|
+
*/
|
|
925
|
+
export async function checkFunctionUsage(content: string, functionName: string): Promise<boolean> {
|
|
926
|
+
try {
|
|
927
|
+
const ts = await import('typescript');
|
|
928
|
+
const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
|
|
929
|
+
|
|
930
|
+
let hasImport = false;
|
|
931
|
+
let hasUsage = false;
|
|
932
|
+
|
|
933
|
+
function visitNode(node: import('typescript').Node): void {
|
|
934
|
+
// Check for import declarations with the function
|
|
935
|
+
if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
|
|
936
|
+
if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
937
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
938
|
+
if (element.name.text === functionName) {
|
|
939
|
+
hasImport = true;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
// Check for function calls
|
|
945
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
946
|
+
if (node.expression.text === functionName) {
|
|
947
|
+
hasUsage = true;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
// Recursively visit child nodes
|
|
951
|
+
ts.forEachChild(node, visitNode);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
visitNode(sourceFile);
|
|
955
|
+
// Only return true if both import and usage are present
|
|
956
|
+
return hasImport && hasUsage;
|
|
957
|
+
} catch (error) {
|
|
958
|
+
// Fallback to string check if AST parsing fails
|
|
959
|
+
console.warn(`AST parsing failed for ${functionName}, falling back to string check:`, error);
|
|
960
|
+
return content.includes(functionName);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Check if app.ts contains conflicting routes for a given endpoint
|
|
966
|
+
*/
|
|
967
|
+
export async function checkRouteConflicts(
|
|
968
|
+
content: string,
|
|
969
|
+
workbenchEndpoint: string
|
|
970
|
+
): Promise<boolean> {
|
|
971
|
+
try {
|
|
972
|
+
const ts = await import('typescript');
|
|
973
|
+
const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
|
|
974
|
+
|
|
975
|
+
let hasConflict = false;
|
|
976
|
+
|
|
977
|
+
function visitNode(node: import('typescript').Node): void {
|
|
978
|
+
// Check for router.get calls
|
|
979
|
+
if (
|
|
980
|
+
ts.isCallExpression(node) &&
|
|
981
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
982
|
+
ts.isIdentifier(node.expression.name) &&
|
|
983
|
+
node.expression.name.text === 'get'
|
|
984
|
+
) {
|
|
985
|
+
// Check if first argument is the workbench endpoint
|
|
986
|
+
if (node.arguments.length > 0 && ts.isStringLiteral(node.arguments[0])) {
|
|
987
|
+
if (node.arguments[0].text === workbenchEndpoint) {
|
|
988
|
+
hasConflict = true;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
ts.forEachChild(node, visitNode);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
visitNode(sourceFile);
|
|
997
|
+
return hasConflict;
|
|
998
|
+
} catch (_error) {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Analyze workbench usage and extract configuration
|
|
1005
|
+
*
|
|
1006
|
+
* @param content - The TypeScript source code
|
|
1007
|
+
* @returns workbench analysis including usage and config
|
|
1008
|
+
*/
|
|
1009
|
+
export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalysis> {
|
|
1010
|
+
try {
|
|
1011
|
+
const ts = await import('typescript');
|
|
1012
|
+
const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
|
|
1013
|
+
|
|
1014
|
+
let hasImport = false;
|
|
1015
|
+
let hasUsage = false;
|
|
1016
|
+
let config: WorkbenchConfig | null = null;
|
|
1017
|
+
|
|
1018
|
+
function visitNode(node: import('typescript').Node): void {
|
|
1019
|
+
// Check for import declarations with createWorkbench
|
|
1020
|
+
if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
|
|
1021
|
+
if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
1022
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
1023
|
+
if (element.name.text === 'createWorkbench') {
|
|
1024
|
+
hasImport = true;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Check for createWorkbench function calls and extract config
|
|
1031
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
1032
|
+
if (node.expression.text === 'createWorkbench') {
|
|
1033
|
+
hasUsage = true;
|
|
1034
|
+
|
|
1035
|
+
// Extract configuration from the first argument (if any)
|
|
1036
|
+
if (node.arguments.length > 0) {
|
|
1037
|
+
const configArg = node.arguments[0];
|
|
1038
|
+
config = parseConfigObject(configArg, ts);
|
|
1039
|
+
} else {
|
|
1040
|
+
// Default config if no arguments provided
|
|
1041
|
+
config = { route: '/workbench' };
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// Recursively visit child nodes
|
|
1047
|
+
ts.forEachChild(node, visitNode);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
visitNode(sourceFile);
|
|
1051
|
+
|
|
1052
|
+
// Set default config if workbench is used but no config was parsed
|
|
1053
|
+
if (hasImport && hasUsage && !config) {
|
|
1054
|
+
config = { route: '/workbench' };
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
return {
|
|
1058
|
+
hasWorkbench: hasImport && hasUsage,
|
|
1059
|
+
config: config,
|
|
1060
|
+
};
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
// Fallback to simple check if AST parsing fails
|
|
1063
|
+
console.warn('Workbench AST parsing failed, falling back to string check:', error);
|
|
1064
|
+
const hasWorkbench = content.includes('createWorkbench');
|
|
1065
|
+
return {
|
|
1066
|
+
hasWorkbench,
|
|
1067
|
+
config: hasWorkbench ? { route: '/workbench' } : null,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Parse a TypeScript object literal to extract configuration
|
|
1074
|
+
*/
|
|
1075
|
+
function parseConfigObject(
|
|
1076
|
+
node: import('typescript').Node,
|
|
1077
|
+
ts: typeof import('typescript')
|
|
1078
|
+
): WorkbenchConfig | null {
|
|
1079
|
+
if (!ts.isObjectLiteralExpression(node)) {
|
|
1080
|
+
return { route: '/workbench' }; // Default config
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const config: WorkbenchConfig = { route: '/workbench' };
|
|
1084
|
+
|
|
1085
|
+
for (const property of node.properties) {
|
|
1086
|
+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) {
|
|
1087
|
+
const propertyName = property.name.text;
|
|
1088
|
+
|
|
1089
|
+
if (propertyName === 'route' && ts.isStringLiteral(property.initializer)) {
|
|
1090
|
+
config.route = property.initializer.text;
|
|
1091
|
+
} else if (
|
|
1092
|
+
propertyName === 'headers' &&
|
|
1093
|
+
ts.isObjectLiteralExpression(property.initializer)
|
|
1094
|
+
) {
|
|
1095
|
+
// Parse headers object if needed (not implemented for now)
|
|
1096
|
+
config.headers = {};
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return config;
|
|
1102
|
+
}
|
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)) {
|
|
@@ -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
|
@@ -47,7 +47,9 @@ export const command = createCommand({
|
|
|
47
47
|
tui.info('Running type check...');
|
|
48
48
|
const { resolve } = await import('node:path');
|
|
49
49
|
const absoluteProjectDir = resolve(projectDir);
|
|
50
|
-
const result = await Bun.$`bunx tsc --noEmit --skipLibCheck
|
|
50
|
+
const result = await Bun.$`bunx tsc --noEmit --skipLibCheck`
|
|
51
|
+
.cwd(absoluteProjectDir)
|
|
52
|
+
.nothrow();
|
|
51
53
|
|
|
52
54
|
if (result.exitCode === 0) {
|
|
53
55
|
tui.success('Type check passed');
|
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import type { BunPlugin } from 'bun';
|
|
2
2
|
import { dirname, basename, join, resolve } from 'node:path';
|
|
3
|
-
import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
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);
|
|
@@ -123,87 +174,63 @@ function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, s
|
|
|
123
174
|
})
|
|
124
175
|
.join('\n');
|
|
125
176
|
|
|
126
|
-
// Generate nested agent type definitions
|
|
127
|
-
const
|
|
177
|
+
// Generate nested agent type definitions for Hono Context augmentation
|
|
178
|
+
const honoAgentTypeLines: string[] = [];
|
|
128
179
|
for (const parentAgent of parentAgents) {
|
|
129
180
|
const parentCamelName = toCamelCase(parentAgent.name);
|
|
130
181
|
const children = subagentsByParent.get(parentAgent.name) || [];
|
|
131
182
|
|
|
132
183
|
if (children.length === 0) {
|
|
133
184
|
// No subagents
|
|
134
|
-
|
|
135
|
-
` ${parentCamelName}: AgentRunner<
|
|
185
|
+
honoAgentTypeLines.push(
|
|
186
|
+
` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false>;`
|
|
136
187
|
);
|
|
137
188
|
} else {
|
|
138
189
|
// Has subagents - create intersection type
|
|
139
|
-
|
|
140
|
-
` ${parentCamelName}: AgentRunner<
|
|
190
|
+
honoAgentTypeLines.push(
|
|
191
|
+
` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false> & {`
|
|
141
192
|
);
|
|
142
193
|
for (const child of children) {
|
|
143
194
|
const childCamelName = toCamelCase(child.name);
|
|
144
195
|
const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
|
|
145
|
-
|
|
196
|
+
honoAgentTypeLines.push(
|
|
146
197
|
` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`
|
|
147
198
|
);
|
|
148
199
|
}
|
|
149
|
-
|
|
200
|
+
honoAgentTypeLines.push(` };`);
|
|
150
201
|
}
|
|
151
202
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
const generatedContent = `/// <reference types="hono" />
|
|
155
|
-
// Auto-generated by Agentuity - do not edit manually
|
|
156
|
-
${imports}${evalsImportsStr}
|
|
157
|
-
import type { AgentRunner, Logger } from '@agentuity/runtime';
|
|
158
|
-
import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
|
|
159
|
-
|
|
160
|
-
export const agentRegistry = {
|
|
161
|
-
${registry}
|
|
162
|
-
} as const;
|
|
163
|
-
|
|
164
|
-
export type AgentName = keyof typeof agentRegistry;
|
|
165
|
-
export type AgentRegistry = typeof agentRegistry;
|
|
203
|
+
const honoAgentTypes = honoAgentTypeLines.join('\n');
|
|
166
204
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
interface Context {
|
|
173
|
-
agentName: AgentName;
|
|
174
|
-
agent: {
|
|
175
|
-
${agentTypes}
|
|
176
|
-
};
|
|
177
|
-
waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
|
|
178
|
-
logger: Logger;
|
|
179
|
-
kv: KeyValueStorage;
|
|
180
|
-
objectstore: ObjectStorage;
|
|
181
|
-
stream: StreamStorage;
|
|
182
|
-
vector: VectorStorage;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
`;
|
|
186
|
-
|
|
187
|
-
const agentsDir = join(srcDir, 'agents');
|
|
188
|
-
const registryPath = join(agentsDir, 'registry.generated.ts');
|
|
205
|
+
// Generate agent type definitions for AgentRegistry interface augmentation
|
|
206
|
+
const runtimeAgentTypeLines: string[] = [];
|
|
207
|
+
for (const parentAgent of parentAgents) {
|
|
208
|
+
const parentCamelName = toCamelCase(parentAgent.name);
|
|
209
|
+
const children = subagentsByParent.get(parentAgent.name) || [];
|
|
189
210
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
if (children.length === 0) {
|
|
212
|
+
// No subagents - use typeof the imported agent
|
|
213
|
+
runtimeAgentTypeLines.push(
|
|
214
|
+
` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false>;`
|
|
215
|
+
);
|
|
216
|
+
} else {
|
|
217
|
+
// Has subagents - create intersection type using typeof
|
|
218
|
+
runtimeAgentTypeLines.push(
|
|
219
|
+
` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false> & {`
|
|
220
|
+
);
|
|
221
|
+
for (const child of children) {
|
|
222
|
+
const childCamelName = toCamelCase(child.name);
|
|
223
|
+
const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
|
|
224
|
+
runtimeAgentTypeLines.push(
|
|
225
|
+
` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
runtimeAgentTypeLines.push(` };`);
|
|
229
|
+
}
|
|
193
230
|
}
|
|
194
|
-
|
|
195
|
-
writeFileSync(registryPath, generatedContent, 'utf-8');
|
|
231
|
+
const runtimeAgentTypes = runtimeAgentTypeLines.join('\n');
|
|
196
232
|
|
|
197
233
|
// Generate React client types with nested structure
|
|
198
|
-
const clientImports = agentInfo
|
|
199
|
-
.map(({ name, path, parent }) => {
|
|
200
|
-
const fullName = parent ? `${parent}_${name}` : name;
|
|
201
|
-
const camelName = toCamelCase(fullName);
|
|
202
|
-
const relativePath = path.replace(/^\.\/agents\//, './');
|
|
203
|
-
return `import type ${camelName}Agent from '${relativePath}';`;
|
|
204
|
-
})
|
|
205
|
-
.join('\n');
|
|
206
|
-
|
|
207
234
|
const clientAgentTypeLines: string[] = [];
|
|
208
235
|
for (const parentAgent of parentAgents) {
|
|
209
236
|
const parentCamelName = toCamelCase(parentAgent.name);
|
|
@@ -227,21 +254,74 @@ ${agentTypes}
|
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
}
|
|
257
|
+
const reactAgentTypes = clientAgentTypeLines.join('\n');
|
|
230
258
|
|
|
231
|
-
const
|
|
232
|
-
//
|
|
233
|
-
${
|
|
259
|
+
const generatedContent = `/// <reference types="hono" />
|
|
260
|
+
// Auto-generated by Agentuity - do not edit manually
|
|
261
|
+
${imports}${evalsImportsStr}
|
|
262
|
+
import type { AgentRunner, Logger } from '@agentuity/runtime';
|
|
263
|
+
import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
|
|
234
264
|
import type { Agent } from '@agentuity/react';
|
|
235
265
|
|
|
266
|
+
export const agentRegistry = {
|
|
267
|
+
${registry}
|
|
268
|
+
} as const;
|
|
269
|
+
|
|
270
|
+
// Local type aliases for Hono augmentation
|
|
271
|
+
type LocalAgentName = keyof typeof agentRegistry;
|
|
272
|
+
type LocalAgentRegistry = typeof agentRegistry;
|
|
273
|
+
|
|
274
|
+
// Typed runners for each agent
|
|
275
|
+
${typeExports}
|
|
276
|
+
|
|
277
|
+
// Augment @agentuity/runtime types with strongly-typed agents from this project
|
|
278
|
+
declare module "@agentuity/runtime" {
|
|
279
|
+
// Augment the AgentRegistry interface with project-specific strongly-typed agents
|
|
280
|
+
export interface AgentRegistry {
|
|
281
|
+
${runtimeAgentTypes}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Augment Hono Context to provide strongly-typed agents and runtime services
|
|
286
|
+
// Note: Properties are added to Context via middleware in @agentuity/runtime
|
|
287
|
+
declare module "hono" {
|
|
288
|
+
interface Context {
|
|
289
|
+
agentName: LocalAgentName;
|
|
290
|
+
agent: {
|
|
291
|
+
${honoAgentTypes}
|
|
292
|
+
};
|
|
293
|
+
waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
|
|
294
|
+
logger: Logger;
|
|
295
|
+
kv: KeyValueStorage;
|
|
296
|
+
objectstore: ObjectStorage;
|
|
297
|
+
stream: StreamStorage;
|
|
298
|
+
vector: VectorStorage;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Augment @agentuity/react types with strongly-typed agents from this project
|
|
236
303
|
declare module '@agentuity/react' {
|
|
237
304
|
interface AgentRegistry {
|
|
238
|
-
${
|
|
305
|
+
${reactAgentTypes}
|
|
239
306
|
}
|
|
240
307
|
}
|
|
241
308
|
`;
|
|
242
309
|
|
|
243
|
-
const
|
|
244
|
-
|
|
310
|
+
const agentsDir = join(srcDir, 'agents');
|
|
311
|
+
const registryPath = join(agentsDir, 'registry.generated.ts');
|
|
312
|
+
const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
|
|
313
|
+
|
|
314
|
+
// Ensure agents directory exists
|
|
315
|
+
if (!existsSync(agentsDir)) {
|
|
316
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
writeFileSync(registryPath, generatedContent, 'utf-8');
|
|
320
|
+
|
|
321
|
+
// Remove legacy types.generated.d.ts if it exists (now consolidated into registry.generated.ts)
|
|
322
|
+
if (existsSync(legacyTypesPath)) {
|
|
323
|
+
unlinkSync(legacyTypesPath);
|
|
324
|
+
}
|
|
245
325
|
}
|
|
246
326
|
|
|
247
327
|
let metadata: Partial<BuildMetadata>;
|
|
@@ -261,6 +341,10 @@ const AgentuityBundler: BunPlugin = {
|
|
|
261
341
|
const deploymentId = build.config.define?.['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID']
|
|
262
342
|
? JSON.parse(build.config.define['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID'])
|
|
263
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';
|
|
264
348
|
const routes: Set<string> = new Set();
|
|
265
349
|
const agentInfo: Array<Record<string, string>> = [];
|
|
266
350
|
const agentMetadata: Map<string, Map<string, string>> = new Map<
|
|
@@ -463,11 +547,45 @@ const AgentuityBundler: BunPlugin = {
|
|
|
463
547
|
const indexFile = join(srcDir, 'web', 'index.html');
|
|
464
548
|
|
|
465
549
|
if (existsSync(indexFile)) {
|
|
550
|
+
// Setup workbench configuration - evaluate fresh each time during builds
|
|
551
|
+
const workbenchConfig = await setupWorkbench(srcDir);
|
|
552
|
+
|
|
466
553
|
inserts.unshift(`await (async () => {
|
|
467
554
|
const { serveStatic } = require('hono/bun');
|
|
468
|
-
const { getRouter } = await import('@agentuity/runtime');
|
|
555
|
+
const { getRouter, registerDevModeRoutes } = await import('@agentuity/runtime');
|
|
469
556
|
const router = getRouter()!;
|
|
470
|
-
|
|
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
|
+
}
|
|
471
589
|
const webstatic = serveStatic({ root: import.meta.dir + '/web' });
|
|
472
590
|
router.get('/', (c) => c.html(index));
|
|
473
591
|
router.get('/web/chunk/*', webstatic);
|