@fonoster/ctl 0.27.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +586 -88
  2. package/dist/AuthenticatedCommand.d.ts +5 -0
  3. package/dist/AuthenticatedCommand.js +12 -1
  4. package/dist/commands/applications/create.d.ts +23 -0
  5. package/dist/commands/applications/create.js +68 -169
  6. package/dist/commands/applications/eval.d.ts +1 -1
  7. package/dist/commands/applications/eval.js +46 -33
  8. package/dist/commands/applications/get.d.ts +2 -0
  9. package/dist/commands/applications/get.js +55 -24
  10. package/dist/commands/applications/list.js +19 -3
  11. package/dist/commands/applications/rotate-secret.d.ts +9 -0
  12. package/dist/commands/applications/rotate-secret.js +97 -0
  13. package/dist/commands/applications/update.d.ts +22 -1
  14. package/dist/commands/applications/update.js +17 -168
  15. package/dist/commands/providers/catalog.d.ts +10 -0
  16. package/dist/commands/providers/catalog.js +128 -0
  17. package/dist/commands/providers/keys/delete.d.ts +9 -0
  18. package/dist/commands/providers/keys/delete.js +92 -0
  19. package/dist/commands/providers/keys/list.d.ts +6 -0
  20. package/dist/commands/providers/keys/list.js +97 -0
  21. package/dist/commands/providers/keys/set.d.ts +12 -0
  22. package/dist/commands/providers/keys/set.js +110 -0
  23. package/dist/commands/records/download.d.ts +15 -0
  24. package/dist/commands/records/download.js +132 -0
  25. package/dist/commands/records/get.d.ts +15 -0
  26. package/dist/commands/records/get.js +146 -0
  27. package/dist/commands/records/list.d.ts +15 -0
  28. package/dist/commands/records/list.js +136 -0
  29. package/dist/commands/sipnet/calls/create.d.ts +3 -3
  30. package/dist/commands/sipnet/calls/create.js +35 -52
  31. package/dist/commands/webhooks/create.d.ts +10 -0
  32. package/dist/commands/webhooks/create.js +104 -0
  33. package/dist/commands/webhooks/delete.d.ts +9 -0
  34. package/dist/commands/webhooks/delete.js +92 -0
  35. package/dist/commands/webhooks/deliveries.d.ts +12 -0
  36. package/dist/commands/webhooks/deliveries.js +119 -0
  37. package/dist/commands/webhooks/get.d.ts +9 -0
  38. package/dist/commands/webhooks/get.js +110 -0
  39. package/dist/commands/webhooks/list.d.ts +9 -0
  40. package/dist/commands/webhooks/list.js +104 -0
  41. package/dist/commands/webhooks/resend.d.ts +9 -0
  42. package/dist/commands/webhooks/resend.js +95 -0
  43. package/dist/commands/webhooks/rotate.d.ts +9 -0
  44. package/dist/commands/webhooks/rotate.js +98 -0
  45. package/dist/commands/webhooks/update.d.ts +13 -0
  46. package/dist/commands/webhooks/update.js +103 -0
  47. package/dist/commands/workspaces/login.d.ts +5 -0
  48. package/dist/commands/workspaces/login.js +27 -1
  49. package/dist/utils/applicationFlags.d.ts +25 -0
  50. package/dist/utils/applicationFlags.js +95 -0
  51. package/dist/utils/applicationRequest.d.ts +43 -0
  52. package/dist/utils/applicationRequest.js +209 -0
  53. package/dist/utils/createOrUpdateApplication.d.ts +26 -1
  54. package/dist/utils/createOrUpdateApplication.js +23 -27
  55. package/dist/utils/format.d.ts +20 -0
  56. package/dist/utils/format.js +31 -0
  57. package/dist/utils/index.d.ts +1 -0
  58. package/dist/utils/index.js +1 -0
  59. package/dist/utils/trackCallStatus.d.ts +4 -0
  60. package/dist/utils/trackCallStatus.js +67 -0
  61. package/dist/utils/webhookFlags.d.ts +3 -0
  62. package/dist/utils/webhookFlags.js +34 -0
  63. package/package.json +6 -6
@@ -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 `assistant` 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
+ // Renamed in 0.30; kept hidden so old scripts get a clear error.
43
+ flavor: core_1.Flags.string({ hidden: true }),
44
+ assistant: core_1.Flags.string({ hidden: true }),
45
+ voice: core_1.Flags.string({
46
+ description: "TTS voice as a catalog id (e.g. deepgram/aura-2-thalia-en)"
47
+ }),
48
+ language: core_1.Flags.string({ description: "speech language (e.g. en-US)" }),
49
+ "stt-model": core_1.Flags.string({
50
+ description: "STT model as a catalog id (e.g. deepgram/nova-3)"
51
+ }),
52
+ "tts-model": core_1.Flags.string({
53
+ description: "TTS model as a catalog id (e.g. elevenlabs/eleven_flash_v2_5)"
54
+ }),
55
+ "silence-timeout": core_1.Flags.integer({
56
+ description: "milliseconds of caller silence before a silence event"
57
+ }),
58
+ patience: core_1.Flags.string({
59
+ description: "how long to wait before taking the turn",
60
+ options: ["low", "normal", "high"]
61
+ }),
62
+ "audio-codec": core_1.Flags.string({
63
+ description: "audio frames for fonoster-protocol streams",
64
+ options: ["pcm16", "pcmu"]
65
+ }),
66
+ "audio-rate": core_1.Flags.integer({
67
+ description: "audio sample rate for fonoster-protocol streams: 8000, 16000 or 24000 (pcmu is 8000 only)"
68
+ }),
69
+ "fallback-app": core_1.Flags.string({
70
+ description: "the Application to hand the call to when this one fails"
71
+ }),
72
+ "fallback-message": core_1.Flags.string({
73
+ description: "what the caller hears when the app fails and no fallback app answers"
74
+ }),
75
+ listener: core_1.Flags.string({
76
+ char: "l",
77
+ description: "a listen-only socket: <wss-url>[,inbound|outbound|both] (repeatable, at most 4)",
78
+ multiple: true
79
+ }),
80
+ "clear-listeners": core_1.Flags.boolean({
81
+ description: "remove the current listeners (update; combine with --listener to replace)",
82
+ default: false
83
+ }),
84
+ recording: core_1.Flags.string({
85
+ description: "the recording policy",
86
+ options: ["off", "on", "consent"]
87
+ }),
88
+ amd: core_1.Flags.boolean({
89
+ description: "answering-machine detection: the app gets one amd.result per call (--no-amd turns it off)",
90
+ allowNo: true
91
+ }),
92
+ "amd-window": core_1.Flags.integer({
93
+ description: "milliseconds of caller audio AMD classifies (500 to 15000, default 4000); implies --amd"
94
+ })
95
+ };
@@ -0,0 +1,43 @@
1
+ import { Application, AppListener, CreateApplicationRequest } from "@fonoster/types";
2
+ type ApplicationFlags = {
3
+ name?: string;
4
+ url?: string;
5
+ protocol?: string;
6
+ agent?: string;
7
+ /** Renamed flags (0.30): rejected with a clear error. */
8
+ flavor?: string;
9
+ assistant?: string;
10
+ voice?: string;
11
+ language?: string;
12
+ "stt-model"?: string;
13
+ "tts-model"?: string;
14
+ "silence-timeout"?: number;
15
+ patience?: string;
16
+ "audio-codec"?: string;
17
+ "audio-rate"?: number;
18
+ "fallback-app"?: string;
19
+ "fallback-message"?: string;
20
+ listener?: string[];
21
+ "clear-listeners"?: boolean;
22
+ recording?: string;
23
+ amd?: boolean;
24
+ "amd-window"?: number;
25
+ };
26
+ type AppRequest = CreateApplicationRequest & {
27
+ ref?: string;
28
+ };
29
+ /** Reads a YAML or JSON file into an object. */
30
+ declare function readConfigFile<T = Record<string, unknown>>(filePath: string): T;
31
+ /** `wss://host/path` or `wss://host/path,inbound` (tracks: inbound, outbound or both). */
32
+ declare function parseListener(value: string): AppListener;
33
+ /** An existing app as a create/update request (blank fields dropped). */
34
+ declare function applicationToRequest(app: Application): AppRequest;
35
+ /** A file in the new app model; `assistant` (inline) makes it an Autopilot agent. */
36
+ declare function fileToRequest(raw: Record<string, unknown>): AppRequest;
37
+ /**
38
+ * Applies command flags on top of `base` (the current app on update, `{}` on create).
39
+ */
40
+ declare function applyFlags(base: AppRequest, flags: ApplicationFlags): AppRequest;
41
+ /** Throws the errors the control plane would, before a round trip. */
42
+ declare function checkRequest(request: AppRequest): void;
43
+ export { AppRequest, ApplicationFlags, applicationToRequest, applyFlags, checkRequest, fileToRequest, parseListener, readConfigFile };
@@ -0,0 +1,209 @@
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 js_yaml_1 = require("js-yaml");
44
+ const TRACKS = ["inbound", "outbound", "both"];
45
+ /** Reads a YAML or JSON file into an object. */
46
+ function readConfigFile(filePath) {
47
+ const content = (0, fs_1.readFileSync)(filePath, "utf8");
48
+ const ext = path_1.default.extname(filePath).toLowerCase();
49
+ if (ext === ".yaml" || ext === ".yml")
50
+ return (0, js_yaml_1.load)(content);
51
+ if (ext === ".json")
52
+ return JSON.parse(content);
53
+ throw new Error("Unsupported file format. Please use YAML or JSON files.");
54
+ }
55
+ /** `wss://host/path` or `wss://host/path,inbound` (tracks: inbound, outbound or both). */
56
+ function parseListener(value) {
57
+ const comma = value.lastIndexOf(",");
58
+ const tail = comma > 0 ? value.slice(comma + 1).trim() : "";
59
+ if (comma > 0 && TRACKS.includes(tail)) {
60
+ return {
61
+ url: value.slice(0, comma).trim(),
62
+ tracks: tail
63
+ };
64
+ }
65
+ if (comma > 0 && tail && !tail.includes("/")) {
66
+ throw new Error(`invalid listener tracks "${tail}" (use inbound, outbound or both)`);
67
+ }
68
+ return { url: value.trim(), tracks: "both" };
69
+ }
70
+ function blank(value) {
71
+ return value === "" || value === 0 || value === null || value === undefined;
72
+ }
73
+ function compact(obj) {
74
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => !blank(v)));
75
+ }
76
+ /** The LLM product the control plane files the assistant under (`llm.<provider>`). */
77
+ function intelligenceFor(assistant) {
78
+ var _a;
79
+ const model = (_a = assistant.languageModel) === null || _a === void 0 ? void 0 : _a.model;
80
+ return {
81
+ productRef: (model === null || model === void 0 ? void 0 : model.includes("/")) ? `llm.${model.split("/")[0]}` : "",
82
+ config: assistant
83
+ };
84
+ }
85
+ /** An existing app as a create/update request (blank fields dropped). */
86
+ function applicationToRequest(app) {
87
+ var _a, _b, _c, _d, _e;
88
+ const request = {
89
+ name: app.name,
90
+ type: app.type,
91
+ protocol: (app.protocol || undefined),
92
+ speech: compact(Object.assign({}, ((_a = app.speech) !== null && _a !== void 0 ? _a : {}))),
93
+ audio: ((_b = app.audio) === null || _b === void 0 ? void 0 : _b.codec) ? app.audio : undefined,
94
+ fallbackAppRef: app.fallbackAppRef || undefined,
95
+ fallbackMessage: app.fallbackMessage || undefined,
96
+ listeners: (_c = app.listeners) !== null && _c !== void 0 ? _c : [],
97
+ recording: (app.recording || undefined),
98
+ amd: ((_d = app.amd) === null || _d === void 0 ? void 0 : _d.enabled)
99
+ ? compact({ enabled: true, windowMs: app.amd.windowMs })
100
+ : undefined
101
+ };
102
+ // Autopilot apps follow the operator's hosted Autopilot URL; only streams carry theirs.
103
+ if (app.type !== "AUTOPILOT")
104
+ request.url = app.url || app.endpoint;
105
+ const config = (_e = app.intelligence) === null || _e === void 0 ? void 0 : _e.config;
106
+ if (app.type === "AUTOPILOT" && config && Object.keys(config).length) {
107
+ request.intelligence = intelligenceFor(config);
108
+ }
109
+ return compact(request);
110
+ }
111
+ /** A file in the new app model; `assistant` (inline) makes it an Autopilot agent. */
112
+ function fileToRequest(raw) {
113
+ var _a, _b;
114
+ if (raw.flavor !== undefined)
115
+ throw new Error("`flavor` was renamed to `protocol` (fonoster or twilio; native is now fonoster)");
116
+ if (raw.type === "EXTERNAL")
117
+ throw new Error("type EXTERNAL was renamed to STREAM");
118
+ const _c = raw, { assistant } = _c, rest = __rest(_c, ["assistant"]);
119
+ const request = Object.assign({}, rest);
120
+ // Test cases belong to applications:eval, not to the app.
121
+ delete request.testCases;
122
+ if (assistant) {
123
+ request.type = "AUTOPILOT";
124
+ request.intelligence = intelligenceFor(assistant);
125
+ }
126
+ // Never send a model key that sits in an agent file; keys live in the vendor keys.
127
+ const lm = (_b = (_a = request.intelligence) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.languageModel;
128
+ if (lm)
129
+ delete lm.apiKey;
130
+ return request;
131
+ }
132
+ /**
133
+ * Applies command flags on top of `base` (the current app on update, `{}` on create).
134
+ */
135
+ function applyFlags(base, flags) {
136
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
137
+ 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 : [])] });
138
+ if (flags.assistant !== undefined)
139
+ throw new Error("--assistant was renamed to --agent");
140
+ if (flags.flavor !== undefined)
141
+ throw new Error("--flavor was renamed to --protocol (fonoster or twilio; native is now fonoster)");
142
+ if (flags.name !== undefined)
143
+ request.name = flags.name;
144
+ if (flags.agent) {
145
+ const assistant = readConfigFile(flags.agent);
146
+ request.type = "AUTOPILOT";
147
+ request.intelligence = fileToRequest({ assistant }).intelligence;
148
+ }
149
+ if (flags.url !== undefined)
150
+ request.url = flags.url;
151
+ if (flags.protocol !== undefined)
152
+ request.protocol = flags.protocol;
153
+ const speech = request.speech;
154
+ if (flags.voice !== undefined)
155
+ speech.voice = flags.voice;
156
+ if (flags.language !== undefined)
157
+ speech.language = flags.language;
158
+ if (flags["stt-model"] !== undefined)
159
+ speech.sttModel = flags["stt-model"];
160
+ if (flags["tts-model"] !== undefined)
161
+ speech.ttsModel = flags["tts-model"];
162
+ if (flags["silence-timeout"] !== undefined)
163
+ speech.silenceTimeoutMs = flags["silence-timeout"];
164
+ if (flags.patience !== undefined)
165
+ speech.patience = flags.patience;
166
+ if (flags["audio-codec"] !== undefined || flags["audio-rate"] !== undefined) {
167
+ 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");
168
+ 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));
169
+ request.audio = { codec, rate };
170
+ }
171
+ if (flags["fallback-app"] !== undefined)
172
+ request.fallbackAppRef = flags["fallback-app"];
173
+ if (flags["fallback-message"] !== undefined)
174
+ request.fallbackMessage = flags["fallback-message"];
175
+ if (flags["clear-listeners"])
176
+ request.listeners = [];
177
+ if ((_j = flags.listener) === null || _j === void 0 ? void 0 : _j.length) {
178
+ request.listeners = [
179
+ ...((_k = request.listeners) !== null && _k !== void 0 ? _k : []),
180
+ ...flags.listener.map(parseListener)
181
+ ];
182
+ }
183
+ if (flags.recording !== undefined)
184
+ request.recording = flags.recording;
185
+ if (flags.amd === false)
186
+ request.amd = undefined;
187
+ else if (flags.amd || flags["amd-window"] !== undefined) {
188
+ const windowMs = (_l = flags["amd-window"]) !== null && _l !== void 0 ? _l : (_m = request.amd) === null || _m === void 0 ? void 0 : _m.windowMs;
189
+ request.amd = windowMs ? { enabled: true, windowMs } : { enabled: true };
190
+ }
191
+ if (!request.type)
192
+ request.type = "STREAM";
193
+ return request;
194
+ }
195
+ /** Throws the errors the control plane would, before a round trip. */
196
+ function checkRequest(request) {
197
+ var _a;
198
+ if (!request.name)
199
+ throw new Error("a name is required (--name)");
200
+ if (request.type === "AUTOPILOT") {
201
+ if (!((_a = request.intelligence) === null || _a === void 0 ? void 0 : _a.config))
202
+ throw new Error("an Autopilot agent needs an agent file (--agent)");
203
+ if (request.protocol && request.protocol !== "fonoster")
204
+ throw new Error("Autopilot agents use the fonoster protocol");
205
+ }
206
+ else if (!request.url) {
207
+ throw new Error("a url is required (--url ws:// or wss://)");
208
+ }
209
+ }
@@ -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
- export declare function createOrUpdateApplication(client: SDK.Client, filePath: string, appRef?: string, isUpdate?: boolean): Promise<import("@fonoster/types").BaseApiObject>;
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>;
@@ -41,9 +41,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
41
41
  step((generator = generator.apply(thisArg, _arguments || [])).next());
42
42
  });
43
43
  };
44
- var __importDefault = (this && this.__importDefault) || function (mod) {
45
- return (mod && mod.__esModule) ? mod : { "default": mod };
46
- };
47
44
  Object.defineProperty(exports, "__esModule", { value: true });
48
45
  exports.createOrUpdateApplication = createOrUpdateApplication;
49
46
  /**
@@ -64,39 +61,38 @@ exports.createOrUpdateApplication = createOrUpdateApplication;
64
61
  * See the License for the specific language governing permissions and
65
62
  * limitations under the License.
66
63
  */
67
- const fs_1 = require("fs");
68
- const path_1 = __importDefault(require("path"));
69
64
  const SDK = __importStar(require("@fonoster/sdk"));
70
- const js_yaml_1 = require("js-yaml");
65
+ const applicationRequest_1 = require("./applicationRequest");
66
+ /**
67
+ * Creates an app, or updates one, from an optional YAML/JSON file in the app model plus flags.
68
+ * An update starts from the current app (the control plane replaces the whole app), then the
69
+ * file, then the flags.
70
+ */
71
71
  function createOrUpdateApplication(client_1, filePath_1, appRef_1) {
72
- return __awaiter(this, arguments, void 0, function* (client, filePath, appRef, isUpdate = false) {
73
- var _a, _b, _c;
74
- const fileContent = (0, fs_1.readFileSync)(filePath, "utf8");
75
- const fileExt = path_1.default.extname(filePath).toLowerCase();
76
- let config;
77
- if (fileExt === ".yaml" || fileExt === ".yml") {
78
- config = (0, js_yaml_1.load)(fileContent);
79
- }
80
- else if (fileExt === ".json") {
81
- config = JSON.parse(fileContent);
82
- }
83
- else {
84
- throw new Error("Unsupported file format. Please use YAML or JSON files.");
85
- }
72
+ return __awaiter(this, arguments, void 0, function* (client, filePath, appRef, isUpdate = false, flags = {}) {
73
+ var _a, _b;
74
+ const fromFile = filePath
75
+ ? (0, applicationRequest_1.fileToRequest)((0, applicationRequest_1.readConfigFile)(filePath))
76
+ : {};
86
77
  const applications = new SDK.Applications(client);
87
- delete config.testCases;
88
- (_c = (_b = (_a = config.intelligence) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.languageModel) === null || _c === void 0 ? true : delete _c.apiKey;
89
78
  if (isUpdate) {
90
79
  // The positional appRef takes precedence over any `ref` in the file; fall
91
80
  // back to the file's `ref` when the positional is omitted.
92
- const ref = appRef !== null && appRef !== void 0 ? appRef : config.ref;
81
+ const ref = appRef !== null && appRef !== void 0 ? appRef : fromFile.ref;
93
82
  if (!ref) {
94
83
  throw new Error("An Application ref is required to update. Provide it as the positional argument or include `ref` in the file.");
95
84
  }
96
- delete config.ref;
97
- const updateConfig = Object.assign(Object.assign({}, config), { ref });
98
- return applications.updateApplication(updateConfig);
85
+ const current = (0, applicationRequest_1.applicationToRequest)(yield applications.getApplication(ref));
86
+ const merged = Object.assign(Object.assign(Object.assign({}, current), fromFile), { speech: Object.assign(Object.assign({}, ((_a = current.speech) !== null && _a !== void 0 ? _a : {})), ((_b = fromFile.speech) !== null && _b !== void 0 ? _b : {})) });
87
+ delete merged.ref;
88
+ const request = (0, applicationRequest_1.applyFlags)(merged, flags);
89
+ (0, applicationRequest_1.checkRequest)(request);
90
+ const updateRequest = Object.assign(Object.assign({}, request), { ref });
91
+ return applications.updateApplication(updateRequest);
99
92
  }
100
- return applications.createApplication(config);
93
+ delete fromFile.ref;
94
+ const request = (0, applicationRequest_1.applyFlags)(fromFile, flags);
95
+ (0, applicationRequest_1.checkRequest)(request);
96
+ return applications.createApplication(request);
101
97
  });
102
98
  }
@@ -0,0 +1,20 @@
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
+ /** ISO time without milliseconds, or "-" when absent. */
20
+ export declare const formatDate: (value?: Date | string | null) => string;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDate = 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
+ /** ISO time without milliseconds, or "-" when absent. */
23
+ const formatDate = (value) => {
24
+ if (!value)
25
+ return "-";
26
+ const date = new Date(value);
27
+ return isNaN(date.getTime())
28
+ ? "-"
29
+ : date.toISOString().replace(/\.\d{3}Z$/, "Z");
30
+ };
31
+ exports.formatDate = formatDate;
@@ -26,3 +26,4 @@ export * from "./getTwilioTrunk";
26
26
  export * from "./linkTwilioNumberToApplication";
27
27
  export * from "./types";
28
28
  export * from "./createOrUpdateApplication";
29
+ export * from "./applicationRequest";
@@ -42,3 +42,4 @@ __exportStar(require("./getTwilioTrunk"), exports);
42
42
  __exportStar(require("./linkTwilioNumberToApplication"), exports);
43
43
  __exportStar(require("./types"), exports);
44
44
  __exportStar(require("./createOrUpdateApplication"), exports);
45
+ __exportStar(require("./applicationRequest"), exports);
@@ -0,0 +1,4 @@
1
+ /** Prints each dial status until a final one (or the stream ends); returns the last status. */
2
+ export declare function trackCallStatus(statusStream: AsyncIterable<{
3
+ status: string;
4
+ }>, log: (message: string) => void): Promise<string | undefined>;
@@ -0,0 +1,67 @@
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
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.trackCallStatus = trackCallStatus;
20
+ /**
21
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
22
+ * http://github.com/fonoster/fonoster
23
+ *
24
+ * This file is part of Fonoster
25
+ *
26
+ * Licensed under the MIT License (the "License");
27
+ * you may not use this file except in compliance with
28
+ * the License. You may obtain a copy of the License at
29
+ *
30
+ * https://opensource.org/licenses/MIT
31
+ *
32
+ * Unless required by applicable law or agreed to in writing, software
33
+ * distributed under the License is distributed on an "AS IS" BASIS,
34
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35
+ * See the License for the specific language governing permissions and
36
+ * limitations under the License.
37
+ */
38
+ // The dial ends at one of these; the stream may stay open a moment after (the server closes it).
39
+ const FINAL = ["ANSWER", "BUSY", "CANCEL", "FAILED", "NOANSWER", "ERROR"];
40
+ /** Prints each dial status until a final one (or the stream ends); returns the last status. */
41
+ function trackCallStatus(statusStream, log) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ var _a, statusStream_1, statusStream_1_1;
44
+ var _b, e_1, _c, _d;
45
+ let last;
46
+ try {
47
+ // eslint-disable-next-line no-loops/no-loops
48
+ for (_a = true, statusStream_1 = __asyncValues(statusStream); statusStream_1_1 = yield statusStream_1.next(), _b = statusStream_1_1.done, !_b; _a = true) {
49
+ _d = statusStream_1_1.value;
50
+ _a = false;
51
+ const entry = _d;
52
+ last = entry.status;
53
+ log(`Call status: ${entry.status}`);
54
+ if (FINAL.includes(entry.status))
55
+ break;
56
+ }
57
+ }
58
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
59
+ finally {
60
+ try {
61
+ if (!_a && !_b && (_c = statusStream_1.return)) yield _c.call(statusStream_1);
62
+ }
63
+ finally { if (e_1) throw e_1.error; }
64
+ }
65
+ return last;
66
+ });
67
+ }
@@ -0,0 +1,3 @@
1
+ export declare const WEBHOOK_EVENTS: string[];
2
+ export declare const eventFlag: (required: boolean) => import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
3
+ export { formatDate } from "./format";
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDate = exports.eventFlag = exports.WEBHOOK_EVENTS = 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 common_1 = require("@fonoster/common");
23
+ const core_1 = require("@oclif/core");
24
+ exports.WEBHOOK_EVENTS = [...common_1.Webhooks.WEBHOOK_EVENT_TYPES];
25
+ const eventFlag = (required) => core_1.Flags.string({
26
+ char: "e",
27
+ description: "an event to receive (repeatable)",
28
+ options: exports.WEBHOOK_EVENTS,
29
+ multiple: true,
30
+ required
31
+ });
32
+ exports.eventFlag = eventFlag;
33
+ var format_1 = require("./format");
34
+ Object.defineProperty(exports, "formatDate", { enumerable: true, get: function () { return format_1.formatDate; } });