@agentuity/cli 0.0.42 → 0.0.44

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 (249) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/cli.ts +7 -5
  4. package/dist/api.d.ts +3 -3
  5. package/dist/api.d.ts.map +1 -1
  6. package/dist/auth.d.ts +10 -2
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/banner.d.ts.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cmd/auth/api.d.ts +4 -4
  11. package/dist/cmd/auth/api.d.ts.map +1 -1
  12. package/dist/cmd/auth/index.d.ts.map +1 -1
  13. package/dist/cmd/auth/login.d.ts.map +1 -1
  14. package/dist/cmd/auth/signup.d.ts.map +1 -1
  15. package/dist/cmd/auth/ssh/add.d.ts +2 -0
  16. package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
  17. package/dist/cmd/auth/ssh/api.d.ts +16 -0
  18. package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
  19. package/dist/cmd/auth/ssh/delete.d.ts +2 -0
  20. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
  21. package/dist/cmd/auth/ssh/index.d.ts +3 -0
  22. package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
  23. package/dist/cmd/auth/ssh/list.d.ts +2 -0
  24. package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
  25. package/dist/cmd/auth/whoami.d.ts +2 -0
  26. package/dist/cmd/auth/whoami.d.ts.map +1 -0
  27. package/dist/cmd/bundle/ast.d.ts +14 -3
  28. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  29. package/dist/cmd/bundle/ast.test.d.ts +2 -0
  30. package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
  31. package/dist/cmd/bundle/bundler.d.ts +6 -1
  32. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  33. package/dist/cmd/bundle/file.d.ts.map +1 -1
  34. package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
  35. package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
  36. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
  37. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
  38. package/dist/cmd/bundle/index.d.ts +1 -1
  39. package/dist/cmd/bundle/index.d.ts.map +1 -1
  40. package/dist/cmd/bundle/plugin.d.ts +2 -0
  41. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  42. package/dist/cmd/cloud/deploy.d.ts.map +1 -0
  43. package/dist/cmd/cloud/domain.d.ts +17 -0
  44. package/dist/cmd/cloud/domain.d.ts.map +1 -0
  45. package/dist/cmd/cloud/index.d.ts.map +1 -0
  46. package/dist/cmd/cloud/resource/add.d.ts +2 -0
  47. package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
  48. package/dist/cmd/cloud/resource/delete.d.ts +2 -0
  49. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
  50. package/dist/cmd/cloud/resource/index.d.ts +3 -0
  51. package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
  52. package/dist/cmd/cloud/resource/list.d.ts +2 -0
  53. package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
  54. package/dist/cmd/cloud/scp/download.d.ts +2 -0
  55. package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
  56. package/dist/cmd/cloud/scp/index.d.ts +3 -0
  57. package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
  58. package/dist/cmd/cloud/scp/upload.d.ts +2 -0
  59. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
  60. package/dist/cmd/cloud/ssh.d.ts +2 -0
  61. package/dist/cmd/cloud/ssh.d.ts.map +1 -0
  62. package/dist/cmd/dev/api.d.ts +18 -0
  63. package/dist/cmd/dev/api.d.ts.map +1 -0
  64. package/dist/cmd/dev/download.d.ts +11 -0
  65. package/dist/cmd/dev/download.d.ts.map +1 -0
  66. package/dist/cmd/dev/index.d.ts.map +1 -1
  67. package/dist/cmd/dev/templates.d.ts +3 -0
  68. package/dist/cmd/dev/templates.d.ts.map +1 -0
  69. package/dist/cmd/env/delete.d.ts +2 -0
  70. package/dist/cmd/env/delete.d.ts.map +1 -0
  71. package/dist/cmd/env/get.d.ts +2 -0
  72. package/dist/cmd/env/get.d.ts.map +1 -0
  73. package/dist/cmd/env/import.d.ts +2 -0
  74. package/dist/cmd/env/import.d.ts.map +1 -0
  75. package/dist/cmd/env/index.d.ts +2 -0
  76. package/dist/cmd/env/index.d.ts.map +1 -0
  77. package/dist/cmd/env/list.d.ts.map +1 -0
  78. package/dist/cmd/env/pull.d.ts +2 -0
  79. package/dist/cmd/env/pull.d.ts.map +1 -0
  80. package/dist/cmd/env/push.d.ts +2 -0
  81. package/dist/cmd/env/push.d.ts.map +1 -0
  82. package/dist/cmd/env/set.d.ts +2 -0
  83. package/dist/cmd/env/set.d.ts.map +1 -0
  84. package/dist/cmd/profile/show.d.ts.map +1 -1
  85. package/dist/cmd/project/create.d.ts.map +1 -1
  86. package/dist/cmd/project/delete.d.ts.map +1 -1
  87. package/dist/cmd/project/download.d.ts +1 -1
  88. package/dist/cmd/project/download.d.ts.map +1 -1
  89. package/dist/cmd/project/list.d.ts.map +1 -1
  90. package/dist/cmd/project/show.d.ts.map +1 -1
  91. package/dist/cmd/project/template-flow.d.ts +5 -1
  92. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  93. package/dist/cmd/secret/delete.d.ts +2 -0
  94. package/dist/cmd/secret/delete.d.ts.map +1 -0
  95. package/dist/cmd/secret/get.d.ts +2 -0
  96. package/dist/cmd/secret/get.d.ts.map +1 -0
  97. package/dist/cmd/secret/import.d.ts +2 -0
  98. package/dist/cmd/secret/import.d.ts.map +1 -0
  99. package/dist/cmd/secret/index.d.ts +2 -0
  100. package/dist/cmd/secret/index.d.ts.map +1 -0
  101. package/dist/cmd/secret/list.d.ts +2 -0
  102. package/dist/cmd/secret/list.d.ts.map +1 -0
  103. package/dist/cmd/secret/pull.d.ts +2 -0
  104. package/dist/cmd/secret/pull.d.ts.map +1 -0
  105. package/dist/cmd/secret/push.d.ts +2 -0
  106. package/dist/cmd/secret/push.d.ts.map +1 -0
  107. package/dist/cmd/secret/set.d.ts +2 -0
  108. package/dist/cmd/secret/set.d.ts.map +1 -0
  109. package/dist/cmd/version/index.d.ts.map +1 -1
  110. package/dist/config.d.ts +11 -3
  111. package/dist/config.d.ts.map +1 -1
  112. package/dist/crypto/box.d.ts +65 -0
  113. package/dist/crypto/box.d.ts.map +1 -0
  114. package/dist/crypto/box.test.d.ts +2 -0
  115. package/dist/crypto/box.test.d.ts.map +1 -0
  116. package/dist/download.d.ts.map +1 -1
  117. package/dist/env-util.d.ts +67 -0
  118. package/dist/env-util.d.ts.map +1 -0
  119. package/dist/env-util.test.d.ts +2 -0
  120. package/dist/env-util.test.d.ts.map +1 -0
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/schema-parser.d.ts.map +1 -1
  124. package/dist/steps.d.ts +4 -1
  125. package/dist/steps.d.ts.map +1 -1
  126. package/dist/terminal.d.ts.map +1 -1
  127. package/dist/tui.d.ts +32 -2
  128. package/dist/tui.d.ts.map +1 -1
  129. package/dist/types.d.ts +250 -127
  130. package/dist/types.d.ts.map +1 -1
  131. package/dist/utils/detectSubagent.d.ts +15 -0
  132. package/dist/utils/detectSubagent.d.ts.map +1 -0
  133. package/dist/utils/zip.d.ts +7 -0
  134. package/dist/utils/zip.d.ts.map +1 -0
  135. package/package.json +11 -3
  136. package/src/api-errors.md +2 -2
  137. package/src/api.ts +12 -7
  138. package/src/auth.ts +116 -7
  139. package/src/banner.ts +13 -6
  140. package/src/cli.ts +709 -36
  141. package/src/cmd/auth/api.ts +10 -16
  142. package/src/cmd/auth/index.ts +3 -1
  143. package/src/cmd/auth/login.ts +24 -8
  144. package/src/cmd/auth/signup.ts +15 -11
  145. package/src/cmd/auth/ssh/add.ts +263 -0
  146. package/src/cmd/auth/ssh/api.ts +94 -0
  147. package/src/cmd/auth/ssh/delete.ts +102 -0
  148. package/src/cmd/auth/ssh/index.ts +10 -0
  149. package/src/cmd/auth/ssh/list.ts +74 -0
  150. package/src/cmd/auth/whoami.ts +69 -0
  151. package/src/cmd/bundle/ast.test.ts +565 -0
  152. package/src/cmd/bundle/ast.ts +457 -44
  153. package/src/cmd/bundle/bundler.ts +255 -57
  154. package/src/cmd/bundle/file.ts +6 -12
  155. package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
  156. package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
  157. package/src/cmd/bundle/index.ts +11 -11
  158. package/src/cmd/bundle/patch/aisdk.ts +1 -1
  159. package/src/cmd/bundle/plugin.ts +373 -53
  160. package/src/cmd/cloud/deploy.ts +336 -0
  161. package/src/cmd/cloud/domain.ts +92 -0
  162. package/src/cmd/cloud/index.ts +11 -0
  163. package/src/cmd/cloud/resource/add.ts +56 -0
  164. package/src/cmd/cloud/resource/delete.ts +120 -0
  165. package/src/cmd/cloud/resource/index.ts +11 -0
  166. package/src/cmd/cloud/resource/list.ts +69 -0
  167. package/src/cmd/cloud/scp/download.ts +59 -0
  168. package/src/cmd/cloud/scp/index.ts +9 -0
  169. package/src/cmd/cloud/scp/upload.ts +62 -0
  170. package/src/cmd/cloud/ssh.ts +68 -0
  171. package/src/cmd/dev/api.ts +46 -0
  172. package/src/cmd/dev/download.ts +111 -0
  173. package/src/cmd/dev/index.ts +362 -34
  174. package/src/cmd/dev/templates.ts +84 -0
  175. package/src/cmd/env/delete.ts +47 -0
  176. package/src/cmd/env/get.ts +53 -0
  177. package/src/cmd/env/import.ts +102 -0
  178. package/src/cmd/env/index.ts +22 -0
  179. package/src/cmd/env/list.ts +56 -0
  180. package/src/cmd/env/pull.ts +80 -0
  181. package/src/cmd/env/push.ts +37 -0
  182. package/src/cmd/env/set.ts +71 -0
  183. package/src/cmd/index.ts +2 -2
  184. package/src/cmd/profile/show.ts +15 -6
  185. package/src/cmd/project/create.ts +7 -2
  186. package/src/cmd/project/delete.ts +75 -18
  187. package/src/cmd/project/download.ts +3 -3
  188. package/src/cmd/project/list.ts +8 -8
  189. package/src/cmd/project/show.ts +3 -7
  190. package/src/cmd/project/template-flow.ts +186 -48
  191. package/src/cmd/secret/delete.ts +40 -0
  192. package/src/cmd/secret/get.ts +54 -0
  193. package/src/cmd/secret/import.ts +64 -0
  194. package/src/cmd/secret/index.ts +22 -0
  195. package/src/cmd/secret/list.ts +56 -0
  196. package/src/cmd/secret/pull.ts +78 -0
  197. package/src/cmd/secret/push.ts +37 -0
  198. package/src/cmd/secret/set.ts +45 -0
  199. package/src/cmd/version/index.ts +2 -1
  200. package/src/config.ts +257 -27
  201. package/src/crypto/box.test.ts +431 -0
  202. package/src/crypto/box.ts +477 -0
  203. package/src/download.ts +1 -0
  204. package/src/env-util.test.ts +194 -0
  205. package/src/env-util.ts +290 -0
  206. package/src/index.ts +5 -1
  207. package/src/schema-parser.ts +2 -3
  208. package/src/steps.ts +144 -10
  209. package/src/terminal.ts +24 -23
  210. package/src/tui.ts +208 -68
  211. package/src/types.ts +292 -202
  212. package/src/utils/detectSubagent.ts +31 -0
  213. package/src/utils/zip.ts +38 -0
  214. package/dist/cmd/example/create-user.d.ts +0 -2
  215. package/dist/cmd/example/create-user.d.ts.map +0 -1
  216. package/dist/cmd/example/create.d.ts +0 -2
  217. package/dist/cmd/example/create.d.ts.map +0 -1
  218. package/dist/cmd/example/deploy.d.ts.map +0 -1
  219. package/dist/cmd/example/index.d.ts.map +0 -1
  220. package/dist/cmd/example/list.d.ts.map +0 -1
  221. package/dist/cmd/example/optional-auth.d.ts +0 -3
  222. package/dist/cmd/example/optional-auth.d.ts.map +0 -1
  223. package/dist/cmd/example/run-command.d.ts +0 -2
  224. package/dist/cmd/example/run-command.d.ts.map +0 -1
  225. package/dist/cmd/example/sound.d.ts +0 -3
  226. package/dist/cmd/example/sound.d.ts.map +0 -1
  227. package/dist/cmd/example/spinner.d.ts +0 -2
  228. package/dist/cmd/example/spinner.d.ts.map +0 -1
  229. package/dist/cmd/example/steps.d.ts +0 -2
  230. package/dist/cmd/example/steps.d.ts.map +0 -1
  231. package/dist/cmd/example/version.d.ts +0 -2
  232. package/dist/cmd/example/version.d.ts.map +0 -1
  233. package/dist/logger.d.ts +0 -24
  234. package/dist/logger.d.ts.map +0 -1
  235. package/src/cmd/example/create-user.ts +0 -38
  236. package/src/cmd/example/create.ts +0 -31
  237. package/src/cmd/example/deploy.ts +0 -36
  238. package/src/cmd/example/index.ts +0 -29
  239. package/src/cmd/example/list.ts +0 -32
  240. package/src/cmd/example/optional-auth.ts +0 -38
  241. package/src/cmd/example/run-command.ts +0 -45
  242. package/src/cmd/example/sound.ts +0 -14
  243. package/src/cmd/example/spinner.ts +0 -44
  244. package/src/cmd/example/steps.ts +0 -66
  245. package/src/cmd/example/version.ts +0 -13
  246. package/src/logger.ts +0 -235
  247. /package/dist/cmd/{example → cloud}/deploy.d.ts +0 -0
  248. /package/dist/cmd/{example → cloud}/index.d.ts +0 -0
  249. /package/dist/cmd/{example → env}/list.d.ts +0 -0
@@ -1,17 +1,17 @@
1
1
  import { z } from 'zod';
2
2
  import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
- import { projectDelete } from '@agentuity/server';
5
- import { getAPIBaseURL, APIClient } from '../../api';
4
+ import { projectDelete, projectList } from '@agentuity/server';
5
+ import enquirer from 'enquirer';
6
6
 
7
7
  export const deleteSubcommand = createSubcommand({
8
8
  name: 'delete',
9
9
  description: 'Delete a project',
10
10
  aliases: ['rm', 'del'],
11
- requiresAuth: true,
11
+ requires: { auth: true, apiClient: true },
12
12
  schema: {
13
13
  args: z.object({
14
- id: z.string().describe('the project id'),
14
+ id: z.string().optional().describe('the project id'),
15
15
  }),
16
16
  options: z.object({
17
17
  confirm: z.boolean().optional().describe('Skip confirmation prompts'),
@@ -19,10 +19,56 @@ export const deleteSubcommand = createSubcommand({
19
19
  },
20
20
 
21
21
  async handler(ctx) {
22
- const { args, opts, config } = ctx;
22
+ const { args, opts, apiClient } = ctx;
23
23
 
24
- const apiUrl = getAPIBaseURL(config);
25
- const client = new APIClient(apiUrl, config);
24
+ let projectIds: string[] = [];
25
+
26
+ if (args.id) {
27
+ // Command line argument provided
28
+ projectIds = [args.id];
29
+ } else {
30
+ // Check TTY before attempting to prompt
31
+ if (!process.stdin.isTTY) {
32
+ tui.fatal('--id is required in non-interactive mode');
33
+ }
34
+
35
+ // Fetch projects and prompt for selection
36
+ const projects = await tui.spinner({
37
+ message: 'Fetching projects',
38
+ clearOnSuccess: true,
39
+ callback: async () => {
40
+ return projectList(apiClient);
41
+ },
42
+ });
43
+
44
+ if (projects.length === 0) {
45
+ tui.info('No projects found to delete');
46
+ return;
47
+ }
48
+
49
+ // Sort projects by name
50
+ projects.sort((a, b) => a.name.localeCompare(b.name));
51
+
52
+ // Build choices for multi-select
53
+ const choices: Array<{ name: string; message: string }> = projects.map((project) => ({
54
+ name: project.id,
55
+ message: `${project.name.padEnd(25)} ${tui.muted(project.id)} (${project.orgName})`,
56
+ }));
57
+
58
+ const response = await enquirer.prompt<{ projects: string[] }>({
59
+ type: 'multiselect',
60
+ name: 'projects',
61
+ message: 'Select project(s) to delete:',
62
+ choices,
63
+ });
64
+
65
+ projectIds = response.projects;
66
+ }
67
+
68
+ if (projectIds.length === 0) {
69
+ tui.info('No projects selected for deletion');
70
+ return;
71
+ }
26
72
 
27
73
  const skipConfirm = opts?.confirm === true;
28
74
 
@@ -30,25 +76,36 @@ export const deleteSubcommand = createSubcommand({
30
76
  tui.fatal('no TTY and --confirm is false');
31
77
  }
32
78
 
79
+ // Confirm deletion
33
80
  if (!skipConfirm) {
34
- const ok = await tui.confirm('Are you sure you want to delete', false);
35
- if (!ok) {
81
+ const projectNames = projectIds.join(', ');
82
+ tui.warning(`You are about to delete: ${tui.bold(projectNames)}`);
83
+
84
+ const confirm = await enquirer.prompt<{ confirm: boolean }>({
85
+ type: 'confirm',
86
+ name: 'confirm',
87
+ message: `Are you sure you want to delete ${projectIds.length > 1 ? 'these projects' : 'this project'}?`,
88
+ initial: false,
89
+ });
90
+
91
+ if (!confirm.confirm) {
92
+ tui.info('Deletion cancelled');
36
93
  return;
37
94
  }
38
95
  }
39
96
 
40
- const deleted = await tui.spinner('Deleting project', async () => {
41
- const val = await projectDelete(client!, args.id);
42
- if (val.length === 1 && val[0] === args.id) {
43
- return true;
44
- }
45
- return false;
97
+ const deleted = await tui.spinner({
98
+ message: `Deleting ${projectIds.length} project(s)`,
99
+ clearOnSuccess: true,
100
+ callback: async () => {
101
+ return projectDelete(apiClient, ...projectIds);
102
+ },
46
103
  });
47
104
 
48
- if (deleted) {
49
- tui.success(`Project ${args.id} deleted`);
105
+ if (deleted.length > 0) {
106
+ tui.success(`Deleted ${deleted.length} project(s): ${deleted.join(', ')}`);
50
107
  } else {
51
- tui.warning(`${args.id} not found`);
108
+ tui.error('Failed to delete projects');
52
109
  }
53
110
  },
54
111
  });
@@ -13,7 +13,7 @@ import { tmpdir } from 'node:os';
13
13
  import { finished } from 'node:stream/promises';
14
14
  import { createGunzip } from 'node:zlib';
15
15
  import { extract, type Headers } from 'tar-fs';
16
- import type { Logger } from '../../logger';
16
+ import type { Logger } from '@agentuity/core';
17
17
  import * as tui from '../../tui';
18
18
  import { downloadWithSpinner } from '../../download';
19
19
  import type { TemplateInfo } from './templates';
@@ -250,11 +250,11 @@ export async function setupProject(options: SetupOptions): Promise<void> {
250
250
  clearOnSuccess: true,
251
251
  });
252
252
 
253
- // Create initial commit
253
+ // Create initial commit (disable GPG signing to avoid lock issues)
254
254
  await tui.runCommand({
255
255
  command: 'git commit -m "Initial Setup"',
256
256
  cwd: dest,
257
- cmd: ['git', 'commit', '-m', 'Initial Setup'],
257
+ cmd: ['git', '-c', 'commit.gpgsign=false', 'commit', '-m', 'Initial Setup'],
258
258
  clearOnSuccess: true,
259
259
  });
260
260
  }
@@ -2,13 +2,12 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectList } from '@agentuity/server';
5
- import { getAPIBaseURL, APIClient } from '../../api';
6
5
 
7
6
  export const listSubcommand = createSubcommand({
8
7
  name: 'list',
9
8
  description: 'List all projects',
10
9
  aliases: ['ls'],
11
- requiresAuth: true,
10
+ requires: { auth: true, apiClient: true },
12
11
  schema: {
13
12
  options: z.object({
14
13
  format: z
@@ -19,13 +18,14 @@ export const listSubcommand = createSubcommand({
19
18
  },
20
19
 
21
20
  async handler(ctx) {
22
- const { config, opts } = ctx;
21
+ const { apiClient, opts } = ctx;
23
22
 
24
- const apiUrl = getAPIBaseURL(config);
25
- const client = new APIClient(apiUrl, config);
26
-
27
- const projects = await tui.spinner('Fetching projects', () => {
28
- return projectList(client!);
23
+ const projects = await tui.spinner({
24
+ message: 'Fetching projects',
25
+ clearOnSuccess: true,
26
+ callback: () => {
27
+ return projectList(apiClient);
28
+ },
29
29
  });
30
30
 
31
31
  // TODO: might want to sort by the last org_id we used
@@ -2,13 +2,12 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectGet } from '@agentuity/server';
5
- import { getAPIBaseURL, APIClient } from '../../api';
6
5
 
7
6
  export const showSubcommand = createSubcommand({
8
7
  name: 'show',
9
8
  aliases: ['get'],
10
9
  description: 'Show project detail',
11
- requiresAuth: true,
10
+ requires: { auth: true, apiClient: true },
12
11
  schema: {
13
12
  args: z.object({
14
13
  id: z.string().describe('the project id'),
@@ -22,13 +21,10 @@ export const showSubcommand = createSubcommand({
22
21
  },
23
22
 
24
23
  async handler(ctx) {
25
- const { opts, args, config } = ctx;
26
-
27
- const apiUrl = getAPIBaseURL(config);
28
- const client = new APIClient(apiUrl, config);
24
+ const { opts, args, apiClient } = ctx;
29
25
 
30
26
  const project = await tui.spinner('Fetching project', () => {
31
- return projectGet(client!, { id: args.id, mask: true });
27
+ return projectGet(apiClient, { id: args.id, mask: true });
32
28
  });
33
29
 
34
30
  if (!project) {
@@ -1,4 +1,5 @@
1
1
  import { basename, resolve } from 'node:path';
2
+ import { z } from 'zod';
2
3
  import { existsSync, readdirSync, rmSync, statSync } from 'node:fs';
3
4
  import { cwd } from 'node:process';
4
5
  import { homedir } from 'node:os';
@@ -6,18 +7,29 @@ import enquirer from 'enquirer';
6
7
  import {
7
8
  projectCreate,
8
9
  projectExists,
9
- listOrganizations,
10
- type OrganizationList,
10
+ listResources,
11
+ projectEnvUpdate,
12
+ getServiceUrls,
13
+ APIClient as ServerAPIClient,
14
+ Resources,
15
+ createResources,
11
16
  } from '@agentuity/server';
12
- import type { Logger } from '../../logger';
17
+ import type { Logger } from '@agentuity/core';
13
18
  import * as tui from '../../tui';
14
19
  import { playSound } from '../../sound';
15
20
  import { fetchTemplates, type TemplateInfo } from './templates';
16
21
  import { downloadTemplate, setupProject } from './download';
17
- import { showBanner } from '../../banner';
18
22
  import type { AuthData, Config } from '../../types';
19
- import { getAPIBaseURL, APIClient } from '../../api';
23
+ import type { APIClient } from '../../api';
20
24
  import { createProjectConfig } from '../../config';
25
+ import {
26
+ findEnvFile,
27
+ readEnvFile,
28
+ filterAgentuitySdkKeys,
29
+ splitEnvAndSecrets,
30
+ } from '../../env-util';
31
+
32
+ type ResourcesTypes = z.infer<typeof Resources>;
21
33
 
22
34
  interface CreateFlowOptions {
23
35
  projectName?: string;
@@ -31,6 +43,9 @@ interface CreateFlowOptions {
31
43
  logger: Logger;
32
44
  auth?: AuthData;
33
45
  config?: Config;
46
+ orgId?: string;
47
+ region?: string;
48
+ apiClient?: APIClient;
34
49
  }
35
50
 
36
51
  export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
@@ -44,43 +59,39 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
44
59
  logger,
45
60
  auth,
46
61
  config,
62
+ orgId: selectedOrgId,
63
+ region,
64
+ apiClient,
47
65
  } = options;
48
66
 
49
- showBanner();
50
-
51
- // Step 1: Fetch available templates
67
+ // Fetch available templates
52
68
  if (templateDir) {
53
69
  tui.info(`📋 Loading templates from local directory: ${templateDir}...\n`);
54
70
  }
55
71
 
56
- const templates = await tui.spinner('Fetching templates', async () => {
57
- return fetchTemplates(templateDir, templateBranch);
72
+ const templates = await tui.spinner({
73
+ message: 'Fetching templates',
74
+ clearOnSuccess: true,
75
+ callback: async () => {
76
+ return fetchTemplates(templateDir, templateBranch);
77
+ },
58
78
  });
59
79
 
60
80
  if (templates.length === 0) {
61
81
  logger.fatal('No templates available');
62
82
  }
63
83
 
64
- // Step 2: Get project name
84
+ // Get project name
65
85
  let projectName = initialProjectName;
66
86
 
67
- let orgs: OrganizationList | undefined;
68
- let client: APIClient | undefined;
69
- let orgId: string | undefined;
87
+ // Organization is now automatically selected by the CLI framework via optional: { org: true }
88
+ const orgId = selectedOrgId;
89
+ let catalystClient: ServerAPIClient | undefined;
70
90
 
71
91
  if (auth) {
72
- const apiUrl = getAPIBaseURL(config);
73
- client = new APIClient(apiUrl, config);
74
- orgs = await tui.spinner('Fetching organizations', async () => {
75
- const resp = await listOrganizations(client!);
76
- if (resp.data) {
77
- return resp.data;
78
- }
79
- });
80
- if (!orgs) {
81
- tui.fatal('no organizations could be found for your login');
82
- }
83
- orgId = await tui.selectOrganization(orgs, config?.preferences?.orgId);
92
+ const serviceUrls = getServiceUrls();
93
+ const catalystUrl = config?.overrides?.catalyst_url ?? serviceUrls.catalyst;
94
+ catalystClient = new ServerAPIClient(catalystUrl, logger, auth.apiKey);
84
95
  }
85
96
 
86
97
  if (!projectName && !skipPrompts) {
@@ -93,8 +104,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
93
104
  if (!value || value.trim().length === 0) {
94
105
  return 'Project name is required';
95
106
  }
96
- if (client) {
97
- const exists = await projectExists(client, { name: value, organization_id: orgId! });
107
+ if (apiClient && auth && orgId) {
108
+ const exists = await projectExists(apiClient, {
109
+ name: value,
110
+ organization_id: orgId,
111
+ });
98
112
  if (exists) {
99
113
  return `Project with name '${value}' already exists in this organization`;
100
114
  }
@@ -106,13 +120,13 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
106
120
  }
107
121
  projectName = projectName || 'My First Agent';
108
122
 
109
- // Step 3: Generate disk-friendly directory name
123
+ // Generate disk-friendly directory name
110
124
  const dirName = projectName === '.' ? '.' : sanitizeDirectoryName(projectName);
111
125
 
112
- // Step 4: Determine destination directory
126
+ // Determine destination directory
113
127
  // Expand ~ to home directory
114
128
  let expandedTargetDir = targetDir;
115
- if (expandedTargetDir && expandedTargetDir.startsWith('~')) {
129
+ if (expandedTargetDir?.startsWith('~')) {
116
130
  expandedTargetDir = expandedTargetDir.replace(/^~/, homedir());
117
131
  }
118
132
  const baseDir = expandedTargetDir ? resolve(expandedTargetDir) : process.cwd();
@@ -144,7 +158,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
144
158
  return;
145
159
  }
146
160
  rmSync(dest, { recursive: true, force: true });
147
- tui.success(`Deleted ${dest}\n`);
161
+ tui.success(`Deleted ${dest}`);
148
162
  } else {
149
163
  logger.fatal(`Directory ${dest} already exists and is not empty.`, true);
150
164
  }
@@ -181,7 +195,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
181
195
 
182
196
  tui.info(`✨ Using template: ${tui.bold(selectedTemplate.name)}`);
183
197
 
184
- // Step 6: Download template
198
+ // Download template
185
199
  await downloadTemplate({
186
200
  dest,
187
201
  template: selectedTemplate,
@@ -190,7 +204,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
190
204
  logger,
191
205
  });
192
206
 
193
- // Step 7: Setup project (replace placeholders, install deps, build)
207
+ // Setup project (replace placeholders, install deps, build)
194
208
  await setupProject({
195
209
  dest,
196
210
  projectName: projectName === '.' ? basename(dest) : projectName,
@@ -200,29 +214,153 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
200
214
  logger,
201
215
  });
202
216
 
203
- if (auth && client && orgId) {
204
- await tui.spinner('Registering your project', async () => {
205
- const res = await projectCreate(client, {
206
- name: projectName,
207
- organization_id: orgId,
208
- provider: 'bunjs',
209
- });
210
- if (res.success && res.data) {
217
+ const resourceConfig: ResourcesTypes = Resources.parse({});
218
+
219
+ if (auth && apiClient && catalystClient && orgId && region) {
220
+ // Fetch resources for selected org and region using Catalyst API
221
+ const resources = await tui.spinner({
222
+ message: 'Fetching resources',
223
+ clearOnSuccess: true,
224
+ callback: async () => {
225
+ return listResources(catalystClient, orgId, region);
226
+ },
227
+ });
228
+
229
+ logger.debug(`Resources for org ${orgId} in region ${region}:`, resources);
230
+
231
+ const choices = await enquirer.prompt<{
232
+ db_action: string;
233
+ s3_action: string;
234
+ }>([
235
+ {
236
+ type: 'select',
237
+ name: 'db_action',
238
+ message: 'Create SQL Database?',
239
+ choices: [
240
+ { name: 'Create New', message: 'Create a new (free)' },
241
+ { name: 'Skip', message: 'Skip or Setup later' },
242
+ ...resources.db.map((db) => ({
243
+ name: db.name,
244
+ message: `Use database: ${db.name}`,
245
+ })),
246
+ ],
247
+ },
248
+ {
249
+ type: 'select',
250
+ name: 's3_action',
251
+ message: 'Create Storage Bucket?',
252
+ choices: [
253
+ { name: 'Create New', message: 'Create a new (free)' },
254
+ { name: 'Skip', message: 'Skip or Setup later' },
255
+ ...resources.s3.map((db) => ({
256
+ name: db.bucket_name,
257
+ message: `Use bucket: ${db.bucket_name}`,
258
+ })),
259
+ ],
260
+ },
261
+ ]);
262
+ switch (choices.s3_action) {
263
+ case 'Create New': {
264
+ const created = await tui.spinner({
265
+ message: 'Provisioning New Bucket',
266
+ clearOnSuccess: true,
267
+ callback: async () => {
268
+ return createResources(catalystClient, orgId, region!, [{ type: 's3' }]);
269
+ },
270
+ });
271
+ resourceConfig.storage = created[0].name;
272
+ break;
273
+ }
274
+ case 'Skip': {
275
+ break;
276
+ }
277
+ default: {
278
+ resourceConfig.storage = choices.s3_action;
279
+ break;
280
+ }
281
+ }
282
+ switch (choices.db_action) {
283
+ case 'Create New': {
284
+ const created = await tui.spinner({
285
+ message: 'Provisioning New SQL Database',
286
+ clearOnSuccess: true,
287
+ callback: async () => {
288
+ return createResources(catalystClient, orgId, region!, [{ type: 'db' }]);
289
+ },
290
+ });
291
+ resourceConfig.db = created[0].name;
292
+ break;
293
+ }
294
+ case 'Skip': {
295
+ break;
296
+ }
297
+ default: {
298
+ resourceConfig.db = choices.db_action;
299
+ break;
300
+ }
301
+ }
302
+ }
303
+
304
+ if (auth && apiClient && orgId) {
305
+ let projectId: string | undefined;
306
+
307
+ await tui.spinner({
308
+ message: 'Registering your project',
309
+ clearOnSuccess: true,
310
+ callback: async () => {
311
+ const project = await projectCreate(apiClient, {
312
+ name: projectName,
313
+ organization_id: orgId,
314
+ provider: 'bunjs',
315
+ });
316
+ projectId = project.id;
211
317
  return createProjectConfig(dest, {
212
- projectId: res.data.id,
318
+ projectId: project.id,
213
319
  orgId,
214
- apiKey: res.data.api_key,
320
+ apiKey: project.api_key,
321
+ deployment: {
322
+ resources: resourceConfig,
323
+ },
215
324
  });
216
- }
217
- tui.fatal(res.message ?? 'failed to register project');
325
+ },
218
326
  });
327
+
328
+ // After registration, push any existing env/secrets from .env.production
329
+ if (projectId) {
330
+ await tui.spinner({
331
+ message: 'Syncing environment variables',
332
+ clearOnSuccess: true,
333
+ callback: async () => {
334
+ try {
335
+ const envFilePath = await findEnvFile(dest);
336
+ const localEnv = await readEnvFile(envFilePath);
337
+ const filteredEnv = filterAgentuitySdkKeys(localEnv);
338
+
339
+ if (Object.keys(filteredEnv).length > 0) {
340
+ const { env, secrets } = splitEnvAndSecrets(filteredEnv);
341
+ await projectEnvUpdate(apiClient, {
342
+ id: projectId as string,
343
+ env,
344
+ secrets,
345
+ });
346
+ logger.debug(
347
+ `Synced ${Object.keys(filteredEnv).length} environment variables to cloud`
348
+ );
349
+ }
350
+ } catch (error) {
351
+ // Non-fatal: just log the error
352
+ logger.debug('Failed to sync environment variables:', error);
353
+ }
354
+ },
355
+ });
356
+ }
219
357
  }
220
358
 
221
- // Step 8: Show completion message
359
+ // Show completion message
222
360
  tui.success('✨ Project created successfully!\n');
223
361
  tui.info('Next steps:');
224
362
  if (dirName !== '.') {
225
- const dirDisplay = cwd() == targetDir ? basename(dirName) : dest;
363
+ const dirDisplay = cwd() === targetDir ? basename(dirName) : dest;
226
364
  tui.newline();
227
365
  console.log(` 1. ${tui.bold(`cd ${dirDisplay}`)}`);
228
366
  console.log(` 2. ${tui.bold('bun run dev')}`);
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { projectEnvDelete } from '@agentuity/server';
5
+ import { findEnvFile, readEnvFile, writeEnvFile, filterAgentuitySdkKeys } from '../../env-util';
6
+
7
+ export const deleteSubcommand = createSubcommand({
8
+ name: 'delete',
9
+ aliases: ['del', 'remove', 'rm'],
10
+ description: 'Delete a secret',
11
+ requires: { auth: true, project: true, apiClient: true },
12
+ schema: {
13
+ args: z.object({
14
+ key: z.string().describe('the secret key to delete'),
15
+ }),
16
+ },
17
+
18
+ async handler(ctx) {
19
+ const { args, apiClient, project, projectDir } = ctx;
20
+
21
+ // Delete from cloud (using secrets field)
22
+ await tui.spinner('Deleting secret from cloud', () => {
23
+ return projectEnvDelete(apiClient, {
24
+ id: project.projectId,
25
+ secrets: [args.key],
26
+ });
27
+ });
28
+
29
+ // Update local .env.production file
30
+ const envFilePath = await findEnvFile(projectDir);
31
+ const currentEnv = await readEnvFile(envFilePath);
32
+ delete currentEnv[args.key];
33
+
34
+ // Filter out AGENTUITY_ keys before writing
35
+ const filteredEnv = filterAgentuitySdkKeys(currentEnv);
36
+ await writeEnvFile(envFilePath, filteredEnv);
37
+
38
+ tui.success(`Secret '${args.key}' deleted successfully (cloud + ${envFilePath})`);
39
+ },
40
+ });
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { projectGet } from '@agentuity/server';
5
+ import { maskSecret } from '../../env-util';
6
+
7
+ export const getSubcommand = createSubcommand({
8
+ name: 'get',
9
+ description: 'Get a secret value',
10
+ requires: { auth: true, project: true, apiClient: true },
11
+ schema: {
12
+ args: z.object({
13
+ key: z.string().describe('the secret key'),
14
+ }),
15
+ options: z.object({
16
+ mask: z
17
+ .boolean()
18
+ .default(!!process.stdout.isTTY)
19
+ .describe('mask the value in output (default: true in TTY, false otherwise)'),
20
+ }),
21
+ },
22
+
23
+ async handler(ctx) {
24
+ const { args, opts, apiClient, project } = ctx;
25
+
26
+ // Fetch project with unmasked secrets
27
+ const projectData = await tui.spinner('Fetching secrets', () => {
28
+ return projectGet(apiClient, { id: project.projectId, mask: false });
29
+ });
30
+
31
+ // Look for the key in secrets
32
+ const value = projectData.secrets?.[args.key];
33
+
34
+ if (value === undefined) {
35
+ tui.fatal(`Secret '${args.key}' not found`);
36
+ }
37
+
38
+ if (process.stdout.isTTY) {
39
+ // Display the value, masked by default
40
+ if (opts?.mask) {
41
+ tui.success(`${args.key}=${maskSecret(value)}`);
42
+ } else {
43
+ tui.success(`${args.key}=${value}`);
44
+ }
45
+ } else {
46
+ // Display the value, masked by default
47
+ if (opts?.mask) {
48
+ console.log(`${args.key}=${maskSecret(value)}`);
49
+ } else {
50
+ console.log(`${args.key}=${value}`);
51
+ }
52
+ }
53
+ },
54
+ });