@safeskill/cli 0.1.8 → 0.2.0
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/bin/cli.mjs +0 -0
- package/dist/cli.mjs +91 -34
- package/package.json +15 -13
package/bin/cli.mjs
CHANGED
|
File without changes
|
package/dist/cli.mjs
CHANGED
|
@@ -12,6 +12,22 @@ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpat
|
|
|
12
12
|
import { gunzipSync } from "zlib";
|
|
13
13
|
import { createHash } from "crypto";
|
|
14
14
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
15
|
+
const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
16
|
+
let version$2;
|
|
17
|
+
try {
|
|
18
|
+
version$2 = JSON.parse(readFileSync(packageJsonPath, "utf-8")).version || "0.0.0";
|
|
19
|
+
} catch {
|
|
20
|
+
version$2 = "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
function getUserAgent() {
|
|
23
|
+
return `safeskill-cli/${version$2}`;
|
|
24
|
+
}
|
|
25
|
+
function getHeaders(additionalHeaders = {}) {
|
|
26
|
+
return {
|
|
27
|
+
"User-Agent": getUserAgent(),
|
|
28
|
+
...additionalHeaders
|
|
29
|
+
};
|
|
30
|
+
}
|
|
15
31
|
function getOwnerRepo(parsed) {
|
|
16
32
|
if (parsed.type === "local") return null;
|
|
17
33
|
const sshMatch = parsed.url.match(/^git@[^:]+:(.+)$/);
|
|
@@ -39,7 +55,7 @@ function parseOwnerRepo(ownerRepo) {
|
|
|
39
55
|
}
|
|
40
56
|
async function isRepoPrivate(owner, repo) {
|
|
41
57
|
try {
|
|
42
|
-
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}
|
|
58
|
+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers: getHeaders() });
|
|
43
59
|
if (!res.ok) return null;
|
|
44
60
|
return (await res.json()).private === true;
|
|
45
61
|
} catch {
|
|
@@ -404,7 +420,10 @@ function withTimeout(timeoutMs) {
|
|
|
404
420
|
}
|
|
405
421
|
async function fetchText(url, timeoutMs) {
|
|
406
422
|
try {
|
|
407
|
-
const response = await fetch(url, {
|
|
423
|
+
const response = await fetch(url, {
|
|
424
|
+
signal: withTimeout(timeoutMs),
|
|
425
|
+
headers: getHeaders()
|
|
426
|
+
});
|
|
408
427
|
if (!response.ok) return null;
|
|
409
428
|
return (await response.text()).trim();
|
|
410
429
|
} catch {
|
|
@@ -460,7 +479,10 @@ async function ensureRegionAvailable(region, hubBaseUrl) {
|
|
|
460
479
|
const availabilityPromise = (async () => {
|
|
461
480
|
const statusUrl = new URL("/api/v1/status", hubBaseUrl).toString();
|
|
462
481
|
try {
|
|
463
|
-
if ((await fetch(statusUrl, {
|
|
482
|
+
if ((await fetch(statusUrl, {
|
|
483
|
+
signal: withTimeout(STATUS_TIMEOUT_MS),
|
|
484
|
+
headers: getHeaders()
|
|
485
|
+
})).ok) return;
|
|
464
486
|
} catch {}
|
|
465
487
|
const alternateRegion = region === "cn" ? "global" : "cn";
|
|
466
488
|
REGION_DOMAINS[alternateRegion];
|
|
@@ -670,13 +692,12 @@ async function extractDownloadedArtifact(destination, fileName, bytes) {
|
|
|
670
692
|
}
|
|
671
693
|
async function searchSkillsCatalog(query, limit = 10) {
|
|
672
694
|
try {
|
|
673
|
-
const
|
|
674
|
-
const searchBaseUrl = await getSafeSkillSearchUrlWithHub(hubBaseUrl);
|
|
695
|
+
const searchBaseUrl = await getSafeSkillSearchUrlWithHub(await getSafeSkillHubBaseUrl());
|
|
675
696
|
const searchUrl = new URL(searchBaseUrl);
|
|
676
697
|
searchUrl.searchParams.set("query", query);
|
|
677
698
|
searchUrl.searchParams.set("page", "1");
|
|
678
699
|
searchUrl.searchParams.set("page_size", String(limit));
|
|
679
|
-
const response = await fetch(searchUrl.toString());
|
|
700
|
+
const response = await fetch(searchUrl.toString(), { headers: getHeaders() });
|
|
680
701
|
if (!response.ok) return [];
|
|
681
702
|
const payload = await response.json();
|
|
682
703
|
const data = "data" in payload ? payload.data : null;
|
|
@@ -684,11 +705,15 @@ async function searchSkillsCatalog(query, limit = 10) {
|
|
|
684
705
|
return data.items.map((item) => ({
|
|
685
706
|
name: item.name,
|
|
686
707
|
label: item.display_name || item.name,
|
|
687
|
-
description: item.description,
|
|
708
|
+
description: item.summary || item.description,
|
|
688
709
|
slug: `${item.namespace}/${item.name}`,
|
|
689
710
|
source: buildHubSource(item.namespace, item.name, item.latest_version),
|
|
690
|
-
installs: 0,
|
|
691
|
-
|
|
711
|
+
installs: item.stats?.total_installs ?? 0,
|
|
712
|
+
totalStars: item.stats?.total_stars ?? 0,
|
|
713
|
+
trustScore: item.trust_score,
|
|
714
|
+
updatedAt: item.updated_at,
|
|
715
|
+
sha256: item.latest_sha256,
|
|
716
|
+
detailUrl: item.latest_sha256 ? `https://safeskill.cn/report/${item.latest_sha256}` : void 0
|
|
692
717
|
}));
|
|
693
718
|
} catch {
|
|
694
719
|
return [];
|
|
@@ -698,7 +723,7 @@ async function resolveHubSkill(source, agent) {
|
|
|
698
723
|
const baseUrl = await getSafeSkillHubBaseUrl();
|
|
699
724
|
const response = await fetch(buildApiUrl(baseUrl, "/resolve"), {
|
|
700
725
|
method: "POST",
|
|
701
|
-
headers: { "content-type": "application/json" },
|
|
726
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
702
727
|
body: JSON.stringify({
|
|
703
728
|
source,
|
|
704
729
|
...agent ? { agent } : {}
|
|
@@ -714,13 +739,19 @@ async function downloadHubSource(source, agent) {
|
|
|
714
739
|
if (!downloadUrl) throw new Error("hub resolve did not return a downloadable artifact");
|
|
715
740
|
let response;
|
|
716
741
|
try {
|
|
717
|
-
response = await fetch(downloadUrl, {
|
|
742
|
+
response = await fetch(downloadUrl, {
|
|
743
|
+
redirect: "follow",
|
|
744
|
+
headers: getHeaders()
|
|
745
|
+
});
|
|
718
746
|
if (!response.ok && response.status === 502 && resolved.artifact_digest) {
|
|
719
747
|
console.warn(`Primary download URL failed with 502, trying fallback...`);
|
|
720
748
|
const fallbackUrl = buildFallbackDownloadUrl(resolved.artifact_digest);
|
|
721
749
|
if (fallbackUrl && fallbackUrl !== downloadUrl) {
|
|
722
750
|
console.warn(`Trying fallback URL: ${fallbackUrl}`);
|
|
723
|
-
const fallbackResponse = await fetch(fallbackUrl, {
|
|
751
|
+
const fallbackResponse = await fetch(fallbackUrl, {
|
|
752
|
+
redirect: "follow",
|
|
753
|
+
headers: getHeaders()
|
|
754
|
+
});
|
|
724
755
|
if (fallbackResponse.ok) response = fallbackResponse;
|
|
725
756
|
else throw new Error(`hub artifact download failed: ${response.status} (primary) and ${fallbackResponse.status} (fallback)`);
|
|
726
757
|
} else throw new Error(`hub artifact download failed: ${response.status}`);
|
|
@@ -750,7 +781,7 @@ async function checkHubUpdates(items) {
|
|
|
750
781
|
const baseUrl = await getSafeSkillHubBaseUrl();
|
|
751
782
|
const response = await fetch(buildApiUrl(baseUrl, "/check-update"), {
|
|
752
783
|
method: "POST",
|
|
753
|
-
headers: { "content-type": "application/json" },
|
|
784
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
754
785
|
body: JSON.stringify({ items })
|
|
755
786
|
});
|
|
756
787
|
if (!response.ok) throw new Error(`hub check-update failed: ${response.status}`);
|
|
@@ -1919,7 +1950,7 @@ var WellKnownProvider = class {
|
|
|
1919
1950
|
});
|
|
1920
1951
|
}
|
|
1921
1952
|
for (const { indexUrl, baseUrl: resolvedBase, wellKnownPath } of urlsToTry) try {
|
|
1922
|
-
const response = await fetch(indexUrl);
|
|
1953
|
+
const response = await fetch(indexUrl, { headers: getHeaders() });
|
|
1923
1954
|
if (!response.ok) continue;
|
|
1924
1955
|
const index = await response.json();
|
|
1925
1956
|
if (!index.skills || !Array.isArray(index.skills)) continue;
|
|
@@ -1980,7 +2011,7 @@ var WellKnownProvider = class {
|
|
|
1980
2011
|
const resolvedPath = wellKnownPath ?? this.WELL_KNOWN_PATHS[0];
|
|
1981
2012
|
const skillBaseUrl = `${baseUrl.replace(/\/$/, "")}/${resolvedPath}/${entry.name}`;
|
|
1982
2013
|
const skillMdUrl = `${skillBaseUrl}/SKILL.md`;
|
|
1983
|
-
const response = await fetch(skillMdUrl);
|
|
2014
|
+
const response = await fetch(skillMdUrl, { headers: getHeaders() });
|
|
1984
2015
|
if (!response.ok) return null;
|
|
1985
2016
|
const content = await response.text();
|
|
1986
2017
|
const { data } = (0, import_gray_matter.default)(content);
|
|
@@ -1990,7 +2021,7 @@ var WellKnownProvider = class {
|
|
|
1990
2021
|
const filePromises = entry.files.filter((f) => f.toLowerCase() !== "skill.md").map(async (filePath) => {
|
|
1991
2022
|
try {
|
|
1992
2023
|
const fileUrl = `${skillBaseUrl}/${filePath}`;
|
|
1993
|
-
const fileResponse = await fetch(fileUrl);
|
|
2024
|
+
const fileResponse = await fetch(fileUrl, { headers: getHeaders() });
|
|
1994
2025
|
if (fileResponse.ok) return {
|
|
1995
2026
|
path: filePath,
|
|
1996
2027
|
content: await fileResponse.text()
|
|
@@ -2101,10 +2132,7 @@ async function fetchSkillFolderHash(ownerRepo, skillPath, token) {
|
|
|
2101
2132
|
if (folderPath.endsWith("/")) folderPath = folderPath.slice(0, -1);
|
|
2102
2133
|
for (const branch of ["main", "master"]) try {
|
|
2103
2134
|
const url = `https://api.github.com/repos/${ownerRepo}/git/trees/${branch}?recursive=1`;
|
|
2104
|
-
const headers = {
|
|
2105
|
-
Accept: "application/vnd.github.v3+json",
|
|
2106
|
-
"User-Agent": "skills-cli"
|
|
2107
|
-
};
|
|
2135
|
+
const headers = getHeaders({ Accept: "application/vnd.github.v3+json" });
|
|
2108
2136
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
2109
2137
|
const response = await fetch(url, { headers });
|
|
2110
2138
|
if (!response.ok) continue;
|
|
@@ -2231,7 +2259,7 @@ function createEmptyLocalLock() {
|
|
|
2231
2259
|
skills: {}
|
|
2232
2260
|
};
|
|
2233
2261
|
}
|
|
2234
|
-
var version$1 = "0.
|
|
2262
|
+
var version$1 = "0.2.0";
|
|
2235
2263
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2236
2264
|
async function isSourcePrivate(source) {
|
|
2237
2265
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -3267,11 +3295,34 @@ const BOLD$2 = "\x1B[1m";
|
|
|
3267
3295
|
const DIM$2 = "\x1B[38;5;102m";
|
|
3268
3296
|
const TEXT$1 = "\x1B[38;5;145m";
|
|
3269
3297
|
const CYAN$1 = "\x1B[36m";
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
return `${count
|
|
3298
|
+
const GREEN = "\x1B[32m";
|
|
3299
|
+
const YELLOW$1 = "\x1B[33m";
|
|
3300
|
+
const RED = "\x1B[31m";
|
|
3301
|
+
function formatCount(count) {
|
|
3302
|
+
if (count >= 1e6) return `${(count / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
|
|
3303
|
+
if (count >= 1e3) return `${(count / 1e3).toFixed(1).replace(/\.0$/, "")}K`;
|
|
3304
|
+
return `${count}`;
|
|
3305
|
+
}
|
|
3306
|
+
function formatTrustScore(score) {
|
|
3307
|
+
if (score === void 0 || score === null) return "";
|
|
3308
|
+
return `${score >= 80 ? GREEN : score >= 60 ? YELLOW$1 : RED}TrustScore:${score}${RESET$2}`;
|
|
3309
|
+
}
|
|
3310
|
+
function formatRelativeDate(ts) {
|
|
3311
|
+
if (!ts) return "";
|
|
3312
|
+
const diff = Math.floor(Date.now() / 1e3) - ts;
|
|
3313
|
+
if (diff < 0) return "";
|
|
3314
|
+
const days = Math.floor(diff / 86400);
|
|
3315
|
+
if (days < 1) return "today";
|
|
3316
|
+
if (days < 30) return `${days}d ago`;
|
|
3317
|
+
const months = Math.floor(days / 30);
|
|
3318
|
+
if (months < 12) return `${months}mo ago`;
|
|
3319
|
+
return `${Math.floor(months / 12)}y ago`;
|
|
3320
|
+
}
|
|
3321
|
+
function formatStatsBadges(installs, stars) {
|
|
3322
|
+
const parts = [];
|
|
3323
|
+
if (installs > 0) parts.push(`${CYAN$1}↓ ${formatCount(installs)}${RESET$2}`);
|
|
3324
|
+
if (stars > 0) parts.push(`${YELLOW$1}★ ${formatCount(stars)}${RESET$2}`);
|
|
3325
|
+
return parts.join(" ");
|
|
3275
3326
|
}
|
|
3276
3327
|
async function searchSkillsAPI(query) {
|
|
3277
3328
|
return searchSkillsCatalog(query, 10);
|
|
@@ -3311,10 +3362,10 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3311
3362
|
const displayName = skill.label || skill.name;
|
|
3312
3363
|
const name = isSelected ? `${BOLD$2}${displayName}${RESET$2}` : `${TEXT$1}${displayName}${RESET$2}`;
|
|
3313
3364
|
const source = skill.source ? ` ${DIM$2}${skill.source}${RESET$2}` : "";
|
|
3314
|
-
const
|
|
3315
|
-
const
|
|
3365
|
+
const statsBadge = formatStatsBadges(skill.installs, skill.totalStars);
|
|
3366
|
+
const trustBadge = skill.trustScore !== void 0 ? ` ${skill.trustScore >= 80 ? GREEN : skill.trustScore >= 60 ? YELLOW$1 : RED}TrustScore:${skill.trustScore}${RESET$2}` : "";
|
|
3316
3367
|
const loadingIndicator = loading && i === 0 ? ` ${DIM$2}...${RESET$2}` : "";
|
|
3317
|
-
lines.push(` ${arrow}
|
|
3368
|
+
lines.push(` ${arrow}${trustBadge} ${name}${source}${statsBadge ? ` ${statsBadge}` : ""}${loadingIndicator}`);
|
|
3318
3369
|
}
|
|
3319
3370
|
}
|
|
3320
3371
|
lines.push("");
|
|
@@ -3432,12 +3483,18 @@ ${DIM$2} 2) npx safeskill add <owner/repo@skill>${RESET$2}`;
|
|
|
3432
3483
|
console.log();
|
|
3433
3484
|
for (const skill of results.slice(0, 6)) {
|
|
3434
3485
|
const pkg = skill.source || skill.slug;
|
|
3435
|
-
const installs = formatInstalls(skill.installs);
|
|
3436
|
-
const displayName = skill.label || skill.name;
|
|
3437
3486
|
const installRef = pkg.startsWith("safeskill://") ? pkg : `${pkg}@${skill.name}`;
|
|
3438
|
-
console.log(`${TEXT$1}${installRef}${RESET$2}
|
|
3439
|
-
|
|
3440
|
-
|
|
3487
|
+
console.log(`${TEXT$1}${installRef}${RESET$2}`);
|
|
3488
|
+
const statsBadge = formatStatsBadges(skill.installs, skill.totalStars);
|
|
3489
|
+
const badges = [
|
|
3490
|
+
formatTrustScore(skill.trustScore),
|
|
3491
|
+
statsBadge,
|
|
3492
|
+
skill.updatedAt ? `${DIM$2}${formatRelativeDate(skill.updatedAt)}${RESET$2}` : ""
|
|
3493
|
+
].filter(Boolean).join(` `);
|
|
3494
|
+
if (badges) console.log(` ${badges}`);
|
|
3495
|
+
const displayName = skill.label || skill.name;
|
|
3496
|
+
const desc = skill.description || (displayName !== skill.name ? displayName : "");
|
|
3497
|
+
if (desc) console.log(`${DIM$2} ${desc}${RESET$2}`);
|
|
3441
3498
|
if (skill.detailUrl) console.log(`${DIM$2}└ ${skill.detailUrl}${RESET$2}`);
|
|
3442
3499
|
console.log();
|
|
3443
3500
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@safeskill/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "SafeSkill CLI, for safe your skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"CHANGELOG.md",
|
|
14
14
|
"ThirdPartyNoticeText.txt"
|
|
15
15
|
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node scripts/generate-licenses.js && obuild",
|
|
18
|
+
"generate-licenses": "node scripts/generate-licenses.js",
|
|
19
|
+
"dev": "node src/cli.ts",
|
|
20
|
+
"exec:test": "node scripts/execute-tests.ts",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
23
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
24
|
+
"test": "vitest",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"publish:snapshot": "npm version prerelease --preid=snapshot --no-git-tag-version && npm publish --tag snapshot"
|
|
27
|
+
},
|
|
16
28
|
"lint-staged": {
|
|
17
29
|
"src/**/*.ts": "prettier --write",
|
|
18
30
|
"scripts/**/*.ts": "prettier --write",
|
|
@@ -95,21 +107,11 @@
|
|
|
95
107
|
"engines": {
|
|
96
108
|
"node": ">=18"
|
|
97
109
|
},
|
|
110
|
+
"packageManager": "pnpm@10.17.1",
|
|
98
111
|
"dependencies": {
|
|
99
112
|
"@types/yauzl": "^2.10.3",
|
|
100
113
|
"jschardet": "^3.1.4",
|
|
101
114
|
"node-stream-zip": "^1.15.0",
|
|
102
115
|
"yauzl": "^3.3.0"
|
|
103
|
-
},
|
|
104
|
-
"scripts": {
|
|
105
|
-
"build": "node scripts/generate-licenses.js && obuild",
|
|
106
|
-
"generate-licenses": "node scripts/generate-licenses.js",
|
|
107
|
-
"dev": "node src/cli.ts",
|
|
108
|
-
"exec:test": "node scripts/execute-tests.ts",
|
|
109
|
-
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
110
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
111
|
-
"test": "vitest",
|
|
112
|
-
"type-check": "tsc --noEmit",
|
|
113
|
-
"publish:snapshot": "npm version prerelease --preid=snapshot --no-git-tag-version && npm publish --tag snapshot"
|
|
114
116
|
}
|
|
115
|
-
}
|
|
117
|
+
}
|