@rayul/kordis 0.11.0 → 0.11.1
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/dist/index.js +104 -41
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ var init_primitives = __esm({
|
|
|
20
20
|
UsernameSchema = z.string().min(3).max(32).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "username must be lowercase alphanumeric with single hyphens");
|
|
21
21
|
SlugSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "slug must be lowercase alphanumeric with single hyphens");
|
|
22
22
|
FullSlugSchema = z.string().min(5).max(128).regex(/^[a-z0-9](-?[a-z0-9])*\/[a-z0-9](-?[a-z0-9])*$/, "full_slug must match {username}/{slug}");
|
|
23
|
-
SemverSchema = z.string().regex(
|
|
23
|
+
SemverSchema = z.string().max(32).regex(
|
|
24
24
|
// eslint-disable-next-line no-useless-escape
|
|
25
25
|
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,
|
|
26
26
|
"must be a valid semver version"
|
|
@@ -478,8 +478,8 @@ var init_auth = __esm({
|
|
|
478
478
|
});
|
|
479
479
|
DeviceCodeResponseSchema = z8.object({
|
|
480
480
|
device_code: z8.string().min(16),
|
|
481
|
-
user_code: z8.string().
|
|
482
|
-
// shown to the user: e.g. "KORDIS-
|
|
481
|
+
user_code: z8.string().regex(/^KORDIS-[0-9A-Z]{4}-[0-9A-Z]{4}$/),
|
|
482
|
+
// shown to the user: e.g. "KORDIS-7QFX-2M9K"
|
|
483
483
|
verification_url: z8.string().url(),
|
|
484
484
|
expires_in: z8.number().int().positive(),
|
|
485
485
|
// seconds
|
|
@@ -1153,7 +1153,7 @@ var init_client = __esm({
|
|
|
1153
1153
|
`[kordis] api: ${method} ${url.pathname}${url.search} \u2192 fetch failed, retrying in 150ms (${err.message})`
|
|
1154
1154
|
);
|
|
1155
1155
|
}
|
|
1156
|
-
await new Promise((
|
|
1156
|
+
await new Promise((resolve3) => setTimeout(resolve3, 150));
|
|
1157
1157
|
continue;
|
|
1158
1158
|
}
|
|
1159
1159
|
}
|
|
@@ -1178,7 +1178,12 @@ var init_client = __esm({
|
|
|
1178
1178
|
}
|
|
1179
1179
|
if (res.status === 204) return void 0;
|
|
1180
1180
|
const text = await res.text();
|
|
1181
|
-
|
|
1181
|
+
let parsed;
|
|
1182
|
+
try {
|
|
1183
|
+
parsed = text ? JSON.parse(text) : void 0;
|
|
1184
|
+
} catch {
|
|
1185
|
+
parsed = void 0;
|
|
1186
|
+
}
|
|
1182
1187
|
if (!res.ok) {
|
|
1183
1188
|
const envelope = normalizeErrorEnvelope(res.status, parsed, text);
|
|
1184
1189
|
throw new KordisApiError(res.status, envelope, requestId);
|
|
@@ -1919,7 +1924,7 @@ async function reportError(kind, err, extras = {}) {
|
|
|
1919
1924
|
});
|
|
1920
1925
|
await Promise.race([
|
|
1921
1926
|
client.recordTelemetryError(payload),
|
|
1922
|
-
new Promise((
|
|
1927
|
+
new Promise((resolve3) => setTimeout(resolve3, REPORT_TIMEOUT_MS))
|
|
1923
1928
|
]);
|
|
1924
1929
|
} catch {
|
|
1925
1930
|
}
|
|
@@ -2505,8 +2510,8 @@ function normaliseDep(entry, idx) {
|
|
|
2505
2510
|
);
|
|
2506
2511
|
}
|
|
2507
2512
|
return {
|
|
2508
|
-
full_slug: m[1],
|
|
2509
|
-
version_range: (m[4] ?? "*").trim()
|
|
2513
|
+
full_slug: FullSlugSchema.parse(m[1]),
|
|
2514
|
+
version_range: SemverRangeSchema.parse((m[4] ?? "*").trim())
|
|
2510
2515
|
};
|
|
2511
2516
|
}
|
|
2512
2517
|
if (typeof entry === "object" && entry !== null) {
|
|
@@ -2962,7 +2967,7 @@ var ASSET_MAX_COUNT = 50;
|
|
|
2962
2967
|
var SAFE_PATH_RE = /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/;
|
|
2963
2968
|
function isAssetPathSafe(p) {
|
|
2964
2969
|
if (!SAFE_PATH_RE.test(p)) return false;
|
|
2965
|
-
return
|
|
2970
|
+
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
2966
2971
|
}
|
|
2967
2972
|
function validateAssets(assets) {
|
|
2968
2973
|
const flags = [];
|
|
@@ -3255,7 +3260,11 @@ async function bundleSkillZip(zipPath) {
|
|
|
3255
3260
|
const buf = await readFile6(zipPath);
|
|
3256
3261
|
const zip = await JSZip.loadAsync(buf);
|
|
3257
3262
|
const entries = Object.keys(zip.files).filter((k) => !zip.files[k].dir);
|
|
3258
|
-
if (entries.length === 0)
|
|
3263
|
+
if (entries.length === 0) {
|
|
3264
|
+
const e = new Error(".skill file is empty");
|
|
3265
|
+
e.hint = "The .skill archive contains no files. Re-export the .skill from skill-creator, or push the unpacked skill directory instead (the dir containing SKILL.md).";
|
|
3266
|
+
throw e;
|
|
3267
|
+
}
|
|
3259
3268
|
const firstParts = entries[0].split("/");
|
|
3260
3269
|
const prefix = firstParts.length > 1 ? firstParts[0] + "/" : "";
|
|
3261
3270
|
const stripped = entries.map((k) => ({
|
|
@@ -3264,7 +3273,11 @@ async function bundleSkillZip(zipPath) {
|
|
|
3264
3273
|
})).filter((f) => f.rel.length > 0 && !shouldExclude(f.rel, false));
|
|
3265
3274
|
const skillEntry = stripped.find((f) => f.rel === "SKILL.md");
|
|
3266
3275
|
if (!skillEntry) {
|
|
3267
|
-
|
|
3276
|
+
const e = new Error(
|
|
3277
|
+
`.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`
|
|
3278
|
+
);
|
|
3279
|
+
e.hint = "SKILL.md must sit at <skill-name>/SKILL.md at the archive root. Re-package so the single top-level directory contains SKILL.md (skill-creator does this by default).";
|
|
3280
|
+
throw e;
|
|
3268
3281
|
}
|
|
3269
3282
|
const content = await zip.files[skillEntry.zipKey].async("string");
|
|
3270
3283
|
const assets = [];
|
|
@@ -3287,7 +3300,7 @@ async function bundleSkillZip(zipPath) {
|
|
|
3287
3300
|
// src/commands/pull.ts
|
|
3288
3301
|
import { Command as Command5 } from "commander";
|
|
3289
3302
|
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
3290
|
-
import { dirname as dirname2, join as join9 } from "path";
|
|
3303
|
+
import { dirname as dirname2, join as join9, resolve as resolve2, sep as sep2 } from "path";
|
|
3291
3304
|
|
|
3292
3305
|
// ../../packages/translators/src/skill/identity.ts
|
|
3293
3306
|
function buildSkillFile(input, outputPath) {
|
|
@@ -3531,13 +3544,13 @@ var mcpToContinue = (input) => {
|
|
|
3531
3544
|
}
|
|
3532
3545
|
if (t.env && Object.keys(t.env).length > 0) {
|
|
3533
3546
|
lines.push("env:");
|
|
3534
|
-
for (const [k, v] of Object.entries(t.env)) lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
3547
|
+
for (const [k, v] of Object.entries(t.env)) lines.push(` ${JSON.stringify(k)}: ${JSON.stringify(v)}`);
|
|
3535
3548
|
}
|
|
3536
3549
|
} else {
|
|
3537
3550
|
lines.push(`type: ${t.type}`, `url: ${JSON.stringify(t.url)}`);
|
|
3538
3551
|
if (t.headers && Object.keys(t.headers).length > 0) {
|
|
3539
3552
|
lines.push("headers:");
|
|
3540
|
-
for (const [k, v] of Object.entries(t.headers)) lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
3553
|
+
for (const [k, v] of Object.entries(t.headers)) lines.push(` ${JSON.stringify(k)}: ${JSON.stringify(v)}`);
|
|
3541
3554
|
}
|
|
3542
3555
|
}
|
|
3543
3556
|
return {
|
|
@@ -3764,16 +3777,23 @@ async function recordInstalled(fullSlug, entry) {
|
|
|
3764
3777
|
// src/lib/parse-username.ts
|
|
3765
3778
|
init_src();
|
|
3766
3779
|
var SORT_KEYWORDS = ["latest", "popular", "recent", "updated", "stars", "downloads"];
|
|
3780
|
+
function usernameError(message, hint) {
|
|
3781
|
+
const e = new Error(message);
|
|
3782
|
+
e.hint = hint;
|
|
3783
|
+
return e;
|
|
3784
|
+
}
|
|
3767
3785
|
function parseUsername(raw) {
|
|
3768
3786
|
if (SORT_KEYWORDS.includes(raw.toLowerCase())) {
|
|
3769
|
-
throw
|
|
3770
|
-
`"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed)
|
|
3787
|
+
throw usernameError(
|
|
3788
|
+
`"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed).`,
|
|
3789
|
+
`Pass it as the sort flag instead, e.g. \`--sort ${raw}\`, or supply an actual username argument.`
|
|
3771
3790
|
);
|
|
3772
3791
|
}
|
|
3773
3792
|
const parsed = UsernameSchema.safeParse(raw);
|
|
3774
3793
|
if (!parsed.success) {
|
|
3775
|
-
throw
|
|
3776
|
-
`invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")
|
|
3794
|
+
throw usernameError(
|
|
3795
|
+
`invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")`,
|
|
3796
|
+
"Usernames are lowercase alphanumeric with single hyphens. Browse existing users with `kordis search` or look one up with `kordis profile <username>`."
|
|
3777
3797
|
);
|
|
3778
3798
|
}
|
|
3779
3799
|
return parsed.data;
|
|
@@ -3839,7 +3859,11 @@ function parseSlugAtVersion(raw) {
|
|
|
3839
3859
|
// src/commands/pull.ts
|
|
3840
3860
|
async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
3841
3861
|
const [username, slug] = fullSlug.split("/");
|
|
3842
|
-
if (!username || !slug)
|
|
3862
|
+
if (!username || !slug) {
|
|
3863
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
3864
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
3865
|
+
throw e;
|
|
3866
|
+
}
|
|
3843
3867
|
const bundle = await useClient();
|
|
3844
3868
|
const pullRes = await bundle.api.pull(username, slug, {
|
|
3845
3869
|
...opts.version ? { version: opts.version } : {},
|
|
@@ -3854,7 +3878,9 @@ async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
|
3854
3878
|
});
|
|
3855
3879
|
const parsed = validatePackageSource(contentRes.content);
|
|
3856
3880
|
if (!parsed.ok || !parsed.type || !parsed.frontmatter) {
|
|
3857
|
-
|
|
3881
|
+
const e = new Error("package stored content is not currently valid");
|
|
3882
|
+
e.hint = `This version's stored bytes no longer pass validation. Try another version via \`kordis versions ${fullSlug}\` (then pull a specific \`@<version>\`), or re-push a corrected version if you authored it.`;
|
|
3883
|
+
throw e;
|
|
3858
3884
|
}
|
|
3859
3885
|
const written = [];
|
|
3860
3886
|
const notes = [];
|
|
@@ -3869,7 +3895,7 @@ async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
|
3869
3895
|
assets: contentRes.assets
|
|
3870
3896
|
});
|
|
3871
3897
|
for (const f of result.files) {
|
|
3872
|
-
const abs =
|
|
3898
|
+
const abs = safeInstallPath(installRoot, f.path);
|
|
3873
3899
|
await mkdir6(dirname2(abs), { recursive: true });
|
|
3874
3900
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3875
3901
|
written.push(abs);
|
|
@@ -3889,7 +3915,7 @@ async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
|
3889
3915
|
`Tool "${format}" is not supported for type "${parsed.type}"; installed canonical form instead.`
|
|
3890
3916
|
);
|
|
3891
3917
|
for (const f of result.files) {
|
|
3892
|
-
const abs =
|
|
3918
|
+
const abs = safeInstallPath(installRoot, f.path);
|
|
3893
3919
|
await mkdir6(dirname2(abs), { recursive: true });
|
|
3894
3920
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3895
3921
|
written.push(abs);
|
|
@@ -4052,6 +4078,26 @@ async function detectTool(root) {
|
|
|
4052
4078
|
}
|
|
4053
4079
|
return null;
|
|
4054
4080
|
}
|
|
4081
|
+
function safeInstallPath(installRoot, relPath) {
|
|
4082
|
+
if (relPath.startsWith("/") || resolve2(relPath) === relPath) {
|
|
4083
|
+
const e = new Error(
|
|
4084
|
+
`refusing to write absolute path "${relPath}" outside the install root`
|
|
4085
|
+
);
|
|
4086
|
+
e.hint = "Package emitted an absolute file path. Re-push the package with relative paths only; report it to the author if you did not publish it.";
|
|
4087
|
+
throw e;
|
|
4088
|
+
}
|
|
4089
|
+
const abs = join9(installRoot, relPath);
|
|
4090
|
+
const resolvedRoot = resolve2(installRoot);
|
|
4091
|
+
const resolvedAbs = resolve2(abs);
|
|
4092
|
+
if (resolvedAbs !== resolvedRoot && !resolvedAbs.startsWith(resolvedRoot + sep2)) {
|
|
4093
|
+
const e = new Error(
|
|
4094
|
+
`refusing to write "${relPath}" \u2014 resolves outside the install root`
|
|
4095
|
+
);
|
|
4096
|
+
e.hint = "Package tried to write outside the install directory (path traversal). Re-push the package with relative in-tree paths only; report it to the author if you did not publish it.";
|
|
4097
|
+
throw e;
|
|
4098
|
+
}
|
|
4099
|
+
return abs;
|
|
4100
|
+
}
|
|
4055
4101
|
async function writeEmittedFile(abs, content, encoding) {
|
|
4056
4102
|
if (encoding === "base64") {
|
|
4057
4103
|
await writeFile6(abs, Buffer.from(content, "base64"));
|
|
@@ -4549,7 +4595,9 @@ function commentsCommand() {
|
|
|
4549
4595
|
async (rawSlug, opts) => {
|
|
4550
4596
|
const [username, slug] = rawSlug.split("/");
|
|
4551
4597
|
if (!username || !slug) {
|
|
4552
|
-
|
|
4598
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
4599
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
4600
|
+
throw e;
|
|
4553
4601
|
}
|
|
4554
4602
|
if (opts.kind && !KINDS.includes(opts.kind)) {
|
|
4555
4603
|
throw new Error(`--kind must be one of ${KINDS.join(", ")}`);
|
|
@@ -4564,14 +4612,13 @@ function commentsCommand() {
|
|
|
4564
4612
|
...opts.kind ? { kind: opts.kind } : {},
|
|
4565
4613
|
...opts.sort ? { sort: opts.sort } : {}
|
|
4566
4614
|
});
|
|
4615
|
+
const emptyHint = "no comments yet \u2014 be the first: kordis comment " + rawSlug + ' --body "..."';
|
|
4567
4616
|
printDual({
|
|
4568
|
-
json: res,
|
|
4617
|
+
json: res.results.length === 0 ? { ...res, hint: emptyHint } : res,
|
|
4569
4618
|
human: () => {
|
|
4570
4619
|
if (res.results.length === 0) {
|
|
4571
4620
|
printHuman(c.yellow("(no comments)"));
|
|
4572
|
-
printHuman(
|
|
4573
|
-
c.gray(" \u2192 be the first: kordis comment " + rawSlug + ' --body "..."')
|
|
4574
|
-
);
|
|
4621
|
+
printHuman(c.gray(" \u2192 " + emptyHint));
|
|
4575
4622
|
return;
|
|
4576
4623
|
}
|
|
4577
4624
|
printHuman(
|
|
@@ -4639,7 +4686,9 @@ function starCommand() {
|
|
|
4639
4686
|
return new Command11("star").description("star a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
|
|
4640
4687
|
const [username, slug] = rawSlug.split("/");
|
|
4641
4688
|
if (!username || !slug) {
|
|
4642
|
-
|
|
4689
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
4690
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
4691
|
+
throw e;
|
|
4643
4692
|
}
|
|
4644
4693
|
const bundle = await useClient();
|
|
4645
4694
|
requireAuth(bundle);
|
|
@@ -4657,7 +4706,9 @@ function unstarCommand() {
|
|
|
4657
4706
|
return new Command11("unstar").description("remove your star from a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
|
|
4658
4707
|
const [username, slug] = rawSlug.split("/");
|
|
4659
4708
|
if (!username || !slug) {
|
|
4660
|
-
|
|
4709
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
4710
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
4711
|
+
throw e;
|
|
4661
4712
|
}
|
|
4662
4713
|
const bundle = await useClient();
|
|
4663
4714
|
requireAuth(bundle);
|
|
@@ -4681,7 +4732,9 @@ function deleteCommand() {
|
|
|
4681
4732
|
return new Command12("delete").description("permanently delete a package you own").argument("<full_slug>", "package identifier {username}/{slug}").option("--force", "skip the confirmation prompt").action(async (fullSlug, opts) => {
|
|
4682
4733
|
const [username, slug] = fullSlug.split("/");
|
|
4683
4734
|
if (!username || !slug) {
|
|
4684
|
-
|
|
4735
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
4736
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
4737
|
+
throw e;
|
|
4685
4738
|
}
|
|
4686
4739
|
const bundle = await useClient();
|
|
4687
4740
|
const detail = await bundle.api.getPackage(username, slug);
|
|
@@ -4889,11 +4942,13 @@ function listCommand() {
|
|
|
4889
4942
|
sort: publicSort,
|
|
4890
4943
|
...opts.cursor ? { cursor: opts.cursor } : {}
|
|
4891
4944
|
});
|
|
4945
|
+
const emptyHint = `no public packages for ${username} \u2014 verify the username via \`kordis profile ${username}\`, or browse the registry via \`kordis search\``;
|
|
4892
4946
|
printDual({
|
|
4893
|
-
json: res,
|
|
4947
|
+
json: res.results.length === 0 ? { ...res, hint: emptyHint } : res,
|
|
4894
4948
|
human: () => {
|
|
4895
4949
|
if (res.results.length === 0) {
|
|
4896
4950
|
printHuman(c.yellow(`(no public packages for ${username})`));
|
|
4951
|
+
printHuman(c.gray(` \u2192 ${emptyHint}`));
|
|
4897
4952
|
return;
|
|
4898
4953
|
}
|
|
4899
4954
|
for (const p of res.results) {
|
|
@@ -4919,7 +4974,9 @@ function versionsCommand() {
|
|
|
4919
4974
|
return new Command16("versions").description("list all versions of a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
|
|
4920
4975
|
const [username, slug] = fullSlug.split("/");
|
|
4921
4976
|
if (!username || !slug) {
|
|
4922
|
-
|
|
4977
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
4978
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
4979
|
+
throw e;
|
|
4923
4980
|
}
|
|
4924
4981
|
const bundle = await useClient();
|
|
4925
4982
|
const spin = spinner(`Fetching versions for ${fullSlug}`);
|
|
@@ -5233,7 +5290,9 @@ function diffCommand() {
|
|
|
5233
5290
|
}
|
|
5234
5291
|
const [username, slug] = fullSlug.split("/");
|
|
5235
5292
|
if (!username || !slug) {
|
|
5236
|
-
|
|
5293
|
+
const e = new Error('full_slug must be "{username}/{slug}"');
|
|
5294
|
+
e.hint = 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.';
|
|
5295
|
+
throw e;
|
|
5237
5296
|
}
|
|
5238
5297
|
const bundle = await useClient();
|
|
5239
5298
|
if (!from || !to) {
|
|
@@ -5326,8 +5385,9 @@ function recommendCommand() {
|
|
|
5326
5385
|
spin.succeed();
|
|
5327
5386
|
await writeCache(res.items, res.generated_at);
|
|
5328
5387
|
await markSeen();
|
|
5388
|
+
const emptyHint = !res.personalized ? "personalization is OFF, so the server ranks on popularity alone \u2014 likely empty pool. Enable for stack-aware matches: `kordis setup --personalization on`, or broaden via `kordis search <topic>` / `kordis search --trending`." : "personalization is ON but no matches for your stack \u2014 registry may be sparse for this profile. Try `kordis search <topic>` or `kordis search --trending`.";
|
|
5329
5389
|
printDual({
|
|
5330
|
-
json: res,
|
|
5390
|
+
json: res.items.length === 0 ? { ...res, hint: emptyHint } : res,
|
|
5331
5391
|
human: () => {
|
|
5332
5392
|
if (res.items.length === 0) {
|
|
5333
5393
|
printHuman(c.yellow("(no recommendations available right now)"));
|
|
@@ -5449,12 +5509,13 @@ function starsCommand() {
|
|
|
5449
5509
|
limit: opts.limit,
|
|
5450
5510
|
...opts.cursor ? { cursor: opts.cursor } : {}
|
|
5451
5511
|
});
|
|
5512
|
+
const emptyHint = `no public stars for ${username} \u2014 they may have none, or keep them private`;
|
|
5452
5513
|
printDual({
|
|
5453
|
-
json: res,
|
|
5514
|
+
json: res.results.length === 0 ? { ...res, hint: emptyHint } : res,
|
|
5454
5515
|
human: () => {
|
|
5455
5516
|
if (res.results.length === 0) {
|
|
5456
5517
|
printHuman(c.yellow(`(no stars yet for ${username})`));
|
|
5457
|
-
printHuman(c.gray(` \u2192
|
|
5518
|
+
printHuman(c.gray(` \u2192 ${emptyHint}`));
|
|
5458
5519
|
return;
|
|
5459
5520
|
}
|
|
5460
5521
|
for (const p of res.results) {
|
|
@@ -5519,12 +5580,14 @@ function makeListing(name) {
|
|
|
5519
5580
|
limit: opts.limit,
|
|
5520
5581
|
...opts.cursor ? { cursor: opts.cursor } : {}
|
|
5521
5582
|
});
|
|
5583
|
+
const emptyHint = name === "followers" ? `no followers yet for ${username} \u2014 verify the username via \`kordis profile ${username}\`, or this account simply has no followers` : `${username} isn't following anyone yet \u2014 verify the username via \`kordis profile ${username}\``;
|
|
5522
5584
|
printDual({
|
|
5523
|
-
json: res,
|
|
5585
|
+
json: res.results.length === 0 ? { ...res, hint: emptyHint } : res,
|
|
5524
5586
|
human: () => {
|
|
5525
5587
|
if (res.results.length === 0) {
|
|
5526
5588
|
const noun = name === "followers" ? "followers yet" : "one yet";
|
|
5527
5589
|
printHuman(c.yellow(`(no ${noun} for ${username})`));
|
|
5590
|
+
printHuman(c.gray(` \u2192 ${emptyHint}`));
|
|
5528
5591
|
return;
|
|
5529
5592
|
}
|
|
5530
5593
|
for (const u of res.results) {
|
|
@@ -5604,10 +5667,10 @@ async function main(argv) {
|
|
|
5604
5667
|
const cleaned = argv.filter((a, i) => !(a === "--" && i >= 2));
|
|
5605
5668
|
await program.parseAsync(cleaned);
|
|
5606
5669
|
} catch (err) {
|
|
5607
|
-
handleError(err);
|
|
5670
|
+
await handleError(err);
|
|
5608
5671
|
}
|
|
5609
5672
|
}
|
|
5610
|
-
function handleError(err) {
|
|
5673
|
+
async function handleError(err) {
|
|
5611
5674
|
if (err instanceof KordisApiError) {
|
|
5612
5675
|
if (isJsonMode()) {
|
|
5613
5676
|
printJson({
|
|
@@ -5654,7 +5717,7 @@ function handleError(err) {
|
|
|
5654
5717
|
printHuman(c.red(`\u2717 ${err.message}`));
|
|
5655
5718
|
for (const line of hint.split("\n")) printHuman(c.gray(` \u2192 ${line}`));
|
|
5656
5719
|
}
|
|
5657
|
-
|
|
5720
|
+
await reportError("network_unreachable", err);
|
|
5658
5721
|
process.exit(err.exitCode());
|
|
5659
5722
|
}
|
|
5660
5723
|
if (err instanceof Error) {
|
|
@@ -5674,7 +5737,7 @@ function handleError(err) {
|
|
|
5674
5737
|
for (const line of hintStr.split("\n")) printHuman(c.gray(` \u2192 ${line}`));
|
|
5675
5738
|
}
|
|
5676
5739
|
}
|
|
5677
|
-
|
|
5740
|
+
await reportError("unhandled_error", err);
|
|
5678
5741
|
process.exit(1);
|
|
5679
5742
|
}
|
|
5680
5743
|
if (isJsonMode()) printJson({ error: { code: "generic_failure", message: String(err) } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayul/kordis",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command-line client for the Kordis registry — discover, install, and publish agentic-coding packages.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"zod": "^3.23.8"
|
|
38
38
|
},
|
|
39
39
|
"optionalDependencies": {
|
|
40
|
-
"keytar": "
|
|
40
|
+
"keytar": "7.9.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@kordis/api-client": "workspace:*",
|