@openclaw/voice-call 2026.7.2-beta.7 → 2026.8.1-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.
- package/dist/doctor-contract-api.js +10 -8
- package/dist/{guarded-json-api-DS9sZa3C.js → guarded-json-api-DEDwgf4J.js} +24 -46
- package/dist/index.js +308 -629
- package/dist/{mock-YsvBTX-7.js → mock-BCq0q36l.js} +2 -3
- package/dist/{plivo-vZ--Wqxv.js → plivo-DbI17uAc.js} +45 -8
- package/dist/{realtime-handler--i_OERv6.js → realtime-handler-uXR0QgVa.js} +364 -108
- package/dist/{response-generator-D-7ApVfT.js → response-generator-D-hkzo5Y.js} +13 -5
- package/dist/{runtime-entry-D0UJiLfi.js → runtime-entry-F13eJ4pp.js} +279 -172
- package/dist/runtime-entry.js +1 -1
- package/dist/setup-api.js +17 -24
- package/dist/{store-path-nYL-yM0S.js → store-path-51_oC_xn.js} +70 -13
- package/dist/{telnyx-Dx6cfLvV.js → telnyx-sIS_WgnY.js} +3 -2
- package/dist/{twilio-D_D8KysU.js → twilio-CbeVrVAl.js} +20 -26
- package/dist/{webhook-replay-aaP79dI8.js → webhook-replay-FQrEGVSM.js} +20 -6
- package/openclaw.plugin.json +49 -4
- package/package.json +4 -4
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { a as CALL_RECORD_EVENT_META_MAX_ENTRIES, c as buildVoiceCallLegacyJsonlEventKey, f as parseVoiceCallRecordLine, h as resolveVoiceCallLegacyCallLogPath, i as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, m as prepareVoiceCallRecordForStorage, n as CALL_RECORD_CHUNK_MAX_ENTRIES, o as MAX_CALL_RECORD_EVENTS, r as CALL_RECORD_EVENTS_NAMESPACE, s as RAW_CALL_RECORD_CHUNK_BYTES, t as resolveDefaultVoiceCallStoreDir } from "./store-path-
|
|
1
|
+
import { a as CALL_RECORD_EVENT_META_MAX_ENTRIES, c as buildVoiceCallLegacyJsonlEventKey, f as parseVoiceCallRecordLine, h as resolveVoiceCallLegacyCallLogPath, i as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, m as prepareVoiceCallRecordForStorage, n as CALL_RECORD_CHUNK_MAX_ENTRIES, o as MAX_CALL_RECORD_EVENTS, r as CALL_RECORD_EVENTS_NAMESPACE, s as RAW_CALL_RECORD_CHUNK_BYTES, t as resolveDefaultVoiceCallStoreDir } from "./store-path-51_oC_xn.js";
|
|
2
2
|
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
3
|
+
import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import fs from "node:fs/promises";
|
|
6
|
-
import { archiveLegacyStateSource
|
|
7
|
+
import { archiveLegacyStateSource } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
7
8
|
//#region extensions/voice-call/doctor-contract-api.ts
|
|
8
9
|
/** Resolve home from doctor env with OS fallback. */
|
|
9
10
|
function resolveHome(env) {
|
|
@@ -26,21 +27,18 @@ function getVoiceCallConfigStore(config) {
|
|
|
26
27
|
}
|
|
27
28
|
return "";
|
|
28
29
|
}
|
|
29
|
-
function asRecord(value) {
|
|
30
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
31
|
-
}
|
|
32
30
|
/** Return Voice Call agents whose templated core session stores need migration. */
|
|
33
31
|
function resolveSessionStoreAgentIds(params) {
|
|
34
32
|
const agentIds = /* @__PURE__ */ new Set();
|
|
35
33
|
for (const pluginId of ["voice-call", "@openclaw/voice-call"]) {
|
|
36
34
|
const entry = params.cfg.plugins?.entries?.[pluginId];
|
|
37
35
|
if (!entry) continue;
|
|
38
|
-
const config = entry.config === void 0 ? {} :
|
|
36
|
+
const config = entry.config === void 0 ? {} : asOptionalRecord(entry.config);
|
|
39
37
|
if (!config) continue;
|
|
40
38
|
agentIds.add(normalizeAgentId(typeof config.agentId === "string" ? config.agentId : void 0));
|
|
41
|
-
const numbers =
|
|
39
|
+
const numbers = asOptionalRecord(config.numbers);
|
|
42
40
|
for (const route of Object.values(numbers ?? {})) {
|
|
43
|
-
const agentId =
|
|
41
|
+
const agentId = asOptionalRecord(route)?.agentId;
|
|
44
42
|
if (typeof agentId === "string") agentIds.add(normalizeAgentId(agentId));
|
|
45
43
|
}
|
|
46
44
|
}
|
|
@@ -62,6 +60,8 @@ function describeVoiceCallSchemaMigration(migration) {
|
|
|
62
60
|
switch (migration.kind) {
|
|
63
61
|
case "agent-databases-composite-primary-key": return "agent database registry primary key -> agent_id,path";
|
|
64
62
|
case "audit-events-v2": return "audit event ledger -> versioned message lifecycle schema";
|
|
63
|
+
case "commitments-retirement-v7": return "retired commitments storage -> removed table and indexes";
|
|
64
|
+
case "worker-placement-execution-mode-v8": return "cloud worker placements -> execution-mode claims";
|
|
65
65
|
case "operator-approvals-system-agent": return "operator approvals -> OpenClaw system changes";
|
|
66
66
|
case "session-watch-cursor-provenance-v4": return "session watch cursors -> provenance column";
|
|
67
67
|
case "strict-tables-v3": return "tables -> SQLite STRICT typing";
|
|
@@ -185,12 +185,14 @@ const stateMigrations = [{
|
|
|
185
185
|
id: "voice-call-calls-jsonl-to-plugin-state",
|
|
186
186
|
label: "Voice Call call log",
|
|
187
187
|
async detectLegacyState(params) {
|
|
188
|
+
const { detectOpenClawStateDatabaseSchemaMigrations } = await import("openclaw/plugin-sdk/doctor-repair-runtime");
|
|
188
189
|
const { entries } = await readLegacyCallRecords(resolveVoiceCallLegacyCallLogPath(resolveVoiceCallStorePath(params)));
|
|
189
190
|
const schemaMigrations = detectOpenClawStateDatabaseSchemaMigrations({ env: resolveVoiceCallStateDatabaseEnv(params) });
|
|
190
191
|
if (entries.length === 0 && schemaMigrations.length === 0) return null;
|
|
191
192
|
return { preview: [...schemaMigrations.map((migration) => `- Voice Call SQLite schema: ${describeVoiceCallSchemaMigration(migration)}`), ...entries.length > 0 ? [`- Voice Call call log: ${entries.length} ${entries.length === 1 ? "record" : "records"} -> plugin state (${CALL_RECORD_EVENTS_NAMESPACE})`] : []] };
|
|
192
193
|
},
|
|
193
194
|
async migrateLegacyState(params) {
|
|
195
|
+
const { detectOpenClawStateDatabaseSchemaMigrations, repairOpenClawStateDatabaseSchema } = await import("openclaw/plugin-sdk/doctor-repair-runtime");
|
|
194
196
|
const changes = [];
|
|
195
197
|
const warnings = [];
|
|
196
198
|
const filePath = resolveVoiceCallLegacyCallLogPath(resolveVoiceCallStorePath(params));
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { fetchWithSsrFGuard } from "./runtime-api.js";
|
|
2
2
|
import "./api.js";
|
|
3
|
-
import { n as normalizeProxyIp, s as getHeader } from "./runtime-entry-
|
|
4
|
-
import { n as
|
|
3
|
+
import { n as normalizeProxyIp, s as getHeader } from "./runtime-entry-F13eJ4pp.js";
|
|
4
|
+
import { n as reserveWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-FQrEGVSM.js";
|
|
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
|
-
import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
9
|
+
import { redactSensitiveText, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
10
10
|
import { isIP } from "node:net";
|
|
11
11
|
import { readResponseTextPrefix, readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
12
12
|
//#region extensions/voice-call/src/webhook-security.ts
|
|
@@ -256,8 +256,7 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
|
|
|
256
256
|
return {
|
|
257
257
|
ok: true,
|
|
258
258
|
reason: "verification skipped (dev mode)",
|
|
259
|
-
|
|
260
|
-
verifiedRequestKey: replayKey
|
|
259
|
+
...reserveWebhookReplay(telnyxReplayCache, replayKey)
|
|
261
260
|
};
|
|
262
261
|
}
|
|
263
262
|
if (!publicKey) return {
|
|
@@ -293,8 +292,7 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
|
|
|
293
292
|
const replayKey = `telnyx:${sha256Hex(`${timestamp}\n${canonicalSignature}\n${ctx.rawBody}`)}`;
|
|
294
293
|
return {
|
|
295
294
|
ok: true,
|
|
296
|
-
|
|
297
|
-
verifiedRequestKey: replayKey
|
|
295
|
+
...reserveWebhookReplay(telnyxReplayCache, replayKey)
|
|
298
296
|
};
|
|
299
297
|
} catch (err) {
|
|
300
298
|
return {
|
|
@@ -312,8 +310,7 @@ function verifyTwilioWebhook(ctx, authToken, options) {
|
|
|
312
310
|
return {
|
|
313
311
|
ok: true,
|
|
314
312
|
reason: "verification skipped (dev mode)",
|
|
315
|
-
|
|
316
|
-
verifiedRequestKey: replayKey
|
|
313
|
+
...reserveWebhookReplay(twilioReplayCache, replayKey)
|
|
317
314
|
};
|
|
318
315
|
}
|
|
319
316
|
const signature = getHeader(ctx.headers, "x-twilio-signature");
|
|
@@ -339,8 +336,7 @@ function verifyTwilioWebhook(ctx, authToken, options) {
|
|
|
339
336
|
return {
|
|
340
337
|
ok: true,
|
|
341
338
|
verificationUrl,
|
|
342
|
-
|
|
343
|
-
verifiedRequestKey: replayKey
|
|
339
|
+
...reserveWebhookReplay(twilioReplayCache, replayKey)
|
|
344
340
|
};
|
|
345
341
|
}
|
|
346
342
|
const variants = /* @__PURE__ */ new Set();
|
|
@@ -363,8 +359,7 @@ function verifyTwilioWebhook(ctx, authToken, options) {
|
|
|
363
359
|
return {
|
|
364
360
|
ok: true,
|
|
365
361
|
verificationUrl: candidateUrl,
|
|
366
|
-
|
|
367
|
-
verifiedRequestKey: replayKey
|
|
362
|
+
...reserveWebhookReplay(twilioReplayCache, replayKey)
|
|
368
363
|
};
|
|
369
364
|
}
|
|
370
365
|
const isNgrokFreeTier = verificationUrl.includes(".ngrok-free.app") || verificationUrl.includes(".ngrok.io");
|
|
@@ -456,8 +451,7 @@ function verifyPlivoWebhook(ctx, authToken, options) {
|
|
|
456
451
|
return {
|
|
457
452
|
ok: true,
|
|
458
453
|
reason: "verification skipped (dev mode)",
|
|
459
|
-
|
|
460
|
-
verifiedRequestKey: replayKey
|
|
454
|
+
...reserveWebhookReplay(plivoReplayCache, replayKey)
|
|
461
455
|
};
|
|
462
456
|
}
|
|
463
457
|
const signatureV3 = getHeader(ctx.headers, "x-plivo-signature-v3");
|
|
@@ -507,13 +501,11 @@ function verifyPlivoWebhook(ctx, authToken, options) {
|
|
|
507
501
|
postParams,
|
|
508
502
|
nonce: nonceV3
|
|
509
503
|
});
|
|
510
|
-
const isReplay = markWebhookReplay(plivoReplayCache, replayKey);
|
|
511
504
|
return {
|
|
512
505
|
ok: true,
|
|
513
506
|
version: "v3",
|
|
514
507
|
verificationUrl,
|
|
515
|
-
|
|
516
|
-
verifiedRequestKey: replayKey
|
|
508
|
+
...reserveWebhookReplay(plivoReplayCache, replayKey)
|
|
517
509
|
};
|
|
518
510
|
}
|
|
519
511
|
if (signatureV2 && nonceV2) {
|
|
@@ -529,13 +521,11 @@ function verifyPlivoWebhook(ctx, authToken, options) {
|
|
|
529
521
|
reason: "Invalid Plivo V2 signature"
|
|
530
522
|
};
|
|
531
523
|
const replayKey = createPlivoV2ReplayKey(verificationUrl, nonceV2);
|
|
532
|
-
const isReplay = markWebhookReplay(plivoReplayCache, replayKey);
|
|
533
524
|
return {
|
|
534
525
|
ok: true,
|
|
535
526
|
version: "v2",
|
|
536
527
|
verificationUrl,
|
|
537
|
-
|
|
538
|
-
verifiedRequestKey: replayKey
|
|
528
|
+
...reserveWebhookReplay(plivoReplayCache, replayKey)
|
|
539
529
|
};
|
|
540
530
|
}
|
|
541
531
|
return {
|
|
@@ -555,26 +545,20 @@ async function cancelProviderResponseBody(response) {
|
|
|
555
545
|
function appendTruncatedSuffix(text) {
|
|
556
546
|
return `${text.trimEnd()}${TRUNCATED_SUFFIX}`;
|
|
557
547
|
}
|
|
558
|
-
async function
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
548
|
+
async function readVoiceCallProviderJsonResponse(response, malformedJsonMessage) {
|
|
549
|
+
const body = await readResponseWithLimit(response, PROVIDER_JSON_RESPONSE_MAX_BYTES, { onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`provider response body too large: ${size} bytes (limit: ${maxBytes} bytes)`) });
|
|
550
|
+
if (body.byteLength === 0) return;
|
|
551
|
+
try {
|
|
552
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
553
|
+
return JSON.parse(text);
|
|
554
|
+
} catch (cause) {
|
|
555
|
+
throw new Error(malformedJsonMessage, { cause });
|
|
562
556
|
}
|
|
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
|
-
}
|
|
566
|
-
async function readProviderJsonResponseText(response) {
|
|
567
|
-
return await readProviderResponseTextWithLimit({
|
|
568
|
-
response,
|
|
569
|
-
maxBytes: PROVIDER_JSON_RESPONSE_MAX_BYTES
|
|
570
|
-
});
|
|
571
557
|
}
|
|
572
558
|
async function readProviderErrorResponseSnippet(response) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
truncateOnLimit: true
|
|
577
|
-
});
|
|
559
|
+
const prefix = await readResponseTextPrefix(response, PROVIDER_ERROR_RESPONSE_MAX_BYTES);
|
|
560
|
+
const text = redactSensitiveText(prefix.text, { mode: "tools" });
|
|
561
|
+
return prefix.truncated ? appendTruncatedSuffix(text) : text;
|
|
578
562
|
}
|
|
579
563
|
//#endregion
|
|
580
564
|
//#region extensions/voice-call/src/providers/shared/guarded-json-api.ts
|
|
@@ -601,16 +585,10 @@ async function guardedJsonApiRequest(params) {
|
|
|
601
585
|
const errorText = await readProviderErrorResponseSnippet(response);
|
|
602
586
|
throw new Error(`${params.errorPrefix}: ${response.status} ${errorText}`);
|
|
603
587
|
}
|
|
604
|
-
|
|
605
|
-
if (!text) return;
|
|
606
|
-
try {
|
|
607
|
-
return JSON.parse(text);
|
|
608
|
-
} catch {
|
|
609
|
-
throw new Error(`${params.errorPrefix}: malformed JSON response`);
|
|
610
|
-
}
|
|
588
|
+
return await readVoiceCallProviderJsonResponse(response, `${params.errorPrefix}: malformed JSON response`);
|
|
611
589
|
} finally {
|
|
612
590
|
await release();
|
|
613
591
|
}
|
|
614
592
|
}
|
|
615
593
|
//#endregion
|
|
616
|
-
export { reconstructWebhookUrl as a, verifyTwilioWebhook as c,
|
|
594
|
+
export { reconstructWebhookUrl as a, verifyTwilioWebhook as c, readVoiceCallProviderJsonResponse as i, cancelProviderResponseBody as n, verifyPlivoWebhook as o, readProviderErrorResponseSnippet as r, verifyTelnyxWebhook as s, guardedJsonApiRequest as t };
|