@agentuity/cli 0.0.49 → 0.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cmd/agents/index.d.ts +2 -0
  3. package/dist/cmd/agents/index.d.ts.map +1 -0
  4. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  5. package/dist/cmd/bundle/ast.d.ts +3 -1
  6. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  7. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  8. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  9. package/dist/cmd/cloud/index.d.ts.map +1 -1
  10. package/dist/cmd/cloud/session/get.d.ts +2 -0
  11. package/dist/cmd/cloud/session/get.d.ts.map +1 -0
  12. package/dist/cmd/cloud/session/index.d.ts +2 -0
  13. package/dist/cmd/cloud/session/index.d.ts.map +1 -0
  14. package/dist/cmd/cloud/session/list.d.ts +2 -0
  15. package/dist/cmd/cloud/session/list.d.ts.map +1 -0
  16. package/dist/cmd/cloud/session/logs.d.ts +2 -0
  17. package/dist/cmd/cloud/session/logs.d.ts.map +1 -0
  18. package/dist/cmd/dev/agents.d.ts +2 -0
  19. package/dist/cmd/dev/agents.d.ts.map +1 -0
  20. package/dist/cmd/dev/index.d.ts.map +1 -1
  21. package/dist/cmd/dev/sync.d.ts +12 -0
  22. package/dist/cmd/dev/sync.d.ts.map +1 -0
  23. package/dist/repl.d.ts +2 -6
  24. package/dist/repl.d.ts.map +1 -1
  25. package/dist/tui.d.ts +24 -0
  26. package/dist/tui.d.ts.map +1 -1
  27. package/dist/types.d.ts +12 -0
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/utils/format.d.ts +9 -0
  30. package/dist/utils/format.d.ts.map +1 -0
  31. package/package.json +6 -3
  32. package/src/cli.ts +6 -4
  33. package/src/cmd/agents/index.ts +147 -0
  34. package/src/cmd/auth/ssh/list.ts +10 -16
  35. package/src/cmd/bundle/ast.test.ts +2 -2
  36. package/src/cmd/bundle/ast.ts +85 -17
  37. package/src/cmd/bundle/plugin.ts +24 -2
  38. package/src/cmd/cloud/deployment/list.ts +16 -22
  39. package/src/cmd/cloud/index.ts +2 -0
  40. package/src/cmd/cloud/session/get.ts +164 -0
  41. package/src/cmd/cloud/session/index.ts +11 -0
  42. package/src/cmd/cloud/session/list.ts +145 -0
  43. package/src/cmd/cloud/session/logs.ts +68 -0
  44. package/src/cmd/dev/agents.ts +122 -0
  45. package/src/cmd/dev/index.ts +101 -7
  46. package/src/cmd/dev/sync.ts +414 -0
  47. package/src/cmd/project/list.ts +1 -1
  48. package/src/cmd/project/show.ts +1 -1
  49. package/src/repl.ts +2 -12
  50. package/src/tui.ts +94 -0
  51. package/src/types.ts +13 -10
  52. package/src/utils/format.ts +17 -0
@@ -116,6 +116,10 @@ function hashSHA1(...val: string[]): string {
116
116
  return hasher.digest().toHex();
117
117
  }
118
118
 
119
+ export function getDevmodeDeploymentId(projectId: string, endpointId: string): string {
120
+ return `devmode_${hashSHA1(projectId, endpointId)}`;
121
+ }
122
+
119
123
  function getAgentId(
120
124
  projectId: string,
121
125
  deploymentId: string,
@@ -151,6 +155,10 @@ function generateStableAgentId(projectId: string, name: string): string {
151
155
  return `agentid_${hashSHA1(projectId, name)}`.substring(0, 64);
152
156
  }
153
157
 
158
+ function generateStableEvalId(projectId: string, agentId: string, name: string): string {
159
+ return `evalid_${hashSHA1(projectId, agentId, name)}`.substring(0, 64);
160
+ }
161
+
154
162
  type AcornParseResultType = ReturnType<typeof acornLoose.parse>;
155
163
 
156
164
  function augmentAgentMetadataNode(
@@ -242,14 +250,29 @@ function setLiteralValue(literal: ASTLiteral, value: string) {
242
250
  }
243
251
 
244
252
  function augmentEvalMetadataNode(
253
+ projectId: string,
254
+ agentId: string,
245
255
  id: string,
246
256
  name: string,
247
257
  rel: string,
248
258
  version: string,
249
- identifier: string,
250
- metadataObj: ASTObjectExpression
259
+ _ast: AcornParseResultType,
260
+ metadataObj: ASTObjectExpression,
261
+ _filename: string
251
262
  ): void {
252
- // Check if id, version, identifier, filename already exist
263
+ const metadata = parseObjectExpressionToMap(metadataObj);
264
+ // Name can come from metadata.name or variable name (already resolved in caller)
265
+ // If metadata doesn't have name, we'll add it from the resolved name
266
+ if (!metadata.has('name')) {
267
+ metadataObj.properties.push(createObjectPropertyNode('name', name));
268
+ }
269
+ const descriptionNode = metadataObj.properties.find((x) => x.key.name === 'description')?.value;
270
+ const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
271
+ const effectiveAgentId = agentId || '';
272
+ const _evalId = getEvalId(projectId, effectiveAgentId, rel, name, version); // Deployment-specific ID (not used, kept for potential future use)
273
+ const stableEvalId = generateStableEvalId(projectId, effectiveAgentId, name);
274
+
275
+ // Check if id, version, identifier, filename, evalId already exist
253
276
  const existingKeys = new Set<string>();
254
277
  for (const prop of metadataObj.properties) {
255
278
  if (prop.key.type === 'Identifier') {
@@ -286,12 +309,12 @@ function augmentEvalMetadataNode(
286
309
  }
287
310
 
288
311
  if (!existingKeys.has('identifier')) {
289
- metadataObj.properties.push(createObjectPropertyNode('identifier', identifier));
312
+ metadataObj.properties.push(createObjectPropertyNode('identifier', name));
290
313
  } else {
291
314
  for (const prop of metadataObj.properties) {
292
315
  if (prop.key.type === 'Identifier' && prop.key.name === 'identifier') {
293
316
  if (prop.value.type === 'Literal') {
294
- setLiteralValue(prop.value as ASTLiteral, identifier);
317
+ setLiteralValue(prop.value as ASTLiteral, name);
295
318
  }
296
319
  break;
297
320
  }
@@ -310,6 +333,32 @@ function augmentEvalMetadataNode(
310
333
  }
311
334
  }
312
335
  }
336
+
337
+ if (!existingKeys.has('evalId')) {
338
+ metadataObj.properties.push(createObjectPropertyNode('evalId', stableEvalId));
339
+ } else {
340
+ for (const prop of metadataObj.properties) {
341
+ if (prop.key.type === 'Identifier' && prop.key.name === 'evalId') {
342
+ if (prop.value.type === 'Literal') {
343
+ setLiteralValue(prop.value as ASTLiteral, stableEvalId);
344
+ }
345
+ break;
346
+ }
347
+ }
348
+ }
349
+
350
+ if (!existingKeys.has('description')) {
351
+ metadataObj.properties.push(createObjectPropertyNode('description', description));
352
+ } else {
353
+ for (const prop of metadataObj.properties) {
354
+ if (prop.key.type === 'Identifier' && prop.key.name === 'description') {
355
+ if (prop.value.type === 'Literal') {
356
+ setLiteralValue(prop.value as ASTLiteral, description);
357
+ }
358
+ break;
359
+ }
360
+ }
361
+ }
313
362
  }
314
363
 
315
364
  export function parseEvalMetadata(
@@ -317,7 +366,8 @@ export function parseEvalMetadata(
317
366
  filename: string,
318
367
  contents: string,
319
368
  projectId: string,
320
- deploymentId: string
369
+ deploymentId: string,
370
+ agentId?: string
321
371
  ): [
322
372
  string,
323
373
  Array<{
@@ -326,6 +376,7 @@ export function parseEvalMetadata(
326
376
  version: string;
327
377
  identifier: string;
328
378
  name: string;
379
+ evalId: string;
329
380
  description?: string;
330
381
  }>,
331
382
  ] {
@@ -339,8 +390,6 @@ export function parseEvalMetadata(
339
390
  logger.trace(`Parsing evals from ${filename}`);
340
391
  const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
341
392
  const rel = relative(rootDir, filename);
342
- const dir = dirname(filename);
343
- const identifier = basename(dir);
344
393
  const version = hash(contents);
345
394
  const evals: Array<{
346
395
  filename: string;
@@ -348,17 +397,16 @@ export function parseEvalMetadata(
348
397
  version: string;
349
398
  identifier: string;
350
399
  name: string;
400
+ evalId: string;
351
401
  description?: string;
352
402
  }> = [];
353
403
 
354
- // Find all agent.createEval() calls
404
+ // Find all exported agent.createEval() calls
355
405
  for (const body of ast.body) {
356
406
  let variableDeclaration: { declarations: Array<ASTVariableDeclarator> } | undefined;
357
407
 
358
- // Handle both direct VariableDeclaration and ExportNamedDeclaration
359
- if (body.type === 'VariableDeclaration') {
360
- variableDeclaration = body as { declarations: Array<ASTVariableDeclarator> };
361
- } else if (body.type === 'ExportNamedDeclaration') {
408
+ // Only process exported VariableDeclarations
409
+ if (body.type === 'ExportNamedDeclaration') {
362
410
  const exportDecl = body as {
363
411
  declaration?: { type: string; declarations?: Array<ASTVariableDeclarator> };
364
412
  };
@@ -447,14 +495,31 @@ export function parseEvalMetadata(
447
495
  );
448
496
 
449
497
  // Inject metadata into AST if metadata object exists
498
+ let stableEvalId: string;
499
+ const effectiveAgentId = agentId || '';
450
500
  if (metadataObj) {
451
501
  augmentEvalMetadataNode(
502
+ projectId,
503
+ effectiveAgentId,
452
504
  evalId,
453
505
  finalName,
454
506
  rel,
455
507
  version,
456
- identifier,
457
- metadataObj
508
+ ast,
509
+ metadataObj,
510
+ filename
511
+ );
512
+ // Extract evalId from metadata after augmentation
513
+ const metadata = parseObjectExpressionToMap(metadataObj);
514
+ stableEvalId =
515
+ metadata.get('evalId') ||
516
+ generateStableEvalId(projectId, effectiveAgentId, finalName);
517
+ } else {
518
+ // If no metadata object, generate stable evalId
519
+ stableEvalId = generateStableEvalId(
520
+ projectId,
521
+ effectiveAgentId,
522
+ finalName
458
523
  );
459
524
  }
460
525
 
@@ -462,8 +527,9 @@ export function parseEvalMetadata(
462
527
  filename: rel,
463
528
  id: evalId,
464
529
  version,
465
- identifier,
530
+ identifier: finalName,
466
531
  name: finalName,
532
+ evalId: stableEvalId,
467
533
  description: evalDescription,
468
534
  });
469
535
  }
@@ -639,12 +705,14 @@ export async function parseAgentMetadata(
639
705
  const evalsSource = await evalsFile.text();
640
706
  const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
641
707
  const evalsContents = transpiler.transformSync(evalsSource);
708
+ const agentId = result[1].get('agentId') || '';
642
709
  const [, evals] = parseEvalMetadata(
643
710
  rootDir,
644
711
  evalsPath,
645
712
  evalsContents,
646
713
  projectId,
647
- deploymentId
714
+ deploymentId,
715
+ agentId
648
716
  );
649
717
  if (evals.length > 0) {
650
718
  logger.trace(`Adding ${evals.length} eval(s) to agent metadata for ${name}`);
@@ -578,6 +578,7 @@ const AgentuityBundler: BunPlugin = {
578
578
  version: v.get('version')!,
579
579
  name: v.get('name')!,
580
580
  description: v.get('description') ?? '<no description provided>',
581
+ projectId,
581
582
  };
582
583
 
583
584
  const evalsStr = v.get('evals');
@@ -586,7 +587,17 @@ const AgentuityBundler: BunPlugin = {
586
587
  `[plugin] Found evals string for agent ${agentData.name}, parsing...`
587
588
  );
588
589
  try {
589
- agentData.evals = JSON.parse(evalsStr);
590
+ const parsedEvals = JSON.parse(evalsStr) as Array<
591
+ Omit<
592
+ NonNullable<BuildMetadata['agents'][number]['evals']>[number],
593
+ 'agentIdentifier' | 'projectId'
594
+ >
595
+ >;
596
+ agentData.evals = parsedEvals.map((evalItem) => ({
597
+ ...evalItem,
598
+ agentIdentifier: agentData.agentId,
599
+ projectId,
600
+ }));
590
601
  logger.trace(
591
602
  `[plugin] Successfully parsed ${agentData.evals?.length ?? 0} eval(s) for agent ${agentData.name}`
592
603
  );
@@ -614,6 +625,7 @@ const AgentuityBundler: BunPlugin = {
614
625
  version: sub.get('version')!,
615
626
  name: sub.get('name')!,
616
627
  description: sub.get('description') ?? '<no description provided>',
628
+ projectId,
617
629
  };
618
630
 
619
631
  // Add evals for subagents if any
@@ -623,7 +635,17 @@ const AgentuityBundler: BunPlugin = {
623
635
  `[plugin] Found evals string for subagent ${subagentData.name}, parsing...`
624
636
  );
625
637
  try {
626
- subagentData.evals = JSON.parse(subEvalsStr);
638
+ const parsedSubEvals = JSON.parse(subEvalsStr) as Array<
639
+ Omit<
640
+ NonNullable<BuildMetadata['agents'][number]['evals']>[number],
641
+ 'agentIdentifier' | 'projectId'
642
+ >
643
+ >;
644
+ subagentData.evals = parsedSubEvals.map((evalItem) => ({
645
+ ...evalItem,
646
+ agentIdentifier: subagentData.agentId,
647
+ projectId,
648
+ }));
627
649
  logger.trace(
628
650
  `[plugin] Successfully parsed ${subagentData.evals?.length ?? 0} eval(s) for subagent ${subagentData.name}`
629
651
  );
@@ -3,7 +3,6 @@ import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
4
  import { projectDeploymentList } from '@agentuity/server';
5
5
  import { resolveProjectId } from './utils';
6
- import { Table } from 'console-table-printer';
7
6
  import { getCommand } from '../../../command-prefix';
8
7
  import { ErrorCode } from '../../../errors';
9
8
 
@@ -64,28 +63,23 @@ export const listSubcommand = createSubcommand({
64
63
  return [];
65
64
  }
66
65
 
67
- const table = new Table({
68
- columns: [
69
- { name: 'ID', alignment: 'left' },
70
- { name: 'State', alignment: 'left' },
71
- { name: 'Active', alignment: 'center' },
72
- { name: 'Created', alignment: 'left' },
73
- { name: 'Message', alignment: 'left' },
74
- { name: 'Tags', alignment: 'left' },
75
- ],
76
- });
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
+ }));
77
74
 
78
- for (const d of deployments) {
79
- table.addRow({
80
- ID: d.id,
81
- State: d.state || 'unknown',
82
- Active: d.active ? 'Yes' : '',
83
- Created: new Date(d.createdAt).toLocaleString(),
84
- Message: d.message || '',
85
- Tags: d.tags.join(', '),
86
- });
87
- }
88
- table.printTable();
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
+ ]);
89
83
 
90
84
  return deployments.map((d) => ({
91
85
  id: d.id,
@@ -1,6 +1,7 @@
1
1
  import { createCommand } from '../../types';
2
2
  import { deploySubcommand } from './deploy';
3
3
  import { resourceSubcommand } from './resource';
4
+ import { sessionCommand } from './session';
4
5
  import { sshSubcommand } from './ssh';
5
6
  import { scpSubcommand } from './scp';
6
7
  import { deploymentCommand } from './deployment';
@@ -12,6 +13,7 @@ export const command = createCommand({
12
13
  subcommands: [
13
14
  deploySubcommand,
14
15
  resourceSubcommand,
16
+ sessionCommand,
15
17
  sshSubcommand,
16
18
  scpSubcommand,
17
19
  deploymentCommand,
@@ -0,0 +1,164 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { sessionGet } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import { getCatalystAPIClient } from '../../../config';
8
+
9
+ const SessionGetResponseSchema = z.object({
10
+ id: z.string().describe('Session ID'),
11
+ created_at: z.string().describe('Creation timestamp'),
12
+ start_time: z.string().describe('Start time'),
13
+ end_time: z.string().nullable().describe('End time'),
14
+ duration: z.number().nullable().describe('Duration in nanoseconds'),
15
+ org_id: z.string().describe('Organization ID'),
16
+ project_id: z.string().describe('Project ID'),
17
+ deployment_id: z.string().describe('Deployment ID'),
18
+ agent_ids: z.array(z.string()).describe('Agent IDs'),
19
+ trigger: z.string().describe('Trigger type'),
20
+ env: z.string().describe('Environment'),
21
+ devmode: z.boolean().describe('Dev mode'),
22
+ pending: z.boolean().describe('Pending'),
23
+ success: z.boolean().describe('Success'),
24
+ error: z.string().nullable().describe('Error message'),
25
+ method: z.string().describe('HTTP method'),
26
+ url: z.string().describe('Request URL'),
27
+ route_id: z.string().describe('Route ID'),
28
+ thread_id: z.string().describe('Thread ID'),
29
+ agentNames: z.array(z.string()).describe('Agent names'),
30
+ evalRuns: z.array(z.object({
31
+ id: z.string(),
32
+ created_at: z.string(),
33
+ eval_id: z.string(),
34
+ pending: z.boolean(),
35
+ success: z.boolean(),
36
+ error: z.string().nullable(),
37
+ result: z.string().nullable(),
38
+ })).describe('Eval runs'),
39
+ });
40
+
41
+ export const getSubcommand = createSubcommand({
42
+ name: 'get',
43
+ description: 'Get details about a specific session',
44
+ tags: ['read-only', 'fast', 'requires-auth'],
45
+ examples: [`${getCommand('cloud session get')} sess_abc123xyz`],
46
+ requires: { auth: true },
47
+ idempotent: true,
48
+ schema: {
49
+ args: z.object({
50
+ session_id: z.string().describe('Session ID'),
51
+ }),
52
+ response: SessionGetResponseSchema,
53
+ },
54
+ async handler(ctx) {
55
+ const { config, logger, auth, args, options } = ctx;
56
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
57
+
58
+ try {
59
+ const enriched = await sessionGet(catalystClient, { id: args.session_id });
60
+ const session = enriched.session;
61
+
62
+ const result = {
63
+ id: session.id,
64
+ created_at: session.created_at,
65
+ start_time: session.start_time,
66
+ end_time: session.end_time,
67
+ duration: session.duration,
68
+ org_id: session.org_id,
69
+ project_id: session.project_id,
70
+ deployment_id: session.deployment_id,
71
+ agent_ids: session.agent_ids,
72
+ trigger: session.trigger,
73
+ env: session.env,
74
+ devmode: session.devmode,
75
+ pending: session.pending,
76
+ success: session.success,
77
+ error: session.error,
78
+ method: session.method,
79
+ url: session.url,
80
+ route_id: session.route_id,
81
+ thread_id: session.thread_id,
82
+ agentNames: enriched.agentNames,
83
+ evalRuns: enriched.evalRuns.map(run => ({
84
+ id: run.id,
85
+ eval_id: run.eval_id,
86
+ created_at: run.created_at,
87
+ pending: run.pending,
88
+ success: run.success,
89
+ error: run.error,
90
+ result: run.result,
91
+ })),
92
+ };
93
+
94
+ if (options.json) {
95
+ console.log(JSON.stringify(result, null, 2));
96
+ return result;
97
+ }
98
+
99
+ tui.banner(`Session ${session.id}`, `Status: ${session.success ? 'Success' : 'Failed'}`);
100
+
101
+ console.log(tui.bold('ID: ') + session.id);
102
+ console.log(tui.bold('Project: ') + session.project_id);
103
+ console.log(tui.bold('Deployment: ') + (session.deployment_id || '-'));
104
+ console.log(tui.bold('Created: ') + new Date(session.created_at).toLocaleString());
105
+ console.log(tui.bold('Start: ') + new Date(session.start_time).toLocaleString());
106
+ if (session.end_time) {
107
+ console.log(tui.bold('End: ') + new Date(session.end_time).toLocaleString());
108
+ }
109
+ if (session.duration) {
110
+ console.log(
111
+ tui.bold('Duration: ') + `${(session.duration / 1_000_000).toFixed(0)}ms`
112
+ );
113
+ }
114
+ console.log(tui.bold('Method: ') + session.method);
115
+ console.log(tui.bold('URL: ') + tui.link(session.url, session.url));
116
+ console.log(tui.bold('Trigger: ') + session.trigger);
117
+ console.log(tui.bold('Environment: ') + session.env);
118
+ console.log(tui.bold('Dev Mode: ') + (session.devmode ? 'Yes' : 'No'));
119
+ console.log(tui.bold('Success: ') + (session.success ? '✓' : '✗'));
120
+ console.log(tui.bold('Pending: ') + (session.pending ? 'Yes' : 'No'));
121
+ if (session.error) {
122
+ console.log(tui.bold('Error: ') + tui.error(session.error));
123
+ }
124
+ if (enriched.agentNames.length > 0) {
125
+ const agentDisplay = enriched.agentNames.map((name, idx) => {
126
+ const agentId = session.agent_ids[idx];
127
+ return `${name} ${tui.muted(`(${agentId})`)}`;
128
+ }).join(', ');
129
+ console.log(tui.bold('Agents: ') + agentDisplay);
130
+ }
131
+ console.log(tui.bold('Route ID: ') + session.route_id);
132
+ console.log(tui.bold('Thread ID: ') + session.thread_id);
133
+
134
+ if (enriched.evalRuns.length > 0) {
135
+ console.log('');
136
+ console.log(tui.bold('Eval Runs:'));
137
+ const evalTableData = enriched.evalRuns.map((run) => ({
138
+ ID: run.id,
139
+ 'Eval ID': run.eval_id,
140
+ Success: run.success ? '✓' : '✗',
141
+ Pending: run.pending ? '⏳' : '✓',
142
+ Error: run.error || 'No',
143
+ Created: new Date(run.created_at).toLocaleString(),
144
+ }));
145
+
146
+ tui.table(evalTableData, [
147
+ { name: 'ID', alignment: 'left' },
148
+ { name: 'Eval ID', alignment: 'left' },
149
+ { name: 'Success', alignment: 'center' },
150
+ { name: 'Pending', alignment: 'center' },
151
+ { name: 'Error', alignment: 'left' },
152
+ { name: 'Created', alignment: 'left' },
153
+ ]);
154
+ }
155
+
156
+ return result;
157
+ } catch (ex) {
158
+ tui.fatal(
159
+ `Failed to get session: ${ex instanceof Error ? ex.message : String(ex)}`,
160
+ ErrorCode.API_ERROR
161
+ );
162
+ }
163
+ },
164
+ });
@@ -0,0 +1,11 @@
1
+ import { createCommand } from '../../../types';
2
+ import { getSubcommand } from './get';
3
+ import { listSubcommand } from './list';
4
+ import { logsSubcommand } from './logs';
5
+
6
+ export const sessionCommand = createCommand({
7
+ name: 'session',
8
+ description: 'Manage sessions',
9
+ tags: ['requires-auth'],
10
+ subcommands: [getSubcommand, listSubcommand, logsSubcommand],
11
+ });
@@ -0,0 +1,145 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { sessionList } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import { getCatalystAPIClient } from '../../../config';
8
+
9
+ const SessionListResponseSchema = z.array(
10
+ z.object({
11
+ id: z.string().describe('Session ID'),
12
+ created_at: z.string().describe('Creation timestamp'),
13
+ success: z.boolean().describe('Whether the session succeeded'),
14
+ duration: z.number().nullable().describe('Duration in nanoseconds'),
15
+ method: z.string().describe('HTTP method'),
16
+ url: z.string().describe('Request URL'),
17
+ trigger: z.string().describe('Trigger type'),
18
+ env: z.string().describe('Environment'),
19
+ })
20
+ );
21
+
22
+ export const listSubcommand = createSubcommand({
23
+ name: 'list',
24
+ description: 'List recent sessions',
25
+ tags: ['read-only', 'fast', 'requires-auth'],
26
+ examples: [
27
+ `${getCommand('cloud session list')} # List 10 most recent sessions`,
28
+ `${getCommand('cloud session list')} --count=25 # List 25 most recent sessions`,
29
+ `${getCommand('cloud session list')} --project-id=proj_* # Filter by project`,
30
+ `${getCommand('cloud session list')} --deployment-id=* # Filter by deployment`,
31
+ `${getCommand('cloud session list')} --success=true # Only successful sessions`,
32
+ `${getCommand('cloud session list')} --devmode=false # Only production sessions`,
33
+ `${getCommand('cloud session list')} --trigger=api # Only API triggered sessions`,
34
+ `${getCommand('cloud session list')} --env=production # Only production environment`,
35
+ ],
36
+ aliases: ['ls'],
37
+ requires: { auth: true },
38
+ optional: { project: true },
39
+ idempotent: true,
40
+ pagination: {
41
+ supported: true,
42
+ defaultLimit: 10,
43
+ maxLimit: 100,
44
+ parameters: {
45
+ limit: 'count',
46
+ },
47
+ },
48
+ schema: {
49
+ options: z.object({
50
+ count: z.coerce
51
+ .number()
52
+ .int()
53
+ .min(1)
54
+ .max(100)
55
+ .default(10)
56
+ .describe('Number of sessions to list (1–100)'),
57
+ projectId: z.string().optional().describe('Filter by project ID'),
58
+ deploymentId: z.string().optional().describe('Filter by deployment ID'),
59
+ trigger: z.string().optional().describe('Filter by trigger type (api, cron, webhook)'),
60
+ env: z.string().optional().describe('Filter by environment'),
61
+ threadId: z.string().optional().describe('Filter by thread ID'),
62
+ agentIdentifier: z.string().optional().describe('Filter by agent identifier'),
63
+ devmode: z.coerce.boolean().optional().describe('Filter by dev mode (true/false)'),
64
+ success: z.coerce.boolean().optional().describe('Filter by success status (true/false)'),
65
+ startAfter: z.string().optional().describe('Filter by start time after (ISO 8601)'),
66
+ startBefore: z.string().optional().describe('Filter by start time before (ISO 8601)'),
67
+ }),
68
+ response: SessionListResponseSchema,
69
+ },
70
+ async handler(ctx) {
71
+ const { config, logger, auth, project, opts, options } = ctx;
72
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
73
+
74
+ const projectId = opts.projectId || project?.projectId;
75
+
76
+ try {
77
+ const sessions = await sessionList(catalystClient, {
78
+ count: opts.count,
79
+ projectId,
80
+ deploymentId: opts.deploymentId,
81
+ trigger: opts.trigger,
82
+ env: opts.env,
83
+ devmode: opts.devmode,
84
+ success: opts.success,
85
+ threadId: opts.threadId,
86
+ agentIdentifier: opts.agentIdentifier,
87
+ startAfter: opts.startAfter,
88
+ startBefore: opts.startBefore,
89
+ });
90
+
91
+ const result = sessions.map((s) => ({
92
+ id: s.id,
93
+ created_at: s.created_at,
94
+ success: s.success,
95
+ duration: s.duration,
96
+ method: s.method,
97
+ url: s.url,
98
+ trigger: s.trigger,
99
+ env: s.env,
100
+ }));
101
+
102
+ if (options.json) {
103
+ console.log(JSON.stringify(result, null, 2));
104
+ return result;
105
+ }
106
+
107
+ if (sessions.length === 0) {
108
+ tui.info('No sessions found.');
109
+ return [];
110
+ }
111
+
112
+ const tableData = sessions.map((s) => {
113
+ const urlPath = new URL(s.url).pathname;
114
+ return {
115
+ ID: s.id,
116
+ Created: new Date(s.created_at).toLocaleString(),
117
+ Success: s.success ? '✓' : '✗',
118
+ Duration: s.duration ? `${(s.duration / 1_000_000).toFixed(0)}ms` : '-',
119
+ Method: s.method,
120
+ Path: urlPath.length > 50 ? urlPath.substring(0, 47) + '...' : urlPath,
121
+ Trigger: s.trigger,
122
+ Env: s.env,
123
+ };
124
+ });
125
+
126
+ tui.table(tableData, [
127
+ { name: 'ID', alignment: 'left' },
128
+ { name: 'Created', alignment: 'left' },
129
+ { name: 'Success', alignment: 'center' },
130
+ { name: 'Duration', alignment: 'right' },
131
+ { name: 'Method', alignment: 'left' },
132
+ { name: 'Path', alignment: 'left' },
133
+ { name: 'Trigger', alignment: 'left' },
134
+ { name: 'Env', alignment: 'left' },
135
+ ]);
136
+
137
+ return result;
138
+ } catch (ex) {
139
+ tui.fatal(
140
+ `Failed to list sessions: ${ex instanceof Error ? ex.message : String(ex)}`,
141
+ ErrorCode.API_ERROR
142
+ );
143
+ }
144
+ },
145
+ });