@klarkxy/dsh-dev-index 0.2.5 → 0.2.7

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  [简体中文](README.zh-CN.md)
6
6
 
7
- Read the official DeepSeek Harness documentation without leaving DSH, and give Creator mode tools to look up the docs and check plugins before installing them.
7
+ Read the official DeepSeek Harness docs without leaving DSH, and give Creator mode tools to look up docs and check plugins before installing them.
8
8
 
9
9
  ## Install
10
10
 
@@ -17,14 +17,15 @@ Replace `web` with your profile name and restart the profile.
17
17
  ## Use
18
18
 
19
19
  - **Plugins → DSH Docs** shows the [official documentation site](https://deepseek-harness.github.io/deepseek-harness/) inside DSH, with a Chinese / English switch.
20
- - In Creator mode the agent gets five read-only tools: `dsh_docs_search` and `dsh_docs_fetch` to find and read official docs, `dsh_plugins_search` and `dsh_plugins_fetch` to check a plugin's npm information before installing it (search sources: catalog, npm or GitHub repositories), and `dsh_github_fetch` to inspect a GitHub plugin repository and pin its branch to an exact commit. Creator mode is also reminded to verify runtime interfaces with `cordis_inspect_list` and `cordis_inspect_query`.
20
+ - In Creator mode the agent gets five read-only tools: `dsh_docs_search` and `dsh_docs_fetch` to find and read official docs; `dsh_plugins_search` and `dsh_plugins_fetch` to check a plugin's npm information before installing it (search sources: catalog, npm or GitHub repositories); and `dsh_github_fetch` to inspect a GitHub plugin repository and pin its branch to an exact commit. Creator mode is also reminded to verify runtime interfaces with `cordis_inspect_list` and `cordis_inspect_query`.
21
21
 
22
22
  ## Limits
23
23
 
24
24
  - The tools only read; they never install anything. Installing stays with the DSH plugin manager.
25
25
  - The website follows the latest DSH release, which may differ from the version you run.
26
26
  - npm metadata is not proof that a plugin works with your DSH.
27
- - GitHub data comes from your local signed-in gh CLI or anonymous API access (a small hourly quota); the plugin stores no GitHub credentials. Manifest-based code search is only available when gh is signed in.
27
+ - GitHub data comes from your local signed-in gh CLI or anonymous API access (a small hourly quota); the plugin stores no GitHub credentials. Manifest-based code search works only when gh is signed in.
28
+ - Cancelling the last request using a GitHub CLI check waits for that check to stop; unloading the plugin waits for its active GitHub requests and CLI processes to finish.
28
29
 
29
30
  ## Install from GitHub
30
31
 
package/README.zh-CN.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [English](README.md)
6
6
 
7
- 不离开 DSH 就能阅读 DeepSeek Harness 官方文档;创造模式还会获得查文档、在安装前核对插件信息的工具。
7
+ 不离开 DSH 就能阅读 DeepSeek Harness 官方文档;创造模式还会获得查文档、安装前核对插件的工具。
8
8
 
9
9
  ## 安装
10
10
 
@@ -16,15 +16,16 @@ dsh plugin --profile web add @klarkxy/dsh-dev-index
16
16
 
17
17
  ## 使用
18
18
 
19
- - 「插件 → DSH 文档」会在 DSH 内显示[官方文档站](https://deepseek-harness.github.io/deepseek-harness/),可切换中文 / English。
20
- - 创造模式下,智能体获得五个只读工具:`dsh_docs_search` 和 `dsh_docs_fetch` 用于查找和阅读官方文档,`dsh_plugins_search` 和 `dsh_plugins_fetch` 用于在安装前查看插件的 npm 信息(搜索源可选 catalog、npm 或 GitHub 仓库),`dsh_github_fetch` 用于查看 GitHub 插件仓库的元数据并把分支解析成可锁定的 commit。创造模式还会被提醒用 `cordis_inspect_list` 和 `cordis_inspect_query` 核对运行时接口。
19
+ - 「插件 → DSH 文档」在 DSH 内显示[官方文档站](https://deepseek-harness.github.io/deepseek-harness/),可切换中文 / English。
20
+ - 创造模式下,智能体获得五个只读工具:`dsh_docs_search` 和 `dsh_docs_fetch` 用于查找和阅读官方文档;`dsh_plugins_search` 和 `dsh_plugins_fetch` 用于在安装前查看插件的 npm 信息(搜索源可选 catalog、npm 或 GitHub 仓库);`dsh_github_fetch` 用于查看 GitHub 插件仓库的元数据并把分支解析成可锁定的 commit。创造模式还会被提醒用 `cordis_inspect_list` 和 `cordis_inspect_query` 核对运行时接口。
21
21
 
22
22
  ## 限制
23
23
 
24
- - 这些工具只读,不会安装任何东西;安装仍由 DSH 插件管理器负责。
24
+ - 这些工具只读,不安装任何东西;安装仍由 DSH 插件管理器负责。
25
25
  - 官网对应最新发布的 DSH,可能与你正在使用的版本不同。
26
26
  - npm 元数据不能证明插件与你的 DSH 兼容。
27
- - GitHub 数据来自本机已登录的 gh CLI 或匿名 API(匿名额度有限,约每小时几十次请求);插件不保存任何 GitHub 凭据。按 `dsh.plugin.json` 清单的代码搜索只有在 gh 已登录时可用。
27
+ - GitHub 数据来自本机已登录的 gh CLI 或匿名 API(匿名额度有限,约每小时几十次请求);插件不保存任何 GitHub 凭据。按 `dsh.plugin.json` 清单的代码搜索仅在 gh 已登录时可用。
28
+ - 取消最后一个使用 GitHub CLI 检查的请求时,会等待该检查停止;卸载插件会等待正在进行的 GitHub 请求和 CLI 进程结束。
28
29
 
29
30
  ## 从 GitHub 安装
30
31
 
@@ -32,7 +33,7 @@ dsh plugin --profile web add @klarkxy/dsh-dev-index
32
33
  dsh plugin --profile web add "github:klarkxy/dsh-plugins#path:/plugins/dsh-dev-index"
33
34
  ```
34
35
 
35
- 从 GitHub 安装会在本机构建插件,pnpm 会先要求你允许。把下面内容加入 `$DSH_HOME/profiles/web/pnpm-workspace.yaml` 后再执行一次安装命令:
36
+ 从 GitHub 安装会在本机构建插件,pnpm 会先要求你允许。把下面内容加入 `$DSH_HOME/profiles/web/pnpm-workspace.yaml` 后,再执行一次安装命令:
36
37
 
37
38
  ```yaml
38
39
  allowBuilds:
package/client.js CHANGED
@@ -26,7 +26,7 @@ const css = `
26
26
  & *, & *::before, & *::after { box-sizing: border-box; }
27
27
  & h1, & h2, & h3, & h4, & p, & figure { margin: 0; }
28
28
  /* Keyboard focus only: pointer input keeps the native resting appearance. */
29
- & :focus-visible { outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: 2px; border-radius: var(--dsw-radius-sm); }
29
+ & :focus-visible { outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: 2px; }
30
30
  & [hidden] { display: none !important; }
31
31
 
32
32
  /* --- Type tiers. The same 14/22 and 12/18 steps the primitives use. --- */
@@ -36,7 +36,7 @@ const css = `
36
36
  /* --- Layout scaffolding. Each recipe also matches the root itself, because
37
37
  * this plugin's one surface carries the root class and the stack on the same
38
38
  * element; a descendant-only rule would leave it unstyled. --- */
39
- &.dsh-ui-stack, & .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
39
+ &.dsh-ui-page, & .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
40
40
  &.dsh-ui-row-wrap, & .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
41
41
  &.dsh-ui-spacer, & .dsh-ui-spacer { flex: 1; min-width: 0; }
42
42
 
@@ -116,7 +116,7 @@ const css = `
116
116
 
117
117
  return h('section', {
118
118
  'aria-label': chinese ? 'DSH 文档浏览器' : 'DSH documentation browser',
119
- className: 'dsh-di-root dsh-ui-stack',
119
+ className: 'dsh-di-root dsh-ui-page',
120
120
  },
121
121
  h('style', null, css),
122
122
  h('div', { className: 'dsh-ui-row-wrap' },
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "@klarkxy/dsh-dev-index",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "main": "lib/index.js",
5
5
  "description": "DSH Docs: read the official DSH documentation in the Plugins page; Creator mode searches docs and checks plugin metadata on npm and GitHub with native tools.",
6
6
  "engines": {
package/lib/gh.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { access } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { delimiter, join } from "node:path";
4
- import { runCli } from "@klarkxy/dsh-plugin-kit/cli-process";
4
+ import { cliEnvironment, runCli } from "@klarkxy/dsh-plugin-kit/cli-process";
5
5
  // gh api and gh auth status are long-stable subcommands; the floor only rejects
6
6
  // truly ancient binaries.
7
7
  export const MIN_GH_VERSION = [2, 0, 0];
@@ -28,7 +28,7 @@ export async function findGh(deps = {}) {
28
28
  const candidate = join(dir, file);
29
29
  try {
30
30
  await access(candidate);
31
- return { command: candidate, displayPath: candidate };
31
+ return { command: candidate, displayPath: candidate, env: cliEnvironment(process.env, ["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "GH_HOST", "GH_CONFIG_DIR"]) };
32
32
  }
33
33
  catch { /* keep looking */ }
34
34
  }
package/lib/github.d.ts CHANGED
@@ -37,7 +37,9 @@ export declare class GitHubClient {
37
37
  private readonly run;
38
38
  private readonly discoverProbe;
39
39
  constructor(deps?: GitHubDeps);
40
- dispose(): void;
40
+ private readonly work;
41
+ private track;
42
+ dispose(): Promise<void>;
41
43
  private abandonProbe;
42
44
  private ghBroken;
43
45
  private merged;
package/lib/github.js CHANGED
@@ -262,9 +262,19 @@ export class GitHubClient {
262
262
  this.run = deps.run ?? runCli;
263
263
  this.discoverProbe = deps.discover ?? ((signal) => probeGh({ run: this.run }, signal));
264
264
  }
265
- dispose() {
265
+ work = new Set();
266
+ track(work) {
267
+ this.work.add(work);
268
+ void work.then(() => this.work.delete(work), () => this.work.delete(work));
269
+ return work;
270
+ }
271
+ async dispose() {
266
272
  this.lifetime.abort();
273
+ this.confirmedProbe = undefined;
267
274
  this.abandonProbe(this.pendingProbe);
275
+ // Includes abandoned probes: their caller may already have stopped waiting.
276
+ while (this.work.size)
277
+ await Promise.allSettled([...this.work]);
268
278
  }
269
279
  // Detach and cancel a probe nobody wants any more, releasing its listener. Its
270
280
  // late resolution is rejected by the publish gate in startProbe.
@@ -288,8 +298,8 @@ export class GitHubClient {
288
298
  merged(signal) {
289
299
  return AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS), ...(signal ? [signal] : [])]);
290
300
  }
291
- // Resolve gh for one caller: join the shared probe, but reject as soon as this
292
- // caller's own signal or the client lifetime aborts. An already-aborted caller
301
+ // Resolve gh for one caller: a peer can keep the shared probe alive, while
302
+ // the final cancelled waiter must await cleanup. An already-aborted caller
293
303
  // never spawns discovery. The throw runs inside the promise so no caller can
294
304
  // observe a partially started probe.
295
305
  probe(signal) {
@@ -298,8 +308,9 @@ export class GitHubClient {
298
308
  if (this.confirmedProbe)
299
309
  return this.confirmedProbe;
300
310
  const pending = this.pendingProbe ?? this.startProbe();
301
- pending.waiters += 1;
302
- return this.awaitProbe(pending, signal);
311
+ const waiter = { signal };
312
+ pending.waiters.add(waiter);
313
+ return this.awaitProbe(pending, waiter);
303
314
  });
304
315
  }
305
316
  startProbe() {
@@ -309,7 +320,7 @@ export class GitHubClient {
309
320
  // probes do not accumulate listeners on the lifetime signal.
310
321
  this.lifetime.signal.addEventListener("abort", onLifetimeAbort, { once: true });
311
322
  const probe = {
312
- controller, waiters: 0, promise: Promise.resolve(null), onLifetimeAbort,
323
+ controller, waiters: new Set(), promise: Promise.resolve(null), onLifetimeAbort,
313
324
  };
314
325
  // Discovery runs inside the promise chain so a synchronous throw is folded
315
326
  // into the same catch/finally: the listener is always released, pendingProbe
@@ -322,7 +333,8 @@ export class GitHubClient {
322
333
  .then((found) => {
323
334
  // Publish only from the current, live probe: an abandoned, cancelled or
324
335
  // disposed probe may still resolve late and must not overwrite the cache.
325
- if (found && this.pendingProbe === probe && !controller.signal.aborted && !this.lifetime.signal.aborted) {
336
+ if (found && this.pendingProbe === probe && !controller.signal.aborted && !this.lifetime.signal.aborted
337
+ && [...probe.waiters].some(waiter => !waiter.signal.aborted)) {
326
338
  this.confirmedProbe = found;
327
339
  }
328
340
  return found;
@@ -334,12 +346,14 @@ export class GitHubClient {
334
346
  this.pendingProbe = undefined;
335
347
  });
336
348
  this.pendingProbe = probe;
349
+ this.track(probe.promise);
337
350
  return probe;
338
351
  }
339
352
  // Race the shared probe against this caller's cancellation. A null result falls
340
- // through to anonymous access as before. Leaving early decrements the waiter
341
- // count; the last one out abandons the probe, while a peer keeps it alive.
342
- async awaitProbe(probe, signal) {
353
+ // through to anonymous access as before. The last cancelled waiter abandons
354
+ // and drains the probe, while a peer keeps it alive.
355
+ async awaitProbe(probe, waiter) {
356
+ const { signal } = waiter;
343
357
  let onAbort;
344
358
  let settled = false;
345
359
  try {
@@ -357,9 +371,11 @@ export class GitHubClient {
357
371
  finally {
358
372
  if (onAbort)
359
373
  signal.removeEventListener("abort", onAbort);
360
- probe.waiters -= 1;
361
- if (!settled && probe.waiters <= 0)
374
+ probe.waiters.delete(waiter);
375
+ if (!settled && probe.waiters.size === 0) {
362
376
  this.abandonProbe(probe);
377
+ await probe.promise;
378
+ }
363
379
  }
364
380
  }
365
381
  async request(path, signal) {
@@ -369,14 +385,14 @@ export class GitHubClient {
369
385
  merged.throwIfAborted();
370
386
  if (probe) {
371
387
  try {
372
- return { data: await this.viaGh(probe.executable, path, signal), transport: "gh" };
388
+ return { data: await this.track(this.viaGh(probe.executable, path, signal)), transport: "gh" };
373
389
  }
374
390
  catch (error) {
375
391
  if (!this.ghBroken(error))
376
392
  throw error;
377
393
  }
378
394
  }
379
- return { data: await this.viaHttp(path, signal), transport: "anonymous" };
395
+ return { data: await this.track(this.viaHttp(path, signal)), transport: "anonymous" };
380
396
  }
381
397
  async viaGh(executable, path, signal) {
382
398
  const merged = this.merged(signal);
@@ -393,6 +409,7 @@ export class GitHubClient {
393
409
  }
394
410
  throw error;
395
411
  }
412
+ merged.throwIfAborted();
396
413
  if (result.exitCode !== 0)
397
414
  throw classifyGhFailure(result.stderr);
398
415
  try {
@@ -521,7 +538,7 @@ export class GitHubClient {
521
538
  const path = `search/code?q=${encodeURIComponent(query)}&per_page=${PER_PAGE}&page=${page}`;
522
539
  let data;
523
540
  try {
524
- data = await this.viaGh(probe.executable, path, signal);
541
+ data = await this.track(this.viaGh(probe.executable, path, signal));
525
542
  }
526
543
  catch (error) {
527
544
  this.ghBroken(error); // code search has no anonymous lane, but the next call re-probes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-dev-index",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "DSH Docs: read the official DSH documentation in the Plugins page; Creator mode searches docs and checks plugin metadata on npm and GitHub with native tools.",
5
5
  "icon": "./icon.svg",
6
6
  "keywords": [
@@ -24,7 +24,7 @@
24
24
  "url": "https://github.com/klarkxy"
25
25
  },
26
26
  "type": "module",
27
- "packageManager": "pnpm@10.29.2",
27
+ "packageManager": "pnpm@11.28.5",
28
28
  "main": "lib/index.js",
29
29
  "types": "lib/index.d.ts",
30
30
  "exports": {
@@ -80,7 +80,7 @@
80
80
  "@deepseek-ai/dsh-tools": ">=0.2.0-rc.2"
81
81
  },
82
82
  "dependencies": {
83
- "@klarkxy/dsh-plugin-kit": "0.3.4"
83
+ "@klarkxy/dsh-plugin-kit": "0.5.0"
84
84
  },
85
85
  "peerDependenciesMeta": {
86
86
  "@deepseek-ai/dsh-agent-preset-registry": {
@@ -91,22 +91,28 @@
91
91
  },
92
92
  "@deepseek-ai/dsh-tools": {
93
93
  "optional": true
94
+ },
95
+ "@deepseek-ai/cordis": {
96
+ "optional": true
97
+ },
98
+ "@deepseek-ai/schemastery": {
99
+ "optional": true
94
100
  }
95
101
  },
96
102
  "devDependencies": {
97
- "@deepseek-ai/cordis": "4.0.4",
98
- "@deepseek-ai/schemastery": "3.18.4",
99
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
100
- "@deepseek-ai/dsh-agent-preset-registry": "0.2.0-rc.2",
101
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
102
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
103
- "@deepseek-ai/dsh-scope": "0.2.0-rc.2"
103
+ "@deepseek-ai/cordis": "4.0.5",
104
+ "@deepseek-ai/schemastery": "3.18.5",
105
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.1",
106
+ "@deepseek-ai/dsh-agent-preset-registry": "0.2.1-rc.1",
107
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.1",
108
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.1",
109
+ "@deepseek-ai/dsh-scope": "0.2.1-rc.1"
104
110
  },
105
111
  "publishConfig": {
106
112
  "access": "public",
107
113
  "registry": "https://registry.npmjs.org/"
108
114
  },
109
115
  "dshRelease": {
110
- "contentHash": "293f3ffa69fb67d90c06419890c9fe2f0dbada1df9fc818cc2d3a2be9ab24485"
116
+ "contentHash": "a2daf4a6e10ea93df0733a844d8e8eee52a8d946908b0efec797b1dec2467896"
111
117
  }
112
118
  }