@agentuity/cli 1.0.35 → 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 (103) hide show
  1. package/bin/cli.ts +43 -11
  2. package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -3
  3. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  4. package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
  5. package/dist/cmd/cloud/keyvalue/delete-namespace.js +7 -5
  6. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  7. package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
  8. package/dist/cmd/cloud/keyvalue/delete.js +9 -3
  9. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  10. package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
  11. package/dist/cmd/cloud/keyvalue/get.js +6 -3
  12. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  13. package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
  14. package/dist/cmd/cloud/keyvalue/keys.js +9 -3
  15. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  16. package/dist/cmd/cloud/keyvalue/list-namespaces.js +3 -3
  17. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  18. package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
  19. package/dist/cmd/cloud/keyvalue/repl.js +3 -1
  20. package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
  21. package/dist/cmd/cloud/keyvalue/search.d.ts.map +1 -1
  22. package/dist/cmd/cloud/keyvalue/search.js +6 -3
  23. package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
  24. package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
  25. package/dist/cmd/cloud/keyvalue/set.js +7 -5
  26. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  27. package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
  28. package/dist/cmd/cloud/keyvalue/stats.js +6 -3
  29. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  30. package/dist/cmd/cloud/region/index.d.ts.map +1 -1
  31. package/dist/cmd/cloud/region/index.js +3 -1
  32. package/dist/cmd/cloud/region/index.js.map +1 -1
  33. package/dist/cmd/cloud/region/list.d.ts +2 -0
  34. package/dist/cmd/cloud/region/list.d.ts.map +1 -0
  35. package/dist/cmd/cloud/region/list.js +55 -0
  36. package/dist/cmd/cloud/region/list.js.map +1 -0
  37. package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
  38. package/dist/cmd/cloud/sandbox/cp.js +31 -0
  39. package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
  40. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  41. package/dist/cmd/cloud/sandbox/exec.js +44 -11
  42. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  43. package/dist/cmd/cloud/task/create.d.ts.map +1 -1
  44. package/dist/cmd/cloud/task/create.js +19 -0
  45. package/dist/cmd/cloud/task/create.js.map +1 -1
  46. package/dist/cmd/cloud/task/get.d.ts.map +1 -1
  47. package/dist/cmd/cloud/task/get.js +58 -4
  48. package/dist/cmd/cloud/task/get.js.map +1 -1
  49. package/dist/cmd/cloud/task/util.d.ts +1 -0
  50. package/dist/cmd/cloud/task/util.d.ts.map +1 -1
  51. package/dist/cmd/cloud/task/util.js +13 -0
  52. package/dist/cmd/cloud/task/util.js.map +1 -1
  53. package/dist/cmd/coder/hub-url.d.ts +35 -0
  54. package/dist/cmd/coder/hub-url.d.ts.map +1 -0
  55. package/dist/cmd/coder/hub-url.js +101 -0
  56. package/dist/cmd/coder/hub-url.js.map +1 -0
  57. package/dist/cmd/coder/index.d.ts +2 -0
  58. package/dist/cmd/coder/index.d.ts.map +1 -0
  59. package/dist/cmd/coder/index.js +27 -0
  60. package/dist/cmd/coder/index.js.map +1 -0
  61. package/dist/cmd/coder/inspect.d.ts +2 -0
  62. package/dist/cmd/coder/inspect.d.ts.map +1 -0
  63. package/dist/cmd/coder/inspect.js +145 -0
  64. package/dist/cmd/coder/inspect.js.map +1 -0
  65. package/dist/cmd/coder/list.d.ts +2 -0
  66. package/dist/cmd/coder/list.d.ts.map +1 -0
  67. package/dist/cmd/coder/list.js +109 -0
  68. package/dist/cmd/coder/list.js.map +1 -0
  69. package/dist/cmd/coder/start.d.ts +2 -0
  70. package/dist/cmd/coder/start.d.ts.map +1 -0
  71. package/dist/cmd/coder/start.js +384 -0
  72. package/dist/cmd/coder/start.js.map +1 -0
  73. package/dist/cmd/dev/index.d.ts.map +1 -1
  74. package/dist/cmd/dev/index.js +4 -0
  75. package/dist/cmd/dev/index.js.map +1 -1
  76. package/dist/cmd/index.d.ts.map +1 -1
  77. package/dist/cmd/index.js +1 -0
  78. package/dist/cmd/index.js.map +1 -1
  79. package/package.json +6 -6
  80. package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -3
  81. package/src/cmd/cloud/keyvalue/delete-namespace.ts +7 -5
  82. package/src/cmd/cloud/keyvalue/delete.ts +9 -3
  83. package/src/cmd/cloud/keyvalue/get.ts +6 -3
  84. package/src/cmd/cloud/keyvalue/keys.ts +9 -3
  85. package/src/cmd/cloud/keyvalue/list-namespaces.ts +3 -3
  86. package/src/cmd/cloud/keyvalue/repl.ts +3 -1
  87. package/src/cmd/cloud/keyvalue/search.ts +6 -3
  88. package/src/cmd/cloud/keyvalue/set.ts +7 -5
  89. package/src/cmd/cloud/keyvalue/stats.ts +6 -3
  90. package/src/cmd/cloud/region/index.ts +3 -1
  91. package/src/cmd/cloud/region/list.ts +62 -0
  92. package/src/cmd/cloud/sandbox/cp.ts +32 -0
  93. package/src/cmd/cloud/sandbox/exec.ts +62 -13
  94. package/src/cmd/cloud/task/create.ts +22 -0
  95. package/src/cmd/cloud/task/get.ts +68 -4
  96. package/src/cmd/cloud/task/util.ts +18 -0
  97. package/src/cmd/coder/hub-url.ts +105 -0
  98. package/src/cmd/coder/index.ts +27 -0
  99. package/src/cmd/coder/inspect.ts +200 -0
  100. package/src/cmd/coder/list.ts +143 -0
  101. package/src/cmd/coder/start.ts +419 -0
  102. package/src/cmd/dev/index.ts +5 -0
  103. 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.35",
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.35",
45
- "@agentuity/core": "1.0.35",
46
- "@agentuity/server": "1.0.35",
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.35"
63
+ "@agentuity/frontend": "1.0.37"
64
64
  },
65
65
  "devDependencies": {
66
- "@agentuity/test-utils": "1.0.35",
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",
@@ -13,14 +13,14 @@ export const createNamespaceSubcommand = createCommand({
13
13
  optional: { project: true },
14
14
  examples: [
15
15
  {
16
- command: getCommand('kv create-namespace production'),
16
+ command: getCommand('cloud kv create-namespace production'),
17
17
  description: 'Create production namespace',
18
18
  },
19
19
  {
20
- command: getCommand('kv create staging'),
20
+ command: getCommand('cloud kv create staging'),
21
21
  description: 'Create staging namespace (using alias)',
22
22
  },
23
- { command: getCommand('kv create cache'), description: 'Create cache namespace' },
23
+ { command: getCommand('cloud kv create cache'), description: 'Create cache namespace' },
24
24
  ],
25
25
  schema: {
26
26
  args: z.object({
@@ -14,21 +14,23 @@ export const deleteNamespaceSubcommand = createCommand({
14
14
  optional: { project: true },
15
15
  examples: [
16
16
  {
17
- command: getCommand('kv delete-namespace staging'),
17
+ command: getCommand('cloud kv delete-namespace staging'),
18
18
  description: 'Delete staging namespace (interactive)',
19
19
  },
20
20
  {
21
- command: getCommand('kv rm-namespace cache --confirm'),
21
+ command: getCommand('cloud kv rm-namespace cache --confirm'),
22
22
  description: 'Delete cache without confirmation',
23
23
  },
24
24
  {
25
- command: getCommand('kv delete-namespace production --confirm'),
25
+ command: getCommand('cloud kv delete-namespace production --confirm'),
26
26
  description: 'Force delete production',
27
27
  },
28
28
  ],
29
29
  schema: {
30
30
  args: z.object({
31
31
  name: z.string().min(1).max(64).describe('the namespace name'),
32
+ }),
33
+ options: z.object({
32
34
  confirm: z
33
35
  .boolean()
34
36
  .optional()
@@ -43,10 +45,10 @@ export const deleteNamespaceSubcommand = createCommand({
43
45
  },
44
46
 
45
47
  async handler(ctx) {
46
- const { args } = ctx;
48
+ const { args, opts } = ctx;
47
49
  const kv = await createStorageAdapter(ctx);
48
50
 
49
- if (!args.confirm) {
51
+ if (!opts?.confirm) {
50
52
  if (!process.stdin.isTTY) {
51
53
  tui.fatal(
52
54
  'No TTY and --confirm is not set. Refusing to delete',
@@ -19,10 +19,16 @@ export const deleteSubcommand = createCommand({
19
19
  requires: { auth: true, region: true },
20
20
  optional: { project: true },
21
21
  examples: [
22
- { command: getCommand('kv delete production user:123'), description: 'Delete user data' },
23
- { command: getCommand('kv delete cache session:abc'), description: 'Delete cached session' },
24
22
  {
25
- command: getCommand('kv rm staging cache:homepage'),
23
+ command: getCommand('cloud kv delete production user:123'),
24
+ description: 'Delete user data',
25
+ },
26
+ {
27
+ command: getCommand('cloud kv delete cache session:abc'),
28
+ description: 'Delete cached session',
29
+ },
30
+ {
31
+ command: getCommand('cloud kv rm staging cache:homepage'),
26
32
  description: 'Delete homepage cache (using alias)',
27
33
  },
28
34
  ],
@@ -18,9 +18,12 @@ export const getSubcommand = createCommand({
18
18
  requires: { auth: true, region: true },
19
19
  optional: { project: true },
20
20
  examples: [
21
- { command: getCommand('kv get production user:123'), description: 'Get user data' },
22
- { command: getCommand('kv get cache session:abc'), description: 'Get cached session' },
23
- { command: getCommand('kv get staging cache:homepage'), description: 'Get homepage cache' },
21
+ { command: getCommand('cloud kv get production user:123'), description: 'Get user data' },
22
+ { command: getCommand('cloud kv get cache session:abc'), description: 'Get cached session' },
23
+ {
24
+ command: getCommand('cloud kv get staging cache:homepage'),
25
+ description: 'Get homepage cache',
26
+ },
24
27
  ],
25
28
  schema: {
26
29
  args: z.object({
@@ -17,9 +17,15 @@ export const keysSubcommand = createCommand({
17
17
  optional: { project: true },
18
18
  idempotent: true,
19
19
  examples: [
20
- { command: getCommand('kv keys production'), description: 'List all keys in production' },
21
- { command: getCommand('kv ls cache'), description: 'List all cached keys (using alias)' },
22
- { command: getCommand('kv list staging'), description: 'List all staging keys' },
20
+ {
21
+ command: getCommand('cloud kv keys production'),
22
+ description: 'List all keys in production',
23
+ },
24
+ {
25
+ command: getCommand('cloud kv ls cache'),
26
+ description: 'List all cached keys (using alias)',
27
+ },
28
+ { command: getCommand('cloud kv list staging'), description: 'List all staging keys' },
23
29
  ],
24
30
  schema: {
25
31
  args: z.object({
@@ -13,9 +13,9 @@ export const listNamespacesSubcommand = createCommand({
13
13
  requires: { auth: true, region: true },
14
14
  optional: { project: true },
15
15
  examples: [
16
- { command: getCommand('kv list-namespaces'), description: 'List all namespaces' },
17
- { command: getCommand('kv namespaces'), description: 'List namespaces (using alias)' },
18
- { command: getCommand('kv ns'), description: 'List namespaces (short alias)' },
16
+ { command: getCommand('cloud kv list-namespaces'), description: 'List all namespaces' },
17
+ { command: getCommand('cloud kv namespaces'), description: 'List namespaces (using alias)' },
18
+ { command: getCommand('cloud kv ns'), description: 'List namespaces (short alias)' },
19
19
  ],
20
20
  schema: {
21
21
  options: z.object({
@@ -13,7 +13,9 @@ export const replSubcommand = createCommand({
13
13
  idempotent: false,
14
14
  requires: { auth: true, region: true },
15
15
  optional: { project: true },
16
- examples: [{ command: getCommand('kv repl'), description: 'Start interactive KV session' }],
16
+ examples: [
17
+ { command: getCommand('cloud kv repl'), description: 'Start interactive KV session' },
18
+ ],
17
19
 
18
20
  async handler(ctx) {
19
21
  showBanner(undefined, true);
@@ -25,14 +25,17 @@ export const searchSubcommand = createCommand({
25
25
  idempotent: true,
26
26
  examples: [
27
27
  {
28
- command: getCommand('kv search production user'),
28
+ command: getCommand('cloud kv search production user'),
29
29
  description: 'Find all user-related keys',
30
30
  },
31
31
  {
32
- command: getCommand('kv search cache session'),
32
+ command: getCommand('cloud kv search cache session'),
33
33
  description: 'Find all session keys in cache',
34
34
  },
35
- { command: getCommand('kv search staging config'), description: 'Find all config keys' },
35
+ {
36
+ command: getCommand('cloud kv search staging config'),
37
+ description: 'Find all config keys',
38
+ },
36
39
  ],
37
40
  schema: {
38
41
  args: z.object({
@@ -24,16 +24,16 @@ export const setSubcommand = createCommand({
24
24
  examples: [
25
25
  {
26
26
  command: getCommand(
27
- 'kv set production user:123 \'{"name":"Alice","email":"alice@example.com"}\''
27
+ 'cloud kv set production user:123 \'{"name":"Alice","email":"alice@example.com"}\''
28
28
  ),
29
29
  description: 'Store user data',
30
30
  },
31
31
  {
32
- command: getCommand('kv set cache session:abc "session-data-here" --ttl 3600'),
32
+ command: getCommand('cloud kv set cache session:abc "session-data-here" --ttl 3600'),
33
33
  description: 'Store session with 1h TTL',
34
34
  },
35
35
  {
36
- command: getCommand('kv set staging cache:homepage "<!DOCTYPE html>..." --ttl 600'),
36
+ command: getCommand('cloud kv set staging cache:homepage "<!DOCTYPE html>..." --ttl 600'),
37
37
  description: 'Cache homepage for 10m',
38
38
  },
39
39
  ],
@@ -42,6 +42,8 @@ export const setSubcommand = createCommand({
42
42
  namespace: z.string().min(1).max(64).describe('the namespace name'),
43
43
  key: z.string().min(1).max(64).describe('the key name'),
44
44
  value: z.string().min(1).describe('the value'),
45
+ }),
46
+ options: z.object({
45
47
  ttl: z.coerce
46
48
  .number()
47
49
  .refine((val) => val >= 0, {
@@ -54,11 +56,11 @@ export const setSubcommand = createCommand({
54
56
  },
55
57
 
56
58
  async handler(ctx) {
57
- const { args, options } = ctx;
59
+ const { args, opts, options } = ctx;
58
60
  const started = Date.now();
59
61
  const storage = await createStorageAdapter(ctx);
60
62
  const contentType = isPossiblyJSON(args.value) ? 'application/json' : 'text/plain';
61
- const ttl = args.ttl;
63
+ const ttl = opts?.ttl;
62
64
  await storage.set(args.namespace, args.key, args.value, {
63
65
  contentType,
64
66
  ttl,
@@ -40,12 +40,15 @@ export const statsSubcommand = createCommand({
40
40
  optional: { project: true },
41
41
  idempotent: true,
42
42
  examples: [
43
- { command: getCommand('kv stats'), description: 'Show stats for all namespaces' },
43
+ { command: getCommand('cloud kv stats'), description: 'Show stats for all namespaces' },
44
44
  {
45
- command: getCommand('kv stats production'),
45
+ command: getCommand('cloud kv stats production'),
46
46
  description: 'Show stats for production namespace',
47
47
  },
48
- { command: getCommand('kv stats cache'), description: 'Show stats for cache namespace' },
48
+ {
49
+ command: getCommand('cloud kv stats cache'),
50
+ description: 'Show stats for cache namespace',
51
+ },
49
52
  ],
50
53
  schema: {
51
54
  args: z.object({
@@ -3,6 +3,7 @@ import { createSubcommand, createCommand } from '../../../types';
3
3
  import { getCommand } from '../../../command-prefix';
4
4
  import { saveRegion, clearRegion } from '../../../config';
5
5
  import * as tui from '../../../tui';
6
+ import { listSubcommand } from './list';
6
7
 
7
8
  const selectCommand = createSubcommand({
8
9
  name: 'select',
@@ -150,8 +151,9 @@ export const regionSubcommand = createCommand({
150
151
  description: 'Manage default cloud region preference',
151
152
  tags: ['fast'],
152
153
  examples: [
154
+ { command: getCommand('cloud region list'), description: 'List available regions' },
153
155
  { command: getCommand('cloud region select'), description: 'Set default region' },
154
156
  { command: getCommand('cloud region current'), description: 'Show current default' },
155
157
  ],
156
- subcommands: [selectCommand, unselectCommand, currentCommand],
158
+ subcommands: [listSubcommand, selectCommand, unselectCommand, currentCommand],
157
159
  });
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import { getCommand } from '../../../command-prefix';
4
+ import * as tui from '../../../tui';
5
+
6
+ const RegionSchema = z.object({
7
+ region: z.string().describe('Region code'),
8
+ description: z.string().describe('Human-readable region description'),
9
+ default: z.boolean().describe('Whether this is the default region'),
10
+ });
11
+
12
+ export const listSubcommand = createSubcommand({
13
+ name: 'list',
14
+ description: 'List available cloud regions',
15
+ aliases: ['ls'],
16
+ tags: ['read-only', 'fast', 'requires-auth'],
17
+ requires: { auth: true, regions: true },
18
+ idempotent: true,
19
+ examples: [
20
+ { command: getCommand('cloud region list'), description: 'List all available regions' },
21
+ {
22
+ command: getCommand('cloud region ls'),
23
+ description: 'List all available regions (short alias)',
24
+ },
25
+ {
26
+ command: getCommand('--json cloud region list'),
27
+ description: 'List regions in JSON format',
28
+ },
29
+ ],
30
+ schema: {
31
+ response: z.array(RegionSchema),
32
+ },
33
+
34
+ async handler(ctx) {
35
+ const { regions, options, config } = ctx;
36
+ const defaultRegion = config?.preferences?.region ?? null;
37
+
38
+ const result = regions.map((r) => ({
39
+ region: r.region,
40
+ description: r.description,
41
+ default: r.region === defaultRegion,
42
+ }));
43
+
44
+ if (!options.json) {
45
+ tui.info(`Regions (${regions.length})`);
46
+
47
+ const tableData = regions.map((r) => ({
48
+ Code: r.region,
49
+ Description: r.description,
50
+ Default: r.region === defaultRegion ? 'Yes' : '',
51
+ }));
52
+
53
+ tui.table(tableData, [
54
+ { name: 'Code', alignment: 'left' },
55
+ { name: 'Description', alignment: 'left' },
56
+ { name: 'Default', alignment: 'center' },
57
+ ]);
58
+ }
59
+
60
+ return result;
61
+ },
62
+ });
@@ -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 {
@@ -18,6 +18,10 @@ const TaskCreateResponseSchema = z.object({
18
18
  status: z.string().describe('Task status'),
19
19
  priority: z.string().describe('Task priority'),
20
20
  created_at: z.string().describe('Creation timestamp'),
21
+ tags: z
22
+ .array(z.object({ id: z.string(), name: z.string() }))
23
+ .optional()
24
+ .describe('Tags attached to the task'),
21
25
  }),
22
26
  attachment: z
23
27
  .object({
@@ -47,6 +51,12 @@ export const createSubcommand = createCommand({
47
51
  ),
48
52
  description: 'Create a feature with priority and description',
49
53
  },
54
+ {
55
+ command: getCommand(
56
+ 'cloud task create "Fix login bug" --type bug --tag sandbox --tag regression'
57
+ ),
58
+ description: 'Create a task with tags (auto-creates tags that do not exist)',
59
+ },
50
60
  {
51
61
  command: getCommand(
52
62
  'cloud task create "Q1 Planning" --type epic --created-id agent_001 --metadata \'{"team":"engineering"}\''
@@ -90,6 +100,10 @@ export const createSubcommand = createCommand({
90
100
  .describe('initial task status (default: open)'),
91
101
  parentId: z.string().optional().describe('parent task ID for subtasks'),
92
102
  assignedId: z.string().optional().describe('ID of the assigned agent or user'),
103
+ tag: z
104
+ .array(z.string())
105
+ .optional()
106
+ .describe('tag name to attach (repeatable, auto-creates missing tags)'),
93
107
  metadata: z.string().optional().describe('JSON metadata object'),
94
108
  file: z.string().optional().describe('file path to attach to the task'),
95
109
  }),
@@ -172,6 +186,7 @@ export const createSubcommand = createCommand({
172
186
  status: opts.status as TaskStatus,
173
187
  parent_id: opts.parentId,
174
188
  assigned_id: opts.assignedId,
189
+ tag_ids: opts.tag,
175
190
  metadata,
176
191
  });
177
192
 
@@ -228,6 +243,10 @@ export const createSubcommand = createCommand({
228
243
  tableData['Description'] = task.description;
229
244
  }
230
245
 
246
+ if (task.tags?.length) {
247
+ tableData['Tags'] = task.tags.map((t) => t.name).join(', ');
248
+ }
249
+
231
250
  if (project) {
232
251
  tableData['Project'] = project.name;
233
252
  }
@@ -248,6 +267,9 @@ export const createSubcommand = createCommand({
248
267
  status: task.status,
249
268
  priority: task.priority,
250
269
  created_at: task.created_at,
270
+ ...(task.tags?.length
271
+ ? { tags: task.tags.map((t) => ({ id: t.id, name: t.name })) }
272
+ : {}),
251
273
  },
252
274
  ...(attachmentInfo ? { attachment: attachmentInfo } : {}),
253
275
  durationMs,