skillverse 0.1.4 → 0.1.6
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 +364 -61
- package/dist/bin.js.map +1 -1
- package/dist/index.js +924 -572
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/prisma/dev.db +0 -0
- package/public/assets/index-Dd6kW-3f.js +20 -0
- package/public/assets/index-li8hN2px.css +1 -0
- package/public/index.html +2 -2
- package/public/assets/index-BsYtpZSa.css +0 -1
- package/public/assets/index-Dfr_6UV8.js +0 -20
package/dist/bin.js
CHANGED
|
@@ -11,12 +11,26 @@ var __export = (target, all) => {
|
|
|
11
11
|
|
|
12
12
|
// src/config.ts
|
|
13
13
|
import { join, dirname } from "path";
|
|
14
|
-
import { existsSync, mkdirSync } from "fs";
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync } from "fs";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
16
|
import dotenv from "dotenv";
|
|
17
17
|
function setupEnvironment() {
|
|
18
18
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
19
|
-
const
|
|
19
|
+
const defaultHome = join(home, ".skillverse");
|
|
20
|
+
const bootstrapConfigPath = join(defaultHome, "config.json");
|
|
21
|
+
let skillverseHome = process.env.SKILLVERSE_HOME || defaultHome;
|
|
22
|
+
if (existsSync(bootstrapConfigPath)) {
|
|
23
|
+
try {
|
|
24
|
+
const config3 = JSON.parse(readFileSync(bootstrapConfigPath, "utf-8"));
|
|
25
|
+
if (config3.skillverseHome) {
|
|
26
|
+
skillverseHome = config3.skillverseHome;
|
|
27
|
+
process.env.SKILLVERSE_HOME = skillverseHome;
|
|
28
|
+
console.log(`Common config found. Using custom home: ${skillverseHome}`);
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.warn("Failed to read bootstrap config:", e);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
20
34
|
const dbUrl = process.env.DATABASE_URL;
|
|
21
35
|
if (!existsSync(skillverseHome)) {
|
|
22
36
|
try {
|
|
@@ -25,10 +39,14 @@ function setupEnvironment() {
|
|
|
25
39
|
console.error("Failed to create .skillverse directory:", error);
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
|
-
if (!dbUrl) {
|
|
42
|
+
if (!dbUrl || skillverseHome !== defaultHome) {
|
|
29
43
|
const dbPath = join(skillverseHome, "skillverse.db");
|
|
30
44
|
process.env.DATABASE_URL = `file:${dbPath}`;
|
|
45
|
+
console.log(`Using database at: ${dbPath}`);
|
|
31
46
|
}
|
|
47
|
+
process.env.SKILLS_DIR = join(skillverseHome, "skills");
|
|
48
|
+
process.env.MARKETPLACE_DIR = join(skillverseHome, "marketplace");
|
|
49
|
+
process.env.TEMP_DIR = join(skillverseHome, "temp");
|
|
32
50
|
return {
|
|
33
51
|
skillverseHome,
|
|
34
52
|
databaseUrl: process.env.DATABASE_URL
|
|
@@ -47,16 +65,25 @@ var init_config = __esm({
|
|
|
47
65
|
|
|
48
66
|
// src/lib/db.ts
|
|
49
67
|
import { PrismaClient } from "@prisma/client";
|
|
50
|
-
var globalForPrisma, prisma;
|
|
68
|
+
var config2, globalForPrisma, currentDbUrl, prisma;
|
|
51
69
|
var init_db = __esm({
|
|
52
70
|
"src/lib/db.ts"() {
|
|
53
71
|
"use strict";
|
|
54
72
|
init_config();
|
|
73
|
+
config2 = setupEnvironment();
|
|
55
74
|
globalForPrisma = globalThis;
|
|
75
|
+
currentDbUrl = process.env.DATABASE_URL;
|
|
76
|
+
console.log(`\u{1F4CA} DB connection target: ${currentDbUrl}`);
|
|
77
|
+
if (globalForPrisma.prisma && globalForPrisma.lastDbUrl !== currentDbUrl) {
|
|
78
|
+
console.log(`\u{1F504} DATABASE_URL changed from ${globalForPrisma.lastDbUrl} to ${currentDbUrl}, reconnecting...`);
|
|
79
|
+
globalForPrisma.prisma.$disconnect().catch(console.error);
|
|
80
|
+
globalForPrisma.prisma = void 0;
|
|
81
|
+
}
|
|
56
82
|
prisma = globalForPrisma.prisma ?? new PrismaClient({
|
|
57
83
|
log: process.env.NODE_ENV === "development" ? ["error", "warn"] : ["error"]
|
|
58
84
|
});
|
|
59
|
-
|
|
85
|
+
globalForPrisma.prisma = prisma;
|
|
86
|
+
globalForPrisma.lastDbUrl = currentDbUrl;
|
|
60
87
|
}
|
|
61
88
|
});
|
|
62
89
|
|
|
@@ -211,6 +238,11 @@ var init_errorHandler = __esm({
|
|
|
211
238
|
});
|
|
212
239
|
|
|
213
240
|
// src/services/skillService.ts
|
|
241
|
+
var skillService_exports = {};
|
|
242
|
+
__export(skillService_exports, {
|
|
243
|
+
SkillService: () => SkillService,
|
|
244
|
+
skillService: () => skillService
|
|
245
|
+
});
|
|
214
246
|
import { join as join3, basename } from "path";
|
|
215
247
|
import { existsSync as existsSync3 } from "fs";
|
|
216
248
|
import { mkdir, rm, cp, readFile, unlink } from "fs/promises";
|
|
@@ -231,16 +263,21 @@ function parseGitUrl(url) {
|
|
|
231
263
|
const skillName = pathParts[pathParts.length - 1];
|
|
232
264
|
return { repoUrl, subdir, skillName };
|
|
233
265
|
}
|
|
234
|
-
var
|
|
266
|
+
var getPaths, SkillService, skillService;
|
|
235
267
|
var init_skillService = __esm({
|
|
236
268
|
"src/services/skillService.ts"() {
|
|
237
269
|
"use strict";
|
|
238
270
|
init_db();
|
|
239
271
|
init_errorHandler();
|
|
240
272
|
init_dist();
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
273
|
+
getPaths = () => {
|
|
274
|
+
const home = process.env.SKILLVERSE_HOME || join3(process.env.HOME || "", ".skillverse");
|
|
275
|
+
return {
|
|
276
|
+
home,
|
|
277
|
+
skills: process.env.SKILLS_DIR || join3(home, "skills"),
|
|
278
|
+
temp: process.env.TEMP_DIR || join3(home, "temp")
|
|
279
|
+
};
|
|
280
|
+
};
|
|
244
281
|
SkillService = class {
|
|
245
282
|
async getAllSkills() {
|
|
246
283
|
const skills = await prisma.skill.findMany({
|
|
@@ -334,13 +371,14 @@ var init_skillService = __esm({
|
|
|
334
371
|
if (existingSkill) {
|
|
335
372
|
throw new AppError(ErrorCode.ALREADY_EXISTS, `Skill "${skillName}" already exists`, 409);
|
|
336
373
|
}
|
|
337
|
-
const
|
|
374
|
+
const { skills: skillsDir, temp: tempDir } = getPaths();
|
|
375
|
+
const skillPath = join3(skillsDir, skillName);
|
|
338
376
|
if (existsSync3(skillPath)) {
|
|
339
377
|
throw new AppError(ErrorCode.ALREADY_EXISTS, `Skill directory "${skillName}" already exists`, 409);
|
|
340
378
|
}
|
|
341
379
|
let commitHash = "";
|
|
342
380
|
if (subdir) {
|
|
343
|
-
tempPath = join3(
|
|
381
|
+
tempPath = join3(tempDir, `git-clone-${Date.now()}`);
|
|
344
382
|
await mkdir(tempPath, { recursive: true });
|
|
345
383
|
console.log(`Cloning ${repoUrl} to temp path for extraction...`);
|
|
346
384
|
const git = simpleGit();
|
|
@@ -411,8 +449,9 @@ var init_skillService = __esm({
|
|
|
411
449
|
if (!existsSync3(path)) {
|
|
412
450
|
throw new AppError(ErrorCode.FILE_SYSTEM_ERROR, `Source path not found: ${path}`, 400);
|
|
413
451
|
}
|
|
452
|
+
const { skills: skillsDir } = getPaths();
|
|
414
453
|
const skillName = name || basename(path);
|
|
415
|
-
const skillPath = join3(
|
|
454
|
+
const skillPath = join3(skillsDir, skillName);
|
|
416
455
|
const existingSkill = await prisma.skill.findUnique({
|
|
417
456
|
where: { name: skillName }
|
|
418
457
|
});
|
|
@@ -467,7 +506,8 @@ var init_skillService = __esm({
|
|
|
467
506
|
}
|
|
468
507
|
}
|
|
469
508
|
async createSkillFromLocal(name, zipPath, description) {
|
|
470
|
-
const
|
|
509
|
+
const { skills: skillsDir, temp: tempDir } = getPaths();
|
|
510
|
+
const skillPath = join3(skillsDir, name);
|
|
471
511
|
const existingSkill = await prisma.skill.findUnique({
|
|
472
512
|
where: { name }
|
|
473
513
|
});
|
|
@@ -477,7 +517,7 @@ var init_skillService = __esm({
|
|
|
477
517
|
if (existsSync3(skillPath)) {
|
|
478
518
|
throw new AppError(ErrorCode.ALREADY_EXISTS, `Skill directory "${name}" already exists`, 409);
|
|
479
519
|
}
|
|
480
|
-
const extractPath = join3(
|
|
520
|
+
const extractPath = join3(tempDir, `extract-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
|
|
481
521
|
await mkdir(extractPath, { recursive: true });
|
|
482
522
|
try {
|
|
483
523
|
const zip = new AdmZip(zipPath);
|
|
@@ -523,8 +563,9 @@ var init_skillService = __esm({
|
|
|
523
563
|
name = `${originalName}-${counter}`;
|
|
524
564
|
counter++;
|
|
525
565
|
}
|
|
526
|
-
const
|
|
527
|
-
const
|
|
566
|
+
const { skills: skillsDir, temp: tempDir } = getPaths();
|
|
567
|
+
const skillPath = join3(skillsDir, name);
|
|
568
|
+
const extractPath = join3(tempDir, `extract-bundle-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
|
|
528
569
|
await mkdir(extractPath, { recursive: true });
|
|
529
570
|
try {
|
|
530
571
|
const { createReadStream: createReadStream2 } = await import("fs");
|
|
@@ -617,7 +658,8 @@ var init_skillService = __esm({
|
|
|
617
658
|
message: "Not a git skill or missing repo URL"
|
|
618
659
|
};
|
|
619
660
|
}
|
|
620
|
-
const
|
|
661
|
+
const { temp: tempDir } = getPaths();
|
|
662
|
+
const tempPath = join3(tempDir, `check-update-${Date.now()}`);
|
|
621
663
|
await mkdir(tempPath, { recursive: true });
|
|
622
664
|
try {
|
|
623
665
|
const git = simpleGit();
|
|
@@ -690,7 +732,8 @@ var init_skillService = __esm({
|
|
|
690
732
|
let tempSkillPath = null;
|
|
691
733
|
try {
|
|
692
734
|
const { repoUrl, subdir } = parseGitUrl(skill.sourceUrl);
|
|
693
|
-
|
|
735
|
+
const { temp: tempDir } = getPaths();
|
|
736
|
+
tempPath = join3(tempDir, `upgrade-${Date.now()}`);
|
|
694
737
|
await mkdir(tempPath, { recursive: true });
|
|
695
738
|
const git = simpleGit();
|
|
696
739
|
console.log(`Cloning ${repoUrl} to temp for upgrade...`);
|
|
@@ -742,6 +785,99 @@ var init_skillService = __esm({
|
|
|
742
785
|
});
|
|
743
786
|
}
|
|
744
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
|
+
}
|
|
814
|
+
async scanForSkills() {
|
|
815
|
+
const { skills: skillsDir } = getPaths();
|
|
816
|
+
console.log(`Scanning for skills in ${skillsDir}...`);
|
|
817
|
+
if (!existsSync3(skillsDir)) {
|
|
818
|
+
console.log("Skills directory does not exist, skipping scan.");
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const entries = await import("fs/promises").then((fs) => fs.readdir(skillsDir, { withFileTypes: true }));
|
|
822
|
+
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
823
|
+
console.log(`Found ${directories.length} directories in skills folder.`);
|
|
824
|
+
let importedCount = 0;
|
|
825
|
+
for (const dirName of directories) {
|
|
826
|
+
const skillPath = join3(skillsDir, dirName);
|
|
827
|
+
const existingSkill = await prisma.skill.findUnique({
|
|
828
|
+
where: { name: dirName }
|
|
829
|
+
});
|
|
830
|
+
if (existingSkill) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const hasSkillMd = existsSync3(join3(skillPath, "SKILL.md"));
|
|
834
|
+
const hasPackageJson = existsSync3(join3(skillPath, "package.json"));
|
|
835
|
+
const hasSkillJson = existsSync3(join3(skillPath, "skill.json"));
|
|
836
|
+
if (!hasSkillMd && !hasPackageJson && !hasSkillJson) {
|
|
837
|
+
console.log(`Skipping ${dirName}: No skill metadata found.`);
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
console.log(`Importing skill: ${dirName}`);
|
|
842
|
+
const parsed = await this.parseSkillMetadata(skillPath);
|
|
843
|
+
let source = "local";
|
|
844
|
+
let sourceUrl = void 0;
|
|
845
|
+
let repoUrl = void 0;
|
|
846
|
+
let commitHash = void 0;
|
|
847
|
+
if (existsSync3(join3(skillPath, ".git"))) {
|
|
848
|
+
source = "git";
|
|
849
|
+
try {
|
|
850
|
+
const git = simpleGit(skillPath);
|
|
851
|
+
const remotes = await git.getRemotes(true);
|
|
852
|
+
if (remotes.length > 0) {
|
|
853
|
+
sourceUrl = remotes[0].refs.fetch;
|
|
854
|
+
repoUrl = sourceUrl;
|
|
855
|
+
}
|
|
856
|
+
commitHash = await git.revparse(["HEAD"]);
|
|
857
|
+
} catch (e) {
|
|
858
|
+
console.warn(`Failed to read git info for ${dirName}:`, e);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
await prisma.skill.create({
|
|
862
|
+
data: {
|
|
863
|
+
name: dirName,
|
|
864
|
+
source,
|
|
865
|
+
sourceUrl,
|
|
866
|
+
repoUrl,
|
|
867
|
+
commitHash,
|
|
868
|
+
description: parsed.description,
|
|
869
|
+
storagePath: skillPath,
|
|
870
|
+
metadata: JSON.stringify(parsed.metadata)
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
importedCount++;
|
|
874
|
+
} catch (error) {
|
|
875
|
+
console.error(`Failed to import skill ${dirName}:`, error);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
console.log(`Scan complete. Imported ${importedCount} new skills.`);
|
|
879
|
+
return importedCount;
|
|
880
|
+
}
|
|
745
881
|
};
|
|
746
882
|
skillService = new SkillService();
|
|
747
883
|
}
|
|
@@ -1077,17 +1213,17 @@ ${relativeSkillsPath}/
|
|
|
1077
1213
|
*/
|
|
1078
1214
|
async migrateExistingSkills(workspaceId, skillNames) {
|
|
1079
1215
|
const workspace = await this.getWorkspaceById(workspaceId);
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
1082
|
-
if (!existsSync4(
|
|
1083
|
-
await mkdir2(
|
|
1216
|
+
const SKILLVERSE_HOME2 = process.env.SKILLVERSE_HOME || join4(homedir(), ".skillverse");
|
|
1217
|
+
const SKILLS_DIR = process.env.SKILLS_DIR || join4(SKILLVERSE_HOME2, "skills");
|
|
1218
|
+
if (!existsSync4(SKILLS_DIR)) {
|
|
1219
|
+
await mkdir2(SKILLS_DIR, { recursive: true });
|
|
1084
1220
|
}
|
|
1085
1221
|
const { rename, readFile: readFile3, cp: cp2 } = await import("fs/promises");
|
|
1086
1222
|
const migrated = [];
|
|
1087
1223
|
const errors = [];
|
|
1088
1224
|
for (const skillName of skillNames) {
|
|
1089
1225
|
const sourcePath = join4(workspace.path, skillName);
|
|
1090
|
-
const targetPath = join4(
|
|
1226
|
+
const targetPath = join4(SKILLS_DIR, skillName);
|
|
1091
1227
|
try {
|
|
1092
1228
|
if (!existsSync4(sourcePath)) {
|
|
1093
1229
|
errors.push(`${skillName}: Source path not found`);
|
|
@@ -1165,20 +1301,20 @@ import multer from "multer";
|
|
|
1165
1301
|
import { join as join5 } from "path";
|
|
1166
1302
|
import { existsSync as existsSync5 } from "fs";
|
|
1167
1303
|
import { mkdir as mkdir3, rm as rm3 } from "fs/promises";
|
|
1168
|
-
var router,
|
|
1304
|
+
var router, TEMP_DIR, storage, upload, skills_default;
|
|
1169
1305
|
var init_skills = __esm({
|
|
1170
1306
|
"src/routes/skills.ts"() {
|
|
1171
1307
|
"use strict";
|
|
1172
1308
|
init_skillService();
|
|
1173
1309
|
init_workspaceService();
|
|
1174
1310
|
router = Router();
|
|
1175
|
-
|
|
1176
|
-
if (!existsSync5(
|
|
1177
|
-
mkdir3(
|
|
1311
|
+
TEMP_DIR = process.env.TEMP_DIR || join5(process.env.HOME || "", ".skillverse", "temp");
|
|
1312
|
+
if (!existsSync5(TEMP_DIR)) {
|
|
1313
|
+
mkdir3(TEMP_DIR, { recursive: true });
|
|
1178
1314
|
}
|
|
1179
1315
|
storage = multer.diskStorage({
|
|
1180
1316
|
destination: (req, file, cb) => {
|
|
1181
|
-
cb(null,
|
|
1317
|
+
cb(null, TEMP_DIR);
|
|
1182
1318
|
},
|
|
1183
1319
|
filename: (req, file, cb) => {
|
|
1184
1320
|
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
|
@@ -1210,6 +1346,18 @@ var init_skills = __esm({
|
|
|
1210
1346
|
next(error);
|
|
1211
1347
|
}
|
|
1212
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
|
+
});
|
|
1213
1361
|
router.get("/:id", async (req, res, next) => {
|
|
1214
1362
|
try {
|
|
1215
1363
|
const skill = await skillService.getSkillById(req.params.id);
|
|
@@ -1980,6 +2128,156 @@ var init_dashboard = __esm({
|
|
|
1980
2128
|
}
|
|
1981
2129
|
});
|
|
1982
2130
|
|
|
2131
|
+
// src/routes/config.ts
|
|
2132
|
+
import { Router as Router5 } from "express";
|
|
2133
|
+
import { join as join7 } from "path";
|
|
2134
|
+
import { existsSync as existsSync8, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
2135
|
+
function readConfig() {
|
|
2136
|
+
if (existsSync8(CONFIG_FILE)) {
|
|
2137
|
+
try {
|
|
2138
|
+
return JSON.parse(readFileSync2(CONFIG_FILE, "utf-8"));
|
|
2139
|
+
} catch (e) {
|
|
2140
|
+
console.error("Failed to read config.json:", e);
|
|
2141
|
+
return {};
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
return {};
|
|
2145
|
+
}
|
|
2146
|
+
var router5, HOME, BOOTSTRAP_HOME, CONFIG_FILE, config_default;
|
|
2147
|
+
var init_config2 = __esm({
|
|
2148
|
+
"src/routes/config.ts"() {
|
|
2149
|
+
"use strict";
|
|
2150
|
+
router5 = Router5();
|
|
2151
|
+
HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
2152
|
+
BOOTSTRAP_HOME = join7(HOME, ".skillverse");
|
|
2153
|
+
CONFIG_FILE = join7(BOOTSTRAP_HOME, "config.json");
|
|
2154
|
+
router5.get("/", (req, res, next) => {
|
|
2155
|
+
try {
|
|
2156
|
+
const fileConfig = readConfig();
|
|
2157
|
+
const config3 = {
|
|
2158
|
+
// Show saved preference if available, otherwise current env/default
|
|
2159
|
+
skillverseHome: fileConfig.skillverseHome || process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME,
|
|
2160
|
+
// registryUrl is potentially in config.json already
|
|
2161
|
+
registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000"
|
|
2162
|
+
// ... add other configurable items
|
|
2163
|
+
};
|
|
2164
|
+
res.json({
|
|
2165
|
+
success: true,
|
|
2166
|
+
data: config3
|
|
2167
|
+
});
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
next(error);
|
|
2170
|
+
}
|
|
2171
|
+
});
|
|
2172
|
+
router5.post("/", async (req, res, next) => {
|
|
2173
|
+
try {
|
|
2174
|
+
const { skillverseHome, registryUrl, migrate } = req.body;
|
|
2175
|
+
const currentConfig = readConfig();
|
|
2176
|
+
const oldHome = process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME;
|
|
2177
|
+
if (migrate && skillverseHome && skillverseHome !== oldHome) {
|
|
2178
|
+
console.log(`Migrating data from ${oldHome} to ${skillverseHome}...`);
|
|
2179
|
+
const { cp: cp2 } = await import("fs/promises");
|
|
2180
|
+
if (!existsSync8(skillverseHome)) {
|
|
2181
|
+
const { mkdir: mkdir6 } = await import("fs/promises");
|
|
2182
|
+
await mkdir6(skillverseHome, { recursive: true });
|
|
2183
|
+
}
|
|
2184
|
+
const copyDir = async (srcName) => {
|
|
2185
|
+
const src = join7(oldHome, srcName);
|
|
2186
|
+
const dest = join7(skillverseHome, srcName);
|
|
2187
|
+
if (existsSync8(src)) {
|
|
2188
|
+
console.log(`Copying ${srcName}...`);
|
|
2189
|
+
await cp2(src, dest, { recursive: true, force: true });
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
await copyDir("skills");
|
|
2193
|
+
await copyDir("marketplace");
|
|
2194
|
+
const dbSrc = join7(oldHome, "skillverse.db");
|
|
2195
|
+
const dbDest = join7(skillverseHome, "skillverse.db");
|
|
2196
|
+
if (existsSync8(dbSrc)) {
|
|
2197
|
+
console.log("Copying database...");
|
|
2198
|
+
await cp2(dbSrc, dbDest, { force: true });
|
|
2199
|
+
const walSrc = dbSrc + "-wal";
|
|
2200
|
+
if (existsSync8(walSrc)) await cp2(walSrc, dbDest + "-wal", { force: true });
|
|
2201
|
+
const shmSrc = dbSrc + "-shm";
|
|
2202
|
+
if (existsSync8(shmSrc)) await cp2(shmSrc, dbDest + "-shm", { force: true });
|
|
2203
|
+
console.log("Updating skill paths in new database...");
|
|
2204
|
+
const { execSync } = await import("child_process");
|
|
2205
|
+
const oldSkillsPath = join7(oldHome, "skills");
|
|
2206
|
+
const newSkillsPath = join7(skillverseHome, "skills");
|
|
2207
|
+
const updateQuery = `UPDATE Skill SET storagePath = replace(storagePath, '${oldSkillsPath}', '${newSkillsPath}') WHERE storagePath LIKE '${oldSkillsPath}%';`;
|
|
2208
|
+
try {
|
|
2209
|
+
execSync(`sqlite3 "${dbDest}" "${updateQuery}"`, { encoding: "utf-8" });
|
|
2210
|
+
console.log("\u2705 Updated skill paths in new database");
|
|
2211
|
+
} catch (sqlErr) {
|
|
2212
|
+
console.error("Failed to update skill paths:", sqlErr);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
const newConfig = {
|
|
2217
|
+
...currentConfig,
|
|
2218
|
+
...skillverseHome && { skillverseHome },
|
|
2219
|
+
...registryUrl && { registryUrl }
|
|
2220
|
+
};
|
|
2221
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2));
|
|
2222
|
+
res.json({
|
|
2223
|
+
success: true,
|
|
2224
|
+
data: newConfig,
|
|
2225
|
+
message: "Configuration saved. Restart server for changes to take effect."
|
|
2226
|
+
});
|
|
2227
|
+
} catch (error) {
|
|
2228
|
+
next(error);
|
|
2229
|
+
}
|
|
2230
|
+
});
|
|
2231
|
+
router5.get("/version", async (req, res, next) => {
|
|
2232
|
+
try {
|
|
2233
|
+
const { dirname: dirname4, join: pathJoin } = await import("path");
|
|
2234
|
+
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
2235
|
+
const __filename4 = fileURLToPath4(import.meta.url);
|
|
2236
|
+
const __dirname4 = dirname4(__filename4);
|
|
2237
|
+
const packageJsonPath = pathJoin(__dirname4, "..", "..", "package.json");
|
|
2238
|
+
let currentVersion = "0.0.0";
|
|
2239
|
+
if (existsSync8(packageJsonPath)) {
|
|
2240
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
2241
|
+
currentVersion = pkg.version || "0.0.0";
|
|
2242
|
+
}
|
|
2243
|
+
let latestVersion = currentVersion;
|
|
2244
|
+
let hasUpdate = false;
|
|
2245
|
+
try {
|
|
2246
|
+
const response = await fetch("https://registry.npmjs.org/skillverse/latest");
|
|
2247
|
+
if (response.ok) {
|
|
2248
|
+
const data = await response.json();
|
|
2249
|
+
latestVersion = data.version || currentVersion;
|
|
2250
|
+
const parseVersion = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
|
|
2251
|
+
const current = parseVersion(currentVersion);
|
|
2252
|
+
const latest = parseVersion(latestVersion);
|
|
2253
|
+
for (let i = 0; i < 3; i++) {
|
|
2254
|
+
if (latest[i] > current[i]) {
|
|
2255
|
+
hasUpdate = true;
|
|
2256
|
+
break;
|
|
2257
|
+
} else if (latest[i] < current[i]) {
|
|
2258
|
+
break;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
} catch (fetchError) {
|
|
2263
|
+
console.warn("Failed to check npm registry for updates:", fetchError);
|
|
2264
|
+
}
|
|
2265
|
+
res.json({
|
|
2266
|
+
success: true,
|
|
2267
|
+
data: {
|
|
2268
|
+
currentVersion,
|
|
2269
|
+
latestVersion,
|
|
2270
|
+
hasUpdate
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
next(error);
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
config_default = router5;
|
|
2278
|
+
}
|
|
2279
|
+
});
|
|
2280
|
+
|
|
1983
2281
|
// src/middleware/logger.ts
|
|
1984
2282
|
function requestLogger(req, res, next) {
|
|
1985
2283
|
const start = Date.now();
|
|
@@ -2004,16 +2302,16 @@ import express from "express";
|
|
|
2004
2302
|
import cors from "cors";
|
|
2005
2303
|
import dotenv2 from "dotenv";
|
|
2006
2304
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2007
|
-
import { dirname as dirname3, join as
|
|
2305
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
2008
2306
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
2009
|
-
import { existsSync as
|
|
2307
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2010
2308
|
async function initializeStorage() {
|
|
2011
|
-
const skillverseHome = process.env.SKILLVERSE_HOME ||
|
|
2012
|
-
const skillsDir = process.env.SKILLS_DIR ||
|
|
2013
|
-
const marketplaceDir = process.env.MARKETPLACE_DIR ||
|
|
2309
|
+
const skillverseHome = process.env.SKILLVERSE_HOME || join8(process.env.HOME || "", ".skillverse");
|
|
2310
|
+
const skillsDir = process.env.SKILLS_DIR || join8(skillverseHome, "skills");
|
|
2311
|
+
const marketplaceDir = process.env.MARKETPLACE_DIR || join8(skillverseHome, "marketplace");
|
|
2014
2312
|
const dirs = [skillverseHome, skillsDir, marketplaceDir];
|
|
2015
2313
|
for (const dir of dirs) {
|
|
2016
|
-
if (!
|
|
2314
|
+
if (!existsSync9(dir)) {
|
|
2017
2315
|
await mkdir5(dir, { recursive: true });
|
|
2018
2316
|
console.log(`Created directory: ${dir}`);
|
|
2019
2317
|
}
|
|
@@ -2023,10 +2321,13 @@ async function startServer(port = 3001) {
|
|
|
2023
2321
|
try {
|
|
2024
2322
|
await initializeStorage();
|
|
2025
2323
|
await ensureDatabaseInitialized();
|
|
2324
|
+
console.log("\u{1F50D} Syncing skills with filesystem...");
|
|
2325
|
+
const { skillService: skillService2 } = await Promise.resolve().then(() => (init_skillService(), skillService_exports));
|
|
2326
|
+
await skillService2.syncSkillsWithFileSystem();
|
|
2026
2327
|
return new Promise((resolve) => {
|
|
2027
2328
|
app.listen(port, () => {
|
|
2028
2329
|
console.log(`\u{1F680} SkillVerse server running on http://localhost:${port}`);
|
|
2029
|
-
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME ||
|
|
2330
|
+
console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME || join8(process.env.HOME || "", ".skillverse")}`);
|
|
2030
2331
|
resolve();
|
|
2031
2332
|
});
|
|
2032
2333
|
});
|
|
@@ -2043,6 +2344,7 @@ var init_index = __esm({
|
|
|
2043
2344
|
init_workspaces();
|
|
2044
2345
|
init_marketplace();
|
|
2045
2346
|
init_dashboard();
|
|
2347
|
+
init_config2();
|
|
2046
2348
|
init_errorHandler();
|
|
2047
2349
|
init_logger();
|
|
2048
2350
|
init_initDb();
|
|
@@ -2056,16 +2358,16 @@ var init_index = __esm({
|
|
|
2056
2358
|
app.use(express.urlencoded({ extended: true }));
|
|
2057
2359
|
app.use(requestLogger);
|
|
2058
2360
|
possiblePublicDirs = [
|
|
2059
|
-
|
|
2361
|
+
join8(__dirname3, "../public"),
|
|
2060
2362
|
// Production: dist/index.js -> public
|
|
2061
|
-
|
|
2363
|
+
join8(__dirname3, "../../public"),
|
|
2062
2364
|
// Dev tsx might resolve here
|
|
2063
|
-
|
|
2365
|
+
join8(process.cwd(), "public"),
|
|
2064
2366
|
// Fallback: relative to cwd
|
|
2065
|
-
|
|
2367
|
+
join8(process.cwd(), "server/public")
|
|
2066
2368
|
// Fallback: from root
|
|
2067
2369
|
];
|
|
2068
|
-
publicDir = possiblePublicDirs.find((dir) =>
|
|
2370
|
+
publicDir = possiblePublicDirs.find((dir) => existsSync9(dir));
|
|
2069
2371
|
if (publicDir) {
|
|
2070
2372
|
app.use(express.static(publicDir));
|
|
2071
2373
|
console.log(`\u{1F4C2} Serving static files from: ${publicDir}`);
|
|
@@ -2076,12 +2378,13 @@ var init_index = __esm({
|
|
|
2076
2378
|
app.use("/api/workspaces", workspaces_default);
|
|
2077
2379
|
app.use("/api/marketplace", marketplace_default);
|
|
2078
2380
|
app.use("/api/dashboard", dashboard_default);
|
|
2381
|
+
app.use("/api/config", config_default);
|
|
2079
2382
|
app.get("/health", (req, res) => {
|
|
2080
2383
|
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2081
2384
|
});
|
|
2082
|
-
if (publicDir &&
|
|
2385
|
+
if (publicDir && existsSync9(publicDir)) {
|
|
2083
2386
|
app.get("*", (req, res) => {
|
|
2084
|
-
res.sendFile(
|
|
2387
|
+
res.sendFile(join8(publicDir, "index.html"));
|
|
2085
2388
|
});
|
|
2086
2389
|
}
|
|
2087
2390
|
app.use(errorHandler);
|
|
@@ -2098,33 +2401,33 @@ init_skillService();
|
|
|
2098
2401
|
init_workspaceService();
|
|
2099
2402
|
import { program } from "commander";
|
|
2100
2403
|
import open from "open";
|
|
2101
|
-
import { existsSync as
|
|
2102
|
-
import { join as
|
|
2404
|
+
import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
2405
|
+
import { join as join9 } from "path";
|
|
2103
2406
|
import readline from "readline";
|
|
2104
|
-
var { skillverseHome:
|
|
2105
|
-
var AUTH_FILE =
|
|
2106
|
-
var
|
|
2407
|
+
var { skillverseHome: SKILLVERSE_HOME } = config;
|
|
2408
|
+
var AUTH_FILE = join9(SKILLVERSE_HOME, "auth.json");
|
|
2409
|
+
var CONFIG_FILE2 = join9(SKILLVERSE_HOME, "config.json");
|
|
2107
2410
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
2108
2411
|
if (["list", "start", "install", "add", "remove", "import", "search"].includes(actionCommand.name())) {
|
|
2109
2412
|
await ensureDatabaseInitialized();
|
|
2110
2413
|
}
|
|
2111
2414
|
});
|
|
2112
2415
|
function getRegistryUrl() {
|
|
2113
|
-
if (
|
|
2114
|
-
const
|
|
2115
|
-
return
|
|
2416
|
+
if (existsSync10(CONFIG_FILE2)) {
|
|
2417
|
+
const config3 = JSON.parse(readFileSync3(CONFIG_FILE2, "utf-8"));
|
|
2418
|
+
return config3.registryUrl || "http://localhost:4000";
|
|
2116
2419
|
}
|
|
2117
2420
|
return process.env.SKILLVERSE_REGISTRY || "http://localhost:4000";
|
|
2118
2421
|
}
|
|
2119
2422
|
function getAuthToken() {
|
|
2120
|
-
if (
|
|
2121
|
-
const auth = JSON.parse(
|
|
2423
|
+
if (existsSync10(AUTH_FILE)) {
|
|
2424
|
+
const auth = JSON.parse(readFileSync3(AUTH_FILE, "utf-8"));
|
|
2122
2425
|
return auth.token || null;
|
|
2123
2426
|
}
|
|
2124
2427
|
return null;
|
|
2125
2428
|
}
|
|
2126
2429
|
function saveAuth(token, user) {
|
|
2127
|
-
|
|
2430
|
+
writeFileSync2(AUTH_FILE, JSON.stringify({ token, user }, null, 2));
|
|
2128
2431
|
}
|
|
2129
2432
|
async function prompt(question, hidden = false) {
|
|
2130
2433
|
const rl = readline.createInterface({
|
|
@@ -2215,7 +2518,7 @@ program.command("publish [path]").description("Publish a skill to the Registry")
|
|
|
2215
2518
|
}
|
|
2216
2519
|
const targetPath = skillPath || process.cwd();
|
|
2217
2520
|
const registryUrl = options.registry || getRegistryUrl();
|
|
2218
|
-
if (!
|
|
2521
|
+
if (!existsSync10(targetPath)) {
|
|
2219
2522
|
console.error(`
|
|
2220
2523
|
\u274C Path not found: ${targetPath}`);
|
|
2221
2524
|
process.exit(1);
|
|
@@ -2230,7 +2533,7 @@ program.command("publish [path]").description("Publish a skill to the Registry")
|
|
|
2230
2533
|
const form = new FormData();
|
|
2231
2534
|
form.append("name", skillName);
|
|
2232
2535
|
if (options.description) form.append("description", options.description);
|
|
2233
|
-
form.append("bundle",
|
|
2536
|
+
form.append("bundle", readFileSync3(bundlePath), {
|
|
2234
2537
|
filename: `${skillName}.tar.gz`,
|
|
2235
2538
|
contentType: "application/gzip"
|
|
2236
2539
|
});
|
|
@@ -2289,14 +2592,14 @@ program.command("search <query>").description("Search for skills in the Registry
|
|
|
2289
2592
|
}
|
|
2290
2593
|
});
|
|
2291
2594
|
program.command("config").description("Configure SkillVerse settings").option("-r, --registry <url>", "Set default registry URL").action((options) => {
|
|
2292
|
-
const
|
|
2595
|
+
const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(readFileSync3(CONFIG_FILE2, "utf-8")) : {};
|
|
2293
2596
|
if (options.registry) {
|
|
2294
|
-
|
|
2295
|
-
|
|
2597
|
+
config3.registryUrl = options.registry;
|
|
2598
|
+
writeFileSync2(CONFIG_FILE2, JSON.stringify(config3, null, 2));
|
|
2296
2599
|
console.log(`\u2705 Registry URL set to: ${options.registry}`);
|
|
2297
2600
|
} else {
|
|
2298
2601
|
console.log("\nCurrent Configuration:");
|
|
2299
|
-
console.log(` Registry: ${
|
|
2602
|
+
console.log(` Registry: ${config3.registryUrl || getRegistryUrl()}`);
|
|
2300
2603
|
}
|
|
2301
2604
|
});
|
|
2302
2605
|
program.command("install [gitUrl]").description("Install a skill from Git URL").option("-a, --agent <agent>", "Link to agent workspace (vscode, cursor, etc.)").action(async (gitUrl, options) => {
|
|
@@ -2342,8 +2645,8 @@ program.command("install [gitUrl]").description("Install a skill from Git URL").
|
|
|
2342
2645
|
}
|
|
2343
2646
|
});
|
|
2344
2647
|
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) => {
|
|
2345
|
-
const sourcePath = options.path.startsWith("/") ? options.path :
|
|
2346
|
-
if (!
|
|
2648
|
+
const sourcePath = options.path.startsWith("/") ? options.path : join9(process.cwd(), options.path);
|
|
2649
|
+
if (!existsSync10(sourcePath)) {
|
|
2347
2650
|
console.error(`\u274C Source path not found: ${sourcePath}`);
|
|
2348
2651
|
process.exit(1);
|
|
2349
2652
|
}
|