@blinkdotnew/cli 0.3.0 → 0.3.1
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/dist/cli.js +180 -6
- package/package.json +1 -1
- package/src/cli.ts +8 -0
- package/src/commands/phone.ts +177 -0
- package/src/commands/storage.ts +28 -12
package/dist/cli.js
CHANGED
|
@@ -886,9 +886,21 @@ Examples:
|
|
|
886
886
|
for (const f of files) table.push([f.name, f.size ? `${(f.size / 1024).toFixed(1)} KB` : "-"]);
|
|
887
887
|
console.log(table.toString());
|
|
888
888
|
});
|
|
889
|
-
storage.command("download <
|
|
889
|
+
storage.command("download <arg1> [arg2] [output]").description("Download a file from storage \u2014 blink storage download <path> [output] OR blink storage download <proj> <path> [output]").action(async (arg1, arg2, output) => {
|
|
890
890
|
requireToken();
|
|
891
|
-
|
|
891
|
+
let projectId;
|
|
892
|
+
let storagePath;
|
|
893
|
+
if (arg2 !== void 0 && !arg1.startsWith("proj_")) {
|
|
894
|
+
projectId = requireProjectId();
|
|
895
|
+
storagePath = arg1;
|
|
896
|
+
output = arg2;
|
|
897
|
+
} else if (arg2 !== void 0) {
|
|
898
|
+
projectId = requireProjectId(arg1);
|
|
899
|
+
storagePath = arg2;
|
|
900
|
+
} else {
|
|
901
|
+
projectId = requireProjectId();
|
|
902
|
+
storagePath = arg1;
|
|
903
|
+
}
|
|
892
904
|
const result = await withSpinner(
|
|
893
905
|
"Downloading...",
|
|
894
906
|
() => resourcesRequest(`/api/storage/${projectId}/download`, { body: { path: storagePath } })
|
|
@@ -898,9 +910,17 @@ Examples:
|
|
|
898
910
|
else writeFileSync4(outFile, Buffer.from(result?.data ?? "", "base64"));
|
|
899
911
|
if (!isJsonMode()) console.log("Saved to " + outFile);
|
|
900
912
|
});
|
|
901
|
-
storage.command("delete <
|
|
913
|
+
storage.command("delete <arg1> [arg2]").description("Delete a file from storage \u2014 blink storage delete <path> OR blink storage delete <proj> <path>").action(async (arg1, arg2) => {
|
|
902
914
|
requireToken();
|
|
903
|
-
|
|
915
|
+
let projectId;
|
|
916
|
+
let storagePath;
|
|
917
|
+
if (arg2 !== void 0) {
|
|
918
|
+
projectId = requireProjectId(arg1);
|
|
919
|
+
storagePath = arg2;
|
|
920
|
+
} else {
|
|
921
|
+
projectId = requireProjectId();
|
|
922
|
+
storagePath = arg1;
|
|
923
|
+
}
|
|
904
924
|
await withSpinner(
|
|
905
925
|
"Deleting...",
|
|
906
926
|
() => resourcesRequest(`/api/storage/${projectId}/remove`, { method: "DELETE", body: { path: storagePath } })
|
|
@@ -908,9 +928,17 @@ Examples:
|
|
|
908
928
|
if (!isJsonMode()) console.log("Deleted: " + storagePath);
|
|
909
929
|
else printJson({ status: "ok", path: storagePath });
|
|
910
930
|
});
|
|
911
|
-
storage.command("url <
|
|
931
|
+
storage.command("url <arg1> [arg2]").description("Get public URL for a storage file \u2014 blink storage url <path> OR blink storage url <proj> <path>").action(async (arg1, arg2) => {
|
|
912
932
|
requireToken();
|
|
913
|
-
|
|
933
|
+
let projectId;
|
|
934
|
+
let storagePath;
|
|
935
|
+
if (arg2 !== void 0) {
|
|
936
|
+
projectId = requireProjectId(arg1);
|
|
937
|
+
storagePath = arg2;
|
|
938
|
+
} else {
|
|
939
|
+
projectId = requireProjectId();
|
|
940
|
+
storagePath = arg1;
|
|
941
|
+
}
|
|
914
942
|
const result = await resourcesRequest(`/api/storage/${projectId}/public-url`, { body: { path: storagePath } });
|
|
915
943
|
if (isJsonMode()) return printJson(result);
|
|
916
944
|
console.log(result?.url ?? result);
|
|
@@ -1438,6 +1466,145 @@ Examples:
|
|
|
1438
1466
|
});
|
|
1439
1467
|
}
|
|
1440
1468
|
|
|
1469
|
+
// src/commands/phone.ts
|
|
1470
|
+
function formatPhone(num) {
|
|
1471
|
+
const m = num.match(/^\+1(\d{3})(\d{3})(\d{4})$/);
|
|
1472
|
+
return m ? `+1 (${m[1]}) ${m[2]}-${m[3]}` : num;
|
|
1473
|
+
}
|
|
1474
|
+
function statusDot(status) {
|
|
1475
|
+
return status === "active" ? "\u25CF" : status === "grace" ? "\u26A1" : "\u25CB";
|
|
1476
|
+
}
|
|
1477
|
+
function nextCharge(lastCharged) {
|
|
1478
|
+
if (!lastCharged) return "";
|
|
1479
|
+
const d = new Date(new Date(lastCharged).getTime() + 30 * 864e5);
|
|
1480
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
1481
|
+
}
|
|
1482
|
+
function printRecords(records) {
|
|
1483
|
+
if (!records.length) {
|
|
1484
|
+
console.log("No phone numbers. Run: blink phone buy");
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
records.forEach((r, i) => {
|
|
1488
|
+
const label = r.label ? ` ${r.label}` : "";
|
|
1489
|
+
const primary = i === 0 && r.status === "active" ? " \u2605 primary" : "";
|
|
1490
|
+
const next = nextCharge(r.last_charged_at);
|
|
1491
|
+
const charge = next ? ` Next charge ${next}` : "";
|
|
1492
|
+
console.log(`${statusDot(r.status)} ${formatPhone(r.phone_number)}${label}${primary}`);
|
|
1493
|
+
console.log(` ${r.id} ${r.country}${r.area_code ? ` \xB7 ${r.area_code}` : ""}${charge}`);
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
function registerPhoneCommands(program2) {
|
|
1497
|
+
const phone = program2.command("phone").description("Manage workspace phone numbers for AI calling").addHelpText("after", `
|
|
1498
|
+
Commands:
|
|
1499
|
+
list List all workspace phone numbers
|
|
1500
|
+
buy Provision a new phone number
|
|
1501
|
+
label Update a number's label
|
|
1502
|
+
release Release (cancel) a phone number
|
|
1503
|
+
|
|
1504
|
+
Examples:
|
|
1505
|
+
$ blink phone list
|
|
1506
|
+
$ blink phone buy --label "Sales" --country US --area-code 415
|
|
1507
|
+
$ blink phone buy --label "Support"
|
|
1508
|
+
$ blink phone label wpn_abc123 "Support line"
|
|
1509
|
+
$ blink phone release wpn_abc123
|
|
1510
|
+
|
|
1511
|
+
Numbers cost 10 credits/month each. First charge is immediate on buy.
|
|
1512
|
+
Primary number (oldest active) is used by default for \`blink ai call\`.
|
|
1513
|
+
Use \`blink ai call --from +1XXXXXXXXXX\` to specify a different number.
|
|
1514
|
+
`);
|
|
1515
|
+
phone.action(async () => {
|
|
1516
|
+
requireToken();
|
|
1517
|
+
const records = await withSpinner(
|
|
1518
|
+
"Fetching phone numbers...",
|
|
1519
|
+
() => resourcesRequest("/api/v1/phone-numbers")
|
|
1520
|
+
);
|
|
1521
|
+
if (isJsonMode()) return printJson(records);
|
|
1522
|
+
printRecords(records);
|
|
1523
|
+
});
|
|
1524
|
+
phone.command("list").description("List all workspace phone numbers").addHelpText("after", `
|
|
1525
|
+
Examples:
|
|
1526
|
+
$ blink phone list
|
|
1527
|
+
$ blink phone list --json
|
|
1528
|
+
`).action(async () => {
|
|
1529
|
+
requireToken();
|
|
1530
|
+
const records = await withSpinner(
|
|
1531
|
+
"Fetching phone numbers...",
|
|
1532
|
+
() => resourcesRequest("/api/v1/phone-numbers")
|
|
1533
|
+
);
|
|
1534
|
+
if (isJsonMode()) return printJson(records);
|
|
1535
|
+
printRecords(records);
|
|
1536
|
+
});
|
|
1537
|
+
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", `
|
|
1538
|
+
Examples:
|
|
1539
|
+
$ blink phone buy
|
|
1540
|
+
$ blink phone buy --label "Sales" --area-code 415
|
|
1541
|
+
$ blink phone buy --label "UK Support" --country GB
|
|
1542
|
+
$ blink phone buy --json | jq '.phone_number'
|
|
1543
|
+
|
|
1544
|
+
10 credits charged immediately, then monthly on anniversary.
|
|
1545
|
+
`).action(async (opts) => {
|
|
1546
|
+
requireToken();
|
|
1547
|
+
const result = await withSpinner(
|
|
1548
|
+
"Provisioning phone number...",
|
|
1549
|
+
() => resourcesRequest("/api/v1/phone-numbers", {
|
|
1550
|
+
body: {
|
|
1551
|
+
label: opts.label || void 0,
|
|
1552
|
+
country: opts.country,
|
|
1553
|
+
area_code: opts.areaCode || void 0
|
|
1554
|
+
}
|
|
1555
|
+
})
|
|
1556
|
+
);
|
|
1557
|
+
if (isJsonMode()) return printJson(result);
|
|
1558
|
+
console.log(`\u2713 Phone number provisioned`);
|
|
1559
|
+
console.log(` Number ${formatPhone(result.phone_number)}`);
|
|
1560
|
+
if (result.label) console.log(` Label ${result.label}`);
|
|
1561
|
+
console.log(` Country ${result.country}${result.area_code ? ` \xB7 Area ${result.area_code}` : ""}`);
|
|
1562
|
+
console.log(` ID ${result.id}`);
|
|
1563
|
+
console.log(` Billing 10 credits/month`);
|
|
1564
|
+
console.log();
|
|
1565
|
+
console.log(`Use it: blink ai call "+1..." "Your task" --from "${result.phone_number}"`);
|
|
1566
|
+
});
|
|
1567
|
+
phone.command("label <id> <label>").description("Set or update the label for a phone number").addHelpText("after", `
|
|
1568
|
+
Examples:
|
|
1569
|
+
$ blink phone label wpn_abc123 "Sales"
|
|
1570
|
+
$ blink phone label wpn_abc123 "" Clear label
|
|
1571
|
+
`).action(async (id, label) => {
|
|
1572
|
+
requireToken();
|
|
1573
|
+
const result = await withSpinner(
|
|
1574
|
+
"Updating label...",
|
|
1575
|
+
() => resourcesRequest(`/api/v1/phone-numbers/${id}`, {
|
|
1576
|
+
method: "PATCH",
|
|
1577
|
+
body: { label }
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
if (isJsonMode()) return printJson(result);
|
|
1581
|
+
console.log(`\u2713 Label updated: ${formatPhone(result.phone_number)} \u2192 "${result.label ?? ""}"`);
|
|
1582
|
+
});
|
|
1583
|
+
phone.command("release <id>").description("Release a phone number (permanent)").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
1584
|
+
Examples:
|
|
1585
|
+
$ blink phone release wpn_abc123
|
|
1586
|
+
$ blink phone release wpn_abc123 --yes
|
|
1587
|
+
|
|
1588
|
+
The number is permanently returned to the carrier pool. This action cannot be undone.
|
|
1589
|
+
`).action(async (id, opts) => {
|
|
1590
|
+
requireToken();
|
|
1591
|
+
if (!opts.yes && !isJsonMode()) {
|
|
1592
|
+
const { confirm } = await import("@clack/prompts");
|
|
1593
|
+
const yes = await confirm({ message: `Release ${id}? This cannot be undone.` });
|
|
1594
|
+
if (!yes) {
|
|
1595
|
+
console.log("Cancelled.");
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
await withSpinner(
|
|
1600
|
+
"Releasing phone number...",
|
|
1601
|
+
() => resourcesRequest(`/api/v1/phone-numbers/${id}`, { method: "DELETE" })
|
|
1602
|
+
);
|
|
1603
|
+
if (isJsonMode()) return printJson({ success: true, id });
|
|
1604
|
+
console.log(`\u2713 Phone number ${id} released`);
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1441
1608
|
// src/lib/api-app.ts
|
|
1442
1609
|
var BASE_URL2 = process.env.BLINK_APP_URL ?? "https://blink.new";
|
|
1443
1610
|
async function appRequest(path, opts = {}) {
|
|
@@ -1981,6 +2148,12 @@ Realtime / RAG / Notify:
|
|
|
1981
2148
|
$ blink rag search "how does billing work" --ai
|
|
1982
2149
|
$ blink notify email user@example.com "Subject" "Body"
|
|
1983
2150
|
|
|
2151
|
+
Phone Numbers (10 credits/month per number):
|
|
2152
|
+
$ blink phone list List all workspace phone numbers
|
|
2153
|
+
$ blink phone buy --label Sales Buy a new number (US, UK, CA, AU)
|
|
2154
|
+
$ blink phone label <id> Sales Update label
|
|
2155
|
+
$ blink phone release <id> Release a number
|
|
2156
|
+
|
|
1984
2157
|
Connectors (38 OAuth providers \u2014 GitHub, Notion, Slack, Stripe, Shopify, Jira, Linear, and more):
|
|
1985
2158
|
$ blink connector providers List all 38 providers
|
|
1986
2159
|
$ blink connector status Show all connected accounts
|
|
@@ -2042,6 +2215,7 @@ registerRagCommands(program);
|
|
|
2042
2215
|
registerNotifyCommands(program);
|
|
2043
2216
|
registerConnectorCommands(program);
|
|
2044
2217
|
registerLinkedInCommands(program);
|
|
2218
|
+
registerPhoneCommands(program);
|
|
2045
2219
|
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", `
|
|
2046
2220
|
Examples:
|
|
2047
2221
|
$ blink use proj_xxx Shows the export command to run
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { registerRagCommands } from './commands/rag.js'
|
|
|
10
10
|
import { registerNotifyCommands } from './commands/notify.js'
|
|
11
11
|
import { registerConnectorCommands } from './commands/connector.js'
|
|
12
12
|
import { registerLinkedInCommands } from './commands/linkedin.js'
|
|
13
|
+
import { registerPhoneCommands } from './commands/phone.js'
|
|
13
14
|
import { registerDeployCommands } from './commands/deploy.js'
|
|
14
15
|
import { registerProjectCommands } from './commands/project.js'
|
|
15
16
|
import { registerAuthCommands } from './commands/auth.js'
|
|
@@ -79,6 +80,12 @@ Realtime / RAG / Notify:
|
|
|
79
80
|
$ blink rag search "how does billing work" --ai
|
|
80
81
|
$ blink notify email user@example.com "Subject" "Body"
|
|
81
82
|
|
|
83
|
+
Phone Numbers (10 credits/month per number):
|
|
84
|
+
$ blink phone list List all workspace phone numbers
|
|
85
|
+
$ blink phone buy --label Sales Buy a new number (US, UK, CA, AU)
|
|
86
|
+
$ blink phone label <id> Sales Update label
|
|
87
|
+
$ blink phone release <id> Release a number
|
|
88
|
+
|
|
82
89
|
Connectors (38 OAuth providers — GitHub, Notion, Slack, Stripe, Shopify, Jira, Linear, and more):
|
|
83
90
|
$ blink connector providers List all 38 providers
|
|
84
91
|
$ blink connector status Show all connected accounts
|
|
@@ -142,6 +149,7 @@ registerRagCommands(program)
|
|
|
142
149
|
registerNotifyCommands(program)
|
|
143
150
|
registerConnectorCommands(program)
|
|
144
151
|
registerLinkedInCommands(program)
|
|
152
|
+
registerPhoneCommands(program)
|
|
145
153
|
|
|
146
154
|
program.command('use <project_id>')
|
|
147
155
|
.description('Set active project for this shell session (alternative to blink link)')
|
|
@@ -0,0 +1,177 @@
|
|
|
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 PhoneRecord {
|
|
7
|
+
id: string
|
|
8
|
+
phone_number: string
|
|
9
|
+
label?: string | null
|
|
10
|
+
area_code?: string | null
|
|
11
|
+
country: string
|
|
12
|
+
status: string
|
|
13
|
+
last_charged_at?: string | null
|
|
14
|
+
created_at: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatPhone(num: string): string {
|
|
18
|
+
const m = num.match(/^\+1(\d{3})(\d{3})(\d{4})$/)
|
|
19
|
+
return m ? `+1 (${m[1]}) ${m[2]}-${m[3]}` : num
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function statusDot(status: string): string {
|
|
23
|
+
return status === 'active' ? '●' : status === 'grace' ? '⚡' : '○'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function nextCharge(lastCharged?: string | null): string {
|
|
27
|
+
if (!lastCharged) return ''
|
|
28
|
+
const d = new Date(new Date(lastCharged).getTime() + 30 * 86_400_000)
|
|
29
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printRecords(records: PhoneRecord[]) {
|
|
33
|
+
if (!records.length) {
|
|
34
|
+
console.log('No phone numbers. Run: blink phone buy')
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
records.forEach((r, i) => {
|
|
38
|
+
const label = r.label ? ` ${r.label}` : ''
|
|
39
|
+
const primary = i === 0 && r.status === 'active' ? ' ★ primary' : ''
|
|
40
|
+
const next = nextCharge(r.last_charged_at)
|
|
41
|
+
const charge = next ? ` Next charge ${next}` : ''
|
|
42
|
+
console.log(`${statusDot(r.status)} ${formatPhone(r.phone_number)}${label}${primary}`)
|
|
43
|
+
console.log(` ${r.id} ${r.country}${r.area_code ? ` · ${r.area_code}` : ''}${charge}`)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function registerPhoneCommands(program: Command) {
|
|
48
|
+
const phone = program.command('phone')
|
|
49
|
+
.description('Manage workspace phone numbers for AI calling')
|
|
50
|
+
.addHelpText('after', `
|
|
51
|
+
Commands:
|
|
52
|
+
list List all workspace phone numbers
|
|
53
|
+
buy Provision a new phone number
|
|
54
|
+
label Update a number's label
|
|
55
|
+
release Release (cancel) a phone number
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
$ blink phone list
|
|
59
|
+
$ blink phone buy --label "Sales" --country US --area-code 415
|
|
60
|
+
$ blink phone buy --label "Support"
|
|
61
|
+
$ blink phone label wpn_abc123 "Support line"
|
|
62
|
+
$ blink phone release wpn_abc123
|
|
63
|
+
|
|
64
|
+
Numbers cost 10 credits/month each. First charge is immediate on buy.
|
|
65
|
+
Primary number (oldest active) is used by default for \`blink ai call\`.
|
|
66
|
+
Use \`blink ai call --from +1XXXXXXXXXX\` to specify a different number.
|
|
67
|
+
`)
|
|
68
|
+
|
|
69
|
+
// Default: list
|
|
70
|
+
phone.action(async () => {
|
|
71
|
+
requireToken()
|
|
72
|
+
const records = await withSpinner('Fetching phone numbers...', () =>
|
|
73
|
+
resourcesRequest('/api/v1/phone-numbers')
|
|
74
|
+
) as PhoneRecord[]
|
|
75
|
+
if (isJsonMode()) return printJson(records)
|
|
76
|
+
printRecords(records)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// blink phone list
|
|
80
|
+
phone.command('list')
|
|
81
|
+
.description('List all workspace phone numbers')
|
|
82
|
+
.addHelpText('after', `
|
|
83
|
+
Examples:
|
|
84
|
+
$ blink phone list
|
|
85
|
+
$ blink phone list --json
|
|
86
|
+
`)
|
|
87
|
+
.action(async () => {
|
|
88
|
+
requireToken()
|
|
89
|
+
const records = await withSpinner('Fetching phone numbers...', () =>
|
|
90
|
+
resourcesRequest('/api/v1/phone-numbers')
|
|
91
|
+
) as PhoneRecord[]
|
|
92
|
+
if (isJsonMode()) return printJson(records)
|
|
93
|
+
printRecords(records)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// blink phone buy
|
|
97
|
+
phone.command('buy')
|
|
98
|
+
.description('Provision a new phone number (10 credits/month)')
|
|
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)')
|
|
102
|
+
.addHelpText('after', `
|
|
103
|
+
Examples:
|
|
104
|
+
$ blink phone buy
|
|
105
|
+
$ blink phone buy --label "Sales" --area-code 415
|
|
106
|
+
$ blink phone buy --label "UK Support" --country GB
|
|
107
|
+
$ blink phone buy --json | jq '.phone_number'
|
|
108
|
+
|
|
109
|
+
10 credits charged immediately, then monthly on anniversary.
|
|
110
|
+
`)
|
|
111
|
+
.action(async (opts) => {
|
|
112
|
+
requireToken()
|
|
113
|
+
const result = await withSpinner('Provisioning phone number...', () =>
|
|
114
|
+
resourcesRequest('/api/v1/phone-numbers', {
|
|
115
|
+
body: {
|
|
116
|
+
label: opts.label || undefined,
|
|
117
|
+
country: opts.country,
|
|
118
|
+
area_code: opts.areaCode || undefined,
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
) as PhoneRecord
|
|
122
|
+
if (isJsonMode()) return printJson(result)
|
|
123
|
+
console.log(`✓ Phone number provisioned`)
|
|
124
|
+
console.log(` Number ${formatPhone(result.phone_number)}`)
|
|
125
|
+
if (result.label) console.log(` Label ${result.label}`)
|
|
126
|
+
console.log(` Country ${result.country}${result.area_code ? ` · Area ${result.area_code}` : ''}`)
|
|
127
|
+
console.log(` ID ${result.id}`)
|
|
128
|
+
console.log(` Billing 10 credits/month`)
|
|
129
|
+
console.log()
|
|
130
|
+
console.log(`Use it: blink ai call "+1..." "Your task" --from "${result.phone_number}"`)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// blink phone label <id> <label>
|
|
134
|
+
phone.command('label <id> <label>')
|
|
135
|
+
.description('Set or update the label for a phone number')
|
|
136
|
+
.addHelpText('after', `
|
|
137
|
+
Examples:
|
|
138
|
+
$ blink phone label wpn_abc123 "Sales"
|
|
139
|
+
$ blink phone label wpn_abc123 "" Clear label
|
|
140
|
+
`)
|
|
141
|
+
.action(async (id: string, label: string) => {
|
|
142
|
+
requireToken()
|
|
143
|
+
const result = await withSpinner('Updating label...', () =>
|
|
144
|
+
resourcesRequest(`/api/v1/phone-numbers/${id}`, {
|
|
145
|
+
method: 'PATCH',
|
|
146
|
+
body: { label },
|
|
147
|
+
})
|
|
148
|
+
) as PhoneRecord
|
|
149
|
+
if (isJsonMode()) return printJson(result)
|
|
150
|
+
console.log(`✓ Label updated: ${formatPhone(result.phone_number)} → "${result.label ?? ''}"`)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// blink phone release <id>
|
|
154
|
+
phone.command('release <id>')
|
|
155
|
+
.description('Release a phone number (permanent)')
|
|
156
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
157
|
+
.addHelpText('after', `
|
|
158
|
+
Examples:
|
|
159
|
+
$ blink phone release wpn_abc123
|
|
160
|
+
$ blink phone release wpn_abc123 --yes
|
|
161
|
+
|
|
162
|
+
The number is permanently returned to the carrier pool. This action cannot be undone.
|
|
163
|
+
`)
|
|
164
|
+
.action(async (id: string, opts) => {
|
|
165
|
+
requireToken()
|
|
166
|
+
if (!opts.yes && !isJsonMode()) {
|
|
167
|
+
const { confirm } = await import('@clack/prompts')
|
|
168
|
+
const yes = await confirm({ message: `Release ${id}? This cannot be undone.` })
|
|
169
|
+
if (!yes) { console.log('Cancelled.'); return }
|
|
170
|
+
}
|
|
171
|
+
await withSpinner('Releasing phone number...', () =>
|
|
172
|
+
resourcesRequest(`/api/v1/phone-numbers/${id}`, { method: 'DELETE' })
|
|
173
|
+
)
|
|
174
|
+
if (isJsonMode()) return printJson({ success: true, id })
|
|
175
|
+
console.log(`✓ Phone number ${id} released`)
|
|
176
|
+
})
|
|
177
|
+
}
|
package/src/commands/storage.ts
CHANGED
|
@@ -80,11 +80,21 @@ Examples:
|
|
|
80
80
|
console.log(table.toString())
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
storage.command('download <
|
|
84
|
-
.description('Download a file from storage')
|
|
85
|
-
.action(async (
|
|
83
|
+
storage.command('download <arg1> [arg2] [output]')
|
|
84
|
+
.description('Download a file from storage — blink storage download <path> [output] OR blink storage download <proj> <path> [output]')
|
|
85
|
+
.action(async (arg1: string, arg2: string | undefined, output: string | undefined) => {
|
|
86
86
|
requireToken()
|
|
87
|
-
|
|
87
|
+
let projectId: string
|
|
88
|
+
let storagePath: string
|
|
89
|
+
if (arg2 !== undefined && !arg1.startsWith('proj_')) {
|
|
90
|
+
// blink storage download <path> <output> (no project_id)
|
|
91
|
+
projectId = requireProjectId(); storagePath = arg1; output = arg2
|
|
92
|
+
} else if (arg2 !== undefined) {
|
|
93
|
+
// blink storage download proj_xxx <path> [output]
|
|
94
|
+
projectId = requireProjectId(arg1); storagePath = arg2
|
|
95
|
+
} else {
|
|
96
|
+
projectId = requireProjectId(); storagePath = arg1
|
|
97
|
+
}
|
|
88
98
|
const result = await withSpinner('Downloading...', () =>
|
|
89
99
|
resourcesRequest(`/api/storage/${projectId}/download`, { body: { path: storagePath } })
|
|
90
100
|
)
|
|
@@ -94,11 +104,14 @@ Examples:
|
|
|
94
104
|
if (!isJsonMode()) console.log('Saved to ' + outFile)
|
|
95
105
|
})
|
|
96
106
|
|
|
97
|
-
storage.command('delete <
|
|
98
|
-
.description('Delete a file from storage')
|
|
99
|
-
.action(async (
|
|
107
|
+
storage.command('delete <arg1> [arg2]')
|
|
108
|
+
.description('Delete a file from storage — blink storage delete <path> OR blink storage delete <proj> <path>')
|
|
109
|
+
.action(async (arg1: string, arg2: string | undefined) => {
|
|
100
110
|
requireToken()
|
|
101
|
-
|
|
111
|
+
let projectId: string
|
|
112
|
+
let storagePath: string
|
|
113
|
+
if (arg2 !== undefined) { projectId = requireProjectId(arg1); storagePath = arg2 }
|
|
114
|
+
else { projectId = requireProjectId(); storagePath = arg1 }
|
|
102
115
|
await withSpinner('Deleting...', () =>
|
|
103
116
|
resourcesRequest(`/api/storage/${projectId}/remove`, { method: 'DELETE', body: { path: storagePath } })
|
|
104
117
|
)
|
|
@@ -106,11 +119,14 @@ Examples:
|
|
|
106
119
|
else printJson({ status: 'ok', path: storagePath })
|
|
107
120
|
})
|
|
108
121
|
|
|
109
|
-
storage.command('url <
|
|
110
|
-
.description('Get public URL for a storage file')
|
|
111
|
-
.action(async (
|
|
122
|
+
storage.command('url <arg1> [arg2]')
|
|
123
|
+
.description('Get public URL for a storage file — blink storage url <path> OR blink storage url <proj> <path>')
|
|
124
|
+
.action(async (arg1: string, arg2: string | undefined) => {
|
|
112
125
|
requireToken()
|
|
113
|
-
|
|
126
|
+
let projectId: string
|
|
127
|
+
let storagePath: string
|
|
128
|
+
if (arg2 !== undefined) { projectId = requireProjectId(arg1); storagePath = arg2 }
|
|
129
|
+
else { projectId = requireProjectId(); storagePath = arg1 }
|
|
114
130
|
const result = await resourcesRequest(`/api/storage/${projectId}/public-url`, { body: { path: storagePath } })
|
|
115
131
|
if (isJsonMode()) return printJson(result)
|
|
116
132
|
console.log(result?.url ?? result)
|