@fonoster/common 0.30.0 → 0.31.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 → agents}/index.d.ts +1 -1
- package/dist/{assistants → agents}/index.js +1 -1
- package/dist/{assistants/sessionAssistantSchema.d.ts → agents/sessionAgentSchema.d.ts} +20 -6
- package/dist/{assistants/sessionAssistantSchema.js → agents/sessionAgentSchema.js} +16 -11
- package/dist/cellsecrets/index.d.ts +2 -2
- package/dist/cellsecrets/index.js +3 -3
- package/dist/config/cli/render.d.ts +1 -1
- package/dist/config/cli/render.js +3 -3
- package/dist/config/configHints.d.ts +1 -14
- package/dist/config/configHints.js +16 -30
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +0 -1
- package/dist/config/interpolateEnv.d.ts +1 -1
- package/dist/config/loadServiceConfig.d.ts +0 -5
- package/dist/config/loadServiceConfig.js +3 -12
- package/dist/constants.d.ts +1 -3
- package/dist/constants.js +3 -5
- package/dist/errors/handleZodError.js +3 -1
- package/dist/filters/index.d.ts +70 -0
- package/dist/filters/index.js +101 -0
- package/dist/identity/roles.js +11 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -2
- package/dist/protos/applications.proto +16 -2
- package/dist/protos/authz.proto +3 -0
- package/dist/protos/billing.proto +92 -0
- package/dist/protos/calls.proto +4 -0
- package/dist/protos/capabilities.proto +8 -0
- package/dist/protos/providers.proto +6 -2
- package/dist/protos/roomr.proto +146 -0
- package/dist/protos/session.proto +194 -0
- package/dist/protos/usage.proto +59 -0
- package/dist/registry/Registry.d.ts +13 -5
- package/dist/registry/Registry.js +49 -35
- package/dist/registry/cli.js +19 -7
- package/dist/registry/features.d.ts +2 -2
- package/dist/registry/features.js +7 -5
- package/dist/registry/prisma.d.ts +2 -2
- package/dist/registry/prisma.js +26 -18
- package/dist/registry/types.d.ts +17 -5
- package/dist/registry/types.js +2 -0
- package/dist/roomr/health.d.ts +9 -0
- package/dist/roomr/health.js +75 -0
- package/dist/roomr/index.d.ts +75 -0
- package/dist/roomr/index.js +146 -0
- package/dist/roomr/session.d.ts +133 -0
- package/dist/roomr/session.js +132 -0
- package/dist/session/schema.d.ts +196 -7
- package/dist/session/schema.js +59 -8
- package/dist/sessions/index.d.ts +15 -12
- package/dist/sessions/index.js +15 -12
- package/dist/telemetry.d.ts +1 -1
- package/dist/usage/CallUsage.d.ts +38 -0
- package/dist/usage/CallUsage.js +76 -0
- package/dist/{utils/types.d.ts → usage/index.d.ts} +4 -5
- package/dist/usage/index.js +38 -0
- package/dist/usage/meters.d.ts +113 -0
- package/dist/usage/meters.js +90 -0
- package/dist/usage/types.d.ts +159 -0
- package/dist/usage/types.js +84 -0
- package/dist/usage/usageId.d.ts +6 -0
- package/dist/usage/usageId.js +45 -0
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -2
- package/dist/utils/withErrorHandling.js +1 -1
- package/dist/validators/calls.d.ts +5 -2
- package/dist/validators/calls.js +8 -1
- package/dist/validators/sip.d.ts +2 -2
- package/dist/validators/sip.js +3 -3
- package/dist/webhooks/bus.d.ts +29 -10
- package/dist/webhooks/bus.js +58 -50
- package/dist/webhooks/events.d.ts +1 -1
- package/dist/webhooks/events.js +1 -1
- package/package.json +9 -8
- package/schema/session.v1.json +82 -2
- package/dist/config/integrationsSchema.d.ts +0 -32
- package/dist/config/integrationsSchema.js +0 -56
- package/dist/utils/createFetchSingleCallByCallId.d.ts +0 -22
- package/dist/utils/createFetchSingleCallByCallId.js +0 -33
- package/dist/utils/types.js +0 -2
- /package/dist/{assistants → agents}/testCasesSchema.d.ts +0 -0
- /package/dist/{assistants → agents}/testCasesSchema.js +0 -0
- /package/dist/{assistants → agents}/tools/index.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/index.js +0 -0
- /package/dist/{assistants → agents}/tools/propertySchema.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/propertySchema.js +0 -0
- /package/dist/{assistants → agents}/tools/toolSchema.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/toolSchema.js +0 -0
package/dist/registry/prisma.js
CHANGED
|
@@ -71,7 +71,12 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
71
71
|
id: p.id,
|
|
72
72
|
adapter: p.adapter,
|
|
73
73
|
capabilities: p.capabilities,
|
|
74
|
-
credentialRef: p.credentialRef,
|
|
74
|
+
credentialRef: opt(p.credentialRef),
|
|
75
|
+
label: opt(p.label),
|
|
76
|
+
// Stored as {} when the provider names none.
|
|
77
|
+
endpoints: p.endpoints && Object.keys(p.endpoints).length
|
|
78
|
+
? p.endpoints
|
|
79
|
+
: undefined,
|
|
75
80
|
state: fromState(p.state),
|
|
76
81
|
replacement: opt(p.replacement),
|
|
77
82
|
retireOn: opt(p.retireOn)
|
|
@@ -80,6 +85,7 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
80
85
|
entries.set(m.id, clean({
|
|
81
86
|
kind: "model",
|
|
82
87
|
id: m.id,
|
|
88
|
+
label: opt(m.label),
|
|
83
89
|
type: m.type,
|
|
84
90
|
features: m.features,
|
|
85
91
|
languages: m.languages,
|
|
@@ -92,6 +98,7 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
92
98
|
entries.set(v.id, clean({
|
|
93
99
|
kind: "voice",
|
|
94
100
|
id: v.id,
|
|
101
|
+
label: opt(v.label),
|
|
95
102
|
models: v.models.map((r) => r.modelId),
|
|
96
103
|
language: opt(v.language),
|
|
97
104
|
gender: opt(v.gender),
|
|
@@ -111,19 +118,20 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
111
118
|
});
|
|
112
119
|
}
|
|
113
120
|
put(entry) {
|
|
114
|
-
var _a, _b;
|
|
121
|
+
var _a, _b, _c;
|
|
115
122
|
super.put(entry);
|
|
116
123
|
const e = JSON.parse(JSON.stringify(entry));
|
|
117
124
|
const common = {
|
|
125
|
+
label: (_a = e.label) !== null && _a !== void 0 ? _a : null,
|
|
118
126
|
state: toState(e.state),
|
|
119
|
-
replacement: (
|
|
120
|
-
retireOn: (
|
|
127
|
+
replacement: (_b = e.replacement) !== null && _b !== void 0 ? _b : null,
|
|
128
|
+
retireOn: (_c = e.retireOn) !== null && _c !== void 0 ? _c : null
|
|
121
129
|
};
|
|
122
130
|
const provider = e.id.split("/")[0];
|
|
123
131
|
this.writes.push(() => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
-
var _a, _b, _c;
|
|
132
|
+
var _a, _b, _c, _d, _e;
|
|
125
133
|
if (e.kind === "provider") {
|
|
126
|
-
const data = Object.assign({ adapter: e.adapter, capabilities: e.capabilities, credentialRef: e.credentialRef }, common);
|
|
134
|
+
const data = Object.assign({ adapter: e.adapter, capabilities: e.capabilities, credentialRef: (_a = e.credentialRef) !== null && _a !== void 0 ? _a : null, endpoints: (_b = e.endpoints) !== null && _b !== void 0 ? _b : {} }, common);
|
|
127
135
|
yield this.prisma.registryProvider.upsert({
|
|
128
136
|
where: { id: e.id },
|
|
129
137
|
create: Object.assign({ id: e.id }, data),
|
|
@@ -139,7 +147,7 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
139
147
|
});
|
|
140
148
|
}
|
|
141
149
|
else {
|
|
142
|
-
const data = Object.assign({ providerId: provider, language: (
|
|
150
|
+
const data = Object.assign({ providerId: provider, language: (_c = e.language) !== null && _c !== void 0 ? _c : null, gender: (_d = e.gender) !== null && _d !== void 0 ? _d : null, ownerWorkspace: (_e = e.ownerWorkspace) !== null && _e !== void 0 ? _e : null }, common);
|
|
143
151
|
yield this.prisma.$transaction([
|
|
144
152
|
this.prisma.registryVoice.upsert({
|
|
145
153
|
where: { id: e.id },
|
|
@@ -182,8 +190,8 @@ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
|
|
|
182
190
|
}
|
|
183
191
|
exports.PrismaRegistryStore = PrismaRegistryStore;
|
|
184
192
|
/**
|
|
185
|
-
* Usages of catalog ids in apps (speech defaults: voice, TTS and STT models) and
|
|
186
|
-
* Autopilot
|
|
193
|
+
* Usages of catalog ids in apps (speech defaults: voice, TTS and STT models) and agents (the
|
|
194
|
+
* Autopilot agent in an app's Intelligence config: `languageModel.model`, and
|
|
187
195
|
* `conversationSettings.voice`). Legacy rows with raw vendor names are not usages until migrated
|
|
188
196
|
* to catalog ids (task 5.1).
|
|
189
197
|
*/
|
|
@@ -250,10 +258,10 @@ class PrismaUsageSource {
|
|
|
250
258
|
requires: ["streaming"]
|
|
251
259
|
}, { kind: "app-stt", row });
|
|
252
260
|
}
|
|
253
|
-
const
|
|
254
|
-
const llm = (_e =
|
|
261
|
+
const agent = (_d = row.intelligence) === null || _d === void 0 ? void 0 : _d.config;
|
|
262
|
+
const llm = (_e = agent === null || agent === void 0 ? void 0 : agent.languageModel) === null || _e === void 0 ? void 0 : _e.model;
|
|
255
263
|
const config = {
|
|
256
|
-
kind: "
|
|
264
|
+
kind: "agent",
|
|
257
265
|
ref: row.ref,
|
|
258
266
|
name: row.name
|
|
259
267
|
};
|
|
@@ -263,12 +271,12 @@ class PrismaUsageSource {
|
|
|
263
271
|
config,
|
|
264
272
|
model: llm,
|
|
265
273
|
settings: [],
|
|
266
|
-
requires: ((_g = (_f =
|
|
274
|
+
requires: ((_g = (_f = agent === null || agent === void 0 ? void 0 : agent.languageModel) === null || _f === void 0 ? void 0 : _f.tools) === null || _g === void 0 ? void 0 : _g.length)
|
|
267
275
|
? ["streaming", "tool-calling"]
|
|
268
276
|
: ["streaming"]
|
|
269
|
-
}, { kind: "
|
|
277
|
+
}, { kind: "agent-llm", row, model: llm });
|
|
270
278
|
}
|
|
271
|
-
const voice = (_h =
|
|
279
|
+
const voice = (_h = agent === null || agent === void 0 ? void 0 : agent.conversationSettings) === null || _h === void 0 ? void 0 : _h.voice;
|
|
272
280
|
if (voice === id) {
|
|
273
281
|
add({
|
|
274
282
|
workspace: row.accessKeyId,
|
|
@@ -277,7 +285,7 @@ class PrismaUsageSource {
|
|
|
277
285
|
voice,
|
|
278
286
|
settings: [],
|
|
279
287
|
requires: ["streaming"]
|
|
280
|
-
}, { kind: "
|
|
288
|
+
}, { kind: "agent-voice", row, voice });
|
|
281
289
|
}
|
|
282
290
|
}
|
|
283
291
|
return out;
|
|
@@ -325,7 +333,7 @@ class PrismaUsageSource {
|
|
|
325
333
|
const config = JSON.parse(JSON.stringify((_b = (_a = row.intelligence) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : {}));
|
|
326
334
|
let path;
|
|
327
335
|
let expected;
|
|
328
|
-
if (origin.kind === "
|
|
336
|
+
if (origin.kind === "agent-llm") {
|
|
329
337
|
path = ["languageModel", "model"];
|
|
330
338
|
expected = origin.model;
|
|
331
339
|
if (change.model)
|
|
@@ -343,7 +351,7 @@ class PrismaUsageSource {
|
|
|
343
351
|
data: { config }
|
|
344
352
|
});
|
|
345
353
|
if (count === 0)
|
|
346
|
-
conflict("
|
|
354
|
+
conflict("agent changed since it was read");
|
|
347
355
|
else
|
|
348
356
|
yield this.prisma.application.update({
|
|
349
357
|
where: { ref: row.ref },
|
package/dist/registry/types.d.ts
CHANGED
|
@@ -17,14 +17,22 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
export type Lifecycle = "active" | "deprecated" | "retired";
|
|
20
|
-
|
|
20
|
+
/** What a provider or model serves: speech, language models, or noise cancelling (`nc`). */
|
|
21
|
+
export type Capability = "stt" | "tts" | "llm" | "nc";
|
|
22
|
+
export declare const CAPABILITIES: Capability[];
|
|
21
23
|
export type ItemKind = "provider" | "model" | "voice";
|
|
24
|
+
/** Where a provider serves each capability; services use these unless their config overrides. */
|
|
25
|
+
export type ProviderEndpoints = Partial<Record<Capability, string>>;
|
|
22
26
|
export type ProviderEntry = {
|
|
23
27
|
kind: "provider";
|
|
24
28
|
id: string;
|
|
29
|
+
/** A display name ("Deepgram"); the dashboard shows the id without one. */
|
|
30
|
+
label?: string;
|
|
25
31
|
adapter: string;
|
|
26
32
|
capabilities: Capability[];
|
|
27
|
-
|
|
33
|
+
/** The platform credential's name; omitted for a keyless provider (no key needed). */
|
|
34
|
+
credentialRef?: string;
|
|
35
|
+
endpoints?: ProviderEndpoints;
|
|
28
36
|
state: Lifecycle;
|
|
29
37
|
replacement?: string;
|
|
30
38
|
retireOn?: string;
|
|
@@ -32,6 +40,8 @@ export type ProviderEntry = {
|
|
|
32
40
|
export type ModelEntry = {
|
|
33
41
|
kind: "model";
|
|
34
42
|
id: string;
|
|
43
|
+
/** A display name ("Nova 3"); the dashboard shows the id without one. */
|
|
44
|
+
label?: string;
|
|
35
45
|
type: Capability;
|
|
36
46
|
features: string[];
|
|
37
47
|
languages: string[];
|
|
@@ -44,6 +54,8 @@ export type ModelEntry = {
|
|
|
44
54
|
export type VoiceEntry = {
|
|
45
55
|
kind: "voice";
|
|
46
56
|
id: string;
|
|
57
|
+
/** A display name ("Aura 2 Thalia"); the dashboard shows the id without one. */
|
|
58
|
+
label?: string;
|
|
47
59
|
models: string[];
|
|
48
60
|
language?: string;
|
|
49
61
|
gender?: string;
|
|
@@ -61,11 +73,11 @@ export type AuditEntry = {
|
|
|
61
73
|
target: string;
|
|
62
74
|
details?: Record<string, unknown>;
|
|
63
75
|
};
|
|
64
|
-
/** One place a catalog id is used: an app's speech config or an
|
|
76
|
+
/** One place a catalog id is used: an app's speech config or an agent's model config. */
|
|
65
77
|
export type Usage = {
|
|
66
78
|
workspace: string;
|
|
67
79
|
config: {
|
|
68
|
-
kind: "app" | "
|
|
80
|
+
kind: "app" | "agent";
|
|
69
81
|
ref: string;
|
|
70
82
|
name: string;
|
|
71
83
|
};
|
|
@@ -76,7 +88,7 @@ export type Usage = {
|
|
|
76
88
|
/** Hard features the config needs (derived from its content, e.g. tools need tool-calling). */
|
|
77
89
|
requires: string[];
|
|
78
90
|
};
|
|
79
|
-
/** Where usages come from (apps and
|
|
91
|
+
/** Where usages come from (apps and agents in the control-plane database). */
|
|
80
92
|
export interface UsageSource {
|
|
81
93
|
find(id: string): Usage[];
|
|
82
94
|
/** Rewrites one config; returns false if it changed since it was read. */
|
package/dist/registry/types.js
CHANGED
|
@@ -20,3 +20,5 @@
|
|
|
20
20
|
// The provider registry (add-provider-registry): providers, models and voices by catalog id,
|
|
21
21
|
// with a manual lifecycle. The catalog never holds a secret: providers name a credentialRef.
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.CAPABILITIES = void 0;
|
|
24
|
+
exports.CAPABILITIES = ["stt", "tts", "llm", "nc"];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** How long one health check may take. */
|
|
2
|
+
export declare const HEALTH_CHECK_TIMEOUT_MS = 2000;
|
|
3
|
+
/**
|
|
4
|
+
* Whether `address` (host:port) answers the standard gRPC health check (grpc.health.v1) with
|
|
5
|
+
* SERVING for the server as a whole. False on any error, a timeout or any other status (a Roomr
|
|
6
|
+
* node or AppBridge replica draining after SIGTERM reports NOT_SERVING). Through a Kubernetes
|
|
7
|
+
* Service or a Compose service name the check reaches one ready replica.
|
|
8
|
+
*/
|
|
9
|
+
export declare function checkGrpcHealth(address: string, timeoutMs?: number): Promise<boolean>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HEALTH_CHECK_TIMEOUT_MS = void 0;
|
|
37
|
+
exports.checkGrpcHealth = checkGrpcHealth;
|
|
38
|
+
/**
|
|
39
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
40
|
+
* http://github.com/fonoster/fonoster
|
|
41
|
+
*
|
|
42
|
+
* This file is part of Fonoster
|
|
43
|
+
*
|
|
44
|
+
* Licensed under the MIT License (the "License");
|
|
45
|
+
* you may not use this file except in compliance with
|
|
46
|
+
* the License. You may obtain a copy of the License at
|
|
47
|
+
*
|
|
48
|
+
* https://opensource.org/licenses/MIT
|
|
49
|
+
*
|
|
50
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
51
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
52
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
53
|
+
* See the License for the specific language governing permissions and
|
|
54
|
+
* limitations under the License.
|
|
55
|
+
*/
|
|
56
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
57
|
+
const grpc_health_check_1 = require("grpc-health-check");
|
|
58
|
+
/** How long one health check may take. */
|
|
59
|
+
exports.HEALTH_CHECK_TIMEOUT_MS = 2000;
|
|
60
|
+
/**
|
|
61
|
+
* Whether `address` (host:port) answers the standard gRPC health check (grpc.health.v1) with
|
|
62
|
+
* SERVING for the server as a whole. False on any error, a timeout or any other status (a Roomr
|
|
63
|
+
* node or AppBridge replica draining after SIGTERM reports NOT_SERVING). Through a Kubernetes
|
|
64
|
+
* Service or a Compose service name the check reaches one ready replica.
|
|
65
|
+
*/
|
|
66
|
+
function checkGrpcHealth(address, timeoutMs = exports.HEALTH_CHECK_TIMEOUT_MS) {
|
|
67
|
+
const Health = grpc.makeGenericClientConstructor(grpc_health_check_1.service, "Health");
|
|
68
|
+
const client = new Health(address, grpc.credentials.createInsecure());
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
client.Check({ service: "" }, { deadline: Date.now() + timeoutMs }, (error, res) => {
|
|
71
|
+
client.close();
|
|
72
|
+
resolve(!error && ((res === null || res === void 0 ? void 0 : res.status) === "SERVING" || (res === null || res === void 0 ? void 0 : res.status) === 1));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
/** The proto file this package loads. */
|
|
3
|
+
export declare const ROOMR_PROTO: string;
|
|
4
|
+
export type SipAuth = {
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
};
|
|
8
|
+
export type SipTarget = {
|
|
9
|
+
uri: string;
|
|
10
|
+
proxy?: string;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
auth?: SipAuth;
|
|
13
|
+
};
|
|
14
|
+
export type CreateCallRequest = {
|
|
15
|
+
callRef?: string;
|
|
16
|
+
to: SipTarget;
|
|
17
|
+
from?: string;
|
|
18
|
+
roomRef?: string;
|
|
19
|
+
session?: string;
|
|
20
|
+
ringTimeoutS?: number;
|
|
21
|
+
/** Passed through untouched in the events for this call (e.g. { app_ref }). */
|
|
22
|
+
metadata?: Record<string, string>;
|
|
23
|
+
maxDurationS?: number;
|
|
24
|
+
};
|
|
25
|
+
export type Call = {
|
|
26
|
+
callRef: string;
|
|
27
|
+
roomRef: string;
|
|
28
|
+
state: "RINGING" | "ANSWERED" | "ENDED";
|
|
29
|
+
participantRef: string;
|
|
30
|
+
};
|
|
31
|
+
export type CreateRoomRequest = {
|
|
32
|
+
roomRef?: string;
|
|
33
|
+
maxDurationS?: number;
|
|
34
|
+
/** Overrides the node's emptyRoomTimeout for this room (0: the node's). */
|
|
35
|
+
emptyTimeoutS?: number;
|
|
36
|
+
/** Overrides the node's maxParticipants for this room (0: the node's). */
|
|
37
|
+
maxParticipants?: number;
|
|
38
|
+
};
|
|
39
|
+
export type Room = {
|
|
40
|
+
roomRef: string;
|
|
41
|
+
};
|
|
42
|
+
/** The RPCs the control plane uses, each against one node's control address. */
|
|
43
|
+
export type RoomrCalls = {
|
|
44
|
+
createCall(addr: string, request: CreateCallRequest): Promise<Call>;
|
|
45
|
+
createRoom(addr: string, request: CreateRoomRequest): Promise<Room>;
|
|
46
|
+
endCall(addr: string, request: {
|
|
47
|
+
callRef: string;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
endRoom(addr: string, request: {
|
|
50
|
+
roomRef: string;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
};
|
|
53
|
+
/** A failed RPC, with its gRPC status code. */
|
|
54
|
+
export declare class RoomrError extends Error {
|
|
55
|
+
readonly code: grpc.status;
|
|
56
|
+
constructor(code: grpc.status, message: string);
|
|
57
|
+
}
|
|
58
|
+
/** The roomr.v1 package (protoLoader options as for the other Fonoster services). */
|
|
59
|
+
export declare function roomrPackage(): grpc.GrpcObject;
|
|
60
|
+
/** The Roomr service definition (fakes serve it). */
|
|
61
|
+
export declare function roomrService(): grpc.ServiceDefinition;
|
|
62
|
+
/** Each call's deadline (ms). */
|
|
63
|
+
export declare const ROOMR_DEADLINE_MS = 2000;
|
|
64
|
+
/**
|
|
65
|
+
* One gRPC channel per node address, reused by every call to it, with HTTP/2 keepalive and a
|
|
66
|
+
* deadline per call. Plaintext: the control port is reachable only inside the deployment's
|
|
67
|
+
* network. `close()` closes every channel.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createRoomrPool(options?: {
|
|
70
|
+
deadlineMs?: number;
|
|
71
|
+
}): RoomrCalls & {
|
|
72
|
+
close(): void;
|
|
73
|
+
};
|
|
74
|
+
export * from "./health";
|
|
75
|
+
export * from "./session";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
36
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
|
+
};
|
|
38
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
39
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
40
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
41
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
42
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
43
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
44
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.ROOMR_DEADLINE_MS = exports.RoomrError = exports.ROOMR_PROTO = void 0;
|
|
49
|
+
exports.roomrPackage = roomrPackage;
|
|
50
|
+
exports.roomrService = roomrService;
|
|
51
|
+
exports.createRoomrPool = createRoomrPool;
|
|
52
|
+
/**
|
|
53
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
54
|
+
* http://github.com/fonoster/fonoster
|
|
55
|
+
*
|
|
56
|
+
* This file is part of Fonoster
|
|
57
|
+
*
|
|
58
|
+
* Licensed under the MIT License (the "License");
|
|
59
|
+
* you may not use this file except in compliance with
|
|
60
|
+
* the License. You may obtain a copy of the License at
|
|
61
|
+
*
|
|
62
|
+
* https://opensource.org/licenses/MIT
|
|
63
|
+
*
|
|
64
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
65
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
66
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
67
|
+
* See the License for the specific language governing permissions and
|
|
68
|
+
* limitations under the License.
|
|
69
|
+
*/
|
|
70
|
+
const path = __importStar(require("path"));
|
|
71
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
72
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
73
|
+
// The controller's side of Roomr (roomr.v1.Roomr, redesign-roomr "Roomr is a gRPC service
|
|
74
|
+
// with three nouns"). The proto is fonoster/roomr's api/roomr/v1/roomr.proto; src/protos/roomr.proto
|
|
75
|
+
// is the copy of the pinned release (npm run sync:roomr; the client test checks it).
|
|
76
|
+
/** The proto file this package loads. */
|
|
77
|
+
exports.ROOMR_PROTO = path.join(__dirname, "..", "protos", "roomr.proto");
|
|
78
|
+
/** A failed RPC, with its gRPC status code. */
|
|
79
|
+
class RoomrError extends Error {
|
|
80
|
+
constructor(code, message) {
|
|
81
|
+
super(message);
|
|
82
|
+
this.code = code;
|
|
83
|
+
this.name = "RoomrError";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.RoomrError = RoomrError;
|
|
87
|
+
let loaded;
|
|
88
|
+
/** The roomr.v1 package (protoLoader options as for the other Fonoster services). */
|
|
89
|
+
function roomrPackage() {
|
|
90
|
+
loaded !== null && loaded !== void 0 ? loaded : (loaded = grpc.loadPackageDefinition(protoLoader.loadSync(exports.ROOMR_PROTO, {
|
|
91
|
+
keepCase: false,
|
|
92
|
+
longs: String,
|
|
93
|
+
enums: String,
|
|
94
|
+
defaults: true,
|
|
95
|
+
oneofs: true
|
|
96
|
+
})));
|
|
97
|
+
return loaded.roomr.v1;
|
|
98
|
+
}
|
|
99
|
+
/** The Roomr service definition (fakes serve it). */
|
|
100
|
+
function roomrService() {
|
|
101
|
+
return roomrPackage().Roomr.service;
|
|
102
|
+
}
|
|
103
|
+
/** Each call's deadline (ms). */
|
|
104
|
+
exports.ROOMR_DEADLINE_MS = 2000;
|
|
105
|
+
/**
|
|
106
|
+
* One gRPC channel per node address, reused by every call to it, with HTTP/2 keepalive and a
|
|
107
|
+
* deadline per call. Plaintext: the control port is reachable only inside the deployment's
|
|
108
|
+
* network. `close()` closes every channel.
|
|
109
|
+
*/
|
|
110
|
+
function createRoomrPool(options = {}) {
|
|
111
|
+
const Client = roomrPackage().Roomr;
|
|
112
|
+
const clients = new Map();
|
|
113
|
+
const client = (addr) => {
|
|
114
|
+
let c = clients.get(addr);
|
|
115
|
+
if (!c) {
|
|
116
|
+
c = new Client(addr, grpc.credentials.createInsecure(), {
|
|
117
|
+
"grpc.keepalive_time_ms": 10000,
|
|
118
|
+
"grpc.keepalive_timeout_ms": 5000,
|
|
119
|
+
"grpc.keepalive_permit_without_calls": 1
|
|
120
|
+
});
|
|
121
|
+
clients.set(addr, c);
|
|
122
|
+
}
|
|
123
|
+
return c;
|
|
124
|
+
};
|
|
125
|
+
const call = (method) => (addr, request) => new Promise((resolve, reject) => {
|
|
126
|
+
var _a;
|
|
127
|
+
const rpc = client(addr)[method].bind(client(addr));
|
|
128
|
+
rpc(request, { deadline: Date.now() + ((_a = options.deadlineMs) !== null && _a !== void 0 ? _a : exports.ROOMR_DEADLINE_MS) }, (err, res) => err ? reject(new RoomrError(err.code, err.details)) : resolve(res));
|
|
129
|
+
});
|
|
130
|
+
const empty = (method) => (addr, request) => __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
yield call(method)(addr, request);
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
createCall: call("CreateCall"),
|
|
135
|
+
createRoom: call("CreateRoom"),
|
|
136
|
+
endCall: empty("EndCall"),
|
|
137
|
+
endRoom: empty("EndRoom"),
|
|
138
|
+
close() {
|
|
139
|
+
for (const c of clients.values())
|
|
140
|
+
c.close();
|
|
141
|
+
clients.clear();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
__exportStar(require("./health"), exports);
|
|
146
|
+
__exportStar(require("./session"), exports);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
/** The proto file this module loads. */
|
|
3
|
+
export declare const SESSION_PROTO: string;
|
|
4
|
+
/** The port AppBridge serves Session.Connect (and grpc.health.v1) on by default. */
|
|
5
|
+
export declare const SESSION_PORT = 50071;
|
|
6
|
+
/** The gRPC service name, as the health service reports it. */
|
|
7
|
+
export declare const SESSION_SERVICE_NAME = "session.v1.Session";
|
|
8
|
+
/** An RTP endpoint: `codec` is "L16/16000" on app legs; `srtpKey` (SDES, base64) when encrypted. */
|
|
9
|
+
export type MediaEndpoint = {
|
|
10
|
+
ip: string;
|
|
11
|
+
port: number;
|
|
12
|
+
codec?: string;
|
|
13
|
+
srtpKey?: string;
|
|
14
|
+
};
|
|
15
|
+
export type SessionSipTarget = {
|
|
16
|
+
uri: string;
|
|
17
|
+
proxy?: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
auth?: {
|
|
20
|
+
username: string;
|
|
21
|
+
password: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export type SessionStart = {
|
|
25
|
+
callRef: string;
|
|
26
|
+
direction: "INBOUND" | "OUTBOUND";
|
|
27
|
+
from: string;
|
|
28
|
+
to: string;
|
|
29
|
+
/** Every INVITE header (inbound) or CreateCall's to.headers (outbound). */
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
/** CreateCall's metadata, untouched (outbound calls carry `app_ref`). */
|
|
32
|
+
metadata: Record<string, string>;
|
|
33
|
+
roomRef: string;
|
|
34
|
+
/** The node's RTP endpoint for this call's app leg. */
|
|
35
|
+
media?: MediaEndpoint;
|
|
36
|
+
nodeRef: string;
|
|
37
|
+
};
|
|
38
|
+
export type SessionDtmf = {
|
|
39
|
+
digit: string;
|
|
40
|
+
source?: string;
|
|
41
|
+
};
|
|
42
|
+
export type SessionEvent = {
|
|
43
|
+
participantJoined?: {
|
|
44
|
+
participantRef: string;
|
|
45
|
+
trackId: number;
|
|
46
|
+
};
|
|
47
|
+
participantLeft?: {
|
|
48
|
+
participantRef: string;
|
|
49
|
+
cause: string;
|
|
50
|
+
};
|
|
51
|
+
transferResult?: {
|
|
52
|
+
status: string;
|
|
53
|
+
cause?: string;
|
|
54
|
+
sipStatus?: number;
|
|
55
|
+
};
|
|
56
|
+
ended?: {
|
|
57
|
+
cause: string;
|
|
58
|
+
sipStatus?: number;
|
|
59
|
+
};
|
|
60
|
+
/** The oneof member that is set (protoLoader `oneofs: true`). */
|
|
61
|
+
event?: "participantJoined" | "participantLeft" | "transferResult" | "ended";
|
|
62
|
+
};
|
|
63
|
+
/** A message from the node. */
|
|
64
|
+
export type NodeMessage = {
|
|
65
|
+
start?: SessionStart;
|
|
66
|
+
dtmf?: SessionDtmf;
|
|
67
|
+
event?: SessionEvent;
|
|
68
|
+
msg?: "start" | "dtmf" | "event";
|
|
69
|
+
};
|
|
70
|
+
export type ListenTrack = {
|
|
71
|
+
trackId: number;
|
|
72
|
+
rtp: MediaEndpoint;
|
|
73
|
+
};
|
|
74
|
+
/** A message to the node (exactly one member set). */
|
|
75
|
+
export type AppMessage = {
|
|
76
|
+
answer?: {
|
|
77
|
+
media: MediaEndpoint;
|
|
78
|
+
maxDurationS?: number;
|
|
79
|
+
};
|
|
80
|
+
ready?: {
|
|
81
|
+
media: MediaEndpoint;
|
|
82
|
+
};
|
|
83
|
+
reject?: {
|
|
84
|
+
sipStatus: number;
|
|
85
|
+
reason?: string;
|
|
86
|
+
};
|
|
87
|
+
transfer?: {
|
|
88
|
+
to: SessionSipTarget;
|
|
89
|
+
mode: "cold" | "warm" | "refer";
|
|
90
|
+
music?: string;
|
|
91
|
+
ringTimeoutS?: number;
|
|
92
|
+
};
|
|
93
|
+
transferComplete?: Record<string, never>;
|
|
94
|
+
/** Cuts the caller off from the app; the node loops `music` (a WAV URL) to the caller alone. */
|
|
95
|
+
hold?: {
|
|
96
|
+
music?: string;
|
|
97
|
+
};
|
|
98
|
+
resume?: Record<string, never>;
|
|
99
|
+
/** RFC 4733 events on the caller's leg, in order. */
|
|
100
|
+
sendDtmf?: {
|
|
101
|
+
digits: string;
|
|
102
|
+
};
|
|
103
|
+
addListener?: {
|
|
104
|
+
listenerRef: string;
|
|
105
|
+
listen: ListenTrack[];
|
|
106
|
+
};
|
|
107
|
+
removeListener?: {
|
|
108
|
+
listenerRef: string;
|
|
109
|
+
};
|
|
110
|
+
hangup?: {
|
|
111
|
+
sipStatus?: number;
|
|
112
|
+
reason?: string;
|
|
113
|
+
};
|
|
114
|
+
msg?: AppMessageType;
|
|
115
|
+
};
|
|
116
|
+
export type AppMessageType = "answer" | "ready" | "reject" | "transfer" | "transferComplete" | "hold" | "resume" | "sendDtmf" | "addListener" | "removeListener" | "hangup";
|
|
117
|
+
/** The member a message carries (the oneof's name), for logs and fakes. */
|
|
118
|
+
export declare function messageType<T extends Record<string, unknown>>(message: T, members: readonly string[]): string | undefined;
|
|
119
|
+
export declare const APP_MESSAGE_TYPES: readonly AppMessageType[];
|
|
120
|
+
export declare const NODE_MESSAGE_TYPES: readonly ["start", "dtmf", "event"];
|
|
121
|
+
export declare const SESSION_EVENT_TYPES: readonly ["participantJoined", "participantLeft", "transferResult", "ended"];
|
|
122
|
+
/** Ended causes the node sends (media-control "Endings carry a reason"). */
|
|
123
|
+
export declare const ENDED_CAUSES: readonly ["caller_hangup", "callee_hangup", "busy", "no_answer", "rejected", "media_timeout", "node_error", "max_duration", "removed", "app_gone"];
|
|
124
|
+
/** The session.v1 package (protoLoader options as for the other Fonoster services). */
|
|
125
|
+
export declare function sessionPackage(): grpc.GrpcObject;
|
|
126
|
+
/** The Session service definition (AppBridge serves it). */
|
|
127
|
+
export declare function sessionService(): grpc.ServiceDefinition;
|
|
128
|
+
/** The Session client constructor (the node's side; fakes use it). */
|
|
129
|
+
export declare function sessionClient(): grpc.ServiceClientConstructor;
|
|
130
|
+
/** The server side of one control stream. */
|
|
131
|
+
export type SessionServerStream = grpc.ServerDuplexStream<NodeMessage, AppMessage>;
|
|
132
|
+
/** The client side of one control stream. */
|
|
133
|
+
export type SessionClientStream = grpc.ClientDuplexStream<NodeMessage, AppMessage>;
|