itismyskillmarket 1.3.45 → 1.3.47
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-ALNUNP4E.js → chunk-KDSWKP7T.js} +154 -57
- package/dist/electron-entry.js +1 -1
- package/dist/index.js +93 -241
- package/gui/app.js +1 -1
- package/gui/index.html +1 -0
- package/gui/style.css +17 -0
- package/package.json +1 -1
|
@@ -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/
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1182,6 +1264,24 @@ async function publishSkill(skillName, options) {
|
|
|
1182
1264
|
|
|
1183
1265
|
// src/commands/admin.ts
|
|
1184
1266
|
import { execSync as execSync2 } from "child_process";
|
|
1267
|
+
|
|
1268
|
+
// src/utils/concurrency.ts
|
|
1269
|
+
async function throttledMap(items, fn, concurrency = 3, batchDelayMs = 200) {
|
|
1270
|
+
const results = [];
|
|
1271
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
1272
|
+
const batch = items.slice(i, i + concurrency);
|
|
1273
|
+
const batchResults = await Promise.all(
|
|
1274
|
+
batch.map((item, idx) => fn(item, i + idx))
|
|
1275
|
+
);
|
|
1276
|
+
results.push(...batchResults);
|
|
1277
|
+
if (i + concurrency < items.length) {
|
|
1278
|
+
await new Promise((r) => setTimeout(r, batchDelayMs));
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return results;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/commands/admin.ts
|
|
1185
1285
|
async function fetchScopePackages() {
|
|
1186
1286
|
const all = /* @__PURE__ */ new Set();
|
|
1187
1287
|
for (const scope of SKILL_SCOPES) {
|
|
@@ -1202,8 +1302,9 @@ async function adminList() {
|
|
|
1202
1302
|
console.log("No published skills found.");
|
|
1203
1303
|
return;
|
|
1204
1304
|
}
|
|
1205
|
-
const details = await
|
|
1206
|
-
packages
|
|
1305
|
+
const details = await throttledMap(
|
|
1306
|
+
packages,
|
|
1307
|
+
async (pkg) => {
|
|
1207
1308
|
try {
|
|
1208
1309
|
const info = await fetchNpmPackage(pkg);
|
|
1209
1310
|
if (!info) return null;
|
|
@@ -1220,7 +1321,11 @@ async function adminList() {
|
|
|
1220
1321
|
} catch {
|
|
1221
1322
|
return null;
|
|
1222
1323
|
}
|
|
1223
|
-
}
|
|
1324
|
+
},
|
|
1325
|
+
3,
|
|
1326
|
+
// 并发 3
|
|
1327
|
+
200
|
|
1328
|
+
// 批次间 200ms
|
|
1224
1329
|
);
|
|
1225
1330
|
const valid = details.filter(Boolean);
|
|
1226
1331
|
console.log(`\u{1F4E6} ${valid.length} published skill(s):
|
|
@@ -1347,15 +1452,20 @@ async function adminStats() {
|
|
|
1347
1452
|
console.log("No published skills found.");
|
|
1348
1453
|
return;
|
|
1349
1454
|
}
|
|
1350
|
-
const infos = (await
|
|
1351
|
-
packages
|
|
1455
|
+
const infos = (await throttledMap(
|
|
1456
|
+
packages,
|
|
1457
|
+
async (pkg) => {
|
|
1352
1458
|
try {
|
|
1353
1459
|
const info = await fetchNpmPackage(pkg);
|
|
1354
1460
|
return info ? { name: pkg, info } : null;
|
|
1355
1461
|
} catch {
|
|
1356
1462
|
return null;
|
|
1357
1463
|
}
|
|
1358
|
-
}
|
|
1464
|
+
},
|
|
1465
|
+
3,
|
|
1466
|
+
// 并发 3
|
|
1467
|
+
200
|
|
1468
|
+
// 批次间 200ms
|
|
1359
1469
|
)).filter(Boolean);
|
|
1360
1470
|
const totalSkills = infos.length;
|
|
1361
1471
|
let totalVersions = 0;
|
|
@@ -1872,30 +1982,16 @@ var MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
|
|
|
1872
1982
|
var __filename = fileURLToPath2(import.meta.url);
|
|
1873
1983
|
var __dirname = dirname(__filename);
|
|
1874
1984
|
var guiDir = join3(__dirname, "..", "gui");
|
|
1875
|
-
var
|
|
1876
|
-
function
|
|
1877
|
-
|
|
1878
|
-
if (
|
|
1879
|
-
|
|
1880
|
-
cache.delete(key);
|
|
1881
|
-
return null;
|
|
1882
|
-
}
|
|
1883
|
-
return entry.data;
|
|
1884
|
-
}
|
|
1885
|
-
function setCache2(key, data, ttlMs = 6e4) {
|
|
1886
|
-
cache.set(key, { data, expiry: Date.now() + ttlMs });
|
|
1985
|
+
var uiCache = new TtlCache();
|
|
1986
|
+
function getAuthorName(author) {
|
|
1987
|
+
if (!author) return "";
|
|
1988
|
+
if (typeof author === "string") return author.replace(/<[^>]*>/g, "").trim();
|
|
1989
|
+
return author.name || "";
|
|
1887
1990
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
const batchResults = await Promise.all(batch.map((item, idx) => fn(item, i + idx)));
|
|
1893
|
-
results.push(...batchResults);
|
|
1894
|
-
if (i + concurrency < items.length) {
|
|
1895
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
return results;
|
|
1991
|
+
function getRepoUrl(repo) {
|
|
1992
|
+
if (!repo) return "";
|
|
1993
|
+
if (typeof repo === "string") return repo;
|
|
1994
|
+
return repo.url || "";
|
|
1899
1995
|
}
|
|
1900
1996
|
var MIME_TYPES = {
|
|
1901
1997
|
".html": "text/html; charset=utf-8",
|
|
@@ -1943,7 +2039,7 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1943
2039
|
const sort = url.searchParams.get("sort") || "name";
|
|
1944
2040
|
const platform = url.searchParams.get("platform") || "";
|
|
1945
2041
|
const cacheKey = `search:${search}:limit:${limit}`;
|
|
1946
|
-
let searchResult =
|
|
2042
|
+
let searchResult = uiCache.get(cacheKey);
|
|
1947
2043
|
if (!searchResult) {
|
|
1948
2044
|
searchResult = await searchSkillmarketPackages({
|
|
1949
2045
|
from: 0,
|
|
@@ -1951,17 +2047,17 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1951
2047
|
// 一次拉取更多,避免分页
|
|
1952
2048
|
keyword: search || void 0
|
|
1953
2049
|
});
|
|
1954
|
-
|
|
2050
|
+
uiCache.set(cacheKey, searchResult, 3e4);
|
|
1955
2051
|
}
|
|
1956
2052
|
const { packages, total } = searchResult;
|
|
1957
2053
|
let fetchErrors = 0;
|
|
1958
2054
|
const skillDetails = await throttledMap(packages, async (pkgName) => {
|
|
1959
2055
|
try {
|
|
1960
2056
|
const pkgCacheKey = `pkg:${pkgName}`;
|
|
1961
|
-
let info =
|
|
2057
|
+
let info = uiCache.get(pkgCacheKey);
|
|
1962
2058
|
if (!info) {
|
|
1963
2059
|
info = await fetchNpmPackage(pkgName);
|
|
1964
|
-
if (info)
|
|
2060
|
+
if (info) uiCache.set(pkgCacheKey, info, 3e4);
|
|
1965
2061
|
}
|
|
1966
2062
|
if (!info) {
|
|
1967
2063
|
fetchErrors++;
|
|
@@ -1977,9 +2073,9 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1977
2073
|
version: latestVersion,
|
|
1978
2074
|
description: pkg?.description || "",
|
|
1979
2075
|
platforms: meta?.platforms || [],
|
|
1980
|
-
author: info.author
|
|
2076
|
+
author: getAuthorName(info.author) || getAuthorName(pkg?.author),
|
|
1981
2077
|
homepage: pkg?.homepage || "",
|
|
1982
|
-
repository: pkg?.repository
|
|
2078
|
+
repository: getRepoUrl(pkg?.repository),
|
|
1983
2079
|
updated: info.time?.[latestVersion] || info.time?.modified || ""
|
|
1984
2080
|
};
|
|
1985
2081
|
} catch {
|
|
@@ -2095,10 +2191,10 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
|
|
|
2095
2191
|
return;
|
|
2096
2192
|
}
|
|
2097
2193
|
const cacheKey = `skill-info:${skillName}`;
|
|
2098
|
-
let info =
|
|
2194
|
+
let info = uiCache.get(cacheKey);
|
|
2099
2195
|
if (!info) {
|
|
2100
2196
|
info = await fetchSkillPackage(skillName);
|
|
2101
|
-
if (info)
|
|
2197
|
+
if (info) uiCache.set(cacheKey, info, 3e4);
|
|
2102
2198
|
}
|
|
2103
2199
|
if (!info) {
|
|
2104
2200
|
jsonResponse(res, 404, { error: `Skill "${skillName}" not found` });
|
|
@@ -2117,10 +2213,10 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
|
|
|
2117
2213
|
version: latestVersion,
|
|
2118
2214
|
platforms: meta?.platforms || [],
|
|
2119
2215
|
versions: recentVersions,
|
|
2120
|
-
author: info.author
|
|
2216
|
+
author: getAuthorName(info.author) || getAuthorName(pkg?.author),
|
|
2121
2217
|
license: info.license || "",
|
|
2122
2218
|
homepage: pkg?.homepage || "",
|
|
2123
|
-
repository: pkg?.repository
|
|
2219
|
+
repository: getRepoUrl(pkg?.repository),
|
|
2124
2220
|
readme: info.readme || ""
|
|
2125
2221
|
});
|
|
2126
2222
|
} catch (err) {
|
|
@@ -2228,7 +2324,7 @@ API_ROUTES.GET["/api/admin/stats"] = async (_req, res, _url) => {
|
|
|
2228
2324
|
return null;
|
|
2229
2325
|
}
|
|
2230
2326
|
})
|
|
2231
|
-
)).filter(
|
|
2327
|
+
)).filter((item) => item !== null);
|
|
2232
2328
|
let totalVersions = 0;
|
|
2233
2329
|
let totalSize = 0;
|
|
2234
2330
|
const platformSet = /* @__PURE__ */ new Set();
|
|
@@ -2625,6 +2721,7 @@ export {
|
|
|
2625
2721
|
LATEST_LINK,
|
|
2626
2722
|
getSkillsDir,
|
|
2627
2723
|
getPlatformLinksDir,
|
|
2724
|
+
getRegistryPath,
|
|
2628
2725
|
ensureMarketDirs,
|
|
2629
2726
|
loadRegistry,
|
|
2630
2727
|
saveRegistry,
|
package/dist/electron-entry.js
CHANGED
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-
|
|
42
|
+
} from "./chunk-KDSWKP7T.js";
|
|
42
43
|
|
|
43
44
|
// src/cli.ts
|
|
44
45
|
import { Command } from "commander";
|
|
45
46
|
import { readFileSync } from "fs";
|
|
46
|
-
import { fileURLToPath
|
|
47
|
+
import { fileURLToPath } from "url";
|
|
47
48
|
import { dirname, resolve } from "path";
|
|
48
49
|
import { execSync } from "child_process";
|
|
49
50
|
|
|
@@ -261,9 +262,14 @@ async function syncPlatformLinks() {
|
|
|
261
262
|
await fs.remove(platformSkillDir);
|
|
262
263
|
await fs.symlink(targetPlatformDir, platformSkillDir, "junction");
|
|
263
264
|
console.log(` Linked: ${platform}/${skillId}`);
|
|
264
|
-
} catch {
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
} catch (err) {
|
|
266
|
+
const nodeErr = err;
|
|
267
|
+
if (nodeErr.code === "EPERM" || nodeErr.code === "EACCES" || nodeErr.code === "ENOTSUP") {
|
|
268
|
+
await fs.copy(targetPlatformDir, platformSkillDir, { overwrite: true });
|
|
269
|
+
console.log(` Copied: ${platform}/${skillId}`);
|
|
270
|
+
} else {
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
267
273
|
}
|
|
268
274
|
}
|
|
269
275
|
}
|
|
@@ -554,15 +560,12 @@ Installing to ${targetPlatforms.length} platform(s)...
|
|
|
554
560
|
// src/commands/verify.ts
|
|
555
561
|
import fs3 from "fs-extra";
|
|
556
562
|
import path3 from "path";
|
|
557
|
-
import { fileURLToPath } from "url";
|
|
558
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
559
|
-
var __dirname = path3.dirname(__filename);
|
|
560
563
|
async function verifySkill(skillName) {
|
|
561
564
|
try {
|
|
562
565
|
console.log(`
|
|
563
566
|
\u{1F50D} Verifying skill: ${skillName}
|
|
564
567
|
`);
|
|
565
|
-
const skillDir = path3.join(
|
|
568
|
+
const skillDir = path3.join(getSkillsDir(), skillName);
|
|
566
569
|
if (!await fs3.pathExists(skillDir)) {
|
|
567
570
|
console.error(`\u274C Skill "${skillName}" not found locally.`);
|
|
568
571
|
console.log(` Try: skm install ${skillName}`);
|
|
@@ -609,7 +612,7 @@ async function verifySkill(skillName) {
|
|
|
609
612
|
} else {
|
|
610
613
|
console.log(`\u26A0\uFE0F package.json not found (optional for basic skills)`);
|
|
611
614
|
}
|
|
612
|
-
const registryPath =
|
|
615
|
+
const registryPath = getRegistryPath();
|
|
613
616
|
if (await fs3.pathExists(registryPath)) {
|
|
614
617
|
try {
|
|
615
618
|
const registry = await fs3.readJson(registryPath);
|
|
@@ -646,64 +649,13 @@ async function verifySkill(skillName) {
|
|
|
646
649
|
}
|
|
647
650
|
|
|
648
651
|
// src/cli.ts
|
|
649
|
-
var
|
|
650
|
-
var
|
|
651
|
-
var packageJson = JSON.parse(readFileSync(resolve(
|
|
652
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
653
|
+
var __dirname = dirname(__filename);
|
|
654
|
+
var packageJson = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
|
|
652
655
|
var VERSION = packageJson.version || "1.3.1";
|
|
653
656
|
var program = new Command();
|
|
654
657
|
program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version(VERSION);
|
|
655
|
-
program.
|
|
656
|
-
if (thisCommand.opts().help) {
|
|
657
|
-
console.log(`
|
|
658
|
-
SkillMarket CLI
|
|
659
|
-
|
|
660
|
-
Usage: skm <command> [options]
|
|
661
|
-
|
|
662
|
-
Commands:
|
|
663
|
-
ls [options] List available skills
|
|
664
|
-
--installed Show only installed skills
|
|
665
|
-
--updates Check for updates
|
|
666
|
-
--page <n> Page number (default: 1)
|
|
667
|
-
--limit <n> Items per page (default: 20)
|
|
668
|
-
-s, --search Search by keyword
|
|
669
|
-
search <keyword> Search skills from npm registry
|
|
670
|
-
-l, --limit Max results (default: 20)
|
|
671
|
-
info <skill> Display skill information
|
|
672
|
-
install <skill> Install a skill from npm or GitHub
|
|
673
|
-
@<version> Install specific version
|
|
674
|
-
--platform Target platforms (opencode,claude,vscode,codex,...)
|
|
675
|
-
--force Overwrite if already installed
|
|
676
|
-
-b, --branch GitHub branch to install from
|
|
677
|
-
-c, --commit GitHub commit to install from
|
|
678
|
-
uninstall <skill> Remove an installed skill
|
|
679
|
-
--platform Target platforms
|
|
680
|
-
--all Uninstall ALL installed skills
|
|
681
|
-
--dry-run Preview without deleting
|
|
682
|
-
-y, --yes Skip confirmation
|
|
683
|
-
update [skill] Update installed skills (all if no skill specified)
|
|
684
|
-
--all Update all skills
|
|
685
|
-
publish <skill> Publish a skill to npm
|
|
686
|
-
-v, --version Specify version
|
|
687
|
-
verify <skill> Verify skill integrity and format
|
|
688
|
-
sync [skill] Synchronize platform links (or sync a skill to latest)
|
|
689
|
-
platforms Show available platforms
|
|
690
|
-
gui [port] Start SkillMarket GUI web interface
|
|
691
|
-
config View and manage configuration
|
|
692
|
-
config [ls] List all configuration values
|
|
693
|
-
config get <key> Get a config value
|
|
694
|
-
config set <key> <val> Set a config value
|
|
695
|
-
config reset [key] Reset config to defaults
|
|
696
|
-
admin Admin: manage published skills
|
|
697
|
-
admin ls List all published skills
|
|
698
|
-
admin info <skill> Show detailed info for a published skill
|
|
699
|
-
admin search <keyword> Search published skills
|
|
700
|
-
admin stats Publishing statistics
|
|
701
|
-
admin verify <skill> Verify a published skill
|
|
702
|
-
admin deprecate <skill> Deprecate a skill (--version, --message)
|
|
703
|
-
admin unpublish <skill> Unpublish a skill (--version, --force)
|
|
704
|
-
admin tag set/tag rm/tag ls Manage dist-tags
|
|
705
|
-
admin owner add/rm Manage package maintainers
|
|
706
|
-
admin access <skill> Set package access (public|restricted)
|
|
658
|
+
program.addHelpText("afterAll", `
|
|
707
659
|
|
|
708
660
|
Examples:
|
|
709
661
|
skm ls List available skills
|
|
@@ -738,11 +690,7 @@ Examples:
|
|
|
738
690
|
skm config reset --all Reset all config
|
|
739
691
|
skm admin ls List all published skills
|
|
740
692
|
skm admin stats Publishing statistics
|
|
741
|
-
skm admin deprecate my-skill --message "Use v2" Deprecate a skill
|
|
742
|
-
`);
|
|
743
|
-
process.exit(0);
|
|
744
|
-
}
|
|
745
|
-
});
|
|
693
|
+
skm admin deprecate my-skill --message "Use v2" Deprecate a skill`);
|
|
746
694
|
var lsCmd = program.command("ls").description("List available skills");
|
|
747
695
|
lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").option("-p, --page <number>", "Page number (default: 1)", parseInt).option("-l, --limit <number>", "Items per page (default: 20)", parseInt).option("-s, --search <keyword>", "Search by keyword (id, displayName, description)").action((opts) => {
|
|
748
696
|
const options = {
|
|
@@ -764,118 +712,82 @@ infoCmd.argument("<skill-id>", "Skill ID to show info").action((skillId) => {
|
|
|
764
712
|
});
|
|
765
713
|
var installCmd = program.command("install").description("Install a skill from npm or GitHub");
|
|
766
714
|
installCmd.argument("<skill>", "Skill ID, npm package, or GitHub URL (owner/repo, https://github.com/owner/repo)").option("-p, --platform <platforms>", "Target platforms (comma-separated: opencode,claude,vscode)").option("-f, --force", "Overwrite if already installed").option("-v, --version <version>", "Specific version to install (npm only)").option("-b, --branch <branch>", "GitHub branch to install from").option("-c, --commit <commit>", "GitHub commit hash to install from").action(async (skill, opts) => {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
} catch (err) {
|
|
784
|
-
console.error("Installation failed:", err);
|
|
785
|
-
process.exit(1);
|
|
715
|
+
const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
|
|
716
|
+
const githubSource = parseGitHubUrl(skill);
|
|
717
|
+
if (githubSource) {
|
|
718
|
+
await installFromGitHub(skill, {
|
|
719
|
+
platforms,
|
|
720
|
+
force: opts.force,
|
|
721
|
+
branch: opts.branch,
|
|
722
|
+
commit: opts.commit
|
|
723
|
+
});
|
|
724
|
+
} else {
|
|
725
|
+
await installSkill(skill, opts.version, {
|
|
726
|
+
platforms,
|
|
727
|
+
force: opts.force
|
|
728
|
+
});
|
|
786
729
|
}
|
|
787
730
|
});
|
|
788
731
|
var uninstallCmd = program.command("uninstall").description("Remove an installed skill from local and platform directories");
|
|
789
732
|
uninstallCmd.argument("[skill]", "Skill ID to uninstall (required unless using --all)").option("-p, --platform <platforms>", "Target platforms (comma-separated)").option("-a, --all", "Uninstall ALL installed skills (requires confirmation)").option("-d, --dry-run", "Preview what would be uninstalled without actually deleting").option("-y, --yes", "Skip confirmation prompts").action(async (skill, opts) => {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
await uninstallAll({
|
|
794
|
-
platforms,
|
|
795
|
-
dryRun: opts.dryRun,
|
|
796
|
-
yes: opts.yes
|
|
797
|
-
});
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
if (!skill) {
|
|
801
|
-
console.error("Error: Skill ID is required (or use --all to uninstall all)");
|
|
802
|
-
process.exit(1);
|
|
803
|
-
}
|
|
804
|
-
await uninstallSkill(skill, {
|
|
733
|
+
const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
|
|
734
|
+
if (opts.all) {
|
|
735
|
+
await uninstallAll({
|
|
805
736
|
platforms,
|
|
806
737
|
dryRun: opts.dryRun,
|
|
807
738
|
yes: opts.yes
|
|
808
739
|
});
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if (!skill) {
|
|
743
|
+
throw new Error("Skill ID is required (or use --all to uninstall all)");
|
|
812
744
|
}
|
|
745
|
+
await uninstallSkill(skill, {
|
|
746
|
+
platforms,
|
|
747
|
+
dryRun: opts.dryRun,
|
|
748
|
+
yes: opts.yes
|
|
749
|
+
});
|
|
813
750
|
});
|
|
814
751
|
var updateCmd = program.command("update").description("Update installed skills");
|
|
815
752
|
updateCmd.argument("[skill]", "Skill ID to update (optional, updates all if not specified)").option("--all", "Update all skills").action(async (skill, opts) => {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
await updateSkill(skill);
|
|
821
|
-
}
|
|
822
|
-
} catch (err) {
|
|
823
|
-
console.error("Update failed:", err);
|
|
824
|
-
process.exit(1);
|
|
753
|
+
if (opts.all || !skill) {
|
|
754
|
+
await updateSkill();
|
|
755
|
+
} else {
|
|
756
|
+
await updateSkill(skill);
|
|
825
757
|
}
|
|
826
758
|
});
|
|
827
759
|
program.command("sync [skill]").description("Synchronize platform links or sync skill to latest version").action(async (skill) => {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
await syncPlatformLinks();
|
|
833
|
-
}
|
|
834
|
-
} catch (err) {
|
|
835
|
-
console.error("Sync failed:", err);
|
|
836
|
-
process.exit(1);
|
|
760
|
+
if (skill) {
|
|
761
|
+
await syncSkill(skill);
|
|
762
|
+
} else {
|
|
763
|
+
await syncPlatformLinks();
|
|
837
764
|
}
|
|
838
765
|
});
|
|
839
766
|
var platformsCmd = program.command("platforms").description("Show available platforms");
|
|
840
767
|
platformsCmd.action(async () => {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
}
|
|
851
|
-
console.log(`${adapter.name.padEnd(15)} \u274C Not detected`);
|
|
852
|
-
}
|
|
768
|
+
const available = await detectPlatforms();
|
|
769
|
+
const allAdapters = getAllAdapters();
|
|
770
|
+
console.log("\n\u{1F4CD} Available Platforms:\n");
|
|
771
|
+
for (const adapter of allAdapters) {
|
|
772
|
+
const isAvailable = available.find((a) => a.id === adapter.id);
|
|
773
|
+
const installed = await adapter.listInstalled();
|
|
774
|
+
if (isAvailable) {
|
|
775
|
+
console.log(`${adapter.name.padEnd(15)} \u2705 Available (${installed.length} skills installed)`);
|
|
776
|
+
} else {
|
|
777
|
+
console.log(`${adapter.name.padEnd(15)} \u274C Not detected`);
|
|
853
778
|
}
|
|
854
|
-
console.log("");
|
|
855
|
-
} catch (err) {
|
|
856
|
-
console.error("Failed to list platforms:", err);
|
|
857
|
-
process.exit(1);
|
|
858
779
|
}
|
|
780
|
+
console.log("");
|
|
859
781
|
});
|
|
860
782
|
program.command("gui [port]").description("Start SkillMarket GUI (web interface)").action(async (port) => {
|
|
861
783
|
const portNum = port ? parseInt(port) : 18770;
|
|
862
784
|
startGuiServer(portNum);
|
|
863
785
|
});
|
|
864
786
|
program.command("publish <skill>").description("Publish a skill to npm").option("-v, --version <version>", "Specify version (optional, auto-increment patch if not specified)").action(async (skill, options) => {
|
|
865
|
-
|
|
866
|
-
await publishSkill(skill, options.version ? { version: options.version } : void 0);
|
|
867
|
-
} catch (err) {
|
|
868
|
-
console.error("Publish failed:", err);
|
|
869
|
-
process.exit(1);
|
|
870
|
-
}
|
|
787
|
+
await publishSkill(skill, options.version ? { version: options.version } : void 0);
|
|
871
788
|
});
|
|
872
789
|
program.command("verify <skill>").description("Verify skill integrity and format").action(async (skill) => {
|
|
873
|
-
|
|
874
|
-
await verifySkill(skill);
|
|
875
|
-
} catch (err) {
|
|
876
|
-
console.error("Verify failed:", err);
|
|
877
|
-
process.exit(1);
|
|
878
|
-
}
|
|
790
|
+
await verifySkill(skill);
|
|
879
791
|
});
|
|
880
792
|
var config = program.command("config").description("View and manage configuration");
|
|
881
793
|
config.command("list").alias("ls").description("List all configuration values").action(async () => {
|
|
@@ -895,121 +807,56 @@ config.command("reset [key]").description("Reset configuration to default values
|
|
|
895
807
|
});
|
|
896
808
|
var admin = program.command("admin").description("Admin: manage published skills (cloud)");
|
|
897
809
|
admin.command("ls").description("List all published skills").action(async () => {
|
|
898
|
-
|
|
899
|
-
await adminList();
|
|
900
|
-
} catch (err) {
|
|
901
|
-
console.error("Admin ls failed:", err);
|
|
902
|
-
process.exit(1);
|
|
903
|
-
}
|
|
810
|
+
await adminList();
|
|
904
811
|
});
|
|
905
812
|
admin.command("info <skill>").description("Show detailed info for a published skill").action(async (skill) => {
|
|
906
|
-
|
|
907
|
-
await adminInfo(skill);
|
|
908
|
-
} catch (err) {
|
|
909
|
-
console.error("Admin info failed:", err);
|
|
910
|
-
process.exit(1);
|
|
911
|
-
}
|
|
813
|
+
await adminInfo(skill);
|
|
912
814
|
});
|
|
913
815
|
admin.command("search <keyword>").description("Search across published skills").option("-l, --limit <number>", "Max results (default: 20)", parseInt).action(async (keyword, opts) => {
|
|
914
|
-
|
|
915
|
-
await adminSearch(keyword, opts.limit ?? 20);
|
|
916
|
-
} catch (err) {
|
|
917
|
-
console.error("Admin search failed:", err);
|
|
918
|
-
process.exit(1);
|
|
919
|
-
}
|
|
816
|
+
await adminSearch(keyword, opts.limit ?? 20);
|
|
920
817
|
});
|
|
921
818
|
admin.command("stats").description("Show publishing statistics").action(async () => {
|
|
922
|
-
|
|
923
|
-
await adminStats();
|
|
924
|
-
} catch (err) {
|
|
925
|
-
console.error("Admin stats failed:", err);
|
|
926
|
-
process.exit(1);
|
|
927
|
-
}
|
|
819
|
+
await adminStats();
|
|
928
820
|
});
|
|
929
821
|
admin.command("verify <skill>").description("Verify a published skill structure and metadata").action(async (skill) => {
|
|
930
|
-
|
|
931
|
-
await adminVerify(skill);
|
|
932
|
-
} catch (err) {
|
|
933
|
-
console.error("Admin verify failed:", err);
|
|
934
|
-
process.exit(1);
|
|
935
|
-
}
|
|
822
|
+
await adminVerify(skill);
|
|
936
823
|
});
|
|
937
824
|
admin.command("deprecate <skill>").description("Deprecate a published skill (or specific version)").option("-v, --version <version>", "Deprecate a specific version only").option("-m, --message <message>", "Deprecation message").action(async (skill, opts) => {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
});
|
|
943
|
-
} catch (err) {
|
|
944
|
-
console.error("Admin deprecate failed:", err);
|
|
945
|
-
process.exit(1);
|
|
946
|
-
}
|
|
825
|
+
await adminDeprecate(skill, {
|
|
826
|
+
version: opts.version,
|
|
827
|
+
message: opts.message
|
|
828
|
+
});
|
|
947
829
|
});
|
|
948
830
|
admin.command("unpublish <skill>").description("Unpublish a skill (or specific version) from npm").option("-v, --version <version>", "Unpublish a specific version only").option("-f, --force", "Force unpublish entire package").action(async (skill, opts) => {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
});
|
|
954
|
-
} catch (err) {
|
|
955
|
-
console.error("Admin unpublish failed:", err);
|
|
956
|
-
process.exit(1);
|
|
957
|
-
}
|
|
831
|
+
await adminUnpublish(skill, {
|
|
832
|
+
version: opts.version,
|
|
833
|
+
force: opts.force
|
|
834
|
+
});
|
|
958
835
|
});
|
|
959
836
|
var adminTag = admin.command("tag").description("Manage dist-tags for a skill");
|
|
960
837
|
adminTag.command("set <skill> <tag> <version>").description("Set a dist-tag for a specific version").action(async (skill, tag, version) => {
|
|
961
|
-
|
|
962
|
-
await adminTagSet(skill, tag, version);
|
|
963
|
-
} catch (err) {
|
|
964
|
-
console.error("Admin tag set failed:", err);
|
|
965
|
-
process.exit(1);
|
|
966
|
-
}
|
|
838
|
+
await adminTagSet(skill, tag, version);
|
|
967
839
|
});
|
|
968
840
|
adminTag.command("rm <skill> <tag>").description("Remove a dist-tag").action(async (skill, tag) => {
|
|
969
|
-
|
|
970
|
-
await adminTagRemove(skill, tag);
|
|
971
|
-
} catch (err) {
|
|
972
|
-
console.error("Admin tag rm failed:", err);
|
|
973
|
-
process.exit(1);
|
|
974
|
-
}
|
|
841
|
+
await adminTagRemove(skill, tag);
|
|
975
842
|
});
|
|
976
843
|
adminTag.command("ls <skill>").description("List all dist-tags for a skill").action(async (skill) => {
|
|
977
|
-
|
|
978
|
-
await adminTagList(skill);
|
|
979
|
-
} catch (err) {
|
|
980
|
-
console.error("Admin tag ls failed:", err);
|
|
981
|
-
process.exit(1);
|
|
982
|
-
}
|
|
844
|
+
await adminTagList(skill);
|
|
983
845
|
});
|
|
984
846
|
var adminOwner = admin.command("owner").description("Manage package owners/maintainers");
|
|
985
847
|
adminOwner.command("add <skill> <user>").description("Add an owner to a skill package").action(async (skill, user) => {
|
|
986
|
-
|
|
987
|
-
await adminOwnerAdd(skill, user);
|
|
988
|
-
} catch (err) {
|
|
989
|
-
console.error("Admin owner add failed:", err);
|
|
990
|
-
process.exit(1);
|
|
991
|
-
}
|
|
848
|
+
await adminOwnerAdd(skill, user);
|
|
992
849
|
});
|
|
993
850
|
adminOwner.command("rm <skill> <user>").description("Remove an owner from a skill package").action(async (skill, user) => {
|
|
994
|
-
|
|
995
|
-
await adminOwnerRemove(skill, user);
|
|
996
|
-
} catch (err) {
|
|
997
|
-
console.error("Admin owner rm failed:", err);
|
|
998
|
-
process.exit(1);
|
|
999
|
-
}
|
|
851
|
+
await adminOwnerRemove(skill, user);
|
|
1000
852
|
});
|
|
1001
853
|
admin.command("access <skill> <level>").description("Set package access (public|restricted)").action(async (skill, level) => {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
console.error('\u274C Access level must be "public" or "restricted"');
|
|
1005
|
-
process.exit(1);
|
|
1006
|
-
}
|
|
1007
|
-
await adminAccess(skill, level);
|
|
1008
|
-
} catch (err) {
|
|
1009
|
-
console.error("Admin access failed:", err);
|
|
1010
|
-
process.exit(1);
|
|
854
|
+
if (level !== "public" && level !== "restricted") {
|
|
855
|
+
throw new Error('Access level must be "public" or "restricted"');
|
|
1011
856
|
}
|
|
857
|
+
await adminAccess(skill, level);
|
|
1012
858
|
});
|
|
859
|
+
program.exitOverride();
|
|
1013
860
|
var hasArgs = process.argv.slice(2).length > 0;
|
|
1014
861
|
if (!hasArgs) {
|
|
1015
862
|
const port = 18770;
|
|
@@ -1021,5 +868,10 @@ if (!hasArgs) {
|
|
|
1021
868
|
}, 1500);
|
|
1022
869
|
startGuiServer(port);
|
|
1023
870
|
} else {
|
|
1024
|
-
|
|
871
|
+
try {
|
|
872
|
+
await program.parseAsync();
|
|
873
|
+
} catch (err) {
|
|
874
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
1025
877
|
}
|
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
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;
|