@fonoster/common 0.27.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.
Files changed (176) hide show
  1. package/dist/assistants/index.d.ts +2 -6
  2. package/dist/assistants/index.js +2 -6
  3. package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
  4. package/dist/assistants/sessionAssistantSchema.js +112 -0
  5. package/dist/assistants/testCasesSchema.d.ts +4 -4
  6. package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
  7. package/dist/calls/index.js +44 -0
  8. package/dist/cellsecrets/index.d.ts +91 -0
  9. package/dist/cellsecrets/index.js +299 -0
  10. package/dist/config/cli/check.d.ts +6 -0
  11. package/dist/config/cli/check.js +202 -0
  12. package/dist/config/cli/envFile.d.ts +23 -0
  13. package/dist/config/cli/envFile.js +81 -0
  14. package/dist/config/cli/exposure.d.ts +10 -0
  15. package/dist/config/cli/exposure.js +93 -0
  16. package/dist/config/cli/index.d.ts +2 -0
  17. package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
  18. package/dist/config/cli/info.d.ts +6 -0
  19. package/dist/config/cli/info.js +62 -0
  20. package/dist/config/cli/init.d.ts +9 -0
  21. package/dist/config/cli/init.js +152 -0
  22. package/dist/config/cli/main.d.ts +11 -0
  23. package/dist/config/cli/main.js +155 -0
  24. package/dist/config/cli/render.d.ts +44 -0
  25. package/dist/config/cli/render.js +129 -0
  26. package/dist/config/cli/secrets.d.ts +21 -0
  27. package/dist/config/cli/secrets.js +117 -0
  28. package/dist/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
  29. package/dist/config/cli/types.js +7 -0
  30. package/dist/config/configHints.d.ts +39 -0
  31. package/dist/config/configHints.js +61 -0
  32. package/dist/{voice → config}/index.d.ts +12 -13
  33. package/dist/config/index.js +46 -0
  34. package/dist/config/integrationsSchema.d.ts +32 -0
  35. package/dist/config/integrationsSchema.js +56 -0
  36. package/dist/config/interpolateEnv.d.ts +34 -0
  37. package/dist/config/interpolateEnv.js +70 -0
  38. package/dist/config/loadServiceConfig.d.ts +33 -0
  39. package/dist/config/loadServiceConfig.js +121 -0
  40. package/dist/config/logsSchema.d.ts +76 -0
  41. package/dist/config/logsSchema.js +49 -0
  42. package/dist/config/natsConnection.d.ts +53 -0
  43. package/dist/config/natsConnection.js +58 -0
  44. package/dist/config/parseConfigFile.d.ts +7 -0
  45. package/dist/config/parseConfigFile.js +60 -0
  46. package/dist/config/resolveConfigFile.d.ts +17 -0
  47. package/dist/config/resolveConfigFile.js +54 -0
  48. package/dist/config/resolveSecretRefs.d.ts +35 -0
  49. package/dist/config/resolveSecretRefs.js +129 -0
  50. package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
  51. package/dist/config/strictDeep.js +58 -0
  52. package/dist/config/telemetrySchema.d.ts +39 -0
  53. package/dist/config/telemetrySchema.js +44 -0
  54. package/dist/config/toJsonSchema.d.ts +30 -0
  55. package/dist/config/toJsonSchema.js +161 -0
  56. package/dist/constants.d.ts +1 -4
  57. package/dist/constants.js +4 -5
  58. package/dist/envs.d.ts +2 -1
  59. package/dist/envs.js +13 -11
  60. package/dist/identity/createAuthInterceptor.d.ts +9 -2
  61. package/dist/identity/createAuthInterceptor.js +36 -30
  62. package/dist/identity/hasAccess.d.ts +9 -3
  63. package/dist/identity/hasAccess.js +32 -8
  64. package/dist/identity/index.d.ts +1 -0
  65. package/dist/identity/index.js +1 -0
  66. package/dist/identity/isValidToken.d.ts +12 -2
  67. package/dist/identity/isValidToken.js +25 -9
  68. package/dist/identity/roles.js +22 -6
  69. package/dist/identity/types.d.ts +6 -1
  70. package/dist/identity/types.js +1 -0
  71. package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
  72. package/dist/identity/verifyAccessToken.js +44 -0
  73. package/dist/index.d.ts +11 -1
  74. package/dist/index.js +14 -2
  75. package/dist/net/assertPublicDestination.d.ts +52 -0
  76. package/dist/net/assertPublicDestination.js +342 -0
  77. package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
  78. package/dist/{voice → net}/index.js +1 -13
  79. package/dist/notifications/createSendEmail.js +3 -1
  80. package/dist/protos/applications.proto +124 -3
  81. package/dist/protos/authz.proto +23 -4
  82. package/dist/protos/capabilities.proto +94 -0
  83. package/dist/protos/identity.proto +26 -1
  84. package/dist/protos/providers.proto +135 -0
  85. package/dist/protos/webhooks.proto +140 -0
  86. package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
  87. package/dist/regions/hub/index.js +35 -0
  88. package/dist/regions/hub/natsJwt.d.ts +63 -0
  89. package/dist/regions/hub/natsJwt.js +195 -0
  90. package/dist/registry/Registry.d.ts +79 -0
  91. package/dist/registry/Registry.js +418 -0
  92. package/dist/registry/cli.d.ts +20 -0
  93. package/dist/registry/cli.js +424 -0
  94. package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
  95. package/dist/registry/features.js +36 -0
  96. package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
  97. package/dist/registry/index.js +40 -0
  98. package/dist/registry/prisma.d.ts +67 -0
  99. package/dist/registry/prisma.js +391 -0
  100. package/dist/registry/stores.d.ts +21 -0
  101. package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
  102. package/dist/registry/types.d.ts +98 -0
  103. package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
  104. package/dist/registry/workspaceKeys.d.ts +41 -0
  105. package/dist/registry/workspaceKeys.js +64 -0
  106. package/dist/session/index.d.ts +19 -0
  107. package/dist/session/index.js +35 -0
  108. package/dist/session/schema.d.ts +1951 -0
  109. package/dist/session/schema.js +397 -0
  110. package/dist/sessions/index.d.ts +93 -0
  111. package/dist/sessions/index.js +194 -0
  112. package/dist/telemetry.d.ts +76 -0
  113. package/dist/telemetry.js +360 -0
  114. package/dist/types.d.ts +3 -5
  115. package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
  116. package/dist/utils/WriteBehind.js +64 -0
  117. package/dist/utils/index.d.ts +1 -1
  118. package/dist/utils/index.js +1 -1
  119. package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
  120. package/dist/utils/logUnhandledRejections.js +37 -0
  121. package/dist/validators/calls.d.ts +2 -2
  122. package/dist/validators/calls.js +5 -2
  123. package/dist/validators/identity.d.ts +37 -8
  124. package/dist/validators/identity.js +22 -3
  125. package/dist/validators/index.d.ts +1 -0
  126. package/dist/validators/index.js +1 -0
  127. package/dist/validators/sip.d.ts +40 -0
  128. package/dist/validators/sip.js +99 -0
  129. package/dist/validators/sipnet/credentials.d.ts +2 -2
  130. package/dist/validators/sipnet/domains.js +7 -7
  131. package/dist/validators/sipnet/trunks.d.ts +14 -14
  132. package/dist/validators/sipnet/trunks.js +4 -2
  133. package/dist/webhooks/Dispatcher.d.ts +87 -0
  134. package/dist/webhooks/Dispatcher.js +225 -0
  135. package/dist/webhooks/bus.d.ts +49 -0
  136. package/dist/webhooks/bus.js +170 -0
  137. package/dist/webhooks/events.d.ts +66 -0
  138. package/dist/webhooks/events.js +62 -0
  139. package/dist/webhooks/index.d.ts +23 -0
  140. package/dist/webhooks/index.js +39 -0
  141. package/dist/webhooks/prisma.d.ts +55 -0
  142. package/dist/webhooks/prisma.js +242 -0
  143. package/dist/webhooks/signing.d.ts +8 -0
  144. package/dist/webhooks/signing.js +53 -0
  145. package/package.json +25 -12
  146. package/schema/session.v1.json +791 -0
  147. package/dist/assistants/assistantSchema.d.ts +0 -739
  148. package/dist/assistants/assistantSchema.js +0 -37
  149. package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
  150. package/dist/assistants/conversationSettingsSchema.js +0 -133
  151. package/dist/assistants/eventsHookSchema.d.ts +0 -38
  152. package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
  153. package/dist/assistants/languageModelConfigSchema.js +0 -91
  154. package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
  155. package/dist/protos/voice.proto +0 -557
  156. package/dist/voice/Dial.js +0 -20
  157. package/dist/voice/Gather.d.ts +0 -37
  158. package/dist/voice/Gather.js +0 -9
  159. package/dist/voice/Mute.d.ts +0 -31
  160. package/dist/voice/Mute.js +0 -9
  161. package/dist/voice/Play.js +0 -2
  162. package/dist/voice/PlayDtmf.js +0 -2
  163. package/dist/voice/PlaybackControl.d.ts +0 -31
  164. package/dist/voice/PlaybackControl.js +0 -11
  165. package/dist/voice/Record.d.ts +0 -36
  166. package/dist/voice/Record.js +0 -7
  167. package/dist/voice/Say.js +0 -2
  168. package/dist/voice/StopSay.js +0 -2
  169. package/dist/voice/Stream.d.ts +0 -53
  170. package/dist/voice/Stream.js +0 -19
  171. package/dist/voice/StreamGather.d.ts +0 -34
  172. package/dist/voice/StreamGather.js +0 -9
  173. package/dist/voice/Verb.d.ts +0 -27
  174. package/dist/voice/Verb.js +0 -2
  175. package/dist/voice/voice.d.ts +0 -171
  176. package/dist/voice/voice.js +0 -65
@@ -0,0 +1,391 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PrismaUsageSource = exports.PrismaRegistryStore = void 0;
13
+ exports.openPrismaClient = openPrismaClient;
14
+ /**
15
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
16
+ * http://github.com/fonoster/fonoster
17
+ *
18
+ * This file is part of Fonoster
19
+ *
20
+ * Licensed under the MIT License (the "License");
21
+ * you may not use this file except in compliance with
22
+ * the License. You may obtain a copy of the License at
23
+ *
24
+ * https://opensource.org/licenses/MIT
25
+ *
26
+ * Unless required by applicable law or agreed to in writing, software
27
+ * distributed under the License is distributed on an "AS IS" BASIS,
28
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29
+ * See the License for the specific language governing permissions and
30
+ * limitations under the License.
31
+ */
32
+ const WriteBehind_1 = require("../utils/WriteBehind");
33
+ const stores_1 = require("./stores");
34
+ const toState = (s) => s.toUpperCase();
35
+ const fromState = (s) => s.toLowerCase();
36
+ const opt = (v) => (v === null ? undefined : v);
37
+ /** Drops undefined keys so loaded entries equal the ones the registry wrote. */
38
+ function clean(o) {
39
+ return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
40
+ }
41
+ class PrismaRegistryStore extends stores_1.MemoryRegistryStore {
42
+ constructor(prisma) {
43
+ super();
44
+ this.prisma = prisma;
45
+ this.writes = new WriteBehind_1.WriteBehind();
46
+ }
47
+ /** Opens the store with the current catalog and audit log loaded. */
48
+ static open(prisma) {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ const store = new PrismaRegistryStore(prisma);
51
+ yield store.refresh();
52
+ return store;
53
+ });
54
+ }
55
+ /** Reloads the catalog (and audit log) from the database, after pending writes. */
56
+ refresh() {
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ yield this.writes.flush();
59
+ const [providers, models, voices, audit] = yield Promise.all([
60
+ this.prisma.registryProvider.findMany(),
61
+ this.prisma.registryModel.findMany(),
62
+ this.prisma.registryVoice.findMany({
63
+ include: { models: { orderBy: { position: "asc" } } }
64
+ }),
65
+ this.prisma.registryAudit.findMany({ orderBy: { seq: "asc" } })
66
+ ]);
67
+ const entries = new Map();
68
+ for (const p of providers)
69
+ entries.set(p.id, clean({
70
+ kind: "provider",
71
+ id: p.id,
72
+ adapter: p.adapter,
73
+ capabilities: p.capabilities,
74
+ credentialRef: p.credentialRef,
75
+ state: fromState(p.state),
76
+ replacement: opt(p.replacement),
77
+ retireOn: opt(p.retireOn)
78
+ }));
79
+ for (const m of models)
80
+ entries.set(m.id, clean({
81
+ kind: "model",
82
+ id: m.id,
83
+ type: m.type,
84
+ features: m.features,
85
+ languages: m.languages,
86
+ settings: m.settings,
87
+ state: fromState(m.state),
88
+ replacement: opt(m.replacement),
89
+ retireOn: opt(m.retireOn)
90
+ }));
91
+ for (const v of voices)
92
+ entries.set(v.id, clean({
93
+ kind: "voice",
94
+ id: v.id,
95
+ models: v.models.map((r) => r.modelId),
96
+ language: opt(v.language),
97
+ gender: opt(v.gender),
98
+ ownerWorkspace: opt(v.ownerWorkspace),
99
+ state: fromState(v.state),
100
+ replacement: opt(v.replacement),
101
+ retireOn: opt(v.retireOn)
102
+ }));
103
+ this.entries = entries;
104
+ this.log = audit.map((a) => clean({
105
+ at: a.at.toISOString(),
106
+ actor: a.actor,
107
+ action: a.action,
108
+ target: a.target,
109
+ details: opt(a.details)
110
+ }));
111
+ });
112
+ }
113
+ put(entry) {
114
+ var _a, _b;
115
+ super.put(entry);
116
+ const e = JSON.parse(JSON.stringify(entry));
117
+ const common = {
118
+ state: toState(e.state),
119
+ replacement: (_a = e.replacement) !== null && _a !== void 0 ? _a : null,
120
+ retireOn: (_b = e.retireOn) !== null && _b !== void 0 ? _b : null
121
+ };
122
+ const provider = e.id.split("/")[0];
123
+ this.writes.push(() => __awaiter(this, void 0, void 0, function* () {
124
+ var _a, _b, _c;
125
+ if (e.kind === "provider") {
126
+ const data = Object.assign({ adapter: e.adapter, capabilities: e.capabilities, credentialRef: e.credentialRef }, common);
127
+ yield this.prisma.registryProvider.upsert({
128
+ where: { id: e.id },
129
+ create: Object.assign({ id: e.id }, data),
130
+ update: data
131
+ });
132
+ }
133
+ else if (e.kind === "model") {
134
+ const data = Object.assign({ providerId: provider, type: e.type, features: e.features, languages: e.languages, settings: e.settings }, common);
135
+ yield this.prisma.registryModel.upsert({
136
+ where: { id: e.id },
137
+ create: Object.assign({ id: e.id }, data),
138
+ update: data
139
+ });
140
+ }
141
+ else {
142
+ const data = Object.assign({ providerId: provider, language: (_a = e.language) !== null && _a !== void 0 ? _a : null, gender: (_b = e.gender) !== null && _b !== void 0 ? _b : null, ownerWorkspace: (_c = e.ownerWorkspace) !== null && _c !== void 0 ? _c : null }, common);
143
+ yield this.prisma.$transaction([
144
+ this.prisma.registryVoice.upsert({
145
+ where: { id: e.id },
146
+ create: Object.assign({ id: e.id }, data),
147
+ update: data
148
+ }),
149
+ this.prisma.registryVoiceModel.deleteMany({
150
+ where: { voiceId: e.id }
151
+ }),
152
+ this.prisma.registryVoiceModel.createMany({
153
+ data: e.models.map((modelId, position) => ({
154
+ voiceId: e.id,
155
+ modelId,
156
+ position
157
+ }))
158
+ })
159
+ ]);
160
+ }
161
+ }));
162
+ }
163
+ audit(entry) {
164
+ super.audit(entry);
165
+ this.writes.push(() => {
166
+ var _a;
167
+ return this.prisma.registryAudit.create({
168
+ data: {
169
+ at: new Date(entry.at),
170
+ actor: entry.actor,
171
+ action: entry.action,
172
+ target: entry.target,
173
+ details: (_a = entry.details) !== null && _a !== void 0 ? _a : undefined
174
+ }
175
+ });
176
+ });
177
+ }
178
+ /** Waits for every queued write; throws the first failure. */
179
+ flush() {
180
+ return this.writes.flush();
181
+ }
182
+ }
183
+ exports.PrismaRegistryStore = PrismaRegistryStore;
184
+ /**
185
+ * Usages of catalog ids in apps (speech defaults: voice, TTS and STT models) and assistants (the
186
+ * Autopilot assistant in an app's Intelligence config: `languageModel.model`, and
187
+ * `conversationSettings.voice`). Legacy rows with raw vendor names are not usages until migrated
188
+ * to catalog ids (task 5.1).
189
+ */
190
+ class PrismaUsageSource {
191
+ constructor(prisma, options) {
192
+ this.prisma = prisma;
193
+ this.options = options;
194
+ this.rows = [];
195
+ this.origins = new WeakMap();
196
+ this.writes = new WriteBehind_1.WriteBehind();
197
+ /** Rewrites skipped because the config changed after it was read. */
198
+ this.conflicts = [];
199
+ }
200
+ static open(prisma_1) {
201
+ return __awaiter(this, arguments, void 0, function* (prisma, options = {}) {
202
+ const source = new PrismaUsageSource(prisma, options);
203
+ yield source.refresh();
204
+ return source;
205
+ });
206
+ }
207
+ refresh() {
208
+ return __awaiter(this, void 0, void 0, function* () {
209
+ yield this.writes.flush();
210
+ this.rows = yield this.prisma.application.findMany({
211
+ select: {
212
+ ref: true,
213
+ accessKeyId: true,
214
+ name: true,
215
+ speechVoice: true,
216
+ speechSttModel: true,
217
+ speechTtsModel: true,
218
+ speechSettings: true,
219
+ intelligence: { select: { config: true } }
220
+ },
221
+ orderBy: { ref: "asc" }
222
+ });
223
+ });
224
+ }
225
+ find(id) {
226
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
227
+ const out = [];
228
+ const add = (usage, origin) => {
229
+ this.origins.set(usage, origin);
230
+ out.push(usage);
231
+ };
232
+ for (const row of this.rows) {
233
+ const app = { kind: "app", ref: row.ref, name: row.name };
234
+ if (row.speechVoice === id || row.speechTtsModel === id) {
235
+ add({
236
+ workspace: row.accessKeyId,
237
+ config: app,
238
+ model: (_a = row.speechTtsModel) !== null && _a !== void 0 ? _a : undefined,
239
+ voice: (_b = row.speechVoice) !== null && _b !== void 0 ? _b : undefined,
240
+ settings: Object.keys((_c = row.speechSettings) !== null && _c !== void 0 ? _c : {}),
241
+ requires: ["streaming"]
242
+ }, { kind: "app-tts", row });
243
+ }
244
+ if (row.speechSttModel === id) {
245
+ add({
246
+ workspace: row.accessKeyId,
247
+ config: app,
248
+ model: id,
249
+ settings: [],
250
+ requires: ["streaming"]
251
+ }, { kind: "app-stt", row });
252
+ }
253
+ const assistant = (_d = row.intelligence) === null || _d === void 0 ? void 0 : _d.config;
254
+ const llm = (_e = assistant === null || assistant === void 0 ? void 0 : assistant.languageModel) === null || _e === void 0 ? void 0 : _e.model;
255
+ const config = {
256
+ kind: "assistant",
257
+ ref: row.ref,
258
+ name: row.name
259
+ };
260
+ if (llm === id) {
261
+ add({
262
+ workspace: row.accessKeyId,
263
+ config,
264
+ model: llm,
265
+ settings: [],
266
+ requires: ((_g = (_f = assistant === null || assistant === void 0 ? void 0 : assistant.languageModel) === null || _f === void 0 ? void 0 : _f.tools) === null || _g === void 0 ? void 0 : _g.length)
267
+ ? ["streaming", "tool-calling"]
268
+ : ["streaming"]
269
+ }, { kind: "assistant-llm", row, model: llm });
270
+ }
271
+ const voice = (_h = assistant === null || assistant === void 0 ? void 0 : assistant.conversationSettings) === null || _h === void 0 ? void 0 : _h.voice;
272
+ if (voice === id) {
273
+ add({
274
+ workspace: row.accessKeyId,
275
+ config,
276
+ model: (_j = row.speechTtsModel) !== null && _j !== void 0 ? _j : undefined,
277
+ voice,
278
+ settings: [],
279
+ requires: ["streaming"]
280
+ }, { kind: "assistant-voice", row, voice });
281
+ }
282
+ }
283
+ return out;
284
+ }
285
+ /**
286
+ * Queues the rewrite. The write only applies if the field still holds the value read; otherwise
287
+ * it is skipped, audited by the caller's flush report (`conflicts`), and the config keeps its id.
288
+ */
289
+ rewrite(usage, change) {
290
+ var _a, _b;
291
+ const origin = this.origins.get(usage);
292
+ if (!origin)
293
+ return false;
294
+ const row = origin.row;
295
+ const conflict = (reason) => this.conflicts.push({ usage, reason });
296
+ if (origin.kind === "app-tts" || origin.kind === "app-stt") {
297
+ const where = { ref: row.ref };
298
+ const data = { updatedAt: new Date() };
299
+ if (origin.kind === "app-stt") {
300
+ where.speechSttModel = row.speechSttModel;
301
+ if (change.model)
302
+ data.speechSttModel = change.model;
303
+ }
304
+ else {
305
+ where.speechVoice = row.speechVoice;
306
+ where.speechTtsModel = row.speechTtsModel;
307
+ if (change.model)
308
+ data.speechTtsModel = change.model;
309
+ if (change.voice)
310
+ data.speechVoice = change.voice;
311
+ if (change.dropSettings.length && row.speechSettings) {
312
+ data.speechSettings = Object.fromEntries(Object.entries(row.speechSettings).filter(([k]) => !change.dropSettings.includes(k)));
313
+ }
314
+ }
315
+ this.writes.push(() => __awaiter(this, void 0, void 0, function* () {
316
+ const { count } = yield this.prisma.application.updateMany({
317
+ where,
318
+ data
319
+ });
320
+ if (count === 0)
321
+ conflict("app changed since it was read");
322
+ }));
323
+ return true;
324
+ }
325
+ const config = JSON.parse(JSON.stringify((_b = (_a = row.intelligence) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : {}));
326
+ let path;
327
+ let expected;
328
+ if (origin.kind === "assistant-llm") {
329
+ path = ["languageModel", "model"];
330
+ expected = origin.model;
331
+ if (change.model)
332
+ config.languageModel.model = change.model;
333
+ }
334
+ else {
335
+ path = ["conversationSettings", "voice"];
336
+ expected = origin.voice;
337
+ if (change.voice)
338
+ config.conversationSettings.voice = change.voice;
339
+ }
340
+ this.writes.push(() => __awaiter(this, void 0, void 0, function* () {
341
+ const { count } = yield this.prisma.intelligence.updateMany({
342
+ where: { applicationRef: row.ref, config: { path, equals: expected } },
343
+ data: { config }
344
+ });
345
+ if (count === 0)
346
+ conflict("assistant changed since it was read");
347
+ else
348
+ yield this.prisma.application.update({
349
+ where: { ref: row.ref },
350
+ data: { updatedAt: new Date() }
351
+ });
352
+ }));
353
+ return true;
354
+ }
355
+ notify(workspace, notice) {
356
+ this.writes.push(() => __awaiter(this, void 0, void 0, function* () {
357
+ var _a, _b;
358
+ yield this.prisma.registryNotice.create({
359
+ data: {
360
+ accessKeyId: workspace,
361
+ type: String(notice.type),
362
+ data: notice
363
+ }
364
+ });
365
+ (_b = (_a = this.options).onNotice) === null || _b === void 0 ? void 0 : _b.call(_a, workspace, notice);
366
+ }));
367
+ }
368
+ /** Waits for every queued write; throws the first failure. */
369
+ flush() {
370
+ return this.writes.flush();
371
+ }
372
+ }
373
+ exports.PrismaUsageSource = PrismaUsageSource;
374
+ /**
375
+ * Opens a Prisma client generated from the apiserver's schema, with field encryption when a key
376
+ * is given. Loaded at run time so `@fonoster/common` does not depend on a generated client.
377
+ */
378
+ function openPrismaClient(options) {
379
+ var _a, _b;
380
+ /* eslint-disable @typescript-eslint/no-var-requires */
381
+ const { PrismaClient, Prisma } = require((_b = (_a = options.clientModule) !== null && _a !== void 0 ? _a : process.env.FONOSTER_PRISMA_CLIENT) !== null && _b !== void 0 ? _b : "@prisma/client");
382
+ const client = new PrismaClient({ datasourceUrl: options.url });
383
+ if (!options.encryptionKey)
384
+ return client;
385
+ const { fieldEncryptionExtension } = require("prisma-field-encryption");
386
+ /* eslint-enable @typescript-eslint/no-var-requires */
387
+ return client.$extends(fieldEncryptionExtension({
388
+ encryptionKey: options.encryptionKey,
389
+ dmmf: Prisma === null || Prisma === void 0 ? void 0 : Prisma.dmmf
390
+ }));
391
+ }
@@ -0,0 +1,21 @@
1
+ import { AuditEntry, CatalogEntry, RegistryStore } from "./types";
2
+ export declare class MemoryRegistryStore implements RegistryStore {
3
+ protected entries: Map<string, CatalogEntry>;
4
+ protected log: AuditEntry[];
5
+ all(): CatalogEntry[];
6
+ get(id: string): CatalogEntry;
7
+ put(entry: CatalogEntry): void;
8
+ audit(entry: AuditEntry): void;
9
+ history(id?: string): AuditEntry[];
10
+ }
11
+ /**
12
+ * A JSON file store, for the operator utility until the registry tables exist (see build-log.md).
13
+ * Every write rewrites the file atomically.
14
+ */
15
+ export declare class FileRegistryStore extends MemoryRegistryStore {
16
+ private readonly file;
17
+ constructor(file: string);
18
+ put(entry: CatalogEntry): void;
19
+ audit(entry: AuditEntry): void;
20
+ private save;
21
+ }
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.EventsHookAllowedEvents = exports.eventsHookSchema = void 0;
36
+ exports.FileRegistryStore = exports.MemoryRegistryStore = void 0;
37
37
  /**
38
38
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
39
39
  * http://github.com/fonoster/fonoster
@@ -52,20 +52,57 @@ exports.EventsHookAllowedEvents = exports.eventsHookSchema = void 0;
52
52
  * See the License for the specific language governing permissions and
53
53
  * limitations under the License.
54
54
  */
55
- const zod_1 = require("zod");
56
- const Messages = __importStar(require("../messages"));
57
- var EventsHookAllowedEvents;
58
- (function (EventsHookAllowedEvents) {
59
- EventsHookAllowedEvents["ALL"] = "all";
60
- EventsHookAllowedEvents["CONVERSATION_STARTED"] = "conversation.started";
61
- EventsHookAllowedEvents["CONVERSATION_ENDED"] = "conversation.ended";
62
- })(EventsHookAllowedEvents || (exports.EventsHookAllowedEvents = EventsHookAllowedEvents = {}));
63
- const eventsHookSchema = zod_1.z.object({
64
- url: zod_1.z.string().url({ message: Messages.VALID_URL }),
65
- headers: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
66
- events: zod_1.z
67
- .array(zod_1.z.nativeEnum(EventsHookAllowedEvents))
68
- .min(1)
69
- .default([EventsHookAllowedEvents.ALL])
70
- });
71
- exports.eventsHookSchema = eventsHookSchema;
55
+ const fs = __importStar(require("fs"));
56
+ class MemoryRegistryStore {
57
+ constructor() {
58
+ this.entries = new Map();
59
+ this.log = [];
60
+ }
61
+ all() {
62
+ return [...this.entries.values()].sort((a, b) => a.id.localeCompare(b.id));
63
+ }
64
+ get(id) {
65
+ return this.entries.get(id);
66
+ }
67
+ put(entry) {
68
+ this.entries.set(entry.id, JSON.parse(JSON.stringify(entry)));
69
+ }
70
+ audit(entry) {
71
+ this.log.push(entry);
72
+ }
73
+ history(id) {
74
+ return id ? this.log.filter((e) => e.target === id) : [...this.log];
75
+ }
76
+ }
77
+ exports.MemoryRegistryStore = MemoryRegistryStore;
78
+ /**
79
+ * A JSON file store, for the operator utility until the registry tables exist (see build-log.md).
80
+ * Every write rewrites the file atomically.
81
+ */
82
+ class FileRegistryStore extends MemoryRegistryStore {
83
+ constructor(file) {
84
+ var _a;
85
+ super();
86
+ this.file = file;
87
+ if (fs.existsSync(file)) {
88
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
89
+ for (const e of data.entries)
90
+ this.entries.set(e.id, e);
91
+ this.log = (_a = data.audit) !== null && _a !== void 0 ? _a : [];
92
+ }
93
+ }
94
+ put(entry) {
95
+ super.put(entry);
96
+ this.save();
97
+ }
98
+ audit(entry) {
99
+ super.audit(entry);
100
+ this.save();
101
+ }
102
+ save() {
103
+ const tmp = `${this.file}.tmp`;
104
+ fs.writeFileSync(tmp, JSON.stringify({ entries: this.all(), audit: this.log }, null, 2));
105
+ fs.renameSync(tmp, this.file);
106
+ }
107
+ }
108
+ exports.FileRegistryStore = FileRegistryStore;
@@ -0,0 +1,98 @@
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
+ export type Lifecycle = "active" | "deprecated" | "retired";
20
+ export type Capability = "stt" | "tts" | "llm";
21
+ export type ItemKind = "provider" | "model" | "voice";
22
+ export type ProviderEntry = {
23
+ kind: "provider";
24
+ id: string;
25
+ adapter: string;
26
+ capabilities: Capability[];
27
+ credentialRef: string;
28
+ state: Lifecycle;
29
+ replacement?: string;
30
+ retireOn?: string;
31
+ };
32
+ export type ModelEntry = {
33
+ kind: "model";
34
+ id: string;
35
+ type: Capability;
36
+ features: string[];
37
+ languages: string[];
38
+ /** Settings the model accepts (for example "style", "stability"). */
39
+ settings: string[];
40
+ state: Lifecycle;
41
+ replacement?: string;
42
+ retireOn?: string;
43
+ };
44
+ export type VoiceEntry = {
45
+ kind: "voice";
46
+ id: string;
47
+ models: string[];
48
+ language?: string;
49
+ gender?: string;
50
+ /** Set for a workspace's own (cloned) voice. */
51
+ ownerWorkspace?: string;
52
+ state: Lifecycle;
53
+ replacement?: string;
54
+ retireOn?: string;
55
+ };
56
+ export type CatalogEntry = ProviderEntry | ModelEntry | VoiceEntry;
57
+ export type AuditEntry = {
58
+ at: string;
59
+ actor: string;
60
+ action: string;
61
+ target: string;
62
+ details?: Record<string, unknown>;
63
+ };
64
+ /** One place a catalog id is used: an app's speech config or an assistant's model config. */
65
+ export type Usage = {
66
+ workspace: string;
67
+ config: {
68
+ kind: "app" | "assistant";
69
+ ref: string;
70
+ name: string;
71
+ };
72
+ model?: string;
73
+ voice?: string;
74
+ /** Settings the config sets on the model (dropped when the replacement does not accept them). */
75
+ settings: string[];
76
+ /** Hard features the config needs (derived from its content, e.g. tools need tool-calling). */
77
+ requires: string[];
78
+ };
79
+ /** Where usages come from (apps and assistants in the control-plane database). */
80
+ export interface UsageSource {
81
+ find(id: string): Usage[];
82
+ /** Rewrites one config; returns false if it changed since it was read. */
83
+ rewrite(usage: Usage, change: {
84
+ model?: string;
85
+ voice?: string;
86
+ dropSettings: string[];
87
+ }): boolean;
88
+ /** Tells a workspace about a change (dashboard notice). */
89
+ notify(workspace: string, notice: Record<string, unknown>): void;
90
+ }
91
+ /** Where the catalog and the audit log are kept. */
92
+ export interface RegistryStore {
93
+ all(): CatalogEntry[];
94
+ get(id: string): CatalogEntry | undefined;
95
+ put(entry: CatalogEntry): void;
96
+ audit(entry: AuditEntry): void;
97
+ history(id?: string): AuditEntry[];
98
+ }
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
4
  * http://github.com/fonoster/fonoster
@@ -16,13 +17,6 @@
16
17
  * See the License for the specific language governing permissions and
17
18
  * limitations under the License.
18
19
  */
19
- import { Struct } from "pb-util";
20
- import { VerbRequest } from "./Verb";
21
- type SayRequest = VerbRequest & {
22
- text: string;
23
- options?: Struct;
24
- };
25
- type SayOptions = {
26
- [key: string]: any;
27
- };
28
- export { SayOptions, SayRequest };
20
+ // The provider registry (add-provider-registry): providers, models and voices by catalog id,
21
+ // with a manual lifecycle. The catalog never holds a secret: providers name a credentialRef.
22
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,41 @@
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
+ import { PrismaLike } from "./prisma";
20
+ export type WorkspaceKeysOptions = {
21
+ /** How long a looked-up key (or its absence) is reused (default 30 s). */
22
+ cacheMs?: number;
23
+ now?: () => number;
24
+ };
25
+ export declare class PrismaWorkspaceKeys {
26
+ private readonly prisma;
27
+ private readonly options;
28
+ private cache;
29
+ constructor(prisma: PrismaLike, options?: WorkspaceKeysOptions);
30
+ private now;
31
+ /** The workspace's key for a provider, or undefined (then the platform key applies). */
32
+ get(workspaceRef: string, providerId: string): Promise<string | undefined>;
33
+ /**
34
+ * The workspace's key for a credentialRef: the providers that use that ref (from the catalog)
35
+ * are tried in order.
36
+ */
37
+ forCredentialRef(workspaceRef: string, credentialRef: string, providers: {
38
+ id: string;
39
+ credentialRef: string;
40
+ }[]): Promise<string | undefined>;
41
+ }