dsh-capability-panel 1.3.0 → 1.4.0

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.d.ts CHANGED
@@ -166,6 +166,32 @@ interface AgentCreatedPayload {
166
166
  };
167
167
  };
168
168
  }
169
+ /**
170
+ * The Host's Session controller, as far as this plugin uses it: the two
171
+ * members that open one path on the user's own desktop. Structural like every
172
+ * other service here, so nothing static is imported from
173
+ * `@deepseek-ai/dsh-api-session-controller` — and it spans this plugin's whole
174
+ * supported range, because both members exist in every generation from
175
+ * 0.1.2-rc.1 through 0.2.x. The richer members (`workspaceDesktop`,
176
+ * `workspacePathApplications`) only appear from 0.1.7-rc.2, so they stay unused
177
+ * rather than optional-by-extra-probing.
178
+ */
179
+ interface SessionControllerLike {
180
+ /**
181
+ * Whether this deployment can hand one path to a native desktop. `false` is
182
+ * an answer rather than a failure: either the deployment turned native
183
+ * opening off, or the platform announces no desktop at all.
184
+ */
185
+ canOpenWorkspacePath(): boolean;
186
+ /**
187
+ * Verify the path through the composed filesystem, then open it natively.
188
+ * Rejects with the Host's own error when no verified mapping exists, which is
189
+ * how a sandboxed deployment refuses a path outside its roots.
190
+ */
191
+ openWorkspacePath(request: {
192
+ readonly path: string;
193
+ }, signal: AbortSignal): Promise<unknown>;
194
+ }
169
195
  interface HostServices {
170
196
  /**
171
197
  * This plugin's own cordis fiber, read for the profile entry id that 0.1.7
@@ -201,6 +227,7 @@ interface HostServices {
201
227
  get(name: 'agents', strict?: boolean): AgentsService | undefined;
202
228
  get(name: 'agentPresets', strict?: boolean): AgentPresetsService | undefined;
203
229
  get(name: 'settings', strict?: boolean): SettingsService | undefined;
230
+ get(name: 'sessionController', strict?: boolean): SessionControllerLike | undefined;
204
231
  get(name: 'skills', strict?: boolean): SkillsService | undefined;
205
232
  get(name: 'tools', strict?: boolean): ToolsService | undefined;
206
233
  on(event: 'agent/created',
@@ -285,6 +312,12 @@ interface SkillSummary {
285
312
  readonly modelInvocable?: unknown;
286
313
  };
287
314
  readonly resourceBase?: unknown;
315
+ /**
316
+ * The absolute instruction file path, when the provider supplies one —
317
+ * `SKILL.md` for a directory bundle, the file itself for a flat Markdown
318
+ * skill, absent for a virtual skill registered in memory.
319
+ */
320
+ readonly path?: unknown;
288
321
  }
289
322
  interface SkillDefinitionLike {
290
323
  readonly name?: unknown;
@@ -293,6 +326,8 @@ interface SkillDefinitionLike {
293
326
  readonly source?: unknown;
294
327
  readonly provider?: unknown;
295
328
  readonly resourceBase?: unknown;
329
+ /** The provider's instruction-file path, inherited by the panel's shadow. */
330
+ readonly path?: unknown;
296
331
  }
297
332
  interface IncomingLike$1 extends IncomingLike {
298
333
  on?(event: 'data', listener: (chunk: unknown) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/loopback.ts","../src/host/types.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;AACiB,UAAA,YAAA,CAAY;;;oBAGT;ECkBH;AAQjB;;;EAQY,SAAA,MAAA,CAAA,EAAA;IAAO,SAAA,aAAA,CAAA,EAAA,MAAA;EAkBF,CAAA;AAMjB;;;;AAAA;AAoBA;AAKA;;;;;AAa8B,UA9Eb,eAAA,CA8Ea;EAAO,SAAA,QAAA,CAAA,EAAA,OAAA;EA8BpB;EAWA,SAAA,OAAA,CAAA,EAAA;IAgBA,SAAA,EAAA,CAAA,EAAe,OAAA;IAMkF,SAAA,IAAA,CAAA,EAAA,OAAA;IAAlB,SAAA,MAAA,CAAA,EAAA,OAAA;EAKnC,CAAA;EAEc,QAAA,EAAA,EAhJ7D,OAgJ6D,CAAA,IAAA,CAAA;EAED,OAAA,EAAA,EAjJ7D,OAiJ6D,CAAA,IAAA,CAAA;;AAEqB,UAhJ9E,UAAA,CAgJ8E;EAW9E,OAAA,EAAA,EA1JJ,QA0JiB,CA1JR,eA0JQ,CAAA;EACsC;;;;;AAIpE;EAMiB,KAAA,GAAA,EA9JL,OA8JK,CAAA,OAAmB,CAAA;AAOpC;AAkES,UArNQ,aAAA,CAqNR;EAAO,GAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EApNU,SAoNV,GAAA,SAAA;EAqBC;EAIA,IAAA,EAAA,EA3OP,SA2OgB,EAAA;AAiB1B;AASiB,UAlQA,eAAA,CAkQmB;EASnB,SAAA,EAAA,EAAA,MAAa;EAMb;;;;ACrTjB;EAGgB,SAAK,KAAA,CAAA,EAAA,QAAM,GAAA,MAAY;EA6B1B;;;;;;;;;;;UDwBI,cAAA;;2BAEU;;UAGV,mBAAA;UACP,QAAQ;;;;;;gCAMc;;;;;;8BAMF,QAAQ;;;UA8BrB;SACR;;;;;;4BAMmB;;;UAIX,kBAAA;;;;;;;;;;;;;;;UAgBA,eAAA;;;;;;;;MAM+E,kBAAkB;;;;;;;eAKrD;;2EAEc;;0EAED;;wFAEc;;UAWvE,aAAA;;;;MACkC,iBAAiB;;;;MACJ,QAAQ;;UAGvD,YAAA;4BACW;;;;;;;;;;;;UAKX,mBAAA;kBACC;;;0BAEsB;;;;UAIvB,YAAA;;;;;;;;;;;;;;;;;;;qBAeI,qBAAmB,uBAAuB;;;;;;;;;;;;yCAYtB;yCACA;+CACM;2CACJ;yCACF;wCACD;;;;;;;;sBAShB,+BAA+B;;iGAKS;;mDAKrC;;;;;;;;;;;;;;;;;;;;;;;;iBAcT;;;;SACT;;;;;;;UAqBQ,mBAAA;;;;;UAIA,SAAA;;;;;;;;;;;;;;;;;;;;;;UAiBA,YAAA;;;;;;;;;;UASA,mBAAA;;;;;;;;UASA,cAAA,SAAqB;;;;;UAMrB,kBAAA;qCACoB;;;;;AD/UrC;;;;ACqBA;AAQA;;;;;AA0BA;AAMA;AAoBA;AAKA;;AACU,cC9DG,MD8DH,EC9DS,WD8DT,CAAA,GAAA,EAAA,GAAA,EAAA,OAAA,CAAA;;AAY4B,iBCvEtB,KAAA,CDuEsB,GAAA,ECvEX,YDuEW,CAAA,EAAA,IAAA;AAAR,cC1CjB,MD0CiB,EAAA,MAAA,EAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/loopback.ts","../src/host/types.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;AACiB,UAAA,YAAA,CAAY;;;oBAGT;ECkBH;AAQjB;;;EAQY,SAAA,MAAA,CAAA,EAAA;IAAO,SAAA,aAAA,CAAA,EAAA,MAAA;EAkBF,CAAA;AAMjB;;;;AAAA;AAoBA;AAKA;;;;;AAa8B,UA9Eb,eAAA,CA8Ea;EAAO,SAAA,QAAA,CAAA,EAAA,OAAA;EA8BpB;EAWA,SAAA,OAAA,CAAA,EAAA;IAgBA,SAAA,EAAA,CAAA,EAAe,OAAA;IAMkF,SAAA,IAAA,CAAA,EAAA,OAAA;IAAlB,SAAA,MAAA,CAAA,EAAA,OAAA;EAKnC,CAAA;EAEc,QAAA,EAAA,EAhJ7D,OAgJ6D,CAAA,IAAA,CAAA;EAED,OAAA,EAAA,EAjJ7D,OAiJ6D,CAAA,IAAA,CAAA;;AAEqB,UAhJ9E,UAAA,CAgJ8E;EAW9E,OAAA,EAAA,EA1JJ,QA0JiB,CA1JR,eA0JQ,CAAA;EACsC;;;;;AAIpE;EAMiB,KAAA,GAAA,EA9JL,OA8JK,CAAA,OAAmB,CAAA;AAiBpC;AAmE2B,UAhOV,aAAA,CAgOU;EAcT,GAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EA7OQ,SA6OR,GAAA,SAAA;EACT;EAAO,IAAA,EAAA,EA5ON,SA4OM,EAAA;AAqBhB;AAIiB,UAlQA,eAAA,CAkQS;EAiBT,SAAA,EAAA,EAAA,MAAY;EAeZ;AAWjB;AAMA;;;;ECvVa;EAGG,SAAK,IAAA,CAAA,EAAA,MAAM;EA6Bd,SAAsB,IAAA,CAAA,EAAA,MAAA;;;;;;;;;UDwBlB,cAAA;;2BAEU;;UAGV,mBAAA;UACP,QAAQ;;;;;;gCAMc;;;;;;8BAMF,QAAQ;;;UA8BrB;SACR;;;;;;4BAMmB;;;UAIX,kBAAA;;;;;;;;;;;;;;;UAgBA,eAAA;;;;;;;;MAM+E,kBAAkB;;;;;;;eAKrD;;2EAEc;;0EAED;;wFAEc;;UAWvE,aAAA;;;;MACkC,iBAAiB;;;;MACJ,QAAQ;;UAGvD,YAAA;4BACW;;;;;;;;;;;;UAKX,mBAAA;kBACC;;;0BAEsB;;;;;;;;;;;;;;UAcvB,qBAAA;;;;;;;;;;;;;;aAY+C,cAAc;;UAG7D,YAAA;;;;;;;;;;;;;;;;;;;qBAeI,qBAAmB,uBAAuB;;;;;;;;;;;;yCAYtB;yCACA;+CACM;2CACJ;oDACS;yCACX;wCACD;;;;;;;;sBAShB,+BAA+B;;iGAKS;;mDAKrC;;;;;;;;;;;;;;;;;;;;;;;;iBAcT;;;;SACT;;;;;;;UAqBQ,mBAAA;;;;;UAIA,SAAA;;;;;;;;;;;;;;;;;;;;;;UAiBA,YAAA;;;;;;;;;;;;;;;;UAeA,mBAAA;;;;;;;;;;UAWA,cAAA,SAAqB;;;;;UAMrB,kBAAA;qCACoB;;;;;ADjXrC;;;;ACqBA;AAQA;;;;;AA0BA;AAMA;AAoBA;AAKA;;AACU,cC9DG,MD8DH,EC9DS,WD8DT,CAAA,GAAA,EAAA,GAAA,EAAA,OAAA,CAAA;;AAY4B,iBCvEtB,KAAA,CDuEsB,GAAA,ECvEX,YDuEW,CAAA,EAAA,IAAA;AAAR,cC1CjB,MD0CiB,EAAA,MAAA,EAAA"}
package/lib/index.js CHANGED
@@ -252,7 +252,7 @@ function decideStates(available, loads, shadowedSeqs, disabledSkills = /* @__PUR
252
252
  if (bucket === void 0) byName.set(record.skillName, [record]);
253
253
  else bucket.push(record);
254
254
  }
255
- return available.map(({ name, description, masked, source, provider, path, group }) => {
255
+ return available.map(({ name, description, masked, source, provider, path, fileAddress, group }) => {
256
256
  const records = byName.get(name) ?? [];
257
257
  let state = "unloaded";
258
258
  if (records.length > 0) {
@@ -269,6 +269,7 @@ function decideStates(available, loads, shadowedSeqs, disabledSkills = /* @__PUR
269
269
  source,
270
270
  provider,
271
271
  ...path === void 0 ? {} : { path },
272
+ ...fileAddress === void 0 ? {} : { fileAddress },
272
273
  ...group === void 0 ? {} : { group }
273
274
  };
274
275
  });
@@ -449,6 +450,7 @@ function createCapabilityController(ctx, appendStats, blockedCounts) {
449
450
  source: typeof original.source === "string" ? original.source : "custom",
450
451
  provider: typeof original.provider === "string" ? original.provider : "capability-panel",
451
452
  ...original.resourceBase === void 0 ? {} : { resourceBase: original.resourceBase },
453
+ ...typeof original.path === "string" ? { path: original.path } : {},
452
454
  invocation: {
453
455
  modelInvocable: false,
454
456
  userInvocable: true
@@ -594,6 +596,7 @@ function createCapabilityController(ctx, appendStats, blockedCounts) {
594
596
  source: typeof original.source === "string" ? original.source : "custom",
595
597
  provider: typeof original.provider === "string" ? original.provider : "capability-panel",
596
598
  ...original.resourceBase === void 0 ? {} : { resourceBase: original.resourceBase },
599
+ ...typeof original.path === "string" ? { path: original.path } : {},
597
600
  invocation: {
598
601
  modelInvocable: false,
599
602
  userInvocable: true
@@ -7815,6 +7818,44 @@ async function restartMcpServer(ctx, server) {
7815
7818
  await entry.refresh();
7816
7819
  }
7817
7820
 
7821
+ //#endregion
7822
+ //#region src/host/skill-preview.ts
7823
+ /** The single member used from `@deepseek-ai/dsh-util-workspace-path`. */
7824
+ function pickFileAddressFor(module) {
7825
+ const candidate = module.fileAddressFor;
7826
+ return typeof candidate === "function" ? candidate : void 0;
7827
+ }
7828
+ let probed = false;
7829
+ let builder;
7830
+ /**
7831
+ * Resolve the Host's address helper once, or `undefined` when this deployment
7832
+ * has no such export. The probe result is remembered either way: a missing
7833
+ * package cannot appear later in one process's life.
7834
+ * @returns the helper, when the running Host ships it.
7835
+ */
7836
+ async function loadFileAddressFor() {
7837
+ if (!probed) {
7838
+ probed = true;
7839
+ try {
7840
+ builder = pickFileAddressFor(await import("@deepseek-ai/dsh-util-workspace-path"));
7841
+ } catch {
7842
+ builder = void 0;
7843
+ }
7844
+ }
7845
+ return builder;
7846
+ }
7847
+ /**
7848
+ * The address that opens `file` in the Host's right-sidebar preview.
7849
+ * @param forFile - the resolved helper; `undefined` means this Host cannot.
7850
+ * @param sessionId - the Session whose workspace resolves the path.
7851
+ * @param cwd - that Session's workspace root, when the Host reported one.
7852
+ * @param file - the skill's absolute instruction file path.
7853
+ * @returns the address, or `undefined` when the preview is unavailable.
7854
+ */
7855
+ function previewAddressFor(forFile, sessionId, cwd, file) {
7856
+ return forFile === void 0 ? void 0 : forFile(sessionId, cwd, file);
7857
+ }
7858
+
7818
7859
  //#endregion
7819
7860
  //#region src/host/catalog.ts
7820
7861
  /** Coerce a skill summary field to a string, falling back when the runtime shape is wrong. */
@@ -7875,6 +7916,7 @@ async function readAvailable(services, sessionId, degraded, presetDirs = []) {
7875
7916
  ...cwd === void 0 ? {} : { cwd },
7876
7917
  scope: agent
7877
7918
  });
7919
+ const forFile = await loadFileAddressFor();
7878
7920
  const out = [];
7879
7921
  const seen = /* @__PURE__ */ new Map();
7880
7922
  const groupFor = (source, rawRoot) => {
@@ -7892,6 +7934,8 @@ async function readAvailable(services, sessionId, degraded, presetDirs = []) {
7892
7934
  const rawRoot = rawPath === "" ? "" : parentDir(rawPath);
7893
7935
  const path = rawRoot === "" ? "" : displayPath(rawRoot, cwd);
7894
7936
  const group = groupFor(source, rawRoot);
7937
+ const file = coerceString(item.path, "");
7938
+ const fileAddress = file === "" ? void 0 : previewAddressFor(forFile, sessionId, cwd, file);
7895
7939
  const existing = seen.get(item.name);
7896
7940
  if (existing !== void 0) {
7897
7941
  if (masked) existing.masked = true;
@@ -7899,6 +7943,7 @@ async function readAvailable(services, sessionId, degraded, presetDirs = []) {
7899
7943
  existing.source = source;
7900
7944
  existing.provider = provider;
7901
7945
  if (path !== "") existing.path = path;
7946
+ if (fileAddress !== void 0) existing.fileAddress = fileAddress;
7902
7947
  if (group !== void 0) existing.group = group;
7903
7948
  }
7904
7949
  continue;
@@ -7911,6 +7956,7 @@ async function readAvailable(services, sessionId, degraded, presetDirs = []) {
7911
7956
  source,
7912
7957
  provider,
7913
7958
  ...path === "" ? {} : { path },
7959
+ ...fileAddress === void 0 ? {} : { fileAddress },
7914
7960
  ...group === void 0 ? {} : { group }
7915
7961
  };
7916
7962
  seen.set(item.name, row);
@@ -8439,13 +8485,43 @@ function isLoopback(req) {
8439
8485
  */
8440
8486
  function openFolder(path) {
8441
8487
  return new Promise((resolve, reject) => {
8442
- exec(`${process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"} "${path}"`, (error) => {
8488
+ exec(`${process.platform === "darwin" ? "open" : process.platform === "win32" ? "start \"\"" : "xdg-open"} "${path}"`, (error) => {
8443
8489
  if (error) reject(/* @__PURE__ */ new Error(`failed to open folder: ${error.message}`));
8444
8490
  else resolve();
8445
8491
  });
8446
8492
  });
8447
8493
  }
8448
8494
 
8495
+ //#endregion
8496
+ //#region src/host/open-source-folder.ts
8497
+ /**
8498
+ * Whether the resolved service really carries both members this route calls.
8499
+ * Probed rather than assumed, exactly like the other optional services: the
8500
+ * service name is stable across generations, but a composition is free to
8501
+ * mount a partial implementation.
8502
+ */
8503
+ function isUsableOpener(opener) {
8504
+ return opener !== void 0 && typeof opener.canOpenWorkspacePath === "function" && typeof opener.openWorkspacePath === "function";
8505
+ }
8506
+ /**
8507
+ * Open `path` through the Host when it can, else through `fallback`.
8508
+ * @param opener - the Session controller, when the composition mounts one.
8509
+ * @param path - absolute folder path already resolved from the source key.
8510
+ * @param fallback - this plugin's own opener, used where the Host cannot.
8511
+ * @returns which opener ran.
8512
+ */
8513
+ async function openSourceFolder(opener, path, fallback) {
8514
+ if (isUsableOpener(opener)) {
8515
+ if (!opener.canOpenWorkspacePath()) return "unsupported";
8516
+ try {
8517
+ await opener.openWorkspacePath({ path }, new AbortController().signal);
8518
+ return "host";
8519
+ } catch {}
8520
+ }
8521
+ await fallback(path);
8522
+ return "local";
8523
+ }
8524
+
8449
8525
  //#endregion
8450
8526
  //#region src/host/route.ts
8451
8527
  const ROUTE = "/api/capability-panel";
@@ -8675,7 +8751,10 @@ function createRouteHandler(services, capabilities, stats, blockedCounts, preset
8675
8751
  return;
8676
8752
  }
8677
8753
  try {
8678
- await openFolder(folderPath);
8754
+ if (await openSourceFolder(services.get("sessionController", false), folderPath, openFolder) === "unsupported") {
8755
+ json(res, 500, { error: "this deployment cannot open folders on a desktop" });
8756
+ return;
8757
+ }
8679
8758
  json(res, 200, { ok: true });
8680
8759
  } catch (error) {
8681
8760
  json(res, 500, { error: `failed to open folder: ${errorMessage(error)}` });