@barryfan2045/dsh-matrix 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 +286 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/assets/avatars/instance-01.png +0 -0
- package/assets/avatars/instance-02.png +0 -0
- package/assets/avatars/instance-03.png +0 -0
- package/assets/avatars/instance-04.png +0 -0
- package/assets/avatars/instance-05.png +0 -0
- package/assets/avatars/instance-06.png +0 -0
- package/assets/avatars/instance-07.png +0 -0
- package/assets/avatars/instance-08.png +0 -0
- package/assets/avatars/instance-09.png +0 -0
- package/assets/avatars/instance-10.png +0 -0
- package/assets/avatars/instance-11.png +0 -0
- package/assets/avatars/instance-12.png +0 -0
- package/assets/avatars/management.png +0 -0
- package/cordis.patch.yml +13 -0
- package/dist/auth/admin-commands.d.ts +47 -0
- package/dist/auth/admin-commands.d.ts.map +1 -0
- package/dist/auth/admin-commands.js +112 -0
- package/dist/auth/admin-commands.js.map +1 -0
- package/dist/auth/challenge.d.ts +93 -0
- package/dist/auth/challenge.d.ts.map +1 -0
- package/dist/auth/challenge.js +104 -0
- package/dist/auth/challenge.js.map +1 -0
- package/dist/auth/store.d.ts +55 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +124 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/bridge/agent-commands.d.ts +39 -0
- package/dist/bridge/agent-commands.d.ts.map +1 -0
- package/dist/bridge/agent-commands.js +243 -0
- package/dist/bridge/agent-commands.js.map +1 -0
- package/dist/bridge/attachments-ledger.d.ts +54 -0
- package/dist/bridge/attachments-ledger.d.ts.map +1 -0
- package/dist/bridge/attachments-ledger.js +68 -0
- package/dist/bridge/attachments-ledger.js.map +1 -0
- package/dist/bridge/commands.d.ts +28 -0
- package/dist/bridge/commands.d.ts.map +1 -0
- package/dist/bridge/commands.js +18 -0
- package/dist/bridge/commands.js.map +1 -0
- package/dist/bridge/crypto-health.d.ts +37 -0
- package/dist/bridge/crypto-health.d.ts.map +1 -0
- package/dist/bridge/crypto-health.js +38 -0
- package/dist/bridge/crypto-health.js.map +1 -0
- package/dist/bridge/group-gate.d.ts +38 -0
- package/dist/bridge/group-gate.d.ts.map +1 -0
- package/dist/bridge/group-gate.js +49 -0
- package/dist/bridge/group-gate.js.map +1 -0
- package/dist/bridge/instance-lock.d.ts +10 -0
- package/dist/bridge/instance-lock.d.ts.map +1 -0
- package/dist/bridge/instance-lock.js +82 -0
- package/dist/bridge/instance-lock.js.map +1 -0
- package/dist/bridge/interactions.d.ts +62 -0
- package/dist/bridge/interactions.d.ts.map +1 -0
- package/dist/bridge/interactions.js +237 -0
- package/dist/bridge/interactions.js.map +1 -0
- package/dist/bridge/management-room.d.ts +13 -0
- package/dist/bridge/management-room.d.ts.map +1 -0
- package/dist/bridge/management-room.js +27 -0
- package/dist/bridge/management-room.js.map +1 -0
- package/dist/bridge/multi-project.d.ts +34 -0
- package/dist/bridge/multi-project.d.ts.map +1 -0
- package/dist/bridge/multi-project.js +44 -0
- package/dist/bridge/multi-project.js.map +1 -0
- package/dist/bridge/pipeline.d.ts +151 -0
- package/dist/bridge/pipeline.d.ts.map +1 -0
- package/dist/bridge/pipeline.js +502 -0
- package/dist/bridge/pipeline.js.map +1 -0
- package/dist/bridge/pmctl.d.ts +50 -0
- package/dist/bridge/pmctl.d.ts.map +1 -0
- package/dist/bridge/pmctl.js +272 -0
- package/dist/bridge/pmctl.js.map +1 -0
- package/dist/bridge/power-levels.d.ts +57 -0
- package/dist/bridge/power-levels.d.ts.map +1 -0
- package/dist/bridge/power-levels.js +92 -0
- package/dist/bridge/power-levels.js.map +1 -0
- package/dist/bridge/power-sync.d.ts +64 -0
- package/dist/bridge/power-sync.d.ts.map +1 -0
- package/dist/bridge/power-sync.js +121 -0
- package/dist/bridge/power-sync.js.map +1 -0
- package/dist/bridge/projects.d.ts +97 -0
- package/dist/bridge/projects.d.ts.map +1 -0
- package/dist/bridge/projects.js +288 -0
- package/dist/bridge/projects.js.map +1 -0
- package/dist/bridge/quote-cache.d.ts +38 -0
- package/dist/bridge/quote-cache.d.ts.map +1 -0
- package/dist/bridge/quote-cache.js +56 -0
- package/dist/bridge/quote-cache.js.map +1 -0
- package/dist/bridge/reply.d.ts +31 -0
- package/dist/bridge/reply.d.ts.map +1 -0
- package/dist/bridge/reply.js +122 -0
- package/dist/bridge/reply.js.map +1 -0
- package/dist/bridge/space-identity.d.ts +43 -0
- package/dist/bridge/space-identity.d.ts.map +1 -0
- package/dist/bridge/space-identity.js +92 -0
- package/dist/bridge/space-identity.js.map +1 -0
- package/dist/bridge/space-sync.d.ts +94 -0
- package/dist/bridge/space-sync.d.ts.map +1 -0
- package/dist/bridge/space-sync.js +137 -0
- package/dist/bridge/space-sync.js.map +1 -0
- package/dist/bridge/turn-commands.d.ts +28 -0
- package/dist/bridge/turn-commands.d.ts.map +1 -0
- package/dist/bridge/turn-commands.js +97 -0
- package/dist/bridge/turn-commands.js.map +1 -0
- package/dist/cli/config-store.d.ts +32 -0
- package/dist/cli/config-store.d.ts.map +1 -0
- package/dist/cli/config-store.js +118 -0
- package/dist/cli/config-store.js.map +1 -0
- package/dist/cli/crypto-store.d.ts +42 -0
- package/dist/cli/crypto-store.d.ts.map +1 -0
- package/dist/cli/crypto-store.js +70 -0
- package/dist/cli/crypto-store.js.map +1 -0
- package/dist/cli/device-prune.d.ts +105 -0
- package/dist/cli/device-prune.d.ts.map +1 -0
- package/dist/cli/device-prune.js +163 -0
- package/dist/cli/device-prune.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +584 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/log-filter.d.ts +32 -0
- package/dist/cli/log-filter.d.ts.map +1 -0
- package/dist/cli/log-filter.js +40 -0
- package/dist/cli/log-filter.js.map +1 -0
- package/dist/cli/pnpm-builds.d.ts +32 -0
- package/dist/cli/pnpm-builds.d.ts.map +1 -0
- package/dist/cli/pnpm-builds.js +73 -0
- package/dist/cli/pnpm-builds.js.map +1 -0
- package/dist/cli/profile-patch.d.ts +26 -0
- package/dist/cli/profile-patch.d.ts.map +1 -0
- package/dist/cli/profile-patch.js +56 -0
- package/dist/cli/profile-patch.js.map +1 -0
- package/dist/cli/setup.d.ts +39 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +234 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/systemd-hint.d.ts +11 -0
- package/dist/cli/systemd-hint.d.ts.map +1 -0
- package/dist/cli/systemd-hint.js +46 -0
- package/dist/cli/systemd-hint.js.map +1 -0
- package/dist/cli/systemd.d.ts +32 -0
- package/dist/cli/systemd.d.ts.map +1 -0
- package/dist/cli/systemd.js +58 -0
- package/dist/cli/systemd.js.map +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/dsh/backend.d.ts +110 -0
- package/dist/dsh/backend.d.ts.map +1 -0
- package/dist/dsh/backend.js +481 -0
- package/dist/dsh/backend.js.map +1 -0
- package/dist/dsh/interactions.d.ts +50 -0
- package/dist/dsh/interactions.d.ts.map +1 -0
- package/dist/dsh/interactions.js +141 -0
- package/dist/dsh/interactions.js.map +1 -0
- package/dist/dsh/stream.d.ts +32 -0
- package/dist/dsh/stream.d.ts.map +1 -0
- package/dist/dsh/stream.js +109 -0
- package/dist/dsh/stream.js.map +1 -0
- package/dist/formatting.d.ts +14 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +63 -0
- package/dist/formatting.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/matrix/matrix-events.d.ts +49 -0
- package/dist/matrix/matrix-events.d.ts.map +1 -0
- package/dist/matrix/matrix-events.js +89 -0
- package/dist/matrix/matrix-events.js.map +1 -0
- package/dist/matrix/transport.d.ts +192 -0
- package/dist/matrix/transport.d.ts.map +1 -0
- package/dist/matrix/transport.js +510 -0
- package/dist/matrix/transport.js.map +1 -0
- package/dist/ports/attachments.d.ts +51 -0
- package/dist/ports/attachments.d.ts.map +1 -0
- package/dist/ports/attachments.js +27 -0
- package/dist/ports/attachments.js.map +1 -0
- package/dist/ports/backend.d.ts +159 -0
- package/dist/ports/backend.d.ts.map +1 -0
- package/dist/ports/backend.js +25 -0
- package/dist/ports/backend.js.map +1 -0
- package/dist/ports/interactions.d.ts +65 -0
- package/dist/ports/interactions.d.ts.map +1 -0
- package/dist/ports/interactions.js +15 -0
- package/dist/ports/interactions.js.map +1 -0
- package/dist/ports/streaming.d.ts +45 -0
- package/dist/ports/streaming.d.ts.map +1 -0
- package/dist/ports/streaming.js +23 -0
- package/dist/ports/streaming.js.map +1 -0
- package/dist/ports/transport.d.ts +68 -0
- package/dist/ports/transport.d.ts.map +1 -0
- package/dist/ports/transport.js +2 -0
- package/dist/ports/transport.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hi-Barry
|
|
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,286 @@
|
|
|
1
|
+
# dsh-matrix
|
|
2
|
+
|
|
3
|
+
用 **Matrix** 远程驱动 DSH(DeepSeek Harness)编码代理:每个聊天房间对应一个 agent 会话,斜杠命令、提问、审批都能在聊天室里完成。
|
|
4
|
+
|
|
5
|
+
Drive [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) from **Matrix** — every room is an agent session; slash commands, questions and approvals all work from your phone.
|
|
6
|
+
|
|
7
|
+
[简体中文](#简体中文) | [English](#english)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 简体中文
|
|
12
|
+
|
|
13
|
+
### 它是什么
|
|
14
|
+
|
|
15
|
+
dsh-matrix 是一个 DSH 的 Cordis 插件包(bundle 形态)。装上后,`dsh --profile matrix` 就是一个常驻的 Matrix 桥:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Matrix 聊天室 <--> dsh-matrix(桥插件,DSH 进程内) <--> Agent 会话(同进程)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **不是子进程桥**:会话就是 DSH 自己的 Agent,重启后自动恢复(resume),上下文不丢
|
|
22
|
+
- **完整命令面**:`/compact`、`/plan`、`/goal` 等 DSH 命令透传;桥自带 `/model`、`/thinking`、`/stop`、`/queue`、`/interrupt`、`/sessions`、`/name`、`/status`、`/help`
|
|
23
|
+
- **提问进房间**:agent 的 `ask_user_question` 会渲染成编号列表发到房间,回一条消息就是答案
|
|
24
|
+
- **审批进房间**:沙箱升级请求会在房间里问 (y/n),一条消息放行
|
|
25
|
+
- **多模型在线切换**:`/model <provider/id>` 即切,对话上下文保持
|
|
26
|
+
- **端到端加密(E2EE)**:加密房间正常收发(matrix-bot-sdk + Rust crypto)
|
|
27
|
+
- **信任与挑战认证**:陌生用户首聊需回日志里的 6 位验证码;`/trusted`、`/revoke` 管理
|
|
28
|
+
- **附件**:发图片给房间,agent 用视觉能力读图(DSH 原生 attachments)
|
|
29
|
+
|
|
30
|
+
### 安装(三步)
|
|
31
|
+
|
|
32
|
+
前置:Node >= 20、一个 Matrix bot 账号、已安装 `dsh`。
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 1) 建 profile 并装桥
|
|
36
|
+
dsh plugin --profile matrix add @barryfan2045/dsh-matrix
|
|
37
|
+
|
|
38
|
+
# 2) 写配置: ~/.dsh/profiles/matrix/cordis.patch.yml 追加(chmod 600)
|
|
39
|
+
- id: matrix-bridge
|
|
40
|
+
config:
|
|
41
|
+
homeserver: https://matrix.example.com
|
|
42
|
+
username: bot
|
|
43
|
+
password: "xxx" # 或 accessToken: syt_xxx
|
|
44
|
+
trustedUsers: ['@you:matrix.example.com']
|
|
45
|
+
workdir: /home/you/Projects
|
|
46
|
+
|
|
47
|
+
# 3) 跑起来(或交给 systemd)
|
|
48
|
+
dsh --profile matrix
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **装不上的话**:第 1 步可能报 `ERR_PNPM_IGNORED_BUILDS` —— pnpm 默认不跑依赖的
|
|
52
|
+
> 构建脚本,而加密原生库(`@matrix-org/matrix-sdk-crypto-nodejs`)要靠它下载预编译
|
|
53
|
+
> 二进制。此时依赖已写进 profile 的 `package.json`,但命令非零退出,dsh 就中止了,
|
|
54
|
+
> 插件并没有真正挂上。把 `~/.dsh/profiles/matrix/pnpm-workspace.yaml` 里
|
|
55
|
+
> `allowBuilds:` 下那行改成 `true`,再跑一次 `dsh plugin --profile matrix install` 即可。
|
|
56
|
+
> 用 `dsh-matrix install-profile` 安装的话,这一步会自动处理。
|
|
57
|
+
|
|
58
|
+
> **E2EE 突然收不到加密消息**:日志里若反复出现 `Can't find the room key to decrypt the
|
|
59
|
+
> event`,先看启动时那两行:`E2EE 已启用(crypto: …)` 与 `matrix 登录成功: …`。
|
|
60
|
+
> 桥现在会**复用 crypto 存储里的设备身份**去登录(日志里带 `(复用设备 XXXXXXXX)`),
|
|
61
|
+
> "设备密钥挂在 A、`/sync` 却在听 B"这种分叉不会再发生。
|
|
62
|
+
> 只有当日志出现 `E2EE 设备身份分叉` 的 WARN(用 `accessToken` 直连时才会)才需要复位:
|
|
63
|
+
> 跑 `dsh-matrix reset-crypto`,默认把旧存储**改名备份**(`--purge` 才真删)。
|
|
64
|
+
> 代价要想清楚 —— **换存储 = 换设备身份**,Element 那边会看到一个新设备、需要重新验证,
|
|
65
|
+
> 历史加密消息也解不开。复位后 `dsh-matrix enable` 恢复服务。
|
|
66
|
+
> 另外:复位只换本地存储,**不会**删服务端上的旧设备(它们会成为孤儿设备、拖慢加密发送);
|
|
67
|
+
> 用 `dsh-matrix prune-devices` 清理(默认只列出,确认后加 `--yes`)。
|
|
68
|
+
|
|
69
|
+
在 Element(或任何 Matrix 客户端)里给 bot 发私信即可开聊。
|
|
70
|
+
|
|
71
|
+
### 运维命令(`dsh-matrix`)
|
|
72
|
+
|
|
73
|
+
| 命令 | 作用 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `setup` | 配置向导,写 `~/.dsh/dsh-matrix.json`(0600) |
|
|
76
|
+
| `install-profile` | 挂载 bundle + 幂等生成 profile patch |
|
|
77
|
+
| `enable` / `start` / `stop` / `restart` / `disable` | systemd user unit 生命周期 |
|
|
78
|
+
| `status` / `logs` | 查看服务状态 / 跟踪日志 |
|
|
79
|
+
| `run` | 前台运行(等价 `dsh --profile <实例名>`) |
|
|
80
|
+
| `reset-crypto` | 复位 crypto 存储(默认**改名备份**,`--purge` 才真删) |
|
|
81
|
+
| `prune-devices` | 清理账号上遗留的孤儿设备——**默认只列出**,加 `--yes` 才真删 |
|
|
82
|
+
|
|
83
|
+
> **什么是孤儿设备**:桥以前每次密码登录都会新建设备(现已修好)。这些旧设备的私钥
|
|
84
|
+
> 早已随着复位的 crypto 目录作废,却仍然留在账号上、参与**每一次**加密发送的设备列表
|
|
85
|
+
> (要把房间密钥逐个分享给它们),实测能把首次发送拖到分钟级。清理时会保留
|
|
86
|
+
> **当前桥设备**,其余先列出来给你确认:
|
|
87
|
+
>
|
|
88
|
+
> ```bash
|
|
89
|
+
> dsh-matrix prune-devices # 演练:只列出,不删任何设备
|
|
90
|
+
> dsh-matrix prune-devices --yes # 确认后真删(不可逆)
|
|
91
|
+
> dsh-matrix prune-devices --yes --keep-days 7 # 顺带保留最近 7 天活动过的
|
|
92
|
+
> ```
|
|
93
|
+
>
|
|
94
|
+
> 删设备需要 Matrix 的 UI auth 重认证,所以运维配置里得有 `password`(只有
|
|
95
|
+
> `accessToken` 时会明确报错,不会静默失败)。
|
|
96
|
+
> **这条命令有一条硬规矩:绝不新建设备。** 它会复用 crypto 存储里的设备身份去登录;
|
|
97
|
+
> 万一找不到既有身份(比如换了 `DSH_HOME`),它会**直接拒绝并告诉你怎么办**,而不是
|
|
98
|
+
> 默默造一个新设备 —— 那正好是它要治的毛病。桥自定义过 `dataDir` 时用
|
|
99
|
+
> `--data-dir <dir>` 指过来。
|
|
100
|
+
|
|
101
|
+
### 常用命令
|
|
102
|
+
|
|
103
|
+
| 命令 | 作用 |
|
|
104
|
+
|---|---|
|
|
105
|
+
| 普通文本 | 空闲 -> 开回合;运行中 -> steer 注入当前回合 |
|
|
106
|
+
| `/stop` `/abort` `/queue <文本>` `/interrupt <文本>` `/last` | 回合控制(对齐 pi TUI 语义) |
|
|
107
|
+
| `/model [provider/id]` `/models` `/thinking [档位]` | 查看/切换模型与推理档位 |
|
|
108
|
+
| `/sessions` `/name <标题>` `/status` `/export` | 会话管理(`/sessions` 列出本进程已知的会话) |
|
|
109
|
+
| `/compact` `/plan` `/goal` … | DSH 原生命令透传(按当前 profile 实时列出) |
|
|
110
|
+
| `/help` | 全量命令表(桥命令 + 透传命令) |
|
|
111
|
+
| `/trusted` `/revoke <用户>` | 信任名单管理(admin) |
|
|
112
|
+
| `/channels` `/enable` `/disable` | 群聊响应模式:全部 / 仅 @ 提到 / 仅信任用户(admin) |
|
|
113
|
+
| `/pmctl new\|list\|show\|rm\|mv\|rename` | 多项目管理(仅管理房间) |
|
|
114
|
+
| `y` / `n` / 数字 / 「取消」 | 回答房间里的提问与审批 |
|
|
115
|
+
|
|
116
|
+
### 与 pi-courier 的差异
|
|
117
|
+
|
|
118
|
+
| 维度 | pi-courier | dsh-matrix |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| 运行形态 | 外部 RPC 子进程(pi CLI),每会话一个进程 | DSH 进程内插件(Cordis bundle),会话就是 DSH Agent |
|
|
121
|
+
| 会话恢复 | 自己管进程与上下文 | DSH 原生 `resume`,重启进程后上下文不丢 |
|
|
122
|
+
| 提问 | 无 | agent 的 `ask_user_question` 渲染成编号列表进房间 |
|
|
123
|
+
| 审批 | 无 | 沙箱升级审批进房间,一条 `y`/`n` 放行 |
|
|
124
|
+
| 命令面 | pi 自己的命令表 | 桥命令 + DSH 原生命令透传(`/help` 实时列出) |
|
|
125
|
+
| 登录 | `/login` `/logout` `/auth`(OAuth 设备码) | 无此命令:DSH 用 settings/credentials 里的 API key |
|
|
126
|
+
| `/autocompact` `/autoretry` | 支持(写 pi 全局设置) | 未实现:全局设置 vs composition 行,语义不可平移 |
|
|
127
|
+
| `/reload` | 支持(重启 pi 进程) | 未实现:进程内插件没有子进程可重启 |
|
|
128
|
+
| `/new` | 支持 | 未实现:房间↔会话是确定性一对一,同房间换会话会牵动持久化/鉴权/账本;要全新上下文请用 `/pmctl new` 开新房间 |
|
|
129
|
+
| `/clear` `/cost` `/cyclemodel` `/cyclethinking` `/bash` | 支持 | 未实现(不在本规格的用户故事内) |
|
|
130
|
+
|
|
131
|
+
### 来自 pi-courier
|
|
132
|
+
|
|
133
|
+
本项目移植自 [pi-courier](https://github.com/Hi-Barry/pi-courier)(MIT)的 Matrix 层——
|
|
134
|
+
传输、挑战认证、引用缓存、格式化逻辑在这里重获新生;架构从「外部 RPC 子进程」换成了
|
|
135
|
+
「DSH 进程内插件」。再上游致谢 [tintinweb/pi-messenger-bridge](https://github.com/tintinweb/pi-messenger-bridge)(MIT)。
|
|
136
|
+
逐文件的来源标注与致谢见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
|
|
137
|
+
|
|
138
|
+
### 许可
|
|
139
|
+
|
|
140
|
+
MIT,全文见 [LICENSE](LICENSE)。
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## English
|
|
145
|
+
|
|
146
|
+
### What it is
|
|
147
|
+
|
|
148
|
+
dsh-matrix is a **Cordis plugin (bundle)** for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
149
|
+
Once installed, `dsh --profile matrix` is a long-running Matrix bridge:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
Matrix room <--> dsh-matrix (plugin, inside the DSH process) <--> agent session (same process)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- **Not a child-process bridge**: the session *is* a DSH agent, and it resumes after a restart with its context intact.
|
|
156
|
+
- **Full command surface**: DSH commands (`/compact`, `/plan`, `/goal`, …) pass through; the bridge adds `/model`, `/thinking`, `/stop`, `/queue`, `/interrupt`, `/sessions`, `/name`, `/status`, `/help`.
|
|
157
|
+
- **Questions land in the room**: the agent's `ask_user_question` is rendered as a numbered list — one reply is the answer.
|
|
158
|
+
- **Approvals land in the room**: a sandbox escalation asks `(y/n)` in the room; one message lets it through.
|
|
159
|
+
- **Live model switching**: `/model <provider/id>` switches mid-conversation without losing context.
|
|
160
|
+
- **End-to-end encryption**: encrypted rooms work (matrix-bot-sdk + Rust crypto).
|
|
161
|
+
- **Trust on first contact**: a stranger's first DM must echo a 6-digit code printed in the log; `/trusted` and `/revoke` manage the list.
|
|
162
|
+
- **Attachments**: send an image and the agent reads it through DSH's native attachments (vision).
|
|
163
|
+
|
|
164
|
+
### Install (three steps)
|
|
165
|
+
|
|
166
|
+
Prerequisites: Node >= 20, a Matrix bot account, and `dsh` installed.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 1) create the profile and install the bridge
|
|
170
|
+
dsh plugin --profile matrix add @barryfan2045/dsh-matrix
|
|
171
|
+
|
|
172
|
+
# 2) append this to ~/.dsh/profiles/matrix/cordis.patch.yml (chmod 600)
|
|
173
|
+
- id: matrix-bridge
|
|
174
|
+
config:
|
|
175
|
+
homeserver: https://matrix.example.com
|
|
176
|
+
username: bot
|
|
177
|
+
password: "xxx" # or accessToken: syt_xxx
|
|
178
|
+
trustedUsers: ['@you:matrix.example.com']
|
|
179
|
+
workdir: /home/you/Projects
|
|
180
|
+
|
|
181
|
+
# 3) run it (or hand it to systemd)
|
|
182
|
+
dsh --profile matrix
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> **If step 1 fails**: it may abort with `ERR_PNPM_IGNORED_BUILDS` — pnpm does not run
|
|
186
|
+
> dependency build scripts by default, and the crypto native library
|
|
187
|
+
> (`@matrix-org/matrix-sdk-crypto-nodejs`) needs its postinstall to download a prebuilt
|
|
188
|
+
> binary. The dependency is already in the profile's `package.json`, but the non-zero exit
|
|
189
|
+
> makes dsh stop before mounting the bundle. Set the `allowBuilds:` entry in
|
|
190
|
+
> `~/.dsh/profiles/matrix/pnpm-workspace.yaml` to `true` and re-run
|
|
191
|
+
> `dsh plugin --profile matrix install`. Installing through `dsh-matrix install-profile`
|
|
192
|
+
> handles this automatically.
|
|
193
|
+
|
|
194
|
+
> **E2EE suddenly stops delivering encrypted messages**: if the log keeps repeating
|
|
195
|
+
> `Can't find the room key to decrypt the event`, first check the two startup lines:
|
|
196
|
+
> `E2EE 已启用(crypto: …)` and `matrix 登录成功: …`. The bridge now **reuses the device
|
|
197
|
+
> identity stored in its crypto store** when it logs in (the log says
|
|
198
|
+
> `(复用设备 XXXXXXXX)`), so the split brain — device keys uploaded for device A while
|
|
199
|
+
> `/sync` listens as device B — no longer happens.
|
|
200
|
+
> Only if you see the `E2EE 设备身份分叉` WARN (possible only on the `accessToken` path)
|
|
201
|
+
> should you reset: run `dsh-matrix reset-crypto`, which **renames the old store to a
|
|
202
|
+
> backup** by default (`--purge` deletes it). Be aware of the cost — **a new store means a
|
|
203
|
+
> new device identity**: Element will show a new device that needs re-verification, and old
|
|
204
|
+
> encrypted messages stay unreadable. Then `dsh-matrix enable` to bring the bridge back.
|
|
205
|
+
> Note: a reset only swaps the local store — it does **not** delete the old devices on the
|
|
206
|
+
> server (they become orphans and slow encrypted sends down). Clean them up with
|
|
207
|
+
> `dsh-matrix prune-devices` (lists only by default; add `--yes` to actually delete).
|
|
208
|
+
|
|
209
|
+
DM the bot from Element (or any Matrix client) to start chatting.
|
|
210
|
+
|
|
211
|
+
### Ops commands (`dsh-matrix`)
|
|
212
|
+
|
|
213
|
+
| Command | Effect |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `setup` | Config wizard; writes `~/.dsh/dsh-matrix.json` (0600) |
|
|
216
|
+
| `install-profile` | Mounts the bundle + idempotently writes the profile patch |
|
|
217
|
+
| `enable` / `start` / `stop` / `restart` / `disable` | systemd user unit lifecycle |
|
|
218
|
+
| `status` / `logs` | Service status / follow logs |
|
|
219
|
+
| `run` | Foreground run (same as `dsh --profile <name>`) |
|
|
220
|
+
| `reset-crypto` | Reset the crypto store (**renames to a backup**; `--purge` deletes) |
|
|
221
|
+
| `prune-devices` | Clean up leftover orphan devices — **lists only** unless you add `--yes` |
|
|
222
|
+
|
|
223
|
+
> **What is an orphan device?** The bridge used to create a brand-new device on every
|
|
224
|
+
> password login (fixed now). Those old devices' private keys died with the crypto store
|
|
225
|
+
> they were reset away from, yet they still sit on the account and take part in **every**
|
|
226
|
+
> encrypted send (the room key gets shared with each of them) — measured to push the first
|
|
227
|
+
> send into the minutes. Pruning always keeps the **current bridge device** and lists the
|
|
228
|
+
> rest for you to confirm first:
|
|
229
|
+
>
|
|
230
|
+
> ```bash
|
|
231
|
+
> dsh-matrix prune-devices # dry run: lists, deletes nothing
|
|
232
|
+
> dsh-matrix prune-devices --yes # actually delete (irreversible)
|
|
233
|
+
> dsh-matrix prune-devices --yes --keep-days 7 # also keep devices seen in the last 7 days
|
|
234
|
+
> ```
|
|
235
|
+
>
|
|
236
|
+
> Deleting a device needs Matrix UI auth re-authentication, so the ops config must have a
|
|
237
|
+
> `password` (with only an `accessToken` it fails loudly rather than silently).
|
|
238
|
+
> **This command has one hard rule: it never creates a device.** It logs in by reusing the
|
|
239
|
+
> device identity in the crypto store; if it cannot find an existing identity (e.g. you
|
|
240
|
+
> pointed at a different `DSH_HOME`) it **refuses and tells you what to do** instead of
|
|
241
|
+
> quietly minting a new device — which is exactly the disease it exists to cure. If the
|
|
242
|
+
> bridge uses a custom `dataDir`, pass `--data-dir <dir>`.
|
|
243
|
+
|
|
244
|
+
### Commands
|
|
245
|
+
|
|
246
|
+
| Command | Effect |
|
|
247
|
+
|---|---|
|
|
248
|
+
| Plain text | idle -> starts a turn; running -> steers the current turn |
|
|
249
|
+
| `/stop` `/abort` `/queue <text>` `/interrupt <text>` `/last` | Turn control (same semantics as the pi TUI) |
|
|
250
|
+
| `/model [provider/id]` `/models` `/thinking [level]` | Show/switch model and reasoning effort |
|
|
251
|
+
| `/sessions` `/name <title>` `/status` `/export` | Session management (`/sessions` lists the sessions this process knows) |
|
|
252
|
+
| `/compact` `/plan` `/goal` … | DSH commands, passed through (listed live from the current profile) |
|
|
253
|
+
| `/help` | Full command table (bridge commands + pass-through) |
|
|
254
|
+
| `/trusted` `/revoke <user>` | Trusted-list management (admin) |
|
|
255
|
+
| `/channels` `/enable` `/disable` | Group response mode: all / mentions only / trusted users only (admin) |
|
|
256
|
+
| `/pmctl new\|list\|show\|rm\|mv\|rename` | Multi-project management (management room only) |
|
|
257
|
+
| `y` / `n` / a number / "cancel" | Answer a question or approval in the room |
|
|
258
|
+
|
|
259
|
+
### Differences from pi-courier
|
|
260
|
+
|
|
261
|
+
| Aspect | pi-courier | dsh-matrix |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| Runtime shape | External RPC child process (pi CLI), one per session | In-process DSH plugin (Cordis bundle); the session *is* the DSH agent |
|
|
264
|
+
| Session restore | Manages its own processes and context | DSH-native `resume`; context survives a restart |
|
|
265
|
+
| Questions | none | the agent's `ask_user_question` becomes a numbered list in the room |
|
|
266
|
+
| Approvals | none | sandbox escalation asks in the room; one `y`/`n` releases it |
|
|
267
|
+
| Command surface | pi's own command table | bridge commands + DSH pass-through (listed live by `/help`) |
|
|
268
|
+
| Login | `/login` `/logout` `/auth` (OAuth device code) | no such command: DSH uses API keys from settings/credentials |
|
|
269
|
+
| `/autocompact` `/autoretry` | supported (writes pi's global settings) | not implemented: global settings vs composition rows |
|
|
270
|
+
| `/reload` | supported (restarts the pi process) | not implemented: an in-process plugin has no child process to restart |
|
|
271
|
+
| `/new` | supported | not implemented: a room maps to exactly one session, so a same-room reset would ripple through persistence, auth and ledgers. Use `/pmctl new` to start a fresh room/session |
|
|
272
|
+
| `/clear` `/cost` `/cyclemodel` `/cyclethinking` `/bash` | supported | not implemented (outside this spec's user stories) |
|
|
273
|
+
|
|
274
|
+
### Ported from pi-courier
|
|
275
|
+
|
|
276
|
+
This project ports the Matrix layer of [pi-courier](https://github.com/Hi-Barry/pi-courier) (MIT):
|
|
277
|
+
transport, challenge auth, the quote cache and the formatting logic all live on here. The architecture
|
|
278
|
+
changed from "external RPC child process" to "in-process DSH plugin". Upstream thanks to
|
|
279
|
+
[tintinweb/pi-messenger-bridge](https://github.com/tintinweb/pi-messenger-bridge) (MIT).
|
|
280
|
+
Per-file attribution is in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
281
|
+
|
|
282
|
+
### License
|
|
283
|
+
|
|
284
|
+
MIT — see [LICENSE](LICENSE). Includes code ported from pi-courier (MIT) and
|
|
285
|
+
tintinweb/pi-messenger-bridge (MIT); per-file attribution is in
|
|
286
|
+
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 第三方来源与致谢
|
|
2
|
+
|
|
3
|
+
dsh-matrix 以 MIT 许可发布(见 [LICENSE](./LICENSE))。下面列出实际被移植进本仓库的
|
|
4
|
+
上游代码。上游同样都是 MIT,逐文件的来源标注写在对应源码文件的头部注释里。
|
|
5
|
+
|
|
6
|
+
## pi-courier — https://github.com/Hi-Barry/pi-courier
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Hi-Barry — MIT。
|
|
9
|
+
|
|
10
|
+
本项目的 Matrix 传输层、挑战认证、引用缓存、附件账本、格式化与运维 CLI 移植自
|
|
11
|
+
pi-courier。**架构不同**:pi-courier 是「外部 RPC 子进程」,dsh-matrix 是
|
|
12
|
+
「DSH 进程内 Cordis 插件」;移植的是层内逻辑,不是进程模型。
|
|
13
|
+
|
|
14
|
+
主要落点(文件头注释里逐一标注了上游对应文件):
|
|
15
|
+
|
|
16
|
+
| 本仓库 | 上游来源 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `src/matrix/transport.ts` | `src/transports/matrix.ts`、`src/matrix-utils.ts`、`src/matrix-rooms.ts` |
|
|
19
|
+
| `src/matrix/matrix-events.ts` | pi-courier 事件翻译模块 |
|
|
20
|
+
| `src/auth/challenge.ts` | `src/challenge-auth.ts` |
|
|
21
|
+
| `src/auth/admin-commands.ts` | `src/auth/admin-commands.ts` |
|
|
22
|
+
| `src/auth/store.ts` | pi-courier 认证存储 |
|
|
23
|
+
| `src/bridge/attachments-ledger.ts` | pi-courier 附件账本 |
|
|
24
|
+
| `src/bridge/quote-cache.ts` | `src/quote-cache.ts` |
|
|
25
|
+
| `src/bridge/pmctl.ts`、`src/bridge/projects.ts` | pi-courier 多项目管理 |
|
|
26
|
+
| `src/cli/systemd.ts`、`src/cli/systemd-hint.ts` | pi-courier 部署脚本 |
|
|
27
|
+
| `src/logger.ts` | `src/logger.ts` |
|
|
28
|
+
|
|
29
|
+
## tintinweb/pi-messenger-bridge — https://github.com/tintinweb/pi-messenger-bridge
|
|
30
|
+
|
|
31
|
+
Copyright (c) tintinweb — MIT。
|
|
32
|
+
|
|
33
|
+
这是更上游的项目:pi-courier 的 Matrix 传输层与挑战认证源自它,dsh-matrix 又从
|
|
34
|
+
pi-courier 承接了这一层。在此一并致谢。
|
|
35
|
+
|
|
36
|
+
## 依赖
|
|
37
|
+
|
|
38
|
+
运行时依赖(`matrix-bot-sdk`、`@matrix-org/matrix-sdk-crypto-nodejs`、
|
|
39
|
+
`@deepseek-ai/*`)各自遵循其自身许可,见 `package.json` 与
|
|
40
|
+
`node_modules/<包>/LICENSE`。
|
|
41
|
+
|
|
42
|
+
## 头像资源(assets/avatars/)
|
|
43
|
+
|
|
44
|
+
`assets/avatars/` 下的 13 张 PNG(`instance-01.png` … `instance-12.png` +
|
|
45
|
+
`management.png`)来自上游 [pi-courier](https://github.com/Hi-Barry/pi-courier)(MIT)
|
|
46
|
+
的 `assets/avatars/`,是上游原创的 AI 生成美术(candy / 棉花糖动物风格,512×512)。
|
|
47
|
+
|
|
48
|
+
本项目把它们**等比缩到 128×128 后入库**:上游 13 张合计约 6.4MB,而本包(不含图)才
|
|
49
|
+
140KB,原样入库会让包体积涨约 47 倍,且二进制一旦进 git 历史就无法回收。缩放由
|
|
50
|
+
`scripts/downscale-avatars.mjs` 完成(只依赖 node 内置 zlib,可复现),源目录是只读的
|
|
51
|
+
上游克隆,不入本仓库。
|
|
52
|
+
|
|
53
|
+
想换成自己的图:同名覆盖 `assets/avatars/` 下的文件即可,不需要改代码。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dsh-matrix bundle patch:插入桥插件行。
|
|
2
|
+
# 凭据与偏好由 profile 用户层按 id `matrix-bridge` 覆盖 config(spike 实测的挂层方式)。
|
|
3
|
+
- insert:
|
|
4
|
+
# 提问进房间的模型侧开关:桥 profile 挂载 ask_user_question 工具
|
|
5
|
+
# (userQuestions 服务行由 base 提供,这里只补工具)
|
|
6
|
+
- id: tool-ask-user
|
|
7
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
8
|
+
|
|
9
|
+
- id: matrix-bridge
|
|
10
|
+
name: '@barryfan2045/dsh-matrix'
|
|
11
|
+
config:
|
|
12
|
+
# 全部可被 profile 层覆盖;这里只放安全默认值
|
|
13
|
+
logLevel: info
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T2: bridge admin commands (/trusted, /revoke) - pure functions ported in
|
|
3
|
+
* spirit from pi-courier src/auth/admin-commands.ts (MIT).
|
|
4
|
+
*
|
|
5
|
+
* parse turns text into a command value; execute mutates only through the
|
|
6
|
+
* injected AuthStore and returns the one room-facing reply string. The admin
|
|
7
|
+
* gate lives here (the bridge diverges from pi-courier's trusted-only rule:
|
|
8
|
+
* these two commands are admin-only, others get a permission notice).
|
|
9
|
+
*
|
|
10
|
+
* Scope note: /revoke only touches the RUNTIME trust list; users seeded from
|
|
11
|
+
* the row-config snapshot must be removed from the config itself (T10 ops).
|
|
12
|
+
*/
|
|
13
|
+
import type { AuthStore } from './store.ts';
|
|
14
|
+
export type AdminCommand = {
|
|
15
|
+
name: 'trusted';
|
|
16
|
+
} | {
|
|
17
|
+
name: 'revoke';
|
|
18
|
+
target?: string;
|
|
19
|
+
} | {
|
|
20
|
+
name: 'channels';
|
|
21
|
+
} | {
|
|
22
|
+
name: 'enable';
|
|
23
|
+
roomId?: string;
|
|
24
|
+
mode?: string;
|
|
25
|
+
} | {
|
|
26
|
+
name: 'disable';
|
|
27
|
+
roomId?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function adminCommandNames(): string[];
|
|
30
|
+
/** Match a message against the bridge admin command set (first token, case-insensitive). */
|
|
31
|
+
export declare function parseAdminCommand(text: string): AdminCommand | undefined;
|
|
32
|
+
export interface AdminExecDeps {
|
|
33
|
+
store: AuthStore;
|
|
34
|
+
/** Who sent the command (raw MXID). */
|
|
35
|
+
sender: string;
|
|
36
|
+
/** Admin decision, injected by the pipeline (store admin ?? static first). */
|
|
37
|
+
isAdmin: (userId: string) => boolean;
|
|
38
|
+
/** Trust decision (T8: /enable is trusted-level). Defaults to isAdmin. */
|
|
39
|
+
isTrusted?: (userId: string) => boolean;
|
|
40
|
+
/** Room the command came from (T8: current-room /enable target). */
|
|
41
|
+
roomId?: string;
|
|
42
|
+
/** Host-side chatter hook (defaults to no-op). */
|
|
43
|
+
notify?: (message: string) => void;
|
|
44
|
+
}
|
|
45
|
+
/** Run a parsed admin command; returns the text to send back to the room. */
|
|
46
|
+
export declare function executeAdminCommand(cmd: AdminCommand, deps: AdminExecDeps): string;
|
|
47
|
+
//# sourceMappingURL=admin-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-commands.d.ts","sourceRoot":"","sources":["../../src/auth/admin-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAA;AAExD,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AASxC,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAiBxE;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAA;IAChB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACpC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACvC,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kDAAkD;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CA8ClF"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const COMMAND_NAMES = new Set(['trusted', 'revoke', 'channels', 'enable', 'disable']);
|
|
2
|
+
const CHANNEL_MODES = ['all', 'mentions', 'trusted-only'];
|
|
3
|
+
function isChannelMode(value) {
|
|
4
|
+
return value !== undefined && CHANNEL_MODES.includes(value);
|
|
5
|
+
}
|
|
6
|
+
export function adminCommandNames() {
|
|
7
|
+
return [...COMMAND_NAMES];
|
|
8
|
+
}
|
|
9
|
+
/** Match a message against the bridge admin command set (first token, case-insensitive). */
|
|
10
|
+
export function parseAdminCommand(text) {
|
|
11
|
+
const parts = text.trim().split(/\s+/);
|
|
12
|
+
const head = (parts[0] ?? '').toLowerCase();
|
|
13
|
+
if (head === '/trusted')
|
|
14
|
+
return { name: 'trusted' };
|
|
15
|
+
if (head === '/revoke') {
|
|
16
|
+
return parts[1] !== undefined ? { name: 'revoke', target: parts[1] } : { name: 'revoke' };
|
|
17
|
+
}
|
|
18
|
+
if (head === '/channels')
|
|
19
|
+
return { name: 'channels' };
|
|
20
|
+
if (head === '/enable') {
|
|
21
|
+
// Two args → explicit target room; one arg → the current room's mode.
|
|
22
|
+
if (parts.length >= 3)
|
|
23
|
+
return { name: 'enable', roomId: parts[1], mode: parts[2] };
|
|
24
|
+
return { name: 'enable', roomId: undefined, mode: parts[1] };
|
|
25
|
+
}
|
|
26
|
+
if (head === '/disable') {
|
|
27
|
+
return parts[1] !== undefined ? { name: 'disable', roomId: parts[1] } : { name: 'disable' };
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Run a parsed admin command; returns the text to send back to the room. */
|
|
32
|
+
export function executeAdminCommand(cmd, deps) {
|
|
33
|
+
const isTrusted = deps.isTrusted ?? deps.isAdmin;
|
|
34
|
+
// T8 group commands keep their own permission ladder: /enable is
|
|
35
|
+
// trusted-level (spec US32), /disable and /channels stay admin-only.
|
|
36
|
+
if (cmd.name === 'enable') {
|
|
37
|
+
if (!isTrusted(deps.sender))
|
|
38
|
+
return `⛔ 权限不足:/enable 仅 trusted 用户可用。`;
|
|
39
|
+
return executeEnable(cmd, deps);
|
|
40
|
+
}
|
|
41
|
+
if (cmd.name === 'disable' || cmd.name === 'channels') {
|
|
42
|
+
if (!deps.isAdmin(deps.sender))
|
|
43
|
+
return `⛔ 权限不足:/${cmd.name} 仅管理员可用。`;
|
|
44
|
+
return cmd.name === 'channels' ? executeChannels(deps) : executeDisable(cmd, deps);
|
|
45
|
+
}
|
|
46
|
+
if (!deps.isAdmin(deps.sender)) {
|
|
47
|
+
return `⛔ 权限不足:/${cmd.name} 仅管理员可用。`;
|
|
48
|
+
}
|
|
49
|
+
if (cmd.name === 'trusted') {
|
|
50
|
+
const state = deps.store.get();
|
|
51
|
+
if (state.trustedUsers.length === 0)
|
|
52
|
+
return '信任名单为空(还没有人通过挑战认证)。';
|
|
53
|
+
const lines = state.trustedUsers.map((id) => `• ${id}${id === state.adminUserId ? '(管理员)' : ''}`);
|
|
54
|
+
return [`信任名单 (${state.trustedUsers.length}):`, ...lines].join('\n');
|
|
55
|
+
}
|
|
56
|
+
// /revoke
|
|
57
|
+
const target = cmd.target;
|
|
58
|
+
if (!target)
|
|
59
|
+
return '用法: /revoke <mxid>';
|
|
60
|
+
const state = deps.store.get();
|
|
61
|
+
const index = state.trustedUsers.indexOf(target);
|
|
62
|
+
if (index === -1)
|
|
63
|
+
return `❌ ${target} 不在信任名单(运行时名单只收录通过挑战认证的用户;配置里的静态用户请改配置文件)。`;
|
|
64
|
+
state.trustedUsers.splice(index, 1);
|
|
65
|
+
if (state.adminUserId === target) {
|
|
66
|
+
// Revoke demotes: the earliest remaining runtime-trusted user takes over,
|
|
67
|
+
// otherwise fall back to the pipeline's static-list admin rule.
|
|
68
|
+
const next = state.trustedUsers[0];
|
|
69
|
+
if (next !== undefined)
|
|
70
|
+
state.adminUserId = next;
|
|
71
|
+
else
|
|
72
|
+
delete state.adminUserId;
|
|
73
|
+
}
|
|
74
|
+
deps.store.set(state);
|
|
75
|
+
deps.notify?.(`Revoked: ${target}`);
|
|
76
|
+
return `🔓 已回收 ${target} 的信任。`;
|
|
77
|
+
}
|
|
78
|
+
// ── T8: group room toggles ─────────────────────────────────────────────
|
|
79
|
+
function executeEnable(cmd, deps) {
|
|
80
|
+
const usage = '用法: /enable <all|mentions|trusted-only>(本房间)或 /enable <roomId> <模式>(DM)';
|
|
81
|
+
// Three-arg form carries an explicit room; one-arg form targets the room
|
|
82
|
+
// the command was sent in (needs the pipeline to supply deps.roomId).
|
|
83
|
+
const roomId = cmd.roomId ?? deps.roomId;
|
|
84
|
+
if (!roomId)
|
|
85
|
+
return usage;
|
|
86
|
+
if (!isChannelMode(cmd.mode))
|
|
87
|
+
return usage;
|
|
88
|
+
const state = deps.store.get();
|
|
89
|
+
state.channels[roomId] = { enabled: true, mode: cmd.mode };
|
|
90
|
+
deps.store.set(state);
|
|
91
|
+
deps.notify?.(`Enabled channel ${roomId} (${cmd.mode})`);
|
|
92
|
+
return `✅ ${cmd.roomId ? `房间 ${roomId}` : '本房间'}已启用 (mode: ${cmd.mode})`;
|
|
93
|
+
}
|
|
94
|
+
function executeDisable(cmd, deps) {
|
|
95
|
+
const roomId = cmd.roomId;
|
|
96
|
+
if (!roomId)
|
|
97
|
+
return '用法: /disable <roomId>';
|
|
98
|
+
const state = deps.store.get();
|
|
99
|
+
const entry = state.channels[roomId];
|
|
100
|
+
if (!entry || !entry.enabled)
|
|
101
|
+
return `❌ 房间 ${roomId} 没有启用,无需停用。`;
|
|
102
|
+
entry.enabled = false;
|
|
103
|
+
deps.store.set(state);
|
|
104
|
+
deps.notify?.(`Disabled channel ${roomId}`);
|
|
105
|
+
return `🚫 已停用 ${roomId}(mode 保留,可再 /enable)。`;
|
|
106
|
+
}
|
|
107
|
+
function executeChannels(deps) {
|
|
108
|
+
const channels = deps.store.get().channels;
|
|
109
|
+
const lines = Object.entries(channels).map(([id, c]) => `• ${id}: ${c.enabled ? '✅' : '❌'} (${c.mode})`);
|
|
110
|
+
return lines.length > 0 ? ['群房间策略:', ...lines].join('\n') : '还没有配置任何群房间(默认全部沉默)。';
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=admin-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-commands.js","sourceRoot":"","sources":["../../src/auth/admin-commands.ts"],"names":[],"mappings":"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"}
|