@agentuity/cli 0.0.108 → 0.0.110

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 (87) 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/cmd/build/entry-generator.d.ts.map +1 -1
  6. package/dist/cmd/build/entry-generator.js +9 -3
  7. package/dist/cmd/build/entry-generator.js.map +1 -1
  8. package/dist/cmd/build/index.d.ts.map +1 -1
  9. package/dist/cmd/build/index.js +44 -1
  10. package/dist/cmd/build/index.js.map +1 -1
  11. package/dist/cmd/build/typecheck.d.ts +7 -1
  12. package/dist/cmd/build/typecheck.d.ts.map +1 -1
  13. package/dist/cmd/build/typecheck.js +11 -1
  14. package/dist/cmd/build/typecheck.js.map +1 -1
  15. package/dist/cmd/build/vite/index.d.ts +2 -1
  16. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  17. package/dist/cmd/build/vite/index.js +2 -1
  18. package/dist/cmd/build/vite/index.js.map +1 -1
  19. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  20. package/dist/cmd/build/vite/metadata-generator.js +2 -4
  21. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  22. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  23. package/dist/cmd/build/vite/registry-generator.js +56 -18
  24. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  25. package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
  26. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/vite-builder.js +14 -1
  28. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  29. package/dist/cmd/build/vite-bundler.d.ts +3 -0
  30. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  31. package/dist/cmd/build/vite-bundler.js +14 -5
  32. package/dist/cmd/build/vite-bundler.js.map +1 -1
  33. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deploy.js +86 -9
  35. package/dist/cmd/cloud/deploy.js.map +1 -1
  36. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  37. package/dist/cmd/cloud/deployment/show.js +0 -1
  38. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  39. package/dist/cmd/dev/index.d.ts.map +1 -1
  40. package/dist/cmd/dev/index.js +109 -15
  41. package/dist/cmd/dev/index.js.map +1 -1
  42. package/dist/cmd/project/auth/generate.d.ts +5 -0
  43. package/dist/cmd/project/auth/generate.d.ts.map +1 -0
  44. package/dist/cmd/project/auth/generate.js +102 -0
  45. package/dist/cmd/project/auth/generate.js.map +1 -0
  46. package/dist/cmd/project/auth/index.d.ts +2 -0
  47. package/dist/cmd/project/auth/index.d.ts.map +1 -0
  48. package/dist/cmd/project/auth/index.js +21 -0
  49. package/dist/cmd/project/auth/index.js.map +1 -0
  50. package/dist/cmd/project/auth/init.d.ts +2 -0
  51. package/dist/cmd/project/auth/init.d.ts.map +1 -0
  52. package/dist/cmd/project/auth/init.js +220 -0
  53. package/dist/cmd/project/auth/init.js.map +1 -0
  54. package/dist/cmd/project/auth/shared.d.ts +88 -0
  55. package/dist/cmd/project/auth/shared.d.ts.map +1 -0
  56. package/dist/cmd/project/auth/shared.js +435 -0
  57. package/dist/cmd/project/auth/shared.js.map +1 -0
  58. package/dist/cmd/project/index.d.ts.map +1 -1
  59. package/dist/cmd/project/index.js +9 -1
  60. package/dist/cmd/project/index.js.map +1 -1
  61. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  62. package/dist/cmd/project/template-flow.js +106 -0
  63. package/dist/cmd/project/template-flow.js.map +1 -1
  64. package/dist/types.d.ts +1 -3
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/types.js +1 -2
  67. package/dist/types.js.map +1 -1
  68. package/package.json +5 -4
  69. package/src/build-report.ts +457 -0
  70. package/src/cmd/build/entry-generator.ts +9 -3
  71. package/src/cmd/build/index.ts +51 -1
  72. package/src/cmd/build/typecheck.ts +19 -1
  73. package/src/cmd/build/vite/index.ts +4 -1
  74. package/src/cmd/build/vite/metadata-generator.ts +4 -6
  75. package/src/cmd/build/vite/registry-generator.ts +58 -19
  76. package/src/cmd/build/vite/vite-builder.ts +18 -1
  77. package/src/cmd/build/vite-bundler.ts +17 -4
  78. package/src/cmd/cloud/deploy.ts +105 -11
  79. package/src/cmd/cloud/deployment/show.ts +0 -1
  80. package/src/cmd/dev/index.ts +115 -14
  81. package/src/cmd/project/auth/generate.ts +116 -0
  82. package/src/cmd/project/auth/index.ts +21 -0
  83. package/src/cmd/project/auth/init.ts +263 -0
  84. package/src/cmd/project/auth/shared.ts +534 -0
  85. package/src/cmd/project/index.ts +9 -1
  86. package/src/cmd/project/template-flow.ts +125 -0
  87. package/src/types.ts +1 -2
@@ -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
+ }
@@ -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
 
@@ -237,7 +239,7 @@ if (isDevelopment()) {
237
239
  // 404 for unmatched API/system routes
238
240
  app.all('/_agentuity/*', (c: Context) => c.notFound());
239
241
  app.all('/api/*', (c: Context) => c.notFound());
240
- if (hasWorkbench) {
242
+ if (!hasWorkbench) {
241
243
  app.all('/workbench/*', (c: Context) => c.notFound());
242
244
  }
243
245
 
@@ -272,7 +274,7 @@ if (isDevelopment()) {
272
274
  // 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
273
275
  app.all('/_agentuity/*', (c: Context) => c.notFound());
274
276
  app.all('/api/*', (c: Context) => c.notFound());
275
- if (hasWorkbench) {
277
+ if (!hasWorkbench) {
276
278
  app.all('/workbench/*', (c: Context) => c.notFound());
277
279
  }
278
280
 
@@ -457,6 +459,10 @@ app.use('/api/*', createAgentMiddleware(''));
457
459
  // Step 4: Import user's app.ts (runs createApp, gets state/config)
458
460
  await import('../../app.js');
459
461
 
462
+ // Step 4.5: Import agent registry to ensure all agents are registered
463
+ // This is needed for workbench metadata to return JSON schemas
464
+ await import('./registry.js');
465
+
460
466
  // Step 5: Initialize providers
461
467
  const appState = getAppState();
462
468
  const appConfig = getAppConfig();
@@ -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);
@@ -1,5 +1,6 @@
1
1
  import { parse, type GrammarItem } from '../../tsc-output-parser';
2
2
  import { formatTypeScriptErrors, hasErrors } from '../../typescript-errors';
3
+ import type { BuildReportCollector } from '../../build-report';
3
4
 
4
5
  interface TypeError {
5
6
  success: false;
@@ -18,13 +19,20 @@ interface TypeSuccess {
18
19
 
19
20
  type TypeResult = TypeError | TypeSuccess | TypeUnknownError;
20
21
 
22
+ export interface TypecheckOptions {
23
+ /** Optional collector for structured error reporting */
24
+ collector?: BuildReportCollector;
25
+ }
26
+
21
27
  /**
22
28
  * run the typescript compiler and result formatted results
23
29
  *
24
30
  * @param dir the absolute path to the directory containing the project (must have tsconfig.json in this folder)
31
+ * @param options optional configuration including error collector
25
32
  * @returns
26
33
  */
27
- export async function typecheck(dir: string): Promise<TypeResult> {
34
+ export async function typecheck(dir: string, options?: TypecheckOptions): Promise<TypeResult> {
35
+ const { collector } = options ?? {};
28
36
  const result = await Bun.$`bunx tsc --noEmit --skipLibCheck --pretty false`
29
37
  .cwd(dir)
30
38
  .quiet()
@@ -38,6 +46,11 @@ export async function typecheck(dir: string): Promise<TypeResult> {
38
46
  success: true,
39
47
  };
40
48
  } else if (errors && hasErrors(errors)) {
49
+ // Add errors to collector if provided
50
+ if (collector) {
51
+ collector.addTypeScriptErrors(errors);
52
+ }
53
+
41
54
  const formattedErrors = await formatTypeScriptErrors(errors, {
42
55
  projectDir: dir,
43
56
  });
@@ -47,6 +60,11 @@ export async function typecheck(dir: string): Promise<TypeResult> {
47
60
  output: formattedErrors,
48
61
  };
49
62
  } else {
63
+ // Unknown error - add to collector as general error
64
+ if (collector) {
65
+ collector.addGeneralError('typescript', output || result.stderr.toString());
66
+ }
67
+
50
68
  return {
51
69
  success: false,
52
70
  output: output || result.stderr.toString(),
@@ -1,7 +1,7 @@
1
1
  import type { Plugin } from 'vite';
2
2
  import { join } from 'node:path';
3
3
  import { createLogger } from '@agentuity/server';
4
- import type { LogLevel } from '../../../types';
4
+ import type { LogLevel, DeployOptions } from '../../../types';
5
5
  import { discoverAgents, type AgentMetadata } from './agent-discovery';
6
6
  import { discoverRoutes, type RouteMetadata, type RouteInfo } from './route-discovery';
7
7
  import { generateAgentRegistry, generateRouteRegistry } from './registry-generator';
@@ -20,6 +20,7 @@ export interface AgentuityPluginOptions {
20
20
  orgId?: string;
21
21
  deploymentId?: string;
22
22
  logLevel?: LogLevel;
23
+ deploymentOptions?: DeployOptions;
23
24
  }
24
25
 
25
26
  /**
@@ -42,6 +43,7 @@ export function agentuityPlugin(options: AgentuityPluginOptions): Plugin {
42
43
  orgId = '',
43
44
  deploymentId = '',
44
45
  logLevel = 'info',
46
+ deploymentOptions,
45
47
  } = options;
46
48
  const logger = createLogger(logLevel);
47
49
  const srcDir = join(rootDir, 'src');
@@ -153,6 +155,7 @@ export function agentuityPlugin(options: AgentuityPluginOptions): Plugin {
153
155
  routes,
154
156
  dev,
155
157
  logger,
158
+ deploymentOptions,
156
159
  });
157
160
 
158
161
  // Write metadata file