skillverse 0.1.6 → 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 +526 -158
- package/dist/bin.js.map +1 -1
- package/dist/index.js +514 -158
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- 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-Dd6kW-3f.js +0 -20
- package/public/assets/index-li8hN2px.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -1177,7 +1177,7 @@ ${relativeSkillsPath}/
|
|
|
1177
1177
|
if (!existsSync4(skillsPath)) {
|
|
1178
1178
|
return [];
|
|
1179
1179
|
}
|
|
1180
|
-
const { readdir: readdir2, stat: stat2, readFile:
|
|
1180
|
+
const { readdir: readdir2, stat: stat2, readFile: readFile4 } = await import("fs/promises");
|
|
1181
1181
|
const items = await readdir2(skillsPath);
|
|
1182
1182
|
const existingSkills = [];
|
|
1183
1183
|
for (const item of items) {
|
|
@@ -1218,7 +1218,7 @@ ${relativeSkillsPath}/
|
|
|
1218
1218
|
if (!existsSync4(SKILLS_DIR)) {
|
|
1219
1219
|
await mkdir2(SKILLS_DIR, { recursive: true });
|
|
1220
1220
|
}
|
|
1221
|
-
const { rename, readFile:
|
|
1221
|
+
const { rename, readFile: readFile4, cp: cp2 } = await import("fs/promises");
|
|
1222
1222
|
const migrated = [];
|
|
1223
1223
|
const errors = [];
|
|
1224
1224
|
for (const skillName of skillNames) {
|
|
@@ -1251,7 +1251,7 @@ ${relativeSkillsPath}/
|
|
|
1251
1251
|
if (existsSync4(skillMdPath)) {
|
|
1252
1252
|
try {
|
|
1253
1253
|
const matter2 = await import("gray-matter");
|
|
1254
|
-
const fileContent = await
|
|
1254
|
+
const fileContent = await readFile4(skillMdPath, "utf-8");
|
|
1255
1255
|
const parsed = matter2.default(fileContent);
|
|
1256
1256
|
description = parsed.data.description || "";
|
|
1257
1257
|
metadata = parsed.data;
|
|
@@ -1534,7 +1534,7 @@ var init_skills = __esm({
|
|
|
1534
1534
|
});
|
|
1535
1535
|
router.get("/:id/skill-md", async (req, res, next) => {
|
|
1536
1536
|
try {
|
|
1537
|
-
const { readFile:
|
|
1537
|
+
const { readFile: readFile4 } = await import("fs/promises");
|
|
1538
1538
|
const skill = await skillService.getSkillById(req.params.id);
|
|
1539
1539
|
const skillMdPath = join5(skill.storagePath, "SKILL.md");
|
|
1540
1540
|
if (!existsSync5(skillMdPath)) {
|
|
@@ -1546,7 +1546,7 @@ var init_skills = __esm({
|
|
|
1546
1546
|
}
|
|
1547
1547
|
});
|
|
1548
1548
|
}
|
|
1549
|
-
const content = await
|
|
1549
|
+
const content = await readFile4(skillMdPath, "utf-8");
|
|
1550
1550
|
res.json({
|
|
1551
1551
|
success: true,
|
|
1552
1552
|
data: {
|
|
@@ -1804,52 +1804,300 @@ var init_bundleService = __esm({
|
|
|
1804
1804
|
}
|
|
1805
1805
|
});
|
|
1806
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
|
+
|
|
1807
2009
|
// src/routes/marketplace.ts
|
|
1808
2010
|
import { Router as Router3 } from "express";
|
|
1809
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
|
+
}
|
|
1810
2073
|
var router3, marketplace_default;
|
|
1811
2074
|
var init_marketplace = __esm({
|
|
1812
2075
|
"src/routes/marketplace.ts"() {
|
|
1813
2076
|
"use strict";
|
|
1814
|
-
init_db();
|
|
1815
2077
|
init_skillService();
|
|
1816
2078
|
init_bundleService();
|
|
1817
2079
|
init_errorHandler();
|
|
1818
2080
|
init_dist();
|
|
2081
|
+
init_db();
|
|
2082
|
+
init_githubMarketplaceRegistry();
|
|
2083
|
+
init_marketplaceConfig();
|
|
1819
2084
|
router3 = Router3();
|
|
1820
2085
|
router3.get("/skills", async (req, res, next) => {
|
|
1821
2086
|
try {
|
|
1822
2087
|
const { search, page = "1", pageSize = "20" } = req.query;
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
]
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
const [items, total] = await Promise.all([
|
|
1833
|
-
prisma.marketplaceSkill.findMany({
|
|
1834
|
-
where,
|
|
1835
|
-
include: {
|
|
1836
|
-
skill: true
|
|
1837
|
-
},
|
|
1838
|
-
orderBy: {
|
|
1839
|
-
downloads: "desc"
|
|
1840
|
-
},
|
|
1841
|
-
skip: (parseInt(page) - 1) * parseInt(pageSize),
|
|
1842
|
-
take: parseInt(pageSize)
|
|
1843
|
-
}),
|
|
1844
|
-
prisma.marketplaceSkill.count({ where })
|
|
1845
|
-
]);
|
|
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);
|
|
1846
2094
|
res.json({
|
|
1847
2095
|
success: true,
|
|
1848
2096
|
data: {
|
|
1849
2097
|
items,
|
|
1850
2098
|
total,
|
|
1851
|
-
page:
|
|
1852
|
-
pageSize:
|
|
2099
|
+
page: p,
|
|
2100
|
+
pageSize: ps
|
|
1853
2101
|
}
|
|
1854
2102
|
});
|
|
1855
2103
|
} catch (error) {
|
|
@@ -1858,12 +2106,9 @@ var init_marketplace = __esm({
|
|
|
1858
2106
|
});
|
|
1859
2107
|
router3.get("/skills/:id", async (req, res, next) => {
|
|
1860
2108
|
try {
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
skill: true
|
|
1865
|
-
}
|
|
1866
|
-
});
|
|
2109
|
+
const index = await fetchIndex();
|
|
2110
|
+
const items = mapIndexToMarketplaceItems(index);
|
|
2111
|
+
const marketplaceSkill = items.find((i) => i.id === req.params.id);
|
|
1867
2112
|
if (!marketplaceSkill) {
|
|
1868
2113
|
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1869
2114
|
}
|
|
@@ -1877,33 +2122,48 @@ var init_marketplace = __esm({
|
|
|
1877
2122
|
});
|
|
1878
2123
|
router3.get("/download/:id", async (req, res, next) => {
|
|
1879
2124
|
try {
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
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);
|
|
1884
2149
|
}
|
|
1885
|
-
});
|
|
1886
|
-
if (!marketplaceSkill) {
|
|
1887
|
-
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1888
2150
|
}
|
|
1889
|
-
if (
|
|
2151
|
+
if (entry.type === "git") {
|
|
1890
2152
|
return res.json({
|
|
1891
2153
|
success: true,
|
|
1892
|
-
data: {
|
|
1893
|
-
type: "git",
|
|
1894
|
-
sourceUrl: marketplaceSkill.skill.sourceUrl
|
|
1895
|
-
}
|
|
2154
|
+
data: { type: "git", sourceUrl: entry.gitUrl }
|
|
1896
2155
|
});
|
|
1897
2156
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
"
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
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
|
+
});
|
|
1907
2167
|
} catch (error) {
|
|
1908
2168
|
next(error);
|
|
1909
2169
|
}
|
|
@@ -1917,38 +2177,74 @@ var init_marketplace = __esm({
|
|
|
1917
2177
|
error: "skillId is required"
|
|
1918
2178
|
});
|
|
1919
2179
|
}
|
|
1920
|
-
const skill = await
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
});
|
|
1929
|
-
if (existingEntry) {
|
|
1930
|
-
throw new AppError(ErrorCode.ALREADY_EXISTS, "Skill is already published to marketplace", 409);
|
|
1931
|
-
}
|
|
1932
|
-
let bundlePath = null;
|
|
1933
|
-
let bundleSize = null;
|
|
1934
|
-
if (skill.source === "local" && existsSync7(skill.storagePath)) {
|
|
1935
|
-
bundlePath = await bundleService.createBundle(skill.storagePath, skill.name);
|
|
1936
|
-
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
|
+
);
|
|
1937
2188
|
}
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
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 || "",
|
|
1941
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,
|
|
1942
2218
|
bundlePath,
|
|
1943
|
-
|
|
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
|
|
1944
2239
|
},
|
|
1945
|
-
|
|
1946
|
-
|
|
2240
|
+
update: {
|
|
2241
|
+
publisherName: publisherName || "Anonymous",
|
|
2242
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1947
2243
|
}
|
|
1948
2244
|
});
|
|
1949
2245
|
res.status(201).json({
|
|
1950
2246
|
success: true,
|
|
1951
|
-
data:
|
|
2247
|
+
data: { ok: true },
|
|
1952
2248
|
message: "Skill published to marketplace successfully"
|
|
1953
2249
|
});
|
|
1954
2250
|
} catch (error) {
|
|
@@ -1957,66 +2253,117 @@ var init_marketplace = __esm({
|
|
|
1957
2253
|
});
|
|
1958
2254
|
router3.post("/install/:id", async (req, res, next) => {
|
|
1959
2255
|
try {
|
|
1960
|
-
const
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
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);
|
|
1964
2280
|
}
|
|
1965
|
-
});
|
|
1966
|
-
if (!marketplaceSkill) {
|
|
1967
|
-
throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
|
|
1968
2281
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const newSkill = await skillService.createSkillFromGit(
|
|
1972
|
-
sourceSkill.sourceUrl,
|
|
1973
|
-
sourceSkill.description || void 0
|
|
1974
|
-
);
|
|
1975
|
-
await prisma.marketplaceSkill.update({
|
|
1976
|
-
where: { id: req.params.id },
|
|
1977
|
-
data: { downloads: { increment: 1 } }
|
|
1978
|
-
});
|
|
2282
|
+
if (entry.type === "git") {
|
|
2283
|
+
const newSkill = await skillService.createSkillFromGit(entry.gitUrl, entry.description || void 0);
|
|
1979
2284
|
return res.status(201).json({
|
|
1980
2285
|
success: true,
|
|
1981
2286
|
data: newSkill,
|
|
1982
2287
|
message: "Skill installed from marketplace successfully"
|
|
1983
2288
|
});
|
|
1984
2289
|
}
|
|
1985
|
-
|
|
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 {
|
|
1986
2304
|
const newSkill = await skillService.createSkillFromBundle(
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2305
|
+
tmpPath,
|
|
2306
|
+
entry.name,
|
|
2307
|
+
entry.description || void 0
|
|
1990
2308
|
);
|
|
1991
|
-
await prisma.marketplaceSkill.update({
|
|
1992
|
-
where: { id: req.params.id },
|
|
1993
|
-
data: { downloads: { increment: 1 } }
|
|
1994
|
-
});
|
|
1995
2309
|
return res.status(201).json({
|
|
1996
2310
|
success: true,
|
|
1997
2311
|
data: newSkill,
|
|
1998
2312
|
message: "Skill installed from marketplace successfully"
|
|
1999
2313
|
});
|
|
2314
|
+
} finally {
|
|
2315
|
+
await rm5(tmpPath, { force: true }).catch(() => {
|
|
2316
|
+
});
|
|
2000
2317
|
}
|
|
2001
|
-
return res.status(400).json({
|
|
2002
|
-
success: false,
|
|
2003
|
-
error: "Cannot install this skill. Bundle not available."
|
|
2004
|
-
});
|
|
2005
2318
|
} catch (error) {
|
|
2006
2319
|
next(error);
|
|
2007
2320
|
}
|
|
2008
2321
|
});
|
|
2009
2322
|
router3.delete("/unpublish/:skillId", async (req, res, next) => {
|
|
2010
2323
|
try {
|
|
2011
|
-
const
|
|
2012
|
-
|
|
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";
|
|
2013
2340
|
});
|
|
2014
|
-
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) {
|
|
2015
2358
|
throw new AppError(ErrorCode.NOT_FOUND, "Skill is not published to marketplace", 404);
|
|
2016
2359
|
}
|
|
2017
|
-
await
|
|
2018
|
-
|
|
2019
|
-
|
|
2360
|
+
await registry.upsertIndexToRepo(index, sha);
|
|
2361
|
+
try {
|
|
2362
|
+
await prisma.marketplaceSkill.delete({
|
|
2363
|
+
where: { skillId: skill.id }
|
|
2364
|
+
});
|
|
2365
|
+
} catch (e) {
|
|
2366
|
+
}
|
|
2020
2367
|
res.json({
|
|
2021
2368
|
success: true,
|
|
2022
2369
|
message: "Skill unpublished from marketplace successfully"
|
|
@@ -2130,12 +2477,12 @@ var init_dashboard = __esm({
|
|
|
2130
2477
|
|
|
2131
2478
|
// src/routes/config.ts
|
|
2132
2479
|
import { Router as Router5 } from "express";
|
|
2133
|
-
import { join as
|
|
2134
|
-
import { existsSync as existsSync8, readFileSync as
|
|
2480
|
+
import { join as join8 } from "path";
|
|
2481
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
2135
2482
|
function readConfig() {
|
|
2136
2483
|
if (existsSync8(CONFIG_FILE)) {
|
|
2137
2484
|
try {
|
|
2138
|
-
return JSON.parse(
|
|
2485
|
+
return JSON.parse(readFileSync3(CONFIG_FILE, "utf-8"));
|
|
2139
2486
|
} catch (e) {
|
|
2140
2487
|
console.error("Failed to read config.json:", e);
|
|
2141
2488
|
return {};
|
|
@@ -2149,8 +2496,8 @@ var init_config2 = __esm({
|
|
|
2149
2496
|
"use strict";
|
|
2150
2497
|
router5 = Router5();
|
|
2151
2498
|
HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
2152
|
-
BOOTSTRAP_HOME =
|
|
2153
|
-
CONFIG_FILE =
|
|
2499
|
+
BOOTSTRAP_HOME = join8(HOME, ".skillverse");
|
|
2500
|
+
CONFIG_FILE = join8(BOOTSTRAP_HOME, "config.json");
|
|
2154
2501
|
router5.get("/", (req, res, next) => {
|
|
2155
2502
|
try {
|
|
2156
2503
|
const fileConfig = readConfig();
|
|
@@ -2158,8 +2505,17 @@ var init_config2 = __esm({
|
|
|
2158
2505
|
// Show saved preference if available, otherwise current env/default
|
|
2159
2506
|
skillverseHome: fileConfig.skillverseHome || process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME,
|
|
2160
2507
|
// registryUrl is potentially in config.json already
|
|
2161
|
-
registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000"
|
|
2162
|
-
//
|
|
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
|
+
}
|
|
2163
2519
|
};
|
|
2164
2520
|
res.json({
|
|
2165
2521
|
success: true,
|
|
@@ -2171,19 +2527,19 @@ var init_config2 = __esm({
|
|
|
2171
2527
|
});
|
|
2172
2528
|
router5.post("/", async (req, res, next) => {
|
|
2173
2529
|
try {
|
|
2174
|
-
const { skillverseHome, registryUrl, migrate } = req.body;
|
|
2530
|
+
const { skillverseHome, registryUrl, migrate, marketplace } = req.body;
|
|
2175
2531
|
const currentConfig = readConfig();
|
|
2176
2532
|
const oldHome = process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME;
|
|
2177
2533
|
if (migrate && skillverseHome && skillverseHome !== oldHome) {
|
|
2178
2534
|
console.log(`Migrating data from ${oldHome} to ${skillverseHome}...`);
|
|
2179
2535
|
const { cp: cp2 } = await import("fs/promises");
|
|
2180
2536
|
if (!existsSync8(skillverseHome)) {
|
|
2181
|
-
const { mkdir:
|
|
2182
|
-
await
|
|
2537
|
+
const { mkdir: mkdir7 } = await import("fs/promises");
|
|
2538
|
+
await mkdir7(skillverseHome, { recursive: true });
|
|
2183
2539
|
}
|
|
2184
2540
|
const copyDir = async (srcName) => {
|
|
2185
|
-
const src =
|
|
2186
|
-
const dest =
|
|
2541
|
+
const src = join8(oldHome, srcName);
|
|
2542
|
+
const dest = join8(skillverseHome, srcName);
|
|
2187
2543
|
if (existsSync8(src)) {
|
|
2188
2544
|
console.log(`Copying ${srcName}...`);
|
|
2189
2545
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -2191,8 +2547,8 @@ var init_config2 = __esm({
|
|
|
2191
2547
|
};
|
|
2192
2548
|
await copyDir("skills");
|
|
2193
2549
|
await copyDir("marketplace");
|
|
2194
|
-
const dbSrc =
|
|
2195
|
-
const dbDest =
|
|
2550
|
+
const dbSrc = join8(oldHome, "skillverse.db");
|
|
2551
|
+
const dbDest = join8(skillverseHome, "skillverse.db");
|
|
2196
2552
|
if (existsSync8(dbSrc)) {
|
|
2197
2553
|
console.log("Copying database...");
|
|
2198
2554
|
await cp2(dbSrc, dbDest, { force: true });
|
|
@@ -2202,8 +2558,8 @@ var init_config2 = __esm({
|
|
|
2202
2558
|
if (existsSync8(shmSrc)) await cp2(shmSrc, dbDest + "-shm", { force: true });
|
|
2203
2559
|
console.log("Updating skill paths in new database...");
|
|
2204
2560
|
const { execSync } = await import("child_process");
|
|
2205
|
-
const oldSkillsPath =
|
|
2206
|
-
const newSkillsPath =
|
|
2561
|
+
const oldSkillsPath = join8(oldHome, "skills");
|
|
2562
|
+
const newSkillsPath = join8(skillverseHome, "skills");
|
|
2207
2563
|
const updateQuery = `UPDATE Skill SET storagePath = replace(storagePath, '${oldSkillsPath}', '${newSkillsPath}') WHERE storagePath LIKE '${oldSkillsPath}%';`;
|
|
2208
2564
|
try {
|
|
2209
2565
|
execSync(`sqlite3 "${dbDest}" "${updateQuery}"`, { encoding: "utf-8" });
|
|
@@ -2213,15 +2569,27 @@ var init_config2 = __esm({
|
|
|
2213
2569
|
}
|
|
2214
2570
|
}
|
|
2215
2571
|
}
|
|
2216
|
-
const
|
|
2572
|
+
const nextConfig = {
|
|
2217
2573
|
...currentConfig,
|
|
2218
2574
|
...skillverseHome && { skillverseHome },
|
|
2219
2575
|
...registryUrl && { registryUrl }
|
|
2220
2576
|
};
|
|
2221
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
2577
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(nextConfig, null, 2));
|
|
2222
2578
|
res.json({
|
|
2223
2579
|
success: true,
|
|
2224
|
-
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
|
+
},
|
|
2225
2593
|
message: "Configuration saved. Restart server for changes to take effect."
|
|
2226
2594
|
});
|
|
2227
2595
|
} catch (error) {
|
|
@@ -2237,7 +2605,7 @@ var init_config2 = __esm({
|
|
|
2237
2605
|
const packageJsonPath = pathJoin(__dirname4, "..", "..", "package.json");
|
|
2238
2606
|
let currentVersion = "0.0.0";
|
|
2239
2607
|
if (existsSync8(packageJsonPath)) {
|
|
2240
|
-
const pkg = JSON.parse(
|
|
2608
|
+
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
2241
2609
|
currentVersion = pkg.version || "0.0.0";
|
|
2242
2610
|
}
|
|
2243
2611
|
let latestVersion = currentVersion;
|
|
@@ -2302,17 +2670,17 @@ import express from "express";
|
|
|
2302
2670
|
import cors from "cors";
|
|
2303
2671
|
import dotenv2 from "dotenv";
|
|
2304
2672
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2305
|
-
import { dirname as dirname3, join as
|
|
2306
|
-
import { mkdir as
|
|
2673
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
2674
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
2307
2675
|
import { existsSync as existsSync9 } from "fs";
|
|
2308
2676
|
async function initializeStorage() {
|
|
2309
|
-
const skillverseHome = process.env.SKILLVERSE_HOME ||
|
|
2310
|
-
const skillsDir = process.env.SKILLS_DIR ||
|
|
2311
|
-
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");
|
|
2312
2680
|
const dirs = [skillverseHome, skillsDir, marketplaceDir];
|
|
2313
2681
|
for (const dir of dirs) {
|
|
2314
2682
|
if (!existsSync9(dir)) {
|
|
2315
|
-
await
|
|
2683
|
+
await mkdir6(dir, { recursive: true });
|
|
2316
2684
|
console.log(`Created directory: ${dir}`);
|
|
2317
2685
|
}
|
|
2318
2686
|
}
|
|
@@ -2327,7 +2695,7 @@ async function startServer(port = 3001) {
|
|
|
2327
2695
|
return new Promise((resolve) => {
|
|
2328
2696
|
app.listen(port, () => {
|
|
2329
2697
|
console.log(`\u{1F680} SkillVerse server running on http://localhost:${port}`);
|
|
2330
|
-
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME ||
|
|
2698
|
+
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME || join9(process.env.HOME || "", ".skillverse")}`);
|
|
2331
2699
|
resolve();
|
|
2332
2700
|
});
|
|
2333
2701
|
});
|
|
@@ -2358,13 +2726,13 @@ var init_index = __esm({
|
|
|
2358
2726
|
app.use(express.urlencoded({ extended: true }));
|
|
2359
2727
|
app.use(requestLogger);
|
|
2360
2728
|
possiblePublicDirs = [
|
|
2361
|
-
|
|
2729
|
+
join9(__dirname3, "../public"),
|
|
2362
2730
|
// Production: dist/index.js -> public
|
|
2363
|
-
|
|
2731
|
+
join9(__dirname3, "../../public"),
|
|
2364
2732
|
// Dev tsx might resolve here
|
|
2365
|
-
|
|
2733
|
+
join9(process.cwd(), "public"),
|
|
2366
2734
|
// Fallback: relative to cwd
|
|
2367
|
-
|
|
2735
|
+
join9(process.cwd(), "server/public")
|
|
2368
2736
|
// Fallback: from root
|
|
2369
2737
|
];
|
|
2370
2738
|
publicDir = possiblePublicDirs.find((dir) => existsSync9(dir));
|
|
@@ -2384,7 +2752,7 @@ var init_index = __esm({
|
|
|
2384
2752
|
});
|
|
2385
2753
|
if (publicDir && existsSync9(publicDir)) {
|
|
2386
2754
|
app.get("*", (req, res) => {
|
|
2387
|
-
res.sendFile(
|
|
2755
|
+
res.sendFile(join9(publicDir, "index.html"));
|
|
2388
2756
|
});
|
|
2389
2757
|
}
|
|
2390
2758
|
app.use(errorHandler);
|
|
@@ -2401,12 +2769,12 @@ init_skillService();
|
|
|
2401
2769
|
init_workspaceService();
|
|
2402
2770
|
import { program } from "commander";
|
|
2403
2771
|
import open from "open";
|
|
2404
|
-
import { existsSync as existsSync10, readFileSync as
|
|
2405
|
-
import { join as
|
|
2772
|
+
import { existsSync as existsSync10, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2773
|
+
import { join as join10 } from "path";
|
|
2406
2774
|
import readline from "readline";
|
|
2407
2775
|
var { skillverseHome: SKILLVERSE_HOME } = config;
|
|
2408
|
-
var AUTH_FILE =
|
|
2409
|
-
var CONFIG_FILE2 =
|
|
2776
|
+
var AUTH_FILE = join10(SKILLVERSE_HOME, "auth.json");
|
|
2777
|
+
var CONFIG_FILE2 = join10(SKILLVERSE_HOME, "config.json");
|
|
2410
2778
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
2411
2779
|
if (["list", "start", "install", "add", "remove", "import", "search"].includes(actionCommand.name())) {
|
|
2412
2780
|
await ensureDatabaseInitialized();
|
|
@@ -2414,14 +2782,14 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
|
2414
2782
|
});
|
|
2415
2783
|
function getRegistryUrl() {
|
|
2416
2784
|
if (existsSync10(CONFIG_FILE2)) {
|
|
2417
|
-
const config3 = JSON.parse(
|
|
2785
|
+
const config3 = JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8"));
|
|
2418
2786
|
return config3.registryUrl || "http://localhost:4000";
|
|
2419
2787
|
}
|
|
2420
2788
|
return process.env.SKILLVERSE_REGISTRY || "http://localhost:4000";
|
|
2421
2789
|
}
|
|
2422
2790
|
function getAuthToken() {
|
|
2423
2791
|
if (existsSync10(AUTH_FILE)) {
|
|
2424
|
-
const auth = JSON.parse(
|
|
2792
|
+
const auth = JSON.parse(readFileSync4(AUTH_FILE, "utf-8"));
|
|
2425
2793
|
return auth.token || null;
|
|
2426
2794
|
}
|
|
2427
2795
|
return null;
|
|
@@ -2533,7 +2901,7 @@ program.command("publish [path]").description("Publish a skill to the Registry")
|
|
|
2533
2901
|
const form = new FormData();
|
|
2534
2902
|
form.append("name", skillName);
|
|
2535
2903
|
if (options.description) form.append("description", options.description);
|
|
2536
|
-
form.append("bundle",
|
|
2904
|
+
form.append("bundle", readFileSync4(bundlePath), {
|
|
2537
2905
|
filename: `${skillName}.tar.gz`,
|
|
2538
2906
|
contentType: "application/gzip"
|
|
2539
2907
|
});
|
|
@@ -2592,7 +2960,7 @@ program.command("search <query>").description("Search for skills in the Registry
|
|
|
2592
2960
|
}
|
|
2593
2961
|
});
|
|
2594
2962
|
program.command("config").description("Configure SkillVerse settings").option("-r, --registry <url>", "Set default registry URL").action((options) => {
|
|
2595
|
-
const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(
|
|
2963
|
+
const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8")) : {};
|
|
2596
2964
|
if (options.registry) {
|
|
2597
2965
|
config3.registryUrl = options.registry;
|
|
2598
2966
|
writeFileSync2(CONFIG_FILE2, JSON.stringify(config3, null, 2));
|
|
@@ -2645,7 +3013,7 @@ program.command("install [gitUrl]").description("Install a skill from Git URL").
|
|
|
2645
3013
|
}
|
|
2646
3014
|
});
|
|
2647
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) => {
|
|
2648
|
-
const sourcePath = options.path.startsWith("/") ? options.path :
|
|
3016
|
+
const sourcePath = options.path.startsWith("/") ? options.path : join10(process.cwd(), options.path);
|
|
2649
3017
|
if (!existsSync10(sourcePath)) {
|
|
2650
3018
|
console.error(`\u274C Source path not found: ${sourcePath}`);
|
|
2651
3019
|
process.exit(1);
|