@aibtc/mcp-server 1.57.0 → 1.59.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/README.md +63 -15
- package/dist/index.js +150 -47
- package/dist/services/credentials.js +7 -2
- package/dist/services/defi.service.js +23 -11
- package/dist/services/hiro-api.d.ts +10 -0
- package/dist/services/hiro-api.js +30 -8
- package/dist/services/nft.service.js +14 -5
- package/dist/services/pillar-api.service.d.ts +8 -0
- package/dist/services/pillar-api.service.js +13 -1
- package/dist/services/x402.service.js +45 -13
- package/dist/tools/bitcoin.tools.js +14 -0
- package/dist/tools/endpoint.tools.js +3 -1
- package/dist/tools/inbox.tools.js +9 -12
- package/dist/tools/news.tools.js +9 -12
- package/dist/tools/pillar-direct.tools.js +29 -22
- package/dist/tools/sbtc.tools.js +2 -9
- package/dist/tools/scaffold.tools.js +9 -14
- package/dist/tools/settings.tools.js +2 -2
- package/dist/tools/stacking-lottery.tools.js +8 -5
- package/dist/tools/yield-dashboard.tools.js +10 -14
- package/dist/utils/encryption.js +2 -0
- package/package.json +10 -5
- package/skill/SKILL.md +14 -3
- package/skill/references/genesis-lifecycle.md +105 -92
- package/dist/config/bitcoin-constants.d.ts.map +0 -1
- package/dist/config/bitcoin-constants.js.map +0 -1
- package/dist/config/caip.d.ts.map +0 -1
- package/dist/config/caip.js.map +0 -1
- package/dist/config/competition.d.ts.map +0 -1
- package/dist/config/competition.js.map +0 -1
- package/dist/config/contracts.d.ts.map +0 -1
- package/dist/config/contracts.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/config/networks.d.ts.map +0 -1
- package/dist/config/networks.js.map +0 -1
- package/dist/config/pillar.d.ts.map +0 -1
- package/dist/config/pillar.js.map +0 -1
- package/dist/config/sponsor.d.ts.map +0 -1
- package/dist/config/sponsor.js.map +0 -1
- package/dist/endpoints/index.d.ts.map +0 -1
- package/dist/endpoints/index.js.map +0 -1
- package/dist/endpoints/registry.d.ts.map +0 -1
- package/dist/endpoints/registry.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/bitflow.service.d.ts.map +0 -1
- package/dist/services/bitflow.service.js.map +0 -1
- package/dist/services/bns.service.d.ts.map +0 -1
- package/dist/services/bns.service.js.map +0 -1
- package/dist/services/credentials.d.ts.map +0 -1
- package/dist/services/credentials.js.map +0 -1
- package/dist/services/defi.service.d.ts.map +0 -1
- package/dist/services/defi.service.js.map +0 -1
- package/dist/services/erc8004.service.d.ts.map +0 -1
- package/dist/services/erc8004.service.js.map +0 -1
- package/dist/services/hiro-api.d.ts.map +0 -1
- package/dist/services/hiro-api.js.map +0 -1
- package/dist/services/inscription-parser.d.ts.map +0 -1
- package/dist/services/inscription-parser.js.map +0 -1
- package/dist/services/lightning/provider.d.ts.map +0 -1
- package/dist/services/lightning/provider.js.map +0 -1
- package/dist/services/lightning/spark-provider.d.ts.map +0 -1
- package/dist/services/lightning/spark-provider.js.map +0 -1
- package/dist/services/lightning-manager.d.ts.map +0 -1
- package/dist/services/lightning-manager.js.map +0 -1
- package/dist/services/mempool-api.d.ts.map +0 -1
- package/dist/services/mempool-api.js.map +0 -1
- package/dist/services/nft.service.d.ts.map +0 -1
- package/dist/services/nft.service.js.map +0 -1
- package/dist/services/nonce-tracker.d.ts.map +0 -1
- package/dist/services/nonce-tracker.js.map +0 -1
- package/dist/services/pillar-api.service.d.ts.map +0 -1
- package/dist/services/pillar-api.service.js.map +0 -1
- package/dist/services/sbtc-deposit.service.d.ts.map +0 -1
- package/dist/services/sbtc-deposit.service.js.map +0 -1
- package/dist/services/sbtc.service.d.ts.map +0 -1
- package/dist/services/sbtc.service.js.map +0 -1
- package/dist/services/scaffold.service.d.ts.map +0 -1
- package/dist/services/scaffold.service.js.map +0 -1
- package/dist/services/signing-key.service.d.ts.map +0 -1
- package/dist/services/signing-key.service.js.map +0 -1
- package/dist/services/stacking.service.d.ts.map +0 -1
- package/dist/services/stacking.service.js.map +0 -1
- package/dist/services/tenero-api.d.ts.map +0 -1
- package/dist/services/tenero-api.js.map +0 -1
- package/dist/services/tokens.service.d.ts.map +0 -1
- package/dist/services/tokens.service.js.map +0 -1
- package/dist/services/unisat-indexer.d.ts.map +0 -1
- package/dist/services/unisat-indexer.js.map +0 -1
- package/dist/services/wallet-manager.d.ts.map +0 -1
- package/dist/services/wallet-manager.js.map +0 -1
- package/dist/services/x402.service.d.ts.map +0 -1
- package/dist/services/x402.service.js.map +0 -1
- package/dist/tools/arxiv-research.tools.d.ts.map +0 -1
- package/dist/tools/arxiv-research.tools.js.map +0 -1
- package/dist/tools/bitcoin.tools.d.ts.map +0 -1
- package/dist/tools/bitcoin.tools.js.map +0 -1
- package/dist/tools/bitflow.tools.d.ts.map +0 -1
- package/dist/tools/bitflow.tools.js.map +0 -1
- package/dist/tools/bns.tools.d.ts.map +0 -1
- package/dist/tools/bns.tools.js.map +0 -1
- package/dist/tools/bounty-scanner.tools.d.ts.map +0 -1
- package/dist/tools/bounty-scanner.tools.js.map +0 -1
- package/dist/tools/child-inscription.tools.d.ts.map +0 -1
- package/dist/tools/child-inscription.tools.js.map +0 -1
- package/dist/tools/competition.tools.d.ts.map +0 -1
- package/dist/tools/competition.tools.js.map +0 -1
- package/dist/tools/contract.tools.d.ts.map +0 -1
- package/dist/tools/contract.tools.js.map +0 -1
- package/dist/tools/credentials.tools.d.ts.map +0 -1
- package/dist/tools/credentials.tools.js.map +0 -1
- package/dist/tools/defi.tools.d.ts.map +0 -1
- package/dist/tools/defi.tools.js.map +0 -1
- package/dist/tools/dual-stacking.tools.d.ts.map +0 -1
- package/dist/tools/dual-stacking.tools.js.map +0 -1
- package/dist/tools/earning.tools.d.ts.map +0 -1
- package/dist/tools/earning.tools.js.map +0 -1
- package/dist/tools/endpoint.tools.d.ts.map +0 -1
- package/dist/tools/endpoint.tools.js.map +0 -1
- package/dist/tools/erc8004.tools.d.ts.map +0 -1
- package/dist/tools/erc8004.tools.js.map +0 -1
- package/dist/tools/identity.tools.d.ts.map +0 -1
- package/dist/tools/identity.tools.js.map +0 -1
- package/dist/tools/inbox-x402.tools.d.ts.map +0 -1
- package/dist/tools/inbox-x402.tools.js.map +0 -1
- package/dist/tools/inbox.tools.d.ts.map +0 -1
- package/dist/tools/inbox.tools.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/jingswap.tools.d.ts.map +0 -1
- package/dist/tools/jingswap.tools.js.map +0 -1
- package/dist/tools/lightning.tools.d.ts.map +0 -1
- package/dist/tools/lightning.tools.js.map +0 -1
- package/dist/tools/mempool.tools.d.ts.map +0 -1
- package/dist/tools/mempool.tools.js.map +0 -1
- package/dist/tools/news.tools.d.ts.map +0 -1
- package/dist/tools/news.tools.js.map +0 -1
- package/dist/tools/nft.tools.d.ts.map +0 -1
- package/dist/tools/nft.tools.js.map +0 -1
- package/dist/tools/nonce.tools.d.ts.map +0 -1
- package/dist/tools/nonce.tools.js.map +0 -1
- package/dist/tools/nostr.tools.d.ts.map +0 -1
- package/dist/tools/nostr.tools.js.map +0 -1
- package/dist/tools/openrouter.tools.d.ts.map +0 -1
- package/dist/tools/openrouter.tools.js.map +0 -1
- package/dist/tools/ordinals-marketplace.tools.d.ts.map +0 -1
- package/dist/tools/ordinals-marketplace.tools.js.map +0 -1
- package/dist/tools/ordinals-p2p.tools.d.ts.map +0 -1
- package/dist/tools/ordinals-p2p.tools.js.map +0 -1
- package/dist/tools/ordinals.tools.d.ts.map +0 -1
- package/dist/tools/ordinals.tools.js.map +0 -1
- package/dist/tools/pillar-direct.tools.d.ts.map +0 -1
- package/dist/tools/pillar-direct.tools.js.map +0 -1
- package/dist/tools/pillar.tools.d.ts.map +0 -1
- package/dist/tools/pillar.tools.js.map +0 -1
- package/dist/tools/psbt.helpers.d.ts.map +0 -1
- package/dist/tools/psbt.helpers.js.map +0 -1
- package/dist/tools/psbt.tools.d.ts.map +0 -1
- package/dist/tools/psbt.tools.js.map +0 -1
- package/dist/tools/query.tools.d.ts.map +0 -1
- package/dist/tools/query.tools.js.map +0 -1
- package/dist/tools/relay-diagnostic.tools.d.ts.map +0 -1
- package/dist/tools/relay-diagnostic.tools.js.map +0 -1
- package/dist/tools/reputation.tools.d.ts.map +0 -1
- package/dist/tools/reputation.tools.js.map +0 -1
- package/dist/tools/runes.tools.d.ts.map +0 -1
- package/dist/tools/runes.tools.js.map +0 -1
- package/dist/tools/sbtc.tools.d.ts.map +0 -1
- package/dist/tools/sbtc.tools.js.map +0 -1
- package/dist/tools/scaffold.tools.d.ts.map +0 -1
- package/dist/tools/scaffold.tools.js.map +0 -1
- package/dist/tools/schemas.d.ts.map +0 -1
- package/dist/tools/schemas.js.map +0 -1
- package/dist/tools/settings.tools.d.ts.map +0 -1
- package/dist/tools/settings.tools.js.map +0 -1
- package/dist/tools/signing.tools.d.ts.map +0 -1
- package/dist/tools/signing.tools.js.map +0 -1
- package/dist/tools/skill-mappings.d.ts.map +0 -1
- package/dist/tools/skill-mappings.js.map +0 -1
- package/dist/tools/souldinals.tools.d.ts.map +0 -1
- package/dist/tools/souldinals.tools.js.map +0 -1
- package/dist/tools/stacking-lottery.tools.d.ts.map +0 -1
- package/dist/tools/stacking-lottery.tools.js.map +0 -1
- package/dist/tools/stacking.tools.d.ts.map +0 -1
- package/dist/tools/stacking.tools.js.map +0 -1
- package/dist/tools/stacks-market.tools.d.ts.map +0 -1
- package/dist/tools/stacks-market.tools.js.map +0 -1
- package/dist/tools/styx.tools.d.ts.map +0 -1
- package/dist/tools/styx.tools.js.map +0 -1
- package/dist/tools/taproot-multisig.tools.d.ts.map +0 -1
- package/dist/tools/taproot-multisig.tools.js.map +0 -1
- package/dist/tools/tenero.tools.d.ts.map +0 -1
- package/dist/tools/tenero.tools.js.map +0 -1
- package/dist/tools/tokens.tools.d.ts.map +0 -1
- package/dist/tools/tokens.tools.js.map +0 -1
- package/dist/tools/transfer.tools.d.ts.map +0 -1
- package/dist/tools/transfer.tools.js.map +0 -1
- package/dist/tools/wallet-management.tools.d.ts.map +0 -1
- package/dist/tools/wallet-management.tools.js.map +0 -1
- package/dist/tools/wallet.tools.d.ts.map +0 -1
- package/dist/tools/wallet.tools.js.map +0 -1
- package/dist/tools/yield-dashboard.tools.d.ts.map +0 -1
- package/dist/tools/yield-dashboard.tools.js.map +0 -1
- package/dist/tools/yield-hunter.tools.d.ts.map +0 -1
- package/dist/tools/yield-hunter.tools.js.map +0 -1
- package/dist/transactions/bitcoin-builder.d.ts.map +0 -1
- package/dist/transactions/bitcoin-builder.js.map +0 -1
- package/dist/transactions/builder.d.ts.map +0 -1
- package/dist/transactions/builder.js.map +0 -1
- package/dist/transactions/child-inscription-builder.d.ts.map +0 -1
- package/dist/transactions/child-inscription-builder.js.map +0 -1
- package/dist/transactions/clarity-values.d.ts.map +0 -1
- package/dist/transactions/clarity-values.js.map +0 -1
- package/dist/transactions/inscription-builder.d.ts.map +0 -1
- package/dist/transactions/inscription-builder.js.map +0 -1
- package/dist/transactions/post-conditions.d.ts.map +0 -1
- package/dist/transactions/post-conditions.js.map +0 -1
- package/dist/transactions/rune-transfer-builder.d.ts.map +0 -1
- package/dist/transactions/rune-transfer-builder.js.map +0 -1
- package/dist/transactions/runestone-builder.d.ts.map +0 -1
- package/dist/transactions/runestone-builder.js.map +0 -1
- package/dist/transactions/sponsor-builder.d.ts.map +0 -1
- package/dist/transactions/sponsor-builder.js.map +0 -1
- package/dist/utils/bip322.d.ts.map +0 -1
- package/dist/utils/bip322.js.map +0 -1
- package/dist/utils/bitcoin.d.ts.map +0 -1
- package/dist/utils/bitcoin.js.map +0 -1
- package/dist/utils/encryption.d.ts.map +0 -1
- package/dist/utils/encryption.js.map +0 -1
- package/dist/utils/erc8004-helpers.d.ts.map +0 -1
- package/dist/utils/erc8004-helpers.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/fee.d.ts.map +0 -1
- package/dist/utils/fee.js.map +0 -1
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/l402-protocol.d.ts.map +0 -1
- package/dist/utils/l402-protocol.js.map +0 -1
- package/dist/utils/redact.d.ts.map +0 -1
- package/dist/utils/redact.js.map +0 -1
- package/dist/utils/relay-health.d.ts.map +0 -1
- package/dist/utils/relay-health.js.map +0 -1
- package/dist/utils/storage.d.ts.map +0 -1
- package/dist/utils/storage.js.map +0 -1
- package/dist/utils/tokens.d.ts.map +0 -1
- package/dist/utils/tokens.js.map +0 -1
- package/dist/utils/x402-payment-logging.d.ts.map +0 -1
- package/dist/utils/x402-payment-logging.js.map +0 -1
- package/dist/utils/x402-payment-state.d.ts.map +0 -1
- package/dist/utils/x402-payment-state.js.map +0 -1
- package/dist/utils/x402-protocol.d.ts.map +0 -1
- package/dist/utils/x402-protocol.js.map +0 -1
- package/dist/utils/x402-recovery.d.ts.map +0 -1
- package/dist/utils/x402-recovery.js.map +0 -1
- package/dist/yield-hunter/index.d.ts.map +0 -1
- package/dist/yield-hunter/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -45,32 +45,53 @@ This detects your OS and writes to the correct Claude Desktop config file:
|
|
|
45
45
|
|
|
46
46
|
Restart Claude Desktop after installing.
|
|
47
47
|
|
|
48
|
-
###
|
|
48
|
+
### Other MCP Clients
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
This is a standard stdio MCP server, so it works with **any** MCP-compatible client. Claude Code is the default `--install` target; select another client with a flag:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
|
|
54
|
-
npx @aibtc/mcp-server@latest --install --
|
|
53
|
+
npx @aibtc/mcp-server@latest --install --cursor # Cursor
|
|
54
|
+
npx @aibtc/mcp-server@latest --install --windsurf # Windsurf
|
|
55
|
+
npx @aibtc/mcp-server@latest --install --gemini # Gemini CLI
|
|
56
|
+
npx @aibtc/mcp-server@latest --install --codex # OpenAI Codex CLI
|
|
57
|
+
npx @aibtc/mcp-server@latest --install --vscode # VS Code (writes ./.vscode/mcp.json)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Flag | Client | Config written |
|
|
61
|
+
|------|--------|----------------|
|
|
62
|
+
| _(none)_ | Claude Code | `~/.claude.json` |
|
|
63
|
+
| `--desktop` | Claude Desktop | `claude_desktop_config.json` (see path table above) |
|
|
64
|
+
| `--cursor` | Cursor | `~/.cursor/mcp.json` |
|
|
65
|
+
| `--windsurf` | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
66
|
+
| `--gemini` | Gemini CLI | `~/.gemini/settings.json` |
|
|
67
|
+
| `--codex` | OpenAI Codex CLI | `~/.codex/config.toml` |
|
|
68
|
+
| `--vscode` | VS Code (Copilot agent) | `./.vscode/mcp.json` (project-scoped) |
|
|
69
|
+
|
|
70
|
+
Each installer merges into the existing config — it won't clobber other servers or settings. Restart the client afterward.
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
### Testnet Mode
|
|
73
|
+
|
|
74
|
+
Add `--testnet` to any install command:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @aibtc/mcp-server@latest --install --testnet # Claude Code, testnet
|
|
78
|
+
npx @aibtc/mcp-server@latest --install --cursor --testnet # Cursor, testnet
|
|
58
79
|
```
|
|
59
80
|
|
|
60
81
|
> **Why npx?** Using `npx @aibtc/mcp-server@latest` ensures you always get the newest version automatically. Global installs (`npm install -g`) won't auto-update.
|
|
61
82
|
|
|
62
83
|
### Manual Configuration
|
|
63
84
|
|
|
64
|
-
If you prefer to configure manually, add the following to your config file.
|
|
85
|
+
If you prefer to configure manually, add the following to your client's config file. The `-y` flag stops npx from prompting for confirmation.
|
|
65
86
|
|
|
66
|
-
**Claude Code**
|
|
87
|
+
**Claude Code / Claude Desktop / Cursor / Windsurf / Gemini CLI** — `mcpServers` JSON:
|
|
67
88
|
|
|
68
89
|
```json
|
|
69
90
|
{
|
|
70
91
|
"mcpServers": {
|
|
71
92
|
"aibtc": {
|
|
72
93
|
"command": "npx",
|
|
73
|
-
"args": ["@aibtc/mcp-server@latest"],
|
|
94
|
+
"args": ["-y", "@aibtc/mcp-server@latest"],
|
|
74
95
|
"env": {
|
|
75
96
|
"NETWORK": "mainnet"
|
|
76
97
|
}
|
|
@@ -79,23 +100,50 @@ If you prefer to configure manually, add the following to your config file.
|
|
|
79
100
|
}
|
|
80
101
|
```
|
|
81
102
|
|
|
82
|
-
**
|
|
103
|
+
**VS Code** (`.vscode/mcp.json`) — uses a `servers` key and a typed entry:
|
|
83
104
|
|
|
84
105
|
```json
|
|
85
106
|
{
|
|
86
|
-
"
|
|
107
|
+
"servers": {
|
|
87
108
|
"aibtc": {
|
|
109
|
+
"type": "stdio",
|
|
88
110
|
"command": "npx",
|
|
89
111
|
"args": ["-y", "@aibtc/mcp-server@latest"],
|
|
90
|
-
"env": {
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
"env": { "NETWORK": "mainnet" }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**OpenAI Codex CLI** (`~/.codex/config.toml`) — TOML, not JSON:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[mcp_servers.aibtc]
|
|
122
|
+
command = "npx"
|
|
123
|
+
args = ["-y", "@aibtc/mcp-server@latest"]
|
|
124
|
+
|
|
125
|
+
[mcp_servers.aibtc.env]
|
|
126
|
+
NETWORK = "mainnet"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Zed** (`settings.json`) — uses a `context_servers` key:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"context_servers": {
|
|
134
|
+
"aibtc": {
|
|
135
|
+
"source": "custom",
|
|
136
|
+
"command": "npx",
|
|
137
|
+
"args": ["-y", "@aibtc/mcp-server@latest"],
|
|
138
|
+
"env": { "NETWORK": "mainnet" }
|
|
93
139
|
}
|
|
94
140
|
}
|
|
95
141
|
}
|
|
96
142
|
```
|
|
97
143
|
|
|
98
|
-
|
|
144
|
+
**Cline / Roo Code** (VS Code extension) — add the same `mcpServers` JSON block above via the extension's MCP settings panel (the exact `cline_mcp_settings.json` path varies by OS and VS Code build).
|
|
145
|
+
|
|
146
|
+
> Any other MCP client works too — point it at `npx -y @aibtc/mcp-server@latest` over stdio with `NETWORK` in the env.
|
|
99
147
|
|
|
100
148
|
## Giving Claude a Wallet
|
|
101
149
|
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,13 @@ import { initializeStorage } from "./utils/storage.js";
|
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
const packageJson = require("../package.json");
|
|
15
15
|
// =============================================================================
|
|
16
|
-
// AUTO-INSTALL FOR
|
|
16
|
+
// AUTO-INSTALL FOR MCP CLIENTS
|
|
17
|
+
//
|
|
18
|
+
// The server is a standard stdio MCP server, so it works with any MCP client.
|
|
19
|
+
// `--install` writes the correct config for a target client. Claude Code is the
|
|
20
|
+
// default; other clients are selected with a flag (e.g. --cursor, --codex).
|
|
17
21
|
// =============================================================================
|
|
22
|
+
const SERVER_NPM = "@aibtc/mcp-server@latest";
|
|
18
23
|
function getClaudeDesktopConfigPath() {
|
|
19
24
|
const platform = process.platform;
|
|
20
25
|
const home = os.homedir();
|
|
@@ -31,13 +36,24 @@ function getClaudeDesktopConfigPath() {
|
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
38
|
async function readJsonConfig(filePath) {
|
|
39
|
+
let content;
|
|
40
|
+
try {
|
|
41
|
+
content = await fs.readFile(filePath, "utf8");
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error.code === "ENOENT") {
|
|
45
|
+
return {}; // No config yet - fresh install
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
34
49
|
try {
|
|
35
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
36
50
|
return JSON.parse(content);
|
|
37
51
|
}
|
|
38
52
|
catch {
|
|
39
|
-
//
|
|
40
|
-
|
|
53
|
+
// Never silently replace an unparseable config: it may hold the user's
|
|
54
|
+
// other MCP servers, and "merging" into {} would discard them on write.
|
|
55
|
+
throw new Error(`Existing config at ${filePath} is not valid JSON. ` +
|
|
56
|
+
`Fix or remove it, then re-run the install.`);
|
|
41
57
|
}
|
|
42
58
|
}
|
|
43
59
|
async function writeJsonConfig(filePath, config) {
|
|
@@ -46,56 +62,145 @@ async function writeJsonConfig(filePath, config) {
|
|
|
46
62
|
await fs.mkdir(dir, { recursive: true });
|
|
47
63
|
await fs.writeFile(filePath, JSON.stringify(config, null, 2));
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
console.log("🔧 Installing @aibtc/mcp-server to Claude Code...\n");
|
|
53
|
-
const config = await readJsonConfig(claudeConfigPath);
|
|
54
|
-
if (!config.mcpServers) {
|
|
55
|
-
config.mcpServers = {};
|
|
56
|
-
}
|
|
57
|
-
config.mcpServers["aibtc"] = {
|
|
65
|
+
// Standard MCP server entry shared by every JSON-based client config.
|
|
66
|
+
function serverEntry(network) {
|
|
67
|
+
return {
|
|
58
68
|
command: "npx",
|
|
59
|
-
args: ["
|
|
60
|
-
env: {
|
|
61
|
-
NETWORK: network,
|
|
62
|
-
},
|
|
69
|
+
args: ["-y", SERVER_NPM],
|
|
70
|
+
env: { NETWORK: network },
|
|
63
71
|
};
|
|
64
|
-
await writeJsonConfig(claudeConfigPath, config);
|
|
65
|
-
console.log("✅ Successfully installed!\n");
|
|
66
|
-
console.log(` Config: ${claudeConfigPath}`);
|
|
67
|
-
console.log(` Network: ${network}`);
|
|
68
|
-
console.log("\n📋 Next steps:");
|
|
69
|
-
console.log(" 1. Restart Claude Code (close and reopen terminal)");
|
|
70
|
-
console.log(" 2. Ask Claude: \"What's your wallet address?\"");
|
|
71
|
-
console.log(" 3. Claude will guide you through wallet setup\n");
|
|
72
|
-
if (network === "testnet") {
|
|
73
|
-
console.log("💡 Tip: Get testnet STX at https://explorer.hiro.so/sandbox/faucet?chain=testnet\n");
|
|
74
|
-
}
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
console.log("🔧 Installing @aibtc/mcp-server to Claude Desktop...\n");
|
|
73
|
+
// Most clients (Claude Code, Claude Desktop, Cursor, Windsurf, Gemini CLI) use
|
|
74
|
+
// the same `{ "mcpServers": { "aibtc": {...} } }` JSON shape.
|
|
75
|
+
async function writeMcpServersJson(configPath, network) {
|
|
80
76
|
const config = await readJsonConfig(configPath);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
const servers = (config.mcpServers ??= {});
|
|
78
|
+
servers["aibtc"] = serverEntry(network);
|
|
79
|
+
await writeJsonConfig(configPath, config);
|
|
80
|
+
}
|
|
81
|
+
// VS Code (.vscode/mcp.json) uses a `servers` key and a typed stdio entry.
|
|
82
|
+
async function writeVsCodeJson(configPath, network) {
|
|
83
|
+
const config = await readJsonConfig(configPath);
|
|
84
|
+
const servers = (config.servers ??= {});
|
|
85
|
+
servers["aibtc"] = {
|
|
86
|
+
type: "stdio",
|
|
85
87
|
command: "npx",
|
|
86
|
-
args: ["-y",
|
|
87
|
-
env: {
|
|
88
|
-
NETWORK: network,
|
|
89
|
-
},
|
|
88
|
+
args: ["-y", SERVER_NPM],
|
|
89
|
+
env: { NETWORK: network },
|
|
90
90
|
};
|
|
91
91
|
await writeJsonConfig(configPath, config);
|
|
92
|
+
}
|
|
93
|
+
// Codex uses TOML, not JSON. Rewrite only the `[mcp_servers.aibtc]` section so
|
|
94
|
+
// the rest of the user's config.toml (including comments) is preserved.
|
|
95
|
+
function stripCodexSection(content) {
|
|
96
|
+
const out = [];
|
|
97
|
+
let skipping = false;
|
|
98
|
+
for (const line of content.split("\n")) {
|
|
99
|
+
const header = line.trim();
|
|
100
|
+
if (header.startsWith("[")) {
|
|
101
|
+
skipping = header === "[mcp_servers.aibtc]" || header.startsWith("[mcp_servers.aibtc.");
|
|
102
|
+
}
|
|
103
|
+
if (!skipping)
|
|
104
|
+
out.push(line);
|
|
105
|
+
}
|
|
106
|
+
return out.join("\n");
|
|
107
|
+
}
|
|
108
|
+
async function writeCodexToml(configPath, network) {
|
|
109
|
+
let existing = "";
|
|
110
|
+
try {
|
|
111
|
+
existing = await fs.readFile(configPath, "utf8");
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code !== "ENOENT") {
|
|
115
|
+
throw error; // Unreadable existing config must not be clobbered
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const preserved = stripCodexSection(existing).replace(/\s+$/, "");
|
|
119
|
+
const block = [
|
|
120
|
+
"[mcp_servers.aibtc]",
|
|
121
|
+
'command = "npx"',
|
|
122
|
+
`args = ["-y", "${SERVER_NPM}"]`,
|
|
123
|
+
"",
|
|
124
|
+
"[mcp_servers.aibtc.env]",
|
|
125
|
+
`NETWORK = "${network}"`,
|
|
126
|
+
].join("\n");
|
|
127
|
+
const content = (preserved ? `${preserved}\n\n` : "") + block + "\n";
|
|
128
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
129
|
+
await fs.writeFile(configPath, content);
|
|
130
|
+
}
|
|
131
|
+
const INSTALL_TARGETS = [
|
|
132
|
+
{
|
|
133
|
+
flag: "--desktop",
|
|
134
|
+
label: "Claude Desktop",
|
|
135
|
+
configPath: getClaudeDesktopConfigPath,
|
|
136
|
+
write: writeMcpServersJson,
|
|
137
|
+
restart: "Restart Claude Desktop (quit and reopen the app)",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
flag: "--cursor",
|
|
141
|
+
label: "Cursor",
|
|
142
|
+
configPath: () => path.join(os.homedir(), ".cursor", "mcp.json"),
|
|
143
|
+
write: writeMcpServersJson,
|
|
144
|
+
restart: "Restart Cursor",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
flag: "--windsurf",
|
|
148
|
+
label: "Windsurf",
|
|
149
|
+
configPath: () => path.join(os.homedir(), ".codeium", "windsurf", "mcp_config.json"),
|
|
150
|
+
write: writeMcpServersJson,
|
|
151
|
+
restart: "Restart Windsurf",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
flag: "--gemini",
|
|
155
|
+
label: "Gemini CLI",
|
|
156
|
+
configPath: () => path.join(os.homedir(), ".gemini", "settings.json"),
|
|
157
|
+
write: writeMcpServersJson,
|
|
158
|
+
restart: "Restart the Gemini CLI",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
flag: "--vscode",
|
|
162
|
+
label: "VS Code",
|
|
163
|
+
configPath: () => path.join(process.cwd(), ".vscode", "mcp.json"),
|
|
164
|
+
write: writeVsCodeJson,
|
|
165
|
+
restart: "Reload VS Code, then start the MCP server from the .vscode/mcp.json gutter",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
flag: "--codex",
|
|
169
|
+
label: "Codex CLI",
|
|
170
|
+
configPath: () => path.join(os.homedir(), ".codex", "config.toml"),
|
|
171
|
+
write: writeCodexToml,
|
|
172
|
+
restart: "Restart the Codex CLI",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
flag: null,
|
|
176
|
+
label: "Claude Code",
|
|
177
|
+
configPath: () => path.join(os.homedir(), ".claude.json"),
|
|
178
|
+
write: writeMcpServersJson,
|
|
179
|
+
restart: "Restart Claude Code (close and reopen terminal)",
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
function resolveInstallTarget() {
|
|
183
|
+
const matched = INSTALL_TARGETS.filter((t) => t.flag && process.argv.includes(t.flag));
|
|
184
|
+
if (matched.length > 1) {
|
|
185
|
+
console.warn(`⚠️ Multiple client flags passed (${matched.map((t) => t.flag).join(", ")}); using ${matched[0].label}.`);
|
|
186
|
+
}
|
|
187
|
+
// Default to the entry explicitly marked as default (flag === null) — Claude Code.
|
|
188
|
+
return matched[0] ?? INSTALL_TARGETS.find((t) => t.flag === null);
|
|
189
|
+
}
|
|
190
|
+
async function runInstall() {
|
|
191
|
+
const network = process.argv.includes("--testnet") ? "testnet" : "mainnet";
|
|
192
|
+
const target = resolveInstallTarget();
|
|
193
|
+
const configPath = target.configPath();
|
|
194
|
+
console.log(`🔧 Installing @aibtc/mcp-server to ${target.label}...\n`);
|
|
195
|
+
await target.write(configPath, network);
|
|
92
196
|
console.log("✅ Successfully installed!\n");
|
|
93
|
-
console.log(`
|
|
197
|
+
console.log(` Client: ${target.label}`);
|
|
198
|
+
console.log(` Config: ${configPath}`);
|
|
94
199
|
console.log(` Network: ${network}`);
|
|
95
200
|
console.log("\n📋 Next steps:");
|
|
96
|
-
console.log(
|
|
97
|
-
console.log(" 2. Ask
|
|
98
|
-
console.log(" 3.
|
|
201
|
+
console.log(` 1. ${target.restart}`);
|
|
202
|
+
console.log(" 2. Ask the agent: \"What's your wallet address?\"");
|
|
203
|
+
console.log(" 3. The agent will guide you through wallet setup\n");
|
|
99
204
|
if (network === "testnet") {
|
|
100
205
|
console.log("💡 Tip: Get testnet STX at https://explorer.hiro.so/sandbox/faucet?chain=testnet\n");
|
|
101
206
|
}
|
|
@@ -126,9 +231,7 @@ if (process.argv[2] === "yield-hunter") {
|
|
|
126
231
|
}
|
|
127
232
|
// Check for --install flag
|
|
128
233
|
else if (process.argv.includes("--install") || process.argv.includes("install")) {
|
|
129
|
-
|
|
130
|
-
const installFn = isDesktop ? installToClaudeDesktop : installToClaudeCode;
|
|
131
|
-
installFn()
|
|
234
|
+
runInstall()
|
|
132
235
|
.then(() => process.exit(0))
|
|
133
236
|
.catch((error) => {
|
|
134
237
|
console.error("❌ Installation failed:", redactSensitive(error.message));
|
|
@@ -39,6 +39,7 @@ async function encrypt(data, password) {
|
|
|
39
39
|
const iv = crypto.randomBytes(12);
|
|
40
40
|
const key = await deriveKey(password, salt);
|
|
41
41
|
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
42
|
+
key.fill(0); // cipher holds its own copy
|
|
42
43
|
const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
|
|
43
44
|
const authTag = cipher.getAuthTag();
|
|
44
45
|
return {
|
|
@@ -53,6 +54,7 @@ async function encrypt(data, password) {
|
|
|
53
54
|
async function decrypt(encrypted, password) {
|
|
54
55
|
const key = await deriveKey(password, Buffer.from(encrypted.salt, "base64"));
|
|
55
56
|
const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(encrypted.iv, "base64"));
|
|
57
|
+
key.fill(0); // decipher holds its own copy
|
|
56
58
|
decipher.setAuthTag(Buffer.from(encrypted.authTag, "base64"));
|
|
57
59
|
const decrypted = Buffer.concat([
|
|
58
60
|
decipher.update(Buffer.from(encrypted.ciphertext, "base64")),
|
|
@@ -67,12 +69,15 @@ function emptyStore() {
|
|
|
67
69
|
async function save() {
|
|
68
70
|
if (!_store || !_password)
|
|
69
71
|
throw new Error("Store not unlocked");
|
|
70
|
-
await fs.mkdir(getStoreDir(), { recursive: true });
|
|
72
|
+
await fs.mkdir(getStoreDir(), { recursive: true, mode: 0o700 });
|
|
71
73
|
const file = {
|
|
72
74
|
version: VERSION,
|
|
73
75
|
encrypted: await encrypt(JSON.stringify(_store), _password),
|
|
74
76
|
};
|
|
75
|
-
|
|
77
|
+
// Owner-only perms + atomic rename, same pattern as the wallet keystores
|
|
78
|
+
const tempFile = getStoreFile() + ".tmp";
|
|
79
|
+
await fs.writeFile(tempFile, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
80
|
+
await fs.rename(tempFile, getStoreFile());
|
|
76
81
|
}
|
|
77
82
|
async function load(password) {
|
|
78
83
|
const raw = await fs.readFile(getStoreFile(), "utf-8");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { uintCV, contractPrincipalCV, cvToJSON, hexToCV, PostConditionMode, Pc, principalCV, noneCV, someCV, listCV, bufferCV, } from "@stacks/transactions";
|
|
2
2
|
import { AlexSDK, Currency } from "alex-sdk";
|
|
3
|
-
import { getHiroApi } from "./hiro-api.js";
|
|
3
|
+
import { getHiroApi, isNotFoundError } from "./hiro-api.js";
|
|
4
4
|
import { getAlexContracts, getZestContracts, parseContractId, ZEST_ASSETS, ZEST_V2_MARKET, ZEST_V2_MARKET_VAULT, } from "../config/index.js";
|
|
5
5
|
import { callContract } from "../transactions/builder.js";
|
|
6
6
|
// ============================================================================
|
|
@@ -129,8 +129,10 @@ export class AlexDexService {
|
|
|
129
129
|
}
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
|
-
catch {
|
|
133
|
-
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (isNotFoundError(error))
|
|
134
|
+
return null;
|
|
135
|
+
throw error;
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
/**
|
|
@@ -360,8 +362,10 @@ export class ZestProtocolService {
|
|
|
360
362
|
healthFactor: position["health-factor"]?.value,
|
|
361
363
|
};
|
|
362
364
|
}
|
|
363
|
-
catch {
|
|
364
|
-
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (isNotFoundError(error))
|
|
367
|
+
return null;
|
|
368
|
+
throw error;
|
|
365
369
|
}
|
|
366
370
|
}
|
|
367
371
|
/**
|
|
@@ -377,8 +381,10 @@ export class ZestProtocolService {
|
|
|
377
381
|
}
|
|
378
382
|
return cvToJSON(hexToCV(result.result));
|
|
379
383
|
}
|
|
380
|
-
catch {
|
|
381
|
-
|
|
384
|
+
catch (error) {
|
|
385
|
+
if (isNotFoundError(error))
|
|
386
|
+
return null;
|
|
387
|
+
throw error;
|
|
382
388
|
}
|
|
383
389
|
}
|
|
384
390
|
/**
|
|
@@ -411,8 +417,12 @@ export class ZestProtocolService {
|
|
|
411
417
|
}
|
|
412
418
|
}
|
|
413
419
|
}
|
|
414
|
-
catch {
|
|
415
|
-
//
|
|
420
|
+
catch (error) {
|
|
421
|
+
// Only fall back to the shares lower-bound estimate when the vault
|
|
422
|
+
// function is missing — network/server failures must surface, or the
|
|
423
|
+
// post-conditions built from this value would be silently wrong.
|
|
424
|
+
if (!isNotFoundError(error))
|
|
425
|
+
throw error;
|
|
416
426
|
}
|
|
417
427
|
return shares;
|
|
418
428
|
}
|
|
@@ -431,8 +441,10 @@ export class ZestProtocolService {
|
|
|
431
441
|
}
|
|
432
442
|
}
|
|
433
443
|
}
|
|
434
|
-
catch {
|
|
435
|
-
//
|
|
444
|
+
catch (error) {
|
|
445
|
+
// Same rationale as getUnderlyingFromShares: estimate only on 404.
|
|
446
|
+
if (!isNotFoundError(error))
|
|
447
|
+
throw error;
|
|
436
448
|
}
|
|
437
449
|
return amount;
|
|
438
450
|
}
|
|
@@ -7,6 +7,16 @@ export declare class HiroApiRateLimitError extends Error {
|
|
|
7
7
|
readonly retryAfterSeconds: number;
|
|
8
8
|
constructor(message: string, retryAfterSeconds: number);
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* HTTP error from the Hiro / BNS V2 APIs, carrying the status code so
|
|
12
|
+
* callers can treat 404 as "not found" without swallowing other failures.
|
|
13
|
+
*/
|
|
14
|
+
export declare class HiroApiError extends Error {
|
|
15
|
+
readonly status: number;
|
|
16
|
+
constructor(message: string, status: number);
|
|
17
|
+
}
|
|
18
|
+
/** True when the error is an expected "resource does not exist" response. */
|
|
19
|
+
export declare function isNotFoundError(error: unknown): boolean;
|
|
10
20
|
export interface AccountInfo {
|
|
11
21
|
balance: string;
|
|
12
22
|
locked: string;
|
|
@@ -16,6 +16,22 @@ export class HiroApiRateLimitError extends Error {
|
|
|
16
16
|
this.name = "HiroApiRateLimitError";
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* HTTP error from the Hiro / BNS V2 APIs, carrying the status code so
|
|
21
|
+
* callers can treat 404 as "not found" without swallowing other failures.
|
|
22
|
+
*/
|
|
23
|
+
export class HiroApiError extends Error {
|
|
24
|
+
status;
|
|
25
|
+
constructor(message, status) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.status = status;
|
|
28
|
+
this.name = "HiroApiError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** True when the error is an expected "resource does not exist" response. */
|
|
32
|
+
export function isNotFoundError(error) {
|
|
33
|
+
return error instanceof HiroApiError && error.status === 404;
|
|
34
|
+
}
|
|
19
35
|
// ============================================================================
|
|
20
36
|
// Hiro API Service
|
|
21
37
|
// ============================================================================
|
|
@@ -51,7 +67,7 @@ export class HiroApiService {
|
|
|
51
67
|
throw new HiroApiRateLimitError(`Hiro API rate limit exceeded. Retry after ${retryAfterSeconds}s`, retryAfterSeconds);
|
|
52
68
|
}
|
|
53
69
|
const errorText = await response.text();
|
|
54
|
-
throw new
|
|
70
|
+
throw new HiroApiError(`Hiro API error (${response.status}): ${errorText}`, response.status);
|
|
55
71
|
}
|
|
56
72
|
return response.json();
|
|
57
73
|
}
|
|
@@ -123,8 +139,10 @@ export class HiroApiService {
|
|
|
123
139
|
try {
|
|
124
140
|
return await this.fetch(`/metadata/v1/ft/${contractId}`);
|
|
125
141
|
}
|
|
126
|
-
catch {
|
|
127
|
-
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (isNotFoundError(error))
|
|
144
|
+
return null;
|
|
145
|
+
throw error;
|
|
128
146
|
}
|
|
129
147
|
}
|
|
130
148
|
async getTokenHolders(contractId, options) {
|
|
@@ -329,8 +347,10 @@ export class HiroApiService {
|
|
|
329
347
|
const info = await this.getBnsNameInfo(name);
|
|
330
348
|
return info.address;
|
|
331
349
|
}
|
|
332
|
-
catch {
|
|
333
|
-
|
|
350
|
+
catch (error) {
|
|
351
|
+
if (isNotFoundError(error))
|
|
352
|
+
return null;
|
|
353
|
+
throw error;
|
|
334
354
|
}
|
|
335
355
|
}
|
|
336
356
|
// ==========================================================================
|
|
@@ -385,7 +405,7 @@ export class BnsV2ApiService {
|
|
|
385
405
|
});
|
|
386
406
|
if (!response.ok) {
|
|
387
407
|
const errorText = await response.text();
|
|
388
|
-
throw new
|
|
408
|
+
throw new HiroApiError(`BNS V2 API error (${response.status}): ${errorText}`, response.status);
|
|
389
409
|
}
|
|
390
410
|
return response.json();
|
|
391
411
|
}
|
|
@@ -431,8 +451,10 @@ export class BnsV2ApiService {
|
|
|
431
451
|
}
|
|
432
452
|
return null;
|
|
433
453
|
}
|
|
434
|
-
catch {
|
|
435
|
-
|
|
454
|
+
catch (error) {
|
|
455
|
+
if (isNotFoundError(error))
|
|
456
|
+
return null;
|
|
457
|
+
throw error;
|
|
436
458
|
}
|
|
437
459
|
}
|
|
438
460
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uintCV, principalCV } from "@stacks/transactions";
|
|
2
|
-
import { getHiroApi } from "./hiro-api.js";
|
|
2
|
+
import { getHiroApi, HiroApiError } from "./hiro-api.js";
|
|
3
3
|
import { parseContractId } from "../config/index.js";
|
|
4
4
|
import { callContract } from "../transactions/builder.js";
|
|
5
5
|
import { createNftSendPostCondition } from "../transactions/post-conditions.js";
|
|
@@ -51,8 +51,13 @@ export class NftService {
|
|
|
51
51
|
}
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
|
-
catch {
|
|
55
|
-
|
|
54
|
+
catch (error) {
|
|
55
|
+
// 4xx means the contract doesn't implement get-owner (or rejects the
|
|
56
|
+
// call) — that is "no owner". Network/server failures must surface.
|
|
57
|
+
if (error instanceof HiroApiError && error.status < 500) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
@@ -75,8 +80,12 @@ export class NftService {
|
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
|
-
catch {
|
|
79
|
-
//
|
|
83
|
+
catch (error) {
|
|
84
|
+
// 4xx means the contract doesn't implement get-last-token-id —
|
|
85
|
+
// supply is simply unknown. Network/server failures must surface.
|
|
86
|
+
if (!(error instanceof HiroApiError && error.status < 500)) {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
80
89
|
}
|
|
81
90
|
return {
|
|
82
91
|
contractId,
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP error from the Pillar API, carrying the status code so callers can
|
|
3
|
+
* treat 4xx ("no such wallet / no position") differently from infra failures.
|
|
4
|
+
*/
|
|
5
|
+
export declare class PillarApiError extends Error {
|
|
6
|
+
readonly status: number;
|
|
7
|
+
constructor(message: string, status: number);
|
|
8
|
+
}
|
|
1
9
|
declare class PillarApiService {
|
|
2
10
|
private baseUrl;
|
|
3
11
|
private apiKey;
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { PILLAR_API_URL, PILLAR_API_KEY } from "../config/pillar.js";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP error from the Pillar API, carrying the status code so callers can
|
|
4
|
+
* treat 4xx ("no such wallet / no position") differently from infra failures.
|
|
5
|
+
*/
|
|
6
|
+
export class PillarApiError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(message, status) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.name = "PillarApiError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
2
14
|
class PillarApiService {
|
|
3
15
|
baseUrl;
|
|
4
16
|
apiKey;
|
|
@@ -32,7 +44,7 @@ class PillarApiService {
|
|
|
32
44
|
});
|
|
33
45
|
if (!response.ok) {
|
|
34
46
|
const errorText = await response.text();
|
|
35
|
-
throw new
|
|
47
|
+
throw new PillarApiError(`Pillar API error (${response.status}): ${errorText}`, response.status);
|
|
36
48
|
}
|
|
37
49
|
return response.json();
|
|
38
50
|
}
|