tenjin-cli 0.1.0-alpha.0 → 0.1.0-alpha.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 (51) hide show
  1. package/README.md +39 -7
  2. package/dist/{ccip-FXWZDDF5.js → ccip-UCLT5VOW.js} +4 -4
  3. package/dist/{chunk-F5DZJLU3.js → chunk-BRJHUQSY.js} +770 -59
  4. package/dist/chunk-BRJHUQSY.js.map +1 -0
  5. package/dist/{chunk-CPUZRZ2A.js → chunk-CUWUHTIL.js} +42 -1
  6. package/dist/{chunk-CPUZRZ2A.js.map → chunk-CUWUHTIL.js.map} +1 -1
  7. package/dist/{chunk-376LHMK2.js → chunk-DXPYFVKG.js} +32 -6
  8. package/dist/chunk-DXPYFVKG.js.map +1 -0
  9. package/dist/chunk-EXBMF5X7.js +1198 -0
  10. package/dist/chunk-EXBMF5X7.js.map +1 -0
  11. package/dist/{chunk-LTTRND4A.js → chunk-EZROUI5U.js} +2 -2
  12. package/dist/{chunk-PAAL45FJ.js → chunk-IPOWB3G3.js} +123 -133
  13. package/dist/chunk-IPOWB3G3.js.map +1 -0
  14. package/dist/{chunk-WOHCAMDQ.js → chunk-LPRVHM7P.js} +14 -697
  15. package/dist/chunk-LPRVHM7P.js.map +1 -0
  16. package/dist/{chunk-TX5T3LKJ.js → chunk-Q53IYV2G.js} +3 -2
  17. package/dist/{chunk-SXQQ2ZSR.js → chunk-T4LKNY7T.js} +24 -11
  18. package/dist/chunk-T4LKNY7T.js.map +1 -0
  19. package/dist/chunk-YBMMV4Y4.js +51 -0
  20. package/dist/chunk-YBMMV4Y4.js.map +1 -0
  21. package/dist/{chunk-FIYZLITH.js → chunk-YG2DO46P.js} +6 -2
  22. package/dist/{chunk-FIYZLITH.js.map → chunk-YG2DO46P.js.map} +1 -1
  23. package/dist/{cli-LMHGCZ5S.js → cli-WA65NP4Y.js} +13 -17
  24. package/dist/{cli-LMHGCZ5S.js.map → cli-WA65NP4Y.js.map} +1 -1
  25. package/dist/{config-XVXZ3V2Q.js → config-CEFTL5ZL.js} +9 -51
  26. package/dist/config-CEFTL5ZL.js.map +1 -0
  27. package/dist/{doctor-MVLZRVPU.js → doctor-3QBYN46N.js} +7 -7
  28. package/dist/index.js +1 -1
  29. package/dist/{secp256k1-35YNNB6F.js → secp256k1-BEZS642W.js} +3 -3
  30. package/dist/{usdc-OQUOFF6R.js → usdc-44R6XZF2.js} +7 -7
  31. package/dist/{wallet-QUWK2PA4.js → wallet-ELBTBMXO.js} +8 -10
  32. package/dist/wallet-XD576HF3.js +173 -0
  33. package/dist/wallet-XD576HF3.js.map +1 -0
  34. package/package.json +3 -1
  35. package/dist/chunk-376LHMK2.js.map +0 -1
  36. package/dist/chunk-F5DZJLU3.js.map +0 -1
  37. package/dist/chunk-PAAL45FJ.js.map +0 -1
  38. package/dist/chunk-QHIEYBLV.js +0 -320
  39. package/dist/chunk-QHIEYBLV.js.map +0 -1
  40. package/dist/chunk-SXQQ2ZSR.js.map +0 -1
  41. package/dist/chunk-WOHCAMDQ.js.map +0 -1
  42. package/dist/config-XVXZ3V2Q.js.map +0 -1
  43. package/dist/wallet-NRKWLKHX.js +0 -153
  44. package/dist/wallet-NRKWLKHX.js.map +0 -1
  45. /package/dist/{ccip-FXWZDDF5.js.map → ccip-UCLT5VOW.js.map} +0 -0
  46. /package/dist/{chunk-LTTRND4A.js.map → chunk-EZROUI5U.js.map} +0 -0
  47. /package/dist/{chunk-TX5T3LKJ.js.map → chunk-Q53IYV2G.js.map} +0 -0
  48. /package/dist/{doctor-MVLZRVPU.js.map → doctor-3QBYN46N.js.map} +0 -0
  49. /package/dist/{secp256k1-35YNNB6F.js.map → secp256k1-BEZS642W.js.map} +0 -0
  50. /package/dist/{usdc-OQUOFF6R.js.map → usdc-44R6XZF2.js.map} +0 -0
  51. /package/dist/{wallet-QUWK2PA4.js.map → wallet-ELBTBMXO.js.map} +0 -0
@@ -1,4 +1,8 @@
1
1
  import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
2
+ import {
3
+ LockTimeoutError,
4
+ withFileLock
5
+ } from "./chunk-YBMMV4Y4.js";
2
6
  import {
3
7
  CONFIG_KEYS,
4
8
  RawConfigSchema,
@@ -6,65 +10,19 @@ import {
6
10
  parseUsdToAtomic,
7
11
  resolveSettings,
8
12
  toMoney
9
- } from "./chunk-LTTRND4A.js";
13
+ } from "./chunk-EZROUI5U.js";
10
14
  import {
11
15
  writeFileAtomic
12
16
  } from "./chunk-ZPZFPVDN.js";
13
17
  import {
14
18
  CliError,
15
19
  configPath
16
- } from "./chunk-FIYZLITH.js";
20
+ } from "./chunk-YG2DO46P.js";
17
21
  import "./chunk-3IMLRUZE.js";
18
22
 
19
23
  // src/commands/config.ts
20
- import { mkdir as mkdir2 } from "fs/promises";
24
+ import { mkdir } from "fs/promises";
21
25
  import { styleText } from "util";
22
-
23
- // src/lib/lock.ts
24
- import { mkdir, rm, writeFile } from "fs/promises";
25
- import { join } from "path";
26
- var DEFAULTS = { timeoutMs: 5e3, retryMs: 25 };
27
- var LockTimeoutError = class extends Error {
28
- lockPath;
29
- constructor(lockPath, timeoutMs) {
30
- super(
31
- `Timed out waiting for lock at ${lockPath} after ${timeoutMs}ms; if no other tenjin process is running, remove that directory and retry.`
32
- );
33
- this.name = "LockTimeoutError";
34
- this.lockPath = lockPath;
35
- }
36
- };
37
- async function withFileLock(lockPath, fn, opts = {}) {
38
- const { timeoutMs, retryMs } = { ...DEFAULTS, ...opts };
39
- const metaPath = join(lockPath, "meta");
40
- const deadline = Date.now() + timeoutMs;
41
- for (; ; ) {
42
- try {
43
- await mkdir(lockPath);
44
- break;
45
- } catch (err) {
46
- if (!isEexist(err)) throw err;
47
- if (Date.now() >= deadline) throw new LockTimeoutError(lockPath, timeoutMs);
48
- await delay(retryMs);
49
- }
50
- }
51
- await writeFile(metaPath, JSON.stringify({ pid: process.pid, acquiredAt: Date.now() })).catch(
52
- () => void 0
53
- );
54
- try {
55
- return await fn();
56
- } finally {
57
- await rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
58
- }
59
- }
60
- function isEexist(err) {
61
- return typeof err === "object" && err !== null && err.code === "EEXIST";
62
- }
63
- function delay(ms) {
64
- return new Promise((resolve) => setTimeout(resolve, ms));
65
- }
66
-
67
- // src/commands/config.ts
68
26
  var CONFIRM_ABOVE = "above:";
69
27
  var KEY_WIDTH = Math.max(...CONFIG_KEYS.map((key) => key.length));
70
28
  async function runConfigList(ctx) {
@@ -164,7 +122,7 @@ function parseHttpUrl(value) {
164
122
  return value;
165
123
  }
166
124
  async function persist(dir, key, stored) {
167
- await mkdir2(dir, { recursive: true, mode: 448 });
125
+ await mkdir(dir, { recursive: true, mode: 448 });
168
126
  const lockPath = `${configPath(dir)}.lock`;
169
127
  try {
170
128
  await withFileLock(lockPath, async () => {
@@ -203,4 +161,4 @@ export {
203
161
  runConfigList,
204
162
  runConfigSet
205
163
  };
206
- //# sourceMappingURL=config-XVXZ3V2Q.js.map
164
+ //# sourceMappingURL=config-CEFTL5ZL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { styleText } from 'node:util';\nimport { CliError } from '../lib/errors';\nimport { CONFIG_KEYS, RawConfigSchema, loadRawConfig, resolveSettings } from '../lib/config';\nimport type { Config, EffectiveSettings, Provenance } from '../lib/config';\nimport { configPath } from '../lib/paths';\nimport { writeFileAtomic } from '../lib/atomic-json';\nimport { withFileLock, LockTimeoutError } from '../lib/lock';\nimport { parseUsdToAtomic, toMoney } from '../lib/money';\nimport type { Money } from '../schemas';\nimport type { CommandContext, CommandResult } from '../context';\n\n/**\n * How one config key is presented in `data`. `value` is the machine form: dual\n * Money for the spend keys, the stored string for `confirm`/URLs, the string[]\n * for the allowlist. `threshold` rides along only for `confirm: above:<atomic>`\n * so an agent reads the dollar amount without re-parsing the string.\n */\ninterface RenderedValue {\n value: Money | string | string[];\n threshold?: Money;\n}\ninterface RenderedSetting extends RenderedValue {\n source: Provenance;\n}\n\nconst CONFIRM_ABOVE = 'above:';\nconst KEY_WIDTH = Math.max(...CONFIG_KEYS.map((key) => key.length));\n\n/**\n * Show every effective key with its value and provenance. `data` is keyed by\n * config key; provenance comes from resolveSettings over the *raw* file (not the\n * defaults-merged config), so a key the user never set reads `default`, not\n * `file`.\n */\nexport async function runConfigList(ctx: CommandContext): Promise<CommandResult> {\n const settings = await resolveFromContext(ctx);\n const data: Record<string, RenderedSetting> = {};\n const humanLines: string[] = [];\n for (const key of CONFIG_KEYS) {\n const entry = renderSetting(key, settings[key].value, settings[key].source);\n data[key] = entry;\n humanLines.push(formatLine(key, entry));\n }\n return { data, humanLines };\n}\n\n/** Same per-key shape as list, for one key. Unknown key is a USAGE failure. */\nexport async function runConfigGet(\n { key }: { key: string },\n ctx: CommandContext,\n): Promise<CommandResult> {\n const configKey = assertKey(key);\n const settings = await resolveFromContext(ctx);\n const entry = renderSetting(configKey, settings[configKey].value, settings[configKey].source);\n return { data: { key: configKey, ...entry }, humanLines: [formatLine(configKey, entry)] };\n}\n\n/**\n * Parse the value for this key, then persist it merged into the existing raw\n * file — never materializing defaults for keys the user did not set, so\n * provenance stays truthful. The written key now reads `file`.\n */\nexport async function runConfigSet(\n { key, value }: { key: string; value: string },\n ctx: CommandContext,\n): Promise<CommandResult> {\n const configKey = assertKey(key);\n const stored = parseValue(configKey, value);\n await persist(ctx.dataDir, configKey, stored);\n const entry = renderSetting(configKey, stored, 'file');\n return { data: { key: configKey, ...entry }, humanLines: [formatLine(configKey, entry)] };\n}\n\nasync function resolveFromContext(ctx: CommandContext): Promise<EffectiveSettings> {\n const config = await loadRawConfig(ctx.dataDir);\n return resolveSettings({ config, flags: { baseUrl: ctx.flags.baseUrl }, env: process.env });\n}\n\nfunction assertKey(key: string): keyof Config {\n if ((CONFIG_KEYS as string[]).includes(key)) return key as keyof Config;\n throw new CliError('USAGE', `Unknown config key: ${JSON.stringify(key)}`, {\n fix: `Valid keys: ${CONFIG_KEYS.join(', ')}.`,\n });\n}\n\nfunction renderSetting(\n key: keyof Config,\n stored: string | string[],\n source: Provenance,\n): RenderedSetting {\n return { ...renderValue(key, stored), source };\n}\n\nfunction renderValue(key: keyof Config, stored: string | string[]): RenderedValue {\n if (Array.isArray(stored)) return { value: stored }; // allowlistCreators\n if (key === 'maxAutoSpend' || key === 'sessionBudget') return { value: toMoney(stored) };\n if (key === 'confirm' && stored.startsWith(CONFIRM_ABOVE)) {\n return { value: stored, threshold: toMoney(stored.slice(CONFIRM_ABOVE.length)) };\n }\n return { value: stored };\n}\n\n/** Per-key edge parsing. Returns the persisted form; throws USAGE on bad input. */\nfunction parseValue(key: keyof Config, value: string): string | string[] {\n switch (key) {\n case 'maxAutoSpend':\n case 'sessionBudget':\n return parseUsdToAtomic(value); // throws USAGE on a bad amount\n case 'confirm':\n return parseConfirm(value);\n case 'allowlistCreators':\n return parseAllowlist(value);\n case 'baseUrl':\n case 'rpcUrl':\n return parseHttpUrl(value);\n }\n}\n\nfunction parseConfirm(value: string): string {\n if (value === 'always') return 'always';\n if (value.startsWith(CONFIRM_ABOVE)) {\n return `${CONFIRM_ABOVE}${parseUsdToAtomic(value.slice(CONFIRM_ABOVE.length))}`;\n }\n throw new CliError('USAGE', `Invalid confirm value: ${JSON.stringify(value)}`, {\n fix: 'Use \"always\" or \"above:<usd>\", e.g. above:0.25.',\n });\n}\n\nfunction parseAllowlist(value: string): string[] {\n // \"\" clears to []; comma-split, trim, drop empties. Reject an entry with\n // internal whitespace — a creator handle/address is a single token.\n const entries = value\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n for (const entry of entries) {\n if (/\\s/.test(entry)) {\n throw new CliError('USAGE', `Invalid creator entry: ${JSON.stringify(entry)}`, {\n fix: 'Creator entries cannot contain spaces; separate multiple with commas.',\n });\n }\n }\n return entries;\n}\n\nfunction parseHttpUrl(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new CliError('USAGE', `Invalid URL: ${JSON.stringify(value)}`, {\n fix: 'Pass an absolute http(s) URL like https://tenjin.blog.',\n });\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new CliError('USAGE', `URL must be http or https: ${JSON.stringify(value)}`, {\n fix: 'Pass an absolute http(s) URL like https://tenjin.blog.',\n });\n }\n return value;\n}\n\n/**\n * Merge one key into the raw file and write it back atomically. Uses the partial\n * schema and writeFileAtomic (not writeConfig, which would materialize every\n * default into the file and corrupt provenance). loadRawConfig surfaces a\n * corrupt existing file as CONFIG_INVALID, which propagates.\n *\n * The whole read-merge-write runs under a cross-process file lock: without it two\n * concurrent `config set` on different keys race on the read, and the last writer\n * drops the other's update (worst case, a zeroed maxAutoSpend resurrected). The\n * data dir is ensured first so the lock's mkdir has a parent on a fresh install.\n */\nasync function persist(dir: string, key: keyof Config, stored: string | string[]): Promise<void> {\n await mkdir(dir, { recursive: true, mode: 0o700 });\n const lockPath = `${configPath(dir)}.lock`;\n try {\n await withFileLock(lockPath, async () => {\n const existing = await loadRawConfig(dir);\n const merged = { ...existing, [key]: stored };\n const validated = RawConfigSchema.parse(merged);\n await writeFileAtomic(configPath(dir), `${JSON.stringify(validated, null, 2)}\\n`, {\n mode: 0o644,\n dirMode: 0o700,\n });\n });\n } catch (err) {\n // A lock timeout is not the user's malformed input; surface it as INTERNAL with\n // the one manual step (there is no auto-steal), keeping the JSON error contract.\n if (err instanceof LockTimeoutError) {\n throw new CliError('INTERNAL', err.message, {\n fix: `If no other tenjin process is running, remove ${lockPath} and retry.`,\n cause: err,\n });\n }\n throw err;\n }\n}\n\nfunction formatLine(key: string, entry: RenderedSetting): string {\n const label = key.padEnd(KEY_WIDTH);\n return ` ${label} ${displayValue(entry)} ${styleText('dim', `(${entry.source})`)}`;\n}\n\nfunction displayValue(entry: RenderedSetting): string {\n const { value } = entry;\n if (Array.isArray(value)) return value.length > 0 ? value.join(', ') : '(empty)';\n if (typeof value === 'object') return `${value.usd} USD`; // Money (spend keys)\n if (entry.threshold !== undefined) return `above ${entry.threshold.usd} USD`; // confirm\n return value; // 'always', baseUrl, rpcUrl\n}\n"],"mappings":"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","names":[]}
@@ -3,15 +3,15 @@ import {
3
3
  loadRawConfig,
4
4
  resolveSettings,
5
5
  toMoney
6
- } from "./chunk-LTTRND4A.js";
6
+ } from "./chunk-EZROUI5U.js";
7
7
  import {
8
8
  walletFileExists
9
- } from "./chunk-376LHMK2.js";
9
+ } from "./chunk-DXPYFVKG.js";
10
10
  import "./chunk-ZPZFPVDN.js";
11
11
  import {
12
12
  CliError,
13
13
  configPath
14
- } from "./chunk-FIYZLITH.js";
14
+ } from "./chunk-YG2DO46P.js";
15
15
  import "./chunk-3IMLRUZE.js";
16
16
 
17
17
  // src/commands/doctor.ts
@@ -227,7 +227,7 @@ async function checkReadPath(baseUrl, timeoutMs, fetchImpl) {
227
227
  async function checkWallet(ctx, deps, env, rpcUrl) {
228
228
  const provider = deps.provider ?? await resolveLocalProviderOrNull(ctx, env);
229
229
  if (provider === null) return [noWalletCheck()];
230
- const { describeWallet } = await import("./wallet-QUWK2PA4.js");
230
+ const { describeWallet } = await import("./wallet-ELBTBMXO.js");
231
231
  let desc;
232
232
  try {
233
233
  desc = await describeWallet(provider);
@@ -253,7 +253,7 @@ async function resolveLocalProviderOrNull(ctx, env) {
253
253
  const envKey = env.TENJIN_WALLET_KEY;
254
254
  const envKeySet = typeof envKey === "string" && envKey.length > 0;
255
255
  if (!envKeySet && !await walletFileExists(ctx.dataDir)) return null;
256
- const { resolveWalletProvider } = await import("./wallet-QUWK2PA4.js");
256
+ const { resolveWalletProvider } = await import("./wallet-ELBTBMXO.js");
257
257
  return resolveWalletProvider(ctx);
258
258
  }
259
259
  function noWalletCheck() {
@@ -276,7 +276,7 @@ function walletWarn(err) {
276
276
  }
277
277
  async function checkBalance(address, rpcUrl) {
278
278
  try {
279
- const { getUsdcBalance } = await import("./usdc-OQUOFF6R.js");
279
+ const { getUsdcBalance } = await import("./usdc-44R6XZF2.js");
280
280
  const balance = await getUsdcBalance(address, rpcUrl);
281
281
  if (balance === 0n) {
282
282
  return {
@@ -335,4 +335,4 @@ function trimSlash(url) {
335
335
  export {
336
336
  runDoctor
337
337
  };
338
- //# sourceMappingURL=doctor-MVLZRVPU.js.map
338
+ //# sourceMappingURL=doctor-3QBYN46N.js.map
package/dist/index.js CHANGED
@@ -21,6 +21,6 @@ if (Number.isNaN(nodeMajor) || nodeMajor < MIN_NODE_MAJOR) {
21
21
  `);
22
22
  process.exit(1);
23
23
  }
24
- var { main } = await import("./cli-LMHGCZ5S.js");
24
+ var { main } = await import("./cli-WA65NP4Y.js");
25
25
  process.exit(await main(process.argv.slice(2)));
26
26
  //# sourceMappingURL=index.js.map
@@ -5,8 +5,8 @@ import {
5
5
  schnorr,
6
6
  secp256k1,
7
7
  secp256k1_hasher
8
- } from "./chunk-TX5T3LKJ.js";
9
- import "./chunk-CPUZRZ2A.js";
8
+ } from "./chunk-Q53IYV2G.js";
9
+ import "./chunk-CUWUHTIL.js";
10
10
  import "./chunk-3IMLRUZE.js";
11
11
  export {
12
12
  encodeToCurve,
@@ -15,4 +15,4 @@ export {
15
15
  secp256k1,
16
16
  secp256k1_hasher
17
17
  };
18
- //# sourceMappingURL=secp256k1-35YNNB6F.js.map
18
+ //# sourceMappingURL=secp256k1-BEZS642W.js.map
@@ -3,16 +3,16 @@ import {
3
3
  USDC_ADDRESS,
4
4
  USDC_DECIMALS,
5
5
  getUsdcBalance
6
- } from "./chunk-PAAL45FJ.js";
7
- import "./chunk-SXQQ2ZSR.js";
8
- import "./chunk-TX5T3LKJ.js";
9
- import "./chunk-WOHCAMDQ.js";
10
- import "./chunk-F5DZJLU3.js";
11
- import "./chunk-CPUZRZ2A.js";
6
+ } from "./chunk-IPOWB3G3.js";
7
+ import "./chunk-T4LKNY7T.js";
8
+ import "./chunk-Q53IYV2G.js";
9
+ import "./chunk-LPRVHM7P.js";
10
+ import "./chunk-BRJHUQSY.js";
11
+ import "./chunk-CUWUHTIL.js";
12
12
  import "./chunk-3IMLRUZE.js";
13
13
  export {
14
14
  USDC_ADDRESS,
15
15
  USDC_DECIMALS,
16
16
  getUsdcBalance
17
17
  };
18
- //# sourceMappingURL=usdc-OQUOFF6R.js.map
18
+ //# sourceMappingURL=usdc-44R6XZF2.js.map
@@ -2,21 +2,19 @@ import { createRequire as __tenjinCreateRequire } from 'node:module'; const requ
2
2
  import {
3
3
  createLocalWallet,
4
4
  describeWallet,
5
- importLocalWallet,
6
5
  resolveWalletProvider
7
- } from "./chunk-QHIEYBLV.js";
8
- import "./chunk-SXQQ2ZSR.js";
9
- import "./chunk-TX5T3LKJ.js";
10
- import "./chunk-F5DZJLU3.js";
11
- import "./chunk-CPUZRZ2A.js";
12
- import "./chunk-376LHMK2.js";
6
+ } from "./chunk-EXBMF5X7.js";
7
+ import "./chunk-T4LKNY7T.js";
8
+ import "./chunk-Q53IYV2G.js";
9
+ import "./chunk-BRJHUQSY.js";
10
+ import "./chunk-CUWUHTIL.js";
11
+ import "./chunk-DXPYFVKG.js";
13
12
  import "./chunk-ZPZFPVDN.js";
14
- import "./chunk-FIYZLITH.js";
13
+ import "./chunk-YG2DO46P.js";
15
14
  import "./chunk-3IMLRUZE.js";
16
15
  export {
17
16
  createLocalWallet,
18
17
  describeWallet,
19
- importLocalWallet,
20
18
  resolveWalletProvider
21
19
  };
22
- //# sourceMappingURL=wallet-QUWK2PA4.js.map
20
+ //# sourceMappingURL=wallet-ELBTBMXO.js.map
@@ -0,0 +1,173 @@
1
+ import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
2
+ import {
3
+ LockTimeoutError,
4
+ withFileLock
5
+ } from "./chunk-YBMMV4Y4.js";
6
+ import {
7
+ createLocalWallet,
8
+ describeWallet,
9
+ resolvePassphraseForCreate,
10
+ resolveWalletProvider
11
+ } from "./chunk-EXBMF5X7.js";
12
+ import {
13
+ getUsdcBalance
14
+ } from "./chunk-IPOWB3G3.js";
15
+ import "./chunk-T4LKNY7T.js";
16
+ import "./chunk-Q53IYV2G.js";
17
+ import "./chunk-LPRVHM7P.js";
18
+ import "./chunk-BRJHUQSY.js";
19
+ import "./chunk-CUWUHTIL.js";
20
+ import {
21
+ loadRawConfig,
22
+ resolveSettings,
23
+ toMoney
24
+ } from "./chunk-EZROUI5U.js";
25
+ import {
26
+ walletFileExists
27
+ } from "./chunk-DXPYFVKG.js";
28
+ import "./chunk-ZPZFPVDN.js";
29
+ import {
30
+ CliError,
31
+ walletPath
32
+ } from "./chunk-YG2DO46P.js";
33
+ import "./chunk-3IMLRUZE.js";
34
+
35
+ // src/commands/wallet.ts
36
+ import { mkdir } from "fs/promises";
37
+ import { join } from "path";
38
+ var FUNDING_LINE = "Send USDC on Base. $5 covers ~50 typical resources.";
39
+ var KEY_STORAGE = "encrypted (keystore v3, scrypt)";
40
+ var isWindows = process.platform === "win32";
41
+ async function runWalletCreate(ctx, opts = {}) {
42
+ const { address, walletPath: path, source } = await createWalletLocked(ctx.dataDir, opts);
43
+ const warnings = custodyWarnings();
44
+ return {
45
+ data: {
46
+ address,
47
+ walletPath: path,
48
+ provider: "local",
49
+ policyEnforcement: "client-only",
50
+ keyStorage: KEY_STORAGE,
51
+ passphraseSource: source,
52
+ warnings
53
+ },
54
+ humanLines: [
55
+ `Wallet created: ${address}`,
56
+ `Key stored ${KEY_STORAGE}.`,
57
+ passphraseNote(source),
58
+ FUNDING_LINE,
59
+ ...warnings
60
+ ]
61
+ };
62
+ }
63
+ async function createWalletLocked(dir, opts) {
64
+ await mkdir(dir, { recursive: true, mode: 448 });
65
+ const lockPath = join(dir, "wallet.create.lock");
66
+ try {
67
+ return await withFileLock(lockPath, async () => {
68
+ await refuseIfWalletExists(dir);
69
+ const { passphrase, source } = await resolvePassphraseForCreate({
70
+ env: process.env,
71
+ dir,
72
+ ...opts.passphrase
73
+ });
74
+ const { address, walletPath: path } = await createLocalWallet(dir, passphrase);
75
+ return { address, walletPath: path, source };
76
+ });
77
+ } catch (err) {
78
+ if (err instanceof LockTimeoutError) {
79
+ throw new CliError("INTERNAL", err.message, {
80
+ fix: `If no other tenjin process is running, remove ${lockPath} and retry.`,
81
+ cause: err
82
+ });
83
+ }
84
+ throw err;
85
+ }
86
+ }
87
+ function passphraseNote(source) {
88
+ switch (source) {
89
+ case "keychain":
90
+ return "Passphrase saved to your macOS keychain (service tenjin-cli); signing will be transparent on this machine.";
91
+ case "dpapi":
92
+ return "Passphrase saved to a DPAPI-encrypted file, readable only by you on this Windows machine; signing will be transparent here.";
93
+ case "secret-service":
94
+ return "Passphrase saved to your Linux keyring (Secret Service, service tenjin-cli); signing will be transparent on this machine.";
95
+ case "env":
96
+ return "Encrypted with TENJIN_WALLET_PASSPHRASE; keep that value to sign.";
97
+ case "prompt":
98
+ return "Remember your passphrase: it is required to sign and cannot be recovered.";
99
+ }
100
+ }
101
+ async function runWalletShow(ctx, opts = {}) {
102
+ const provider = resolveWalletProvider(ctx, opts);
103
+ const desc = await describeWallet(provider);
104
+ const { walletPath: path, keyStorage, passphraseSource, warnings } = await provider.diagnostics();
105
+ const humanLines = [`Address: ${desc.address}`, `Key source: ${desc.credentialSource}`];
106
+ if (keyStorage !== void 0) humanLines.push(`Key storage: ${keyStorage}`);
107
+ if (passphraseSource !== void 0) humanLines.push(`Passphrase: ${passphraseSource}`);
108
+ humanLines.push(...warnings);
109
+ return {
110
+ data: {
111
+ address: desc.address,
112
+ provider: desc.provider,
113
+ credentialSource: desc.credentialSource,
114
+ policyEnforcement: desc.policyEnforcement,
115
+ ...path !== void 0 ? { walletPath: path } : {},
116
+ ...keyStorage !== void 0 ? { keyStorage } : {},
117
+ ...passphraseSource !== void 0 ? { passphraseSource } : {},
118
+ warnings
119
+ },
120
+ humanLines
121
+ };
122
+ }
123
+ async function runWalletBalance(ctx, opts = {}) {
124
+ const provider = resolveWalletProvider(ctx, opts);
125
+ const desc = await describeWallet(provider);
126
+ const config = await loadRawConfig(ctx.dataDir);
127
+ const rpcUrl = resolveSettings({
128
+ config,
129
+ flags: { baseUrl: ctx.flags.baseUrl },
130
+ env: process.env
131
+ }).rpcUrl.value;
132
+ let atomic;
133
+ try {
134
+ atomic = await getUsdcBalance(desc.address, rpcUrl);
135
+ } catch (err) {
136
+ throw new CliError("RPC_ERROR", `Could not read the USDC balance from ${rpcUrl}.`, {
137
+ fix: "Check your network, or set a working RPC with `tenjin config set rpcUrl <url>`.",
138
+ cause: err
139
+ });
140
+ }
141
+ const balance = toMoney(atomic.toString());
142
+ return {
143
+ data: { address: desc.address, balance },
144
+ humanLines: [`Balance: ${balance.usd} USDC on Base`]
145
+ };
146
+ }
147
+ async function refuseIfWalletExists(dir) {
148
+ if (!await walletFileExists(dir)) return;
149
+ const path = walletPath(dir);
150
+ throw new CliError("WALLET_EXISTS", `A wallet already exists at ${path}.`, {
151
+ fix: `Keys are non-recoverable; move it aside first (e.g. \`mv ${path} ${path}.bak\`) to create a new one.`
152
+ });
153
+ }
154
+ function custodyWarnings() {
155
+ const warnings = [];
156
+ if (hasEnvKey()) {
157
+ warnings.push("TENJIN_WALLET_KEY is set and shadows this file at runtime.");
158
+ }
159
+ if (isWindows) {
160
+ warnings.push("File permissions are not enforced on Windows; the key file is not restricted.");
161
+ }
162
+ return warnings;
163
+ }
164
+ function hasEnvKey() {
165
+ const envKey = process.env.TENJIN_WALLET_KEY;
166
+ return envKey !== void 0 && envKey.length > 0;
167
+ }
168
+ export {
169
+ runWalletBalance,
170
+ runWalletCreate,
171
+ runWalletShow
172
+ };
173
+ //# sourceMappingURL=wallet-XD576HF3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/wallet.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { CliError } from '../lib/errors';\nimport { walletPath } from '../lib/paths';\nimport { withFileLock, LockTimeoutError } from '../lib/lock';\nimport { loadRawConfig, resolveSettings } from '../lib/config';\nimport { toMoney } from '../lib/money';\nimport { getUsdcBalance } from '../lib/usdc';\nimport {\n createLocalWallet,\n describeWallet,\n resolveWalletProvider,\n type ResolveWalletProviderOptions,\n} from '../lib/wallet';\nimport { walletFileExists } from '../lib/wallet/store';\nimport { resolvePassphraseForCreate, type PassphraseSource } from '../lib/wallet/passphrase';\nimport type { PassphraseOverrides } from '../lib/wallet/local';\nimport type { CommandContext, CommandResult } from '../context';\n\nconst FUNDING_LINE = 'Send USDC on Base. $5 covers ~50 typical resources.';\nconst KEY_STORAGE = 'encrypted (keystore v3, scrypt)';\nconst isWindows = process.platform === 'win32';\n\nexport interface WalletCreateOptions {\n /** Test seam for passphrase resolution (keychain exec, TTY prompt, platform). */\n passphrase?: PassphraseOverrides;\n}\n\nexport async function runWalletCreate(\n ctx: CommandContext,\n opts: WalletCreateOptions = {},\n): Promise<CommandResult> {\n const { address, walletPath: path, source } = await createWalletLocked(ctx.dataDir, opts);\n\n const warnings = custodyWarnings();\n return {\n data: {\n address,\n walletPath: path,\n provider: 'local',\n policyEnforcement: 'client-only',\n keyStorage: KEY_STORAGE,\n passphraseSource: source,\n warnings,\n },\n humanLines: [\n `Wallet created: ${address}`,\n `Key stored ${KEY_STORAGE}.`,\n passphraseNote(source),\n FUNDING_LINE,\n ...warnings,\n ],\n };\n}\n\n/**\n * Run the whole create critical section under a cross-process file lock: the\n * existence pre-check, the passphrase generate-and-store, and the no-clobber\n * write. Without the lock these interleave across two concurrent creates — each\n * generates a distinct passphrase and persists it to the OS store (which is\n * last-writer-wins on every platform) BEFORE the exclusive file write, so a\n * different process can win the file, orphaning the winning wallet from the\n * passphrase the store now holds (which then decrypts nothing, unrecoverably).\n * Serializing means the loser blocks until the winner commits, then trips\n * refuseIfWalletExists and exits WALLET_EXISTS having never generated or stored a\n * passphrase. The store-before-write ordering stays (it still guards the\n * single-process crash window), and the exclusive write stays the final authority.\n */\nasync function createWalletLocked(\n dir: string,\n opts: WalletCreateOptions,\n): Promise<{ address: string; walletPath: string; source: PassphraseSource }> {\n // Ensure the data dir exists so the lock's own mkdir has a parent on a fresh\n // install; the exclusive wallet write below re-ensures it at 0700 regardless.\n await mkdir(dir, { recursive: true, mode: 0o700 });\n const lockPath = join(dir, 'wallet.create.lock');\n try {\n return await withFileLock(lockPath, async () => {\n await refuseIfWalletExists(dir);\n const { passphrase, source } = await resolvePassphraseForCreate({\n env: process.env,\n dir,\n ...opts.passphrase,\n });\n const { address, walletPath: path } = await createLocalWallet(dir, passphrase);\n return { address, walletPath: path, source };\n });\n } catch (err) {\n // A lock timeout is not the user's error; surface it as INTERNAL with the one\n // manual step (there is no auto-steal), keeping the JSON error contract. Every\n // other error — notably the loser's WALLET_EXISTS — propagates unchanged.\n if (err instanceof LockTimeoutError) {\n throw new CliError('INTERNAL', err.message, {\n fix: `If no other tenjin process is running, remove ${lockPath} and retry.`,\n cause: err,\n });\n }\n throw err;\n }\n}\n\n/** Where the encryption passphrase lives, and what the user must remember. */\nfunction passphraseNote(source: PassphraseSource): string {\n switch (source) {\n case 'keychain':\n return 'Passphrase saved to your macOS keychain (service tenjin-cli); signing will be transparent on this machine.';\n case 'dpapi':\n return 'Passphrase saved to a DPAPI-encrypted file, readable only by you on this Windows machine; signing will be transparent here.';\n case 'secret-service':\n return 'Passphrase saved to your Linux keyring (Secret Service, service tenjin-cli); signing will be transparent on this machine.';\n case 'env':\n return 'Encrypted with TENJIN_WALLET_PASSPHRASE; keep that value to sign.';\n case 'prompt':\n return 'Remember your passphrase: it is required to sign and cannot be recovered.';\n }\n}\n\nexport async function runWalletShow(\n ctx: CommandContext,\n opts: ResolveWalletProviderOptions = {},\n): Promise<CommandResult> {\n const provider = resolveWalletProvider(ctx, opts);\n const desc = await describeWallet(provider);\n // Diagnostics are the provider's own: a remote provider reports no local file\n // path or perms warning, so `show` never contaminates a remote wallet's output\n // with a stale local wallet.json's state. keyStorage/passphraseSource are the\n // custody posture reported without decrypting or requiring a passphrase.\n const { walletPath: path, keyStorage, passphraseSource, warnings } = await provider.diagnostics();\n\n const humanLines = [`Address: ${desc.address}`, `Key source: ${desc.credentialSource}`];\n if (keyStorage !== undefined) humanLines.push(`Key storage: ${keyStorage}`);\n if (passphraseSource !== undefined) humanLines.push(`Passphrase: ${passphraseSource}`);\n humanLines.push(...warnings);\n\n return {\n data: {\n address: desc.address,\n provider: desc.provider,\n credentialSource: desc.credentialSource,\n policyEnforcement: desc.policyEnforcement,\n ...(path !== undefined ? { walletPath: path } : {}),\n ...(keyStorage !== undefined ? { keyStorage } : {}),\n ...(passphraseSource !== undefined ? { passphraseSource } : {}),\n warnings,\n },\n humanLines,\n };\n}\n\nexport async function runWalletBalance(\n ctx: CommandContext,\n opts: ResolveWalletProviderOptions = {},\n): Promise<CommandResult> {\n const provider = resolveWalletProvider(ctx, opts);\n const desc = await describeWallet(provider);\n\n const config = await loadRawConfig(ctx.dataDir);\n const rpcUrl = resolveSettings({\n config,\n flags: { baseUrl: ctx.flags.baseUrl },\n env: process.env,\n }).rpcUrl.value;\n\n let atomic: bigint;\n try {\n atomic = await getUsdcBalance(desc.address, rpcUrl);\n } catch (err) {\n throw new CliError('RPC_ERROR', `Could not read the USDC balance from ${rpcUrl}.`, {\n fix: 'Check your network, or set a working RPC with `tenjin config set rpcUrl <url>`.',\n cause: err,\n });\n }\n\n const balance = toMoney(atomic.toString());\n return {\n data: { address: desc.address, balance },\n humanLines: [`Balance: ${balance.usd} USDC on Base`],\n };\n}\n\n/** create refuses to overwrite: keys are non-recoverable and may hold funds. */\nasync function refuseIfWalletExists(dir: string): Promise<void> {\n if (!(await walletFileExists(dir))) return;\n const path = walletPath(dir);\n throw new CliError('WALLET_EXISTS', `A wallet already exists at ${path}.`, {\n fix: `Keys are non-recoverable; move it aside first (e.g. \\`mv ${path} ${path}.bak\\`) to create a new one.`,\n });\n}\n\nfunction custodyWarnings(): string[] {\n const warnings: string[] = [];\n if (hasEnvKey()) {\n warnings.push('TENJIN_WALLET_KEY is set and shadows this file at runtime.');\n }\n if (isWindows) {\n warnings.push('File permissions are not enforced on Windows; the key file is not restricted.');\n }\n return warnings;\n}\n\nfunction hasEnvKey(): boolean {\n const envKey = process.env.TENJIN_WALLET_KEY;\n return envKey !== undefined && envKey.length > 0;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tenjin-cli",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "type": "module",
5
5
  "description": "Tenjin agent CLI for the x402 knowledge marketplace.",
6
6
  "license": "MIT",
@@ -42,6 +42,7 @@
42
42
  "format": "prettier --write .",
43
43
  "format:check": "prettier --check .",
44
44
  "check": "pnpm run build && pnpm run test",
45
+ "sync:skill": "node scripts/sync-skill.mjs",
45
46
  "pack-smoke": "bash scripts/pack-smoke.sh",
46
47
  "prepare": "git config core.hooksPath .githooks || true"
47
48
  },
@@ -52,6 +53,7 @@
52
53
  "commander": "^14.0.1",
53
54
  "eslint": "^9",
54
55
  "eslint-config-prettier": "^10",
56
+ "ox": "0.14.30",
55
57
  "prettier": "^3",
56
58
  "tsup": "^8.5.0",
57
59
  "typescript": "^6",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/wallet/store.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises';\nimport { z } from 'zod';\nimport { CliError } from '../errors';\nimport { writeFileAtomicExclusive } from '../atomic-json';\nimport { walletPath } from '../paths';\n\n/** A 0x-prefixed 32-byte hex private key (case-insensitive). */\nexport const PRIVATE_KEY_RE = /^0x[0-9a-f]{64}$/i;\nconst ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;\n\n/**\n * The persisted wallet record. The `provider` discriminator keeps the schema\n * from implying every future wallet embeds a raw key; `address` is stored EIP-55\n * checksummed so `show`/`doctor` never derive it from the key. Validated on read\n * (a corrupt file is WALLET_INVALID_KEY, never a silent partial parse).\n */\nexport const WalletRecordSchema = z.object({\n schemaVersion: z.literal(1),\n provider: z.literal('local'),\n address: z.string().regex(ADDRESS_RE, 'expected a 0x-prefixed 20-byte address'),\n privateKey: z.string().regex(PRIVATE_KEY_RE, 'expected a 0x-prefixed 32-byte hex key'),\n createdAt: z.string(),\n});\nexport type WalletRecord = z.infer<typeof WalletRecordSchema>;\n\nexport async function walletFileExists(dir: string): Promise<boolean> {\n return (await walletFileMode(dir)) !== null;\n}\n\n/** File permission bits (`mode & 0o777`), or null when the wallet file is absent. */\nexport async function walletFileMode(dir: string): Promise<number | null> {\n try {\n return (await stat(walletPath(dir))).mode & 0o777;\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n}\n\n/** Read + validate the wallet record; null when absent, WALLET_INVALID_KEY when corrupt. */\nexport async function readWalletRecord(dir: string): Promise<WalletRecord | null> {\n const path = walletPath(dir);\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return null;\n throw new CliError('WALLET_INVALID_KEY', `Could not read the wallet file at ${path}.`, {\n fix: `Check file permissions on ${path}.`,\n cause: err,\n });\n }\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch (err) {\n throw new CliError('WALLET_INVALID_KEY', `The wallet file at ${path} is not valid JSON.`, {\n fix: `Move ${path} aside, then run \\`tenjin wallet create\\` or \\`tenjin wallet import\\`.`,\n cause: err,\n });\n }\n const parsed = WalletRecordSchema.safeParse(json);\n if (!parsed.success) {\n throw new CliError(\n 'WALLET_INVALID_KEY',\n `The wallet file at ${path} is not a valid wallet record.`,\n {\n fix: `Move ${path} aside, then run \\`tenjin wallet create\\` or \\`tenjin wallet import\\`.`,\n details: parsed.error.issues,\n },\n );\n }\n return parsed.data;\n}\n\n/**\n * Persist a validated record at 0600 in a 0700 dir, NO-CLOBBER. The exclusive\n * write — not an earlier existence check — is the authority: two concurrent\n * `create`/`import` runs can both pass a pre-check, but only one can win the\n * atomic commit; the loser surfaces as WALLET_EXISTS instead of silently\n * overwriting (and losing) a non-recoverable key.\n */\nexport async function writeWalletRecord(dir: string, record: WalletRecord): Promise<void> {\n const validated = WalletRecordSchema.parse(record);\n const path = walletPath(dir);\n try {\n await writeFileAtomicExclusive(path, `${JSON.stringify(validated, null, 2)}\\n`, {\n mode: 0o600,\n dirMode: 0o700,\n });\n } catch (err) {\n if (hasCode(err, 'EEXIST')) {\n throw new CliError('WALLET_EXISTS', `A wallet already exists at ${path}.`, {\n fix: `Keys are non-recoverable; move it aside first (e.g. \\`mv ${path} ${path}.bak\\`) to create a new one.`,\n cause: err,\n });\n }\n throw err;\n }\n}\n\nfunction isNotFound(err: unknown): boolean {\n return hasCode(err, 'ENOENT');\n}\n\nfunction hasCode(err: unknown, code: string): boolean {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'code' in err &&\n (err as { code?: unknown }).code === code\n );\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,UAAU,YAAY;AAOxB,IAAM,iBAAiB;AAC9B,IAAM,aAAa;AAQZ,IAAM,qBAAqB,iBAAE,OAAO;AAAA,EACzC,eAAe,iBAAE,QAAQ,CAAC;AAAA,EAC1B,UAAU,iBAAE,QAAQ,OAAO;AAAA,EAC3B,SAAS,iBAAE,OAAO,EAAE,MAAM,YAAY,wCAAwC;AAAA,EAC9E,YAAY,iBAAE,OAAO,EAAE,MAAM,gBAAgB,wCAAwC;AAAA,EACrF,WAAW,iBAAE,OAAO;AACtB,CAAC;AAGD,eAAsB,iBAAiB,KAA+B;AACpE,SAAQ,MAAM,eAAe,GAAG,MAAO;AACzC;AAGA,eAAsB,eAAe,KAAqC;AACxE,MAAI;AACF,YAAQ,MAAM,KAAK,WAAW,GAAG,CAAC,GAAG,OAAO;AAAA,EAC9C,SAAS,KAAK;AACZ,QAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,UAAM;AAAA,EACR;AACF;AAGA,eAAsB,iBAAiB,KAA2C;AAChF,QAAM,OAAO,WAAW,GAAG;AAC3B,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,SAAS,MAAM,MAAM;AAAA,EACnC,SAAS,KAAK;AACZ,QAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,UAAM,IAAI,SAAS,sBAAsB,qCAAqC,IAAI,KAAK;AAAA,MACrF,KAAK,6BAA6B,IAAI;AAAA,MACtC,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,UAAM,IAAI,SAAS,sBAAsB,sBAAsB,IAAI,uBAAuB;AAAA,MACxF,KAAK,QAAQ,IAAI;AAAA,MACjB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,QAAM,SAAS,mBAAmB,UAAU,IAAI;AAChD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,sBAAsB,IAAI;AAAA,MAC1B;AAAA,QACE,KAAK,QAAQ,IAAI;AAAA,QACjB,SAAS,OAAO,MAAM;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO;AAChB;AASA,eAAsB,kBAAkB,KAAa,QAAqC;AACxF,QAAM,YAAY,mBAAmB,MAAM,MAAM;AACjD,QAAM,OAAO,WAAW,GAAG;AAC3B,MAAI;AACF,UAAM,yBAAyB,MAAM,GAAG,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA,GAAM;AAAA,MAC9E,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,QAAQ,KAAK,QAAQ,GAAG;AAC1B,YAAM,IAAI,SAAS,iBAAiB,8BAA8B,IAAI,KAAK;AAAA,QACzE,KAAK,4DAA4D,IAAI,IAAI,IAAI;AAAA,QAC7E,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,WAAW,KAAuB;AACzC,SAAO,QAAQ,KAAK,QAAQ;AAC9B;AAEA,SAAS,QAAQ,KAAc,MAAuB;AACpD,SACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACT,IAA2B,SAAS;AAEzC;","names":[]}