@solgridai/cli 0.2.0-beta.1 → 0.2.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/index.js +116 -26
- package/package.json +1 -1
- package/skills/solgrid-cli/SKILL.md +8 -2
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ solgrid run -p <provider> -e <endpoint> -i '{"screen_name":"nasa"}' --wait
|
|
|
58
58
|
| `files get -i <fileId>` | 查看文件的类型、大小和状态 |
|
|
59
59
|
| `files url -i <fileId>` | 为已上传的文件重新签发 24 小时有效的 URL |
|
|
60
60
|
| `files download -i <fileId> [-o path] [--force]` | 通过签名 URL 下载文件到本地 |
|
|
61
|
-
| `skills list [-q text] [--kind k] [--mine] [-l n] [--page n]` | 在 SkillHub 搜索 Skill
|
|
61
|
+
| `skills list [-q text] [--kind k] [--mine] [-l n] [--page n]` | 在 SkillHub 搜索 Skill(`-q` 按标题搜索) |
|
|
62
62
|
| `skills get (-s slug \| -i id)` | 查看 Skill 详情 |
|
|
63
63
|
| `skills tags` | 列出 Skill 可用的标签 |
|
|
64
64
|
| `skills create [-s slug] -t <title> --kind <k> -d <text> (--dir <dir> \| --zip <file>) [--cover f] [--preview f] [--tag id]… [-y]` | 发布 Skill,创建后立即公开 |
|
|
@@ -102,8 +102,8 @@ solgrid skills create -t "Mecha portrait" --kind image \
|
|
|
102
102
|
|
|
103
103
|
用 `--dir` 时 slug 默认取 `name`,传了 `-s` 就必须和 `name` 一致;用 `--zip` 时必须传 `-s`,由服务端核对。`skills update` 换包时,新包的 `name` 也必须等于原来的 slug。
|
|
104
104
|
- `-d` 是显示在标题下的简短描述(纯文本,不按 Markdown 渲染)。Skill 的详细说明请写在 `SKILL.md` 里。
|
|
105
|
-
- `--kind` 表示 Skill 产出的作品类型:`image`、`video`、`web`、`audio`。除视频外都需要封面(`--cover
|
|
106
|
-
- `--tag` 填 `solgrid skills tags` 列出的标签 ID,最多 3
|
|
105
|
+
- `--kind` 表示 Skill 产出的作品类型:`image`、`video`、`web`、`audio`。除视频外都需要封面(`--cover`,图片)。预览(`--preview`)的类型由 kind 决定:图片和网页类可选,只能是图片;视频类必填,只能是视频;音频类必填,只能是音频。
|
|
106
|
+
- `--tag` 填 `solgrid skills tags` 列出的标签 ID,最多 3 个,不能重复。
|
|
107
107
|
- `create`、`delete` 会先请你确认;在脚本或 Agent 中使用时加 `-y`。
|
|
108
108
|
- `update` 只修改传入的选项;`--tag` 会替换全部标签,`--clear-tags` 清空标签,`--clear-preview` 去掉预览(仅图片和网页类 Skill)。只能修改、删除自己的 Skill。
|
|
109
109
|
|
|
@@ -132,6 +132,7 @@ solgrid run -p <provider> -e <endpoint> -i '<同样的输入>' --idempotency-key
|
|
|
132
132
|
| `~/.config/solgrid/cli/config.json` | 当前激活的 key |
|
|
133
133
|
| `SOLGRID_API_KEY` | 设置后优先于本机保存的 key,适合 CI 和 Agent |
|
|
134
134
|
| `SOLGRID_API_BASE_URL` | 覆盖 API 地址,默认为 `https://api.solgrid.ai` |
|
|
135
|
+
| `SOLGRID_LANG` / `--lang` | 服务端返回文本(标签名、错误信息)的语言:`en`(默认)或 `zh`;`--lang` 是全局选项,优先于环境变量。标签 ID、错误键和 CLI 自身的提示不变 |
|
|
135
136
|
| `NO_COLOR` / `FORCE_COLOR` | 关闭或强制彩色输出 |
|
|
136
137
|
|
|
137
138
|
## 开发
|
package/dist/index.js
CHANGED
|
@@ -2075,7 +2075,7 @@ function useColor() {
|
|
|
2075
2075
|
// node_modules/commander/index.js
|
|
2076
2076
|
var program = new Command;
|
|
2077
2077
|
// src/build-info.ts
|
|
2078
|
-
var buildInfo = { packageName: "@solgridai/cli", version: "0.2.0-beta.
|
|
2078
|
+
var buildInfo = { packageName: "@solgridai/cli", version: "0.2.0-beta.3" };
|
|
2079
2079
|
|
|
2080
2080
|
// src/core/errors.ts
|
|
2081
2081
|
class CliError extends Error {
|
|
@@ -2344,8 +2344,10 @@ var CONSOLE_URLS = {
|
|
|
2344
2344
|
};
|
|
2345
2345
|
var ENV = {
|
|
2346
2346
|
apiKey: "SOLGRID_API_KEY",
|
|
2347
|
-
apiBaseUrl: "SOLGRID_API_BASE_URL"
|
|
2347
|
+
apiBaseUrl: "SOLGRID_API_BASE_URL",
|
|
2348
|
+
language: "SOLGRID_LANG"
|
|
2348
2349
|
};
|
|
2350
|
+
var LANGUAGES = ["en", "zh"];
|
|
2349
2351
|
|
|
2350
2352
|
// src/core/config.ts
|
|
2351
2353
|
function resolveConfigDir(env, homeDir) {
|
|
@@ -2591,6 +2593,9 @@ class ApiClient {
|
|
|
2591
2593
|
this.options = options;
|
|
2592
2594
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
2593
2595
|
}
|
|
2596
|
+
languageHeader() {
|
|
2597
|
+
return this.options.language ? { "Accept-Language": this.options.language } : {};
|
|
2598
|
+
}
|
|
2594
2599
|
post(path, body = {}, headers = {}, request = {}) {
|
|
2595
2600
|
return this.send("POST", path, {
|
|
2596
2601
|
body: JSON.stringify(body),
|
|
@@ -2632,6 +2637,7 @@ class ApiClient {
|
|
|
2632
2637
|
method: "GET",
|
|
2633
2638
|
headers: {
|
|
2634
2639
|
"X-Solgrid-Client": this.options.clientId,
|
|
2640
|
+
...this.languageHeader(),
|
|
2635
2641
|
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}
|
|
2636
2642
|
},
|
|
2637
2643
|
signal: AbortSignal.timeout(TRANSFER_TIMEOUT_MS)
|
|
@@ -2658,6 +2664,7 @@ class ApiClient {
|
|
|
2658
2664
|
headers: {
|
|
2659
2665
|
Accept: "application/json",
|
|
2660
2666
|
"X-Solgrid-Client": this.options.clientId,
|
|
2667
|
+
...this.languageHeader(),
|
|
2661
2668
|
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {},
|
|
2662
2669
|
...options.headers
|
|
2663
2670
|
},
|
|
@@ -3119,13 +3126,29 @@ function apiClient(ctx) {
|
|
|
3119
3126
|
});
|
|
3120
3127
|
}
|
|
3121
3128
|
sentKeys.set(ctx, resolved);
|
|
3129
|
+
const language = requestLanguage(ctx);
|
|
3122
3130
|
return new ApiClient({
|
|
3123
3131
|
baseUrl: ctx.env[ENV.apiBaseUrl]?.trim() || DEFAULT_API_BASE_URL,
|
|
3124
3132
|
apiKey: resolved.key,
|
|
3125
3133
|
clientId: `cli@${buildInfo.version}`,
|
|
3134
|
+
...language ? { language } : {},
|
|
3126
3135
|
fetch: ctx.fetch
|
|
3127
3136
|
});
|
|
3128
3137
|
}
|
|
3138
|
+
function requestLanguage(ctx) {
|
|
3139
|
+
const value = ctx.language ?? ctx.env[ENV.language]?.trim();
|
|
3140
|
+
if (!value)
|
|
3141
|
+
return;
|
|
3142
|
+
return parseLanguage(value, ctx.language === undefined ? ENV.language : "--lang");
|
|
3143
|
+
}
|
|
3144
|
+
function parseLanguage(value, source = "--lang") {
|
|
3145
|
+
const language = value.trim().toLowerCase();
|
|
3146
|
+
if (LANGUAGES.includes(language))
|
|
3147
|
+
return language;
|
|
3148
|
+
throw new CliError("USAGE", `Unsupported language in ${source}: ${value}.`, {
|
|
3149
|
+
hint: `Use one of: ${LANGUAGES.join(", ")}.`
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3129
3152
|
function withAuthContext(ctx, error) {
|
|
3130
3153
|
const key = sentKeys.get(ctx);
|
|
3131
3154
|
if (!(error instanceof ApiError) || error.code !== "AUTH_FAILED" || !key)
|
|
@@ -4158,6 +4181,7 @@ function registerSessionIdCommand(program, ctx) {
|
|
|
4158
4181
|
|
|
4159
4182
|
// src/commands/skills.ts
|
|
4160
4183
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
4184
|
+
import { basename as basename4 } from "node:path";
|
|
4161
4185
|
|
|
4162
4186
|
// src/core/skill-markdown.ts
|
|
4163
4187
|
var MAX_SKILL_NAME_LENGTH = 64;
|
|
@@ -4609,6 +4633,10 @@ async function listSkillTags(client) {
|
|
|
4609
4633
|
return checked.tags;
|
|
4610
4634
|
}
|
|
4611
4635
|
function checkTags(tags, available) {
|
|
4636
|
+
const repeated = [...new Set(tags.filter((tag, index) => tags.indexOf(tag) !== index))];
|
|
4637
|
+
if (repeated.length > 0) {
|
|
4638
|
+
throw new CliError("USAGE", `Tag${repeated.length > 1 ? "s" : ""} given more than once: ${repeated.join(", ")}.`, { hint: "Pass each --tag once." });
|
|
4639
|
+
}
|
|
4612
4640
|
if (tags.length > MAX_TAGS) {
|
|
4613
4641
|
throw new CliError("USAGE", `A Skill takes at most ${MAX_TAGS} tags; ${tags.length} were given.`);
|
|
4614
4642
|
}
|
|
@@ -4620,6 +4648,9 @@ function checkTags(tags, available) {
|
|
|
4620
4648
|
});
|
|
4621
4649
|
}
|
|
4622
4650
|
}
|
|
4651
|
+
function previewKindFor(kind) {
|
|
4652
|
+
return kind === "video" ? "video" : kind === "audio" ? "audio" : "image";
|
|
4653
|
+
}
|
|
4623
4654
|
var PUBLIC_DISPLAY = "1";
|
|
4624
4655
|
function prepareSkillFile(path, role) {
|
|
4625
4656
|
let size;
|
|
@@ -4693,7 +4724,7 @@ function collect2(value, previous = []) {
|
|
|
4693
4724
|
}
|
|
4694
4725
|
function addContentOptions(command, required) {
|
|
4695
4726
|
const note = required ? "" : " (replaces the current one)";
|
|
4696
|
-
command.option("-t, --title <title>", `title${note}`).option("--kind <kind>", `what the Skill makes: ${SKILL_KINDS.join(", ")}${note}`, parseKind2).option("-d, --description <text>", `a short description shown under the title${note}`).option("--description-file <path>", "read the description from a file instead").option("--tag <id>", `a tag ID from \`solgrid skills tags\`; up to ${MAX_TAGS}, repeat for several${required ? "" : " (replaces all current tags)"}`, collect2).option("--cover <path>", `cover image; required unless the kind is video${note}`).option("--preview <path>", `preview image, video
|
|
4727
|
+
command.option("-t, --title <title>", `title${note}`).option("--kind <kind>", `what the Skill makes: ${SKILL_KINDS.join(", ")}${note}`, parseKind2).option("-d, --description <text>", `a short description shown under the title${note}`).option("--description-file <path>", "read the description from a file instead").option("--tag <id>", `a tag ID from \`solgrid skills tags\`; up to ${MAX_TAGS}, repeat for several${required ? "" : " (replaces all current tags)"}`, collect2).option("--cover <path>", `cover image; required unless the kind is video${note}`).option("--preview <path>", `preview of what the Skill makes: an image for image and web Skills (optional), a video for video Skills, an audio file for audio Skills${note}`).option("--dir <path>", `a Skill directory with SKILL.md at its top, packed for you${note}`).option("--zip <path>", `or an already packed Skill (.zip with SKILL.md)${note}`);
|
|
4697
4728
|
return command;
|
|
4698
4729
|
}
|
|
4699
4730
|
function readDescription(opts) {
|
|
@@ -4719,7 +4750,22 @@ function checkMedia(kind, hasCover, hasPreview) {
|
|
|
4719
4750
|
}
|
|
4720
4751
|
if ((kind === "video" || kind === "audio") && !hasPreview) {
|
|
4721
4752
|
throw new CliError("USAGE", `${aSkill(kind)} needs a preview.`, {
|
|
4722
|
-
hint: `Pass --preview <${kind}
|
|
4753
|
+
hint: `Pass --preview <${previewKindFor(kind)} file>.`
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
var A_FILE = {
|
|
4758
|
+
image: "an image",
|
|
4759
|
+
video: "a video",
|
|
4760
|
+
audio: "an audio file",
|
|
4761
|
+
skill: "a Skill package"
|
|
4762
|
+
};
|
|
4763
|
+
function checkPreviewFile(kind, path) {
|
|
4764
|
+
const { kind: fileKind } = prepareSkillFile(path, "preview");
|
|
4765
|
+
const expected = previewKindFor(kind);
|
|
4766
|
+
if (fileKind !== expected) {
|
|
4767
|
+
throw new CliError("USAGE", `${aSkill(kind)}'s preview must be ${A_FILE[expected]}, not ${A_FILE[fileKind]}: ${basename4(path)}.`, {
|
|
4768
|
+
hint: "Image and web Skills take an image preview, video Skills a video, audio Skills an audio file."
|
|
4723
4769
|
});
|
|
4724
4770
|
}
|
|
4725
4771
|
}
|
|
@@ -4811,7 +4857,7 @@ function registerSkillsCommand(program, ctx) {
|
|
|
4811
4857
|
const skills = program.command("skills").description("Find, publish and manage Skills on SkillHub.").addHelpText("after", `
|
|
4812
4858
|
A Skill is public as soon as it is created. Its slug identifies it everywhere and cannot
|
|
4813
4859
|
be changed later.`);
|
|
4814
|
-
skills.command("list").description("Search Skills, newest first.").option("-q, --query <words>", "
|
|
4860
|
+
skills.command("list").description("Search Skills, newest first.").option("-q, --query <words>", "search by title").option("--kind <kind>", `only this kind: ${SKILL_KINDS.join(", ")}`, parseKind2).option("--mine", "only your own Skills").option("-l, --limit <n>", "Skills per page (default 20, up to 100)", parsePositiveInt).option("--page <n>", "page number (default 1)", parsePositiveInt).option(...JSON_FLAG).action((opts) => runAction(ctx, opts.json, async (out) => {
|
|
4815
4861
|
const client = apiClient(ctx);
|
|
4816
4862
|
out.progress.start("Searching Skills…");
|
|
4817
4863
|
const data = await listSkills(client, {
|
|
@@ -4848,7 +4894,8 @@ be changed later.`);
|
|
|
4848
4894
|
const client = apiClient(ctx);
|
|
4849
4895
|
out.progress.start("Fetching Skill…");
|
|
4850
4896
|
const skill = await findSkill(client, target);
|
|
4851
|
-
|
|
4897
|
+
const tagNames = opts.json ? undefined : await tagNamesFor(client, skill.tags);
|
|
4898
|
+
out.result(skill, () => renderSkill(skill, out.style, tagNames));
|
|
4852
4899
|
}));
|
|
4853
4900
|
skills.command("tags").description(`List the tags a Skill can carry (up to ${MAX_TAGS} each).`).option(...JSON_FLAG).action((opts) => runAction(ctx, opts.json, async (out) => {
|
|
4854
4901
|
const client = apiClient(ctx);
|
|
@@ -4876,7 +4923,7 @@ be changed later.`);
|
|
|
4876
4923
|
if (opts.cover)
|
|
4877
4924
|
prepareSkillFile(opts.cover, "cover");
|
|
4878
4925
|
if (opts.preview)
|
|
4879
|
-
|
|
4926
|
+
checkPreviewFile(kind, opts.preview);
|
|
4880
4927
|
const pkg = skillPackage(opts);
|
|
4881
4928
|
try {
|
|
4882
4929
|
const slug = slugFor(pkg, opts);
|
|
@@ -4957,8 +5004,9 @@ be changed later.`);
|
|
|
4957
5004
|
if (pkg?.name !== undefined && pkg.name !== skill.slug) {
|
|
4958
5005
|
throw nameMismatch(pkg.name, skill.slug);
|
|
4959
5006
|
}
|
|
5007
|
+
const current = contentOf(skill);
|
|
4960
5008
|
const next = {
|
|
4961
|
-
...
|
|
5009
|
+
...current,
|
|
4962
5010
|
...opts.title !== undefined ? { title: opts.title } : {},
|
|
4963
5011
|
...opts.kind !== undefined ? { kind: opts.kind } : {},
|
|
4964
5012
|
...description !== undefined ? { description } : {},
|
|
@@ -4971,6 +5019,14 @@ be changed later.`);
|
|
|
4971
5019
|
});
|
|
4972
5020
|
}
|
|
4973
5021
|
checkMedia(next.kind, Boolean(opts.cover ?? next.coverFileAssetId), Boolean(opts.preview ?? next.previewFileAssetId));
|
|
5022
|
+
if (opts.preview) {
|
|
5023
|
+
checkPreviewFile(next.kind, opts.preview);
|
|
5024
|
+
} else if (next.previewFileAssetId && previewKindFor(current.kind) !== previewKindFor(next.kind)) {
|
|
5025
|
+
const wanted = previewKindFor(next.kind);
|
|
5026
|
+
throw new CliError("USAGE", `Changing this ${current.kind} Skill to ${next.kind} needs a new preview: the current one is ${A_FILE[previewKindFor(current.kind)]}.`, {
|
|
5027
|
+
hint: `Pass --preview <${wanted} file>${wanted === "image" ? ", or --clear-preview" : ""}.`
|
|
5028
|
+
});
|
|
5029
|
+
}
|
|
4974
5030
|
await checkTagOptions(client, out, opts.tag);
|
|
4975
5031
|
next.skillFileAssetId = await uploadIfGiven(client, out, pkg?.path, "skill") ?? next.skillFileAssetId;
|
|
4976
5032
|
next.coverFileAssetId = await uploadIfGiven(client, out, opts.cover, "cover") ?? next.coverFileAssetId;
|
|
@@ -5019,7 +5075,20 @@ be changed later.`);
|
|
|
5019
5075
|
out.result({ slug: opts.slug, path: destination, sizeBytes: bytes }, () => `Saved ${destination} (${formatBytes(bytes)}).`);
|
|
5020
5076
|
}));
|
|
5021
5077
|
}
|
|
5022
|
-
function
|
|
5078
|
+
async function tagNamesFor(client, tags) {
|
|
5079
|
+
if (!tags?.length)
|
|
5080
|
+
return;
|
|
5081
|
+
try {
|
|
5082
|
+
return new Map((await listSkillTags(client)).map((tag) => [tag.id, tag.name]));
|
|
5083
|
+
} catch {
|
|
5084
|
+
return;
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
function renderSkill(skill, style, tagNames) {
|
|
5088
|
+
const tags = skill.tags?.map((id) => {
|
|
5089
|
+
const name = tagNames?.get(id);
|
|
5090
|
+
return name ? `${name} (${id})` : id;
|
|
5091
|
+
});
|
|
5023
5092
|
const lines = [
|
|
5024
5093
|
style("bold", skill.title),
|
|
5025
5094
|
"",
|
|
@@ -5027,8 +5096,8 @@ function renderSkill(skill, style) {
|
|
|
5027
5096
|
["Slug", skill.slug],
|
|
5028
5097
|
["ID", skill.id],
|
|
5029
5098
|
["Kind", kindName(skill.kind)],
|
|
5030
|
-
["Author", skill.author?.nickname ||
|
|
5031
|
-
["Tags",
|
|
5099
|
+
["Author", skill.author?.nickname || "-"],
|
|
5100
|
+
["Tags", tags?.length ? tags.join(", ") : undefined],
|
|
5032
5101
|
["Created", skill.createdAt ? formatTime(skill.createdAt) : undefined],
|
|
5033
5102
|
["Updated", skill.updatedAt ? formatTime(skill.updatedAt) : undefined]
|
|
5034
5103
|
], style)
|
|
@@ -5394,11 +5463,15 @@ function renderInput(input, legacySchema, style) {
|
|
|
5394
5463
|
|
|
5395
5464
|
// src/program.ts
|
|
5396
5465
|
function createProgram(ctx, options = {}) {
|
|
5397
|
-
const program = new Command().name("solgrid").description("Discover, inspect and run Solgrid tools from your terminal or agent.").version(buildInfo.version, "-v, --version", "Print the CLI version.").helpOption("-h, --help", "Show help.").configureOutput({
|
|
5466
|
+
const program = new Command().name("solgrid").description("Discover, inspect and run Solgrid tools from your terminal or agent.").version(buildInfo.version, "-v, --version", "Print the CLI version.").helpOption("-h, --help", "Show help.").option("--lang <code>", `language for text the Solgrid API writes, such as tag names and error messages: ${LANGUAGES.join(" or ")} (default: en; or set ${ENV.language})`).configureOutput({
|
|
5398
5467
|
writeOut: (text) => ctx.stdout.write(text),
|
|
5399
5468
|
writeErr: (text) => options.jsonErrors ? undefined : ctx.stderr.write(text),
|
|
5400
5469
|
outputError: (text, write) => options.jsonErrors ? undefined : write(text)
|
|
5401
|
-
}).showHelpAfterError("(run with --help for usage)").exitOverride()
|
|
5470
|
+
}).showHelpAfterError("(run with --help for usage)").exitOverride().hook("preAction", () => {
|
|
5471
|
+
const { lang } = program.opts();
|
|
5472
|
+
if (lang !== undefined)
|
|
5473
|
+
ctx.language = lang;
|
|
5474
|
+
});
|
|
5402
5475
|
registerToolCommands(program, ctx);
|
|
5403
5476
|
registerRunCommand(program, ctx);
|
|
5404
5477
|
registerRunsCommand(program, ctx);
|
|
@@ -5434,16 +5507,8 @@ async function runCli(ctx, args) {
|
|
|
5434
5507
|
return ctx.exitCode;
|
|
5435
5508
|
}
|
|
5436
5509
|
function wantsJson(program, args) {
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
for (;index < args.length; index++) {
|
|
5440
|
-
const arg = args[index] ?? "";
|
|
5441
|
-
const next = command.commands.find((sub) => sub.name() === arg || sub.aliases().includes(arg));
|
|
5442
|
-
if (!next)
|
|
5443
|
-
break;
|
|
5444
|
-
command = next;
|
|
5445
|
-
}
|
|
5446
|
-
const rest = args.slice(index);
|
|
5510
|
+
const { command, index, local } = resolveCommand(program, args);
|
|
5511
|
+
const rest = local.slice(index);
|
|
5447
5512
|
if (command.commands.length > 0) {
|
|
5448
5513
|
const end = rest.indexOf("--");
|
|
5449
5514
|
return (end === -1 ? rest : rest.slice(0, end)).some((arg) => arg === "--json" || /^-[A-Za-z]*j[A-Za-z]*$/.test(arg));
|
|
@@ -5481,14 +5546,39 @@ function wantsJson(program, args) {
|
|
|
5481
5546
|
return false;
|
|
5482
5547
|
}
|
|
5483
5548
|
function withImplicitHelp(program, args) {
|
|
5549
|
+
const { command, index, local } = resolveCommand(program, args);
|
|
5550
|
+
if (index < local.length)
|
|
5551
|
+
return args;
|
|
5552
|
+
return command.commands.length > 0 ? [...args, "--help"] : args;
|
|
5553
|
+
}
|
|
5554
|
+
function resolveCommand(program, args) {
|
|
5555
|
+
const local = [];
|
|
5556
|
+
for (let i = 0;i < args.length; i++) {
|
|
5557
|
+
const arg = args[i] ?? "";
|
|
5558
|
+
if (arg === "--") {
|
|
5559
|
+
local.push(...args.slice(i));
|
|
5560
|
+
break;
|
|
5561
|
+
}
|
|
5562
|
+
const global = program.options.find((o) => o.long === arg || o.short === arg);
|
|
5563
|
+
if (global) {
|
|
5564
|
+
if (global.required)
|
|
5565
|
+
i++;
|
|
5566
|
+
continue;
|
|
5567
|
+
}
|
|
5568
|
+
if (program.options.some((o) => o.long !== undefined && arg.startsWith(`${o.long}=`)))
|
|
5569
|
+
continue;
|
|
5570
|
+
local.push(arg);
|
|
5571
|
+
}
|
|
5484
5572
|
let command = program;
|
|
5485
|
-
|
|
5573
|
+
let index = 0;
|
|
5574
|
+
for (;index < local.length; index++) {
|
|
5575
|
+
const arg = local[index] ?? "";
|
|
5486
5576
|
const next = command.commands.find((sub) => sub.name() === arg || sub.aliases().includes(arg));
|
|
5487
5577
|
if (!next)
|
|
5488
|
-
|
|
5578
|
+
break;
|
|
5489
5579
|
command = next;
|
|
5490
5580
|
}
|
|
5491
|
-
return command
|
|
5581
|
+
return { command, index, local };
|
|
5492
5582
|
}
|
|
5493
5583
|
|
|
5494
5584
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -215,7 +215,7 @@ Use these only when the user asks to publish, change or remove *their* Skill on
|
|
|
215
215
|
SkillHub; finding and downloading Skills is always fine.
|
|
216
216
|
|
|
217
217
|
```bash
|
|
218
|
-
solgrid skills list -q "<words>" --json # search
|
|
218
|
+
solgrid skills list -q "<words>" --json # search by title
|
|
219
219
|
solgrid skills get -s <slug> --json # details
|
|
220
220
|
solgrid skills download -s <slug> -o <path> # the Skill package (.zip)
|
|
221
221
|
```
|
|
@@ -248,11 +248,17 @@ solgrid skills create -t "<title>" --kind <image|video|web|audio> \
|
|
|
248
248
|
--tag <tag-id> --yes --json
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
+
Tag names come in English. To show them to a user in Chinese, add the global
|
|
252
|
+
`--lang zh` (e.g. `solgrid skills tags --lang zh --json`); it also turns the
|
|
253
|
+
server's error messages into Chinese. Always pass tag **IDs** to `--tag`.
|
|
254
|
+
|
|
251
255
|
- A Skill is **public as soon as it is created**, and its slug can never be
|
|
252
256
|
changed. Confirm the title, slug (the `name` in `SKILL.md`) and files with
|
|
253
257
|
the user before passing `--yes`.
|
|
254
258
|
- `--kind` is what the Skill produces. Every kind except `video` needs
|
|
255
|
-
`--cover
|
|
259
|
+
`--cover` (an image). The `--preview` type follows the kind: an image for
|
|
260
|
+
`image` and `web` (optional), a video for `video`, an audio file for `audio`
|
|
261
|
+
(both required).
|
|
256
262
|
- `-d` is a short plain-text line shown under the title; the Skill's real
|
|
257
263
|
instructions belong in `SKILL.md`.
|
|
258
264
|
- If the CLI refuses the directory, it says which rule it breaks (for example
|