@agentuity/cli 1.0.47 → 2.0.0-beta.0

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 (115) hide show
  1. package/dist/cmd/build/app-router-detector.d.ts +2 -5
  2. package/dist/cmd/build/app-router-detector.d.ts.map +1 -1
  3. package/dist/cmd/build/app-router-detector.js +130 -154
  4. package/dist/cmd/build/app-router-detector.js.map +1 -1
  5. package/dist/cmd/build/ids.d.ts +11 -0
  6. package/dist/cmd/build/ids.d.ts.map +1 -0
  7. package/dist/cmd/build/ids.js +18 -0
  8. package/dist/cmd/build/ids.js.map +1 -0
  9. package/dist/cmd/build/vite/agent-discovery.d.ts +8 -4
  10. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  11. package/dist/cmd/build/vite/agent-discovery.js +166 -487
  12. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  13. package/dist/cmd/build/vite/bun-dev-server.d.ts +10 -16
  14. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  15. package/dist/cmd/build/vite/bun-dev-server.js +67 -134
  16. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  17. package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
  18. package/dist/cmd/build/vite/docs-generator.js +0 -2
  19. package/dist/cmd/build/vite/docs-generator.js.map +1 -1
  20. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  21. package/dist/cmd/build/vite/index.js +0 -36
  22. package/dist/cmd/build/vite/index.js.map +1 -1
  23. package/dist/cmd/build/vite/lifecycle-generator.d.ts +10 -2
  24. package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
  25. package/dist/cmd/build/vite/lifecycle-generator.js +302 -23
  26. package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
  27. package/dist/cmd/build/vite/route-discovery.d.ts +11 -38
  28. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  29. package/dist/cmd/build/vite/route-discovery.js +97 -177
  30. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  31. package/dist/cmd/build/vite/server-bundler.js +1 -1
  32. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  33. package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
  34. package/dist/cmd/build/vite/static-renderer.js +1 -9
  35. package/dist/cmd/build/vite/static-renderer.js.map +1 -1
  36. package/dist/cmd/build/vite/vite-asset-server-config.d.ts +6 -3
  37. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  38. package/dist/cmd/build/vite/vite-asset-server-config.js +171 -18
  39. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  40. package/dist/cmd/build/vite/vite-asset-server.d.ts +8 -3
  41. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  42. package/dist/cmd/build/vite/vite-asset-server.js +14 -13
  43. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  44. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  45. package/dist/cmd/build/vite/vite-builder.js +6 -34
  46. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  47. package/dist/cmd/build/vite/ws-proxy.d.ts +53 -0
  48. package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -0
  49. package/dist/cmd/build/vite/ws-proxy.js +95 -0
  50. package/dist/cmd/build/vite/ws-proxy.js.map +1 -0
  51. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  52. package/dist/cmd/build/vite-bundler.js +0 -3
  53. package/dist/cmd/build/vite-bundler.js.map +1 -1
  54. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  55. package/dist/cmd/dev/file-watcher.js +2 -8
  56. package/dist/cmd/dev/file-watcher.js.map +1 -1
  57. package/dist/cmd/dev/index.d.ts.map +1 -1
  58. package/dist/cmd/dev/index.js +369 -720
  59. package/dist/cmd/dev/index.js.map +1 -1
  60. package/package.json +6 -8
  61. package/src/cmd/ai/prompt/agent.md +0 -1
  62. package/src/cmd/ai/prompt/api.md +0 -7
  63. package/src/cmd/ai/prompt/web.md +51 -213
  64. package/src/cmd/build/app-router-detector.ts +152 -182
  65. package/src/cmd/build/ids.ts +19 -0
  66. package/src/cmd/build/vite/agent-discovery.ts +208 -679
  67. package/src/cmd/build/vite/bun-dev-server.ts +78 -154
  68. package/src/cmd/build/vite/docs-generator.ts +0 -2
  69. package/src/cmd/build/vite/index.ts +1 -42
  70. package/src/cmd/build/vite/lifecycle-generator.ts +345 -21
  71. package/src/cmd/build/vite/route-discovery.ts +116 -274
  72. package/src/cmd/build/vite/server-bundler.ts +1 -1
  73. package/src/cmd/build/vite/static-renderer.ts +1 -11
  74. package/src/cmd/build/vite/vite-asset-server-config.ts +196 -20
  75. package/src/cmd/build/vite/vite-asset-server.ts +25 -15
  76. package/src/cmd/build/vite/vite-builder.ts +6 -51
  77. package/src/cmd/build/vite/ws-proxy.ts +126 -0
  78. package/src/cmd/build/vite-bundler.ts +0 -4
  79. package/src/cmd/dev/file-watcher.ts +2 -9
  80. package/src/cmd/dev/index.ts +409 -832
  81. package/dist/cmd/build/ast.d.ts +0 -78
  82. package/dist/cmd/build/ast.d.ts.map +0 -1
  83. package/dist/cmd/build/ast.js +0 -2703
  84. package/dist/cmd/build/ast.js.map +0 -1
  85. package/dist/cmd/build/entry-generator.d.ts +0 -25
  86. package/dist/cmd/build/entry-generator.d.ts.map +0 -1
  87. package/dist/cmd/build/entry-generator.js +0 -695
  88. package/dist/cmd/build/entry-generator.js.map +0 -1
  89. package/dist/cmd/build/vite/api-mount-path.d.ts +0 -61
  90. package/dist/cmd/build/vite/api-mount-path.d.ts.map +0 -1
  91. package/dist/cmd/build/vite/api-mount-path.js +0 -83
  92. package/dist/cmd/build/vite/api-mount-path.js.map +0 -1
  93. package/dist/cmd/build/vite/registry-generator.d.ts +0 -19
  94. package/dist/cmd/build/vite/registry-generator.d.ts.map +0 -1
  95. package/dist/cmd/build/vite/registry-generator.js +0 -1108
  96. package/dist/cmd/build/vite/registry-generator.js.map +0 -1
  97. package/dist/cmd/build/webanalytics-generator.d.ts +0 -16
  98. package/dist/cmd/build/webanalytics-generator.d.ts.map +0 -1
  99. package/dist/cmd/build/webanalytics-generator.js +0 -178
  100. package/dist/cmd/build/webanalytics-generator.js.map +0 -1
  101. package/dist/cmd/build/workbench.d.ts +0 -7
  102. package/dist/cmd/build/workbench.d.ts.map +0 -1
  103. package/dist/cmd/build/workbench.js +0 -55
  104. package/dist/cmd/build/workbench.js.map +0 -1
  105. package/dist/utils/route-migration.d.ts +0 -62
  106. package/dist/utils/route-migration.d.ts.map +0 -1
  107. package/dist/utils/route-migration.js +0 -630
  108. package/dist/utils/route-migration.js.map +0 -1
  109. package/src/cmd/build/ast.ts +0 -3529
  110. package/src/cmd/build/entry-generator.ts +0 -760
  111. package/src/cmd/build/vite/api-mount-path.ts +0 -87
  112. package/src/cmd/build/vite/registry-generator.ts +0 -1267
  113. package/src/cmd/build/webanalytics-generator.ts +0 -197
  114. package/src/cmd/build/workbench.ts +0 -58
  115. package/src/utils/route-migration.ts +0 -757
@@ -1,695 +0,0 @@
1
- /**
2
- * Vite-native entry file generator (v2 - clean architecture)
3
- * Single source for both dev and prod with minimal differences
4
- */
5
- import { join } from 'node:path';
6
- import { discoverRoutes } from './vite/route-discovery';
7
- import { generateWebAnalyticsFile } from './webanalytics-generator';
8
- import { computeApiMountPath } from './vite/api-mount-path';
9
- /**
10
- * Generate entry file with clean Vite-native architecture
11
- */
12
- export async function generateEntryFile(options) {
13
- const { rootDir, projectId, deploymentId, logger, mode, workbench, analytics, vitePort, noBundle, preDiscoveredRoutes, } = options;
14
- const srcDir = join(rootDir, 'src');
15
- const generatedDir = join(srcDir, 'generated');
16
- const entryPath = join(generatedDir, 'app.ts');
17
- logger.trace(`Generating unified entry file (supports both dev and prod modes)...`);
18
- // Check if analytics is enabled
19
- const analyticsEnabled = analytics !== false;
20
- // Generate web analytics files only if enabled
21
- if (analyticsEnabled) {
22
- await generateWebAnalyticsFile({ rootDir, logger, analytics });
23
- }
24
- // Use pre-discovered routes if available, otherwise discover them
25
- const routeInfoList = preDiscoveredRoutes ??
26
- (await discoverRoutes(srcDir, projectId, deploymentId, logger)).routeInfoList;
27
- // Check for web and workbench
28
- const hasWebFrontend = (await Bun.file(join(srcDir, 'web', 'index.html')).exists()) ||
29
- (await Bun.file(join(srcDir, 'web', 'frontend.tsx')).exists());
30
- // Workbench is configured at build time, but only enabled at runtime in dev mode
31
- const hasWorkbenchConfig = !!workbench;
32
- // Get unique route files that need to be imported (relative to src/)
33
- const routeFiles = new Set();
34
- for (const route of routeInfoList) {
35
- if (route.filename) {
36
- routeFiles.add(route.filename);
37
- }
38
- }
39
- // Generate imports
40
- const runtimeImports = [
41
- ` createRouter,`,
42
- ` createBaseMiddleware,`,
43
- ` createCorsMiddleware,`,
44
- ` createOtelMiddleware,`,
45
- ` createAgentMiddleware,`,
46
- ` createCompressionMiddleware,`,
47
- ` getAppState,`,
48
- ` getAppConfig,`,
49
- ` getUserRouter,`,
50
- ` register,`,
51
- ` getSpanProcessors,`,
52
- ` createServices,`,
53
- ` runAgentSetups,`,
54
- ` getThreadProvider,`,
55
- ` getSessionProvider,`,
56
- ` setGlobalLogger,`,
57
- ` setGlobalTracer,`,
58
- ` setGlobalRouter,`,
59
- ` enableProcessExitProtection,`,
60
- ` hasWaitUntilPending,`,
61
- ` loadBuildMetadata,`,
62
- ` createWorkbenchRouter,`,
63
- ` bootstrapRuntimeEnv,`,
64
- ` patchBunS3ForStorageDev,`,
65
- ` runShutdown,`,
66
- ];
67
- if (noBundle) {
68
- runtimeImports.push(` applyDevPatches,`);
69
- }
70
- if (hasWebFrontend) {
71
- runtimeImports.push(` mimeTypes,`);
72
- }
73
- const imports = [
74
- `import { `,
75
- ...runtimeImports,
76
- `} from '@agentuity/runtime';`,
77
- `import type { Context } from 'hono';`,
78
- `import { websocket${hasWebFrontend ? ', serveStatic' : ''} } from 'hono/bun';`,
79
- hasWebFrontend ? `import { readFileSync, existsSync } from 'node:fs';` : '',
80
- ].filter(Boolean);
81
- imports.push(`import { type LogLevel } from '@agentuity/core';`);
82
- if (analyticsEnabled) {
83
- imports.push(`import { injectAnalytics, registerAnalyticsRoutes } from './webanalytics.js';`);
84
- imports.push(`import { analyticsConfig } from './analytics-config.js';`);
85
- }
86
- // Generate route mounting code for all discovered routes
87
- // Sort route files for deterministic output
88
- const sortedRouteFiles = [...routeFiles].sort();
89
- const routeImportsAndMounts = [];
90
- let routeIndex = 0;
91
- for (const routeFile of sortedRouteFiles) {
92
- // Normalize path separators for cross-platform compatibility (Windows uses backslashes)
93
- const normalizedRouteFile = routeFile.replace(/\\/g, '/');
94
- // Convert src/api/auth/route.ts -> auth/route
95
- const relativePath = normalizedRouteFile.replace(/^src\/api\//, '').replace(/\.tsx?$/, '');
96
- // Determine the mount path using the shared helper
97
- // This ensures consistency with route type generation in ast.ts
98
- const mountPath = computeApiMountPath(relativePath);
99
- const importName = `router_${routeIndex++}`;
100
- routeImportsAndMounts.push(`const { default: ${importName} } = await import('../api/${relativePath}.js');`);
101
- routeImportsAndMounts.push(`app.route('${mountPath}', ${importName});`);
102
- }
103
- const apiMount = routeImportsAndMounts.length > 0
104
- ? `
105
- // Apply middleware and mount API routes
106
- // If user passed router(s) via createApp({ router }), mount those instead of discovered files
107
- const __userMounts = getUserRouter();
108
- if (__userMounts) {
109
- for (const mount of __userMounts) {
110
- // Apply Agentuity middleware (CORS, OTel, agent context) to each user-provided prefix
111
- const prefix = mount.path.endsWith('/') ? mount.path + '*' : mount.path + '/*';
112
- app.use(prefix, createCorsMiddleware());
113
- app.use(prefix, createOtelMiddleware());
114
- app.use(prefix, createAgentMiddleware(''));
115
- app.route(mount.path, mount.router);
116
- }
117
- } else {
118
- // File-based routing: apply middleware to /api/* and mount discovered route files
119
- app.use('/api/*', createCorsMiddleware());
120
- app.use('/api/*', createOtelMiddleware());
121
- app.use('/api/*', createAgentMiddleware(''));
122
- ${routeImportsAndMounts.map((line) => `\t${line}`).join('\n')}
123
- }
124
- `
125
- : '';
126
- // Workbench API routes mounting
127
- // Always mounted - these routes are needed for the cloud workbench to communicate with deployed agents
128
- // Auth is handled by middleware inside the router (signature verification in production, no auth in development)
129
- // The hasWorkbenchConfig flag only controls whether the local workbench UI is served
130
- const workbenchApiMount = `
131
- // Mount workbench API routes (/_agentuity/workbench/*)
132
- // Always available for cloud workbench communication
133
- // Auth is handled inside the router (signature verification in production)
134
- const workbenchRouter = createWorkbenchRouter();
135
- app.route('/', workbenchRouter);
136
-
137
- // hasWorkbenchConfig controls whether the local workbench UI is served (dev mode only)
138
- const hasWorkbenchConfig = ${hasWorkbenchConfig};
139
- `;
140
- // Asset proxy routes - Always generated, but only active at runtime when:
141
- // - NODE_ENV !== 'production' (isDevelopment())
142
- // - and process.env.VITE_PORT is set
143
- const assetProxyRoutes = `
144
- // Asset proxy routes - Development mode only (proxies to Vite asset server)
145
- if (isDevelopment() && process.env.VITE_PORT) {
146
- const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT, 10);
147
-
148
- const proxyToVite = async (c: Context, pathOverride?: string) => {
149
- const targetPath = pathOverride ?? c.req.path;
150
- const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${targetPath}\`;
151
- try {
152
- otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\${targetPath}\`);
153
- const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
154
- otel.logger.debug(\`[Proxy] \${c.req.path} -> \${res.status} (\${res.headers.get('content-type')})\`);
155
- return new Response(res.body, {
156
- status: res.status,
157
- headers: res.headers,
158
- });
159
- } catch (err) {
160
- if (err instanceof Error && err.name === 'TimeoutError') {
161
- otel.logger.error(\`Vite proxy timeout: \${c.req.path}\`);
162
- return c.text('Vite asset server timeout', 504);
163
- }
164
- otel.logger.error(\`Failed to proxy to Vite: \${c.req.path} - \${err instanceof Error ? err.message : String(err)}\`);
165
- return c.text('Vite asset server error', 500);
166
- }
167
- };
168
-
169
- // HMR WebSocket proxy - enables hot reload through tunnels (*.agentuity.live)
170
- // This proxies the Vite HMR WebSocket connection from the Bun server to Vite
171
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
- const viteHmrWebsocket = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__ = {
173
- // Map of client WebSocket -> Vite WebSocket
174
- connections: new Map<WebSocket, WebSocket>(),
175
-
176
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
- open(clientWs: any) {
178
- // Get the query string from ws.data (set during upgrade)
179
- const queryString = clientWs.data?.queryString || '';
180
- const viteWsUrl = \`ws://127.0.0.1:\${VITE_ASSET_PORT}/__vite_hmr\${queryString}\`;
181
- otel.logger.debug('[HMR Proxy] Client connected, opening connection to Vite at %s', viteWsUrl);
182
-
183
- // Connect to Vite with the 'vite-hmr' subprotocol (required by Vite)
184
- const viteWs = new WebSocket(viteWsUrl, ['vite-hmr']);
185
-
186
- viteWs.onopen = () => {
187
- otel.logger.debug('[HMR Proxy] Connected to Vite HMR server');
188
- };
189
-
190
- viteWs.onmessage = (event) => {
191
- // Forward messages from Vite to client
192
- if (clientWs.readyState === WebSocket.OPEN) {
193
- clientWs.send(event.data);
194
- }
195
- };
196
-
197
- viteWs.onerror = (error) => {
198
- otel.logger.error('[HMR Proxy] Vite WebSocket error: %s', error);
199
- };
200
-
201
- viteWs.onclose = () => {
202
- otel.logger.debug('[HMR Proxy] Vite WebSocket closed');
203
- viteHmrWebsocket.connections.delete(clientWs);
204
- if (clientWs.readyState === WebSocket.OPEN) {
205
- clientWs.close();
206
- }
207
- };
208
-
209
- viteHmrWebsocket.connections.set(clientWs, viteWs);
210
- },
211
-
212
- message(clientWs: WebSocket, message: string | Buffer) {
213
- // Forward messages from client to Vite
214
- const viteWs = viteHmrWebsocket.connections.get(clientWs);
215
- if (viteWs && viteWs.readyState === WebSocket.OPEN) {
216
- viteWs.send(message);
217
- }
218
- },
219
-
220
- close(clientWs: WebSocket) {
221
- otel.logger.debug('[HMR Proxy] Client WebSocket closed');
222
- const viteWs = viteHmrWebsocket.connections.get(clientWs);
223
- if (viteWs) {
224
- viteWs.close();
225
- viteHmrWebsocket.connections.delete(clientWs);
226
- }
227
- },
228
- };
229
-
230
- // Register HMR WebSocket route - must be before other routes
231
- app.get('/__vite_hmr', (c: Context) => {
232
- const upgradeHeader = c.req.header('upgrade');
233
- if (upgradeHeader?.toLowerCase() === 'websocket') {
234
- // Get the Bun server from context using Hono's pattern
235
- // When app.fetch(req, server) is called, Hono stores server as c.env
236
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
237
- const server = 'server' in (c.env as any) ? (c.env as any).server : c.env;
238
-
239
- if (server?.upgrade) {
240
- // Extract query string to forward to Vite (includes token parameter)
241
- const url = new URL(c.req.url);
242
- const queryString = url.search; // Includes the '?' prefix
243
-
244
- const success = server.upgrade(c.req.raw, {
245
- data: { type: 'vite-hmr', queryString },
246
- });
247
- if (success) {
248
- otel.logger.debug('[HMR Proxy] WebSocket upgrade successful');
249
- return new Response(null);
250
- }
251
- otel.logger.error('[HMR Proxy] WebSocket upgrade returned false');
252
- } else {
253
- otel.logger.error('[HMR Proxy] Server upgrade method not available. c.env type: %s, keys: %s',
254
- typeof c.env,
255
- Object.keys(c.env || {}).join(', '));
256
- }
257
- return c.text('WebSocket upgrade failed', 500);
258
- }
259
- // Non-WebSocket request to HMR endpoint - proxy to Vite
260
- return proxyToVite(c);
261
- });
262
-
263
- // Vite client scripts and HMR
264
- app.get('/@vite/*', (c: Context) => proxyToVite(c));
265
- app.get('/@react-refresh', (c: Context) => proxyToVite(c));
266
-
267
- // Source files for HMR
268
- app.get('/src/web/*', (c: Context) => proxyToVite(c));
269
- app.get('/src/*', (c: Context) => proxyToVite(c)); // Catch-all for other source files
270
-
271
- // Workbench source files (in .agentuity/workbench-src/)
272
- app.get('/.agentuity/workbench-src/*', (c: Context) => proxyToVite(c));
273
-
274
- // Node modules (Vite transforms these)
275
- app.get('/node_modules/*', (c: Context) => proxyToVite(c));
276
-
277
- // Scoped packages (e.g., @agentuity/*, @types/*)
278
- app.get('/@*', (c: Context) => proxyToVite(c));
279
-
280
- // File system access (for Vite's @fs protocol)
281
- app.get('/@fs/*', (c: Context) => proxyToVite(c));
282
-
283
- // Module resolution (for Vite's @id protocol)
284
- app.get('/@id/*', (c: Context) => proxyToVite(c));
285
-
286
- // Static assets - Vite serves src/web/public/* at root, but code uses /public/* paths
287
- // In production, the plugin transforms /public/foo.svg to CDN URLs
288
- // Rewrite /public/foo.svg -> /foo.svg before proxying to Vite
289
- app.get('/public/*', (c: Context) => {
290
- const rootPath = c.req.path.replace(/^\\/public/, '');
291
- return proxyToVite(c, rootPath);
292
- });
293
-
294
- // Any .js, .jsx, .ts, .tsx files (catch remaining modules)
295
- app.get('/*.js', (c: Context) => proxyToVite(c));
296
- app.get('/*.jsx', (c: Context) => proxyToVite(c));
297
- app.get('/*.ts', (c: Context) => proxyToVite(c));
298
- app.get('/*.tsx', (c: Context) => proxyToVite(c));
299
- app.get('/*.css', (c: Context) => proxyToVite(c));
300
- }
301
- `;
302
- // Runtime mode detection helper (defined at top level for reuse)
303
- // Dynamic property access prevents Bun.build from inlining NODE_ENV at build time
304
- const modeDetection = `
305
- // Runtime mode detection helper
306
- // Dynamic string concatenation prevents Bun.build from inlining NODE_ENV at build time
307
- // See: https://github.com/oven-sh/bun/issues/20183
308
- const getEnv = (key: string) => process.env[key];
309
- const isDevelopment = () => getEnv('NODE' + '_' + 'ENV') !== 'production';
310
- `;
311
- // Web routes (runtime mode detection)
312
- let webRoutes = '';
313
- if (hasWebFrontend) {
314
- webRoutes = `
315
- // Web routes - Runtime mode detection (dev proxies to Vite, prod serves static)
316
- // Note: Session/thread cookies are set by /_agentuity/webanalytics/session.js (loaded via script tag)
317
- // This keeps the HTML response static and cacheable
318
-
319
- if (isDevelopment()) {
320
- // Development mode: Proxy HTML from Vite to enable React Fast Refresh
321
- const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT || '${vitePort || 5173}', 10);
322
-
323
- const devHtmlHandler = async (c: Context) => {
324
- const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}/src/web/index.html\`;
325
-
326
- try {
327
- otel.logger.debug('[Proxy] GET /src/web/index.html -> Vite:%d', VITE_ASSET_PORT);
328
- const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
329
-
330
- // Get HTML text and transform relative paths to absolute
331
- let html = await res.text();
332
- html = html
333
- .replace(/src="\\.\\//g, 'src="/src/web/')
334
- .replace(/href="\\.\\//g, 'href="/src/web/');
335
-
336
- ${analyticsEnabled
337
- ? ` // Inject analytics config and script (session/thread read from cookies by beacon)
338
- html = injectAnalytics(html, analyticsConfig);`
339
- : ''}
340
-
341
- return new Response(html, {
342
- status: res.status,
343
- headers: res.headers,
344
- });
345
- } catch (err) {
346
- otel.logger.error('Failed to proxy HTML to Vite: %s', err instanceof Error ? err.message : String(err));
347
- return c.text('Vite asset server error (HTML)', 500);
348
- }
349
- };
350
-
351
- app.get('/', devHtmlHandler);
352
-
353
- // 404 for unmatched API/system routes
354
- app.all('/_agentuity/*', (c: Context) => c.notFound());
355
- app.all('/api/*', (c: Context) => c.notFound());
356
- if (!(hasWorkbenchConfig && isDevelopment())) {
357
- app.all('/workbench/*', (c: Context) => c.notFound());
358
- }
359
-
360
- // SPA fallback - serve index.html for client-side routing
361
- app.get('*', async (c: Context) => {
362
- const path = c.req.path;
363
- // If path has a file extension, try proxying to Vite first (serves public files like robots.txt, llms.txt)
364
- // Fall back to 404 if Vite also returns 404
365
- if (/\\.[a-zA-Z0-9]+$/.test(path)) {
366
- try {
367
- const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${path}\`;
368
- const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
369
- if (res.status !== 404) {
370
- return new Response(res.body, { status: res.status, headers: res.headers });
371
- }
372
- } catch {
373
- // Vite unavailable, fall through to 404
374
- }
375
- return c.notFound();
376
- }
377
- return devHtmlHandler(c);
378
- });
379
- } else {
380
- // Production mode: Serve static files from bundled output
381
- const indexHtmlPath = import.meta.dir + '/client/index.html';
382
- const baseIndexHtml = existsSync(indexHtmlPath)
383
- ? readFileSync(indexHtmlPath, 'utf-8')
384
- : '';
385
-
386
- if (!baseIndexHtml) {
387
- otel.logger.warn('Production HTML not found at %s', indexHtmlPath);
388
- }
389
-
390
- const prodHtmlHandler = (c: Context) => {
391
- if (!baseIndexHtml) {
392
- return c.text('Production build incomplete', 500);
393
- }
394
- ${analyticsEnabled
395
- ? ` // Inject analytics config and script (session/thread loaded via session.js)
396
- const html = injectAnalytics(baseIndexHtml, analyticsConfig);
397
- return c.html(html);`
398
- : ` return c.html(baseIndexHtml);`}
399
- };
400
-
401
- app.get('/', prodHtmlHandler);
402
-
403
- // Serve static assets from /assets/* (Vite bundled output)
404
- app.use('/assets/*', serveStatic({ root: import.meta.dir + '/client', mimes: mimeTypes }));
405
-
406
- // Serve static public assets (favicon.ico, robots.txt, etc.)
407
- app.use('/*', serveStatic({ root: import.meta.dir + '/client', rewriteRequestPath: (path) => path, mimes: mimeTypes }));
408
-
409
- // 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
410
- app.all('/_agentuity/*', (c: Context) => c.notFound());
411
- app.all('/api/*', (c: Context) => c.notFound());
412
- if (!(hasWorkbenchConfig && isDevelopment())) {
413
- app.all('/workbench/*', (c: Context) => c.notFound());
414
- }
415
-
416
- // SPA fallback with asset protection
417
- app.get('*', (c: Context) => {
418
- const path = c.req.path;
419
- // If path has a file extension, it's likely an asset request - return 404
420
- if (/\\.[a-zA-Z0-9]+$/.test(path)) {
421
- return c.notFound();
422
- }
423
- return prodHtmlHandler(c);
424
- });
425
- }
426
- `;
427
- }
428
- // Workbench UI routes (development only)
429
- // The workbench UI is only served in development mode; the API routes are always available
430
- const workbenchRoute = workbench?.route ?? '/workbench';
431
- const workbenchRoutes = `
432
- // Workbench UI is only available in development mode (API routes are always available)
433
- if (hasWorkbenchConfig && isDevelopment()) {
434
- const workbenchSrcDir = import.meta.dir + '/workbench-src';
435
- const workbenchIndexPath = import.meta.dir + '/workbench-src/index.html';
436
- app.get('${workbenchRoute}', async (c: Context) => {
437
- const html = await Bun.file(workbenchIndexPath).text();
438
- // Rewrite script/css paths to use Vite's @fs protocol
439
- const withVite = html
440
- .replace('src="./main.tsx"', \`src="/@fs\${workbenchSrcDir}/main.tsx"\`)
441
- .replace('href="./styles.css"', \`href="/@fs\${workbenchSrcDir}/styles.css"\`);
442
- return c.html(withVite);
443
- });
444
- }
445
- `;
446
- // Server startup (same for dev and prod - Bun.serve with native WebSocket)
447
- const serverStartup = `
448
- // Start Bun server
449
- if (typeof Bun !== 'undefined') {
450
- // Enable process exit protection now that we're starting the server
451
- enableProcessExitProtection();
452
-
453
- const port = parseInt(process.env.PORT || '3500', 10);
454
-
455
- // Create custom WebSocket handler that supports both regular WebSockets and HMR proxy
456
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
457
- const hmrHandler = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__;
458
- const customWebsocket = {
459
- ...websocket,
460
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
461
- open(ws: any) {
462
- // Check if this is an HMR connection
463
- if (ws.data?.type === 'vite-hmr' && hmrHandler) {
464
- hmrHandler.open(ws);
465
- } else if (websocket.open) {
466
- websocket.open(ws);
467
- }
468
- },
469
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
470
- message(ws: any, message: string | Buffer) {
471
- // Check if this is an HMR connection
472
- if (ws.data?.type === 'vite-hmr' && hmrHandler) {
473
- hmrHandler.message(ws, message);
474
- } else if (websocket.message) {
475
- websocket.message(ws, message);
476
- }
477
- },
478
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
479
- close(ws: any, code?: number, reason?: string) {
480
- // Check if this is an HMR connection
481
- if (ws.data?.type === 'vite-hmr' && hmrHandler) {
482
- hmrHandler.close(ws);
483
- } else if (websocket.close) {
484
- websocket.close(ws, code, reason);
485
- }
486
- },
487
- };
488
-
489
- const server = Bun.serve({
490
- fetch: (req, server) => {
491
- // Get timeout from config on each request (0 = no timeout)
492
- server.timeout(req, getAppConfig()?.requestTimeout ?? 0);
493
- return app.fetch(req, server);
494
- },
495
- websocket: customWebsocket,
496
- port,
497
- hostname: '127.0.0.1',
498
- development: isDevelopment(),
499
- });
500
-
501
- // Make server available globally for health checks
502
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
503
- (globalThis as any).__AGENTUITY_SERVER__ = server;
504
-
505
- otel.logger.info(\`Server listening on http://127.0.0.1:\${port}\`);
506
- if (isDevelopment() && process.env.VITE_PORT) {
507
- otel.logger.debug(\`Proxying Vite assets from port \${process.env.VITE_PORT}\`);
508
- }
509
-
510
- // Register signal handlers for graceful shutdown (production only)
511
- // Dev mode has its own handlers in devmode.ts
512
- if (!isDevelopment()) {
513
- const handleShutdown = async (signal: string) => {
514
- otel.logger.info(\`Received \${signal}, initiating graceful shutdown...\`);
515
- try {
516
- await runShutdown();
517
- otel.logger.info('Shutdown complete');
518
- } catch (err) {
519
- otel.logger.error(\`Error during shutdown: \${err instanceof Error ? err.message : String(err)}\`);
520
- }
521
- process.exit(0);
522
- };
523
-
524
- process.once('SIGTERM', () => handleShutdown('SIGTERM'));
525
- process.once('SIGINT', () => handleShutdown('SIGINT'));
526
- }
527
- }
528
-
529
- // FOUND AN ERROR IN THIS FILE?
530
- // Please file an issue at https://github.com/agentuity/sdk/issues
531
- // or if you know the fix please submit a PR!
532
- `;
533
- const healthRoutes = `
534
- // Health check routes (production only)
535
- if (!isDevelopment()) {
536
- const healthHandler = (c: Context) => {
537
- return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
538
- };
539
- const idleHandler = (c: Context) => {
540
- // Check if server is idle (no pending requests/connections)
541
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
542
- const server = (globalThis as any).__AGENTUITY_SERVER__;
543
- if (!server) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
544
-
545
- // Check for pending background tasks
546
- if (hasWaitUntilPending()) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
547
-
548
- if (server.pendingRequests > 1) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
549
- if (server.pendingWebSockets > 0) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
550
-
551
- return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
552
- };
553
- app.get('/_agentuity/health', healthHandler);
554
- app.get('/_health', healthHandler);
555
- app.get('/_agentuity/idle', idleHandler);
556
- app.get('/_idle', idleHandler);
557
- }
558
-
559
- // Dev readiness check - verifies Vite asset server is ready to serve frontend
560
- if (isDevelopment()) {
561
- app.get('/_agentuity/ready', async (c: Context) => {
562
- const vitePort = process.env.VITE_PORT;
563
- if (!vitePort) {
564
- // No Vite port means we're not using Vite proxy
565
- return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
566
- }
567
-
568
- try {
569
- // Probe Vite to check if it can serve the main entry point
570
- // Use @vite/client as a lightweight check - it's always available
571
- const viteUrl = \`http://127.0.0.1:\${vitePort}/@vite/client\`;
572
- const res = await fetch(viteUrl, {
573
- signal: AbortSignal.timeout(5000),
574
- method: 'HEAD'
575
- });
576
-
577
- if (res.ok) {
578
- return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
579
- }
580
- return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
581
- } catch (err) {
582
- otel.logger.debug('Vite readiness check failed: %s', err instanceof Error ? err.message : String(err));
583
- return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
584
- }
585
- });
586
- }
587
- `;
588
- const devPatchesStep = noBundle
589
- ? `
590
- // Step 0.1: Apply runtime dev patches (--experimental-no-bundle mode)
591
- // Replaces build-time Bun.build patches for LLM gateway routing, AI SDK telemetry, and OTel spans
592
- if (isDevelopment()) {
593
- await applyDevPatches();
594
- }
595
- `
596
- : '';
597
- const code = `// @generated
598
- // Auto-generated by Agentuity
599
- // DO NOT EDIT - This file is regenerated on every build
600
- // Supports both development and production modes via runtime detection
601
- ${imports.join('\n')}
602
-
603
- ${modeDetection}
604
-
605
- // Step 0: Bootstrap runtime environment (load profile-specific .env files)
606
- // Only in development - production env vars are injected by platform
607
- // This must happen BEFORE any imports that depend on environment variables
608
- if (isDevelopment()) {
609
- // Pass project directory (two levels up from src/generated/) so .env files are loaded correctly
610
- await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/../..' });
611
- }
612
-
613
- ${devPatchesStep}
614
-
615
- // Step 0.25: load our runtime metadata and cache it
616
- loadBuildMetadata();
617
-
618
- // Step 0.5: Patch Bun's S3 client for Agentuity storage endpoints
619
- // Agentuity storage uses virtual-hosted-style URLs (*.storage.dev)
620
- // This patches s3.file() to automatically set virtualHostedStyle: true
621
- patchBunS3ForStorageDev();
622
-
623
- // Step 1: Initialize telemetry and services
624
- const serverUrl = \`http://127.0.0.1:\${process.env.PORT || '3500'}\`;
625
- const otel = register({ processors: getSpanProcessors(), logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });
626
-
627
- // Step 2: Create router and set as global
628
- const app = createRouter();
629
- setGlobalRouter(app);
630
-
631
- // Step 3: Apply middleware in correct order (BEFORE mounting routes)
632
- // Compression runs first (outermost) so it can compress the final response
633
- app.use('*', createCompressionMiddleware());
634
-
635
- app.use('*', createBaseMiddleware({
636
- logger: otel.logger,
637
- tracer: otel.tracer,
638
- meter: otel.meter,
639
- }));
640
-
641
- // Workbench routes always get OTel middleware for session tracking
642
- app.use('/_agentuity/workbench/*', createOtelMiddleware());
643
-
644
- // Note: /api/* middleware (CORS, OTel, agent context) is applied in Step 6
645
- // after app.ts import, so user-provided routers can specify custom prefixes.
646
-
647
- // Step 4: Import user's app.ts (runs createApp, gets state/config)
648
- await import('../../app.js');
649
-
650
- // Step 4.5: Import agent registry to ensure all agents are registered
651
- // This is needed for workbench metadata to return JSON schemas
652
- await import('./registry.js');
653
-
654
- // Step 5: Initialize providers
655
- const appState = getAppState();
656
- const appConfig = getAppConfig();
657
-
658
- createServices(otel.logger, appConfig, serverUrl);
659
-
660
- // Make logger and tracer globally available for user's app.ts
661
- setGlobalLogger(otel.logger);
662
- setGlobalTracer(otel.tracer);
663
-
664
- const threadProvider = getThreadProvider();
665
- const sessionProvider = getSessionProvider();
666
-
667
- await threadProvider.initialize(appState);
668
- await sessionProvider.initialize(appState);
669
-
670
- // Step 6: Mount routes (AFTER middleware is applied)
671
-
672
- ${healthRoutes}
673
-
674
- ${analyticsEnabled
675
- ? `// Register analytics routes
676
- registerAnalyticsRoutes(app);`
677
- : ''}
678
-
679
- ${assetProxyRoutes}
680
- ${apiMount}
681
- ${workbenchApiMount}
682
- ${workbenchRoutes}
683
- ${webRoutes}
684
-
685
- // Step 7: Run agent setup to signal completion
686
- await runAgentSetups(appState);
687
-
688
- ${serverStartup}
689
- `;
690
- // Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
691
- const cleanedCode = code.replace(/\n{3,}/g, '\n\n');
692
- await Bun.write(entryPath, cleanedCode);
693
- logger.trace(`Generated unified entry file at %s (mode: ${mode})`, entryPath);
694
- }
695
- //# sourceMappingURL=entry-generator.js.map