@dxos/plugin-discord 0.0.0-stage → 0.14.0
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/LICENSE +105 -0
- package/PLUGIN.mdl +494 -0
- package/dist/lib/DiscordAdminOperation.mjs +142 -0
- package/dist/lib/DiscordAdminOperation.mjs.map +1 -0
- package/dist/lib/DiscordChannel.mjs +95 -0
- package/dist/lib/DiscordChannel.mjs.map +1 -0
- package/dist/lib/DiscordEvents.mjs +15 -0
- package/dist/lib/DiscordEvents.mjs.map +1 -0
- package/dist/lib/DiscordOperation.mjs +107 -0
- package/dist/lib/DiscordOperation.mjs.map +1 -0
- package/dist/lib/DiscordPlugin.mjs +16 -0
- package/dist/lib/DiscordPlugin.mjs.map +1 -0
- package/dist/lib/DiscordTargetOptions.mjs +30 -0
- package/dist/lib/DiscordTargetOptions.mjs.map +1 -0
- package/dist/lib/capabilities.browser.mjs +39 -0
- package/dist/lib/capabilities.browser.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +39 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/capabilities.node.mjs +32 -0
- package/dist/lib/capabilities.node.mjs.map +1 -0
- package/dist/lib/capabilities.tauri.mjs +39 -0
- package/dist/lib/capabilities.tauri.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs +32 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -0
- package/dist/lib/chunk-DiscordAgentSetup.mjs +266 -0
- package/dist/lib/chunk-DiscordAgentSetup.mjs.map +1 -0
- package/dist/lib/chunk-DiscordChannelProperties.mjs +95 -0
- package/dist/lib/chunk-DiscordChannelProperties.mjs.map +1 -0
- package/dist/lib/chunk-PLUGIN.mjs +6 -0
- package/dist/lib/chunk-PLUGIN.mjs.map +1 -0
- package/dist/lib/chunk-bind-channel.mjs +93 -0
- package/dist/lib/chunk-bind-channel.mjs.map +1 -0
- package/dist/lib/chunk-bot-gateway.mjs +71 -0
- package/dist/lib/chunk-bot-gateway.mjs.map +1 -0
- package/dist/lib/chunk-bot.mjs +28 -0
- package/dist/lib/chunk-bot.mjs.map +1 -0
- package/dist/lib/chunk-channel-backend.mjs +248 -0
- package/dist/lib/chunk-channel-backend.mjs.map +1 -0
- package/dist/lib/chunk-connector.mjs +150 -0
- package/dist/lib/chunk-connector.mjs.map +1 -0
- package/dist/lib/chunk-constants.mjs +41 -0
- package/dist/lib/chunk-constants.mjs.map +1 -0
- package/dist/lib/chunk-crawl.mjs +69 -0
- package/dist/lib/chunk-crawl.mjs.map +1 -0
- package/dist/lib/chunk-discord-source.mjs +140 -0
- package/dist/lib/chunk-discord-source.mjs.map +1 -0
- package/dist/lib/chunk-discord.mjs +117 -0
- package/dist/lib/chunk-discord.mjs.map +1 -0
- package/dist/lib/chunk-errors.mjs +46 -0
- package/dist/lib/chunk-errors.mjs.map +1 -0
- package/dist/lib/chunk-get-discord-channels.mjs +60 -0
- package/dist/lib/chunk-get-discord-channels.mjs.map +1 -0
- package/dist/lib/chunk-list-bindings.mjs +13 -0
- package/dist/lib/chunk-list-bindings.mjs.map +1 -0
- package/dist/lib/chunk-list-bot-guilds.mjs +13 -0
- package/dist/lib/chunk-list-bot-guilds.mjs.map +1 -0
- package/dist/lib/chunk-list-guild-channels.mjs +13 -0
- package/dist/lib/chunk-list-guild-channels.mjs.map +1 -0
- package/dist/lib/chunk-list-managed-guilds.mjs +21 -0
- package/dist/lib/chunk-list-managed-guilds.mjs.map +1 -0
- package/dist/lib/chunk-materialize-target.mjs +47 -0
- package/dist/lib/chunk-materialize-target.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +12 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +29 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-schema.mjs +7 -0
- package/dist/lib/chunk-schema.mjs.map +1 -0
- package/dist/lib/chunk-sync.mjs +184 -0
- package/dist/lib/chunk-sync.mjs.map +1 -0
- package/dist/lib/chunk-unbind-channel.mjs +40 -0
- package/dist/lib/chunk-unbind-channel.mjs.map +1 -0
- package/dist/lib/components.mjs +190 -0
- package/dist/lib/components.mjs.map +1 -0
- package/dist/lib/containers.mjs +8 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +39 -0
- package/dist/lib/meta.mjs.map +1 -0
- package/dist/lib/ns/DiscordSource.mjs +8 -0
- package/dist/lib/ns/DiscordSource.mjs.map +1 -0
- package/dist/lib/operations.mjs +20 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/translations.mjs +59 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +6 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/DiscordPlugin.d.ts +37 -0
- package/dist/types/src/DiscordPlugin.d.ts.map +1 -0
- package/dist/types/src/DiscordSource.d.ts +2 -0
- package/dist/types/src/DiscordSource.d.ts.map +1 -0
- package/dist/types/src/capabilities/channel-backend.d.ts +19 -0
- package/dist/types/src/capabilities/channel-backend.d.ts.map +1 -0
- package/dist/types/src/capabilities/channel-backend.test.d.ts +2 -0
- package/dist/types/src/capabilities/channel-backend.test.d.ts.map +1 -0
- package/dist/types/src/capabilities/connector.d.ts +16 -0
- package/dist/types/src/capabilities/connector.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/browser.d.ts +9 -0
- package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/node.d.ts +9 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts +9 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts +9 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +9 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +5 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/schema.d.ts +4 -0
- package/dist/types/src/capabilities/schema.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotStatus/DiscordBotStatus.d.ts +13 -0
- package/dist/types/src/components/DiscordBotStatus/DiscordBotStatus.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotStatus/DiscordBotStatus.stories.d.ts +114 -0
- package/dist/types/src/components/DiscordBotStatus/DiscordBotStatus.stories.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotStatus/index.d.ts +2 -0
- package/dist/types/src/components/DiscordBotStatus/index.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotToolbar/DiscordBotToolbar.d.ts +15 -0
- package/dist/types/src/components/DiscordBotToolbar/DiscordBotToolbar.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotToolbar/DiscordBotToolbar.stories.d.ts +113 -0
- package/dist/types/src/components/DiscordBotToolbar/DiscordBotToolbar.stories.d.ts.map +1 -0
- package/dist/types/src/components/DiscordBotToolbar/index.d.ts +2 -0
- package/dist/types/src/components/DiscordBotToolbar/index.d.ts.map +1 -0
- package/dist/types/src/components/DiscordChannelForm/DiscordChannelForm.d.ts +18 -0
- package/dist/types/src/components/DiscordChannelForm/DiscordChannelForm.d.ts.map +1 -0
- package/dist/types/src/components/DiscordChannelForm/DiscordChannelForm.stories.d.ts +112 -0
- package/dist/types/src/components/DiscordChannelForm/DiscordChannelForm.stories.d.ts.map +1 -0
- package/dist/types/src/components/DiscordChannelForm/index.d.ts +2 -0
- package/dist/types/src/components/DiscordChannelForm/index.d.ts.map +1 -0
- package/dist/types/src/components/DiscordSetup/DiscordSetup.d.ts +57 -0
- package/dist/types/src/components/DiscordSetup/DiscordSetup.d.ts.map +1 -0
- package/dist/types/src/components/DiscordSetup/index.d.ts +2 -0
- package/dist/types/src/components/DiscordSetup/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/constants.d.ts +45 -0
- package/dist/types/src/constants.d.ts.map +1 -0
- package/dist/types/src/containers/DiscordAgentSetup/DiscordAgentSetup.d.ts +12 -0
- package/dist/types/src/containers/DiscordAgentSetup/DiscordAgentSetup.d.ts.map +1 -0
- package/dist/types/src/containers/DiscordAgentSetup/index.d.ts +2 -0
- package/dist/types/src/containers/DiscordAgentSetup/index.d.ts.map +1 -0
- package/dist/types/src/containers/DiscordChannelProperties/DiscordChannelProperties.d.ts +11 -0
- package/dist/types/src/containers/DiscordChannelProperties/DiscordChannelProperties.d.ts.map +1 -0
- package/dist/types/src/containers/DiscordChannelProperties/index.d.ts +2 -0
- package/dist/types/src/containers/DiscordChannelProperties/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +4 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +131 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +33 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/operations/admin/bind-channel.d.ts +5 -0
- package/dist/types/src/operations/admin/bind-channel.d.ts.map +1 -0
- package/dist/types/src/operations/admin/bot.d.ts +37 -0
- package/dist/types/src/operations/admin/bot.d.ts.map +1 -0
- package/dist/types/src/operations/admin/guild-channel.d.ts +11 -0
- package/dist/types/src/operations/admin/guild-channel.d.ts.map +1 -0
- package/dist/types/src/operations/admin/list-bindings.d.ts +5 -0
- package/dist/types/src/operations/admin/list-bindings.d.ts.map +1 -0
- package/dist/types/src/operations/admin/list-bot-guilds.d.ts +5 -0
- package/dist/types/src/operations/admin/list-bot-guilds.d.ts.map +1 -0
- package/dist/types/src/operations/admin/list-guild-channels.d.ts +5 -0
- package/dist/types/src/operations/admin/list-guild-channels.d.ts.map +1 -0
- package/dist/types/src/operations/admin/list-managed-guilds.d.ts +5 -0
- package/dist/types/src/operations/admin/list-managed-guilds.d.ts.map +1 -0
- package/dist/types/src/operations/admin/unbind-channel.d.ts +5 -0
- package/dist/types/src/operations/admin/unbind-channel.d.ts.map +1 -0
- package/dist/types/src/operations/crawl.d.ts +10 -0
- package/dist/types/src/operations/crawl.d.ts.map +1 -0
- package/dist/types/src/operations/errors.d.ts +33 -0
- package/dist/types/src/operations/errors.d.ts.map +1 -0
- package/dist/types/src/operations/get-discord-channels.d.ts +16 -0
- package/dist/types/src/operations/get-discord-channels.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/materialize-target.d.ts +13 -0
- package/dist/types/src/operations/materialize-target.d.ts.map +1 -0
- package/dist/types/src/operations/sync.d.ts +32 -0
- package/dist/types/src/operations/sync.d.ts.map +1 -0
- package/dist/types/src/operations/sync.test.d.ts +2 -0
- package/dist/types/src/operations/sync.test.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/plugin.test.d.ts +2 -0
- package/dist/types/src/plugin.test.d.ts.map +1 -0
- package/dist/types/src/services/bot-gateway.d.ts +24 -0
- package/dist/types/src/services/bot-gateway.d.ts.map +1 -0
- package/dist/types/src/services/bot-rest.d.ts +17 -0
- package/dist/types/src/services/bot-rest.d.ts.map +1 -0
- package/dist/types/src/services/crawl-stores.d.ts +8 -0
- package/dist/types/src/services/crawl-stores.d.ts.map +1 -0
- package/dist/types/src/services/discord-source.d.ts +28 -0
- package/dist/types/src/services/discord-source.d.ts.map +1 -0
- package/dist/types/src/services/discord-source.test.d.ts +2 -0
- package/dist/types/src/services/discord-source.test.d.ts.map +1 -0
- package/dist/types/src/services/discord.d.ts +49 -0
- package/dist/types/src/services/discord.d.ts.map +1 -0
- package/dist/types/src/services/discord.test.d.ts +2 -0
- package/dist/types/src/services/discord.test.d.ts.map +1 -0
- package/dist/types/src/services/index.d.ts +5 -0
- package/dist/types/src/services/index.d.ts.map +1 -0
- package/dist/types/src/services/proxy-http-client.d.ts +38 -0
- package/dist/types/src/services/proxy-http-client.d.ts.map +1 -0
- package/dist/types/src/testing/crawl-demo.test.d.ts +2 -0
- package/dist/types/src/testing/crawl-demo.test.d.ts.map +1 -0
- package/dist/types/src/testing/fetch.d.ts +65 -0
- package/dist/types/src/testing/fetch.d.ts.map +1 -0
- package/dist/types/src/testing/generate-fixtures.d.ts +2 -0
- package/dist/types/src/testing/generate-fixtures.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/questions-demo.test.d.ts +2 -0
- package/dist/types/src/testing/questions-demo.test.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +98 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/DiscordAdminOperation.d.ts +104 -0
- package/dist/types/src/types/DiscordAdminOperation.d.ts.map +1 -0
- package/dist/types/src/types/DiscordChannel.d.ts +73 -0
- package/dist/types/src/types/DiscordChannel.d.ts.map +1 -0
- package/dist/types/src/types/DiscordEvents.d.ts +8 -0
- package/dist/types/src/types/DiscordEvents.d.ts.map +1 -0
- package/dist/types/src/types/DiscordOperation.d.ts +79 -0
- package/dist/types/src/types/DiscordOperation.d.ts.map +1 -0
- package/dist/types/src/types/DiscordTargetOptions.d.ts +24 -0
- package/dist/types/src/types/DiscordTargetOptions.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +6 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +34 -0
- package/package.json +198 -3
- package/src/DiscordPlugin.ts +15 -0
- package/src/DiscordSource.ts +7 -0
- package/src/__fixtures__/discord-messages.json +216 -0
- package/src/capabilities/channel-backend.test.ts +261 -0
- package/src/capabilities/channel-backend.ts +185 -0
- package/src/capabilities/connector.ts +203 -0
- package/src/capabilities/gen/browser.ts +54 -0
- package/src/capabilities/gen/node.ts +43 -0
- package/src/capabilities/gen/tauri.ts +54 -0
- package/src/capabilities/gen/workerd.ts +43 -0
- package/src/capabilities/index.ts +47 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +45 -0
- package/src/capabilities/schema.ts +7 -0
- package/src/components/DiscordBotStatus/DiscordBotStatus.stories.tsx +47 -0
- package/src/components/DiscordBotStatus/DiscordBotStatus.tsx +76 -0
- package/src/components/DiscordBotStatus/index.ts +5 -0
- package/src/components/DiscordBotToolbar/DiscordBotToolbar.stories.tsx +37 -0
- package/src/components/DiscordBotToolbar/DiscordBotToolbar.tsx +69 -0
- package/src/components/DiscordBotToolbar/index.ts +5 -0
- package/src/components/DiscordChannelForm/DiscordChannelForm.stories.tsx +63 -0
- package/src/components/DiscordChannelForm/DiscordChannelForm.tsx +53 -0
- package/src/components/DiscordChannelForm/index.ts +5 -0
- package/src/components/DiscordSetup/DiscordSetup.tsx +174 -0
- package/src/components/DiscordSetup/index.ts +5 -0
- package/src/components/index.ts +8 -0
- package/src/constants.ts +80 -0
- package/src/containers/DiscordAgentSetup/DiscordAgentSetup.tsx +354 -0
- package/src/containers/DiscordAgentSetup/index.ts +5 -0
- package/src/containers/DiscordChannelProperties/DiscordChannelProperties.tsx +129 -0
- package/src/containers/DiscordChannelProperties/index.ts +5 -0
- package/src/containers/index.ts +8 -0
- package/src/errors.ts +103 -0
- package/src/index.ts +7 -0
- package/src/meta.ts +9 -0
- package/src/operations/admin/bind-channel.ts +103 -0
- package/src/operations/admin/bot.ts +36 -0
- package/src/operations/admin/guild-channel.ts +35 -0
- package/src/operations/admin/list-bindings.ts +25 -0
- package/src/operations/admin/list-bot-guilds.ts +25 -0
- package/src/operations/admin/list-guild-channels.ts +28 -0
- package/src/operations/admin/list-managed-guilds.ts +34 -0
- package/src/operations/admin/unbind-channel.ts +69 -0
- package/src/operations/crawl.ts +71 -0
- package/src/operations/errors.ts +8 -0
- package/src/operations/get-discord-channels.ts +91 -0
- package/src/operations/index.ts +21 -0
- package/src/operations/materialize-target.ts +56 -0
- package/src/operations/sync.test.ts +68 -0
- package/src/operations/sync.ts +258 -0
- package/src/plugin.test.ts +33 -0
- package/src/plugin.ts +29 -0
- package/src/services/bot-gateway.ts +92 -0
- package/src/services/bot-rest.ts +122 -0
- package/src/services/crawl-stores.ts +37 -0
- package/src/services/discord-source.test.ts +179 -0
- package/src/services/discord-source.ts +179 -0
- package/src/services/discord.test.ts +43 -0
- package/src/services/discord.ts +87 -0
- package/src/services/index.ts +19 -0
- package/src/services/proxy-http-client.ts +78 -0
- package/src/testing/crawl-demo.test.ts +78 -0
- package/src/testing/fetch.ts +167 -0
- package/src/testing/generate-fixtures.ts +89 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/questions-demo.test.ts +76 -0
- package/src/translations.ts +64 -0
- package/src/types/DiscordAdminOperation.ts +149 -0
- package/src/types/DiscordChannel.ts +100 -0
- package/src/types/DiscordEvents.ts +16 -0
- package/src/types/DiscordOperation.ts +106 -0
- package/src/types/DiscordTargetOptions.ts +35 -0
- package/src/types/index.ts +9 -0
- package/src/vite-env.d.ts +10 -0
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-Apache-2.0
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 DXOS
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
package/PLUGIN.mdl
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: org.dxos.plugin.discord
|
|
3
|
+
name: DiscordPlugin
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
A Composer plugin that connects a Discord bot to a workspace so that server
|
|
8
|
+
channels stream alongside everything else the user is working on. Messages
|
|
9
|
+
from every guild the bot belongs to are synced into ECHO as `@dxos/types`
|
|
10
|
+
`Message` objects inside `Channel` feeds — fully local-first and replicated
|
|
11
|
+
to all peers.
|
|
12
|
+
|
|
13
|
+
## Extensions
|
|
14
|
+
|
|
15
|
+
The following extension dialects are used in this document.
|
|
16
|
+
Each extension is defined in the Appendix or resolved via its URI.
|
|
17
|
+
|
|
18
|
+
| Term | URI |
|
|
19
|
+
|-------------|--------------------------------|
|
|
20
|
+
| `type` | `org.dxos.mdl.type@1.0` |
|
|
21
|
+
| `feat` | `org.dxos.mdl.feat@1.0` |
|
|
22
|
+
| `scenario` | `org.dxos.mdl.scenario@1.0` |
|
|
23
|
+
| `component` | `org.dxos.mdl.component@1.0` |
|
|
24
|
+
| `op` | `org.dxos.mdl.op@1.0` |
|
|
25
|
+
| `op@1.1` | `org.dxos.mdl.op@1.1` |
|
|
26
|
+
| `test` | `org.dxos.mdl.test@1.0` |
|
|
27
|
+
|
|
28
|
+
## Types
|
|
29
|
+
|
|
30
|
+
```mdl
|
|
31
|
+
type RemoteTarget
|
|
32
|
+
desc: Wire shape returned by GetDiscordChannels representing one Discord text channel.
|
|
33
|
+
fields:
|
|
34
|
+
id: string # Discord channel snowflake id
|
|
35
|
+
name: string # "#channel — Guild" display label
|
|
36
|
+
description?: string # channel topic, if set
|
|
37
|
+
metadata?: Record<string, unknown> # guildId, guildName, channelType, etc.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```mdl
|
|
41
|
+
type DiscordTargetOptions
|
|
42
|
+
desc: Per-target sync options stored under IntegrationTarget.options and edited from the Integration UI.
|
|
43
|
+
fields:
|
|
44
|
+
maxDays?: number # initial backfill window in days (default 30; ignored after first sync)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```mdl
|
|
48
|
+
type PullResult
|
|
49
|
+
desc: Summary of a completed incremental sync for one target channel.
|
|
50
|
+
fields:
|
|
51
|
+
added: number # number of new Message objects appended to the channel feed
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```mdl
|
|
55
|
+
type DiscordChannel
|
|
56
|
+
desc: >
|
|
57
|
+
Config of a Discord-backed Channel (`Channel.backend.config`, backend kind
|
|
58
|
+
`org.dxos.channel.backend.discord`): the bot that posts and the Discord channels it listens in.
|
|
59
|
+
EDGE's gateway reads it from the space when the bot starts.
|
|
60
|
+
fields:
|
|
61
|
+
accessToken: Ref<AccessToken> # pasted bot token
|
|
62
|
+
applicationId: string # Discord application (bot) id; keys the EDGE gateway
|
|
63
|
+
guildId?: string
|
|
64
|
+
channels: string[] # Discord channel ids; the first receives posts with no thread
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Services
|
|
68
|
+
|
|
69
|
+
```mdl
|
|
70
|
+
service DiscordREST
|
|
71
|
+
desc: >
|
|
72
|
+
dfx DiscordREST service layer. Wraps Discord's REST API v10 with an
|
|
73
|
+
in-memory rate-limit store. All HTTP traffic is routed through the edge
|
|
74
|
+
CORS proxy via makeEdgeProxyHttpClientLayer so browser-side requests work
|
|
75
|
+
without direct Discord API access.
|
|
76
|
+
loading: lazy
|
|
77
|
+
ops:
|
|
78
|
+
getMyUser() → User
|
|
79
|
+
listMyGuilds(params) → Guild[]
|
|
80
|
+
listGuildChannels(guildId: string) → Channel[]
|
|
81
|
+
listMessages(channelId: string, params) → Message[]
|
|
82
|
+
errors:
|
|
83
|
+
ErrorResponse: non-2xx Discord API error (code + message in body)
|
|
84
|
+
RatelimitedResponse: HTTP 429; cause carries retry_after seconds
|
|
85
|
+
note: >
|
|
86
|
+
The layer is constructed per-operation via makeDiscordLayer (integration ref)
|
|
87
|
+
or makeDiscordLayerFromToken (raw token for credential-form validation).
|
|
88
|
+
The CORS proxy is temporary; a future revision will route through the
|
|
89
|
+
authenticated /proxy/* edge endpoint using signed verifiable presentations.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Operations
|
|
93
|
+
|
|
94
|
+
```mdl
|
|
95
|
+
op GetDiscordChannels
|
|
96
|
+
key: org.dxos.operation.discord.getChannels
|
|
97
|
+
desc: >
|
|
98
|
+
Discovery only. Lists every text and announcement channel (type 0 and 5)
|
|
99
|
+
across all guilds the bot belongs to and returns one RemoteTarget per
|
|
100
|
+
channel. Read-only: never creates a local Channel object. Guild pagination
|
|
101
|
+
is drained in full; channel fetch failures for individual guilds are logged
|
|
102
|
+
and skipped so the rest of the discovery result remains usable.
|
|
103
|
+
input:
|
|
104
|
+
connection: Ref<Connection>
|
|
105
|
+
output: { targets: RemoteTarget[] }
|
|
106
|
+
effects: [http]
|
|
107
|
+
requires: [DiscordREST]
|
|
108
|
+
note: >
|
|
109
|
+
Channel names are rendered as "#channel — Guild" so the integration UI can
|
|
110
|
+
disambiguate same-named channels across multiple guilds without a separate
|
|
111
|
+
grouping concept.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```mdl
|
|
115
|
+
op MaterializeDiscordTarget
|
|
116
|
+
key: org.dxos.operation.discord.materializeTarget
|
|
117
|
+
desc: >
|
|
118
|
+
Find-or-create the empty local feed-backed Channel for a selected Discord
|
|
119
|
+
channel, keyed by the Discord channel id, so a sync cursor can reference it.
|
|
120
|
+
input:
|
|
121
|
+
connection: Ref<Connection>
|
|
122
|
+
remoteTarget: RemoteTarget
|
|
123
|
+
output: { target: Ref<Channel> }
|
|
124
|
+
effects: [echo:write]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```mdl
|
|
128
|
+
op SyncDiscordChannel
|
|
129
|
+
key: org.dxos.operation.discord.syncChannel
|
|
130
|
+
desc: >
|
|
131
|
+
Pull-only incremental sync for every Discord target bound to a Connection.
|
|
132
|
+
For each bound target: resolves the local ECHO Channel keyed by the Discord
|
|
133
|
+
channel snowflake id, pages through messages newer than target.cursor (or
|
|
134
|
+
from "now minus maxDays" on first sync), maps each Discord message to a
|
|
135
|
+
@dxos/types Message, appends the batch to the channel's ECHO Feed, then
|
|
136
|
+
advances target.cursor to the newest id seen. Targets are processed in
|
|
137
|
+
parallel (concurrency 3). Failures on one target record lastError on that
|
|
138
|
+
target and continue; the overall op only fails if the outer guild-list or
|
|
139
|
+
Discord layer setup fails.
|
|
140
|
+
input:
|
|
141
|
+
connection: Ref<Connection>
|
|
142
|
+
priority?: string # cursor id of the binding to sync first
|
|
143
|
+
output: { pulled: PullResult }
|
|
144
|
+
effects: [http, echo:write]
|
|
145
|
+
requires: [DiscordREST]
|
|
146
|
+
note: >
|
|
147
|
+
Message mapping skips non-DEFAULT/non-REPLY types (joins, pins, calls, …)
|
|
148
|
+
to keep the feed clean. Discord embeds and attachments are not yet mapped
|
|
149
|
+
to ContentBlock — plain text only for now. A toast is emitted on success
|
|
150
|
+
or failure via LayoutOperation.AddToast.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Channel backend
|
|
154
|
+
|
|
155
|
+
```mdl
|
|
156
|
+
service DiscordChannelBackend
|
|
157
|
+
desc: >
|
|
158
|
+
plugin-thread `ChannelBackendProvider` for Discord channels, so agents and other callers reach
|
|
159
|
+
Discord through plugin-thread's generic channel operations rather than Discord-specific ones.
|
|
160
|
+
ops:
|
|
161
|
+
send(channel, message) # REST post into the first configured Discord channel
|
|
162
|
+
threads.send(channel, thread, msg) # REST post into a thread or DM channel id
|
|
163
|
+
openDirect(channel, person) # DM channel from the person's `discord` identity
|
|
164
|
+
connection.start/stop/status # PUT/DELETE/GET /compute/discord/bots/:applicationId on EDGE
|
|
165
|
+
note: >
|
|
166
|
+
Posts use plain fetch (no proxy) so they also run on EDGE; text past 2000 characters is split,
|
|
167
|
+
and mentions are never echoed. The receipt carries `properties.discord.messageId`, which EDGE's
|
|
168
|
+
reply mirror reads to skip posts the agent already made. The Channel's ObjectProperties surface
|
|
169
|
+
renders the config form, the bot status and Start/Stop.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Features
|
|
173
|
+
|
|
174
|
+
```mdl
|
|
175
|
+
feat F-1: Bot Identity and Credential Setup
|
|
176
|
+
|
|
177
|
+
req F-1.1: User provides a bot token from the Discord developer portal via the credential form.
|
|
178
|
+
req F-1.2:
|
|
179
|
+
when: user submits the credential form
|
|
180
|
+
then: token is validated against GET /users/@me before any Integration is persisted
|
|
181
|
+
errors: [InvalidToken: Discord rejected the token with 401]
|
|
182
|
+
req F-1.3:
|
|
183
|
+
when: token is valid
|
|
184
|
+
then: bot's display name (global_name ?? username) is stored on AccessToken.account
|
|
185
|
+
req F-1.4: AccessToken is stored in ECHO and referenced by the Integration; the raw token is never re-exposed after setup.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```mdl
|
|
189
|
+
feat F-2: Channel Discovery
|
|
190
|
+
|
|
191
|
+
req F-2.1:
|
|
192
|
+
when: user opens the Integration target picker
|
|
193
|
+
then: op:GetDiscordChannels is invoked and returns one entry per reachable text/announcement channel
|
|
194
|
+
req F-2.2: Entries are labeled "#channel — Guild" to disambiguate channels with identical names across guilds.
|
|
195
|
+
req F-2.3:
|
|
196
|
+
when: a guild's channel list cannot be fetched (bot removed, network error)
|
|
197
|
+
then: that guild's channels are omitted from results; other guilds are unaffected
|
|
198
|
+
req F-2.4: User selects one or more channels as Integration targets; unselected channels leave no trace in the space.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```mdl
|
|
202
|
+
feat F-3: Incremental Message Sync
|
|
203
|
+
|
|
204
|
+
req F-3.1:
|
|
205
|
+
when: op:SyncDiscordChannel is invoked for an Integration
|
|
206
|
+
then: for each selected target channel, messages newer than target.cursor are fetched and appended
|
|
207
|
+
req F-3.2:
|
|
208
|
+
when: a target has no cursor (first sync)
|
|
209
|
+
then: lookback window is determined by DiscordTargetOptions.maxDays (default 30, max ~3 years)
|
|
210
|
+
req F-3.3:
|
|
211
|
+
when: sync succeeds for a target
|
|
212
|
+
then: target.cursor advances to the newest message snowflake id seen; subsequent runs are incremental
|
|
213
|
+
req F-3.4:
|
|
214
|
+
when: sync fails for a target
|
|
215
|
+
then: target.lastError is set with a human-readable description; other targets continue unaffected
|
|
216
|
+
req F-3.5: System messages (joins, pins, calls) are filtered out; only DEFAULT (type 0) and REPLY (type 19) messages are stored.
|
|
217
|
+
req F-3.6: Discord bot replies carry the referenced message id in Message.threadId to enable reply-chain reconstruction.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```mdl
|
|
221
|
+
feat F-4: Channel Materialization
|
|
222
|
+
|
|
223
|
+
req F-4.1:
|
|
224
|
+
when: a target channel is synced for the first time
|
|
225
|
+
then: a local ECHO Channel keyed by the Discord channel snowflake id is created in the space
|
|
226
|
+
req F-4.2: Channel keying uses Obj.Meta foreign-key [{source: 'discord.com', id: channelSnowflakeId}]; re-running sync on the same channel is idempotent.
|
|
227
|
+
req F-4.3:
|
|
228
|
+
when: the channel's name changes on Discord
|
|
229
|
+
then: the local Channel.name is updated to the "#channel — Guild" label on the next sync
|
|
230
|
+
req F-4.4:
|
|
231
|
+
when: a target ref exists from a legacy integration (remoteId not set)
|
|
232
|
+
then: the Discord channel id is recovered from the Channel's foreign-key metadata rather than the target entry
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```mdl
|
|
236
|
+
feat F-5: ECHO Persistence and Replication
|
|
237
|
+
|
|
238
|
+
req F-5.1: Each synced Message is appended to the Channel's ECHO Feed via Feed.append.
|
|
239
|
+
req F-5.2:
|
|
240
|
+
when: a peer receives a Message-feed update
|
|
241
|
+
then: the channel's message list reflects the new messages without additional sync
|
|
242
|
+
tags: [collaborative]
|
|
243
|
+
req F-5.3: All ECHO mutations (Channel creation, Message append, cursor update) are transacted per-target so a mid-sync crash cannot leave the space in a partially-updated state.
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Acceptance
|
|
247
|
+
|
|
248
|
+
```mdl
|
|
249
|
+
scenario T-1: Valid bot token accepted
|
|
250
|
+
given: user opens the Discord integration credential form
|
|
251
|
+
when: user pastes a valid bot token and submits
|
|
252
|
+
then:
|
|
253
|
+
- GET /users/@me returns 200
|
|
254
|
+
- AccessToken created with account = bot display name
|
|
255
|
+
- Integration created with targets: []
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```mdl
|
|
259
|
+
scenario T-2: Invalid token rejected
|
|
260
|
+
given: user opens the credential form
|
|
261
|
+
when: user pastes an invalid token (application id, expired token, etc.)
|
|
262
|
+
then:
|
|
263
|
+
- Discord returns 401
|
|
264
|
+
- Form shows error: 'Discord rejected the token (401). Reset the bot token…'
|
|
265
|
+
- No AccessToken or Integration is persisted
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```mdl
|
|
269
|
+
scenario T-3: Channel discovery lists all guilds
|
|
270
|
+
given: a valid Integration whose bot belongs to two guilds, each with two text channels
|
|
271
|
+
when: op:GetDiscordChannels is invoked
|
|
272
|
+
then:
|
|
273
|
+
- output.targets contains four entries, one per channel
|
|
274
|
+
- each entry has id, name ("#channel — Guild"), and optional description
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```mdl
|
|
278
|
+
scenario T-4: Channel discovery skips failed guild
|
|
279
|
+
given: bot belongs to two guilds; guild B's channel list returns 403
|
|
280
|
+
when: op:GetDiscordChannels is invoked
|
|
281
|
+
then:
|
|
282
|
+
- targets contains only guild A's channels
|
|
283
|
+
- no error is raised; guild B failure is logged and skipped
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
```mdl
|
|
287
|
+
scenario T-5: First sync materializes channel and appends messages
|
|
288
|
+
given: Integration with one selected target (no cursor); Discord returns 10 messages
|
|
289
|
+
when: op:SyncDiscordChannel is invoked
|
|
290
|
+
then:
|
|
291
|
+
- local Channel created in space with source=discord.com foreign key
|
|
292
|
+
- 10 Message objects appended to Channel.feed
|
|
293
|
+
- target.cursor set to the newest message snowflake id
|
|
294
|
+
- output.pulled.added === 10
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```mdl
|
|
298
|
+
scenario T-6: Incremental sync fetches only new messages
|
|
299
|
+
given: target.cursor is set to snowflake S; Discord returns 3 messages after S
|
|
300
|
+
when: op:SyncDiscordChannel is invoked
|
|
301
|
+
then:
|
|
302
|
+
- only messages with id > S are fetched
|
|
303
|
+
- 3 new Messages appended; existing Messages unchanged
|
|
304
|
+
- target.cursor advances to the newest of the 3 ids
|
|
305
|
+
- output.pulled.added === 3
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```mdl
|
|
309
|
+
scenario T-7: System messages filtered out
|
|
310
|
+
given: Discord returns 5 messages: 2 DEFAULT, 1 REPLY, 1 JOIN (type 7), 1 PIN (type 6)
|
|
311
|
+
when: op:SyncDiscordChannel maps the batch
|
|
312
|
+
then:
|
|
313
|
+
- 3 Messages appended (DEFAULT + REPLY only)
|
|
314
|
+
- JOIN and PIN messages discarded
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```mdl
|
|
318
|
+
scenario T-8: Sync failure recorded per target
|
|
319
|
+
given: Integration with two selected targets; target B's channel returns 403
|
|
320
|
+
when: op:SyncDiscordChannel is invoked
|
|
321
|
+
then:
|
|
322
|
+
- target A syncs successfully; its cursor advances
|
|
323
|
+
- target B.lastError is set with a human-readable Discord error string
|
|
324
|
+
- op returns without throwing; output.pulled.added counts only target A's messages
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```mdl
|
|
328
|
+
scenario T-9: Channel re-sync is idempotent
|
|
329
|
+
given: Channel already exists for discord channel id X with 5 Messages
|
|
330
|
+
when: op:SyncDiscordChannel is invoked again with same cursor
|
|
331
|
+
then:
|
|
332
|
+
- no duplicate Channel created (foreign-key lookup returns existing Channel)
|
|
333
|
+
- 0 Messages appended (no new messages beyond cursor)
|
|
334
|
+
- output.pulled.added === 0
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
```mdl
|
|
338
|
+
scenario T-10: maxDays controls initial lookback
|
|
339
|
+
given: new target with DiscordTargetOptions.maxDays = 14
|
|
340
|
+
when: op:SyncDiscordChannel is invoked for the first time
|
|
341
|
+
then:
|
|
342
|
+
- after parameter is a snowflake derived from (now - 14 days)
|
|
343
|
+
- Messages older than 14 days are not fetched
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## QA
|
|
349
|
+
|
|
350
|
+
Executable tests. Each step carries a human instruction (`do`) and, where the step maps to an
|
|
351
|
+
invocable operation, an agent binding (`invoke`). Report failures by test and step number.
|
|
352
|
+
|
|
353
|
+
plugin-discord is not in `getDefaults` or the dev-only default set (see
|
|
354
|
+
`packages/apps/composer-app/src/plugin-defs.tsx`) — it is OFF by default. Its whole surface
|
|
355
|
+
depends on a real Discord bot token, which no agent session holds, so the credential and discovery
|
|
356
|
+
steps are `blocked` per Execution Rule 9; only the sync step, given a pre-existing Integration, can
|
|
357
|
+
run unattended.
|
|
358
|
+
|
|
359
|
+
```mdl
|
|
360
|
+
test QA-1: Connect a bot and sync a channel
|
|
361
|
+
desc: The core Discord round-trip — add the credential, discover channels, select one, sync messages.
|
|
362
|
+
actors: human
|
|
363
|
+
covers: [F-1.1, F-1.2, F-2.1, F-3.1, F-4.1, T-1, T-3, T-5]
|
|
364
|
+
status: blocked
|
|
365
|
+
given:
|
|
366
|
+
- an identity exists
|
|
367
|
+
- space: a space is open
|
|
368
|
+
- plugin-discord is enabled — it is OFF by default
|
|
369
|
+
- connection: a real Discord bot token, invited to at least one guild with a text channel,
|
|
370
|
+
bound as a Connection
|
|
371
|
+
steps:
|
|
372
|
+
- name: Add the Discord integration credential
|
|
373
|
+
do: Open Settings > Integrations > Discord, and paste a bot token from the Discord developer portal.
|
|
374
|
+
expect: the token validates against GET /users/@me and a Connection is created with targets [] and the bot's display name
|
|
375
|
+
blocked: |
|
|
376
|
+
Requires a real Discord bot token — no agent session holds credentials for an external
|
|
377
|
+
service. A human tester must run this step manually.
|
|
378
|
+
- name: Discover channels
|
|
379
|
+
do: Open the Integration's target picker.
|
|
380
|
+
invoke: [op:GetDiscordChannels] { connection: $given.connection }
|
|
381
|
+
capture: discovered
|
|
382
|
+
expect: one entry per reachable text/announcement channel, labeled "#channel — Guild"
|
|
383
|
+
blocked: Depends on the previous step's Connection, which is itself blocked without a real token.
|
|
384
|
+
- name: Select a channel and materialize its local Channel
|
|
385
|
+
do: Select one channel from the discovery list and confirm.
|
|
386
|
+
invoke: [op:MaterializeDiscordTarget] { connection: $given.connection, remoteTarget: $discovered.targets[0] }
|
|
387
|
+
capture: materialized
|
|
388
|
+
expect: a local Channel bound to the selected Discord channel now exists
|
|
389
|
+
blocked: Same dependency chain — cannot run without a live Connection.
|
|
390
|
+
- name: Sync the bound targets
|
|
391
|
+
do: (no separate gesture — sync runs automatically once a target is bound, or via a manual "Sync now" action)
|
|
392
|
+
invoke: [op:SyncDiscordChannel] { connection: $given.connection }
|
|
393
|
+
capture: synced
|
|
394
|
+
expect: the materialized Channel's recent messages appear in the feed
|
|
395
|
+
assert: return $synced.pulled.added >= 0
|
|
396
|
+
blocked: Same dependency chain.
|
|
397
|
+
after:
|
|
398
|
+
- name: Remove the synced channel and integration
|
|
399
|
+
do: Delete the synced Channel object and remove the Discord Integration from Settings.
|
|
400
|
+
invoke: org.dxos.operation.space.removeObjects { refs: [$materialized.target] }
|
|
401
|
+
expect: the Channel and its messages are gone, and the Integration no longer lists the target
|
|
402
|
+
blocked: Same dependency chain.
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
```mdl
|
|
406
|
+
test QA-2: Re-sync is idempotent
|
|
407
|
+
desc: |
|
|
408
|
+
The incremental-sync guarantee (F-3.3, F-4.2) against an Integration that already exists —
|
|
409
|
+
still requires a live bot token, so this is also blocked absent one, but is written separately
|
|
410
|
+
since it exercises different requirements than QA-1's first sync.
|
|
411
|
+
actors: agent
|
|
412
|
+
covers: [F-3.3, F-4.2, T-6, T-9]
|
|
413
|
+
status: blocked
|
|
414
|
+
given:
|
|
415
|
+
- an identity exists
|
|
416
|
+
- connection: a Discord Connection with at least one selected target already synced once
|
|
417
|
+
(see QA-1)
|
|
418
|
+
steps:
|
|
419
|
+
- name: Re-run sync against the same cursor
|
|
420
|
+
do: Trigger a sync again without any new Discord activity.
|
|
421
|
+
invoke: [op:SyncDiscordChannel] { connection: $given.connection }
|
|
422
|
+
expect: no duplicate Channel is created and pulled.added is 0
|
|
423
|
+
assert: return $result.pulled.added === 0
|
|
424
|
+
blocked: Requires a live Discord bot token and an already-synced Integration (see QA-1).
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Appendix: Extension Definitions
|
|
430
|
+
|
|
431
|
+
Extension block types used in this document are defined below using
|
|
432
|
+
the core `ext` primitive — the only construct the base language provides.
|
|
433
|
+
|
|
434
|
+
```mdl
|
|
435
|
+
ext type
|
|
436
|
+
uri: org.dxos.mdl.type@1.0
|
|
437
|
+
desc: A named data structure with typed fields and optional literals.
|
|
438
|
+
fields:
|
|
439
|
+
desc?: Prose
|
|
440
|
+
fields?: FieldMap # name[?]: TypeExpr (# inline comment)
|
|
441
|
+
literals?: UnionList # a | b | c
|
|
442
|
+
extends?: TypeRef[]
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
```mdl
|
|
446
|
+
ext feat
|
|
447
|
+
uri: org.dxos.mdl.feat@1.0
|
|
448
|
+
desc: A named feature grouping one or more requirements.
|
|
449
|
+
fields:
|
|
450
|
+
desc?: Prose
|
|
451
|
+
req: RequirementList
|
|
452
|
+
nesting: self # feat blocks may contain feat blocks
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
```mdl
|
|
456
|
+
ext scenario
|
|
457
|
+
uri: org.dxos.mdl.scenario@1.0
|
|
458
|
+
desc: An acceptance scenario expressed as given / when / then steps.
|
|
459
|
+
fields:
|
|
460
|
+
given?: Step | Step[]
|
|
461
|
+
when?: Step | Step[]
|
|
462
|
+
then: Step | Step[]
|
|
463
|
+
tags?: TagList
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
```mdl
|
|
467
|
+
ext component
|
|
468
|
+
uri: org.dxos.mdl.component@1.0
|
|
469
|
+
desc: A UI component with props, internal state, slots, actions, and events.
|
|
470
|
+
fields:
|
|
471
|
+
desc?: Prose
|
|
472
|
+
props?: FieldMap # external inputs (immutable inside component)
|
|
473
|
+
state?: FieldMap # internal reactive state
|
|
474
|
+
slots?: FieldMap # named ReactNode injection points
|
|
475
|
+
actions?: ActionMap # methods the component exposes or handles
|
|
476
|
+
emits?: EventMap # events the component raises to its parent
|
|
477
|
+
layout?: CodeBlock # ASCII sketch of visual structure (non-normative)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
```mdl
|
|
481
|
+
ext op
|
|
482
|
+
uri: org.dxos.mdl.op@1.0
|
|
483
|
+
desc: |
|
|
484
|
+
A named operation with typed inputs, outputs, and declared errors.
|
|
485
|
+
Pure ops have no effects or requires. Effectful ops declare both.
|
|
486
|
+
fields:
|
|
487
|
+
desc?: Prose
|
|
488
|
+
input?: FieldMap # named input parameters
|
|
489
|
+
output?: TypeExpr # return type
|
|
490
|
+
errors?: ErrorMap # name: Prose (when this error occurs)
|
|
491
|
+
effects?: EffectList # echo:read | echo:write | http | fs | ...
|
|
492
|
+
requires?: ServiceList # injected service dependencies
|
|
493
|
+
note?: Prose # implementation guidance (non-normative)
|
|
494
|
+
```
|