@helyx/bot 1.5.2 → 1.5.4

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 (115) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/command-root-catalogue.json +452 -0
  3. package/dist/cached-message-deletion-authority.d.ts +27 -0
  4. package/dist/cached-message-deletion-authority.js +288 -0
  5. package/dist/channel-audit-configuration.d.ts +11 -1
  6. package/dist/channel-audit-configuration.js +28 -6
  7. package/dist/channel-audit-control.js +42 -2
  8. package/dist/channel-audit-permissions.js +76 -3
  9. package/dist/cloud-command-policy-sync.d.ts +26 -0
  10. package/dist/cloud-command-policy-sync.js +198 -0
  11. package/dist/cloud-connector.d.ts +6 -1
  12. package/dist/cloud-connector.js +224 -53
  13. package/dist/cloud-control-dispatcher.js +18 -0
  14. package/dist/command-policy-runtime.d.ts +23 -0
  15. package/dist/command-policy-runtime.js +53 -0
  16. package/dist/command-root-catalogue.d.ts +5 -0
  17. package/dist/command-root-catalogue.js +16 -0
  18. package/dist/control-error-mapping.d.ts +8 -0
  19. package/dist/control-error-mapping.js +60 -0
  20. package/dist/control-errors.d.ts +3 -1
  21. package/dist/control-errors.js +4 -1
  22. package/dist/control-health-routes.d.ts +10 -0
  23. package/dist/control-health-routes.js +28 -0
  24. package/dist/control-server-schemas.d.ts +7 -0
  25. package/dist/control-server-schemas.js +9 -0
  26. package/dist/control-server.d.ts +27 -6
  27. package/dist/control-server.js +103 -122
  28. package/dist/dashboard-feature-control.d.ts +2 -1
  29. package/dist/dashboard-feature-control.js +1 -0
  30. package/dist/dashboard-logging.d.ts +20 -0
  31. package/dist/dashboard-logging.js +39 -0
  32. package/dist/dashboard-module-projection.d.ts +1 -1
  33. package/dist/dashboard-module-projection.js +5 -2
  34. package/dist/dashboard-preview-import-routes.js +8 -0
  35. package/dist/database-bootstrap.d.ts +1 -1
  36. package/dist/database-bootstrap.js +4 -3
  37. package/dist/discord-channel-audit.js +1 -0
  38. package/dist/discord-user-identities.d.ts +14 -0
  39. package/dist/discord-user-identities.js +64 -0
  40. package/dist/emergency-deletion-dispatcher.d.ts +68 -0
  41. package/dist/emergency-deletion-dispatcher.js +502 -0
  42. package/dist/emergency-deletion-rest-transport.d.ts +24 -0
  43. package/dist/emergency-deletion-rest-transport.js +253 -0
  44. package/dist/events.d.ts +5 -0
  45. package/dist/events.js +53 -10
  46. package/dist/guarded-discord-dispatcher.d.ts +29 -0
  47. package/dist/guarded-discord-dispatcher.js +204 -0
  48. package/dist/guild-command-discord-client.d.ts +2 -0
  49. package/dist/guild-command-discord-client.js +7 -0
  50. package/dist/guild-command-projection.d.ts +4 -1
  51. package/dist/guild-command-projection.js +25 -0
  52. package/dist/guild-command-read-service.d.ts +10 -1
  53. package/dist/guild-command-read-service.js +37 -0
  54. package/dist/guild-command-worker.d.ts +19 -4
  55. package/dist/guild-command-worker.js +356 -26
  56. package/dist/guild-message-protection-author-roles.d.ts +7 -0
  57. package/dist/guild-message-protection-author-roles.js +93 -0
  58. package/dist/guild-message-protection-deletion.d.ts +21 -0
  59. package/dist/guild-message-protection-deletion.js +138 -0
  60. package/dist/guild-message-protection-delivery.d.ts +41 -0
  61. package/dist/guild-message-protection-delivery.js +223 -0
  62. package/dist/guild-message-update-metadata.d.ts +32 -0
  63. package/dist/guild-message-update-metadata.js +255 -0
  64. package/dist/guilds.d.ts +2 -1
  65. package/dist/guilds.js +11 -1
  66. package/dist/hosted-server-moderation.d.ts +2 -2
  67. package/dist/index.js +68 -4
  68. package/dist/interactions.d.ts +3 -1
  69. package/dist/interactions.js +77 -12
  70. package/dist/invite-rollup-maintenance.d.ts +9 -0
  71. package/dist/invite-rollup-maintenance.js +12 -0
  72. package/dist/managed-resource-authorization.d.ts +15 -0
  73. package/dist/managed-resource-authorization.js +37 -0
  74. package/dist/managed-resource-create-defaults.d.ts +21 -0
  75. package/dist/managed-resource-create-defaults.js +83 -0
  76. package/dist/managed-resource-query.d.ts +6 -0
  77. package/dist/managed-resource-query.js +15 -0
  78. package/dist/member-role-ids.d.ts +17 -0
  79. package/dist/member-role-ids.js +66 -0
  80. package/dist/message-protection-admission.d.ts +60 -0
  81. package/dist/message-protection-admission.js +279 -0
  82. package/dist/message-protection-cold-data.d.ts +42 -0
  83. package/dist/message-protection-cold-data.js +180 -0
  84. package/dist/message-protection-coordinator.d.ts +73 -0
  85. package/dist/message-protection-coordinator.js +622 -0
  86. package/dist/message-protection-settings.d.ts +40 -0
  87. package/dist/message-protection-settings.js +275 -0
  88. package/dist/message-protection-summary.d.ts +8 -0
  89. package/dist/message-protection-summary.js +98 -0
  90. package/dist/module-availability-control.js +20 -12
  91. package/dist/module-logging.d.ts +3 -2
  92. package/dist/module-logging.js +6 -1
  93. package/dist/modules.js +56 -1
  94. package/dist/mysql-runtime-persistence.d.ts +1 -0
  95. package/dist/mysql-runtime-persistence.js +4 -1
  96. package/dist/nexus-command-recovery-control-routes.d.ts +5 -0
  97. package/dist/nexus-command-recovery-control-routes.js +48 -0
  98. package/dist/nexus-command-references.d.ts +6 -0
  99. package/dist/nexus-command-references.js +33 -0
  100. package/dist/scheduled-task-runner.js +23 -4
  101. package/dist/shard-command-startup.d.ts +13 -0
  102. package/dist/shard-command-startup.js +27 -0
  103. package/dist/shard-contracts.d.ts +84 -0
  104. package/dist/shard-contracts.js +33 -0
  105. package/dist/shard-core-readiness.d.ts +5 -0
  106. package/dist/shard-core-readiness.js +19 -0
  107. package/dist/shard-router.d.ts +3 -0
  108. package/dist/shard-router.js +10 -1
  109. package/dist/shard-supervisor.d.ts +1 -1
  110. package/dist/shard-supervisor.js +124 -43
  111. package/dist/shard-worker.js +25 -15
  112. package/documentation/README.md +1 -0
  113. package/documentation/examples/.env.example +0 -45
  114. package/documentation/guides/self-hosting.md +54 -0
  115. package/package.json +18 -17
@@ -1,4 +1,4 @@
1
- import type { GuildCommandVisibilityProfile } from "@helyx/database";
1
+ import type { GuildCommandRootMappingRecord, GuildCommandVisibilityProfile } from "@helyx/database";
2
2
  import type { ModuleDefinition } from "@helyx/sdk";
3
3
  export { commandRegistrationPolicy } from "./command-registration-policy.js";
4
4
  export interface ProjectedGuildCommand {
@@ -15,6 +15,7 @@ export interface GuildCommandProjection {
15
15
  rootCount: number;
16
16
  subcommandCount: number;
17
17
  selectedModuleIds: readonly string[];
18
+ rootMappings?: readonly GuildCommandRootMappingRecord[];
18
19
  }
19
20
  export interface ObservedGuildCommand {
20
21
  id?: string;
@@ -39,6 +40,8 @@ export declare function projectGuildCommands(input: {
39
40
  globallyDisabledModuleIds?: ReadonlySet<string>;
40
41
  profile: GuildCommandVisibilityProfile;
41
42
  includeInternalCommands?: boolean;
43
+ rootMappings?: readonly GuildCommandRootMappingRecord[];
44
+ reservedCommandNames?: readonly string[];
42
45
  }): GuildCommandProjection;
43
46
  export declare function diffGuildCommands(desired: GuildCommandProjection, observedInput: readonly ObservedGuildCommand[]): GuildCommandDiff;
44
47
  export declare function guildCommandDefinitionHash(definitions: readonly Readonly<Record<string, unknown>>[]): string;
@@ -1,4 +1,5 @@
1
1
  import { isCustomerSelectableModule } from "@helyx/module-manifest";
2
+ import { collectReservedCommandNames } from "@helyx/sdk";
2
3
  import { createHash } from "node:crypto";
3
4
  import { collectCommandRegistrations, } from "./shared-commands.js";
4
5
  import { toCommandDefinition } from "./interactions.js";
@@ -46,6 +47,7 @@ export function projectGuildCommands(input) {
46
47
  (input.includeInternalCommands && policy === "internal-guild"));
47
48
  })
48
49
  .map(projectRegistration)
50
+ .map((command) => projectRootMapping(command, input))
49
51
  .sort(compareProjectedCommands);
50
52
  validateProjectedCommands(definitions);
51
53
  const canonicalDefinitions = definitions.map(({ definition }) => definition);
@@ -55,6 +57,29 @@ export function projectGuildCommands(input) {
55
57
  rootCount: definitions.length,
56
58
  subcommandCount: definitions.reduce((total, definition) => total + definition.executablePaths.length, 0),
57
59
  selectedModuleIds: [...selected].sort(),
60
+ ...(input.rootMappings ? { rootMappings: input.rootMappings } : {}),
61
+ };
62
+ }
63
+ function projectRootMapping(command, input) {
64
+ if (command.name !== "nexus" || command.type !== 1)
65
+ return command;
66
+ const mappings = (input.rootMappings ?? []).filter(({ canonicalRoot }) => canonicalRoot === "nexus");
67
+ if (mappings.length > 1)
68
+ throw new Error("Duplicate Nexus root mapping");
69
+ const mapping = mappings[0];
70
+ if (!mapping || mapping.desiredRoot === "nexus")
71
+ return command;
72
+ // Reserve every loaded command, including currently disabled modules and aliases.
73
+ const reserved = new Set([
74
+ ...collectReservedCommandNames(input.modules),
75
+ ...(input.reservedCommandNames ?? []),
76
+ ]);
77
+ if (reserved.has(mapping.desiredRoot))
78
+ throw new Error("Nexus root conflicts with a reserved command");
79
+ return {
80
+ ...command,
81
+ name: mapping.desiredRoot,
82
+ definition: { ...command.definition, name: mapping.desiredRoot },
58
83
  };
59
84
  }
60
85
  function projectionRegistrationPolicy(module, contribution) {
@@ -16,6 +16,13 @@ export interface GuildCommandReadStatus {
16
16
  windowStartedAt: string;
17
17
  };
18
18
  }
19
+ export interface GuildCommandInteractionRoute {
20
+ applicationId: string;
21
+ guildId: string | null;
22
+ commandGuildId: string | null;
23
+ commandId: string;
24
+ commandName: string;
25
+ }
19
26
  export interface GuildCommandReadHandler {
20
27
  getStatus(guildId: string): Promise<GuildCommandReadStatus>;
21
28
  requestReconciliation(input: {
@@ -79,7 +86,7 @@ export interface GuildCommandReadHandler {
79
86
  scheduledGuildCount: number;
80
87
  }>;
81
88
  }
82
- type ReadCommandMethod = "getState" | "getActiveRequest" | "getCapacity" | "ensureState" | "schedule" | "recordStatusRefresh" | "changeVisibilityProfile" | "setMigrationHold" | "listHistory" | "getHealth";
89
+ type ReadCommandMethod = "getState" | "getActiveRequest" | "getCapacity" | "ensureState" | "schedule" | "recordStatusRefresh" | "changeVisibilityProfile" | "setMigrationHold" | "listHistory" | "getHealth" | "getRootMapping" | "isRetiredRootCommandId";
83
90
  export interface GuildCommandReadRepositories {
84
91
  commands: Pick<GuildCommandRepository, ReadCommandMethod> & Pick<MysqlGuildCommandRepository, ReadCommandMethod>;
85
92
  installations: Pick<InstallationRepository, "listActiveGuildIdsWithEnabledModule"> & Pick<MysqlInstallationRepository, "listActiveGuildIdsWithEnabledModule">;
@@ -94,6 +101,8 @@ export declare class GuildCommandReadService implements GuildCommandReadHandler
94
101
  private readonly writeAllGuilds;
95
102
  private readonly modules;
96
103
  constructor(databaseOrRepositories: Pool | GuildCommandReadRepositories, applicationId: string, workerHealth?: (() => Promise<GuildCommandWorkerHealth> | undefined) | undefined, discordToken?: string | undefined, guildCommandWritesEnabled?: boolean, writeGuildIds?: readonly string[], writeAllGuilds?: boolean, modules?: readonly ModuleDefinition[]);
104
+ /** Resolve only Discord-observed guild roots; desired settings never alias. */
105
+ resolveInteractionCommand(input: GuildCommandInteractionRoute): Promise<string | null>;
97
106
  previewFirstTimeSetup(moduleIds: string[]): Promise<{
98
107
  selectedModuleIds: string[];
99
108
  rootCount: number;
@@ -31,6 +31,43 @@ export class GuildCommandReadService {
31
31
  this.#repository = repositories.commands;
32
32
  this.#installations = repositories.installations;
33
33
  }
34
+ /** Resolve only Discord-observed guild roots; desired settings never alias. */
35
+ async resolveInteractionCommand(input) {
36
+ if (input.applicationId !== this.applicationId)
37
+ return null;
38
+ if (!input.guildId)
39
+ return input.commandName === "nexus" ? null : input.commandName;
40
+ if (typeof this.#repository.getRootMapping !== "function" ||
41
+ typeof this.#repository.isRetiredRootCommandId !== "function")
42
+ throw new Error("Guild command routing requires root mapping and retired ID capabilities");
43
+ const mapping = await this.#repository.getRootMapping(this.applicationId, input.guildId, "nexus");
44
+ if (mapping &&
45
+ (mapping.applicationId !== this.applicationId ||
46
+ mapping.guildId !== input.guildId ||
47
+ mapping.canonicalRoot !== "nexus"))
48
+ throw new Error("Guild command routing mapping has an invalid scope");
49
+ if (mapping && input.commandName === mapping.observedRoot) {
50
+ return mapping.observedCommandId &&
51
+ input.commandId === mapping.observedCommandId &&
52
+ input.commandGuildId === input.guildId
53
+ ? "nexus"
54
+ : null;
55
+ }
56
+ // A Discord ID retained by an in-place rename still belongs to Nexus;
57
+ // a stale name cannot send that same ID to a newly registered owner.
58
+ if (mapping && input.commandId === mapping.observedCommandId)
59
+ return null;
60
+ // Only the exact active observed name/ID is accepted ahead of retirement.
61
+ // Reusing an old root name for another contribution cannot revive its ID.
62
+ if (mapping &&
63
+ (input.commandName === "nexus" ||
64
+ input.commandName === mapping.desiredRoot))
65
+ return null;
66
+ if (input.commandGuildId === input.guildId &&
67
+ (await this.#repository.isRetiredRootCommandId(this.applicationId, input.guildId, input.commandId)))
68
+ return null;
69
+ return input.commandName;
70
+ }
34
71
  previewFirstTimeSetup(moduleIds) {
35
72
  const requested = new Set(moduleIds);
36
73
  const byId = new Map(this.modules.map((module) => [module.manifest.id, module]));
@@ -1,6 +1,8 @@
1
1
  import { GuildCommandRepository, InstallationRepository, ModuleAvailabilityRepository, type GuildCommandHealth, type GuildCommandRequestRecord } from "@helyx/database";
2
2
  import { type GuildCommandRepository as MysqlGuildCommandRepository, type InstallationRepository as MysqlInstallationRepository, type ModuleAvailabilityRepository as MysqlModuleAvailabilityRepository } from "@helyx/database-mysql";
3
3
  import type { Logger, ModuleDefinition } from "@helyx/sdk";
4
+ import type { CommandRestrictionRecovery } from "@helyx/platform";
5
+ import type { CommandPolicyDeliveryGate } from "@helyx/database-contracts";
4
6
  import type { Pool } from "pg";
5
7
  import { type GuildCommandDiff, type GuildCommandProjection } from "./guild-command-projection.js";
6
8
  import type { GuildCommandDiscordClient } from "./guild-command-discord-client.js";
@@ -10,7 +12,14 @@ export interface GuildCommandWorkerHealth extends GuildCommandHealth {
10
12
  workerRunning: boolean;
11
13
  activeCount: number;
12
14
  }
13
- type WorkerCommandRepository = Pick<GuildCommandRepository, "claimDue" | "getHealth" | "heartbeat" | "getState" | "completeDryRun" | "defer" | "markBlocked" | "markFailed" | "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation"> & Pick<MysqlGuildCommandRepository, "claimDue" | "getHealth" | "heartbeat" | "getState" | "completeDryRun" | "defer" | "markBlocked" | "markFailed" | "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation">;
15
+ export interface GuildCommandCataloguePort {
16
+ recheck(limit?: number): Promise<{
17
+ examined: number;
18
+ reset: number;
19
+ }>;
20
+ getReservedCommandNames(): Promise<readonly string[]>;
21
+ }
22
+ type WorkerCommandRepository = Pick<GuildCommandRepository, "claimDue" | "getHealth" | "heartbeat" | "getState" | "listRootMappings" | "completeDryRun" | "defer" | "markBlocked" | "markFailed" | "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation" | "retireRootObservation" | "isRetiredRootCommandId"> & Pick<MysqlGuildCommandRepository, "claimDue" | "getHealth" | "heartbeat" | "getState" | "listRootMappings" | "completeDryRun" | "defer" | "markBlocked" | "markFailed" | "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation" | "retireRootObservation" | "isRetiredRootCommandId">;
14
23
  export interface GuildCommandWorkerRepositories {
15
24
  commands: WorkerCommandRepository;
16
25
  installations: Pick<InstallationRepository, "listEnabledModules"> & Pick<MysqlInstallationRepository, "listEnabledModules">;
@@ -27,10 +36,15 @@ export declare class GuildCommandWorker {
27
36
  #private;
28
37
  constructor(input: {
29
38
  modules: readonly ModuleDefinition[];
39
+ commandRestrictions?: Pick<CommandRestrictionRecovery, "getPolicy" | "recheck">;
40
+ commandCatalogue?: GuildCommandCataloguePort;
41
+ commandPolicyDelivery?: {
42
+ readGate(): Promise<CommandPolicyDeliveryGate>;
43
+ };
30
44
  logger: Logger;
31
45
  workerId?: string;
32
46
  fencingToken: bigint;
33
- discord?: Pick<GuildCommandDiscordClient, "canWrite" | "observe" | "replace">;
47
+ discord?: Pick<GuildCommandDiscordClient, "canWrite" | "observe" | "replace" | "remove">;
34
48
  internalGuildId?: string;
35
49
  pollIntervalMs?: number;
36
50
  leaseMs?: number;
@@ -43,10 +57,10 @@ export declare class GuildCommandWorker {
43
57
  stop(): Promise<void>;
44
58
  getHealth(): Promise<GuildCommandWorkerHealth>;
45
59
  }
46
- type LiveRepository = Pick<GuildCommandRepository, "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation"> & Pick<MysqlGuildCommandRepository, "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation">;
60
+ type LiveRepository = Pick<GuildCommandRepository, "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation" | "retireRootObservation" | "isRetiredRootCommandId"> & Pick<MysqlGuildCommandRepository, "completeLive" | "isLeaseCurrent" | "reserveCreations" | "settleCreation" | "retireRootObservation" | "isRetiredRootCommandId">;
47
61
  export declare function reconcileLiveGuildCommands(input: {
48
62
  repository: LiveRepository;
49
- discord: Pick<GuildCommandDiscordClient, "observe" | "replace">;
63
+ discord: Pick<GuildCommandDiscordClient, "observe" | "replace" | "remove">;
50
64
  request: GuildCommandRequestRecord;
51
65
  projection: GuildCommandProjection;
52
66
  workerId: string;
@@ -54,6 +68,7 @@ export declare function reconcileLiveGuildCommands(input: {
54
68
  leaseMs: number;
55
69
  verificationDelayMs?: number;
56
70
  signal?: AbortSignal;
71
+ validateWrite?(): Promise<void>;
57
72
  }): Promise<{
58
73
  outcome: "completed" | "stale" | "fenced";
59
74
  diff: GuildCommandDiff;