@kashdao/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +868 -0
- package/SECURITY.md +110 -0
- package/dist/account-AVFLEM5D.js +90 -0
- package/dist/account-AVFLEM5D.js.map +1 -0
- package/dist/auth-GUJCVKTD.js +239 -0
- package/dist/auth-GUJCVKTD.js.map +1 -0
- package/dist/chunk-BN2CUM42.js +9 -0
- package/dist/chunk-BN2CUM42.js.map +1 -0
- package/dist/chunk-BRK7KJ4O.js +154 -0
- package/dist/chunk-BRK7KJ4O.js.map +1 -0
- package/dist/chunk-KMBMQIZ7.js +393 -0
- package/dist/chunk-KMBMQIZ7.js.map +1 -0
- package/dist/chunk-LBIRQHX5.js +133 -0
- package/dist/chunk-LBIRQHX5.js.map +1 -0
- package/dist/chunk-MIXOZU2S.js +366 -0
- package/dist/chunk-MIXOZU2S.js.map +1 -0
- package/dist/chunk-QJMF73M5.js +129 -0
- package/dist/chunk-QJMF73M5.js.map +1 -0
- package/dist/chunk-UZNSYATZ.js +680 -0
- package/dist/chunk-UZNSYATZ.js.map +1 -0
- package/dist/chunk-VIADBYFY.js +94 -0
- package/dist/chunk-VIADBYFY.js.map +1 -0
- package/dist/chunk-YHCG2SUC.js +159 -0
- package/dist/chunk-YHCG2SUC.js.map +1 -0
- package/dist/chunk-YJX3JJ4M.js +174 -0
- package/dist/chunk-YJX3JJ4M.js.map +1 -0
- package/dist/client-IOM55ZCS.js +14 -0
- package/dist/client-IOM55ZCS.js.map +1 -0
- package/dist/completion-ZZGX5BQC.js +100 -0
- package/dist/completion-ZZGX5BQC.js.map +1 -0
- package/dist/config-XJL5TSYK.js +690 -0
- package/dist/config-XJL5TSYK.js.map +1 -0
- package/dist/config-store-3ZSYGXMQ.js +44 -0
- package/dist/config-store-3ZSYGXMQ.js.map +1 -0
- package/dist/docs-MDKKSJDC.js +102 -0
- package/dist/docs-MDKKSJDC.js.map +1 -0
- package/dist/eoa-IQ72EIHR.js +551 -0
- package/dist/eoa-IQ72EIHR.js.map +1 -0
- package/dist/errors-WMZIEGQI.js +18 -0
- package/dist/errors-WMZIEGQI.js.map +1 -0
- package/dist/explain-H4EH3KH3.js +165 -0
- package/dist/explain-H4EH3KH3.js.map +1 -0
- package/dist/global-options-XOLJUPTT.js +18 -0
- package/dist/global-options-XOLJUPTT.js.map +1 -0
- package/dist/health-VZEIII74.js +98 -0
- package/dist/health-VZEIII74.js.map +1 -0
- package/dist/help-footer-GTANVDNP.js +43 -0
- package/dist/help-footer-GTANVDNP.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +251 -0
- package/dist/index.js.map +1 -0
- package/dist/intro-PQQTYWR7.js +41 -0
- package/dist/intro-PQQTYWR7.js.map +1 -0
- package/dist/markets-C37JZDPE.js +295 -0
- package/dist/markets-C37JZDPE.js.map +1 -0
- package/dist/output-VCBZ3FM7.js +22 -0
- package/dist/output-VCBZ3FM7.js.map +1 -0
- package/dist/portfolio-2AEPIJIG.js +115 -0
- package/dist/portfolio-2AEPIJIG.js.map +1 -0
- package/dist/protocol-XEQXF2GW.js +1572 -0
- package/dist/protocol-XEQXF2GW.js.map +1 -0
- package/dist/quote-I4DVJ7ZB.js +144 -0
- package/dist/quote-I4DVJ7ZB.js.map +1 -0
- package/dist/schema-PIFQ65TS.js +410 -0
- package/dist/schema-PIFQ65TS.js.map +1 -0
- package/dist/setup-USZ6IODD.js +260 -0
- package/dist/setup-USZ6IODD.js.map +1 -0
- package/dist/stdin-YW2CEQXU.js +28 -0
- package/dist/stdin-YW2CEQXU.js.map +1 -0
- package/dist/trace-IZBYTUFO.js +103 -0
- package/dist/trace-IZBYTUFO.js.map +1 -0
- package/dist/trade-IIZSEXEI.js +586 -0
- package/dist/trade-IIZSEXEI.js.map +1 -0
- package/dist/version-JDK3PEP5.js +117 -0
- package/dist/version-JDK3PEP5.js.map +1 -0
- package/dist/version-check-TDCION37.js +138 -0
- package/dist/version-check-TDCION37.js.map +1 -0
- package/dist/webhooks-PTVRKICZ.js +680 -0
- package/dist/webhooks-PTVRKICZ.js.map +1 -0
- package/dist/with-retry-4FIZG3A7.js +223 -0
- package/dist/with-retry-4FIZG3A7.js.map +1 -0
- package/package.json +99 -0
package/SECURITY.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Non-custodial design
|
|
4
|
+
|
|
5
|
+
`@kashdao/cli` is **non-custodial software**. The following invariants
|
|
6
|
+
hold across both of its execution modes (Kash-orchestrated and
|
|
7
|
+
self-orchestrated):
|
|
8
|
+
|
|
9
|
+
- **Kash never holds or controls user funds.** Customer USDC,
|
|
10
|
+
outcome tokens, and any other on-chain assets always sit at an
|
|
11
|
+
on-chain address the user controls. The CLI holds zero balances on
|
|
12
|
+
the user's behalf; the Kash backend holds zero balances on the
|
|
13
|
+
user's behalf.
|
|
14
|
+
- **Kash never has access to user signing keys.** Smart-account keys
|
|
15
|
+
are split via Privy's MPC across the user's device and Privy's
|
|
16
|
+
HSM-backed enclave; Kash operates no key share. In self-orchestrated
|
|
17
|
+
mode the user's private key lives wherever they put it (file ref,
|
|
18
|
+
env var, KMS, Fireblocks, hardware wallet) and never leaves the
|
|
19
|
+
CLI process boundary.
|
|
20
|
+
- **The CLI never persists user keys.** API keys persist to
|
|
21
|
+
`~/.kash/config.json` at mode `0600`; raw private keys are never
|
|
22
|
+
written to disk by the CLI. The `signerKeyRef` config field is a
|
|
23
|
+
_reference_ (`file:<path>` / `env:<NAME>`) — the underlying secret
|
|
24
|
+
is resolved at invocation time and never persisted by the CLI.
|
|
25
|
+
- **Kash never signs transactions or UserOps on the user's behalf.**
|
|
26
|
+
Every state-changing on-chain action is signed inside the user's
|
|
27
|
+
Privy MPC enclave or by the consumer's own signer; no signature ever
|
|
28
|
+
originates on a Kash server or inside the Kash backend's process
|
|
29
|
+
boundary.
|
|
30
|
+
- **Kash never moves user funds.** Settlement is on-chain via
|
|
31
|
+
open-source protocol contracts; there is no Kash-controlled pool of
|
|
32
|
+
funds in the path, no Kash-controlled balance ledger, and no
|
|
33
|
+
Kash-controlled relay that can re-route value.
|
|
34
|
+
- **The API-key delegation is scoped and revocable.** A `kash_live_*`
|
|
35
|
+
/ `kash_test_*` key carries narrowly-scoped limits (per-trade caps,
|
|
36
|
+
daily caps, allowed operations, allowlisted IPs) the customer sets
|
|
37
|
+
themselves. Revocation via `kash auth revoke <id>` (or the Kash
|
|
38
|
+
dashboard) takes effect on the next request.
|
|
39
|
+
- **Kash is not a money-services business, custodian, exchange, or
|
|
40
|
+
broker-dealer.** Kash publishes software and protocol contracts;
|
|
41
|
+
customers run the software and interact with the protocol from
|
|
42
|
+
accounts they control.
|
|
43
|
+
|
|
44
|
+
Equivalent statements hold for `@kashdao/sdk`, `@kashdao/protocol-sdk`,
|
|
45
|
+
and `kashdao-protocol-sdk` (Python).
|
|
46
|
+
|
|
47
|
+
## Reporting a vulnerability
|
|
48
|
+
|
|
49
|
+
**Please do not file a public GitHub issue for security vulnerabilities.**
|
|
50
|
+
|
|
51
|
+
Email: `security@kash.bot`
|
|
52
|
+
|
|
53
|
+
Include:
|
|
54
|
+
|
|
55
|
+
- A clear description of the issue
|
|
56
|
+
- Reproduction steps or proof-of-concept
|
|
57
|
+
- The version of `@kashdao/cli` (and any relevant runtime info: Node version,
|
|
58
|
+
shell, OS)
|
|
59
|
+
- Whether the issue affects the CLI, the underlying SDK, the public API,
|
|
60
|
+
or some combination
|
|
61
|
+
|
|
62
|
+
We aim to:
|
|
63
|
+
|
|
64
|
+
1. Acknowledge receipt within **2 business days**.
|
|
65
|
+
2. Confirm or reject the report within **7 business days**.
|
|
66
|
+
3. Ship a fix within **30 days** for accepted reports, faster for issues
|
|
67
|
+
actively being exploited.
|
|
68
|
+
|
|
69
|
+
Once the fix has shipped and a reasonable upgrade window has elapsed, we
|
|
70
|
+
publish a coordinated disclosure crediting the reporter (with consent).
|
|
71
|
+
|
|
72
|
+
## Scope
|
|
73
|
+
|
|
74
|
+
In scope:
|
|
75
|
+
|
|
76
|
+
- Vulnerabilities in `@kashdao/cli` itself (this package).
|
|
77
|
+
- Issues that compromise the security guarantees of the local CLI surface:
|
|
78
|
+
- API key leakage to disk in plaintext outside `~/.kash/config.json`
|
|
79
|
+
- `~/.kash/config.json` being created or rewritten with permissions
|
|
80
|
+
looser than `0600` on POSIX systems
|
|
81
|
+
- Shell completion files being installed without user consent
|
|
82
|
+
- JSON output containing API keys, webhook secrets, or other plaintext
|
|
83
|
+
credentials when not explicitly requested
|
|
84
|
+
- Path-traversal or arbitrary-file-write through any CLI argument
|
|
85
|
+
|
|
86
|
+
Out of scope (please report to `security@kash.bot` separately if relevant):
|
|
87
|
+
|
|
88
|
+
- Vulnerabilities in `@kashdao/sdk` — see that package's `SECURITY.md`
|
|
89
|
+
- Vulnerabilities in the Kash public API
|
|
90
|
+
- Vulnerabilities in transitive dependencies (`commander`, `chalk`,
|
|
91
|
+
`cli-table3`, `ora`, `omelette`, `zod`) — report upstream first; we'll
|
|
92
|
+
respond to coordinated disclosures
|
|
93
|
+
- Issues requiring physical access to the user's machine, or the user
|
|
94
|
+
having already lost local credentials
|
|
95
|
+
- Permission tightening failing on Windows or non-POSIX filesystems
|
|
96
|
+
(chmod is best-effort; documented in `src/utils/config-store.ts`)
|
|
97
|
+
|
|
98
|
+
## Supported versions
|
|
99
|
+
|
|
100
|
+
| Version | Supported |
|
|
101
|
+
| ------- | -------------------- |
|
|
102
|
+
| 0.x | ✅ Latest minor only |
|
|
103
|
+
|
|
104
|
+
While the package is `0.x`, only the latest published `0.x.y` receives
|
|
105
|
+
security fixes. After 1.0, we'll publish a long-term-support policy here.
|
|
106
|
+
|
|
107
|
+
## Disclosure
|
|
108
|
+
|
|
109
|
+
Acknowledged researchers are listed in `CHANGELOG.md` against the patched
|
|
110
|
+
release.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildClient
|
|
4
|
+
} from "./chunk-QJMF73M5.js";
|
|
5
|
+
import "./chunk-KMBMQIZ7.js";
|
|
6
|
+
import "./chunk-YHCG2SUC.js";
|
|
7
|
+
import "./chunk-BN2CUM42.js";
|
|
8
|
+
import {
|
|
9
|
+
readGlobals
|
|
10
|
+
} from "./chunk-LBIRQHX5.js";
|
|
11
|
+
import {
|
|
12
|
+
print,
|
|
13
|
+
printJson,
|
|
14
|
+
style
|
|
15
|
+
} from "./chunk-VIADBYFY.js";
|
|
16
|
+
import "./chunk-MIXOZU2S.js";
|
|
17
|
+
import {
|
|
18
|
+
toCliError
|
|
19
|
+
} from "./chunk-UZNSYATZ.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/account/index.ts
|
|
22
|
+
import { Command as Command2 } from "commander";
|
|
23
|
+
|
|
24
|
+
// src/commands/account/usage.ts
|
|
25
|
+
import { Command } from "commander";
|
|
26
|
+
var usageCommand = new Command("usage").description("Show per-key telemetry summary (24h / 7d / 30d windows).").addHelpText(
|
|
27
|
+
"after",
|
|
28
|
+
`
|
|
29
|
+
Examples:
|
|
30
|
+
$ kash account usage
|
|
31
|
+
$ kash account usage --json | jq '.trades["24h"].successRate'
|
|
32
|
+
$ kash account usage --json --quiet | jq -r '.trades["7d"].successRate * 100'
|
|
33
|
+
`
|
|
34
|
+
).action(async (_opts, cmd) => {
|
|
35
|
+
const globals = readGlobals(cmd);
|
|
36
|
+
let usage;
|
|
37
|
+
try {
|
|
38
|
+
const { client } = await buildClient({ requireAuth: true, globals });
|
|
39
|
+
usage = await client.account.usage();
|
|
40
|
+
} catch (cause) {
|
|
41
|
+
throw toCliError(cause);
|
|
42
|
+
}
|
|
43
|
+
if (globals.json) {
|
|
44
|
+
printJson(usage);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
print("");
|
|
48
|
+
print(` ${style.dim("API key ")} ${usage.apiKeyId}`);
|
|
49
|
+
print(` ${style.dim("Generated at ")} ${usage.generatedAt}`);
|
|
50
|
+
print("");
|
|
51
|
+
print(` ${style.dim("Trades")}`);
|
|
52
|
+
renderTradeWindow(" 24h", usage.trades["24h"]);
|
|
53
|
+
print(
|
|
54
|
+
` latency p50 ${formatMs(usage.trades["24h"].latencyMs.p50)} p99 ${formatMs(usage.trades["24h"].latencyMs.p99)}`
|
|
55
|
+
);
|
|
56
|
+
renderTradeWindow(" 7d", usage.trades["7d"]);
|
|
57
|
+
renderTradeWindow(" 30d", usage.trades["30d"]);
|
|
58
|
+
print("");
|
|
59
|
+
print(` ${style.dim("Webhooks (7d)")}`);
|
|
60
|
+
const wh = usage.webhooks["7d"];
|
|
61
|
+
print(
|
|
62
|
+
` emitted=${wh.emitted} delivered=${wh.delivered} failed=${wh.failed} success=${formatRate(wh.successRate)}`
|
|
63
|
+
);
|
|
64
|
+
print("");
|
|
65
|
+
print(` ${style.dim("Auth (24h)")}`);
|
|
66
|
+
print(
|
|
67
|
+
` failures=${usage.auth["24h"].failures} rate-limit-rejections=${usage.auth["24h"].rateLimitRejections}`
|
|
68
|
+
);
|
|
69
|
+
print("");
|
|
70
|
+
});
|
|
71
|
+
function renderTradeWindow(label, w) {
|
|
72
|
+
print(
|
|
73
|
+
` ${label} total=${w.total} completed=${w.completed} failed=${w.failed} success=${formatRate(w.successRate)}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
function formatRate(rate) {
|
|
77
|
+
if (rate === null) return style.dim("\u2014");
|
|
78
|
+
return `${(rate * 100).toFixed(2)}%`;
|
|
79
|
+
}
|
|
80
|
+
function formatMs(value) {
|
|
81
|
+
if (value === null) return style.dim("\u2014");
|
|
82
|
+
return `${value}ms`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/commands/account/index.ts
|
|
86
|
+
var accountCommand = new Command2("account").description("Read-only account surfaces (usage telemetry, etc.).").addCommand(usageCommand);
|
|
87
|
+
export {
|
|
88
|
+
accountCommand
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=account-AVFLEM5D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/account/index.ts","../src/commands/account/usage.ts"],"sourcesContent":["/**\n * `kash account` — read-only account-scoped surfaces for the\n * authenticating API key.\n *\n * Currently just `usage` (per-key telemetry). Future siblings could\n * include `keys list / revoke` if those flows ever migrate from the\n * webapp Settings page into the CLI.\n */\n\nimport { Command } from 'commander';\n\nimport { usageCommand } from './usage.js';\n\nexport const accountCommand = new Command('account')\n .description('Read-only account surfaces (usage telemetry, etc.).')\n .addCommand(usageCommand);\n","/**\n * `kash account usage` — per-key telemetry summary.\n *\n * Wraps `GET /v1/account/usage` (DX7). Returns rolling-window stats\n * for the calling key: trade volume + success rate (24h / 7d / 30d),\n * end-to-end latency p50/p99, webhook delivery success, and recent\n * auth + rate-limit signals.\n */\n\nimport { Command } from 'commander';\n\nimport { toCliError } from '../../errors.js';\nimport { buildClient } from '../../utils/client.js';\nimport { readGlobals } from '../../utils/global-options.js';\nimport { print, printJson, style } from '../../utils/output.js';\n\nimport type { AccountUsage } from '@kashdao/sdk';\n\nexport const usageCommand = new Command('usage')\n .description('Show per-key telemetry summary (24h / 7d / 30d windows).')\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash account usage\n $ kash account usage --json | jq '.trades[\"24h\"].successRate'\n $ kash account usage --json --quiet | jq -r '.trades[\"7d\"].successRate * 100'\n`\n )\n .action(async (_opts, cmd: Command) => {\n const globals = readGlobals(cmd);\n let usage: AccountUsage;\n try {\n const { client } = await buildClient({ requireAuth: true, globals });\n usage = await client.account.usage();\n } catch (cause) {\n throw toCliError(cause);\n }\n\n if (globals.json) {\n printJson(usage);\n return;\n }\n\n print('');\n print(` ${style.dim('API key ')} ${usage.apiKeyId}`);\n print(` ${style.dim('Generated at ')} ${usage.generatedAt}`);\n\n print('');\n print(` ${style.dim('Trades')}`);\n renderTradeWindow(' 24h', usage.trades['24h']);\n print(\n ` latency p50 ${formatMs(usage.trades['24h'].latencyMs.p50)} ` +\n `p99 ${formatMs(usage.trades['24h'].latencyMs.p99)}`\n );\n renderTradeWindow(' 7d', usage.trades['7d']);\n renderTradeWindow(' 30d', usage.trades['30d']);\n\n print('');\n print(` ${style.dim('Webhooks (7d)')}`);\n const wh = usage.webhooks['7d'];\n print(\n ` emitted=${wh.emitted} delivered=${wh.delivered} failed=${wh.failed} ` +\n `success=${formatRate(wh.successRate)}`\n );\n\n print('');\n print(` ${style.dim('Auth (24h)')}`);\n print(\n ` failures=${usage.auth['24h'].failures} ` +\n `rate-limit-rejections=${usage.auth['24h'].rateLimitRejections}`\n );\n print('');\n });\n\nfunction renderTradeWindow(\n label: string,\n w: AccountUsage['trades']['24h'] | AccountUsage['trades']['7d']\n): void {\n print(\n ` ${label} total=${w.total} completed=${w.completed} ` +\n `failed=${w.failed} success=${formatRate(w.successRate)}`\n );\n}\n\nfunction formatRate(rate: number | null): string {\n if (rate === null) return style.dim('—');\n return `${(rate * 100).toFixed(2)}%`;\n}\n\nfunction formatMs(value: number | null): string {\n if (value === null) return style.dim('—');\n return `${value}ms`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,eAAe;AASjB,IAAM,eAAe,IAAI,QAAQ,OAAO,EAC5C,YAAY,0DAA0D,EACtE;AAAA,EACC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMF,EACC,OAAO,OAAO,OAAO,QAAiB;AACrC,QAAM,UAAU,YAAY,GAAG;AAC/B,MAAI;AACJ,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,YAAY,EAAE,aAAa,MAAM,QAAQ,CAAC;AACnE,YAAQ,MAAM,OAAO,QAAQ,MAAM;AAAA,EACrC,SAAS,OAAO;AACd,UAAM,WAAW,KAAK;AAAA,EACxB;AAEA,MAAI,QAAQ,MAAM;AAChB,cAAU,KAAK;AACf;AAAA,EACF;AAEA,QAAM,EAAE;AACR,QAAM,KAAK,MAAM,IAAI,gBAAgB,CAAC,IAAI,MAAM,QAAQ,EAAE;AAC1D,QAAM,KAAK,MAAM,IAAI,gBAAgB,CAAC,IAAI,MAAM,WAAW,EAAE;AAE7D,QAAM,EAAE;AACR,QAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,EAAE;AAChC,oBAAkB,SAAS,MAAM,OAAO,KAAK,CAAC;AAC9C;AAAA,IACE,uBAAuB,SAAS,MAAM,OAAO,KAAK,EAAE,UAAU,GAAG,CAAC,SACzD,SAAS,MAAM,OAAO,KAAK,EAAE,UAAU,GAAG,CAAC;AAAA,EACtD;AACA,oBAAkB,SAAS,MAAM,OAAO,IAAI,CAAC;AAC7C,oBAAkB,SAAS,MAAM,OAAO,KAAK,CAAC;AAE9C,QAAM,EAAE;AACR,QAAM,KAAK,MAAM,IAAI,eAAe,CAAC,EAAE;AACvC,QAAM,KAAK,MAAM,SAAS,IAAI;AAC9B;AAAA,IACE,mBAAmB,GAAG,OAAO,eAAe,GAAG,SAAS,YAAY,GAAG,MAAM,aAChE,WAAW,GAAG,WAAW,CAAC;AAAA,EACzC;AAEA,QAAM,EAAE;AACR,QAAM,KAAK,MAAM,IAAI,YAAY,CAAC,EAAE;AACpC;AAAA,IACE,oBAAoB,MAAM,KAAK,KAAK,EAAE,QAAQ,2BACnB,MAAM,KAAK,KAAK,EAAE,mBAAmB;AAAA,EAClE;AACA,QAAM,EAAE;AACV,CAAC;AAEH,SAAS,kBACP,OACA,GACM;AACN;AAAA,IACE,OAAO,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,SAAS,YAC5C,EAAE,MAAM,aAAa,WAAW,EAAE,WAAW,CAAC;AAAA,EAC5D;AACF;AAEA,SAAS,WAAW,MAA6B;AAC/C,MAAI,SAAS,KAAM,QAAO,MAAM,IAAI,QAAG;AACvC,SAAO,IAAI,OAAO,KAAK,QAAQ,CAAC,CAAC;AACnC;AAEA,SAAS,SAAS,OAA8B;AAC9C,MAAI,UAAU,KAAM,QAAO,MAAM,IAAI,QAAG;AACxC,SAAO,GAAG,KAAK;AACjB;;;ADhFO,IAAM,iBAAiB,IAAIC,SAAQ,SAAS,EAChD,YAAY,qDAAqD,EACjE,WAAW,YAAY;","names":["Command","Command"]}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
redact
|
|
4
|
+
} from "./chunk-BRK7KJ4O.js";
|
|
5
|
+
import {
|
|
6
|
+
clearConfigField,
|
|
7
|
+
readConfig,
|
|
8
|
+
setCurrentProfile,
|
|
9
|
+
updateConfig
|
|
10
|
+
} from "./chunk-KMBMQIZ7.js";
|
|
11
|
+
import "./chunk-YHCG2SUC.js";
|
|
12
|
+
import {
|
|
13
|
+
readGlobals
|
|
14
|
+
} from "./chunk-LBIRQHX5.js";
|
|
15
|
+
import {
|
|
16
|
+
log,
|
|
17
|
+
print,
|
|
18
|
+
printJson,
|
|
19
|
+
style
|
|
20
|
+
} from "./chunk-VIADBYFY.js";
|
|
21
|
+
import "./chunk-MIXOZU2S.js";
|
|
22
|
+
import {
|
|
23
|
+
CliValidationError,
|
|
24
|
+
toCliError
|
|
25
|
+
} from "./chunk-UZNSYATZ.js";
|
|
26
|
+
|
|
27
|
+
// src/commands/auth/index.ts
|
|
28
|
+
import { Command as Command4 } from "commander";
|
|
29
|
+
|
|
30
|
+
// src/commands/auth/logout.ts
|
|
31
|
+
import { Command } from "commander";
|
|
32
|
+
var logoutCommand = new Command("logout").description("Remove the stored API key from ~/.kash/config.json.").addHelpText(
|
|
33
|
+
"after",
|
|
34
|
+
`
|
|
35
|
+
Examples:
|
|
36
|
+
$ kash auth logout
|
|
37
|
+
$ kash auth logout --profile staging
|
|
38
|
+
`
|
|
39
|
+
).action(async (_opts, cmd) => {
|
|
40
|
+
const globals = readGlobals(cmd);
|
|
41
|
+
const scope = {
|
|
42
|
+
...globals.profile === void 0 ? {} : { profile: globals.profile },
|
|
43
|
+
...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
|
|
44
|
+
};
|
|
45
|
+
const before = await readConfig(scope);
|
|
46
|
+
await clearConfigField("apiKey", scope);
|
|
47
|
+
const envWillOverride = Boolean(process.env["KASH_API_KEY"]);
|
|
48
|
+
if (globals.json) {
|
|
49
|
+
printJson({
|
|
50
|
+
ok: true,
|
|
51
|
+
profile: before.profile,
|
|
52
|
+
cleared: Boolean(before.apiKey && before.sources.apiKey === "file"),
|
|
53
|
+
envOverride: envWillOverride
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (before.apiKey === void 0) {
|
|
58
|
+
log.info(`No API key was stored for profile "${before.profile}".`);
|
|
59
|
+
} else {
|
|
60
|
+
log.success(`API key cleared from profile "${before.profile}".`);
|
|
61
|
+
}
|
|
62
|
+
if (envWillOverride) {
|
|
63
|
+
log.warn("KASH_API_KEY is still set in your environment. Unset it to fully log out.");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/commands/auth/set-key.ts
|
|
68
|
+
import { Command as Command2 } from "commander";
|
|
69
|
+
var setKeyCommand = new Command2("set-key").description("Store an API key in ~/.kash/config.json (mode 0600).").argument("[key]", 'API key starting with "kash_" (omit to read from stdin or prompt)').option("--from-stdin", "read the key from stdin (preferred for secret-store integration)").option(
|
|
70
|
+
"--activate",
|
|
71
|
+
"also switch the active profile to the one being written (default: leave the active profile unchanged \u2014 `kash auth set-key --profile staging` updates 'staging' but doesn't make it active; pass --activate or run `kash config use staging` separately)"
|
|
72
|
+
).addHelpText(
|
|
73
|
+
"after",
|
|
74
|
+
`
|
|
75
|
+
Examples:
|
|
76
|
+
# Interactive prompt (no shell history footprint).
|
|
77
|
+
$ kash auth set-key
|
|
78
|
+
|
|
79
|
+
# Pipe from a secret store.
|
|
80
|
+
$ pass show kash/api-key | kash auth set-key --from-stdin
|
|
81
|
+
$ op read "op://Private/kash/api-key" | kash auth set-key --from-stdin
|
|
82
|
+
|
|
83
|
+
# Positional (convenient but shows up in ~/.bash_history).
|
|
84
|
+
$ kash auth set-key kash_live_abc...
|
|
85
|
+
$ kash auth set-key kash_test_abc... --profile staging
|
|
86
|
+
`
|
|
87
|
+
).action(async (positional, options, cmd) => {
|
|
88
|
+
const globals = readGlobals(cmd);
|
|
89
|
+
const key = await resolveKey(positional, options);
|
|
90
|
+
if (!key.startsWith("kash_")) {
|
|
91
|
+
throw new CliValidationError(
|
|
92
|
+
'API keys must start with "kash_".',
|
|
93
|
+
"Request a `kash_test_*` staging key by emailing `engineering@kash.bot` with your intended use case. Self-service issuance and `kash_live_*` keys land with the production launch."
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
let result;
|
|
97
|
+
try {
|
|
98
|
+
result = await updateConfig(
|
|
99
|
+
{ apiKey: key },
|
|
100
|
+
{
|
|
101
|
+
...globals.profile === void 0 ? {} : { profile: globals.profile },
|
|
102
|
+
...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
if (options.activate === true) {
|
|
106
|
+
await setCurrentProfile(
|
|
107
|
+
result.profile,
|
|
108
|
+
globals.configPath === void 0 ? void 0 : { configPath: globals.configPath }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} catch (cause) {
|
|
112
|
+
throw toCliError(cause);
|
|
113
|
+
}
|
|
114
|
+
if (globals.json) {
|
|
115
|
+
printJson({
|
|
116
|
+
ok: true,
|
|
117
|
+
apiKey: redact(key),
|
|
118
|
+
profile: result.profile,
|
|
119
|
+
// Whether this run also flipped the file's currentProfile.
|
|
120
|
+
// Agents driving multi-profile workflows can chain off this
|
|
121
|
+
// to know if a follow-up `kash config use` is needed.
|
|
122
|
+
activated: options.activate === true
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
log.success(`API key saved to profile "${result.profile}" (${redact(key)}).`);
|
|
127
|
+
log.detail("Location", globals.configPath ?? "~/.kash/config.json (mode 0600)");
|
|
128
|
+
if (options.activate === true) {
|
|
129
|
+
log.success(`Active profile is now "${result.profile}".`);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
async function resolveKey(positional, options) {
|
|
133
|
+
if (positional !== void 0 && positional !== "" && options.fromStdin === true) {
|
|
134
|
+
throw new CliValidationError(
|
|
135
|
+
"Pass either the positional <key> OR --from-stdin, not both.",
|
|
136
|
+
"The positional key would silently win and the piped value would be dropped."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (positional !== void 0 && positional !== "") {
|
|
140
|
+
return positional;
|
|
141
|
+
}
|
|
142
|
+
if (options.fromStdin === true) {
|
|
143
|
+
return readFromStdin();
|
|
144
|
+
}
|
|
145
|
+
if (process.stdin.isTTY) {
|
|
146
|
+
return promptInteractively();
|
|
147
|
+
}
|
|
148
|
+
throw new CliValidationError(
|
|
149
|
+
"No API key provided.",
|
|
150
|
+
"Pass the key positionally, use --from-stdin to read it from a pipe, or run interactively for a prompt."
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
async function readFromStdin() {
|
|
154
|
+
const { readStdinTrimmed } = await import("./stdin-YW2CEQXU.js");
|
|
155
|
+
return readStdinTrimmed();
|
|
156
|
+
}
|
|
157
|
+
async function promptInteractively() {
|
|
158
|
+
const { password } = await import("@inquirer/prompts");
|
|
159
|
+
const answer = await password({
|
|
160
|
+
message: "Paste your API key (kash_live_\u2026 or kash_test_\u2026):",
|
|
161
|
+
mask: "*"
|
|
162
|
+
});
|
|
163
|
+
return answer.trim();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/commands/auth/status.ts
|
|
167
|
+
import { Command as Command3 } from "commander";
|
|
168
|
+
var statusCommand = new Command3("status").description("Show locally-configured credentials. Does not call the API.").addHelpText(
|
|
169
|
+
"after",
|
|
170
|
+
`
|
|
171
|
+
Examples:
|
|
172
|
+
$ kash auth status
|
|
173
|
+
$ kash auth status --profile prod
|
|
174
|
+
$ kash auth status --json --quiet | jq -r '.apiKey'
|
|
175
|
+
`
|
|
176
|
+
).action(async (_opts, cmd) => {
|
|
177
|
+
const globals = readGlobals(cmd);
|
|
178
|
+
const config = await readConfig({
|
|
179
|
+
...globals.profile === void 0 ? {} : { profile: globals.profile },
|
|
180
|
+
...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
|
|
181
|
+
});
|
|
182
|
+
if (globals.json) {
|
|
183
|
+
printJson({
|
|
184
|
+
// Pure local-state field. `true` means "an API key is in
|
|
185
|
+
// scope for this profile" (file or env), NOT "this key is
|
|
186
|
+
// accepted by the server" — `kash auth status` is offline by
|
|
187
|
+
// design. Use `kash health --json --quiet | jq .ok` for a
|
|
188
|
+
// server-side check.
|
|
189
|
+
configured: Boolean(config.apiKey),
|
|
190
|
+
// Backward-compat alias. Same value as `configured`. Kept
|
|
191
|
+
// for downstream scripts that pin to the old field name —
|
|
192
|
+
// will be removed in a major bump after the rename has
|
|
193
|
+
// burned in.
|
|
194
|
+
authenticated: Boolean(config.apiKey),
|
|
195
|
+
apiKey: config.apiKey ? redact(config.apiKey) : null,
|
|
196
|
+
profile: config.profile,
|
|
197
|
+
baseUrl: config.baseUrl,
|
|
198
|
+
defaultChainId: config.defaultChainId,
|
|
199
|
+
// Direct-mode fields (`kash protocol …` / `kash eoa …`). Always
|
|
200
|
+
// emitted (`null` when unset) so direct-mode operators can
|
|
201
|
+
// introspect their own profile via JSON without falling back
|
|
202
|
+
// to reading `~/.kash/config.json` directly. The signerKeyRef
|
|
203
|
+
// is the reference shape (`file:<path>` / `env:<NAME>`),
|
|
204
|
+
// never the raw private key — the CLI never persists keys.
|
|
205
|
+
rpcUrl: config.rpcUrl ?? null,
|
|
206
|
+
smartAccount: config.smartAccount ?? null,
|
|
207
|
+
bundlerUrl: config.bundlerUrl ?? null,
|
|
208
|
+
bundlerProvider: config.bundlerProvider ?? null,
|
|
209
|
+
signerKeyRef: config.signerKeyRef ?? null,
|
|
210
|
+
sources: config.sources
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (!config.apiKey) {
|
|
215
|
+
log.warn(`No API key configured for profile "${config.profile}".`);
|
|
216
|
+
log.detail(
|
|
217
|
+
"Hint",
|
|
218
|
+
"Run 'kash setup' for the wizard, or 'kash auth set-key' for the bare key. Or set KASH_API_KEY."
|
|
219
|
+
);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
print(`${style.success("\u2713")} API key configured (profile: ${config.profile})`);
|
|
223
|
+
print(
|
|
224
|
+
` ${style.dim("Note ")} not verified against the server \u2014 run \`kash health\` to probe`
|
|
225
|
+
);
|
|
226
|
+
print(` ${style.dim("API key ")} ${redact(config.apiKey)} (${config.sources.apiKey})`);
|
|
227
|
+
print(` ${style.dim("Profile ")} ${config.profile} (${config.sources.profile})`);
|
|
228
|
+
print(` ${style.dim("Base URL ")} ${config.baseUrl} (${config.sources.baseUrl})`);
|
|
229
|
+
print(
|
|
230
|
+
` ${style.dim("Chain id ")} ${String(config.defaultChainId)} (${config.sources.defaultChainId})`
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// src/commands/auth/index.ts
|
|
235
|
+
var authCommand = new Command4("auth").description("Manage local API credentials.").addCommand(setKeyCommand).addCommand(statusCommand).addCommand(logoutCommand);
|
|
236
|
+
export {
|
|
237
|
+
authCommand
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=auth-GUJCVKTD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/auth/index.ts","../src/commands/auth/logout.ts","../src/commands/auth/set-key.ts","../src/commands/auth/status.ts"],"sourcesContent":["/**\n * `kash auth` — credential management.\n *\n * Users issue a key from the Kash dashboard and run `auth set-key`\n * (or set `KASH_API_KEY` in their environment). A future browser-based\n * login flow may be added; for now, key issuance lives on the dashboard.\n */\n\nimport { Command } from 'commander';\n\nimport { logoutCommand } from './logout.js';\nimport { setKeyCommand } from './set-key.js';\nimport { statusCommand } from './status.js';\n\nexport const authCommand = new Command('auth')\n .description('Manage local API credentials.')\n .addCommand(setKeyCommand)\n .addCommand(statusCommand)\n .addCommand(logoutCommand);\n","/**\n * `kash auth logout` — remove the stored API key.\n *\n * Only clears the `apiKey` field; the rest of the config (baseUrl,\n * chain id) survives so logging back in doesn't lose unrelated\n * preferences. If `KASH_API_KEY` is set in the environment, we warn\n * the user that env vars take precedence so the \"logout\" is partial.\n */\n\nimport { Command } from 'commander';\n\nimport { clearConfigField, readConfig } from '../../utils/config-store.js';\nimport { readGlobals } from '../../utils/global-options.js';\nimport { log, printJson } from '../../utils/output.js';\n\nexport const logoutCommand = new Command('logout')\n .description('Remove the stored API key from ~/.kash/config.json.')\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash auth logout\n $ kash auth logout --profile staging\n`\n )\n .action(async (_opts, cmd: Command) => {\n const globals = readGlobals(cmd);\n const scope = {\n ...(globals.profile === undefined ? {} : { profile: globals.profile }),\n ...(globals.configPath === undefined ? {} : { configPath: globals.configPath }),\n };\n const before = await readConfig(scope);\n await clearConfigField('apiKey', scope);\n\n const envWillOverride = Boolean(process.env['KASH_API_KEY']);\n\n if (globals.json) {\n printJson({\n ok: true,\n profile: before.profile,\n cleared: Boolean(before.apiKey && before.sources.apiKey === 'file'),\n envOverride: envWillOverride,\n });\n return;\n }\n\n if (before.apiKey === undefined) {\n log.info(`No API key was stored for profile \"${before.profile}\".`);\n } else {\n log.success(`API key cleared from profile \"${before.profile}\".`);\n }\n if (envWillOverride) {\n log.warn('KASH_API_KEY is still set in your environment. Unset it to fully log out.');\n }\n });\n","/**\n * `kash auth set-key [key]` — store an API key in `~/.kash/config.json`.\n *\n * Three input paths, each leaves no trace of the key in shell history:\n *\n * 1. Positional `<key>` — convenient for one-off use, but shows up\n * in `~/.bash_history`. Kept for backward compat.\n * 2. `--from-stdin` — pipe a key from a secret-store (`pass kash`,\n * `1password read`, `aws secretsmanager`, …). The key is\n * `.trim()`-ed so trailing newlines from the source are ignored.\n * 3. Interactive prompt (no positional, no flag, TTY available) —\n * reads from stderr-bound readline so the prompt doesn't taint\n * stdout pipes. Single newline of input.\n *\n * Validates the prefix client-side (the SDK's config schema enforces\n * the same shape, but failing here gives a clearer error than the\n * generic config error chain). The persisted file is `0600`.\n */\n\nimport { Command } from 'commander';\n\nimport { CliValidationError, toCliError } from '../../errors.js';\nimport { setCurrentProfile, updateConfig } from '../../utils/config-store.js';\nimport { redact } from '../../utils/formatting.js';\nimport { readGlobals } from '../../utils/global-options.js';\nimport { log, printJson } from '../../utils/output.js';\n\ntype SetKeyOptions = {\n fromStdin?: boolean;\n activate?: boolean;\n};\n\nexport const setKeyCommand = new Command('set-key')\n .description('Store an API key in ~/.kash/config.json (mode 0600).')\n .argument('[key]', 'API key starting with \"kash_\" (omit to read from stdin or prompt)')\n .option('--from-stdin', 'read the key from stdin (preferred for secret-store integration)')\n .option(\n '--activate',\n \"also switch the active profile to the one being written (default: leave the active profile unchanged — `kash auth set-key --profile staging` updates 'staging' but doesn't make it active; pass --activate or run `kash config use staging` separately)\"\n )\n .addHelpText(\n 'after',\n `\nExamples:\n # Interactive prompt (no shell history footprint).\n $ kash auth set-key\n\n # Pipe from a secret store.\n $ pass show kash/api-key | kash auth set-key --from-stdin\n $ op read \"op://Private/kash/api-key\" | kash auth set-key --from-stdin\n\n # Positional (convenient but shows up in ~/.bash_history).\n $ kash auth set-key kash_live_abc...\n $ kash auth set-key kash_test_abc... --profile staging\n`\n )\n .action(async (positional: string | undefined, options: SetKeyOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n const key = await resolveKey(positional, options);\n\n if (!key.startsWith('kash_')) {\n throw new CliValidationError(\n 'API keys must start with \"kash_\".',\n 'Request a `kash_test_*` staging key by emailing `engineering@kash.bot` with your intended use case. Self-service issuance and `kash_live_*` keys land with the production launch.'\n );\n }\n let result;\n try {\n result = await updateConfig(\n { apiKey: key },\n {\n ...(globals.profile === undefined ? {} : { profile: globals.profile }),\n ...(globals.configPath === undefined ? {} : { configPath: globals.configPath }),\n }\n );\n // `--activate` flips the file's `currentProfile` so the next\n // command picks up this profile without needing\n // `kash config use <name>`. Mirrors the all-in-one wizard\n // behaviour of `kash setup`.\n if (options.activate === true) {\n await setCurrentProfile(\n result.profile,\n globals.configPath === undefined ? undefined : { configPath: globals.configPath }\n );\n }\n } catch (cause) {\n throw toCliError(cause);\n }\n\n if (globals.json) {\n // Surface the actually-written profile (resolved from --profile,\n // KASH_PROFILE, file's currentProfile, or \"default\") so the JSON\n // contract reports truth, not the unresolved flag value.\n printJson({\n ok: true,\n apiKey: redact(key),\n profile: result.profile,\n // Whether this run also flipped the file's currentProfile.\n // Agents driving multi-profile workflows can chain off this\n // to know if a follow-up `kash config use` is needed.\n activated: options.activate === true,\n });\n return;\n }\n log.success(`API key saved to profile \"${result.profile}\" (${redact(key)}).`);\n log.detail('Location', globals.configPath ?? '~/.kash/config.json (mode 0600)');\n if (options.activate === true) {\n log.success(`Active profile is now \"${result.profile}\".`);\n }\n });\n\n/**\n * Resolve the API key from the three supported sources. Order:\n * 1. Positional argument (legacy, less secure — still works).\n * 2. `--from-stdin` (read until EOF, trim, single key per stream).\n * 3. Interactive prompt (TTY only).\n *\n * **Mutual-exclusion guard.** Pre-fix, `kash auth set-key <key>\n * --from-stdin` silently used the positional and dropped the piped\n * secret. That's a real footgun: the operator sees \"API key saved\"\n * for the WRONG key (the one on argv, possibly a stale value from\n * shell history). We refuse to guess and require the operator to\n * pick one source.\n */\nasync function resolveKey(positional: string | undefined, options: SetKeyOptions): Promise<string> {\n if (positional !== undefined && positional !== '' && options.fromStdin === true) {\n throw new CliValidationError(\n 'Pass either the positional <key> OR --from-stdin, not both.',\n 'The positional key would silently win and the piped value would be dropped.'\n );\n }\n if (positional !== undefined && positional !== '') {\n return positional;\n }\n if (options.fromStdin === true) {\n return readFromStdin();\n }\n if (process.stdin.isTTY) {\n return promptInteractively();\n }\n // No positional, no --from-stdin, no TTY — caller's intent is\n // ambiguous. Surface a clear validation error rather than reading\n // stdin silently (which would surprise CI runs that pipe other\n // data into the command).\n throw new CliValidationError(\n 'No API key provided.',\n 'Pass the key positionally, use --from-stdin to read it from a pipe, or run interactively for a prompt.'\n );\n}\n\nasync function readFromStdin(): Promise<string> {\n // Use the shared BOM-aware reader. Notepad-saved files (and VSCode\n // with `files.encoding=utf8bom`) prepend U+FEFF; without the strip,\n // the leading three bytes leak into the API key and fail the\n // `kash_` prefix check.\n const { readStdinTrimmed } = await import('../../utils/stdin.js');\n return readStdinTrimmed();\n}\n\nasync function promptInteractively(): Promise<string> {\n // Use the masked-input prompt so the key never echoes to the\n // terminal (defends against shoulder-surfing, screen recordings,\n // and scroll-back capture). The previous `readline.question` path\n // echoed every keystroke which defeated the \"no shell history\"\n // promise of this command. Lazy-load `@inquirer/prompts` to keep\n // it off the cold path for non-interactive invocations.\n const { password } = await import('@inquirer/prompts');\n const answer = await password({\n message: 'Paste your API key (kash_live_… or kash_test_…):',\n mask: '*',\n });\n return answer.trim();\n}\n","/**\n * `kash auth status` — report the locally-stored credentials.\n *\n * Does NOT call the API. There is no `whoami` endpoint on the public\n * API today (the only auth-flavoured route is webhook-secret rotation,\n * which is a write). Reporting purely-local state keeps the command\n * cheap and side-effect-free; the moment the user issues a real call\n * (e.g. `kash markets list`) the SDK raises `KashAuthenticationError`\n * if the key is invalid.\n */\n\nimport { Command } from 'commander';\n\nimport { readConfig } from '../../utils/config-store.js';\nimport { redact } from '../../utils/formatting.js';\nimport { readGlobals } from '../../utils/global-options.js';\nimport { log, print, printJson, style } from '../../utils/output.js';\n\nexport const statusCommand = new Command('status')\n .description('Show locally-configured credentials. Does not call the API.')\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash auth status\n $ kash auth status --profile prod\n $ kash auth status --json --quiet | jq -r '.apiKey'\n`\n )\n .action(async (_opts, cmd: Command) => {\n const globals = readGlobals(cmd);\n const config = await readConfig({\n ...(globals.profile === undefined ? {} : { profile: globals.profile }),\n ...(globals.configPath === undefined ? {} : { configPath: globals.configPath }),\n });\n\n if (globals.json) {\n printJson({\n // Pure local-state field. `true` means \"an API key is in\n // scope for this profile\" (file or env), NOT \"this key is\n // accepted by the server\" — `kash auth status` is offline by\n // design. Use `kash health --json --quiet | jq .ok` for a\n // server-side check.\n configured: Boolean(config.apiKey),\n // Backward-compat alias. Same value as `configured`. Kept\n // for downstream scripts that pin to the old field name —\n // will be removed in a major bump after the rename has\n // burned in.\n authenticated: Boolean(config.apiKey),\n apiKey: config.apiKey ? redact(config.apiKey) : null,\n profile: config.profile,\n baseUrl: config.baseUrl,\n defaultChainId: config.defaultChainId,\n // Direct-mode fields (`kash protocol …` / `kash eoa …`). Always\n // emitted (`null` when unset) so direct-mode operators can\n // introspect their own profile via JSON without falling back\n // to reading `~/.kash/config.json` directly. The signerKeyRef\n // is the reference shape (`file:<path>` / `env:<NAME>`),\n // never the raw private key — the CLI never persists keys.\n rpcUrl: config.rpcUrl ?? null,\n smartAccount: config.smartAccount ?? null,\n bundlerUrl: config.bundlerUrl ?? null,\n bundlerProvider: config.bundlerProvider ?? null,\n signerKeyRef: config.signerKeyRef ?? null,\n sources: config.sources,\n });\n return;\n }\n\n if (!config.apiKey) {\n log.warn(`No API key configured for profile \"${config.profile}\".`);\n log.detail(\n 'Hint',\n \"Run 'kash setup' for the wizard, or 'kash auth set-key' for the bare key. Or set KASH_API_KEY.\"\n );\n return;\n }\n\n print(`${style.success('✓')} API key configured (profile: ${config.profile})`);\n print(\n ` ${style.dim('Note ')} not verified against the server — run \\`kash health\\` to probe`\n );\n print(` ${style.dim('API key ')} ${redact(config.apiKey)} (${config.sources.apiKey})`);\n print(` ${style.dim('Profile ')} ${config.profile} (${config.sources.profile})`);\n print(` ${style.dim('Base URL ')} ${config.baseUrl} (${config.sources.baseUrl})`);\n print(\n ` ${style.dim('Chain id ')} ${String(config.defaultChainId)} (${config.sources.defaultChainId})`\n );\n });\n"],"mappings":"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","names":["Command","Command","Command","Command","Command","Command"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["/**\n * Package version stamp.\n *\n * Hardcoded rather than imported from `package.json` because:\n *\n * 1. JSON imports vary across runtimes; the bundled CLI must work\n * under Node 22+ regardless of how the consumer's tsconfig is\n * shaped.\n * 2. tsup's JSON-bundling behaviour is bundler-version-specific and\n * we'd rather not depend on it.\n * 3. A simple constant is auditable: `git grep CLI_VERSION` finds\n * every reference instantly.\n *\n * **Maintenance**: bumped in lockstep with `package.json#version` by\n * the changesets release flow. The unit test in\n * `tests/unit/version.test.ts` reads `package.json` and asserts the\n * two stay in sync, so a forgotten bump fails CI before the bad\n * release ships to npm.\n */\nexport const CLI_VERSION = '0.1.0';\n"],"mappings":";;;AAmBO,IAAM,cAAc;","names":[]}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
style
|
|
4
|
+
} from "./chunk-VIADBYFY.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/formatting.ts
|
|
7
|
+
import Table from "cli-table3";
|
|
8
|
+
var ELLIPSIS = "...";
|
|
9
|
+
function codepointSlice(s, start, end) {
|
|
10
|
+
return Array.from(s).slice(start, end).join("");
|
|
11
|
+
}
|
|
12
|
+
function codepointLength(s) {
|
|
13
|
+
return Array.from(s).length;
|
|
14
|
+
}
|
|
15
|
+
function shortId(id, prefix = 8) {
|
|
16
|
+
if (codepointLength(id) <= prefix + ELLIPSIS.length) return id;
|
|
17
|
+
return `${codepointSlice(id, 0, prefix)}${ELLIPSIS}`;
|
|
18
|
+
}
|
|
19
|
+
function truncate(s, max) {
|
|
20
|
+
return codepointLength(s) > max ? `${codepointSlice(s, 0, max - ELLIPSIS.length)}${ELLIPSIS}` : s;
|
|
21
|
+
}
|
|
22
|
+
function redact(key) {
|
|
23
|
+
if (codepointLength(key) <= 8) return "***";
|
|
24
|
+
return `${codepointSlice(key, 0, 8)}${ELLIPSIS}${codepointSlice(key, -4)}`;
|
|
25
|
+
}
|
|
26
|
+
function capitalize(s) {
|
|
27
|
+
if (s.length === 0) return s;
|
|
28
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
29
|
+
}
|
|
30
|
+
function formatAddress(address, lead = 6, tail = 4) {
|
|
31
|
+
if (codepointLength(address) <= lead + tail + ELLIPSIS.length) return address;
|
|
32
|
+
return `${codepointSlice(address, 0, lead)}${ELLIPSIS}${codepointSlice(address, -tail)}`;
|
|
33
|
+
}
|
|
34
|
+
function formatDate(value) {
|
|
35
|
+
if (!value) return "-";
|
|
36
|
+
const d = typeof value === "string" ? new Date(value) : value;
|
|
37
|
+
if (Number.isNaN(d.getTime())) return "-";
|
|
38
|
+
return d.toLocaleString();
|
|
39
|
+
}
|
|
40
|
+
function formatUsdcAtomic(atomic) {
|
|
41
|
+
if (typeof atomic === "string" && atomic.includes(".")) {
|
|
42
|
+
const n = Number.parseFloat(atomic);
|
|
43
|
+
if (Number.isNaN(n)) return "$0.00";
|
|
44
|
+
return n.toLocaleString("en-US", {
|
|
45
|
+
style: "currency",
|
|
46
|
+
currency: "USD",
|
|
47
|
+
minimumFractionDigits: 2,
|
|
48
|
+
maximumFractionDigits: 2
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
let big;
|
|
52
|
+
try {
|
|
53
|
+
big = typeof atomic === "bigint" ? atomic : BigInt(atomic);
|
|
54
|
+
} catch {
|
|
55
|
+
return "$0.00";
|
|
56
|
+
}
|
|
57
|
+
const negative = big < 0n;
|
|
58
|
+
const abs = negative ? -big : big;
|
|
59
|
+
const whole = abs / 1000000n;
|
|
60
|
+
const frac = abs % 1000000n;
|
|
61
|
+
const wholeStr = whole.toLocaleString("en-US");
|
|
62
|
+
const cents = (frac / 10000n).toString().padStart(2, "0");
|
|
63
|
+
return `${negative ? "-" : ""}$${wholeStr}.${cents}`;
|
|
64
|
+
}
|
|
65
|
+
function formatUsdcDecimal(value) {
|
|
66
|
+
const n = Number.parseFloat(value);
|
|
67
|
+
if (Number.isNaN(n)) return value;
|
|
68
|
+
return n.toLocaleString("en-US", {
|
|
69
|
+
style: "currency",
|
|
70
|
+
currency: "USD",
|
|
71
|
+
minimumFractionDigits: 2,
|
|
72
|
+
maximumFractionDigits: 2
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function formatProbability(p) {
|
|
76
|
+
if (p === null || p === void 0 || Number.isNaN(p)) return "-";
|
|
77
|
+
return `${(p * 100).toFixed(1)}%`;
|
|
78
|
+
}
|
|
79
|
+
function formatBigDecimal(value, options) {
|
|
80
|
+
const { baseDecimals, displayDecimals } = options;
|
|
81
|
+
const useThousandSeparator = options.thousandSeparator ?? true;
|
|
82
|
+
let big;
|
|
83
|
+
try {
|
|
84
|
+
big = typeof value === "bigint" ? value : BigInt(value);
|
|
85
|
+
} catch {
|
|
86
|
+
return typeof value === "string" ? value : "0";
|
|
87
|
+
}
|
|
88
|
+
const negative = big < 0n;
|
|
89
|
+
const abs = negative ? -big : big;
|
|
90
|
+
const baseScale = 10n ** BigInt(baseDecimals);
|
|
91
|
+
const whole = abs / baseScale;
|
|
92
|
+
const frac = abs % baseScale;
|
|
93
|
+
const truncated = (frac / 10n ** BigInt(baseDecimals - displayDecimals)).toString().padStart(displayDecimals, "0");
|
|
94
|
+
const wholeStr = useThousandSeparator ? whole.toLocaleString("en-US") : whole.toString();
|
|
95
|
+
return `${negative ? "-" : ""}${wholeStr}.${truncated}`;
|
|
96
|
+
}
|
|
97
|
+
function formatWad(wad) {
|
|
98
|
+
return formatBigDecimal(wad, { baseDecimals: 18, displayDecimals: 4 });
|
|
99
|
+
}
|
|
100
|
+
function formatGwei(wei) {
|
|
101
|
+
return formatBigDecimal(wei, {
|
|
102
|
+
baseDecimals: 9,
|
|
103
|
+
displayDecimals: 3,
|
|
104
|
+
thousandSeparator: false
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function colorStatus(value) {
|
|
108
|
+
if (!value) return style.dim("-");
|
|
109
|
+
switch (value.toUpperCase()) {
|
|
110
|
+
case "ACTIVE":
|
|
111
|
+
case "COMPLETED":
|
|
112
|
+
return style.success(value);
|
|
113
|
+
case "EXECUTING":
|
|
114
|
+
case "VALIDATING":
|
|
115
|
+
case "PENDING":
|
|
116
|
+
return style.cyan(value);
|
|
117
|
+
case "PENDING_CONFIRMATION":
|
|
118
|
+
return style.warn(value);
|
|
119
|
+
case "RESOLVED":
|
|
120
|
+
return style.info(value);
|
|
121
|
+
case "REJECTED":
|
|
122
|
+
case "FAILED":
|
|
123
|
+
return style.error(value);
|
|
124
|
+
case "UNSEEDED":
|
|
125
|
+
case "FROZEN":
|
|
126
|
+
return style.dim(value);
|
|
127
|
+
default:
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function createTable(headers) {
|
|
132
|
+
return new Table({
|
|
133
|
+
head: headers.map((h) => style.bold(h)),
|
|
134
|
+
style: { head: [], border: [] }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export {
|
|
139
|
+
shortId,
|
|
140
|
+
truncate,
|
|
141
|
+
redact,
|
|
142
|
+
capitalize,
|
|
143
|
+
formatAddress,
|
|
144
|
+
formatDate,
|
|
145
|
+
formatUsdcAtomic,
|
|
146
|
+
formatUsdcDecimal,
|
|
147
|
+
formatProbability,
|
|
148
|
+
formatBigDecimal,
|
|
149
|
+
formatWad,
|
|
150
|
+
formatGwei,
|
|
151
|
+
colorStatus,
|
|
152
|
+
createTable
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=chunk-BRK7KJ4O.js.map
|