@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.
- package/dist/assistants/index.d.ts +2 -6
- package/dist/assistants/index.js +2 -6
- package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
- package/dist/assistants/sessionAssistantSchema.js +112 -0
- package/dist/assistants/testCasesSchema.d.ts +4 -4
- 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/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
- package/dist/config/cli/types.js +7 -0
- package/dist/config/configHints.d.ts +39 -0
- package/dist/config/configHints.js +61 -0
- package/dist/{voice → config}/index.d.ts +12 -13
- package/dist/config/index.js +46 -0
- package/dist/config/integrationsSchema.d.ts +32 -0
- package/dist/config/integrationsSchema.js +56 -0
- package/dist/config/interpolateEnv.d.ts +34 -0
- package/dist/config/interpolateEnv.js +70 -0
- package/dist/config/loadServiceConfig.d.ts +33 -0
- package/dist/config/loadServiceConfig.js +121 -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 -4
- package/dist/constants.js +4 -5
- package/dist/envs.d.ts +2 -1
- package/dist/envs.js +13 -11
- 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 +22 -6
- 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 +11 -1
- package/dist/index.js +14 -2
- package/dist/net/assertPublicDestination.d.ts +52 -0
- package/dist/net/assertPublicDestination.js +342 -0
- package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
- package/dist/{voice → net}/index.js +1 -13
- package/dist/notifications/createSendEmail.js +3 -1
- package/dist/protos/applications.proto +124 -3
- package/dist/protos/authz.proto +23 -4
- package/dist/protos/capabilities.proto +94 -0
- package/dist/protos/identity.proto +26 -1
- package/dist/protos/providers.proto +135 -0
- package/dist/protos/webhooks.proto +140 -0
- package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
- 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 +79 -0
- package/dist/registry/Registry.js +418 -0
- package/dist/registry/cli.d.ts +20 -0
- package/dist/registry/cli.js +424 -0
- package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
- package/dist/registry/features.js +36 -0
- package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
- package/dist/registry/index.js +40 -0
- package/dist/registry/prisma.d.ts +67 -0
- package/dist/registry/prisma.js +391 -0
- package/dist/registry/stores.d.ts +21 -0
- package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
- package/dist/registry/types.d.ts +98 -0
- package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
- package/dist/registry/workspaceKeys.d.ts +41 -0
- package/dist/registry/workspaceKeys.js +64 -0
- package/dist/session/index.d.ts +19 -0
- package/dist/session/index.js +35 -0
- package/dist/session/schema.d.ts +1951 -0
- package/dist/session/schema.js +397 -0
- package/dist/sessions/index.d.ts +93 -0
- package/dist/sessions/index.js +194 -0
- package/dist/telemetry.d.ts +76 -0
- package/dist/telemetry.js +360 -0
- package/dist/types.d.ts +3 -5
- package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
- package/dist/utils/WriteBehind.js +64 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
- package/dist/utils/logUnhandledRejections.js +37 -0
- package/dist/validators/calls.d.ts +2 -2
- package/dist/validators/calls.js +5 -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 +49 -0
- package/dist/webhooks/bus.js +170 -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 -12
- package/schema/session.v1.json +791 -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/assistants/languageModelConfigSchema.js +0 -91
- package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
- package/dist/protos/voice.proto +0 -557
- package/dist/voice/Dial.js +0 -20
- package/dist/voice/Gather.d.ts +0 -37
- 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.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.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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Io } from "./types";
|
|
2
|
+
export declare const HELP = "Usage: fonoster-config <command> [options]\n\nSets up Fonoster from its Docker Compose bundle. Run it in the unpacked fonoster folder.\n\nCommands:\n init Generate secrets and keys, detect the host address, write .env and config/*.yaml.\n Secrets stay in .env; config/*.yaml reference them as ${NAME}. Running it again\n keeps every existing secret and adds only missing ones.\n info Print the dashboard URL, the owner login, the SIP address and where files live.\n check Validate config/*.yaml with .env's values (unknown keys, missing values, unset\n ${NAME} placeholders, secret:// references).\n\nOptions:\n --dir <folder> The bundle folder (default: the current folder)\n --host-ip <address> init: the address softphones and browsers reach this machine at\n -h, --help Show this help\n\nThen start it with: docker compose up -d\nGuide: https://docs.fonoster.com/self-hosting/install-compose\n";
|
|
3
|
+
type Parsed = {
|
|
4
|
+
command?: string;
|
|
5
|
+
flags: Record<string, string | true>;
|
|
6
|
+
lists: Record<string, string[]>;
|
|
7
|
+
error?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function parseArgs(argv: string[]): Parsed;
|
|
10
|
+
export declare function main(argv: string[], io?: Io): Promise<number>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.HELP = void 0;
|
|
13
|
+
exports.parseArgs = parseArgs;
|
|
14
|
+
exports.main = main;
|
|
15
|
+
/**
|
|
16
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
17
|
+
* http://github.com/fonoster/fonoster
|
|
18
|
+
*
|
|
19
|
+
* This file is part of Fonoster
|
|
20
|
+
*
|
|
21
|
+
* Licensed under the MIT License (the "License");
|
|
22
|
+
* you may not use this file except in compliance with
|
|
23
|
+
* the License. You may obtain a copy of the License at
|
|
24
|
+
*
|
|
25
|
+
* https://opensource.org/licenses/MIT
|
|
26
|
+
*
|
|
27
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
28
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
29
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
30
|
+
* See the License for the specific language governing permissions and
|
|
31
|
+
* limitations under the License.
|
|
32
|
+
*/
|
|
33
|
+
const path_1 = require("path");
|
|
34
|
+
const check_1 = require("./check");
|
|
35
|
+
const envFile_1 = require("./envFile");
|
|
36
|
+
const info_1 = require("./info");
|
|
37
|
+
const init_1 = require("./init");
|
|
38
|
+
const render_1 = require("./render");
|
|
39
|
+
const types_1 = require("./types");
|
|
40
|
+
// fonoster-config: sets up a Fonoster install from the Docker Compose bundle (self-host
|
|
41
|
+
// installer). Run it in the unpacked folder: `npx -p @fonoster/common fonoster-config init`.
|
|
42
|
+
exports.HELP = `Usage: fonoster-config <command> [options]
|
|
43
|
+
|
|
44
|
+
Sets up Fonoster from its Docker Compose bundle. Run it in the unpacked fonoster folder.
|
|
45
|
+
|
|
46
|
+
Commands:
|
|
47
|
+
init Generate secrets and keys, detect the host address, write .env and config/*.yaml.
|
|
48
|
+
Secrets stay in .env; config/*.yaml reference them as \${NAME}. Running it again
|
|
49
|
+
keeps every existing secret and adds only missing ones.
|
|
50
|
+
info Print the dashboard URL, the owner login, the SIP address and where files live.
|
|
51
|
+
check Validate config/*.yaml with .env's values (unknown keys, missing values, unset
|
|
52
|
+
\${NAME} placeholders, secret:// references).
|
|
53
|
+
|
|
54
|
+
Options:
|
|
55
|
+
--dir <folder> The bundle folder (default: the current folder)
|
|
56
|
+
--host-ip <address> init: the address softphones and browsers reach this machine at
|
|
57
|
+
-h, --help Show this help
|
|
58
|
+
|
|
59
|
+
Then start it with: docker compose up -d
|
|
60
|
+
Guide: https://docs.fonoster.com/self-hosting/install-compose
|
|
61
|
+
`;
|
|
62
|
+
const VALUE_FLAGS = ["dir", "host-ip", "config-dir", "env-file", "overlay"];
|
|
63
|
+
function parseArgs(argv) {
|
|
64
|
+
var _a;
|
|
65
|
+
var _b;
|
|
66
|
+
const out = { flags: {}, lists: {} };
|
|
67
|
+
for (let i = 0; i < argv.length; i++) {
|
|
68
|
+
const a = argv[i];
|
|
69
|
+
if (a === "-h" || a === "--help")
|
|
70
|
+
out.flags.help = true;
|
|
71
|
+
else if (a.startsWith("--")) {
|
|
72
|
+
const eq = a.indexOf("=");
|
|
73
|
+
const name = eq < 0 ? a.slice(2) : a.slice(2, eq);
|
|
74
|
+
const inline = eq < 0 ? undefined : a.slice(eq + 1);
|
|
75
|
+
if (!VALUE_FLAGS.includes(name))
|
|
76
|
+
return Object.assign(Object.assign({}, out), { error: `unknown option ${a}` });
|
|
77
|
+
const value = inline !== null && inline !== void 0 ? inline : argv[++i];
|
|
78
|
+
if (value === undefined)
|
|
79
|
+
return Object.assign(Object.assign({}, out), { error: `${a} needs a value` });
|
|
80
|
+
out.flags[name] = value;
|
|
81
|
+
((_a = (_b = out.lists)[name]) !== null && _a !== void 0 ? _a : (_b[name] = [])).push(value);
|
|
82
|
+
}
|
|
83
|
+
else if (!out.command)
|
|
84
|
+
out.command = a;
|
|
85
|
+
else
|
|
86
|
+
return Object.assign(Object.assign({}, out), { error: `unexpected argument ${a}` });
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* render: the renderer init uses, for other layouts (the development stack): renders the
|
|
92
|
+
* examples in --config-dir (default <dir>/config) with the values of each --env-file (default
|
|
93
|
+
* <dir>/.env; later files win), deep-merging YAML overlays from --overlay.
|
|
94
|
+
*/
|
|
95
|
+
function runRender(args, dir, io) {
|
|
96
|
+
var _a, _b, _c;
|
|
97
|
+
const envFiles = (_a = args.lists["env-file"]) !== null && _a !== void 0 ? _a : [(0, path_1.resolve)(dir, ".env")];
|
|
98
|
+
const entries = new Map();
|
|
99
|
+
for (const f of envFiles)
|
|
100
|
+
for (const [k, v] of (0, envFile_1.readEnvFile)((0, path_1.resolve)(f)))
|
|
101
|
+
entries.set(k, v);
|
|
102
|
+
const configDir = (0, path_1.resolve)(String((_b = args.flags["config-dir"]) !== null && _b !== void 0 ? _b : `${dir}/config`));
|
|
103
|
+
try {
|
|
104
|
+
const files = (0, render_1.renderConfigDir)({
|
|
105
|
+
configDir,
|
|
106
|
+
env: (0, envFile_1.expandEnv)(entries),
|
|
107
|
+
overlayDirs: ((_c = args.lists.overlay) !== null && _c !== void 0 ? _c : []).map((d) => (0, path_1.resolve)(d))
|
|
108
|
+
});
|
|
109
|
+
io.out(`rendered ${files.join(", ")}`);
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (!(e instanceof render_1.RenderError))
|
|
114
|
+
throw e;
|
|
115
|
+
e.problems.forEach((p) => io.err(p));
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function main(argv_1) {
|
|
120
|
+
return __awaiter(this, arguments, void 0, function* (argv, io = types_1.consoleIo) {
|
|
121
|
+
var _a;
|
|
122
|
+
const args = parseArgs(argv);
|
|
123
|
+
if (args.error) {
|
|
124
|
+
io.err(`fonoster-config: ${args.error}`);
|
|
125
|
+
io.err("Run fonoster-config --help for usage.");
|
|
126
|
+
return 2;
|
|
127
|
+
}
|
|
128
|
+
if (args.flags.help || !args.command || args.command === "help") {
|
|
129
|
+
exports.HELP.trimEnd()
|
|
130
|
+
.split("\n")
|
|
131
|
+
.forEach((l) => io.out(l));
|
|
132
|
+
return args.command || args.flags.help ? 0 : 2;
|
|
133
|
+
}
|
|
134
|
+
const dir = (0, path_1.resolve)(String((_a = args.flags.dir) !== null && _a !== void 0 ? _a : "."));
|
|
135
|
+
switch (args.command) {
|
|
136
|
+
case "init":
|
|
137
|
+
return (0, init_1.runInit)({
|
|
138
|
+
dir,
|
|
139
|
+
hostIp: args.flags["host-ip"] === undefined
|
|
140
|
+
? undefined
|
|
141
|
+
: String(args.flags["host-ip"])
|
|
142
|
+
}, io);
|
|
143
|
+
case "info":
|
|
144
|
+
return (0, info_1.runInfo)({ dir }, io);
|
|
145
|
+
case "check":
|
|
146
|
+
return (0, check_1.runCheck)({ dir }, io);
|
|
147
|
+
case "render":
|
|
148
|
+
return runRender(args, dir, io);
|
|
149
|
+
default:
|
|
150
|
+
io.err(`fonoster-config: unknown command ${args.command}`);
|
|
151
|
+
io.err("Run fonoster-config --help for usage.");
|
|
152
|
+
return 2;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders `config/<name>.example.<ext>` into `config/<name>.<ext>` (self-host bundle, D1).
|
|
3
|
+
*
|
|
4
|
+
* A Fonoster service's file (FONOSTER_SERVICE_FILES) is the example as written: `${NAME}` and
|
|
5
|
+
* `${NAME:-default}` stay placeholders that the service resolves from its container environment,
|
|
6
|
+
* which Compose fills from .env (unify-config-placeholders), so no secret is written into it.
|
|
7
|
+
* Another container's file (Routr's EdgePort) gets the values, since that container cannot
|
|
8
|
+
* resolve them; those hold no secrets. Either way every `${NAME}` without a default must be set,
|
|
9
|
+
* so a missing value fails at init, not at start.
|
|
10
|
+
*
|
|
11
|
+
* An overlay folder (the development stack's; several merge in order) may hold a
|
|
12
|
+
* `<name>.<ext>` YAML that is deep-merged into the result: objects merge, other values replace,
|
|
13
|
+
* and `null` removes a key.
|
|
14
|
+
*/
|
|
15
|
+
export type Env = Record<string, string | undefined>;
|
|
16
|
+
export declare class RenderError extends Error {
|
|
17
|
+
readonly problems: string[];
|
|
18
|
+
constructor(problems: string[]);
|
|
19
|
+
}
|
|
20
|
+
/** Config files read by Fonoster's own services: placeholders stay in them. */
|
|
21
|
+
export declare const FONOSTER_SERVICE_FILES: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Checks one template's placeholders and returns it with them kept (`substitute` false) or
|
|
24
|
+
* replaced by their values. Problems name the file, the line and the variable, never a value.
|
|
25
|
+
* Whole-line comments are copied as they are (they may mention `${NAME}`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function renderTemplate(text: string, env: Env, file?: string, substitute?: boolean): string;
|
|
28
|
+
export declare function deepMerge(base: unknown, overlay: unknown): unknown;
|
|
29
|
+
export declare const EXAMPLE: RegExp;
|
|
30
|
+
/** The rendered name of an example (`gateway.example.yaml` → `gateway.yaml`). */
|
|
31
|
+
export declare function renderedName(example: string): string | undefined;
|
|
32
|
+
export declare function listExamples(configDir: string): string[];
|
|
33
|
+
export type RenderOptions = {
|
|
34
|
+
configDir: string;
|
|
35
|
+
/** Where rendered files go (default configDir). */
|
|
36
|
+
outDir?: string;
|
|
37
|
+
env: Env;
|
|
38
|
+
/** Overlay folders, merged in order. */
|
|
39
|
+
overlayDirs?: string[];
|
|
40
|
+
/** File mode of rendered files (default 0644). */
|
|
41
|
+
mode?: number;
|
|
42
|
+
};
|
|
43
|
+
/** Renders every example of a config folder; returns the rendered file names. */
|
|
44
|
+
export declare function renderConfigDir(options: RenderOptions): string[];
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EXAMPLE = exports.FONOSTER_SERVICE_FILES = exports.RenderError = void 0;
|
|
4
|
+
exports.renderTemplate = renderTemplate;
|
|
5
|
+
exports.deepMerge = deepMerge;
|
|
6
|
+
exports.renderedName = renderedName;
|
|
7
|
+
exports.listExamples = listExamples;
|
|
8
|
+
exports.renderConfigDir = renderConfigDir;
|
|
9
|
+
/**
|
|
10
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
11
|
+
* http://github.com/fonoster/fonoster
|
|
12
|
+
*
|
|
13
|
+
* This file is part of Fonoster
|
|
14
|
+
*
|
|
15
|
+
* Licensed under the MIT License (the "License");
|
|
16
|
+
* you may not use this file except in compliance with
|
|
17
|
+
* the License. You may obtain a copy of the License at
|
|
18
|
+
*
|
|
19
|
+
* https://opensource.org/licenses/MIT
|
|
20
|
+
*
|
|
21
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
22
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
23
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
24
|
+
* See the License for the specific language governing permissions and
|
|
25
|
+
* limitations under the License.
|
|
26
|
+
*/
|
|
27
|
+
const fs_1 = require("fs");
|
|
28
|
+
const path_1 = require("path");
|
|
29
|
+
const yaml_1 = require("yaml");
|
|
30
|
+
const envFile_1 = require("./envFile");
|
|
31
|
+
const REF = /\$\{([A-Za-z_][A-Za-z0-9_]*)(:-([^}]*))?\}/g;
|
|
32
|
+
class RenderError extends Error {
|
|
33
|
+
constructor(problems) {
|
|
34
|
+
super(problems.join("\n"));
|
|
35
|
+
this.problems = problems;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.RenderError = RenderError;
|
|
39
|
+
/** Config files read by Fonoster's own services: placeholders stay in them. */
|
|
40
|
+
exports.FONOSTER_SERVICE_FILES = [
|
|
41
|
+
"fonoster",
|
|
42
|
+
"identity",
|
|
43
|
+
"gateway",
|
|
44
|
+
"autopilot",
|
|
45
|
+
"medianode"
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Checks one template's placeholders and returns it with them kept (`substitute` false) or
|
|
49
|
+
* replaced by their values. Problems name the file, the line and the variable, never a value.
|
|
50
|
+
* Whole-line comments are copied as they are (they may mention `${NAME}`).
|
|
51
|
+
*/
|
|
52
|
+
function renderTemplate(text, env, file = "template", substitute = false) {
|
|
53
|
+
const problems = [];
|
|
54
|
+
const out = text.split("\n").map((line, i) => {
|
|
55
|
+
if (line.trimStart().startsWith("#"))
|
|
56
|
+
return line;
|
|
57
|
+
return line.replace(REF, (m, name, dflt, fallback) => {
|
|
58
|
+
const v = env[name];
|
|
59
|
+
if (v !== undefined && v !== "")
|
|
60
|
+
return substitute ? v : m;
|
|
61
|
+
if (dflt !== undefined)
|
|
62
|
+
return substitute ? fallback : m;
|
|
63
|
+
problems.push(`${file}:${i + 1}: \${${name}} is not set`);
|
|
64
|
+
return m;
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
if (problems.length)
|
|
68
|
+
throw new RenderError(problems);
|
|
69
|
+
return out.join("\n");
|
|
70
|
+
}
|
|
71
|
+
const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
72
|
+
function deepMerge(base, overlay) {
|
|
73
|
+
if (!isObj(base) || !isObj(overlay))
|
|
74
|
+
return overlay;
|
|
75
|
+
const out = Object.assign({}, base);
|
|
76
|
+
for (const [k, v] of Object.entries(overlay)) {
|
|
77
|
+
if (v === null)
|
|
78
|
+
delete out[k];
|
|
79
|
+
else
|
|
80
|
+
out[k] = k in out ? deepMerge(out[k], v) : v;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
exports.EXAMPLE = /^(.+)\.example\.([A-Za-z0-9]+)$/;
|
|
85
|
+
/** The rendered name of an example (`gateway.example.yaml` → `gateway.yaml`). */
|
|
86
|
+
function renderedName(example) {
|
|
87
|
+
const m = exports.EXAMPLE.exec(example);
|
|
88
|
+
return m ? `${m[1]}.${m[2]}` : undefined;
|
|
89
|
+
}
|
|
90
|
+
function listExamples(configDir) {
|
|
91
|
+
return (0, fs_1.readdirSync)(configDir)
|
|
92
|
+
.filter((f) => exports.EXAMPLE.test(f))
|
|
93
|
+
.sort();
|
|
94
|
+
}
|
|
95
|
+
/** Renders every example of a config folder; returns the rendered file names. */
|
|
96
|
+
function renderConfigDir(options) {
|
|
97
|
+
var _a, _b, _c, _d, _e;
|
|
98
|
+
const outDir = (_a = options.outDir) !== null && _a !== void 0 ? _a : options.configDir;
|
|
99
|
+
const problems = [];
|
|
100
|
+
const rendered = [];
|
|
101
|
+
for (const example of listExamples(options.configDir)) {
|
|
102
|
+
const name = renderedName(example);
|
|
103
|
+
const substitute = !exports.FONOSTER_SERVICE_FILES.includes(exports.EXAMPLE.exec(example)[1]);
|
|
104
|
+
try {
|
|
105
|
+
let text = renderTemplate((0, fs_1.readFileSync)((0, path_1.join)(options.configDir, example), "utf8"), options.env, `config/${example}`, substitute);
|
|
106
|
+
const overlays = ((_b = options.overlayDirs) !== null && _b !== void 0 ? _b : [])
|
|
107
|
+
.map((d) => (0, path_1.join)(d, name))
|
|
108
|
+
.filter((f) => (0, fs_1.existsSync)(f));
|
|
109
|
+
if (overlays.length) {
|
|
110
|
+
let merged = (_c = (0, yaml_1.parse)(text)) !== null && _c !== void 0 ? _c : {};
|
|
111
|
+
for (const overlay of overlays)
|
|
112
|
+
merged = deepMerge(merged, (_d = (0, yaml_1.parse)(renderTemplate((0, fs_1.readFileSync)(overlay, "utf8"), options.env, overlay, substitute))) !== null && _d !== void 0 ? _d : {});
|
|
113
|
+
text = (0, yaml_1.stringify)(merged, { lineWidth: 0 });
|
|
114
|
+
}
|
|
115
|
+
rendered.push({ name, text });
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
if (e instanceof RenderError)
|
|
119
|
+
problems.push(...e.problems);
|
|
120
|
+
else
|
|
121
|
+
problems.push(`config/${example}: ${e.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (problems.length)
|
|
125
|
+
throw new RenderError(problems);
|
|
126
|
+
for (const { name, text } of rendered)
|
|
127
|
+
(0, envFile_1.writeWithMode)((0, path_1.join)(outDir, name), text, (_e = options.mode) !== null && _e !== void 0 ? _e : 0o644);
|
|
128
|
+
return rendered.map((r) => r.name);
|
|
129
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** A Cloak key (`npx @47ng/cloak generate` format): base64url of 32 random bytes. */
|
|
2
|
+
export declare function encryptionKey(): string;
|
|
3
|
+
/**
|
|
4
|
+
* The value `init` generates for an empty `.env` name, or undefined when the name is not a
|
|
5
|
+
* secret. Passwords start with a letter: NATS parses a variable's value as config, and one
|
|
6
|
+
* starting with a digit fails to load.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateSecret(name: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The IPv4 address of the default route's interface: a UDP socket "connected" to a public
|
|
11
|
+
* address (no packet is sent) reports the source address the kernel would use.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectHostIp(timeoutMs?: number): Promise<string | undefined>;
|
|
14
|
+
/** Files the containers read: world-readable, not writable (they run as another user). */
|
|
15
|
+
export declare const CONTAINER_READ = 420;
|
|
16
|
+
/**
|
|
17
|
+
* The keys of an install, each generated only when its file is missing (config/keys):
|
|
18
|
+
* Identity's RS256 pair and the call-record ES256 key. Hosting private regions is a manual
|
|
19
|
+
* addition (fonoster-region-hub-setup in the apiserver image), not part of init.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensureKeys(configDir: string): string[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONTAINER_READ = void 0;
|
|
4
|
+
exports.encryptionKey = encryptionKey;
|
|
5
|
+
exports.generateSecret = generateSecret;
|
|
6
|
+
exports.detectHostIp = detectHostIp;
|
|
7
|
+
exports.ensureKeys = ensureKeys;
|
|
8
|
+
/**
|
|
9
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
10
|
+
* http://github.com/fonoster/fonoster
|
|
11
|
+
*
|
|
12
|
+
* This file is part of Fonoster
|
|
13
|
+
*
|
|
14
|
+
* Licensed under the MIT License (the "License");
|
|
15
|
+
* you may not use this file except in compliance with
|
|
16
|
+
* the License. You may obtain a copy of the License at
|
|
17
|
+
*
|
|
18
|
+
* https://opensource.org/licenses/MIT
|
|
19
|
+
*
|
|
20
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
* See the License for the specific language governing permissions and
|
|
24
|
+
* limitations under the License.
|
|
25
|
+
*/
|
|
26
|
+
const crypto_1 = require("crypto");
|
|
27
|
+
const dgram_1 = require("dgram");
|
|
28
|
+
const fs_1 = require("fs");
|
|
29
|
+
const path_1 = require("path");
|
|
30
|
+
const envFile_1 = require("./envFile");
|
|
31
|
+
const hex = (bytes) => (0, crypto_1.randomBytes)(bytes).toString("hex");
|
|
32
|
+
/** A Cloak key (`npx @47ng/cloak generate` format): base64url of 32 random bytes. */
|
|
33
|
+
function encryptionKey() {
|
|
34
|
+
return `k1.aesgcm256.${(0, crypto_1.randomBytes)(32).toString("base64url")}=`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The value `init` generates for an empty `.env` name, or undefined when the name is not a
|
|
38
|
+
* secret. Passwords start with a letter: NATS parses a variable's value as config, and one
|
|
39
|
+
* starting with a digit fails to load.
|
|
40
|
+
*/
|
|
41
|
+
function generateSecret(name) {
|
|
42
|
+
if (name === "ENCRYPTION_KEY")
|
|
43
|
+
return encryptionKey();
|
|
44
|
+
if (name === "AUTOPILOT_SIGNING_SECRET")
|
|
45
|
+
return hex(24);
|
|
46
|
+
if (name === "OWNER_PASSWORD")
|
|
47
|
+
return `p${hex(8)}`;
|
|
48
|
+
if (/(_PASSWORD|_SECRET|_KEY|_TOKEN)$/.test(name))
|
|
49
|
+
return `s${hex(16)}`;
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The IPv4 address of the default route's interface: a UDP socket "connected" to a public
|
|
54
|
+
* address (no packet is sent) reports the source address the kernel would use.
|
|
55
|
+
*/
|
|
56
|
+
function detectHostIp(timeoutMs = 2000) {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const socket = (0, dgram_1.createSocket)("udp4");
|
|
59
|
+
const done = (ip) => {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
try {
|
|
62
|
+
socket.close();
|
|
63
|
+
}
|
|
64
|
+
catch (_a) {
|
|
65
|
+
// already closed
|
|
66
|
+
}
|
|
67
|
+
resolve(ip && ip !== "0.0.0.0" ? ip : undefined);
|
|
68
|
+
};
|
|
69
|
+
const timer = setTimeout(() => done(), timeoutMs);
|
|
70
|
+
socket.on("error", () => done());
|
|
71
|
+
try {
|
|
72
|
+
socket.connect(53, "192.0.2.1", () => {
|
|
73
|
+
try {
|
|
74
|
+
done(socket.address().address);
|
|
75
|
+
}
|
|
76
|
+
catch (_a) {
|
|
77
|
+
done();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (_a) {
|
|
82
|
+
done();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Files the containers read: world-readable, not writable (they run as another user). */
|
|
87
|
+
exports.CONTAINER_READ = 0o644;
|
|
88
|
+
function once(path, make, created) {
|
|
89
|
+
if ((0, fs_1.existsSync)(path))
|
|
90
|
+
return;
|
|
91
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(path), { recursive: true, mode: 0o755 });
|
|
92
|
+
(0, envFile_1.writeWithMode)(path, make().toString(), exports.CONTAINER_READ);
|
|
93
|
+
created.push(path);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The keys of an install, each generated only when its file is missing (config/keys):
|
|
97
|
+
* Identity's RS256 pair and the call-record ES256 key. Hosting private regions is a manual
|
|
98
|
+
* addition (fonoster-region-hub-setup in the apiserver image), not part of init.
|
|
99
|
+
*/
|
|
100
|
+
function ensureKeys(configDir) {
|
|
101
|
+
const keys = (0, path_1.join)(configDir, "keys");
|
|
102
|
+
const created = [];
|
|
103
|
+
if (!(0, fs_1.existsSync)((0, path_1.join)(keys, "private.pem"))) {
|
|
104
|
+
const pair = (0, crypto_1.generateKeyPairSync)("rsa", { modulusLength: 2048 });
|
|
105
|
+
(0, fs_1.mkdirSync)(keys, { recursive: true, mode: 0o755 });
|
|
106
|
+
const pub = (0, path_1.join)(keys, "public.pem");
|
|
107
|
+
const priv = (0, path_1.join)(keys, "private.pem");
|
|
108
|
+
(0, envFile_1.writeWithMode)(pub, pair.publicKey.export({ type: "spki", format: "pem" }).toString(), exports.CONTAINER_READ);
|
|
109
|
+
(0, envFile_1.writeWithMode)(priv, pair.privateKey.export({ type: "pkcs8", format: "pem" }).toString(), exports.CONTAINER_READ);
|
|
110
|
+
created.push(priv, pub);
|
|
111
|
+
}
|
|
112
|
+
once((0, path_1.join)(keys, "records.pem"), () => (0, crypto_1.generateKeyPairSync)("ec", { namedCurve: "P-256" }).privateKey.export({
|
|
113
|
+
type: "pkcs8",
|
|
114
|
+
format: "pem"
|
|
115
|
+
}), created);
|
|
116
|
+
return created;
|
|
117
|
+
}
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/** Where a command writes: its normal output and its problems (tests capture both). */
|
|
20
|
+
export type Io = {
|
|
21
|
+
out: (line: string) => void;
|
|
22
|
+
err: (line: string) => void;
|
|
23
|
+
};
|
|
24
|
+
export declare const consoleIo: Io;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
type Env = Record<string, string | undefined>;
|
|
20
|
+
/** Old key path → new key path, e.g. `{ encryptionKey: "encryption.key" }`. */
|
|
21
|
+
type MovedKeys = Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Lists the moved keys present in a raw config, as readable hints. The new
|
|
24
|
+
* location may itself be valid, so this only reports what was found.
|
|
25
|
+
*/
|
|
26
|
+
declare function findMovedKeys(raw: unknown, movedKeys: MovedKeys): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Names of environment variables that look like the old env-based config for
|
|
29
|
+
* this service. Values are never returned, so they can be logged safely.
|
|
30
|
+
*/
|
|
31
|
+
declare function findLegacyEnv(prefixes: string[], env?: Env): string[];
|
|
32
|
+
/** Edit distance between two strings (insertions, deletions, substitutions). */
|
|
33
|
+
declare function levenshtein(a: string, b: string): number;
|
|
34
|
+
/**
|
|
35
|
+
* The known key closest to an unknown one (`turnTakng` → `turnTaking`), when it is close enough
|
|
36
|
+
* to be a typo: at most a third of the key's length (at least 1) edits away, ignoring case.
|
|
37
|
+
*/
|
|
38
|
+
declare function suggestKey(unknown: string, known: string[]): string | undefined;
|
|
39
|
+
export { findMovedKeys, findLegacyEnv, levenshtein, suggestKey, MovedKeys };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findMovedKeys = findMovedKeys;
|
|
4
|
+
exports.findLegacyEnv = findLegacyEnv;
|
|
5
|
+
exports.levenshtein = levenshtein;
|
|
6
|
+
exports.suggestKey = suggestKey;
|
|
7
|
+
function hasPath(input, path) {
|
|
8
|
+
let current = input;
|
|
9
|
+
for (const segment of path.split(".")) {
|
|
10
|
+
if (current === null ||
|
|
11
|
+
typeof current !== "object" ||
|
|
12
|
+
!(segment in current)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
current = current[segment];
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Lists the moved keys present in a raw config, as readable hints. The new
|
|
21
|
+
* location may itself be valid, so this only reports what was found.
|
|
22
|
+
*/
|
|
23
|
+
function findMovedKeys(raw, movedKeys) {
|
|
24
|
+
return Object.entries(movedKeys)
|
|
25
|
+
.filter(([from]) => hasPath(raw, from))
|
|
26
|
+
.map(([from, to]) => `"${from}" moved to "${to}"`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Names of environment variables that look like the old env-based config for
|
|
30
|
+
* this service. Values are never returned, so they can be logged safely.
|
|
31
|
+
*/
|
|
32
|
+
function findLegacyEnv(prefixes, env = process.env) {
|
|
33
|
+
return Object.keys(env)
|
|
34
|
+
.filter((name) => prefixes.some((prefix) => name.startsWith(prefix)))
|
|
35
|
+
.sort();
|
|
36
|
+
}
|
|
37
|
+
/** Edit distance between two strings (insertions, deletions, substitutions). */
|
|
38
|
+
function levenshtein(a, b) {
|
|
39
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
40
|
+
for (let i = 1; i <= a.length; i++) {
|
|
41
|
+
const row = [i];
|
|
42
|
+
for (let j = 1; j <= b.length; j++)
|
|
43
|
+
row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
44
|
+
prev = row;
|
|
45
|
+
}
|
|
46
|
+
return prev[b.length];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The known key closest to an unknown one (`turnTakng` → `turnTaking`), when it is close enough
|
|
50
|
+
* to be a typo: at most a third of the key's length (at least 1) edits away, ignoring case.
|
|
51
|
+
*/
|
|
52
|
+
function suggestKey(unknown, known) {
|
|
53
|
+
const limit = Math.max(1, Math.floor(unknown.length / 3));
|
|
54
|
+
let best;
|
|
55
|
+
for (const key of known) {
|
|
56
|
+
const distance = levenshtein(unknown.toLowerCase(), key.toLowerCase());
|
|
57
|
+
if (distance <= limit && (!best || distance < best.distance))
|
|
58
|
+
best = { key, distance };
|
|
59
|
+
}
|
|
60
|
+
return best === null || best === void 0 ? void 0 : best.key;
|
|
61
|
+
}
|
|
@@ -16,16 +16,15 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
export * from "./
|
|
20
|
-
export * from "./
|
|
21
|
-
export * from "./
|
|
22
|
-
export * from "./
|
|
23
|
-
export * from "./
|
|
24
|
-
export * from "./
|
|
25
|
-
export * from "./
|
|
26
|
-
export * from "./
|
|
27
|
-
export * from "./
|
|
28
|
-
export * from "./
|
|
29
|
-
export * from "./
|
|
30
|
-
export * from "./
|
|
31
|
-
export * from "./voice";
|
|
19
|
+
export * from "./configHints";
|
|
20
|
+
export * from "./integrationsSchema";
|
|
21
|
+
export * from "./interpolateEnv";
|
|
22
|
+
export * from "./loadServiceConfig";
|
|
23
|
+
export * from "./logsSchema";
|
|
24
|
+
export * from "./parseConfigFile";
|
|
25
|
+
export * from "./resolveConfigFile";
|
|
26
|
+
export * from "./resolveSecretRefs";
|
|
27
|
+
export * from "./strictDeep";
|
|
28
|
+
export * from "./telemetrySchema";
|
|
29
|
+
export * from "./natsConnection";
|
|
30
|
+
export * from "./toJsonSchema";
|