@coderook/cli 0.38.2 → 0.40.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.
@@ -308,6 +308,56 @@ async function commandCollaborators(parsed) {
308
308
  const project = await (0, project_commands_js_1.resolveProject)(parsed.positional[0]);
309
309
  if (!project)
310
310
  return 1;
311
+ /*
312
+ Changing a role, taking somebody off, and withdrawing an invite: the rest
313
+ of the website's Collaborators screen. Each names the person by email or
314
+ display name, which is what the listing below shows.
315
+ */
316
+ const changing = parsed.flags.get("change");
317
+ const removing = parsed.flags.get("remove");
318
+ const cancelling = parsed.flags.get("cancel-invite");
319
+ if (changing !== undefined || removing !== undefined || cancelling !== undefined) {
320
+ const wanted = changing ?? removing ?? cancelling;
321
+ if (typeof wanted !== "string" || !wanted.trim()) {
322
+ console.error((0, colour_js_1.red)("Who? Name them by email address or display name."));
323
+ return 1;
324
+ }
325
+ const who = wanted.trim().toLowerCase();
326
+ const people = await (0, api_js_1.collaborators)(project.id);
327
+ if (cancelling !== undefined) {
328
+ const pending = people.invites.find((one) => one.email.toLowerCase() === who || one.id === wanted.trim());
329
+ if (!pending) {
330
+ console.error((0, colour_js_1.red)(`No invite waiting for ${wanted.trim()} on ${project.name}.`));
331
+ return 1;
332
+ }
333
+ await (0, api_js_1.revokeInvite)(project.id, pending.id);
334
+ console.log((0, colour_js_1.green)(`Withdrew the invite to ${pending.email}. Its link no longer works.`));
335
+ return 0;
336
+ }
337
+ const person = people.collaborators.find((one) => one.email.toLowerCase() === who ||
338
+ one.displayName.toLowerCase() === who ||
339
+ one.userId === wanted.trim());
340
+ if (!person) {
341
+ console.error((0, colour_js_1.red)(`${wanted.trim()} is not one of ${project.name}'s people.`));
342
+ console.error((0, colour_js_1.dim)(` See who is: cbx people ${project.slug}`));
343
+ return 1;
344
+ }
345
+ const label = person.displayName || person.email;
346
+ if (removing !== undefined) {
347
+ await (0, api_js_1.removeCollaborator)(project.id, person.userId);
348
+ console.log((0, colour_js_1.green)(`${label} is no longer on ${project.name}.`));
349
+ return 0;
350
+ }
351
+ const flag = parsed.flags.get("role");
352
+ const role = typeof flag === "string" ? flag.trim().toLowerCase() : "";
353
+ if (!["write", "read", "admin"].includes(role)) {
354
+ console.error((0, colour_js_1.red)(role ? `"${role}" is not a role. Use write, read or admin.` : "Which role? Add --role write, read or admin."));
355
+ return 1;
356
+ }
357
+ await (0, api_js_1.setCollaboratorRole)(project.id, person.userId, role);
358
+ console.log((0, colour_js_1.green)(`${label} now has ${role} access to ${project.name}.`));
359
+ return 0;
360
+ }
311
361
  const email = parsed.flags.get("invite");
312
362
  if (email === true) {
313
363
  console.error((0, colour_js_1.red)("Whose address? Try: --invite someone@example.com"));
@@ -807,6 +857,76 @@ async function commandHooks(parsed) {
807
857
  }
808
858
  return 0;
809
859
  }
860
+ /*
861
+ Everything else the website's Webhooks card does to one hook: send a test,
862
+ read what was delivered, send one again, change its events, switch it back
863
+ on. Each names the hook by id or address, as --remove does.
864
+ */
865
+ const tested = parsed.flags.get("test");
866
+ const history = parsed.flags.get("deliveries");
867
+ const resend = parsed.flags.get("redeliver");
868
+ const resume = parsed.flags.get("on");
869
+ const named = parsed.flags.get("hook");
870
+ const changingEvents = parsed.flags.has("events");
871
+ if (tested !== undefined || history !== undefined || resend !== undefined || resume !== undefined || changingEvents) {
872
+ const wanted = [tested, history, resume, named].find((value) => typeof value === "string" && value.trim());
873
+ const hook = await whichHook(project.id, project.name, typeof wanted === "string" ? wanted.trim() : null);
874
+ if (!hook)
875
+ return 1;
876
+ if (tested !== undefined) {
877
+ const delivery = await (0, api_js_1.testWebhook)(project.id, hook.id);
878
+ console.log(`Sent a test to ${hook.url}.`);
879
+ console.log(` ${deliveryLine(delivery)}`);
880
+ return delivery.state === "delivered" ? 0 : 1;
881
+ }
882
+ if (history !== undefined) {
883
+ const found = await (0, api_js_1.webhookDeliveries)(project.id, hook.id);
884
+ if (!found.length) {
885
+ console.log((0, colour_js_1.dim)(`Nothing has been sent to ${hook.url} yet. Send a test: cbx hooks --test ${hook.id}`));
886
+ return 0;
887
+ }
888
+ console.log((0, colour_js_1.bold)(hook.url));
889
+ for (const delivery of found)
890
+ console.log(` ${deliveryLine(delivery)}`);
891
+ console.log("");
892
+ console.log((0, colour_js_1.dim)(` Send one again: cbx hooks --redeliver <delivery> --hook ${hook.id}`));
893
+ return 0;
894
+ }
895
+ if (resend !== undefined) {
896
+ if (typeof resend !== "string" || !resend.trim()) {
897
+ console.error((0, colour_js_1.red)("Which delivery?") + (0, colour_js_1.dim)(` cbx hooks --deliveries ${hook.id} lists them`));
898
+ return 1;
899
+ }
900
+ /* The start of an id is enough, as long as it picks out one. */
901
+ const prefix = resend.trim().toLowerCase();
902
+ const matches = (await (0, api_js_1.webhookDeliveries)(project.id, hook.id)).filter((one) => one.id.toLowerCase().startsWith(prefix));
903
+ if (matches.length !== 1) {
904
+ console.error((0, colour_js_1.red)(matches.length ? `${resend.trim()} matches more than one delivery; give more of its id.` : `No recent delivery ${resend.trim()} for ${hook.url}.`));
905
+ return 1;
906
+ }
907
+ const delivery = await (0, api_js_1.redeliverWebhook)(project.id, hook.id, matches[0].id);
908
+ console.log(`Sent ${matches[0].event} to ${hook.url} again.`);
909
+ console.log(` ${deliveryLine(delivery)}`);
910
+ return delivery.state === "delivered" ? 0 : 1;
911
+ }
912
+ if (changingEvents) {
913
+ const value = parsed.flags.get("events");
914
+ const text = typeof value === "string" ? value.trim() : "";
915
+ const events = text === "all" ? [] : text.split(",").map((one) => one.trim()).filter(Boolean);
916
+ if (!events.length && text !== "all") {
917
+ console.error((0, colour_js_1.red)("Which events? A comma separated list, or all.") + (0, colour_js_1.dim)(" cbx hooks --hook <id> --events version.published"));
918
+ return 1;
919
+ }
920
+ await (0, api_js_1.updateWebhook)(project.id, hook.id, { events });
921
+ }
922
+ if (resume !== undefined) {
923
+ await (0, api_js_1.updateWebhook)(project.id, hook.id, { active: true });
924
+ }
925
+ const after = (await (0, api_js_1.webhooks)(project.id)).find((one) => one.id === hook.id) ?? hook;
926
+ console.log((0, colour_js_1.green)(`Updated ${hook.url}.`));
927
+ console.log(` ${after.active ? (0, colour_js_1.green)("on") : (0, colour_js_1.red)("off")} ${(0, colour_js_1.dim)(after.events.length ? after.events.join(", ") : "everything")}`);
928
+ return 0;
929
+ }
810
930
  const removing = parsed.flags.get("remove");
811
931
  if (removing !== undefined) {
812
932
  if (typeof removing !== "string" || !removing.trim()) {
@@ -847,5 +967,39 @@ async function commandHooks(parsed) {
847
967
  console.log(` ${(0, colour_js_1.dim)(hook.id)} ${health}` +
848
968
  (hook.events.length ? ` ${(0, colour_js_1.dim)(hook.events.join(", "))}` : ` ${(0, colour_js_1.dim)("everything")}`));
849
969
  }
970
+ if (found.some((hook) => !hook.active)) {
971
+ console.log("");
972
+ console.log((0, colour_js_1.dim)(" Switch one back on: cbx hooks --on <id>"));
973
+ }
850
974
  return 0;
851
975
  }
976
+ /**
977
+ * The hook a command is about: named by id or address, or the only one.
978
+ *
979
+ * Naming none is fine when there is exactly one, which is the usual case and
980
+ * the one where asking "which?" would be a question with one answer.
981
+ */
982
+ async function whichHook(projectId, projectName, wanted) {
983
+ const found = await (0, api_js_1.webhooks)(projectId);
984
+ if (!wanted) {
985
+ if (found.length === 1)
986
+ return found[0];
987
+ console.error((0, colour_js_1.red)(found.length ? `${projectName} has ${found.length} webhooks. Say which: --hook <id>` : `${projectName} notifies nothing.`));
988
+ return null;
989
+ }
990
+ const hook = found.find((one) => one.id === wanted || one.url === wanted);
991
+ if (!hook)
992
+ console.error((0, colour_js_1.red)(`${projectName} has no webhook called ${wanted}.`));
993
+ return hook ?? null;
994
+ }
995
+ /** One delivery on one line: when, what, how it went, and its id. */
996
+ function deliveryLine(delivery) {
997
+ const when = new Date(delivery.createdAt).toLocaleString();
998
+ const how = delivery.state === "delivered"
999
+ ? (0, colour_js_1.green)(`delivered${delivery.status ? ` (${delivery.status})` : ""}`)
1000
+ : delivery.state === "pending"
1001
+ ? (0, colour_js_1.dim)(`waiting, ${delivery.attempts} ${delivery.attempts === 1 ? "try" : "tries"} so far`)
1002
+ : (0, colour_js_1.red)(`${delivery.state}${delivery.status ? ` (${delivery.status})` : ""}`);
1003
+ const detail = delivery.detail && delivery.state !== "delivered" ? (0, colour_js_1.dim)(` ${delivery.detail}`) : "";
1004
+ return `${(0, colour_js_1.dim)(delivery.id.slice(0, 8))} ${when.padEnd(22)} ${delivery.event.padEnd(24)} ${how}${detail}`;
1005
+ }