itismyskillmarket 1.3.38 → 1.3.39

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.
@@ -5,6 +5,7 @@ import { createServer } from "http";
5
5
  import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync, rmSync, readdirSync, renameSync } from "fs";
6
6
  import { join as join3, extname, dirname, basename } from "path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "url";
8
+ import { tmpdir } from "os";
8
9
  import AdmZip from "adm-zip";
9
10
 
10
11
  // src/commands/npm.ts
@@ -636,44 +637,57 @@ import * as tar from "tar";
636
637
  var execAsync = promisify(exec);
637
638
  async function installSkill(skillId, version, options) {
638
639
  await ensureMarketDirs();
639
- console.log(`Installing ${skillId}${version ? `@${version}` : ""}...`);
640
- const pkgInfo = await fetchSkillPackage(skillId);
641
- if (!pkgInfo) {
642
- throw new Error(`Package ${skillId} not found`);
643
- }
644
- const packageName = pkgInfo.name;
645
- const targetVersion = version || pkgInfo["dist-tags"]?.latest;
646
- if (!targetVersion) {
647
- throw new Error(`No version found for ${packageName}`);
648
- }
649
- const cacheDir = getCacheDir();
650
- const targetDir = path9.join(cacheDir, `${packageName}@${targetVersion}`);
651
- if (!await fs10.pathExists(targetDir)) {
652
- console.log("Downloading package...");
653
- await fs10.ensureDir(cacheDir);
654
- try {
655
- const { stdout } = await execAsync(
656
- `npm pack ${packageName}@${targetVersion} --pack-destination "${cacheDir}"`
657
- );
658
- const tarballName = stdout.trim();
659
- const tarballPath = path9.join(cacheDir, tarballName);
660
- if (await fs10.pathExists(tarballPath)) {
661
- await tar.extract({
662
- file: tarballPath,
663
- cwd: cacheDir
664
- });
665
- await fs10.remove(tarballPath);
666
- await fs10.move(path9.join(cacheDir, "package"), targetDir, { overwrite: true });
640
+ let targetVersion;
641
+ let pkgRoot;
642
+ if (options?.sourceDir) {
643
+ console.log(`Installing ${skillId} from local source...`);
644
+ pkgRoot = options.sourceDir;
645
+ const pkgJsonPath = path9.join(pkgRoot, "package.json");
646
+ targetVersion = version || "0.0.0";
647
+ if (await fs10.pathExists(pkgJsonPath)) {
648
+ try {
649
+ const pkg = JSON.parse(await fs10.readFile(pkgJsonPath, "utf-8"));
650
+ if (pkg.version) targetVersion = pkg.version;
651
+ } catch {
667
652
  }
668
- } catch (err) {
669
- throw new Error(`Failed to download package: ${err}`);
670
653
  }
654
+ } else {
655
+ console.log(`Installing ${skillId}${version ? `@${version}` : ""}...`);
656
+ const pkgInfo = await fetchSkillPackage(skillId);
657
+ if (!pkgInfo) {
658
+ throw new Error(`Package ${skillId} not found`);
659
+ }
660
+ const packageName = pkgInfo.name;
661
+ targetVersion = version || pkgInfo["dist-tags"]?.latest;
662
+ if (!targetVersion) {
663
+ throw new Error(`No version found for ${packageName}`);
664
+ }
665
+ const cacheDir = getCacheDir();
666
+ const targetDir = path9.join(cacheDir, `${packageName}@${targetVersion}`);
667
+ if (!await fs10.pathExists(targetDir)) {
668
+ console.log("Downloading package...");
669
+ await fs10.ensureDir(cacheDir);
670
+ try {
671
+ const { stdout } = await execAsync(
672
+ `npm pack ${packageName}@${targetVersion} --pack-destination "${cacheDir}"`
673
+ );
674
+ const tarballName = stdout.trim();
675
+ const tarballPath = path9.join(cacheDir, tarballName);
676
+ if (await fs10.pathExists(tarballPath)) {
677
+ await tar.extract({ file: tarballPath, cwd: cacheDir });
678
+ await fs10.remove(tarballPath);
679
+ await fs10.move(path9.join(cacheDir, "package"), targetDir, { overwrite: true });
680
+ }
681
+ } catch (err) {
682
+ throw new Error(`Failed to download package: ${err}`);
683
+ }
684
+ }
685
+ pkgRoot = targetDir;
671
686
  }
672
687
  const skillsDir = getSkillsDir();
673
688
  const skillVersionDir = path9.join(skillsDir, `${skillId}@${targetVersion}`);
674
689
  console.log("Setting up skill...");
675
690
  await fs10.ensureDir(skillVersionDir);
676
- const pkgRoot = targetDir;
677
691
  if (await fs10.pathExists(path9.join(pkgRoot, "SKILL.md"))) {
678
692
  await fs10.copy(
679
693
  path9.join(pkgRoot, "SKILL.md"),
@@ -1035,9 +1049,14 @@ import { fileURLToPath } from "url";
1035
1049
  import { promisify as promisify2 } from "util";
1036
1050
  var execAsync2 = promisify2(exec2);
1037
1051
  async function publishSkill(skillName, options) {
1038
- const __dirname2 = fileURLToPath(new URL(".", import.meta.url));
1039
- const projectRoot = join2(__dirname2, "..");
1040
- const skillDir = join2(projectRoot, "skills", skillName);
1052
+ let skillDir;
1053
+ if (options?.skillDir) {
1054
+ skillDir = options.skillDir;
1055
+ } else {
1056
+ const __dirname2 = fileURLToPath(new URL(".", import.meta.url));
1057
+ const projectRoot = join2(__dirname2, "..");
1058
+ skillDir = join2(projectRoot, "skills", skillName);
1059
+ }
1041
1060
  console.log(`Publishing ${skillName}...`);
1042
1061
  if (!existsSync2(skillDir)) {
1043
1062
  throw new Error(`Skill '${skillName}' not found in skills/ directory`);
@@ -1578,6 +1597,7 @@ async function adminAccess(skillId, level) {
1578
1597
  }
1579
1598
 
1580
1599
  // src/commands/ui.ts
1600
+ var MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
1581
1601
  var __filename = fileURLToPath2(import.meta.url);
1582
1602
  var __dirname = dirname(__filename);
1583
1603
  var guiDir = join3(__dirname, "..", "gui");
@@ -2074,7 +2094,6 @@ API_ROUTES.POST["/api/update"] = async (req, res, _url) => {
2074
2094
  jsonResponse(res, 500, { error: String(err) });
2075
2095
  }
2076
2096
  };
2077
- var PROJECT_ROOT = join3(__dirname, "..");
2078
2097
  API_ROUTES.POST["/api/upload"] = async (req, res, _url) => {
2079
2098
  try {
2080
2099
  const body = await parseBody(req);
@@ -2090,6 +2109,11 @@ API_ROUTES.POST["/api/upload"] = async (req, res, _url) => {
2090
2109
  jsonResponse(res, 400, { error: "Empty file data" });
2091
2110
  return;
2092
2111
  }
2112
+ if (buffer.length > MAX_UPLOAD_SIZE) {
2113
+ const sizeMB = (buffer.length / 1024 / 1024).toFixed(1);
2114
+ jsonResponse(res, 400, { error: `File too large (${sizeMB} MB). Maximum is 50 MB.` });
2115
+ return;
2116
+ }
2093
2117
  const zip = new AdmZip(buffer);
2094
2118
  const entries = zip.getEntries();
2095
2119
  if (entries.length === 0) {
@@ -2119,26 +2143,27 @@ API_ROUTES.POST["/api/upload"] = async (req, res, _url) => {
2119
2143
  }
2120
2144
  skillName = skillName.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
2121
2145
  if (!skillName) skillName = "untitled-skill";
2122
- const skillDir = join3(PROJECT_ROOT, "skills", skillName);
2123
- if (existsSync3(skillDir)) {
2124
- rmSync(skillDir, { recursive: true, force: true });
2125
- }
2126
- mkdirSync(skillDir, { recursive: true });
2127
- zip.extractAllTo(skillDir, true);
2128
- const extractedItems = readdirSync(skillDir, { withFileTypes: true });
2146
+ const uploadId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2147
+ const tempDir = join3(tmpdir(), `skm-upload-${uploadId}`);
2148
+ if (existsSync3(tempDir)) {
2149
+ rmSync(tempDir, { recursive: true, force: true });
2150
+ }
2151
+ mkdirSync(tempDir, { recursive: true });
2152
+ zip.extractAllTo(tempDir, true);
2153
+ const extractedItems = readdirSync(tempDir, { withFileTypes: true });
2129
2154
  const subDirs = extractedItems.filter((i) => i.isDirectory());
2130
2155
  const files = extractedItems.filter((i) => !i.isDirectory());
2131
2156
  if (subDirs.length === 1 && files.length === 0) {
2132
- const subDirPath = join3(skillDir, subDirs[0].name);
2157
+ const subDirPath = join3(tempDir, subDirs[0].name);
2133
2158
  const subItems = readdirSync(subDirPath, { withFileTypes: true });
2134
2159
  for (const item of subItems) {
2135
- renameSync(join3(subDirPath, item.name), join3(skillDir, item.name));
2160
+ renameSync(join3(subDirPath, item.name), join3(tempDir, item.name));
2136
2161
  }
2137
2162
  rmSync(subDirPath, { recursive: true, force: true });
2138
2163
  }
2139
- const skillMdPath = findFileSync(skillDir, "SKILL.md");
2164
+ const skillMdPath = findFileSync(tempDir, "SKILL.md");
2140
2165
  const skillMdExists = skillMdPath !== null;
2141
- const pkgJsonPath = findFileSync(skillDir, "package.json") || join3(skillDir, "package.json");
2166
+ const pkgJsonPath = findFileSync(tempDir, "package.json") || join3(tempDir, "package.json");
2142
2167
  if (existsSync3(pkgJsonPath)) {
2143
2168
  try {
2144
2169
  pkgInfo = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
@@ -2154,7 +2179,9 @@ API_ROUTES.POST["/api/upload"] = async (req, res, _url) => {
2154
2179
  platforms: meta.platforms || [],
2155
2180
  hasPackageJson: existsSync3(pkgJsonPath),
2156
2181
  hasSkillMd: skillMdExists,
2157
- fileCount: entries.length
2182
+ fileCount: entries.length,
2183
+ tempDir
2184
+ // 返回临时目录路径,供后续 action 使用
2158
2185
  };
2159
2186
  jsonResponse(res, 200, result);
2160
2187
  } catch (err) {
@@ -2166,6 +2193,7 @@ API_ROUTES.POST["/api/upload/action"] = async (req, res, _url) => {
2166
2193
  const body = await parseBody(req);
2167
2194
  const skillName = String(body.skillName || "");
2168
2195
  const action = String(body.action || "");
2196
+ const tempDir = body.tempDir ? String(body.tempDir) : "";
2169
2197
  if (!skillName) {
2170
2198
  jsonResponse(res, 400, { error: "Missing skillName" });
2171
2199
  return;
@@ -2174,15 +2202,14 @@ API_ROUTES.POST["/api/upload/action"] = async (req, res, _url) => {
2174
2202
  jsonResponse(res, 400, { error: 'action must be "publish", "install", or "both"' });
2175
2203
  return;
2176
2204
  }
2177
- const skillDir = join3(PROJECT_ROOT, "skills", skillName);
2178
- if (!existsSync3(skillDir)) {
2179
- jsonResponse(res, 404, { error: `Skill "${skillName}" not found in skills/ directory. Upload first.` });
2205
+ if (!tempDir || !existsSync3(tempDir)) {
2206
+ jsonResponse(res, 404, { error: "Upload session expired or not found. Please upload again." });
2180
2207
  return;
2181
2208
  }
2182
2209
  const results = {};
2183
2210
  if (action === "publish" || action === "both") {
2184
2211
  try {
2185
- await publishSkill(skillName);
2212
+ await publishSkill(skillName, { skillDir: tempDir });
2186
2213
  results.publish = { success: true, message: `${skillName} published to npm` };
2187
2214
  } catch (err) {
2188
2215
  results.publish = { success: false, message: String(err) };
@@ -2190,12 +2217,16 @@ API_ROUTES.POST["/api/upload/action"] = async (req, res, _url) => {
2190
2217
  }
2191
2218
  if (action === "install" || action === "both") {
2192
2219
  try {
2193
- await installSkill(skillName, void 0, { force: true });
2220
+ await installSkill(skillName, void 0, { force: true, sourceDir: tempDir });
2194
2221
  results.install = { success: true, message: `${skillName} installed locally` };
2195
2222
  } catch (err) {
2196
2223
  results.install = { success: false, message: String(err) };
2197
2224
  }
2198
2225
  }
2226
+ try {
2227
+ rmSync(tempDir, { recursive: true, force: true });
2228
+ } catch {
2229
+ }
2199
2230
  jsonResponse(res, 200, { success: true, skillName, action, results });
2200
2231
  } catch (err) {
2201
2232
  jsonResponse(res, 500, { error: String(err) });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startGuiServer
4
- } from "./chunk-NWGRVBSX.js";
4
+ } from "./chunk-VRXNOGLL.js";
5
5
  export {
6
6
  startGuiServer
7
7
  };
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  uninstallAll,
35
35
  uninstallSkill,
36
36
  updateSkill
37
- } from "./chunk-NWGRVBSX.js";
37
+ } from "./chunk-VRXNOGLL.js";
38
38
 
39
39
  // src/cli.ts
40
40
  import { Command } from "commander";
package/gui/app.js CHANGED
@@ -1933,8 +1933,12 @@ const uploadState = {
1933
1933
  skillName: '',
1934
1934
  file: null,
1935
1935
  data: null, // Parsed result from backend
1936
+ tempDir: '', // Temporary directory path from server
1936
1937
  };
1937
1938
 
1939
+ /** 上传文件大小限制:50 MB */
1940
+ const MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
1941
+
1938
1942
  /** 初始化 Upload 控件 */
1939
1943
  function initializeUploadControls() {
1940
1944
  const dropzone = document.getElementById('upload-dropzone');
@@ -1946,16 +1950,30 @@ function initializeUploadControls() {
1946
1950
  if (!dropzone) return;
1947
1951
 
1948
1952
  // 文件选择
1949
- selectBtn.addEventListener('click', () => fileInput.click());
1953
+ selectBtn.addEventListener('click', (e) => {
1954
+ e.stopPropagation(); // 防止冒泡到 dropzone 的 click 事件
1955
+ fileInput.click();
1956
+ });
1950
1957
  fileInput.addEventListener('change', (e) => {
1951
1958
  if (e.target.files.length > 0) {
1952
- uploadState.file = e.target.files[0];
1959
+ const file = e.target.files[0];
1960
+ if (file.size > MAX_UPLOAD_SIZE) {
1961
+ const sizeMB = (file.size / 1024 / 1024).toFixed(1);
1962
+ showToast(`File too large (${sizeMB} MB). Maximum is 50 MB.`, 'error');
1963
+ e.target.value = '';
1964
+ return;
1965
+ }
1966
+ uploadState.file = file;
1953
1967
  submitBtn.disabled = false;
1954
1968
  }
1955
1969
  });
1956
1970
 
1957
- // 拖拽上传
1958
- dropzone.addEventListener('click', () => fileInput.click());
1971
+ // 拖拽上传 — 仅处理拖拽事件,点击由 Choose File 按钮或单独点击处理
1972
+ dropzone.addEventListener('click', (e) => {
1973
+ // 如果点击的是内部按钮,不重复触发文件选择(由按钮的 stopPropagation 处理)
1974
+ if (e.target.closest('button')) return;
1975
+ fileInput.click();
1976
+ });
1959
1977
 
1960
1978
  dropzone.addEventListener('dragover', (e) => {
1961
1979
  e.preventDefault();
@@ -1970,7 +1988,13 @@ function initializeUploadControls() {
1970
1988
  e.preventDefault();
1971
1989
  dropzone.classList.remove('drag-over');
1972
1990
  if (e.dataTransfer.files.length > 0) {
1973
- uploadState.file = e.dataTransfer.files[0];
1991
+ const file = e.dataTransfer.files[0];
1992
+ if (file.size > MAX_UPLOAD_SIZE) {
1993
+ const sizeMB = (file.size / 1024 / 1024).toFixed(1);
1994
+ showToast(`File too large (${sizeMB} MB). Maximum is 50 MB.`, 'error');
1995
+ return;
1996
+ }
1997
+ uploadState.file = file;
1974
1998
  submitBtn.disabled = false;
1975
1999
  }
1976
2000
  });
@@ -1981,6 +2005,12 @@ function initializeUploadControls() {
1981
2005
  showToast(t('upload.errorNoFile'), 'error');
1982
2006
  return;
1983
2007
  }
2008
+ // 文件大小校验
2009
+ if (uploadState.file.size > MAX_UPLOAD_SIZE) {
2010
+ const sizeMB = (uploadState.file.size / 1024 / 1024).toFixed(1);
2011
+ showToast(`File too large (${sizeMB} MB). Maximum is 50 MB.`, 'error');
2012
+ return;
2013
+ }
1984
2014
  // Use skill name override if provided
1985
2015
  const override = skillNameInput.value.trim();
1986
2016
  handleUpload(uploadState.file, override || undefined);
@@ -2007,6 +2037,7 @@ function resetUploadView() {
2007
2037
  uploadState.file = null;
2008
2038
  uploadState.data = null;
2009
2039
  uploadState.skillName = '';
2040
+ uploadState.tempDir = '';
2010
2041
  document.getElementById('upload-phase1').classList.remove('hidden');
2011
2042
  document.getElementById('upload-phase2').classList.add('hidden');
2012
2043
  document.getElementById('upload-submit-btn').disabled = true;
@@ -2068,6 +2099,7 @@ async function handleUpload(file, skillNameOverride) {
2068
2099
 
2069
2100
  uploadState.data = result;
2070
2101
  uploadState.skillName = skillNameOverride || result.skillName;
2102
+ uploadState.tempDir = result.tempDir || '';
2071
2103
 
2072
2104
  // Switch to preview phase
2073
2105
  setTimeout(() => {
@@ -2151,6 +2183,7 @@ async function executeUploadAction(action) {
2151
2183
  body: JSON.stringify({
2152
2184
  skillName: uploadState.skillName,
2153
2185
  action: action,
2186
+ tempDir: uploadState.tempDir,
2154
2187
  }),
2155
2188
  });
2156
2189
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.38",
3
+ "version": "1.3.39",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {