@kitsy/cnos-cli 1.1.0 → 1.1.2

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 +189 -55
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ var COMMAND_OPTION_KEYS_WITH_VALUE = /* @__PURE__ */ new Set([
15
15
  "--to",
16
16
  "--provider",
17
17
  "--passphrase",
18
+ "--vault",
18
19
  "--inherit"
19
20
  ]);
20
21
  var COMMAND_FLAG_KEYS = /* @__PURE__ */ new Set(["--flatten", "--public", "--local", "--remote", "--ref"]);
@@ -99,6 +100,10 @@ function parseArgs(argv) {
99
100
  options.json = true;
100
101
  continue;
101
102
  }
103
+ if (token === "--verbose") {
104
+ options.verbose = true;
105
+ continue;
106
+ }
102
107
  if (token === "--help" || token === "-h") {
103
108
  options.help = true;
104
109
  continue;
@@ -181,12 +186,13 @@ function printJson(value) {
181
186
  }
182
187
 
183
188
  // src/services/writes.ts
184
- import { randomUUID } from "crypto";
185
189
  import { mkdir, readFile, rm, writeFile } from "fs/promises";
186
190
  import path from "path";
187
191
  import {
192
+ createSecretVault,
188
193
  parseYaml,
189
194
  resolveConfigDocumentPath,
195
+ resolveSecretPassphrase,
190
196
  resolveSecretStoreRoot,
191
197
  stringifyYaml,
192
198
  writeLocalSecret
@@ -315,6 +321,22 @@ async function defineValue(namespace, configPath, rawValue, options = {}) {
315
321
  value: parsedValue
316
322
  };
317
323
  }
324
+ async function createVault(vault, options = {}) {
325
+ const passphrase = options.passphrase ?? resolveSecretPassphrase(vault, options.processEnv ?? process.env);
326
+ if (!passphrase) {
327
+ throw new Error("Vault creation requires --passphrase or CNOS_SECRET_PASSPHRASE");
328
+ }
329
+ const normalizedVault = vault.trim() || "default";
330
+ const filePath = await createSecretVault(
331
+ resolveSecretStoreRoot(options.processEnv),
332
+ normalizedVault,
333
+ passphrase
334
+ );
335
+ return {
336
+ vault: normalizedVault,
337
+ filePath
338
+ };
339
+ }
318
340
  async function setSecret(configPath, rawValue, options = {}) {
319
341
  const runtime = await createRuntimeService(options);
320
342
  const workspaceRoot = getSelectedWorkspaceRoot(options, runtime);
@@ -322,25 +344,20 @@ async function setSecret(configPath, rawValue, options = {}) {
322
344
  const filePath = resolveConfigDocumentPath(workspaceRoot, "secret", configPath, profile);
323
345
  const document = await readYamlDocument(filePath);
324
346
  const mode = options.mode ?? "local";
347
+ const vault = options.vault?.trim() || "default";
325
348
  let reference;
326
349
  let storePath;
327
350
  if (mode === "local") {
328
- const passphrase = options.passphrase ?? options.processEnv?.CNOS_SECRET_PASSPHRASE ?? process.env.CNOS_SECRET_PASSPHRASE;
351
+ const passphrase = options.passphrase ?? resolveSecretPassphrase(vault, options.processEnv ?? process.env);
329
352
  if (!passphrase) {
330
- throw new Error("Local secret writes require --passphrase or CNOS_SECRET_PASSPHRASE");
353
+ throw new Error(`Vault "${vault}" requires --passphrase or CNOS_SECRET_PASSPHRASE`);
331
354
  }
332
- const profileSegment = profile && profile !== "base" ? profile : "base";
333
- const ref = [
334
- runtime.manifest.project.name,
335
- runtime.graph.workspace.workspaceId,
336
- profileSegment,
337
- ...configPath.split("."),
338
- randomUUID()
339
- ].map((segment) => segment.replace(/[^A-Za-z0-9._-]+/g, "-")).join("/");
340
- storePath = await writeLocalSecret(resolveSecretStoreRoot(options.processEnv), ref, rawValue, passphrase);
355
+ const ref = configPath;
356
+ storePath = await writeLocalSecret(resolveSecretStoreRoot(options.processEnv), ref, rawValue, passphrase, vault);
341
357
  reference = {
342
358
  provider: "local",
343
- ref
359
+ ref,
360
+ vault
344
361
  };
345
362
  } else {
346
363
  reference = {
@@ -355,6 +372,7 @@ async function setSecret(configPath, rawValue, options = {}) {
355
372
  filePath,
356
373
  provider: reference.provider,
357
374
  ref: reference.ref,
375
+ ...reference.vault ? { vault: reference.vault } : {},
358
376
  ...storePath ? { storePath } : {}
359
377
  };
360
378
  }
@@ -376,7 +394,13 @@ async function deleteSecret(configPath, options = {}) {
376
394
  let removedStore;
377
395
  const secretRef = metadata?.secretRef;
378
396
  if (isSecretReference(secretRef) && secretRef.provider === "local") {
379
- const storePath = path.join(resolveSecretStoreRoot(options.processEnv), "store", ...secretRef.ref.split("/")).concat(".json");
397
+ const storePath = path.join(
398
+ resolveSecretStoreRoot(options.processEnv),
399
+ "vaults",
400
+ secretRef.vault ?? "default",
401
+ "store",
402
+ ...secretRef.ref.split("/")
403
+ ).concat(".json");
380
404
  await rm(storePath, { force: true });
381
405
  removedStore = storePath;
382
406
  }
@@ -608,7 +632,12 @@ async function runExportEnv(options = {}) {
608
632
  const env = isPublic ? runtime.toPublicEnv({
609
633
  ...framework ? { framework } : {},
610
634
  ...prefix ? { prefix } : {}
611
- }) : runtime.toEnv();
635
+ }) : Object.fromEntries(
636
+ Object.entries(runtime.manifest.envMapping.explicit).map(([envVar, logicalKey]) => [envVar, runtime.read(logicalKey)]).filter((entry) => entry[1] !== void 0).map(([envVar, value]) => [
637
+ envVar,
638
+ typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" ? String(value) : JSON.stringify(value)
639
+ ])
640
+ );
612
641
  if (options.json) {
613
642
  return printJson(env);
614
643
  }
@@ -645,6 +674,10 @@ var GLOBAL_OPTIONS = [
645
674
  flag: "--json",
646
675
  description: "Emit JSON output for commands that support structured responses."
647
676
  },
677
+ {
678
+ flag: "--verbose",
679
+ description: "Print full stack traces and verbose diagnostics for command failures."
680
+ },
648
681
  {
649
682
  flag: "--help, -h",
650
683
  description: "Show command help."
@@ -810,18 +843,18 @@ var COMMANDS = [
810
843
  {
811
844
  id: "use",
812
845
  summary: "Persist repo-local CLI defaults such as workspace and profile.",
813
- usage: "cnos use [--workspace <id>] [--profile <name>] [--global-root <path>] [--root <path>] [--json]",
814
- description: "Writes .cnos-workspace.yml so future CLI invocations can omit repeated workspace/profile/global-root flags.",
815
- examples: ["cnos use --workspace api --profile stage", "cnos use --global-root ~/.cnos"]
846
+ usage: "cnos use [show] [--workspace <id>] [--profile <name>] [--global-root <path>] [--root <path>] [--json]",
847
+ description: "Shows the current repo-local CLI context by default, or writes .cnos-workspace.yml when workspace/profile/global-root flags are provided.",
848
+ examples: ["cnos use show", "cnos use --workspace api --profile stage", "cnos use --global-root ~/.cnos"]
816
849
  },
817
850
  {
818
851
  id: "list",
819
852
  summary: "List resolved config entries.",
820
- usage: "cnos list [value|secret|meta|all] [--prefix <path>] [global-options]",
821
- description: "Lists resolved config entries across one namespace or the full effective graph, with optional key-prefix filtering.",
853
+ usage: "cnos list [value|secret|meta|env|public|all] [--prefix <path>] [global-options]",
854
+ description: "Lists stored config or derived projections across one namespace or the full effective graph, with optional prefix filtering.",
822
855
  options: [
823
856
  {
824
- flag: "--namespace <value|secret|meta|all>",
857
+ flag: "--namespace <value|secret|meta|env|public|all>",
825
858
  description: "Explicit namespace selector when not using a positional namespace argument."
826
859
  },
827
860
  {
@@ -829,7 +862,7 @@ var COMMANDS = [
829
862
  description: "Filter list output to entries whose logical keys begin with this prefix."
830
863
  }
831
864
  ],
832
- examples: ["cnos list", "cnos list value --prefix app.", "cnos list --namespace secret"]
865
+ examples: ["cnos list", "cnos list value --prefix app.", "cnos list env", "cnos list --namespace secret"]
833
866
  },
834
867
  {
835
868
  id: "profile",
@@ -879,9 +912,19 @@ var COMMANDS = [
879
912
  {
880
913
  id: "secret set",
881
914
  summary: "Write a secret securely.",
882
- usage: "cnos secret set <path> <value> [--local|--remote|--ref] [--provider <name>] [--passphrase <value>] [global-options]",
883
- description: "Writes a secret reference into the repo and, when --local is used, stores encrypted secret material outside the repo under ~/.cnos/secrets.",
884
- examples: ["cnos secret set app.token super-secret --local --passphrase dev-pass"]
915
+ usage: "cnos secret set <path> <value> [--local|--remote|--ref] [--vault <name>] [--provider <name>] [--passphrase <value>] [global-options]",
916
+ description: "Writes a secret reference into the repo and, when --local is used, stores encrypted secret material outside the repo under ~/.cnos/secrets/vaults/<vault>.",
917
+ examples: [
918
+ "cnos secret create vault db --passphrase dev-pass",
919
+ "cnos secret set app.token super-secret --local --vault db --passphrase dev-pass"
920
+ ]
921
+ },
922
+ {
923
+ id: "secret create vault",
924
+ summary: "Create a local secret vault.",
925
+ usage: "cnos secret create vault <name> --passphrase <value> [global-options]",
926
+ description: "Creates a named local secret vault under ~/.cnos/secrets/vaults.",
927
+ examples: ["cnos secret create vault db --passphrase dev-pass"]
885
928
  },
886
929
  {
887
930
  id: "secret list",
@@ -1363,25 +1406,54 @@ function printValue(value, json = false) {
1363
1406
 
1364
1407
  // src/services/listing.ts
1365
1408
  import { flattenObject as flattenObject2 } from "@kitsy/cnos/internal";
1409
+ function matchesPrefix(key, prefix) {
1410
+ if (!prefix) {
1411
+ return true;
1412
+ }
1413
+ return key.startsWith(prefix) || key.split(".").slice(1).join(".").startsWith(prefix);
1414
+ }
1415
+ function toStoredEntry(namespace, entry) {
1416
+ const sourceId = namespace === "value" ? "filesystem-values" : "filesystem-secrets";
1417
+ const candidates = [entry.winner, ...entry.overridden].filter((candidate) => candidate.sourceId === sourceId);
1418
+ if (candidates.length === 0) {
1419
+ return void 0;
1420
+ }
1421
+ return {
1422
+ key: entry.key,
1423
+ value: candidates[0]?.value
1424
+ };
1425
+ }
1426
+ function listStoredNamespace(namespace, options) {
1427
+ return createRuntimeService(options).then(
1428
+ (runtime) => Array.from(runtime.graph.entries.values()).filter((entry) => entry.namespace === namespace).map((entry) => toStoredEntry(namespace, entry)).filter((entry) => Boolean(entry)).filter((entry) => matchesPrefix(entry.key, options.prefix)).sort((left, right) => left.key.localeCompare(right.key))
1429
+ );
1430
+ }
1431
+ function listProjectedNamespace(namespace, options) {
1432
+ return createRuntimeService(options).then((runtime) => {
1433
+ const projected = namespace === "meta" ? flattenObject2(runtime.toNamespace("meta")) : namespace === "env" ? runtime.manifest.envMapping.explicit : runtime.toPublicEnv();
1434
+ const entries = namespace === "env" ? Object.entries(projected).map(([envVar, logicalKey]) => ({
1435
+ key: envVar,
1436
+ value: runtime.read(logicalKey)
1437
+ })) : Object.entries(projected).map(([key, value]) => ({
1438
+ key: namespace === "meta" ? `meta.${key}` : key,
1439
+ value
1440
+ }));
1441
+ return entries.filter((entry) => entry.value !== void 0).filter((entry) => matchesPrefix(entry.key, options.prefix)).sort((left, right) => left.key.localeCompare(right.key));
1442
+ });
1443
+ }
1366
1444
  async function listConfigEntries(namespace, options = {}) {
1367
- const runtime = await createRuntimeService(options);
1368
- const namespaces = namespace === "all" ? ["value", "secret", "meta"] : [namespace];
1369
- const entries = [];
1370
- const prefix = options.prefix?.trim();
1371
- for (const currentNamespace of namespaces) {
1372
- const projected = flattenObject2(runtime.toNamespace(currentNamespace));
1373
- for (const [path10, value] of Object.entries(projected)) {
1374
- const key = `${currentNamespace}.${path10}`;
1375
- if (prefix && !key.startsWith(prefix) && !path10.startsWith(prefix)) {
1376
- continue;
1377
- }
1378
- entries.push({
1379
- key,
1380
- value
1381
- });
1382
- }
1445
+ if (namespace === "value" || namespace === "secret") {
1446
+ return listStoredNamespace(namespace, options);
1383
1447
  }
1384
- return entries.sort((left, right) => left.key.localeCompare(right.key));
1448
+ if (namespace === "meta" || namespace === "env" || namespace === "public") {
1449
+ return listProjectedNamespace(namespace, options);
1450
+ }
1451
+ const [values, secrets, meta] = await Promise.all([
1452
+ listStoredNamespace("value", options),
1453
+ listStoredNamespace("secret", options),
1454
+ listProjectedNamespace("meta", options)
1455
+ ]);
1456
+ return [...values, ...secrets, ...meta].sort((left, right) => left.key.localeCompare(right.key));
1385
1457
  }
1386
1458
 
1387
1459
  // src/commands/list.ts
@@ -1398,6 +1470,12 @@ function normalizeNamespace(value) {
1398
1470
  if (value === "meta") {
1399
1471
  return "meta";
1400
1472
  }
1473
+ if (value === "env") {
1474
+ return "env";
1475
+ }
1476
+ if (value === "public") {
1477
+ return "public";
1478
+ }
1401
1479
  throw new Error(`Unsupported list namespace: ${value}`);
1402
1480
  }
1403
1481
  async function runList(args = [], options = {}) {
@@ -1412,6 +1490,9 @@ async function runList(args = [], options = {}) {
1412
1490
  if (options.json) {
1413
1491
  return printJson(entries);
1414
1492
  }
1493
+ if (entries.length === 0) {
1494
+ return "";
1495
+ }
1415
1496
  return entries.map((entry) => `${entry.key}=${printValue(entry.value)}`).join("\n");
1416
1497
  }
1417
1498
 
@@ -1691,19 +1772,25 @@ function isSecretRef(value) {
1691
1772
  );
1692
1773
  }
1693
1774
  function normalizeSecretCommand(args) {
1694
- const [actionOrPath, ...tail] = args;
1775
+ const [actionOrPath, next, ...tail] = args;
1695
1776
  if (!actionOrPath) {
1696
1777
  return {
1697
1778
  action: "list",
1698
1779
  tail: []
1699
1780
  };
1700
1781
  }
1701
- if (["get", "set", "create", "add", "list", "delete", "remove"].includes(actionOrPath)) {
1782
+ if ((actionOrPath === "create" || actionOrPath === "add") && next === "vault") {
1702
1783
  return {
1703
- action: actionOrPath === "remove" ? "delete" : actionOrPath === "create" || actionOrPath === "add" ? "set" : actionOrPath,
1784
+ action: "create-vault",
1704
1785
  tail
1705
1786
  };
1706
1787
  }
1788
+ if (["get", "set", "list", "delete", "remove"].includes(actionOrPath)) {
1789
+ return {
1790
+ action: actionOrPath === "remove" ? "delete" : actionOrPath,
1791
+ tail: [next, ...tail].filter((value) => Boolean(value))
1792
+ };
1793
+ }
1707
1794
  return {
1708
1795
  action: "get",
1709
1796
  tail: args
@@ -1713,13 +1800,30 @@ async function runSecret(argsOrPath, options = {}) {
1713
1800
  const args = Array.isArray(argsOrPath) ? argsOrPath : [argsOrPath];
1714
1801
  const { action, tail } = normalizeSecretCommand(args);
1715
1802
  const cliArgs = [...options.cliArgs ?? []];
1803
+ if (action === "create-vault") {
1804
+ const vault = tail[0] ?? "default";
1805
+ const passphrase = consumeOption(cliArgs, "--passphrase");
1806
+ const result = await createVault(vault, {
1807
+ ...options,
1808
+ cliArgs,
1809
+ ...passphrase ? { passphrase } : {}
1810
+ });
1811
+ if (options.json) {
1812
+ return printJson(result);
1813
+ }
1814
+ return `created secret vault "${result.vault}" in ${result.filePath}`;
1815
+ }
1716
1816
  if (action === "list") {
1717
- const runtime2 = await createRuntimeService(options);
1718
- const secrets = runtime2.toNamespace("secret");
1817
+ const prefix = consumeOption(cliArgs, "--prefix");
1818
+ const entries = await listConfigEntries("secret", {
1819
+ ...options,
1820
+ cliArgs,
1821
+ ...prefix ? { prefix } : {}
1822
+ });
1719
1823
  if (options.json) {
1720
- return printJson(secrets);
1824
+ return printJson(entries);
1721
1825
  }
1722
- return Object.entries(secrets).sort(([left], [right]) => left.localeCompare(right)).map(([key, value2]) => `${key}=${printValue(value2)}`).join("\n");
1826
+ return entries.map((entry) => `${entry.key}=${printValue(entry.value)}`).join("\n");
1723
1827
  }
1724
1828
  if (action === "set") {
1725
1829
  const secretPath = tail[0];
@@ -1730,19 +1834,21 @@ async function runSecret(argsOrPath, options = {}) {
1730
1834
  const target = consumeOption(cliArgs, "--target") ?? "local";
1731
1835
  const provider = consumeOption(cliArgs, "--provider");
1732
1836
  const passphrase = consumeOption(cliArgs, "--passphrase");
1837
+ const vault = consumeOption(cliArgs, "--vault") ?? "default";
1733
1838
  const mode = local ? "local" : remote ? "remote" : ref ? "ref" : "local";
1734
1839
  const result = await setSecret(secretPath ?? "app.token", rawValue, {
1735
1840
  ...options,
1736
1841
  cliArgs,
1737
1842
  target,
1738
1843
  mode,
1844
+ vault,
1739
1845
  ...provider ? { provider } : {},
1740
1846
  ...passphrase ? { passphrase } : {}
1741
1847
  });
1742
1848
  if (options.json) {
1743
1849
  return printJson(result);
1744
1850
  }
1745
- return `set secret.${secretPath} via ${result.provider} in ${result.filePath}`;
1851
+ return result.provider === "local" ? `set secret.${secretPath} in vault "${result.vault ?? "default"}" with ref "${result.ref}" and repo pointer ${result.filePath}` : `set secret.${secretPath} via ${result.provider} in ${result.filePath}`;
1746
1852
  }
1747
1853
  if (action === "delete") {
1748
1854
  const secretPath = tail[0];
@@ -1763,8 +1869,9 @@ async function runSecret(argsOrPath, options = {}) {
1763
1869
  throw new Error(`Missing CNOS secret path: ${tail[0] ?? "app.token"}`);
1764
1870
  }
1765
1871
  if (isSecretRef(value)) {
1872
+ const vault = value.vault ?? "default";
1766
1873
  throw new Error(
1767
- `Secret ${tail[0] ?? "app.token"} is stored as an unresolved ${value.provider} reference. Provide the required provider context to resolve it.`
1874
+ `Secret ${tail[0] ?? "app.token"} is stored in vault "${vault}" as ref "${value.ref}". Provide the correct vault passphrase to resolve it.`
1768
1875
  );
1769
1876
  }
1770
1877
  if (options.json) {
@@ -1778,8 +1885,17 @@ async function runSecret(argsOrPath, options = {}) {
1778
1885
 
1779
1886
  // src/commands/use.ts
1780
1887
  import path9 from "path";
1781
- async function runUse(options = {}) {
1888
+ async function runUse(args = [], options = {}) {
1782
1889
  const root = path9.resolve(options.root ?? process.cwd());
1890
+ const action = args[0] ?? "show";
1891
+ const hasUpdates = Boolean(options.workspace || options.profile || options.globalRoot);
1892
+ if (action === "show" || !hasUpdates) {
1893
+ const context = await loadCliContext(root);
1894
+ if (options.json) {
1895
+ return printJson(context);
1896
+ }
1897
+ return Object.keys(context).length === 0 ? "no CLI context configured" : printJson(context);
1898
+ }
1783
1899
  const result = await saveCliContext({
1784
1900
  root,
1785
1901
  ...options.workspace ? { workspace: options.workspace } : {},
@@ -1807,7 +1923,7 @@ async function runValidate(options = {}) {
1807
1923
  // package.json
1808
1924
  var package_default = {
1809
1925
  name: "@kitsy/cnos-cli",
1810
- version: "1.0.1",
1926
+ version: "1.1.2",
1811
1927
  description: "CLI entry point and developer tooling for CNOS.",
1812
1928
  type: "module",
1813
1929
  main: "./dist/index.js",
@@ -1850,6 +1966,7 @@ var package_default = {
1850
1966
  clean: "rimraf dist",
1851
1967
  dev: "tsup src/index.ts --watch --format esm --dts",
1852
1968
  lint: "eslint src test",
1969
+ prepack: "pnpm build",
1853
1970
  test: "vitest run",
1854
1971
  typecheck: "tsc -p tsconfig.json --noEmit"
1855
1972
  }
@@ -1952,6 +2069,9 @@ function resolveHelpTopic(command, args) {
1952
2069
  return normalizeHelpTopic([command, args[0]]);
1953
2070
  }
1954
2071
  if (command === "secret" && args[0] && ["set", "create", "add", "list", "delete", "remove"].includes(args[0])) {
2072
+ if ((args[0] === "create" || args[0] === "add") && args[1] === "vault") {
2073
+ return normalizeHelpTopic(["secret", "create", "vault"]);
2074
+ }
1955
2075
  return normalizeHelpTopic([
1956
2076
  command,
1957
2077
  args[0] === "remove" ? "delete" : args[0] === "create" || args[0] === "add" ? "set" : args[0]
@@ -1991,6 +2111,9 @@ async function main(argv) {
1991
2111
  ...options.json ? {
1992
2112
  json: true
1993
2113
  } : {},
2114
+ ...options.verbose ? {
2115
+ verbose: true
2116
+ } : {},
1994
2117
  ...options.cliArgs.length > 0 ? {
1995
2118
  cliArgs: options.cliArgs
1996
2119
  } : {}
@@ -2029,7 +2152,7 @@ async function main(argv) {
2029
2152
  `);
2030
2153
  return;
2031
2154
  case "use":
2032
- process.stdout.write(`${await runUse(runtimeOptions)}
2155
+ process.stdout.write(`${await runUse(args, runtimeOptions)}
2033
2156
  `);
2034
2157
  return;
2035
2158
  case "profile":
@@ -2084,7 +2207,18 @@ async function main(argv) {
2084
2207
  process.exitCode = 1;
2085
2208
  }
2086
2209
  }
2087
- void main(process.argv.slice(2));
2210
+ void main(process.argv.slice(2)).catch((error) => {
2211
+ const message = error instanceof Error ? error.message : String(error);
2212
+ const verbose = process.argv.includes("--verbose");
2213
+ if (verbose && error instanceof Error && error.stack) {
2214
+ process.stderr.write(`${error.stack}
2215
+ `);
2216
+ } else {
2217
+ process.stderr.write(`${message}
2218
+ `);
2219
+ }
2220
+ process.exitCode = 1;
2221
+ });
2088
2222
  export {
2089
2223
  main
2090
2224
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitsy/cnos-cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "CLI entry point and developer tooling for CNOS.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@kitsy/cnos": "1.1.0"
39
+ "@kitsy/cnos": "1.1.2"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsup src/index.ts --format esm --dts",