@agentuity/cli 0.0.100 → 0.0.102

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 (264) hide show
  1. package/AGENTS.md +19 -188
  2. package/bin/cli.ts +13 -6
  3. package/dist/api.d.ts +1 -0
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +1 -1
  6. package/dist/api.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +41 -12
  9. package/dist/cli.js.map +1 -1
  10. package/dist/cmd/ai/index.d.ts.map +1 -1
  11. package/dist/cmd/ai/index.js +6 -1
  12. package/dist/cmd/ai/index.js.map +1 -1
  13. package/dist/cmd/ai/prompt/agent.d.ts +7 -0
  14. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  15. package/dist/cmd/ai/prompt/agent.js +12 -323
  16. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  17. package/dist/cmd/ai/prompt/api.d.ts +7 -0
  18. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  19. package/dist/cmd/ai/prompt/api.js +12 -260
  20. package/dist/cmd/ai/prompt/api.js.map +1 -1
  21. package/dist/cmd/ai/prompt/version.d.ts +35 -0
  22. package/dist/cmd/ai/prompt/version.d.ts.map +1 -0
  23. package/dist/cmd/ai/prompt/version.js +55 -0
  24. package/dist/cmd/ai/prompt/version.js.map +1 -0
  25. package/dist/cmd/ai/prompt/web.d.ts +7 -0
  26. package/dist/cmd/ai/prompt/web.d.ts.map +1 -1
  27. package/dist/cmd/ai/prompt/web.js +12 -283
  28. package/dist/cmd/ai/prompt/web.js.map +1 -1
  29. package/dist/cmd/ai/skills/generate.d.ts +3 -0
  30. package/dist/cmd/ai/skills/generate.d.ts.map +1 -0
  31. package/dist/cmd/ai/skills/generate.js +65 -0
  32. package/dist/cmd/ai/skills/generate.js.map +1 -0
  33. package/dist/cmd/ai/skills/generator.d.ts +4 -0
  34. package/dist/cmd/ai/skills/generator.d.ts.map +1 -0
  35. package/dist/cmd/ai/skills/generator.js +402 -0
  36. package/dist/cmd/ai/skills/generator.js.map +1 -0
  37. package/dist/cmd/ai/skills/index.d.ts +4 -0
  38. package/dist/cmd/ai/skills/index.d.ts.map +1 -0
  39. package/dist/cmd/ai/skills/index.js +21 -0
  40. package/dist/cmd/ai/skills/index.js.map +1 -0
  41. package/dist/cmd/auth/signup.d.ts.map +1 -1
  42. package/dist/cmd/auth/signup.js +1 -0
  43. package/dist/cmd/auth/signup.js.map +1 -1
  44. package/dist/cmd/build/ast.d.ts +2 -1
  45. package/dist/cmd/build/ast.d.ts.map +1 -1
  46. package/dist/cmd/build/ast.js +135 -47
  47. package/dist/cmd/build/ast.js.map +1 -1
  48. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  49. package/dist/cmd/build/entry-generator.js +255 -188
  50. package/dist/cmd/build/entry-generator.js.map +1 -1
  51. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  52. package/dist/cmd/build/vite/agent-discovery.js +103 -45
  53. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  54. package/dist/cmd/build/vite/bun-dev-server.d.ts +7 -1
  55. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  56. package/dist/cmd/build/vite/bun-dev-server.js +52 -26
  57. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  58. package/dist/cmd/build/vite/docs-generator.d.ts +13 -0
  59. package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -0
  60. package/dist/cmd/build/vite/docs-generator.js +81 -0
  61. package/dist/cmd/build/vite/docs-generator.js.map +1 -0
  62. package/dist/cmd/build/vite/index.d.ts +3 -3
  63. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  64. package/dist/cmd/build/vite/index.js +9 -7
  65. package/dist/cmd/build/vite/index.js.map +1 -1
  66. package/dist/cmd/build/vite/lifecycle-generator.d.ts +1 -1
  67. package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
  68. package/dist/cmd/build/vite/lifecycle-generator.js +19 -5
  69. package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
  70. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  71. package/dist/cmd/build/vite/metadata-generator.js +203 -7
  72. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  73. package/dist/cmd/build/vite/prompt-generator.d.ts +23 -0
  74. package/dist/cmd/build/vite/prompt-generator.d.ts.map +1 -0
  75. package/dist/cmd/build/vite/prompt-generator.js +123 -0
  76. package/dist/cmd/build/vite/prompt-generator.js.map +1 -0
  77. package/dist/cmd/build/vite/registry-generator.d.ts +3 -3
  78. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  79. package/dist/cmd/build/vite/registry-generator.js +644 -103
  80. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  81. package/dist/cmd/build/vite/route-discovery.d.ts +4 -0
  82. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  83. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  84. package/dist/cmd/build/vite/server-bundler.d.ts +4 -0
  85. package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
  86. package/dist/cmd/build/vite/server-bundler.js +63 -17
  87. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  88. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  89. package/dist/cmd/build/vite/vite-asset-server-config.js +4 -0
  90. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  91. package/dist/cmd/build/vite/vite-builder.d.ts +1 -1
  92. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  93. package/dist/cmd/build/vite/vite-builder.js +118 -96
  94. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  95. package/dist/cmd/build/vite-bundler.js +6 -6
  96. package/dist/cmd/build/vite-bundler.js.map +1 -1
  97. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  98. package/dist/cmd/cloud/deploy.js +89 -32
  99. package/dist/cmd/cloud/deploy.js.map +1 -1
  100. package/dist/cmd/cloud/keyvalue/create-namespace.d.ts.map +1 -1
  101. package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -1
  102. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  103. package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
  104. package/dist/cmd/cloud/keyvalue/delete-namespace.js +3 -1
  105. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  106. package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
  107. package/dist/cmd/cloud/keyvalue/delete.js +3 -1
  108. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  109. package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
  110. package/dist/cmd/cloud/keyvalue/set.js +4 -2
  111. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  112. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  113. package/dist/cmd/cloud/stream/get.js +2 -13
  114. package/dist/cmd/cloud/stream/get.js.map +1 -1
  115. package/dist/cmd/cloud/vector/delete-namespace.d.ts +3 -0
  116. package/dist/cmd/cloud/vector/delete-namespace.d.ts.map +1 -0
  117. package/dist/cmd/cloud/vector/delete-namespace.js +77 -0
  118. package/dist/cmd/cloud/vector/delete-namespace.js.map +1 -0
  119. package/dist/cmd/cloud/vector/index.d.ts.map +1 -1
  120. package/dist/cmd/cloud/vector/index.js +21 -4
  121. package/dist/cmd/cloud/vector/index.js.map +1 -1
  122. package/dist/cmd/cloud/vector/list-namespaces.d.ts +3 -0
  123. package/dist/cmd/cloud/vector/list-namespaces.d.ts.map +1 -0
  124. package/dist/cmd/cloud/vector/list-namespaces.js +42 -0
  125. package/dist/cmd/cloud/vector/list-namespaces.js.map +1 -0
  126. package/dist/cmd/cloud/vector/stats.d.ts +3 -0
  127. package/dist/cmd/cloud/vector/stats.d.ts.map +1 -0
  128. package/dist/cmd/cloud/vector/stats.js +142 -0
  129. package/dist/cmd/cloud/vector/stats.js.map +1 -0
  130. package/dist/cmd/cloud/vector/upsert.d.ts +3 -0
  131. package/dist/cmd/cloud/vector/upsert.d.ts.map +1 -0
  132. package/dist/cmd/cloud/vector/upsert.js +192 -0
  133. package/dist/cmd/cloud/vector/upsert.js.map +1 -0
  134. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  135. package/dist/cmd/dev/file-watcher.js +94 -33
  136. package/dist/cmd/dev/file-watcher.js.map +1 -1
  137. package/dist/cmd/dev/index.d.ts.map +1 -1
  138. package/dist/cmd/dev/index.js +298 -61
  139. package/dist/cmd/dev/index.js.map +1 -1
  140. package/dist/cmd/dev/skills.d.ts +10 -0
  141. package/dist/cmd/dev/skills.d.ts.map +1 -0
  142. package/dist/cmd/dev/skills.js +57 -0
  143. package/dist/cmd/dev/skills.js.map +1 -0
  144. package/dist/cmd/dev/sync.d.ts.map +1 -1
  145. package/dist/cmd/dev/sync.js +19 -3
  146. package/dist/cmd/dev/sync.js.map +1 -1
  147. package/dist/cmd/index.d.ts.map +1 -1
  148. package/dist/cmd/index.js +1 -0
  149. package/dist/cmd/index.js.map +1 -1
  150. package/dist/cmd/project/create.d.ts.map +1 -1
  151. package/dist/cmd/project/create.js +3 -0
  152. package/dist/cmd/project/create.js.map +1 -1
  153. package/dist/cmd/project/template-flow.d.ts +1 -0
  154. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  155. package/dist/cmd/project/template-flow.js +30 -5
  156. package/dist/cmd/project/template-flow.js.map +1 -1
  157. package/dist/cmd/setup/index.d.ts.map +1 -1
  158. package/dist/cmd/setup/index.js +1 -0
  159. package/dist/cmd/setup/index.js.map +1 -1
  160. package/dist/cmd/upgrade/index.d.ts +15 -0
  161. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  162. package/dist/cmd/upgrade/index.js +59 -4
  163. package/dist/cmd/upgrade/index.js.map +1 -1
  164. package/dist/config.d.ts.map +1 -1
  165. package/dist/config.js +8 -0
  166. package/dist/config.js.map +1 -1
  167. package/dist/domain.d.ts +45 -0
  168. package/dist/domain.d.ts.map +1 -0
  169. package/dist/domain.js +200 -0
  170. package/dist/domain.js.map +1 -0
  171. package/dist/index.d.ts +0 -1
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +0 -1
  174. package/dist/index.js.map +1 -1
  175. package/dist/schema-generator.d.ts +2 -0
  176. package/dist/schema-generator.d.ts.map +1 -1
  177. package/dist/schema-generator.js +18 -0
  178. package/dist/schema-generator.js.map +1 -1
  179. package/dist/steps.d.ts +1 -1
  180. package/dist/steps.d.ts.map +1 -1
  181. package/dist/steps.js +16 -5
  182. package/dist/steps.js.map +1 -1
  183. package/dist/tui/prompt.d.ts +1 -2
  184. package/dist/tui/prompt.d.ts.map +1 -1
  185. package/dist/tui/prompt.js +8 -4
  186. package/dist/tui/prompt.js.map +1 -1
  187. package/dist/tui.d.ts +16 -0
  188. package/dist/tui.d.ts.map +1 -1
  189. package/dist/tui.js +23 -2
  190. package/dist/tui.js.map +1 -1
  191. package/dist/types.d.ts +9 -2
  192. package/dist/types.d.ts.map +1 -1
  193. package/dist/types.js +3 -3
  194. package/dist/types.js.map +1 -1
  195. package/package.json +5 -8
  196. package/src/api.ts +1 -1
  197. package/src/cli.ts +47 -12
  198. package/src/cmd/ai/index.ts +6 -1
  199. package/src/cmd/ai/prompt/agent.md +306 -0
  200. package/src/cmd/ai/prompt/agent.ts +12 -322
  201. package/src/cmd/ai/prompt/api.md +360 -0
  202. package/src/cmd/ai/prompt/api.ts +13 -260
  203. package/src/cmd/ai/prompt/version.ts +61 -0
  204. package/src/cmd/ai/prompt/web.md +509 -0
  205. package/src/cmd/ai/prompt/web.ts +12 -282
  206. package/src/cmd/ai/skills/generate.ts +75 -0
  207. package/src/cmd/ai/skills/generator.ts +519 -0
  208. package/src/cmd/ai/skills/index.ts +23 -0
  209. package/src/cmd/auth/signup.ts +1 -0
  210. package/src/cmd/build/ast.ts +161 -48
  211. package/src/cmd/build/entry-generator.ts +258 -187
  212. package/src/cmd/build/vite/agent-discovery.ts +151 -58
  213. package/src/cmd/build/vite/bun-dev-server.ts +57 -27
  214. package/src/cmd/build/vite/docs-generator.ts +87 -0
  215. package/src/cmd/build/vite/index.ts +9 -7
  216. package/src/cmd/build/vite/lifecycle-generator.ts +19 -5
  217. package/src/cmd/build/vite/metadata-generator.ts +251 -7
  218. package/src/cmd/build/vite/prompt-generator.ts +169 -0
  219. package/src/cmd/build/vite/registry-generator.ts +750 -108
  220. package/src/cmd/build/vite/route-discovery.ts +4 -0
  221. package/src/cmd/build/vite/server-bundler.ts +73 -23
  222. package/src/cmd/build/vite/vite-asset-server-config.ts +5 -0
  223. package/src/cmd/build/vite/vite-builder.ts +134 -100
  224. package/src/cmd/build/vite-bundler.ts +6 -6
  225. package/src/cmd/cloud/deploy.ts +114 -36
  226. package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -1
  227. package/src/cmd/cloud/keyvalue/delete-namespace.ts +3 -1
  228. package/src/cmd/cloud/keyvalue/delete.ts +3 -1
  229. package/src/cmd/cloud/keyvalue/set.ts +4 -2
  230. package/src/cmd/cloud/stream/get.ts +2 -9
  231. package/src/cmd/cloud/vector/delete-namespace.ts +89 -0
  232. package/src/cmd/cloud/vector/index.ts +21 -4
  233. package/src/cmd/cloud/vector/list-namespaces.ts +46 -0
  234. package/src/cmd/cloud/vector/stats.ts +160 -0
  235. package/src/cmd/cloud/vector/upsert.ts +216 -0
  236. package/src/cmd/dev/file-watcher.ts +109 -34
  237. package/src/cmd/dev/index.ts +364 -60
  238. package/src/cmd/dev/skills.ts +82 -0
  239. package/src/cmd/dev/sync.ts +41 -6
  240. package/src/cmd/index.ts +1 -0
  241. package/src/cmd/project/create.ts +3 -0
  242. package/src/cmd/project/template-flow.ts +37 -5
  243. package/src/cmd/setup/index.ts +1 -0
  244. package/src/cmd/upgrade/index.ts +68 -4
  245. package/src/config.ts +9 -0
  246. package/src/domain.ts +273 -0
  247. package/src/index.ts +0 -5
  248. package/src/runtime-bootstrap.md +1 -1
  249. package/src/schema-generator.ts +23 -0
  250. package/src/steps.ts +16 -5
  251. package/src/tui/prompt.ts +11 -5
  252. package/src/tui.ts +21 -2
  253. package/src/types/md.d.ts +8 -0
  254. package/src/types.ts +12 -3
  255. package/dist/cmd/cloud/domain.d.ts +0 -17
  256. package/dist/cmd/cloud/domain.d.ts.map +0 -1
  257. package/dist/cmd/cloud/domain.js +0 -79
  258. package/dist/cmd/cloud/domain.js.map +0 -1
  259. package/dist/runtime-bootstrap.d.ts +0 -56
  260. package/dist/runtime-bootstrap.d.ts.map +0 -1
  261. package/dist/runtime-bootstrap.js +0 -95
  262. package/dist/runtime-bootstrap.js.map +0 -1
  263. package/src/cmd/cloud/domain.ts +0 -100
  264. package/src/runtime-bootstrap.ts +0 -131
@@ -22,13 +22,12 @@ interface GenerateEntryOptions {
22
22
  */
23
23
  export async function generateEntryFile(options: GenerateEntryOptions): Promise<void> {
24
24
  const { rootDir, projectId, deploymentId, logger, mode, workbench, vitePort } = options;
25
- const isDev = mode === 'dev';
26
25
 
27
26
  const srcDir = join(rootDir, 'src');
28
- const agentuityDir = join(rootDir, '.agentuity');
29
- const entryPath = join(agentuityDir, 'app.generated.ts');
27
+ const generatedDir = join(srcDir, 'generated');
28
+ const entryPath = join(generatedDir, 'app.ts');
30
29
 
31
- logger.trace(`Generating ${mode} mode entry file...`);
30
+ logger.trace(`Generating unified entry file (supports both dev and prod modes)...`);
32
31
 
33
32
  // Discover routes to determine which files need to be imported
34
33
  const { routeInfoList } = await discoverRoutes(srcDir, projectId, deploymentId, logger);
@@ -54,6 +53,7 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
54
53
  ` createCorsMiddleware,`,
55
54
  ` createOtelMiddleware,`,
56
55
  ` createAgentMiddleware,`,
56
+ ` createCompressionMiddleware,`,
57
57
  ` getAppState,`,
58
58
  ` getAppConfig,`,
59
59
  ` register,`,
@@ -77,12 +77,14 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
77
77
  ...runtimeImports,
78
78
  `} from '@agentuity/runtime';`,
79
79
  `import type { Context } from 'hono';`,
80
- `import { websocket } from 'hono/bun';`, // Always use Bun WebSocket (dev and prod)
81
- !isDev && hasWebFrontend ? `import { serveStatic } from 'hono/bun';` : '',
80
+ `import { websocket } from 'hono/bun';`,
81
+ // Conditionally import serveStatic and readFileSync for web frontend or workbench support
82
+ hasWebFrontend || hasWorkbench ? `import { serveStatic } from 'hono/bun';` : '',
83
+ hasWebFrontend || hasWorkbench ? `import { readFileSync, existsSync } from 'node:fs';` : '',
82
84
  ].filter(Boolean);
83
85
 
84
86
  imports.push(`import { type LogLevel } from '@agentuity/core';`);
85
- imports.push(`import { bootstrapRuntimeEnv } from '@agentuity/cli/runtime-bootstrap';`);
87
+ imports.push(`import { bootstrapRuntimeEnv } from '@agentuity/runtime';`);
86
88
 
87
89
  // Generate route mounting code for all discovered routes
88
90
  const routeImportsAndMounts: string[] = [];
@@ -107,7 +109,7 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
107
109
 
108
110
  const importName = `router_${routeIndex++}`;
109
111
  routeImportsAndMounts.push(
110
- `const { default: ${importName} } = await import('../src/api/${relativePath}.js');`
112
+ `const { default: ${importName} } = await import('../api/${relativePath}.js');`
111
113
  );
112
114
  routeImportsAndMounts.push(`app.route('${mountPath}', ${importName});`);
113
115
  }
@@ -129,185 +131,202 @@ app.route('/', workbenchRouter);
129
131
  `
130
132
  : '';
131
133
 
132
- // Asset proxy routes (dev mode only - proxy to Vite asset server)
134
+ // Asset proxy routes - generated for dev mode, reads VITE_PORT from env at runtime
133
135
  const assetProxyRoutes =
134
- isDev && vitePort
136
+ mode === 'dev'
135
137
  ? `
136
- // Asset proxy routes - Forward Vite-specific requests to asset server
137
- const VITE_ASSET_PORT = ${vitePort};
138
-
139
- const proxyToVite = async (c: Context) => {
140
- const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${c.req.path}\`;
141
- const controller = new AbortController();
142
- const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout
143
- try {
144
- otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\`);
145
- const res = await fetch(viteUrl, { signal: controller.signal });
146
- clearTimeout(timeout);
147
- otel.logger.debug(\`[Proxy] \${c.req.path} -> \${res.status} (\${res.headers.get('content-type')})\`);
148
- return new Response(res.body, {
149
- status: res.status,
150
- headers: res.headers,
151
- });
152
- } catch (err) {
153
- clearTimeout(timeout);
154
- if (err instanceof Error && err.name === 'AbortError') {
155
- otel.logger.error(\`Vite proxy timeout: \${c.req.path}\`);
156
- return c.text('Vite asset server timeout', 504);
138
+ // Asset proxy routes - Development mode only (proxies to Vite asset server)
139
+ if (isDevelopment() && process.env.VITE_PORT) {
140
+ const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT, 10);
141
+
142
+ const proxyToVite = async (c: Context) => {
143
+ const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${c.req.path}\`;
144
+ const controller = new AbortController();
145
+ const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout
146
+ try {
147
+ otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\`);
148
+ const res = await fetch(viteUrl, { signal: controller.signal });
149
+ clearTimeout(timeout);
150
+ otel.logger.debug(\`[Proxy] \${c.req.path} -> \${res.status} (\${res.headers.get('content-type')})\`);
151
+ return new Response(res.body, {
152
+ status: res.status,
153
+ headers: res.headers,
154
+ });
155
+ } catch (err) {
156
+ clearTimeout(timeout);
157
+ if (err instanceof Error && err.name === 'AbortError') {
158
+ otel.logger.error(\`Vite proxy timeout: \${c.req.path}\`);
159
+ return c.text('Vite asset server timeout', 504);
160
+ }
161
+ otel.logger.error(\`Failed to proxy to Vite: \${c.req.path} - \${err instanceof Error ? err.message : String(err)}\`);
162
+ return c.text('Vite asset server error', 500);
157
163
  }
158
- otel.logger.error(\`Failed to proxy to Vite: \${c.req.path} - \${err instanceof Error ? err.message : String(err)}\`);
159
- return c.text('Vite asset server error', 500);
160
- }
161
- };
164
+ };
162
165
 
163
- // Vite client scripts and HMR
164
- app.get('/@vite/*', proxyToVite);
165
- app.get('/@react-refresh', proxyToVite);
166
+ // Vite client scripts and HMR
167
+ app.get('/@vite/*', proxyToVite);
168
+ app.get('/@react-refresh', proxyToVite);
166
169
 
167
- // Source files for HMR
168
- app.get('/src/web/*', proxyToVite);
169
- app.get('/src/*', proxyToVite); // Catch-all for other source files
170
+ // Source files for HMR
171
+ app.get('/src/web/*', proxyToVite);
172
+ app.get('/src/*', proxyToVite); // Catch-all for other source files
170
173
 
171
- // Workbench source files (in .agentuity/workbench-src/)
172
- app.get('/.agentuity/workbench-src/*', proxyToVite);
174
+ // Workbench source files (in .agentuity/workbench-src/)
175
+ app.get('/.agentuity/workbench-src/*', proxyToVite);
173
176
 
174
- // Node modules (Vite transforms these)
175
- app.get('/node_modules/*', proxyToVite);
177
+ // Node modules (Vite transforms these)
178
+ app.get('/node_modules/*', proxyToVite);
176
179
 
177
- // Scoped packages (e.g., @agentuity/*, @types/*)
178
- app.get('/@*', proxyToVite);
180
+ // Scoped packages (e.g., @agentuity/*, @types/*)
181
+ app.get('/@*', proxyToVite);
179
182
 
180
- // File system access (for Vite's @fs protocol)
181
- app.get('/@fs/*', proxyToVite);
183
+ // File system access (for Vite's @fs protocol)
184
+ app.get('/@fs/*', proxyToVite);
182
185
 
183
- // Module resolution (for Vite's @id protocol)
184
- app.get('/@id/*', proxyToVite);
186
+ // Module resolution (for Vite's @id protocol)
187
+ app.get('/@id/*', proxyToVite);
185
188
 
186
- // Any .js, .jsx, .ts, .tsx files (catch remaining modules)
187
- app.get('/*.js', proxyToVite);
188
- app.get('/*.jsx', proxyToVite);
189
- app.get('/*.ts', proxyToVite);
190
- app.get('/*.tsx', proxyToVite);
191
- app.get('/*.css', proxyToVite);
189
+ // Any .js, .jsx, .ts, .tsx files (catch remaining modules)
190
+ app.get('/*.js', proxyToVite);
191
+ app.get('/*.jsx', proxyToVite);
192
+ app.get('/*.ts', proxyToVite);
193
+ app.get('/*.tsx', proxyToVite);
194
+ app.get('/*.css', proxyToVite);
195
+ }
192
196
  `
193
197
  : '';
194
198
 
195
- // Web routes (different for dev/prod)
199
+ // Runtime mode detection helper (defined at top level for reuse)
200
+ // Dynamic property access prevents Bun.build from inlining NODE_ENV at build time
201
+ const modeDetection = `
202
+ // Runtime mode detection helper
203
+ // Dynamic string concatenation prevents Bun.build from inlining NODE_ENV at build time
204
+ // See: https://github.com/oven-sh/bun/issues/20183
205
+ const getEnv = (key: string) => process.env[key];
206
+ const isDevelopment = () => getEnv('NODE' + '_' + 'ENV') !== 'production';
207
+ `;
208
+
209
+ // Web routes (runtime mode detection)
196
210
  let webRoutes = '';
197
211
  if (hasWebFrontend) {
198
- if (isDev) {
199
- webRoutes = `
200
- // Web routes (dev mode with Vite HMR via proxy)
201
- // Proxy HTML from Vite to let @vitejs/plugin-react handle React Fast Refresh preamble
202
- const devHtmlHandler = async (c: Context) => {
203
- const viteUrl = \`http://127.0.0.1:${vitePort}/src/web/index.html\`;
204
-
205
- try {
206
- otel.logger.debug('[Proxy] GET /src/web/index.html -> Vite:%d', ${vitePort});
207
- const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
208
-
209
- // Get HTML text and transform relative paths to absolute
210
- const html = await res.text();
211
- const transformedHtml = html
212
- .replace(/src="\\.\\//g, 'src="/src/web/')
213
- .replace(/href="\\.\\//g, 'href="/src/web/');
214
-
215
- return new Response(transformedHtml, {
216
- status: res.status,
217
- headers: res.headers,
218
- });
219
- } catch (err) {
220
- otel.logger.error('Failed to proxy HTML to Vite: %s', err instanceof Error ? err.message : String(err));
221
- return c.text('Vite asset server error (HTML)', 500);
222
- }
223
- };
224
- app.get('/', devHtmlHandler);
225
- // 404 for unmatched API/system routes
226
- app.all('/_agentuity/*', (c: Context) => c.notFound());
227
- app.all('/api/*', (c: Context) => c.notFound());
228
- ${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
229
- // SPA fallback - serve index.html for client-side routing
230
- // Asset requests (/*.js, /*.tsx, /*.css, etc.) are handled by Vite proxy routes if present,
231
- // otherwise we check for file extensions to avoid returning HTML for missing assets
232
- app.get('*', (c: Context) => {
233
- const path = c.req.path;
234
- // If path has a file extension and Vite proxy isn't handling it, return 404
235
- // This prevents returning HTML for missing assets like /foo.js
236
- if (${!vitePort} && /\\.[a-zA-Z0-9]+$/.test(path)) {
237
- return c.notFound();
238
- }
239
- return devHtmlHandler(c);
240
- });
241
- `;
242
- } else {
243
- webRoutes = `
244
- // Web routes (production - static files)
245
- import { readFileSync } from 'node:fs';
246
- const indexHtml = readFileSync(import.meta.dir + '/client/index.html', 'utf-8');
247
-
248
- app.get('/', (c: Context) => c.html(indexHtml));
249
-
250
- // Serve static assets from /assets/* (Vite bundled output)
251
- app.use('/assets/*', serveStatic({ root: import.meta.dir + '/client' }));
252
-
253
- // Serve static public assets (favicon.ico, robots.txt, etc. from Vite's public folder)
254
- app.use('/*', serveStatic({ root: import.meta.dir + '/client', rewriteRequestPath: (path) => path }));
255
-
256
- // 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
257
- app.all('/_agentuity/*', (c: Context) => c.notFound());
258
- app.all('/api/*', (c: Context) => c.notFound());
259
- ${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
260
-
261
- // SPA fallback with asset protection
262
- // In production, we need to distinguish between:
263
- // - SPA routes like /dashboard, /users/123 (should return HTML)
264
- // - Missing assets like /foo.js, /bar.css (should return 404)
265
- // We check for file extensions to detect asset requests
266
- app.get('*', (c: Context) => {
267
- const path = c.req.path;
268
- // If path has a file extension, it's likely an asset request
269
- // Return 404 instead of serving HTML
270
- if (/\\.[a-zA-Z0-9]+$/.test(path)) {
271
- return c.notFound();
212
+ webRoutes = `
213
+ // Web routes - Runtime mode detection (dev proxies to Vite, prod serves static)
214
+ if (isDevelopment()) {
215
+ // Development mode: Proxy HTML from Vite to enable React Fast Refresh
216
+ const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT || '${vitePort || 5173}', 10);
217
+
218
+ const devHtmlHandler = async (c: Context) => {
219
+ const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}/src/web/index.html\`;
220
+
221
+ try {
222
+ otel.logger.debug('[Proxy] GET /src/web/index.html -> Vite:%d', VITE_ASSET_PORT);
223
+ const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
224
+
225
+ // Get HTML text and transform relative paths to absolute
226
+ const html = await res.text();
227
+ const transformedHtml = html
228
+ .replace(/src="\\.\\//g, 'src="/src/web/')
229
+ .replace(/href="\\.\\//g, 'href="/src/web/');
230
+
231
+ return new Response(transformedHtml, {
232
+ status: res.status,
233
+ headers: res.headers,
234
+ });
235
+ } catch (err) {
236
+ otel.logger.error('Failed to proxy HTML to Vite: %s', err instanceof Error ? err.message : String(err));
237
+ return c.text('Vite asset server error (HTML)', 500);
238
+ }
239
+ };
240
+
241
+ app.get('/', devHtmlHandler);
242
+
243
+ // 404 for unmatched API/system routes
244
+ app.all('/_agentuity/*', (c: Context) => c.notFound());
245
+ app.all('/api/*', (c: Context) => c.notFound());
246
+ ${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
247
+
248
+ // SPA fallback - serve index.html for client-side routing
249
+ app.get('*', (c: Context) => {
250
+ const path = c.req.path;
251
+ // If path has a file extension, return 404 (prevents serving HTML for missing assets)
252
+ if (/\\.[a-zA-Z0-9]+$/.test(path)) {
253
+ return c.notFound();
254
+ }
255
+ return devHtmlHandler(c);
256
+ });
257
+ } else {
258
+ // Production mode: Serve static files from bundled output
259
+ const indexHtmlPath = import.meta.dir + '/client/index.html';
260
+ const indexHtml = existsSync(indexHtmlPath)
261
+ ? readFileSync(indexHtmlPath, 'utf-8')
262
+ : '';
263
+
264
+ if (!indexHtml) {
265
+ otel.logger.warn('Production HTML not found at %s', indexHtmlPath);
272
266
  }
273
- return c.html(indexHtml);
274
- });
275
- `;
267
+
268
+ app.get('/', (c: Context) => indexHtml ? c.html(indexHtml) : c.text('Production build incomplete', 500));
269
+
270
+ // Serve static assets from /assets/* (Vite bundled output)
271
+ app.use('/assets/*', serveStatic({ root: import.meta.dir + '/client' }));
272
+
273
+ // Serve static public assets (favicon.ico, robots.txt, etc.)
274
+ app.use('/*', serveStatic({ root: import.meta.dir + '/client', rewriteRequestPath: (path) => path }));
275
+
276
+ // 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
277
+ app.all('/_agentuity/*', (c: Context) => c.notFound());
278
+ app.all('/api/*', (c: Context) => c.notFound());
279
+ ${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
280
+
281
+ // SPA fallback with asset protection
282
+ app.get('*', (c: Context) => {
283
+ const path = c.req.path;
284
+ // If path has a file extension, it's likely an asset request - return 404
285
+ if (/\\.[a-zA-Z0-9]+$/.test(path)) {
286
+ return c.notFound();
276
287
  }
288
+ return c.html(indexHtml);
289
+ });
290
+ }
291
+ `;
277
292
  }
278
293
 
279
- // Workbench routes (if enabled)
294
+ // Workbench routes (if enabled) - runtime mode detection
280
295
  const workbenchRoute = workbench?.route ?? '/workbench';
281
- const workbenchSrcDir = join(agentuityDir, 'workbench-src');
282
296
  const workbenchRoutes = hasWorkbench
283
- ? isDev
284
- ? `
285
- // Workbench route (dev mode - let Vite serve source files with HMR)
286
- app.get('${workbenchRoute}', async (c: Context) => {
287
- const html = await Bun.file('${workbenchSrcDir}/index.html').text();
288
- // Rewrite script/css paths to use Vite's @fs protocol
289
- const withVite = html
290
- .replace('src="./main.tsx"', 'src="/@fs${workbenchSrcDir}/main.tsx"')
291
- .replace('href="./styles.css"', 'href="/@fs${workbenchSrcDir}/styles.css"');
292
- return c.html(withVite);
293
- });
294
- `
295
- : `
296
- // Workbench routes (production - serve pre-built assets)
297
- // Use import.meta.dir for absolute paths (app.js runs from .agentuity/)
298
- import { readFileSync, existsSync } from 'node:fs';
297
+ ? `
298
+ // Workbench routes - Runtime mode detection
299
+ // Both dev and prod run from .agentuity/app.js (dev bundles before running)
300
+ // So workbench-src is always in the same directory
301
+ const workbenchSrcDir = import.meta.dir + '/workbench-src';
299
302
  const workbenchIndexPath = import.meta.dir + '/workbench/index.html';
300
- if (existsSync(workbenchIndexPath)) {
301
- const workbenchIndex = readFileSync(workbenchIndexPath, 'utf-8');
302
- app.get('${workbenchRoute}', (c: Context) => c.html(workbenchIndex));
303
- app.get('${workbenchRoute}/*', serveStatic({ root: import.meta.dir + '/workbench' }));
303
+ const workbenchIndex = existsSync(workbenchIndexPath)
304
+ ? readFileSync(workbenchIndexPath, 'utf-8')
305
+ : '';
306
+
307
+ if (isDevelopment()) {
308
+ // Development mode: Let Vite serve source files with HMR
309
+ app.get('${workbenchRoute}', async (c: Context) => {
310
+ const html = await Bun.file(workbenchSrcDir + '/index.html').text();
311
+ // Rewrite script/css paths to use Vite's @fs protocol
312
+ const withVite = html
313
+ .replace('src="./main.tsx"', \`src="/@fs\${workbenchSrcDir}/main.tsx"\`)
314
+ .replace('href="./styles.css"', \`href="/@fs\${workbenchSrcDir}/styles.css"\`);
315
+ return c.html(withVite);
316
+ });
317
+ } else {
318
+ // Production mode: Serve pre-built assets
319
+ if (workbenchIndex) {
320
+ app.get('${workbenchRoute}', (c: Context) => c.html(workbenchIndex));
321
+ app.get('${workbenchRoute}/*', serveStatic({ root: import.meta.dir + '/workbench' }));
322
+ }
304
323
  }
305
324
  `
306
325
  : '';
307
326
 
308
327
  // Server startup (same for dev and prod - Bun.serve with native WebSocket)
309
328
  const serverStartup = `
310
- // Start Bun server${isDev ? ' (dev mode with Vite asset proxy)' : ''}
329
+ // Start Bun server
311
330
  if (typeof Bun !== 'undefined') {
312
331
  // Enable process exit protection now that we're starting the server
313
332
  enableProcessExitProtection();
@@ -323,20 +342,86 @@ if (typeof Bun !== 'undefined') {
323
342
  // Make server available globally for health checks
324
343
  (globalThis as any).__AGENTUITY_SERVER__ = server;
325
344
 
326
- otel.logger.info(\`Server listening on http://127.0.0.1:\${port}\`);${isDev && vitePort ? `\n\totel.logger.debug(\`Proxying Vite assets from port ${vitePort}\`);` : ''}
345
+ otel.logger.info(\`Server listening on http://127.0.0.1:\${port}\`);
346
+ if (isDevelopment() && process.env.VITE_PORT) {
347
+ otel.logger.debug(\`Proxying Vite assets from port \${process.env.VITE_PORT}\`);
348
+ }
327
349
  }
350
+
351
+ // FOUND AN ERROR IN THIS FILE?
352
+ // Please file an issue at https://github.com/agentuity/sdk/issues
353
+ // or if you know the fix please submit a PR!
328
354
  `;
329
355
 
330
- const code = `// Auto-generated by Agentuity for ${mode} mode
356
+ const healthRoutes = `
357
+ // Health check routes (production only)
358
+ if (!isDevelopment()) {
359
+ const healthHandler = (c: Context) => {
360
+ return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
361
+ };
362
+ const idleHandler = (c: Context) => {
363
+ // Check if server is idle (no pending requests/connections)
364
+ const server = (globalThis as any).__AGENTUITY_SERVER__;
365
+ if (!server) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
366
+
367
+ // Check for pending background tasks
368
+ if (hasWaitUntilPending()) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
369
+
370
+ if (server.pendingRequests > 1) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
371
+ if (server.pendingWebSockets > 0) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
372
+
373
+ return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
374
+ };
375
+ app.get('/_agentuity/health', healthHandler);
376
+ app.get('/_health', healthHandler);
377
+ app.get('/_agentuity/idle', idleHandler);
378
+ app.get('/_idle', idleHandler);
379
+ }
380
+
381
+ // Dev readiness check - verifies Vite asset server is ready to serve frontend
382
+ if (isDevelopment()) {
383
+ app.get('/_agentuity/ready', async (c: Context) => {
384
+ const vitePort = process.env.VITE_PORT;
385
+ if (!vitePort) {
386
+ // No Vite port means we're not using Vite proxy
387
+ return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
388
+ }
389
+
390
+ try {
391
+ // Probe Vite to check if it can serve the main entry point
392
+ // Use @vite/client as a lightweight check - it's always available
393
+ const viteUrl = \`http://127.0.0.1:\${vitePort}/@vite/client\`;
394
+ const res = await fetch(viteUrl, {
395
+ signal: AbortSignal.timeout(5000),
396
+ method: 'HEAD'
397
+ });
398
+
399
+ if (res.ok) {
400
+ return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
401
+ }
402
+ return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
403
+ } catch (err) {
404
+ otel.logger.debug('Vite readiness check failed: %s', err instanceof Error ? err.message : String(err));
405
+ return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
406
+ }
407
+ });
408
+ }
409
+ `;
410
+
411
+ const code = `// @generated
412
+ // Auto-generated by Agentuity
331
413
  // DO NOT EDIT - This file is regenerated on every build
414
+ // Supports both development and production modes via runtime detection
332
415
  ${imports.join('\n')}
333
416
 
417
+ ${modeDetection}
418
+
334
419
  // Step 0: Bootstrap runtime environment (load profile-specific .env files)
335
420
  // Only in development - production env vars are injected by platform
336
421
  // This must happen BEFORE any imports that depend on environment variables
337
- if (process.env.NODE_ENV !== 'production') {
338
- // Pass project directory (parent of .agentuity/) so .env files are loaded correctly
339
- await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/..' });
422
+ if (isDevelopment()) {
423
+ // Pass project directory (two levels up from src/generated/) so .env files are loaded correctly
424
+ await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/../..' });
340
425
  }
341
426
 
342
427
  // Step 1: Initialize telemetry and services
@@ -358,6 +443,9 @@ const app = createRouter();
358
443
  setGlobalRouter(app);
359
444
 
360
445
  // Step 3: Apply middleware in correct order (BEFORE mounting routes)
446
+ // Compression runs first (outermost) so it can compress the final response
447
+ app.use('*', createCompressionMiddleware());
448
+
361
449
  app.use('*', createBaseMiddleware({
362
450
  logger: otel.logger,
363
451
  tracer: otel.tracer,
@@ -375,7 +463,7 @@ app.use('/api/*', createOtelMiddleware());
375
463
  app.use('/api/*', createAgentMiddleware(''));
376
464
 
377
465
  // Step 4: Import user's app.ts (runs createApp, gets state/config)
378
- await import('../app.ts');
466
+ await import('../../app.js');
379
467
 
380
468
  // Step 5: Initialize providers
381
469
  const threadProvider = getThreadProvider();
@@ -386,27 +474,7 @@ await sessionProvider.initialize(appState);
386
474
 
387
475
  // Step 6: Mount routes (AFTER middleware is applied)
388
476
 
389
- // System health/idle endpoints
390
- const healthHandler = (c: Context) => c.text('OK');
391
- const idleHandler = (c: Context) => {
392
- // Check if server is idle (no pending requests/connections)
393
- const server = (globalThis as any).__AGENTUITY_SERVER__;
394
- if (!server) return c.text('NO', { status: 200 });
395
-
396
- // Check for pending background tasks
397
- if (hasWaitUntilPending()) return c.text('NO', { status: 200 });
398
-
399
- if (server.pendingRequests > 1) return c.text('NO', { status: 200 });
400
- if (server.pendingWebSockets > 0) return c.text('NO', { status: 200 });
401
-
402
- return c.text('OK', { status: 200 });
403
- };
404
-
405
- app.get('/_agentuity/health', healthHandler);
406
- app.get('/_health', healthHandler);
407
- app.get('/_agentuity/idle', idleHandler);
408
- app.get('/_idle', idleHandler);
409
-
477
+ ${healthRoutes}
410
478
  ${assetProxyRoutes}
411
479
  ${apiMount}
412
480
  ${workbenchApiMount}
@@ -419,6 +487,9 @@ await runAgentSetups(appState);
419
487
  ${serverStartup}
420
488
  `;
421
489
 
422
- await Bun.write(entryPath, code);
423
- logger.trace(`Generated ${mode} mode entry file at %s`, entryPath);
490
+ // Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
491
+ const cleanedCode = code.replace(/\n{3,}/g, '\n\n');
492
+
493
+ await Bun.write(entryPath, cleanedCode);
494
+ logger.trace(`Generated unified entry file at %s (mode: ${mode})`, entryPath);
424
495
  }