@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 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
- Install via the bootstrap script:
5
+ ## Installation
6
+
7
+ Install globally from npm:
6
8
 
7
9
  ```bash
8
- curl -fsSL https://dial.up.railway.app/install | bash
10
+ npm install -g @getdial/cli
9
11
  ```
10
12
 
11
- Or directly from npm:
13
+ Or use the bootstrap script, which installs the CLI and walks you through sign-up:
12
14
 
13
15
  ```bash
14
- npm install -g @getdial/cli
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
- Requires Node 22+.
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
- See `docs/superpowers/specs/2026-05-25-dial-cli-and-listen-service-design.md` for design.
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("--system-prompt <text>", "system prompt for the agent that will speak")
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.systemPrompt) {
133
- console.error("error: --to and --system-prompt 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.");
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
- systemPrompt: opts.systemPrompt,
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.systemPrompt) {
27
- console.log(`systemPrompt:`);
28
- console.log(c.systemPrompt);
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
- systemPrompt: opts.systemPrompt,
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
+ }
@@ -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", { verificationId, code: opts.code });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdial/cli",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "Dial CLI — install, sign up, and run the local listen service.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/skills.tar.gz CHANGED
Binary file