mdk-skills 2.3.1 → 2.3.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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>mdk-skills 管理面板</title>
7
- <script type="module" crossorigin src="/assets/index-CuiL4aIa.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-Cj7FAJgB.css">
7
+ <script type="module" crossorigin src="/assets/index-BnMBIily.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BItq1iGH.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="app"></div>
@@ -2,7 +2,7 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const http = require("http");
4
4
  const crypto = require("crypto");
5
- const { execSync } = require("child_process");
5
+ const { execSync, exec } = require("child_process");
6
6
  const os = require("os");
7
7
  const core = require("../core");
8
8
 
@@ -167,6 +167,24 @@ function writeSkillMeta(dest, wasUpdated) {
167
167
  fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
168
168
  }
169
169
 
170
+ // ---------- 异步 npx 管理:支持取消正在执行的进程 ----------
171
+
172
+ let _npxProcess = null;
173
+
174
+ function runNpx(cmd, cwd) {
175
+ return new Promise((resolve, reject) => {
176
+ const proc = exec(cmd, { cwd, timeout: 120000 }, (err) => {
177
+ _npxProcess = null;
178
+ if (err) {
179
+ reject(err.killed ? new Error("已取消") : err);
180
+ } else {
181
+ resolve();
182
+ }
183
+ });
184
+ _npxProcess = proc;
185
+ });
186
+ }
187
+
170
188
  // 读取 profiles.json
171
189
  function loadProfiles() {
172
190
  const profilesPath = path.join(skillsSource, ".claude", "profiles.json");
@@ -392,14 +410,11 @@ async function handleApi(req, res) {
392
410
  const tmpDir = path.join(os.tmpdir(), "mdk-pull-" + Date.now());
393
411
  fs.mkdirSync(path.join(tmpDir, ".claude", "skills"), { recursive: true });
394
412
  try {
395
- execSync("npx --yes skills add \"" + url + "\" --copy -y -a claude-code", {
396
- cwd: tmpDir,
397
- stdio: "pipe",
398
- timeout: 120000,
399
- });
400
- } catch {
413
+ await runNpx("npx --yes skills add \"" + url + "\" --copy -y -a claude-code", tmpDir);
414
+ } catch (e) {
401
415
  cleanNpxTemp();
402
416
  fs.rmSync(tmpDir, { recursive: true, force: true });
417
+ if (e.message === "已取消") return sendJSON(res, { cancelled: true }, 499);
403
418
  return sendJSON(res, { error: "拉取失败,请检查地址或网络连接" }, 400);
404
419
  }
405
420
  cleanNpxTemp();
@@ -457,12 +472,11 @@ async function handleApi(req, res) {
457
472
  const tmpDir = path.join(os.tmpdir(), "mdk-update-" + Date.now());
458
473
  fs.mkdirSync(path.join(tmpDir, ".claude", "skills"), { recursive: true });
459
474
  try {
460
- execSync("npx --yes skills add \"" + source.url + "\" --copy -y -a claude-code", {
461
- cwd: tmpDir, stdio: "pipe", timeout: 120000,
462
- });
463
- } catch {
475
+ await runNpx("npx --yes skills add \"" + source.url + "\" --copy -y -a claude-code", tmpDir);
476
+ } catch (e) {
464
477
  cleanNpxTemp();
465
478
  fs.rmSync(tmpDir, { recursive: true, force: true });
479
+ if (e.message === "已取消") return sendJSON(res, { cancelled: true }, 499);
466
480
  return sendJSON(res, { error: "重新拉取失败,请检查网络连接" }, 400);
467
481
  }
468
482
 
@@ -519,12 +533,11 @@ async function handleApi(req, res) {
519
533
  const tmpDir = path.join(os.tmpdir(), "mdk-batch-" + Date.now());
520
534
  fs.mkdirSync(path.join(tmpDir, ".claude", "skills"), { recursive: true });
521
535
  try {
522
- execSync("npx --yes skills add \"" + url + "\" --copy -y -a claude-code", {
523
- cwd: tmpDir, stdio: "pipe", timeout: 120000,
524
- });
525
- } catch {
536
+ await runNpx("npx --yes skills add \"" + url + "\" --copy -y -a claude-code", tmpDir);
537
+ } catch (e) {
526
538
  cleanNpxTemp();
527
539
  fs.rmSync(tmpDir, { recursive: true, force: true });
540
+ if (e.message === "已取消") return sendJSON(res, { cancelled: true }, 499);
528
541
  return sendJSON(res, { error: "拉取失败,请检查网络连接" }, 400);
529
542
  }
530
543
 
@@ -1144,6 +1157,15 @@ ${skillsList}
1144
1157
  return sendJSON(res, results);
1145
1158
  }
1146
1159
 
1160
+ // POST /api/tasks/cancel — 取消正在执行的 npx 操作
1161
+ if (method === "POST" && pathname === "/api/tasks/cancel") {
1162
+ if (_npxProcess) {
1163
+ _npxProcess.kill();
1164
+ _npxProcess = null;
1165
+ }
1166
+ return sendJSON(res, { ok: true });
1167
+ }
1168
+
1147
1169
  // 404
1148
1170
  sendJSON(res, { error: "Not Found: " + pathname }, 404);
1149
1171
  } catch (err) {
@@ -151,3 +151,7 @@ export function createReadme(data) {
151
151
  body: JSON.stringify(data),
152
152
  });
153
153
  }
154
+
155
+ export function cancelTask() {
156
+ return fetch("/api/tasks/cancel", { method: "POST" }).catch(() => {});
157
+ }
@@ -99,6 +99,7 @@
99
99
  style="width: 720px"
100
100
  content-style="padding: 0;"
101
101
  :mask-closable="true"
102
+ @update:show="(v) => { if (!v) { updatingSkill = false; detailLoading = false; cancelTask(); } }"
102
103
  >
103
104
  <div class="detail-body">
104
105
  <!-- 编辑面板 -->
@@ -174,6 +175,7 @@
174
175
  preset="card"
175
176
  style="width: 600px"
176
177
  :mask-closable="true"
178
+ @update:show="(v) => { if (!v) { pulling = false; cancelTask(); } }"
177
179
  >
178
180
  <div class="pull-modal-body">
179
181
  <div class="pull-input-row">
@@ -251,7 +253,7 @@ import { RefreshOutline } from "@vicons/ionicons5";
251
253
  import { marked } from "marked";
252
254
  import hljs from "highlight.js";
253
255
  import SkillCard from "../components/SkillCard.vue";
254
- import { getSkills, getReadme, getSkillReadme, updateSkillMeta, deleteSkill, pullSkills, installSkills, getSkillSource, updateSkill, batchUpdateSkills, openSkillDir } from "../api/skills";
256
+ import { getSkills, getReadme, getSkillReadme, updateSkillMeta, deleteSkill, pullSkills, installSkills, getSkillSource, updateSkill, batchUpdateSkills, openSkillDir, cancelTask } from "../api/skills";
255
257
  import { sortSkills, getUsageMap } from "../utils/usage";
256
258
 
257
259
  // marked 配置:代码高亮 + 外链安全
@@ -374,6 +376,7 @@ async function handlePull() {
374
376
  pullError.value = "";
375
377
  try {
376
378
  const res = await pullSkills(pullUrl.value.trim());
379
+ if (!showPullModal.value) return;
377
380
  if (res.ok) {
378
381
  pullResult.value = { imported: res.imported, skipped: res.skipped };
379
382
  // 默认全选
@@ -385,6 +388,7 @@ async function handlePull() {
385
388
  pullError.value = res.error || "拉取失败";
386
389
  }
387
390
  } catch (e) {
391
+ if (!showPullModal.value) return;
388
392
  pullError.value = "拉取失败,请检查地址或网络";
389
393
  } finally {
390
394
  pulling.value = false;
@@ -541,6 +545,7 @@ async function handleUpdate() {
541
545
  updatingSkill.value = true;
542
546
  try {
543
547
  const res = await updateSkill(detailSkill.value.name);
548
+ if (!detailVisible.value) return;
544
549
  if (res.ok) {
545
550
  if (res.updated === false) {
546
551
  message.info("已是最新版本,无需更新");
@@ -565,6 +570,7 @@ async function handleUpdate() {
565
570
  message.error(res.error || "更新失败");
566
571
  }
567
572
  } catch {
573
+ if (!detailVisible.value) return;
568
574
  message.error("更新失败");
569
575
  } finally {
570
576
  updatingSkill.value = false;