skill-base 2.0.16 → 2.0.18
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/README.md +8 -2
- package/bin/skill-base.js +73 -28
- package/package.json +2 -2
- package/src/cappy.js +162 -50
- package/src/database.js +17 -17
- package/src/index.js +75 -22
- package/src/middleware/admin.js +3 -3
- package/src/middleware/auth.js +22 -22
- package/src/middleware/error.js +4 -4
- package/src/models/skill.js +6 -6
- package/src/models/user.js +10 -10
- package/src/models/version.js +6 -6
- package/src/routes/auth.js +17 -17
- package/src/routes/collaborators.js +28 -28
- package/src/routes/init.js +7 -7
- package/src/routes/publish.js +15 -15
- package/src/routes/skills.js +13 -13
- package/src/routes/users.js +9 -9
- package/src/utils/crypto.js +6 -6
- package/src/utils/detect-language.js +56 -0
- package/src/utils/permission.js +7 -7
- package/src/utils/zip.js +6 -6
- package/static/assets/{index-BHB0vddE.js → index-BVgsNsqr.js} +43 -43
- package/static/assets/index-ByONPaqz.css +1 -0
- package/static/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/static/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/static/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/static/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/static/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
- package/static/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
- package/static/assets/inter-cyrillic-700-normal-CjBOestx.woff2 +0 -0
- package/static/assets/inter-cyrillic-700-normal-DrXBdSj3.woff +0 -0
- package/static/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/static/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/static/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/static/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/static/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
- package/static/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
- package/static/assets/inter-cyrillic-ext-700-normal-BjwYoWNd.woff2 +0 -0
- package/static/assets/inter-cyrillic-ext-700-normal-LO58E6JB.woff +0 -0
- package/static/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/static/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/static/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/static/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/static/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
- package/static/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
- package/static/assets/inter-greek-700-normal-BUv2fZ6O.woff +0 -0
- package/static/assets/inter-greek-700-normal-C3JjAnD8.woff2 +0 -0
- package/static/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/static/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/static/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/static/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/static/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
- package/static/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
- package/static/assets/inter-greek-ext-700-normal-BoQ6DsYi.woff +0 -0
- package/static/assets/inter-greek-ext-700-normal-qfdV9bQt.woff2 +0 -0
- package/static/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/static/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/static/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/static/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/static/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/static/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/static/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
- package/static/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
- package/static/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/static/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/static/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/static/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/static/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
- package/static/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
- package/static/assets/inter-latin-ext-700-normal-Ca8adRJv.woff2 +0 -0
- package/static/assets/inter-latin-ext-700-normal-TidjK2hL.woff +0 -0
- package/static/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/static/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/static/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/static/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/static/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
- package/static/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
- package/static/assets/inter-vietnamese-700-normal-BZaoP0fm.woff +0 -0
- package/static/assets/inter-vietnamese-700-normal-DlLaEgI2.woff2 +0 -0
- package/static/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/static/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/static/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/static/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/static/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
- package/static/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
- package/static/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/static/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/static/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/static/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/static/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
- package/static/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
- package/static/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/static/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/static/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-700-normal-D3wTyLJW.woff +0 -0
- package/static/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/static/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/static/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
- package/static/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
- package/static/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/static/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/static/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
- package/static/index.html +2 -5
- package/static/assets/index-EVWfLxoq.css +0 -1
package/src/utils/crypto.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
const bcrypt = require('bcryptjs');
|
|
2
2
|
const { v4: uuidv4 } = require('uuid');
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Password hash (10 rounds)
|
|
5
5
|
function hashPassword(password) {
|
|
6
6
|
return bcrypt.hashSync(password, 10);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Verify password
|
|
10
10
|
function verifyPassword(password, hash) {
|
|
11
11
|
return bcrypt.compareSync(password, hash);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Generate PAT Token, format: sk-base-{uuid without dashes}
|
|
15
15
|
function generatePAT() {
|
|
16
16
|
return 'sk-base-' + uuidv4().replace(/-/g, '');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// Generate CLI verification code, format: XXXX-XXXX (uppercase letters + numbers)
|
|
20
20
|
function generateCliCode() {
|
|
21
|
-
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; //
|
|
21
|
+
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Exclude confusing characters
|
|
22
22
|
let code = '';
|
|
23
23
|
for (let i = 0; i < 8; i++) {
|
|
24
24
|
if (i === 4) code += '-';
|
|
@@ -27,7 +27,7 @@ function generateCliCode() {
|
|
|
27
27
|
return code;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Generate Session ID
|
|
31
31
|
function generateSessionId() {
|
|
32
32
|
return uuidv4();
|
|
33
33
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For Cappy / CLI: zh vs en.
|
|
3
|
+
* Order: explicit locale env vars → (macOS only) system region → Intl.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
function parseLocaleTag(raw) {
|
|
9
|
+
if (!raw || typeof raw !== 'string') return null;
|
|
10
|
+
const first = raw.trim().split(':')[0].split('.')[0].split('@')[0];
|
|
11
|
+
if (!first || first === 'C' || first === 'POSIX') return null;
|
|
12
|
+
const norm = first.toLowerCase().replace(/_/g, '-');
|
|
13
|
+
if (norm.startsWith('zh')) return 'zh';
|
|
14
|
+
if (norm.startsWith('en')) return 'en';
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Terminals often set LANG=C.UTF-8; Intl resolves to en-US, but the OS UI language may still be Chinese.
|
|
20
|
+
* AppleLocale matches System Settings and needs no extra npm packages.
|
|
21
|
+
*/
|
|
22
|
+
function detectDarwinLocalePreference() {
|
|
23
|
+
if (process.platform !== 'darwin') return null;
|
|
24
|
+
try {
|
|
25
|
+
const out = execFileSync('/usr/bin/defaults', ['read', '-g', 'AppleLocale'], {
|
|
26
|
+
encoding: 'utf8',
|
|
27
|
+
maxBuffer: 256,
|
|
28
|
+
timeout: 3000
|
|
29
|
+
});
|
|
30
|
+
return parseLocaleTag(out);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function detectSystemLanguage() {
|
|
37
|
+
const envKeys = ['LC_ALL', 'LC_MESSAGES', 'LANG', 'LANGUAGE'];
|
|
38
|
+
for (const k of envKeys) {
|
|
39
|
+
const v = process.env[k];
|
|
40
|
+
if (!v) continue;
|
|
41
|
+
const tag = parseLocaleTag(v);
|
|
42
|
+
if (tag) return tag;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const fromDarwin = detectDarwinLocalePreference();
|
|
46
|
+
if (fromDarwin) return fromDarwin;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale || '';
|
|
50
|
+
return locale.toLowerCase().startsWith('zh') ? 'zh' : 'en';
|
|
51
|
+
} catch {
|
|
52
|
+
return 'en';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { detectSystemLanguage };
|
package/src/utils/permission.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const db = require('../database');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {object} user - request.user
|
|
4
|
+
* Check if user has specified permission for a Skill
|
|
5
|
+
* @param {object} user - request.user object
|
|
6
6
|
* @param {string} skillId - Skill ID
|
|
7
7
|
* @param {string} requiredRole - 'owner' | 'collaborator' | 'any'
|
|
8
8
|
* @returns {boolean}
|
|
9
9
|
*/
|
|
10
10
|
function hasSkillPermission(user, skillId, requiredRole = 'any') {
|
|
11
|
-
//
|
|
11
|
+
// Admin has all permissions
|
|
12
12
|
if (user.role === 'admin') return true;
|
|
13
13
|
|
|
14
14
|
const collaborator = db.prepare(
|
|
@@ -18,21 +18,21 @@ function hasSkillPermission(user, skillId, requiredRole = 'any') {
|
|
|
18
18
|
if (!collaborator) return false;
|
|
19
19
|
if (requiredRole === 'any') return true;
|
|
20
20
|
if (requiredRole === 'owner') return collaborator.role === 'owner';
|
|
21
|
-
if (requiredRole === 'collaborator') return true; // owner
|
|
21
|
+
if (requiredRole === 'collaborator') return true; // owner also has collaborator permission
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Check if user can publish Skill
|
|
27
27
|
*/
|
|
28
28
|
function canPublishSkill(user, skillId) {
|
|
29
29
|
const skill = db.prepare('SELECT id FROM skills WHERE id = ?').get(skillId);
|
|
30
|
-
if (!skill) return true; //
|
|
30
|
+
if (!skill) return true; // New Skill, any logged-in user can create
|
|
31
31
|
return hasSkillPermission(user, skillId, 'any');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
* Check if user can manage collaborators / delete Skill
|
|
36
36
|
*/
|
|
37
37
|
function canManageSkill(user, skillId) {
|
|
38
38
|
return hasSkillPermission(user, skillId, 'owner');
|
package/src/utils/zip.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Get zip storage directory
|
|
5
5
|
function getDataDir() {
|
|
6
6
|
return process.env.DATA_DIR || path.join(__dirname, '../../data');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Ensure skill storage directory exists
|
|
10
10
|
function ensureSkillDir(skillId) {
|
|
11
11
|
const dir = path.join(getDataDir(), 'skills', skillId);
|
|
12
12
|
if (!fs.existsSync(dir)) {
|
|
@@ -15,19 +15,19 @@ function ensureSkillDir(skillId) {
|
|
|
15
15
|
return dir;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Generate timestamp version number vYYYYMMDD.HHMMSS
|
|
19
19
|
function generateVersionNumber() {
|
|
20
20
|
const now = new Date();
|
|
21
21
|
const pad = (n) => String(n).padStart(2, '0');
|
|
22
22
|
return `v${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}.${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Get full path of zip file
|
|
26
26
|
function getZipPath(skillId, version) {
|
|
27
27
|
return path.join(getDataDir(), 'skills', skillId, `${version}.zip`);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Resolve actual file path from zip_path in database, compatible with historical relative path format
|
|
31
31
|
function resolveZipPath(zipPath, skillId, version) {
|
|
32
32
|
if (zipPath) {
|
|
33
33
|
if (path.isAbsolute(zipPath)) {
|
|
@@ -38,7 +38,7 @@ function resolveZipPath(zipPath, skillId, version) {
|
|
|
38
38
|
return getZipPath(skillId, version);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// Get relative path of zip file (stored in database)
|
|
42
42
|
function getZipRelativePath(skillId, version) {
|
|
43
43
|
return `skills/${skillId}/${version}.zip`;
|
|
44
44
|
}
|