@agentuity/cli 0.0.43 → 0.0.45

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 (209) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/api.d.ts +3 -3
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/auth.d.ts +10 -2
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/banner.d.ts.map +1 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cmd/auth/api.d.ts +4 -4
  10. package/dist/cmd/auth/api.d.ts.map +1 -1
  11. package/dist/cmd/auth/index.d.ts.map +1 -1
  12. package/dist/cmd/auth/login.d.ts.map +1 -1
  13. package/dist/cmd/auth/signup.d.ts.map +1 -1
  14. package/dist/cmd/auth/ssh/add.d.ts +2 -0
  15. package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
  16. package/dist/cmd/auth/ssh/api.d.ts +16 -0
  17. package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
  18. package/dist/cmd/auth/ssh/delete.d.ts +2 -0
  19. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
  20. package/dist/cmd/auth/ssh/index.d.ts +3 -0
  21. package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
  22. package/dist/cmd/auth/ssh/list.d.ts +2 -0
  23. package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
  24. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  25. package/dist/cmd/bundle/ast.d.ts +14 -3
  26. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  27. package/dist/cmd/bundle/ast.test.d.ts +2 -0
  28. package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
  29. package/dist/cmd/bundle/bundler.d.ts +6 -1
  30. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  31. package/dist/cmd/bundle/file.d.ts.map +1 -1
  32. package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
  33. package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
  34. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
  35. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
  36. package/dist/cmd/bundle/plugin.d.ts +2 -0
  37. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  38. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  39. package/dist/cmd/cloud/domain.d.ts +17 -0
  40. package/dist/cmd/cloud/domain.d.ts.map +1 -0
  41. package/dist/cmd/cloud/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/resource/add.d.ts +2 -0
  43. package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
  44. package/dist/cmd/cloud/resource/delete.d.ts +2 -0
  45. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
  46. package/dist/cmd/cloud/resource/index.d.ts +3 -0
  47. package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
  48. package/dist/cmd/cloud/resource/list.d.ts +2 -0
  49. package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
  50. package/dist/cmd/cloud/scp/download.d.ts +2 -0
  51. package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
  52. package/dist/cmd/cloud/scp/index.d.ts +3 -0
  53. package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
  54. package/dist/cmd/cloud/scp/upload.d.ts +2 -0
  55. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
  56. package/dist/cmd/cloud/ssh.d.ts +2 -0
  57. package/dist/cmd/cloud/ssh.d.ts.map +1 -0
  58. package/dist/cmd/dev/api.d.ts +18 -0
  59. package/dist/cmd/dev/api.d.ts.map +1 -0
  60. package/dist/cmd/dev/download.d.ts +11 -0
  61. package/dist/cmd/dev/download.d.ts.map +1 -0
  62. package/dist/cmd/dev/index.d.ts.map +1 -1
  63. package/dist/cmd/dev/templates.d.ts +3 -0
  64. package/dist/cmd/dev/templates.d.ts.map +1 -0
  65. package/dist/cmd/env/delete.d.ts.map +1 -1
  66. package/dist/cmd/env/get.d.ts.map +1 -1
  67. package/dist/cmd/env/import.d.ts.map +1 -1
  68. package/dist/cmd/env/list.d.ts.map +1 -1
  69. package/dist/cmd/env/pull.d.ts.map +1 -1
  70. package/dist/cmd/env/push.d.ts.map +1 -1
  71. package/dist/cmd/env/set.d.ts.map +1 -1
  72. package/dist/cmd/profile/show.d.ts.map +1 -1
  73. package/dist/cmd/project/create.d.ts.map +1 -1
  74. package/dist/cmd/project/delete.d.ts.map +1 -1
  75. package/dist/cmd/project/list.d.ts.map +1 -1
  76. package/dist/cmd/project/show.d.ts.map +1 -1
  77. package/dist/cmd/project/template-flow.d.ts +4 -0
  78. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  79. package/dist/cmd/secret/delete.d.ts.map +1 -1
  80. package/dist/cmd/secret/get.d.ts.map +1 -1
  81. package/dist/cmd/secret/import.d.ts.map +1 -1
  82. package/dist/cmd/secret/list.d.ts.map +1 -1
  83. package/dist/cmd/secret/pull.d.ts.map +1 -1
  84. package/dist/cmd/secret/push.d.ts.map +1 -1
  85. package/dist/cmd/secret/set.d.ts.map +1 -1
  86. package/dist/config.d.ts +9 -3
  87. package/dist/config.d.ts.map +1 -1
  88. package/dist/crypto/box.d.ts +65 -0
  89. package/dist/crypto/box.d.ts.map +1 -0
  90. package/dist/crypto/box.test.d.ts +2 -0
  91. package/dist/crypto/box.test.d.ts.map +1 -0
  92. package/dist/download.d.ts.map +1 -1
  93. package/dist/steps.d.ts +4 -1
  94. package/dist/steps.d.ts.map +1 -1
  95. package/dist/terminal.d.ts.map +1 -1
  96. package/dist/tui.d.ts +31 -1
  97. package/dist/tui.d.ts.map +1 -1
  98. package/dist/types.d.ts +249 -126
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/utils/detectSubagent.d.ts +15 -0
  101. package/dist/utils/detectSubagent.d.ts.map +1 -0
  102. package/dist/utils/zip.d.ts +7 -0
  103. package/dist/utils/zip.d.ts.map +1 -0
  104. package/package.json +11 -3
  105. package/src/api-errors.md +2 -2
  106. package/src/api.ts +12 -7
  107. package/src/auth.ts +116 -7
  108. package/src/banner.ts +13 -6
  109. package/src/cli.ts +695 -63
  110. package/src/cmd/auth/api.ts +10 -16
  111. package/src/cmd/auth/index.ts +2 -1
  112. package/src/cmd/auth/login.ts +24 -8
  113. package/src/cmd/auth/signup.ts +15 -11
  114. package/src/cmd/auth/ssh/add.ts +263 -0
  115. package/src/cmd/auth/ssh/api.ts +94 -0
  116. package/src/cmd/auth/ssh/delete.ts +102 -0
  117. package/src/cmd/auth/ssh/index.ts +10 -0
  118. package/src/cmd/auth/ssh/list.ts +74 -0
  119. package/src/cmd/auth/whoami.ts +13 -13
  120. package/src/cmd/bundle/ast.test.ts +565 -0
  121. package/src/cmd/bundle/ast.ts +457 -44
  122. package/src/cmd/bundle/bundler.ts +255 -57
  123. package/src/cmd/bundle/file.ts +6 -12
  124. package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
  125. package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
  126. package/src/cmd/bundle/index.ts +9 -9
  127. package/src/cmd/bundle/patch/aisdk.ts +1 -1
  128. package/src/cmd/bundle/plugin.ts +373 -53
  129. package/src/cmd/cloud/deploy.ts +300 -93
  130. package/src/cmd/cloud/domain.ts +92 -0
  131. package/src/cmd/cloud/index.ts +4 -1
  132. package/src/cmd/cloud/resource/add.ts +56 -0
  133. package/src/cmd/cloud/resource/delete.ts +120 -0
  134. package/src/cmd/cloud/resource/index.ts +11 -0
  135. package/src/cmd/cloud/resource/list.ts +69 -0
  136. package/src/cmd/cloud/scp/download.ts +59 -0
  137. package/src/cmd/cloud/scp/index.ts +9 -0
  138. package/src/cmd/cloud/scp/upload.ts +62 -0
  139. package/src/cmd/cloud/ssh.ts +68 -0
  140. package/src/cmd/dev/api.ts +46 -0
  141. package/src/cmd/dev/download.ts +111 -0
  142. package/src/cmd/dev/index.ts +360 -34
  143. package/src/cmd/dev/templates.ts +84 -0
  144. package/src/cmd/env/delete.ts +5 -20
  145. package/src/cmd/env/get.ts +5 -18
  146. package/src/cmd/env/import.ts +5 -20
  147. package/src/cmd/env/list.ts +5 -18
  148. package/src/cmd/env/pull.ts +10 -23
  149. package/src/cmd/env/push.ts +5 -23
  150. package/src/cmd/env/set.ts +5 -20
  151. package/src/cmd/index.ts +2 -2
  152. package/src/cmd/profile/show.ts +15 -6
  153. package/src/cmd/project/create.ts +7 -2
  154. package/src/cmd/project/delete.ts +75 -18
  155. package/src/cmd/project/download.ts +2 -2
  156. package/src/cmd/project/list.ts +8 -8
  157. package/src/cmd/project/show.ts +3 -7
  158. package/src/cmd/project/template-flow.ts +170 -72
  159. package/src/cmd/secret/delete.ts +5 -20
  160. package/src/cmd/secret/get.ts +5 -18
  161. package/src/cmd/secret/import.ts +5 -20
  162. package/src/cmd/secret/list.ts +5 -18
  163. package/src/cmd/secret/pull.ts +10 -23
  164. package/src/cmd/secret/push.ts +5 -23
  165. package/src/cmd/secret/set.ts +5 -20
  166. package/src/config.ts +224 -24
  167. package/src/crypto/box.test.ts +431 -0
  168. package/src/crypto/box.ts +477 -0
  169. package/src/download.ts +1 -0
  170. package/src/env-util.test.ts +1 -1
  171. package/src/steps.ts +65 -6
  172. package/src/terminal.ts +24 -23
  173. package/src/tui.ts +192 -61
  174. package/src/types.ts +291 -201
  175. package/src/utils/detectSubagent.ts +31 -0
  176. package/src/utils/zip.ts +38 -0
  177. package/dist/cmd/example/create-user.d.ts +0 -2
  178. package/dist/cmd/example/create-user.d.ts.map +0 -1
  179. package/dist/cmd/example/create.d.ts +0 -2
  180. package/dist/cmd/example/create.d.ts.map +0 -1
  181. package/dist/cmd/example/deploy.d.ts +0 -2
  182. package/dist/cmd/example/deploy.d.ts.map +0 -1
  183. package/dist/cmd/example/index.d.ts +0 -2
  184. package/dist/cmd/example/index.d.ts.map +0 -1
  185. package/dist/cmd/example/list.d.ts +0 -2
  186. package/dist/cmd/example/list.d.ts.map +0 -1
  187. package/dist/cmd/example/optional-auth.d.ts +0 -3
  188. package/dist/cmd/example/optional-auth.d.ts.map +0 -1
  189. package/dist/cmd/example/run-command.d.ts +0 -2
  190. package/dist/cmd/example/run-command.d.ts.map +0 -1
  191. package/dist/cmd/example/sound.d.ts +0 -3
  192. package/dist/cmd/example/sound.d.ts.map +0 -1
  193. package/dist/cmd/example/spinner.d.ts +0 -2
  194. package/dist/cmd/example/spinner.d.ts.map +0 -1
  195. package/dist/cmd/example/steps.d.ts +0 -2
  196. package/dist/cmd/example/steps.d.ts.map +0 -1
  197. package/dist/cmd/example/version.d.ts +0 -2
  198. package/dist/cmd/example/version.d.ts.map +0 -1
  199. package/src/cmd/example/create-user.ts +0 -38
  200. package/src/cmd/example/create.ts +0 -31
  201. package/src/cmd/example/deploy.ts +0 -36
  202. package/src/cmd/example/index.ts +0 -29
  203. package/src/cmd/example/list.ts +0 -32
  204. package/src/cmd/example/optional-auth.ts +0 -38
  205. package/src/cmd/example/run-command.ts +0 -45
  206. package/src/cmd/example/sound.ts +0 -14
  207. package/src/cmd/example/spinner.ts +0 -44
  208. package/src/cmd/example/steps.ts +0 -66
  209. package/src/cmd/example/version.ts +0 -13
@@ -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,19 +7,21 @@ import enquirer from 'enquirer';
6
7
  import {
7
8
  projectCreate,
8
9
  projectExists,
9
- listOrganizations,
10
+ listResources,
10
11
  projectEnvUpdate,
11
- type OrganizationList,
12
+ getServiceUrls,
13
+ APIClient as ServerAPIClient,
14
+ Resources,
15
+ createResources,
12
16
  } from '@agentuity/server';
13
17
  import type { Logger } from '@agentuity/core';
14
18
  import * as tui from '../../tui';
15
19
  import { playSound } from '../../sound';
16
20
  import { fetchTemplates, type TemplateInfo } from './templates';
17
21
  import { downloadTemplate, setupProject } from './download';
18
- import { showBanner } from '../../banner';
19
22
  import type { AuthData, Config } from '../../types';
20
- import { getAPIBaseURL, APIClient } from '../../api';
21
- import { createProjectConfig, saveOrgId } from '../../config';
23
+ import type { APIClient } from '../../api';
24
+ import { createProjectConfig } from '../../config';
22
25
  import {
23
26
  findEnvFile,
24
27
  readEnvFile,
@@ -26,6 +29,8 @@ import {
26
29
  splitEnvAndSecrets,
27
30
  } from '../../env-util';
28
31
 
32
+ type ResourcesTypes = z.infer<typeof Resources>;
33
+
29
34
  interface CreateFlowOptions {
30
35
  projectName?: string;
31
36
  dir?: string;
@@ -38,6 +43,9 @@ interface CreateFlowOptions {
38
43
  logger: Logger;
39
44
  auth?: AuthData;
40
45
  config?: Config;
46
+ orgId?: string;
47
+ region?: string;
48
+ apiClient?: APIClient;
41
49
  }
42
50
 
43
51
  export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
@@ -51,47 +59,39 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
51
59
  logger,
52
60
  auth,
53
61
  config,
62
+ orgId: selectedOrgId,
63
+ region,
64
+ apiClient,
54
65
  } = options;
55
66
 
56
- showBanner();
57
-
58
- // Step 1: Fetch available templates
67
+ // Fetch available templates
59
68
  if (templateDir) {
60
69
  tui.info(`📋 Loading templates from local directory: ${templateDir}...\n`);
61
70
  }
62
71
 
63
- const templates = await tui.spinner('Fetching templates', async () => {
64
- 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
+ },
65
78
  });
66
79
 
67
80
  if (templates.length === 0) {
68
81
  logger.fatal('No templates available');
69
82
  }
70
83
 
71
- // Step 2: Get project name
84
+ // Get project name
72
85
  let projectName = initialProjectName;
73
86
 
74
- let orgs: OrganizationList | undefined;
75
- let client: APIClient | undefined;
76
- 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;
77
90
 
78
91
  if (auth) {
79
- const apiUrl = getAPIBaseURL(config);
80
- client = new APIClient(apiUrl, config);
81
- orgs = await tui.spinner('Fetching organizations', async () => {
82
- const resp = await listOrganizations(client!);
83
- if (resp.data) {
84
- return resp.data;
85
- }
86
- });
87
- if (!orgs) {
88
- tui.fatal('no organizations could be found for your login');
89
- }
90
- orgId = await tui.selectOrganization(orgs, config?.preferences?.orgId);
91
-
92
- if (orgId && orgId !== config?.preferences?.orgId) {
93
- await saveOrgId(orgId);
94
- }
92
+ const serviceUrls = getServiceUrls();
93
+ const catalystUrl = config?.overrides?.catalyst_url ?? serviceUrls.catalyst;
94
+ catalystClient = new ServerAPIClient(catalystUrl, logger, auth.apiKey);
95
95
  }
96
96
 
97
97
  if (!projectName && !skipPrompts) {
@@ -104,8 +104,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
104
104
  if (!value || value.trim().length === 0) {
105
105
  return 'Project name is required';
106
106
  }
107
- if (client) {
108
- 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
+ });
109
112
  if (exists) {
110
113
  return `Project with name '${value}' already exists in this organization`;
111
114
  }
@@ -117,13 +120,13 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
117
120
  }
118
121
  projectName = projectName || 'My First Agent';
119
122
 
120
- // Step 3: Generate disk-friendly directory name
123
+ // Generate disk-friendly directory name
121
124
  const dirName = projectName === '.' ? '.' : sanitizeDirectoryName(projectName);
122
125
 
123
- // Step 4: Determine destination directory
126
+ // Determine destination directory
124
127
  // Expand ~ to home directory
125
128
  let expandedTargetDir = targetDir;
126
- if (expandedTargetDir && expandedTargetDir.startsWith('~')) {
129
+ if (expandedTargetDir?.startsWith('~')) {
127
130
  expandedTargetDir = expandedTargetDir.replace(/^~/, homedir());
128
131
  }
129
132
  const baseDir = expandedTargetDir ? resolve(expandedTargetDir) : process.cwd();
@@ -155,7 +158,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
155
158
  return;
156
159
  }
157
160
  rmSync(dest, { recursive: true, force: true });
158
- tui.success(`Deleted ${dest}\n`);
161
+ tui.success(`Deleted ${dest}`);
159
162
  } else {
160
163
  logger.fatal(`Directory ${dest} already exists and is not empty.`, true);
161
164
  }
@@ -192,7 +195,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
192
195
 
193
196
  tui.info(`✨ Using template: ${tui.bold(selectedTemplate.name)}`);
194
197
 
195
- // Step 6: Download template
198
+ // Download template
196
199
  await downloadTemplate({
197
200
  dest,
198
201
  template: selectedTemplate,
@@ -201,7 +204,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
201
204
  logger,
202
205
  });
203
206
 
204
- // Step 7: Setup project (replace placeholders, install deps, build)
207
+ // Setup project (replace placeholders, install deps, build)
205
208
  await setupProject({
206
209
  dest,
207
210
  projectName: projectName === '.' ? basename(dest) : projectName,
@@ -211,58 +214,153 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
211
214
  logger,
212
215
  });
213
216
 
214
- if (auth && client && orgId) {
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) {
215
305
  let projectId: string | undefined;
216
306
 
217
- await tui.spinner('Registering your project', async () => {
218
- const res = await projectCreate(client, {
219
- name: projectName,
220
- organization_id: orgId,
221
- provider: 'bunjs',
222
- });
223
- if (res.success && res.data) {
224
- projectId = res.data.id;
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;
225
317
  return createProjectConfig(dest, {
226
- projectId: res.data.id,
318
+ projectId: project.id,
227
319
  orgId,
228
- apiKey: res.data.api_key,
320
+ apiKey: project.api_key,
321
+ deployment: {
322
+ resources: resourceConfig,
323
+ },
229
324
  });
230
- }
231
- tui.fatal(res.message ?? 'failed to register project');
325
+ },
232
326
  });
233
327
 
234
328
  // After registration, push any existing env/secrets from .env.production
235
329
  if (projectId) {
236
- await tui.spinner('Syncing environment variables', async () => {
237
- try {
238
- const envFilePath = await findEnvFile(dest);
239
- const localEnv = await readEnvFile(envFilePath);
240
- const filteredEnv = filterAgentuitySdkKeys(localEnv);
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);
241
338
 
242
- if (Object.keys(filteredEnv).length > 0) {
243
- const { env, secrets } = splitEnvAndSecrets(filteredEnv);
244
- await projectEnvUpdate(client, {
245
- id: projectId!,
246
- env,
247
- secrets,
248
- });
249
- logger.debug(
250
- `Synced ${Object.keys(filteredEnv).length} environment variables to cloud`
251
- );
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);
252
353
  }
253
- } catch (error) {
254
- // Non-fatal: just log the error
255
- logger.debug('Failed to sync environment variables:', error);
256
- }
354
+ },
257
355
  });
258
356
  }
259
357
  }
260
358
 
261
- // Step 8: Show completion message
359
+ // Show completion message
262
360
  tui.success('✨ Project created successfully!\n');
263
361
  tui.info('Next steps:');
264
362
  if (dirName !== '.') {
265
- const dirDisplay = cwd() == targetDir ? basename(dirName) : dest;
363
+ const dirDisplay = cwd() === targetDir ? basename(dirName) : dest;
266
364
  tui.newline();
267
365
  console.log(` 1. ${tui.bold(`cd ${dirDisplay}`)}`);
268
366
  console.log(` 2. ${tui.bold('bun run dev')}`);
@@ -2,47 +2,32 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectEnvDelete } from '@agentuity/server';
5
- import { getAPIBaseURL, APIClient } from '../../api';
6
- import { loadProjectConfig } from '../../config';
7
5
  import { findEnvFile, readEnvFile, writeEnvFile, filterAgentuitySdkKeys } from '../../env-util';
8
6
 
9
7
  export const deleteSubcommand = createSubcommand({
10
8
  name: 'delete',
11
9
  aliases: ['del', 'remove', 'rm'],
12
10
  description: 'Delete a secret',
13
- requiresAuth: true,
11
+ requires: { auth: true, project: true, apiClient: true },
14
12
  schema: {
15
13
  args: z.object({
16
14
  key: z.string().describe('the secret key to delete'),
17
15
  }),
18
- options: z.object({
19
- dir: z.string().optional().describe('project directory (default: current directory)'),
20
- }),
21
16
  },
22
17
 
23
18
  async handler(ctx) {
24
- const { args, opts, config } = ctx;
25
- const dir = opts?.dir ?? process.cwd();
26
-
27
- // Load project config to get project ID
28
- const projectConfig = await loadProjectConfig(dir);
29
- if (!projectConfig) {
30
- tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
31
- }
32
-
33
- const apiUrl = getAPIBaseURL(config);
34
- const client = new APIClient(apiUrl, config);
19
+ const { args, apiClient, project, projectDir } = ctx;
35
20
 
36
21
  // Delete from cloud (using secrets field)
37
22
  await tui.spinner('Deleting secret from cloud', () => {
38
- return projectEnvDelete(client, {
39
- id: projectConfig.projectId,
23
+ return projectEnvDelete(apiClient, {
24
+ id: project.projectId,
40
25
  secrets: [args.key],
41
26
  });
42
27
  });
43
28
 
44
29
  // Update local .env.production file
45
- const envFilePath = await findEnvFile(dir);
30
+ const envFilePath = await findEnvFile(projectDir);
46
31
  const currentEnv = await readEnvFile(envFilePath);
47
32
  delete currentEnv[args.key];
48
33
 
@@ -2,20 +2,17 @@ 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
- import { loadProjectConfig } from '../../config';
7
5
  import { maskSecret } from '../../env-util';
8
6
 
9
7
  export const getSubcommand = createSubcommand({
10
8
  name: 'get',
11
9
  description: 'Get a secret value',
12
- requiresAuth: true,
10
+ requires: { auth: true, project: true, apiClient: true },
13
11
  schema: {
14
12
  args: z.object({
15
13
  key: z.string().describe('the secret key'),
16
14
  }),
17
15
  options: z.object({
18
- dir: z.string().optional().describe('project directory (default: current directory)'),
19
16
  mask: z
20
17
  .boolean()
21
18
  .default(!!process.stdout.isTTY)
@@ -24,25 +21,15 @@ export const getSubcommand = createSubcommand({
24
21
  },
25
22
 
26
23
  async handler(ctx) {
27
- const { args, opts, config } = ctx;
28
- const dir = opts?.dir ?? process.cwd();
29
-
30
- // Load project config to get project ID
31
- const projectConfig = await loadProjectConfig(dir);
32
- if (!projectConfig) {
33
- tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
34
- }
35
-
36
- const apiUrl = getAPIBaseURL(config);
37
- const client = new APIClient(apiUrl, config);
24
+ const { args, opts, apiClient, project } = ctx;
38
25
 
39
26
  // Fetch project with unmasked secrets
40
- const project = await tui.spinner('Fetching secrets', () => {
41
- return projectGet(client, { id: projectConfig.projectId, mask: false });
27
+ const projectData = await tui.spinner('Fetching secrets', () => {
28
+ return projectGet(apiClient, { id: project.projectId, mask: false });
42
29
  });
43
30
 
44
31
  // Look for the key in secrets
45
- const value = project.secrets?.[args.key];
32
+ const value = projectData.secrets?.[args.key];
46
33
 
47
34
  if (value === undefined) {
48
35
  tui.fatal(`Secret '${args.key}' not found`);
@@ -2,8 +2,6 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectEnvUpdate } from '@agentuity/server';
5
- import { getAPIBaseURL, APIClient } from '../../api';
6
- import { loadProjectConfig } from '../../config';
7
5
  import {
8
6
  findEnvFile,
9
7
  readEnvFile,
@@ -15,25 +13,15 @@ import {
15
13
  export const importSubcommand = createSubcommand({
16
14
  name: 'import',
17
15
  description: 'Import secrets from a file to cloud and local .env.production',
18
- requiresAuth: true,
16
+ requires: { auth: true, project: true, apiClient: true },
19
17
  schema: {
20
18
  args: z.object({
21
19
  file: z.string().describe('path to the .env file to import'),
22
20
  }),
23
- options: z.object({
24
- dir: z.string().optional().describe('project directory (default: current directory)'),
25
- }),
26
21
  },
27
22
 
28
23
  async handler(ctx) {
29
- const { args, opts, config } = ctx;
30
- const dir = opts?.dir ?? process.cwd();
31
-
32
- // Load project config to get project ID
33
- const projectConfig = await loadProjectConfig(dir);
34
- if (!projectConfig) {
35
- tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
36
- }
24
+ const { args, apiClient, project, projectDir } = ctx;
37
25
 
38
26
  // Read the import file
39
27
  const importedSecrets = await readEnvFile(args.file);
@@ -51,19 +39,16 @@ export const importSubcommand = createSubcommand({
51
39
  return;
52
40
  }
53
41
 
54
- const apiUrl = getAPIBaseURL(config);
55
- const client = new APIClient(apiUrl, config);
56
-
57
42
  // Push to cloud (using secrets field)
58
43
  await tui.spinner('Importing secrets to cloud', () => {
59
- return projectEnvUpdate(client, {
60
- id: projectConfig.projectId,
44
+ return projectEnvUpdate(apiClient, {
45
+ id: project.projectId,
61
46
  secrets: filteredSecrets,
62
47
  });
63
48
  });
64
49
 
65
50
  // Merge with local .env.production file
66
- const localEnvPath = await findEnvFile(dir);
51
+ const localEnvPath = await findEnvFile(projectDir);
67
52
  const localEnv = await readEnvFile(localEnvPath);
68
53
  const mergedEnv = mergeEnvVars(localEnv, filteredSecrets);
69
54
 
@@ -2,18 +2,15 @@ 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
- import { loadProjectConfig } from '../../config';
7
5
  import { maskSecret } from '../../env-util';
8
6
 
9
7
  export const listSubcommand = createSubcommand({
10
8
  name: 'list',
11
9
  aliases: ['ls'],
12
10
  description: 'List all secrets',
13
- requiresAuth: true,
11
+ requires: { auth: true, project: true, apiClient: true },
14
12
  schema: {
15
13
  options: z.object({
16
- dir: z.string().optional().describe('project directory (default: current directory)'),
17
14
  mask: z
18
15
  .boolean()
19
16
  .default(!!process.stdout.isTTY)
@@ -22,24 +19,14 @@ export const listSubcommand = createSubcommand({
22
19
  },
23
20
 
24
21
  async handler(ctx) {
25
- const { opts, config } = ctx;
26
- const dir = opts?.dir ?? process.cwd();
27
-
28
- // Load project config to get project ID
29
- const projectConfig = await loadProjectConfig(dir);
30
- if (!projectConfig) {
31
- tui.fatal(`No Agentuity project found in ${dir}. Missing agentuity.json`);
32
- }
33
-
34
- const apiUrl = getAPIBaseURL(config);
35
- const client = new APIClient(apiUrl, config);
22
+ const { opts, apiClient, project } = ctx;
36
23
 
37
24
  // Fetch project with unmasked secrets
38
- const project = await tui.spinner('Fetching secrets', () => {
39
- return projectGet(client, { id: projectConfig.projectId, mask: false });
25
+ const projectData = await tui.spinner('Fetching secrets', () => {
26
+ return projectGet(apiClient, { id: project.projectId, mask: false });
40
27
  });
41
28
 
42
- const secrets = project.secrets || {};
29
+ const secrets = projectData.secrets || {};
43
30
 
44
31
  if (Object.keys(secrets).length === 0) {
45
32
  tui.info('No secrets found');