@fonoster/common 0.27.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{voice/PlayDtmf.d.ts → agents/index.d.ts} +3 -5
- package/dist/{assistants → agents}/index.js +2 -6
- package/dist/agents/sessionAgentSchema.d.ts +755 -0
- package/dist/agents/sessionAgentSchema.js +117 -0
- package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
- package/dist/calls/index.js +44 -0
- package/dist/cellsecrets/index.d.ts +91 -0
- package/dist/cellsecrets/index.js +299 -0
- package/dist/config/cli/check.d.ts +6 -0
- package/dist/config/cli/check.js +202 -0
- package/dist/config/cli/envFile.d.ts +23 -0
- package/dist/config/cli/envFile.js +81 -0
- package/dist/config/cli/exposure.d.ts +10 -0
- package/dist/config/cli/exposure.js +93 -0
- package/dist/config/cli/index.d.ts +2 -0
- package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
- package/dist/config/cli/info.d.ts +6 -0
- package/dist/config/cli/info.js +62 -0
- package/dist/config/cli/init.d.ts +9 -0
- package/dist/config/cli/init.js +152 -0
- package/dist/config/cli/main.d.ts +11 -0
- package/dist/config/cli/main.js +155 -0
- package/dist/config/cli/render.d.ts +44 -0
- package/dist/config/cli/render.js +129 -0
- package/dist/config/cli/secrets.d.ts +21 -0
- package/dist/config/cli/secrets.js +117 -0
- package/dist/config/cli/types.d.ts +24 -0
- package/dist/config/cli/types.js +7 -0
- package/dist/{assistants/index.d.ts → config/configHints.d.ts} +8 -7
- package/dist/config/configHints.js +47 -0
- package/dist/{voice → config}/index.d.ts +11 -13
- package/dist/{voice → config}/index.js +11 -13
- package/dist/config/interpolateEnv.d.ts +34 -0
- package/dist/config/interpolateEnv.js +70 -0
- package/dist/config/loadServiceConfig.d.ts +28 -0
- package/dist/config/loadServiceConfig.js +112 -0
- package/dist/config/logsSchema.d.ts +76 -0
- package/dist/config/logsSchema.js +49 -0
- package/dist/config/natsConnection.d.ts +53 -0
- package/dist/config/natsConnection.js +58 -0
- package/dist/config/parseConfigFile.d.ts +7 -0
- package/dist/config/parseConfigFile.js +60 -0
- package/dist/config/resolveConfigFile.d.ts +17 -0
- package/dist/config/resolveConfigFile.js +54 -0
- package/dist/config/resolveSecretRefs.d.ts +35 -0
- package/dist/config/resolveSecretRefs.js +129 -0
- package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
- package/dist/config/strictDeep.js +58 -0
- package/dist/config/telemetrySchema.d.ts +39 -0
- package/dist/config/telemetrySchema.js +44 -0
- package/dist/config/toJsonSchema.d.ts +30 -0
- package/dist/config/toJsonSchema.js +161 -0
- package/dist/constants.d.ts +1 -6
- package/dist/constants.js +4 -7
- package/dist/envs.d.ts +2 -1
- package/dist/envs.js +13 -11
- package/dist/errors/handleZodError.js +3 -1
- package/dist/filters/index.d.ts +70 -0
- package/dist/filters/index.js +101 -0
- package/dist/identity/createAuthInterceptor.d.ts +9 -2
- package/dist/identity/createAuthInterceptor.js +36 -30
- package/dist/identity/hasAccess.d.ts +9 -3
- package/dist/identity/hasAccess.js +32 -8
- package/dist/identity/index.d.ts +1 -0
- package/dist/identity/index.js +1 -0
- package/dist/identity/isValidToken.d.ts +12 -2
- package/dist/identity/isValidToken.js +25 -9
- package/dist/identity/roles.js +33 -7
- package/dist/identity/types.d.ts +6 -1
- package/dist/identity/types.js +1 -0
- package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
- package/dist/identity/verifyAccessToken.js +44 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +18 -3
- package/dist/net/assertPublicDestination.d.ts +52 -0
- package/dist/net/assertPublicDestination.js +342 -0
- package/dist/{voice/StopSay.d.ts → net/index.d.ts} +1 -6
- package/dist/net/index.js +35 -0
- package/dist/notifications/createSendEmail.js +3 -1
- package/dist/protos/applications.proto +138 -3
- package/dist/protos/authz.proto +26 -4
- package/dist/protos/billing.proto +92 -0
- package/dist/protos/calls.proto +4 -0
- package/dist/protos/capabilities.proto +102 -0
- package/dist/protos/identity.proto +26 -1
- package/dist/protos/providers.proto +139 -0
- package/dist/protos/roomr.proto +146 -0
- package/dist/protos/session.proto +194 -0
- package/dist/protos/usage.proto +59 -0
- package/dist/protos/webhooks.proto +140 -0
- package/dist/{utils/types.d.ts → regions/hub/index.d.ts} +1 -5
- package/dist/regions/hub/index.js +35 -0
- package/dist/regions/hub/natsJwt.d.ts +63 -0
- package/dist/regions/hub/natsJwt.js +195 -0
- package/dist/registry/Registry.d.ts +87 -0
- package/dist/registry/Registry.js +432 -0
- package/dist/registry/cli.d.ts +20 -0
- package/dist/registry/cli.js +436 -0
- package/dist/registry/features.d.ts +31 -0
- package/dist/registry/features.js +38 -0
- package/dist/registry/index.d.ts +24 -0
- package/dist/registry/index.js +40 -0
- package/dist/registry/prisma.d.ts +67 -0
- package/dist/registry/prisma.js +399 -0
- package/dist/registry/stores.d.ts +21 -0
- package/dist/{assistants/languageModelConfigSchema.js → registry/stores.js} +55 -38
- package/dist/registry/types.d.ts +110 -0
- package/dist/{assistants/LanguageModelProvider.d.ts → registry/types.js} +6 -8
- package/dist/registry/workspaceKeys.d.ts +41 -0
- package/dist/registry/workspaceKeys.js +64 -0
- package/dist/roomr/health.d.ts +9 -0
- package/dist/{assistants/eventsHookSchema.js → roomr/health.js} +22 -18
- package/dist/roomr/index.d.ts +75 -0
- package/dist/roomr/index.js +146 -0
- package/dist/roomr/session.d.ts +133 -0
- package/dist/roomr/session.js +132 -0
- package/dist/session/index.d.ts +19 -0
- package/dist/session/index.js +35 -0
- package/dist/session/schema.d.ts +2140 -0
- package/dist/session/schema.js +448 -0
- package/dist/sessions/index.d.ts +96 -0
- package/dist/sessions/index.js +197 -0
- package/dist/telemetry.d.ts +76 -0
- package/dist/telemetry.js +360 -0
- package/dist/types.d.ts +3 -5
- package/dist/{voice/Gather.d.ts → usage/CallUsage.d.ts} +19 -18
- package/dist/usage/CallUsage.js +76 -0
- package/dist/usage/index.d.ts +22 -0
- package/dist/usage/index.js +38 -0
- package/dist/usage/meters.d.ts +113 -0
- package/dist/usage/meters.js +90 -0
- package/dist/usage/types.d.ts +159 -0
- package/dist/usage/types.js +84 -0
- package/dist/usage/usageId.d.ts +6 -0
- package/dist/usage/usageId.js +45 -0
- package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
- package/dist/utils/WriteBehind.js +64 -0
- package/dist/utils/index.d.ts +1 -3
- package/dist/utils/index.js +1 -3
- package/dist/utils/{createFetchSingleCallByCallId.d.ts → logUnhandledRejections.d.ts} +8 -4
- package/dist/{assistants/audioFiltersSchema.js → utils/logUnhandledRejections.js} +17 -12
- package/dist/utils/withErrorHandling.js +1 -1
- package/dist/validators/calls.d.ts +3 -0
- package/dist/validators/calls.js +12 -2
- package/dist/validators/identity.d.ts +37 -8
- package/dist/validators/identity.js +22 -3
- package/dist/validators/index.d.ts +1 -0
- package/dist/validators/index.js +1 -0
- package/dist/validators/sip.d.ts +40 -0
- package/dist/validators/sip.js +99 -0
- package/dist/validators/sipnet/credentials.d.ts +2 -2
- package/dist/validators/sipnet/domains.js +7 -7
- package/dist/validators/sipnet/trunks.d.ts +14 -14
- package/dist/validators/sipnet/trunks.js +4 -2
- package/dist/webhooks/Dispatcher.d.ts +87 -0
- package/dist/webhooks/Dispatcher.js +225 -0
- package/dist/webhooks/bus.d.ts +68 -0
- package/dist/webhooks/bus.js +178 -0
- package/dist/webhooks/events.d.ts +66 -0
- package/dist/webhooks/events.js +62 -0
- package/dist/webhooks/index.d.ts +23 -0
- package/dist/webhooks/index.js +39 -0
- package/dist/webhooks/prisma.d.ts +55 -0
- package/dist/webhooks/prisma.js +242 -0
- package/dist/webhooks/signing.d.ts +8 -0
- package/dist/webhooks/signing.js +53 -0
- package/package.json +25 -11
- package/schema/session.v1.json +871 -0
- package/dist/assistants/assistantSchema.d.ts +0 -739
- package/dist/assistants/assistantSchema.js +0 -37
- package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
- package/dist/assistants/conversationSettingsSchema.js +0 -133
- package/dist/assistants/eventsHookSchema.d.ts +0 -38
- package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
- package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
- package/dist/protos/voice.proto +0 -557
- package/dist/utils/createFetchSingleCallByCallId.js +0 -33
- package/dist/utils/findIntegrationsCredentials.d.ts +0 -23
- package/dist/utils/types.js +0 -2
- package/dist/voice/Dial.js +0 -20
- package/dist/voice/Gather.js +0 -9
- package/dist/voice/Mute.d.ts +0 -31
- package/dist/voice/Mute.js +0 -9
- package/dist/voice/Play.d.ts +0 -30
- package/dist/voice/Play.js +0 -2
- package/dist/voice/PlayDtmf.js +0 -2
- package/dist/voice/PlaybackControl.d.ts +0 -31
- package/dist/voice/PlaybackControl.js +0 -11
- package/dist/voice/Record.d.ts +0 -36
- package/dist/voice/Record.js +0 -7
- package/dist/voice/Say.d.ts +0 -28
- package/dist/voice/Say.js +0 -2
- package/dist/voice/StopSay.js +0 -2
- package/dist/voice/Stream.d.ts +0 -53
- package/dist/voice/Stream.js +0 -19
- package/dist/voice/StreamGather.d.ts +0 -34
- package/dist/voice/StreamGather.js +0 -9
- package/dist/voice/Verb.d.ts +0 -27
- package/dist/voice/Verb.js +0 -2
- package/dist/voice/voice.d.ts +0 -171
- package/dist/voice/voice.js +0 -65
- package/dist/{assistants → agents}/testCasesSchema.d.ts +4 -4
- /package/dist/{assistants → agents}/testCasesSchema.js +0 -0
- /package/dist/{assistants → agents}/tools/index.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/index.js +0 -0
- /package/dist/{assistants → agents}/tools/propertySchema.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/propertySchema.js +0 -0
- /package/dist/{assistants → agents}/tools/toolSchema.d.ts +0 -0
- /package/dist/{assistants → agents}/tools/toolSchema.js +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionAgentSchema = 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 agent 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 agent webhook are
|
|
26
|
+
// rejected with messages pointing to where they moved. It is the only agent schema: the legacy
|
|
27
|
+
// one was deleted after the cutover job converts stored agents (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({
|
|
77
|
+
phoneNumber: zod_1.z.string(),
|
|
78
|
+
message: zod_1.z.string(),
|
|
79
|
+
/** Said when the transfer fails, before the conversation goes on (default in Autopilot). */
|
|
80
|
+
failedMessage: zod_1.z.string().optional()
|
|
81
|
+
})
|
|
82
|
+
.optional(),
|
|
83
|
+
idleOptions: zod_1.z
|
|
84
|
+
.object({
|
|
85
|
+
message: zod_1.z.string(),
|
|
86
|
+
timeout: zod_1.z.number().int().positive().default(30000),
|
|
87
|
+
maxTimeoutCount: zod_1.z.number().int().positive().default(2)
|
|
88
|
+
})
|
|
89
|
+
.optional(),
|
|
90
|
+
allowUserBargeIn: zod_1.z.boolean().default(true),
|
|
91
|
+
/**
|
|
92
|
+
* The turn-taking patience knob (TT-B5): sent to AppBridge with listen.update so its Pacing
|
|
93
|
+
* policy waits less (low) or more (high) before turn.ended. Autopilot prepares a reply on
|
|
94
|
+
* turn.likely_ended at low and normal, and only on turn.ended at high.
|
|
95
|
+
*/
|
|
96
|
+
patience: zod_1.z.enum(["low", "normal", "high"]).default("normal"),
|
|
97
|
+
/** Words to bias speech recognition toward (names, brands), sent with listen.update. */
|
|
98
|
+
hints: zod_1.z.array(zod_1.z.string().min(1)).max(100).optional(),
|
|
99
|
+
/** Played with play() while thinking; the reply preempts it (A9). */
|
|
100
|
+
filler: zod_1.z.object({ url: zod_1.z.string().url() }).optional(),
|
|
101
|
+
/** Catalog ids overriding the app's speech defaults on every speak. */
|
|
102
|
+
voice: zod_1.z.string().optional(),
|
|
103
|
+
language: zod_1.z.string().optional(),
|
|
104
|
+
vad: removed("vad", "turn detection moved to AppBridge (turn-taking configuration)"),
|
|
105
|
+
maxSpeechWaitTimeout: removed("maxSpeechWaitTimeout", "turn detection moved to AppBridge")
|
|
106
|
+
})
|
|
107
|
+
.strict();
|
|
108
|
+
exports.sessionAgentSchema = zod_1.z
|
|
109
|
+
.object({
|
|
110
|
+
conversationSettings: exports.sessionConversationSettingsSchema,
|
|
111
|
+
languageModel: exports.sessionLanguageModelSchema,
|
|
112
|
+
/** Conversation eval cases, kept as they were (evaluated outside calls). */
|
|
113
|
+
testCases: zod_1.z.any().optional(),
|
|
114
|
+
eventsHook: removed("eventsHook", "agent webhooks were retired; use workspace webhooks (conversation.ended → call.ended or call_record.sealed)"),
|
|
115
|
+
audioFilters: removed("audioFilters", "audio filters are part of the app's configuration")
|
|
116
|
+
})
|
|
117
|
+
.strict();
|
|
@@ -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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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 {
|
|
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 (appbridge 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: AppBridge 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
|
+
// AppBridge 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 (appbridge 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: AppBridge 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;
|