itismyskillmarket 1.3.38 → 1.3.40
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/{chunk-NWGRVBSX.js → chunk-VRXNOGLL.js} +83 -52
- package/dist/electron-entry.js +1 -1
- package/dist/index.js +1 -1
- package/gui/app.js +38 -5
- package/package.json +1 -1
|
@@ -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
|
-
|
|
640
|
-
|
|
641
|
-
if (
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
2164
|
+
const skillMdPath = findFileSync(tempDir, "SKILL.md");
|
|
2140
2165
|
const skillMdExists = skillMdPath !== null;
|
|
2141
|
-
const pkgJsonPath = findFileSync(
|
|
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
|
-
|
|
2178
|
-
|
|
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) });
|
package/dist/electron-entry.js
CHANGED
package/dist/index.js
CHANGED
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', () =>
|
|
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
|
-
|
|
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', () =>
|
|
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
|
-
|
|
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
|
|