@getdial/cli 0.14.0 → 0.15.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,5 +1,7 @@
1
1
  # @getdial/cli
2
2
 
3
+ [![smithery badge](https://smithery.ai/badge/getdial-ai/dial)](https://smithery.ai/servers/getdial-ai/dial)
4
+
3
5
  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
6
 
5
7
  ## Installation
@@ -1,5 +1,15 @@
1
+ /**
2
+ * JSON tool response. Rendered as text, and mirrored into `structuredContent` (the
3
+ * JSON-coerced form) for clients that validate against the tool's `outputSchema`.
4
+ */
1
5
  export function jsonResult(data) {
2
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
6
+ const text = JSON.stringify(data, null, 2);
7
+ const structured = JSON.parse(text);
8
+ const result = { content: [{ type: "text", text }] };
9
+ if (structured !== null && typeof structured === "object" && !Array.isArray(structured)) {
10
+ result.structuredContent = structured;
11
+ }
12
+ return result;
3
13
  }
4
14
  export function errorResult(message) {
5
15
  return { content: [{ type: "text", text: message }], isError: true };
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Reusable Zod output schemas for tool results. Inner entity objects are
4
+ * `.passthrough()` so extra fields aren't stripped from `structuredContent`.
5
+ * Mirrors the remote server's schemas so the two surfaces stay aligned.
6
+ */
7
+ export const phoneNumberSchema = z
8
+ .object({
9
+ id: z.string().describe("Phone number id (pn_…)"),
10
+ number: z.string().describe("E.164 phone number"),
11
+ country: z.string().optional(),
12
+ inboundInstruction: z.string().nullable().optional(),
13
+ })
14
+ .passthrough();
15
+ export const messageSchema = z
16
+ .object({
17
+ id: z.string(),
18
+ from: z.string(),
19
+ to: z.string(),
20
+ body: z.string(),
21
+ channel: z.string().optional(),
22
+ direction: z.string().optional(),
23
+ status: z.string(),
24
+ createdAt: z.string().optional(),
25
+ })
26
+ .passthrough();
27
+ export const callSchema = z
28
+ .object({
29
+ id: z.string(),
30
+ from: z.string(),
31
+ to: z.string(),
32
+ direction: z.string().optional(),
33
+ status: z.string(),
34
+ duration: z.number().optional(),
35
+ transcript: z.string().nullable().optional(),
36
+ instruction: z.string().nullable().optional(),
37
+ createdAt: z.string().optional(),
38
+ })
39
+ .passthrough();
40
+ export const eventSchema = z
41
+ .object({})
42
+ .passthrough()
43
+ .describe("The matched account event; shape varies by event type");
44
+ export const localTargetSchema = z
45
+ .object({
46
+ kind: z.string().describe('"url" or "cmd"'),
47
+ id: z.string().describe("Target id (URL for url targets, path for cmd targets)"),
48
+ target: z.object({}).passthrough(),
49
+ })
50
+ .passthrough();
@@ -12,7 +12,12 @@ export const addCommandTargetTool = {
12
12
  title: "Add Command Fan-out Target",
13
13
  description: "Register an executable the local listen daemon runs once per event (event JSON as the final arg).",
14
14
  inputSchema,
15
- annotations: {},
15
+ outputSchema: {
16
+ added: z.boolean().describe("False if the target was already registered"),
17
+ path: z.string(),
18
+ args: z.array(z.string()),
19
+ },
20
+ annotations: { openWorldHint: false },
16
21
  },
17
22
  run: async (args) => jsonResult(addCommandTarget({
18
23
  path: args.path,
@@ -14,7 +14,11 @@ export const addUrlTargetTool = {
14
14
  title: "Add URL Fan-out Target",
15
15
  description: "Register a loopback HTTP endpoint the local listen daemon delivers each event to.",
16
16
  inputSchema,
17
- annotations: {},
17
+ outputSchema: {
18
+ added: z.boolean().describe("False if the target was already registered"),
19
+ url: z.string(),
20
+ },
21
+ annotations: { openWorldHint: false },
18
22
  },
19
23
  run: async (args) => jsonResult(addUrlTarget({
20
24
  url: args.url,
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { accountStatus } from "../../lib/ops/account.js";
3
4
  export const getAccountStatusTool = {
@@ -7,6 +8,14 @@ export const getAccountStatusTool = {
7
8
  description: "Report local Dial setup state: CLI version, backend reachability, sign-in/key validity, " +
8
9
  "any pending OTP, the listen daemon state, and the recommended next step.",
9
10
  inputSchema: {},
11
+ outputSchema: {
12
+ cli: z.object({}).passthrough().describe("CLI and node versions"),
13
+ backend: z.object({}).passthrough().describe("Backend URL and reachability"),
14
+ auth: z.object({}).passthrough().describe("Sign-in and API-key state"),
15
+ pendingOtp: z.object({}).passthrough().describe("Any pending sign-up OTP"),
16
+ listen: z.object({}).passthrough().describe("Listen daemon state"),
17
+ nextStep: z.string().describe("Recommended next step (signup, onboard, install_listen, ready, …)"),
18
+ },
10
19
  annotations: { readOnlyHint: true, openWorldHint: true },
11
20
  },
12
21
  run: async () => jsonResult(await accountStatus()),
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { getCall } from "../../lib/ops/calls.js";
4
+ import { callSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  callId: z.string().min(1).describe("The call id to fetch"),
6
7
  };
@@ -10,7 +11,8 @@ export const getCallTool = {
10
11
  title: "Get Call",
11
12
  description: "Fetch a single call by id — status, duration, and transcript when available.",
12
13
  inputSchema,
14
+ outputSchema: { call: callSchema },
13
15
  annotations: { readOnlyHint: true, openWorldHint: true },
14
16
  },
15
- run: async (args) => jsonResult(await getCall(args.callId)),
17
+ run: async (args) => jsonResult({ call: await getCall(args.callId) }),
16
18
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { listCalls } from "../../lib/ops/calls.js";
4
+ import { callSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  numberId: z.string().optional().describe("Filter to a single phone number id"),
6
7
  direction: z.enum(["inbound", "outbound"]).optional().describe("Filter by direction"),
@@ -12,11 +13,14 @@ export const listCallsTool = {
12
13
  title: "List Calls",
13
14
  description: "List recent calls on your account, newest first.",
14
15
  inputSchema,
16
+ outputSchema: { calls: z.array(callSchema) },
15
17
  annotations: { readOnlyHint: true, openWorldHint: true },
16
18
  },
17
- run: async (args) => jsonResult(await listCalls({
18
- numberId: args.numberId,
19
- direction: args.direction,
20
- since: args.since,
21
- })),
19
+ run: async (args) => jsonResult({
20
+ calls: await listCalls({
21
+ numberId: args.numberId,
22
+ direction: args.direction,
23
+ since: args.since,
24
+ }),
25
+ }),
22
26
  };
@@ -1,12 +1,15 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { listLocalTargets } from "../../lib/ops/local-targets.js";
4
+ import { localTargetSchema } from "../schemas.js";
3
5
  export const listLocalTargetsTool = {
4
6
  name: "list_local_targets",
5
7
  config: {
6
8
  title: "List Fan-out Targets",
7
9
  description: "List the local fan-out targets the listen daemon currently delivers events to.",
8
10
  inputSchema: {},
11
+ outputSchema: { targets: z.array(localTargetSchema) },
9
12
  annotations: { readOnlyHint: true },
10
13
  },
11
- run: async () => jsonResult(listLocalTargets()),
14
+ run: async () => jsonResult({ targets: listLocalTargets() }),
12
15
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { listMessages } from "../../lib/ops/messages.js";
4
+ import { messageSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  numberId: z.string().optional().describe("Filter to a single phone number id"),
6
7
  direction: z.enum(["inbound", "outbound"]).optional().describe("Filter by direction"),
@@ -12,11 +13,14 @@ export const listMessagesTool = {
12
13
  title: "List Messages",
13
14
  description: "List recent messages on your account, newest first.",
14
15
  inputSchema,
16
+ outputSchema: { messages: z.array(messageSchema) },
15
17
  annotations: { readOnlyHint: true, openWorldHint: true },
16
18
  },
17
- run: async (args) => jsonResult(await listMessages({
18
- numberId: args.numberId,
19
- direction: args.direction,
20
- since: args.since,
21
- })),
19
+ run: async (args) => jsonResult({
20
+ messages: await listMessages({
21
+ numberId: args.numberId,
22
+ direction: args.direction,
23
+ since: args.since,
24
+ }),
25
+ }),
22
26
  };
@@ -1,11 +1,17 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { listNumbers } from "../../lib/ops/numbers.js";
4
+ import { phoneNumberSchema } from "../schemas.js";
3
5
  export const listNumbersTool = {
4
6
  name: "list_numbers",
5
7
  config: {
6
8
  title: "List Phone Numbers",
7
9
  description: "List the phone numbers on your Dial account, with the default number id.",
8
10
  inputSchema: {},
11
+ outputSchema: {
12
+ numbers: z.array(phoneNumberSchema),
13
+ defaultNumberId: z.string().nullable().describe("Your primary number id, or null"),
14
+ },
9
15
  annotations: { readOnlyHint: true, openWorldHint: true },
10
16
  },
11
17
  run: async () => jsonResult(await listNumbers()),
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { listenInstall } from "../../lib/ops/listen.js";
3
4
  export const listenInstallTool = {
@@ -7,6 +8,11 @@ export const listenInstallTool = {
7
8
  description: "Install the background event daemon (launchd on macOS, systemd --user on Linux) so inbound " +
8
9
  "SMS and call-ended events are delivered to this machine in real time.",
9
10
  inputSchema: {},
11
+ outputSchema: {
12
+ changed: z.boolean().describe("False if the daemon was already installed"),
13
+ warnings: z.array(z.string()),
14
+ unitPath: z.string().describe("Path to the installed launchd/systemd unit"),
15
+ },
10
16
  annotations: {},
11
17
  },
12
18
  run: async () => jsonResult(listenInstall()),
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { listenStatus } from "../../lib/ops/listen.js";
3
4
  export const listenStatusTool = {
@@ -6,6 +7,14 @@ export const listenStatusTool = {
6
7
  title: "Listen Daemon Status",
7
8
  description: "Report the background event daemon's state (installed/running/pid) and the last few events.",
8
9
  inputSchema: {},
10
+ outputSchema: {
11
+ installed: z.boolean(),
12
+ running: z.boolean(),
13
+ pid: z.number().nullable(),
14
+ unitPath: z.string(),
15
+ lastEventAt: z.string().nullable(),
16
+ lastEvents: z.array(z.unknown()).describe("Up to the last 5 events from the local log"),
17
+ },
9
18
  annotations: { readOnlyHint: true },
10
19
  },
11
20
  run: async () => jsonResult(listenStatus()),
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { jsonResult } from "../result.js";
2
3
  import { listenUninstall } from "../../lib/ops/listen.js";
3
4
  export const listenUninstallTool = {
@@ -6,6 +7,7 @@ export const listenUninstallTool = {
6
7
  title: "Uninstall Listen Daemon",
7
8
  description: "Stop and remove the background event daemon from this machine.",
8
9
  inputSchema: {},
10
+ outputSchema: { ok: z.boolean() },
9
11
  annotations: { destructiveHint: true },
10
12
  },
11
13
  run: async () => jsonResult(listenUninstall()),
@@ -14,6 +14,16 @@ export const onboardTool = {
14
14
  description: "Verify the sign-up OTP and finish onboarding: saves the API key locally and optionally installs " +
15
15
  "the Dial skill into named agents. Returns the account summary (the raw API key is never returned).",
16
16
  inputSchema,
17
+ outputSchema: {
18
+ apiKeyFingerprint: z.string().describe("Last 4 chars of the saved API key"),
19
+ apiKeyPath: z.string().describe("Where the key was saved"),
20
+ accountId: z.string(),
21
+ phoneNumber: z.string().nullable(),
22
+ phoneNumberId: z.string().nullable(),
23
+ skills: z.array(z.object({}).passthrough()).describe("Per-agent skill install results"),
24
+ supervisor: z.object({}).passthrough().describe("Listen daemon availability on this machine"),
25
+ listenAvailable: z.boolean(),
26
+ },
17
27
  annotations: { openWorldHint: true },
18
28
  },
19
29
  run: async (args) => {
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { placeCall } from "../../lib/ops/calls.js";
4
+ import { callSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  to: z.string().min(7).describe("Destination phone number, E.164 (e.g. +14155550123)"),
6
7
  outboundInstruction: z.string().min(1).describe("System prompt for the AI voice agent on this call"),
@@ -14,6 +15,7 @@ export const placeCallTool = {
14
15
  description: "Place an outbound voice call handled by an AI agent. The call runs asynchronously — " +
15
16
  "use wait_for_event to block until it ends, then get_call for the transcript.",
16
17
  inputSchema,
18
+ outputSchema: { call: callSchema, hint: z.string().describe("Next-step guidance for tracking the call") },
17
19
  annotations: { openWorldHint: true },
18
20
  },
19
21
  run: async (args) => {
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { purchaseNumber } from "../../lib/ops/numbers.js";
4
+ import { phoneNumberSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  inboundInstruction: z.string().min(1).describe("System prompt for inbound calls to this number"),
6
7
  country: z.string().optional().describe("ISO-3166-1 alpha-2 country code (defaults to US server-side)"),
@@ -12,11 +13,14 @@ export const purchaseNumberTool = {
12
13
  title: "Purchase Phone Number",
13
14
  description: "Purchase an additional phone number. This spends money on the account.",
14
15
  inputSchema,
16
+ outputSchema: { number: phoneNumberSchema },
15
17
  annotations: { destructiveHint: true, openWorldHint: true },
16
18
  },
17
- run: async (args) => jsonResult(await purchaseNumber({
18
- inboundInstruction: args.inboundInstruction,
19
- country: args.country,
20
- areaCode: args.areaCode,
21
- })),
19
+ run: async (args) => jsonResult({
20
+ number: await purchaseNumber({
21
+ inboundInstruction: args.inboundInstruction,
22
+ country: args.country,
23
+ areaCode: args.areaCode,
24
+ }),
25
+ }),
22
26
  };
@@ -10,6 +10,10 @@ export const removeLocalTargetTool = {
10
10
  title: "Remove Fan-out Target",
11
11
  description: "Unregister a local fan-out target by id. Returns removed:false if no such target.",
12
12
  inputSchema,
13
+ outputSchema: {
14
+ removed: z.boolean().describe("False if no target matched the id"),
15
+ id: z.string(),
16
+ },
13
17
  annotations: { destructiveHint: true },
14
18
  },
15
19
  run: async (args) => jsonResult(removeLocalTarget(args.id)),
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { sendMessage } from "../../lib/ops/messages.js";
4
+ import { messageSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  to: z.string().min(7).describe("Destination phone number, E.164 (e.g. +14155550123)"),
6
7
  body: z.string().min(1).describe("Message body"),
@@ -12,11 +13,14 @@ export const sendMessageTool = {
12
13
  title: "Send SMS",
13
14
  description: "Send an SMS from one of your Dial numbers.",
14
15
  inputSchema,
16
+ outputSchema: { message: messageSchema },
15
17
  annotations: { openWorldHint: true },
16
18
  },
17
- run: async (args) => jsonResult(await sendMessage({
18
- to: args.to,
19
- body: args.body,
20
- fromNumberId: args.fromNumberId,
21
- })),
19
+ run: async (args) => jsonResult({
20
+ message: await sendMessage({
21
+ to: args.to,
22
+ body: args.body,
23
+ fromNumberId: args.fromNumberId,
24
+ }),
25
+ }),
22
26
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { setNumberProperties } from "../../lib/ops/numbers.js";
4
+ import { phoneNumberSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  number: z.string().min(7).describe("The E.164 phone number to update (e.g. +14155550123)"),
6
7
  inboundInstruction: z.string().min(1).describe("New system prompt for inbound calls to this number"),
@@ -11,10 +12,13 @@ export const setNumberPropertiesTool = {
11
12
  title: "Set Number Properties",
12
13
  description: "Update a phone number's inbound instruction (the system prompt for inbound calls).",
13
14
  inputSchema,
15
+ outputSchema: { number: phoneNumberSchema },
14
16
  annotations: { openWorldHint: true },
15
17
  },
16
- run: async (args) => jsonResult(await setNumberProperties({
17
- number: args.number,
18
- inboundInstruction: args.inboundInstruction,
19
- })),
18
+ run: async (args) => jsonResult({
19
+ number: await setNumberProperties({
20
+ number: args.number,
21
+ inboundInstruction: args.inboundInstruction,
22
+ }),
23
+ }),
20
24
  };
@@ -12,6 +12,10 @@ export const signUpTool = {
12
12
  description: "Request an email OTP for a Dial account. The code is emailed; finish with the onboard tool. " +
13
13
  "Stores the pending verification locally.",
14
14
  inputSchema,
15
+ outputSchema: {
16
+ verificationId: z.string().describe("Pending verification id (also stored locally)"),
17
+ email: z.string(),
18
+ },
15
19
  annotations: { openWorldHint: true },
16
20
  },
17
21
  run: async (args) => jsonResult(await signup({ email: args.email, force: args.force })),
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
3
  import { waitForEvent } from "../../lib/ops/events.js";
4
+ import { eventSchema } from "../schemas.js";
4
5
  const inputSchema = {
5
6
  eventType: z.string().min(1).describe('Event type to wait for (e.g. "call.ended", "message.received")'),
6
7
  field: z.array(z.string()).optional().describe('Exact-match filters, each "name=value" (e.g. "callId=abc")'),
@@ -14,6 +15,12 @@ export const waitForEventTool = {
14
15
  description: "Block until a matching account event arrives. Reads the local listen log when the daemon " +
15
16
  "is running, otherwise long-polls the REST API. Returns the event, or a timeout result.",
16
17
  inputSchema,
18
+ outputSchema: {
19
+ source: z.string().nullable().describe('"log", "api", or null when nothing matched'),
20
+ timedOut: z.boolean(),
21
+ event: eventSchema.nullable(),
22
+ line: z.string().nullable().describe("Raw matched event line, when available"),
23
+ },
17
24
  annotations: { readOnlyHint: true, openWorldHint: true },
18
25
  },
19
26
  run: async (args) => jsonResult(await waitForEvent({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdial/cli",
3
- "version": "0.14.0",
3
+ "version": "0.15.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