@klarkxy/dsh-dev-index 0.2.5 → 0.2.6
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 +1 -0
- package/README.zh-CN.md +1 -0
- package/dsh.plugin.json +1 -1
- package/lib/gh.js +2 -2
- package/lib/github.d.ts +3 -1
- package/lib/github.js +32 -15
- package/package.json +17 -11
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Replace `web` with your profile name and restart the profile.
|
|
|
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
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.
|
|
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
|
@@ -25,6 +25,7 @@ dsh plugin --profile web add @klarkxy/dsh-dev-index
|
|
|
25
25
|
- 官网对应最新发布的 DSH,可能与你正在使用的版本不同。
|
|
26
26
|
- npm 元数据不能证明插件与你的 DSH 兼容。
|
|
27
27
|
- GitHub 数据来自本机已登录的 gh CLI 或匿名 API(匿名额度有限,约每小时几十次请求);插件不保存任何 GitHub 凭据。按 `dsh.plugin.json` 清单的代码搜索只有在 gh 已登录时可用。
|
|
28
|
+
- 取消最后一个使用 GitHub CLI 检查的请求时,会等待检查停止;卸载插件会等待正在进行的 GitHub 请求和 CLI 进程结束。
|
|
28
29
|
|
|
29
30
|
## 从 GitHub 安装
|
|
30
31
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "@klarkxy/dsh-dev-index",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
292
|
-
//
|
|
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
|
-
|
|
302
|
-
|
|
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:
|
|
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.
|
|
341
|
-
//
|
|
342
|
-
async awaitProbe(probe,
|
|
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
|
|
361
|
-
if (!settled && probe.waiters
|
|
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.
|
|
3
|
+
"version": "0.2.6",
|
|
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@
|
|
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.
|
|
83
|
+
"@klarkxy/dsh-plugin-kit": "0.4.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.
|
|
98
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
99
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.2.
|
|
100
|
-
"@deepseek-ai/dsh-agent-preset-registry": "0.2.
|
|
101
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
102
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
103
|
-
"@deepseek-ai/dsh-scope": "0.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": "
|
|
116
|
+
"contentHash": "377d5037bbd8893b148874afb94991f8b493eeeb7b00a60c4ea19fbe4a5a1926"
|
|
111
117
|
}
|
|
112
118
|
}
|