@agentuity/cli 0.1.41 → 0.1.43

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 (247) hide show
  1. package/dist/agent-detection.d.ts.map +1 -1
  2. package/dist/agent-detection.js +8 -2
  3. package/dist/agent-detection.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +9 -5
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/index.d.ts.map +1 -1
  8. package/dist/cmd/ai/index.js +1 -8
  9. package/dist/cmd/ai/index.js.map +1 -1
  10. package/dist/cmd/ai/prompt/version.js +1 -1
  11. package/dist/cmd/ai/prompt/version.js.map +1 -1
  12. package/dist/cmd/auth/api.d.ts.map +1 -1
  13. package/dist/cmd/auth/api.js +4 -1
  14. package/dist/cmd/auth/api.js.map +1 -1
  15. package/dist/cmd/auth/ssh/api.js +2 -2
  16. package/dist/cmd/auth/ssh/api.js.map +1 -1
  17. package/dist/cmd/build/ast.d.ts.map +1 -1
  18. package/dist/cmd/build/ast.js +14 -13
  19. package/dist/cmd/build/ast.js.map +1 -1
  20. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  21. package/dist/cmd/build/entry-generator.js +137 -1
  22. package/dist/cmd/build/entry-generator.js.map +1 -1
  23. package/dist/cmd/build/patch/index.d.ts.map +1 -1
  24. package/dist/cmd/build/patch/index.js +3 -0
  25. package/dist/cmd/build/patch/index.js.map +1 -1
  26. package/dist/cmd/build/vite/index.d.ts +1 -0
  27. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  28. package/dist/cmd/build/vite/index.js +1 -0
  29. package/dist/cmd/build/vite/index.js.map +1 -1
  30. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/metadata-generator.js +19 -9
  32. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  33. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +46 -0
  34. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -0
  35. package/dist/cmd/build/vite/public-asset-path-plugin.js +133 -0
  36. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -0
  37. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  38. package/dist/cmd/build/vite/registry-generator.js +14 -5
  39. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  40. package/dist/cmd/build/vite/route-discovery.js +1 -1
  41. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  42. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  43. package/dist/cmd/build/vite/vite-asset-server-config.js +17 -7
  44. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  45. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  46. package/dist/cmd/build/vite/vite-asset-server.js +1 -1
  47. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  48. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  49. package/dist/cmd/build/vite/vite-builder.js +13 -9
  50. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  51. package/dist/cmd/canary/index.d.ts.map +1 -1
  52. package/dist/cmd/canary/index.js +9 -1
  53. package/dist/cmd/canary/index.js.map +1 -1
  54. package/dist/cmd/cloud/db/create.d.ts.map +1 -1
  55. package/dist/cmd/cloud/db/create.js +2 -2
  56. package/dist/cmd/cloud/db/create.js.map +1 -1
  57. package/dist/cmd/cloud/db/delete.d.ts.map +1 -1
  58. package/dist/cmd/cloud/db/delete.js +2 -2
  59. package/dist/cmd/cloud/db/delete.js.map +1 -1
  60. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  61. package/dist/cmd/cloud/env/import.js +4 -1
  62. package/dist/cmd/cloud/env/import.js.map +1 -1
  63. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  64. package/dist/cmd/cloud/env/list.js +4 -1
  65. package/dist/cmd/cloud/env/list.js.map +1 -1
  66. package/dist/cmd/cloud/env/push.d.ts.map +1 -1
  67. package/dist/cmd/cloud/env/push.js +4 -1
  68. package/dist/cmd/cloud/env/push.js.map +1 -1
  69. package/dist/cmd/cloud/region/index.js +1 -1
  70. package/dist/cmd/cloud/region/index.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
  72. package/dist/cmd/cloud/sandbox/snapshot/build.js +22 -10
  73. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  74. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  75. package/dist/cmd/cloud/sandbox/snapshot/get.js +12 -2
  76. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  77. package/dist/cmd/cloud/storage/create.d.ts.map +1 -1
  78. package/dist/cmd/cloud/storage/create.js +2 -2
  79. package/dist/cmd/cloud/storage/create.js.map +1 -1
  80. package/dist/cmd/cloud/storage/delete.d.ts.map +1 -1
  81. package/dist/cmd/cloud/storage/delete.js +2 -2
  82. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  83. package/dist/cmd/dev/download.d.ts.map +1 -1
  84. package/dist/cmd/dev/download.js +9 -2
  85. package/dist/cmd/dev/download.js.map +1 -1
  86. package/dist/cmd/dev/index.js +1 -1
  87. package/dist/cmd/dev/index.js.map +1 -1
  88. package/dist/cmd/git/account/add.d.ts.map +1 -1
  89. package/dist/cmd/git/account/add.js +4 -3
  90. package/dist/cmd/git/account/add.js.map +1 -1
  91. package/dist/cmd/git/link.js +2 -2
  92. package/dist/cmd/git/link.js.map +1 -1
  93. package/dist/cmd/git/list.d.ts.map +1 -1
  94. package/dist/cmd/git/list.js +3 -2
  95. package/dist/cmd/git/list.js.map +1 -1
  96. package/dist/cmd/project/auth/init.js +1 -1
  97. package/dist/cmd/project/auth/init.js.map +1 -1
  98. package/dist/cmd/project/auth/shared.d.ts.map +1 -1
  99. package/dist/cmd/project/auth/shared.js +8 -3
  100. package/dist/cmd/project/auth/shared.js.map +1 -1
  101. package/dist/cmd/project/delete.d.ts.map +1 -1
  102. package/dist/cmd/project/delete.js +3 -2
  103. package/dist/cmd/project/delete.js.map +1 -1
  104. package/dist/cmd/project/reconcile.d.ts.map +1 -1
  105. package/dist/cmd/project/reconcile.js +9 -5
  106. package/dist/cmd/project/reconcile.js.map +1 -1
  107. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  108. package/dist/cmd/project/template-flow.js +15 -5
  109. package/dist/cmd/project/template-flow.js.map +1 -1
  110. package/dist/cmd/support/report.d.ts.map +1 -1
  111. package/dist/cmd/support/report.js +58 -23
  112. package/dist/cmd/support/report.js.map +1 -1
  113. package/dist/config.d.ts.map +1 -1
  114. package/dist/config.js +3 -2
  115. package/dist/config.js.map +1 -1
  116. package/dist/domain.d.ts.map +1 -1
  117. package/dist/domain.js +4 -3
  118. package/dist/domain.js.map +1 -1
  119. package/dist/internal-logger.d.ts +7 -0
  120. package/dist/internal-logger.d.ts.map +1 -1
  121. package/dist/internal-logger.js +82 -27
  122. package/dist/internal-logger.js.map +1 -1
  123. package/dist/repl.d.ts.map +1 -1
  124. package/dist/repl.js +31 -14
  125. package/dist/repl.js.map +1 -1
  126. package/dist/schema-parser.d.ts.map +1 -1
  127. package/dist/schema-parser.js +4 -1
  128. package/dist/schema-parser.js.map +1 -1
  129. package/dist/sound.d.ts.map +1 -1
  130. package/dist/sound.js +2 -1
  131. package/dist/sound.js.map +1 -1
  132. package/dist/steps.d.ts.map +1 -1
  133. package/dist/steps.js +13 -6
  134. package/dist/steps.js.map +1 -1
  135. package/dist/terminal.js +2 -0
  136. package/dist/terminal.js.map +1 -1
  137. package/dist/tsc-output-parser.d.ts +3 -0
  138. package/dist/tsc-output-parser.d.ts.map +1 -1
  139. package/dist/tsc-output-parser.js +32 -9
  140. package/dist/tsc-output-parser.js.map +1 -1
  141. package/dist/tui/prompt.d.ts.map +1 -1
  142. package/dist/tui/prompt.js +6 -2
  143. package/dist/tui/prompt.js.map +1 -1
  144. package/dist/tui.d.ts.map +1 -1
  145. package/dist/tui.js +26 -15
  146. package/dist/tui.js.map +1 -1
  147. package/dist/typescript-errors.d.ts.map +1 -1
  148. package/dist/typescript-errors.js +5 -2
  149. package/dist/typescript-errors.js.map +1 -1
  150. package/dist/utils/date.d.ts.map +1 -1
  151. package/dist/utils/date.js +5 -1
  152. package/dist/utils/date.js.map +1 -1
  153. package/dist/utils/deps.d.ts.map +1 -1
  154. package/dist/utils/deps.js +5 -3
  155. package/dist/utils/deps.js.map +1 -1
  156. package/dist/utils/detectSubagent.js +1 -1
  157. package/dist/utils/detectSubagent.js.map +1 -1
  158. package/package.json +8 -7
  159. package/src/agent-detection.ts +9 -2
  160. package/src/cli.ts +9 -5
  161. package/src/cmd/ai/index.ts +1 -8
  162. package/src/cmd/ai/prompt/version.ts +1 -1
  163. package/src/cmd/auth/api.ts +4 -1
  164. package/src/cmd/auth/ssh/api.ts +2 -2
  165. package/src/cmd/build/ast.ts +14 -13
  166. package/src/cmd/build/entry-generator.ts +137 -1
  167. package/src/cmd/build/patch/index.ts +3 -0
  168. package/src/cmd/build/vite/index.ts +1 -0
  169. package/src/cmd/build/vite/metadata-generator.ts +20 -9
  170. package/src/cmd/build/vite/public-asset-path-plugin.ts +167 -0
  171. package/src/cmd/build/vite/registry-generator.ts +14 -5
  172. package/src/cmd/build/vite/route-discovery.ts +1 -1
  173. package/src/cmd/build/vite/vite-asset-server-config.ts +19 -9
  174. package/src/cmd/build/vite/vite-asset-server.ts +3 -1
  175. package/src/cmd/build/vite/vite-builder.ts +22 -18
  176. package/src/cmd/canary/index.ts +9 -1
  177. package/src/cmd/cloud/db/create.ts +16 -17
  178. package/src/cmd/cloud/db/delete.ts +19 -20
  179. package/src/cmd/cloud/env/import.ts +6 -3
  180. package/src/cmd/cloud/env/list.ts +11 -9
  181. package/src/cmd/cloud/env/push.ts +6 -3
  182. package/src/cmd/cloud/region/index.ts +3 -3
  183. package/src/cmd/cloud/sandbox/snapshot/build.ts +42 -33
  184. package/src/cmd/cloud/sandbox/snapshot/get.ts +21 -15
  185. package/src/cmd/cloud/storage/create.ts +16 -17
  186. package/src/cmd/cloud/storage/delete.ts +19 -20
  187. package/src/cmd/dev/download.ts +10 -2
  188. package/src/cmd/dev/index.ts +4 -4
  189. package/src/cmd/git/account/add.ts +5 -4
  190. package/src/cmd/git/link.ts +2 -2
  191. package/src/cmd/git/list.ts +7 -6
  192. package/src/cmd/project/auth/init.ts +6 -6
  193. package/src/cmd/project/auth/shared.ts +8 -3
  194. package/src/cmd/project/delete.ts +3 -2
  195. package/src/cmd/project/reconcile.ts +9 -5
  196. package/src/cmd/project/template-flow.ts +15 -5
  197. package/src/cmd/support/report.ts +82 -28
  198. package/src/config.ts +3 -2
  199. package/src/domain.ts +4 -3
  200. package/src/internal-logger.ts +91 -26
  201. package/src/repl.ts +27 -14
  202. package/src/schema-parser.ts +4 -1
  203. package/src/sound.ts +2 -1
  204. package/src/steps.ts +11 -6
  205. package/src/terminal.ts +2 -1
  206. package/src/tsc-output-parser.ts +61 -38
  207. package/src/tui/prompt.ts +6 -2
  208. package/src/tui.ts +26 -17
  209. package/src/typescript-errors.ts +5 -2
  210. package/src/utils/date.ts +5 -1
  211. package/src/utils/deps.ts +5 -3
  212. package/src/utils/detectSubagent.ts +1 -1
  213. package/dist/cmd/ai/cadence/index.d.ts +0 -3
  214. package/dist/cmd/ai/cadence/index.d.ts.map +0 -1
  215. package/dist/cmd/ai/cadence/index.js +0 -35
  216. package/dist/cmd/ai/cadence/index.js.map +0 -1
  217. package/dist/cmd/ai/cadence/list.d.ts +0 -3
  218. package/dist/cmd/ai/cadence/list.d.ts.map +0 -1
  219. package/dist/cmd/ai/cadence/list.js +0 -167
  220. package/dist/cmd/ai/cadence/list.js.map +0 -1
  221. package/dist/cmd/ai/cadence/pause.d.ts +0 -3
  222. package/dist/cmd/ai/cadence/pause.d.ts.map +0 -1
  223. package/dist/cmd/ai/cadence/pause.js +0 -103
  224. package/dist/cmd/ai/cadence/pause.js.map +0 -1
  225. package/dist/cmd/ai/cadence/resume.d.ts +0 -3
  226. package/dist/cmd/ai/cadence/resume.d.ts.map +0 -1
  227. package/dist/cmd/ai/cadence/resume.js +0 -106
  228. package/dist/cmd/ai/cadence/resume.js.map +0 -1
  229. package/dist/cmd/ai/cadence/status.d.ts +0 -3
  230. package/dist/cmd/ai/cadence/status.d.ts.map +0 -1
  231. package/dist/cmd/ai/cadence/status.js +0 -129
  232. package/dist/cmd/ai/cadence/status.js.map +0 -1
  233. package/dist/cmd/ai/cadence/stop.d.ts +0 -3
  234. package/dist/cmd/ai/cadence/stop.d.ts.map +0 -1
  235. package/dist/cmd/ai/cadence/stop.js +0 -107
  236. package/dist/cmd/ai/cadence/stop.js.map +0 -1
  237. package/dist/cmd/ai/cadence/util.d.ts +0 -44
  238. package/dist/cmd/ai/cadence/util.d.ts.map +0 -1
  239. package/dist/cmd/ai/cadence/util.js +0 -52
  240. package/dist/cmd/ai/cadence/util.js.map +0 -1
  241. package/src/cmd/ai/cadence/index.ts +0 -36
  242. package/src/cmd/ai/cadence/list.ts +0 -183
  243. package/src/cmd/ai/cadence/pause.ts +0 -119
  244. package/src/cmd/ai/cadence/resume.ts +0 -124
  245. package/src/cmd/ai/cadence/status.ts +0 -141
  246. package/src/cmd/ai/cadence/stop.ts +0 -124
  247. package/src/cmd/ai/cadence/util.ts +0 -86
@@ -2079,8 +2079,9 @@ export function checkRouteConflicts(content: string, workbenchEndpoint: string):
2079
2079
  node.expression.name.text === 'get'
2080
2080
  ) {
2081
2081
  // Check if first argument is the workbench endpoint
2082
- if (node.arguments.length > 0 && ts.isStringLiteral(node.arguments[0])) {
2083
- if (node.arguments[0].text === workbenchEndpoint) {
2082
+ const firstArg = node.arguments[0];
2083
+ if (node.arguments.length > 0 && firstArg && ts.isStringLiteral(firstArg)) {
2084
+ if (firstArg.text === workbenchEndpoint) {
2084
2085
  hasConflict = true;
2085
2086
  }
2086
2087
  }
@@ -2135,8 +2136,8 @@ export function extractAppStateType(content: string): string | null {
2135
2136
 
2136
2137
  if (callExpr) {
2137
2138
  // Check if it has a config object argument
2138
- if (callExpr.arguments.length > 0) {
2139
- const configArg = callExpr.arguments[0];
2139
+ const configArg = callExpr.arguments[0];
2140
+ if (callExpr.arguments.length > 0 && configArg) {
2140
2141
  if (ts.isObjectLiteralExpression(configArg)) {
2141
2142
  // Find setup property
2142
2143
  for (const prop of configArg.properties) {
@@ -2637,9 +2638,9 @@ export function analyzeWorkbench(content: string): WorkbenchAnalysis {
2637
2638
  if (ts.isIdentifier(node.name)) {
2638
2639
  // Extract configuration from the first argument (if any)
2639
2640
  let varConfig: WorkbenchConfig;
2640
- if (node.initializer.arguments.length > 0) {
2641
- const configArg = node.initializer.arguments[0];
2642
- varConfig = parseConfigObject(configArg) || { route: '/workbench' };
2641
+ const firstInitArg = node.initializer.arguments[0];
2642
+ if (node.initializer.arguments.length > 0 && firstInitArg) {
2643
+ varConfig = parseConfigObject(firstInitArg) || { route: '/workbench' };
2643
2644
  } else {
2644
2645
  // Default config if no arguments provided
2645
2646
  varConfig = { route: '/workbench' };
@@ -2655,10 +2656,10 @@ export function analyzeWorkbench(content: string): WorkbenchAnalysis {
2655
2656
  node.expression.text === 'createApp' &&
2656
2657
  node.arguments.length > 0
2657
2658
  ) {
2658
- const configArg = node.arguments[0];
2659
- if (ts.isObjectLiteralExpression(configArg)) {
2659
+ const createAppConfigArg = node.arguments[0];
2660
+ if (createAppConfigArg && ts.isObjectLiteralExpression(createAppConfigArg)) {
2660
2661
  // Find the services property
2661
- for (const prop of configArg.properties) {
2662
+ for (const prop of createAppConfigArg.properties) {
2662
2663
  if (
2663
2664
  ts.isPropertyAssignment(prop) &&
2664
2665
  ts.isIdentifier(prop.name) &&
@@ -2740,9 +2741,9 @@ export function analyzeWorkbench(content: string): WorkbenchAnalysis {
2740
2741
  node.expression.text === 'createApp' &&
2741
2742
  node.arguments.length > 0
2742
2743
  ) {
2743
- const configArg = node.arguments[0];
2744
- if (ts.isObjectLiteralExpression(configArg)) {
2745
- for (const prop of configArg.properties) {
2744
+ const checkConfigArg = node.arguments[0];
2745
+ if (checkConfigArg && ts.isObjectLiteralExpression(checkConfigArg)) {
2746
+ for (const prop of checkConfigArg.properties) {
2746
2747
  if (
2747
2748
  ts.isPropertyAssignment(prop) &&
2748
2749
  ts.isIdentifier(prop.name) &&
@@ -174,6 +174,107 @@ if (isDevelopment() && process.env.VITE_PORT) {
174
174
  }
175
175
  };
176
176
 
177
+ // HMR WebSocket proxy - enables hot reload through tunnels (*.agentuity.live)
178
+ // This proxies the Vite HMR WebSocket connection from the Bun server to Vite
179
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
+ const viteHmrWebsocket = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__ = {
181
+ // Map of client WebSocket -> Vite WebSocket
182
+ connections: new Map<WebSocket, WebSocket>(),
183
+
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ open(clientWs: any) {
186
+ // Get the query string from ws.data (set during upgrade)
187
+ const queryString = clientWs.data?.queryString || '';
188
+ const viteWsUrl = \`ws://127.0.0.1:\${VITE_ASSET_PORT}/__vite_hmr\${queryString}\`;
189
+ otel.logger.debug('[HMR Proxy] Client connected, opening connection to Vite at %s', viteWsUrl);
190
+
191
+ // Connect to Vite with the 'vite-hmr' subprotocol (required by Vite)
192
+ const viteWs = new WebSocket(viteWsUrl, ['vite-hmr']);
193
+
194
+ viteWs.onopen = () => {
195
+ otel.logger.debug('[HMR Proxy] Connected to Vite HMR server');
196
+ };
197
+
198
+ viteWs.onmessage = (event) => {
199
+ // Forward messages from Vite to client
200
+ if (clientWs.readyState === WebSocket.OPEN) {
201
+ clientWs.send(event.data);
202
+ }
203
+ };
204
+
205
+ viteWs.onerror = (error) => {
206
+ otel.logger.error('[HMR Proxy] Vite WebSocket error: %s', error);
207
+ };
208
+
209
+ viteWs.onclose = () => {
210
+ otel.logger.debug('[HMR Proxy] Vite WebSocket closed');
211
+ viteHmrWebsocket.connections.delete(clientWs);
212
+ if (clientWs.readyState === WebSocket.OPEN) {
213
+ clientWs.close();
214
+ }
215
+ };
216
+
217
+ viteHmrWebsocket.connections.set(clientWs, viteWs);
218
+ },
219
+
220
+ message(clientWs: WebSocket, message: string | Buffer) {
221
+ // Forward messages from client to Vite
222
+ const viteWs = viteHmrWebsocket.connections.get(clientWs);
223
+ if (viteWs && viteWs.readyState === WebSocket.OPEN) {
224
+ viteWs.send(message);
225
+ }
226
+ },
227
+
228
+ close(clientWs: WebSocket) {
229
+ otel.logger.debug('[HMR Proxy] Client WebSocket closed');
230
+ const viteWs = viteHmrWebsocket.connections.get(clientWs);
231
+ if (viteWs) {
232
+ viteWs.close();
233
+ viteHmrWebsocket.connections.delete(clientWs);
234
+ }
235
+ },
236
+ };
237
+
238
+ // Register HMR WebSocket route - must be before other routes
239
+ app.get('/__vite_hmr', (c: Context) => {
240
+ const upgradeHeader = c.req.header('upgrade');
241
+ if (upgradeHeader?.toLowerCase() === 'websocket') {
242
+ // Get the Bun server from context using Hono's pattern
243
+ // When app.fetch(req, server) is called, Hono stores server as c.env
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+ const server = 'server' in (c.env as any) ? (c.env as any).server : c.env;
246
+
247
+ if (server?.upgrade) {
248
+ // Extract query string to forward to Vite (includes token parameter)
249
+ const url = new URL(c.req.url);
250
+ const queryString = url.search; // Includes the '?' prefix
251
+
252
+ // Get the requested WebSocket subprotocol (Vite uses 'vite-hmr')
253
+ const requestedProtocol = c.req.header('sec-websocket-protocol');
254
+
255
+ const success = server.upgrade(c.req.raw, {
256
+ data: { type: 'vite-hmr', queryString },
257
+ // Echo back the requested subprotocol so the browser accepts the connection
258
+ headers: requestedProtocol ? {
259
+ 'Sec-WebSocket-Protocol': requestedProtocol,
260
+ } : undefined,
261
+ });
262
+ if (success) {
263
+ otel.logger.debug('[HMR Proxy] WebSocket upgrade successful (protocol: %s)', requestedProtocol || 'none');
264
+ return new Response(null);
265
+ }
266
+ otel.logger.error('[HMR Proxy] WebSocket upgrade returned false');
267
+ } else {
268
+ otel.logger.error('[HMR Proxy] Server upgrade method not available. c.env type: %s, keys: %s',
269
+ typeof c.env,
270
+ Object.keys(c.env || {}).join(', '));
271
+ }
272
+ return c.text('WebSocket upgrade failed', 500);
273
+ }
274
+ // Non-WebSocket request to HMR endpoint - proxy to Vite
275
+ return proxyToVite(c);
276
+ });
277
+
177
278
  // Vite client scripts and HMR
178
279
  app.get('/@vite/*', proxyToVite);
179
280
  app.get('/@react-refresh', proxyToVite);
@@ -358,13 +459,48 @@ if (typeof Bun !== 'undefined') {
358
459
  enableProcessExitProtection();
359
460
 
360
461
  const port = parseInt(process.env.PORT || '3500', 10);
462
+
463
+ // Create custom WebSocket handler that supports both regular WebSockets and HMR proxy
464
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
465
+ const hmrHandler = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__;
466
+ const customWebsocket = {
467
+ ...websocket,
468
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
469
+ open(ws: any) {
470
+ // Check if this is an HMR connection
471
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
472
+ hmrHandler.open(ws);
473
+ } else if (websocket.open) {
474
+ websocket.open(ws);
475
+ }
476
+ },
477
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
478
+ message(ws: any, message: string | Buffer) {
479
+ // Check if this is an HMR connection
480
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
481
+ hmrHandler.message(ws, message);
482
+ } else if (websocket.message) {
483
+ websocket.message(ws, message);
484
+ }
485
+ },
486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
487
+ close(ws: any, code?: number, reason?: string) {
488
+ // Check if this is an HMR connection
489
+ if (ws.data?.type === 'vite-hmr' && hmrHandler) {
490
+ hmrHandler.close(ws);
491
+ } else if (websocket.close) {
492
+ websocket.close(ws, code, reason);
493
+ }
494
+ },
495
+ };
496
+
361
497
  const server = Bun.serve({
362
498
  fetch: (req, server) => {
363
499
  // Get timeout from config on each request (0 = no timeout)
364
500
  server.timeout(req, getAppConfig()?.requestTimeout ?? 0);
365
501
  return app.fetch(req, server);
366
502
  },
367
- websocket,
503
+ websocket: customWebsocket,
368
504
  port,
369
505
  hostname: '127.0.0.1',
370
506
  development: isDevelopment(),
@@ -27,6 +27,9 @@ export async function applyPatch(
27
27
  if (patch.functions) {
28
28
  for (const fn of Object.keys(patch.functions)) {
29
29
  const mod = patch.functions[fn];
30
+ if (!mod) {
31
+ continue;
32
+ }
30
33
  let fnname = `function ${fn}`;
31
34
  let index = contents.indexOf(fnname);
32
35
  let isConstVariable = false;
@@ -12,6 +12,7 @@ import { loadAgentuityConfig, getWorkbenchConfig } from './config-loader';
12
12
 
13
13
  // Re-export plugins
14
14
  export { browserEnvPlugin } from './browser-env-plugin';
15
+ export { publicAssetPathPlugin } from './public-asset-path-plugin';
15
16
 
16
17
  export interface AgentuityPluginOptions {
17
18
  dev?: boolean;
@@ -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
 
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Vite plugin to fix incorrect public asset paths
3
+ *
4
+ * Developers sometimes accidentally use source paths or relative paths instead
5
+ * of the correct absolute root path. This plugin:
6
+ *
7
+ * 1. During build: Rewrites all public asset paths to root paths (/)
8
+ * 2. During dev: Warns about incorrect paths so developers can fix them
9
+ *
10
+ * Patterns handled (all rewritten to root path in production):
11
+ * - '/src/web/public/foo.svg' → '/foo.svg'
12
+ * - './src/web/public/foo.svg' → '/foo.svg'
13
+ * - 'src/web/public/foo.svg' → '/foo.svg'
14
+ * - './public/foo.svg' → '/foo.svg'
15
+ * - '/public/foo.svg' → '/foo.svg'
16
+ *
17
+ * Vite's base config then rewrites these to CDN URLs (e.g., https://cdn.example.com/deploy/client/foo.svg)
18
+ */
19
+
20
+ import type { Plugin } from 'vite';
21
+
22
+ export interface PublicAssetPathPluginOptions {
23
+ /** Whether to show warnings in dev mode (default: true) */
24
+ warnInDev?: boolean;
25
+ /** CDN base URL for production builds (e.g., 'https://cdn.agentuity.com/{deploymentId}/client/') */
26
+ cdnBaseUrl?: string;
27
+ }
28
+
29
+ interface PathPattern {
30
+ regex: RegExp;
31
+ description: string;
32
+ }
33
+
34
+ /**
35
+ * Create fresh regex instances for each transform call
36
+ * (RegExp with global flag maintains state via lastIndex)
37
+ */
38
+ function createPatterns(): PathPattern[] {
39
+ return [
40
+ // '/src/web/public/...' or './src/web/public/...' or 'src/web/public/...'
41
+ {
42
+ regex: /(['"`])(?:\.?\/)?src\/web\/public\//g,
43
+ description: 'src/web/public/',
44
+ },
45
+ // './public/...' (relative public path - should be absolute)
46
+ {
47
+ regex: /(['"`])\.\/public\//g,
48
+ description: './public/',
49
+ },
50
+ ];
51
+ }
52
+
53
+ /**
54
+ * Vite plugin that fixes public asset paths and rewrites to CDN URLs
55
+ *
56
+ * Rewrites all public asset paths to CDN URLs in production, or root paths
57
+ * if no CDN base URL is provided.
58
+ *
59
+ * @example
60
+ * // In vite config:
61
+ * plugins: [publicAssetPathPlugin({ cdnBaseUrl: 'https://cdn.example.com/deploy/client/' })]
62
+ *
63
+ * // Transforms in production with CDN:
64
+ * // '/src/web/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
65
+ * // './src/web/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
66
+ * // '/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
67
+ *
68
+ * // Transforms in production without CDN:
69
+ * // '/src/web/public/logo.svg' → '/logo.svg'
70
+ * // '/public/logo.svg' → '/logo.svg'
71
+ */
72
+ export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}): Plugin {
73
+ const { warnInDev = true, cdnBaseUrl } = options;
74
+
75
+ let isDev = false;
76
+ const warnedFiles = new Map<string, Set<string>>(); // file -> set of patterns warned
77
+
78
+ return {
79
+ name: 'agentuity:public-asset-path',
80
+
81
+ configResolved(config) {
82
+ isDev = config.command === 'serve';
83
+ },
84
+
85
+ transform(code, id) {
86
+ // Only transform files in src/web (browser code)
87
+ if (!id.includes('/src/web/') && !id.includes('\\src\\web\\')) {
88
+ return null;
89
+ }
90
+
91
+ // Quick check: does the code contain any patterns we care about?
92
+ const hasIncorrectPaths = code.includes('src/web/public/') || code.includes('./public/');
93
+ const hasPublicPaths = code.includes('/public/');
94
+
95
+ if (!hasIncorrectPaths && !hasPublicPaths) {
96
+ return null;
97
+ }
98
+
99
+ // In dev mode, optionally warn about incorrect paths but don't transform
100
+ if (isDev) {
101
+ if (warnInDev && hasIncorrectPaths) {
102
+ const patterns = createPatterns();
103
+ const foundPatterns: string[] = [];
104
+
105
+ for (const { regex, description } of patterns) {
106
+ if (regex.test(code)) {
107
+ foundPatterns.push(description);
108
+ }
109
+ }
110
+
111
+ if (foundPatterns.length > 0) {
112
+ const fileWarnings = warnedFiles.get(id) || new Set();
113
+ const newWarnings = foundPatterns.filter((p) => !fileWarnings.has(p));
114
+
115
+ if (newWarnings.length > 0) {
116
+ for (const p of newWarnings) {
117
+ fileWarnings.add(p);
118
+ }
119
+ warnedFiles.set(id, fileWarnings);
120
+
121
+ this.warn(
122
+ `Found incorrect asset path(s) in ${id}:\n` +
123
+ newWarnings.map((p) => ` - '${p}' should be '/public/'`).join('\n') +
124
+ `\nUse absolute '/public/...' paths for production compatibility.`
125
+ );
126
+ }
127
+ }
128
+ }
129
+ // In dev mode, never transform - Vite serves from source paths
130
+ return null;
131
+ }
132
+
133
+ // Build mode: transform paths
134
+ let transformed = code;
135
+
136
+ // Determine target URL: CDN base if provided, otherwise root
137
+ const targetBase = cdnBaseUrl ? (cdnBaseUrl.endsWith('/') ? cdnBaseUrl : `${cdnBaseUrl}/`) : '/';
138
+
139
+ // First, fix incorrect source paths (src/web/public/, ./public/) → targetBase
140
+ if (hasIncorrectPaths) {
141
+ const patterns = createPatterns();
142
+ for (const { regex } of patterns) {
143
+ const replaceRegex = new RegExp(regex.source, regex.flags);
144
+ transformed = transformed.replace(replaceRegex, `$1${targetBase}`);
145
+ }
146
+ }
147
+
148
+ // Then, rewrite /public/foo → {targetBase}foo
149
+ if (hasPublicPaths) {
150
+ // Match '/public/...' paths in strings (single, double, or backtick quotes)
151
+ // Captures: $1 = quote char, $2 = path after /public/
152
+ const publicPathRegex = /(['"`])\/public\/([^'"`\s]+)/g;
153
+ transformed = transformed.replace(publicPathRegex, `$1${targetBase}$2`);
154
+ }
155
+
156
+ // Return transformed code if changed
157
+ if (transformed !== code) {
158
+ return {
159
+ code: transformed,
160
+ map: null,
161
+ };
162
+ }
163
+
164
+ return null;
165
+ },
166
+ };
167
+ }
@@ -374,7 +374,11 @@ function generateRPCRegistryType(
374
374
 
375
375
  // Add path segments - sanitize for valid TypeScript property names
376
376
  for (let i = 0; i < pathParts.length; i++) {
377
- const part = sanitizePathSegment(pathParts[i]);
377
+ const rawPart = pathParts[i];
378
+ if (!rawPart) {
379
+ continue;
380
+ }
381
+ const part = sanitizePathSegment(rawPart);
378
382
  // Skip empty segments (e.g., wildcards like '*' that sanitize to '')
379
383
  if (!part) {
380
384
  continue;
@@ -561,7 +565,10 @@ function generateRPCRuntimeMetadata(
561
565
  const sorted: MetadataNode = {};
562
566
  for (const key of Object.keys(obj).sort()) {
563
567
  const value = obj[key];
564
- if (value && typeof value === 'object' && !('type' in value)) {
568
+ if (value === undefined) {
569
+ continue;
570
+ }
571
+ if (typeof value === 'object' && !('type' in value)) {
565
572
  sorted[key] = sortObject(value as MetadataNode);
566
573
  } else {
567
574
  sorted[key] = value;
@@ -640,9 +647,11 @@ export async function generateRouteRegistry(
640
647
  const match = route.agentImportPath.match(/@agent[s]?\/([^/]+)/);
641
648
  if (match) {
642
649
  const agentName = match[1];
643
- const metadata = agentNameMap.get(agentName);
644
- if (metadata) {
645
- agentMetadataMap.set(route.agentVariable, metadata);
650
+ if (agentName) {
651
+ const metadata = agentNameMap.get(agentName);
652
+ if (metadata) {
653
+ agentMetadataMap.set(route.agentVariable, metadata);
654
+ }
646
655
  }
647
656
  }
648
657
  }
@@ -213,7 +213,7 @@ export function detectRouteConflicts(
213
213
  // Check for exact duplicates
214
214
  for (const [methodPath, routeList] of methodPathMap.entries()) {
215
215
  if (routeList.length > 1) {
216
- const [method] = methodPath.split(' ', 2);
216
+ const [method = 'UNKNOWN'] = methodPath.split(' ', 2);
217
217
  conflicts.push({
218
218
  type: 'duplicate',
219
219
  routes: routeList.map((r) => ({ method, path: r.path, filename: r.filename })),
@@ -49,10 +49,16 @@ export async function generateAssetServerConfig(
49
49
  const tsconfig = JSON.parse(await Bun.file(tsconfigPath).text());
50
50
  const paths = tsconfig?.compilerOptions?.paths || {};
51
51
  alias = Object.fromEntries(
52
- Object.entries(paths).map(([key, value]) => {
53
- const pathArray = value as string[];
54
- return [key.replace('/*', ''), join(rootDir, pathArray[0].replace('/*', ''))];
55
- })
52
+ Object.entries(paths)
53
+ .filter(([, value]) => {
54
+ const pathArray = value as string[];
55
+ return pathArray.length > 0 && pathArray[0] !== undefined;
56
+ })
57
+ .map(([key, value]) => {
58
+ const pathArray = value as string[];
59
+ const firstPath = pathArray[0] ?? '';
60
+ return [key.replace('/*', ''), join(rootDir, firstPath.replace('/*', ''))];
61
+ })
56
62
  );
57
63
  } catch {
58
64
  // No tsconfig or no paths - that's fine
@@ -93,12 +99,13 @@ export async function generateAssetServerConfig(
93
99
  credentials: true,
94
100
  },
95
101
 
96
- // HMR configuration - client must connect to Vite asset server directly
97
- // Do NOT set port/clientPort - let Vite use the actual server port it binds to
98
- // (important when strictPort: false and Vite falls back to an alternate port)
102
+ // HMR configuration for development with tunnel support (*.agentuity.live)
103
+ // Do NOT set host/protocol - let Vite auto-detect from page origin
104
+ // This allows HMR to work both locally and through the Gravity tunnel
105
+ // The Bun server proxies /__vite_hmr WebSocket connections to Vite
99
106
  hmr: {
100
- protocol: 'ws',
101
- host: '127.0.0.1',
107
+ // Use a dedicated path for HMR WebSocket to enable proxying
108
+ path: '/__vite_hmr',
102
109
  },
103
110
 
104
111
  // Don't open browser - Bun server will be the entry point
@@ -131,6 +138,7 @@ export async function generateAssetServerConfig(
131
138
  }
132
139
  const reactPlugin = (await import(reactPluginPath)).default();
133
140
  const { browserEnvPlugin } = await import('./browser-env-plugin');
141
+ const { publicAssetPathPlugin } = await import('./public-asset-path-plugin');
134
142
  return [
135
143
  // User-defined plugins from agentuity.config.ts (e.g., Tailwind CSS)
136
144
  ...userPlugins,
@@ -138,6 +146,8 @@ export async function generateAssetServerConfig(
138
146
  reactPlugin,
139
147
  // Browser env plugin to map process.env to import.meta.env
140
148
  browserEnvPlugin(),
149
+ // Warn about incorrect public asset paths in dev mode
150
+ publicAssetPathPlugin({ warnInDev: true }),
141
151
  ];
142
152
  })(),
143
153
 
@@ -119,7 +119,9 @@ export async function startViteAssetServer(
119
119
  );
120
120
  }
121
121
  logger.debug(`Asset server will handle: HMR, React transformation, source maps`);
122
- logger.debug(`HMR WebSocket configured to connect to ws://127.0.0.1:${actualPort}`);
122
+ logger.debug(
123
+ `HMR WebSocket configured at /__vite_hmr (proxied through Bun server for tunnel support)`
124
+ );
123
125
 
124
126
  return { server, port: actualPort };
125
127
  }
@@ -11,6 +11,7 @@ import type { InlineConfig, Plugin } from 'vite';
11
11
  import type { Logger, DeployOptions } from '../../../types';
12
12
  import { browserEnvPlugin } from './browser-env-plugin';
13
13
  import { beaconPlugin } from './beacon-plugin';
14
+ import { publicAssetPathPlugin } from './public-asset-path-plugin';
14
15
  import type { BuildReportCollector } from '../../../build-report';
15
16
 
16
17
  /**
@@ -148,11 +149,22 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
148
149
  analyticsEnabled = false,
149
150
  } = options;
150
151
 
152
+ // Determine CDN base URL for production builds
153
+ // Use CDN for all non-dev builds with a deploymentId (including local region)
154
+ const isLocalRegion = options.region === 'local';
155
+ const cdnDomain = isLocalRegion
156
+ ? 'localstack-static-assets.t3.storage.dev'
157
+ : 'cdn.agentuity.com';
158
+ const cdnBaseUrl =
159
+ !dev && deploymentId ? `https://${cdnDomain}/${deploymentId}/client/` : undefined;
160
+
151
161
  // Load custom user plugins from agentuity.config.ts if it exists
152
162
  const clientOutDir = join(rootDir, '.agentuity/client');
153
163
  const plugins = [
154
164
  react(),
155
165
  browserEnvPlugin(),
166
+ // Fix incorrect public asset paths and rewrite to CDN URLs
167
+ publicAssetPathPlugin({ cdnBaseUrl }),
156
168
  flattenHtmlOutputPlugin(clientOutDir),
157
169
  // Emit analytics beacon as hashed CDN asset (prod builds only)
158
170
  beaconPlugin({ enabled: analyticsEnabled && !dev }),
@@ -174,15 +186,6 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
174
186
  );
175
187
  }
176
188
 
177
- // Determine CDN base URL for production builds
178
- // Use CDN for all non-dev builds with a deploymentId (including local region)
179
- const isLocalRegion = options.region === 'local';
180
- const cdnDomain = isLocalRegion
181
- ? 'localstack-static-assets.t3.storage.dev'
182
- : 'cdn.agentuity.com';
183
- const cdnBaseUrl =
184
- !dev && deploymentId ? `https://${cdnDomain}/${deploymentId}/client/` : undefined;
185
-
186
189
  viteConfig = {
187
190
  // Use project root as Vite root so plugins (e.g., TanStack Router) resolve paths
188
191
  // from the repo root, matching where agentuity.config.ts is located
@@ -200,16 +203,17 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
200
203
  ? JSON.stringify(workbenchRoute)
201
204
  : 'undefined',
202
205
  },
203
- build: {
204
- outDir: clientOutDir,
205
- rollupOptions: {
206
- input: htmlPath,
207
- },
208
- manifest: true,
209
- emptyOutDir: true,
210
- // Disable copying public files - Vite already includes them in assets with hashing
211
- copyPublicDir: false,
206
+ build: {
207
+ outDir: clientOutDir,
208
+ rollupOptions: {
209
+ input: htmlPath,
212
210
  },
211
+ manifest: true,
212
+ emptyOutDir: true,
213
+ // Copy public files to output for CDN upload (production builds only)
214
+ // In dev mode, Vite serves them directly from src/web/public/
215
+ copyPublicDir: !dev,
216
+ },
213
217
  logLevel: 'warn',
214
218
  };
215
219
  } else if (mode === 'workbench') {