@agentuity/cli 0.1.42 → 0.1.44

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 (93) hide show
  1. package/dist/auth.d.ts +2 -2
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +7 -5
  4. package/dist/auth.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +24 -12
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  9. package/dist/cmd/build/entry-generator.js +163 -18
  10. package/dist/cmd/build/entry-generator.js.map +1 -1
  11. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  12. package/dist/cmd/build/vite/metadata-generator.js +19 -9
  13. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  14. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +24 -15
  15. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -1
  16. package/dist/cmd/build/vite/public-asset-path-plugin.js +92 -47
  17. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -1
  18. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  19. package/dist/cmd/build/vite/vite-asset-server-config.js +9 -6
  20. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  21. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  22. package/dist/cmd/build/vite/vite-asset-server.js +1 -1
  23. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  24. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  25. package/dist/cmd/build/vite/vite-builder.js +12 -11
  26. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  27. package/dist/cmd/cloud/env/org-util.d.ts +2 -1
  28. package/dist/cmd/cloud/env/org-util.d.ts.map +1 -1
  29. package/dist/cmd/cloud/env/org-util.js +4 -2
  30. package/dist/cmd/cloud/env/org-util.js.map +1 -1
  31. package/dist/cmd/cloud/stream/create.d.ts +3 -0
  32. package/dist/cmd/cloud/stream/create.d.ts.map +1 -0
  33. package/dist/cmd/cloud/stream/create.js +227 -0
  34. package/dist/cmd/cloud/stream/create.js.map +1 -0
  35. package/dist/cmd/cloud/stream/delete.d.ts.map +1 -1
  36. package/dist/cmd/cloud/stream/delete.js +2 -1
  37. package/dist/cmd/cloud/stream/delete.js.map +1 -1
  38. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  39. package/dist/cmd/cloud/stream/get.js +2 -1
  40. package/dist/cmd/cloud/stream/get.js.map +1 -1
  41. package/dist/cmd/cloud/stream/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/stream/index.js +10 -1
  43. package/dist/cmd/cloud/stream/index.js.map +1 -1
  44. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  45. package/dist/cmd/cloud/stream/list.js +2 -1
  46. package/dist/cmd/cloud/stream/list.js.map +1 -1
  47. package/dist/cmd/cloud/stream/util.d.ts +6 -5
  48. package/dist/cmd/cloud/stream/util.d.ts.map +1 -1
  49. package/dist/cmd/cloud/stream/util.js +26 -5
  50. package/dist/cmd/cloud/stream/util.js.map +1 -1
  51. package/dist/cmd/support/report.d.ts.map +1 -1
  52. package/dist/cmd/support/report.js +58 -23
  53. package/dist/cmd/support/report.js.map +1 -1
  54. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  55. package/dist/cmd/upgrade/index.js +23 -0
  56. package/dist/cmd/upgrade/index.js.map +1 -1
  57. package/dist/cmd/upgrade/npm-availability.d.ts +44 -0
  58. package/dist/cmd/upgrade/npm-availability.d.ts.map +1 -0
  59. package/dist/cmd/upgrade/npm-availability.js +73 -0
  60. package/dist/cmd/upgrade/npm-availability.js.map +1 -0
  61. package/dist/internal-logger.d.ts +7 -0
  62. package/dist/internal-logger.d.ts.map +1 -1
  63. package/dist/internal-logger.js +82 -28
  64. package/dist/internal-logger.js.map +1 -1
  65. package/dist/tui.d.ts +9 -1
  66. package/dist/tui.d.ts.map +1 -1
  67. package/dist/tui.js +39 -14
  68. package/dist/tui.js.map +1 -1
  69. package/dist/version-check.d.ts.map +1 -1
  70. package/dist/version-check.js +13 -2
  71. package/dist/version-check.js.map +1 -1
  72. package/package.json +8 -7
  73. package/src/auth.ts +9 -5
  74. package/src/cli.ts +44 -12
  75. package/src/cmd/build/entry-generator.ts +163 -18
  76. package/src/cmd/build/vite/metadata-generator.ts +20 -9
  77. package/src/cmd/build/vite/public-asset-path-plugin.ts +105 -53
  78. package/src/cmd/build/vite/vite-asset-server-config.ts +9 -6
  79. package/src/cmd/build/vite/vite-asset-server.ts +3 -1
  80. package/src/cmd/build/vite/vite-builder.ts +21 -20
  81. package/src/cmd/cloud/env/org-util.ts +5 -2
  82. package/src/cmd/cloud/stream/create.ts +248 -0
  83. package/src/cmd/cloud/stream/delete.ts +2 -1
  84. package/src/cmd/cloud/stream/get.ts +2 -1
  85. package/src/cmd/cloud/stream/index.ts +10 -1
  86. package/src/cmd/cloud/stream/list.ts +2 -1
  87. package/src/cmd/cloud/stream/util.ts +39 -12
  88. package/src/cmd/support/report.ts +82 -28
  89. package/src/cmd/upgrade/index.ts +25 -0
  90. package/src/cmd/upgrade/npm-availability.ts +105 -0
  91. package/src/internal-logger.ts +91 -27
  92. package/src/tui.ts +42 -14
  93. package/src/version-check.ts +19 -3
package/src/cli.ts CHANGED
@@ -1198,9 +1198,12 @@ async function registerSubcommand(
1198
1198
  // Recreate apiClient with auth credentials
1199
1199
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
1200
1200
  }
1201
+ // Auto-select org when --confirm flag is used
1202
+ const autoSelectOrg = options.confirm === true;
1201
1203
  if (normalized.requiresOrg) {
1202
1204
  ctx.orgId = await requireOrg(
1203
- ctx as CommandContext & { apiClient: APIClientType }
1205
+ ctx as CommandContext & { apiClient: APIClientType },
1206
+ autoSelectOrg
1204
1207
  );
1205
1208
  }
1206
1209
  // Skip org handling if --no-register is set (org only needed for registration)
@@ -1212,7 +1215,8 @@ async function registerSubcommand(
1212
1215
 
1213
1216
  if (normalized.optionalOrg && ctx.auth && !skipOrg) {
1214
1217
  ctx.orgId = await selectOptionalOrg(
1215
- ctx as CommandContext & { apiClient: APIClientType }
1218
+ ctx as CommandContext & { apiClient: APIClientType },
1219
+ autoSelectOrg
1216
1220
  );
1217
1221
  }
1218
1222
  // Skip region handling if --no-register is set (region only needed for registration)
@@ -1302,8 +1306,13 @@ async function registerSubcommand(
1302
1306
  // Recreate apiClient with auth credentials
1303
1307
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
1304
1308
  }
1309
+ // Auto-select org when --confirm flag is used
1310
+ const autoSelectOrg2 = options.confirm === true;
1305
1311
  if (normalized.requiresOrg) {
1306
- ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1312
+ ctx.orgId = await requireOrg(
1313
+ ctx as CommandContext & { apiClient: APIClientType },
1314
+ autoSelectOrg2
1315
+ );
1307
1316
  }
1308
1317
  // Skip org handling if --no-register is set (org only needed for registration)
1309
1318
  const skipOrg =
@@ -1314,7 +1323,8 @@ async function registerSubcommand(
1314
1323
 
1315
1324
  if (normalized.optionalOrg && ctx.auth && !skipOrg) {
1316
1325
  ctx.orgId = await selectOptionalOrg(
1317
- ctx as CommandContext & { apiClient: APIClientType }
1326
+ ctx as CommandContext & { apiClient: APIClientType },
1327
+ autoSelectOrg2
1318
1328
  );
1319
1329
  }
1320
1330
  // Skip region handling if --no-register is set (region only needed for registration)
@@ -1454,9 +1464,12 @@ async function registerSubcommand(
1454
1464
  !!ctx.apiClient,
1455
1465
  !!auth
1456
1466
  );
1467
+ // Auto-select org when --confirm flag is used
1468
+ const autoSelectOrg3 = options.confirm === true;
1457
1469
  if (normalized.requiresOrg && ctx.apiClient) {
1458
1470
  ctx.orgId = await requireOrg(
1459
- ctx as CommandContext & { apiClient: APIClientType }
1471
+ ctx as CommandContext & { apiClient: APIClientType },
1472
+ autoSelectOrg3
1460
1473
  );
1461
1474
  }
1462
1475
  // Skip org handling if --no-register is set (org only needed for registration)
@@ -1468,7 +1481,8 @@ async function registerSubcommand(
1468
1481
 
1469
1482
  if (normalized.optionalOrg && ctx.apiClient && auth && !skipOrg) {
1470
1483
  ctx.orgId = await selectOptionalOrg(
1471
- ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
1484
+ ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData },
1485
+ autoSelectOrg3
1472
1486
  );
1473
1487
  baseCtx.logger.trace('selected orgId: %s', ctx.orgId);
1474
1488
  }
@@ -1551,8 +1565,13 @@ async function registerSubcommand(
1551
1565
  // Recreate apiClient with auth credentials if auth was provided
1552
1566
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
1553
1567
  }
1568
+ // Auto-select org when --confirm flag is used
1569
+ const autoSelectOrg4 = options.confirm === true;
1554
1570
  if (normalized.requiresOrg && ctx.apiClient) {
1555
- ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1571
+ ctx.orgId = await requireOrg(
1572
+ ctx as CommandContext & { apiClient: APIClientType },
1573
+ autoSelectOrg4
1574
+ );
1556
1575
  }
1557
1576
  // Skip org handling if --no-register is set (org only needed for registration)
1558
1577
  // For non-schema commands, check options directly (Commander passes all options)
@@ -1563,7 +1582,8 @@ async function registerSubcommand(
1563
1582
 
1564
1583
  if (normalized.optionalOrg && ctx.apiClient && !skipOrg) {
1565
1584
  ctx.orgId = await selectOptionalOrg(
1566
- ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
1585
+ ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData },
1586
+ autoSelectOrg4
1567
1587
  );
1568
1588
  }
1569
1589
  // Skip region handling if --no-register is set (region only needed for registration)
@@ -1656,14 +1676,18 @@ async function registerSubcommand(
1656
1676
  if (normalized.requiresAPIClient && !ctx.apiClient) {
1657
1677
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
1658
1678
  }
1679
+ // Auto-select org when --confirm flag is used
1680
+ const autoSelectOrg5 = options.confirm === true;
1659
1681
  if (normalized.requiresOrg && ctx.apiClient) {
1660
1682
  ctx.orgId = await requireOrg(
1661
- ctx as CommandContext & { apiClient: APIClientType }
1683
+ ctx as CommandContext & { apiClient: APIClientType },
1684
+ autoSelectOrg5
1662
1685
  );
1663
1686
  }
1664
1687
  if (normalized.optionalOrg && ctx.apiClient && ctx.auth) {
1665
1688
  ctx.orgId = await requireOrg(
1666
- ctx as CommandContext & { apiClient: APIClientType }
1689
+ ctx as CommandContext & { apiClient: APIClientType },
1690
+ autoSelectOrg5
1667
1691
  );
1668
1692
  }
1669
1693
  await executeOrValidate(
@@ -1697,11 +1721,19 @@ async function registerSubcommand(
1697
1721
  if (normalized.requiresAPIClient && !ctx.apiClient) {
1698
1722
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
1699
1723
  }
1724
+ // Auto-select org when --confirm flag is used
1725
+ const autoSelectOrg6 = options.confirm === true;
1700
1726
  if (normalized.requiresOrg && ctx.apiClient) {
1701
- ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1727
+ ctx.orgId = await requireOrg(
1728
+ ctx as CommandContext & { apiClient: APIClientType },
1729
+ autoSelectOrg6
1730
+ );
1702
1731
  }
1703
1732
  if (normalized.optionalOrg && ctx.apiClient && ctx.auth) {
1704
- ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
1733
+ ctx.orgId = await requireOrg(
1734
+ ctx as CommandContext & { apiClient: APIClientType },
1735
+ autoSelectOrg6
1736
+ );
1705
1737
  }
1706
1738
  if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
1707
1739
  const apiClient: APIClientType = ctx.apiClient as APIClientType;
@@ -154,10 +154,11 @@ if (hasWorkbench) {
154
154
  if (isDevelopment() && process.env.VITE_PORT) {
155
155
  const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT, 10);
156
156
 
157
- const proxyToVite = async (c: Context) => {
158
- const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${c.req.path}\`;
157
+ const proxyToVite = async (c: Context, pathOverride?: string) => {
158
+ const targetPath = pathOverride ?? c.req.path;
159
+ const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${targetPath}\`;
159
160
  try {
160
- otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\`);
161
+ otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\${targetPath}\`);
161
162
  const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
162
163
  otel.logger.debug(\`[Proxy] \${c.req.path} -> \${res.status} (\${res.headers.get('content-type')})\`);
163
164
  return new Response(res.body, {
@@ -174,35 +175,144 @@ if (isDevelopment() && process.env.VITE_PORT) {
174
175
  }
175
176
  };
176
177
 
178
+ // HMR WebSocket proxy - enables hot reload through tunnels (*.agentuity.live)
179
+ // This proxies the Vite HMR WebSocket connection from the Bun server to Vite
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ const viteHmrWebsocket = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__ = {
182
+ // Map of client WebSocket -> Vite WebSocket
183
+ connections: new Map<WebSocket, WebSocket>(),
184
+
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
+ open(clientWs: any) {
187
+ // Get the query string from ws.data (set during upgrade)
188
+ const queryString = clientWs.data?.queryString || '';
189
+ const viteWsUrl = \`ws://127.0.0.1:\${VITE_ASSET_PORT}/__vite_hmr\${queryString}\`;
190
+ otel.logger.debug('[HMR Proxy] Client connected, opening connection to Vite at %s', viteWsUrl);
191
+
192
+ // Connect to Vite with the 'vite-hmr' subprotocol (required by Vite)
193
+ const viteWs = new WebSocket(viteWsUrl, ['vite-hmr']);
194
+
195
+ viteWs.onopen = () => {
196
+ otel.logger.debug('[HMR Proxy] Connected to Vite HMR server');
197
+ };
198
+
199
+ viteWs.onmessage = (event) => {
200
+ // Forward messages from Vite to client
201
+ if (clientWs.readyState === WebSocket.OPEN) {
202
+ clientWs.send(event.data);
203
+ }
204
+ };
205
+
206
+ viteWs.onerror = (error) => {
207
+ otel.logger.error('[HMR Proxy] Vite WebSocket error: %s', error);
208
+ };
209
+
210
+ viteWs.onclose = () => {
211
+ otel.logger.debug('[HMR Proxy] Vite WebSocket closed');
212
+ viteHmrWebsocket.connections.delete(clientWs);
213
+ if (clientWs.readyState === WebSocket.OPEN) {
214
+ clientWs.close();
215
+ }
216
+ };
217
+
218
+ viteHmrWebsocket.connections.set(clientWs, viteWs);
219
+ },
220
+
221
+ message(clientWs: WebSocket, message: string | Buffer) {
222
+ // Forward messages from client to Vite
223
+ const viteWs = viteHmrWebsocket.connections.get(clientWs);
224
+ if (viteWs && viteWs.readyState === WebSocket.OPEN) {
225
+ viteWs.send(message);
226
+ }
227
+ },
228
+
229
+ close(clientWs: WebSocket) {
230
+ otel.logger.debug('[HMR Proxy] Client WebSocket closed');
231
+ const viteWs = viteHmrWebsocket.connections.get(clientWs);
232
+ if (viteWs) {
233
+ viteWs.close();
234
+ viteHmrWebsocket.connections.delete(clientWs);
235
+ }
236
+ },
237
+ };
238
+
239
+ // Register HMR WebSocket route - must be before other routes
240
+ app.get('/__vite_hmr', (c: Context) => {
241
+ const upgradeHeader = c.req.header('upgrade');
242
+ if (upgradeHeader?.toLowerCase() === 'websocket') {
243
+ // Get the Bun server from context using Hono's pattern
244
+ // When app.fetch(req, server) is called, Hono stores server as c.env
245
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
246
+ const server = 'server' in (c.env as any) ? (c.env as any).server : c.env;
247
+
248
+ if (server?.upgrade) {
249
+ // Extract query string to forward to Vite (includes token parameter)
250
+ const url = new URL(c.req.url);
251
+ const queryString = url.search; // Includes the '?' prefix
252
+
253
+ // Get the requested WebSocket subprotocol (Vite uses 'vite-hmr')
254
+ const requestedProtocol = c.req.header('sec-websocket-protocol');
255
+
256
+ const success = server.upgrade(c.req.raw, {
257
+ data: { type: 'vite-hmr', queryString },
258
+ // Echo back the requested subprotocol so the browser accepts the connection
259
+ headers: requestedProtocol ? {
260
+ 'Sec-WebSocket-Protocol': requestedProtocol,
261
+ } : undefined,
262
+ });
263
+ if (success) {
264
+ otel.logger.debug('[HMR Proxy] WebSocket upgrade successful (protocol: %s)', requestedProtocol || 'none');
265
+ return new Response(null);
266
+ }
267
+ otel.logger.error('[HMR Proxy] WebSocket upgrade returned false');
268
+ } else {
269
+ otel.logger.error('[HMR Proxy] Server upgrade method not available. c.env type: %s, keys: %s',
270
+ typeof c.env,
271
+ Object.keys(c.env || {}).join(', '));
272
+ }
273
+ return c.text('WebSocket upgrade failed', 500);
274
+ }
275
+ // Non-WebSocket request to HMR endpoint - proxy to Vite
276
+ return proxyToVite(c);
277
+ });
278
+
177
279
  // Vite client scripts and HMR
178
- app.get('/@vite/*', proxyToVite);
179
- app.get('/@react-refresh', proxyToVite);
280
+ app.get('/@vite/*', (c: Context) => proxyToVite(c));
281
+ app.get('/@react-refresh', (c: Context) => proxyToVite(c));
180
282
 
181
283
  // Source files for HMR
182
- app.get('/src/web/*', proxyToVite);
183
- app.get('/src/*', proxyToVite); // Catch-all for other source files
284
+ app.get('/src/web/*', (c: Context) => proxyToVite(c));
285
+ app.get('/src/*', (c: Context) => proxyToVite(c)); // Catch-all for other source files
184
286
 
185
287
  // Workbench source files (in .agentuity/workbench-src/)
186
- app.get('/.agentuity/workbench-src/*', proxyToVite);
288
+ app.get('/.agentuity/workbench-src/*', (c: Context) => proxyToVite(c));
187
289
 
188
290
  // Node modules (Vite transforms these)
189
- app.get('/node_modules/*', proxyToVite);
291
+ app.get('/node_modules/*', (c: Context) => proxyToVite(c));
190
292
 
191
293
  // Scoped packages (e.g., @agentuity/*, @types/*)
192
- app.get('/@*', proxyToVite);
294
+ app.get('/@*', (c: Context) => proxyToVite(c));
193
295
 
194
296
  // File system access (for Vite's @fs protocol)
195
- app.get('/@fs/*', proxyToVite);
297
+ app.get('/@fs/*', (c: Context) => proxyToVite(c));
196
298
 
197
299
  // Module resolution (for Vite's @id protocol)
198
- app.get('/@id/*', proxyToVite);
300
+ app.get('/@id/*', (c: Context) => proxyToVite(c));
301
+
302
+ // Static assets - Vite serves src/web/public/* at root, but code uses /public/* paths
303
+ // In production, the plugin transforms /public/foo.svg to CDN URLs
304
+ // Rewrite /public/foo.svg -> /foo.svg before proxying to Vite
305
+ app.get('/public/*', (c: Context) => {
306
+ const rootPath = c.req.path.replace(/^\\/public/, '');
307
+ return proxyToVite(c, rootPath);
308
+ });
199
309
 
200
310
  // Any .js, .jsx, .ts, .tsx files (catch remaining modules)
201
- app.get('/*.js', proxyToVite);
202
- app.get('/*.jsx', proxyToVite);
203
- app.get('/*.ts', proxyToVite);
204
- app.get('/*.tsx', proxyToVite);
205
- app.get('/*.css', proxyToVite);
311
+ app.get('/*.js', (c: Context) => proxyToVite(c));
312
+ app.get('/*.jsx', (c: Context) => proxyToVite(c));
313
+ app.get('/*.ts', (c: Context) => proxyToVite(c));
314
+ app.get('/*.tsx', (c: Context) => proxyToVite(c));
315
+ app.get('/*.css', (c: Context) => proxyToVite(c));
206
316
  }
207
317
  `;
208
318
 
@@ -358,13 +468,48 @@ if (typeof Bun !== 'undefined') {
358
468
  enableProcessExitProtection();
359
469
 
360
470
  const port = parseInt(process.env.PORT || '3500', 10);
471
+
472
+ // Create custom WebSocket handler that supports both regular WebSockets and HMR proxy
473
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
474
+ const hmrHandler = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__;
475
+ const customWebsocket = {
476
+ ...websocket,
477
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
478
+ open(ws: any) {
479
+ // Check if this is an HMR connection
480
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
481
+ hmrHandler.open(ws);
482
+ } else if (websocket.open) {
483
+ websocket.open(ws);
484
+ }
485
+ },
486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
487
+ message(ws: any, message: string | Buffer) {
488
+ // Check if this is an HMR connection
489
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
490
+ hmrHandler.message(ws, message);
491
+ } else if (websocket.message) {
492
+ websocket.message(ws, message);
493
+ }
494
+ },
495
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
496
+ close(ws: any, code?: number, reason?: string) {
497
+ // Check if this is an HMR connection
498
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
499
+ hmrHandler.close(ws);
500
+ } else if (websocket.close) {
501
+ websocket.close(ws, code, reason);
502
+ }
503
+ },
504
+ };
505
+
361
506
  const server = Bun.serve({
362
507
  fetch: (req, server) => {
363
508
  // Get timeout from config on each request (0 = no timeout)
364
509
  server.timeout(req, getAppConfig()?.requestTimeout ?? 0);
365
510
  return app.fetch(req, server);
366
511
  },
367
- websocket,
512
+ websocket: customWebsocket,
368
513
  port,
369
514
  hostname: '127.0.0.1',
370
515
  development: isDevelopment(),
@@ -370,24 +370,35 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
370
370
  }
371
371
  }
372
372
 
373
- // Scan public/ directory for static files
374
- const publicDir = join(rootDir, 'public');
375
- if (existsSync(publicDir)) {
373
+ // Scan client directory for static files copied from public/
374
+ // Vite copies src/web/public/* to .agentuity/client/* (at root, not in public/ subfolder)
375
+ // We need to find these files and add them to assets for CDN upload
376
+ const clientDir = join(agentuityDir, 'client');
377
+ if (existsSync(clientDir)) {
376
378
  try {
377
- function scanDirectory(dir: string, prefix: string = '') {
379
+ function scanClientDirectory(dir: string, prefix: string = '') {
378
380
  const entries = readdirSync(dir, { withFileTypes: true });
379
381
  for (const entry of entries) {
380
382
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
381
383
  const fullPath = join(dir, entry.name);
382
384
 
385
+ // Skip directories we already process (assets from manifest, .vite metadata)
383
386
  if (entry.isDirectory()) {
384
- scanDirectory(fullPath, relativePath);
387
+ if (entry.name === 'assets' || entry.name === '.vite') {
388
+ continue;
389
+ }
390
+ scanClientDirectory(fullPath, relativePath);
385
391
  } else if (entry.isFile()) {
392
+ // Skip files we already added from manifest (index.html is the entry point)
393
+ if (entry.name === 'index.html') {
394
+ continue;
395
+ }
396
+
386
397
  const stats = statSync(fullPath);
387
398
  // Skip empty files
388
399
  if (stats.size === 0) continue;
389
400
 
390
- const assetPath = `public/${relativePath}`;
401
+ const assetPath = `client/${relativePath}`;
391
402
  if (!seenAssets.has(assetPath)) {
392
403
  seenAssets.add(assetPath);
393
404
  const contentType = getContentType(entry.name);
@@ -406,10 +417,10 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
406
417
  }
407
418
  }
408
419
 
409
- scanDirectory(publicDir);
410
- logger.trace(`Found ${assets.length} total assets (including public/)`);
420
+ scanClientDirectory(clientDir);
421
+ logger.trace(`Found ${assets.length} total assets (including static files)`);
411
422
  } catch (error) {
412
- logger.warn(`Failed to scan public directory: ${error}`);
423
+ logger.warn(`Failed to scan client directory for static files: ${error}`);
413
424
  }
414
425
  }
415
426
 
@@ -1,17 +1,21 @@
1
1
  /**
2
2
  * Vite plugin to fix incorrect public asset paths
3
3
  *
4
- * Developers sometimes accidentally use source paths or relative paths instead
5
- * of the correct absolute production path '/public/...'. This plugin:
4
+ * Developers should use /public/ paths for static assets from src/web/public/.
5
+ * In production, these paths are transformed to CDN URLs.
6
6
  *
7
- * 1. During build: Rewrites incorrect paths to '/public/*' in the bundle
8
- * 2. During dev: Warns about incorrect paths so developers can fix them
7
+ * This plugin:
8
+ * 1. During build: Rewrites /public/* paths to CDN URLs
9
+ * 2. During dev: Warns only about incorrect source paths (src/web/public/)
9
10
  *
10
- * Patterns handled:
11
- * - '/src/web/public/...' '/public/...'
12
- * - './src/web/public/...' '/public/...'
13
- * - 'src/web/public/...' → '/public/...'
14
- * - './public/...' → '/public/...'
11
+ * Supported patterns (work in dev, rewritten to CDN in production):
12
+ * - '/public/foo.svg' CDN URL (recommended)
13
+ * - './public/foo.svg' CDN URL
14
+ *
15
+ * Incorrect patterns (warned in dev, rewritten in production):
16
+ * - '/src/web/public/foo.svg' → CDN URL
17
+ * - './src/web/public/foo.svg' → CDN URL
18
+ * - 'src/web/public/foo.svg' → CDN URL
15
19
  */
16
20
 
17
21
  import type { Plugin } from 'vite';
@@ -19,46 +23,69 @@ import type { Plugin } from 'vite';
19
23
  export interface PublicAssetPathPluginOptions {
20
24
  /** Whether to show warnings in dev mode (default: true) */
21
25
  warnInDev?: boolean;
26
+ /** CDN base URL for production builds (e.g., 'https://cdn.agentuity.com/{deploymentId}/client/') */
27
+ cdnBaseUrl?: string;
22
28
  }
23
29
 
24
30
  interface PathPattern {
25
31
  regex: RegExp;
26
32
  description: string;
33
+ /** Replacement template - use {base} for the target base URL */
34
+ replacement: string;
27
35
  }
28
36
 
29
37
  /**
30
- * Create fresh regex instances for each transform call
31
- * (RegExp with global flag maintains state via lastIndex)
38
+ * Patterns that are incorrect - reference source paths directly
32
39
  */
33
- function createPatterns(): PathPattern[] {
40
+ function createIncorrectPatterns(): PathPattern[] {
34
41
  return [
35
42
  // '/src/web/public/...' or './src/web/public/...' or 'src/web/public/...'
36
43
  {
37
44
  regex: /(['"`])(?:\.?\/)?src\/web\/public\//g,
38
45
  description: 'src/web/public/',
46
+ replacement: '$1{base}',
39
47
  },
40
- // './public/...' (relative public path - should be absolute)
48
+ ];
49
+ }
50
+
51
+ /**
52
+ * Patterns that need rewriting for production CDN
53
+ * Both patterns simply replace the prefix while preserving the rest of the path naturally.
54
+ */
55
+ function createPublicPatterns(): PathPattern[] {
56
+ return [
57
+ // './public/...' (relative public path) - replace prefix, keep rest
41
58
  {
42
59
  regex: /(['"`])\.\/public\//g,
43
60
  description: './public/',
61
+ replacement: '$1{base}',
62
+ },
63
+ // '/public/...' (absolute public path) - replace prefix, keep rest
64
+ {
65
+ regex: /(['"`])\/public\//g,
66
+ description: '/public/',
67
+ replacement: '$1{base}',
44
68
  },
45
69
  ];
46
70
  }
47
71
 
48
72
  /**
49
- * Vite plugin that fixes incorrect public asset paths
73
+ * Vite plugin that fixes public asset paths and rewrites to CDN URLs
74
+ *
75
+ * Rewrites all public asset paths to CDN URLs in production.
50
76
  *
51
77
  * @example
52
78
  * // In vite config:
53
- * plugins: [publicAssetPathPlugin()]
79
+ * plugins: [publicAssetPathPlugin({ cdnBaseUrl: 'https://cdn.example.com/deploy/client/' })]
54
80
  *
55
- * // Transforms:
56
- * // '/src/web/public/logo.svg' → '/public/logo.svg'
57
- * // './src/web/public/logo.svg' → '/public/logo.svg'
58
- * // './public/logo.svg' → '/public/logo.svg'
81
+ * // In code, use /public/ paths:
82
+ * <img src="/public/logo.svg" />
83
+ *
84
+ * // Transforms in production:
85
+ * // '/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
59
86
  */
60
87
  export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}): Plugin {
61
- const { warnInDev = true } = options;
88
+ const { warnInDev = true, cdnBaseUrl } = options;
62
89
 
63
90
  let isDev = false;
64
91
  const warnedFiles = new Map<string, Set<string>>(); // file -> set of patterns warned
@@ -77,53 +104,78 @@ export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}
77
104
  }
78
105
 
79
106
  // Quick check: does the code contain any patterns we care about?
80
- const hasIncorrectPaths = code.includes('src/web/public/') || code.includes('./public/');
107
+ const hasIncorrectSourcePaths = code.includes('src/web/public/');
108
+ const hasPublicPaths = code.includes('/public/') || code.includes('./public/');
81
109
 
82
- if (!hasIncorrectPaths) {
110
+ if (!hasIncorrectSourcePaths && !hasPublicPaths) {
83
111
  return null;
84
112
  }
85
113
 
86
- // Create fresh patterns for this transform
87
- const patterns = createPatterns();
88
-
89
- // Track which patterns were found for warnings
90
- const foundPatterns: string[] = [];
91
- let transformed = code;
92
-
93
- for (const { regex, description } of patterns) {
94
- if (regex.test(transformed)) {
95
- foundPatterns.push(description);
96
-
97
- // Create a fresh regex for the replacement (test() consumed the previous one)
98
- const replaceRegex = new RegExp(regex.source, regex.flags);
99
- transformed = transformed.replace(replaceRegex, '$1/public/');
100
- }
101
- }
102
-
103
- // In dev mode, optionally warn but don't transform (Vite serves from source paths)
114
+ // In dev mode, only warn about incorrect source paths (src/web/public/)
115
+ // /public/ and ./public/ paths work correctly in dev mode
104
116
  if (isDev) {
105
- if (warnInDev && foundPatterns.length > 0) {
106
- const fileWarnings = warnedFiles.get(id) || new Set();
107
- const newWarnings = foundPatterns.filter((p) => !fileWarnings.has(p));
117
+ if (warnInDev && hasIncorrectSourcePaths) {
118
+ const patterns = createIncorrectPatterns();
119
+ const foundPatterns: string[] = [];
108
120
 
109
- if (newWarnings.length > 0) {
110
- for (const p of newWarnings) {
111
- fileWarnings.add(p);
121
+ for (const { regex, description } of patterns) {
122
+ if (regex.test(code)) {
123
+ foundPatterns.push(description);
112
124
  }
113
- warnedFiles.set(id, fileWarnings);
125
+ }
114
126
 
115
- this.warn(
116
- `Found incorrect asset path(s) in ${id}:\n` +
117
- newWarnings.map((p) => ` - '${p}' should be '/public/'`).join('\n') +
118
- `\nUse absolute '/public/...' paths for production compatibility.`
119
- );
127
+ if (foundPatterns.length > 0) {
128
+ const fileWarnings = warnedFiles.get(id) || new Set();
129
+ const newWarnings = foundPatterns.filter((p) => !fileWarnings.has(p));
130
+
131
+ if (newWarnings.length > 0) {
132
+ for (const p of newWarnings) {
133
+ fileWarnings.add(p);
134
+ }
135
+ warnedFiles.set(id, fileWarnings);
136
+
137
+ this.warn(
138
+ `Found incorrect asset path(s) in ${id}:\n` +
139
+ newWarnings.map((p) => ` - '${p}' should be '/public/'`).join('\n') +
140
+ `\nUse '/public/...' paths for static assets.`
141
+ );
142
+ }
120
143
  }
121
144
  }
122
145
  // In dev mode, never transform - Vite serves from source paths
146
+ // and the Bun server proxies /public/* to Vite
123
147
  return null;
124
148
  }
125
149
 
126
- // In build mode, return transformed code if changed
150
+ // Build mode: transform paths to CDN URLs
151
+ let transformed = code;
152
+
153
+ // Determine target URL: CDN base if provided, otherwise root
154
+ const targetBase = cdnBaseUrl
155
+ ? cdnBaseUrl.endsWith('/')
156
+ ? cdnBaseUrl
157
+ : `${cdnBaseUrl}/`
158
+ : '/';
159
+
160
+ // Transform incorrect source paths (src/web/public/) → CDN
161
+ if (hasIncorrectSourcePaths) {
162
+ const patterns = createIncorrectPatterns();
163
+ for (const { regex, replacement } of patterns) {
164
+ const replaceRegex = new RegExp(regex.source, regex.flags);
165
+ transformed = transformed.replace(replaceRegex, replacement.replace('{base}', targetBase));
166
+ }
167
+ }
168
+
169
+ // Transform public paths → CDN
170
+ if (hasPublicPaths) {
171
+ const publicPatterns = createPublicPatterns();
172
+ for (const { regex, replacement } of publicPatterns) {
173
+ const replaceRegex = new RegExp(regex.source, regex.flags);
174
+ transformed = transformed.replace(replaceRegex, replacement.replace('{base}', targetBase));
175
+ }
176
+ }
177
+
178
+ // Return transformed code if changed
127
179
  if (transformed !== code) {
128
180
  return {
129
181
  code: transformed,