@agentuity/cli 1.0.36 → 1.0.37

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 (51) hide show
  1. package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
  2. package/dist/cmd/cloud/sandbox/cp.js +31 -0
  3. package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
  4. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  5. package/dist/cmd/cloud/sandbox/exec.js +44 -11
  6. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  7. package/dist/cmd/cloud/task/get.d.ts.map +1 -1
  8. package/dist/cmd/cloud/task/get.js +58 -4
  9. package/dist/cmd/cloud/task/get.js.map +1 -1
  10. package/dist/cmd/cloud/task/util.d.ts +1 -0
  11. package/dist/cmd/cloud/task/util.d.ts.map +1 -1
  12. package/dist/cmd/cloud/task/util.js +13 -0
  13. package/dist/cmd/cloud/task/util.js.map +1 -1
  14. package/dist/cmd/coder/hub-url.d.ts +35 -0
  15. package/dist/cmd/coder/hub-url.d.ts.map +1 -0
  16. package/dist/cmd/coder/hub-url.js +101 -0
  17. package/dist/cmd/coder/hub-url.js.map +1 -0
  18. package/dist/cmd/coder/index.d.ts +2 -0
  19. package/dist/cmd/coder/index.d.ts.map +1 -0
  20. package/dist/cmd/coder/index.js +27 -0
  21. package/dist/cmd/coder/index.js.map +1 -0
  22. package/dist/cmd/coder/inspect.d.ts +2 -0
  23. package/dist/cmd/coder/inspect.d.ts.map +1 -0
  24. package/dist/cmd/coder/inspect.js +145 -0
  25. package/dist/cmd/coder/inspect.js.map +1 -0
  26. package/dist/cmd/coder/list.d.ts +2 -0
  27. package/dist/cmd/coder/list.d.ts.map +1 -0
  28. package/dist/cmd/coder/list.js +109 -0
  29. package/dist/cmd/coder/list.js.map +1 -0
  30. package/dist/cmd/coder/start.d.ts +2 -0
  31. package/dist/cmd/coder/start.d.ts.map +1 -0
  32. package/dist/cmd/coder/start.js +384 -0
  33. package/dist/cmd/coder/start.js.map +1 -0
  34. package/dist/cmd/dev/index.d.ts.map +1 -1
  35. package/dist/cmd/dev/index.js +4 -0
  36. package/dist/cmd/dev/index.js.map +1 -1
  37. package/dist/cmd/index.d.ts.map +1 -1
  38. package/dist/cmd/index.js +1 -0
  39. package/dist/cmd/index.js.map +1 -1
  40. package/package.json +6 -6
  41. package/src/cmd/cloud/sandbox/cp.ts +32 -0
  42. package/src/cmd/cloud/sandbox/exec.ts +62 -13
  43. package/src/cmd/cloud/task/get.ts +68 -4
  44. package/src/cmd/cloud/task/util.ts +18 -0
  45. package/src/cmd/coder/hub-url.ts +105 -0
  46. package/src/cmd/coder/index.ts +27 -0
  47. package/src/cmd/coder/inspect.ts +200 -0
  48. package/src/cmd/coder/list.ts +143 -0
  49. package/src/cmd/coder/start.ts +419 -0
  50. package/src/cmd/dev/index.ts +5 -0
  51. package/src/cmd/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -41,9 +41,9 @@
41
41
  "prepublishOnly": "bun run clean && bun run build"
42
42
  },
43
43
  "dependencies": {
44
- "@agentuity/auth": "1.0.36",
45
- "@agentuity/core": "1.0.36",
46
- "@agentuity/server": "1.0.36",
44
+ "@agentuity/auth": "1.0.37",
45
+ "@agentuity/core": "1.0.37",
46
+ "@agentuity/server": "1.0.37",
47
47
  "@datasert/cronjs-parser": "^1.4.0",
48
48
  "@vitejs/plugin-react": "^5.1.2",
49
49
  "acorn-loose": "^8.5.2",
@@ -60,10 +60,10 @@
60
60
  "typescript": "^5.9.0",
61
61
  "vite": "^7.2.7",
62
62
  "zod": "^4.3.5",
63
- "@agentuity/frontend": "1.0.36"
63
+ "@agentuity/frontend": "1.0.37"
64
64
  },
65
65
  "devDependencies": {
66
- "@agentuity/test-utils": "1.0.36",
66
+ "@agentuity/test-utils": "1.0.37",
67
67
  "@types/adm-zip": "^0.5.7",
68
68
  "@types/bun": "latest",
69
69
  "@types/tar-fs": "^2.0.4",
@@ -42,6 +42,10 @@ const SandboxCpResponseSchema = z.object({
42
42
  destination: z.string().describe('Destination path'),
43
43
  bytesTransferred: z.number().describe('Number of bytes transferred'),
44
44
  filesTransferred: z.number().describe('Number of files transferred'),
45
+ directoriesCreated: z
46
+ .array(z.string())
47
+ .optional()
48
+ .describe('Parent directories auto-created on the destination'),
45
49
  });
46
50
 
47
51
  export const cpSubcommand = createCommand({
@@ -156,6 +160,30 @@ function getAllFiles(dirPath: string, basePath: string = dirPath): string[] {
156
160
  return files;
157
161
  }
158
162
 
163
+ /**
164
+ * Computes the parent directories that would be auto-created by the server
165
+ * when writing files to the given paths. Filters out directories that always
166
+ * exist in a sandbox (/, /home, /home/agentuity).
167
+ */
168
+ function getImplicitDirectories(filePaths: string[]): string[] {
169
+ const dirs = new Set<string>();
170
+ // Directories that always exist in a sandbox
171
+ const knownDirs = new Set(['/', '/home', '/home/agentuity']);
172
+
173
+ for (const filePath of filePaths) {
174
+ let dir = dirname(filePath);
175
+ while (dir && dir !== '.' && dir !== '/') {
176
+ if (!knownDirs.has(dir)) {
177
+ dirs.add(dir);
178
+ }
179
+ const parent = dirname(dir);
180
+ if (parent === dir) break;
181
+ dir = parent;
182
+ }
183
+ }
184
+ return Array.from(dirs).sort();
185
+ }
186
+
159
187
  async function uploadToSandbox(
160
188
  client: APIClient,
161
189
  logger: Logger,
@@ -234,11 +262,13 @@ async function uploadSingleFile(
234
262
  tui.success(`Copied ${displayPath} → ${sandboxId}:${targetPath} (${buffer.length} bytes)`);
235
263
  }
236
264
 
265
+ const implicitDirs = getImplicitDirectories([targetPath]);
237
266
  return {
238
267
  source: displayPath,
239
268
  destination: `${sandboxId}:${targetPath}`,
240
269
  bytesTransferred: buffer.length,
241
270
  filesTransferred: 1,
271
+ directoriesCreated: implicitDirs.length > 0 ? implicitDirs : undefined,
242
272
  };
243
273
  }
244
274
 
@@ -281,11 +311,13 @@ async function uploadDirectory(
281
311
  );
282
312
  }
283
313
 
314
+ const implicitDirs = getImplicitDirectories(files.map((f) => f.path));
284
315
  return {
285
316
  source: localDir,
286
317
  destination: `${sandboxId}:${baseRemotePath}`,
287
318
  bytesTransferred: totalBytes,
288
319
  filesTransferred: allFiles.length,
320
+ directoriesCreated: implicitDirs.length > 0 ? implicitDirs : undefined,
289
321
  };
290
322
  }
291
323
 
@@ -15,6 +15,14 @@ const SandboxExecResponseSchema = z.object({
15
15
  status: z.string().describe('Execution status'),
16
16
  exitCode: z.number().optional().describe('Exit code (if completed)'),
17
17
  durationMs: z.number().optional().describe('Duration in milliseconds (if completed)'),
18
+ stdout: z
19
+ .string()
20
+ .optional()
21
+ .describe('Standard output (only when separate streams are available)'),
22
+ stderr: z
23
+ .string()
24
+ .optional()
25
+ .describe('Standard error output (only when separate streams are available)'),
18
26
  output: z.string().optional().describe('Combined stdout/stderr output'),
19
27
  });
20
28
 
@@ -68,16 +76,6 @@ export const execSubcommand = createCommand({
68
76
  process.on('SIGINT', handleSignal);
69
77
  process.on('SIGTERM', handleSignal);
70
78
 
71
- const outputChunks: string[] = [];
72
-
73
- // For JSON output, capture to buffer; otherwise stream to process
74
- const stdout = options.json
75
- ? createCaptureStream((chunk) => outputChunks.push(chunk))
76
- : process.stdout;
77
- const stderr = options.json
78
- ? createCaptureStream((chunk) => outputChunks.push(chunk))
79
- : process.stderr;
80
-
81
79
  try {
82
80
  const execution = await sandboxExecute(client, {
83
81
  sandboxId: args.sandboxId,
@@ -98,24 +96,69 @@ export const execSubcommand = createCommand({
98
96
  const isCombinedOutput =
99
97
  stdoutStreamUrl && stderrStreamUrl && stdoutStreamUrl === stderrStreamUrl;
100
98
 
99
+ // Set up stream capture — in JSON mode, capture to buffers;
100
+ // when streams are separate, capture stdout/stderr independently
101
+ const outputChunks: string[] = [];
102
+ const stdoutChunks: string[] = [];
103
+ const stderrChunks: string[] = [];
104
+
105
+ let stdoutWritable: NodeJS.WritableStream;
106
+ let stderrWritable: NodeJS.WritableStream;
107
+
108
+ if (options.json) {
109
+ if (isCombinedOutput) {
110
+ // Combined stream: can't distinguish stdout from stderr
111
+ stdoutWritable = createCaptureStream((chunk) => outputChunks.push(chunk));
112
+ stderrWritable = createCaptureStream((chunk) => outputChunks.push(chunk));
113
+ } else {
114
+ // Separate streams: capture each independently and also to combined output
115
+ stdoutWritable = createCaptureStream((chunk) => {
116
+ stdoutChunks.push(chunk);
117
+ outputChunks.push(chunk);
118
+ });
119
+ stderrWritable = createCaptureStream((chunk) => {
120
+ stderrChunks.push(chunk);
121
+ outputChunks.push(chunk);
122
+ });
123
+ }
124
+ } else {
125
+ stdoutWritable = process.stdout;
126
+ stderrWritable = process.stderr;
127
+ }
128
+
101
129
  if (isCombinedOutput) {
102
130
  // Stream combined output to stdout only to avoid duplicates
103
131
  logger.debug('using combined output stream (stdout === stderr): %s', stdoutStreamUrl);
104
132
  streamPromises.push(
105
- streamUrlToWritable(stdoutStreamUrl, stdout, streamAbortController.signal, logger)
133
+ streamUrlToWritable(
134
+ stdoutStreamUrl,
135
+ stdoutWritable,
136
+ streamAbortController.signal,
137
+ logger
138
+ )
106
139
  );
107
140
  } else {
108
141
  if (stdoutStreamUrl) {
109
142
  logger.debug('starting stdout stream from: %s', stdoutStreamUrl);
110
143
  streamPromises.push(
111
- streamUrlToWritable(stdoutStreamUrl, stdout, streamAbortController.signal, logger)
144
+ streamUrlToWritable(
145
+ stdoutStreamUrl,
146
+ stdoutWritable,
147
+ streamAbortController.signal,
148
+ logger
149
+ )
112
150
  );
113
151
  }
114
152
 
115
153
  if (stderrStreamUrl) {
116
154
  logger.debug('starting stderr stream from: %s', stderrStreamUrl);
117
155
  streamPromises.push(
118
- streamUrlToWritable(stderrStreamUrl, stderr, streamAbortController.signal, logger)
156
+ streamUrlToWritable(
157
+ stderrStreamUrl,
158
+ stderrWritable,
159
+ streamAbortController.signal,
160
+ logger
161
+ )
119
162
  );
120
163
  }
121
164
  }
@@ -145,6 +188,10 @@ export const execSubcommand = createCommand({
145
188
 
146
189
  const duration = Date.now() - started;
147
190
  const output = outputChunks.join('');
191
+ const stdoutOutput =
192
+ !isCombinedOutput && stdoutStreamUrl ? stdoutChunks.join('') : undefined;
193
+ const stderrOutput =
194
+ !isCombinedOutput && stderrStreamUrl ? stderrChunks.join('') : undefined;
148
195
 
149
196
  if (!options.json) {
150
197
  if (finalExecution.exitCode === 0) {
@@ -163,6 +210,8 @@ export const execSubcommand = createCommand({
163
210
  status: finalExecution.status,
164
211
  exitCode: finalExecution.exitCode,
165
212
  durationMs: finalExecution.durationMs,
213
+ stdout: options.json ? stdoutOutput : undefined,
214
+ stderr: options.json ? stderrOutput : undefined,
166
215
  output: options.json ? output : undefined,
167
216
  };
168
217
  } finally {
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { createCommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
- import { createStorageAdapter, cacheTaskId } from './util';
4
+ import { createStorageAdapterOptionalOrg, cacheTaskId } from './util';
5
5
  import { getCommand } from '../../../command-prefix';
6
6
 
7
7
  const EntityRefSchema = z
@@ -19,6 +19,15 @@ const UserEntityRefSchema = z
19
19
  })
20
20
  .optional();
21
21
 
22
+ const SubtaskSchema = z.object({
23
+ id: z.string().describe('Subtask ID'),
24
+ title: z.string().describe('Subtask title'),
25
+ type: z.string().describe('Subtask type'),
26
+ status: z.string().describe('Subtask status'),
27
+ priority: z.string().describe('Subtask priority'),
28
+ assignee: UserEntityRefSchema.describe('Subtask assignee'),
29
+ });
30
+
22
31
  const TaskGetResponseSchema = z.object({
23
32
  success: z.boolean().describe('Whether the operation succeeded'),
24
33
  task: z.object({
@@ -41,6 +50,7 @@ const TaskGetResponseSchema = z.object({
41
50
  closed_date: z.string().optional().describe('Date task was closed'),
42
51
  cancelled_date: z.string().optional().describe('Date task was cancelled'),
43
52
  }),
53
+ subtasks: z.array(SubtaskSchema).optional().describe('Subtasks of this task'),
44
54
  durationMs: z.number().describe('Operation duration in milliseconds'),
45
55
  });
46
56
 
@@ -60,21 +70,27 @@ export const getSubcommand = createCommand({
60
70
  command: getCommand('cloud task get task_abc123 --json'),
61
71
  description: 'Get task details as JSON',
62
72
  },
73
+ {
74
+ command: getCommand('cloud task get task_abc123 --no-subtasks'),
75
+ description: 'Get task details without subtasks',
76
+ },
63
77
  ],
64
78
  schema: {
65
79
  args: z.object({
66
80
  id: z.string().min(1).describe('the task ID to get'),
67
81
  }),
82
+ options: z.object({
83
+ 'no-subtasks': z.boolean().optional().describe('Do not show subtasks'),
84
+ }),
68
85
  response: TaskGetResponseSchema,
69
86
  },
70
87
 
71
88
  async handler(ctx) {
72
- const { args, options } = ctx;
89
+ const { args, opts, options } = ctx;
73
90
  const started = Date.now();
74
- const storage = await createStorageAdapter(ctx);
91
+ const storage = await createStorageAdapterOptionalOrg(ctx);
75
92
 
76
93
  const task = await storage.get(args.id);
77
- const durationMs = Date.now() - started;
78
94
 
79
95
  if (!task) {
80
96
  tui.fatal(`Task not found: ${args.id}`);
@@ -82,6 +98,37 @@ export const getSubcommand = createCommand({
82
98
 
83
99
  await cacheTaskId(ctx, task.id);
84
100
 
101
+ // Fetch subtasks unless disabled
102
+ let subtasksList: {
103
+ id: string;
104
+ title: string;
105
+ type: string;
106
+ status: string;
107
+ priority: string;
108
+ assignee?: { id: string; name: string; type?: 'human' | 'agent' };
109
+ }[] = [];
110
+ let subtasksError: string | undefined;
111
+ if (!opts['no-subtasks']) {
112
+ try {
113
+ const subtasksResult = await storage.list({ parent_id: task.id });
114
+ subtasksList = subtasksResult.tasks.map((st) => ({
115
+ id: st.id,
116
+ title: st.title,
117
+ type: st.type,
118
+ status: st.status,
119
+ priority: st.priority,
120
+ assignee: st.assignee,
121
+ }));
122
+ } catch (err) {
123
+ subtasksError = err instanceof Error ? err.message : 'Failed to fetch subtasks';
124
+ if (!options.json) {
125
+ tui.warn(`Could not load subtasks: ${subtasksError}`);
126
+ }
127
+ }
128
+ }
129
+
130
+ const durationMs = Date.now() - started;
131
+
85
132
  if (!options.json) {
86
133
  const tableData: Record<string, string> = {
87
134
  ID: task.id,
@@ -133,6 +180,21 @@ export const getSubcommand = createCommand({
133
180
  tui.header('Metadata');
134
181
  tui.json(task.metadata);
135
182
  }
183
+
184
+ // Show subtasks
185
+ if (subtasksList.length > 0) {
186
+ tui.newline();
187
+ tui.header('Subtasks');
188
+ const subtaskRows = subtasksList.map((st) => ({
189
+ ID: st.id,
190
+ Title: st.title,
191
+ Type: st.type,
192
+ Status: st.status,
193
+ Priority: st.priority,
194
+ Assignee: st.assignee?.name ?? 'Unassigned',
195
+ }));
196
+ tui.table(subtaskRows, ['ID', 'Title', 'Type', 'Status', 'Priority', 'Assignee']);
197
+ }
136
198
  }
137
199
 
138
200
  return {
@@ -157,6 +219,8 @@ export const getSubcommand = createCommand({
157
219
  closed_date: task.closed_date,
158
220
  cancelled_date: task.cancelled_date,
159
221
  },
222
+ subtasks: subtasksList.length > 0 ? subtasksList : undefined,
223
+ subtasksError,
160
224
  durationMs,
161
225
  };
162
226
  },
@@ -36,6 +36,24 @@ export async function createStorageAdapter(ctx: TaskContext) {
36
36
  return new TaskStorageService(baseUrl, adapter);
37
37
  }
38
38
 
39
+ export async function createStorageAdapterOptionalOrg(ctx: TaskContext) {
40
+ const orgId =
41
+ ctx.options.orgId ?? (process.env.AGENTUITY_CLOUD_ORG_ID || ctx.config?.preferences?.orgId);
42
+
43
+ const headers: Record<string, string> = {
44
+ Authorization: `Bearer ${ctx.auth.apiKey}`,
45
+ };
46
+ if (orgId) {
47
+ headers['x-agentuity-orgid'] = orgId;
48
+ }
49
+
50
+ const adapter = createServerFetchAdapter({ headers }, ctx.logger);
51
+
52
+ const region = await getDefaultRegion(ctx.config?.name ?? defaultProfileName, ctx.config);
53
+ const baseUrl = getCatalystUrl(region, ctx.config?.overrides);
54
+ return new TaskStorageService(baseUrl, adapter);
55
+ }
56
+
39
57
  export async function cacheTaskId(
40
58
  ctx: {
41
59
  config: Config | null;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Hub URL resolution for Coder CLI commands.
3
+ *
4
+ * Resolution priority:
5
+ * 1. --hub-url flag (explicit per-command override)
6
+ * 2. AGENTUITY_CODER_HUB_URL env var
7
+ * 3. AGENTUITY_DEVMODE_URL env var (dev tunnel URL)
8
+ */
9
+
10
+ import { getVersion } from '../../version';
11
+
12
+ /**
13
+ * Resolve the Hub HTTP base URL for REST API calls.
14
+ * Converts ws:// URLs to http:// automatically.
15
+ *
16
+ * @param flagUrl Optional --hub-url flag value
17
+ * @returns HTTP base URL (e.g. "http://localhost:3500") or null if Hub is unreachable
18
+ */
19
+ export async function resolveHubUrl(flagUrl?: string): Promise<string | null> {
20
+ // 1. Explicit flag
21
+ if (flagUrl) return normalizeToHttp(flagUrl);
22
+
23
+ // 2. Env var (explicit)
24
+ const envUrl = process.env.AGENTUITY_CODER_HUB_URL;
25
+ if (envUrl) return normalizeToHttp(envUrl);
26
+
27
+ // 3. Dev mode URL (tunnel)
28
+ const devUrl = process.env.AGENTUITY_DEVMODE_URL;
29
+ if (devUrl) return normalizeToHttp(devUrl);
30
+
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * Resolve the Hub WebSocket URL for Pi extension connections.
36
+ * Converts http:// URLs to ws:// automatically and ensures /api/ws path.
37
+ *
38
+ * @param flagUrl Optional --hub-url flag value
39
+ * @returns WebSocket URL (e.g. "ws://127.0.0.1:3500/api/ws") or null
40
+ */
41
+ export async function resolveHubWsUrl(flagUrl?: string): Promise<string | null> {
42
+ const httpUrl = await resolveHubUrl(flagUrl);
43
+ if (!httpUrl) return null;
44
+ return normalizeToWs(httpUrl);
45
+ }
46
+
47
+ /**
48
+ * Convert any URL form to an HTTP base URL (strip paths, convert ws->http).
49
+ */
50
+ function normalizeToHttp(url: string): string {
51
+ let normalized = url.trim().replace(/\/+$/, '');
52
+
53
+ // ws:// -> http://
54
+ if (normalized.startsWith('ws://')) normalized = 'http://' + normalized.slice(5);
55
+ else if (normalized.startsWith('wss://')) normalized = 'https://' + normalized.slice(6);
56
+
57
+ // Strip /api/ws or /api/hub/* paths to get base URL
58
+ normalized = normalized.replace(/\/api\/ws\b.*$/, '');
59
+ normalized = normalized.replace(/\/api\/hub\b.*$/, '');
60
+
61
+ return normalized.replace(/\/+$/, '');
62
+ }
63
+
64
+ /**
65
+ * Convert an HTTP base URL to a WebSocket URL with /api/ws path.
66
+ */
67
+ function normalizeToWs(httpUrl: string): string {
68
+ let wsUrl = httpUrl;
69
+ if (wsUrl.startsWith('http://')) wsUrl = 'ws://' + wsUrl.slice(7);
70
+ else if (wsUrl.startsWith('https://')) wsUrl = 'wss://' + wsUrl.slice(8);
71
+
72
+ try {
73
+ const parsed = new URL(wsUrl);
74
+ if (parsed.pathname !== '/api/ws') {
75
+ parsed.pathname = '/api/ws';
76
+ wsUrl = parsed.toString().replace(/\/$/, '');
77
+ }
78
+ } catch {
79
+ if (!wsUrl.endsWith('/api/ws')) {
80
+ wsUrl = wsUrl.replace(/\/?$/, '/api/ws');
81
+ }
82
+ }
83
+
84
+ return wsUrl;
85
+ }
86
+
87
+ /**
88
+ * Resolve the API key for Hub authentication.
89
+ * TODO: Remove/Change when we get Agentuity service level auth enabled, this is just temporary
90
+ */
91
+ export function resolveApiKey(): string | null {
92
+ return process.env.AGENTUITY_CODER_API_KEY || null;
93
+ }
94
+
95
+ /**
96
+ * Build headers object with API key if available.
97
+ * TODO: Remove/Change when we get Agentuity service level auth enabled, this is just temporary
98
+ */
99
+ export function hubFetchHeaders(extra?: Record<string, string>): Record<string, string> {
100
+ const headers: Record<string, string> = { ...extra };
101
+ headers['User-Agent'] = `Agentuity Coder/${getVersion()}`;
102
+ const apiKey = resolveApiKey();
103
+ if (apiKey) headers['x-agentuity-auth-api-key'] = apiKey;
104
+ return headers;
105
+ }
@@ -0,0 +1,27 @@
1
+ import { createCommand } from '../../types';
2
+ import { listSubcommand } from './list';
3
+ import { inspectSubcommand } from './inspect';
4
+ import { startSubcommand } from './start';
5
+ import { getCommand } from '../../command-prefix';
6
+
7
+ export const command = createCommand({
8
+ name: 'coder',
9
+ description: 'Coder Hub session management commands',
10
+ tags: ['requires-auth'],
11
+ examples: [
12
+ {
13
+ command: getCommand('coder start'),
14
+ description: 'Start a Pi session connected to the Coder Hub',
15
+ },
16
+ {
17
+ command: getCommand('coder ls'),
18
+ description: 'List all active Coder Hub sessions',
19
+ },
20
+ {
21
+ command: getCommand('coder inspect <session-id>'),
22
+ description: 'Show detailed session information',
23
+ },
24
+ ],
25
+ subcommands: [startSubcommand, listSubcommand, inspectSubcommand],
26
+ optional: { auth: true },
27
+ });