@openclaw/voice-call 2026.7.2-beta.7 → 2026.7.34
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 +3 -2
- package/dist/.setup/config-BBpso-fA.mjs +674 -0
- package/dist/.setup/config-compat-CYRE-SSo.mjs +156 -0
- package/dist/{guarded-json-api-DS9sZa3C.js → .setup/guarded-json-api-BukKSVNW.mjs} +56 -42
- package/dist/{mock-YsvBTX-7.js → .setup/mock-DY4yTqR2.mjs} +1 -1
- package/dist/{plivo-vZ--Wqxv.js → .setup/plivo-DFHh72MH.mjs} +3 -2
- package/dist/{realtime-handler--i_OERv6.js → .setup/realtime-handler-DWLxvZuO.mjs} +198 -130
- package/dist/{response-generator-D-7ApVfT.js → .setup/response-generator-DoC6lfcm.mjs} +6 -21
- package/dist/{runtime-entry-D0UJiLfi.js → .setup/runtime-entry-CH-CV0Sf.mjs} +384 -868
- package/dist/{store-path-nYL-yM0S.js → .setup/store-1UfyY3HS.mjs} +6 -13
- package/dist/{telnyx-Dx6cfLvV.js → .setup/telnyx-Bb8X-nin.mjs} +1 -1
- package/dist/{twilio-D_D8KysU.js → .setup/twilio-Bsbf3O9A.mjs} +11 -15
- package/dist/{webhook-replay-aaP79dI8.js → .setup/webhook-replay-g3cDA5wr.mjs} +1 -1
- package/dist/cli-metadata.js +1 -5
- package/dist/doctor-contract-api.js +14 -36
- package/dist/index.js +46 -48
- package/dist/runtime-entry.js +1 -1
- package/dist/setup-api.js +2 -86
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1172 -0
- package/node_modules/commander/index.js +21 -0
- package/node_modules/commander/lib/argument.js +147 -0
- package/node_modules/commander/lib/command.js +2790 -0
- package/node_modules/commander/lib/error.js +36 -0
- package/node_modules/commander/lib/help.js +731 -0
- package/node_modules/commander/lib/option.js +377 -0
- package/node_modules/commander/lib/suggestSimilar.js +99 -0
- package/node_modules/commander/package-support.json +19 -0
- package/node_modules/commander/package.json +64 -0
- package/node_modules/commander/typings/index.d.ts +1113 -0
- package/node_modules/typebox/build/compile/validator.d.mts +4 -1
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +2 -5
- package/node_modules/typebox/build/guard/string.mjs +4 -16
- package/node_modules/typebox/build/schema/engine/_functions.mjs +15 -34
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +0 -5
- package/node_modules/typebox/build/type/action/readonly_object.mjs +0 -8
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +32 -29
- package/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/ws/package.json +1 -1
- package/openclaw.plugin.json +15 -2
- package/package.json +8 -6
- package/dist/cli-output-mode.js +0 -27
- package/dist/twilio-region-4fkgz3UG.js +0 -25
- /package/dist/{realtime-transcription.runtime-CbJAs5t_.js → .setup/realtime-transcription.runtime-CbJAs5t_.mjs} +0 -0
- /package/dist/{realtime-voice.runtime-vtdCOWg-.js → .setup/realtime-voice.runtime-vtdCOWg-.mjs} +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { t as VoiceCallConfigSchema } from "./config-BBpso-fA.mjs";
|
|
2
|
+
import { asOptionalRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
//#region extensions/voice-call/src/config-compat.ts
|
|
4
|
+
/** Version where legacy voice-call config shape support is removed. */
|
|
5
|
+
const VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION = "2026.6.0";
|
|
6
|
+
const asObject = asOptionalRecord;
|
|
7
|
+
const getString = readStringField;
|
|
8
|
+
/** Read finite numeric config values. */
|
|
9
|
+
function getNumber(obj, key) {
|
|
10
|
+
const value = obj?.[key];
|
|
11
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12
|
+
}
|
|
13
|
+
/** Merge legacy provider-specific values into the canonical providers map. */
|
|
14
|
+
function mergeProviderConfig(providersValue, providerId, compatValues) {
|
|
15
|
+
if (Object.keys(compatValues).length === 0) return asObject(providersValue);
|
|
16
|
+
const providers = asObject(providersValue) ?? {};
|
|
17
|
+
const existing = asObject(providers[providerId]) ?? {};
|
|
18
|
+
return {
|
|
19
|
+
...providers,
|
|
20
|
+
[providerId]: {
|
|
21
|
+
...existing,
|
|
22
|
+
...compatValues
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Collect legacy voice-call config keys that should be migrated. */
|
|
27
|
+
function collectVoiceCallLegacyConfigIssues(value) {
|
|
28
|
+
const raw = asObject(value) ?? {};
|
|
29
|
+
const realtime = asObject(raw.realtime);
|
|
30
|
+
const realtimeAgentContext = asObject(realtime?.agentContext);
|
|
31
|
+
const twilio = asObject(raw.twilio);
|
|
32
|
+
const streaming = asObject(raw.streaming);
|
|
33
|
+
const issues = [];
|
|
34
|
+
if (raw.provider === "log") issues.push({
|
|
35
|
+
path: "provider",
|
|
36
|
+
replacement: "provider",
|
|
37
|
+
message: "Replace provider \"log\" with \"mock\"."
|
|
38
|
+
});
|
|
39
|
+
if (typeof twilio?.from === "string") issues.push({
|
|
40
|
+
path: "twilio.from",
|
|
41
|
+
replacement: "fromNumber",
|
|
42
|
+
message: "Move twilio.from to fromNumber."
|
|
43
|
+
});
|
|
44
|
+
if (typeof streaming?.sttProvider === "string") issues.push({
|
|
45
|
+
path: "streaming.sttProvider",
|
|
46
|
+
replacement: "streaming.provider",
|
|
47
|
+
message: "Move streaming.sttProvider to streaming.provider."
|
|
48
|
+
});
|
|
49
|
+
if (typeof streaming?.openaiApiKey === "string") issues.push({
|
|
50
|
+
path: "streaming.openaiApiKey",
|
|
51
|
+
replacement: "streaming.providers.openai.apiKey",
|
|
52
|
+
message: "Move streaming.openaiApiKey to streaming.providers.openai.apiKey."
|
|
53
|
+
});
|
|
54
|
+
if (typeof streaming?.sttModel === "string") issues.push({
|
|
55
|
+
path: "streaming.sttModel",
|
|
56
|
+
replacement: "streaming.providers.openai.model",
|
|
57
|
+
message: "Move streaming.sttModel to streaming.providers.openai.model."
|
|
58
|
+
});
|
|
59
|
+
if (typeof streaming?.silenceDurationMs === "number") issues.push({
|
|
60
|
+
path: "streaming.silenceDurationMs",
|
|
61
|
+
replacement: "streaming.providers.openai.silenceDurationMs",
|
|
62
|
+
message: "Move streaming.silenceDurationMs to streaming.providers.openai.silenceDurationMs."
|
|
63
|
+
});
|
|
64
|
+
if (typeof streaming?.vadThreshold === "number") issues.push({
|
|
65
|
+
path: "streaming.vadThreshold",
|
|
66
|
+
replacement: "streaming.providers.openai.vadThreshold",
|
|
67
|
+
message: "Move streaming.vadThreshold to streaming.providers.openai.vadThreshold."
|
|
68
|
+
});
|
|
69
|
+
if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) issues.push({
|
|
70
|
+
path: "realtime.agentContext.includeSystemPrompt",
|
|
71
|
+
replacement: "realtime.agentContext",
|
|
72
|
+
message: "Remove realtime.agentContext.includeSystemPrompt; realtime context now uses the generated agent prompt."
|
|
73
|
+
});
|
|
74
|
+
return issues;
|
|
75
|
+
}
|
|
76
|
+
/** Format runtime warnings for legacy voice-call config keys. */
|
|
77
|
+
function formatVoiceCallLegacyConfigWarnings(params) {
|
|
78
|
+
const issues = collectVoiceCallLegacyConfigIssues(params.value);
|
|
79
|
+
if (issues.length === 0) return [];
|
|
80
|
+
return [`[voice-call] legacy config keys detected under ${params.configPathPrefix}; runtime loading will not rewrite them, and support for the legacy shape will be removed in ${VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION}. Run "${params.doctorFixCommand}".`, ...issues.map((issue) => `[voice-call] ${params.configPathPrefix}.${issue.path}: ${issue.message}`)];
|
|
81
|
+
}
|
|
82
|
+
/** Migrate legacy voice-call config input to the current canonical shape. */
|
|
83
|
+
function migrateVoiceCallLegacyConfigInput(params) {
|
|
84
|
+
const raw = asObject(params.value) ?? {};
|
|
85
|
+
const realtime = asObject(raw.realtime);
|
|
86
|
+
const realtimeAgentContext = asObject(realtime?.agentContext);
|
|
87
|
+
const twilio = asObject(raw.twilio);
|
|
88
|
+
const streaming = asObject(raw.streaming);
|
|
89
|
+
const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
|
|
90
|
+
const issues = collectVoiceCallLegacyConfigIssues(raw);
|
|
91
|
+
const legacyStreamingOpenAICompat = {};
|
|
92
|
+
const streamingOpenAIApiKey = getString(streaming, "openaiApiKey");
|
|
93
|
+
if (streamingOpenAIApiKey) legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
|
|
94
|
+
const streamingSttModel = getString(streaming, "sttModel");
|
|
95
|
+
if (streamingSttModel) legacyStreamingOpenAICompat.model = streamingSttModel;
|
|
96
|
+
const streamingSilenceDurationMs = getNumber(streaming, "silenceDurationMs");
|
|
97
|
+
if (streamingSilenceDurationMs !== void 0) legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
|
|
98
|
+
const streamingVadThreshold = getNumber(streaming, "vadThreshold");
|
|
99
|
+
if (streamingVadThreshold !== void 0) legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
|
|
100
|
+
const streamingProvider = getString(streaming, "provider");
|
|
101
|
+
const legacyStreamingProvider = getString(streaming, "sttProvider");
|
|
102
|
+
const normalizedStreaming = streaming ? {
|
|
103
|
+
...streaming,
|
|
104
|
+
provider: streamingProvider ?? legacyStreamingProvider,
|
|
105
|
+
providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat)
|
|
106
|
+
} : void 0;
|
|
107
|
+
if (normalizedStreaming) {
|
|
108
|
+
delete normalizedStreaming.sttProvider;
|
|
109
|
+
delete normalizedStreaming.openaiApiKey;
|
|
110
|
+
delete normalizedStreaming.sttModel;
|
|
111
|
+
delete normalizedStreaming.silenceDurationMs;
|
|
112
|
+
delete normalizedStreaming.vadThreshold;
|
|
113
|
+
}
|
|
114
|
+
const normalizedTwilio = twilio ? { ...twilio } : void 0;
|
|
115
|
+
if (normalizedTwilio) delete normalizedTwilio.from;
|
|
116
|
+
const normalizedRealtimeAgentContext = realtimeAgentContext ? { ...realtimeAgentContext } : void 0;
|
|
117
|
+
if (normalizedRealtimeAgentContext) delete normalizedRealtimeAgentContext.includeSystemPrompt;
|
|
118
|
+
const normalizedRealtime = realtime ? {
|
|
119
|
+
...realtime,
|
|
120
|
+
agentContext: normalizedRealtimeAgentContext ?? realtime.agentContext
|
|
121
|
+
} : void 0;
|
|
122
|
+
const config = {
|
|
123
|
+
...raw,
|
|
124
|
+
provider: raw.provider === "log" ? "mock" : raw.provider,
|
|
125
|
+
fromNumber: raw.fromNumber ?? (typeof twilio?.from === "string" ? twilio.from : void 0),
|
|
126
|
+
twilio: normalizedTwilio,
|
|
127
|
+
streaming: normalizedStreaming,
|
|
128
|
+
realtime: normalizedRealtime
|
|
129
|
+
};
|
|
130
|
+
const changes = [];
|
|
131
|
+
if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
|
|
132
|
+
if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
|
|
133
|
+
if (typeof streaming?.sttProvider === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`);
|
|
134
|
+
if (typeof streaming?.openaiApiKey === "string") changes.push(`Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`);
|
|
135
|
+
if (typeof streaming?.sttModel === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`);
|
|
136
|
+
if (getNumber(streaming, "silenceDurationMs") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`);
|
|
137
|
+
else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
|
|
138
|
+
if (getNumber(streaming, "vadThreshold") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
|
|
139
|
+
else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
|
|
140
|
+
if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
|
|
141
|
+
return {
|
|
142
|
+
config,
|
|
143
|
+
changes,
|
|
144
|
+
issues
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** Normalize legacy voice-call config input without returning migration metadata. */
|
|
148
|
+
function normalizeVoiceCallLegacyConfigInput(value) {
|
|
149
|
+
return migrateVoiceCallLegacyConfigInput({ value }).config;
|
|
150
|
+
}
|
|
151
|
+
/** Parse voice-call plugin config after applying legacy normalization. */
|
|
152
|
+
function parseVoiceCallPluginConfig(value) {
|
|
153
|
+
return VoiceCallConfigSchema.parse(normalizeVoiceCallLegacyConfigInput(value));
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
export { parseVoiceCallPluginConfig as i, migrateVoiceCallLegacyConfigInput as n, normalizeVoiceCallLegacyConfigInput as r, formatVoiceCallLegacyConfigWarnings as t };
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { fetchWithSsrFGuard } from "
|
|
2
|
-
import "
|
|
3
|
-
import { n as normalizeProxyIp,
|
|
4
|
-
import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-
|
|
1
|
+
import { fetchWithSsrFGuard } from "../runtime-api.js";
|
|
2
|
+
import "../api.js";
|
|
3
|
+
import { n as normalizeProxyIp, o as getHeader } from "./runtime-entry-CH-CV0Sf.mjs";
|
|
4
|
+
import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-g3cDA5wr.mjs";
|
|
5
5
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
6
6
|
import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
|
|
7
7
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
import crypto from "node:crypto";
|
|
9
9
|
import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
10
|
-
import { isIP } from "node:net";
|
|
11
|
-
import { readResponseTextPrefix, readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
12
10
|
//#region extensions/voice-call/src/webhook-security.ts
|
|
13
11
|
const twilioReplayCache = createWebhookReplayCache();
|
|
14
12
|
const plivoReplayCache = createWebhookReplayCache();
|
|
@@ -30,7 +28,8 @@ function createSkippedVerificationReplayKey(provider, ctx) {
|
|
|
30
28
|
function validateTwilioSignature(authToken, signature, url, params) {
|
|
31
29
|
if (!signature) return false;
|
|
32
30
|
const dataToSign = buildTwilioDataToSign(url, params);
|
|
33
|
-
|
|
31
|
+
const expectedSignature = crypto.createHmac("sha1", authToken).update(dataToSign).digest("base64");
|
|
32
|
+
return safeEqualSecret(signature, expectedSignature);
|
|
34
33
|
}
|
|
35
34
|
function buildTwilioDataToSign(url, params) {
|
|
36
35
|
let dataToSign = url;
|
|
@@ -55,24 +54,16 @@ function isValidHostname(hostname) {
|
|
|
55
54
|
*/
|
|
56
55
|
function extractHostname(hostHeader) {
|
|
57
56
|
if (!hostHeader) return null;
|
|
57
|
+
let hostname;
|
|
58
58
|
if (hostHeader.startsWith("[")) {
|
|
59
59
|
const endBracket = hostHeader.indexOf("]");
|
|
60
60
|
if (endBracket === -1) return null;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const hostname = hostHeader.slice(1, endBracket);
|
|
64
|
-
if (isIP(hostname) !== 6) return null;
|
|
65
|
-
try {
|
|
66
|
-
const parsedHostname = new URL(`https://[${hostname}]/`).hostname;
|
|
67
|
-
if (!parsedHostname.startsWith("[") || !parsedHostname.endsWith("]")) return null;
|
|
68
|
-
return normalizeLowercaseStringOrEmpty(parsedHostname.slice(1, -1));
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
61
|
+
hostname = hostHeader.slice(1, endBracket);
|
|
62
|
+
return normalizeLowercaseStringOrEmpty(hostname);
|
|
72
63
|
}
|
|
73
64
|
if (hostHeader.includes("@")) return null;
|
|
74
|
-
|
|
75
|
-
if (!
|
|
65
|
+
hostname = hostHeader.split(":")[0];
|
|
66
|
+
if (!isValidHostname(hostname)) return null;
|
|
76
67
|
return normalizeLowercaseStringOrEmpty(hostname);
|
|
77
68
|
}
|
|
78
69
|
function extractHostnameFromHeader(headerValue) {
|
|
@@ -89,9 +80,6 @@ function normalizeAllowedHosts(allowedHosts) {
|
|
|
89
80
|
}
|
|
90
81
|
return normalized.size > 0 ? normalized : null;
|
|
91
82
|
}
|
|
92
|
-
function formatHostnameForUrl(hostname) {
|
|
93
|
-
return isIP(hostname) === 6 ? `[${hostname}]` : hostname;
|
|
94
|
-
}
|
|
95
83
|
/**
|
|
96
84
|
* Reconstruct the public webhook URL from request headers.
|
|
97
85
|
*
|
|
@@ -161,13 +149,15 @@ function reconstructWebhookUrl(ctx, options) {
|
|
|
161
149
|
const parsed = new URL(ctx.url);
|
|
162
150
|
path = parsed.pathname + parsed.search;
|
|
163
151
|
} catch {}
|
|
164
|
-
return `${proto}://${
|
|
152
|
+
return `${proto}://${host}${path}`;
|
|
165
153
|
}
|
|
166
154
|
function buildTwilioVerificationUrl(ctx, publicUrl, urlOptions) {
|
|
167
155
|
if (!publicUrl) return reconstructWebhookUrl(ctx, urlOptions);
|
|
168
156
|
try {
|
|
169
157
|
const base = new URL(publicUrl);
|
|
170
|
-
|
|
158
|
+
const requestUrl = new URL(ctx.url);
|
|
159
|
+
base.pathname = requestUrl.pathname;
|
|
160
|
+
base.search = requestUrl.search;
|
|
171
161
|
return base.toString();
|
|
172
162
|
} catch {
|
|
173
163
|
return publicUrl;
|
|
@@ -270,8 +260,8 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
|
|
|
270
260
|
ok: false,
|
|
271
261
|
reason: "Missing signature or timestamp header"
|
|
272
262
|
};
|
|
273
|
-
const eventTimeSec =
|
|
274
|
-
if (
|
|
263
|
+
const eventTimeSec = Number.parseInt(timestamp, 10);
|
|
264
|
+
if (!Number.isFinite(eventTimeSec)) return {
|
|
275
265
|
ok: false,
|
|
276
266
|
reason: "Invalid timestamp header"
|
|
277
267
|
};
|
|
@@ -284,7 +274,7 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
|
|
|
284
274
|
ok: false,
|
|
285
275
|
reason: "Invalid signature"
|
|
286
276
|
};
|
|
287
|
-
const maxSkewMs = options?.maxSkewMs ??
|
|
277
|
+
const maxSkewMs = options?.maxSkewMs ?? 3e5;
|
|
288
278
|
const eventTimeMs = eventTimeSec * 1e3;
|
|
289
279
|
if (Math.abs(Date.now() - eventTimeMs) > maxSkewMs) return {
|
|
290
280
|
ok: false,
|
|
@@ -393,7 +383,9 @@ function createPlivoV3ReplayKey(params) {
|
|
|
393
383
|
}
|
|
394
384
|
function validatePlivoV2Signature(params) {
|
|
395
385
|
const baseUrl = getBaseUrlNoQuery(params.url);
|
|
396
|
-
|
|
386
|
+
const expected = normalizeSignatureBase64(crypto.createHmac("sha256", params.authToken).update(baseUrl + params.nonce).digest("base64"));
|
|
387
|
+
const provided = normalizeSignatureBase64(params.signature);
|
|
388
|
+
return safeEqualSecret(expected, provided);
|
|
397
389
|
}
|
|
398
390
|
function toParamMapFromSearchParams(sp) {
|
|
399
391
|
const map = {};
|
|
@@ -405,18 +397,16 @@ function toParamMapFromSearchParams(sp) {
|
|
|
405
397
|
}
|
|
406
398
|
function sortedQueryString(params) {
|
|
407
399
|
const parts = [];
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
const values = [...entryValues].toSorted();
|
|
400
|
+
for (const key of Object.keys(params).toSorted()) {
|
|
401
|
+
const values = [...params[key]].toSorted();
|
|
411
402
|
for (const value of values) parts.push(`${key}=${value}`);
|
|
412
403
|
}
|
|
413
404
|
return parts.join("&");
|
|
414
405
|
}
|
|
415
406
|
function sortedParamsString(params) {
|
|
416
407
|
const parts = [];
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
const values = [...entryValues].toSorted();
|
|
408
|
+
for (const key of Object.keys(params).toSorted()) {
|
|
409
|
+
const values = [...params[key]].toSorted();
|
|
420
410
|
for (const value of values) parts.push(`${key}${value}`);
|
|
421
411
|
}
|
|
422
412
|
return parts.join("");
|
|
@@ -474,6 +464,7 @@ function verifyPlivoWebhook(ctx, authToken, options) {
|
|
|
474
464
|
if (options?.publicUrl) try {
|
|
475
465
|
const req = new URL(reconstructed);
|
|
476
466
|
const base = new URL(options.publicUrl);
|
|
467
|
+
base.pathname = req.pathname;
|
|
477
468
|
base.search = req.search;
|
|
478
469
|
verificationUrl = base.toString();
|
|
479
470
|
} catch {
|
|
@@ -546,8 +537,8 @@ function verifyPlivoWebhook(ctx, authToken, options) {
|
|
|
546
537
|
}
|
|
547
538
|
//#endregion
|
|
548
539
|
//#region extensions/voice-call/src/providers/shared/response-body.ts
|
|
549
|
-
const PROVIDER_JSON_RESPONSE_MAX_BYTES =
|
|
550
|
-
const PROVIDER_ERROR_RESPONSE_MAX_BYTES =
|
|
540
|
+
const PROVIDER_JSON_RESPONSE_MAX_BYTES = 1048576;
|
|
541
|
+
const PROVIDER_ERROR_RESPONSE_MAX_BYTES = 8192;
|
|
551
542
|
const TRUNCATED_SUFFIX = "... [truncated]";
|
|
552
543
|
async function cancelProviderResponseBody(response) {
|
|
553
544
|
await response.body?.cancel().catch(() => void 0);
|
|
@@ -556,12 +547,35 @@ function appendTruncatedSuffix(text) {
|
|
|
556
547
|
return `${text.trimEnd()}${TRUNCATED_SUFFIX}`;
|
|
557
548
|
}
|
|
558
549
|
async function readProviderResponseTextWithLimit(params) {
|
|
559
|
-
if (params.
|
|
560
|
-
|
|
561
|
-
|
|
550
|
+
if (!params.response.body) return "";
|
|
551
|
+
const reader = params.response.body.getReader();
|
|
552
|
+
const decoder = new TextDecoder();
|
|
553
|
+
let totalBytes = 0;
|
|
554
|
+
let text = "";
|
|
555
|
+
try {
|
|
556
|
+
while (true) {
|
|
557
|
+
const { done, value } = await reader.read();
|
|
558
|
+
if (done) return text + decoder.decode();
|
|
559
|
+
if (!value?.byteLength) continue;
|
|
560
|
+
const remainingBytes = params.maxBytes - totalBytes;
|
|
561
|
+
if (value.byteLength > remainingBytes) {
|
|
562
|
+
if (params.truncateOnLimit) {
|
|
563
|
+
const clipped = remainingBytes > 0 ? value.slice(0, remainingBytes) : void 0;
|
|
564
|
+
if (clipped) text += decoder.decode(clipped, { stream: true });
|
|
565
|
+
await reader.cancel().catch(() => void 0);
|
|
566
|
+
return appendTruncatedSuffix(text + decoder.decode());
|
|
567
|
+
}
|
|
568
|
+
await reader.cancel().catch(() => void 0);
|
|
569
|
+
throw new Error(`provider response body too large: ${totalBytes + value.byteLength} bytes (limit: ${params.maxBytes} bytes)`);
|
|
570
|
+
}
|
|
571
|
+
text += decoder.decode(value, { stream: true });
|
|
572
|
+
totalBytes += value.byteLength;
|
|
573
|
+
}
|
|
574
|
+
} finally {
|
|
575
|
+
try {
|
|
576
|
+
reader.releaseLock();
|
|
577
|
+
} catch {}
|
|
562
578
|
}
|
|
563
|
-
const body = await readResponseWithLimit(params.response, params.maxBytes, { onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`provider response body too large: ${size} bytes (limit: ${maxBytes} bytes)`) });
|
|
564
|
-
return new TextDecoder().decode(body);
|
|
565
579
|
}
|
|
566
580
|
async function readProviderJsonResponseText(response) {
|
|
567
581
|
return await readProviderResponseTextWithLimit({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-
|
|
1
|
+
import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-g3cDA5wr.mjs";
|
|
2
2
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
4
|
//#region extensions/voice-call/src/providers/mock.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-
|
|
1
|
+
import { g as escapeXml, o as getHeader } from "./runtime-entry-CH-CV0Sf.mjs";
|
|
2
|
+
import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BukKSVNW.mjs";
|
|
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
|
|
@@ -371,6 +371,7 @@ var PlivoProvider = class PlivoProvider {
|
|
|
371
371
|
try {
|
|
372
372
|
if (this.options.publicUrl) {
|
|
373
373
|
const base = new URL(this.options.publicUrl);
|
|
374
|
+
base.pathname = new URL(ctx.url).pathname;
|
|
374
375
|
return `${base.origin}${base.pathname}`;
|
|
375
376
|
}
|
|
376
377
|
const u = new URL(reconstructWebhookUrl(ctx, {
|