@agentuity/cli 0.0.95 → 0.0.96

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 (267) hide show
  1. package/AGENTS.md +54 -0
  2. package/dist/auth.d.ts +1 -1
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +8 -5
  5. package/dist/auth.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +190 -27
  8. package/dist/cli.js.map +1 -1
  9. package/dist/cmd/build/entry-generator.d.ts +20 -0
  10. package/dist/cmd/build/entry-generator.d.ts.map +1 -0
  11. package/dist/cmd/build/entry-generator.js +366 -0
  12. package/dist/cmd/build/entry-generator.js.map +1 -0
  13. package/dist/cmd/build/index.d.ts.map +1 -1
  14. package/dist/cmd/build/index.js +5 -23
  15. package/dist/cmd/build/index.js.map +1 -1
  16. package/dist/cmd/build/vite/agent-discovery.d.ts +33 -0
  17. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -0
  18. package/dist/cmd/build/vite/agent-discovery.js +297 -0
  19. package/dist/cmd/build/vite/agent-discovery.js.map +1 -0
  20. package/dist/cmd/build/vite/browser-env-plugin.d.ts +9 -0
  21. package/dist/cmd/build/vite/browser-env-plugin.d.ts.map +1 -0
  22. package/dist/cmd/build/vite/browser-env-plugin.js +28 -0
  23. package/dist/cmd/build/vite/browser-env-plugin.js.map +1 -0
  24. package/dist/cmd/build/vite/bun-dev-server.d.ts +29 -0
  25. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -0
  26. package/dist/cmd/build/vite/bun-dev-server.js +54 -0
  27. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -0
  28. package/dist/cmd/build/vite/config-loader.d.ts +23 -0
  29. package/dist/cmd/build/vite/config-loader.d.ts.map +1 -0
  30. package/dist/cmd/build/vite/config-loader.js +50 -0
  31. package/dist/cmd/build/vite/config-loader.js.map +1 -0
  32. package/dist/cmd/build/vite/index.d.ts +26 -0
  33. package/dist/cmd/build/vite/index.d.ts.map +1 -0
  34. package/dist/cmd/build/vite/index.js +127 -0
  35. package/dist/cmd/build/vite/index.js.map +1 -0
  36. package/dist/cmd/build/vite/lifecycle-generator.d.ts +11 -0
  37. package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -0
  38. package/dist/cmd/build/vite/lifecycle-generator.js +35 -0
  39. package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -0
  40. package/dist/cmd/build/vite/metadata-generator.d.ts +32 -0
  41. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -0
  42. package/dist/cmd/build/vite/metadata-generator.js +489 -0
  43. package/dist/cmd/build/vite/metadata-generator.js.map +1 -0
  44. package/dist/cmd/build/vite/patch-plugin.d.ts +21 -0
  45. package/dist/cmd/build/vite/patch-plugin.d.ts.map +1 -0
  46. package/dist/cmd/build/vite/patch-plugin.js +70 -0
  47. package/dist/cmd/build/vite/patch-plugin.js.map +1 -0
  48. package/dist/cmd/build/vite/registry-generator.d.ts +19 -0
  49. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -0
  50. package/dist/cmd/build/{route-registry.js → vite/registry-generator.js} +126 -48
  51. package/dist/cmd/build/vite/registry-generator.js.map +1 -0
  52. package/dist/cmd/build/vite/route-discovery.d.ts +58 -0
  53. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -0
  54. package/dist/cmd/build/vite/route-discovery.js +125 -0
  55. package/dist/cmd/build/vite/route-discovery.js.map +1 -0
  56. package/dist/cmd/build/vite/server-bundler.d.ts +16 -0
  57. package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -0
  58. package/dist/cmd/build/vite/server-bundler.js +194 -0
  59. package/dist/cmd/build/vite/server-bundler.js.map +1 -0
  60. package/dist/cmd/build/vite/vite-asset-server-config.d.ts +19 -0
  61. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -0
  62. package/dist/cmd/build/vite/vite-asset-server-config.js +105 -0
  63. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -0
  64. package/dist/cmd/build/vite/vite-asset-server.d.ts +23 -0
  65. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -0
  66. package/dist/cmd/build/vite/vite-asset-server.js +40 -0
  67. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -0
  68. package/dist/cmd/build/vite/vite-builder.d.ts +44 -0
  69. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -0
  70. package/dist/cmd/build/vite/vite-builder.js +232 -0
  71. package/dist/cmd/build/vite/vite-builder.js.map +1 -0
  72. package/dist/cmd/build/vite/workbench-generator.d.ts +10 -0
  73. package/dist/cmd/build/vite/workbench-generator.d.ts.map +1 -0
  74. package/dist/cmd/build/vite/workbench-generator.js +135 -0
  75. package/dist/cmd/build/vite/workbench-generator.js.map +1 -0
  76. package/dist/cmd/build/vite-bundler.d.ts +23 -0
  77. package/dist/cmd/build/vite-bundler.d.ts.map +1 -0
  78. package/dist/cmd/build/vite-bundler.js +79 -0
  79. package/dist/cmd/build/vite-bundler.js.map +1 -0
  80. package/dist/cmd/cloud/agent/get.d.ts.map +1 -1
  81. package/dist/cmd/cloud/agent/get.js +1 -0
  82. package/dist/cmd/cloud/agent/get.js.map +1 -1
  83. package/dist/cmd/cloud/agent/list.d.ts.map +1 -1
  84. package/dist/cmd/cloud/agent/list.js +1 -0
  85. package/dist/cmd/cloud/agent/list.js.map +1 -1
  86. package/dist/cmd/cloud/db/get.d.ts.map +1 -1
  87. package/dist/cmd/cloud/db/get.js +1 -0
  88. package/dist/cmd/cloud/db/get.js.map +1 -1
  89. package/dist/cmd/cloud/db/list.d.ts.map +1 -1
  90. package/dist/cmd/cloud/db/list.js +1 -0
  91. package/dist/cmd/cloud/db/list.js.map +1 -1
  92. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  93. package/dist/cmd/cloud/deploy.js +152 -128
  94. package/dist/cmd/cloud/deploy.js.map +1 -1
  95. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  96. package/dist/cmd/cloud/deployment/list.js +4 -0
  97. package/dist/cmd/cloud/deployment/list.js.map +1 -1
  98. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  99. package/dist/cmd/cloud/env/list.js +1 -0
  100. package/dist/cmd/cloud/env/list.js.map +1 -1
  101. package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
  102. package/dist/cmd/cloud/keyvalue/get.js +1 -0
  103. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  104. package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
  105. package/dist/cmd/cloud/keyvalue/keys.js +1 -0
  106. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  107. package/dist/cmd/cloud/keyvalue/list-namespaces.d.ts.map +1 -1
  108. package/dist/cmd/cloud/keyvalue/list-namespaces.js +1 -0
  109. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  110. package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
  111. package/dist/cmd/cloud/keyvalue/stats.js +1 -0
  112. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  113. package/dist/cmd/cloud/secret/list.d.ts.map +1 -1
  114. package/dist/cmd/cloud/secret/list.js +1 -0
  115. package/dist/cmd/cloud/secret/list.js.map +1 -1
  116. package/dist/cmd/cloud/session/list.d.ts.map +1 -1
  117. package/dist/cmd/cloud/session/list.js +4 -0
  118. package/dist/cmd/cloud/session/list.js.map +1 -1
  119. package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
  120. package/dist/cmd/cloud/storage/get.js +1 -0
  121. package/dist/cmd/cloud/storage/get.js.map +1 -1
  122. package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
  123. package/dist/cmd/cloud/storage/list.js +3 -0
  124. package/dist/cmd/cloud/storage/list.js.map +1 -1
  125. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  126. package/dist/cmd/cloud/stream/get.js +1 -0
  127. package/dist/cmd/cloud/stream/get.js.map +1 -1
  128. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  129. package/dist/cmd/cloud/stream/list.js +1 -0
  130. package/dist/cmd/cloud/stream/list.js.map +1 -1
  131. package/dist/cmd/cloud/vector/get.d.ts.map +1 -1
  132. package/dist/cmd/cloud/vector/get.js +1 -0
  133. package/dist/cmd/cloud/vector/get.js.map +1 -1
  134. package/dist/cmd/cloud/vector/search.d.ts.map +1 -1
  135. package/dist/cmd/cloud/vector/search.js +1 -0
  136. package/dist/cmd/cloud/vector/search.js.map +1 -1
  137. package/dist/cmd/dev/index.d.ts.map +1 -1
  138. package/dist/cmd/dev/index.js +289 -758
  139. package/dist/cmd/dev/index.js.map +1 -1
  140. package/dist/cmd/dev/sync.d.ts +1 -1
  141. package/dist/cmd/dev/sync.d.ts.map +1 -1
  142. package/dist/cmd/dev/sync.js +3 -0
  143. package/dist/cmd/dev/sync.js.map +1 -1
  144. package/dist/cmd/profile/show.d.ts.map +1 -1
  145. package/dist/cmd/profile/show.js +3 -4
  146. package/dist/cmd/profile/show.js.map +1 -1
  147. package/dist/cmd/project/create.d.ts.map +1 -1
  148. package/dist/cmd/project/create.js +3 -4
  149. package/dist/cmd/project/create.js.map +1 -1
  150. package/dist/cmd/project/list.d.ts.map +1 -1
  151. package/dist/cmd/project/list.js +1 -0
  152. package/dist/cmd/project/list.js.map +1 -1
  153. package/dist/cmd/project/show.d.ts.map +1 -1
  154. package/dist/cmd/project/show.js +1 -0
  155. package/dist/cmd/project/show.js.map +1 -1
  156. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  157. package/dist/cmd/upgrade/index.js +5 -3
  158. package/dist/cmd/upgrade/index.js.map +1 -1
  159. package/dist/config.d.ts +1 -1
  160. package/dist/config.d.ts.map +1 -1
  161. package/dist/config.js +29 -11
  162. package/dist/config.js.map +1 -1
  163. package/dist/index.d.ts +1 -1
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js.map +1 -1
  166. package/dist/runtime-bootstrap.d.ts +3 -2
  167. package/dist/runtime-bootstrap.d.ts.map +1 -1
  168. package/dist/runtime-bootstrap.js +7 -2
  169. package/dist/runtime-bootstrap.js.map +1 -1
  170. package/dist/schemas/deploy.d.ts +1 -1
  171. package/dist/types.d.ts +40 -1
  172. package/dist/types.d.ts.map +1 -1
  173. package/dist/types.js.map +1 -1
  174. package/dist/utils/bun-version-checker.d.ts +11 -0
  175. package/dist/utils/bun-version-checker.d.ts.map +1 -0
  176. package/dist/utils/bun-version-checker.js +56 -0
  177. package/dist/utils/bun-version-checker.js.map +1 -0
  178. package/dist/version-check.d.ts.map +1 -1
  179. package/dist/version-check.js +5 -2
  180. package/dist/version-check.js.map +1 -1
  181. package/package.json +10 -3
  182. package/src/auth.ts +9 -5
  183. package/src/cli.ts +228 -29
  184. package/src/cmd/build/entry-generator.ts +404 -0
  185. package/src/cmd/build/index.ts +7 -28
  186. package/src/cmd/build/vite/agent-discovery.ts +467 -0
  187. package/src/cmd/build/vite/browser-env-plugin.ts +34 -0
  188. package/src/cmd/build/vite/bun-dev-server.ts +78 -0
  189. package/src/cmd/build/vite/config-loader.ts +70 -0
  190. package/src/cmd/build/vite/index.ts +166 -0
  191. package/src/cmd/build/vite/lifecycle-generator.ts +43 -0
  192. package/src/cmd/build/vite/metadata-generator.ts +602 -0
  193. package/src/cmd/build/vite/patch-plugin.ts +88 -0
  194. package/src/cmd/build/vite/registry-generator.ts +288 -0
  195. package/src/cmd/build/vite/route-discovery.ts +186 -0
  196. package/src/cmd/build/vite/server-bundler.ts +258 -0
  197. package/src/cmd/build/vite/vite-asset-server-config.ts +134 -0
  198. package/src/cmd/build/vite/vite-asset-server.ts +63 -0
  199. package/src/cmd/build/vite/vite-builder.ts +284 -0
  200. package/src/cmd/build/vite/workbench-generator.ts +152 -0
  201. package/src/cmd/build/vite-bundler.ts +110 -0
  202. package/src/cmd/cloud/agent/get.ts +2 -0
  203. package/src/cmd/cloud/agent/list.ts +1 -0
  204. package/src/cmd/cloud/db/get.ts +1 -0
  205. package/src/cmd/cloud/db/list.ts +1 -0
  206. package/src/cmd/cloud/deploy.ts +175 -144
  207. package/src/cmd/cloud/deployment/list.ts +4 -0
  208. package/src/cmd/cloud/env/list.ts +1 -0
  209. package/src/cmd/cloud/keyvalue/get.ts +1 -0
  210. package/src/cmd/cloud/keyvalue/keys.ts +1 -0
  211. package/src/cmd/cloud/keyvalue/list-namespaces.ts +1 -0
  212. package/src/cmd/cloud/keyvalue/stats.ts +2 -0
  213. package/src/cmd/cloud/secret/list.ts +1 -0
  214. package/src/cmd/cloud/session/list.ts +4 -0
  215. package/src/cmd/cloud/storage/get.ts +1 -0
  216. package/src/cmd/cloud/storage/list.ts +4 -0
  217. package/src/cmd/cloud/stream/get.ts +1 -0
  218. package/src/cmd/cloud/stream/list.ts +1 -0
  219. package/src/cmd/cloud/vector/get.ts +1 -0
  220. package/src/cmd/cloud/vector/search.ts +1 -0
  221. package/src/cmd/dev/index.ts +319 -921
  222. package/src/cmd/dev/sync.ts +5 -1
  223. package/src/cmd/profile/show.ts +3 -4
  224. package/src/cmd/project/create.ts +3 -4
  225. package/src/cmd/project/list.ts +1 -0
  226. package/src/cmd/project/show.ts +1 -0
  227. package/src/cmd/upgrade/index.ts +6 -3
  228. package/src/config.ts +31 -11
  229. package/src/index.ts +2 -0
  230. package/src/runtime-bootstrap.ts +8 -2
  231. package/src/types.ts +48 -1
  232. package/src/utils/bun-version-checker.ts +70 -0
  233. package/src/version-check.ts +6 -2
  234. package/dist/cmd/build/bundler.d.ts +0 -28
  235. package/dist/cmd/build/bundler.d.ts.map +0 -1
  236. package/dist/cmd/build/bundler.js +0 -800
  237. package/dist/cmd/build/bundler.js.map +0 -1
  238. package/dist/cmd/build/config-loader.d.ts +0 -16
  239. package/dist/cmd/build/config-loader.d.ts.map +0 -1
  240. package/dist/cmd/build/config-loader.js +0 -227
  241. package/dist/cmd/build/config-loader.js.map +0 -1
  242. package/dist/cmd/build/file.d.ts +0 -2
  243. package/dist/cmd/build/file.d.ts.map +0 -1
  244. package/dist/cmd/build/file.js +0 -10
  245. package/dist/cmd/build/file.js.map +0 -1
  246. package/dist/cmd/build/fix-duplicate-exports.d.ts +0 -2
  247. package/dist/cmd/build/fix-duplicate-exports.d.ts.map +0 -1
  248. package/dist/cmd/build/fix-duplicate-exports.js +0 -170
  249. package/dist/cmd/build/fix-duplicate-exports.js.map +0 -1
  250. package/dist/cmd/build/plugin.d.ts +0 -6
  251. package/dist/cmd/build/plugin.d.ts.map +0 -1
  252. package/dist/cmd/build/plugin.js +0 -645
  253. package/dist/cmd/build/plugin.js.map +0 -1
  254. package/dist/cmd/build/route-discovery.d.ts +0 -54
  255. package/dist/cmd/build/route-discovery.d.ts.map +0 -1
  256. package/dist/cmd/build/route-discovery.js +0 -148
  257. package/dist/cmd/build/route-discovery.js.map +0 -1
  258. package/dist/cmd/build/route-registry.d.ts +0 -38
  259. package/dist/cmd/build/route-registry.d.ts.map +0 -1
  260. package/dist/cmd/build/route-registry.js.map +0 -1
  261. package/src/cmd/build/bundler.ts +0 -965
  262. package/src/cmd/build/config-loader.ts +0 -268
  263. package/src/cmd/build/file.ts +0 -10
  264. package/src/cmd/build/fix-duplicate-exports.ts +0 -207
  265. package/src/cmd/build/plugin.ts +0 -782
  266. package/src/cmd/build/route-discovery.ts +0 -202
  267. package/src/cmd/build/route-registry.ts +0 -222
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Registry Generator
3
+ *
4
+ * Generates .agentuity/registry.generated.ts from discovered agents
5
+ */
6
+
7
+ import { join } from 'node:path';
8
+ import { writeFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
9
+ import { StructuredError } from '@agentuity/core';
10
+ import { toCamelCase, toPascalCase } from '../../../utils/string';
11
+ import type { AgentMetadata } from './agent-discovery';
12
+ import type { RouteInfo } from './route-discovery';
13
+
14
+ const AgentIdentifierCollisionError = StructuredError('AgentIdentifierCollisionError');
15
+
16
+ /**
17
+ * Generate .agentuity/registry.generated.ts with agent registry and types
18
+ */
19
+ export function generateAgentRegistry(srcDir: string, agents: AgentMetadata[]): void {
20
+ const projectRoot = join(srcDir, '..');
21
+ const agentuityDir = join(projectRoot, '.agentuity');
22
+ const registryPath = join(agentuityDir, 'registry.generated.ts');
23
+
24
+ // Detect naming collisions in generated identifiers
25
+ const generatedNames = new Set<string>();
26
+ const collisions: string[] = [];
27
+
28
+ for (const agent of agents) {
29
+ const camelName = toCamelCase(agent.name);
30
+
31
+ if (generatedNames.has(camelName)) {
32
+ collisions.push(`Identifier collision detected: "${camelName}" (from "${agent.name}")`);
33
+ }
34
+ generatedNames.add(camelName);
35
+ }
36
+
37
+ if (collisions.length > 0) {
38
+ throw new AgentIdentifierCollisionError({
39
+ message:
40
+ `Agent identifier naming collisions detected:\n${collisions.join('\n')}\n\n` +
41
+ `This occurs when different agent names produce the same camelCase identifier.\n` +
42
+ `Please rename your agents to avoid this collision.`,
43
+ });
44
+ }
45
+
46
+ // Generate imports for all agents
47
+ const imports = agents
48
+ .map(({ name, filename }) => {
49
+ const camelName = toCamelCase(name);
50
+ // Handle both './agent/...' and 'src/agent/...' formats
51
+ let relativePath = filename;
52
+ if (relativePath.startsWith('./agent/')) {
53
+ relativePath = relativePath.replace(/^\.\/agent\//, '../src/agent/');
54
+ } else if (relativePath.startsWith('src/agent/')) {
55
+ relativePath = '../' + relativePath;
56
+ }
57
+ return `import ${camelName} from '${relativePath}';`;
58
+ })
59
+ .join('\n');
60
+
61
+ // Generate flat registry structure
62
+ const registry = agents
63
+ .map(({ name }) => {
64
+ const camelName = toCamelCase(name);
65
+ return ` ${camelName}: ${camelName},`;
66
+ })
67
+ .join('\n');
68
+
69
+ // Generate type exports for all agents
70
+ const typeExports = agents
71
+ .map(({ name }) => {
72
+ const camelName = toCamelCase(name);
73
+ const pascalName = toPascalCase(name);
74
+ return `export type ${pascalName}Runner = AgentRunner<typeof ${camelName}['inputSchema'], typeof ${camelName}['outputSchema'], typeof ${camelName}['stream'] extends true ? true : false>;`;
75
+ })
76
+ .join('\n');
77
+
78
+ // Generate flat agent type definitions for AgentRegistry interface augmentation
79
+ const runtimeAgentTypes = agents
80
+ .map(({ name }) => {
81
+ const camelName = toCamelCase(name);
82
+ return ` ${camelName}: AgentRunner<typeof ${camelName}['inputSchema'], typeof ${camelName}['outputSchema'], typeof ${camelName}['stream'] extends true ? true : false>;`;
83
+ })
84
+ .join('\n');
85
+
86
+ const generatedContent = `/// <reference types="hono" />
87
+ // Auto-generated by Agentuity - do not edit manually
88
+ ${imports}
89
+ import type { AgentRunner, Logger } from '@agentuity/runtime';
90
+ import type { KeyValueStorage, StreamStorage, VectorStorage } from '@agentuity/core';
91
+
92
+ /**
93
+ * Registry of all agents in this application.
94
+ * Provides strongly-typed access to agent metadata and runner functions.
95
+ * Auto-generated from your agent files during build.
96
+ */
97
+ export const agentRegistry = {
98
+ ${registry}
99
+ } as const;
100
+
101
+ // Local type aliases for Hono augmentation
102
+ type LocalAgentName = keyof typeof agentRegistry;
103
+ type LocalAgentRegistry = typeof agentRegistry;
104
+
105
+ // Typed runners for each agent
106
+ ${typeExports}
107
+
108
+ // Augment @agentuity/runtime types with strongly-typed agents from this project
109
+ declare module "@agentuity/runtime" {
110
+ // Augment the AgentRegistry interface with project-specific strongly-typed agents
111
+ export interface AgentRegistry {
112
+ ${runtimeAgentTypes}
113
+ }
114
+ }
115
+
116
+ // NOTE: Hono Context properties are accessed via c.var (e.g., c.var.logger, c.var.kv)
117
+ // The Variables interface in @agentuity/runtime defines all available context properties
118
+ `;
119
+
120
+ const agentsDir = join(srcDir, 'agent');
121
+ const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
122
+
123
+ // Ensure .agentuity directory exists
124
+ if (!existsSync(agentuityDir)) {
125
+ mkdirSync(agentuityDir, { recursive: true });
126
+ }
127
+
128
+ writeFileSync(registryPath, generatedContent, 'utf-8');
129
+
130
+ // Remove legacy types.generated.d.ts if it exists (now consolidated into registry.generated.ts)
131
+ if (existsSync(legacyTypesPath)) {
132
+ unlinkSync(legacyTypesPath);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Generate RouteRegistry type definitions from discovered routes.
138
+ *
139
+ * Creates a module augmentation for @agentuity/react that provides
140
+ * strongly-typed route keys with input/output schema information.
141
+ */
142
+ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void {
143
+ // Filter routes by type
144
+ const apiRoutes = routes.filter((r) => r.routeType === 'api' || r.routeType === 'stream');
145
+ const websocketRoutes = routes.filter((r) => r.routeType === 'websocket');
146
+ const sseRoutes = routes.filter((r) => r.routeType === 'sse');
147
+
148
+ if (apiRoutes.length === 0 && websocketRoutes.length === 0 && sseRoutes.length === 0) {
149
+ return;
150
+ }
151
+
152
+ // Generate imports for agents and schemas
153
+ const imports: string[] = [];
154
+ const agentImports = new Map<string, string>();
155
+ const routeFileImports = new Map<string, Set<string>>();
156
+
157
+ const allRoutes = [...apiRoutes, ...websocketRoutes, ...sseRoutes];
158
+
159
+ // Collect agent imports
160
+ allRoutes.forEach((route) => {
161
+ if (!route.hasValidator) return;
162
+
163
+ if (route.agentVariable && route.agentImportPath && !agentImports.has(route.agentVariable)) {
164
+ let resolvedPath = route.agentImportPath;
165
+
166
+ if (resolvedPath.startsWith('@agent/')) {
167
+ resolvedPath = `../src/agent/${resolvedPath.substring('@agent/'.length)}`;
168
+ } else if (resolvedPath.startsWith('@api/')) {
169
+ resolvedPath = `../src/web/${resolvedPath.substring('@api/'.length)}`;
170
+ } else if (resolvedPath.startsWith('./') || resolvedPath.startsWith('../')) {
171
+ const routeDir = route.filename.substring(0, route.filename.lastIndexOf('/'));
172
+ resolvedPath = `../${routeDir}/${resolvedPath}`;
173
+ }
174
+
175
+ const uniqueImportName = `agent_${route.agentVariable}`;
176
+ imports.push(`import type ${uniqueImportName} from '${resolvedPath}';`);
177
+ agentImports.set(route.agentVariable, uniqueImportName);
178
+ }
179
+
180
+ // Collect schema variable imports
181
+ if (route.inputSchemaVariable || route.outputSchemaVariable) {
182
+ const filename = route.filename.replace(/\\/g, '/');
183
+ const importPath = `../${filename.replace(/\.ts$/, '')}`;
184
+
185
+ if (!routeFileImports.has(importPath)) {
186
+ routeFileImports.set(importPath, new Set());
187
+ }
188
+
189
+ if (route.inputSchemaVariable) {
190
+ routeFileImports.get(importPath)!.add(route.inputSchemaVariable);
191
+ }
192
+ if (route.outputSchemaVariable) {
193
+ routeFileImports.get(importPath)!.add(route.outputSchemaVariable);
194
+ }
195
+ }
196
+ });
197
+
198
+ // Generate schema imports
199
+ routeFileImports.forEach((schemas, importPath) => {
200
+ const schemaList = Array.from(schemas).join(', ');
201
+ imports.push(`import type { ${schemaList} } from '${importPath}';`);
202
+ });
203
+
204
+ const importsStr = imports.join('\n');
205
+
206
+ // Helper to generate route entry
207
+ const generateRouteEntry = (route: RouteInfo): string => {
208
+ const routeKey = route.path;
209
+
210
+ if (!route.hasValidator) {
211
+ const streamValue = route.stream === true ? 'true' : 'false';
212
+ return ` '${routeKey}': {
213
+ inputSchema: never;
214
+ outputSchema: never;
215
+ stream: ${streamValue};
216
+ };`;
217
+ }
218
+
219
+ if (route.agentVariable) {
220
+ const importName = agentImports.get(route.agentVariable)!;
221
+ return ` '${routeKey}': {
222
+ inputSchema: typeof ${importName} extends { inputSchema?: infer I } ? I : never;
223
+ outputSchema: typeof ${importName} extends { outputSchema?: infer O } ? O : never;
224
+ stream: typeof ${importName} extends { stream?: infer S } ? S : false;
225
+ };`;
226
+ }
227
+
228
+ if (route.inputSchemaVariable || route.outputSchemaVariable) {
229
+ const inputType = route.inputSchemaVariable
230
+ ? `typeof ${route.inputSchemaVariable}`
231
+ : 'never';
232
+ const outputType = route.outputSchemaVariable
233
+ ? `typeof ${route.outputSchemaVariable}`
234
+ : 'never';
235
+ const streamValue = route.stream === true ? 'true' : 'false';
236
+ return ` '${routeKey}': {
237
+ inputSchema: ${inputType};
238
+ outputSchema: ${outputType};
239
+ stream: ${streamValue};
240
+ };`;
241
+ }
242
+
243
+ return ` '${routeKey}': {
244
+ inputSchema: any;
245
+ outputSchema: any;
246
+ };`;
247
+ };
248
+
249
+ // Generate route entries with METHOD prefix for API routes
250
+ const apiRouteEntries = apiRoutes
251
+ .map((route) => {
252
+ const routeKey = `${route.method.toUpperCase()} ${route.path}`;
253
+ return generateRouteEntry({ ...route, path: routeKey });
254
+ })
255
+ .join('\n');
256
+
257
+ const websocketRouteEntries = websocketRoutes.map(generateRouteEntry).join('\n');
258
+ const sseRouteEntries = sseRoutes.map(generateRouteEntry).join('\n');
259
+
260
+ const generatedContent = `// Auto-generated by Agentuity - do not edit manually
261
+ ${importsStr}
262
+
263
+ // Augment @agentuity/react types with project-specific routes
264
+ declare module '@agentuity/react' {
265
+ export interface RouteRegistry {
266
+ ${apiRouteEntries}
267
+ }
268
+
269
+ export interface WebSocketRouteRegistry {
270
+ ${websocketRouteEntries}
271
+ }
272
+
273
+ export interface SSERouteRegistry {
274
+ ${sseRouteEntries}
275
+ }
276
+ }
277
+ `;
278
+
279
+ const projectRoot = join(srcDir, '..');
280
+ const agentuityDir = join(projectRoot, '.agentuity');
281
+ const registryPath = join(agentuityDir, 'routes.generated.ts');
282
+
283
+ if (!existsSync(agentuityDir)) {
284
+ mkdirSync(agentuityDir, { recursive: true });
285
+ }
286
+
287
+ writeFileSync(registryPath, generatedContent, 'utf-8');
288
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Route Discovery - READ-ONLY AST analysis
3
+ *
4
+ * Discovers routes by scanning src/api/**\/*.ts files
5
+ * Extracts route definitions WITHOUT mutating source files
6
+ */
7
+
8
+ import { join, relative } from 'node:path';
9
+ import { existsSync } from 'node:fs';
10
+ import type { Logger } from '../../../types';
11
+ import { parseRoute } from '../ast';
12
+
13
+ export interface RouteMetadata {
14
+ id: string;
15
+ filename: string;
16
+ path: string;
17
+ method: string;
18
+ version: string;
19
+ type: 'api' | 'sms' | 'email' | 'cron' | 'websocket' | 'sse' | 'stream';
20
+ agentIds?: string[];
21
+ config?: Record<string, unknown>;
22
+ schema?: {
23
+ input?: string;
24
+ output?: string;
25
+ };
26
+ }
27
+
28
+ export interface RouteInfo {
29
+ method: string;
30
+ path: string;
31
+ filename: string;
32
+ hasValidator: boolean;
33
+ routeType: 'api' | 'sms' | 'email' | 'cron' | 'websocket' | 'sse' | 'stream';
34
+ agentVariable?: string;
35
+ agentImportPath?: string;
36
+ inputSchemaVariable?: string;
37
+ outputSchemaVariable?: string;
38
+ stream?: boolean;
39
+ }
40
+
41
+ /**
42
+ * Discover all routes in src/api directory (READ-ONLY)
43
+ */
44
+ export async function discoverRoutes(
45
+ srcDir: string,
46
+ projectId: string,
47
+ deploymentId: string,
48
+ logger: Logger
49
+ ): Promise<{ routes: RouteMetadata[]; routeInfoList: RouteInfo[] }> {
50
+ const apiDir = join(srcDir, 'api');
51
+ const routes: RouteMetadata[] = [];
52
+ const routeInfoList: RouteInfo[] = [];
53
+
54
+ // Check if API directory exists
55
+ if (!existsSync(apiDir)) {
56
+ logger.trace('No api directory found at %s', apiDir);
57
+ return { routes, routeInfoList };
58
+ }
59
+
60
+ const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
61
+
62
+ // Scan all .ts files in api directory
63
+ const glob = new Bun.Glob('**/*.ts');
64
+ for await (const file of glob.scan(apiDir)) {
65
+ const filePath = join(apiDir, file);
66
+
67
+ try {
68
+ const source = await Bun.file(filePath).text();
69
+ const contents = transpiler.transformSync(source);
70
+
71
+ // Check if file has createRouter or Hono
72
+ if (!contents.includes('createRouter') && !contents.includes('new Hono')) {
73
+ logger.trace('Skipping %s (no router)', file);
74
+ continue;
75
+ }
76
+
77
+ const rootDir = join(srcDir, '..');
78
+ const relativeFilename = './' + relative(srcDir, filePath);
79
+
80
+ try {
81
+ const parsedRoutes = await parseRoute(rootDir, filePath, projectId, deploymentId);
82
+
83
+ if (parsedRoutes.length > 0) {
84
+ logger.trace('Discovered %d route(s) in %s', parsedRoutes.length, relativeFilename);
85
+ routes.push(...parsedRoutes);
86
+
87
+ // Convert to RouteInfo for registry
88
+ for (const route of parsedRoutes) {
89
+ routeInfoList.push({
90
+ method: route.method.toUpperCase(),
91
+ path: route.path,
92
+ filename: route.filename,
93
+ hasValidator: route.config?.hasValidator === true,
94
+ routeType: route.type || 'api',
95
+ agentVariable: route.config?.agentVariable as string | undefined,
96
+ agentImportPath: route.config?.agentImportPath as string | undefined,
97
+ inputSchemaVariable: route.config?.inputSchemaVariable as string | undefined,
98
+ outputSchemaVariable: route.config?.outputSchemaVariable as string | undefined,
99
+ stream:
100
+ route.config?.stream !== undefined && route.config.stream !== null
101
+ ? Boolean(route.config.stream)
102
+ : route.type === 'stream'
103
+ ? true
104
+ : undefined,
105
+ });
106
+ }
107
+ }
108
+ } catch (error) {
109
+ // Skip files that don't have proper router setup
110
+ if (error instanceof Error) {
111
+ if (
112
+ error.message.includes('could not find default export') ||
113
+ error.message.includes('could not find an proper createRouter')
114
+ ) {
115
+ logger.trace('Skipping %s: %s', file, error.message);
116
+ } else {
117
+ throw error;
118
+ }
119
+ } else {
120
+ throw error;
121
+ }
122
+ }
123
+ } catch (error) {
124
+ logger.warn(`Failed to parse route file ${filePath}: ${error}`);
125
+ }
126
+ }
127
+
128
+ logger.debug('Discovered %d route(s)', routes.length);
129
+
130
+ // Check for route conflicts
131
+ const conflicts = detectRouteConflicts(routeInfoList);
132
+ if (conflicts.length > 0) {
133
+ logger.error('Route conflicts detected:');
134
+ for (const conflict of conflicts) {
135
+ logger.error(' %s', conflict.message);
136
+ for (const route of conflict.routes) {
137
+ logger.error(' - %s %s in %s', route.method, route.path, route.filename);
138
+ }
139
+ }
140
+ throw new Error(
141
+ `Found ${conflicts.length} route conflict(s). Fix the conflicts and try again.`
142
+ );
143
+ }
144
+
145
+ return { routes, routeInfoList };
146
+ }
147
+
148
+ export interface RouteConflict {
149
+ type: 'duplicate';
150
+ routes: Array<{ method: string; path: string; filename: string }>;
151
+ message: string;
152
+ }
153
+
154
+ /**
155
+ * Detect conflicts between routes
156
+ */
157
+ export function detectRouteConflicts(
158
+ routes: Array<{ method: string; path: string; filename: string }>
159
+ ): RouteConflict[] {
160
+ const conflicts: RouteConflict[] = [];
161
+
162
+ // Group routes by method+path
163
+ const methodPathMap = new Map<string, Array<{ path: string; filename: string }>>();
164
+
165
+ for (const route of routes) {
166
+ const key = `${route.method.toUpperCase()} ${route.path}`;
167
+ if (!methodPathMap.has(key)) {
168
+ methodPathMap.set(key, []);
169
+ }
170
+ methodPathMap.get(key)!.push({ path: route.path, filename: route.filename });
171
+ }
172
+
173
+ // Check for exact duplicates
174
+ for (const [methodPath, routeList] of methodPathMap.entries()) {
175
+ if (routeList.length > 1) {
176
+ const [method] = methodPath.split(' ', 2);
177
+ conflicts.push({
178
+ type: 'duplicate',
179
+ routes: routeList.map((r) => ({ method, path: r.path, filename: r.filename })),
180
+ message: `Duplicate route: ${methodPath} defined in ${routeList.length} files`,
181
+ });
182
+ }
183
+ }
184
+
185
+ return conflicts;
186
+ }