@agentuity/cli 0.0.109 → 0.0.111

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 (253) hide show
  1. package/dist/build-report.d.ts +201 -0
  2. package/dist/build-report.d.ts.map +1 -0
  3. package/dist/build-report.js +335 -0
  4. package/dist/build-report.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +19 -4
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  9. package/dist/cmd/build/entry-generator.js +3 -1
  10. package/dist/cmd/build/entry-generator.js.map +1 -1
  11. package/dist/cmd/build/index.d.ts.map +1 -1
  12. package/dist/cmd/build/index.js +44 -1
  13. package/dist/cmd/build/index.js.map +1 -1
  14. package/dist/cmd/build/typecheck.d.ts +7 -1
  15. package/dist/cmd/build/typecheck.d.ts.map +1 -1
  16. package/dist/cmd/build/typecheck.js +11 -1
  17. package/dist/cmd/build/typecheck.js.map +1 -1
  18. package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
  19. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  20. package/dist/cmd/build/vite/agent-discovery.js +3 -3
  21. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  22. package/dist/cmd/build/vite/index.d.ts +2 -1
  23. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/index.js +3 -2
  25. package/dist/cmd/build/vite/index.js.map +1 -1
  26. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/metadata-generator.js +3 -5
  28. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  29. package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
  30. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/registry-generator.js +126 -41
  32. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  33. package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
  34. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  35. package/dist/cmd/build/vite/route-discovery.js +19 -0
  36. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  37. package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
  38. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  39. package/dist/cmd/build/vite/vite-builder.js +10 -2
  40. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  41. package/dist/cmd/build/vite-bundler.d.ts +3 -0
  42. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  43. package/dist/cmd/build/vite-bundler.js +14 -5
  44. package/dist/cmd/build/vite-bundler.js.map +1 -1
  45. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  46. package/dist/cmd/cloud/deploy.js +149 -10
  47. package/dist/cmd/cloud/deploy.js.map +1 -1
  48. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  49. package/dist/cmd/cloud/deployment/show.js +0 -1
  50. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  51. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  52. package/dist/cmd/cloud/sandbox/create.js +18 -0
  53. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  54. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  55. package/dist/cmd/cloud/sandbox/delete.js +2 -6
  56. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  57. package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
  58. package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
  59. package/dist/cmd/cloud/sandbox/download.js +89 -0
  60. package/dist/cmd/cloud/sandbox/download.js.map +1 -0
  61. package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
  62. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
  63. package/dist/cmd/cloud/sandbox/env.js +90 -0
  64. package/dist/cmd/cloud/sandbox/env.js.map +1 -0
  65. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  66. package/dist/cmd/cloud/sandbox/get.js +5 -0
  67. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  68. package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
  69. package/dist/cmd/cloud/sandbox/index.js +14 -0
  70. package/dist/cmd/cloud/sandbox/index.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
  72. package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
  73. package/dist/cmd/cloud/sandbox/ls.js +119 -0
  74. package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
  75. package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
  76. package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
  77. package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
  78. package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
  79. package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
  80. package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
  81. package/dist/cmd/cloud/sandbox/rm.js +45 -0
  82. package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
  83. package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
  84. package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
  85. package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
  86. package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
  87. package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
  88. package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
  89. package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
  90. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  91. package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
  92. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  93. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
  94. package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
  95. package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
  96. package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
  97. package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
  98. package/dist/cmd/cloud/sandbox/upload.js +77 -0
  99. package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
  100. package/dist/cmd/dev/index.d.ts.map +1 -1
  101. package/dist/cmd/dev/index.js +126 -23
  102. package/dist/cmd/dev/index.js.map +1 -1
  103. package/dist/cmd/dev/sync.d.ts.map +1 -1
  104. package/dist/cmd/dev/sync.js +8 -14
  105. package/dist/cmd/dev/sync.js.map +1 -1
  106. package/dist/cmd/git/account/add.d.ts +17 -0
  107. package/dist/cmd/git/account/add.d.ts.map +1 -0
  108. package/dist/cmd/git/account/add.js +244 -0
  109. package/dist/cmd/git/account/add.js.map +1 -0
  110. package/dist/cmd/git/account/index.d.ts +3 -0
  111. package/dist/cmd/git/account/index.d.ts.map +1 -0
  112. package/dist/cmd/git/account/index.js +11 -0
  113. package/dist/cmd/git/account/index.js.map +1 -0
  114. package/dist/cmd/git/account/list.d.ts +2 -0
  115. package/dist/cmd/git/account/list.d.ts.map +1 -0
  116. package/dist/cmd/git/account/list.js +111 -0
  117. package/dist/cmd/git/account/list.js.map +1 -0
  118. package/dist/cmd/git/account/remove.d.ts +2 -0
  119. package/dist/cmd/git/account/remove.d.ts.map +1 -0
  120. package/dist/cmd/git/account/remove.js +171 -0
  121. package/dist/cmd/git/account/remove.js.map +1 -0
  122. package/dist/cmd/git/index.d.ts +3 -0
  123. package/dist/cmd/git/index.d.ts.map +1 -0
  124. package/dist/cmd/git/index.js +19 -0
  125. package/dist/cmd/git/index.js.map +1 -0
  126. package/dist/cmd/git/link.d.ts +32 -0
  127. package/dist/cmd/git/link.d.ts.map +1 -0
  128. package/dist/cmd/git/link.js +357 -0
  129. package/dist/cmd/git/link.js.map +1 -0
  130. package/dist/cmd/git/list.d.ts +2 -0
  131. package/dist/cmd/git/list.d.ts.map +1 -0
  132. package/dist/cmd/git/list.js +137 -0
  133. package/dist/cmd/git/list.js.map +1 -0
  134. package/dist/cmd/git/status.d.ts +2 -0
  135. package/dist/cmd/git/status.d.ts.map +1 -0
  136. package/dist/cmd/git/status.js +119 -0
  137. package/dist/cmd/git/status.js.map +1 -0
  138. package/dist/cmd/git/unlink.d.ts +2 -0
  139. package/dist/cmd/git/unlink.d.ts.map +1 -0
  140. package/dist/cmd/git/unlink.js +98 -0
  141. package/dist/cmd/git/unlink.js.map +1 -0
  142. package/dist/cmd/index.d.ts.map +1 -1
  143. package/dist/cmd/index.js +2 -0
  144. package/dist/cmd/index.js.map +1 -1
  145. package/dist/cmd/integration/api.d.ts +61 -0
  146. package/dist/cmd/integration/api.d.ts.map +1 -0
  147. package/dist/cmd/integration/api.js +176 -0
  148. package/dist/cmd/integration/api.js.map +1 -0
  149. package/dist/cmd/integration/github/connect.d.ts +2 -0
  150. package/dist/cmd/integration/github/connect.d.ts.map +1 -0
  151. package/dist/cmd/integration/github/connect.js +197 -0
  152. package/dist/cmd/integration/github/connect.js.map +1 -0
  153. package/dist/cmd/integration/github/disconnect.d.ts +2 -0
  154. package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
  155. package/dist/cmd/integration/github/disconnect.js +121 -0
  156. package/dist/cmd/integration/github/disconnect.js.map +1 -0
  157. package/dist/cmd/integration/github/index.d.ts +2 -0
  158. package/dist/cmd/integration/github/index.d.ts.map +1 -0
  159. package/dist/cmd/integration/github/index.js +21 -0
  160. package/dist/cmd/integration/github/index.js.map +1 -0
  161. package/dist/cmd/integration/index.d.ts +2 -0
  162. package/dist/cmd/integration/index.d.ts.map +1 -0
  163. package/dist/cmd/integration/index.js +16 -0
  164. package/dist/cmd/integration/index.js.map +1 -0
  165. package/dist/cmd/project/auth/generate.d.ts +5 -0
  166. package/dist/cmd/project/auth/generate.d.ts.map +1 -0
  167. package/dist/cmd/project/auth/generate.js +102 -0
  168. package/dist/cmd/project/auth/generate.js.map +1 -0
  169. package/dist/cmd/project/auth/index.d.ts +2 -0
  170. package/dist/cmd/project/auth/index.d.ts.map +1 -0
  171. package/dist/cmd/project/auth/index.js +21 -0
  172. package/dist/cmd/project/auth/index.js.map +1 -0
  173. package/dist/cmd/project/auth/init.d.ts +2 -0
  174. package/dist/cmd/project/auth/init.d.ts.map +1 -0
  175. package/dist/cmd/project/auth/init.js +220 -0
  176. package/dist/cmd/project/auth/init.js.map +1 -0
  177. package/dist/cmd/project/auth/shared.d.ts +88 -0
  178. package/dist/cmd/project/auth/shared.d.ts.map +1 -0
  179. package/dist/cmd/project/auth/shared.js +435 -0
  180. package/dist/cmd/project/auth/shared.js.map +1 -0
  181. package/dist/cmd/project/index.d.ts.map +1 -1
  182. package/dist/cmd/project/index.js +9 -1
  183. package/dist/cmd/project/index.js.map +1 -1
  184. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  185. package/dist/cmd/project/template-flow.js +106 -0
  186. package/dist/cmd/project/template-flow.js.map +1 -1
  187. package/dist/config.d.ts +2 -0
  188. package/dist/config.d.ts.map +1 -1
  189. package/dist/config.js +24 -0
  190. package/dist/config.js.map +1 -1
  191. package/dist/errors.d.ts +2 -1
  192. package/dist/errors.d.ts.map +1 -1
  193. package/dist/errors.js +5 -0
  194. package/dist/errors.js.map +1 -1
  195. package/dist/types.d.ts +3 -4
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/types.js +5 -2
  198. package/dist/types.js.map +1 -1
  199. package/package.json +6 -5
  200. package/src/build-report.ts +457 -0
  201. package/src/cli.ts +20 -4
  202. package/src/cmd/build/entry-generator.ts +3 -1
  203. package/src/cmd/build/index.ts +51 -1
  204. package/src/cmd/build/typecheck.ts +19 -1
  205. package/src/cmd/build/vite/agent-discovery.ts +4 -4
  206. package/src/cmd/build/vite/index.ts +5 -2
  207. package/src/cmd/build/vite/metadata-generator.ts +5 -7
  208. package/src/cmd/build/vite/registry-generator.ts +136 -43
  209. package/src/cmd/build/vite/route-discovery.ts +20 -0
  210. package/src/cmd/build/vite/vite-builder.ts +13 -2
  211. package/src/cmd/build/vite-bundler.ts +17 -4
  212. package/src/cmd/cloud/deploy.ts +183 -12
  213. package/src/cmd/cloud/deployment/show.ts +0 -1
  214. package/src/cmd/cloud/sandbox/create.ts +22 -0
  215. package/src/cmd/cloud/sandbox/delete.ts +2 -6
  216. package/src/cmd/cloud/sandbox/download.ts +96 -0
  217. package/src/cmd/cloud/sandbox/env.ts +104 -0
  218. package/src/cmd/cloud/sandbox/get.ts +5 -0
  219. package/src/cmd/cloud/sandbox/index.ts +14 -0
  220. package/src/cmd/cloud/sandbox/ls.ts +126 -0
  221. package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
  222. package/src/cmd/cloud/sandbox/rm.ts +51 -0
  223. package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
  224. package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
  225. package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
  226. package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
  227. package/src/cmd/cloud/sandbox/upload.ts +83 -0
  228. package/src/cmd/dev/index.ts +147 -33
  229. package/src/cmd/dev/sync.ts +26 -30
  230. package/src/cmd/git/account/add.ts +317 -0
  231. package/src/cmd/git/account/index.ts +12 -0
  232. package/src/cmd/git/account/list.ts +139 -0
  233. package/src/cmd/git/account/remove.ts +212 -0
  234. package/src/cmd/git/index.ts +20 -0
  235. package/src/cmd/git/link.ts +468 -0
  236. package/src/cmd/git/list.ts +161 -0
  237. package/src/cmd/git/status.ts +144 -0
  238. package/src/cmd/git/unlink.ts +117 -0
  239. package/src/cmd/index.ts +2 -0
  240. package/src/cmd/integration/api.ts +379 -0
  241. package/src/cmd/integration/github/connect.ts +242 -0
  242. package/src/cmd/integration/github/disconnect.ts +149 -0
  243. package/src/cmd/integration/github/index.ts +21 -0
  244. package/src/cmd/integration/index.ts +16 -0
  245. package/src/cmd/project/auth/generate.ts +116 -0
  246. package/src/cmd/project/auth/index.ts +21 -0
  247. package/src/cmd/project/auth/init.ts +263 -0
  248. package/src/cmd/project/auth/shared.ts +534 -0
  249. package/src/cmd/project/index.ts +9 -1
  250. package/src/cmd/project/template-flow.ts +125 -0
  251. package/src/config.ts +34 -0
  252. package/src/errors.ts +7 -0
  253. package/src/types.ts +5 -2
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Shared helpers for Agentuity Auth setup
3
+ */
4
+
5
+ import * as path from 'node:path';
6
+ import { listResources, createResources, dbQuery } from '@agentuity/server';
7
+ import * as tui from '../../../tui';
8
+ import { getCatalystAPIClient } from '../../../config';
9
+ import type { Logger } from '../../../types';
10
+ import type { AuthData } from '../../../types';
11
+ import enquirer from 'enquirer';
12
+
13
+ /**
14
+ * Database info returned from selection
15
+ */
16
+ export interface DatabaseInfo {
17
+ name: string;
18
+ url: string;
19
+ }
20
+
21
+ /**
22
+ * Select an existing database or create a new one
23
+ */
24
+ export async function selectOrCreateDatabase(options: {
25
+ logger: Logger;
26
+ auth: AuthData;
27
+ orgId: string;
28
+ region: string;
29
+ existingUrl?: string;
30
+ }): Promise<DatabaseInfo> {
31
+ const { logger, auth, orgId, region, existingUrl } = options;
32
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
33
+
34
+ const resources = await tui.spinner({
35
+ message: `Fetching databases for ${orgId} in ${region}`,
36
+ clearOnSuccess: true,
37
+ callback: async () => listResources(catalystClient, orgId, region),
38
+ });
39
+
40
+ const databases = resources.db;
41
+
42
+ // Extract existing database name from URL if provided
43
+ let existingDbName: string | undefined;
44
+ if (existingUrl) {
45
+ const urlMatch = existingUrl.match(/\/([^/?]+)(\?|$)/);
46
+ if (urlMatch) {
47
+ existingDbName = urlMatch[1];
48
+ }
49
+ }
50
+
51
+ type Choice = { name: string; message: string };
52
+ const choices: Choice[] = [];
53
+
54
+ // Add "use existing" option first if we have an existing URL
55
+ if (existingUrl && existingDbName) {
56
+ choices.push({
57
+ name: '__existing__',
58
+ message: `${tui.tuiColors.success('✓')} Use existing (found in .env): ${existingDbName}`,
59
+ });
60
+ }
61
+
62
+ // Add create new option
63
+ choices.push({ name: '__create__', message: tui.bold('+ Create new database') });
64
+
65
+ // Add other databases
66
+ choices.push(
67
+ ...databases
68
+ .filter((db) => db.name !== existingDbName) // Don't duplicate existing
69
+ .map((db) => ({
70
+ name: db.name,
71
+ message: db.name,
72
+ }))
73
+ );
74
+
75
+ const response = await enquirer.prompt<{ database: string }>({
76
+ type: 'select',
77
+ name: 'database',
78
+ message: 'Select a database for auth:',
79
+ choices,
80
+ });
81
+
82
+ // Handle "use existing" selection
83
+ if (response.database === '__existing__' && existingUrl && existingDbName) {
84
+ return { name: existingDbName, url: existingUrl };
85
+ }
86
+
87
+ if (response.database === '__create__') {
88
+ const created = await tui.spinner({
89
+ message: `Creating database in ${region}`,
90
+ clearOnSuccess: true,
91
+ callback: async () => createResources(catalystClient, orgId, region, [{ type: 'db' }]),
92
+ });
93
+
94
+ if (created.length === 0) {
95
+ tui.fatal('Failed to create database');
96
+ }
97
+
98
+ const newDb = created[0];
99
+ tui.success(`Created database: ${tui.bold(newDb.name)}`);
100
+
101
+ const updatedResources = await listResources(catalystClient, orgId, region);
102
+ const dbInfo = updatedResources.db.find((d) => d.name === newDb.name);
103
+
104
+ if (!dbInfo?.url) {
105
+ tui.fatal('Failed to retrieve database connection URL');
106
+ }
107
+
108
+ return { name: newDb.name, url: dbInfo.url };
109
+ }
110
+
111
+ const selectedDb = databases.find((d) => d.name === response.database);
112
+ if (!selectedDb?.url) {
113
+ tui.fatal('Failed to retrieve database connection URL');
114
+ }
115
+
116
+ return { name: selectedDb.name, url: selectedDb.url };
117
+ }
118
+
119
+ /**
120
+ * Required auth dependencies
121
+ */
122
+ export const AUTH_DEPENDENCIES = {
123
+ '@agentuity/auth': 'latest',
124
+ 'better-auth': '^1.4.9',
125
+ 'drizzle-orm': '^0.44.0',
126
+ } as const;
127
+
128
+ /**
129
+ * Check and install auth dependencies
130
+ */
131
+ export async function ensureAuthDependencies(options: {
132
+ projectDir: string;
133
+ logger: Logger;
134
+ }): Promise<boolean> {
135
+ const { projectDir } = options;
136
+ const fs = await import('fs');
137
+ const path = await import('path');
138
+
139
+ const packageJsonPath = path.join(projectDir, 'package.json');
140
+
141
+ if (!fs.existsSync(packageJsonPath)) {
142
+ tui.fatal('No package.json found in project directory');
143
+ }
144
+
145
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
146
+ const deps = packageJson.dependencies || {};
147
+
148
+ const missingDeps: string[] = [];
149
+
150
+ for (const [dep, version] of Object.entries(AUTH_DEPENDENCIES)) {
151
+ if (!deps[dep]) {
152
+ missingDeps.push(`${dep}@${version}`);
153
+ }
154
+ }
155
+
156
+ if (missingDeps.length === 0) {
157
+ return false;
158
+ }
159
+
160
+ tui.info(`Installing auth dependencies: ${missingDeps.join(', ')}`);
161
+
162
+ const proc = Bun.spawn(['bun', 'install', ...missingDeps], {
163
+ cwd: projectDir,
164
+ stdout: 'inherit',
165
+ stderr: 'inherit',
166
+ });
167
+
168
+ const exitCode = await proc.exited;
169
+ if (exitCode !== 0) {
170
+ throw new Error(`bun install failed with code ${exitCode}`);
171
+ }
172
+
173
+ tui.success('Dependencies installed');
174
+ return true;
175
+ }
176
+
177
+ /**
178
+ * ORM setup type detected in a project.
179
+ */
180
+ export type OrmSetup = 'drizzle' | 'prisma' | 'none';
181
+
182
+ /**
183
+ * Get the directory for generated SQL files.
184
+ * Uses src/generated/ if it exists, otherwise falls back to project root.
185
+ */
186
+ export async function getGeneratedSqlDir(projectDir: string): Promise<string> {
187
+ const generatedDir = path.join(projectDir, 'src', 'generated');
188
+ if (await Bun.file(path.join(generatedDir, 'registry.ts')).exists()) {
189
+ return generatedDir;
190
+ }
191
+ return projectDir;
192
+ }
193
+
194
+ /**
195
+ * Detect existing ORM setup in project.
196
+ * TODO: This is probably not 100% accurate. Drizzle config could be in all sorts of places in a repo.
197
+ */
198
+ export async function detectOrmSetup(projectDir: string): Promise<OrmSetup> {
199
+ const drizzleConfigTs = path.join(projectDir, 'drizzle.config.ts');
200
+ const drizzleConfigJs = path.join(projectDir, 'drizzle.config.js');
201
+ const prismaSchema = path.join(projectDir, 'prisma', 'schema.prisma');
202
+
203
+ if ((await Bun.file(drizzleConfigTs).exists()) || (await Bun.file(drizzleConfigJs).exists())) {
204
+ return 'drizzle';
205
+ }
206
+
207
+ if (await Bun.file(prismaSchema).exists()) {
208
+ return 'prisma';
209
+ }
210
+
211
+ return 'none';
212
+ }
213
+
214
+ /**
215
+ * Generate auth schema SQL using drizzle-kit export.
216
+ *
217
+ * This generates SQL DDL statements from the @agentuity/auth Drizzle schema
218
+ * without needing a database connection.
219
+ *
220
+ * @param projectDir - Project directory (must have @agentuity/auth installed)
221
+ * @returns SQL DDL statements for auth tables
222
+ */
223
+ export async function generateAuthSchemaSql(projectDir: string): Promise<string> {
224
+ const schemaPath = path.join(projectDir, 'node_modules/@agentuity/auth/src/schema.ts');
225
+
226
+ if (!(await Bun.file(schemaPath).exists())) {
227
+ throw new Error(
228
+ `@agentuity/auth schema not found at ${schemaPath}. Ensure @agentuity/auth is installed.`
229
+ );
230
+ }
231
+
232
+ const proc = Bun.spawn(
233
+ ['bunx', 'drizzle-kit', 'export', '--dialect=postgresql', `--schema=${schemaPath}`],
234
+ {
235
+ cwd: projectDir,
236
+ stdout: 'pipe',
237
+ stderr: 'pipe',
238
+ }
239
+ );
240
+
241
+ const [stdout, stderr, exitCode] = await Promise.all([
242
+ new Response(proc.stdout).text(),
243
+ new Response(proc.stderr).text(),
244
+ proc.exited,
245
+ ]);
246
+
247
+ if (exitCode !== 0) {
248
+ const errorMsg = stderr
249
+ .split('\n')
250
+ .filter((line) => !line.includes('Please install'))
251
+ .join('\n')
252
+ .trim();
253
+ throw new Error(`drizzle-kit export failed with code ${exitCode}: ${errorMsg}`);
254
+ }
255
+
256
+ return makeIdempotent(stdout);
257
+ }
258
+
259
+ /**
260
+ * Transform drizzle-kit SQL output to be idempotent.
261
+ *
262
+ * - Converts CREATE TABLE to CREATE TABLE IF NOT EXISTS
263
+ * - Converts CREATE INDEX to CREATE INDEX IF NOT EXISTS
264
+ * - Wraps ALTER TABLE ADD CONSTRAINT in DO blocks to handle existing constraints
265
+ */
266
+ function makeIdempotent(sql: string): string {
267
+ const lines = sql.split('\n');
268
+ const result: string[] = [];
269
+
270
+ for (const line of lines) {
271
+ const trimmed = line.trim();
272
+
273
+ if (trimmed.startsWith('CREATE TABLE ') && !trimmed.includes('IF NOT EXISTS')) {
274
+ result.push(line.replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS '));
275
+ } else if (trimmed.startsWith('CREATE INDEX ') && !trimmed.includes('IF NOT EXISTS')) {
276
+ result.push(line.replace('CREATE INDEX ', 'CREATE INDEX IF NOT EXISTS '));
277
+ } else if (trimmed.startsWith('ALTER TABLE ') && trimmed.includes('ADD CONSTRAINT')) {
278
+ const constraintMatch = trimmed.match(/ADD CONSTRAINT "([^"]+)"/);
279
+ if (constraintMatch) {
280
+ result.push(
281
+ `DO $$ BEGIN ${trimmed} EXCEPTION WHEN duplicate_object THEN NULL; END $$;`
282
+ );
283
+ } else {
284
+ result.push(line);
285
+ }
286
+ } else {
287
+ result.push(line);
288
+ }
289
+ }
290
+
291
+ return result.join('\n');
292
+ }
293
+
294
+ /**
295
+ * Split SQL into individual statements for sequential execution
296
+ * The dbQuery API only supports single statements
297
+ */
298
+ export function splitSqlStatements(sql: string): string[] {
299
+ // Split on semicolons, but be careful about edge cases
300
+ const statements: string[] = [];
301
+ let current = '';
302
+
303
+ for (const line of sql.split('\n')) {
304
+ const trimmed = line.trim();
305
+
306
+ // Skip empty lines and comments
307
+ if (!trimmed || trimmed.startsWith('--')) {
308
+ continue;
309
+ }
310
+
311
+ current += line + '\n';
312
+
313
+ // If line ends with semicolon, it's end of statement
314
+ if (trimmed.endsWith(';')) {
315
+ const stmt = current.trim();
316
+ if (stmt && stmt !== ';') {
317
+ statements.push(stmt);
318
+ }
319
+ current = '';
320
+ }
321
+ }
322
+
323
+ // Handle any remaining content
324
+ if (current.trim()) {
325
+ statements.push(current.trim());
326
+ }
327
+
328
+ return statements;
329
+ }
330
+
331
+ /**
332
+ * Run auth migrations against a database.
333
+ *
334
+ * @param options.sql - SQL to execute (from generateAuthSchemaSql or custom)
335
+ */
336
+ export async function runAuthMigrations(options: {
337
+ logger: Logger;
338
+ auth: AuthData;
339
+ orgId: string;
340
+ region: string;
341
+ databaseName: string;
342
+ sql: string;
343
+ }): Promise<void> {
344
+ const { logger, auth, orgId, region, databaseName, sql } = options;
345
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
346
+
347
+ const statements = splitSqlStatements(sql);
348
+
349
+ await tui.spinner({
350
+ message: `Creating auth tables in database "${databaseName}" (${statements.length} SQL statements)`,
351
+ clearOnSuccess: true,
352
+ callback: async () => {
353
+ for (const statement of statements) {
354
+ await dbQuery(catalystClient, {
355
+ database: databaseName,
356
+ query: statement,
357
+ orgId,
358
+ region,
359
+ });
360
+ }
361
+ },
362
+ });
363
+
364
+ tui.success(`Auth tables created in ${tui.bold(databaseName)}`);
365
+ }
366
+
367
+ /**
368
+ * Generate the auth.ts file content
369
+ */
370
+ export function generateAuthFileContent(): string {
371
+ return `/**
372
+ * Agentuity Auth configuration.
373
+ *
374
+ * This is the single source of truth for authentication in this project.
375
+ * All auth tables are stored in your Postgres database.
376
+ */
377
+
378
+ import {
379
+ createAuth,
380
+ createSessionMiddleware,
381
+ createApiKeyMiddleware,
382
+ } from '@agentuity/auth';
383
+
384
+ /**
385
+ * Database URL for authentication.
386
+ *
387
+ * Set via DATABASE_URL environment variable.
388
+ * Get yours from: \`agentuity cloud database list --region use --json\`
389
+ */
390
+ const DATABASE_URL = process.env.DATABASE_URL;
391
+
392
+ if (!DATABASE_URL) {
393
+ throw new Error('DATABASE_URL environment variable is required for authentication');
394
+ }
395
+
396
+ /**
397
+ * Agentuity Auth instance with sensible defaults.
398
+ *
399
+ * Defaults:
400
+ * - basePath: '/api/auth'
401
+ * - emailAndPassword: { enabled: true }
402
+ * - Uses AGENTUITY_AUTH_SECRET env var for signing
403
+ *
404
+ * Default plugins included:
405
+ * - organization (multi-tenancy)
406
+ * - jwt (token signing)
407
+ * - bearer (API auth)
408
+ * - apiKey (programmatic access)
409
+ */
410
+ export const auth = createAuth({
411
+ // Simplest setup: just provide the connection string
412
+ // We create pg pool + Drizzle internally with joins enabled
413
+ connectionString: DATABASE_URL,
414
+ // All options below have sensible defaults and can be omitted:
415
+ // secret: process.env.AGENTUITY_AUTH_SECRET, // auto-resolved from env
416
+ // basePath: '/api/auth', // default
417
+ // emailAndPassword: { enabled: true }, // default
418
+ });
419
+
420
+ /**
421
+ * Session middleware - validates cookies/bearer tokens.
422
+ * Use for routes that require authentication.
423
+ */
424
+ export const authMiddleware = createSessionMiddleware(auth);
425
+
426
+ /**
427
+ * Optional auth middleware - allows anonymous access.
428
+ * Sets ctx.auth = null for unauthenticated requests.
429
+ */
430
+ export const optionalAuthMiddleware = createSessionMiddleware(auth, { optional: true });
431
+
432
+ /**
433
+ * API key middleware for programmatic access.
434
+ * Use for webhook endpoints or external integrations.
435
+ */
436
+ export const apiKeyMiddleware = createApiKeyMiddleware(auth);
437
+
438
+ /**
439
+ * Optional API key middleware - continues without auth if no API key present.
440
+ */
441
+ export const optionalApiKeyMiddleware = createApiKeyMiddleware(auth, { optional: true });
442
+
443
+ /**
444
+ * Type export for end-to-end type safety.
445
+ */
446
+ export type Auth = typeof auth;
447
+ `;
448
+ }
449
+
450
+ /**
451
+ * Print integration examples to the console
452
+ */
453
+ export function printIntegrationExamples(): void {
454
+ tui.newline();
455
+ tui.info(tui.bold('Next Steps - Add these to your project:'));
456
+ tui.newline();
457
+
458
+ console.log(tui.muted('━'.repeat(60)));
459
+ console.log(tui.bold(' 1. Set up your API routes (e.g., src/api/index.ts):'));
460
+ console.log(tui.muted('━'.repeat(60)));
461
+ console.log(`
462
+ import { createRouter } from '@agentuity/runtime';
463
+ import { mountAuthRoutes } from '@agentuity/auth';
464
+ import { auth, authMiddleware } from '../auth';
465
+
466
+ const api = createRouter();
467
+
468
+ // Mount auth routes (sign-in, sign-up, sign-out, session, etc.)
469
+ // Must match the basePath configured in createAuth (default: /api/auth)
470
+ api.on(['GET', 'POST'], '/api/auth/*', mountAuthRoutes(auth));
471
+
472
+ // Protect your API routes with auth middleware
473
+ api.use('/api/*', authMiddleware);
474
+
475
+ api.get('/api/me', async (c) => {
476
+ const user = await c.var.auth.getUser();
477
+ return c.json({ id: user.id, email: user.email });
478
+ });
479
+
480
+ export default api;
481
+ `);
482
+
483
+ console.log(tui.muted('━'.repeat(60)));
484
+ console.log(tui.bold(' 2. Wrap your React app with AuthProvider:'));
485
+ console.log(tui.muted('━'.repeat(60)));
486
+ console.log(`
487
+ import { AgentuityProvider } from '@agentuity/react';
488
+ import { createAuthClient } from '@agentuity/auth/react';
489
+ import { AuthProvider } from '@agentuity/auth';
490
+
491
+ const authClient = createAuthClient();
492
+
493
+ function App() {
494
+ return (
495
+ <AgentuityProvider>
496
+ <AuthProvider authClient={authClient}>
497
+ {/* your app */}
498
+ </AuthProvider>
499
+ </AgentuityProvider>
500
+ );
501
+ }
502
+ `);
503
+
504
+ console.log(tui.muted('━'.repeat(60)));
505
+ console.log(tui.bold(' 3. Access auth in agents via ctx.auth:'));
506
+ console.log(tui.muted('━'.repeat(60)));
507
+ console.log(`
508
+ import { createAgent } from '@agentuity/runtime';
509
+
510
+ export default createAgent('my-agent', {
511
+ schema: { input: s.object({ name: s.string() }), output: s.string() },
512
+ handler: async (ctx, input) => {
513
+ // ctx.auth is available when using auth middleware
514
+ if (ctx.auth) {
515
+ const user = await ctx.auth.getUser();
516
+ return \`Hello, \${user.email}!\`;
517
+ }
518
+ return 'Hello, anonymous!';
519
+ },
520
+ });
521
+ `);
522
+
523
+ tui.newline();
524
+ console.log(tui.muted('━'.repeat(60)));
525
+ tui.info('Checklist:');
526
+ console.log(` ${tui.tuiColors.success('✓')} DATABASE_URL configured`);
527
+ console.log(` ${tui.tuiColors.success('✓')} AGENTUITY_AUTH_SECRET configured`);
528
+ console.log(` ${tui.tuiColors.success('✓')} Auth tables migrated`);
529
+ console.log(` ${tui.tuiColors.success('✓')} Dependencies installed`);
530
+ console.log(` ${tui.muted('○')} Wire Hono middleware`);
531
+ console.log(` ${tui.muted('○')} Add auth routes (mountAuthRoutes at /api/auth/*)`);
532
+ console.log(` ${tui.muted('○')} Wrap app with AuthProvider`);
533
+ tui.newline();
534
+ }
@@ -3,6 +3,7 @@ import { createProjectSubcommand } from './create';
3
3
  import { listSubcommand } from './list';
4
4
  import { deleteSubcommand } from './delete';
5
5
  import { showSubcommand } from './show';
6
+ import { authCommand } from './auth';
6
7
  import { getCommand } from '../../command-prefix';
7
8
 
8
9
  export const command = createCommand({
@@ -12,6 +13,13 @@ export const command = createCommand({
12
13
  examples: [
13
14
  { command: getCommand('project create my-agent'), description: 'Create a new project' },
14
15
  { command: getCommand('project list'), description: 'List all projects' },
16
+ { command: getCommand('project auth init'), description: 'Set up Agentuity Auth' },
17
+ ],
18
+ subcommands: [
19
+ createProjectSubcommand,
20
+ listSubcommand,
21
+ deleteSubcommand,
22
+ showSubcommand,
23
+ authCommand,
15
24
  ],
16
- subcommands: [createProjectSubcommand, listSubcommand, deleteSubcommand, showSubcommand],
17
25
  });
@@ -30,6 +30,13 @@ import {
30
30
  type EnvVars,
31
31
  } from '../../env-util';
32
32
  import { promptForDNS } from '../../domain';
33
+ import {
34
+ ensureAuthDependencies,
35
+ runAuthMigrations,
36
+ generateAuthFileContent,
37
+ printIntegrationExamples,
38
+ generateAuthSchemaSql,
39
+ } from './auth/shared';
33
40
 
34
41
  interface CreateFlowOptions {
35
42
  projectName?: string;
@@ -362,6 +369,100 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
362
369
  }
363
370
  }
364
371
 
372
+ // Auth setup - either from template or user choice
373
+ const templateHasAuth = selectedTemplate.id === 'agentuity-auth';
374
+
375
+ let authEnabled = templateHasAuth; // Auth templates have auth enabled by default
376
+ let authDatabaseName: string | undefined;
377
+ let authDatabaseUrl: string | undefined;
378
+
379
+ // For non-auth templates, ask if they want to enable auth
380
+ if (auth && catalystClient && orgId && region && !skipPrompts && !templateHasAuth) {
381
+ const enableAuth = await prompt.select({
382
+ message: 'Enable Agentuity Authentication?',
383
+ options: [
384
+ { value: 'no', label: "No, I'll add auth later" },
385
+ { value: 'yes', label: 'Yes, set up Agentuity Auth' },
386
+ ],
387
+ });
388
+
389
+ if (enableAuth === 'yes') {
390
+ authEnabled = true;
391
+ }
392
+ }
393
+
394
+ // Set up database and secret for any auth-enabled project
395
+ if (authEnabled && auth && catalystClient && orgId && region && !skipPrompts) {
396
+ // If a database was already selected/created above, use it for auth
397
+ if (resourceEnvVars.DATABASE_URL) {
398
+ authDatabaseUrl = resourceEnvVars.DATABASE_URL;
399
+ // Extract database name from URL using proper URL parsing
400
+ try {
401
+ const dbUrl = new URL(authDatabaseUrl);
402
+ const dbName = dbUrl.pathname.replace(/^\/+/, ''); // Remove leading slashes
403
+ // Validate: non-empty and contains only safe characters
404
+ if (dbName && /^[A-Za-z0-9_-]+$/.test(dbName)) {
405
+ authDatabaseName = dbName;
406
+ }
407
+ } catch {
408
+ // Invalid URL format, authDatabaseName stays undefined
409
+ }
410
+ } else {
411
+ // No database selected yet, create one for auth
412
+ const created = await tui.spinner({
413
+ message: 'Provisioning database for auth',
414
+ clearOnSuccess: true,
415
+ callback: async () => {
416
+ return createResources(catalystClient, orgId, region!, [{ type: 'db' }]);
417
+ },
418
+ });
419
+ authDatabaseName = created[0].name;
420
+
421
+ // Get env vars from created resource
422
+ if (created[0]?.env) {
423
+ authDatabaseUrl = created[0].env.DATABASE_URL;
424
+ // Also add to resourceEnvVars if not already set
425
+ if (!resourceEnvVars.DATABASE_URL) {
426
+ Object.assign(resourceEnvVars, created[0].env);
427
+ }
428
+ }
429
+ }
430
+
431
+ // Install auth dependencies (skip for agentuity-auth template which has them)
432
+ if (!templateHasAuth) {
433
+ await ensureAuthDependencies({ projectDir: dest, logger });
434
+
435
+ // Generate auth.ts
436
+ const authFilePath = resolve(dest, 'src', 'auth.ts');
437
+ if (!existsSync(authFilePath)) {
438
+ const srcDir = resolve(dest, 'src');
439
+ if (!existsSync(srcDir)) {
440
+ await Bun.write(resolve(srcDir, '.gitkeep'), '');
441
+ }
442
+ await Bun.write(authFilePath, generateAuthFileContent());
443
+ tui.success('Created src/auth.ts');
444
+ }
445
+ }
446
+
447
+ // Run migrations
448
+ if (authDatabaseName) {
449
+ const sql = await tui.spinner({
450
+ message: 'Preparing auth database schema...',
451
+ clearOnSuccess: true,
452
+ callback: () => generateAuthSchemaSql(dest),
453
+ });
454
+
455
+ await runAuthMigrations({
456
+ logger,
457
+ auth,
458
+ orgId,
459
+ region,
460
+ databaseName: authDatabaseName,
461
+ sql,
462
+ });
463
+ }
464
+ }
465
+
365
466
  let projectId: string | undefined;
366
467
 
367
468
  if (auth && apiClient && orgId) {
@@ -403,9 +504,28 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
403
504
  },
404
505
  });
405
506
 
507
+ // Add auth secret to resourceEnvVars if auth is enabled
508
+ if (authEnabled && !resourceEnvVars.AGENTUITY_AUTH_SECRET) {
509
+ const devSecret = `dev-${crypto.randomUUID()}`;
510
+ resourceEnvVars.AGENTUITY_AUTH_SECRET = devSecret;
511
+ }
512
+
406
513
  // Write resource environment variables to .env
407
514
  if (Object.keys(resourceEnvVars).length > 0) {
408
515
  await addResourceEnvVars(dest, resourceEnvVars);
516
+
517
+ // Show user feedback for auth-related env vars
518
+ if (authEnabled) {
519
+ if (resourceEnvVars.DATABASE_URL) {
520
+ tui.success('DATABASE_URL added to .env');
521
+ }
522
+ if (resourceEnvVars.AGENTUITY_AUTH_SECRET) {
523
+ tui.success('AGENTUITY_AUTH_SECRET added to .env');
524
+ tui.info(
525
+ `Generate one with: ${tui.muted('npx @better-auth/cli secret')} or ${tui.muted('openssl rand -hex 32')}`
526
+ );
527
+ }
528
+ }
409
529
  }
410
530
 
411
531
  // After registration, push any existing env/secrets from .env
@@ -477,6 +597,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
477
597
  await promptForDNS(projectId, _domains, config);
478
598
  }
479
599
  }
600
+
601
+ // Print auth integration examples if auth was enabled (skip for auth template - already set up)
602
+ if (authEnabled && !templateHasAuth) {
603
+ printIntegrationExamples();
604
+ }
480
605
  }
481
606
 
482
607
  /**