@addozhang/dsh-discord 0.5.0-rc.3 → 0.5.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/README.md +15 -2
- package/README.zh.md +15 -2
- package/lib/client/card-controller.d.ts +2 -2
- package/lib/client/card-form.d.ts +2 -2
- package/lib/client.js +5 -5
- package/lib/index.d.ts +46 -2
- package/lib/index.js +24 -7
- package/lib/settings.d.ts +40 -2
- package/lib/settings.js +36 -26
- package/lib/startup.js +9 -6
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -29,13 +29,26 @@ No extra process: the adapter is a DSH plugin that mounts straight onto your `ds
|
|
|
29
29
|
|
|
30
30
|
## Requirements
|
|
31
31
|
|
|
32
|
-
- The [dsh CLI](https://www.npmjs.com/package/@deepseek-ai/dsh)
|
|
32
|
+
- The [dsh CLI](https://www.npmjs.com/package/@deepseek-ai/dsh), running a web profile. Match the dsh and adapter channels — see [Versions](#versions).
|
|
33
33
|
- Node.js `^22.19.0 || >=24`
|
|
34
34
|
- A Discord application with a bot user and the **MESSAGE CONTENT** privileged intent enabled (Developer Portal → your application → Bot → Privileged Gateway Intents)
|
|
35
35
|
|
|
36
|
+
## Versions
|
|
37
|
+
|
|
38
|
+
Two channels ship in parallel, each pinned to the dsh host surface it was verified against. Pick the adapter and the dsh CLI from the same row:
|
|
39
|
+
|
|
40
|
+
| Channel | Adapter | dsh CLI | Install |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| **stable** | `0.5.0` — npm `latest` | `0.1.7-rc.1` — npm `next` | `dsh plugin --profile web add @addozhang/dsh-discord` |
|
|
43
|
+
| **next** | `>=0.5.0-rc.4` — npm `next` | `0.1.7-alpha` — npm `alpha` | `dsh plugin --profile web add @addozhang/dsh-discord@next` |
|
|
44
|
+
|
|
45
|
+
- **The rows do not mix.** Adapter `>=0.5.0-rc.4` (including `0.5.0`) speaks the 0.1.7 profile-backed settings forms and controller services and will not load on older hosts (the startup probe refuses with one actionable diagnostic); `0.5.0-rc.1`–`rc.3` target dsh `0.1.6-alpha`; adapter `0.4.x` talks to the former `apiProxy` surface, which `0.1.6-alpha` removed — it remains the pairing for the dsh `0.1.5` stable line, reachable by exact version (e.g. `@addozhang/dsh-discord@0.4.0`). A mismatched pair fails at startup.
|
|
46
|
+
- To switch the dsh CLI line: `npm install -g @deepseek-ai/dsh@next` (0.1.7-rc.1, pairs with adapter 0.5.0) or `npm install -g @deepseek-ai/dsh@alpha` (0.1.7-alpha line).
|
|
47
|
+
- Right after a `next` release, the `@next` spec can resolve to a stale cached version. If the installed copy looks old, install by exact version instead (e.g. `@addozhang/dsh-discord@0.5.0-rc.4`) and verify the `version` field inside the profile's `node_modules`.
|
|
48
|
+
|
|
36
49
|
## Install
|
|
37
50
|
|
|
38
|
-
Install with the dsh CLI — it installs the package into the profile and registers the bundle for you:
|
|
51
|
+
Install with the dsh CLI — it installs the package into the profile and registers the bundle for you. The stable channel is shown below; for the next channel use the `@next` command from the [Versions](#versions) table (`up` / `rm` take the same spec):
|
|
39
52
|
|
|
40
53
|
```sh
|
|
41
54
|
dsh plugin --profile web add @addozhang/dsh-discord
|
package/README.zh.md
CHANGED
|
@@ -29,13 +29,26 @@
|
|
|
29
29
|
|
|
30
30
|
## 环境要求
|
|
31
31
|
|
|
32
|
-
- [dsh CLI](https://www.npmjs.com/package/@deepseek-ai/dsh)
|
|
32
|
+
- [dsh CLI](https://www.npmjs.com/package/@deepseek-ai/dsh)(web profile)。dsh 与适配器的通道需匹配——见[版本支持](#版本支持)。
|
|
33
33
|
- Node.js `^22.19.0 || >=24`
|
|
34
34
|
- 一个 Discord 应用(含 Bot 用户),并在开发者门户启用 **MESSAGE CONTENT** 特权 intent(Developer Portal → 你的应用 → Bot → Privileged Gateway Intents)
|
|
35
35
|
|
|
36
|
+
## 版本支持
|
|
37
|
+
|
|
38
|
+
两条通道并行发布,各自钉在经过真机验证的 dsh 宿主面上。适配器与 dsh CLI 请取同一行:
|
|
39
|
+
|
|
40
|
+
| 通道 | 适配器 | dsh CLI | 安装命令 |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| **stable** | `0.5.0` — npm `latest` | `0.1.7-rc.1` — npm `next` | `dsh plugin --profile web add @addozhang/dsh-discord` |
|
|
43
|
+
| **next** | `>=0.5.0-rc.4` — npm `next` | `0.1.7-alpha` — npm `alpha` | `dsh plugin --profile web add @addozhang/dsh-discord@next` |
|
|
44
|
+
|
|
45
|
+
- **两行不可混搭。** 适配器 `>=0.5.0-rc.4`(含 `0.5.0`)使用 0.1.7 的 profile-backed settings forms 与控制器服务,在更旧的宿主上无法加载(startup 探针会给出一条可操作的诊断后拒绝激活);`0.5.0-rc.1`–`rc.3` 对应 dsh `0.1.6-alpha`;适配器 `0.4.x` 使用旧版 `apiProxy` 宿主面(该面已在 `0.1.6-alpha` 中移除)——它仍是 dsh `0.1.5` stable 线的配对,可用精确版本号安装(如 `@addozhang/dsh-discord@0.4.0`)。错配会在启动时失败。
|
|
46
|
+
- 切换 dsh CLI 通道:`npm install -g @deepseek-ai/dsh@next`(0.1.7-rc.1,配适配器 0.5.0)或 `npm install -g @deepseek-ai/dsh@alpha`(0.1.7-alpha 线)。
|
|
47
|
+
- `next` 刚发版后,`@next` 规格可能解析到过期的缓存版本。若装到的版本偏旧,请改用精确版本号安装(如 `@addozhang/dsh-discord@0.5.0-rc.4`),并核对 profile `node_modules` 中的 `version` 字段。
|
|
48
|
+
|
|
36
49
|
## 安装
|
|
37
50
|
|
|
38
|
-
使用 dsh CLI 安装——它会自动把包装进 profile 并注册 bundle
|
|
51
|
+
使用 dsh CLI 安装——它会自动把包装进 profile 并注册 bundle。下面是 stable 通道;next 通道使用[版本支持](#版本支持)表中的 `@next` 命令(`up` / `rm` 同规格):
|
|
39
52
|
|
|
40
53
|
```sh
|
|
41
54
|
dsh plugin --profile web add @addozhang/dsh-discord
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* hands to the component: the edit actions plus the `discordCard` snapshot
|
|
5
5
|
* store the framework binds as the `useDiscordCard` selector hook.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
8
8
|
import type { AdapterStatusView } from '../features/adapter-status.js';
|
|
9
9
|
import type { DiscordSettings } from '../settings.js';
|
|
10
10
|
import { type CardManagement, type DiscordCardFace, type DiscordCardState } from './card-form.js';
|
|
11
11
|
export declare class DiscordCardController {
|
|
12
12
|
private readonly form;
|
|
13
13
|
private management;
|
|
14
|
-
constructor(
|
|
14
|
+
constructor(form: ConfigForm<DiscordSettings>);
|
|
15
15
|
/** Attach the token write/connect path once the RPC face is known. */
|
|
16
16
|
setManagement(management: CardManagement): void;
|
|
17
17
|
/** Publish the Host's sanitized connection status onto the card. */
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* is not a list of Discord IDs, which blocks the save rather than dropping it.
|
|
8
8
|
*/
|
|
9
9
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
10
|
-
import type {
|
|
10
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
11
11
|
import type { AdapterStatusView } from '../features/adapter-status.js';
|
|
12
12
|
import type { DiscordSettings } from '../settings.js';
|
|
13
13
|
import { type AdapterStatusPresentation } from './settings-model.js';
|
|
@@ -88,7 +88,7 @@ export declare class DiscordCardForm {
|
|
|
88
88
|
private saving;
|
|
89
89
|
private failed;
|
|
90
90
|
private status;
|
|
91
|
-
constructor(scope:
|
|
91
|
+
constructor(scope: ConfigForm<DiscordSettings>);
|
|
92
92
|
/**
|
|
93
93
|
* Publish the Host's sanitized status view. Edits, saves, and failures
|
|
94
94
|
* never touch it — connection state is the Host's report, not form state.
|
package/lib/client.js
CHANGED
|
@@ -1022,8 +1022,8 @@ var DiscordCardForm = class {
|
|
|
1022
1022
|
var DiscordCardController = class {
|
|
1023
1023
|
form;
|
|
1024
1024
|
management;
|
|
1025
|
-
constructor(
|
|
1026
|
-
this.form = new DiscordCardForm(
|
|
1025
|
+
constructor(form) {
|
|
1026
|
+
this.form = new DiscordCardForm(form);
|
|
1027
1027
|
}
|
|
1028
1028
|
/** Attach the token write/connect path once the RPC face is known. */
|
|
1029
1029
|
setManagement(management) {
|
|
@@ -1469,7 +1469,7 @@ function DiscordSettingsCard(props) {
|
|
|
1469
1469
|
|
|
1470
1470
|
// src/client/index.ts
|
|
1471
1471
|
var name = "dsh-discord-client";
|
|
1472
|
-
var inject = ["
|
|
1472
|
+
var inject = ["configForms", "slots", "connection", "locale"];
|
|
1473
1473
|
var STATUS_POLL_MS = 3e4;
|
|
1474
1474
|
function isStatusView(value) {
|
|
1475
1475
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -1477,8 +1477,8 @@ function isStatusView(value) {
|
|
|
1477
1477
|
return typeof candidate["token"] === "string" && typeof candidate["connection"] === "string";
|
|
1478
1478
|
}
|
|
1479
1479
|
function apply(ctx) {
|
|
1480
|
-
const
|
|
1481
|
-
const controller = new DiscordCardController(
|
|
1480
|
+
const form = ctx.configForms.get(DISCORD_SETTINGS_NAMESPACE);
|
|
1481
|
+
const controller = new DiscordCardController(form);
|
|
1482
1482
|
ctx.locale.register("dsh-discord", {
|
|
1483
1483
|
en: DISCORD_CARD_LOCALE_EN,
|
|
1484
1484
|
zh: DISCORD_CARD_LOCALE_ZH
|
package/lib/index.d.ts
CHANGED
|
@@ -1,12 +1,56 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
-
import type z from '@deepseek-ai/schemastery';
|
|
3
2
|
import { type DiscordSettings } from './settings.js';
|
|
4
3
|
/** Stable Cordis plugin name for diagnostics. */
|
|
5
4
|
export declare const name = "dsh-discord";
|
|
6
5
|
/** Host services required by the complete embedded adapter. */
|
|
7
6
|
export declare const inject: string[];
|
|
8
7
|
export type Config = DiscordSettings;
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The plugin's static Config (0.1.7 profile-backed forms): every field is
|
|
10
|
+
* volatile, so the Host delivers each as a stable live reference instead of
|
|
11
|
+
* remounting the fiber on writes. The annotation is deliberately absent —
|
|
12
|
+
* schemastery's volatile mode types field defaults as `Volatile<T>`, which a
|
|
13
|
+
* `z<DiscordSettings>` annotation cannot express.
|
|
14
|
+
*/
|
|
15
|
+
export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
16
|
+
enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
17
|
+
allowedGuildIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
18
|
+
memberUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
19
|
+
memberRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
20
|
+
administratorUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
21
|
+
administratorRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
22
|
+
deniedUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
23
|
+
deniedRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
24
|
+
hostOperatorUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
25
|
+
defaultVerbosity: import("@deepseek-ai/schemastery").default<"text-only" | "essential-tools" | "full-tools", "text-only" | "essential-tools" | "full-tools", "volatile-defined">;
|
|
26
|
+
language: import("@deepseek-ai/schemastery").default<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
27
|
+
threadAutoArchiveMinutes: import("@deepseek-ai/schemastery").default<60 | 1440 | 4320 | 10080, 60 | 1440 | 4320 | 10080, "volatile-defined">;
|
|
28
|
+
streamUpdateIntervalMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
29
|
+
typingIntervalMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
30
|
+
approvalTimeoutMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
31
|
+
questionTimeoutMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
32
|
+
modelSelectOperatorOnly: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
33
|
+
permissionSelectOperatorOnly: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
34
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
35
|
+
enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
36
|
+
allowedGuildIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
37
|
+
memberUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
38
|
+
memberRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
39
|
+
administratorUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
40
|
+
administratorRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
41
|
+
deniedUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
42
|
+
deniedRoleIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
43
|
+
hostOperatorUserIds: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
44
|
+
defaultVerbosity: import("@deepseek-ai/schemastery").default<"text-only" | "essential-tools" | "full-tools", "text-only" | "essential-tools" | "full-tools", "volatile-defined">;
|
|
45
|
+
language: import("@deepseek-ai/schemastery").default<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
46
|
+
threadAutoArchiveMinutes: import("@deepseek-ai/schemastery").default<60 | 1440 | 4320 | 10080, 60 | 1440 | 4320 | 10080, "volatile-defined">;
|
|
47
|
+
streamUpdateIntervalMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
48
|
+
typingIntervalMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
49
|
+
approvalTimeoutMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
50
|
+
questionTimeoutMs: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
51
|
+
modelSelectOperatorOnly: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
52
|
+
permissionSelectOperatorOnly: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
53
|
+
}>>, "plain">;
|
|
10
54
|
/**
|
|
11
55
|
* Mount the embedded Discord adapter. Composition of the runtime path
|
|
12
56
|
* (Gateway → ingress → command/mention dispatch → Discord REST, plus the
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_DISCORD_SETTINGS, DiscordSettingsSchema,
|
|
1
|
+
import { DEFAULT_DISCORD_SETTINGS, DiscordSettingsSchema, bindDiscordSettings, } from './settings.js';
|
|
2
2
|
import { installCancellationRoot } from './lifecycle.js';
|
|
3
3
|
import { ALLOWED_MENTIONS_NONE, DISCORD_SUPPRESS_NOTIFICATIONS_FLAG, OUTBOUND_EPHEMERAL_FLAGS, safeTitle } from './policy/disclosure.js';
|
|
4
4
|
import { validateHostCapabilities } from './startup.js';
|
|
@@ -39,6 +39,13 @@ import { planWorkspaceChannel, workspaceChannelName } from './features/workspace
|
|
|
39
39
|
export const name = 'dsh-discord';
|
|
40
40
|
/** Host services required by the complete embedded adapter. */
|
|
41
41
|
export const inject = ['sessionController', 'workspaceController', 'sessionQuery', 'webServer', 'credentials', 'settings', 'storageDomain', 'connection'];
|
|
42
|
+
/**
|
|
43
|
+
* The plugin's static Config (0.1.7 profile-backed forms): every field is
|
|
44
|
+
* volatile, so the Host delivers each as a stable live reference instead of
|
|
45
|
+
* remounting the fiber on writes. The annotation is deliberately absent —
|
|
46
|
+
* schemastery's volatile mode types field defaults as `Volatile<T>`, which a
|
|
47
|
+
* `z<DiscordSettings>` annotation cannot express.
|
|
48
|
+
*/
|
|
42
49
|
export const Config = DiscordSettingsSchema;
|
|
43
50
|
/**
|
|
44
51
|
* Fixed Milestone 1 Gateway intents: guilds, messages, content. Member data
|
|
@@ -66,18 +73,27 @@ function emitLog(ctx, level, message) {
|
|
|
66
73
|
export function apply(ctx, config = DEFAULT_DISCORD_SETTINGS) {
|
|
67
74
|
validateHostCapabilities(name => ctx.get(name));
|
|
68
75
|
installCancellationRoot(ctx);
|
|
69
|
-
let current = normalizeDiscordSettings(config);
|
|
70
76
|
/**
|
|
71
77
|
* Discord-visible copy, re-resolved on every access so a language change
|
|
72
78
|
* on the settings card applies without rebuilding the composition. The
|
|
73
|
-
* 'auto' preference follows the DSH locale
|
|
74
|
-
*
|
|
79
|
+
* 'auto' preference follows the DSH locale — read from the settings forms
|
|
80
|
+
* describe projection (the 0.1.7 replacement for the retired
|
|
81
|
+
* `.get(namespace)` service method); non-Chinese locales fall back to
|
|
82
|
+
* English.
|
|
75
83
|
*/
|
|
84
|
+
const readLocalePreference = (service) => {
|
|
85
|
+
const describe = service?.describe;
|
|
86
|
+
if (typeof describe !== 'function')
|
|
87
|
+
return undefined;
|
|
88
|
+
const row = describe.call(service, { redactSecrets: true }).find(entry => entry.ns === 'locale');
|
|
89
|
+
const preference = row?.value?.preference;
|
|
90
|
+
return typeof preference === 'string' ? preference : undefined;
|
|
91
|
+
};
|
|
76
92
|
const resolveLanguage = () => {
|
|
77
93
|
if (current.language !== 'auto')
|
|
78
94
|
return current.language;
|
|
79
|
-
const
|
|
80
|
-
return
|
|
95
|
+
const preference = readLocalePreference(ctx.get('settings'));
|
|
96
|
+
return preference !== undefined && preference.startsWith('zh') ? 'zh' : 'en';
|
|
81
97
|
};
|
|
82
98
|
const copy = new Proxy({}, {
|
|
83
99
|
get: (_target, key) => createCopy(resolveLanguage())[key],
|
|
@@ -85,7 +101,7 @@ export function apply(ctx, config = DEFAULT_DISCORD_SETTINGS) {
|
|
|
85
101
|
// Reassigned once the async composition has built the real registrar.
|
|
86
102
|
let registerCommands = async () => { };
|
|
87
103
|
const onGuildsChanged = () => { void registerCommands(); };
|
|
88
|
-
|
|
104
|
+
const settingsSource = bindDiscordSettings(ctx, config, (next) => {
|
|
89
105
|
const guildsChanged = next.allowedGuildIds.join(',') !== current.allowedGuildIds.join(',');
|
|
90
106
|
current = next;
|
|
91
107
|
if (guildsChanged)
|
|
@@ -96,6 +112,7 @@ export function apply(ctx, config = DEFAULT_DISCORD_SETTINGS) {
|
|
|
96
112
|
allowedGuildCount: current.allowedGuildIds.length,
|
|
97
113
|
});
|
|
98
114
|
});
|
|
115
|
+
let current = settingsSource.get();
|
|
99
116
|
// The settings card's status surface: credential presence seeded now, the
|
|
100
117
|
// Gateway observation fed by the adapter composition as it starts.
|
|
101
118
|
// Services resolve through the same accessor the startup boundary probes.
|
package/lib/settings.d.ts
CHANGED
|
@@ -43,10 +43,48 @@ export interface DiscordSettings {
|
|
|
43
43
|
export type ThreadAutoArchiveMinutes = 60 | 1440 | 4320 | 10080;
|
|
44
44
|
export declare const THREAD_AUTO_ARCHIVE_OPTIONS: readonly ThreadAutoArchiveMinutes[];
|
|
45
45
|
export declare const DEFAULT_DISCORD_SETTINGS: DiscordSettings;
|
|
46
|
-
export declare const DiscordSettingsSchema: z<
|
|
46
|
+
export declare const DiscordSettingsSchema: z<Schemastery.ObjectS<NoInfer<{
|
|
47
|
+
enabled: z<boolean, boolean, "volatile-defined">;
|
|
48
|
+
allowedGuildIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
49
|
+
memberUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
50
|
+
memberRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
51
|
+
administratorUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
52
|
+
administratorRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
53
|
+
deniedUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
54
|
+
deniedRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
55
|
+
hostOperatorUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
56
|
+
defaultVerbosity: z<"text-only" | "essential-tools" | "full-tools", "text-only" | "essential-tools" | "full-tools", "volatile-defined">;
|
|
57
|
+
language: z<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
58
|
+
threadAutoArchiveMinutes: z<60 | 1440 | 4320 | 10080, 60 | 1440 | 4320 | 10080, "volatile-defined">;
|
|
59
|
+
streamUpdateIntervalMs: z<number, number, "volatile-defined">;
|
|
60
|
+
typingIntervalMs: z<number, number, "volatile-defined">;
|
|
61
|
+
approvalTimeoutMs: z<number, number, "volatile-defined">;
|
|
62
|
+
questionTimeoutMs: z<number, number, "volatile-defined">;
|
|
63
|
+
modelSelectOperatorOnly: z<boolean, boolean, "volatile-defined">;
|
|
64
|
+
permissionSelectOperatorOnly: z<boolean, boolean, "volatile-defined">;
|
|
65
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
66
|
+
enabled: z<boolean, boolean, "volatile-defined">;
|
|
67
|
+
allowedGuildIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
68
|
+
memberUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
69
|
+
memberRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
70
|
+
administratorUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
71
|
+
administratorRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
72
|
+
deniedUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
73
|
+
deniedRoleIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
74
|
+
hostOperatorUserIds: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
75
|
+
defaultVerbosity: z<"text-only" | "essential-tools" | "full-tools", "text-only" | "essential-tools" | "full-tools", "volatile-defined">;
|
|
76
|
+
language: z<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
77
|
+
threadAutoArchiveMinutes: z<60 | 1440 | 4320 | 10080, 60 | 1440 | 4320 | 10080, "volatile-defined">;
|
|
78
|
+
streamUpdateIntervalMs: z<number, number, "volatile-defined">;
|
|
79
|
+
typingIntervalMs: z<number, number, "volatile-defined">;
|
|
80
|
+
approvalTimeoutMs: z<number, number, "volatile-defined">;
|
|
81
|
+
questionTimeoutMs: z<number, number, "volatile-defined">;
|
|
82
|
+
modelSelectOperatorOnly: z<boolean, boolean, "volatile-defined">;
|
|
83
|
+
permissionSelectOperatorOnly: z<boolean, boolean, "volatile-defined">;
|
|
84
|
+
}>>, "plain">;
|
|
47
85
|
export declare function normalizeDiscordSettings(input: DiscordSettings): DiscordSettings;
|
|
48
86
|
export declare function validateDiscordSettings(input: DiscordSettings): void;
|
|
49
87
|
export interface DiscordSettingsSource {
|
|
50
88
|
get(): DiscordSettings;
|
|
51
89
|
}
|
|
52
|
-
export declare function
|
|
90
|
+
export declare function bindDiscordSettings(ctx: Context, config: Readonly<Record<string, unknown>>, onChange: (value: DiscordSettings) => void): DiscordSettingsSource;
|
package/lib/settings.js
CHANGED
|
@@ -22,9 +22,17 @@ export const DEFAULT_DISCORD_SETTINGS = Object.freeze({
|
|
|
22
22
|
modelSelectOperatorOnly: false,
|
|
23
23
|
permissionSelectOperatorOnly: true,
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Every field is volatile (0.1.7 profile-backed forms): values live-update
|
|
27
|
+
* through stable per-field references without remounting the plugin fiber —
|
|
28
|
+
* the exact semantics the 0.1.6 `installSection` registration provided.
|
|
29
|
+
* Snowflake patterns ride the schema so the Host rejects malformed IDs at
|
|
30
|
+
* write time (parity with the retired validate hook).
|
|
31
|
+
*/
|
|
32
|
+
const DISCORD_SNOWFLAKE = /^\d{17,20}$/u;
|
|
33
|
+
const discordIdList = z.array(z.string().pattern(DISCORD_SNOWFLAKE)).default([]).volatile();
|
|
26
34
|
export const DiscordSettingsSchema = z.object({
|
|
27
|
-
enabled: z.boolean().default(false),
|
|
35
|
+
enabled: z.boolean().default(false).volatile(),
|
|
28
36
|
allowedGuildIds: discordIdList,
|
|
29
37
|
memberUserIds: discordIdList,
|
|
30
38
|
memberRoleIds: discordIdList,
|
|
@@ -34,18 +42,17 @@ export const DiscordSettingsSchema = z.object({
|
|
|
34
42
|
deniedRoleIds: discordIdList,
|
|
35
43
|
hostOperatorUserIds: discordIdList,
|
|
36
44
|
defaultVerbosity: z.union(['text-only', 'essential-tools', 'full-tools'])
|
|
37
|
-
.default('essential-tools'),
|
|
38
|
-
language: z.union(['auto', 'zh', 'en']).default('auto'),
|
|
45
|
+
.default('essential-tools').volatile(),
|
|
46
|
+
language: z.union(['auto', 'zh', 'en']).default('auto').volatile(),
|
|
39
47
|
threadAutoArchiveMinutes: z.union([60, 1440, 4320, 10080])
|
|
40
|
-
.default(1440),
|
|
41
|
-
streamUpdateIntervalMs: z.number().step(1).min(250).max(10_000).default(800),
|
|
42
|
-
typingIntervalMs: z.number().step(1).min(1_000).max(30_000).default(7_000),
|
|
43
|
-
approvalTimeoutMs: z.number().step(1).min(30_000).max(86_400_000).default(600_000),
|
|
44
|
-
questionTimeoutMs: z.number().step(1).min(30_000).max(86_400_000).default(1_800_000),
|
|
45
|
-
modelSelectOperatorOnly: z.boolean().default(false),
|
|
46
|
-
permissionSelectOperatorOnly: z.boolean().default(true),
|
|
48
|
+
.default(1440).volatile(),
|
|
49
|
+
streamUpdateIntervalMs: z.number().step(1).min(250).max(10_000).default(800).volatile(),
|
|
50
|
+
typingIntervalMs: z.number().step(1).min(1_000).max(30_000).default(7_000).volatile(),
|
|
51
|
+
approvalTimeoutMs: z.number().step(1).min(30_000).max(86_400_000).default(600_000).volatile(),
|
|
52
|
+
questionTimeoutMs: z.number().step(1).min(30_000).max(86_400_000).default(1_800_000).volatile(),
|
|
53
|
+
modelSelectOperatorOnly: z.boolean().default(false).volatile(),
|
|
54
|
+
permissionSelectOperatorOnly: z.boolean().default(true).volatile(),
|
|
47
55
|
});
|
|
48
|
-
const DISCORD_SNOWFLAKE = /^\d{17,20}$/u;
|
|
49
56
|
const ID_FIELDS = [
|
|
50
57
|
'allowedGuildIds',
|
|
51
58
|
'memberUserIds',
|
|
@@ -91,19 +98,22 @@ export function validateDiscordSettings(input) {
|
|
|
91
98
|
throw new TypeError('threadAutoArchiveMinutes must be one of 60, 1440, 4320, 10080');
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
function isVolatileField(value) {
|
|
102
|
+
return typeof value === 'object' && value !== null
|
|
103
|
+
&& typeof value.get === 'function';
|
|
104
|
+
}
|
|
105
|
+
export function bindDiscordSettings(ctx, config, onChange) {
|
|
106
|
+
const read = () => {
|
|
107
|
+
const raw = {};
|
|
108
|
+
for (const key of Object.keys(DEFAULT_DISCORD_SETTINGS)) {
|
|
109
|
+
const field = config[key];
|
|
110
|
+
raw[key] = isVolatileField(field) ? field.get() : field;
|
|
111
|
+
}
|
|
112
|
+
return normalizeDiscordSettings(raw);
|
|
113
|
+
};
|
|
114
|
+
ctx.on('settings/document-updated', (ns) => {
|
|
115
|
+
if (ns === DISCORD_SETTINGS_NAMESPACE)
|
|
116
|
+
onChange(read());
|
|
108
117
|
});
|
|
118
|
+
return { get: read };
|
|
109
119
|
}
|
package/lib/startup.js
CHANGED
|
@@ -21,10 +21,13 @@ export const REQUIRED_HOST_SERVICES = [
|
|
|
21
21
|
'permissionPresets',
|
|
22
22
|
];
|
|
23
23
|
/**
|
|
24
|
-
* Minimum contract members per service, confirmed against the 0.1.
|
|
25
|
-
* controller faces (dsh-api-session-controller /
|
|
26
|
-
* / dsh-session-query
|
|
27
|
-
*
|
|
24
|
+
* Minimum contract members per service, confirmed against the 0.1.7
|
|
25
|
+
* controller and settings faces (dsh-api-session-controller /
|
|
26
|
+
* dsh-api-workspace-controller / dsh-session-query / dsh-settings
|
|
27
|
+
* SettingsForms — describe reads the forms projection including the locale
|
|
28
|
+
* entry; update is the service-side landing of every card write). A service
|
|
29
|
+
* lacking any member means a version drift the startup probe should have
|
|
30
|
+
* refused; refuse rather than probe at runtime.
|
|
28
31
|
*/
|
|
29
32
|
const REQUIRED_CONTRACT_MEMBERS = {
|
|
30
33
|
sessionController: ['prompt', 'create', 'list', 'cancel', 'updateQueue', 'selectModel', 'modelCatalog', 'follow', 'control', 'resolveAgent'],
|
|
@@ -32,14 +35,14 @@ const REQUIRED_CONTRACT_MEMBERS = {
|
|
|
32
35
|
sessionQuery: ['observeSession'],
|
|
33
36
|
webServer: [],
|
|
34
37
|
credentials: ['resolve', 'describe', 'set', 'unset'],
|
|
35
|
-
settings: ['
|
|
38
|
+
settings: ['describe', 'update'],
|
|
36
39
|
storageDomain: ['open'],
|
|
37
40
|
connection: ['rpc'],
|
|
38
41
|
commands: ['execute'],
|
|
39
42
|
permissionPresets: ['catalog'],
|
|
40
43
|
};
|
|
41
44
|
/** Actionable remediation hint appended to every activation failure. */
|
|
42
|
-
const REMEDIATION = 'install @addozhang/dsh-discord into a dsh web profile providing the 0.1.
|
|
45
|
+
const REMEDIATION = 'install @addozhang/dsh-discord into a dsh web profile providing the 0.1.7 host services (profile-backed settings forms)';
|
|
43
46
|
function missingMembers(service, required) {
|
|
44
47
|
if (service === undefined || service === null)
|
|
45
48
|
return [...required];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@addozhang/dsh-discord",
|
|
3
|
-
"version": "0.5.0
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Discord adapter for DeepSeek Harness · DeepSeek Harness 的 Discord 适配器",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,14 +53,15 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
56
|
+
"@deepseek-ai/cordis": "^4.0.3"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@deepseek-ai/cordis": "4.0.
|
|
60
|
-
"@deepseek-ai/dsh-client-store": "0.1.
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.
|
|
59
|
+
"@deepseek-ai/cordis": "4.0.3",
|
|
60
|
+
"@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
64
65
|
"@eslint/js": "9.39.1",
|
|
65
66
|
"@types/node": "24.10.1",
|
|
66
67
|
"@types/react": "18.3.1",
|
|
@@ -75,10 +76,9 @@
|
|
|
75
76
|
"vitest": "4.1.11"
|
|
76
77
|
},
|
|
77
78
|
"dependencies": {
|
|
78
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
79
|
-
"@deepseek-ai/dsh-
|
|
80
|
-
"@deepseek-ai/
|
|
81
|
-
"@deepseek-ai/schemastery": "3.18.1",
|
|
79
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.7-rc.1",
|
|
81
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
82
82
|
"zod": "^4.4.3"
|
|
83
83
|
},
|
|
84
84
|
"pnpm": {},
|