skillverse 0.1.5 → 0.1.8
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/bin.js +611 -159
- package/dist/bin.js.map +1 -1
- package/dist/index.js +599 -159
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/prisma/dev.db +0 -0
- package/public/assets/index-C5PzeRR6.css +1 -0
- package/public/assets/index-CHREpWKn.js +23 -0
- package/public/index.html +2 -2
- package/public/assets/index-DVybgO9W.js +0 -20
- package/public/assets/index-li8hN2px.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -785,6 +785,32 @@ var init_skillService = __esm({
|
|
|
785
785
|
});
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
|
+
/**
|
|
789
|
+
* Sync skills between filesystem and database:
|
|
790
|
+
* 1. Remove orphaned database records (skills in DB but not on disk)
|
|
791
|
+
* 2. Import new skills from disk (skills on disk but not in DB)
|
|
792
|
+
*/
|
|
793
|
+
async syncSkillsWithFileSystem() {
|
|
794
|
+
const { skills: skillsDir } = getPaths();
|
|
795
|
+
const dbSkills = await prisma.skill.findMany();
|
|
796
|
+
let removedCount = 0;
|
|
797
|
+
for (const skill of dbSkills) {
|
|
798
|
+
if (!existsSync3(skill.storagePath)) {
|
|
799
|
+
console.log(`\u{1F5D1}\uFE0F Removing orphaned skill from database: ${skill.name} (path: ${skill.storagePath})`);
|
|
800
|
+
try {
|
|
801
|
+
await prisma.skill.delete({ where: { id: skill.id } });
|
|
802
|
+
removedCount++;
|
|
803
|
+
} catch (error) {
|
|
804
|
+
console.error(`Failed to remove orphaned skill ${skill.name}:`, error);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (removedCount > 0) {
|
|
809
|
+
console.log(`\u2705 Removed ${removedCount} orphaned skill(s) from database.`);
|
|
810
|
+
}
|
|
811
|
+
const importedCount = await this.scanForSkills();
|
|
812
|
+
return { removedCount, importedCount: importedCount || 0 };
|
|
813
|
+
}
|
|
788
814
|
async scanForSkills() {
|
|
789
815
|
const { skills: skillsDir } = getPaths();
|
|
790
816
|
console.log(`Scanning for skills in ${skillsDir}...`);
|
|
@@ -1151,7 +1177,7 @@ ${relativeSkillsPath}/
|
|
|
1151
1177
|
if (!existsSync4(skillsPath)) {
|
|
1152
1178
|
return [];
|
|
1153
1179
|
}
|
|
1154
|
-
const { readdir: readdir2, stat: stat2, readFile:
|
|
1180
|
+
const { readdir: readdir2, stat: stat2, readFile: readFile4 } = await import("fs/promises");
|
|
1155
1181
|
const items = await readdir2(skillsPath);
|
|
1156
1182
|
const existingSkills = [];
|
|
1157
1183
|
for (const item of items) {
|
|
@@ -1192,7 +1218,7 @@ ${relativeSkillsPath}/
|
|
|
1192
1218
|
if (!existsSync4(SKILLS_DIR)) {
|
|
1193
1219
|
await mkdir2(SKILLS_DIR, { recursive: true });
|
|
1194
1220
|
}
|
|
1195
|
-
const { rename, readFile:
|
|
1221
|
+
const { rename, readFile: readFile4, cp: cp2 } = await import("fs/promises");
|
|
1196
1222
|
const migrated = [];
|
|
1197
1223
|
const errors = [];
|
|
1198
1224
|
for (const skillName of skillNames) {
|
|
@@ -1225,7 +1251,7 @@ ${relativeSkillsPath}/
|
|
|
1225
1251
|
if (existsSync4(skillMdPath)) {
|
|
1226
1252
|
try {
|
|
1227
1253
|
const matter2 = await import("gray-matter");
|
|
1228
|
-
const fileContent = await
|
|
1254
|
+
const fileContent = await readFile4(skillMdPath, "utf-8");
|
|
1229
1255
|
const parsed = matter2.default(fileContent);
|
|
1230
1256
|
description = parsed.data.description || "";
|
|
1231
1257
|
metadata = parsed.data;
|
|
@@ -1320,6 +1346,18 @@ var init_skills = __esm({
|
|
|
1320
1346
|
next(error);
|
|
1321
1347
|
}
|
|
1322
1348
|
});
|
|
1349
|
+
router.post("/sync", async (req, res, next) => {
|
|
1350
|
+
try {
|
|
1351
|
+
const result = await skillService.syncSkillsWithFileSystem();
|
|
1352
|
+
res.json({
|
|
1353
|
+
success: true,
|
|
1354
|
+
data: result,
|
|
1355
|
+
message: `Synced: removed ${result.removedCount} orphan(s), imported ${result.importedCount} new skill(s)`
|
|
1356
|
+
});
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
next(error);
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1323
1361
|
router.get("/:id", async (req, res, next) => {
|
|
1324
1362
|
try {
|
|
1325
1363
|
const skill = await skillService.getSkillById(req.params.id);
|
|
@@ -1496,7 +1534,7 @@ var init_skills = __esm({
|
|
|
1496
1534
|
});
|
|
1497
1535
|
router.get("/:id/skill-md", async (req, res, next) => {
|
|
1498
1536
|
try {
|
|
1499
|
-
const { readFile:
|
|
1537
|
+
const { readFile: readFile4 } = await import("fs/promises");
|
|
1500
1538
|
const skill = await skillService.getSkillById(req.params.id);
|
|
1501
1539
|
const skillMdPath = join5(skill.storagePath, "SKILL.md");
|
|
1502
1540
|
if (!existsSync5(skillMdPath)) {
|
|
@@ -1508,7 +1546,7 @@ var init_skills = __esm({
|
|
|
1508
1546
|
}
|
|
1509
1547
|
});
|
|
1510
1548
|
}
|
|
1511
|
-
const content = await
|
|
1549
|
+
const content = await readFile4(skillMdPath, "utf-8");
|
|
1512
1550
|
res.json({
|
|
1513
1551
|
success: true,
|
|
1514
1552
|
data: {
|
|
@@ -1766,52 +1804,300 @@ var init_bundleService = __esm({
|
|
|
1766
1804
|
}
|
|
1767
1805
|
});
|
|
1768
1806
|
|
|
1807
|
+
// src/services/githubMarketplaceRegistry.ts
|
|
1808
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1809
|
+
import { createHash } from "crypto";
|
|
1810
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1811
|
+
function sha256Hex(buf) {
|
|
1812
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
1813
|
+
}
|
|
1814
|
+
function nowIso() {
|
|
1815
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1816
|
+
}
|
|
1817
|
+
function normalizeIndex(index) {
|
|
1818
|
+
const base = {
|
|
1819
|
+
version: typeof index?.version === "number" ? index.version : 1,
|
|
1820
|
+
updatedAt: typeof index?.updatedAt === "string" ? index.updatedAt : nowIso(),
|
|
1821
|
+
skills: Array.isArray(index?.skills) ? index.skills : []
|
|
1822
|
+
};
|
|
1823
|
+
return base;
|
|
1824
|
+
}
|
|
1825
|
+
async function requireOctokit() {
|
|
1826
|
+
const mod = await import("octokit");
|
|
1827
|
+
return mod.Octokit;
|
|
1828
|
+
}
|
|
1829
|
+
var GitHubMarketplaceRegistry;
|
|
1830
|
+
var init_githubMarketplaceRegistry = __esm({
|
|
1831
|
+
"src/services/githubMarketplaceRegistry.ts"() {
|
|
1832
|
+
"use strict";
|
|
1833
|
+
GitHubMarketplaceRegistry = class {
|
|
1834
|
+
constructor(cfg) {
|
|
1835
|
+
this.cfg = cfg;
|
|
1836
|
+
}
|
|
1837
|
+
getIndexPath() {
|
|
1838
|
+
return this.cfg.indexPath || "index.json";
|
|
1839
|
+
}
|
|
1840
|
+
async octokit() {
|
|
1841
|
+
const Octokit = await requireOctokit();
|
|
1842
|
+
return new Octokit({ auth: this.cfg.token });
|
|
1843
|
+
}
|
|
1844
|
+
async fetchIndexFromPages() {
|
|
1845
|
+
const resp = await fetch(this.cfg.indexUrl, { method: "GET" });
|
|
1846
|
+
if (!resp.ok) {
|
|
1847
|
+
throw new Error(`Failed to fetch marketplace index from Pages: ${resp.status} ${resp.statusText}`);
|
|
1848
|
+
}
|
|
1849
|
+
const json = await resp.json();
|
|
1850
|
+
return normalizeIndex(json);
|
|
1851
|
+
}
|
|
1852
|
+
async readIndexFromRepo() {
|
|
1853
|
+
const octokit = await this.octokit();
|
|
1854
|
+
const path = this.getIndexPath();
|
|
1855
|
+
const res = await octokit.rest.repos.getContent({
|
|
1856
|
+
owner: this.cfg.owner,
|
|
1857
|
+
repo: this.cfg.repo,
|
|
1858
|
+
path,
|
|
1859
|
+
ref: this.cfg.branch
|
|
1860
|
+
});
|
|
1861
|
+
if (Array.isArray(res.data) || res.data.type !== "file") {
|
|
1862
|
+
throw new Error(`Invalid index path in repo: ${path}`);
|
|
1863
|
+
}
|
|
1864
|
+
const content = Buffer2.from(res.data.content, res.data.encoding).toString("utf-8");
|
|
1865
|
+
const parsed = JSON.parse(content);
|
|
1866
|
+
return { index: normalizeIndex(parsed), sha: res.data.sha };
|
|
1867
|
+
}
|
|
1868
|
+
async upsertIndexToRepo(index, previousSha) {
|
|
1869
|
+
const octokit = await this.octokit();
|
|
1870
|
+
const path = this.getIndexPath();
|
|
1871
|
+
const body = JSON.stringify(
|
|
1872
|
+
{
|
|
1873
|
+
...index,
|
|
1874
|
+
updatedAt: nowIso()
|
|
1875
|
+
},
|
|
1876
|
+
null,
|
|
1877
|
+
2
|
|
1878
|
+
);
|
|
1879
|
+
const contentBase64 = Buffer2.from(body, "utf-8").toString("base64");
|
|
1880
|
+
await octokit.rest.repos.createOrUpdateFileContents({
|
|
1881
|
+
owner: this.cfg.owner,
|
|
1882
|
+
repo: this.cfg.repo,
|
|
1883
|
+
branch: this.cfg.branch,
|
|
1884
|
+
path,
|
|
1885
|
+
message: `chore(marketplace): update ${path}`,
|
|
1886
|
+
content: contentBase64,
|
|
1887
|
+
...previousSha ? { sha: previousSha } : {}
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
async createReleaseWithAsset(params) {
|
|
1891
|
+
const octokit = await this.octokit();
|
|
1892
|
+
const bundleBuf = await readFile3(params.bundlePath);
|
|
1893
|
+
const sha256 = sha256Hex(bundleBuf);
|
|
1894
|
+
const bundleSize = bundleBuf.byteLength;
|
|
1895
|
+
const safeName = params.skillName.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
1896
|
+
const safeVer = params.skillVersion.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
1897
|
+
const tag = `skillverse-marketplace/${safeName}/v${safeVer}/${Date.now()}`;
|
|
1898
|
+
const release = await octokit.rest.repos.createRelease({
|
|
1899
|
+
owner: this.cfg.owner,
|
|
1900
|
+
repo: this.cfg.repo,
|
|
1901
|
+
tag_name: tag,
|
|
1902
|
+
name: `SkillVerse Marketplace: ${params.skillName}@${params.skillVersion}`,
|
|
1903
|
+
body: params.description || "",
|
|
1904
|
+
draft: false,
|
|
1905
|
+
prerelease: false,
|
|
1906
|
+
target_commitish: this.cfg.branch
|
|
1907
|
+
});
|
|
1908
|
+
const assetName = `${safeName}-${safeVer}.tar.gz`;
|
|
1909
|
+
const uploadRes = await octokit.rest.repos.uploadReleaseAsset({
|
|
1910
|
+
owner: this.cfg.owner,
|
|
1911
|
+
repo: this.cfg.repo,
|
|
1912
|
+
release_id: release.data.id,
|
|
1913
|
+
name: assetName,
|
|
1914
|
+
data: bundleBuf,
|
|
1915
|
+
headers: {
|
|
1916
|
+
"content-type": "application/gzip",
|
|
1917
|
+
"content-length": bundleSize
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
return {
|
|
1921
|
+
bundleUrl: uploadRes.data.browser_download_url,
|
|
1922
|
+
bundleSize,
|
|
1923
|
+
sha256,
|
|
1924
|
+
tag
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
upsertSkillEntry(index, entry) {
|
|
1928
|
+
const createdAt = nowIso();
|
|
1929
|
+
const existingIdx = index.skills.findIndex((s) => s?.name === entry.name && s?.type === entry.type);
|
|
1930
|
+
if (existingIdx >= 0) {
|
|
1931
|
+
const prev = index.skills[existingIdx];
|
|
1932
|
+
index.skills[existingIdx] = {
|
|
1933
|
+
...prev,
|
|
1934
|
+
...entry,
|
|
1935
|
+
updatedAt: nowIso(),
|
|
1936
|
+
createdAt: prev.createdAt || createdAt
|
|
1937
|
+
};
|
|
1938
|
+
} else {
|
|
1939
|
+
index.skills.push({
|
|
1940
|
+
...entry,
|
|
1941
|
+
createdAt,
|
|
1942
|
+
updatedAt: nowIso()
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
return index;
|
|
1946
|
+
}
|
|
1947
|
+
static stableIdForEntry(entry) {
|
|
1948
|
+
const key = entry.type === "git" ? `git:${entry.name}:${entry.gitUrl}` : `bundle:${entry.name}:${entry.skillVersion || ""}:${entry.bundleUrl}`;
|
|
1949
|
+
return createHash("sha256").update(key).digest("hex").slice(0, 24);
|
|
1950
|
+
}
|
|
1951
|
+
async deleteReleaseByTag(tag) {
|
|
1952
|
+
const octokit = await this.octokit();
|
|
1953
|
+
let releaseId;
|
|
1954
|
+
let page = 1;
|
|
1955
|
+
const perPage = 30;
|
|
1956
|
+
while (!releaseId) {
|
|
1957
|
+
const { data: releases } = await octokit.rest.repos.listReleases({
|
|
1958
|
+
owner: this.cfg.owner,
|
|
1959
|
+
repo: this.cfg.repo,
|
|
1960
|
+
page,
|
|
1961
|
+
per_page: perPage
|
|
1962
|
+
});
|
|
1963
|
+
if (releases.length === 0) break;
|
|
1964
|
+
const found = releases.find((r) => r.tag_name === tag);
|
|
1965
|
+
if (found) {
|
|
1966
|
+
releaseId = found.id;
|
|
1967
|
+
break;
|
|
1968
|
+
}
|
|
1969
|
+
page++;
|
|
1970
|
+
}
|
|
1971
|
+
if (!releaseId) {
|
|
1972
|
+
console.warn(`Release with tag "${tag}" not found, skipping deletion.`);
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
await octokit.rest.repos.deleteRelease({
|
|
1976
|
+
owner: this.cfg.owner,
|
|
1977
|
+
repo: this.cfg.repo,
|
|
1978
|
+
release_id: releaseId
|
|
1979
|
+
});
|
|
1980
|
+
try {
|
|
1981
|
+
await octokit.rest.git.deleteRef({
|
|
1982
|
+
owner: this.cfg.owner,
|
|
1983
|
+
repo: this.cfg.repo,
|
|
1984
|
+
ref: `tags/${tag}`
|
|
1985
|
+
});
|
|
1986
|
+
} catch (e) {
|
|
1987
|
+
console.warn(`Failed to delete git tag ref "tags/${tag}":`, e.message);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
// src/marketplaceConfig.ts
|
|
1995
|
+
var marketplaceConfig;
|
|
1996
|
+
var init_marketplaceConfig = __esm({
|
|
1997
|
+
"src/marketplaceConfig.ts"() {
|
|
1998
|
+
"use strict";
|
|
1999
|
+
marketplaceConfig = {
|
|
2000
|
+
indexUrl: "https://fdksd.github.io/skillverse-marketplace/index.json",
|
|
2001
|
+
owner: "fdksd",
|
|
2002
|
+
repo: "skillverse-marketplace",
|
|
2003
|
+
branch: "main",
|
|
2004
|
+
token: "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"
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
|
|
1769
2009
|
// src/routes/marketplace.ts
|
|
1770
2010
|
import { Router as Router3 } from "express";
|
|
1771
2011
|
import { existsSync as existsSync7 } from "fs";
|
|
2012
|
+
import { join as join7 } from "path";
|
|
2013
|
+
import { mkdir as mkdir5, rm as rm5, writeFile } from "fs/promises";
|
|
2014
|
+
function requireMarketplaceGitHubConfig() {
|
|
2015
|
+
const { indexUrl, owner, repo, branch, token } = marketplaceConfig;
|
|
2016
|
+
if (!indexUrl) {
|
|
2017
|
+
throw new AppError(
|
|
2018
|
+
ErrorCode.VALIDATION_ERROR,
|
|
2019
|
+
"Marketplace indexUrl is not configured.",
|
|
2020
|
+
400
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
return {
|
|
2024
|
+
indexUrl,
|
|
2025
|
+
owner,
|
|
2026
|
+
repo,
|
|
2027
|
+
branch,
|
|
2028
|
+
token,
|
|
2029
|
+
indexPath: "index.json"
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function mapIndexToMarketplaceItems(index, search) {
|
|
2033
|
+
const query = (search || "").trim().toLowerCase();
|
|
2034
|
+
const skills = index.skills || [];
|
|
2035
|
+
const filtered = query ? skills.filter((s) => {
|
|
2036
|
+
const name = String(s?.name || "").toLowerCase();
|
|
2037
|
+
const desc = String(s?.description || "").toLowerCase();
|
|
2038
|
+
return name.includes(query) || desc.includes(query);
|
|
2039
|
+
}) : skills;
|
|
2040
|
+
return filtered.map((entry) => {
|
|
2041
|
+
const id = GitHubMarketplaceRegistry.stableIdForEntry(entry);
|
|
2042
|
+
const publishDate = entry.createdAt || entry.updatedAt || index.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
2043
|
+
return {
|
|
2044
|
+
id,
|
|
2045
|
+
skillId: id,
|
|
2046
|
+
publisherId: void 0,
|
|
2047
|
+
publisherName: entry.publisherName || "Anonymous",
|
|
2048
|
+
publishDate: new Date(publishDate),
|
|
2049
|
+
downloads: entry.downloads || 0,
|
|
2050
|
+
skill: {
|
|
2051
|
+
id,
|
|
2052
|
+
name: entry.name,
|
|
2053
|
+
source: entry.type === "git" ? "git" : "local",
|
|
2054
|
+
sourceUrl: entry.type === "git" ? entry.gitUrl : void 0,
|
|
2055
|
+
description: entry.description || "",
|
|
2056
|
+
commitHash: void 0,
|
|
2057
|
+
repoUrl: entry.type === "git" ? entry.gitUrl : void 0,
|
|
2058
|
+
updateAvailable: false,
|
|
2059
|
+
lastUpdateCheck: void 0,
|
|
2060
|
+
installDate: new Date(publishDate),
|
|
2061
|
+
metadata: {
|
|
2062
|
+
marketplace: entry
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
async function fetchIndex() {
|
|
2069
|
+
const ghCfg = requireMarketplaceGitHubConfig();
|
|
2070
|
+
const registry = new GitHubMarketplaceRegistry(ghCfg);
|
|
2071
|
+
return await registry.fetchIndexFromPages();
|
|
2072
|
+
}
|
|
1772
2073
|
var router3, marketplace_default;
|
|
1773
2074
|
var init_marketplace = __esm({
|
|
1774
2075
|
"src/routes/marketplace.ts"() {
|
|
1775
2076
|
"use strict";
|
|
1776
|
-
init_db();
|
|
1777
2077
|
init_skillService();
|
|
1778
2078
|
init_bundleService();
|
|
1779
2079
|
init_errorHandler();
|
|
1780
2080
|
init_dist();
|
|
2081
|
+
init_db();
|
|
2082
|
+
init_githubMarketplaceRegistry();
|
|
2083
|
+
init_marketplaceConfig();
|
|
1781
2084
|
router3 = Router3();
|
|
1782
2085
|
router3.get("/skills", async (req, res, next) => {
|
|
1783
2086
|
try {
|
|
1784
2087
|
const { search, page = "1", pageSize = "20" } = req.query;
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
]
|
|
1792
|
-
};
|
|
1793
|
-
}
|
|
1794
|
-
const [items, total] = await Promise.all([
|
|
1795
|
-
prisma.marketplaceSkill.findMany({
|
|
1796
|
-
where,
|
|
1797
|
-
include: {
|
|
1798
|
-
skill: true
|
|
1799
|
-
},
|
|
1800
|
-
orderBy: {
|
|
1801
|
-
downloads: "desc"
|
|
1802
|
-
},
|
|
1803
|
-
skip: (parseInt(page) - 1) * parseInt(pageSize),
|
|
1804
|
-
take: parseInt(pageSize)
|
|
1805
|
-
}),
|
|
1806
|
-
prisma.marketplaceSkill.count({ where })
|
|
1807
|
-
]);
|
|
2088
|
+
const index = await fetchIndex();
|
|
2089
|
+
const allItems = mapIndexToMarketplaceItems(index, search);
|
|
2090
|
+
const p = parseInt(page);
|
|
2091
|
+
const ps = parseInt(pageSize);
|
|
2092
|
+
const total = allItems.length;
|
|
2093
|
+
const items = allItems.slice((p - 1) * ps, (p - 1) * ps + ps);
|
|
1808
2094
|
res.json({
|
|
1809
2095
|
success: true,
|
|
1810
2096
|
data: {
|
|
1811
2097
|
items,
|
|
1812
2098
|
total,
|
|
1813
|
-
page:
|
|
1814
|
-
pageSize:
|
|
2099
|
+
page: p,
|
|
2100
|
+
pageSize: ps
|
|
1815
2101
|
}
|
|
1816
2102
|
});
|
|
1817
2103
|
} catch (error) {
|
|
@@ -1820,12 +2106,9 @@ var init_marketplace = __esm({
|
|
|
1820
2106
|
});
|
|
1821
2107
|
router3.get("/skills/:id", async (req, res, next) => {
|
|
1822
2108
|
try {
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
skill: true
|
|
1827
|
-
}
|
|
1828
|
-
});
|
|
2109
|
+
const index = await fetchIndex();
|
|
2110
|
+
const items = mapIndexToMarketplaceItems(index);
|
|
2111
|
+
const marketplaceSkill = items.find((i) => i.id === req.params.id);
|
|
1829
2112
|
if (!marketplaceSkill) {
|
|
1830
2113
|
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1831
2114
|
}
|
|
@@ -1839,33 +2122,48 @@ var init_marketplace = __esm({
|
|
|
1839
2122
|
});
|
|
1840
2123
|
router3.get("/download/:id", async (req, res, next) => {
|
|
1841
2124
|
try {
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
2125
|
+
const ghCfg = requireMarketplaceGitHubConfig();
|
|
2126
|
+
const registry = new GitHubMarketplaceRegistry(ghCfg);
|
|
2127
|
+
let index;
|
|
2128
|
+
let sha;
|
|
2129
|
+
try {
|
|
2130
|
+
const result = await registry.readIndexFromRepo();
|
|
2131
|
+
index = result.index;
|
|
2132
|
+
sha = result.sha;
|
|
2133
|
+
} catch (e) {
|
|
2134
|
+
console.warn("Failed to read index from repo, falling back to cached:", e);
|
|
2135
|
+
index = await fetchIndex();
|
|
2136
|
+
}
|
|
2137
|
+
const entryIndex = (index.skills || []).findIndex(
|
|
2138
|
+
(s) => GitHubMarketplaceRegistry.stableIdForEntry(s) === req.params.id
|
|
2139
|
+
);
|
|
2140
|
+
const entry = entryIndex >= 0 ? index.skills[entryIndex] : void 0;
|
|
2141
|
+
if (!entry) throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
2142
|
+
if (sha) {
|
|
2143
|
+
try {
|
|
2144
|
+
entry.downloads = (entry.downloads || 0) + 1;
|
|
2145
|
+
index.skills[entryIndex] = entry;
|
|
2146
|
+
await registry.upsertIndexToRepo(index, sha);
|
|
2147
|
+
} catch (e) {
|
|
2148
|
+
console.warn("Failed to update download count:", e);
|
|
1846
2149
|
}
|
|
1847
|
-
});
|
|
1848
|
-
if (!marketplaceSkill) {
|
|
1849
|
-
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1850
2150
|
}
|
|
1851
|
-
if (
|
|
2151
|
+
if (entry.type === "git") {
|
|
1852
2152
|
return res.json({
|
|
1853
2153
|
success: true,
|
|
1854
|
-
data: {
|
|
1855
|
-
type: "git",
|
|
1856
|
-
sourceUrl: marketplaceSkill.skill.sourceUrl
|
|
1857
|
-
}
|
|
2154
|
+
data: { type: "git", sourceUrl: entry.gitUrl }
|
|
1858
2155
|
});
|
|
1859
2156
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
"
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2157
|
+
return res.json({
|
|
2158
|
+
success: true,
|
|
2159
|
+
data: {
|
|
2160
|
+
type: "bundle",
|
|
2161
|
+
bundleUrl: entry.bundleUrl,
|
|
2162
|
+
bundleSize: entry.bundleSize,
|
|
2163
|
+
sha256: entry.sha256,
|
|
2164
|
+
skillVersion: entry.skillVersion
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
1869
2167
|
} catch (error) {
|
|
1870
2168
|
next(error);
|
|
1871
2169
|
}
|
|
@@ -1879,38 +2177,74 @@ var init_marketplace = __esm({
|
|
|
1879
2177
|
error: "skillId is required"
|
|
1880
2178
|
});
|
|
1881
2179
|
}
|
|
1882
|
-
const skill = await
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
});
|
|
1891
|
-
if (existingEntry) {
|
|
1892
|
-
throw new AppError(ErrorCode.ALREADY_EXISTS, "Skill is already published to marketplace", 409);
|
|
1893
|
-
}
|
|
1894
|
-
let bundlePath = null;
|
|
1895
|
-
let bundleSize = null;
|
|
1896
|
-
if (skill.source === "local" && existsSync7(skill.storagePath)) {
|
|
1897
|
-
bundlePath = await bundleService.createBundle(skill.storagePath, skill.name);
|
|
1898
|
-
bundleSize = await bundleService.getBundleSize(bundlePath);
|
|
2180
|
+
const skill = await skillService.getSkillById(skillId);
|
|
2181
|
+
const ghCfg = requireMarketplaceGitHubConfig();
|
|
2182
|
+
if (!ghCfg.owner || !ghCfg.repo || !ghCfg.branch || !ghCfg.token) {
|
|
2183
|
+
throw new AppError(
|
|
2184
|
+
ErrorCode.VALIDATION_ERROR,
|
|
2185
|
+
"GitHub registry config is incomplete. Please set owner/repo/branch/token in Settings -> Marketplace.",
|
|
2186
|
+
400
|
|
2187
|
+
);
|
|
1899
2188
|
}
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
2189
|
+
const registry = new GitHubMarketplaceRegistry(ghCfg);
|
|
2190
|
+
const { index, sha } = await registry.readIndexFromRepo();
|
|
2191
|
+
if (skill.source === "git") {
|
|
2192
|
+
if (!skill.sourceUrl) {
|
|
2193
|
+
throw new AppError(ErrorCode.VALIDATION_ERROR, "Git skill is missing sourceUrl", 400);
|
|
2194
|
+
}
|
|
2195
|
+
registry.upsertSkillEntry(index, {
|
|
2196
|
+
type: "git",
|
|
2197
|
+
name: skill.name,
|
|
2198
|
+
description: skill.description || "",
|
|
1903
2199
|
publisherName: publisherName || "Anonymous",
|
|
2200
|
+
gitUrl: skill.sourceUrl
|
|
2201
|
+
});
|
|
2202
|
+
} else {
|
|
2203
|
+
if (!existsSync7(skill.storagePath)) {
|
|
2204
|
+
throw new AppError(ErrorCode.FILE_SYSTEM_ERROR, "Skill files not found on disk", 400);
|
|
2205
|
+
}
|
|
2206
|
+
const bundlePath = await bundleService.createBundle(skill.storagePath, skill.name);
|
|
2207
|
+
const parsedMeta = (() => {
|
|
2208
|
+
try {
|
|
2209
|
+
return skill.metadata ? JSON.parse(skill.metadata) : {};
|
|
2210
|
+
} catch {
|
|
2211
|
+
return {};
|
|
2212
|
+
}
|
|
2213
|
+
})();
|
|
2214
|
+
const skillVersion = String(parsedMeta.version || parsedMeta.skillVersion || "1.0.0");
|
|
2215
|
+
const uploaded = await registry.createReleaseWithAsset({
|
|
2216
|
+
skillName: skill.name,
|
|
2217
|
+
skillVersion,
|
|
1904
2218
|
bundlePath,
|
|
1905
|
-
|
|
2219
|
+
description: skill.description || ""
|
|
2220
|
+
});
|
|
2221
|
+
registry.upsertSkillEntry(index, {
|
|
2222
|
+
type: "bundle",
|
|
2223
|
+
name: skill.name,
|
|
2224
|
+
description: skill.description || "",
|
|
2225
|
+
publisherName: publisherName || "Anonymous",
|
|
2226
|
+
bundleUrl: uploaded.bundleUrl,
|
|
2227
|
+
bundleSize: uploaded.bundleSize,
|
|
2228
|
+
sha256: uploaded.sha256,
|
|
2229
|
+
skillVersion
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
await registry.upsertIndexToRepo(index, sha);
|
|
2233
|
+
await prisma.marketplaceSkill.upsert({
|
|
2234
|
+
where: { skillId: skill.id },
|
|
2235
|
+
create: {
|
|
2236
|
+
skillId: skill.id,
|
|
2237
|
+
publisherName: publisherName || "Anonymous",
|
|
2238
|
+
downloads: 0
|
|
1906
2239
|
},
|
|
1907
|
-
|
|
1908
|
-
|
|
2240
|
+
update: {
|
|
2241
|
+
publisherName: publisherName || "Anonymous",
|
|
2242
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1909
2243
|
}
|
|
1910
2244
|
});
|
|
1911
2245
|
res.status(201).json({
|
|
1912
2246
|
success: true,
|
|
1913
|
-
data:
|
|
2247
|
+
data: { ok: true },
|
|
1914
2248
|
message: "Skill published to marketplace successfully"
|
|
1915
2249
|
});
|
|
1916
2250
|
} catch (error) {
|
|
@@ -1919,66 +2253,117 @@ var init_marketplace = __esm({
|
|
|
1919
2253
|
});
|
|
1920
2254
|
router3.post("/install/:id", async (req, res, next) => {
|
|
1921
2255
|
try {
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
2256
|
+
const ghCfg = requireMarketplaceGitHubConfig();
|
|
2257
|
+
const registry = new GitHubMarketplaceRegistry(ghCfg);
|
|
2258
|
+
let index;
|
|
2259
|
+
let sha;
|
|
2260
|
+
try {
|
|
2261
|
+
const result = await registry.readIndexFromRepo();
|
|
2262
|
+
index = result.index;
|
|
2263
|
+
sha = result.sha;
|
|
2264
|
+
} catch (e) {
|
|
2265
|
+
console.warn("Failed to read index from repo for install count:", e);
|
|
2266
|
+
index = await fetchIndex();
|
|
2267
|
+
}
|
|
2268
|
+
const entryIndex = (index.skills || []).findIndex(
|
|
2269
|
+
(s) => GitHubMarketplaceRegistry.stableIdForEntry(s) === req.params.id
|
|
2270
|
+
);
|
|
2271
|
+
const entry = entryIndex >= 0 ? index.skills[entryIndex] : void 0;
|
|
2272
|
+
if (!entry) throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
2273
|
+
if (sha) {
|
|
2274
|
+
try {
|
|
2275
|
+
entry.downloads = (entry.downloads || 0) + 1;
|
|
2276
|
+
index.skills[entryIndex] = entry;
|
|
2277
|
+
await registry.upsertIndexToRepo(index, sha);
|
|
2278
|
+
} catch (e) {
|
|
2279
|
+
console.warn("Failed to update download count:", e);
|
|
1926
2280
|
}
|
|
1927
|
-
});
|
|
1928
|
-
if (!marketplaceSkill) {
|
|
1929
|
-
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1930
2281
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
const newSkill = await skillService.createSkillFromGit(
|
|
1934
|
-
sourceSkill.sourceUrl,
|
|
1935
|
-
sourceSkill.description || void 0
|
|
1936
|
-
);
|
|
1937
|
-
await prisma.marketplaceSkill.update({
|
|
1938
|
-
where: { id: req.params.id },
|
|
1939
|
-
data: { downloads: { increment: 1 } }
|
|
1940
|
-
});
|
|
2282
|
+
if (entry.type === "git") {
|
|
2283
|
+
const newSkill = await skillService.createSkillFromGit(entry.gitUrl, entry.description || void 0);
|
|
1941
2284
|
return res.status(201).json({
|
|
1942
2285
|
success: true,
|
|
1943
2286
|
data: newSkill,
|
|
1944
2287
|
message: "Skill installed from marketplace successfully"
|
|
1945
2288
|
});
|
|
1946
2289
|
}
|
|
1947
|
-
|
|
2290
|
+
const tempDir = process.env.TEMP_DIR || join7(process.env.HOME || "", ".skillverse", "temp");
|
|
2291
|
+
await mkdir5(tempDir, { recursive: true });
|
|
2292
|
+
const resp = await fetch(entry.bundleUrl);
|
|
2293
|
+
if (!resp.ok) {
|
|
2294
|
+
throw new AppError(
|
|
2295
|
+
ErrorCode.INTERNAL_ERROR,
|
|
2296
|
+
`Failed to download bundle: ${resp.status} ${resp.statusText}`,
|
|
2297
|
+
502
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
2301
|
+
const tmpPath = join7(tempDir, `marketplace-${entry.name}-${Date.now()}.tar.gz`);
|
|
2302
|
+
await writeFile(tmpPath, buf);
|
|
2303
|
+
try {
|
|
1948
2304
|
const newSkill = await skillService.createSkillFromBundle(
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2305
|
+
tmpPath,
|
|
2306
|
+
entry.name,
|
|
2307
|
+
entry.description || void 0
|
|
1952
2308
|
);
|
|
1953
|
-
await prisma.marketplaceSkill.update({
|
|
1954
|
-
where: { id: req.params.id },
|
|
1955
|
-
data: { downloads: { increment: 1 } }
|
|
1956
|
-
});
|
|
1957
2309
|
return res.status(201).json({
|
|
1958
2310
|
success: true,
|
|
1959
2311
|
data: newSkill,
|
|
1960
2312
|
message: "Skill installed from marketplace successfully"
|
|
1961
2313
|
});
|
|
2314
|
+
} finally {
|
|
2315
|
+
await rm5(tmpPath, { force: true }).catch(() => {
|
|
2316
|
+
});
|
|
1962
2317
|
}
|
|
1963
|
-
return res.status(400).json({
|
|
1964
|
-
success: false,
|
|
1965
|
-
error: "Cannot install this skill. Bundle not available."
|
|
1966
|
-
});
|
|
1967
2318
|
} catch (error) {
|
|
1968
2319
|
next(error);
|
|
1969
2320
|
}
|
|
1970
2321
|
});
|
|
1971
2322
|
router3.delete("/unpublish/:skillId", async (req, res, next) => {
|
|
1972
2323
|
try {
|
|
1973
|
-
const
|
|
1974
|
-
|
|
2324
|
+
const skill = await skillService.getSkillById(req.params.skillId);
|
|
2325
|
+
const ghCfg = requireMarketplaceGitHubConfig();
|
|
2326
|
+
if (!ghCfg.owner || !ghCfg.repo || !ghCfg.branch || !ghCfg.token) {
|
|
2327
|
+
throw new AppError(
|
|
2328
|
+
ErrorCode.VALIDATION_ERROR,
|
|
2329
|
+
"GitHub registry config is incomplete. Please set owner/repo/branch/token in Settings -> Marketplace.",
|
|
2330
|
+
400
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
const registry = new GitHubMarketplaceRegistry(ghCfg);
|
|
2334
|
+
const { index, sha } = await registry.readIndexFromRepo();
|
|
2335
|
+
const before = index.skills.length;
|
|
2336
|
+
const entryToRemove = index.skills.find((s) => {
|
|
2337
|
+
if (s?.name !== skill.name) return false;
|
|
2338
|
+
if (skill.source === "git") return s.type === "git";
|
|
2339
|
+
return s.type === "bundle";
|
|
1975
2340
|
});
|
|
1976
|
-
if (
|
|
2341
|
+
if (entryToRemove) {
|
|
2342
|
+
if (entryToRemove.type === "bundle" && entryToRemove.bundleUrl) {
|
|
2343
|
+
try {
|
|
2344
|
+
const match = entryToRemove.bundleUrl.match(/\/releases\/download\/(.+)\/[^/]+$/);
|
|
2345
|
+
if (match && match[1]) {
|
|
2346
|
+
const tag = decodeURIComponent(match[1]);
|
|
2347
|
+
console.log(`Deleting release tag: ${tag}`);
|
|
2348
|
+
await registry.deleteReleaseByTag(tag);
|
|
2349
|
+
}
|
|
2350
|
+
} catch (e) {
|
|
2351
|
+
console.warn("Failed to delete GitHub release:", e);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
index.skills = index.skills.filter((s) => s !== entryToRemove);
|
|
2355
|
+
}
|
|
2356
|
+
const after = index.skills.length;
|
|
2357
|
+
if (before === after) {
|
|
1977
2358
|
throw new AppError(ErrorCode.NOT_FOUND, "Skill is not published to marketplace", 404);
|
|
1978
2359
|
}
|
|
1979
|
-
await
|
|
1980
|
-
|
|
1981
|
-
|
|
2360
|
+
await registry.upsertIndexToRepo(index, sha);
|
|
2361
|
+
try {
|
|
2362
|
+
await prisma.marketplaceSkill.delete({
|
|
2363
|
+
where: { skillId: skill.id }
|
|
2364
|
+
});
|
|
2365
|
+
} catch (e) {
|
|
2366
|
+
}
|
|
1982
2367
|
res.json({
|
|
1983
2368
|
success: true,
|
|
1984
2369
|
message: "Skill unpublished from marketplace successfully"
|
|
@@ -2092,12 +2477,12 @@ var init_dashboard = __esm({
|
|
|
2092
2477
|
|
|
2093
2478
|
// src/routes/config.ts
|
|
2094
2479
|
import { Router as Router5 } from "express";
|
|
2095
|
-
import { join as
|
|
2096
|
-
import { existsSync as existsSync8, readFileSync as
|
|
2480
|
+
import { join as join8 } from "path";
|
|
2481
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
2097
2482
|
function readConfig() {
|
|
2098
2483
|
if (existsSync8(CONFIG_FILE)) {
|
|
2099
2484
|
try {
|
|
2100
|
-
return JSON.parse(
|
|
2485
|
+
return JSON.parse(readFileSync3(CONFIG_FILE, "utf-8"));
|
|
2101
2486
|
} catch (e) {
|
|
2102
2487
|
console.error("Failed to read config.json:", e);
|
|
2103
2488
|
return {};
|
|
@@ -2111,8 +2496,8 @@ var init_config2 = __esm({
|
|
|
2111
2496
|
"use strict";
|
|
2112
2497
|
router5 = Router5();
|
|
2113
2498
|
HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
2114
|
-
BOOTSTRAP_HOME =
|
|
2115
|
-
CONFIG_FILE =
|
|
2499
|
+
BOOTSTRAP_HOME = join8(HOME, ".skillverse");
|
|
2500
|
+
CONFIG_FILE = join8(BOOTSTRAP_HOME, "config.json");
|
|
2116
2501
|
router5.get("/", (req, res, next) => {
|
|
2117
2502
|
try {
|
|
2118
2503
|
const fileConfig = readConfig();
|
|
@@ -2120,8 +2505,17 @@ var init_config2 = __esm({
|
|
|
2120
2505
|
// Show saved preference if available, otherwise current env/default
|
|
2121
2506
|
skillverseHome: fileConfig.skillverseHome || process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME,
|
|
2122
2507
|
// registryUrl is potentially in config.json already
|
|
2123
|
-
registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000"
|
|
2124
|
-
//
|
|
2508
|
+
registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000",
|
|
2509
|
+
// Marketplace is now hardcoded
|
|
2510
|
+
marketplace: {
|
|
2511
|
+
indexUrl: "",
|
|
2512
|
+
github: {
|
|
2513
|
+
owner: "",
|
|
2514
|
+
repo: "",
|
|
2515
|
+
branch: "",
|
|
2516
|
+
tokenConfigured: false
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2125
2519
|
};
|
|
2126
2520
|
res.json({
|
|
2127
2521
|
success: true,
|
|
@@ -2133,19 +2527,19 @@ var init_config2 = __esm({
|
|
|
2133
2527
|
});
|
|
2134
2528
|
router5.post("/", async (req, res, next) => {
|
|
2135
2529
|
try {
|
|
2136
|
-
const { skillverseHome, registryUrl, migrate } = req.body;
|
|
2530
|
+
const { skillverseHome, registryUrl, migrate, marketplace } = req.body;
|
|
2137
2531
|
const currentConfig = readConfig();
|
|
2138
2532
|
const oldHome = process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME;
|
|
2139
2533
|
if (migrate && skillverseHome && skillverseHome !== oldHome) {
|
|
2140
2534
|
console.log(`Migrating data from ${oldHome} to ${skillverseHome}...`);
|
|
2141
2535
|
const { cp: cp2 } = await import("fs/promises");
|
|
2142
2536
|
if (!existsSync8(skillverseHome)) {
|
|
2143
|
-
const { mkdir:
|
|
2144
|
-
await
|
|
2537
|
+
const { mkdir: mkdir7 } = await import("fs/promises");
|
|
2538
|
+
await mkdir7(skillverseHome, { recursive: true });
|
|
2145
2539
|
}
|
|
2146
2540
|
const copyDir = async (srcName) => {
|
|
2147
|
-
const src =
|
|
2148
|
-
const dest =
|
|
2541
|
+
const src = join8(oldHome, srcName);
|
|
2542
|
+
const dest = join8(skillverseHome, srcName);
|
|
2149
2543
|
if (existsSync8(src)) {
|
|
2150
2544
|
console.log(`Copying ${srcName}...`);
|
|
2151
2545
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -2153,8 +2547,8 @@ var init_config2 = __esm({
|
|
|
2153
2547
|
};
|
|
2154
2548
|
await copyDir("skills");
|
|
2155
2549
|
await copyDir("marketplace");
|
|
2156
|
-
const dbSrc =
|
|
2157
|
-
const dbDest =
|
|
2550
|
+
const dbSrc = join8(oldHome, "skillverse.db");
|
|
2551
|
+
const dbDest = join8(skillverseHome, "skillverse.db");
|
|
2158
2552
|
if (existsSync8(dbSrc)) {
|
|
2159
2553
|
console.log("Copying database...");
|
|
2160
2554
|
await cp2(dbSrc, dbDest, { force: true });
|
|
@@ -2164,8 +2558,8 @@ var init_config2 = __esm({
|
|
|
2164
2558
|
if (existsSync8(shmSrc)) await cp2(shmSrc, dbDest + "-shm", { force: true });
|
|
2165
2559
|
console.log("Updating skill paths in new database...");
|
|
2166
2560
|
const { execSync } = await import("child_process");
|
|
2167
|
-
const oldSkillsPath =
|
|
2168
|
-
const newSkillsPath =
|
|
2561
|
+
const oldSkillsPath = join8(oldHome, "skills");
|
|
2562
|
+
const newSkillsPath = join8(skillverseHome, "skills");
|
|
2169
2563
|
const updateQuery = `UPDATE Skill SET storagePath = replace(storagePath, '${oldSkillsPath}', '${newSkillsPath}') WHERE storagePath LIKE '${oldSkillsPath}%';`;
|
|
2170
2564
|
try {
|
|
2171
2565
|
execSync(`sqlite3 "${dbDest}" "${updateQuery}"`, { encoding: "utf-8" });
|
|
@@ -2175,21 +2569,79 @@ var init_config2 = __esm({
|
|
|
2175
2569
|
}
|
|
2176
2570
|
}
|
|
2177
2571
|
}
|
|
2178
|
-
const
|
|
2572
|
+
const nextConfig = {
|
|
2179
2573
|
...currentConfig,
|
|
2180
2574
|
...skillverseHome && { skillverseHome },
|
|
2181
2575
|
...registryUrl && { registryUrl }
|
|
2182
2576
|
};
|
|
2183
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
2577
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(nextConfig, null, 2));
|
|
2184
2578
|
res.json({
|
|
2185
2579
|
success: true,
|
|
2186
|
-
data:
|
|
2580
|
+
data: {
|
|
2581
|
+
...nextConfig,
|
|
2582
|
+
// Never return token
|
|
2583
|
+
marketplace: {
|
|
2584
|
+
indexUrl: nextConfig.marketplace?.indexUrl || "",
|
|
2585
|
+
github: {
|
|
2586
|
+
owner: nextConfig.marketplace?.github?.owner || "",
|
|
2587
|
+
repo: nextConfig.marketplace?.github?.repo || "",
|
|
2588
|
+
branch: nextConfig.marketplace?.github?.branch || "main",
|
|
2589
|
+
tokenConfigured: Boolean(nextConfig.marketplace?.github?.token)
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
},
|
|
2187
2593
|
message: "Configuration saved. Restart server for changes to take effect."
|
|
2188
2594
|
});
|
|
2189
2595
|
} catch (error) {
|
|
2190
2596
|
next(error);
|
|
2191
2597
|
}
|
|
2192
2598
|
});
|
|
2599
|
+
router5.get("/version", async (req, res, next) => {
|
|
2600
|
+
try {
|
|
2601
|
+
const { dirname: dirname4, join: pathJoin } = await import("path");
|
|
2602
|
+
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
2603
|
+
const __filename4 = fileURLToPath4(import.meta.url);
|
|
2604
|
+
const __dirname4 = dirname4(__filename4);
|
|
2605
|
+
const packageJsonPath = pathJoin(__dirname4, "..", "..", "package.json");
|
|
2606
|
+
let currentVersion = "0.0.0";
|
|
2607
|
+
if (existsSync8(packageJsonPath)) {
|
|
2608
|
+
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
2609
|
+
currentVersion = pkg.version || "0.0.0";
|
|
2610
|
+
}
|
|
2611
|
+
let latestVersion = currentVersion;
|
|
2612
|
+
let hasUpdate = false;
|
|
2613
|
+
try {
|
|
2614
|
+
const response = await fetch("https://registry.npmjs.org/skillverse/latest");
|
|
2615
|
+
if (response.ok) {
|
|
2616
|
+
const data = await response.json();
|
|
2617
|
+
latestVersion = data.version || currentVersion;
|
|
2618
|
+
const parseVersion = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
|
|
2619
|
+
const current = parseVersion(currentVersion);
|
|
2620
|
+
const latest = parseVersion(latestVersion);
|
|
2621
|
+
for (let i = 0; i < 3; i++) {
|
|
2622
|
+
if (latest[i] > current[i]) {
|
|
2623
|
+
hasUpdate = true;
|
|
2624
|
+
break;
|
|
2625
|
+
} else if (latest[i] < current[i]) {
|
|
2626
|
+
break;
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
} catch (fetchError) {
|
|
2631
|
+
console.warn("Failed to check npm registry for updates:", fetchError);
|
|
2632
|
+
}
|
|
2633
|
+
res.json({
|
|
2634
|
+
success: true,
|
|
2635
|
+
data: {
|
|
2636
|
+
currentVersion,
|
|
2637
|
+
latestVersion,
|
|
2638
|
+
hasUpdate
|
|
2639
|
+
}
|
|
2640
|
+
});
|
|
2641
|
+
} catch (error) {
|
|
2642
|
+
next(error);
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2193
2645
|
config_default = router5;
|
|
2194
2646
|
}
|
|
2195
2647
|
});
|
|
@@ -2218,17 +2670,17 @@ import express from "express";
|
|
|
2218
2670
|
import cors from "cors";
|
|
2219
2671
|
import dotenv2 from "dotenv";
|
|
2220
2672
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2221
|
-
import { dirname as dirname3, join as
|
|
2222
|
-
import { mkdir as
|
|
2673
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
2674
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
2223
2675
|
import { existsSync as existsSync9 } from "fs";
|
|
2224
2676
|
async function initializeStorage() {
|
|
2225
|
-
const skillverseHome = process.env.SKILLVERSE_HOME ||
|
|
2226
|
-
const skillsDir = process.env.SKILLS_DIR ||
|
|
2227
|
-
const marketplaceDir = process.env.MARKETPLACE_DIR ||
|
|
2677
|
+
const skillverseHome = process.env.SKILLVERSE_HOME || join9(process.env.HOME || "", ".skillverse");
|
|
2678
|
+
const skillsDir = process.env.SKILLS_DIR || join9(skillverseHome, "skills");
|
|
2679
|
+
const marketplaceDir = process.env.MARKETPLACE_DIR || join9(skillverseHome, "marketplace");
|
|
2228
2680
|
const dirs = [skillverseHome, skillsDir, marketplaceDir];
|
|
2229
2681
|
for (const dir of dirs) {
|
|
2230
2682
|
if (!existsSync9(dir)) {
|
|
2231
|
-
await
|
|
2683
|
+
await mkdir6(dir, { recursive: true });
|
|
2232
2684
|
console.log(`Created directory: ${dir}`);
|
|
2233
2685
|
}
|
|
2234
2686
|
}
|
|
@@ -2237,13 +2689,13 @@ async function startServer(port = 3001) {
|
|
|
2237
2689
|
try {
|
|
2238
2690
|
await initializeStorage();
|
|
2239
2691
|
await ensureDatabaseInitialized();
|
|
2240
|
-
console.log("\u{1F50D}
|
|
2692
|
+
console.log("\u{1F50D} Syncing skills with filesystem...");
|
|
2241
2693
|
const { skillService: skillService2 } = await Promise.resolve().then(() => (init_skillService(), skillService_exports));
|
|
2242
|
-
await skillService2.
|
|
2694
|
+
await skillService2.syncSkillsWithFileSystem();
|
|
2243
2695
|
return new Promise((resolve) => {
|
|
2244
2696
|
app.listen(port, () => {
|
|
2245
2697
|
console.log(`\u{1F680} SkillVerse server running on http://localhost:${port}`);
|
|
2246
|
-
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME ||
|
|
2698
|
+
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME || join9(process.env.HOME || "", ".skillverse")}`);
|
|
2247
2699
|
resolve();
|
|
2248
2700
|
});
|
|
2249
2701
|
});
|
|
@@ -2274,13 +2726,13 @@ var init_index = __esm({
|
|
|
2274
2726
|
app.use(express.urlencoded({ extended: true }));
|
|
2275
2727
|
app.use(requestLogger);
|
|
2276
2728
|
possiblePublicDirs = [
|
|
2277
|
-
|
|
2729
|
+
join9(__dirname3, "../public"),
|
|
2278
2730
|
// Production: dist/index.js -> public
|
|
2279
|
-
|
|
2731
|
+
join9(__dirname3, "../../public"),
|
|
2280
2732
|
// Dev tsx might resolve here
|
|
2281
|
-
|
|
2733
|
+
join9(process.cwd(), "public"),
|
|
2282
2734
|
// Fallback: relative to cwd
|
|
2283
|
-
|
|
2735
|
+
join9(process.cwd(), "server/public")
|
|
2284
2736
|
// Fallback: from root
|
|
2285
2737
|
];
|
|
2286
2738
|
publicDir = possiblePublicDirs.find((dir) => existsSync9(dir));
|
|
@@ -2300,7 +2752,7 @@ var init_index = __esm({
|
|
|
2300
2752
|
});
|
|
2301
2753
|
if (publicDir && existsSync9(publicDir)) {
|
|
2302
2754
|
app.get("*", (req, res) => {
|
|
2303
|
-
res.sendFile(
|
|
2755
|
+
res.sendFile(join9(publicDir, "index.html"));
|
|
2304
2756
|
});
|
|
2305
2757
|
}
|
|
2306
2758
|
app.use(errorHandler);
|
|
@@ -2317,12 +2769,12 @@ init_skillService();
|
|
|
2317
2769
|
init_workspaceService();
|
|
2318
2770
|
import { program } from "commander";
|
|
2319
2771
|
import open from "open";
|
|
2320
|
-
import { existsSync as existsSync10, readFileSync as
|
|
2321
|
-
import { join as
|
|
2772
|
+
import { existsSync as existsSync10, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2773
|
+
import { join as join10 } from "path";
|
|
2322
2774
|
import readline from "readline";
|
|
2323
2775
|
var { skillverseHome: SKILLVERSE_HOME } = config;
|
|
2324
|
-
var AUTH_FILE =
|
|
2325
|
-
var CONFIG_FILE2 =
|
|
2776
|
+
var AUTH_FILE = join10(SKILLVERSE_HOME, "auth.json");
|
|
2777
|
+
var CONFIG_FILE2 = join10(SKILLVERSE_HOME, "config.json");
|
|
2326
2778
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
2327
2779
|
if (["list", "start", "install", "add", "remove", "import", "search"].includes(actionCommand.name())) {
|
|
2328
2780
|
await ensureDatabaseInitialized();
|
|
@@ -2330,14 +2782,14 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
|
2330
2782
|
});
|
|
2331
2783
|
function getRegistryUrl() {
|
|
2332
2784
|
if (existsSync10(CONFIG_FILE2)) {
|
|
2333
|
-
const config3 = JSON.parse(
|
|
2785
|
+
const config3 = JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8"));
|
|
2334
2786
|
return config3.registryUrl || "http://localhost:4000";
|
|
2335
2787
|
}
|
|
2336
2788
|
return process.env.SKILLVERSE_REGISTRY || "http://localhost:4000";
|
|
2337
2789
|
}
|
|
2338
2790
|
function getAuthToken() {
|
|
2339
2791
|
if (existsSync10(AUTH_FILE)) {
|
|
2340
|
-
const auth = JSON.parse(
|
|
2792
|
+
const auth = JSON.parse(readFileSync4(AUTH_FILE, "utf-8"));
|
|
2341
2793
|
return auth.token || null;
|
|
2342
2794
|
}
|
|
2343
2795
|
return null;
|
|
@@ -2449,7 +2901,7 @@ program.command("publish [path]").description("Publish a skill to the Registry")
|
|
|
2449
2901
|
const form = new FormData();
|
|
2450
2902
|
form.append("name", skillName);
|
|
2451
2903
|
if (options.description) form.append("description", options.description);
|
|
2452
|
-
form.append("bundle",
|
|
2904
|
+
form.append("bundle", readFileSync4(bundlePath), {
|
|
2453
2905
|
filename: `${skillName}.tar.gz`,
|
|
2454
2906
|
contentType: "application/gzip"
|
|
2455
2907
|
});
|
|
@@ -2508,7 +2960,7 @@ program.command("search <query>").description("Search for skills in the Registry
|
|
|
2508
2960
|
}
|
|
2509
2961
|
});
|
|
2510
2962
|
program.command("config").description("Configure SkillVerse settings").option("-r, --registry <url>", "Set default registry URL").action((options) => {
|
|
2511
|
-
const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(
|
|
2963
|
+
const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8")) : {};
|
|
2512
2964
|
if (options.registry) {
|
|
2513
2965
|
config3.registryUrl = options.registry;
|
|
2514
2966
|
writeFileSync2(CONFIG_FILE2, JSON.stringify(config3, null, 2));
|
|
@@ -2561,7 +3013,7 @@ program.command("install [gitUrl]").description("Install a skill from Git URL").
|
|
|
2561
3013
|
}
|
|
2562
3014
|
});
|
|
2563
3015
|
program.command("add").description("Add a local skill").requiredOption("-p, --path <path>", "Path to skill directory").option("-a, --agent <agent>", "Link to agent workspace").action(async (options) => {
|
|
2564
|
-
const sourcePath = options.path.startsWith("/") ? options.path :
|
|
3016
|
+
const sourcePath = options.path.startsWith("/") ? options.path : join10(process.cwd(), options.path);
|
|
2565
3017
|
if (!existsSync10(sourcePath)) {
|
|
2566
3018
|
console.error(`\u274C Source path not found: ${sourcePath}`);
|
|
2567
3019
|
process.exit(1);
|