@blinkdotnew/cli 0.3.5 → 0.3.7
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 +59 -6
- package/package.json +1 -1
- package/src/cli.ts +2 -0
- package/src/commands/linkedin.ts +5 -5
- package/src/commands/sms.ts +71 -0
- package/src/lib/api-resources.ts +8 -1
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
|
@@ -197,7 +197,12 @@ async function resourcesRequest(path, opts = {}) {
|
|
|
197
197
|
const errText = await res.text();
|
|
198
198
|
let errMsg = `HTTP ${res.status}`;
|
|
199
199
|
try {
|
|
200
|
-
|
|
200
|
+
const parsed = JSON.parse(errText);
|
|
201
|
+
const err = parsed.error;
|
|
202
|
+
if (typeof err === "string") errMsg = err;
|
|
203
|
+
else if (err?.message) errMsg = err.message;
|
|
204
|
+
else if (parsed.message) errMsg = parsed.message;
|
|
205
|
+
else if (err) errMsg = JSON.stringify(err);
|
|
201
206
|
} catch {
|
|
202
207
|
}
|
|
203
208
|
throw new Error(errMsg);
|
|
@@ -1391,7 +1396,7 @@ async function liExec(method, httpMethod, params, agentId) {
|
|
|
1391
1396
|
return result.data;
|
|
1392
1397
|
}
|
|
1393
1398
|
async function getPersonId(agentId) {
|
|
1394
|
-
const data = await liExec("
|
|
1399
|
+
const data = await liExec("userinfo", "GET", {}, agentId);
|
|
1395
1400
|
const id = data?.sub ?? data?.id;
|
|
1396
1401
|
if (!id) throw new Error("Could not resolve LinkedIn person ID");
|
|
1397
1402
|
return id;
|
|
@@ -1436,7 +1441,7 @@ Examples:
|
|
|
1436
1441
|
const agentId = requireAgentId(opts.agent);
|
|
1437
1442
|
const data = await withSpinner(
|
|
1438
1443
|
"Fetching LinkedIn profile...",
|
|
1439
|
-
() => liExec("
|
|
1444
|
+
() => liExec("userinfo", "GET", {}, agentId)
|
|
1440
1445
|
);
|
|
1441
1446
|
if (isJsonMode()) return printJson(data);
|
|
1442
1447
|
const name = data?.name ?? [data?.given_name, data?.family_name].filter(Boolean).join(" ");
|
|
@@ -1515,7 +1520,7 @@ Examples:
|
|
|
1515
1520
|
const encoded = encodeURIComponent(postUrn);
|
|
1516
1521
|
const data = await withSpinner(
|
|
1517
1522
|
"Liking post...",
|
|
1518
|
-
() => liExec(`
|
|
1523
|
+
() => liExec(`socialActions/${encoded}/likes`, "POST", {
|
|
1519
1524
|
actor: `urn:li:person:${personId}`
|
|
1520
1525
|
}, agentId)
|
|
1521
1526
|
);
|
|
@@ -1537,7 +1542,7 @@ Examples:
|
|
|
1537
1542
|
const encodedPerson = encodeURIComponent(`urn:li:person:${personId}`);
|
|
1538
1543
|
await withSpinner(
|
|
1539
1544
|
"Unliking post...",
|
|
1540
|
-
() => liExec(`
|
|
1545
|
+
() => liExec(`socialActions/${encodedPost}/likes/${encodedPerson}`, "DELETE", {}, agentId)
|
|
1541
1546
|
);
|
|
1542
1547
|
if (isJsonMode()) return printJson({ unliked: true });
|
|
1543
1548
|
console.log(chalk7.green("\u2713 Post unliked"));
|
|
@@ -1558,7 +1563,7 @@ Examples:
|
|
|
1558
1563
|
const encoded = encodeURIComponent(postUrn);
|
|
1559
1564
|
const data = await withSpinner(
|
|
1560
1565
|
"Adding comment...",
|
|
1561
|
-
() => liExec(`
|
|
1566
|
+
() => liExec(`socialActions/${encoded}/comments`, "POST", {
|
|
1562
1567
|
actor: `urn:li:person:${personId}`,
|
|
1563
1568
|
message: { text }
|
|
1564
1569
|
}, agentId)
|
|
@@ -1726,6 +1731,53 @@ The number is permanently returned to the carrier pool. This action cannot be un
|
|
|
1726
1731
|
});
|
|
1727
1732
|
}
|
|
1728
1733
|
|
|
1734
|
+
// src/commands/sms.ts
|
|
1735
|
+
function formatPhone2(num) {
|
|
1736
|
+
const m = num.match(/^\+1(\d{3})(\d{3})(\d{4})$/);
|
|
1737
|
+
return m ? `+1 (${m[1]}) ${m[2]}-${m[3]}` : num;
|
|
1738
|
+
}
|
|
1739
|
+
function registerSmsCommands(program2) {
|
|
1740
|
+
const sms = program2.command("sms").description("Send SMS messages from your workspace phone number").addHelpText("after", `
|
|
1741
|
+
Commands:
|
|
1742
|
+
send Send an SMS text message to any phone number
|
|
1743
|
+
|
|
1744
|
+
Examples:
|
|
1745
|
+
$ blink sms send "+14155551234" "Your order is ready!"
|
|
1746
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
1747
|
+
$ blink sms send "+447911123456" "Your appointment is confirmed."
|
|
1748
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
1749
|
+
|
|
1750
|
+
Requires a provisioned workspace phone number (blink phone list / blink phone buy).
|
|
1751
|
+
0.1 credits per message. Credits charged immediately on send.
|
|
1752
|
+
`).action(() => sms.help());
|
|
1753
|
+
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", `
|
|
1754
|
+
Examples:
|
|
1755
|
+
$ blink sms send "+14155551234" "Your appointment is confirmed for tomorrow at 2pm."
|
|
1756
|
+
$ blink sms send "+447911123456" "Your order #1042 has shipped!"
|
|
1757
|
+
$ blink sms send "+14155551234" "Code: 492817" --from "+19143720262"
|
|
1758
|
+
$ blink sms send "+14155551234" "Hello" --json
|
|
1759
|
+
|
|
1760
|
+
Phone numbers must be in E.164 format (+14155551234).
|
|
1761
|
+
Messages over 160 characters count as multiple segments (still 0.1 credits).
|
|
1762
|
+
Requires a provisioned workspace phone number: blink phone buy
|
|
1763
|
+
`).action(async (to, message, opts) => {
|
|
1764
|
+
requireToken();
|
|
1765
|
+
const body = { to, message };
|
|
1766
|
+
if (opts.from?.trim()) body.from = opts.from.trim();
|
|
1767
|
+
const result = await withSpinner(
|
|
1768
|
+
`Sending SMS to ${to}...`,
|
|
1769
|
+
() => resourcesRequest("/api/v1/sms/send", { body })
|
|
1770
|
+
);
|
|
1771
|
+
if (isJsonMode()) return printJson(result);
|
|
1772
|
+
console.log(`\u2713 SMS sent`);
|
|
1773
|
+
console.log(` To ${formatPhone2(result.to)}`);
|
|
1774
|
+
console.log(` From ${formatPhone2(result.from)}`);
|
|
1775
|
+
if (result.segment_count > 1) console.log(` Segments ${result.segment_count}`);
|
|
1776
|
+
console.log(` ID ${result.message_id}`);
|
|
1777
|
+
console.log(` Credits ${result.credits_charged}`);
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1729
1781
|
// src/lib/api-app.ts
|
|
1730
1782
|
var BASE_URL2 = process.env.BLINK_APP_URL ?? "https://blink.new";
|
|
1731
1783
|
async function appRequest(path, opts = {}) {
|
|
@@ -2372,6 +2424,7 @@ registerNotifyCommands(program);
|
|
|
2372
2424
|
registerConnectorCommands(program);
|
|
2373
2425
|
registerLinkedInCommands(program);
|
|
2374
2426
|
registerPhoneCommands(program);
|
|
2427
|
+
registerSmsCommands(program);
|
|
2375
2428
|
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", `
|
|
2376
2429
|
Examples:
|
|
2377
2430
|
$ 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/linkedin.ts
CHANGED
|
@@ -34,7 +34,7 @@ async function liExec(
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async function getPersonId(agentId: string): Promise<string> {
|
|
37
|
-
const data = await liExec('
|
|
37
|
+
const data = await liExec('userinfo', 'GET', {}, agentId)
|
|
38
38
|
const id = data?.sub ?? data?.id
|
|
39
39
|
if (!id) throw new Error('Could not resolve LinkedIn person ID')
|
|
40
40
|
return id
|
|
@@ -87,7 +87,7 @@ Examples:
|
|
|
87
87
|
requireToken()
|
|
88
88
|
const agentId = requireAgentId(opts.agent)
|
|
89
89
|
const data = await withSpinner('Fetching LinkedIn profile...', () =>
|
|
90
|
-
liExec('
|
|
90
|
+
liExec('userinfo', 'GET', {}, agentId)
|
|
91
91
|
)
|
|
92
92
|
if (isJsonMode()) return printJson(data)
|
|
93
93
|
const name = data?.name ?? [data?.given_name, data?.family_name].filter(Boolean).join(' ')
|
|
@@ -180,7 +180,7 @@ Examples:
|
|
|
180
180
|
)
|
|
181
181
|
const encoded = encodeURIComponent(postUrn)
|
|
182
182
|
const data = await withSpinner('Liking post...', () =>
|
|
183
|
-
liExec(`
|
|
183
|
+
liExec(`socialActions/${encoded}/likes`, 'POST', {
|
|
184
184
|
actor: `urn:li:person:${personId}`,
|
|
185
185
|
}, agentId)
|
|
186
186
|
)
|
|
@@ -206,7 +206,7 @@ Examples:
|
|
|
206
206
|
const encodedPost = encodeURIComponent(postUrn)
|
|
207
207
|
const encodedPerson = encodeURIComponent(`urn:li:person:${personId}`)
|
|
208
208
|
await withSpinner('Unliking post...', () =>
|
|
209
|
-
liExec(`
|
|
209
|
+
liExec(`socialActions/${encodedPost}/likes/${encodedPerson}`, 'DELETE', {}, agentId)
|
|
210
210
|
)
|
|
211
211
|
if (isJsonMode()) return printJson({ unliked: true })
|
|
212
212
|
console.log(chalk.green('✓ Post unliked'))
|
|
@@ -231,7 +231,7 @@ Examples:
|
|
|
231
231
|
)
|
|
232
232
|
const encoded = encodeURIComponent(postUrn)
|
|
233
233
|
const data = await withSpinner('Adding comment...', () =>
|
|
234
|
-
liExec(`
|
|
234
|
+
liExec(`socialActions/${encoded}/comments`, 'POST', {
|
|
235
235
|
actor: `urn:li:person:${personId}`,
|
|
236
236
|
message: { text },
|
|
237
237
|
}, agentId)
|
|
@@ -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
|
+
}
|
package/src/lib/api-resources.ts
CHANGED
|
@@ -35,7 +35,14 @@ export async function resourcesRequest(path: string, opts: RequestOptions = {})
|
|
|
35
35
|
if (!res.ok) {
|
|
36
36
|
const errText = await res.text()
|
|
37
37
|
let errMsg = `HTTP ${res.status}`
|
|
38
|
-
try {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(errText)
|
|
40
|
+
const err = parsed.error
|
|
41
|
+
if (typeof err === 'string') errMsg = err
|
|
42
|
+
else if (err?.message) errMsg = err.message
|
|
43
|
+
else if (parsed.message) errMsg = parsed.message
|
|
44
|
+
else if (err) errMsg = JSON.stringify(err)
|
|
45
|
+
} catch { /* use default */ }
|
|
39
46
|
throw new Error(errMsg)
|
|
40
47
|
}
|
|
41
48
|
const ct = res.headers.get('content-type') ?? ''
|