@nanmicoder/dsh-agent-teams 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -4
- package/lib/client.js +70 -70
- package/lib/state.js +31 -4
- package/lib/types/state.d.ts +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,20 +13,54 @@ DeepSeek Harness 的 AgentTeams 插件:安装后,任何会话只需一句自
|
|
|
13
13
|
|
|
14
14
|
## 安装
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**前置要求**:已安装 [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness)(`dsh` 命令可用);Node.js `^22.19` 或 `>=24`;pnpm 11。
|
|
17
17
|
|
|
18
18
|
插件已发布到 npm(`@nanmicoder` scope),一条命令装好:
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
|
-
|
|
21
|
+
dsh plugin --profile web add @nanmicoder/dsh-agent-teams
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
`dsh plugin` 会把插件加入 `web` profile,并根据包内的 `dsh.bundle` 声明自动启用它;工具、系统提示和 Web 客户端入口随该 profile 一起加载。
|
|
25
25
|
|
|
26
|
-
建议带版本号安装:`latest` 的解析可能受 registry 缓存影响(国内镜像源同步新版本通常有延迟)。升级时把 `@0.1.1` 换成新版本号即可。
|
|
27
|
-
|
|
28
26
|
> **重启生效**:安装完成后,重启正在运行的 DeepSeek Harness Web 服务并刷新页面。
|
|
29
27
|
|
|
28
|
+
装好后可以用 `dsh plugin --profile web list` 确认插件在列表里。升级用同一条 add 命令即可。
|
|
29
|
+
|
|
30
|
+
### 其他安装方式
|
|
31
|
+
|
|
32
|
+
**指定版本**——`latest` 的解析可能受 registry 缓存影响(国内镜像源同步新版本通常有延迟),想钉死版本时:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
dsh plugin --profile web add @nanmicoder/dsh-agent-teams@<version>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
版本号见 [npm 发布页](https://www.npmjs.com/package/@nanmicoder/dsh-agent-teams?activeTab=versions)。
|
|
39
|
+
|
|
40
|
+
**还没装 DSH,或不想全局安装**——用 `npx` 直接跑,无需预装:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npx -p @deepseek-ai/dsh dsh plugin --profile web add @nanmicoder/dsh-agent-teams
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**从 GitHub 安装**——想用尚未发布到 npm 的最新提交:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
dsh plugin --profile web add github:NanmiCoder/dsh-agent-teams
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**从源码安装**——要改插件本身,或参与开发:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
git clone https://github.com/NanmiCoder/dsh-agent-teams.git
|
|
56
|
+
cd dsh-agent-teams
|
|
57
|
+
pnpm install
|
|
58
|
+
pnpm build
|
|
59
|
+
dsh plugin --profile web add "link:$(pwd)"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`link:` 安装后,改完代码跑 `pnpm build` 即时生效,不必重装插件(客户端改动需刷新页面,host 侧改动需重启 dsh 服务)。提交前跑 `pnpm verify` 做离线校验。
|
|
63
|
+
|
|
30
64
|
## 使用
|
|
31
65
|
|
|
32
66
|
安装并重启后,直接对助手说:
|
package/lib/client.js
CHANGED
|
@@ -123,13 +123,13 @@ window.__ModuleLoader__.load({
|
|
|
123
123
|
var AgentTeamsCard_module_css_default = {
|
|
124
124
|
"head": "_6Ci0pW_head",
|
|
125
125
|
"leadAvatar": "_6Ci0pW_leadAvatar",
|
|
126
|
-
"members": "_6Ci0pW_members",
|
|
127
|
-
"memberInitial": "_6Ci0pW_memberInitial",
|
|
128
126
|
"member": "_6Ci0pW_member",
|
|
129
127
|
"memberArt": "_6Ci0pW_memberArt",
|
|
130
|
-
"panelButton": "_6Ci0pW_panelButton",
|
|
131
128
|
"memberCount": "_6Ci0pW_memberCount",
|
|
129
|
+
"memberInitial": "_6Ci0pW_memberInitial",
|
|
132
130
|
"memberName": "_6Ci0pW_memberName",
|
|
131
|
+
"members": "_6Ci0pW_members",
|
|
132
|
+
"panelButton": "_6Ci0pW_panelButton",
|
|
133
133
|
"root": "_6Ci0pW_root",
|
|
134
134
|
"teamName": "_6Ci0pW_teamName"
|
|
135
135
|
};
|
|
@@ -271,86 +271,86 @@ window.__ModuleLoader__.load({
|
|
|
271
271
|
document.head.appendChild(tag);
|
|
272
272
|
}
|
|
273
273
|
var ActivityPanel_module_css_default = {
|
|
274
|
+
"agentTeamsBreathe": "qZToFW_agentTeamsBreathe",
|
|
275
|
+
"agentTeamsFloat": "qZToFW_agentTeamsFloat",
|
|
276
|
+
"agentTeamsPanelIn": "qZToFW_agentTeamsPanelIn",
|
|
277
|
+
"agentTeamsPulse": "qZToFW_agentTeamsPulse",
|
|
278
|
+
"agentTeamsRing": "qZToFW_agentTeamsRing",
|
|
279
|
+
"agentTeamsThink": "qZToFW_agentTeamsThink",
|
|
280
|
+
"archivedWrap": "qZToFW_archivedWrap",
|
|
281
|
+
"assignmentChip": "qZToFW_assignmentChip",
|
|
282
|
+
"assignmentLabel": "qZToFW_assignmentLabel",
|
|
283
|
+
"assignmentLine": "qZToFW_assignmentLine",
|
|
274
284
|
"assignmentTasks": "qZToFW_assignmentTasks",
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
285
|
+
"badge": "qZToFW_badge",
|
|
286
|
+
"badgeCount": "qZToFW_badgeCount",
|
|
287
|
+
"badgeDot": "qZToFW_badgeDot",
|
|
278
288
|
"captainAvatar": "qZToFW_captainAvatar",
|
|
279
|
-
"captainRole": "qZToFW_captainRole",
|
|
280
|
-
"unreadPill": "qZToFW_unreadPill",
|
|
281
|
-
"stageColumn": "qZToFW_stageColumn",
|
|
282
|
-
"memberInfo": "qZToFW_memberInfo",
|
|
283
|
-
"memberCount": "qZToFW_memberCount",
|
|
284
|
-
"sectionTitle": "qZToFW_sectionTitle",
|
|
285
|
-
"captainLine": "qZToFW_captainLine",
|
|
286
|
-
"assignmentLine": "qZToFW_assignmentLine",
|
|
287
|
-
"teamStats": "qZToFW_teamStats",
|
|
288
289
|
"captainInfo": "qZToFW_captainInfo",
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
"
|
|
296
|
-
"taskSubject": "qZToFW_taskSubject",
|
|
297
|
-
"stageLine": "qZToFW_stageLine",
|
|
298
|
-
"unclaimed": "qZToFW_unclaimed",
|
|
299
|
-
"memberBranch": "qZToFW_memberBranch",
|
|
300
|
-
"memberLine": "qZToFW_memberLine",
|
|
290
|
+
"captainLine": "qZToFW_captainLine",
|
|
291
|
+
"captainName": "qZToFW_captainName",
|
|
292
|
+
"captainNode": "qZToFW_captainNode",
|
|
293
|
+
"captainRole": "qZToFW_captainRole",
|
|
294
|
+
"captainState": "qZToFW_captainState",
|
|
295
|
+
"captainSummary": "qZToFW_captainSummary",
|
|
296
|
+
"closeButton": "qZToFW_closeButton",
|
|
301
297
|
"delegationSection": "qZToFW_delegationSection",
|
|
302
|
-
"leadAvatar": "qZToFW_leadAvatar",
|
|
303
|
-
"dependencySection": "qZToFW_dependencySection",
|
|
304
|
-
"taskOwner": "qZToFW_taskOwner",
|
|
305
|
-
"badge": "qZToFW_badge",
|
|
306
298
|
"delegationTree": "qZToFW_delegationTree",
|
|
307
|
-
"
|
|
308
|
-
"taskId": "qZToFW_taskId",
|
|
309
|
-
"taskNode": "qZToFW_taskNode",
|
|
310
|
-
"stageLabel": "qZToFW_stageLabel",
|
|
311
|
-
"badgeCount": "qZToFW_badgeCount",
|
|
312
|
-
"inbox": "qZToFW_inbox",
|
|
299
|
+
"dependencySection": "qZToFW_dependencySection",
|
|
313
300
|
"emptyHint": "qZToFW_emptyHint",
|
|
314
|
-
"
|
|
315
|
-
"
|
|
301
|
+
"historicPill": "qZToFW_historicPill",
|
|
302
|
+
"inbox": "qZToFW_inbox",
|
|
303
|
+
"inboxContent": "qZToFW_inboxContent",
|
|
304
|
+
"inboxRoute": "qZToFW_inboxRoute",
|
|
316
305
|
"inboxRow": "qZToFW_inboxRow",
|
|
317
|
-
"
|
|
318
|
-
"taskDeps": "qZToFW_taskDeps",
|
|
319
|
-
"closeButton": "qZToFW_closeButton",
|
|
320
|
-
"members": "qZToFW_members",
|
|
321
|
-
"teamHead": "qZToFW_teamHead",
|
|
322
|
-
"sectionHead": "qZToFW_sectionHead",
|
|
323
|
-
"captainNode": "qZToFW_captainNode",
|
|
324
|
-
"memberStatusLine": "qZToFW_memberStatusLine",
|
|
306
|
+
"leadAvatar": "qZToFW_leadAvatar",
|
|
325
307
|
"memberArt": "qZToFW_memberArt",
|
|
326
|
-
"teamName": "qZToFW_teamName",
|
|
327
|
-
"stateArt": "qZToFW_stateArt",
|
|
328
|
-
"assignmentLabel": "qZToFW_assignmentLabel",
|
|
329
|
-
"stageFlow": "qZToFW_stageFlow",
|
|
330
|
-
"agentTeamsPanelIn": "qZToFW_agentTeamsPanelIn",
|
|
331
|
-
"captainName": "qZToFW_captainName",
|
|
332
|
-
"memberRow": "qZToFW_memberRow",
|
|
333
|
-
"taskRoute": "qZToFW_taskRoute",
|
|
334
|
-
"agentTeamsPulse": "qZToFW_agentTeamsPulse",
|
|
335
308
|
"memberAvatar": "qZToFW_memberAvatar",
|
|
336
|
-
"
|
|
337
|
-
"
|
|
338
|
-
"
|
|
339
|
-
"
|
|
309
|
+
"memberBlock": "qZToFW_memberBlock",
|
|
310
|
+
"memberBranch": "qZToFW_memberBranch",
|
|
311
|
+
"memberCount": "qZToFW_memberCount",
|
|
312
|
+
"memberInfo": "qZToFW_memberInfo",
|
|
340
313
|
"memberInitial": "qZToFW_memberInitial",
|
|
341
|
-
"
|
|
314
|
+
"memberLine": "qZToFW_memberLine",
|
|
315
|
+
"memberName": "qZToFW_memberName",
|
|
316
|
+
"memberRole": "qZToFW_memberRole",
|
|
317
|
+
"memberRow": "qZToFW_memberRow",
|
|
318
|
+
"memberState": "qZToFW_memberState",
|
|
319
|
+
"memberStatusLine": "qZToFW_memberStatusLine",
|
|
320
|
+
"members": "qZToFW_members",
|
|
321
|
+
"panel": "qZToFW_panel",
|
|
342
322
|
"panelDot": "qZToFW_panelDot",
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
323
|
+
"panelHead": "qZToFW_panelHead",
|
|
324
|
+
"panelTitle": "qZToFW_panelTitle",
|
|
325
|
+
"sectionHead": "qZToFW_sectionHead",
|
|
346
326
|
"sectionHint": "qZToFW_sectionHint",
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
"
|
|
350
|
-
"
|
|
327
|
+
"sectionTitle": "qZToFW_sectionTitle",
|
|
328
|
+
"stageColumn": "qZToFW_stageColumn",
|
|
329
|
+
"stageConnector": "qZToFW_stageConnector",
|
|
330
|
+
"stageFlow": "qZToFW_stageFlow",
|
|
351
331
|
"stageGroup": "qZToFW_stageGroup",
|
|
352
|
-
"
|
|
353
|
-
"
|
|
332
|
+
"stageLabel": "qZToFW_stageLabel",
|
|
333
|
+
"stageLine": "qZToFW_stageLine",
|
|
334
|
+
"stageTasks": "qZToFW_stageTasks",
|
|
335
|
+
"stateArt": "qZToFW_stateArt",
|
|
336
|
+
"taskBadge": "qZToFW_taskBadge",
|
|
337
|
+
"taskDeps": "qZToFW_taskDeps",
|
|
338
|
+
"taskEmpty": "qZToFW_taskEmpty",
|
|
339
|
+
"taskId": "qZToFW_taskId",
|
|
340
|
+
"taskNode": "qZToFW_taskNode",
|
|
341
|
+
"taskNodeHead": "qZToFW_taskNodeHead",
|
|
342
|
+
"taskOwner": "qZToFW_taskOwner",
|
|
343
|
+
"taskRoute": "qZToFW_taskRoute",
|
|
344
|
+
"taskStart": "qZToFW_taskStart",
|
|
345
|
+
"taskSubject": "qZToFW_taskSubject",
|
|
346
|
+
"team": "qZToFW_team",
|
|
347
|
+
"teamHead": "qZToFW_teamHead",
|
|
348
|
+
"teamName": "qZToFW_teamName",
|
|
349
|
+
"teamStats": "qZToFW_teamStats",
|
|
350
|
+
"teams": "qZToFW_teams",
|
|
351
|
+
"unclaimed": "qZToFW_unclaimed",
|
|
352
|
+
"unclaimedTitle": "qZToFW_unclaimedTitle",
|
|
353
|
+
"unreadPill": "qZToFW_unreadPill"
|
|
354
354
|
};
|
|
355
355
|
//#endregion
|
|
356
356
|
//#region lib/client/ActivityPanel.js
|
package/lib/state.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `fs` service offers no directory deletion.
|
|
13
13
|
* @module dsh-agent-teams/state
|
|
14
14
|
*/
|
|
15
|
-
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
16
16
|
import { mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
18
|
/** Mailbox key of the captain. */
|
|
@@ -38,14 +38,41 @@ export async function withTeamLock(key, fn) {
|
|
|
38
38
|
release();
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
/** Longest key emitted before truncating and appending a digest. */
|
|
42
|
+
const MAX_KEY_LENGTH = 48;
|
|
43
|
+
/** Short stable digest, used to keep otherwise-colliding keys distinct. */
|
|
44
|
+
function keyDigest(name) {
|
|
45
|
+
return createHash('sha256').update(name).digest('hex').slice(0, 8);
|
|
46
|
+
}
|
|
41
47
|
/**
|
|
42
48
|
* Fold a free-form name into a safe path/key segment.
|
|
49
|
+
*
|
|
50
|
+
* Unicode letters and digits survive, so CJK/Cyrillic/Greek names stay
|
|
51
|
+
* distinct and readable; everything else — spaces, punctuation, path
|
|
52
|
+
* separators, control characters — folds to `-`. An ASCII-only whitelist
|
|
53
|
+
* mapped *every* non-Latin name onto one shared fallback, which silently
|
|
54
|
+
* merged their mailboxes and rejected the second such member as a duplicate.
|
|
55
|
+
*
|
|
56
|
+
* A name with no letters or digits at all (pure emoji or punctuation) cannot
|
|
57
|
+
* yield a readable key, so it gets a digest rather than a shared constant.
|
|
58
|
+
* Over-long names are truncated with a digest appended, so names sharing a
|
|
59
|
+
* long prefix stay distinct and the result stays within filesystem limits
|
|
60
|
+
* (CJK costs 3 bytes per character in UTF-8).
|
|
61
|
+
*
|
|
43
62
|
* @param name - any user-supplied name.
|
|
44
|
-
* @returns
|
|
63
|
+
* @returns a non-empty key safe as a single path segment.
|
|
45
64
|
*/
|
|
46
65
|
export function sanitizeKey(name) {
|
|
47
|
-
const cleaned = name.
|
|
48
|
-
|
|
66
|
+
const cleaned = name.normalize('NFC').trim().toLowerCase()
|
|
67
|
+
.replace(/[^\p{L}\p{N}]+/gu, '-')
|
|
68
|
+
.replace(/^-+|-+$/g, '');
|
|
69
|
+
if (cleaned === '')
|
|
70
|
+
return `k-${keyDigest(name)}`;
|
|
71
|
+
const points = [...cleaned];
|
|
72
|
+
if (points.length > MAX_KEY_LENGTH) {
|
|
73
|
+
return `${points.slice(0, MAX_KEY_LENGTH).join('')}-${keyDigest(name)}`;
|
|
74
|
+
}
|
|
75
|
+
return cleaned;
|
|
49
76
|
}
|
|
50
77
|
/**
|
|
51
78
|
* Whether `dependencies` are all satisfied (every named task exists and
|
package/lib/types/state.d.ts
CHANGED
|
@@ -24,8 +24,21 @@ export declare const CAPTAIN_KEY = "captain";
|
|
|
24
24
|
export declare function withTeamLock<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
25
25
|
/**
|
|
26
26
|
* Fold a free-form name into a safe path/key segment.
|
|
27
|
+
*
|
|
28
|
+
* Unicode letters and digits survive, so CJK/Cyrillic/Greek names stay
|
|
29
|
+
* distinct and readable; everything else — spaces, punctuation, path
|
|
30
|
+
* separators, control characters — folds to `-`. An ASCII-only whitelist
|
|
31
|
+
* mapped *every* non-Latin name onto one shared fallback, which silently
|
|
32
|
+
* merged their mailboxes and rejected the second such member as a duplicate.
|
|
33
|
+
*
|
|
34
|
+
* A name with no letters or digits at all (pure emoji or punctuation) cannot
|
|
35
|
+
* yield a readable key, so it gets a digest rather than a shared constant.
|
|
36
|
+
* Over-long names are truncated with a digest appended, so names sharing a
|
|
37
|
+
* long prefix stay distinct and the result stays within filesystem limits
|
|
38
|
+
* (CJK costs 3 bytes per character in UTF-8).
|
|
39
|
+
*
|
|
27
40
|
* @param name - any user-supplied name.
|
|
28
|
-
* @returns
|
|
41
|
+
* @returns a non-empty key safe as a single path segment.
|
|
29
42
|
*/
|
|
30
43
|
export declare function sanitizeKey(name: string): string;
|
|
31
44
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanmicoder/dsh-agent-teams",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "AgentTeams for DeepSeek Harness: multi-agent team collaboration (captain, members, tasks with dependencies, messaging) driven by natural language, with a tree monitor in the web GUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|