dsh-agent-plugins-market 0.4.3 → 0.4.5
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 +78 -46
- package/README.zh.md +78 -46
- package/client/client.js +116 -10
- package/client/client.js.map +1 -1
- package/lib/index.js +1 -0
- package/lib/manager.js +85 -5
- package/lib/routes.js +3 -0
- package/lib/types/manager.d.ts +31 -0
- package/lib/types/types.d.ts +2 -0
- package/package.json +9 -2
package/lib/manager.js
CHANGED
|
@@ -20,6 +20,10 @@ export class SuiteManager {
|
|
|
20
20
|
state = EMPTY_STATE;
|
|
21
21
|
mutationQueue = Promise.resolve();
|
|
22
22
|
statePath;
|
|
23
|
+
/** Cached `git rev-parse HEAD` per source id, so overview reads stay instant; cleared whenever a checkout may have moved. */
|
|
24
|
+
headCache = new Map();
|
|
25
|
+
/** Latest MCP mount diagnostics (suiteId -> reasons), fed by the host reconcile. */
|
|
26
|
+
mcpDiagnostics = [];
|
|
23
27
|
constructor(options) {
|
|
24
28
|
this.options = options;
|
|
25
29
|
this.statePath = join(options.userRoot, STATE_FILE_NAME);
|
|
@@ -72,6 +76,7 @@ export class SuiteManager {
|
|
|
72
76
|
agents: remoteUrl === undefined ? await this.agentPreviews(`${suite.root}/agents`) : [],
|
|
73
77
|
lsp: remoteUrl === undefined ? await this.lspPreviews(suite.root) : [],
|
|
74
78
|
errors: suite.errors,
|
|
79
|
+
mcpErrors: this.mcpDiagnostics.filter(diagnostic => diagnostic.suiteId === suite.id).map(diagnostic => `${diagnostic.serverKey}: ${diagnostic.reason}`),
|
|
75
80
|
};
|
|
76
81
|
}
|
|
77
82
|
async readPreview(path, capBytes = 64 * 1024) {
|
|
@@ -101,6 +106,7 @@ export class SuiteManager {
|
|
|
101
106
|
const suites = await this.discoverDimension('user', this.options.userRoot);
|
|
102
107
|
const sourceRows = [];
|
|
103
108
|
for (const source of this.state.sources) {
|
|
109
|
+
const inFlight = this.currentSourceState?.sourceId === source.id ? this.currentSourceState : undefined;
|
|
104
110
|
const checkout = this.sourceCheckoutPath(source);
|
|
105
111
|
let cloned = false;
|
|
106
112
|
let lockCommit;
|
|
@@ -110,13 +116,27 @@ export class SuiteManager {
|
|
|
110
116
|
if (!cloned)
|
|
111
117
|
error = `local source directory ${checkout} is missing`;
|
|
112
118
|
}
|
|
119
|
+
else if (inFlight !== undefined) {
|
|
120
|
+
// A mutation owns this source right now (clone/pull in progress): report
|
|
121
|
+
// its snapshot instead of racing git against the transient directory.
|
|
122
|
+
cloned = inFlight.cloned || await isDirectory(checkout);
|
|
123
|
+
lockCommit = inFlight.head;
|
|
124
|
+
}
|
|
113
125
|
else {
|
|
114
|
-
|
|
115
|
-
|
|
126
|
+
const cachedHead = this.headCache.get(source.id);
|
|
127
|
+
if (cachedHead !== undefined && await isDirectory(checkout)) {
|
|
116
128
|
cloned = true;
|
|
129
|
+
lockCommit = cachedHead;
|
|
117
130
|
}
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
else {
|
|
132
|
+
try {
|
|
133
|
+
lockCommit = await gitHead(checkout);
|
|
134
|
+
cloned = true;
|
|
135
|
+
this.headCache.set(source.id, lockCommit);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// not cloned yet (or a broken checkout); the clone attempt records its own error on refresh
|
|
139
|
+
}
|
|
120
140
|
}
|
|
121
141
|
}
|
|
122
142
|
const sourceSuites = suites.filter(suite => suite.sourceId === source.id);
|
|
@@ -145,6 +165,7 @@ export class SuiteManager {
|
|
|
145
165
|
dimension: suite.dimension,
|
|
146
166
|
layout: suite.manifest.layout,
|
|
147
167
|
errors: suite.errors,
|
|
168
|
+
mcpErrors: this.mcpDiagnostics.filter(diagnostic => diagnostic.suiteId === suite.id).map(diagnostic => `${diagnostic.serverKey}: ${diagnostic.reason}`),
|
|
148
169
|
}));
|
|
149
170
|
return {
|
|
150
171
|
sources: sourceRows,
|
|
@@ -194,15 +215,59 @@ export class SuiteManager {
|
|
|
194
215
|
throw new Error(`local source directory ${checkout} is missing`);
|
|
195
216
|
}
|
|
196
217
|
else {
|
|
197
|
-
|
|
218
|
+
this.beginSourceState(baseId, 'cloning', false);
|
|
219
|
+
try {
|
|
220
|
+
await this.ensureClone(source);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
this.endSourceState();
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
this.updateSourceStep('reading');
|
|
198
227
|
}
|
|
199
228
|
source.id = this.uniqueSourceId(sanitizeId(await repoName(checkout)));
|
|
229
|
+
const head = await tryHead(checkout);
|
|
230
|
+
if (head !== undefined)
|
|
231
|
+
this.headCache.set(source.id, head);
|
|
232
|
+
this.endSourceState();
|
|
200
233
|
this.state = { ...this.state, sources: [...this.state.sources, source] };
|
|
201
234
|
await saveState(this.statePath, this.state);
|
|
202
235
|
this.options.onChanged();
|
|
203
236
|
return source;
|
|
204
237
|
});
|
|
205
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Progress snapshot of the source mutation currently in flight, if any.
|
|
241
|
+
* While present, `overview()` reports it instead of running git against
|
|
242
|
+
* the transient checkout directory.
|
|
243
|
+
*/
|
|
244
|
+
currentSourceState;
|
|
245
|
+
/**
|
|
246
|
+
* Begin reporting progress for a source mutation: `step` names the active
|
|
247
|
+
* phase and `cloned` whether the checkout directory exists yet. The client
|
|
248
|
+
* polls this through the `progress` route while its add-source modal is
|
|
249
|
+
* open, and `overview()` merges the snapshot into the source row.
|
|
250
|
+
*/
|
|
251
|
+
beginSourceState(sourceId, step, cloned) {
|
|
252
|
+
this.currentSourceState = { sourceId, step, cloned };
|
|
253
|
+
}
|
|
254
|
+
/** Advance the in-flight mutation's step (checkout state unchanged). */
|
|
255
|
+
updateSourceStep(step) {
|
|
256
|
+
if (this.currentSourceState !== undefined) {
|
|
257
|
+
this.currentSourceState = { ...this.currentSourceState, step };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/** Stop reporting progress; `overview()` falls back to on-disk inspection. */
|
|
261
|
+
endSourceState() {
|
|
262
|
+
this.currentSourceState = undefined;
|
|
263
|
+
}
|
|
264
|
+
/** Progress snapshot for the `progress` route (never throws). */
|
|
265
|
+
sourceProgress() {
|
|
266
|
+
const state = this.currentSourceState;
|
|
267
|
+
return state === undefined
|
|
268
|
+
? { active: false, sourceId: '', step: '' }
|
|
269
|
+
: { active: true, sourceId: state.sourceId, step: state.step };
|
|
270
|
+
}
|
|
206
271
|
uniqueSourceId(derived) {
|
|
207
272
|
if (!this.state.sources.some(source => source.id === derived))
|
|
208
273
|
return derived;
|
|
@@ -230,6 +295,7 @@ export class SuiteManager {
|
|
|
230
295
|
...(patch.local !== undefined ? { local: patch.local } : current.local === undefined ? {} : { local: current.local }),
|
|
231
296
|
};
|
|
232
297
|
if (current.local !== true && patch.url !== undefined && patch.url !== current.url) {
|
|
298
|
+
this.headCache.delete(sourceId);
|
|
233
299
|
await gitRemove(sourceCheckoutDir(this.options.userRoot, sourceId));
|
|
234
300
|
}
|
|
235
301
|
this.state = { ...this.state, sources: this.state.sources.map((source, i) => i === index ? next : source) };
|
|
@@ -247,6 +313,7 @@ export class SuiteManager {
|
|
|
247
313
|
};
|
|
248
314
|
await saveState(this.statePath, this.state);
|
|
249
315
|
const source = this.state.sources.find(entry => entry.id === sourceId);
|
|
316
|
+
this.headCache.delete(sourceId);
|
|
250
317
|
if (source === undefined || source.local !== true) {
|
|
251
318
|
await gitRemove(sourceCheckoutDir(this.options.userRoot, sourceId));
|
|
252
319
|
}
|
|
@@ -265,14 +332,27 @@ export class SuiteManager {
|
|
|
265
332
|
continue;
|
|
266
333
|
}
|
|
267
334
|
const checkout = sourceCheckoutDir(this.options.userRoot, source.id);
|
|
335
|
+
this.headCache.delete(source.id);
|
|
268
336
|
try {
|
|
269
337
|
await gitHead(checkout);
|
|
270
338
|
}
|
|
271
339
|
catch {
|
|
272
340
|
await this.ensureClone(source);
|
|
341
|
+
try {
|
|
342
|
+
this.headCache.set(source.id, await gitHead(checkout));
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// a failed head read simply means the next overview re-probes
|
|
346
|
+
}
|
|
273
347
|
continue;
|
|
274
348
|
}
|
|
275
349
|
await gitPull(checkout);
|
|
350
|
+
try {
|
|
351
|
+
this.headCache.set(source.id, await gitHead(checkout));
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// a failed head read simply means the next overview re-probes
|
|
355
|
+
}
|
|
276
356
|
}
|
|
277
357
|
this.options.onChanged();
|
|
278
358
|
});
|
package/lib/routes.js
CHANGED
|
@@ -46,6 +46,9 @@ export function mountSuiteRoutes(hostCtx, manager) {
|
|
|
46
46
|
get(`${API_PREFIX}overview`, async (_request, response) => {
|
|
47
47
|
sendJson(response, 200, await manager.overview());
|
|
48
48
|
});
|
|
49
|
+
get(`${API_PREFIX}progress`, async (_request, response) => {
|
|
50
|
+
sendJson(response, 200, manager.sourceProgress());
|
|
51
|
+
});
|
|
49
52
|
get(`${API_PREFIX}config`, async (_request, response) => {
|
|
50
53
|
sendJson(response, 200, { sources: manager.sources });
|
|
51
54
|
});
|
package/lib/types/manager.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ export declare class SuiteManager {
|
|
|
9
9
|
private state;
|
|
10
10
|
private mutationQueue;
|
|
11
11
|
private readonly statePath;
|
|
12
|
+
/** Cached `git rev-parse HEAD` per source id, so overview reads stay instant; cleared whenever a checkout may have moved. */
|
|
13
|
+
private readonly headCache;
|
|
14
|
+
/** Latest MCP mount diagnostics (suiteId -> reasons), fed by the host reconcile. */
|
|
15
|
+
mcpDiagnostics: Array<{
|
|
16
|
+
suiteId: string;
|
|
17
|
+
serverKey: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}>;
|
|
12
20
|
constructor(options: ManagerOptions);
|
|
13
21
|
/** Load persisted state once at plugin activation. */
|
|
14
22
|
load(): Promise<void>;
|
|
@@ -50,6 +58,29 @@ export declare class SuiteManager {
|
|
|
50
58
|
branch?: string;
|
|
51
59
|
local?: boolean;
|
|
52
60
|
}): Promise<SourceRef>;
|
|
61
|
+
/**
|
|
62
|
+
* Progress snapshot of the source mutation currently in flight, if any.
|
|
63
|
+
* While present, `overview()` reports it instead of running git against
|
|
64
|
+
* the transient checkout directory.
|
|
65
|
+
*/
|
|
66
|
+
private currentSourceState;
|
|
67
|
+
/**
|
|
68
|
+
* Begin reporting progress for a source mutation: `step` names the active
|
|
69
|
+
* phase and `cloned` whether the checkout directory exists yet. The client
|
|
70
|
+
* polls this through the `progress` route while its add-source modal is
|
|
71
|
+
* open, and `overview()` merges the snapshot into the source row.
|
|
72
|
+
*/
|
|
73
|
+
beginSourceState(sourceId: string, step: string, cloned: boolean): void;
|
|
74
|
+
/** Advance the in-flight mutation's step (checkout state unchanged). */
|
|
75
|
+
updateSourceStep(step: string): void;
|
|
76
|
+
/** Stop reporting progress; `overview()` falls back to on-disk inspection. */
|
|
77
|
+
endSourceState(): void;
|
|
78
|
+
/** Progress snapshot for the `progress` route (never throws). */
|
|
79
|
+
sourceProgress(): {
|
|
80
|
+
active: boolean;
|
|
81
|
+
sourceId: string;
|
|
82
|
+
step: string;
|
|
83
|
+
};
|
|
53
84
|
private uniqueSourceId;
|
|
54
85
|
/**
|
|
55
86
|
* Update one source's url / branch / local flag. A git source whose URL
|
package/lib/types/types.d.ts
CHANGED
|
@@ -137,6 +137,8 @@ export interface SuiteOverviewCard {
|
|
|
137
137
|
dimension: SuiteDimension;
|
|
138
138
|
layout: SuiteLayoutKind;
|
|
139
139
|
errors: string[];
|
|
140
|
+
/** Live MCP mount failures for this suite (reason per failed server). */
|
|
141
|
+
mcpErrors: string[];
|
|
140
142
|
}
|
|
141
143
|
export interface SourceOverview {
|
|
142
144
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agent-plugins-market",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "0.4.
|
|
3
|
+
"description": "The standard way to install Claude Code / Codex / Cursor / Kimi plugin marketplace suites in DeepSeek Harness (DSH) — zero conversion, zero file copying. Skills, MCP servers, hooks and slash commands are injected into your dsh sessions at runtime.",
|
|
4
|
+
"version": "0.4.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -56,11 +56,18 @@
|
|
|
56
56
|
"harness",
|
|
57
57
|
"dsh",
|
|
58
58
|
"dsh-plugin",
|
|
59
|
+
"dsh-plugins",
|
|
60
|
+
"dsh-marketplace",
|
|
61
|
+
"dsh-plugin-marketplace",
|
|
62
|
+
"deepseek-plugin",
|
|
63
|
+
"deepseek-plugins",
|
|
59
64
|
"agent-plugins",
|
|
60
65
|
"agent-plugin",
|
|
61
66
|
"plugin-marketplace",
|
|
62
67
|
"marketplace",
|
|
63
68
|
"claude-code",
|
|
69
|
+
"claude-code-plugins",
|
|
70
|
+
"claude-code-plugin-marketplace",
|
|
64
71
|
"claude",
|
|
65
72
|
"codex",
|
|
66
73
|
"cursor",
|