@openclaw/voice-call 2026.5.28-beta.1 → 2026.5.28-beta.2

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.
@@ -1,6 +1,6 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as getHeader } from "./runtime-entry-lBiyw_6t.js";
3
+ import { a as getHeader } from "./runtime-entry-DwBgs2Sq.js";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { definePluginEntry, sleep } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { i as resolveVoiceCallConfig, s as validateProviderConfig } from "./config-U-rgixyY.js";
4
- import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-lBiyw_6t.js";
4
+ import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-DwBgs2Sq.js";
5
5
  import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-CokN3Zzr.js";
6
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
7
  import { ErrorCodes, callGatewayFromCli, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
@@ -12,12 +12,12 @@ import os from "node:os";
12
12
  import path from "node:path";
13
13
  import { randomUUID } from "node:crypto";
14
14
  import { format } from "node:util";
15
+ import { MAX_TCP_PORT, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
15
16
  //#region extensions/voice-call/src/cli.ts
16
17
  const VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS = 5e3;
17
18
  const VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS = 3e4;
18
19
  const VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS = 1e4;
19
20
  const VOICE_CALL_GATEWAY_POLL_INTERVAL_MS = 1e3;
20
- const DECIMAL_INTEGER_RE = /^\d+$/;
21
21
  const voiceCallCliDeps = { callGatewayFromCli };
22
22
  function writeStdoutLine(...values) {
23
23
  process.stdout.write(`${format(...values)}\n`);
@@ -27,9 +27,8 @@ function writeStdoutJson(value) {
27
27
  }
28
28
  function parseVoiceCallIntOption(raw, optionName, opts) {
29
29
  const min = opts?.min ?? 0;
30
- const value = raw?.trim() ?? "";
31
- const parsed = DECIMAL_INTEGER_RE.test(value) ? Number(value) : NaN;
32
- if (!Number.isInteger(parsed) || parsed < min) throw new Error(`Invalid numeric value for ${optionName}: ${raw ?? ""}`);
30
+ const parsed = parseStrictNonNegativeInteger(raw?.trim() ?? "");
31
+ if (parsed === void 0 || parsed < min || opts?.max !== void 0 && parsed > opts.max) throw new Error(`Invalid numeric value for ${optionName}: ${raw ?? ""}`);
33
32
  return parsed;
34
33
  }
35
34
  function isGatewayUnavailableForLocalFallback(err) {
@@ -459,7 +458,10 @@ function registerVoiceCallCli(params) {
459
458
  });
460
459
  root.command("expose").description("Enable/disable Tailscale serve/funnel for the webhook").option("--mode <mode>", "off | serve (tailnet) | funnel (public)", "funnel").option("--path <path>", "Tailscale path to expose (recommend matching serve.path)").option("--port <port>", "Local webhook port").option("--serve-path <path>", "Local webhook path").action(async (options) => {
461
460
  const mode = resolveMode(options.mode ?? "funnel");
462
- const servePort = parseVoiceCallIntOption(options.port ?? String(config.serve.port ?? 3334), "--port", { min: 1 });
461
+ const servePort = parseVoiceCallIntOption(options.port ?? String(config.serve.port ?? 3334), "--port", {
462
+ min: 1,
463
+ max: MAX_TCP_PORT
464
+ });
463
465
  const servePath = options.servePath ?? config.serve.path ?? "/voice/webhook";
464
466
  const tsPath = options.path ?? config.tailscale?.path ?? servePath;
465
467
  const localUrl = `http://127.0.0.1:${servePort}`;
@@ -1,5 +1,5 @@
1
- import { a as getHeader, m as escapeXml } from "./runtime-entry-lBiyw_6t.js";
2
- import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BT5XfY80.js";
1
+ import { a as getHeader, m as escapeXml } from "./runtime-entry-DwBgs2Sq.js";
2
+ import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  //#region extensions/voice-call/src/providers/plivo.ts
@@ -154,73 +154,96 @@ function tryParseJson(rawMessage) {
154
154
  } catch {}
155
155
  return null;
156
156
  }
157
+ function readRecordField(record, field) {
158
+ const value = record[field];
159
+ return typeof value === "object" && value !== null ? value : void 0;
160
+ }
157
161
  function normalizeBase64ForCompare(value) {
158
162
  return value.replace(/=+$/u, "").replace(/-/gu, "+").replace(/_/gu, "/");
159
163
  }
160
164
  function isValidBase64Payload(value) {
161
165
  return normalizeBase64ForCompare(Buffer.from(value, "base64").toString("base64")) === normalizeBase64ForCompare(value);
162
166
  }
167
+ function parseMediaFrame(msg) {
168
+ const mediaData = readRecordField(msg, "media");
169
+ const payload = typeof mediaData?.payload === "string" ? mediaData.payload : void 0;
170
+ if (!payload || !isValidBase64Payload(payload)) return { kind: "ignored" };
171
+ return {
172
+ kind: "media",
173
+ payloadBase64: payload,
174
+ timestampMs: parseTimestampMs(mediaData?.timestamp),
175
+ track: typeof mediaData?.track === "string" ? mediaData.track : void 0
176
+ };
177
+ }
178
+ function parseMarkFrame(msg) {
179
+ const markData = readRecordField(msg, "mark");
180
+ return {
181
+ kind: "mark",
182
+ name: typeof markData?.name === "string" ? markData.name : void 0
183
+ };
184
+ }
185
+ function parseCommonInboundFrame(event, msg) {
186
+ if (event === "media") return parseMediaFrame(msg);
187
+ if (event === "mark") return parseMarkFrame(msg);
188
+ if (event === "stop") return { kind: "stop" };
189
+ }
190
+ function parseProviderInboundFrame(rawMessage, parseStartFrame, parseExtraFrame) {
191
+ const msg = tryParseJson(rawMessage);
192
+ if (!msg) return { kind: "ignored" };
193
+ const event = msg.event;
194
+ if (event === "start") return parseStartFrame(msg) ?? { kind: "ignored" };
195
+ return parseCommonInboundFrame(event, msg) ?? parseExtraFrame?.(event, msg) ?? { kind: "ignored" };
196
+ }
197
+ function withOptionalStreamSid(streamSid) {
198
+ return streamSid === void 0 ? {} : { streamSid };
199
+ }
200
+ function serializeMediaFrame(payloadBase64, streamSid) {
201
+ return JSON.stringify({
202
+ event: "media",
203
+ ...withOptionalStreamSid(streamSid),
204
+ media: { payload: payloadBase64 }
205
+ });
206
+ }
207
+ function serializeClearFrame(streamSid) {
208
+ return JSON.stringify({
209
+ event: "clear",
210
+ ...withOptionalStreamSid(streamSid)
211
+ });
212
+ }
213
+ function serializeMarkFrame(name, streamSid) {
214
+ return JSON.stringify({
215
+ event: "mark",
216
+ ...withOptionalStreamSid(streamSid),
217
+ mark: { name }
218
+ });
219
+ }
163
220
  var TwilioStreamFrameAdapter = class {
164
221
  constructor() {
165
222
  this.providerName = "twilio";
166
223
  this.streamSid = "";
167
224
  }
168
225
  parseInbound(rawMessage) {
169
- const msg = tryParseJson(rawMessage);
170
- if (!msg) return { kind: "ignored" };
171
- const event = msg.event;
172
- if (event === "start") {
173
- const startData = typeof msg.start === "object" && msg.start !== null ? msg.start : void 0;
226
+ return parseProviderInboundFrame(rawMessage, (msg) => {
227
+ const startData = readRecordField(msg, "start");
174
228
  const streamSid = typeof startData?.streamSid === "string" ? startData.streamSid : "";
175
229
  const callSid = typeof startData?.callSid === "string" ? startData.callSid : "";
176
- if (!streamSid || !callSid) return { kind: "ignored" };
230
+ if (!streamSid || !callSid) return;
177
231
  this.streamSid = streamSid;
178
232
  return {
179
233
  kind: "start",
180
234
  streamId: streamSid,
181
235
  providerCallId: callSid
182
236
  };
183
- }
184
- if (event === "media") {
185
- const mediaData = typeof msg.media === "object" && msg.media !== null ? msg.media : void 0;
186
- const payload = typeof mediaData?.payload === "string" ? mediaData.payload : void 0;
187
- if (!payload || !isValidBase64Payload(payload)) return { kind: "ignored" };
188
- return {
189
- kind: "media",
190
- payloadBase64: payload,
191
- timestampMs: parseTimestampMs(mediaData?.timestamp),
192
- track: typeof mediaData?.track === "string" ? mediaData.track : void 0
193
- };
194
- }
195
- if (event === "mark") {
196
- const markData = typeof msg.mark === "object" && msg.mark !== null ? msg.mark : void 0;
197
- return {
198
- kind: "mark",
199
- name: typeof markData?.name === "string" ? markData.name : void 0
200
- };
201
- }
202
- if (event === "stop") return { kind: "stop" };
203
- return { kind: "ignored" };
237
+ });
204
238
  }
205
239
  serializeMedia(payloadBase64) {
206
- return JSON.stringify({
207
- event: "media",
208
- streamSid: this.streamSid,
209
- media: { payload: payloadBase64 }
210
- });
240
+ return serializeMediaFrame(payloadBase64, this.streamSid);
211
241
  }
212
242
  serializeClear() {
213
- return JSON.stringify({
214
- event: "clear",
215
- streamSid: this.streamSid
216
- });
243
+ return serializeClearFrame(this.streamSid);
217
244
  }
218
245
  serializeMark(name) {
219
- return JSON.stringify({
220
- event: "mark",
221
- streamSid: this.streamSid,
222
- mark: { name }
223
- });
246
+ return serializeMarkFrame(name, this.streamSid);
224
247
  }
225
248
  };
226
249
  var TelnyxStreamFrameAdapter = class {
@@ -228,64 +251,35 @@ var TelnyxStreamFrameAdapter = class {
228
251
  this.providerName = "telnyx";
229
252
  }
230
253
  parseInbound(rawMessage) {
231
- const msg = tryParseJson(rawMessage);
232
- if (!msg) return { kind: "ignored" };
233
- const event = msg.event;
234
- const topLevelStreamId = typeof msg.stream_id === "string" && msg.stream_id ? msg.stream_id : void 0;
235
- if (event === "start") {
236
- const startData = typeof msg.start === "object" && msg.start !== null ? msg.start : void 0;
254
+ return parseProviderInboundFrame(rawMessage, (msg) => {
255
+ const topLevelStreamId = typeof msg.stream_id === "string" && msg.stream_id ? msg.stream_id : void 0;
256
+ const startData = readRecordField(msg, "start");
237
257
  const providerCallId = typeof startData?.call_control_id === "string" && startData.call_control_id ? startData.call_control_id : void 0;
238
- if (!topLevelStreamId || !providerCallId) return { kind: "ignored" };
258
+ if (!topLevelStreamId || !providerCallId) return;
239
259
  return {
240
260
  kind: "start",
241
261
  streamId: topLevelStreamId,
242
262
  providerCallId
243
263
  };
244
- }
245
- if (event === "media") {
246
- const mediaData = typeof msg.media === "object" && msg.media !== null ? msg.media : void 0;
247
- const payload = typeof mediaData?.payload === "string" ? mediaData.payload : void 0;
248
- if (!payload || !isValidBase64Payload(payload)) return { kind: "ignored" };
249
- return {
250
- kind: "media",
251
- payloadBase64: payload,
252
- timestampMs: parseTimestampMs(mediaData?.timestamp),
253
- track: typeof mediaData?.track === "string" ? mediaData.track : void 0
254
- };
255
- }
256
- if (event === "mark") {
257
- const markData = typeof msg.mark === "object" && msg.mark !== null ? msg.mark : void 0;
258
- return {
259
- kind: "mark",
260
- name: typeof markData?.name === "string" ? markData.name : void 0
261
- };
262
- }
263
- if (event === "stop") return { kind: "stop" };
264
- if (event === "error") {
265
- const errorData = typeof msg.payload === "object" && msg.payload !== null ? msg.payload : void 0;
264
+ }, (event, msg) => {
265
+ if (event !== "error") return;
266
+ const errorData = readRecordField(msg, "payload");
266
267
  return {
267
268
  kind: "error",
268
269
  code: typeof errorData?.code === "string" || typeof errorData?.code === "number" ? String(errorData.code) : void 0,
269
270
  title: typeof errorData?.title === "string" ? errorData.title : void 0,
270
271
  detail: typeof errorData?.detail === "string" ? errorData.detail : void 0
271
272
  };
272
- }
273
- return { kind: "ignored" };
273
+ });
274
274
  }
275
275
  serializeMedia(payloadBase64) {
276
- return JSON.stringify({
277
- event: "media",
278
- media: { payload: payloadBase64 }
279
- });
276
+ return serializeMediaFrame(payloadBase64);
280
277
  }
281
278
  serializeClear() {
282
- return JSON.stringify({ event: "clear" });
279
+ return serializeClearFrame();
283
280
  }
284
281
  serializeMark(name) {
285
- return JSON.stringify({
286
- event: "mark",
287
- mark: { name }
288
- });
282
+ return serializeMarkFrame(name);
289
283
  }
290
284
  };
291
285
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { o as resolveVoiceCallSessionKey } from "./config-U-rgixyY.js";
2
- import { f as resolveVoiceResponseModel } from "./runtime-entry-lBiyw_6t.js";
2
+ import { f as resolveVoiceResponseModel } from "./runtime-entry-DwBgs2Sq.js";
3
3
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  import { applyModelOverrideToSessionEntry } from "openclaw/plugin-sdk/model-session-runtime";
@@ -339,7 +339,7 @@ function findCall(params) {
339
339
  function resolveProviderVoiceSetting(providerConfig) {
340
340
  if (!providerConfig || typeof providerConfig !== "object") return;
341
341
  const candidate = providerConfig;
342
- return normalizeOptionalString(candidate.voice) ?? normalizeOptionalString(candidate.voiceId);
342
+ return normalizeOptionalString(candidate.speakerVoice) ?? normalizeOptionalString(candidate.speakerVoiceId) ?? normalizeOptionalString(candidate.voice) ?? normalizeOptionalString(candidate.voiceId);
343
343
  }
344
344
  function resolvePreferredTtsVoice(config) {
345
345
  const providerId = config.tts?.provider;
@@ -2588,7 +2588,7 @@ function loadRealtimeTranscriptionRuntime() {
2588
2588
  return realtimeTranscriptionRuntimePromise;
2589
2589
  }
2590
2590
  function loadResponseGeneratorModule() {
2591
- responseGeneratorModulePromise ??= import("./response-generator-Ca_N0987.js");
2591
+ responseGeneratorModulePromise ??= import("./response-generator-C8EHsqMw.js");
2592
2592
  return responseGeneratorModulePromise;
2593
2593
  }
2594
2594
  function sanitizeTranscriptForLog(value) {
@@ -3268,15 +3268,15 @@ let mockProviderPromise;
3268
3268
  let realtimeVoiceRuntimePromise;
3269
3269
  let realtimeHandlerPromise;
3270
3270
  function loadTelnyxProvider() {
3271
- telnyxProviderPromise ??= import("./telnyx-l9-ePiyj.js");
3271
+ telnyxProviderPromise ??= import("./telnyx-C8sgJugJ.js");
3272
3272
  return telnyxProviderPromise;
3273
3273
  }
3274
3274
  function loadTwilioProvider() {
3275
- twilioProviderPromise ??= import("./twilio-C5wUc1S2.js");
3275
+ twilioProviderPromise ??= import("./twilio-CpBg6Ir5.js");
3276
3276
  return twilioProviderPromise;
3277
3277
  }
3278
3278
  function loadPlivoProvider() {
3279
- plivoProviderPromise ??= import("./plivo-D4QmQaQu.js");
3279
+ plivoProviderPromise ??= import("./plivo-CVgE_V_c.js");
3280
3280
  return plivoProviderPromise;
3281
3281
  }
3282
3282
  function loadMockProvider() {
@@ -3288,7 +3288,7 @@ function loadRealtimeVoiceRuntime() {
3288
3288
  return realtimeVoiceRuntimePromise;
3289
3289
  }
3290
3290
  function loadRealtimeHandler() {
3291
- realtimeHandlerPromise ??= import("./realtime-handler-DT0Rlj3m.js");
3291
+ realtimeHandlerPromise ??= import("./realtime-handler-DQDCDHv2.js");
3292
3292
  return realtimeHandlerPromise;
3293
3293
  }
3294
3294
  function resolveVoiceCallConsultSessionKey(call) {
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-lBiyw_6t.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-DwBgs2Sq.js";
2
2
  export { createVoiceCallRuntime };
@@ -1,4 +1,4 @@
1
- import { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BT5XfY80.js";
1
+ import { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,7 +1,7 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as getHeader, d as chunkAudio, h as mapVoiceToPolly, i as normalizeProviderStatus, m as escapeXml, n as isProviderStatusTerminal, r as mapProviderStatusToEndReason } from "./runtime-entry-lBiyw_6t.js";
4
- import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BT5XfY80.js";
3
+ import { a as getHeader, d as chunkAudio, h as mapVoiceToPolly, i as normalizeProviderStatus, m as escapeXml, n as isProviderStatusTerminal, r as mapProviderStatusToEndReason } from "./runtime-entry-DwBgs2Sq.js";
4
+ import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.js";
5
5
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import crypto from "node:crypto";
7
7
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.5.28-beta.1",
3
+ "version": "2026.5.28-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/voice-call",
9
- "version": "2026.5.28-beta.1",
9
+ "version": "2026.5.28-beta.2",
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
12
  "typebox": "1.1.38",
@@ -14,7 +14,7 @@
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.5.28-beta.1"
17
+ "openclaw": ">=2026.5.28-beta.2"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.5.28-beta.1",
3
+ "version": "2026.5.28-beta.2",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.5.28-beta.1"
17
+ "openclaw": ">=2026.5.28-beta.2"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "openclaw": {
@@ -31,10 +31,10 @@
31
31
  "minHostVersion": ">=2026.4.10"
32
32
  },
33
33
  "compat": {
34
- "pluginApi": ">=2026.5.28-beta.1"
34
+ "pluginApi": ">=2026.5.28-beta.2"
35
35
  },
36
36
  "build": {
37
- "openclawVersion": "2026.5.28-beta.1"
37
+ "openclawVersion": "2026.5.28-beta.2"
38
38
  },
39
39
  "release": {
40
40
  "publishToClawHub": true,