@fonoster/common 0.26.0 → 0.30.0
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/assistants/index.d.ts +2 -6
- package/dist/assistants/index.js +2 -6
- package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
- package/dist/assistants/sessionAssistantSchema.js +112 -0
- package/dist/assistants/testCasesSchema.d.ts +4 -4
- package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
- package/dist/calls/index.js +44 -0
- package/dist/cellsecrets/index.d.ts +91 -0
- package/dist/cellsecrets/index.js +299 -0
- package/dist/config/cli/check.d.ts +6 -0
- package/dist/config/cli/check.js +202 -0
- package/dist/config/cli/envFile.d.ts +23 -0
- package/dist/config/cli/envFile.js +81 -0
- package/dist/config/cli/exposure.d.ts +10 -0
- package/dist/config/cli/exposure.js +93 -0
- package/dist/config/cli/index.d.ts +2 -0
- package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
- package/dist/config/cli/info.d.ts +6 -0
- package/dist/config/cli/info.js +62 -0
- package/dist/config/cli/init.d.ts +9 -0
- package/dist/config/cli/init.js +152 -0
- package/dist/config/cli/main.d.ts +11 -0
- package/dist/config/cli/main.js +155 -0
- package/dist/config/cli/render.d.ts +44 -0
- package/dist/config/cli/render.js +129 -0
- package/dist/config/cli/secrets.d.ts +21 -0
- package/dist/config/cli/secrets.js +117 -0
- package/dist/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
- package/dist/config/cli/types.js +7 -0
- package/dist/config/configHints.d.ts +39 -0
- package/dist/config/configHints.js +61 -0
- package/dist/{voice → config}/index.d.ts +12 -13
- package/dist/config/index.js +46 -0
- package/dist/config/integrationsSchema.d.ts +32 -0
- package/dist/config/integrationsSchema.js +56 -0
- package/dist/config/interpolateEnv.d.ts +34 -0
- package/dist/config/interpolateEnv.js +70 -0
- package/dist/config/loadServiceConfig.d.ts +33 -0
- package/dist/config/loadServiceConfig.js +121 -0
- package/dist/config/logsSchema.d.ts +76 -0
- package/dist/config/logsSchema.js +49 -0
- package/dist/config/natsConnection.d.ts +53 -0
- package/dist/config/natsConnection.js +58 -0
- package/dist/config/parseConfigFile.d.ts +7 -0
- package/dist/config/parseConfigFile.js +60 -0
- package/dist/config/resolveConfigFile.d.ts +17 -0
- package/dist/config/resolveConfigFile.js +54 -0
- package/dist/config/resolveSecretRefs.d.ts +35 -0
- package/dist/config/resolveSecretRefs.js +129 -0
- package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
- package/dist/config/strictDeep.js +58 -0
- package/dist/config/telemetrySchema.d.ts +39 -0
- package/dist/config/telemetrySchema.js +44 -0
- package/dist/config/toJsonSchema.d.ts +30 -0
- package/dist/config/toJsonSchema.js +161 -0
- package/dist/constants.d.ts +1 -4
- package/dist/constants.js +4 -5
- package/dist/envs.d.ts +2 -1
- package/dist/envs.js +13 -11
- package/dist/identity/createAuthInterceptor.d.ts +9 -2
- package/dist/identity/createAuthInterceptor.js +36 -30
- package/dist/identity/hasAccess.d.ts +9 -3
- package/dist/identity/hasAccess.js +32 -8
- package/dist/identity/index.d.ts +1 -0
- package/dist/identity/index.js +1 -0
- package/dist/identity/isValidToken.d.ts +12 -2
- package/dist/identity/isValidToken.js +25 -9
- package/dist/identity/roles.js +22 -6
- package/dist/identity/types.d.ts +6 -1
- package/dist/identity/types.js +1 -0
- package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
- package/dist/identity/verifyAccessToken.js +44 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +14 -2
- package/dist/net/assertPublicDestination.d.ts +52 -0
- package/dist/net/assertPublicDestination.js +342 -0
- package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
- package/dist/{voice → net}/index.js +1 -13
- package/dist/notifications/createSendEmail.js +3 -1
- package/dist/protos/applications.proto +124 -3
- package/dist/protos/authz.proto +23 -4
- package/dist/protos/capabilities.proto +94 -0
- package/dist/protos/identity.proto +26 -1
- package/dist/protos/providers.proto +135 -0
- package/dist/protos/webhooks.proto +140 -0
- package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
- package/dist/regions/hub/index.js +35 -0
- package/dist/regions/hub/natsJwt.d.ts +63 -0
- package/dist/regions/hub/natsJwt.js +195 -0
- package/dist/registry/Registry.d.ts +79 -0
- package/dist/registry/Registry.js +418 -0
- package/dist/registry/cli.d.ts +20 -0
- package/dist/registry/cli.js +424 -0
- package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
- package/dist/registry/features.js +36 -0
- package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
- package/dist/registry/index.js +40 -0
- package/dist/registry/prisma.d.ts +67 -0
- package/dist/registry/prisma.js +391 -0
- package/dist/registry/stores.d.ts +21 -0
- package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
- package/dist/registry/types.d.ts +98 -0
- package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
- package/dist/registry/workspaceKeys.d.ts +41 -0
- package/dist/registry/workspaceKeys.js +64 -0
- package/dist/session/index.d.ts +19 -0
- package/dist/session/index.js +35 -0
- package/dist/session/schema.d.ts +1951 -0
- package/dist/session/schema.js +397 -0
- package/dist/sessions/index.d.ts +93 -0
- package/dist/sessions/index.js +194 -0
- package/dist/telemetry.d.ts +76 -0
- package/dist/telemetry.js +360 -0
- package/dist/types.d.ts +3 -5
- package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
- package/dist/utils/WriteBehind.js +64 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
- package/dist/utils/logUnhandledRejections.js +37 -0
- package/dist/validators/calls.d.ts +2 -2
- package/dist/validators/calls.js +5 -2
- package/dist/validators/identity.d.ts +37 -8
- package/dist/validators/identity.js +22 -3
- package/dist/validators/index.d.ts +1 -0
- package/dist/validators/index.js +1 -0
- package/dist/validators/sip.d.ts +40 -0
- package/dist/validators/sip.js +99 -0
- package/dist/validators/sipnet/credentials.d.ts +2 -2
- package/dist/validators/sipnet/domains.js +7 -7
- package/dist/validators/sipnet/trunks.d.ts +14 -14
- package/dist/validators/sipnet/trunks.js +4 -2
- package/dist/webhooks/Dispatcher.d.ts +87 -0
- package/dist/webhooks/Dispatcher.js +225 -0
- package/dist/webhooks/bus.d.ts +49 -0
- package/dist/webhooks/bus.js +170 -0
- package/dist/webhooks/events.d.ts +66 -0
- package/dist/webhooks/events.js +62 -0
- package/dist/webhooks/index.d.ts +23 -0
- package/dist/webhooks/index.js +39 -0
- package/dist/webhooks/prisma.d.ts +55 -0
- package/dist/webhooks/prisma.js +242 -0
- package/dist/webhooks/signing.d.ts +8 -0
- package/dist/webhooks/signing.js +53 -0
- package/package.json +25 -12
- package/schema/session.v1.json +791 -0
- package/dist/assistants/assistantSchema.d.ts +0 -739
- package/dist/assistants/assistantSchema.js +0 -37
- package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
- package/dist/assistants/conversationSettingsSchema.js +0 -133
- package/dist/assistants/eventsHookSchema.d.ts +0 -38
- package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
- package/dist/assistants/languageModelConfigSchema.js +0 -91
- package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
- package/dist/protos/voice.proto +0 -557
- package/dist/voice/Dial.js +0 -20
- package/dist/voice/Gather.d.ts +0 -37
- package/dist/voice/Gather.js +0 -9
- package/dist/voice/Mute.d.ts +0 -31
- package/dist/voice/Mute.js +0 -9
- package/dist/voice/Play.js +0 -2
- package/dist/voice/PlayDtmf.js +0 -2
- package/dist/voice/PlaybackControl.d.ts +0 -31
- package/dist/voice/PlaybackControl.js +0 -11
- package/dist/voice/Record.d.ts +0 -36
- package/dist/voice/Record.js +0 -7
- package/dist/voice/Say.js +0 -2
- package/dist/voice/StopSay.js +0 -2
- package/dist/voice/Stream.d.ts +0 -53
- package/dist/voice/Stream.js +0 -19
- package/dist/voice/StreamGather.d.ts +0 -34
- package/dist/voice/StreamGather.js +0 -9
- package/dist/voice/Verb.d.ts +0 -27
- package/dist/voice/Verb.js +0 -2
- package/dist/voice/voice.d.ts +0 -171
- package/dist/voice/voice.js +0 -65
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.UnsafeDestinationError = void 0;
|
|
16
|
+
exports.isPrivateAddress = isPrivateAddress;
|
|
17
|
+
exports.assertPublicDestination = assertPublicDestination;
|
|
18
|
+
exports.assertPublicUrlShape = assertPublicUrlShape;
|
|
19
|
+
exports.publicWebSocketGuard = publicWebSocketGuard;
|
|
20
|
+
exports.assertPublicWebSocketUrlShape = assertPublicWebSocketUrlShape;
|
|
21
|
+
exports.safeFetch = safeFetch;
|
|
22
|
+
/**
|
|
23
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
24
|
+
* http://github.com/fonoster/fonoster
|
|
25
|
+
*
|
|
26
|
+
* This file is part of Fonoster
|
|
27
|
+
*
|
|
28
|
+
* Licensed under the MIT License (the "License");
|
|
29
|
+
* you may not use this file except in compliance with
|
|
30
|
+
* the License. You may obtain a copy of the License at
|
|
31
|
+
*
|
|
32
|
+
* https://opensource.org/licenses/MIT
|
|
33
|
+
*
|
|
34
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
35
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
36
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
37
|
+
* See the License for the specific language governing permissions and
|
|
38
|
+
* limitations under the License.
|
|
39
|
+
*/
|
|
40
|
+
const node_dns_1 = __importDefault(require("node:dns"));
|
|
41
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
42
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
43
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
44
|
+
const node_url_1 = require("node:url");
|
|
45
|
+
/* global Response, Headers */
|
|
46
|
+
/**
|
|
47
|
+
* Outbound requests to tenant-chosen URLs (Autopilot HTTP tools, webhooks) must not reach the
|
|
48
|
+
* platform's own network (SSRF, RV-B2): http/https only, and every address the host resolves to
|
|
49
|
+
* must be public. Operators who self-host can allow internal destinations with
|
|
50
|
+
* `FONOSTER_ALLOWED_DESTINATIONS` (comma-separated host names, IPs or CIDRs; default empty).
|
|
51
|
+
*/
|
|
52
|
+
class UnsafeDestinationError extends Error {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "UnsafeDestinationError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.UnsafeDestinationError = UnsafeDestinationError;
|
|
59
|
+
const blocked = new node_net_1.default.BlockList();
|
|
60
|
+
for (const [net4, prefix] of [
|
|
61
|
+
["0.0.0.0", 8], // "this network", 0.0.0.0
|
|
62
|
+
["10.0.0.0", 8],
|
|
63
|
+
["100.64.0.0", 10], // CGNAT
|
|
64
|
+
["127.0.0.0", 8],
|
|
65
|
+
["169.254.0.0", 16], // link-local, cloud metadata
|
|
66
|
+
["172.16.0.0", 12],
|
|
67
|
+
["192.0.0.0", 24],
|
|
68
|
+
["192.0.2.0", 24],
|
|
69
|
+
["192.168.0.0", 16],
|
|
70
|
+
["198.18.0.0", 15],
|
|
71
|
+
["198.51.100.0", 24],
|
|
72
|
+
["203.0.113.0", 24],
|
|
73
|
+
["224.0.0.0", 4], // multicast
|
|
74
|
+
["240.0.0.0", 4] // reserved, broadcast
|
|
75
|
+
])
|
|
76
|
+
blocked.addSubnet(net4, prefix, "ipv4");
|
|
77
|
+
for (const [net6, prefix] of [
|
|
78
|
+
["::", 128],
|
|
79
|
+
["::1", 128],
|
|
80
|
+
["64:ff9b::", 96], // NAT64 can reach IPv4 private ranges
|
|
81
|
+
["100::", 64],
|
|
82
|
+
["2001:db8::", 32],
|
|
83
|
+
["fc00::", 7], // ULA
|
|
84
|
+
["fe80::", 10], // link-local
|
|
85
|
+
["ff00::", 8] // multicast
|
|
86
|
+
])
|
|
87
|
+
blocked.addSubnet(net6, prefix, "ipv6");
|
|
88
|
+
const INTERNAL_SUFFIXES = [
|
|
89
|
+
".localhost",
|
|
90
|
+
".local",
|
|
91
|
+
".internal",
|
|
92
|
+
".intranet",
|
|
93
|
+
".lan",
|
|
94
|
+
".home.arpa",
|
|
95
|
+
".cluster.local",
|
|
96
|
+
".svc"
|
|
97
|
+
];
|
|
98
|
+
/** The IPv4 address inside an IPv4-mapped IPv6 address (::ffff:a.b.c.d or ::ffff:hhhh:hhhh). */
|
|
99
|
+
function mappedV4(ip) {
|
|
100
|
+
const m = /^::ffff:(.+)$/i.exec(ip);
|
|
101
|
+
if (!m)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (node_net_1.default.isIPv4(m[1]))
|
|
104
|
+
return m[1];
|
|
105
|
+
const hex = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(m[1]);
|
|
106
|
+
if (!hex)
|
|
107
|
+
return undefined;
|
|
108
|
+
const hi = parseInt(hex[1], 16);
|
|
109
|
+
const lo = parseInt(hex[2], 16);
|
|
110
|
+
return [hi >> 8, hi & 255, lo >> 8, lo & 255].join(".");
|
|
111
|
+
}
|
|
112
|
+
/** True when the address is loopback, private, link-local, ULA, CGNAT, multicast or reserved. */
|
|
113
|
+
function isPrivateAddress(ip) {
|
|
114
|
+
const bare = ip.replace(/^\[|\]$/g, "").split("%")[0];
|
|
115
|
+
const v4 = mappedV4(bare);
|
|
116
|
+
if (v4)
|
|
117
|
+
return blocked.check(v4, "ipv4");
|
|
118
|
+
if (node_net_1.default.isIPv4(bare))
|
|
119
|
+
return blocked.check(bare, "ipv4");
|
|
120
|
+
if (node_net_1.default.isIPv6(bare))
|
|
121
|
+
return blocked.check(bare, "ipv6");
|
|
122
|
+
return true; // not an address: refuse
|
|
123
|
+
}
|
|
124
|
+
function allowList(policy) {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
return ((_a = policy === null || policy === void 0 ? void 0 : policy.allow) !== null && _a !== void 0 ? _a : ((_b = process.env.FONOSTER_ALLOWED_DESTINATIONS) !== null && _b !== void 0 ? _b : "").split(","))
|
|
127
|
+
.map((s) => s.trim().toLowerCase())
|
|
128
|
+
.filter(Boolean);
|
|
129
|
+
}
|
|
130
|
+
function allowedByOperator(host, ip, allow) {
|
|
131
|
+
var _a;
|
|
132
|
+
for (const entry of allow) {
|
|
133
|
+
if (entry === host)
|
|
134
|
+
return true;
|
|
135
|
+
if (!ip)
|
|
136
|
+
continue;
|
|
137
|
+
const [base, bits] = entry.split("/");
|
|
138
|
+
if (!node_net_1.default.isIP(base))
|
|
139
|
+
continue;
|
|
140
|
+
const family = node_net_1.default.isIPv4(base) ? "ipv4" : "ipv6";
|
|
141
|
+
const list = new node_net_1.default.BlockList();
|
|
142
|
+
const prefix = bits ? Number(bits) : family === "ipv4" ? 32 : 128;
|
|
143
|
+
list.addSubnet(base, prefix, family);
|
|
144
|
+
const target = (_a = mappedV4(ip)) !== null && _a !== void 0 ? _a : ip;
|
|
145
|
+
const targetFamily = node_net_1.default.isIPv4(target) ? "ipv4" : "ipv6";
|
|
146
|
+
if (targetFamily === family && list.check(target, family))
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
function checkAddress(host, ip, allow) {
|
|
152
|
+
if (isPrivateAddress(ip) && !allowedByOperator(host, ip, allow))
|
|
153
|
+
throw new UnsafeDestinationError(`destination ${host} resolves to a non-public address`);
|
|
154
|
+
}
|
|
155
|
+
function hostOf(url) {
|
|
156
|
+
return url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
157
|
+
}
|
|
158
|
+
/** Throws unless the URL is http(s) and its host is public, before any DNS lookup. */
|
|
159
|
+
function checkUrlShape(url, allow) {
|
|
160
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
161
|
+
throw new UnsafeDestinationError(`unsupported scheme ${url.protocol}`);
|
|
162
|
+
const host = hostOf(url);
|
|
163
|
+
if (!host)
|
|
164
|
+
throw new UnsafeDestinationError("missing host");
|
|
165
|
+
if (node_net_1.default.isIP(host)) {
|
|
166
|
+
checkAddress(host, host, allow);
|
|
167
|
+
return host;
|
|
168
|
+
}
|
|
169
|
+
if (allow.includes(host))
|
|
170
|
+
return host;
|
|
171
|
+
const name = host.replace(/\.$/, "");
|
|
172
|
+
if (!name.includes(".") ||
|
|
173
|
+
name === "localhost" ||
|
|
174
|
+
INTERNAL_SUFFIXES.some((s) => name.endsWith(s)))
|
|
175
|
+
throw new UnsafeDestinationError(`destination ${host} is an internal name`);
|
|
176
|
+
return host;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Checks a tenant-chosen URL: http/https, not an internal name, and every resolved address
|
|
180
|
+
* public (or allowed by the operator). Returns the parsed URL.
|
|
181
|
+
*/
|
|
182
|
+
function assertPublicDestination(input, policy) {
|
|
183
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
184
|
+
let url;
|
|
185
|
+
try {
|
|
186
|
+
url = new node_url_1.URL(String(input));
|
|
187
|
+
}
|
|
188
|
+
catch (_a) {
|
|
189
|
+
throw new UnsafeDestinationError("invalid URL");
|
|
190
|
+
}
|
|
191
|
+
const allow = allowList(policy);
|
|
192
|
+
const host = checkUrlShape(url, allow);
|
|
193
|
+
if (node_net_1.default.isIP(host) || allow.includes(host))
|
|
194
|
+
return url;
|
|
195
|
+
let addresses;
|
|
196
|
+
try {
|
|
197
|
+
addresses = yield node_dns_1.default.promises.lookup(host, { all: true });
|
|
198
|
+
}
|
|
199
|
+
catch (_b) {
|
|
200
|
+
throw new UnsafeDestinationError(`cannot resolve ${host}`);
|
|
201
|
+
}
|
|
202
|
+
for (const a of addresses)
|
|
203
|
+
checkAddress(host, a.address, allow);
|
|
204
|
+
return url;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/** Synchronous shape check (scheme, literal IPs, internal names) for request validation. */
|
|
208
|
+
function assertPublicUrlShape(input, policy) {
|
|
209
|
+
let url;
|
|
210
|
+
try {
|
|
211
|
+
url = new node_url_1.URL(String(input));
|
|
212
|
+
}
|
|
213
|
+
catch (_a) {
|
|
214
|
+
throw new UnsafeDestinationError("invalid URL");
|
|
215
|
+
}
|
|
216
|
+
checkUrlShape(url, allowList(policy));
|
|
217
|
+
return url;
|
|
218
|
+
}
|
|
219
|
+
// The connection is checked when the socket resolves the name, so a DNS answer that changes
|
|
220
|
+
// between the check and the request (rebinding) cannot reach a private address.
|
|
221
|
+
function guardedLookup(host, allow) {
|
|
222
|
+
return (hostname, options, callback) => {
|
|
223
|
+
node_dns_1.default.lookup(hostname, Object.assign(Object.assign({}, options), { all: true }), (err, addrs) => {
|
|
224
|
+
if (err)
|
|
225
|
+
return callback(err, "", 4);
|
|
226
|
+
const list = addrs;
|
|
227
|
+
try {
|
|
228
|
+
for (const a of list)
|
|
229
|
+
checkAddress(host, a.address, allow);
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
return callback(e, "", 4);
|
|
233
|
+
}
|
|
234
|
+
if (options.all)
|
|
235
|
+
return callback(null, list);
|
|
236
|
+
callback(null, list[0].address, list[0].family);
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** A ws:// or wss:// URL as the http(s) URL its handshake uses. */
|
|
241
|
+
function asHttpUrl(input) {
|
|
242
|
+
return String(input).replace(/^ws(s?):/i, "http$1:");
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* For WebSocket connections to tenant-chosen URLs (session apps, listeners): checks the URL's
|
|
246
|
+
* shape (ws/wss, no internal names or private literal IPs) and returns a socket `lookup` that
|
|
247
|
+
* refuses private addresses when the name resolves. Pass it as the `lookup` option of `ws`.
|
|
248
|
+
*/
|
|
249
|
+
function publicWebSocketGuard(input, policy) {
|
|
250
|
+
if (!/^wss?:\/\//i.test(String(input)))
|
|
251
|
+
throw new UnsafeDestinationError("unsupported scheme");
|
|
252
|
+
const allow = allowList(policy);
|
|
253
|
+
const url = assertPublicUrlShape(asHttpUrl(input), { allow });
|
|
254
|
+
return { lookup: guardedLookup(hostOf(url), allow) };
|
|
255
|
+
}
|
|
256
|
+
/** Synchronous shape check of a ws:// or wss:// URL (see assertPublicUrlShape). */
|
|
257
|
+
function assertPublicWebSocketUrlShape(input, policy) {
|
|
258
|
+
if (!/^wss?:\/\//i.test(String(input)))
|
|
259
|
+
throw new UnsafeDestinationError("unsupported scheme");
|
|
260
|
+
return assertPublicUrlShape(asHttpUrl(input), policy);
|
|
261
|
+
}
|
|
262
|
+
const NO_BODY = new Set([101, 204, 205, 304]);
|
|
263
|
+
function requestOnce(url, init, method, body, signal, allow) {
|
|
264
|
+
const lookup = guardedLookup(hostOf(url), allow);
|
|
265
|
+
const lib = url.protocol === "https:" ? node_https_1.default : node_http_1.default;
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
var _a;
|
|
268
|
+
const req = lib.request(url, {
|
|
269
|
+
method,
|
|
270
|
+
headers: Object.assign(Object.assign({}, ((_a = init.headers) !== null && _a !== void 0 ? _a : {})), (body !== undefined
|
|
271
|
+
? { "content-length": String(Buffer.byteLength(body)) }
|
|
272
|
+
: {})),
|
|
273
|
+
lookup,
|
|
274
|
+
signal
|
|
275
|
+
}, (res) => {
|
|
276
|
+
var _a;
|
|
277
|
+
const chunks = [];
|
|
278
|
+
let size = 0;
|
|
279
|
+
const max = (_a = init.maxBytes) !== null && _a !== void 0 ? _a : 5 * 1024 * 1024;
|
|
280
|
+
res.on("data", (c) => {
|
|
281
|
+
size += c.length;
|
|
282
|
+
if (size > max) {
|
|
283
|
+
res.destroy(new UnsafeDestinationError("response too large"));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
chunks.push(c);
|
|
287
|
+
});
|
|
288
|
+
res.on("error", reject);
|
|
289
|
+
res.on("end", () => {
|
|
290
|
+
var _a;
|
|
291
|
+
const headers = new Headers();
|
|
292
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
293
|
+
if (v === undefined)
|
|
294
|
+
continue;
|
|
295
|
+
for (const item of Array.isArray(v) ? v : [v])
|
|
296
|
+
headers.append(k, item);
|
|
297
|
+
}
|
|
298
|
+
const status = (_a = res.statusCode) !== null && _a !== void 0 ? _a : 0;
|
|
299
|
+
resolve(new Response(NO_BODY.has(status) ? null : Buffer.concat(chunks), {
|
|
300
|
+
status: status < 200 || status > 599 ? 502 : status,
|
|
301
|
+
statusText: res.statusMessage,
|
|
302
|
+
headers
|
|
303
|
+
}));
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
req.on("error", reject);
|
|
307
|
+
if (body !== undefined)
|
|
308
|
+
req.write(body);
|
|
309
|
+
req.end();
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* fetch for tenant-chosen URLs: checks the destination (and each redirect) with
|
|
314
|
+
* assertPublicDestination, re-checks the addresses the socket connects to, and times out.
|
|
315
|
+
*/
|
|
316
|
+
function safeFetch(input_1) {
|
|
317
|
+
return __awaiter(this, arguments, void 0, function* (input, init = {}) {
|
|
318
|
+
var _a, _b, _c;
|
|
319
|
+
const allow = allowList(init.policy);
|
|
320
|
+
const timeout = AbortSignal.timeout((_a = init.timeoutMs) !== null && _a !== void 0 ? _a : 10000);
|
|
321
|
+
const signal = init.signal
|
|
322
|
+
? AbortSignal.any([init.signal, timeout])
|
|
323
|
+
: timeout;
|
|
324
|
+
let url = yield assertPublicDestination(input, { allow });
|
|
325
|
+
let method = ((_b = init.method) !== null && _b !== void 0 ? _b : "GET").toUpperCase();
|
|
326
|
+
let body = init.body;
|
|
327
|
+
const maxRedirects = (_c = init.maxRedirects) !== null && _c !== void 0 ? _c : 3;
|
|
328
|
+
for (let hop = 0;; hop++) {
|
|
329
|
+
const res = yield requestOnce(url, init, method, body, signal, allow);
|
|
330
|
+
const location = res.headers.get("location");
|
|
331
|
+
if (![301, 302, 303, 307, 308].includes(res.status) || !location)
|
|
332
|
+
return res;
|
|
333
|
+
if (hop >= maxRedirects)
|
|
334
|
+
throw new UnsafeDestinationError("too many redirects");
|
|
335
|
+
url = yield assertPublicDestination(new node_url_1.URL(location, url), { allow });
|
|
336
|
+
if (res.status === 303 || (res.status <= 302 && method === "POST")) {
|
|
337
|
+
method = "GET";
|
|
338
|
+
body = undefined;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
@@ -16,8 +16,4 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
type PlayDtmfRequest = VerbRequest & {
|
|
21
|
-
digits: string;
|
|
22
|
-
};
|
|
23
|
-
export { PlayDtmfRequest };
|
|
19
|
+
export * from "./assertPublicDestination";
|
|
@@ -32,16 +32,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
32
32
|
* See the License for the specific language governing permissions and
|
|
33
33
|
* limitations under the License.
|
|
34
34
|
*/
|
|
35
|
-
__exportStar(require("./
|
|
36
|
-
__exportStar(require("./Gather"), exports);
|
|
37
|
-
__exportStar(require("./Mute"), exports);
|
|
38
|
-
__exportStar(require("./Play"), exports);
|
|
39
|
-
__exportStar(require("./PlayDtmf"), exports);
|
|
40
|
-
__exportStar(require("./PlaybackControl"), exports);
|
|
41
|
-
__exportStar(require("./Record"), exports);
|
|
42
|
-
__exportStar(require("./Say"), exports);
|
|
43
|
-
__exportStar(require("./Stream"), exports);
|
|
44
|
-
__exportStar(require("./StreamGather"), exports);
|
|
45
|
-
__exportStar(require("./StopSay"), exports);
|
|
46
|
-
__exportStar(require("./Verb"), exports);
|
|
47
|
-
__exportStar(require("./voice"), exports);
|
|
35
|
+
__exportStar(require("./assertPublicDestination"), exports);
|
|
@@ -37,7 +37,9 @@ function createSendEmail(config) {
|
|
|
37
37
|
host,
|
|
38
38
|
port,
|
|
39
39
|
secure,
|
|
40
|
-
auth
|
|
40
|
+
// Relays without authentication (e.g. a local MailHog) get no auth block;
|
|
41
|
+
// an empty one makes nodemailer fail with "Missing credentials".
|
|
42
|
+
auth: (auth === null || auth === void 0 ? void 0 : auth.user) ? auth : undefined
|
|
41
43
|
});
|
|
42
44
|
return function sendEmail(params) {
|
|
43
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -39,13 +39,69 @@ service Applications {
|
|
|
39
39
|
rpc EvaluateIntelligence(EvaluateIntelligenceRequest) returns (stream EvaluateIntelligenceEvent);
|
|
40
40
|
// Create an Ephemeral Agent to perform test calls to an application
|
|
41
41
|
rpc CreateTestToken(google.protobuf.Empty) returns (TestTokenResponse) {}
|
|
42
|
+
// Replace an application's signing secret; the old one stops working at once (no overlap)
|
|
43
|
+
rpc RotateSigningSecret(RotateSigningSecretRequest) returns (RotateSigningSecretResponse) {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Speech defaults of an application; voices and models are catalog ids (provider/model-or-voice)
|
|
47
|
+
message SpeechDefaults {
|
|
48
|
+
// Default voice, e.g. elevenlabs/rachel
|
|
49
|
+
string voice = 1;
|
|
50
|
+
// Default language (BCP 47), e.g. en-US
|
|
51
|
+
string language = 2;
|
|
52
|
+
// Speech-to-text model, e.g. deepgram/nova-3 (empty: the platform default)
|
|
53
|
+
string stt_model = 3;
|
|
54
|
+
// Text-to-speech model (empty: the voice's default)
|
|
55
|
+
string tts_model = 4;
|
|
56
|
+
// Silence timeout in milliseconds (0: the gateway default)
|
|
57
|
+
int32 silence_timeout_ms = 5;
|
|
58
|
+
// The Pacing policy's patience: low, normal or high (empty: normal)
|
|
59
|
+
string patience = 6;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Caller and agent audio format for the fonoster protocol
|
|
63
|
+
message AudioFormat {
|
|
64
|
+
// pcm16 or pcmu
|
|
65
|
+
string codec = 1;
|
|
66
|
+
// Sample rate in Hz: 8000, 16000 or 24000 (pcmu is 8000 only)
|
|
67
|
+
int32 rate = 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Answering-machine detection: one amd.result event per call
|
|
71
|
+
message AmdSettings {
|
|
72
|
+
// Classify the caller's first seconds of speech
|
|
73
|
+
bool enabled = 1;
|
|
74
|
+
// How much caller speech to classify, in milliseconds (0: 4000; 500 to 15000)
|
|
75
|
+
int32 window_ms = 2;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// A listen-only socket dialed for every session of the app
|
|
79
|
+
message Listener {
|
|
80
|
+
// ws:// or wss:// address
|
|
81
|
+
string url = 1;
|
|
82
|
+
// inbound, outbound or both
|
|
83
|
+
string tracks = 2;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message RotateSigningSecretRequest {
|
|
87
|
+
// The reference to the application
|
|
88
|
+
string ref = 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
message RotateSigningSecretResponse {
|
|
92
|
+
// The reference to the application
|
|
93
|
+
string ref = 1;
|
|
94
|
+
// The new secret; shown only in this response
|
|
95
|
+
string signing_secret = 2;
|
|
96
|
+
// The last characters of the secret
|
|
97
|
+
string signing_secret_hint = 3;
|
|
42
98
|
}
|
|
43
99
|
|
|
44
100
|
// The type of application
|
|
45
101
|
enum ApplicationType {
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
//
|
|
102
|
+
// A stream: your own WebSocket server
|
|
103
|
+
STREAM = 0;
|
|
104
|
+
// An Autopilot agent (hosted by Fonoster)
|
|
49
105
|
AUTOPILOT = 1;
|
|
50
106
|
}
|
|
51
107
|
|
|
@@ -78,6 +134,26 @@ message Application {
|
|
|
78
134
|
int32 created_at = 8;
|
|
79
135
|
// Update time
|
|
80
136
|
int32 updated_at = 9;
|
|
137
|
+
// The WebSocket URL the gateway dials
|
|
138
|
+
string url = 10;
|
|
139
|
+
// Stream protocol: fonoster or twilio
|
|
140
|
+
string protocol = 11;
|
|
141
|
+
// The last characters of the signing secret (the secret is never returned on reads)
|
|
142
|
+
string signing_secret_hint = 12;
|
|
143
|
+
// Speech defaults
|
|
144
|
+
SpeechDefaults speech = 13;
|
|
145
|
+
// Audio format (fonoster protocol)
|
|
146
|
+
AudioFormat audio = 14;
|
|
147
|
+
// App used when this one cannot be reached
|
|
148
|
+
string fallback_app_ref = 15;
|
|
149
|
+
// Message spoken when neither the app nor its fallback can be reached
|
|
150
|
+
string fallback_message = 16;
|
|
151
|
+
// Listen-only sockets
|
|
152
|
+
repeated Listener listeners = 17;
|
|
153
|
+
// Recording policy: off, on or consent (empty: the workspace's)
|
|
154
|
+
string recording = 18;
|
|
155
|
+
// Answering-machine detection (off when unset)
|
|
156
|
+
AmdSettings amd = 19;
|
|
81
157
|
}
|
|
82
158
|
|
|
83
159
|
// Request to create a new application
|
|
@@ -94,12 +170,34 @@ message CreateApplicationRequest {
|
|
|
94
170
|
ProductContainer speech_to_text = 5;
|
|
95
171
|
// Inference engine
|
|
96
172
|
ProductContainer intelligence = 6;
|
|
173
|
+
// The WebSocket URL (Autopilot agents default to the hosted Autopilot)
|
|
174
|
+
string url = 7;
|
|
175
|
+
// fonoster or twilio (default fonoster)
|
|
176
|
+
string protocol = 8;
|
|
177
|
+
// Speech defaults
|
|
178
|
+
SpeechDefaults speech = 9;
|
|
179
|
+
// Audio format (fonoster protocol)
|
|
180
|
+
AudioFormat audio = 10;
|
|
181
|
+
// App used when this one cannot be reached
|
|
182
|
+
string fallback_app_ref = 11;
|
|
183
|
+
// Message spoken when neither the app nor its fallback can be reached
|
|
184
|
+
string fallback_message = 12;
|
|
185
|
+
// Listen-only sockets
|
|
186
|
+
repeated Listener listeners = 13;
|
|
187
|
+
// Recording policy: off, on or consent (empty: the workspace's)
|
|
188
|
+
string recording = 14;
|
|
189
|
+
// Answering-machine detection (off when unset)
|
|
190
|
+
AmdSettings amd = 15;
|
|
97
191
|
}
|
|
98
192
|
|
|
99
193
|
// Response for create application
|
|
100
194
|
message CreateApplicationResponse {
|
|
101
195
|
// Echo the reference of the application
|
|
102
196
|
string ref = 1;
|
|
197
|
+
// The app's signing secret; shown only in this response
|
|
198
|
+
string signing_secret = 2;
|
|
199
|
+
// The last characters of the secret
|
|
200
|
+
string signing_secret_hint = 3;
|
|
103
201
|
}
|
|
104
202
|
|
|
105
203
|
// Request to get an application
|
|
@@ -140,12 +238,35 @@ message UpdateApplicationRequest {
|
|
|
140
238
|
ProductContainer speech_to_text = 6;
|
|
141
239
|
// Intelligence product
|
|
142
240
|
ProductContainer intelligence = 7;
|
|
241
|
+
// The WebSocket URL (Autopilot agents default to the hosted Autopilot)
|
|
242
|
+
string url = 8;
|
|
243
|
+
// fonoster or twilio (default fonoster)
|
|
244
|
+
string protocol = 9;
|
|
245
|
+
// Speech defaults
|
|
246
|
+
SpeechDefaults speech = 10;
|
|
247
|
+
// Audio format (fonoster protocol)
|
|
248
|
+
AudioFormat audio = 11;
|
|
249
|
+
// App used when this one cannot be reached
|
|
250
|
+
string fallback_app_ref = 12;
|
|
251
|
+
// Message spoken when neither the app nor its fallback can be reached
|
|
252
|
+
string fallback_message = 13;
|
|
253
|
+
// Listen-only sockets
|
|
254
|
+
repeated Listener listeners = 14;
|
|
255
|
+
// Recording policy: off, on or consent (empty: the workspace's)
|
|
256
|
+
string recording = 15;
|
|
257
|
+
// Answering-machine detection (off when unset)
|
|
258
|
+
AmdSettings amd = 16;
|
|
143
259
|
}
|
|
144
260
|
|
|
145
261
|
// Response for update application
|
|
146
262
|
message UpdateApplicationResponse {
|
|
147
263
|
// Echo the reference of the application
|
|
148
264
|
string ref = 1;
|
|
265
|
+
// A new signing secret when the update generated one (the app stopped being an Autopilot
|
|
266
|
+
// app); shown only in this response. Empty otherwise.
|
|
267
|
+
string signing_secret = 2;
|
|
268
|
+
// The last characters of the new secret (empty when there is none)
|
|
269
|
+
string signing_secret_hint = 3;
|
|
149
270
|
}
|
|
150
271
|
|
|
151
272
|
// Request to delete an application
|
package/dist/protos/authz.proto
CHANGED
|
@@ -23,19 +23,38 @@ package fonoster.authz.v1beta2;
|
|
|
23
23
|
import "google/protobuf/struct.proto";
|
|
24
24
|
import "google/protobuf/empty.proto";
|
|
25
25
|
|
|
26
|
-
import "voice.proto";
|
|
27
|
-
|
|
28
26
|
// Authentication service definition
|
|
29
27
|
service Authz {
|
|
30
28
|
// Check if a user has permission to perform an action
|
|
31
|
-
rpc CheckSessionAuthorized (
|
|
29
|
+
rpc CheckSessionAuthorized (CheckSessionAuthorizedRequest) returns (CheckSessionAuthorizedResponse) {}
|
|
32
30
|
// Check if a given accessKeyId has permission to access a method
|
|
33
31
|
rpc CheckMethodAuthorized (CheckMethodAuthorizedRequest) returns (CheckMethodAuthorizedResponse) {}
|
|
34
32
|
// Add billing meter event to an account
|
|
35
33
|
rpc AddBillingMeterEvent (AddBillingMeterEventRequest) returns (google.protobuf.Empty) {}
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
//
|
|
36
|
+
// The call a session would serve. Field numbers match the retired
|
|
37
|
+
// fonoster.voice.v1beta2.CreateSessionRequest, so existing authorization servers keep decoding it.
|
|
38
|
+
message CheckSessionAuthorizedRequest {
|
|
39
|
+
string access_key_id = 1;
|
|
40
|
+
string app_ref = 2;
|
|
41
|
+
string endpoint = 3;
|
|
42
|
+
string ingress_number = 4;
|
|
43
|
+
string caller_name = 5;
|
|
44
|
+
string caller_number = 6;
|
|
45
|
+
string media_session_ref = 7;
|
|
46
|
+
string session_token = 8;
|
|
47
|
+
enum CallDirection {
|
|
48
|
+
FROM_PSTN = 0;
|
|
49
|
+
TO_PSTN = 1;
|
|
50
|
+
INTRA_NETWORK = 2;
|
|
51
|
+
}
|
|
52
|
+
CallDirection call_direction = 9;
|
|
53
|
+
map<string, string> metadata = 10;
|
|
54
|
+
string call_ref = 11;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Response message for the CheckSessionAuthorized method
|
|
39
58
|
message CheckSessionAuthorizedResponse {
|
|
40
59
|
// The authorization server will look for one or more parameters in the request
|
|
41
60
|
// to determine if the request is authorized. If the request is authorized, the
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
syntax = "proto3";
|
|
20
|
+
|
|
21
|
+
package fonoster.capabilities.v1beta2;
|
|
22
|
+
|
|
23
|
+
import "google/protobuf/empty.proto";
|
|
24
|
+
|
|
25
|
+
// What this deployment supports, from fonoster.yaml. Public (no credentials
|
|
26
|
+
// needed) so sign-in screens can render from it; it never returns secrets.
|
|
27
|
+
service Capabilities {
|
|
28
|
+
// Gets the deployment's capabilities
|
|
29
|
+
rpc GetCapabilities (google.protobuf.Empty) returns (GetCapabilitiesResponse) {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
message GitHubCapability {
|
|
33
|
+
string client_id = 1;
|
|
34
|
+
string authorize_url = 2;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message AuthCapabilities {
|
|
38
|
+
// Enabled sign-in methods: password | email_code | github
|
|
39
|
+
repeated string methods = 1;
|
|
40
|
+
// Who may create an account: invite | open
|
|
41
|
+
string signup = 2;
|
|
42
|
+
// Set when github is among the methods
|
|
43
|
+
GitHubCapability github = 3;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
message BillingCapabilities {
|
|
47
|
+
bool enabled = 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
message CostsCapabilities {
|
|
51
|
+
// none | usage | prices
|
|
52
|
+
string display = 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Private regions (customer-run regions enrolled through the region hub).
|
|
56
|
+
// The local public region always exists and is not reported here.
|
|
57
|
+
message RegionsCapabilities {
|
|
58
|
+
// True when the operator configured the region hub and the region-signing
|
|
59
|
+
// key (regions.hub and regions.signingKey), so workspaces can enroll
|
|
60
|
+
// private regions. Never carries hub URLs or keys.
|
|
61
|
+
bool private = 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
message SipEndpoint {
|
|
65
|
+
string host = 1;
|
|
66
|
+
int32 port = 2;
|
|
67
|
+
// udp | tcp | tls | ws | wss
|
|
68
|
+
string transport = 3;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
message PortRange {
|
|
72
|
+
int32 min = 1;
|
|
73
|
+
int32 max = 2;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Where carriers and softphones reach this deployment: the default public
|
|
77
|
+
// region's endpoints (regions.public in fonoster.yaml). Addresses and ports
|
|
78
|
+
// only, never credentials. Empty when the operator listed none.
|
|
79
|
+
message ConnectionCapabilities {
|
|
80
|
+
repeated SipEndpoint sip = 1;
|
|
81
|
+
repeated string signaling_addresses = 2;
|
|
82
|
+
repeated string media_addresses = 3;
|
|
83
|
+
PortRange rtp_ports = 4;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message GetCapabilitiesResponse {
|
|
87
|
+
AuthCapabilities auth = 1;
|
|
88
|
+
BillingCapabilities billing = 2;
|
|
89
|
+
CostsCapabilities costs = 3;
|
|
90
|
+
string domain = 4;
|
|
91
|
+
string app_url = 5;
|
|
92
|
+
RegionsCapabilities regions = 6;
|
|
93
|
+
ConnectionCapabilities connection = 7;
|
|
94
|
+
}
|