Skip to content

Commit 6c7d63e

Browse files
khaliqgantRicky Schema Cascade
andauthored
Improve deployment list and logs CLI (#126)
* Improve deployment list and logs CLI * Fix deployment log tail ordering --------- Co-authored-by: Ricky Schema Cascade <ricky@agent-relay.com>
1 parent 3f7ca1f commit 6c7d63e

3 files changed

Lines changed: 392 additions & 18 deletions

File tree

packages/cli/src/cli.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import {
6060
import ora, { type Ora } from 'ora';
6161
import { runDeploy, runLogin, runLogout } from './deploy-command.js';
6262
import { runDestroy } from './destroy-command.js';
63-
import { runDeploymentList } from './list-command.js';
63+
import { runDeploymentList, runDeploymentLogs } from './list-command.js';
6464
import {
6565
startLaunchMetadataRecording,
6666
type LaunchMetadataRun
@@ -206,6 +206,7 @@ Commands:
206206
--input KEY=value override a declared persona input
207207
(repeat for multiple)
208208
deployments list List deployed cloud agents in the active workspace.
209+
deployments logs Show structured logs for a deployed cloud agent.
209210
destroy <persona-or-agent-id> [flags]
210211
Tear down a deployed agent: cancel all schedules and
211212
mark the agent as destroyed in the workspace. Accepts
@@ -3805,14 +3806,18 @@ export async function main(): Promise<void> {
38053806
if (subcommand === 'deployments') {
38063807
const [action, ...extra] = rest;
38073808
if (!action || action === '-h' || action === '--help') {
3808-
process.stdout.write('Usage: agentworkforce deployments list [flags]\n');
3809+
process.stdout.write('Usage: agentworkforce deployments <list|logs> [flags]\n');
38093810
process.exit(action ? 0 : 1);
38103811
}
3811-
if (action !== 'list') {
3812-
die(`deployments: unknown action "${action}". Expected: list`);
3812+
if (action === 'list') {
3813+
await runDeploymentList(extra);
3814+
return;
38133815
}
3814-
await runDeploymentList(extra);
3815-
return;
3816+
if (action === 'logs') {
3817+
await runDeploymentLogs(extra);
3818+
return;
3819+
}
3820+
die(`deployments: unknown action "${action}". Expected: list, logs`);
38163821
}
38173822

38183823
if (subcommand === 'destroy') {

packages/cli/src/list-command.test.ts

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import test from 'node:test';
22
import assert from 'node:assert/strict';
3-
import { formatDeploymentsTable, parseDeploymentListArgs } from './list-command.js';
3+
import {
4+
formatDeploymentLogEntries,
5+
formatDeploymentsTable,
6+
parseDeploymentListArgs,
7+
parseDeploymentLogsArgs,
8+
tailLogEntriesFromNewestFiles
9+
} from './list-command.js';
410

511
test('parseDeploymentListArgs accepts deployment list filters', () => {
612
assert.deepEqual(
@@ -30,7 +36,8 @@ test('formatDeploymentsTable renders agent rows', () => {
3036
const out = formatDeploymentsTable([
3137
{
3238
agentId: 'b2f111111111111111111111e8c2',
33-
personaId: 'weekly-digest',
39+
personaId: '7133e815-8c84-5d05-a08b-e434006b11ac',
40+
personaSlug: 'weekly-digest',
3441
deployedName: 'Weekly Digest',
3542
status: 'active',
3643
createdAt: '2026-05-13T09:11:00.000Z',
@@ -39,8 +46,66 @@ test('formatDeploymentsTable renders agent rows', () => {
3946
deployedByUserId: 'user-1'
4047
}
4148
]);
42-
assert.match(out, /agentId\s+persona\s+status\s+deployed\s+lastUsed/);
49+
assert.match(out, /name\s+status\s+deployed\s+lastUsed\s+agentId/);
4350
assert.match(out, /b2f1\.\.\.e8c2/);
44-
assert.match(out, /weekly-digest/);
51+
assert.match(out, /Weekly Digest/);
52+
assert.doesNotMatch(out, /7133e815/);
4553
assert.match(out, /2026-05-13 09:11 UTC/);
4654
});
55+
56+
test('parseDeploymentLogsArgs accepts selector and log flags', () => {
57+
assert.deepEqual(
58+
parseDeploymentLogsArgs([
59+
'Weekly Digest',
60+
'--workspace=ws-1',
61+
'--path',
62+
'/_logs/ws-1/2026-05-19.jsonl',
63+
'--tail',
64+
'25',
65+
'--cloud-url',
66+
'https://cloud.example.test',
67+
'--json',
68+
'--no-prompt'
69+
]),
70+
{
71+
selector: 'Weekly Digest',
72+
workspace: 'ws-1',
73+
path: '/_logs/ws-1/2026-05-19.jsonl',
74+
tail: 25,
75+
cloudUrl: 'https://cloud.example.test',
76+
json: true,
77+
noPrompt: true
78+
}
79+
);
80+
});
81+
82+
test('formatDeploymentLogEntries renders structured log rows', () => {
83+
const out = formatDeploymentLogEntries([
84+
{
85+
ts: '2026-05-19T13:00:00.000Z',
86+
level: 'info',
87+
agentId: 'agent-1',
88+
msg: 'handled event'
89+
}
90+
]);
91+
assert.match(out, /2026-05-19T13:00:00.000Z\s+INFO\s+agent-1\s+handled event/);
92+
});
93+
94+
test('tailLogEntriesFromNewestFiles keeps the newest entries across files', () => {
95+
const entries = tailLogEntriesFromNewestFiles(
96+
[
97+
[
98+
{ ts: '2026-05-19T13:00:00.000Z', msg: 'newer-a' },
99+
{ ts: '2026-05-19T14:00:00.000Z', msg: 'newer-b' },
100+
{ ts: '2026-05-19T15:00:00.000Z', msg: 'newer-c' }
101+
],
102+
[
103+
{ ts: '2026-05-18T10:00:00.000Z', msg: 'older-a' },
104+
{ ts: '2026-05-18T11:00:00.000Z', msg: 'older-b' }
105+
]
106+
],
107+
3
108+
);
109+
110+
assert.deepEqual(entries.map((entry) => entry.msg), ['newer-a', 'newer-b', 'newer-c']);
111+
});

0 commit comments

Comments
 (0)