@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,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sessionAssistantSchema = exports.sessionConversationSettingsSchema = exports.sessionLanguageModelSchema = void 0;
4
+ /**
5
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/fonoster
7
+ *
8
+ * This file is part of Fonoster
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const zod_1 = require("zod");
23
+ const tools_1 = require("./tools");
24
+ // The assistant config for the rebuilt Autopilot (rework-autopilot A8, A11, A12; tasks 5.1, 5.2,
25
+ // 5.4). Strict: vendor credentials, vendor enums, VAD settings and the assistant webhook are
26
+ // rejected with messages pointing to where they moved. It is the only assistant schema: the legacy
27
+ // one was deleted after the cutover job converts stored assistants (rework-autopilot 8.2).
28
+ const removed = (key, message) => zod_1.z
29
+ .any()
30
+ .optional()
31
+ .superRefine((value, ctx) => {
32
+ if (value !== undefined)
33
+ ctx.addIssue({
34
+ code: zod_1.z.ZodIssueCode.custom,
35
+ message: `${key}: ${message}`
36
+ });
37
+ });
38
+ exports.sessionLanguageModelSchema = zod_1.z
39
+ .object({
40
+ /** A catalog id such as "openai/gpt-4o-mini". */
41
+ model: zod_1.z
42
+ .string()
43
+ .regex(/^[a-z0-9-]+\/.+/, "model must be a catalog id like openai/gpt-4o-mini"),
44
+ temperature: zod_1.z.number().min(0).max(2).default(0.4),
45
+ maxTokens: zod_1.z.number().int().positive().default(400),
46
+ tools: zod_1.z.array(tools_1.toolSchema).default([]),
47
+ /** Documents for the knowledge port (same shape as before; wired by the operator). */
48
+ knowledgeBase: zod_1.z
49
+ .array(zod_1.z.object({
50
+ type: zod_1.z.enum(["s3"]),
51
+ title: zod_1.z.string(),
52
+ document: zod_1.z.string()
53
+ }))
54
+ .default([]),
55
+ provider: removed("provider", "use a catalog id in model (for example openai/gpt-4o-mini)"),
56
+ apiKey: removed("apiKey", "credentials come from the provider registry or workspace secrets"),
57
+ baseUrl: removed("baseUrl", "endpoints come from the provider registry")
58
+ })
59
+ .strict();
60
+ exports.sessionConversationSettingsSchema = zod_1.z
61
+ .object({
62
+ firstMessage: zod_1.z.string().optional(),
63
+ systemPrompt: zod_1.z.string(),
64
+ goodbyeMessage: zod_1.z.string(),
65
+ systemErrorMessage: zod_1.z.string(),
66
+ initialDtmf: zod_1.z
67
+ .string()
68
+ .regex(/^[0-9*#]+$/)
69
+ .optional(),
70
+ maxSessionDuration: zod_1.z
71
+ .number()
72
+ .int()
73
+ .positive()
74
+ .default(30 * 60 * 1000),
75
+ transferOptions: zod_1.z
76
+ .object({ phoneNumber: zod_1.z.string(), message: zod_1.z.string() })
77
+ .optional(),
78
+ idleOptions: zod_1.z
79
+ .object({
80
+ message: zod_1.z.string(),
81
+ timeout: zod_1.z.number().int().positive().default(30000),
82
+ maxTimeoutCount: zod_1.z.number().int().positive().default(2)
83
+ })
84
+ .optional(),
85
+ allowUserBargeIn: zod_1.z.boolean().default(true),
86
+ /**
87
+ * The turn-taking patience knob (TT-B5): sent to the gateway with listen.update so its Pacing
88
+ * policy waits less (low) or more (high) before turn.ended. Autopilot prepares a reply on
89
+ * turn.likely_ended at low and normal, and only on turn.ended at high.
90
+ */
91
+ patience: zod_1.z.enum(["low", "normal", "high"]).default("normal"),
92
+ /** Words to bias speech recognition toward (names, brands), sent with listen.update. */
93
+ hints: zod_1.z.array(zod_1.z.string().min(1)).max(100).optional(),
94
+ /** Played with play() while thinking; the reply preempts it (A9). */
95
+ filler: zod_1.z.object({ url: zod_1.z.string().url() }).optional(),
96
+ /** Catalog ids overriding the app's speech defaults on every speak. */
97
+ voice: zod_1.z.string().optional(),
98
+ language: zod_1.z.string().optional(),
99
+ vad: removed("vad", "turn detection moved to the gateway (turn-taking configuration)"),
100
+ maxSpeechWaitTimeout: removed("maxSpeechWaitTimeout", "turn detection moved to the gateway")
101
+ })
102
+ .strict();
103
+ exports.sessionAssistantSchema = zod_1.z
104
+ .object({
105
+ conversationSettings: exports.sessionConversationSettingsSchema,
106
+ languageModel: exports.sessionLanguageModelSchema,
107
+ /** Conversation eval cases, kept as they were (evaluated outside calls). */
108
+ testCases: zod_1.z.any().optional(),
109
+ eventsHook: removed("eventsHook", "assistant webhooks were retired; use workspace webhooks (conversation.ended → call.ended or call_record.sealed)"),
110
+ audioFilters: removed("audioFilters", "audio filters are part of the app's configuration")
111
+ })
112
+ .strict();
@@ -24,13 +24,13 @@ declare const testCasesSchema: z.ZodObject<{
24
24
  model: z.ZodString;
25
25
  apiKey: z.ZodOptional<z.ZodString>;
26
26
  }, "strip", z.ZodTypeAny, {
27
+ model?: string;
27
28
  provider?: "openai";
28
29
  apiKey?: string;
29
- model?: string;
30
30
  }, {
31
+ model?: string;
31
32
  provider?: "openai";
32
33
  apiKey?: string;
33
- model?: string;
34
34
  }>;
35
35
  evalsSystemPrompt: z.ZodOptional<z.ZodString>;
36
36
  scenarios: z.ZodArray<z.ZodObject<{
@@ -166,9 +166,9 @@ declare const testCasesSchema: z.ZodObject<{
166
166
  }>, "many">;
167
167
  }, "strip", z.ZodTypeAny, {
168
168
  evalsLanguageModel?: {
169
+ model?: string;
169
170
  provider?: "openai";
170
171
  apiKey?: string;
171
- model?: string;
172
172
  };
173
173
  evalsSystemPrompt?: string;
174
174
  scenarios?: {
@@ -196,9 +196,9 @@ declare const testCasesSchema: z.ZodObject<{
196
196
  }[];
197
197
  }, {
198
198
  evalsLanguageModel?: {
199
+ model?: string;
199
200
  provider?: "openai";
200
201
  apiKey?: string;
201
- model?: string;
202
202
  };
203
203
  evalsSystemPrompt?: string;
204
204
  scenarios?: {
@@ -16,12 +16,7 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- import { VerbRequest } from "./Verb";
20
- declare enum DialRecordDirection {
21
- IN = "IN",
22
- OUT = "OUT",
23
- BOTH = "BOTH"
24
- }
19
+ /** Progress of a dialed call, as the Calls API's trackCall reports it. */
25
20
  declare enum DialStatus {
26
21
  TRYING = "TRYING",
27
22
  CANCEL = "CANCEL",
@@ -31,11 +26,19 @@ declare enum DialStatus {
31
26
  NOANSWER = "NOANSWER",
32
27
  FAILED = "FAILED"
33
28
  }
34
- type DialOptions = {
35
- timeout?: number;
36
- recordDirection?: DialRecordDirection;
37
- };
38
- type DialRequest = VerbRequest & DialOptions & {
39
- destination: string;
29
+ /** Answering machine detection verdict (the amd classifier). */
30
+ declare enum AmdStatus {
31
+ UNSPECIFIED = "AMD_STATUS_UNSPECIFIED",
32
+ HUMAN = "HUMAN",
33
+ MACHINE = "MACHINE",
34
+ VOICEMAIL = "VOICEMAIL",
35
+ IVR = "IVR",
36
+ UNKNOWN = "UNKNOWN"
37
+ }
38
+ type Amd = {
39
+ status: AmdStatus;
40
+ confidence: number;
41
+ detector: string;
42
+ latencyMs: number;
40
43
  };
41
- export { DialOptions, DialRecordDirection, DialRequest, DialStatus };
44
+ export { Amd, AmdStatus, DialStatus };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
4
+ * http://github.com/fonoster/fonoster
5
+ *
6
+ * This file is part of Fonoster
7
+ *
8
+ * Licensed under the MIT License (the "License");
9
+ * you may not use this file except in compliance with
10
+ * the License. You may obtain a copy of the License at
11
+ *
12
+ * https://opensource.org/licenses/MIT
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+ // Call statuses shared by the Calls API, the SDK and the CLI. The verb and stream types of the
21
+ // retired voice.proto are gone (cleanbreak CB-B1).
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.DialStatus = exports.AmdStatus = void 0;
24
+ /** Progress of a dialed call, as the Calls API's trackCall reports it. */
25
+ var DialStatus;
26
+ (function (DialStatus) {
27
+ DialStatus["TRYING"] = "TRYING";
28
+ DialStatus["CANCEL"] = "CANCEL";
29
+ DialStatus["ANSWER"] = "ANSWER";
30
+ DialStatus["BUSY"] = "BUSY";
31
+ DialStatus["PROGRESS"] = "PROGRESS";
32
+ DialStatus["NOANSWER"] = "NOANSWER";
33
+ DialStatus["FAILED"] = "FAILED";
34
+ })(DialStatus || (exports.DialStatus = DialStatus = {}));
35
+ /** Answering machine detection verdict (the amd classifier). */
36
+ var AmdStatus;
37
+ (function (AmdStatus) {
38
+ AmdStatus["UNSPECIFIED"] = "AMD_STATUS_UNSPECIFIED";
39
+ AmdStatus["HUMAN"] = "HUMAN";
40
+ AmdStatus["MACHINE"] = "MACHINE";
41
+ AmdStatus["VOICEMAIL"] = "VOICEMAIL";
42
+ AmdStatus["IVR"] = "IVR";
43
+ AmdStatus["UNKNOWN"] = "UNKNOWN";
44
+ })(AmdStatus || (exports.AmdStatus = AmdStatus = {}));
@@ -0,0 +1,91 @@
1
+ /** Secret names: provider ids and credential refs (`deepgram`, `groq`, `openai`, `s3.secret`). */
2
+ export declare const SECRET_NAME: RegExp;
3
+ export type CellSecretsConfig = {
4
+ /** The encrypted file. */
5
+ file?: string;
6
+ /** A directory with one file per secret (a mounted Kubernetes Secret). */
7
+ dir?: string;
8
+ /** Environment variable with the file's passphrase (default FONOSTER_REGION_SECRETS_KEY). */
9
+ keyEnv?: string;
10
+ /** The passphrase itself (tests); prefer keyEnv. */
11
+ key?: string;
12
+ };
13
+ export declare const DEFAULT_SECRETS_KEY_ENV = "FONOSTER_REGION_SECRETS_KEY";
14
+ export interface CellSecretStore {
15
+ get(name: string): string | undefined;
16
+ /** The names that are set, sorted. */
17
+ names(): string[];
18
+ set(name: string, value: string): void;
19
+ /** Returns whether the secret existed. */
20
+ delete(name: string): boolean;
21
+ }
22
+ export declare function assertSecretName(name: string): void;
23
+ /** Writes a file atomically with mode 0600. */
24
+ export declare function writePrivateFile(file: string, data: string | Buffer): void;
25
+ /** One AES-256-GCM sealed JSON object `{name: value}` in a file. */
26
+ export declare class EncryptedFileSecretStore implements CellSecretStore {
27
+ private readonly file;
28
+ private readonly passphrase;
29
+ private cache?;
30
+ private keys;
31
+ constructor(file: string, passphrase: string);
32
+ private key;
33
+ private read;
34
+ private write;
35
+ get(name: string): string;
36
+ names(): string[];
37
+ set(name: string, value: string): void;
38
+ delete(name: string): boolean;
39
+ }
40
+ /**
41
+ * One file per secret: a mounted Kubernetes Secret (keys are file names; `..data` and other
42
+ * dot-entries are Kubernetes' own). Trailing newlines are trimmed.
43
+ */
44
+ export declare class DirectorySecretStore implements CellSecretStore {
45
+ private readonly dir;
46
+ constructor(dir: string);
47
+ get(name: string): string;
48
+ names(): string[];
49
+ set(name: string, value: string): void;
50
+ delete(name: string): boolean;
51
+ }
52
+ /** The store a config names (dir wins over file). */
53
+ export declare function openCellSecrets(config: CellSecretsConfig, env?: Record<string, string | undefined>): CellSecretStore;
54
+ /**
55
+ * The cell's secrets as a workspace key source (gateway Catalog, Autopilot): a private region
56
+ * serves one workspace, so the provider name alone picks the key. Read errors count as unset.
57
+ */
58
+ export declare class CellSecretKeys {
59
+ private readonly store;
60
+ constructor(store: CellSecretStore);
61
+ get(_workspaceRef: string, providerId: string): Promise<string>;
62
+ /** By credentialRef first, then by the providers that use that ref. */
63
+ forCredentialRef(credentialRef: string, providers?: {
64
+ id: string;
65
+ credentialRef: string;
66
+ }[]): string | undefined;
67
+ }
68
+ /** What `NatsWorkspaceKeys` needs of a NATS connection (nats.js connections fit). */
69
+ export interface KeyRequester {
70
+ request(subject: string, data: Uint8Array, options: {
71
+ timeout: number;
72
+ }): Promise<{
73
+ data: Uint8Array;
74
+ }>;
75
+ }
76
+ /** The cell-local subject the region agent answers (contract 6); it never crosses the link. */
77
+ export declare const CELL_WORKSPACE_KEYS_SUBJECT = "cell.workspacekeys.get";
78
+ /**
79
+ * A workspace's bring-your-own key in a public region (add-private-regions PR-20): asked of the
80
+ * region agent on the cell's NATS for each call, which asks the control plane over the region
81
+ * link. Never cached and never logged; undefined (the region's own key applies) when the
82
+ * workspace has none or the answer does not come within the timeout.
83
+ */
84
+ export declare class NatsWorkspaceKeys {
85
+ private readonly requester;
86
+ private readonly timeoutMs;
87
+ /** Never cached: the gateway keeps no adapter built with these keys past the call's lookup. */
88
+ readonly cacheable = false;
89
+ constructor(requester: () => Promise<KeyRequester>, timeoutMs?: number);
90
+ get(workspaceRef: string, providerId: string): Promise<string | undefined>;
91
+ }
@@ -0,0 +1,299 @@
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.NatsWorkspaceKeys = exports.CELL_WORKSPACE_KEYS_SUBJECT = exports.CellSecretKeys = exports.DirectorySecretStore = exports.EncryptedFileSecretStore = exports.DEFAULT_SECRETS_KEY_ENV = exports.SECRET_NAME = void 0;
46
+ exports.assertSecretName = assertSecretName;
47
+ exports.writePrivateFile = writePrivateFile;
48
+ exports.openCellSecrets = openCellSecrets;
49
+ /**
50
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
51
+ * http://github.com/fonoster/fonoster
52
+ *
53
+ * This file is part of Fonoster
54
+ *
55
+ * Licensed under the MIT License (the "License");
56
+ * you may not use this file except in compliance with
57
+ * the License. You may obtain a copy of the License at
58
+ *
59
+ * https://opensource.org/licenses/MIT
60
+ *
61
+ * Unless required by applicable law or agreed to in writing, software
62
+ * distributed under the License is distributed on an "AS IS" BASIS,
63
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64
+ * See the License for the specific language governing permissions and
65
+ * limitations under the License.
66
+ */
67
+ const crypto_1 = require("crypto");
68
+ const fs = __importStar(require("fs"));
69
+ const path = __importStar(require("path"));
70
+ // The secret store of a private region's cell (add-private-regions PR-4): provider keys and other
71
+ // secrets that never leave the cell. Two backends:
72
+ // - an encrypted file (Compose, bare metal): AES-256-GCM, key derived with scrypt from a
73
+ // passphrase in the environment (default FONOSTER_REGION_SECRETS_KEY);
74
+ // - a directory with one file per secret: a mounted Kubernetes Secret.
75
+ // The gateway and the hosted Autopilot read it; `fonoster-region secrets` writes it. Values are
76
+ // never logged; `names()` is what the region agent reports in heartbeats.
77
+ /** Secret names: provider ids and credential refs (`deepgram`, `groq`, `openai`, `s3.secret`). */
78
+ exports.SECRET_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
79
+ exports.DEFAULT_SECRETS_KEY_ENV = "FONOSTER_REGION_SECRETS_KEY";
80
+ function assertSecretName(name) {
81
+ if (!exports.SECRET_NAME.test(name))
82
+ throw new Error(`invalid secret name "${name}" (lowercase letters, digits, ".", "_" and "-"; up to 64)`);
83
+ }
84
+ /** Writes a file atomically with mode 0600. */
85
+ function writePrivateFile(file, data) {
86
+ fs.mkdirSync(path.dirname(file), { recursive: true });
87
+ const tmp = `${file}.${process.pid}.${(0, crypto_1.randomBytes)(4).toString("hex")}.tmp`;
88
+ fs.writeFileSync(tmp, data, { mode: 0o600 });
89
+ fs.renameSync(tmp, file);
90
+ }
91
+ /** One AES-256-GCM sealed JSON object `{name: value}` in a file. */
92
+ class EncryptedFileSecretStore {
93
+ constructor(file, passphrase) {
94
+ this.file = file;
95
+ this.passphrase = passphrase;
96
+ this.keys = new Map();
97
+ if (!passphrase)
98
+ throw new Error("the region secret store needs a key (passphrase)");
99
+ }
100
+ key(salt) {
101
+ const id = salt.toString("base64");
102
+ let k = this.keys.get(id);
103
+ if (!k) {
104
+ k = (0, crypto_1.scryptSync)(this.passphrase, salt, 32);
105
+ this.keys.set(id, k);
106
+ }
107
+ return k;
108
+ }
109
+ read() {
110
+ if (!fs.existsSync(this.file))
111
+ return {};
112
+ const mtimeMs = fs.statSync(this.file).mtimeMs;
113
+ if (this.cache && this.cache.mtimeMs === mtimeMs)
114
+ return this.cache.values;
115
+ const sealed = JSON.parse(fs.readFileSync(this.file, "utf8"));
116
+ const decipher = (0, crypto_1.createDecipheriv)("aes-256-gcm", this.key(Buffer.from(sealed.salt, "base64")), Buffer.from(sealed.iv, "base64"));
117
+ decipher.setAuthTag(Buffer.from(sealed.tag, "base64"));
118
+ let plain;
119
+ try {
120
+ plain = Buffer.concat([
121
+ decipher.update(Buffer.from(sealed.data, "base64")),
122
+ decipher.final()
123
+ ]);
124
+ }
125
+ catch (_a) {
126
+ throw new Error(`cannot decrypt ${this.file}: wrong key (check ${exports.DEFAULT_SECRETS_KEY_ENV})`);
127
+ }
128
+ const values = JSON.parse(plain.toString("utf8"));
129
+ this.cache = { mtimeMs, values };
130
+ return values;
131
+ }
132
+ write(values) {
133
+ const salt = (0, crypto_1.randomBytes)(16);
134
+ const iv = (0, crypto_1.randomBytes)(12);
135
+ const cipher = (0, crypto_1.createCipheriv)("aes-256-gcm", this.key(salt), iv);
136
+ const data = Buffer.concat([
137
+ cipher.update(Buffer.from(JSON.stringify(values), "utf8")),
138
+ cipher.final()
139
+ ]);
140
+ const sealed = {
141
+ v: 1,
142
+ salt: salt.toString("base64"),
143
+ iv: iv.toString("base64"),
144
+ tag: cipher.getAuthTag().toString("base64"),
145
+ data: data.toString("base64")
146
+ };
147
+ writePrivateFile(this.file, JSON.stringify(sealed));
148
+ this.cache = undefined;
149
+ }
150
+ get(name) {
151
+ return this.read()[name] || undefined;
152
+ }
153
+ names() {
154
+ return Object.keys(this.read()).sort();
155
+ }
156
+ set(name, value) {
157
+ assertSecretName(name);
158
+ if (!value)
159
+ throw new Error("empty secret value");
160
+ this.write(Object.assign(Object.assign({}, this.read()), { [name]: value }));
161
+ }
162
+ delete(name) {
163
+ const values = Object.assign({}, this.read());
164
+ if (!(name in values))
165
+ return false;
166
+ delete values[name];
167
+ this.write(values);
168
+ return true;
169
+ }
170
+ }
171
+ exports.EncryptedFileSecretStore = EncryptedFileSecretStore;
172
+ /**
173
+ * One file per secret: a mounted Kubernetes Secret (keys are file names; `..data` and other
174
+ * dot-entries are Kubernetes' own). Trailing newlines are trimmed.
175
+ */
176
+ class DirectorySecretStore {
177
+ constructor(dir) {
178
+ this.dir = dir;
179
+ }
180
+ get(name) {
181
+ if (!exports.SECRET_NAME.test(name))
182
+ return undefined;
183
+ const file = path.join(this.dir, name);
184
+ if (!fs.existsSync(file))
185
+ return undefined;
186
+ return fs.readFileSync(file, "utf8").replace(/\r?\n$/, "") || undefined;
187
+ }
188
+ names() {
189
+ if (!fs.existsSync(this.dir))
190
+ return [];
191
+ return fs
192
+ .readdirSync(this.dir)
193
+ .filter((n) => exports.SECRET_NAME.test(n))
194
+ .filter((n) => fs.statSync(path.join(this.dir, n)).isFile())
195
+ .sort();
196
+ }
197
+ set(name, value) {
198
+ assertSecretName(name);
199
+ if (!value)
200
+ throw new Error("empty secret value");
201
+ writePrivateFile(path.join(this.dir, name), value);
202
+ }
203
+ delete(name) {
204
+ if (!exports.SECRET_NAME.test(name))
205
+ return false;
206
+ const file = path.join(this.dir, name);
207
+ if (!fs.existsSync(file))
208
+ return false;
209
+ fs.rmSync(file);
210
+ return true;
211
+ }
212
+ }
213
+ exports.DirectorySecretStore = DirectorySecretStore;
214
+ /** The store a config names (dir wins over file). */
215
+ function openCellSecrets(config, env = process.env) {
216
+ var _a, _b, _c;
217
+ if (config.dir)
218
+ return new DirectorySecretStore(config.dir);
219
+ if (!config.file)
220
+ throw new Error("cellSecrets: set file or dir");
221
+ const key = (_a = config.key) !== null && _a !== void 0 ? _a : env[(_b = config.keyEnv) !== null && _b !== void 0 ? _b : exports.DEFAULT_SECRETS_KEY_ENV];
222
+ if (!key)
223
+ throw new Error(`cellSecrets: ${(_c = config.keyEnv) !== null && _c !== void 0 ? _c : exports.DEFAULT_SECRETS_KEY_ENV} is not set`);
224
+ return new EncryptedFileSecretStore(config.file, key);
225
+ }
226
+ /**
227
+ * The cell's secrets as a workspace key source (gateway Catalog, Autopilot): a private region
228
+ * serves one workspace, so the provider name alone picks the key. Read errors count as unset.
229
+ */
230
+ class CellSecretKeys {
231
+ constructor(store) {
232
+ this.store = store;
233
+ }
234
+ get(_workspaceRef, providerId) {
235
+ return __awaiter(this, void 0, void 0, function* () {
236
+ try {
237
+ return this.store.get(providerId);
238
+ }
239
+ catch (_a) {
240
+ return undefined;
241
+ }
242
+ });
243
+ }
244
+ /** By credentialRef first, then by the providers that use that ref. */
245
+ forCredentialRef(credentialRef, providers = []) {
246
+ try {
247
+ const direct = exports.SECRET_NAME.test(credentialRef)
248
+ ? this.store.get(credentialRef)
249
+ : undefined;
250
+ if (direct)
251
+ return direct;
252
+ for (const p of providers.filter((x) => x.credentialRef === credentialRef))
253
+ if (exports.SECRET_NAME.test(p.id)) {
254
+ const v = this.store.get(p.id);
255
+ if (v)
256
+ return v;
257
+ }
258
+ }
259
+ catch (_a) {
260
+ return undefined;
261
+ }
262
+ return undefined;
263
+ }
264
+ }
265
+ exports.CellSecretKeys = CellSecretKeys;
266
+ /** The cell-local subject the region agent answers (contract 6); it never crosses the link. */
267
+ exports.CELL_WORKSPACE_KEYS_SUBJECT = "cell.workspacekeys.get";
268
+ /**
269
+ * A workspace's bring-your-own key in a public region (add-private-regions PR-20): asked of the
270
+ * region agent on the cell's NATS for each call, which asks the control plane over the region
271
+ * link. Never cached and never logged; undefined (the region's own key applies) when the
272
+ * workspace has none or the answer does not come within the timeout.
273
+ */
274
+ class NatsWorkspaceKeys {
275
+ constructor(requester, timeoutMs = 2000) {
276
+ this.requester = requester;
277
+ this.timeoutMs = timeoutMs;
278
+ /** Never cached: the gateway keeps no adapter built with these keys past the call's lookup. */
279
+ this.cacheable = false;
280
+ }
281
+ get(workspaceRef, providerId) {
282
+ return __awaiter(this, void 0, void 0, function* () {
283
+ if (!workspaceRef || !providerId)
284
+ return undefined;
285
+ try {
286
+ const nc = yield this.requester();
287
+ const reply = yield nc.request(exports.CELL_WORKSPACE_KEYS_SUBJECT, Buffer.from(JSON.stringify({ workspaceRef, provider: providerId })), { timeout: this.timeoutMs });
288
+ const body = JSON.parse(Buffer.from(reply.data).toString("utf8"));
289
+ return typeof body.value === "string" && body.value
290
+ ? body.value
291
+ : undefined;
292
+ }
293
+ catch (_a) {
294
+ return undefined;
295
+ }
296
+ });
297
+ }
298
+ }
299
+ exports.NatsWorkspaceKeys = NatsWorkspaceKeys;
@@ -0,0 +1,6 @@
1
+ import { Io } from "./types";
2
+ export type CheckOptions = {
3
+ dir: string;
4
+ };
5
+ /** Validates config/*.yaml, placeholders resolved from .env, against config/schemas/*.json (D5). */
6
+ export declare function runCheck(options: CheckOptions, io: Io): number;