@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.
Files changed (2) hide show
  1. package/dist/index.js +104 -41
  2. 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().length(8),
482
- // shown to the user: e.g. "KORDIS-AB12"
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((resolve2) => setTimeout(resolve2, 150));
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
- const parsed = text ? JSON.parse(text) : void 0;
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((resolve2) => setTimeout(resolve2, REPORT_TIMEOUT_MS))
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 !p.split("/").some((seg) => seg === "..");
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) throw new Error(".skill file is empty");
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
- throw new Error(`.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`);
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 new Error(
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 new Error(
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) throw new Error('full_slug must be "{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
- throw new Error("package stored content is not currently valid");
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 = join9(installRoot, f.path);
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 = join9(installRoot, f.path);
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
- throw new Error('full_slug must be "{username}/{slug}"');
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
- throw new Error('full_slug must be "{username}/{slug}"');
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
- throw new Error('full_slug must be "{username}/{slug}"');
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
- throw new Error('full_slug must be "{username}/{slug}"');
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
- throw new Error('full_slug must be "{username}/{slug}"');
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
- throw new Error('full_slug must be "{username}/{slug}"');
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 they may not have starred any public packages, or have set stars to private`));
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
- void reportError("network_unreachable", err);
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
- void reportError("unhandled_error", err);
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.0",
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": "^7.9.0"
40
+ "keytar": "7.9.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@kordis/api-client": "workspace:*",