@agentuity/cli 2.0.10 → 2.0.12

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 (233) hide show
  1. package/dist/cache/resource-region.d.ts.map +1 -1
  2. package/dist/cache/resource-region.js +48 -25
  3. package/dist/cache/resource-region.js.map +1 -1
  4. package/dist/cmd/build/vite/agent-discovery.js +4 -4
  5. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  6. package/dist/cmd/build/vite/bun-dev-server.d.ts +20 -0
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  8. package/dist/cmd/build/vite/bun-dev-server.js +62 -4
  9. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  10. package/dist/cmd/build/vite/index.d.ts +0 -1
  11. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  12. package/dist/cmd/build/vite/index.js +0 -1
  13. package/dist/cmd/build/vite/index.js.map +1 -1
  14. package/dist/cmd/build/vite/static-renderer.d.ts +17 -0
  15. package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
  16. package/dist/cmd/build/vite/static-renderer.js +18 -6
  17. package/dist/cmd/build/vite/static-renderer.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 +34 -27
  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 +9 -0
  22. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  23. package/dist/cmd/build/vite/vite-asset-server.js +5 -1
  24. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  25. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  26. package/dist/cmd/build/vite/vite-builder.js +12 -1
  27. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  28. package/dist/cmd/build/vite/ws-proxy.d.ts +15 -1
  29. package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -1
  30. package/dist/cmd/build/vite/ws-proxy.js +33 -0
  31. package/dist/cmd/build/vite/ws-proxy.js.map +1 -1
  32. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  33. package/dist/cmd/cloud/deploy.js +98 -39
  34. package/dist/cmd/cloud/deploy.js.map +1 -1
  35. package/dist/cmd/cloud/sandbox/checkpoint/create.d.ts.map +1 -1
  36. package/dist/cmd/cloud/sandbox/checkpoint/create.js +3 -4
  37. package/dist/cmd/cloud/sandbox/checkpoint/create.js.map +1 -1
  38. package/dist/cmd/cloud/sandbox/checkpoint/delete.d.ts.map +1 -1
  39. package/dist/cmd/cloud/sandbox/checkpoint/delete.js +3 -4
  40. package/dist/cmd/cloud/sandbox/checkpoint/delete.js.map +1 -1
  41. package/dist/cmd/cloud/sandbox/checkpoint/list.d.ts.map +1 -1
  42. package/dist/cmd/cloud/sandbox/checkpoint/list.js +3 -4
  43. package/dist/cmd/cloud/sandbox/checkpoint/list.js.map +1 -1
  44. package/dist/cmd/cloud/sandbox/checkpoint/restore.d.ts.map +1 -1
  45. package/dist/cmd/cloud/sandbox/checkpoint/restore.js +3 -4
  46. package/dist/cmd/cloud/sandbox/checkpoint/restore.js.map +1 -1
  47. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  48. package/dist/cmd/cloud/sandbox/create.js +13 -4
  49. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  50. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  51. package/dist/cmd/cloud/sandbox/delete.js +3 -4
  52. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  53. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -1
  54. package/dist/cmd/cloud/sandbox/env.js +3 -5
  55. package/dist/cmd/cloud/sandbox/env.js.map +1 -1
  56. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  57. package/dist/cmd/cloud/sandbox/exec.js +114 -41
  58. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  59. package/dist/cmd/cloud/sandbox/execution/list.d.ts.map +1 -1
  60. package/dist/cmd/cloud/sandbox/execution/list.js +3 -5
  61. package/dist/cmd/cloud/sandbox/execution/list.js.map +1 -1
  62. package/dist/cmd/cloud/sandbox/fs/cp.d.ts.map +1 -1
  63. package/dist/cmd/cloud/sandbox/fs/cp.js +61 -113
  64. package/dist/cmd/cloud/sandbox/fs/cp.js.map +1 -1
  65. package/dist/cmd/cloud/sandbox/fs/download.d.ts.map +1 -1
  66. package/dist/cmd/cloud/sandbox/fs/download.js +11 -22
  67. package/dist/cmd/cloud/sandbox/fs/download.js.map +1 -1
  68. package/dist/cmd/cloud/sandbox/fs/ls.d.ts.map +1 -1
  69. package/dist/cmd/cloud/sandbox/fs/ls.js +3 -5
  70. package/dist/cmd/cloud/sandbox/fs/ls.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/fs/mkdir.d.ts.map +1 -1
  72. package/dist/cmd/cloud/sandbox/fs/mkdir.js +3 -5
  73. package/dist/cmd/cloud/sandbox/fs/mkdir.js.map +1 -1
  74. package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
  75. package/dist/cmd/cloud/sandbox/fs/rm.js +3 -5
  76. package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
  77. package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
  78. package/dist/cmd/cloud/sandbox/fs/rmdir.js +3 -5
  79. package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
  80. package/dist/cmd/cloud/sandbox/fs/upload.d.ts.map +1 -1
  81. package/dist/cmd/cloud/sandbox/fs/upload.js +7 -8
  82. package/dist/cmd/cloud/sandbox/fs/upload.js.map +1 -1
  83. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  84. package/dist/cmd/cloud/sandbox/get.js +21 -7
  85. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  86. package/dist/cmd/cloud/sandbox/job/create.d.ts.map +1 -1
  87. package/dist/cmd/cloud/sandbox/job/create.js +3 -4
  88. package/dist/cmd/cloud/sandbox/job/create.js.map +1 -1
  89. package/dist/cmd/cloud/sandbox/job/destroy.d.ts.map +1 -1
  90. package/dist/cmd/cloud/sandbox/job/destroy.js +3 -4
  91. package/dist/cmd/cloud/sandbox/job/destroy.js.map +1 -1
  92. package/dist/cmd/cloud/sandbox/job/get.d.ts.map +1 -1
  93. package/dist/cmd/cloud/sandbox/job/get.js +3 -4
  94. package/dist/cmd/cloud/sandbox/job/get.js.map +1 -1
  95. package/dist/cmd/cloud/sandbox/job/list.d.ts.map +1 -1
  96. package/dist/cmd/cloud/sandbox/job/list.js +3 -4
  97. package/dist/cmd/cloud/sandbox/job/list.js.map +1 -1
  98. package/dist/cmd/cloud/sandbox/job/logs.d.ts.map +1 -1
  99. package/dist/cmd/cloud/sandbox/job/logs.js +4 -4
  100. package/dist/cmd/cloud/sandbox/job/logs.js.map +1 -1
  101. package/dist/cmd/cloud/sandbox/pause.d.ts.map +1 -1
  102. package/dist/cmd/cloud/sandbox/pause.js +21 -5
  103. package/dist/cmd/cloud/sandbox/pause.js.map +1 -1
  104. package/dist/cmd/cloud/sandbox/resume.d.ts.map +1 -1
  105. package/dist/cmd/cloud/sandbox/resume.js +3 -4
  106. package/dist/cmd/cloud/sandbox/resume.js.map +1 -1
  107. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  108. package/dist/cmd/cloud/sandbox/run.js +36 -7
  109. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  110. package/dist/cmd/cloud/sandbox/util.d.ts +19 -0
  111. package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -1
  112. package/dist/cmd/cloud/sandbox/util.js +40 -2
  113. package/dist/cmd/cloud/sandbox/util.js.map +1 -1
  114. package/dist/cmd/coder/create.d.ts.map +1 -1
  115. package/dist/cmd/coder/create.js +18 -0
  116. package/dist/cmd/coder/create.js.map +1 -1
  117. package/dist/cmd/coder/index.d.ts.map +1 -1
  118. package/dist/cmd/coder/index.js +4 -0
  119. package/dist/cmd/coder/index.js.map +1 -1
  120. package/dist/cmd/coder/start.d.ts.map +1 -1
  121. package/dist/cmd/coder/start.js +52 -1
  122. package/dist/cmd/coder/start.js.map +1 -1
  123. package/dist/cmd/coder/tui-init.js +1 -1
  124. package/dist/cmd/coder/tui-init.js.map +1 -1
  125. package/dist/cmd/coder/update.d.ts.map +1 -1
  126. package/dist/cmd/coder/update.js +21 -1
  127. package/dist/cmd/coder/update.js.map +1 -1
  128. package/dist/cmd/coder/workspace/create.d.ts.map +1 -1
  129. package/dist/cmd/coder/workspace/create.js +57 -13
  130. package/dist/cmd/coder/workspace/create.js.map +1 -1
  131. package/dist/cmd/coder/workspace/index.d.ts.map +1 -1
  132. package/dist/cmd/coder/workspace/index.js +1 -1
  133. package/dist/cmd/coder/workspace/index.js.map +1 -1
  134. package/dist/cmd/coder/workspace/list.js +2 -2
  135. package/dist/cmd/coder/workspace/list.js.map +1 -1
  136. package/dist/cmd/dev/dev-lock.d.ts.map +1 -1
  137. package/dist/cmd/dev/dev-lock.js +43 -17
  138. package/dist/cmd/dev/dev-lock.js.map +1 -1
  139. package/dist/cmd/dev/index.d.ts.map +1 -1
  140. package/dist/cmd/dev/index.js +211 -125
  141. package/dist/cmd/dev/index.js.map +1 -1
  142. package/dist/cmd/dev/process-manager.d.ts +41 -1
  143. package/dist/cmd/dev/process-manager.d.ts.map +1 -1
  144. package/dist/cmd/dev/process-manager.js +160 -31
  145. package/dist/cmd/dev/process-manager.js.map +1 -1
  146. package/dist/cmd/project/create.d.ts.map +1 -1
  147. package/dist/cmd/project/create.js +0 -2
  148. package/dist/cmd/project/create.js.map +1 -1
  149. package/dist/cmd/project/index.d.ts.map +1 -1
  150. package/dist/cmd/project/index.js +0 -3
  151. package/dist/cmd/project/index.js.map +1 -1
  152. package/dist/cmd/project/template-flow.d.ts +0 -1
  153. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  154. package/dist/cmd/project/template-flow.js +1 -124
  155. package/dist/cmd/project/template-flow.js.map +1 -1
  156. package/dist/types.d.ts +1 -1
  157. package/package.json +7 -7
  158. package/src/cache/resource-region.ts +68 -44
  159. package/src/cmd/ai/prompt/web.md +43 -17
  160. package/src/cmd/build/vite/agent-discovery.ts +4 -4
  161. package/src/cmd/build/vite/bun-dev-server.ts +92 -6
  162. package/src/cmd/build/vite/index.ts +0 -1
  163. package/src/cmd/build/vite/static-renderer.ts +18 -7
  164. package/src/cmd/build/vite/vite-asset-server-config.ts +37 -27
  165. package/src/cmd/build/vite/vite-asset-server.ts +5 -1
  166. package/src/cmd/build/vite/vite-builder.ts +12 -1
  167. package/src/cmd/build/vite/ws-proxy.ts +52 -3
  168. package/src/cmd/cloud/deploy.ts +117 -49
  169. package/src/cmd/cloud/sandbox/checkpoint/create.ts +10 -4
  170. package/src/cmd/cloud/sandbox/checkpoint/delete.ts +10 -4
  171. package/src/cmd/cloud/sandbox/checkpoint/list.ts +10 -4
  172. package/src/cmd/cloud/sandbox/checkpoint/restore.ts +10 -4
  173. package/src/cmd/cloud/sandbox/create.ts +14 -4
  174. package/src/cmd/cloud/sandbox/delete.ts +10 -4
  175. package/src/cmd/cloud/sandbox/env.ts +10 -5
  176. package/src/cmd/cloud/sandbox/exec.ts +157 -42
  177. package/src/cmd/cloud/sandbox/execution/list.ts +10 -5
  178. package/src/cmd/cloud/sandbox/fs/cp.ts +94 -126
  179. package/src/cmd/cloud/sandbox/fs/download.ts +18 -25
  180. package/src/cmd/cloud/sandbox/fs/ls.ts +10 -5
  181. package/src/cmd/cloud/sandbox/fs/mkdir.ts +10 -5
  182. package/src/cmd/cloud/sandbox/fs/rm.ts +10 -5
  183. package/src/cmd/cloud/sandbox/fs/rmdir.ts +10 -5
  184. package/src/cmd/cloud/sandbox/fs/upload.ts +14 -8
  185. package/src/cmd/cloud/sandbox/get.ts +28 -7
  186. package/src/cmd/cloud/sandbox/job/create.ts +10 -4
  187. package/src/cmd/cloud/sandbox/job/destroy.ts +10 -4
  188. package/src/cmd/cloud/sandbox/job/get.ts +10 -4
  189. package/src/cmd/cloud/sandbox/job/list.ts +10 -4
  190. package/src/cmd/cloud/sandbox/job/logs.ts +11 -4
  191. package/src/cmd/cloud/sandbox/pause.ts +31 -5
  192. package/src/cmd/cloud/sandbox/resume.ts +10 -4
  193. package/src/cmd/cloud/sandbox/run.ts +49 -11
  194. package/src/cmd/cloud/sandbox/util.ts +63 -2
  195. package/src/cmd/coder/create.ts +24 -1
  196. package/src/cmd/coder/index.ts +4 -0
  197. package/src/cmd/coder/start.ts +63 -1
  198. package/src/cmd/coder/tui-init.ts +1 -1
  199. package/src/cmd/coder/update.ts +18 -1
  200. package/src/cmd/coder/workspace/create.ts +84 -15
  201. package/src/cmd/coder/workspace/index.ts +3 -1
  202. package/src/cmd/coder/workspace/list.ts +2 -2
  203. package/src/cmd/dev/dev-lock.ts +50 -16
  204. package/src/cmd/dev/index.ts +249 -134
  205. package/src/cmd/dev/process-manager.ts +173 -33
  206. package/src/cmd/project/create.ts +0 -2
  207. package/src/cmd/project/index.ts +0 -3
  208. package/src/cmd/project/template-flow.ts +0 -147
  209. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +0 -45
  210. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +0 -1
  211. package/dist/cmd/build/vite/public-asset-path-plugin.js +0 -166
  212. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +0 -1
  213. package/dist/cmd/project/auth/generate.d.ts +0 -5
  214. package/dist/cmd/project/auth/generate.d.ts.map +0 -1
  215. package/dist/cmd/project/auth/generate.js +0 -102
  216. package/dist/cmd/project/auth/generate.js.map +0 -1
  217. package/dist/cmd/project/auth/index.d.ts +0 -2
  218. package/dist/cmd/project/auth/index.d.ts.map +0 -1
  219. package/dist/cmd/project/auth/index.js +0 -21
  220. package/dist/cmd/project/auth/index.js.map +0 -1
  221. package/dist/cmd/project/auth/init.d.ts +0 -2
  222. package/dist/cmd/project/auth/init.d.ts.map +0 -1
  223. package/dist/cmd/project/auth/init.js +0 -213
  224. package/dist/cmd/project/auth/init.js.map +0 -1
  225. package/dist/cmd/project/auth/shared.d.ts +0 -93
  226. package/dist/cmd/project/auth/shared.d.ts.map +0 -1
  227. package/dist/cmd/project/auth/shared.js +0 -475
  228. package/dist/cmd/project/auth/shared.js.map +0 -1
  229. package/src/cmd/build/vite/public-asset-path-plugin.ts +0 -209
  230. package/src/cmd/project/auth/generate.ts +0 -116
  231. package/src/cmd/project/auth/index.ts +0 -21
  232. package/src/cmd/project/auth/init.ts +0 -256
  233. package/src/cmd/project/auth/shared.ts +0 -591
@@ -17,14 +17,35 @@ import type { Logger } from '../../../types';
17
17
  import { getAgentEnv } from '../../../agent-detection';
18
18
  import { createServer as createNetServer } from 'node:net';
19
19
 
20
+ /**
21
+ * Minimal handle for the spawned Bun subprocess. Mirrors the shape used
22
+ * elsewhere (procManager, killBunSubprocess) so callers can register
23
+ * the process with their tracking layer without depending on Bun-specific
24
+ * Subprocess types.
25
+ */
26
+ export interface BunSubprocessHandle {
27
+ kill: (signal?: number | NodeJS.Signals) => void;
28
+ exitCode: number | null;
29
+ pid?: number;
30
+ }
31
+
20
32
  export interface BunDevServerOptions {
21
33
  rootDir: string;
22
34
  port?: number;
23
35
  logger: Logger;
24
- vitePort: number;
36
+ vitePort: number; // Kept for backward compatibility — no longer used internally
25
37
  inspect?: boolean;
26
38
  inspectWait?: boolean;
27
39
  inspectBrk?: boolean;
40
+ /**
41
+ * Optional callback fired the moment `Bun.spawn` returns, BEFORE the
42
+ * readiness wait. Lets callers register the subprocess with a process
43
+ * manager / shutdown handler immediately, eliminating the multi-second
44
+ * window during which a SIGINT could orphan the child.
45
+ *
46
+ * If this throws, the subprocess is killed and the error is rethrown.
47
+ */
48
+ onSpawn?: (proc: BunSubprocessHandle) => void;
28
49
  }
29
50
 
30
51
  export interface BunDevServerResult {
@@ -311,9 +332,17 @@ export function buildStartupErrorMessage(
311
332
  * are re-evaluated.
312
333
  */
313
334
  export async function startBunDevServer(options: BunDevServerOptions): Promise<BunDevServerResult> {
314
- const { rootDir, port = 3500, logger, vitePort, inspect, inspectWait, inspectBrk } = options;
315
-
316
- logger.debug('Starting Bun dev server (Vite already running on port %d)...', vitePort);
335
+ const {
336
+ rootDir,
337
+ port = 3500,
338
+ logger,
339
+ vitePort: _vitePort,
340
+ inspect,
341
+ inspectWait,
342
+ inspectBrk,
343
+ } = options;
344
+
345
+ logger.debug('Starting Bun dev server (port %d)...', port);
317
346
 
318
347
  const appPath = `${rootDir}/app.ts`;
319
348
 
@@ -391,6 +420,15 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
391
420
  PORT: String(port),
392
421
  FORCE_COLOR: '1', // Enable colors even though stdout is piped
393
422
  },
423
+ // Make the child a process-group leader so the CLI's procManager /
424
+ // killBunSubprocess() can signal the whole tree via process.kill(-pid, ...).
425
+ // Without detached:true, bun --hot and any workers it spawns share our
426
+ // process group, process.kill(-pid) fails with EPERM (not a group leader),
427
+ // and we fall back to a direct PID kill that leaves children orphaned.
428
+ //
429
+ // We intentionally do NOT call .unref() here — the parent still tracks
430
+ // and drives the child's lifecycle, and piped stdio is unaffected.
431
+ detached: true,
394
432
  });
395
433
 
396
434
  // Start capturing streams in the background (don't await, we need to check server readiness)
@@ -404,6 +442,35 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
404
442
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
443
  (globalThis as any).__AGENTUITY_BUN_SUBPROCESS__ = bunProcess;
406
444
 
445
+ // Notify caller IMMEDIATELY so the subprocess can be registered with the
446
+ // process manager before the readiness wait below. Without this, a SIGINT
447
+ // during the up-to-5s readiness wait would leave the subprocess unmanaged
448
+ // (only the synchronous exit-handler safety net would catch it).
449
+ if (options.onSpawn) {
450
+ try {
451
+ options.onSpawn(bunProcess as BunSubprocessHandle);
452
+ } catch (err) {
453
+ logger.debug('onSpawn callback threw, killing subprocess: %s', err);
454
+ const pid = bunProcess.pid;
455
+ try {
456
+ if (typeof pid === 'number' && pid > 1 && process.platform !== 'win32') {
457
+ try {
458
+ process.kill(-pid, 'SIGKILL');
459
+ } catch {
460
+ bunProcess.kill('SIGKILL');
461
+ }
462
+ } else {
463
+ bunProcess.kill('SIGKILL');
464
+ }
465
+ } catch {
466
+ // Best effort
467
+ }
468
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
469
+ (globalThis as any).__AGENTUITY_BUN_SUBPROCESS__ = undefined;
470
+ throw err;
471
+ }
472
+ }
473
+
407
474
  // Wait for server to start listening
408
475
  const maxRetries = 50;
409
476
  const retryDelay = 100;
@@ -436,8 +503,28 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
436
503
  }
437
504
 
438
505
  if (!serverReady) {
506
+ // The subprocess is spawned with detached:true and is therefore the
507
+ // leader of its own process group. bunProcess.kill() only targets the
508
+ // direct PID, which would leave any workers/grandchildren orphaned.
509
+ // Signal the whole process group with SIGKILL and fall back to the
510
+ // handle's kill() if the group-kill fails (EPERM, not-group-leader,
511
+ // or Windows where negative PIDs aren't supported).
512
+ const pid = bunProcess.pid;
439
513
  try {
440
- bunProcess.kill();
514
+ if (typeof pid === 'number' && pid > 1 && process.platform !== 'win32') {
515
+ try {
516
+ process.kill(-pid, 'SIGKILL');
517
+ } catch (groupErr) {
518
+ logger.debug(
519
+ 'Process-group SIGKILL failed for pid %d (%s), falling back to direct kill',
520
+ pid,
521
+ (groupErr as NodeJS.ErrnoException).code ?? groupErr
522
+ );
523
+ bunProcess.kill('SIGKILL');
524
+ }
525
+ } else {
526
+ bunProcess.kill('SIGKILL');
527
+ }
441
528
  } catch (err) {
442
529
  logger.debug('Error killing subprocess during startup failure: %s', err);
443
530
  }
@@ -452,7 +539,6 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
452
539
  }
453
540
 
454
541
  logger.debug(`Bun dev server started on http://127.0.0.1:${port} (--hot mode)`);
455
- logger.debug(`Proxied to Vite:${vitePort}`);
456
542
 
457
543
  return { bunServerPort: port };
458
544
  }
@@ -1,3 +1,2 @@
1
1
  // Re-export plugins for direct use in vite.config.ts
2
2
  export { browserEnvPlugin } from './browser-env-plugin';
3
- export { publicAssetPathPlugin } from './public-asset-path-plugin';
@@ -18,7 +18,7 @@ import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node
18
18
  import type { Logger } from '../../../types';
19
19
 
20
20
  /** Minimal shape of a TanStack Router route tree node. */
21
- interface RouteTreeNode {
21
+ export interface RouteTreeNode {
22
22
  path?: string;
23
23
  options?: { path?: string };
24
24
  children?: Record<string, RouteTreeNode>;
@@ -32,7 +32,7 @@ interface RouteTreeNode {
32
32
  * under layout routes have relative paths (e.g., '/key-value' under a
33
33
  * '/reference/api' layout should resolve to '/reference/api/key-value').
34
34
  */
35
- function extractRoutePaths(node: RouteTreeNode): string[] {
35
+ export function extractRoutePaths(node: RouteTreeNode): string[] {
36
36
  const paths = new Set<string>();
37
37
 
38
38
  function walk(route: RouteTreeNode, parentPath: string) {
@@ -40,18 +40,29 @@ function extractRoutePaths(node: RouteTreeNode): string[] {
40
40
 
41
41
  // Build the full path by accumulating segments from parent routes.
42
42
  // - Layout routes have no path (undefined) and don't contribute to the URL.
43
- // - Index routes have path '/' and resolve to the parent path itself.
43
+ // - Index routes have path '/' and resolve to the parent path itself
44
+ // (e.g., the '/' child of the root is the landing page; the '/' child
45
+ // of a '/docs' layout is '/docs'). They do not append anything.
44
46
  // - Leaf/layout routes have paths like '/reference/api' or '/key-value'.
45
47
  let currentPath = parentPath;
46
- if (segment && segment !== '/') {
48
+ let isIndexRoute = false;
49
+ if (segment === '/') {
50
+ isIndexRoute = true;
51
+ } else if (segment) {
47
52
  // Non-root segment: append to parent path.
48
53
  // Segments always start with '/' (TanStack Router convention).
49
54
  currentPath = parentPath === '/' ? segment : parentPath + segment;
50
55
  }
51
56
 
52
- // Add non-parameterized, non-empty paths
53
- if (currentPath && !currentPath.includes('$')) {
54
- const normalized = currentPath === '/' ? '/' : currentPath.replace(/\/+$/, '');
57
+ // Decide which URL, if any, to emit for this node.
58
+ // - Index routes emit the parent URL, defaulting to '/' when the parent
59
+ // itself is pathless (i.e., the site root). Without this, a
60
+ // `createFileRoute('/')` route is never pre-rendered and the
61
+ // landing page ships with the unreplaced <!--app-html--> placeholder.
62
+ // - Non-index routes emit their own accumulated path.
63
+ const emit = isIndexRoute ? parentPath || '/' : currentPath;
64
+ if (emit && !emit.includes('$')) {
65
+ const normalized = emit === '/' ? '/' : emit.replace(/\/+$/, '');
55
66
  if (normalized) {
56
67
  paths.add(normalized);
57
68
  }
@@ -22,6 +22,37 @@ export interface GenerateAssetServerConfigOptions {
22
22
  liveHostname?: string;
23
23
  }
24
24
 
25
+ /**
26
+ * Shared proxy configuration for backend routes.
27
+ *
28
+ * Includes `configure` callback that gracefully handles ECONNREFUSED errors
29
+ * when the Bun backend isn't ready yet (startup race condition or brief
30
+ * disconnect during --hot reload). Instead of logging noisy errors, the
31
+ * proxy returns 503 Service Unavailable with a retry hint.
32
+ */
33
+ function backendProxyOptions(backendPort: number) {
34
+ return {
35
+ target: `http://127.0.0.1:${backendPort}`,
36
+ changeOrigin: true,
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ configure: (proxy: any, _options: any) => {
39
+ proxy.on('error', (err: Error & { code?: string }, _req: any, res: any) => {
40
+ if (err.code === 'ECONNREFUSED' && res && !res.writableEnded) {
41
+ res.statusCode = 503;
42
+ res.setHeader('Content-Type', 'application/json');
43
+ res.end(
44
+ JSON.stringify({
45
+ error: 'Backend unavailable',
46
+ message: 'The Bun backend is not ready yet. Retrying shortly...',
47
+ retry: true,
48
+ })
49
+ );
50
+ }
51
+ });
52
+ },
53
+ };
54
+ }
55
+
25
56
  /**
26
57
  * Vite plugin that injects analytics scripts in dev mode.
27
58
  *
@@ -95,7 +126,7 @@ function spaFallbackPlugin(rootDir: string, routePaths: string[], workbenchPath?
95
126
  const isDocumentRequest = secFetchDest === 'document' || accept.includes('text/html');
96
127
 
97
128
  // Skip file requests (have an extension)
98
- if (pathname !== '/' && /\.[a-zA-Z0-9]+$/.test(pathname)) return next();
129
+ if (pathname !== '/' && /\.[\w-]+$/.test(pathname)) return next();
99
130
 
100
131
  // For non-document requests, only allow root path fallback.
101
132
  // (e.g. don't turn module/script fetches into HTML accidentally)
@@ -241,37 +272,19 @@ export async function generateAssetServerConfig(
241
272
  proxy: {
242
273
  // User-defined route mounts (from createApp({ router }))
243
274
  ...Object.fromEntries(
244
- routePaths.map((routePath) => [
245
- routePath,
246
- {
247
- target: `http://127.0.0.1:${backendPort}`,
248
- changeOrigin: true,
249
- },
250
- ])
275
+ routePaths.map((routePath) => [routePath, backendProxyOptions(backendPort)])
251
276
  ),
252
277
  // Agentuity system routes (workbench API, health, analytics, etc.)
253
- '/_agentuity': {
254
- target: `http://127.0.0.1:${backendPort}`,
255
- changeOrigin: true,
256
- },
278
+ '/_agentuity': backendProxyOptions(backendPort),
257
279
  // Workbench UI route (served by Bun, references /@fs/* paths handled by Vite)
258
280
  ...(workbenchPath
259
281
  ? {
260
- [workbenchPath]: {
261
- target: `http://127.0.0.1:${backendPort}`,
262
- changeOrigin: true,
263
- },
282
+ [workbenchPath]: backendProxyOptions(backendPort),
264
283
  }
265
284
  : {}),
266
285
  // Legacy health check routes
267
- '/_health': {
268
- target: `http://127.0.0.1:${backendPort}`,
269
- changeOrigin: true,
270
- },
271
- '/_idle': {
272
- target: `http://127.0.0.1:${backendPort}`,
273
- changeOrigin: true,
274
- },
286
+ '/_health': backendProxyOptions(backendPort),
287
+ '/_idle': backendProxyOptions(backendPort),
275
288
  },
276
289
 
277
290
  // HMR works natively — Vite is the primary server, no proxy needed
@@ -296,13 +309,10 @@ export async function generateAssetServerConfig(
296
309
  // Agentuity-specific plugins (Vite loads user plugins from vite.config.ts automatically)
297
310
  plugins: await (async () => {
298
311
  const { browserEnvPlugin } = await import('./browser-env-plugin');
299
- const { publicAssetPathPlugin } = await import('./public-asset-path-plugin');
300
312
 
301
313
  return [
302
314
  // Browser env plugin to map process.env to import.meta.env
303
315
  browserEnvPlugin(),
304
- // Warn about incorrect public asset paths in dev mode
305
- publicAssetPathPlugin({ warnInDev: true }),
306
316
  // Inject analytics scripts in dev HTML
307
317
  devAnalyticsPlugin(),
308
318
  // SPA fallback: serve src/web/index.html for navigation requests
@@ -50,8 +50,12 @@ function isPortAvailable(port: number, host: string): Promise<boolean> {
50
50
  /**
51
51
  * Find an available port starting from the preferred port.
52
52
  * Tries incrementing ports up to maxAttempts times.
53
+ *
54
+ * Exported so the dev command can pre-resolve the Vite port before
55
+ * starting the Bun backend (env vars like AGENTUITY_BASE_URL need
56
+ * the real port before Bun initializes CORS).
53
57
  */
54
- async function findAvailablePort(
58
+ export async function findAvailablePort(
55
59
  preferredPort: number,
56
60
  host: string = '127.0.0.1',
57
61
  maxAttempts: number = 20
@@ -227,6 +227,7 @@ import { join } from 'node:path';
227
227
  export default defineConfig({
228
228
  plugins: [react()],
229
229
  root: '.',
230
+ publicDir: 'src/web/public',
230
231
  build: {
231
232
  rollupOptions: {
232
233
  input: join(__dirname, 'src/web/index.html'),
@@ -244,17 +245,27 @@ export default defineConfig({
244
245
  ? `https://${options.region === 'local' ? 'localstack-static-assets.t3.storageapi.dev' : 'cdn.agentuity.com'}/${options.deploymentId}/client/`
245
246
  : undefined;
246
247
 
248
+ // Pass the user's vite.config.ts directly to the subprocess. We used to
249
+ // wrap it in an auto-generated `.agentuity/vite.client.config.ts` to
250
+ // merge a lint plugin in, but that wrapper added complexity (plugin-path
251
+ // resolution, a junk file in the deploy bundle) for a warning-only
252
+ // linter. If we need to inject plugins again, expose them as a published
253
+ // CLI export and have users add them in their own vite.config.ts.
247
254
  const args = [
248
255
  'bun',
249
256
  'x',
250
257
  'vite',
251
258
  'build',
259
+ '--config',
260
+ viteConfigPath,
252
261
  '--mode',
253
262
  buildMode,
254
263
  '--outDir',
255
264
  clientOutDir,
265
+ // `warn` surfaces Vite warnings (e.g. large-chunk notices) without
266
+ // adding its own info-level chatter.
256
267
  '--logLevel',
257
- 'error',
268
+ 'warn',
258
269
  '--clearScreen',
259
270
  'false',
260
271
  ];
@@ -33,7 +33,7 @@
33
33
  * ```
34
34
  */
35
35
 
36
- import { createServer, connect, type Server } from 'node:net';
36
+ import { createServer, connect, type Server, type Socket } from 'node:net';
37
37
  import type { Logger } from '../../../types';
38
38
 
39
39
  export interface WsProxyOptions {
@@ -48,18 +48,44 @@ export interface WsProxyOptions {
48
48
  logger: Logger;
49
49
  }
50
50
 
51
+ /**
52
+ * Front-door TCP proxy server.
53
+ *
54
+ * Extends `net.Server` with a `closeAll()` method that destroys all live
55
+ * client + upstream sockets and waits for the listening socket to close.
56
+ * The native `Server.close()` only stops accepting new connections — long-
57
+ * lived piped sockets (Vite HMR WebSocket, backend WS) keep the listener
58
+ * bound until they close on their own. During dev-mode shutdown we want
59
+ * the user-facing port released immediately, so cleanup paths should
60
+ * prefer `closeAll()` over `close()`.
61
+ */
62
+ export interface WsProxyServer extends Server {
63
+ closeAll(): Promise<void>;
64
+ }
65
+
51
66
  /**
52
67
  * Start a front-door TCP proxy that routes WebSocket upgrades to the Bun
53
68
  * backend and everything else to Vite. Returns the `net.Server` instance.
54
69
  */
55
- export function startWsProxy(options: WsProxyOptions): Promise<Server> {
70
+ export function startWsProxy(options: WsProxyOptions): Promise<WsProxyServer> {
56
71
  const { port, vitePort, backendPort, routePaths, logger } = options;
57
72
 
58
73
  // Prefixes whose WebSocket upgrades go to Bun instead of Vite
59
74
  const wsPathPrefixes = ['/_agentuity', ...routePaths];
60
75
 
76
+ // Track every live socket pair so shutdown can drop them. Without this,
77
+ // `server.close()` waits for active connections to terminate by themselves
78
+ // (e.g. browser HMR WebSockets), which can keep the user-facing port bound
79
+ // for many seconds after dev mode exits.
80
+ const liveSockets = new Set<Socket>();
81
+ const trackSocket = (sock: Socket) => {
82
+ liveSockets.add(sock);
83
+ sock.once('close', () => liveSockets.delete(sock));
84
+ };
85
+
61
86
  return new Promise((resolve, reject) => {
62
87
  const server = createServer((socket) => {
88
+ trackSocket(socket);
63
89
  let handled = false;
64
90
 
65
91
  // Peek at the first chunk to decide where to route
@@ -87,6 +113,7 @@ export function startWsProxy(options: WsProxyOptions): Promise<Server> {
87
113
  }
88
114
 
89
115
  const target = connect(targetPort, '127.0.0.1');
116
+ trackSocket(target);
90
117
 
91
118
  target.on('connect', () => {
92
119
  target.write(firstChunk);
@@ -109,7 +136,29 @@ export function startWsProxy(options: WsProxyOptions): Promise<Server> {
109
136
  socket.on('error', () => {
110
137
  if (!handled) socket.destroy();
111
138
  });
112
- });
139
+ }) as WsProxyServer;
140
+
141
+ // Async close that destroys live sockets first, then waits for the
142
+ // listener to close. Idempotent: safe to call after the server has
143
+ // already been closed by other means.
144
+ server.closeAll = () => {
145
+ return new Promise<void>((resolveClose) => {
146
+ for (const sock of liveSockets) {
147
+ try {
148
+ if (!sock.destroyed) sock.destroy();
149
+ } catch {
150
+ // Best effort
151
+ }
152
+ }
153
+ liveSockets.clear();
154
+
155
+ if (!server.listening) {
156
+ resolveClose();
157
+ return;
158
+ }
159
+ server.close(() => resolveClose());
160
+ });
161
+ };
113
162
 
114
163
  server.on('error', reject);
115
164