dsh-supercrew 0.0.0-stage → 0.1.4

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/DESIGN.md ADDED
@@ -0,0 +1,125 @@
1
+ # 设计说明
2
+
3
+ 这份文件讲**为什么这么做**,不讲怎么用(用法看 [README.md](README.md),装机看 [INSTALL.md](INSTALL.md))。
4
+
5
+ ## 起点:官方原型要什么,我们缺什么
6
+
7
+ `deepseek-ai/dsh-superagent-prototype` 的形态是:
8
+
9
+ - **router / worker 分离**:router 只沟通、拆活;worker 拿独立上下文干活
10
+ - **work item**:每个请求开一张工单,状态可查
11
+ - **常驻**:用 tmux 守着进程,靠 gh CLI 收发 GitHub 消息
12
+ - **隔离**:独立的 DSH profile、独立端口、独立状态目录
13
+
14
+ 前两条是**机制**,后两条是**部署方式**。机制值得抄,部署方式在 Windows 上全是坎(tmux 没有、gh 要单独装、还要一个专用 GitHub 账号)。
15
+
16
+ 所以 supercrew 的做法是:**抄机制,换部署**。
17
+
18
+ ## 三个核心取舍
19
+
20
+ ### 1. worker = 一次性的 headless 进程,不是常驻守护
21
+
22
+ ```js
23
+ spawn(<dsh exe>, ['--profile', 'headless', '--patch', patch, '--permission-mode',
24
+ 'danger-full-access', '--approval-policy', 'never', 任务])
25
+ ```
26
+
27
+ `dsh --profile headless "<任务>"` 的设计就是「答一个任务、把结果打到 stdout、退出」。这正好是 worker 需要的语义:
28
+
29
+ | 需求 | headless 给什么 |
30
+ |---|---|
31
+ | 独立上下文 | 每次全新进程,天然互不污染 |
32
+ | 能干活 | 能读写文件、跑命令(`danger-full-access`) |
33
+ | 不等人 | `--approval-policy never` |
34
+ | 有结果 | stdout = 结果,stderr = 诊断 |
35
+ | 不常驻 | 跑完自退,没有要守的进程 |
36
+
37
+ **换来什么**:不需要 tmux,不需要看门狗管 worker 生命周期,进程崩了就是一次失败的工单而不是一个需要复活的服务。
38
+ **代价**:每次都重建上下文(慢几秒、token 多花一点),且没法跨工单复用会话。
39
+
40
+ ### 2. 工单 = 一个 JSON 文件,不是数据库
41
+
42
+ ```
43
+ <CREW_HOME>/tickets/<id>.json 工单本身(状态、结果、错误、来源)
44
+ <CREW_HOME>/runs/<id>.attemptN.log 每次尝试的完整输出
45
+ <CREW_HOME>/seen.json QQ 侧的去重表
46
+ ```
47
+
48
+ **为什么不上 SQLite**:工单的量级是「一天几十张」,不是「一秒几十条」。文件的好处是人能直接看、能改、能删、`git diff` 得出来、坏了只坏一张。
49
+ **代价**:并发写要自己保护 —— 见下一条。
50
+
51
+ ### 3. 并发靠文件锁,不靠队列服务
52
+
53
+ 真正的并发风险有两个:
54
+
55
+ - 看板连点两下 / CLI 和看板同时按 → **同一张工单起两个 worker**(token 双倍,日志互相覆盖)
56
+ - 两张 `queued` 工单同时开跑 → 抢同一个工作目录
57
+
58
+ 做法:`acquireLock()` 用 `fs.mkdirSync` 的原子性抢锁(`.` 开头的锁文件写成 `<lock>/owner` 存 pid):
59
+
60
+ ```
61
+ 抢到了 → 干活 → 释放
62
+ 没抢到 → 看 owner 里的 pid 还活着吗
63
+ 活着 → 让给它,本次不干
64
+ 死了 → 是陈旧锁,抢过来(上次进程被强杀留下的)
65
+ ```
66
+
67
+ 同一把锁也保护「读工单 → 改状态 → 写回去」这一段,所以不会有最后写者赢的问题。
68
+
69
+ **为什么不用现成的锁库**:worker 需要一个「崩了也能自愈」的语义(pid 探活 + 抢陈旧锁),普通锁文件做不到,自己写反而更短。
70
+
71
+ ## 数据流
72
+
73
+ ```
74
+ ┌──────────────── 入口(可插拔)────────────────┐
75
+ │ QQ 群/私聊(crew-watch) 看板(panel) 命令行(bin)│
76
+ └───────────────────┬───────────────────────────┘
77
+ │ 一段人话
78
+ ┌──────▼──────┐
79
+ │ 门卫 router │ 是不是活?能不能写清楚?
80
+ │ (便宜模型)│ → {isTask, title, body}
81
+ └──────┬──────┘
82
+ │ 是活
83
+ ┌──────▼──────┐
84
+ │ 内核 crew │ 开单 → 抢锁 → 建 runs 日志
85
+ └──────┬──────┘
86
+ │
87
+ ┌───────────▼───────────┐
88
+ │ worker = headless 进程 │ 独立上下文,在 --cwd 里干活
89
+ └───────────┬───────────┘
90
+ │ stdout / stderr
91
+ ┌──────▼──────┐
92
+ │ 写回工单 │ state=done|failed, result, error
93
+ └──────┬──────┘
94
+ │
95
+ ┌──────▼──────┐
96
+ │ 回到入口 │ 发回原会话 / 看板刷新
97
+ └─────────────┘
98
+ ```
99
+
100
+ ## 模型分层
101
+
102
+ | 角色 | 谁 | 为什么 |
103
+ |---|---|---|
104
+ | 门卫 | 便宜/快的模型 | 高频小额,只输出一个 JSON |
105
+ | worker | 你当前在用的强模型 | 真干活,要求理解力 |
106
+
107
+ 两者默认用**同一个 provider**(`crew-main`),免费池(`crew-free`)是备选。免费池不适合当主力:它会被上游限流,而且降级链可能落到本机模型上——本机模型抢显存,撞上画图任务会 503。
108
+
109
+ **别在模型层写 `reasoningEffort`**:上游没声明支持档位时会直接报错起不来。
110
+
111
+ ## 已知限制
112
+
113
+ - **没有跨工单记忆**:worker 每次都从零开始,上一张工单学到的东西不会带给下一张。(要这个得接长期记忆库,属于下一步)
114
+ - **worker 是全权权限**:它能改你机器上的文件。这是无人值守的代价,别把 `--cwd` 指到整个盘。
115
+ - **画图类任务容易卡**:worker 起来浏览器截图后有时自己不退,工单会一直挂在「working」。现在有 10 分钟超时强杀(`CREW_TIMEOUT_MS` 可调),产出会留在 `--cwd` 里;但这类活最好还是交给专门的服务做。
116
+ - **门卫会误判**:「你能帮我写道题吗」这种口语活,早期版本判错过;现在靠重试 + 换模型兜底,但仍然不是 100%。
117
+ - **QQ 正文有 200 字上限**:桥那边截的,超了收不到全文。
118
+ - **回传有内容闸门**:桥会拦带盘符的路径和凭据赋值,所以结果里的路径会被替换成「(本机文件)」。
119
+ - **看板没有鉴权**:只监听 `127.0.0.1`,别改成 `0.0.0.0`。
120
+
121
+ ## 下一步
122
+
123
+ - 把工单挂进长期记忆(同一类活第二次做能快)
124
+ - GitHub 入口(issue → 工单 → PR)
125
+ - 验收环节(干完等一次点头,而不是直接算完成)
package/INSTALL.md ADDED
@@ -0,0 +1,211 @@
1
+ # 安装说明
2
+
3
+ 给装到一台新机器上的人看。**全程不需要 tmux、不需要 gh CLI、不需要另建 GitHub 账号。**
4
+
5
+ ---
6
+
7
+ ## 一、前提
8
+
9
+ | 要什么 | 说明 |
10
+ |---|---|
11
+ | DeepSeek Harness | 桌面版或 CLI 都行,**0.2.2-rc.1 及以上**(`dsh --version` 看一眼) |
12
+ | Node.js | **>= 22.19.0**(`node -v` 看一眼) |
13
+ | 一个模型 | 任意 OpenAI 兼容接口:官方 DeepSeek、本机网关、随便哪家中转都行 |
14
+
15
+ `dsh` 命令在哪:桌面版自带,路径形如
16
+ `<安装目录>\resources\runtime\cli\bin\dsh.cmd`。
17
+ 下文用 `dsh` 代指它。
18
+
19
+ ---
20
+
21
+ ## 二、装
22
+
23
+ 把这份代码放到任意目录(下称 `<插件目录>`,例如 `D:\crew`)。
24
+
25
+ **A. 从本地目录装**(拿到的是源码压缩包时用这个)
26
+
27
+ ```bat
28
+ dsh plugin --profile desktop add link:<插件目录>
29
+ ```
30
+
31
+ **B. 从 npm 装**(发布之后)
32
+
33
+ ```bat
34
+ dsh plugin --profile desktop add dsh-supercrew
35
+ ```
36
+
37
+ **C. 从 git 装**
38
+
39
+ ```bat
40
+ dsh plugin --profile desktop add github:<你的用户名>/dsh-supercrew
41
+ ```
42
+
43
+ > `--profile desktop` 是主 Web 界面。想装到别的 profile 就把 desktop 换成那个名字。
44
+
45
+ 装完**重启一次 DSH**。
46
+
47
+ 确认装上了:
48
+
49
+ ```bat
50
+ dsh desktop --dump-config
51
+ ```
52
+
53
+ 输出里能看到这两行就对了:
54
+
55
+ ```
56
+ # == dsh-supercrew
57
+ - id: supercrew
58
+ name: dsh-supercrew
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 三、配 worker 的模型
64
+
65
+ **只有一件事必须改:worker 用哪个模型。**
66
+
67
+ 编辑 `<插件目录>\worker-models.yml`。默认配的是作者自己的手机网关,你多半用不了,改成你能用的那个:
68
+
69
+ ```yaml
70
+ - id: agent-default-model
71
+ config:
72
+ provider: crew-main
73
+ model: <你的模型 id> # ← 改这里
74
+ - id: llm-pi-ai
75
+ config:
76
+ providers:
77
+ crew-main:
78
+ baseURL: <你的接口地址>/v1 # ← 改这里,比如 https://api.deepseek.com/v1
79
+ apiKeyEnv: DEEPSEEK_API_KEY # ← key 从哪个环境变量取(见下)
80
+ ```
81
+
82
+ **关于 `apiKeyEnv`**:它填的是**环境变量的名字**,不是 key 本身。程序会去读那个环境变量当作 Bearer token。比如填 `DEEPSEEK_API_KEY`,启动前要:
83
+
84
+ ```bat
85
+ set DEEPSEEK_API_KEY=sk-xxxx
86
+ ```
87
+
88
+ 用本机网关(不校验 key)时,随便填个存在的变量名(默认的 `USERNAME` 是 Windows 自带的)。
89
+
90
+ 也可以不改文件,用环境变量覆盖:
91
+
92
+ ```bat
93
+ set CREW_MAIN_URL=http://127.0.0.1:8000/v1
94
+ set CREW_MAIN_KEY_ENV=MY_KEY
95
+ ```
96
+
97
+ **三条别踩:**
98
+
99
+ 1. **不要写 `reasoningEffort`** —— 上游没声明支持的档位时会报 `UNSUPPORTED_REASONING_EFFORT` 直接起不来。
100
+ 2. **`baseURL` 不要用会降级到本地模型的网关** —— 本地模型抢显存,撞上画图任务会 503。
101
+ 3. **别把 key 写进 yml** —— 那会被一起提交/发布出去。
102
+
103
+ ### 还需要一个 `headless` profile
104
+
105
+ worker 是用 `dsh --profile headless "<任务>"` 跑的(跑完自退,不需要 tmux)。
106
+
107
+ 第一次用会自动创建;如果是干净的机器上没有它,先跑一次:
108
+
109
+ ```bat
110
+ dsh --profile headless "回复两个字:收到"
111
+ ```
112
+
113
+ 看到 `收到` 就成了。要是报缺 profile,用官方模板重建一个:
114
+
115
+ ```bat
116
+ dsh headless --from-default-profile web
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 四、用
122
+
123
+ ```bat
124
+ supercrew panel # 看板 → http://127.0.0.1:3090/
125
+ supercrew add "把 D:\temp 的图片按日期归档" --cwd D:\work
126
+ supercrew run # 派出去
127
+ supercrew list # 看工单
128
+ supercrew requeue # 把卡在「在干」的死工单放回队列
129
+ ```
130
+
131
+ 命令行入口没进 PATH 的话,直接调:
132
+
133
+ ```bat
134
+ node <插件目录>\bin\supercrew.mjs list
135
+ ```
136
+
137
+ 自检(开一张工单并跑掉,约 30 秒):
138
+
139
+ ```bat
140
+ supercrew selftest
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 五、接 QQ(可选)
146
+
147
+ 接 SnowLuma 的 DSH 桥,就能从 QQ 群/私聊收需求。
148
+
149
+ 1. 确认桥的控制台在跑(默认 `http://127.0.0.1:3100`)。
150
+ 2. 确认令牌文件在(默认 `<桥目录>\state\console-token`,用 `CREW_TOKENFILE` 改)。
151
+ 3. 起值守:
152
+
153
+ ```bat
154
+ supercrew watch
155
+ ```
156
+
157
+ 它每 15 秒扫一遍会话,有人 @ 你并提了具体活,就走:
158
+ **门卫判断 → 开工单 → 起独立 worker 干活 → 结果发回原会话**。
159
+
160
+ 触发词默认 `@真白|@椎名真白|干活|派活|帮我做`,**改成你自己的昵称**:
161
+
162
+ ```bat
163
+ set CREW_TRIGGER=@你的昵称|干活
164
+ ```
165
+
166
+ ---
167
+
168
+ ## 六、开机自启(Windows)
169
+
170
+ 包里的 `crew_guard.ps1` 每 5 分钟看一眼:看板和值守都不在就拉起来。
171
+
172
+ ```powershell
173
+ $g = '<插件目录>\crew_guard.ps1'
174
+ $a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -WindowStyle Hidden -File `"$g`""
175
+ $p = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
176
+ Register-ScheduledTask -TaskName 'SuperCrewGuard' -Action $a `
177
+ -Trigger (New-ScheduledTaskTrigger -AtLogOn) -Principal $p -Force
178
+ ```
179
+
180
+ > `-RunLevel Highest` 必须配 `New-ScheduledTaskPrincipal` 一起用,单独写会报「用户名或安全标识符没有任何映射完成」。
181
+
182
+ Linux/macOS 上换成 cron 或 systemd,看一眼 `crew_guard.ps1` 的内容照抄逻辑即可。
183
+
184
+ ---
185
+
186
+ ## 七、安全说明(先读再用)
187
+
188
+ worker 是以 **`--permission-mode danger-full-access --approval-policy never`** 跑的:
189
+ 它能读写你的文件、执行命令,**不会中途问你**。
190
+
191
+ - 这是故意的 —— 无人值守的工单没法等人点确认。
192
+ - 所以:**别把工单目录设成整个盘**(用 `--cwd` 指到一个项目目录),**别把触发词设成广到人人可用**。
193
+ - 要高隔离就在专门的 OS 账号里跑。
194
+
195
+ ---
196
+
197
+ ## 八、常见问题
198
+
199
+ **装上没反应?** 重启 DSH,再 `dsh desktop --dump-config` 看有没有 `# == dsh-supercrew`。
200
+
201
+ **看板打不开?** `netstat -ano | findstr 3090`,没在听就 `supercrew panel`;换端口用 `supercrew panel --port 3091`(或 `--port=3091`)。
202
+
203
+ **worker 老是失败?** 看运行日志:`<CREW_HOME>\runs\<工单号>.attempt1.log`,里面是 worker 的完整报错。
204
+
205
+ **工单一直卡在「在干」?** worker 被强杀过。`supercrew requeue` 把它放回队列(默认只处理卡超过 30 分钟的)。
206
+
207
+ **门卫把闲聊判成活了?** 看 `supercrew judge` 的输出,它会把每条判断的理由打出来;触发词在环境变量 `CREW_TRIGGER`,判断的提示词在 `lib/router.mjs`。
208
+
209
+ **结果发不回 QQ?** 看工单里的 `replyError` 字段。最常见两种:桥的内容闸门拦了本机路径(已自动替换),或者消息超过 500 字(已自动截断)。
210
+
211
+ **装到别的 profile 上不生效?** 检查 `package.json` 里的 `dsh.bundle.patch` 路径对不对。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dafeiyu
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 CHANGED
@@ -1,3 +1,138 @@
1
- # Temporary Holding Version
1
+ # dsh-supercrew —— 给 DSH 装一个「团队」
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 把「收需求 → 判断 → 开工单 → 派活 → 回报」这套流程做成一个能装到任何 DSH 上的东西。
4
+
5
+ **一句话**:单会话 agent 是一个人在干所有事;supercrew 让它变成一个小队——拆活、派人、验收、留痕。
6
+
7
+ ## 它解决什么
8
+
9
+ 单会话 agent 的老问题:一个上下文干所有事,聊着聊着被杂事冲散,长任务容易断,干过的活查不到。
10
+
11
+ supercrew 把它拆开:
12
+
13
+ - **router(门卫)** —— 判断一句话是「活」还是「闲聊」,顺手把口语需求补成可执行的指令
14
+ - **worker** —— 每张工单起一个**独立的 DSH 会话**去干,互不污染,干完只交结果
15
+ - **工单** —— 状态落文件,看得见、可重试、有完整运行日志
16
+ - **入口** —— QQ 群/私聊、网页面板、命令行,可插拔
17
+ - **看板** —— 浏览器里看待办 / 在干 / 已完成 / 失败
18
+
19
+ ## 为什么不需要 tmux 和 gh
20
+
21
+ 官方原型(`deepseek-ai/dsh-superagent-prototype`)靠 tmux 守护进程、靠 gh CLI 收发消息,Windows 上两样都难搞。这里全部绕开:
22
+
23
+ | 官方原型要 | supercrew 用 |
24
+ |---|---|
25
+ | tmux 守护 | `dsh --profile headless "任务"` —— 一次性进程,跑完自退 |
26
+ | gh CLI | 直接 HTTP 调 GitHub API(要接 GitHub 的话) |
27
+ | 专用 GitHub 账号 | 入口做成可插拔,接哪个渠道都行 |
28
+ | 独立 superagent profile | 复用现有的 `headless` profile + 一个 patch 层 |
29
+
30
+ ## 组成
31
+
32
+ | 文件 | 干什么 |
33
+ |---|---|
34
+ | `crew.mjs` | 内核:工单存储 + 文件锁 + 派工(起 headless worker) |
35
+ | `lib/router.mjs` | 门卫:判断是不是活、把需求写清楚 |
36
+ | `crew-watch.mjs` | 入口:从 QQ 会话里捞需求、把结果发回去 |
37
+ | `panel.mjs` | 看板:本地网页 |
38
+ | `bin/supercrew.mjs` | 命令行入口(`supercrew panel / watch / add / run / list / requeue`) |
39
+ | `lib/index.js` | 插件入口:装进 DSH 后自动起看板 |
40
+ | `worker-models.yml` | worker 和门卫用哪些模型(默认走当前在用的,免费池当备选) |
41
+ | `skills/team-dispatch/` | 配套 skill:教当前会话「大活先拆」的路数 |
42
+ | `crew_guard.ps1` | Windows 看门狗(看板/值守挂了自动拉起) |
43
+
44
+ ## 快速开始
45
+
46
+ ```bash
47
+ # 开一张工单
48
+ node crew.mjs add "把 D:\temp 下的图片按日期归档到子文件夹" --cwd D:\work
49
+
50
+ # 派出去(起一个独立 DSH 会话干活)
51
+ node crew.mjs run
52
+
53
+ # 看有哪些单子
54
+ node crew.mjs list
55
+
56
+ # 打开看板
57
+ node panel.mjs # → http://127.0.0.1:3090/
58
+ ```
59
+
60
+ 装成插件之后,这些都能用 `supercrew add / run / list / panel` 代替。完整步骤见 [INSTALL.md](INSTALL.md)。
61
+
62
+ ## 从 QQ 收需求(可选)
63
+
64
+ ```bash
65
+ node crew-watch.mjs scan # 看看有哪些人 @ 了我
66
+ node crew-watch.mjs judge # 让门卫过一遍,看哪些是活(不真开单,也不吃掉消息)
67
+ node crew-watch.mjs once # 真开单 + 派活
68
+ node crew-watch.mjs loop # 一直守着,来活就干,干完把结果发回原会话
69
+ ```
70
+
71
+ 触发词默认 `@真白|@椎名真白|干活|派活|帮我做`,**换成你自己的**:`set CREW_TRIGGER=@你的昵称|干活`。
72
+
73
+ ## 环境变量
74
+
75
+ | 变量 | 默认 | 说明 |
76
+ |---|---|---|
77
+ | `CREW_HOME` | `~/.dsh-supercrew` | 工单和运行日志放这儿 |
78
+ | `CREW_DSH` | 自动找 | `dsh` **可执行文件**的路径(不是目录) |
79
+ | `CREW_PATCH` | `<本目录>/worker-models.yml` | worker 的模型层 |
80
+ | `CREW_WORKER_PROFILE` | `headless` | worker 用哪个 profile |
81
+ | `CREW_MODEL` | 空 | 覆盖 worker 的模型 id(会临时生成一份 patch) |
82
+ | `CREW_MAX_OUT` | `1048576` | worker 输出上限(字节),超了截断 |
83
+ | `CREW_TIMEOUT_MS` | `600000` | worker 超过这个时间没结束就强杀整棵进程树(默认 10 分钟) |
84
+ | `CREW_WORKDIR` | 当前目录 | worker 默认在哪儿干活 |
85
+ | `CREW_GATEWAY` | `http://192.168.1.35:17890/v1` | 门卫问哪个网关(**换机器要改**) |
86
+ | `CREW_ROUTER_MODEL` | `DeepSeek-V4.1-Flash` | 门卫用哪个模型(高频小额调用) |
87
+ | `CREW_ROUTER_FALLBACKS` | `GLM-5.3-Flash` | 主模型不给 JSON 时换谁 |
88
+ | `CREW_ROUTER_TIMEOUT_MS` | `25000` | 单次判断超时 |
89
+ | `CREW_SOCIAL` | `D:\sf\surfcore-main\state\social-v2.json` | QQ 会话状态文件(**换机器要改**) |
90
+ | `CREW_TOKENFILE` | `D:\sf\surfcore-main\state\console-token` | 桥的控制台令牌(**换机器要改**) |
91
+ | `CREW_API` | `http://127.0.0.1:3100` | QQ 桥控制台 |
92
+ | `CREW_TRIGGER` | `@真白\|@椎名真白\|干活\|派活\|帮我做` | 什么话算「在叫我」 |
93
+ | `CREW_PANEL_PORT` | `3090` | 看板端口 |
94
+ | `CREW_MAIN_URL` / `CREW_MAIN_KEY_ENV` / `CREW_FREE_URL` | 见 `worker-models.yml` | 覆盖模型层的地址与 key 变量名 |
95
+
96
+ ## 和 DSH 自带能力的关系
97
+
98
+ DSH 本来就有「派活」的原语:`subagent`(1~2 张工单)、`workflow`(5 张以上、要阶段结果)。supercrew 补的是它们外面那圈:
99
+
100
+ - **该不该分工的判断**(不然每件小事都开会,反而更慢)
101
+ - **工单化**(可追溯、可重试、看得见)
102
+ - **入口**(从 QQ 或者网页丢一句人话进来)
103
+
104
+ 配套的 `skills/team-dispatch/` 就是把这套路数写给我自己看的:
105
+ 遇到大活先拆、每张工单自带背景/目标/验收/边界、回来后只汇总结论而不是贴原文。
106
+
107
+ ## 进度
108
+
109
+ - [x] 内核(工单 + 文件锁 + 派工)—— 端到端跑通,目标文件真实生成
110
+ - [x] QQ 入口 —— 实测从 3 个群认出 16 条 @ 我的消息
111
+ - [x] 门卫 —— 16 条真实消息放行 3 条、拦下 12 条(判的是「活 vs 接梗」)
112
+ - [x] 看板 —— 四列、5 秒自刷、能新建能派工
113
+ - [x] 结果自动回群 —— 实测 `sent: 1`
114
+ - [x] 并行派工 —— 靠文件锁串行化,同一张工单不会起两个 worker
115
+ - [x] 包成 DSH 插件 —— `dsh plugin add link:...` 实测装上,`--dump-config` 里能看到
116
+ - [x] 看门狗 —— `crew_guard.ps1` + 计划任务 `SuperCrewGuard`
117
+
118
+ ## 和官方原型的差别
119
+
120
+ 官方那份是研究原型(README 自己写着 "Research prototype for reference only"),重点在 chain-of-recall、投机执行、快慢两级过滤、BYOM 这些论文级机制。
121
+
122
+ supercrew 不照搬那些——给的是**同一套流程的工程实现**,另外加上它没有的东西:长期记忆、免费模型池当大脑、本地模型兜底、以及「什么模型干什么活」的分层。
123
+
124
+ ## 安全
125
+
126
+ worker 以 `--permission-mode danger-full-access --approval-policy never` 跑:能读写文件、执行命令、**不中途问你**。
127
+
128
+ - 无人值守的工单没法等人点确认,这是故意的。
129
+ - 所以 `--cwd` 要指到具体项目目录,别指整个盘;触发词别设得太宽。
130
+ - 要高隔离就在专门的 OS 账号里跑。
131
+
132
+ ## 交流群
133
+
134
+ QQ 群:**1126694040**(DSH / supercrew 的问题都可以在这里问)
135
+
136
+ ## 许可
137
+
138
+ MIT
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * supercrew —— 命令行入口
4
+ *
5
+ * 用法:
6
+ * supercrew panel # 起看板(默认 http://127.0.0.1:3090/)
7
+ * supercrew watch # 守着 QQ,来活就干、干完回话(= crew-watch loop)
8
+ * supercrew add "<需求>" [--cwd <目录>]
9
+ * supercrew run [--all] # 把待办的工单派出去
10
+ * supercrew list # 看工单
11
+ * supercrew requeue # 把卡住的工单放回队列
12
+ * supercrew scan | judge # 只看看群里有什么、门卫怎么判
13
+ * supercrew selftest # 在本目录跑一张真工单,验证装好没有
14
+ *
15
+ * 其它参数原样透传给对应的脚本。
16
+ *
17
+ * 注意两个平台坑(改之前先看):
18
+ * 1) 每个子命令对应的脚本可能还需要一个"子命令名"才能进入正确分支
19
+ * (crew-watch.mjs 认 loop/scan/judge/once/reply,crew.mjs 认 add/run/...)。
20
+ * 漏传就会变成"只打印一行用法然后退出"——watch 不值守就是这么来的。
21
+ * 2) 这个脚本可能被 Electron(DSH 本体)调起,那时 process.execPath 是
22
+ * "DeepSeek Harness.exe"。要加 ELECTRON_RUN_AS_NODE=1 才会被当成 node 跑。
23
+ */
24
+ import { spawn } from 'node:child_process';
25
+ import path from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+
28
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
29
+ const ROOT = path.resolve(HERE, '..');
30
+
31
+ // 子命令 → [脚本文件, 要透传的第一个参数]
32
+ const MAP = {
33
+ panel: ['panel.mjs', null],
34
+ watch: ['crew-watch.mjs', 'loop'],
35
+ work: ['crew.mjs', 'work'],
36
+ add: ['crew.mjs', 'add'],
37
+ run: ['crew.mjs', 'run'],
38
+ list: ['crew.mjs', 'list'],
39
+ show: ['crew.mjs', 'show'],
40
+ requeue: ['crew.mjs', 'requeue'],
41
+ scan: ['crew-watch.mjs', 'scan'],
42
+ judge: ['crew-watch.mjs', 'judge'],
43
+ once: ['crew-watch.mjs', 'once'],
44
+ loop: ['crew-watch.mjs', 'loop'],
45
+ reply: ['crew-watch.mjs', 'reply'],
46
+ selftest: ['selftest.mjs', null],
47
+ };
48
+
49
+ const argv = process.argv.slice(2);
50
+ let cmd = argv[0];
51
+ let extra = argv.slice(1);
52
+
53
+ if (!cmd || cmd === '-h' || cmd === '--help') {
54
+ console.log(`supercrew —— 工单制团队系统
55
+
56
+ supercrew panel 起看板(默认 3090)
57
+ supercrew watch 守着入口收需求、派活、回话
58
+ supercrew work "<活>" 工作区的活:开单并立刻派出去(可加 --cwd <目录>)
59
+ supercrew add "<需求>" 只开单,不派(可加 --cwd <目录>)
60
+ supercrew run [--all] 派工
61
+ supercrew list 看所有工单
62
+ supercrew requeue 把卡住的工单放回队列
63
+ supercrew scan | judge 只看入口有什么 / 门卫怎么判
64
+ supercrew selftest 装完先跑这个:连「worker 能不能正确读写中文」一起测
65
+
66
+ 环境变量见 README。`);
67
+ process.exit(0);
68
+ }
69
+
70
+ if (cmd === 'selftest' && extra.includes('--quick')) {
71
+ // 只查环境与进程,不真派 worker —— 快
72
+ cmd = 'selftest';
73
+ }
74
+
75
+ const entry = MAP[cmd];
76
+ if (!entry) {
77
+ console.error(`不认识这个子命令:${cmd}\n试 supercrew --help`);
78
+ process.exit(2);
79
+ }
80
+
81
+ const [file, sub] = entry;
82
+ const args = [path.join(ROOT, file)];
83
+ if (sub) args.push(sub);
84
+ args.push(...extra);
85
+
86
+ const env = { ...process.env };
87
+ if (process.versions.electron) env.ELECTRON_RUN_AS_NODE = '1';
88
+
89
+ const child = spawn(process.execPath, args, {
90
+ stdio: 'inherit',
91
+ cwd: process.cwd(),
92
+ env,
93
+ });
94
+ child.on('exit', (code) => process.exit(code ?? 0));
@@ -0,0 +1,15 @@
1
+ # dsh-supercrew bundle patch.
2
+ # 一行 insert 就把这套东西挂进 profile —— 装上即用。
3
+ - insert:
4
+ - id: supercrew
5
+ name: 'dsh-supercrew'
6
+ config:
7
+ # 看板端口
8
+ panelPort: 3090
9
+ # 打开插件时是否自动把看板拉起来
10
+ autoPanel: true
11
+ # 工单目录(留空 = 用 $CREW_HOME 或 ~/.dsh-supercrew)
12
+ home: ''
13
+ # worker 用的 profile 和模型层
14
+ workerProfile: headless
15
+ workerPatch: worker-models.yml