@agentuity/cli 0.0.43 → 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 (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
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+ import { listResources, deleteResources } from '@agentuity/server';
3
+ import enquirer from 'enquirer';
4
+ import { createSubcommand } from '../../../types';
5
+ import * as tui from '../../../tui';
6
+ import { getCatalystAPIClient } from '../../../config';
7
+
8
+ export const deleteSubcommand = createSubcommand({
9
+ name: 'delete',
10
+ description: 'Delete cloud resource(s) for an organization',
11
+ aliases: ['rm', 'del', 'remove'],
12
+ requires: { auth: true, org: true, region: true },
13
+ schema: {
14
+ options: z.object({
15
+ type: z.enum(['db', 's3']).optional().describe('Resource type (db or s3)'),
16
+ name: z.string().optional().describe('Resource name to delete'),
17
+ confirm: z.boolean().optional().describe('Skip confirmation prompts'),
18
+ }),
19
+ },
20
+
21
+ async handler(ctx) {
22
+ const { logger, opts, config, orgId, region, auth } = ctx;
23
+
24
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
25
+
26
+ // Determine what to delete
27
+ let resourcesToDelete: Array<{ type: 'db' | 's3'; name: string }> = [];
28
+
29
+ if (opts.type && opts.name) {
30
+ // Command line arguments provided
31
+ resourcesToDelete = [{ type: opts.type, name: opts.name }];
32
+ } else {
33
+ // Fetch resources and prompt for selection
34
+ const resources = await tui.spinner({
35
+ message: `Fetching resources for ${orgId} in ${region}`,
36
+ clearOnSuccess: true,
37
+ callback: async () => {
38
+ return listResources(catalystClient, orgId, region!);
39
+ },
40
+ });
41
+
42
+ if (resources.db.length === 0 && resources.s3.length === 0) {
43
+ tui.info('No resources found to delete');
44
+ return;
45
+ }
46
+
47
+ // Build choices for multi-select and resource map
48
+ const choices: Array<{ name: string; message: string }> = [];
49
+ const resourceMap = new Map<string, { type: 'db' | 's3'; name: string }>();
50
+
51
+ for (const db of resources.db) {
52
+ const key = `db:${db.name}`;
53
+ choices.push({
54
+ name: key,
55
+ message: `Database: ${db.name}`,
56
+ });
57
+ resourceMap.set(key, { type: 'db', name: db.name });
58
+ }
59
+
60
+ for (const s3 of resources.s3) {
61
+ const key = `s3:${s3.bucket_name}`;
62
+ choices.push({
63
+ name: key,
64
+ message: `Storage: ${s3.bucket_name}`,
65
+ });
66
+ resourceMap.set(key, { type: 's3', name: s3.bucket_name });
67
+ }
68
+
69
+ const response = await enquirer.prompt<{ resources: string[] }>({
70
+ type: 'multiselect',
71
+ name: 'resources',
72
+ message: 'Select resource(s) to delete:',
73
+ choices,
74
+ });
75
+
76
+ // Map selected keys back to resource objects
77
+ resourcesToDelete = response.resources
78
+ .map((key) => resourceMap.get(key))
79
+ .filter((r): r is { type: 'db' | 's3'; name: string } => r !== undefined);
80
+ }
81
+
82
+ if (resourcesToDelete.length === 0) {
83
+ tui.info('No resources selected for deletion');
84
+ return;
85
+ }
86
+
87
+ // Confirm deletion
88
+ if (!opts.confirm) {
89
+ const resourceNames = resourcesToDelete.map((r) => `${r.type}:${r.name}`).join(', ');
90
+ tui.warning(`You are about to delete: ${tui.bold(resourceNames)}`);
91
+
92
+ const confirm = await enquirer.prompt<{ confirm: boolean }>({
93
+ type: 'confirm',
94
+ name: 'confirm',
95
+ message: 'Are you sure you want to delete these resources?',
96
+ initial: false,
97
+ });
98
+
99
+ if (!confirm.confirm) {
100
+ tui.info('Deletion cancelled');
101
+ return;
102
+ }
103
+ }
104
+
105
+ // Delete resources
106
+ const deleted = await tui.spinner({
107
+ message: `Deleting ${resourcesToDelete.length} resource(s)`,
108
+ clearOnSuccess: true,
109
+ callback: async () => {
110
+ return deleteResources(catalystClient, orgId, region!, resourcesToDelete);
111
+ },
112
+ });
113
+
114
+ if (deleted.length > 0) {
115
+ tui.success(`Deleted ${deleted.length} resource(s): ${deleted.join(', ')}`);
116
+ } else {
117
+ tui.error('Failed to delete resources');
118
+ }
119
+ },
120
+ });
@@ -0,0 +1,11 @@
1
+ import type { SubcommandDefinition } from '../../../types';
2
+ import { listSubcommand } from './list';
3
+ import { addSubcommand } from './add';
4
+ import { deleteSubcommand } from './delete';
5
+
6
+ export const resourceSubcommand: SubcommandDefinition = {
7
+ name: 'resource',
8
+ aliases: ['resources'],
9
+ description: 'Manage cloud resources',
10
+ subcommands: [listSubcommand, addSubcommand, deleteSubcommand],
11
+ };
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { listResources } from '@agentuity/server';
3
+ import { createSubcommand } from '../../../types';
4
+ import * as tui from '../../../tui';
5
+ import { getCatalystAPIClient } from '../../../config';
6
+
7
+ export const listSubcommand = createSubcommand({
8
+ name: 'list',
9
+ description: 'List cloud resources for an organization',
10
+ aliases: ['ls'],
11
+ requires: { auth: true, org: true, region: true },
12
+ schema: {
13
+ options: z.object({
14
+ format: z
15
+ .enum(['text', 'json'])
16
+ .optional()
17
+ .default('text')
18
+ .describe('Output format (text or json)'),
19
+ }),
20
+ },
21
+
22
+ async handler(ctx) {
23
+ const { logger, opts, orgId, region, config, auth } = ctx;
24
+
25
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
26
+
27
+ const resources = await tui.spinner({
28
+ message: `Fetching resources for ${orgId} in ${region}`,
29
+ clearOnSuccess: true,
30
+ callback: async () => {
31
+ return listResources(catalystClient, orgId, region);
32
+ },
33
+ });
34
+
35
+ // Output based on format
36
+ if (opts.format === 'json') {
37
+ console.log(JSON.stringify(resources, null, 2));
38
+ } else {
39
+ // Text table format
40
+ if (resources.db.length === 0 && resources.s3.length === 0) {
41
+ tui.info('No resources found');
42
+ return;
43
+ }
44
+
45
+ if (resources.db.length > 0) {
46
+ tui.info(tui.bold('Databases'));
47
+ tui.newline();
48
+ for (const db of resources.db) {
49
+ console.log(tui.bold(db.name));
50
+ if (db.url) console.log(` URL: ${tui.muted(db.url)}`);
51
+ tui.newline();
52
+ }
53
+ }
54
+
55
+ if (resources.s3.length > 0) {
56
+ tui.info(tui.bold('Storage'));
57
+ tui.newline();
58
+ for (const s3 of resources.s3) {
59
+ console.log(tui.bold(s3.bucket_name));
60
+ if (s3.access_key) console.log(` Access Key: ${tui.muted(s3.access_key)}`);
61
+ if (s3.secret_key) console.log(` Secret Key: ${tui.muted(s3.secret_key)}`);
62
+ if (s3.region) console.log(` Region: ${tui.muted(s3.region)}`);
63
+ if (s3.endpoint) console.log(` Endpoint: ${tui.muted(s3.endpoint)}`);
64
+ tui.newline();
65
+ }
66
+ }
67
+ }
68
+ },
69
+ });
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { getIONHost } from '../../../config';
5
+
6
+ const args = z.object({
7
+ source: z.string().describe('the source file'),
8
+ destination: z.string().optional().describe('the destination file'),
9
+ });
10
+
11
+ const options = z.object({
12
+ identifier: z.string().optional().describe('The project or deployment id to use'),
13
+ });
14
+
15
+ export const downloadCommand = createSubcommand({
16
+ name: 'download',
17
+ aliases: ['get'],
18
+ description: 'Download a file using security copy',
19
+ requires: { apiClient: true, auth: true },
20
+ optional: { project: true },
21
+ schema: { args, options },
22
+
23
+ async handler(ctx) {
24
+ const { apiClient, args, opts, project, projectDir, config } = ctx;
25
+
26
+ let identifier = opts?.identifier ?? project?.projectId;
27
+
28
+ if (!identifier) {
29
+ identifier = await tui.showProjectList(apiClient, true);
30
+ }
31
+
32
+ const hostname = getIONHost(config);
33
+ const destination = args.destination ?? projectDir;
34
+
35
+ try {
36
+ const spawn = Bun.spawn({
37
+ cmd: ['scp', `${identifier}@${hostname}:${args.source}`, destination],
38
+ cwd: projectDir,
39
+ stdout: 'inherit',
40
+ stderr: 'inherit',
41
+ stdin: 'inherit',
42
+ });
43
+
44
+ await spawn.exited;
45
+
46
+ if (spawn.exitCode !== 0) {
47
+ tui.error(
48
+ `SCP download failed: ${identifier}@${hostname}:${args.source} -> ${destination} (exit code: ${spawn.exitCode})`
49
+ );
50
+ process.exit(spawn.exitCode ?? 1);
51
+ }
52
+ } catch (error) {
53
+ tui.error(
54
+ `SCP download error: ${error instanceof Error ? error.message : 'Unknown error'}`
55
+ );
56
+ process.exit(1);
57
+ }
58
+ },
59
+ });
@@ -0,0 +1,9 @@
1
+ import type { SubcommandDefinition } from '../../../types';
2
+ import { downloadCommand } from './download';
3
+ import { uploadCommand } from './upload';
4
+
5
+ export const scpSubcommand: SubcommandDefinition = {
6
+ name: 'scp',
7
+ description: 'Secure Copy commands',
8
+ subcommands: [downloadCommand, uploadCommand],
9
+ };
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { getIONHost } from '../../../config';
5
+
6
+ const args = z.object({
7
+ source: z.string().describe('the source file'),
8
+ destination: z
9
+ .string()
10
+ .optional()
11
+ .describe('the destination file (defaults to . for current directory on remote)'),
12
+ });
13
+
14
+ const options = z.object({
15
+ identifier: z.string().optional().describe('The project or deployment id to use'),
16
+ });
17
+
18
+ export const uploadCommand = createSubcommand({
19
+ name: 'upload',
20
+ aliases: ['cp', 'put'],
21
+ description: 'Upload a file using security copy',
22
+ requires: { apiClient: true, auth: true },
23
+ schema: { args, options },
24
+ optional: { project: true },
25
+
26
+ async handler(ctx) {
27
+ const { apiClient, args, opts, project, projectDir, config } = ctx;
28
+
29
+ let identifier = opts?.identifier ?? project?.projectId;
30
+
31
+ if (!identifier) {
32
+ identifier = await tui.showProjectList(apiClient, true);
33
+ }
34
+
35
+ const hostname = getIONHost(config);
36
+ const destination = args.destination ?? '.';
37
+
38
+ try {
39
+ const spawn = Bun.spawn({
40
+ cmd: ['scp', args.source, `${identifier}@${hostname}:${destination}`],
41
+ cwd: projectDir,
42
+ stdout: 'inherit',
43
+ stderr: 'inherit',
44
+ stdin: 'inherit',
45
+ });
46
+
47
+ await spawn.exited;
48
+
49
+ if (spawn.exitCode !== 0) {
50
+ tui.error(
51
+ `SCP upload failed: ${args.source} -> ${identifier}@${hostname}:${destination} (exit code: ${spawn.exitCode})`
52
+ );
53
+ process.exit(spawn.exitCode ?? 1);
54
+ }
55
+ } catch (error) {
56
+ tui.error(
57
+ `SCP upload error: ${error instanceof Error ? error.message : 'Unknown error'}`
58
+ );
59
+ process.exit(1);
60
+ }
61
+ },
62
+ });
@@ -0,0 +1,68 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { getIONHost } from '../../config';
5
+
6
+ const args = z.object({
7
+ identifier: z.string().optional().describe('The project or deployment id to use'),
8
+ command: z.string().optional().describe('The command to run'),
9
+ });
10
+
11
+ const options = z.object({
12
+ show: z.boolean().optional().describe('Show the command and exit'),
13
+ });
14
+
15
+ export const sshSubcommand = createSubcommand({
16
+ name: 'ssh',
17
+ description: 'SSH into a cloud project',
18
+ toplevel: true,
19
+ requires: { auth: true, apiClient: true },
20
+ optional: { project: true },
21
+ schema: { args, options },
22
+
23
+ async handler(ctx) {
24
+ const { apiClient, project, projectDir, args, config, opts } = ctx;
25
+
26
+ let projectId = project?.projectId;
27
+ let identifier = args?.identifier;
28
+ let command = args?.command;
29
+
30
+ if (!(identifier?.startsWith('proj_') || identifier?.startsWith('deploy_'))) {
31
+ command = identifier;
32
+ identifier = undefined;
33
+ }
34
+
35
+ if (!projectId && !identifier) {
36
+ projectId = await tui.showProjectList(apiClient, true);
37
+ }
38
+
39
+ const hostname = getIONHost(config);
40
+
41
+ const cmd = ['ssh', `${identifier ?? projectId}@${hostname}`, command].filter(
42
+ Boolean
43
+ ) as string[];
44
+
45
+ // if show is passed, just show the SSH command and exit
46
+ if (opts?.show) {
47
+ if (command) {
48
+ // if we have a command we want to show it quoted
49
+ console.log(cmd[0], cmd[1], `'${cmd.slice(2).join(' ')}'`);
50
+ } else {
51
+ console.log(cmd.join(' '));
52
+ }
53
+ process.exit(0);
54
+ }
55
+
56
+ const spawn = Bun.spawn({
57
+ cmd,
58
+ cwd: projectDir,
59
+ stdout: 'inherit',
60
+ stderr: 'inherit',
61
+ stdin: 'inherit',
62
+ });
63
+
64
+ await spawn.exited;
65
+
66
+ process.exit(spawn.exitCode);
67
+ },
68
+ });
@@ -0,0 +1,46 @@
1
+ import { APIResponseSchema } from '@agentuity/server';
2
+ import { z } from 'zod';
3
+ import type { APIClient } from '../../api';
4
+
5
+ const DevmodeRequestSchema = z.object({
6
+ hostname: z.string().optional().describe('the hostname for the endpoint'),
7
+ });
8
+
9
+ type DevmodeRequest = z.infer<typeof DevmodeRequestSchema>;
10
+
11
+ const DevmodeResponseSchema = z.object({
12
+ id: z.string(),
13
+ hostname: z.string(),
14
+ });
15
+ export type DevmodeResponse = z.infer<typeof DevmodeResponseSchema>;
16
+
17
+ const DevmodeResponseAPISchema = APIResponseSchema(DevmodeResponseSchema);
18
+ type DevmodeResponseAPI = z.infer<typeof DevmodeResponseAPISchema>;
19
+
20
+ /**
21
+ * Generate an Endpoint ID and Hostname
22
+ *
23
+ * @param apiClient the api client to use
24
+ * @param projectId the project id
25
+ * @param hostname the hostname is already configured
26
+ * @returns
27
+ */
28
+ export async function generateEndpoint(
29
+ apiClient: APIClient,
30
+ projectId: string,
31
+ hostname?: string
32
+ ): Promise<DevmodeResponse> {
33
+ const resp = await apiClient.request<DevmodeResponseAPI, DevmodeRequest>(
34
+ 'POST',
35
+ `/cli/devmode/2/${projectId}`,
36
+ DevmodeResponseAPISchema,
37
+ { hostname },
38
+ DevmodeRequestSchema
39
+ );
40
+
41
+ if (!resp.success) {
42
+ throw new Error(resp.message);
43
+ }
44
+
45
+ return resp.data;
46
+ }
@@ -0,0 +1,111 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync, createReadStream, mkdirSync, rmSync } from 'node:fs';
3
+ import { tmpdir, platform } from 'node:os';
4
+ import { join, dirname } from 'node:path';
5
+ import * as tar from 'tar';
6
+ import { downloadRelease } from '@terascope/fetch-github-release';
7
+ import { spinner } from '../../tui';
8
+
9
+ const user = 'agentuity';
10
+ const repo = 'gravity';
11
+
12
+ function filterRelease(release: { prerelease: boolean }) {
13
+ // Filter out prereleases.
14
+ return release.prerelease === false;
15
+ }
16
+
17
+ function filterAsset(asset: { name: string }): boolean {
18
+ // Filter out the release matching our os and architecture
19
+ let arch: string = process.arch;
20
+ if (arch === 'x64') {
21
+ arch = 'x86_64';
22
+ }
23
+ return asset.name.includes(arch) && asset.name.includes(platform());
24
+ }
25
+
26
+ interface GravityClient {
27
+ filename: string;
28
+ version: string;
29
+ }
30
+
31
+ /**
32
+ *
33
+ * @returns full path to the downloaded file
34
+ */
35
+ export async function download(gravityDir: string): Promise<GravityClient> {
36
+ const outputdir = join(tmpdir(), randomUUID());
37
+
38
+ const res = (await spinner({
39
+ message: 'Checking Agentuity Gravity',
40
+ callback: async () => {
41
+ return downloadRelease(
42
+ user,
43
+ repo,
44
+ outputdir,
45
+ filterRelease,
46
+ filterAsset,
47
+ false,
48
+ true,
49
+ true,
50
+ ''
51
+ );
52
+ },
53
+ clearOnSuccess: true,
54
+ })) as { release: string; assetFileNames: string[] };
55
+
56
+ const versionTok = res.release.split('@');
57
+ const version = versionTok[1];
58
+ const releaseFilename = join(gravityDir, version, 'gravity');
59
+ const mustDownload = !existsSync(releaseFilename);
60
+
61
+ if (!mustDownload) {
62
+ return { filename: releaseFilename, version };
63
+ }
64
+
65
+ const downloadedFile = await spinner({
66
+ message: `Downloading Gravity ${version}`,
67
+ callback: async () => {
68
+ const res = (await downloadRelease(
69
+ user,
70
+ repo,
71
+ outputdir,
72
+ filterRelease,
73
+ filterAsset,
74
+ false,
75
+ true,
76
+ false,
77
+ ''
78
+ )) as string[];
79
+ return res[0] as string;
80
+ },
81
+ clearOnSuccess: true,
82
+ });
83
+
84
+ if (downloadedFile.endsWith('.tar.gz')) {
85
+ await spinner({
86
+ message: 'Extracting release',
87
+ callback: async () => {
88
+ return new Promise<void>((resolve, reject) => {
89
+ const input = createReadStream(downloadedFile);
90
+ const downloadDir = dirname(releaseFilename);
91
+ if (!existsSync(downloadDir)) {
92
+ mkdirSync(downloadDir, { recursive: true });
93
+ }
94
+ input.on('finish', resolve);
95
+ input.on('end', resolve);
96
+ input.on('error', reject);
97
+ input.pipe(tar.x({ C: downloadDir, chmod: true }));
98
+ });
99
+ },
100
+ clearOnSuccess: true,
101
+ });
102
+ } else {
103
+ // TODO:
104
+ }
105
+
106
+ if (existsSync(outputdir)) {
107
+ rmSync(outputdir, { recursive: true });
108
+ }
109
+
110
+ return { filename: releaseFilename, version };
111
+ }