@michengai/dsh-skills-manager 0.1.6 → 0.1.7

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/client.js CHANGED
@@ -84,6 +84,12 @@ window.__ModuleLoader__.load({
84
84
  "error.import.invalidName": "无法生成合法 kebab-case 名称(原始名: {name})",
85
85
  "error.import.duplicateName": "批量来源中存在多个同名插件: {name}",
86
86
  "error.import.failed": "导入失败",
87
+ "error.proto.forbidden": "禁止的修改请求(缺少客户端标记)",
88
+ "error.proto.contentType": "请求体必须是 application/json",
89
+ "error.proto.method": "不支持的请求方法",
90
+ "error.proto.unknownAction": "未知操作",
91
+ "error.proto.bodyTooLarge": "请求体过大",
92
+ "error.proto.invalidJson": "请求体不是合法 JSON",
87
93
  "action.enable": "启用",
88
94
  "action.disable": "停用",
89
95
  "action.delete": "删除",
@@ -160,6 +166,12 @@ window.__ModuleLoader__.load({
160
166
  "error.import.invalidName": "Cannot generate a valid kebab-case name (original name: {name})",
161
167
  "error.import.duplicateName": "Batch source contains multiple plugins with the same name: {name}",
162
168
  "error.import.failed": "Import failed",
169
+ "error.proto.forbidden": "Forbidden mutation request (missing client marker)",
170
+ "error.proto.contentType": "Content type must be application/json",
171
+ "error.proto.method": "Method not allowed",
172
+ "error.proto.unknownAction": "Unknown action",
173
+ "error.proto.bodyTooLarge": "Request body too large",
174
+ "error.proto.invalidJson": "Invalid JSON request body",
163
175
  "action.enable": "enable",
164
176
  "action.disable": "disable",
165
177
  "action.delete": "deleting",
@@ -296,12 +308,12 @@ window.__ModuleLoader__.load({
296
308
  function selectFile(file) {
297
309
  if (!file) return;
298
310
  if (file.name.toLowerCase() !== "skill.md") {
299
- setImportResult({ error: t("select.file.invalid") });
311
+ setImportResult({ code: "select.file.invalid" });
300
312
  return;
301
313
  }
302
314
  if (!file.path) {
303
315
  setSelected(null);
304
- setImportResult({ error: t("select.path.missing") });
316
+ setImportResult({ code: "select.path.missing" });
305
317
  return;
306
318
  }
307
319
  setSelected({ name: file.name, source: file.path });
@@ -310,7 +322,7 @@ window.__ModuleLoader__.load({
310
322
 
311
323
  function selectDirectory() {
312
324
  if (!pickDirectory) {
313
- setImportResult({ error: t("dir.service.missing") });
325
+ setImportResult({ code: "dir.service.missing" });
314
326
  return;
315
327
  }
316
328
  setImportResult(null);
@@ -318,12 +330,12 @@ window.__ModuleLoader__.load({
318
330
  pickDirectory().then(function (path) {
319
331
  setBusy(null);
320
332
  if (path) {
321
- setSelected({ name: t("dir.selected"), source: path });
333
+ setSelected({ nameKey: "dir.selected", source: path });
322
334
  setImportResult(null);
323
335
  }
324
336
  }).catch(function (error) {
325
337
  setBusy(null);
326
- setImportResult({ error: t("dir.pick.failed", { error: String(error && error.message || error) }) });
338
+ setImportResult({ code: "dir.pick.failed", params: { error: String(error && error.message || error) } });
327
339
  });
328
340
  }
329
341
 
@@ -403,13 +415,15 @@ window.__ModuleLoader__.load({
403
415
  if (importResult) {
404
416
  var nameSeparator = t("sep.names");
405
417
  var errorSeparator = t("sep.errors");
418
+ // 错误形态:业务/协议错误带 code,或 host 失败带 error(Error 对象);结果形态只有 imported/failed/skipped。
419
+ var isImportError = importResult.code !== undefined || importResult.error !== undefined;
406
420
  var importedNames = (importResult.imported || []).map(function (item) { return item.name; });
407
421
  // catch 场景把 core 的失败明细挂在 Error.failed 上,这里一并取用。
408
422
  var failedItems = importResult.failed || (importResult.error && importResult.error.failed) || [];
409
423
  var skippedNames = (importResult.skipped || []).map(function (item) { return item.name; });
410
424
  var failedText = failedItems.map(function (item) { return translateError(t, item); }).join(errorSeparator);
411
425
  var resultText;
412
- if (importResult.error) {
426
+ if (isImportError) {
413
427
  // 顶层聚合错误优先展示已翻译的失败明细;无明细时由 translateError 兜底(含 Error 对象)。
414
428
  resultText = t("result.failed", { error: failedItems.length ? failedText : translateError(t, importResult) });
415
429
  } else if (failedItems.length) {
@@ -421,16 +435,16 @@ window.__ModuleLoader__.load({
421
435
  } else {
422
436
  resultText = t("result.none");
423
437
  }
424
- nodes.push(h("div", { key: "import-result", className: importResult.error || failedItems.length ? "dssm-error" : "dssm-note", role: "status" }, resultText));
438
+ nodes.push(h("div", { key: "import-result", className: isImportError || failedItems.length ? "dssm-error" : "dssm-note", role: "status" }, resultText));
425
439
  }
426
440
  if (uploadOpen) {
427
441
  nodes.push(h("div", { key: "upload", className: "dssm-mask" }, h("div", { className: "dssm-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "dssm-upload-title" },
428
442
  h("div", { className: "dssm-modal-head" }, h("h3", { id: "dssm-upload-title", className: "dssm-modal-title" }, t("upload.title")), h("button", { className: "dssm-icon-btn", "aria-label": t("upload.close"), onClick: function () { setUploadOpen(false); } }, "×")),
429
443
  h("input", { ref: inputRef, className: "dssm-hidden-input", type: "file", accept: ".md", onChange: function (event) { selectFile(event.target.files && event.target.files[0]); event.target.value = ""; } }),
430
444
  h("div", { className: "dssm-dropzone", tabIndex: 0, onClick: openNativePicker, onKeyDown: function (event) { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); openNativePicker(); } }, onDragOver: function (event) { event.preventDefault(); }, onDrop: function (event) { event.preventDefault(); selectFile(event.dataTransfer.files && event.dataTransfer.files[0]); } }, h("span", { className: "dssm-dropzone-title" }, t("upload.drop.title")), h("span", { className: "dssm-dropzone-copy" }, t("upload.drop.copy"))),
431
- selected ? h("div", { className: "dssm-file", title: selected.source }, h("span", { className: "dssm-file-name" }, selected.name), h("span", { className: "dssm-tag dssm-tag-on" }, t("status.selected"))) : h("p", { className: "dssm-desc" }, t("upload.hint")),
445
+ selected ? h("div", { className: "dssm-file", title: selected.source }, h("span", { className: "dssm-file-name" }, selected.nameKey ? t(selected.nameKey) : selected.name), h("span", { className: "dssm-tag dssm-tag-on" }, t("status.selected"))) : h("p", { className: "dssm-desc" }, t("upload.hint")),
432
446
  busy === "pick-directory" ? h("div", { className: "dssm-note", role: "status" }, t("upload.picking.dir")) : null,
433
- importResult && importResult.error ? h("div", { className: "dssm-error", role: "alert" }, t("select.failed", { error: translateError(t, importResult) })) : null,
447
+ importResult && (importResult.code !== undefined || importResult.error !== undefined) ? h("div", { className: "dssm-error", role: "alert" }, t("select.failed", { error: translateError(t, importResult) })) : null,
434
448
  h("div", { className: "dssm-modal-actions" }, h("button", { className: "dssm-btn dssm-btn-secondary", disabled: !!busy, onClick: openNativePicker }, t("btn.file.pick")), pickDirectory ? h("button", { className: "dssm-btn dssm-btn-secondary", disabled: !!busy, onClick: selectDirectory }, t("btn.dir.pick")) : null, h("button", { className: "dssm-btn", disabled: !selected || !!busy, onClick: installSelected }, busy === "import" || busy === "import-check" ? t("btn.uploading") : t("btn.upload.dsh"))))));
435
449
  }
436
450
  if (confirmImport) {
package/lib/core.js CHANGED
@@ -29,9 +29,9 @@ function codedError(message, code, params) {
29
29
  return error;
30
30
  }
31
31
 
32
- /** 把业务 Error 的 code/params 附加到失败明细;系统异常(无 code)保持原文。 */
32
+ /** 把业务 Error 的 code/params 附加到失败明细;系统异常(ENOENT 等,非 error.* 前缀)保持原文。 */
33
33
  function attachCode(item, error) {
34
- if (error && typeof error.code === "string") item.code = error.code;
34
+ if (error && typeof error.code === "string" && /^error\./.test(error.code)) item.code = error.code;
35
35
  if (error && error.params) item.params = error.params;
36
36
  return item;
37
37
  }
package/lib/index.js CHANGED
@@ -47,6 +47,7 @@ function readBody(req, limit = 1 << 20) {
47
47
  if (size > limit) {
48
48
  const error = new Error("body too large");
49
49
  error.statusCode = 413;
50
+ error.code = "error.proto.bodyTooLarge";
50
51
  fail(error);
51
52
  req.resume();
52
53
  return;
@@ -63,6 +64,7 @@ function readBody(req, limit = 1 << 20) {
63
64
  } catch (e) {
64
65
  const error = new Error(`invalid JSON body: ${e.message}`);
65
66
  error.statusCode = 400;
67
+ error.code = "error.proto.invalidJson";
66
68
  fail(error);
67
69
  }
68
70
  });
@@ -72,9 +74,9 @@ function readBody(req, limit = 1 << 20) {
72
74
 
73
75
  /** 自定义请求头使跨站 fetch 必须预检;本地接口不提供 CORS 响应。 */
74
76
  function validateMutationRequest(req) {
75
- if (req.headers[CLIENT_MARKER_HEADER] !== "1") return { statusCode: 403, error: "forbidden mutation request" };
77
+ if (req.headers[CLIENT_MARKER_HEADER] !== "1") return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden mutation request" };
76
78
  const contentType = String(req.headers["content-type"] || "").split(";", 1)[0].trim().toLowerCase();
77
- if (contentType !== "application/json") return { statusCode: 415, error: "content-type must be application/json" };
79
+ if (contentType !== "application/json") return { statusCode: 415, code: "error.proto.contentType", error: "content-type must be application/json" };
78
80
  return null;
79
81
  }
80
82
 
@@ -134,7 +136,7 @@ function apply(ctx) {
134
136
  return;
135
137
  }
136
138
  if (req.method !== "POST") {
137
- json(res, 405, { ok: false, error: `method not allowed: ${req.method}` });
139
+ json(res, 405, { ok: false, code: "error.proto.method", error: `method not allowed: ${req.method}` });
138
140
  return;
139
141
  }
140
142
  const requestError = validateMutationRequest(req);
@@ -163,10 +165,14 @@ function apply(ctx) {
163
165
  }), body.dryRun === true ? undefined : invalidateSkills);
164
166
  return;
165
167
  default:
166
- json(res, 404, { ok: false, error: `unknown action: ${path}` });
168
+ json(res, 404, { ok: false, code: "error.proto.unknownAction", error: `unknown action: ${path}` });
167
169
  }
168
170
  } catch (e) {
169
- json(res, Number.isInteger(e && e.statusCode) ? e.statusCode : 500, { ok: false, error: String(e && e.message ? e.message : e) });
171
+ json(res, Number.isInteger(e && e.statusCode) ? e.statusCode : 500, {
172
+ ok: false,
173
+ ...(e && e.code ? { code: e.code } : {}),
174
+ error: String(e && e.message ? e.message : e),
175
+ });
170
176
  }
171
177
  },
172
178
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-skills-manager",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "NPM-installable DSH Web plugin for managing local skills and viewing shared Agent skills safely.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {