@addozhang/dsh-discord 0.1.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 +21 -0
- package/README.md +68 -0
- package/cordis.patch.yml +14 -0
- package/lib/client/DiscordSettingsCard.d.ts +6 -0
- package/lib/client/PluginCard.d.ts +43 -0
- package/lib/client/card-controller.d.ts +22 -0
- package/lib/client/card-form.d.ts +111 -0
- package/lib/client/fields.d.ts +50 -0
- package/lib/client/index.d.ts +12 -0
- package/lib/client/nav-icon.d.ts +21 -0
- package/lib/client/settings-model.d.ts +49 -0
- package/lib/client/slot-contract.d.ts +57 -0
- package/lib/client/snapshot-store.d.ts +9 -0
- package/lib/client.js +1622 -0
- package/lib/compose.d.ts +158 -0
- package/lib/compose.js +227 -0
- package/lib/credential.d.ts +20 -0
- package/lib/credential.js +24 -0
- package/lib/discord/commands.d.ts +45 -0
- package/lib/discord/commands.js +87 -0
- package/lib/discord/components.d.ts +29 -0
- package/lib/discord/components.js +57 -0
- package/lib/discord/delivery.d.ts +21 -0
- package/lib/discord/delivery.js +31 -0
- package/lib/discord/interaction-lifecycle.d.ts +42 -0
- package/lib/discord/interaction-lifecycle.js +78 -0
- package/lib/discord/rest.d.ts +74 -0
- package/lib/discord/rest.js +149 -0
- package/lib/discord/selector.d.ts +39 -0
- package/lib/discord/selector.js +70 -0
- package/lib/discord/thread-port.d.ts +37 -0
- package/lib/discord/thread-port.js +58 -0
- package/lib/dsh/api-proxy-face.d.ts +253 -0
- package/lib/dsh/api-proxy-face.js +370 -0
- package/lib/features/adapter-status.d.ts +111 -0
- package/lib/features/adapter-status.js +103 -0
- package/lib/features/approval-expiry.d.ts +27 -0
- package/lib/features/approval-expiry.js +36 -0
- package/lib/features/approval-routing.d.ts +50 -0
- package/lib/features/approval-routing.js +57 -0
- package/lib/features/approval-store.d.ts +86 -0
- package/lib/features/approval-store.js +119 -0
- package/lib/features/approval-view.d.ts +47 -0
- package/lib/features/approval-view.js +48 -0
- package/lib/features/ask-wiring.d.ts +74 -0
- package/lib/features/ask-wiring.js +151 -0
- package/lib/features/host-status.d.ts +41 -0
- package/lib/features/host-status.js +24 -0
- package/lib/features/image-collection.d.ts +56 -0
- package/lib/features/image-collection.js +79 -0
- package/lib/features/image-download.d.ts +48 -0
- package/lib/features/image-download.js +77 -0
- package/lib/features/image-submission.d.ts +67 -0
- package/lib/features/image-submission.js +57 -0
- package/lib/features/interaction-router.d.ts +84 -0
- package/lib/features/interaction-router.js +655 -0
- package/lib/features/model-control.d.ts +79 -0
- package/lib/features/model-control.js +51 -0
- package/lib/features/preset-control.d.ts +84 -0
- package/lib/features/preset-control.js +43 -0
- package/lib/features/project-bind.d.ts +69 -0
- package/lib/features/project-bind.js +57 -0
- package/lib/features/project-info.d.ts +25 -0
- package/lib/features/project-info.js +22 -0
- package/lib/features/project-list.d.ts +79 -0
- package/lib/features/project-list.js +93 -0
- package/lib/features/prompt-submission.d.ts +53 -0
- package/lib/features/prompt-submission.js +47 -0
- package/lib/features/question-expiry.d.ts +53 -0
- package/lib/features/question-expiry.js +56 -0
- package/lib/features/question-routing.d.ts +108 -0
- package/lib/features/question-routing.js +162 -0
- package/lib/features/question-store.d.ts +124 -0
- package/lib/features/question-store.js +186 -0
- package/lib/features/question-view.d.ts +58 -0
- package/lib/features/question-view.js +81 -0
- package/lib/features/queue-control.d.ts +63 -0
- package/lib/features/queue-control.js +31 -0
- package/lib/features/reconcile-bindings.d.ts +75 -0
- package/lib/features/reconcile-bindings.js +54 -0
- package/lib/features/reconcile-deliveries.d.ts +29 -0
- package/lib/features/reconcile-deliveries.js +28 -0
- package/lib/features/reconcile-events.d.ts +49 -0
- package/lib/features/reconcile-events.js +50 -0
- package/lib/features/reconcile-intents.d.ts +32 -0
- package/lib/features/reconcile-intents.js +32 -0
- package/lib/features/reconcile-interactions.d.ts +59 -0
- package/lib/features/reconcile-interactions.js +54 -0
- package/lib/features/session-adopt.d.ts +89 -0
- package/lib/features/session-adopt.js +77 -0
- package/lib/features/session-creation.d.ts +49 -0
- package/lib/features/session-creation.js +43 -0
- package/lib/features/session-mainline.d.ts +75 -0
- package/lib/features/session-mainline.js +96 -0
- package/lib/features/session-resume.d.ts +44 -0
- package/lib/features/session-resume.js +40 -0
- package/lib/features/skill-run.d.ts +48 -0
- package/lib/features/skill-run.js +25 -0
- package/lib/features/steer-control.d.ts +37 -0
- package/lib/features/steer-control.js +28 -0
- package/lib/features/stop-control.d.ts +38 -0
- package/lib/features/stop-control.js +31 -0
- package/lib/features/task-admission.d.ts +29 -0
- package/lib/features/task-admission.js +21 -0
- package/lib/features/thread-continuation.d.ts +36 -0
- package/lib/features/thread-continuation.js +18 -0
- package/lib/features/thread-creation.d.ts +81 -0
- package/lib/features/thread-creation.js +81 -0
- package/lib/features/thread-routing.d.ts +49 -0
- package/lib/features/thread-routing.js +34 -0
- package/lib/features/turn-ownership.d.ts +39 -0
- package/lib/features/turn-ownership.js +40 -0
- package/lib/features/unbound-mention.d.ts +49 -0
- package/lib/features/unbound-mention.js +31 -0
- package/lib/features/workspace-channel.d.ts +41 -0
- package/lib/features/workspace-channel.js +47 -0
- package/lib/gateway/gateway.d.ts +50 -0
- package/lib/gateway/gateway.js +229 -0
- package/lib/gateway/inbound.d.ts +85 -0
- package/lib/gateway/inbound.js +186 -0
- package/lib/gateway/ingress.d.ts +15 -0
- package/lib/gateway/ingress.js +22 -0
- package/lib/i18n.d.ts +92 -0
- package/lib/i18n.js +180 -0
- package/lib/index.d.ts +17 -0
- package/lib/index.js +869 -0
- package/lib/lifecycle.d.ts +44 -0
- package/lib/lifecycle.js +84 -0
- package/lib/policy/authorization.d.ts +41 -0
- package/lib/policy/authorization.js +64 -0
- package/lib/policy/disclosure.d.ts +93 -0
- package/lib/policy/disclosure.js +153 -0
- package/lib/policy/guard.d.ts +31 -0
- package/lib/policy/guard.js +46 -0
- package/lib/policy/suppress.d.ts +13 -0
- package/lib/policy/suppress.js +20 -0
- package/lib/settings-namespace.d.ts +7 -0
- package/lib/settings-namespace.js +6 -0
- package/lib/settings.d.ts +37 -0
- package/lib/settings.js +102 -0
- package/lib/startup.d.ts +16 -0
- package/lib/startup.js +60 -0
- package/lib/state/bindings.d.ts +46 -0
- package/lib/state/bindings.js +55 -0
- package/lib/state/channel-bindings.d.ts +37 -0
- package/lib/state/channel-bindings.js +43 -0
- package/lib/state/domain.d.ts +82 -0
- package/lib/state/domain.js +86 -0
- package/lib/state/effect-machine.d.ts +37 -0
- package/lib/state/effect-machine.js +52 -0
- package/lib/state/fail-closed.d.ts +44 -0
- package/lib/state/fail-closed.js +57 -0
- package/lib/state/intents.d.ts +51 -0
- package/lib/state/intents.js +73 -0
- package/lib/state/records.d.ts +37 -0
- package/lib/state/records.js +30 -0
- package/lib/state/retention.d.ts +74 -0
- package/lib/state/retention.js +68 -0
- package/lib/state/session-owners.d.ts +50 -0
- package/lib/state/session-owners.js +50 -0
- package/lib/stream/finalizer.d.ts +49 -0
- package/lib/stream/finalizer.js +67 -0
- package/lib/stream/icons.d.ts +15 -0
- package/lib/stream/icons.js +36 -0
- package/lib/stream/live.d.ts +140 -0
- package/lib/stream/live.js +515 -0
- package/lib/stream/markdown.d.ts +24 -0
- package/lib/stream/markdown.js +95 -0
- package/lib/stream/outbound.d.ts +27 -0
- package/lib/stream/outbound.js +25 -0
- package/lib/stream/render-fence.d.ts +27 -0
- package/lib/stream/render-fence.js +37 -0
- package/lib/stream/render-model.d.ts +48 -0
- package/lib/stream/render-model.js +68 -0
- package/lib/stream/splitter.d.ts +8 -0
- package/lib/stream/splitter.js +58 -0
- package/lib/stream/tool-view.d.ts +40 -0
- package/lib/stream/tool-view.js +39 -0
- package/lib/stream/typing.d.ts +22 -0
- package/lib/stream/typing.js +58 -0
- package/lib/stream/update-scheduler.d.ts +20 -0
- package/lib/stream/update-scheduler.js +65 -0
- package/package.json +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 addozhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @addozhang/dsh-discord
|
|
2
|
+
|
|
3
|
+
Discord-first adapter for [DeepSeek Harness](https://github.com/deepseek-ai) — run DSH sessions from a Discord guild: mention the bot to open a task thread, steer and stop turns, answer approvals and questions inline, and watch the answer stream in.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Mention-driven sessions** — an authorized `@bot <task>` in a bound channel anchors a thread (the author's message becomes the first post), creates the DSH session, and submits the prompt at most once. Follow-ups inside the thread queue without a mention.
|
|
8
|
+
- **Stream rendering** — typing indicators, a single edited head message, per-tool activity rows, fenced long-answer splitting, one-time finalize; the activity message is deleted when the turn ends.
|
|
9
|
+
- **Approvals & questions** — DSH ask frames become Discord buttons, select menus, and a free-text modal; ownership is enforced (the asker — or the thread owner on later turns — clicks), expiry sweeps fail closed, and remote resolution retires the controls.
|
|
10
|
+
- **Session control** — `/steer`, `/stop`, `/queue list|remove` with turn-ownership checks; `/project bind|list|info` for guild↔workspace binding; `/guild forget` for operator cleanup.
|
|
11
|
+
- **Settings card** — token onboarding (paste + Connect; stored in the Host credential service, never in settings or logs), connect/disconnect, guild allowlist, thread auto-archive, and bot language (follow the DSH language, or pin 中文/English).
|
|
12
|
+
- **Bilingual copy** — every Discord-visible string ships in Chinese and English, selected live from the card.
|
|
13
|
+
- **Hardened by design** — deny-first authorization inside an explicit guild allowlist, byte-level mention neutralization plus `allowed_mentions` on every wire body, at-most-once DSH submission with unknown-preserving reconciliation, and durable bindings that survive restarts.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
The adapter runs as a DSH plugin bundle inside a web profile:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm build && pnpm pack --pack-destination /tmp
|
|
21
|
+
cd "$DSH_HOME/profiles/<your-profile>"
|
|
22
|
+
pnpm add file:/tmp/addozhang-dsh-discord-0.1.0.tgz
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
and register it in the profile's `package.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@addozhang/dsh-discord"] } }
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> Publishing to npm is pending (account unavailable). Until then, install from the packed tarball.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
1. Boot the profile and open the web UI.
|
|
38
|
+
2. In **Settings → Discord**, paste the bot token (Discord Developer Portal → your application → Bot → Reset Token) and press **Connect**. The token is stored by the Host credential service — never in settings, logs, or the client.
|
|
39
|
+
3. Fill in the allowed servers (server IDs via Discord's Developer Mode → right-click → Copy Server ID) and invite the bot to those guilds.
|
|
40
|
+
4. Mention the bot in a bound channel to start a session.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
| Command | Where | What |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `/project bind` | any channel | bind the guild to a workspace (admin; provisions the home channel) |
|
|
47
|
+
| `/project list` / `info` | any channel | list workspaces / inspect this channel's binding |
|
|
48
|
+
| `/queue list`, `/queue remove` | session thread | inspect and trim the pending queue |
|
|
49
|
+
| `/steer`, `/stop` | session thread | steer or cancel the running turn (owner only) |
|
|
50
|
+
| `/model`, `/preset`, `/skill` | per context | model/preset defaults and skill runs |
|
|
51
|
+
| `/host status` | any channel | connection and version |
|
|
52
|
+
| `/guild forget` | any channel | operator-only removal of adapter records |
|
|
53
|
+
|
|
54
|
+
## Development
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
pnpm install --ignore-scripts
|
|
58
|
+
pnpm test # 642 tests incl. gateway/REST twin E2E
|
|
59
|
+
pnpm typecheck
|
|
60
|
+
pnpm lint
|
|
61
|
+
pnpm build # lib + client bundle
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The implementation follows the OpenSpec change at `openspec/changes/build-discord-native-adapter/` (design, capability specs, verification checklist, review reports).
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# The dsh-discord bundle patch: mounts the embedded Discord adapter into the
|
|
2
|
+
# Web profile's Host plugin tree. The row injects every required DSH service,
|
|
3
|
+
# so activation waits until the Host provides them; the plugin's own startup
|
|
4
|
+
# validation (task 1.3) fails loud when a dependency is present but
|
|
5
|
+
# incompatible.
|
|
6
|
+
#
|
|
7
|
+
# The browser half is declared through this package's `dsh.client` manifest,
|
|
8
|
+
# so the modules node half scans it into the browser roster automatically and
|
|
9
|
+
# no client row is needed here.
|
|
10
|
+
|
|
11
|
+
- insert:
|
|
12
|
+
- id: dsh-discord
|
|
13
|
+
name: '@addozhang/dsh-discord'
|
|
14
|
+
inject: [apiProxy, credentials, settings, storageDomain, connection]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The Discord adapter's plugin settings card: connection status, guild allowlist, and output behavior. */
|
|
2
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { DiscordCardFace } from './card-controller.js';
|
|
4
|
+
/** Props the runtime binds for the Discord settings card. */
|
|
5
|
+
export type DiscordSettingsCardProps = PropsRuntime<"settings.section"> & PropsLocale<'dsh-discord'> & InjectFace<DiscordCardFace>;
|
|
6
|
+
export declare function DiscordSettingsCard(props: DiscordSettingsCardProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One plugin card's chrome: a disclosure header naming the plugin, the card's
|
|
3
|
+
* form state, and the save/discard footer. Disclosure is card-local reading
|
|
4
|
+
* state; staged edits outlive collapsing and mark the header unsaved.
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactNode } from 'react';
|
|
7
|
+
import type { CardShell } from './card-form.js';
|
|
8
|
+
/** Copy keys the host settings section provides to every card. */
|
|
9
|
+
export interface PluginCardCopy {
|
|
10
|
+
expand: string;
|
|
11
|
+
collapse: string;
|
|
12
|
+
unsaved: string;
|
|
13
|
+
readOnly: string;
|
|
14
|
+
saveFailed: string;
|
|
15
|
+
discard: string;
|
|
16
|
+
save: string;
|
|
17
|
+
saving: string;
|
|
18
|
+
}
|
|
19
|
+
/** Props for the Discord adapter's card chrome. */
|
|
20
|
+
export interface PluginCardProps {
|
|
21
|
+
/** Card copy from the section locale. */
|
|
22
|
+
copy: PluginCardCopy;
|
|
23
|
+
/** Card title. */
|
|
24
|
+
title: string;
|
|
25
|
+
/** Monochrome mark rendered before the title. */
|
|
26
|
+
icon?: ReactNode;
|
|
27
|
+
/** Line describing what this card's settings govern. */
|
|
28
|
+
description: string;
|
|
29
|
+
/** The card's form state. */
|
|
30
|
+
state: CardShell;
|
|
31
|
+
/** Write every staged edit. */
|
|
32
|
+
onSave: () => void;
|
|
33
|
+
/** Drop every staged edit. */
|
|
34
|
+
onDiscard: () => void;
|
|
35
|
+
/** The plugin's controls. */
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Render the Discord adapter card chrome.
|
|
40
|
+
* @param props - the card's copy, state, and controls.
|
|
41
|
+
* @returns the card, or nothing when the namespace is unavailable.
|
|
42
|
+
*/
|
|
43
|
+
export declare function PluginCard(props: PluginCardProps): ReactNode;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The card's registration-side controller. It owns the staged form over one
|
|
3
|
+
* bound settings scope and exposes the inject face the slot registration
|
|
4
|
+
* hands to the component: the edit actions plus the `discordCard` snapshot
|
|
5
|
+
* store the framework binds as the `useDiscordCard` selector hook.
|
|
6
|
+
*/
|
|
7
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
|
|
8
|
+
import type { AdapterStatusView } from '../features/adapter-status.js';
|
|
9
|
+
import type { DiscordSettings } from '../settings.js';
|
|
10
|
+
import { type CardManagement, type DiscordCardFace, type DiscordCardState } from './card-form.js';
|
|
11
|
+
export declare class DiscordCardController {
|
|
12
|
+
private readonly form;
|
|
13
|
+
private management;
|
|
14
|
+
constructor(scope: SettingsScope<DiscordSettings>);
|
|
15
|
+
/** Attach the token write/connect path once the RPC face is known. */
|
|
16
|
+
setManagement(management: CardManagement): void;
|
|
17
|
+
/** Publish the Host's sanitized connection status onto the card. */
|
|
18
|
+
setStatus(view: AdapterStatusView | undefined): void;
|
|
19
|
+
/** Build the face the registration's inject factory returns per entry. */
|
|
20
|
+
face(): DiscordCardFace;
|
|
21
|
+
}
|
|
22
|
+
export type { DiscordCardFace, DiscordCardState };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Discord card's staged form model.
|
|
3
|
+
*
|
|
4
|
+
* A card stages what the user types and writes it only on save. Each field
|
|
5
|
+
* shows its effective value and whether the user layer carries it — presence,
|
|
6
|
+
* not value comparison, marks an override. A field is invalid when its draft
|
|
7
|
+
* is not a list of Discord IDs, which blocks the save rather than dropping it.
|
|
8
|
+
*/
|
|
9
|
+
import type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
10
|
+
import type { AdapterStatusView } from '../features/adapter-status.js';
|
|
11
|
+
import type { DiscordSettings } from '../settings.js';
|
|
12
|
+
import { type AdapterStatusPresentation } from './settings-model.js';
|
|
13
|
+
/** One field as its control renders it. */
|
|
14
|
+
export interface CardFieldState {
|
|
15
|
+
text: string;
|
|
16
|
+
overridden: boolean;
|
|
17
|
+
invalid: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Form state the card chrome shares. */
|
|
20
|
+
export interface CardShell {
|
|
21
|
+
available: boolean;
|
|
22
|
+
writable: boolean;
|
|
23
|
+
dirty: boolean;
|
|
24
|
+
invalid: boolean;
|
|
25
|
+
saving: boolean;
|
|
26
|
+
failed: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** The edit actions a card's slot entry injects. */
|
|
29
|
+
export interface CardActions {
|
|
30
|
+
edit: (field: string, text: string) => void;
|
|
31
|
+
resetField: (field: string) => void;
|
|
32
|
+
save: () => void;
|
|
33
|
+
discard: () => void;
|
|
34
|
+
}
|
|
35
|
+
/** The ID list fields this card edits, in display order. */
|
|
36
|
+
export declare const DISCORD_ID_FIELDS: readonly ["allowedGuildIds", "memberUserIds", "memberRoleIds", "administratorUserIds", "administratorRoleIds", "deniedUserIds", "deniedRoleIds", "hostOperatorUserIds"];
|
|
37
|
+
/** The single-choice numeric field this card edits. */
|
|
38
|
+
export declare const ARCHIVE_FIELD: "threadAutoArchiveMinutes";
|
|
39
|
+
/** The adapter copy language field this card edits (16.25). */
|
|
40
|
+
export declare const LANGUAGE_FIELD: "language";
|
|
41
|
+
export type DiscordCardField = typeof DISCORD_ID_FIELDS[number] | typeof ARCHIVE_FIELD | typeof LANGUAGE_FIELD;
|
|
42
|
+
/** The adapter copy languages, as select choices (labels are endonyms). */
|
|
43
|
+
export declare const LANGUAGE_CHOICES: ReadonlyArray<{
|
|
44
|
+
value: string;
|
|
45
|
+
label: string;
|
|
46
|
+
}>;
|
|
47
|
+
/** The archive durations Discord accepts, as select choices. */
|
|
48
|
+
export declare const ARCHIVE_CHOICES: ReadonlyArray<{
|
|
49
|
+
value: string;
|
|
50
|
+
label: string;
|
|
51
|
+
}>;
|
|
52
|
+
/** What the Discord card renders. */
|
|
53
|
+
export type DiscordCardState = CardShell & Record<DiscordCardField, CardFieldState> & {
|
|
54
|
+
/** The Host's sanitized connection status, once reported. */
|
|
55
|
+
status: AdapterStatusPresentation | undefined;
|
|
56
|
+
};
|
|
57
|
+
/** The write path for the bot token and the connect trigger. The token
|
|
58
|
+
* value never enters card state — it flows client → Host over the plugin
|
|
59
|
+
* management channel and is stored in the credential service. */
|
|
60
|
+
export interface CardManagement {
|
|
61
|
+
/** Store the token durably and refresh the credential status. */
|
|
62
|
+
setToken(value: string): Promise<void>;
|
|
63
|
+
/** Re-run the adapter start chain with the stored credential. */
|
|
64
|
+
connect(): void;
|
|
65
|
+
/** Operator-initiated offline; the stored credential is kept. */
|
|
66
|
+
disconnect(): void;
|
|
67
|
+
/** Pull the Host status now instead of waiting for the next poll. */
|
|
68
|
+
refresh(): void;
|
|
69
|
+
}
|
|
70
|
+
/** The registration-side face the Discord card's slot entry injects. */
|
|
71
|
+
export interface DiscordCardFace extends CardActions {
|
|
72
|
+
hooks: {
|
|
73
|
+
/** Card snapshot bound by the renderer. */
|
|
74
|
+
discordCard: SnapshotStore<DiscordCardState>;
|
|
75
|
+
};
|
|
76
|
+
management?: CardManagement | undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stages the Discord card's ID-list edits over its settings namespace and
|
|
80
|
+
* writes them on save. Outcomes are read back from the namespace: the Host is
|
|
81
|
+
* the only authority on whether a value was accepted.
|
|
82
|
+
*/
|
|
83
|
+
export declare class DiscordCardForm {
|
|
84
|
+
private readonly scope;
|
|
85
|
+
private readonly staged;
|
|
86
|
+
private readonly listeners;
|
|
87
|
+
private saving;
|
|
88
|
+
private failed;
|
|
89
|
+
private status;
|
|
90
|
+
constructor(scope: SettingsScope<DiscordSettings>);
|
|
91
|
+
/**
|
|
92
|
+
* Publish the Host's sanitized status view. Edits, saves, and failures
|
|
93
|
+
* never touch it — connection state is the Host's report, not form state.
|
|
94
|
+
*/
|
|
95
|
+
setStatus(view: AdapterStatusView | undefined): void;
|
|
96
|
+
/** Publish the card state the renderer reads through its bound selector. */
|
|
97
|
+
bind(): SnapshotStore<DiscordCardState>;
|
|
98
|
+
/** Build the edit, reset, save, and discard actions bound to this form. */
|
|
99
|
+
actions(): CardActions;
|
|
100
|
+
private project;
|
|
101
|
+
private fieldState;
|
|
102
|
+
private currentText;
|
|
103
|
+
private snapshot;
|
|
104
|
+
private userLayer;
|
|
105
|
+
private stored;
|
|
106
|
+
private plan;
|
|
107
|
+
private clear;
|
|
108
|
+
private store;
|
|
109
|
+
private save;
|
|
110
|
+
private publish;
|
|
111
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written control for the Discord settings card. It renders one field's
|
|
3
|
+
* label, staged text, override badge, and reset action. Nothing here writes:
|
|
4
|
+
* the card's save is the single point where a draft becomes a document write.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
export interface FieldProps {
|
|
8
|
+
/** Stable id associating the label with its control. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Visible label. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** One-line explanation rendered under the control. */
|
|
13
|
+
hint: string;
|
|
14
|
+
/** Draft text this control renders. */
|
|
15
|
+
text: string;
|
|
16
|
+
/** True when saving would leave a user-layer entry for this field. */
|
|
17
|
+
overridden: boolean;
|
|
18
|
+
/** True when the draft is not a value this field accepts. */
|
|
19
|
+
invalid: boolean;
|
|
20
|
+
/** Copy for the overridden badge. */
|
|
21
|
+
overriddenLabel: string;
|
|
22
|
+
/** Copy for the reset control. */
|
|
23
|
+
resetLabel: string;
|
|
24
|
+
/** Copy shown in place of the hint while the draft is invalid. */
|
|
25
|
+
invalidLabel: string;
|
|
26
|
+
/** Disables the control while the document is read-only. */
|
|
27
|
+
disabled: boolean;
|
|
28
|
+
/** Stage draft text. */
|
|
29
|
+
onEdit: (text: string) => void;
|
|
30
|
+
/** Stage a clear so the field re-inherits the composition layer. */
|
|
31
|
+
onReset: () => void;
|
|
32
|
+
}
|
|
33
|
+
/** One choice in a SelectField. */
|
|
34
|
+
export interface SelectOption {
|
|
35
|
+
value: string;
|
|
36
|
+
label: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A staged single-choice field (native select) sharing the ID field's
|
|
40
|
+
* head layout: label, override badge, reset, and a hint/invalid note.
|
|
41
|
+
*/
|
|
42
|
+
export declare function SelectField(props: FieldProps & {
|
|
43
|
+
options: readonly SelectOption[];
|
|
44
|
+
}): ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* A staged multi-line Discord ID field.
|
|
47
|
+
* @param props - the field's copy, its staged text, and the edit actions.
|
|
48
|
+
* @returns the labelled control.
|
|
49
|
+
*/
|
|
50
|
+
export declare function ValueField(props: FieldProps): ReactNode;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Discord adapter's browser half. It binds the plugin's settings
|
|
3
|
+
* namespace scope on its own fiber, owns the card controller, and registers
|
|
4
|
+
* the settings card into the Plugins section's tab list until unload.
|
|
5
|
+
*/
|
|
6
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
7
|
+
/** Stable Cordis plugin name for diagnostics. */
|
|
8
|
+
export declare const name = "dsh-discord-client";
|
|
9
|
+
/** Client services the card needs: settings transport, slot registry, plugin RPC channel, and locale. */
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
/** Mount the Discord settings card into the Plugins section. */
|
|
12
|
+
export declare function apply(ctx: ClientContext): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The settings navigation is Host-rendered: its per-section icon mapping
|
|
3
|
+
* (`navIcon(id)` in the general-settings bundle) knows only the built-in
|
|
4
|
+
* sections and falls back to a gear for every plugin-contributed entry, and
|
|
5
|
+
* the slot registration has no icon field. This shim watches for the nav cell
|
|
6
|
+
* whose label reads "Discord" and swaps its gear for the monochrome Discord
|
|
7
|
+
* mark, styled by the Host's own navIcon class. Idempotent: the replacement
|
|
8
|
+
* carries a data marker, so re-running the sweep after our own mutation is a
|
|
9
|
+
* no-op.
|
|
10
|
+
*/
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
12
|
+
/** The Discord mark, monochrome: it inherits the surrounding text color. */
|
|
13
|
+
export declare function DiscordMark({ size }: {
|
|
14
|
+
size?: number;
|
|
15
|
+
}): ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Install the nav-icon shim and keep it applied while the settings dialog
|
|
18
|
+
* mounts and re-renders.
|
|
19
|
+
* @returns the disposer releasing the mutation observer.
|
|
20
|
+
*/
|
|
21
|
+
export declare function installDiscordNavIcon(): () => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AdapterStatusView } from '../features/adapter-status.js';
|
|
2
|
+
import type { DiscordSettings } from '../settings.js';
|
|
3
|
+
export interface DiscordSettingsDraft {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
allowedGuildIds: string;
|
|
6
|
+
memberUserIds: string;
|
|
7
|
+
memberRoleIds: string;
|
|
8
|
+
administratorUserIds: string;
|
|
9
|
+
administratorRoleIds: string;
|
|
10
|
+
deniedUserIds: string;
|
|
11
|
+
deniedRoleIds: string;
|
|
12
|
+
hostOperatorUserIds: string;
|
|
13
|
+
defaultVerbosity: DiscordSettings['defaultVerbosity'];
|
|
14
|
+
language: DiscordSettings['language'];
|
|
15
|
+
threadAutoArchiveMinutes: DiscordSettings['threadAutoArchiveMinutes'];
|
|
16
|
+
streamUpdateIntervalMs: number;
|
|
17
|
+
typingIntervalMs: number;
|
|
18
|
+
approvalTimeoutMs: number;
|
|
19
|
+
questionTimeoutMs: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function serializeIdList(value: string): string[];
|
|
22
|
+
export declare function createSettingsDraft(value: Partial<DiscordSettings>): DiscordSettingsDraft;
|
|
23
|
+
export declare function presentCredentialStatus(info: {
|
|
24
|
+
configured: boolean;
|
|
25
|
+
writable: boolean;
|
|
26
|
+
source?: string;
|
|
27
|
+
}): {
|
|
28
|
+
label: string;
|
|
29
|
+
writable: boolean;
|
|
30
|
+
source?: string;
|
|
31
|
+
};
|
|
32
|
+
/** The card status line's presentation: locale keys plus an action flag. */
|
|
33
|
+
export interface AdapterStatusPresentation {
|
|
34
|
+
connectionKey: DiscordStatusConnectionKey;
|
|
35
|
+
hintKey?: DiscordStatusHintKey | undefined;
|
|
36
|
+
actionable: boolean;
|
|
37
|
+
/** Whether the credential service already holds a token. */
|
|
38
|
+
tokenConfigured: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Locale keys the card renders for each connection condition. */
|
|
41
|
+
export type DiscordStatusConnectionKey = 'discordStatusConnected' | 'discordStatusConnecting' | 'discordStatusDisconnected' | 'discordStatusInvalidToken' | 'discordStatusIntentsBlocked' | 'discordStatusPermissionsBlocked';
|
|
42
|
+
/** Locale keys for each actionable hint the Host can send. */
|
|
43
|
+
export type DiscordStatusHintKey = 'discordHintConfigureToken' | 'discordHintTokenRejected' | 'discordHintEnableIntents' | 'discordHintGatewayClosed' | 'discordHintChannelPermissions';
|
|
44
|
+
/**
|
|
45
|
+
* Distill the Host's sanitized status view into the card's copy keys. The
|
|
46
|
+
* projection never carries secrets; this mapping adds none either — only
|
|
47
|
+
* stable locale keys and whether the condition demands user action.
|
|
48
|
+
*/
|
|
49
|
+
export declare function presentAdapterStatus(view: AdapterStatusView): AdapterStatusPresentation;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The settings surface's slot and locale contracts this card consumes. The
|
|
3
|
+
* slot entry types come from the settings domain base (`dsh-client-ui-settings`,
|
|
4
|
+
* the canonical home of every settings slot type); this module adds the
|
|
5
|
+
* locale namespace the Discord card's copy lives in. Importing it is what
|
|
6
|
+
* merges the declarations into this program.
|
|
7
|
+
*/
|
|
8
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
9
|
+
interface LocaleNamespaceMap {
|
|
10
|
+
'dsh-discord': keyof DiscordPluginsLocale;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Copy keys the Discord card renders; every key must have a dictionary entry. */
|
|
14
|
+
interface DiscordPluginsLocale {
|
|
15
|
+
discordTitle: string;
|
|
16
|
+
discordDescription: string;
|
|
17
|
+
discordAllowedGuildIds: string;
|
|
18
|
+
discordGuildIdHelp: string;
|
|
19
|
+
discordLanguageAuto: string;
|
|
20
|
+
discordTokenLabel: string;
|
|
21
|
+
discordTokenPlaceholder: string;
|
|
22
|
+
discordTokenConnect: string;
|
|
23
|
+
discordTokenConnecting: string;
|
|
24
|
+
discordTokenHelp: string;
|
|
25
|
+
discordTokenSaved: string;
|
|
26
|
+
discordTokenReconnectHint: string;
|
|
27
|
+
discordDisconnect: string;
|
|
28
|
+
discordThreadAutoArchive: string;
|
|
29
|
+
discordThreadAutoArchiveHint: string;
|
|
30
|
+
discordLanguage: string;
|
|
31
|
+
discordLanguageHint: string;
|
|
32
|
+
discordInvalidLanguage: string;
|
|
33
|
+
discordInvalidIds: string;
|
|
34
|
+
discordInvalidArchive: string;
|
|
35
|
+
discordStatusConnected: string;
|
|
36
|
+
discordStatusConnecting: string;
|
|
37
|
+
discordStatusDisconnected: string;
|
|
38
|
+
discordStatusInvalidToken: string;
|
|
39
|
+
discordStatusIntentsBlocked: string;
|
|
40
|
+
discordStatusPermissionsBlocked: string;
|
|
41
|
+
discordHintConfigureToken: string;
|
|
42
|
+
discordHintTokenRejected: string;
|
|
43
|
+
discordHintEnableIntents: string;
|
|
44
|
+
discordHintGatewayClosed: string;
|
|
45
|
+
discordHintChannelPermissions: string;
|
|
46
|
+
reset: string;
|
|
47
|
+
expand: string;
|
|
48
|
+
collapse: string;
|
|
49
|
+
unsaved: string;
|
|
50
|
+
readOnly: string;
|
|
51
|
+
saveFailed: string;
|
|
52
|
+
discard: string;
|
|
53
|
+
save: string;
|
|
54
|
+
saving: string;
|
|
55
|
+
overridden: string;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal snapshot store matching the client runtime's `SnapshotStore`
|
|
3
|
+
* contract. The runtime's `/client` artifact is a browser-only lazy-CJS
|
|
4
|
+
* factory, so the card implements the same interface locally and imports the
|
|
5
|
+
* contract type only.
|
|
6
|
+
*/
|
|
7
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
8
|
+
/** Create an immutable-snapshot store with synchronous notification. */
|
|
9
|
+
export declare function createLocalSnapshotStore<T>(initial: T): SnapshotStore<T>;
|