@sendblue/cli 0.5.1 → 0.6.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,6 @@
1
+ export declare function messagesCommand(opts: {
2
+ number?: string;
3
+ limit?: string;
4
+ outbound?: boolean;
5
+ inbound?: boolean;
6
+ }): Promise<void>;
@@ -0,0 +1,66 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { getCredentials } from '../lib/config.js';
4
+ import { getMessages } from '../lib/api.js';
5
+ import { formatPhoneNumber, normalizeNumber, printError } from '../lib/format.js';
6
+ export async function messagesCommand(opts) {
7
+ const creds = getCredentials();
8
+ if (!creds) {
9
+ printError('No credentials found. Run `sendblue login` first.');
10
+ process.exit(1);
11
+ }
12
+ const spinner = ora({ text: 'Fetching messages...', indent: 2 }).start();
13
+ try {
14
+ const isOutbound = opts.outbound ? true : opts.inbound ? false : undefined;
15
+ const number = opts.number ? normalizeNumber(opts.number) : undefined;
16
+ const limit = Math.min(parseInt(opts.limit || '10', 10) || 10, 100);
17
+ const result = await getMessages(creds.apiKey, creds.apiSecret, {
18
+ number,
19
+ limit,
20
+ isOutbound
21
+ });
22
+ spinner.stop();
23
+ console.log();
24
+ if (result.data.length === 0) {
25
+ console.log(chalk.dim(' No messages found.'));
26
+ console.log();
27
+ return;
28
+ }
29
+ console.log(chalk.bold(` Messages`) + chalk.dim(` (${result.data.length} of ${result.pagination.total})`));
30
+ console.log();
31
+ for (const msg of result.data) {
32
+ const direction = msg.is_outbound
33
+ ? chalk.cyan('OUT')
34
+ : chalk.green(' IN');
35
+ const otherNumber = msg.is_outbound ? msg.to_number : msg.from_number;
36
+ const formatted = otherNumber && typeof otherNumber === 'string' ? formatPhoneNumber(otherNumber) : '?';
37
+ const date = new Date(msg.date_sent);
38
+ const timestamp = date.toLocaleString('en-US', {
39
+ month: 'short',
40
+ day: 'numeric',
41
+ hour: 'numeric',
42
+ minute: '2-digit',
43
+ hour12: true
44
+ });
45
+ const status = msg.status === 'SENT' || msg.status === 'DELIVERED'
46
+ ? ''
47
+ : ` ${chalk.yellow(`[${msg.status}]`)}`;
48
+ const content = msg.content
49
+ ? msg.content.length > 80
50
+ ? msg.content.slice(0, 77) + '...'
51
+ : msg.content
52
+ : chalk.dim('(media)');
53
+ console.log(` ${direction} ${chalk.dim(timestamp)} ${chalk.bold(formatted)}${status}`);
54
+ console.log(` ${content}`);
55
+ console.log();
56
+ }
57
+ if (result.pagination.hasMore) {
58
+ console.log(chalk.dim(` Showing ${result.data.length} of ${result.pagination.total}. Use --limit to see more.`));
59
+ console.log();
60
+ }
61
+ }
62
+ catch (err) {
63
+ spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
64
+ process.exit(1);
65
+ }
66
+ }
package/dist/index.js CHANGED
@@ -4,15 +4,17 @@ import { Command } from 'commander';
4
4
  import { setupCommand } from './commands/setup.js';
5
5
  import { loginCommand } from './commands/login.js';
6
6
  import { sendCommand } from './commands/send.js';
7
+ import { messagesCommand } from './commands/messages.js';
7
8
  import { statusCommand } from './commands/status.js';
8
9
  import { whoamiCommand } from './commands/whoami.js';
9
10
  import { addContactCommand, contactsCommand } from './commands/add-contact.js';
11
+ import { getLogo } from './lib/format.js';
10
12
  const require = createRequire(import.meta.url);
11
13
  const { version } = require('../package.json');
12
14
  const program = new Command();
13
15
  program
14
16
  .name('sendblue')
15
- .description('Sendblue CLI — iMessage numbers for agents')
17
+ .description(getLogo())
16
18
  .version(version);
17
19
  program
18
20
  .command('setup')
@@ -37,6 +39,14 @@ program
37
39
  .argument('<number>', 'Recipient phone number (E.164 format)')
38
40
  .argument('<message>', 'Message content')
39
41
  .action(sendCommand);
42
+ program
43
+ .command('messages')
44
+ .description('View recent messages')
45
+ .option('-n, --number <number>', 'Filter by contact phone number')
46
+ .option('-l, --limit <count>', 'Number of messages to show', '10')
47
+ .option('--outbound', 'Show only outbound messages')
48
+ .option('--inbound', 'Show only inbound messages')
49
+ .action(messagesCommand);
40
50
  program
41
51
  .command('status')
42
52
  .description('Check your account status')
package/dist/lib/api.d.ts CHANGED
@@ -37,5 +37,35 @@ interface SharedContactsResponse {
37
37
  }
38
38
  export declare function addContact(apiKey: string, apiSecret: string, recipient: string): Promise<ContactRoute>;
39
39
  export declare function getSharedContacts(apiKey: string, apiSecret: string): Promise<SharedContactsResponse>;
40
+ export interface Message {
41
+ content: string;
42
+ number: string;
43
+ from_number: string;
44
+ to_number: string;
45
+ is_outbound: boolean;
46
+ status: string;
47
+ date_sent: string;
48
+ date_updated: string;
49
+ sendblue_number: string;
50
+ media_url?: string;
51
+ message_handle: string;
52
+ row_id: string;
53
+ [key: string]: unknown;
54
+ }
55
+ interface MessagesResponse {
56
+ status: string;
57
+ data: Message[];
58
+ pagination: {
59
+ total: number;
60
+ limit: number;
61
+ offset: number;
62
+ hasMore: boolean;
63
+ };
64
+ }
65
+ export declare function getMessages(apiKey: string, apiSecret: string, opts: {
66
+ number?: string;
67
+ limit?: number;
68
+ isOutbound?: boolean;
69
+ }): Promise<MessagesResponse>;
40
70
  export declare function testKeys(apiKey: string, apiSecret: string): Promise<boolean>;
41
71
  export {};
package/dist/lib/api.js CHANGED
@@ -99,6 +99,28 @@ export async function getSharedContacts(apiKey, apiSecret) {
99
99
  }
100
100
  return res.json();
101
101
  }
102
+ export async function getMessages(apiKey, apiSecret, opts) {
103
+ const params = new URLSearchParams();
104
+ params.set('limit', String(opts.limit || 10));
105
+ params.set('order_by', 'createdAt');
106
+ params.set('order_direction', 'desc');
107
+ if (opts.number)
108
+ params.set('number', opts.number);
109
+ if (opts.isOutbound !== undefined)
110
+ params.set('is_outbound', String(opts.isOutbound));
111
+ const res = await fetch(`${API_BASE}/api/v2/messages?${params}`, {
112
+ method: 'GET',
113
+ headers: {
114
+ 'sb-api-key-id': apiKey,
115
+ 'sb-api-secret-key': apiSecret
116
+ }
117
+ });
118
+ if (!res.ok) {
119
+ const body = await res.json().catch(() => ({}));
120
+ throw new Error(body.error || body.message || `Failed to get messages (${res.status})`);
121
+ }
122
+ return res.json();
123
+ }
102
124
  export async function testKeys(apiKey, apiSecret) {
103
125
  try {
104
126
  const res = await fetch(`${API_BASE}/account`, {
@@ -1,4 +1,5 @@
1
1
  export declare function printLogo(): void;
2
+ export declare function getLogo(): string;
2
3
  export declare function normalizeNumber(input: string): string;
3
4
  export declare function formatPhoneNumber(e164: string): string;
4
5
  export declare function printSuccess(message: string): void;
@@ -1,16 +1,21 @@
1
1
  import chalk from 'chalk';
2
2
  const blue = chalk.hex('#0088FF');
3
3
  export function printLogo() {
4
+ // Cloud/bubble shape with small circle top-right, matching Sendblue logo
5
+ const b = blue('█');
6
+ const h = blue('▀');
7
+ const l = blue('▄');
4
8
  const logo = [
5
- ` ${blue('██')} `,
6
- ` ${blue('██████')} `,
7
- ` ${blue('██████████')} `,
8
- ` ${blue('████████████')}`,
9
- ` ${blue('██████████████')}`,
10
- ` ${blue('██████████████')}`,
11
- ` ${blue('████████████')}`,
12
- ` ${blue('██████████')} `,
13
- ` ${blue('████████')} `,
9
+ ` ${l}${l}${l} `,
10
+ ` ${l}${l}${l}${l} ${b}${b}${b} `,
11
+ ` ${l}${b}${b}${b}${b}${b}${l} ${h}${h}${h} `,
12
+ ` ${l}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
13
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
14
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
15
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b} `,
16
+ ` ${h}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${h} `,
17
+ ` ${h}${b}${b}${b}${b}${b}${b}${b}${b}${h} `,
18
+ ` ${h}${h}${h}${h}${h}${h}${h} `,
14
19
  ];
15
20
  for (const line of logo) {
16
21
  console.log(` ${line}`);
@@ -20,6 +25,27 @@ export function printLogo() {
20
25
  console.log(chalk.dim(' iMessage for agents'));
21
26
  console.log();
22
27
  }
28
+ export function getLogo() {
29
+ const b = blue('█');
30
+ const h = blue('▀');
31
+ const l = blue('▄');
32
+ const lines = [
33
+ ` ${l}${l}${l} `,
34
+ ` ${l}${l}${l}${l} ${b}${b}${b} `,
35
+ ` ${l}${b}${b}${b}${b}${b}${l} ${h}${h}${h} `,
36
+ ` ${l}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
37
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
38
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${l} `,
39
+ ` ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b} `,
40
+ ` ${h}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${h} `,
41
+ ` ${h}${b}${b}${b}${b}${b}${b}${b}${b}${h} `,
42
+ ` ${h}${h}${h}${h}${h}${h}${h} `,
43
+ ``,
44
+ blue.bold(' sendblue'),
45
+ chalk.dim(' iMessage for agents'),
46
+ ];
47
+ return '\n' + lines.map(l => ` ${l}`).join('\n') + '\n';
48
+ }
23
49
  export function normalizeNumber(input) {
24
50
  // Strip non-digit chars except leading +
25
51
  let num = input.replace(/[^\d+]/g, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendblue/cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Sendblue CLI — iMessage numbers for agents",
5
5
  "type": "module",
6
6
  "bin": {