itismyskillmarket 1.3.9 → 1.3.10

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/dist/index.js CHANGED
@@ -103,7 +103,21 @@ async function isSkillInstalled(skillId) {
103
103
  // src/commands/npm.ts
104
104
  import https from "https";
105
105
  import { URL as URL2 } from "url";
106
- async function fetchNpmPackage(packageName) {
106
+ async function fetchNpmPackage(packageName, retries = 1) {
107
+ for (let attempt = 0; attempt <= retries; attempt++) {
108
+ try {
109
+ const result = await fetchNpmPackageOnce(packageName);
110
+ if (result !== null) return result;
111
+ } catch {
112
+ if (attempt === retries) return null;
113
+ }
114
+ if (attempt < retries) {
115
+ await new Promise((r) => setTimeout(r, 500));
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+ async function fetchNpmPackageOnce(packageName) {
107
121
  return new Promise((resolve2, reject) => {
108
122
  const isScoped = packageName.startsWith("@");
109
123
  let encodedName;
@@ -123,6 +137,11 @@ async function fetchNpmPackage(packageName) {
123
137
  const url = new URL2(`https://registry.npmjs.org/${encodedName}`);
124
138
  const req = https.get(url.toString(), { timeout: 1e4 }, (res) => {
125
139
  let data = "";
140
+ if (res.statusCode && res.statusCode >= 400) {
141
+ res.resume();
142
+ resolve2(null);
143
+ return;
144
+ }
126
145
  res.on("data", (chunk) => {
127
146
  data += chunk;
128
147
  });
@@ -1607,6 +1626,7 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1607
1626
  setCache(cacheKey, searchResult, 3e4);
1608
1627
  }
1609
1628
  const { packages, total } = searchResult;
1629
+ let fetchErrors = 0;
1610
1630
  const skillDetails = await throttledMap(packages, async (pkgName) => {
1611
1631
  try {
1612
1632
  const pkgCacheKey = `pkg:${pkgName}`;
@@ -1615,7 +1635,10 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1615
1635
  info = await fetchNpmPackage(pkgName);
1616
1636
  if (info) setCache(pkgCacheKey, info, 3e4);
1617
1637
  }
1618
- if (!info) return null;
1638
+ if (!info) {
1639
+ fetchErrors++;
1640
+ return null;
1641
+ }
1619
1642
  const latestVersion = info["dist-tags"]?.latest || "unknown";
1620
1643
  const pkg = info.versions?.[latestVersion];
1621
1644
  const meta = pkg?.skillmarket;
@@ -1631,12 +1654,13 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
1631
1654
  repository: pkg?.repository?.url || ""
1632
1655
  };
1633
1656
  } catch {
1657
+ fetchErrors++;
1634
1658
  return null;
1635
1659
  }
1636
1660
  }, 3);
1637
1661
  const skills = skillDetails.filter(Boolean);
1638
1662
  const totalPages = Math.ceil(total / limit) || 1;
1639
- jsonResponse(res, 200, { skills, page, totalPages, total });
1663
+ jsonResponse(res, 200, { skills, page, totalPages, total, fetchErrors });
1640
1664
  } catch (err) {
1641
1665
  jsonResponse(res, 500, {
1642
1666
  error: String(err),
@@ -1779,7 +1803,12 @@ function serveStaticFile(res, filePath) {
1779
1803
  const content = readFileSync(filePath);
1780
1804
  const ext = extname(filePath);
1781
1805
  const mime = MIME_TYPES[ext] || "application/octet-stream";
1782
- res.writeHead(200, { "Content-Type": mime });
1806
+ res.writeHead(200, {
1807
+ "Content-Type": mime,
1808
+ "Cache-Control": "no-cache, no-store, must-revalidate",
1809
+ "Pragma": "no-cache",
1810
+ "Expires": "0"
1811
+ });
1783
1812
  res.end(content);
1784
1813
  }
1785
1814
  async function handleRequest(req, res) {
package/gui/app.js CHANGED
@@ -135,6 +135,7 @@ async function loadSkills() {
135
135
 
136
136
  renderSkills(data.skills || data, container);
137
137
  renderPagination(data.page, data.totalPages || 1);
138
+ renderFetchWarning(data.fetchErrors);
138
139
  } catch (err) {
139
140
  container.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
140
141
  }
@@ -191,6 +192,23 @@ function createSkillCard(skill, isInstalled) {
191
192
  `;
192
193
  }
193
194
 
195
+ // -----------------------------------------------------------------------------
196
+ // 网络错误提示
197
+ // -----------------------------------------------------------------------------
198
+
199
+ function renderFetchWarning(fetchErrors) {
200
+ const existing = document.getElementById('fetch-warning');
201
+ if (existing) existing.remove();
202
+
203
+ if (!fetchErrors || fetchErrors === 0) return;
204
+
205
+ const warning = document.createElement('div');
206
+ warning.id = 'fetch-warning';
207
+ warning.style.cssText = 'background: #664400; color: #ffcc00; padding: 8px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem;';
208
+ warning.textContent = `⚠ ${fetchErrors} skill(s) failed to load details from npm registry. Refresh to retry.`;
209
+ document.querySelector('.view-header').after(warning);
210
+ }
211
+
194
212
  // -----------------------------------------------------------------------------
195
213
  // 分页
196
214
  // -----------------------------------------------------------------------------
package/gui/index.html CHANGED
@@ -25,7 +25,7 @@
25
25
  </button>
26
26
  </nav>
27
27
  <div class="sidebar-footer">
28
- <span class="version" id="gui-version">v1.3.9</span>
28
+ <span class="version" id="gui-version">v1.3.10</span>
29
29
  </div>
30
30
  </aside>
31
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {