@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,73 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../types';
3
+ import { ErrorCode } from '../../errors';
4
+ import * as tui from '../../tui';
5
+ import { createStorageAdapter } from './util';
6
+ import { getCommand } from '../../command-prefix';
7
+
8
+ export const deleteNamespaceSubcommand = createCommand({
9
+ name: 'delete-namespace',
10
+ aliases: ['rm-namespace'],
11
+ description: 'Delete a keyvalue namespace and all its keys',
12
+ tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth', 'requires-project'],
13
+ idempotent: true,
14
+ requires: { auth: true, project: true },
15
+ examples: [
16
+ `${getCommand('kv delete-namespace staging')} - Delete staging namespace (interactive)`,
17
+ `${getCommand('kv rm-namespace cache --confirm')} - Delete cache without confirmation`,
18
+ `${getCommand('kv delete-namespace production --confirm')} - Force delete production`,
19
+ ],
20
+ schema: {
21
+ args: z.object({
22
+ name: z.string().min(1).max(64).describe('the namespace name'),
23
+ confirm: z
24
+ .boolean()
25
+ .optional()
26
+ .default(false)
27
+ .describe('if true will not prompt for confirmation'),
28
+ }),
29
+ response: z.object({
30
+ success: z.boolean().describe('Whether the deletion succeeded'),
31
+ namespace: z.string().describe('Deleted namespace name'),
32
+ message: z.string().optional().describe('Confirmation message'),
33
+ }),
34
+ },
35
+
36
+ async handler(ctx) {
37
+ const { args } = ctx;
38
+ const kv = await createStorageAdapter(ctx);
39
+
40
+ if (!args.confirm) {
41
+ if (!process.stdin.isTTY) {
42
+ tui.fatal(
43
+ 'No TTY and --confirm is not set. Refusing to delete',
44
+ ErrorCode.VALIDATION_FAILED
45
+ );
46
+ }
47
+ tui.warning(`This will delete namespace ${tui.bold(args.name)} and ALL its keys.`);
48
+ const confirm = await new Promise<boolean>((resolve) => {
49
+ process.stdout.write('Are you sure? (yes/no): ');
50
+ process.stdin.once('data', (data) => {
51
+ const answer = data.toString().trim().toLowerCase();
52
+ resolve(answer === 'yes' || answer === 'y');
53
+ });
54
+ });
55
+
56
+ if (!confirm) {
57
+ tui.info('Cancelled');
58
+ return { success: false, namespace: args.name, message: 'Cancelled' };
59
+ }
60
+ }
61
+
62
+ await kv.deleteNamespace(args.name);
63
+ tui.success(`Namespace ${tui.bold(args.name)} deleted`);
64
+
65
+ return {
66
+ success: true,
67
+ namespace: args.name,
68
+ message: `Namespace ${args.name} deleted`,
69
+ };
70
+ },
71
+ });
72
+
73
+ export default deleteNamespaceSubcommand;
@@ -0,0 +1,51 @@
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 KVDeleteResponseSchema = z.object({
8
+ success: z.boolean().describe('Whether the operation succeeded'),
9
+ namespace: z.string().describe('Namespace name'),
10
+ key: z.string().describe('Key name'),
11
+ durationMs: z.number().describe('Operation duration in milliseconds'),
12
+ });
13
+
14
+ export const deleteSubcommand = createCommand({
15
+ name: 'delete',
16
+ aliases: ['del', 'rm', 'remove'],
17
+ description: 'Delete a key from the keyvalue storage',
18
+ tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
19
+ idempotent: true,
20
+ requires: { auth: true, project: true },
21
+ examples: [
22
+ `${getCommand('kv delete production user:123')} - Delete user data`,
23
+ `${getCommand('kv delete cache session:abc')} - Delete cached session`,
24
+ `${getCommand('kv rm staging cache:homepage')} - Delete homepage cache (using alias)`,
25
+ ],
26
+ schema: {
27
+ args: z.object({
28
+ namespace: z.string().min(1).describe('the namespace name'),
29
+ key: z.string().min(1).describe('the key name'),
30
+ }),
31
+ response: KVDeleteResponseSchema,
32
+ },
33
+
34
+ async handler(ctx) {
35
+ const { args } = ctx;
36
+ const started = Date.now();
37
+ const storage = await createStorageAdapter(ctx);
38
+ await storage.delete(args.namespace, args.key);
39
+ const durationMs = Date.now() - started;
40
+ tui.success(`deleted in ${durationMs.toFixed(1)}ms`);
41
+
42
+ return {
43
+ success: true,
44
+ namespace: args.namespace,
45
+ key: args.key,
46
+ durationMs,
47
+ };
48
+ },
49
+ });
50
+
51
+ export default deleteSubcommand;
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { tryParseJSON } from '../../json';
5
+ import { createStorageAdapter } from './util';
6
+ import { getCommand } from '../../command-prefix';
7
+
8
+ const KVGetResponseSchema = z.object({
9
+ exists: z.boolean().describe('Whether the key exists'),
10
+ data: z.union([z.string(), z.any()]).optional().describe('Value data (string or binary)'),
11
+ contentType: z.string().optional().describe('Content type'),
12
+ });
13
+
14
+ export const getSubcommand = createCommand({
15
+ name: 'get',
16
+ description: 'Get a value from the keyvalue storage',
17
+ tags: ['read-only', 'fast', 'requires-auth'],
18
+ requires: { auth: true, project: true },
19
+ examples: [
20
+ `${getCommand('kv get production user:123')} - Get user data`,
21
+ `${getCommand('kv get cache session:abc')} - Get cached session`,
22
+ `${getCommand('kv get staging cache:homepage')} - Get homepage cache`,
23
+ ],
24
+ schema: {
25
+ args: z.object({
26
+ namespace: z.string().min(1).describe('the namespace name'),
27
+ key: z.string().min(1).describe('the key name'),
28
+ }),
29
+ response: KVGetResponseSchema,
30
+ },
31
+ idempotent: true,
32
+
33
+ async handler(ctx) {
34
+ const { args } = ctx;
35
+ const storage = await createStorageAdapter(ctx);
36
+ const started = Date.now();
37
+ const res = await storage.get(args.namespace, args.key);
38
+ if (res.exists) {
39
+ if (res.data) {
40
+ if (res.contentType?.includes('json')) {
41
+ const val = tryParseJSON(res.data as unknown as string);
42
+ tui.json(val);
43
+ } else if (res.contentType?.includes('text')) {
44
+ console.log(String(res.data));
45
+ } else {
46
+ const b = res.data as ArrayBuffer;
47
+ tui.info(`Read ${b.byteLength} bytes (${res.contentType})`);
48
+ }
49
+ tui.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms (${res.contentType})`);
50
+ } else {
51
+ tui.warning(`${args.key} returned empty data for ${args.namespace}`);
52
+ }
53
+ } else {
54
+ tui.warning(`${args.key} does not exist in ${args.namespace}`);
55
+ }
56
+
57
+ return {
58
+ exists: res.exists,
59
+ data: res.data,
60
+ contentType: res.exists ? res.contentType : undefined,
61
+ };
62
+ },
63
+ });
64
+
65
+ export default getSubcommand;
@@ -0,0 +1,31 @@
1
+ import { createCommand } from '../../types';
2
+ import { createNamespaceSubcommand } from './create-namespace';
3
+ import { deleteSubcommand } from './delete';
4
+ import { deleteNamespaceSubcommand } from './delete-namespace';
5
+ import { getSubcommand } from './get';
6
+ import { keysSubcommand } from './keys';
7
+ import { listNamespacesSubcommand } from './list-namespaces';
8
+ import { replSubcommand } from './repl';
9
+ import { searchSubcommand } from './search';
10
+ import { setSubcommand } from './set';
11
+ import { statsSubcommand } from './stats';
12
+
13
+ export const command = createCommand({
14
+ name: 'keyvalue',
15
+ aliases: ['kv'],
16
+ description: 'Manage keyvalue storage for your projects',
17
+ tags: ['read-only', 'fast', 'requires-auth'],
18
+ subcommands: [
19
+ replSubcommand,
20
+ getSubcommand,
21
+ setSubcommand,
22
+ deleteSubcommand,
23
+ statsSubcommand,
24
+ searchSubcommand,
25
+ keysSubcommand,
26
+ listNamespacesSubcommand,
27
+ createNamespaceSubcommand,
28
+ deleteNamespaceSubcommand,
29
+ ],
30
+ requires: { auth: true, project: true },
31
+ });
@@ -0,0 +1,57 @@
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 KVKeysResponseSchema = z.object({
8
+ namespace: z.string().describe('Namespace name'),
9
+ keys: z.array(z.string()).describe('List of keys in the namespace'),
10
+ });
11
+
12
+ export const keysSubcommand = createCommand({
13
+ name: 'keys',
14
+ aliases: ['ls', 'list'],
15
+ description: 'List all keys in a keyvalue namespace',
16
+ tags: ['read-only', 'slow', 'requires-auth'],
17
+ requires: { auth: true, project: true },
18
+ idempotent: true,
19
+ examples: [
20
+ `${getCommand('kv keys production')} - List all keys in production`,
21
+ `${getCommand('kv ls cache')} - List all cached keys (using alias)`,
22
+ `${getCommand('kv list staging')} - List all staging keys`,
23
+ ],
24
+ schema: {
25
+ args: z.object({
26
+ name: z.string().min(1).describe('the namespace name'),
27
+ }),
28
+ response: KVKeysResponseSchema,
29
+ },
30
+
31
+ async handler(ctx) {
32
+ const { args } = ctx;
33
+ const kv = await createStorageAdapter(ctx);
34
+
35
+ const keys = await kv.getKeys(args.name);
36
+
37
+ if (keys.length === 0) {
38
+ tui.info(`No keys found in namespace ${tui.bold(args.name)}`);
39
+ return {
40
+ namespace: args.name,
41
+ keys: [],
42
+ };
43
+ }
44
+
45
+ tui.info(`Found ${keys.length} key(s) in ${tui.bold(args.name)}:`);
46
+ for (const key of keys) {
47
+ tui.info(` ${key}`);
48
+ }
49
+
50
+ return {
51
+ namespace: args.name,
52
+ keys,
53
+ };
54
+ },
55
+ });
56
+
57
+ export default keysSubcommand;
@@ -0,0 +1,43 @@
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 NamespaceListResponseSchema = z.array(z.string().describe('Namespace name'));
8
+
9
+ export const listNamespacesSubcommand = createCommand({
10
+ name: 'list-namespaces',
11
+ aliases: ['namespaces', 'ns'],
12
+ description: 'List all keyvalue namespaces',
13
+ tags: ['read-only', 'fast', 'requires-auth'],
14
+ requires: { auth: true, project: true },
15
+ examples: [
16
+ `${getCommand('kv list-namespaces')} - List all namespaces`,
17
+ `${getCommand('kv namespaces')} - List namespaces (using alias)`,
18
+ `${getCommand('kv ns')} - List namespaces (short alias)`,
19
+ ],
20
+ schema: {
21
+ response: NamespaceListResponseSchema,
22
+ },
23
+ idempotent: true,
24
+
25
+ async handler(ctx) {
26
+ const storage = await createStorageAdapter(ctx);
27
+ const namespaces = await storage.getNamespaces();
28
+
29
+ if (namespaces.length === 0) {
30
+ tui.info('No namespaces found');
31
+ return [];
32
+ }
33
+
34
+ tui.info(`Found ${namespaces.length} namespace(s):`);
35
+ for (const name of namespaces) {
36
+ tui.arrow(name);
37
+ }
38
+
39
+ return namespaces;
40
+ },
41
+ });
42
+
43
+ export default listNamespacesSubcommand;
@@ -0,0 +1,284 @@
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 keyvalue database',
13
+ tags: ['slow', 'requires-auth'],
14
+ idempotent: false,
15
+ requires: { auth: true, project: true },
16
+ examples: [`${getCommand('kv repl')} - Start interactive KV session`],
17
+
18
+ async handler(ctx) {
19
+ showBanner(undefined, true);
20
+ tui.info('Managing keyvalue 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: 'set',
31
+ aliases: ['put'],
32
+ description: 'Set a value for a namespace',
33
+ schema: {
34
+ args: z.tuple([
35
+ z.string().min(1),
36
+ z.string().min(1),
37
+ z.string().min(1),
38
+ z.coerce.number().min(60).optional(),
39
+ ]),
40
+ argNames: ['namespace', 'key', 'value', 'ttl'],
41
+ },
42
+ handler: async (ctx) => {
43
+ ctx.setProgress('saving');
44
+ const started = Date.now();
45
+ const contentType = isPossiblyJSON(ctx.parsed.args[2]!)
46
+ ? 'application/json'
47
+ : 'text/plain';
48
+ const ttl = ctx.parsed.args.length > 3 ? parseInt(ctx.parsed.args[3]!) : undefined;
49
+ await storage.set(ctx.parsed.args[0]!, ctx.parsed.args[1]!, ctx.parsed.args[2]!, {
50
+ contentType,
51
+ ttl,
52
+ });
53
+ ctx.success(`saved in ${(Date.now() - started).toFixed(1)}ms (${contentType})`);
54
+ },
55
+ },
56
+ {
57
+ name: 'get',
58
+ description: 'Get a value for a namespace and key',
59
+ schema: {
60
+ args: z.tuple([z.string().min(1), z.string().min(1)]),
61
+ argNames: ['namespace', 'key'],
62
+ },
63
+ handler: async (ctx) => {
64
+ ctx.setProgress('fetching');
65
+ const started = Date.now();
66
+ const res = await storage.get(ctx.parsed.args[0]!, ctx.parsed.args[1]!);
67
+ if (res.exists) {
68
+ if (res.data) {
69
+ if (res.contentType?.includes('json')) {
70
+ const val = tryParseJSON(res.data as unknown as string);
71
+ ctx.json(val);
72
+ } else if (res.contentType?.includes('text')) {
73
+ ctx.write(String(res.data));
74
+ } else {
75
+ const b = res.data as ArrayBuffer;
76
+ ctx.info(`Read ${b.byteLength} bytes (${res.contentType})`);
77
+ }
78
+ ctx.success(
79
+ `retrieved in ${(Date.now() - started).toFixed(1)}ms (${res.contentType})`
80
+ );
81
+ } else {
82
+ ctx.warning(
83
+ `${ctx.parsed.args[1]!} returned empty data for ${ctx.parsed.args[0]!}`
84
+ );
85
+ }
86
+ } else {
87
+ ctx.warning(`${ctx.parsed.args[1]!} does not exist in ${ctx.parsed.args[0]!}`);
88
+ }
89
+ },
90
+ },
91
+ {
92
+ name: 'delete',
93
+ aliases: ['rm', 'remove', 'del'],
94
+ description: 'Delete a value for a namespace and key',
95
+ schema: {
96
+ args: z.tuple([z.string().min(1), z.string().min(1)]),
97
+ argNames: ['namespace', 'key'],
98
+ },
99
+ handler: async (ctx) => {
100
+ ctx.setProgress('deleting');
101
+ const started = Date.now();
102
+ await storage.delete(ctx.parsed.args[0]!, ctx.parsed.args[1]!);
103
+ ctx.success(`deleted in ${(Date.now() - started).toFixed(1)}ms`);
104
+ },
105
+ },
106
+ {
107
+ name: 'stats',
108
+ description: 'Get statistics for a namespace or all namespaces',
109
+ schema: {
110
+ args: z.tuple([z.string().min(1).optional()]),
111
+ argNames: ['namespace'],
112
+ },
113
+ handler: async (ctx) => {
114
+ ctx.setProgress('fetching stats');
115
+ const started = Date.now();
116
+ if (ctx.parsed.args.length > 0 && ctx.parsed.args[0]) {
117
+ const stats = await storage.getStats(ctx.parsed.args[0]!);
118
+ ctx.info(`Statistics for ${tui.bold(ctx.parsed.args[0]!)}:`);
119
+ ctx.write(` Keys: ${stats.count}`);
120
+ const sizeMB = (stats.sum / (1024 * 1024)).toFixed(2);
121
+ ctx.write(` Total size: ${sizeMB} MB`);
122
+ if (stats.createdAt) {
123
+ ctx.write(` Created: ${new Date(stats.createdAt).toLocaleString()}`);
124
+ }
125
+ if (stats.lastUsedAt) {
126
+ ctx.write(` Last used: ${new Date(stats.lastUsedAt).toLocaleString()}`);
127
+ }
128
+ } else {
129
+ const allStats = await storage.getAllStats();
130
+ const entries = Object.entries(allStats);
131
+ if (entries.length === 0) {
132
+ ctx.info('No namespaces found');
133
+ } else {
134
+ ctx.info(`Found ${entries.length} namespace(s):`);
135
+ for (const [name, stats] of entries) {
136
+ const sizeMB = (stats.sum / (1024 * 1024)).toFixed(2);
137
+ ctx.write(` ${tui.bold(name)}: ${stats.count} keys, ${sizeMB} MB`);
138
+ }
139
+ }
140
+ }
141
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
142
+ },
143
+ },
144
+ {
145
+ name: 'search',
146
+ description: 'Search for keys matching a keyword',
147
+ schema: {
148
+ args: z.tuple([z.string().min(1), z.string().min(1)]),
149
+ argNames: ['namespace', 'keyword'],
150
+ },
151
+ handler: async (ctx) => {
152
+ ctx.setProgress('searching');
153
+ const started = Date.now();
154
+ const results = await storage.search(ctx.parsed.args[0]!, ctx.parsed.args[1]!);
155
+ const keys = Object.keys(results);
156
+ if (keys.length === 0) {
157
+ ctx.info(
158
+ `No keys found matching ${tui.bold(ctx.parsed.args[1]!)} in ${tui.bold(ctx.parsed.args[0]!)}`
159
+ );
160
+ } else {
161
+ ctx.info(
162
+ `Found ${keys.length} key(s) matching ${tui.bold(ctx.parsed.args[1]!)}:`
163
+ );
164
+ for (const key of keys) {
165
+ const item = results[key];
166
+ if (!item) continue;
167
+ const sizeMB = (item.size / (1024 * 1024)).toFixed(2);
168
+ const date = new Date(item.updated_at).toLocaleString();
169
+ ctx.write(
170
+ ` ${tui.bold(key)}: ${sizeMB} MB, ${item.contentType}, updated ${date}`
171
+ );
172
+ }
173
+ }
174
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
175
+ },
176
+ },
177
+ {
178
+ name: 'keys',
179
+ aliases: ['ls', 'list'],
180
+ description: 'List all keys in a namespace',
181
+ schema: {
182
+ args: z.tuple([z.string().min(1)]),
183
+ argNames: ['namespace'],
184
+ },
185
+ handler: async (ctx) => {
186
+ ctx.setProgress('listing keys');
187
+ const started = Date.now();
188
+ const keys = await storage.getKeys(ctx.parsed.args[0]!);
189
+ if (keys.length === 0) {
190
+ ctx.info(`No keys found in namespace ${tui.bold(ctx.parsed.args[0]!)}`);
191
+ } else {
192
+ ctx.info(`Found ${keys.length} key(s) in ${tui.bold(ctx.parsed.args[0]!)}:`);
193
+ for (const key of keys) {
194
+ ctx.write(` ${key}`);
195
+ }
196
+ }
197
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
198
+ },
199
+ },
200
+ {
201
+ name: 'namespaces',
202
+ aliases: ['ns', 'list-namespaces'],
203
+ description: 'List all namespaces',
204
+ handler: async (ctx) => {
205
+ ctx.setProgress('listing namespaces');
206
+ const started = Date.now();
207
+ const namespaces = await storage.getNamespaces();
208
+ if (namespaces.length === 0) {
209
+ ctx.info('No namespaces found');
210
+ } else {
211
+ ctx.info(`Found ${namespaces.length} namespace(s):`);
212
+ for (const name of namespaces) {
213
+ ctx.write(` ${name}`);
214
+ }
215
+ }
216
+ ctx.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms`);
217
+ },
218
+ },
219
+ {
220
+ name: 'create-namespace',
221
+ aliases: ['create', 'mkns'],
222
+ description: 'Create a new namespace',
223
+ schema: {
224
+ args: z.tuple([z.string().min(1)]),
225
+ argNames: ['namespace'],
226
+ },
227
+ handler: async (ctx) => {
228
+ ctx.setProgress('creating namespace');
229
+ const started = Date.now();
230
+ await storage.createNamespace(ctx.parsed.args[0]!);
231
+ ctx.success(`created in ${(Date.now() - started).toFixed(1)}ms`);
232
+ },
233
+ },
234
+ {
235
+ name: 'delete-namespace',
236
+ aliases: ['rmns', 'delns'],
237
+ description: 'Delete a namespace and all its keys',
238
+ schema: {
239
+ args: z.tuple([z.string().min(1)]),
240
+ argNames: ['namespace'],
241
+ },
242
+ handler: async (ctx) => {
243
+ ctx.warning(
244
+ `This will delete namespace ${tui.bold(ctx.parsed.args[0]!)} and ALL its keys.`
245
+ );
246
+ const confirm = await new Promise<boolean>((resolve) => {
247
+ process.stdout.write('Are you sure? (yes/no): ');
248
+ process.stdin.once('data', (data) => {
249
+ const answer = data.toString().trim().toLowerCase();
250
+ resolve(answer === 'yes' || answer === 'y');
251
+ });
252
+ });
253
+ if (!confirm) {
254
+ ctx.info('Cancelled');
255
+ return;
256
+ }
257
+ ctx.setProgress('deleting namespace');
258
+ const started = Date.now();
259
+ await storage.deleteNamespace(ctx.parsed.args[0]!);
260
+ ctx.success(`deleted in ${(Date.now() - started).toFixed(1)}ms`);
261
+ },
262
+ },
263
+ {
264
+ name: 'exit',
265
+ aliases: ['quit'],
266
+ description: 'Exit the repl',
267
+ handler: async (ctx) => {
268
+ return ctx.exit();
269
+ },
270
+ },
271
+ ];
272
+
273
+ // Start the REPL
274
+ await createRepl({
275
+ name: 'keyvalue',
276
+ prompt: '> ',
277
+ welcome: tui.muted('Type "help" or / for available commands.'),
278
+ exitMessage: 'Goodbye!',
279
+ commands,
280
+ });
281
+ },
282
+ });
283
+
284
+ export default replSubcommand;
@@ -0,0 +1,80 @@
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 KVSearchResponseSchema = z.object({
8
+ namespace: z.string().describe('Namespace name'),
9
+ keyword: z.string().describe('Search keyword used'),
10
+ results: z.array(
11
+ z.object({
12
+ key: z.string().describe('Key name'),
13
+ size: z.number().describe('Size in bytes'),
14
+ contentType: z.string().describe('Content type'),
15
+ updatedAt: z.string().describe('Last updated timestamp'),
16
+ })
17
+ ),
18
+ });
19
+
20
+ export const searchSubcommand = createCommand({
21
+ name: 'search',
22
+ description: 'Search for keys matching a keyword in a keyvalue namespace',
23
+ tags: ['read-only', 'slow', 'requires-auth'],
24
+ requires: { auth: true, project: true },
25
+ idempotent: true,
26
+ examples: [
27
+ `${getCommand('kv search production user')} - Find all user-related keys`,
28
+ `${getCommand('kv search cache session')} - Find all session keys in cache`,
29
+ `${getCommand('kv search staging config')} - Find all config keys`,
30
+ ],
31
+ schema: {
32
+ args: z.object({
33
+ name: z.string().min(1).describe('the namespace name'),
34
+ keyword: z.string().min(1).describe('keyword to use for filtering'),
35
+ }),
36
+ response: KVSearchResponseSchema,
37
+ },
38
+
39
+ async handler(ctx) {
40
+ const { args } = ctx;
41
+ const kv = await createStorageAdapter(ctx);
42
+
43
+ const results = await kv.search(args.name, args.keyword);
44
+ const keys = Object.keys(results);
45
+
46
+ if (keys.length === 0) {
47
+ tui.info(`No keys found matching ${tui.bold(args.keyword)} in ${tui.bold(args.name)}`);
48
+ return {
49
+ namespace: args.name,
50
+ keyword: args.keyword,
51
+ results: [],
52
+ };
53
+ }
54
+
55
+ tui.info(`Found ${keys.length} key(s) matching ${tui.bold(args.keyword)}:`);
56
+ for (const key of keys) {
57
+ const item = results[key];
58
+ if (!item) continue;
59
+ const sizeMB = (item.size / (1024 * 1024)).toFixed(2);
60
+ const date = new Date(item.updated_at).toLocaleString();
61
+ tui.info(` ${tui.bold(key)}: ${sizeMB} MB, ${item.contentType}, updated ${date}`);
62
+ }
63
+
64
+ return {
65
+ namespace: args.name,
66
+ keyword: args.keyword,
67
+ results: keys.map((key) => {
68
+ const item = results[key]!;
69
+ return {
70
+ key,
71
+ size: item.size,
72
+ contentType: item.contentType,
73
+ updatedAt: item.updated_at,
74
+ };
75
+ }),
76
+ };
77
+ },
78
+ });
79
+
80
+ export default searchSubcommand;