@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "0.0.109",
3
+ "version": "0.0.111",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -40,8 +40,9 @@
40
40
  "prepublishOnly": "bun run clean && bun run build"
41
41
  },
42
42
  "dependencies": {
43
- "@agentuity/core": "0.0.109",
44
- "@agentuity/server": "0.0.109",
43
+ "@agentuity/auth": "0.0.111",
44
+ "@agentuity/core": "0.0.111",
45
+ "@agentuity/server": "0.0.111",
45
46
  "@datasert/cronjs-parser": "^1.4.0",
46
47
  "@terascope/fetch-github-release": "^2.2.1",
47
48
  "@vitejs/plugin-react": "^5.1.2",
@@ -58,10 +59,10 @@
58
59
  "tar-fs": "^3.1.1",
59
60
  "typescript": "^5.9.0",
60
61
  "vite": "^7.2.7",
61
- "zod": "^4.1.12"
62
+ "zod": "^4.3.5"
62
63
  },
63
64
  "devDependencies": {
64
- "@agentuity/test-utils": "0.0.109",
65
+ "@agentuity/test-utils": "0.0.111",
65
66
  "@types/adm-zip": "^0.5.7",
66
67
  "@types/bun": "latest",
67
68
  "@types/tar-fs": "^2.0.4",
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Build Report
3
+ *
4
+ * Structured error/warning collection and reporting for build and deploy commands.
5
+ * Outputs a JSON file with a strict schema for CI tooling integration.
6
+ */
7
+
8
+ import { writeFileSync } from 'node:fs';
9
+ import type { GrammarItem } from './tsc-output-parser';
10
+
11
+ /**
12
+ * Error codes for non-TypeScript errors.
13
+ * TypeScript errors use their native TS#### codes.
14
+ */
15
+ export const BuildErrorCodes = {
16
+ // AST/Metadata errors (AST0xx)
17
+ AST001: 'MetadataNameMissing',
18
+ AST002: 'DuplicateName',
19
+ AST003: 'InvalidExport',
20
+ AST004: 'InvalidCronExpression',
21
+ AST005: 'InvalidAgentConfig',
22
+
23
+ // Build errors (BUILD0xx)
24
+ BUILD001: 'AppFileNotFound',
25
+ BUILD002: 'SourceDirNotFound',
26
+ BUILD003: 'DependencyUpgradeFailed',
27
+ BUILD004: 'BundleFailed',
28
+ BUILD005: 'EntryPointNotFound',
29
+ BUILD006: 'ViteBuildFailed',
30
+ BUILD007: 'RuntimePackageNotFound',
31
+ BUILD008: 'TypecheckToolFailed',
32
+
33
+ // Validation errors (VAL0xx)
34
+ VAL001: 'AgentIdentifierCollision',
35
+ VAL002: 'InvalidRoutePath',
36
+ VAL003: 'InvalidRouteMethod',
37
+ VAL004: 'SchemaValidationFailed',
38
+
39
+ // Deploy errors (DEPLOY0xx)
40
+ DEPLOY001: 'DeploymentCreationFailed',
41
+ DEPLOY002: 'UploadFailed',
42
+ DEPLOY003: 'DeploymentTimeout',
43
+ DEPLOY004: 'DeploymentFailed',
44
+ DEPLOY005: 'EncryptionFailed',
45
+ DEPLOY006: 'CDNUploadFailed',
46
+ } as const;
47
+
48
+ export type BuildErrorCode = keyof typeof BuildErrorCodes;
49
+
50
+ /**
51
+ * Error scopes for categorizing errors
52
+ */
53
+ export type ErrorScope = 'typescript' | 'ast' | 'build' | 'bundler' | 'validation' | 'deploy';
54
+
55
+ /**
56
+ * File-specific error with location information
57
+ */
58
+ export interface FileError {
59
+ type: 'file';
60
+ scope: ErrorScope;
61
+ path: string;
62
+ line: number;
63
+ column: number;
64
+ message: string;
65
+ code?: string;
66
+ }
67
+
68
+ /**
69
+ * General error without file location
70
+ */
71
+ export interface GeneralError {
72
+ type: 'general';
73
+ scope: ErrorScope;
74
+ message: string;
75
+ code?: string;
76
+ }
77
+
78
+ /**
79
+ * Union type for all build errors
80
+ */
81
+ export type BuildError = FileError | GeneralError;
82
+
83
+ /**
84
+ * Union type for all build warnings (same structure as errors)
85
+ */
86
+ export type BuildWarning = FileError | GeneralError;
87
+
88
+ /**
89
+ * Diagnostic timing information for a build phase
90
+ */
91
+ export interface BuildDiagnostic {
92
+ name: string;
93
+ startedAt: string;
94
+ completedAt: string;
95
+ durationMs: number;
96
+ }
97
+
98
+ /**
99
+ * Complete build report structure
100
+ */
101
+ export interface BuildReport {
102
+ success: boolean;
103
+ errors: BuildError[];
104
+ warnings: BuildWarning[];
105
+ diagnostics: BuildDiagnostic[];
106
+ }
107
+
108
+ /**
109
+ * Diagnostic phases for timing
110
+ */
111
+ export const DiagnosticPhases = [
112
+ 'typecheck',
113
+ 'client-build',
114
+ 'workbench-build',
115
+ 'server-build',
116
+ 'metadata-generation',
117
+ 'zip-package',
118
+ 'encrypt',
119
+ 'code-upload',
120
+ 'cdn-upload',
121
+ 'deployment-wait',
122
+ ] as const;
123
+
124
+ export type DiagnosticPhase = (typeof DiagnosticPhases)[number];
125
+
126
+ /**
127
+ * Active diagnostic tracker
128
+ */
129
+ interface ActiveDiagnostic {
130
+ name: string;
131
+ startedAt: Date;
132
+ }
133
+
134
+ /**
135
+ * Build Report Collector
136
+ *
137
+ * Collects errors, warnings, and diagnostic timing information throughout
138
+ * the build/deploy pipeline. Can be configured to automatically write
139
+ * the report on process exit.
140
+ */
141
+ export class BuildReportCollector {
142
+ private errors: BuildError[] = [];
143
+ private warnings: BuildWarning[] = [];
144
+ private diagnostics: BuildDiagnostic[] = [];
145
+ private activeDiagnostics: Map<string, ActiveDiagnostic> = new Map();
146
+ private outputPath: string | null = null;
147
+ private autoWriteEnabled = false;
148
+ private written = false;
149
+
150
+ private beforeExitHandler: (() => void) | null = null;
151
+ private sigintHandler: (() => void) | null = null;
152
+ private sigtermHandler: (() => void) | null = null;
153
+
154
+ /**
155
+ * Set the output path for the report file
156
+ */
157
+ setOutputPath(path: string): void {
158
+ this.outputPath = path;
159
+ }
160
+
161
+ /**
162
+ * Enable automatic writing of the report on process exit.
163
+ * This ensures the report is written even if the process exits unexpectedly.
164
+ */
165
+ enableAutoWrite(): void {
166
+ if (this.autoWriteEnabled || !this.outputPath) return;
167
+
168
+ this.autoWriteEnabled = true;
169
+
170
+ // Use beforeExit for graceful exits
171
+ this.beforeExitHandler = () => {
172
+ this.writeSync();
173
+ };
174
+ process.once('beforeExit', this.beforeExitHandler);
175
+
176
+ // Handle SIGINT/SIGTERM with process.once to avoid stacking handlers
177
+ this.sigintHandler = () => {
178
+ this.writeSync();
179
+ process.exit(130);
180
+ };
181
+ this.sigtermHandler = () => {
182
+ this.writeSync();
183
+ process.exit(143);
184
+ };
185
+ process.once('SIGINT', this.sigintHandler);
186
+ process.once('SIGTERM', this.sigtermHandler);
187
+ }
188
+
189
+ /**
190
+ * Disable automatic writing and remove signal handlers.
191
+ * Call this when done with the collector to prevent handler conflicts.
192
+ */
193
+ disableAutoWrite(): void {
194
+ if (!this.autoWriteEnabled) return;
195
+
196
+ this.autoWriteEnabled = false;
197
+
198
+ if (this.beforeExitHandler) {
199
+ process.removeListener('beforeExit', this.beforeExitHandler);
200
+ this.beforeExitHandler = null;
201
+ }
202
+ if (this.sigintHandler) {
203
+ process.removeListener('SIGINT', this.sigintHandler);
204
+ this.sigintHandler = null;
205
+ }
206
+ if (this.sigtermHandler) {
207
+ process.removeListener('SIGTERM', this.sigtermHandler);
208
+ this.sigtermHandler = null;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Add TypeScript errors from parsed tsc output
214
+ */
215
+ addTypeScriptErrors(items: GrammarItem[]): void {
216
+ for (const item of items) {
217
+ if (item.type !== 'Item' || !item.value) continue;
218
+
219
+ const val = item.value;
220
+ const isError = val.tsError?.value?.type === 'error';
221
+ const isWarning = val.tsError?.value?.type === 'warning';
222
+
223
+ if (!isError && !isWarning) continue;
224
+
225
+ const entry: FileError = {
226
+ type: 'file',
227
+ scope: 'typescript',
228
+ path: val.path?.value ?? 'unknown',
229
+ line: val.cursor?.value?.line ?? 0,
230
+ column: val.cursor?.value?.col ?? 0,
231
+ message: (val.message?.value ?? '').trim(),
232
+ code: val.tsError?.value?.errorString,
233
+ };
234
+
235
+ if (isError) {
236
+ this.errors.push(entry);
237
+ } else {
238
+ this.warnings.push(entry);
239
+ }
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Add a file-specific error
245
+ */
246
+ addFileError(
247
+ scope: ErrorScope,
248
+ path: string,
249
+ line: number,
250
+ column: number,
251
+ message: string,
252
+ code?: string
253
+ ): void {
254
+ this.errors.push({
255
+ type: 'file',
256
+ scope,
257
+ path,
258
+ line,
259
+ column,
260
+ message,
261
+ code,
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Add a general error without file location
267
+ */
268
+ addGeneralError(scope: ErrorScope, message: string, code?: string): void {
269
+ this.errors.push({
270
+ type: 'general',
271
+ scope,
272
+ message,
273
+ code,
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Add a file-specific warning
279
+ */
280
+ addFileWarning(
281
+ scope: ErrorScope,
282
+ path: string,
283
+ line: number,
284
+ column: number,
285
+ message: string,
286
+ code?: string
287
+ ): void {
288
+ this.warnings.push({
289
+ type: 'file',
290
+ scope,
291
+ path,
292
+ line,
293
+ column,
294
+ message,
295
+ code,
296
+ });
297
+ }
298
+
299
+ /**
300
+ * Add a general warning without file location
301
+ */
302
+ addGeneralWarning(scope: ErrorScope, message: string, code?: string): void {
303
+ this.warnings.push({
304
+ type: 'general',
305
+ scope,
306
+ message,
307
+ code,
308
+ });
309
+ }
310
+
311
+ /**
312
+ * Start timing a diagnostic phase
313
+ * @returns A function to call when the phase completes
314
+ */
315
+ startDiagnostic(name: string): () => void {
316
+ const startedAt = new Date();
317
+ this.activeDiagnostics.set(name, { name, startedAt });
318
+
319
+ return () => {
320
+ this.endDiagnostic(name);
321
+ };
322
+ }
323
+
324
+ /**
325
+ * End a diagnostic phase
326
+ */
327
+ private endDiagnostic(name: string): void {
328
+ const active = this.activeDiagnostics.get(name);
329
+ if (!active) return;
330
+
331
+ const completedAt = new Date();
332
+ const durationMs = completedAt.getTime() - active.startedAt.getTime();
333
+
334
+ this.diagnostics.push({
335
+ name,
336
+ startedAt: active.startedAt.toISOString(),
337
+ completedAt: completedAt.toISOString(),
338
+ durationMs,
339
+ });
340
+
341
+ this.activeDiagnostics.delete(name);
342
+ }
343
+
344
+ /**
345
+ * Check if there are any errors
346
+ */
347
+ hasErrors(): boolean {
348
+ return this.errors.length > 0;
349
+ }
350
+
351
+ /**
352
+ * Check if there are any warnings
353
+ */
354
+ hasWarnings(): boolean {
355
+ return this.warnings.length > 0;
356
+ }
357
+
358
+ /**
359
+ * Get the error count
360
+ */
361
+ getErrorCount(): number {
362
+ return this.errors.length;
363
+ }
364
+
365
+ /**
366
+ * Get the warning count
367
+ */
368
+ getWarningCount(): number {
369
+ return this.warnings.length;
370
+ }
371
+
372
+ /**
373
+ * Generate the complete build report
374
+ */
375
+ toReport(): BuildReport {
376
+ // Complete any active diagnostics - collect keys first to avoid
377
+ // iterating while modifying the map
378
+ const activeKeys = [...this.activeDiagnostics.keys()];
379
+ for (const name of activeKeys) {
380
+ this.endDiagnostic(name);
381
+ }
382
+
383
+ return {
384
+ success: this.errors.length === 0,
385
+ errors: [...this.errors],
386
+ warnings: [...this.warnings],
387
+ diagnostics: [...this.diagnostics],
388
+ };
389
+ }
390
+
391
+ /**
392
+ * Write the report to the configured output path asynchronously
393
+ */
394
+ async write(): Promise<void> {
395
+ if (!this.outputPath || this.written) return;
396
+
397
+ this.written = true;
398
+ const report = this.toReport();
399
+ const file = Bun.file(this.outputPath);
400
+ await file.write(JSON.stringify(report, null, '\t'));
401
+ }
402
+
403
+ /**
404
+ * Write the report synchronously (for exit handlers)
405
+ */
406
+ writeSync(): void {
407
+ if (!this.outputPath || this.written) return;
408
+
409
+ this.written = true;
410
+ const report = this.toReport();
411
+ writeFileSync(this.outputPath, JSON.stringify(report, null, '\t'));
412
+ }
413
+
414
+ /**
415
+ * Force write the report (bypasses the written flag)
416
+ * Use this when you want to update the report file mid-process
417
+ */
418
+ async forceWrite(): Promise<void> {
419
+ if (!this.outputPath) return;
420
+
421
+ const report = this.toReport();
422
+ const file = Bun.file(this.outputPath);
423
+ await file.write(JSON.stringify(report, null, '\t'));
424
+ this.written = true;
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Global collector instance for use across the build pipeline.
430
+ * Commands should create their own collector and pass it through,
431
+ * but this provides a fallback for error handling in deeply nested code.
432
+ */
433
+ let globalCollector: BuildReportCollector | null = null;
434
+
435
+ /**
436
+ * Set the global collector instance
437
+ */
438
+ export function setGlobalCollector(collector: BuildReportCollector): void {
439
+ globalCollector = collector;
440
+ }
441
+
442
+ /**
443
+ * Get the global collector instance (may be null)
444
+ */
445
+ export function getGlobalCollector(): BuildReportCollector | null {
446
+ return globalCollector;
447
+ }
448
+
449
+ /**
450
+ * Clear the global collector instance and clean up its signal handlers
451
+ */
452
+ export function clearGlobalCollector(): void {
453
+ if (globalCollector) {
454
+ globalCollector.disableAutoWrite();
455
+ }
456
+ globalCollector = null;
457
+ }
package/src/cli.ts CHANGED
@@ -15,7 +15,7 @@ import type {
15
15
  } from './types';
16
16
  import { showBanner, generateBanner } from './banner';
17
17
  import { requireAuth, optionalAuth, requireOrg, optionalOrg as selectOptionalOrg } from './auth';
18
- import { listRegions, type RegionList } from '@agentuity/server';
18
+ import { listRegions, type RegionList, ValidationOutputError } from '@agentuity/server';
19
19
  import enquirer from 'enquirer';
20
20
  import * as tui from './tui';
21
21
  import { parseArgsSchema, parseOptionsSchema, buildValidationInput } from './schema-parser';
@@ -26,6 +26,22 @@ import { getCommand } from './command-prefix';
26
26
  import { isValidateMode, outputValidation, type ValidationResult } from './output';
27
27
  import { StructuredError } from '@agentuity/core';
28
28
 
29
+ /**
30
+ * Check if an error is a CLI input validation error (Zod error from schema parsing),
31
+ * and not an API response validation error (ValidationOutputError).
32
+ */
33
+ function isCLIValidationError(error: unknown): boolean {
34
+ if (!error || typeof error !== 'object' || !('issues' in error)) {
35
+ return false;
36
+ }
37
+ // ValidationOutputError from API responses should NOT be treated as CLI validation errors
38
+ if (error instanceof ValidationOutputError) {
39
+ return false;
40
+ }
41
+ // Check for Zod error structure (has name 'ZodError' or is from SchemaValidationError)
42
+ return true;
43
+ }
44
+
29
45
  const APIClientConfigError = StructuredError('APIClientConfigError');
30
46
 
31
47
  function createAPIClient(baseCtx: CommandContext, config: Config | null): APIClient {
@@ -1143,7 +1159,7 @@ async function registerSubcommand(
1143
1159
  subcommand.webUrl
1144
1160
  );
1145
1161
  } catch (error) {
1146
- if (error && typeof error === 'object' && 'issues' in error) {
1162
+ if (isCLIValidationError(error)) {
1147
1163
  handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1148
1164
  }
1149
1165
  handleProjectConfigError(
@@ -1322,7 +1338,7 @@ async function registerSubcommand(
1322
1338
  subcommand.webUrl
1323
1339
  );
1324
1340
  } catch (error) {
1325
- if (error && typeof error === 'object' && 'issues' in error) {
1341
+ if (isCLIValidationError(error)) {
1326
1342
  handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1327
1343
  }
1328
1344
  handleProjectConfigError(
@@ -1447,7 +1463,7 @@ async function registerSubcommand(
1447
1463
  subcommand.webUrl
1448
1464
  );
1449
1465
  } catch (error) {
1450
- if (error && typeof error === 'object' && 'issues' in error) {
1466
+ if (isCLIValidationError(error)) {
1451
1467
  handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1452
1468
  }
1453
1469
  handleProjectConfigError(
@@ -85,10 +85,12 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
85
85
  imports.push(`import { type LogLevel } from '@agentuity/core';`);
86
86
 
87
87
  // Generate route mounting code for all discovered routes
88
+ // Sort route files for deterministic output
89
+ const sortedRouteFiles = [...routeFiles].sort();
88
90
  const routeImportsAndMounts: string[] = [];
89
91
  let routeIndex = 0;
90
92
 
91
- for (const routeFile of routeFiles) {
93
+ for (const routeFile of sortedRouteFiles) {
92
94
  // Convert src/api/auth/route.ts -> auth/route
93
95
  const relativePath = routeFile.replace(/^src\/api\//, '').replace(/\.tsx?$/, '');
94
96
 
@@ -6,6 +6,7 @@ import * as tui from '../../tui';
6
6
  import { getCommand } from '../../command-prefix';
7
7
  import { ErrorCode } from '../../errors';
8
8
  import { typecheck } from './typecheck';
9
+ import { BuildReportCollector, setGlobalCollector, clearGlobalCollector } from '../../build-report';
9
10
 
10
11
  const BuildResponseSchema = z.object({
11
12
  success: z.boolean().describe('Whether the build succeeded'),
@@ -36,6 +37,10 @@ export const command = createCommand({
36
37
  .default(false)
37
38
  .optional()
38
39
  .describe('Skip typecheck after build'),
40
+ reportFile: z
41
+ .string()
42
+ .optional()
43
+ .describe('file path to save build report JSON with errors, warnings, and diagnostics'),
39
44
  }),
40
45
  response: BuildResponseSchema,
41
46
  },
@@ -43,6 +48,14 @@ export const command = createCommand({
43
48
  async handler(ctx) {
44
49
  const { opts, projectDir, project } = ctx;
45
50
 
51
+ // Initialize build report collector if reportFile is specified
52
+ const collector = new BuildReportCollector();
53
+ if (opts.reportFile) {
54
+ collector.setOutputPath(opts.reportFile);
55
+ collector.enableAutoWrite();
56
+ setGlobalCollector(collector);
57
+ }
58
+
46
59
  const absoluteProjectDir = resolve(projectDir);
47
60
  const outDir = opts.outdir ? resolve(opts.outdir) : join(absoluteProjectDir, '.agentuity');
48
61
 
@@ -59,6 +72,7 @@ export const command = createCommand({
59
72
  orgId: project?.orgId,
60
73
  region: project?.region ?? 'local',
61
74
  logger: ctx.logger,
75
+ collector,
62
76
  });
63
77
 
64
78
  // Copy profile-specific .env file AFTER bundling (bundler clears outDir first)
@@ -79,7 +93,10 @@ export const command = createCommand({
79
93
  if (!opts.dev && !opts.skipTypeCheck) {
80
94
  try {
81
95
  tui.info('Running type check...');
82
- const typeResult = await typecheck(absoluteProjectDir);
96
+ const endTypecheckDiagnostic = collector.startDiagnostic('typecheck');
97
+ const typeResult = await typecheck(absoluteProjectDir, { collector });
98
+ endTypecheckDiagnostic();
99
+
83
100
  if (typeResult.success) {
84
101
  tui.success('Type check passed');
85
102
  } else {
@@ -88,10 +105,24 @@ export const command = createCommand({
88
105
  console.error('');
89
106
  const msg =
90
107
  'errors' in typeResult ? 'Fix type errors before building' : 'Build error';
108
+
109
+ // Write report before fatal exit
110
+ if (opts.reportFile) {
111
+ await collector.forceWrite();
112
+ }
113
+ clearGlobalCollector();
91
114
  tui.fatal(msg, ErrorCode.BUILD_FAILED);
92
115
  }
93
116
  } catch (error: unknown) {
94
117
  const errorMsg = error instanceof Error ? error.message : String(error);
118
+ collector.addGeneralError('typescript', errorMsg, 'BUILD008');
119
+
120
+ // Write report before fatal exit
121
+ if (opts.reportFile) {
122
+ await collector.forceWrite();
123
+ }
124
+ clearGlobalCollector();
125
+
95
126
  tui.error(`Type check failed to run: ${errorMsg}`);
96
127
  tui.fatal(
97
128
  'Unable to run TypeScript type checking. Ensure TypeScript is installed.',
@@ -102,6 +133,12 @@ export const command = createCommand({
102
133
 
103
134
  tui.success('Build complete');
104
135
 
136
+ // Write final report on success
137
+ if (opts.reportFile) {
138
+ await collector.forceWrite();
139
+ }
140
+ clearGlobalCollector();
141
+
105
142
  return {
106
143
  success: true,
107
144
  bundlePath: outDir,
@@ -109,11 +146,24 @@ export const command = createCommand({
109
146
  dev: opts.dev || false,
110
147
  };
111
148
  } catch (error: unknown) {
149
+ // Add error to collector
112
150
  if (error instanceof AggregateError) {
113
151
  const ae = error as AggregateError;
114
152
  for (const e of ae.errors) {
153
+ collector.addGeneralError('build', e.message, 'BUILD004');
115
154
  tui.error(e.message);
116
155
  }
156
+ } else {
157
+ collector.addGeneralError('build', String(error), 'BUILD004');
158
+ }
159
+
160
+ // Write report before fatal exit
161
+ if (opts.reportFile) {
162
+ await collector.forceWrite();
163
+ }
164
+ clearGlobalCollector();
165
+
166
+ if (error instanceof AggregateError) {
117
167
  tui.fatal('Build failed', ErrorCode.BUILD_FAILED);
118
168
  } else {
119
169
  tui.fatal(`Build failed: ${error}`, ErrorCode.BUILD_FAILED);