resend-cli 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -10
- package/dist/cli.cjs +562 -0
- package/package.json +31 -12
- package/.claude/settings.local.json +0 -5
- package/.github/scripts/pr-title-check.js +0 -34
- package/.github/workflows/ci.yml +0 -32
- package/.github/workflows/pr-title-check.yml +0 -13
- package/.github/workflows/release.yml +0 -120
- package/.github/workflows/test-install-windows.yml +0 -48
- package/CHANGELOG.md +0 -31
- package/biome.json +0 -36
- package/bun.lock +0 -73
- package/bunfig.toml +0 -2
- package/docs/agent-dx-gaps.md +0 -167
- package/docs/missing-commands.md +0 -58
- package/docs/production-readiness.md +0 -99
- package/docs/secure-key-storage.md +0 -174
- package/install.ps1 +0 -141
- package/install.sh +0 -301
- package/renovate.json +0 -4
- package/src/cli.ts +0 -92
- package/src/commands/api-keys/create.ts +0 -114
- package/src/commands/api-keys/delete.ts +0 -47
- package/src/commands/api-keys/index.ts +0 -26
- package/src/commands/api-keys/list.ts +0 -35
- package/src/commands/api-keys/utils.ts +0 -8
- package/src/commands/auth/index.ts +0 -20
- package/src/commands/auth/login.ts +0 -234
- package/src/commands/auth/logout.ts +0 -105
- package/src/commands/broadcasts/create.ts +0 -196
- package/src/commands/broadcasts/delete.ts +0 -46
- package/src/commands/broadcasts/get.ts +0 -59
- package/src/commands/broadcasts/index.ts +0 -43
- package/src/commands/broadcasts/list.ts +0 -60
- package/src/commands/broadcasts/send.ts +0 -56
- package/src/commands/broadcasts/update.ts +0 -95
- package/src/commands/broadcasts/utils.ts +0 -35
- package/src/commands/contact-properties/create.ts +0 -118
- package/src/commands/contact-properties/delete.ts +0 -48
- package/src/commands/contact-properties/get.ts +0 -46
- package/src/commands/contact-properties/index.ts +0 -48
- package/src/commands/contact-properties/list.ts +0 -68
- package/src/commands/contact-properties/update.ts +0 -88
- package/src/commands/contact-properties/utils.ts +0 -17
- package/src/commands/contacts/add-segment.ts +0 -78
- package/src/commands/contacts/create.ts +0 -122
- package/src/commands/contacts/delete.ts +0 -49
- package/src/commands/contacts/get.ts +0 -53
- package/src/commands/contacts/index.ts +0 -58
- package/src/commands/contacts/list.ts +0 -57
- package/src/commands/contacts/remove-segment.ts +0 -48
- package/src/commands/contacts/segments.ts +0 -39
- package/src/commands/contacts/topics.ts +0 -45
- package/src/commands/contacts/update-topics.ts +0 -90
- package/src/commands/contacts/update.ts +0 -77
- package/src/commands/contacts/utils.ts +0 -119
- package/src/commands/doctor.ts +0 -216
- package/src/commands/domains/create.ts +0 -83
- package/src/commands/domains/delete.ts +0 -42
- package/src/commands/domains/get.ts +0 -47
- package/src/commands/domains/index.ts +0 -35
- package/src/commands/domains/list.ts +0 -53
- package/src/commands/domains/update.ts +0 -75
- package/src/commands/domains/utils.ts +0 -44
- package/src/commands/domains/verify.ts +0 -38
- package/src/commands/emails/batch.ts +0 -140
- package/src/commands/emails/index.ts +0 -24
- package/src/commands/emails/receiving/attachment.ts +0 -55
- package/src/commands/emails/receiving/attachments.ts +0 -68
- package/src/commands/emails/receiving/get.ts +0 -58
- package/src/commands/emails/receiving/index.ts +0 -28
- package/src/commands/emails/receiving/list.ts +0 -59
- package/src/commands/emails/receiving/utils.ts +0 -38
- package/src/commands/emails/send.ts +0 -189
- package/src/commands/open.ts +0 -24
- package/src/commands/segments/create.ts +0 -50
- package/src/commands/segments/delete.ts +0 -47
- package/src/commands/segments/get.ts +0 -38
- package/src/commands/segments/index.ts +0 -36
- package/src/commands/segments/list.ts +0 -58
- package/src/commands/segments/utils.ts +0 -7
- package/src/commands/teams/index.ts +0 -10
- package/src/commands/teams/list.ts +0 -35
- package/src/commands/teams/remove.ts +0 -86
- package/src/commands/teams/switch.ts +0 -76
- package/src/commands/topics/create.ts +0 -73
- package/src/commands/topics/delete.ts +0 -47
- package/src/commands/topics/get.ts +0 -42
- package/src/commands/topics/index.ts +0 -42
- package/src/commands/topics/list.ts +0 -34
- package/src/commands/topics/update.ts +0 -59
- package/src/commands/topics/utils.ts +0 -16
- package/src/commands/webhooks/create.ts +0 -128
- package/src/commands/webhooks/delete.ts +0 -49
- package/src/commands/webhooks/get.ts +0 -42
- package/src/commands/webhooks/index.ts +0 -44
- package/src/commands/webhooks/list.ts +0 -55
- package/src/commands/webhooks/update.ts +0 -83
- package/src/commands/webhooks/utils.ts +0 -36
- package/src/commands/whoami.ts +0 -71
- package/src/lib/actions.ts +0 -157
- package/src/lib/client.ts +0 -37
- package/src/lib/config.ts +0 -217
- package/src/lib/files.ts +0 -15
- package/src/lib/help-text.ts +0 -38
- package/src/lib/output.ts +0 -54
- package/src/lib/pagination.ts +0 -36
- package/src/lib/prompts.ts +0 -149
- package/src/lib/spinner.ts +0 -100
- package/src/lib/table.ts +0 -57
- package/src/lib/tty.ts +0 -28
- package/src/lib/update-check.ts +0 -172
- package/src/lib/version.ts +0 -4
- package/tests/commands/api-keys/create.test.ts +0 -195
- package/tests/commands/api-keys/delete.test.ts +0 -156
- package/tests/commands/api-keys/list.test.ts +0 -133
- package/tests/commands/auth/login.test.ts +0 -156
- package/tests/commands/auth/logout.test.ts +0 -146
- package/tests/commands/broadcasts/create.test.ts +0 -447
- package/tests/commands/broadcasts/delete.test.ts +0 -182
- package/tests/commands/broadcasts/get.test.ts +0 -146
- package/tests/commands/broadcasts/list.test.ts +0 -196
- package/tests/commands/broadcasts/send.test.ts +0 -161
- package/tests/commands/broadcasts/update.test.ts +0 -283
- package/tests/commands/contact-properties/create.test.ts +0 -250
- package/tests/commands/contact-properties/delete.test.ts +0 -183
- package/tests/commands/contact-properties/get.test.ts +0 -144
- package/tests/commands/contact-properties/list.test.ts +0 -180
- package/tests/commands/contact-properties/update.test.ts +0 -216
- package/tests/commands/contacts/add-segment.test.ts +0 -188
- package/tests/commands/contacts/create.test.ts +0 -270
- package/tests/commands/contacts/delete.test.ts +0 -192
- package/tests/commands/contacts/get.test.ts +0 -148
- package/tests/commands/contacts/list.test.ts +0 -175
- package/tests/commands/contacts/remove-segment.test.ts +0 -166
- package/tests/commands/contacts/segments.test.ts +0 -167
- package/tests/commands/contacts/topics.test.ts +0 -163
- package/tests/commands/contacts/update-topics.test.ts +0 -247
- package/tests/commands/contacts/update.test.ts +0 -205
- package/tests/commands/doctor.test.ts +0 -165
- package/tests/commands/domains/create.test.ts +0 -192
- package/tests/commands/domains/delete.test.ts +0 -156
- package/tests/commands/domains/get.test.ts +0 -137
- package/tests/commands/domains/list.test.ts +0 -164
- package/tests/commands/domains/update.test.ts +0 -223
- package/tests/commands/domains/verify.test.ts +0 -117
- package/tests/commands/emails/batch.test.ts +0 -313
- package/tests/commands/emails/receiving/attachment.test.ts +0 -140
- package/tests/commands/emails/receiving/attachments.test.ts +0 -168
- package/tests/commands/emails/receiving/get.test.ts +0 -140
- package/tests/commands/emails/receiving/list.test.ts +0 -181
- package/tests/commands/emails/send.test.ts +0 -309
- package/tests/commands/segments/create.test.ts +0 -163
- package/tests/commands/segments/delete.test.ts +0 -182
- package/tests/commands/segments/get.test.ts +0 -137
- package/tests/commands/segments/list.test.ts +0 -173
- package/tests/commands/teams/list.test.ts +0 -63
- package/tests/commands/teams/remove.test.ts +0 -103
- package/tests/commands/teams/switch.test.ts +0 -96
- package/tests/commands/topics/create.test.ts +0 -191
- package/tests/commands/topics/delete.test.ts +0 -156
- package/tests/commands/topics/get.test.ts +0 -125
- package/tests/commands/topics/list.test.ts +0 -124
- package/tests/commands/topics/update.test.ts +0 -177
- package/tests/commands/webhooks/create.test.ts +0 -224
- package/tests/commands/webhooks/delete.test.ts +0 -156
- package/tests/commands/webhooks/get.test.ts +0 -125
- package/tests/commands/webhooks/list.test.ts +0 -177
- package/tests/commands/webhooks/update.test.ts +0 -206
- package/tests/commands/whoami.test.ts +0 -99
- package/tests/helpers.ts +0 -93
- package/tests/lib/client.test.ts +0 -71
- package/tests/lib/config.test.ts +0 -445
- package/tests/lib/files.test.ts +0 -65
- package/tests/lib/help-text.test.ts +0 -97
- package/tests/lib/output.test.ts +0 -127
- package/tests/lib/prompts.test.ts +0 -178
- package/tests/lib/spinner.test.ts +0 -146
- package/tests/lib/table.test.ts +0 -63
- package/tests/lib/tty.test.ts +0 -85
- package/tests/lib/update-check.test.ts +0 -169
- package/tsconfig.json +0 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resend-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "The official CLI for Resend",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -14,29 +14,48 @@
|
|
|
14
14
|
"email",
|
|
15
15
|
"api"
|
|
16
16
|
],
|
|
17
|
-
"
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"packageManager": "pnpm@10.11.0",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/cli.cjs"
|
|
23
|
+
],
|
|
18
24
|
"type": "module",
|
|
19
25
|
"bin": {
|
|
20
|
-
"resend": "./
|
|
26
|
+
"resend": "./dist/cli.cjs"
|
|
21
27
|
},
|
|
22
28
|
"scripts": {
|
|
23
|
-
"dev": "
|
|
24
|
-
"dev:watch": "
|
|
25
|
-
"build": "
|
|
29
|
+
"dev": "tsx src/cli.ts",
|
|
30
|
+
"dev:watch": "tsx --watch src/cli.ts",
|
|
31
|
+
"build": "esbuild src/cli.ts --bundle --platform=node --format=cjs --minify --outfile=dist/cli.cjs",
|
|
32
|
+
"build:bin": "pnpm build && pkg dist/cli.cjs --compress Brotli --target node24 --output dist/resend",
|
|
26
33
|
"lint": "biome check",
|
|
27
34
|
"format": "biome format --write",
|
|
28
35
|
"typecheck": "tsc --noEmit",
|
|
29
|
-
"test": "
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:e2e": "vitest run --config vitest.config.e2e.ts"
|
|
30
38
|
},
|
|
31
39
|
"dependencies": {
|
|
32
|
-
"
|
|
40
|
+
"@clack/prompts": "1.1.0",
|
|
33
41
|
"@commander-js/extra-typings": "14.0.0",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
42
|
+
"commander": "14.0.3",
|
|
43
|
+
"picocolors": "^1.1.1",
|
|
44
|
+
"resend": "6.9.3"
|
|
45
|
+
},
|
|
46
|
+
"pnpm": {
|
|
47
|
+
"onlyBuiltDependencies": [
|
|
48
|
+
"esbuild",
|
|
49
|
+
"@biomejs/biome"
|
|
50
|
+
]
|
|
36
51
|
},
|
|
37
52
|
"devDependencies": {
|
|
38
53
|
"@biomejs/biome": "2.4.6",
|
|
39
|
-
"@types/
|
|
40
|
-
"
|
|
54
|
+
"@types/node": "22.19.15",
|
|
55
|
+
"esbuild": "0.27.3",
|
|
56
|
+
"tsx": "4.19.4",
|
|
57
|
+
"typescript": "5.9.3",
|
|
58
|
+
"vitest": "3.1.4",
|
|
59
|
+
"@yao-pkg/pkg": "6.14.1"
|
|
41
60
|
}
|
|
42
61
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
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);
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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@v6
|
|
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@v6
|
|
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@v6
|
|
30
|
-
- uses: oven-sh/setup-bun@v2
|
|
31
|
-
- run: bun install --frozen-lockfile
|
|
32
|
-
- run: bun run test
|
|
@@ -1,13 +0,0 @@
|
|
|
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@v6
|
|
13
|
-
- run: node .github/scripts/pr-title-check.js
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
tags:
|
|
5
|
-
- 'v*'
|
|
6
|
-
concurrency:
|
|
7
|
-
group: release
|
|
8
|
-
cancel-in-progress: false
|
|
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
|
-
|
|
42
|
-
release:
|
|
43
|
-
needs: test-binary
|
|
44
|
-
permissions:
|
|
45
|
-
contents: write
|
|
46
|
-
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
47
|
-
steps:
|
|
48
|
-
- uses: actions/checkout@v6
|
|
49
|
-
|
|
50
|
-
- uses: oven-sh/setup-bun@v2
|
|
51
|
-
|
|
52
|
-
- name: Install dependencies
|
|
53
|
-
run: bun install --frozen-lockfile
|
|
54
|
-
|
|
55
|
-
- name: Build binaries
|
|
56
|
-
run: |
|
|
57
|
-
bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-darwin-arm64 --outfile dist/resend-darwin-arm64
|
|
58
|
-
bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-darwin-x64 --outfile dist/resend-darwin-x64
|
|
59
|
-
bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-linux-x64 --outfile dist/resend-linux-x64
|
|
60
|
-
bun build --compile --minify --sourcemap --bytecode src/cli.ts --target=bun-linux-arm64 --outfile dist/resend-linux-arm64
|
|
61
|
-
bun build --compile --minify --sourcemap src/cli.ts --target=bun-windows-x64 --outfile dist/resend-windows-x64.exe
|
|
62
|
-
|
|
63
|
-
- name: Verify binaries
|
|
64
|
-
run: |
|
|
65
|
-
for f in dist/resend-darwin-arm64 dist/resend-darwin-x64 \
|
|
66
|
-
dist/resend-linux-x64 dist/resend-linux-arm64 \
|
|
67
|
-
dist/resend-windows-x64.exe; do
|
|
68
|
-
[ -s "$f" ] || { echo "Missing or empty: $f"; exit 1; }
|
|
69
|
-
done
|
|
70
|
-
|
|
71
|
-
- name: Package archives
|
|
72
|
-
run: |
|
|
73
|
-
cd dist
|
|
74
|
-
for bin in resend-darwin-arm64 resend-darwin-x64 resend-linux-x64 resend-linux-arm64; do
|
|
75
|
-
cp "$bin" resend
|
|
76
|
-
chmod +x resend
|
|
77
|
-
tar -czf "${bin}.tar.gz" resend
|
|
78
|
-
rm resend
|
|
79
|
-
done
|
|
80
|
-
cp resend-windows-x64.exe resend.exe
|
|
81
|
-
zip resend-windows-x64.zip resend.exe
|
|
82
|
-
rm resend.exe
|
|
83
|
-
|
|
84
|
-
- name: Create GitHub Release
|
|
85
|
-
uses: softprops/action-gh-release@v2
|
|
86
|
-
with:
|
|
87
|
-
generate_release_notes: true
|
|
88
|
-
body: |
|
|
89
|
-
## Install
|
|
90
|
-
|
|
91
|
-
**macOS / Linux**
|
|
92
|
-
```sh
|
|
93
|
-
curl -fsSL https://resend.com/install.sh | bash
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Windows (PowerShell)**
|
|
97
|
-
```pwsh
|
|
98
|
-
irm https://resend.com/install.ps1 | iex
|
|
99
|
-
```
|
|
100
|
-
files: |
|
|
101
|
-
dist/resend-darwin-arm64.tar.gz
|
|
102
|
-
dist/resend-darwin-x64.tar.gz
|
|
103
|
-
dist/resend-linux-x64.tar.gz
|
|
104
|
-
dist/resend-linux-arm64.tar.gz
|
|
105
|
-
dist/resend-windows-x64.zip
|
|
106
|
-
|
|
107
|
-
notify-tap:
|
|
108
|
-
name: Trigger Homebrew Tap Update
|
|
109
|
-
needs: release
|
|
110
|
-
runs-on: ubuntu-latest
|
|
111
|
-
permissions:
|
|
112
|
-
contents: read
|
|
113
|
-
steps:
|
|
114
|
-
- name: Dispatch publish-release workflow on tap
|
|
115
|
-
env:
|
|
116
|
-
GH_TOKEN: ${{ secrets.RELEASE_CLI_ON_HOMEBREW }}
|
|
117
|
-
run: |
|
|
118
|
-
gh workflow run publish-release.yml \
|
|
119
|
-
--repo resend/homebrew-cli \
|
|
120
|
-
--field version="${GITHUB_REF_NAME}"
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
name: Test Windows Installer
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
paths:
|
|
6
|
-
- install.ps1
|
|
7
|
-
workflow_dispatch:
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
runs-on: windows-latest
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v6
|
|
14
|
-
|
|
15
|
-
- name: Run install.ps1 from repo
|
|
16
|
-
shell: pwsh
|
|
17
|
-
run: .\install.ps1
|
|
18
|
-
|
|
19
|
-
- name: Verify binary exists
|
|
20
|
-
shell: pwsh
|
|
21
|
-
run: |
|
|
22
|
-
$exe = "$env:USERPROFILE\.resend\bin\resend.exe"
|
|
23
|
-
if (-not (Test-Path $exe)) {
|
|
24
|
-
Write-Error "Binary not found at $exe"
|
|
25
|
-
exit 1
|
|
26
|
-
}
|
|
27
|
-
Write-Host "Found binary at $exe"
|
|
28
|
-
|
|
29
|
-
- name: Verify binary runs
|
|
30
|
-
shell: pwsh
|
|
31
|
-
run: |
|
|
32
|
-
$exe = "$env:USERPROFILE\.resend\bin\resend.exe"
|
|
33
|
-
& $exe --version
|
|
34
|
-
if ($LASTEXITCODE -ne 0) {
|
|
35
|
-
Write-Error "resend --version failed"
|
|
36
|
-
exit 1
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
- name: Verify PATH was updated
|
|
40
|
-
shell: pwsh
|
|
41
|
-
run: |
|
|
42
|
-
$binDir = "$env:USERPROFILE\.resend\bin"
|
|
43
|
-
$path = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
44
|
-
if ($path -notlike "*$binDir*") {
|
|
45
|
-
Write-Error "$binDir was not added to PATH"
|
|
46
|
-
exit 1
|
|
47
|
-
}
|
|
48
|
-
Write-Host "PATH correctly updated"
|
package/CHANGELOG.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
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
|
package/biome.json
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.4.6/schema.json",
|
|
3
|
-
"assist": {
|
|
4
|
-
"actions": {
|
|
5
|
-
"source": {
|
|
6
|
-
"organizeImports": "on"
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"formatter": {
|
|
11
|
-
"indentStyle": "space",
|
|
12
|
-
"indentWidth": 2,
|
|
13
|
-
"lineWidth": 80
|
|
14
|
-
},
|
|
15
|
-
"javascript": {
|
|
16
|
-
"formatter": {
|
|
17
|
-
"quoteStyle": "single"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"linter": {
|
|
21
|
-
"enabled": true,
|
|
22
|
-
"rules": {
|
|
23
|
-
"recommended": true,
|
|
24
|
-
"correctness": {
|
|
25
|
-
"noUnusedImports": "error",
|
|
26
|
-
"noUnusedVariables": "error"
|
|
27
|
-
},
|
|
28
|
-
"style": {
|
|
29
|
-
"useBlockStatements": "error"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"files": {
|
|
34
|
-
"includes": ["**", "!**/dist", "!**/bun.lock", "!.claude"]
|
|
35
|
-
}
|
|
36
|
-
}
|
package/bun.lock
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"lockfileVersion": 1,
|
|
3
|
-
"configVersion": 0,
|
|
4
|
-
"workspaces": {
|
|
5
|
-
"": {
|
|
6
|
-
"name": "resend-cli",
|
|
7
|
-
"dependencies": {
|
|
8
|
-
"@clack/prompts": "1.1.0",
|
|
9
|
-
"@commander-js/extra-typings": "14.0.0",
|
|
10
|
-
"commander": "14.0.3",
|
|
11
|
-
"resend": "6.9.3",
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"@biomejs/biome": "2.4.6",
|
|
15
|
-
"@types/bun": "1.3.10",
|
|
16
|
-
"typescript": "5.9.3",
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
"packages": {
|
|
21
|
-
"@biomejs/biome": ["@biomejs/biome@2.4.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="],
|
|
22
|
-
|
|
23
|
-
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="],
|
|
24
|
-
|
|
25
|
-
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="],
|
|
26
|
-
|
|
27
|
-
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="],
|
|
28
|
-
|
|
29
|
-
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="],
|
|
30
|
-
|
|
31
|
-
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="],
|
|
32
|
-
|
|
33
|
-
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="],
|
|
34
|
-
|
|
35
|
-
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="],
|
|
36
|
-
|
|
37
|
-
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="],
|
|
38
|
-
|
|
39
|
-
"@clack/core": ["@clack/core@1.1.0", "", { "dependencies": { "sisteransi": "^1.0.5" } }, "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA=="],
|
|
40
|
-
|
|
41
|
-
"@clack/prompts": ["@clack/prompts@1.1.0", "", { "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g=="],
|
|
42
|
-
|
|
43
|
-
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
|
|
44
|
-
|
|
45
|
-
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
|
46
|
-
|
|
47
|
-
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
|
48
|
-
|
|
49
|
-
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
|
50
|
-
|
|
51
|
-
"bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
|
|
52
|
-
|
|
53
|
-
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
|
|
54
|
-
|
|
55
|
-
"fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="],
|
|
56
|
-
|
|
57
|
-
"postal-mime": ["postal-mime@2.7.3", "", {}, "sha512-MjhXadAJaWgYzevi46+3kLak8y6gbg0ku14O1gO/LNOuay8dO+1PtcSGvAdgDR0DoIsSaiIA8y/Ddw6MnrO0Tw=="],
|
|
58
|
-
|
|
59
|
-
"resend": ["resend@6.9.3", "", { "dependencies": { "postal-mime": "2.7.3", "svix": "1.84.1" }, "peerDependencies": { "@react-email/render": "*" }, "optionalPeers": ["@react-email/render"] }, "sha512-GRXjH9XZBJA+daH7bBVDuTShr22iWCxXA8P7t495G4dM/RC+d+3gHBK/6bz9K6Vpcq11zRQKmD+B+jECwQlyGQ=="],
|
|
60
|
-
|
|
61
|
-
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
|
62
|
-
|
|
63
|
-
"standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
|
|
64
|
-
|
|
65
|
-
"svix": ["svix@1.84.1", "", { "dependencies": { "standardwebhooks": "1.0.0", "uuid": "^10.0.0" } }, "sha512-K8DPPSZaW/XqXiz1kEyzSHYgmGLnhB43nQCMeKjWGCUpLIpAMMM8kx3rVVOSm6Bo6EHyK1RQLPT4R06skM/MlQ=="],
|
|
66
|
-
|
|
67
|
-
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
68
|
-
|
|
69
|
-
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
70
|
-
|
|
71
|
-
"uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
|
72
|
-
}
|
|
73
|
-
}
|
package/bunfig.toml
DELETED
package/docs/agent-dx-gaps.md
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Agent DX Gap Analysis
|
|
2
|
-
|
|
3
|
-
Gap analysis of the Resend CLI against the 7 dimensions from
|
|
4
|
-
["You Need to Rewrite Your CLI for AI Agents"](https://justinpoehnelt.com/rewrite-your-cli-for-agents/)
|
|
5
|
-
by Justin Poehnelt (Google).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Structured Output
|
|
10
|
-
|
|
11
|
-
> Agents need machine-readable output — JSON by default when piped.
|
|
12
|
-
|
|
13
|
-
### Strengths
|
|
14
|
-
|
|
15
|
-
- **Auto-JSON when piped.** `shouldOutputJson()` returns `true` when `!process.stdout.isTTY` (`src/lib/output.ts:14`). Every command goes through `outputResult` / `outputError`, so piped output is always JSON with zero flags required.
|
|
16
|
-
- **Consistent error envelope.** `outputError` emits `{"error":{"message":"…","code":"…"}}` with exit code 1 (`src/lib/output.ts:35-54`).
|
|
17
|
-
- **Help text documents the JSON shape.** `buildHelpText` includes an `output` section and `errorCodes` list (`src/lib/help-text.ts:1-34`).
|
|
18
|
-
|
|
19
|
-
### Gaps
|
|
20
|
-
|
|
21
|
-
| Gap | Details | Recommendation |
|
|
22
|
-
|-----|---------|----------------|
|
|
23
|
-
| No `--output` format flag | Only `--json` exists. No `--output=yaml`, `--output=csv`, or `--output=table`. | Low priority — JSON is sufficient for agents. Consider adding `--output` only if human table formatting is needed. |
|
|
24
|
-
| No JSON-Lines streaming | List endpoints return a single JSON blob. For large result sets an agent can't stream-process rows. | Add optional `--jsonl` or newline-delimited mode to `runList`. |
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## 2. Deterministic, Non-Interactive Behavior
|
|
29
|
-
|
|
30
|
-
> When piped, CLIs must never prompt — they should fail fast with an actionable error.
|
|
31
|
-
|
|
32
|
-
### Strengths
|
|
33
|
-
|
|
34
|
-
- **TTY detection is solid.** `isInteractive()` checks `stdin.isTTY`, `stdout.isTTY`, `CI`, `GITHUB_ACTIONS`, and `TERM=dumb` (`src/lib/tty.ts:1-15`).
|
|
35
|
-
- **Missing flags → structured error.** `promptForMissing` exits with `missing_flags` code and lists which flags are needed (`src/lib/prompts.ts:55-61`).
|
|
36
|
-
- **Delete confirmation → structured error.** `confirmDelete` exits with `confirmation_required` and tells the agent to pass `--yes` (`src/lib/prompts.ts:28-36`).
|
|
37
|
-
- **Spinner is a no-op.** `createSpinner` returns stub methods in non-interactive mode (`src/lib/spinner.ts:48-55`).
|
|
38
|
-
|
|
39
|
-
### Gaps
|
|
40
|
-
|
|
41
|
-
| Gap | Details | Recommendation |
|
|
42
|
-
|-----|---------|----------------|
|
|
43
|
-
| `setup` command has no non-interactive default | Bare `resend setup` errors with `missing_target` when piped — the agent must already know the valid targets (`src/commands/setup/index.ts:70-79`). | Consider an `--all` flag or `resend setup --target cursor,vscode` to let agents configure multiple targets in one call. |
|
|
44
|
-
| `skills install` shells out to `npx skills` in TTY | Interactive path delegates to an external process (`src/commands/skills/install.ts:231-246`). If TTY detection is wrong, the agent gets an interactive subprocess. | The non-interactive path already exists and works — just ensure the interactive branch can never trigger when `--json` is passed (currently it can't, but worth a guard). |
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## 3. Meaningful Exit Codes
|
|
49
|
-
|
|
50
|
-
> Agents rely on exit codes to branch logic. 0 = success, non-zero = specific failure category.
|
|
51
|
-
|
|
52
|
-
### Strengths
|
|
53
|
-
|
|
54
|
-
- **Errors exit 1.** `outputError` defaults to exit code 1 (`src/lib/output.ts:39`).
|
|
55
|
-
- **Error codes in JSON.** The `code` field (`missing_flags`, `auth_error`, `fetch_error`, etc.) lets agents distinguish failure types without parsing messages.
|
|
56
|
-
|
|
57
|
-
### Gaps
|
|
58
|
-
|
|
59
|
-
| Gap | Details | Recommendation |
|
|
60
|
-
|-----|---------|----------------|
|
|
61
|
-
| Single exit code for all errors | Everything exits 1. Auth failures, validation errors, network errors, and 404s are indistinguishable by exit code alone. | Define a small set of exit codes: `1` = general, `2` = usage/validation, `3` = auth, `4` = network/API, `78` = config. Map `outputError` codes to exit codes. |
|
|
62
|
-
| `process.exit(0)` on cancel | `cancelAndExit` exits 0 (`src/lib/prompts.ts:14-17`). A cancelled operation should exit non-zero so agents don't interpret it as success. | Exit 130 (standard SIGINT convention) or a dedicated code on cancellation. |
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## 4. Stderr vs Stdout Separation
|
|
67
|
-
|
|
68
|
-
> Machines read stdout. Humans read stderr. Progress, spinners, and status go to stderr.
|
|
69
|
-
|
|
70
|
-
### Strengths
|
|
71
|
-
|
|
72
|
-
- **Spinners write to stderr.** All spinner output uses `process.stderr.write` (`src/lib/spinner.ts:62-80`).
|
|
73
|
-
- **Error JSON goes to stderr.** `outputError` uses `console.error` for JSON errors (`src/lib/output.ts:42`).
|
|
74
|
-
|
|
75
|
-
### Gaps
|
|
76
|
-
|
|
77
|
-
| Gap | Details | Recommendation |
|
|
78
|
-
|-----|---------|----------------|
|
|
79
|
-
| Interactive success messages go to stdout | `console.log(config.successMsg)` in `runDelete`, `runWrite`, and setup commands writes human text to stdout (`src/lib/actions.ts:67,124`). If an agent accidentally gets the interactive branch, it mixes human text with machine output. | Route all non-JSON human messages through `console.error` (stderr) so stdout is always machine-parseable. |
|
|
80
|
-
| `skills install` interactive output on stdout | `console.log(' ✔ ...')` in `installSkills` goes to stdout (`src/commands/skills/install.ts:173-178`). | Move to stderr. |
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## 5. Discoverability & Self-Documentation
|
|
85
|
-
|
|
86
|
-
> Agents need to understand what a CLI can do from the CLI itself — not from docs websites.
|
|
87
|
-
|
|
88
|
-
### Strengths
|
|
89
|
-
|
|
90
|
-
- **Rich help text.** `buildHelpText` adds output shape, error codes, and examples to every command (`src/lib/help-text.ts`).
|
|
91
|
-
- **Examples are runnable.** Help text examples use real `$ resend ...` invocations.
|
|
92
|
-
- **MCP setup exists.** `resend setup` configures the CLI as an MCP server for 5 agents (`src/commands/setup/index.ts`).
|
|
93
|
-
|
|
94
|
-
### Gaps
|
|
95
|
-
|
|
96
|
-
| Gap | Details | Recommendation |
|
|
97
|
-
|-----|---------|----------------|
|
|
98
|
-
| No machine-readable command tree | `--help` outputs human-formatted text. There's no `resend --help --json` or `resend commands --json` that returns a structured list of all commands, flags, and types. | Add a hidden `resend commands` (or `--help --json`) that outputs the full command tree as JSON — subcommands, flags, types, defaults, required/optional. This is the single highest-impact improvement for agent discoverability. |
|
|
99
|
-
| No schema per command | Agents guess at flag types and constraints. | Emit JSON Schema for each command's input (flags + args) and output shape. Could be auto-generated from Commander metadata. |
|
|
100
|
-
| No version/capability negotiation | No `resend capabilities` or feature flags. An agent can't check if a subcommand exists without running it. | Add `resend capabilities --json` returning supported commands and API version. |
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## 6. Auth & Configuration
|
|
105
|
-
|
|
106
|
-
> Agents need non-interactive auth: env vars, config files, or flags — never browser OAuth flows.
|
|
107
|
-
|
|
108
|
-
### Strengths
|
|
109
|
-
|
|
110
|
-
- **Three-tier key resolution.** `resolveApiKey` checks `--api-key` flag → `RESEND_API_KEY` env → `~/.config/resend/credentials.json` (`src/lib/config.ts:24-45`). All three work without interaction.
|
|
111
|
-
- **Config file is secure.** Written with `0o600` permissions (`src/lib/config.ts:52-59`).
|
|
112
|
-
- **XDG-compliant.** Respects `XDG_CONFIG_HOME` and `APPDATA` (`src/lib/config.ts:14-22`).
|
|
113
|
-
|
|
114
|
-
### Gaps
|
|
115
|
-
|
|
116
|
-
| Gap | Details | Recommendation |
|
|
117
|
-
|-----|---------|----------------|
|
|
118
|
-
| No auth status command | There's no way to verify auth works without making an API call. | Add `resend auth status --json` → `{"authenticated":true,"source":"env","key_prefix":"re_..."}`. Useful for agents to pre-flight check credentials. |
|
|
119
|
-
| No key scoping info | The CLI doesn't surface whether the key is a full-access or sending-only key. | Return key metadata (permissions, team) from a status endpoint if the API supports it. |
|
|
120
|
-
| `resend login` is interactive-only | If `resend login` exists and requires TTY input, agents can't use it. | Ensure `resend login --api-key <key>` works non-interactively (just stores the key). |
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## 7. Idempotency & Safety
|
|
125
|
-
|
|
126
|
-
> Agent retries are inevitable. Commands should be safe to re-run.
|
|
127
|
-
|
|
128
|
-
### Strengths
|
|
129
|
-
|
|
130
|
-
- **Setup commands are idempotent.** `mergeJsonConfig` reads existing config and merges — running twice produces the same result (`src/commands/setup/utils.ts:14-33`). Help text documents this.
|
|
131
|
-
- **Skills install is idempotent.** Files are overwritten with the same content.
|
|
132
|
-
- **Delete requires `--yes`.** Prevents accidental destructive actions in non-interactive mode (`src/lib/prompts.ts:28-36`).
|
|
133
|
-
|
|
134
|
-
### Gaps
|
|
135
|
-
|
|
136
|
-
| Gap | Details | Recommendation |
|
|
137
|
-
|-----|---------|----------------|
|
|
138
|
-
| No idempotency keys for create operations | `resend emails send` or `resend domains create` with the same args may create duplicates. | Support `--idempotency-key <key>` passed through to the API's `Idempotency-Key` header. Critical for agent retry loops. |
|
|
139
|
-
| No dry-run mode | No `--dry-run` flag to preview what a command would do. | Add `--dry-run` that validates inputs, resolves auth, and returns the request that *would* be made — without executing it. |
|
|
140
|
-
| File reads have no path validation | `readFile` in `src/lib/files.ts` reads any path the process can access. An agent could be tricked into reading sensitive files. | Validate paths against an allowlist or working-directory scope. Low priority if the CLI is always invoked by a trusted agent. |
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## Implementation Priority
|
|
145
|
-
|
|
146
|
-
Ordered by impact on agent usability:
|
|
147
|
-
|
|
148
|
-
| # | Item | Dimension | Effort |
|
|
149
|
-
|---|------|-----------|--------|
|
|
150
|
-
| 1 | `resend commands --json` — machine-readable command tree | Discoverability | Medium |
|
|
151
|
-
| 2 | Differentiated exit codes (2/3/4 by error category) | Exit Codes | Small |
|
|
152
|
-
| 3 | Route all human messages to stderr | Stderr/Stdout | Small |
|
|
153
|
-
| 4 | `resend auth status --json` | Auth | Small |
|
|
154
|
-
| 5 | `--idempotency-key` flag for create/send commands | Idempotency | Small |
|
|
155
|
-
| 6 | `--dry-run` flag | Idempotency | Medium |
|
|
156
|
-
| 7 | JSON Schema per command (input + output) | Discoverability | Large |
|
|
157
|
-
| 8 | `resend capabilities --json` | Discoverability | Medium |
|
|
158
|
-
| 9 | Cancel exits non-zero (130) | Exit Codes | Tiny |
|
|
159
|
-
| 10 | `--jsonl` streaming for list commands | Structured Output | Medium |
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## Summary
|
|
164
|
-
|
|
165
|
-
The Resend CLI already handles the two most common agent pitfalls well: **structured output is automatic when piped**, and **interactive prompts fail fast with actionable errors**. The spinner, auth, and setup systems are agent-friendly out of the box.
|
|
166
|
-
|
|
167
|
-
The biggest gaps are in **discoverability** (no way for an agent to introspect available commands as JSON) and **exit code granularity** (everything is exit 1). Fixing items 1-4 above would cover ~80% of the agent DX surface with relatively small changes.
|