@getdial/cli 0.13.2 → 0.14.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.
Files changed (51) hide show
  1. package/README.md +13 -1
  2. package/dist/cli.js +5 -0
  3. package/dist/commands/call/get.js +28 -36
  4. package/dist/commands/call/list.js +20 -37
  5. package/dist/commands/call/send.js +52 -65
  6. package/dist/commands/doctor.js +2 -68
  7. package/dist/commands/mcp.js +17 -0
  8. package/dist/commands/message/list.js +20 -37
  9. package/dist/commands/message/send.js +22 -35
  10. package/dist/commands/number/list.js +21 -29
  11. package/dist/commands/number/purchase.js +23 -32
  12. package/dist/commands/number/set.js +19 -40
  13. package/dist/commands/onboard.js +36 -67
  14. package/dist/commands/signup.js +23 -24
  15. package/dist/commands/wait-for.js +33 -67
  16. package/dist/lib/cli-error.js +24 -0
  17. package/dist/lib/ops/account.js +136 -0
  18. package/dist/lib/ops/auth.js +18 -0
  19. package/dist/lib/ops/calls.js +33 -0
  20. package/dist/lib/ops/errors.js +23 -0
  21. package/dist/lib/ops/events.js +63 -0
  22. package/dist/lib/ops/listen.js +51 -0
  23. package/dist/lib/ops/local-targets.js +35 -0
  24. package/dist/lib/ops/messages.js +26 -0
  25. package/dist/lib/ops/numbers.js +39 -0
  26. package/dist/mcp/register.js +27 -0
  27. package/dist/mcp/result.js +6 -0
  28. package/dist/mcp/server.js +20 -0
  29. package/dist/mcp/tool.js +1 -0
  30. package/dist/mcp/tools/add-command-target.js +22 -0
  31. package/dist/mcp/tools/add-url-target.js +26 -0
  32. package/dist/mcp/tools/get-account-status.js +13 -0
  33. package/dist/mcp/tools/get-call.js +16 -0
  34. package/dist/mcp/tools/index.js +41 -0
  35. package/dist/mcp/tools/list-calls.js +22 -0
  36. package/dist/mcp/tools/list-local-targets.js +12 -0
  37. package/dist/mcp/tools/list-messages.js +22 -0
  38. package/dist/mcp/tools/list-numbers.js +12 -0
  39. package/dist/mcp/tools/listen-install.js +13 -0
  40. package/dist/mcp/tools/listen-status.js +12 -0
  41. package/dist/mcp/tools/listen-uninstall.js +12 -0
  42. package/dist/mcp/tools/onboard.js +34 -0
  43. package/dist/mcp/tools/place-call.js +33 -0
  44. package/dist/mcp/tools/purchase-number.js +22 -0
  45. package/dist/mcp/tools/remove-local-target.js +16 -0
  46. package/dist/mcp/tools/send-message.js +22 -0
  47. package/dist/mcp/tools/set-number-properties.js +20 -0
  48. package/dist/mcp/tools/sign-up.js +18 -0
  49. package/dist/mcp/tools/wait-for-event.js +25 -0
  50. package/package.json +2 -1
  51. package/skills.tar.gz +0 -0
package/README.md CHANGED
@@ -63,8 +63,9 @@ dial wait-for message.received --field to=+14155550123
63
63
  | `dial local-target add cmd <path>` | Run an executable once per event. |
64
64
  | `dial local-target list` | List registered fan-out targets. |
65
65
  | `dial local-target remove <id>` | Unregister a fan-out target. |
66
+ | `dial mcp` | Run a local stdio MCP server exposing every command as an agent tool. |
66
67
 
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
+ 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 (except `dial mcp`, which speaks JSON-RPC on stdout).
68
69
 
69
70
  ## Configuration
70
71
 
@@ -84,6 +85,17 @@ dial onboard --code 123456 --agent claude-code
84
85
 
85
86
  Supported agents: `claude-code`, `cursor`, `codex`, `opencode`, `pi`, `openclaw`, `nanoclaw`, `hermes`.
86
87
 
88
+ ## Local MCP server
89
+
90
+ `dial mcp` runs a local [Model Context Protocol](https://modelcontextprotocol.io) server over stdio, exposing every command as an agent tool. Point a local MCP client at `dial mcp` as the server command — it reuses the API key saved by `dial onboard` (no OAuth, no config). It's the local counterpart to the hosted [Remote MCP](https://docs.getdial.ai/integrations/tools/remote-mcp) server, with the same operational tools plus the local-only verbs (`signup`, `onboard`, `listen`, `local-target`).
91
+
92
+ ```bash
93
+ # Claude Code, for example
94
+ claude mcp add dial -- dial mcp
95
+ ```
96
+
97
+ See the [Local MCP](https://docs.getdial.ai/integrations/tools/local-mcp) docs for client setup.
98
+
87
99
  ## Documentation
88
100
 
89
101
  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.
package/dist/cli.js CHANGED
@@ -21,6 +21,7 @@ import { runLocalTargetAddUrl } from "./commands/local-target/add-url.js";
21
21
  import { runLocalTargetAddCmd } from "./commands/local-target/add-cmd.js";
22
22
  import { runLocalTargetRemove } from "./commands/local-target/remove.js";
23
23
  import { runLocalTargetList } from "./commands/local-target/list.js";
24
+ import { runMcp } from "./commands/mcp.js";
24
25
  const program = new Command();
25
26
  program
26
27
  .name("dial")
@@ -234,6 +235,10 @@ program
234
235
  timeoutSeconds: opts.timeout,
235
236
  json: !!opts.json,
236
237
  })));
238
+ program
239
+ .command("mcp")
240
+ .description("Run a local stdio MCP server exposing Dial as agent tools (reuses your saved API key).")
241
+ .action(async () => process.exit(await runMcp()));
237
242
  program.parseAsync(process.argv).catch((err) => {
238
243
  console.error(err instanceof Error ? err.message : String(err));
239
244
  process.exit(2);
@@ -1,41 +1,33 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiGet } from "../../lib/api.js";
1
+ import { getCall } from "../../lib/ops/calls.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runCallGet(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
- const res = await apiGet(`/api/v1/calls/${encodeURIComponent(opts.callId)}`, auth.apiKey);
10
- if (!res.ok) {
11
- fail(opts.json, res.status === 404 ? "not_found" : "get_failed", res.error, { status: res.status });
12
- return res.status === 404 ? 1 : 2;
13
- }
14
- const c = res.data.call;
15
- if (opts.json) {
16
- console.log(JSON.stringify({ ok: true, call: c }));
5
+ try {
6
+ const c = await getCall(opts.callId);
7
+ if (opts.json) {
8
+ console.log(JSON.stringify({ ok: true, call: c }));
9
+ return 0;
10
+ }
11
+ console.log(`id: ${c.id}`);
12
+ console.log(`direction: ${c.direction}`);
13
+ console.log(`from: ${c.from}`);
14
+ console.log(`to: ${c.to}`);
15
+ console.log(`status: ${c.status}`);
16
+ console.log(`duration: ${c.duration}s`);
17
+ console.log(`created: ${c.createdAt}`);
18
+ if (c.instruction) {
19
+ console.log(`instruction:`);
20
+ console.log(c.instruction);
21
+ }
22
+ if (c.transcript) {
23
+ console.log(`transcript:`);
24
+ console.log(c.transcript);
25
+ }
17
26
  return 0;
18
27
  }
19
- console.log(`id: ${c.id}`);
20
- console.log(`direction: ${c.direction}`);
21
- console.log(`from: ${c.from}`);
22
- console.log(`to: ${c.to}`);
23
- console.log(`status: ${c.status}`);
24
- console.log(`duration: ${c.duration}s`);
25
- console.log(`created: ${c.createdAt}`);
26
- if (c.instruction) {
27
- console.log(`instruction:`);
28
- console.log(c.instruction);
28
+ catch (e) {
29
+ if (isDialError(e))
30
+ return printDialError(opts.json, e);
31
+ throw e;
29
32
  }
30
- if (c.transcript) {
31
- console.log(`transcript:`);
32
- console.log(c.transcript);
33
- }
34
- return 0;
35
- }
36
- function fail(json, code, message, extra) {
37
- if (json)
38
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
39
- else
40
- console.error(message);
41
33
  }
@@ -1,42 +1,25 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiGet } from "../../lib/api.js";
1
+ import { listCalls } from "../../lib/ops/calls.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runCallList(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
- const params = new URLSearchParams();
10
- if (opts.numberId)
11
- params.set("numberId", opts.numberId);
12
- if (opts.direction)
13
- params.set("direction", opts.direction);
14
- if (opts.since)
15
- params.set("since", opts.since);
16
- const qs = params.toString();
17
- const path = qs ? `/api/v1/calls?${qs}` : "/api/v1/calls";
18
- const res = await apiGet(path, auth.apiKey);
19
- if (!res.ok) {
20
- fail(opts.json, "list_failed", res.error, { status: res.status });
21
- return 2;
22
- }
23
- const calls = res.data.calls ?? [];
24
- if (opts.json) {
25
- console.log(JSON.stringify({ ok: true, calls }));
5
+ try {
6
+ const calls = await listCalls({ numberId: opts.numberId, direction: opts.direction, since: opts.since });
7
+ if (opts.json) {
8
+ console.log(JSON.stringify({ ok: true, calls }));
9
+ return 0;
10
+ }
11
+ if (calls.length === 0) {
12
+ console.log("no calls.");
13
+ return 0;
14
+ }
15
+ for (const c of calls) {
16
+ console.log(`${c.createdAt} ${c.direction.padEnd(8)} ${c.from} -> ${c.to} ${c.status} ${c.duration}s id=${c.id}`);
17
+ }
26
18
  return 0;
27
19
  }
28
- if (calls.length === 0) {
29
- console.log("no calls.");
30
- return 0;
20
+ catch (e) {
21
+ if (isDialError(e))
22
+ return printDialError(opts.json, e);
23
+ throw e;
31
24
  }
32
- for (const c of calls) {
33
- console.log(`${c.createdAt} ${c.direction.padEnd(8)} ${c.from} -> ${c.to} ${c.status} ${c.duration}s id=${c.id}`);
34
- }
35
- return 0;
36
- }
37
- function fail(json, code, message, extra) {
38
- if (json)
39
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
40
- else
41
- console.error(message);
42
25
  }
@@ -1,69 +1,56 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiPost } from "../../lib/api.js";
1
+ import { placeCall } from "../../lib/ops/calls.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runCallSend(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;
5
+ try {
6
+ const c = await placeCall({
7
+ to: opts.to,
8
+ outboundInstruction: opts.outboundInstruction,
9
+ language: opts.language,
10
+ fromNumberId: opts.fromNumberId,
11
+ });
12
+ const waitCmd = `dial wait-for call.ended -f callId=${c.id} --json`;
13
+ const followUpQuestion = "Want me to wait for the call to finish and report back (duration, final status, transcript)?";
14
+ if (opts.json) {
15
+ console.log(JSON.stringify({
16
+ ok: true,
17
+ call: c,
18
+ agentHint: {
19
+ action: "ask_user_about_waiting",
20
+ kind: "post_call_followup",
21
+ question: followUpQuestion,
22
+ waitCommand: waitCmd,
23
+ onYes: waitCmd,
24
+ onNo: null,
25
+ useStructuredToolIfAvailable: true,
26
+ },
27
+ }));
28
+ }
29
+ else {
30
+ console.log(`call initiated.`);
31
+ console.log(` from: ${c.from}`);
32
+ console.log(` to: ${c.to}`);
33
+ console.log(` status: ${c.status}`);
34
+ console.log(` call id: ${c.id}`);
35
+ console.log(``);
36
+ console.log(`The call has been dispatched. To learn when it ends (duration, final`);
37
+ console.log(`status, transcript availability), wait for the call.ended event:`);
38
+ console.log(``);
39
+ console.log(` ${waitCmd}`);
40
+ console.log(``);
41
+ console.log(`Suggestion: ask the user whether they'd like you to wait for the call`);
42
+ console.log(`to finish and report back, e.g.`);
43
+ console.log(``);
44
+ console.log(` "${followUpQuestion}"`);
45
+ console.log(``);
46
+ console.log(`If yes → run the wait-for above and report when it returns.`);
47
+ console.log(`If no → you're done; they can run the wait-for themselves later.`);
48
+ }
49
+ return 0;
8
50
  }
9
- const fromNumberId = opts.fromNumberId ?? auth.phoneNumberId;
10
- if (!fromNumberId) {
11
- fail(opts.json, "no_from_number", "No default phoneNumberId in auth. Pass --from-number-id <id>.");
12
- return 1;
51
+ catch (e) {
52
+ if (isDialError(e))
53
+ return printDialError(opts.json, e);
54
+ throw e;
13
55
  }
14
- const res = await apiPost("/api/v1/calls", {
15
- to: opts.to,
16
- fromNumberId,
17
- outboundInstruction: opts.outboundInstruction,
18
- language: opts.language,
19
- }, auth.apiKey);
20
- if (!res.ok) {
21
- fail(opts.json, "call_failed", res.error, { status: res.status });
22
- return 2;
23
- }
24
- const c = res.data.call;
25
- const waitCmd = `dial wait-for call.ended -f callId=${c.id} --json`;
26
- const followUpQuestion = "Want me to wait for the call to finish and report back (duration, final status, transcript)?";
27
- if (opts.json) {
28
- console.log(JSON.stringify({
29
- ok: true,
30
- call: c,
31
- agentHint: {
32
- action: "ask_user_about_waiting",
33
- kind: "post_call_followup",
34
- question: followUpQuestion,
35
- waitCommand: waitCmd,
36
- onYes: waitCmd,
37
- onNo: null,
38
- useStructuredToolIfAvailable: true,
39
- },
40
- }));
41
- }
42
- else {
43
- console.log(`call initiated.`);
44
- console.log(` from: ${c.from}`);
45
- console.log(` to: ${c.to}`);
46
- console.log(` status: ${c.status}`);
47
- console.log(` call id: ${c.id}`);
48
- console.log(``);
49
- console.log(`The call has been dispatched. To learn when it ends (duration, final`);
50
- console.log(`status, transcript availability), wait for the call.ended event:`);
51
- console.log(``);
52
- console.log(` ${waitCmd}`);
53
- console.log(``);
54
- console.log(`Suggestion: ask the user whether they'd like you to wait for the call`);
55
- console.log(`to finish and report back, e.g.`);
56
- console.log(``);
57
- console.log(` "${followUpQuestion}"`);
58
- console.log(``);
59
- console.log(`If yes → run the wait-for above and report when it returns.`);
60
- console.log(`If no → you're done; they can run the wait-for themselves later.`);
61
- }
62
- return 0;
63
- }
64
- function fail(json, code, message, extra) {
65
- if (json)
66
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
67
- else
68
- console.error(message);
69
56
  }
@@ -1,70 +1,4 @@
1
- import { readPendingSignup, readAuth } from "../lib/state.js";
2
- import { apiGet, baseUrl, pingBackend } from "../lib/api.js";
3
- import { supervisorStatus, lastEventAtFromLog } from "../lib/supervisor/index.js";
4
- import { paths } from "../lib/paths.js";
5
- import { VERSION } from "../lib/version.js";
6
- const OTP_EXPIRY_MS = 10 * 60 * 1000;
7
- async function buildReport() {
8
- const ping = await pingBackend();
9
- const auth = readAuth();
10
- const pending = readPendingSignup();
11
- let keyValid = null;
12
- if (auth?.apiKey) {
13
- const res = await apiGet("/api/v1/account", auth.apiKey);
14
- keyValid = res.ok;
15
- }
16
- const pendingAgeMs = pending ? Date.now() - Date.parse(pending.createdAt) : null;
17
- const pendingExpired = pendingAgeMs == null ? null : pendingAgeMs > OTP_EXPIRY_MS;
18
- let listenState = { installed: false, running: false, lastEventAt: null };
19
- try {
20
- const s = supervisorStatus();
21
- listenState = {
22
- installed: s.installed,
23
- running: s.running,
24
- lastEventAt: lastEventAtFromLog(paths().listenLog),
25
- };
26
- }
27
- catch {
28
- // unsupported platform — leave defaults
29
- }
30
- let nextStep;
31
- if (!auth) {
32
- if (pending && pendingExpired === false)
33
- nextStep = "onboard";
34
- else if (pending && pendingExpired)
35
- nextStep = "resend_otp";
36
- else
37
- nextStep = "signup";
38
- }
39
- else if (keyValid === false) {
40
- nextStep = "signup";
41
- }
42
- else if (!listenState.installed || !listenState.running) {
43
- nextStep = "install_listen";
44
- }
45
- else {
46
- nextStep = "ready";
47
- }
48
- return {
49
- cli: { version: VERSION, node: process.versions.node },
50
- backend: { url: baseUrl(), reachable: ping.reachable, latencyMs: ping.latencyMs },
51
- auth: {
52
- signedIn: Boolean(auth),
53
- email: auth?.email ?? null,
54
- accountId: auth?.accountId ?? null,
55
- apiKeyPresent: Boolean(auth?.apiKey),
56
- apiKeyFingerprint: auth?.apiKey ? auth.apiKey.slice(-4) : null,
57
- keyValid,
58
- },
59
- pendingOtp: {
60
- verificationId: pending?.verificationId ?? null,
61
- ageSeconds: pendingAgeMs == null ? null : Math.round(pendingAgeMs / 1000),
62
- expired: pendingExpired,
63
- },
64
- listen: listenState,
65
- nextStep,
66
- };
67
- }
1
+ import { accountStatus } from "../lib/ops/account.js";
68
2
  function humanRender(r) {
69
3
  const lines = [];
70
4
  lines.push(`dial ${r.cli.version} (node ${r.cli.node})`);
@@ -87,7 +21,7 @@ function humanRender(r) {
87
21
  return lines.join("\n");
88
22
  }
89
23
  export async function runDoctor(opts) {
90
- const report = await buildReport();
24
+ const report = await accountStatus();
91
25
  if (opts.json)
92
26
  console.log(JSON.stringify(report, null, 2));
93
27
  else
@@ -0,0 +1,17 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { buildServer } from "../mcp/server.js";
3
+ /**
4
+ * Run the local stdio MCP server. JSON-RPC travels over stdout; the pino logger is
5
+ * already pinned to stderr (see lib/log.ts), and ops/tools never write to stdout, so
6
+ * the protocol stream stays clean. Stays alive until the client disconnects / stdin closes.
7
+ */
8
+ export async function runMcp() {
9
+ const server = buildServer();
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ await new Promise((resolve) => {
13
+ transport.onclose = () => resolve();
14
+ process.stdin.on("close", resolve);
15
+ });
16
+ return 0;
17
+ }
@@ -1,42 +1,25 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiGet } from "../../lib/api.js";
1
+ import { listMessages } from "../../lib/ops/messages.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runMessageList(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
- const params = new URLSearchParams();
10
- if (opts.numberId)
11
- params.set("numberId", opts.numberId);
12
- if (opts.direction)
13
- params.set("direction", opts.direction);
14
- if (opts.since)
15
- params.set("since", opts.since);
16
- const qs = params.toString();
17
- const path = qs ? `/api/v1/messages?${qs}` : "/api/v1/messages";
18
- const res = await apiGet(path, auth.apiKey);
19
- if (!res.ok) {
20
- fail(opts.json, "list_failed", res.error, { status: res.status });
21
- return 2;
22
- }
23
- const messages = res.data.messages ?? [];
24
- if (opts.json) {
25
- console.log(JSON.stringify({ ok: true, messages }));
5
+ try {
6
+ const messages = await listMessages({ numberId: opts.numberId, direction: opts.direction, since: opts.since });
7
+ if (opts.json) {
8
+ console.log(JSON.stringify({ ok: true, messages }));
9
+ return 0;
10
+ }
11
+ if (messages.length === 0) {
12
+ console.log("no messages.");
13
+ return 0;
14
+ }
15
+ for (const m of messages) {
16
+ console.log(`${m.createdAt} ${(m.direction ?? "").padEnd(8)} ${m.from} -> ${m.to} ${m.body}`);
17
+ }
26
18
  return 0;
27
19
  }
28
- if (messages.length === 0) {
29
- console.log("no messages.");
30
- return 0;
20
+ catch (e) {
21
+ if (isDialError(e))
22
+ return printDialError(opts.json, e);
23
+ throw e;
31
24
  }
32
- for (const m of messages) {
33
- console.log(`${m.createdAt} ${m.direction.padEnd(8)} ${m.from} -> ${m.to} ${m.body}`);
34
- }
35
- return 0;
36
- }
37
- function fail(json, code, message, extra) {
38
- if (json)
39
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
40
- else
41
- console.error(message);
42
25
  }
@@ -1,39 +1,26 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiPost } from "../../lib/api.js";
1
+ import { sendMessage } from "../../lib/ops/messages.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runMessageSend(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;
5
+ try {
6
+ const m = await sendMessage({ to: opts.to, body: opts.body, fromNumberId: opts.fromNumberId });
7
+ if (opts.json) {
8
+ console.log(JSON.stringify({ ok: true, message: m }));
9
+ }
10
+ else {
11
+ console.log(`sent.`);
12
+ console.log(` channel: ${m.channel}`);
13
+ console.log(` from: ${m.from}`);
14
+ console.log(` to: ${m.to}`);
15
+ console.log(` body: ${m.body}`);
16
+ console.log(` status: ${m.status}`);
17
+ console.log(` sid: ${m.sid}`);
18
+ }
19
+ return 0;
8
20
  }
9
- const fromNumberId = opts.fromNumberId ?? auth.phoneNumberId;
10
- if (!fromNumberId) {
11
- fail(opts.json, "no_from_number", "No default phoneNumberId in auth. Pass --from-number-id <id>.");
12
- return 1;
21
+ catch (e) {
22
+ if (isDialError(e))
23
+ return printDialError(opts.json, e);
24
+ throw e;
13
25
  }
14
- const res = await apiPost("/api/v1/messages", { to: opts.to, body: opts.body, channel: "sms", fromNumberId }, auth.apiKey);
15
- if (!res.ok) {
16
- fail(opts.json, "send_failed", res.error, { status: res.status });
17
- return 2;
18
- }
19
- const m = res.data.message;
20
- if (opts.json) {
21
- console.log(JSON.stringify({ ok: true, message: m }));
22
- }
23
- else {
24
- console.log(`sent.`);
25
- console.log(` channel: ${m.channel}`);
26
- console.log(` from: ${m.from}`);
27
- console.log(` to: ${m.to}`);
28
- console.log(` body: ${m.body}`);
29
- console.log(` status: ${m.status}`);
30
- console.log(` sid: ${m.sid}`);
31
- }
32
- return 0;
33
- }
34
- function fail(json, code, message, extra) {
35
- if (json)
36
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
37
- else
38
- console.error(message);
39
26
  }
@@ -1,34 +1,26 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiGet } from "../../lib/api.js";
1
+ import { listNumbers } from "../../lib/ops/numbers.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runNumberList(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
- const res = await apiGet("/api/v1/numbers", auth.apiKey);
10
- if (!res.ok) {
11
- fail(opts.json, "list_failed", res.error, { status: res.status });
12
- return 2;
13
- }
14
- const numbers = res.data.numbers ?? [];
15
- if (opts.json) {
16
- console.log(JSON.stringify({ ok: true, numbers, defaultNumberId: auth.phoneNumberId ?? null }));
5
+ try {
6
+ const { numbers, defaultNumberId } = await listNumbers();
7
+ if (opts.json) {
8
+ console.log(JSON.stringify({ ok: true, numbers, defaultNumberId }));
9
+ return 0;
10
+ }
11
+ if (numbers.length === 0) {
12
+ console.log("no phone numbers. provision one with `dial number purchase`.");
13
+ return 0;
14
+ }
15
+ for (const n of numbers) {
16
+ const tag = n.id === defaultNumberId ? " (default)" : "";
17
+ console.log(`${n.number} id=${n.id} ${n.country}${tag}`);
18
+ }
17
19
  return 0;
18
20
  }
19
- if (numbers.length === 0) {
20
- console.log("no phone numbers. provision one with `dial number purchase`.");
21
- return 0;
21
+ catch (e) {
22
+ if (isDialError(e))
23
+ return printDialError(opts.json, e);
24
+ throw e;
22
25
  }
23
- for (const n of numbers) {
24
- const tag = n.id === auth.phoneNumberId ? " (default)" : "";
25
- console.log(`${n.number} id=${n.id} ${n.country}${tag}`);
26
- }
27
- return 0;
28
- }
29
- function fail(json, code, message, extra) {
30
- if (json)
31
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
32
- else
33
- console.error(message);
34
26
  }
@@ -1,36 +1,27 @@
1
- import { readAuth } from "../../lib/state.js";
2
- import { apiPost } from "../../lib/api.js";
1
+ import { purchaseNumber } from "../../lib/ops/numbers.js";
2
+ import { isDialError } from "../../lib/ops/errors.js";
3
+ import { printDialError } from "../../lib/cli-error.js";
3
4
  export async function runNumberPurchase(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;
5
+ try {
6
+ const n = await purchaseNumber({
7
+ inboundInstruction: opts.inboundInstruction,
8
+ country: opts.country,
9
+ areaCode: opts.areaCode,
10
+ });
11
+ if (opts.json) {
12
+ console.log(JSON.stringify({ ok: true, number: n }));
13
+ }
14
+ else {
15
+ console.log(`purchased.`);
16
+ console.log(` number: ${n.number}`);
17
+ console.log(` id: ${n.id}`);
18
+ console.log(` country: ${n.country}`);
19
+ }
20
+ return 0;
8
21
  }
9
- const body = { inboundInstruction: opts.inboundInstruction };
10
- if (opts.country)
11
- body.country = opts.country;
12
- if (opts.areaCode)
13
- body.areaCode = opts.areaCode;
14
- const res = await apiPost("/api/v1/numbers", body, auth.apiKey);
15
- if (!res.ok) {
16
- fail(opts.json, "purchase_failed", res.error, { status: res.status });
17
- return 2;
22
+ catch (e) {
23
+ if (isDialError(e))
24
+ return printDialError(opts.json, e);
25
+ throw e;
18
26
  }
19
- const n = res.data.number;
20
- if (opts.json) {
21
- console.log(JSON.stringify({ ok: true, number: n }));
22
- }
23
- else {
24
- console.log(`purchased.`);
25
- console.log(` number: ${n.number}`);
26
- console.log(` id: ${n.id}`);
27
- console.log(` country: ${n.country}`);
28
- }
29
- return 0;
30
- }
31
- function fail(json, code, message, extra) {
32
- if (json)
33
- console.log(JSON.stringify({ ok: false, code, message, ...extra }));
34
- else
35
- console.error(message);
36
27
  }