resend-cli 1.2.1 → 1.3.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.
- package/README.md +25 -10
- package/dist/cli.cjs +539 -0
- package/package.json +31 -13
- package/.claude/settings.local.json +0 -5
- 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/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 -76
- 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 -82
- 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 -207
- 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 -298
- 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 -83
- package/src/commands/teams/switch.ts +0 -73
- 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 -211
- 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 -93
- package/src/lib/table.ts +0 -57
- package/src/lib/tty.ts +0 -28
- 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 -119
- 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 -414
- 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/tsconfig.json +0 -14
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
# Secure API Key Storage via macOS Keychain
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
API keys are currently stored as **plain text** in `~/.config/resend/credentials.json` (with `0o600` file permissions). The CLI already supports **multi-team profiles** — the credentials file has this structure:
|
|
6
|
-
|
|
7
|
-
```json
|
|
8
|
-
{ "active_team": "production", "teams": { "production": { "api_key": "re_..." }, "staging": { "api_key": "re_..." } } }
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
The keychain integration must preserve this multi-team model: each team's API key gets its own keychain entry, and team metadata (active team, team list) stays in the config file.
|
|
12
|
-
|
|
13
|
-
**Goal:** Move API key secrets into macOS Keychain while keeping team management metadata in the config file.
|
|
14
|
-
|
|
15
|
-
## Approach: Shell out to `/usr/bin/security`
|
|
16
|
-
|
|
17
|
-
Use macOS's built-in `security` CLI to interact with Keychain. Zero dependencies, works with Bun compiled binaries.
|
|
18
|
-
|
|
19
|
-
Each team gets its own keychain entry using the team name as the `account` field:
|
|
20
|
-
- `security add-generic-password -s resend-cli -a <team> -w <key> -U` (store/update)
|
|
21
|
-
- `security find-generic-password -s resend-cli -a <team> -w` (retrieve)
|
|
22
|
-
- `security delete-generic-password -s resend-cli -a <team>` (delete)
|
|
23
|
-
|
|
24
|
-
**Why not keytar (the prototype's approach):**
|
|
25
|
-
`bun build --compile` produces a self-contained binary with embedded JS bytecode. Native N-API addons (`.node` files) cannot be embedded — they require OS-level dynamic library loading at runtime. The release workflow (`.github/workflows/release.yml`) cross-compiles to 5 platform targets from a single Ubuntu runner, which further rules out native modules needing per-platform node-gyp compilation. The prototype uses keytar because it's a standard Node.js/oclif package where native modules compile during `npm install`.
|
|
26
|
-
|
|
27
|
-
**Why not other approaches:**
|
|
28
|
-
- **Bun FFI**: Hundreds of lines of C-interop for CoreFoundation/Security.framework — not worth it
|
|
29
|
-
- **Pure-JS wrappers**: Most just shell out to `security` internally anyway
|
|
30
|
-
|
|
31
|
-
## Key resolution priority (updated)
|
|
32
|
-
|
|
33
|
-
1. `--api-key` flag → source: `'flag'`
|
|
34
|
-
2. `RESEND_API_KEY` env var → source: `'env'`
|
|
35
|
-
3. **macOS Keychain** (for resolved team name) → source: `'keychain'` *(new)*
|
|
36
|
-
4. `credentials.json` teams → source: `'config'` *(kept as fallback)*
|
|
37
|
-
|
|
38
|
-
Team resolution is unchanged: `--team` flag > `RESEND_TEAM` env > `active_team` in config > `"default"`
|
|
39
|
-
|
|
40
|
-
## Storage model
|
|
41
|
-
|
|
42
|
-
**Keychain** stores secrets only:
|
|
43
|
-
- Service: `resend-cli`, Account: `<team-name>`, Password: `<api-key>`
|
|
44
|
-
- One entry per team
|
|
45
|
-
|
|
46
|
-
**Config file** (`credentials.json`) stores non-secret metadata:
|
|
47
|
-
- `active_team`: which team is currently active
|
|
48
|
-
- `teams`: record of team names (but **no more `api_key` fields** when keychain is available)
|
|
49
|
-
|
|
50
|
-
When keychain is available, the config file looks like:
|
|
51
|
-
```json
|
|
52
|
-
{ "active_team": "production", "teams": { "production": {}, "staging": {} } }
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
When keychain is unavailable (fallback), `api_key` is stored in the teams record as before.
|
|
56
|
-
|
|
57
|
-
## Migration strategy
|
|
58
|
-
|
|
59
|
-
No automatic migration. Both backends coexist:
|
|
60
|
-
- New `resend login` writes key to keychain + team metadata to config (no `api_key` in config)
|
|
61
|
-
- Old config files with `api_key` fields still work at lower priority
|
|
62
|
-
- `resend logout` / `resend teams remove` clean up both keychain entry and config
|
|
63
|
-
- Users naturally migrate when they next run `resend login`
|
|
64
|
-
|
|
65
|
-
## Files to change
|
|
66
|
-
|
|
67
|
-
### New: `src/lib/keychain.ts` (~60 lines)
|
|
68
|
-
|
|
69
|
-
Keychain abstraction wrapping `/usr/bin/security` via `child_process.execFile` (arg array, no shell — safe from injection).
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
keychainStore(team: string, apiKey: string): Promise<void>
|
|
73
|
-
keychainGet(team: string): Promise<string | null> // null on not-found (exit code 44)
|
|
74
|
-
keychainDelete(team: string): Promise<boolean>
|
|
75
|
-
keychainAvailable(): boolean // process.platform === 'darwin' && existsSync('/usr/bin/security')
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Constants: `SERVICE = 'resend-cli'`
|
|
79
|
-
|
|
80
|
-
### Modify: `src/lib/config.ts`
|
|
81
|
-
|
|
82
|
-
Current functions and how they change:
|
|
83
|
-
|
|
84
|
-
- `ApiKeySource` type: add `'keychain'`
|
|
85
|
-
- `TeamProfile` type: make `api_key` optional (`{ api_key?: string }`) — empty when key is in keychain
|
|
86
|
-
- `resolveApiKey(flagValue?, teamName?)` → **async**: after env check, try `keychainGet(team)` before falling back to config file's `api_key`
|
|
87
|
-
- `storeApiKey(apiKey, teamName?)` → **async**: if `keychainAvailable()`, store key in keychain and write team to config without `api_key`; otherwise write `api_key` to config as fallback
|
|
88
|
-
- `removeApiKey(teamName?)` → **async**: delete from keychain + remove team from config
|
|
89
|
-
- `removeTeam(teamName)` → **async**: delete from keychain + existing config cleanup
|
|
90
|
-
- `resolveTeamName`, `readCredentials`, `writeCredentials`, `setActiveTeam`, `listTeams`: unchanged (they don't touch API keys directly)
|
|
91
|
-
|
|
92
|
-
### Modify: `src/lib/client.ts`
|
|
93
|
-
|
|
94
|
-
- `createClient` and `requireClient` → **async** (add `await` to `resolveApiKey`)
|
|
95
|
-
|
|
96
|
-
### Modify: `src/commands/auth/login.ts`
|
|
97
|
-
|
|
98
|
-
- `await storeApiKey(apiKey, teamName)` (line 151)
|
|
99
|
-
- Update success message: "API key stored in macOS Keychain" vs "API key stored at {path}"
|
|
100
|
-
- `await resolveApiKey()` (line 71)
|
|
101
|
-
|
|
102
|
-
### Modify: `src/commands/auth/logout.ts`
|
|
103
|
-
|
|
104
|
-
- Check keychain + config to determine if logged in (not just file existence)
|
|
105
|
-
- `await removeApiKey()`
|
|
106
|
-
- Update confirmation message for keychain
|
|
107
|
-
|
|
108
|
-
### Modify: `src/commands/teams/remove.ts`
|
|
109
|
-
|
|
110
|
-
- `await removeTeam(name)` — needs to delete keychain entry too
|
|
111
|
-
|
|
112
|
-
### Modify: `src/commands/doctor.ts`
|
|
113
|
-
|
|
114
|
-
- `checkApiKeyPresence` → **async**: `await resolveApiKey()`
|
|
115
|
-
- `checkApiValidationAndDomains` → `await resolveApiKey()`
|
|
116
|
-
- Show `(source: keychain)` when applicable
|
|
117
|
-
- When source is `'config'`, hint: "run `resend login` to migrate to Keychain"
|
|
118
|
-
|
|
119
|
-
### Modify: `src/commands/setup/utils.ts` and `src/commands/setup/claude-code.ts`
|
|
120
|
-
|
|
121
|
-
- Add `await` to `resolveApiKey()` calls
|
|
122
|
-
|
|
123
|
-
### Update help text in: `src/cli.ts`, `login.ts`, `logout.ts`
|
|
124
|
-
|
|
125
|
-
- Reflect new priority chain mentioning Keychain
|
|
126
|
-
|
|
127
|
-
### New: `tests/lib/keychain.test.ts`
|
|
128
|
-
|
|
129
|
-
- Mock `execFile` to test store/get/delete without touching real keychain
|
|
130
|
-
- Test per-team accounts work correctly
|
|
131
|
-
- Test `keychainGet` returns `null` on exit code 44
|
|
132
|
-
- Test `keychainAvailable` returns `false` on non-darwin
|
|
133
|
-
|
|
134
|
-
### Update: existing tests
|
|
135
|
-
|
|
136
|
-
- Mock keychain module in config/auth tests
|
|
137
|
-
|
|
138
|
-
## File-based fallback hardening
|
|
139
|
-
|
|
140
|
-
When keychain is unavailable (headless Linux, CI, containers), the file-based backend is used with hardening:
|
|
141
|
-
|
|
142
|
-
1. **Warn on fallback**: When `storeApiKey` writes to file instead of keychain, print:
|
|
143
|
-
`"⚠ No system keychain available. API key stored in plain text at {path}. Consider using RESEND_API_KEY env var instead."`
|
|
144
|
-
|
|
145
|
-
2. **Permission check on read**: When `resolveApiKey` reads from the config file, verify permissions are still `0o600`. If loosened, warn:
|
|
146
|
-
`"⚠ Credentials file {path} has loose permissions. Run: chmod 600 {path}"`
|
|
147
|
-
|
|
148
|
-
3. **Doctor check**: Show storage backend and warn if using file-based storage on a platform that supports keychain.
|
|
149
|
-
|
|
150
|
-
**Industry context**: Plain text storage is common (AWS CLI, Stripe CLI, Heroku) but increasingly criticized. GitHub CLI now supports keychain (becoming default). Google Cloud uses encrypted SQLite. The trend is toward OS-level secure storage. The file fallback exists for environments where no keychain daemon is available.
|
|
151
|
-
|
|
152
|
-
## Future: Linux & Windows support
|
|
153
|
-
|
|
154
|
-
The `keychain.ts` module abstracts the backend per platform:
|
|
155
|
-
|
|
156
|
-
| Platform | System | CLI tool |
|
|
157
|
-
|----------|--------|----------|
|
|
158
|
-
| **macOS** | Keychain Access | `/usr/bin/security` |
|
|
159
|
-
| **Linux** | libsecret / GNOME Keyring | `secret-tool` (`libsecret-tools`) |
|
|
160
|
-
| **Windows** | Credential Manager | `cmdkey` or PowerShell |
|
|
161
|
-
|
|
162
|
-
To add a platform: add platform-specific functions in `keychain.ts` and extend `keychainAvailable()`. The file-based fallback always remains for headless/CI environments.
|
|
163
|
-
|
|
164
|
-
## Verification
|
|
165
|
-
|
|
166
|
-
1. `bun run build` — ensure compiled binary works
|
|
167
|
-
2. `resend login --key re_test` — stores in keychain, verify in Keychain Access.app under service "resend-cli"
|
|
168
|
-
3. `resend login --key re_staging` with `--team staging` — second keychain entry
|
|
169
|
-
4. `resend teams list` — shows both teams
|
|
170
|
-
5. `resend teams switch staging` → `resend doctor` — shows staging key from keychain
|
|
171
|
-
6. `resend teams remove staging` — deletes keychain entry
|
|
172
|
-
7. `resend logout` — removes active team's keychain entry
|
|
173
|
-
8. `RESEND_API_KEY=re_env resend doctor` — env override still works
|
|
174
|
-
9. Test with old `credentials.json` containing `api_key` fields — falls back to file
|
package/install.ps1
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env pwsh
|
|
2
|
-
# Resend CLI installer for Windows
|
|
3
|
-
#
|
|
4
|
-
# Usage (PowerShell):
|
|
5
|
-
# irm https://resend.com/install.ps1 | iex
|
|
6
|
-
#
|
|
7
|
-
# Pin a version:
|
|
8
|
-
# $env:RESEND_VERSION = 'v0.1.0'; irm https://resend.com/install.ps1 | iex
|
|
9
|
-
#
|
|
10
|
-
# Environment variables:
|
|
11
|
-
# RESEND_INSTALL - Custom install directory (default: $HOME\.resend)
|
|
12
|
-
# RESEND_VERSION - Version to install (default: latest)
|
|
13
|
-
|
|
14
|
-
param(
|
|
15
|
-
[string]$Version = $env:RESEND_VERSION
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
Set-StrictMode -Version Latest
|
|
19
|
-
$ErrorActionPreference = 'Stop'
|
|
20
|
-
|
|
21
|
-
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
function Write-Info { param($msg) Write-Host " $msg" -ForegroundColor DarkGray }
|
|
24
|
-
function Write-Ok { param($msg) Write-Host " $msg" -ForegroundColor Green }
|
|
25
|
-
|
|
26
|
-
function Write-Fail {
|
|
27
|
-
param($msg)
|
|
28
|
-
Write-Host " error: $msg" -ForegroundColor Red
|
|
29
|
-
exit 1
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# ─── Architecture detection ───────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
if ($env:PROCESSOR_ARCHITECTURE -notin @('AMD64', 'EM64T')) {
|
|
35
|
-
Write-Fail "Unsupported architecture: $env:PROCESSOR_ARCHITECTURE`n`n Resend CLI currently supports Windows x64 only."
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# ─── Version + Download URL ───────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
$repo = 'https://github.com/resend/resend-cli'
|
|
41
|
-
$target = 'windows-x64'
|
|
42
|
-
|
|
43
|
-
if ($Version) {
|
|
44
|
-
$Version = $Version.TrimStart('v')
|
|
45
|
-
if ($Version -notmatch '^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$') {
|
|
46
|
-
Write-Fail "Invalid version format: $Version`n`n Expected: semantic version like 0.1.0 or 1.2.3-beta.1`n Usage: `$env:RESEND_VERSION = 'v0.1.0'; irm https://resend.com/install.ps1 | iex"
|
|
47
|
-
}
|
|
48
|
-
$url = "$repo/releases/download/v$Version/resend-$target.zip"
|
|
49
|
-
} else {
|
|
50
|
-
$url = "$repo/releases/latest/download/resend-$target.zip"
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# ─── Install directory ────────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
$installDir = if ($env:RESEND_INSTALL) { $env:RESEND_INSTALL } else { Join-Path $HOME '.resend' }
|
|
56
|
-
$binDir = Join-Path $installDir 'bin'
|
|
57
|
-
$exe = Join-Path $binDir 'resend.exe'
|
|
58
|
-
|
|
59
|
-
if (-not (Test-Path $binDir)) {
|
|
60
|
-
New-Item -ItemType Directory -Path $binDir -Force | Out-Null
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
# ─── Download + Extract ───────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
Write-Host ""
|
|
66
|
-
Write-Host " Installing Resend CLI..." -ForegroundColor White
|
|
67
|
-
Write-Host ""
|
|
68
|
-
Write-Info "Downloading from $url"
|
|
69
|
-
Write-Host ""
|
|
70
|
-
|
|
71
|
-
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "resend-$([System.Guid]::NewGuid())"
|
|
72
|
-
New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
|
|
73
|
-
$tmpZip = Join-Path $tmpDir 'resend.zip'
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
try {
|
|
77
|
-
# Force TLS 1.2 for Windows PowerShell 5.1 (no-op on PowerShell 7+ where it is the default)
|
|
78
|
-
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
79
|
-
$ProgressPreference = 'SilentlyContinue' # Invoke-WebRequest is ~10x faster without progress bar
|
|
80
|
-
Invoke-WebRequest -Uri $url -OutFile $tmpZip -UseBasicParsing
|
|
81
|
-
} catch {
|
|
82
|
-
Write-Fail "Download failed.`n`n Possible causes:`n - No internet connection`n - The version does not exist: $(if ($Version) { $Version } else { 'latest' })`n - GitHub is unreachable`n`n URL: $url"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
Expand-Archive -Path $tmpZip -DestinationPath $binDir -Force
|
|
87
|
-
} catch {
|
|
88
|
-
Write-Fail "Failed to extract archive: $_"
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
finally {
|
|
92
|
-
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (-not (Test-Path $exe)) {
|
|
96
|
-
Write-Fail "Binary not found after extraction. The download may be corrupted — try again."
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# ─── Verify installation ──────────────────────────────────────────────────────
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
$installedVersion = (& $exe --version 2>$null).Trim()
|
|
103
|
-
} catch {
|
|
104
|
-
$installedVersion = 'unknown'
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
Write-Host ""
|
|
108
|
-
Write-Ok "Resend CLI $installedVersion installed successfully!"
|
|
109
|
-
Write-Host ""
|
|
110
|
-
Write-Info "Binary: $exe"
|
|
111
|
-
|
|
112
|
-
# ─── PATH setup ───────────────────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
$userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') ?? ''
|
|
115
|
-
$pathEntries = $userPath -split ';' | Where-Object { $_ -ne '' }
|
|
116
|
-
|
|
117
|
-
if ($pathEntries -contains $binDir) {
|
|
118
|
-
# Already on PATH — just print the getting-started line
|
|
119
|
-
Write-Host ""
|
|
120
|
-
Write-Host " Run " -NoNewline
|
|
121
|
-
Write-Host "resend --help" -ForegroundColor Cyan -NoNewline
|
|
122
|
-
Write-Host " to get started"
|
|
123
|
-
Write-Host ""
|
|
124
|
-
exit 0
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
# Add to user PATH (persists across sessions — no admin rights needed)
|
|
128
|
-
$newPath = ($pathEntries + $binDir) -join ';'
|
|
129
|
-
[Environment]::SetEnvironmentVariable('PATH', $newPath, 'User')
|
|
130
|
-
$env:PATH = "$env:PATH;$binDir" # Also update the current session
|
|
131
|
-
|
|
132
|
-
Write-Info "Added $binDir to PATH (User scope)"
|
|
133
|
-
Write-Host ""
|
|
134
|
-
Write-Info "Restart your terminal, then:"
|
|
135
|
-
Write-Host ""
|
|
136
|
-
Write-Info "Next steps:"
|
|
137
|
-
Write-Host ""
|
|
138
|
-
Write-Host " `$env:RESEND_API_KEY = 're_...'" -ForegroundColor Cyan
|
|
139
|
-
Write-Host " resend --help" -ForegroundColor Cyan
|
|
140
|
-
Write-Host ""
|
|
141
|
-
exit 0
|
package/install.sh
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Resend CLI installer
|
|
3
|
-
#
|
|
4
|
-
# Usage:
|
|
5
|
-
# curl -fsSL https://resend.com/install.sh | bash
|
|
6
|
-
# curl -fsSL https://resend.com/install.sh | bash -s v0.1.0
|
|
7
|
-
#
|
|
8
|
-
# Environment variables:
|
|
9
|
-
# RESEND_INSTALL - Custom install directory (default: ~/.resend)
|
|
10
|
-
# GITHUB_BASE - Custom GitHub base URL (default: https://github.com)
|
|
11
|
-
|
|
12
|
-
# Wrap everything in a function to protect against partial download.
|
|
13
|
-
# If the connection drops mid-transfer, bash won't execute a truncated script.
|
|
14
|
-
main() {
|
|
15
|
-
|
|
16
|
-
set -euo pipefail
|
|
17
|
-
|
|
18
|
-
# ─── Colors (only when outputting to a terminal) ─────────────────────────────
|
|
19
|
-
|
|
20
|
-
Color_Off='' Red='' Green='' Dim='' Bold='' Blue='' Yellow=''
|
|
21
|
-
|
|
22
|
-
if [[ -t 1 ]]; then
|
|
23
|
-
Color_Off='\033[0m'
|
|
24
|
-
Red='\033[0;31m'
|
|
25
|
-
Green='\033[0;32m'
|
|
26
|
-
Yellow='\033[0;33m'
|
|
27
|
-
Dim='\033[0;2m'
|
|
28
|
-
Bold='\033[1m'
|
|
29
|
-
Blue='\033[0;34m'
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
error() {
|
|
35
|
-
printf "%b\n" "${Red}error${Color_Off}: $*" >&2
|
|
36
|
-
exit 1
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
warn() {
|
|
40
|
-
printf "%b\n" "${Yellow}warn${Color_Off}: $*" >&2
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
info() {
|
|
44
|
-
printf "%b\n" "${Dim}$*${Color_Off}"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
success() {
|
|
48
|
-
printf "%b\n" "${Green}$*${Color_Off}"
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
bold() {
|
|
52
|
-
printf "%b\n" "${Bold}$*${Color_Off}"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
tildify() {
|
|
56
|
-
if [[ $1 == "$HOME"/* ]]; then
|
|
57
|
-
echo "~${1#"$HOME"}"
|
|
58
|
-
else
|
|
59
|
-
echo "$1"
|
|
60
|
-
fi
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
# ─── Dependency checks ──────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
command -v curl >/dev/null 2>&1 || error "curl is required but not found. Install it and try again."
|
|
66
|
-
command -v tar >/dev/null 2>&1 || error "tar is required but not found. Install it and try again."
|
|
67
|
-
|
|
68
|
-
# ─── OS / Architecture detection ────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
platform=$(uname -ms)
|
|
71
|
-
|
|
72
|
-
case $platform in
|
|
73
|
-
'Darwin x86_64') target=darwin-x64 ;;
|
|
74
|
-
'Darwin arm64') target=darwin-arm64 ;;
|
|
75
|
-
'Linux aarch64') target=linux-arm64 ;;
|
|
76
|
-
'Linux arm64') target=linux-arm64 ;;
|
|
77
|
-
'Linux x86_64') target=linux-x64 ;;
|
|
78
|
-
*)
|
|
79
|
-
error "Unsupported platform: ${platform}.
|
|
80
|
-
|
|
81
|
-
Resend CLI supports:
|
|
82
|
-
- macOS (Apple Silicon / Intel)
|
|
83
|
-
- Linux (x64 / arm64)
|
|
84
|
-
|
|
85
|
-
For Windows, run this in PowerShell:
|
|
86
|
-
irm https://resend.com/install.ps1 | iex"
|
|
87
|
-
;;
|
|
88
|
-
esac
|
|
89
|
-
|
|
90
|
-
# Detect Rosetta 2 on macOS — prefer native arm64 binary
|
|
91
|
-
if [[ $target == "darwin-x64" ]]; then
|
|
92
|
-
if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0) == "1" ]]; then
|
|
93
|
-
target=darwin-arm64
|
|
94
|
-
info " Rosetta 2 detected — installing native arm64 binary"
|
|
95
|
-
fi
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# Detect musl (Alpine Linux) — glibc binaries won't work
|
|
99
|
-
if [[ $target == linux-* ]]; then
|
|
100
|
-
if ldd --version 2>&1 | grep -qi musl 2>/dev/null; then
|
|
101
|
-
error "Alpine Linux (musl) is not currently supported.
|
|
102
|
-
|
|
103
|
-
The compiled binary requires glibc. Use one of these alternatives:
|
|
104
|
-
- npm install -g resend-cli
|
|
105
|
-
- Run in a glibc-based container (e.g., ubuntu, debian)"
|
|
106
|
-
fi
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
# ─── Version + Download URL ─────────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
GITHUB_BASE=${GITHUB_BASE:-"https://github.com"}
|
|
112
|
-
|
|
113
|
-
# Validate GITHUB_BASE is HTTPS to prevent download from arbitrary sources
|
|
114
|
-
case "$GITHUB_BASE" in
|
|
115
|
-
https://*) ;;
|
|
116
|
-
*) error "GITHUB_BASE must start with https:// (got: ${GITHUB_BASE})" ;;
|
|
117
|
-
esac
|
|
118
|
-
|
|
119
|
-
REPO="${GITHUB_BASE}/resend/resend-cli"
|
|
120
|
-
|
|
121
|
-
VERSION=${1:-}
|
|
122
|
-
|
|
123
|
-
# Validate version format if provided
|
|
124
|
-
if [[ -n $VERSION ]]; then
|
|
125
|
-
# Strip leading 'v' if present, then re-add for the tag
|
|
126
|
-
VERSION="${VERSION#v}"
|
|
127
|
-
if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
|
128
|
-
error "Invalid version format: ${VERSION}
|
|
129
|
-
|
|
130
|
-
Expected: semantic version like 0.1.0 or 1.2.3-beta.1
|
|
131
|
-
Usage: curl -fsSL https://resend.com/install.sh | bash -s v0.1.0"
|
|
132
|
-
fi
|
|
133
|
-
url="${REPO}/releases/download/v${VERSION}/resend-${target}.tar.gz"
|
|
134
|
-
else
|
|
135
|
-
url="${REPO}/releases/latest/download/resend-${target}.tar.gz"
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
# ─── Install directory ──────────────────────────────────────────────────────
|
|
139
|
-
|
|
140
|
-
install_dir="${RESEND_INSTALL:-$HOME/.resend}"
|
|
141
|
-
bin_dir="${install_dir}/bin"
|
|
142
|
-
exe="${bin_dir}/resend"
|
|
143
|
-
|
|
144
|
-
mkdir -p "$bin_dir" || error "Failed to create install directory: ${bin_dir}"
|
|
145
|
-
|
|
146
|
-
# ─── Download + Extract ─────────────────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
echo ""
|
|
149
|
-
bold " Installing Resend CLI..."
|
|
150
|
-
echo ""
|
|
151
|
-
|
|
152
|
-
tmpdir=$(mktemp -d) || error "Failed to create temporary directory"
|
|
153
|
-
trap 'rm -rf "$tmpdir"' EXIT INT TERM
|
|
154
|
-
|
|
155
|
-
tmpfile="${tmpdir}/resend.tar.gz"
|
|
156
|
-
|
|
157
|
-
info " Downloading from ${url}"
|
|
158
|
-
echo ""
|
|
159
|
-
|
|
160
|
-
curl --fail --location --progress-bar --output "$tmpfile" "$url" ||
|
|
161
|
-
error "Download failed.
|
|
162
|
-
|
|
163
|
-
Possible causes:
|
|
164
|
-
- No internet connection
|
|
165
|
-
- The version does not exist: ${VERSION:-latest}
|
|
166
|
-
- GitHub is unreachable
|
|
167
|
-
|
|
168
|
-
URL: ${url}"
|
|
169
|
-
|
|
170
|
-
tar -xzf "$tmpfile" -C "$bin_dir" ||
|
|
171
|
-
error "Failed to extract archive. The download may be corrupted — try again."
|
|
172
|
-
|
|
173
|
-
chmod +x "$exe" || error "Failed to make binary executable"
|
|
174
|
-
|
|
175
|
-
# Strip macOS Gatekeeper quarantine flag (set automatically on curl downloads)
|
|
176
|
-
# Without this, macOS will block the binary: "cannot be opened because Apple
|
|
177
|
-
# cannot check it for malicious software"
|
|
178
|
-
if [[ $(uname -s) == "Darwin" ]]; then
|
|
179
|
-
xattr -d com.apple.quarantine "$exe" 2>/dev/null || true
|
|
180
|
-
fi
|
|
181
|
-
|
|
182
|
-
# ─── Verify installation ────────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
installed_version=$("$exe" --version 2>/dev/null || echo "unknown")
|
|
185
|
-
|
|
186
|
-
echo ""
|
|
187
|
-
success " Resend CLI ${installed_version} installed successfully!"
|
|
188
|
-
echo ""
|
|
189
|
-
info " Binary: $(tildify "$exe")"
|
|
190
|
-
|
|
191
|
-
# ─── PATH setup ─────────────────────────────────────────────────────────────
|
|
192
|
-
|
|
193
|
-
# Check if already on PATH
|
|
194
|
-
if command -v resend >/dev/null 2>&1; then
|
|
195
|
-
existing=$(command -v resend)
|
|
196
|
-
if [[ "$existing" == "$exe" ]]; then
|
|
197
|
-
echo ""
|
|
198
|
-
bold " Run ${Blue}resend --help${Color_Off}${Bold} to get started${Color_Off}"
|
|
199
|
-
echo ""
|
|
200
|
-
exit 0
|
|
201
|
-
else
|
|
202
|
-
warn "another 'resend' was found at ${existing}"
|
|
203
|
-
info " The new installation at $(tildify "$exe") may be shadowed."
|
|
204
|
-
fi
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
# Check if bin_dir is already in PATH
|
|
208
|
-
if echo "$PATH" | tr ':' '\n' | grep -qxF "${bin_dir}" 2>/dev/null; then
|
|
209
|
-
echo ""
|
|
210
|
-
bold " Run ${Blue}resend --help${Color_Off}${Bold} to get started${Color_Off}"
|
|
211
|
-
echo ""
|
|
212
|
-
exit 0
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
# Determine shell config file
|
|
216
|
-
shell_name=$(basename "${SHELL:-}")
|
|
217
|
-
config=""
|
|
218
|
-
shell_line=""
|
|
219
|
-
|
|
220
|
-
# Build a $HOME-relative path for shell config (~ doesn't expand inside quotes)
|
|
221
|
-
if [[ $bin_dir == "$HOME"/* ]]; then
|
|
222
|
-
shell_bin_dir="\$HOME${bin_dir#"$HOME"}"
|
|
223
|
-
else
|
|
224
|
-
shell_bin_dir="$bin_dir"
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
case $shell_name in
|
|
228
|
-
zsh)
|
|
229
|
-
config="${ZDOTDIR:-$HOME}/.zshrc"
|
|
230
|
-
shell_line="export PATH=\"${shell_bin_dir}:\$PATH\""
|
|
231
|
-
;;
|
|
232
|
-
bash)
|
|
233
|
-
# macOS bash opens login shells — .bash_profile is loaded, not .bashrc.
|
|
234
|
-
# Linux bash opens non-login interactive shells — .bashrc is preferred.
|
|
235
|
-
if [[ $(uname -s) == "Darwin" ]]; then
|
|
236
|
-
if [[ -f "$HOME/.bash_profile" ]]; then
|
|
237
|
-
config="$HOME/.bash_profile"
|
|
238
|
-
elif [[ -f "$HOME/.bashrc" ]]; then
|
|
239
|
-
config="$HOME/.bashrc"
|
|
240
|
-
else
|
|
241
|
-
config="$HOME/.bash_profile"
|
|
242
|
-
fi
|
|
243
|
-
else
|
|
244
|
-
if [[ -f "$HOME/.bashrc" ]]; then
|
|
245
|
-
config="$HOME/.bashrc"
|
|
246
|
-
elif [[ -f "$HOME/.bash_profile" ]]; then
|
|
247
|
-
config="$HOME/.bash_profile"
|
|
248
|
-
else
|
|
249
|
-
config="$HOME/.bashrc"
|
|
250
|
-
fi
|
|
251
|
-
fi
|
|
252
|
-
shell_line="export PATH=\"${shell_bin_dir}:\$PATH\""
|
|
253
|
-
;;
|
|
254
|
-
fish)
|
|
255
|
-
config="${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/resend.fish"
|
|
256
|
-
mkdir -p "$(dirname "$config")"
|
|
257
|
-
shell_line="fish_add_path ${shell_bin_dir}"
|
|
258
|
-
;;
|
|
259
|
-
esac
|
|
260
|
-
|
|
261
|
-
if [[ -n $config ]]; then
|
|
262
|
-
# Check if PATH entry already exists (check both tildified and absolute)
|
|
263
|
-
if [[ -f "$config" ]] && (grep -qF "$(tildify "$bin_dir")" "$config" 2>/dev/null || grep -qF "$bin_dir" "$config" 2>/dev/null); then
|
|
264
|
-
info " PATH already configured in $(tildify "$config")"
|
|
265
|
-
elif [[ -w "${config%/*}" ]] || [[ -w "$config" ]]; then
|
|
266
|
-
{
|
|
267
|
-
echo ""
|
|
268
|
-
echo "# Resend CLI"
|
|
269
|
-
echo "$shell_line"
|
|
270
|
-
} >> "$config"
|
|
271
|
-
info " Added $(tildify "$bin_dir") to \$PATH in $(tildify "$config")"
|
|
272
|
-
echo ""
|
|
273
|
-
info " To start using Resend CLI, run:"
|
|
274
|
-
echo ""
|
|
275
|
-
bold " source $(tildify "$config")"
|
|
276
|
-
bold " resend --help"
|
|
277
|
-
else
|
|
278
|
-
echo ""
|
|
279
|
-
info " Manually add to your shell config:"
|
|
280
|
-
echo ""
|
|
281
|
-
bold " ${shell_line}"
|
|
282
|
-
fi
|
|
283
|
-
else
|
|
284
|
-
echo ""
|
|
285
|
-
info " Add to your shell config:"
|
|
286
|
-
echo ""
|
|
287
|
-
bold " export PATH=\"${shell_bin_dir}:\$PATH\""
|
|
288
|
-
fi
|
|
289
|
-
|
|
290
|
-
echo ""
|
|
291
|
-
info " Next steps:"
|
|
292
|
-
echo ""
|
|
293
|
-
bold " export RESEND_API_KEY=re_..."
|
|
294
|
-
bold " resend --help"
|
|
295
|
-
echo ""
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
# Run the installer — this line MUST be the last line in the file.
|
|
300
|
-
# If the download is interrupted, bash will not execute an incomplete function.
|
|
301
|
-
main "$@"
|
package/renovate.json
DELETED