@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 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 payment of $240 from John Smith. Be polite but firm."
484
- $ blink ai call "+14155551234" "Confirm John's appointment for tomorrow at 3pm" --voice openai:nova
485
- $ blink ai call "+14155551234" "Leave a message about our new product launch" --no-wait
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" --country US --area-code 415
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("Provision a new phone number (10 credits/month)").option("--label <label>", 'Label for this number, e.g. "Sales" or "Support"').option("--country <code>", "Country code: US, GB, CA, AU", "US").option("--area-code <code>", "Preferred area code (US/CA only, e.g. 415)").addHelpText("after", `
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
- 10 credits charged immediately, then monthly on anniversary.
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
- "Provisioning phone number...",
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
- country: opts.country,
1655
- area_code: opts.areaCode || void 0
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
- console.log(` Country ${result.country}${result.area_code ? ` \xB7 Area ${result.area_code}` : ""}`);
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 10 credits/month`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.6",
4
4
  "description": "Blink platform CLI — deploy apps, manage databases, generate AI content",
5
5
  "bin": {
6
6
  "blink": "dist/cli.js"
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)')
@@ -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 payment of $240 from John Smith. Be polite but firm."
297
- $ blink ai call "+14155551234" "Confirm John's appointment for tomorrow at 3pm" --voice openai:nova
298
- $ blink ai call "+14155551234" "Leave a message about our new product launch" --no-wait
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
  })
@@ -56,7 +56,7 @@ Commands:
56
56
 
57
57
  Examples:
58
58
  $ blink phone list
59
- $ blink phone buy --label "Sales" --country US --area-code 415
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('Provision a new phone number (10 credits/month)')
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: US, GB, CA, AU', 'US')
101
- .option('--area-code <code>', 'Preferred area code (US/CA only, e.g. 415)')
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
- 10 credits charged immediately, then monthly on anniversary.
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
- const result = await withSpinner('Provisioning phone number...', () =>
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
- country: opts.country,
118
- area_code: opts.areaCode || undefined,
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
- console.log(` Country ${result.country}${result.area_code ? ` · Area ${result.area_code}` : ''}`)
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 10 credits/month`)
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
+ }