@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.
- package/dist/build-report.d.ts +201 -0
- package/dist/build-report.d.ts.map +1 -0
- package/dist/build-report.js +335 -0
- package/dist/build-report.js.map +1 -0
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +9 -3
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +44 -1
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/typecheck.d.ts +7 -1
- package/dist/cmd/build/typecheck.d.ts.map +1 -1
- package/dist/cmd/build/typecheck.js +11 -1
- package/dist/cmd/build/typecheck.js.map +1 -1
- package/dist/cmd/build/vite/index.d.ts +2 -1
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +2 -1
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +2 -4
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +56 -18
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +14 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite-bundler.d.ts +3 -0
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite-bundler.js +14 -5
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +86 -9
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +0 -1
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +109 -15
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/project/auth/generate.d.ts +5 -0
- package/dist/cmd/project/auth/generate.d.ts.map +1 -0
- package/dist/cmd/project/auth/generate.js +102 -0
- package/dist/cmd/project/auth/generate.js.map +1 -0
- package/dist/cmd/project/auth/index.d.ts +2 -0
- package/dist/cmd/project/auth/index.d.ts.map +1 -0
- package/dist/cmd/project/auth/index.js +21 -0
- package/dist/cmd/project/auth/index.js.map +1 -0
- package/dist/cmd/project/auth/init.d.ts +2 -0
- package/dist/cmd/project/auth/init.d.ts.map +1 -0
- package/dist/cmd/project/auth/init.js +220 -0
- package/dist/cmd/project/auth/init.js.map +1 -0
- package/dist/cmd/project/auth/shared.d.ts +88 -0
- package/dist/cmd/project/auth/shared.d.ts.map +1 -0
- package/dist/cmd/project/auth/shared.js +435 -0
- package/dist/cmd/project/auth/shared.js.map +1 -0
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +9 -1
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +106 -0
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/types.d.ts +1 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/package.json +5 -4
- package/src/build-report.ts +457 -0
- package/src/cmd/build/entry-generator.ts +9 -3
- package/src/cmd/build/index.ts +51 -1
- package/src/cmd/build/typecheck.ts +19 -1
- package/src/cmd/build/vite/index.ts +4 -1
- package/src/cmd/build/vite/metadata-generator.ts +4 -6
- package/src/cmd/build/vite/registry-generator.ts +58 -19
- package/src/cmd/build/vite/vite-builder.ts +18 -1
- package/src/cmd/build/vite-bundler.ts +17 -4
- package/src/cmd/cloud/deploy.ts +105 -11
- package/src/cmd/cloud/deployment/show.ts +0 -1
- package/src/cmd/dev/index.ts +115 -14
- package/src/cmd/project/auth/generate.ts +116 -0
- package/src/cmd/project/auth/index.ts +21 -0
- package/src/cmd/project/auth/init.ts +263 -0
- package/src/cmd/project/auth/shared.ts +534 -0
- package/src/cmd/project/index.ts +9 -1
- package/src/cmd/project/template-flow.ts +125 -0
- 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
|
|
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();
|
package/src/cmd/build/index.ts
CHANGED
|
@@ -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
|
|
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
|