@agentuity/cli 0.0.48 → 0.0.49

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 (256) hide show
  1. package/bin/cli.ts +26 -5
  2. package/dist/banner.d.ts +1 -1
  3. package/dist/banner.d.ts.map +1 -1
  4. package/dist/cli-logger.d.ts +27 -0
  5. package/dist/cli-logger.d.ts.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cmd/auth/index.d.ts.map +1 -1
  8. package/dist/cmd/auth/login.d.ts.map +1 -1
  9. package/dist/cmd/auth/logout.d.ts.map +1 -1
  10. package/dist/cmd/auth/signup.d.ts.map +1 -1
  11. package/dist/cmd/auth/ssh/add.d.ts.map +1 -1
  12. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -1
  13. package/dist/cmd/auth/ssh/index.d.ts +1 -2
  14. package/dist/cmd/auth/ssh/index.d.ts.map +1 -1
  15. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  16. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  17. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  18. package/dist/cmd/bundle/index.d.ts.map +1 -1
  19. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  20. package/dist/cmd/capabilities/index.d.ts +4 -0
  21. package/dist/cmd/capabilities/index.d.ts.map +1 -0
  22. package/dist/cmd/capabilities/show.d.ts +20 -0
  23. package/dist/cmd/capabilities/show.d.ts.map +1 -0
  24. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  25. package/dist/cmd/cloud/deployment/index.d.ts.map +1 -1
  26. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  27. package/dist/cmd/cloud/deployment/remove.d.ts.map +1 -1
  28. package/dist/cmd/cloud/deployment/rollback.d.ts.map +1 -1
  29. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  30. package/dist/cmd/cloud/deployment/undeploy.d.ts.map +1 -1
  31. package/dist/cmd/cloud/deployment/utils.d.ts +4 -2
  32. package/dist/cmd/cloud/deployment/utils.d.ts.map +1 -1
  33. package/dist/cmd/cloud/domain.d.ts.map +1 -1
  34. package/dist/cmd/cloud/index.d.ts.map +1 -1
  35. package/dist/cmd/cloud/resource/add.d.ts.map +1 -1
  36. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -1
  37. package/dist/cmd/cloud/resource/index.d.ts +1 -2
  38. package/dist/cmd/cloud/resource/index.d.ts.map +1 -1
  39. package/dist/cmd/cloud/resource/list.d.ts.map +1 -1
  40. package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
  41. package/dist/cmd/cloud/scp/index.d.ts +1 -2
  42. package/dist/cmd/cloud/scp/index.d.ts.map +1 -1
  43. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
  44. package/dist/cmd/cloud/ssh.d.ts.map +1 -1
  45. package/dist/cmd/dev/index.d.ts.map +1 -1
  46. package/dist/cmd/env/delete.d.ts.map +1 -1
  47. package/dist/cmd/env/get.d.ts.map +1 -1
  48. package/dist/cmd/env/import.d.ts.map +1 -1
  49. package/dist/cmd/env/index.d.ts.map +1 -1
  50. package/dist/cmd/env/list.d.ts.map +1 -1
  51. package/dist/cmd/env/pull.d.ts.map +1 -1
  52. package/dist/cmd/env/push.d.ts.map +1 -1
  53. package/dist/cmd/env/set.d.ts.map +1 -1
  54. package/dist/cmd/index.d.ts.map +1 -1
  55. package/dist/cmd/kv/create-namespace.d.ts +3 -0
  56. package/dist/cmd/kv/create-namespace.d.ts.map +1 -0
  57. package/dist/cmd/kv/delete-namespace.d.ts +3 -0
  58. package/dist/cmd/kv/delete-namespace.d.ts.map +1 -0
  59. package/dist/cmd/kv/delete.d.ts +3 -0
  60. package/dist/cmd/kv/delete.d.ts.map +1 -0
  61. package/dist/cmd/kv/get.d.ts +3 -0
  62. package/dist/cmd/kv/get.d.ts.map +1 -0
  63. package/dist/cmd/kv/index.d.ts +2 -0
  64. package/dist/cmd/kv/index.d.ts.map +1 -0
  65. package/dist/cmd/kv/keys.d.ts +3 -0
  66. package/dist/cmd/kv/keys.d.ts.map +1 -0
  67. package/dist/cmd/kv/list-namespaces.d.ts +3 -0
  68. package/dist/cmd/kv/list-namespaces.d.ts.map +1 -0
  69. package/dist/cmd/kv/repl.d.ts +3 -0
  70. package/dist/cmd/kv/repl.d.ts.map +1 -0
  71. package/dist/cmd/kv/search.d.ts +3 -0
  72. package/dist/cmd/kv/search.d.ts.map +1 -0
  73. package/dist/cmd/kv/set.d.ts +3 -0
  74. package/dist/cmd/kv/set.d.ts.map +1 -0
  75. package/dist/cmd/kv/stats.d.ts +3 -0
  76. package/dist/cmd/kv/stats.d.ts.map +1 -0
  77. package/dist/cmd/kv/util.d.ts +8 -0
  78. package/dist/cmd/kv/util.d.ts.map +1 -0
  79. package/dist/cmd/objectstore/delete-bucket.d.ts +3 -0
  80. package/dist/cmd/objectstore/delete-bucket.d.ts.map +1 -0
  81. package/dist/cmd/objectstore/delete.d.ts +3 -0
  82. package/dist/cmd/objectstore/delete.d.ts.map +1 -0
  83. package/dist/cmd/objectstore/get.d.ts +3 -0
  84. package/dist/cmd/objectstore/get.d.ts.map +1 -0
  85. package/dist/cmd/objectstore/index.d.ts +2 -0
  86. package/dist/cmd/objectstore/index.d.ts.map +1 -0
  87. package/dist/cmd/objectstore/list-buckets.d.ts +3 -0
  88. package/dist/cmd/objectstore/list-buckets.d.ts.map +1 -0
  89. package/dist/cmd/objectstore/list-keys.d.ts +3 -0
  90. package/dist/cmd/objectstore/list-keys.d.ts.map +1 -0
  91. package/dist/cmd/objectstore/put.d.ts +3 -0
  92. package/dist/cmd/objectstore/put.d.ts.map +1 -0
  93. package/dist/cmd/objectstore/repl.d.ts +3 -0
  94. package/dist/cmd/objectstore/repl.d.ts.map +1 -0
  95. package/dist/cmd/objectstore/url.d.ts +3 -0
  96. package/dist/cmd/objectstore/url.d.ts.map +1 -0
  97. package/dist/cmd/objectstore/util.d.ts +8 -0
  98. package/dist/cmd/objectstore/util.d.ts.map +1 -0
  99. package/dist/cmd/profile/create.d.ts.map +1 -1
  100. package/dist/cmd/profile/delete.d.ts.map +1 -1
  101. package/dist/cmd/profile/index.d.ts.map +1 -1
  102. package/dist/cmd/profile/list.d.ts +1 -2
  103. package/dist/cmd/profile/list.d.ts.map +1 -1
  104. package/dist/cmd/profile/show.d.ts.map +1 -1
  105. package/dist/cmd/profile/use.d.ts.map +1 -1
  106. package/dist/cmd/project/create.d.ts.map +1 -1
  107. package/dist/cmd/project/delete.d.ts.map +1 -1
  108. package/dist/cmd/project/index.d.ts.map +1 -1
  109. package/dist/cmd/project/list.d.ts.map +1 -1
  110. package/dist/cmd/project/show.d.ts.map +1 -1
  111. package/dist/cmd/project/template-flow.d.ts +1 -1
  112. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  113. package/dist/cmd/prompt/index.d.ts +4 -0
  114. package/dist/cmd/prompt/index.d.ts.map +1 -0
  115. package/dist/cmd/prompt/llm.d.ts +3 -0
  116. package/dist/cmd/prompt/llm.d.ts.map +1 -0
  117. package/dist/cmd/repl/index.d.ts +3 -0
  118. package/dist/cmd/repl/index.d.ts.map +1 -0
  119. package/dist/cmd/schema/index.d.ts +4 -0
  120. package/dist/cmd/schema/index.d.ts.map +1 -0
  121. package/dist/cmd/schema/show.d.ts +3 -0
  122. package/dist/cmd/schema/show.d.ts.map +1 -0
  123. package/dist/cmd/secret/delete.d.ts.map +1 -1
  124. package/dist/cmd/secret/get.d.ts.map +1 -1
  125. package/dist/cmd/secret/import.d.ts.map +1 -1
  126. package/dist/cmd/secret/index.d.ts.map +1 -1
  127. package/dist/cmd/secret/list.d.ts.map +1 -1
  128. package/dist/cmd/secret/pull.d.ts.map +1 -1
  129. package/dist/cmd/secret/push.d.ts.map +1 -1
  130. package/dist/cmd/secret/set.d.ts.map +1 -1
  131. package/dist/cmd/version/index.d.ts.map +1 -1
  132. package/dist/config.d.ts +1 -1
  133. package/dist/config.d.ts.map +1 -1
  134. package/dist/errors.d.ts +83 -0
  135. package/dist/errors.d.ts.map +1 -0
  136. package/dist/explain.d.ts +47 -0
  137. package/dist/explain.d.ts.map +1 -0
  138. package/dist/index.d.ts +6 -0
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/json.d.ts +3 -0
  141. package/dist/json.d.ts.map +1 -0
  142. package/dist/output.d.ts +136 -0
  143. package/dist/output.d.ts.map +1 -0
  144. package/dist/repl.d.ts +124 -0
  145. package/dist/repl.d.ts.map +1 -0
  146. package/dist/schema-generator.d.ts +67 -0
  147. package/dist/schema-generator.d.ts.map +1 -0
  148. package/dist/tui.d.ts +11 -1
  149. package/dist/tui.d.ts.map +1 -1
  150. package/dist/types.d.ts +65 -6
  151. package/dist/types.d.ts.map +1 -1
  152. package/package.json +9 -4
  153. package/src/banner.ts +7 -7
  154. package/src/cli-logger.ts +80 -0
  155. package/src/cli.ts +186 -54
  156. package/src/cmd/auth/index.ts +1 -0
  157. package/src/cmd/auth/login.ts +7 -2
  158. package/src/cmd/auth/logout.ts +4 -0
  159. package/src/cmd/auth/signup.ts +7 -2
  160. package/src/cmd/auth/ssh/add.ts +20 -3
  161. package/src/cmd/auth/ssh/delete.ts +57 -4
  162. package/src/cmd/auth/ssh/index.ts +4 -3
  163. package/src/cmd/auth/ssh/list.ts +29 -12
  164. package/src/cmd/auth/whoami.ts +32 -21
  165. package/src/cmd/bundle/ast.ts +27 -5
  166. package/src/cmd/bundle/index.ts +20 -0
  167. package/src/cmd/bundle/plugin.ts +36 -12
  168. package/src/cmd/capabilities/index.ts +12 -0
  169. package/src/cmd/capabilities/show.ts +256 -0
  170. package/src/cmd/cloud/deploy.ts +54 -0
  171. package/src/cmd/cloud/deployment/index.ts +1 -0
  172. package/src/cmd/cloud/deployment/list.ts +50 -3
  173. package/src/cmd/cloud/deployment/remove.ts +26 -2
  174. package/src/cmd/cloud/deployment/rollback.ts +35 -4
  175. package/src/cmd/cloud/deployment/show.ts +37 -2
  176. package/src/cmd/cloud/deployment/undeploy.ts +12 -1
  177. package/src/cmd/cloud/deployment/utils.ts +5 -2
  178. package/src/cmd/cloud/domain.ts +3 -2
  179. package/src/cmd/cloud/index.ts +8 -1
  180. package/src/cmd/cloud/resource/add.ts +19 -0
  181. package/src/cmd/cloud/resource/delete.ts +24 -3
  182. package/src/cmd/cloud/resource/index.ts +4 -3
  183. package/src/cmd/cloud/resource/list.ts +36 -10
  184. package/src/cmd/cloud/scp/download.ts +27 -1
  185. package/src/cmd/cloud/scp/index.ts +4 -3
  186. package/src/cmd/cloud/scp/upload.ts +27 -1
  187. package/src/cmd/cloud/ssh.ts +12 -0
  188. package/src/cmd/dev/index.ts +11 -7
  189. package/src/cmd/dev/templates.ts +1 -1
  190. package/src/cmd/env/delete.ts +17 -0
  191. package/src/cmd/env/get.ts +17 -1
  192. package/src/cmd/env/import.ts +47 -3
  193. package/src/cmd/env/index.ts +1 -0
  194. package/src/cmd/env/list.ts +13 -1
  195. package/src/cmd/env/pull.ts +20 -0
  196. package/src/cmd/env/push.ts +33 -1
  197. package/src/cmd/env/set.ts +25 -1
  198. package/src/cmd/index.ts +9 -2
  199. package/src/cmd/kv/create-namespace.ts +45 -0
  200. package/src/cmd/kv/delete-namespace.ts +73 -0
  201. package/src/cmd/kv/delete.ts +51 -0
  202. package/src/cmd/kv/get.ts +65 -0
  203. package/src/cmd/kv/index.ts +31 -0
  204. package/src/cmd/kv/keys.ts +57 -0
  205. package/src/cmd/kv/list-namespaces.ts +43 -0
  206. package/src/cmd/kv/repl.ts +284 -0
  207. package/src/cmd/kv/search.ts +80 -0
  208. package/src/cmd/kv/set.ts +63 -0
  209. package/src/cmd/kv/stats.ts +96 -0
  210. package/src/cmd/kv/util.ts +32 -0
  211. package/src/cmd/objectstore/delete-bucket.ts +72 -0
  212. package/src/cmd/objectstore/delete.ts +59 -0
  213. package/src/cmd/objectstore/get.ts +64 -0
  214. package/src/cmd/objectstore/index.ts +27 -0
  215. package/src/cmd/objectstore/list-buckets.ts +45 -0
  216. package/src/cmd/objectstore/list-keys.ts +60 -0
  217. package/src/cmd/objectstore/put.ts +62 -0
  218. package/src/cmd/objectstore/repl.ts +235 -0
  219. package/src/cmd/objectstore/url.ts +59 -0
  220. package/src/cmd/objectstore/util.ts +28 -0
  221. package/src/cmd/profile/create.ts +28 -2
  222. package/src/cmd/profile/delete.ts +17 -2
  223. package/src/cmd/profile/index.ts +1 -0
  224. package/src/cmd/profile/list.ts +7 -3
  225. package/src/cmd/profile/show.ts +20 -5
  226. package/src/cmd/profile/use.ts +8 -0
  227. package/src/cmd/project/create.ts +31 -0
  228. package/src/cmd/project/delete.ts +24 -2
  229. package/src/cmd/project/index.ts +1 -0
  230. package/src/cmd/project/list.ts +23 -9
  231. package/src/cmd/project/show.ts +27 -8
  232. package/src/cmd/project/template-flow.ts +10 -6
  233. package/src/cmd/prompt/index.ts +12 -0
  234. package/src/cmd/prompt/llm.ts +368 -0
  235. package/src/cmd/repl/index.ts +477 -0
  236. package/src/cmd/schema/index.ts +12 -0
  237. package/src/cmd/schema/show.ts +27 -0
  238. package/src/cmd/secret/delete.ts +17 -0
  239. package/src/cmd/secret/get.ts +20 -1
  240. package/src/cmd/secret/import.ts +45 -2
  241. package/src/cmd/secret/index.ts +1 -0
  242. package/src/cmd/secret/list.ts +10 -1
  243. package/src/cmd/secret/pull.ts +20 -0
  244. package/src/cmd/secret/push.ts +33 -1
  245. package/src/cmd/secret/set.ts +20 -0
  246. package/src/cmd/version/index.ts +15 -2
  247. package/src/config.ts +17 -4
  248. package/src/errors.ts +222 -0
  249. package/src/explain.ts +126 -0
  250. package/src/index.ts +51 -0
  251. package/src/json.ts +28 -0
  252. package/src/output.ts +307 -0
  253. package/src/repl.ts +1517 -0
  254. package/src/schema-generator.ts +389 -0
  255. package/src/tui.ts +84 -13
  256. package/src/types.ts +62 -12
@@ -0,0 +1,235 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../types';
3
+ import { createRepl, type ReplCommand } from '../../repl';
4
+ import { showBanner } from '../../banner';
5
+ import * as tui from '../../tui';
6
+ import { isPossiblyJSON, tryParseJSON } from '../../json';
7
+ import { createStorageAdapter } from './util';
8
+ import { getCommand } from '../../command-prefix';
9
+
10
+ export const replSubcommand = createCommand({
11
+ name: 'repl',
12
+ description: 'Start an interactive repl for working with object storage',
13
+ tags: ['slow', 'requires-auth'],
14
+ idempotent: false,
15
+ requires: { auth: true, project: true },
16
+ examples: [`${getCommand('objectstore repl')} - Start interactive object storage session`],
17
+
18
+ async handler(ctx) {
19
+ showBanner(undefined, true);
20
+ tui.info('Managing object store for project');
21
+ tui.newline();
22
+ console.log(tui.bold('Org:'.padEnd(10, ' ')), ' ', tui.muted(ctx.project.orgId));
23
+ console.log(tui.bold('Project:'.padEnd(10, ' ')), ' ', tui.muted(ctx.project.projectId));
24
+ tui.newline();
25
+
26
+ const storage = await createStorageAdapter(ctx);
27
+
28
+ const commands: ReplCommand[] = [
29
+ {
30
+ name: 'put',
31
+ description: 'Put an object into a bucket',
32
+ schema: {
33
+ args: z.tuple([z.string().min(1), z.string().min(1), z.string().min(1)]),
34
+ argNames: ['bucket', 'key', 'value'],
35
+ },
36
+ handler: async (ctx) => {
37
+ ctx.setProgress('saving');
38
+ const started = Date.now();
39
+ const contentType = isPossiblyJSON(ctx.parsed.args[2]!)
40
+ ? 'application/json'
41
+ : 'text/plain';
42
+ const data = new TextEncoder().encode(ctx.parsed.args[2]!);
43
+ await storage.put(ctx.parsed.args[0]!, ctx.parsed.args[1]!, data, {
44
+ contentType,
45
+ });
46
+ ctx.success(`saved in ${(Date.now() - started).toFixed(1)}ms (${contentType})`);
47
+ },
48
+ },
49
+ {
50
+ name: 'get',
51
+ description: 'Get an object from a bucket',
52
+ schema: {
53
+ args: z.tuple([z.string().min(1), z.string().min(1)]),
54
+ argNames: ['bucket', 'key'],
55
+ },
56
+ handler: async (ctx) => {
57
+ ctx.setProgress('fetching');
58
+ const started = Date.now();
59
+ const res = await storage.get(ctx.parsed.args[0]!, ctx.parsed.args[1]!);
60
+ if (res.exists) {
61
+ if (res.data) {
62
+ if (res.contentType?.includes('json')) {
63
+ const val = tryParseJSON(new TextDecoder().decode(res.data));
64
+ ctx.json(val);
65
+ } else if (res.contentType?.includes('text')) {
66
+ ctx.write(new TextDecoder().decode(res.data));
67
+ } else {
68
+ ctx.info(`Read ${res.data.byteLength} bytes (${res.contentType})`);
69
+ }
70
+ ctx.success(
71
+ `retrieved in ${(Date.now() - started).toFixed(1)}ms (${res.contentType})`
72
+ );
73
+ } else {
74
+ ctx.warning(
75
+ `${ctx.parsed.args[1]!} returned empty data for bucket ${ctx.parsed.args[0]!}`
76
+ );
77
+ }
78
+ } else {
79
+ ctx.warning(
80
+ `${ctx.parsed.args[1]!} does not exist in bucket ${ctx.parsed.args[0]!}`
81
+ );
82
+ }
83
+ },
84
+ },
85
+ {
86
+ name: 'delete',
87
+ aliases: ['rm', 'remove', 'del'],
88
+ description: 'Delete an object from a bucket',
89
+ schema: {
90
+ args: z.tuple([z.string().min(1), z.string().min(1)]),
91
+ argNames: ['bucket', 'key'],
92
+ },
93
+ handler: async (ctx) => {
94
+ ctx.setProgress('deleting');
95
+ const started = Date.now();
96
+ const deleted = await storage.delete(ctx.parsed.args[0]!, ctx.parsed.args[1]!);
97
+ if (deleted) {
98
+ ctx.success(`deleted in ${(Date.now() - started).toFixed(1)}ms`);
99
+ } else {
100
+ ctx.warning(
101
+ `${ctx.parsed.args[1]!} did not exist in bucket ${ctx.parsed.args[0]!}`
102
+ );
103
+ }
104
+ },
105
+ },
106
+ {
107
+ name: 'url',
108
+ aliases: ['publicurl', 'presigned'],
109
+ description: 'Create a public URL for an object',
110
+ schema: {
111
+ args: z.tuple([
112
+ z.string().min(1),
113
+ z.string().min(1),
114
+ z.coerce.number().min(60).optional(),
115
+ ]),
116
+ argNames: ['bucket', 'key', 'expires'],
117
+ },
118
+ handler: async (ctx) => {
119
+ ctx.setProgress('creating url');
120
+ const started = Date.now();
121
+ const expires =
122
+ ctx.parsed.args.length > 2 ? parseInt(ctx.parsed.args[2]!) : undefined;
123
+ const url = await storage.createPublicURL(
124
+ ctx.parsed.args[0]!,
125
+ ctx.parsed.args[1]!,
126
+ expires ? { expiresDuration: expires } : undefined
127
+ );
128
+ ctx.write(url);
129
+ ctx.success(`created in ${(Date.now() - started).toFixed(1)}ms`);
130
+ },
131
+ },
132
+ {
133
+ name: 'list-buckets',
134
+ aliases: ['buckets', 'lb'],
135
+ description: 'List all buckets',
136
+ handler: async (ctx) => {
137
+ ctx.setProgress('listing buckets');
138
+ const started = Date.now();
139
+ const buckets = await storage.listBuckets();
140
+ if (buckets.length === 0) {
141
+ ctx.info('No buckets found');
142
+ } else {
143
+ ctx.info(`Found ${buckets.length} bucket(s):`);
144
+ for (const bucket of buckets) {
145
+ const sizeMB = (bucket.total_bytes / (1024 * 1024)).toFixed(2);
146
+ ctx.write(
147
+ ` ${tui.bold(bucket.name)}: ${bucket.object_count} objects, ${sizeMB} MB`
148
+ );
149
+ }
150
+ }
151
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
152
+ },
153
+ },
154
+ {
155
+ name: 'list-keys',
156
+ aliases: ['keys', 'ls'],
157
+ description: 'List all keys in a bucket',
158
+ schema: {
159
+ args: z.tuple([z.string().min(1)]),
160
+ argNames: ['bucket'],
161
+ },
162
+ handler: async (ctx) => {
163
+ ctx.setProgress('listing keys');
164
+ const started = Date.now();
165
+ const objects = await storage.listKeys(ctx.parsed.args[0]!);
166
+ if (objects.length === 0) {
167
+ ctx.info(`No objects found in bucket ${tui.bold(ctx.parsed.args[0]!)}`);
168
+ } else {
169
+ ctx.info(
170
+ `Found ${objects.length} object(s) in ${tui.bold(ctx.parsed.args[0]!)}:`
171
+ );
172
+ for (const obj of objects) {
173
+ const sizeMB = (obj.size / (1024 * 1024)).toFixed(2);
174
+ const date = new Date(obj.updated_at).toLocaleString();
175
+ ctx.write(` ${tui.bold(obj.key)}: ${sizeMB} MB, updated ${date}`);
176
+ }
177
+ }
178
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
179
+ },
180
+ },
181
+ {
182
+ name: 'delete-bucket',
183
+ aliases: ['rmbucket', 'delbucket'],
184
+ description: 'Delete a bucket and all its contents',
185
+ schema: {
186
+ args: z.tuple([z.string().min(1)]),
187
+ argNames: ['bucket'],
188
+ },
189
+ handler: async (ctx) => {
190
+ ctx.warning(
191
+ `This will delete bucket ${tui.bold(ctx.parsed.args[0]!)} and ALL its contents.`
192
+ );
193
+ const confirm = await new Promise<boolean>((resolve) => {
194
+ process.stdout.write('Are you sure? (yes/no): ');
195
+ process.stdin.once('data', (data) => {
196
+ const answer = data.toString().trim().toLowerCase();
197
+ resolve(answer === 'yes' || answer === 'y');
198
+ });
199
+ });
200
+ if (!confirm) {
201
+ ctx.info('Cancelled');
202
+ return;
203
+ }
204
+ ctx.setProgress('deleting bucket');
205
+ const started = Date.now();
206
+ const deleted = await storage.deleteBucket(ctx.parsed.args[0]!);
207
+ if (deleted) {
208
+ ctx.success(`deleted in ${(Date.now() - started).toFixed(1)}ms`);
209
+ } else {
210
+ ctx.warning(`Bucket ${tui.bold(ctx.parsed.args[0]!)} not found`);
211
+ }
212
+ },
213
+ },
214
+ {
215
+ name: 'exit',
216
+ aliases: ['quit'],
217
+ description: 'Exit the repl',
218
+ handler: async (ctx) => {
219
+ return ctx.exit();
220
+ },
221
+ },
222
+ ];
223
+
224
+ // Start the REPL
225
+ await createRepl({
226
+ name: 'objectstore',
227
+ prompt: '> ',
228
+ welcome: tui.muted('Type "help" or / for available commands.'),
229
+ exitMessage: 'Goodbye!',
230
+ commands,
231
+ });
232
+ },
233
+ });
234
+
235
+ export default replSubcommand;
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { createStorageAdapter } from './util';
5
+ import { getCommand } from '../../command-prefix';
6
+
7
+ const ObjectStoreURLResponseSchema = z.object({
8
+ success: z.boolean().describe('Whether the operation succeeded'),
9
+ bucket: z.string().describe('Bucket name'),
10
+ key: z.string().describe('Object key'),
11
+ url: z.string().describe('Public or presigned URL'),
12
+ expires: z.number().optional().describe('URL expiration time in seconds'),
13
+ durationMs: z.number().describe('Operation duration in milliseconds'),
14
+ });
15
+
16
+ export const urlSubcommand = createCommand({
17
+ name: 'url',
18
+ aliases: ['publicurl', 'presigned'],
19
+ description: 'Create a public URL for an object',
20
+ tags: ['read-only', 'fast', 'requires-auth'],
21
+ requires: { auth: true, project: true },
22
+ idempotent: true,
23
+ examples: [
24
+ `${getCommand('objectstore url uploads images/logo.png')} - Get public URL for logo`,
25
+ `${getCommand('objectstore url assets data/export.json --expires 3600')} - Get 1h temporary URL`,
26
+ `${getCommand('objectstore presigned backups db-2024.sql --expires 300')} - Get 5m presigned URL`,
27
+ ],
28
+ schema: {
29
+ args: z.object({
30
+ bucket: z.string().min(1).describe('the bucket name'),
31
+ key: z.string().min(1).describe('the key name'),
32
+ expires: z.coerce.number().min(60).optional().describe('the expiration in seconds'),
33
+ }),
34
+ response: ObjectStoreURLResponseSchema,
35
+ },
36
+
37
+ async handler(ctx) {
38
+ const { args } = ctx;
39
+ const started = Date.now();
40
+ const storage = await createStorageAdapter(ctx);
41
+ const url = await storage.createPublicURL(args.bucket, args.key, {
42
+ expiresDuration: args.expires,
43
+ });
44
+ const durationMs = Date.now() - started;
45
+ console.log(url);
46
+ tui.success(`created in ${durationMs.toFixed(1)}ms`);
47
+
48
+ return {
49
+ success: true,
50
+ bucket: args.bucket,
51
+ key: args.key,
52
+ url,
53
+ expires: args.expires,
54
+ durationMs,
55
+ };
56
+ },
57
+ });
58
+
59
+ export default urlSubcommand;
@@ -0,0 +1,28 @@
1
+ import { Logger, ObjectStorageService } from '@agentuity/core';
2
+ import { createServerFetchAdapter } from '@agentuity/server';
3
+ import { loadProjectSDKKey } from '../../config';
4
+ import type { Config } from '../../types';
5
+ import * as tui from '../../tui';
6
+
7
+ export async function createStorageAdapter(ctx: {
8
+ logger: Logger;
9
+ projectDir: string;
10
+ config: Config | null;
11
+ }) {
12
+ const sdkKey = await loadProjectSDKKey(ctx.projectDir);
13
+ if (!sdkKey) {
14
+ tui.fatal(`Couldn't find the AGENTUITY_SDK_KEY in ${ctx.projectDir} .env file`);
15
+ }
16
+
17
+ const adapter = createServerFetchAdapter(
18
+ {
19
+ headers: {
20
+ Authorization: `Bearer ${sdkKey}`,
21
+ },
22
+ },
23
+ ctx.logger
24
+ );
25
+
26
+ const baseUrl = ctx.config?.overrides?.object_url ?? 'https://catalyst.agentuity.cloud';
27
+ return new ObjectStorageService(baseUrl, adapter);
28
+ }
@@ -12,13 +12,22 @@ import {
12
12
  saveProfile,
13
13
  } from '../../config';
14
14
  import * as tui from '../../tui';
15
+ import { getCommand } from '../../command-prefix';
16
+ import { ErrorCode } from '../../errors';
15
17
 
16
18
  const PROFILE_NAME_REGEX = /^[\w_-]{3,}$/;
17
19
 
18
20
  export const createCommand = createSubcommand({
19
21
  name: 'create',
20
22
  description: 'Create a new configuration profile',
23
+ tags: ['mutating', 'creates-resource', 'fast'],
21
24
  aliases: ['new'],
25
+ idempotent: false,
26
+ examples: [
27
+ getCommand('profile create production'),
28
+ getCommand('profile create staging --switch'),
29
+ getCommand('profile create development'),
30
+ ],
22
31
  schema: {
23
32
  args: z
24
33
  .object({
@@ -32,6 +41,11 @@ export const createCommand = createSubcommand({
32
41
  options: z.object({
33
42
  switch: z.boolean().optional().describe('switch to this profile (if more than one)'),
34
43
  }),
44
+ response: z.object({
45
+ success: z.boolean().describe('Whether creation succeeded'),
46
+ name: z.string().describe('Profile name'),
47
+ path: z.string().describe('Profile file path'),
48
+ }),
35
49
  },
36
50
 
37
51
  async handler(ctx) {
@@ -42,7 +56,10 @@ export const createCommand = createSubcommand({
42
56
  const existing = profiles.find((p) => p.name === name);
43
57
 
44
58
  if (existing) {
45
- return logger.fatal(`Profile "${name}" already exists at ${existing.filename}`);
59
+ return logger.fatal(
60
+ `Profile "${name}" already exists at ${existing.filename}`,
61
+ ErrorCode.RESOURCE_ALREADY_EXISTS
62
+ );
46
63
  }
47
64
 
48
65
  await ensureConfigDir();
@@ -77,10 +94,19 @@ export const createCommand = createSubcommand({
77
94
  }
78
95
 
79
96
  tui.success(`Created profile "${name}" at ${filename}`);
97
+
98
+ return {
99
+ success: true,
100
+ name,
101
+ path: filename,
102
+ };
80
103
  } catch (error) {
81
104
  const message = error instanceof Error ? error.message : String(error);
82
105
  const stack = error instanceof Error ? error.stack : undefined;
83
- logger.fatal(`Failed to create profile: ${message}${stack ? `\n${stack}` : ''}`);
106
+ logger.fatal(
107
+ `Failed to create profile: ${message}${stack ? `\n${stack}` : ''}`,
108
+ ErrorCode.INTERNAL_ERROR
109
+ );
84
110
  }
85
111
  },
86
112
  });
@@ -3,11 +3,20 @@ import { z } from 'zod';
3
3
  import { fetchProfiles } from '../../config';
4
4
  import { unlink } from 'node:fs/promises';
5
5
  import * as tui from '../../tui';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
6
8
 
7
9
  export const deleteCommand = createSubcommand({
8
10
  name: 'delete',
9
11
  description: 'Delete a configuration profile',
12
+ tags: ['destructive', 'deletes-resource', 'fast'],
13
+ idempotent: false,
10
14
  aliases: ['remove', 'rm', 'del'],
15
+ examples: [
16
+ getCommand('profile delete staging'),
17
+ getCommand('profile delete old-dev --confirm'),
18
+ getCommand('profile delete'),
19
+ ],
11
20
  schema: {
12
21
  args: z.object({
13
22
  name: z.string().optional().describe('The name of the profile to delete'),
@@ -15,6 +24,10 @@ export const deleteCommand = createSubcommand({
15
24
  options: z.object({
16
25
  confirm: z.boolean().optional().describe('Skip confirmation prompt'),
17
26
  }),
27
+ response: z.object({
28
+ success: z.boolean().describe('Whether deletion succeeded'),
29
+ name: z.string().describe('Deleted profile name'),
30
+ }),
18
31
  },
19
32
 
20
33
  async handler(ctx) {
@@ -30,7 +43,7 @@ export const deleteCommand = createSubcommand({
30
43
  const profile = profiles.find((p) => p.name === name);
31
44
 
32
45
  if (!profile) {
33
- return logger.fatal(`Profile "${name}" not found`);
46
+ return logger.fatal(`Profile "${name}" not found`, ErrorCode.RESOURCE_NOT_FOUND);
34
47
  }
35
48
 
36
49
  // Ask for confirmation unless --confirm flag is passed
@@ -38,7 +51,7 @@ export const deleteCommand = createSubcommand({
38
51
  const confirmed = await tui.confirm(`Delete profile "${name}"?`, false);
39
52
  if (!confirmed) {
40
53
  logger.info('Cancelled');
41
- return;
54
+ return { success: false, name };
42
55
  }
43
56
  }
44
57
 
@@ -50,6 +63,8 @@ export const deleteCommand = createSubcommand({
50
63
  'The active profile was deleted. Use "profile use <name>" to select a new default.'
51
64
  );
52
65
  }
66
+
67
+ return { success: true, name };
53
68
  } catch (error) {
54
69
  logger.fatal(`Failed to delete profile: ${error}`);
55
70
  }
@@ -8,5 +8,6 @@ import { deleteCommand } from './delete';
8
8
  export const command = createCommand({
9
9
  name: 'profile',
10
10
  description: 'Manage configuration profiles',
11
+ tags: ['read-only', 'fast'],
11
12
  subcommands: [createProfileCmd, useCommand, listCommand, showCommand, deleteCommand],
12
13
  });
@@ -1,12 +1,16 @@
1
- import type { SubcommandDefinition } from '../../types';
1
+ import { createSubcommand } from '../../types';
2
2
  import { fetchProfiles } from '../../config';
3
3
  import { basename, dirname } from 'node:path';
4
4
  import * as tui from '../../tui';
5
+ import { getCommand } from '../../command-prefix';
5
6
 
6
- export const listCommand: SubcommandDefinition = {
7
+ export const listCommand = createSubcommand({
7
8
  name: 'list',
8
9
  description: 'List all available profiles',
10
+ tags: ['read-only', 'fast'],
11
+ idempotent: true,
9
12
  aliases: ['ls'],
13
+ examples: [getCommand('profile list'), getCommand('profile ls')],
10
14
 
11
15
  async handler() {
12
16
  const profiles = await fetchProfiles();
@@ -24,4 +28,4 @@ export const listCommand: SubcommandDefinition = {
24
28
  console.log(`${marker} ${name} ${tui.muted(path)}`);
25
29
  }
26
30
  },
27
- };
31
+ });
@@ -1,13 +1,23 @@
1
- import { createSubcommand } from '../../types';
1
+ import { createSubcommand, ConfigSchema } from '../../types';
2
2
  import { z } from 'zod';
3
3
  import { fetchProfiles, loadConfig } from '../../config';
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import * as tui from '../../tui';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
8
+
9
+ const ProfileShowResponseSchema = ConfigSchema;
6
10
 
7
11
  export const showCommand = createSubcommand({
8
12
  name: 'show',
9
13
  description: 'Show the configuration of a profile',
14
+ tags: ['read-only', 'fast'],
10
15
  aliases: ['current'],
16
+ examples: [
17
+ getCommand('profile show'),
18
+ getCommand('profile show production'),
19
+ getCommand('profile show staging --json'),
20
+ ],
11
21
  schema: {
12
22
  options: z.object({
13
23
  json: z.boolean().optional().describe('Show the JSON config'),
@@ -17,7 +27,9 @@ export const showCommand = createSubcommand({
17
27
  name: z.string().optional().describe('Profile name to show (optional)'),
18
28
  })
19
29
  .describe('Profile show arguments'),
30
+ response: ProfileShowResponseSchema,
20
31
  },
32
+ idempotent: true,
21
33
 
22
34
  async handler(ctx) {
23
35
  const { logger, args, opts } = ctx;
@@ -35,7 +47,7 @@ export const showCommand = createSubcommand({
35
47
  const profile = profiles.find((p) => p.name === name);
36
48
 
37
49
  if (!profile) {
38
- return logger.fatal(`Profile "${name}" not found`);
50
+ return logger.fatal(`Profile "${name}" not found`, ErrorCode.RESOURCE_NOT_FOUND);
39
51
  }
40
52
 
41
53
  const profilePath = profile.filename;
@@ -43,14 +55,17 @@ export const showCommand = createSubcommand({
43
55
 
44
56
  tui.info(`Profile: ${profilePath}`);
45
57
 
58
+ const content = await loadConfig(current ? undefined : profilePath);
59
+
46
60
  if (opts?.json) {
47
- const content = await loadConfig(current ? undefined : profilePath);
48
61
  console.log(JSON.stringify(content, null, 2));
49
62
  } else {
50
63
  tui.newline();
51
- const content = await readFile(profilePath, 'utf-8');
52
- console.log(content);
64
+ const textContent = await readFile(profilePath, 'utf-8');
65
+ console.log(textContent);
53
66
  }
67
+
68
+ return content;
54
69
  } catch (error) {
55
70
  if (error instanceof Error) {
56
71
  logger.fatal(`Failed to show profile: ${error.message}`);
@@ -2,11 +2,19 @@ import { createSubcommand } from '../../types';
2
2
  import { z } from 'zod';
3
3
  import { fetchProfiles, saveProfile } from '../../config';
4
4
  import * as tui from '../../tui';
5
+ import { getCommand } from '../../command-prefix';
5
6
 
6
7
  export const useCommand = createSubcommand({
7
8
  name: 'use',
8
9
  description: 'Switch to a different configuration profile',
10
+ tags: ['mutating', 'updates-resource', 'fast'],
9
11
  aliases: ['switch'],
12
+ idempotent: true,
13
+ examples: [
14
+ getCommand('profile use production'),
15
+ getCommand('profile switch staging'),
16
+ getCommand('profile use'),
17
+ ],
10
18
  schema: {
11
19
  args: z.object({
12
20
  name: z.string().optional().describe('The name of the profile to use'),
@@ -1,15 +1,35 @@
1
1
  import { createSubcommand } from '../../types';
2
2
  import { z } from 'zod';
3
3
  import { runCreateFlow } from './template-flow';
4
+ import { getCommand } from '../../command-prefix';
5
+
6
+ const ProjectCreateResponseSchema = z.object({
7
+ success: z.boolean().describe('Whether the operation succeeded'),
8
+ name: z.string().describe('Project name'),
9
+ path: z.string().describe('Project directory path'),
10
+ projectId: z.string().optional().describe('Project ID if registered'),
11
+ template: z.string().describe('Template used'),
12
+ installed: z.boolean().describe('Whether dependencies were installed'),
13
+ built: z.boolean().describe('Whether the project was built'),
14
+ });
4
15
 
5
16
  export const createProjectSubcommand = createSubcommand({
6
17
  name: 'create',
7
18
  description: 'Create a new project',
19
+ tags: ['mutating', 'creates-resource', 'slow', 'api-intensive', 'requires-auth'],
8
20
  aliases: ['new'],
9
21
  banner: true,
10
22
  toplevel: true,
23
+ idempotent: false,
11
24
  optional: { auth: true, org: true, region: true },
12
25
  requires: { apiClient: true },
26
+ examples: [
27
+ getCommand('project create'),
28
+ getCommand('project create --name my-ai-agent'),
29
+ getCommand('project create --name customer-service-bot --dir ~/projects/agent'),
30
+ getCommand('project create --template basic --no-install'),
31
+ getCommand('project new --no-register'),
32
+ ],
13
33
  schema: {
14
34
  options: z.object({
15
35
  name: z.string().optional().describe('Project name'),
@@ -40,6 +60,7 @@ export const createProjectSubcommand = createSubcommand({
40
60
  .optional()
41
61
  .describe('Register the project, if authenticated (use --no-register to skip)'),
42
62
  }),
63
+ response: ProjectCreateResponseSchema,
43
64
  },
44
65
 
45
66
  async handler(ctx) {
@@ -61,5 +82,15 @@ export const createProjectSubcommand = createSubcommand({
61
82
  orgId,
62
83
  region,
63
84
  });
85
+
86
+ // Return best-effort response (runCreateFlow doesn't return values)
87
+ return {
88
+ success: true,
89
+ name: opts.name || 'project',
90
+ path: opts.dir || process.cwd(),
91
+ template: opts.template || 'default',
92
+ installed: opts.install !== false,
93
+ built: opts.build !== false,
94
+ };
64
95
  },
65
96
  });
@@ -3,12 +3,23 @@ import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectDelete, projectList } from '@agentuity/server';
5
5
  import enquirer from 'enquirer';
6
+ import { getCommand } from '../../command-prefix';
6
7
 
7
8
  export const deleteSubcommand = createSubcommand({
8
9
  name: 'delete',
9
10
  description: 'Delete a project',
11
+ tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
10
12
  aliases: ['rm', 'del'],
11
13
  requires: { auth: true, apiClient: true },
14
+ idempotent: false,
15
+ examples: [
16
+ getCommand('project delete'),
17
+ getCommand('project delete proj_abc123def456'),
18
+ getCommand('project delete proj_abc123def456 --confirm'),
19
+ getCommand('project rm proj_abc123def456'),
20
+ getCommand('--explain project delete proj_abc123def456'),
21
+ getCommand('--dry-run project delete proj_abc123def456'),
22
+ ],
12
23
  schema: {
13
24
  args: z.object({
14
25
  id: z.string().optional().describe('the project id'),
@@ -16,6 +27,11 @@ export const deleteSubcommand = createSubcommand({
16
27
  options: z.object({
17
28
  confirm: z.boolean().optional().describe('Skip confirmation prompts'),
18
29
  }),
30
+ response: z.object({
31
+ success: z.boolean().describe('Whether the deletion succeeded'),
32
+ projectIds: z.array(z.string()).describe('Deleted project IDs'),
33
+ count: z.number().describe('Number of projects deleted'),
34
+ }),
19
35
  },
20
36
 
21
37
  async handler(ctx) {
@@ -67,7 +83,7 @@ export const deleteSubcommand = createSubcommand({
67
83
 
68
84
  if (projectIds.length === 0) {
69
85
  tui.info('No projects selected for deletion');
70
- return;
86
+ return { success: false, projectIds: [], count: 0 };
71
87
  }
72
88
 
73
89
  const skipConfirm = opts?.confirm === true;
@@ -90,7 +106,7 @@ export const deleteSubcommand = createSubcommand({
90
106
 
91
107
  if (!confirm.confirm) {
92
108
  tui.info('Deletion cancelled');
93
- return;
109
+ return { success: false, projectIds: [], count: 0 };
94
110
  }
95
111
  }
96
112
 
@@ -107,5 +123,11 @@ export const deleteSubcommand = createSubcommand({
107
123
  } else {
108
124
  tui.error('Failed to delete projects');
109
125
  }
126
+
127
+ return {
128
+ success: deleted.length > 0,
129
+ projectIds: deleted,
130
+ count: deleted.length,
131
+ };
110
132
  },
111
133
  });