@sendblue/cli 0.1.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.
@@ -0,0 +1 @@
1
+ export declare function sendCommand(number: string, message: string): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import chalk from 'chalk';
2
+ import { getCredentials } from '../lib/config.js';
3
+ import { sendMessage } from '../lib/api.js';
4
+ import { printError, printSuccess } from '../lib/format.js';
5
+ export async function sendCommand(number, message) {
6
+ const creds = getCredentials();
7
+ if (!creds) {
8
+ printError('No credentials found. Run `sendblue setup` first.');
9
+ process.exit(1);
10
+ }
11
+ try {
12
+ const result = await sendMessage(creds.apiKey, creds.apiSecret, number, message);
13
+ printSuccess(`Message sent to ${number}`);
14
+ if (result.messageId) {
15
+ console.log(chalk.dim(` Message ID: ${result.messageId}`));
16
+ }
17
+ }
18
+ catch (err) {
19
+ printError(`Send failed: ${err instanceof Error ? err.message : String(err)}`);
20
+ process.exit(1);
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ export declare function setupCommand(): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import prompts from 'prompts';
2
+ import chalk from 'chalk';
3
+ import { getCredentials, saveCredentials, credentialsPath } from '../lib/config.js';
4
+ import { setupAccount } from '../lib/api.js';
5
+ import { printCredentials, printError, printInfo } from '../lib/format.js';
6
+ export async function setupCommand() {
7
+ console.log();
8
+ console.log(chalk.bold(' sendblue setup'));
9
+ console.log(chalk.dim(' iMessage numbers for agents'));
10
+ console.log();
11
+ // Check for existing credentials
12
+ const existing = getCredentials();
13
+ if (existing) {
14
+ const { overwrite } = await prompts({
15
+ type: 'confirm',
16
+ name: 'overwrite',
17
+ message: `You already have an account configured (${existing.email}). Overwrite?`,
18
+ initial: false
19
+ });
20
+ if (!overwrite) {
21
+ printInfo('Setup cancelled.');
22
+ return;
23
+ }
24
+ }
25
+ // Collect info
26
+ const response = await prompts([
27
+ {
28
+ type: 'text',
29
+ name: 'email',
30
+ message: 'Email',
31
+ validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || 'Enter a valid email'
32
+ },
33
+ {
34
+ type: 'text',
35
+ name: 'companyName',
36
+ message: 'Company name (optional)',
37
+ initial: ''
38
+ }
39
+ ]);
40
+ if (!response.email) {
41
+ printError('Setup cancelled.');
42
+ return;
43
+ }
44
+ console.log();
45
+ printInfo(' Setting up your account...');
46
+ try {
47
+ const result = await setupAccount(response.email, response.companyName || undefined);
48
+ saveCredentials({
49
+ apiKey: result.apiKey,
50
+ apiSecret: result.apiSecret,
51
+ email: result.email,
52
+ assignedNumber: result.assignedNumber,
53
+ plan: result.plan,
54
+ createdAt: new Date().toISOString()
55
+ });
56
+ printCredentials(result);
57
+ console.log(chalk.dim(` Credentials saved to ${credentialsPath()}`));
58
+ console.log();
59
+ console.log(chalk.bold(' Quick start:'));
60
+ console.log(chalk.cyan(` sendblue send +15551234567 "Hello from Sendblue!"`));
61
+ console.log();
62
+ }
63
+ catch (err) {
64
+ printError(` Setup failed: ${err instanceof Error ? err.message : String(err)}`);
65
+ process.exit(1);
66
+ }
67
+ }
@@ -0,0 +1 @@
1
+ export declare function statusCommand(): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import chalk from 'chalk';
2
+ import { getCredentials } from '../lib/config.js';
3
+ import { getAccount } from '../lib/api.js';
4
+ import { formatPhoneNumber, printError } from '../lib/format.js';
5
+ export async function statusCommand() {
6
+ const creds = getCredentials();
7
+ if (!creds) {
8
+ printError('No credentials found. Run `sendblue setup` first.');
9
+ process.exit(1);
10
+ }
11
+ try {
12
+ const account = await getAccount(creds.apiKey, creds.apiSecret);
13
+ console.log();
14
+ console.log(chalk.bold(' Account Status'));
15
+ console.log();
16
+ console.log(` ${chalk.bold('Email')}: ${creds.email}`);
17
+ console.log(` ${chalk.bold('Phone Number')}: ${formatPhoneNumber(creds.assignedNumber)}`);
18
+ console.log(` ${chalk.bold('Plan')}: ${account.plan || creds.plan}`);
19
+ console.log();
20
+ }
21
+ catch (err) {
22
+ printError(`Failed to fetch status: ${err instanceof Error ? err.message : String(err)}`);
23
+ process.exit(1);
24
+ }
25
+ }
@@ -0,0 +1 @@
1
+ export declare function whoamiCommand(): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import chalk from 'chalk';
2
+ import { getCredentials, credentialsPath } from '../lib/config.js';
3
+ import { testKeys } from '../lib/api.js';
4
+ import { formatPhoneNumber, printError } from '../lib/format.js';
5
+ export async function whoamiCommand() {
6
+ const creds = getCredentials();
7
+ if (!creds) {
8
+ printError('No credentials found. Run `sendblue setup` first.');
9
+ process.exit(1);
10
+ }
11
+ const valid = await testKeys(creds.apiKey, creds.apiSecret);
12
+ console.log();
13
+ console.log(chalk.bold(' Current Account'));
14
+ console.log();
15
+ console.log(` ${chalk.bold('Email')}: ${creds.email}`);
16
+ console.log(` ${chalk.bold('Phone Number')}: ${formatPhoneNumber(creds.assignedNumber)}`);
17
+ console.log(` ${chalk.bold('API Key')}: ${creds.apiKey.slice(0, 8)}...`);
18
+ console.log(` ${chalk.bold('Plan')}: ${creds.plan}`);
19
+ console.log(` ${chalk.bold('Keys Valid')}: ${valid ? chalk.green('Yes') : chalk.red('No')}`);
20
+ console.log(` ${chalk.bold('Config')}: ${credentialsPath()}`);
21
+ console.log();
22
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { setupCommand } from './commands/setup.js';
4
+ import { sendCommand } from './commands/send.js';
5
+ import { statusCommand } from './commands/status.js';
6
+ import { whoamiCommand } from './commands/whoami.js';
7
+ const program = new Command();
8
+ program
9
+ .name('sendblue')
10
+ .description('Sendblue CLI — iMessage numbers for agents')
11
+ .version('0.1.0');
12
+ program
13
+ .command('setup')
14
+ .description('Create an account and get a phone number')
15
+ .action(setupCommand);
16
+ program
17
+ .command('send')
18
+ .description('Send a message')
19
+ .argument('<number>', 'Recipient phone number (E.164 format)')
20
+ .argument('<message>', 'Message content')
21
+ .action(sendCommand);
22
+ program
23
+ .command('status')
24
+ .description('Check your account status')
25
+ .action(statusCommand);
26
+ program
27
+ .command('whoami')
28
+ .description('Show current credentials')
29
+ .action(whoamiCommand);
30
+ program.parse();
@@ -0,0 +1,25 @@
1
+ interface SetupResponse {
2
+ status: string;
3
+ email: string;
4
+ apiKey: string;
5
+ apiSecret: string;
6
+ assignedNumber: string;
7
+ plan: string;
8
+ }
9
+ interface SendMessageResponse {
10
+ status: string;
11
+ message?: string;
12
+ messageId?: string;
13
+ [key: string]: unknown;
14
+ }
15
+ interface AccountResponse {
16
+ status?: string;
17
+ email?: string;
18
+ plan?: string;
19
+ [key: string]: unknown;
20
+ }
21
+ export declare function setupAccount(email: string, companyName?: string): Promise<SetupResponse>;
22
+ export declare function sendMessage(apiKey: string, apiSecret: string, number: string, content: string): Promise<SendMessageResponse>;
23
+ export declare function getAccount(apiKey: string, apiSecret: string): Promise<AccountResponse>;
24
+ export declare function testKeys(apiKey: string, apiSecret: string): Promise<boolean>;
25
+ export {};
@@ -0,0 +1,59 @@
1
+ const API_BASE = 'https://api.sendblue.co';
2
+ const SETUP_BASE = process.env.SENDBLUE_SETUP_URL || 'https://app.sendblue.co';
3
+ export async function setupAccount(email, companyName) {
4
+ const res = await fetch(`${SETUP_BASE}/api/v3/cli/setup`, {
5
+ method: 'POST',
6
+ headers: { 'Content-Type': 'application/json' },
7
+ body: JSON.stringify({ email, companyName })
8
+ });
9
+ if (!res.ok) {
10
+ const body = await res.json().catch(() => ({}));
11
+ throw new Error(body.error || body.message || `Setup failed (${res.status})`);
12
+ }
13
+ return res.json();
14
+ }
15
+ export async function sendMessage(apiKey, apiSecret, number, content) {
16
+ const res = await fetch(`${API_BASE}/api/send-message`, {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ 'sb-api-key-id': apiKey,
21
+ 'sb-api-secret-key': apiSecret
22
+ },
23
+ body: JSON.stringify({ number, content })
24
+ });
25
+ if (!res.ok) {
26
+ const body = await res.json().catch(() => ({}));
27
+ throw new Error(body.error || body.message || `Send failed (${res.status})`);
28
+ }
29
+ return res.json();
30
+ }
31
+ export async function getAccount(apiKey, apiSecret) {
32
+ const res = await fetch(`${API_BASE}/account`, {
33
+ method: 'GET',
34
+ headers: {
35
+ 'sb-api-key-id': apiKey,
36
+ 'sb-api-secret-key': apiSecret
37
+ }
38
+ });
39
+ if (!res.ok) {
40
+ const body = await res.json().catch(() => ({}));
41
+ throw new Error(body.error || body.message || `Request failed (${res.status})`);
42
+ }
43
+ return res.json();
44
+ }
45
+ export async function testKeys(apiKey, apiSecret) {
46
+ try {
47
+ const res = await fetch(`${API_BASE}/account`, {
48
+ method: 'GET',
49
+ headers: {
50
+ 'sb-api-key-id': apiKey,
51
+ 'sb-api-secret-key': apiSecret
52
+ }
53
+ });
54
+ return res.ok;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
@@ -0,0 +1,12 @@
1
+ export interface SendblueCredentials {
2
+ apiKey: string;
3
+ apiSecret: string;
4
+ email: string;
5
+ assignedNumber: string;
6
+ plan: string;
7
+ createdAt: string;
8
+ }
9
+ export declare function getCredentials(): SendblueCredentials | null;
10
+ export declare function saveCredentials(creds: SendblueCredentials): void;
11
+ export declare function clearCredentials(): void;
12
+ export declare function credentialsPath(): string;
@@ -0,0 +1,33 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.sendblue');
5
+ const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
6
+ export function getCredentials() {
7
+ try {
8
+ const data = fs.readFileSync(CREDENTIALS_FILE, 'utf-8');
9
+ return JSON.parse(data);
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function saveCredentials(creds) {
16
+ if (!fs.existsSync(CONFIG_DIR)) {
17
+ fs.mkdirSync(CONFIG_DIR, { mode: 0o700 });
18
+ }
19
+ fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), {
20
+ mode: 0o600
21
+ });
22
+ }
23
+ export function clearCredentials() {
24
+ try {
25
+ fs.unlinkSync(CREDENTIALS_FILE);
26
+ }
27
+ catch {
28
+ // ignore if doesn't exist
29
+ }
30
+ }
31
+ export function credentialsPath() {
32
+ return CREDENTIALS_FILE;
33
+ }
@@ -0,0 +1,11 @@
1
+ export declare function formatPhoneNumber(e164: string): string;
2
+ export declare function printSuccess(message: string): void;
3
+ export declare function printError(message: string): void;
4
+ export declare function printInfo(message: string): void;
5
+ export declare function printCredentials(data: {
6
+ email: string;
7
+ apiKey: string;
8
+ apiSecret: string;
9
+ assignedNumber: string;
10
+ plan: string;
11
+ }): void;
@@ -0,0 +1,28 @@
1
+ import chalk from 'chalk';
2
+ export function formatPhoneNumber(e164) {
3
+ // +15551234567 -> +1 (555) 123-4567
4
+ const match = e164.match(/^\+1(\d{3})(\d{3})(\d{4})$/);
5
+ if (match) {
6
+ return `+1 (${match[1]}) ${match[2]}-${match[3]}`;
7
+ }
8
+ return e164;
9
+ }
10
+ export function printSuccess(message) {
11
+ console.log(chalk.green(message));
12
+ }
13
+ export function printError(message) {
14
+ console.error(chalk.red(message));
15
+ }
16
+ export function printInfo(message) {
17
+ console.log(chalk.cyan(message));
18
+ }
19
+ export function printCredentials(data) {
20
+ console.log();
21
+ console.log(chalk.green.bold(' Account created successfully!'));
22
+ console.log();
23
+ console.log(` ${chalk.bold('Phone Number')}: ${formatPhoneNumber(data.assignedNumber)}`);
24
+ console.log(` ${chalk.bold('API Key')}: ${data.apiKey}`);
25
+ console.log(` ${chalk.bold('API Secret')}: ${data.apiSecret}`);
26
+ console.log(` ${chalk.bold('Plan')}: ${data.plan}`);
27
+ console.log();
28
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@sendblue/cli",
3
+ "version": "0.1.0",
4
+ "description": "Sendblue CLI — iMessage numbers for agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "sendblue": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "keywords": [
21
+ "sendblue",
22
+ "imessage",
23
+ "sms",
24
+ "cli",
25
+ "messaging",
26
+ "agents"
27
+ ],
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "chalk": "^5.3.0",
31
+ "commander": "^12.1.0",
32
+ "prompts": "^2.4.2"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0",
36
+ "@types/prompts": "^2.4.9",
37
+ "typescript": "^5.5.0"
38
+ }
39
+ }