@agentuity/cli 0.0.109 → 0.0.111

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 (253) hide show
  1. package/dist/build-report.d.ts +201 -0
  2. package/dist/build-report.d.ts.map +1 -0
  3. package/dist/build-report.js +335 -0
  4. package/dist/build-report.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +19 -4
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  9. package/dist/cmd/build/entry-generator.js +3 -1
  10. package/dist/cmd/build/entry-generator.js.map +1 -1
  11. package/dist/cmd/build/index.d.ts.map +1 -1
  12. package/dist/cmd/build/index.js +44 -1
  13. package/dist/cmd/build/index.js.map +1 -1
  14. package/dist/cmd/build/typecheck.d.ts +7 -1
  15. package/dist/cmd/build/typecheck.d.ts.map +1 -1
  16. package/dist/cmd/build/typecheck.js +11 -1
  17. package/dist/cmd/build/typecheck.js.map +1 -1
  18. package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
  19. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  20. package/dist/cmd/build/vite/agent-discovery.js +3 -3
  21. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  22. package/dist/cmd/build/vite/index.d.ts +2 -1
  23. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/index.js +3 -2
  25. package/dist/cmd/build/vite/index.js.map +1 -1
  26. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/metadata-generator.js +3 -5
  28. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  29. package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
  30. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/registry-generator.js +126 -41
  32. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  33. package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
  34. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  35. package/dist/cmd/build/vite/route-discovery.js +19 -0
  36. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  37. package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
  38. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  39. package/dist/cmd/build/vite/vite-builder.js +10 -2
  40. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  41. package/dist/cmd/build/vite-bundler.d.ts +3 -0
  42. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  43. package/dist/cmd/build/vite-bundler.js +14 -5
  44. package/dist/cmd/build/vite-bundler.js.map +1 -1
  45. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  46. package/dist/cmd/cloud/deploy.js +149 -10
  47. package/dist/cmd/cloud/deploy.js.map +1 -1
  48. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  49. package/dist/cmd/cloud/deployment/show.js +0 -1
  50. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  51. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  52. package/dist/cmd/cloud/sandbox/create.js +18 -0
  53. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  54. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  55. package/dist/cmd/cloud/sandbox/delete.js +2 -6
  56. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  57. package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
  58. package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
  59. package/dist/cmd/cloud/sandbox/download.js +89 -0
  60. package/dist/cmd/cloud/sandbox/download.js.map +1 -0
  61. package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
  62. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
  63. package/dist/cmd/cloud/sandbox/env.js +90 -0
  64. package/dist/cmd/cloud/sandbox/env.js.map +1 -0
  65. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  66. package/dist/cmd/cloud/sandbox/get.js +5 -0
  67. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  68. package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
  69. package/dist/cmd/cloud/sandbox/index.js +14 -0
  70. package/dist/cmd/cloud/sandbox/index.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
  72. package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
  73. package/dist/cmd/cloud/sandbox/ls.js +119 -0
  74. package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
  75. package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
  76. package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
  77. package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
  78. package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
  79. package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
  80. package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
  81. package/dist/cmd/cloud/sandbox/rm.js +45 -0
  82. package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
  83. package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
  84. package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
  85. package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
  86. package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
  87. package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
  88. package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
  89. package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
  90. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  91. package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
  92. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  93. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
  94. package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
  95. package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
  96. package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
  97. package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
  98. package/dist/cmd/cloud/sandbox/upload.js +77 -0
  99. package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
  100. package/dist/cmd/dev/index.d.ts.map +1 -1
  101. package/dist/cmd/dev/index.js +126 -23
  102. package/dist/cmd/dev/index.js.map +1 -1
  103. package/dist/cmd/dev/sync.d.ts.map +1 -1
  104. package/dist/cmd/dev/sync.js +8 -14
  105. package/dist/cmd/dev/sync.js.map +1 -1
  106. package/dist/cmd/git/account/add.d.ts +17 -0
  107. package/dist/cmd/git/account/add.d.ts.map +1 -0
  108. package/dist/cmd/git/account/add.js +244 -0
  109. package/dist/cmd/git/account/add.js.map +1 -0
  110. package/dist/cmd/git/account/index.d.ts +3 -0
  111. package/dist/cmd/git/account/index.d.ts.map +1 -0
  112. package/dist/cmd/git/account/index.js +11 -0
  113. package/dist/cmd/git/account/index.js.map +1 -0
  114. package/dist/cmd/git/account/list.d.ts +2 -0
  115. package/dist/cmd/git/account/list.d.ts.map +1 -0
  116. package/dist/cmd/git/account/list.js +111 -0
  117. package/dist/cmd/git/account/list.js.map +1 -0
  118. package/dist/cmd/git/account/remove.d.ts +2 -0
  119. package/dist/cmd/git/account/remove.d.ts.map +1 -0
  120. package/dist/cmd/git/account/remove.js +171 -0
  121. package/dist/cmd/git/account/remove.js.map +1 -0
  122. package/dist/cmd/git/index.d.ts +3 -0
  123. package/dist/cmd/git/index.d.ts.map +1 -0
  124. package/dist/cmd/git/index.js +19 -0
  125. package/dist/cmd/git/index.js.map +1 -0
  126. package/dist/cmd/git/link.d.ts +32 -0
  127. package/dist/cmd/git/link.d.ts.map +1 -0
  128. package/dist/cmd/git/link.js +357 -0
  129. package/dist/cmd/git/link.js.map +1 -0
  130. package/dist/cmd/git/list.d.ts +2 -0
  131. package/dist/cmd/git/list.d.ts.map +1 -0
  132. package/dist/cmd/git/list.js +137 -0
  133. package/dist/cmd/git/list.js.map +1 -0
  134. package/dist/cmd/git/status.d.ts +2 -0
  135. package/dist/cmd/git/status.d.ts.map +1 -0
  136. package/dist/cmd/git/status.js +119 -0
  137. package/dist/cmd/git/status.js.map +1 -0
  138. package/dist/cmd/git/unlink.d.ts +2 -0
  139. package/dist/cmd/git/unlink.d.ts.map +1 -0
  140. package/dist/cmd/git/unlink.js +98 -0
  141. package/dist/cmd/git/unlink.js.map +1 -0
  142. package/dist/cmd/index.d.ts.map +1 -1
  143. package/dist/cmd/index.js +2 -0
  144. package/dist/cmd/index.js.map +1 -1
  145. package/dist/cmd/integration/api.d.ts +61 -0
  146. package/dist/cmd/integration/api.d.ts.map +1 -0
  147. package/dist/cmd/integration/api.js +176 -0
  148. package/dist/cmd/integration/api.js.map +1 -0
  149. package/dist/cmd/integration/github/connect.d.ts +2 -0
  150. package/dist/cmd/integration/github/connect.d.ts.map +1 -0
  151. package/dist/cmd/integration/github/connect.js +197 -0
  152. package/dist/cmd/integration/github/connect.js.map +1 -0
  153. package/dist/cmd/integration/github/disconnect.d.ts +2 -0
  154. package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
  155. package/dist/cmd/integration/github/disconnect.js +121 -0
  156. package/dist/cmd/integration/github/disconnect.js.map +1 -0
  157. package/dist/cmd/integration/github/index.d.ts +2 -0
  158. package/dist/cmd/integration/github/index.d.ts.map +1 -0
  159. package/dist/cmd/integration/github/index.js +21 -0
  160. package/dist/cmd/integration/github/index.js.map +1 -0
  161. package/dist/cmd/integration/index.d.ts +2 -0
  162. package/dist/cmd/integration/index.d.ts.map +1 -0
  163. package/dist/cmd/integration/index.js +16 -0
  164. package/dist/cmd/integration/index.js.map +1 -0
  165. package/dist/cmd/project/auth/generate.d.ts +5 -0
  166. package/dist/cmd/project/auth/generate.d.ts.map +1 -0
  167. package/dist/cmd/project/auth/generate.js +102 -0
  168. package/dist/cmd/project/auth/generate.js.map +1 -0
  169. package/dist/cmd/project/auth/index.d.ts +2 -0
  170. package/dist/cmd/project/auth/index.d.ts.map +1 -0
  171. package/dist/cmd/project/auth/index.js +21 -0
  172. package/dist/cmd/project/auth/index.js.map +1 -0
  173. package/dist/cmd/project/auth/init.d.ts +2 -0
  174. package/dist/cmd/project/auth/init.d.ts.map +1 -0
  175. package/dist/cmd/project/auth/init.js +220 -0
  176. package/dist/cmd/project/auth/init.js.map +1 -0
  177. package/dist/cmd/project/auth/shared.d.ts +88 -0
  178. package/dist/cmd/project/auth/shared.d.ts.map +1 -0
  179. package/dist/cmd/project/auth/shared.js +435 -0
  180. package/dist/cmd/project/auth/shared.js.map +1 -0
  181. package/dist/cmd/project/index.d.ts.map +1 -1
  182. package/dist/cmd/project/index.js +9 -1
  183. package/dist/cmd/project/index.js.map +1 -1
  184. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  185. package/dist/cmd/project/template-flow.js +106 -0
  186. package/dist/cmd/project/template-flow.js.map +1 -1
  187. package/dist/config.d.ts +2 -0
  188. package/dist/config.d.ts.map +1 -1
  189. package/dist/config.js +24 -0
  190. package/dist/config.js.map +1 -1
  191. package/dist/errors.d.ts +2 -1
  192. package/dist/errors.d.ts.map +1 -1
  193. package/dist/errors.js +5 -0
  194. package/dist/errors.js.map +1 -1
  195. package/dist/types.d.ts +3 -4
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/types.js +5 -2
  198. package/dist/types.js.map +1 -1
  199. package/package.json +6 -5
  200. package/src/build-report.ts +457 -0
  201. package/src/cli.ts +20 -4
  202. package/src/cmd/build/entry-generator.ts +3 -1
  203. package/src/cmd/build/index.ts +51 -1
  204. package/src/cmd/build/typecheck.ts +19 -1
  205. package/src/cmd/build/vite/agent-discovery.ts +4 -4
  206. package/src/cmd/build/vite/index.ts +5 -2
  207. package/src/cmd/build/vite/metadata-generator.ts +5 -7
  208. package/src/cmd/build/vite/registry-generator.ts +136 -43
  209. package/src/cmd/build/vite/route-discovery.ts +20 -0
  210. package/src/cmd/build/vite/vite-builder.ts +13 -2
  211. package/src/cmd/build/vite-bundler.ts +17 -4
  212. package/src/cmd/cloud/deploy.ts +183 -12
  213. package/src/cmd/cloud/deployment/show.ts +0 -1
  214. package/src/cmd/cloud/sandbox/create.ts +22 -0
  215. package/src/cmd/cloud/sandbox/delete.ts +2 -6
  216. package/src/cmd/cloud/sandbox/download.ts +96 -0
  217. package/src/cmd/cloud/sandbox/env.ts +104 -0
  218. package/src/cmd/cloud/sandbox/get.ts +5 -0
  219. package/src/cmd/cloud/sandbox/index.ts +14 -0
  220. package/src/cmd/cloud/sandbox/ls.ts +126 -0
  221. package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
  222. package/src/cmd/cloud/sandbox/rm.ts +51 -0
  223. package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
  224. package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
  225. package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
  226. package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
  227. package/src/cmd/cloud/sandbox/upload.ts +83 -0
  228. package/src/cmd/dev/index.ts +147 -33
  229. package/src/cmd/dev/sync.ts +26 -30
  230. package/src/cmd/git/account/add.ts +317 -0
  231. package/src/cmd/git/account/index.ts +12 -0
  232. package/src/cmd/git/account/list.ts +139 -0
  233. package/src/cmd/git/account/remove.ts +212 -0
  234. package/src/cmd/git/index.ts +20 -0
  235. package/src/cmd/git/link.ts +468 -0
  236. package/src/cmd/git/list.ts +161 -0
  237. package/src/cmd/git/status.ts +144 -0
  238. package/src/cmd/git/unlink.ts +117 -0
  239. package/src/cmd/index.ts +2 -0
  240. package/src/cmd/integration/api.ts +379 -0
  241. package/src/cmd/integration/github/connect.ts +242 -0
  242. package/src/cmd/integration/github/disconnect.ts +149 -0
  243. package/src/cmd/integration/github/index.ts +21 -0
  244. package/src/cmd/integration/index.ts +16 -0
  245. package/src/cmd/project/auth/generate.ts +116 -0
  246. package/src/cmd/project/auth/index.ts +21 -0
  247. package/src/cmd/project/auth/init.ts +263 -0
  248. package/src/cmd/project/auth/shared.ts +534 -0
  249. package/src/cmd/project/index.ts +9 -1
  250. package/src/cmd/project/template-flow.ts +125 -0
  251. package/src/config.ts +34 -0
  252. package/src/errors.ts +7 -0
  253. package/src/types.ts +5 -2
@@ -12,6 +12,7 @@ import { runAllBuilds } from './vite/vite-builder';
12
12
  import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
13
13
  import { checkBunVersion } from '../../utils/bun-version-checker';
14
14
  import * as tui from '../../tui';
15
+ import type { BuildReportCollector } from '../../build-report';
15
16
 
16
17
  const AppFileNotFoundError = StructuredError('AppFileNotFoundError');
17
18
  const BuildFailedError = StructuredError('BuildFailedError');
@@ -26,6 +27,8 @@ export interface ViteBundleOptions {
26
27
  port?: number;
27
28
  logger: Logger;
28
29
  deploymentOptions?: DeployOptions;
30
+ /** Optional collector for structured error reporting */
31
+ collector?: BuildReportCollector;
29
32
  }
30
33
 
31
34
  /**
@@ -41,6 +44,7 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
41
44
  port = 3500,
42
45
  logger,
43
46
  deploymentOptions,
47
+ collector,
44
48
  } = options;
45
49
 
46
50
  const output: string[] = [];
@@ -52,8 +56,10 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
52
56
  // Verify app.ts exists
53
57
  const appFile = join(rootDir, 'app.ts');
54
58
  if (!(await Bun.file(appFile).exists())) {
59
+ const errorMessage = `App file not found at expected location: ${appFile}`;
60
+ collector?.addGeneralError('build', errorMessage, 'BUILD001');
55
61
  throw new AppFileNotFoundError({
56
- message: `App file not found at expected location: ${appFile}`,
62
+ message: errorMessage,
57
63
  });
58
64
  }
59
65
 
@@ -63,16 +69,20 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
63
69
  .then((s) => s.isDirectory())
64
70
  .catch(() => false);
65
71
  if (!srcDirExists) {
72
+ const errorMessage = `Source directory not found: ${srcDir}`;
73
+ collector?.addGeneralError('build', errorMessage, 'BUILD002');
66
74
  throw new BuildFailedError({
67
- message: `Source directory not found: ${srcDir}`,
75
+ message: errorMessage,
68
76
  });
69
77
  }
70
78
 
71
79
  // Check and upgrade @agentuity/* dependencies if needed
72
80
  const upgradeResult = await checkAndUpgradeDependencies(rootDir, logger);
73
81
  if (upgradeResult.failed.length > 0 && process.stdin.isTTY) {
82
+ const errorMessage = `Failed to upgrade dependencies: ${upgradeResult.failed.join(', ')}`;
83
+ collector?.addGeneralError('build', errorMessage, 'BUILD003');
74
84
  throw new BuildFailedError({
75
- message: `Failed to upgrade dependencies: ${upgradeResult.failed.join(', ')}`,
85
+ message: errorMessage,
76
86
  });
77
87
  }
78
88
 
@@ -90,6 +100,7 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
90
100
  deploymentId,
91
101
  logger,
92
102
  deploymentOptions,
103
+ collector,
93
104
  });
94
105
 
95
106
  if (result.client.included) {
@@ -106,8 +117,10 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
106
117
 
107
118
  return { output };
108
119
  } catch (error) {
120
+ const errorMessage = `Build failed: ${error instanceof Error ? error.message : String(error)}`;
121
+ collector?.addGeneralError('build', errorMessage, 'BUILD004');
109
122
  throw new BuildFailedError({
110
- message: `Build failed: ${error instanceof Error ? error.message : String(error)}`,
123
+ message: errorMessage,
111
124
  });
112
125
  }
113
126
  }
@@ -8,7 +8,14 @@ import { isRunningFromExecutable } from '../upgrade';
8
8
  import { createSubcommand, DeployOptionsSchema } from '../../types';
9
9
  import { getUserAgent } from '../../api';
10
10
  import * as tui from '../../tui';
11
- import { saveProjectDir, getDefaultConfigDir, loadProjectSDKKey } from '../../config';
11
+ import {
12
+ saveProjectDir,
13
+ getDefaultConfigDir,
14
+ loadProjectSDKKey,
15
+ updateProjectConfig,
16
+ } from '../../config';
17
+ import { getProjectGithubStatus } from '../integration/api';
18
+ import { runGitLink } from '../git/link';
12
19
  import {
13
20
  runSteps,
14
21
  stepSuccess,
@@ -45,6 +52,7 @@ import { getCommand } from '../../command-prefix';
45
52
  import * as domain from '../../domain';
46
53
  import { ErrorCode } from '../../errors';
47
54
  import { typecheck } from '../build/typecheck';
55
+ import { BuildReportCollector, setGlobalCollector, clearGlobalCollector } from '../../build-report';
48
56
 
49
57
  const DeploymentCancelledError = StructuredError(
50
58
  'DeploymentCancelled',
@@ -93,7 +101,12 @@ export const deploySubcommand = createSubcommand({
93
101
  options: z.intersection(
94
102
  DeployOptionsSchema,
95
103
  z.object({
96
- saveTypeErrors: z.string().optional().describe('file path to save typecheck errors'),
104
+ reportFile: z
105
+ .string()
106
+ .optional()
107
+ .describe(
108
+ 'file path to save build report JSON with errors, warnings, and diagnostics'
109
+ ),
97
110
  })
98
111
  ),
99
112
  response: DeployResponseSchema,
@@ -102,6 +115,14 @@ export const deploySubcommand = createSubcommand({
102
115
  async handler(ctx) {
103
116
  const { project, apiClient, projectDir, config, options, logger, opts } = ctx;
104
117
 
118
+ // Initialize build report collector if reportFile is specified
119
+ const collector = new BuildReportCollector();
120
+ if (opts.reportFile) {
121
+ collector.setOutputPath(opts.reportFile);
122
+ collector.enableAutoWrite();
123
+ setGlobalCollector(collector);
124
+ }
125
+
105
126
  let deployment: Deployment | undefined;
106
127
  let build: BuildMetadata | undefined;
107
128
  let instructions: DeploymentInstructions | undefined;
@@ -149,6 +170,76 @@ export const deploySubcommand = createSubcommand({
149
170
  try {
150
171
  await saveProjectDir(projectDir);
151
172
 
173
+ // Check GitHub status and prompt for setup if not linked
174
+ // Skip in non-TTY environments (CI, automated runs) to prevent hanging
175
+ const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
176
+ if (!useExistingDeployment && !project.skipGitSetup && hasTTY) {
177
+ try {
178
+ const githubStatus = await getProjectGithubStatus(apiClient, project.projectId);
179
+
180
+ if (githubStatus.linked && githubStatus.autoDeploy) {
181
+ // GitHub is already set up with auto-deploy, tell user to push instead
182
+ tui.newline();
183
+ tui.info(
184
+ `This project is linked to ${tui.bold(githubStatus.repoFullName ?? 'GitHub')} with automatic deployments enabled.`
185
+ );
186
+ tui.newline();
187
+ tui.info(
188
+ `Push a commit to the ${tui.bold(githubStatus.branch ?? 'main')} branch to trigger a deployment.`
189
+ );
190
+ tui.newline();
191
+ throw new DeploymentCancelledError();
192
+ }
193
+
194
+ if (!githubStatus.linked) {
195
+ tui.newline();
196
+ const wantSetup = await tui.confirm(
197
+ 'Would you like to set up automatic deployments from GitHub?'
198
+ );
199
+
200
+ if (wantSetup) {
201
+ const result = await runGitLink({
202
+ apiClient,
203
+ projectId: project.projectId,
204
+ orgId: project.orgId,
205
+ logger,
206
+ skipAlreadyLinkedCheck: true,
207
+ config,
208
+ });
209
+
210
+ if (result.linked && result.autoDeploy) {
211
+ // GitHub linked with auto-deploy, tell user to push instead
212
+ tui.newline();
213
+ tui.info('GitHub integration set up successfully!');
214
+ tui.newline();
215
+ tui.info('Push a commit to trigger your first deployment.');
216
+ tui.newline();
217
+ throw new DeploymentCancelledError();
218
+ } else if (result.linked) {
219
+ // Linked but auto-deploy disabled, continue with manual deploy
220
+ tui.newline();
221
+ tui.info('GitHub repository linked. Continuing with deployment...');
222
+ tui.newline();
223
+ }
224
+ } else {
225
+ await updateProjectConfig(projectDir, { skipGitSetup: true }, config);
226
+ tui.newline();
227
+ tui.info(
228
+ `Skipping GitHub setup. Run ${tui.bold(getCommand('git link'))} later to enable it.`
229
+ );
230
+ tui.newline();
231
+ }
232
+ }
233
+ } catch (err) {
234
+ // Re-throw intentional cancellations
235
+ if (err instanceof DeploymentCancelledError) {
236
+ throw err;
237
+ }
238
+ // Log other errors as non-fatal and continue
239
+ logger.trace('Failed to check GitHub status: %s', err);
240
+ }
241
+ }
242
+
152
243
  await runSteps(
153
244
  [
154
245
  !project.deployment?.domains?.length
@@ -239,8 +330,13 @@ export const deploySubcommand = createSubcommand({
239
330
  }
240
331
  let capturedOutput: string[] = [];
241
332
  const rootDir = resolve(projectDir);
333
+
334
+ // Run typecheck with collector for error reporting
335
+ const endTypecheckDiagnostic = collector.startDiagnostic('typecheck');
242
336
  const started = Date.now();
243
- const typeResult = await typecheck(rootDir);
337
+ const typeResult = await typecheck(rootDir, { collector });
338
+ endTypecheckDiagnostic();
339
+
244
340
  if (typeResult.success) {
245
341
  capturedOutput.push(
246
342
  tui.muted(
@@ -248,9 +344,10 @@ export const deploySubcommand = createSubcommand({
248
344
  )
249
345
  );
250
346
  } else {
251
- if ('errors' in typeResult && opts.saveTypeErrors) {
252
- const f = Bun.file(opts.saveTypeErrors);
253
- await f.write(JSON.stringify(typeResult.errors));
347
+ // Errors already added to collector by typecheck()
348
+ // Write report before returning error
349
+ if (opts.reportFile) {
350
+ await collector.forceWrite();
254
351
  }
255
352
  return stepError('Typecheck failed\n\n' + typeResult.output);
256
353
  }
@@ -264,6 +361,7 @@ export const deploySubcommand = createSubcommand({
264
361
  region: project.region,
265
362
  logger: ctx.logger,
266
363
  deploymentOptions: opts,
364
+ collector,
267
365
  });
268
366
  capturedOutput = [...capturedOutput, ...bundleResult.output];
269
367
  build = await loadBuildMetadata(join(projectDir, '.agentuity'));
@@ -275,6 +373,10 @@ export const deploySubcommand = createSubcommand({
275
373
  return stepSuccess(capturedOutput.length > 0 ? capturedOutput : undefined);
276
374
  } catch (ex) {
277
375
  const _ex = ex as Error;
376
+ // Write report before returning error
377
+ if (opts.reportFile) {
378
+ await collector.forceWrite();
379
+ }
278
380
  return stepError(
279
381
  _ex.message ?? 'Error building your project',
280
382
  _ex,
@@ -294,6 +396,8 @@ export const deploySubcommand = createSubcommand({
294
396
  return stepError('deployment instructions were null');
295
397
  }
296
398
 
399
+ // Start diagnostic for zip/encrypt phase
400
+ const endZipDiagnostic = collector.startDiagnostic('zip-package');
297
401
  progress(5);
298
402
  ctx.logger.trace('Starting deployment zip creation');
299
403
  // zip up the assets folder
@@ -319,8 +423,11 @@ export const deploySubcommand = createSubcommand({
319
423
  });
320
424
  ctx.logger.trace(`Deployment zip created: ${deploymentZip}`);
321
425
 
426
+ endZipDiagnostic();
427
+
322
428
  progress(20);
323
429
  // Encrypt the deployment zip using the public key from deployment
430
+ const endEncryptDiagnostic = collector.startDiagnostic('encrypt');
324
431
  const encryptedZip = join(tmpdir(), `${deployment.id}.enc.zip`);
325
432
  try {
326
433
  ctx.logger.trace('Creating public key');
@@ -348,8 +455,11 @@ export const deploySubcommand = createSubcommand({
348
455
  dst.end();
349
456
  });
350
457
  ctx.logger.trace('Stream finished');
458
+ endEncryptDiagnostic();
351
459
 
352
460
  progress(50);
461
+ // Start code upload diagnostic
462
+ const endCodeUploadDiagnostic = collector.startDiagnostic('code-upload');
353
463
  ctx.logger.trace(`Uploading deployment to ${instructions.deployment}`);
354
464
  const zipfile = Bun.file(encryptedZip);
355
465
  const fileSize = await zipfile.size;
@@ -364,8 +474,15 @@ export const deploySubcommand = createSubcommand({
364
474
  });
365
475
  ctx.logger.trace(`Upload response: ${resp.status}`);
366
476
  if (!resp.ok) {
367
- return stepError(`Error uploading deployment: ${await resp.text()}`);
477
+ endCodeUploadDiagnostic();
478
+ const errorMsg = `Error uploading deployment: ${await resp.text()}`;
479
+ collector.addGeneralError('deploy', errorMsg, 'DEPLOY002');
480
+ if (opts.reportFile) {
481
+ await collector.forceWrite();
482
+ }
483
+ return stepError(errorMsg);
368
484
  }
485
+ endCodeUploadDiagnostic();
369
486
 
370
487
  progress(70);
371
488
  ctx.logger.trace('Consuming response body');
@@ -385,11 +502,17 @@ export const deploySubcommand = createSubcommand({
385
502
  progress(80);
386
503
  let bytes = 0;
387
504
  if (build?.assets) {
505
+ // Start CDN upload diagnostic
506
+ const endCdnUploadDiagnostic = collector.startDiagnostic('cdn-upload');
388
507
  ctx.logger.trace(`Uploading ${build.assets.length} assets`);
389
508
  if (!instructions.assets) {
390
- return stepError(
391
- 'server did not provide asset upload URLs; upload aborted'
392
- );
509
+ const errorMsg =
510
+ 'server did not provide asset upload URLs; upload aborted';
511
+ collector.addGeneralError('deploy', errorMsg, 'DEPLOY006');
512
+ if (opts.reportFile) {
513
+ await collector.forceWrite();
514
+ }
515
+ return stepError(errorMsg);
393
516
  }
394
517
 
395
518
  // Workaround for Bun crash in compiled executables (https://github.com/agentuity/sdk/issues/191)
@@ -452,11 +575,17 @@ export const deploySubcommand = createSubcommand({
452
575
  const resps = await Promise.all(promises);
453
576
  for (const r of resps) {
454
577
  if (!r.ok) {
455
- return stepError(`error uploading asset: ${await r.text()}`);
578
+ const errorMsg = `error uploading asset: ${await r.text()}`;
579
+ collector.addGeneralError('deploy', errorMsg, 'DEPLOY006');
580
+ if (opts.reportFile) {
581
+ await collector.forceWrite();
582
+ }
583
+ return stepError(errorMsg);
456
584
  }
457
585
  }
458
586
  }
459
587
  ctx.logger.trace('Asset uploads complete');
588
+ endCdnUploadDiagnostic();
460
589
  progress(95);
461
590
  }
462
591
 
@@ -503,6 +632,7 @@ export const deploySubcommand = createSubcommand({
503
632
  const dashboard = `${appUrl}/r/${deployment.id}`;
504
633
 
505
634
  // Poll for deployment status with optional log streaming
635
+ const endDeploymentWaitDiagnostic = collector.startDiagnostic('deployment-wait');
506
636
  const pollInterval = 500;
507
637
  const maxAttempts = 600;
508
638
  let attempts = 0;
@@ -615,11 +745,16 @@ export const deploySubcommand = createSubcommand({
615
745
  },
616
746
  })
617
747
  .then(() => {
748
+ endDeploymentWaitDiagnostic();
618
749
  tui.success('Your project was deployed!');
619
750
  })
620
- .catch((ex) => {
751
+ .catch(async (ex) => {
752
+ endDeploymentWaitDiagnostic();
621
753
  // Handle cancellation
622
754
  if (ex instanceof DeploymentCancelledError) {
755
+ if (opts.reportFile) {
756
+ await collector.forceWrite();
757
+ }
623
758
  tui.warning('Deployment cancelled');
624
759
  process.exit(130); // Standard exit code for SIGINT
625
760
  }
@@ -628,6 +763,18 @@ export const deploySubcommand = createSubcommand({
628
763
  exwithmessage.message === 'Deployment failed'
629
764
  ? ''
630
765
  : exwithmessage.toString();
766
+
767
+ // Add error to collector
768
+ const isTimeout = exwithmessage.message === 'Deployment timed out';
769
+ collector.addGeneralError(
770
+ 'deploy',
771
+ msg || 'Deployment failed',
772
+ isTimeout ? 'DEPLOY003' : 'DEPLOY004'
773
+ );
774
+ if (opts.reportFile) {
775
+ await collector.forceWrite();
776
+ }
777
+
631
778
  tui.error(`Your deployment failed to start${msg ? `: ${msg}` : ''}`);
632
779
  if (logs.length) {
633
780
  const logsDir = join(getDefaultConfigDir(), 'logs');
@@ -687,9 +834,22 @@ export const deploySubcommand = createSubcommand({
687
834
  },
688
835
  });
689
836
 
837
+ endDeploymentWaitDiagnostic();
690
838
  tui.success('Your project was deployed!');
691
839
  }
692
840
  } catch (ex) {
841
+ endDeploymentWaitDiagnostic();
842
+ const exwithmessage = ex as { message: string };
843
+ const isTimeout = exwithmessage?.message === 'Deployment timed out';
844
+ collector.addGeneralError(
845
+ 'deploy',
846
+ exwithmessage?.message || String(ex),
847
+ isTimeout ? 'DEPLOY003' : 'DEPLOY004'
848
+ );
849
+ if (opts.reportFile) {
850
+ await collector.forceWrite();
851
+ }
852
+
693
853
  const lines = [`${ex}`, ''];
694
854
  lines.push(
695
855
  `${tui.ICONS.arrow} ${
@@ -741,6 +901,12 @@ export const deploySubcommand = createSubcommand({
741
901
  });
742
902
  }
743
903
 
904
+ // Write final report on success
905
+ if (opts.reportFile) {
906
+ await collector.forceWrite();
907
+ }
908
+ clearGlobalCollector();
909
+
744
910
  return {
745
911
  success: true,
746
912
  deploymentId: deployment.id,
@@ -756,6 +922,11 @@ export const deploySubcommand = createSubcommand({
756
922
  : undefined,
757
923
  };
758
924
  } catch (ex) {
925
+ collector.addGeneralError('deploy', String(ex), 'DEPLOY004');
926
+ if (opts.reportFile) {
927
+ await collector.forceWrite();
928
+ }
929
+ clearGlobalCollector();
759
930
  tui.fatal(`unexpected error trying to deploy project. ${ex}`);
760
931
  }
761
932
  },
@@ -36,7 +36,6 @@ const DeploymentShowResponseSchema = z.object({
36
36
  .object({
37
37
  number: z.number(),
38
38
  url: z.string().optional(),
39
- commentId: z.string().optional(),
40
39
  })
41
40
  .optional(),
42
41
  })
@@ -4,6 +4,12 @@ import * as tui from '../../../tui';
4
4
  import { createSandboxClient, parseFileArgs } from './util';
5
5
  import { getCommand } from '../../../command-prefix';
6
6
  import { sandboxCreate } from '@agentuity/server';
7
+ import { StructuredError } from '@agentuity/core';
8
+
9
+ const InvalidMetadataError = StructuredError(
10
+ 'InvalidMetadataError',
11
+ 'Metadata must be a valid JSON object'
12
+ );
7
13
 
8
14
  const SandboxCreateResponseSchema = z.object({
9
15
  sandboxId: z.string().describe('Unique sandbox identifier'),
@@ -55,6 +61,7 @@ export const createSubcommand = createCommand({
55
61
  .array(z.string())
56
62
  .optional()
57
63
  .describe('Apt packages to install (can be specified multiple times)'),
64
+ metadata: z.string().optional().describe('JSON object of user-defined metadata'),
58
65
  }),
59
66
  response: SandboxCreateResponseSchema,
60
67
  },
@@ -77,6 +84,20 @@ export const createSubcommand = createCommand({
77
84
  const files = parseFileArgs(opts.file);
78
85
  const hasFiles = files.length > 0;
79
86
 
87
+ let metadata: Record<string, unknown> | undefined;
88
+ if (opts.metadata) {
89
+ let parsed: unknown;
90
+ try {
91
+ parsed = JSON.parse(opts.metadata);
92
+ } catch {
93
+ throw new InvalidMetadataError();
94
+ }
95
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
96
+ throw new InvalidMetadataError();
97
+ }
98
+ metadata = parsed as Record<string, unknown>;
99
+ }
100
+
80
101
  const result = await sandboxCreate(client, {
81
102
  options: {
82
103
  resources:
@@ -93,6 +114,7 @@ export const createSubcommand = createCommand({
93
114
  command: hasFiles ? { exec: [], files } : undefined,
94
115
  snapshot: opts.snapshot,
95
116
  dependencies: opts.dependency,
117
+ metadata,
96
118
  },
97
119
  orgId,
98
120
  });
@@ -14,7 +14,7 @@ const SandboxDeleteResponseSchema = z.object({
14
14
 
15
15
  export const deleteSubcommand = createCommand({
16
16
  name: 'delete',
17
- aliases: ['del', 'rm', 'remove', 'destroy'],
17
+ aliases: ['del', 'remove', 'destroy'],
18
18
  description: 'Delete a sandbox',
19
19
  tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
20
20
  requires: { auth: true, region: true, org: true },
@@ -25,11 +25,7 @@ export const deleteSubcommand = createCommand({
25
25
  description: 'Delete a sandbox',
26
26
  },
27
27
  {
28
- command: getCommand('cloud sandbox rm abc123'),
29
- description: 'Delete using alias',
30
- },
31
- {
32
- command: getCommand('cloud sandbox rm abc123 --confirm'),
28
+ command: getCommand('cloud sandbox delete abc123 --confirm'),
33
29
  description: 'Delete without confirmation prompt',
34
30
  },
35
31
  ],
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { createCommand } from '../../../types';
4
+ import * as tui from '../../../tui';
5
+ import { createSandboxClient } from './util';
6
+ import { getCommand } from '../../../command-prefix';
7
+ import { sandboxDownloadArchive } from '@agentuity/server';
8
+
9
+ export const downloadSubcommand = createCommand({
10
+ name: 'download',
11
+ aliases: ['dl'],
12
+ description: 'Download files from a sandbox as a compressed archive',
13
+ tags: ['slow', 'requires-auth'],
14
+ requires: { auth: true, region: true, org: true },
15
+ examples: [
16
+ {
17
+ command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz'),
18
+ description: 'Download sandbox files as tar.gz archive',
19
+ },
20
+ {
21
+ command: getCommand('cloud sandbox download sbx_abc123 ./backup.zip --format zip'),
22
+ description: 'Download sandbox files as zip archive',
23
+ },
24
+ {
25
+ command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz --path /subdir'),
26
+ description: 'Download only a specific directory',
27
+ },
28
+ ],
29
+ schema: {
30
+ args: z.object({
31
+ sandboxId: z.string().describe('The sandbox ID'),
32
+ output: z.string().describe('Output file path for the archive'),
33
+ }),
34
+ options: z.object({
35
+ path: z.string().optional().describe('Path in sandbox to download (defaults to root)'),
36
+ format: z
37
+ .enum(['zip', 'tar.gz'])
38
+ .default('tar.gz')
39
+ .optional()
40
+ .describe('Archive format (zip or tar.gz)'),
41
+ }),
42
+ response: z.object({
43
+ success: z.boolean(),
44
+ output: z.string(),
45
+ bytes: z.number(),
46
+ }),
47
+ },
48
+
49
+ async handler(ctx) {
50
+ const { args, opts, options, auth, region, logger, orgId } = ctx;
51
+
52
+ const client = createSandboxClient(logger, auth, region);
53
+ const format = opts.format || 'tar.gz';
54
+
55
+ const stream = await sandboxDownloadArchive(client, {
56
+ sandboxId: args.sandboxId,
57
+ path: opts.path || '.',
58
+ format,
59
+ orgId,
60
+ });
61
+
62
+ const chunks: Uint8Array[] = [];
63
+ const reader = stream.getReader();
64
+
65
+ while (true) {
66
+ const { done, value } = await reader.read();
67
+ if (done) break;
68
+ chunks.push(value);
69
+ }
70
+
71
+ const totalBytes = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
72
+ const buffer = new Uint8Array(totalBytes);
73
+ let offset = 0;
74
+ for (const chunk of chunks) {
75
+ buffer.set(chunk, offset);
76
+ offset += chunk.length;
77
+ }
78
+
79
+ writeFileSync(args.output, buffer);
80
+
81
+ if (!options.json) {
82
+ tui.success(`Downloaded ${formatSize(totalBytes)} to ${args.output}`);
83
+ }
84
+
85
+ return { success: true, output: args.output, bytes: totalBytes };
86
+ },
87
+ });
88
+
89
+ function formatSize(bytes: number): string {
90
+ if (bytes < 1024) return `${bytes} bytes`;
91
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
92
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
93
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
94
+ }
95
+
96
+ export default downloadSubcommand;