dsh-connect 0.7.2 → 0.9.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.i18n.yaml +2 -2
- package/README.md +196 -43
- package/README.zh.md +171 -42
- package/client/client.js +817 -0
- package/client/client.js.map +7 -0
- package/client/locale.mjs +227 -0
- package/client/panel-state.mjs +54 -0
- package/client/settings-client.mjs +389 -0
- package/docs/images/settings-advanced-en.png +0 -0
- package/docs/images/settings-advanced-zh.png +0 -0
- package/docs/images/settings-defaults-en.png +0 -0
- package/docs/images/settings-defaults-zh.png +0 -0
- package/docs/images/settings-overview-en.png +0 -0
- package/docs/images/settings-overview-zh.png +0 -0
- package/examples/minimal.config.json +16 -0
- package/lib/binding.d.ts.map +1 -1
- package/lib/binding.js +2 -2
- package/lib/binding.js.map +1 -1
- package/lib/channels/dingtalk/adapter.d.ts +59 -0
- package/lib/channels/dingtalk/adapter.d.ts.map +1 -0
- package/lib/channels/dingtalk/adapter.js +123 -0
- package/lib/channels/dingtalk/adapter.js.map +1 -0
- package/lib/channels/dingtalk/i18n.d.ts +14 -0
- package/lib/channels/dingtalk/i18n.d.ts.map +1 -0
- package/lib/channels/dingtalk/i18n.js +16 -0
- package/lib/channels/dingtalk/i18n.js.map +1 -0
- package/lib/channels/dingtalk/index.d.ts +127 -0
- package/lib/channels/dingtalk/index.d.ts.map +1 -0
- package/lib/channels/dingtalk/index.js +144 -0
- package/lib/channels/dingtalk/index.js.map +1 -0
- package/lib/channels/dingtalk/message.d.ts +49 -0
- package/lib/channels/dingtalk/message.d.ts.map +1 -0
- package/lib/channels/dingtalk/message.js +50 -0
- package/lib/channels/dingtalk/message.js.map +1 -0
- package/lib/channels/dingtalk/stomp.d.ts +36 -0
- package/lib/channels/dingtalk/stomp.d.ts.map +1 -0
- package/lib/channels/dingtalk/stomp.js +107 -0
- package/lib/channels/dingtalk/stomp.js.map +1 -0
- package/lib/channels/dingtalk/stream.d.ts +53 -0
- package/lib/channels/dingtalk/stream.d.ts.map +1 -0
- package/lib/channels/dingtalk/stream.js +199 -0
- package/lib/channels/dingtalk/stream.js.map +1 -0
- package/lib/channels/dingtalk/webhook.d.ts +84 -0
- package/lib/channels/dingtalk/webhook.d.ts.map +1 -0
- package/lib/channels/dingtalk/webhook.js +143 -0
- package/lib/channels/dingtalk/webhook.js.map +1 -0
- package/lib/channels/feishu/adapter.d.ts +133 -0
- package/lib/channels/feishu/adapter.d.ts.map +1 -0
- package/lib/channels/feishu/adapter.js +662 -0
- package/lib/channels/feishu/adapter.js.map +1 -0
- package/lib/channels/feishu/i18n.d.ts +30 -0
- package/lib/channels/feishu/i18n.d.ts.map +1 -0
- package/lib/channels/feishu/i18n.js +42 -0
- package/lib/channels/feishu/i18n.js.map +1 -0
- package/lib/channels/feishu/index.d.ts +82 -0
- package/lib/channels/feishu/index.d.ts.map +1 -0
- package/lib/channels/feishu/index.js +103 -0
- package/lib/channels/feishu/index.js.map +1 -0
- package/lib/channels/feishu/onboard.d.ts +16 -0
- package/lib/channels/feishu/onboard.d.ts.map +1 -0
- package/lib/channels/feishu/onboard.js +86 -0
- package/lib/channels/feishu/onboard.js.map +1 -0
- package/lib/channels/telegram/adapter.d.ts +70 -0
- package/lib/channels/telegram/adapter.d.ts.map +1 -0
- package/lib/channels/telegram/adapter.js +460 -0
- package/lib/channels/telegram/adapter.js.map +1 -0
- package/lib/channels/telegram/client.d.ts +115 -0
- package/lib/channels/telegram/client.d.ts.map +1 -0
- package/lib/channels/telegram/client.js +198 -0
- package/lib/channels/telegram/client.js.map +1 -0
- package/lib/channels/telegram/i18n.d.ts +14 -0
- package/lib/channels/telegram/i18n.d.ts.map +1 -0
- package/lib/channels/telegram/i18n.js +18 -0
- package/lib/channels/telegram/i18n.js.map +1 -0
- package/lib/channels/telegram/index.d.ts +46 -0
- package/lib/channels/telegram/index.d.ts.map +1 -0
- package/lib/channels/telegram/index.js +54 -0
- package/lib/channels/telegram/index.js.map +1 -0
- package/lib/channels/web/adapter.d.ts +103 -0
- package/lib/channels/web/adapter.d.ts.map +1 -0
- package/lib/channels/web/adapter.js +161 -0
- package/lib/channels/web/adapter.js.map +1 -0
- package/lib/channels/web/index.d.ts +47 -0
- package/lib/channels/web/index.d.ts.map +1 -0
- package/lib/channels/web/index.js +59 -0
- package/lib/channels/web/index.js.map +1 -0
- package/lib/chat-key.d.ts +29 -0
- package/lib/chat-key.d.ts.map +1 -0
- package/lib/chat-key.js +38 -0
- package/lib/chat-key.js.map +1 -0
- package/lib/index.d.ts +78 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +312 -9
- package/lib/index.js.map +1 -1
- package/lib/runner.d.ts +55 -2
- package/lib/runner.d.ts.map +1 -1
- package/lib/runner.js +158 -23
- package/lib/runner.js.map +1 -1
- package/lib/scheduler.d.ts.map +1 -1
- package/lib/scheduler.js +2 -2
- package/lib/scheduler.js.map +1 -1
- package/lib/service.d.ts +17 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +47 -3
- package/lib/service.js.map +1 -1
- package/lib/settings/channel-runtime.d.ts +75 -0
- package/lib/settings/channel-runtime.d.ts.map +1 -0
- package/lib/settings/channel-runtime.js +165 -0
- package/lib/settings/channel-runtime.js.map +1 -0
- package/lib/settings/channels.d.ts +62 -0
- package/lib/settings/channels.d.ts.map +1 -0
- package/lib/settings/channels.js +132 -0
- package/lib/settings/channels.js.map +1 -0
- package/lib/settings/credential-store.d.ts +85 -0
- package/lib/settings/credential-store.d.ts.map +1 -0
- package/lib/settings/credential-store.js +142 -0
- package/lib/settings/credential-store.js.map +1 -0
- package/lib/settings/index.d.ts +14 -0
- package/lib/settings/index.d.ts.map +1 -0
- package/lib/settings/index.js +14 -0
- package/lib/settings/index.js.map +1 -0
- package/lib/settings/namespace.d.ts +175 -0
- package/lib/settings/namespace.d.ts.map +1 -0
- package/lib/settings/namespace.js +200 -0
- package/lib/settings/namespace.js.map +1 -0
- package/lib/settings/rpc-client.d.ts +36 -0
- package/lib/settings/rpc-client.d.ts.map +1 -0
- package/lib/settings/rpc-client.js +45 -0
- package/lib/settings/rpc-client.js.map +1 -0
- package/lib/settings/secret-disclosure.d.ts +50 -0
- package/lib/settings/secret-disclosure.d.ts.map +1 -0
- package/lib/settings/secret-disclosure.js +132 -0
- package/lib/settings/secret-disclosure.js.map +1 -0
- package/lib/settings/settings-model.d.ts +124 -0
- package/lib/settings/settings-model.d.ts.map +1 -0
- package/lib/settings/settings-model.js +118 -0
- package/lib/settings/settings-model.js.map +1 -0
- package/lib/settings/settings-rpc.d.ts +130 -0
- package/lib/settings/settings-rpc.d.ts.map +1 -0
- package/lib/settings/settings-rpc.js +233 -0
- package/lib/settings/settings-rpc.js.map +1 -0
- package/lib/settings/settings-service.d.ts +35 -0
- package/lib/settings/settings-service.d.ts.map +1 -0
- package/lib/settings/settings-service.js +171 -0
- package/lib/settings/settings-service.js.map +1 -0
- package/lib/state-dir.d.ts +28 -0
- package/lib/state-dir.d.ts.map +1 -0
- package/lib/state-dir.js +33 -0
- package/lib/state-dir.js.map +1 -0
- package/lib/stream.d.ts.map +1 -1
- package/lib/stream.js +3 -2
- package/lib/stream.js.map +1 -1
- package/package.json +56 -12
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# last confirmed-consistent state. Both languages carry equal authority; after
|
|
3
3
|
# editing either side, bring the other along and re-record with:
|
|
4
4
|
# git hash-object README.md README.zh.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: ae5b3e1207735af0f2bb272229111153a4b048a8
|
|
6
|
+
README.zh.md: bcced73f7d395930285480d770cc4618d90af1c6
|
package/README.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
The **
|
|
5
|
+
The **all-in-one plugin** for connecting [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (**DSH**) agents to chat platforms (Feishu / Lark, Telegram, DingTalk, and the Web mirror; more to come): session binding, agent driving, streaming reply bridging, interactive menu cards, local commands, and the web-settings stack.
|
|
6
6
|
|
|
7
|
-
>
|
|
7
|
+
> One install, one config block: the core `connect` service, every channel adapter (feishu / telegram / dingtalk / web), and the web-settings stack all live in this single package. Enable the channels you use via the `channels` selector. The former split packages (`dsh-connect-feishu`, `dsh-connect-telegram`, `dsh-connect-dingtalk`, `dsh-connect-web`) and the `dsh-connect-all` bundle no longer exist.
|
|
8
8
|
|
|
9
9
|
## Overview
|
|
10
10
|
|
|
11
11
|
`dsh-connect` binds a chat conversation to a DSH agent session and drives it end to end:
|
|
12
12
|
|
|
13
13
|
- **Session binding & routing** — one chat ⇄ one agent session, persisted in a `bindings.json` route store; sessions can be created, resumed, switched, cleared and mirrored to the DSH Web GUI.
|
|
14
|
-
- **Streaming replies** —
|
|
14
|
+
- **Streaming replies** — the model's live deltas are bridged into the channel's native streaming (Feishu typewriter cards): the thinking hint opens the reasoning phase, reasoning streams live with readable paragraph breaks, tool calls appear as `🔧` progress lines, and a liveness heartbeat keeps the card moving even through long silent stretches (long first-token waits, heavy tool runs) so it never sits frozen on "Thinking…". The runner keeps two subscriptions, because `0.1.5-rc.2` split what used to be one: the durable `session/event` stream carries turns, tools and settlement, while the transient `agent/assistant-stream` frames carry the deltas — the `assistant/chunk` session event that used to carry both is gone.
|
|
15
15
|
- **Notification levels** — per-chat control over how much of the process is streamed: `尽量输出过程` (full process) / `输出重要节点` (key milestones) / `只输出结果` (result only). Switch any time via the settings menu or `/notify`; the choice is persisted per chat and applies immediately.
|
|
16
16
|
- **Task-end stats** — after every task a compact card reports the model used, input/output tokens, elapsed time and context-window usage, and suggests `/compact` when the context is getting full.
|
|
17
17
|
- **Interactive menus** — button cards for status, tasks, history, goals, schedule, model/effort switching, workspace picking, language, and more (see the in-chat `/` commands).
|
|
@@ -25,10 +25,19 @@ The **channel-agnostic core** for connecting [DeepSeek Harness](https://github.c
|
|
|
25
25
|
|
|
26
26
|
| Aspect | Value |
|
|
27
27
|
|---|---|
|
|
28
|
-
| DSH version | `^0.1.
|
|
28
|
+
| DSH version | `^0.1.5-rc.2` (peer `@deepseek-ai/dsh-agent`, `dsh-llm`, `dsh-session`) |
|
|
29
29
|
| Cordis | `^4.0.1` |
|
|
30
30
|
| Node.js | ≥ 20 (ESM, `NodeNext`) |
|
|
31
|
-
| Last verified | **2026-
|
|
31
|
+
| Last verified | **2026-09-21** against DSH `0.1.5-rc.2` on Windows (host load, Feishu WebSocket transport, web-settings pane) |
|
|
32
|
+
|
|
33
|
+
**Keep the peer range in step with the host.** Upstream ships no changelog or
|
|
34
|
+
migration guide, so a stale range is the only thing standing between this plugin
|
|
35
|
+
and silent breakage: DSH `0.1.5-rc.2` deleted the `Session.events` accessor and
|
|
36
|
+
the `assistant/chunk` event type outright, and every `dsh-*` package is versioned
|
|
37
|
+
on the same line. When you upgrade DSH, bump `peerDependencies` (and
|
|
38
|
+
`devDependencies`) for `dsh-agent`, `dsh-llm` and `dsh-session` together, re-run
|
|
39
|
+
`tsc`, and re-run the test suite — a range that no longer overlaps the host
|
|
40
|
+
version is the signal that the bridge needs another migration.
|
|
32
41
|
|
|
33
42
|
The plugin runs on the DSH **Host plane** (process-level singleton services), not inside an agent preset.
|
|
34
43
|
|
|
@@ -37,31 +46,28 @@ The plugin runs on the DSH **Host plane** (process-level singleton services), no
|
|
|
37
46
|
Plugin management is a thin wrapper over pnpm in the DSH profile:
|
|
38
47
|
|
|
39
48
|
```sh
|
|
40
|
-
# Install (core +
|
|
41
|
-
dsh plugin --profile web add dsh-connect
|
|
42
|
-
|
|
43
|
-
# Optional: Web mirror monitor
|
|
44
|
-
dsh plugin --profile web add dsh-connect-web
|
|
49
|
+
# Install the single all-in-one plugin (core + all channel adapters + web-settings)
|
|
50
|
+
dsh plugin --profile web add dsh-connect
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
**Upgrade**
|
|
48
54
|
|
|
49
55
|
```sh
|
|
50
|
-
dsh plugin --profile web update dsh-connect
|
|
56
|
+
dsh plugin --profile web update dsh-connect
|
|
51
57
|
```
|
|
52
58
|
|
|
53
|
-
**Disable** — override the bundle-registered
|
|
59
|
+
**Disable** — override the bundle-registered entry with `disabled: true` in the profile patch (see `~/.dsh/profiles/<profile>/cordis.patch.yml`):
|
|
54
60
|
|
|
55
61
|
```yaml
|
|
56
|
-
- id: connect
|
|
62
|
+
- id: connect
|
|
57
63
|
name: dsh-connect
|
|
58
64
|
disabled: true
|
|
59
65
|
```
|
|
60
66
|
|
|
61
|
-
**Complete removal** — uninstall the
|
|
67
|
+
**Complete removal** — uninstall the package and delete the data it created:
|
|
62
68
|
|
|
63
69
|
```sh
|
|
64
|
-
dsh plugin --profile web remove dsh-connect
|
|
70
|
+
dsh plugin --profile web remove dsh-connect
|
|
65
71
|
# then remove the plugin data (see "Permissions & data" below):
|
|
66
72
|
rm -rf .dsh-connect # binding route store (stateDir)
|
|
67
73
|
rm -f ~/.dsh/.dsh-connect/feishu-credentials.json
|
|
@@ -70,36 +76,37 @@ rm -f ~/.dsh/.dsh-connect/feishu-credentials.json
|
|
|
70
76
|
## Quick start
|
|
71
77
|
|
|
72
78
|
1. **Install the plugins** (see above).
|
|
73
|
-
2. **Add the minimal config** to `~/.dsh/profiles/<profile>/cordis.patch.yml`
|
|
79
|
+
2. **Add the minimal config** to `~/.dsh/profiles/<profile>/cordis.patch.yml` — the shape is also in [`examples/minimal.config.json`](examples/minimal.config.json), and the fully commented version is [`examples/profile-cordis.patch.yml`](https://github.com/IvanWu2015/dsh-connect/blob/main/examples/profile-cordis.patch.yml) in the repository (that path is outside the published tarball, hence the absolute link). The plugin registers itself via its bundle manifest, so only override its config — do **not** `insert` it again (a duplicate id crashes dsh at boot):
|
|
74
80
|
|
|
75
81
|
```yaml
|
|
76
82
|
- id: connect
|
|
77
83
|
name: dsh-connect
|
|
78
84
|
# workDir: D:\your\workdir # agent working directory (default: process cwd)
|
|
79
|
-
- id: connect-feishu
|
|
80
|
-
name: dsh-connect-feishu
|
|
81
85
|
config:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
channels: [feishu] # which channels to activate; omit = all built-in
|
|
87
|
+
# channelDefaults: { language: zh } # keys applied to every channel that doesn't set its own
|
|
88
|
+
feishu:
|
|
89
|
+
appId: cli_xxxx
|
|
90
|
+
appSecret: cli_secret_xxxx
|
|
91
|
+
transport: websocket
|
|
92
|
+
requireMention: true
|
|
93
|
+
dmMode: open
|
|
87
94
|
```
|
|
88
95
|
|
|
89
|
-
3. **Start the host** — `dsh web` (or `dsh run`). With no credentials configured, `
|
|
96
|
+
3. **Start the host** — `dsh web` (or `dsh run`). With no credentials configured, the `feishu` channel enters **one-click onboarding**: scan the QR / open the link from the log to authorize the bot.
|
|
90
97
|
4. **Send a message** to the bot in Feishu. The bot replies with a streaming card; `/help` lists all commands; the conversation also appears in the DSH Web GUI automatically (auto-mirror).
|
|
91
98
|
|
|
92
|
-
A fully reproducible example is the `examples/` folder plus
|
|
99
|
+
A fully reproducible example is the [`examples/`](examples/) folder plus the repository's [Feishu setup manual](https://github.com/IvanWu2015/dsh-connect/blob/main/docs/feishu-setup.md) (Feishu app creation, event subscriptions, publishing).
|
|
93
100
|
|
|
94
101
|
## Configuration
|
|
95
102
|
|
|
96
|
-
Configuration lives in the DSH profile patch (`cordis.patch.yml`) under
|
|
103
|
+
Configuration lives in the DSH profile patch (`cordis.patch.yml`) under the plugin's `config:`. `dsh.shared.config.json` in the project root (or its parent) can supply workspace/state defaults that take precedence for those keys.
|
|
97
104
|
|
|
98
105
|
### `dsh-connect` (core)
|
|
99
106
|
|
|
100
107
|
| Key | Default | Description |
|
|
101
108
|
|---|---|---|
|
|
102
|
-
| `agentPreset` | roster default | Agent preset id composed into each bound session |
|
|
109
|
+
| `agentPreset` | roster default | Agent preset id composed into each bound session. Resolution is best-effort: the configured id is tried, then `standard` (or the roster's first mountable row), and if neither composes the agent is built without a preset so the turn still runs. A stale id therefore degrades instead of failing every message — it is logged, not thrown. |
|
|
103
110
|
| `workDir` | process cwd | Absolute working directory for each bound agent |
|
|
104
111
|
| `workspaces` | `[]` | Extra workspaces offered by the `/dir` picker |
|
|
105
112
|
| `visionModel` | auto-detected | `{ provider, model }` used to describe images when the main model can't see them |
|
|
@@ -109,10 +116,18 @@ Configuration lives in the DSH profile patch (`cordis.patch.yml`) under each plu
|
|
|
109
116
|
| `stateDir` | `.dsh-connect` | Directory holding the `bindings.json` route store (env `DSH_CONNECT_STATE_DIR` overrides) |
|
|
110
117
|
| `autoMirror` | `true` | Automatically create a Web GUI mirror for every new session |
|
|
111
118
|
| `streamHeartbeatMs` | `60000` | Liveness heartbeat interval (ms) for the streaming card; `0` disables it |
|
|
112
|
-
| `notifyLevel` | `
|
|
119
|
+
| `notifyLevel` | `result` | Default notification level: `full` (stream everything) / `important` (key milestones) / `result` (answer only, the default); per-chat override via settings menu or `/notify` |
|
|
113
120
|
| `progressTimeoutMs` | `300000` | Proactive progress-notice interval (ms): when a turn has sent no standalone card/text for this long, a status card reports the latest milestone; `0` disables; per-chat override via settings menu or `/progress` |
|
|
114
121
|
|
|
115
|
-
###
|
|
122
|
+
### Shared (all channels)
|
|
123
|
+
|
|
124
|
+
| Key | Default | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `channels` | all built-in | Which channels to activate: `feishu` / `telegram` / `dingtalk` / `web`. Omit to activate all built-in channels. |
|
|
127
|
+
| `channelDefaults` | `{}` | Keys applied to every channel that doesn't set its own (e.g. `{ language: "zh" }`). |
|
|
128
|
+
| `settingsStatePath` | `<stateDir>/dsh-connect-settings.json` | Where the web-settings pane mirrors non-secret config. Defaults to `dsh-connect-settings.json` *inside* `stateDir`, so it lands beside `bindings.json` and can never disagree with the stores; set it to override. Since 0.9.0 the pane's authoritative store is the `dsh-connect` section of `$DSH_HOME/settings.yaml` (see [User settings](#user-settings)), and this file is only the compatibility mirror the legacy `/dsh-connect` RPC reads and writes. |
|
|
129
|
+
|
|
130
|
+
### `feishu` (Feishu / Lark channel)
|
|
116
131
|
|
|
117
132
|
| Key | Default | Description |
|
|
118
133
|
|---|---|---|
|
|
@@ -137,11 +152,42 @@ Configuration lives in the DSH profile patch (`cordis.patch.yml`) under each plu
|
|
|
137
152
|
|
|
138
153
|
**Sensitive items** — `appSecret`, `verificationToken`, `encryptKey`, and `feishu-credentials.json`. Prefer environment variables or one-click onboarding; keep them out of version control.
|
|
139
154
|
|
|
155
|
+
### `telegram` (Telegram channel)
|
|
156
|
+
|
|
157
|
+
| Key | Default | Description |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `botToken` | env `TELEGRAM_BOT_TOKEN` | Telegram bot token from @BotFather (**secret**) |
|
|
160
|
+
| `requireMention` | `true` | Groups only respond when the bot is @mentioned (or replying to the bot's own message) |
|
|
161
|
+
| `pollingTimeoutSeconds` | `50` | `getUpdates` long-poll timeout in seconds |
|
|
162
|
+
| `baseUrl` | — | Optional Bot API base URL override (e.g. a local Bot API server) |
|
|
163
|
+
| `language` | `zh` | User-facing message language: `zh` / `en` |
|
|
164
|
+
|
|
165
|
+
### `dingtalk` (DingTalk channel)
|
|
166
|
+
|
|
167
|
+
| Key | Default | Description |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `webhookUrl` | env `DINGTALK_WEBHOOK_URL` | Group custom-robot webhook URL (proactive push) |
|
|
170
|
+
| `secret` | env `DINGTALK_WEBHOOK_SECRET` | Signing secret (`SEC…`) only when signing is enabled |
|
|
171
|
+
| `stream.clientId` / `stream.clientSecret` | env `DINGTALK_STREAM_CLIENT_ID` / `DINGTALK_STREAM_CLIENT_SECRET` | Bidirectional stream-mode app credentials (**secret**, nested under `stream`) |
|
|
172
|
+
| `stream.requireMention` | `true` | Group replies need an @-mention (stream mode) |
|
|
173
|
+
| `defaultAt` | — | Default @-mentions merged into every push (`{ mobiles, userIds, all }`) |
|
|
174
|
+
| `language` | `zh` | User-facing message language: `zh` / `en` |
|
|
175
|
+
|
|
176
|
+
### `web` (Web mirror channel)
|
|
177
|
+
|
|
178
|
+
| Key | Default | Description |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| `pollIntervalMs` | `1000` | Mirror-session polling interval (ms) |
|
|
181
|
+
|
|
182
|
+
Environment variables (`FEISHU_*`, `TELEGRAM_*`, `DINGTALK_*`, `DSH_CONNECT_STATE_DIR`, `DSH_HOME`) and the DSH credential store are the preferred way to supply per-channel secrets — the web settings pane writes them to the credential store and `injectSecrets` populates them on load.
|
|
183
|
+
|
|
140
184
|
## Permissions & data
|
|
141
185
|
|
|
142
186
|
- **Files written**
|
|
143
187
|
- `<stateDir>/bindings.json` (default `.dsh-connect/`) — the chat ⇄ session route store (chat keys, session ids, mirror and lock state).
|
|
144
|
-
-
|
|
188
|
+
- `<stateDir>/dsh-connect-settings.json` (default `.dsh-connect/`) — the non-secret compatibility mirror, see `settingsStatePath`.
|
|
189
|
+
- the `dsh-connect` section of `$DSH_HOME/settings.yaml` — written through DSH's first-party settings seam (atomic, file-locked, comment-preserving).
|
|
190
|
+
- `~/.dsh/.dsh-connect/feishu-credentials.json` — **legacy**, read-only. One-click onboarding used to save Feishu credentials here instead of the credential store, so a user who had just scanned the QR code still saw `未配置凭据` forever. Onboarding now writes to the credential store, and an existing install is backfilled from this file once on boot; after that it is never read or written again.
|
|
145
191
|
- `<workDir>/.dsh-connect-images/` — user images/attachments staged for the agent's tools.
|
|
146
192
|
- DSH's own session logs and settings under `~/.dsh/` (sessions, settings, etc.).
|
|
147
193
|
- **Network**
|
|
@@ -149,6 +195,107 @@ Configuration lives in the DSH profile patch (`cordis.patch.yml`) under each plu
|
|
|
149
195
|
- LLM provider APIs used by DSH for the agent's model (e.g. DeepSeek), plus the optional vision model.
|
|
150
196
|
- **User data** — message text and attachments flow through the bot to the agent session; they are stored in the DSH session log like any DSH conversation. The allowlists (`allowUsers` / `allowChats`) limit who can drive the bot.
|
|
151
197
|
|
|
198
|
+
## The settings pane
|
|
199
|
+
|
|
200
|
+
`dsh-connect` adds its own page under **Settings → dsh-connect**. It is a channel
|
|
201
|
+
tab strip over collapsible cards: each card is headed by a button, its
|
|
202
|
+
low-frequency fields sit behind a second-level **Advanced** fold, and
|
|
203
|
+
Save/status stay pinned to the bottom of the scroll region.
|
|
204
|
+
|
|
205
|
+
| Channels & credentials | Advanced fields opened |
|
|
206
|
+
|---|---|
|
|
207
|
+
|  |  |
|
|
208
|
+
|
|
209
|
+

|
|
210
|
+
|
|
211
|
+
English captures: [overview](docs/images/settings-overview-en.png) · [advanced](docs/images/settings-advanced-en.png) · [defaults](docs/images/settings-defaults-en.png).
|
|
212
|
+
|
|
213
|
+
> Captured from a throwaway profile whose credentials are all placeholders. Nothing
|
|
214
|
+
> above contains a real secret — and it could not, because the host masks every
|
|
215
|
+
> stored value before it reaches the browser (see [below](#seeing-and-masking-stored-values)).
|
|
216
|
+
|
|
217
|
+
Cards default to the channels you have enabled (the first one, if none are), and
|
|
218
|
+
clicking a tab opens that card and scrolls it into view *without* closing the
|
|
219
|
+
others — several open at once is a legitimate state. Collapsing a card unmounts
|
|
220
|
+
its body rather than hiding it, which is safe because an unsaved secret you typed
|
|
221
|
+
lives in the pane's own state, not in the card. Ticking a channel's enable box
|
|
222
|
+
opens it too.
|
|
223
|
+
|
|
224
|
+
## User settings
|
|
225
|
+
|
|
226
|
+
The web settings pane (`dsh-connect` under **Settings**) edits the `dsh-connect`
|
|
227
|
+
section of `$DSH_HOME/settings.yaml`, through DSH's own first-party settings
|
|
228
|
+
seam. That document is hot-reloaded, written atomically under a file lock, and
|
|
229
|
+
keeps your comments — so editing it by hand works too, and a change takes
|
|
230
|
+
effect without a restart.
|
|
231
|
+
|
|
232
|
+
Values resolve in three layers, most specific last:
|
|
233
|
+
|
|
234
|
+
1. the schema defaults shipped with the plugin;
|
|
235
|
+
2. the plugin's own `cordis.patch.yml` entry (your existing config is *not*
|
|
236
|
+
discarded — it is the registered base layer);
|
|
237
|
+
3. the `dsh-connect` section in `settings.yaml`.
|
|
238
|
+
|
|
239
|
+
**Secrets are never written to `settings.yaml`.** It is a plain document users
|
|
240
|
+
are invited to paste into bug reports, so credentials stay in the DSH
|
|
241
|
+
credential store (`ctx.credentials`) — which is also where one-click onboarding
|
|
242
|
+
and the `FEISHU_*`-style environment variables put them.
|
|
243
|
+
|
|
244
|
+
The legacy `/dsh-connect` HTTP RPC is retained for panel compatibility; it now
|
|
245
|
+
reads and writes the same namespace, and mirrors non-secret config to
|
|
246
|
+
`settingsStatePath` (see [Shared](#shared-all-channels)) for older panels.
|
|
247
|
+
|
|
248
|
+
### Seeing and masking stored values
|
|
249
|
+
|
|
250
|
+
The pane shows each stored credential as a read-only *current value* line under
|
|
251
|
+
its input (`not configured` when nothing is stored), so you can confirm what you
|
|
252
|
+
configured without retyping it. **The masking happens on the host**, in one shared table
|
|
253
|
+
(`src/settings/secret-disclosure.ts`) that both the host and the pane read — the
|
|
254
|
+
pane only decides whether to render the input as a `password` or a `text` field,
|
|
255
|
+
so the display and the policy cannot drift apart.
|
|
256
|
+
|
|
257
|
+
| Field | How it is shown |
|
|
258
|
+
|---|---|
|
|
259
|
+
| `appId`, `clientId` | **In full.** These are identifiers, not authenticators: they appear in every outbound API call and in the vendor console, so hiding them protects nothing. |
|
|
260
|
+
| `appSecret`, `clientSecret`, `botToken`, `secret` | Head and tail only — `a1b2…z9y8`. Values too short to survive partial disclosure show a fixed `••••••` instead. |
|
|
261
|
+
| `webhookUrl` (DingTalk) | URL-aware. Origin, path and parameter *names* are kept and only the token's middle is masked, because DingTalk puts the token in the query string — masking the whole URL (`https…bcde`) would confirm nothing. |
|
|
262
|
+
| any other key | Masked by default. |
|
|
263
|
+
|
|
264
|
+
Two properties hold regardless:
|
|
265
|
+
|
|
266
|
+
- **No usable secret crosses the wire.** The value is masked before it leaves the
|
|
267
|
+
host, so a browser tab — or a screenshot of one — never holds one.
|
|
268
|
+
- **A mask can never be written back.** The preview is text *beside* the input,
|
|
269
|
+
never the input's value. Inputs always start blank; blank means "leave the
|
|
270
|
+
stored value alone", so saving the config alone writes no credential at all.
|
|
271
|
+
|
|
272
|
+
The pane's own strings (channel names, field labels, option text, status) all
|
|
273
|
+
come from `client/locale.mjs`, which ships `zh` and `en`. A test asserts both
|
|
274
|
+
languages cover exactly the same key set — the host resolves a missing key by
|
|
275
|
+
silently falling back to the other language, so an untranslated string shows up
|
|
276
|
+
as mixed-language text rather than as an error.
|
|
277
|
+
|
|
278
|
+
### Credential groups
|
|
279
|
+
|
|
280
|
+
A channel counts as *configured* when **any one** of its credential groups is
|
|
281
|
+
fully satisfied, and each group is satisfied only when **all** of its refs are
|
|
282
|
+
set — all-of within a group, any-of across groups. A channel with no groups
|
|
283
|
+
(`web`) is configured by definition and never shows a warning badge.
|
|
284
|
+
|
|
285
|
+
The grouping exists because a channel can have more than one mutually exclusive
|
|
286
|
+
way to authenticate, and requiring all of them would wrongly report a working
|
|
287
|
+
bot as unconfigured:
|
|
288
|
+
|
|
289
|
+
| Channel | Groups |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `feishu` | app id + app secret |
|
|
292
|
+
| `telegram` | bot token |
|
|
293
|
+
| `dingtalk` | webhook URL + sign secret — *or* — stream client id + client secret |
|
|
294
|
+
| `web` | none |
|
|
295
|
+
|
|
296
|
+
So a DingTalk bot using only webhook push (no stream credentials) is correctly
|
|
297
|
+
reported as configured, as is one using only stream mode.
|
|
298
|
+
|
|
152
299
|
## Troubleshooting
|
|
153
300
|
|
|
154
301
|
Logs come from the DSH host logger (run `dsh web` in a terminal); plugin messages are prefixed `connect:` / `connect-feishu:`.
|
|
@@ -157,44 +304,50 @@ Logs come from the DSH host logger (run `dsh web` in a terminal); plugin message
|
|
|
157
304
|
|---|---|
|
|
158
305
|
| `connect-feishu: adapter init failed` / `start failed` | Bad credentials, app not published, or network blocked. Check `appId`/`appSecret`, re-run onboarding, verify the bot is online in the Feishu console. |
|
|
159
306
|
| `connect: resume of <id> failed, creating fresh session` | The persisted session could not be resumed (missing workdir, persistence issue). Check `workDir` and `~/.dsh/sessions`. |
|
|
307
|
+
| The bot answers every message with a raw `agent-presets: preset "…" not found` line, and nothing reaches the agent | A stale `agent-presets.default` in `$DSH_HOME/settings.yaml` names an id no installed build ships. Fixed in **0.9.0**, which retries `standard` and logs the decision rather than failing the turn; on an older build, set the key to a shipped id (`standard`). |
|
|
160
308
|
| Session-locked notices | Another client (Feishu or Web) holds the write lock. Use `/unlock` or wait for the lock timeout. |
|
|
161
|
-
| Model switch in the Web GUI appears ignored | Fixed in
|
|
309
|
+
| Model switch in the Web GUI appears ignored | Fixed in **0.9.0**: the plugin no longer pins a static default model over the Web GUI's session selection. Upgrade, then restart `dsh web`. |
|
|
162
310
|
| `[用户发送了图片,但下载失败…]` | Feishu `im:resource` permission is missing on the app; grant it and re-approve. |
|
|
163
|
-
| Streaming reply is one unbroken blob | Fixed in
|
|
164
|
-
| Card frozen on "Thinking…" with no progress on a long task | Fixed in
|
|
311
|
+
| Streaming reply is one unbroken blob | Fixed in **0.9.0**: block boundaries and the reasoning/answer split now insert blank lines (and reasoning soft breaks are expanded for Feishu cards). Upgrade, then restart `dsh web`. |
|
|
312
|
+
| Card frozen on "Thinking…" with no progress on a long task | Fixed in **0.9.0**: reasoning now streams live, tool calls show as `🔧` progress lines, and a liveness heartbeat updates the card during silent stretches. Upgrade, then restart `dsh web`. |
|
|
165
313
|
| Menu cards don't update / expire | Cards auto-close after 60 s idle by design; re-open the menu. |
|
|
166
314
|
|
|
167
315
|
**Rollback** — reinstall a previous release (`dsh plugin --profile web add dsh-connect@<version>` after removing the current one), or `git checkout` the pinned commit in a source install.
|
|
168
316
|
|
|
169
317
|
## Development
|
|
170
318
|
|
|
171
|
-
This is a pnpm workspace;
|
|
319
|
+
This is a pnpm workspace; `dsh-connect` is the single package under `packages/`:
|
|
172
320
|
|
|
173
321
|
```
|
|
174
322
|
packages/
|
|
175
|
-
connect/ # this package —
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
323
|
+
connect/ # this package — the all-in-one plugin
|
|
324
|
+
src/ # core: runner, service, binding, commands, menus, chat keys …
|
|
325
|
+
src/channels/ # channel adapters: feishu / telegram / dingtalk / web
|
|
326
|
+
src/settings/ # web-settings stack: host RPC, credential store, disclosure policy
|
|
327
|
+
client/ # web-settings frontend plugin + its pure, testable modules
|
|
328
|
+
test/ # node:test suites (run-all.mjs imports every suite)
|
|
329
|
+
docs/images/ # screenshots used by this README
|
|
330
|
+
examples/ # minimal.config.json
|
|
180
331
|
```
|
|
181
332
|
|
|
182
333
|
```sh
|
|
183
334
|
pnpm install
|
|
184
335
|
|
|
185
|
-
# build & typecheck
|
|
336
|
+
# build & typecheck the package
|
|
186
337
|
pnpm --filter dsh-connect build
|
|
187
338
|
pnpm --filter dsh-connect typecheck
|
|
188
339
|
|
|
189
|
-
# unit tests (node:test)
|
|
340
|
+
# unit tests (node:test) — run-all.mjs imports every suite in-process
|
|
190
341
|
pnpm test
|
|
191
342
|
# or run one suite
|
|
192
343
|
node packages/connect/test/unit.test.mjs
|
|
193
344
|
```
|
|
194
345
|
|
|
195
|
-
**Structure** — `src/runner.ts` owns the per-chat agent driver and the streaming bridge (`applyStreamChunk` is the pure, unit-tested chunk assembler); `src/service.ts` owns the adapter registry and routing; `src/
|
|
346
|
+
**Structure** — `src/runner.ts` owns the per-chat agent driver and the streaming bridge (`applyStreamChunk` is the pure, unit-tested chunk assembler); `src/service.ts` owns the adapter registry and routing; `src/channels/` holds the feishu / telegram / dingtalk / web channel adapters; `src/settings/` holds the web-settings stack (host RPC, credential store, disclosure policy); `src/binding.ts` is the route store.
|
|
347
|
+
|
|
348
|
+
Two things that used to live in `src/` moved to `client/` so they could be unit-tested without React: **`client/locale.mjs`** holds every user-visible pane string in `zh` and `en` (keep the key sets identical — the host resolves a missing key by silently rendering the *other* language, so an untranslated string shows up as half-English text, not as an error), and **`client/panel-state.mjs`** holds the card open/advanced rules. Both are plain ESM with no dependencies and are asserted directly in `test/locale.test.mjs` / `test/panel-state.test.mjs`.
|
|
196
349
|
|
|
197
|
-
**Contributing** — PRs welcome at [github.com/IvanWu2015/dsh-connect](https://github.com/IvanWu2015/dsh-connect). For user-facing strings, add the key to both `zh` and `en` in `
|
|
350
|
+
**Contributing** — PRs welcome at [github.com/IvanWu2015/dsh-connect](https://github.com/IvanWu2015/dsh-connect). For user-facing pane strings, add the key to both `zh` and `en` in `client/locale.mjs`, then rebuild the bundle (`node scripts/build-client.mjs`) — `test/client-bundle.test.mjs` runs the **built** artifact and fails if it is stale. Release notes live in `CHANGELOG.md`; see [`docs/PUBLISHING.md`](https://github.com/IvanWu2015/dsh-connect/blob/main/docs/PUBLISHING.md) for the release flow.
|
|
198
351
|
|
|
199
352
|
## License & security
|
|
200
353
|
|