@agentuity/cli 0.0.54 → 0.0.56
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/ai/schema/generate.d.ts +3 -0
- package/dist/cmd/ai/schema/generate.d.ts.map +1 -0
- package/dist/cmd/ai/schema/generate.js +50 -0
- package/dist/cmd/ai/schema/generate.js.map +1 -0
- package/dist/cmd/ai/schema/index.d.ts.map +1 -1
- package/dist/cmd/ai/schema/index.js +2 -1
- package/dist/cmd/ai/schema/index.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +107 -86
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/ast.test.js +135 -370
- package/dist/cmd/build/ast.test.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +9 -6
- 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 +10 -4
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/cloud/session/get.d.ts.map +1 -1
- package/dist/cmd/cloud/session/get.js +77 -17
- package/dist/cmd/cloud/session/get.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +32 -14
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +1 -0
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +27 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +31 -3
- package/dist/config.js.map +1 -1
- package/dist/types.d.ts +24 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -75
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/src/cmd/ai/schema/generate.ts +64 -0
- package/src/cmd/ai/schema/index.ts +2 -1
- package/src/cmd/build/ast.test.ts +157 -549
- package/src/cmd/build/ast.ts +121 -94
- package/src/cmd/build/bundler.ts +9 -6
- package/src/cmd/build/index.ts +2 -0
- package/src/cmd/build/plugin.ts +11 -4
- package/src/cmd/cloud/session/get.ts +91 -19
- package/src/cmd/dev/index.ts +39 -14
- package/src/cmd/project/template-flow.ts +1 -0
- package/src/config.ts +44 -5
- package/src/types.ts +5 -84
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);
|
|
@@ -789,111 +790,137 @@ export async function parseRoute(
|
|
|
789
790
|
const routes: RouteDefinition = [];
|
|
790
791
|
const routePrefix = filename.includes('src/agents') ? '/agent' : '/api';
|
|
791
792
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
793
|
+
try {
|
|
794
|
+
for (const body of ast.body) {
|
|
795
|
+
if (body.type === 'ExpressionStatement') {
|
|
796
|
+
const statement = body as ASTExpressionStatement;
|
|
797
|
+
|
|
798
|
+
// Validate that the expression is a call expression (e.g. function call)
|
|
799
|
+
if (statement.expression.type !== 'CallExpression') {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const callee = statement.expression.callee;
|
|
804
|
+
|
|
805
|
+
// Validate that the callee is a member expression (e.g. object.method())
|
|
806
|
+
// This handles cases like 'console.log()' or 'router.get()'
|
|
807
|
+
// direct function calls like 'myFunc()' have type 'Identifier' and will be skipped
|
|
808
|
+
if (callee.type !== 'MemberExpression') {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (callee.object.type === 'Identifier' && statement.expression.arguments?.length > 0) {
|
|
813
|
+
const identifier = callee.object as ASTNodeIdentifier;
|
|
814
|
+
if (identifier.name === variableName) {
|
|
815
|
+
let method = (callee.property as ASTNodeIdentifier).name;
|
|
816
|
+
let type = 'api';
|
|
817
|
+
const action = statement.expression.arguments[0];
|
|
818
|
+
let suffix = '';
|
|
819
|
+
let config: Record<string, unknown> | undefined;
|
|
820
|
+
switch (method) {
|
|
821
|
+
case 'get':
|
|
822
|
+
case 'put':
|
|
823
|
+
case 'post':
|
|
824
|
+
case 'patch':
|
|
825
|
+
case 'delete': {
|
|
826
|
+
if (action && (action as ASTLiteral).type === 'Literal') {
|
|
827
|
+
suffix = (action as ASTLiteral).value;
|
|
828
|
+
} else {
|
|
829
|
+
throw new Error(
|
|
830
|
+
`unsupported HTTP method ${method} in ${filename} at line ${body.start}`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
813
833
|
break;
|
|
814
834
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
835
|
+
case 'stream':
|
|
836
|
+
case 'sse':
|
|
837
|
+
case 'websocket': {
|
|
838
|
+
type = method;
|
|
839
|
+
method = 'post';
|
|
840
|
+
const theaction = action as ASTLiteral;
|
|
841
|
+
if (theaction.type === 'Literal') {
|
|
842
|
+
suffix = theaction.value;
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
825
845
|
break;
|
|
826
846
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
847
|
+
case 'sms': {
|
|
848
|
+
type = method;
|
|
849
|
+
method = 'post';
|
|
850
|
+
const theaction = action as ASTObjectExpression;
|
|
851
|
+
if (theaction.type === 'ObjectExpression') {
|
|
852
|
+
config = {};
|
|
853
|
+
theaction.properties.forEach((p) => {
|
|
854
|
+
if (p.value.type === 'Literal') {
|
|
855
|
+
const literal = p.value as ASTLiteral;
|
|
856
|
+
config![p.key.name] = literal.value;
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
const number = theaction.properties.find((p) => p.key.name === 'number');
|
|
860
|
+
if (number && number.value.type === 'Literal') {
|
|
861
|
+
const phoneNumber = number.value as ASTLiteral;
|
|
862
|
+
suffix = hash(phoneNumber.value);
|
|
863
|
+
break;
|
|
839
864
|
}
|
|
840
|
-
});
|
|
841
|
-
const number = theaction.properties.find((p) => p.key.name === 'number');
|
|
842
|
-
if (number && number.value.type === 'Literal') {
|
|
843
|
-
const phoneNumber = number.value as ASTLiteral;
|
|
844
|
-
suffix = hash(phoneNumber.value);
|
|
845
|
-
break;
|
|
846
865
|
}
|
|
866
|
+
break;
|
|
847
867
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
868
|
+
case 'email': {
|
|
869
|
+
type = method;
|
|
870
|
+
method = 'post';
|
|
871
|
+
const theaction = action as ASTLiteral;
|
|
872
|
+
if (theaction.type === 'Literal') {
|
|
873
|
+
const email = theaction.value;
|
|
874
|
+
suffix = hash(email);
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
857
877
|
break;
|
|
858
878
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
879
|
+
case 'cron': {
|
|
880
|
+
type = method;
|
|
881
|
+
method = 'post';
|
|
882
|
+
const theaction = action as ASTLiteral;
|
|
883
|
+
if (theaction.type === 'Literal') {
|
|
884
|
+
const number = theaction.value;
|
|
885
|
+
suffix = hash(number);
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
868
888
|
break;
|
|
869
889
|
}
|
|
870
|
-
|
|
890
|
+
default: {
|
|
891
|
+
throw new Error(
|
|
892
|
+
`unsupported router method ${method} in ${filename} at line ${body.start}`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
871
895
|
}
|
|
896
|
+
const thepath = `${routePrefix}/${routeName}/${suffix}`
|
|
897
|
+
.replaceAll(/\/{2,}/g, '/')
|
|
898
|
+
.replaceAll(/\/$/g, '');
|
|
899
|
+
const id = generateRouteId(
|
|
900
|
+
projectId,
|
|
901
|
+
deploymentId,
|
|
902
|
+
type,
|
|
903
|
+
method,
|
|
904
|
+
rel,
|
|
905
|
+
thepath,
|
|
906
|
+
version
|
|
907
|
+
);
|
|
908
|
+
routes.push({
|
|
909
|
+
id,
|
|
910
|
+
method: method as 'get' | 'post' | 'put' | 'delete' | 'patch',
|
|
911
|
+
type: type as 'api' | 'sms' | 'email' | 'cron',
|
|
912
|
+
filename: rel,
|
|
913
|
+
path: thepath,
|
|
914
|
+
version,
|
|
915
|
+
config,
|
|
916
|
+
});
|
|
872
917
|
}
|
|
873
|
-
const thepath = `${routePrefix}/${routeName}/${suffix}`
|
|
874
|
-
.replaceAll(/\/{2,}/g, '/')
|
|
875
|
-
.replaceAll(/\/$/g, '');
|
|
876
|
-
const id = generateRouteId(
|
|
877
|
-
projectId,
|
|
878
|
-
deploymentId,
|
|
879
|
-
type,
|
|
880
|
-
method,
|
|
881
|
-
rel,
|
|
882
|
-
thepath,
|
|
883
|
-
version
|
|
884
|
-
);
|
|
885
|
-
routes.push({
|
|
886
|
-
id,
|
|
887
|
-
method: method as 'get' | 'post' | 'put' | 'delete' | 'patch',
|
|
888
|
-
type: type as 'api' | 'sms' | 'email' | 'cron',
|
|
889
|
-
filename: rel,
|
|
890
|
-
path: thepath,
|
|
891
|
-
version,
|
|
892
|
-
config,
|
|
893
|
-
});
|
|
894
918
|
}
|
|
895
919
|
}
|
|
896
920
|
}
|
|
921
|
+
} catch (error) {
|
|
922
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
923
|
+
throw new Error(`Failed to parse route file ${filename}: ${err.message}`);
|
|
897
924
|
}
|
|
898
925
|
return routes;
|
|
899
926
|
}
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -50,7 +50,7 @@ export async function bundle({
|
|
|
50
50
|
}
|
|
51
51
|
const files = await getFilesRecursively(dir);
|
|
52
52
|
for (const filename of files) {
|
|
53
|
-
if (/\.[jt]s?$/.test(filename)) {
|
|
53
|
+
if (/\.[jt]s?$/.test(filename) && !filename.includes('.generated.')) {
|
|
54
54
|
appEntrypoints.push(filename);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -358,13 +358,16 @@ export async function bundle({
|
|
|
358
358
|
.map((s) => s.trim())
|
|
359
359
|
.filter(Boolean);
|
|
360
360
|
}
|
|
361
|
-
|
|
362
|
-
if (branch) {
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
365
|
-
|
|
361
|
+
let branch = process.env.GITHUB_HEAD_REF;
|
|
362
|
+
if (!branch) {
|
|
363
|
+
const branchText = $`git branch --show-current`.nothrow().quiet();
|
|
364
|
+
if (branchText) {
|
|
365
|
+
branch = await branchText.text();
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
|
+
if (branch) {
|
|
369
|
+
buildmetadata.deployment.git.branch = branch.trim();
|
|
370
|
+
}
|
|
368
371
|
const commit = $`git rev-parse HEAD`.nothrow().quiet();
|
|
369
372
|
if (commit) {
|
|
370
373
|
const sha = await commit.text();
|
package/src/cmd/build/index.ts
CHANGED
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -493,7 +493,6 @@ const AgentuityBundler: BunPlugin = {
|
|
|
493
493
|
projectId,
|
|
494
494
|
deploymentId
|
|
495
495
|
);
|
|
496
|
-
routeDefinitions = [...routeDefinitions, ...definitions];
|
|
497
496
|
|
|
498
497
|
let agentDetail: Record<string, string> = {};
|
|
499
498
|
|
|
@@ -504,6 +503,7 @@ const AgentuityBundler: BunPlugin = {
|
|
|
504
503
|
}
|
|
505
504
|
agentDetail = {
|
|
506
505
|
name,
|
|
506
|
+
id: md.get('id')!,
|
|
507
507
|
path: `.${agent}`,
|
|
508
508
|
filename: md.get('filename')!,
|
|
509
509
|
identifier: md.get('identifier')!,
|
|
@@ -514,8 +514,14 @@ const AgentuityBundler: BunPlugin = {
|
|
|
514
514
|
agentDetail.parent = parentName;
|
|
515
515
|
}
|
|
516
516
|
agentInfo.push(agentDetail);
|
|
517
|
+
for (const def of definitions) {
|
|
518
|
+
def.agentIds = [agentDetail.agentId, agentDetail.id];
|
|
519
|
+
}
|
|
517
520
|
}
|
|
518
521
|
|
|
522
|
+
// do this after handling the agent association (if any)
|
|
523
|
+
routeDefinitions = [...routeDefinitions, ...definitions];
|
|
524
|
+
|
|
519
525
|
let buffer = `await (async() => {
|
|
520
526
|
const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
|
|
521
527
|
const router = getRouter()!;
|
|
@@ -599,9 +605,7 @@ if (route !== '/workbench') {
|
|
|
599
605
|
// 1. Evals are already imported when agents are registered (see line 421-422)
|
|
600
606
|
// 2. The registry is for type definitions only, not runtime execution
|
|
601
607
|
// 3. Importing it causes bundler resolution issues since it's generated during build
|
|
602
|
-
|
|
603
|
-
generateAgentRegistry(srcDir, agentInfo);
|
|
604
|
-
}
|
|
608
|
+
generateAgentRegistry(srcDir, agentInfo);
|
|
605
609
|
|
|
606
610
|
// create the workbench routes
|
|
607
611
|
inserts.push(`await (async() => {
|
|
@@ -659,6 +663,9 @@ if (route !== '/workbench') {
|
|
|
659
663
|
if (!v.has('name')) {
|
|
660
664
|
throw new Error('agent metadata is missing expected name property');
|
|
661
665
|
}
|
|
666
|
+
if (!v.has('agentId')) {
|
|
667
|
+
throw new Error('agent metadata is missing expected agentId property');
|
|
668
|
+
}
|
|
662
669
|
|
|
663
670
|
const parentName = v.get('parent');
|
|
664
671
|
if (parentName) {
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createSubcommand } from '../../../types';
|
|
3
3
|
import * as tui from '../../../tui';
|
|
4
|
-
import { sessionGet } from '@agentuity/server';
|
|
4
|
+
import { sessionGet, type SpanNode } from '@agentuity/server';
|
|
5
5
|
import { getCommand } from '../../../command-prefix';
|
|
6
6
|
import { ErrorCode } from '../../../errors';
|
|
7
7
|
import { getCatalystAPIClient } from '../../../config';
|
|
8
8
|
|
|
9
|
+
const SpanNodeSchema: z.ZodType<SpanNode> = z.lazy(() =>
|
|
10
|
+
z.object({
|
|
11
|
+
id: z.string().describe('Span ID'),
|
|
12
|
+
duration: z.number().describe('Duration in milliseconds'),
|
|
13
|
+
operation: z.string().describe('Operation name'),
|
|
14
|
+
attributes: z.record(z.string(), z.unknown()).describe('Span attributes'),
|
|
15
|
+
children: z.array(SpanNodeSchema).optional().describe('Child spans'),
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const RouteInfoSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
id: z.string().describe('Route ID'),
|
|
22
|
+
method: z.string().describe('HTTP method'),
|
|
23
|
+
path: z.string().describe('Route path'),
|
|
24
|
+
})
|
|
25
|
+
.nullable();
|
|
26
|
+
|
|
9
27
|
const SessionGetResponseSchema = z.object({
|
|
10
28
|
id: z.string().describe('Session ID'),
|
|
11
29
|
created_at: z.string().describe('Creation timestamp'),
|
|
@@ -26,8 +44,15 @@ const SessionGetResponseSchema = z.object({
|
|
|
26
44
|
url: z.string().describe('Request URL'),
|
|
27
45
|
route_id: z.string().describe('Route ID'),
|
|
28
46
|
thread_id: z.string().describe('Thread ID'),
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
agents: z
|
|
48
|
+
.array(
|
|
49
|
+
z.object({
|
|
50
|
+
name: z.string(),
|
|
51
|
+
identifier: z.string(),
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
.describe('Agents'),
|
|
55
|
+
eval_runs: z
|
|
31
56
|
.array(
|
|
32
57
|
z.object({
|
|
33
58
|
id: z.string(),
|
|
@@ -40,8 +65,41 @@ const SessionGetResponseSchema = z.object({
|
|
|
40
65
|
})
|
|
41
66
|
)
|
|
42
67
|
.describe('Eval runs'),
|
|
68
|
+
timeline: SpanNodeSchema.nullable().optional().describe('Session timeline'),
|
|
69
|
+
route: RouteInfoSchema.optional().describe('Route information'),
|
|
43
70
|
});
|
|
44
71
|
|
|
72
|
+
function formatDuration(ms: number): string {
|
|
73
|
+
if (ms < 1) {
|
|
74
|
+
return `${(ms * 1000).toFixed(0)}µs`;
|
|
75
|
+
}
|
|
76
|
+
if (ms < 1000) {
|
|
77
|
+
return `${ms.toFixed(1)}ms`;
|
|
78
|
+
}
|
|
79
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function printTimeline(node: SpanNode, prefix: string, isLast = true): void {
|
|
83
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
84
|
+
const duration = tui.muted(`(${formatDuration(node.duration)})`);
|
|
85
|
+
let extra = '';
|
|
86
|
+
if (node.operation.startsWith('agentuity.')) {
|
|
87
|
+
if ('name' in node.attributes && 'key' in node.attributes) {
|
|
88
|
+
extra = tui.colorSuccess(`${node.attributes.name} ${node.attributes.key}`) + ' ';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (node.operation.startsWith('HTTP ') && 'http.url' in node.attributes) {
|
|
92
|
+
extra = `${tui.colorSuccess(node.attributes['http.url'] as string)} `;
|
|
93
|
+
}
|
|
94
|
+
console.log(`${prefix}${connector}${node.operation} ${extra}${duration}`);
|
|
95
|
+
|
|
96
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
97
|
+
const children = node.children ?? [];
|
|
98
|
+
children.forEach((child, index) => {
|
|
99
|
+
printTimeline(child, childPrefix, index === children.length - 1);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
45
103
|
export const getSubcommand = createSubcommand({
|
|
46
104
|
name: 'get',
|
|
47
105
|
description: 'Get details about a specific session',
|
|
@@ -83,8 +141,8 @@ export const getSubcommand = createSubcommand({
|
|
|
83
141
|
url: session.url,
|
|
84
142
|
route_id: session.route_id,
|
|
85
143
|
thread_id: session.thread_id,
|
|
86
|
-
|
|
87
|
-
|
|
144
|
+
agents: enriched.agents,
|
|
145
|
+
eval_runs: enriched.evalRuns.map((run) => ({
|
|
88
146
|
id: run.id,
|
|
89
147
|
eval_id: run.eval_id,
|
|
90
148
|
created_at: run.created_at,
|
|
@@ -93,6 +151,8 @@ export const getSubcommand = createSubcommand({
|
|
|
93
151
|
error: run.error,
|
|
94
152
|
result: run.result,
|
|
95
153
|
})),
|
|
154
|
+
timeline: enriched.timeline,
|
|
155
|
+
route: enriched.route,
|
|
96
156
|
};
|
|
97
157
|
|
|
98
158
|
if (options.json) {
|
|
@@ -100,17 +160,14 @@ export const getSubcommand = createSubcommand({
|
|
|
100
160
|
return result;
|
|
101
161
|
}
|
|
102
162
|
|
|
103
|
-
tui.banner(`Session ${session.id}`, `Status: ${session.success ? 'Success' : 'Failed'}`);
|
|
104
|
-
|
|
105
163
|
console.log(tui.bold('ID: ') + session.id);
|
|
106
164
|
console.log(tui.bold('Project: ') + session.project_id);
|
|
107
165
|
console.log(tui.bold('Deployment: ') + (session.deployment_id || '-'));
|
|
108
|
-
console.log(tui.bold('Created: ') + new Date(session.created_at).toLocaleString());
|
|
109
166
|
console.log(tui.bold('Start: ') + new Date(session.start_time).toLocaleString());
|
|
110
167
|
if (session.end_time) {
|
|
111
168
|
console.log(tui.bold('End: ') + new Date(session.end_time).toLocaleString());
|
|
112
169
|
}
|
|
113
|
-
if (session.duration) {
|
|
170
|
+
if (session.duration && session.end_time) {
|
|
114
171
|
console.log(
|
|
115
172
|
tui.bold('Duration: ') + `${(session.duration / 1_000_000).toFixed(0)}ms`
|
|
116
173
|
);
|
|
@@ -118,23 +175,32 @@ export const getSubcommand = createSubcommand({
|
|
|
118
175
|
console.log(tui.bold('Method: ') + session.method);
|
|
119
176
|
console.log(tui.bold('URL: ') + tui.link(session.url, session.url));
|
|
120
177
|
console.log(tui.bold('Trigger: ') + session.trigger);
|
|
121
|
-
|
|
178
|
+
if (session.env !== 'production') {
|
|
179
|
+
console.log(tui.bold('Environment: ') + session.env);
|
|
180
|
+
}
|
|
122
181
|
console.log(tui.bold('Dev Mode: ') + (session.devmode ? 'Yes' : 'No'));
|
|
123
|
-
console.log(
|
|
182
|
+
console.log(
|
|
183
|
+
tui.bold('Success: ') +
|
|
184
|
+
(session.success ? tui.colorSuccess('✓') : tui.colorError('✗'))
|
|
185
|
+
);
|
|
124
186
|
console.log(tui.bold('Pending: ') + (session.pending ? 'Yes' : 'No'));
|
|
125
187
|
if (session.error) {
|
|
126
188
|
console.log(tui.bold('Error: ') + tui.error(session.error));
|
|
127
189
|
}
|
|
128
|
-
if (enriched.
|
|
129
|
-
const agentDisplay = enriched.
|
|
130
|
-
.map((
|
|
131
|
-
const agentId = session.agent_ids[idx];
|
|
132
|
-
return `${name} ${tui.muted(`(${agentId})`)}`;
|
|
133
|
-
})
|
|
190
|
+
if (enriched.agents.length > 0) {
|
|
191
|
+
const agentDisplay = enriched.agents
|
|
192
|
+
.map((agent) => `${agent.name} ${tui.muted(`(${agent.identifier})`)}`)
|
|
134
193
|
.join(', ');
|
|
135
194
|
console.log(tui.bold('Agents: ') + agentDisplay);
|
|
136
195
|
}
|
|
137
|
-
|
|
196
|
+
if (enriched.route) {
|
|
197
|
+
console.log(
|
|
198
|
+
tui.bold('Route: ') +
|
|
199
|
+
`${enriched.route.method.toUpperCase()} ${enriched.route.path} ${tui.muted(`(${enriched.route.id})`)}`
|
|
200
|
+
);
|
|
201
|
+
} else {
|
|
202
|
+
console.log(tui.bold('Route ID: ') + session.route_id);
|
|
203
|
+
}
|
|
138
204
|
console.log(tui.bold('Thread ID: ') + session.thread_id);
|
|
139
205
|
|
|
140
206
|
if (enriched.evalRuns.length > 0) {
|
|
@@ -143,7 +209,7 @@ export const getSubcommand = createSubcommand({
|
|
|
143
209
|
const evalTableData = enriched.evalRuns.map((run) => ({
|
|
144
210
|
ID: run.id,
|
|
145
211
|
'Eval ID': run.eval_id,
|
|
146
|
-
Success: run.success ? '✓' : '✗',
|
|
212
|
+
Success: run.success ? tui.colorSuccess('✓') : tui.colorError('✗'),
|
|
147
213
|
Pending: run.pending ? '⏳' : '✓',
|
|
148
214
|
Error: run.error || 'No',
|
|
149
215
|
Created: new Date(run.created_at).toLocaleString(),
|
|
@@ -159,6 +225,12 @@ export const getSubcommand = createSubcommand({
|
|
|
159
225
|
]);
|
|
160
226
|
}
|
|
161
227
|
|
|
228
|
+
if (result.timeline) {
|
|
229
|
+
console.log('');
|
|
230
|
+
console.log(tui.bold('Timeline:'));
|
|
231
|
+
printTimeline(result.timeline, '');
|
|
232
|
+
}
|
|
233
|
+
|
|
162
234
|
return result;
|
|
163
235
|
} catch (ex) {
|
|
164
236
|
tui.fatal(
|
package/src/cmd/dev/index.ts
CHANGED
|
@@ -236,6 +236,7 @@ export const command = createCommand({
|
|
|
236
236
|
let showInitialReadyMessage = true;
|
|
237
237
|
let serverStartTime = 0;
|
|
238
238
|
let gravityClient: Bun.Subprocess | undefined;
|
|
239
|
+
let initialStartupComplete = false;
|
|
239
240
|
|
|
240
241
|
if (gravityBin && devmode && project) {
|
|
241
242
|
const sdkKey = await loadProjectSDKKey(rootDir);
|
|
@@ -259,7 +260,7 @@ export const command = createCommand({
|
|
|
259
260
|
'--url',
|
|
260
261
|
config?.overrides?.gravity_url ?? 'grpc://devmode.agentuity.com',
|
|
261
262
|
'--log-level',
|
|
262
|
-
'error',
|
|
263
|
+
process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
|
|
263
264
|
],
|
|
264
265
|
{
|
|
265
266
|
cwd: rootDir,
|
|
@@ -412,17 +413,10 @@ export const command = createCommand({
|
|
|
412
413
|
logger.trace('Initial server start');
|
|
413
414
|
}
|
|
414
415
|
logger.trace('Starting typecheck and build...');
|
|
415
|
-
await
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
cwd: rootDir,
|
|
420
|
-
clearOnSuccess: true,
|
|
421
|
-
truncate: false,
|
|
422
|
-
maxLinesOutput: 2,
|
|
423
|
-
maxLinesOnFailure: 15,
|
|
424
|
-
}),
|
|
425
|
-
tui.spinner('Building project', async () => {
|
|
416
|
+
await tui.spinner({
|
|
417
|
+
message: 'Building project',
|
|
418
|
+
clearOnSuccess: true,
|
|
419
|
+
callback: async () => {
|
|
426
420
|
try {
|
|
427
421
|
logger.trace('Bundle starting...');
|
|
428
422
|
building = true;
|
|
@@ -436,13 +430,26 @@ export const command = createCommand({
|
|
|
436
430
|
building = false;
|
|
437
431
|
buildCompletedAt = Date.now();
|
|
438
432
|
logger.trace('Bundle completed successfully');
|
|
433
|
+
logger.trace('tsc starting...');
|
|
434
|
+
await tui.runCommand({
|
|
435
|
+
command: 'tsc',
|
|
436
|
+
cmd: ['bunx', 'tsc', '--noEmit'],
|
|
437
|
+
cwd: rootDir,
|
|
438
|
+
clearOnSuccess: true,
|
|
439
|
+
truncate: false,
|
|
440
|
+
maxLinesOutput: 2,
|
|
441
|
+
maxLinesOnFailure: 15,
|
|
442
|
+
});
|
|
443
|
+
logger.trace('tsc completed successfully');
|
|
439
444
|
} catch (error) {
|
|
440
445
|
building = false;
|
|
441
446
|
logger.trace('Bundle failed: %s', error);
|
|
442
447
|
failure('Build failed');
|
|
448
|
+
return;
|
|
443
449
|
}
|
|
444
|
-
}
|
|
445
|
-
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
|
|
446
453
|
logger.trace('Typecheck and build completed');
|
|
447
454
|
|
|
448
455
|
if (failed) {
|
|
@@ -541,8 +548,14 @@ export const command = createCommand({
|
|
|
541
548
|
|
|
542
549
|
if (showInitialReadyMessage) {
|
|
543
550
|
showInitialReadyMessage = false;
|
|
551
|
+
// Clear any lingering spinner/command output - clear everything below cursor
|
|
552
|
+
process.stderr.write('\x1B[J'); // Clear from cursor to end of screen
|
|
553
|
+
process.stdout.write('\x1B[J'); // Clear from cursor to end of screen
|
|
544
554
|
logger.info('DevMode ready 🚀');
|
|
545
555
|
logger.trace('Initial ready message logged');
|
|
556
|
+
// Mark initial startup complete immediately to prevent watcher restarts
|
|
557
|
+
initialStartupComplete = true;
|
|
558
|
+
logger.trace('Initial startup complete, file watcher restarts now enabled');
|
|
546
559
|
}
|
|
547
560
|
|
|
548
561
|
logger.trace('Attaching exit handler to dev server process...');
|
|
@@ -634,6 +647,7 @@ export const command = createCommand({
|
|
|
634
647
|
logger.trace('Starting initial build and server');
|
|
635
648
|
await restart();
|
|
636
649
|
logger.trace('Initial restart completed, setting up watchers');
|
|
650
|
+
logger.trace('initialStartupComplete is now: %s', initialStartupComplete);
|
|
637
651
|
|
|
638
652
|
// Setup keyboard shortcuts (only if we have a TTY)
|
|
639
653
|
if (canDoInput) {
|
|
@@ -743,6 +757,17 @@ export const command = createCommand({
|
|
|
743
757
|
const watcher = watch(watchDir, { recursive: true }, (eventType, changedFile) => {
|
|
744
758
|
const absPath = changedFile ? resolve(watchDir, changedFile) : watchDir;
|
|
745
759
|
|
|
760
|
+
// Ignore file changes during initial startup to prevent spurious restarts
|
|
761
|
+
if (!initialStartupComplete) {
|
|
762
|
+
logger.trace(
|
|
763
|
+
'File change ignored (initial startup): %s (event: %s, file: %s)',
|
|
764
|
+
watchDir,
|
|
765
|
+
eventType,
|
|
766
|
+
changedFile
|
|
767
|
+
);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
746
771
|
// Ignore file changes during active build to prevent loops
|
|
747
772
|
if (building) {
|
|
748
773
|
logger.trace(
|