resend-cli 1.1.0 → 1.2.0

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 (205) hide show
  1. package/.claude/settings.local.json +1 -10
  2. package/.claude/worktrees/emails-list/.claude/settings.local.json +5 -0
  3. package/.claude/worktrees/emails-list/.github/scripts/pr-title-check.js +34 -0
  4. package/.claude/worktrees/emails-list/.github/workflows/ci.yml +32 -0
  5. package/.claude/worktrees/emails-list/.github/workflows/pr-title-check.yml +13 -0
  6. package/.claude/worktrees/emails-list/.github/workflows/release.yml +93 -0
  7. package/.claude/worktrees/emails-list/CHANGELOG.md +31 -0
  8. package/.claude/worktrees/emails-list/LICENSE +21 -0
  9. package/.claude/worktrees/emails-list/README.md +424 -0
  10. package/.claude/worktrees/emails-list/biome.json +36 -0
  11. package/.claude/worktrees/emails-list/bun.lock +76 -0
  12. package/.claude/worktrees/emails-list/bunfig.toml +2 -0
  13. package/.claude/worktrees/emails-list/install.ps1 +140 -0
  14. package/.claude/worktrees/emails-list/install.sh +301 -0
  15. package/.claude/worktrees/emails-list/package.json +43 -0
  16. package/.claude/worktrees/emails-list/renovate.json +6 -0
  17. package/.claude/worktrees/emails-list/src/cli.ts +74 -0
  18. package/.claude/worktrees/emails-list/src/commands/api-keys/create.ts +114 -0
  19. package/.claude/worktrees/emails-list/src/commands/api-keys/delete.ts +47 -0
  20. package/.claude/worktrees/emails-list/src/commands/api-keys/index.ts +26 -0
  21. package/.claude/worktrees/emails-list/src/commands/api-keys/list.ts +35 -0
  22. package/.claude/worktrees/emails-list/src/commands/api-keys/utils.ts +8 -0
  23. package/.claude/worktrees/emails-list/src/commands/auth/index.ts +20 -0
  24. package/.claude/worktrees/emails-list/src/commands/auth/login.ts +207 -0
  25. package/.claude/worktrees/emails-list/src/commands/auth/logout.ts +105 -0
  26. package/.claude/worktrees/emails-list/src/commands/broadcasts/create.ts +196 -0
  27. package/.claude/worktrees/emails-list/src/commands/broadcasts/delete.ts +46 -0
  28. package/.claude/worktrees/emails-list/src/commands/broadcasts/get.ts +59 -0
  29. package/.claude/worktrees/emails-list/src/commands/broadcasts/index.ts +43 -0
  30. package/.claude/worktrees/emails-list/src/commands/broadcasts/list.ts +60 -0
  31. package/.claude/worktrees/emails-list/src/commands/broadcasts/send.ts +56 -0
  32. package/.claude/worktrees/emails-list/src/commands/broadcasts/update.ts +95 -0
  33. package/.claude/worktrees/emails-list/src/commands/broadcasts/utils.ts +35 -0
  34. package/.claude/worktrees/emails-list/src/commands/contact-properties/create.ts +118 -0
  35. package/.claude/worktrees/emails-list/src/commands/contact-properties/delete.ts +48 -0
  36. package/.claude/worktrees/emails-list/src/commands/contact-properties/get.ts +46 -0
  37. package/.claude/worktrees/emails-list/src/commands/contact-properties/index.ts +48 -0
  38. package/.claude/worktrees/emails-list/src/commands/contact-properties/list.ts +68 -0
  39. package/.claude/worktrees/emails-list/src/commands/contact-properties/update.ts +88 -0
  40. package/.claude/worktrees/emails-list/src/commands/contact-properties/utils.ts +17 -0
  41. package/.claude/worktrees/emails-list/src/commands/contacts/add-segment.ts +78 -0
  42. package/.claude/worktrees/emails-list/src/commands/contacts/create.ts +122 -0
  43. package/.claude/worktrees/emails-list/src/commands/contacts/delete.ts +49 -0
  44. package/.claude/worktrees/emails-list/src/commands/contacts/get.ts +53 -0
  45. package/.claude/worktrees/emails-list/src/commands/contacts/index.ts +58 -0
  46. package/.claude/worktrees/emails-list/src/commands/contacts/list.ts +57 -0
  47. package/.claude/worktrees/emails-list/src/commands/contacts/remove-segment.ts +48 -0
  48. package/.claude/worktrees/emails-list/src/commands/contacts/segments.ts +39 -0
  49. package/.claude/worktrees/emails-list/src/commands/contacts/topics.ts +45 -0
  50. package/.claude/worktrees/emails-list/src/commands/contacts/update-topics.ts +90 -0
  51. package/.claude/worktrees/emails-list/src/commands/contacts/update.ts +77 -0
  52. package/.claude/worktrees/emails-list/src/commands/contacts/utils.ts +119 -0
  53. package/.claude/worktrees/emails-list/src/commands/doctor.ts +298 -0
  54. package/.claude/worktrees/emails-list/src/commands/domains/create.ts +83 -0
  55. package/.claude/worktrees/emails-list/src/commands/domains/delete.ts +42 -0
  56. package/.claude/worktrees/emails-list/src/commands/domains/get.ts +47 -0
  57. package/.claude/worktrees/emails-list/src/commands/domains/index.ts +35 -0
  58. package/.claude/worktrees/emails-list/src/commands/domains/list.ts +53 -0
  59. package/.claude/worktrees/emails-list/src/commands/domains/update.ts +75 -0
  60. package/.claude/worktrees/emails-list/src/commands/domains/utils.ts +44 -0
  61. package/.claude/worktrees/emails-list/src/commands/domains/verify.ts +38 -0
  62. package/.claude/worktrees/emails-list/src/commands/emails/batch.ts +140 -0
  63. package/.claude/worktrees/emails-list/src/commands/emails/index.ts +28 -0
  64. package/.claude/worktrees/emails-list/src/commands/emails/list.ts +73 -0
  65. package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachment.ts +55 -0
  66. package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachments.ts +68 -0
  67. package/.claude/worktrees/emails-list/src/commands/emails/receiving/get.ts +58 -0
  68. package/.claude/worktrees/emails-list/src/commands/emails/receiving/index.ts +28 -0
  69. package/.claude/worktrees/emails-list/src/commands/emails/receiving/list.ts +59 -0
  70. package/.claude/worktrees/emails-list/src/commands/emails/receiving/utils.ts +38 -0
  71. package/.claude/worktrees/emails-list/src/commands/emails/send.ts +189 -0
  72. package/.claude/worktrees/emails-list/src/commands/open.ts +24 -0
  73. package/.claude/worktrees/emails-list/src/commands/segments/create.ts +50 -0
  74. package/.claude/worktrees/emails-list/src/commands/segments/delete.ts +47 -0
  75. package/.claude/worktrees/emails-list/src/commands/segments/get.ts +38 -0
  76. package/.claude/worktrees/emails-list/src/commands/segments/index.ts +36 -0
  77. package/.claude/worktrees/emails-list/src/commands/segments/list.ts +58 -0
  78. package/.claude/worktrees/emails-list/src/commands/segments/utils.ts +7 -0
  79. package/.claude/worktrees/emails-list/src/commands/teams/index.ts +10 -0
  80. package/.claude/worktrees/emails-list/src/commands/teams/list.ts +35 -0
  81. package/.claude/worktrees/emails-list/src/commands/teams/remove.ts +83 -0
  82. package/.claude/worktrees/emails-list/src/commands/teams/switch.ts +73 -0
  83. package/.claude/worktrees/emails-list/src/commands/topics/create.ts +73 -0
  84. package/.claude/worktrees/emails-list/src/commands/topics/delete.ts +47 -0
  85. package/.claude/worktrees/emails-list/src/commands/topics/get.ts +42 -0
  86. package/.claude/worktrees/emails-list/src/commands/topics/index.ts +42 -0
  87. package/.claude/worktrees/emails-list/src/commands/topics/list.ts +34 -0
  88. package/.claude/worktrees/emails-list/src/commands/topics/update.ts +59 -0
  89. package/.claude/worktrees/emails-list/src/commands/topics/utils.ts +16 -0
  90. package/.claude/worktrees/emails-list/src/commands/webhooks/create.ts +128 -0
  91. package/.claude/worktrees/emails-list/src/commands/webhooks/delete.ts +49 -0
  92. package/.claude/worktrees/emails-list/src/commands/webhooks/get.ts +42 -0
  93. package/.claude/worktrees/emails-list/src/commands/webhooks/index.ts +44 -0
  94. package/.claude/worktrees/emails-list/src/commands/webhooks/list.ts +55 -0
  95. package/.claude/worktrees/emails-list/src/commands/webhooks/update.ts +83 -0
  96. package/.claude/worktrees/emails-list/src/commands/webhooks/utils.ts +36 -0
  97. package/.claude/worktrees/emails-list/src/commands/whoami.ts +71 -0
  98. package/.claude/worktrees/emails-list/src/lib/actions.ts +157 -0
  99. package/.claude/worktrees/emails-list/src/lib/client.ts +34 -0
  100. package/.claude/worktrees/emails-list/src/lib/config.ts +211 -0
  101. package/.claude/worktrees/emails-list/src/lib/files.ts +15 -0
  102. package/.claude/worktrees/emails-list/src/lib/help-text.ts +38 -0
  103. package/.claude/worktrees/emails-list/src/lib/output.ts +54 -0
  104. package/.claude/worktrees/emails-list/src/lib/pagination.ts +36 -0
  105. package/.claude/worktrees/emails-list/src/lib/prompts.ts +149 -0
  106. package/.claude/worktrees/emails-list/src/lib/spinner.ts +93 -0
  107. package/.claude/worktrees/emails-list/src/lib/table.ts +57 -0
  108. package/.claude/worktrees/emails-list/src/lib/tty.ts +28 -0
  109. package/.claude/worktrees/emails-list/src/lib/version.ts +4 -0
  110. package/.claude/worktrees/emails-list/tests/commands/api-keys/create.test.ts +195 -0
  111. package/.claude/worktrees/emails-list/tests/commands/api-keys/delete.test.ts +156 -0
  112. package/.claude/worktrees/emails-list/tests/commands/api-keys/list.test.ts +133 -0
  113. package/.claude/worktrees/emails-list/tests/commands/auth/login.test.ts +119 -0
  114. package/.claude/worktrees/emails-list/tests/commands/auth/logout.test.ts +146 -0
  115. package/.claude/worktrees/emails-list/tests/commands/broadcasts/create.test.ts +447 -0
  116. package/.claude/worktrees/emails-list/tests/commands/broadcasts/delete.test.ts +182 -0
  117. package/.claude/worktrees/emails-list/tests/commands/broadcasts/get.test.ts +146 -0
  118. package/.claude/worktrees/emails-list/tests/commands/broadcasts/list.test.ts +196 -0
  119. package/.claude/worktrees/emails-list/tests/commands/broadcasts/send.test.ts +161 -0
  120. package/.claude/worktrees/emails-list/tests/commands/broadcasts/update.test.ts +283 -0
  121. package/.claude/worktrees/emails-list/tests/commands/contact-properties/create.test.ts +250 -0
  122. package/.claude/worktrees/emails-list/tests/commands/contact-properties/delete.test.ts +183 -0
  123. package/.claude/worktrees/emails-list/tests/commands/contact-properties/get.test.ts +144 -0
  124. package/.claude/worktrees/emails-list/tests/commands/contact-properties/list.test.ts +180 -0
  125. package/.claude/worktrees/emails-list/tests/commands/contact-properties/update.test.ts +216 -0
  126. package/.claude/worktrees/emails-list/tests/commands/contacts/add-segment.test.ts +188 -0
  127. package/.claude/worktrees/emails-list/tests/commands/contacts/create.test.ts +270 -0
  128. package/.claude/worktrees/emails-list/tests/commands/contacts/delete.test.ts +192 -0
  129. package/.claude/worktrees/emails-list/tests/commands/contacts/get.test.ts +148 -0
  130. package/.claude/worktrees/emails-list/tests/commands/contacts/list.test.ts +175 -0
  131. package/.claude/worktrees/emails-list/tests/commands/contacts/remove-segment.test.ts +166 -0
  132. package/.claude/worktrees/emails-list/tests/commands/contacts/segments.test.ts +167 -0
  133. package/.claude/worktrees/emails-list/tests/commands/contacts/topics.test.ts +163 -0
  134. package/.claude/worktrees/emails-list/tests/commands/contacts/update-topics.test.ts +247 -0
  135. package/.claude/worktrees/emails-list/tests/commands/contacts/update.test.ts +205 -0
  136. package/.claude/worktrees/emails-list/tests/commands/doctor.test.ts +165 -0
  137. package/.claude/worktrees/emails-list/tests/commands/domains/create.test.ts +192 -0
  138. package/.claude/worktrees/emails-list/tests/commands/domains/delete.test.ts +156 -0
  139. package/.claude/worktrees/emails-list/tests/commands/domains/get.test.ts +137 -0
  140. package/.claude/worktrees/emails-list/tests/commands/domains/list.test.ts +164 -0
  141. package/.claude/worktrees/emails-list/tests/commands/domains/update.test.ts +223 -0
  142. package/.claude/worktrees/emails-list/tests/commands/domains/verify.test.ts +117 -0
  143. package/.claude/worktrees/emails-list/tests/commands/emails/batch.test.ts +313 -0
  144. package/.claude/worktrees/emails-list/tests/commands/emails/list.test.ts +196 -0
  145. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachment.test.ts +140 -0
  146. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachments.test.ts +168 -0
  147. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/get.test.ts +140 -0
  148. package/.claude/worktrees/emails-list/tests/commands/emails/receiving/list.test.ts +181 -0
  149. package/.claude/worktrees/emails-list/tests/commands/emails/send.test.ts +309 -0
  150. package/.claude/worktrees/emails-list/tests/commands/segments/create.test.ts +163 -0
  151. package/.claude/worktrees/emails-list/tests/commands/segments/delete.test.ts +182 -0
  152. package/.claude/worktrees/emails-list/tests/commands/segments/get.test.ts +137 -0
  153. package/.claude/worktrees/emails-list/tests/commands/segments/list.test.ts +173 -0
  154. package/.claude/worktrees/emails-list/tests/commands/teams/list.test.ts +63 -0
  155. package/.claude/worktrees/emails-list/tests/commands/teams/remove.test.ts +103 -0
  156. package/.claude/worktrees/emails-list/tests/commands/teams/switch.test.ts +96 -0
  157. package/.claude/worktrees/emails-list/tests/commands/topics/create.test.ts +191 -0
  158. package/.claude/worktrees/emails-list/tests/commands/topics/delete.test.ts +156 -0
  159. package/.claude/worktrees/emails-list/tests/commands/topics/get.test.ts +125 -0
  160. package/.claude/worktrees/emails-list/tests/commands/topics/list.test.ts +124 -0
  161. package/.claude/worktrees/emails-list/tests/commands/topics/update.test.ts +177 -0
  162. package/.claude/worktrees/emails-list/tests/commands/webhooks/create.test.ts +224 -0
  163. package/.claude/worktrees/emails-list/tests/commands/webhooks/delete.test.ts +156 -0
  164. package/.claude/worktrees/emails-list/tests/commands/webhooks/get.test.ts +125 -0
  165. package/.claude/worktrees/emails-list/tests/commands/webhooks/list.test.ts +177 -0
  166. package/.claude/worktrees/emails-list/tests/commands/webhooks/update.test.ts +206 -0
  167. package/.claude/worktrees/emails-list/tests/commands/whoami.test.ts +99 -0
  168. package/.claude/worktrees/emails-list/tests/helpers.ts +93 -0
  169. package/.claude/worktrees/emails-list/tests/lib/client.test.ts +71 -0
  170. package/.claude/worktrees/emails-list/tests/lib/config.test.ts +414 -0
  171. package/.claude/worktrees/emails-list/tests/lib/files.test.ts +65 -0
  172. package/.claude/worktrees/emails-list/tests/lib/help-text.test.ts +97 -0
  173. package/.claude/worktrees/emails-list/tests/lib/output.test.ts +127 -0
  174. package/.claude/worktrees/emails-list/tests/lib/prompts.test.ts +178 -0
  175. package/.claude/worktrees/emails-list/tests/lib/spinner.test.ts +146 -0
  176. package/.claude/worktrees/emails-list/tests/lib/table.test.ts +63 -0
  177. package/.claude/worktrees/emails-list/tests/lib/tty.test.ts +85 -0
  178. package/.claude/worktrees/emails-list/tsconfig.json +14 -0
  179. package/.github/workflows/ci.yml +3 -3
  180. package/.github/workflows/pr-title-check.yml +1 -1
  181. package/.github/workflows/release.yml +2 -2
  182. package/.github/workflows/test-build-windows.yml +44 -0
  183. package/.github/workflows/test-install-windows.yml +48 -0
  184. package/README.md +8 -0
  185. package/docs/agent-dx-gaps.md +167 -0
  186. package/docs/missing-commands.md +58 -0
  187. package/docs/production-readiness.md +99 -0
  188. package/docs/secure-key-storage.md +174 -0
  189. package/install.ps1 +1 -0
  190. package/install.sh +11 -4
  191. package/package.json +1 -1
  192. package/renovate.json +4 -0
  193. package/src/cli.ts +9 -0
  194. package/src/commands/auth/login.ts +34 -13
  195. package/src/commands/doctor.ts +3 -3
  196. package/src/commands/open.ts +24 -0
  197. package/src/commands/teams/remove.ts +5 -2
  198. package/src/commands/teams/switch.ts +3 -0
  199. package/src/lib/client.ts +6 -1
  200. package/src/lib/config.ts +37 -30
  201. package/src/lib/help-text.ts +4 -2
  202. package/src/lib/spinner.ts +7 -3
  203. package/tests/commands/auth/login.test.ts +35 -0
  204. package/tests/lib/config.test.ts +40 -7
  205. package/tests/lib/help-text.test.ts +2 -1
@@ -1,14 +1,5 @@
1
1
  {
2
2
  "permissions": {
3
- "allow": [
4
- "Bash(git config:*)",
5
- "Bash(find /Users/zeno/Projects/resend/resend-cli/src -name \"index.ts\" -path \"*/commands/index.ts\" -o -path \"*/cli.ts\" | xargs ls -la 2>/dev/null)",
6
- "Bash(bun run:*)",
7
- "Bash(./dist/resend --help 2>&1 | head -30)",
8
- "Bash(./dist/resend whoami:*)",
9
- "Bash(npx tsc:*)",
10
- "Bash(npx vitest:*)",
11
- "Bash(bun test:*)"
12
- ]
3
+ "allow": ["Bash(npx biome:*)"]
13
4
  }
14
5
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "permissions": {
3
+ "allow": ["Bash(npx biome:*)"]
4
+ }
5
+ }
@@ -0,0 +1,34 @@
1
+ import fs from 'node:fs';
2
+
3
+ const eventPath = process.env.GITHUB_EVENT_PATH;
4
+ if (!eventPath) {
5
+ console.error(
6
+ 'GITHUB_EVENT_PATH is not set. This script must run inside GitHub Actions.',
7
+ );
8
+ process.exit(1);
9
+ }
10
+
11
+ const eventJson = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
12
+ const prTitle = eventJson.pull_request?.title;
13
+ if (!prTitle) {
14
+ console.error('Could not read pull_request.title from event payload.');
15
+ process.exit(1);
16
+ }
17
+
18
+ const isValidType = (title) =>
19
+ /^(feat|fix|chore|refactor)(\([a-zA-Z0-9-]+\))?:\s[a-z0-9].*$/.test(title);
20
+
21
+ const validateTitle = (title) => {
22
+ if (!isValidType(title)) {
23
+ console.error(
24
+ `PR title does not follow the required format "[type]: [description]"
25
+ Examples: "feat: add --cc flag" | "fix: domain fetch timeout"
26
+ Types: feat · fix · chore · refactor
27
+ Rules: lowercase after the colon`,
28
+ );
29
+ process.exit(1);
30
+ }
31
+ console.info(`PR title valid: "${title}"`);
32
+ };
33
+
34
+ validateTitle(prTitle);
@@ -0,0 +1,32 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ concurrency:
7
+ group: ci-${{ github.ref }}
8
+ cancel-in-progress: true
9
+ jobs:
10
+ lint:
11
+ runs-on: blacksmith-2vcpu-ubuntu-2204
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: oven-sh/setup-bun@v2
15
+ - run: bun install --frozen-lockfile
16
+ - run: bun run lint
17
+
18
+ typecheck:
19
+ runs-on: blacksmith-2vcpu-ubuntu-2204
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: oven-sh/setup-bun@v2
23
+ - run: bun install --frozen-lockfile
24
+ - run: bun run typecheck
25
+
26
+ test:
27
+ runs-on: blacksmith-2vcpu-ubuntu-2204
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: oven-sh/setup-bun@v2
31
+ - run: bun install --frozen-lockfile
32
+ - run: bun run test
@@ -0,0 +1,13 @@
1
+ name: PR Title Check
2
+ on:
3
+ pull_request:
4
+ types: [opened, edited, synchronize, reopened]
5
+ permissions:
6
+ contents: read
7
+ pull-requests: read
8
+ jobs:
9
+ pr-title-check:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - run: node .github/scripts/pr-title-check.js
@@ -0,0 +1,93 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags:
5
+ - 'v*'
6
+ permissions:
7
+ contents: write
8
+ concurrency:
9
+ group: release
10
+ cancel-in-progress: false
11
+ jobs:
12
+ release:
13
+ runs-on: blacksmith-2vcpu-ubuntu-2204
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: oven-sh/setup-bun@v2
18
+
19
+ - name: Install dependencies
20
+ run: bun install --frozen-lockfile
21
+
22
+ - name: Build binaries
23
+ run: |
24
+ bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-darwin-arm64 --outfile dist/resend-darwin-arm64
25
+ bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-darwin-x64 --outfile dist/resend-darwin-x64
26
+ bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-linux-x64 --outfile dist/resend-linux-x64
27
+ bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-linux-arm64 --outfile dist/resend-linux-arm64
28
+ bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-windows-x64 --outfile dist/resend-windows-x64.exe
29
+
30
+ - name: Verify binaries
31
+ run: |
32
+ for f in dist/resend-darwin-arm64 dist/resend-darwin-x64 \
33
+ dist/resend-linux-x64 dist/resend-linux-arm64 \
34
+ dist/resend-windows-x64.exe; do
35
+ [ -s "$f" ] || { echo "Missing or empty: $f"; exit 1; }
36
+ done
37
+
38
+ - name: Package archives
39
+ run: |
40
+ cd dist
41
+ for bin in resend-darwin-arm64 resend-darwin-x64 resend-linux-x64 resend-linux-arm64; do
42
+ cp "$bin" resend
43
+ chmod +x resend
44
+ tar -czf "${bin}.tar.gz" resend
45
+ rm resend
46
+ done
47
+ cp resend-windows-x64.exe resend.exe
48
+ zip resend-windows-x64.zip resend.exe
49
+ rm resend.exe
50
+
51
+ - name: Create GitHub Release
52
+ uses: softprops/action-gh-release@v2
53
+ with:
54
+ generate_release_notes: true
55
+ body: |
56
+ ## Install
57
+
58
+ **macOS / Linux**
59
+ ```sh
60
+ curl -fsSL https://resend.com/install.sh | bash
61
+ ```
62
+
63
+ **Windows (PowerShell)**
64
+ ```pwsh
65
+ irm https://resend.com/install.ps1 | iex
66
+ ```
67
+
68
+ **GitHub CLI** _(use while repo is private)_
69
+ ```sh
70
+ gh release download --repo resend/resend-cli --pattern "resend-darwin-arm64.tar.gz"
71
+ tar -xzf resend-darwin-arm64.tar.gz && sudo mv resend /usr/local/bin/ && rm resend-darwin-arm64.tar.gz
72
+ ```
73
+ files: |
74
+ dist/resend-darwin-arm64.tar.gz
75
+ dist/resend-darwin-x64.tar.gz
76
+ dist/resend-linux-x64.tar.gz
77
+ dist/resend-linux-arm64.tar.gz
78
+ dist/resend-windows-x64.zip
79
+
80
+ notify-tap:
81
+ name: Trigger Homebrew Tap Update
82
+ needs: release
83
+ runs-on: ubuntu-latest
84
+ permissions:
85
+ contents: read
86
+ steps:
87
+ - name: Dispatch publish-release workflow on tap
88
+ env:
89
+ GH_TOKEN: ${{ secrets.RELEASE_CLI_ON_HOMEBREW }}
90
+ run: |
91
+ gh workflow run publish-release.yml \
92
+ --repo resend/homebrew-cli \
93
+ --field version="${GITHUB_REF_NAME}"
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0] - 2026-02-18
4
+
5
+ ### Added
6
+
7
+ - `resend domains` — create, verify, get, list, update, delete sending domains
8
+ - `resend api-keys` — create, list, delete API keys
9
+ - `resend broadcasts` — full broadcast lifecycle (create, send, get, list, update, delete)
10
+ - `resend contacts` — manage contacts, segments, and topics across all CRUD operations
11
+ - `resend emails batch` — send up to 100 emails in a single request from a JSON file
12
+ - Shared pagination (`--limit`, `--after`, `--before`) on all list commands
13
+ - `--html-file` flag on `emails send` and `broadcasts create` to read body from a file
14
+
15
+ ### Fixed
16
+
17
+ - `isInteractive()` now checks both `stdin` and `stdout` TTY — CI environments are correctly detected as non-interactive
18
+ - `domains delete` now returns a consistent `{ id, deleted: true }` object instead of an empty `{}`
19
+
20
+ ### Changed
21
+
22
+ - All delete commands return a uniform `{ object, id, deleted: true }` response
23
+ - `--help` improved across all commands with output shape, error codes, and usage examples
24
+
25
+ ---
26
+
27
+ ## [0.1.0] - 2026-02-18
28
+
29
+ - Initial release: `auth login`, `emails send`, `doctor`
30
+ - Auto JSON output when stdout is not a TTY (`--json`)
31
+ - Cross-platform binaries for macOS, Linux, and Windows via GitHub Actions
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Resend
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,424 @@
1
+ # Resend CLI
2
+
3
+ Command-line interface for the [Resend](https://resend.com) email API. Works for humans, AI agents, and CI/CD pipelines.
4
+
5
+ ## Install
6
+
7
+ ### Homebrew (macOS)
8
+
9
+ ```bash
10
+ brew install resend/cli/resend
11
+ ```
12
+
13
+ ### Shell script (macOS and Linux)
14
+
15
+ ```bash
16
+ curl -fsSL https://resend.com/install.sh | bash
17
+ ```
18
+
19
+ ## Local development
20
+
21
+ Use this when you want to change the CLI and run your build locally.
22
+
23
+ ### Prerequisites
24
+
25
+ - [Bun](https://bun.sh) (runtime and package manager)
26
+
27
+ ### Setup
28
+
29
+ 1. **Clone the repo**
30
+
31
+ ```bash
32
+ git clone https://github.com/resend/resend-cli.git
33
+ cd resend-cli
34
+ ```
35
+
36
+ 2. **Install dependencies**
37
+
38
+ ```bash
39
+ bun install
40
+ ```
41
+
42
+ 3. **Build locally**
43
+
44
+ ```bash
45
+ bun run build
46
+ ```
47
+
48
+ Output: `./dist/resend`
49
+
50
+ ### Running the CLI locally
51
+
52
+ Use the built binary directly:
53
+
54
+ ```bash
55
+ ./dist/resend --version
56
+ ```
57
+
58
+ Or add the project to your `PATH`:
59
+
60
+ ```bash
61
+ export PATH="$(pwd)/dist:$PATH"
62
+ resend doctor
63
+ ```
64
+
65
+ ### Making changes
66
+
67
+ After editing source files, rebuild:
68
+
69
+ ```bash
70
+ bun run build
71
+ ```
72
+
73
+ ## Quick start
74
+
75
+ ```bash
76
+ # Authenticate
77
+ resend login
78
+
79
+ # Send an email
80
+ resend emails send \
81
+ --from "you@yourdomain.com" \
82
+ --to recipient@example.com \
83
+ --subject "Hello from Resend CLI" \
84
+ --text "Sent from my terminal."
85
+
86
+ # Check your environment
87
+ resend doctor
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Authentication
93
+
94
+ The CLI resolves your API key using the following priority chain:
95
+
96
+ | Priority | Source | How to set |
97
+ |----------|--------|------------|
98
+ | 1 (highest) | `--api-key` flag | `resend --api-key re_xxx emails send ...` |
99
+ | 2 | `RESEND_API_KEY` env var | `export RESEND_API_KEY=re_xxx` |
100
+ | 3 (lowest) | Config file | `resend login` |
101
+
102
+ If no key is found from any source, the CLI errors with code `auth_error`.
103
+
104
+ ---
105
+
106
+ ## Commands
107
+
108
+ ### `resend login`
109
+
110
+ Authenticate by storing your API key locally. The key is validated against the Resend API before being saved.
111
+
112
+ ```bash
113
+ resend login
114
+ ```
115
+
116
+ #### Interactive mode (default in terminals)
117
+
118
+ When run in a terminal, the command checks for an existing key:
119
+
120
+ - **No key found** — Offers to open the [Resend API keys dashboard](https://resend.com/api-keys) in your browser so you can create one, then prompts for the key.
121
+ - **Existing key found** — Shows the key source (`env`, `config`) and prompts for a new key to replace it.
122
+
123
+ The key is entered via a masked password input and must start with `re_`.
124
+
125
+ #### Non-interactive mode (CI, pipes, scripts)
126
+
127
+ When stdin is not a TTY, the `--key` flag is required:
128
+
129
+ ```bash
130
+ resend login --key re_xxxxxxxxxxxxx
131
+ ```
132
+
133
+ Omitting `--key` in non-interactive mode exits with error code `missing_key`.
134
+
135
+ #### Options
136
+
137
+ | Flag | Description |
138
+ |------|-------------|
139
+ | `--key <key>` | API key to store (required in non-interactive mode) |
140
+
141
+ #### Output
142
+
143
+ On success, credentials are saved to `~/.config/resend/credentials.json` with `0600` permissions (owner read/write only). The config directory is created with `0700` permissions.
144
+
145
+ ```bash
146
+ # JSON output
147
+ resend login --key re_xxx --json
148
+ # => {"success":true,"config_path":"/Users/you/.config/resend/credentials.json"}
149
+ ```
150
+
151
+ #### Error codes
152
+
153
+ | Code | Cause |
154
+ |------|-------|
155
+ | `missing_key` | No `--key` provided in non-interactive mode |
156
+ | `invalid_key_format` | Key does not start with `re_` |
157
+ | `validation_failed` | Resend API rejected the key |
158
+
159
+ ---
160
+
161
+ ### `resend emails send`
162
+
163
+ Send an email via the Resend API. Provide all options via flags for scripting, or let the CLI prompt interactively for missing fields.
164
+
165
+ ```bash
166
+ resend emails send \
167
+ --from "Name <sender@yourdomain.com>" \
168
+ --to recipient@example.com \
169
+ --subject "Subject line" \
170
+ --text "Plain text body"
171
+ ```
172
+
173
+ #### Options
174
+
175
+ | Flag | Required | Description |
176
+ |------|----------|-------------|
177
+ | `--from <address>` | Yes | Sender email address (must be from a verified domain) |
178
+ | `--to <addresses...>` | Yes | One or more recipient email addresses (space-separated) |
179
+ | `--subject <subject>` | Yes | Email subject line |
180
+ | `--text <text>` | One of text/html/html-file | Plain text body |
181
+ | `--html <html>` | One of text/html/html-file | HTML body as a string |
182
+ | `--html-file <path>` | One of text/html/html-file | Path to an HTML file to use as body |
183
+ | `--cc <addresses...>` | No | CC recipients (space-separated) |
184
+ | `--bcc <addresses...>` | No | BCC recipients (space-separated) |
185
+ | `--reply-to <address>` | No | Reply-to email address |
186
+
187
+ #### Interactive mode
188
+
189
+ When run in a terminal without all required flags, the CLI prompts for missing fields:
190
+
191
+ ```bash
192
+ # prompts for from, to, subject, and body
193
+ resend emails send
194
+
195
+ # prompts only for missing fields
196
+ resend emails send --from "you@yourdomain.com"
197
+ ```
198
+
199
+ #### Non-interactive mode
200
+
201
+ When piped or run in CI, all required flags must be provided. Missing flags cause an error listing what's needed:
202
+
203
+ ```bash
204
+ echo "" | resend emails send --from "you@yourdomain.com"
205
+ # Error: Missing required flags: --to, --subject
206
+ ```
207
+
208
+ A body (`--text`, `--html`, or `--html-file`) is also required — omitting all three exits with code `missing_body`.
209
+
210
+ #### Examples
211
+
212
+ **Multiple recipients:**
213
+
214
+ ```bash
215
+ resend emails send \
216
+ --from "you@yourdomain.com" \
217
+ --to alice@example.com bob@example.com \
218
+ --subject "Team update" \
219
+ --text "Hello everyone"
220
+ ```
221
+
222
+ **HTML from a file:**
223
+
224
+ ```bash
225
+ resend emails send \
226
+ --from "you@yourdomain.com" \
227
+ --to recipient@example.com \
228
+ --subject "Newsletter" \
229
+ --html-file ./newsletter.html
230
+ ```
231
+
232
+ **With CC, BCC, and reply-to:**
233
+
234
+ ```bash
235
+ resend emails send \
236
+ --from "you@yourdomain.com" \
237
+ --to recipient@example.com \
238
+ --subject "Meeting notes" \
239
+ --text "See attached." \
240
+ --cc manager@example.com \
241
+ --bcc archive@example.com \
242
+ --reply-to noreply@example.com
243
+ ```
244
+
245
+ **Overriding the API key for one send:**
246
+
247
+ ```bash
248
+ resend --api-key re_other_key emails send \
249
+ --from "you@yourdomain.com" \
250
+ --to recipient@example.com \
251
+ --subject "Test" \
252
+ --text "Using a different key"
253
+ ```
254
+
255
+ #### Output
256
+
257
+ Returns the email ID on success:
258
+
259
+ ```json
260
+ { "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" }
261
+ ```
262
+
263
+ #### Error codes
264
+
265
+ | Code | Cause |
266
+ |------|-------|
267
+ | `auth_error` | No API key found or client creation failed |
268
+ | `missing_body` | No `--text`, `--html`, or `--html-file` provided |
269
+ | `file_read_error` | Could not read the file passed to `--html-file` |
270
+ | `send_error` | Resend API returned an error |
271
+
272
+ ---
273
+
274
+ ### `resend doctor`
275
+
276
+ Run environment diagnostics. Verifies your CLI version, API key, domains, and detects AI agent integrations.
277
+
278
+ ```bash
279
+ resend doctor
280
+ ```
281
+
282
+ #### Checks performed
283
+
284
+ | Check | Pass | Warn | Fail |
285
+ |-------|------|------|------|
286
+ | **CLI Version** | Running latest | Update available or registry unreachable | — |
287
+ | **API Key** | Key found (shows masked key + source) | — | No key found |
288
+ | **Domains** | Verified domains exist | No domains or all pending verification | API key invalid |
289
+ | **AI Agents** | Lists detected agents (or none) | — | — |
290
+
291
+ The API key is always masked in output (e.g. `re_...xxxx`).
292
+
293
+ #### Interactive mode
294
+
295
+ In a terminal, shows animated spinners for each check with colored status icons:
296
+
297
+ ```
298
+ Resend Doctor
299
+
300
+ ✔ CLI Version: v0.1.0 (latest)
301
+ ✔ API Key: re_...xxxx (source: env)
302
+ ✔ Domains: 2 verified, 0 pending
303
+ ✔ AI Agents: Detected: Cursor, Claude Desktop
304
+ ```
305
+
306
+ #### JSON mode
307
+
308
+ ```bash
309
+ resend doctor --json
310
+ ```
311
+
312
+ ```json
313
+ {
314
+ "ok": true,
315
+ "checks": [
316
+ { "name": "CLI Version", "status": "pass", "message": "v0.1.0 (latest)" },
317
+ { "name": "API Key", "status": "pass", "message": "re_...xxxx (source: env)" },
318
+ { "name": "Domains", "status": "pass", "message": "2 verified, 0 pending" },
319
+ { "name": "AI Agents", "status": "pass", "message": "Detected: Cursor" }
320
+ ]
321
+ }
322
+ ```
323
+
324
+ Each check has a `status` of `pass`, `warn`, or `fail`. The top-level `ok` is `false` if any check is `fail`.
325
+
326
+ #### Detected AI agents
327
+
328
+ | Agent | Detection method |
329
+ |-------|-----------------|
330
+ | OpenClaw | `~/clawd/skills` directory exists |
331
+ | Cursor | `~/.cursor` directory exists |
332
+ | Claude Desktop | Platform-specific config file exists |
333
+ | VS Code | `.vscode/mcp.json` in current directory |
334
+
335
+ #### Exit code
336
+
337
+ Exits `0` when all checks pass or warn. Exits `1` if any check fails.
338
+
339
+ ---
340
+
341
+ ## Global options
342
+
343
+ These flags work on every command and are passed before the subcommand:
344
+
345
+ ```bash
346
+ resend [global options] <command> [command options]
347
+ ```
348
+
349
+ | Flag | Description |
350
+ |------|-------------|
351
+ | `--api-key <key>` | Override API key for this invocation (takes highest priority) |
352
+ | `--json` | Force JSON output even in interactive terminals |
353
+ | `--version` | Print version and exit |
354
+ | `--help` | Show help text |
355
+
356
+ ---
357
+
358
+ ## Output behavior
359
+
360
+ The CLI has two output modes:
361
+
362
+ | Mode | When | Stdout | Stderr |
363
+ |------|------|--------|--------|
364
+ | **Interactive** | Terminal (TTY) | Formatted text | Spinners, prompts |
365
+ | **Machine** | Piped, CI, or `--json` | JSON | Nothing |
366
+
367
+ Switching is automatic — pipe to another command and JSON output activates:
368
+
369
+ ```bash
370
+ resend doctor | jq '.checks[].name'
371
+ resend emails send --from ... --to ... --subject ... --text ... | jq '.id'
372
+ ```
373
+
374
+ ### Error output
375
+
376
+ Errors always exit with code `1` and output structured JSON to stdout:
377
+
378
+ ```json
379
+ { "error": { "message": "No API key found", "code": "auth_error" } }
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Agent & CI/CD usage
385
+
386
+ ### CI/CD
387
+
388
+ Set `RESEND_API_KEY` as an environment variable — no `resend login` needed:
389
+
390
+ ```yaml
391
+ # GitHub Actions
392
+ env:
393
+ RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
394
+ steps:
395
+ - run: |
396
+ resend emails send \
397
+ --from "deploy@yourdomain.com" \
398
+ --to "team@yourdomain.com" \
399
+ --subject "Deploy complete" \
400
+ --text "Version ${{ github.sha }} deployed."
401
+ ```
402
+
403
+ ### AI agents
404
+
405
+ Agents calling the CLI as a subprocess automatically get JSON output (non-TTY detection). The contract:
406
+
407
+ - **Input:** All required flags must be provided (no interactive prompts)
408
+ - **Output:** JSON to stdout, nothing to stderr
409
+ - **Exit code:** `0` success, `1` error
410
+ - **Errors:** Always include `message` and `code` fields
411
+
412
+ ---
413
+
414
+ ## Configuration
415
+
416
+ | Item | Path | Notes |
417
+ |------|------|-------|
418
+ | Config directory | `~/.config/resend/` | Respects `$XDG_CONFIG_HOME` on Linux, `%APPDATA%` on Windows |
419
+ | Credentials | `~/.config/resend/credentials.json` | `0600` permissions (owner read/write) |
420
+ | Install directory | `~/.resend/bin/` | Respects `$RESEND_INSTALL` |
421
+
422
+ ## License
423
+
424
+ MIT