@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 CHANGED
@@ -13,20 +13,54 @@ DeepSeek Harness 的 AgentTeams 插件:安装后,任何会话只需一句自
13
13
 
14
14
  ## 安装
15
15
 
16
- **前置要求**:Node.js `^22.19` 或 `>=24`;pnpm 11;已安装或可通过 `npx` 使用 DeepSeek Harness
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
- npx -p @deepseek-ai/dsh dsh plugin --profile web add @nanmicoder/dsh-agent-teams@0.1.1
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
- "taskNodeHead": "qZToFW_taskNodeHead",
276
- "captainSummary": "qZToFW_captainSummary",
277
- "unclaimedTitle": "qZToFW_unclaimedTitle",
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
- "panelHead": "qZToFW_panelHead",
290
- "agentTeamsFloat": "qZToFW_agentTeamsFloat",
291
- "taskEmpty": "qZToFW_taskEmpty",
292
- "archivedWrap": "qZToFW_archivedWrap",
293
- "memberState": "qZToFW_memberState",
294
- "inboxRoute": "qZToFW_inboxRoute",
295
- "memberName": "qZToFW_memberName",
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
- "agentTeamsBreathe": "qZToFW_agentTeamsBreathe",
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
- "team": "qZToFW_team",
315
- "teams": "qZToFW_teams",
301
+ "historicPill": "qZToFW_historicPill",
302
+ "inbox": "qZToFW_inbox",
303
+ "inboxContent": "qZToFW_inboxContent",
304
+ "inboxRoute": "qZToFW_inboxRoute",
316
305
  "inboxRow": "qZToFW_inboxRow",
317
- "agentTeamsRing": "qZToFW_agentTeamsRing",
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
- "taskBadge": "qZToFW_taskBadge",
337
- "stageTasks": "qZToFW_stageTasks",
338
- "badgeDot": "qZToFW_badgeDot",
339
- "panelTitle": "qZToFW_panelTitle",
309
+ "memberBlock": "qZToFW_memberBlock",
310
+ "memberBranch": "qZToFW_memberBranch",
311
+ "memberCount": "qZToFW_memberCount",
312
+ "memberInfo": "qZToFW_memberInfo",
340
313
  "memberInitial": "qZToFW_memberInitial",
341
- "stageConnector": "qZToFW_stageConnector",
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
- "captainState": "qZToFW_captainState",
344
- "taskStart": "qZToFW_taskStart",
345
- "assignmentChip": "qZToFW_assignmentChip",
323
+ "panelHead": "qZToFW_panelHead",
324
+ "panelTitle": "qZToFW_panelTitle",
325
+ "sectionHead": "qZToFW_sectionHead",
346
326
  "sectionHint": "qZToFW_sectionHint",
347
- "memberRole": "qZToFW_memberRole",
348
- "memberBlock": "qZToFW_memberBlock",
349
- "agentTeamsThink": "qZToFW_agentTeamsThink",
350
- "historicPill": "qZToFW_historicPill",
327
+ "sectionTitle": "qZToFW_sectionTitle",
328
+ "stageColumn": "qZToFW_stageColumn",
329
+ "stageConnector": "qZToFW_stageConnector",
330
+ "stageFlow": "qZToFW_stageFlow",
351
331
  "stageGroup": "qZToFW_stageGroup",
352
- "panel": "qZToFW_panel",
353
- "inboxContent": "qZToFW_inboxContent"
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 lowercase `[a-z0-9-]` key, never empty.
63
+ * @returns a non-empty key safe as a single path segment.
45
64
  */
46
65
  export function sanitizeKey(name) {
47
- const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
48
- return cleaned === '' ? 'team' : cleaned;
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
@@ -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 lowercase `[a-z0-9-]` key, never empty.
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.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",