@agentuity/cli 0.0.48 → 0.0.50

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 (282) 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/agents/index.d.ts +2 -0
  8. package/dist/cmd/agents/index.d.ts.map +1 -0
  9. package/dist/cmd/auth/index.d.ts.map +1 -1
  10. package/dist/cmd/auth/login.d.ts.map +1 -1
  11. package/dist/cmd/auth/logout.d.ts.map +1 -1
  12. package/dist/cmd/auth/signup.d.ts.map +1 -1
  13. package/dist/cmd/auth/ssh/add.d.ts.map +1 -1
  14. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -1
  15. package/dist/cmd/auth/ssh/index.d.ts +1 -2
  16. package/dist/cmd/auth/ssh/index.d.ts.map +1 -1
  17. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  18. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  19. package/dist/cmd/bundle/ast.d.ts +3 -1
  20. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  21. package/dist/cmd/bundle/index.d.ts.map +1 -1
  22. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  23. package/dist/cmd/capabilities/index.d.ts +4 -0
  24. package/dist/cmd/capabilities/index.d.ts.map +1 -0
  25. package/dist/cmd/capabilities/show.d.ts +20 -0
  26. package/dist/cmd/capabilities/show.d.ts.map +1 -0
  27. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  28. package/dist/cmd/cloud/deployment/index.d.ts.map +1 -1
  29. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  30. package/dist/cmd/cloud/deployment/remove.d.ts.map +1 -1
  31. package/dist/cmd/cloud/deployment/rollback.d.ts.map +1 -1
  32. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  33. package/dist/cmd/cloud/deployment/undeploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deployment/utils.d.ts +4 -2
  35. package/dist/cmd/cloud/deployment/utils.d.ts.map +1 -1
  36. package/dist/cmd/cloud/domain.d.ts.map +1 -1
  37. package/dist/cmd/cloud/index.d.ts.map +1 -1
  38. package/dist/cmd/cloud/resource/add.d.ts.map +1 -1
  39. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -1
  40. package/dist/cmd/cloud/resource/index.d.ts +1 -2
  41. package/dist/cmd/cloud/resource/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/resource/list.d.ts.map +1 -1
  43. package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
  44. package/dist/cmd/cloud/scp/index.d.ts +1 -2
  45. package/dist/cmd/cloud/scp/index.d.ts.map +1 -1
  46. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
  47. package/dist/cmd/cloud/session/get.d.ts +2 -0
  48. package/dist/cmd/cloud/session/get.d.ts.map +1 -0
  49. package/dist/cmd/cloud/session/index.d.ts +2 -0
  50. package/dist/cmd/cloud/session/index.d.ts.map +1 -0
  51. package/dist/cmd/cloud/session/list.d.ts +2 -0
  52. package/dist/cmd/cloud/session/list.d.ts.map +1 -0
  53. package/dist/cmd/cloud/session/logs.d.ts +2 -0
  54. package/dist/cmd/cloud/session/logs.d.ts.map +1 -0
  55. package/dist/cmd/cloud/ssh.d.ts.map +1 -1
  56. package/dist/cmd/dev/agents.d.ts +2 -0
  57. package/dist/cmd/dev/agents.d.ts.map +1 -0
  58. package/dist/cmd/dev/index.d.ts.map +1 -1
  59. package/dist/cmd/dev/sync.d.ts +12 -0
  60. package/dist/cmd/dev/sync.d.ts.map +1 -0
  61. package/dist/cmd/env/delete.d.ts.map +1 -1
  62. package/dist/cmd/env/get.d.ts.map +1 -1
  63. package/dist/cmd/env/import.d.ts.map +1 -1
  64. package/dist/cmd/env/index.d.ts.map +1 -1
  65. package/dist/cmd/env/list.d.ts.map +1 -1
  66. package/dist/cmd/env/pull.d.ts.map +1 -1
  67. package/dist/cmd/env/push.d.ts.map +1 -1
  68. package/dist/cmd/env/set.d.ts.map +1 -1
  69. package/dist/cmd/index.d.ts.map +1 -1
  70. package/dist/cmd/kv/create-namespace.d.ts +3 -0
  71. package/dist/cmd/kv/create-namespace.d.ts.map +1 -0
  72. package/dist/cmd/kv/delete-namespace.d.ts +3 -0
  73. package/dist/cmd/kv/delete-namespace.d.ts.map +1 -0
  74. package/dist/cmd/kv/delete.d.ts +3 -0
  75. package/dist/cmd/kv/delete.d.ts.map +1 -0
  76. package/dist/cmd/kv/get.d.ts +3 -0
  77. package/dist/cmd/kv/get.d.ts.map +1 -0
  78. package/dist/cmd/kv/index.d.ts +2 -0
  79. package/dist/cmd/kv/index.d.ts.map +1 -0
  80. package/dist/cmd/kv/keys.d.ts +3 -0
  81. package/dist/cmd/kv/keys.d.ts.map +1 -0
  82. package/dist/cmd/kv/list-namespaces.d.ts +3 -0
  83. package/dist/cmd/kv/list-namespaces.d.ts.map +1 -0
  84. package/dist/cmd/kv/repl.d.ts +3 -0
  85. package/dist/cmd/kv/repl.d.ts.map +1 -0
  86. package/dist/cmd/kv/search.d.ts +3 -0
  87. package/dist/cmd/kv/search.d.ts.map +1 -0
  88. package/dist/cmd/kv/set.d.ts +3 -0
  89. package/dist/cmd/kv/set.d.ts.map +1 -0
  90. package/dist/cmd/kv/stats.d.ts +3 -0
  91. package/dist/cmd/kv/stats.d.ts.map +1 -0
  92. package/dist/cmd/kv/util.d.ts +8 -0
  93. package/dist/cmd/kv/util.d.ts.map +1 -0
  94. package/dist/cmd/objectstore/delete-bucket.d.ts +3 -0
  95. package/dist/cmd/objectstore/delete-bucket.d.ts.map +1 -0
  96. package/dist/cmd/objectstore/delete.d.ts +3 -0
  97. package/dist/cmd/objectstore/delete.d.ts.map +1 -0
  98. package/dist/cmd/objectstore/get.d.ts +3 -0
  99. package/dist/cmd/objectstore/get.d.ts.map +1 -0
  100. package/dist/cmd/objectstore/index.d.ts +2 -0
  101. package/dist/cmd/objectstore/index.d.ts.map +1 -0
  102. package/dist/cmd/objectstore/list-buckets.d.ts +3 -0
  103. package/dist/cmd/objectstore/list-buckets.d.ts.map +1 -0
  104. package/dist/cmd/objectstore/list-keys.d.ts +3 -0
  105. package/dist/cmd/objectstore/list-keys.d.ts.map +1 -0
  106. package/dist/cmd/objectstore/put.d.ts +3 -0
  107. package/dist/cmd/objectstore/put.d.ts.map +1 -0
  108. package/dist/cmd/objectstore/repl.d.ts +3 -0
  109. package/dist/cmd/objectstore/repl.d.ts.map +1 -0
  110. package/dist/cmd/objectstore/url.d.ts +3 -0
  111. package/dist/cmd/objectstore/url.d.ts.map +1 -0
  112. package/dist/cmd/objectstore/util.d.ts +8 -0
  113. package/dist/cmd/objectstore/util.d.ts.map +1 -0
  114. package/dist/cmd/profile/create.d.ts.map +1 -1
  115. package/dist/cmd/profile/delete.d.ts.map +1 -1
  116. package/dist/cmd/profile/index.d.ts.map +1 -1
  117. package/dist/cmd/profile/list.d.ts +1 -2
  118. package/dist/cmd/profile/list.d.ts.map +1 -1
  119. package/dist/cmd/profile/show.d.ts.map +1 -1
  120. package/dist/cmd/profile/use.d.ts.map +1 -1
  121. package/dist/cmd/project/create.d.ts.map +1 -1
  122. package/dist/cmd/project/delete.d.ts.map +1 -1
  123. package/dist/cmd/project/index.d.ts.map +1 -1
  124. package/dist/cmd/project/list.d.ts.map +1 -1
  125. package/dist/cmd/project/show.d.ts.map +1 -1
  126. package/dist/cmd/project/template-flow.d.ts +1 -1
  127. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  128. package/dist/cmd/prompt/index.d.ts +4 -0
  129. package/dist/cmd/prompt/index.d.ts.map +1 -0
  130. package/dist/cmd/prompt/llm.d.ts +3 -0
  131. package/dist/cmd/prompt/llm.d.ts.map +1 -0
  132. package/dist/cmd/repl/index.d.ts +3 -0
  133. package/dist/cmd/repl/index.d.ts.map +1 -0
  134. package/dist/cmd/schema/index.d.ts +4 -0
  135. package/dist/cmd/schema/index.d.ts.map +1 -0
  136. package/dist/cmd/schema/show.d.ts +3 -0
  137. package/dist/cmd/schema/show.d.ts.map +1 -0
  138. package/dist/cmd/secret/delete.d.ts.map +1 -1
  139. package/dist/cmd/secret/get.d.ts.map +1 -1
  140. package/dist/cmd/secret/import.d.ts.map +1 -1
  141. package/dist/cmd/secret/index.d.ts.map +1 -1
  142. package/dist/cmd/secret/list.d.ts.map +1 -1
  143. package/dist/cmd/secret/pull.d.ts.map +1 -1
  144. package/dist/cmd/secret/push.d.ts.map +1 -1
  145. package/dist/cmd/secret/set.d.ts.map +1 -1
  146. package/dist/cmd/version/index.d.ts.map +1 -1
  147. package/dist/config.d.ts +1 -1
  148. package/dist/config.d.ts.map +1 -1
  149. package/dist/errors.d.ts +83 -0
  150. package/dist/errors.d.ts.map +1 -0
  151. package/dist/explain.d.ts +47 -0
  152. package/dist/explain.d.ts.map +1 -0
  153. package/dist/index.d.ts +6 -0
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/json.d.ts +3 -0
  156. package/dist/json.d.ts.map +1 -0
  157. package/dist/output.d.ts +136 -0
  158. package/dist/output.d.ts.map +1 -0
  159. package/dist/repl.d.ts +120 -0
  160. package/dist/repl.d.ts.map +1 -0
  161. package/dist/schema-generator.d.ts +67 -0
  162. package/dist/schema-generator.d.ts.map +1 -0
  163. package/dist/tui.d.ts +35 -1
  164. package/dist/tui.d.ts.map +1 -1
  165. package/dist/types.d.ts +77 -6
  166. package/dist/types.d.ts.map +1 -1
  167. package/dist/utils/format.d.ts +9 -0
  168. package/dist/utils/format.d.ts.map +1 -0
  169. package/package.json +12 -4
  170. package/src/banner.ts +7 -7
  171. package/src/cli-logger.ts +80 -0
  172. package/src/cli.ts +192 -58
  173. package/src/cmd/agents/index.ts +147 -0
  174. package/src/cmd/auth/index.ts +1 -0
  175. package/src/cmd/auth/login.ts +7 -2
  176. package/src/cmd/auth/logout.ts +4 -0
  177. package/src/cmd/auth/signup.ts +7 -2
  178. package/src/cmd/auth/ssh/add.ts +20 -3
  179. package/src/cmd/auth/ssh/delete.ts +57 -4
  180. package/src/cmd/auth/ssh/index.ts +4 -3
  181. package/src/cmd/auth/ssh/list.ts +38 -27
  182. package/src/cmd/auth/whoami.ts +32 -21
  183. package/src/cmd/bundle/ast.test.ts +2 -2
  184. package/src/cmd/bundle/ast.ts +112 -22
  185. package/src/cmd/bundle/index.ts +20 -0
  186. package/src/cmd/bundle/plugin.ts +60 -14
  187. package/src/cmd/capabilities/index.ts +12 -0
  188. package/src/cmd/capabilities/show.ts +256 -0
  189. package/src/cmd/cloud/deploy.ts +54 -0
  190. package/src/cmd/cloud/deployment/index.ts +1 -0
  191. package/src/cmd/cloud/deployment/list.ts +66 -25
  192. package/src/cmd/cloud/deployment/remove.ts +26 -2
  193. package/src/cmd/cloud/deployment/rollback.ts +35 -4
  194. package/src/cmd/cloud/deployment/show.ts +37 -2
  195. package/src/cmd/cloud/deployment/undeploy.ts +12 -1
  196. package/src/cmd/cloud/deployment/utils.ts +5 -2
  197. package/src/cmd/cloud/domain.ts +3 -2
  198. package/src/cmd/cloud/index.ts +10 -1
  199. package/src/cmd/cloud/resource/add.ts +19 -0
  200. package/src/cmd/cloud/resource/delete.ts +24 -3
  201. package/src/cmd/cloud/resource/index.ts +4 -3
  202. package/src/cmd/cloud/resource/list.ts +36 -10
  203. package/src/cmd/cloud/scp/download.ts +27 -1
  204. package/src/cmd/cloud/scp/index.ts +4 -3
  205. package/src/cmd/cloud/scp/upload.ts +27 -1
  206. package/src/cmd/cloud/session/get.ts +164 -0
  207. package/src/cmd/cloud/session/index.ts +11 -0
  208. package/src/cmd/cloud/session/list.ts +145 -0
  209. package/src/cmd/cloud/session/logs.ts +68 -0
  210. package/src/cmd/cloud/ssh.ts +12 -0
  211. package/src/cmd/dev/agents.ts +122 -0
  212. package/src/cmd/dev/index.ts +106 -8
  213. package/src/cmd/dev/sync.ts +414 -0
  214. package/src/cmd/dev/templates.ts +1 -1
  215. package/src/cmd/env/delete.ts +17 -0
  216. package/src/cmd/env/get.ts +17 -1
  217. package/src/cmd/env/import.ts +47 -3
  218. package/src/cmd/env/index.ts +1 -0
  219. package/src/cmd/env/list.ts +13 -1
  220. package/src/cmd/env/pull.ts +20 -0
  221. package/src/cmd/env/push.ts +33 -1
  222. package/src/cmd/env/set.ts +25 -1
  223. package/src/cmd/index.ts +9 -2
  224. package/src/cmd/kv/create-namespace.ts +45 -0
  225. package/src/cmd/kv/delete-namespace.ts +73 -0
  226. package/src/cmd/kv/delete.ts +51 -0
  227. package/src/cmd/kv/get.ts +65 -0
  228. package/src/cmd/kv/index.ts +31 -0
  229. package/src/cmd/kv/keys.ts +57 -0
  230. package/src/cmd/kv/list-namespaces.ts +43 -0
  231. package/src/cmd/kv/repl.ts +284 -0
  232. package/src/cmd/kv/search.ts +80 -0
  233. package/src/cmd/kv/set.ts +63 -0
  234. package/src/cmd/kv/stats.ts +96 -0
  235. package/src/cmd/kv/util.ts +32 -0
  236. package/src/cmd/objectstore/delete-bucket.ts +72 -0
  237. package/src/cmd/objectstore/delete.ts +59 -0
  238. package/src/cmd/objectstore/get.ts +64 -0
  239. package/src/cmd/objectstore/index.ts +27 -0
  240. package/src/cmd/objectstore/list-buckets.ts +45 -0
  241. package/src/cmd/objectstore/list-keys.ts +60 -0
  242. package/src/cmd/objectstore/put.ts +62 -0
  243. package/src/cmd/objectstore/repl.ts +235 -0
  244. package/src/cmd/objectstore/url.ts +59 -0
  245. package/src/cmd/objectstore/util.ts +28 -0
  246. package/src/cmd/profile/create.ts +28 -2
  247. package/src/cmd/profile/delete.ts +17 -2
  248. package/src/cmd/profile/index.ts +1 -0
  249. package/src/cmd/profile/list.ts +7 -3
  250. package/src/cmd/profile/show.ts +20 -5
  251. package/src/cmd/profile/use.ts +8 -0
  252. package/src/cmd/project/create.ts +31 -0
  253. package/src/cmd/project/delete.ts +24 -2
  254. package/src/cmd/project/index.ts +1 -0
  255. package/src/cmd/project/list.ts +24 -10
  256. package/src/cmd/project/show.ts +28 -9
  257. package/src/cmd/project/template-flow.ts +10 -6
  258. package/src/cmd/prompt/index.ts +12 -0
  259. package/src/cmd/prompt/llm.ts +368 -0
  260. package/src/cmd/repl/index.ts +477 -0
  261. package/src/cmd/schema/index.ts +12 -0
  262. package/src/cmd/schema/show.ts +27 -0
  263. package/src/cmd/secret/delete.ts +17 -0
  264. package/src/cmd/secret/get.ts +20 -1
  265. package/src/cmd/secret/import.ts +45 -2
  266. package/src/cmd/secret/index.ts +1 -0
  267. package/src/cmd/secret/list.ts +10 -1
  268. package/src/cmd/secret/pull.ts +20 -0
  269. package/src/cmd/secret/push.ts +33 -1
  270. package/src/cmd/secret/set.ts +20 -0
  271. package/src/cmd/version/index.ts +15 -2
  272. package/src/config.ts +17 -4
  273. package/src/errors.ts +222 -0
  274. package/src/explain.ts +126 -0
  275. package/src/index.ts +51 -0
  276. package/src/json.ts +28 -0
  277. package/src/output.ts +307 -0
  278. package/src/repl.ts +1507 -0
  279. package/src/schema-generator.ts +389 -0
  280. package/src/tui.ts +178 -13
  281. package/src/types.ts +75 -22
  282. package/src/utils/format.ts +17 -0
@@ -3,12 +3,17 @@ import { getAppBaseURL, UpgradeRequiredError } from '@agentuity/server';
3
3
  import { saveAuth } from '../../config';
4
4
  import { generateSignupOTP, pollForSignupCompletion } from './api';
5
5
  import * as tui from '../../tui';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
6
8
 
7
9
  export const signupCommand = createSubcommand({
8
10
  name: 'signup',
9
11
  description: 'Create a new Agentuity Cloud Platform account',
12
+ tags: ['mutating', 'creates-resource', 'slow', 'api-intensive'],
10
13
  toplevel: true,
14
+ idempotent: false,
11
15
  requires: { apiClient: true },
16
+ examples: [getCommand('auth signup'), getCommand('signup')],
12
17
 
13
18
  async handler(ctx) {
14
19
  const { logger, config, apiClient } = ctx;
@@ -46,9 +51,9 @@ export const signupCommand = createSubcommand({
46
51
  tui.banner('CLI Upgrade Required', bannerBody);
47
52
  process.exit(1);
48
53
  } else if (error instanceof Error) {
49
- logger.fatal(`Signup failed: ${error.message}`);
54
+ logger.fatal(`Signup failed: ${error.message}`, ErrorCode.AUTH_FAILED);
50
55
  } else {
51
- logger.fatal('Signup failed');
56
+ logger.fatal('Signup failed', ErrorCode.AUTH_FAILED);
52
57
  }
53
58
  }
54
59
  },
@@ -7,11 +7,19 @@ import { readFileSync, readdirSync, statSync } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { homedir } from 'os';
9
9
  import { z } from 'zod';
10
+ import { ErrorCode } from '../../../errors';
10
11
 
11
12
  const optionsSchema = z.object({
12
13
  file: z.string().optional().describe('File containing the public key'),
13
14
  });
14
15
 
16
+ const SSHAddResponseSchema = z.object({
17
+ success: z.boolean().describe('Whether the operation succeeded'),
18
+ fingerprint: z.string().describe('SSH key fingerprint'),
19
+ keyType: z.string().describe('SSH key type (e.g., ssh-rsa, ssh-ed25519)'),
20
+ added: z.number().describe('Number of keys added'),
21
+ });
22
+
15
23
  interface SSHKeyOption {
16
24
  path: string;
17
25
  filename: string;
@@ -107,15 +115,24 @@ export const addCommand = createSubcommand({
107
115
  name: 'add',
108
116
  aliases: ['create'],
109
117
  description: 'Add an SSH public key to your account (reads from file or stdin)',
118
+ tags: ['mutating', 'creates-resource', 'slow', 'requires-auth'],
119
+ idempotent: false,
110
120
  requires: { apiClient: true, auth: true },
121
+ examples: [
122
+ getCommand('auth ssh add'),
123
+ getCommand('auth ssh add --file ~/.ssh/id_ed25519.pub'),
124
+ getCommand('auth ssh add --file ./deploy_key.pub'),
125
+ 'cat ~/.ssh/id_rsa.pub | ' + getCommand('auth ssh add'),
126
+ ],
111
127
  schema: {
112
128
  options: optionsSchema,
129
+ response: SSHAddResponseSchema,
113
130
  },
114
131
  async handler(ctx) {
115
132
  const { logger, apiClient, opts } = ctx;
116
133
 
117
134
  if (!apiClient) {
118
- logger.fatal('API client is not available');
135
+ logger.fatal('API client is not available', ErrorCode.INTERNAL_ERROR);
119
136
  }
120
137
 
121
138
  try {
@@ -254,9 +271,9 @@ export const addCommand = createSubcommand({
254
271
  } catch (error) {
255
272
  logger.trace(error);
256
273
  if (error instanceof Error) {
257
- logger.fatal(`Failed to add SSH key: ${error.message}`);
274
+ logger.fatal(`Failed to add SSH key: ${error.message}`, ErrorCode.API_ERROR);
258
275
  } else {
259
- logger.fatal('Failed to add SSH key');
276
+ logger.fatal('Failed to add SSH key', ErrorCode.API_ERROR);
260
277
  }
261
278
  }
262
279
  },
@@ -3,12 +3,29 @@ import { removeSSHKey, listSSHKeys } from './api';
3
3
  import * as tui from '../../../tui';
4
4
  import enquirer from 'enquirer';
5
5
  import { z } from 'zod';
6
+ import { isExplainMode, isDryRunMode, outputExplain, outputDryRun } from '../../../explain';
7
+ import { getCommand } from '../../../command-prefix';
8
+ import { ErrorCode } from '../../../errors';
9
+
10
+ const SSHDeleteResponseSchema = z.object({
11
+ success: z.boolean().describe('Whether the operation succeeded'),
12
+ removed: z.number().describe('Number of keys removed'),
13
+ fingerprints: z.array(z.string()).describe('Fingerprints of removed keys'),
14
+ });
6
15
 
7
16
  export const deleteCommand = createSubcommand({
8
17
  name: 'delete',
9
18
  aliases: ['rm', 'del', 'remove'],
10
19
  description: 'Delete an SSH key from your account',
20
+ tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
11
21
  requires: { apiClient: true, auth: true },
22
+ idempotent: false,
23
+ examples: [
24
+ getCommand('auth ssh delete'),
25
+ getCommand('auth ssh delete <fingerprint>'),
26
+ getCommand('--explain auth ssh delete abc123'),
27
+ getCommand('--dry-run auth ssh delete abc123'),
28
+ ],
12
29
  schema: {
13
30
  args: z.object({
14
31
  fingerprints: z.array(z.string()).optional().describe('SSH key fingerprint(s) to remove'),
@@ -16,12 +33,13 @@ export const deleteCommand = createSubcommand({
16
33
  options: z.object({
17
34
  confirm: z.boolean().default(true).describe('prompt for confirmation before deletion'),
18
35
  }),
36
+ response: SSHDeleteResponseSchema,
19
37
  },
20
38
  async handler(ctx) {
21
- const { logger, apiClient, args, opts } = ctx;
39
+ const { logger, apiClient, args, opts, options } = ctx;
22
40
 
23
41
  if (!apiClient) {
24
- logger.fatal('API client is not available');
42
+ logger.fatal('API client is not available', ErrorCode.INTERNAL_ERROR);
25
43
  }
26
44
 
27
45
  const shouldConfirm = process.stdin.isTTY ? opts.confirm : false;
@@ -67,6 +85,22 @@ export const deleteCommand = createSubcommand({
67
85
  }
68
86
  }
69
87
 
88
+ // If in explain mode, show what would happen
89
+ if (isExplainMode(options)) {
90
+ outputExplain(
91
+ {
92
+ command: 'auth ssh delete',
93
+ description: 'Delete SSH keys from your account',
94
+ steps: fingerprintsToRemove.map((fp) => ({
95
+ action: `Remove SSH key with fingerprint: ${fp}`,
96
+ })),
97
+ warnings: ['This action cannot be undone'],
98
+ },
99
+ options
100
+ );
101
+ return;
102
+ }
103
+
70
104
  if (shouldConfirm) {
71
105
  tui.newline();
72
106
  const confirmed = await tui.confirm(
@@ -80,6 +114,19 @@ export const deleteCommand = createSubcommand({
80
114
  }
81
115
  }
82
116
 
117
+ // Handle dry-run mode
118
+ if (isDryRunMode(options)) {
119
+ for (const fingerprint of fingerprintsToRemove) {
120
+ outputDryRun(`Would remove SSH key: ${fingerprint}`, options);
121
+ }
122
+ tui.newline();
123
+ tui.info(
124
+ `[DRY RUN] Would remove ${fingerprintsToRemove.length} SSH key${fingerprintsToRemove.length > 1 ? 's' : ''}`
125
+ );
126
+ return;
127
+ }
128
+
129
+ // Actually execute the deletion
83
130
  for (const fingerprint of fingerprintsToRemove) {
84
131
  await tui.spinner(`Removing SSH key ${fingerprint}...`, () =>
85
132
  removeSSHKey(apiClient, fingerprint)
@@ -90,12 +137,18 @@ export const deleteCommand = createSubcommand({
90
137
  tui.success(
91
138
  `Removed ${fingerprintsToRemove.length} SSH key${fingerprintsToRemove.length > 1 ? 's' : ''}`
92
139
  );
140
+
141
+ return {
142
+ success: true,
143
+ removed: fingerprintsToRemove.length,
144
+ fingerprints: fingerprintsToRemove,
145
+ };
93
146
  } catch (error) {
94
147
  logger.trace(error);
95
148
  if (error instanceof Error) {
96
- logger.fatal(`Failed to remove SSH key: ${error.message}`);
149
+ logger.fatal(`Failed to remove SSH key: ${error.message}`, ErrorCode.API_ERROR);
97
150
  } else {
98
- logger.fatal('Failed to remove SSH key');
151
+ logger.fatal('Failed to remove SSH key', ErrorCode.API_ERROR);
99
152
  }
100
153
  }
101
154
  },
@@ -1,10 +1,11 @@
1
- import type { SubcommandDefinition } from '../../../types';
1
+ import { createCommand } from '../../../types';
2
2
  import { listCommand } from './list';
3
3
  import { addCommand } from './add';
4
4
  import { deleteCommand } from './delete';
5
5
 
6
- export const sshSubcommand: SubcommandDefinition = {
6
+ export const sshSubcommand = createCommand({
7
7
  name: 'ssh',
8
8
  description: 'Manage SSH keys',
9
+ tags: ['fast', 'requires-auth'],
9
10
  subcommands: [listCommand, addCommand, deleteCommand],
10
- };
11
+ });
@@ -1,68 +1,79 @@
1
1
  import { createSubcommand } from '../../../types';
2
2
  import { listSSHKeys } from './api';
3
3
  import * as tui from '../../../tui';
4
+ import { getCommand } from '../../../command-prefix';
5
+ import { ErrorCode } from '../../../errors';
4
6
  import { z } from 'zod';
5
- import { Table } from 'console-table-printer';
7
+
8
+ const SSHListResponseSchema = z.array(
9
+ z.object({
10
+ id: z.string().describe('SSH key ID'),
11
+ fingerprint: z.string().describe('SSH key fingerprint'),
12
+ keyType: z.string().describe('SSH key type (e.g., ssh-rsa, ssh-ed25519)'),
13
+ comment: z.string().optional().describe('SSH key comment'),
14
+ createdAt: z.string().optional().describe('Creation timestamp'),
15
+ })
16
+ );
6
17
 
7
18
  export const listCommand = createSubcommand({
8
19
  name: 'list',
9
20
  aliases: ['ls'],
10
21
  description: 'List all SSH keys on your account',
22
+ tags: ['read-only', 'fast', 'requires-auth'],
11
23
  requires: { apiClient: true, auth: true },
24
+ idempotent: true,
25
+ examples: [
26
+ getCommand('auth ssh list'),
27
+ getCommand('auth ssh ls'),
28
+ getCommand('--json auth ssh list'),
29
+ ],
12
30
  schema: {
13
- options: z.object({
14
- format: z.enum(['text', 'json']).default('text').describe('output format'),
15
- }),
31
+ response: SSHListResponseSchema,
16
32
  },
17
33
  async handler(ctx) {
18
- const { logger, apiClient, opts } = ctx;
19
- const format = opts.format;
34
+ const { logger, apiClient, options } = ctx;
20
35
 
21
36
  if (!apiClient) {
22
- logger.fatal('API client is not available');
37
+ logger.fatal('API client is not available', ErrorCode.INTERNAL_ERROR);
23
38
  }
24
39
 
25
40
  try {
26
41
  const keys = await tui.spinner('Fetching SSH keys...', () => listSSHKeys(apiClient));
27
42
 
28
- if (format === 'json') {
43
+ if (options.json) {
29
44
  console.log(JSON.stringify(keys, null, 2));
30
- return;
45
+ return keys;
31
46
  }
32
47
 
33
48
  tui.newline();
34
49
 
35
50
  if (keys.length === 0) {
36
51
  console.log('No SSH keys found');
37
- return;
52
+ return [];
38
53
  }
39
54
 
40
55
  console.log(tui.bold('SSH Keys:'));
41
56
  tui.newline();
42
57
 
43
- const table = new Table({
44
- columns: [
45
- { name: 'TYPE', alignment: 'left' },
46
- { name: 'FINGERPRINT', alignment: 'left' },
47
- { name: 'COMMENT', alignment: 'left' },
48
- ],
49
- });
58
+ const tableData = keys.map((key) => ({
59
+ TYPE: key.keyType,
60
+ FINGERPRINT: key.fingerprint,
61
+ COMMENT: key.comment || tui.muted('(no comment)'),
62
+ }));
50
63
 
51
- for (const key of keys) {
52
- table.addRow({
53
- TYPE: key.keyType,
54
- FINGERPRINT: key.fingerprint,
55
- COMMENT: key.comment || tui.muted('(no comment)'),
56
- });
57
- }
58
- table.printTable();
64
+ tui.table(tableData, [
65
+ { name: 'TYPE', alignment: 'left' },
66
+ { name: 'FINGERPRINT', alignment: 'left' },
67
+ { name: 'COMMENT', alignment: 'left' },
68
+ ]);
59
69
 
70
+ return keys;
60
71
  } catch (error) {
61
72
  logger.trace(error);
62
73
  if (error instanceof Error) {
63
- logger.fatal(`Failed to list SSH keys: ${error.message}`);
74
+ logger.fatal(`Failed to list SSH keys: ${error.message}`, ErrorCode.API_ERROR);
64
75
  } else {
65
- logger.fatal('Failed to list SSH keys');
76
+ logger.fatal('Failed to list SSH keys', ErrorCode.API_ERROR);
66
77
  }
67
78
  }
68
79
  },
@@ -1,23 +1,36 @@
1
- import { z } from 'zod';
2
1
  import { createSubcommand } from '../../types';
3
2
  import * as tui from '../../tui';
4
3
  import { whoami } from '@agentuity/server';
4
+ import { getCommand } from '../../command-prefix';
5
+ import { z } from 'zod';
6
+
7
+ const WhoamiResponseSchema = z.object({
8
+ userId: z.string().describe('Unique user identifier'),
9
+ firstName: z.string().describe('User first name'),
10
+ lastName: z.string().describe('User last name'),
11
+ organizations: z
12
+ .array(
13
+ z.object({
14
+ id: z.string().describe('Organization ID'),
15
+ name: z.string().describe('Organization name'),
16
+ })
17
+ )
18
+ .describe('Organizations the user belongs to'),
19
+ });
5
20
 
6
21
  export const whoamiCommand = createSubcommand({
7
22
  name: 'whoami',
8
23
  description: 'Display information about the currently authenticated user',
24
+ tags: ['read-only', 'fast', 'requires-auth'],
9
25
  requires: { auth: true, apiClient: true },
26
+ idempotent: true,
10
27
  schema: {
11
- options: z.object({
12
- format: z
13
- .enum(['json', 'table'])
14
- .optional()
15
- .describe('the output format: json, table (default)'),
16
- }),
28
+ response: WhoamiResponseSchema,
17
29
  },
30
+ examples: [getCommand('auth whoami'), getCommand('--json auth whoami')],
18
31
 
19
32
  async handler(ctx) {
20
- const { apiClient, opts, auth } = ctx;
33
+ const { apiClient, auth, options } = ctx;
21
34
 
22
35
  if (!apiClient) {
23
36
  throw new Error(
@@ -33,19 +46,15 @@ export const whoamiCommand = createSubcommand({
33
46
  },
34
47
  });
35
48
 
36
- if (opts?.format === 'json') {
37
- console.log(
38
- JSON.stringify(
39
- {
40
- userId: auth?.userId,
41
- firstName: user.firstName,
42
- lastName: user.lastName,
43
- organizations: user.organizations,
44
- },
45
- null,
46
- 2
47
- )
48
- );
49
+ const result = {
50
+ userId: auth?.userId || '',
51
+ firstName: user.firstName,
52
+ lastName: user.lastName,
53
+ organizations: user.organizations,
54
+ };
55
+
56
+ if (options.json) {
57
+ console.log(JSON.stringify(result, null, 2));
49
58
  } else {
50
59
  const fullName = `${user.firstName} ${user.lastName}`;
51
60
 
@@ -65,5 +74,7 @@ export const whoamiCommand = createSubcommand({
65
74
  }
66
75
  tui.newline();
67
76
  }
77
+
78
+ return result;
68
79
  },
69
80
  });
@@ -278,8 +278,8 @@ describe('parseEvalMetadata', () => {
278
278
  TEST_DEPLOYMENT_ID
279
279
  );
280
280
 
281
- expect(result).toHaveLength(1);
282
- expect(result[0].name).toBe('non-exported-eval');
281
+ // Non-exported evals should not be included in metadata
282
+ expect(result).toHaveLength(0);
283
283
  });
284
284
  });
285
285
 
@@ -116,6 +116,10 @@ function hashSHA1(...val: string[]): string {
116
116
  return hasher.digest().toHex();
117
117
  }
118
118
 
119
+ export function getDevmodeDeploymentId(projectId: string, endpointId: string): string {
120
+ return `devmode_${hashSHA1(projectId, endpointId)}`;
121
+ }
122
+
119
123
  function getAgentId(
120
124
  projectId: string,
121
125
  deploymentId: string,
@@ -147,31 +151,55 @@ function generateRouteId(
147
151
  return `route_${hashSHA1(projectId, deploymentId, type, method, filename, path, version)}`;
148
152
  }
149
153
 
154
+ function generateStableAgentId(projectId: string, name: string): string {
155
+ return `agentid_${hashSHA1(projectId, name)}`.substring(0, 64);
156
+ }
157
+
158
+ function generateStableEvalId(projectId: string, agentId: string, name: string): string {
159
+ return `evalid_${hashSHA1(projectId, agentId, name)}`.substring(0, 64);
160
+ }
161
+
150
162
  type AcornParseResultType = ReturnType<typeof acornLoose.parse>;
151
163
 
152
164
  function augmentAgentMetadataNode(
165
+ projectId: string,
153
166
  id: string,
154
167
  name: string,
155
168
  rel: string,
156
169
  version: string,
157
170
  ast: AcornParseResultType,
158
171
  propvalue: ASTObjectExpression,
159
- _filename: string
172
+ filename: string
160
173
  ): [string, Map<string, string>] {
161
174
  const metadata = parseObjectExpressionToMap(propvalue);
162
175
  if (!metadata.has('name')) {
163
- metadata.set('name', name);
164
- propvalue.properties.push(createObjectPropertyNode('name', name));
176
+ const location = ast.loc?.start ? ` on line ${ast.loc.start}` : '';
177
+ throw new Error(
178
+ `missing required metadata.name in ${filename}${location}. This Agent should have a unique and human readable name for this project.`
179
+ );
165
180
  }
181
+ if (metadata.has('identifier') && name !== metadata.get('identifier')) {
182
+ const location = ast.loc?.start ? ` on line ${ast.loc.start}` : '';
183
+ throw new Error(
184
+ `metadata.identifier (${metadata.get('identifier')}) in ${filename}${location} is mismatched (${name}). This is an internal error.`
185
+ );
186
+ }
187
+ const descriptionNode = propvalue.properties.find((x) => x.key.name === 'description')?.value;
188
+ const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
189
+ const agentId = generateStableAgentId(projectId, name);
166
190
  metadata.set('version', version);
167
191
  metadata.set('identifier', name);
168
192
  metadata.set('filename', rel);
169
193
  metadata.set('id', id);
194
+ metadata.set('agentId', agentId);
195
+ metadata.set('description', description);
170
196
  propvalue.properties.push(
171
197
  createObjectPropertyNode('id', id),
198
+ createObjectPropertyNode('agentId', agentId),
172
199
  createObjectPropertyNode('version', version),
173
200
  createObjectPropertyNode('identifier', name),
174
- createObjectPropertyNode('filename', rel)
201
+ createObjectPropertyNode('filename', rel),
202
+ createObjectPropertyNode('description', description)
175
203
  );
176
204
 
177
205
  const newsource = generate(ast);
@@ -222,14 +250,29 @@ function setLiteralValue(literal: ASTLiteral, value: string) {
222
250
  }
223
251
 
224
252
  function augmentEvalMetadataNode(
253
+ projectId: string,
254
+ agentId: string,
225
255
  id: string,
226
256
  name: string,
227
257
  rel: string,
228
258
  version: string,
229
- identifier: string,
230
- metadataObj: ASTObjectExpression
259
+ _ast: AcornParseResultType,
260
+ metadataObj: ASTObjectExpression,
261
+ _filename: string
231
262
  ): void {
232
- // Check if id, version, identifier, filename already exist
263
+ const metadata = parseObjectExpressionToMap(metadataObj);
264
+ // Name can come from metadata.name or variable name (already resolved in caller)
265
+ // If metadata doesn't have name, we'll add it from the resolved name
266
+ if (!metadata.has('name')) {
267
+ metadataObj.properties.push(createObjectPropertyNode('name', name));
268
+ }
269
+ const descriptionNode = metadataObj.properties.find((x) => x.key.name === 'description')?.value;
270
+ const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
271
+ const effectiveAgentId = agentId || '';
272
+ const _evalId = getEvalId(projectId, effectiveAgentId, rel, name, version); // Deployment-specific ID (not used, kept for potential future use)
273
+ const stableEvalId = generateStableEvalId(projectId, effectiveAgentId, name);
274
+
275
+ // Check if id, version, identifier, filename, evalId already exist
233
276
  const existingKeys = new Set<string>();
234
277
  for (const prop of metadataObj.properties) {
235
278
  if (prop.key.type === 'Identifier') {
@@ -266,12 +309,12 @@ function augmentEvalMetadataNode(
266
309
  }
267
310
 
268
311
  if (!existingKeys.has('identifier')) {
269
- metadataObj.properties.push(createObjectPropertyNode('identifier', identifier));
312
+ metadataObj.properties.push(createObjectPropertyNode('identifier', name));
270
313
  } else {
271
314
  for (const prop of metadataObj.properties) {
272
315
  if (prop.key.type === 'Identifier' && prop.key.name === 'identifier') {
273
316
  if (prop.value.type === 'Literal') {
274
- setLiteralValue(prop.value as ASTLiteral, identifier);
317
+ setLiteralValue(prop.value as ASTLiteral, name);
275
318
  }
276
319
  break;
277
320
  }
@@ -290,6 +333,32 @@ function augmentEvalMetadataNode(
290
333
  }
291
334
  }
292
335
  }
336
+
337
+ if (!existingKeys.has('evalId')) {
338
+ metadataObj.properties.push(createObjectPropertyNode('evalId', stableEvalId));
339
+ } else {
340
+ for (const prop of metadataObj.properties) {
341
+ if (prop.key.type === 'Identifier' && prop.key.name === 'evalId') {
342
+ if (prop.value.type === 'Literal') {
343
+ setLiteralValue(prop.value as ASTLiteral, stableEvalId);
344
+ }
345
+ break;
346
+ }
347
+ }
348
+ }
349
+
350
+ if (!existingKeys.has('description')) {
351
+ metadataObj.properties.push(createObjectPropertyNode('description', description));
352
+ } else {
353
+ for (const prop of metadataObj.properties) {
354
+ if (prop.key.type === 'Identifier' && prop.key.name === 'description') {
355
+ if (prop.value.type === 'Literal') {
356
+ setLiteralValue(prop.value as ASTLiteral, description);
357
+ }
358
+ break;
359
+ }
360
+ }
361
+ }
293
362
  }
294
363
 
295
364
  export function parseEvalMetadata(
@@ -297,7 +366,8 @@ export function parseEvalMetadata(
297
366
  filename: string,
298
367
  contents: string,
299
368
  projectId: string,
300
- deploymentId: string
369
+ deploymentId: string,
370
+ agentId?: string
301
371
  ): [
302
372
  string,
303
373
  Array<{
@@ -306,6 +376,7 @@ export function parseEvalMetadata(
306
376
  version: string;
307
377
  identifier: string;
308
378
  name: string;
379
+ evalId: string;
309
380
  description?: string;
310
381
  }>,
311
382
  ] {
@@ -319,8 +390,6 @@ export function parseEvalMetadata(
319
390
  logger.trace(`Parsing evals from ${filename}`);
320
391
  const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
321
392
  const rel = relative(rootDir, filename);
322
- const dir = dirname(filename);
323
- const identifier = basename(dir);
324
393
  const version = hash(contents);
325
394
  const evals: Array<{
326
395
  filename: string;
@@ -328,17 +397,16 @@ export function parseEvalMetadata(
328
397
  version: string;
329
398
  identifier: string;
330
399
  name: string;
400
+ evalId: string;
331
401
  description?: string;
332
402
  }> = [];
333
403
 
334
- // Find all agent.createEval() calls
404
+ // Find all exported agent.createEval() calls
335
405
  for (const body of ast.body) {
336
406
  let variableDeclaration: { declarations: Array<ASTVariableDeclarator> } | undefined;
337
407
 
338
- // Handle both direct VariableDeclaration and ExportNamedDeclaration
339
- if (body.type === 'VariableDeclaration') {
340
- variableDeclaration = body as { declarations: Array<ASTVariableDeclarator> };
341
- } else if (body.type === 'ExportNamedDeclaration') {
408
+ // Only process exported VariableDeclarations
409
+ if (body.type === 'ExportNamedDeclaration') {
342
410
  const exportDecl = body as {
343
411
  declaration?: { type: string; declarations?: Array<ASTVariableDeclarator> };
344
412
  };
@@ -427,14 +495,31 @@ export function parseEvalMetadata(
427
495
  );
428
496
 
429
497
  // Inject metadata into AST if metadata object exists
498
+ let stableEvalId: string;
499
+ const effectiveAgentId = agentId || '';
430
500
  if (metadataObj) {
431
501
  augmentEvalMetadataNode(
502
+ projectId,
503
+ effectiveAgentId,
432
504
  evalId,
433
505
  finalName,
434
506
  rel,
435
507
  version,
436
- identifier,
437
- metadataObj
508
+ ast,
509
+ metadataObj,
510
+ filename
511
+ );
512
+ // Extract evalId from metadata after augmentation
513
+ const metadata = parseObjectExpressionToMap(metadataObj);
514
+ stableEvalId =
515
+ metadata.get('evalId') ||
516
+ generateStableEvalId(projectId, effectiveAgentId, finalName);
517
+ } else {
518
+ // If no metadata object, generate stable evalId
519
+ stableEvalId = generateStableEvalId(
520
+ projectId,
521
+ effectiveAgentId,
522
+ finalName
438
523
  );
439
524
  }
440
525
 
@@ -442,8 +527,9 @@ export function parseEvalMetadata(
442
527
  filename: rel,
443
528
  id: evalId,
444
529
  version,
445
- identifier,
530
+ identifier: finalName,
446
531
  name: finalName,
532
+ evalId: stableEvalId,
447
533
  description: evalDescription,
448
534
  });
449
535
  }
@@ -508,6 +594,7 @@ export async function parseAgentMetadata(
508
594
  for (const prop of callargexp.properties) {
509
595
  if (prop.key.type === 'Identifier' && prop.key.name === 'metadata') {
510
596
  result = augmentAgentMetadataNode(
597
+ projectId,
511
598
  id,
512
599
  name,
513
600
  rel,
@@ -562,6 +649,7 @@ export async function parseAgentMetadata(
562
649
  prop.key.name === 'metadata'
563
650
  ) {
564
651
  result = augmentAgentMetadataNode(
652
+ projectId,
565
653
  id,
566
654
  name,
567
655
  rel,
@@ -615,14 +703,16 @@ export async function parseAgentMetadata(
615
703
  if (await evalsFile.exists()) {
616
704
  logger.trace(`Found evals file at ${evalsPath}, parsing...`);
617
705
  const evalsSource = await evalsFile.text();
618
- const transpiler = new Bun.Transpiler({ loader: 'ts' });
706
+ const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
619
707
  const evalsContents = transpiler.transformSync(evalsSource);
708
+ const agentId = result[1].get('agentId') || '';
620
709
  const [, evals] = parseEvalMetadata(
621
710
  rootDir,
622
711
  evalsPath,
623
712
  evalsContents,
624
713
  projectId,
625
- deploymentId
714
+ deploymentId,
715
+ agentId
626
716
  );
627
717
  if (evals.length > 0) {
628
718
  logger.trace(`Adding ${evals.length} eval(s) to agent metadata for ${name}`);