dsh-agent-plugins-market 0.5.1 → 0.5.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/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { mountSuiteContext } from './context.js';
2
1
  import { Catalog } from './application/catalog.js';
3
2
  import { RuntimeReconciler } from './runtime/reconciler.js';
4
3
  import { inspectToolRegistry } from './runtime/tool-registry-observer.js';
@@ -60,12 +59,10 @@ export function apply(ctx, config = {}) {
60
59
  providerControl = control;
61
60
  return new SuiteSkillProvider(catalog, key => hostLocale.t(key));
62
61
  });
63
- const disposeContext = mountSuiteContext(ctx, catalog, key => hostLocale.t(key));
64
62
  ctx.inject(['webServer', 'loader'], hostCtx => {
65
63
  hostCtx.effect(() => mountSuiteRoutes(hostCtx, catalog), 'dsh-agent-plugins-market: http routes');
66
64
  });
67
65
  ctx.effect(() => () => {
68
66
  void runtime.dispose();
69
- disposeContext();
70
67
  }, 'dsh-agent-plugins-market: lifecycle');
71
68
  }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Host-side runtime locale: bilingual copy for strings injected into agent
3
- * context (agent-definition wrappers, command acknowledgements, the
4
- * `agent_plugins` tool), resolved from the harness's `locale.preference`
5
- * setting.
3
+ * context (agent-definition wrappers and command acknowledgements), resolved
4
+ * from the harness's `locale.preference` setting.
6
5
  *
7
6
  * The web client resolves locale through its own injected service; the host
8
7
  * process has no such service, so this module reads `$DSH_HOME/settings.yaml`
@@ -16,26 +15,14 @@ const zh = {
16
15
  agentDefinitionIntro: '当任务匹配下方描述时,通过 subagent 工具创建子代理并将「定义正文」原样作为指令执行。',
17
16
  commandForwardTitle: '[Agent Plugins 命令 /{command}(来自 {suite})]',
18
17
  commandAcknowledged: '/{command} 已转交模型执行({suite})',
19
- agentCommandHint: '子代理',
20
- agentPluginsToolDescription: '查询当前会话启用的 Agent Plugins:列出插件清单、技能与 MCP 工具前缀;技能正文通过 skill 工具加载。',
21
- agentPluginsListAction: 'list: 列出全部 Agent Plugins;info: 查看单个 Agent Plugins 详情',
22
- agentPluginsSuiteId: 'info 动作时必填:Agent Plugins id',
23
- agentPluginsSourceId: 'info 动作时可选:Agent Plugins 所属仓库源 id',
24
- agentPluginsListNote: '技能正文通过 skill 工具按 name 加载;MCP 工具名形如 mcp__<plugin>__<server>__<tool>。',
25
- agentPluginsNotFound: '未找到 Agent Plugins "{suiteId}"(仅列出用户级已启用 Agent Plugins)'
18
+ agentCommandHint: '子代理'
26
19
  };
27
20
  const en = {
28
21
  agentDefinitionTitle: '## Subagent definition (from Agent Plugins {suite}, Claude Code agents format)',
29
22
  agentDefinitionIntro: 'When the task matches the description below, create a subagent through the subagent tool and pass the definition body verbatim as its instructions.',
30
23
  commandForwardTitle: '[Agent Plugins command /{command} (from {suite})]',
31
24
  commandAcknowledged: '/{command} forwarded to the model for execution ({suite})',
32
- agentCommandHint: 'subagent',
33
- agentPluginsToolDescription: 'Query the Agent Plugins enabled in the current session: list suites, skills, and MCP tool prefixes; skill bodies load through the skill tool.',
34
- agentPluginsListAction: 'list: list every Agent Plugin; info: inspect one Agent Plugin in detail',
35
- agentPluginsSuiteId: 'required for the info action: the Agent Plugin id',
36
- agentPluginsSourceId: 'optional for the info action: the source repository id of the Agent Plugin',
37
- agentPluginsListNote: 'Load skill bodies through the skill tool by name; MCP tool names look like mcp__<plugin>__<server>__<tool>.',
38
- agentPluginsNotFound: 'Agent Plugin "{suiteId}" not found (only user-dimension enabled Agent Plugins are listed)'
25
+ agentCommandHint: 'subagent'
39
26
  };
40
27
  const DICTS = { zh, en };
41
28
  /** Resolve the active host language from settings; unknown values default to zh. */
@@ -5,6 +5,8 @@ export class McpMountRegistry {
5
5
  live = new Map();
6
6
  names = new Map();
7
7
  overridesProvider = async () => new Map();
8
+ /** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
9
+ reconcileQueue = Promise.resolve();
8
10
  constructor(ctx, pluginDataRoot) {
9
11
  this.ctx = ctx;
10
12
  this.pluginDataRoot = pluginDataRoot;
@@ -13,8 +15,14 @@ export class McpMountRegistry {
13
15
  setOverridesProvider(provider) {
14
16
  this.overridesProvider = provider;
15
17
  }
16
- /** Mount/unmount MCP servers to match the enabled suites exactly. */
18
+ /** Queue one reconciliation behind any in-flight mount/unmount pass. */
17
19
  async reconcile(enabledSuites) {
20
+ const run = this.reconcileQueue.then(() => this.reconcileNow(enabledSuites));
21
+ this.reconcileQueue = run.then(() => undefined, () => undefined);
22
+ return run;
23
+ }
24
+ /** Mount/unmount MCP servers to match the enabled suites exactly. */
25
+ async reconcileNow(enabledSuites) {
18
26
  const active = enabledSuites.filter(suite => suite.activeSurfaces?.mcp !== false);
19
27
  const overrides = await this.overridesProvider();
20
28
  const wanted = new Map();
@@ -43,11 +51,15 @@ export class McpMountRegistry {
43
51
  }
44
52
  return diagnostics.filter(diagnostic => diagnostic.reason !== 'unmounted');
45
53
  }
46
- /** Dispose every live mount; used at plugin teardown. */
54
+ /** Dispose every live mount after queued reconciliation passes settle. */
47
55
  async disposeAll() {
48
- for (const [key, live] of [...this.live]) {
49
- await this.unmount(key, live);
50
- }
56
+ const run = this.reconcileQueue.then(async () => {
57
+ for (const [key, live] of [...this.live]) {
58
+ await this.unmount(key, live);
59
+ }
60
+ });
61
+ this.reconcileQueue = run.then(() => undefined, () => undefined);
62
+ await run;
51
63
  }
52
64
  /** Mount one precomputed request (source config merged with overrides). */
53
65
  async mountWith(request) {
@@ -3,9 +3,10 @@
3
3
  *
4
4
  * Function plugin (named exports, no default export). It registers one skill
5
5
  * provider feeding enabled suites into `ctx.skills`, reconciles enabled
6
- * suites' `mcp.json` servers into live `dsh-mcp-client` mounts, injects the
7
- * enabled-suite catalog at session start, registers the `agent_plugins`
8
- * tool, and mounts the market page's HTTP routes on the web server.
6
+ * suites' `mcp.json` servers into live `dsh-mcp-client` mounts, and mounts the
7
+ * market page's HTTP routes on the web server. Skills and MCP tools are
8
+ * exposed through the host's native model surfaces; this plugin does not
9
+ * register a redundant suite-inventory model tool.
9
10
  *
10
11
  * Requires `ctx.skills` (the dsh skill registry). The MCP client package is
11
12
  * optional at runtime: without it, suites still load their skills and the
@@ -1,5 +1,5 @@
1
1
  /** Host runtime dictionary keys (mirrored for zh and en). */
2
- export type HostLocaleKey = 'agentDefinitionTitle' | 'agentDefinitionIntro' | 'commandForwardTitle' | 'commandAcknowledged' | 'agentCommandHint' | 'agentPluginsToolDescription' | 'agentPluginsListAction' | 'agentPluginsSuiteId' | 'agentPluginsSourceId' | 'agentPluginsListNote' | 'agentPluginsNotFound';
2
+ export type HostLocaleKey = 'agentDefinitionTitle' | 'agentDefinitionIntro' | 'commandForwardTitle' | 'commandAcknowledged' | 'agentCommandHint';
3
3
  export type HostTranslate = (key: HostLocaleKey, params?: Record<string, string>) => string;
4
4
  interface ResolvedLocale {
5
5
  t: HostTranslate;
@@ -23,12 +23,16 @@ export declare class McpMountRegistry {
23
23
  private readonly live;
24
24
  private readonly names;
25
25
  private overridesProvider;
26
+ /** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
27
+ private reconcileQueue;
26
28
  constructor(ctx: Context, pluginDataRoot: string);
27
29
  /** Install the per-suite overrides provider (suiteId -> overrides). */
28
30
  setOverridesProvider(provider: () => Promise<Map<string, McpSuiteOverrides>>): void;
29
- /** Mount/unmount MCP servers to match the enabled suites exactly. */
31
+ /** Queue one reconciliation behind any in-flight mount/unmount pass. */
30
32
  reconcile(enabledSuites: Suite[]): Promise<McpMountDiagnostic[]>;
31
- /** Dispose every live mount; used at plugin teardown. */
33
+ /** Mount/unmount MCP servers to match the enabled suites exactly. */
34
+ private reconcileNow;
35
+ /** Dispose every live mount after queued reconciliation passes settle. */
32
36
  disposeAll(): Promise<void>;
33
37
  /** Mount one precomputed request (source config merged with overrides). */
34
38
  private mountWith;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-agent-plugins-market",
3
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.5.1",
4
+ "version": "0.5.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
package/lib/context.js DELETED
@@ -1,78 +0,0 @@
1
- import { deriveServerName } from './runtime/mcp-config.js';
2
- /** Mount the agent_plugins query tool. */
3
- export function mountSuiteContext(ctx, manager, t) {
4
- const disposers = [];
5
- ctx.inject(['tools'], toolsCtx => {
6
- const tools = toolsCtx;
7
- if (typeof tools.tools?.register !== 'function')
8
- return;
9
- const parameters = {
10
- type: 'object',
11
- properties: {
12
- action: { type: 'string', enum: ['list', 'info'], description: t('agentPluginsListAction') },
13
- suiteId: { type: 'string', description: t('agentPluginsSuiteId') },
14
- sourceId: { type: 'string', description: t('agentPluginsSourceId') }
15
- },
16
- required: ['action'],
17
- additionalProperties: false
18
- };
19
- const outputSchema = {
20
- type: 'object',
21
- properties: {
22
- suites: { type: 'array', items: { type: 'object' } },
23
- skills: { type: 'array', items: { type: 'object' } },
24
- mcpServers: { type: 'array', items: { type: 'object' } },
25
- note: { type: 'string' }
26
- }
27
- };
28
- const dispose = tools.tools.register({
29
- name: 'agent_plugins',
30
- description: t('agentPluginsToolDescription'),
31
- parameters,
32
- renderIntent: 'generic',
33
- output: {
34
- schema: outputSchema,
35
- render: (_args, value) => {
36
- const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
37
- return [{ type: 'text', text }];
38
- }
39
- },
40
- execute: async (args) => {
41
- const record = args;
42
- const action = record['action'];
43
- if (action === 'list')
44
- return listPayload(await manager.enabledUserSuites(), t);
45
- if (action === 'info')
46
- return infoPayload(await manager.enabledUserSuites(), record, t);
47
- return { suites: [], skills: [], mcpServers: [], note: `unknown action ${JSON.stringify(action)}` };
48
- }
49
- });
50
- disposers.push(dispose);
51
- });
52
- return () => {
53
- for (const dispose of disposers)
54
- dispose();
55
- };
56
- }
57
- function listPayload(suites, t) {
58
- return {
59
- suites: suites.map(suite => ({
60
- id: suite.id,
61
- sourceId: suite.sourceId,
62
- name: suite.manifest.name,
63
- version: suite.manifest.version ?? null,
64
- description: suite.manifest.description ?? null,
65
- layout: suite.manifest.layout
66
- })),
67
- skills: suites.flatMap(suite => suite.skills.map(skill => ({ suiteId: suite.id, name: skill.name, description: `[${suite.manifest.name}] ${skill.description}` }))),
68
- mcpServers: suites.flatMap(suite => suite.mcp === undefined ? [] : Object.keys(suite.mcp.servers).map(key => ({ suiteId: suite.id, server: key, tools: `mcp__${deriveServerName(suite.id, key)}__*` }))),
69
- note: t('agentPluginsListNote')
70
- };
71
- }
72
- function infoPayload(suites, record, t) {
73
- const suiteId = record['suiteId'];
74
- const suite = suites.find(entry => entry.id === suiteId);
75
- if (suite === undefined)
76
- return { suites: [], skills: [], mcpServers: [], note: t('agentPluginsNotFound', { suiteId: String(suiteId) }) };
77
- return listPayload([suite], t);
78
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Runtime suite context: the `agent_plugins` query tool.
3
- *
4
- * Skills are injected by the native skill catalog (`dsh-tool-skill` renders
5
- * `ctx.skills` providers into the model's `<available_skills>` catalog), so
6
- * this module does not inject a duplicate session-start message. The tool
7
- * exists for facts the catalog does not carry: enabled plugins per
8
- * dimension, skill names per plugin, and MCP server prefixes.
9
- */
10
- import type { Context } from '@deepseek-ai/cordis';
11
- import type { Catalog } from './application/catalog.js';
12
- import type { HostTranslate } from './runtime/host-locale.js';
13
- /** Mount the agent_plugins query tool. */
14
- export declare function mountSuiteContext(ctx: Context, manager: Catalog, t: HostTranslate): () => void;