conduyt 1.56.0 → 1.57.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/index.js +84 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2413,6 +2413,90 @@ function onOffArg(raw, flag) {
|
|
|
2413
2413
|
return false;
|
|
2414
2414
|
throw new Error(`${flag} takes on or off (got '${raw}').`);
|
|
2415
2415
|
}
|
|
2416
|
+
/** the two lines a workspace records calls on: its Twilio lines (settings.recording) and its iPhone line (settings.projectBlueRecording) */
|
|
2417
|
+
const RECORDING_KEY = { twilio: "recording", iphone: "projectBlueRecording" };
|
|
2418
|
+
const isDisclosureOn = (v) => v === "inbound" || v === "outbound" || v === "both";
|
|
2419
|
+
/** what a stored policy MEANS, read the way the server reads it: the old shape { enabled, consentGreeting } is outbound
|
|
2420
|
+
* recording with the disclosure on whenever a wording is set; enabled stays beside outbound as the old name of the switch */
|
|
2421
|
+
export function effectiveRecordingPolicy(raw) {
|
|
2422
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
2423
|
+
return null;
|
|
2424
|
+
const r = raw;
|
|
2425
|
+
const greeting = typeof r.consentGreeting === "string" ? r.consentGreeting.trim() : "";
|
|
2426
|
+
const newShape = typeof r.inbound === "boolean" || typeof r.outbound === "boolean" || (r.disclosure !== undefined && r.disclosure !== null);
|
|
2427
|
+
if (newShape) {
|
|
2428
|
+
const d = (r.disclosure && typeof r.disclosure === "object" ? r.disclosure : {});
|
|
2429
|
+
const outbound = typeof r.outbound === "boolean" ? r.outbound : r.enabled === true;
|
|
2430
|
+
return { inbound: r.inbound === true, outbound, disclosure: { enabled: d.enabled === true, on: isDisclosureOn(d.on) ? d.on : "outbound" }, consentGreeting: greeting, enabled: outbound };
|
|
2431
|
+
}
|
|
2432
|
+
const outbound = r.enabled === true;
|
|
2433
|
+
return { inbound: false, outbound, disclosure: { enabled: outbound && greeting.length > 0, on: "outbound" }, consentGreeting: greeting, enabled: outbound };
|
|
2434
|
+
}
|
|
2435
|
+
/** the iPhone line has no inbound calls: what its policy does there, the way the server applies it */
|
|
2436
|
+
export function iphoneRecordingDomain(policy) {
|
|
2437
|
+
return { ...policy, inbound: false, disclosure: policy.disclosure.on === "inbound" ? { enabled: false, on: "outbound" } : { enabled: policy.disclosure.enabled, on: "outbound" } };
|
|
2438
|
+
}
|
|
2439
|
+
dialer
|
|
2440
|
+
.command("recording")
|
|
2441
|
+
.description("Show the workspace's call recording policy for both kinds of line (GET /settings). twilio = its Twilio lines: always an object, with everything off when the workspace never set it. Needs a key whose creator may view settings; without it the command fails instead of showing a policy. iphone = the iPhone line's OWN policy (outbound only); null = none was written, and the iPhone line then follows the Twilio lines' policy without its inbound part. Each object is the policy as it is APPLIED: inbound, outbound, disclosure { enabled, on: inbound | outbound | both }, consentGreeting (what is said; empty = the default sentence) and enabled (the OLD name of the outbound switch: the same value as outbound). A workspace that never saved the new shape is read the way the server reads its old one: outbound recording with the disclosure on whenever a wording is set")
|
|
2442
|
+
.action(run(async (client) => {
|
|
2443
|
+
const result = (await client.get("/api/v1/settings"));
|
|
2444
|
+
const raw = result?.data ?? {};
|
|
2445
|
+
const twilio = effectiveRecordingPolicy(raw[RECORDING_KEY.twilio]);
|
|
2446
|
+
// a key whose creator may not view settings gets a short answer WITHOUT the policy: that is "not visible",
|
|
2447
|
+
// never "not configured" (the full answer always carries the Twilio policy)
|
|
2448
|
+
if (!twilio)
|
|
2449
|
+
throw new Error("The call recording policy is not visible to this API key: its creator needs permission to view settings. Nothing can be said about whether calls are recorded.");
|
|
2450
|
+
const iphone = effectiveRecordingPolicy(raw[RECORDING_KEY.iphone]);
|
|
2451
|
+
return { data: { twilio, iphone: iphone ? iphoneRecordingDomain(iphone) : null } };
|
|
2452
|
+
}));
|
|
2453
|
+
dialer
|
|
2454
|
+
.command("set-recording")
|
|
2455
|
+
.description("Change the workspace's call recording policy (admin: settings scope). Send only what changes; the rest stays. The workspace is responsible for consent where it operates. Example: conduyt dialer set-recording --inbound on --outbound on --disclosure on --disclosure-on inbound")
|
|
2456
|
+
.option("--line <line>", "which line's policy: twilio (default) or iphone")
|
|
2457
|
+
.option("--inbound <on|off>", "record calls the team receives (Twilio lines only)")
|
|
2458
|
+
.option("--outbound <on|off>", "record calls the team makes, including Smart Dialing")
|
|
2459
|
+
.option("--enabled <on|off>", "the OLD name of --outbound, accepted for older callers and sent as --outbound (when both are sent, --outbound decides); nothing else changes with it")
|
|
2460
|
+
.option("--disclosure <on|off>", "play a recording disclosure (send together with --disclosure-on)")
|
|
2461
|
+
.option("--disclosure-on <calls>", "which calls the disclosure plays on: inbound | outbound | both (send together with --disclosure). On the iPhone line: inbound is refused, both is saved as outbound")
|
|
2462
|
+
.option("--greeting <text>", "what is said (500 characters at most; an empty string = the default sentence)")
|
|
2463
|
+
.action(run(async (client, opts) => {
|
|
2464
|
+
const line = opts.line ?? "twilio";
|
|
2465
|
+
if (line !== "twilio" && line !== "iphone")
|
|
2466
|
+
throw new Error("--line must be twilio or iphone.");
|
|
2467
|
+
if ([opts.inbound, opts.outbound, opts.enabled, opts.disclosure, opts.disclosureOn, opts.greeting].every((v) => v === undefined)) {
|
|
2468
|
+
throw new Error("Nothing to change: pass at least one of --inbound, --outbound, --disclosure with --disclosure-on, --greeting.");
|
|
2469
|
+
}
|
|
2470
|
+
if ((opts.disclosure === undefined) !== (opts.disclosureOn === undefined)) {
|
|
2471
|
+
throw new Error("--disclosure and --disclosure-on are sent together: the disclosure is one setting (whether it plays, and on which calls).");
|
|
2472
|
+
}
|
|
2473
|
+
if (line === "iphone" && opts.inbound !== undefined)
|
|
2474
|
+
throw new Error("The iPhone line records outbound calls only: --inbound is not a setting of that line.");
|
|
2475
|
+
const policy = {};
|
|
2476
|
+
if (opts.inbound !== undefined)
|
|
2477
|
+
policy.inbound = onOffArg(opts.inbound, "--inbound");
|
|
2478
|
+
// (Codex r4) the old name is the outbound switch and nothing else: sent AS outbound, never as enabled (the server
|
|
2479
|
+
// gives an enabled-only write over a policy saved in the old shape its old whole meaning, disclosure included)
|
|
2480
|
+
if (opts.outbound !== undefined)
|
|
2481
|
+
policy.outbound = onOffArg(opts.outbound, "--outbound");
|
|
2482
|
+
else if (opts.enabled !== undefined)
|
|
2483
|
+
policy.outbound = onOffArg(opts.enabled, "--enabled");
|
|
2484
|
+
if (opts.disclosure !== undefined && opts.disclosureOn !== undefined) {
|
|
2485
|
+
if (!["inbound", "outbound", "both"].includes(opts.disclosureOn))
|
|
2486
|
+
throw new Error("--disclosure-on must be one of: inbound, outbound, both.");
|
|
2487
|
+
// the iPhone line has no inbound calls: a disclosure on inbound only would never play, so it is refused here
|
|
2488
|
+
// instead of saved as if it were on; both is what the line does on its outbound calls
|
|
2489
|
+
if (line === "iphone" && opts.disclosureOn === "inbound")
|
|
2490
|
+
throw new Error("The iPhone line has outbound calls only: a disclosure on inbound calls would never play. Use --disclosure-on outbound.");
|
|
2491
|
+
policy.disclosure = { enabled: onOffArg(opts.disclosure, "--disclosure"), on: line === "iphone" && opts.disclosureOn === "both" ? "outbound" : opts.disclosureOn };
|
|
2492
|
+
}
|
|
2493
|
+
if (opts.greeting !== undefined) {
|
|
2494
|
+
if (opts.greeting.trim().length > 500)
|
|
2495
|
+
throw new Error("--greeting may be at most 500 characters.");
|
|
2496
|
+
policy.consentGreeting = opts.greeting.trim();
|
|
2497
|
+
}
|
|
2498
|
+
return patchGeneralSettings(client, { [RECORDING_KEY[line]]: policy });
|
|
2499
|
+
}));
|
|
2416
2500
|
dialer
|
|
2417
2501
|
.command("settings")
|
|
2418
2502
|
.description("Show the account's Smart Dialing policy (GET /dialer/settings, dialer scope): autoAdvanceEnabled, pauseSeconds, advanceOn (disposition | timer), openLeadOnDial (the lead's record opens as each Smart Dialing call goes out), smartDialCallerId (local_presence = an area-code match from the number pool, the rep's own line when no pool exists; did = always the rep's own line), plus localPresenceConfigured.")
|
package/package.json
CHANGED