resend-cli 1.2.0 → 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.
- package/.github/workflows/release.yml +35 -8
- package/README.md +12 -1
- package/biome.json +1 -1
- package/bun.lock +0 -3
- package/package.json +3 -4
- package/src/cli.ts +23 -5
- package/src/commands/auth/login.ts +10 -8
- package/src/commands/doctor.ts +30 -112
- package/src/lib/client.ts +3 -0
- package/src/lib/config.ts +2 -3
- package/src/lib/spinner.ts +17 -10
- package/src/lib/update-check.ts +172 -0
- package/tests/commands/auth/login.test.ts +3 -1
- package/tests/lib/config.test.ts +4 -6
- package/tests/lib/update-check.test.ts +169 -0
- package/.claude/worktrees/emails-list/.claude/settings.local.json +0 -5
- package/.claude/worktrees/emails-list/.github/scripts/pr-title-check.js +0 -34
- package/.claude/worktrees/emails-list/.github/workflows/ci.yml +0 -32
- package/.claude/worktrees/emails-list/.github/workflows/pr-title-check.yml +0 -13
- package/.claude/worktrees/emails-list/.github/workflows/release.yml +0 -93
- package/.claude/worktrees/emails-list/CHANGELOG.md +0 -31
- package/.claude/worktrees/emails-list/LICENSE +0 -21
- package/.claude/worktrees/emails-list/README.md +0 -424
- package/.claude/worktrees/emails-list/biome.json +0 -36
- package/.claude/worktrees/emails-list/bun.lock +0 -76
- package/.claude/worktrees/emails-list/bunfig.toml +0 -2
- package/.claude/worktrees/emails-list/install.ps1 +0 -140
- package/.claude/worktrees/emails-list/install.sh +0 -301
- package/.claude/worktrees/emails-list/package.json +0 -43
- package/.claude/worktrees/emails-list/renovate.json +0 -6
- package/.claude/worktrees/emails-list/src/cli.ts +0 -74
- package/.claude/worktrees/emails-list/src/commands/api-keys/create.ts +0 -114
- package/.claude/worktrees/emails-list/src/commands/api-keys/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/api-keys/index.ts +0 -26
- package/.claude/worktrees/emails-list/src/commands/api-keys/list.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/api-keys/utils.ts +0 -8
- package/.claude/worktrees/emails-list/src/commands/auth/index.ts +0 -20
- package/.claude/worktrees/emails-list/src/commands/auth/login.ts +0 -207
- package/.claude/worktrees/emails-list/src/commands/auth/logout.ts +0 -105
- package/.claude/worktrees/emails-list/src/commands/broadcasts/create.ts +0 -196
- package/.claude/worktrees/emails-list/src/commands/broadcasts/delete.ts +0 -46
- package/.claude/worktrees/emails-list/src/commands/broadcasts/get.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/broadcasts/index.ts +0 -43
- package/.claude/worktrees/emails-list/src/commands/broadcasts/list.ts +0 -60
- package/.claude/worktrees/emails-list/src/commands/broadcasts/send.ts +0 -56
- package/.claude/worktrees/emails-list/src/commands/broadcasts/update.ts +0 -95
- package/.claude/worktrees/emails-list/src/commands/broadcasts/utils.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/contact-properties/create.ts +0 -118
- package/.claude/worktrees/emails-list/src/commands/contact-properties/delete.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contact-properties/get.ts +0 -46
- package/.claude/worktrees/emails-list/src/commands/contact-properties/index.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contact-properties/list.ts +0 -68
- package/.claude/worktrees/emails-list/src/commands/contact-properties/update.ts +0 -88
- package/.claude/worktrees/emails-list/src/commands/contact-properties/utils.ts +0 -17
- package/.claude/worktrees/emails-list/src/commands/contacts/add-segment.ts +0 -78
- package/.claude/worktrees/emails-list/src/commands/contacts/create.ts +0 -122
- package/.claude/worktrees/emails-list/src/commands/contacts/delete.ts +0 -49
- package/.claude/worktrees/emails-list/src/commands/contacts/get.ts +0 -53
- package/.claude/worktrees/emails-list/src/commands/contacts/index.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/contacts/list.ts +0 -57
- package/.claude/worktrees/emails-list/src/commands/contacts/remove-segment.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contacts/segments.ts +0 -39
- package/.claude/worktrees/emails-list/src/commands/contacts/topics.ts +0 -45
- package/.claude/worktrees/emails-list/src/commands/contacts/update-topics.ts +0 -90
- package/.claude/worktrees/emails-list/src/commands/contacts/update.ts +0 -77
- package/.claude/worktrees/emails-list/src/commands/contacts/utils.ts +0 -119
- package/.claude/worktrees/emails-list/src/commands/doctor.ts +0 -298
- package/.claude/worktrees/emails-list/src/commands/domains/create.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/domains/delete.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/domains/get.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/domains/index.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/domains/list.ts +0 -53
- package/.claude/worktrees/emails-list/src/commands/domains/update.ts +0 -75
- package/.claude/worktrees/emails-list/src/commands/domains/utils.ts +0 -44
- package/.claude/worktrees/emails-list/src/commands/domains/verify.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/emails/batch.ts +0 -140
- package/.claude/worktrees/emails-list/src/commands/emails/index.ts +0 -28
- package/.claude/worktrees/emails-list/src/commands/emails/list.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachment.ts +0 -55
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachments.ts +0 -68
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/get.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/index.ts +0 -28
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/list.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/utils.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/emails/send.ts +0 -189
- package/.claude/worktrees/emails-list/src/commands/open.ts +0 -24
- package/.claude/worktrees/emails-list/src/commands/segments/create.ts +0 -50
- package/.claude/worktrees/emails-list/src/commands/segments/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/segments/get.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/segments/index.ts +0 -36
- package/.claude/worktrees/emails-list/src/commands/segments/list.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/segments/utils.ts +0 -7
- package/.claude/worktrees/emails-list/src/commands/teams/index.ts +0 -10
- package/.claude/worktrees/emails-list/src/commands/teams/list.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/teams/remove.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/teams/switch.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/topics/create.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/topics/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/topics/get.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/topics/index.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/topics/list.ts +0 -34
- package/.claude/worktrees/emails-list/src/commands/topics/update.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/topics/utils.ts +0 -16
- package/.claude/worktrees/emails-list/src/commands/webhooks/create.ts +0 -128
- package/.claude/worktrees/emails-list/src/commands/webhooks/delete.ts +0 -49
- package/.claude/worktrees/emails-list/src/commands/webhooks/get.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/webhooks/index.ts +0 -44
- package/.claude/worktrees/emails-list/src/commands/webhooks/list.ts +0 -55
- package/.claude/worktrees/emails-list/src/commands/webhooks/update.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/webhooks/utils.ts +0 -36
- package/.claude/worktrees/emails-list/src/commands/whoami.ts +0 -71
- package/.claude/worktrees/emails-list/src/lib/actions.ts +0 -157
- package/.claude/worktrees/emails-list/src/lib/client.ts +0 -34
- package/.claude/worktrees/emails-list/src/lib/config.ts +0 -211
- package/.claude/worktrees/emails-list/src/lib/files.ts +0 -15
- package/.claude/worktrees/emails-list/src/lib/help-text.ts +0 -38
- package/.claude/worktrees/emails-list/src/lib/output.ts +0 -54
- package/.claude/worktrees/emails-list/src/lib/pagination.ts +0 -36
- package/.claude/worktrees/emails-list/src/lib/prompts.ts +0 -149
- package/.claude/worktrees/emails-list/src/lib/spinner.ts +0 -93
- package/.claude/worktrees/emails-list/src/lib/table.ts +0 -57
- package/.claude/worktrees/emails-list/src/lib/tty.ts +0 -28
- package/.claude/worktrees/emails-list/src/lib/version.ts +0 -4
- package/.claude/worktrees/emails-list/tests/commands/api-keys/create.test.ts +0 -195
- package/.claude/worktrees/emails-list/tests/commands/api-keys/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/api-keys/list.test.ts +0 -133
- package/.claude/worktrees/emails-list/tests/commands/auth/login.test.ts +0 -119
- package/.claude/worktrees/emails-list/tests/commands/auth/logout.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/create.test.ts +0 -447
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/delete.test.ts +0 -182
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/get.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/list.test.ts +0 -196
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/send.test.ts +0 -161
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/update.test.ts +0 -283
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/create.test.ts +0 -250
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/delete.test.ts +0 -183
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/get.test.ts +0 -144
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/list.test.ts +0 -180
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/update.test.ts +0 -216
- package/.claude/worktrees/emails-list/tests/commands/contacts/add-segment.test.ts +0 -188
- package/.claude/worktrees/emails-list/tests/commands/contacts/create.test.ts +0 -270
- package/.claude/worktrees/emails-list/tests/commands/contacts/delete.test.ts +0 -192
- package/.claude/worktrees/emails-list/tests/commands/contacts/get.test.ts +0 -148
- package/.claude/worktrees/emails-list/tests/commands/contacts/list.test.ts +0 -175
- package/.claude/worktrees/emails-list/tests/commands/contacts/remove-segment.test.ts +0 -166
- package/.claude/worktrees/emails-list/tests/commands/contacts/segments.test.ts +0 -167
- package/.claude/worktrees/emails-list/tests/commands/contacts/topics.test.ts +0 -163
- package/.claude/worktrees/emails-list/tests/commands/contacts/update-topics.test.ts +0 -247
- package/.claude/worktrees/emails-list/tests/commands/contacts/update.test.ts +0 -205
- package/.claude/worktrees/emails-list/tests/commands/doctor.test.ts +0 -165
- package/.claude/worktrees/emails-list/tests/commands/domains/create.test.ts +0 -192
- package/.claude/worktrees/emails-list/tests/commands/domains/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/domains/get.test.ts +0 -137
- package/.claude/worktrees/emails-list/tests/commands/domains/list.test.ts +0 -164
- package/.claude/worktrees/emails-list/tests/commands/domains/update.test.ts +0 -223
- package/.claude/worktrees/emails-list/tests/commands/domains/verify.test.ts +0 -117
- package/.claude/worktrees/emails-list/tests/commands/emails/batch.test.ts +0 -313
- package/.claude/worktrees/emails-list/tests/commands/emails/list.test.ts +0 -196
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachment.test.ts +0 -140
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachments.test.ts +0 -168
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/get.test.ts +0 -140
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/list.test.ts +0 -181
- package/.claude/worktrees/emails-list/tests/commands/emails/send.test.ts +0 -309
- package/.claude/worktrees/emails-list/tests/commands/segments/create.test.ts +0 -163
- package/.claude/worktrees/emails-list/tests/commands/segments/delete.test.ts +0 -182
- package/.claude/worktrees/emails-list/tests/commands/segments/get.test.ts +0 -137
- package/.claude/worktrees/emails-list/tests/commands/segments/list.test.ts +0 -173
- package/.claude/worktrees/emails-list/tests/commands/teams/list.test.ts +0 -63
- package/.claude/worktrees/emails-list/tests/commands/teams/remove.test.ts +0 -103
- package/.claude/worktrees/emails-list/tests/commands/teams/switch.test.ts +0 -96
- package/.claude/worktrees/emails-list/tests/commands/topics/create.test.ts +0 -191
- package/.claude/worktrees/emails-list/tests/commands/topics/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/topics/get.test.ts +0 -125
- package/.claude/worktrees/emails-list/tests/commands/topics/list.test.ts +0 -124
- package/.claude/worktrees/emails-list/tests/commands/topics/update.test.ts +0 -177
- package/.claude/worktrees/emails-list/tests/commands/webhooks/create.test.ts +0 -224
- package/.claude/worktrees/emails-list/tests/commands/webhooks/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/webhooks/get.test.ts +0 -125
- package/.claude/worktrees/emails-list/tests/commands/webhooks/list.test.ts +0 -177
- package/.claude/worktrees/emails-list/tests/commands/webhooks/update.test.ts +0 -206
- package/.claude/worktrees/emails-list/tests/commands/whoami.test.ts +0 -99
- package/.claude/worktrees/emails-list/tests/helpers.ts +0 -93
- package/.claude/worktrees/emails-list/tests/lib/client.test.ts +0 -71
- package/.claude/worktrees/emails-list/tests/lib/config.test.ts +0 -414
- package/.claude/worktrees/emails-list/tests/lib/files.test.ts +0 -65
- package/.claude/worktrees/emails-list/tests/lib/help-text.test.ts +0 -97
- package/.claude/worktrees/emails-list/tests/lib/output.test.ts +0 -127
- package/.claude/worktrees/emails-list/tests/lib/prompts.test.ts +0 -178
- package/.claude/worktrees/emails-list/tests/lib/spinner.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/lib/table.test.ts +0 -63
- package/.claude/worktrees/emails-list/tests/lib/tty.test.ts +0 -85
- package/.claude/worktrees/emails-list/tsconfig.json +0 -14
- package/.github/workflows/test-build-windows.yml +0 -44
|
@@ -3,13 +3,46 @@ on:
|
|
|
3
3
|
push:
|
|
4
4
|
tags:
|
|
5
5
|
- 'v*'
|
|
6
|
-
permissions:
|
|
7
|
-
contents: write
|
|
8
6
|
concurrency:
|
|
9
7
|
group: release
|
|
10
8
|
cancel-in-progress: false
|
|
11
9
|
jobs:
|
|
10
|
+
test-binary:
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
include:
|
|
15
|
+
- os: macos-latest
|
|
16
|
+
target: bun-darwin-arm64
|
|
17
|
+
binary: dist/resend
|
|
18
|
+
flags: "--bytecode"
|
|
19
|
+
- os: ubuntu-latest
|
|
20
|
+
target: bun-linux-x64
|
|
21
|
+
binary: dist/resend
|
|
22
|
+
flags: "--bytecode"
|
|
23
|
+
- os: windows-latest
|
|
24
|
+
target: bun-windows-x64
|
|
25
|
+
binary: dist/resend.exe
|
|
26
|
+
flags: ""
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v6
|
|
30
|
+
|
|
31
|
+
- uses: oven-sh/setup-bun@v2
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: bun install --frozen-lockfile
|
|
35
|
+
|
|
36
|
+
- name: Build binary
|
|
37
|
+
run: bun build --compile --minify --sourcemap ${{ matrix.flags }} src/cli.ts --target=${{ matrix.target }} --outfile ${{ matrix.binary }}
|
|
38
|
+
|
|
39
|
+
- name: Verify binary runs
|
|
40
|
+
run: ${{ matrix.binary }} --version
|
|
41
|
+
|
|
12
42
|
release:
|
|
43
|
+
needs: test-binary
|
|
44
|
+
permissions:
|
|
45
|
+
contents: write
|
|
13
46
|
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
14
47
|
steps:
|
|
15
48
|
- uses: actions/checkout@v6
|
|
@@ -64,12 +97,6 @@ jobs:
|
|
|
64
97
|
```pwsh
|
|
65
98
|
irm https://resend.com/install.ps1 | iex
|
|
66
99
|
```
|
|
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
100
|
files: |
|
|
74
101
|
dist/resend-darwin-arm64.tar.gz
|
|
75
102
|
dist/resend-darwin-x64.tar.gz
|
package/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Resend CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The official CLI for [Resend](https://resend.com).
|
|
4
|
+
|
|
5
|
+
Built for humans, AI agents, and CI/CD pipelines.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
██████╗ ███████╗███████╗███████╗███╗ ██╗██████╗
|
|
9
|
+
██╔══██╗██╔════╝██╔════╝██╔════╝████╗ ██║██╔══██╗
|
|
10
|
+
██████╔╝█████╗ ███████╗█████╗ ██╔██╗ ██║██║ ██║
|
|
11
|
+
██╔══██╗██╔══╝ ╚════██║██╔══╝ ██║╚██╗██║██║ ██║
|
|
12
|
+
██║ ██║███████╗███████║███████╗██║ ╚████║██████╔╝
|
|
13
|
+
╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═════╝
|
|
14
|
+
```
|
|
4
15
|
|
|
5
16
|
## Install
|
|
6
17
|
|
package/biome.json
CHANGED
package/bun.lock
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"@commander-js/extra-typings": "14.0.0",
|
|
10
10
|
"commander": "14.0.3",
|
|
11
11
|
"resend": "6.9.3",
|
|
12
|
-
"unicode-animations": "1.0.3",
|
|
13
12
|
},
|
|
14
13
|
"devDependencies": {
|
|
15
14
|
"@biomejs/biome": "2.4.6",
|
|
@@ -69,8 +68,6 @@
|
|
|
69
68
|
|
|
70
69
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
71
70
|
|
|
72
|
-
"unicode-animations": ["unicode-animations@1.0.3", "", { "dependencies": { "unicode-animations": "^1.0.1" }, "bin": { "unicode-animations": "scripts/demo.cjs" } }, "sha512-+klB2oWwcYZjYWhwP4Pr8UZffWDFVx6jKeIahE6z0QYyM2dwDeDPyn5nevCYbyotxvtT9lh21cVURO1RX0+YMg=="],
|
|
73
|
-
|
|
74
71
|
"uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
|
75
72
|
}
|
|
76
73
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resend-cli",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "The official CLI for Resend",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
"commander": "14.0.3",
|
|
33
33
|
"@commander-js/extra-typings": "14.0.0",
|
|
34
34
|
"resend": "6.9.3",
|
|
35
|
-
"@clack/prompts": "1.1.0"
|
|
36
|
-
"unicode-animations": "1.0.3"
|
|
35
|
+
"@clack/prompts": "1.1.0"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
38
|
"@biomejs/biome": "2.4.6",
|
package/src/cli.ts
CHANGED
|
@@ -15,11 +15,23 @@ import { teamsCommand } from './commands/teams/index';
|
|
|
15
15
|
import { topicsCommand } from './commands/topics/index';
|
|
16
16
|
import { webhooksCommand } from './commands/webhooks/index';
|
|
17
17
|
import { whoamiCommand } from './commands/whoami';
|
|
18
|
+
import { errorMessage, outputError } from './lib/output';
|
|
19
|
+
import { checkForUpdates } from './lib/update-check';
|
|
18
20
|
import { PACKAGE_NAME, VERSION } from './lib/version';
|
|
19
21
|
|
|
22
|
+
const BANNER = `
|
|
23
|
+
██████╗ ███████╗███████╗███████╗███╗ ██╗██████╗
|
|
24
|
+
██╔══██╗██╔════╝██╔════╝██╔════╝████╗ ██║██╔══██╗
|
|
25
|
+
██████╔╝█████╗ ███████╗█████╗ ██╔██╗ ██║██║ ██║
|
|
26
|
+
██╔══██╗██╔══╝ ╚════██║██╔══╝ ██║╚██╗██║██║ ██║
|
|
27
|
+
██║ ██║███████╗███████║███████╗██║ ╚████║██████╔╝
|
|
28
|
+
╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═════╝
|
|
29
|
+
`;
|
|
30
|
+
|
|
20
31
|
const program = new Command()
|
|
21
32
|
.name('resend')
|
|
22
33
|
.description('Resend CLI — email for developers')
|
|
34
|
+
.addHelpText('beforeAll', BANNER)
|
|
23
35
|
.version(
|
|
24
36
|
`${PACKAGE_NAME} v${VERSION}`,
|
|
25
37
|
'-v, --version',
|
|
@@ -50,10 +62,8 @@ Output:
|
|
|
50
62
|
Errors always exit with code 1: {"error":{"message":"...","code":"..."}}
|
|
51
63
|
|
|
52
64
|
Examples:
|
|
53
|
-
$ resend login
|
|
54
|
-
$ resend emails send
|
|
55
|
-
$ resend emails batch --file ./emails.json --json
|
|
56
|
-
$ resend doctor --json`,
|
|
65
|
+
$ resend login
|
|
66
|
+
$ resend emails send`,
|
|
57
67
|
)
|
|
58
68
|
.addCommand(loginCommand)
|
|
59
69
|
.addCommand(logoutCommand)
|
|
@@ -71,4 +81,12 @@ Examples:
|
|
|
71
81
|
.addCommand(openCommand)
|
|
72
82
|
.addCommand(whoamiCommand);
|
|
73
83
|
|
|
74
|
-
program
|
|
84
|
+
program
|
|
85
|
+
.parseAsync()
|
|
86
|
+
.then(() => checkForUpdates().catch(() => {}))
|
|
87
|
+
.catch((err) => {
|
|
88
|
+
outputError({
|
|
89
|
+
message: errorMessage(err, 'An unexpected error occurred'),
|
|
90
|
+
code: 'unexpected_error',
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -133,11 +133,7 @@ export const loginCommand = new Command('login')
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
const spinner = createSpinner(
|
|
137
|
-
'Validating API key...',
|
|
138
|
-
'braille',
|
|
139
|
-
globalOpts.quiet,
|
|
140
|
-
);
|
|
136
|
+
const spinner = createSpinner('Validating API key...', globalOpts.quiet);
|
|
141
137
|
|
|
142
138
|
try {
|
|
143
139
|
const resend = new Resend(apiKey);
|
|
@@ -190,7 +186,7 @@ export const loginCommand = new Command('login')
|
|
|
190
186
|
if (choice === '__new__') {
|
|
191
187
|
const newName = await p.text({
|
|
192
188
|
message: 'Enter a name for the new team:',
|
|
193
|
-
validate: (v) => validateTeamName(v),
|
|
189
|
+
validate: (v) => validateTeamName(v as string),
|
|
194
190
|
});
|
|
195
191
|
if (p.isCancel(newName)) {
|
|
196
192
|
cancelAndExit('Login cancelled.');
|
|
@@ -211,8 +207,14 @@ export const loginCommand = new Command('login')
|
|
|
211
207
|
if (teamName) {
|
|
212
208
|
try {
|
|
213
209
|
setActiveTeam(teamName);
|
|
214
|
-
} catch {
|
|
215
|
-
|
|
210
|
+
} catch (err) {
|
|
211
|
+
outputError(
|
|
212
|
+
{
|
|
213
|
+
message: errorMessage(err, 'Failed to switch team'),
|
|
214
|
+
code: 'switch_failed',
|
|
215
|
+
},
|
|
216
|
+
{ json: globalOpts.json },
|
|
217
|
+
);
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
220
|
|
package/src/commands/doctor.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
1
|
import { Command } from '@commander-js/extra-typings';
|
|
5
2
|
import { Resend } from 'resend';
|
|
6
3
|
import type { GlobalOpts } from '../lib/client';
|
|
@@ -9,7 +6,8 @@ import { buildHelpText } from '../lib/help-text';
|
|
|
9
6
|
import { errorMessage, outputResult } from '../lib/output';
|
|
10
7
|
import { createSpinner } from '../lib/spinner';
|
|
11
8
|
import { isInteractive } from '../lib/tty';
|
|
12
|
-
import {
|
|
9
|
+
import { GITHUB_RELEASES_URL } from '../lib/update-check';
|
|
10
|
+
import { VERSION } from '../lib/version';
|
|
13
11
|
|
|
14
12
|
type CheckStatus = 'pass' | 'warn' | 'fail';
|
|
15
13
|
|
|
@@ -20,21 +18,12 @@ type CheckResult = {
|
|
|
20
18
|
detail?: string;
|
|
21
19
|
};
|
|
22
20
|
|
|
23
|
-
const statusIcons: Record<CheckStatus, string> = {
|
|
24
|
-
pass: '\x1B[32m✔\x1B[0m',
|
|
25
|
-
warn: '\x1B[33m!\x1B[0m',
|
|
26
|
-
fail: '\x1B[31m✗\x1B[0m',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
21
|
async function checkCliVersion(): Promise<CheckResult> {
|
|
30
22
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
signal: AbortSignal.timeout(5000),
|
|
36
|
-
},
|
|
37
|
-
);
|
|
23
|
+
const res = await fetch(GITHUB_RELEASES_URL, {
|
|
24
|
+
headers: { Accept: 'application/vnd.github.v3+json' },
|
|
25
|
+
signal: AbortSignal.timeout(5000),
|
|
26
|
+
});
|
|
38
27
|
if (!res.ok) {
|
|
39
28
|
return {
|
|
40
29
|
name: 'CLI Version',
|
|
@@ -42,8 +31,19 @@ async function checkCliVersion(): Promise<CheckResult> {
|
|
|
42
31
|
message: `v${VERSION} (could not check for updates)`,
|
|
43
32
|
};
|
|
44
33
|
}
|
|
45
|
-
const data = (await res.json()) as {
|
|
46
|
-
|
|
34
|
+
const data = (await res.json()) as {
|
|
35
|
+
tag_name?: string;
|
|
36
|
+
prerelease?: boolean;
|
|
37
|
+
draft?: boolean;
|
|
38
|
+
};
|
|
39
|
+
if (data.prerelease || data.draft) {
|
|
40
|
+
return {
|
|
41
|
+
name: 'CLI Version',
|
|
42
|
+
status: 'warn',
|
|
43
|
+
message: `v${VERSION} (could not check for updates)`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const latest = data.tag_name?.replace(/^v/, '') ?? 'unknown';
|
|
47
47
|
if (latest === VERSION) {
|
|
48
48
|
return {
|
|
49
49
|
name: 'CLI Version',
|
|
@@ -84,8 +84,10 @@ function checkApiKeyPresence(flagValue?: string): CheckResult {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async function checkApiValidationAndDomains(
|
|
88
|
-
|
|
87
|
+
async function checkApiValidationAndDomains(
|
|
88
|
+
flagValue?: string,
|
|
89
|
+
): Promise<CheckResult> {
|
|
90
|
+
const resolved = resolveApiKey(flagValue);
|
|
89
91
|
if (!resolved) {
|
|
90
92
|
return {
|
|
91
93
|
name: 'API Validation',
|
|
@@ -143,73 +145,8 @@ async function checkApiValidationAndDomains(): Promise<CheckResult> {
|
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
function checkAgentDetection(): CheckResult {
|
|
147
|
-
const home = homedir();
|
|
148
|
-
const agents: { name: string; found: boolean }[] = [];
|
|
149
|
-
|
|
150
|
-
// OpenClaw
|
|
151
|
-
agents.push({
|
|
152
|
-
name: 'OpenClaw',
|
|
153
|
-
found: existsSync(join(home, 'clawd', 'skills')),
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Cursor
|
|
157
|
-
agents.push({ name: 'Cursor', found: existsSync(join(home, '.cursor')) });
|
|
158
|
-
|
|
159
|
-
// Claude Desktop
|
|
160
|
-
const claudeConfigPaths =
|
|
161
|
-
process.platform === 'darwin'
|
|
162
|
-
? [
|
|
163
|
-
join(
|
|
164
|
-
home,
|
|
165
|
-
'Library',
|
|
166
|
-
'Application Support',
|
|
167
|
-
'Claude',
|
|
168
|
-
'claude_desktop_config.json',
|
|
169
|
-
),
|
|
170
|
-
]
|
|
171
|
-
: process.platform === 'win32'
|
|
172
|
-
? [
|
|
173
|
-
join(
|
|
174
|
-
process.env.APPDATA ?? '',
|
|
175
|
-
'Claude',
|
|
176
|
-
'claude_desktop_config.json',
|
|
177
|
-
),
|
|
178
|
-
]
|
|
179
|
-
: [join(home, '.config', 'Claude', 'claude_desktop_config.json')];
|
|
180
|
-
agents.push({
|
|
181
|
-
name: 'Claude Desktop',
|
|
182
|
-
found: claudeConfigPaths.some(existsSync),
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// VS Code MCP
|
|
186
|
-
agents.push({
|
|
187
|
-
name: 'VS Code',
|
|
188
|
-
found: existsSync(join(process.cwd(), '.vscode', 'mcp.json')),
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const detected = agents.filter((a) => a.found);
|
|
192
|
-
|
|
193
|
-
if (detected.length === 0) {
|
|
194
|
-
return {
|
|
195
|
-
name: 'AI Agents',
|
|
196
|
-
status: 'pass',
|
|
197
|
-
message: 'No AI agents detected',
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
name: 'AI Agents',
|
|
203
|
-
status: 'pass',
|
|
204
|
-
message: `Detected: ${detected.map((a) => a.name).join(', ')}`,
|
|
205
|
-
detail: 'Future: run `resend setup <agent>` to configure integration',
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
148
|
export const doctorCommand = new Command('doctor')
|
|
210
|
-
.description(
|
|
211
|
-
'Check CLI version, API key, domain status, and AI agent detection',
|
|
212
|
-
)
|
|
149
|
+
.description('Check CLI version, API key, and domain status')
|
|
213
150
|
.addHelpText(
|
|
214
151
|
'after',
|
|
215
152
|
buildHelpText({
|
|
@@ -217,9 +154,8 @@ export const doctorCommand = new Command('doctor')
|
|
|
217
154
|
context: `Checks performed:
|
|
218
155
|
CLI Version Is the installed version up to date?
|
|
219
156
|
API Key Is a key present (--api-key, RESEND_API_KEY, or credentials file)?
|
|
220
|
-
API Validation Is the key valid and accepted by the Resend API
|
|
221
|
-
|
|
222
|
-
output: ` {\n "ok": true,\n "checks": [\n {"name":"CLI Version","status":"pass","message":"v0.1.0 (latest)"},\n {"name":"API Key","status":"pass","message":"re_...abcd (source: env)"},\n {"name":"Domains","status":"pass","message":"1 verified, 0 pending"},\n {"name":"AI Agents","status":"pass","message":"Detected: Claude Desktop"}\n ]\n }\n status values: "pass" | "warn" | "fail"\n Exit code 1 if any check has status "fail"`,
|
|
157
|
+
API Validation Is the key valid and accepted by the Resend API?`,
|
|
158
|
+
output: ` {\n "ok": true,\n "checks": [\n {"name":"CLI Version","status":"pass","message":"v0.1.0 (latest)"},\n {"name":"API Key","status":"pass","message":"re_...abcd (source: env)"},\n {"name":"Domains","status":"pass","message":"1 verified, 0 pending"}\n ]\n }\n status values: "pass" | "warn" | "fail"\n Exit code 1 if any check has status "fail"`,
|
|
223
159
|
examples: ['resend doctor', 'resend doctor --json'],
|
|
224
160
|
}),
|
|
225
161
|
)
|
|
@@ -233,9 +169,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
233
169
|
}
|
|
234
170
|
|
|
235
171
|
// Check 1: CLI Version
|
|
236
|
-
let spinner = interactive
|
|
237
|
-
? createSpinner('Checking CLI version...', 'orbit')
|
|
238
|
-
: null;
|
|
172
|
+
let spinner = interactive ? createSpinner('Checking CLI version...') : null;
|
|
239
173
|
const versionCheck = await checkCliVersion();
|
|
240
174
|
checks.push(versionCheck);
|
|
241
175
|
if (versionCheck.status === 'warn') {
|
|
@@ -245,7 +179,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
245
179
|
}
|
|
246
180
|
|
|
247
181
|
// Check 2: API Key
|
|
248
|
-
spinner = interactive ? createSpinner('Checking API key...'
|
|
182
|
+
spinner = interactive ? createSpinner('Checking API key...') : null;
|
|
249
183
|
const keyCheck = checkApiKeyPresence(globalOpts.apiKey);
|
|
250
184
|
checks.push(keyCheck);
|
|
251
185
|
if (keyCheck.status === 'fail') {
|
|
@@ -256,9 +190,9 @@ export const doctorCommand = new Command('doctor')
|
|
|
256
190
|
|
|
257
191
|
// Check 3: API Validation + Domains
|
|
258
192
|
spinner = interactive
|
|
259
|
-
? createSpinner('Validating API key & domains...'
|
|
193
|
+
? createSpinner('Validating API key & domains...')
|
|
260
194
|
: null;
|
|
261
|
-
const domainCheck = await checkApiValidationAndDomains();
|
|
195
|
+
const domainCheck = await checkApiValidationAndDomains(globalOpts.apiKey);
|
|
262
196
|
checks.push(domainCheck);
|
|
263
197
|
if (domainCheck.status === 'fail') {
|
|
264
198
|
spinner?.fail(domainCheck.message);
|
|
@@ -268,26 +202,10 @@ export const doctorCommand = new Command('doctor')
|
|
|
268
202
|
spinner?.stop(domainCheck.message);
|
|
269
203
|
}
|
|
270
204
|
|
|
271
|
-
// Check 4: Agent Detection
|
|
272
|
-
spinner = interactive
|
|
273
|
-
? createSpinner('Detecting AI agents...', 'scan')
|
|
274
|
-
: null;
|
|
275
|
-
const agentCheck = checkAgentDetection();
|
|
276
|
-
checks.push(agentCheck);
|
|
277
|
-
spinner?.stop(agentCheck.message);
|
|
278
|
-
|
|
279
205
|
const hasFails = checks.some((c) => c.status === 'fail');
|
|
280
206
|
|
|
281
207
|
if (!globalOpts.json && isInteractive()) {
|
|
282
208
|
console.log('');
|
|
283
|
-
for (const check of checks) {
|
|
284
|
-
const icon = statusIcons[check.status];
|
|
285
|
-
console.log(` ${icon} ${check.name}: ${check.message}`);
|
|
286
|
-
if (check.detail) {
|
|
287
|
-
console.log(` ${check.detail}`);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
console.log('');
|
|
291
209
|
} else {
|
|
292
210
|
outputResult({ ok: !hasFails, checks }, { json: globalOpts.json });
|
|
293
211
|
}
|
package/src/lib/client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Resend } from 'resend';
|
|
2
2
|
import { resolveApiKey } from './config';
|
|
3
3
|
import { errorMessage, outputError } from './output';
|
|
4
|
+
import { VERSION } from './version';
|
|
4
5
|
|
|
5
6
|
export type GlobalOpts = {
|
|
6
7
|
apiKey?: string;
|
|
@@ -9,6 +10,8 @@ export type GlobalOpts = {
|
|
|
9
10
|
team?: string;
|
|
10
11
|
};
|
|
11
12
|
|
|
13
|
+
process.env.RESEND_USER_AGENT = `resend-cli:${VERSION}`;
|
|
14
|
+
|
|
12
15
|
export function createClient(flagValue?: string, teamName?: string): Resend {
|
|
13
16
|
const resolved = resolveApiKey(flagValue, teamName);
|
|
14
17
|
if (!resolved) {
|
package/src/lib/config.ts
CHANGED
|
@@ -203,8 +203,8 @@ export function validateTeamName(name: string): string | undefined {
|
|
|
203
203
|
if (name.length > 64) {
|
|
204
204
|
return 'Team name must be 64 characters or fewer';
|
|
205
205
|
}
|
|
206
|
-
if (!/^[a-
|
|
207
|
-
return 'Team name must contain only
|
|
206
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
207
|
+
return 'Team name must contain only letters, numbers, dashes, and underscores';
|
|
208
208
|
}
|
|
209
209
|
return undefined;
|
|
210
210
|
}
|
|
@@ -215,4 +215,3 @@ export function maskKey(key: string): string {
|
|
|
215
215
|
}
|
|
216
216
|
return `${key.slice(0, 3)}...${key.slice(-4)}`;
|
|
217
217
|
}
|
|
218
|
-
|
package/src/lib/spinner.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import spinners from 'unicode-animations';
|
|
2
1
|
import type { GlobalOpts } from './client';
|
|
3
2
|
import { errorMessage, outputError } from './output';
|
|
4
3
|
import { isInteractive, isUnicodeSupported } from './tty';
|
|
@@ -9,6 +8,19 @@ const TICK = isUnicodeSupported ? String.fromCodePoint(0x2714) : 'v'; // ✔
|
|
|
9
8
|
const WARN = isUnicodeSupported ? String.fromCodePoint(0x26a0) : '!'; // ⚠
|
|
10
9
|
const CROSS = isUnicodeSupported ? String.fromCodePoint(0x2717) : 'x'; // ✗
|
|
11
10
|
|
|
11
|
+
// Braille spinner: cycle through U+2800-block dot patterns.
|
|
12
|
+
const SPINNER_FRAMES = [
|
|
13
|
+
'\u2839',
|
|
14
|
+
'\u2838',
|
|
15
|
+
'\u2834',
|
|
16
|
+
'\u2826',
|
|
17
|
+
'\u2807',
|
|
18
|
+
'\u280F',
|
|
19
|
+
'\u2819',
|
|
20
|
+
'\u2839',
|
|
21
|
+
];
|
|
22
|
+
const SPINNER_INTERVAL = 80;
|
|
23
|
+
|
|
12
24
|
type SdkResponse<T> = { data: T | null; error: { message: string } | null };
|
|
13
25
|
|
|
14
26
|
/**
|
|
@@ -21,7 +33,7 @@ export async function withSpinner<T>(
|
|
|
21
33
|
errorCode: string,
|
|
22
34
|
globalOpts: GlobalOpts,
|
|
23
35
|
): Promise<T> {
|
|
24
|
-
const spinner = createSpinner(messages.loading,
|
|
36
|
+
const spinner = createSpinner(messages.loading, globalOpts.quiet);
|
|
25
37
|
try {
|
|
26
38
|
const { data, error } = await call();
|
|
27
39
|
if (error) {
|
|
@@ -49,13 +61,7 @@ export async function withSpinner<T>(
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
export function createSpinner(
|
|
55
|
-
message: string,
|
|
56
|
-
name: SpinnerName = 'braille',
|
|
57
|
-
quiet?: boolean,
|
|
58
|
-
) {
|
|
64
|
+
export function createSpinner(message: string, quiet?: boolean) {
|
|
59
65
|
if (quiet || !isInteractive()) {
|
|
60
66
|
return {
|
|
61
67
|
update(_msg: string) {},
|
|
@@ -65,7 +71,8 @@ export function createSpinner(
|
|
|
65
71
|
};
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
const
|
|
74
|
+
const frames = isUnicodeSupported ? SPINNER_FRAMES : ['-', '\\', '|', '/'];
|
|
75
|
+
const interval = SPINNER_INTERVAL;
|
|
69
76
|
let i = 0;
|
|
70
77
|
let text = message;
|
|
71
78
|
|