@sendblue/cli 0.6.1 → 0.6.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.
@@ -25,7 +25,7 @@ export async function addContactCommand(number) {
25
25
  spinner.succeed(`Contact ${formatPhoneNumber(normalized)} is already verified!`);
26
26
  console.log();
27
27
  console.log(chalk.bold(' You can now send messages:'));
28
- console.log(chalk.cyan(` sendblue send ${normalized} "Hello!"`));
28
+ console.log(chalk.cyan(` sendblue send ${normalized} 'Hello!'`));
29
29
  console.log();
30
30
  return;
31
31
  }
@@ -1 +1,8 @@
1
- export declare function setupCommand(): Promise<void>;
1
+ interface SetupOptions {
2
+ email?: string;
3
+ code?: string;
4
+ company?: string;
5
+ contact?: string;
6
+ }
7
+ export declare function setupCommand(opts: SetupOptions): Promise<void>;
8
+ export {};
@@ -17,7 +17,8 @@ const onCancel = () => {
17
17
  printError('Setup cancelled.');
18
18
  process.exit(0);
19
19
  };
20
- export async function setupCommand() {
20
+ export async function setupCommand(opts) {
21
+ const nonInteractive = !!(opts.email && opts.code && opts.company);
21
22
  console.log();
22
23
  printLogo();
23
24
  console.log(chalk.bold(' sendblue setup'));
@@ -26,57 +27,113 @@ export async function setupCommand() {
26
27
  // Check for existing credentials
27
28
  const existing = getCredentials();
28
29
  if (existing) {
29
- const { overwrite } = await prompts({
30
- type: 'confirm',
31
- name: 'overwrite',
32
- message: `You already have an account configured (${existing.email}). Overwrite?`,
33
- initial: false
34
- }, { onCancel });
35
- if (!overwrite) {
36
- console.log(chalk.dim(' Setup cancelled.'));
37
- return;
30
+ if (nonInteractive) {
31
+ console.log(chalk.dim(` Overwriting existing account (${existing.email})`));
32
+ }
33
+ else {
34
+ const { overwrite } = await prompts({
35
+ type: 'confirm',
36
+ name: 'overwrite',
37
+ message: `You already have an account configured (${existing.email}). Overwrite?`,
38
+ initial: false
39
+ }, { onCancel });
40
+ if (!overwrite) {
41
+ console.log(chalk.dim(' Setup cancelled.'));
42
+ return;
43
+ }
38
44
  }
39
45
  }
40
- // Step 1: Collect email
41
- const { email } = await prompts({
42
- type: 'text',
43
- name: 'email',
44
- message: 'Email',
45
- validate: (v) => /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(v) || 'Enter a valid email'
46
- }, { onCancel });
47
- // Step 2: Send verification code
48
- const sendSpinner = ora({ text: 'Sending verification code...', indent: 2 }).start();
49
- try {
50
- await sendCode(email);
51
- sendSpinner.succeed(`Code sent to ${email}`);
46
+ // Validate flags upfront
47
+ if (opts.email && !/^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(opts.email)) {
48
+ printError('Invalid email address.');
49
+ process.exit(1);
52
50
  }
53
- catch (err) {
54
- sendSpinner.fail(`Failed to send code: ${err instanceof Error ? err.message : String(err)}`);
51
+ if (opts.code && !/^\d{8}$/.test(opts.code)) {
52
+ printError('Verification code must be 8 digits.');
55
53
  process.exit(1);
56
54
  }
57
- console.log();
58
- // Step 3: Enter code
59
- const { code } = await prompts({
60
- type: 'text',
61
- name: 'code',
62
- message: 'Verification code',
63
- validate: (v) => /^\d{8}$/.test(v) || 'Enter the 8-digit code from your email'
64
- }, { onCancel });
65
- // Step 4: Company name (required for setup)
66
- const { companyName } = await prompts({
67
- type: 'text',
68
- name: 'companyName',
69
- message: 'Company name (lowercase, hyphens/underscores only)',
70
- validate: (v) => {
71
- if (!v)
72
- return 'Company name is required';
73
- if (!/^[a-z0-9_-]+$/.test(v))
74
- return 'Only lowercase letters, numbers, hyphens, and underscores';
75
- if (v.length < 3 || v.length > 64)
76
- return 'Must be 3-64 characters';
77
- return true;
55
+ if (opts.company) {
56
+ if (!/^[a-z0-9_-]+$/.test(opts.company)) {
57
+ printError('Company name: only lowercase letters, numbers, hyphens, and underscores.');
58
+ process.exit(1);
59
+ }
60
+ if (opts.company.length < 3 || opts.company.length > 64) {
61
+ printError('Company name must be 3-64 characters.');
62
+ process.exit(1);
63
+ }
64
+ }
65
+ // Step 1: Collect email
66
+ let email;
67
+ if (opts.email) {
68
+ email = opts.email;
69
+ }
70
+ else {
71
+ const response = await prompts({
72
+ type: 'text',
73
+ name: 'email',
74
+ message: 'Email',
75
+ validate: (v) => /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(v) || 'Enter a valid email'
76
+ }, { onCancel });
77
+ email = response.email;
78
+ }
79
+ // Step 2: Send verification code (skip if code already provided)
80
+ if (!opts.code) {
81
+ const sendSpinner = ora({ text: 'Sending verification code...', indent: 2 }).start();
82
+ try {
83
+ await sendCode(email);
84
+ sendSpinner.succeed(`Code sent to ${email}`);
85
+ }
86
+ catch (err) {
87
+ sendSpinner.fail(`Failed to send code: ${err instanceof Error ? err.message : String(err)}`);
88
+ process.exit(1);
89
+ }
90
+ // Non-interactive: just send the code and exit so the user can
91
+ // re-run with --code once they have it
92
+ if (opts.email) {
93
+ console.log();
94
+ console.log(chalk.dim(' Once you have the code, run:'));
95
+ console.log(chalk.cyan(` sendblue setup --email ${email} --code <CODE> --company <NAME>`));
96
+ console.log();
97
+ return;
78
98
  }
79
- }, { onCancel });
99
+ console.log();
100
+ }
101
+ // Step 3: Enter code
102
+ let code;
103
+ if (opts.code) {
104
+ code = opts.code;
105
+ }
106
+ else {
107
+ const response = await prompts({
108
+ type: 'text',
109
+ name: 'code',
110
+ message: 'Verification code',
111
+ validate: (v) => /^\d{8}$/.test(v) || 'Enter the 8-digit code from your email'
112
+ }, { onCancel });
113
+ code = response.code;
114
+ }
115
+ // Step 4: Company name
116
+ let companyName;
117
+ if (opts.company) {
118
+ companyName = opts.company;
119
+ }
120
+ else {
121
+ const response = await prompts({
122
+ type: 'text',
123
+ name: 'companyName',
124
+ message: 'Company name (lowercase, hyphens/underscores only)',
125
+ validate: (v) => {
126
+ if (!v)
127
+ return 'Company name is required';
128
+ if (!/^[a-z0-9_-]+$/.test(v))
129
+ return 'Only lowercase letters, numbers, hyphens, and underscores';
130
+ if (v.length < 3 || v.length > 64)
131
+ return 'Must be 3-64 characters';
132
+ return true;
133
+ }
134
+ }, { onCancel });
135
+ companyName = response.companyName;
136
+ }
80
137
  // Step 5: Verify code + create account
81
138
  const setupSpinner = ora({ text: 'Setting up your account...', indent: 2 }).start();
82
139
  let result;
@@ -100,18 +157,29 @@ export async function setupCommand() {
100
157
  process.exit(1);
101
158
  }
102
159
  // Step 6: Add first contact
103
- console.log(chalk.bold(' Add your first contact'));
104
- console.log(chalk.dim(' Enter the phone number you want to message via iMessage.'));
105
- console.log();
106
- const { contactNumber } = await prompts({
107
- type: 'text',
108
- name: 'contactNumber',
109
- message: 'Contact phone number',
110
- validate: (v) => {
111
- const n = normalizeNumber(v);
112
- return /^\+\d{10,15}$/.test(n) || 'Enter a valid phone number (e.g. +15551234567)';
113
- }
114
- }, { onCancel });
160
+ let contactNumber;
161
+ if (opts.contact) {
162
+ contactNumber = opts.contact;
163
+ }
164
+ else if (!nonInteractive) {
165
+ console.log(chalk.bold(' Add your first contact'));
166
+ console.log(chalk.dim(' Enter the phone number you want to message via iMessage.'));
167
+ console.log();
168
+ const response = await prompts({
169
+ type: 'text',
170
+ name: 'contactNumber',
171
+ message: 'Contact phone number',
172
+ validate: (v) => {
173
+ const n = normalizeNumber(v);
174
+ return /^\+\d{10,15}$/.test(n) || 'Enter a valid phone number (e.g. +15551234567)';
175
+ }
176
+ }, { onCancel });
177
+ contactNumber = response.contactNumber;
178
+ }
179
+ if (!contactNumber) {
180
+ console.log();
181
+ return;
182
+ }
115
183
  const normalized = normalizeNumber(contactNumber);
116
184
  const contactSpinner = ora({ text: `Adding contact ${formatPhoneNumber(normalized)}...`, indent: 2 }).start();
117
185
  try {
@@ -120,7 +188,7 @@ export async function setupCommand() {
120
188
  contactSpinner.succeed(`Contact ${formatPhoneNumber(normalized)} is already verified!`);
121
189
  console.log();
122
190
  console.log(chalk.bold(' You\'re all set! Send a message:'));
123
- console.log(chalk.cyan(` sendblue send ${normalized} "Hello from Sendblue!"`));
191
+ console.log(chalk.cyan(` sendblue send ${normalized} 'Hello from Sendblue!'`));
124
192
  console.log();
125
193
  return;
126
194
  }
@@ -136,17 +204,19 @@ export async function setupCommand() {
136
204
  console.log();
137
205
  console.log(chalk.cyan.bold(` ${formatPhoneNumber(sharedNumber)}`));
138
206
  console.log();
139
- // Show QR code for easy texting
140
- const qr = await generateSmsQr(sharedNumber);
141
- console.log(chalk.dim(' Or scan this QR code to open a text:'));
142
- console.log();
143
- // Indent each line of the QR code
144
- for (const line of qr.split('\n')) {
145
- console.log(` ${line}`);
207
+ if (!nonInteractive) {
208
+ // Show QR code for easy texting
209
+ const qr = await generateSmsQr(sharedNumber);
210
+ console.log(chalk.dim(' Or scan this QR code to open a text:'));
211
+ console.log();
212
+ // Indent each line of the QR code
213
+ for (const line of qr.split('\n')) {
214
+ console.log(` ${line}`);
215
+ }
216
+ console.log();
146
217
  }
147
- console.log();
148
218
  console.log(chalk.dim(' Once they text in, run:'));
149
- console.log(chalk.cyan(` sendblue send ${normalized} "Hello from Sendblue!"`));
219
+ console.log(chalk.cyan(` sendblue send ${normalized} 'Hello from Sendblue!'`));
150
220
  }
151
221
  console.log();
152
222
  }
package/dist/index.js CHANGED
@@ -19,6 +19,10 @@ program
19
19
  program
20
20
  .command('setup')
21
21
  .description('Create a new Sendblue account and get an iMessage number')
22
+ .option('--email <email>', 'Email address (skip prompt)')
23
+ .option('--code <code>', 'Verification code (skip prompt, requires --email)')
24
+ .option('--company <name>', 'Company name (skip prompt)')
25
+ .option('--contact <number>', 'First contact phone number (skip prompt)')
22
26
  .action(setupCommand);
23
27
  program
24
28
  .command('login')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendblue/cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Sendblue CLI — iMessage numbers for agents",
5
5
  "type": "module",
6
6
  "bin": {