ali-skills 0.1.2 → 0.1.4
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/node_modules/@ali/cli-skills/dist/_chunks/plugin.mjs +1 -1
- package/node_modules/@ali/cli-skills/dist/_chunks/publish.mjs +1 -1
- package/node_modules/@ali/cli-skills/dist/_chunks/skill-lock.mjs +75 -66
- package/node_modules/@ali/cli-skills/dist/cli.mjs +418 -405
- package/node_modules/@ali/cli-skills/package.json +1 -1
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import { i as __toESM } from "./rolldown-runtime.mjs";
|
|
|
2
2
|
import { l as pD, u as require_picocolors } from "./libs/@clack/core.mjs";
|
|
3
3
|
import { a as Y, n as M, s as fe } from "./libs/@clack/prompts.mjs";
|
|
4
4
|
import { i as SKILLS_API_BASE_URL, n as findPluginPackageRoots, t as validatePluginPackage } from "./plugin-manifest-validate.mjs";
|
|
5
|
-
import { A as getDisplaySource, D as redactCliSnippet, F as resolveSourceInput, I as toInternalHttpsCloneUrl, P as parseSource,
|
|
5
|
+
import { A as getDisplaySource, D as redactCliSnippet, F as resolveSourceInput, I as toInternalHttpsCloneUrl, P as parseSource, _ as trackPluginCommand, b as PLUGINS_SUBDIR, d as saveSelectedAgents, k as embedInternalGitCredentials, o as getLastSelectedAgents, v as trackPluginInstall, w as cloneRepo, y as AGENTS_DIR } from "./skill-lock.mjs";
|
|
6
6
|
import "./libs/@kwsites/file-exists.mjs";
|
|
7
7
|
import "./libs/@kwsites/promise-deferred.mjs";
|
|
8
8
|
import "./libs/simple-git.mjs";
|
|
@@ -741,4 +741,4 @@ async function runPluginPublish(_options = {}) {
|
|
|
741
741
|
spinner.stop(`${import_picocolors.default.green("✓")} Registered ${items.length} plugin(s)`);
|
|
742
742
|
Se(import_picocolors.default.green("插件录入成功,已可在平台检索安装。"));
|
|
743
743
|
}
|
|
744
|
-
export { addSkillToLocalLock as a,
|
|
744
|
+
export { addSkillToLocalLock as a, writeLocalLock as c, getSkillDisplayName as d, isValidSkillName as f, logSkillParseFailures as i, discoverSkills as l, runPluginPublish as n, computeSkillFolderHash as o, parseSkillMd as p, runPublish as r, readLocalLock as s, parsePublishOptions as t, filterSkills as u };
|
|
@@ -6,10 +6,17 @@ import { dirname, isAbsolute, join, normalize, resolve, sep } from "path";
|
|
|
6
6
|
import { homedir, tmpdir } from "os";
|
|
7
7
|
import { createHash } from "crypto";
|
|
8
8
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "fs/promises";
|
|
9
|
+
function stripGitSuffix(value) {
|
|
10
|
+
return value.replace(/\.git$/i, "");
|
|
11
|
+
}
|
|
9
12
|
function resolveSourceInput(source, options = {}) {
|
|
10
13
|
let resolved = source.replace(/\bgitlab\.alibaba-inc\.com\b/g, "code.alibaba-inc.com");
|
|
11
14
|
if (!options.github) {
|
|
12
|
-
|
|
15
|
+
const shorthandMatch = resolved.match(/^([^/]+)\/([^/]+)$/);
|
|
16
|
+
if (shorthandMatch && !resolved.includes(":") && !resolved.startsWith(".") && !resolved.startsWith("/")) {
|
|
17
|
+
const [, owner, repo] = shorthandMatch;
|
|
18
|
+
resolved = `https://${GITLAB_ACCOUNT.user}:${GITLAB_ACCOUNT.token}@code.alibaba-inc.com/${owner}/${stripGitSuffix(repo)}.git`;
|
|
19
|
+
}
|
|
13
20
|
}
|
|
14
21
|
if (/^https?:\/\/code\.alibaba-inc\.com\//.test(resolved) && !resolved.includes("@")) resolved = resolved.replace(/^(https?):\/\/code\.alibaba-inc\.com\//, `$1://${GITLAB_ACCOUNT.user}:${GITLAB_ACCOUNT.token}@code.alibaba-inc.com/`);
|
|
15
22
|
return resolved;
|
|
@@ -36,14 +43,14 @@ function getOwnerRepo(parsed) {
|
|
|
36
43
|
const sshMatch = parsed.url.match(/^git@[^:]+:(.+)$/);
|
|
37
44
|
if (sshMatch) {
|
|
38
45
|
let path = sshMatch[1];
|
|
39
|
-
path = path
|
|
46
|
+
path = stripGitSuffix(path);
|
|
40
47
|
if (path.includes("/")) return path;
|
|
41
48
|
return null;
|
|
42
49
|
}
|
|
43
50
|
if (!parsed.url.startsWith("http://") && !parsed.url.startsWith("https://")) return null;
|
|
44
51
|
try {
|
|
45
52
|
let path = new URL(parsed.url).pathname.slice(1);
|
|
46
|
-
path = path
|
|
53
|
+
path = stripGitSuffix(path);
|
|
47
54
|
if (path.includes("/")) return path;
|
|
48
55
|
} catch {}
|
|
49
56
|
return null;
|
|
@@ -57,7 +64,15 @@ function parseOwnerRepo(ownerRepo) {
|
|
|
57
64
|
return null;
|
|
58
65
|
}
|
|
59
66
|
async function isRepoPrivate(owner, repo) {
|
|
60
|
-
|
|
67
|
+
try {
|
|
68
|
+
const source = encodeURIComponent(`${owner}/${repo}`);
|
|
69
|
+
const res = await fetch(`${SKILLS_API_BASE_URL}/github/repository?source=${source}`);
|
|
70
|
+
if (!res.ok) return null;
|
|
71
|
+
const data = await res.json();
|
|
72
|
+
return data.success && typeof data.data?.private === "boolean" ? data.data.private : null;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
61
76
|
}
|
|
62
77
|
function sanitizeSubpath(subpath) {
|
|
63
78
|
const segments = subpath.replace(/\\/g, "/").split("/");
|
|
@@ -165,7 +180,7 @@ function parseSource(input) {
|
|
|
165
180
|
const [, owner, repo, skillFilter] = atSkillMatch;
|
|
166
181
|
return {
|
|
167
182
|
type: "github",
|
|
168
|
-
url: `https://github.com/${owner}/${repo}.git`,
|
|
183
|
+
url: `https://github.com/${owner}/${stripGitSuffix(repo)}.git`,
|
|
169
184
|
skillFilter
|
|
170
185
|
};
|
|
171
186
|
}
|
|
@@ -174,7 +189,7 @@ function parseSource(input) {
|
|
|
174
189
|
const [, owner, repo, subpath] = shorthandMatch;
|
|
175
190
|
return {
|
|
176
191
|
type: "github",
|
|
177
|
-
url: `https://github.com/${owner}/${repo}.git`,
|
|
192
|
+
url: `https://github.com/${owner}/${stripGitSuffix(repo)}.git`,
|
|
178
193
|
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
179
194
|
};
|
|
180
195
|
}
|
|
@@ -353,6 +368,9 @@ async function cloneRepo(url, ref) {
|
|
|
353
368
|
throw new GitCloneError(`Failed to clone ${safeUrl}: ${safeErrorMessage}`, safeUrl, false, false);
|
|
354
369
|
}
|
|
355
370
|
}
|
|
371
|
+
async function getGitHeadHash(repoDir) {
|
|
372
|
+
return (await esm_default(repoDir).revparse(["HEAD"])).trim();
|
|
373
|
+
}
|
|
356
374
|
async function listRemoteBranches(url) {
|
|
357
375
|
const git = esm_default({
|
|
358
376
|
timeout: { block: 15e3 },
|
|
@@ -548,72 +566,63 @@ async function writeSkillLock(lock) {
|
|
|
548
566
|
await mkdir(dirname(lockPath), { recursive: true });
|
|
549
567
|
await writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
|
|
550
568
|
}
|
|
551
|
-
function
|
|
552
|
-
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
|
|
553
|
-
if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
|
|
554
|
-
try {
|
|
555
|
-
const token = execSync("gh auth token", {
|
|
556
|
-
encoding: "utf-8",
|
|
557
|
-
stdio: [
|
|
558
|
-
"pipe",
|
|
559
|
-
"pipe",
|
|
560
|
-
"pipe"
|
|
561
|
-
]
|
|
562
|
-
}).trim();
|
|
563
|
-
if (token) return token;
|
|
564
|
-
} catch {}
|
|
565
|
-
return null;
|
|
566
|
-
}
|
|
567
|
-
async function fetchSkillFolderHash(ownerRepo, skillPath, token) {
|
|
568
|
-
let folderPath = skillPath.replace(/\\/g, "/");
|
|
569
|
-
if (folderPath.endsWith("/SKILL.md")) folderPath = folderPath.slice(0, -9);
|
|
570
|
-
else if (folderPath.endsWith("SKILL.md")) folderPath = folderPath.slice(0, -8);
|
|
571
|
-
if (folderPath.endsWith("/")) folderPath = folderPath.slice(0, -1);
|
|
572
|
-
for (const ref of [
|
|
573
|
-
"HEAD",
|
|
574
|
-
"master",
|
|
575
|
-
"main"
|
|
576
|
-
]) try {
|
|
577
|
-
const url = `https://api.github.com/repos/${ownerRepo}/git/trees/${ref}?recursive=1`;
|
|
578
|
-
const headers = {
|
|
579
|
-
Accept: "application/vnd.github.v3+json",
|
|
580
|
-
"User-Agent": "skills-cli"
|
|
581
|
-
};
|
|
582
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
583
|
-
const response = await fetch(url, { headers });
|
|
584
|
-
if (!response.ok) continue;
|
|
585
|
-
const data = await response.json();
|
|
586
|
-
if (!folderPath) return data.sha;
|
|
587
|
-
const folderEntry = data.tree.find((entry) => entry.type === "tree" && entry.path === folderPath);
|
|
588
|
-
if (folderEntry) return folderEntry.sha;
|
|
589
|
-
} catch {
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
592
|
-
return null;
|
|
593
|
-
}
|
|
594
|
-
async function fetchAliInternalCommitHash(source, skillPath = "", token) {
|
|
569
|
+
async function fetchAliInternalSkillVersion(source, skillPath = "", token) {
|
|
595
570
|
const refs = [
|
|
596
571
|
"HEAD",
|
|
597
572
|
"master",
|
|
598
573
|
"main"
|
|
599
574
|
];
|
|
600
575
|
const privateToken = token || process.env.GITLAB_PRIVATE_TOKEN || process.env.CODE_ALIBABA_TOKEN || GITLAB_ACCOUNT.token;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
576
|
+
const encodedSource = encodeURIComponent(source);
|
|
577
|
+
const normalizedPath = skillPath.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").replace(/\/?SKILL\.md$/i, "").replace(/\/+$/g, "");
|
|
578
|
+
const skillFile = normalizedPath ? `${normalizedPath}/SKILL.md` : "SKILL.md";
|
|
579
|
+
const headers = {
|
|
580
|
+
Accept: "application/json",
|
|
581
|
+
"User-Agent": "skills-cli",
|
|
582
|
+
"Private-Token": privateToken
|
|
583
|
+
};
|
|
584
|
+
let sawNotFound = false;
|
|
585
|
+
for (const ref of refs) {
|
|
586
|
+
const controller = new AbortController();
|
|
587
|
+
const timer = setTimeout(() => controller.abort(), 8e3);
|
|
588
|
+
try {
|
|
589
|
+
const fileUrl = `https://code.alibaba-inc.com/api/v4/projects/${encodedSource}/repository/files/${encodeURIComponent(skillFile)}?ref=${encodeURIComponent(ref)}`;
|
|
590
|
+
const fileResponse = await fetch(fileUrl, {
|
|
591
|
+
headers,
|
|
592
|
+
signal: controller.signal
|
|
593
|
+
});
|
|
594
|
+
if (fileResponse.status === 404) {
|
|
595
|
+
sawNotFound = true;
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
if (fileResponse.status === 401 || fileResponse.status === 403) return { status: "unauthorized" };
|
|
599
|
+
if (fileResponse.status === 429) return { status: "rate-limited" };
|
|
600
|
+
if (!fileResponse.ok) return { status: "upstream-error" };
|
|
601
|
+
let commitUrl = `https://code.alibaba-inc.com/api/v4/projects/${encodedSource}/repository/commits?ref_name=${encodeURIComponent(ref)}&per_page=1`;
|
|
602
|
+
if (normalizedPath) commitUrl += `&path=${encodeURIComponent(normalizedPath)}`;
|
|
603
|
+
const commitResponse = await fetch(commitUrl, {
|
|
604
|
+
headers,
|
|
605
|
+
signal: controller.signal
|
|
606
|
+
});
|
|
607
|
+
if (commitResponse.status === 401 || commitResponse.status === 403) return { status: "unauthorized" };
|
|
608
|
+
if (commitResponse.status === 429) return { status: "rate-limited" };
|
|
609
|
+
if (!commitResponse.ok) return { status: "upstream-error" };
|
|
610
|
+
const commits = await commitResponse.json();
|
|
611
|
+
return commits[0]?.id ? {
|
|
612
|
+
status: "ok",
|
|
613
|
+
latestHash: commits[0].id
|
|
614
|
+
} : { status: "upstream-error" };
|
|
615
|
+
} catch (error) {
|
|
616
|
+
return { status: error instanceof Error && error.name === "AbortError" ? "timeout" : "upstream-error" };
|
|
617
|
+
} finally {
|
|
618
|
+
clearTimeout(timer);
|
|
619
|
+
}
|
|
615
620
|
}
|
|
616
|
-
return
|
|
621
|
+
return { status: sawNotFound ? "not-found" : "upstream-error" };
|
|
622
|
+
}
|
|
623
|
+
async function fetchAliInternalCommitHash(source, skillPath = "", token) {
|
|
624
|
+
const result = await fetchAliInternalSkillVersion(source, skillPath, token);
|
|
625
|
+
return result.status === "ok" ? result.latestHash ?? null : null;
|
|
617
626
|
}
|
|
618
627
|
async function addSkillToLock(skillName, entry) {
|
|
619
628
|
const lock = await readSkillLock();
|
|
@@ -682,4 +691,4 @@ function sanitizeSourceUrl(url) {
|
|
|
682
691
|
}
|
|
683
692
|
return url;
|
|
684
693
|
}
|
|
685
|
-
export { getDisplaySource as A,
|
|
694
|
+
export { getDisplaySource as A, cleanupTempDir as C, redactCliSnippet as D, listRemoteBranches as E, resolveSourceInput as F, toInternalHttpsCloneUrl as I, isRepoPrivate as M, parseOwnerRepo as N, redactSensitiveText as O, parseSource as P, GitCloneError as S, getGitHeadHash as T, trackPluginCommand as _, getAllLockedSkills as a, PLUGINS_SUBDIR as b, isPromptDismissed as c, saveSelectedAgents as d, fetchAuditData as f, track as g, setVersion as h, fetchAliInternalSkillVersion as i, getOwnerRepo as j, embedInternalGitCredentials as k, removeSkillFromLock as l, getPendingTelemetryCount as m, dismissPrompt as n, getLastSelectedAgents as o, flushTelemetry as p, fetchAliInternalCommitHash as r, getSkillFromLock as s, addSkillToLock as t, sanitizeSourceUrl as u, trackPluginInstall as v, cloneRepo as w, SKILLS_SUBDIR as x, AGENTS_DIR$1 as y };
|
|
@@ -3,24 +3,24 @@ import { i as __toESM, n as __exportAll, r as __require } from "./_chunks/rolldo
|
|
|
3
3
|
import { l as pD, u as require_picocolors } from "./_chunks/libs/@clack/core.mjs";
|
|
4
4
|
import { a as Y, c as ge, d as ye, i as Se, l as ve, n as M, o as be, r as Me, s as fe, t as Ie, u as xe } from "./_chunks/libs/@clack/prompts.mjs";
|
|
5
5
|
import { i as SKILLS_API_BASE_URL, r as GITLAB_ACCOUNT } from "./_chunks/plugin-manifest-validate.mjs";
|
|
6
|
-
import { A as getDisplaySource, C as
|
|
6
|
+
import { A as getDisplaySource, C as cleanupTempDir, E as listRemoteBranches, F as resolveSourceInput, I as toInternalHttpsCloneUrl, M as isRepoPrivate, N as parseOwnerRepo, O as redactSensitiveText, P as parseSource, S as GitCloneError, T as getGitHeadHash, a as getAllLockedSkills, c as isPromptDismissed, d as saveSelectedAgents, f as fetchAuditData, g as track, h as setVersion, i as fetchAliInternalSkillVersion, j as getOwnerRepo, l as removeSkillFromLock, m as getPendingTelemetryCount, n as dismissPrompt, o as getLastSelectedAgents, p as flushTelemetry, r as fetchAliInternalCommitHash, s as getSkillFromLock, t as addSkillToLock, u as sanitizeSourceUrl, w as cloneRepo, x as SKILLS_SUBDIR, y as AGENTS_DIR$1 } from "./_chunks/skill-lock.mjs";
|
|
7
7
|
import "./_chunks/libs/@kwsites/file-exists.mjs";
|
|
8
8
|
import "./_chunks/libs/@kwsites/promise-deferred.mjs";
|
|
9
9
|
import "./_chunks/libs/simple-git.mjs";
|
|
10
10
|
import { t as require_gray_matter } from "./_chunks/libs/gray-matter.mjs";
|
|
11
11
|
import "./_chunks/libs/extend-shallow.mjs";
|
|
12
12
|
import "./_chunks/libs/esprima.mjs";
|
|
13
|
-
import { a as addSkillToLocalLock, c as
|
|
13
|
+
import { a as addSkillToLocalLock, c as writeLocalLock, d as getSkillDisplayName, f as isValidSkillName, i as logSkillParseFailures, l as discoverSkills, o as computeSkillFolderHash, p as parseSkillMd, r as runPublish, s as readLocalLock, t as parsePublishOptions, u as filterSkills } from "./_chunks/publish.mjs";
|
|
14
14
|
import { t as xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
|
|
15
|
-
import {
|
|
15
|
+
import { spawn } from "child_process";
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
17
17
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "path";
|
|
18
|
-
import { homedir, platform as platform$1
|
|
18
|
+
import { homedir, platform as platform$1 } from "os";
|
|
19
19
|
import { createHash } from "crypto";
|
|
20
20
|
import { URL as URL$1, fileURLToPath } from "url";
|
|
21
21
|
import * as readline from "readline";
|
|
22
22
|
import { Writable } from "stream";
|
|
23
|
-
import { access, cp, lstat, mkdir,
|
|
23
|
+
import { access, cp, lstat, mkdir, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
24
24
|
import * as http from "http";
|
|
25
25
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
26
26
|
const silentOutput = new Writable({ write(_chunk, _encoding, callback) {
|
|
@@ -202,67 +202,32 @@ async function getGitLabProject(ownerRepo, token) {
|
|
|
202
202
|
return null;
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
async function
|
|
205
|
+
async function getSkillLastCommitInfo(projectId, filePath, token) {
|
|
206
206
|
try {
|
|
207
207
|
const url = `${CODE_HOST}/api/v3/projects/${projectId}/repository/files/last_commit?filepath=${encodeURIComponent(filePath)}&sha=HEAD&private_token=${token}&projectId=${projectId}`;
|
|
208
208
|
const res = await fetch(url);
|
|
209
209
|
if (!res.ok) return null;
|
|
210
|
-
const
|
|
211
|
-
|
|
210
|
+
const commit = await res.json();
|
|
211
|
+
const hash = commit?.id ?? commit?.short_id ?? "";
|
|
212
|
+
const committedAt = commit?.committed_date ?? commit?.authored_date ?? commit?.created_at ?? "";
|
|
213
|
+
if (!hash && !committedAt) return null;
|
|
214
|
+
const author = commit?.user;
|
|
212
215
|
return {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
hash,
|
|
217
|
+
committedAt,
|
|
218
|
+
...author && { author: {
|
|
219
|
+
employeeId: author.extern_uid ?? "",
|
|
220
|
+
email: author.email ?? "",
|
|
221
|
+
name: author.name ?? "",
|
|
222
|
+
avatarUrl: author.avatar_url ?? "",
|
|
223
|
+
username: author.username ?? ""
|
|
224
|
+
} }
|
|
218
225
|
};
|
|
219
226
|
} catch (error) {
|
|
220
|
-
console.error("[GitLab] Failed to get skill last commit
|
|
227
|
+
console.error("[GitLab] Failed to get skill last commit info:", error);
|
|
221
228
|
return null;
|
|
222
229
|
}
|
|
223
230
|
}
|
|
224
|
-
const RESOLVE_URL = `${SKILLS_API_BASE_URL}/resolve`;
|
|
225
|
-
async function resolveFromOss(source, sourceType, skillName) {
|
|
226
|
-
try {
|
|
227
|
-
const params = new URLSearchParams({
|
|
228
|
-
source,
|
|
229
|
-
sourceType
|
|
230
|
-
});
|
|
231
|
-
if (skillName) params.set("skillName", skillName);
|
|
232
|
-
const controller = new AbortController();
|
|
233
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
234
|
-
const res = await fetch(`${RESOLVE_URL}?${params.toString()}`, { signal: controller.signal });
|
|
235
|
-
clearTimeout(timeout);
|
|
236
|
-
if (!res.ok) return null;
|
|
237
|
-
const json = await res.json();
|
|
238
|
-
if (!json.success || !json.data) return null;
|
|
239
|
-
return json.data;
|
|
240
|
-
} catch (err) {
|
|
241
|
-
console.error("[OSS Fallback] Failed to resolve from OSS:", err);
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
async function downloadAndExtractZip(zipUrl) {
|
|
246
|
-
const tempDir = await mkdtemp(join(tmpdir(), "skills-oss-"));
|
|
247
|
-
const zipPath = join(tempDir, "skill.zip");
|
|
248
|
-
try {
|
|
249
|
-
const controller = new AbortController();
|
|
250
|
-
const timeout = setTimeout(() => controller.abort(), 6e4);
|
|
251
|
-
const res = await fetch(zipUrl, { signal: controller.signal });
|
|
252
|
-
clearTimeout(timeout);
|
|
253
|
-
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
|
254
|
-
await writeFile(zipPath, Buffer.from(await res.arrayBuffer()));
|
|
255
|
-
execSync(`unzip -q -o "${zipPath}" -d "${tempDir}"`, { stdio: "pipe" });
|
|
256
|
-
await rm(zipPath, { force: true }).catch(() => {});
|
|
257
|
-
return tempDir;
|
|
258
|
-
} catch (error) {
|
|
259
|
-
await rm(tempDir, {
|
|
260
|
-
recursive: true,
|
|
261
|
-
force: true
|
|
262
|
-
}).catch(() => {});
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
231
|
const home = homedir();
|
|
267
232
|
const configHome = xdgConfig ?? join(home, ".config");
|
|
268
233
|
const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
|
|
@@ -947,6 +912,33 @@ async function copyDirectory(src, dest) {
|
|
|
947
912
|
}
|
|
948
913
|
}));
|
|
949
914
|
}
|
|
915
|
+
async function installSkillCanonical(skill, options = {}) {
|
|
916
|
+
const canonicalBase = getCanonicalSkillsDir(options.global ?? false, options.cwd);
|
|
917
|
+
const canonicalDir = join(canonicalBase, sanitizeName(skill.name || basename(skill.path)));
|
|
918
|
+
if (!isPathSafe(canonicalBase, canonicalDir)) return {
|
|
919
|
+
success: false,
|
|
920
|
+
path: canonicalDir,
|
|
921
|
+
mode: "symlink",
|
|
922
|
+
error: "Invalid skill name: potential path traversal detected"
|
|
923
|
+
};
|
|
924
|
+
try {
|
|
925
|
+
await cleanAndCreateDirectory(canonicalDir);
|
|
926
|
+
await copyDirectory(skill.path, canonicalDir);
|
|
927
|
+
return {
|
|
928
|
+
success: true,
|
|
929
|
+
path: canonicalDir,
|
|
930
|
+
canonicalPath: canonicalDir,
|
|
931
|
+
mode: "symlink"
|
|
932
|
+
};
|
|
933
|
+
} catch (error) {
|
|
934
|
+
return {
|
|
935
|
+
success: false,
|
|
936
|
+
path: canonicalDir,
|
|
937
|
+
mode: "symlink",
|
|
938
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
950
942
|
async function isSkillInstalled(skillName, agentType, options = {}) {
|
|
951
943
|
const agent = agents[agentType];
|
|
952
944
|
const sanitized = sanitizeName(skillName);
|
|
@@ -1394,7 +1386,178 @@ var WellKnownProvider = class {
|
|
|
1394
1386
|
}
|
|
1395
1387
|
};
|
|
1396
1388
|
const wellKnownProvider = new WellKnownProvider();
|
|
1397
|
-
var version$1 = "2.1.
|
|
1389
|
+
var version$1 = "2.1.5";
|
|
1390
|
+
async function checkGithubVersions$1(requests, debugLog = () => {}) {
|
|
1391
|
+
const results = [];
|
|
1392
|
+
for (let offset = 0; offset < requests.length; offset += 100) results.push(...await checkGithubVersionBatch(requests.slice(offset, offset + 100), debugLog));
|
|
1393
|
+
return results;
|
|
1394
|
+
}
|
|
1395
|
+
async function checkGithubVersionBatch(requests, debugLog) {
|
|
1396
|
+
const startedAt = Date.now();
|
|
1397
|
+
const endpoint = `${SKILLS_API_BASE_URL}/check-updates`;
|
|
1398
|
+
debugLog(`POST ${endpoint} skills=${requests.length}`);
|
|
1399
|
+
try {
|
|
1400
|
+
const response = await fetch(endpoint, {
|
|
1401
|
+
method: "POST",
|
|
1402
|
+
headers: { "Content-Type": "application/json" },
|
|
1403
|
+
body: JSON.stringify({ skills: requests })
|
|
1404
|
+
});
|
|
1405
|
+
debugLog(`POST ${endpoint} status=${response.status} durationMs=${Date.now() - startedAt}`);
|
|
1406
|
+
if (!response.ok) return requests.map((request) => ({
|
|
1407
|
+
...request,
|
|
1408
|
+
status: "upstream-error"
|
|
1409
|
+
}));
|
|
1410
|
+
const payload = await response.json();
|
|
1411
|
+
return payload.success && Array.isArray(payload.data?.results) ? payload.data.results : requests.map((request) => ({
|
|
1412
|
+
...request,
|
|
1413
|
+
status: "upstream-error"
|
|
1414
|
+
}));
|
|
1415
|
+
} catch (error) {
|
|
1416
|
+
debugLog(`POST ${endpoint} failed durationMs=${Date.now() - startedAt} error=${error instanceof Error ? error.name : "unknown"}`);
|
|
1417
|
+
return requests.map((request) => ({
|
|
1418
|
+
...request,
|
|
1419
|
+
status: "upstream-error"
|
|
1420
|
+
}));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
function normalizeSkillPath(skillPath) {
|
|
1424
|
+
return (skillPath ?? "").trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").replace(/\/?SKILL\.md$/i, "").replace(/\/+$/g, "");
|
|
1425
|
+
}
|
|
1426
|
+
function remoteKey(request) {
|
|
1427
|
+
return `${request.name}\0${request.source}\0${request.skillPath}`;
|
|
1428
|
+
}
|
|
1429
|
+
function fromRemote(entry, skillPath, remote, localHash) {
|
|
1430
|
+
const base = {
|
|
1431
|
+
name: entry.name,
|
|
1432
|
+
source: entry.source,
|
|
1433
|
+
entry,
|
|
1434
|
+
skillPath
|
|
1435
|
+
};
|
|
1436
|
+
if (!remote) return {
|
|
1437
|
+
...base,
|
|
1438
|
+
status: "check-failed",
|
|
1439
|
+
reason: "missing-response"
|
|
1440
|
+
};
|
|
1441
|
+
if (remote.status === "not-found") return {
|
|
1442
|
+
...base,
|
|
1443
|
+
status: "invalid-skill-path",
|
|
1444
|
+
reason: "SKILL.md not found"
|
|
1445
|
+
};
|
|
1446
|
+
if (remote.status !== "ok" || !remote.latestHash) return {
|
|
1447
|
+
...base,
|
|
1448
|
+
status: "check-failed",
|
|
1449
|
+
reason: remote.status
|
|
1450
|
+
};
|
|
1451
|
+
if (!localHash) return {
|
|
1452
|
+
...base,
|
|
1453
|
+
status: "update-required",
|
|
1454
|
+
reason: "missing-version",
|
|
1455
|
+
latestHash: remote.latestHash
|
|
1456
|
+
};
|
|
1457
|
+
if (localHash !== remote.latestHash) return {
|
|
1458
|
+
...base,
|
|
1459
|
+
status: "update-required",
|
|
1460
|
+
reason: "version-changed",
|
|
1461
|
+
latestHash: remote.latestHash
|
|
1462
|
+
};
|
|
1463
|
+
return {
|
|
1464
|
+
...base,
|
|
1465
|
+
status: "up-to-date",
|
|
1466
|
+
latestHash: remote.latestHash
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
function fromGithubRemote(entry, skillPath, remote) {
|
|
1470
|
+
const base = {
|
|
1471
|
+
name: entry.name,
|
|
1472
|
+
source: entry.source,
|
|
1473
|
+
entry,
|
|
1474
|
+
skillPath
|
|
1475
|
+
};
|
|
1476
|
+
if (!remote) return {
|
|
1477
|
+
...base,
|
|
1478
|
+
status: "check-failed",
|
|
1479
|
+
reason: "missing-response"
|
|
1480
|
+
};
|
|
1481
|
+
if (remote.status === "not-found") return {
|
|
1482
|
+
...base,
|
|
1483
|
+
status: "invalid-skill-path",
|
|
1484
|
+
reason: "SKILL.md not found"
|
|
1485
|
+
};
|
|
1486
|
+
if (remote.status !== "ok" || !remote.latestHash) return {
|
|
1487
|
+
...base,
|
|
1488
|
+
status: "check-failed",
|
|
1489
|
+
reason: remote.status
|
|
1490
|
+
};
|
|
1491
|
+
if (entry.commitHash) return fromRemote(entry, skillPath, remote, entry.commitHash);
|
|
1492
|
+
if (entry.skillFolderHash) {
|
|
1493
|
+
if (!remote.latestTreeHash) return {
|
|
1494
|
+
...base,
|
|
1495
|
+
status: "check-failed",
|
|
1496
|
+
reason: "missing-legacy-tree"
|
|
1497
|
+
};
|
|
1498
|
+
if (entry.skillFolderHash !== remote.latestTreeHash) return {
|
|
1499
|
+
...base,
|
|
1500
|
+
status: "update-required",
|
|
1501
|
+
reason: "version-changed",
|
|
1502
|
+
latestHash: remote.latestHash
|
|
1503
|
+
};
|
|
1504
|
+
return {
|
|
1505
|
+
...base,
|
|
1506
|
+
status: "up-to-date",
|
|
1507
|
+
latestHash: remote.latestHash,
|
|
1508
|
+
migrationCommitHash: remote.latestHash
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
...base,
|
|
1513
|
+
status: "update-required",
|
|
1514
|
+
reason: "missing-version",
|
|
1515
|
+
latestHash: remote.latestHash
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
async function checkSkillUpdates(entries, dependencies) {
|
|
1519
|
+
const results = /* @__PURE__ */ new Map();
|
|
1520
|
+
const githubEntries = entries.filter((entry) => entry.sourceType === "github");
|
|
1521
|
+
const githubRequests = githubEntries.map((entry) => ({
|
|
1522
|
+
name: entry.name,
|
|
1523
|
+
source: entry.checkSource ?? entry.source,
|
|
1524
|
+
skillPath: normalizeSkillPath(entry.skillPath),
|
|
1525
|
+
...!entry.commitHash && entry.skillFolderHash ? { legacySkillFolderHash: entry.skillFolderHash } : {}
|
|
1526
|
+
}));
|
|
1527
|
+
const githubResults = githubRequests.length ? await dependencies.checkGithub(githubRequests) : [];
|
|
1528
|
+
const githubByKey = new Map(githubResults.map((result) => [remoteKey(result), result]));
|
|
1529
|
+
for (const entry of githubEntries) {
|
|
1530
|
+
const skillPath = normalizeSkillPath(entry.skillPath);
|
|
1531
|
+
const request = {
|
|
1532
|
+
name: entry.name,
|
|
1533
|
+
source: entry.checkSource ?? entry.source,
|
|
1534
|
+
skillPath
|
|
1535
|
+
};
|
|
1536
|
+
results.set(entry.name, fromGithubRemote(entry, skillPath, githubByKey.get(remoteKey(request))));
|
|
1537
|
+
}
|
|
1538
|
+
await Promise.all(entries.filter((entry) => entry.sourceType === "internal").map(async (entry) => {
|
|
1539
|
+
const skillPath = normalizeSkillPath(entry.skillPath);
|
|
1540
|
+
const remote = await dependencies.checkInternal({
|
|
1541
|
+
name: entry.name,
|
|
1542
|
+
source: entry.source,
|
|
1543
|
+
skillPath
|
|
1544
|
+
}).catch(() => void 0);
|
|
1545
|
+
results.set(entry.name, fromRemote(entry, skillPath, remote, entry.commitHash));
|
|
1546
|
+
}));
|
|
1547
|
+
for (const entry of entries) {
|
|
1548
|
+
if (results.has(entry.name)) continue;
|
|
1549
|
+
const skillPath = normalizeSkillPath(entry.skillPath);
|
|
1550
|
+
results.set(entry.name, {
|
|
1551
|
+
name: entry.name,
|
|
1552
|
+
source: entry.source,
|
|
1553
|
+
entry,
|
|
1554
|
+
skillPath,
|
|
1555
|
+
status: "unsupported",
|
|
1556
|
+
reason: entry.sourceType === "local" ? "Local path" : "Version tracking not supported"
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
return entries.map((entry) => results.get(entry.name));
|
|
1560
|
+
}
|
|
1398
1561
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1399
1562
|
async function isSourcePrivate(source) {
|
|
1400
1563
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -1939,7 +2102,6 @@ async function runAdd(args, options = {}) {
|
|
|
1939
2102
|
return;
|
|
1940
2103
|
}
|
|
1941
2104
|
let skillsDir;
|
|
1942
|
-
let usedOssFallback = false;
|
|
1943
2105
|
if (parsed.type === "local") {
|
|
1944
2106
|
spinner.start("Validating local path...");
|
|
1945
2107
|
if (!existsSync(parsed.localPath)) {
|
|
@@ -1951,47 +2113,10 @@ async function runAdd(args, options = {}) {
|
|
|
1951
2113
|
spinner.stop("Local path validated");
|
|
1952
2114
|
} else {
|
|
1953
2115
|
spinner.start("Cloning repository...");
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
spinner.stop("Repository cloned");
|
|
1959
|
-
} catch (cloneError) {
|
|
1960
|
-
if (![
|
|
1961
|
-
"github",
|
|
1962
|
-
"internal",
|
|
1963
|
-
"git"
|
|
1964
|
-
].includes(parsed.type)) throw cloneError;
|
|
1965
|
-
if (cloneError instanceof GitCloneError && cloneError.isAuthError && cloneError.url.includes("code.alibaba-inc.com")) {
|
|
1966
|
-
spinner.stop(import_picocolors.default.yellow("Git clone failed: 无访问权限"));
|
|
1967
|
-
const gitlabUser = GITLAB_ACCOUNT.user;
|
|
1968
|
-
console.log();
|
|
1969
|
-
M.warn(import_picocolors.default.yellow(`仓库未授权给 ${import_picocolors.default.bold(gitlabUser)} 账号,请按以下步骤授权:`));
|
|
1970
|
-
M.message([
|
|
1971
|
-
` ${import_picocolors.default.cyan("推荐")} 在 ${import_picocolors.default.bold("Group")} 层级将 ${import_picocolors.default.cyan(gitlabUser)} 添加为 ${import_picocolors.default.bold("Owner")} 角色`,
|
|
1972
|
-
` 路径:GitLab Group → Manage → Members → Invite members`,
|
|
1973
|
-
` ${import_picocolors.default.dim("最低")} 在 ${import_picocolors.default.bold("Project")} 层级将 ${import_picocolors.default.cyan(gitlabUser)} 添加为 ${import_picocolors.default.bold("Reporter")} 及以上角色(可下载代码)`,
|
|
1974
|
-
` 路径:GitLab Project → Manage → Members → Invite members`
|
|
1975
|
-
].join("\n"));
|
|
1976
|
-
console.log();
|
|
1977
|
-
M.info("正在尝试 OSS 备用下载...");
|
|
1978
|
-
} else spinner.stop(import_picocolors.default.yellow("Git clone failed, trying OSS fallback..."));
|
|
1979
|
-
const normalizedSource = getOwnerRepo(parsed);
|
|
1980
|
-
if (!normalizedSource) throw cloneError;
|
|
1981
|
-
const singleSkillForOss = parsed.skillFilter ?? (options.skill?.length === 1 && !options.skill.includes("*") ? options.skill[0] : void 0);
|
|
1982
|
-
try {
|
|
1983
|
-
const resolved = await resolveFromOss(normalizedSource, parsed.type, singleSkillForOss);
|
|
1984
|
-
if (!resolved) throw cloneError;
|
|
1985
|
-
spinner.start("Downloading skill package from OSS...");
|
|
1986
|
-
const extractedDir = await downloadAndExtractZip(resolved.zipUrl);
|
|
1987
|
-
tempDir = extractedDir;
|
|
1988
|
-
skillsDir = extractedDir;
|
|
1989
|
-
usedOssFallback = true;
|
|
1990
|
-
spinner.stop("Skill package downloaded");
|
|
1991
|
-
} catch {
|
|
1992
|
-
throw cloneError;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
2116
|
+
const ref = parsed.ref === "HEAD" ? void 0 : parsed.ref;
|
|
2117
|
+
tempDir = await cloneRepo(parsed.url, ref);
|
|
2118
|
+
skillsDir = tempDir;
|
|
2119
|
+
spinner.stop("Repository cloned");
|
|
1995
2120
|
}
|
|
1996
2121
|
if (parsed.skillFilter) {
|
|
1997
2122
|
options.skill = options.skill || [];
|
|
@@ -2005,33 +2130,7 @@ async function runAdd(args, options = {}) {
|
|
|
2005
2130
|
parseFailures: skillParseFailures
|
|
2006
2131
|
};
|
|
2007
2132
|
spinner.start("Discovering skills...");
|
|
2008
|
-
|
|
2009
|
-
if (skills.length === 0 && !usedOssFallback && [
|
|
2010
|
-
"github",
|
|
2011
|
-
"internal",
|
|
2012
|
-
"git"
|
|
2013
|
-
].includes(parsed.type)) {
|
|
2014
|
-
const normalizedSource = getOwnerRepo(parsed);
|
|
2015
|
-
if (normalizedSource) {
|
|
2016
|
-
spinner.stop(import_picocolors.default.yellow("默认分支未找到 skill,检查 OSS 已发布版本..."));
|
|
2017
|
-
const singleSkillForOss = options.skill?.length === 1 && !options.skill.includes("*") ? options.skill[0] : void 0;
|
|
2018
|
-
try {
|
|
2019
|
-
const resolved = await resolveFromOss(normalizedSource, parsed.type, singleSkillForOss);
|
|
2020
|
-
if (resolved) {
|
|
2021
|
-
spinner.start("Downloading skill package from OSS...");
|
|
2022
|
-
const ossDir = await downloadAndExtractZip(resolved.zipUrl);
|
|
2023
|
-
await cleanup(tempDir);
|
|
2024
|
-
tempDir = ossDir;
|
|
2025
|
-
skillsDir = ossDir;
|
|
2026
|
-
usedOssFallback = true;
|
|
2027
|
-
spinner.stop("Skill package downloaded");
|
|
2028
|
-
spinner.start("Discovering skills...");
|
|
2029
|
-
skillParseFailures.length = 0;
|
|
2030
|
-
skills = await discoverSkills(skillsDir, parsed.subpath, discoverOpts);
|
|
2031
|
-
}
|
|
2032
|
-
} catch {}
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2133
|
+
const skills = await discoverSkills(skillsDir, parsed.subpath, discoverOpts);
|
|
2035
2134
|
if (skills.length === 0) {
|
|
2036
2135
|
spinner.stop(import_picocolors.default.red("No skills found"));
|
|
2037
2136
|
logSkillParseFailures(skillParseFailures);
|
|
@@ -2039,7 +2138,7 @@ async function runAdd(args, options = {}) {
|
|
|
2039
2138
|
await cleanup(tempDir);
|
|
2040
2139
|
process.exit(1);
|
|
2041
2140
|
}
|
|
2042
|
-
spinner.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}
|
|
2141
|
+
spinner.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
2043
2142
|
if (options.list) {
|
|
2044
2143
|
console.log();
|
|
2045
2144
|
M.step(import_picocolors.default.bold("Available Skills"));
|
|
@@ -2140,7 +2239,8 @@ async function runAdd(args, options = {}) {
|
|
|
2140
2239
|
const auditPromise = ownerRepoForAudit ? fetchAuditData(ownerRepoForAudit, selectedSkills.map((s) => getSkillDisplayName(s)), parsed.type) : Promise.resolve(null);
|
|
2141
2240
|
let targetAgents;
|
|
2142
2241
|
const validAgents = Object.keys(agents);
|
|
2143
|
-
if (options.
|
|
2242
|
+
if (options.canonicalOnly) targetAgents = [];
|
|
2243
|
+
else if (options.agent?.includes("*")) {
|
|
2144
2244
|
targetAgents = validAgents;
|
|
2145
2245
|
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
2146
2246
|
} else if (options.agent && options.agent.length > 0) {
|
|
@@ -2189,7 +2289,7 @@ async function runAdd(args, options = {}) {
|
|
|
2189
2289
|
targetAgents = selected;
|
|
2190
2290
|
}
|
|
2191
2291
|
}
|
|
2192
|
-
let installGlobally = options.global ?? false;
|
|
2292
|
+
let installGlobally = options.canonicalOnly ? true : options.global ?? false;
|
|
2193
2293
|
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
2194
2294
|
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
2195
2295
|
const scope = await ve({
|
|
@@ -2248,7 +2348,8 @@ async function runAdd(args, options = {}) {
|
|
|
2248
2348
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
2249
2349
|
const shortCanonical = shortenPath$2(getEntityPath(skill.name, installMode, installGlobally, cwd), cwd);
|
|
2250
2350
|
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2251
|
-
summaryLines.push(
|
|
2351
|
+
if (options.canonicalOnly) summaryLines.push(` ${import_picocolors.default.dim("shared entity only (not linked)")}`);
|
|
2352
|
+
else summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2252
2353
|
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
2253
2354
|
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
2254
2355
|
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
@@ -2291,17 +2392,29 @@ async function runAdd(args, options = {}) {
|
|
|
2291
2392
|
spinner.start("Installing skills...");
|
|
2292
2393
|
const results = [];
|
|
2293
2394
|
const effectiveAgents = installMode === "copy" || installMode === "copy-isolated" ? targetAgents.filter((a, i, arr) => arr.findIndex((b) => agents[b].skillsDir === agents[a].skillsDir) === i) : targetAgents;
|
|
2294
|
-
for (const skill of selectedSkills)
|
|
2295
|
-
|
|
2296
|
-
global:
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
}
|
|
2395
|
+
for (const skill of selectedSkills) {
|
|
2396
|
+
if (options.canonicalOnly) {
|
|
2397
|
+
const result = await installSkillCanonical(skill, { global: true });
|
|
2398
|
+
results.push({
|
|
2399
|
+
skill: getSkillDisplayName(skill),
|
|
2400
|
+
agent: "Shared",
|
|
2401
|
+
pluginName: skill.pluginName,
|
|
2402
|
+
...result
|
|
2403
|
+
});
|
|
2404
|
+
continue;
|
|
2405
|
+
}
|
|
2406
|
+
for (const agent of effectiveAgents) {
|
|
2407
|
+
const result = await installSkillForAgent(skill, agent, {
|
|
2408
|
+
global: installGlobally,
|
|
2409
|
+
mode: installMode
|
|
2410
|
+
});
|
|
2411
|
+
results.push({
|
|
2412
|
+
skill: getSkillDisplayName(skill),
|
|
2413
|
+
agent: agents[agent].displayName,
|
|
2414
|
+
pluginName: skill.pluginName,
|
|
2415
|
+
...result
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2305
2418
|
}
|
|
2306
2419
|
spinner.stop("Installation complete");
|
|
2307
2420
|
console.log();
|
|
@@ -2328,6 +2441,7 @@ async function runAdd(args, options = {}) {
|
|
|
2328
2441
|
const lockSource = parsed.url.startsWith("git@") ? parsed.url : normalizedSource;
|
|
2329
2442
|
const trackableSkillNames = selectedSkills.filter((s) => isValidSkillName(s.name)).map((s) => s.name);
|
|
2330
2443
|
const skillMdLastCommitAuthors = {};
|
|
2444
|
+
const skillMdLatestVersions = {};
|
|
2331
2445
|
if (parsed.type === "internal" && normalizedSource) {
|
|
2332
2446
|
const gitlabToken = GITLAB_ACCOUNT.token;
|
|
2333
2447
|
if (gitlabToken) {
|
|
@@ -2335,7 +2449,12 @@ async function runAdd(args, options = {}) {
|
|
|
2335
2449
|
if (project) for (const skillName of trackableSkillNames) {
|
|
2336
2450
|
const skillFilePath = skillFiles[skillName];
|
|
2337
2451
|
if (skillFilePath) {
|
|
2338
|
-
const
|
|
2452
|
+
const commitInfo = await getSkillLastCommitInfo(project.id, skillFilePath, gitlabToken);
|
|
2453
|
+
if (commitInfo?.hash || commitInfo?.committedAt) skillMdLatestVersions[skillName] = {
|
|
2454
|
+
hash: commitInfo.hash,
|
|
2455
|
+
committedAt: commitInfo.committedAt
|
|
2456
|
+
};
|
|
2457
|
+
const author = commitInfo?.author;
|
|
2339
2458
|
if (author) {
|
|
2340
2459
|
const formattedName = author.username ? `${author.name}/${author.username}` : author.name;
|
|
2341
2460
|
skillMdLastCommitAuthors[skillName] = {
|
|
@@ -2360,6 +2479,7 @@ async function runAdd(args, options = {}) {
|
|
|
2360
2479
|
skillFiles: JSON.stringify(skillFiles),
|
|
2361
2480
|
sourceType: effectiveSourceType,
|
|
2362
2481
|
skillMdLastCommitAuthors: JSON.stringify(skillMdLastCommitAuthors),
|
|
2482
|
+
skillMdLatestVersions: JSON.stringify(skillMdLatestVersions),
|
|
2363
2483
|
skillMdConfigAuthors: JSON.stringify(skillMdAuthors),
|
|
2364
2484
|
descriptions: JSON.stringify(skillDescriptions)
|
|
2365
2485
|
});
|
|
@@ -2372,27 +2492,37 @@ async function runAdd(args, options = {}) {
|
|
|
2372
2492
|
skillFiles: JSON.stringify(skillFiles),
|
|
2373
2493
|
sourceType: effectiveSourceType,
|
|
2374
2494
|
skillMdLastCommitAuthors: JSON.stringify(skillMdLastCommitAuthors),
|
|
2495
|
+
skillMdLatestVersions: JSON.stringify(skillMdLatestVersions),
|
|
2375
2496
|
skillMdConfigAuthors: JSON.stringify(skillMdAuthors),
|
|
2376
2497
|
descriptions: JSON.stringify(skillDescriptions)
|
|
2377
2498
|
});
|
|
2378
2499
|
}
|
|
2500
|
+
const githubCommitHashes = /* @__PURE__ */ new Map();
|
|
2501
|
+
if (successful.length > 0 && effectiveSourceType === "github" && tempDir) try {
|
|
2502
|
+
const successfulSkillNames = new Set(successful.map((result) => result.skill));
|
|
2503
|
+
const source = normalizedSource || getOwnerRepo(parsed);
|
|
2504
|
+
if (source) {
|
|
2505
|
+
const ref = await getGitHeadHash(tempDir);
|
|
2506
|
+
(await checkGithubVersions$1(selectedSkills.filter((skill) => successfulSkillNames.has(getSkillDisplayName(skill))).map((skill) => ({
|
|
2507
|
+
name: skill.name,
|
|
2508
|
+
source,
|
|
2509
|
+
skillPath: normalizeSkillPath(skillFiles[skill.name]),
|
|
2510
|
+
ref
|
|
2511
|
+
})))).forEach((version) => {
|
|
2512
|
+
if (version.status === "ok" && version.latestHash) githubCommitHashes.set(version.name, version.latestHash);
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
} catch {}
|
|
2379
2516
|
if (successful.length > 0 && installGlobally && normalizedSource) {
|
|
2380
2517
|
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2381
2518
|
for (const skill of selectedSkills) {
|
|
2382
2519
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
2383
2520
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
2384
|
-
let skillFolderHash;
|
|
2385
2521
|
let commitHash;
|
|
2386
2522
|
const skillPathValue = skillFiles[skill.name];
|
|
2387
|
-
|
|
2388
|
-
if (
|
|
2389
|
-
|
|
2390
|
-
else if (skillPath.endsWith("SKILL.md")) skillPath = skillPath.slice(0, -8);
|
|
2391
|
-
}
|
|
2392
|
-
if (effectiveSourceType === "github" && skillPathValue) {
|
|
2393
|
-
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue, getGitHubToken());
|
|
2394
|
-
if (hash) skillFolderHash = hash;
|
|
2395
|
-
} else if (effectiveSourceType === "internal" && skillPath !== void 0) {
|
|
2523
|
+
const skillPath = normalizeSkillPath(skillPathValue);
|
|
2524
|
+
if (effectiveSourceType === "github") commitHash = githubCommitHashes.get(skill.name);
|
|
2525
|
+
else if (effectiveSourceType === "internal") {
|
|
2396
2526
|
const hash = await fetchAliInternalCommitHash(normalizedSource, skillPath, null);
|
|
2397
2527
|
if (hash) commitHash = hash;
|
|
2398
2528
|
}
|
|
@@ -2401,7 +2531,6 @@ async function runAdd(args, options = {}) {
|
|
|
2401
2531
|
sourceType: effectiveSourceType,
|
|
2402
2532
|
sourceUrl: sanitizeSourceUrl(parsed.url),
|
|
2403
2533
|
skillPath,
|
|
2404
|
-
...skillFolderHash && { skillFolderHash },
|
|
2405
2534
|
...commitHash && { commitHash },
|
|
2406
2535
|
pluginName: skill.pluginName
|
|
2407
2536
|
});
|
|
@@ -2421,10 +2550,10 @@ async function runAdd(args, options = {}) {
|
|
|
2421
2550
|
sourceType: effectiveSourceType,
|
|
2422
2551
|
computedHash
|
|
2423
2552
|
};
|
|
2424
|
-
if (effectiveSourceType === "github"
|
|
2425
|
-
const
|
|
2426
|
-
if (
|
|
2427
|
-
lockEntry.skillPath = skillFilePath
|
|
2553
|
+
if (effectiveSourceType === "github") {
|
|
2554
|
+
const commitHash = githubCommitHashes.get(skill.name);
|
|
2555
|
+
if (commitHash) lockEntry.commitHash = commitHash;
|
|
2556
|
+
lockEntry.skillPath = normalizeSkillPath(skillFilePath);
|
|
2428
2557
|
}
|
|
2429
2558
|
if (effectiveSourceType === "internal") {
|
|
2430
2559
|
const source = normalizedSource || getOwnerRepo(parsed) || parsed.url;
|
|
@@ -5740,16 +5869,6 @@ async function runPlatformLogin(platformArgs) {
|
|
|
5740
5869
|
}
|
|
5741
5870
|
await configurePlatform(platform, platform === "yuque" ? extraArgs[0] : void 0);
|
|
5742
5871
|
}
|
|
5743
|
-
function getUpdateComparisonStrategy(entry) {
|
|
5744
|
-
if (entry.sourceType === "internal") return {
|
|
5745
|
-
field: "commitHash",
|
|
5746
|
-
value: entry.commitHash
|
|
5747
|
-
};
|
|
5748
|
-
return {
|
|
5749
|
-
field: "skillFolderHash",
|
|
5750
|
-
value: entry.skillFolderHash
|
|
5751
|
-
};
|
|
5752
|
-
}
|
|
5753
5872
|
function parseScopeFromArgs(args) {
|
|
5754
5873
|
let scopeGlobal;
|
|
5755
5874
|
let isScopeSpecified = false;
|
|
@@ -5768,6 +5887,20 @@ function parseScopeFromArgs(args) {
|
|
|
5768
5887
|
positionalArgs
|
|
5769
5888
|
};
|
|
5770
5889
|
}
|
|
5890
|
+
function resolveUpdateInstallTarget(installedAgents, global) {
|
|
5891
|
+
if (!installedAgents) return { mode: "skip" };
|
|
5892
|
+
if (installedAgents.length > 0) return {
|
|
5893
|
+
mode: "agents",
|
|
5894
|
+
agents: installedAgents
|
|
5895
|
+
};
|
|
5896
|
+
return global ? { mode: "canonical" } : { mode: "skip" };
|
|
5897
|
+
}
|
|
5898
|
+
function migrateGithubTreeToCommit(entry, expectedTreeHash, commitHash) {
|
|
5899
|
+
if (entry?.sourceType !== "github" || entry.commitHash || entry.skillFolderHash !== expectedTreeHash) return false;
|
|
5900
|
+
entry.commitHash = commitHash;
|
|
5901
|
+
delete entry.skillFolderHash;
|
|
5902
|
+
return true;
|
|
5903
|
+
}
|
|
5771
5904
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5772
5905
|
function getVersion() {
|
|
5773
5906
|
try {
|
|
@@ -5793,6 +5926,13 @@ function isTruthyEnv(value) {
|
|
|
5793
5926
|
function isUpdateDebugEnabled() {
|
|
5794
5927
|
return isTruthyEnv(process.env.ALI_SKILLS_DEBUG_UPDATE) || isTruthyEnv(process.env.DEBUG);
|
|
5795
5928
|
}
|
|
5929
|
+
function isCheckDebugEnabled() {
|
|
5930
|
+
return isTruthyEnv(process.env.ALI_SKILLS_DEBUG_CHECK) || isTruthyEnv(process.env.DEBUG);
|
|
5931
|
+
}
|
|
5932
|
+
function checkDebugLog(message) {
|
|
5933
|
+
if (!isCheckDebugEnabled() && !isUpdateDebugEnabled()) return;
|
|
5934
|
+
console.log(`${DIM}[check-debug]${RESET} ${message}`);
|
|
5935
|
+
}
|
|
5796
5936
|
function updateDebugLog(enabled, message) {
|
|
5797
5937
|
if (!enabled) return;
|
|
5798
5938
|
console.log(`${DIM}[update-debug]${RESET} ${message}`);
|
|
@@ -6465,88 +6605,83 @@ function readSkillLock() {
|
|
|
6465
6605
|
};
|
|
6466
6606
|
}
|
|
6467
6607
|
}
|
|
6468
|
-
function
|
|
6469
|
-
if (
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
}
|
|
6475
|
-
async function
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
const latestCommit = await fetchAliInternalCommitHash(entry.source, entry.skillPath, null);
|
|
6491
|
-
if (!latestCommit) {
|
|
6492
|
-
errors.push({
|
|
6493
|
-
name: skillName,
|
|
6494
|
-
source: entry.source,
|
|
6495
|
-
error: "Could not fetch from GitLab"
|
|
6496
|
-
});
|
|
6497
|
-
continue;
|
|
6498
|
-
}
|
|
6499
|
-
if (latestCommit !== entry.commitHash) updates.push({
|
|
6500
|
-
name: skillName,
|
|
6501
|
-
source: entry.source,
|
|
6502
|
-
entry
|
|
6503
|
-
});
|
|
6504
|
-
} catch (err) {
|
|
6505
|
-
errors.push({
|
|
6506
|
-
name: skillName,
|
|
6507
|
-
source: entry.source,
|
|
6508
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
6509
|
-
});
|
|
6510
|
-
}
|
|
6511
|
-
continue;
|
|
6512
|
-
}
|
|
6513
|
-
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
6514
|
-
skipped.push({
|
|
6515
|
-
name: skillName,
|
|
6516
|
-
reason: getSkipReason(entry),
|
|
6517
|
-
sourceUrl: entry.sourceUrl
|
|
6518
|
-
});
|
|
6519
|
-
continue;
|
|
6520
|
-
}
|
|
6521
|
-
try {
|
|
6522
|
-
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
|
|
6523
|
-
if (!latestHash) {
|
|
6524
|
-
errors.push({
|
|
6525
|
-
name: skillName,
|
|
6526
|
-
source: entry.source,
|
|
6527
|
-
error: "Could not fetch from GitHub"
|
|
6528
|
-
});
|
|
6529
|
-
continue;
|
|
6530
|
-
}
|
|
6531
|
-
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
6532
|
-
name: skillName,
|
|
6533
|
-
source: entry.source,
|
|
6534
|
-
entry
|
|
6535
|
-
});
|
|
6536
|
-
} catch (err) {
|
|
6537
|
-
errors.push({
|
|
6538
|
-
name: skillName,
|
|
6539
|
-
source: entry.source,
|
|
6540
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
6541
|
-
});
|
|
6542
|
-
}
|
|
6608
|
+
function printCheckErrors(errors) {
|
|
6609
|
+
if (errors.length === 0) return;
|
|
6610
|
+
console.log();
|
|
6611
|
+
console.log(`${YELLOW}${errors.length} skill(s) failed during update checking:${RESET}`);
|
|
6612
|
+
for (const error of errors) console.log(` ${YELLOW}⚠${RESET} ${error.name} ${DIM}(${error.source}: ${error.error})${RESET}`);
|
|
6613
|
+
console.log(`${DIM}Re-run with ALI_SKILLS_DEBUG_CHECK=1 for sanitized request diagnostics.${RESET}`);
|
|
6614
|
+
}
|
|
6615
|
+
async function checkGithubVersions(requests) {
|
|
6616
|
+
return checkGithubVersions$1(requests, checkDebugLog);
|
|
6617
|
+
}
|
|
6618
|
+
async function checkInternalVersion(request) {
|
|
6619
|
+
try {
|
|
6620
|
+
const result = await fetchAliInternalSkillVersion(request.source, request.skillPath, null);
|
|
6621
|
+
return {
|
|
6622
|
+
...request,
|
|
6623
|
+
...result
|
|
6624
|
+
};
|
|
6625
|
+
} catch {
|
|
6626
|
+
return {
|
|
6627
|
+
...request,
|
|
6628
|
+
status: "upstream-error"
|
|
6629
|
+
};
|
|
6543
6630
|
}
|
|
6631
|
+
}
|
|
6632
|
+
async function checkSkillsForUpdates(skills) {
|
|
6633
|
+
const results = await checkSkillUpdates(Object.entries(skills).map(([name, entry]) => ({
|
|
6634
|
+
...entry,
|
|
6635
|
+
name,
|
|
6636
|
+
...entry.sourceType === "github" ? { checkSource: getOwnerRepo(parseSource(entry.source)) ?? getOwnerRepo(parseSource(entry.sourceUrl)) ?? entry.source } : {}
|
|
6637
|
+
})), {
|
|
6638
|
+
checkGithub: checkGithubVersions,
|
|
6639
|
+
checkInternal: checkInternalVersion
|
|
6640
|
+
});
|
|
6544
6641
|
return {
|
|
6545
|
-
updates
|
|
6546
|
-
|
|
6547
|
-
|
|
6642
|
+
updates: results.filter((result) => result.status === "update-required").map((result) => ({
|
|
6643
|
+
name: result.name,
|
|
6644
|
+
source: result.source,
|
|
6645
|
+
entry: result.entry
|
|
6646
|
+
})),
|
|
6647
|
+
errors: results.filter((result) => result.status === "check-failed").map((result) => ({
|
|
6648
|
+
name: result.name,
|
|
6649
|
+
source: result.source,
|
|
6650
|
+
error: result.reason ?? "Unknown error"
|
|
6651
|
+
})),
|
|
6652
|
+
skipped: results.filter((result) => result.status === "unsupported" || result.status === "invalid-skill-path").map((result) => ({
|
|
6653
|
+
name: result.name,
|
|
6654
|
+
reason: result.reason ?? "No version tracking",
|
|
6655
|
+
sourceUrl: result.entry.sourceUrl
|
|
6656
|
+
})),
|
|
6657
|
+
migrations: results.filter((result) => result.status === "up-to-date" && Boolean(result.migrationCommitHash) && Boolean(result.entry.skillFolderHash)).map((result) => ({
|
|
6658
|
+
name: result.name,
|
|
6659
|
+
expectedTreeHash: result.entry.skillFolderHash,
|
|
6660
|
+
commitHash: result.migrationCommitHash
|
|
6661
|
+
}))
|
|
6548
6662
|
};
|
|
6549
6663
|
}
|
|
6664
|
+
async function applySafeGithubLockMigrations(migrations, isGlobal) {
|
|
6665
|
+
if (migrations.length === 0) return 0;
|
|
6666
|
+
if (isGlobal) {
|
|
6667
|
+
const { lock } = readSkillLock();
|
|
6668
|
+
let changed = 0;
|
|
6669
|
+
for (const migration of migrations) {
|
|
6670
|
+
const entry = lock.skills[migration.name];
|
|
6671
|
+
if (migrateGithubTreeToCommit(entry, migration.expectedTreeHash, migration.commitHash)) changed++;
|
|
6672
|
+
}
|
|
6673
|
+
if (changed > 0) writeFileSync(getSkillLockPath(), JSON.stringify(lock, null, 2), "utf-8");
|
|
6674
|
+
return changed;
|
|
6675
|
+
}
|
|
6676
|
+
const { lock } = await readLocalLock();
|
|
6677
|
+
let changed = 0;
|
|
6678
|
+
for (const migration of migrations) {
|
|
6679
|
+
const entry = lock.skills[migration.name];
|
|
6680
|
+
if (migrateGithubTreeToCommit(entry, migration.expectedTreeHash, migration.commitHash)) changed++;
|
|
6681
|
+
}
|
|
6682
|
+
if (changed > 0) await writeLocalLock(lock);
|
|
6683
|
+
return changed;
|
|
6684
|
+
}
|
|
6550
6685
|
async function runCheck(args = []) {
|
|
6551
6686
|
const { isGlobal, isScopeSpecified, positionalArgs: skillArgs } = parseScopeFromArgs(args);
|
|
6552
6687
|
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
@@ -6555,8 +6690,8 @@ async function runCheck(args = []) {
|
|
|
6555
6690
|
console.log(`${DIM}提示: 当前为项目级检查(skills-lock.json)。如需检查全局技能,请执行: ${TEXT}npx ali-skills check -g${RESET}`);
|
|
6556
6691
|
console.log();
|
|
6557
6692
|
}
|
|
6558
|
-
const token = getGitHubToken();
|
|
6559
6693
|
let updates = [];
|
|
6694
|
+
let errors = [];
|
|
6560
6695
|
let skipped = [];
|
|
6561
6696
|
let skillCount = 0;
|
|
6562
6697
|
if (isGlobal) {
|
|
@@ -6582,11 +6717,12 @@ async function runCheck(args = []) {
|
|
|
6582
6717
|
skillCount = selectedGlobalSkillNames.length;
|
|
6583
6718
|
console.log(`${BOLD}Global skills${RESET}`);
|
|
6584
6719
|
console.log(`${DIM}Checking ${selectedGlobalSkillNames.length} skill(s)...${RESET}`);
|
|
6585
|
-
const result = await checkSkillsForUpdates(globalSkillsToCheck
|
|
6720
|
+
const result = await checkSkillsForUpdates(globalSkillsToCheck);
|
|
6586
6721
|
updates = result.updates;
|
|
6587
|
-
result.errors;
|
|
6722
|
+
errors = result.errors;
|
|
6588
6723
|
skipped = result.skipped;
|
|
6589
|
-
if (updates.length === 0) console.log(`${TEXT} ✓ ${skillArgs.length > 0 ? "All selected global skills are up to date" : "All global skills are up to date"}${RESET}`);
|
|
6724
|
+
if (updates.length === 0 && errors.length === 0) console.log(`${TEXT} ✓ ${skillArgs.length > 0 ? "All selected global skills are up to date" : "All global skills are up to date"}${RESET}`);
|
|
6725
|
+
else if (updates.length === 0) console.log(`${YELLOW} No updates confirmed; some skills could not be checked.${RESET}`);
|
|
6590
6726
|
else {
|
|
6591
6727
|
console.log(`${TEXT} ${updates.length} update(s) available:${RESET}`);
|
|
6592
6728
|
for (const update of updates) console.log(` ${TEXT}↑${RESET} ${update.name} ${DIM}(${update.source})${RESET}`);
|
|
@@ -6601,6 +6737,7 @@ async function runCheck(args = []) {
|
|
|
6601
6737
|
console.log(` ${DIM}To update: ${TEXT}npx ali-skills add ${displaySource} -g -y${RESET}`);
|
|
6602
6738
|
}
|
|
6603
6739
|
}
|
|
6740
|
+
printCheckErrors(errors);
|
|
6604
6741
|
console.log();
|
|
6605
6742
|
if (updates.length > 0) {
|
|
6606
6743
|
console.log(`${TEXT}${updates.length} update(s) available${RESET}`);
|
|
@@ -6630,82 +6767,21 @@ async function runCheck(args = []) {
|
|
|
6630
6767
|
skillCount = selectedProjectSkillNames.length;
|
|
6631
6768
|
console.log(`${BOLD}Project-level skills${RESET}`);
|
|
6632
6769
|
console.log(`${DIM}Checking ${selectedProjectSkillNames.length} skill(s)...${RESET}`);
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
sourceUrl: `https://code.alibaba-inc.com/${entry.source}`
|
|
6649
|
-
});
|
|
6650
|
-
else if (latestCommit !== entry.commitHash) updates.push({
|
|
6651
|
-
name: skillName,
|
|
6652
|
-
source: entry.source,
|
|
6653
|
-
entry: {
|
|
6654
|
-
source: entry.source,
|
|
6655
|
-
sourceType: entry.sourceType,
|
|
6656
|
-
sourceUrl: `https://code.alibaba-inc.com/${entry.source}`,
|
|
6657
|
-
skillPath,
|
|
6658
|
-
skillFolderHash: entry.computedHash,
|
|
6659
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6660
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6661
|
-
}
|
|
6662
|
-
});
|
|
6663
|
-
continue;
|
|
6664
|
-
}
|
|
6665
|
-
if (entry.sourceType === "github") {
|
|
6666
|
-
if (!entry.skillFolderHash) {
|
|
6667
|
-
skipped.push({
|
|
6668
|
-
name: skillName,
|
|
6669
|
-
reason: "Reinstall required to enable update checking (missing folder hash)",
|
|
6670
|
-
sourceUrl: `https://github.com/${entry.source}`
|
|
6671
|
-
});
|
|
6672
|
-
continue;
|
|
6673
|
-
}
|
|
6674
|
-
const possiblePaths = entry.skillPath ? [entry.skillPath] : [`skills/${skillName}`, `skills/${skillName}/SKILL.md`];
|
|
6675
|
-
let foundMatch = false;
|
|
6676
|
-
for (const skillPath of possiblePaths) try {
|
|
6677
|
-
const latestHash = await fetchSkillFolderHash(entry.source, skillPath, token);
|
|
6678
|
-
if (latestHash === null) continue;
|
|
6679
|
-
foundMatch = true;
|
|
6680
|
-
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
6681
|
-
name: skillName,
|
|
6682
|
-
source: entry.source,
|
|
6683
|
-
entry: {
|
|
6684
|
-
source: entry.source,
|
|
6685
|
-
sourceType: entry.sourceType,
|
|
6686
|
-
sourceUrl: `https://github.com/${entry.source}`,
|
|
6687
|
-
skillPath,
|
|
6688
|
-
skillFolderHash: entry.skillFolderHash,
|
|
6689
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6690
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6691
|
-
}
|
|
6692
|
-
});
|
|
6693
|
-
break;
|
|
6694
|
-
} catch {
|
|
6695
|
-
continue;
|
|
6696
|
-
}
|
|
6697
|
-
if (!foundMatch) skipped.push({
|
|
6698
|
-
name: skillName,
|
|
6699
|
-
reason: "Unable to verify (API failed for all possible paths)",
|
|
6700
|
-
sourceUrl: `https://github.com/${entry.source}`
|
|
6701
|
-
});
|
|
6702
|
-
} else skipped.push({
|
|
6703
|
-
name: skillName,
|
|
6704
|
-
reason: `${entry.sourceType} source type not supported for update checking`,
|
|
6705
|
-
sourceUrl: entry.source
|
|
6706
|
-
});
|
|
6707
|
-
}
|
|
6708
|
-
if (updates.length === 0) console.log(`${TEXT} ✓ ${skillArgs.length > 0 ? "All selected project skills are up to date" : "All project skills are up to date"}${RESET}`);
|
|
6770
|
+
const result = await checkSkillsForUpdates(Object.fromEntries(Object.entries(projectSkillsToCheck).map(([name, entry]) => [name, {
|
|
6771
|
+
source: entry.source,
|
|
6772
|
+
sourceType: entry.sourceType,
|
|
6773
|
+
sourceUrl: entry.sourceType === "github" ? `https://github.com/${entry.source}` : entry.sourceType === "internal" ? `https://code.alibaba-inc.com/${entry.source}` : entry.source,
|
|
6774
|
+
skillPath: entry.skillPath ?? "",
|
|
6775
|
+
skillFolderHash: entry.skillFolderHash,
|
|
6776
|
+
commitHash: entry.commitHash,
|
|
6777
|
+
installedAt: "",
|
|
6778
|
+
updatedAt: ""
|
|
6779
|
+
}])));
|
|
6780
|
+
updates = result.updates;
|
|
6781
|
+
errors = result.errors;
|
|
6782
|
+
skipped = result.skipped;
|
|
6783
|
+
if (updates.length === 0 && errors.length === 0) console.log(`${TEXT} ✓ ${skillArgs.length > 0 ? "All selected project skills are up to date" : "All project skills are up to date"}${RESET}`);
|
|
6784
|
+
else if (updates.length === 0) console.log(`${YELLOW} No updates confirmed; some skills could not be checked.${RESET}`);
|
|
6709
6785
|
else {
|
|
6710
6786
|
console.log(`${TEXT} ${updates.length} update(s) available:${RESET}`);
|
|
6711
6787
|
for (const update of updates) console.log(` ${TEXT}↑${RESET} ${update.name} ${DIM}(${update.source})${RESET}`);
|
|
@@ -6716,6 +6792,7 @@ async function runCheck(args = []) {
|
|
|
6716
6792
|
if (isLocalLockOutdated) console.log(`${YELLOW} Note: Your lock file may be outdated. Re-adding these skills may fix this.${RESET}`);
|
|
6717
6793
|
for (const skill of skipped) console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
6718
6794
|
}
|
|
6795
|
+
printCheckErrors(errors);
|
|
6719
6796
|
console.log();
|
|
6720
6797
|
if (updates.length > 0) {
|
|
6721
6798
|
console.log(`${TEXT}${updates.length} update(s) available${RESET}`);
|
|
@@ -6742,7 +6819,6 @@ async function runUpdate(args = []) {
|
|
|
6742
6819
|
console.log();
|
|
6743
6820
|
}
|
|
6744
6821
|
updateDebugLog(debugUpdate, `scope=${isGlobal ? "global" : "project"}, yes=${yesFlag}, requestedSkills=${skillArgs.length > 0 ? skillArgs.join(",") : "(all)"}`);
|
|
6745
|
-
const token = getGitHubToken();
|
|
6746
6822
|
let skillsToCheck = {};
|
|
6747
6823
|
let scopeLabel = "project";
|
|
6748
6824
|
if (isGlobal) {
|
|
@@ -6760,9 +6836,9 @@ async function runUpdate(args = []) {
|
|
|
6760
6836
|
source: entry.source,
|
|
6761
6837
|
sourceType: entry.sourceType,
|
|
6762
6838
|
sourceUrl,
|
|
6763
|
-
skillPath: entry.skillPath
|
|
6839
|
+
skillPath: entry.skillPath ?? "",
|
|
6764
6840
|
commitHash: entry.commitHash,
|
|
6765
|
-
skillFolderHash: entry.skillFolderHash
|
|
6841
|
+
skillFolderHash: entry.skillFolderHash,
|
|
6766
6842
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6767
6843
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6768
6844
|
};
|
|
@@ -6775,81 +6851,17 @@ async function runUpdate(args = []) {
|
|
|
6775
6851
|
console.log();
|
|
6776
6852
|
return;
|
|
6777
6853
|
}
|
|
6778
|
-
const
|
|
6779
|
-
const
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
const localEntry = projectLock?.skills[skillName];
|
|
6786
|
-
const commitHash = getUpdateComparisonStrategy(entry).value || getUpdateComparisonStrategy(localEntry || {}).value;
|
|
6787
|
-
const storedSkillPath = entry.skillPath || localEntry?.skillPath || "";
|
|
6788
|
-
updateDebugLog(debugUpdate, `checking internal skill=${skillName}, repo=${entry.source}, skillPath=${storedSkillPath || "(root)"}, hasCommitHash=${commitHash ? "yes" : "no"}`);
|
|
6789
|
-
if (!commitHash) {
|
|
6790
|
-
skipped.push({
|
|
6791
|
-
name: skillName,
|
|
6792
|
-
reason: "Reinstall required to enable update checking (missing commit hash)",
|
|
6793
|
-
sourceUrl: entry.sourceUrl
|
|
6794
|
-
});
|
|
6795
|
-
continue;
|
|
6796
|
-
}
|
|
6797
|
-
try {
|
|
6798
|
-
const skillPath = storedSkillPath.replace(/\/?SKILL\.md$/i, "");
|
|
6799
|
-
const latestCommit = await fetchAliInternalCommitHash(entry.source, skillPath, null);
|
|
6800
|
-
if (latestCommit && latestCommit !== commitHash) {
|
|
6801
|
-
availableUpdates.push({
|
|
6802
|
-
name: skillName,
|
|
6803
|
-
source: entry.source,
|
|
6804
|
-
entry
|
|
6805
|
-
});
|
|
6806
|
-
updateDebugLog(debugUpdate, `update available for ${skillName}: local=${commitHash?.slice(0, 12)} remote=${latestCommit.slice(0, 12)}`);
|
|
6807
|
-
} else updateDebugLog(debugUpdate, `no update for ${skillName}: local=${commitHash?.slice(0, 12)} remote=${latestCommit?.slice(0, 12) || "(empty)"}`);
|
|
6808
|
-
} catch {
|
|
6809
|
-
updateDebugLog(debugUpdate, `failed to check internal skill=${skillName}`);
|
|
6810
|
-
skipped.push({
|
|
6811
|
-
name: skillName,
|
|
6812
|
-
reason: "Ali internal repository (API check failed)",
|
|
6813
|
-
sourceUrl: entry.sourceUrl
|
|
6814
|
-
});
|
|
6815
|
-
}
|
|
6816
|
-
continue;
|
|
6817
|
-
}
|
|
6818
|
-
if (!entry.skillFolderHash) {
|
|
6819
|
-
skipped.push({
|
|
6820
|
-
name: skillName,
|
|
6821
|
-
reason: getSkipReason(entry),
|
|
6822
|
-
sourceUrl: entry.sourceUrl
|
|
6823
|
-
});
|
|
6824
|
-
continue;
|
|
6825
|
-
}
|
|
6826
|
-
if (entry.sourceType === "github") {
|
|
6827
|
-
const possiblePaths = entry.skillPath ? [entry.skillPath] : [`skills/${skillName}`, `skills/${skillName}/SKILL.md`];
|
|
6828
|
-
for (const skillPath of possiblePaths) try {
|
|
6829
|
-
const latestHash = await fetchSkillFolderHash(entry.source, skillPath, token);
|
|
6830
|
-
if (latestHash === null) continue;
|
|
6831
|
-
if (latestHash !== entry.skillFolderHash) availableUpdates.push({
|
|
6832
|
-
name: skillName,
|
|
6833
|
-
source: entry.source,
|
|
6834
|
-
entry
|
|
6835
|
-
});
|
|
6836
|
-
break;
|
|
6837
|
-
} catch {
|
|
6838
|
-
continue;
|
|
6839
|
-
}
|
|
6840
|
-
continue;
|
|
6841
|
-
}
|
|
6842
|
-
try {
|
|
6843
|
-
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath || "", token);
|
|
6844
|
-
if (latestHash && latestHash !== entry.skillFolderHash) availableUpdates.push({
|
|
6845
|
-
name: skillName,
|
|
6846
|
-
source: entry.source,
|
|
6847
|
-
entry
|
|
6848
|
-
});
|
|
6849
|
-
} catch {}
|
|
6854
|
+
const updateCheck = await checkSkillsForUpdates(skillsToCheck);
|
|
6855
|
+
const migratedCount = await applySafeGithubLockMigrations(updateCheck.migrations, isGlobal);
|
|
6856
|
+
if (migratedCount > 0) console.log(`${DIM}Migrated ${migratedCount} GitHub skill version(s) to commit hashes.${RESET}`);
|
|
6857
|
+
const availableUpdates = updateCheck.updates;
|
|
6858
|
+
for (const error of updateCheck.errors) {
|
|
6859
|
+
updateDebugLog(debugUpdate, `failed to check skill=${error.name}, source=${error.source}, reason=${error.error}`);
|
|
6860
|
+
console.log(`${DIM}⚠ Could not check ${error.name} (${error.source}): ${error.error}${RESET}`);
|
|
6850
6861
|
}
|
|
6851
6862
|
if (availableUpdates.length === 0) {
|
|
6852
|
-
console.log(`${
|
|
6863
|
+
if (updateCheck.errors.length > 0) console.log(`${YELLOW}No updates confirmed; ${updateCheck.errors.length} skill(s) could not be checked.${RESET}`);
|
|
6864
|
+
else console.log(`${TEXT}✓ All ${scopeLabel} skills are up to date${RESET}`);
|
|
6853
6865
|
console.log();
|
|
6854
6866
|
return;
|
|
6855
6867
|
}
|
|
@@ -6920,8 +6932,8 @@ async function runUpdate(args = []) {
|
|
|
6920
6932
|
installUrl = `${installUrl}/tree/HEAD/${skillFolder}`;
|
|
6921
6933
|
} else installUrl = sourceUrl;
|
|
6922
6934
|
try {
|
|
6923
|
-
const
|
|
6924
|
-
if (
|
|
6935
|
+
const installTarget = resolveUpdateInstallTarget(installedAgentsBySkillName.get(update.name.toLowerCase()), isGlobal);
|
|
6936
|
+
if (installTarget.mode === "skip") {
|
|
6925
6937
|
failCount++;
|
|
6926
6938
|
safeSkipCount++;
|
|
6927
6939
|
const nearMatches = installedSkills.filter((s) => s.name.toLowerCase().includes(update.name.toLowerCase())).map((s) => s.name);
|
|
@@ -6936,9 +6948,10 @@ async function runUpdate(args = []) {
|
|
|
6936
6948
|
await runAdd([installUrl], {
|
|
6937
6949
|
yes: true,
|
|
6938
6950
|
global: isGlobal,
|
|
6939
|
-
agent:
|
|
6951
|
+
...installTarget.mode === "agents" ? { agent: installTarget.agents } : {},
|
|
6952
|
+
canonicalOnly: installTarget.mode === "canonical"
|
|
6940
6953
|
});
|
|
6941
|
-
updateDebugLog(debugUpdate, `updated ${update.name} with installUrl=${installUrl} targetAgents=${
|
|
6954
|
+
updateDebugLog(debugUpdate, `updated ${update.name} with installUrl=${installUrl} targetAgents=${installTarget.mode === "agents" ? installTarget.agents.join(",") : "(canonical-only)"}`);
|
|
6942
6955
|
successCount++;
|
|
6943
6956
|
console.log(` ${TEXT}✓${RESET} Updated ${update.name}`);
|
|
6944
6957
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ali-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"author": "",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ali/cli-skills": "^2.1.
|
|
35
|
+
"@ali/cli-skills": "^2.1.5"
|
|
36
36
|
},
|
|
37
37
|
"bundleDependencies": [
|
|
38
38
|
"@ali/cli-skills"
|