@huaqiu/dsh-kicad 0.4.2 → 0.4.3

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.mts CHANGED
@@ -267,6 +267,33 @@ declare function readBundledSkill(moduleUrl: string, override?: string): {
267
267
  description: string;
268
268
  content: string;
269
269
  };
270
+ /**
271
+ * Read the bundled skill, or `null` when the installed package does not carry
272
+ * one.
273
+ *
274
+ * ── Why this degrades instead of throwing ──────────────────────────────────
275
+ * A missing `skills/` tree is a PACKAGING failure, and the honest signal for a
276
+ * packaging failure is a loud error — but not a dead server. `apply()` runs
277
+ * inside the DSH plugin tree: throwing here aborts the whole loader
278
+ * (`plugin tree failed to load`), kills the DSH process, and takes every other
279
+ * plugin and the entire EDA session down with it, for one missing asset.
280
+ *
281
+ * That actually happened: HQ Edge's builtin-plugin staging copied only
282
+ * `package.json` + `lib/`, so `skills/` never reached the bundle and the
283
+ * server could not start at all. The correct blast radius for "this plugin's
284
+ * skill is missing" is "this plugin is degraded", so we log at error level
285
+ * with the exact path and remedy, skip skill registration, and keep the tools
286
+ * (which fail per-call with a typed `FAILED_PRECONDITION` rather than at load).
287
+ *
288
+ * @param moduleUrl - `import.meta.url` of the calling module.
289
+ * @param override - explicit skill directory (plugin config or env var).
290
+ */
291
+ declare function tryReadBundledSkill(moduleUrl: string, override?: string): {
292
+ dir: string;
293
+ name: string;
294
+ description: string;
295
+ content: string;
296
+ } | null;
270
297
  /**
271
298
  * Host plugin body — register the `kicad-ipc` skill and the KiCad tools.
272
299
  *
@@ -281,4 +308,4 @@ declare function readBundledSkill(moduleUrl: string, override?: string): {
281
308
  */
282
309
  declare function apply(ctx: Context, config?: KicadConfigInput): () => void;
283
310
  //#endregion
284
- export { KICAD_SCRIPTS, KICAD_SCRIPT_IDS, KICAD_SKILL_NAME, type KicadConfig, type KicadConfigInput, type KicadError, type KicadErrorKind, type KicadScript, type ScriptEffect, type ScriptRun, SkillRegistration, apply, classifyRun, createKicadTools, inject, invokeKicadScript, kicadScript, kicadToolNames, name, readBundledSkill, requireSkillDir, resolveSkillDir, runKicadScript, scriptsDir, skillDescription };
311
+ export { KICAD_SCRIPTS, KICAD_SCRIPT_IDS, KICAD_SKILL_NAME, type KicadConfig, type KicadConfigInput, type KicadError, type KicadErrorKind, type KicadScript, type ScriptEffect, type ScriptRun, SkillRegistration, apply, classifyRun, createKicadTools, inject, invokeKicadScript, kicadScript, kicadToolNames, name, readBundledSkill, requireSkillDir, resolveSkillDir, runKicadScript, scriptsDir, skillDescription, tryReadBundledSkill };
package/lib/index.mjs CHANGED
@@ -902,6 +902,38 @@ function readBundledSkill(moduleUrl, override) {
902
902
  };
903
903
  }
904
904
  /**
905
+ * Read the bundled skill, or `null` when the installed package does not carry
906
+ * one.
907
+ *
908
+ * ── Why this degrades instead of throwing ──────────────────────────────────
909
+ * A missing `skills/` tree is a PACKAGING failure, and the honest signal for a
910
+ * packaging failure is a loud error — but not a dead server. `apply()` runs
911
+ * inside the DSH plugin tree: throwing here aborts the whole loader
912
+ * (`plugin tree failed to load`), kills the DSH process, and takes every other
913
+ * plugin and the entire EDA session down with it, for one missing asset.
914
+ *
915
+ * That actually happened: HQ Edge's builtin-plugin staging copied only
916
+ * `package.json` + `lib/`, so `skills/` never reached the bundle and the
917
+ * server could not start at all. The correct blast radius for "this plugin's
918
+ * skill is missing" is "this plugin is degraded", so we log at error level
919
+ * with the exact path and remedy, skip skill registration, and keep the tools
920
+ * (which fail per-call with a typed `FAILED_PRECONDITION` rather than at load).
921
+ *
922
+ * @param moduleUrl - `import.meta.url` of the calling module.
923
+ * @param override - explicit skill directory (plugin config or env var).
924
+ */
925
+ function tryReadBundledSkill(moduleUrl, override) {
926
+ try {
927
+ return readBundledSkill(moduleUrl, override);
928
+ } catch (err) {
929
+ log.error("dsh-kicad: bundled skill unavailable — continuing degraded, the KiCad tools are registered but will fail until the package is reinstalled", {
930
+ expectedDir: resolveSkillDir(moduleUrl, override),
931
+ error: String(err?.message ?? err)
932
+ });
933
+ return null;
934
+ }
935
+ }
936
+ /**
905
937
  * Host plugin body — register the `kicad-ipc` skill and the KiCad tools.
906
938
  *
907
939
  * Both halves are registered here so that one installation delivers both. The
@@ -917,15 +949,17 @@ function apply(ctx, config = {}) {
917
949
  if (!ctx.tools || typeof ctx.tools.register !== "function") throw new Error("@huaqiu/dsh-kicad requires the DSH `tools` service (ctx.tools.register).");
918
950
  if (!ctx.skills || typeof ctx.skills.register !== "function") throw new Error("@huaqiu/dsh-kicad requires the DSH `skills` service (ctx.skills.register).");
919
951
  const resolved = resolveKicadConfig(config);
920
- const skill = readBundledSkill(import.meta.url, config.skillsDir);
952
+ const skill = tryReadBundledSkill(import.meta.url, config.skillsDir);
953
+ const skillDir = skill?.dir ?? resolveSkillDir(import.meta.url, config.skillsDir);
921
954
  log.info("applying dsh-kicad node half", {
922
955
  hasConfigHost: hasHostConfig(config),
923
956
  pythonPath: resolved.pythonPath,
924
- skillDir: skill.dir,
957
+ skillDir,
958
+ skillPresent: skill !== null,
925
959
  timeoutMs: resolved.timeoutMs
926
960
  });
927
961
  const disposers = [];
928
- disposers.push(ctx.skills.register({
962
+ if (skill) disposers.push(ctx.skills.register({
929
963
  name: skill.name,
930
964
  description: skill.description,
931
965
  content: skill.content,
@@ -935,13 +969,14 @@ function apply(ctx, config = {}) {
935
969
  }
936
970
  }));
937
971
  const tools = createKicadTools({
938
- scriptsDir: scriptsDir(skill.dir),
972
+ scriptsDir: scriptsDir(skillDir),
939
973
  pythonPath: resolved.pythonPath,
940
974
  config: resolved
941
975
  });
942
976
  for (const tool of tools) disposers.push(ctx.tools.register(tool));
943
977
  log.info("dsh-kicad node half ready", {
944
- skill: skill.name,
978
+ skill: skill?.name ?? null,
979
+ degraded: skill === null,
945
980
  tools: tools.length,
946
981
  expectedTools: kicadToolNames().length
947
982
  });
@@ -954,4 +989,4 @@ function apply(ctx, config = {}) {
954
989
  };
955
990
  }
956
991
  //#endregion
957
- export { KICAD_SCRIPTS, KICAD_SCRIPT_IDS, KICAD_SKILL_NAME, apply, classifyRun, createKicadTools, inject, invokeKicadScript, kicadScript, kicadToolNames, name, readBundledSkill, requireSkillDir, resolveSkillDir, runKicadScript, scriptsDir, skillDescription };
992
+ export { KICAD_SCRIPTS, KICAD_SCRIPT_IDS, KICAD_SKILL_NAME, apply, classifyRun, createKicadTools, inject, invokeKicadScript, kicadScript, kicadToolNames, name, readBundledSkill, requireSkillDir, resolveSkillDir, runKicadScript, scriptsDir, skillDescription, tryReadBundledSkill };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huaqiu/dsh-kicad",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "main": "./lib/index.mjs",
6
6
  "types": "./lib/index.d.mts",
@@ -22,7 +22,7 @@
22
22
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.0"
23
23
  },
24
24
  "dependencies": {
25
- "@huaqiu/dsh-plugin-log": "0.4.2"
25
+ "@huaqiu/dsh-plugin-log": "0.4.3"
26
26
  },
27
27
  "files": [
28
28
  "lib",
package/src/index.ts CHANGED
@@ -157,6 +157,46 @@ export function readBundledSkill(moduleUrl: string, override?: string): {
157
157
  }
158
158
  }
159
159
 
160
+ /**
161
+ * Read the bundled skill, or `null` when the installed package does not carry
162
+ * one.
163
+ *
164
+ * ── Why this degrades instead of throwing ──────────────────────────────────
165
+ * A missing `skills/` tree is a PACKAGING failure, and the honest signal for a
166
+ * packaging failure is a loud error — but not a dead server. `apply()` runs
167
+ * inside the DSH plugin tree: throwing here aborts the whole loader
168
+ * (`plugin tree failed to load`), kills the DSH process, and takes every other
169
+ * plugin and the entire EDA session down with it, for one missing asset.
170
+ *
171
+ * That actually happened: HQ Edge's builtin-plugin staging copied only
172
+ * `package.json` + `lib/`, so `skills/` never reached the bundle and the
173
+ * server could not start at all. The correct blast radius for "this plugin's
174
+ * skill is missing" is "this plugin is degraded", so we log at error level
175
+ * with the exact path and remedy, skip skill registration, and keep the tools
176
+ * (which fail per-call with a typed `FAILED_PRECONDITION` rather than at load).
177
+ *
178
+ * @param moduleUrl - `import.meta.url` of the calling module.
179
+ * @param override - explicit skill directory (plugin config or env var).
180
+ */
181
+ export function tryReadBundledSkill(
182
+ moduleUrl: string,
183
+ override?: string,
184
+ ): { dir: string; name: string; description: string; content: string } | null {
185
+ try {
186
+ return readBundledSkill(moduleUrl, override)
187
+ } catch (err) {
188
+ log.error(
189
+ 'dsh-kicad: bundled skill unavailable — continuing degraded, the KiCad ' +
190
+ 'tools are registered but will fail until the package is reinstalled',
191
+ {
192
+ expectedDir: resolveSkillDir(moduleUrl, override),
193
+ error: String((err as Error)?.message ?? err),
194
+ },
195
+ )
196
+ return null
197
+ }
198
+ }
199
+
160
200
  /**
161
201
  * Host plugin body — register the `kicad-ipc` skill and the KiCad tools.
162
202
  *
@@ -179,32 +219,39 @@ export function apply(ctx: Context, config: KicadConfigInput = {}): () => void {
179
219
 
180
220
  const resolved = resolveKicadConfig(config)
181
221
 
182
- // Throws when the installed package is missing its skill — a packaging
183
- // failure must be loud, not silently degraded (§15).
184
- const skill = readBundledSkill(import.meta.url, config.skillsDir)
222
+ // Degraded rather than fatal: see tryReadBundledSkill. A null skill means
223
+ // the installed package is incomplete, not that the host is unusable.
224
+ const skill = tryReadBundledSkill(import.meta.url, config.skillsDir)
225
+ // Tools resolve their Python scripts under the skill directory, so they use
226
+ // the same path even when the skill itself is missing — a call then fails
227
+ // with a typed FAILED_PRECONDITION naming the exact script it needed.
228
+ const skillDir = skill?.dir ?? resolveSkillDir(import.meta.url, config.skillsDir)
185
229
 
186
230
  log.info('applying dsh-kicad node half', {
187
231
  hasConfigHost: hasHostConfig(config),
188
232
  pythonPath: resolved.pythonPath,
189
- skillDir: skill.dir,
233
+ skillDir,
234
+ skillPresent: skill !== null,
190
235
  timeoutMs: resolved.timeoutMs,
191
236
  })
192
237
 
193
238
  const disposers: Array<() => void> = []
194
239
 
195
240
  // ── Skill (bundled, no separate installation) ────────────────────────────
196
- disposers.push(
197
- ctx.skills.register({
198
- name: skill.name,
199
- description: skill.description,
200
- content: skill.content,
201
- resourceBase: { kind: 'directory', path: skill.dir },
202
- }),
203
- )
241
+ if (skill) {
242
+ disposers.push(
243
+ ctx.skills.register({
244
+ name: skill.name,
245
+ description: skill.description,
246
+ content: skill.content,
247
+ resourceBase: { kind: 'directory', path: skill.dir },
248
+ }),
249
+ )
250
+ }
204
251
 
205
252
  // ── Tools ────────────────────────────────────────────────────────────────
206
253
  const tools = createKicadTools({
207
- scriptsDir: scriptsDir(skill.dir),
254
+ scriptsDir: scriptsDir(skillDir),
208
255
  pythonPath: resolved.pythonPath,
209
256
  config: resolved,
210
257
  })
@@ -213,7 +260,8 @@ export function apply(ctx: Context, config: KicadConfigInput = {}): () => void {
213
260
  }
214
261
 
215
262
  log.info('dsh-kicad node half ready', {
216
- skill: skill.name,
263
+ skill: skill?.name ?? null,
264
+ degraded: skill === null,
217
265
  tools: tools.length,
218
266
  expectedTools: kicadToolNames().length,
219
267
  })