@helyx/bot 0.4.1 → 0.4.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 (38) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +13 -0
  3. package/dist/cloud-connector.d.ts +7 -0
  4. package/dist/cloud-connector.js +118 -1
  5. package/dist/cloud-control-dispatcher.js +84 -0
  6. package/dist/control-server.d.ts +187 -5
  7. package/dist/control-server.js +673 -6
  8. package/dist/developer-operations-module.js +8 -0
  9. package/dist/discord-owned-message-service.d.ts +39 -0
  10. package/dist/discord-owned-message-service.js +138 -0
  11. package/dist/events.d.ts +70 -1
  12. package/dist/events.js +557 -5
  13. package/dist/index.js +61 -13
  14. package/dist/interactions.d.ts +3 -2
  15. package/dist/interactions.js +66 -31
  16. package/dist/module-logging.d.ts +1 -0
  17. package/dist/module-logging.js +19 -1
  18. package/dist/modules.d.ts +9 -0
  19. package/dist/modules.js +45 -0
  20. package/dist/resource-authorization.d.ts +7 -0
  21. package/dist/resource-authorization.js +12 -0
  22. package/dist/scheduled-task-runner.d.ts +130 -0
  23. package/dist/scheduled-task-runner.js +417 -0
  24. package/dist/shard-cloud-proxy.d.ts +4 -0
  25. package/dist/shard-cloud-proxy.js +34 -0
  26. package/dist/shard-contracts.d.ts +574 -23
  27. package/dist/shard-contracts.js +346 -0
  28. package/dist/shard-coordinator.d.ts +5 -3
  29. package/dist/shard-coordinator.js +85 -2
  30. package/dist/shard-router.d.ts +10 -0
  31. package/dist/shard-router.js +58 -0
  32. package/dist/shard-supervisor.d.ts +2 -1
  33. package/dist/shard-supervisor.js +187 -5
  34. package/dist/shard-worker.d.ts +8 -2
  35. package/dist/shard-worker.js +97 -16
  36. package/dist/support-module.d.ts +9 -0
  37. package/dist/support-module.js +172 -0
  38. package/package.json +9 -9
@@ -28,6 +28,63 @@ export class ShardControlRouter {
28
28
  executeDashboardAction(input) {
29
29
  return this.#route({ kind: "guild.module.action.execute", ...input });
30
30
  }
31
+ listManagedResources(input) {
32
+ return this.#route(shardControlOperationSchema.parse({
33
+ kind: "guild.module.resource.list",
34
+ ...input,
35
+ }));
36
+ }
37
+ readManagedResource(input) {
38
+ return this.#route({
39
+ kind: "guild.module.resource.read",
40
+ ...input,
41
+ });
42
+ }
43
+ listManagedResourceRelation(input) {
44
+ return this.#route(shardControlOperationSchema.parse({
45
+ kind: "guild.module.resource.relation.list",
46
+ ...input,
47
+ }));
48
+ }
49
+ previewManagedResource(input) {
50
+ return this.#route(shardControlOperationSchema.parse({
51
+ kind: "guild.module.resource.preview",
52
+ ...input,
53
+ }));
54
+ }
55
+ createManagedResource(input) {
56
+ return this.#route(shardControlOperationSchema.parse({
57
+ kind: "guild.module.resource.create",
58
+ ...input,
59
+ }));
60
+ }
61
+ updateManagedResource(input) {
62
+ return this.#route(shardControlOperationSchema.parse({
63
+ kind: "guild.module.resource.update",
64
+ ...input,
65
+ }));
66
+ }
67
+ deleteManagedResource(input) {
68
+ return this.#route({
69
+ kind: "guild.module.resource.delete",
70
+ ...input,
71
+ });
72
+ }
73
+ executeManagedResourceAction(input) {
74
+ return this.#route({
75
+ kind: "guild.module.resource.action.execute",
76
+ ...input,
77
+ });
78
+ }
79
+ sendSupportNotification(input) {
80
+ if (!this.input.notificationGuildId)
81
+ throw new BotControlTransportError(404, "The support notification channel is not configured");
82
+ return this.#route({
83
+ kind: "support.notification.send",
84
+ guildId: this.input.notificationGuildId,
85
+ ...input,
86
+ });
87
+ }
31
88
  listPrivacyProviders() {
32
89
  return this.#routeLeader({ kind: "privacy.providers.list" });
33
90
  }
@@ -171,6 +228,7 @@ function unwrapResponse(response) {
171
228
  const accepted = shardControlResponseSchema.parse(response);
172
229
  if (!accepted.ok)
173
230
  throw new BotControlTransportError(accepted.status === 400 ||
231
+ accepted.status === 403 ||
174
232
  accepted.status === 404 ||
175
233
  accepted.status === 409 ||
176
234
  accepted.status === 504
@@ -1,10 +1,11 @@
1
1
  import type { BotEnvironment } from "@helyx/config";
2
2
  import { type DatabasePool } from "@helyx/database";
3
- import type { ModuleDefinition } from "@helyx/sdk";
3
+ import type { ModuleDefinition, ScheduledTaskExecutionResult } from "@helyx/sdk";
4
4
  import { type ShardCloudRequest, type ShardIdentity, type ShardProcessHealth } from "./shard-contracts.js";
5
5
  import { type ShardCoordinator, type ShardGenerationDescriptor, type ShardLeaderLease, type ShardLease } from "./shard-coordinator.js";
6
6
  import type { BotHandle } from "./index.js";
7
7
  export declare const STEADY_STATE_LEASE_LOSS_BOUND_MS: number;
8
+ export declare function deferScheduledRoutingFailures(route: () => Promise<ScheduledTaskExecutionResult>): Promise<ScheduledTaskExecutionResult>;
8
9
  export declare function startShardSupervisor(environment: BotEnvironment, processEnvironment: NodeJS.ProcessEnv, workerEntry: string): Promise<BotHandle>;
9
10
  export declare function buildShardWorkerEnvironment(source: NodeJS.ProcessEnv, identity: ShardIdentity, restartCount: number): NodeJS.ProcessEnv;
10
11
  export declare function shardRuntimeCompositionHash(modules: readonly ModuleDefinition[], internalGuildId?: string): string;
@@ -13,13 +13,15 @@ import { BotControlService, createBotControlServer, mapBotControlError, } from "
13
13
  import { createManagementModule } from "./management-module.js";
14
14
  import { buildApplicationCommandDefinitions } from "./interactions.js";
15
15
  import { resolveModulePackageSpecifiers } from "./module-configuration.js";
16
- import { loadConfiguredModulePackages } from "./modules.js";
16
+ import { deriveSecureRandomSelectionKey, loadConfiguredModulePackages, platformServiceOptions, secureRandomSelectionWorkerKeyName, } from "./modules.js";
17
17
  import { createPermissionsModule } from "./permissions-module.js";
18
- import { SHARD_CONTROL_MAX_BYTES, SHARD_HEALTH_STALE_MS, assertShardMessageSize, controlOperationGuildId, executeShardControlOperation, gatewayBotInformationSchema, shardCloudRequestSchema, shardHealthSnapshotSchema, workerMessageSchema, } from "./shard-contracts.js";
18
+ import { SHARD_CONTROL_MAX_BYTES, SHARD_HEALTH_STALE_MS, assertShardMessageSize, controlOperationGuildId, executeShardControlOperation, gatewayBotInformationSchema, guildIdToShardId, shardCloudRequestSchema, shardHealthSnapshotSchema, workerMessageSchema, } from "./shard-contracts.js";
19
19
  import { RedisShardCoordinator, } from "./shard-coordinator.js";
20
20
  import { ShardControlRouter } from "./shard-router.js";
21
21
  import { createDeveloperOperationsModule } from "./developer-operations-module.js";
22
22
  import { SupervisorCloudDiscordDirectory } from "./supervisor-cloud-directory.js";
23
+ import { createSupportModule } from "./support-module.js";
24
+ import { createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
23
25
  const LEASE_RENEW_INTERVAL_MS = 10_000;
24
26
  const STARTUP_LEASE_RENEW_INTERVAL_MS = 5_000;
25
27
  const LEASE_VALIDATION_TIMEOUT_MS = 1_000;
@@ -29,6 +31,18 @@ const WORKER_SHUTDOWN_MS = 5_000;
29
31
  const MAX_RESTARTS_PER_WINDOW = 5;
30
32
  const RESTART_WINDOW_MS = 10 * 60 * 1_000;
31
33
  export const STEADY_STATE_LEASE_LOSS_BOUND_MS = LEASE_RENEW_INTERVAL_MS + LEASE_VALIDATION_TIMEOUT_MS + WORKER_SHUTDOWN_MS;
34
+ export async function deferScheduledRoutingFailures(route) {
35
+ try {
36
+ return await route();
37
+ }
38
+ catch {
39
+ return {
40
+ outcome: "deferred",
41
+ delayMs: 1_000,
42
+ safeCode: "routing_unavailable",
43
+ };
44
+ }
45
+ }
32
46
  export async function startShardSupervisor(environment, processEnvironment, workerEntry) {
33
47
  if (environment.HELYX_SHARD_MODE === "off")
34
48
  throw new Error("Shard supervisor requires sharding to be enabled");
@@ -65,6 +79,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
65
79
  const localLeases = new Map();
66
80
  const healthByShard = new Map();
67
81
  const pendingControl = new Map();
82
+ const pendingScheduled = new Map();
68
83
  const restartHistory = new Map();
69
84
  const restartCounts = new Map();
70
85
  const workers = new Map();
@@ -75,6 +90,9 @@ export async function startShardSupervisor(environment, processEnvironment, work
75
90
  let shardCount = 1;
76
91
  let privacyService;
77
92
  let manager;
93
+ let scheduledTasks;
94
+ let scheduledModules = [];
95
+ let workersRoutable = false;
78
96
  let promoteToLeader = () => Promise.resolve(false);
79
97
  try {
80
98
  await checkDatabaseConnection(database);
@@ -112,6 +130,9 @@ export async function startShardSupervisor(environment, processEnvironment, work
112
130
  getGuildResources: () => Promise.reject(new Error("Guild resources are worker-owned")),
113
131
  sendToChannel: () => Promise.reject(new Error("Guild resources are worker-owned")),
114
132
  deleteChannelMessage: () => Promise.reject(new Error("Guild resources are worker-owned")),
133
+ editChannelMessage: () => Promise.reject(new Error("Guild resources are worker-owned")),
134
+ addChannelMessageReaction: () => Promise.reject(new Error("Guild resources are worker-owned")),
135
+ resolveActor: () => Promise.reject(new Error("Guild resources are worker-owned")),
115
136
  };
116
137
  const permissionsModule = createPermissionsModule(featureModules);
117
138
  const managementModule = createManagementModule(featureModules, unavailableResources);
@@ -123,12 +144,15 @@ export async function startShardSupervisor(environment, processEnvironment, work
123
144
  configuredOwnerUserIds: environment.HELYX_OWNER_USER_IDS,
124
145
  })
125
146
  : undefined;
147
+ const supportModule = createSupportModule(cloudConnector);
126
148
  const publicModules = [
127
149
  managementModule,
128
150
  permissionsModule,
151
+ supportModule,
129
152
  ...(cloudManagementModule ? [cloudManagementModule] : []),
130
153
  ...featureModules,
131
154
  ];
155
+ scheduledModules = publicModules;
132
156
  const operationsModule = environment.DISCORD_INTERNAL_GUILD_ID
133
157
  ? createDeveloperOperationsModule({
134
158
  internalGuildId: environment.DISCORD_INTERNAL_GUILD_ID,
@@ -142,6 +166,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
142
166
  coreFunctions: [
143
167
  managementModule,
144
168
  permissionsModule,
169
+ supportModule,
145
170
  ...(cloudManagementModule ? [cloudManagementModule] : []),
146
171
  ].map(({ manifest }) => manifest),
147
172
  modules: featureModules.map(({ manifest }) => manifest),
@@ -160,7 +185,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
160
185
  assertStartupLeaseHealthy();
161
186
  privacyDatabase = await createPrivacyDatabase(environment, database);
162
187
  assertStartupLeaseHealthy();
163
- const services = new Map(createPlatformServices(database, publicModules));
188
+ const services = new Map(createPlatformServices(database, publicModules, platformServiceOptions(environment, publicModules)));
164
189
  privacyService = new BotControlService(database, publicModules, unavailableResources, new ServiceRegistry(services), privacyDatabase
165
190
  ? new PostgresPrivacyProviderExecutor({
166
191
  readPool: database,
@@ -176,6 +201,11 @@ export async function startShardSupervisor(environment, processEnvironment, work
176
201
  privacy: privacyService,
177
202
  localLeaderLease: leaderLease,
178
203
  ...(coordinator ? { coordinator } : {}),
204
+ ...(environment.DISCORD_INTERNAL_GUILD_ID
205
+ ? {
206
+ notificationGuildId: environment.DISCORD_INTERNAL_GUILD_ID,
207
+ }
208
+ : {}),
179
209
  }));
180
210
  await startLeaderSingletons();
181
211
  assertStartupLeaseHealthy();
@@ -194,7 +224,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
194
224
  onError: (error) => logger.error({ event: "shard.redis_error", error: safeError(error) }, "Shard coordination failed"),
195
225
  });
196
226
  supervisorId = coordinator.supervisorId;
197
- await coordinator.start((targetShardId, request) => dispatchLocal(targetShardId, request), (request) => executeLeaderControl(request), (request) => executeLeaderCloud(request));
227
+ await coordinator.start((targetShardId, request) => dispatchLocal(targetShardId, request), (request) => executeLeaderControl(request), (request) => executeLeaderCloud(request), (targetShardId, request) => dispatchLocalScheduled(targetShardId, request));
198
228
  deploymentOrder = await coordinator.assertGeneration(generationDescriptor, deploymentOrder);
199
229
  leaderLease = await coordinator.acquireLeader(generationDescriptor, deploymentOrder);
200
230
  startStartupLeaseMaintenance();
@@ -249,6 +279,11 @@ export async function startShardSupervisor(environment, processEnvironment, work
249
279
  ...(privacyService ? { privacy: privacyService } : {}),
250
280
  ...(leaderLease ? { localLeaderLease: leaderLease } : {}),
251
281
  ...(coordinator ? { coordinator } : {}),
282
+ ...(environment.DISCORD_INTERNAL_GUILD_ID
283
+ ? {
284
+ notificationGuildId: environment.DISCORD_INTERNAL_GUILD_ID,
285
+ }
286
+ : {}),
252
287
  });
253
288
  if (!leaderLease)
254
289
  cloudConnector.setControlHandler(router);
@@ -263,6 +298,9 @@ export async function startShardSupervisor(environment, processEnvironment, work
263
298
  });
264
299
  await reserveInitialSessionStarts(claimedIds.length, gateway);
265
300
  await spawnInIdentifyBuckets(claimedIds, gateway.session_start_limit.max_concurrency);
301
+ workersRoutable = true;
302
+ if (leaderLease)
303
+ startScheduledTasks();
266
304
  assertStartupLeaseHealthy();
267
305
  if (environment.HELYX_BOT_CONTROL_SECRET) {
268
306
  controlServer = await createBotControlServer({
@@ -442,6 +480,15 @@ export async function startShardSupervisor(environment, processEnvironment, work
442
480
  pending.resolve(message.data);
443
481
  return;
444
482
  }
483
+ if (message.data.type === "helyx.scheduled.response") {
484
+ const pending = pendingScheduled.get(message.data.requestId);
485
+ if (!pending || pending.shardId !== shardId)
486
+ return;
487
+ pendingScheduled.delete(message.data.requestId);
488
+ clearTimeout(pending.timer);
489
+ pending.resolve(message.data);
490
+ return;
491
+ }
445
492
  if (message.data.type === "helyx.cloud.request") {
446
493
  const response = leaderLease
447
494
  ? await executeLeaderCloud(message.data)
@@ -478,6 +525,24 @@ export async function startShardSupervisor(environment, processEnvironment, work
478
525
  });
479
526
  });
480
527
  }
528
+ function dispatchLocalScheduled(shardId, request) {
529
+ const shard = workers.get(shardId);
530
+ if (!shard?.process)
531
+ return Promise.resolve(unavailableScheduled(request.requestId));
532
+ return new Promise((resolve) => {
533
+ const timer = setTimeout(() => {
534
+ pendingScheduled.delete(request.requestId);
535
+ resolve(unavailableScheduled(request.requestId, "shard_timeout"));
536
+ }, Math.max(1, new Date(request.deadlineAt).getTime() - Date.now()));
537
+ timer.unref();
538
+ pendingScheduled.set(request.requestId, { shardId, resolve, timer });
539
+ void shard.send(request).catch(() => {
540
+ clearTimeout(timer);
541
+ pendingScheduled.delete(request.requestId);
542
+ resolve(unavailableScheduled(request.requestId));
543
+ });
544
+ });
545
+ }
481
546
  async function executeLeaderControl(request) {
482
547
  if (!leaderLease || !privacyService)
483
548
  return unavailableControl(request.requestId);
@@ -549,6 +614,23 @@ export async function startShardSupervisor(environment, processEnvironment, work
549
614
  return failedCloud(parsed.requestId, "Cloud request expired");
550
615
  result = await cloudConnector.rotate();
551
616
  break;
617
+ case "support.bug.create":
618
+ if (cloudRequestExpired(parsed))
619
+ return failedCloud(parsed.requestId, "Support request expired");
620
+ result = await cloudConnector.submitBug({
621
+ operation: "support.bug.create.v1",
622
+ reporterDiscordUserId: parsed.operation.reporterDiscordUserId,
623
+ guildId: parsed.operation.guildId,
624
+ moduleOrArea: parsed.operation.moduleOrArea,
625
+ summary: parsed.operation.summary,
626
+ whatHappened: parsed.operation.whatHappened,
627
+ ...(parsed.operation.extraDetail
628
+ ? { extraDetail: parsed.operation.extraDetail }
629
+ : {}),
630
+ interactionId: parsed.operation.interactionId,
631
+ correlationId: parsed.operation.correlationId,
632
+ });
633
+ break;
552
634
  }
553
635
  const response = {
554
636
  version: 1,
@@ -700,6 +782,72 @@ export async function startShardSupervisor(environment, processEnvironment, work
700
782
  retentionTimer.unref();
701
783
  await cloudConnector?.start();
702
784
  cloudStarted = true;
785
+ if (workersRoutable)
786
+ startScheduledTasks();
787
+ }
788
+ function startScheduledTasks() {
789
+ if (scheduledTasks || !leaderLease || !workersRoutable)
790
+ return;
791
+ scheduledTasks = createScheduledTaskRunner({
792
+ database,
793
+ modules: scheduledModules,
794
+ route: routeScheduledTask,
795
+ logger,
796
+ workerId: `shard-leader:${leaderLease.generation}:${String(leaderLease.fencingToken)}`,
797
+ });
798
+ scheduledTasks.start();
799
+ }
800
+ async function routeScheduledTask(task, signal) {
801
+ if (!task.guildId)
802
+ return {
803
+ outcome: "requires_review",
804
+ safeCode: "guild_route_required",
805
+ };
806
+ if (!leaderLease || signal.aborted)
807
+ return {
808
+ outcome: "deferred",
809
+ delayMs: 1_000,
810
+ safeCode: "leader_unavailable",
811
+ };
812
+ return deferScheduledRoutingFailures(async () => {
813
+ const shardId = guildIdToShardId(task.guildId, shardCount);
814
+ const lease = localLeases.get(shardId) ??
815
+ (coordinator ? await coordinator.getShardLease(shardId) : undefined);
816
+ if (!lease || lease.generation !== generationDescriptor.generation)
817
+ return {
818
+ outcome: "deferred",
819
+ delayMs: 1_000,
820
+ safeCode: "shard_unavailable",
821
+ };
822
+ const request = {
823
+ version: 1,
824
+ type: "helyx.scheduled.request",
825
+ requestId: randomUUID(),
826
+ deadlineAt: new Date(Date.now() + 10_000).toISOString(),
827
+ identity: {
828
+ version: 1,
829
+ mode: shardMode,
830
+ scope: environment.HELYX_SHARD_SCOPE,
831
+ generation: lease.generation,
832
+ deploymentOrder: lease.deploymentOrder,
833
+ supervisorId: lease.supervisorId,
834
+ shardId,
835
+ shardCount,
836
+ fencingToken: lease.fencingToken,
837
+ },
838
+ task: {
839
+ ...task,
840
+ guildId: task.guildId,
841
+ },
842
+ };
843
+ assertShardMessageSize(request);
844
+ const response = lease.supervisorId === supervisorId
845
+ ? await dispatchLocalScheduled(shardId, request)
846
+ : coordinator
847
+ ? await coordinator.routeRemoteScheduled(lease, request)
848
+ : unavailableScheduled(request.requestId);
849
+ return response.result;
850
+ });
703
851
  }
704
852
  function startStartupLeaseMaintenance() {
705
853
  if (!coordinator || startupLeaseTimer)
@@ -749,6 +897,11 @@ export async function startShardSupervisor(environment, processEnvironment, work
749
897
  }
750
898
  async function buildHealth() {
751
899
  const now = Date.now();
900
+ const scheduler = scheduledTasks
901
+ ? await withTimeout(scheduledTasks.getHealth(), LEASE_VALIDATION_TIMEOUT_MS)
902
+ .then(schedulerOperationalHealth)
903
+ .catch(() => ({ status: "unavailable" }))
904
+ : { status: "unavailable" };
752
905
  let redisReady = true;
753
906
  if (coordinator)
754
907
  await withTimeout(coordinator.checkReady(), LEASE_VALIDATION_TIMEOUT_MS).catch(() => {
@@ -802,7 +955,8 @@ export async function startShardSupervisor(environment, processEnvironment, work
802
955
  ? "stale"
803
956
  : readyShardCount === shardCount &&
804
957
  coordinationRedis !== "unavailable" &&
805
- databaseHealthy
958
+ databaseHealthy &&
959
+ scheduler.status === "available"
806
960
  ? "healthy"
807
961
  : readyShardCount === 0
808
962
  ? "unavailable"
@@ -818,6 +972,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
818
972
  redis: coordinationRedis,
819
973
  database: databaseHealthy ? "healthy" : "unavailable",
820
974
  },
975
+ scheduler,
821
976
  ...(sessionBudget ? { sessionStarts: sessionBudget } : {}),
822
977
  shards,
823
978
  });
@@ -832,6 +987,14 @@ export async function startShardSupervisor(environment, processEnvironment, work
832
987
  clearInterval(startupLeaseTimer);
833
988
  if (retentionTimer)
834
989
  clearInterval(retentionTimer);
990
+ workersRoutable = false;
991
+ await scheduledTasks?.stop().catch(() => undefined);
992
+ scheduledTasks = undefined;
993
+ for (const [requestId, pending] of pendingScheduled) {
994
+ clearTimeout(pending.timer);
995
+ pending.resolve(unavailableScheduled(requestId, "supervisor_stopping"));
996
+ }
997
+ pendingScheduled.clear();
835
998
  await controlServer?.close().catch(() => undefined);
836
999
  await Promise.allSettled([...workers.values()].map(async (shard) => {
837
1000
  if (!shard.process)
@@ -965,6 +1128,7 @@ export function buildShardWorkerEnvironment(source, identity, restartCount) {
965
1128
  "DISCORD_TOKEN",
966
1129
  "DISCORD_CLIENT_ID",
967
1130
  "DISCORD_INTERNAL_GUILD_ID",
1131
+ "HELYX_SUPPORT_STAFF_CHANNEL_ID",
968
1132
  "HELYX_MODULE_PACKAGES",
969
1133
  "HELYX_MODULE_CATALOGUE_PATH",
970
1134
  "HELYX_CLOUD_CONNECT_ENABLED",
@@ -988,8 +1152,14 @@ export function buildShardWorkerEnvironment(source, identity, restartCount) {
988
1152
  "TMP",
989
1153
  ]);
990
1154
  const environment = Object.fromEntries(Object.entries(source).filter(([key, value]) => allowed.has(key) && value !== undefined));
1155
+ const selectionKey = source.HELYX_DATA_ENCRYPTION_KEY
1156
+ ? Buffer.from(deriveSecureRandomSelectionKey(source.HELYX_DATA_ENCRYPTION_KEY)).toString("base64")
1157
+ : undefined;
991
1158
  return {
992
1159
  ...environment,
1160
+ ...(selectionKey
1161
+ ? { [secureRandomSelectionWorkerKeyName]: selectionKey }
1162
+ : {}),
993
1163
  SHARDING_MANAGER: "true",
994
1164
  SHARDING_MANAGER_MODE: "process",
995
1165
  SHARDS: String(identity.shardId),
@@ -1042,6 +1212,18 @@ function unavailableControl(requestId, status = 503) {
1042
1212
  : "The owning shard is temporarily unavailable",
1043
1213
  };
1044
1214
  }
1215
+ function unavailableScheduled(requestId, safeCode = "shard_unavailable") {
1216
+ return {
1217
+ version: 1,
1218
+ type: "helyx.scheduled.response",
1219
+ requestId,
1220
+ result: {
1221
+ outcome: "deferred",
1222
+ delayMs: 1_000,
1223
+ safeCode,
1224
+ },
1225
+ };
1226
+ }
1045
1227
  function failedCloud(requestId, message) {
1046
1228
  return {
1047
1229
  version: 1,
@@ -1,6 +1,7 @@
1
1
  import { type CommandRegistration } from "./interactions.js";
2
- import { type ShardIdentity } from "./shard-contracts.js";
2
+ import { type ShardIdentity, type ShardScheduledTaskRequest, type ShardScheduledTaskResponse } from "./shard-contracts.js";
3
3
  import { type BotHandle } from "./index.js";
4
+ import { type ScheduledTaskRoute } from "./scheduled-task-runner.js";
4
5
  export declare const WORKER_ORPHAN_TERMINATION_BOUND_MS: number;
5
6
  export declare function createLeaseHeartbeatWatchdog(onExpired: () => void, timeoutMs?: number): {
6
7
  heartbeat(): void;
@@ -17,8 +18,11 @@ export declare function startShardWorker(processEnvironment?: NodeJS.ProcessEnv,
17
18
  fencingToken: number;
18
19
  deploymentOrder?: number | undefined;
19
20
  }): Promise<BotHandle>;
21
+ export declare function executeShardScheduledTaskRequest(identity: ShardIdentity, request: ShardScheduledTaskRequest, route: ScheduledTaskRoute, now?: number): Promise<ShardScheduledTaskResponse>;
20
22
  export declare function parseShardWorkerLaunchContract(value: string | undefined): ShardIdentity;
21
23
  export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.ProcessEnv, identity: ShardIdentity): {
24
+ HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY?: string;
25
+ HELYX_DATA_ENCRYPTION_KEY: undefined;
22
26
  HELYX_SHARD_WORKERS_PER_REPLICA: number;
23
27
  HELYX_SHARD_SUPERVISOR_DATABASE_POOL_MAX: number | undefined;
24
28
  HELYX_SHARD_WORKER_DATABASE_POOL_MAX: number | undefined;
@@ -30,6 +34,7 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
30
34
  APPROVED_ROLES: string[];
31
35
  HELYX_MODULE_PACKAGES: string[];
32
36
  HELYX_MODULE_CATALOGUE_PATH: string | undefined;
37
+ HELYX_SUPPORT_STAFF_CHANNEL_ID: string | undefined;
33
38
  HELYX_BOT_CONTROL_PORT: number;
34
39
  HELYX_PRIVACY_ENABLED: boolean;
35
40
  HELYX_CLOUD_CONNECT_ENABLED: boolean;
@@ -45,9 +50,9 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
45
50
  LOG_LEVEL: "error" | "fatal" | "warn" | "info" | "debug" | "trace" | "silent";
46
51
  NODE_ENV: "development" | "test" | "production";
47
52
  HELYX_BOT_CONTROL_SECRET?: string | undefined;
53
+ HELYX_SUPPORT_SERVICE_SECRET?: string | undefined;
48
54
  HELYX_PRIVACY_EXECUTOR_DATABASE_URL?: string | undefined;
49
55
  HELYX_PRIVACY_EXECUTOR_DATABASE_SSL?: "disable" | "require" | "verify-full" | undefined;
50
- HELYX_DATA_ENCRYPTION_KEY?: string | undefined;
51
56
  HELYX_SHARD_COUNT?: number | undefined;
52
57
  HELYX_SHARD_REDIS_URL?: string | undefined;
53
58
  HELYX_SHARD_REDIS_NAMESPACE?: string | undefined;
@@ -58,5 +63,6 @@ export declare function shardWorkerCommandRegistration(input: {
58
63
  internalGuildId?: string;
59
64
  prepare: NonNullable<CommandRegistration["prepare"]>;
60
65
  operationsModuleId?: string;
66
+ supportModuleId: string;
61
67
  }): CommandRegistration;
62
68
  //# sourceMappingURL=shard-worker.d.ts.map