ali-skills 0.0.15 → 0.0.17
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.
|
@@ -94,9 +94,32 @@ function parseSource(input) {
|
|
|
94
94
|
const githubRepoMatch = input.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
95
95
|
if (githubRepoMatch) {
|
|
96
96
|
const [, owner, repo] = githubRepoMatch;
|
|
97
|
+
const cleanRepo = repo.replace(/\.git$/, "");
|
|
98
|
+
const credentialMatch = input.match(/^(https?:\/\/)(?:([^@]+)@)?github\.com/);
|
|
99
|
+
const protocol = credentialMatch?.[1] ?? "https://";
|
|
100
|
+
const credential = credentialMatch?.[2];
|
|
97
101
|
return {
|
|
98
102
|
type: "github",
|
|
99
|
-
url:
|
|
103
|
+
url: credential ? `${protocol}${credential}@github.com/${owner}/${cleanRepo}.git` : `${protocol}github.com/${owner}/${cleanRepo}.git`
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const internalTreeWithPathMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/(.+?)(?:\/-)?\/tree\/([^/]+)\/(.+)/);
|
|
107
|
+
if (internalTreeWithPathMatch) {
|
|
108
|
+
const [, host, repoPath, ref, subpath] = internalTreeWithPathMatch;
|
|
109
|
+
if (repoPath && repoPath.includes("/")) return {
|
|
110
|
+
type: "internal",
|
|
111
|
+
url: `${host.replace("gitlab.alibaba-inc.com", "code.alibaba-inc.com")}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
112
|
+
ref,
|
|
113
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const internalTreeMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/(.+?)(?:\/-)?\/tree\/([^/]+)$/);
|
|
117
|
+
if (internalTreeMatch) {
|
|
118
|
+
const [, host, repoPath, ref] = internalTreeMatch;
|
|
119
|
+
if (repoPath && repoPath.includes("/")) return {
|
|
120
|
+
type: "internal",
|
|
121
|
+
url: `${host.replace("gitlab.alibaba-inc.com", "code.alibaba-inc.com")}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
122
|
+
ref
|
|
100
123
|
};
|
|
101
124
|
}
|
|
102
125
|
const gitlabTreeWithPathMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)\/(.+)/);
|
|
@@ -144,25 +167,6 @@ function parseSource(input) {
|
|
|
144
167
|
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
145
168
|
};
|
|
146
169
|
}
|
|
147
|
-
const internalTreeWithPathMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/(.+?)(?:\/-)?\/tree\/([^/]+)\/(.+)/);
|
|
148
|
-
if (internalTreeWithPathMatch) {
|
|
149
|
-
const [, host, repoPath, ref, subpath] = internalTreeWithPathMatch;
|
|
150
|
-
if (repoPath && repoPath.includes("/")) return {
|
|
151
|
-
type: "internal",
|
|
152
|
-
url: `${host.replace("gitlab.alibaba-inc.com", "code.alibaba-inc.com")}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
153
|
-
ref,
|
|
154
|
-
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
const internalTreeMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/(.+?)(?:\/-)?\/tree\/([^/]+)$/);
|
|
158
|
-
if (internalTreeMatch) {
|
|
159
|
-
const [, host, repoPath, ref] = internalTreeMatch;
|
|
160
|
-
if (repoPath && repoPath.includes("/")) return {
|
|
161
|
-
type: "internal",
|
|
162
|
-
url: `${host.replace("gitlab.alibaba-inc.com", "code.alibaba-inc.com")}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
163
|
-
ref
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
170
|
const internalRepoWithSubpathMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/([^/]+)\/([^/]+)\/(.+)$/);
|
|
167
171
|
if (internalRepoWithSubpathMatch) {
|
|
168
172
|
const [, host, owner, repo, subpath] = internalRepoWithSubpathMatch;
|
|
@@ -220,6 +224,27 @@ function isWellKnownUrl(input) {
|
|
|
220
224
|
return false;
|
|
221
225
|
}
|
|
222
226
|
}
|
|
227
|
+
function getDisplaySource(sourceUrl, options = {}) {
|
|
228
|
+
const format = options.format ?? "compact";
|
|
229
|
+
if (sourceUrl.startsWith("/") || sourceUrl.startsWith(".") || sourceUrl.startsWith("~")) return sourceUrl;
|
|
230
|
+
const sshMatch = sourceUrl.match(/^git@([^:]+):(.+)$/);
|
|
231
|
+
if (sshMatch) {
|
|
232
|
+
const [, host, path] = sshMatch;
|
|
233
|
+
if (!host || !path) return sourceUrl;
|
|
234
|
+
const cleanPath = path.replace(/\.git$/, "");
|
|
235
|
+
if (format === "compact" && (host === "code.alibaba-inc.com" || host === "gitlab.alibaba-inc.com")) return cleanPath;
|
|
236
|
+
return `https://${host}/${cleanPath}`;
|
|
237
|
+
}
|
|
238
|
+
if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://")) try {
|
|
239
|
+
const url = new URL(sourceUrl);
|
|
240
|
+
const cleanPath = url.pathname.replace(/\.git$/, "").replace(/^\//, "");
|
|
241
|
+
if (format === "compact" && (url.hostname === "code.alibaba-inc.com" || url.hostname === "gitlab.alibaba-inc.com")) return cleanPath;
|
|
242
|
+
return `https://${url.hostname}/${cleanPath}`;
|
|
243
|
+
} catch {
|
|
244
|
+
return sourceUrl.replace(/^https?:\/\/[^@]+@/, "https://");
|
|
245
|
+
}
|
|
246
|
+
return sourceUrl;
|
|
247
|
+
}
|
|
223
248
|
const silentOutput = new Writable({ write(_chunk, _encoding, callback) {
|
|
224
249
|
callback();
|
|
225
250
|
} });
|
|
@@ -384,6 +409,9 @@ async function searchMultiselect(options) {
|
|
|
384
409
|
});
|
|
385
410
|
}
|
|
386
411
|
const CLONE_TIMEOUT_MS = 6e4;
|
|
412
|
+
function redactSensitiveText$1(text) {
|
|
413
|
+
return text.replace(/(https?:\/\/)([^/\s:@]+)(?::([^@/\s]*))?@/gi, "$1").replace(/([?&](?:private_token|access_token|token|api_key|access_key|secret|password)=)[^&\s]+/gi, "$1***");
|
|
414
|
+
}
|
|
387
415
|
function getGitUserConfig() {
|
|
388
416
|
try {
|
|
389
417
|
const name = execSync("git config user.name", {
|
|
@@ -451,11 +479,13 @@ async function cloneRepo(url, ref) {
|
|
|
451
479
|
force: true
|
|
452
480
|
}).catch(() => {});
|
|
453
481
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
482
|
+
const safeUrl = getDisplaySource(url);
|
|
483
|
+
const safeErrorMessage = redactSensitiveText$1(errorMessage);
|
|
454
484
|
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
455
485
|
const isAuthError = errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("Permission denied") || errorMessage.includes("Repository not found") || errorMessage.includes("HTTP Basic: Access denied") || errorMessage.includes("could not be found or you don") || errorMessage.toLowerCase().includes("you don't have permission") || /repository .+ not found/i.test(errorMessage);
|
|
456
|
-
if (isTimeout) throw new GitCloneError("Clone timed out after 60s. This often happens with private repos that require authentication.\n Ensure you have access and your SSH keys or credentials are configured:\n - For SSH: ssh-add -l (to check loaded keys)\n - For HTTPS: gh auth status (if using GitHub CLI)",
|
|
457
|
-
if (isAuthError) throw new GitCloneError(`Authentication failed for ${
|
|
458
|
-
throw new GitCloneError(`Failed to clone ${
|
|
486
|
+
if (isTimeout) throw new GitCloneError("Clone timed out after 60s. This often happens with private repos that require authentication.\n Ensure you have access and your SSH keys or credentials are configured:\n - For SSH: ssh-add -l (to check loaded keys)\n - For HTTPS: gh auth status (if using GitHub CLI)", safeUrl, true, false);
|
|
487
|
+
if (isAuthError) throw new GitCloneError(`Authentication failed for ${safeUrl}.\n - For private repos, ensure you have access\n - For SSH: Check your keys with 'ssh -T git@github.com'\n - For HTTPS: Run 'gh auth login' or configure git credentials`, safeUrl, false, true);
|
|
488
|
+
throw new GitCloneError(`Failed to clone ${safeUrl}: ${safeErrorMessage}`, safeUrl, false, false);
|
|
459
489
|
}
|
|
460
490
|
}
|
|
461
491
|
async function listRemoteBranches(url) {
|
|
@@ -2056,6 +2086,25 @@ async function saveSelectedAgents(agents) {
|
|
|
2056
2086
|
lock.lastSelectedAgents = agents;
|
|
2057
2087
|
await writeSkillLock(lock);
|
|
2058
2088
|
}
|
|
2089
|
+
function sanitizeSourceUrl(url) {
|
|
2090
|
+
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
2091
|
+
if (sshMatch) {
|
|
2092
|
+
const [, host, path] = sshMatch;
|
|
2093
|
+
if (!host || !path) return url;
|
|
2094
|
+
return `https://${host}/${path.replace(/\.git$/, "")}.git`;
|
|
2095
|
+
}
|
|
2096
|
+
if (url.startsWith("http://") || url.startsWith("https://")) try {
|
|
2097
|
+
const parsed = new URL(url);
|
|
2098
|
+
parsed.username = "";
|
|
2099
|
+
parsed.password = "";
|
|
2100
|
+
let cleanUrl = parsed.toString();
|
|
2101
|
+
if (!cleanUrl.endsWith(".git")) cleanUrl += ".git";
|
|
2102
|
+
return cleanUrl;
|
|
2103
|
+
} catch {
|
|
2104
|
+
return url;
|
|
2105
|
+
}
|
|
2106
|
+
return url;
|
|
2107
|
+
}
|
|
2059
2108
|
const LOCAL_LOCK_FILE = "skills-lock.json";
|
|
2060
2109
|
const CURRENT_VERSION = 1;
|
|
2061
2110
|
function getLocalLockPath(cwd) {
|
|
@@ -2066,11 +2115,27 @@ async function readLocalLock(cwd) {
|
|
|
2066
2115
|
try {
|
|
2067
2116
|
const content = await readFile(lockPath, "utf-8");
|
|
2068
2117
|
const parsed = JSON.parse(content);
|
|
2069
|
-
if (typeof parsed.version !== "number"
|
|
2070
|
-
|
|
2071
|
-
|
|
2118
|
+
if (typeof parsed.version !== "number") return {
|
|
2119
|
+
lock: parsed,
|
|
2120
|
+
isOutdated: true
|
|
2121
|
+
};
|
|
2122
|
+
if (parsed.version < CURRENT_VERSION) return {
|
|
2123
|
+
lock: parsed,
|
|
2124
|
+
isOutdated: true
|
|
2125
|
+
};
|
|
2126
|
+
if (!parsed.skills) return {
|
|
2127
|
+
lock: createEmptyLocalLock(),
|
|
2128
|
+
isOutdated: false
|
|
2129
|
+
};
|
|
2130
|
+
return {
|
|
2131
|
+
lock: parsed,
|
|
2132
|
+
isOutdated: false
|
|
2133
|
+
};
|
|
2072
2134
|
} catch {
|
|
2073
|
-
return
|
|
2135
|
+
return {
|
|
2136
|
+
lock: createEmptyLocalLock(),
|
|
2137
|
+
isOutdated: false
|
|
2138
|
+
};
|
|
2074
2139
|
}
|
|
2075
2140
|
}
|
|
2076
2141
|
async function writeLocalLock(lock, cwd) {
|
|
@@ -2112,7 +2177,7 @@ async function collectFiles(baseDir, currentDir, results) {
|
|
|
2112
2177
|
}));
|
|
2113
2178
|
}
|
|
2114
2179
|
async function addSkillToLocalLock(skillName, entry, cwd) {
|
|
2115
|
-
const lock = await readLocalLock(cwd);
|
|
2180
|
+
const { lock } = await readLocalLock(cwd);
|
|
2116
2181
|
lock.skills[skillName] = entry;
|
|
2117
2182
|
await writeLocalLock(lock, cwd);
|
|
2118
2183
|
}
|
|
@@ -2132,8 +2197,11 @@ function logSkillParseFailures(failures) {
|
|
|
2132
2197
|
if (f.hint) M.message(import_picocolors.default.yellow(` 提示: ${f.hint}`));
|
|
2133
2198
|
}
|
|
2134
2199
|
}
|
|
2135
|
-
var version$1 = "2.0.
|
|
2200
|
+
var version$1 = "2.0.17";
|
|
2136
2201
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2202
|
+
function redactSensitiveText(text) {
|
|
2203
|
+
return text.replace(/(https?:\/\/)([^/\s:@]+)(?::([^@/\s]*))?@/gi, "$1").replace(/([?&](?:private_token|access_token|token|api_key|access_key|secret|password)=)[^&\s]+/gi, "$1***");
|
|
2204
|
+
}
|
|
2137
2205
|
async function isSourcePrivate(source) {
|
|
2138
2206
|
const ownerRepo = parseOwnerRepo(source);
|
|
2139
2207
|
if (!ownerRepo) return false;
|
|
@@ -2386,7 +2454,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2386
2454
|
value: key,
|
|
2387
2455
|
label: config.displayName
|
|
2388
2456
|
})));
|
|
2389
|
-
if (
|
|
2457
|
+
if (isCancelled$1(selected)) {
|
|
2390
2458
|
xe("Installation cancelled");
|
|
2391
2459
|
process.exit(0);
|
|
2392
2460
|
}
|
|
@@ -2400,7 +2468,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2400
2468
|
} else M.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
2401
2469
|
} else {
|
|
2402
2470
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2403
|
-
if (
|
|
2471
|
+
if (isCancelled$1(selected)) {
|
|
2404
2472
|
xe("Installation cancelled");
|
|
2405
2473
|
process.exit(0);
|
|
2406
2474
|
}
|
|
@@ -2524,8 +2592,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2524
2592
|
await addSkillToLock(skill.installName, {
|
|
2525
2593
|
source: sourceIdentifier,
|
|
2526
2594
|
sourceType: "well-known",
|
|
2527
|
-
sourceUrl: skill.sourceUrl
|
|
2528
|
-
skillFolderHash: ""
|
|
2595
|
+
sourceUrl: sanitizeSourceUrl(skill.sourceUrl)
|
|
2529
2596
|
});
|
|
2530
2597
|
} catch {}
|
|
2531
2598
|
}
|
|
@@ -2653,7 +2720,8 @@ async function runAdd(args, options = {}) {
|
|
|
2653
2720
|
}
|
|
2654
2721
|
} catch {}
|
|
2655
2722
|
}
|
|
2656
|
-
|
|
2723
|
+
const displaySource = parsed.type === "local" ? parsed.localPath : getDisplaySource(parsed.url, { format: "absolute" });
|
|
2724
|
+
spinner.stop(`Source: ${displaySource}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
|
|
2657
2725
|
if (parsed.type === "well-known") {
|
|
2658
2726
|
await handleWellKnownSkills(source, parsed.url, options, spinner);
|
|
2659
2727
|
return;
|
|
@@ -2886,7 +2954,7 @@ async function runAdd(args, options = {}) {
|
|
|
2886
2954
|
value: key,
|
|
2887
2955
|
label: config.displayName
|
|
2888
2956
|
})));
|
|
2889
|
-
if (
|
|
2957
|
+
if (isCancelled$1(selected)) {
|
|
2890
2958
|
xe("Installation cancelled");
|
|
2891
2959
|
await cleanup(tempDir);
|
|
2892
2960
|
process.exit(0);
|
|
@@ -2901,7 +2969,7 @@ async function runAdd(args, options = {}) {
|
|
|
2901
2969
|
} else M.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
2902
2970
|
} else {
|
|
2903
2971
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2904
|
-
if (
|
|
2972
|
+
if (isCancelled$1(selected)) {
|
|
2905
2973
|
xe("Installation cancelled");
|
|
2906
2974
|
await cleanup(tempDir);
|
|
2907
2975
|
process.exit(0);
|
|
@@ -3117,18 +3185,28 @@ async function runAdd(args, options = {}) {
|
|
|
3117
3185
|
for (const skill of selectedSkills) {
|
|
3118
3186
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
3119
3187
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
3120
|
-
let skillFolderHash
|
|
3188
|
+
let skillFolderHash;
|
|
3189
|
+
let commitHash;
|
|
3121
3190
|
const skillPathValue = skillFiles[skill.name];
|
|
3122
|
-
|
|
3191
|
+
let skillPath = skillPathValue;
|
|
3192
|
+
if (skillPath) {
|
|
3193
|
+
if (skillPath.endsWith("/SKILL.md")) skillPath = skillPath.slice(0, -9);
|
|
3194
|
+
else if (skillPath.endsWith("SKILL.md")) skillPath = skillPath.slice(0, -8);
|
|
3195
|
+
}
|
|
3196
|
+
if (effectiveSourceType === "github" && skillPathValue) {
|
|
3123
3197
|
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue, getGitHubToken());
|
|
3124
3198
|
if (hash) skillFolderHash = hash;
|
|
3199
|
+
} else if (effectiveSourceType === "internal" && skillPath !== void 0) {
|
|
3200
|
+
const hash = await fetchAliInternalCommitHash(normalizedSource, skillPath, null);
|
|
3201
|
+
if (hash) commitHash = hash;
|
|
3125
3202
|
}
|
|
3126
3203
|
await addSkillToLock(skill.name, {
|
|
3127
3204
|
source: lockSource || normalizedSource,
|
|
3128
3205
|
sourceType: effectiveSourceType,
|
|
3129
|
-
sourceUrl: parsed.url,
|
|
3130
|
-
skillPath
|
|
3131
|
-
skillFolderHash,
|
|
3206
|
+
sourceUrl: sanitizeSourceUrl(parsed.url),
|
|
3207
|
+
skillPath,
|
|
3208
|
+
...skillFolderHash && { skillFolderHash },
|
|
3209
|
+
...commitHash && { commitHash },
|
|
3132
3210
|
pluginName: skill.pluginName
|
|
3133
3211
|
});
|
|
3134
3212
|
} catch {}
|
|
@@ -3141,8 +3219,9 @@ async function runAdd(args, options = {}) {
|
|
|
3141
3219
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
3142
3220
|
const computedHash = await computeSkillFolderHash(skill.path);
|
|
3143
3221
|
const skillFilePath = skillFiles[skill.name];
|
|
3222
|
+
const rawSource = lockSource || parsed.url;
|
|
3144
3223
|
const lockEntry = {
|
|
3145
|
-
source:
|
|
3224
|
+
source: effectiveSourceType === "internal" || effectiveSourceType === "github" ? rawSource.replace(/^https?:\/\/[^@]+@/, "https://") : rawSource,
|
|
3146
3225
|
sourceType: effectiveSourceType,
|
|
3147
3226
|
computedHash
|
|
3148
3227
|
};
|
|
@@ -3231,8 +3310,8 @@ async function runAdd(args, options = {}) {
|
|
|
3231
3310
|
} catch (error) {
|
|
3232
3311
|
if (error instanceof GitCloneError) {
|
|
3233
3312
|
M.error(import_picocolors.default.red("Failed to clone repository"));
|
|
3234
|
-
for (const line of error.message.split("\n")) M.message(import_picocolors.default.dim(line));
|
|
3235
|
-
} else M.error(error instanceof Error ? error.message : "Unknown error occurred");
|
|
3313
|
+
for (const line of error.message.split("\n")) M.message(import_picocolors.default.dim(redactSensitiveText(line)));
|
|
3314
|
+
} else M.error(redactSensitiveText(error instanceof Error ? error.message : "Unknown error occurred"));
|
|
3236
3315
|
showInstallTip();
|
|
3237
3316
|
Se(import_picocolors.default.red("Installation failed"));
|
|
3238
3317
|
process.exit(1);
|
|
@@ -3305,7 +3384,7 @@ const BOLD$2 = "\x1B[1m";
|
|
|
3305
3384
|
const DIM$5 = "\x1B[38;5;102m";
|
|
3306
3385
|
const TEXT$4 = "\x1B[38;5;145m";
|
|
3307
3386
|
const CYAN$1 = "\x1B[36m";
|
|
3308
|
-
const YELLOW$
|
|
3387
|
+
const YELLOW$5 = "\x1B[33m";
|
|
3309
3388
|
const SKILLS_API_URL = process.env.SKILLS_API_URL;
|
|
3310
3389
|
const SEARCH_ENDPOINT = SKILLS_API_URL ? `${SKILLS_API_URL.replace(/\/+$/, "")}/search` : "https://next-ai-base.pre-fn.alibaba-inc.com/api/skills/search";
|
|
3311
3390
|
function formatInstalls(count) {
|
|
@@ -3375,7 +3454,7 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3375
3454
|
const source = skill.source ? ` ${DIM$5}${skill.source}${RESET$5}` : "";
|
|
3376
3455
|
const installs = formatInstalls(skill.installs);
|
|
3377
3456
|
const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$5}` : "";
|
|
3378
|
-
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$
|
|
3457
|
+
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$5}GitHub${RESET$5}` : "";
|
|
3379
3458
|
const loadingIndicator = loading && i === 0 ? ` ${DIM$5}...${RESET$5}` : "";
|
|
3380
3459
|
lines.push(` ${arrow} ${name}${source}${githubBadge}${installsBadge}${loadingIndicator}`);
|
|
3381
3460
|
}
|
|
@@ -3484,7 +3563,7 @@ ${DIM$5} 2) npx @ali/cli-skills add <owner/repo@skill>${RESET$5}`;
|
|
|
3484
3563
|
for (const skill of results.slice(0, 6)) {
|
|
3485
3564
|
const pkg = skill.source || skill.slug;
|
|
3486
3565
|
const installs = formatInstalls(skill.installs);
|
|
3487
|
-
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$
|
|
3566
|
+
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$5}GitHub${RESET$5}` : "";
|
|
3488
3567
|
console.log(`${TEXT$4}${pkg}@${skill.name}${RESET$5}${githubBadge}${installs ? ` ${CYAN$1}${installs}${RESET$5}` : ""}`);
|
|
3489
3568
|
const skillUrl = buildSkillUrl(skill);
|
|
3490
3569
|
console.log(`${DIM$5}└ ${skillUrl}${RESET$5}`);
|
|
@@ -3612,7 +3691,7 @@ async function runSync(args, options = {}) {
|
|
|
3612
3691
|
M.info(`${import_picocolors.default.cyan(skill.name)} ${import_picocolors.default.dim(`from ${skill.packageName}`)}`);
|
|
3613
3692
|
if (skill.description) M.message(import_picocolors.default.dim(` ${skill.description}`));
|
|
3614
3693
|
}
|
|
3615
|
-
const localLock = await readLocalLock(cwd);
|
|
3694
|
+
const { lock: localLock } = await readLocalLock(cwd);
|
|
3616
3695
|
const toInstall = [];
|
|
3617
3696
|
const upToDate = [];
|
|
3618
3697
|
if (options.force) {
|
|
@@ -3812,7 +3891,8 @@ function parseSyncOptions(args) {
|
|
|
3812
3891
|
return { options };
|
|
3813
3892
|
}
|
|
3814
3893
|
async function runInstallFromLock(args) {
|
|
3815
|
-
const lock = await readLocalLock(process.cwd());
|
|
3894
|
+
const { lock, isOutdated } = await readLocalLock(process.cwd());
|
|
3895
|
+
if (isOutdated) M.warn("Project lock file may be outdated. Some skills might not install correctly.");
|
|
3816
3896
|
const skillEntries = Object.entries(lock.skills);
|
|
3817
3897
|
if (skillEntries.length === 0) {
|
|
3818
3898
|
M.warn("No project skills found in skills-lock.json");
|
|
@@ -3863,7 +3943,7 @@ const RESET$4 = "\x1B[0m";
|
|
|
3863
3943
|
const BOLD$1 = "\x1B[1m";
|
|
3864
3944
|
const DIM$4 = "\x1B[38;5;102m";
|
|
3865
3945
|
const CYAN = "\x1B[36m";
|
|
3866
|
-
const YELLOW$
|
|
3946
|
+
const YELLOW$4 = "\x1B[33m";
|
|
3867
3947
|
function shortenPath(fullPath, cwd) {
|
|
3868
3948
|
const home = homedir();
|
|
3869
3949
|
if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
|
|
@@ -3897,7 +3977,7 @@ async function runList(args) {
|
|
|
3897
3977
|
const validAgents = Object.keys(agents);
|
|
3898
3978
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
3899
3979
|
if (invalidAgents.length > 0) {
|
|
3900
|
-
console.log(`${YELLOW$
|
|
3980
|
+
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$4}`);
|
|
3901
3981
|
console.log(`${DIM$4}Valid agents: ${validAgents.join(", ")}${RESET$4}`);
|
|
3902
3982
|
process.exit(1);
|
|
3903
3983
|
}
|
|
@@ -3934,7 +4014,7 @@ async function runList(args) {
|
|
|
3934
4014
|
const prefix = indent ? " " : "";
|
|
3935
4015
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
3936
4016
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
3937
|
-
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$
|
|
4017
|
+
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$4}not linked${RESET$4}`;
|
|
3938
4018
|
console.log(`${prefix}${CYAN}${skill.name}${RESET$4} ${DIM$4}${shortPath}${RESET$4}`);
|
|
3939
4019
|
console.log(`${prefix} ${DIM$4}Agents:${RESET$4} ${agentInfo}`);
|
|
3940
4020
|
}
|
|
@@ -4537,7 +4617,7 @@ const DIM$3 = "\x1B[38;5;102m";
|
|
|
4537
4617
|
const TEXT$3 = "\x1B[38;5;145m";
|
|
4538
4618
|
const RESET$3 = "\x1B[0m";
|
|
4539
4619
|
const GREEN$2 = "\x1B[32m";
|
|
4540
|
-
const YELLOW$
|
|
4620
|
+
const YELLOW$3 = "\x1B[33m";
|
|
4541
4621
|
function startLocalServer() {
|
|
4542
4622
|
return new Promise((resolve) => {
|
|
4543
4623
|
let resolveCallback;
|
|
@@ -4685,7 +4765,7 @@ async function status() {
|
|
|
4685
4765
|
console.log(` 工号: ${auth.empId}`);
|
|
4686
4766
|
console.log(` 网关: ${auth.gatewayUrl}`);
|
|
4687
4767
|
if (expired) {
|
|
4688
|
-
console.log(` Token: ${YELLOW$
|
|
4768
|
+
console.log(` Token: ${YELLOW$3}即将过期${RESET$3}`);
|
|
4689
4769
|
console.log(`${TEXT$3}建议重新登录: npx ali-skills login${RESET$3}`);
|
|
4690
4770
|
} else console.log(` Token: ${GREEN$2}有效${RESET$3} (还剩 ${remainingDays} 天)`);
|
|
4691
4771
|
}
|
|
@@ -4773,7 +4853,7 @@ const TEXT$2 = "\x1B[38;5;145m";
|
|
|
4773
4853
|
const RESET$2 = "\x1B[0m";
|
|
4774
4854
|
const GREEN$1 = "\x1B[32m";
|
|
4775
4855
|
const RED$1 = "\x1B[31m";
|
|
4776
|
-
const YELLOW$
|
|
4856
|
+
const YELLOW$2 = "\x1B[33m";
|
|
4777
4857
|
function openBrowser$1(url) {
|
|
4778
4858
|
const platform = process.platform;
|
|
4779
4859
|
spawn(platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open", [url], {
|
|
@@ -4808,7 +4888,7 @@ function getPlatformPrivateToken(platformKey, explicitToken) {
|
|
|
4808
4888
|
}
|
|
4809
4889
|
async function promptConfigureToken(platformKey, platform) {
|
|
4810
4890
|
console.log();
|
|
4811
|
-
console.log(`${YELLOW$
|
|
4891
|
+
console.log(`${YELLOW$2}⚠️ ${platform.name} 需要配置 Private Token${RESET$2}`);
|
|
4812
4892
|
if (platform.tokenDocUrl) console.log(`${DIM$2}Token 获取地址: ${platform.tokenDocUrl}${RESET$2}`);
|
|
4813
4893
|
if (!await ye({
|
|
4814
4894
|
message: "是否现在配置?",
|
|
@@ -5038,7 +5118,7 @@ const DIM$1 = "\x1B[38;5;102m";
|
|
|
5038
5118
|
const TEXT$1 = "\x1B[38;5;145m";
|
|
5039
5119
|
const RESET$1 = "\x1B[0m";
|
|
5040
5120
|
const GREEN = "\x1B[32m";
|
|
5041
|
-
const YELLOW = "\x1B[33m";
|
|
5121
|
+
const YELLOW$1 = "\x1B[33m";
|
|
5042
5122
|
const RED = "\x1B[31m";
|
|
5043
5123
|
function showConfigHelp() {
|
|
5044
5124
|
console.log(`
|
|
@@ -5108,12 +5188,12 @@ async function configurePlatform(platformKey) {
|
|
|
5108
5188
|
console.log(`${GREEN}✓ ${platform.name} 使用内置凭证,无需配置${RESET$1}`);
|
|
5109
5189
|
break;
|
|
5110
5190
|
case "app_code+buc":
|
|
5111
|
-
console.log(`${YELLOW}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5191
|
+
console.log(`${YELLOW$1}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5112
5192
|
console.log(`${DIM$1}请运行: npx ali-skills login${RESET$1}`);
|
|
5113
5193
|
break;
|
|
5114
5194
|
case "buc_sso_refresh":
|
|
5115
5195
|
case "buc_sso_ticket":
|
|
5116
|
-
console.log(`${YELLOW}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5196
|
+
console.log(`${YELLOW$1}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5117
5197
|
console.log(`${DIM$1}请运行: npx ali-skills login${RESET$1}`);
|
|
5118
5198
|
break;
|
|
5119
5199
|
default:
|
|
@@ -5210,6 +5290,7 @@ const RESET = "\x1B[0m";
|
|
|
5210
5290
|
const BOLD = "\x1B[1m";
|
|
5211
5291
|
const DIM = "\x1B[38;5;102m";
|
|
5212
5292
|
const TEXT = "\x1B[38;5;145m";
|
|
5293
|
+
const YELLOW = "\x1B[33m";
|
|
5213
5294
|
const LOGO_LINES = [
|
|
5214
5295
|
" █████╗ ██╗ ██╗ ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗",
|
|
5215
5296
|
"██╔══██╗██║ ██║ ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝",
|
|
@@ -5571,26 +5652,42 @@ function readSkillLock() {
|
|
|
5571
5652
|
try {
|
|
5572
5653
|
const content = readFileSync(lockPath, "utf-8");
|
|
5573
5654
|
const parsed = JSON.parse(content);
|
|
5574
|
-
if (typeof parsed.version !== "number"
|
|
5575
|
-
|
|
5576
|
-
|
|
5655
|
+
if (typeof parsed.version !== "number") return {
|
|
5656
|
+
lock: {
|
|
5657
|
+
...parsed,
|
|
5658
|
+
version: CURRENT_LOCK_VERSION
|
|
5659
|
+
},
|
|
5660
|
+
isOutdated: true
|
|
5577
5661
|
};
|
|
5578
5662
|
if (parsed.version < CURRENT_LOCK_VERSION) return {
|
|
5579
|
-
|
|
5580
|
-
|
|
5663
|
+
lock: parsed,
|
|
5664
|
+
isOutdated: true
|
|
5665
|
+
};
|
|
5666
|
+
if (!parsed.skills) return {
|
|
5667
|
+
lock: {
|
|
5668
|
+
version: CURRENT_LOCK_VERSION,
|
|
5669
|
+
skills: {}
|
|
5670
|
+
},
|
|
5671
|
+
isOutdated: false
|
|
5672
|
+
};
|
|
5673
|
+
return {
|
|
5674
|
+
lock: parsed,
|
|
5675
|
+
isOutdated: false
|
|
5581
5676
|
};
|
|
5582
|
-
return parsed;
|
|
5583
5677
|
} catch {
|
|
5584
5678
|
return {
|
|
5585
|
-
|
|
5586
|
-
|
|
5679
|
+
lock: {
|
|
5680
|
+
version: CURRENT_LOCK_VERSION,
|
|
5681
|
+
skills: {}
|
|
5682
|
+
},
|
|
5683
|
+
isOutdated: false
|
|
5587
5684
|
};
|
|
5588
5685
|
}
|
|
5589
5686
|
}
|
|
5590
5687
|
function getSkipReason(entry) {
|
|
5591
5688
|
if (entry.sourceType === "local") return "Local path";
|
|
5592
5689
|
if (entry.sourceType === "git") return "Git URL (hash tracking not supported)";
|
|
5593
|
-
if (!entry.skillFolderHash) return "No version hash available";
|
|
5690
|
+
if (!(entry.sourceType === "internal" ? entry.commitHash : entry.skillFolderHash)) return "No version hash available";
|
|
5594
5691
|
if (!entry.skillPath) return "No skill path recorded";
|
|
5595
5692
|
return "No version tracking";
|
|
5596
5693
|
}
|
|
@@ -5599,6 +5696,39 @@ async function checkSkillsForUpdates(skills, token) {
|
|
|
5599
5696
|
const errors = [];
|
|
5600
5697
|
const skipped = [];
|
|
5601
5698
|
for (const [skillName, entry] of Object.entries(skills)) {
|
|
5699
|
+
if (entry.sourceType === "internal") {
|
|
5700
|
+
if (!entry.commitHash || !entry.skillPath) {
|
|
5701
|
+
skipped.push({
|
|
5702
|
+
name: skillName,
|
|
5703
|
+
reason: getSkipReason(entry),
|
|
5704
|
+
sourceUrl: entry.sourceUrl
|
|
5705
|
+
});
|
|
5706
|
+
continue;
|
|
5707
|
+
}
|
|
5708
|
+
try {
|
|
5709
|
+
const latestCommit = await fetchAliInternalCommitHash(entry.source, entry.skillPath, null);
|
|
5710
|
+
if (!latestCommit) {
|
|
5711
|
+
errors.push({
|
|
5712
|
+
name: skillName,
|
|
5713
|
+
source: entry.source,
|
|
5714
|
+
error: "Could not fetch from GitLab"
|
|
5715
|
+
});
|
|
5716
|
+
continue;
|
|
5717
|
+
}
|
|
5718
|
+
if (latestCommit !== entry.commitHash) updates.push({
|
|
5719
|
+
name: skillName,
|
|
5720
|
+
source: entry.source,
|
|
5721
|
+
entry
|
|
5722
|
+
});
|
|
5723
|
+
} catch (err) {
|
|
5724
|
+
errors.push({
|
|
5725
|
+
name: skillName,
|
|
5726
|
+
source: entry.source,
|
|
5727
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
5728
|
+
});
|
|
5729
|
+
}
|
|
5730
|
+
continue;
|
|
5731
|
+
}
|
|
5602
5732
|
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
5603
5733
|
skipped.push({
|
|
5604
5734
|
name: skillName,
|
|
@@ -5645,7 +5775,7 @@ async function runCheck(args = []) {
|
|
|
5645
5775
|
let skipped = [];
|
|
5646
5776
|
let skillCount = 0;
|
|
5647
5777
|
if (isGlobal) {
|
|
5648
|
-
const globalLock = readSkillLock();
|
|
5778
|
+
const { lock: globalLock, isOutdated: isGlobalLockOutdated } = readSkillLock();
|
|
5649
5779
|
const globalSkillNames = Object.keys(globalLock.skills);
|
|
5650
5780
|
skillCount = globalSkillNames.length;
|
|
5651
5781
|
if (globalSkillNames.length === 0) {
|
|
@@ -5667,9 +5797,11 @@ async function runCheck(args = []) {
|
|
|
5667
5797
|
if (skipped.length > 0) {
|
|
5668
5798
|
console.log();
|
|
5669
5799
|
console.log(`${DIM} ${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
5800
|
+
if (isGlobalLockOutdated) console.log(`${YELLOW} Note: Your lock file may be outdated. Re-adding these skills may fix this.${RESET}`);
|
|
5670
5801
|
for (const skill of skipped) {
|
|
5671
5802
|
console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
5672
|
-
|
|
5803
|
+
const displaySource = getDisplaySource(skill.sourceUrl);
|
|
5804
|
+
console.log(` ${DIM}To update: ${TEXT}npx ali-skills add ${displaySource} -g -y${RESET}`);
|
|
5673
5805
|
}
|
|
5674
5806
|
}
|
|
5675
5807
|
console.log();
|
|
@@ -5679,7 +5811,7 @@ async function runCheck(args = []) {
|
|
|
5679
5811
|
console.log(`${DIM}Run${RESET} ${TEXT}npx ali-skills update -g${RESET} ${DIM}to update all global skills${RESET}`);
|
|
5680
5812
|
}
|
|
5681
5813
|
} else {
|
|
5682
|
-
const projectLock = await readLocalLock();
|
|
5814
|
+
const { lock: projectLock, isOutdated: isLocalLockOutdated } = await readLocalLock();
|
|
5683
5815
|
const projectSkillNames = Object.keys(projectLock.skills);
|
|
5684
5816
|
skillCount = projectSkillNames.length;
|
|
5685
5817
|
if (projectSkillNames.length === 0) {
|
|
@@ -5773,6 +5905,7 @@ async function runCheck(args = []) {
|
|
|
5773
5905
|
if (skipped.length > 0) {
|
|
5774
5906
|
console.log();
|
|
5775
5907
|
console.log(`${DIM} ${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
5908
|
+
if (isLocalLockOutdated) console.log(`${YELLOW} Note: Your lock file may be outdated. Re-adding these skills may fix this.${RESET}`);
|
|
5776
5909
|
for (const skill of skipped) console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
5777
5910
|
}
|
|
5778
5911
|
console.log();
|
|
@@ -5800,10 +5933,11 @@ async function runUpdate(args = []) {
|
|
|
5800
5933
|
let skillsToCheck = {};
|
|
5801
5934
|
let scopeLabel = "project";
|
|
5802
5935
|
if (isGlobal) {
|
|
5803
|
-
|
|
5936
|
+
const { lock: globalLock } = readSkillLock();
|
|
5937
|
+
skillsToCheck = globalLock.skills;
|
|
5804
5938
|
scopeLabel = "global";
|
|
5805
5939
|
} else {
|
|
5806
|
-
const projectLock = await readLocalLock();
|
|
5940
|
+
const { lock: projectLock } = await readLocalLock();
|
|
5807
5941
|
for (const [name, entry] of Object.entries(projectLock.skills)) {
|
|
5808
5942
|
let sourceUrl;
|
|
5809
5943
|
if (entry.sourceType === "github") sourceUrl = `https://github.com/${entry.source}`;
|
|
@@ -5829,7 +5963,7 @@ async function runUpdate(args = []) {
|
|
|
5829
5963
|
}
|
|
5830
5964
|
const availableUpdates = [];
|
|
5831
5965
|
const skipped = [];
|
|
5832
|
-
const projectLock = !isGlobal ? await readLocalLock() : null;
|
|
5966
|
+
const projectLock = (!isGlobal ? await readLocalLock() : null)?.lock ?? null;
|
|
5833
5967
|
for (const skillName of skillNames) {
|
|
5834
5968
|
const entry = skillsToCheck[skillName];
|
|
5835
5969
|
if (!entry) continue;
|
|
@@ -6090,10 +6224,20 @@ async function main() {
|
|
|
6090
6224
|
}
|
|
6091
6225
|
main().then(async () => {
|
|
6092
6226
|
if (getPendingTelemetryCount() > 0) {
|
|
6227
|
+
const syncStartAt = Date.now();
|
|
6228
|
+
const longSyncThresholdMs = 800;
|
|
6229
|
+
let didShowStartMessage = false;
|
|
6230
|
+
const startMessageTimer = setTimeout(() => {
|
|
6231
|
+
didShowStartMessage = true;
|
|
6232
|
+
console.log();
|
|
6233
|
+
console.log(`${DIM}Syncing telemetry data...${RESET}`);
|
|
6234
|
+
}, longSyncThresholdMs);
|
|
6235
|
+
await flushTelemetry();
|
|
6236
|
+
clearTimeout(startMessageTimer);
|
|
6093
6237
|
console.log();
|
|
6094
|
-
console.log(`${DIM}
|
|
6095
|
-
|
|
6096
|
-
await flushTelemetry();
|
|
6238
|
+
if (didShowStartMessage || Date.now() - syncStartAt >= longSyncThresholdMs) console.log(`${DIM}Telemetry data sync complete${RESET}`);
|
|
6239
|
+
else console.log(`${DIM}Telemetry data synced${RESET}`);
|
|
6240
|
+
} else await flushTelemetry();
|
|
6097
6241
|
process.exit(0);
|
|
6098
6242
|
}).catch(async (error) => {
|
|
6099
6243
|
console.error(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ali-skills",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ali/cli-skills": "^2.0.
|
|
34
|
+
"@ali/cli-skills": "^2.0.17"
|
|
35
35
|
},
|
|
36
36
|
"bundleDependencies": [
|
|
37
37
|
"@ali/cli-skills"
|