@1ly/mcp-server 0.1.3 → 0.1.5
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 +61 -29
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +22 -1
- package/dist/budget.js.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +81 -15
- package/dist/config.js.map +1 -1
- package/dist/http.d.ts +2 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +27 -1
- package/dist/http.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/rate-limit.d.ts +24 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +50 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/security-log.d.ts +55 -0
- package/dist/security-log.d.ts.map +1 -0
- package/dist/security-log.js +52 -0
- package/dist/security-log.js.map +1 -0
- package/dist/tools/call.d.ts.map +1 -1
- package/dist/tools/call.js +59 -20
- package/dist/tools/call.js.map +1 -1
- package/dist/tools/update-avatar.d.ts.map +1 -1
- package/dist/tools/update-avatar.js +30 -4
- package/dist/tools/update-avatar.js.map +1 -1
- package/dist/wallet/agentic.d.ts +19 -0
- package/dist/wallet/agentic.d.ts.map +1 -0
- package/dist/wallet/agentic.js +77 -0
- package/dist/wallet/agentic.js.map +1 -0
- package/dist/wallet/evm.d.ts.map +1 -1
- package/dist/wallet/evm.js +46 -4
- package/dist/wallet/evm.js.map +1 -1
- package/dist/wallet/solana.d.ts.map +1 -1
- package/dist/wallet/solana.js +43 -5
- package/dist/wallet/solana.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -6,47 +6,68 @@ MCP server for [1ly.store](https://1ly.store) — Enable AI agents to discover,
|
|
|
6
6
|
|
|
7
7
|
This MCP server gives AI agents the ability to:
|
|
8
8
|
|
|
9
|
-
- **Buy** — Search, discover, and pay for APIs
|
|
9
|
+
- **Buy** — Search, discover, and pay for APIs/resources with automatic crypto payments (x402 protocol)
|
|
10
10
|
- **Sell** — Create a store, list paid API endpoints or resources, and accept payments
|
|
11
11
|
|
|
12
12
|
**Supported Networks:** Solana (mainnet), Base (mainnet)
|
|
13
13
|
**Payment Currency:** USDC
|
|
14
14
|
|
|
15
15
|
## What is this?
|
|
16
|
-
This MCP server enables AI agents (Claude, GPT, Cursor,
|
|
16
|
+
This MCP server enables AI agents (Claude, GPT, Cursor, and more) to:
|
|
17
17
|
|
|
18
|
-
Create store on 1ly.store
|
|
19
|
-
Accept payments for your
|
|
20
|
-
Create paid links that any x402‑compatible agent can call and pay automatically
|
|
21
|
-
|
|
22
|
-
Search for APIs and services on 1ly.store
|
|
23
|
-
Get details about pricing, reviews, and usage
|
|
24
|
-
Call paid APIs with automatic crypto payments (x402 protocol)
|
|
25
|
-
Leave reviews after purchases
|
|
18
|
+
- Create a store on 1ly.store
|
|
19
|
+
- Accept payments for your APIs/resources using 1ly as the payment layer
|
|
20
|
+
- Create paid links that any x402‑compatible agent can call and pay automatically
|
|
21
|
+
- List paid links on the 1ly marketplace for instant agent discovery
|
|
22
|
+
- Search for APIs and services on 1ly.store
|
|
23
|
+
- Get details about pricing, reviews, and usage
|
|
24
|
+
- Call paid APIs with automatic crypto payments (x402 protocol) securely
|
|
25
|
+
- Leave reviews after purchases (optional but recommended)
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
29
|
## Quick Start
|
|
30
30
|
|
|
31
|
+
**Wallet path rule:** wallet files must be located in your home directory (recommended) or `/tmp`. Paths outside those locations are rejected for security. `~/` is supported and expands to your home directory.
|
|
32
|
+
|
|
31
33
|
### 1. Install and Run
|
|
32
34
|
|
|
35
|
+
#### Option A: Raw Wallet (default)
|
|
36
|
+
|
|
33
37
|
```bash
|
|
34
38
|
# Solana wallet
|
|
35
|
-
ONELY_WALLET_SOLANA_KEY="/
|
|
39
|
+
ONELY_WALLET_SOLANA_KEY="~/.1ly/wallets/solana.json" npx @1ly/mcp-server
|
|
36
40
|
|
|
37
41
|
# OR Base/EVM wallet
|
|
38
|
-
ONELY_WALLET_EVM_KEY="/
|
|
42
|
+
ONELY_WALLET_EVM_KEY="~/.1ly/wallets/evm.key" npx @1ly/mcp-server
|
|
39
43
|
|
|
40
44
|
# OR both wallets
|
|
41
|
-
ONELY_WALLET_SOLANA_KEY="/
|
|
42
|
-
ONELY_WALLET_EVM_KEY="/
|
|
45
|
+
ONELY_WALLET_SOLANA_KEY="~/.1ly/wallets/solana.json" \
|
|
46
|
+
ONELY_WALLET_EVM_KEY="~/.1ly/wallets/evm.key" \
|
|
43
47
|
npx @1ly/mcp-server
|
|
44
48
|
```
|
|
45
49
|
|
|
50
|
+
#### Option B: Coinbase Agentic Wallet (Base only)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ONELY_WALLET_PROVIDER="coinbase" npx @1ly/mcp-server
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> When using Agentic Wallet, you **do not** pass raw private keys.
|
|
57
|
+
> Make sure the Coinbase Agentic Wallet app is running and authenticated.
|
|
58
|
+
|
|
59
|
+
If you haven't installed Agentic Wallet yet:
|
|
60
|
+
- Follow Coinbase Agentic Wallet docs: `https://docs.cdp.coinbase.com/agentic-wallet/welcome`
|
|
61
|
+
- Quick install:
|
|
62
|
+
```bash
|
|
63
|
+
npx skills add coinbase/agentic-wallet-skills
|
|
64
|
+
npx awal show
|
|
65
|
+
```
|
|
66
|
+
|
|
46
67
|
### 2. Verify Setup
|
|
47
68
|
|
|
48
69
|
```bash
|
|
49
|
-
ONELY_WALLET_SOLANA_KEY="/
|
|
70
|
+
ONELY_WALLET_SOLANA_KEY="~/.1ly/wallets/solana.json" npx @1ly/mcp-server --self-test
|
|
50
71
|
```
|
|
51
72
|
|
|
52
73
|
---
|
|
@@ -57,16 +78,18 @@ ONELY_WALLET_SOLANA_KEY="/path/to/solana-wallet.json" npx @1ly/mcp-server --self
|
|
|
57
78
|
|
|
58
79
|
| Variable | Required | Description |
|
|
59
80
|
|----------|----------|-------------|
|
|
60
|
-
| `ONELY_WALLET_SOLANA_KEY` |
|
|
61
|
-
| `ONELY_WALLET_EVM_KEY` |
|
|
81
|
+
| `ONELY_WALLET_SOLANA_KEY` | No | Path to Solana keypair JSON file, or inline JSON array |
|
|
82
|
+
| `ONELY_WALLET_EVM_KEY` | No | Path to EVM private key file, or inline hex key (with or without `0x`) |
|
|
62
83
|
| `ONELY_API_KEY` | No | API key for seller tools. Auto-loaded after `1ly_create_store` |
|
|
63
84
|
| `ONELY_BUDGET_PER_CALL` | No | Max USD per API call (default: `1.00`) |
|
|
64
85
|
| `ONELY_BUDGET_DAILY` | No | Daily USD spending limit (default: `50.00`) |
|
|
65
86
|
| `ONELY_BUDGET_STATE_FILE` | No | Path to local budget state file (default: `~/.1ly-mcp-budget.json`) |
|
|
66
87
|
| `ONELY_NETWORK` | No | Preferred network: `solana` or `base` (default: `solana`) |
|
|
67
88
|
| `ONELY_API_BASE` | No | API base URL (default: `https://1ly.store`) |
|
|
89
|
+
| `ONELY_WALLET_PROVIDER` | No | `raw` (default) or `coinbase` (Agentic Wallet, Base-only) |
|
|
68
90
|
|
|
69
|
-
*
|
|
91
|
+
*A wallet is required only for **paid** calls. For free search/details you can run without a wallet.*
|
|
92
|
+
Use **one** of: `ONELY_WALLET_SOLANA_KEY`, `ONELY_WALLET_EVM_KEY`, or `ONELY_WALLET_PROVIDER=coinbase`.
|
|
70
93
|
|
|
71
94
|
### Claude Desktop Configuration
|
|
72
95
|
|
|
@@ -79,7 +102,7 @@ Add to `claude_desktop_config.json`:
|
|
|
79
102
|
"command": "npx",
|
|
80
103
|
"args": ["@1ly/mcp-server"],
|
|
81
104
|
"env": {
|
|
82
|
-
"ONELY_WALLET_SOLANA_KEY": "/
|
|
105
|
+
"ONELY_WALLET_SOLANA_KEY": "~/.1ly/wallets/solana.json",
|
|
83
106
|
"ONELY_BUDGET_PER_CALL": "1.00",
|
|
84
107
|
"ONELY_BUDGET_DAILY": "50.00"
|
|
85
108
|
}
|
|
@@ -98,7 +121,7 @@ Add to `claude_desktop_config.json`:
|
|
|
98
121
|
"command": "npx",
|
|
99
122
|
"args": ["@1ly/mcp-server"],
|
|
100
123
|
"env": {
|
|
101
|
-
"ONELY_WALLET_EVM_KEY": "/
|
|
124
|
+
"ONELY_WALLET_EVM_KEY": "~/.1ly/wallets/evm.key",
|
|
102
125
|
"ONELY_BUDGET_PER_CALL": "1.00",
|
|
103
126
|
"ONELY_BUDGET_DAILY": "50.00"
|
|
104
127
|
}
|
|
@@ -118,8 +141,8 @@ Add to `claude_desktop_config.json`:
|
|
|
118
141
|
"command": "npx",
|
|
119
142
|
"args": ["@1ly/mcp-server"],
|
|
120
143
|
"env": {
|
|
121
|
-
"ONELY_WALLET_SOLANA_KEY": "/
|
|
122
|
-
"ONELY_WALLET_EVM_KEY": "/
|
|
144
|
+
"ONELY_WALLET_SOLANA_KEY": "~/.1ly/wallets/solana.json",
|
|
145
|
+
"ONELY_WALLET_EVM_KEY": "~/.1ly/wallets/evm.key",
|
|
123
146
|
"ONELY_BUDGET_PER_CALL": "1.00",
|
|
124
147
|
"ONELY_BUDGET_DAILY": "50.00"
|
|
125
148
|
}
|
|
@@ -133,9 +156,9 @@ Add to `claude_desktop_config.json`:
|
|
|
133
156
|
|
|
134
157
|
## Tools Reference
|
|
135
158
|
|
|
136
|
-
### Buyer Tools
|
|
159
|
+
### Buyer Tools
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
Wallet **required only for paid calls** (`1ly_call`). Search/details work without a wallet.
|
|
139
162
|
|
|
140
163
|
#### `1ly_search`
|
|
141
164
|
|
|
@@ -260,6 +283,11 @@ Call a paid API with automatic crypto payment.
|
|
|
260
283
|
> **Note:** The `_1ly` object contains tokens needed for `1ly_review`. Save these if you want to leave a review.
|
|
261
284
|
> For free APIs, `_1ly` may be `{ "note": "No payment required (free API)" }`.
|
|
262
285
|
|
|
286
|
+
**Agentic Wallet (Base only):**
|
|
287
|
+
- Set `ONELY_WALLET_PROVIDER=coinbase`.
|
|
288
|
+
- Ensure Coinbase Agentic Wallet is installed, running, and authenticated.
|
|
289
|
+
- Local private keys are not required.
|
|
290
|
+
|
|
263
291
|
---
|
|
264
292
|
|
|
265
293
|
#### `1ly_review`
|
|
@@ -684,7 +712,7 @@ Update store avatar using a public URL or base64 image.
|
|
|
684
712
|
| Parameter | Type | Required | Description |
|
|
685
713
|
|-----------|------|----------|-------------|
|
|
686
714
|
| `avatarUrl` | string | No | Public image URL |
|
|
687
|
-
| `imageBase64` | string | No | Base64-encoded image
|
|
715
|
+
| `imageBase64` | string | No | Base64-encoded image (max 5MB decoded) |
|
|
688
716
|
| `mimeType` | string | No | `image/png`, `image/jpeg`, `image/webp`, `image/gif` |
|
|
689
717
|
| `filename` | string | No | Optional filename |
|
|
690
718
|
|
|
@@ -781,16 +809,18 @@ List recent withdrawals.
|
|
|
781
809
|
|
|
782
810
|
Create a new keypair:
|
|
783
811
|
```bash
|
|
784
|
-
|
|
812
|
+
mkdir -p ~/.1ly/wallets
|
|
813
|
+
solana-keygen new -o ~/.1ly/wallets/solana.json
|
|
785
814
|
```
|
|
786
815
|
|
|
787
816
|
Fund with USDC on Solana mainnet.
|
|
788
817
|
|
|
789
818
|
### EVM (Base)
|
|
790
819
|
|
|
791
|
-
Export your private key
|
|
820
|
+
Export your private key or Create a new keypair:
|
|
792
821
|
```bash
|
|
793
|
-
|
|
822
|
+
mkdir -p ~/.1ly/wallets
|
|
823
|
+
echo "0xYOUR_PRIVATE_KEY" > ~/.1ly/wallets/evm.key
|
|
794
824
|
```
|
|
795
825
|
|
|
796
826
|
Fund with USDC on Base mainnet.
|
|
@@ -821,7 +851,9 @@ All responses follow this structure:
|
|
|
821
851
|
|
|
822
852
|
| Error | Cause | Solution |
|
|
823
853
|
|-------|-------|----------|
|
|
824
|
-
| `Missing wallet config` | No wallet env var set | Set `ONELY_WALLET_SOLANA_KEY` or `
|
|
854
|
+
| `Missing wallet config` | No wallet env var set | Set `ONELY_WALLET_SOLANA_KEY`, `ONELY_WALLET_EVM_KEY`, or `ONELY_WALLET_PROVIDER=coinbase` |
|
|
855
|
+
| `Agentic Wallet not running` | Wallet app not running or not authenticated | Open the Agentic Wallet app and sign in (`npx awal show`) |
|
|
856
|
+
| `Agentic Wallet Base-only` | API requires Solana payment | Use raw Solana wallet or a Base-compatible endpoint |
|
|
825
857
|
| `Missing ONELY_API_KEY` | Seller tool called without API key | Run `1ly_create_store` first |
|
|
826
858
|
| `Price exceeds per-call budget` | API costs more than limit | Increase `ONELY_BUDGET_PER_CALL` |
|
|
827
859
|
| `Daily budget exceeded` | Spent more than daily limit | Wait until tomorrow or increase `ONELY_BUDGET_DAILY` |
|
package/dist/budget.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AA0D1C;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAuC/E"}
|
package/dist/budget.js
CHANGED
|
@@ -7,9 +7,13 @@ exports.checkAndRecordDailySpend = checkAndRecordDailySpend;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const security_log_js_1 = require("./security-log.js");
|
|
10
11
|
function getBudgetStatePath() {
|
|
11
12
|
const envPath = process.env.ONELY_BUDGET_STATE_FILE;
|
|
12
13
|
if (envPath && envPath.trim().length > 0) {
|
|
14
|
+
if (envPath.startsWith("~/")) {
|
|
15
|
+
return path_1.default.join(os_1.default.homedir(), envPath.slice(2));
|
|
16
|
+
}
|
|
13
17
|
return envPath;
|
|
14
18
|
}
|
|
15
19
|
return path_1.default.join(os_1.default.homedir(), ".1ly-mcp-budget.json");
|
|
@@ -37,7 +41,10 @@ function loadBudgetState() {
|
|
|
37
41
|
function saveBudgetState(state) {
|
|
38
42
|
const filePath = getBudgetStatePath();
|
|
39
43
|
try {
|
|
40
|
-
fs_1.default.writeFileSync(filePath, JSON.stringify(state, null, 2), {
|
|
44
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(state, null, 2), {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
mode: 0o600, // Owner read/write only (security: prevent other users from reading spending data)
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
49
|
catch {
|
|
43
50
|
// If we cannot persist, we still allow the call; fail-open is safer for UX
|
|
@@ -53,6 +60,14 @@ function checkAndRecordDailySpend(config, priceUsd) {
|
|
|
53
60
|
const current = state.date === today ? state.spentToday : 0;
|
|
54
61
|
const next = current + priceUsd;
|
|
55
62
|
if (next > config.budgets.daily) {
|
|
63
|
+
// Log budget violation for audit trail
|
|
64
|
+
(0, security_log_js_1.logSecurityEvent)("budget_exceeded", {
|
|
65
|
+
type: "daily",
|
|
66
|
+
currentSpent: current,
|
|
67
|
+
limit: config.budgets.daily,
|
|
68
|
+
attemptedAmount: priceUsd,
|
|
69
|
+
wouldTotal: next,
|
|
70
|
+
});
|
|
56
71
|
throw new Error(`Price $${priceUsd.toFixed(4)} would exceed daily budget of $${config.budgets.daily} (already spent: $${current.toFixed(4)})`);
|
|
57
72
|
}
|
|
58
73
|
const updated = {
|
|
@@ -60,5 +75,11 @@ function checkAndRecordDailySpend(config, priceUsd) {
|
|
|
60
75
|
spentToday: next,
|
|
61
76
|
};
|
|
62
77
|
saveBudgetState(updated);
|
|
78
|
+
// Log successful payment for audit trail
|
|
79
|
+
(0, security_log_js_1.logSecurityEvent)("api_call_paid", {
|
|
80
|
+
amount: priceUsd,
|
|
81
|
+
dailyTotal: next,
|
|
82
|
+
dailyLimit: config.budgets.daily,
|
|
83
|
+
});
|
|
63
84
|
}
|
|
64
85
|
//# sourceMappingURL=budget.js.map
|
package/dist/budget.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const ConfigSchema: z.ZodObject<{
|
|
3
|
-
apiBase: z.ZodDefault<z.ZodString
|
|
3
|
+
apiBase: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
|
|
4
4
|
wallet: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
5
5
|
type: z.ZodEnum<["solana", "evm"]>;
|
|
6
6
|
key: z.ZodString;
|
|
@@ -24,6 +24,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
24
24
|
daily?: number | undefined;
|
|
25
25
|
}>;
|
|
26
26
|
network: z.ZodDefault<z.ZodEnum<["solana", "base"]>>;
|
|
27
|
+
walletProvider: z.ZodDefault<z.ZodEnum<["raw", "coinbase"]>>;
|
|
27
28
|
apiKey: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
28
29
|
}, "strip", z.ZodTypeAny, {
|
|
29
30
|
apiBase: string;
|
|
@@ -32,6 +33,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
32
33
|
daily: number;
|
|
33
34
|
};
|
|
34
35
|
network: "solana" | "base";
|
|
36
|
+
walletProvider: "raw" | "coinbase";
|
|
35
37
|
wallet?: {
|
|
36
38
|
type: "solana" | "evm";
|
|
37
39
|
key: string;
|
|
@@ -52,6 +54,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
52
54
|
walletSolana?: string | null | undefined;
|
|
53
55
|
walletEvm?: string | null | undefined;
|
|
54
56
|
network?: "solana" | "base" | undefined;
|
|
57
|
+
walletProvider?: "raw" | "coinbase" | undefined;
|
|
55
58
|
apiKey?: string | null | undefined;
|
|
56
59
|
}>;
|
|
57
60
|
export type Config = z.infer<typeof ConfigSchema>;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wBAAgB,UAAU,IAAI,MAAM,CAqEnC;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK/D"}
|
package/dist/config.js
CHANGED
|
@@ -5,8 +5,32 @@ exports.loadConfig = loadConfig;
|
|
|
5
5
|
exports.loadConfigWithStoredKey = loadConfigWithStoredKey;
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
7
|
const keys_js_1 = require("./keys.js");
|
|
8
|
+
const security_log_js_1 = require("./security-log.js");
|
|
8
9
|
exports.ConfigSchema = zod_1.z.object({
|
|
9
|
-
apiBase: zod_1.z
|
|
10
|
+
apiBase: zod_1.z
|
|
11
|
+
.string()
|
|
12
|
+
.url()
|
|
13
|
+
.refine((url) => {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = new URL(url);
|
|
16
|
+
// Allow 1ly.store with HTTPS only
|
|
17
|
+
if (parsed.hostname === "1ly.store" || parsed.hostname === "www.1ly.store") {
|
|
18
|
+
return parsed.protocol === "https:";
|
|
19
|
+
}
|
|
20
|
+
// Allow localhost for local development (any port)
|
|
21
|
+
if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1") {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
// Block everything else
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}, {
|
|
31
|
+
message: "API base must be https://1ly.store or http://localhost:PORT for local development",
|
|
32
|
+
})
|
|
33
|
+
.default("https://1ly.store"),
|
|
10
34
|
wallet: zod_1.z
|
|
11
35
|
.object({
|
|
12
36
|
type: zod_1.z.enum(["solana", "evm"]),
|
|
@@ -17,10 +41,11 @@ exports.ConfigSchema = zod_1.z.object({
|
|
|
17
41
|
walletSolana: zod_1.z.string().optional().nullable(),
|
|
18
42
|
walletEvm: zod_1.z.string().optional().nullable(),
|
|
19
43
|
budgets: zod_1.z.object({
|
|
20
|
-
perCall: zod_1.z.number().positive().default(1.0),
|
|
21
|
-
daily: zod_1.z.number().positive().default(50.0),
|
|
44
|
+
perCall: zod_1.z.number().positive().finite().default(1.0),
|
|
45
|
+
daily: zod_1.z.number().positive().finite().default(50.0),
|
|
22
46
|
}),
|
|
23
47
|
network: zod_1.z.enum(["solana", "base"]).default("solana"),
|
|
48
|
+
walletProvider: zod_1.z.enum(["raw", "coinbase"]).default("raw"),
|
|
24
49
|
apiKey: zod_1.z.string().optional().nullable(),
|
|
25
50
|
});
|
|
26
51
|
function loadConfig() {
|
|
@@ -30,18 +55,59 @@ function loadConfig() {
|
|
|
30
55
|
const walletEvm = process.env.ONELY_WALLET_EVM_KEY || null;
|
|
31
56
|
const apiKeyEnv = process.env.ONELY_API_KEY || null;
|
|
32
57
|
const apiBase = process.env.ONELY_API_BASE || "https://1ly.store";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
const walletProvider = process.env.ONELY_WALLET_PROVIDER === "coinbase" ? "coinbase" : "raw";
|
|
59
|
+
// Parse and validate budget values to prevent NaN/negative bypasses
|
|
60
|
+
const perCallRaw = process.env.ONELY_BUDGET_PER_CALL || "1.0";
|
|
61
|
+
const dailyRaw = process.env.ONELY_BUDGET_DAILY || "50.0";
|
|
62
|
+
const perCall = parseFloat(perCallRaw);
|
|
63
|
+
const daily = parseFloat(dailyRaw);
|
|
64
|
+
// Validate parsed values are valid positive numbers
|
|
65
|
+
if (!Number.isFinite(perCall) || perCall <= 0) {
|
|
66
|
+
(0, security_log_js_1.logSecurityEvent)("invalid_budget_config", {
|
|
67
|
+
parameter: "ONELY_BUDGET_PER_CALL",
|
|
68
|
+
value: perCallRaw,
|
|
69
|
+
parsed: perCall,
|
|
70
|
+
});
|
|
71
|
+
throw new Error(`Invalid ONELY_BUDGET_PER_CALL: "${perCallRaw}" - must be a positive number`);
|
|
72
|
+
}
|
|
73
|
+
if (!Number.isFinite(daily) || daily <= 0) {
|
|
74
|
+
(0, security_log_js_1.logSecurityEvent)("invalid_budget_config", {
|
|
75
|
+
parameter: "ONELY_BUDGET_DAILY",
|
|
76
|
+
value: dailyRaw,
|
|
77
|
+
parsed: daily,
|
|
78
|
+
});
|
|
79
|
+
throw new Error(`Invalid ONELY_BUDGET_DAILY: "${dailyRaw}" - must be a positive number`);
|
|
80
|
+
}
|
|
81
|
+
let parsedConfig;
|
|
82
|
+
try {
|
|
83
|
+
parsedConfig = exports.ConfigSchema.parse({
|
|
84
|
+
apiBase,
|
|
85
|
+
wallet: walletType && walletKey ? { type: walletType, key: walletKey } : null,
|
|
86
|
+
walletSolana: walletSolana || (walletType === "solana" ? walletKey : null),
|
|
87
|
+
walletEvm: walletEvm || (walletType === "evm" ? walletKey : null),
|
|
88
|
+
budgets: {
|
|
89
|
+
perCall,
|
|
90
|
+
daily,
|
|
91
|
+
},
|
|
92
|
+
network: process.env.ONELY_NETWORK || "solana",
|
|
93
|
+
walletProvider,
|
|
94
|
+
apiKey: apiKeyEnv,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
// Log API base violations
|
|
99
|
+
if (err instanceof zod_1.z.ZodError) {
|
|
100
|
+
const apiBaseError = err.errors.find((e) => e.path[0] === "apiBase");
|
|
101
|
+
if (apiBaseError) {
|
|
102
|
+
(0, security_log_js_1.logSecurityEvent)("api_base_violation", {
|
|
103
|
+
attempted: apiBase,
|
|
104
|
+
error: apiBaseError.message,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
return parsedConfig;
|
|
45
111
|
}
|
|
46
112
|
async function loadConfigWithStoredKey() {
|
|
47
113
|
const config = loadConfig();
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class HttpError extends Error {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* fetchWithTimeout wraps the global fetch with:
|
|
17
|
+
* - rate limiting to prevent abuse
|
|
17
18
|
* - per-request timeout using AbortController
|
|
18
19
|
* - basic retry logic for network/timeout errors
|
|
19
20
|
*/
|
|
@@ -21,6 +22,7 @@ export declare function fetchWithTimeout(url: string, options?: FetchOptions): P
|
|
|
21
22
|
/**
|
|
22
23
|
* Helper to throw a rich HttpError when response.ok is false.
|
|
23
24
|
* Reads a small snippet of the body (if any) for easier debugging.
|
|
25
|
+
* Redacts sensitive information before including in error message.
|
|
24
26
|
*/
|
|
25
27
|
export declare function assertOk(response: Response, contextMessage: string): Promise<void>;
|
|
26
28
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,SAAU,SAAQ,KAAK;aAGhB,MAAM,CAAC,EAAE,MAAM;aACf,GAAG,CAAC,EAAE,MAAM;aACZ,WAAW,CAAC,EAAE,MAAM;gBAHpC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,GAAG,CAAC,EAAE,MAAM,YAAA,EACZ,WAAW,CAAC,EAAE,MAAM,YAAA;CAKvC;AAsBD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,QAAQ,CAAC,CAmDnB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAuBf"}
|
package/dist/http.js
CHANGED
|
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.HttpError = void 0;
|
|
4
4
|
exports.fetchWithTimeout = fetchWithTimeout;
|
|
5
5
|
exports.assertOk = assertOk;
|
|
6
|
+
const rate_limit_js_1 = require("./rate-limit.js");
|
|
6
7
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
7
8
|
const DEFAULT_RETRIES = 2;
|
|
8
9
|
const DEFAULT_RETRY_DELAY_MS = 500;
|
|
10
|
+
// Global rate limiter: 100 requests per minute to prevent abuse
|
|
11
|
+
const globalRateLimiter = new rate_limit_js_1.RateLimiter(100, 60_000);
|
|
9
12
|
class HttpError extends Error {
|
|
10
13
|
status;
|
|
11
14
|
url;
|
|
@@ -22,12 +25,32 @@ exports.HttpError = HttpError;
|
|
|
22
25
|
function sleep(ms) {
|
|
23
26
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Redacts sensitive information from error messages
|
|
30
|
+
*/
|
|
31
|
+
function redactSensitive(text) {
|
|
32
|
+
return text
|
|
33
|
+
.replace(/1ly_live_[a-zA-Z0-9]+/g, "1ly_live_***") // API keys
|
|
34
|
+
.replace(/1ly_test_[a-zA-Z0-9]+/g, "1ly_test_***") // Test API keys
|
|
35
|
+
.replace(/[13][a-km-zA-HJ-NP-Z1-9]{25,34}/g, "***") // Bitcoin/Solana addresses
|
|
36
|
+
.replace(/0x[a-fA-F0-9]{40}/g, "0x***") // Ethereum addresses
|
|
37
|
+
.replace(/"apiKey"\s*:\s*"[^"]+"/g, '"apiKey":"***"')
|
|
38
|
+
.replace(/"token"\s*:\s*"[^"]+"/g, '"token":"***"')
|
|
39
|
+
.replace(/"reviewToken"\s*:\s*"[^"]+"/g, '"reviewToken":"***"')
|
|
40
|
+
.replace(/"privateKey"\s*:\s*"[^"]+"/g, '"privateKey":"***"')
|
|
41
|
+
.replace(/"secretKey"\s*:\s*\[[^\]]+\]/g, '"secretKey":"***"');
|
|
42
|
+
}
|
|
25
43
|
/**
|
|
26
44
|
* fetchWithTimeout wraps the global fetch with:
|
|
45
|
+
* - rate limiting to prevent abuse
|
|
27
46
|
* - per-request timeout using AbortController
|
|
28
47
|
* - basic retry logic for network/timeout errors
|
|
29
48
|
*/
|
|
30
49
|
async function fetchWithTimeout(url, options = {}) {
|
|
50
|
+
// Rate limit check
|
|
51
|
+
if (!globalRateLimiter.check()) {
|
|
52
|
+
throw new Error(`Rate limit exceeded: too many requests (max 100 per minute). Current: ${globalRateLimiter.getCurrentCount()}`);
|
|
53
|
+
}
|
|
31
54
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, retries = DEFAULT_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS, ...fetchOptions } = options;
|
|
32
55
|
let lastError;
|
|
33
56
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
@@ -60,6 +83,7 @@ async function fetchWithTimeout(url, options = {}) {
|
|
|
60
83
|
/**
|
|
61
84
|
* Helper to throw a rich HttpError when response.ok is false.
|
|
62
85
|
* Reads a small snippet of the body (if any) for easier debugging.
|
|
86
|
+
* Redacts sensitive information before including in error message.
|
|
63
87
|
*/
|
|
64
88
|
async function assertOk(response, contextMessage) {
|
|
65
89
|
if (response.ok)
|
|
@@ -67,7 +91,9 @@ async function assertOk(response, contextMessage) {
|
|
|
67
91
|
let bodySnippet;
|
|
68
92
|
try {
|
|
69
93
|
const text = await response.text();
|
|
70
|
-
|
|
94
|
+
// Redact sensitive data before including in error
|
|
95
|
+
const redacted = redactSensitive(text.slice(0, 500));
|
|
96
|
+
bodySnippet = redacted;
|
|
71
97
|
}
|
|
72
98
|
catch {
|
|
73
99
|
// Ignore body read errors
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple token bucket rate limiter to prevent API abuse
|
|
3
|
+
* Limits number of requests within a sliding time window
|
|
4
|
+
*/
|
|
5
|
+
export declare class RateLimiter {
|
|
6
|
+
private maxRequests;
|
|
7
|
+
private windowMs;
|
|
8
|
+
private timestamps;
|
|
9
|
+
constructor(maxRequests: number, windowMs: number);
|
|
10
|
+
/**
|
|
11
|
+
* Check if request is allowed. Returns true if allowed, false if rate limit exceeded.
|
|
12
|
+
* Automatically records the request if allowed.
|
|
13
|
+
*/
|
|
14
|
+
check(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Get current request count within the window
|
|
17
|
+
*/
|
|
18
|
+
getCurrentCount(): number;
|
|
19
|
+
/**
|
|
20
|
+
* Reset the rate limiter
|
|
21
|
+
*/
|
|
22
|
+
reset(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IAIpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,UAAU,CAAgB;gBAGxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM;IAG1B;;;OAGG;IACH,KAAK,IAAI,OAAO;IAiBhB;;OAEG;IACH,eAAe,IAAI,MAAM;IAOzB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RateLimiter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple token bucket rate limiter to prevent API abuse
|
|
6
|
+
* Limits number of requests within a sliding time window
|
|
7
|
+
*/
|
|
8
|
+
class RateLimiter {
|
|
9
|
+
maxRequests;
|
|
10
|
+
windowMs;
|
|
11
|
+
timestamps = [];
|
|
12
|
+
constructor(maxRequests, windowMs) {
|
|
13
|
+
this.maxRequests = maxRequests;
|
|
14
|
+
this.windowMs = windowMs;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if request is allowed. Returns true if allowed, false if rate limit exceeded.
|
|
18
|
+
* Automatically records the request if allowed.
|
|
19
|
+
*/
|
|
20
|
+
check() {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const cutoff = now - this.windowMs;
|
|
23
|
+
// Remove timestamps outside the window
|
|
24
|
+
this.timestamps = this.timestamps.filter((t) => t > cutoff);
|
|
25
|
+
// Check if we're at the limit
|
|
26
|
+
if (this.timestamps.length >= this.maxRequests) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// Record this request
|
|
30
|
+
this.timestamps.push(now);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get current request count within the window
|
|
35
|
+
*/
|
|
36
|
+
getCurrentCount() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const cutoff = now - this.windowMs;
|
|
39
|
+
this.timestamps = this.timestamps.filter((t) => t > cutoff);
|
|
40
|
+
return this.timestamps.length;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Reset the rate limiter
|
|
44
|
+
*/
|
|
45
|
+
reset() {
|
|
46
|
+
this.timestamps = [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.RateLimiter = RateLimiter;
|
|
50
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,WAAW;IAIZ;IACA;IAJF,UAAU,GAAa,EAAE,CAAC;IAElC,YACU,WAAmB,EACnB,QAAgB;QADhB,gBAAW,GAAX,WAAW,CAAQ;QACnB,aAAQ,GAAR,QAAQ,CAAQ;IACvB,CAAC;IAEJ;;;OAGG;IACH,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAEnC,uCAAuC;QACvC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAE5D,8BAA8B;QAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC/C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,eAAe;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;CACF;AA7CD,kCA6CC"}
|