@agentuity/cli 2.0.7 → 2.0.8

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 (176) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +4 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  5. package/dist/cmd/build/vite/route-discovery.js +6 -0
  6. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  7. package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
  8. package/dist/cmd/cloud/sandbox/fs/rm.js +9 -3
  9. package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
  10. package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
  11. package/dist/cmd/cloud/sandbox/fs/rmdir.js +9 -3
  12. package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
  13. package/dist/cmd/coder/archive.d.ts +2 -0
  14. package/dist/cmd/coder/archive.d.ts.map +1 -0
  15. package/dist/cmd/coder/archive.js +57 -0
  16. package/dist/cmd/coder/archive.js.map +1 -0
  17. package/dist/cmd/coder/create.d.ts +2 -0
  18. package/dist/cmd/coder/create.d.ts.map +1 -0
  19. package/dist/cmd/coder/create.js +245 -0
  20. package/dist/cmd/coder/create.js.map +1 -0
  21. package/dist/cmd/coder/delete.d.ts +2 -0
  22. package/dist/cmd/coder/delete.d.ts.map +1 -0
  23. package/dist/cmd/coder/delete.js +64 -0
  24. package/dist/cmd/coder/delete.js.map +1 -0
  25. package/dist/cmd/coder/events.d.ts +2 -0
  26. package/dist/cmd/coder/events.d.ts.map +1 -0
  27. package/dist/cmd/coder/events.js +99 -0
  28. package/dist/cmd/coder/events.js.map +1 -0
  29. package/dist/cmd/coder/extension-path.d.ts +8 -0
  30. package/dist/cmd/coder/extension-path.d.ts.map +1 -0
  31. package/dist/cmd/coder/extension-path.js +59 -0
  32. package/dist/cmd/coder/extension-path.js.map +1 -0
  33. package/dist/cmd/coder/get.d.ts +2 -0
  34. package/dist/cmd/coder/get.d.ts.map +1 -0
  35. package/dist/cmd/coder/{inspect.js → get.js} +38 -42
  36. package/dist/cmd/coder/get.js.map +1 -0
  37. package/dist/cmd/coder/index.d.ts.map +1 -1
  38. package/dist/cmd/coder/index.js +52 -7
  39. package/dist/cmd/coder/index.js.map +1 -1
  40. package/dist/cmd/coder/list.d.ts.map +1 -1
  41. package/dist/cmd/coder/list.js +26 -42
  42. package/dist/cmd/coder/list.js.map +1 -1
  43. package/dist/cmd/coder/loop.d.ts +2 -0
  44. package/dist/cmd/coder/loop.d.ts.map +1 -0
  45. package/dist/cmd/coder/loop.js +78 -0
  46. package/dist/cmd/coder/loop.js.map +1 -0
  47. package/dist/cmd/coder/participants.d.ts +2 -0
  48. package/dist/cmd/coder/participants.d.ts.map +1 -0
  49. package/dist/cmd/coder/participants.js +93 -0
  50. package/dist/cmd/coder/participants.js.map +1 -0
  51. package/dist/cmd/coder/replay.d.ts +2 -0
  52. package/dist/cmd/coder/replay.d.ts.map +1 -0
  53. package/dist/cmd/coder/replay.js +53 -0
  54. package/dist/cmd/coder/replay.js.map +1 -0
  55. package/dist/cmd/coder/resolve-repo.d.ts +27 -0
  56. package/dist/cmd/coder/resolve-repo.d.ts.map +1 -0
  57. package/dist/cmd/coder/resolve-repo.js +97 -0
  58. package/dist/cmd/coder/resolve-repo.js.map +1 -0
  59. package/dist/cmd/coder/skill/buckets.d.ts +2 -0
  60. package/dist/cmd/coder/skill/buckets.d.ts.map +1 -0
  61. package/dist/cmd/coder/skill/buckets.js +174 -0
  62. package/dist/cmd/coder/skill/buckets.js.map +1 -0
  63. package/dist/cmd/coder/skill/delete.d.ts +2 -0
  64. package/dist/cmd/coder/skill/delete.d.ts.map +1 -0
  65. package/dist/cmd/coder/skill/delete.js +64 -0
  66. package/dist/cmd/coder/skill/delete.js.map +1 -0
  67. package/dist/cmd/coder/skill/index.d.ts +2 -0
  68. package/dist/cmd/coder/skill/index.d.ts.map +1 -0
  69. package/dist/cmd/coder/skill/index.js +33 -0
  70. package/dist/cmd/coder/skill/index.js.map +1 -0
  71. package/dist/cmd/coder/skill/list.d.ts +2 -0
  72. package/dist/cmd/coder/skill/list.d.ts.map +1 -0
  73. package/dist/cmd/coder/skill/list.js +93 -0
  74. package/dist/cmd/coder/skill/list.js.map +1 -0
  75. package/dist/cmd/coder/skill/save.d.ts +2 -0
  76. package/dist/cmd/coder/skill/save.d.ts.map +1 -0
  77. package/dist/cmd/coder/skill/save.js +77 -0
  78. package/dist/cmd/coder/skill/save.js.map +1 -0
  79. package/dist/cmd/coder/start.d.ts.map +1 -1
  80. package/dist/cmd/coder/start.js +87 -131
  81. package/dist/cmd/coder/start.js.map +1 -1
  82. package/dist/cmd/coder/tui-init.d.ts.map +1 -1
  83. package/dist/cmd/coder/tui-init.js +7 -2
  84. package/dist/cmd/coder/tui-init.js.map +1 -1
  85. package/dist/cmd/coder/update.d.ts +2 -0
  86. package/dist/cmd/coder/update.d.ts.map +1 -0
  87. package/dist/cmd/coder/update.js +126 -0
  88. package/dist/cmd/coder/update.js.map +1 -0
  89. package/dist/cmd/coder/users.d.ts +2 -0
  90. package/dist/cmd/coder/users.d.ts.map +1 -0
  91. package/dist/cmd/coder/users.js +97 -0
  92. package/dist/cmd/coder/users.js.map +1 -0
  93. package/dist/cmd/coder/workspace/create.d.ts +2 -0
  94. package/dist/cmd/coder/workspace/create.d.ts.map +1 -0
  95. package/dist/cmd/coder/workspace/create.js +97 -0
  96. package/dist/cmd/coder/workspace/create.js.map +1 -0
  97. package/dist/cmd/coder/workspace/delete.d.ts +2 -0
  98. package/dist/cmd/coder/workspace/delete.d.ts.map +1 -0
  99. package/dist/cmd/coder/workspace/delete.js +64 -0
  100. package/dist/cmd/coder/workspace/delete.js.map +1 -0
  101. package/dist/cmd/coder/workspace/get.d.ts +2 -0
  102. package/dist/cmd/coder/workspace/get.d.ts.map +1 -0
  103. package/dist/cmd/coder/workspace/get.js +109 -0
  104. package/dist/cmd/coder/workspace/get.js.map +1 -0
  105. package/dist/cmd/coder/workspace/index.d.ts +2 -0
  106. package/dist/cmd/coder/workspace/index.d.ts.map +1 -0
  107. package/dist/cmd/coder/workspace/index.js +38 -0
  108. package/dist/cmd/coder/workspace/index.js.map +1 -0
  109. package/dist/cmd/coder/workspace/list.d.ts +2 -0
  110. package/dist/cmd/coder/workspace/list.d.ts.map +1 -0
  111. package/dist/cmd/coder/workspace/list.js +93 -0
  112. package/dist/cmd/coder/workspace/list.js.map +1 -0
  113. package/dist/config.d.ts.map +1 -1
  114. package/dist/config.js +3 -3
  115. package/dist/config.js.map +1 -1
  116. package/dist/types.d.ts +1 -5
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js +0 -10
  119. package/dist/types.js.map +1 -1
  120. package/package.json +7 -6
  121. package/src/cli.ts +4 -2
  122. package/src/cmd/ai/prompt/agent.md +6 -6
  123. package/src/cmd/build/vite/route-discovery.ts +8 -0
  124. package/src/cmd/cloud/sandbox/fs/rm.ts +8 -3
  125. package/src/cmd/cloud/sandbox/fs/rmdir.ts +8 -3
  126. package/src/cmd/coder/archive.ts +59 -0
  127. package/src/cmd/coder/create.ts +268 -0
  128. package/src/cmd/coder/delete.ts +67 -0
  129. package/src/cmd/coder/events.ts +106 -0
  130. package/src/cmd/coder/extension-path.ts +71 -0
  131. package/src/cmd/coder/{inspect.ts → get.ts} +45 -69
  132. package/src/cmd/coder/index.ts +52 -7
  133. package/src/cmd/coder/list.ts +29 -89
  134. package/src/cmd/coder/loop.ts +85 -0
  135. package/src/cmd/coder/participants.ts +100 -0
  136. package/src/cmd/coder/replay.ts +58 -0
  137. package/src/cmd/coder/resolve-repo.ts +119 -0
  138. package/src/cmd/coder/skill/buckets.ts +191 -0
  139. package/src/cmd/coder/skill/delete.ts +67 -0
  140. package/src/cmd/coder/skill/index.ts +35 -0
  141. package/src/cmd/coder/skill/list.ts +97 -0
  142. package/src/cmd/coder/skill/save.ts +84 -0
  143. package/src/cmd/coder/start.ts +101 -174
  144. package/src/cmd/coder/tui-init.ts +7 -3
  145. package/src/cmd/coder/update.ts +128 -0
  146. package/src/cmd/coder/users.ts +101 -0
  147. package/src/cmd/coder/workspace/create.ts +104 -0
  148. package/src/cmd/coder/workspace/delete.ts +70 -0
  149. package/src/cmd/coder/workspace/get.ts +112 -0
  150. package/src/cmd/coder/workspace/index.ts +38 -0
  151. package/src/cmd/coder/workspace/list.ts +101 -0
  152. package/src/config.ts +4 -3
  153. package/src/types.ts +0 -10
  154. package/dist/cmd/coder/config/index.d.ts +0 -2
  155. package/dist/cmd/coder/config/index.d.ts.map +0 -1
  156. package/dist/cmd/coder/config/index.js +0 -20
  157. package/dist/cmd/coder/config/index.js.map +0 -1
  158. package/dist/cmd/coder/config/set.d.ts +0 -2
  159. package/dist/cmd/coder/config/set.d.ts.map +0 -1
  160. package/dist/cmd/coder/config/set.js +0 -100
  161. package/dist/cmd/coder/config/set.js.map +0 -1
  162. package/dist/cmd/coder/hub-url.d.ts +0 -47
  163. package/dist/cmd/coder/hub-url.d.ts.map +0 -1
  164. package/dist/cmd/coder/hub-url.js +0 -148
  165. package/dist/cmd/coder/hub-url.js.map +0 -1
  166. package/dist/cmd/coder/inspect.d.ts +0 -2
  167. package/dist/cmd/coder/inspect.d.ts.map +0 -1
  168. package/dist/cmd/coder/inspect.js.map +0 -1
  169. package/dist/coder-config.d.ts +0 -14
  170. package/dist/coder-config.d.ts.map +0 -1
  171. package/dist/coder-config.js +0 -119
  172. package/dist/coder-config.js.map +0 -1
  173. package/src/cmd/coder/config/index.ts +0 -20
  174. package/src/cmd/coder/config/set.ts +0 -112
  175. package/src/cmd/coder/hub-url.ts +0 -205
  176. package/src/coder-config.ts +0 -141
@@ -1,22 +1,20 @@
1
1
  import { z } from 'zod';
2
+ import {
3
+ CoderClient,
4
+ CoderSessionArchivedError,
5
+ CoderSessionNotFoundError,
6
+ } from '@agentuity/core/coder';
7
+ import { ValidationOutputError } from '@agentuity/core';
8
+
2
9
  import { createSubcommand } from '../../types';
3
10
  import * as tui from '../../tui';
4
11
  import { getCommand } from '../../command-prefix';
5
12
  import { ErrorCode } from '../../errors';
6
- import {
7
- clearStoredHubApiKeyOnUnauthorized,
8
- formatHubUnauthorizedMessage,
9
- formatMissingHubUrlMessage,
10
- getHubResponseErrorMessage,
11
- getHubUrlSetupGuidance,
12
- hubFetchHeaders,
13
- isHubUnauthorizedStatus,
14
- resolveHubApiKey,
15
- resolveHubUrl,
16
- } from './hub-url';
17
13
 
18
14
  function formatRelativeTime(isoDate: string): string {
19
- const diffMs = Date.now() - new Date(isoDate).getTime();
15
+ const parsed = new Date(isoDate).getTime();
16
+ if (Number.isNaN(parsed)) return 'unknown';
17
+ const diffMs = Math.max(0, Date.now() - parsed);
20
18
  const seconds = Math.floor(diffMs / 1000);
21
19
  if (seconds < 60) return `${seconds}s ago`;
22
20
  const minutes = Math.floor(seconds / 60);
@@ -38,17 +36,19 @@ function formatDuration(ms: number): string {
38
36
  return `${hours}h ${remainMin}m`;
39
37
  }
40
38
 
41
- export const inspectSubcommand = createSubcommand({
42
- name: 'inspect',
39
+ export const getSubcommand = createSubcommand({
40
+ name: 'get',
41
+ aliases: ['show', 'inspect'],
43
42
  description: 'Show detailed information about a Coder Hub session',
44
43
  tags: ['read-only', 'fast', 'requires-auth'],
44
+ requires: { auth: true, org: true },
45
45
  examples: [
46
46
  {
47
- command: getCommand('coder inspect codesess_abc123'),
48
- description: 'Inspect a session by ID',
47
+ command: getCommand('coder get codesess_abc123'),
48
+ description: 'Get a session by ID',
49
49
  },
50
50
  {
51
- command: getCommand('coder inspect codesess_abc123 --json'),
51
+ command: getCommand('coder get codesess_abc123 --json'),
52
52
  description: 'Get session details as JSON',
53
53
  },
54
54
  ],
@@ -58,20 +58,17 @@ export const inspectSubcommand = createSubcommand({
58
58
  session_id: z.string().describe('Coder session ID to inspect'),
59
59
  }),
60
60
  options: z.object({
61
- hubUrl: z.string().optional().describe('Hub URL override'),
61
+ url: z.string().optional().describe('Coder API URL override'),
62
62
  }),
63
63
  },
64
64
  async handler(ctx) {
65
- const { args, options, opts, config } = ctx;
65
+ const { args, options, opts } = ctx;
66
66
  const sessionId = args.session_id;
67
- const hubUrl = await resolveHubUrl(opts?.hubUrl, config);
68
-
69
- if (!hubUrl) {
70
- tui.fatal(formatMissingHubUrlMessage(), ErrorCode.NETWORK_ERROR);
71
- return;
72
- }
73
-
74
- const resolvedHubApiKey = await resolveHubApiKey(config);
67
+ const client = new CoderClient({
68
+ apiKey: ctx.auth.apiKey,
69
+ url: opts?.url,
70
+ orgId: ctx.orgId,
71
+ });
75
72
 
76
73
  let data: {
77
74
  sessionId: string;
@@ -79,18 +76,18 @@ export const inspectSubcommand = createSubcommand({
79
76
  status: string;
80
77
  createdAt: string;
81
78
  mode: string;
82
- context: {
79
+ context?: {
83
80
  branch?: string;
84
81
  workingDirectory?: string;
85
82
  };
86
- participants: Array<{
83
+ participants?: Array<{
87
84
  id: string;
88
85
  role: string;
89
86
  transport: string;
90
87
  connectedAt: string;
91
88
  idle: boolean;
92
89
  }>;
93
- tasks: Array<{
90
+ tasks?: Array<{
94
91
  taskId: string;
95
92
  agent: string;
96
93
  status: string;
@@ -99,7 +96,7 @@ export const inspectSubcommand = createSubcommand({
99
96
  startedAt: string;
100
97
  completedAt?: string;
101
98
  }>;
102
- agentActivity: Record<
99
+ agentActivity?: Record<
103
100
  string,
104
101
  {
105
102
  name: string;
@@ -112,46 +109,23 @@ export const inspectSubcommand = createSubcommand({
112
109
  };
113
110
 
114
111
  try {
115
- const resp = await fetch(`${hubUrl}/api/hub/session/${encodeURIComponent(sessionId)}`, {
116
- headers: hubFetchHeaders(undefined, resolvedHubApiKey.apiKey),
117
- signal: AbortSignal.timeout(10_000),
118
- });
119
- if (isHubUnauthorizedStatus(resp.status)) {
120
- const message = await getHubResponseErrorMessage(resp);
121
- const clearedStoredKey = await clearStoredHubApiKeyOnUnauthorized(
122
- resp.status,
123
- resolvedHubApiKey,
124
- config
125
- );
126
- tui.fatal(
127
- formatHubUnauthorizedMessage(hubUrl, message, { clearedStoredKey }),
128
- ErrorCode.API_ERROR
129
- );
130
- return;
131
- }
132
- if (resp.status === 404) {
112
+ data = (await client.getSession(sessionId)) as unknown as typeof data;
113
+ } catch (err) {
114
+ if (err instanceof CoderSessionNotFoundError) {
133
115
  tui.fatal(`Session not found: ${sessionId}`, ErrorCode.RESOURCE_NOT_FOUND);
134
116
  return;
135
117
  }
136
- if (resp.status === 410) {
118
+ if (err instanceof CoderSessionArchivedError) {
137
119
  tui.fatal(`Session has shut down: ${sessionId}`, ErrorCode.RESOURCE_NOT_FOUND);
138
120
  return;
139
121
  }
140
- if (!resp.ok) {
141
- const message = await getHubResponseErrorMessage(resp);
142
- tui.fatal(
143
- `Hub returned ${resp.status}: ${message}. Is the Coder Hub running at ${hubUrl}?`,
144
- ErrorCode.API_ERROR
145
- );
146
- return;
122
+
123
+ if (err instanceof ValidationOutputError) {
124
+ ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
125
+ ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
147
126
  }
148
- data = (await resp.json()) as typeof data;
149
- } catch (err) {
150
127
  const msg = err instanceof Error ? err.message : String(err);
151
- tui.fatal(
152
- `Could not connect to Coder Hub at ${hubUrl}: ${msg}\n\n${getHubUrlSetupGuidance()}`,
153
- ErrorCode.NETWORK_ERROR
154
- );
128
+ tui.fatal(`Failed to inspect Coder session ${sessionId}: ${msg}`, ErrorCode.NETWORK_ERROR);
155
129
  return;
156
130
  }
157
131
 
@@ -164,17 +138,18 @@ export const inspectSubcommand = createSubcommand({
164
138
  console.log();
165
139
  console.log(` Session: ${label} (${data.sessionId})`);
166
140
  const parts = [`Status: ${data.status}`, `Mode: ${data.mode}`];
167
- if (data.context.branch) parts.push(`Branch: ${data.context.branch}`);
141
+ if (data.context?.branch) parts.push(`Branch: ${data.context.branch}`);
168
142
  console.log(` ${parts.join(' | ')}`);
169
143
  console.log(` Created: ${data.createdAt}`);
170
144
 
171
145
  // Participants
172
146
  console.log();
173
147
  console.log(' Participants:');
174
- if (data.participants.length === 0) {
148
+ const participants = data.participants ?? [];
149
+ if (participants.length === 0) {
175
150
  console.log(' (none)');
176
151
  } else {
177
- for (const p of data.participants) {
152
+ for (const p of participants) {
178
153
  const idle = p.idle ? ' (idle)' : '';
179
154
  const connected = p.connectedAt
180
155
  ? ` connected ${formatRelativeTime(p.connectedAt)}`
@@ -186,10 +161,11 @@ export const inspectSubcommand = createSubcommand({
186
161
  }
187
162
 
188
163
  // Tasks
189
- if (data.tasks.length > 0) {
164
+ const tasks = data.tasks ?? [];
165
+ if (tasks.length > 0) {
190
166
  console.log();
191
167
  console.log(' Tasks:');
192
- for (const t of data.tasks) {
168
+ for (const t of tasks) {
193
169
  const dur = t.duration ? formatDuration(t.duration) : '-';
194
170
  const prompt = t.prompt.length > 40 ? t.prompt.slice(0, 37) + '...' : t.prompt;
195
171
  console.log(
@@ -199,7 +175,7 @@ export const inspectSubcommand = createSubcommand({
199
175
  }
200
176
 
201
177
  // Agent Activity
202
- const agents = Object.values(data.agentActivity);
178
+ const agents = Object.values(data.agentActivity ?? {});
203
179
  if (agents.length > 0) {
204
180
  console.log();
205
181
  console.log(' Agent Activity:');
@@ -1,8 +1,18 @@
1
1
  import { createCommand } from '../../types';
2
- import { configSubcommand } from './config';
3
2
  import { listSubcommand } from './list';
4
- import { inspectSubcommand } from './inspect';
3
+ import { getSubcommand } from './get';
5
4
  import { startSubcommand } from './start';
5
+ import { createCoderSubcommand } from './create';
6
+ import { deleteSubcommand } from './delete';
7
+ import { archiveSubcommand } from './archive';
8
+ import { updateSubcommand } from './update';
9
+ import { usersSubcommand } from './users';
10
+ import { loopSubcommand } from './loop';
11
+ import { replaySubcommand } from './replay';
12
+ import { participantsSubcommand } from './participants';
13
+ import { eventsSubcommand } from './events';
14
+ import { workspaceCommand } from './workspace';
15
+ import { skillCommand } from './skill';
6
16
  import { getCommand } from '../../command-prefix';
7
17
 
8
18
  export const command = createCommand({
@@ -14,19 +24,54 @@ export const command = createCommand({
14
24
  command: getCommand('coder start'),
15
25
  description: 'Start a Pi session connected to the Coder Hub',
16
26
  },
27
+ {
28
+ command: getCommand('coder create "Build a REST API"'),
29
+ description: 'Create a new Coder session with a task',
30
+ },
17
31
  {
18
32
  command: getCommand('coder ls'),
19
33
  description: 'List all active Coder Hub sessions',
20
34
  },
21
35
  {
22
- command: getCommand('coder inspect <session-id>'),
36
+ command: getCommand('coder get <session-id>'),
23
37
  description: 'Show detailed session information',
24
38
  },
25
39
  {
26
- command: getCommand('coder config set url https://hub.example.com'),
27
- description: 'Set the default Coder Hub URL for this profile',
40
+ command: getCommand('coder users'),
41
+ description: 'List known Coder Hub users',
42
+ },
43
+ {
44
+ command: getCommand('coder loop <session-id>'),
45
+ description: 'Get loop state for a session',
46
+ },
47
+ {
48
+ command: getCommand('coder events <session-id> --limit 100'),
49
+ description: 'Show recent event history for a session',
28
50
  },
51
+ {
52
+ command: getCommand('coder workspace list'),
53
+ description: 'List Coder workspaces',
54
+ },
55
+ {
56
+ command: getCommand('coder skill list'),
57
+ description: 'List saved skills',
58
+ },
59
+ ],
60
+ subcommands: [
61
+ startSubcommand,
62
+ createCoderSubcommand,
63
+ listSubcommand,
64
+ getSubcommand,
65
+ updateSubcommand,
66
+ deleteSubcommand,
67
+ archiveSubcommand,
68
+ usersSubcommand,
69
+ loopSubcommand,
70
+ replaySubcommand,
71
+ participantsSubcommand,
72
+ eventsSubcommand,
73
+ workspaceCommand,
74
+ skillCommand,
29
75
  ],
30
- subcommands: [startSubcommand, listSubcommand, inspectSubcommand, configSubcommand],
31
- optional: { auth: true },
76
+ requires: { auth: true, org: true },
32
77
  });
@@ -1,22 +1,19 @@
1
1
  import { z } from 'zod';
2
+ import {
3
+ CoderClient,
4
+ type CoderSessionListItem,
5
+ CoderSessionListItemSchema,
6
+ } from '@agentuity/core/coder';
7
+ import { ValidationOutputError } from '@agentuity/core';
2
8
  import { createSubcommand } from '../../types';
3
9
  import * as tui from '../../tui';
4
10
  import { getCommand } from '../../command-prefix';
5
11
  import { ErrorCode } from '../../errors';
6
- import {
7
- clearStoredHubApiKeyOnUnauthorized,
8
- formatHubUnauthorizedMessage,
9
- formatMissingHubUrlMessage,
10
- getHubResponseErrorMessage,
11
- getHubUrlSetupGuidance,
12
- hubFetchHeaders,
13
- isHubUnauthorizedStatus,
14
- resolveHubApiKey,
15
- resolveHubUrl,
16
- } from './hub-url';
17
12
 
18
13
  function formatRelativeTime(isoDate: string): string {
19
- const diffMs = Date.now() - new Date(isoDate).getTime();
14
+ const parsed = new Date(isoDate).getTime();
15
+ if (Number.isNaN(parsed)) return 'unknown';
16
+ const diffMs = Math.max(0, Date.now() - parsed);
20
17
  const seconds = Math.floor(diffMs / 1000);
21
18
  if (seconds < 60) return `${seconds}s ago`;
22
19
  const minutes = Math.floor(seconds / 60);
@@ -27,20 +24,6 @@ function formatRelativeTime(isoDate: string): string {
27
24
  return `${days}d ago`;
28
25
  }
29
26
 
30
- const SessionListResponseSchema = z.array(
31
- z.object({
32
- sessionId: z.string().describe('Session ID'),
33
- label: z.string().describe('Human-readable session label'),
34
- status: z.string().describe('Session status'),
35
- mode: z.string().describe('Session mode (sandbox or tui)'),
36
- createdAt: z.string().describe('Creation timestamp'),
37
- taskCount: z.number().describe('Number of tasks'),
38
- subAgentCount: z.number().describe('Number of sub-agents'),
39
- observerCount: z.number().describe('Number of observers'),
40
- participantCount: z.number().describe('Total participant count'),
41
- })
42
- );
43
-
44
27
  export const listSubcommand = createSubcommand({
45
28
  name: 'list',
46
29
  description: 'List active Coder Hub sessions',
@@ -57,79 +40,34 @@ export const listSubcommand = createSubcommand({
57
40
  ],
58
41
  aliases: ['ls'],
59
42
  idempotent: true,
43
+ requires: { auth: true, org: true },
60
44
  schema: {
61
45
  options: z.object({
62
- hubUrl: z.string().optional().describe('Hub URL override'),
46
+ url: z.string().optional().describe('Coder API URL override'),
63
47
  }),
64
- response: SessionListResponseSchema,
48
+ response: z.array(CoderSessionListItemSchema),
65
49
  },
66
50
  async handler(ctx) {
67
- const { options, opts, config } = ctx;
68
- const hubUrl = await resolveHubUrl(opts?.hubUrl, config);
69
-
70
- if (!hubUrl) {
71
- tui.fatal(formatMissingHubUrlMessage(), ErrorCode.NETWORK_ERROR);
72
- return [];
73
- }
74
-
75
- const resolvedHubApiKey = await resolveHubApiKey(config);
76
-
77
- let data: {
78
- sessions: {
79
- websocket: Array<{
80
- sessionId: string;
81
- label: string;
82
- status: string;
83
- mode: string;
84
- createdAt: string;
85
- taskCount: number;
86
- subAgentCount: number;
87
- observerCount: number;
88
- participantCount: number;
89
- }>;
90
- sandbox: Array<Record<string, unknown>>;
91
- };
92
- total: number;
93
- };
51
+ const { options, opts } = ctx;
52
+ const client = new CoderClient({
53
+ apiKey: ctx.auth.apiKey,
54
+ url: opts?.url,
55
+ orgId: ctx.orgId,
56
+ });
94
57
 
58
+ let sessions: CoderSessionListItem[] = [];
95
59
  try {
96
- const resp = await fetch(`${hubUrl}/api/hub/sessions`, {
97
- headers: hubFetchHeaders(undefined, resolvedHubApiKey.apiKey),
98
- signal: AbortSignal.timeout(10_000),
99
- });
100
- if (!resp.ok) {
101
- const message = await getHubResponseErrorMessage(resp);
102
- if (isHubUnauthorizedStatus(resp.status)) {
103
- const clearedStoredKey = await clearStoredHubApiKeyOnUnauthorized(
104
- resp.status,
105
- resolvedHubApiKey,
106
- config
107
- );
108
- tui.fatal(
109
- formatHubUnauthorizedMessage(hubUrl, message, { clearedStoredKey }),
110
- ErrorCode.API_ERROR
111
- );
112
- return [];
113
- }
114
-
115
- tui.fatal(
116
- `Hub returned ${resp.status}: ${message}. Is the Coder Hub running at ${hubUrl}?`,
117
- ErrorCode.API_ERROR
118
- );
119
- return [];
120
- }
121
- data = (await resp.json()) as typeof data;
60
+ const response = await client.listSessions();
61
+ sessions = response.sessions;
122
62
  } catch (err) {
63
+ if (err instanceof ValidationOutputError) {
64
+ ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
65
+ ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
66
+ }
123
67
  const msg = err instanceof Error ? err.message : String(err);
124
- tui.fatal(
125
- `Could not connect to Coder Hub at ${hubUrl}: ${msg}\n\n${getHubUrlSetupGuidance()}`,
126
- ErrorCode.NETWORK_ERROR
127
- );
128
- return [];
68
+ tui.fatal(`Failed to list Coder sessions: ${msg}`, ErrorCode.NETWORK_ERROR);
129
69
  }
130
70
 
131
- const sessions = data.sessions.websocket;
132
-
133
71
  if (options.json) {
134
72
  return sessions;
135
73
  }
@@ -140,10 +78,11 @@ export const listSubcommand = createSubcommand({
140
78
  }
141
79
 
142
80
  const tableData = sessions.map((s) => ({
143
- 'Session ID': s.sessionId.length > 20 ? s.sessionId.slice(0, 17) + '...' : s.sessionId,
81
+ 'Session ID': s.sessionId,
144
82
  Label: s.label || '-',
145
83
  Status: s.status,
146
84
  Mode: s.mode,
85
+ Owner: s.owner?.name ?? s.owner?.userId ?? '-',
147
86
  Observers: String(s.observerCount),
148
87
  Agents: String(s.subAgentCount),
149
88
  Tasks: String(s.taskCount),
@@ -155,6 +94,7 @@ export const listSubcommand = createSubcommand({
155
94
  { name: 'Label', alignment: 'left' },
156
95
  { name: 'Status', alignment: 'center' },
157
96
  { name: 'Mode', alignment: 'center' },
97
+ { name: 'Owner', alignment: 'left' },
158
98
  { name: 'Observers', alignment: 'right' },
159
99
  { name: 'Agents', alignment: 'right' },
160
100
  { name: 'Tasks', alignment: 'right' },
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod';
2
+ import { CoderClient } from '@agentuity/core/coder';
3
+ import { ValidationOutputError } from '@agentuity/core';
4
+ import { createSubcommand } from '../../types';
5
+ import * as tui from '../../tui';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
8
+
9
+ export const loopSubcommand = createSubcommand({
10
+ name: 'loop',
11
+ description: 'Get loop-mode state for a Coder Hub session',
12
+ tags: ['read-only', 'fast', 'requires-auth'],
13
+ idempotent: true,
14
+ requires: { auth: true, org: true },
15
+ examples: [
16
+ {
17
+ command: getCommand('coder loop codesess_abc123'),
18
+ description: 'Show loop state for a session',
19
+ },
20
+ {
21
+ command: getCommand('coder loop codesess_abc123 --json'),
22
+ description: 'Get loop state as JSON',
23
+ },
24
+ ],
25
+ schema: {
26
+ args: z.object({
27
+ sessionId: z.string().describe('Session ID to inspect loop state for'),
28
+ }),
29
+ options: z.object({
30
+ url: z.string().optional().describe('Coder API URL override'),
31
+ }),
32
+ },
33
+ async handler(ctx) {
34
+ const { args, opts, options } = ctx;
35
+ const client = new CoderClient({
36
+ apiKey: ctx.auth.apiKey,
37
+ url: opts?.url,
38
+ orgId: ctx.orgId,
39
+ });
40
+
41
+ try {
42
+ const state = await client.getLoopState(args.sessionId);
43
+
44
+ if (options.json) {
45
+ return state;
46
+ }
47
+
48
+ const rows: Array<{ Field: string; Value: string }> = [
49
+ { Field: 'Session ID', Value: state.sessionId },
50
+ { Field: 'Workflow Mode', Value: state.workflowMode },
51
+ ];
52
+
53
+ if (!state.loop) {
54
+ rows.push({ Field: 'Loop Status', Value: 'not active' });
55
+ } else {
56
+ rows.push({ Field: 'Loop Status', Value: state.loop.status });
57
+ rows.push({ Field: 'Iteration', Value: String(state.loop.iteration) });
58
+ rows.push({
59
+ Field: 'Max Iterations',
60
+ Value: String(state.loop.maxIterations ?? '-'),
61
+ });
62
+ rows.push({ Field: 'Goal', Value: state.loop.goal ?? '-' });
63
+ rows.push({ Field: 'Summary', Value: state.loop.summary ?? '-' });
64
+ rows.push({ Field: 'Next Action', Value: state.loop.nextAction ?? '-' });
65
+ }
66
+
67
+ tui.table(rows, [
68
+ { name: 'Field', alignment: 'left' },
69
+ { name: 'Value', alignment: 'left' },
70
+ ]);
71
+
72
+ return state;
73
+ } catch (err) {
74
+ if (err instanceof ValidationOutputError) {
75
+ ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
76
+ ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
77
+ }
78
+ const msg = err instanceof Error ? err.message : String(err);
79
+ tui.fatal(
80
+ `Failed to get loop state for ${args.sessionId}: ${msg}`,
81
+ ErrorCode.NETWORK_ERROR
82
+ );
83
+ }
84
+ },
85
+ });
@@ -0,0 +1,100 @@
1
+ import { z } from 'zod';
2
+ import { CoderClient } from '@agentuity/core/coder';
3
+ import { ValidationOutputError } from '@agentuity/core';
4
+ import { createSubcommand } from '../../types';
5
+ import * as tui from '../../tui';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
8
+
9
+ function formatRelativeTime(isoDate: string): string {
10
+ const parsed = new Date(isoDate).getTime();
11
+ if (Number.isNaN(parsed)) return 'unknown';
12
+ const diffMs = Math.max(0, Date.now() - parsed);
13
+ const seconds = Math.floor(diffMs / 1000);
14
+ if (seconds < 60) return `${seconds}s ago`;
15
+ const minutes = Math.floor(seconds / 60);
16
+ if (minutes < 60) return `${minutes}m ago`;
17
+ const hours = Math.floor(minutes / 60);
18
+ if (hours < 24) return `${hours}h ago`;
19
+ const days = Math.floor(hours / 24);
20
+ return `${days}d ago`;
21
+ }
22
+
23
+ export const participantsSubcommand = createSubcommand({
24
+ name: 'participants',
25
+ aliases: ['participant', 'members'],
26
+ description: 'List participants for a Coder Hub session',
27
+ tags: ['read-only', 'fast', 'requires-auth'],
28
+ idempotent: true,
29
+ requires: { auth: true, org: true },
30
+ examples: [
31
+ {
32
+ command: getCommand('coder participants codesess_abc123'),
33
+ description: 'List session participants',
34
+ },
35
+ {
36
+ command: getCommand('coder participants codesess_abc123 --json'),
37
+ description: 'Get session participants as JSON',
38
+ },
39
+ ],
40
+ schema: {
41
+ args: z.object({
42
+ sessionId: z.string().describe('Session ID to list participants for'),
43
+ }),
44
+ options: z.object({
45
+ url: z.string().optional().describe('Coder API URL override'),
46
+ }),
47
+ },
48
+ async handler(ctx) {
49
+ const { args, opts, options } = ctx;
50
+ const client = new CoderClient({
51
+ apiKey: ctx.auth.apiKey,
52
+ url: opts?.url,
53
+ orgId: ctx.orgId,
54
+ });
55
+
56
+ try {
57
+ const data = await client.listParticipants(args.sessionId);
58
+
59
+ if (options.json) {
60
+ return data;
61
+ }
62
+
63
+ if (data.participants.length === 0) {
64
+ tui.info(`No participants found for session ${args.sessionId}.`);
65
+ return data;
66
+ }
67
+
68
+ tui.table(
69
+ data.participants.map((p) => ({
70
+ ID: p.id,
71
+ Role: p.role,
72
+ 'Agent Role': p.agentRole ?? '-',
73
+ Transport: p.transport ?? '-',
74
+ Connected: p.connectedAt ? formatRelativeTime(p.connectedAt) : '-',
75
+ 'Last Activity': p.lastActivityAt ? formatRelativeTime(p.lastActivityAt) : '-',
76
+ })),
77
+ [
78
+ { name: 'ID', alignment: 'left' },
79
+ { name: 'Role', alignment: 'left' },
80
+ { name: 'Agent Role', alignment: 'left' },
81
+ { name: 'Transport', alignment: 'center' },
82
+ { name: 'Connected', alignment: 'right' },
83
+ { name: 'Last Activity', alignment: 'right' },
84
+ ]
85
+ );
86
+
87
+ return data;
88
+ } catch (err) {
89
+ if (err instanceof ValidationOutputError) {
90
+ ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
91
+ ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
92
+ }
93
+ const msg = err instanceof Error ? err.message : String(err);
94
+ tui.fatal(
95
+ `Failed to list participants for session ${args.sessionId}: ${msg}`,
96
+ ErrorCode.NETWORK_ERROR
97
+ );
98
+ }
99
+ },
100
+ });