@agentuity/cli 2.0.11 → 2.0.13

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 (274) hide show
  1. package/dist/cache/resource-region.d.ts.map +1 -1
  2. package/dist/cache/resource-region.js +48 -25
  3. package/dist/cache/resource-region.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +15 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts +20 -0
  8. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  9. package/dist/cmd/build/vite/bun-dev-server.js +62 -4
  10. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  11. package/dist/cmd/build/vite/index.d.ts +0 -1
  12. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  13. package/dist/cmd/build/vite/index.js +0 -1
  14. package/dist/cmd/build/vite/index.js.map +1 -1
  15. package/dist/cmd/build/vite/static-renderer.d.ts +17 -0
  16. package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
  17. package/dist/cmd/build/vite/static-renderer.js +18 -6
  18. package/dist/cmd/build/vite/static-renderer.js.map +1 -1
  19. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  20. package/dist/cmd/build/vite/vite-asset-server-config.js +34 -27
  21. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  22. package/dist/cmd/build/vite/vite-asset-server.d.ts +9 -0
  23. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/vite-asset-server.js +5 -1
  25. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  26. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/vite-builder.js +12 -1
  28. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  29. package/dist/cmd/build/vite/ws-proxy.d.ts +15 -1
  30. package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/ws-proxy.js +33 -0
  32. package/dist/cmd/build/vite/ws-proxy.js.map +1 -1
  33. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deploy.js +98 -39
  35. package/dist/cmd/cloud/deploy.js.map +1 -1
  36. package/dist/cmd/cloud/sandbox/checkpoint/create.d.ts.map +1 -1
  37. package/dist/cmd/cloud/sandbox/checkpoint/create.js +3 -4
  38. package/dist/cmd/cloud/sandbox/checkpoint/create.js.map +1 -1
  39. package/dist/cmd/cloud/sandbox/checkpoint/delete.d.ts.map +1 -1
  40. package/dist/cmd/cloud/sandbox/checkpoint/delete.js +3 -4
  41. package/dist/cmd/cloud/sandbox/checkpoint/delete.js.map +1 -1
  42. package/dist/cmd/cloud/sandbox/checkpoint/list.d.ts.map +1 -1
  43. package/dist/cmd/cloud/sandbox/checkpoint/list.js +3 -4
  44. package/dist/cmd/cloud/sandbox/checkpoint/list.js.map +1 -1
  45. package/dist/cmd/cloud/sandbox/checkpoint/restore.d.ts.map +1 -1
  46. package/dist/cmd/cloud/sandbox/checkpoint/restore.js +3 -4
  47. package/dist/cmd/cloud/sandbox/checkpoint/restore.js.map +1 -1
  48. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  49. package/dist/cmd/cloud/sandbox/create.js +13 -4
  50. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  51. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  52. package/dist/cmd/cloud/sandbox/delete.js +3 -4
  53. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  54. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -1
  55. package/dist/cmd/cloud/sandbox/env.js +3 -5
  56. package/dist/cmd/cloud/sandbox/env.js.map +1 -1
  57. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  58. package/dist/cmd/cloud/sandbox/exec.js +114 -41
  59. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  60. package/dist/cmd/cloud/sandbox/execution/list.d.ts.map +1 -1
  61. package/dist/cmd/cloud/sandbox/execution/list.js +3 -5
  62. package/dist/cmd/cloud/sandbox/execution/list.js.map +1 -1
  63. package/dist/cmd/cloud/sandbox/fs/cp.d.ts.map +1 -1
  64. package/dist/cmd/cloud/sandbox/fs/cp.js +61 -113
  65. package/dist/cmd/cloud/sandbox/fs/cp.js.map +1 -1
  66. package/dist/cmd/cloud/sandbox/fs/download.d.ts.map +1 -1
  67. package/dist/cmd/cloud/sandbox/fs/download.js +11 -22
  68. package/dist/cmd/cloud/sandbox/fs/download.js.map +1 -1
  69. package/dist/cmd/cloud/sandbox/fs/ls.d.ts.map +1 -1
  70. package/dist/cmd/cloud/sandbox/fs/ls.js +3 -5
  71. package/dist/cmd/cloud/sandbox/fs/ls.js.map +1 -1
  72. package/dist/cmd/cloud/sandbox/fs/mkdir.d.ts.map +1 -1
  73. package/dist/cmd/cloud/sandbox/fs/mkdir.js +3 -5
  74. package/dist/cmd/cloud/sandbox/fs/mkdir.js.map +1 -1
  75. package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
  76. package/dist/cmd/cloud/sandbox/fs/rm.js +3 -5
  77. package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
  78. package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
  79. package/dist/cmd/cloud/sandbox/fs/rmdir.js +3 -5
  80. package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
  81. package/dist/cmd/cloud/sandbox/fs/upload.d.ts.map +1 -1
  82. package/dist/cmd/cloud/sandbox/fs/upload.js +7 -8
  83. package/dist/cmd/cloud/sandbox/fs/upload.js.map +1 -1
  84. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  85. package/dist/cmd/cloud/sandbox/get.js +21 -7
  86. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  87. package/dist/cmd/cloud/sandbox/job/create.d.ts.map +1 -1
  88. package/dist/cmd/cloud/sandbox/job/create.js +3 -4
  89. package/dist/cmd/cloud/sandbox/job/create.js.map +1 -1
  90. package/dist/cmd/cloud/sandbox/job/destroy.d.ts.map +1 -1
  91. package/dist/cmd/cloud/sandbox/job/destroy.js +3 -4
  92. package/dist/cmd/cloud/sandbox/job/destroy.js.map +1 -1
  93. package/dist/cmd/cloud/sandbox/job/get.d.ts.map +1 -1
  94. package/dist/cmd/cloud/sandbox/job/get.js +3 -4
  95. package/dist/cmd/cloud/sandbox/job/get.js.map +1 -1
  96. package/dist/cmd/cloud/sandbox/job/list.d.ts.map +1 -1
  97. package/dist/cmd/cloud/sandbox/job/list.js +3 -4
  98. package/dist/cmd/cloud/sandbox/job/list.js.map +1 -1
  99. package/dist/cmd/cloud/sandbox/job/logs.d.ts.map +1 -1
  100. package/dist/cmd/cloud/sandbox/job/logs.js +4 -4
  101. package/dist/cmd/cloud/sandbox/job/logs.js.map +1 -1
  102. package/dist/cmd/cloud/sandbox/pause.d.ts.map +1 -1
  103. package/dist/cmd/cloud/sandbox/pause.js +21 -5
  104. package/dist/cmd/cloud/sandbox/pause.js.map +1 -1
  105. package/dist/cmd/cloud/sandbox/resume.d.ts.map +1 -1
  106. package/dist/cmd/cloud/sandbox/resume.js +3 -4
  107. package/dist/cmd/cloud/sandbox/resume.js.map +1 -1
  108. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  109. package/dist/cmd/cloud/sandbox/run.js +36 -7
  110. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  111. package/dist/cmd/cloud/sandbox/snapshot/create.js +4 -4
  112. package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
  113. package/dist/cmd/cloud/sandbox/util.d.ts +19 -0
  114. package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -1
  115. package/dist/cmd/cloud/sandbox/util.js +40 -2
  116. package/dist/cmd/cloud/sandbox/util.js.map +1 -1
  117. package/dist/cmd/coder/create.js +7 -7
  118. package/dist/cmd/coder/create.js.map +1 -1
  119. package/dist/cmd/coder/start.d.ts.map +1 -1
  120. package/dist/cmd/coder/start.js +3 -0
  121. package/dist/cmd/coder/start.js.map +1 -1
  122. package/dist/cmd/coder/tui-init.js +1 -1
  123. package/dist/cmd/coder/tui-init.js.map +1 -1
  124. package/dist/cmd/coder/update.js +8 -8
  125. package/dist/cmd/coder/update.js.map +1 -1
  126. package/dist/cmd/coder/workspace/common.d.ts +29 -0
  127. package/dist/cmd/coder/workspace/common.d.ts.map +1 -0
  128. package/dist/cmd/coder/workspace/common.js +83 -0
  129. package/dist/cmd/coder/workspace/common.js.map +1 -0
  130. package/dist/cmd/coder/workspace/create.d.ts.map +1 -1
  131. package/dist/cmd/coder/workspace/create.js +57 -32
  132. package/dist/cmd/coder/workspace/create.js.map +1 -1
  133. package/dist/cmd/coder/workspace/get.d.ts.map +1 -1
  134. package/dist/cmd/coder/workspace/get.js +2 -5
  135. package/dist/cmd/coder/workspace/get.js.map +1 -1
  136. package/dist/cmd/coder/workspace/index.d.ts.map +1 -1
  137. package/dist/cmd/coder/workspace/index.js +11 -1
  138. package/dist/cmd/coder/workspace/index.js.map +1 -1
  139. package/dist/cmd/coder/workspace/list.d.ts.map +1 -1
  140. package/dist/cmd/coder/workspace/list.js +4 -0
  141. package/dist/cmd/coder/workspace/list.js.map +1 -1
  142. package/dist/cmd/coder/workspace/refresh.d.ts +2 -0
  143. package/dist/cmd/coder/workspace/refresh.d.ts.map +1 -0
  144. package/dist/cmd/coder/workspace/refresh.js +59 -0
  145. package/dist/cmd/coder/workspace/refresh.js.map +1 -0
  146. package/dist/cmd/coder/workspace/update.d.ts +2 -0
  147. package/dist/cmd/coder/workspace/update.d.ts.map +1 -0
  148. package/dist/cmd/coder/workspace/update.js +131 -0
  149. package/dist/cmd/coder/workspace/update.js.map +1 -0
  150. package/dist/cmd/coder/workspace/validate-dependencies.d.ts +2 -0
  151. package/dist/cmd/coder/workspace/validate-dependencies.d.ts.map +1 -0
  152. package/dist/cmd/coder/workspace/validate-dependencies.js +70 -0
  153. package/dist/cmd/coder/workspace/validate-dependencies.js.map +1 -0
  154. package/dist/cmd/dev/dev-lock.d.ts.map +1 -1
  155. package/dist/cmd/dev/dev-lock.js +43 -17
  156. package/dist/cmd/dev/dev-lock.js.map +1 -1
  157. package/dist/cmd/dev/index.d.ts.map +1 -1
  158. package/dist/cmd/dev/index.js +211 -125
  159. package/dist/cmd/dev/index.js.map +1 -1
  160. package/dist/cmd/dev/process-manager.d.ts +41 -1
  161. package/dist/cmd/dev/process-manager.d.ts.map +1 -1
  162. package/dist/cmd/dev/process-manager.js +160 -31
  163. package/dist/cmd/dev/process-manager.js.map +1 -1
  164. package/dist/cmd/project/create.d.ts.map +1 -1
  165. package/dist/cmd/project/create.js +0 -2
  166. package/dist/cmd/project/create.js.map +1 -1
  167. package/dist/cmd/project/index.d.ts.map +1 -1
  168. package/dist/cmd/project/index.js +0 -3
  169. package/dist/cmd/project/index.js.map +1 -1
  170. package/dist/cmd/project/random-name.d.ts +17 -0
  171. package/dist/cmd/project/random-name.d.ts.map +1 -0
  172. package/dist/cmd/project/random-name.js +144 -0
  173. package/dist/cmd/project/random-name.js.map +1 -0
  174. package/dist/cmd/project/template-flow.d.ts +0 -1
  175. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  176. package/dist/cmd/project/template-flow.js +180 -275
  177. package/dist/cmd/project/template-flow.js.map +1 -1
  178. package/dist/composite-logger.d.ts.map +1 -1
  179. package/dist/composite-logger.js +19 -0
  180. package/dist/composite-logger.js.map +1 -1
  181. package/dist/config.d.ts +18 -16
  182. package/dist/config.d.ts.map +1 -1
  183. package/dist/config.js +46 -16
  184. package/dist/config.js.map +1 -1
  185. package/dist/tui/prompt.d.ts +29 -0
  186. package/dist/tui/prompt.d.ts.map +1 -1
  187. package/dist/tui/prompt.js +180 -8
  188. package/dist/tui/prompt.js.map +1 -1
  189. package/package.json +7 -7
  190. package/src/cache/resource-region.ts +68 -44
  191. package/src/cli.ts +30 -8
  192. package/src/cmd/ai/prompt/web.md +43 -17
  193. package/src/cmd/build/vite/bun-dev-server.ts +92 -6
  194. package/src/cmd/build/vite/index.ts +0 -1
  195. package/src/cmd/build/vite/static-renderer.ts +18 -7
  196. package/src/cmd/build/vite/vite-asset-server-config.ts +37 -27
  197. package/src/cmd/build/vite/vite-asset-server.ts +5 -1
  198. package/src/cmd/build/vite/vite-builder.ts +12 -1
  199. package/src/cmd/build/vite/ws-proxy.ts +52 -3
  200. package/src/cmd/cloud/deploy.ts +117 -49
  201. package/src/cmd/cloud/sandbox/checkpoint/create.ts +10 -4
  202. package/src/cmd/cloud/sandbox/checkpoint/delete.ts +10 -4
  203. package/src/cmd/cloud/sandbox/checkpoint/list.ts +10 -4
  204. package/src/cmd/cloud/sandbox/checkpoint/restore.ts +10 -4
  205. package/src/cmd/cloud/sandbox/create.ts +14 -4
  206. package/src/cmd/cloud/sandbox/delete.ts +10 -4
  207. package/src/cmd/cloud/sandbox/env.ts +10 -5
  208. package/src/cmd/cloud/sandbox/exec.ts +157 -42
  209. package/src/cmd/cloud/sandbox/execution/list.ts +10 -5
  210. package/src/cmd/cloud/sandbox/fs/cp.ts +94 -126
  211. package/src/cmd/cloud/sandbox/fs/download.ts +18 -25
  212. package/src/cmd/cloud/sandbox/fs/ls.ts +10 -5
  213. package/src/cmd/cloud/sandbox/fs/mkdir.ts +10 -5
  214. package/src/cmd/cloud/sandbox/fs/rm.ts +10 -5
  215. package/src/cmd/cloud/sandbox/fs/rmdir.ts +10 -5
  216. package/src/cmd/cloud/sandbox/fs/upload.ts +14 -8
  217. package/src/cmd/cloud/sandbox/get.ts +28 -7
  218. package/src/cmd/cloud/sandbox/job/create.ts +10 -4
  219. package/src/cmd/cloud/sandbox/job/destroy.ts +10 -4
  220. package/src/cmd/cloud/sandbox/job/get.ts +10 -4
  221. package/src/cmd/cloud/sandbox/job/list.ts +10 -4
  222. package/src/cmd/cloud/sandbox/job/logs.ts +11 -4
  223. package/src/cmd/cloud/sandbox/pause.ts +31 -5
  224. package/src/cmd/cloud/sandbox/resume.ts +10 -4
  225. package/src/cmd/cloud/sandbox/run.ts +49 -11
  226. package/src/cmd/cloud/sandbox/snapshot/create.ts +6 -6
  227. package/src/cmd/cloud/sandbox/util.ts +63 -2
  228. package/src/cmd/coder/create.ts +8 -8
  229. package/src/cmd/coder/start.ts +3 -0
  230. package/src/cmd/coder/tui-init.ts +1 -1
  231. package/src/cmd/coder/update.ts +7 -7
  232. package/src/cmd/coder/workspace/common.ts +103 -0
  233. package/src/cmd/coder/workspace/create.ts +84 -37
  234. package/src/cmd/coder/workspace/get.ts +2 -5
  235. package/src/cmd/coder/workspace/index.ts +13 -1
  236. package/src/cmd/coder/workspace/list.ts +4 -0
  237. package/src/cmd/coder/workspace/refresh.ts +63 -0
  238. package/src/cmd/coder/workspace/update.ts +154 -0
  239. package/src/cmd/coder/workspace/validate-dependencies.ts +75 -0
  240. package/src/cmd/dev/dev-lock.ts +50 -16
  241. package/src/cmd/dev/index.ts +249 -134
  242. package/src/cmd/dev/process-manager.ts +173 -33
  243. package/src/cmd/project/create.ts +0 -2
  244. package/src/cmd/project/index.ts +0 -3
  245. package/src/cmd/project/random-name.ts +152 -0
  246. package/src/cmd/project/template-flow.ts +196 -305
  247. package/src/composite-logger.ts +20 -0
  248. package/src/config.ts +69 -19
  249. package/src/tui/prompt.ts +214 -8
  250. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +0 -45
  251. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +0 -1
  252. package/dist/cmd/build/vite/public-asset-path-plugin.js +0 -166
  253. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +0 -1
  254. package/dist/cmd/project/auth/generate.d.ts +0 -5
  255. package/dist/cmd/project/auth/generate.d.ts.map +0 -1
  256. package/dist/cmd/project/auth/generate.js +0 -102
  257. package/dist/cmd/project/auth/generate.js.map +0 -1
  258. package/dist/cmd/project/auth/index.d.ts +0 -2
  259. package/dist/cmd/project/auth/index.d.ts.map +0 -1
  260. package/dist/cmd/project/auth/index.js +0 -21
  261. package/dist/cmd/project/auth/index.js.map +0 -1
  262. package/dist/cmd/project/auth/init.d.ts +0 -2
  263. package/dist/cmd/project/auth/init.d.ts.map +0 -1
  264. package/dist/cmd/project/auth/init.js +0 -213
  265. package/dist/cmd/project/auth/init.js.map +0 -1
  266. package/dist/cmd/project/auth/shared.d.ts +0 -93
  267. package/dist/cmd/project/auth/shared.d.ts.map +0 -1
  268. package/dist/cmd/project/auth/shared.js +0 -475
  269. package/dist/cmd/project/auth/shared.js.map +0 -1
  270. package/src/cmd/build/vite/public-asset-path-plugin.ts +0 -209
  271. package/src/cmd/project/auth/generate.ts +0 -116
  272. package/src/cmd/project/auth/index.ts +0 -21
  273. package/src/cmd/project/auth/init.ts +0 -256
  274. package/src/cmd/project/auth/shared.ts +0 -591
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { getCommand } from '../../../../command-prefix';
4
4
  import * as tui from '../../../../tui';
5
5
  import { createCommand } from '../../../../types';
6
- import { createSandboxClient, getSandboxRegion } from '../util';
6
+ import { createSandboxClient, resolveSandboxTarget } from '../util';
7
7
 
8
8
  const SNAPSHOT_NAME_REGEX = /^[a-zA-Z0-9_-]+$/;
9
9
  const SNAPSHOT_TAG_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
@@ -88,15 +88,15 @@ export const createSubcommand = createCommand({
88
88
  }
89
89
 
90
90
  const profileName = config?.name;
91
- const region = await getSandboxRegion(
91
+ const sandboxInfo = await resolveSandboxTarget(
92
92
  logger,
93
93
  auth,
94
- profileName,
94
+ null,
95
95
  args.sandboxId,
96
- orgId,
96
+ profileName,
97
97
  config
98
98
  );
99
- const client = createSandboxClient(logger, auth, region);
99
+ const client = createSandboxClient(logger, auth, sandboxInfo.region);
100
100
 
101
101
  const snapshot = await snapshotCreate(client, {
102
102
  sandboxId: args.sandboxId,
@@ -104,7 +104,7 @@ export const createSubcommand = createCommand({
104
104
  description: opts.description,
105
105
  tag: opts.tag,
106
106
  public: opts.public,
107
- orgId,
107
+ orgId: sandboxInfo.orgId ?? orgId,
108
108
  });
109
109
 
110
110
  if (!options.json) {
@@ -1,17 +1,69 @@
1
- import { readFileSync } from 'node:fs';
1
+ import { fstatSync, readFileSync, statSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import type { FileToWrite, Logger } from '@agentuity/core';
4
- import { APIClient, getServiceUrls, sandboxGet } from '@agentuity/server';
4
+ import { APIClient, getServiceUrls, sandboxGet, sandboxResolve } from '@agentuity/server';
5
5
  import { deleteResourceRegion, getResourceInfo, setResourceInfo } from '../../../cache';
6
6
  import { getGlobalCatalystAPIClient } from '../../../config';
7
7
  import { ErrorCode } from '../../../errors';
8
8
  import * as tui from '../../../tui';
9
9
  import type { AuthData, Config } from '../../../types';
10
10
 
11
+ /**
12
+ * Detect if a file descriptor is redirected to /dev/null (or NUL on Windows).
13
+ * Used to optimize stream creation - when output goes to /dev/null, we can
14
+ * skip creating the stream entirely on the server.
15
+ *
16
+ * @param fd - File descriptor (1 for stdout, 2 for stderr)
17
+ * @returns true if the fd points to /dev/null
18
+ */
19
+ export function detectNullStream(fd: number): boolean {
20
+ try {
21
+ const fdStat = fstatSync(fd);
22
+ const nullPath = process.platform === 'win32' ? 'NUL' : '/dev/null';
23
+ const nullStat = statSync(nullPath);
24
+ return fdStat.dev === nullStat.dev && fdStat.ino === nullStat.ino;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+
11
30
  export function createSandboxClient(logger: Logger, auth: AuthData, region: string): APIClient {
12
31
  return new APIClient(getServiceUrls(region).catalyst, logger, auth.apiKey);
13
32
  }
14
33
 
34
+ export interface ResolvedSandboxTarget {
35
+ region: string;
36
+ orgId: string;
37
+ }
38
+
39
+ /**
40
+ * Resolve sandbox routing context using cache-first lookup.
41
+ * Falls back to the CLI resolve endpoint on cache miss or partial cache.
42
+ */
43
+ export async function resolveSandboxTarget(
44
+ logger: Logger,
45
+ auth: AuthData,
46
+ apiClient: APIClient | null = null,
47
+ sandboxId: string,
48
+ profileName = 'production',
49
+ config?: Config | null
50
+ ): Promise<ResolvedSandboxTarget> {
51
+ const cachedInfo = await getResourceInfo('sandbox', profileName, sandboxId);
52
+ if (cachedInfo?.region && cachedInfo?.orgId) {
53
+ logger.trace(
54
+ `[sandbox] Found cached target for ${sandboxId}: ${cachedInfo.region}/${cachedInfo.orgId}`
55
+ );
56
+ return { region: cachedInfo.region, orgId: cachedInfo.orgId };
57
+ }
58
+
59
+ logger.trace(`[sandbox] Cache miss for target ${sandboxId}, resolving via CLI API`);
60
+ const globalClient =
61
+ apiClient ?? (await getGlobalCatalystAPIClient(logger, auth, profileName, undefined, config));
62
+ const sandbox = await sandboxResolve(globalClient, sandboxId);
63
+ await setResourceInfo('sandbox', profileName, sandboxId, sandbox.region, sandbox.orgId);
64
+ return { region: sandbox.region, orgId: sandbox.orgId };
65
+ }
66
+
15
67
  /**
16
68
  * Look up the region for a sandbox, using cache-first strategy.
17
69
  * Falls back to API lookup if not in cache.
@@ -64,6 +116,15 @@ export async function cacheSandboxRegion(
64
116
  await setResourceInfo('sandbox', profileName, sandboxId, region);
65
117
  }
66
118
 
119
+ export async function cacheSandboxTarget(
120
+ profileName = 'production',
121
+ sandboxId: string,
122
+ region: string,
123
+ orgId?: string
124
+ ): Promise<void> {
125
+ await setResourceInfo('sandbox', profileName, sandboxId, region, orgId);
126
+ }
127
+
67
128
  /**
68
129
  * Clear cached region for a sandbox after delete
69
130
  */
@@ -46,9 +46,9 @@ export const createCoderSubcommand = createSubcommand({
46
46
  },
47
47
  {
48
48
  command: getCommand(
49
- 'coder create "Review this change" --default-agent code-review --agent-slugs code-review'
49
+ 'coder create "Review this change" --default-agent code-review --enabled-agents code-review'
50
50
  ),
51
- description: 'Create with published custom agents and a custom default route target',
51
+ description: 'Create with a selected agent roster and a custom default route target',
52
52
  },
53
53
  ],
54
54
  schema: {
@@ -96,10 +96,10 @@ export const createCoderSubcommand = createSubcommand({
96
96
  // Resources
97
97
  workspaceId: z.string().optional().describe('Workspace ID to use'),
98
98
  tags: z.string().optional().describe('Comma-separated tags'),
99
- agentSlugs: z
99
+ enabledAgents: z
100
100
  .string()
101
101
  .optional()
102
- .describe('Comma-separated published custom agent slugs to include'),
102
+ .describe('Comma-separated built-in/custom agents to include'),
103
103
  env: z
104
104
  .string()
105
105
  .optional()
@@ -121,7 +121,7 @@ export const createCoderSubcommand = createSubcommand({
121
121
  // Build the create session request body from flags
122
122
  const body: CoderCreateSessionRequest & {
123
123
  defaultAgent?: string;
124
- agentSlugs?: string[];
124
+ enabledAgents?: string[];
125
125
  } = {
126
126
  task: args.task,
127
127
  ...(opts?.label && { label: opts.label }),
@@ -167,10 +167,10 @@ export const createCoderSubcommand = createSubcommand({
167
167
  .split(',')
168
168
  .map((t) => t.trim())
169
169
  .filter(Boolean);
170
- if (opts?.agentSlugs)
171
- body.agentSlugs = opts.agentSlugs
170
+ if (opts?.enabledAgents)
171
+ body.enabledAgents = opts.enabledAgents
172
172
  .split(',')
173
- .map((slug) => slug.trim())
173
+ .map((name) => name.trim())
174
174
  .filter(Boolean);
175
175
  if (opts?.savedSkillIds)
176
176
  body.savedSkillIds = opts.savedSkillIds
@@ -128,6 +128,8 @@ export const startSubcommand = createSubcommand({
128
128
  },
129
129
  async handler(ctx) {
130
130
  const { opts, options } = ctx;
131
+ // Keep Pi's interactive install telemetry disabled for Agentuity CLI sessions.
132
+ process.env.PI_TELEMETRY = '0';
131
133
 
132
134
  // Resolve working directory from optional --dir option
133
135
  let cwd = process.cwd();
@@ -388,6 +390,7 @@ export const startSubcommand = createSubcommand({
388
390
  AGENTUITY_CODER_HUB_URL: hubWsUrl,
389
391
  };
390
392
  env.AGENTUITY_CODER_API_KEY = ctx.auth.apiKey;
393
+ env.AGENTUITY_ORGID = ctx.orgId;
391
394
 
392
395
  if (opts?.agent) {
393
396
  env.AGENTUITY_CODER_AGENT = opts.agent;
@@ -43,7 +43,7 @@ export async function probeHubInitAccess(
43
43
  try {
44
44
  const response = await fetchImpl(`${hubHttpUrl}/api/hub/init`, {
45
45
  headers,
46
- signal: AbortSignal.timeout(5_000),
46
+ signal: AbortSignal.timeout(10_000),
47
47
  });
48
48
 
49
49
  let payload: unknown;
@@ -54,10 +54,10 @@ export const updateSubcommand = createSubcommand({
54
54
  loopAutoContinue: z.boolean().optional().describe('Auto-continue loop'),
55
55
  loopAllowDetached: z.boolean().optional().describe('Allow detached loop execution'),
56
56
  tags: z.string().optional().describe('Comma-separated tags (replaces existing)'),
57
- agentSlugs: z
57
+ enabledAgents: z
58
58
  .string()
59
59
  .optional()
60
- .describe('Comma-separated published custom agent slugs (replaces existing)'),
60
+ .describe('Comma-separated built-in/custom agents (replaces existing)'),
61
61
  }),
62
62
  },
63
63
  async handler(ctx) {
@@ -81,10 +81,10 @@ export const updateSubcommand = createSubcommand({
81
81
  .map((t) => t.trim())
82
82
  .filter(Boolean);
83
83
  }
84
- if (opts?.agentSlugs) {
85
- body.agentSlugs = opts.agentSlugs
84
+ if (opts?.enabledAgents) {
85
+ body.enabledAgents = opts.enabledAgents
86
86
  .split(',')
87
- .map((slug) => slug.trim())
87
+ .map((name) => name.trim())
88
88
  .filter(Boolean);
89
89
  }
90
90
 
@@ -105,7 +105,7 @@ export const updateSubcommand = createSubcommand({
105
105
 
106
106
  if (Object.keys(body).length === 0) {
107
107
  tui.fatal(
108
- 'No update fields provided. Use --label, --visibility, --tags, --agent, --default-agent, --agent-slugs, --workflow-mode, or loop options.',
108
+ 'No update fields provided. Use --label, --visibility, --tags, --agent, --default-agent, --enabled-agents, --workflow-mode, or loop options.',
109
109
  ErrorCode.VALIDATION_FAILED
110
110
  );
111
111
  }
@@ -125,7 +125,7 @@ export const updateSubcommand = createSubcommand({
125
125
  if (opts?.tags) fields.push(`Tags: ${(body.tags as string[]).join(', ')}`);
126
126
  if (opts?.agent) fields.push(`Agent: ${opts.agent}`);
127
127
  if (opts?.defaultAgent) fields.push(`Default agent: ${opts.defaultAgent}`);
128
- if (opts?.agentSlugs) fields.push(`Custom agents: ${opts.agentSlugs}`);
128
+ if (opts?.enabledAgents) fields.push(`Enabled agents: ${opts.enabledAgents}`);
129
129
  if (opts?.workflowMode || body.loop) fields.push(`Workflow: ${body.workflowMode}`);
130
130
 
131
131
  for (const f of fields) {
@@ -0,0 +1,103 @@
1
+ import {
2
+ type CoderCreateWorkspaceRequest,
3
+ type CoderUpdateWorkspaceRequest,
4
+ type CoderWorkspaceDetail,
5
+ } from '@agentuity/core/coder';
6
+ import { StructuredError } from '@agentuity/core';
7
+ import * as tui from '../../../tui';
8
+
9
+ export const EMPTY_WORKSPACE_ERROR =
10
+ 'A workspace needs at least one repo, dependency, setup script, saved skill, skill bucket, or agent';
11
+ export const SetupScriptValidationError = StructuredError('SetupScriptValidationError')<{
12
+ message: string;
13
+ path?: string;
14
+ }>();
15
+
16
+ export function parseCommaList(value?: string): string[] {
17
+ return value
18
+ ? value
19
+ .split(',')
20
+ .map((item) => item.trim())
21
+ .filter(Boolean)
22
+ : [];
23
+ }
24
+
25
+ export async function readSetupScript(input: {
26
+ setupScript?: string;
27
+ setupScriptFile?: string;
28
+ }): Promise<string | undefined> {
29
+ if (input.setupScript !== undefined && input.setupScriptFile) {
30
+ throw new SetupScriptValidationError({
31
+ message: 'Use either --setup-script or --setup-script-file, not both.',
32
+ });
33
+ }
34
+ if (input.setupScript !== undefined) return input.setupScript;
35
+ if (!input.setupScriptFile) return undefined;
36
+ try {
37
+ return await Bun.file(input.setupScriptFile).text();
38
+ } catch (error) {
39
+ throw new SetupScriptValidationError({
40
+ message: `Failed to read setup script file "${input.setupScriptFile}": ${
41
+ error instanceof Error ? error.message : String(error)
42
+ }`,
43
+ path: input.setupScriptFile,
44
+ cause: error,
45
+ });
46
+ }
47
+ }
48
+
49
+ export function hasWorkspaceSelections(input: CoderCreateWorkspaceRequest): boolean {
50
+ return (
51
+ (input.repos?.length ?? 0) > 0 ||
52
+ (input.dependencies?.length ?? 0) > 0 ||
53
+ Boolean(input.setupScript?.trim()) ||
54
+ (input.savedSkillIds?.length ?? 0) > 0 ||
55
+ (input.skillBucketIds?.length ?? 0) > 0 ||
56
+ (input.enabledAgents?.length ?? 0) > 0
57
+ );
58
+ }
59
+
60
+ export function hasWorkspaceUpdate(input: CoderUpdateWorkspaceRequest): boolean {
61
+ return Object.keys(input).length > 0;
62
+ }
63
+
64
+ export function formatWorkspaceValidationMessage(issues: Array<{ message: string }>): string {
65
+ const messages = [...new Set(issues.map((issue) => issue.message).filter(Boolean))];
66
+ if (messages.length === 0) {
67
+ return 'Invalid workspace configuration';
68
+ }
69
+ if (messages.includes(EMPTY_WORKSPACE_ERROR)) {
70
+ return `${EMPTY_WORKSPACE_ERROR}. Use --repo, --dependency, --setup-script, or --enabled-agents.`;
71
+ }
72
+ return messages.join('; ');
73
+ }
74
+
75
+ export function printWorkspaceSummary(workspace: CoderWorkspaceDetail): void {
76
+ const enabledAgents = Array.isArray(workspace.enabledAgents)
77
+ ? workspace.enabledAgents.filter((name): name is string => typeof name === 'string')
78
+ : [];
79
+ const dependencies = Array.isArray(workspace.dependencies) ? workspace.dependencies : [];
80
+
81
+ tui.output(` Name: ${tui.bold(workspace.name)}`);
82
+ if (workspace.description) {
83
+ tui.output(` Description: ${workspace.description}`);
84
+ }
85
+ tui.output(` Scope: ${workspace.scope}`);
86
+ tui.output(` Repos: ${workspace.repoCount}`);
87
+ tui.output(` Selections: ${workspace.selectionCount}`);
88
+ if (dependencies.length > 0) {
89
+ tui.output(` Dependencies:${dependencies.length === 1 ? ` ${dependencies[0]}` : ''}`);
90
+ for (const dependency of dependencies.length === 1 ? [] : dependencies) {
91
+ tui.output(` - ${dependency}`);
92
+ }
93
+ }
94
+ if (workspace.setupScript) {
95
+ tui.output(' Setup: configured');
96
+ }
97
+ if (workspace.snapshot?.status) {
98
+ tui.output(` Snapshot: ${workspace.snapshot.status}`);
99
+ }
100
+ if (enabledAgents.length > 0) {
101
+ tui.output(` Agents: ${enabledAgents.join(', ')}`);
102
+ }
103
+ }
@@ -1,37 +1,51 @@
1
1
  import { z } from 'zod';
2
- import { CoderClient, type CoderCreateWorkspaceRequest } from '@agentuity/core/coder';
3
- import { ValidationOutputError } from '@agentuity/core';
2
+ import { APIError, ValidationInputError, ValidationOutputError } from '@agentuity/core';
3
+ import {
4
+ CoderClient,
5
+ CoderCreateWorkspaceRequestSchema,
6
+ type CoderCreateWorkspaceRequest,
7
+ } from '@agentuity/core/coder';
4
8
  import { createSubcommand } from '../../../types';
5
9
  import * as tui from '../../../tui';
6
10
  import { getCommand } from '../../../command-prefix';
7
11
  import { ErrorCode } from '../../../errors';
8
12
  import { resolveGitHubRepo } from '../resolve-repo';
13
+ import {
14
+ EMPTY_WORKSPACE_ERROR,
15
+ formatWorkspaceValidationMessage,
16
+ hasWorkspaceSelections,
17
+ parseCommaList,
18
+ printWorkspaceSummary,
19
+ readSetupScript,
20
+ } from './common';
9
21
 
10
22
  export const createWorkspaceSubcommand = createSubcommand({
11
23
  name: 'create',
12
24
  aliases: ['new', 'add'],
13
- description: 'Create a new Coder workspace',
25
+ description: 'Create a new Coder workspace with at least one repo or agent',
14
26
  tags: ['mutating', 'requires-auth'],
15
27
  requires: { auth: true, org: true },
16
28
  examples: [
17
- {
18
- command: getCommand('coder workspace create "My Workspace"'),
19
- description: 'Create a workspace with a name',
20
- },
21
29
  {
22
30
  command: getCommand(
23
- 'coder workspace create "My Workspace" --description "For frontend work" --scope org'
31
+ 'coder workspace create "My Workspace" --repo https://github.com/org/repo --repo-branch main'
24
32
  ),
25
- description: 'Create an org-scoped workspace with description',
33
+ description: 'Create a workspace with a repository',
26
34
  },
27
35
  {
28
36
  command: getCommand(
29
- 'coder workspace create "My Workspace" --repo https://github.com/org/repo --repo-branch main'
37
+ 'coder workspace create "My Workspace" --dependency git --setup-script-file ./setup.sh --scope org'
30
38
  ),
31
- description: 'Create a workspace with a repository',
39
+ description: 'Create an org-scoped workspace with dependencies and a setup script',
40
+ },
41
+ {
42
+ command: getCommand('coder workspace create "My Workspace" --enabled-agents code-review'),
43
+ description: 'Create a workspace with an agent roster',
32
44
  },
33
45
  {
34
- command: getCommand('coder workspace create "My Workspace" --json'),
46
+ command: getCommand(
47
+ 'coder workspace create "My Workspace" --enabled-agents code-review --json'
48
+ ),
35
49
  description: 'Create a workspace and return JSON output',
36
50
  },
37
51
  ],
@@ -45,10 +59,22 @@ export const createWorkspaceSubcommand = createSubcommand({
45
59
  scope: z.string().optional().describe('Workspace scope: user or org'),
46
60
  repo: z.string().optional().describe('Repository URL to add'),
47
61
  repoBranch: z.string().optional().describe('Branch for the repository'),
48
- agentSlugs: z
62
+ dependency: z
63
+ .string()
64
+ .optional()
65
+ .describe('Comma-separated APT dependencies to install into workspace snapshots'),
66
+ setupScript: z
67
+ .string()
68
+ .optional()
69
+ .describe('Inline shell script to run while preparing workspace snapshots'),
70
+ setupScriptFile: z
49
71
  .string()
50
72
  .optional()
51
- .describe('Comma-separated published custom agent slugs to add'),
73
+ .describe('Path to a shell script to run while preparing workspace snapshots'),
74
+ enabledAgents: z
75
+ .string()
76
+ .optional()
77
+ .describe('Comma-separated built-in/custom agents to include'),
52
78
  }),
53
79
  },
54
80
  async handler(ctx) {
@@ -59,9 +85,7 @@ export const createWorkspaceSubcommand = createSubcommand({
59
85
  orgId: ctx.orgId,
60
86
  });
61
87
 
62
- const body: CoderCreateWorkspaceRequest & {
63
- agentSlugs?: string[];
64
- } = {
88
+ const body: CoderCreateWorkspaceRequest = {
65
89
  name: args.name,
66
90
  ...(opts?.description && { description: opts.description }),
67
91
  ...(opts?.scope && { scope: opts.scope as 'user' | 'org' }),
@@ -78,18 +102,44 @@ export const createWorkspaceSubcommand = createSubcommand({
78
102
  return;
79
103
  }
80
104
  }
81
- if (opts?.agentSlugs) {
82
- body.agentSlugs = opts.agentSlugs
83
- .split(',')
84
- .map((slug) => slug.trim())
85
- .filter(Boolean);
105
+ if (opts?.dependency) {
106
+ body.dependencies = parseCommaList(opts.dependency);
107
+ }
108
+ try {
109
+ const setupScript = await readSetupScript({
110
+ setupScript: opts?.setupScript,
111
+ setupScriptFile: opts?.setupScriptFile,
112
+ });
113
+ if (setupScript !== undefined) body.setupScript = setupScript;
114
+ } catch (err) {
115
+ const msg = err instanceof Error ? err.message : String(err);
116
+ tui.fatal(`Failed to read setup script: ${msg}`, ErrorCode.VALIDATION_FAILED);
117
+ return;
118
+ }
119
+ if (opts?.enabledAgents) {
120
+ body.enabledAgents = parseCommaList(opts.enabledAgents);
121
+ }
122
+ if (!hasWorkspaceSelections(body)) {
123
+ tui.fatal(
124
+ `Failed to create workspace: ${EMPTY_WORKSPACE_ERROR}. Use --repo, --dependency, --setup-script, or --enabled-agents.`,
125
+ ErrorCode.VALIDATION_FAILED
126
+ );
127
+ }
128
+
129
+ const validationResult = CoderCreateWorkspaceRequestSchema.safeParse(body);
130
+ if (!validationResult.success) {
131
+ ctx.logger.trace(
132
+ 'Validation issues: %s',
133
+ JSON.stringify(validationResult.error.issues, null, 2)
134
+ );
135
+ tui.fatal(
136
+ `Failed to create workspace: ${formatWorkspaceValidationMessage(validationResult.error.issues)}`,
137
+ ErrorCode.VALIDATION_FAILED
138
+ );
86
139
  }
87
140
 
88
141
  try {
89
- const created = await client.createWorkspace(body);
90
- const createdAgentSlugs = Array.isArray(created.agentSlugs)
91
- ? created.agentSlugs.filter((slug): slug is string => typeof slug === 'string')
92
- : [];
142
+ const created = await client.createWorkspace(validationResult.data);
93
143
 
94
144
  if (options.json) {
95
145
  return created;
@@ -97,22 +147,19 @@ export const createWorkspaceSubcommand = createSubcommand({
97
147
 
98
148
  tui.success(`Workspace ${created.id} created.`);
99
149
  tui.newline();
100
- tui.output(` Name: ${tui.bold(created.name)}`);
101
- if (created.description) {
102
- tui.output(` Description: ${created.description}`);
103
- }
104
- tui.output(` Scope: ${created.scope}`);
105
- tui.output(` Repos: ${created.repoCount}`);
106
- tui.output(` Selections: ${created.selectionCount}`);
107
- if (createdAgentSlugs.length > 0) {
108
- tui.output(` Agents: ${createdAgentSlugs.join(', ')}`);
109
- }
150
+ printWorkspaceSummary(created);
110
151
 
111
152
  return created;
112
153
  } catch (err) {
113
- if (err instanceof ValidationOutputError) {
154
+ if (err instanceof ValidationInputError || err instanceof ValidationOutputError) {
114
155
  ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
115
156
  ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
157
+ tui.fatal(
158
+ `Failed to create workspace: ${formatWorkspaceValidationMessage(err.issues)}`,
159
+ ErrorCode.VALIDATION_FAILED
160
+ );
161
+ }
162
+ if (err instanceof APIError && err.status >= 400 && err.status < 500) {
116
163
  tui.fatal(`Failed to create workspace: ${err.message}`, ErrorCode.VALIDATION_FAILED);
117
164
  }
118
165
  const msg = err instanceof Error ? err.message : String(err);
@@ -5,6 +5,7 @@ import { createSubcommand } from '../../../types';
5
5
  import * as tui from '../../../tui';
6
6
  import { getCommand } from '../../../command-prefix';
7
7
  import { ErrorCode } from '../../../errors';
8
+ import { printWorkspaceSummary } from './common';
8
9
 
9
10
  function formatRelativeTime(isoDate: string): string {
10
11
  const parsed = new Date(isoDate).getTime();
@@ -63,11 +64,7 @@ export const getWorkspaceSubcommand = createSubcommand({
63
64
  tui.header(`Workspace: ${workspace.name}`);
64
65
  tui.newline();
65
66
  tui.output(` ID: ${workspace.id}`);
66
- tui.output(` Name: ${tui.bold(workspace.name)}`);
67
- if (workspace.description) {
68
- tui.output(` Description: ${workspace.description}`);
69
- }
70
- tui.output(` Scope: ${workspace.scope}`);
67
+ printWorkspaceSummary(workspace);
71
68
  tui.output(` Owner: ${workspace.ownerUserId}`);
72
69
  tui.output(` Created: ${formatRelativeTime(workspace.createdAt)}`);
73
70
  tui.output(` Updated: ${formatRelativeTime(workspace.updatedAt)}`);
@@ -2,6 +2,9 @@ import { createCommand } from '../../../types';
2
2
  import { listSubcommand } from './list';
3
3
  import { createWorkspaceSubcommand } from './create';
4
4
  import { getWorkspaceSubcommand } from './get';
5
+ import { refreshWorkspaceSnapshotSubcommand } from './refresh';
6
+ import { updateWorkspaceSubcommand } from './update';
7
+ import { validateWorkspaceDependenciesSubcommand } from './validate-dependencies';
5
8
  import { deleteWorkspaceSubcommand } from './delete';
6
9
  import { getCommand } from '../../../command-prefix';
7
10
 
@@ -17,7 +20,9 @@ export const workspaceCommand = createCommand({
17
20
  description: 'List all workspaces',
18
21
  },
19
22
  {
20
- command: getCommand('coder workspace create "My Workspace"'),
23
+ command: getCommand(
24
+ 'coder workspace create "My Workspace" --repo https://github.com/org/repo'
25
+ ),
21
26
  description: 'Create a new workspace',
22
27
  },
23
28
  {
@@ -28,11 +33,18 @@ export const workspaceCommand = createCommand({
28
33
  command: getCommand('coder workspace delete ws_abc123'),
29
34
  description: 'Delete a workspace',
30
35
  },
36
+ {
37
+ command: getCommand('coder workspace validate-dependencies git,nodejs'),
38
+ description: 'Validate workspace dependencies',
39
+ },
31
40
  ],
32
41
  subcommands: [
33
42
  listSubcommand,
34
43
  createWorkspaceSubcommand,
35
44
  getWorkspaceSubcommand,
45
+ updateWorkspaceSubcommand,
46
+ refreshWorkspaceSnapshotSubcommand,
47
+ validateWorkspaceDependenciesSubcommand,
36
48
  deleteWorkspaceSubcommand,
37
49
  ],
38
50
  });
@@ -83,6 +83,8 @@ export const listSubcommand = createSubcommand({
83
83
  Name: w.name,
84
84
  Scope: w.scope,
85
85
  Repos: String(w.repoCount),
86
+ Deps: String(w.dependencies?.length ?? 0),
87
+ Snapshot: w.snapshot?.status ?? '',
86
88
  Selections: String(w.selectionCount),
87
89
  Created: formatRelativeTime(w.createdAt),
88
90
  })),
@@ -91,6 +93,8 @@ export const listSubcommand = createSubcommand({
91
93
  { name: 'Name', alignment: 'left' },
92
94
  { name: 'Scope', alignment: 'center' },
93
95
  { name: 'Repos', alignment: 'right' },
96
+ { name: 'Deps', alignment: 'right' },
97
+ { name: 'Snapshot', alignment: 'left' },
94
98
  { name: 'Selections', alignment: 'right' },
95
99
  { name: 'Created', alignment: 'right' },
96
100
  ]
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ import { APIError, ValidationOutputError } from '@agentuity/core';
3
+ import { CoderClient } from '@agentuity/core/coder';
4
+ import { createSubcommand } from '../../../types';
5
+ import * as tui from '../../../tui';
6
+ import { getCommand } from '../../../command-prefix';
7
+ import { ErrorCode } from '../../../errors';
8
+ import { printWorkspaceSummary } from './common';
9
+
10
+ export const refreshWorkspaceSnapshotSubcommand = createSubcommand({
11
+ name: 'refresh',
12
+ aliases: ['snapshot-refresh', 'rebuild'],
13
+ description: 'Refresh a Coder workspace snapshot',
14
+ tags: ['mutating', 'requires-auth'],
15
+ requires: { auth: true, org: true },
16
+ examples: [
17
+ {
18
+ command: getCommand('coder workspace refresh ws_abc123'),
19
+ description: 'Queue a workspace snapshot refresh',
20
+ },
21
+ ],
22
+ schema: {
23
+ args: z.object({
24
+ workspaceId: z.string().describe('Workspace ID to refresh'),
25
+ }),
26
+ options: z.object({
27
+ url: z.string().optional().describe('Coder API URL override'),
28
+ }),
29
+ },
30
+ async handler(ctx) {
31
+ const { args, opts, options } = ctx;
32
+ const client = new CoderClient({
33
+ apiKey: ctx.auth.apiKey,
34
+ url: opts?.url,
35
+ orgId: ctx.orgId,
36
+ });
37
+
38
+ try {
39
+ const workspace = await client.refreshWorkspaceSnapshot(args.workspaceId);
40
+ if (options.json) {
41
+ return workspace;
42
+ }
43
+
44
+ tui.success(`Workspace ${workspace.id} snapshot refresh queued.`);
45
+ tui.newline();
46
+ printWorkspaceSummary(workspace);
47
+ return workspace;
48
+ } catch (err) {
49
+ if (err instanceof ValidationOutputError) {
50
+ ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
51
+ ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
52
+ }
53
+ if (err instanceof APIError && err.status >= 400 && err.status < 500) {
54
+ tui.fatal(
55
+ `Failed to refresh workspace snapshot: ${err.message}`,
56
+ ErrorCode.VALIDATION_FAILED
57
+ );
58
+ }
59
+ const msg = err instanceof Error ? err.message : String(err);
60
+ tui.fatal(`Failed to refresh workspace snapshot: ${msg}`, ErrorCode.NETWORK_ERROR);
61
+ }
62
+ },
63
+ });