@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,424 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.usesDatabase = void 0;
47
+ exports.run = run;
48
+ exports.runWithDatabase = runWithDatabase;
49
+ exports.runTraced = runTraced;
50
+ /**
51
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
52
+ * http://github.com/fonoster/fonoster
53
+ *
54
+ * This file is part of Fonoster
55
+ *
56
+ * Licensed under the MIT License (the "License");
57
+ * you may not use this file except in compliance with
58
+ * the License. You may obtain a copy of the License at
59
+ *
60
+ * https://opensource.org/licenses/MIT
61
+ *
62
+ * Unless required by applicable law or agreed to in writing, software
63
+ * distributed under the License is distributed on an "AS IS" BASIS,
64
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65
+ * See the License for the specific language governing permissions and
66
+ * limitations under the License.
67
+ */
68
+ const fs = __importStar(require("fs"));
69
+ const telemetry_1 = require("../telemetry");
70
+ const Dispatcher_1 = require("../webhooks/Dispatcher");
71
+ const events_1 = require("../webhooks/events");
72
+ const prisma_1 = require("../webhooks/prisma");
73
+ const prisma_2 = require("./prisma");
74
+ const Registry_1 = require("./Registry");
75
+ const stores_1 = require("./stores");
76
+ function parseArgs(argv) {
77
+ const [command = "help", ...rest] = argv;
78
+ const positional = [];
79
+ const flags = {};
80
+ for (let i = 0; i < rest.length; i++) {
81
+ const a = rest[i];
82
+ if (a.startsWith("--") || /^-[a-z]$/.test(a)) {
83
+ // `--name value`, `--flag`, or a short `-f value` (plan -f, apply -f).
84
+ const key = a.replace(/^--?/, "");
85
+ const next = rest[i + 1];
86
+ if (next === undefined || next.startsWith("--"))
87
+ flags[key] = true;
88
+ else {
89
+ flags[key] = next;
90
+ i++;
91
+ }
92
+ }
93
+ else
94
+ positional.push(a);
95
+ }
96
+ return { command, positional, flags };
97
+ }
98
+ /** The command line as audited: database flags (they may carry credentials in URLs) left out. */
99
+ function commandLine(argv) {
100
+ const out = [];
101
+ for (let i = 0; i < argv.length; i++) {
102
+ if (argv[i] === "--database-url" || argv[i] === "--store") {
103
+ i++;
104
+ continue;
105
+ }
106
+ out.push(/\s/.test(argv[i]) ? JSON.stringify(argv[i]) : argv[i]);
107
+ }
108
+ return `fonoster-registry ${out.join(" ")}`;
109
+ }
110
+ const list = (v) => typeof v === "string"
111
+ ? v
112
+ .split(",")
113
+ .map((s) => s.trim())
114
+ .filter(Boolean)
115
+ : [];
116
+ const str = (v, name) => {
117
+ if (typeof v !== "string")
118
+ throw new Registry_1.RegistryError(`--${name} is required`);
119
+ return v;
120
+ };
121
+ const HELP = `fonoster-registry <command>
122
+
123
+ add-provider <id> --adapter <a> --capabilities stt,tts --credential-ref <name>
124
+ add-model <provider/model> --type stt|tts|llm [--features a,b] [--languages en,es] [--settings s]
125
+ add-voice <provider/voice> --models <m1,m2> [--language en] [--gender female]
126
+ deprecate-provider|deprecate-model|deprecate-voice <id> --replacement <id> --retire-on YYYY-MM-DD
127
+ retire-provider|retire-model|retire-voice <id> [--voice-map old=new,...] --yes
128
+ usage <id> | list [provider|model|voice] | show <id> | history [<id>]
129
+ export | plan -f <file> | apply -f <file>
130
+
131
+ --store file|database where the catalog lives (default: FONOSTER_REGISTRY_STORE or file)
132
+ --database-url <url> with --store database (default: FONOSTER_REGISTRY_DATABASE_URL)`;
133
+ const DB_FLAGS = ["store", "database-url"];
134
+ /** The options each write command accepts (by command without its kind). */
135
+ const OPTIONS = {
136
+ add: [
137
+ "adapter",
138
+ "capabilities",
139
+ "credential-ref",
140
+ "type",
141
+ "features",
142
+ "languages",
143
+ "settings",
144
+ "models",
145
+ "language",
146
+ "gender"
147
+ ],
148
+ deprecate: ["replacement", "retire-on"],
149
+ retire: ["voice-map", "yes"]
150
+ };
151
+ /** Runs one command against the JSON file store (synchronous; the default store). */
152
+ function run(argv, out = console.log) {
153
+ var _a;
154
+ const { command, positional, flags } = parseArgs(argv);
155
+ for (const f of DB_FLAGS)
156
+ delete flags[f];
157
+ const registry = new Registry_1.Registry({
158
+ store: new stores_1.FileRegistryStore((_a = process.env.FONOSTER_REGISTRY_FILE) !== null && _a !== void 0 ? _a : "registry.json"),
159
+ testMode: process.env.FONOSTER_TEST_MODE === "true",
160
+ commandLine: commandLine(argv)
161
+ });
162
+ return execute(registry, command, positional, flags, out);
163
+ }
164
+ const usesDatabase = (argv) => {
165
+ var _a, _b;
166
+ const { flags } = parseArgs(argv);
167
+ return (((_b = (_a = flags.store) !== null && _a !== void 0 ? _a : process.env.FONOSTER_REGISTRY_STORE) !== null && _b !== void 0 ? _b : "file") ===
168
+ "database");
169
+ };
170
+ exports.usesDatabase = usesDatabase;
171
+ /**
172
+ * Runs one command against the control-plane database: loads the catalog and usages, runs the
173
+ * command, then waits for every write, records skipped rewrites in the audit log and sends due
174
+ * `provider.deprecated` webhooks once (retries are adopted by a running dispatcher).
175
+ */
176
+ function runWithDatabase(argv_1) {
177
+ return __awaiter(this, arguments, void 0, function* (argv, out = console.log, deps = {}) {
178
+ var _a, _b, _c, _d;
179
+ const { command, positional, flags } = parseArgs(argv);
180
+ for (const f of DB_FLAGS)
181
+ delete flags[f];
182
+ const url = (_a = parseArgs(argv).flags["database-url"]) !== null && _a !== void 0 ? _a : process.env.FONOSTER_REGISTRY_DATABASE_URL;
183
+ if (!deps.prisma && !url) {
184
+ out("error: --database-url or FONOSTER_REGISTRY_DATABASE_URL is required");
185
+ return 1;
186
+ }
187
+ const encryptionKey = process.env.FONOSTER_REGISTRY_ENCRYPTION_KEY;
188
+ const prisma = (_b = deps.prisma) !== null && _b !== void 0 ? _b : (0, prisma_2.openPrismaClient)({ url: url, encryptionKey });
189
+ try {
190
+ const store = yield prisma_2.PrismaRegistryStore.open(prisma);
191
+ const hooks = deps.prisma || encryptionKey
192
+ ? yield prisma_1.PrismaWebhookStore.open(prisma)
193
+ : undefined;
194
+ const dispatcher = hooks && new Dispatcher_1.WebhookDispatcher({ store: hooks });
195
+ const usages = yield prisma_2.PrismaUsageSource.open(prisma, {
196
+ onNotice: (workspace, n) => {
197
+ if (n.type === "provider.deprecated")
198
+ dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.publish((0, events_1.providerDeprecated)(workspace, {
199
+ id: String(n.id),
200
+ replacement: String(n.replacement),
201
+ retireOn: String(n.retireOn)
202
+ }));
203
+ }
204
+ });
205
+ const registry = new Registry_1.Registry({
206
+ store,
207
+ usages,
208
+ actor: deps.actor,
209
+ testMode: process.env.FONOSTER_TEST_MODE === "true",
210
+ commandLine: commandLine(argv)
211
+ });
212
+ const sessions = command === "usage" && positional[0]
213
+ ? yield sessionCounts(prisma, positional[0])
214
+ : undefined;
215
+ const code = execute(registry, command, positional, flags, out, sessions);
216
+ yield usages.flush();
217
+ for (const c of usages.conflicts) {
218
+ out(`skipped ${c.usage.config.kind} ${c.usage.config.name}: ${c.reason}`);
219
+ store.audit({
220
+ at: new Date().toISOString(),
221
+ actor: (_d = (_c = deps.actor) !== null && _c !== void 0 ? _c : process.env.USER) !== null && _d !== void 0 ? _d : "operator",
222
+ action: "rewrite-skipped",
223
+ target: positional[0],
224
+ details: { workspace: c.usage.workspace, config: c.usage.config }
225
+ });
226
+ }
227
+ yield store.flush();
228
+ if (dispatcher && hooks) {
229
+ yield dispatcher.runDue();
230
+ yield hooks.flush();
231
+ }
232
+ else if (command.startsWith("deprecate-")) {
233
+ out("note: no webhooks sent (set FONOSTER_REGISTRY_ENCRYPTION_KEY); notices were stored");
234
+ }
235
+ return code;
236
+ }
237
+ finally {
238
+ if (!deps.prisma)
239
+ yield prisma.$disconnect();
240
+ }
241
+ });
242
+ }
243
+ function execute(registry, command, positional, flags, out, sessions) {
244
+ var _a, _b;
245
+ const id = positional[0];
246
+ const printImpact = (lines) => lines.length
247
+ ? lines.forEach((l) => {
248
+ // The count goes before an action hint: "... (3 apps) → needs --voice-map ...".
249
+ const [what, hint] = l.text.split(" → ");
250
+ out(`${l.mark} ${what} (${l.count} apps)${hint ? ` → ${hint}` : ""}`);
251
+ })
252
+ : out("no configs use it");
253
+ try {
254
+ // Write commands take only their own options: anything else (`--api-key`) is refused before
255
+ // it can reach the catalog (spec "A key cannot be smuggled into the catalog").
256
+ const allowed = OPTIONS[command.replace(/-(provider|model|voice)$/, "")];
257
+ const unknown = allowed && Object.keys(flags).find((f) => !allowed.includes(f));
258
+ if (unknown)
259
+ throw new Registry_1.RegistryError(`unknown option: --${unknown}`);
260
+ switch (command) {
261
+ case "add-provider": {
262
+ registry.addProvider({
263
+ id,
264
+ adapter: str(flags.adapter, "adapter"),
265
+ capabilities: list(flags.capabilities),
266
+ credentialRef: str(flags["credential-ref"], "credential-ref")
267
+ });
268
+ break;
269
+ }
270
+ case "add-model":
271
+ registry.addModel({
272
+ id,
273
+ type: str(flags.type, "type"),
274
+ features: list(flags.features),
275
+ languages: list(flags.languages),
276
+ settings: list(flags.settings)
277
+ });
278
+ break;
279
+ case "add-voice":
280
+ registry.addVoice({
281
+ id,
282
+ models: list(flags.models),
283
+ language: flags.language,
284
+ gender: flags.gender
285
+ });
286
+ break;
287
+ case "deprecate-provider":
288
+ case "deprecate-model":
289
+ case "deprecate-voice":
290
+ printImpact(registry.deprecate(command.split("-")[1], id, str(flags.replacement, "replacement"), str(flags["retire-on"], "retire-on")).lines);
291
+ break;
292
+ case "retire-provider":
293
+ case "retire-model":
294
+ case "retire-voice": {
295
+ const voiceMap = Object.fromEntries(list(flags["voice-map"]).map((p) => p.split("=")));
296
+ if (flags.yes !== true) {
297
+ const entry = registry.show(id);
298
+ out(`this rewrites every compatible config to ${entry.replacement}; run again with --yes to retire ${id}`);
299
+ printImpact(registry.impact(id, (_a = entry.replacement) !== null && _a !== void 0 ? _a : "", voiceMap).lines);
300
+ return 1;
301
+ }
302
+ printImpact(registry.retire(command.split("-")[1], id, voiceMap).lines);
303
+ break;
304
+ }
305
+ case "usage": {
306
+ const usages = registry.usage(id);
307
+ if (!usages.length)
308
+ out("no configs use it");
309
+ if (!sessions) {
310
+ for (const u of usages)
311
+ out(`${u.workspace}\t${u.config.kind}\t${u.config.name}`);
312
+ break;
313
+ }
314
+ // With the database: per workspace, its sessions that resolved the id in the last 30
315
+ // days (from the gateways' registry.used events, LO-B11), then its configs.
316
+ const workspaces = [
317
+ ...new Set([
318
+ ...usages.map((u) => u.workspace),
319
+ ...Object.keys(sessions)
320
+ ])
321
+ ].sort();
322
+ for (const w of workspaces) {
323
+ out(`${w}\tsessions (30 days)\t${(_b = sessions[w]) !== null && _b !== void 0 ? _b : 0}`);
324
+ for (const u of usages.filter((x) => x.workspace === w))
325
+ out(`${w}\t${u.config.kind}\t${u.config.name}`);
326
+ }
327
+ break;
328
+ }
329
+ case "list":
330
+ for (const e of registry.list(positional[0]))
331
+ out(`${e.kind}\t${e.id}\t${e.state}`);
332
+ break;
333
+ case "show":
334
+ out(JSON.stringify(registry.show(id), null, 2));
335
+ break;
336
+ case "history":
337
+ // Newest first (spec "Every write is audited").
338
+ for (const h of [...registry.history(id)].reverse())
339
+ out(`${h.at}\t${h.actor}\t${h.action}\t${h.target}`);
340
+ break;
341
+ case "export":
342
+ out(registry.export().trimEnd());
343
+ break;
344
+ case "plan":
345
+ case "apply": {
346
+ const file = fs.readFileSync(str(flags.f, "f"), "utf8");
347
+ const lines = command === "plan" ? registry.plan(file) : registry.apply(file);
348
+ out(lines.length ? lines.join("\n") : "no changes");
349
+ break;
350
+ }
351
+ default:
352
+ out(HELP);
353
+ return command === "help" ? 0 : 1;
354
+ }
355
+ return 0;
356
+ }
357
+ catch (e) {
358
+ if (e instanceof Registry_1.RegistryError) {
359
+ out(`error: ${e.message}`);
360
+ return 1;
361
+ }
362
+ throw e;
363
+ }
364
+ }
365
+ /**
366
+ * Runs one command with telemetry (add-platform 2.5): a `registry.command` span and a
367
+ * `fonoster.registry.commands` count by command and outcome, flushed before returning. Off unless
368
+ * an OTLP endpoint is set.
369
+ */
370
+ function runTraced(argv_1) {
371
+ return __awaiter(this, arguments, void 0, function* (argv, env = process.env) {
372
+ var _a;
373
+ const telemetry = (0, telemetry_1.initTelemetry)("fonoster-registry", undefined, env);
374
+ const command = (_a = argv[0]) !== null && _a !== void 0 ? _a : "help";
375
+ const store = (0, exports.usesDatabase)(argv) ? "database" : "file";
376
+ const span = telemetry.startSpan("registry.command", {
377
+ "fonoster.registry.command": command,
378
+ "fonoster.registry.store": store
379
+ });
380
+ let code = 1;
381
+ let error;
382
+ try {
383
+ code = (0, exports.usesDatabase)(argv) ? yield runWithDatabase(argv) : run(argv);
384
+ return code;
385
+ }
386
+ catch (e) {
387
+ error = e.message;
388
+ throw e;
389
+ }
390
+ finally {
391
+ telemetry.count("fonoster.registry.commands", 1, {
392
+ command,
393
+ outcome: code === 0 ? "ok" : "error"
394
+ });
395
+ span.end(undefined, error);
396
+ yield telemetry.shutdown();
397
+ }
398
+ });
399
+ }
400
+ /** Sessions per workspace that resolved an id in the last 30 days (registry_usage). */
401
+ function sessionCounts(prisma_3, id_1) {
402
+ return __awaiter(this, arguments, void 0, function* (prisma, id, now = new Date()) {
403
+ if (!prisma.registryUsage)
404
+ return undefined;
405
+ const since = new Date(now.getTime() - 30 * 24 * 3600 * 1000);
406
+ const rows = yield prisma.registryUsage.groupBy({
407
+ by: ["accessKeyId"],
408
+ where: {
409
+ catalogId: id,
410
+ day: { gte: new Date(since.toISOString().slice(0, 10)) }
411
+ },
412
+ _sum: { sessions: true }
413
+ });
414
+ return Object.fromEntries(rows.map((r) => { var _a; return [r.accessKeyId, (_a = r._sum.sessions) !== null && _a !== void 0 ? _a : 0]; }));
415
+ });
416
+ }
417
+ // Run when executed as the fonoster-registry binary.
418
+ // eslint-disable-next-line no-undef
419
+ if (require.main === module) {
420
+ runTraced(process.argv.slice(2)).then((code) => process.exit(code), (e) => {
421
+ console.error(`error: ${e.message}`);
422
+ process.exit(1);
423
+ });
424
+ }
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.audioFiltersSchema = void 0;
4
1
  /**
5
2
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
6
3
  * http://github.com/fonoster/fonoster
@@ -19,14 +16,16 @@ exports.audioFiltersSchema = void 0;
19
16
  * See the License for the specific language governing permissions and
20
17
  * limitations under the License.
21
18
  */
22
- const zod_1 = require("zod");
23
- // Audio filters run in the Media Server. Options carry tuning values only;
24
- // credentials live in the Media Server's environment, never in this config,
25
- // so assistant files stay safe to commit and share.
26
- const audioFiltersSchema = zod_1.z
27
- .array(zod_1.z.object({
28
- name: zod_1.z.string().min(1, { message: "An audio filter name is required" }),
29
- options: zod_1.z.record(zod_1.z.unknown()).optional()
30
- }))
31
- .default([]);
32
- exports.audioFiltersSchema = audioFiltersSchema;
19
+ import { Capability } from "./types";
20
+ /**
21
+ * The feature vocabulary (add-provider-registry, P8). Hard features are needed for a config to
22
+ * work at all; soft ones only degrade it (and their settings are dropped on retirement).
23
+ */
24
+ export declare const FEATURES: Record<Capability, Record<string, "hard" | "soft">>;
25
+ export declare function unknownFeatures(type: Capability, features: string[]): string[];
26
+ /** Hard requirements derived from an assistant config: tools need tool calling, replies stream. */
27
+ export declare function assistantRequirements(assistant: {
28
+ languageModel?: {
29
+ tools?: unknown[];
30
+ };
31
+ }): string[];
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FEATURES = void 0;
4
+ exports.unknownFeatures = unknownFeatures;
5
+ exports.assistantRequirements = assistantRequirements;
6
+ /**
7
+ * The feature vocabulary (add-provider-registry, P8). Hard features are needed for a config to
8
+ * work at all; soft ones only degrade it (and their settings are dropped on retirement).
9
+ */
10
+ exports.FEATURES = {
11
+ llm: {
12
+ "tool-calling": "hard",
13
+ streaming: "hard",
14
+ "json-mode": "soft",
15
+ vision: "soft"
16
+ },
17
+ tts: { streaming: "hard", "word-timings": "soft", ssml: "soft" },
18
+ stt: {
19
+ streaming: "hard",
20
+ "interim-results": "soft",
21
+ "word-timings": "soft",
22
+ "turn-detection": "soft",
23
+ multilingual: "soft"
24
+ }
25
+ };
26
+ function unknownFeatures(type, features) {
27
+ return features.filter((f) => !(f in exports.FEATURES[type]));
28
+ }
29
+ /** Hard requirements derived from an assistant config: tools need tool calling, replies stream. */
30
+ function assistantRequirements(assistant) {
31
+ var _a, _b;
32
+ const req = ["streaming"];
33
+ if (((_b = (_a = assistant.languageModel) === null || _a === void 0 ? void 0 : _a.tools) !== null && _b !== void 0 ? _b : []).length)
34
+ req.push("tool-calling");
35
+ return req;
36
+ }
@@ -16,11 +16,9 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- declare enum LanguageModelProvider {
20
- OPENAI = "openai",
21
- GROQ = "groq",
22
- OLLAMA = "ollama",
23
- GOOGLE = "google",
24
- ANTHROPIC = "anthropic"
25
- }
26
- export { LanguageModelProvider };
19
+ export * from "./features";
20
+ export * from "./Registry";
21
+ export * from "./stores";
22
+ export * from "./types";
23
+ export * from "./prisma";
24
+ export * from "./workspaceKeys";
@@ -0,0 +1,40 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
19
+ * http://github.com/fonoster/fonoster
20
+ *
21
+ * This file is part of Fonoster
22
+ *
23
+ * Licensed under the MIT License (the "License");
24
+ * you may not use this file except in compliance with
25
+ * the License. You may obtain a copy of the License at
26
+ *
27
+ * https://opensource.org/licenses/MIT
28
+ *
29
+ * Unless required by applicable law or agreed to in writing, software
30
+ * distributed under the License is distributed on an "AS IS" BASIS,
31
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32
+ * See the License for the specific language governing permissions and
33
+ * limitations under the License.
34
+ */
35
+ __exportStar(require("./features"), exports);
36
+ __exportStar(require("./Registry"), exports);
37
+ __exportStar(require("./stores"), exports);
38
+ __exportStar(require("./types"), exports);
39
+ __exportStar(require("./prisma"), exports);
40
+ __exportStar(require("./workspaceKeys"), exports);
@@ -0,0 +1,67 @@
1
+ import { MemoryRegistryStore } from "./stores";
2
+ import { AuditEntry, CatalogEntry, Usage, UsageSource } from "./types";
3
+ export type PrismaLike = any;
4
+ export declare class PrismaRegistryStore extends MemoryRegistryStore {
5
+ private readonly prisma;
6
+ private readonly writes;
7
+ private constructor();
8
+ /** Opens the store with the current catalog and audit log loaded. */
9
+ static open(prisma: PrismaLike): Promise<PrismaRegistryStore>;
10
+ /** Reloads the catalog (and audit log) from the database, after pending writes. */
11
+ refresh(): Promise<void>;
12
+ put(entry: CatalogEntry): void;
13
+ audit(entry: AuditEntry): void;
14
+ /** Waits for every queued write; throws the first failure. */
15
+ flush(): Promise<void>;
16
+ }
17
+ export type RewriteConflict = {
18
+ usage: Usage;
19
+ reason: string;
20
+ };
21
+ export type PrismaUsageSourceOptions = {
22
+ /** Receives every notice after it is stored (e.g. to send provider.deprecated webhooks). */
23
+ onNotice?: (workspace: string, notice: Record<string, unknown>) => void;
24
+ };
25
+ /**
26
+ * Usages of catalog ids in apps (speech defaults: voice, TTS and STT models) and assistants (the
27
+ * Autopilot assistant in an app's Intelligence config: `languageModel.model`, and
28
+ * `conversationSettings.voice`). Legacy rows with raw vendor names are not usages until migrated
29
+ * to catalog ids (task 5.1).
30
+ */
31
+ export declare class PrismaUsageSource implements UsageSource {
32
+ private readonly prisma;
33
+ private readonly options;
34
+ private rows;
35
+ private readonly origins;
36
+ private readonly writes;
37
+ /** Rewrites skipped because the config changed after it was read. */
38
+ readonly conflicts: RewriteConflict[];
39
+ private constructor();
40
+ static open(prisma: PrismaLike, options?: PrismaUsageSourceOptions): Promise<PrismaUsageSource>;
41
+ refresh(): Promise<void>;
42
+ find(id: string): Usage[];
43
+ /**
44
+ * Queues the rewrite. The write only applies if the field still holds the value read; otherwise
45
+ * it is skipped, audited by the caller's flush report (`conflicts`), and the config keeps its id.
46
+ */
47
+ rewrite(usage: Usage, change: {
48
+ model?: string;
49
+ voice?: string;
50
+ dropSettings: string[];
51
+ }): boolean;
52
+ notify(workspace: string, notice: Record<string, unknown>): void;
53
+ /** Waits for every queued write; throws the first failure. */
54
+ flush(): Promise<void>;
55
+ }
56
+ export type PrismaClientOptions = {
57
+ url: string;
58
+ /** Encrypts `/// @encrypted` columns (the apiserver's CLOAK key); needed to read app secrets. */
59
+ encryptionKey?: string;
60
+ /** Module of a client generated from the apiserver schema (default `@prisma/client`). */
61
+ clientModule?: string;
62
+ };
63
+ /**
64
+ * Opens a Prisma client generated from the apiserver's schema, with field encryption when a key
65
+ * is given. Loaded at run time so `@fonoster/common` does not depend on a generated client.
66
+ */
67
+ export declare function openPrismaClient(options: PrismaClientOptions): PrismaLike;