conduyt 1.55.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.
Files changed (2) hide show
  1. package/dist/index.js +100 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1096,10 +1096,22 @@ const bookingPageBody = (opts) => {
1096
1096
  set("brandName", opts.brandName);
1097
1097
  if (typeof opts.embedOrigins === "string")
1098
1098
  set("embedAllowedOrigins", opts.embedOrigins.split(",").map((o) => o.trim()).filter(Boolean));
1099
+ if (opts.documents !== undefined)
1100
+ set("documentsEnabled", opts.documents === "on" || opts.documents === true);
1101
+ if (typeof opts.documentsPageUrl === "string")
1102
+ set("documentsPageUrl", opts.documentsPageUrl === "none" ? null : opts.documentsPageUrl);
1099
1103
  if (typeof opts.availability === "string")
1100
1104
  set("availabilityRules", JSON.parse(opts.availability));
1101
1105
  if (opts.active !== undefined)
1102
1106
  set("isActive", opts.active);
1107
+ // (Codex rb-r9 m3) every other field the route accepts (the OpenAPI BookingPagePatch schema) through --data; the typed
1108
+ // options above win over the same key in it
1109
+ if (typeof opts.data === "string") {
1110
+ const extra = JSON.parse(opts.data);
1111
+ if (!extra || typeof extra !== "object" || Array.isArray(extra))
1112
+ throw new Error("--data must be a JSON object");
1113
+ return { ...extra, ...body };
1114
+ }
1103
1115
  return body;
1104
1116
  };
1105
1117
  const bookingPageOptions = (cmd) => cmd
@@ -1107,6 +1119,8 @@ const bookingPageOptions = (cmd) => cmd
1107
1119
  .option("--slug <slug>", "public slug (conduyt.app/book/<slug>)")
1108
1120
  .option("--description <text>", "description shown on the page")
1109
1121
  .option("--host <userId>", "host user UUID (the page owner)")
1122
+ .option("--documents <on|off>", "the documents step after the booking (bank statements and funding agreements attached to the lead)")
1123
+ .option("--documents-page-url <url>", "the site page the confirmation email links back to for the upload (https; reads ?appointment=<id>); 'none' clears it")
1110
1124
  .option("--duration <minutes>", "slot length in minutes")
1111
1125
  .option("--minimum-notice <minutes>", "earliest booking, minutes from now")
1112
1126
  .option("--max-advance-days <days>", "how far ahead visitors may book")
@@ -1118,7 +1132,8 @@ const bookingPageOptions = (cmd) => cmd
1118
1132
  .option("--embed-origins <origins>", "comma-separated origins allowed to embed/call the page (empty = any)")
1119
1133
  .option("--availability <json>", "AvailabilityWindow[] JSON override, e.g. '[{\"dayOfWeek\":1,\"startTime\":\"09:00\",\"endTime\":\"11:00\"}]'")
1120
1134
  .option("--active", "page accepts bookings")
1121
- .option("--no-active", "page stops accepting bookings");
1135
+ .option("--no-active", "page stops accepting bookings")
1136
+ .option("--data <json>", "any other page field as a JSON object (the OpenAPI BookingPagePatch schema: eventType, hostUserIds, reminders, formFields, bufferBefore, color, redirectUrl, gdprEnabled, waitlistEnabled, template, …)");
1122
1137
  bookingPageOptions(bookingPages.command("create").description("Create a booking page (POST /booking-pages): --title, --slug, --host and --duration at least"))
1123
1138
  .action(run(async (client, opts) => {
1124
1139
  const body = bookingPageBody(opts);
@@ -2398,6 +2413,90 @@ function onOffArg(raw, flag) {
2398
2413
  return false;
2399
2414
  throw new Error(`${flag} takes on or off (got '${raw}').`);
2400
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
+ }));
2401
2500
  dialer
2402
2501
  .command("settings")
2403
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt",
3
- "version": "1.55.0",
3
+ "version": "1.57.0",
4
4
  "description": "Command-line interface for Conduyt CRM — manage contacts, deals, pipelines, and run insight queries from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",