conduyt 1.44.0 → 1.45.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 +103 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -960,6 +960,109 @@ fromk
|
|
|
960
960
|
throw new Error("Pass --secret or set FROMK_WEBHOOK_SECRET (or --clear).");
|
|
961
961
|
return client.put("/api/v1/settings/fromk/webhook", { secret });
|
|
962
962
|
}));
|
|
963
|
+
// ---- Zoom (Conduyt #72, 2026-09-15): one Zoom meeting per booking ----
|
|
964
|
+
const zoom = program.command("zoom").description("Zoom: the account's Server-to-Server OAuth app; booking pages set to Zoom create one meeting per booking, delete it on cancel, move it on reschedule (settings scope)");
|
|
965
|
+
zoom
|
|
966
|
+
.command("status")
|
|
967
|
+
.description("Connection status (GET /settings/zoom): connected | not_configured, the account id and client id masked; the client secret is never returned")
|
|
968
|
+
.action(run(async (client) => client.get("/api/v1/settings/zoom")));
|
|
969
|
+
zoom
|
|
970
|
+
.command("connect")
|
|
971
|
+
.description("Connect or replace the Server-to-Server app (scopes meeting:write/read/update/delete:meeting:admin + user:read:user:admin) — verified with Zoom before it is stored; answers the app's own user = the default meeting host")
|
|
972
|
+
.option("--account-id <id>", "Zoom account id; omit to read ZOOM_ACCOUNT_ID from the environment")
|
|
973
|
+
.option("--client-id <id>", "Zoom client id; omit to read ZOOM_CLIENT_ID")
|
|
974
|
+
.option("--client-secret <secret>", "Zoom client secret; omit to read ZOOM_CLIENT_SECRET and keep it out of shell history")
|
|
975
|
+
.option("--force", "replace the app although upcoming bookings still hold meetings the current app created (409 zoom_meetings_active otherwise; those meetings stay on the old Zoom account)")
|
|
976
|
+
.action(run(async (client, opts) => {
|
|
977
|
+
const accountId = (opts.accountId ?? process.env.ZOOM_ACCOUNT_ID ?? "").trim();
|
|
978
|
+
const clientId = (opts.clientId ?? process.env.ZOOM_CLIENT_ID ?? "").trim();
|
|
979
|
+
const clientSecret = (opts.clientSecret ?? process.env.ZOOM_CLIENT_SECRET ?? "").trim();
|
|
980
|
+
if (!accountId || !clientId || !clientSecret)
|
|
981
|
+
throw new Error("Pass --account-id, --client-id and --client-secret (or set ZOOM_ACCOUNT_ID / ZOOM_CLIENT_ID / ZOOM_CLIENT_SECRET).");
|
|
982
|
+
return client.put("/api/v1/settings/zoom", { accountId, clientId, clientSecret, ...(opts.force ? { force: true } : {}) });
|
|
983
|
+
}));
|
|
984
|
+
zoom
|
|
985
|
+
.command("disconnect")
|
|
986
|
+
.description("Forget the app; booking pages set to Zoom fall back to their written location. Refused (409 zoom_meetings_active) while upcoming bookings hold meetings this app created unless --force")
|
|
987
|
+
.option("--force", "disconnect anyway; those meetings stay on the Zoom account and cancellations record zoom_orphaned")
|
|
988
|
+
.action(run(async (client, opts) => client.del(`/api/v1/settings/zoom${opts.force ? "?force=1" : ""}`)));
|
|
989
|
+
zoom
|
|
990
|
+
.command("test")
|
|
991
|
+
.description("Prove the stored app works (a token is issued, the app's user answers); --host also proves that user belongs to the Zoom account (the value a booking page's --video-host must be). Read-only on Zoom")
|
|
992
|
+
.option("--host <email>", "a Zoom user of the connected account to verify as a meeting host")
|
|
993
|
+
.action(run(async (client, opts) => client.post("/api/v1/settings/zoom/test", opts.host ? { hostEmail: opts.host } : {})));
|
|
994
|
+
// ---- Booking pages (Conduyt #72, 2026-09-15): the public scheduling pages and their video option ----
|
|
995
|
+
const bookingPages = program.command("booking-pages").description("Booking pages: the public scheduling pages (slug, host, duration, availability, embed origins) and their video option (calendar scope)");
|
|
996
|
+
bookingPages
|
|
997
|
+
.command("list")
|
|
998
|
+
.description("List the account's booking pages (GET /booking-pages)")
|
|
999
|
+
.option("--active", "only active pages")
|
|
1000
|
+
.action(run(async (client, opts) => client.get(`/api/v1/booking-pages${opts.active ? "?is_active=true" : ""}`)));
|
|
1001
|
+
bookingPages
|
|
1002
|
+
.command("get <id>")
|
|
1003
|
+
.description("One booking page with every setting (GET /booking-pages/:id)")
|
|
1004
|
+
.action(run(async (client, id) => client.get(`/api/v1/booking-pages/${id}`)));
|
|
1005
|
+
const bookingPageBody = (opts) => {
|
|
1006
|
+
const body = {};
|
|
1007
|
+
const set = (key, value) => { if (value !== undefined)
|
|
1008
|
+
body[key] = value; };
|
|
1009
|
+
set("title", opts.title);
|
|
1010
|
+
set("slug", opts.slug);
|
|
1011
|
+
set("description", opts.description);
|
|
1012
|
+
set("userId", opts.host);
|
|
1013
|
+
set("location", opts.location);
|
|
1014
|
+
if (opts.videoProvider !== undefined)
|
|
1015
|
+
set("videoProvider", opts.videoProvider === "none" ? null : opts.videoProvider);
|
|
1016
|
+
if (opts.videoHost !== undefined)
|
|
1017
|
+
set("videoHostEmail", opts.videoHost === "none" ? null : opts.videoHost);
|
|
1018
|
+
if (opts.duration !== undefined)
|
|
1019
|
+
set("durationMinutes", Number(opts.duration));
|
|
1020
|
+
if (opts.minimumNotice !== undefined)
|
|
1021
|
+
set("minimumNotice", Number(opts.minimumNotice));
|
|
1022
|
+
if (opts.maxAdvanceDays !== undefined)
|
|
1023
|
+
set("maxAdvanceDays", Number(opts.maxAdvanceDays));
|
|
1024
|
+
set("leadSource", opts.leadSource);
|
|
1025
|
+
set("brandName", opts.brandName);
|
|
1026
|
+
if (typeof opts.embedOrigins === "string")
|
|
1027
|
+
set("embedAllowedOrigins", opts.embedOrigins.split(",").map((o) => o.trim()).filter(Boolean));
|
|
1028
|
+
if (typeof opts.availability === "string")
|
|
1029
|
+
set("availabilityRules", JSON.parse(opts.availability));
|
|
1030
|
+
if (opts.active !== undefined)
|
|
1031
|
+
set("isActive", opts.active);
|
|
1032
|
+
return body;
|
|
1033
|
+
};
|
|
1034
|
+
const bookingPageOptions = (cmd) => cmd
|
|
1035
|
+
.option("--title <title>", "page title")
|
|
1036
|
+
.option("--slug <slug>", "public slug (conduyt.app/book/<slug>)")
|
|
1037
|
+
.option("--description <text>", "description shown on the page")
|
|
1038
|
+
.option("--host <userId>", "host user UUID (the page owner)")
|
|
1039
|
+
.option("--duration <minutes>", "slot length in minutes")
|
|
1040
|
+
.option("--minimum-notice <minutes>", "earliest booking, minutes from now")
|
|
1041
|
+
.option("--max-advance-days <days>", "how far ahead visitors may book")
|
|
1042
|
+
.option("--location <text>", "written location (Zoom link, phone, address); with --video-provider zoom it is the fallback when Zoom is down")
|
|
1043
|
+
.option("--video-provider <zoom|none>", "zoom = a new Zoom meeting for every booking (needs `conduyt zoom connect`); none = the written location only")
|
|
1044
|
+
.option("--video-host <email|none>", "the Zoom user the meeting is created under (a user of the connected Zoom account); none = the app's own user")
|
|
1045
|
+
.option("--lead-source <source>", "lead source stamped on contacts the page creates")
|
|
1046
|
+
.option("--brand-name <name>", "white-label name shown on the page and in Zoom meeting names")
|
|
1047
|
+
.option("--embed-origins <origins>", "comma-separated origins allowed to embed/call the page (empty = any)")
|
|
1048
|
+
.option("--availability <json>", "AvailabilityWindow[] JSON override, e.g. '[{\"dayOfWeek\":1,\"startTime\":\"09:00\",\"endTime\":\"11:00\"}]'")
|
|
1049
|
+
.option("--active", "page accepts bookings")
|
|
1050
|
+
.option("--no-active", "page stops accepting bookings");
|
|
1051
|
+
bookingPageOptions(bookingPages.command("create").description("Create a booking page (POST /booking-pages): --title, --slug, --host and --duration at least"))
|
|
1052
|
+
.action(run(async (client, opts) => {
|
|
1053
|
+
const body = bookingPageBody(opts);
|
|
1054
|
+
for (const k of ["title", "slug", "userId", "durationMinutes"])
|
|
1055
|
+
if (body[k] === undefined)
|
|
1056
|
+
throw new Error(`--${k === "userId" ? "host" : k === "durationMinutes" ? "duration" : k} is required`);
|
|
1057
|
+
return client.post("/api/v1/booking-pages", body);
|
|
1058
|
+
}));
|
|
1059
|
+
bookingPageOptions(bookingPages.command("update <id>").description("Update a booking page (PATCH /booking-pages/:id): only the options given change"))
|
|
1060
|
+
.action(run(async (client, id, opts) => {
|
|
1061
|
+
const body = bookingPageBody(opts);
|
|
1062
|
+
if (Object.keys(body).length === 0)
|
|
1063
|
+
throw new Error("Nothing to update: pass at least one option.");
|
|
1064
|
+
return client.patch(`/api/v1/booking-pages/${id}`, body);
|
|
1065
|
+
}));
|
|
963
1066
|
// ---- WhatsApp via Twilio (2026-09-11) ----
|
|
964
1067
|
const whatsapp = program.command("whatsapp").description("WhatsApp via your own Twilio account: a Meta-approved sender, approved templates for outreach, free-form replies within a contact's 24-hour window");
|
|
965
1068
|
whatsapp
|
package/package.json
CHANGED