@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,20 +1,163 @@
1
1
  /**
2
- * Vite Asset Server Configuration
2
+ * Vite Dev Server Configuration
3
3
  *
4
- * Minimal Vite config for serving frontend assets with HMR only.
5
- * Does NOT handle API routes, workbench, or WebSocket - that's the Bun server's job.
4
+ * Vite is the primary dev server — serves frontend assets natively and proxies
5
+ * API/WebSocket requests to the Bun backend server.
6
6
  */
7
7
 
8
8
  import { join } from 'node:path';
9
+ import { existsSync } from 'node:fs';
9
10
  import { createRequire } from 'node:module';
10
- import type { InlineConfig } from 'vite';
11
+ import type { InlineConfig, Plugin } from 'vite';
11
12
  import type { Logger } from '../../../types';
12
13
 
13
14
  export interface GenerateAssetServerConfigOptions {
14
15
  rootDir: string;
15
16
  logger: Logger;
16
17
  workbenchPath?: string;
17
- port: number; // The port Vite will run on (for HMR client configuration)
18
+ port: number; // The port Vite will listen on (user-facing)
19
+ backendPort: number; // The port Bun backend is running on (internal)
20
+ /** User-defined route mount paths from createApp({ router }) (e.g., ['/api', '/v1']) */
21
+ routePaths?: string[];
22
+ }
23
+
24
+ /**
25
+ * Vite plugin that injects analytics scripts in dev mode.
26
+ *
27
+ * In production the beacon plugin handles this at build time. In dev mode
28
+ * the analytics config + session + beacon scripts are served by the Bun
29
+ * backend at /_agentuity/webanalytics/* routes, but we need to inject the
30
+ * `<script>` tags into the HTML so the browser loads them.
31
+ */
32
+ function devAnalyticsPlugin(): Plugin {
33
+ return {
34
+ name: 'agentuity:dev-analytics',
35
+ transformIndexHtml: {
36
+ order: 'pre',
37
+ handler(html) {
38
+ // Default analytics config — matches resolveAnalyticsConfig(undefined) in runtime
39
+ const config = {
40
+ enabled: true,
41
+ trackClicks: true,
42
+ trackScroll: true,
43
+ trackOutboundLinks: true,
44
+ trackForms: false,
45
+ trackWebVitals: true,
46
+ trackErrors: true,
47
+ trackSPANavigation: true,
48
+ isDevmode: true,
49
+ };
50
+
51
+ const injection =
52
+ `<script>window.__AGENTUITY_ANALYTICS__=${JSON.stringify(config)};</script>` +
53
+ '<script src="/_agentuity/webanalytics/session.js" async></script>' +
54
+ '<script src="/_agentuity/webanalytics/analytics.js"></script>';
55
+
56
+ if (html.includes('</head>')) {
57
+ return html.replace('</head>', `${injection}</head>`);
58
+ }
59
+ return html;
60
+ },
61
+ },
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Vite plugin that serves src/web/index.html as the SPA fallback.
67
+ *
68
+ * Vite's built-in SPA fallback only serves index.html from the project root.
69
+ * Since Agentuity apps keep their HTML entry at src/web/index.html, we need
70
+ * this plugin to rewrite the URL so Vite's built-in transform pipeline
71
+ * (including React Fast Refresh injection) processes it correctly.
72
+ */
73
+ function spaFallbackPlugin(rootDir: string, routePaths: string[], workbenchPath?: string): Plugin {
74
+ const htmlPath = join(rootDir, 'src', 'web', 'index.html');
75
+ const hasHtml = existsSync(htmlPath);
76
+
77
+ return {
78
+ name: 'agentuity:spa-fallback',
79
+ configureServer(server) {
80
+ if (!hasHtml) return;
81
+
82
+ server.middlewares.use(async (req, res, next) => {
83
+ // Only handle GET/HEAD navigation requests
84
+ if (req.method !== 'GET' && req.method !== 'HEAD') return next();
85
+
86
+ const url = req.url || '/';
87
+ const pathname = url.split('?')[0] || '/';
88
+ const accept = req.headers.accept || '';
89
+ const secFetchDest = req.headers['sec-fetch-dest'] || '';
90
+
91
+ // For robustness, treat unknown GET/HEAD routes as potential SPA navigations.
92
+ // We still avoid intercepting assets/backend paths via the filters below.
93
+ // (This also makes non-browser readiness probes like fetch('/streams') work.)
94
+ const isDocumentRequest = secFetchDest === 'document' || accept.includes('text/html');
95
+
96
+ // Skip file requests (have an extension)
97
+ if (pathname !== '/' && /\.[a-zA-Z0-9]+$/.test(pathname)) return next();
98
+
99
+ // For non-document requests, only allow root path fallback.
100
+ // (e.g. don't turn module/script fetches into HTML accidentally)
101
+ if (!isDocumentRequest && pathname === '/') {
102
+ // allow root fallback for simple probes
103
+ }
104
+
105
+ // Skip Vite/module/internal asset paths
106
+ if (
107
+ pathname.startsWith('/@vite') ||
108
+ pathname.startsWith('/@react-refresh') ||
109
+ pathname.startsWith('/@id/') ||
110
+ pathname.startsWith('/@fs/') ||
111
+ pathname.startsWith('/node_modules/') ||
112
+ pathname.startsWith('/src/') ||
113
+ (pathname.startsWith('/@') && !pathname.startsWith('/_agentuity'))
114
+ ) {
115
+ return next();
116
+ }
117
+
118
+ // Skip paths that are proxied to the Bun backend
119
+ if (
120
+ pathname.startsWith('/_agentuity') ||
121
+ pathname.startsWith('/_health') ||
122
+ pathname.startsWith('/_idle')
123
+ ) {
124
+ return next();
125
+ }
126
+ // Skip workbench path (served by Bun)
127
+ if (
128
+ workbenchPath &&
129
+ (pathname === workbenchPath || pathname.startsWith(workbenchPath + '/'))
130
+ ) {
131
+ return next();
132
+ }
133
+ for (const rp of routePaths) {
134
+ if (pathname === rp || pathname.startsWith(rp + '/')) return next();
135
+ }
136
+
137
+ // If this isn't clearly a document navigation, still allow SPA fallback
138
+ // for extensionless client-side routes like /streams, /rpc, /webrtc.
139
+ // We already excluded backend paths and asset/module paths above.
140
+
141
+ try {
142
+ let html = await Bun.file(htmlPath).text();
143
+ // Match old devHtmlHandler behavior from the generated Bun entry:
144
+ // rewrite relative paths so the app works from / and client-side routes.
145
+ html = html
146
+ .replace(/src="\.\//g, 'src="/src/web/')
147
+ .replace(/href="\.\//g, 'href="/src/web/');
148
+
149
+ // Let Vite inject HMR client, React refresh preamble, etc.
150
+ html = await server.transformIndexHtml(url, html, req.originalUrl);
151
+
152
+ res.statusCode = 200;
153
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
154
+ res.end(html);
155
+ } catch (error) {
156
+ next(error as Error);
157
+ }
158
+ });
159
+ },
160
+ };
18
161
  }
19
162
 
20
163
  /**
@@ -23,7 +166,7 @@ export interface GenerateAssetServerConfigOptions {
23
166
  export async function generateAssetServerConfig(
24
167
  options: GenerateAssetServerConfigOptions
25
168
  ): Promise<InlineConfig> {
26
- const { rootDir, logger, workbenchPath, port } = options;
169
+ const { rootDir, logger, workbenchPath, port, backendPort, routePaths = ['/api'] } = options;
27
170
 
28
171
  // Load custom user config for define values and plugins
29
172
  const { loadAgentuityConfig } = await import('./config-loader');
@@ -90,27 +233,56 @@ export async function generateAssetServerConfig(
90
233
  envPrefix: ['VITE_', 'AGENTUITY_PUBLIC_', 'PUBLIC_'],
91
234
 
92
235
  server: {
93
- // Use the port we selected
236
+ // Vite is the primary dev server — listens on the user-facing port
94
237
  port,
95
238
  strictPort: true, // Port is pre-verified as available by findAvailablePort()
96
239
  host: '127.0.0.1',
97
240
 
98
- // CORS headers to allow Bun server on port 3500 to proxy requests
99
- cors: {
100
- origin: 'http://127.0.0.1:3500',
101
- credentials: true,
241
+ // Proxy backend routes to Bun server (HTTP only).
242
+ // WebSocket upgrades are handled by the front-door TCP proxy (ws-proxy.ts)
243
+ // which routes them directly to the Bun backend, bypassing Vite entirely.
244
+ // This avoids Bun's broken node:http upgrade socket implementation.
245
+ proxy: {
246
+ // User-defined route mounts (from createApp({ router }))
247
+ ...Object.fromEntries(
248
+ routePaths.map((routePath) => [
249
+ routePath,
250
+ {
251
+ target: `http://127.0.0.1:${backendPort}`,
252
+ changeOrigin: true,
253
+ },
254
+ ])
255
+ ),
256
+ // Agentuity system routes (workbench API, health, analytics, etc.)
257
+ '/_agentuity': {
258
+ target: `http://127.0.0.1:${backendPort}`,
259
+ changeOrigin: true,
260
+ },
261
+ // Workbench UI route (served by Bun, references /@fs/* paths handled by Vite)
262
+ ...(workbenchPath
263
+ ? {
264
+ [workbenchPath]: {
265
+ target: `http://127.0.0.1:${backendPort}`,
266
+ changeOrigin: true,
267
+ },
268
+ }
269
+ : {}),
270
+ // Legacy health check routes
271
+ '/_health': {
272
+ target: `http://127.0.0.1:${backendPort}`,
273
+ changeOrigin: true,
274
+ },
275
+ '/_idle': {
276
+ target: `http://127.0.0.1:${backendPort}`,
277
+ changeOrigin: true,
278
+ },
102
279
  },
103
280
 
104
- // HMR configuration for development with tunnel support (*.agentuity.live)
105
- // Do NOT set host/protocol - let Vite auto-detect from page origin
106
- // This allows HMR to work both locally and through the Gravity tunnel
107
- // The Bun server proxies /__vite_hmr WebSocket connections to Vite
108
- hmr: {
109
- // Use a dedicated path for HMR WebSocket to enable proxying
110
- path: '/__vite_hmr',
111
- },
281
+ // HMR works natively Vite is the primary server, no proxy needed
282
+ // Auto-detect host/protocol from page origin for tunnel support
283
+ hmr: true,
112
284
 
113
- // Don't open browser - Bun server will be the entry point
285
+ // Don't open browser automatically
114
286
  open: false,
115
287
  },
116
288
 
@@ -159,6 +331,10 @@ export async function generateAssetServerConfig(
159
331
  browserEnvPlugin(),
160
332
  // Warn about incorrect public asset paths in dev mode
161
333
  publicAssetPathPlugin({ warnInDev: true }),
334
+ // Inject analytics scripts in dev HTML
335
+ devAnalyticsPlugin(),
336
+ // SPA fallback: serve src/web/index.html for navigation requests
337
+ spaFallbackPlugin(rootDir, routePaths, workbenchPath),
162
338
  ];
163
339
  })(),
164
340
 
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Vite Asset Server
2
+ * Vite Dev Server (Primary)
3
3
  *
4
- * Starts a minimal Vite dev server for frontend asset transformation and HMR only.
5
- * Does NOT handle API routes or WebSocket - the Bun server proxies to this.
4
+ * Starts Vite as the primary dev server on the user-facing port.
5
+ * Serves frontend assets natively and proxies API/WebSocket requests
6
+ * to the Bun backend server.
6
7
  */
7
8
 
8
9
  import { join } from 'node:path';
@@ -21,6 +22,10 @@ export interface StartViteAssetServerOptions {
21
22
  rootDir: string;
22
23
  logger: Logger;
23
24
  workbenchPath?: string;
25
+ port?: number; // Preferred user-facing port (default: 3500)
26
+ backendPort: number; // Port of the Bun backend server
27
+ /** User-defined route mount paths from createApp({ router }) */
28
+ routePaths?: string[];
24
29
  }
25
30
 
26
31
  /**
@@ -68,27 +73,34 @@ async function findAvailablePort(
68
73
  export async function startViteAssetServer(
69
74
  options: StartViteAssetServerOptions
70
75
  ): Promise<ViteAssetServerResult> {
71
- const { rootDir, logger, workbenchPath } = options;
76
+ const {
77
+ rootDir,
78
+ logger,
79
+ workbenchPath,
80
+ port: preferredPort = 3500,
81
+ backendPort,
82
+ routePaths,
83
+ } = options;
72
84
 
73
- logger.debug('Starting Vite asset server (HMR only)...');
85
+ logger.debug('Starting Vite dev server (primary, proxying backend on port %d)...', backendPort);
74
86
 
75
- // Find an available port before starting Vite
76
- // This avoids relying on Vite's strictPort:false fallback which can fail
77
- const preferredPort = 5173;
87
+ // Find an available port for the user-facing Vite server
78
88
  const availablePort = await findAvailablePort(preferredPort, '127.0.0.1');
79
89
 
80
90
  if (availablePort !== preferredPort) {
81
91
  logger.info(
82
- `Port ${preferredPort} is in use, using port ${availablePort} for Vite asset server`
92
+ `Port ${preferredPort} is in use, using port ${availablePort} for Vite dev server`
83
93
  );
84
94
  }
85
95
 
86
- // Generate minimal config with the available port
96
+ // Generate config with proxy to Bun backend
87
97
  const config = await generateAssetServerConfig({
88
98
  rootDir,
89
99
  logger,
90
100
  workbenchPath,
91
101
  port: availablePort,
102
+ backendPort,
103
+ routePaths,
92
104
  });
93
105
 
94
106
  // Dynamically import vite from the project's node_modules
@@ -130,11 +142,9 @@ export async function startViteAssetServer(
130
142
  // Port was pre-verified and strictPort:true ensures Vite uses exactly this port
131
143
  const actualPort = availablePort;
132
144
 
133
- logger.info(`Vite asset server running on port ${actualPort}`);
134
- logger.debug(`Asset server will handle: HMR, React transformation, source maps`);
135
- logger.debug(
136
- `HMR WebSocket configured at /__vite_hmr (proxied through Bun server for tunnel support)`
137
- );
145
+ logger.debug(`Vite dev server running on port ${actualPort} (primary)`);
146
+ logger.debug(`Frontend: HMR, React transformation, source maps (native)`);
147
+ logger.debug(`Backend: proxying to Bun on port ${backendPort}`);
138
148
 
139
149
  return { server, port: actualPort };
140
150
  }
@@ -74,25 +74,7 @@ export interface ViteBuildOptions {
74
74
  * Uses inline Vite config (customizable via agentuity.config.ts)
75
75
  */
76
76
  export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
77
- const {
78
- rootDir,
79
- mode,
80
- dev = false,
81
- projectId = '',
82
- deploymentId = '',
83
- logger,
84
- profile,
85
- } = options;
86
-
87
- const isViteDebug =
88
- process.env.AGENTUITY_VITE_DEBUG === '1' || process.env.AGENTUITY_VITE_DEBUG === 'true';
89
- if (isViteDebug) {
90
- logger.debug('Vite debug logging enabled via AGENTUITY_VITE_DEBUG');
91
- const existing = process.env.DEBUG || '';
92
- if (!existing.includes('vite:')) {
93
- process.env.DEBUG = existing ? `${existing},vite:*` : 'vite:*';
94
- }
95
- }
77
+ const { rootDir, mode, dev = false, deploymentId = '', logger, profile } = options;
96
78
 
97
79
  logger.debug(`Running Vite build for mode: ${mode}`);
98
80
 
@@ -125,24 +107,7 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
125
107
  profile,
126
108
  });
127
109
 
128
- // Load workbench config for entry file generation
129
- const { loadAgentuityConfig, getWorkbenchConfig } = await import('./config-loader');
130
- const config = await loadAgentuityConfig(rootDir, logger);
131
- const workbenchConfig = getWorkbenchConfig(config, dev);
132
-
133
- // Then, generate the entry file
134
- const { generateEntryFile } = await import('../entry-generator');
135
- await generateEntryFile({
136
- rootDir,
137
- projectId,
138
- deploymentId: deploymentId || '',
139
- logger,
140
- mode: dev ? 'dev' : 'prod',
141
- workbench: workbenchConfig.configured ? workbenchConfig : undefined,
142
- analytics: config?.analytics,
143
- });
144
-
145
- // Finally, build with Bun.build
110
+ // Build with Bun.build (app.ts is the entrypoint)
146
111
  const { installExternalsAndBuild } = await import('./server-bundler');
147
112
  await installExternalsAndBuild({
148
113
  rootDir,
@@ -255,7 +220,7 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
255
220
  // In dev mode, Vite serves them directly from src/web/public/
256
221
  copyPublicDir: !dev,
257
222
  },
258
- logLevel: isViteDebug ? 'info' : 'warn',
223
+ logLevel: 'warn',
259
224
  };
260
225
  } else if (mode === 'workbench') {
261
226
  const { workbenchRoute = '/workbench' } = options;
@@ -290,7 +255,7 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
290
255
  manifest: true,
291
256
  emptyOutDir: true,
292
257
  },
293
- logLevel: isViteDebug ? 'info' : 'warn',
258
+ logLevel: 'warn',
294
259
  };
295
260
  } else {
296
261
  throw new Error(`Unknown build mode: ${mode}`);
@@ -356,7 +321,6 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
356
321
 
357
322
  // 1. Discover agents and routes BEFORE builds
358
323
  logger.debug('Discovering agents and routes...');
359
- const { generateAgentRegistry, generateRouteRegistry } = await import('./registry-generator');
360
324
  const { discoverAgents } = await import('./agent-discovery');
361
325
  const { discoverRoutes } = await import('./route-discovery');
362
326
 
@@ -367,18 +331,9 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
367
331
  options.deploymentId || '',
368
332
  logger
369
333
  );
370
- const { routes, routeInfoList } = await discoverRoutes(
371
- srcDir,
372
- projectId,
373
- options.deploymentId || '',
374
- logger
375
- );
334
+ const { routes } = await discoverRoutes(srcDir, projectId, options.deploymentId || '', logger);
376
335
 
377
- // Generate agent and route registries for type augmentation BEFORE builds
378
- // (TypeScript needs these files to exist during type checking)
379
- generateAgentRegistry(srcDir, agentMetadata);
380
- await generateRouteRegistry(srcDir, routeInfoList);
381
- logger.debug('Agent and route registries generated');
336
+ // Agent metadata is used for metadata.json generation (no registry codegen needed)
382
337
 
383
338
  // Check if web frontend exists
384
339
  const hasWebFrontend = await Bun.file(join(rootDir, 'src', 'web', 'index.html')).exists();
@@ -0,0 +1,126 @@
1
+ /**
2
+ * WebSocket-aware front-door TCP proxy for dev mode.
3
+ *
4
+ * Bun's node:http has several bugs that prevent Vite's built-in http-proxy
5
+ * from proxying WebSocket upgrades (see linked PRs). Rather than polyfilling
6
+ * those bugs, this module places a lightweight `net.createServer` on the
7
+ * user-facing port. It inspects the first bytes of each TCP connection and
8
+ * routes accordingly:
9
+ *
10
+ * - **WebSocket upgrades to backend paths** → piped directly to Bun backend
11
+ * (Bun's native `server.upgrade()` works perfectly over raw TCP)
12
+ * - **Everything else** (HTTP requests, Vite HMR WebSocket) → piped to Vite
13
+ *
14
+ * From the browser's perspective there is only one port. Vite and Bun both
15
+ * listen on internal ports that are never exposed.
16
+ *
17
+ * Bun bugs this works around:
18
+ * - https://github.com/oven-sh/bun/pull/27237 (socket.write drops data)
19
+ * - https://github.com/oven-sh/bun/pull/26264 (missing destroySoon)
20
+ * - https://github.com/oven-sh/bun/pull/27859 (http.request upgrade event)
21
+ * - Server-side upgrade socket read broken (HTTP parser doesn't hand off)
22
+ *
23
+ * This entire module can be removed once those Bun PRs are merged and the
24
+ * Vite `ws: true` proxy works natively under Bun.
25
+ *
26
+ * ```
27
+ * Browser ──TCP──▶ net.Server (:3500, user-facing)
28
+ * │
29
+ * ┌───────────┴───────────┐
30
+ * ▼ (WS upgrade to ▼ (everything else)
31
+ * backend paths)
32
+ * Bun backend (:3501) Vite server (:3502)
33
+ * ```
34
+ */
35
+
36
+ import { createServer, connect, type Server } from 'node:net';
37
+ import type { Logger } from '../../../types';
38
+
39
+ export interface WsProxyOptions {
40
+ /** Port the front-door proxy listens on (user-facing) */
41
+ port: number;
42
+ /** Port of the Vite dev server (internal) */
43
+ vitePort: number;
44
+ /** Port of the Bun backend server (internal) */
45
+ backendPort: number;
46
+ /** Route path prefixes that should be proxied to the backend */
47
+ routePaths: string[];
48
+ logger: Logger;
49
+ }
50
+
51
+ /**
52
+ * Start a front-door TCP proxy that routes WebSocket upgrades to the Bun
53
+ * backend and everything else to Vite. Returns the `net.Server` instance.
54
+ */
55
+ export function startWsProxy(options: WsProxyOptions): Promise<Server> {
56
+ const { port, vitePort, backendPort, routePaths, logger } = options;
57
+
58
+ // Prefixes whose WebSocket upgrades go to Bun instead of Vite
59
+ const wsPathPrefixes = ['/_agentuity', ...routePaths];
60
+
61
+ return new Promise((resolve, reject) => {
62
+ const server = createServer((socket) => {
63
+ let handled = false;
64
+
65
+ // Peek at the first chunk to decide where to route
66
+ socket.once('data', (firstChunk) => {
67
+ handled = true;
68
+
69
+ const header = firstChunk.toString('utf8', 0, Math.min(firstChunk.length, 4096));
70
+
71
+ // Detect: is this a WebSocket upgrade for a backend path?
72
+ const isUpgrade = /upgrade:\s*websocket/i.test(header);
73
+ let targetPort = vitePort;
74
+
75
+ if (isUpgrade) {
76
+ const pathMatch = header.match(/^(?:GET|POST)\s+(\S+)/);
77
+ const pathname = (pathMatch?.[1] ?? '/').split('?')[0] ?? '/';
78
+
79
+ const isBackendPath = wsPathPrefixes.some(
80
+ (prefix) => pathname === prefix || pathname.startsWith(prefix + '/')
81
+ );
82
+
83
+ if (isBackendPath) {
84
+ targetPort = backendPort;
85
+ logger.debug('WS upgrade %s → Bun :%d', pathname, backendPort);
86
+ }
87
+ }
88
+
89
+ const target = connect(targetPort, '127.0.0.1');
90
+
91
+ target.on('connect', () => {
92
+ target.write(firstChunk);
93
+ socket.pipe(target);
94
+ target.pipe(socket);
95
+ });
96
+
97
+ target.on('error', () => {
98
+ if (!socket.destroyed) socket.destroy();
99
+ });
100
+ socket.on('error', () => {
101
+ if (!target.destroyed) target.destroy();
102
+ });
103
+ });
104
+
105
+ // Client disconnected before sending anything
106
+ socket.on('close', () => {
107
+ if (!handled) socket.destroy();
108
+ });
109
+ socket.on('error', () => {
110
+ if (!handled) socket.destroy();
111
+ });
112
+ });
113
+
114
+ server.on('error', reject);
115
+
116
+ server.listen(port, '127.0.0.1', () => {
117
+ logger.debug(
118
+ 'WS front-door proxy on :%d (Vite :%d, Bun :%d)',
119
+ port,
120
+ vitePort,
121
+ backendPort
122
+ );
123
+ resolve(server);
124
+ });
125
+ });
126
+ }
@@ -10,7 +10,6 @@ import { StructuredError } from '@agentuity/core';
10
10
  import type { Logger, DeployOptions } from '../../types';
11
11
  import { runAllBuilds } from './vite/vite-builder';
12
12
  import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
13
- import { promptRouteMigration } from '../../utils/route-migration';
14
13
  import { checkBunVersion } from '../../utils/bun-version-checker';
15
14
  import * as tui from '../../tui';
16
15
  import type { BuildReportCollector } from '../../build-report';
@@ -90,9 +89,6 @@ export async function viteBundle(options: ViteBundleOptions): Promise<{ output:
90
89
  });
91
90
  }
92
91
 
93
- // Check if project can migrate from file-based to explicit routing
94
- await promptRouteMigration(rootDir, logger);
95
-
96
92
  try {
97
93
  // Run all builds (client -> workbench -> server)
98
94
  logger.debug('Starting builds...');
@@ -8,7 +8,7 @@
8
8
  import { watch, type FSWatcher, statSync, readdirSync, lstatSync } from 'node:fs';
9
9
  import { resolve, relative } from 'node:path';
10
10
  import type { Logger } from '../../types';
11
- import { createAgentTemplates, createAPITemplates } from './templates';
11
+ import { createAgentTemplates } from './templates';
12
12
 
13
13
  export interface FileWatcherOptions {
14
14
  rootDir: string;
@@ -219,7 +219,7 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
219
219
  isDirectory = false;
220
220
  }
221
221
 
222
- // Check if an empty directory was created in src/agent/ or src/api/
222
+ // Check if an empty directory was created in src/agent/
223
223
  // This helps with developer experience by auto-scaffolding template files
224
224
  if (changedFile && eventType === 'rename' && isDirectory) {
225
225
  try {
@@ -229,19 +229,12 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
229
229
  // Check if it's empty
230
230
  const contents = readdirSync(absPath);
231
231
  if (contents.length === 0) {
232
- // Check if this is an agent or API directory
233
232
  if (
234
233
  normalizedPath.startsWith('src/agent/') ||
235
234
  normalizedPath.includes('/src/agent/')
236
235
  ) {
237
236
  logger.debug('Agent directory created: %s', changedFile);
238
237
  createAgentTemplates(absPath);
239
- } else if (
240
- normalizedPath.startsWith('src/api/') ||
241
- normalizedPath.includes('/src/api/')
242
- ) {
243
- logger.debug('API directory created: %s', changedFile);
244
- createAPITemplates(absPath);
245
238
  }
246
239
  }
247
240
  } catch (error) {