@blinkdotnew/cli 0.3.3 → 0.3.6
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/AGENTS.md +9 -0
- package/dist/cli.js +81 -15
- package/package.json +1 -1
- package/src/cli.ts +2 -0
- package/src/commands/ai.ts +6 -4
- package/src/commands/phone.ts +31 -12
- package/src/commands/sms.ts +71 -0
package/AGENTS.md
CHANGED
|
@@ -38,6 +38,7 @@ packages/cli/
|
|
|
38
38
|
│ │ ├── db.ts # blink db query/exec/list → core.blink.new
|
|
39
39
|
│ │ ├── deploy.ts # blink deploy / deployments / rollback → blink.new
|
|
40
40
|
│ │ ├── notify.ts # blink notify email → core.blink.new
|
|
41
|
+
│ │ ├── sms.ts # blink sms send → core.blink.new
|
|
41
42
|
│ │ ├── project.ts # blink project / blink link / blink status → blink.new
|
|
42
43
|
│ │ ├── rag.ts # blink rag search/upload/collections → core.blink.new
|
|
43
44
|
│ │ ├── realtime.ts # blink realtime publish → core.blink.new
|
|
@@ -144,6 +145,14 @@ blink notify email <project_id> <to> <subject> [body]
|
|
|
144
145
|
blink notify email <to> <subject> --file ./body.html
|
|
145
146
|
```
|
|
146
147
|
|
|
148
|
+
### SMS (→ core.blink.new /api/v1/sms/send)
|
|
149
|
+
```bash
|
|
150
|
+
blink sms send <to> <message> # Send SMS from workspace primary number
|
|
151
|
+
blink sms send <to> <message> --from <number> # Send from specific workspace number
|
|
152
|
+
blink sms send <to> <message> --json # Machine-readable output
|
|
153
|
+
```
|
|
154
|
+
Requires a provisioned workspace phone number (`blink phone buy`). 0.1 credits/message.
|
|
155
|
+
|
|
147
156
|
### Connectors (→ core.blink.new /api/v1/connectors/[provider]/execute)
|
|
148
157
|
```bash
|
|
149
158
|
blink connector exec <provider> <action> [params-json] [--account <id>] [--method POST]
|
package/dist/cli.js
CHANGED
|
@@ -478,12 +478,12 @@ Supported formats: mp3, wav, m4a, ogg
|
|
|
478
478
|
if (isJsonMode()) return printJson(result);
|
|
479
479
|
console.log(result?.text ?? result?.transcript ?? JSON.stringify(result));
|
|
480
480
|
});
|
|
481
|
-
ai.command("call <phone-number> <system-prompt>").description("Make an AI phone call to any number (US/International)").option("--voice <voice>", "Voice: openai:alloy | openai:nova | cartesia:sonic-english", "openai:alloy").option("--max-duration <seconds>", "Max call duration in seconds", "300").option("--no-wait", "Return call_id immediately without waiting for completion").option("--from <number>", "Phone number to call from (E.164 format, e.g. +14155551234). Uses primary number if omitted.").addHelpText("after", `
|
|
481
|
+
ai.command("call <phone-number> <system-prompt>").description("Make an AI phone call to any number (US/International)").option("--voice <voice>", "Voice: openai:alloy | openai:nova | cartesia:sonic-english", "openai:alloy").option("--first-message <text>", 'What the AI says first when call connects (default: "Hello?")').option("--max-duration <seconds>", "Max call duration in seconds", "300").option("--no-wait", "Return call_id immediately without waiting for completion").option("--from <number>", "Phone number to call from (E.164 format, e.g. +14155551234). Uses primary number if omitted.").addHelpText("after", `
|
|
482
482
|
Examples:
|
|
483
|
-
$ blink ai call "+14155551234" "You are collecting a
|
|
484
|
-
$ blink ai call "+14155551234" "Confirm John's appointment
|
|
485
|
-
$ blink ai call "+14155551234" "
|
|
486
|
-
$ blink ai call "+14155551234" "Your task" --json | jq '.call_id'
|
|
483
|
+
$ blink ai call "+14155551234" "You are collecting a $240 payment from John Smith. Be polite." --first-message "Hi John, this is an automated call from Acme."
|
|
484
|
+
$ blink ai call "+14155551234" "Confirm John's appointment at 3pm" --first-message "Hello, calling to confirm your appointment."
|
|
485
|
+
$ blink ai call "+14155551234" "Collect feedback on recent order" --voice openai:nova
|
|
486
|
+
$ blink ai call "+14155551234" "Your task" --no-wait --json | jq '.call_id'
|
|
487
487
|
|
|
488
488
|
Phone number must be in E.164 format: +1XXXXXXXXXX (US), +44XXXXXXXXXX (UK), etc.
|
|
489
489
|
|
|
@@ -507,6 +507,7 @@ Use --from to specify which of your workspace numbers to call from.
|
|
|
507
507
|
system_prompt: systemPrompt,
|
|
508
508
|
voice: opts.voice,
|
|
509
509
|
max_duration_seconds: parseInt(opts.maxDuration),
|
|
510
|
+
...opts.firstMessage ? { first_message: opts.firstMessage } : {},
|
|
510
511
|
...opts.from ? { from_number: opts.from } : {}
|
|
511
512
|
}
|
|
512
513
|
})
|
|
@@ -1605,7 +1606,7 @@ Commands:
|
|
|
1605
1606
|
|
|
1606
1607
|
Examples:
|
|
1607
1608
|
$ blink phone list
|
|
1608
|
-
$ blink phone buy --label "Sales" --
|
|
1609
|
+
$ blink phone buy --label "Sales" --area-code 415
|
|
1609
1610
|
$ blink phone buy --label "Support"
|
|
1610
1611
|
$ blink phone label wpn_abc123 "Support line"
|
|
1611
1612
|
$ blink phone release wpn_abc123
|
|
@@ -1636,23 +1637,39 @@ Examples:
|
|
|
1636
1637
|
if (isJsonMode()) return printJson(records);
|
|
1637
1638
|
printRecords(records);
|
|
1638
1639
|
});
|
|
1639
|
-
phone.command("buy").description("
|
|
1640
|
+
phone.command("buy").description("Search and buy a phone number (25 credits/month)").option("--label <label>", 'Label for this number, e.g. "Sales" or "Support"').option("--country <code>", "Country code (e.g. US, GB, AU, DE)", "US").option("--area-code <code>", "Preferred area code (e.g. 415, 914, 020)").addHelpText("after", `
|
|
1640
1641
|
Examples:
|
|
1641
|
-
$ blink phone buy
|
|
1642
|
-
$ blink phone buy --label "Sales" --area-code 415
|
|
1642
|
+
$ blink phone buy --label "Sales" --area-code 914
|
|
1643
1643
|
$ blink phone buy --label "UK Support" --country GB
|
|
1644
|
+
$ blink phone buy --label "Germany" --country DE
|
|
1644
1645
|
$ blink phone buy --json | jq '.phone_number'
|
|
1645
1646
|
|
|
1646
|
-
|
|
1647
|
+
Searches real available numbers from Twilio, picks the first result.
|
|
1648
|
+
25 credits charged immediately, then monthly on anniversary.
|
|
1649
|
+
Supported countries: US, CA, GB, AU, NZ, DE, FR, NL, SE, NO, DK, FI, IE, BE, AT, CH, ES, IT, PT, PL, CZ, RO, HU, GR, JP, SG, HK, BR, MX, CL, CO, ZA, IL, PR
|
|
1647
1650
|
`).action(async (opts) => {
|
|
1648
1651
|
requireToken();
|
|
1652
|
+
const searchParams = new URLSearchParams({ country: opts.country ?? "US" });
|
|
1653
|
+
if (opts.areaCode) searchParams.set("area_code", opts.areaCode);
|
|
1654
|
+
const searchResult = await withSpinner(
|
|
1655
|
+
`Searching for ${opts.country ?? "US"} numbers...`,
|
|
1656
|
+
() => resourcesRequest(`/api/v1/phone-numbers/available?${searchParams}`)
|
|
1657
|
+
);
|
|
1658
|
+
if (!searchResult.numbers?.length) {
|
|
1659
|
+
process.stderr.write(`
|
|
1660
|
+
No numbers available${opts.areaCode ? ` in area code ${opts.areaCode}` : ""}. Try a different area code.
|
|
1661
|
+
`);
|
|
1662
|
+
process.exit(1);
|
|
1663
|
+
}
|
|
1664
|
+
const picked = searchResult.numbers[0];
|
|
1649
1665
|
const result = await withSpinner(
|
|
1650
|
-
|
|
1666
|
+
`Provisioning ${picked.phone_number}...`,
|
|
1651
1667
|
() => resourcesRequest("/api/v1/phone-numbers", {
|
|
1652
1668
|
body: {
|
|
1669
|
+
phone_number: picked.phone_number,
|
|
1653
1670
|
label: opts.label || void 0,
|
|
1654
|
-
|
|
1655
|
-
|
|
1671
|
+
locality: picked.locality || void 0,
|
|
1672
|
+
country: picked.country
|
|
1656
1673
|
}
|
|
1657
1674
|
})
|
|
1658
1675
|
);
|
|
@@ -1660,9 +1677,10 @@ Examples:
|
|
|
1660
1677
|
console.log(`\u2713 Phone number provisioned`);
|
|
1661
1678
|
console.log(` Number ${formatPhone(result.phone_number)}`);
|
|
1662
1679
|
if (result.label) console.log(` Label ${result.label}`);
|
|
1663
|
-
|
|
1680
|
+
const loc = result.locality;
|
|
1681
|
+
console.log(` Location ${loc ? `${loc} \xB7 ` : ""}${result.country}`);
|
|
1664
1682
|
console.log(` ID ${result.id}`);
|
|
1665
|
-
console.log(` Billing
|
|
1683
|
+
console.log(` Billing 25 credits/month`);
|
|
1666
1684
|
console.log();
|
|
1667
1685
|
console.log(`Use it: blink ai call "+1..." "Your task" --from "${result.phone_number}"`);
|
|
1668
1686
|
});
|
|
@@ -1708,6 +1726,53 @@ The number is permanently returned to the carrier pool. This action cannot be un
|
|
|
1708
1726
|
});
|
|
1709
1727
|
}
|
|
1710
1728
|
|
|
1729
|
+
// src/commands/sms.ts
|
|
1730
|
+
function formatPhone2(num) {
|
|
1731
|
+
const m = num.match(/^\+1(\d{3})(\d{3})(\d{4})$/);
|
|
1732
|
+
return m ? `+1 (${m[1]}) ${m[2]}-${m[3]}` : num;
|
|
1733
|
+
}
|
|
1734
|
+
function registerSmsCommands(program2) {
|
|
1735
|
+
const sms = program2.command("sms").description("Send SMS messages from your workspace phone number").addHelpText("after", `
|
|
1736
|
+
Commands:
|
|
1737
|
+
send Send an SMS text message to any phone number
|
|
1738
|
+
|
|
1739
|
+
Examples:
|
|
1740
|
+
$ blink sms send "+14155551234" "Your order is ready!"
|
|
1741
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
1742
|
+
$ blink sms send "+447911123456" "Your appointment is confirmed."
|
|
1743
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
1744
|
+
|
|
1745
|
+
Requires a provisioned workspace phone number (blink phone list / blink phone buy).
|
|
1746
|
+
0.1 credits per message. Credits charged immediately on send.
|
|
1747
|
+
`).action(() => sms.help());
|
|
1748
|
+
sms.command("send <to> <message>").description("Send an SMS message (0.1 credits per message)").option("--from <number>", "Specific sender number (default: workspace primary)").addHelpText("after", `
|
|
1749
|
+
Examples:
|
|
1750
|
+
$ blink sms send "+14155551234" "Your appointment is confirmed for tomorrow at 2pm."
|
|
1751
|
+
$ blink sms send "+447911123456" "Your order #1042 has shipped!"
|
|
1752
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
1753
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
1754
|
+
|
|
1755
|
+
Phone numbers must be in E.164 format (+14155551234).
|
|
1756
|
+
Messages over 160 characters count as multiple segments (still 0.1 credits).
|
|
1757
|
+
Requires a provisioned workspace phone number: blink phone buy
|
|
1758
|
+
`).action(async (to, message, opts) => {
|
|
1759
|
+
requireToken();
|
|
1760
|
+
const body = { to, message };
|
|
1761
|
+
if (opts.from?.trim()) body.from = opts.from.trim();
|
|
1762
|
+
const result = await withSpinner(
|
|
1763
|
+
`Sending SMS to ${to}...`,
|
|
1764
|
+
() => resourcesRequest("/api/v1/sms/send", { body })
|
|
1765
|
+
);
|
|
1766
|
+
if (isJsonMode()) return printJson(result);
|
|
1767
|
+
console.log(`\u2713 SMS sent`);
|
|
1768
|
+
console.log(` To ${formatPhone2(result.to)}`);
|
|
1769
|
+
console.log(` From ${formatPhone2(result.from)}`);
|
|
1770
|
+
if (result.segment_count > 1) console.log(` Segments ${result.segment_count}`);
|
|
1771
|
+
console.log(` ID ${result.message_id}`);
|
|
1772
|
+
console.log(` Credits ${result.credits_charged}`);
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1711
1776
|
// src/lib/api-app.ts
|
|
1712
1777
|
var BASE_URL2 = process.env.BLINK_APP_URL ?? "https://blink.new";
|
|
1713
1778
|
async function appRequest(path, opts = {}) {
|
|
@@ -2354,6 +2419,7 @@ registerNotifyCommands(program);
|
|
|
2354
2419
|
registerConnectorCommands(program);
|
|
2355
2420
|
registerLinkedInCommands(program);
|
|
2356
2421
|
registerPhoneCommands(program);
|
|
2422
|
+
registerSmsCommands(program);
|
|
2357
2423
|
program.command("use <project_id>").description("Set active project for this shell session (alternative to blink link)").option("--export", "Output a shell export statement \u2014 use with eval to actually set it").addHelpText("after", `
|
|
2358
2424
|
Examples:
|
|
2359
2425
|
$ blink use proj_xxx Shows the export command to run
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { registerNotifyCommands } from './commands/notify.js'
|
|
|
11
11
|
import { registerConnectorCommands } from './commands/connector.js'
|
|
12
12
|
import { registerLinkedInCommands } from './commands/linkedin.js'
|
|
13
13
|
import { registerPhoneCommands } from './commands/phone.js'
|
|
14
|
+
import { registerSmsCommands } from './commands/sms.js'
|
|
14
15
|
import { registerDeployCommands } from './commands/deploy.js'
|
|
15
16
|
import { registerProjectCommands } from './commands/project.js'
|
|
16
17
|
import { registerAuthCommands } from './commands/auth.js'
|
|
@@ -150,6 +151,7 @@ registerNotifyCommands(program)
|
|
|
150
151
|
registerConnectorCommands(program)
|
|
151
152
|
registerLinkedInCommands(program)
|
|
152
153
|
registerPhoneCommands(program)
|
|
154
|
+
registerSmsCommands(program)
|
|
153
155
|
|
|
154
156
|
program.command('use <project_id>')
|
|
155
157
|
.description('Set active project for this shell session (alternative to blink link)')
|
package/src/commands/ai.ts
CHANGED
|
@@ -288,15 +288,16 @@ Supported formats: mp3, wav, m4a, ogg
|
|
|
288
288
|
ai.command('call <phone-number> <system-prompt>')
|
|
289
289
|
.description('Make an AI phone call to any number (US/International)')
|
|
290
290
|
.option('--voice <voice>', 'Voice: openai:alloy | openai:nova | cartesia:sonic-english', 'openai:alloy')
|
|
291
|
+
.option('--first-message <text>', 'What the AI says first when call connects (default: "Hello?")')
|
|
291
292
|
.option('--max-duration <seconds>', 'Max call duration in seconds', '300')
|
|
292
293
|
.option('--no-wait', 'Return call_id immediately without waiting for completion')
|
|
293
294
|
.option('--from <number>', 'Phone number to call from (E.164 format, e.g. +14155551234). Uses primary number if omitted.')
|
|
294
295
|
.addHelpText('after', `
|
|
295
296
|
Examples:
|
|
296
|
-
$ blink ai call "+14155551234" "You are collecting a
|
|
297
|
-
$ blink ai call "+14155551234" "Confirm John's appointment
|
|
298
|
-
$ blink ai call "+14155551234" "
|
|
299
|
-
$ blink ai call "+14155551234" "Your task" --json | jq '.call_id'
|
|
297
|
+
$ blink ai call "+14155551234" "You are collecting a $240 payment from John Smith. Be polite." --first-message "Hi John, this is an automated call from Acme."
|
|
298
|
+
$ blink ai call "+14155551234" "Confirm John's appointment at 3pm" --first-message "Hello, calling to confirm your appointment."
|
|
299
|
+
$ blink ai call "+14155551234" "Collect feedback on recent order" --voice openai:nova
|
|
300
|
+
$ blink ai call "+14155551234" "Your task" --no-wait --json | jq '.call_id'
|
|
300
301
|
|
|
301
302
|
Phone number must be in E.164 format: +1XXXXXXXXXX (US), +44XXXXXXXXXX (UK), etc.
|
|
302
303
|
|
|
@@ -320,6 +321,7 @@ Use --from to specify which of your workspace numbers to call from.
|
|
|
320
321
|
system_prompt: systemPrompt,
|
|
321
322
|
voice: opts.voice,
|
|
322
323
|
max_duration_seconds: parseInt(opts.maxDuration),
|
|
324
|
+
...(opts.firstMessage ? { first_message: opts.firstMessage } : {}),
|
|
323
325
|
...(opts.from ? { from_number: opts.from } : {}),
|
|
324
326
|
},
|
|
325
327
|
})
|
package/src/commands/phone.ts
CHANGED
|
@@ -56,7 +56,7 @@ Commands:
|
|
|
56
56
|
|
|
57
57
|
Examples:
|
|
58
58
|
$ blink phone list
|
|
59
|
-
$ blink phone buy --label "Sales" --
|
|
59
|
+
$ blink phone buy --label "Sales" --area-code 415
|
|
60
60
|
$ blink phone buy --label "Support"
|
|
61
61
|
$ blink phone label wpn_abc123 "Support line"
|
|
62
62
|
$ blink phone release wpn_abc123
|
|
@@ -95,27 +95,45 @@ Examples:
|
|
|
95
95
|
|
|
96
96
|
// blink phone buy
|
|
97
97
|
phone.command('buy')
|
|
98
|
-
.description('
|
|
98
|
+
.description('Search and buy a phone number (25 credits/month)')
|
|
99
99
|
.option('--label <label>', 'Label for this number, e.g. "Sales" or "Support"')
|
|
100
|
-
.option('--country <code>', 'Country code
|
|
101
|
-
.option('--area-code <code>', 'Preferred area code (
|
|
100
|
+
.option('--country <code>', 'Country code (e.g. US, GB, AU, DE)', 'US')
|
|
101
|
+
.option('--area-code <code>', 'Preferred area code (e.g. 415, 914, 020)')
|
|
102
102
|
.addHelpText('after', `
|
|
103
103
|
Examples:
|
|
104
|
-
$ blink phone buy
|
|
105
|
-
$ blink phone buy --label "Sales" --area-code 415
|
|
104
|
+
$ blink phone buy --label "Sales" --area-code 914
|
|
106
105
|
$ blink phone buy --label "UK Support" --country GB
|
|
106
|
+
$ blink phone buy --label "Germany" --country DE
|
|
107
107
|
$ blink phone buy --json | jq '.phone_number'
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
Searches real available numbers from Twilio, picks the first result.
|
|
110
|
+
25 credits charged immediately, then monthly on anniversary.
|
|
111
|
+
Supported countries: US, CA, GB, AU, NZ, DE, FR, NL, SE, NO, DK, FI, IE, BE, AT, CH, ES, IT, PT, PL, CZ, RO, HU, GR, JP, SG, HK, BR, MX, CL, CO, ZA, IL, PR
|
|
110
112
|
`)
|
|
111
113
|
.action(async (opts) => {
|
|
112
114
|
requireToken()
|
|
113
|
-
|
|
115
|
+
// Step 1: Search for available numbers
|
|
116
|
+
const searchParams = new URLSearchParams({ country: opts.country ?? 'US' })
|
|
117
|
+
if (opts.areaCode) searchParams.set('area_code', opts.areaCode)
|
|
118
|
+
const searchResult = await withSpinner(`Searching for ${opts.country ?? 'US'} numbers...`, () =>
|
|
119
|
+
resourcesRequest(`/api/v1/phone-numbers/available?${searchParams}`)
|
|
120
|
+
) as { numbers: Array<{ phone_number: string; locality?: string; region?: string; country: string }> }
|
|
121
|
+
|
|
122
|
+
if (!searchResult.numbers?.length) {
|
|
123
|
+
process.stderr.write(`\nNo numbers available${opts.areaCode ? ` in area code ${opts.areaCode}` : ''}. Try a different area code.\n`)
|
|
124
|
+
process.exit(1)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const picked = searchResult.numbers[0]
|
|
128
|
+
|
|
129
|
+
// Step 2: Provision the picked number
|
|
130
|
+
const result = await withSpinner(`Provisioning ${picked.phone_number}...`, () =>
|
|
114
131
|
resourcesRequest('/api/v1/phone-numbers', {
|
|
115
132
|
body: {
|
|
133
|
+
phone_number: picked.phone_number,
|
|
116
134
|
label: opts.label || undefined,
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
locality: picked.locality || undefined,
|
|
136
|
+
country: picked.country,
|
|
119
137
|
},
|
|
120
138
|
})
|
|
121
139
|
) as PhoneRecord
|
|
@@ -123,9 +141,10 @@ Examples:
|
|
|
123
141
|
console.log(`✓ Phone number provisioned`)
|
|
124
142
|
console.log(` Number ${formatPhone(result.phone_number)}`)
|
|
125
143
|
if (result.label) console.log(` Label ${result.label}`)
|
|
126
|
-
|
|
144
|
+
const loc = (result as any).locality
|
|
145
|
+
console.log(` Location ${loc ? `${loc} · ` : ''}${result.country}`)
|
|
127
146
|
console.log(` ID ${result.id}`)
|
|
128
|
-
console.log(` Billing
|
|
147
|
+
console.log(` Billing 25 credits/month`)
|
|
129
148
|
console.log()
|
|
130
149
|
console.log(`Use it: blink ai call "+1..." "Your task" --from "${result.phone_number}"`)
|
|
131
150
|
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resourcesRequest } from '../lib/api-resources.js'
|
|
3
|
+
import { requireToken } from '../lib/auth.js'
|
|
4
|
+
import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
|
|
5
|
+
|
|
6
|
+
interface SmsResult {
|
|
7
|
+
message_id: string
|
|
8
|
+
twilio_sid: string
|
|
9
|
+
status: string
|
|
10
|
+
to: string
|
|
11
|
+
from: string
|
|
12
|
+
segment_count: number
|
|
13
|
+
credits_charged: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatPhone(num: string): string {
|
|
17
|
+
const m = num.match(/^\+1(\d{3})(\d{3})(\d{4})$/)
|
|
18
|
+
return m ? `+1 (${m[1]}) ${m[2]}-${m[3]}` : num
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function registerSmsCommands(program: Command) {
|
|
22
|
+
const sms = program.command('sms')
|
|
23
|
+
.description('Send SMS messages from your workspace phone number')
|
|
24
|
+
.addHelpText('after', `
|
|
25
|
+
Commands:
|
|
26
|
+
send Send an SMS text message to any phone number
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
$ blink sms send "+14155551234" "Your order is ready!"
|
|
30
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
31
|
+
$ blink sms send "+447911123456" "Your appointment is confirmed."
|
|
32
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
33
|
+
|
|
34
|
+
Requires a provisioned workspace phone number (blink phone list / blink phone buy).
|
|
35
|
+
0.1 credits per message. Credits charged immediately on send.
|
|
36
|
+
`)
|
|
37
|
+
.action(() => sms.help())
|
|
38
|
+
|
|
39
|
+
sms.command('send <to> <message>')
|
|
40
|
+
.description('Send an SMS message (0.1 credits per message)')
|
|
41
|
+
.option('--from <number>', 'Specific sender number (default: workspace primary)')
|
|
42
|
+
.addHelpText('after', `
|
|
43
|
+
Examples:
|
|
44
|
+
$ blink sms send "+14155551234" "Your appointment is confirmed for tomorrow at 2pm."
|
|
45
|
+
$ blink sms send "+447911123456" "Your order #1042 has shipped!"
|
|
46
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
47
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
48
|
+
|
|
49
|
+
Phone numbers must be in E.164 format (+14155551234).
|
|
50
|
+
Messages over 160 characters count as multiple segments (still 0.1 credits).
|
|
51
|
+
Requires a provisioned workspace phone number: blink phone buy
|
|
52
|
+
`)
|
|
53
|
+
.action(async (to: string, message: string, opts) => {
|
|
54
|
+
requireToken()
|
|
55
|
+
const body: Record<string, string> = { to, message }
|
|
56
|
+
if (opts.from?.trim()) body.from = opts.from.trim()
|
|
57
|
+
|
|
58
|
+
const result = await withSpinner(`Sending SMS to ${to}...`, () =>
|
|
59
|
+
resourcesRequest('/api/v1/sms/send', { body })
|
|
60
|
+
) as SmsResult
|
|
61
|
+
|
|
62
|
+
if (isJsonMode()) return printJson(result)
|
|
63
|
+
|
|
64
|
+
console.log(`✓ SMS sent`)
|
|
65
|
+
console.log(` To ${formatPhone(result.to)}`)
|
|
66
|
+
console.log(` From ${formatPhone(result.from)}`)
|
|
67
|
+
if (result.segment_count > 1) console.log(` Segments ${result.segment_count}`)
|
|
68
|
+
console.log(` ID ${result.message_id}`)
|
|
69
|
+
console.log(` Credits ${result.credits_charged}`)
|
|
70
|
+
})
|
|
71
|
+
}
|