resend-cli 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/biome.json +1 -1
- package/bun.lock +0 -3
- package/package.json +2 -3
- package/src/cli.ts +11 -1
- package/src/commands/auth/login.ts +35 -8
- package/src/commands/doctor.ts +33 -115
- package/src/commands/teams/remove.ts +5 -2
- package/src/commands/teams/switch.ts +3 -0
- package/src/lib/config.ts +37 -31
- package/src/lib/spinner.ts +17 -10
- package/src/lib/update-check.ts +172 -0
- package/tests/commands/auth/login.test.ts +37 -0
- package/tests/lib/config.test.ts +38 -7
- package/tests/lib/update-check.test.ts +169 -0
- package/.claude/worktrees/emails-list/.claude/settings.local.json +0 -5
- package/.claude/worktrees/emails-list/.github/scripts/pr-title-check.js +0 -34
- package/.claude/worktrees/emails-list/.github/workflows/ci.yml +0 -32
- package/.claude/worktrees/emails-list/.github/workflows/pr-title-check.yml +0 -13
- package/.claude/worktrees/emails-list/.github/workflows/release.yml +0 -93
- package/.claude/worktrees/emails-list/CHANGELOG.md +0 -31
- package/.claude/worktrees/emails-list/LICENSE +0 -21
- package/.claude/worktrees/emails-list/README.md +0 -424
- package/.claude/worktrees/emails-list/biome.json +0 -36
- package/.claude/worktrees/emails-list/bun.lock +0 -76
- package/.claude/worktrees/emails-list/bunfig.toml +0 -2
- package/.claude/worktrees/emails-list/install.ps1 +0 -140
- package/.claude/worktrees/emails-list/install.sh +0 -301
- package/.claude/worktrees/emails-list/package.json +0 -43
- package/.claude/worktrees/emails-list/renovate.json +0 -6
- package/.claude/worktrees/emails-list/src/cli.ts +0 -74
- package/.claude/worktrees/emails-list/src/commands/api-keys/create.ts +0 -114
- package/.claude/worktrees/emails-list/src/commands/api-keys/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/api-keys/index.ts +0 -26
- package/.claude/worktrees/emails-list/src/commands/api-keys/list.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/api-keys/utils.ts +0 -8
- package/.claude/worktrees/emails-list/src/commands/auth/index.ts +0 -20
- package/.claude/worktrees/emails-list/src/commands/auth/login.ts +0 -207
- package/.claude/worktrees/emails-list/src/commands/auth/logout.ts +0 -105
- package/.claude/worktrees/emails-list/src/commands/broadcasts/create.ts +0 -196
- package/.claude/worktrees/emails-list/src/commands/broadcasts/delete.ts +0 -46
- package/.claude/worktrees/emails-list/src/commands/broadcasts/get.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/broadcasts/index.ts +0 -43
- package/.claude/worktrees/emails-list/src/commands/broadcasts/list.ts +0 -60
- package/.claude/worktrees/emails-list/src/commands/broadcasts/send.ts +0 -56
- package/.claude/worktrees/emails-list/src/commands/broadcasts/update.ts +0 -95
- package/.claude/worktrees/emails-list/src/commands/broadcasts/utils.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/contact-properties/create.ts +0 -118
- package/.claude/worktrees/emails-list/src/commands/contact-properties/delete.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contact-properties/get.ts +0 -46
- package/.claude/worktrees/emails-list/src/commands/contact-properties/index.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contact-properties/list.ts +0 -68
- package/.claude/worktrees/emails-list/src/commands/contact-properties/update.ts +0 -88
- package/.claude/worktrees/emails-list/src/commands/contact-properties/utils.ts +0 -17
- package/.claude/worktrees/emails-list/src/commands/contacts/add-segment.ts +0 -78
- package/.claude/worktrees/emails-list/src/commands/contacts/create.ts +0 -122
- package/.claude/worktrees/emails-list/src/commands/contacts/delete.ts +0 -49
- package/.claude/worktrees/emails-list/src/commands/contacts/get.ts +0 -53
- package/.claude/worktrees/emails-list/src/commands/contacts/index.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/contacts/list.ts +0 -57
- package/.claude/worktrees/emails-list/src/commands/contacts/remove-segment.ts +0 -48
- package/.claude/worktrees/emails-list/src/commands/contacts/segments.ts +0 -39
- package/.claude/worktrees/emails-list/src/commands/contacts/topics.ts +0 -45
- package/.claude/worktrees/emails-list/src/commands/contacts/update-topics.ts +0 -90
- package/.claude/worktrees/emails-list/src/commands/contacts/update.ts +0 -77
- package/.claude/worktrees/emails-list/src/commands/contacts/utils.ts +0 -119
- package/.claude/worktrees/emails-list/src/commands/doctor.ts +0 -298
- package/.claude/worktrees/emails-list/src/commands/domains/create.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/domains/delete.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/domains/get.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/domains/index.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/domains/list.ts +0 -53
- package/.claude/worktrees/emails-list/src/commands/domains/update.ts +0 -75
- package/.claude/worktrees/emails-list/src/commands/domains/utils.ts +0 -44
- package/.claude/worktrees/emails-list/src/commands/domains/verify.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/emails/batch.ts +0 -140
- package/.claude/worktrees/emails-list/src/commands/emails/index.ts +0 -28
- package/.claude/worktrees/emails-list/src/commands/emails/list.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachment.ts +0 -55
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/attachments.ts +0 -68
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/get.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/index.ts +0 -28
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/list.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/emails/receiving/utils.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/emails/send.ts +0 -189
- package/.claude/worktrees/emails-list/src/commands/open.ts +0 -24
- package/.claude/worktrees/emails-list/src/commands/segments/create.ts +0 -50
- package/.claude/worktrees/emails-list/src/commands/segments/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/segments/get.ts +0 -38
- package/.claude/worktrees/emails-list/src/commands/segments/index.ts +0 -36
- package/.claude/worktrees/emails-list/src/commands/segments/list.ts +0 -58
- package/.claude/worktrees/emails-list/src/commands/segments/utils.ts +0 -7
- package/.claude/worktrees/emails-list/src/commands/teams/index.ts +0 -10
- package/.claude/worktrees/emails-list/src/commands/teams/list.ts +0 -35
- package/.claude/worktrees/emails-list/src/commands/teams/remove.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/teams/switch.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/topics/create.ts +0 -73
- package/.claude/worktrees/emails-list/src/commands/topics/delete.ts +0 -47
- package/.claude/worktrees/emails-list/src/commands/topics/get.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/topics/index.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/topics/list.ts +0 -34
- package/.claude/worktrees/emails-list/src/commands/topics/update.ts +0 -59
- package/.claude/worktrees/emails-list/src/commands/topics/utils.ts +0 -16
- package/.claude/worktrees/emails-list/src/commands/webhooks/create.ts +0 -128
- package/.claude/worktrees/emails-list/src/commands/webhooks/delete.ts +0 -49
- package/.claude/worktrees/emails-list/src/commands/webhooks/get.ts +0 -42
- package/.claude/worktrees/emails-list/src/commands/webhooks/index.ts +0 -44
- package/.claude/worktrees/emails-list/src/commands/webhooks/list.ts +0 -55
- package/.claude/worktrees/emails-list/src/commands/webhooks/update.ts +0 -83
- package/.claude/worktrees/emails-list/src/commands/webhooks/utils.ts +0 -36
- package/.claude/worktrees/emails-list/src/commands/whoami.ts +0 -71
- package/.claude/worktrees/emails-list/src/lib/actions.ts +0 -157
- package/.claude/worktrees/emails-list/src/lib/client.ts +0 -34
- package/.claude/worktrees/emails-list/src/lib/config.ts +0 -211
- package/.claude/worktrees/emails-list/src/lib/files.ts +0 -15
- package/.claude/worktrees/emails-list/src/lib/help-text.ts +0 -38
- package/.claude/worktrees/emails-list/src/lib/output.ts +0 -54
- package/.claude/worktrees/emails-list/src/lib/pagination.ts +0 -36
- package/.claude/worktrees/emails-list/src/lib/prompts.ts +0 -149
- package/.claude/worktrees/emails-list/src/lib/spinner.ts +0 -93
- package/.claude/worktrees/emails-list/src/lib/table.ts +0 -57
- package/.claude/worktrees/emails-list/src/lib/tty.ts +0 -28
- package/.claude/worktrees/emails-list/src/lib/version.ts +0 -4
- package/.claude/worktrees/emails-list/tests/commands/api-keys/create.test.ts +0 -195
- package/.claude/worktrees/emails-list/tests/commands/api-keys/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/api-keys/list.test.ts +0 -133
- package/.claude/worktrees/emails-list/tests/commands/auth/login.test.ts +0 -119
- package/.claude/worktrees/emails-list/tests/commands/auth/logout.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/create.test.ts +0 -447
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/delete.test.ts +0 -182
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/get.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/list.test.ts +0 -196
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/send.test.ts +0 -161
- package/.claude/worktrees/emails-list/tests/commands/broadcasts/update.test.ts +0 -283
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/create.test.ts +0 -250
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/delete.test.ts +0 -183
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/get.test.ts +0 -144
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/list.test.ts +0 -180
- package/.claude/worktrees/emails-list/tests/commands/contact-properties/update.test.ts +0 -216
- package/.claude/worktrees/emails-list/tests/commands/contacts/add-segment.test.ts +0 -188
- package/.claude/worktrees/emails-list/tests/commands/contacts/create.test.ts +0 -270
- package/.claude/worktrees/emails-list/tests/commands/contacts/delete.test.ts +0 -192
- package/.claude/worktrees/emails-list/tests/commands/contacts/get.test.ts +0 -148
- package/.claude/worktrees/emails-list/tests/commands/contacts/list.test.ts +0 -175
- package/.claude/worktrees/emails-list/tests/commands/contacts/remove-segment.test.ts +0 -166
- package/.claude/worktrees/emails-list/tests/commands/contacts/segments.test.ts +0 -167
- package/.claude/worktrees/emails-list/tests/commands/contacts/topics.test.ts +0 -163
- package/.claude/worktrees/emails-list/tests/commands/contacts/update-topics.test.ts +0 -247
- package/.claude/worktrees/emails-list/tests/commands/contacts/update.test.ts +0 -205
- package/.claude/worktrees/emails-list/tests/commands/doctor.test.ts +0 -165
- package/.claude/worktrees/emails-list/tests/commands/domains/create.test.ts +0 -192
- package/.claude/worktrees/emails-list/tests/commands/domains/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/domains/get.test.ts +0 -137
- package/.claude/worktrees/emails-list/tests/commands/domains/list.test.ts +0 -164
- package/.claude/worktrees/emails-list/tests/commands/domains/update.test.ts +0 -223
- package/.claude/worktrees/emails-list/tests/commands/domains/verify.test.ts +0 -117
- package/.claude/worktrees/emails-list/tests/commands/emails/batch.test.ts +0 -313
- package/.claude/worktrees/emails-list/tests/commands/emails/list.test.ts +0 -196
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachment.test.ts +0 -140
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/attachments.test.ts +0 -168
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/get.test.ts +0 -140
- package/.claude/worktrees/emails-list/tests/commands/emails/receiving/list.test.ts +0 -181
- package/.claude/worktrees/emails-list/tests/commands/emails/send.test.ts +0 -309
- package/.claude/worktrees/emails-list/tests/commands/segments/create.test.ts +0 -163
- package/.claude/worktrees/emails-list/tests/commands/segments/delete.test.ts +0 -182
- package/.claude/worktrees/emails-list/tests/commands/segments/get.test.ts +0 -137
- package/.claude/worktrees/emails-list/tests/commands/segments/list.test.ts +0 -173
- package/.claude/worktrees/emails-list/tests/commands/teams/list.test.ts +0 -63
- package/.claude/worktrees/emails-list/tests/commands/teams/remove.test.ts +0 -103
- package/.claude/worktrees/emails-list/tests/commands/teams/switch.test.ts +0 -96
- package/.claude/worktrees/emails-list/tests/commands/topics/create.test.ts +0 -191
- package/.claude/worktrees/emails-list/tests/commands/topics/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/topics/get.test.ts +0 -125
- package/.claude/worktrees/emails-list/tests/commands/topics/list.test.ts +0 -124
- package/.claude/worktrees/emails-list/tests/commands/topics/update.test.ts +0 -177
- package/.claude/worktrees/emails-list/tests/commands/webhooks/create.test.ts +0 -224
- package/.claude/worktrees/emails-list/tests/commands/webhooks/delete.test.ts +0 -156
- package/.claude/worktrees/emails-list/tests/commands/webhooks/get.test.ts +0 -125
- package/.claude/worktrees/emails-list/tests/commands/webhooks/list.test.ts +0 -177
- package/.claude/worktrees/emails-list/tests/commands/webhooks/update.test.ts +0 -206
- package/.claude/worktrees/emails-list/tests/commands/whoami.test.ts +0 -99
- package/.claude/worktrees/emails-list/tests/helpers.ts +0 -93
- package/.claude/worktrees/emails-list/tests/lib/client.test.ts +0 -71
- package/.claude/worktrees/emails-list/tests/lib/config.test.ts +0 -414
- package/.claude/worktrees/emails-list/tests/lib/files.test.ts +0 -65
- package/.claude/worktrees/emails-list/tests/lib/help-text.test.ts +0 -97
- package/.claude/worktrees/emails-list/tests/lib/output.test.ts +0 -127
- package/.claude/worktrees/emails-list/tests/lib/prompts.test.ts +0 -178
- package/.claude/worktrees/emails-list/tests/lib/spinner.test.ts +0 -146
- package/.claude/worktrees/emails-list/tests/lib/table.test.ts +0 -63
- package/.claude/worktrees/emails-list/tests/lib/tty.test.ts +0 -85
- package/.claude/worktrees/emails-list/tsconfig.json +0 -14
package/biome.json
CHANGED
package/bun.lock
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"@commander-js/extra-typings": "14.0.0",
|
|
10
10
|
"commander": "14.0.3",
|
|
11
11
|
"resend": "6.9.3",
|
|
12
|
-
"unicode-animations": "1.0.3",
|
|
13
12
|
},
|
|
14
13
|
"devDependencies": {
|
|
15
14
|
"@biomejs/biome": "2.4.6",
|
|
@@ -69,8 +68,6 @@
|
|
|
69
68
|
|
|
70
69
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
71
70
|
|
|
72
|
-
"unicode-animations": ["unicode-animations@1.0.3", "", { "dependencies": { "unicode-animations": "^1.0.1" }, "bin": { "unicode-animations": "scripts/demo.cjs" } }, "sha512-+klB2oWwcYZjYWhwP4Pr8UZffWDFVx6jKeIahE6z0QYyM2dwDeDPyn5nevCYbyotxvtT9lh21cVURO1RX0+YMg=="],
|
|
73
|
-
|
|
74
71
|
"uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
|
75
72
|
}
|
|
76
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resend-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "The official CLI for Resend",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
"commander": "14.0.3",
|
|
33
33
|
"@commander-js/extra-typings": "14.0.0",
|
|
34
34
|
"resend": "6.9.3",
|
|
35
|
-
"@clack/prompts": "1.1.0"
|
|
36
|
-
"unicode-animations": "1.0.3"
|
|
35
|
+
"@clack/prompts": "1.1.0"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
38
|
"@biomejs/biome": "2.4.6",
|
package/src/cli.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { teamsCommand } from './commands/teams/index';
|
|
|
15
15
|
import { topicsCommand } from './commands/topics/index';
|
|
16
16
|
import { webhooksCommand } from './commands/webhooks/index';
|
|
17
17
|
import { whoamiCommand } from './commands/whoami';
|
|
18
|
+
import { errorMessage, outputError } from './lib/output';
|
|
19
|
+
import { checkForUpdates } from './lib/update-check';
|
|
18
20
|
import { PACKAGE_NAME, VERSION } from './lib/version';
|
|
19
21
|
|
|
20
22
|
const BANNER = `
|
|
@@ -79,4 +81,12 @@ Examples:
|
|
|
79
81
|
.addCommand(openCommand)
|
|
80
82
|
.addCommand(whoamiCommand);
|
|
81
83
|
|
|
82
|
-
program
|
|
84
|
+
program
|
|
85
|
+
.parseAsync()
|
|
86
|
+
.then(() => checkForUpdates().catch(() => {}))
|
|
87
|
+
.catch((err) => {
|
|
88
|
+
outputError({
|
|
89
|
+
message: errorMessage(err, 'An unexpected error occurred'),
|
|
90
|
+
code: 'unexpected_error',
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -3,7 +3,13 @@ import * as p from '@clack/prompts';
|
|
|
3
3
|
import { Command } from '@commander-js/extra-typings';
|
|
4
4
|
import { Resend } from 'resend';
|
|
5
5
|
import type { GlobalOpts } from '../../lib/client';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
listTeams,
|
|
8
|
+
resolveApiKey,
|
|
9
|
+
setActiveTeam,
|
|
10
|
+
storeApiKey,
|
|
11
|
+
validateTeamName,
|
|
12
|
+
} from '../../lib/config';
|
|
7
13
|
import { buildHelpText } from '../../lib/help-text';
|
|
8
14
|
import { errorMessage, outputError, outputResult } from '../../lib/output';
|
|
9
15
|
import { cancelAndExit } from '../../lib/prompts';
|
|
@@ -127,11 +133,7 @@ export const loginCommand = new Command('login')
|
|
|
127
133
|
);
|
|
128
134
|
}
|
|
129
135
|
|
|
130
|
-
const spinner = createSpinner(
|
|
131
|
-
'Validating API key...',
|
|
132
|
-
'braille',
|
|
133
|
-
globalOpts.quiet,
|
|
134
|
-
);
|
|
136
|
+
const spinner = createSpinner('Validating API key...', globalOpts.quiet);
|
|
135
137
|
|
|
136
138
|
try {
|
|
137
139
|
const resend = new Resend(apiKey);
|
|
@@ -150,6 +152,17 @@ export const loginCommand = new Command('login')
|
|
|
150
152
|
|
|
151
153
|
let teamName = globalOpts.team;
|
|
152
154
|
|
|
155
|
+
if (teamName) {
|
|
156
|
+
const teamError = validateTeamName(teamName);
|
|
157
|
+
if (teamError) {
|
|
158
|
+
outputError(
|
|
159
|
+
{ message: teamError, code: 'invalid_team_name' },
|
|
160
|
+
{ json: globalOpts.json },
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
153
166
|
if (!teamName && isInteractive()) {
|
|
154
167
|
const existingTeams = listTeams();
|
|
155
168
|
if (existingTeams.length > 0) {
|
|
@@ -173,8 +186,7 @@ export const loginCommand = new Command('login')
|
|
|
173
186
|
if (choice === '__new__') {
|
|
174
187
|
const newName = await p.text({
|
|
175
188
|
message: 'Enter a name for the new team:',
|
|
176
|
-
validate: (v) =>
|
|
177
|
-
!v || v.length === 0 ? 'Team name is required' : undefined,
|
|
189
|
+
validate: (v) => validateTeamName(v as string),
|
|
178
190
|
});
|
|
179
191
|
if (p.isCancel(newName)) {
|
|
180
192
|
cancelAndExit('Login cancelled.');
|
|
@@ -191,6 +203,21 @@ export const loginCommand = new Command('login')
|
|
|
191
203
|
const configPath = storeApiKey(apiKey, teamName);
|
|
192
204
|
const teamLabel = teamName || 'default';
|
|
193
205
|
|
|
206
|
+
// Auto-switch to the newly added team
|
|
207
|
+
if (teamName) {
|
|
208
|
+
try {
|
|
209
|
+
setActiveTeam(teamName);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
outputError(
|
|
212
|
+
{
|
|
213
|
+
message: errorMessage(err, 'Failed to switch team'),
|
|
214
|
+
code: 'switch_failed',
|
|
215
|
+
},
|
|
216
|
+
{ json: globalOpts.json },
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
194
221
|
if (globalOpts.json) {
|
|
195
222
|
outputResult(
|
|
196
223
|
{ success: true, config_path: configPath, team: teamLabel },
|
package/src/commands/doctor.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
1
|
import { Command } from '@commander-js/extra-typings';
|
|
5
2
|
import { Resend } from 'resend';
|
|
6
3
|
import type { GlobalOpts } from '../lib/client';
|
|
@@ -9,7 +6,8 @@ import { buildHelpText } from '../lib/help-text';
|
|
|
9
6
|
import { errorMessage, outputResult } from '../lib/output';
|
|
10
7
|
import { createSpinner } from '../lib/spinner';
|
|
11
8
|
import { isInteractive } from '../lib/tty';
|
|
12
|
-
import {
|
|
9
|
+
import { GITHUB_RELEASES_URL } from '../lib/update-check';
|
|
10
|
+
import { VERSION } from '../lib/version';
|
|
13
11
|
|
|
14
12
|
type CheckStatus = 'pass' | 'warn' | 'fail';
|
|
15
13
|
|
|
@@ -20,21 +18,12 @@ type CheckResult = {
|
|
|
20
18
|
detail?: string;
|
|
21
19
|
};
|
|
22
20
|
|
|
23
|
-
const statusIcons: Record<CheckStatus, string> = {
|
|
24
|
-
pass: '\x1B[32m✔\x1B[0m',
|
|
25
|
-
warn: '\x1B[33m!\x1B[0m',
|
|
26
|
-
fail: '\x1B[31m✗\x1B[0m',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
21
|
async function checkCliVersion(): Promise<CheckResult> {
|
|
30
22
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
signal: AbortSignal.timeout(5000),
|
|
36
|
-
},
|
|
37
|
-
);
|
|
23
|
+
const res = await fetch(GITHUB_RELEASES_URL, {
|
|
24
|
+
headers: { Accept: 'application/vnd.github.v3+json' },
|
|
25
|
+
signal: AbortSignal.timeout(5000),
|
|
26
|
+
});
|
|
38
27
|
if (!res.ok) {
|
|
39
28
|
return {
|
|
40
29
|
name: 'CLI Version',
|
|
@@ -42,8 +31,19 @@ async function checkCliVersion(): Promise<CheckResult> {
|
|
|
42
31
|
message: `v${VERSION} (could not check for updates)`,
|
|
43
32
|
};
|
|
44
33
|
}
|
|
45
|
-
const data = (await res.json()) as {
|
|
46
|
-
|
|
34
|
+
const data = (await res.json()) as {
|
|
35
|
+
tag_name?: string;
|
|
36
|
+
prerelease?: boolean;
|
|
37
|
+
draft?: boolean;
|
|
38
|
+
};
|
|
39
|
+
if (data.prerelease || data.draft) {
|
|
40
|
+
return {
|
|
41
|
+
name: 'CLI Version',
|
|
42
|
+
status: 'warn',
|
|
43
|
+
message: `v${VERSION} (could not check for updates)`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const latest = data.tag_name?.replace(/^v/, '') ?? 'unknown';
|
|
47
47
|
if (latest === VERSION) {
|
|
48
48
|
return {
|
|
49
49
|
name: 'CLI Version',
|
|
@@ -66,8 +66,8 @@ async function checkCliVersion(): Promise<CheckResult> {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function checkApiKeyPresence(): CheckResult {
|
|
70
|
-
const resolved = resolveApiKey();
|
|
69
|
+
function checkApiKeyPresence(flagValue?: string): CheckResult {
|
|
70
|
+
const resolved = resolveApiKey(flagValue);
|
|
71
71
|
if (!resolved) {
|
|
72
72
|
return {
|
|
73
73
|
name: 'API Key',
|
|
@@ -84,8 +84,10 @@ function checkApiKeyPresence(): CheckResult {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async function checkApiValidationAndDomains(
|
|
88
|
-
|
|
87
|
+
async function checkApiValidationAndDomains(
|
|
88
|
+
flagValue?: string,
|
|
89
|
+
): Promise<CheckResult> {
|
|
90
|
+
const resolved = resolveApiKey(flagValue);
|
|
89
91
|
if (!resolved) {
|
|
90
92
|
return {
|
|
91
93
|
name: 'API Validation',
|
|
@@ -143,73 +145,8 @@ async function checkApiValidationAndDomains(): Promise<CheckResult> {
|
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
function checkAgentDetection(): CheckResult {
|
|
147
|
-
const home = homedir();
|
|
148
|
-
const agents: { name: string; found: boolean }[] = [];
|
|
149
|
-
|
|
150
|
-
// OpenClaw
|
|
151
|
-
agents.push({
|
|
152
|
-
name: 'OpenClaw',
|
|
153
|
-
found: existsSync(join(home, 'clawd', 'skills')),
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Cursor
|
|
157
|
-
agents.push({ name: 'Cursor', found: existsSync(join(home, '.cursor')) });
|
|
158
|
-
|
|
159
|
-
// Claude Desktop
|
|
160
|
-
const claudeConfigPaths =
|
|
161
|
-
process.platform === 'darwin'
|
|
162
|
-
? [
|
|
163
|
-
join(
|
|
164
|
-
home,
|
|
165
|
-
'Library',
|
|
166
|
-
'Application Support',
|
|
167
|
-
'Claude',
|
|
168
|
-
'claude_desktop_config.json',
|
|
169
|
-
),
|
|
170
|
-
]
|
|
171
|
-
: process.platform === 'win32'
|
|
172
|
-
? [
|
|
173
|
-
join(
|
|
174
|
-
process.env.APPDATA ?? '',
|
|
175
|
-
'Claude',
|
|
176
|
-
'claude_desktop_config.json',
|
|
177
|
-
),
|
|
178
|
-
]
|
|
179
|
-
: [join(home, '.config', 'Claude', 'claude_desktop_config.json')];
|
|
180
|
-
agents.push({
|
|
181
|
-
name: 'Claude Desktop',
|
|
182
|
-
found: claudeConfigPaths.some(existsSync),
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// VS Code MCP
|
|
186
|
-
agents.push({
|
|
187
|
-
name: 'VS Code',
|
|
188
|
-
found: existsSync(join(process.cwd(), '.vscode', 'mcp.json')),
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const detected = agents.filter((a) => a.found);
|
|
192
|
-
|
|
193
|
-
if (detected.length === 0) {
|
|
194
|
-
return {
|
|
195
|
-
name: 'AI Agents',
|
|
196
|
-
status: 'pass',
|
|
197
|
-
message: 'No AI agents detected',
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
name: 'AI Agents',
|
|
203
|
-
status: 'pass',
|
|
204
|
-
message: `Detected: ${detected.map((a) => a.name).join(', ')}`,
|
|
205
|
-
detail: 'Future: run `resend setup <agent>` to configure integration',
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
148
|
export const doctorCommand = new Command('doctor')
|
|
210
|
-
.description(
|
|
211
|
-
'Check CLI version, API key, domain status, and AI agent detection',
|
|
212
|
-
)
|
|
149
|
+
.description('Check CLI version, API key, and domain status')
|
|
213
150
|
.addHelpText(
|
|
214
151
|
'after',
|
|
215
152
|
buildHelpText({
|
|
@@ -217,9 +154,8 @@ export const doctorCommand = new Command('doctor')
|
|
|
217
154
|
context: `Checks performed:
|
|
218
155
|
CLI Version Is the installed version up to date?
|
|
219
156
|
API Key Is a key present (--api-key, RESEND_API_KEY, or credentials file)?
|
|
220
|
-
API Validation Is the key valid and accepted by the Resend API
|
|
221
|
-
|
|
222
|
-
output: ` {\n "ok": true,\n "checks": [\n {"name":"CLI Version","status":"pass","message":"v0.1.0 (latest)"},\n {"name":"API Key","status":"pass","message":"re_...abcd (source: env)"},\n {"name":"Domains","status":"pass","message":"1 verified, 0 pending"},\n {"name":"AI Agents","status":"pass","message":"Detected: Claude Desktop"}\n ]\n }\n status values: "pass" | "warn" | "fail"\n Exit code 1 if any check has status "fail"`,
|
|
157
|
+
API Validation Is the key valid and accepted by the Resend API?`,
|
|
158
|
+
output: ` {\n "ok": true,\n "checks": [\n {"name":"CLI Version","status":"pass","message":"v0.1.0 (latest)"},\n {"name":"API Key","status":"pass","message":"re_...abcd (source: env)"},\n {"name":"Domains","status":"pass","message":"1 verified, 0 pending"}\n ]\n }\n status values: "pass" | "warn" | "fail"\n Exit code 1 if any check has status "fail"`,
|
|
223
159
|
examples: ['resend doctor', 'resend doctor --json'],
|
|
224
160
|
}),
|
|
225
161
|
)
|
|
@@ -233,9 +169,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
233
169
|
}
|
|
234
170
|
|
|
235
171
|
// Check 1: CLI Version
|
|
236
|
-
let spinner = interactive
|
|
237
|
-
? createSpinner('Checking CLI version...', 'orbit')
|
|
238
|
-
: null;
|
|
172
|
+
let spinner = interactive ? createSpinner('Checking CLI version...') : null;
|
|
239
173
|
const versionCheck = await checkCliVersion();
|
|
240
174
|
checks.push(versionCheck);
|
|
241
175
|
if (versionCheck.status === 'warn') {
|
|
@@ -245,8 +179,8 @@ export const doctorCommand = new Command('doctor')
|
|
|
245
179
|
}
|
|
246
180
|
|
|
247
181
|
// Check 2: API Key
|
|
248
|
-
spinner = interactive ? createSpinner('Checking API key...'
|
|
249
|
-
const keyCheck = checkApiKeyPresence();
|
|
182
|
+
spinner = interactive ? createSpinner('Checking API key...') : null;
|
|
183
|
+
const keyCheck = checkApiKeyPresence(globalOpts.apiKey);
|
|
250
184
|
checks.push(keyCheck);
|
|
251
185
|
if (keyCheck.status === 'fail') {
|
|
252
186
|
spinner?.fail(keyCheck.message);
|
|
@@ -256,9 +190,9 @@ export const doctorCommand = new Command('doctor')
|
|
|
256
190
|
|
|
257
191
|
// Check 3: API Validation + Domains
|
|
258
192
|
spinner = interactive
|
|
259
|
-
? createSpinner('Validating API key & domains...'
|
|
193
|
+
? createSpinner('Validating API key & domains...')
|
|
260
194
|
: null;
|
|
261
|
-
const domainCheck = await checkApiValidationAndDomains();
|
|
195
|
+
const domainCheck = await checkApiValidationAndDomains(globalOpts.apiKey);
|
|
262
196
|
checks.push(domainCheck);
|
|
263
197
|
if (domainCheck.status === 'fail') {
|
|
264
198
|
spinner?.fail(domainCheck.message);
|
|
@@ -268,26 +202,10 @@ export const doctorCommand = new Command('doctor')
|
|
|
268
202
|
spinner?.stop(domainCheck.message);
|
|
269
203
|
}
|
|
270
204
|
|
|
271
|
-
// Check 4: Agent Detection
|
|
272
|
-
spinner = interactive
|
|
273
|
-
? createSpinner('Detecting AI agents...', 'scan')
|
|
274
|
-
: null;
|
|
275
|
-
const agentCheck = checkAgentDetection();
|
|
276
|
-
checks.push(agentCheck);
|
|
277
|
-
spinner?.stop(agentCheck.message);
|
|
278
|
-
|
|
279
205
|
const hasFails = checks.some((c) => c.status === 'fail');
|
|
280
206
|
|
|
281
207
|
if (!globalOpts.json && isInteractive()) {
|
|
282
208
|
console.log('');
|
|
283
|
-
for (const check of checks) {
|
|
284
|
-
const icon = statusIcons[check.status];
|
|
285
|
-
console.log(` ${icon} ${check.name}: ${check.message}`);
|
|
286
|
-
if (check.detail) {
|
|
287
|
-
console.log(` ${check.detail}`);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
console.log('');
|
|
291
209
|
} else {
|
|
292
210
|
outputResult({ ok: !hasFails, checks }, { json: globalOpts.json });
|
|
293
211
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import { Command } from '@commander-js/extra-typings';
|
|
3
3
|
import type { GlobalOpts } from '../../lib/client';
|
|
4
|
-
import { listTeams,
|
|
4
|
+
import { listTeams, removeApiKey } from '../../lib/config';
|
|
5
5
|
import { errorMessage, outputError, outputResult } from '../../lib/output';
|
|
6
6
|
import { cancelAndExit } from '../../lib/prompts';
|
|
7
7
|
import { isInteractive } from '../../lib/tty';
|
|
@@ -24,6 +24,7 @@ export const removeCommand = new Command('remove')
|
|
|
24
24
|
},
|
|
25
25
|
{ json: globalOpts.json },
|
|
26
26
|
);
|
|
27
|
+
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const teams = listTeams();
|
|
@@ -35,6 +36,7 @@ export const removeCommand = new Command('remove')
|
|
|
35
36
|
},
|
|
36
37
|
{ json: globalOpts.json },
|
|
37
38
|
);
|
|
39
|
+
return;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const choice = await p.select({
|
|
@@ -64,7 +66,7 @@ export const removeCommand = new Command('remove')
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
try {
|
|
67
|
-
|
|
69
|
+
removeApiKey(teamName);
|
|
68
70
|
} catch (err) {
|
|
69
71
|
outputError(
|
|
70
72
|
{
|
|
@@ -73,6 +75,7 @@ export const removeCommand = new Command('remove')
|
|
|
73
75
|
},
|
|
74
76
|
{ json: globalOpts.json },
|
|
75
77
|
);
|
|
78
|
+
return;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
if (globalOpts.json) {
|
|
@@ -24,6 +24,7 @@ export const switchCommand = new Command('switch')
|
|
|
24
24
|
},
|
|
25
25
|
{ json: globalOpts.json },
|
|
26
26
|
);
|
|
27
|
+
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const teams = listTeams();
|
|
@@ -35,6 +36,7 @@ export const switchCommand = new Command('switch')
|
|
|
35
36
|
},
|
|
36
37
|
{ json: globalOpts.json },
|
|
37
38
|
);
|
|
39
|
+
return;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const choice = await p.select({
|
|
@@ -63,6 +65,7 @@ export const switchCommand = new Command('switch')
|
|
|
63
65
|
},
|
|
64
66
|
{ json: globalOpts.json },
|
|
65
67
|
);
|
|
68
|
+
return;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
if (globalOpts.json) {
|
package/src/lib/config.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
unlinkSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'node:fs';
|
|
2
9
|
import { homedir } from 'node:os';
|
|
3
10
|
import { join } from 'node:path';
|
|
4
11
|
|
|
@@ -102,6 +109,10 @@ export function resolveApiKey(
|
|
|
102
109
|
|
|
103
110
|
export function storeApiKey(apiKey: string, teamName?: string): string {
|
|
104
111
|
const team = teamName || 'default';
|
|
112
|
+
const validationError = validateTeamName(team);
|
|
113
|
+
if (validationError) {
|
|
114
|
+
throw new Error(validationError);
|
|
115
|
+
}
|
|
105
116
|
const creds = readCredentials() || { active_team: 'default', teams: {} };
|
|
106
117
|
|
|
107
118
|
creds.teams[team] = { api_key: apiKey };
|
|
@@ -116,7 +127,6 @@ export function storeApiKey(apiKey: string, teamName?: string): string {
|
|
|
116
127
|
|
|
117
128
|
export function removeAllApiKeys(): string {
|
|
118
129
|
const configPath = getCredentialsPath();
|
|
119
|
-
const { unlinkSync } = require('node:fs');
|
|
120
130
|
unlinkSync(configPath);
|
|
121
131
|
return configPath;
|
|
122
132
|
}
|
|
@@ -125,13 +135,20 @@ export function removeApiKey(teamName?: string): string {
|
|
|
125
135
|
const creds = readCredentials();
|
|
126
136
|
if (!creds) {
|
|
127
137
|
const configPath = getCredentialsPath();
|
|
138
|
+
if (!existsSync(configPath)) {
|
|
139
|
+
throw new Error('No credentials file found.');
|
|
140
|
+
}
|
|
128
141
|
// Try to delete legacy file
|
|
129
|
-
const { unlinkSync } = require('node:fs');
|
|
130
142
|
unlinkSync(configPath);
|
|
131
143
|
return configPath;
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
const team = teamName || resolveTeamName();
|
|
147
|
+
if (!creds.teams[team]) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Team "${team}" not found. Available teams: ${Object.keys(creds.teams).join(', ')}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
135
152
|
delete creds.teams[team];
|
|
136
153
|
|
|
137
154
|
// If we removed the active team, switch to first available or "default"
|
|
@@ -142,7 +159,6 @@ export function removeApiKey(teamName?: string): string {
|
|
|
142
159
|
|
|
143
160
|
// If no teams left, delete the file
|
|
144
161
|
if (Object.keys(creds.teams).length === 0) {
|
|
145
|
-
const { unlinkSync } = require('node:fs');
|
|
146
162
|
const configPath = getCredentialsPath();
|
|
147
163
|
unlinkSync(configPath);
|
|
148
164
|
return configPath;
|
|
@@ -152,6 +168,10 @@ export function removeApiKey(teamName?: string): string {
|
|
|
152
168
|
}
|
|
153
169
|
|
|
154
170
|
export function setActiveTeam(teamName: string): void {
|
|
171
|
+
const validationError = validateTeamName(teamName);
|
|
172
|
+
if (validationError) {
|
|
173
|
+
throw new Error(validationError);
|
|
174
|
+
}
|
|
155
175
|
const creds = readCredentials();
|
|
156
176
|
if (!creds) {
|
|
157
177
|
throw new Error('No credentials file found. Run: resend login');
|
|
@@ -176,36 +196,22 @@ export function listTeams(): Array<{ name: string; active: boolean }> {
|
|
|
176
196
|
}));
|
|
177
197
|
}
|
|
178
198
|
|
|
179
|
-
export function
|
|
180
|
-
if (
|
|
181
|
-
return
|
|
199
|
+
export function validateTeamName(name: string): string | undefined {
|
|
200
|
+
if (!name || name.length === 0) {
|
|
201
|
+
return 'Team name must not be empty';
|
|
182
202
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
export function removeTeam(teamName: string): void {
|
|
187
|
-
const creds = readCredentials();
|
|
188
|
-
if (!creds) {
|
|
189
|
-
throw new Error('No credentials file found.');
|
|
203
|
+
if (name.length > 64) {
|
|
204
|
+
return 'Team name must be 64 characters or fewer';
|
|
190
205
|
}
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
`Team "${teamName}" not found. Available teams: ${Object.keys(creds.teams).join(', ')}`,
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
delete creds.teams[teamName];
|
|
198
|
-
|
|
199
|
-
if (creds.active_team === teamName) {
|
|
200
|
-
const remaining = Object.keys(creds.teams);
|
|
201
|
-
creds.active_team = remaining[0] || 'default';
|
|
206
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
207
|
+
return 'Team name must contain only letters, numbers, dashes, and underscores';
|
|
202
208
|
}
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
203
211
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return;
|
|
212
|
+
export function maskKey(key: string): string {
|
|
213
|
+
if (key.length <= 7) {
|
|
214
|
+
return `${key.slice(0, 3)}...`;
|
|
208
215
|
}
|
|
209
|
-
|
|
210
|
-
writeCredentials(creds);
|
|
216
|
+
return `${key.slice(0, 3)}...${key.slice(-4)}`;
|
|
211
217
|
}
|
package/src/lib/spinner.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import spinners from 'unicode-animations';
|
|
2
1
|
import type { GlobalOpts } from './client';
|
|
3
2
|
import { errorMessage, outputError } from './output';
|
|
4
3
|
import { isInteractive, isUnicodeSupported } from './tty';
|
|
@@ -9,6 +8,19 @@ const TICK = isUnicodeSupported ? String.fromCodePoint(0x2714) : 'v'; // ✔
|
|
|
9
8
|
const WARN = isUnicodeSupported ? String.fromCodePoint(0x26a0) : '!'; // ⚠
|
|
10
9
|
const CROSS = isUnicodeSupported ? String.fromCodePoint(0x2717) : 'x'; // ✗
|
|
11
10
|
|
|
11
|
+
// Braille spinner: cycle through U+2800-block dot patterns.
|
|
12
|
+
const SPINNER_FRAMES = [
|
|
13
|
+
'\u2839',
|
|
14
|
+
'\u2838',
|
|
15
|
+
'\u2834',
|
|
16
|
+
'\u2826',
|
|
17
|
+
'\u2807',
|
|
18
|
+
'\u280F',
|
|
19
|
+
'\u2819',
|
|
20
|
+
'\u2839',
|
|
21
|
+
];
|
|
22
|
+
const SPINNER_INTERVAL = 80;
|
|
23
|
+
|
|
12
24
|
type SdkResponse<T> = { data: T | null; error: { message: string } | null };
|
|
13
25
|
|
|
14
26
|
/**
|
|
@@ -21,7 +33,7 @@ export async function withSpinner<T>(
|
|
|
21
33
|
errorCode: string,
|
|
22
34
|
globalOpts: GlobalOpts,
|
|
23
35
|
): Promise<T> {
|
|
24
|
-
const spinner = createSpinner(messages.loading,
|
|
36
|
+
const spinner = createSpinner(messages.loading, globalOpts.quiet);
|
|
25
37
|
try {
|
|
26
38
|
const { data, error } = await call();
|
|
27
39
|
if (error) {
|
|
@@ -49,13 +61,7 @@ export async function withSpinner<T>(
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
export function createSpinner(
|
|
55
|
-
message: string,
|
|
56
|
-
name: SpinnerName = 'braille',
|
|
57
|
-
quiet?: boolean,
|
|
58
|
-
) {
|
|
64
|
+
export function createSpinner(message: string, quiet?: boolean) {
|
|
59
65
|
if (quiet || !isInteractive()) {
|
|
60
66
|
return {
|
|
61
67
|
update(_msg: string) {},
|
|
@@ -65,7 +71,8 @@ export function createSpinner(
|
|
|
65
71
|
};
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
const
|
|
74
|
+
const frames = isUnicodeSupported ? SPINNER_FRAMES : ['-', '\\', '|', '/'];
|
|
75
|
+
const interval = SPINNER_INTERVAL;
|
|
69
76
|
let i = 0;
|
|
70
77
|
let text = message;
|
|
71
78
|
|