@theholocron/cli 5.0.0-alpha.197 → 5.0.0-alpha.199

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/cli.mjs CHANGED
@@ -2960,11 +2960,6 @@ async function runNotificationsChannelAdd(input) {
2960
2960
  */
2961
2961
  /** Features not built yet, in the order of the table in holocron#1098. Move an entry into a real check when it lands. */
2962
2962
  const PLANNED_CHECKS = [
2963
- {
2964
- step: 13,
2965
- id: "app-settings",
2966
- title: "bot description, tags, install link and URLs set"
2967
- },
2968
2963
  {
2969
2964
  step: 14,
2970
2965
  id: "role-assignment",
@@ -2999,6 +2994,7 @@ const promptKey = (p) => JSON.stringify([
2999
2994
  const accessText = (v) => v === "everyone" ? "everyone" : v.length === 0 ? "nobody (bots and webhooks only)" : v.join(", ");
3000
2995
  const shown = (s) => sanitizeDetail(s);
3001
2996
  const WELCOME_TITLE = "welcome screen and onboarding set";
2997
+ const APP_TITLE = "bot description, tags, install link and URLs set";
3002
2998
  const AVATAR_TITLE = "webhook names and avatars set";
3003
2999
  const SERVER_TITLE = "server verification, notifications and community set";
3004
3000
  const ACCESS_TITLE = "channel and category access set";
@@ -3118,12 +3114,13 @@ async function runDoctorNotifications(input) {
3118
3114
  ["categories", "categories exist"],
3119
3115
  ["channels", "channels in their category, with topics"],
3120
3116
  ["webhooks", "webhooks on their channels"],
3117
+ ["webhook-appearance", AVATAR_TITLE],
3121
3118
  ["roles", "roles exist and are mentionable"],
3122
3119
  ["role-appearance", ROLE_APPEARANCE_TITLE],
3123
3120
  ["access", ACCESS_TITLE],
3124
3121
  ["server-settings", SERVER_TITLE],
3125
3122
  ["welcome-onboarding", WELCOME_TITLE],
3126
- ["webhook-appearance", AVATAR_TITLE]
3123
+ ["app-settings", APP_TITLE]
3127
3124
  ]) failed(id, title, "not checked: could not read the server");
3128
3125
  } else {
3129
3126
  const inspected = found;
@@ -3154,6 +3151,17 @@ async function runDoctorNotifications(input) {
3154
3151
  else if (!match.webhooks.some((w) => same(w, name))) webhookProblems.push(`#${shown(channel.name)}: no webhook named ${shown(name)}`);
3155
3152
  }
3156
3153
  add("webhooks", "webhooks on their channels", webhookProblems, withWebhook.length === 0 ? "none declared" : `${withWebhook.length} of ${withWebhook.length} exist`);
3154
+ const avatarOf = (webhook) => (typeof webhook === "object" && webhook ? webhook.avatar : void 0) ?? layout.webhookAvatar;
3155
+ const wantAvatar = declaredChannels.filter(({ channel }) => channel.webhook && avatarOf(channel.webhook));
3156
+ const avatarProblems = [];
3157
+ for (const { cat, channel } of wantAvatar) {
3158
+ const name = (channel.webhook === true ? {} : channel.webhook).name ?? channel.name;
3159
+ const { match } = pick(channel.name, cat);
3160
+ if (!match) avatarProblems.push(`#${shown(channel.name)}: no channel to hold the webhook`);
3161
+ else if (match.webhooksError) avatarProblems.push(`#${shown(channel.name)}: could not list its webhooks (${shown(match.webhooksError)})`);
3162
+ else if (match.webhookAvatars && !match.webhookAvatars.some((w) => same(w, name))) avatarProblems.push(`#${shown(channel.name)}: webhook ${shown(name)} has no avatar`);
3163
+ }
3164
+ add("webhook-appearance", AVATAR_TITLE, avatarProblems, wantAvatar.length === 0 ? "none declared" : `${wantAvatar.length} of ${wantAvatar.length} set`);
3157
3165
  const roles = layout.roles ?? [];
3158
3166
  const roleProblems = [];
3159
3167
  if (inspected.rolesError) roleProblems.push(`could not read the roles (${shown(inspected.rolesError)})`);
@@ -3187,8 +3195,21 @@ async function runDoctorNotifications(input) {
3187
3195
  }
3188
3196
  }
3189
3197
  }
3190
- const styled = roles.filter((r) => r.color !== void 0 || r.hoist !== void 0 || r.permissions !== void 0);
3191
- add("role-appearance", ROLE_APPEARANCE_TITLE, inspected.rolesError ? [`not checked: could not read the roles`] : appearance, styled.length === 0 ? "none declared" : `${styled.length} of ${styled.length} as declared`);
3198
+ const everyone = layout.everyone;
3199
+ const everyoneProblems = [];
3200
+ if (everyone) {
3201
+ if (inspected.everyoneError) everyoneProblems.push(`@everyone: not checked, could not read its permissions (${shown(inspected.everyoneError)})`);
3202
+ else if (!inspected.everyone) everyoneProblems.push("@everyone: the provider did not report its permissions");
3203
+ else {
3204
+ const have = new Set(inspected.everyone.permissions.map((n) => n.toLowerCase()));
3205
+ const lacking = (everyone.allow ?? []).filter((n) => !have.has(n.toLowerCase()));
3206
+ const keeping = (everyone.deny ?? []).filter((n) => have.has(n.toLowerCase()));
3207
+ if (lacking.length > 0) everyoneProblems.push(`@everyone: missing permissions ${lacking.map(shown).join(", ")}`);
3208
+ if (keeping.length > 0) everyoneProblems.push(`@everyone: still has permissions the layout takes away: ${keeping.map(shown).join(", ")}`);
3209
+ }
3210
+ }
3211
+ const styledCount = roles.filter((r) => r.color !== void 0 || r.hoist !== void 0 || r.permissions !== void 0).length + (everyone ? 1 : 0);
3212
+ add("role-appearance", ROLE_APPEARANCE_TITLE, inspected.rolesError ? [`not checked: could not read the roles`, ...everyoneProblems] : [...appearance, ...everyoneProblems], styledCount === 0 ? "none declared" : `${styledCount} of ${styledCount} as declared`);
3192
3213
  const accessProblems = [];
3193
3214
  const compare = (label, want, have) => {
3194
3215
  if (!have) {
@@ -3305,19 +3326,35 @@ async function runDoctorNotifications(input) {
3305
3326
  }
3306
3327
  const welcomeParts = (wantWelcome ? 1 : 0) + (wantOnboarding ? 1 : 0);
3307
3328
  add("welcome-onboarding", WELCOME_TITLE, [...welcomeProblems, ...onboardingProblems], welcomeParts === 0 ? "none declared" : `${welcomeParts} of ${welcomeParts} as declared`);
3308
- }
3309
- if (found) {
3310
- const avatarOf = (webhook) => (typeof webhook === "object" && webhook ? webhook.avatar : void 0) ?? layout.webhookAvatar;
3311
- const wantAvatar = declaredChannels.filter(({ channel }) => channel.webhook && avatarOf(channel.webhook));
3312
- const avatarProblems = [];
3313
- for (const { cat, channel } of wantAvatar) {
3314
- const name = (channel.webhook === true ? {} : channel.webhook).name ?? channel.name;
3315
- const { match } = pick(channel.name, cat);
3316
- if (!match) avatarProblems.push(`#${shown(channel.name)}: no channel to hold the webhook`);
3317
- else if (match.webhooksError) avatarProblems.push(`#${shown(channel.name)}: could not list its webhooks (${shown(match.webhooksError)})`);
3318
- else if (match.webhookAvatars && !match.webhookAvatars.some((w) => same(w, name))) avatarProblems.push(`#${shown(channel.name)}: webhook ${shown(name)} has no avatar`);
3329
+ const wantApp = layout.application;
3330
+ const appProblems = [];
3331
+ if (wantApp) {
3332
+ if (inspected.applicationError) appProblems.push(`not checked: could not read the application (${shown(inspected.applicationError)})`);
3333
+ else if (!inspected.application) appProblems.push("the provider did not report the application");
3334
+ else {
3335
+ const have = inspected.application;
3336
+ const set = (list) => JSON.stringify([...new Set(list.map((n) => n.toLowerCase()))].sort());
3337
+ const exact = (list) => JSON.stringify([...new Set(list)].sort());
3338
+ if (wantApp.description !== void 0 && (have.description ?? "") !== wantApp.description) appProblems.push("description differs from the layout");
3339
+ if (wantApp.tags !== void 0 && set(have.tags ?? []) !== set(wantApp.tags)) appProblems.push(`tags are ${(have.tags ?? []).map(shown).join(", ") || "none"} but the layout says ${wantApp.tags.map(shown).join(", ") || "none"}`);
3340
+ if (wantApp.install !== void 0) {
3341
+ if (!have.install) appProblems.push("no install link is set");
3342
+ else {
3343
+ if (exact(have.install.scopes) !== exact(wantApp.install.scopes)) appProblems.push(`install scopes are ${have.install.scopes.map(shown).join(", ")} but the layout says ${wantApp.install.scopes.map(shown).join(", ")}`);
3344
+ if (set(have.install.permissions) !== set(wantApp.install.permissions)) appProblems.push("install permissions differ from the layout");
3345
+ }
3346
+ }
3347
+ for (const [field, label] of [
3348
+ ["termsUrl", "terms URL"],
3349
+ ["privacyUrl", "privacy URL"],
3350
+ ["roleConnectionsUrl", "role-connections URL"]
3351
+ ]) {
3352
+ const wanted = wantApp[field];
3353
+ if (wanted !== void 0 && (have[field] ?? "") !== wanted) appProblems.push(`${label} is ${shown(have[field] || "not set")} but the layout says ${shown(wanted)}`);
3354
+ }
3355
+ }
3319
3356
  }
3320
- add("webhook-appearance", AVATAR_TITLE, avatarProblems, wantAvatar.length === 0 ? "none declared" : `${wantAvatar.length} of ${wantAvatar.length} set`);
3357
+ add("app-settings", APP_TITLE, appProblems, wantApp ? "1 of 1 as declared" : "none declared");
3321
3358
  }
3322
3359
  const wanted = [...declaredChannels.filter(({ channel }) => channel.webhook).map(({ channel }) => channel.webhook !== true && channel.webhook && channel.webhook.secret ? channel.webhook.secret : defaultSecretName(channel.name)), ...(layout.roles ?? []).flatMap((r) => r.secret ? [r.secret] : [])];
3323
3360
  if (wanted.length === 0) checks.push({
@@ -4034,6 +4071,9 @@ function serverLayoutProblem(layout) {
4034
4071
  if (count === 0) return `${where} names the channel \`${name}\`, which is not in the layout's channels`;
4035
4072
  if (count > 1) return `${where} names \`${name}\`, but more than one channel in the layout has that name`;
4036
4073
  };
4074
+ const everyone = layout.everyone;
4075
+ const both = (everyone?.allow ?? []).find((p) => (everyone?.deny ?? []).some((d) => d.toLowerCase() === p.toLowerCase()));
4076
+ if (both !== void 0) return `everyone: \`${both}\` is in both \`allow\` and \`deny\``;
4037
4077
  const server = layout.server;
4038
4078
  if (server) {
4039
4079
  for (const field of [
@@ -4135,6 +4175,8 @@ async function runNotificationsSetup(input) {
4135
4175
  if (layout.server && !notifications.ensureServer) return refuse(`the layout declares \`server\` settings, but the \`${notifications.providerName}\` provider cannot manage them`);
4136
4176
  if (layout.welcome && !notifications.ensureWelcome) return refuse(`the layout declares a \`welcome\` screen, but the \`${notifications.providerName}\` provider cannot manage one`);
4137
4177
  if (layout.onboarding && !notifications.ensureOnboarding) return refuse(`the layout declares \`onboarding\`, but the \`${notifications.providerName}\` provider cannot manage it`);
4178
+ if (layout.application && !notifications.ensureApplication) return refuse(`the layout declares \`application\` settings, but the \`${notifications.providerName}\` provider cannot manage them`);
4179
+ if (layout.everyone && !notifications.ensureEveryone) return refuse(`the layout declares \`everyone\` permissions, but the \`${notifications.providerName}\` provider cannot manage them`);
4138
4180
  const needsCategories = (layout.categories ?? []).find((c) => c.access);
4139
4181
  if (needsCategories && !notifications.ensureCategory) return refuse(`category \`${needsCategories.name}\` declares \`access\`, but the \`${notifications.providerName}\` provider cannot manage categories`);
4140
4182
  const providerProblem = notifications.validateLayout?.(layout);
@@ -4252,7 +4294,7 @@ async function runNotificationsSetup(input) {
4252
4294
  });
4253
4295
  });
4254
4296
  }
4255
- if (layout.server || layout.welcome || layout.onboarding) {
4297
+ if (layout.server || layout.welcome || layout.onboarding || layout.application || layout.everyone) {
4256
4298
  print(`\n${style.header("Server")}`);
4257
4299
  const apply = async (label, declared, run) => {
4258
4300
  if (!declared) return;
@@ -4268,9 +4310,11 @@ async function runNotificationsSetup(input) {
4268
4310
  made(changed);
4269
4311
  });
4270
4312
  };
4313
+ await apply("@everyone permissions", !!layout.everyone, () => notifications.ensureEveryone(layout.everyone));
4271
4314
  await apply("server settings", !!layout.server, () => notifications.ensureServer(layout.server));
4272
4315
  await apply("welcome screen", !!layout.welcome, () => notifications.ensureWelcome(layout.welcome));
4273
4316
  await apply("onboarding", !!layout.onboarding, () => notifications.ensureOnboarding(layout.onboarding));
4317
+ await apply("bot application", !!layout.application, () => notifications.ensureApplication(layout.application));
4274
4318
  }
4275
4319
  if (report.failures.length > 0) report.status = "fail";
4276
4320
  else if (dryRun) report.status = "dry-run";