@kodelyth/voice-call 2026.5.39 → 2026.5.42
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 +167 -0
- package/api.ts +16 -0
- package/cli-metadata.ts +10 -0
- package/config-api.ts +12 -0
- package/dist/api.js +2 -0
- package/dist/cli-metadata.js +12 -0
- package/dist/config-DAwbG2aw.js +621 -0
- package/dist/config-compat-BYfJ5ueI.js +129 -0
- package/dist/guarded-json-api-xAIbFPZh.js +591 -0
- package/dist/index.js +1341 -0
- package/dist/mock-jtSdKDQN.js +135 -0
- package/dist/plivo-L-JTeuEc.js +392 -0
- package/dist/realtime-handler-5pSItXxX.js +1227 -0
- package/dist/realtime-transcription.runtime-CAbQKwCN.js +2 -0
- package/dist/realtime-voice.runtime-vCpCAutg.js +2 -0
- package/dist/response-generator-B-MjbtsM.js +199 -0
- package/dist/runtime-api.js +6 -0
- package/dist/runtime-entry-ohPMJR46.js +3435 -0
- package/dist/runtime-entry.js +2 -0
- package/dist/setup-api.js +37 -0
- package/dist/telnyx-BWr9EZ4x.js +278 -0
- package/dist/twilio-D9B0zY1k.js +679 -0
- package/index.test.ts +1075 -0
- package/index.ts +863 -0
- package/klaw.plugin.json +30 -133
- package/package.json +3 -3
- package/runtime-api.ts +20 -0
- package/runtime-entry.ts +1 -0
- package/setup-api.ts +47 -0
- package/src/allowlist.test.ts +18 -0
- package/src/allowlist.ts +19 -0
- package/src/cli.test.ts +12 -0
- package/src/cli.ts +866 -0
- package/src/config-compat.test.ts +130 -0
- package/src/config-compat.ts +227 -0
- package/src/config.test.ts +542 -0
- package/src/config.ts +883 -0
- package/src/core-bridge.ts +14 -0
- package/src/deep-merge.test.ts +40 -0
- package/src/deep-merge.ts +23 -0
- package/src/gateway-continue-operation.ts +200 -0
- package/src/http-headers.test.ts +16 -0
- package/src/http-headers.ts +15 -0
- package/src/manager/context.ts +50 -0
- package/src/manager/events.test.ts +578 -0
- package/src/manager/events.ts +332 -0
- package/src/manager/lifecycle.ts +53 -0
- package/src/manager/lookup.test.ts +52 -0
- package/src/manager/lookup.ts +35 -0
- package/src/manager/outbound.test.ts +629 -0
- package/src/manager/outbound.ts +508 -0
- package/src/manager/state.ts +48 -0
- package/src/manager/store.ts +107 -0
- package/src/manager/timers.test.ts +127 -0
- package/src/manager/timers.ts +113 -0
- package/src/manager/twiml.test.ts +13 -0
- package/src/manager/twiml.ts +17 -0
- package/src/manager.closed-loop.test.ts +259 -0
- package/src/manager.inbound-allowlist.test.ts +183 -0
- package/src/manager.notify.test.ts +390 -0
- package/src/manager.restore.test.ts +310 -0
- package/src/manager.test-harness.ts +127 -0
- package/src/manager.ts +441 -0
- package/src/media-stream.test.ts +953 -0
- package/src/media-stream.ts +876 -0
- package/src/providers/base.ts +99 -0
- package/src/providers/mock.test.ts +86 -0
- package/src/providers/mock.ts +185 -0
- package/src/providers/plivo.test.ts +93 -0
- package/src/providers/plivo.ts +601 -0
- package/src/providers/shared/call-status.test.ts +24 -0
- package/src/providers/shared/call-status.ts +24 -0
- package/src/providers/shared/guarded-json-api.test.ts +127 -0
- package/src/providers/shared/guarded-json-api.ts +49 -0
- package/src/providers/telnyx.test.ts +489 -0
- package/src/providers/telnyx.ts +419 -0
- package/src/providers/twilio/api.test.ts +184 -0
- package/src/providers/twilio/api.ts +100 -0
- package/src/providers/twilio/twiml-policy.test.ts +84 -0
- package/src/providers/twilio/twiml-policy.ts +87 -0
- package/src/providers/twilio/webhook.ts +34 -0
- package/src/providers/twilio.test.ts +607 -0
- package/src/providers/twilio.ts +861 -0
- package/src/providers/twilio.types.ts +17 -0
- package/src/realtime-agent-context.test.ts +101 -0
- package/src/realtime-agent-context.ts +149 -0
- package/src/realtime-defaults.ts +3 -0
- package/src/realtime-fast-context.test.ts +74 -0
- package/src/realtime-fast-context.ts +27 -0
- package/src/realtime-transcription.runtime.ts +4 -0
- package/src/realtime-voice.runtime.ts +5 -0
- package/src/response-generator.test.ts +385 -0
- package/src/response-generator.ts +348 -0
- package/src/response-model.test.ts +71 -0
- package/src/response-model.ts +23 -0
- package/src/runtime.test.ts +625 -0
- package/src/runtime.ts +528 -0
- package/src/telephony-audio.test.ts +61 -0
- package/src/telephony-audio.ts +12 -0
- package/src/telephony-tts.test.ts +196 -0
- package/src/telephony-tts.ts +235 -0
- package/src/test-fixtures.ts +82 -0
- package/src/tts-provider-voice.test.ts +34 -0
- package/src/tts-provider-voice.ts +21 -0
- package/src/tunnel.test.ts +173 -0
- package/src/tunnel.ts +314 -0
- package/src/types.ts +311 -0
- package/src/utils.test.ts +17 -0
- package/src/utils.ts +14 -0
- package/src/voice-mapping.test.ts +32 -0
- package/src/voice-mapping.ts +65 -0
- package/src/webhook/realtime-audio-pacer.test.ts +146 -0
- package/src/webhook/realtime-audio-pacer.ts +204 -0
- package/src/webhook/realtime-handler.test.ts +1450 -0
- package/src/webhook/realtime-handler.ts +1382 -0
- package/src/webhook/stale-call-reaper.test.ts +89 -0
- package/src/webhook/stale-call-reaper.ts +38 -0
- package/src/webhook/stream-frame-adapter.test.ts +187 -0
- package/src/webhook/stream-frame-adapter.ts +219 -0
- package/src/webhook/tailscale.test.ts +216 -0
- package/src/webhook/tailscale.ts +129 -0
- package/src/webhook-exposure.test.ts +33 -0
- package/src/webhook-exposure.ts +84 -0
- package/src/webhook-security.test.ts +813 -0
- package/src/webhook-security.ts +982 -0
- package/src/webhook.hangup-once.lifecycle.test.ts +179 -0
- package/src/webhook.test.ts +1615 -0
- package/src/webhook.ts +933 -0
- package/src/webhook.types.ts +5 -0
- package/src/websocket-test-support.ts +72 -0
- package/tsconfig.json +16 -0
- package/api.js +0 -7
- package/cli-metadata.js +0 -7
- package/index.js +0 -7
- package/runtime-api.js +0 -7
- package/runtime-entry.js +0 -7
- package/setup-api.js +0 -7
package/klaw.plugin.json
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "voice-call",
|
|
3
|
-
"commandAliases": [
|
|
4
|
-
{
|
|
5
|
-
"name": "voicecall"
|
|
6
|
-
}
|
|
7
|
-
],
|
|
3
|
+
"commandAliases": [{ "name": "voicecall" }],
|
|
8
4
|
"activation": {
|
|
9
5
|
"onStartup": true,
|
|
10
|
-
"onCommands": [
|
|
11
|
-
"voicecall"
|
|
12
|
-
]
|
|
6
|
+
"onCommands": ["voicecall"]
|
|
13
7
|
},
|
|
14
8
|
"contracts": {
|
|
15
|
-
"tools": [
|
|
16
|
-
"voice_call"
|
|
17
|
-
]
|
|
9
|
+
"tools": ["voice_call"]
|
|
18
10
|
},
|
|
19
11
|
"channelEnvVars": {
|
|
20
12
|
"voice-call": [
|
|
@@ -269,12 +261,7 @@
|
|
|
269
261
|
},
|
|
270
262
|
"provider": {
|
|
271
263
|
"type": "string",
|
|
272
|
-
"enum": [
|
|
273
|
-
"telnyx",
|
|
274
|
-
"twilio",
|
|
275
|
-
"plivo",
|
|
276
|
-
"mock"
|
|
277
|
-
]
|
|
264
|
+
"enum": ["telnyx", "twilio", "plivo", "mock"]
|
|
278
265
|
},
|
|
279
266
|
"telnyx": {
|
|
280
267
|
"type": "object",
|
|
@@ -299,10 +286,7 @@
|
|
|
299
286
|
"type": "string"
|
|
300
287
|
},
|
|
301
288
|
"authToken": {
|
|
302
|
-
"type": [
|
|
303
|
-
"string",
|
|
304
|
-
"object"
|
|
305
|
-
]
|
|
289
|
+
"type": ["string", "object"]
|
|
306
290
|
}
|
|
307
291
|
}
|
|
308
292
|
},
|
|
@@ -328,12 +312,7 @@
|
|
|
328
312
|
},
|
|
329
313
|
"inboundPolicy": {
|
|
330
314
|
"type": "string",
|
|
331
|
-
"enum": [
|
|
332
|
-
"disabled",
|
|
333
|
-
"allowlist",
|
|
334
|
-
"pairing",
|
|
335
|
-
"open"
|
|
336
|
-
]
|
|
315
|
+
"enum": ["disabled", "allowlist", "pairing", "open"]
|
|
337
316
|
},
|
|
338
317
|
"allowFrom": {
|
|
339
318
|
"type": "array",
|
|
@@ -383,10 +362,7 @@
|
|
|
383
362
|
"properties": {
|
|
384
363
|
"defaultMode": {
|
|
385
364
|
"type": "string",
|
|
386
|
-
"enum": [
|
|
387
|
-
"notify",
|
|
388
|
-
"conversation"
|
|
389
|
-
]
|
|
365
|
+
"enum": ["notify", "conversation"]
|
|
390
366
|
},
|
|
391
367
|
"notifyHangupDelaySec": {
|
|
392
368
|
"type": "integer",
|
|
@@ -440,11 +416,7 @@
|
|
|
440
416
|
"properties": {
|
|
441
417
|
"mode": {
|
|
442
418
|
"type": "string",
|
|
443
|
-
"enum": [
|
|
444
|
-
"off",
|
|
445
|
-
"serve",
|
|
446
|
-
"funnel"
|
|
447
|
-
]
|
|
419
|
+
"enum": ["off", "serve", "funnel"]
|
|
448
420
|
},
|
|
449
421
|
"path": {
|
|
450
422
|
"type": "string"
|
|
@@ -457,12 +429,7 @@
|
|
|
457
429
|
"properties": {
|
|
458
430
|
"provider": {
|
|
459
431
|
"type": "string",
|
|
460
|
-
"enum": [
|
|
461
|
-
"none",
|
|
462
|
-
"ngrok",
|
|
463
|
-
"tailscale-serve",
|
|
464
|
-
"tailscale-funnel"
|
|
465
|
-
]
|
|
432
|
+
"enum": ["none", "ngrok", "tailscale-serve", "tailscale-funnel"]
|
|
466
433
|
},
|
|
467
434
|
"ngrokAuthToken": {
|
|
468
435
|
"type": "string"
|
|
@@ -552,32 +519,15 @@
|
|
|
552
519
|
},
|
|
553
520
|
"toolPolicy": {
|
|
554
521
|
"type": "string",
|
|
555
|
-
"enum": [
|
|
556
|
-
"safe-read-only",
|
|
557
|
-
"owner",
|
|
558
|
-
"none"
|
|
559
|
-
]
|
|
522
|
+
"enum": ["safe-read-only", "owner", "none"]
|
|
560
523
|
},
|
|
561
524
|
"consultPolicy": {
|
|
562
525
|
"type": "string",
|
|
563
|
-
"enum": [
|
|
564
|
-
"auto",
|
|
565
|
-
"substantive",
|
|
566
|
-
"always"
|
|
567
|
-
]
|
|
526
|
+
"enum": ["auto", "substantive", "always"]
|
|
568
527
|
},
|
|
569
528
|
"consultThinkingLevel": {
|
|
570
529
|
"type": "string",
|
|
571
|
-
"enum": [
|
|
572
|
-
"off",
|
|
573
|
-
"minimal",
|
|
574
|
-
"low",
|
|
575
|
-
"medium",
|
|
576
|
-
"high",
|
|
577
|
-
"xhigh",
|
|
578
|
-
"adaptive",
|
|
579
|
-
"max"
|
|
580
|
-
]
|
|
530
|
+
"enum": ["off", "minimal", "low", "medium", "high", "xhigh", "adaptive", "max"]
|
|
581
531
|
},
|
|
582
532
|
"consultFastMode": {
|
|
583
533
|
"type": "boolean"
|
|
@@ -590,9 +540,7 @@
|
|
|
590
540
|
"properties": {
|
|
591
541
|
"type": {
|
|
592
542
|
"type": "string",
|
|
593
|
-
"enum": [
|
|
594
|
-
"function"
|
|
595
|
-
]
|
|
543
|
+
"enum": ["function"]
|
|
596
544
|
},
|
|
597
545
|
"name": {
|
|
598
546
|
"type": "string"
|
|
@@ -606,9 +554,7 @@
|
|
|
606
554
|
"properties": {
|
|
607
555
|
"type": {
|
|
608
556
|
"type": "string",
|
|
609
|
-
"enum": [
|
|
610
|
-
"object"
|
|
611
|
-
]
|
|
557
|
+
"enum": ["object"]
|
|
612
558
|
},
|
|
613
559
|
"properties": {
|
|
614
560
|
"type": "object",
|
|
@@ -621,18 +567,10 @@
|
|
|
621
567
|
}
|
|
622
568
|
}
|
|
623
569
|
},
|
|
624
|
-
"required": [
|
|
625
|
-
"type",
|
|
626
|
-
"properties"
|
|
627
|
-
]
|
|
570
|
+
"required": ["type", "properties"]
|
|
628
571
|
}
|
|
629
572
|
},
|
|
630
|
-
"required": [
|
|
631
|
-
"type",
|
|
632
|
-
"name",
|
|
633
|
-
"description",
|
|
634
|
-
"parameters"
|
|
635
|
-
]
|
|
573
|
+
"required": ["type", "name", "description", "parameters"]
|
|
636
574
|
}
|
|
637
575
|
},
|
|
638
576
|
"fastContext": {
|
|
@@ -655,10 +593,7 @@
|
|
|
655
593
|
"minItems": 1,
|
|
656
594
|
"items": {
|
|
657
595
|
"type": "string",
|
|
658
|
-
"enum": [
|
|
659
|
-
"memory",
|
|
660
|
-
"sessions"
|
|
661
|
-
]
|
|
596
|
+
"enum": ["memory", "sessions"]
|
|
662
597
|
}
|
|
663
598
|
},
|
|
664
599
|
"fallbackToConsult": {
|
|
@@ -716,22 +651,14 @@
|
|
|
716
651
|
"properties": {
|
|
717
652
|
"auto": {
|
|
718
653
|
"type": "string",
|
|
719
|
-
"enum": [
|
|
720
|
-
"off",
|
|
721
|
-
"always",
|
|
722
|
-
"inbound",
|
|
723
|
-
"tagged"
|
|
724
|
-
]
|
|
654
|
+
"enum": ["off", "always", "inbound", "tagged"]
|
|
725
655
|
},
|
|
726
656
|
"enabled": {
|
|
727
657
|
"type": "boolean"
|
|
728
658
|
},
|
|
729
659
|
"mode": {
|
|
730
660
|
"type": "string",
|
|
731
|
-
"enum": [
|
|
732
|
-
"final",
|
|
733
|
-
"all"
|
|
734
|
-
]
|
|
661
|
+
"enum": ["final", "all"]
|
|
735
662
|
},
|
|
736
663
|
"provider": {
|
|
737
664
|
"type": "string"
|
|
@@ -777,10 +704,7 @@
|
|
|
777
704
|
"additionalProperties": false,
|
|
778
705
|
"properties": {
|
|
779
706
|
"apiKey": {
|
|
780
|
-
"type": [
|
|
781
|
-
"string",
|
|
782
|
-
"object"
|
|
783
|
-
]
|
|
707
|
+
"type": ["string", "object"]
|
|
784
708
|
},
|
|
785
709
|
"baseUrl": {
|
|
786
710
|
"type": "string"
|
|
@@ -794,7 +718,7 @@
|
|
|
794
718
|
"speed": {
|
|
795
719
|
"type": "number",
|
|
796
720
|
"minimum": 0.25,
|
|
797
|
-
"maximum": 4
|
|
721
|
+
"maximum": 4.0
|
|
798
722
|
},
|
|
799
723
|
"instructions": {
|
|
800
724
|
"type": "string"
|
|
@@ -806,10 +730,7 @@
|
|
|
806
730
|
"additionalProperties": false,
|
|
807
731
|
"properties": {
|
|
808
732
|
"apiKey": {
|
|
809
|
-
"type": [
|
|
810
|
-
"string",
|
|
811
|
-
"object"
|
|
812
|
-
]
|
|
733
|
+
"type": ["string", "object"]
|
|
813
734
|
},
|
|
814
735
|
"baseUrl": {
|
|
815
736
|
"type": "string"
|
|
@@ -827,11 +748,7 @@
|
|
|
827
748
|
},
|
|
828
749
|
"applyTextNormalization": {
|
|
829
750
|
"type": "string",
|
|
830
|
-
"enum": [
|
|
831
|
-
"auto",
|
|
832
|
-
"on",
|
|
833
|
-
"off"
|
|
834
|
-
]
|
|
751
|
+
"enum": ["auto", "on", "off"]
|
|
835
752
|
},
|
|
836
753
|
"languageCode": {
|
|
837
754
|
"type": "string"
|
|
@@ -948,10 +865,7 @@
|
|
|
948
865
|
"type": "object",
|
|
949
866
|
"properties": {
|
|
950
867
|
"apiKey": {
|
|
951
|
-
"type": [
|
|
952
|
-
"string",
|
|
953
|
-
"object"
|
|
954
|
-
]
|
|
868
|
+
"type": ["string", "object"]
|
|
955
869
|
}
|
|
956
870
|
},
|
|
957
871
|
"additionalProperties": true
|
|
@@ -976,10 +890,7 @@
|
|
|
976
890
|
},
|
|
977
891
|
"sessionScope": {
|
|
978
892
|
"type": "string",
|
|
979
|
-
"enum": [
|
|
980
|
-
"per-phone",
|
|
981
|
-
"per-call"
|
|
982
|
-
]
|
|
893
|
+
"enum": ["per-phone", "per-call"]
|
|
983
894
|
},
|
|
984
895
|
"responseModel": {
|
|
985
896
|
"type": "string"
|
|
@@ -994,27 +905,13 @@
|
|
|
994
905
|
}
|
|
995
906
|
},
|
|
996
907
|
"configContracts": {
|
|
997
|
-
"compatibilityMigrationPaths": [
|
|
998
|
-
"plugins.entries.voice-call.config"
|
|
999
|
-
],
|
|
908
|
+
"compatibilityMigrationPaths": ["plugins.entries.voice-call.config"],
|
|
1000
909
|
"secretInputs": {
|
|
1001
910
|
"paths": [
|
|
1002
|
-
{
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
{
|
|
1007
|
-
"path": "realtime.providers.*.apiKey",
|
|
1008
|
-
"expected": "string"
|
|
1009
|
-
},
|
|
1010
|
-
{
|
|
1011
|
-
"path": "streaming.providers.*.apiKey",
|
|
1012
|
-
"expected": "string"
|
|
1013
|
-
},
|
|
1014
|
-
{
|
|
1015
|
-
"path": "tts.providers.*.apiKey",
|
|
1016
|
-
"expected": "string"
|
|
1017
|
-
}
|
|
911
|
+
{ "path": "twilio.authToken", "expected": "string" },
|
|
912
|
+
{ "path": "realtime.providers.*.apiKey", "expected": "string" },
|
|
913
|
+
{ "path": "streaming.providers.*.apiKey", "expected": "string" },
|
|
914
|
+
{ "path": "tts.providers.*.apiKey", "expected": "string" }
|
|
1018
915
|
]
|
|
1019
916
|
}
|
|
1020
917
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kodelyth/voice-call",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.42",
|
|
4
4
|
"description": "Klaw voice-call plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@kodelyth/plugin-sdk": "1.0.1",
|
|
18
|
-
"@kodelyth/klaw": "2026.5.
|
|
18
|
+
"@kodelyth/klaw": "2026.5.42"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@kodelyth/klaw": ">=2026.5.19"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"klaw": {
|
|
29
29
|
"extensions": [
|
|
30
|
-
"./index.
|
|
30
|
+
"./index.ts"
|
|
31
31
|
],
|
|
32
32
|
"install": {
|
|
33
33
|
"npmSpec": "@kodelyth/voice-call",
|
package/runtime-api.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Private runtime barrel for the bundled Voice Call extension.
|
|
2
|
+
// Keep this barrel thin and aligned with the local extension surface.
|
|
3
|
+
|
|
4
|
+
export { definePluginEntry } from "klaw/plugin-sdk/plugin-entry";
|
|
5
|
+
export type { KlawPluginApi } from "klaw/plugin-sdk/plugin-entry";
|
|
6
|
+
export type { GatewayRequestHandlerOptions } from "klaw/plugin-sdk/gateway-runtime";
|
|
7
|
+
export {
|
|
8
|
+
isRequestBodyLimitError,
|
|
9
|
+
readRequestBodyWithLimit,
|
|
10
|
+
requestBodyErrorToText,
|
|
11
|
+
} from "klaw/plugin-sdk/webhook-request-guards";
|
|
12
|
+
export { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "klaw/plugin-sdk/ssrf-runtime";
|
|
13
|
+
export type { SessionEntry } from "klaw/plugin-sdk/session-store-runtime";
|
|
14
|
+
export {
|
|
15
|
+
TtsAutoSchema,
|
|
16
|
+
TtsConfigSchema,
|
|
17
|
+
TtsModeSchema,
|
|
18
|
+
TtsProviderSchema,
|
|
19
|
+
} from "klaw/plugin-sdk/tts-runtime";
|
|
20
|
+
export { sleep } from "klaw/plugin-sdk/runtime-env";
|
package/runtime-entry.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createVoiceCallRuntime, type VoiceCallRuntime } from "./src/runtime.js";
|
package/setup-api.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { KlawConfig } from "klaw/plugin-sdk/plugin-entry";
|
|
2
|
+
import { definePluginEntry } from "klaw/plugin-sdk/plugin-entry";
|
|
3
|
+
import { isRecord } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { migrateVoiceCallLegacyConfigInput } from "./config-api.js";
|
|
5
|
+
|
|
6
|
+
function migrateVoiceCallPluginConfig(config: KlawConfig): {
|
|
7
|
+
config: KlawConfig;
|
|
8
|
+
changes: string[];
|
|
9
|
+
} | null {
|
|
10
|
+
const rawVoiceCallConfig = config.plugins?.entries?.["voice-call"]?.config;
|
|
11
|
+
if (!isRecord(rawVoiceCallConfig)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const migration = migrateVoiceCallLegacyConfigInput({
|
|
15
|
+
value: rawVoiceCallConfig,
|
|
16
|
+
configPathPrefix: "plugins.entries.voice-call.config",
|
|
17
|
+
});
|
|
18
|
+
if (migration.changes.length === 0) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const plugins = structuredClone(config.plugins ?? {});
|
|
22
|
+
const entries = { ...plugins.entries };
|
|
23
|
+
const existingVoiceCallEntry = isRecord(entries["voice-call"])
|
|
24
|
+
? (entries["voice-call"] as Record<string, unknown>)
|
|
25
|
+
: {};
|
|
26
|
+
entries["voice-call"] = {
|
|
27
|
+
...existingVoiceCallEntry,
|
|
28
|
+
config: migration.config,
|
|
29
|
+
};
|
|
30
|
+
plugins.entries = entries;
|
|
31
|
+
return {
|
|
32
|
+
config: {
|
|
33
|
+
...config,
|
|
34
|
+
plugins,
|
|
35
|
+
},
|
|
36
|
+
changes: migration.changes,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default definePluginEntry({
|
|
41
|
+
id: "voice-call",
|
|
42
|
+
name: "Voice Call Setup",
|
|
43
|
+
description: "Lightweight Voice Call setup hooks",
|
|
44
|
+
register(api) {
|
|
45
|
+
api.registerConfigMigration((config) => migrateVoiceCallPluginConfig(config));
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isAllowlistedCaller, normalizePhoneNumber } from "./allowlist.js";
|
|
3
|
+
|
|
4
|
+
describe("voice-call allowlist", () => {
|
|
5
|
+
it("normalizes phone numbers by stripping non-digits", () => {
|
|
6
|
+
expect(normalizePhoneNumber("+1 (415) 555-0123")).toBe("14155550123");
|
|
7
|
+
expect(normalizePhoneNumber(" 020-7946-0958 ")).toBe("02079460958");
|
|
8
|
+
expect(normalizePhoneNumber("")).toBe("");
|
|
9
|
+
expect(normalizePhoneNumber()).toBe("");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("matches normalized allowlist entries and rejects blank callers", () => {
|
|
13
|
+
expect(isAllowlistedCaller("14155550123", ["+1 (415) 555-0123", " 020-7946-0958 "])).toBe(true);
|
|
14
|
+
expect(isAllowlistedCaller("02079460958", ["+1 (415) 555-0123", " 020-7946-0958 "])).toBe(true);
|
|
15
|
+
expect(isAllowlistedCaller("", ["+1 (415) 555-0123"])).toBe(false);
|
|
16
|
+
expect(isAllowlistedCaller("14155550123", ["", "abc"])).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/allowlist.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function normalizePhoneNumber(input?: string): string {
|
|
2
|
+
if (!input) {
|
|
3
|
+
return "";
|
|
4
|
+
}
|
|
5
|
+
return input.replace(/\D/g, "");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isAllowlistedCaller(
|
|
9
|
+
normalizedFrom: string,
|
|
10
|
+
allowFrom: string[] | undefined,
|
|
11
|
+
): boolean {
|
|
12
|
+
if (!normalizedFrom) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return (allowFrom ?? []).some((num) => {
|
|
16
|
+
const normalizedAllow = normalizePhoneNumber(num);
|
|
17
|
+
return normalizedAllow !== "" && normalizedAllow === normalizedFrom;
|
|
18
|
+
});
|
|
19
|
+
}
|
package/src/cli.test.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { testing } from "./cli.js";
|
|
3
|
+
|
|
4
|
+
describe("voice-call CLI gateway fallback", () => {
|
|
5
|
+
it("treats abnormal local gateway closes as standalone-runtime fallback candidates", () => {
|
|
6
|
+
expect(
|
|
7
|
+
testing.isGatewayUnavailableForLocalFallback(
|
|
8
|
+
new Error("gateway closed (1006 abnormal closure (no close frame)): no close reason"),
|
|
9
|
+
),
|
|
10
|
+
).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
});
|