@stofone/dsh-agent-roster 0.6.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 与 [SemVer](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.6.2] - 2026-10-30
6
+
7
+ ### Packaging
8
+
9
+ - `peerDependencies` for `@deepseek-ai/dsh-tools` no longer uses the wildcard. node-semver only
10
+ admits a prerelease version when one comparator in the range shares its exact
11
+ `major.minor.patch` tuple and itself carries a prerelease tag, so `*` — like the broad
12
+ `>=0.0.1-rc.1 <0.2.0` form — silently excludes every prerelease harness build. The range is now
13
+ `^0.1.0-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1 || ^0.3.0-rc.1`, which admits the 0.1.x, 0.2.0-x,
14
+ 0.2.1-x and 0.3.x prerelease lines while still refusing 0.4.x prereleases.
15
+ - No runtime change; the peer stays optional because the host always provides the module.
16
+
17
+ ## [0.6.1] - 2026-10-30
18
+
19
+ ### Custom registry mirror
20
+
21
+ - New config field `registryMirror`: type any npm registry URL in Settings (download section)
22
+ and index packages published as npm packages are fetched from there instead of the region
23
+ presets. Sub-paths are kept, so corporate Nexus layouts such as
24
+ `http://nexus.corp.local/repository/npm` work.
25
+ - Precedence: a registry pinned inside a source wins, then `registryMirror`, then `region`.
26
+ A blank or malformed value is ignored instead of silently disabling downloads.
27
+ - Tests: 195 passing (mirror normalisation, precedence, config-view passthrough).
28
+
5
29
  ## [0.6.0] - 2026-10-30
6
30
 
7
31
  ### Captain verification and applying a roster
package/README.md CHANGED
@@ -191,7 +191,7 @@ pool is a filtered view only; it never writes tags back to files.
191
191
  Header actions follow the place they act on: **Import** and **New archive** sit in the archive tab
192
192
  next to the search box, **Refresh sources** sits in the sources tab (it bypasses the one-hour cache and
193
193
  re-reads the public index), and the header keeps a single **gear button** that opens the settings dialog.
194
- The dialog edits the language, the download region and the roster directories, and shows the resolved
194
+ The dialog edits the language, the download region, the mirror address and the roster directories, and shows the resolved
195
195
  data directory, the team state directory and the version.
196
196
 
197
197
  A badge at the top summarises how many items need attention
@@ -249,7 +249,8 @@ All six are `exact` routes behind the host's `connection` fence and answer `405`
249
249
  | `agentTeamsStateDir` | `.agent-teams` | read only: where `roster_verify` and the Teams tab look for team state |
250
250
  | `language` | `"auto"` | panel language: `auto` follows DSH, or pin `zh` / `en` |
251
251
  | `indexes` | `[]` | public registry sources: an `https` `registry.json` or `npm:pkg`. Empty means the official source; `false` means local-only |
252
- | `region` | `"auto"` | download region: `china` reads npm-style index packages from the Tencent mirror; `auto` equals `global` (no probing, one less request) |
252
+ | `region` | `"auto"` | download region: `china` reads npm-style index packages from the Tencent mirror; `auto` equals `global` (no probing, one less request) |
253
+ | `registryMirror` | `""` | custom npm registry for download sources, e.g. `https://registry.npmmirror.com` or `http://nexus.corp.local/repository/npm`. Set it and it beats `region`; a registry pinned inside a source still beats both. Empty means no custom mirror |
253
254
 
254
255
  All six are `volatile`: changes apply immediately, no host restart.
255
256
 
package/README.zh.md CHANGED
@@ -181,12 +181,10 @@ npx dsh-agent-roster list
181
181
  以及各标签的档案数,点标签即筛选——多选是「且」,一个按钮清除选择。标签池只影响视图,不会把
182
182
  标签写回文件。
183
183
 
184
- 头部只留一个齿轮按钮打开**设置弹窗**:语言、下载区、档案库目录都能在这里改;解析出来的数据目录、
184
+ 头部只留一个齿轮按钮打开**设置弹窗**:语言、下载区、镜像地址、档案库目录都能在这里改;解析出来的数据目录、
185
185
  团队状态目录与版本号也列在同一处。导入与新建档案挪到档案页签的搜索框右侧,刷新只属于档案源页签
186
186
  (绕过一小时的缓存,直接回源读公共索引)。
187
187
 
188
- 顶部一条徽标汇总
189
-
190
188
  客户端插件在左侧全局栏新增一行 **Agent Roster** 图标(`sidebar.panellist` 列表槽),并在 `main` 槽用同一个面板 id 注册整页内容;点图标即在主区域打开,宽度与对话页一致。面板分三个页签,不只是看:
191
189
 
192
190
  | 页签 | 能做什么 |
@@ -239,6 +237,7 @@ npx dsh-agent-roster list
239
237
  | `language` | `"auto"` | 面板语言:`auto` 跟随 DSH,或固定 `zh` / `en` |
240
238
  | `indexes` | `[]` | 公共档案源:`https` 的 `registry.json` 或 `npm:pkg`。留空 = 官方源;显式 `false` = 只在本机库工作 |
241
239
  | `region` | `"auto"` | 下载区:`china` 时用腾讯 npm 镜像读 npm 形式的目录包;`auto` 等同 `global`(不自动探测,省一次出网) |
240
+ | `registryMirror` | `""` | 自定义 npm 镜像地址(如 `https://registry.npmmirror.com`、`http://nexus.corp.local/repository/npm`)。填了它优先于 `region`;源里自己写死的 registry 又优先于它。留空 = 不用自定义镜像 |
242
241
 
243
242
  六项都是 `volatile`:改完立即生效,无需重启。
244
243
 
package/client.js CHANGED
@@ -1846,6 +1846,7 @@ window.__ModuleLoader__.load({
1846
1846
  useTranslation();
1847
1847
  const [language, setLanguage] = useState(() => readLanguage(form));
1848
1848
  const [region, setRegion] = useState(() => readFormValue(form, "region", "auto"));
1849
+ const [mirror, setMirror] = useState(() => readFormValue(form, "registryMirror", ""));
1849
1850
  const [dirs, setDirs] = useState(() => (library?.data?.config?.rosterDirs ?? []).join(", "));
1850
1851
  const [saved, setSaved] = useState("");
1851
1852
  const [version, setVersion] = useState("");
@@ -1855,6 +1856,7 @@ window.__ModuleLoader__.load({
1855
1856
  const off = form.subscribe(() => {
1856
1857
  setLanguage(readLanguage(form));
1857
1858
  setRegion(readFormValue(form, "region", "auto"));
1859
+ setMirror(readFormValue(form, "registryMirror", ""));
1858
1860
  });
1859
1861
  return () => {
1860
1862
  if (typeof off === "function") off();
@@ -1891,6 +1893,12 @@ window.__ModuleLoader__.load({
1891
1893
  write("region", next);
1892
1894
  }
1893
1895
 
1896
+ function saveMirror() {
1897
+ // 镜像地址原样存:路径前缀(公司 Nexus 常带 /repository/npm)是它的一部分,
1898
+ // 规整交给宿主(lib/remote.js 的 normalizeRegistryUrl),界面不在写盘前偷偷改写。
1899
+ write("registryMirror", mirror.trim());
1900
+ }
1901
+
1894
1902
  function saveDirs() {
1895
1903
  // 逗号/分号/空白都当分隔符:用户从别处粘路径时不该因为分隔符被卡住。
1896
1904
  const list = dirs
@@ -1950,6 +1958,24 @@ window.__ModuleLoader__.load({
1950
1958
  ),
1951
1959
  ),
1952
1960
  ),
1961
+ h(
1962
+ "div",
1963
+ { className: "rstr-settings-field" },
1964
+ h("span", { className: "rstr-label" }, t("settings.mirror")),
1965
+ h(
1966
+ "div",
1967
+ { className: "rstr-settings-inline" },
1968
+ h("input", {
1969
+ className: "rstr-input",
1970
+ value: mirror,
1971
+ placeholder: t("settings.mirrorPlaceholder"),
1972
+ "aria-label": t("settings.mirror"),
1973
+ onChange: (event) => setMirror(event.target.value),
1974
+ }),
1975
+ h("button", { type: "button", className: "rstr-btn rstr-btn-sm", onClick: saveMirror }, t("settings.save")),
1976
+ ),
1977
+ h("span", { className: "rstr-muted rstr-small" }, t("settings.mirrorHint")),
1978
+ ),
1953
1979
  h(
1954
1980
  "div",
1955
1981
  { className: "rstr-settings-field" },
package/cordis.patch.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  # @stofone/dsh-agent-roster 的 Host 行。
2
2
  #
3
3
  # config 会被整体替换(不会深合并):覆盖本行时请把需要的字段全部重述一遍。
4
- # 所有字段都有默认值,留空即可用「项目内 .dsh/agent-rosters/」与「用户级
5
- # <dshHome>/agent-rosters/」两个目录。
4
+ # 所有字段都有默认值:rosterDirs 留空即用全局档案库 <dshHome>/agent-roster
5
+ # (docker 下就是被挂载的数据卷),与工作区无关。
6
6
  - insert:
7
7
  - id: agent-roster
8
8
  name: '@stofone/dsh-agent-roster'
package/index.js CHANGED
@@ -80,6 +80,14 @@ export const Config = z.object({
80
80
  .default("auto")
81
81
  .description("下载区:china 用腾讯 npm 镜像读 npm 形式的目录包;auto 等同 global(不自动探测,避免额外的出网请求)。")
82
82
  .volatile(),
83
+ registryMirror: z
84
+ .string()
85
+ .default("")
86
+ .description(
87
+ "自定义 npm 镜像地址(如 https://registry.npmmirror.com 或公司 Nexus 的 /repository/npm 子路径):" +
88
+ "填了就优先于下载区的预置,用于按 npm 包形式发布的公共档案源。留空 = 用下载区的预置。",
89
+ )
90
+ .volatile(),
83
91
  });
84
92
 
85
93
  /**
@@ -94,7 +102,7 @@ export function apply(ctx, config) {
94
102
  const settings = configView(config);
95
103
 
96
104
  registerRosterTools(ctx, settings, { version: pkg.version });
97
- ctx.logger?.info?.(`dsh-agent-roster ${pkg.version}: registered 4 tools`);
105
+ ctx.logger?.info?.(`dsh-agent-roster ${pkg.version}: registered 5 tools`);
98
106
 
99
107
  // 面板路由:读快照 / 读单份档案原文 / 保存 / 删除 / 导入。
100
108
  // 没有 webServer 也不影响工具可用性。
@@ -40,6 +40,7 @@ function read(cell, fallback) {
40
40
  * agentTeamsStateDir: string,
41
41
  * indexes: unknown,
42
42
  * region: string,
43
+ * registryMirror: string,
43
44
  * }}
44
45
  */
45
46
  export function configView(config) {
@@ -72,5 +73,10 @@ export function configView(config) {
72
73
  const region = read(source.region, "auto");
73
74
  return typeof region === "string" && region !== "" ? region : "auto";
74
75
  },
76
+ /** 自定义镜像:空串 = 没填,按 region 选预置。 */
77
+ get registryMirror() {
78
+ const mirror = read(source.registryMirror, "");
79
+ return typeof mirror === "string" ? mirror : "";
80
+ },
75
81
  };
76
82
  }
package/lib/remote.js CHANGED
@@ -56,6 +56,40 @@ export const NPM_REGISTRIES = {
56
56
  china: "https://mirrors.cloud.tencent.com/npm",
57
57
  };
58
58
 
59
+ /**
60
+ * 把用户填的镜像地址规整成可用的 registry 根。
61
+ *
62
+ * 只接受 http/https——下载区那两个预置之外,用户可能填 npmmirror、公司 Nexus、
63
+ * Verdaccio 之类。尾斜杠去掉(后面自己拼 `/pkg/latest`),但**路径前缀保留**:
64
+ * 内网镜像常常挂在 `/repository/npm` 这种子路径下,吃掉就等于换了个错地址。
65
+ *
66
+ * @param {unknown} value
67
+ * @returns {string | null}
68
+ */
69
+ export function normalizeRegistryUrl(value) {
70
+ if (typeof value !== "string") return null;
71
+ const trimmed = value.trim().replace(/\/+$/, "");
72
+ if (!/^https?:\/\/[^\s/]+/i.test(trimmed)) return null;
73
+ return trimmed;
74
+ }
75
+
76
+ /**
77
+ * 决定一个源用哪个 registry:源自己写死的 > 用户填的镜像 > 下载区预置。
78
+ *
79
+ * 顺序不能反:源里写 `registry` 是作者对自己包的明确声明;
80
+ * 用户填镜像是在"我这边怎么下"这一层做覆盖;预置只是兜底。
81
+ *
82
+ * @param {{ sourceRegistry?: unknown, mirror?: unknown, region?: unknown }} options
83
+ * @returns {string}
84
+ */
85
+ export function resolveRegistryRoot(options = {}) {
86
+ return (
87
+ normalizeRegistryUrl(options.sourceRegistry) ??
88
+ normalizeRegistryUrl(options.mirror) ??
89
+ NPM_REGISTRIES[options.region === "china" ? "china" : "global"]
90
+ );
91
+ }
92
+
59
93
  /** 网络单次请求的预算:够了,慢就换下一个源。 */
60
94
  const TIMEOUT_MS = 15_000;
61
95
 
@@ -174,6 +208,7 @@ export function sourceLabel(source) {
174
208
  * @param {typeof fetch} [options.fetchImpl]
175
209
  * @param {boolean} [options.refresh] 忽略 TTL(但仍受 {@link MIN_REFRESH_MS} 约束)
176
210
  * @param {number} [options.region] 选择 npm registry
211
+ * @param {string} [options.mirror] 用户填写的镜像地址(优先于 region 预置)
177
212
  * @returns {Promise<object>} `{ source, ok, stale, fetchedAt, document, diagnostics, collections }`
178
213
  */
179
214
  export async function loadRegistry(source, options = {}) {
@@ -277,7 +312,7 @@ function buildResult(source, document, meta) {
277
312
  */
278
313
  async function fetchRegistry(source, options) {
279
314
  if (source.kind === "npm") {
280
- const registry = source.registry ?? NPM_REGISTRIES[options.region === "china" ? "china" : "global"];
315
+ const registry = resolveRegistryRoot({ sourceRegistry: source.registry, mirror: options.mirror, region: options.region });
281
316
  return fetchFromPackage(source, { ...options, registry });
282
317
  }
283
318
 
@@ -551,6 +586,7 @@ export async function loadRegistryIndex(config, options = {}) {
551
586
  const loaded = await loadRegistry(source, {
552
587
  ...options,
553
588
  region: options.region ?? (config?.region === "china" ? "china" : "global"),
589
+ mirror: options.mirror ?? config?.registryMirror,
554
590
  });
555
591
  indexes.push({
556
592
  id: source.id,
package/lib/service.js CHANGED
@@ -87,6 +87,8 @@ export function buildPanelSnapshot(ctx, config, meta, options = {}) {
87
87
  defaultRoot: globalRosterRoot(),
88
88
  knownToolPolicies: Array.isArray(config.knownToolPolicies) ? config.knownToolPolicies : [],
89
89
  stateDirSpec: config.agentTeamsStateDir,
90
+ region: config.region,
91
+ registryMirror: config.registryMirror ?? "",
90
92
  },
91
93
  workspaces: library.workspaces,
92
94
  teams,
package/lib/tools.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * 四个模型可见工具:列档案、看成员、生成桥接调用、核对运行时。
2
+ * 五个模型可见工具:列档案、看成员、生成桥接调用、核对运行时、读公共档案源。
3
+ * (roster_list / roster_show / roster_create / roster_verify / roster_index)
3
4
  *
4
5
  * 只读优先:`roster_list` / `roster_show` / `roster_verify` 只读文件系统;
5
6
  * `roster_create` 也不写任何东西——它输出的是**参数**,真正的成员创建仍由队长调用
package/locale/en.json CHANGED
@@ -27,7 +27,10 @@
27
27
  "regionChina": "China mirror",
28
28
  "save": "Save",
29
29
  "saved": "Saved",
30
- "saveFailed": "Could not save — the settings form is unavailable."
30
+ "saveFailed": "Could not save — the settings form is unavailable.",
31
+ "mirror": "Custom registry mirror",
32
+ "mirrorPlaceholder": "https://registry.npmmirror.com",
33
+ "mirrorHint": "When set, this replaces the region preset for index sources published as npm packages. Company mirrors with a sub-path (e.g. /repository/npm) work too; leave empty to use the region preset."
31
34
  },
32
35
  "panel": {
33
36
  "title": "Agent Roster",
package/locale/zh.json CHANGED
@@ -27,7 +27,10 @@
27
27
  "regionChina": "国内镜像",
28
28
  "save": "保存",
29
29
  "saved": "已保存",
30
- "saveFailed": "保存失败——设置表单当前不可用。"
30
+ "saveFailed": "保存失败——设置表单当前不可用。",
31
+ "mirror": "自定义镜像地址",
32
+ "mirrorPlaceholder": "https://registry.npmmirror.com",
33
+ "mirrorHint": "填了就用它代替下载区:按 npm 包形式发布的公共档案源从这里取。支持带子路径的公司镜像(如 /repository/npm);留空用下载区的预置。"
31
34
  },
32
35
  "panel": {
33
36
  "title": "Agent 档案",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stofone/dsh-agent-roster",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "多 Agent 团队的静态成员档案定义与校验层:档案库在面板里看得见、改得动,团队成员的人设、模型与工具边界固化成可校验、可版本化的文件,并在 dsh-agent-teams 创建成员时把档案桥接给运行时。",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -62,7 +62,7 @@
62
62
  "yaml": "^2.9.1"
63
63
  },
64
64
  "peerDependencies": {
65
- "@deepseek-ai/dsh-tools": "*"
65
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1 || ^0.3.0-rc.1"
66
66
  },
67
67
  "peerDependenciesMeta": {
68
68
  "@deepseek-ai/dsh-tools": {