@mtndev/cli 1.1.0 → 2.0.1

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 (260) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/cjs/__chunks/VH7oDXx4.cjs +28 -0
  3. package/cjs/commands/aws/ec2/allow-ip/handler.cjs +261 -0
  4. package/cjs/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
  5. package/cjs/commands/aws/ec2/allow-ip/index.cjs +25 -0
  6. package/cjs/commands/aws/ec2/allow-ip/index.d.ts +3 -0
  7. package/cjs/commands/aws/ec2/allow-ip/schema.cjs +33 -0
  8. package/cjs/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
  9. package/cjs/commands/aws/ec2/allow-ip/utils.cjs +379 -0
  10. package/cjs/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
  11. package/cjs/commands/aws/iam/admin-group.cjs +111 -0
  12. package/cjs/commands/aws/iam/admin-group.d.ts +55 -0
  13. package/cjs/commands/aws/iam/break-glass/handler.cjs +127 -0
  14. package/cjs/commands/aws/iam/break-glass/handler.d.ts +7 -0
  15. package/cjs/commands/aws/iam/break-glass/index.cjs +7 -0
  16. package/cjs/commands/aws/iam/break-glass/index.d.ts +2 -0
  17. package/cjs/commands/aws/iam/break-glass/schema.cjs +21 -0
  18. package/cjs/commands/aws/iam/break-glass/schema.d.ts +25 -0
  19. package/cjs/commands/aws/iam/check-admin/handler.cjs +89 -0
  20. package/cjs/commands/aws/iam/check-admin/handler.d.ts +5 -0
  21. package/cjs/commands/aws/iam/check-admin/index.cjs +7 -0
  22. package/cjs/commands/aws/iam/check-admin/index.d.ts +2 -0
  23. package/cjs/commands/aws/iam/check-admin/schema.cjs +19 -0
  24. package/cjs/commands/aws/iam/check-admin/schema.d.ts +19 -0
  25. package/cjs/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
  26. package/cjs/commands/aws/iam/policies/sts-federation.json +11 -0
  27. package/cjs/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
  28. package/cjs/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
  29. package/cjs/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
  30. package/cjs/commands/aws/iam/policies/user-admins-write-list.json +117 -0
  31. package/cjs/commands/aws/iam/remove-admin/handler.cjs +120 -0
  32. package/cjs/commands/aws/iam/remove-admin/handler.d.ts +6 -0
  33. package/cjs/commands/aws/iam/remove-admin/index.cjs +7 -0
  34. package/cjs/commands/aws/iam/remove-admin/index.d.ts +2 -0
  35. package/cjs/commands/aws/iam/remove-admin/schema.cjs +22 -0
  36. package/cjs/commands/aws/iam/remove-admin/schema.d.ts +28 -0
  37. package/cjs/commands/aws/iam/setup-acl/handler.cjs +227 -0
  38. package/cjs/commands/aws/iam/setup-acl/handler.d.ts +6 -0
  39. package/cjs/commands/aws/iam/setup-acl/index.cjs +13 -0
  40. package/cjs/commands/aws/iam/setup-acl/index.d.ts +3 -0
  41. package/cjs/commands/aws/iam/setup-acl/schema.cjs +19 -0
  42. package/cjs/commands/aws/iam/setup-acl/schema.d.ts +24 -0
  43. package/cjs/commands/aws/iam/setup-acl/utils.cjs +305 -0
  44. package/cjs/commands/aws/iam/setup-acl/utils.d.ts +61 -0
  45. package/cjs/commands/aws/iam/users/handler.cjs +59 -0
  46. package/cjs/commands/aws/iam/users/handler.d.ts +5 -0
  47. package/cjs/commands/aws/iam/users/index.cjs +18 -0
  48. package/cjs/commands/aws/iam/users/index.d.ts +3 -0
  49. package/cjs/commands/aws/iam/users/schema.cjs +17 -0
  50. package/cjs/commands/aws/iam/users/schema.d.ts +18 -0
  51. package/cjs/commands/aws/iam/users/utils.cjs +454 -0
  52. package/cjs/commands/aws/iam/users/utils.d.ts +119 -0
  53. package/cjs/commands/aws/index.cjs +223 -0
  54. package/cjs/commands/aws/index.d.ts +14 -0
  55. package/cjs/commands/aws/route53/add-record/handler.cjs +264 -0
  56. package/cjs/commands/aws/route53/add-record/handler.d.ts +36 -0
  57. package/cjs/commands/aws/route53/add-record/index.cjs +26 -0
  58. package/cjs/commands/aws/route53/add-record/index.d.ts +3 -0
  59. package/cjs/commands/aws/route53/add-record/schema.cjs +43 -0
  60. package/cjs/commands/aws/route53/add-record/schema.d.ts +45 -0
  61. package/cjs/commands/aws/route53/add-record/utils.cjs +404 -0
  62. package/cjs/commands/aws/route53/add-record/utils.d.ts +216 -0
  63. package/cjs/commands/aws/route53/remove-record/handler.cjs +114 -0
  64. package/cjs/commands/aws/route53/remove-record/handler.d.ts +6 -0
  65. package/cjs/commands/aws/route53/remove-record/index.cjs +5 -0
  66. package/cjs/commands/aws/route53/remove-record/index.d.ts +2 -0
  67. package/cjs/commands/aws/route53/remove-record/schema.cjs +22 -0
  68. package/cjs/commands/aws/route53/remove-record/schema.d.ts +30 -0
  69. package/cjs/commands/aws/schema.cjs +19 -0
  70. package/cjs/commands/aws/schema.d.ts +13 -0
  71. package/cjs/commands/aws/ses/send-email/handler.cjs +73 -0
  72. package/cjs/commands/aws/ses/send-email/handler.d.ts +5 -0
  73. package/cjs/commands/aws/ses/send-email/index.cjs +9 -0
  74. package/cjs/commands/aws/ses/send-email/index.d.ts +3 -0
  75. package/cjs/commands/aws/ses/send-email/schema.cjs +24 -0
  76. package/cjs/commands/aws/ses/send-email/schema.d.ts +39 -0
  77. package/cjs/commands/aws/ses/send-email/utils.cjs +87 -0
  78. package/cjs/commands/aws/ses/send-email/utils.d.ts +47 -0
  79. package/cjs/commands/aws/ses/smtp-password.cjs +72 -0
  80. package/cjs/commands/aws/ses/smtp-password.d.ts +21 -0
  81. package/cjs/commands/aws/utils/exec-aws.cjs +145 -0
  82. package/cjs/commands/aws/utils/exec-aws.d.ts +79 -0
  83. package/cjs/commands/aws/utils/resolve-policy-path.cjs +15 -0
  84. package/cjs/commands/aws/utils/resolve-policy-path.d.ts +8 -0
  85. package/cjs/commands/aws/utils/resolve-profile.cjs +93 -0
  86. package/cjs/commands/aws/utils/resolve-profile.d.ts +28 -0
  87. package/cjs/commands/aws/utils/tty.cjs +54 -0
  88. package/cjs/commands/aws/utils/tty.d.ts +24 -0
  89. package/cjs/commands/db/create-readonly-user/handler.cjs +254 -0
  90. package/cjs/commands/db/create-readonly-user/handler.d.ts +5 -0
  91. package/cjs/commands/db/create-readonly-user/index.cjs +18 -0
  92. package/cjs/commands/db/create-readonly-user/index.d.ts +3 -0
  93. package/cjs/commands/db/create-readonly-user/mysql.cjs +102 -0
  94. package/cjs/commands/db/create-readonly-user/mysql.d.ts +33 -0
  95. package/cjs/commands/db/create-readonly-user/postgres.cjs +209 -0
  96. package/cjs/commands/db/create-readonly-user/postgres.d.ts +31 -0
  97. package/cjs/commands/db/create-readonly-user/schema.cjs +39 -0
  98. package/cjs/commands/db/create-readonly-user/schema.d.ts +55 -0
  99. package/cjs/commands/db/create-readonly-user/utils.cjs +160 -0
  100. package/cjs/commands/db/create-readonly-user/utils.d.ts +112 -0
  101. package/cjs/commands/db/index.cjs +46 -0
  102. package/cjs/commands/db/index.d.ts +6 -0
  103. package/cjs/commands/doppler/handler.cjs +95 -9
  104. package/cjs/commands/doppler/index.cjs +33 -41
  105. package/cjs/commands/doppler/index.d.ts +1 -0
  106. package/cjs/commands/doppler/schema.cjs +130 -6
  107. package/cjs/commands/doppler/schema.d.ts +233 -17
  108. package/cjs/commands/doppler/set-secret.cjs +378 -0
  109. package/cjs/commands/doppler/set-secret.d.ts +113 -0
  110. package/cjs/commands/doppler/utils/exec.cjs +106 -12
  111. package/cjs/commands/doppler/utils.cjs +412 -30
  112. package/cjs/commands/doppler/utils.d.ts +32 -8
  113. package/cjs/commands/icons/handler.cjs +82 -8
  114. package/cjs/commands/icons/index.cjs +15 -31
  115. package/cjs/commands/icons/schema.cjs +101 -13
  116. package/cjs/commands/icons/schema.d.ts +1 -1
  117. package/cjs/commands/icons/utils.cjs +131 -13
  118. package/cjs/commands/index.cjs +54 -82
  119. package/cjs/commands/schema.cjs +25 -8
  120. package/cjs/index.cjs +3 -4
  121. package/esm/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
  122. package/esm/commands/aws/ec2/allow-ip/handler.js +258 -0
  123. package/esm/commands/aws/ec2/allow-ip/index.d.ts +3 -0
  124. package/esm/commands/aws/ec2/allow-ip/index.js +4 -0
  125. package/esm/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
  126. package/esm/commands/aws/ec2/allow-ip/schema.js +29 -0
  127. package/esm/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
  128. package/esm/commands/aws/ec2/allow-ip/utils.js +363 -0
  129. package/esm/commands/aws/iam/admin-group.d.ts +55 -0
  130. package/esm/commands/aws/iam/admin-group.js +104 -0
  131. package/esm/commands/aws/iam/break-glass/handler.d.ts +7 -0
  132. package/esm/commands/aws/iam/break-glass/handler.js +124 -0
  133. package/esm/commands/aws/iam/break-glass/index.d.ts +2 -0
  134. package/esm/commands/aws/iam/break-glass/index.js +4 -0
  135. package/esm/commands/aws/iam/break-glass/schema.d.ts +25 -0
  136. package/esm/commands/aws/iam/break-glass/schema.js +19 -0
  137. package/esm/commands/aws/iam/check-admin/handler.d.ts +5 -0
  138. package/esm/commands/aws/iam/check-admin/handler.js +86 -0
  139. package/esm/commands/aws/iam/check-admin/index.d.ts +2 -0
  140. package/esm/commands/aws/iam/check-admin/index.js +4 -0
  141. package/esm/commands/aws/iam/check-admin/schema.d.ts +19 -0
  142. package/esm/commands/aws/iam/check-admin/schema.js +17 -0
  143. package/esm/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
  144. package/esm/commands/aws/iam/policies/sts-federation.json +11 -0
  145. package/esm/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
  146. package/esm/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
  147. package/esm/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
  148. package/esm/commands/aws/iam/policies/user-admins-write-list.json +117 -0
  149. package/esm/commands/aws/iam/remove-admin/handler.d.ts +6 -0
  150. package/esm/commands/aws/iam/remove-admin/handler.js +117 -0
  151. package/esm/commands/aws/iam/remove-admin/index.d.ts +2 -0
  152. package/esm/commands/aws/iam/remove-admin/index.js +4 -0
  153. package/esm/commands/aws/iam/remove-admin/schema.d.ts +28 -0
  154. package/esm/commands/aws/iam/remove-admin/schema.js +20 -0
  155. package/esm/commands/aws/iam/setup-acl/handler.d.ts +6 -0
  156. package/esm/commands/aws/iam/setup-acl/handler.js +224 -0
  157. package/esm/commands/aws/iam/setup-acl/index.d.ts +3 -0
  158. package/esm/commands/aws/iam/setup-acl/index.js +4 -0
  159. package/esm/commands/aws/iam/setup-acl/schema.d.ts +24 -0
  160. package/esm/commands/aws/iam/setup-acl/schema.js +18 -0
  161. package/esm/commands/aws/iam/setup-acl/utils.d.ts +61 -0
  162. package/esm/commands/aws/iam/setup-acl/utils.js +298 -0
  163. package/esm/commands/aws/iam/users/handler.d.ts +5 -0
  164. package/esm/commands/aws/iam/users/handler.js +56 -0
  165. package/esm/commands/aws/iam/users/index.d.ts +3 -0
  166. package/esm/commands/aws/iam/users/index.js +4 -0
  167. package/esm/commands/aws/iam/users/schema.d.ts +18 -0
  168. package/esm/commands/aws/iam/users/schema.js +16 -0
  169. package/esm/commands/aws/iam/users/utils.d.ts +119 -0
  170. package/esm/commands/aws/iam/users/utils.js +440 -0
  171. package/esm/commands/aws/index.d.ts +14 -0
  172. package/esm/commands/aws/index.js +142 -0
  173. package/esm/commands/aws/route53/add-record/handler.d.ts +36 -0
  174. package/esm/commands/aws/route53/add-record/handler.js +258 -0
  175. package/esm/commands/aws/route53/add-record/index.d.ts +3 -0
  176. package/esm/commands/aws/route53/add-record/index.js +4 -0
  177. package/esm/commands/aws/route53/add-record/schema.d.ts +45 -0
  178. package/esm/commands/aws/route53/add-record/schema.js +40 -0
  179. package/esm/commands/aws/route53/add-record/utils.d.ts +216 -0
  180. package/esm/commands/aws/route53/add-record/utils.js +389 -0
  181. package/esm/commands/aws/route53/remove-record/handler.d.ts +6 -0
  182. package/esm/commands/aws/route53/remove-record/handler.js +111 -0
  183. package/esm/commands/aws/route53/remove-record/index.d.ts +2 -0
  184. package/esm/commands/aws/route53/remove-record/index.js +3 -0
  185. package/esm/commands/aws/route53/remove-record/schema.d.ts +30 -0
  186. package/esm/commands/aws/route53/remove-record/schema.js +21 -0
  187. package/esm/commands/aws/schema.d.ts +13 -0
  188. package/esm/commands/aws/schema.js +16 -0
  189. package/esm/commands/aws/ses/send-email/handler.d.ts +5 -0
  190. package/esm/commands/aws/ses/send-email/handler.js +70 -0
  191. package/esm/commands/aws/ses/send-email/index.d.ts +3 -0
  192. package/esm/commands/aws/ses/send-email/index.js +4 -0
  193. package/esm/commands/aws/ses/send-email/schema.d.ts +39 -0
  194. package/esm/commands/aws/ses/send-email/schema.js +23 -0
  195. package/esm/commands/aws/ses/send-email/utils.d.ts +47 -0
  196. package/esm/commands/aws/ses/send-email/utils.js +84 -0
  197. package/esm/commands/aws/ses/smtp-password.d.ts +21 -0
  198. package/esm/commands/aws/ses/smtp-password.js +69 -0
  199. package/esm/commands/aws/utils/exec-aws.d.ts +79 -0
  200. package/esm/commands/aws/utils/exec-aws.js +137 -0
  201. package/esm/commands/aws/utils/resolve-policy-path.d.ts +8 -0
  202. package/esm/commands/aws/utils/resolve-policy-path.js +14 -0
  203. package/esm/commands/aws/utils/resolve-profile.d.ts +28 -0
  204. package/esm/commands/aws/utils/resolve-profile.js +88 -0
  205. package/esm/commands/aws/utils/tty.d.ts +24 -0
  206. package/esm/commands/aws/utils/tty.js +51 -0
  207. package/esm/commands/db/create-readonly-user/handler.d.ts +5 -0
  208. package/esm/commands/db/create-readonly-user/handler.js +251 -0
  209. package/esm/commands/db/create-readonly-user/index.d.ts +3 -0
  210. package/esm/commands/db/create-readonly-user/index.js +4 -0
  211. package/esm/commands/db/create-readonly-user/mysql.d.ts +33 -0
  212. package/esm/commands/db/create-readonly-user/mysql.js +98 -0
  213. package/esm/commands/db/create-readonly-user/postgres.d.ts +31 -0
  214. package/esm/commands/db/create-readonly-user/postgres.js +205 -0
  215. package/esm/commands/db/create-readonly-user/schema.d.ts +55 -0
  216. package/esm/commands/db/create-readonly-user/schema.js +35 -0
  217. package/esm/commands/db/create-readonly-user/utils.d.ts +112 -0
  218. package/esm/commands/db/create-readonly-user/utils.js +151 -0
  219. package/esm/commands/db/index.d.ts +6 -0
  220. package/esm/commands/db/index.js +31 -0
  221. package/esm/commands/doppler/handler.js +92 -9
  222. package/esm/commands/doppler/index.d.ts +1 -0
  223. package/esm/commands/doppler/index.js +6 -41
  224. package/esm/commands/doppler/schema.d.ts +233 -17
  225. package/esm/commands/doppler/schema.js +125 -7
  226. package/esm/commands/doppler/set-secret.d.ts +113 -0
  227. package/esm/commands/doppler/set-secret.js +370 -0
  228. package/esm/commands/doppler/utils/exec.js +100 -12
  229. package/esm/commands/doppler/utils.d.ts +32 -8
  230. package/esm/commands/doppler/utils.js +396 -30
  231. package/esm/commands/icons/handler.js +79 -8
  232. package/esm/commands/icons/index.js +4 -31
  233. package/esm/commands/icons/schema.d.ts +1 -1
  234. package/esm/commands/icons/schema.js +97 -15
  235. package/esm/commands/icons/utils.js +125 -13
  236. package/esm/commands/index.js +49 -78
  237. package/esm/commands/schema.js +22 -7
  238. package/esm/index.js +3 -4
  239. package/package.json +12 -2
  240. package/vitest.config.ts +11 -0
  241. package/cjs/__chunks/4GFJUCSP.cjs +0 -52
  242. package/cjs/__chunks/4SFWHCB3.cjs +0 -133
  243. package/cjs/__chunks/B2KFW7T7.cjs +0 -1
  244. package/cjs/__chunks/EMMC33QA.cjs +0 -401
  245. package/cjs/__chunks/INFOPAHN.cjs +0 -117
  246. package/cjs/__chunks/J36P6UNE.cjs +0 -75
  247. package/cjs/__chunks/NIMXXHJQ.cjs +0 -92
  248. package/cjs/__chunks/NSRFG4DF.cjs +0 -100
  249. package/cjs/__chunks/WNJ3ZYLI.cjs +0 -1
  250. package/cjs/__chunks/ZMDIOZPT.cjs +0 -22
  251. package/esm/__chunks/46HGXFME.js +0 -0
  252. package/esm/__chunks/6G4PXXKF.js +0 -75
  253. package/esm/__chunks/6L5RNR7G.js +0 -100
  254. package/esm/__chunks/FL7AILGH.js +0 -133
  255. package/esm/__chunks/HJSXC6HP.js +0 -117
  256. package/esm/__chunks/O262GK4B.js +0 -401
  257. package/esm/__chunks/O3CCA76Q.js +0 -22
  258. package/esm/__chunks/SJXQJ7RS.js +0 -92
  259. package/esm/__chunks/SNCKIAOR.js +0 -0
  260. package/esm/__chunks/X3R5PSWH.js +0 -52
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Zod schema for `aws ses send-email` options.
3
+ */
4
+ export const sendEmailOptionsSchema: z.ZodObject<{
5
+ to: z.ZodString;
6
+ from: z.ZodString;
7
+ subject: z.ZodDefault<z.ZodOptional<z.ZodString>>;
8
+ body: z.ZodOptional<z.ZodString>;
9
+ transport: z.ZodDefault<z.ZodOptional<z.ZodEnum<["api", "smtp"]>>>;
10
+ region: z.ZodDefault<z.ZodOptional<z.ZodString>>;
11
+ profile: z.ZodOptional<z.ZodString>;
12
+ accessKeyId: z.ZodOptional<z.ZodString>;
13
+ dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
14
+ verbose: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ region: string;
17
+ verbose: boolean;
18
+ dryRun: boolean;
19
+ to: string;
20
+ from: string;
21
+ subject: string;
22
+ transport: "api" | "smtp";
23
+ profile?: string | undefined;
24
+ accessKeyId?: string | undefined;
25
+ body?: string | undefined;
26
+ }, {
27
+ to: string;
28
+ from: string;
29
+ profile?: string | undefined;
30
+ region?: string | undefined;
31
+ verbose?: boolean | undefined;
32
+ dryRun?: boolean | undefined;
33
+ accessKeyId?: string | undefined;
34
+ subject?: string | undefined;
35
+ body?: string | undefined;
36
+ transport?: "api" | "smtp" | undefined;
37
+ }>;
38
+ export type SendEmailOptions = z.infer<typeof sendEmailOptionsSchema>;
39
+ import { z } from "zod";
@@ -0,0 +1,23 @@
1
+ import { profileField, verboseField } from "../../schema.js";
2
+ import { z } from "zod";
3
+ //#region src/commands/aws/ses/send-email/schema.js
4
+ /**
5
+ * Zod schema for `aws ses send-email` options.
6
+ */
7
+ const sendEmailOptionsSchema = z.object({
8
+ to: z.string({ required_error: "--to <address> is required" }).describe("Recipient email address"),
9
+ from: z.string({ required_error: "--from <address> is required" }).describe("Verified SES sender email address"),
10
+ subject: z.string().optional().default("MTN CLI test email").describe("Email subject line"),
11
+ body: z.string().optional().describe("Email body (defaults to a timestamp and random token)"),
12
+ transport: z.enum(["api", "smtp"]).optional().default("api").describe("Send via the SES API (default) or SES SMTP"),
13
+ region: z.string().optional().default("us-east-1").describe("AWS region for the SES endpoint"),
14
+ profile: profileField,
15
+ accessKeyId: z.string().optional().describe("IAM access key ID used as the SMTP username (smtp transport only)"),
16
+ dryRun: z.boolean().optional().default(false).describe("Print the email payload without sending"),
17
+ verbose: verboseField
18
+ });
19
+ /**
20
+ * @typedef {z.infer<typeof sendEmailOptionsSchema>} SendEmailOptions
21
+ */
22
+ //#endregion
23
+ export { sendEmailOptionsSchema };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Build a default test-email body with a timestamp and random token.
3
+ * @returns {string}
4
+ */
5
+ export function defaultBody(): string;
6
+ /**
7
+ * Send an email through the SES API using the AWS SDK.
8
+ * @param {{
9
+ * to: string;
10
+ * from: string;
11
+ * subject: string;
12
+ * body: string;
13
+ * region: string;
14
+ * profile?: string;
15
+ * }} params
16
+ * @returns {Promise<string | undefined>} The SES message id
17
+ */
18
+ export function sendViaApi(params: {
19
+ to: string;
20
+ from: string;
21
+ subject: string;
22
+ body: string;
23
+ region: string;
24
+ profile?: string;
25
+ }): Promise<string | undefined>;
26
+ /**
27
+ * Send an email through the SES SMTP endpoint using nodemailer.
28
+ * Credentials come from SES_SMTP_USER/SES_SMTP_PASSWORD, or the password is
29
+ * derived from AWS_SECRET_ACCESS_KEY when only that is available.
30
+ * @param {{
31
+ * to: string;
32
+ * from: string;
33
+ * subject: string;
34
+ * body: string;
35
+ * region: string;
36
+ * accessKeyId?: string;
37
+ * }} params
38
+ * @returns {Promise<string | undefined>} The SMTP message id
39
+ */
40
+ export function sendViaSmtp(params: {
41
+ to: string;
42
+ from: string;
43
+ subject: string;
44
+ body: string;
45
+ region: string;
46
+ accessKeyId?: string;
47
+ }): Promise<string | undefined>;
@@ -0,0 +1,84 @@
1
+ import { calculateSmtpPassword, smtpEndpoint } from "../smtp-password.js";
2
+ import { randomBytes } from "crypto";
3
+ //#region src/commands/aws/ses/send-email/utils.js
4
+ /**
5
+ * Build a default test-email body with a timestamp and random token.
6
+ * @returns {string}
7
+ */
8
+ function defaultBody() {
9
+ return `MTN CLI test email — random token: ${randomBytes(16).toString("hex")} — sent at ${(/* @__PURE__ */ new Date()).toISOString()}`;
10
+ }
11
+ /**
12
+ * Send an email through the SES API using the AWS SDK.
13
+ * @param {{
14
+ * to: string;
15
+ * from: string;
16
+ * subject: string;
17
+ * body: string;
18
+ * region: string;
19
+ * profile?: string;
20
+ * }} params
21
+ * @returns {Promise<string | undefined>} The SES message id
22
+ */
23
+ async function sendViaApi(params) {
24
+ const { to, from, subject, body, region, profile } = params;
25
+ const { SESClient, SendEmailCommand } = await import("@aws-sdk/client-ses");
26
+ const { fromNodeProviderChain } = await import("@aws-sdk/credential-providers");
27
+ const client = new SESClient({
28
+ region,
29
+ credentials: fromNodeProviderChain(profile ? { profile } : {})
30
+ });
31
+ const command = new SendEmailCommand({
32
+ Source: from,
33
+ Destination: { ToAddresses: [to] },
34
+ Message: {
35
+ Subject: { Data: subject },
36
+ Body: { Text: { Data: body } }
37
+ }
38
+ });
39
+ return (await client.send(command)).MessageId;
40
+ }
41
+ /**
42
+ * Send an email through the SES SMTP endpoint using nodemailer.
43
+ * Credentials come from SES_SMTP_USER/SES_SMTP_PASSWORD, or the password is
44
+ * derived from AWS_SECRET_ACCESS_KEY when only that is available.
45
+ * @param {{
46
+ * to: string;
47
+ * from: string;
48
+ * subject: string;
49
+ * body: string;
50
+ * region: string;
51
+ * accessKeyId?: string;
52
+ * }} params
53
+ * @returns {Promise<string | undefined>} The SMTP message id
54
+ */
55
+ async function sendViaSmtp(params) {
56
+ const { to, from, subject, body, region, accessKeyId } = params;
57
+ const user = process.env.SES_SMTP_USER ?? accessKeyId;
58
+ let pass = process.env.SES_SMTP_PASSWORD;
59
+ if (!pass) {
60
+ const secret = process.env.AWS_SECRET_ACCESS_KEY;
61
+ if (secret) pass = calculateSmtpPassword({
62
+ secretAccessKey: secret,
63
+ region
64
+ });
65
+ }
66
+ if (!user || !pass) throw new Error("SMTP transport requires SES_SMTP_USER and SES_SMTP_PASSWORD, or AWS_SECRET_ACCESS_KEY (plus --access-key-id) to derive the SMTP password.");
67
+ const nodemailerModule = await import("nodemailer");
68
+ return (await (nodemailerModule.default ?? nodemailerModule).createTransport({
69
+ host: smtpEndpoint(region),
70
+ port: 587,
71
+ secure: false,
72
+ auth: {
73
+ user,
74
+ pass
75
+ }
76
+ }).sendMail({
77
+ from,
78
+ to,
79
+ subject,
80
+ text: body
81
+ })).messageId;
82
+ }
83
+ //#endregion
84
+ export { defaultBody, sendViaApi, sendViaSmtp };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Derive an SES SMTP password from an IAM secret access key, following the
3
+ * AWS Signature V4 algorithm with SES-specific constants.
4
+ * @see https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html#smtp-credentials-convert
5
+ * @param {{ secretAccessKey: string; region: string }} params
6
+ * @returns {string}
7
+ */
8
+ export function calculateSmtpPassword(params: {
9
+ secretAccessKey: string;
10
+ region: string;
11
+ }): string;
12
+ /**
13
+ * SES SMTP endpoint host for a region.
14
+ * @param {string} region
15
+ * @returns {string}
16
+ */
17
+ export function smtpEndpoint(region: string): string;
18
+ /**
19
+ * AWS regions that expose an SES SMTP endpoint.
20
+ */
21
+ export const SMTP_REGIONS: Set<string>;
@@ -0,0 +1,69 @@
1
+ import { createHmac } from "crypto";
2
+ //#region src/commands/aws/ses/smtp-password.js
3
+ /**
4
+ * AWS regions that expose an SES SMTP endpoint.
5
+ */
6
+ const SMTP_REGIONS = /* @__PURE__ */ new Set([
7
+ "us-east-2",
8
+ "us-east-1",
9
+ "us-west-1",
10
+ "us-west-2",
11
+ "ap-south-1",
12
+ "ap-northeast-2",
13
+ "ap-southeast-1",
14
+ "ap-southeast-2",
15
+ "ap-northeast-1",
16
+ "ca-central-1",
17
+ "eu-central-1",
18
+ "eu-west-1",
19
+ "eu-west-2",
20
+ "eu-west-3",
21
+ "eu-south-1",
22
+ "eu-north-1",
23
+ "sa-east-1",
24
+ "us-gov-west-1",
25
+ "us-gov-east-1",
26
+ "me-south-1",
27
+ "af-south-1",
28
+ "il-central-1"
29
+ ]);
30
+ const DATE = "11111111";
31
+ const SERVICE = "ses";
32
+ const MESSAGE = "SendRawEmail";
33
+ const TERMINAL = "aws4_request";
34
+ const VERSION = 4;
35
+ /**
36
+ * @param {Buffer} key
37
+ * @param {string} message
38
+ * @returns {Buffer}
39
+ */
40
+ function sign(key, message) {
41
+ return createHmac("sha256", key).update(message, "utf8").digest();
42
+ }
43
+ /**
44
+ * Derive an SES SMTP password from an IAM secret access key, following the
45
+ * AWS Signature V4 algorithm with SES-specific constants.
46
+ * @see https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html#smtp-credentials-convert
47
+ * @param {{ secretAccessKey: string; region: string }} params
48
+ * @returns {string}
49
+ */
50
+ function calculateSmtpPassword(params) {
51
+ const { secretAccessKey, region } = params;
52
+ if (!SMTP_REGIONS.has(region)) throw new Error(`calculateSmtpPassword: region "${region}" has no SES SMTP endpoint`);
53
+ let signature = sign(Buffer.from(`AWS4${secretAccessKey}`, "utf8"), DATE);
54
+ signature = sign(signature, region);
55
+ signature = sign(signature, SERVICE);
56
+ signature = sign(signature, TERMINAL);
57
+ signature = sign(signature, MESSAGE);
58
+ return Buffer.concat([Buffer.from([VERSION]), signature]).toString("base64");
59
+ }
60
+ /**
61
+ * SES SMTP endpoint host for a region.
62
+ * @param {string} region
63
+ * @returns {string}
64
+ */
65
+ function smtpEndpoint(region) {
66
+ return `email-smtp.${region}.amazonaws.com`;
67
+ }
68
+ //#endregion
69
+ export { SMTP_REGIONS, calculateSmtpPassword, smtpEndpoint };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Extract the AWS service error code from a CLI stderr string.
3
+ * AWS prints codes in parentheses, e.g. "...(UnauthorizedOperation) when calling...".
4
+ * @param {string} stderr
5
+ * @returns {string | null}
6
+ */
7
+ export function parseAwsErrorCode(stderr: string): string | null;
8
+ /**
9
+ * Verify that the given executables are available on PATH.
10
+ * @param {string[]} commands
11
+ * @throws {Error} If any command is missing
12
+ */
13
+ export function requireCommands(commands: string[]): void;
14
+ /**
15
+ * Run an AWS CLI command using a credential profile.
16
+ * Uses an argument array (no shell) so JSON arguments need no escaping.
17
+ * @param {{
18
+ * profile?: string;
19
+ * region?: string;
20
+ * args: string[];
21
+ * expectJson?: boolean;
22
+ * verbose?: boolean;
23
+ * env?: Record<string, string>;
24
+ * }} params
25
+ * @returns {any} Parsed JSON when expectJson is true, otherwise trimmed stdout string
26
+ */
27
+ export function runAws(params: {
28
+ profile?: string;
29
+ region?: string;
30
+ args: string[];
31
+ expectJson?: boolean;
32
+ verbose?: boolean;
33
+ env?: Record<string, string>;
34
+ }): any;
35
+ /**
36
+ * Run an AWS CLI command through `aws-vault exec` for profiles whose credentials
37
+ * are stored in the OS keychain rather than ~/.aws/credentials.
38
+ * @param {{
39
+ * profile: string;
40
+ * args: string[];
41
+ * duration?: string;
42
+ * expectJson?: boolean;
43
+ * verbose?: boolean;
44
+ * }} params
45
+ * @returns {any}
46
+ */
47
+ export function runAwsVault(params: {
48
+ profile: string;
49
+ args: string[];
50
+ duration?: string;
51
+ expectJson?: boolean;
52
+ verbose?: boolean;
53
+ }): any;
54
+ /**
55
+ * Fetch the caller identity for a profile (account id and ARN).
56
+ * @param {{ profile?: string; region?: string; verbose?: boolean }} params
57
+ * @returns {{ Account: string; Arn: string; UserId: string }}
58
+ */
59
+ export function getCallerIdentity(params: {
60
+ profile?: string;
61
+ region?: string;
62
+ verbose?: boolean;
63
+ }): {
64
+ Account: string;
65
+ Arn: string;
66
+ UserId: string;
67
+ };
68
+ /**
69
+ * Error raised when the AWS CLI (or aws-vault) exits non-zero.
70
+ * The `code` is the AWS service error code parsed from stderr (e.g. "UnauthorizedOperation").
71
+ */
72
+ export class AwsCliError extends Error {
73
+ /**
74
+ * @param {string} message
75
+ * @param {string | null} [code]
76
+ */
77
+ constructor(message: string, code?: string | null);
78
+ code: string | null;
79
+ }
@@ -0,0 +1,137 @@
1
+ import chalk from "chalk";
2
+ import { spawnSync } from "child_process";
3
+ //#region src/commands/aws/utils/exec-aws.js
4
+ /**
5
+ * Error raised when the AWS CLI (or aws-vault) exits non-zero.
6
+ * The `code` is the AWS service error code parsed from stderr (e.g. "UnauthorizedOperation").
7
+ */
8
+ var AwsCliError = class extends Error {
9
+ /**
10
+ * @param {string} message
11
+ * @param {string | null} [code]
12
+ */
13
+ constructor(message, code = null) {
14
+ super(message);
15
+ this.name = "AwsCliError";
16
+ this.code = code;
17
+ }
18
+ };
19
+ /**
20
+ * Extract the AWS service error code from a CLI stderr string.
21
+ * AWS prints codes in parentheses, e.g. "...(UnauthorizedOperation) when calling...".
22
+ * @param {string} stderr
23
+ * @returns {string | null}
24
+ */
25
+ function parseAwsErrorCode(stderr) {
26
+ return /\(([A-Za-z.]+)\)/.exec(stderr)?.[1] ?? null;
27
+ }
28
+ /**
29
+ * Verify that the given executables are available on PATH.
30
+ * @param {string[]} commands
31
+ * @throws {Error} If any command is missing
32
+ */
33
+ function requireCommands(commands) {
34
+ const missing = commands.filter((command) => {
35
+ return spawnSync("which", [command], { stdio: "pipe" }).status !== 0;
36
+ });
37
+ if (missing.length > 0) throw new Error(`Required command(s) not found on PATH: ${missing.join(", ")}`);
38
+ }
39
+ /**
40
+ * Run an AWS CLI command using a credential profile.
41
+ * Uses an argument array (no shell) so JSON arguments need no escaping.
42
+ * @param {{
43
+ * profile?: string;
44
+ * region?: string;
45
+ * args: string[];
46
+ * expectJson?: boolean;
47
+ * verbose?: boolean;
48
+ * env?: Record<string, string>;
49
+ * }} params
50
+ * @returns {any} Parsed JSON when expectJson is true, otherwise trimmed stdout string
51
+ */
52
+ function runAws(params) {
53
+ const { profile, region, args, expectJson = true, verbose = false, env } = params;
54
+ /** @type {string[]} */
55
+ const finalArgs = [];
56
+ if (profile) finalArgs.push("--profile", profile);
57
+ if (region) finalArgs.push("--region", region);
58
+ finalArgs.push("--no-cli-pager", ...args);
59
+ if (expectJson) finalArgs.push("--output", "json");
60
+ return spawnAws("aws", finalArgs, {
61
+ expectJson,
62
+ verbose,
63
+ env
64
+ });
65
+ }
66
+ /**
67
+ * Run an AWS CLI command through `aws-vault exec` for profiles whose credentials
68
+ * are stored in the OS keychain rather than ~/.aws/credentials.
69
+ * @param {{
70
+ * profile: string;
71
+ * args: string[];
72
+ * duration?: string;
73
+ * expectJson?: boolean;
74
+ * verbose?: boolean;
75
+ * }} params
76
+ * @returns {any}
77
+ */
78
+ function runAwsVault(params) {
79
+ const { profile, args, duration = "15m", expectJson = true, verbose = false } = params;
80
+ /** @type {string[]} */
81
+ const awsArgs = ["--no-cli-pager", ...args];
82
+ if (expectJson) awsArgs.push("--output", "json");
83
+ return spawnAws("aws-vault", [
84
+ "exec",
85
+ profile,
86
+ "--duration",
87
+ duration,
88
+ "--",
89
+ "aws",
90
+ ...awsArgs
91
+ ], {
92
+ expectJson,
93
+ verbose
94
+ });
95
+ }
96
+ /**
97
+ * @param {string} command
98
+ * @param {string[]} args
99
+ * @param {{ expectJson: boolean; verbose: boolean; env?: Record<string, string> }} options
100
+ * @returns {any}
101
+ */
102
+ function spawnAws(command, args, options) {
103
+ const { expectJson, verbose, env } = options;
104
+ if (verbose) console.log(chalk.gray(` Executing: ${command} ${args.join(" ")}`));
105
+ const result = spawnSync(command, args, {
106
+ encoding: "utf-8",
107
+ env: env ? {
108
+ ...process.env,
109
+ ...env
110
+ } : process.env
111
+ });
112
+ if (result.error) throw new AwsCliError(result.error.message);
113
+ if (result.status !== 0) {
114
+ const stderr = (result.stderr || result.stdout || "").trim();
115
+ throw new AwsCliError(stderr || `Command failed: ${command} ${args.join(" ")}`, parseAwsErrorCode(stderr));
116
+ }
117
+ const stdout = (result.stdout ?? "").trim();
118
+ if (!expectJson) return stdout;
119
+ if (!stdout) return {};
120
+ return JSON.parse(stdout);
121
+ }
122
+ /**
123
+ * Fetch the caller identity for a profile (account id and ARN).
124
+ * @param {{ profile?: string; region?: string; verbose?: boolean }} params
125
+ * @returns {{ Account: string; Arn: string; UserId: string }}
126
+ */
127
+ function getCallerIdentity(params) {
128
+ const { profile, region, verbose } = params;
129
+ return runAws({
130
+ profile,
131
+ region,
132
+ args: ["sts", "get-caller-identity"],
133
+ verbose
134
+ });
135
+ }
136
+ //#endregion
137
+ export { AwsCliError, getCallerIdentity, parseAwsErrorCode, requireCommands, runAws, runAwsVault };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolve the absolute path to a bundled IAM policy document.
3
+ * Policies live alongside the source in `iam/policies/`, and the CLI runs from
4
+ * source, so this path is valid at runtime.
5
+ * @param {string} fileName - Policy JSON file name (e.g. "user-admins-write-list.json")
6
+ * @returns {string}
7
+ */
8
+ export function resolvePolicyPath(fileName: string): string;
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath } from "url";
2
+ //#region src/commands/aws/utils/resolve-policy-path.js
3
+ /**
4
+ * Resolve the absolute path to a bundled IAM policy document.
5
+ * Policies live alongside the source in `iam/policies/`, and the CLI runs from
6
+ * source, so this path is valid at runtime.
7
+ * @param {string} fileName - Policy JSON file name (e.g. "user-admins-write-list.json")
8
+ * @returns {string}
9
+ */
10
+ function resolvePolicyPath(fileName) {
11
+ return fileURLToPath(new URL(`../iam/policies/${fileName}`, import.meta.url));
12
+ }
13
+ //#endregion
14
+ export { resolvePolicyPath };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Read profile names from ~/.aws/config (honoring AWS_CONFIG_FILE).
3
+ * Handles both `[profile NAME]` entries and a bare `[default]` section.
4
+ * @returns {string[]}
5
+ */
6
+ export function listConfigProfiles(): string[];
7
+ /**
8
+ * Interactively narrow a profile list by substring, then pick one.
9
+ * A single match auto-selects; too many matches asks the user to narrow.
10
+ * @param {string[]} profiles
11
+ * @returns {Promise<string>}
12
+ */
13
+ export function pickProfileInteractive(profiles: string[]): Promise<string>;
14
+ /**
15
+ * Resolve the AWS profile for a single-profile command.
16
+ *
17
+ * Precedence: explicit `--profile`, then `AWS_PROFILE`, then — only when stdin
18
+ * is a TTY — an interactive filter-and-pick over `~/.aws/config` profiles.
19
+ * Returns `undefined` when nothing is set and we cannot prompt (non-interactive
20
+ * shell, or no config profiles), leaving each command's existing "no profile"
21
+ * handling in place.
22
+ * @param {{ profile?: string; interactive?: boolean }} params
23
+ * @returns {Promise<string | undefined>}
24
+ */
25
+ export function resolveProfile(params: {
26
+ profile?: string;
27
+ interactive?: boolean;
28
+ }): Promise<string | undefined>;
@@ -0,0 +1,88 @@
1
+ import { prompt, promptChoice } from "./tty.js";
2
+ import chalk from "chalk";
3
+ import { readFileSync } from "fs";
4
+ import { join } from "path";
5
+ import { homedir } from "os";
6
+ //#region src/commands/aws/utils/resolve-profile.js
7
+ /**
8
+ * Above this many matches we ask the user to narrow the filter rather than
9
+ * printing an unusable wall of numbered options.
10
+ */
11
+ const MAX_LISTED = 30;
12
+ /**
13
+ * Read profile names from ~/.aws/config (honoring AWS_CONFIG_FILE).
14
+ * Handles both `[profile NAME]` entries and a bare `[default]` section.
15
+ * @returns {string[]}
16
+ */
17
+ function listConfigProfiles() {
18
+ const configPath = process.env.AWS_CONFIG_FILE ?? join(homedir(), ".aws", "config");
19
+ let contents;
20
+ try {
21
+ contents = readFileSync(configPath, "utf-8");
22
+ } catch {
23
+ return [];
24
+ }
25
+ /** @type {string[]} */
26
+ const profiles = [];
27
+ const profileRe = /^\[profile\s+(.+?)\]$/;
28
+ for (const rawLine of contents.split("\n")) {
29
+ const line = rawLine.trim();
30
+ const match = profileRe.exec(line);
31
+ if (match?.[1]) profiles.push(match[1].trim());
32
+ else if (line === "[default]") profiles.push("default");
33
+ }
34
+ return profiles;
35
+ }
36
+ /**
37
+ * Interactively narrow a profile list by substring, then pick one.
38
+ * A single match auto-selects; too many matches asks the user to narrow.
39
+ * @param {string[]} profiles
40
+ * @returns {Promise<string>}
41
+ */
42
+ async function pickProfileInteractive(profiles) {
43
+ for (;;) {
44
+ const filter = (await prompt(chalk.cyan("Filter profiles (substring, Enter for all): "))).trim().toLowerCase();
45
+ const matches = filter ? profiles.filter((name) => name.toLowerCase().includes(filter)) : profiles;
46
+ if (matches.length === 0) {
47
+ console.log(chalk.yellow(` No profiles match "${filter}". Try again.`));
48
+ continue;
49
+ }
50
+ if (matches.length === 1) {
51
+ const only = matches[0] ?? "";
52
+ console.log(chalk.green(` → ${only}`));
53
+ return only;
54
+ }
55
+ if (matches.length > MAX_LISTED) {
56
+ console.log(chalk.yellow(` ${matches.length} profiles match — narrow the filter (over ${MAX_LISTED} to list).`));
57
+ continue;
58
+ }
59
+ return matches[await promptChoice({
60
+ message: chalk.cyan("Select a profile:"),
61
+ options: matches
62
+ })] ?? "";
63
+ }
64
+ }
65
+ /**
66
+ * Resolve the AWS profile for a single-profile command.
67
+ *
68
+ * Precedence: explicit `--profile`, then `AWS_PROFILE`, then — only when stdin
69
+ * is a TTY — an interactive filter-and-pick over `~/.aws/config` profiles.
70
+ * Returns `undefined` when nothing is set and we cannot prompt (non-interactive
71
+ * shell, or no config profiles), leaving each command's existing "no profile"
72
+ * handling in place.
73
+ * @param {{ profile?: string; interactive?: boolean }} params
74
+ * @returns {Promise<string | undefined>}
75
+ */
76
+ async function resolveProfile(params) {
77
+ const explicit = params.profile ?? process.env.AWS_PROFILE;
78
+ if (explicit) return explicit;
79
+ if (!(params.interactive ?? Boolean(process.stdin.isTTY))) return;
80
+ const profiles = listConfigProfiles();
81
+ if (profiles.length === 0) return;
82
+ console.log(chalk.yellow("\nNo AWS profile selected (--profile and AWS_PROFILE are unset)."));
83
+ const chosen = await pickProfileInteractive(profiles);
84
+ console.log(chalk.gray(`Using profile "${chosen}" for this command.\n`));
85
+ return chosen;
86
+ }
87
+ //#endregion
88
+ export { listConfigProfiles, pickProfileInteractive, resolveProfile };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Prompt for a single line of input.
3
+ * @param {string} message - Prompt text shown to the user
4
+ * @returns {Promise<string>}
5
+ */
6
+ export function prompt(message: string): Promise<string>;
7
+ /**
8
+ * Prompt the user to pick one option from a numbered list.
9
+ * @param {{ message: string; options: string[] }} params
10
+ * @returns {Promise<number>} Zero-based index of the chosen option
11
+ */
12
+ export function promptChoice(params: {
13
+ message: string;
14
+ options: string[];
15
+ }): Promise<number>;
16
+ /**
17
+ * Prompt a yes/no question.
18
+ * @param {{ message: string; defaultYes?: boolean }} params
19
+ * @returns {Promise<boolean>}
20
+ */
21
+ export function promptYesNo(params: {
22
+ message: string;
23
+ defaultYes?: boolean;
24
+ }): Promise<boolean>;