@sinch/functions-runtime 0.1.0-beta.28 → 0.2.1-beta
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 +166 -207
- package/dist/bin/sinch-runtime.js +454 -6
- package/dist/bin/sinch-runtime.js.map +1 -1
- package/dist/index.d.ts +91 -5
- package/dist/index.js +343 -71
- package/dist/index.js.map +1 -1
- package/package.json +5 -13
|
@@ -6,6 +6,12 @@ import { createRequire as createRequire3 } from "module";
|
|
|
6
6
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
7
7
|
import fs3 from "fs";
|
|
8
8
|
|
|
9
|
+
// ../runtime-shared/dist/ai/connect-agent.js
|
|
10
|
+
var AgentProvider;
|
|
11
|
+
(function(AgentProvider2) {
|
|
12
|
+
AgentProvider2["ElevenLabs"] = "elevenlabs";
|
|
13
|
+
})(AgentProvider || (AgentProvider = {}));
|
|
14
|
+
|
|
9
15
|
// ../runtime-shared/dist/host/middleware.js
|
|
10
16
|
import { createRequire } from "module";
|
|
11
17
|
var requireCjs = createRequire(import.meta.url);
|
|
@@ -522,6 +528,65 @@ function setupRequestHandler(app, options = {}) {
|
|
|
522
528
|
});
|
|
523
529
|
}
|
|
524
530
|
|
|
531
|
+
// ../runtime-shared/dist/sinch/index.js
|
|
532
|
+
import { SinchClient, validateAuthenticationHeader } from "@sinch/sdk-core";
|
|
533
|
+
|
|
534
|
+
// ../runtime-shared/dist/ai/elevenlabs/state.js
|
|
535
|
+
var ElevenLabsStateManager = class {
|
|
536
|
+
state = {
|
|
537
|
+
isConfigured: false
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Get the current state
|
|
541
|
+
*/
|
|
542
|
+
getState() {
|
|
543
|
+
return { ...this.state };
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Check if ElevenLabs is configured
|
|
547
|
+
*/
|
|
548
|
+
isConfigured() {
|
|
549
|
+
return this.state.isConfigured;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Update state with auto-configuration results
|
|
553
|
+
*/
|
|
554
|
+
setConfigured(data) {
|
|
555
|
+
this.state = {
|
|
556
|
+
...data,
|
|
557
|
+
isConfigured: true,
|
|
558
|
+
configuredAt: /* @__PURE__ */ new Date()
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Clear the configuration state
|
|
563
|
+
*/
|
|
564
|
+
clear() {
|
|
565
|
+
this.state = {
|
|
566
|
+
isConfigured: false
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Get the phone number ID for making calls
|
|
571
|
+
*/
|
|
572
|
+
getPhoneNumberId() {
|
|
573
|
+
return this.state.phoneNumberId;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Get the SIP address for connecting to the agent
|
|
577
|
+
*/
|
|
578
|
+
getSipAddress() {
|
|
579
|
+
return this.state.sipAddress;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Get the configured agent ID
|
|
583
|
+
*/
|
|
584
|
+
getAgentId() {
|
|
585
|
+
return this.state.agentId;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
var ElevenLabsState = new ElevenLabsStateManager();
|
|
589
|
+
|
|
525
590
|
// ../runtime-shared/dist/utils/templateRender.js
|
|
526
591
|
import fs from "fs";
|
|
527
592
|
import path from "path";
|
|
@@ -611,6 +676,382 @@ function createCacheClient(_projectId, _functionName) {
|
|
|
611
676
|
return new LocalCache();
|
|
612
677
|
}
|
|
613
678
|
|
|
679
|
+
// src/tunnel/index.ts
|
|
680
|
+
import WebSocket from "ws";
|
|
681
|
+
import axios from "axios";
|
|
682
|
+
|
|
683
|
+
// src/tunnel/webhook-config.ts
|
|
684
|
+
import { SinchClient as SinchClient2 } from "@sinch/sdk-core";
|
|
685
|
+
async function configureConversationWebhooks(tunnelUrl, config) {
|
|
686
|
+
try {
|
|
687
|
+
const conversationAppId = process.env.CONVERSATION_APP_ID;
|
|
688
|
+
const projectId = process.env.PROJECT_ID;
|
|
689
|
+
const keyId = process.env.KEY_ID;
|
|
690
|
+
const keySecret = process.env.KEY_SECRET;
|
|
691
|
+
if (!conversationAppId || !projectId || !keyId || !keySecret) {
|
|
692
|
+
console.log("\u{1F4A1} Conversation API not fully configured - skipping webhook setup");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const webhookUrl = `${tunnelUrl}/conversation`;
|
|
696
|
+
console.log(`\u{1F4AC} Conversation webhook URL: ${webhookUrl}`);
|
|
697
|
+
const sinchClient = new SinchClient2({
|
|
698
|
+
projectId,
|
|
699
|
+
keyId,
|
|
700
|
+
keySecret
|
|
701
|
+
});
|
|
702
|
+
const webhooksResult = await sinchClient.conversation.webhooks.list({ app_id: conversationAppId });
|
|
703
|
+
const existingWebhooks = webhooksResult.webhooks || [];
|
|
704
|
+
const tunnelWebhooks = existingWebhooks.filter(
|
|
705
|
+
(w) => w.target?.includes("/api/ingress/")
|
|
706
|
+
);
|
|
707
|
+
for (const staleWebhook of tunnelWebhooks) {
|
|
708
|
+
try {
|
|
709
|
+
await sinchClient.conversation.webhooks.delete({ webhook_id: staleWebhook.id });
|
|
710
|
+
console.log(`\u{1F9F9} Cleaned up stale tunnel webhook: ${staleWebhook.id}`);
|
|
711
|
+
} catch (err) {
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const createResult = await sinchClient.conversation.webhooks.create({
|
|
715
|
+
webhookCreateRequestBody: {
|
|
716
|
+
app_id: conversationAppId,
|
|
717
|
+
target: webhookUrl,
|
|
718
|
+
target_type: "HTTP",
|
|
719
|
+
triggers: ["MESSAGE_INBOUND"]
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
config.conversationWebhookId = createResult.id;
|
|
723
|
+
console.log(`\u2705 Created Conversation webhook: ${webhookUrl}`);
|
|
724
|
+
console.log("\u{1F4AC} Send a message to your Conversation app to test!");
|
|
725
|
+
} catch (error) {
|
|
726
|
+
console.log("\u26A0\uFE0F Could not configure Conversation webhooks:", error.message);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
async function cleanupConversationWebhook(config) {
|
|
730
|
+
if (!config.conversationWebhookId) return;
|
|
731
|
+
try {
|
|
732
|
+
const conversationAppId = process.env.CONVERSATION_APP_ID;
|
|
733
|
+
const projectId = process.env.PROJECT_ID;
|
|
734
|
+
const keyId = process.env.KEY_ID;
|
|
735
|
+
const keySecret = process.env.KEY_SECRET;
|
|
736
|
+
if (!conversationAppId || !projectId || !keyId || !keySecret) return;
|
|
737
|
+
const sinchClient = new SinchClient2({
|
|
738
|
+
projectId,
|
|
739
|
+
keyId,
|
|
740
|
+
keySecret
|
|
741
|
+
});
|
|
742
|
+
await sinchClient.conversation.webhooks.delete({ webhook_id: config.conversationWebhookId });
|
|
743
|
+
console.log("\u{1F9F9} Cleaned up tunnel webhook");
|
|
744
|
+
config.conversationWebhookId = void 0;
|
|
745
|
+
} catch (error) {
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async function configureElevenLabs() {
|
|
749
|
+
try {
|
|
750
|
+
const agentId = process.env.ELEVENLABS_AGENT_ID;
|
|
751
|
+
const apiKey = process.env.ELEVENLABS_API_KEY;
|
|
752
|
+
if (!agentId || !apiKey) {
|
|
753
|
+
console.log("\u{1F4A1} ElevenLabs not fully configured - skipping auto-configuration");
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
void apiKey;
|
|
757
|
+
console.log("\u{1F916} ElevenLabs auto-configuration enabled");
|
|
758
|
+
console.log(` Agent ID: ${agentId}`);
|
|
759
|
+
} catch (error) {
|
|
760
|
+
console.log("\u26A0\uFE0F Could not configure ElevenLabs:", error.message);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/tunnel/index.ts
|
|
765
|
+
var TUNNEL_GATEWAY_DEFAULT = "https://tunnel.fn.sinch.com";
|
|
766
|
+
var TunnelClient = class {
|
|
767
|
+
ws = null;
|
|
768
|
+
tunnelUrl = null;
|
|
769
|
+
tunnelId = null;
|
|
770
|
+
isConnected = false;
|
|
771
|
+
reconnectAttempts = 0;
|
|
772
|
+
maxReconnectAttempts = 10;
|
|
773
|
+
reconnectDelay = 5e3;
|
|
774
|
+
heartbeatInterval = null;
|
|
775
|
+
localPort;
|
|
776
|
+
webhookConfig = {};
|
|
777
|
+
welcomeResolver = null;
|
|
778
|
+
constructor(localPort = 3e3) {
|
|
779
|
+
this.localPort = localPort;
|
|
780
|
+
}
|
|
781
|
+
getTunnelGatewayUrl() {
|
|
782
|
+
const explicitUrl = process.env.TUNNEL_GATEWAY_URL;
|
|
783
|
+
if (explicitUrl) {
|
|
784
|
+
return explicitUrl;
|
|
785
|
+
}
|
|
786
|
+
return TUNNEL_GATEWAY_DEFAULT;
|
|
787
|
+
}
|
|
788
|
+
generateTunnelId() {
|
|
789
|
+
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
790
|
+
const timestamp = Date.now();
|
|
791
|
+
let timestampPart = "";
|
|
792
|
+
let t = timestamp;
|
|
793
|
+
for (let i = 0; i < 10; i++) {
|
|
794
|
+
timestampPart = ENCODING[t % 32] + timestampPart;
|
|
795
|
+
t = Math.floor(t / 32);
|
|
796
|
+
}
|
|
797
|
+
const randomBytes = new Uint8Array(10);
|
|
798
|
+
crypto.getRandomValues(randomBytes);
|
|
799
|
+
let randomPart = "";
|
|
800
|
+
for (let i = 0; i < 10; i++) {
|
|
801
|
+
const byte = randomBytes[i];
|
|
802
|
+
randomPart += ENCODING[byte >> 3];
|
|
803
|
+
if (randomPart.length < 16) {
|
|
804
|
+
randomPart += ENCODING[(byte & 7) << 2 | (i + 1 < 10 ? randomBytes[i + 1] >> 6 : 0)];
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
randomPart = randomPart.substring(0, 16);
|
|
808
|
+
return timestampPart + randomPart;
|
|
809
|
+
}
|
|
810
|
+
async connect() {
|
|
811
|
+
if (process.env.SINCH_TUNNEL !== "true") {
|
|
812
|
+
console.log("Tunnel is disabled (set SINCH_TUNNEL=true to enable)");
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const gatewayUrl = this.getTunnelGatewayUrl();
|
|
816
|
+
this.tunnelId = this.generateTunnelId();
|
|
817
|
+
const gatewayUri = new URL(gatewayUrl);
|
|
818
|
+
const wsUrl = new URL(gatewayUrl);
|
|
819
|
+
wsUrl.protocol = gatewayUri.protocol === "https:" ? "wss:" : "ws:";
|
|
820
|
+
wsUrl.pathname = "/ws";
|
|
821
|
+
wsUrl.searchParams.set("tunnel", this.tunnelId);
|
|
822
|
+
const tunnelEndpoint = wsUrl.toString();
|
|
823
|
+
console.log(`Connecting to tunnel gateway at ${tunnelEndpoint}...`);
|
|
824
|
+
try {
|
|
825
|
+
this.ws = new WebSocket(tunnelEndpoint);
|
|
826
|
+
const welcomePromise = new Promise((resolve, reject) => {
|
|
827
|
+
this.welcomeResolver = resolve;
|
|
828
|
+
setTimeout(() => reject(new Error("Timed out waiting for welcome message")), 1e4);
|
|
829
|
+
});
|
|
830
|
+
this.ws.on("open", () => {
|
|
831
|
+
this.isConnected = true;
|
|
832
|
+
this.reconnectAttempts = 0;
|
|
833
|
+
console.log("WebSocket connected, waiting for welcome message...");
|
|
834
|
+
});
|
|
835
|
+
this.ws.on("message", async (data) => {
|
|
836
|
+
try {
|
|
837
|
+
const message = JSON.parse(data.toString());
|
|
838
|
+
await this.handleMessage(message);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
console.error("Error processing tunnel message:", error);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
this.ws.on("close", async () => {
|
|
844
|
+
this.isConnected = false;
|
|
845
|
+
console.log("Tunnel connection closed");
|
|
846
|
+
this.stopHeartbeat();
|
|
847
|
+
this.scheduleReconnect();
|
|
848
|
+
});
|
|
849
|
+
this.ws.on("error", (error) => {
|
|
850
|
+
console.error("Tunnel connection error:", error.message);
|
|
851
|
+
});
|
|
852
|
+
await welcomePromise;
|
|
853
|
+
if (!this.tunnelUrl) {
|
|
854
|
+
throw new Error("Did not receive tunnel URL from gateway");
|
|
855
|
+
}
|
|
856
|
+
console.log("Tunnel connected successfully!");
|
|
857
|
+
this.startHeartbeat();
|
|
858
|
+
await this.configureWebhooks();
|
|
859
|
+
} catch (error) {
|
|
860
|
+
console.error("Failed to establish tunnel connection:", error.message);
|
|
861
|
+
this.scheduleReconnect();
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
async handleMessage(message) {
|
|
865
|
+
switch (message.type) {
|
|
866
|
+
case "welcome":
|
|
867
|
+
this.handleWelcomeMessage(message);
|
|
868
|
+
break;
|
|
869
|
+
case "request":
|
|
870
|
+
await this.handleRequest(message);
|
|
871
|
+
break;
|
|
872
|
+
case "ping":
|
|
873
|
+
this.sendPong();
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
handleWelcomeMessage(message) {
|
|
878
|
+
this.tunnelId = message.tunnelId || null;
|
|
879
|
+
this.tunnelUrl = message.publicUrl || null;
|
|
880
|
+
console.log(`Received welcome: tunnelId=${this.tunnelId}`);
|
|
881
|
+
if (this.welcomeResolver) {
|
|
882
|
+
this.welcomeResolver(true);
|
|
883
|
+
this.welcomeResolver = null;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async handleRequest(message) {
|
|
887
|
+
console.log(`Forwarding ${message.method} request to ${message.path}`);
|
|
888
|
+
try {
|
|
889
|
+
const localUrl = `http://localhost:${this.localPort}${message.path}${message.query || ""}`;
|
|
890
|
+
const axiosConfig = {
|
|
891
|
+
method: message.method,
|
|
892
|
+
url: localUrl,
|
|
893
|
+
headers: {}
|
|
894
|
+
};
|
|
895
|
+
if (message.headers) {
|
|
896
|
+
axiosConfig.headers = { ...message.headers };
|
|
897
|
+
}
|
|
898
|
+
if (message.body) {
|
|
899
|
+
axiosConfig.data = message.body;
|
|
900
|
+
}
|
|
901
|
+
const response = await axios(axiosConfig);
|
|
902
|
+
const headers = {};
|
|
903
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
904
|
+
if (value) {
|
|
905
|
+
const normalizedKey = key.toLowerCase() === "content-type" ? "Content-Type" : key.toLowerCase() === "content-length" ? "Content-Length" : key;
|
|
906
|
+
headers[normalizedKey] = String(value);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (!headers["Content-Type"] && response.data) {
|
|
910
|
+
headers["Content-Type"] = "application/json";
|
|
911
|
+
}
|
|
912
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
913
|
+
const responseMessage = {
|
|
914
|
+
type: "response",
|
|
915
|
+
id: message.id,
|
|
916
|
+
statusCode: response.status,
|
|
917
|
+
headers,
|
|
918
|
+
body
|
|
919
|
+
};
|
|
920
|
+
this.ws?.send(JSON.stringify(responseMessage));
|
|
921
|
+
} catch (error) {
|
|
922
|
+
console.error("Error forwarding request:", error.message);
|
|
923
|
+
const errorResponse = {
|
|
924
|
+
type: "response",
|
|
925
|
+
id: message.id,
|
|
926
|
+
statusCode: error.response?.status || 502,
|
|
927
|
+
headers: { "Content-Type": "text/plain" },
|
|
928
|
+
body: "Error forwarding request to local server"
|
|
929
|
+
};
|
|
930
|
+
this.ws?.send(JSON.stringify(errorResponse));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
sendPong() {
|
|
934
|
+
const pongMessage = { type: "pong" };
|
|
935
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
936
|
+
this.ws.send(JSON.stringify(pongMessage));
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
startHeartbeat() {
|
|
940
|
+
this.heartbeatInterval = setInterval(() => {
|
|
941
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
942
|
+
const pingMessage = { type: "ping" };
|
|
943
|
+
this.ws.send(JSON.stringify(pingMessage));
|
|
944
|
+
}
|
|
945
|
+
}, 3e4);
|
|
946
|
+
}
|
|
947
|
+
stopHeartbeat() {
|
|
948
|
+
if (this.heartbeatInterval) {
|
|
949
|
+
clearInterval(this.heartbeatInterval);
|
|
950
|
+
this.heartbeatInterval = null;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Configure all webhooks (Voice, Conversation, ElevenLabs)
|
|
955
|
+
*/
|
|
956
|
+
async configureWebhooks() {
|
|
957
|
+
const autoConfigVoice = process.env.AUTO_CONFIGURE_VOICE !== "false";
|
|
958
|
+
const autoConfigConversation = process.env.AUTO_CONFIGURE_CONVERSATION !== "false";
|
|
959
|
+
if (autoConfigVoice && process.env.VOICE_APPLICATION_KEY) {
|
|
960
|
+
await this.configureVoiceWebhooks();
|
|
961
|
+
}
|
|
962
|
+
if (autoConfigConversation && process.env.CONVERSATION_APP_ID) {
|
|
963
|
+
await configureConversationWebhooks(this.tunnelUrl, this.webhookConfig);
|
|
964
|
+
}
|
|
965
|
+
if (process.env.ELEVENLABS_AUTO_CONFIGURE === "true") {
|
|
966
|
+
await configureElevenLabs();
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Cleanup webhooks on disconnect
|
|
971
|
+
*/
|
|
972
|
+
async cleanupWebhooks() {
|
|
973
|
+
await cleanupConversationWebhook(this.webhookConfig);
|
|
974
|
+
}
|
|
975
|
+
async configureVoiceWebhooks() {
|
|
976
|
+
try {
|
|
977
|
+
const appKey = process.env.VOICE_APPLICATION_KEY;
|
|
978
|
+
const appSecret = process.env.VOICE_APPLICATION_SECRET;
|
|
979
|
+
if (!appKey || !appSecret) {
|
|
980
|
+
console.log("\u{1F4A1} Voice API not configured - skipping phone number display");
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
const updateUrl = `https://callingapi.sinch.com/v1/configuration/callbacks/applications/${appKey}/`;
|
|
985
|
+
const auth = Buffer.from(`${appKey}:${appSecret}`).toString("base64");
|
|
986
|
+
await axios.post(updateUrl, {
|
|
987
|
+
url: {
|
|
988
|
+
primary: this.tunnelUrl,
|
|
989
|
+
fallback: null
|
|
990
|
+
}
|
|
991
|
+
}, {
|
|
992
|
+
headers: {
|
|
993
|
+
"Authorization": `Basic ${auth}`,
|
|
994
|
+
"Content-Type": "application/json"
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
console.log("\u2705 Updated voice webhook URL");
|
|
998
|
+
} catch (error) {
|
|
999
|
+
console.log("\u26A0\uFE0F Could not update webhook URL:", error.message);
|
|
1000
|
+
}
|
|
1001
|
+
try {
|
|
1002
|
+
const listUrl = `https://callingapi.sinch.com/v1/configuration/numbers/`;
|
|
1003
|
+
const auth = Buffer.from(`${appKey}:${appSecret}`).toString("base64");
|
|
1004
|
+
const response = await axios.get(listUrl, {
|
|
1005
|
+
headers: {
|
|
1006
|
+
"Authorization": `Basic ${auth}`
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
const numbers = response.data?.numbers || [];
|
|
1010
|
+
const appNumbers = numbers.filter((n) => n.applicationkey === appKey);
|
|
1011
|
+
if (appNumbers.length > 0) {
|
|
1012
|
+
console.log("\u{1F4F1} Test Phone Numbers:");
|
|
1013
|
+
appNumbers.forEach((num) => {
|
|
1014
|
+
console.log(` \u260E\uFE0F ${num.number}`);
|
|
1015
|
+
});
|
|
1016
|
+
console.log("\u{1F4A1} Call any of these numbers to test your voice function!");
|
|
1017
|
+
} else {
|
|
1018
|
+
console.log("\u26A0\uFE0F No phone numbers assigned to this application yet");
|
|
1019
|
+
console.log("\u{1F4A1} Add numbers at https://dashboard.sinch.com/voice/apps");
|
|
1020
|
+
}
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
console.log("\u{1F4A1} Could not fetch phone numbers:", error.message);
|
|
1023
|
+
}
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
console.log("\u{1F4A1} Could not fetch phone numbers (Voice API may not be configured)");
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
scheduleReconnect() {
|
|
1029
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
1030
|
+
console.error("Max reconnection attempts reached. Giving up.");
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
this.reconnectAttempts++;
|
|
1034
|
+
const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), 3e4);
|
|
1035
|
+
console.log(`Attempting to reconnect in ${delay / 1e3} seconds...`);
|
|
1036
|
+
setTimeout(() => this.connect(), delay);
|
|
1037
|
+
}
|
|
1038
|
+
async disconnect() {
|
|
1039
|
+
this.stopHeartbeat();
|
|
1040
|
+
await this.cleanupWebhooks();
|
|
1041
|
+
if (this.ws) {
|
|
1042
|
+
this.ws.close();
|
|
1043
|
+
this.ws = null;
|
|
1044
|
+
}
|
|
1045
|
+
this.isConnected = false;
|
|
1046
|
+
}
|
|
1047
|
+
getTunnelUrl() {
|
|
1048
|
+
return this.tunnelUrl;
|
|
1049
|
+
}
|
|
1050
|
+
getIsConnected() {
|
|
1051
|
+
return this.isConnected;
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
614
1055
|
// src/bin/sinch-runtime.ts
|
|
615
1056
|
var requireCjs3 = createRequire3(import.meta.url);
|
|
616
1057
|
function findFunctionPath3() {
|
|
@@ -783,18 +1224,25 @@ async function main() {
|
|
|
783
1224
|
});
|
|
784
1225
|
});
|
|
785
1226
|
displayStartupInfo(config, verbose, port);
|
|
786
|
-
app.listen(port, () => {
|
|
1227
|
+
app.listen(port, async () => {
|
|
787
1228
|
console.log(`Function server running on http://localhost:${port}`);
|
|
788
1229
|
console.log("\nTest endpoints:");
|
|
789
1230
|
console.log(` ICE: POST http://localhost:${port}/ice`);
|
|
790
1231
|
console.log(` PIE: POST http://localhost:${port}/pie`);
|
|
791
1232
|
console.log(` ACE: POST http://localhost:${port}/ace`);
|
|
792
1233
|
console.log(` DICE: POST http://localhost:${port}/dice`);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1234
|
+
await displayDetectedFunctions();
|
|
1235
|
+
if (!verbose) {
|
|
1236
|
+
console.log("\nTip: Set VERBOSE=true or use --verbose for detailed output");
|
|
1237
|
+
}
|
|
1238
|
+
if (process.env.SINCH_TUNNEL === "true") {
|
|
1239
|
+
console.log("\nStarting tunnel...");
|
|
1240
|
+
const tunnelClient = new TunnelClient(port);
|
|
1241
|
+
await tunnelClient.connect();
|
|
1242
|
+
process.on("beforeExit", async () => {
|
|
1243
|
+
await tunnelClient.disconnect();
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
798
1246
|
});
|
|
799
1247
|
}
|
|
800
1248
|
main().catch((error) => {
|