@getdial/cli 0.27.0 → 0.28.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/dist/cli.js CHANGED
@@ -152,10 +152,15 @@ const message = program
152
152
  .option("--body <text>", "message body")
153
153
  .option("--from-number-id <id>", "phoneNumberId to send from (defaults to onboard's number)")
154
154
  .option("--media <path-or-url>", "media attachment: local file path (uploaded) or public http(s) URL (repeatable, max 10)", (v, prev = []) => [...prev, v], [])
155
+ .option("--force-audio-file", "send an audio attachment as a regular file attachment instead of an iMessage voice message")
155
156
  .option("--json", "machine-readable output")
156
157
  .action(async (opts) => {
157
- if (!opts.to || !opts.body) {
158
- console.error("error: --to and --body are required to send a message. Use `dial message list` to list, or `dial message --help` for usage.");
158
+ if (!opts.to) {
159
+ console.error("error: --to is required to send a message. Use `dial message list` to list, or `dial message --help` for usage.");
160
+ process.exit(2);
161
+ }
162
+ if (!opts.body && (opts.media ?? []).length === 0) {
163
+ console.error("error: provide --body, --media, or both — a message needs text or an attachment.");
159
164
  process.exit(2);
160
165
  }
161
166
  process.exit(await runMessageSend({
@@ -163,6 +168,7 @@ const message = program
163
168
  body: opts.body,
164
169
  fromNumberId: opts.fromNumberId,
165
170
  media: opts.media,
171
+ forceAudioFile: !!opts.forceAudioFile,
166
172
  json: !!opts.json,
167
173
  }));
168
174
  });
@@ -3,7 +3,13 @@ import { isDialError } from "../../lib/ops/errors.js";
3
3
  import { printDialError } from "../../lib/cli-error.js";
4
4
  export async function runMessageSend(opts) {
5
5
  try {
6
- const m = await sendMessage({ to: opts.to, body: opts.body, fromNumberId: opts.fromNumberId, media: opts.media });
6
+ const m = await sendMessage({
7
+ to: opts.to,
8
+ body: opts.body,
9
+ fromNumberId: opts.fromNumberId,
10
+ media: opts.media,
11
+ forceAudioFile: opts.forceAudioFile,
12
+ });
7
13
  if (opts.json) {
8
14
  console.log(JSON.stringify({ ok: true, message: m }));
9
15
  }
@@ -58,13 +58,22 @@ export async function sendMessage(opts) {
58
58
  const hasFiles = media.some((m) => !isHttpUrl(m));
59
59
  let res;
60
60
  if (!hasFiles) {
61
- res = await apiPost("/api/v1/messages", { to: opts.to, body: opts.body, fromNumberId, ...(media.length ? { mediaUrls: media } : {}) }, auth.apiKey);
61
+ res = await apiPost("/api/v1/messages", {
62
+ to: opts.to,
63
+ ...(opts.body ? { body: opts.body } : {}),
64
+ fromNumberId,
65
+ ...(media.length ? { mediaUrls: media } : {}),
66
+ ...(opts.forceAudioFile ? { forceAudioFile: true } : {}),
67
+ }, auth.apiKey);
62
68
  }
63
69
  else {
64
70
  const form = new ApiFormData();
65
71
  form.set("to", opts.to);
66
- form.set("body", opts.body);
72
+ if (opts.body)
73
+ form.set("body", opts.body);
67
74
  form.set("fromNumberId", fromNumberId);
75
+ if (opts.forceAudioFile)
76
+ form.set("forceAudioFile", "true");
68
77
  for (const item of media) {
69
78
  if (isHttpUrl(item)) {
70
79
  form.append("mediaUrls", item);
@@ -1,17 +1,26 @@
1
1
  import { z } from "zod";
2
2
  import { jsonResult } from "../result.js";
3
- import { sendMessage } from "../../lib/ops/messages.js";
3
+ import { sendMessage, MAX_MEDIA_ITEMS } from "../../lib/ops/messages.js";
4
4
  import { messageSchema } from "../schemas.js";
5
5
  const inputSchema = {
6
6
  to: z.string().min(7).describe("Destination phone number, E.164 (e.g. +14155550123)"),
7
- body: z.string().min(1).describe("Message body"),
7
+ body: z.string().optional().describe("Message body; optional when mediaUrls is given (media-only send)"),
8
8
  fromNumberId: z.string().optional().describe("Number id to send from; defaults to your primary number"),
9
+ mediaUrls: z
10
+ .array(z.string().url())
11
+ .max(MAX_MEDIA_ITEMS)
12
+ .optional()
13
+ .describe("Publicly reachable http(s) URLs of media to attach (MMS); Dial mirrors and re-hosts them"),
14
+ forceAudioFile: z
15
+ .boolean()
16
+ .optional()
17
+ .describe("Send an audio attachment as a regular file attachment instead of an iMessage voice message. No effect on standard numbers or non-audio media."),
9
18
  };
10
19
  export const sendMessageTool = {
11
20
  name: "send_message",
12
21
  config: {
13
- title: "Send SMS",
14
- description: "Send an SMS from one of your Dial numbers.",
22
+ title: "Send message",
23
+ description: "Send a message from one of your Dial numbers, optionally with media attachments (MMS). On an iMessage number, a single audio attachment is delivered as a voice message unless forceAudioFile is true.",
15
24
  inputSchema,
16
25
  outputSchema: { message: messageSchema },
17
26
  annotations: { openWorldHint: true },
@@ -21,6 +30,8 @@ export const sendMessageTool = {
21
30
  to: args.to,
22
31
  body: args.body,
23
32
  fromNumberId: args.fromNumberId,
33
+ media: args.mediaUrls,
34
+ forceAudioFile: args.forceAudioFile,
24
35
  }),
25
36
  }),
26
37
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdial/cli",
3
- "version": "0.27.0",
3
+ "version": "0.28.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