@sendblue/cli 0.4.1 → 0.5.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/dist/commands/add-contact.js +17 -1
- package/dist/commands/login.js +2 -1
- package/dist/commands/setup.js +70 -9
- package/dist/lib/format.d.ts +1 -0
- package/dist/lib/format.js +21 -0
- package/package.json +4 -2
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
+
import qrcode from 'qrcode-terminal';
|
|
3
4
|
import { getCredentials } from '../lib/config.js';
|
|
4
5
|
import { addContact, getSharedContacts } from '../lib/api.js';
|
|
5
6
|
import { formatPhoneNumber, normalizeNumber, printError } from '../lib/format.js';
|
|
7
|
+
function generateSmsQr(phoneNumber) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
qrcode.generate(`sms:${phoneNumber}`, { small: true }, (code) => {
|
|
10
|
+
resolve(code);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
6
14
|
export async function addContactCommand(number) {
|
|
7
15
|
const creds = getCredentials();
|
|
8
16
|
if (!creds) {
|
|
@@ -29,10 +37,18 @@ export async function addContactCommand(number) {
|
|
|
29
37
|
console.log(chalk.bold(' To verify this contact:'));
|
|
30
38
|
console.log();
|
|
31
39
|
if (sharedNumber) {
|
|
32
|
-
console.log(` Have ${formatPhoneNumber(normalized)} send
|
|
40
|
+
console.log(` Have ${formatPhoneNumber(normalized)} send any text to:`);
|
|
33
41
|
console.log();
|
|
34
42
|
console.log(chalk.cyan.bold(` ${formatPhoneNumber(sharedNumber)}`));
|
|
35
43
|
console.log();
|
|
44
|
+
// Show QR code for easy texting
|
|
45
|
+
const qr = await generateSmsQr(sharedNumber);
|
|
46
|
+
console.log(chalk.dim(' Or scan to open a text:'));
|
|
47
|
+
console.log();
|
|
48
|
+
for (const line of qr.split('\n')) {
|
|
49
|
+
console.log(` ${line}`);
|
|
50
|
+
}
|
|
51
|
+
console.log();
|
|
36
52
|
console.log(chalk.dim(` (any message works — "hi" is fine)`));
|
|
37
53
|
}
|
|
38
54
|
else {
|
package/dist/commands/login.js
CHANGED
|
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { getCredentials, saveCredentials, credentialsPath } from '../lib/config.js';
|
|
5
5
|
import { sendCode, verifyLogin } from '../lib/api.js';
|
|
6
|
-
import { printError, formatPhoneNumber } from '../lib/format.js';
|
|
6
|
+
import { printError, printLogo, formatPhoneNumber } from '../lib/format.js';
|
|
7
7
|
const onCancel = () => {
|
|
8
8
|
console.log();
|
|
9
9
|
printError('Login cancelled.');
|
|
@@ -11,6 +11,7 @@ const onCancel = () => {
|
|
|
11
11
|
};
|
|
12
12
|
export async function loginCommand() {
|
|
13
13
|
console.log();
|
|
14
|
+
printLogo();
|
|
14
15
|
console.log(chalk.bold(' sendblue login'));
|
|
15
16
|
console.log(chalk.dim(' Log in to an existing Sendblue account'));
|
|
16
17
|
console.log();
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
+
import qrcode from 'qrcode-terminal';
|
|
4
5
|
import { getCredentials, saveCredentials, credentialsPath } from '../lib/config.js';
|
|
5
|
-
import { sendCode, verifySetup } from '../lib/api.js';
|
|
6
|
-
import { printCredentials, printError, formatPhoneNumber } from '../lib/format.js';
|
|
6
|
+
import { sendCode, verifySetup, addContact, getSharedContacts } from '../lib/api.js';
|
|
7
|
+
import { printCredentials, printError, printLogo, formatPhoneNumber, normalizeNumber } from '../lib/format.js';
|
|
8
|
+
function generateSmsQr(phoneNumber) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
qrcode.generate(`sms:${phoneNumber}`, { small: true }, (code) => {
|
|
11
|
+
resolve(code);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
7
15
|
const onCancel = () => {
|
|
8
16
|
console.log();
|
|
9
17
|
printError('Setup cancelled.');
|
|
@@ -11,6 +19,7 @@ const onCancel = () => {
|
|
|
11
19
|
};
|
|
12
20
|
export async function setupCommand() {
|
|
13
21
|
console.log();
|
|
22
|
+
printLogo();
|
|
14
23
|
console.log(chalk.bold(' sendblue setup'));
|
|
15
24
|
console.log(chalk.dim(' Create a new Sendblue account'));
|
|
16
25
|
console.log();
|
|
@@ -70,8 +79,9 @@ export async function setupCommand() {
|
|
|
70
79
|
}, { onCancel });
|
|
71
80
|
// Step 5: Verify code + create account
|
|
72
81
|
const setupSpinner = ora({ text: 'Setting up your account...', indent: 2 }).start();
|
|
82
|
+
let result;
|
|
73
83
|
try {
|
|
74
|
-
|
|
84
|
+
result = await verifySetup(email, code, companyName);
|
|
75
85
|
setupSpinner.succeed('Account created!');
|
|
76
86
|
saveCredentials({
|
|
77
87
|
apiKey: result.apiKey,
|
|
@@ -84,16 +94,67 @@ export async function setupCommand() {
|
|
|
84
94
|
printCredentials(result);
|
|
85
95
|
console.log(chalk.dim(` Credentials saved to ${credentialsPath()}`));
|
|
86
96
|
console.log();
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
setupSpinner.fail(`Setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
// 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 });
|
|
115
|
+
const normalized = normalizeNumber(contactNumber);
|
|
116
|
+
const contactSpinner = ora({ text: `Adding contact ${formatPhoneNumber(normalized)}...`, indent: 2 }).start();
|
|
117
|
+
try {
|
|
118
|
+
const contact = await addContact(result.apiKey, result.apiSecret, normalized);
|
|
119
|
+
if (contact.verified) {
|
|
120
|
+
contactSpinner.succeed(`Contact ${formatPhoneNumber(normalized)} is already verified!`);
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(chalk.bold(' You\'re all set! Send a message:'));
|
|
123
|
+
console.log(chalk.cyan(` sendblue send ${normalized} "Hello from Sendblue!"`));
|
|
124
|
+
console.log();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
contactSpinner.succeed('Contact added!');
|
|
89
128
|
console.log();
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
// Get the shared number to show QR code
|
|
130
|
+
const contacts = await getSharedContacts(result.apiKey, result.apiSecret);
|
|
131
|
+
const sharedNumber = contacts.sharedNumber || result.assignedNumber;
|
|
132
|
+
if (sharedNumber) {
|
|
133
|
+
console.log(chalk.bold(' One last step — verify the contact'));
|
|
134
|
+
console.log();
|
|
135
|
+
console.log(` Have ${chalk.cyan(formatPhoneNumber(normalized))} send any text to:`);
|
|
136
|
+
console.log();
|
|
137
|
+
console.log(chalk.cyan.bold(` ${formatPhoneNumber(sharedNumber)}`));
|
|
138
|
+
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:'));
|
|
92
142
|
console.log();
|
|
143
|
+
// Indent each line of the QR code
|
|
144
|
+
for (const line of qr.split('\n')) {
|
|
145
|
+
console.log(` ${line}`);
|
|
146
|
+
}
|
|
147
|
+
console.log();
|
|
148
|
+
console.log(chalk.dim(' Once they text in, run:'));
|
|
149
|
+
console.log(chalk.cyan(` sendblue send ${normalized} "Hello from Sendblue!"`));
|
|
93
150
|
}
|
|
151
|
+
console.log();
|
|
94
152
|
}
|
|
95
153
|
catch (err) {
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
contactSpinner.fail(`Failed to add contact: ${err instanceof Error ? err.message : String(err)}`);
|
|
155
|
+
console.log();
|
|
156
|
+
console.log(chalk.dim(' You can add contacts later with:'));
|
|
157
|
+
console.log(chalk.cyan(` sendblue add-contact +15551234567`));
|
|
158
|
+
console.log();
|
|
98
159
|
}
|
|
99
160
|
}
|
package/dist/lib/format.d.ts
CHANGED
package/dist/lib/format.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
const blue = chalk.hex('#0088FF');
|
|
3
|
+
export function printLogo() {
|
|
4
|
+
const logo = [
|
|
5
|
+
` ${blue('██')} `,
|
|
6
|
+
` ${blue('██████')} `,
|
|
7
|
+
` ${blue('██████████')} `,
|
|
8
|
+
` ${blue('████████████')}`,
|
|
9
|
+
` ${blue('██████████████')}`,
|
|
10
|
+
` ${blue('██████████████')}`,
|
|
11
|
+
` ${blue('████████████')}`,
|
|
12
|
+
` ${blue('██████████')} `,
|
|
13
|
+
` ${blue('████████')} `,
|
|
14
|
+
];
|
|
15
|
+
for (const line of logo) {
|
|
16
|
+
console.log(` ${line}`);
|
|
17
|
+
}
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(blue.bold(' sendblue'));
|
|
20
|
+
console.log(chalk.dim(' iMessage for agents'));
|
|
21
|
+
console.log();
|
|
22
|
+
}
|
|
2
23
|
export function normalizeNumber(input) {
|
|
3
24
|
// Strip non-digit chars except leading +
|
|
4
25
|
let num = input.replace(/[^\d+]/g, '');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendblue/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Sendblue CLI — iMessage numbers for agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
"chalk": "^5.3.0",
|
|
31
31
|
"commander": "^12.1.0",
|
|
32
32
|
"ora": "^9.3.0",
|
|
33
|
-
"prompts": "^2.4.2"
|
|
33
|
+
"prompts": "^2.4.2",
|
|
34
|
+
"qrcode-terminal": "^0.12.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/node": "^20.0.0",
|
|
37
38
|
"@types/prompts": "^2.4.9",
|
|
39
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
38
40
|
"typescript": "^5.5.0"
|
|
39
41
|
}
|
|
40
42
|
}
|