ali-skills 0.0.13 → 0.0.15
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as __toESM } from "./_chunks/rolldown-runtime.mjs";
|
|
2
|
+
import { n as __require, r as __toESM } from "./_chunks/rolldown-runtime.mjs";
|
|
3
3
|
import { l as pD, u as require_picocolors } from "./_chunks/libs/@clack/core.mjs";
|
|
4
|
-
import { a as Y, c as
|
|
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 "./_chunks/libs/@kwsites/file-exists.mjs";
|
|
6
6
|
import "./_chunks/libs/@kwsites/promise-deferred.mjs";
|
|
7
7
|
import { t as esm_default } from "./_chunks/libs/simple-git.mjs";
|
|
@@ -9,15 +9,16 @@ import { t as require_gray_matter } from "./_chunks/libs/gray-matter.mjs";
|
|
|
9
9
|
import "./_chunks/libs/extend-shallow.mjs";
|
|
10
10
|
import "./_chunks/libs/esprima.mjs";
|
|
11
11
|
import { t as xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
|
|
12
|
-
import { execSync, spawn
|
|
12
|
+
import { execSync, spawn } from "child_process";
|
|
13
13
|
import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
14
14
|
import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
15
15
|
import { homedir, platform, tmpdir } from "os";
|
|
16
16
|
import { createHash } from "crypto";
|
|
17
|
-
import { fileURLToPath } from "url";
|
|
17
|
+
import { URL as URL$1, fileURLToPath } from "url";
|
|
18
18
|
import * as readline from "readline";
|
|
19
19
|
import { Writable } from "stream";
|
|
20
20
|
import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
21
|
+
import * as http from "http";
|
|
21
22
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
22
23
|
function getOwnerRepo(parsed) {
|
|
23
24
|
if (parsed.type === "local") return null;
|
|
@@ -162,6 +163,16 @@ function parseSource(input) {
|
|
|
162
163
|
ref
|
|
163
164
|
};
|
|
164
165
|
}
|
|
166
|
+
const internalRepoWithSubpathMatch = input.match(/^(https?:\/\/(?:[^@/]+@)?(?:code|gitlab)\.alibaba-inc\.com)\/([^/]+)\/([^/]+)\/(.+)$/);
|
|
167
|
+
if (internalRepoWithSubpathMatch) {
|
|
168
|
+
const [, host, owner, repo, subpath] = internalRepoWithSubpathMatch;
|
|
169
|
+
if (!owner || !repo) throw new Error(`Invalid internal URL format: ${input}`);
|
|
170
|
+
return {
|
|
171
|
+
type: "internal",
|
|
172
|
+
url: `${host.replace("gitlab.alibaba-inc.com", "code.alibaba-inc.com")}/${owner}/${repo.replace(/\.git$/, "")}.git`,
|
|
173
|
+
subpath: sanitizeSubpath(subpath)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
165
176
|
if (isInternalUrl(input)) return {
|
|
166
177
|
type: "internal",
|
|
167
178
|
url: input.replace(/\bgitlab\.alibaba-inc\.com\b/g, "code.alibaba-inc.com")
|
|
@@ -1977,6 +1988,26 @@ async function fetchSkillFolderHash(ownerRepo, skillPath, token) {
|
|
|
1977
1988
|
}
|
|
1978
1989
|
return null;
|
|
1979
1990
|
}
|
|
1991
|
+
async function fetchAliInternalCommitHash(source, skillPath = "", token) {
|
|
1992
|
+
const branches = ["main", "master"];
|
|
1993
|
+
const privateToken = token || process.env.GITLAB_PRIVATE_TOKEN || process.env.CODE_ALIBABA_TOKEN || GITLAB_ACCOUNT.token;
|
|
1994
|
+
for (const branch of branches) try {
|
|
1995
|
+
let url = `https://code.alibaba-inc.com/api/v4/projects/${encodeURIComponent(source)}/repository/commits?ref_name=${branch}&per_page=1`;
|
|
1996
|
+
if (skillPath) url += `&path=${encodeURIComponent(skillPath)}`;
|
|
1997
|
+
const headers = {
|
|
1998
|
+
Accept: "application/json",
|
|
1999
|
+
"User-Agent": "skills-cli",
|
|
2000
|
+
"Private-Token": privateToken
|
|
2001
|
+
};
|
|
2002
|
+
const response = await fetch(url, { headers });
|
|
2003
|
+
if (!response.ok) continue;
|
|
2004
|
+
const data = await response.json();
|
|
2005
|
+
if (data && data.length > 0) return data[0].id;
|
|
2006
|
+
} catch {
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
return null;
|
|
2010
|
+
}
|
|
1980
2011
|
async function addSkillToLock(skillName, entry) {
|
|
1981
2012
|
const lock = await readSkillLock$1();
|
|
1982
2013
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2101,7 +2132,7 @@ function logSkillParseFailures(failures) {
|
|
|
2101
2132
|
if (f.hint) M.message(import_picocolors.default.yellow(` 提示: ${f.hint}`));
|
|
2102
2133
|
}
|
|
2103
2134
|
}
|
|
2104
|
-
var version$1 = "2.0.
|
|
2135
|
+
var version$1 = "2.0.15";
|
|
2105
2136
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2106
2137
|
async function isSourcePrivate(source) {
|
|
2107
2138
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -2641,7 +2672,8 @@ async function runAdd(args, options = {}) {
|
|
|
2641
2672
|
} else {
|
|
2642
2673
|
spinner.start("Cloning repository...");
|
|
2643
2674
|
try {
|
|
2644
|
-
|
|
2675
|
+
const ref = parsed.ref === "HEAD" ? void 0 : parsed.ref;
|
|
2676
|
+
tempDir = await cloneRepo(parsed.url, ref);
|
|
2645
2677
|
skillsDir = tempDir;
|
|
2646
2678
|
spinner.stop("Repository cloned");
|
|
2647
2679
|
} catch (cloneError) {
|
|
@@ -3108,11 +3140,25 @@ async function runAdd(args, options = {}) {
|
|
|
3108
3140
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
3109
3141
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
3110
3142
|
const computedHash = await computeSkillFolderHash(skill.path);
|
|
3111
|
-
|
|
3143
|
+
const skillFilePath = skillFiles[skill.name];
|
|
3144
|
+
const lockEntry = {
|
|
3112
3145
|
source: lockSource || parsed.url,
|
|
3113
3146
|
sourceType: effectiveSourceType,
|
|
3114
3147
|
computedHash
|
|
3115
|
-
}
|
|
3148
|
+
};
|
|
3149
|
+
if (effectiveSourceType === "github" && skillFilePath && normalizedSource) {
|
|
3150
|
+
const folderHash = await fetchSkillFolderHash(normalizedSource, skillFilePath, getGitHubToken());
|
|
3151
|
+
if (folderHash) lockEntry.skillFolderHash = folderHash;
|
|
3152
|
+
lockEntry.skillPath = skillFilePath.replace(/\/?SKILL\.md$/i, "").replace(/\/+$/, "");
|
|
3153
|
+
}
|
|
3154
|
+
if (effectiveSourceType === "internal") {
|
|
3155
|
+
const source = normalizedSource || getOwnerRepo(parsed) || parsed.url;
|
|
3156
|
+
const skillFolder = skillFilePath ? skillFilePath.replace(/\/?SKILL\.md$/i, "").replace(/\/+$/, "") : "";
|
|
3157
|
+
const commitHash = await fetchAliInternalCommitHash(source, skillFolder, null);
|
|
3158
|
+
if (commitHash) lockEntry.commitHash = commitHash;
|
|
3159
|
+
lockEntry.skillPath = skillFolder;
|
|
3160
|
+
}
|
|
3161
|
+
await addSkillToLocalLock(skill.name, lockEntry, cwd);
|
|
3116
3162
|
} catch {}
|
|
3117
3163
|
}
|
|
3118
3164
|
}
|
|
@@ -3254,12 +3300,12 @@ function parseAddOptions(args) {
|
|
|
3254
3300
|
options
|
|
3255
3301
|
};
|
|
3256
3302
|
}
|
|
3257
|
-
const RESET$
|
|
3303
|
+
const RESET$5 = "\x1B[0m";
|
|
3258
3304
|
const BOLD$2 = "\x1B[1m";
|
|
3259
|
-
const DIM$
|
|
3260
|
-
const TEXT$
|
|
3305
|
+
const DIM$5 = "\x1B[38;5;102m";
|
|
3306
|
+
const TEXT$4 = "\x1B[38;5;145m";
|
|
3261
3307
|
const CYAN$1 = "\x1B[36m";
|
|
3262
|
-
const YELLOW$
|
|
3308
|
+
const YELLOW$4 = "\x1B[33m";
|
|
3263
3309
|
const SKILLS_API_URL = process.env.SKILLS_API_URL;
|
|
3264
3310
|
const SEARCH_ENDPOINT = SKILLS_API_URL ? `${SKILLS_API_URL.replace(/\/+$/, "")}/search` : "https://next-ai-base.pre-fn.alibaba-inc.com/api/skills/search";
|
|
3265
3311
|
function formatInstalls(count) {
|
|
@@ -3313,29 +3359,29 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3313
3359
|
if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
|
|
3314
3360
|
process.stdout.write(CLEAR_DOWN);
|
|
3315
3361
|
const lines = [];
|
|
3316
|
-
const cursor = `${BOLD$2}_${RESET$
|
|
3317
|
-
lines.push(`${TEXT$
|
|
3362
|
+
const cursor = `${BOLD$2}_${RESET$5}`;
|
|
3363
|
+
lines.push(`${TEXT$4}Search skills:${RESET$5} ${query}${cursor}`);
|
|
3318
3364
|
lines.push("");
|
|
3319
|
-
if (!query || query.length < 2) lines.push(`${DIM$
|
|
3320
|
-
else if (results.length === 0 && loading) lines.push(`${DIM$
|
|
3321
|
-
else if (results.length === 0) lines.push(`${DIM$
|
|
3365
|
+
if (!query || query.length < 2) lines.push(`${DIM$5}Start typing to search (min 2 chars)${RESET$5}`);
|
|
3366
|
+
else if (results.length === 0 && loading) lines.push(`${DIM$5}Searching...${RESET$5}`);
|
|
3367
|
+
else if (results.length === 0) lines.push(`${DIM$5}No skills found${RESET$5}`);
|
|
3322
3368
|
else {
|
|
3323
3369
|
const visible = results.slice(0, 8);
|
|
3324
3370
|
for (let i = 0; i < visible.length; i++) {
|
|
3325
3371
|
const skill = visible[i];
|
|
3326
3372
|
const isSelected = i === selectedIndex;
|
|
3327
|
-
const arrow = isSelected ? `${BOLD$2}>${RESET$
|
|
3328
|
-
const name = isSelected ? `${BOLD$2}${skill.name}${RESET$
|
|
3329
|
-
const source = skill.source ? ` ${DIM$
|
|
3373
|
+
const arrow = isSelected ? `${BOLD$2}>${RESET$5}` : " ";
|
|
3374
|
+
const name = isSelected ? `${BOLD$2}${skill.name}${RESET$5}` : `${TEXT$4}${skill.name}${RESET$5}`;
|
|
3375
|
+
const source = skill.source ? ` ${DIM$5}${skill.source}${RESET$5}` : "";
|
|
3330
3376
|
const installs = formatInstalls(skill.installs);
|
|
3331
|
-
const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$
|
|
3332
|
-
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$
|
|
3333
|
-
const loadingIndicator = loading && i === 0 ? ` ${DIM$
|
|
3377
|
+
const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$5}` : "";
|
|
3378
|
+
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$4}GitHub${RESET$5}` : "";
|
|
3379
|
+
const loadingIndicator = loading && i === 0 ? ` ${DIM$5}...${RESET$5}` : "";
|
|
3334
3380
|
lines.push(` ${arrow} ${name}${source}${githubBadge}${installsBadge}${loadingIndicator}`);
|
|
3335
3381
|
}
|
|
3336
3382
|
}
|
|
3337
3383
|
lines.push("");
|
|
3338
|
-
lines.push(`${DIM$
|
|
3384
|
+
lines.push(`${DIM$5}up/down navigate | enter select | esc cancel${RESET$5}`);
|
|
3339
3385
|
for (const line of lines) process.stdout.write(line + "\n");
|
|
3340
3386
|
lastRenderedLines = lines.length;
|
|
3341
3387
|
}
|
|
@@ -3419,9 +3465,9 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3419
3465
|
async function runFind(args) {
|
|
3420
3466
|
const query = args.join(" ");
|
|
3421
3467
|
const isNonInteractive = !process.stdin.isTTY;
|
|
3422
|
-
const agentTip = `${DIM$
|
|
3423
|
-
${DIM$
|
|
3424
|
-
${DIM$
|
|
3468
|
+
const agentTip = `${DIM$5}Tip: if running in a coding agent, follow these steps:${RESET$5}
|
|
3469
|
+
${DIM$5} 1) npx @ali/cli-skills find [query]${RESET$5}
|
|
3470
|
+
${DIM$5} 2) npx @ali/cli-skills add <owner/repo@skill>${RESET$5}`;
|
|
3425
3471
|
if (query) {
|
|
3426
3472
|
const results = await searchSkillsAPI(query);
|
|
3427
3473
|
track({
|
|
@@ -3430,18 +3476,18 @@ ${DIM$2} 2) npx @ali/cli-skills add <owner/repo@skill>${RESET$2}`;
|
|
|
3430
3476
|
resultCount: String(results.length)
|
|
3431
3477
|
});
|
|
3432
3478
|
if (results.length === 0) {
|
|
3433
|
-
console.log(`${DIM$
|
|
3479
|
+
console.log(`${DIM$5}No skills found for "${query}"${RESET$5}`);
|
|
3434
3480
|
return;
|
|
3435
3481
|
}
|
|
3436
|
-
console.log(`${DIM$
|
|
3482
|
+
console.log(`${DIM$5}Install with${RESET$5} npx ali-skills add <owner/repo>`);
|
|
3437
3483
|
console.log();
|
|
3438
3484
|
for (const skill of results.slice(0, 6)) {
|
|
3439
3485
|
const pkg = skill.source || skill.slug;
|
|
3440
3486
|
const installs = formatInstalls(skill.installs);
|
|
3441
|
-
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$
|
|
3442
|
-
console.log(`${TEXT$
|
|
3487
|
+
const githubBadge = skill.sourceType === "github" ? ` ${YELLOW$4}GitHub${RESET$5}` : "";
|
|
3488
|
+
console.log(`${TEXT$4}${pkg}@${skill.name}${RESET$5}${githubBadge}${installs ? ` ${CYAN$1}${installs}${RESET$5}` : ""}`);
|
|
3443
3489
|
const skillUrl = buildSkillUrl(skill);
|
|
3444
|
-
console.log(`${DIM$
|
|
3490
|
+
console.log(`${DIM$5}└ ${skillUrl}${RESET$5}`);
|
|
3445
3491
|
console.log();
|
|
3446
3492
|
}
|
|
3447
3493
|
return;
|
|
@@ -3458,14 +3504,14 @@ ${DIM$2} 2) npx @ali/cli-skills add <owner/repo@skill>${RESET$2}`;
|
|
|
3458
3504
|
interactive: "1"
|
|
3459
3505
|
});
|
|
3460
3506
|
if (!selected) {
|
|
3461
|
-
console.log(`${DIM$
|
|
3507
|
+
console.log(`${DIM$5}Search cancelled${RESET$5}`);
|
|
3462
3508
|
console.log();
|
|
3463
3509
|
return;
|
|
3464
3510
|
}
|
|
3465
3511
|
const pkg = selected.source || selected.slug;
|
|
3466
3512
|
const skillName = selected.name;
|
|
3467
3513
|
console.log();
|
|
3468
|
-
console.log(`${TEXT$
|
|
3514
|
+
console.log(`${TEXT$4}Installing ${BOLD$2}${skillName}${RESET$5} from ${DIM$5}${pkg}${RESET$5}...`);
|
|
3469
3515
|
console.log();
|
|
3470
3516
|
const { source, options } = parseAddOptions([
|
|
3471
3517
|
pkg,
|
|
@@ -3475,7 +3521,7 @@ ${DIM$2} 2) npx @ali/cli-skills add <owner/repo@skill>${RESET$2}`;
|
|
|
3475
3521
|
await runAdd(source, options);
|
|
3476
3522
|
console.log();
|
|
3477
3523
|
const skillUrl = buildSkillUrl(selected);
|
|
3478
|
-
console.log(`${DIM$
|
|
3524
|
+
console.log(`${DIM$5}View the skill at${RESET$5} ${TEXT$4}${skillUrl}${RESET$5}`);
|
|
3479
3525
|
console.log();
|
|
3480
3526
|
}
|
|
3481
3527
|
const isCancelled = (value) => typeof value === "symbol";
|
|
@@ -3813,11 +3859,11 @@ async function runInstallFromLock(args) {
|
|
|
3813
3859
|
}
|
|
3814
3860
|
}
|
|
3815
3861
|
}
|
|
3816
|
-
const RESET$
|
|
3862
|
+
const RESET$4 = "\x1B[0m";
|
|
3817
3863
|
const BOLD$1 = "\x1B[1m";
|
|
3818
|
-
const DIM$
|
|
3864
|
+
const DIM$4 = "\x1B[38;5;102m";
|
|
3819
3865
|
const CYAN = "\x1B[36m";
|
|
3820
|
-
const YELLOW = "\x1B[33m";
|
|
3866
|
+
const YELLOW$3 = "\x1B[33m";
|
|
3821
3867
|
function shortenPath(fullPath, cwd) {
|
|
3822
3868
|
const home = homedir();
|
|
3823
3869
|
if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
|
|
@@ -3851,8 +3897,8 @@ async function runList(args) {
|
|
|
3851
3897
|
const validAgents = Object.keys(agents);
|
|
3852
3898
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
3853
3899
|
if (invalidAgents.length > 0) {
|
|
3854
|
-
console.log(`${YELLOW}Invalid agents: ${invalidAgents.join(", ")}${RESET$
|
|
3855
|
-
console.log(`${DIM$
|
|
3900
|
+
console.log(`${YELLOW$3}Invalid agents: ${invalidAgents.join(", ")}${RESET$4}`);
|
|
3901
|
+
console.log(`${DIM$4}Valid agents: ${validAgents.join(", ")}${RESET$4}`);
|
|
3856
3902
|
process.exit(1);
|
|
3857
3903
|
}
|
|
3858
3904
|
agentFilter = options.agent;
|
|
@@ -3879,20 +3925,20 @@ async function runList(args) {
|
|
|
3879
3925
|
console.log("[]");
|
|
3880
3926
|
return;
|
|
3881
3927
|
}
|
|
3882
|
-
console.log(`${DIM$
|
|
3883
|
-
if (scope) console.log(`${DIM$
|
|
3884
|
-
else console.log(`${DIM$
|
|
3928
|
+
console.log(`${DIM$4}No ${scopeLabel.toLowerCase()} skills found.${RESET$4}`);
|
|
3929
|
+
if (scope) console.log(`${DIM$4}Try listing project skills without -g${RESET$4}`);
|
|
3930
|
+
else console.log(`${DIM$4}Try listing global skills with -g${RESET$4}`);
|
|
3885
3931
|
return;
|
|
3886
3932
|
}
|
|
3887
3933
|
function printSkill(skill, indent = false) {
|
|
3888
3934
|
const prefix = indent ? " " : "";
|
|
3889
3935
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
3890
3936
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
3891
|
-
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW}not linked${RESET$
|
|
3892
|
-
console.log(`${prefix}${CYAN}${skill.name}${RESET$
|
|
3893
|
-
console.log(`${prefix} ${DIM$
|
|
3937
|
+
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$3}not linked${RESET$4}`;
|
|
3938
|
+
console.log(`${prefix}${CYAN}${skill.name}${RESET$4} ${DIM$4}${shortPath}${RESET$4}`);
|
|
3939
|
+
console.log(`${prefix} ${DIM$4}Agents:${RESET$4} ${agentInfo}`);
|
|
3894
3940
|
}
|
|
3895
|
-
console.log(`${BOLD$1}${scopeLabel} Skills${RESET$
|
|
3941
|
+
console.log(`${BOLD$1}${scopeLabel} Skills${RESET$4}`);
|
|
3896
3942
|
console.log();
|
|
3897
3943
|
const groupedSkills = {};
|
|
3898
3944
|
const ungroupedSkills = [];
|
|
@@ -3908,13 +3954,13 @@ async function runList(args) {
|
|
|
3908
3954
|
const sortedGroups = Object.keys(groupedSkills).sort();
|
|
3909
3955
|
for (const group of sortedGroups) {
|
|
3910
3956
|
const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3911
|
-
console.log(`${BOLD$1}${title}${RESET$
|
|
3957
|
+
console.log(`${BOLD$1}${title}${RESET$4}`);
|
|
3912
3958
|
const skills = groupedSkills[group];
|
|
3913
3959
|
if (skills) for (const skill of skills) printSkill(skill, true);
|
|
3914
3960
|
console.log();
|
|
3915
3961
|
}
|
|
3916
3962
|
if (ungroupedSkills.length > 0) {
|
|
3917
|
-
console.log(`${BOLD$1}General${RESET$
|
|
3963
|
+
console.log(`${BOLD$1}General${RESET$4}`);
|
|
3918
3964
|
for (const skill of ungroupedSkills) printSkill(skill, true);
|
|
3919
3965
|
console.log();
|
|
3920
3966
|
}
|
|
@@ -4397,6 +4443,758 @@ function parsePublishOptions(args) {
|
|
|
4397
4443
|
}
|
|
4398
4444
|
return { options };
|
|
4399
4445
|
}
|
|
4446
|
+
const CONFIG_DIR = join(homedir(), ".ali-skills");
|
|
4447
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
4448
|
+
const AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
4449
|
+
function ensureConfigDir() {
|
|
4450
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, {
|
|
4451
|
+
recursive: true,
|
|
4452
|
+
mode: 448
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
function safeWriteFile(path, data) {
|
|
4456
|
+
ensureConfigDir();
|
|
4457
|
+
writeFileSync(path, data, { mode: 384 });
|
|
4458
|
+
}
|
|
4459
|
+
function readConfig() {
|
|
4460
|
+
try {
|
|
4461
|
+
if (!existsSync(CONFIG_FILE)) return {
|
|
4462
|
+
version: 1,
|
|
4463
|
+
gatewayUrl: getGatewayUrl(),
|
|
4464
|
+
defaultTimeout: 3e4
|
|
4465
|
+
};
|
|
4466
|
+
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
4467
|
+
return JSON.parse(content);
|
|
4468
|
+
} catch {
|
|
4469
|
+
return {
|
|
4470
|
+
version: 1,
|
|
4471
|
+
gatewayUrl: getGatewayUrl(),
|
|
4472
|
+
defaultTimeout: 3e4
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
function saveConfig(config) {
|
|
4477
|
+
safeWriteFile(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
4478
|
+
}
|
|
4479
|
+
function readAuth() {
|
|
4480
|
+
try {
|
|
4481
|
+
if (!existsSync(AUTH_FILE)) return null;
|
|
4482
|
+
const content = readFileSync(AUTH_FILE, "utf-8");
|
|
4483
|
+
return JSON.parse(content);
|
|
4484
|
+
} catch {
|
|
4485
|
+
return null;
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
function saveAuth(auth) {
|
|
4489
|
+
const data = {
|
|
4490
|
+
version: 1,
|
|
4491
|
+
...auth
|
|
4492
|
+
};
|
|
4493
|
+
safeWriteFile(AUTH_FILE, JSON.stringify(data, null, 2));
|
|
4494
|
+
}
|
|
4495
|
+
function removeAuth() {
|
|
4496
|
+
try {
|
|
4497
|
+
if (existsSync(AUTH_FILE)) {
|
|
4498
|
+
const { unlinkSync } = __require("fs");
|
|
4499
|
+
unlinkSync(AUTH_FILE);
|
|
4500
|
+
}
|
|
4501
|
+
} catch {}
|
|
4502
|
+
}
|
|
4503
|
+
function isAuthExpired(auth) {
|
|
4504
|
+
return Date.now() >= auth.expiresAt - 10080 * 60 * 1e3;
|
|
4505
|
+
}
|
|
4506
|
+
function getPrivateToken() {
|
|
4507
|
+
return readAuth()?.privateToken || null;
|
|
4508
|
+
}
|
|
4509
|
+
function getGatewayUrl() {
|
|
4510
|
+
const envUrl = process.env.SKILLS_API_URL || process.env.ALI_GATEWAY_URL;
|
|
4511
|
+
if (envUrl) switch (envUrl.toLowerCase()) {
|
|
4512
|
+
case "local": return "http://localhost:3000";
|
|
4513
|
+
case "daily": return "https://next-ai-base.pre-fn.alibaba-inc.com";
|
|
4514
|
+
case "pre": return "https://ali-skills.pre.alibaba-inc.com";
|
|
4515
|
+
case "prod": return "https://ali-skills.alibaba-inc.com";
|
|
4516
|
+
default: return envUrl;
|
|
4517
|
+
}
|
|
4518
|
+
return "https://ali-skills.alibaba-inc.com";
|
|
4519
|
+
}
|
|
4520
|
+
function getConfigFilePath() {
|
|
4521
|
+
return CONFIG_FILE;
|
|
4522
|
+
}
|
|
4523
|
+
function setPlatformToken(platform, data) {
|
|
4524
|
+
const config = readConfig();
|
|
4525
|
+
if (!config.platforms) config.platforms = {};
|
|
4526
|
+
config.platforms[platform] = data;
|
|
4527
|
+
saveConfig(config);
|
|
4528
|
+
}
|
|
4529
|
+
function removePlatformToken(platform) {
|
|
4530
|
+
const config = readConfig();
|
|
4531
|
+
if (config.platforms && config.platforms[platform]) {
|
|
4532
|
+
delete config.platforms[platform];
|
|
4533
|
+
saveConfig(config);
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
const DIM$3 = "\x1B[38;5;102m";
|
|
4537
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
4538
|
+
const RESET$3 = "\x1B[0m";
|
|
4539
|
+
const GREEN$2 = "\x1B[32m";
|
|
4540
|
+
const YELLOW$2 = "\x1B[33m";
|
|
4541
|
+
function startLocalServer() {
|
|
4542
|
+
return new Promise((resolve) => {
|
|
4543
|
+
let resolveCallback;
|
|
4544
|
+
const callbackPromise = new Promise((resolve) => {
|
|
4545
|
+
resolveCallback = resolve;
|
|
4546
|
+
});
|
|
4547
|
+
const server = http.createServer((req, res) => {
|
|
4548
|
+
const url = new URL$1(req.url || "/", `http://localhost`);
|
|
4549
|
+
if (url.pathname === "/callback") {
|
|
4550
|
+
const empId = url.searchParams.get("emp_id") || "";
|
|
4551
|
+
const account = url.searchParams.get("account") || "";
|
|
4552
|
+
const name = url.searchParams.get("name") || "";
|
|
4553
|
+
const privateToken = url.searchParams.get("private_token") || "";
|
|
4554
|
+
const ssoRefreshToken = url.searchParams.get("sso_refresh_token") || "";
|
|
4555
|
+
const expiresAt = url.searchParams.get("expires_at") || "0";
|
|
4556
|
+
if (!empId || !privateToken) {
|
|
4557
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4558
|
+
res.end(`
|
|
4559
|
+
<html>
|
|
4560
|
+
<body style="font-family: sans-serif; text-align: center; padding: 50px;">
|
|
4561
|
+
<h1 style="color: #ff4d4f;">登录失败</h1>
|
|
4562
|
+
<p>缺少必要的参数</p>
|
|
4563
|
+
</body>
|
|
4564
|
+
</html>
|
|
4565
|
+
`);
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4569
|
+
res.end(`
|
|
4570
|
+
<html>
|
|
4571
|
+
<head><meta charset='UTF-8'><title>登录成功</title>
|
|
4572
|
+
<style>
|
|
4573
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; text-align: center; padding: 50px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; min-height: 100vh; margin: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; }
|
|
4574
|
+
.card { background: rgba(255, 255, 255, 0.95); padding: 40px; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); color: #333; max-width: 400px; }
|
|
4575
|
+
h1 { margin: 0 0 16px 0; color: #52c41a; }
|
|
4576
|
+
.icon { font-size: 64px; margin-bottom: 16px; }
|
|
4577
|
+
.user-info { background: #f6ffed; border: 1px solid #b7eb8f; border-radius: 8px; padding: 16px; margin: 16px 0; text-align: left; }
|
|
4578
|
+
.user-info p { margin: 8px 0; }
|
|
4579
|
+
.hint { color: #666; font-size: 14px; margin-top: 24px; }
|
|
4580
|
+
</style>
|
|
4581
|
+
</head>
|
|
4582
|
+
<body>
|
|
4583
|
+
<div class="card">
|
|
4584
|
+
<div class="icon">✓</div>
|
|
4585
|
+
<h1>登录成功</h1>
|
|
4586
|
+
<div class="user-info">
|
|
4587
|
+
<p><strong>姓名:</strong> ${name}</p>
|
|
4588
|
+
<p><strong>账号:</strong> ${account}</p>
|
|
4589
|
+
<p><strong>工号:</strong> ${empId}</p>
|
|
4590
|
+
</div>
|
|
4591
|
+
<p class="hint">您可以关闭此页面并返回 CLI</p>
|
|
4592
|
+
</div>
|
|
4593
|
+
</body>
|
|
4594
|
+
</html>
|
|
4595
|
+
`);
|
|
4596
|
+
resolveCallback({
|
|
4597
|
+
empId,
|
|
4598
|
+
account,
|
|
4599
|
+
name,
|
|
4600
|
+
privateToken,
|
|
4601
|
+
ssoRefreshToken,
|
|
4602
|
+
expiresAt: parseInt(expiresAt, 10)
|
|
4603
|
+
});
|
|
4604
|
+
setTimeout(() => server.close(), 1e3);
|
|
4605
|
+
} else {
|
|
4606
|
+
res.writeHead(404);
|
|
4607
|
+
res.end("Not Found");
|
|
4608
|
+
}
|
|
4609
|
+
});
|
|
4610
|
+
server.listen(0, "localhost", () => {
|
|
4611
|
+
const port = server.address().port;
|
|
4612
|
+
resolve({
|
|
4613
|
+
port,
|
|
4614
|
+
waitForCallback: () => callbackPromise,
|
|
4615
|
+
close: () => server.close()
|
|
4616
|
+
});
|
|
4617
|
+
});
|
|
4618
|
+
});
|
|
4619
|
+
}
|
|
4620
|
+
function openBrowser$2(url) {
|
|
4621
|
+
const platform = process.platform;
|
|
4622
|
+
spawn(platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open", [url], {
|
|
4623
|
+
detached: true,
|
|
4624
|
+
stdio: "ignore"
|
|
4625
|
+
});
|
|
4626
|
+
}
|
|
4627
|
+
async function login() {
|
|
4628
|
+
const existingAuth = readAuth();
|
|
4629
|
+
if (existingAuth && !isAuthExpired(existingAuth)) {
|
|
4630
|
+
console.log(`${TEXT$3}您已经登录为: ${existingAuth.name} (${existingAuth.account})${RESET$3}`);
|
|
4631
|
+
console.log(`${DIM$3}如需重新登录,请先执行: npx ali-skills logout${RESET$3}`);
|
|
4632
|
+
return;
|
|
4633
|
+
}
|
|
4634
|
+
console.log(`${TEXT$3}正在启动本地服务...${RESET$3}`);
|
|
4635
|
+
const { port, waitForCallback, close } = await startLocalServer();
|
|
4636
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
4637
|
+
const gatewayUrl = getGatewayUrl();
|
|
4638
|
+
const loginUrl = `${gatewayUrl}/api/gateway/login?callback_url=${encodeURIComponent(callbackUrl)}&type=cli`;
|
|
4639
|
+
console.log(`${TEXT$3}正在打开浏览器进行 BUC 授权...${RESET$3}`);
|
|
4640
|
+
console.log(`${DIM$3}如果浏览器没有自动打开,请手动访问:${RESET$3}`);
|
|
4641
|
+
console.log(`${DIM$3}${loginUrl}${RESET$3}`);
|
|
4642
|
+
openBrowser$2(loginUrl);
|
|
4643
|
+
console.log(`${TEXT$3}等待授权完成...${RESET$3}`);
|
|
4644
|
+
try {
|
|
4645
|
+
const { empId, account, name, privateToken, ssoRefreshToken, expiresAt } = await waitForCallback();
|
|
4646
|
+
saveAuth({
|
|
4647
|
+
empId,
|
|
4648
|
+
account,
|
|
4649
|
+
name,
|
|
4650
|
+
privateToken,
|
|
4651
|
+
ssoRefreshToken,
|
|
4652
|
+
expiresAt: expiresAt * 1e3,
|
|
4653
|
+
gatewayUrl
|
|
4654
|
+
});
|
|
4655
|
+
console.log();
|
|
4656
|
+
console.log(`${GREEN$2}✓ 登录成功!${RESET$3}`);
|
|
4657
|
+
console.log(`${TEXT$3}欢迎: ${name} (${account})${RESET$3}`);
|
|
4658
|
+
console.log(`${DIM$3}Token 有效期至: ${(/* @__PURE__ */ new Date(expiresAt * 1e3)).toLocaleDateString()}${RESET$3}`);
|
|
4659
|
+
if (!ssoRefreshToken) console.log(`${DIM$3}注意: SSO_REFRESH_TOKEN 未获取,需要时将从服务端获取${RESET$3}`);
|
|
4660
|
+
} catch (error) {
|
|
4661
|
+
close();
|
|
4662
|
+
throw error;
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
async function logout() {
|
|
4666
|
+
if (!readAuth()) {
|
|
4667
|
+
console.log(`${DIM$3}您尚未登录${RESET$3}`);
|
|
4668
|
+
return;
|
|
4669
|
+
}
|
|
4670
|
+
removeAuth();
|
|
4671
|
+
console.log(`${GREEN$2}✓ 已登出${RESET$3}`);
|
|
4672
|
+
}
|
|
4673
|
+
async function status() {
|
|
4674
|
+
const auth = readAuth();
|
|
4675
|
+
if (!auth) {
|
|
4676
|
+
console.log(`${DIM$3}您尚未登录${RESET$3}`);
|
|
4677
|
+
console.log(`${TEXT$3}请执行: npx ali-skills login${RESET$3}`);
|
|
4678
|
+
return;
|
|
4679
|
+
}
|
|
4680
|
+
const expired = isAuthExpired(auth);
|
|
4681
|
+
const remaining = auth.expiresAt - Date.now();
|
|
4682
|
+
const remainingDays = Math.max(0, Math.floor(remaining / (1440 * 60 * 1e3)));
|
|
4683
|
+
console.log(`${TEXT$3}登录状态:${RESET$3}`);
|
|
4684
|
+
console.log(` 用户: ${auth.name} (${auth.account})`);
|
|
4685
|
+
console.log(` 工号: ${auth.empId}`);
|
|
4686
|
+
console.log(` 网关: ${auth.gatewayUrl}`);
|
|
4687
|
+
if (expired) {
|
|
4688
|
+
console.log(` Token: ${YELLOW$2}即将过期${RESET$3}`);
|
|
4689
|
+
console.log(`${TEXT$3}建议重新登录: npx ali-skills login${RESET$3}`);
|
|
4690
|
+
} else console.log(` Token: ${GREEN$2}有效${RESET$3} (还剩 ${remainingDays} 天)`);
|
|
4691
|
+
}
|
|
4692
|
+
async function ensureLogin() {
|
|
4693
|
+
const auth = readAuth();
|
|
4694
|
+
if (!auth) throw new Error("未登录,请先执行: npx ali-skills login");
|
|
4695
|
+
if (isAuthExpired(auth)) throw new Error("登录已过期,请重新执行: npx ali-skills login");
|
|
4696
|
+
return {
|
|
4697
|
+
privateToken: auth.privateToken,
|
|
4698
|
+
ssoRefreshToken: auth.ssoRefreshToken
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
const PLATFORMS = {
|
|
4702
|
+
code: {
|
|
4703
|
+
name: "Code (GitLab)",
|
|
4704
|
+
host: "code.alibaba-inc.com",
|
|
4705
|
+
aliases: ["gitlab", "git"],
|
|
4706
|
+
authType: "private_token",
|
|
4707
|
+
tokenHeaderName: "PRIVATE-TOKEN",
|
|
4708
|
+
tokenDocUrl: "https://code.alibaba-inc.com/profile/account"
|
|
4709
|
+
},
|
|
4710
|
+
yuque: {
|
|
4711
|
+
name: "语雀",
|
|
4712
|
+
host: "aliyuque.antfin.com",
|
|
4713
|
+
aliases: ["yuque"],
|
|
4714
|
+
authType: "private_token",
|
|
4715
|
+
tokenHeaderName: "X-Auth-Token",
|
|
4716
|
+
tokenDocUrl: "https://aliyuque.antfin.com/settings/tokens"
|
|
4717
|
+
},
|
|
4718
|
+
o2: {
|
|
4719
|
+
name: "O2 平台",
|
|
4720
|
+
host: "o2.alibaba-inc.com",
|
|
4721
|
+
authType: "app_code+buc",
|
|
4722
|
+
appCode: process.env.O2_APP_CODE || "SYKcE5kklxmOS7Z2XatNn"
|
|
4723
|
+
},
|
|
4724
|
+
o2open: {
|
|
4725
|
+
name: "O2 开放接口",
|
|
4726
|
+
host: "open.o2.alibaba-inc.com",
|
|
4727
|
+
authType: "app_code",
|
|
4728
|
+
appCode: process.env.O2_APP_CODE || "cli-skills"
|
|
4729
|
+
},
|
|
4730
|
+
aone: {
|
|
4731
|
+
name: "Aone",
|
|
4732
|
+
host: "aone.alibaba-inc.com",
|
|
4733
|
+
authType: "buc_sso_refresh",
|
|
4734
|
+
ssoType: "refresh_token"
|
|
4735
|
+
},
|
|
4736
|
+
aonedevops: {
|
|
4737
|
+
name: "Aone DevOps",
|
|
4738
|
+
host: "devops.alibaba-inc.com",
|
|
4739
|
+
authType: "buc_sso_refresh",
|
|
4740
|
+
ssoType: "refresh_token"
|
|
4741
|
+
}
|
|
4742
|
+
};
|
|
4743
|
+
function detectPlatformWithKey(hostname) {
|
|
4744
|
+
for (const [key, config] of Object.entries(PLATFORMS)) {
|
|
4745
|
+
if (config.host === hostname) return {
|
|
4746
|
+
key,
|
|
4747
|
+
config
|
|
4748
|
+
};
|
|
4749
|
+
if (config.aliases?.some((alias) => hostname.includes(alias))) return {
|
|
4750
|
+
key,
|
|
4751
|
+
config
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
for (const [key, config] of Object.entries(PLATFORMS)) if (hostname.endsWith(config.host) || hostname === config.host) return {
|
|
4755
|
+
key,
|
|
4756
|
+
config
|
|
4757
|
+
};
|
|
4758
|
+
return null;
|
|
4759
|
+
}
|
|
4760
|
+
function getPlatform(key) {
|
|
4761
|
+
if (PLATFORMS[key]) return PLATFORMS[key];
|
|
4762
|
+
for (const [, config] of Object.entries(PLATFORMS)) if (config.aliases?.includes(key)) return config;
|
|
4763
|
+
return null;
|
|
4764
|
+
}
|
|
4765
|
+
function listPlatforms() {
|
|
4766
|
+
return Object.entries(PLATFORMS).map(([key, config]) => ({
|
|
4767
|
+
key,
|
|
4768
|
+
config
|
|
4769
|
+
}));
|
|
4770
|
+
}
|
|
4771
|
+
const DIM$2 = "\x1B[38;5;102m";
|
|
4772
|
+
const TEXT$2 = "\x1B[38;5;145m";
|
|
4773
|
+
const RESET$2 = "\x1B[0m";
|
|
4774
|
+
const GREEN$1 = "\x1B[32m";
|
|
4775
|
+
const RED$1 = "\x1B[31m";
|
|
4776
|
+
const YELLOW$1 = "\x1B[33m";
|
|
4777
|
+
function openBrowser$1(url) {
|
|
4778
|
+
const platform = process.platform;
|
|
4779
|
+
spawn(platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open", [url], {
|
|
4780
|
+
detached: true,
|
|
4781
|
+
stdio: "ignore"
|
|
4782
|
+
});
|
|
4783
|
+
}
|
|
4784
|
+
function detectAuthMode(platformKey, platform, hostname) {
|
|
4785
|
+
if (platform.authType === "private_token") return "private_token";
|
|
4786
|
+
if (platformKey === "o2" || platform.authType === "app_code+buc") return "app_code+buc";
|
|
4787
|
+
if (platform.authType === "app_code") return "app_code";
|
|
4788
|
+
if (hostname.endsWith(".alibaba-inc.com") || hostname === "alibaba-inc.com") return "sso_cookie";
|
|
4789
|
+
return "sso_ticket";
|
|
4790
|
+
}
|
|
4791
|
+
function getAuthModeDescription(mode) {
|
|
4792
|
+
switch (mode) {
|
|
4793
|
+
case "private_token": return "Private Token";
|
|
4794
|
+
case "app_code": return "App Code";
|
|
4795
|
+
case "app_code+buc": return "App Code + BUC SSO";
|
|
4796
|
+
case "sso_cookie": return "SSO Cookie (REFRESH_TOKEN)";
|
|
4797
|
+
case "sso_ticket": return "SSO Ticket";
|
|
4798
|
+
case "gateway": return "Gateway Proxy";
|
|
4799
|
+
default: return "Auto";
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
function getPlatformPrivateToken(platformKey, explicitToken) {
|
|
4803
|
+
if (explicitToken) return explicitToken;
|
|
4804
|
+
const envVarName = `ALI_SKILLS_${platformKey.toUpperCase()}_TOKEN`;
|
|
4805
|
+
const envToken = process.env[envVarName] || process.env[`${platformKey.toUpperCase()}_TOKEN`];
|
|
4806
|
+
if (envToken) return envToken;
|
|
4807
|
+
return readConfig().platforms?.[platformKey]?.privateToken || null;
|
|
4808
|
+
}
|
|
4809
|
+
async function promptConfigureToken(platformKey, platform) {
|
|
4810
|
+
console.log();
|
|
4811
|
+
console.log(`${YELLOW$1}⚠️ ${platform.name} 需要配置 Private Token${RESET$2}`);
|
|
4812
|
+
if (platform.tokenDocUrl) console.log(`${DIM$2}Token 获取地址: ${platform.tokenDocUrl}${RESET$2}`);
|
|
4813
|
+
if (!await ye({
|
|
4814
|
+
message: "是否现在配置?",
|
|
4815
|
+
initialValue: true
|
|
4816
|
+
})) {
|
|
4817
|
+
console.log(`${DIM$2}已取消配置${RESET$2}`);
|
|
4818
|
+
return null;
|
|
4819
|
+
}
|
|
4820
|
+
if (platform.tokenDocUrl) {
|
|
4821
|
+
console.log(`${TEXT$2}正在打开浏览器...${RESET$2}`);
|
|
4822
|
+
openBrowser$1(platform.tokenDocUrl);
|
|
4823
|
+
}
|
|
4824
|
+
const token = await ge({
|
|
4825
|
+
message: `请输入 ${platform.name} 的 Private Token:`,
|
|
4826
|
+
mask: "*"
|
|
4827
|
+
});
|
|
4828
|
+
if (pD(token) || !token) {
|
|
4829
|
+
console.log(`${DIM$2}已取消配置${RESET$2}`);
|
|
4830
|
+
return null;
|
|
4831
|
+
}
|
|
4832
|
+
setPlatformToken(platformKey, { privateToken: token });
|
|
4833
|
+
console.log(`${GREEN$1}✓ ${platform.name} Token 已保存${RESET$2}`);
|
|
4834
|
+
return token;
|
|
4835
|
+
}
|
|
4836
|
+
async function sendDirectRequest(targetUrl, platform, token, options) {
|
|
4837
|
+
const requestHeaders = {
|
|
4838
|
+
"User-Agent": "ali-skills-cli/1.0",
|
|
4839
|
+
[platform.tokenHeaderName]: token
|
|
4840
|
+
};
|
|
4841
|
+
if (options.headers) Object.entries(options.headers).forEach(([key, value]) => {
|
|
4842
|
+
if (key.toLowerCase() !== platform.tokenHeaderName?.toLowerCase()) requestHeaders[key] = value;
|
|
4843
|
+
});
|
|
4844
|
+
let body;
|
|
4845
|
+
if (options.body) {
|
|
4846
|
+
body = options.body;
|
|
4847
|
+
if (!requestHeaders["Content-Type"]) requestHeaders["Content-Type"] = "application/json";
|
|
4848
|
+
}
|
|
4849
|
+
if (!options.quiet) {
|
|
4850
|
+
console.log(`${DIM$2}${options.method.toUpperCase()} ${targetUrl}${RESET$2}`);
|
|
4851
|
+
console.log(`${DIM$2}→ 直接请求 (Header: ${platform.tokenHeaderName})${RESET$2}`);
|
|
4852
|
+
}
|
|
4853
|
+
const controller = new AbortController();
|
|
4854
|
+
const timeout = options.timeout || 3e4;
|
|
4855
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
4856
|
+
const response = await fetch(targetUrl, {
|
|
4857
|
+
method: options.method.toUpperCase(),
|
|
4858
|
+
headers: requestHeaders,
|
|
4859
|
+
body: body || void 0,
|
|
4860
|
+
signal: controller.signal
|
|
4861
|
+
});
|
|
4862
|
+
clearTimeout(timeoutId);
|
|
4863
|
+
const responseBody = await response.text();
|
|
4864
|
+
if (!options.quiet) {
|
|
4865
|
+
const statusColor = response.ok ? GREEN$1 : RED$1;
|
|
4866
|
+
console.log(`${statusColor}${response.status} ${response.statusText}${RESET$2}`);
|
|
4867
|
+
if (options.includeHeaders) {
|
|
4868
|
+
console.log();
|
|
4869
|
+
console.log(`${TEXT$2}Response Headers:${RESET$2}`);
|
|
4870
|
+
response.headers.forEach((value, key) => {
|
|
4871
|
+
console.log(` ${key}: ${value}`);
|
|
4872
|
+
});
|
|
4873
|
+
console.log();
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
let outputBody = responseBody;
|
|
4877
|
+
if (!options.raw && options.pretty !== false) try {
|
|
4878
|
+
const json = JSON.parse(responseBody);
|
|
4879
|
+
outputBody = JSON.stringify(json, null, 2);
|
|
4880
|
+
} catch {}
|
|
4881
|
+
if (options.output) {
|
|
4882
|
+
const { writeFileSync } = __require("fs");
|
|
4883
|
+
writeFileSync(options.output, outputBody);
|
|
4884
|
+
console.log(`${GREEN$1}✓ 已保存到 ${options.output}${RESET$2}`);
|
|
4885
|
+
} else console.log(outputBody);
|
|
4886
|
+
if (!response.ok) process.exit(1);
|
|
4887
|
+
}
|
|
4888
|
+
async function sendGatewayRequest(targetUrl, platformKey, options, authMode = "gateway") {
|
|
4889
|
+
let credentials;
|
|
4890
|
+
try {
|
|
4891
|
+
credentials = await ensureLogin();
|
|
4892
|
+
} catch (error) {
|
|
4893
|
+
console.error(`${RED$1}Error: ${error.message}${RESET$2}`);
|
|
4894
|
+
process.exit(1);
|
|
4895
|
+
}
|
|
4896
|
+
const proxyUrl = `${getGatewayUrl()}/api/gateway/proxy`;
|
|
4897
|
+
const requestHeaders = {
|
|
4898
|
+
Authorization: `Bearer ${credentials.privateToken}`,
|
|
4899
|
+
"X-Private-Token": credentials.privateToken,
|
|
4900
|
+
"X-Target-Url": targetUrl,
|
|
4901
|
+
"X-Target-Method": options.method.toUpperCase(),
|
|
4902
|
+
"X-Platform": platformKey,
|
|
4903
|
+
"X-Auth-Mode": authMode
|
|
4904
|
+
};
|
|
4905
|
+
if (options.headers) Object.entries(options.headers).forEach(([key, value]) => {
|
|
4906
|
+
requestHeaders[key] = value;
|
|
4907
|
+
});
|
|
4908
|
+
let body;
|
|
4909
|
+
if (options.body) {
|
|
4910
|
+
body = options.body;
|
|
4911
|
+
if (!requestHeaders["Content-Type"]) requestHeaders["Content-Type"] = "application/json";
|
|
4912
|
+
}
|
|
4913
|
+
if (!options.quiet) {
|
|
4914
|
+
console.log(`${DIM$2}${options.method.toUpperCase()} ${targetUrl}${RESET$2}`);
|
|
4915
|
+
console.log(`${DIM$2}→ Gateway: ${proxyUrl}${RESET$2}`);
|
|
4916
|
+
}
|
|
4917
|
+
const controller = new AbortController();
|
|
4918
|
+
const timeout = options.timeout || 3e4;
|
|
4919
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
4920
|
+
const response = await fetch(proxyUrl, {
|
|
4921
|
+
method: "POST",
|
|
4922
|
+
headers: requestHeaders,
|
|
4923
|
+
body: body || void 0,
|
|
4924
|
+
signal: controller.signal
|
|
4925
|
+
});
|
|
4926
|
+
clearTimeout(timeoutId);
|
|
4927
|
+
if (response.status === 401) {
|
|
4928
|
+
if ((await response.json().catch(() => ({
|
|
4929
|
+
error: "Unauthorized",
|
|
4930
|
+
code: "UNKNOWN"
|
|
4931
|
+
}))).code === "INVALID_TOKEN") {
|
|
4932
|
+
console.error(`${RED$1}Error: Token 已失效,请重新登录${RESET$2}`);
|
|
4933
|
+
console.log(`${TEXT$2}执行: npx ali-skills login${RESET$2}`);
|
|
4934
|
+
process.exit(1);
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
const responseBody = await response.text();
|
|
4938
|
+
const requestId = response.headers.get("X-Gateway-Request-Id");
|
|
4939
|
+
if (!options.quiet) {
|
|
4940
|
+
const statusColor = response.ok ? GREEN$1 : RED$1;
|
|
4941
|
+
console.log(`${statusColor}${response.status} ${response.statusText}${RESET$2}`);
|
|
4942
|
+
if (requestId) console.log(`${DIM$2}Request ID: ${requestId}${RESET$2}`);
|
|
4943
|
+
if (options.includeHeaders) {
|
|
4944
|
+
console.log();
|
|
4945
|
+
console.log(`${TEXT$2}Response Headers:${RESET$2}`);
|
|
4946
|
+
response.headers.forEach((value, key) => {
|
|
4947
|
+
console.log(` ${key}: ${value}`);
|
|
4948
|
+
});
|
|
4949
|
+
console.log();
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
let outputBody = responseBody;
|
|
4953
|
+
if (!options.raw && options.pretty !== false) try {
|
|
4954
|
+
const json = JSON.parse(responseBody);
|
|
4955
|
+
outputBody = JSON.stringify(json, null, 2);
|
|
4956
|
+
} catch {}
|
|
4957
|
+
if (options.output) {
|
|
4958
|
+
const { writeFileSync } = __require("fs");
|
|
4959
|
+
writeFileSync(options.output, outputBody);
|
|
4960
|
+
console.log(`${GREEN$1}✓ 已保存到 ${options.output}${RESET$2}`);
|
|
4961
|
+
} else console.log(outputBody);
|
|
4962
|
+
if (!response.ok) process.exit(1);
|
|
4963
|
+
}
|
|
4964
|
+
async function proxyRequest(targetUrl, options) {
|
|
4965
|
+
let url;
|
|
4966
|
+
try {
|
|
4967
|
+
url = new URL(targetUrl);
|
|
4968
|
+
} catch {
|
|
4969
|
+
console.error(`${RED$1}Error: 无效的 URL "${targetUrl}"${RESET$2}`);
|
|
4970
|
+
process.exit(1);
|
|
4971
|
+
}
|
|
4972
|
+
const platformResult = detectPlatformWithKey(url.hostname);
|
|
4973
|
+
if (!platformResult) {
|
|
4974
|
+
console.error(`${RED$1}Error: 无法识别平台 "${url.hostname}"${RESET$2}`);
|
|
4975
|
+
console.log(`${DIM$2}支持的平台:${RESET$2}`);
|
|
4976
|
+
console.log(`${DIM$2} - code.alibaba-inc.com (GitLab)${RESET$2}`);
|
|
4977
|
+
console.log(`${DIM$2} - aliyuque.antfin.com (语雀)${RESET$2}`);
|
|
4978
|
+
console.log(`${DIM$2} - o2.alibaba-inc.com${RESET$2}`);
|
|
4979
|
+
console.log(`${DIM$2} - aone.alibaba-inc.com${RESET$2}`);
|
|
4980
|
+
process.exit(1);
|
|
4981
|
+
}
|
|
4982
|
+
const { key: platformKey, config: platform } = platformResult;
|
|
4983
|
+
if (!options.quiet) console.log(`${DIM$2}平台: ${platform.name}${RESET$2}`);
|
|
4984
|
+
const authMode = options.authMode === "auto" || !options.authMode ? detectAuthMode(platformKey, platform, url.hostname) : options.authMode;
|
|
4985
|
+
if (!options.quiet && authMode !== "auto") console.log(`${DIM$2}认证方式: ${getAuthModeDescription(authMode)}${RESET$2}`);
|
|
4986
|
+
try {
|
|
4987
|
+
switch (authMode) {
|
|
4988
|
+
case "private_token": {
|
|
4989
|
+
let token = getPlatformPrivateToken(platformKey, options.token);
|
|
4990
|
+
if (!token) {
|
|
4991
|
+
token = await promptConfigureToken(platformKey, platform);
|
|
4992
|
+
if (!token) {
|
|
4993
|
+
console.error(`${RED$1}Error: ${platform.name} 需要配置 Private Token 才能访问${RESET$2}`);
|
|
4994
|
+
console.log(`${DIM$2}您可以通过以下方式配置:${RESET$2}`);
|
|
4995
|
+
console.log(` 1. 参数: --token <your_token>`);
|
|
4996
|
+
console.log(` 2. 环境变量: ALI_SKILLS_${platformKey.toUpperCase()}_TOKEN=<your_token>`);
|
|
4997
|
+
console.log(` 3. 配置文件: npx ali-skills gateway config --platform ${platformKey}`);
|
|
4998
|
+
process.exit(1);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
await sendDirectRequest(targetUrl, platform, token, options);
|
|
5002
|
+
break;
|
|
5003
|
+
}
|
|
5004
|
+
case "app_code":
|
|
5005
|
+
case "app_code+buc":
|
|
5006
|
+
case "sso_cookie":
|
|
5007
|
+
case "sso_ticket":
|
|
5008
|
+
case "gateway":
|
|
5009
|
+
await sendGatewayRequest(targetUrl, platformKey, options, authMode);
|
|
5010
|
+
break;
|
|
5011
|
+
default: if (platform.authType === "private_token") {
|
|
5012
|
+
const token = getPlatformPrivateToken(platformKey, options.token);
|
|
5013
|
+
if (token) await sendDirectRequest(targetUrl, platform, token, options);
|
|
5014
|
+
else {
|
|
5015
|
+
console.error(`${RED$1}Error: ${platform.name} 需要配置 Private Token${RESET$2}`);
|
|
5016
|
+
console.log(`${DIM$2}您可以通过以下方式配置:${RESET$2}`);
|
|
5017
|
+
console.log(` 1. 参数: --token <your_token>`);
|
|
5018
|
+
console.log(` 2. 环境变量: ALI_SKILLS_${platformKey.toUpperCase()}_TOKEN=<your_token>`);
|
|
5019
|
+
console.log(` 3. 配置文件: npx ali-skills gateway config --platform ${platformKey}`);
|
|
5020
|
+
process.exit(1);
|
|
5021
|
+
}
|
|
5022
|
+
} else await sendGatewayRequest(targetUrl, platformKey, options, "gateway");
|
|
5023
|
+
}
|
|
5024
|
+
} catch (error) {
|
|
5025
|
+
if (error instanceof Error) if (error.name === "AbortError") console.error(`${RED$1}Error: 请求超时${RESET$2}`);
|
|
5026
|
+
else console.error(`${RED$1}Error: ${error.message}${RESET$2}`);
|
|
5027
|
+
process.exit(1);
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
function readBodyFromFile(filePath) {
|
|
5031
|
+
const { readFileSync, existsSync } = __require("fs");
|
|
5032
|
+
const { resolve } = __require("path");
|
|
5033
|
+
const fullPath = resolve(filePath);
|
|
5034
|
+
if (!existsSync(fullPath)) throw new Error(`File not found: ${filePath}`);
|
|
5035
|
+
return readFileSync(fullPath, "utf-8");
|
|
5036
|
+
}
|
|
5037
|
+
const DIM$1 = "\x1B[38;5;102m";
|
|
5038
|
+
const TEXT$1 = "\x1B[38;5;145m";
|
|
5039
|
+
const RESET$1 = "\x1B[0m";
|
|
5040
|
+
const GREEN = "\x1B[32m";
|
|
5041
|
+
const YELLOW = "\x1B[33m";
|
|
5042
|
+
const RED = "\x1B[31m";
|
|
5043
|
+
function showConfigHelp() {
|
|
5044
|
+
console.log(`
|
|
5045
|
+
${TEXT$1}npx ali-skills config - 配置各平台访问凭证${RESET$1}
|
|
5046
|
+
|
|
5047
|
+
${DIM$1}Usage:${RESET$1}
|
|
5048
|
+
npx ali-skills config <platform> 配置指定平台的 token
|
|
5049
|
+
npx ali-skills config --list 列出已配置的平台
|
|
5050
|
+
npx ali-skills config --remove <p> 移除指定平台的配置
|
|
5051
|
+
|
|
5052
|
+
${DIM$1}Supported Platforms:${RESET$1}`);
|
|
5053
|
+
const platforms = listPlatforms();
|
|
5054
|
+
const displayWidth = (str) => {
|
|
5055
|
+
let width = 0;
|
|
5056
|
+
for (const char of str) width += char.charCodeAt(0) > 127 ? 2 : 1;
|
|
5057
|
+
return width;
|
|
5058
|
+
};
|
|
5059
|
+
const maxNameWidth = Math.max(...platforms.map(({ config }) => displayWidth(config.name)));
|
|
5060
|
+
for (const { key, config } of platforms) {
|
|
5061
|
+
const authType = config.authType === "private_token" ? "需配置 Token" : config.authType === "app_code" ? "内置凭证" : config.authType === "buc_sso_refresh" ? "需 BUC 登录" : "需 BUC 登录";
|
|
5062
|
+
const namePadding = " ".repeat(15 - displayWidth(config.name) + maxNameWidth);
|
|
5063
|
+
console.log(` ${key.padEnd(12)} ${config.name}${namePadding} ${DIM$1}${authType}${RESET$1}`);
|
|
5064
|
+
}
|
|
5065
|
+
console.log(`
|
|
5066
|
+
${DIM$1}Examples:${RESET$1}
|
|
5067
|
+
npx ali-skills config code
|
|
5068
|
+
npx ali-skills config yuque
|
|
5069
|
+
npx ali-skills config --list
|
|
5070
|
+
npx ali-skills config --remove code
|
|
5071
|
+
`);
|
|
5072
|
+
}
|
|
5073
|
+
async function prompt(question, defaultValue) {
|
|
5074
|
+
const rl = (await import("readline")).createInterface({
|
|
5075
|
+
input: process.stdin,
|
|
5076
|
+
output: process.stdout
|
|
5077
|
+
});
|
|
5078
|
+
return new Promise((resolve) => {
|
|
5079
|
+
const fullQuestion = defaultValue ? `${question} (默认: ${defaultValue}): ` : `${question}: `;
|
|
5080
|
+
rl.question(fullQuestion, (answer) => {
|
|
5081
|
+
rl.close();
|
|
5082
|
+
resolve(answer.trim() || defaultValue || "");
|
|
5083
|
+
});
|
|
5084
|
+
});
|
|
5085
|
+
}
|
|
5086
|
+
async function openBrowser(url) {
|
|
5087
|
+
const { spawn } = await import("node:child_process");
|
|
5088
|
+
const platform = process.platform;
|
|
5089
|
+
spawn(platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open", [url], {
|
|
5090
|
+
detached: true,
|
|
5091
|
+
stdio: "ignore"
|
|
5092
|
+
});
|
|
5093
|
+
}
|
|
5094
|
+
async function configurePlatform(platformKey) {
|
|
5095
|
+
const platform = getPlatform(platformKey);
|
|
5096
|
+
if (!platform) {
|
|
5097
|
+
console.error(`${RED}Error: 不支持的平台 "${platformKey}"${RESET$1}`);
|
|
5098
|
+
console.log(`${DIM$1}运行 'npx ali-skills config --list' 查看支持的平台${RESET$1}`);
|
|
5099
|
+
process.exit(1);
|
|
5100
|
+
}
|
|
5101
|
+
console.log(`${TEXT$1}配置 ${platform.name}${RESET$1}`);
|
|
5102
|
+
console.log(`${DIM$1}认证方式: ${platform.authType}${RESET$1}`);
|
|
5103
|
+
switch (platform.authType) {
|
|
5104
|
+
case "private_token":
|
|
5105
|
+
await configurePrivateToken(platformKey, platform);
|
|
5106
|
+
break;
|
|
5107
|
+
case "app_code":
|
|
5108
|
+
console.log(`${GREEN}✓ ${platform.name} 使用内置凭证,无需配置${RESET$1}`);
|
|
5109
|
+
break;
|
|
5110
|
+
case "app_code+buc":
|
|
5111
|
+
console.log(`${YELLOW}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5112
|
+
console.log(`${DIM$1}请运行: npx ali-skills login${RESET$1}`);
|
|
5113
|
+
break;
|
|
5114
|
+
case "buc_sso_refresh":
|
|
5115
|
+
case "buc_sso_ticket":
|
|
5116
|
+
console.log(`${YELLOW}! ${platform.name} 需要 BUC 登录${RESET$1}`);
|
|
5117
|
+
console.log(`${DIM$1}请运行: npx ali-skills login${RESET$1}`);
|
|
5118
|
+
break;
|
|
5119
|
+
default:
|
|
5120
|
+
console.error(`${RED}Error: 未知的认证类型${RESET$1}`);
|
|
5121
|
+
process.exit(1);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
async function configurePrivateToken(platformKey, platform) {
|
|
5125
|
+
if (!platform.tokenDocUrl) {
|
|
5126
|
+
console.error(`${RED}Error: 未找到 ${platform.name} 的 token 文档地址${RESET$1}`);
|
|
5127
|
+
process.exit(1);
|
|
5128
|
+
}
|
|
5129
|
+
console.log();
|
|
5130
|
+
console.log(`${DIM$1}1. 正在打开 ${platform.name} Token 页面...${RESET$1}`);
|
|
5131
|
+
console.log(`${DIM$1} ${platform.tokenDocUrl}${RESET$1}`);
|
|
5132
|
+
await openBrowser(platform.tokenDocUrl);
|
|
5133
|
+
console.log();
|
|
5134
|
+
console.log(`${DIM$1}2. 请在浏览器中创建 Access Token,然后复制到这里${RESET$1}`);
|
|
5135
|
+
const token = await prompt("Access Token");
|
|
5136
|
+
if (!token) {
|
|
5137
|
+
console.error(`${RED}Error: Token 不能为空${RESET$1}`);
|
|
5138
|
+
process.exit(1);
|
|
5139
|
+
}
|
|
5140
|
+
setPlatformToken(platformKey, { privateToken: token });
|
|
5141
|
+
console.log();
|
|
5142
|
+
console.log(`${GREEN}✓ ${platform.name} Token 已保存${RESET$1}`);
|
|
5143
|
+
console.log(`${DIM$1}配置文件: ${getConfigFilePath()}${RESET$1}`);
|
|
5144
|
+
}
|
|
5145
|
+
function listConfigured() {
|
|
5146
|
+
const config = readConfig();
|
|
5147
|
+
const platforms = Object.entries(config.platforms);
|
|
5148
|
+
if (platforms.length === 0) {
|
|
5149
|
+
console.log(`${DIM$1}尚未配置任何平台${RESET$1}`);
|
|
5150
|
+
console.log(`${TEXT$1}运行 'npx ali-skills config <platform>' 开始配置${RESET$1}`);
|
|
5151
|
+
return;
|
|
5152
|
+
}
|
|
5153
|
+
console.log(`${TEXT$1}已配置的平台:${RESET$1}`);
|
|
5154
|
+
for (const [key, token] of platforms) {
|
|
5155
|
+
const name = getPlatform(key)?.name || key;
|
|
5156
|
+
const hasToken = token.privateToken ? "✓" : "✗";
|
|
5157
|
+
console.log(` ${hasToken} ${name} (${key})`);
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
function removePlatform(platformKey) {
|
|
5161
|
+
const name = getPlatform(platformKey)?.name || platformKey;
|
|
5162
|
+
removePlatformToken(platformKey);
|
|
5163
|
+
console.log(`${GREEN}✓ 已移除 ${name} 的配置${RESET$1}`);
|
|
5164
|
+
}
|
|
5165
|
+
async function runConfig(args) {
|
|
5166
|
+
if (args.length === 0) {
|
|
5167
|
+
showConfigHelp();
|
|
5168
|
+
return;
|
|
5169
|
+
}
|
|
5170
|
+
const command = args[0];
|
|
5171
|
+
switch (command) {
|
|
5172
|
+
case "--help":
|
|
5173
|
+
case "-h":
|
|
5174
|
+
showConfigHelp();
|
|
5175
|
+
break;
|
|
5176
|
+
case "--list":
|
|
5177
|
+
case "-l":
|
|
5178
|
+
listConfigured();
|
|
5179
|
+
break;
|
|
5180
|
+
case "--remove":
|
|
5181
|
+
case "-r": {
|
|
5182
|
+
const platform = args[1];
|
|
5183
|
+
if (!platform) {
|
|
5184
|
+
console.error(`${RED}Error: 请指定要移除的平台${RESET$1}`);
|
|
5185
|
+
process.exit(1);
|
|
5186
|
+
}
|
|
5187
|
+
removePlatform(platform);
|
|
5188
|
+
break;
|
|
5189
|
+
}
|
|
5190
|
+
default: if (!command.startsWith("-")) await configurePlatform(command);
|
|
5191
|
+
else {
|
|
5192
|
+
console.error(`${RED}Error: 未知选项 "${command}"${RESET$1}`);
|
|
5193
|
+
showConfigHelp();
|
|
5194
|
+
process.exit(1);
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
4400
5198
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
4401
5199
|
function getVersion() {
|
|
4402
5200
|
try {
|
|
@@ -4439,32 +5237,32 @@ function showBanner() {
|
|
|
4439
5237
|
console.log();
|
|
4440
5238
|
console.log(`${DIM}The open agent skills ecosystem${RESET}`);
|
|
4441
5239
|
console.log();
|
|
4442
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4443
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4444
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4445
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
5240
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills add ${DIM}<package>${RESET} ${DIM}Add a new skill${RESET}`);
|
|
5241
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills remove${RESET} ${DIM}Remove installed skills${RESET}`);
|
|
5242
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills list${RESET} ${DIM}List installed skills${RESET}`);
|
|
5243
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills find ${DIM}[query]${RESET} ${DIM}Search for skills${RESET}`);
|
|
4446
5244
|
console.log();
|
|
4447
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
5245
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills publish${RESET} ${DIM}Publish skills to OSS${RESET}`);
|
|
4448
5246
|
console.log();
|
|
4449
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4450
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
5247
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills check${RESET} ${DIM}Check for updates${RESET}`);
|
|
5248
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills update${RESET} ${DIM}Update all skills${RESET}`);
|
|
4451
5249
|
console.log();
|
|
4452
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4453
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
4454
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx
|
|
5250
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills experimental_install${RESET} ${DIM}Restore from skills-lock.json${RESET}`);
|
|
5251
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills init ${DIM}[name]${RESET} ${DIM}Create a new skill${RESET}`);
|
|
5252
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx ali-skills experimental_sync${RESET} ${DIM}Sync skills from node_modules${RESET}`);
|
|
4455
5253
|
console.log();
|
|
4456
|
-
console.log(`${DIM}try:${RESET} npx
|
|
5254
|
+
console.log(`${DIM}try:${RESET} npx ali-skills add group/project`);
|
|
4457
5255
|
console.log();
|
|
4458
|
-
console.log(`Discover more skills at ${TEXT}https://skills.
|
|
5256
|
+
console.log(`Discover more skills at ${TEXT}https://ali-skills.alibaba-inc.com/${RESET}`);
|
|
4459
5257
|
console.log();
|
|
4460
5258
|
}
|
|
4461
5259
|
function showHelp() {
|
|
4462
5260
|
console.log(`
|
|
4463
|
-
${BOLD}Usage:${RESET}
|
|
5261
|
+
${BOLD}Usage:${RESET} ali-skills <command> [options]
|
|
4464
5262
|
|
|
4465
5263
|
${BOLD}Manage Skills:${RESET}
|
|
4466
5264
|
add <package> Add a skill package (alias: a)
|
|
4467
|
-
e.g.
|
|
5265
|
+
e.g. group/project
|
|
4468
5266
|
https://github.com/vercel-labs/agent-skills
|
|
4469
5267
|
remove [skills] Remove installed skills
|
|
4470
5268
|
list, ls List installed skills
|
|
@@ -4474,8 +5272,12 @@ ${BOLD}Publish:${RESET}
|
|
|
4474
5272
|
publish [options] Publish skills from current repo to OSS (alias: pub)
|
|
4475
5273
|
|
|
4476
5274
|
${BOLD}Updates:${RESET}
|
|
4477
|
-
check Check for available skill updates
|
|
4478
|
-
|
|
5275
|
+
check Check for available skill updates (project-level by default)
|
|
5276
|
+
check -g Check global skill updates
|
|
5277
|
+
update Update skills interactively (project-level by default)
|
|
5278
|
+
update -g Update global skills
|
|
5279
|
+
update -y Update all skills without prompting
|
|
5280
|
+
update <skill> Update specific skill(s)
|
|
4479
5281
|
|
|
4480
5282
|
${BOLD}Project:${RESET}
|
|
4481
5283
|
experimental_install Restore skills from skills-lock.json
|
|
@@ -4514,32 +5316,32 @@ ${BOLD}Options:${RESET}
|
|
|
4514
5316
|
--version, -v Show version number
|
|
4515
5317
|
|
|
4516
5318
|
${BOLD}Examples:${RESET}
|
|
4517
|
-
${DIM}$${RESET}
|
|
4518
|
-
${DIM}$${RESET}
|
|
4519
|
-
${DIM}$${RESET}
|
|
4520
|
-
${DIM}$${RESET}
|
|
4521
|
-
${DIM}$${RESET}
|
|
4522
|
-
${DIM}$${RESET}
|
|
4523
|
-
${DIM}$${RESET}
|
|
4524
|
-
${DIM}$${RESET}
|
|
4525
|
-
${DIM}$${RESET}
|
|
4526
|
-
${DIM}$${RESET}
|
|
4527
|
-
${DIM}$${RESET}
|
|
4528
|
-
${DIM}$${RESET}
|
|
4529
|
-
${DIM}$${RESET}
|
|
4530
|
-
${DIM}$${RESET}
|
|
4531
|
-
${DIM}$${RESET}
|
|
4532
|
-
${DIM}$${RESET}
|
|
4533
|
-
${DIM}$${RESET}
|
|
4534
|
-
${DIM}$${RESET}
|
|
4535
|
-
${DIM}$${RESET}
|
|
5319
|
+
${DIM}$${RESET} ali-skills add group/project
|
|
5320
|
+
${DIM}$${RESET} ali-skills add group/project -g
|
|
5321
|
+
${DIM}$${RESET} ali-skills add group/project --agent claude-code cursor
|
|
5322
|
+
${DIM}$${RESET} ali-skills add group/project --skill pr-review commit
|
|
5323
|
+
${DIM}$${RESET} ali-skills remove ${DIM}# interactive remove${RESET}
|
|
5324
|
+
${DIM}$${RESET} ali-skills remove web-design ${DIM}# remove by name${RESET}
|
|
5325
|
+
${DIM}$${RESET} ali-skills rm --global frontend-design
|
|
5326
|
+
${DIM}$${RESET} ali-skills list ${DIM}# list project skills${RESET}
|
|
5327
|
+
${DIM}$${RESET} ali-skills ls -g ${DIM}# list global skills${RESET}
|
|
5328
|
+
${DIM}$${RESET} ali-skills ls -a claude-code ${DIM}# filter by agent${RESET}
|
|
5329
|
+
${DIM}$${RESET} ali-skills ls --json ${DIM}# JSON output${RESET}
|
|
5330
|
+
${DIM}$${RESET} ali-skills find ${DIM}# interactive search${RESET}
|
|
5331
|
+
${DIM}$${RESET} ali-skills find typescript ${DIM}# search by keyword${RESET}
|
|
5332
|
+
${DIM}$${RESET} ali-skills check
|
|
5333
|
+
${DIM}$${RESET} ali-skills update
|
|
5334
|
+
${DIM}$${RESET} ali-skills experimental_install ${DIM}# restore from skills-lock.json${RESET}
|
|
5335
|
+
${DIM}$${RESET} ali-skills init my-skill
|
|
5336
|
+
${DIM}$${RESET} ali-skills experimental_sync ${DIM}# sync from node_modules${RESET}
|
|
5337
|
+
${DIM}$${RESET} ali-skills experimental_sync -y ${DIM}# sync without prompts${RESET}
|
|
4536
5338
|
|
|
4537
|
-
Discover more skills at ${TEXT}https://skills.
|
|
5339
|
+
Discover more skills at ${TEXT}https://ali-skills.alibaba-inc.com/${RESET}
|
|
4538
5340
|
`);
|
|
4539
5341
|
}
|
|
4540
5342
|
function showRemoveHelp() {
|
|
4541
5343
|
console.log(`
|
|
4542
|
-
${BOLD}Usage:${RESET}
|
|
5344
|
+
${BOLD}Usage:${RESET} ali-skills remove [skills...] [options]
|
|
4543
5345
|
|
|
4544
5346
|
${BOLD}Description:${RESET}
|
|
4545
5347
|
Remove installed skills from agents. If no skill names are provided,
|
|
@@ -4556,17 +5358,159 @@ ${BOLD}Options:${RESET}
|
|
|
4556
5358
|
--all Shorthand for --skill '*' --agent '*' -y
|
|
4557
5359
|
|
|
4558
5360
|
${BOLD}Examples:${RESET}
|
|
4559
|
-
${DIM}$${RESET}
|
|
4560
|
-
${DIM}$${RESET}
|
|
4561
|
-
${DIM}$${RESET}
|
|
4562
|
-
${DIM}$${RESET}
|
|
4563
|
-
${DIM}$${RESET}
|
|
4564
|
-
${DIM}$${RESET}
|
|
4565
|
-
${DIM}$${RESET}
|
|
5361
|
+
${DIM}$${RESET} ali-skills remove ${DIM}# interactive selection${RESET}
|
|
5362
|
+
${DIM}$${RESET} ali-skills remove my-skill ${DIM}# remove specific skill${RESET}
|
|
5363
|
+
${DIM}$${RESET} ali-skills remove skill1 skill2 -y ${DIM}# remove multiple skills${RESET}
|
|
5364
|
+
${DIM}$${RESET} ali-skills remove --global my-skill ${DIM}# remove from global scope${RESET}
|
|
5365
|
+
${DIM}$${RESET} ali-skills rm --agent claude-code my-skill ${DIM}# remove from specific agent${RESET}
|
|
5366
|
+
${DIM}$${RESET} ali-skills remove --all ${DIM}# remove all skills${RESET}
|
|
5367
|
+
${DIM}$${RESET} ali-skills remove --skill '*' -a cursor ${DIM}# remove all skills from cursor${RESET}
|
|
5368
|
+
|
|
5369
|
+
Discover more skills at ${TEXT}https://ali-skills.alibaba-inc.com/${RESET}
|
|
5370
|
+
`);
|
|
5371
|
+
}
|
|
5372
|
+
function showGatewayHelp() {
|
|
5373
|
+
console.log(`
|
|
5374
|
+
${BOLD}Gateway Commands:${RESET}
|
|
5375
|
+
login Login to Gateway (BUC OAuth)
|
|
5376
|
+
logout Logout from Gateway
|
|
5377
|
+
status Show login status
|
|
5378
|
+
request <url> Send HTTP request via Gateway proxy
|
|
5379
|
+
req <url> Alias for request
|
|
5380
|
+
|
|
5381
|
+
${BOLD}Config Commands:${RESET}
|
|
5382
|
+
config <platform> Configure platform token
|
|
5383
|
+
config --list List configured platforms
|
|
5384
|
+
config --remove <p> Remove platform config
|
|
4566
5385
|
|
|
4567
|
-
|
|
5386
|
+
${BOLD}Request Options:${RESET}
|
|
5387
|
+
-X, --method <method> HTTP method (default: GET)
|
|
5388
|
+
-b, --body <body> Request body (JSON string or @file)
|
|
5389
|
+
-h, --header <header> Custom header (can be used multiple times)
|
|
5390
|
+
-o, --output <file> Save response to file
|
|
5391
|
+
-i, --include-headers Include response headers in output
|
|
5392
|
+
-t, --timeout <ms> Request timeout (default: 30000)
|
|
5393
|
+
--pretty Pretty print JSON output (default: true)
|
|
5394
|
+
--raw Raw output without formatting
|
|
5395
|
+
-q, --quiet Quiet mode, only output response body
|
|
5396
|
+
|
|
5397
|
+
${BOLD}Examples:${RESET}
|
|
5398
|
+
${DIM}$${RESET} ali-skills login
|
|
5399
|
+
${DIM}$${RESET} ali-skills config code
|
|
5400
|
+
${DIM}$${RESET} ali-skills config yuque
|
|
5401
|
+
${DIM}$${RESET} ali-skills req https://o2.alibaba-inc.com/api/v2/apps
|
|
5402
|
+
${DIM}$${RESET} ali-skills req https://code.alibaba-inc.com/api/v4/projects -X GET
|
|
5403
|
+
${DIM}$${RESET} ali-skills req https://aliyuque.antfin.com/api/v2/repos -o repos.json
|
|
5404
|
+
${DIM}$${RESET} ali-skills logout
|
|
4568
5405
|
`);
|
|
4569
5406
|
}
|
|
5407
|
+
function parseRequestOptions(args) {
|
|
5408
|
+
let url = "";
|
|
5409
|
+
const options = { method: "GET" };
|
|
5410
|
+
const headers = {};
|
|
5411
|
+
for (let i = 0; i < args.length; i++) {
|
|
5412
|
+
const arg = args[i];
|
|
5413
|
+
switch (arg) {
|
|
5414
|
+
case "-m":
|
|
5415
|
+
case "--method":
|
|
5416
|
+
options.method = args[++i] || "GET";
|
|
5417
|
+
break;
|
|
5418
|
+
case "-b":
|
|
5419
|
+
case "--body":
|
|
5420
|
+
options.body = args[++i];
|
|
5421
|
+
break;
|
|
5422
|
+
case "-h":
|
|
5423
|
+
case "--header": {
|
|
5424
|
+
const header = args[++i];
|
|
5425
|
+
if (header) {
|
|
5426
|
+
const [key, ...valueParts] = header.split(":");
|
|
5427
|
+
if (key && valueParts.length > 0) headers[key.trim()] = valueParts.join(":").trim();
|
|
5428
|
+
}
|
|
5429
|
+
break;
|
|
5430
|
+
}
|
|
5431
|
+
case "-o":
|
|
5432
|
+
case "--output":
|
|
5433
|
+
options.output = args[++i];
|
|
5434
|
+
break;
|
|
5435
|
+
case "-i":
|
|
5436
|
+
case "--include-headers":
|
|
5437
|
+
options.includeHeaders = true;
|
|
5438
|
+
break;
|
|
5439
|
+
case "-t":
|
|
5440
|
+
case "--timeout":
|
|
5441
|
+
options.timeout = parseInt(args[++i], 10);
|
|
5442
|
+
break;
|
|
5443
|
+
case "--pretty":
|
|
5444
|
+
options.pretty = true;
|
|
5445
|
+
break;
|
|
5446
|
+
case "--raw":
|
|
5447
|
+
options.raw = true;
|
|
5448
|
+
break;
|
|
5449
|
+
case "-q":
|
|
5450
|
+
case "--quiet":
|
|
5451
|
+
options.quiet = true;
|
|
5452
|
+
break;
|
|
5453
|
+
default:
|
|
5454
|
+
if (!arg.startsWith("-") && !url) url = arg;
|
|
5455
|
+
break;
|
|
5456
|
+
}
|
|
5457
|
+
}
|
|
5458
|
+
if (Object.keys(headers).length > 0) options.headers = headers;
|
|
5459
|
+
if (options.body?.startsWith("@")) options.body = readBodyFromFile(options.body.slice(1));
|
|
5460
|
+
return {
|
|
5461
|
+
url,
|
|
5462
|
+
options
|
|
5463
|
+
};
|
|
5464
|
+
}
|
|
5465
|
+
async function runGatewayCommand(args) {
|
|
5466
|
+
if (args.length === 0) {
|
|
5467
|
+
showGatewayHelp();
|
|
5468
|
+
return;
|
|
5469
|
+
}
|
|
5470
|
+
const command = args[0];
|
|
5471
|
+
const rest = args.slice(1);
|
|
5472
|
+
switch (command) {
|
|
5473
|
+
case "login":
|
|
5474
|
+
showLogo();
|
|
5475
|
+
console.log();
|
|
5476
|
+
await login();
|
|
5477
|
+
break;
|
|
5478
|
+
case "logout":
|
|
5479
|
+
await logout();
|
|
5480
|
+
break;
|
|
5481
|
+
case "status":
|
|
5482
|
+
await status();
|
|
5483
|
+
break;
|
|
5484
|
+
case "token": {
|
|
5485
|
+
const token = getPrivateToken();
|
|
5486
|
+
if (token) console.log(token);
|
|
5487
|
+
else {
|
|
5488
|
+
console.error("Not logged in");
|
|
5489
|
+
process.exit(1);
|
|
5490
|
+
}
|
|
5491
|
+
break;
|
|
5492
|
+
}
|
|
5493
|
+
case "request":
|
|
5494
|
+
case "req": {
|
|
5495
|
+
const { url, options } = parseRequestOptions(rest);
|
|
5496
|
+
if (!url) {
|
|
5497
|
+
console.error("Error: URL is required");
|
|
5498
|
+
showGatewayHelp();
|
|
5499
|
+
process.exit(1);
|
|
5500
|
+
}
|
|
5501
|
+
await proxyRequest(url, options);
|
|
5502
|
+
break;
|
|
5503
|
+
}
|
|
5504
|
+
case "--help":
|
|
5505
|
+
case "-h":
|
|
5506
|
+
showGatewayHelp();
|
|
5507
|
+
break;
|
|
5508
|
+
default:
|
|
5509
|
+
console.error(`Unknown gateway command: ${command}`);
|
|
5510
|
+
showGatewayHelp();
|
|
5511
|
+
process.exit(1);
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
4570
5514
|
function runInit(args) {
|
|
4571
5515
|
const cwd = process.cwd();
|
|
4572
5516
|
const skillName = args[0] || basename(cwd);
|
|
@@ -4608,10 +5552,10 @@ Describe when this skill should be used.
|
|
|
4608
5552
|
console.log(` 2. Update the ${TEXT}name${RESET} and ${TEXT}description${RESET} in the frontmatter`);
|
|
4609
5553
|
console.log();
|
|
4610
5554
|
console.log(`${DIM}Publishing:${RESET}`);
|
|
4611
|
-
console.log(` ${DIM}GitHub:${RESET} Push to a repo, then ${TEXT}npx
|
|
4612
|
-
console.log(` ${DIM}URL:${RESET} Host the file, then ${TEXT}npx
|
|
5555
|
+
console.log(` ${DIM}GitHub:${RESET} Push to a repo, then ${TEXT}npx ali-skills add <owner>/<repo>${RESET}`);
|
|
5556
|
+
console.log(` ${DIM}URL:${RESET} Host the file, then ${TEXT}npx ali-skills add https://example.com/${displayPath}${RESET}`);
|
|
4613
5557
|
console.log();
|
|
4614
|
-
console.log(`Browse existing skills for inspiration at ${TEXT}https://skills.
|
|
5558
|
+
console.log(`Browse existing skills for inspiration at ${TEXT}https://ali-skills.alibaba-inc.com/${RESET}`);
|
|
4615
5559
|
console.log();
|
|
4616
5560
|
}
|
|
4617
5561
|
const AGENTS_DIR = ".agents";
|
|
@@ -4650,31 +5594,11 @@ function getSkipReason(entry) {
|
|
|
4650
5594
|
if (!entry.skillPath) return "No skill path recorded";
|
|
4651
5595
|
return "No version tracking";
|
|
4652
5596
|
}
|
|
4653
|
-
function
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
console.log(`${DIM}${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
4657
|
-
for (const skill of skipped) {
|
|
4658
|
-
console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
4659
|
-
console.log(` ${DIM}To update: ${TEXT}npx @ali/cli-skills add ${skill.sourceUrl} -g -y${RESET}`);
|
|
4660
|
-
}
|
|
4661
|
-
}
|
|
4662
|
-
async function runCheck(args = []) {
|
|
4663
|
-
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4664
|
-
console.log();
|
|
4665
|
-
const lock = readSkillLock();
|
|
4666
|
-
const skillNames = Object.keys(lock.skills);
|
|
4667
|
-
if (skillNames.length === 0) {
|
|
4668
|
-
console.log(`${DIM}No skills tracked in lock file.${RESET}`);
|
|
4669
|
-
console.log(`${DIM}Install skills with${RESET} ${TEXT}npx @ali/cli-skills add <package>${RESET}`);
|
|
4670
|
-
return;
|
|
4671
|
-
}
|
|
4672
|
-
const token = getGitHubToken();
|
|
4673
|
-
const skillsBySource = /* @__PURE__ */ new Map();
|
|
5597
|
+
async function checkSkillsForUpdates(skills, token) {
|
|
5598
|
+
const updates = [];
|
|
5599
|
+
const errors = [];
|
|
4674
5600
|
const skipped = [];
|
|
4675
|
-
for (const skillName of
|
|
4676
|
-
const entry = lock.skills[skillName];
|
|
4677
|
-
if (!entry) continue;
|
|
5601
|
+
for (const [skillName, entry] of Object.entries(skills)) {
|
|
4678
5602
|
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
4679
5603
|
skipped.push({
|
|
4680
5604
|
name: skillName,
|
|
@@ -4683,84 +5607,260 @@ async function runCheck(args = []) {
|
|
|
4683
5607
|
});
|
|
4684
5608
|
continue;
|
|
4685
5609
|
}
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
for (const [source, skills] of skillsBySource) for (const { name, entry } of skills) try {
|
|
4703
|
-
const latestHash = await fetchSkillFolderHash(source, entry.skillPath, token);
|
|
4704
|
-
if (!latestHash) {
|
|
5610
|
+
try {
|
|
5611
|
+
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
|
|
5612
|
+
if (!latestHash) {
|
|
5613
|
+
errors.push({
|
|
5614
|
+
name: skillName,
|
|
5615
|
+
source: entry.source,
|
|
5616
|
+
error: "Could not fetch from GitHub"
|
|
5617
|
+
});
|
|
5618
|
+
continue;
|
|
5619
|
+
}
|
|
5620
|
+
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
5621
|
+
name: skillName,
|
|
5622
|
+
source: entry.source,
|
|
5623
|
+
entry
|
|
5624
|
+
});
|
|
5625
|
+
} catch (err) {
|
|
4705
5626
|
errors.push({
|
|
4706
|
-
name,
|
|
4707
|
-
source,
|
|
4708
|
-
error:
|
|
5627
|
+
name: skillName,
|
|
5628
|
+
source: entry.source,
|
|
5629
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
4709
5630
|
});
|
|
4710
|
-
continue;
|
|
4711
5631
|
}
|
|
4712
|
-
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
4713
|
-
name,
|
|
4714
|
-
source
|
|
4715
|
-
});
|
|
4716
|
-
} catch (err) {
|
|
4717
|
-
errors.push({
|
|
4718
|
-
name,
|
|
4719
|
-
source,
|
|
4720
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
4721
|
-
});
|
|
4722
5632
|
}
|
|
5633
|
+
return {
|
|
5634
|
+
updates,
|
|
5635
|
+
errors,
|
|
5636
|
+
skipped
|
|
5637
|
+
};
|
|
5638
|
+
}
|
|
5639
|
+
async function runCheck(args = []) {
|
|
5640
|
+
const isGlobal = args.includes("-g") || args.includes("--global");
|
|
5641
|
+
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4723
5642
|
console.log();
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
5643
|
+
const token = getGitHubToken();
|
|
5644
|
+
let updates = [];
|
|
5645
|
+
let skipped = [];
|
|
5646
|
+
let skillCount = 0;
|
|
5647
|
+
if (isGlobal) {
|
|
5648
|
+
const globalLock = readSkillLock();
|
|
5649
|
+
const globalSkillNames = Object.keys(globalLock.skills);
|
|
5650
|
+
skillCount = globalSkillNames.length;
|
|
5651
|
+
if (globalSkillNames.length === 0) {
|
|
5652
|
+
console.log(`${DIM}No global skills found.${RESET}`);
|
|
5653
|
+
console.log();
|
|
5654
|
+
return;
|
|
5655
|
+
}
|
|
5656
|
+
console.log(`${BOLD}Global skills${RESET}`);
|
|
5657
|
+
console.log(`${DIM}Checking ${globalSkillNames.length} skill(s)...${RESET}`);
|
|
5658
|
+
const result = await checkSkillsForUpdates(globalLock.skills, token);
|
|
5659
|
+
updates = result.updates;
|
|
5660
|
+
result.errors;
|
|
5661
|
+
skipped = result.skipped;
|
|
5662
|
+
if (updates.length === 0) console.log(`${TEXT} ✓ All global skills are up to date${RESET}`);
|
|
5663
|
+
else {
|
|
5664
|
+
console.log(`${TEXT} ${updates.length} update(s) available:${RESET}`);
|
|
5665
|
+
for (const update of updates) console.log(` ${TEXT}↑${RESET} ${update.name} ${DIM}(${update.source})${RESET}`);
|
|
5666
|
+
}
|
|
5667
|
+
if (skipped.length > 0) {
|
|
5668
|
+
console.log();
|
|
5669
|
+
console.log(`${DIM} ${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
5670
|
+
for (const skill of skipped) {
|
|
5671
|
+
console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
5672
|
+
console.log(` ${DIM}To update: ${TEXT}npx ali-skills add ${skill.sourceUrl} -g -y${RESET}`);
|
|
5673
|
+
}
|
|
4731
5674
|
}
|
|
4732
5675
|
console.log();
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
5676
|
+
if (updates.length > 0) {
|
|
5677
|
+
console.log(`${TEXT}${updates.length} update(s) available${RESET}`);
|
|
5678
|
+
console.log();
|
|
5679
|
+
console.log(`${DIM}Run${RESET} ${TEXT}npx ali-skills update -g${RESET} ${DIM}to update all global skills${RESET}`);
|
|
5680
|
+
}
|
|
5681
|
+
} else {
|
|
5682
|
+
const projectLock = await readLocalLock();
|
|
5683
|
+
const projectSkillNames = Object.keys(projectLock.skills);
|
|
5684
|
+
skillCount = projectSkillNames.length;
|
|
5685
|
+
if (projectSkillNames.length === 0) {
|
|
5686
|
+
console.log(`${DIM}No project-level skills found.${RESET}`);
|
|
5687
|
+
console.log();
|
|
5688
|
+
console.log(`${DIM}To check global skills, use:${RESET} ${TEXT}npx ali-skills check -g${RESET}`);
|
|
5689
|
+
return;
|
|
5690
|
+
}
|
|
5691
|
+
console.log(`${BOLD}Project-level skills${RESET}`);
|
|
5692
|
+
console.log(`${DIM}Checking ${projectSkillNames.length} skill(s)...${RESET}`);
|
|
5693
|
+
for (const [skillName, entry] of Object.entries(projectLock.skills)) {
|
|
5694
|
+
if (entry.sourceType === "internal") {
|
|
5695
|
+
if (!entry.commitHash) {
|
|
5696
|
+
skipped.push({
|
|
5697
|
+
name: skillName,
|
|
5698
|
+
reason: "Reinstall required to enable update checking (missing commit hash)",
|
|
5699
|
+
sourceUrl: `https://code.alibaba-inc.com/${entry.source}`
|
|
5700
|
+
});
|
|
5701
|
+
continue;
|
|
5702
|
+
}
|
|
5703
|
+
const skillPath = (entry.skillPath || "").replace(/\/?SKILL\.md$/i, "");
|
|
5704
|
+
const latestCommit = await fetchAliInternalCommitHash(entry.source, skillPath, null);
|
|
5705
|
+
if (latestCommit === null) skipped.push({
|
|
5706
|
+
name: skillName,
|
|
5707
|
+
reason: "Ali internal repository (API authentication failed)",
|
|
5708
|
+
sourceUrl: `https://code.alibaba-inc.com/${entry.source}`
|
|
5709
|
+
});
|
|
5710
|
+
else if (latestCommit !== entry.commitHash) updates.push({
|
|
5711
|
+
name: skillName,
|
|
5712
|
+
source: entry.source,
|
|
5713
|
+
entry: {
|
|
5714
|
+
source: entry.source,
|
|
5715
|
+
sourceType: entry.sourceType,
|
|
5716
|
+
sourceUrl: `https://code.alibaba-inc.com/${entry.source}`,
|
|
5717
|
+
skillPath,
|
|
5718
|
+
skillFolderHash: entry.computedHash,
|
|
5719
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5720
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5721
|
+
}
|
|
5722
|
+
});
|
|
5723
|
+
continue;
|
|
5724
|
+
}
|
|
5725
|
+
if (entry.sourceType === "github") {
|
|
5726
|
+
if (!entry.skillFolderHash) {
|
|
5727
|
+
skipped.push({
|
|
5728
|
+
name: skillName,
|
|
5729
|
+
reason: "Reinstall required to enable update checking (missing folder hash)",
|
|
5730
|
+
sourceUrl: `https://github.com/${entry.source}`
|
|
5731
|
+
});
|
|
5732
|
+
continue;
|
|
5733
|
+
}
|
|
5734
|
+
const possiblePaths = entry.skillPath ? [entry.skillPath] : [`skills/${skillName}`, `skills/${skillName}/SKILL.md`];
|
|
5735
|
+
let foundMatch = false;
|
|
5736
|
+
for (const skillPath of possiblePaths) try {
|
|
5737
|
+
const latestHash = await fetchSkillFolderHash(entry.source, skillPath, token);
|
|
5738
|
+
if (latestHash === null) continue;
|
|
5739
|
+
foundMatch = true;
|
|
5740
|
+
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
5741
|
+
name: skillName,
|
|
5742
|
+
source: entry.source,
|
|
5743
|
+
entry: {
|
|
5744
|
+
source: entry.source,
|
|
5745
|
+
sourceType: entry.sourceType,
|
|
5746
|
+
sourceUrl: `https://github.com/${entry.source}`,
|
|
5747
|
+
skillPath,
|
|
5748
|
+
skillFolderHash: entry.skillFolderHash,
|
|
5749
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5750
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5751
|
+
}
|
|
5752
|
+
});
|
|
5753
|
+
break;
|
|
5754
|
+
} catch {
|
|
5755
|
+
continue;
|
|
5756
|
+
}
|
|
5757
|
+
if (!foundMatch) skipped.push({
|
|
5758
|
+
name: skillName,
|
|
5759
|
+
reason: "Unable to verify (API failed for all possible paths)",
|
|
5760
|
+
sourceUrl: `https://github.com/${entry.source}`
|
|
5761
|
+
});
|
|
5762
|
+
} else skipped.push({
|
|
5763
|
+
name: skillName,
|
|
5764
|
+
reason: `${entry.sourceType} source type not supported for update checking`,
|
|
5765
|
+
sourceUrl: entry.source
|
|
5766
|
+
});
|
|
5767
|
+
}
|
|
5768
|
+
if (updates.length === 0) console.log(`${TEXT} ✓ All project skills are up to date${RESET}`);
|
|
5769
|
+
else {
|
|
5770
|
+
console.log(`${TEXT} ${updates.length} update(s) available:${RESET}`);
|
|
5771
|
+
for (const update of updates) console.log(` ${TEXT}↑${RESET} ${update.name} ${DIM}(${update.source})${RESET}`);
|
|
5772
|
+
}
|
|
5773
|
+
if (skipped.length > 0) {
|
|
5774
|
+
console.log();
|
|
5775
|
+
console.log(`${DIM} ${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
5776
|
+
for (const skill of skipped) console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
5777
|
+
}
|
|
4736
5778
|
console.log();
|
|
4737
|
-
|
|
5779
|
+
if (updates.length > 0) {
|
|
5780
|
+
console.log(`${TEXT}${updates.length} update(s) available${RESET}`);
|
|
5781
|
+
console.log();
|
|
5782
|
+
console.log(`${DIM}Run${RESET} ${TEXT}npx ali-skills update${RESET} ${DIM}to update all project skills${RESET}`);
|
|
5783
|
+
console.log(`${DIM}Run${RESET} ${TEXT}npx ali-skills update <skill-name>${RESET} ${DIM}to update specific skill${RESET}`);
|
|
5784
|
+
}
|
|
4738
5785
|
}
|
|
4739
|
-
printSkippedSkills(skipped);
|
|
4740
5786
|
track({
|
|
4741
5787
|
event: "check",
|
|
4742
|
-
skillCount: String(
|
|
5788
|
+
skillCount: String(skillCount),
|
|
4743
5789
|
updatesAvailable: String(updates.length)
|
|
4744
5790
|
});
|
|
4745
5791
|
console.log();
|
|
4746
5792
|
}
|
|
4747
|
-
async function runUpdate() {
|
|
5793
|
+
async function runUpdate(args = []) {
|
|
5794
|
+
const isGlobal = args.includes("-g") || args.includes("--global");
|
|
5795
|
+
const yesFlag = args.includes("-y") || args.includes("--yes");
|
|
5796
|
+
const skillArgs = args.filter((arg) => !arg.startsWith("-"));
|
|
4748
5797
|
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4749
5798
|
console.log();
|
|
4750
|
-
const
|
|
4751
|
-
|
|
5799
|
+
const token = getGitHubToken();
|
|
5800
|
+
let skillsToCheck = {};
|
|
5801
|
+
let scopeLabel = "project";
|
|
5802
|
+
if (isGlobal) {
|
|
5803
|
+
skillsToCheck = readSkillLock().skills;
|
|
5804
|
+
scopeLabel = "global";
|
|
5805
|
+
} else {
|
|
5806
|
+
const projectLock = await readLocalLock();
|
|
5807
|
+
for (const [name, entry] of Object.entries(projectLock.skills)) {
|
|
5808
|
+
let sourceUrl;
|
|
5809
|
+
if (entry.sourceType === "github") sourceUrl = `https://github.com/${entry.source}`;
|
|
5810
|
+
else if (entry.sourceType === "internal") sourceUrl = `https://code.alibaba-inc.com/${entry.source}`;
|
|
5811
|
+
else sourceUrl = entry.source;
|
|
5812
|
+
skillsToCheck[name] = {
|
|
5813
|
+
source: entry.source,
|
|
5814
|
+
sourceType: entry.sourceType,
|
|
5815
|
+
sourceUrl,
|
|
5816
|
+
skillPath: entry.skillPath || "",
|
|
5817
|
+
skillFolderHash: entry.skillFolderHash || entry.computedHash,
|
|
5818
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5819
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
const skillNames = Object.keys(skillsToCheck);
|
|
4752
5824
|
if (skillNames.length === 0) {
|
|
4753
|
-
console.log(`${DIM}No skills
|
|
4754
|
-
console.log(
|
|
5825
|
+
console.log(`${DIM}No ${scopeLabel}-level skills found.${RESET}`);
|
|
5826
|
+
console.log();
|
|
5827
|
+
if (!isGlobal) console.log(`${DIM}To update global skills, use:${RESET} ${TEXT}npx ali-skills update -g${RESET}`);
|
|
4755
5828
|
return;
|
|
4756
5829
|
}
|
|
4757
|
-
const
|
|
4758
|
-
const updates = [];
|
|
5830
|
+
const availableUpdates = [];
|
|
4759
5831
|
const skipped = [];
|
|
5832
|
+
const projectLock = !isGlobal ? await readLocalLock() : null;
|
|
4760
5833
|
for (const skillName of skillNames) {
|
|
4761
|
-
const entry =
|
|
5834
|
+
const entry = skillsToCheck[skillName];
|
|
4762
5835
|
if (!entry) continue;
|
|
4763
|
-
if (
|
|
5836
|
+
if (entry.sourceType === "internal" && projectLock) {
|
|
5837
|
+
const localEntry = projectLock.skills[skillName];
|
|
5838
|
+
if (!localEntry?.commitHash) {
|
|
5839
|
+
skipped.push({
|
|
5840
|
+
name: skillName,
|
|
5841
|
+
reason: "Reinstall required to enable update checking (missing commit hash)",
|
|
5842
|
+
sourceUrl: entry.sourceUrl
|
|
5843
|
+
});
|
|
5844
|
+
continue;
|
|
5845
|
+
}
|
|
5846
|
+
try {
|
|
5847
|
+
const skillPath = (localEntry.skillPath || "").replace(/\/?SKILL\.md$/i, "");
|
|
5848
|
+
const latestCommit = await fetchAliInternalCommitHash(entry.source, skillPath, null);
|
|
5849
|
+
if (latestCommit && latestCommit !== localEntry.commitHash) availableUpdates.push({
|
|
5850
|
+
name: skillName,
|
|
5851
|
+
source: entry.source,
|
|
5852
|
+
entry
|
|
5853
|
+
});
|
|
5854
|
+
} catch {
|
|
5855
|
+
skipped.push({
|
|
5856
|
+
name: skillName,
|
|
5857
|
+
reason: "Ali internal repository (API check failed)",
|
|
5858
|
+
sourceUrl: entry.sourceUrl
|
|
5859
|
+
});
|
|
5860
|
+
}
|
|
5861
|
+
continue;
|
|
5862
|
+
}
|
|
5863
|
+
if (!entry.skillFolderHash) {
|
|
4764
5864
|
skipped.push({
|
|
4765
5865
|
name: skillName,
|
|
4766
5866
|
reason: getSkipReason(entry),
|
|
@@ -4768,58 +5868,100 @@ async function runUpdate() {
|
|
|
4768
5868
|
});
|
|
4769
5869
|
continue;
|
|
4770
5870
|
}
|
|
5871
|
+
if (entry.sourceType === "github") {
|
|
5872
|
+
const possiblePaths = entry.skillPath ? [entry.skillPath] : [`skills/${skillName}`, `skills/${skillName}/SKILL.md`];
|
|
5873
|
+
for (const skillPath of possiblePaths) try {
|
|
5874
|
+
const latestHash = await fetchSkillFolderHash(entry.source, skillPath, token);
|
|
5875
|
+
if (latestHash === null) continue;
|
|
5876
|
+
if (latestHash !== entry.skillFolderHash) availableUpdates.push({
|
|
5877
|
+
name: skillName,
|
|
5878
|
+
source: entry.source,
|
|
5879
|
+
entry
|
|
5880
|
+
});
|
|
5881
|
+
break;
|
|
5882
|
+
} catch {
|
|
5883
|
+
continue;
|
|
5884
|
+
}
|
|
5885
|
+
continue;
|
|
5886
|
+
}
|
|
4771
5887
|
try {
|
|
4772
|
-
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
|
|
4773
|
-
if (latestHash && latestHash !== entry.skillFolderHash)
|
|
5888
|
+
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath || "", token);
|
|
5889
|
+
if (latestHash && latestHash !== entry.skillFolderHash) availableUpdates.push({
|
|
4774
5890
|
name: skillName,
|
|
4775
5891
|
source: entry.source,
|
|
4776
5892
|
entry
|
|
4777
5893
|
});
|
|
4778
5894
|
} catch {}
|
|
4779
5895
|
}
|
|
4780
|
-
if (
|
|
4781
|
-
console.log(`${
|
|
4782
|
-
|
|
5896
|
+
if (availableUpdates.length === 0) {
|
|
5897
|
+
console.log(`${TEXT}✓ All ${scopeLabel} skills are up to date${RESET}`);
|
|
5898
|
+
console.log();
|
|
4783
5899
|
return;
|
|
4784
5900
|
}
|
|
4785
|
-
|
|
4786
|
-
|
|
5901
|
+
let selectedUpdates = [];
|
|
5902
|
+
if (skillArgs.length > 0) {
|
|
5903
|
+
const requestedSkills = skillArgs.map((s) => s.toLowerCase());
|
|
5904
|
+
selectedUpdates = availableUpdates.filter((u) => requestedSkills.includes(u.name.toLowerCase()));
|
|
5905
|
+
for (const reqSkill of skillArgs) {
|
|
5906
|
+
const exists = skillNames.some((n) => n.toLowerCase() === reqSkill.toLowerCase());
|
|
5907
|
+
const hasUpdate = availableUpdates.some((u) => u.name.toLowerCase() === reqSkill.toLowerCase());
|
|
5908
|
+
if (!exists) console.log(`${DIM}Skill not found: ${reqSkill}${RESET}`);
|
|
5909
|
+
else if (!hasUpdate) console.log(`${TEXT}✓ ${reqSkill} is already up to date${RESET}`);
|
|
5910
|
+
}
|
|
5911
|
+
} else if (yesFlag) selectedUpdates = availableUpdates;
|
|
5912
|
+
else {
|
|
5913
|
+
console.log(`${TEXT}Found ${availableUpdates.length} ${scopeLabel} skill update(s) available:${RESET}`);
|
|
4787
5914
|
console.log();
|
|
5915
|
+
const selected = await fe({
|
|
5916
|
+
message: "Select skills to update (space to select, enter to confirm):",
|
|
5917
|
+
options: [{
|
|
5918
|
+
value: "__ALL__",
|
|
5919
|
+
label: `${import_picocolors.default.green("Update all")} (${availableUpdates.length} skill(s))`
|
|
5920
|
+
}, ...availableUpdates.map((u) => ({
|
|
5921
|
+
value: u.name,
|
|
5922
|
+
label: ` ${u.name} ${DIM}(${u.source})${RESET}`
|
|
5923
|
+
}))],
|
|
5924
|
+
required: true
|
|
5925
|
+
});
|
|
5926
|
+
if (pD(selected)) {
|
|
5927
|
+
Se(import_picocolors.default.yellow("Update cancelled."));
|
|
5928
|
+
return;
|
|
5929
|
+
}
|
|
5930
|
+
const selectedArray = selected;
|
|
5931
|
+
if (selectedArray.includes("__ALL__")) selectedUpdates = availableUpdates;
|
|
5932
|
+
else selectedUpdates = availableUpdates.filter((u) => selectedArray.includes(u.name));
|
|
5933
|
+
}
|
|
5934
|
+
if (selectedUpdates.length === 0) {
|
|
5935
|
+
console.log(`${DIM}No skills selected for update.${RESET}`);
|
|
4788
5936
|
return;
|
|
4789
5937
|
}
|
|
4790
|
-
console.log(
|
|
5938
|
+
console.log();
|
|
5939
|
+
console.log(`${TEXT}Updating ${selectedUpdates.length} ${scopeLabel} skill(s)...${RESET}`);
|
|
4791
5940
|
console.log();
|
|
4792
5941
|
let successCount = 0;
|
|
4793
5942
|
let failCount = 0;
|
|
4794
|
-
for (const update of
|
|
5943
|
+
for (const update of selectedUpdates) {
|
|
4795
5944
|
console.log(`${TEXT}Updating ${update.name}...${RESET}`);
|
|
4796
|
-
let
|
|
4797
|
-
if (
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
installUrl,
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
stdio: [
|
|
4814
|
-
"inherit",
|
|
4815
|
-
"pipe",
|
|
4816
|
-
"pipe"
|
|
4817
|
-
],
|
|
4818
|
-
shell: process.platform === "win32"
|
|
4819
|
-
}).status === 0) {
|
|
5945
|
+
let skillFolder = update.entry.skillPath || "";
|
|
5946
|
+
if (skillFolder.endsWith("/SKILL.md")) skillFolder = skillFolder.slice(0, -9);
|
|
5947
|
+
else if (skillFolder.endsWith("SKILL.md")) skillFolder = skillFolder.slice(0, -8);
|
|
5948
|
+
if (skillFolder.endsWith("/")) skillFolder = skillFolder.slice(0, -1);
|
|
5949
|
+
let sourceUrl = update.entry.sourceUrl || update.entry.source;
|
|
5950
|
+
if (update.entry.sourceType === "internal" && !sourceUrl.startsWith("http") && !sourceUrl.startsWith("git@")) sourceUrl = `https://code.alibaba-inc.com/${sourceUrl}.git`;
|
|
5951
|
+
else if (update.entry.sourceType === "github" && !sourceUrl.startsWith("http") && !sourceUrl.startsWith("git@")) sourceUrl = `https://github.com/${sourceUrl}.git`;
|
|
5952
|
+
let installUrl;
|
|
5953
|
+
if (skillFolder) {
|
|
5954
|
+
installUrl = sourceUrl.replace(/\.git$/, "").replace(/\/$/, "");
|
|
5955
|
+
installUrl = `${installUrl}/tree/HEAD/${skillFolder}`;
|
|
5956
|
+
} else installUrl = sourceUrl;
|
|
5957
|
+
try {
|
|
5958
|
+
await runAdd([installUrl], {
|
|
5959
|
+
yes: true,
|
|
5960
|
+
global: isGlobal
|
|
5961
|
+
});
|
|
4820
5962
|
successCount++;
|
|
4821
5963
|
console.log(` ${TEXT}✓${RESET} Updated ${update.name}`);
|
|
4822
|
-
}
|
|
5964
|
+
} catch {
|
|
4823
5965
|
failCount++;
|
|
4824
5966
|
console.log(` ${DIM}✗ Failed to update ${update.name}${RESET}`);
|
|
4825
5967
|
}
|
|
@@ -4829,7 +5971,7 @@ async function runUpdate() {
|
|
|
4829
5971
|
if (failCount > 0) console.log(`${DIM}Failed to update ${failCount} skill(s)${RESET}`);
|
|
4830
5972
|
track({
|
|
4831
5973
|
event: "update",
|
|
4832
|
-
skillCount: String(
|
|
5974
|
+
skillCount: String(selectedUpdates.length),
|
|
4833
5975
|
successCount: String(successCount),
|
|
4834
5976
|
failCount: String(failCount)
|
|
4835
5977
|
});
|
|
@@ -4857,6 +5999,11 @@ async function main() {
|
|
|
4857
5999
|
console.log();
|
|
4858
6000
|
runInit(restArgs);
|
|
4859
6001
|
break;
|
|
6002
|
+
case "config":
|
|
6003
|
+
showLogo();
|
|
6004
|
+
console.log();
|
|
6005
|
+
await runConfig(restArgs);
|
|
6006
|
+
break;
|
|
4860
6007
|
case "experimental_install":
|
|
4861
6008
|
showLogo();
|
|
4862
6009
|
await runInstallFromLock(restArgs);
|
|
@@ -4900,9 +6047,33 @@ async function main() {
|
|
|
4900
6047
|
case "check":
|
|
4901
6048
|
await runCheck(restArgs);
|
|
4902
6049
|
break;
|
|
6050
|
+
case "login":
|
|
6051
|
+
await login();
|
|
6052
|
+
break;
|
|
6053
|
+
case "logout":
|
|
6054
|
+
await logout();
|
|
6055
|
+
break;
|
|
6056
|
+
case "status":
|
|
6057
|
+
await status();
|
|
6058
|
+
break;
|
|
6059
|
+
case "req":
|
|
6060
|
+
case "request": {
|
|
6061
|
+
const { url, options } = parseRequestOptions(restArgs);
|
|
6062
|
+
if (!url) {
|
|
6063
|
+
console.error("Error: URL is required");
|
|
6064
|
+
showGatewayHelp();
|
|
6065
|
+
process.exit(1);
|
|
6066
|
+
}
|
|
6067
|
+
await proxyRequest(url, options);
|
|
6068
|
+
break;
|
|
6069
|
+
}
|
|
6070
|
+
case "gateway":
|
|
6071
|
+
case "gw":
|
|
6072
|
+
await runGatewayCommand(restArgs);
|
|
6073
|
+
break;
|
|
4903
6074
|
case "update":
|
|
4904
6075
|
case "upgrade":
|
|
4905
|
-
await runUpdate();
|
|
6076
|
+
await runUpdate(restArgs);
|
|
4906
6077
|
break;
|
|
4907
6078
|
case "--help":
|
|
4908
6079
|
case "-h":
|
|
@@ -4914,7 +6085,7 @@ async function main() {
|
|
|
4914
6085
|
break;
|
|
4915
6086
|
default:
|
|
4916
6087
|
console.log(`Unknown command: ${command}`);
|
|
4917
|
-
console.log(`Run ${BOLD}
|
|
6088
|
+
console.log(`Run ${BOLD}ali-skills --help${RESET} for usage.`);
|
|
4918
6089
|
}
|
|
4919
6090
|
}
|
|
4920
6091
|
main().then(async () => {
|