resend-cli 1.2.1 → 1.2.2

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 (191) hide show
  1. package/biome.json +1 -1
  2. package/bun.lock +0 -3
  3. package/package.json +2 -3
  4. package/src/cli.ts +11 -1
  5. package/src/commands/auth/login.ts +35 -8
  6. package/src/commands/doctor.ts +33 -115
  7. package/src/commands/teams/remove.ts +5 -2
  8. package/src/commands/teams/switch.ts +3 -0
  9. package/src/lib/config.ts +37 -31
  10. package/src/lib/spinner.ts +17 -10
  11. package/src/lib/update-check.ts +172 -0
  12. package/tests/commands/auth/login.test.ts +37 -0
  13. package/tests/lib/config.test.ts +38 -7
  14. package/tests/lib/update-check.test.ts +169 -0
  15. package/.claude/worktrees/emails-list/.claude/settings.local.json +0 -5
  16. package/.claude/worktrees/emails-list/.github/scripts/pr-title-check.js +0 -34
  17. package/.claude/worktrees/emails-list/.github/workflows/ci.yml +0 -32
  18. package/.claude/worktrees/emails-list/.github/workflows/pr-title-check.yml +0 -13
  19. package/.claude/worktrees/emails-list/.github/workflows/release.yml +0 -93
  20. package/.claude/worktrees/emails-list/CHANGELOG.md +0 -31
  21. package/.claude/worktrees/emails-list/LICENSE +0 -21
  22. package/.claude/worktrees/emails-list/README.md +0 -424
  23. package/.claude/worktrees/emails-list/biome.json +0 -36
  24. package/.claude/worktrees/emails-list/bun.lock +0 -76
  25. package/.claude/worktrees/emails-list/bunfig.toml +0 -2
  26. package/.claude/worktrees/emails-list/install.ps1 +0 -140
  27. package/.claude/worktrees/emails-list/install.sh +0 -301
  28. package/.claude/worktrees/emails-list/package.json +0 -43
  29. package/.claude/worktrees/emails-list/renovate.json +0 -6
  30. package/.claude/worktrees/emails-list/src/cli.ts +0 -74
  31. package/.claude/worktrees/emails-list/src/commands/api-keys/create.ts +0 -114
  32. package/.claude/worktrees/emails-list/src/commands/api-keys/delete.ts +0 -47
  33. package/.claude/worktrees/emails-list/src/commands/api-keys/index.ts +0 -26
  34. package/.claude/worktrees/emails-list/src/commands/api-keys/list.ts +0 -35
  35. package/.claude/worktrees/emails-list/src/commands/api-keys/utils.ts +0 -8
  36. package/.claude/worktrees/emails-list/src/commands/auth/index.ts +0 -20
  37. package/.claude/worktrees/emails-list/src/commands/auth/login.ts +0 -207
  38. package/.claude/worktrees/emails-list/src/commands/auth/logout.ts +0 -105
  39. package/.claude/worktrees/emails-list/src/commands/broadcasts/create.ts +0 -196
  40. package/.claude/worktrees/emails-list/src/commands/broadcasts/delete.ts +0 -46
  41. package/.claude/worktrees/emails-list/src/commands/broadcasts/get.ts +0 -59
  42. package/.claude/worktrees/emails-list/src/commands/broadcasts/index.ts +0 -43
  43. package/.claude/worktrees/emails-list/src/commands/broadcasts/list.ts +0 -60
  44. package/.claude/worktrees/emails-list/src/commands/broadcasts/send.ts +0 -56
  45. package/.claude/worktrees/emails-list/src/commands/broadcasts/update.ts +0 -95
  46. package/.claude/worktrees/emails-list/src/commands/broadcasts/utils.ts +0 -35
  47. package/.claude/worktrees/emails-list/src/commands/contact-properties/create.ts +0 -118
  48. package/.claude/worktrees/emails-list/src/commands/contact-properties/delete.ts +0 -48
  49. package/.claude/worktrees/emails-list/src/commands/contact-properties/get.ts +0 -46
  50. package/.claude/worktrees/emails-list/src/commands/contact-properties/index.ts +0 -48
  51. package/.claude/worktrees/emails-list/src/commands/contact-properties/list.ts +0 -68
  52. package/.claude/worktrees/emails-list/src/commands/contact-properties/update.ts +0 -88
  53. package/.claude/worktrees/emails-list/src/commands/contact-properties/utils.ts +0 -17
  54. package/.claude/worktrees/emails-list/src/commands/contacts/add-segment.ts +0 -78
  55. package/.claude/worktrees/emails-list/src/commands/contacts/create.ts +0 -122
  56. package/.claude/worktrees/emails-list/src/commands/contacts/delete.ts +0 -49
  57. package/.claude/worktrees/emails-list/src/commands/contacts/get.ts +0 -53
  58. package/.claude/worktrees/emails-list/src/commands/contacts/index.ts +0 -58
  59. package/.claude/worktrees/emails-list/src/commands/contacts/list.ts +0 -57
  60. package/.claude/worktrees/emails-list/src/commands/contacts/remove-segment.ts +0 -48
  61. package/.claude/worktrees/emails-list/src/commands/contacts/segments.ts +0 -39
  62. package/.claude/worktrees/emails-list/src/commands/contacts/topics.ts +0 -45
  63. package/.claude/worktrees/emails-list/src/commands/contacts/update-topics.ts +0 -90
  64. package/.claude/worktrees/emails-list/src/commands/contacts/update.ts +0 -77
  65. package/.claude/worktrees/emails-list/src/commands/contacts/utils.ts +0 -119
  66. package/.claude/worktrees/emails-list/src/commands/doctor.ts +0 -298
  67. package/.claude/worktrees/emails-list/src/commands/domains/create.ts +0 -83
  68. package/.claude/worktrees/emails-list/src/commands/domains/delete.ts +0 -42
  69. package/.claude/worktrees/emails-list/src/commands/domains/get.ts +0 -47
  70. package/.claude/worktrees/emails-list/src/commands/domains/index.ts +0 -35
  71. package/.claude/worktrees/emails-list/src/commands/domains/list.ts +0 -53
  72. package/.claude/worktrees/emails-list/src/commands/domains/update.ts +0 -75
  73. package/.claude/worktrees/emails-list/src/commands/domains/utils.ts +0 -44
  74. package/.claude/worktrees/emails-list/src/commands/domains/verify.ts +0 -38
  75. package/.claude/worktrees/emails-list/src/commands/emails/batch.ts +0 -140
  76. package/.claude/worktrees/emails-list/src/commands/emails/index.ts +0 -28
  77. package/.claude/worktrees/emails-list/src/commands/emails/list.ts +0 -73
  78. package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachment.ts +0 -55
  79. package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachments.ts +0 -68
  80. package/.claude/worktrees/emails-list/src/commands/emails/receiving/get.ts +0 -58
  81. package/.claude/worktrees/emails-list/src/commands/emails/receiving/index.ts +0 -28
  82. package/.claude/worktrees/emails-list/src/commands/emails/receiving/list.ts +0 -59
  83. package/.claude/worktrees/emails-list/src/commands/emails/receiving/utils.ts +0 -38
  84. package/.claude/worktrees/emails-list/src/commands/emails/send.ts +0 -189
  85. package/.claude/worktrees/emails-list/src/commands/open.ts +0 -24
  86. package/.claude/worktrees/emails-list/src/commands/segments/create.ts +0 -50
  87. package/.claude/worktrees/emails-list/src/commands/segments/delete.ts +0 -47
  88. package/.claude/worktrees/emails-list/src/commands/segments/get.ts +0 -38
  89. package/.claude/worktrees/emails-list/src/commands/segments/index.ts +0 -36
  90. package/.claude/worktrees/emails-list/src/commands/segments/list.ts +0 -58
  91. package/.claude/worktrees/emails-list/src/commands/segments/utils.ts +0 -7
  92. package/.claude/worktrees/emails-list/src/commands/teams/index.ts +0 -10
  93. package/.claude/worktrees/emails-list/src/commands/teams/list.ts +0 -35
  94. package/.claude/worktrees/emails-list/src/commands/teams/remove.ts +0 -83
  95. package/.claude/worktrees/emails-list/src/commands/teams/switch.ts +0 -73
  96. package/.claude/worktrees/emails-list/src/commands/topics/create.ts +0 -73
  97. package/.claude/worktrees/emails-list/src/commands/topics/delete.ts +0 -47
  98. package/.claude/worktrees/emails-list/src/commands/topics/get.ts +0 -42
  99. package/.claude/worktrees/emails-list/src/commands/topics/index.ts +0 -42
  100. package/.claude/worktrees/emails-list/src/commands/topics/list.ts +0 -34
  101. package/.claude/worktrees/emails-list/src/commands/topics/update.ts +0 -59
  102. package/.claude/worktrees/emails-list/src/commands/topics/utils.ts +0 -16
  103. package/.claude/worktrees/emails-list/src/commands/webhooks/create.ts +0 -128
  104. package/.claude/worktrees/emails-list/src/commands/webhooks/delete.ts +0 -49
  105. package/.claude/worktrees/emails-list/src/commands/webhooks/get.ts +0 -42
  106. package/.claude/worktrees/emails-list/src/commands/webhooks/index.ts +0 -44
  107. package/.claude/worktrees/emails-list/src/commands/webhooks/list.ts +0 -55
  108. package/.claude/worktrees/emails-list/src/commands/webhooks/update.ts +0 -83
  109. package/.claude/worktrees/emails-list/src/commands/webhooks/utils.ts +0 -36
  110. package/.claude/worktrees/emails-list/src/commands/whoami.ts +0 -71
  111. package/.claude/worktrees/emails-list/src/lib/actions.ts +0 -157
  112. package/.claude/worktrees/emails-list/src/lib/client.ts +0 -34
  113. package/.claude/worktrees/emails-list/src/lib/config.ts +0 -211
  114. package/.claude/worktrees/emails-list/src/lib/files.ts +0 -15
  115. package/.claude/worktrees/emails-list/src/lib/help-text.ts +0 -38
  116. package/.claude/worktrees/emails-list/src/lib/output.ts +0 -54
  117. package/.claude/worktrees/emails-list/src/lib/pagination.ts +0 -36
  118. package/.claude/worktrees/emails-list/src/lib/prompts.ts +0 -149
  119. package/.claude/worktrees/emails-list/src/lib/spinner.ts +0 -93
  120. package/.claude/worktrees/emails-list/src/lib/table.ts +0 -57
  121. package/.claude/worktrees/emails-list/src/lib/tty.ts +0 -28
  122. package/.claude/worktrees/emails-list/src/lib/version.ts +0 -4
  123. package/.claude/worktrees/emails-list/tests/commands/api-keys/create.test.ts +0 -195
  124. package/.claude/worktrees/emails-list/tests/commands/api-keys/delete.test.ts +0 -156
  125. package/.claude/worktrees/emails-list/tests/commands/api-keys/list.test.ts +0 -133
  126. package/.claude/worktrees/emails-list/tests/commands/auth/login.test.ts +0 -119
  127. package/.claude/worktrees/emails-list/tests/commands/auth/logout.test.ts +0 -146
  128. package/.claude/worktrees/emails-list/tests/commands/broadcasts/create.test.ts +0 -447
  129. package/.claude/worktrees/emails-list/tests/commands/broadcasts/delete.test.ts +0 -182
  130. package/.claude/worktrees/emails-list/tests/commands/broadcasts/get.test.ts +0 -146
  131. package/.claude/worktrees/emails-list/tests/commands/broadcasts/list.test.ts +0 -196
  132. package/.claude/worktrees/emails-list/tests/commands/broadcasts/send.test.ts +0 -161
  133. package/.claude/worktrees/emails-list/tests/commands/broadcasts/update.test.ts +0 -283
  134. package/.claude/worktrees/emails-list/tests/commands/contact-properties/create.test.ts +0 -250
  135. package/.claude/worktrees/emails-list/tests/commands/contact-properties/delete.test.ts +0 -183
  136. package/.claude/worktrees/emails-list/tests/commands/contact-properties/get.test.ts +0 -144
  137. package/.claude/worktrees/emails-list/tests/commands/contact-properties/list.test.ts +0 -180
  138. package/.claude/worktrees/emails-list/tests/commands/contact-properties/update.test.ts +0 -216
  139. package/.claude/worktrees/emails-list/tests/commands/contacts/add-segment.test.ts +0 -188
  140. package/.claude/worktrees/emails-list/tests/commands/contacts/create.test.ts +0 -270
  141. package/.claude/worktrees/emails-list/tests/commands/contacts/delete.test.ts +0 -192
  142. package/.claude/worktrees/emails-list/tests/commands/contacts/get.test.ts +0 -148
  143. package/.claude/worktrees/emails-list/tests/commands/contacts/list.test.ts +0 -175
  144. package/.claude/worktrees/emails-list/tests/commands/contacts/remove-segment.test.ts +0 -166
  145. package/.claude/worktrees/emails-list/tests/commands/contacts/segments.test.ts +0 -167
  146. package/.claude/worktrees/emails-list/tests/commands/contacts/topics.test.ts +0 -163
  147. package/.claude/worktrees/emails-list/tests/commands/contacts/update-topics.test.ts +0 -247
  148. package/.claude/worktrees/emails-list/tests/commands/contacts/update.test.ts +0 -205
  149. package/.claude/worktrees/emails-list/tests/commands/doctor.test.ts +0 -165
  150. package/.claude/worktrees/emails-list/tests/commands/domains/create.test.ts +0 -192
  151. package/.claude/worktrees/emails-list/tests/commands/domains/delete.test.ts +0 -156
  152. package/.claude/worktrees/emails-list/tests/commands/domains/get.test.ts +0 -137
  153. package/.claude/worktrees/emails-list/tests/commands/domains/list.test.ts +0 -164
  154. package/.claude/worktrees/emails-list/tests/commands/domains/update.test.ts +0 -223
  155. package/.claude/worktrees/emails-list/tests/commands/domains/verify.test.ts +0 -117
  156. package/.claude/worktrees/emails-list/tests/commands/emails/batch.test.ts +0 -313
  157. package/.claude/worktrees/emails-list/tests/commands/emails/list.test.ts +0 -196
  158. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachment.test.ts +0 -140
  159. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachments.test.ts +0 -168
  160. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/get.test.ts +0 -140
  161. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/list.test.ts +0 -181
  162. package/.claude/worktrees/emails-list/tests/commands/emails/send.test.ts +0 -309
  163. package/.claude/worktrees/emails-list/tests/commands/segments/create.test.ts +0 -163
  164. package/.claude/worktrees/emails-list/tests/commands/segments/delete.test.ts +0 -182
  165. package/.claude/worktrees/emails-list/tests/commands/segments/get.test.ts +0 -137
  166. package/.claude/worktrees/emails-list/tests/commands/segments/list.test.ts +0 -173
  167. package/.claude/worktrees/emails-list/tests/commands/teams/list.test.ts +0 -63
  168. package/.claude/worktrees/emails-list/tests/commands/teams/remove.test.ts +0 -103
  169. package/.claude/worktrees/emails-list/tests/commands/teams/switch.test.ts +0 -96
  170. package/.claude/worktrees/emails-list/tests/commands/topics/create.test.ts +0 -191
  171. package/.claude/worktrees/emails-list/tests/commands/topics/delete.test.ts +0 -156
  172. package/.claude/worktrees/emails-list/tests/commands/topics/get.test.ts +0 -125
  173. package/.claude/worktrees/emails-list/tests/commands/topics/list.test.ts +0 -124
  174. package/.claude/worktrees/emails-list/tests/commands/topics/update.test.ts +0 -177
  175. package/.claude/worktrees/emails-list/tests/commands/webhooks/create.test.ts +0 -224
  176. package/.claude/worktrees/emails-list/tests/commands/webhooks/delete.test.ts +0 -156
  177. package/.claude/worktrees/emails-list/tests/commands/webhooks/get.test.ts +0 -125
  178. package/.claude/worktrees/emails-list/tests/commands/webhooks/list.test.ts +0 -177
  179. package/.claude/worktrees/emails-list/tests/commands/webhooks/update.test.ts +0 -206
  180. package/.claude/worktrees/emails-list/tests/commands/whoami.test.ts +0 -99
  181. package/.claude/worktrees/emails-list/tests/helpers.ts +0 -93
  182. package/.claude/worktrees/emails-list/tests/lib/client.test.ts +0 -71
  183. package/.claude/worktrees/emails-list/tests/lib/config.test.ts +0 -414
  184. package/.claude/worktrees/emails-list/tests/lib/files.test.ts +0 -65
  185. package/.claude/worktrees/emails-list/tests/lib/help-text.test.ts +0 -97
  186. package/.claude/worktrees/emails-list/tests/lib/output.test.ts +0 -127
  187. package/.claude/worktrees/emails-list/tests/lib/prompts.test.ts +0 -178
  188. package/.claude/worktrees/emails-list/tests/lib/spinner.test.ts +0 -146
  189. package/.claude/worktrees/emails-list/tests/lib/table.test.ts +0 -63
  190. package/.claude/worktrees/emails-list/tests/lib/tty.test.ts +0 -85
  191. package/.claude/worktrees/emails-list/tsconfig.json +0 -14
@@ -1,55 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import type { GlobalOpts } from '../../../lib/client';
3
- import { requireClient } from '../../../lib/client';
4
- import { buildHelpText } from '../../../lib/help-text';
5
- import { outputResult } from '../../../lib/output';
6
- import { withSpinner } from '../../../lib/spinner';
7
- import { isInteractive } from '../../../lib/tty';
8
-
9
- export const getAttachmentCommand = new Command('attachment')
10
- .description('Retrieve a single attachment from a received (inbound) email')
11
- .argument('<emailId>', 'Received email UUID')
12
- .argument('<attachmentId>', 'Attachment UUID')
13
- .addHelpText(
14
- 'after',
15
- buildHelpText({
16
- context:
17
- 'The download_url is a signed URL that expires in ~1 hour. Download the file directly:\n resend emails receiving attachment <emailId> <attachmentId> --json | jq -r .download_url | xargs curl -O',
18
- output:
19
- ' {"object":"attachment","id":"<uuid>","filename":"invoice.pdf","size":51200,"content_type":"application/pdf","content_disposition":"attachment","content_id":null,"download_url":"<signed-url>","expires_at":"<iso-date>"}',
20
- errorCodes: ['auth_error', 'fetch_error'],
21
- examples: [
22
- 'resend emails receiving attachment <email-id> <attachment-id>',
23
- 'resend emails receiving attachment <email-id> <attachment-id> --json',
24
- ],
25
- }),
26
- )
27
- .action(async (emailId, attachmentId, _opts, cmd) => {
28
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
29
- const resend = requireClient(globalOpts);
30
-
31
- const data = await withSpinner(
32
- {
33
- loading: 'Fetching attachment...',
34
- success: 'Attachment fetched',
35
- fail: 'Failed to fetch attachment',
36
- },
37
- () =>
38
- resend.emails.receiving.attachments.get({ emailId, id: attachmentId }),
39
- 'fetch_error',
40
- globalOpts,
41
- );
42
-
43
- if (!globalOpts.json && isInteractive()) {
44
- const d = data;
45
- console.log(`\n${d.filename ?? '(unnamed)'}`);
46
- console.log(`ID: ${d.id}`);
47
- console.log(`Content-Type: ${d.content_type}`);
48
- console.log(`Size: ${d.size} bytes`);
49
- console.log(`Disposition: ${d.content_disposition}`);
50
- console.log(`Download URL: ${d.download_url}`);
51
- console.log(`Expires: ${d.expires_at}`);
52
- } else {
53
- outputResult(data, { json: globalOpts.json });
54
- }
55
- });
@@ -1,68 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runList } from '../../../lib/actions';
3
- import type { GlobalOpts } from '../../../lib/client';
4
- import { buildHelpText } from '../../../lib/help-text';
5
- import {
6
- buildPaginationOpts,
7
- parseLimitOpt,
8
- printPaginationHint,
9
- } from '../../../lib/pagination';
10
- import { renderAttachmentsTable } from './utils';
11
-
12
- export const listAttachmentsCommand = new Command('attachments')
13
- .description('List attachments on a received (inbound) email')
14
- .argument('<emailId>', 'Received email UUID')
15
- .option(
16
- '--limit <n>',
17
- 'Maximum number of attachments to return (1-100)',
18
- '10',
19
- )
20
- .option(
21
- '--after <cursor>',
22
- 'Return attachments after this cursor (next page)',
23
- )
24
- .option(
25
- '--before <cursor>',
26
- 'Return attachments before this cursor (previous page)',
27
- )
28
- .addHelpText(
29
- 'after',
30
- buildHelpText({
31
- context:
32
- 'Each attachment has a download_url (signed, expires ~1 hour).\nUse the attachment sub-command to retrieve a single attachment with its download URL:\n resend emails receiving attachment <emailId> <attachmentId>\n\ncontent_disposition: "inline" means the attachment is embedded in the HTML body (e.g. an image).\ncontent_disposition: "attachment" means it is a standalone file download.',
33
- output:
34
- ' {"object":"list","has_more":false,"data":[{"id":"<uuid>","filename":"invoice.pdf","size":51200,"content_type":"application/pdf","content_disposition":"attachment","content_id":null,"download_url":"<url>","expires_at":"<iso-date>"}]}',
35
- errorCodes: ['auth_error', 'invalid_limit', 'list_error'],
36
- examples: [
37
- 'resend emails receiving attachments <email-id>',
38
- 'resend emails receiving attachments <email-id> --json',
39
- 'resend emails receiving attachments <email-id> --limit 25 --json',
40
- ],
41
- }),
42
- )
43
- .action(async (emailId, opts, cmd) => {
44
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
45
-
46
- const limit = parseLimitOpt(opts.limit, globalOpts);
47
- const paginationOpts = buildPaginationOpts(limit, opts.after, opts.before);
48
-
49
- await runList(
50
- {
51
- spinner: {
52
- loading: 'Fetching attachments...',
53
- success: 'Attachments fetched',
54
- fail: 'Failed to list attachments',
55
- },
56
- sdkCall: (resend) =>
57
- resend.emails.receiving.attachments.list({
58
- emailId,
59
- ...paginationOpts,
60
- }),
61
- onInteractive: (list) => {
62
- console.log(renderAttachmentsTable(list.data));
63
- printPaginationHint(list);
64
- },
65
- },
66
- globalOpts,
67
- );
68
- });
@@ -1,58 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runGet } from '../../../lib/actions';
3
- import type { GlobalOpts } from '../../../lib/client';
4
- import { buildHelpText } from '../../../lib/help-text';
5
-
6
- export const getReceivingCommand = new Command('get')
7
- .description(
8
- 'Retrieve a single received (inbound) email with full details including HTML, text, and headers',
9
- )
10
- .argument('<id>', 'Received email UUID')
11
- .addHelpText(
12
- 'after',
13
- buildHelpText({
14
- context:
15
- 'The raw.download_url field is a signed URL (expires ~1 hour) containing the full RFC 2822\nMIME message. Pipe it to curl to save the original email:\n resend emails receiving get <id> --json | jq -r .raw.download_url | xargs curl > email.eml\n\nAttachments are listed in the attachments array. Use the attachments sub-command to get\ndownload URLs:\n resend emails receiving attachments <id>',
16
- output:
17
- ' {"object":"email","id":"<uuid>","to":["inbox@yourdomain.com"],"from":"sender@external.com","subject":"Hello","html":"<p>Hello!</p>","text":"Hello!","headers":{"x-mailer":"..."},"message_id":"<str>","bcc":[],"cc":[],"reply_to":[],"raw":{"download_url":"<url>","expires_at":"<iso-date>"},"attachments":[]}',
18
- errorCodes: ['auth_error', 'fetch_error'],
19
- examples: [
20
- 'resend emails receiving get <email-id>',
21
- 'resend emails receiving get <email-id> --json',
22
- ],
23
- }),
24
- )
25
- .action(async (id, _opts, cmd) => {
26
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
27
- await runGet(
28
- {
29
- spinner: {
30
- loading: 'Fetching received email...',
31
- success: 'Received email fetched',
32
- fail: 'Failed to fetch received email',
33
- },
34
- sdkCall: (resend) => resend.emails.receiving.get(id),
35
- onInteractive: (data) => {
36
- console.log(`\nFrom: ${data.from}`);
37
- console.log(`To: ${data.to.join(', ')}`);
38
- console.log(`Subject: ${data.subject}`);
39
- console.log(`Date: ${data.created_at}`);
40
- if (data.attachments.length > 0) {
41
- console.log(`Files: ${data.attachments.length} attachment(s)`);
42
- }
43
- if (data.text) {
44
- const snippet =
45
- data.text.length > 200
46
- ? `${data.text.slice(0, 197)}...`
47
- : data.text;
48
- console.log(`\n${snippet}`);
49
- } else if (data.html) {
50
- console.log(
51
- '\n(HTML body only — use --json to view or pipe to a browser)',
52
- );
53
- }
54
- },
55
- },
56
- globalOpts,
57
- );
58
- });
@@ -1,28 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { buildHelpText } from '../../../lib/help-text';
3
- import { getAttachmentCommand } from './attachment';
4
- import { listAttachmentsCommand } from './attachments';
5
- import { getReceivingCommand } from './get';
6
- import { listReceivingCommand } from './list';
7
-
8
- export const receivingCommand = new Command('receiving')
9
- .description(
10
- 'Manage received (inbound) emails — requires domain receiving to be enabled',
11
- )
12
- .addHelpText(
13
- 'after',
14
- buildHelpText({
15
- context:
16
- 'Receiving must be enabled on the domain first:\n resend domains update <id> --receiving enabled',
17
- examples: [
18
- 'resend emails receiving list',
19
- 'resend emails receiving get <email-id>',
20
- 'resend emails receiving attachments <email-id>',
21
- 'resend emails receiving attachment <email-id> <attachment-id>',
22
- ],
23
- }),
24
- )
25
- .addCommand(listReceivingCommand, { isDefault: true })
26
- .addCommand(getReceivingCommand)
27
- .addCommand(listAttachmentsCommand)
28
- .addCommand(getAttachmentCommand);
@@ -1,59 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runList } from '../../../lib/actions';
3
- import type { GlobalOpts } from '../../../lib/client';
4
- import { buildHelpText } from '../../../lib/help-text';
5
- import {
6
- buildPaginationOpts,
7
- parseLimitOpt,
8
- printPaginationHint,
9
- } from '../../../lib/pagination';
10
- import { renderReceivingEmailsTable } from './utils';
11
-
12
- export const listReceivingCommand = new Command('list')
13
- .alias('ls')
14
- .description(
15
- 'List received (inbound) emails for domains with receiving enabled',
16
- )
17
- .option('--limit <n>', 'Maximum number of emails to return (1-100)', '10')
18
- .option('--after <cursor>', 'Return emails after this cursor (next page)')
19
- .option(
20
- '--before <cursor>',
21
- 'Return emails before this cursor (previous page)',
22
- )
23
- .addHelpText(
24
- 'after',
25
- buildHelpText({
26
- context:
27
- 'Receiving must be enabled on the domain first:\n resend domains update <id> --receiving enabled\n\nPagination: use --after or --before with a received email ID as the cursor.\nOnly one of --after or --before may be used at a time.\nThe response includes has_more: true when additional pages exist.',
28
- output:
29
- ' {"object":"list","has_more":false,"data":[{"id":"<uuid>","to":["inbox@yourdomain.com"],"from":"sender@external.com","subject":"Hello","created_at":"<iso-date>","message_id":"<str>","bcc":null,"cc":null,"reply_to":null,"attachments":[]}]}',
30
- errorCodes: ['auth_error', 'invalid_limit', 'list_error'],
31
- examples: [
32
- 'resend emails receiving list',
33
- 'resend emails receiving list --limit 25 --json',
34
- 'resend emails receiving list --after <email-id> --json',
35
- ],
36
- }),
37
- )
38
- .action(async (opts, cmd) => {
39
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
40
-
41
- const limit = parseLimitOpt(opts.limit, globalOpts);
42
- const paginationOpts = buildPaginationOpts(limit, opts.after, opts.before);
43
-
44
- await runList(
45
- {
46
- spinner: {
47
- loading: 'Fetching received emails...',
48
- success: 'Received emails fetched',
49
- fail: 'Failed to list received emails',
50
- },
51
- sdkCall: (resend) => resend.emails.receiving.list(paginationOpts),
52
- onInteractive: (list) => {
53
- console.log(renderReceivingEmailsTable(list.data));
54
- printPaginationHint(list);
55
- },
56
- },
57
- globalOpts,
58
- );
59
- });
@@ -1,38 +0,0 @@
1
- import type {
2
- ListAttachmentsResponseSuccess,
3
- ListReceivingEmail,
4
- } from 'resend';
5
- import { renderTable } from '../../../lib/table';
6
-
7
- export function renderReceivingEmailsTable(
8
- emails: ListReceivingEmail[],
9
- ): string {
10
- const rows = emails.map((e) => {
11
- const to = e.to.join(', ');
12
- const toStr = to.length > 40 ? `${to.slice(0, 37)}...` : to;
13
- const subject =
14
- e.subject.length > 50 ? `${e.subject.slice(0, 47)}...` : e.subject;
15
- return [e.from, toStr, subject, e.created_at, e.id];
16
- });
17
- return renderTable(
18
- ['From', 'To', 'Subject', 'Created At', 'ID'],
19
- rows,
20
- '(no received emails)',
21
- );
22
- }
23
-
24
- export function renderAttachmentsTable(
25
- attachments: ListAttachmentsResponseSuccess['data'],
26
- ): string {
27
- const rows = attachments.map((a) => [
28
- a.filename ?? '(unnamed)',
29
- a.content_type,
30
- String(a.size),
31
- a.id,
32
- ]);
33
- return renderTable(
34
- ['Filename', 'Content-Type', 'Size (bytes)', 'ID'],
35
- rows,
36
- '(no attachments)',
37
- );
38
- }
@@ -1,189 +0,0 @@
1
- import * as p from '@clack/prompts';
2
- import { Command } from '@commander-js/extra-typings';
3
- import type { Resend } from 'resend';
4
- import type { GlobalOpts } from '../../lib/client';
5
- import { requireClient } from '../../lib/client';
6
- import { readFile } from '../../lib/files';
7
- import { buildHelpText } from '../../lib/help-text';
8
- import { outputResult } from '../../lib/output';
9
- import {
10
- cancelAndExit,
11
- promptForMissing,
12
- requireText,
13
- } from '../../lib/prompts';
14
- import { withSpinner } from '../../lib/spinner';
15
- import { isInteractive } from '../../lib/tty';
16
-
17
- export async function fetchVerifiedDomains(resend: Resend): Promise<string[]> {
18
- try {
19
- const { data, error } = await resend.domains.list();
20
- if (error || !data) {
21
- return [];
22
- }
23
- return data.data
24
- .filter(
25
- (d) => d.status === 'verified' && d.capabilities.sending === 'enabled',
26
- )
27
- .map((d) => d.name);
28
- } catch {
29
- return [];
30
- }
31
- }
32
-
33
- const FROM_PREFIXES = ['noreply', 'hello', 'hi', 'info', 'support', 'team'];
34
-
35
- async function promptForFromAddress(domains: string[]): Promise<string> {
36
- let domain: string;
37
- if (domains.length === 1) {
38
- domain = domains[0];
39
- } else {
40
- const result = await p.select({
41
- message: 'Select a verified domain',
42
- options: domains.map((d) => ({ value: d, label: d })),
43
- });
44
- if (p.isCancel(result)) {
45
- cancelAndExit('Send cancelled.');
46
- }
47
- domain = result;
48
- }
49
-
50
- const options: Array<{ value: string | null; label: string }> =
51
- FROM_PREFIXES.map((prefix) => ({
52
- value: `${prefix}@${domain}`,
53
- label: `${prefix}@${domain}`,
54
- }));
55
- options.push({ value: null, label: 'Custom address...' });
56
-
57
- const result = await p.select({
58
- message: `From address (@${domain})`,
59
- options,
60
- });
61
- if (p.isCancel(result)) {
62
- cancelAndExit('Send cancelled.');
63
- }
64
-
65
- if (result === null) {
66
- const custom = await p.text({
67
- message: 'From address',
68
- placeholder: `you@${domain}`,
69
- validate: (v) =>
70
- !v || !v.includes('@') ? 'Enter a valid email address' : undefined,
71
- });
72
- if (p.isCancel(custom)) {
73
- cancelAndExit('Send cancelled.');
74
- }
75
- return custom;
76
- }
77
-
78
- return result;
79
- }
80
-
81
- export const sendCommand = new Command('send')
82
- .description('Send an email')
83
- .option('--from <address>', 'Sender address (required)')
84
- .option('--to <addresses...>', 'Recipient address(es) (required)')
85
- .option('--subject <subject>', 'Email subject (required)')
86
- .option('--html <html>', 'HTML body')
87
- .option('--html-file <path>', 'Path to an HTML file for the body')
88
- .option('--text <text>', 'Plain-text body')
89
- .option('--cc <addresses...>', 'CC recipients')
90
- .option('--bcc <addresses...>', 'BCC recipients')
91
- .option('--reply-to <address>', 'Reply-to address')
92
- .addHelpText(
93
- 'after',
94
- buildHelpText({
95
- context:
96
- 'Required: --from, --to, --subject, and one of --text | --html | --html-file',
97
- output: ' {"id":"<email-id>"}',
98
- errorCodes: ['auth_error', 'missing_body', 'send_error'],
99
- examples: [
100
- 'resend emails send --from you@domain.com --to user@example.com --subject "Hello" --text "Hi"',
101
- 'resend emails send --from you@domain.com --to a@example.com --to b@example.com --subject "Hi" --html "<b>Hi</b>" --json',
102
- 'resend emails send --from you@domain.com --to user@example.com --subject "Hi" --html-file ./email.html --json',
103
- 'RESEND_API_KEY=re_123 resend emails send --from you@domain.com --to user@example.com --subject "Hi" --text "Hi"',
104
- ],
105
- }),
106
- )
107
- .action(async (opts, cmd) => {
108
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
109
-
110
- const resend = requireClient(globalOpts);
111
-
112
- // Only fetch verified domains in interactive mode — non-interactive
113
- // callers (CI, agents, scripts) must pass --from explicitly.
114
- let fromAddress = opts.from;
115
- if (!fromAddress && isInteractive()) {
116
- const domains = await fetchVerifiedDomains(resend);
117
- if (domains.length > 0) {
118
- fromAddress = await promptForFromAddress(domains);
119
- }
120
- }
121
-
122
- const filled = await promptForMissing(
123
- { from: fromAddress, to: opts.to?.[0], subject: opts.subject },
124
- [
125
- {
126
- flag: 'from',
127
- message: 'From address',
128
- placeholder: 'you@example.com',
129
- },
130
- {
131
- flag: 'to',
132
- message: 'To address',
133
- placeholder: 'recipient@example.com',
134
- },
135
- { flag: 'subject', message: 'Subject', placeholder: 'Hello!' },
136
- ],
137
- globalOpts,
138
- );
139
-
140
- let html = opts.html;
141
- const text = opts.text;
142
-
143
- if (opts.htmlFile) {
144
- html = readFile(opts.htmlFile, globalOpts);
145
- }
146
-
147
- let body: string | undefined = text;
148
- if (!html && !text) {
149
- body = await requireText(
150
- undefined,
151
- {
152
- message: 'Email body (plain text)',
153
- placeholder: 'Type your message...',
154
- },
155
- {
156
- message: 'Missing email body. Provide --html, --html-file, or --text',
157
- code: 'missing_body',
158
- },
159
- globalOpts,
160
- );
161
- }
162
-
163
- const toAddresses = opts.to ?? [filled.to];
164
-
165
- const data = await withSpinner(
166
- {
167
- loading: 'Sending email...',
168
- success: 'Email sent',
169
- fail: 'Failed to send email',
170
- },
171
- () =>
172
- resend.emails.send({
173
- from: filled.from,
174
- to: toAddresses,
175
- subject: filled.subject,
176
- ...(html ? { html } : { text: body as string }),
177
- ...(opts.cc && { cc: opts.cc }),
178
- ...(opts.bcc && { bcc: opts.bcc }),
179
- ...(opts.replyTo && { replyTo: opts.replyTo }),
180
- }),
181
- 'send_error',
182
- globalOpts,
183
- );
184
- if (!globalOpts.json && isInteractive()) {
185
- console.log(`\nEmail sent: ${data.id}`);
186
- } else {
187
- outputResult(data, { json: globalOpts.json });
188
- }
189
- });
@@ -1,24 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { buildHelpText } from '../lib/help-text';
3
-
4
- export const openCommand = new Command('open')
5
- .description('Open the Resend dashboard in your browser')
6
- .addHelpText(
7
- 'after',
8
- buildHelpText({
9
- context: 'Opens https://resend.com/emails in your default browser.',
10
- examples: ['resend open'],
11
- }),
12
- )
13
- .action(async () => {
14
- const url = 'https://resend.com/emails';
15
- const { platform } = process;
16
- const args =
17
- platform === 'darwin'
18
- ? ['open', url]
19
- : platform === 'win32'
20
- ? ['cmd', '/c', 'start', url]
21
- : ['xdg-open', url];
22
-
23
- Bun.spawn(args, { stdio: ['ignore', 'ignore', 'ignore'] });
24
- });
@@ -1,50 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runCreate } from '../../lib/actions';
3
- import type { GlobalOpts } from '../../lib/client';
4
- import { buildHelpText } from '../../lib/help-text';
5
- import { requireText } from '../../lib/prompts';
6
-
7
- export const createSegmentCommand = new Command('create')
8
- .description('Create a new segment')
9
- .option('--name <name>', 'Segment name (required)')
10
- .addHelpText(
11
- 'after',
12
- buildHelpText({
13
- context: `Segments are named groups of contacts. Broadcasts target segments via segment_id.
14
- Contacts can belong to multiple segments. Audiences are deprecated — use segments instead.
15
-
16
- Non-interactive: --name is required.`,
17
- output: ` {"object":"segment","id":"<uuid>","name":"<name>"}`,
18
- errorCodes: ['auth_error', 'missing_name', 'create_error'],
19
- examples: [
20
- 'resend segments create --name "Newsletter Subscribers"',
21
- 'resend segments create --name "Beta Users" --json',
22
- ],
23
- }),
24
- )
25
- .action(async (opts, cmd) => {
26
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
27
-
28
- const name = await requireText(
29
- opts.name,
30
- { message: 'Segment name', placeholder: 'Newsletter Subscribers' },
31
- { message: 'Missing --name flag.', code: 'missing_name' },
32
- globalOpts,
33
- );
34
-
35
- await runCreate(
36
- {
37
- spinner: {
38
- loading: 'Creating segment...',
39
- success: 'Segment created',
40
- fail: 'Failed to create segment',
41
- },
42
- sdkCall: (resend) => resend.segments.create({ name }),
43
- onInteractive: (data) => {
44
- console.log(`\nSegment created: ${data.id}`);
45
- console.log(`Name: ${data.name}`);
46
- },
47
- },
48
- globalOpts,
49
- );
50
- });
@@ -1,47 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runDelete } from '../../lib/actions';
3
- import type { GlobalOpts } from '../../lib/client';
4
- import { buildHelpText } from '../../lib/help-text';
5
-
6
- export const deleteSegmentCommand = new Command('delete')
7
- .alias('rm')
8
- .description('Delete a segment')
9
- .argument('<id>', 'Segment UUID')
10
- .option(
11
- '--yes',
12
- 'Skip the confirmation prompt (required in non-interactive mode)',
13
- )
14
- .addHelpText(
15
- 'after',
16
- buildHelpText({
17
- context: `Warning: Deleting a segment removes it as a target for future broadcasts,
18
- but does NOT delete the contacts within it.
19
-
20
- Non-interactive: --yes is required to confirm deletion when stdin/stdout is not a TTY.`,
21
- output: ` {"object":"segment","id":"<uuid>","deleted":true}`,
22
- errorCodes: ['auth_error', 'confirmation_required', 'delete_error'],
23
- examples: [
24
- 'resend segments delete 78261eea-8f8b-4381-83c6-79fa7120f1cf --yes',
25
- 'resend segments delete 78261eea-8f8b-4381-83c6-79fa7120f1cf --yes --json',
26
- ],
27
- }),
28
- )
29
- .action(async (id, opts, cmd) => {
30
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
31
- await runDelete(
32
- id,
33
- !!opts.yes,
34
- {
35
- confirmMessage: `Delete segment ${id}?\nContacts will not be deleted, but broadcasts targeting this segment will no longer work.`,
36
- spinner: {
37
- loading: 'Deleting segment...',
38
- success: 'Segment deleted',
39
- fail: 'Failed to delete segment',
40
- },
41
- object: 'segment',
42
- successMsg: 'Segment deleted.',
43
- sdkCall: (resend) => resend.segments.remove(id),
44
- },
45
- globalOpts,
46
- );
47
- });
@@ -1,38 +0,0 @@
1
- import { Command } from '@commander-js/extra-typings';
2
- import { runGet } from '../../lib/actions';
3
- import type { GlobalOpts } from '../../lib/client';
4
- import { buildHelpText } from '../../lib/help-text';
5
-
6
- export const getSegmentCommand = new Command('get')
7
- .description('Retrieve a segment by ID')
8
- .argument('<id>', 'Segment UUID')
9
- .addHelpText(
10
- 'after',
11
- buildHelpText({
12
- output: ` {"object":"segment","id":"<uuid>","name":"<name>","created_at":"<iso-date>"}`,
13
- errorCodes: ['auth_error', 'fetch_error'],
14
- examples: [
15
- 'resend segments get 78261eea-8f8b-4381-83c6-79fa7120f1cf',
16
- 'resend segments get 78261eea-8f8b-4381-83c6-79fa7120f1cf --json',
17
- ],
18
- }),
19
- )
20
- .action(async (id, _opts, cmd) => {
21
- const globalOpts = cmd.optsWithGlobals() as GlobalOpts;
22
- await runGet(
23
- {
24
- spinner: {
25
- loading: 'Fetching segment...',
26
- success: 'Segment fetched',
27
- fail: 'Failed to fetch segment',
28
- },
29
- sdkCall: (resend) => resend.segments.get(id),
30
- onInteractive: (data) => {
31
- console.log(`\n${data.name}`);
32
- console.log(`ID: ${data.id}`);
33
- console.log(`Created: ${data.created_at}`);
34
- },
35
- },
36
- globalOpts,
37
- );
38
- });