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 +0,0 @@
1
- {"version":3,"sources":["../src/commands/config.ts","../src/lib/lock.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","import { mkdir, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface FileLockOptions {\n /** Give up acquiring after this long (ms). */\n timeoutMs?: number;\n /** Poll interval between acquisition attempts (ms). */\n retryMs?: number;\n}\n\nconst DEFAULTS = { timeoutMs: 5000, retryMs: 25 };\n\n/** Thrown when the lock cannot be acquired within `timeoutMs`. Carries the lock\n * path so a caller can surface the manual-recovery step in its own error contract. */\nexport class LockTimeoutError extends Error {\n readonly lockPath: string;\n constructor(lockPath: string, timeoutMs: number) {\n super(\n `Timed out waiting for lock at ${lockPath} after ${timeoutMs}ms; if no other tenjin process is running, remove that directory and retry.`,\n );\n this.name = 'LockTimeoutError';\n this.lockPath = lockPath;\n }\n}\n\n/**\n * Minimal cross-process mutex: the lock IS a directory, and mkdir is atomic, so a\n * second holder gets EEXIST and retries with backoff until `timeoutMs`, then\n * throws LockTimeoutError. There is deliberately NO auto-steal of a \"stale\" lock:\n * concurrent waiters each judging a lock stale could remove a fresh holder's\n * directory and lose its update. A crashed holder's lock is recovered by hand (the\n * timeout error names the path); the pid+acquiredAt meta exists only to make that\n * manual call diagnosable. Always released in the finally.\n */\nexport async function withFileLock<T>(\n lockPath: string,\n fn: () => Promise<T>,\n opts: FileLockOptions = {},\n): Promise<T> {\n const { timeoutMs, retryMs } = { ...DEFAULTS, ...opts };\n const metaPath = join(lockPath, 'meta');\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n await mkdir(lockPath);\n break; // acquired\n } catch (err) {\n if (!isEexist(err)) throw err;\n if (Date.now() >= deadline) throw new LockTimeoutError(lockPath, timeoutMs);\n await delay(retryMs);\n }\n }\n await writeFile(metaPath, JSON.stringify({ pid: process.pid, acquiredAt: Date.now() })).catch(\n () => undefined,\n );\n try {\n return await fn();\n } finally {\n // With no stealing, nothing else ever removes or replaces this directory, so\n // the lock here is provably the one we created — the unconditional rm is sound.\n await rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n\nfunction isEexist(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'EEXIST';\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":["mkdir","mkdir"]}
@@ -1,153 +0,0 @@
1
- import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
2
- import {
3
- createLocalWallet,
4
- describeWallet,
5
- importLocalWallet,
6
- resolveWalletProvider
7
- } from "./chunk-QHIEYBLV.js";
8
- import {
9
- getUsdcBalance
10
- } from "./chunk-PAAL45FJ.js";
11
- import "./chunk-SXQQ2ZSR.js";
12
- import "./chunk-TX5T3LKJ.js";
13
- import "./chunk-WOHCAMDQ.js";
14
- import "./chunk-F5DZJLU3.js";
15
- import "./chunk-CPUZRZ2A.js";
16
- import {
17
- loadRawConfig,
18
- resolveSettings,
19
- toMoney
20
- } from "./chunk-LTTRND4A.js";
21
- import {
22
- walletFileExists
23
- } from "./chunk-376LHMK2.js";
24
- import "./chunk-ZPZFPVDN.js";
25
- import {
26
- CliError,
27
- walletPath
28
- } from "./chunk-FIYZLITH.js";
29
- import "./chunk-3IMLRUZE.js";
30
-
31
- // src/commands/wallet.ts
32
- var FUNDING_LINE = "Send USDC on Base. $5 covers ~50 typical resources.";
33
- var isWindows = process.platform === "win32";
34
- var readStdinDefault = async () => {
35
- if (process.stdin.isTTY) return null;
36
- const chunks = [];
37
- for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
38
- const data = Buffer.concat(chunks).toString("utf8").trim();
39
- return data.length > 0 ? data : null;
40
- };
41
- async function runWalletCreate(ctx) {
42
- await refuseIfWalletExists(ctx.dataDir);
43
- const { address, walletPath: path } = await createLocalWallet(ctx.dataDir);
44
- const warnings = custodyWarnings();
45
- return {
46
- data: {
47
- address,
48
- walletPath: path,
49
- provider: "local",
50
- policyEnforcement: "client-only",
51
- warnings
52
- },
53
- humanLines: [`Wallet created: ${address}`, FUNDING_LINE, ...warnings]
54
- };
55
- }
56
- async function runWalletShow(ctx, opts = {}) {
57
- const provider = resolveWalletProvider(ctx, opts);
58
- const desc = await describeWallet(provider);
59
- const { walletPath: path, warnings } = await provider.diagnostics();
60
- return {
61
- data: {
62
- address: desc.address,
63
- provider: desc.provider,
64
- credentialSource: desc.credentialSource,
65
- policyEnforcement: desc.policyEnforcement,
66
- ...path !== void 0 ? { walletPath: path } : {},
67
- warnings
68
- },
69
- humanLines: [`Address: ${desc.address}`, `Key source: ${desc.credentialSource}`, ...warnings]
70
- };
71
- }
72
- async function runWalletBalance(ctx, opts = {}) {
73
- const provider = resolveWalletProvider(ctx, opts);
74
- const desc = await describeWallet(provider);
75
- const config = await loadRawConfig(ctx.dataDir);
76
- const rpcUrl = resolveSettings({
77
- config,
78
- flags: { baseUrl: ctx.flags.baseUrl },
79
- env: process.env
80
- }).rpcUrl.value;
81
- let atomic;
82
- try {
83
- atomic = await getUsdcBalance(desc.address, rpcUrl);
84
- } catch (err) {
85
- throw new CliError("RPC_ERROR", `Could not read the USDC balance from ${rpcUrl}.`, {
86
- fix: "Check your network, or set a working RPC with `tenjin config set rpcUrl <url>`.",
87
- cause: err
88
- });
89
- }
90
- const balance = toMoney(atomic.toString());
91
- return {
92
- data: { address: desc.address, balance },
93
- humanLines: [`Balance: ${balance.usd} USDC on Base`]
94
- };
95
- }
96
- async function runWalletImport(args, ctx, readStdin = readStdinDefault) {
97
- await refuseIfWalletExists(ctx.dataDir);
98
- let key;
99
- let source;
100
- if (args.fromEnv) {
101
- const envKey = process.env.TENJIN_WALLET_KEY;
102
- if (envKey === void 0 || envKey.trim().length === 0) {
103
- throw new CliError("USAGE", "TENJIN_WALLET_KEY is not set.", {
104
- fix: "Set TENJIN_WALLET_KEY, or pipe the key: `echo $KEY | tenjin wallet import`."
105
- });
106
- }
107
- key = envKey.trim();
108
- source = "env";
109
- } else {
110
- const input = await readStdin();
111
- if (input === null) {
112
- throw new CliError("USAGE", "No private key on stdin.", {
113
- fix: "Pipe the key (`echo $KEY | tenjin wallet import`) or use `--from-env`."
114
- });
115
- }
116
- key = input;
117
- source = "stdin";
118
- }
119
- const { address, walletPath: path } = await importLocalWallet(ctx.dataDir, key);
120
- return {
121
- data: { address, walletPath: path, source },
122
- humanLines: [`Imported wallet: ${address}`]
123
- };
124
- }
125
- async function refuseIfWalletExists(dir) {
126
- if (!await walletFileExists(dir)) return;
127
- const path = walletPath(dir);
128
- throw new CliError("WALLET_EXISTS", `A wallet already exists at ${path}.`, {
129
- fix: `Keys are non-recoverable; move it aside first (e.g. \`mv ${path} ${path}.bak\`) to create a new one.`
130
- });
131
- }
132
- function custodyWarnings() {
133
- const warnings = [];
134
- if (hasEnvKey()) {
135
- warnings.push("TENJIN_WALLET_KEY is set and shadows this file at runtime.");
136
- }
137
- if (isWindows) {
138
- warnings.push("File permissions are not enforced on Windows; the key file is not restricted.");
139
- }
140
- return warnings;
141
- }
142
- function hasEnvKey() {
143
- const envKey = process.env.TENJIN_WALLET_KEY;
144
- return envKey !== void 0 && envKey.length > 0;
145
- }
146
- export {
147
- readStdinDefault,
148
- runWalletBalance,
149
- runWalletCreate,
150
- runWalletImport,
151
- runWalletShow
152
- };
153
- //# sourceMappingURL=wallet-NRKWLKHX.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/wallet.ts"],"sourcesContent":["import { CliError } from '../lib/errors';\nimport { walletPath } from '../lib/paths';\nimport { loadRawConfig, resolveSettings } from '../lib/config';\nimport { toMoney } from '../lib/money';\nimport { getUsdcBalance } from '../lib/usdc';\nimport {\n createLocalWallet,\n describeWallet,\n importLocalWallet,\n resolveWalletProvider,\n type ResolveWalletProviderOptions,\n} from '../lib/wallet';\nimport { walletFileExists } from '../lib/wallet/store';\nimport type { CommandContext, CommandResult } from '../context';\n\nconst FUNDING_LINE = 'Send USDC on Base. $5 covers ~50 typical resources.';\nconst isWindows = process.platform === 'win32';\n\n/** Injectable stdin reader for `import`. Null means \"no piped input\" (a TTY, or an\n * empty pipe): the default never blocks on a TTY, so the command turns null into a\n * USAGE error instead of hanging. Tests inject a stub to drive every branch. */\nexport type ReadStdin = () => Promise<string | null>;\n\nexport const readStdinDefault: ReadStdin = async () => {\n if (process.stdin.isTTY) return null;\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));\n const data = Buffer.concat(chunks).toString('utf8').trim();\n return data.length > 0 ? data : null;\n};\n\nexport async function runWalletCreate(ctx: CommandContext): Promise<CommandResult> {\n await refuseIfWalletExists(ctx.dataDir);\n\n const { address, walletPath: path } = await createLocalWallet(ctx.dataDir);\n\n const warnings = custodyWarnings();\n return {\n data: {\n address,\n walletPath: path,\n provider: 'local',\n policyEnforcement: 'client-only',\n warnings,\n },\n humanLines: [`Wallet created: ${address}`, FUNDING_LINE, ...warnings],\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.\n const { walletPath: path, warnings } = await provider.diagnostics();\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 warnings,\n },\n humanLines: [`Address: ${desc.address}`, `Key source: ${desc.credentialSource}`, ...warnings],\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\nexport async function runWalletImport(\n args: { fromEnv: boolean },\n ctx: CommandContext,\n readStdin: ReadStdin = readStdinDefault,\n): Promise<CommandResult> {\n await refuseIfWalletExists(ctx.dataDir);\n\n let key: string;\n let source: 'env' | 'stdin';\n if (args.fromEnv) {\n const envKey = process.env.TENJIN_WALLET_KEY;\n if (envKey === undefined || envKey.trim().length === 0) {\n throw new CliError('USAGE', 'TENJIN_WALLET_KEY is not set.', {\n fix: 'Set TENJIN_WALLET_KEY, or pipe the key: `echo $KEY | tenjin wallet import`.',\n });\n }\n key = envKey.trim();\n source = 'env';\n } else {\n const input = await readStdin();\n if (input === null) {\n throw new CliError('USAGE', 'No private key on stdin.', {\n fix: 'Pipe the key (`echo $KEY | tenjin wallet import`) or use `--from-env`.',\n });\n }\n key = input;\n source = 'stdin';\n }\n\n const { address, walletPath: path } = await importLocalWallet(ctx.dataDir, key);\n\n return {\n data: { address, walletPath: path, source },\n humanLines: [`Imported wallet: ${address}`],\n };\n}\n\n/** create and import both refuse 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":[]}