@helyx/bot 0.4.37 → 0.4.39
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 +20 -0
- package/README.md +22 -2
- package/dist/cloud-management-module.js +275 -206
- package/dist/command-registration-policy.d.ts +8 -0
- package/dist/command-registration-policy.js +39 -0
- package/dist/command-sync.d.ts +5 -1
- package/dist/command-sync.js +67 -3
- package/dist/control-server.d.ts +15 -3
- package/dist/control-server.js +34 -48
- package/dist/dashboard-commands.js +5 -1
- package/dist/developer-operations-module.d.ts +2 -0
- package/dist/developer-operations-module.js +65 -4
- package/dist/discord-rest.d.ts +5 -0
- package/dist/discord-rest.js +9 -0
- package/dist/events.js +4 -2
- package/dist/first-time-command-setup.d.ts +25 -0
- package/dist/first-time-command-setup.js +22 -0
- package/dist/guild-command-control-routes.d.ts +5 -0
- package/dist/guild-command-control-routes.js +191 -0
- package/dist/guild-command-discord-client.d.ts +21 -0
- package/dist/guild-command-discord-client.js +50 -0
- package/dist/guild-command-projection.d.ts +46 -0
- package/dist/guild-command-projection.js +254 -0
- package/dist/guild-command-read-service.d.ts +201 -0
- package/dist/guild-command-read-service.js +272 -0
- package/dist/guild-command-release.d.ts +19 -0
- package/dist/guild-command-release.js +47 -0
- package/dist/guild-command-worker.d.ts +49 -0
- package/dist/guild-command-worker.js +443 -0
- package/dist/guilds.d.ts +1 -1
- package/dist/guilds.js +41 -26
- package/dist/index.js +75 -4
- package/dist/interactions.d.ts +15 -4
- package/dist/interactions.js +285 -70
- package/dist/management-module.js +117 -41
- package/dist/module-logging.d.ts +1 -0
- package/dist/module-logging.js +5 -5
- package/dist/module-status-control.d.ts +19 -0
- package/dist/module-status-control.js +69 -0
- package/dist/modules.d.ts +2 -0
- package/dist/modules.js +21 -2
- package/dist/nexus-commands.d.ts +1 -12
- package/dist/nexus-commands.js +1 -99
- package/dist/oauth-module.js +134 -88
- package/dist/permissions-module.js +112 -39
- package/dist/privacy-module.d.ts +2 -0
- package/dist/privacy-module.js +60 -0
- package/dist/scheduled-task-runner.js +36 -1
- package/dist/shard-contracts.d.ts +24 -0
- package/dist/shard-contracts.js +20 -0
- package/dist/shard-router.d.ts +1 -0
- package/dist/shard-router.js +7 -0
- package/dist/shard-supervisor.d.ts +2 -1
- package/dist/shard-supervisor.js +94 -16
- package/dist/shard-worker.d.ts +5 -0
- package/dist/shard-worker.js +9 -1
- package/dist/shared-commands.d.ts +13 -0
- package/dist/shared-commands.js +163 -0
- package/dist/support-module.js +51 -51
- package/dist/trivia-data-provider.d.ts +26 -0
- package/dist/trivia-data-provider.js +51 -0
- package/documentation/README.md +32 -0
- package/documentation/examples/.env.example +117 -0
- package/documentation/guides/getting-started.md +51 -0
- package/documentation/guides/self-hosting.md +40 -0
- package/documentation/modules/building-a-module.md +37 -0
- package/documentation/modules/publishing-a-module.md +27 -0
- package/documentation/modules/testing-a-module.md +46 -0
- package/documentation/reference/environment.md +42 -0
- package/documentation/runbooks/backup-and-recovery.md +41 -0
- package/documentation/runbooks/installation-and-upgrades.md +48 -0
- package/package.json +7 -4
- package/dist/oauth-module.test.d.ts +0 -2
- package/dist/oauth-module.test.js +0 -172
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Installation and upgrade runbook
|
|
2
|
+
|
|
3
|
+
## First installation
|
|
4
|
+
|
|
5
|
+
1. Generate the project with `helyx self-host init` and a reviewed build
|
|
6
|
+
manifest.
|
|
7
|
+
2. Copy and complete `.env` with `helyx setup`.
|
|
8
|
+
3. Install dependencies with `npm ci` if they were not installed during
|
|
9
|
+
generation.
|
|
10
|
+
4. Run `npm run doctor` and resolve every error.
|
|
11
|
+
5. Start the process and confirm database, Discord, and module health.
|
|
12
|
+
6. Exercise one low-risk command in a test server before production use.
|
|
13
|
+
|
|
14
|
+
## Upgrade
|
|
15
|
+
|
|
16
|
+
1. Read the release notes and confirm the supported Node.js and npm versions.
|
|
17
|
+
2. Create a current PostgreSQL backup and preserve the existing project
|
|
18
|
+
metadata and environment file.
|
|
19
|
+
3. Stop the Helyx process cleanly.
|
|
20
|
+
4. Preview the compatible release set:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
helyx self-host update --check
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
5. Apply it only after reviewing the complete change:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
helyx self-host update --confirm
|
|
30
|
+
npm ci
|
|
31
|
+
npm run doctor
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
6. Start Helyx and verify health, command availability, and one representative
|
|
35
|
+
workflow for every upgraded module.
|
|
36
|
+
|
|
37
|
+
The updater backs up managed project metadata under `.helyx/backups` and does
|
|
38
|
+
not change `.env` or customer database data. Database migrations can be
|
|
39
|
+
forward-only. Never edit migration history or downgrade individual Helyx
|
|
40
|
+
packages to simulate a rollback.
|
|
41
|
+
|
|
42
|
+
## Failed validation
|
|
43
|
+
|
|
44
|
+
Leave the process stopped, retain sanitized logs, and determine whether the
|
|
45
|
+
failure is configuration, package integrity, database connectivity, or module
|
|
46
|
+
compatibility. If the updater restored managed files, rerun `npm ci` and
|
|
47
|
+
`npm run doctor` before restarting the previous version. Restore data only from
|
|
48
|
+
a tested backup and only when the incident specifically requires it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/bot",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.39",
|
|
4
4
|
"description": "Production Discord runtime for the modular Helyx platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dist",
|
|
33
33
|
"!dist/*.tsbuildinfo",
|
|
34
34
|
"!dist/**/*.map",
|
|
35
|
+
"documentation",
|
|
35
36
|
"CHANGELOG.md",
|
|
36
37
|
"README.md",
|
|
37
38
|
"LICENSE"
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
},
|
|
45
46
|
"scripts": {
|
|
46
47
|
"build": "tsc -b",
|
|
48
|
+
"prepack": "node ../../scripts/public-documentation.mjs --sync-bot",
|
|
47
49
|
"dev": "node --env-file-if-exists=../../.env --import tsx src/index.ts",
|
|
48
50
|
"start": "node --env-file-if-exists=../../.env dist/index.js"
|
|
49
51
|
},
|
|
@@ -51,17 +53,18 @@
|
|
|
51
53
|
"@helyx/config": ">=0.5.0 <0.6.0",
|
|
52
54
|
"@helyx/control-protocol": ">=0.4.0 <0.5.0",
|
|
53
55
|
"@helyx/core": ">=0.1.0 <0.2.0",
|
|
54
|
-
"@helyx/database": ">=0.8.
|
|
56
|
+
"@helyx/database": ">=0.8.6 <0.9.0",
|
|
55
57
|
"@helyx/framework": ">=0.1.0 <0.2.0",
|
|
56
58
|
"@helyx/module-manifest": ">=0.7.0 <0.8.0",
|
|
57
|
-
"@helyx/platform": ">=0.6.
|
|
58
|
-
"@helyx/sdk": ">=0.8.
|
|
59
|
+
"@helyx/platform": ">=0.6.2 <0.7.0",
|
|
60
|
+
"@helyx/sdk": ">=0.8.4 <0.9.0",
|
|
59
61
|
"@helyx/status-client": ">=0.1.0 <0.2.0",
|
|
60
62
|
"@helyx/verification-contracts": ">=0.3.0 <0.4.0",
|
|
61
63
|
"discord.js": "14.27.0",
|
|
62
64
|
"fastify": "5.10.0",
|
|
63
65
|
"pino": "10.3.1",
|
|
64
66
|
"redis": "6.1.0",
|
|
67
|
+
"undici": "6.28.0",
|
|
65
68
|
"ws": "8.21.1",
|
|
66
69
|
"zod": "4.4.3"
|
|
67
70
|
},
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { createOAuthModule } from "./oauth-module.js";
|
|
4
|
-
describe("global OAuth command", () => {
|
|
5
|
-
it("is global, guild-scoped and available without management permissions", () => {
|
|
6
|
-
const module = createOAuthModule();
|
|
7
|
-
const command = module.interactions?.commands?.[0];
|
|
8
|
-
const revoke = command?.subcommands?.[0];
|
|
9
|
-
expect(command).toMatchObject({
|
|
10
|
-
name: "oauth",
|
|
11
|
-
registration: "global",
|
|
12
|
-
contexts: ["guild"],
|
|
13
|
-
integrationTypes: ["guild_install"],
|
|
14
|
-
});
|
|
15
|
-
expect(revoke).toMatchObject({
|
|
16
|
-
name: "revoke",
|
|
17
|
-
rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
|
|
18
|
-
});
|
|
19
|
-
expect(module.manifest.permissions).toEqual([]);
|
|
20
|
-
expect(module.defaultPermissions?.commands["oauth.revoke"]).toMatchObject({
|
|
21
|
-
everyone: true,
|
|
22
|
-
requiredPermissions: [],
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
it("does not open a modal when the acting user has no connections", async () => {
|
|
26
|
-
const oauth = oauthService({ connections: [] });
|
|
27
|
-
const showModal = vi.fn();
|
|
28
|
-
const replies = [];
|
|
29
|
-
const command = createOAuthModule().interactions?.commands?.[0]?.subcommands?.[0];
|
|
30
|
-
await command?.execute(commandContext(oauth, {
|
|
31
|
-
showModal,
|
|
32
|
-
reply: (response) => {
|
|
33
|
-
replies.push(response);
|
|
34
|
-
return Promise.resolve();
|
|
35
|
-
},
|
|
36
|
-
}));
|
|
37
|
-
expect(showModal).not.toHaveBeenCalled();
|
|
38
|
-
expect(replies).toEqual([expect.objectContaining({ ephemeral: true })]);
|
|
39
|
-
expect(replies[0]?.componentsV2?.text?.[0]?.content).toContain("do not have any OAuth services");
|
|
40
|
-
});
|
|
41
|
-
it("uses opaque selections and requires explicit removal confirmation", async () => {
|
|
42
|
-
const oauth = oauthService({
|
|
43
|
-
connections: [
|
|
44
|
-
{
|
|
45
|
-
selectionRef: "opaque-selection-reference-123",
|
|
46
|
-
provider: "youtube",
|
|
47
|
-
displayName: "Example creator",
|
|
48
|
-
connectedAt: new Date().toISOString(),
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
});
|
|
52
|
-
const showModal = vi.fn();
|
|
53
|
-
const command = createOAuthModule().interactions?.commands?.[0]?.subcommands?.[0];
|
|
54
|
-
await command?.execute(commandContext(oauth, { showModal }));
|
|
55
|
-
const modal = showModal.mock.calls[0]?.[0];
|
|
56
|
-
expect(modal?.title).toBe("Revoke OAuth access");
|
|
57
|
-
const service = modal?.items?.find((item) => "customId" in item && item.customId.endsWith("service"));
|
|
58
|
-
const confirmation = modal?.items?.find((item) => "customId" in item && item.customId.endsWith("confirm"));
|
|
59
|
-
expect(service).toMatchObject({
|
|
60
|
-
type: "string-select",
|
|
61
|
-
required: true,
|
|
62
|
-
options: [
|
|
63
|
-
expect.objectContaining({ value: "opaque-selection-reference-123" }),
|
|
64
|
-
],
|
|
65
|
-
});
|
|
66
|
-
expect(confirmation).toMatchObject({
|
|
67
|
-
type: "string-select",
|
|
68
|
-
required: true,
|
|
69
|
-
options: [expect.objectContaining({ value: "remove_access" })],
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it("revokes only for the acting user and returns an ephemeral advisory", async () => {
|
|
73
|
-
const revoke = vi.fn().mockResolvedValue({
|
|
74
|
-
provider: "twitch",
|
|
75
|
-
outcome: "completed",
|
|
76
|
-
providerRevocation: "failed",
|
|
77
|
-
});
|
|
78
|
-
const oauth = oauthService({ connections: [], revoke });
|
|
79
|
-
const replies = [];
|
|
80
|
-
const modal = createOAuthModule().interactions?.modals?.[0];
|
|
81
|
-
await modal?.execute(modalContext(oauth, {
|
|
82
|
-
reply: (response) => {
|
|
83
|
-
replies.push(response);
|
|
84
|
-
return Promise.resolve();
|
|
85
|
-
},
|
|
86
|
-
}));
|
|
87
|
-
expect(revoke).toHaveBeenCalledWith({
|
|
88
|
-
discordUserId: "12345678901234567",
|
|
89
|
-
selectionRef: "opaque-selection-reference-123",
|
|
90
|
-
confirmation: "remove_access",
|
|
91
|
-
comment: "No longer needed",
|
|
92
|
-
correlationId: "interaction-1",
|
|
93
|
-
});
|
|
94
|
-
expect(replies[0]).toMatchObject({ ephemeral: true });
|
|
95
|
-
expect(replies[0]?.componentsV2?.text?.[0]?.content).toContain("connected-app settings");
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
function oauthService(input) {
|
|
99
|
-
return {
|
|
100
|
-
connectionStatus: () => Promise.resolve([]),
|
|
101
|
-
listConnections: () => Promise.resolve(input.connections),
|
|
102
|
-
createAuthorization: () => Promise.resolve({
|
|
103
|
-
authorizationUrl: "https://example.com",
|
|
104
|
-
expiresAt: "",
|
|
105
|
-
}),
|
|
106
|
-
verifyEligibility: () => Promise.resolve({
|
|
107
|
-
provider: "youtube",
|
|
108
|
-
status: "unknown",
|
|
109
|
-
connected: false,
|
|
110
|
-
checkedAt: new Date().toISOString(),
|
|
111
|
-
}),
|
|
112
|
-
revoke: input.revoke ??
|
|
113
|
-
(() => Promise.resolve({
|
|
114
|
-
provider: "youtube",
|
|
115
|
-
outcome: "completed",
|
|
116
|
-
providerRevocation: "completed",
|
|
117
|
-
})),
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function services(oauth) {
|
|
121
|
-
return {
|
|
122
|
-
has: (name) => name === HELYX_SERVICE_NAMES.oauth,
|
|
123
|
-
get: (name) => {
|
|
124
|
-
if (name !== HELYX_SERVICE_NAMES.oauth)
|
|
125
|
-
throw new Error("unknown service");
|
|
126
|
-
return oauth;
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function commandContext(oauth, overrides = {}) {
|
|
131
|
-
return {
|
|
132
|
-
...interactionBase(oauth),
|
|
133
|
-
showModal: () => Promise.resolve(),
|
|
134
|
-
getStringOption: () => null,
|
|
135
|
-
getBooleanOption: () => null,
|
|
136
|
-
getUserOption: () => null,
|
|
137
|
-
getChannelOption: () => null,
|
|
138
|
-
getRoleOption: () => null,
|
|
139
|
-
...overrides,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
function modalContext(oauth, overrides = {}) {
|
|
143
|
-
return {
|
|
144
|
-
...interactionBase(oauth),
|
|
145
|
-
getField: (customId) => customId.endsWith("comment") ? "No longer needed" : "",
|
|
146
|
-
getStringSelectValues: (customId) => customId.endsWith("service")
|
|
147
|
-
? ["opaque-selection-reference-123"]
|
|
148
|
-
: ["remove_access"],
|
|
149
|
-
getSelectedUserIds: () => [],
|
|
150
|
-
getSelectedRoleIds: () => [],
|
|
151
|
-
getSelectedMentionables: () => ({ userIds: [], roleIds: [] }),
|
|
152
|
-
getSelectedChannelIds: () => [],
|
|
153
|
-
...overrides,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function interactionBase(oauth) {
|
|
157
|
-
return {
|
|
158
|
-
interactionId: "interaction-1",
|
|
159
|
-
userId: "12345678901234567",
|
|
160
|
-
userDisplayName: "Member",
|
|
161
|
-
userRoleIds: [],
|
|
162
|
-
isAdministrator: false,
|
|
163
|
-
serverId: "12345678901234568",
|
|
164
|
-
services: services(oauth),
|
|
165
|
-
reply: () => Promise.resolve(),
|
|
166
|
-
sendToChannel: () => Promise.resolve({ messageId: "message-1" }),
|
|
167
|
-
deleteChannelMessage: () => Promise.resolve(),
|
|
168
|
-
editChannelMessage: () => Promise.resolve(),
|
|
169
|
-
addChannelMessageReaction: () => Promise.resolve(),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
//# sourceMappingURL=oauth-module.test.js.map
|