@helyx/bot 0.4.32 → 0.4.35
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/CHANGELOG.md +49 -0
- package/dist/cloud-management-module.js +5 -0
- package/dist/control-server.d.ts +29 -8
- package/dist/control-server.js +69 -36
- package/dist/dashboard-commands.d.ts +10 -0
- package/dist/dashboard-commands.js +47 -0
- package/dist/developer-operations-module.js +3 -0
- package/dist/discord-guild-health.d.ts +2 -1
- package/dist/discord-guild-health.js +6 -3
- package/dist/events.js +27 -13
- package/dist/index.js +43 -3
- package/dist/interactions.d.ts +3 -2
- package/dist/interactions.js +132 -45
- package/dist/management-module.js +5 -1
- package/dist/module-availability-control.d.ts +24 -0
- package/dist/module-availability-control.js +61 -0
- package/dist/modules.js +24 -1
- package/dist/nexus-commands.d.ts +13 -0
- package/dist/nexus-commands.js +100 -0
- package/dist/oauth-module.js +8 -3
- package/dist/oauth-service.js +1 -1
- package/dist/permissions-module.js +16 -4
- package/dist/scheduled-task-runner.js +7 -0
- package/dist/shard-contracts.d.ts +42 -0
- package/dist/shard-contracts.js +26 -0
- package/dist/shard-router.d.ts +3 -0
- package/dist/shard-router.js +8 -0
- package/dist/shard-supervisor.js +30 -3
- package/dist/shard-worker.d.ts +8 -2
- package/dist/shard-worker.js +4 -1
- package/dist/support-module.js +1 -0
- package/package.json +5 -4
package/dist/modules.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { orderModuleManifests, parseModuleManifest, } from "@helyx/module-manifest";
|
|
2
|
+
import { NEXUS_COMMAND_SPECS, NEXUS_ROOT_MODULE_ID } from "@helyx/sdk";
|
|
2
3
|
import { createHmac } from "node:crypto";
|
|
3
4
|
import { readFile } from "node:fs/promises";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
@@ -102,6 +103,28 @@ export function validateContributionParity(definition) {
|
|
|
102
103
|
throw new Error("Module requests an unsupported or duplicate Discord capability");
|
|
103
104
|
}
|
|
104
105
|
const executableCommands = (definition.interactions?.commands ?? []).map(({ name }) => name);
|
|
106
|
+
const nexusCommandPaths = new Set();
|
|
107
|
+
for (const command of definition.nexusCommands ?? []) {
|
|
108
|
+
const spec = NEXUS_COMMAND_SPECS.find(({ path }) => path === command.path);
|
|
109
|
+
if (!spec || spec.ownerModuleId !== definition.manifest.id) {
|
|
110
|
+
throw new Error(`Module ${definition.manifest.id} cannot contribute Nexus command ${command.path}`);
|
|
111
|
+
}
|
|
112
|
+
if (nexusCommandPaths.has(command.path)) {
|
|
113
|
+
throw new Error(`Duplicate Nexus command path for ${definition.manifest.id}: ${command.path}`);
|
|
114
|
+
}
|
|
115
|
+
nexusCommandPaths.add(command.path);
|
|
116
|
+
}
|
|
117
|
+
if (definition.manifest.id !== NEXUS_ROOT_MODULE_ID) {
|
|
118
|
+
executableCommands.push(...[...nexusCommandPaths].map((path) => `nexus.${path}`));
|
|
119
|
+
if (nexusCommandPaths.size > 0 &&
|
|
120
|
+
!definition.manifest.dependencies.some(({ id }) => id === NEXUS_ROOT_MODULE_ID)) {
|
|
121
|
+
throw new Error(`Module ${definition.manifest.id} contributes Nexus commands without depending on Nexus Core`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (definition.manifest.id !== NEXUS_ROOT_MODULE_ID &&
|
|
125
|
+
definition.interactions?.commands?.some(({ name }) => name === "nexus")) {
|
|
126
|
+
throw new Error("Nexus Core must be the sole owner of /nexus");
|
|
127
|
+
}
|
|
105
128
|
const executableEvents = [
|
|
106
129
|
...(definition.events?.privilegedMessageCreate ?? []),
|
|
107
130
|
...(definition.events?.guildMessageCreate ?? []),
|
|
@@ -135,7 +158,7 @@ export function validateContributionParity(definition) {
|
|
|
135
158
|
function validatePrivacyParity(definition) {
|
|
136
159
|
if (definition.manifest.manifestVersion === "0.1") {
|
|
137
160
|
if (definition.privacy !== undefined) {
|
|
138
|
-
throw new Error("Module executable privacy provider requires manifest version 0.2");
|
|
161
|
+
throw new Error("Module executable privacy provider requires manifest version 0.2 or later");
|
|
139
162
|
}
|
|
140
163
|
return;
|
|
141
164
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CommandContribution, type ModuleDefinition } from "@helyx/sdk";
|
|
2
|
+
export interface CommandRouteOwner {
|
|
3
|
+
moduleId: string;
|
|
4
|
+
moduleName: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RegisteredCommand {
|
|
7
|
+
moduleId: string;
|
|
8
|
+
moduleName: string;
|
|
9
|
+
contribution: CommandContribution;
|
|
10
|
+
routeOwners?: ReadonlyMap<string, CommandRouteOwner>;
|
|
11
|
+
}
|
|
12
|
+
export declare function collectCommandRegistrations(modules: readonly ModuleDefinition[]): Map<string, RegisteredCommand>;
|
|
13
|
+
//# sourceMappingURL=nexus-commands.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { NEXUS_COMMAND_SPECS, NEXUS_ROOT_MODULE_ID, } from "@helyx/sdk";
|
|
2
|
+
export function collectCommandRegistrations(modules) {
|
|
3
|
+
const registrations = modules.flatMap((module) => (module.interactions?.commands ?? []).map((contribution) => ({
|
|
4
|
+
moduleId: module.manifest.id,
|
|
5
|
+
moduleName: module.manifest.name,
|
|
6
|
+
contribution,
|
|
7
|
+
})));
|
|
8
|
+
const nexusContributions = modules.flatMap((module) => (module.nexusCommands ?? []).map((contribution) => ({
|
|
9
|
+
moduleId: module.manifest.id,
|
|
10
|
+
moduleName: module.manifest.name,
|
|
11
|
+
contribution,
|
|
12
|
+
})));
|
|
13
|
+
if (nexusContributions.length > 0) {
|
|
14
|
+
const roots = registrations.filter(({ contribution }) => contribution.name === "nexus");
|
|
15
|
+
if (roots.length !== 1 || roots[0].moduleId !== NEXUS_ROOT_MODULE_ID) {
|
|
16
|
+
throw new Error("Nexus Core must be the sole owner of /nexus");
|
|
17
|
+
}
|
|
18
|
+
const root = roots[0];
|
|
19
|
+
if (root.contribution.execute ||
|
|
20
|
+
root.contribution.options?.length ||
|
|
21
|
+
root.contribution.subcommands?.length ||
|
|
22
|
+
root.contribution.subcommandGroups?.length) {
|
|
23
|
+
throw new Error("Nexus Core /nexus must be composed from Nexus commands");
|
|
24
|
+
}
|
|
25
|
+
const composed = composeNexusCommand(root, nexusContributions);
|
|
26
|
+
registrations.splice(registrations.indexOf(root), 1, composed);
|
|
27
|
+
}
|
|
28
|
+
return uniqueRegistrations(registrations);
|
|
29
|
+
}
|
|
30
|
+
function composeNexusCommand(root, contributions) {
|
|
31
|
+
const byPath = new Map(contributions.map((item) => {
|
|
32
|
+
const spec = NEXUS_COMMAND_SPECS.find(({ path }) => path === item.contribution.path);
|
|
33
|
+
if (!spec || spec.ownerModuleId !== item.moduleId) {
|
|
34
|
+
throw new Error(`Module ${item.moduleId} cannot contribute Nexus command ${item.contribution.path}`);
|
|
35
|
+
}
|
|
36
|
+
return [item.contribution.path, item];
|
|
37
|
+
}));
|
|
38
|
+
if (byPath.size !== contributions.length) {
|
|
39
|
+
throw new Error("Duplicate Nexus command path");
|
|
40
|
+
}
|
|
41
|
+
const subcommands = [];
|
|
42
|
+
const groups = new Map();
|
|
43
|
+
const routeOwners = new Map();
|
|
44
|
+
for (const spec of NEXUS_COMMAND_SPECS) {
|
|
45
|
+
const item = byPath.get(spec.path);
|
|
46
|
+
if (!item)
|
|
47
|
+
continue;
|
|
48
|
+
routeOwners.set(spec.path, {
|
|
49
|
+
moduleId: item.moduleId,
|
|
50
|
+
moduleName: item.moduleName,
|
|
51
|
+
});
|
|
52
|
+
const [first, second] = spec.path.split(".");
|
|
53
|
+
const command = toSubcommand(item.contribution, second ?? first);
|
|
54
|
+
if (!second) {
|
|
55
|
+
subcommands.push(command);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const current = groups.get(first);
|
|
59
|
+
const group = current ?? {
|
|
60
|
+
name: first,
|
|
61
|
+
description: "groupDescription" in spec
|
|
62
|
+
? spec.groupDescription
|
|
63
|
+
: `Manage Nexus ${first}`,
|
|
64
|
+
subcommands: [],
|
|
65
|
+
};
|
|
66
|
+
groups.set(first, {
|
|
67
|
+
...group,
|
|
68
|
+
subcommands: [...group.subcommands, command],
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...root,
|
|
73
|
+
contribution: {
|
|
74
|
+
...root.contribution,
|
|
75
|
+
subcommands,
|
|
76
|
+
subcommandGroups: [...groups.values()],
|
|
77
|
+
},
|
|
78
|
+
routeOwners,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function toSubcommand(contribution, name) {
|
|
82
|
+
const subcommand = {
|
|
83
|
+
...contribution,
|
|
84
|
+
name,
|
|
85
|
+
permissionId: `nexus.${contribution.path}`,
|
|
86
|
+
};
|
|
87
|
+
delete subcommand.path;
|
|
88
|
+
return subcommand;
|
|
89
|
+
}
|
|
90
|
+
function uniqueRegistrations(items) {
|
|
91
|
+
const result = new Map();
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
const key = item.contribution.name;
|
|
94
|
+
if (result.has(key))
|
|
95
|
+
throw new Error(`Duplicate command name: ${key}`);
|
|
96
|
+
result.set(key, item);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=nexus-commands.js.map
|
package/dist/oauth-module.js
CHANGED
|
@@ -43,6 +43,7 @@ export function createOAuthModule() {
|
|
|
43
43
|
{
|
|
44
44
|
name: "revoke",
|
|
45
45
|
description: "Revoke a service and delete the OAuth details Helyx stores for you.",
|
|
46
|
+
acknowledgement: "immediate-modal",
|
|
46
47
|
rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
|
|
47
48
|
execute: async (context) => {
|
|
48
49
|
const oauth = context.services.get(HELYX_SERVICE_NAMES.oauth);
|
|
@@ -155,9 +156,13 @@ export function createOAuthModule() {
|
|
|
155
156
|
componentsV2: {
|
|
156
157
|
text: [
|
|
157
158
|
{
|
|
158
|
-
content: result.
|
|
159
|
-
? `${providerName(result.provider)} access
|
|
160
|
-
:
|
|
159
|
+
content: result.outcome === "processing"
|
|
160
|
+
? `${providerName(result.provider)} access removal is already in progress. Helyx will finish it automatically.`
|
|
161
|
+
: result.outcome === "cleanup_pending"
|
|
162
|
+
? `${providerName(result.provider)} credentials were deleted. Helyx is still removing related roles and connection records.`
|
|
163
|
+
: result.providerRevocation === "completed"
|
|
164
|
+
? `${providerName(result.provider)} access has been removed from Helyx. Local credentials were deleted.`
|
|
165
|
+
: `${providerName(result.provider)} access has been removed from Helyx and local credentials were deleted. Helyx could not confirm provider-side revocation, so you may also remove Helyx from your ${providerName(result.provider)} connected-app settings.`,
|
|
161
166
|
},
|
|
162
167
|
],
|
|
163
168
|
},
|
package/dist/oauth-service.js
CHANGED
|
@@ -30,7 +30,7 @@ export class HttpOAuthService {
|
|
|
30
30
|
"content-type": "application/json",
|
|
31
31
|
},
|
|
32
32
|
body: JSON.stringify(body),
|
|
33
|
-
signal: AbortSignal.timeout(
|
|
33
|
+
signal: AbortSignal.timeout(15_000),
|
|
34
34
|
});
|
|
35
35
|
if (!response.ok)
|
|
36
36
|
throw new Error(`oauth_service_http_${response.status}`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineModule, HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
2
|
+
import { isCustomerSelectableModule } from "@helyx/module-manifest";
|
|
2
3
|
import { PermissionFlagsBits } from "discord.js";
|
|
3
4
|
import { canMutatePermissionPolicy, permissionMutationDenialMessage, } from "@helyx/core";
|
|
4
5
|
const MODULE_SELECT_ID = "helyx.permissions.module";
|
|
@@ -17,7 +18,9 @@ const MODULE_PAGE_SIZE = 25;
|
|
|
17
18
|
const TARGET_PAGE_SIZE = 24;
|
|
18
19
|
const VIOLET = 0x7c3aed;
|
|
19
20
|
export function createPermissionsModule(configurableModules) {
|
|
20
|
-
const modules = new Map(configurableModules
|
|
21
|
+
const modules = new Map(configurableModules
|
|
22
|
+
.filter((module) => isCustomerSelectableModule(module.manifest))
|
|
23
|
+
.map((module) => [module.manifest.id, module]));
|
|
21
24
|
const commandModules = [...modules.values()]
|
|
22
25
|
.filter((module) => commandTargets(module).length > 0)
|
|
23
26
|
.sort((left, right) => left.manifest.name.localeCompare(right.manifest.name, "en-GB"));
|
|
@@ -46,6 +49,7 @@ export function createPermissionsModule(configurableModules) {
|
|
|
46
49
|
{
|
|
47
50
|
name: "permissions",
|
|
48
51
|
description: "Configure access to Helyx modules and commands",
|
|
52
|
+
acknowledgement: "deferred-ephemeral",
|
|
49
53
|
registration: "global",
|
|
50
54
|
contexts: ["guild"],
|
|
51
55
|
integrationTypes: ["guild_install"],
|
|
@@ -359,9 +363,17 @@ function restrictedPolicy() {
|
|
|
359
363
|
};
|
|
360
364
|
}
|
|
361
365
|
function commandTargets(module) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
366
|
+
const targets = (module.interactions?.commands ?? []).flatMap((command) => {
|
|
367
|
+
const nested = [
|
|
368
|
+
...(command.subcommands ?? []).map((subcommand) => `${command.name}.${subcommand.name}`),
|
|
369
|
+
...(command.subcommandGroups ?? []).flatMap((group) => group.subcommands.map((subcommand) => `${command.name}.${group.name}.${subcommand.name}`)),
|
|
370
|
+
];
|
|
371
|
+
if (nested.length > 0)
|
|
372
|
+
return nested;
|
|
373
|
+
return [command.name];
|
|
374
|
+
});
|
|
375
|
+
targets.push(...(module.nexusCommands ?? []).map((command) => `nexus.${command.path}`));
|
|
376
|
+
return [...new Set(targets)];
|
|
365
377
|
}
|
|
366
378
|
function modulePickerResponse(modules, requestedPage) {
|
|
367
379
|
const pages = pageCount(modules.length, MODULE_PAGE_SIZE);
|
|
@@ -321,6 +321,13 @@ export function createLocalScheduledTaskRoute(input) {
|
|
|
321
321
|
const module = modules.get(task.moduleId);
|
|
322
322
|
if (!module)
|
|
323
323
|
return deferred("module_unavailable", DEFAULT_POLL_INTERVAL_MS * 30);
|
|
324
|
+
if (typeof input.services.has === "function" &&
|
|
325
|
+
input.services.has(HELYX_SERVICE_NAMES.moduleAvailability)) {
|
|
326
|
+
const availability = input.services.get(HELYX_SERVICE_NAMES.moduleAvailability);
|
|
327
|
+
if (typeof availability?.isAvailable === "function" &&
|
|
328
|
+
!(await availability.isAvailable(task.moduleId)))
|
|
329
|
+
return deferred("module_globally_disabled", DEFAULT_POLL_INTERVAL_MS * 30);
|
|
330
|
+
}
|
|
324
331
|
if (task.guildId) {
|
|
325
332
|
const enabled = await input.services
|
|
326
333
|
.get(HELYX_SERVICE_NAMES.installations)
|
|
@@ -359,6 +359,20 @@ export declare const shardControlOperationSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
359
359
|
expectedRevision: z.ZodNumber;
|
|
360
360
|
value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
|
|
361
361
|
actorAuthenticatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
362
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
363
|
+
kind: z.ZodLiteral<"oauth.consumer.cleanup">;
|
|
364
|
+
consumerBindingId: z.ZodUUID;
|
|
365
|
+
moduleId: z.ZodString;
|
|
366
|
+
provider: z.ZodEnum<{
|
|
367
|
+
youtube: "youtube";
|
|
368
|
+
twitch: "twitch";
|
|
369
|
+
}>;
|
|
370
|
+
guildId: z.ZodString;
|
|
371
|
+
resourceType: z.ZodString;
|
|
372
|
+
resourceId: z.ZodString;
|
|
373
|
+
subjectUserId: z.ZodString;
|
|
374
|
+
contractVersion: z.ZodNumber;
|
|
375
|
+
correlationId: z.ZodString;
|
|
362
376
|
}, z.core.$strict>, z.ZodObject<{
|
|
363
377
|
kind: z.ZodLiteral<"support.notification.send">;
|
|
364
378
|
guildId: z.ZodString;
|
|
@@ -698,6 +712,20 @@ export declare const shardControlRequestSchema: z.ZodObject<{
|
|
|
698
712
|
expectedRevision: z.ZodNumber;
|
|
699
713
|
value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
|
|
700
714
|
actorAuthenticatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
715
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
716
|
+
kind: z.ZodLiteral<"oauth.consumer.cleanup">;
|
|
717
|
+
consumerBindingId: z.ZodUUID;
|
|
718
|
+
moduleId: z.ZodString;
|
|
719
|
+
provider: z.ZodEnum<{
|
|
720
|
+
youtube: "youtube";
|
|
721
|
+
twitch: "twitch";
|
|
722
|
+
}>;
|
|
723
|
+
guildId: z.ZodString;
|
|
724
|
+
resourceType: z.ZodString;
|
|
725
|
+
resourceId: z.ZodString;
|
|
726
|
+
subjectUserId: z.ZodString;
|
|
727
|
+
contractVersion: z.ZodNumber;
|
|
728
|
+
correlationId: z.ZodString;
|
|
701
729
|
}, z.core.$strict>, z.ZodObject<{
|
|
702
730
|
kind: z.ZodLiteral<"support.notification.send">;
|
|
703
731
|
guildId: z.ZodString;
|
|
@@ -1542,6 +1570,20 @@ export declare const supervisorMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
1542
1570
|
expectedRevision: z.ZodNumber;
|
|
1543
1571
|
value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
|
|
1544
1572
|
actorAuthenticatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
1573
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1574
|
+
kind: z.ZodLiteral<"oauth.consumer.cleanup">;
|
|
1575
|
+
consumerBindingId: z.ZodUUID;
|
|
1576
|
+
moduleId: z.ZodString;
|
|
1577
|
+
provider: z.ZodEnum<{
|
|
1578
|
+
youtube: "youtube";
|
|
1579
|
+
twitch: "twitch";
|
|
1580
|
+
}>;
|
|
1581
|
+
guildId: z.ZodString;
|
|
1582
|
+
resourceType: z.ZodString;
|
|
1583
|
+
resourceId: z.ZodString;
|
|
1584
|
+
subjectUserId: z.ZodString;
|
|
1585
|
+
contractVersion: z.ZodNumber;
|
|
1586
|
+
correlationId: z.ZodString;
|
|
1545
1587
|
}, z.core.$strict>, z.ZodObject<{
|
|
1546
1588
|
kind: z.ZodLiteral<"support.notification.send">;
|
|
1547
1589
|
guildId: z.ZodString;
|
package/dist/shard-contracts.js
CHANGED
|
@@ -425,6 +425,20 @@ export const shardControlOperationSchema = z.discriminatedUnion("kind", [
|
|
|
425
425
|
...mutationContext.shape,
|
|
426
426
|
})
|
|
427
427
|
.strict(),
|
|
428
|
+
z
|
|
429
|
+
.object({
|
|
430
|
+
kind: z.literal("oauth.consumer.cleanup"),
|
|
431
|
+
consumerBindingId: z.uuid(),
|
|
432
|
+
moduleId,
|
|
433
|
+
provider: z.enum(["youtube", "twitch"]),
|
|
434
|
+
guildId: snowflake,
|
|
435
|
+
resourceType,
|
|
436
|
+
resourceId,
|
|
437
|
+
subjectUserId: snowflake,
|
|
438
|
+
contractVersion: z.number().int().min(1).max(32_767),
|
|
439
|
+
correlationId: z.string().trim().min(1).max(200),
|
|
440
|
+
})
|
|
441
|
+
.strict(),
|
|
428
442
|
z
|
|
429
443
|
.object({
|
|
430
444
|
kind: z.literal("support.notification.send"),
|
|
@@ -1111,6 +1125,18 @@ export async function executeShardControlOperation(handler, operation) {
|
|
|
1111
1125
|
? { actorAuthenticatedAt: operation.actorAuthenticatedAt }
|
|
1112
1126
|
: {}),
|
|
1113
1127
|
});
|
|
1128
|
+
case "oauth.consumer.cleanup":
|
|
1129
|
+
return handler.cleanupOAuthConsumer({
|
|
1130
|
+
consumerBindingId: operation.consumerBindingId,
|
|
1131
|
+
moduleId: operation.moduleId,
|
|
1132
|
+
provider: operation.provider,
|
|
1133
|
+
guildId: operation.guildId,
|
|
1134
|
+
resourceType: operation.resourceType,
|
|
1135
|
+
resourceId: operation.resourceId,
|
|
1136
|
+
subjectUserId: operation.subjectUserId,
|
|
1137
|
+
contractVersion: operation.contractVersion,
|
|
1138
|
+
correlationId: operation.correlationId,
|
|
1139
|
+
});
|
|
1114
1140
|
case "support.notification.send": {
|
|
1115
1141
|
return handler.sendSupportNotification({
|
|
1116
1142
|
notificationId: operation.notificationId,
|
package/dist/shard-router.d.ts
CHANGED
|
@@ -11,10 +11,13 @@ export declare class ShardControlRouter implements BotControlHandler {
|
|
|
11
11
|
localLeases: ReadonlyMap<number, ShardLease>;
|
|
12
12
|
dispatchLocal: LocalDispatcher;
|
|
13
13
|
privacy?: Pick<BotControlHandler, "listPrivacyProviders" | "capturePrivacy" | "erasePrivacy">;
|
|
14
|
+
availability?: Pick<BotControlHandler, "listAvailabilityModules">;
|
|
14
15
|
localLeaderLease?: ShardLeaderLease;
|
|
15
16
|
coordinator?: ShardCoordinator;
|
|
16
17
|
notificationGuildId?: string;
|
|
17
18
|
});
|
|
19
|
+
cleanupOAuthConsumer(input: Parameters<BotControlHandler["cleanupOAuthConsumer"]>[0]): ReturnType<BotControlHandler["cleanupOAuthConsumer"]>;
|
|
20
|
+
listAvailabilityModules(): ReturnType<BotControlHandler["listAvailabilityModules"]>;
|
|
18
21
|
getGuildState(guildId: string): ReturnType<BotControlHandler["getGuildState"]>;
|
|
19
22
|
canAccessDashboard(input: Parameters<BotControlHandler["canAccessDashboard"]>[0]): ReturnType<BotControlHandler["canAccessDashboard"]>;
|
|
20
23
|
getGuildAccessPolicy(input: Parameters<BotControlHandler["getGuildAccessPolicy"]>[0]): ReturnType<BotControlHandler["getGuildAccessPolicy"]>;
|
package/dist/shard-router.js
CHANGED
|
@@ -12,6 +12,14 @@ export class ShardControlRouter {
|
|
|
12
12
|
constructor(input) {
|
|
13
13
|
this.input = input;
|
|
14
14
|
}
|
|
15
|
+
cleanupOAuthConsumer(input) {
|
|
16
|
+
return this.#route({ kind: "oauth.consumer.cleanup", ...input });
|
|
17
|
+
}
|
|
18
|
+
listAvailabilityModules() {
|
|
19
|
+
if (!this.input.availability)
|
|
20
|
+
return Promise.reject(new BotControlTransportError(503, "Module availability catalogue is unavailable"));
|
|
21
|
+
return this.input.availability.listAvailabilityModules();
|
|
22
|
+
}
|
|
15
23
|
getGuildState(guildId) {
|
|
16
24
|
return this.#route({ kind: "guild.state.get", guildId });
|
|
17
25
|
}
|
package/dist/shard-supervisor.js
CHANGED
|
@@ -23,6 +23,7 @@ import { createDeveloperOperationsModule } from "./developer-operations-module.j
|
|
|
23
23
|
import { SupervisorCloudDiscordDirectory } from "./supervisor-cloud-directory.js";
|
|
24
24
|
import { createSupportModule } from "./support-module.js";
|
|
25
25
|
import { createOAuthModule } from "./oauth-module.js";
|
|
26
|
+
import { availabilityCatalogue, availabilityCatalogueDefinitions, } from "./module-availability-control.js";
|
|
26
27
|
import { createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
|
|
27
28
|
import { numericWriteBehindRegistrations } from "./numeric-write-behind.js";
|
|
28
29
|
import { executeRedisNumericOperation, RedisNumericWriteBehindService, } from "./numeric-write-behind-redis.js";
|
|
@@ -229,6 +230,10 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
229
230
|
...publicModules,
|
|
230
231
|
...(operationsModule ? [operationsModule] : []),
|
|
231
232
|
];
|
|
233
|
+
const availabilityModules = availabilityCatalogueDefinitions(modules);
|
|
234
|
+
const availabilityService = {
|
|
235
|
+
listAvailabilityModules: () => availabilityCatalogue(availabilityModules),
|
|
236
|
+
};
|
|
232
237
|
const initialiseLeader = async () => {
|
|
233
238
|
await runLeaderStartup(database, environment, modulePackages, modules, logger);
|
|
234
239
|
assertStartupLeaseHealthy();
|
|
@@ -258,6 +263,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
258
263
|
localLeases,
|
|
259
264
|
dispatchLocal,
|
|
260
265
|
privacy: privacyService,
|
|
266
|
+
availability: availabilityService,
|
|
261
267
|
localLeaderLease: leaderLease,
|
|
262
268
|
...(coordinator ? { coordinator } : {}),
|
|
263
269
|
...(environment.DISCORD_INTERNAL_GUILD_ID
|
|
@@ -351,6 +357,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
351
357
|
localLeases,
|
|
352
358
|
dispatchLocal,
|
|
353
359
|
...(privacyService ? { privacy: privacyService } : {}),
|
|
360
|
+
availability: availabilityService,
|
|
354
361
|
...(leaderLease ? { localLeaderLease: leaderLease } : {}),
|
|
355
362
|
...(coordinator ? { coordinator } : {}),
|
|
356
363
|
...(environment.DISCORD_INTERNAL_GUILD_ID
|
|
@@ -651,7 +658,22 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
651
658
|
}
|
|
652
659
|
}
|
|
653
660
|
assertShardMessageSize(response);
|
|
654
|
-
await
|
|
661
|
+
await sendWorkerResponse(shardId, response);
|
|
662
|
+
}
|
|
663
|
+
async function sendWorkerResponse(shardId, response) {
|
|
664
|
+
if (shuttingDown)
|
|
665
|
+
return;
|
|
666
|
+
const worker = workers.get(shardId);
|
|
667
|
+
if (!worker?.process)
|
|
668
|
+
return;
|
|
669
|
+
try {
|
|
670
|
+
await worker.send(response);
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
if (shuttingDown || !worker.process)
|
|
674
|
+
return;
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
655
677
|
}
|
|
656
678
|
async function handleWorkerMessage(shardId, raw) {
|
|
657
679
|
if (serializedBytes(raw) > SHARD_CONTROL_MAX_BYTES)
|
|
@@ -709,7 +731,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
709
731
|
const response = leaderLease
|
|
710
732
|
? await executeLeaderCloud(message.data)
|
|
711
733
|
: await routeCloudToLeader(message.data);
|
|
712
|
-
await
|
|
734
|
+
await sendWorkerResponse(shardId, response);
|
|
713
735
|
return;
|
|
714
736
|
}
|
|
715
737
|
if (message.data.type === "helyx.operations.health.request") {
|
|
@@ -720,7 +742,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
|
|
|
720
742
|
health: boundedOperationalHealth(await buildHealth()),
|
|
721
743
|
};
|
|
722
744
|
assertShardMessageSize(response);
|
|
723
|
-
await
|
|
745
|
+
await sendWorkerResponse(shardId, response);
|
|
724
746
|
}
|
|
725
747
|
}
|
|
726
748
|
function dispatchLocal(shardId, request) {
|
|
@@ -1392,9 +1414,14 @@ export function buildShardWorkerEnvironment(source, identity, restartCount) {
|
|
|
1392
1414
|
"HELYX_SUPPORT_STAFF_CHANNEL_ID",
|
|
1393
1415
|
"HELYX_MODULE_PACKAGES",
|
|
1394
1416
|
"HELYX_MESSAGE_CONTENT_ENABLED",
|
|
1417
|
+
"HELYX_GUILD_MEMBERS_ENABLED",
|
|
1418
|
+
"HELYX_GUILD_RECONCILIATION_INTERVAL_SECONDS",
|
|
1395
1419
|
"HELYX_MODULE_CATALOGUE_PATH",
|
|
1396
1420
|
"HELYX_CLOUD_CONNECT_ENABLED",
|
|
1421
|
+
"HELYX_CLOUD_API_URL",
|
|
1397
1422
|
"HELYX_OWNER_USER_IDS",
|
|
1423
|
+
"HELYX_OAUTH_ENABLED",
|
|
1424
|
+
"HELYX_OAUTH_INTERNAL_SECRET",
|
|
1398
1425
|
"HELYX_VERIFICATION_ENABLED",
|
|
1399
1426
|
"HELYX_VERIFICATION_INSTALLATION_ID",
|
|
1400
1427
|
"HELYX_VERIFICATION_URL",
|
package/dist/shard-worker.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare function createLeaseHeartbeatWatchdog(onExpired: () => void, time
|
|
|
9
9
|
};
|
|
10
10
|
export declare function startShardWorker(processEnvironment?: NodeJS.ProcessEnv, identity?: {
|
|
11
11
|
version: 1;
|
|
12
|
-
mode: "
|
|
12
|
+
mode: "fixed" | "auto";
|
|
13
13
|
scope: "local" | "coordinated";
|
|
14
14
|
generation: string;
|
|
15
15
|
supervisorId: string;
|
|
@@ -50,12 +50,15 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
|
|
|
50
50
|
HELYX_OAUTH_ENABLED: boolean;
|
|
51
51
|
HELYX_CLOUD_ENDPOINT: string;
|
|
52
52
|
HELYX_OWNER_USER_IDS: string[];
|
|
53
|
-
HELYX_SHARD_MODE: "
|
|
53
|
+
HELYX_SHARD_MODE: "fixed" | "off" | "auto";
|
|
54
54
|
HELYX_SHARD_SCOPE: "local" | "coordinated";
|
|
55
55
|
HELYX_CLOUD_AUDIT_RETENTION_DAYS: number;
|
|
56
56
|
HELYX_CLOUD_CREDENTIAL_AUDIT_RETENTION_DAYS: number;
|
|
57
57
|
LOG_LEVEL: "error" | "fatal" | "warn" | "info" | "debug" | "trace" | "silent";
|
|
58
58
|
NODE_ENV: "development" | "test" | "production";
|
|
59
|
+
HELYX_STATUS_ENABLED: boolean;
|
|
60
|
+
HELYX_STATUS_URL: string;
|
|
61
|
+
HELYX_STATUS_HEARTBEAT_INTERVAL_SECONDS: number;
|
|
59
62
|
HELYX_BOT_CONTROL_SECRET?: string | undefined;
|
|
60
63
|
HELYX_SUPPORT_SERVICE_SECRET?: string | undefined;
|
|
61
64
|
HELYX_VERIFICATION_INSTALLATION_ID?: string | undefined;
|
|
@@ -69,6 +72,9 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
|
|
|
69
72
|
HELYX_SHARD_REDIS_NAMESPACE?: string | undefined;
|
|
70
73
|
HELYX_SHARD_GENERATION?: string | undefined;
|
|
71
74
|
HELYX_SHARD_DATABASE_CONNECTION_BUDGET?: number | undefined;
|
|
75
|
+
HELYX_STATUS_HEARTBEAT_SECRET?: string | undefined;
|
|
76
|
+
CF_ACCESS_CLIENT_ID?: string | undefined;
|
|
77
|
+
CF_ACCESS_CLIENT_SECRET?: string | undefined;
|
|
72
78
|
};
|
|
73
79
|
export declare function shardWorkerCommandRegistration(input: {
|
|
74
80
|
internalGuildId?: string;
|
package/dist/shard-worker.js
CHANGED
|
@@ -3,7 +3,7 @@ import { safeError, ServiceRegistry } from "@helyx/core";
|
|
|
3
3
|
import { checkDatabaseConnection, createDatabasePool } from "@helyx/database";
|
|
4
4
|
import { createRuntime } from "@helyx/framework";
|
|
5
5
|
import { createPlatformServices } from "@helyx/platform";
|
|
6
|
-
import { HELYX_SERVICE_NAMES } from "@helyx/sdk";
|
|
6
|
+
import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
7
7
|
import { Client, Events, Partials } from "discord.js";
|
|
8
8
|
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
9
9
|
import pino from "pino";
|
|
@@ -179,6 +179,9 @@ export async function startShardWorker(processEnvironment = process.env, identit
|
|
|
179
179
|
})) {
|
|
180
180
|
platformServices.set(name, service);
|
|
181
181
|
}
|
|
182
|
+
await serviceAccess
|
|
183
|
+
.get(HELYX_SERVICE_NAMES.moduleAvailability)
|
|
184
|
+
.refresh();
|
|
182
185
|
if (environment.HELYX_VERIFICATION_ENABLED &&
|
|
183
186
|
environment.HELYX_VERIFICATION_URL &&
|
|
184
187
|
environment.HELYX_VERIFICATION_INTERNAL_SECRET) {
|
package/dist/support-module.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/bot",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.35",
|
|
4
4
|
"description": "Production Discord runtime for the modular Helyx platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -53,9 +53,10 @@
|
|
|
53
53
|
"@helyx/core": ">=0.1.0 <0.2.0",
|
|
54
54
|
"@helyx/database": ">=0.8.0 <0.9.0",
|
|
55
55
|
"@helyx/framework": ">=0.1.0 <0.2.0",
|
|
56
|
-
"@helyx/module-manifest": ">=0.
|
|
57
|
-
"@helyx/platform": ">=0.
|
|
58
|
-
"@helyx/sdk": ">=0.
|
|
56
|
+
"@helyx/module-manifest": ">=0.7.0 <0.8.0",
|
|
57
|
+
"@helyx/platform": ">=0.6.0 <0.7.0",
|
|
58
|
+
"@helyx/sdk": ">=0.8.0 <0.9.0",
|
|
59
|
+
"@helyx/status-client": ">=0.1.0 <0.2.0",
|
|
59
60
|
"@helyx/verification-contracts": ">=0.3.0 <0.4.0",
|
|
60
61
|
"discord.js": "14.27.0",
|
|
61
62
|
"fastify": "5.10.0",
|