@fonoster/ctl 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/README.md +588 -90
- package/dist/AuthenticatedCommand.d.ts +5 -0
- package/dist/AuthenticatedCommand.js +12 -1
- package/dist/commands/applications/create.d.ts +22 -0
- package/dist/commands/applications/create.js +68 -169
- package/dist/commands/applications/eval.d.ts +1 -1
- package/dist/commands/applications/eval.js +46 -33
- package/dist/commands/applications/get.d.ts +2 -0
- package/dist/commands/applications/get.js +67 -24
- package/dist/commands/applications/list.js +19 -3
- package/dist/commands/applications/rotate-secret.d.ts +9 -0
- package/dist/commands/applications/rotate-secret.js +97 -0
- package/dist/commands/applications/update.d.ts +21 -1
- package/dist/commands/applications/update.js +18 -168
- package/dist/commands/providers/catalog.d.ts +10 -0
- package/dist/commands/providers/catalog.js +128 -0
- package/dist/commands/providers/keys/delete.d.ts +9 -0
- package/dist/commands/providers/keys/delete.js +92 -0
- package/dist/commands/providers/keys/list.d.ts +6 -0
- package/dist/commands/providers/keys/list.js +97 -0
- package/dist/commands/providers/keys/set.d.ts +12 -0
- package/dist/commands/providers/keys/set.js +110 -0
- package/dist/commands/records/download.d.ts +15 -0
- package/dist/commands/records/download.js +132 -0
- package/dist/commands/records/get.d.ts +15 -0
- package/dist/commands/records/get.js +146 -0
- package/dist/commands/records/list.d.ts +15 -0
- package/dist/commands/records/list.js +136 -0
- package/dist/commands/sipnet/calls/create.d.ts +3 -3
- package/dist/commands/sipnet/calls/create.js +35 -52
- package/dist/commands/sipnet/numbers/linkTwilioNumber.js +4 -3
- package/dist/commands/webhooks/create.d.ts +10 -0
- package/dist/commands/webhooks/create.js +104 -0
- package/dist/commands/webhooks/delete.d.ts +9 -0
- package/dist/commands/webhooks/delete.js +92 -0
- package/dist/commands/webhooks/deliveries.d.ts +12 -0
- package/dist/commands/webhooks/deliveries.js +119 -0
- package/dist/commands/webhooks/get.d.ts +9 -0
- package/dist/commands/webhooks/get.js +110 -0
- package/dist/commands/webhooks/list.d.ts +9 -0
- package/dist/commands/webhooks/list.js +104 -0
- package/dist/commands/webhooks/resend.d.ts +9 -0
- package/dist/commands/webhooks/resend.js +95 -0
- package/dist/commands/webhooks/rotate.d.ts +9 -0
- package/dist/commands/webhooks/rotate.js +98 -0
- package/dist/commands/webhooks/update.d.ts +13 -0
- package/dist/commands/webhooks/update.js +103 -0
- package/dist/commands/workspaces/login.d.ts +5 -0
- package/dist/commands/workspaces/login.js +28 -2
- package/dist/constants.d.ts +1 -2
- package/dist/constants.js +2 -3
- package/dist/utils/applicationFlags.d.ts +24 -0
- package/dist/utils/applicationFlags.js +95 -0
- package/dist/utils/applicationRequest.d.ts +42 -0
- package/dist/utils/applicationRequest.js +215 -0
- package/dist/utils/createOrUpdateApplication.d.ts +26 -1
- package/dist/utils/createOrUpdateApplication.js +23 -27
- package/dist/utils/format.d.ts +20 -0
- package/dist/utils/format.js +31 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/trackCallStatus.d.ts +4 -0
- package/dist/utils/trackCallStatus.js +67 -0
- package/dist/utils/webhookFlags.d.ts +3 -0
- package/dist/utils/webhookFlags.js +34 -0
- package/package.json +6 -7
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
/**
|
|
49
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
50
|
+
* http://github.com/fonoster/fonoster
|
|
51
|
+
*
|
|
52
|
+
* This file is part of Fonoster
|
|
53
|
+
*
|
|
54
|
+
* Licensed under the MIT License (the "License");
|
|
55
|
+
* you may not use this file except in compliance with
|
|
56
|
+
* the License. You may obtain a copy of the License at
|
|
57
|
+
*
|
|
58
|
+
* https://opensource.org/licenses/MIT
|
|
59
|
+
*
|
|
60
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
61
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
62
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
63
|
+
* See the License for the specific language governing permissions and
|
|
64
|
+
* limitations under the License.
|
|
65
|
+
*/
|
|
66
|
+
const SDK = __importStar(require("@fonoster/sdk"));
|
|
67
|
+
const core_1 = require("@oclif/core");
|
|
68
|
+
const AuthenticatedCommand_1 = require("../../AuthenticatedCommand");
|
|
69
|
+
const errorHandler_1 = __importDefault(require("../../errorHandler"));
|
|
70
|
+
const webhookFlags_1 = require("../../utils/webhookFlags");
|
|
71
|
+
class Update extends AuthenticatedCommand_1.AuthenticatedCommand {
|
|
72
|
+
run() {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
var _a, _b;
|
|
75
|
+
const { args, flags } = yield this.parse(Update);
|
|
76
|
+
if (!flags.url && !((_a = flags.event) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
77
|
+
this.error("Nothing to update: pass --url and/or --event.");
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const client = yield this.createSdkClient();
|
|
81
|
+
const webhooks = new SDK.Webhooks(client);
|
|
82
|
+
yield webhooks.updateWebhook(Object.assign(Object.assign({ ref: args.ref }, (flags.url ? { url: flags.url } : {})), (((_b = flags.event) === null || _b === void 0 ? void 0 : _b.length) ? { events: flags.event } : {})));
|
|
83
|
+
this.log("Done!");
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
(0, errorHandler_1.default)(e, this.error.bind(this));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
Update.description = "change a Webhook's URL or events (--event replaces the list)";
|
|
92
|
+
Update.examples = [
|
|
93
|
+
"<%= config.bin %> <%= command.id %> <ref> -e call.started -e call.ended",
|
|
94
|
+
"<%= config.bin %> <%= command.id %> <ref> --url https://example.com/new"
|
|
95
|
+
];
|
|
96
|
+
Update.args = {
|
|
97
|
+
ref: core_1.Args.string({ description: "the Webhook to update", required: true })
|
|
98
|
+
};
|
|
99
|
+
Update.flags = {
|
|
100
|
+
url: core_1.Flags.string({ char: "u", description: "the new endpoint" }),
|
|
101
|
+
event: (0, webhookFlags_1.eventFlag)(false)
|
|
102
|
+
};
|
|
103
|
+
exports.default = Update;
|
|
@@ -2,6 +2,11 @@ import { BaseCommand } from "../../BaseCommand";
|
|
|
2
2
|
export default class Login extends BaseCommand<typeof Login> {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
endpoint: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
"access-key-id": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
"access-key-secret": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
5
10
|
run(): Promise<void>;
|
|
6
11
|
private saveConfig;
|
|
7
12
|
private getWorkspaceFromDB;
|
|
@@ -62,6 +62,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
62
62
|
*/
|
|
63
63
|
const SDK = __importStar(require("@fonoster/sdk"));
|
|
64
64
|
const prompts_1 = require("@inquirer/prompts");
|
|
65
|
+
const core_1 = require("@oclif/core");
|
|
65
66
|
const BaseCommand_1 = require("../../BaseCommand");
|
|
66
67
|
const config_1 = require("../../config");
|
|
67
68
|
const saveConfig_1 = require("../../config/saveConfig");
|
|
@@ -69,12 +70,24 @@ const constants_1 = require("../../constants");
|
|
|
69
70
|
class Login extends BaseCommand_1.BaseCommand {
|
|
70
71
|
run() {
|
|
71
72
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
var _a;
|
|
74
|
+
const { flags } = yield this.parse(Login);
|
|
75
|
+
if (flags["access-key-id"] && flags["access-key-secret"]) {
|
|
76
|
+
const answers = {
|
|
77
|
+
endpoint: (_a = flags.endpoint) !== null && _a !== void 0 ? _a : "api.fonoster.ai",
|
|
78
|
+
accessKeyId: flags["access-key-id"],
|
|
79
|
+
accessKeySecret: flags["access-key-secret"]
|
|
80
|
+
};
|
|
81
|
+
const workspaceFromDB = yield this.getWorkspaceFromDB(answers);
|
|
82
|
+
this.saveConfig(Object.assign(Object.assign({}, answers), { workspaceAccessKeyId: workspaceFromDB.accessKeyId, ref: workspaceFromDB.ref, name: workspaceFromDB.name }));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
72
85
|
this.log("This utility will help you add a Workspace.");
|
|
73
86
|
this.log("Press ^C at any time to quit.");
|
|
74
87
|
const answers = {
|
|
75
88
|
endpoint: yield (0, prompts_1.input)({
|
|
76
89
|
message: "Endpoint",
|
|
77
|
-
default: "api.fonoster.
|
|
90
|
+
default: "api.fonoster.ai"
|
|
78
91
|
}),
|
|
79
92
|
accessKeyId: yield (0, prompts_1.input)({
|
|
80
93
|
message: "Access Key Id",
|
|
@@ -140,5 +153,18 @@ class Login extends BaseCommand_1.BaseCommand {
|
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
Login.description = "link a Workspace to the local environment";
|
|
143
|
-
Login.examples = [
|
|
156
|
+
Login.examples = [
|
|
157
|
+
"<%= config.bin %> <%= command.id %>",
|
|
158
|
+
"<%= config.bin %> <%= command.id %> --insecure --endpoint localhost:16449 --access-key-id WO... --access-key-secret ..."
|
|
159
|
+
];
|
|
160
|
+
Login.flags = {
|
|
161
|
+
endpoint: core_1.Flags.string({
|
|
162
|
+
char: "e",
|
|
163
|
+
description: "the API endpoint (skips the questions with the key flags)"
|
|
164
|
+
}),
|
|
165
|
+
"access-key-id": core_1.Flags.string({ description: "a Workspace API key id" }),
|
|
166
|
+
"access-key-secret": core_1.Flags.string({
|
|
167
|
+
description: "the API key secret"
|
|
168
|
+
})
|
|
169
|
+
};
|
|
144
170
|
exports.default = Login;
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare const BASE_DIR: string;
|
|
2
2
|
export declare const CONFIG_FILE: string;
|
|
3
3
|
export declare const FONOSTER_ACCESS_CONTROL_LIST: string[];
|
|
4
|
-
export declare const FONOSTER_ORIGINATION_URI_BASE = "pstn.fonoster.com";
|
|
5
4
|
export declare const TWILIO_PSTN_URI_BASE = "pstn.twilio.com";
|
|
6
|
-
export declare const WORKSPACE_ENDPOINT = "api.fonoster.
|
|
5
|
+
export declare const WORKSPACE_ENDPOINT = "api.fonoster.ai";
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WORKSPACE_ENDPOINT = exports.TWILIO_PSTN_URI_BASE = exports.
|
|
3
|
+
exports.WORKSPACE_ENDPOINT = exports.TWILIO_PSTN_URI_BASE = exports.FONOSTER_ACCESS_CONTROL_LIST = exports.CONFIG_FILE = exports.BASE_DIR = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
6
|
* http://github.com/fonoster/fonoster
|
|
@@ -24,6 +24,5 @@ const path_1 = require("path");
|
|
|
24
24
|
exports.BASE_DIR = (0, path_1.join)((0, os_1.homedir)(), ".fonoster");
|
|
25
25
|
exports.CONFIG_FILE = (0, path_1.join)((0, os_1.homedir)(), ".fonoster", "config.json");
|
|
26
26
|
exports.FONOSTER_ACCESS_CONTROL_LIST = ["165.22.7.155/32"]; // TODO: We will need to allow passing this as a parameter
|
|
27
|
-
exports.FONOSTER_ORIGINATION_URI_BASE = "pstn.fonoster.com";
|
|
28
27
|
exports.TWILIO_PSTN_URI_BASE = "pstn.twilio.com";
|
|
29
|
-
exports.WORKSPACE_ENDPOINT = "api.fonoster.
|
|
28
|
+
exports.WORKSPACE_ENDPOINT = "api.fonoster.ai";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Flags shared by `applications:create` and `applications:update` (a stream or an Autopilot agent). */
|
|
2
|
+
export declare const applicationFlags: {
|
|
3
|
+
"from-file": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
5
|
+
url: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
protocol: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
voice: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
language: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
"stt-model": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
"tts-model": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
"silence-timeout": import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
patience: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
"audio-codec": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
"audio-rate": import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
"fallback-app": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
+
"fallback-message": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
+
listener: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
"clear-listeners": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
20
|
+
recording: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
amd: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
"amd-window": import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
"noise-cancelling": import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applicationFlags = 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 core_1 = require("@oclif/core");
|
|
23
|
+
/** Flags shared by `applications:create` and `applications:update` (a stream or an Autopilot agent). */
|
|
24
|
+
exports.applicationFlags = {
|
|
25
|
+
"from-file": core_1.Flags.string({
|
|
26
|
+
char: "f",
|
|
27
|
+
description: "a YAML or JSON file with the Application (an `agent` key makes it an Autopilot agent)"
|
|
28
|
+
}),
|
|
29
|
+
name: core_1.Flags.string({ char: "n", description: "the Application name" }),
|
|
30
|
+
url: core_1.Flags.string({
|
|
31
|
+
char: "u",
|
|
32
|
+
description: "the stream's WebSocket URL (ws:// or wss://)"
|
|
33
|
+
}),
|
|
34
|
+
protocol: core_1.Flags.string({
|
|
35
|
+
description: "the protocol the stream speaks",
|
|
36
|
+
options: ["fonoster", "twilio"]
|
|
37
|
+
}),
|
|
38
|
+
agent: core_1.Flags.string({
|
|
39
|
+
char: "a",
|
|
40
|
+
description: "a YAML or JSON agent file; makes it an Autopilot agent (hosted by Fonoster)"
|
|
41
|
+
}),
|
|
42
|
+
voice: core_1.Flags.string({
|
|
43
|
+
description: "TTS voice as a catalog id (e.g. deepgram/aura-2-thalia-en)"
|
|
44
|
+
}),
|
|
45
|
+
language: core_1.Flags.string({ description: "speech language (e.g. en-US)" }),
|
|
46
|
+
"stt-model": core_1.Flags.string({
|
|
47
|
+
description: "STT model as a catalog id (e.g. deepgram/nova-3)"
|
|
48
|
+
}),
|
|
49
|
+
"tts-model": core_1.Flags.string({
|
|
50
|
+
description: "TTS model as a catalog id (e.g. elevenlabs/eleven_flash_v2_5)"
|
|
51
|
+
}),
|
|
52
|
+
"silence-timeout": core_1.Flags.integer({
|
|
53
|
+
description: "milliseconds of caller silence before a silence event"
|
|
54
|
+
}),
|
|
55
|
+
patience: core_1.Flags.string({
|
|
56
|
+
description: "how long to wait before taking the turn",
|
|
57
|
+
options: ["low", "normal", "high"]
|
|
58
|
+
}),
|
|
59
|
+
"audio-codec": core_1.Flags.string({
|
|
60
|
+
description: "audio frames for fonoster-protocol streams",
|
|
61
|
+
options: ["pcm16", "pcmu"]
|
|
62
|
+
}),
|
|
63
|
+
"audio-rate": core_1.Flags.integer({
|
|
64
|
+
description: "audio sample rate for fonoster-protocol streams: 8000, 16000 or 24000 (pcmu is 8000 only)"
|
|
65
|
+
}),
|
|
66
|
+
"fallback-app": core_1.Flags.string({
|
|
67
|
+
description: "the Application to hand the call to when this one fails"
|
|
68
|
+
}),
|
|
69
|
+
"fallback-message": core_1.Flags.string({
|
|
70
|
+
description: "what the caller hears when the app fails and no fallback app answers"
|
|
71
|
+
}),
|
|
72
|
+
listener: core_1.Flags.string({
|
|
73
|
+
char: "l",
|
|
74
|
+
description: "a listen-only socket: <wss-url>[,inbound|outbound|both] (repeatable, at most 4)",
|
|
75
|
+
multiple: true
|
|
76
|
+
}),
|
|
77
|
+
"clear-listeners": core_1.Flags.boolean({
|
|
78
|
+
description: "remove the current listeners (update; combine with --listener to replace)",
|
|
79
|
+
default: false
|
|
80
|
+
}),
|
|
81
|
+
recording: core_1.Flags.string({
|
|
82
|
+
description: "the recording policy",
|
|
83
|
+
options: ["off", "on", "consent"]
|
|
84
|
+
}),
|
|
85
|
+
amd: core_1.Flags.boolean({
|
|
86
|
+
description: "answering-machine detection: the app gets one amd.result per call (--no-amd turns it off)",
|
|
87
|
+
allowNo: true
|
|
88
|
+
}),
|
|
89
|
+
"amd-window": core_1.Flags.integer({
|
|
90
|
+
description: "milliseconds of caller audio AMD classifies (500 to 15000, default 4000); implies --amd"
|
|
91
|
+
}),
|
|
92
|
+
"noise-cancelling": core_1.Flags.string({
|
|
93
|
+
description: "noise cancelling on the caller's audio: none, or a noise-cancelling provider (aicoustics)"
|
|
94
|
+
})
|
|
95
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Application, AppListener, CreateApplicationRequest } from "@fonoster/types";
|
|
2
|
+
type ApplicationFlags = {
|
|
3
|
+
name?: string;
|
|
4
|
+
url?: string;
|
|
5
|
+
protocol?: string;
|
|
6
|
+
agent?: string;
|
|
7
|
+
voice?: string;
|
|
8
|
+
language?: string;
|
|
9
|
+
"stt-model"?: string;
|
|
10
|
+
"tts-model"?: string;
|
|
11
|
+
"silence-timeout"?: number;
|
|
12
|
+
patience?: string;
|
|
13
|
+
"audio-codec"?: string;
|
|
14
|
+
"audio-rate"?: number;
|
|
15
|
+
"fallback-app"?: string;
|
|
16
|
+
"fallback-message"?: string;
|
|
17
|
+
listener?: string[];
|
|
18
|
+
"clear-listeners"?: boolean;
|
|
19
|
+
recording?: string;
|
|
20
|
+
amd?: boolean;
|
|
21
|
+
"amd-window"?: number;
|
|
22
|
+
/** none, or an nc provider id (aicoustics). */
|
|
23
|
+
"noise-cancelling"?: string;
|
|
24
|
+
};
|
|
25
|
+
type AppRequest = CreateApplicationRequest & {
|
|
26
|
+
ref?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Reads a YAML or JSON file into an object. */
|
|
29
|
+
declare function readConfigFile<T = Record<string, unknown>>(filePath: string): T;
|
|
30
|
+
/** `wss://host/path` or `wss://host/path,inbound` (tracks: inbound, outbound or both). */
|
|
31
|
+
declare function parseListener(value: string): AppListener;
|
|
32
|
+
/** An existing app as a create/update request (blank fields dropped). */
|
|
33
|
+
declare function applicationToRequest(app: Application): AppRequest;
|
|
34
|
+
/** A file in the new app model; `agent` (inline) makes it an Autopilot agent. */
|
|
35
|
+
declare function fileToRequest(raw: Record<string, unknown>): AppRequest;
|
|
36
|
+
/**
|
|
37
|
+
* Applies command flags on top of `base` (the current app on update, `{}` on create).
|
|
38
|
+
*/
|
|
39
|
+
declare function applyFlags(base: AppRequest, flags: ApplicationFlags): AppRequest;
|
|
40
|
+
/** Throws the errors the control plane would, before a round trip. */
|
|
41
|
+
declare function checkRequest(request: AppRequest): void;
|
|
42
|
+
export { AppRequest, ApplicationFlags, applicationToRequest, applyFlags, checkRequest, fileToRequest, parseListener, readConfigFile };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.applicationToRequest = applicationToRequest;
|
|
18
|
+
exports.applyFlags = applyFlags;
|
|
19
|
+
exports.checkRequest = checkRequest;
|
|
20
|
+
exports.fileToRequest = fileToRequest;
|
|
21
|
+
exports.parseListener = parseListener;
|
|
22
|
+
exports.readConfigFile = readConfigFile;
|
|
23
|
+
/**
|
|
24
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
25
|
+
* http://github.com/fonoster/fonoster
|
|
26
|
+
*
|
|
27
|
+
* This file is part of Fonoster
|
|
28
|
+
*
|
|
29
|
+
* Licensed under the MIT License (the "License");
|
|
30
|
+
* you may not use this file except in compliance with
|
|
31
|
+
* the License. You may obtain a copy of the License at
|
|
32
|
+
*
|
|
33
|
+
* https://opensource.org/licenses/MIT
|
|
34
|
+
*
|
|
35
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
36
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
37
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
38
|
+
* See the License for the specific language governing permissions and
|
|
39
|
+
* limitations under the License.
|
|
40
|
+
*/
|
|
41
|
+
const fs_1 = require("fs");
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const common_1 = require("@fonoster/common");
|
|
44
|
+
const js_yaml_1 = require("js-yaml");
|
|
45
|
+
const TRACKS = ["inbound", "outbound", "both"];
|
|
46
|
+
/** Reads a YAML or JSON file into an object. */
|
|
47
|
+
function readConfigFile(filePath) {
|
|
48
|
+
const content = (0, fs_1.readFileSync)(filePath, "utf8");
|
|
49
|
+
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
50
|
+
if (ext === ".yaml" || ext === ".yml")
|
|
51
|
+
return (0, js_yaml_1.load)(content);
|
|
52
|
+
if (ext === ".json")
|
|
53
|
+
return JSON.parse(content);
|
|
54
|
+
throw new Error("Unsupported file format. Please use YAML or JSON files.");
|
|
55
|
+
}
|
|
56
|
+
/** `wss://host/path` or `wss://host/path,inbound` (tracks: inbound, outbound or both). */
|
|
57
|
+
function parseListener(value) {
|
|
58
|
+
const comma = value.lastIndexOf(",");
|
|
59
|
+
const tail = comma > 0 ? value.slice(comma + 1).trim() : "";
|
|
60
|
+
if (comma > 0 && TRACKS.includes(tail)) {
|
|
61
|
+
return {
|
|
62
|
+
url: value.slice(0, comma).trim(),
|
|
63
|
+
tracks: tail
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (comma > 0 && tail && !tail.includes("/")) {
|
|
67
|
+
throw new Error(`invalid listener tracks "${tail}" (use inbound, outbound or both)`);
|
|
68
|
+
}
|
|
69
|
+
return { url: value.trim(), tracks: "both" };
|
|
70
|
+
}
|
|
71
|
+
function blank(value) {
|
|
72
|
+
return value === "" || value === 0 || value === null || value === undefined;
|
|
73
|
+
}
|
|
74
|
+
function compact(obj) {
|
|
75
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => !blank(v)));
|
|
76
|
+
}
|
|
77
|
+
/** The LLM product the control plane files the agent under (`llm.<provider>`). */
|
|
78
|
+
function intelligenceFor(agent) {
|
|
79
|
+
var _a;
|
|
80
|
+
const model = (_a = agent.languageModel) === null || _a === void 0 ? void 0 : _a.model;
|
|
81
|
+
return {
|
|
82
|
+
productRef: (model === null || model === void 0 ? void 0 : model.includes("/")) ? `llm.${model.split("/")[0]}` : "",
|
|
83
|
+
config: agent
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** An existing app as a create/update request (blank fields dropped). */
|
|
87
|
+
function applicationToRequest(app) {
|
|
88
|
+
var _a, _b, _c, _d, _e, _f;
|
|
89
|
+
const request = {
|
|
90
|
+
name: app.name,
|
|
91
|
+
type: app.type,
|
|
92
|
+
protocol: (app.protocol || undefined),
|
|
93
|
+
speech: compact(Object.assign({}, ((_a = app.speech) !== null && _a !== void 0 ? _a : {}))),
|
|
94
|
+
audio: ((_b = app.audio) === null || _b === void 0 ? void 0 : _b.codec) ? app.audio : undefined,
|
|
95
|
+
fallbackAppRef: app.fallbackAppRef || undefined,
|
|
96
|
+
fallbackMessage: app.fallbackMessage || undefined,
|
|
97
|
+
listeners: (_c = app.listeners) !== null && _c !== void 0 ? _c : [],
|
|
98
|
+
recording: (app.recording || undefined),
|
|
99
|
+
amd: ((_d = app.amd) === null || _d === void 0 ? void 0 : _d.enabled)
|
|
100
|
+
? compact({ enabled: true, windowMs: app.amd.windowMs })
|
|
101
|
+
: undefined,
|
|
102
|
+
filters: (_e = app.filters) !== null && _e !== void 0 ? _e : []
|
|
103
|
+
};
|
|
104
|
+
// Autopilot apps follow the operator's hosted Autopilot URL; only streams carry theirs.
|
|
105
|
+
if (app.type !== "AUTOPILOT")
|
|
106
|
+
request.url = app.url || app.endpoint;
|
|
107
|
+
const config = (_f = app.intelligence) === null || _f === void 0 ? void 0 : _f.config;
|
|
108
|
+
if (app.type === "AUTOPILOT" && config && Object.keys(config).length) {
|
|
109
|
+
request.intelligence = intelligenceFor(config);
|
|
110
|
+
}
|
|
111
|
+
return compact(request);
|
|
112
|
+
}
|
|
113
|
+
/** A file in the new app model; `agent` (inline) makes it an Autopilot agent. */
|
|
114
|
+
function fileToRequest(raw) {
|
|
115
|
+
var _a, _b;
|
|
116
|
+
const _c = raw, { agent } = _c, rest = __rest(_c, ["agent"]);
|
|
117
|
+
const request = Object.assign({}, rest);
|
|
118
|
+
// Test cases belong to applications:eval, not to the app.
|
|
119
|
+
delete request.testCases;
|
|
120
|
+
if (agent) {
|
|
121
|
+
request.type = "AUTOPILOT";
|
|
122
|
+
request.intelligence = intelligenceFor(agent);
|
|
123
|
+
}
|
|
124
|
+
// Never send a model key that sits in an agent file; keys live in the vendor keys.
|
|
125
|
+
const lm = (_b = (_a = request.intelligence) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.languageModel;
|
|
126
|
+
if (lm)
|
|
127
|
+
delete lm.apiKey;
|
|
128
|
+
return request;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Applies command flags on top of `base` (the current app on update, `{}` on create).
|
|
132
|
+
*/
|
|
133
|
+
function applyFlags(base, flags) {
|
|
134
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
135
|
+
const request = Object.assign(Object.assign({}, base), { speech: Object.assign({}, ((_a = base.speech) !== null && _a !== void 0 ? _a : {})), listeners: [...((_b = base.listeners) !== null && _b !== void 0 ? _b : [])] });
|
|
136
|
+
if (flags.name !== undefined)
|
|
137
|
+
request.name = flags.name;
|
|
138
|
+
if (flags.agent) {
|
|
139
|
+
const agent = readConfigFile(flags.agent);
|
|
140
|
+
request.type = "AUTOPILOT";
|
|
141
|
+
request.intelligence = fileToRequest({ agent }).intelligence;
|
|
142
|
+
}
|
|
143
|
+
if (flags.url !== undefined)
|
|
144
|
+
request.url = flags.url;
|
|
145
|
+
if (flags.protocol !== undefined)
|
|
146
|
+
request.protocol = flags.protocol;
|
|
147
|
+
const speech = request.speech;
|
|
148
|
+
if (flags.voice !== undefined)
|
|
149
|
+
speech.voice = flags.voice;
|
|
150
|
+
if (flags.language !== undefined)
|
|
151
|
+
speech.language = flags.language;
|
|
152
|
+
if (flags["stt-model"] !== undefined)
|
|
153
|
+
speech.sttModel = flags["stt-model"];
|
|
154
|
+
if (flags["tts-model"] !== undefined)
|
|
155
|
+
speech.ttsModel = flags["tts-model"];
|
|
156
|
+
if (flags["silence-timeout"] !== undefined)
|
|
157
|
+
speech.silenceTimeoutMs = flags["silence-timeout"];
|
|
158
|
+
if (flags.patience !== undefined)
|
|
159
|
+
speech.patience = flags.patience;
|
|
160
|
+
if (flags["audio-codec"] !== undefined || flags["audio-rate"] !== undefined) {
|
|
161
|
+
const codec = ((_e = (_c = flags["audio-codec"]) !== null && _c !== void 0 ? _c : (_d = request.audio) === null || _d === void 0 ? void 0 : _d.codec) !== null && _e !== void 0 ? _e : "pcm16");
|
|
162
|
+
const rate = (_f = flags["audio-rate"]) !== null && _f !== void 0 ? _f : (codec === "pcmu" ? 8000 : ((_h = (_g = request.audio) === null || _g === void 0 ? void 0 : _g.rate) !== null && _h !== void 0 ? _h : 16000));
|
|
163
|
+
request.audio = { codec, rate };
|
|
164
|
+
}
|
|
165
|
+
if (flags["fallback-app"] !== undefined)
|
|
166
|
+
request.fallbackAppRef = flags["fallback-app"];
|
|
167
|
+
if (flags["fallback-message"] !== undefined)
|
|
168
|
+
request.fallbackMessage = flags["fallback-message"];
|
|
169
|
+
if (flags["clear-listeners"])
|
|
170
|
+
request.listeners = [];
|
|
171
|
+
if ((_j = flags.listener) === null || _j === void 0 ? void 0 : _j.length) {
|
|
172
|
+
request.listeners = [
|
|
173
|
+
...((_k = request.listeners) !== null && _k !== void 0 ? _k : []),
|
|
174
|
+
...flags.listener.map(parseListener)
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
if (flags.recording !== undefined)
|
|
178
|
+
request.recording = flags.recording;
|
|
179
|
+
if (flags.amd === false)
|
|
180
|
+
request.amd = undefined;
|
|
181
|
+
else if (flags.amd || flags["amd-window"] !== undefined) {
|
|
182
|
+
const windowMs = (_l = flags["amd-window"]) !== null && _l !== void 0 ? _l : (_m = request.amd) === null || _m === void 0 ? void 0 : _m.windowMs;
|
|
183
|
+
request.amd = windowMs ? { enabled: true, windowMs } : { enabled: true };
|
|
184
|
+
}
|
|
185
|
+
if (flags["noise-cancelling"] !== undefined)
|
|
186
|
+
request.filters = noiseCancellingFilters(flags["noise-cancelling"]);
|
|
187
|
+
if (!request.type)
|
|
188
|
+
request.type = "STREAM";
|
|
189
|
+
return request;
|
|
190
|
+
}
|
|
191
|
+
/** `--noise-cancelling none|<nc provider>` as the app's filters (one filter per provider). */
|
|
192
|
+
function noiseCancellingFilters(value) {
|
|
193
|
+
const provider = value.trim().toLowerCase();
|
|
194
|
+
if (!provider || provider === "none" || provider === "off")
|
|
195
|
+
return [];
|
|
196
|
+
const name = common_1.AudioFilters.FILTER_BY_PROVIDER[provider];
|
|
197
|
+
if (!name)
|
|
198
|
+
throw new Error(`unknown noise-cancelling provider "${value}" (use none or ${Object.keys(common_1.AudioFilters.FILTER_BY_PROVIDER).join(", ")})`);
|
|
199
|
+
return [{ name }];
|
|
200
|
+
}
|
|
201
|
+
/** Throws the errors the control plane would, before a round trip. */
|
|
202
|
+
function checkRequest(request) {
|
|
203
|
+
var _a;
|
|
204
|
+
if (!request.name)
|
|
205
|
+
throw new Error("a name is required (--name)");
|
|
206
|
+
if (request.type === "AUTOPILOT") {
|
|
207
|
+
if (!((_a = request.intelligence) === null || _a === void 0 ? void 0 : _a.config))
|
|
208
|
+
throw new Error("an Autopilot agent needs an agent file (--agent)");
|
|
209
|
+
if (request.protocol && request.protocol !== "fonoster")
|
|
210
|
+
throw new Error("Autopilot agents use the fonoster protocol");
|
|
211
|
+
}
|
|
212
|
+
else if (!request.url) {
|
|
213
|
+
throw new Error("a url is required (--url ws:// or wss://)");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -1,2 +1,27 @@
|
|
|
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
|
+
*/
|
|
1
19
|
import * as SDK from "@fonoster/sdk";
|
|
2
|
-
|
|
20
|
+
import { CreateApplicationResponse } from "@fonoster/types";
|
|
21
|
+
import { ApplicationFlags } from "./applicationRequest";
|
|
22
|
+
/**
|
|
23
|
+
* Creates an app, or updates one, from an optional YAML/JSON file in the app model plus flags.
|
|
24
|
+
* An update starts from the current app (the control plane replaces the whole app), then the
|
|
25
|
+
* file, then the flags.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createOrUpdateApplication(client: SDK.Client, filePath?: string, appRef?: string, isUpdate?: boolean, flags?: ApplicationFlags): Promise<CreateApplicationResponse>;
|