itismyskillmarket 1.3.45 → 1.3.46

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.
@@ -42,29 +42,63 @@ var fileScopes = fileConfig.npmScopes ? fileConfig.npmScopes.split(",").map((s)
42
42
  var SKILL_SCOPES = process.env.SKM_NPM_SCOPES ? process.env.SKM_NPM_SCOPES.split(",").map((s) => s.trim()).filter(Boolean) : fileScopes || DEFAULT_SCOPES;
43
43
  var SKM_URL = process.env.SKM_URL || fileConfig.skmUrl || `https://www.npmjs.com/package/${NPM_SCOPE}`;
44
44
 
45
- // src/commands/npm.ts
46
- var npmCache = /* @__PURE__ */ new Map();
47
- function getCached(key) {
48
- const entry = npmCache.get(key);
49
- if (!entry) return null;
50
- if (Date.now() > entry.expiry) {
51
- npmCache.delete(key);
52
- return null;
45
+ // src/utils/cache.ts
46
+ var TtlCache = class {
47
+ store = /* @__PURE__ */ new Map();
48
+ /**
49
+ * 获取缓存值。如果 key 不存在或已过期,返回 null
50
+ * 过期条目会被惰性删除。
51
+ */
52
+ get(key) {
53
+ const entry = this.store.get(key);
54
+ if (!entry) return null;
55
+ if (Date.now() > entry.expiry) {
56
+ this.store.delete(key);
57
+ return null;
58
+ }
59
+ return entry.data;
53
60
  }
54
- return entry.data;
55
- }
56
- function setCache(key, data, ttlMs = 3e4) {
57
- npmCache.set(key, { data, expiry: Date.now() + ttlMs });
58
- }
61
+ /**
62
+ * 设置缓存值
63
+ *
64
+ * @param key - 缓存键
65
+ * @param data - 缓存数据
66
+ * @param ttlMs - 生存时间(毫秒),默认 30 秒
67
+ */
68
+ set(key, data, ttlMs = 3e4) {
69
+ this.store.set(key, { data, expiry: Date.now() + ttlMs });
70
+ }
71
+ /**
72
+ * 删除指定的缓存条目
73
+ */
74
+ delete(key) {
75
+ this.store.delete(key);
76
+ }
77
+ /**
78
+ * 清空所有缓存
79
+ */
80
+ clear() {
81
+ this.store.clear();
82
+ }
83
+ /**
84
+ * 当前缓存条目数量
85
+ */
86
+ get size() {
87
+ return this.store.size;
88
+ }
89
+ };
90
+ var cache = new TtlCache();
91
+
92
+ // src/commands/npm.ts
59
93
  async function fetchNpmPackage(packageName, retries = 1) {
60
94
  const cacheKey = `pkg:${packageName}`;
61
- const cached = getCached(cacheKey);
95
+ const cached = cache.get(cacheKey);
62
96
  if (cached) return cached;
63
97
  for (let attempt = 0; attempt <= retries; attempt++) {
64
98
  try {
65
99
  const result = await fetchNpmPackageOnce(packageName);
66
100
  if (result !== null) {
67
- setCache(cacheKey, result);
101
+ cache.set(cacheKey, result);
68
102
  return result;
69
103
  }
70
104
  } catch {
@@ -311,8 +345,7 @@ var BaseAdapter = class {
311
345
  }
312
346
  async isAvailable() {
313
347
  try {
314
- await fs3.ensureDir(this.skillDir);
315
- return true;
348
+ return fs3.pathExists(this.skillDir);
316
349
  } catch {
317
350
  return false;
318
351
  }
@@ -687,6 +720,54 @@ import { exec } from "child_process";
687
720
  import { promisify } from "util";
688
721
  import * as tar from "tar";
689
722
  var execAsync = promisify(exec);
723
+ var CACHE_MAX_ENTRIES = 50;
724
+ var CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
725
+ async function cleanupCache() {
726
+ try {
727
+ const cacheDir = getCacheDir();
728
+ if (!await fs11.pathExists(cacheDir)) return;
729
+ const entries = await fs11.readdir(cacheDir, { withFileTypes: true });
730
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, path: path10.join(cacheDir, e.name) }));
731
+ if (dirs.length <= CACHE_MAX_ENTRIES) {
732
+ const now = Date.now();
733
+ let removed = 0;
734
+ for (const dir of dirs) {
735
+ try {
736
+ const stat = await fs11.stat(dir.path);
737
+ if (now - stat.mtimeMs > CACHE_MAX_AGE_MS) {
738
+ await fs11.remove(dir.path);
739
+ removed++;
740
+ }
741
+ } catch {
742
+ }
743
+ }
744
+ if (removed > 0) {
745
+ console.log(` Cache cleanup: removed ${removed} expired entr${removed > 1 ? "ies" : "y"}`);
746
+ }
747
+ return;
748
+ }
749
+ const withMtime = await Promise.all(
750
+ dirs.map(async (dir) => {
751
+ try {
752
+ const stat = await fs11.stat(dir.path);
753
+ return { ...dir, mtime: stat.mtimeMs };
754
+ } catch {
755
+ return { ...dir, mtime: 0 };
756
+ }
757
+ })
758
+ );
759
+ withMtime.sort((a, b) => b.mtime - a.mtime);
760
+ const toRemove = withMtime.slice(CACHE_MAX_ENTRIES);
761
+ for (const dir of toRemove) {
762
+ try {
763
+ await fs11.remove(dir.path);
764
+ } catch {
765
+ }
766
+ }
767
+ console.log(` Cache cleanup: removed ${toRemove.length} old entr${toRemove.length > 1 ? "ies" : "y"}`);
768
+ } catch {
769
+ }
770
+ }
690
771
  async function installSkill(skillId, version, options) {
691
772
  await ensureMarketDirs();
692
773
  let targetVersion;
@@ -733,6 +814,7 @@ async function installSkill(skillId, version, options) {
733
814
  } catch (err) {
734
815
  throw new Error(`Failed to download package: ${err}`);
735
816
  }
817
+ await cleanupCache();
736
818
  }
737
819
  pkgRoot = targetDir;
738
820
  }
@@ -1872,18 +1954,16 @@ var MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
1872
1954
  var __filename = fileURLToPath2(import.meta.url);
1873
1955
  var __dirname = dirname(__filename);
1874
1956
  var guiDir = join3(__dirname, "..", "gui");
1875
- var cache = /* @__PURE__ */ new Map();
1876
- function getCached2(key) {
1877
- const entry = cache.get(key);
1878
- if (!entry) return null;
1879
- if (Date.now() > entry.expiry) {
1880
- cache.delete(key);
1881
- return null;
1882
- }
1883
- return entry.data;
1957
+ var uiCache = new TtlCache();
1958
+ function getAuthorName(author) {
1959
+ if (!author) return "";
1960
+ if (typeof author === "string") return author.replace(/<[^>]*>/g, "").trim();
1961
+ return author.name || "";
1884
1962
  }
1885
- function setCache2(key, data, ttlMs = 6e4) {
1886
- cache.set(key, { data, expiry: Date.now() + ttlMs });
1963
+ function getRepoUrl(repo) {
1964
+ if (!repo) return "";
1965
+ if (typeof repo === "string") return repo;
1966
+ return repo.url || "";
1887
1967
  }
1888
1968
  async function throttledMap(items, fn, concurrency = 3) {
1889
1969
  const results = [];
@@ -1943,7 +2023,7 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1943
2023
  const sort = url.searchParams.get("sort") || "name";
1944
2024
  const platform = url.searchParams.get("platform") || "";
1945
2025
  const cacheKey = `search:${search}:limit:${limit}`;
1946
- let searchResult = getCached2(cacheKey);
2026
+ let searchResult = uiCache.get(cacheKey);
1947
2027
  if (!searchResult) {
1948
2028
  searchResult = await searchSkillmarketPackages({
1949
2029
  from: 0,
@@ -1951,17 +2031,17 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1951
2031
  // 一次拉取更多,避免分页
1952
2032
  keyword: search || void 0
1953
2033
  });
1954
- setCache2(cacheKey, searchResult, 3e4);
2034
+ uiCache.set(cacheKey, searchResult, 3e4);
1955
2035
  }
1956
2036
  const { packages, total } = searchResult;
1957
2037
  let fetchErrors = 0;
1958
2038
  const skillDetails = await throttledMap(packages, async (pkgName) => {
1959
2039
  try {
1960
2040
  const pkgCacheKey = `pkg:${pkgName}`;
1961
- let info = getCached2(pkgCacheKey);
2041
+ let info = uiCache.get(pkgCacheKey);
1962
2042
  if (!info) {
1963
2043
  info = await fetchNpmPackage(pkgName);
1964
- if (info) setCache2(pkgCacheKey, info, 3e4);
2044
+ if (info) uiCache.set(pkgCacheKey, info, 3e4);
1965
2045
  }
1966
2046
  if (!info) {
1967
2047
  fetchErrors++;
@@ -1977,9 +2057,9 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1977
2057
  version: latestVersion,
1978
2058
  description: pkg?.description || "",
1979
2059
  platforms: meta?.platforms || [],
1980
- author: info.author?.name || pkg?.author?.name || "",
2060
+ author: getAuthorName(info.author) || getAuthorName(pkg?.author),
1981
2061
  homepage: pkg?.homepage || "",
1982
- repository: pkg?.repository?.url || "",
2062
+ repository: getRepoUrl(pkg?.repository),
1983
2063
  updated: info.time?.[latestVersion] || info.time?.modified || ""
1984
2064
  };
1985
2065
  } catch {
@@ -2095,10 +2175,10 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
2095
2175
  return;
2096
2176
  }
2097
2177
  const cacheKey = `skill-info:${skillName}`;
2098
- let info = getCached2(cacheKey);
2178
+ let info = uiCache.get(cacheKey);
2099
2179
  if (!info) {
2100
2180
  info = await fetchSkillPackage(skillName);
2101
- if (info) setCache2(cacheKey, info, 3e4);
2181
+ if (info) uiCache.set(cacheKey, info, 3e4);
2102
2182
  }
2103
2183
  if (!info) {
2104
2184
  jsonResponse(res, 404, { error: `Skill "${skillName}" not found` });
@@ -2117,10 +2197,10 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
2117
2197
  version: latestVersion,
2118
2198
  platforms: meta?.platforms || [],
2119
2199
  versions: recentVersions,
2120
- author: info.author?.name || pkg?.author?.name || "",
2200
+ author: getAuthorName(info.author) || getAuthorName(pkg?.author),
2121
2201
  license: info.license || "",
2122
2202
  homepage: pkg?.homepage || "",
2123
- repository: pkg?.repository?.url || "",
2203
+ repository: getRepoUrl(pkg?.repository),
2124
2204
  readme: info.readme || ""
2125
2205
  });
2126
2206
  } catch (err) {
@@ -2228,7 +2308,7 @@ API_ROUTES.GET["/api/admin/stats"] = async (_req, res, _url) => {
2228
2308
  return null;
2229
2309
  }
2230
2310
  })
2231
- )).filter(Boolean);
2311
+ )).filter((item) => item !== null);
2232
2312
  let totalVersions = 0;
2233
2313
  let totalSize = 0;
2234
2314
  const platformSet = /* @__PURE__ */ new Set();
@@ -2625,6 +2705,7 @@ export {
2625
2705
  LATEST_LINK,
2626
2706
  getSkillsDir,
2627
2707
  getPlatformLinksDir,
2708
+ getRegistryPath,
2628
2709
  ensureMarketDirs,
2629
2710
  loadRegistry,
2630
2711
  saveRegistry,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startGuiServer
4
- } from "./chunk-ALNUNP4E.js";
4
+ } from "./chunk-ARRJETWL.js";
5
5
  export {
6
6
  startGuiServer
7
7
  };
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  getConfigValue,
25
25
  getInstalledSkills,
26
26
  getPlatformLinksDir,
27
+ getRegistryPath,
27
28
  getSkillsDir,
28
29
  installSkill,
29
30
  isSkillInstalled,
@@ -38,12 +39,12 @@ import {
38
39
  uninstallAll,
39
40
  uninstallSkill,
40
41
  updateSkill
41
- } from "./chunk-ALNUNP4E.js";
42
+ } from "./chunk-ARRJETWL.js";
42
43
 
43
44
  // src/cli.ts
44
45
  import { Command } from "commander";
45
46
  import { readFileSync } from "fs";
46
- import { fileURLToPath as fileURLToPath2 } from "url";
47
+ import { fileURLToPath } from "url";
47
48
  import { dirname, resolve } from "path";
48
49
  import { execSync } from "child_process";
49
50
 
@@ -554,15 +555,12 @@ Installing to ${targetPlatforms.length} platform(s)...
554
555
  // src/commands/verify.ts
555
556
  import fs3 from "fs-extra";
556
557
  import path3 from "path";
557
- import { fileURLToPath } from "url";
558
- var __filename = fileURLToPath(import.meta.url);
559
- var __dirname = path3.dirname(__filename);
560
558
  async function verifySkill(skillName) {
561
559
  try {
562
560
  console.log(`
563
561
  \u{1F50D} Verifying skill: ${skillName}
564
562
  `);
565
- const skillDir = path3.join(process.env.HOME || process.env.USERPROFILE || "", ".skillmarket", "skills", skillName);
563
+ const skillDir = path3.join(getSkillsDir(), skillName);
566
564
  if (!await fs3.pathExists(skillDir)) {
567
565
  console.error(`\u274C Skill "${skillName}" not found locally.`);
568
566
  console.log(` Try: skm install ${skillName}`);
@@ -609,7 +607,7 @@ async function verifySkill(skillName) {
609
607
  } else {
610
608
  console.log(`\u26A0\uFE0F package.json not found (optional for basic skills)`);
611
609
  }
612
- const registryPath = path3.join(process.env.HOME || process.env.USERPROFILE || "", ".skillmarket", "registry.json");
610
+ const registryPath = getRegistryPath();
613
611
  if (await fs3.pathExists(registryPath)) {
614
612
  try {
615
613
  const registry = await fs3.readJson(registryPath);
@@ -646,9 +644,9 @@ async function verifySkill(skillName) {
646
644
  }
647
645
 
648
646
  // src/cli.ts
649
- var __filename2 = fileURLToPath2(import.meta.url);
650
- var __dirname2 = dirname(__filename2);
651
- var packageJson = JSON.parse(readFileSync(resolve(__dirname2, "../package.json"), "utf-8"));
647
+ var __filename = fileURLToPath(import.meta.url);
648
+ var __dirname = dirname(__filename);
649
+ var packageJson = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
652
650
  var VERSION = packageJson.version || "1.3.1";
653
651
  var program = new Command();
654
652
  program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version(VERSION);
package/gui/app.js CHANGED
@@ -697,7 +697,7 @@ function applyI18nToStaticElements() {
697
697
  // 主题切换
698
698
  // -----------------------------------------------------------------------------
699
699
 
700
- const THEMES = ['ocean', 'purple', 'forest', 'amber', 'sepia', 'gray', 'teal', 'dusk'];
700
+ const THEMES = ['ocean', 'purple', 'forest', 'amber', 'sepia', 'gray', 'teal', 'dusk', 'light'];
701
701
 
702
702
  function detectTheme() {
703
703
  const saved = localStorage.getItem('skm-theme');
package/gui/index.html CHANGED
@@ -32,6 +32,7 @@
32
32
  <option value="gray">🌫 柔灰</option>
33
33
  <option value="teal">🦆 墨青</option>
34
34
  <option value="dusk">🌆 暮紫</option>
35
+ <option value="light">☀ 纯白</option>
35
36
  </select>
36
37
  </div>
37
38
  <div class="lang-switch">
package/gui/style.css CHANGED
@@ -144,6 +144,23 @@ body.theme-dusk {
144
144
  --danger: #b55373;
145
145
  }
146
146
 
147
+ /* 纯白 - 明亮模式 */
148
+ body.theme-light {
149
+ --bg-primary: #f0f2f5;
150
+ --bg-secondary: #ffffff;
151
+ --bg-card: #ffffff;
152
+ --bg-hover: #e4e6eb;
153
+ --text-primary: #e94560;
154
+ --text-secondary: #1a1a2e;
155
+ --text-muted: #8a8a9a;
156
+ --border-color: #d0d2d8;
157
+ --accent: #e94560;
158
+ --accent-hover: #d63850;
159
+ --success: #4caf50;
160
+ --warning: #ff9800;
161
+ --danger: #f44336;
162
+ }
163
+
147
164
  * {
148
165
  margin: 0;
149
166
  padding: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.45",
3
+ "version": "1.3.46",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {