@getdial/cli 0.12.1 → 0.13.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.
- package/README.md +81 -7
- package/dist/cli.js +19 -4
- package/dist/commands/call/get.js +3 -3
- package/dist/commands/call/send.js +1 -1
- package/dist/commands/number/purchase.js +1 -1
- package/dist/commands/number/set.js +44 -0
- package/dist/commands/onboard.js +7 -1
- package/dist/lib/api.js +3 -0
- package/package.json +1 -1
- package/skills.tar.gz +0 -0
package/README.md
CHANGED
|
@@ -1,19 +1,93 @@
|
|
|
1
1
|
# @getdial/cli
|
|
2
2
|
|
|
3
|
-
The Dial CLI.
|
|
3
|
+
The official command-line interface for [Dial](https://getdial.ai) — a communication stack for AI agents. Provision phone numbers, send SMS, place AI voice calls, and react to inbound events, all from your terminal. The CLI wraps the [Dial REST API](https://docs.getdial.ai) so you never have to write HTTP code.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install globally from npm:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
10
|
+
npm install -g @getdial/cli
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
Or
|
|
13
|
+
Or use the bootstrap script, which installs the CLI and walks you through sign-up:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
|
|
16
|
+
curl -fsSL https://getdial.ai/install | bash
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requires **Node.js 22+**.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
dial signup you@example.com # email a 6-digit sign-up code
|
|
25
|
+
dial onboard --code 123456 \ # verify the code and provision your account
|
|
26
|
+
--inbound-instruction "You are my receptionist. Greet the caller and find out what they need."
|
|
27
|
+
dial doctor # check account state and what to do next
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Once onboarded, your API key is saved locally and the CLI uses it automatically.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Send an SMS
|
|
34
|
+
dial message --to +14155550123 --body "Hello from Dial"
|
|
35
|
+
|
|
36
|
+
# Place an AI voice call
|
|
37
|
+
dial call --to +14155550123 --outbound-instruction "You are a helpful scheduling assistant."
|
|
38
|
+
|
|
39
|
+
# Wait for an inbound event (e.g. a verification code)
|
|
40
|
+
dial wait-for message.received --field to=+14155550123
|
|
15
41
|
```
|
|
16
42
|
|
|
17
|
-
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
| Command | Description |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `dial doctor` | Report account state and what to do next. |
|
|
48
|
+
| `dial signup <email>` | Email a 6-digit sign-up code. |
|
|
49
|
+
| `dial onboard --code <code>` | Verify the code and finish onboarding. |
|
|
50
|
+
| `dial number list` | List the phone numbers on your account. |
|
|
51
|
+
| `dial number purchase` | Purchase an additional phone number. |
|
|
52
|
+
| `dial number set <number>` | Update a number's inbound instruction. |
|
|
53
|
+
| `dial message` | Send an SMS. |
|
|
54
|
+
| `dial message list` | List recent messages. |
|
|
55
|
+
| `dial call` | Place an outbound AI voice call. |
|
|
56
|
+
| `dial call list` | List recent calls. |
|
|
57
|
+
| `dial call get <id>` | Fetch a single call — status, duration, transcript. |
|
|
58
|
+
| `dial wait-for <event>` | Block until a matching account event arrives. |
|
|
59
|
+
| `dial listen install` | Install the background event daemon. |
|
|
60
|
+
| `dial listen status` | Report the daemon's state and recent events. |
|
|
61
|
+
| `dial listen uninstall` | Stop and remove the daemon. |
|
|
62
|
+
| `dial local-target add url <url>` | Fan out events to a local HTTP endpoint. |
|
|
63
|
+
| `dial local-target add cmd <path>` | Run an executable once per event. |
|
|
64
|
+
| `dial local-target list` | List registered fan-out targets. |
|
|
65
|
+
| `dial local-target remove <id>` | Unregister a fan-out target. |
|
|
66
|
+
|
|
67
|
+
Run `dial --help` for the full command tree, or `dial <command> --help` for a specific command's flags. Every command accepts `--json` for machine-readable output.
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
| Variable | Description |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `DIAL_API_URL` | Target a non-default Dial deployment. |
|
|
74
|
+
|
|
75
|
+
Your API key is stored at `~/.local/share/dial/auth.json` (honoring `XDG_DATA_HOME`).
|
|
76
|
+
|
|
77
|
+
## Use from an AI agent
|
|
78
|
+
|
|
79
|
+
The CLI ships with a [skill](https://docs.getdial.ai/integrations/tools/cli-skill) that teaches AI coding agents how to drive `dial`. Install it into your agent's config during onboarding:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
dial onboard --code 123456 --agent claude-code
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Supported agents: `claude-code`, `cursor`, `codex`, `opencode`, `pi`, `openclaw`, `nanoclaw`, `hermes`.
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
Full documentation lives at **[docs.getdial.ai](https://docs.getdial.ai)** — including the [CLI reference](https://docs.getdial.ai/documentation/cli/commands) and the [listen service](https://docs.getdial.ai/documentation/cli/listen-service) guide.
|
|
90
|
+
|
|
91
|
+
## License
|
|
18
92
|
|
|
19
|
-
|
|
93
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import { runListenStatus } from "./commands/listen/status.js";
|
|
|
11
11
|
import { runWaitFor } from "./commands/wait-for.js";
|
|
12
12
|
import { runNumberList } from "./commands/number/list.js";
|
|
13
13
|
import { runNumberPurchase } from "./commands/number/purchase.js";
|
|
14
|
+
import { runNumberSet } from "./commands/number/set.js";
|
|
14
15
|
import { runMessageSend } from "./commands/message/send.js";
|
|
15
16
|
import { runMessageList } from "./commands/message/list.js";
|
|
16
17
|
import { runCallSend } from "./commands/call/send.js";
|
|
@@ -42,11 +43,13 @@ program
|
|
|
42
43
|
.description("Verify the OTP and finish onboarding.")
|
|
43
44
|
.option("--verification-id <id>", "explicit verification id (falls back to local pending signup)")
|
|
44
45
|
.requiredOption("--code <code>", "6-digit OTP from your email")
|
|
46
|
+
.option("--inbound-instruction <text>", "system prompt for inbound calls to your auto-provisioned number (required for a new account; ignored when signing in)")
|
|
45
47
|
.option("--agent <name>", "install the Dial skill into the named agent's config dir. One of: claude-code, cursor, codex, opencode, pi, openclaw, nanoclaw, hermes. Repeatable.", (v, prev = []) => [...prev, v], [])
|
|
46
48
|
.option("--json", "machine-readable output")
|
|
47
49
|
.action(async (opts) => process.exit(await runOnboard({
|
|
48
50
|
verificationId: opts.verificationId,
|
|
49
51
|
code: opts.code,
|
|
52
|
+
inboundInstruction: opts.inboundInstruction,
|
|
50
53
|
agents: opts.agent,
|
|
51
54
|
json: !!opts.json,
|
|
52
55
|
})));
|
|
@@ -80,14 +83,26 @@ number
|
|
|
80
83
|
number
|
|
81
84
|
.command("purchase")
|
|
82
85
|
.description("Purchase an additional phone number. POST /api/v1/numbers.")
|
|
86
|
+
.requiredOption("--inbound-instruction <text>", "system prompt for inbound calls to this number")
|
|
83
87
|
.option("--country <iso2>", "ISO-3166-1 alpha-2 country code (defaults to US server-side)")
|
|
84
88
|
.option("--area-code <code>", "preferred area code (US/CA)")
|
|
85
89
|
.option("--json", "machine-readable output")
|
|
86
90
|
.action(async (opts) => process.exit(await runNumberPurchase({
|
|
91
|
+
inboundInstruction: opts.inboundInstruction,
|
|
87
92
|
country: opts.country,
|
|
88
93
|
areaCode: opts.areaCode,
|
|
89
94
|
json: !!opts.json,
|
|
90
95
|
})));
|
|
96
|
+
number
|
|
97
|
+
.command("set <number>")
|
|
98
|
+
.description("Update a number's inbound instruction. PATCH /api/v1/numbers/<id>.")
|
|
99
|
+
.requiredOption("--inbound-instruction <text>", "new system prompt for inbound calls to this number")
|
|
100
|
+
.option("--json", "machine-readable output")
|
|
101
|
+
.action(async (numberArg, opts) => process.exit(await runNumberSet({
|
|
102
|
+
number: numberArg,
|
|
103
|
+
inboundInstruction: opts.inboundInstruction,
|
|
104
|
+
json: !!opts.json,
|
|
105
|
+
})));
|
|
91
106
|
const message = program
|
|
92
107
|
.command("message")
|
|
93
108
|
.description("Send an SMS. POST /api/v1/messages.")
|
|
@@ -124,18 +139,18 @@ const call = program
|
|
|
124
139
|
.command("call")
|
|
125
140
|
.description("Place an outbound voice call. POST /api/v1/calls.")
|
|
126
141
|
.option("--to <e164>", "destination phone number, E.164 (e.g. +14155551234)")
|
|
127
|
-
.option("--
|
|
142
|
+
.option("--outbound-instruction <text>", "system prompt for the agent that will speak")
|
|
128
143
|
.option("--language <bcp47>", "BCP-47 language tag for the call", "en-US")
|
|
129
144
|
.option("--from-number-id <id>", "phoneNumberId to call from (defaults to onboard's number)")
|
|
130
145
|
.option("--json", "machine-readable output")
|
|
131
146
|
.action(async (opts) => {
|
|
132
|
-
if (!opts.to || !opts.
|
|
133
|
-
console.error("error: --to and --
|
|
147
|
+
if (!opts.to || !opts.outboundInstruction) {
|
|
148
|
+
console.error("error: --to and --outbound-instruction are required to place a call. Use `dial call list` to list, `dial call get <id>` to fetch one, or `dial call --help` for usage.");
|
|
134
149
|
process.exit(2);
|
|
135
150
|
}
|
|
136
151
|
process.exit(await runCallSend({
|
|
137
152
|
to: opts.to,
|
|
138
|
-
|
|
153
|
+
outboundInstruction: opts.outboundInstruction,
|
|
139
154
|
language: opts.language,
|
|
140
155
|
fromNumberId: opts.fromNumberId,
|
|
141
156
|
json: !!opts.json,
|
|
@@ -23,9 +23,9 @@ export async function runCallGet(opts) {
|
|
|
23
23
|
console.log(`status: ${c.status}`);
|
|
24
24
|
console.log(`duration: ${c.duration}s`);
|
|
25
25
|
console.log(`created: ${c.createdAt}`);
|
|
26
|
-
if (c.
|
|
27
|
-
console.log(`
|
|
28
|
-
console.log(c.
|
|
26
|
+
if (c.instruction) {
|
|
27
|
+
console.log(`instruction:`);
|
|
28
|
+
console.log(c.instruction);
|
|
29
29
|
}
|
|
30
30
|
if (c.transcript) {
|
|
31
31
|
console.log(`transcript:`);
|
|
@@ -14,7 +14,7 @@ export async function runCallSend(opts) {
|
|
|
14
14
|
const res = await apiPost("/api/v1/calls", {
|
|
15
15
|
to: opts.to,
|
|
16
16
|
fromNumberId,
|
|
17
|
-
|
|
17
|
+
outboundInstruction: opts.outboundInstruction,
|
|
18
18
|
language: opts.language,
|
|
19
19
|
}, auth.apiKey);
|
|
20
20
|
if (!res.ok) {
|
|
@@ -6,7 +6,7 @@ export async function runNumberPurchase(opts) {
|
|
|
6
6
|
fail(opts.json, "not_signed_in", "Not signed in. Run `dial signup` and `dial onboard` first.");
|
|
7
7
|
return 1;
|
|
8
8
|
}
|
|
9
|
-
const body = {};
|
|
9
|
+
const body = { inboundInstruction: opts.inboundInstruction };
|
|
10
10
|
if (opts.country)
|
|
11
11
|
body.country = opts.country;
|
|
12
12
|
if (opts.areaCode)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readAuth } from "../../lib/state.js";
|
|
2
|
+
import { apiGet, apiPatch } from "../../lib/api.js";
|
|
3
|
+
export async function runNumberSet(opts) {
|
|
4
|
+
const auth = readAuth();
|
|
5
|
+
if (!auth) {
|
|
6
|
+
fail(opts.json, "not_signed_in", "Not signed in. Run `dial signup` and `dial onboard` first.");
|
|
7
|
+
return 1;
|
|
8
|
+
}
|
|
9
|
+
// The REST API keys numbers by id; the CLI takes the E.164 number for ergonomics,
|
|
10
|
+
// so resolve it to its id first.
|
|
11
|
+
const list = await apiGet("/api/v1/numbers", auth.apiKey);
|
|
12
|
+
if (!list.ok) {
|
|
13
|
+
fail(opts.json, "list_failed", list.error, { status: list.status });
|
|
14
|
+
return 2;
|
|
15
|
+
}
|
|
16
|
+
const match = list.data.numbers.find((n) => n.number === opts.number);
|
|
17
|
+
if (!match) {
|
|
18
|
+
const known = list.data.numbers.map((n) => n.number).join(", ") || "(none)";
|
|
19
|
+
fail(opts.json, "number_not_found", `No phone number ${opts.number} on your account. Yours: ${known}.`);
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
const res = await apiPatch(`/api/v1/numbers/${match.id}`, { inboundInstruction: opts.inboundInstruction }, auth.apiKey);
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
fail(opts.json, "update_failed", res.error, { status: res.status });
|
|
25
|
+
return 2;
|
|
26
|
+
}
|
|
27
|
+
const n = res.data.number;
|
|
28
|
+
if (opts.json) {
|
|
29
|
+
console.log(JSON.stringify({ ok: true, number: n }));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log(`updated.`);
|
|
33
|
+
console.log(` number: ${n.number}`);
|
|
34
|
+
console.log(` id: ${n.id}`);
|
|
35
|
+
console.log(` inbound instruction: ${n.inboundInstruction ?? ""}`);
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
function fail(json, code, message, extra) {
|
|
40
|
+
if (json)
|
|
41
|
+
console.log(JSON.stringify({ ok: false, code, message, ...extra }));
|
|
42
|
+
else
|
|
43
|
+
console.error(message);
|
|
44
|
+
}
|
package/dist/commands/onboard.js
CHANGED
|
@@ -27,7 +27,13 @@ export async function runOnboard(opts) {
|
|
|
27
27
|
verificationId = pending.verificationId;
|
|
28
28
|
email = pending.email;
|
|
29
29
|
}
|
|
30
|
-
const res = await apiPost("/api/v1/auth/verify", {
|
|
30
|
+
const res = await apiPost("/api/v1/auth/verify", {
|
|
31
|
+
verificationId,
|
|
32
|
+
code: opts.code,
|
|
33
|
+
// Sent only when provided; the server requires it when provisioning a first
|
|
34
|
+
// number (new account) and ignores it when signing in.
|
|
35
|
+
...(opts.inboundInstruction ? { inboundInstruction: opts.inboundInstruction } : {}),
|
|
36
|
+
});
|
|
31
37
|
if (!res.ok) {
|
|
32
38
|
if (opts.json)
|
|
33
39
|
console.log(JSON.stringify({ ok: false, code: "verify_failed", status: res.status, error: res.error }));
|
package/dist/lib/api.js
CHANGED
|
@@ -10,6 +10,9 @@ export async function apiPost(path, body, apiKey) {
|
|
|
10
10
|
export async function apiGet(path, apiKey) {
|
|
11
11
|
return apiRequest("GET", path, undefined, apiKey);
|
|
12
12
|
}
|
|
13
|
+
export async function apiPatch(path, body, apiKey) {
|
|
14
|
+
return apiRequest("PATCH", path, body, apiKey);
|
|
15
|
+
}
|
|
13
16
|
async function apiRequest(method, path, body, apiKey) {
|
|
14
17
|
const url = `${baseUrl()}${path}`;
|
|
15
18
|
const headers = { "content-type": "application/json" };
|
package/package.json
CHANGED
package/skills.tar.gz
CHANGED
|
Binary file
|