@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 +23 -9
- package/lib/core.js +2 -2
- package/lib/index.js +11 -5
- package/package.json +1 -1
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({
|
|
311
|
+
setImportResult({ code: "select.file.invalid" });
|
|
300
312
|
return;
|
|
301
313
|
}
|
|
302
314
|
if (!file.path) {
|
|
303
315
|
setSelected(null);
|
|
304
|
-
setImportResult({
|
|
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({
|
|
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({
|
|
333
|
+
setSelected({ nameKey: "dir.selected", source: path });
|
|
322
334
|
setImportResult(null);
|
|
323
335
|
}
|
|
324
336
|
}).catch(function (error) {
|
|
325
337
|
setBusy(null);
|
|
326
|
-
setImportResult({
|
|
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 (
|
|
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:
|
|
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
|
|
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, {
|
|
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