@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +22 -2
  3. package/dist/cloud-management-module.js +275 -206
  4. package/dist/command-registration-policy.d.ts +8 -0
  5. package/dist/command-registration-policy.js +39 -0
  6. package/dist/command-sync.d.ts +5 -1
  7. package/dist/command-sync.js +67 -3
  8. package/dist/control-server.d.ts +15 -3
  9. package/dist/control-server.js +34 -48
  10. package/dist/dashboard-commands.js +5 -1
  11. package/dist/developer-operations-module.d.ts +2 -0
  12. package/dist/developer-operations-module.js +65 -4
  13. package/dist/discord-rest.d.ts +5 -0
  14. package/dist/discord-rest.js +9 -0
  15. package/dist/events.js +4 -2
  16. package/dist/first-time-command-setup.d.ts +25 -0
  17. package/dist/first-time-command-setup.js +22 -0
  18. package/dist/guild-command-control-routes.d.ts +5 -0
  19. package/dist/guild-command-control-routes.js +191 -0
  20. package/dist/guild-command-discord-client.d.ts +21 -0
  21. package/dist/guild-command-discord-client.js +50 -0
  22. package/dist/guild-command-projection.d.ts +46 -0
  23. package/dist/guild-command-projection.js +254 -0
  24. package/dist/guild-command-read-service.d.ts +201 -0
  25. package/dist/guild-command-read-service.js +272 -0
  26. package/dist/guild-command-release.d.ts +19 -0
  27. package/dist/guild-command-release.js +47 -0
  28. package/dist/guild-command-worker.d.ts +49 -0
  29. package/dist/guild-command-worker.js +443 -0
  30. package/dist/guilds.d.ts +1 -1
  31. package/dist/guilds.js +41 -26
  32. package/dist/index.js +75 -4
  33. package/dist/interactions.d.ts +15 -4
  34. package/dist/interactions.js +285 -70
  35. package/dist/management-module.js +117 -41
  36. package/dist/module-logging.d.ts +1 -0
  37. package/dist/module-logging.js +5 -5
  38. package/dist/module-status-control.d.ts +19 -0
  39. package/dist/module-status-control.js +69 -0
  40. package/dist/modules.d.ts +2 -0
  41. package/dist/modules.js +21 -2
  42. package/dist/nexus-commands.d.ts +1 -12
  43. package/dist/nexus-commands.js +1 -99
  44. package/dist/oauth-module.js +134 -88
  45. package/dist/permissions-module.js +112 -39
  46. package/dist/privacy-module.d.ts +2 -0
  47. package/dist/privacy-module.js +60 -0
  48. package/dist/scheduled-task-runner.js +36 -1
  49. package/dist/shard-contracts.d.ts +24 -0
  50. package/dist/shard-contracts.js +20 -0
  51. package/dist/shard-router.d.ts +1 -0
  52. package/dist/shard-router.js +7 -0
  53. package/dist/shard-supervisor.d.ts +2 -1
  54. package/dist/shard-supervisor.js +94 -16
  55. package/dist/shard-worker.d.ts +5 -0
  56. package/dist/shard-worker.js +9 -1
  57. package/dist/shared-commands.d.ts +13 -0
  58. package/dist/shared-commands.js +163 -0
  59. package/dist/support-module.js +51 -51
  60. package/dist/trivia-data-provider.d.ts +26 -0
  61. package/dist/trivia-data-provider.js +51 -0
  62. package/documentation/README.md +32 -0
  63. package/documentation/examples/.env.example +117 -0
  64. package/documentation/guides/getting-started.md +51 -0
  65. package/documentation/guides/self-hosting.md +40 -0
  66. package/documentation/modules/building-a-module.md +37 -0
  67. package/documentation/modules/publishing-a-module.md +27 -0
  68. package/documentation/modules/testing-a-module.md +46 -0
  69. package/documentation/reference/environment.md +42 -0
  70. package/documentation/runbooks/backup-and-recovery.md +41 -0
  71. package/documentation/runbooks/installation-and-upgrades.md +48 -0
  72. package/package.json +7 -4
  73. package/dist/oauth-module.test.d.ts +0 -2
  74. package/dist/oauth-module.test.js +0 -172
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.39
4
+
5
+ ### Patch Changes
6
+
7
+ - 38c70f7: Ship maintained public documentation with the bot package and point newly generated self-host projects to its version-matched offline copy.
8
+
9
+ ## 0.4.38
10
+
11
+ ### Patch Changes
12
+
13
+ - Add per-server command registration, shared command families, command-management controls, and the supporting configuration and persistence contracts.
14
+ - Add `/dev trivia-data` so approved developers can inspect distinct Trivia question totals and category difficulty counts from the active database.
15
+ - Make the per-server command worker the sole writer for managed guild command collections so startup cannot replace enabled module commands with the internal `/dev` command.
16
+ - Add idempotent, staggered production command rollouts and authoritative first-time command previews from the live command projector.
17
+ - Report exact command roots changed by successful Discord reconciliations.
18
+ - Make static public platform commands respond without database authorization, avoid redundant platform availability reads, and run required customer command authorization reads concurrently.
19
+ - Warm each shard's Discord REST connection during startup and fast-path public commands so the first modal response does not expire while opening a cold HTTPS connection.
20
+ - Compose the Batch 1 Achievements, Feedback, and Suggestions routes under shared enabled-server roots, while retaining the complete Polls and FAQ command surfaces.
21
+ - Report whether a failed module log delivery is retryable so modules do not queue work for intentionally disabled events.
22
+
3
23
  ## 0.4.37
4
24
 
5
25
  ### Patch Changes
package/README.md CHANGED
@@ -2,15 +2,35 @@
2
2
 
3
3
  The Helyx Discord runtime validates its environment, verifies and migrates PostgreSQL, validates explicitly configured module packages, applies their migrations, composes platform services, synchronises guild installations and commands, logs in with the non-privileged Guilds intent, and shuts down Discord, modules, and PostgreSQL safely.
4
4
 
5
- Each command declares `registration: "global"` for every installed Discord server or `registration: "guild"` for the internal server. Set `DISCORD_INTERNAL_GUILD_ID` whenever a loaded module contains guild-only commands. Global and guild commands are synchronized independently.
5
+ Version-matched public guides, operator runbooks, environment reference
6
+ material, and module-authoring instructions ship at `documentation/README.md`
7
+ inside this npm package. Repository-only `docs/` and `docs-old/` content is not
8
+ published.
9
+
10
+ Command contributions use an explicit registration policy: platform commands
11
+ remain global, migrated customer commands are projected into enabled guilds,
12
+ deployment commands stay with their deployment, and internal commands are
13
+ limited to `DISCORD_INTERNAL_GUILD_ID`. Global and guild commands are
14
+ synchronized independently.
6
15
 
7
16
  Registration uses Discord's bulk-overwrite operation, so both scopes are synchronized to exactly the commands supplied by the active Helyx modules. Duplicate command names and component IDs fail before the bot logs in.
8
17
 
18
+ `HELYX_GLOBAL_COMMAND_MODE` defaults to `legacy`, preserving customer globals
19
+ for production and self-hosted deployments during migration. Use
20
+ `platform-only` only for an approved clean-slate pilot or final cutover; in
21
+ that mode startup publishes only platform globals and migrated customer
22
+ commands come from each guild's command profile and enabled module state.
23
+
9
24
  The built-in global `/helyx` and `/permissions` commands are core infrastructure. `/helyx` gives server owners, administrators and members with Manage Server permission an ephemeral module-management flow. It lists installed feature packages, enables or disables them, stages schema-defined settings, uses native Discord channel and role selectors, validates the complete configuration, and saves through the same versioned configuration and audit services as the dashboard. Dependency rules prevent a required module being disabled or a dependent module being enabled out of order.
10
25
 
11
26
  `/permissions` uses an owner/administrator-protected select and modal flow with native Discord selectors to manage inherited module policies and command overrides. Feature packages require an explicit allowlist: hosted distributions carry a reviewed `hosted-modules.json` catalogue, generated self-host projects use `HELYX_MODULE_CATALOGUE_PATH`, and `HELYX_MODULE_PACKAGES` remains an intentional comma-separated override. The sources are merged and de-duplicated. A core distribution has no catalogue and runs core-only when both self-host selectors are empty.
12
27
 
13
- Newly discovered feature modules are disabled for each Discord server by default. Global commands remain registered so Discord can present the installed Helyx command catalogue, but attempts to use a disabled module receive an ephemeral explanation directing the member to `/helyx` or the dashboard. Package upgrades preserve each server's existing enabled or disabled state.
28
+ Newly discovered feature modules are disabled for each Discord server by
29
+ default. In legacy mode their global commands remain registered, but attempts
30
+ to use a disabled module receive an ephemeral explanation directing the member
31
+ to `/helyx` or the dashboard. In platform-only mode, migrated commands are
32
+ included in a guild only when its command profile selects them. Package
33
+ upgrades preserve each server's existing enabled or disabled state.
14
34
 
15
35
  Gateway intents are derived from installed event contributions. The bot always requests Guilds, adds non-privileged reaction access only for reaction modules, and adds the privileged Server Members intent only when an installed module handles member joins. Deployments containing such a module must enable the matching intent in the Discord Developer Portal before startup.
16
36
 
@@ -9,6 +9,7 @@ const DISCONNECT_CONFIRM_FIELD_ID = "helyx.cloud.disconnect.confirm";
9
9
  const ROTATE_MODAL_ID = "helyx.cloud.rotate";
10
10
  const ROTATE_CONFIRM_FIELD_ID = "helyx.cloud.rotate.confirm";
11
11
  const SUPPORT_MODAL_ID = "helyx.cloud.support";
12
+ const SUPPORT_OPEN_MODAL_ID = "helyx.cloud.support.open";
12
13
  const SUPPORT_ENABLED_FIELD_ID = "helyx.cloud.support.enabled";
13
14
  const SUPPORT_DATA_FIELD_ID = "helyx.cloud.support.data";
14
15
  const DIAGNOSTICS_ENABLED_FIELD_ID = "helyx.cloud.support.diagnostics";
@@ -53,6 +54,7 @@ function discordOwnerIds(owner) {
53
54
  }
54
55
  export function createCloudManagementModule(input) {
55
56
  const ownerAuthorizer = new DeploymentOwnerAuthorizer(input.discord ?? new ClientCloudDiscordDirectory(input.client), input.configuredOwnerUserIds);
57
+ const preparedSupportModals = new Map();
56
58
  let ownerRefreshInterval;
57
59
  let onClientReady;
58
60
  const requireOwner = async (context) => {
@@ -83,7 +85,16 @@ export function createCloudManagementModule(input) {
83
85
  framework: "^0.1.0",
84
86
  dependencies: [],
85
87
  permissions: [],
86
- contributions: { commands: ["helyx-cloud"], events: [] },
88
+ contributions: {
89
+ commands: [
90
+ "helyx.cloud.connect",
91
+ "helyx.cloud.status",
92
+ "helyx.cloud.disconnect",
93
+ "helyx.cloud.rotate",
94
+ "helyx.cloud.support",
95
+ ],
96
+ events: [],
97
+ },
87
98
  },
88
99
  defaultPermissions: {
89
100
  module: restrictedPolicy(),
@@ -95,220 +106,278 @@ export function createCloudManagementModule(input) {
95
106
  "helyx-cloud.support": restrictedPolicy(),
96
107
  },
97
108
  },
98
- interactions: {
99
- commands: [
100
- {
101
- name: "helyx-cloud",
102
- description: "Manage this self-hosted bot's Helyx Cloud connection",
103
- registration: "global",
104
- contexts: ["guild", "bot_dm"],
105
- integrationTypes: ["guild_install"],
106
- subcommands: [
107
- {
108
- name: "connect",
109
- description: "Connect this deployment using a one-use code",
110
- acknowledgement: "immediate-modal",
111
- rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
112
- async execute(context) {
113
- if (!(await requireOwner(context)))
114
- return;
115
- await context.showModal({
116
- customId: CONNECT_MODAL_ID,
117
- title: "Connect to Helyx Cloud",
118
- text: [
119
- {
120
- content: "Create a short-lived connection code in `app.helyx.gg`, then paste it below. The code is used once and is not stored.",
121
- },
122
- ],
123
- fields: [
124
- {
125
- customId: CONNECT_CODE_FIELD_ID,
126
- label: "Cloud Connect code",
127
- description: "Five groups of four letters and numbers.",
128
- style: "short",
129
- required: true,
130
- minLength: 24,
131
- maxLength: 24,
132
- placeholder: "ABCD-EFGH-JKLM-NPQR-STUV",
133
- },
134
- ],
135
- });
109
+ sharedCommands: [
110
+ {
111
+ family: "helyx",
112
+ path: "cloud.connect",
113
+ contexts: ["guild", "bot_dm"],
114
+ permissionId: "helyx-cloud.connect",
115
+ description: "Connect this deployment using a one-use code",
116
+ acknowledgement: "immediate-modal",
117
+ authorization: "self-authorized",
118
+ rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
119
+ async execute(context) {
120
+ if (!(await requireOwner(context)))
121
+ return;
122
+ await context.showModal({
123
+ customId: CONNECT_MODAL_ID,
124
+ title: "Connect to Helyx Cloud",
125
+ text: [
126
+ {
127
+ content: "Create a short-lived connection code in `app.helyx.gg`, then paste it below. The code is used once and is not stored.",
136
128
  },
137
- },
138
- {
139
- name: "status",
140
- description: "Show this deployment's Cloud Connect status",
141
- acknowledgement: "deferred-ephemeral",
142
- rateLimit: { scope: "user", limit: 20, windowSeconds: 60 },
143
- async execute(context) {
144
- if (!(await requireOwner(context)))
145
- return;
146
- await context.reply(statusResponse(input.connector.status()));
129
+ ],
130
+ fields: [
131
+ {
132
+ customId: CONNECT_CODE_FIELD_ID,
133
+ label: "Cloud Connect code",
134
+ description: "Five groups of four letters and numbers.",
135
+ style: "short",
136
+ required: true,
137
+ minLength: 24,
138
+ maxLength: 24,
139
+ placeholder: "ABCD-EFGH-JKLM-NPQR-STUV",
147
140
  },
148
- },
149
- {
150
- name: "disconnect",
151
- description: "Revoke this deployment's Cloud Connect identity",
152
- acknowledgement: "immediate-modal",
153
- rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
154
- async execute(context) {
155
- if (!(await requireOwner(context)))
156
- return;
157
- await context.showModal({
158
- customId: DISCONNECT_MODAL_ID,
159
- title: "Disconnect Helyx Cloud",
160
- text: [
161
- {
162
- content: "This revokes the saved connector identity. A new dashboard code will be required to reconnect.",
163
- },
164
- ],
165
- fields: [
166
- {
167
- type: "string-select",
168
- customId: DISCONNECT_CONFIRM_FIELD_ID,
169
- label: "Confirm disconnection",
170
- required: true,
171
- minValues: 1,
172
- maxValues: 1,
173
- options: [
174
- {
175
- label: "Keep Cloud Connect",
176
- value: "keep",
177
- default: true,
178
- },
179
- {
180
- label: "Disconnect and revoke",
181
- value: "disconnect",
182
- },
183
- ],
184
- },
185
- ],
186
- });
141
+ ],
142
+ });
143
+ },
144
+ },
145
+ {
146
+ family: "helyx",
147
+ path: "cloud.status",
148
+ contexts: ["guild", "bot_dm"],
149
+ permissionId: "helyx-cloud.status",
150
+ description: "Show this deployment's Cloud Connect status",
151
+ acknowledgement: "immediate-response",
152
+ authorization: "self-authorized",
153
+ async execute(context) {
154
+ if (!(await requireOwner(context)))
155
+ return;
156
+ await context.reply(statusResponse(input.connector.status()));
157
+ },
158
+ },
159
+ {
160
+ family: "helyx",
161
+ path: "cloud.disconnect",
162
+ contexts: ["guild", "bot_dm"],
163
+ permissionId: "helyx-cloud.disconnect",
164
+ description: "Revoke this deployment's Cloud Connect identity",
165
+ acknowledgement: "immediate-modal",
166
+ authorization: "self-authorized",
167
+ rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
168
+ async execute(context) {
169
+ if (!(await requireOwner(context)))
170
+ return;
171
+ await context.showModal({
172
+ customId: DISCONNECT_MODAL_ID,
173
+ title: "Disconnect Helyx Cloud",
174
+ text: [
175
+ {
176
+ content: "This revokes the saved connector identity. A new dashboard code will be required to reconnect.",
187
177
  },
188
- },
189
- {
190
- name: "rotate",
191
- description: "Replace the saved Cloud Connect credential",
192
- acknowledgement: "immediate-modal",
193
- rateLimit: { scope: "user", limit: 3, windowSeconds: 60 },
194
- async execute(context) {
195
- if (!(await requireOwner(context)))
196
- return;
197
- await context.showModal({
198
- customId: ROTATE_MODAL_ID,
199
- title: "Rotate Cloud credential",
200
- text: [
201
- {
202
- content: "Rotation replaces the encrypted connector key and immediately reconnects the bot. Local Discord features keep running.",
203
- },
204
- ],
205
- fields: [
206
- {
207
- type: "string-select",
208
- customId: ROTATE_CONFIRM_FIELD_ID,
209
- label: "Confirm credential rotation",
210
- required: true,
211
- minValues: 1,
212
- maxValues: 1,
213
- options: [
214
- {
215
- label: "Cancel",
216
- value: "cancel",
217
- default: true,
218
- },
219
- {
220
- label: "Rotate credential",
221
- value: "rotate",
222
- },
223
- ],
224
- },
225
- ],
226
- });
178
+ ],
179
+ fields: [
180
+ {
181
+ type: "string-select",
182
+ customId: DISCONNECT_CONFIRM_FIELD_ID,
183
+ label: "Confirm disconnection",
184
+ required: true,
185
+ minValues: 1,
186
+ maxValues: 1,
187
+ options: [
188
+ {
189
+ label: "Keep Cloud Connect",
190
+ value: "keep",
191
+ default: true,
192
+ },
193
+ {
194
+ label: "Disconnect and revoke",
195
+ value: "disconnect",
196
+ },
197
+ ],
198
+ },
199
+ ],
200
+ });
201
+ },
202
+ },
203
+ {
204
+ family: "helyx",
205
+ path: "cloud.rotate",
206
+ contexts: ["guild", "bot_dm"],
207
+ permissionId: "helyx-cloud.rotate",
208
+ description: "Replace the saved Cloud Connect credential",
209
+ acknowledgement: "immediate-modal",
210
+ authorization: "self-authorized",
211
+ rateLimit: { scope: "user", limit: 3, windowSeconds: 60 },
212
+ async execute(context) {
213
+ if (!(await requireOwner(context)))
214
+ return;
215
+ await context.showModal({
216
+ customId: ROTATE_MODAL_ID,
217
+ title: "Rotate Cloud credential",
218
+ text: [
219
+ {
220
+ content: "Rotation replaces the encrypted connector key and immediately reconnects the bot. Local Discord features keep running.",
227
221
  },
222
+ ],
223
+ fields: [
224
+ {
225
+ type: "string-select",
226
+ customId: ROTATE_CONFIRM_FIELD_ID,
227
+ label: "Confirm credential rotation",
228
+ required: true,
229
+ minValues: 1,
230
+ maxValues: 1,
231
+ options: [
232
+ {
233
+ label: "Cancel",
234
+ value: "cancel",
235
+ default: true,
236
+ },
237
+ {
238
+ label: "Rotate credential",
239
+ value: "rotate",
240
+ },
241
+ ],
242
+ },
243
+ ],
244
+ });
245
+ },
246
+ },
247
+ {
248
+ family: "helyx",
249
+ path: "cloud.support",
250
+ contexts: ["guild", "bot_dm"],
251
+ permissionId: "helyx-cloud.support",
252
+ description: "Choose what Helyx support may inspect",
253
+ acknowledgement: "deferred-ephemeral",
254
+ authorization: "self-authorized",
255
+ rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
256
+ async execute(context) {
257
+ if (!(await requireOwner(context)))
258
+ return;
259
+ const consent = await input.connector.getSupportConsent();
260
+ const definition = {
261
+ customId: SUPPORT_MODAL_ID,
262
+ title: "Cloud support access",
263
+ text: [
264
+ {
265
+ content: "Support inspection is off by default. Submitted message content, secrets and arbitrary logs are never included.",
266
+ },
267
+ ],
268
+ fields: [
269
+ {
270
+ type: "string-select",
271
+ customId: SUPPORT_ENABLED_FIELD_ID,
272
+ label: "Allow bounded support inspection",
273
+ required: true,
274
+ minValues: 1,
275
+ maxValues: 1,
276
+ options: [
277
+ {
278
+ label: "No",
279
+ value: "no",
280
+ default: !consent.supportInspectionEnabled,
281
+ },
282
+ {
283
+ label: "Yes",
284
+ value: "yes",
285
+ default: consent.supportInspectionEnabled,
286
+ },
287
+ ],
288
+ },
289
+ {
290
+ type: "string-select",
291
+ customId: SUPPORT_DATA_FIELD_ID,
292
+ label: "Allowed support data",
293
+ description: "Select only the bounded metadata support may inspect.",
294
+ required: false,
295
+ minValues: 0,
296
+ maxValues: 5,
297
+ options: [
298
+ ["Module inventory", "module_inventory"],
299
+ ["Redacted configuration", "configuration"],
300
+ ["Permission summaries", "permissions"],
301
+ ["Logging policy", "logging"],
302
+ ["Connector health", "health"],
303
+ ].map(([label, value]) => ({
304
+ label,
305
+ value,
306
+ default: consent.supportDataClasses.includes(value),
307
+ })),
308
+ },
309
+ {
310
+ type: "string-select",
311
+ customId: DIAGNOSTICS_ENABLED_FIELD_ID,
312
+ label: "Allow bounded diagnostics",
313
+ required: true,
314
+ minValues: 1,
315
+ maxValues: 1,
316
+ options: [
317
+ {
318
+ label: "No",
319
+ value: "no",
320
+ default: !consent.diagnosticsEnabled,
321
+ },
322
+ {
323
+ label: "Yes",
324
+ value: "yes",
325
+ default: consent.diagnosticsEnabled,
326
+ },
327
+ ],
328
+ },
329
+ ],
330
+ };
331
+ preparedSupportModals.set(context.userId, {
332
+ definition,
333
+ expiresAt: Date.now() + 2 * 60 * 1_000,
334
+ });
335
+ await context.reply({
336
+ ephemeral: true,
337
+ componentsV2: {
338
+ text: [
339
+ {
340
+ content: "Your current support settings are ready. Open the form to review or change them.",
341
+ },
342
+ ],
228
343
  },
229
- {
230
- name: "support",
231
- description: "Choose what Helyx support may inspect",
232
- acknowledgement: "immediate-modal",
233
- rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
234
- async execute(context) {
235
- if (!(await requireOwner(context)))
236
- return;
237
- const consent = await input.connector.getSupportConsent();
238
- await context.showModal({
239
- customId: SUPPORT_MODAL_ID,
240
- title: "Cloud support access",
344
+ buttons: [
345
+ {
346
+ type: "button",
347
+ customId: SUPPORT_OPEN_MODAL_ID,
348
+ label: "Open support settings",
349
+ style: "primary",
350
+ },
351
+ ],
352
+ });
353
+ },
354
+ },
355
+ ],
356
+ interactions: {
357
+ buttons: [
358
+ {
359
+ customId: SUPPORT_OPEN_MODAL_ID,
360
+ authorization: "public",
361
+ async execute(context) {
362
+ if (!(await requireOwner(context)))
363
+ return;
364
+ const prepared = preparedSupportModals.get(context.userId);
365
+ preparedSupportModals.delete(context.userId);
366
+ if (!prepared || prepared.expiresAt <= Date.now()) {
367
+ await context.reply({
368
+ ephemeral: true,
369
+ componentsV2: {
241
370
  text: [
242
371
  {
243
- content: "Support inspection is off by default. Submitted message content, secrets and arbitrary logs are never included.",
244
- },
245
- ],
246
- fields: [
247
- {
248
- type: "string-select",
249
- customId: SUPPORT_ENABLED_FIELD_ID,
250
- label: "Allow bounded support inspection",
251
- required: true,
252
- minValues: 1,
253
- maxValues: 1,
254
- options: [
255
- {
256
- label: "No",
257
- value: "no",
258
- default: !consent.supportInspectionEnabled,
259
- },
260
- {
261
- label: "Yes",
262
- value: "yes",
263
- default: consent.supportInspectionEnabled,
264
- },
265
- ],
266
- },
267
- {
268
- type: "string-select",
269
- customId: SUPPORT_DATA_FIELD_ID,
270
- label: "Allowed support data",
271
- description: "Select only the bounded metadata support may inspect.",
272
- required: false,
273
- minValues: 0,
274
- maxValues: 5,
275
- options: [
276
- ["Module inventory", "module_inventory"],
277
- ["Redacted configuration", "configuration"],
278
- ["Permission summaries", "permissions"],
279
- ["Logging policy", "logging"],
280
- ["Connector health", "health"],
281
- ].map(([label, value]) => ({
282
- label,
283
- value,
284
- default: consent.supportDataClasses.includes(value),
285
- })),
286
- },
287
- {
288
- type: "string-select",
289
- customId: DIAGNOSTICS_ENABLED_FIELD_ID,
290
- label: "Allow bounded diagnostics",
291
- required: true,
292
- minValues: 1,
293
- maxValues: 1,
294
- options: [
295
- {
296
- label: "No",
297
- value: "no",
298
- default: !consent.diagnosticsEnabled,
299
- },
300
- {
301
- label: "Yes",
302
- value: "yes",
303
- default: consent.diagnosticsEnabled,
304
- },
305
- ],
372
+ content: "This support form expired. Run `/helyx cloud support` again.",
306
373
  },
307
374
  ],
308
- });
309
- },
310
- },
311
- ],
375
+ },
376
+ });
377
+ return;
378
+ }
379
+ await context.showModal(prepared.definition);
380
+ },
312
381
  },
313
382
  ],
314
383
  modals: [
@@ -0,0 +1,8 @@
1
+ import type { CommandContribution, CommandRegistrationPolicy, ModuleDefinition } from "@helyx/sdk";
2
+ export type ModuleCommandRegistrationPolicy = CommandRegistrationPolicy | "none";
3
+ export type GlobalCommandRegistrationMode = "legacy" | "platform-only";
4
+ export declare function commandRegistrationPolicy(module: ModuleDefinition): ModuleCommandRegistrationPolicy;
5
+ export declare function contributionRegistrationPolicy(module: ModuleDefinition, contribution: CommandContribution, options: {
6
+ allowLegacyCustomerGlobals: boolean;
7
+ }): ModuleCommandRegistrationPolicy;
8
+ //# sourceMappingURL=command-registration-policy.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { isCustomerSelectableModule } from "@helyx/module-manifest";
2
+ const platformGlobalModuleIds = new Set([
3
+ "helyx.management",
4
+ "helyx.support",
5
+ "helyx.oauth",
6
+ "helyx.permissions",
7
+ "helyx.privacy",
8
+ ]);
9
+ const deploymentModuleIds = new Set(["helyx.cloud-connect"]);
10
+ const internalGuildModuleIds = new Set(["helyx.developer-operations"]);
11
+ export function commandRegistrationPolicy(module) {
12
+ if (platformGlobalModuleIds.has(module.manifest.id))
13
+ return "platform-global";
14
+ if (deploymentModuleIds.has(module.manifest.id))
15
+ return "deployment";
16
+ if (internalGuildModuleIds.has(module.manifest.id))
17
+ return "internal-guild";
18
+ if (!isCustomerSelectableModule(module.manifest))
19
+ return "none";
20
+ return hasCommands(module) ? "enabled-guild" : "none";
21
+ }
22
+ export function contributionRegistrationPolicy(module, contribution, options) {
23
+ if (contribution.registrationPolicy)
24
+ return contribution.registrationPolicy;
25
+ const modulePolicy = commandRegistrationPolicy(module);
26
+ if (modulePolicy !== "enabled-guild")
27
+ return modulePolicy;
28
+ if (!options.allowLegacyCustomerGlobals)
29
+ return "none";
30
+ return contribution.registration === "guild"
31
+ ? "internal-guild"
32
+ : "platform-global";
33
+ }
34
+ function hasCommands(module) {
35
+ return Boolean(module.interactions?.commands?.length ||
36
+ module.sharedCommands?.length ||
37
+ module.nexusCommands?.length);
38
+ }
39
+ //# sourceMappingURL=command-registration-policy.js.map