minara 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/README.md +246 -0
- package/bin/minara.js +2 -0
- package/dist/api/auth.d.ts +30 -0
- package/dist/api/auth.js +46 -0
- package/dist/api/chat.d.ts +19 -0
- package/dist/api/chat.js +54 -0
- package/dist/api/client.d.ts +16 -0
- package/dist/api/client.js +133 -0
- package/dist/api/copytrade.d.ts +19 -0
- package/dist/api/copytrade.js +37 -0
- package/dist/api/crosschain.d.ts +19 -0
- package/dist/api/crosschain.js +43 -0
- package/dist/api/limitorder.d.ts +13 -0
- package/dist/api/limitorder.js +25 -0
- package/dist/api/perps.d.ts +27 -0
- package/dist/api/perps.js +53 -0
- package/dist/api/tokens.d.ts +20 -0
- package/dist/api/tokens.js +41 -0
- package/dist/api/tradeconfig.d.ts +9 -0
- package/dist/api/tradeconfig.js +17 -0
- package/dist/auth-refresh.d.ts +15 -0
- package/dist/auth-refresh.js +94 -0
- package/dist/commands/account.d.ts +2 -0
- package/dist/commands/account.js +44 -0
- package/dist/commands/assets.d.ts +2 -0
- package/dist/commands/assets.js +26 -0
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +184 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +48 -0
- package/dist/commands/copy-trade.d.ts +2 -0
- package/dist/commands/copy-trade.js +190 -0
- package/dist/commands/deposit.d.ts +2 -0
- package/dist/commands/deposit.js +101 -0
- package/dist/commands/discover.d.ts +2 -0
- package/dist/commands/discover.js +88 -0
- package/dist/commands/limit-order.d.ts +2 -0
- package/dist/commands/limit-order.js +151 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +189 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +31 -0
- package/dist/commands/perps.d.ts +2 -0
- package/dist/commands/perps.js +280 -0
- package/dist/commands/swap.d.ts +2 -0
- package/dist/commands/swap.js +100 -0
- package/dist/commands/transfer.d.ts +2 -0
- package/dist/commands/transfer.js +76 -0
- package/dist/commands/withdraw.d.ts +2 -0
- package/dist/commands/withdraw.js +117 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +67 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +54 -0
- package/dist/oauth-server.d.ts +38 -0
- package/dist/oauth-server.js +136 -0
- package/dist/types.d.ts +246 -0
- package/dist/types.js +13 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +62 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">Minara CLI</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
Your AI-powered digital finance assistant — from the terminal.
|
|
5
|
+
<br />
|
|
6
|
+
Trade, swap, chat, and manage your portfolio without leaving the command line.
|
|
7
|
+
</p>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/minara"><img alt="npm version" src="https://img.shields.io/npm/v/minara?color=cb3837&label=npm"></a>
|
|
12
|
+
<a href="https://github.com/user/minara-cli/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
|
13
|
+
<img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D18-brightgreen">
|
|
14
|
+
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-strict-3178c6">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Multi-method Login** — Email verification code, Google OAuth, or Apple ID
|
|
22
|
+
- **Deposit & Withdraw** — View deposit addresses across chains, withdraw to external wallets
|
|
23
|
+
- **Asset Management** — View wallet balances across all supported chains
|
|
24
|
+
- **AI Chat** — Stream conversations with Minara AI, with thinking and deep-research modes
|
|
25
|
+
- **Spot Trading** — Cross-chain token swaps with dry-run simulation
|
|
26
|
+
- **Perpetual Futures** — Deposit, withdraw, place/cancel orders, manage leverage (Hyperliquid)
|
|
27
|
+
- **Limit Orders** — Create, list, and cancel price-triggered orders
|
|
28
|
+
- **Copy Trading** — Follow wallet addresses with configurable bots
|
|
29
|
+
- **Market Discovery** — Trending tokens, Fear & Greed Index, Bitcoin metrics
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g minara
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or run without installing:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx minara --help
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Requires Node.js >= 18**
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Login (interactive — choose email, Google, or Apple)
|
|
49
|
+
minara login
|
|
50
|
+
|
|
51
|
+
# Check your account
|
|
52
|
+
minara account
|
|
53
|
+
|
|
54
|
+
# View deposit addresses
|
|
55
|
+
minara deposit
|
|
56
|
+
|
|
57
|
+
# Chat with Minara AI
|
|
58
|
+
minara chat "What's the best DeFi yield right now?"
|
|
59
|
+
|
|
60
|
+
# Swap tokens
|
|
61
|
+
minara swap
|
|
62
|
+
|
|
63
|
+
# View trending tokens
|
|
64
|
+
minara discover trending
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
### Auth & Account
|
|
70
|
+
|
|
71
|
+
| Command | Description |
|
|
72
|
+
|---------|-------------|
|
|
73
|
+
| `minara login` | Login via email, Google, or Apple ID |
|
|
74
|
+
| `minara logout` | Logout and clear local credentials |
|
|
75
|
+
| `minara account` | View your account info and wallet addresses |
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
minara login # Interactive method selection
|
|
79
|
+
minara login -e user@mail.com # Email verification code
|
|
80
|
+
minara login --google # Google OAuth (opens browser)
|
|
81
|
+
minara login --apple # Apple ID (opens browser)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Wallet & Funds
|
|
85
|
+
|
|
86
|
+
| Command | Description |
|
|
87
|
+
|---------|-------------|
|
|
88
|
+
| `minara assets` | View wallet balances across all chains |
|
|
89
|
+
| `minara deposit` | Show deposit addresses and supported networks |
|
|
90
|
+
| `minara withdraw` | Withdraw tokens to an external wallet |
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
minara deposit
|
|
94
|
+
minara withdraw -c solana -t <token> -a 10 --to <address>
|
|
95
|
+
minara withdraw # Interactive mode
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Spot Trading
|
|
99
|
+
|
|
100
|
+
| Command | Description |
|
|
101
|
+
|---------|-------------|
|
|
102
|
+
| `minara swap` | Swap tokens (cross-chain) |
|
|
103
|
+
| `minara transfer` | Transfer tokens to another address |
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
minara swap # Interactive
|
|
107
|
+
minara swap -c solana -s buy -t <token> -a 100
|
|
108
|
+
minara swap --dry-run # Simulate without executing
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Perpetual Futures
|
|
112
|
+
|
|
113
|
+
| Command | Description |
|
|
114
|
+
|---------|-------------|
|
|
115
|
+
| `minara perps deposit` | Deposit USDC to Hyperliquid perps account |
|
|
116
|
+
| `minara perps withdraw` | Withdraw USDC from perps account |
|
|
117
|
+
| `minara perps positions` | View all open positions |
|
|
118
|
+
| `minara perps order` | Place an order (interactive builder) |
|
|
119
|
+
| `minara perps cancel` | Cancel open orders |
|
|
120
|
+
| `minara perps leverage` | Update leverage for a symbol |
|
|
121
|
+
| `minara perps trades` | View completed trade history |
|
|
122
|
+
| `minara perps fund-records` | View fund deposit/withdrawal records |
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
minara perps deposit -a 100 # Deposit 100 USDC to perps
|
|
126
|
+
minara perps withdraw -a 50 # Withdraw 50 USDC from perps
|
|
127
|
+
minara perps positions # List current positions
|
|
128
|
+
minara perps order # Interactive: choose symbol, side, size, price
|
|
129
|
+
minara perps leverage # Interactive: set leverage for a trading pair
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Limit Orders
|
|
133
|
+
|
|
134
|
+
| Command | Description |
|
|
135
|
+
|---------|-------------|
|
|
136
|
+
| `minara limit-order create` | Create a price-triggered limit order |
|
|
137
|
+
| `minara limit-order list` | List all your limit orders |
|
|
138
|
+
| `minara limit-order cancel <id>` | Cancel a specific order by ID |
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
minara limit-order create # Interactive: token, price, side, amount, expiry
|
|
142
|
+
minara limit-order list # Show all orders with status
|
|
143
|
+
minara limit-order cancel abc123 # Cancel order by ID
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Copy Trading
|
|
147
|
+
|
|
148
|
+
| Command | Description |
|
|
149
|
+
|---------|-------------|
|
|
150
|
+
| `minara copy-trade create` | Create a new copy-trade bot |
|
|
151
|
+
| `minara copy-trade list` | List all copy-trade bots |
|
|
152
|
+
| `minara copy-trade start <id>` | Start a paused bot |
|
|
153
|
+
| `minara copy-trade stop <id>` | Pause a running bot |
|
|
154
|
+
| `minara copy-trade delete <id>` | Delete a bot permanently |
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
minara copy-trade create # Interactive: target wallet, chain, amount, options
|
|
158
|
+
minara copy-trade list # Show all bots with status
|
|
159
|
+
minara copy-trade start abc123 # Resume a paused bot
|
|
160
|
+
minara copy-trade stop abc123 # Pause a running bot
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### AI Chat
|
|
164
|
+
|
|
165
|
+
| Command | Description |
|
|
166
|
+
|---------|-------------|
|
|
167
|
+
| `minara chat [message]` | Send a message (or enter interactive mode) |
|
|
168
|
+
| `minara chat --list` | List all your conversations |
|
|
169
|
+
| `minara chat --history <chatId>` | View messages in a conversation |
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
minara chat "What is the current BTC price?" # Single question, streamed answer
|
|
173
|
+
minara chat # Enter interactive REPL mode
|
|
174
|
+
minara chat --thinking "Analyze ETH outlook" # Enable reasoning mode
|
|
175
|
+
minara chat --deep-research "DeFi yield trends"# Deep research mode
|
|
176
|
+
minara chat --list # List past conversations
|
|
177
|
+
minara chat --history <chatId> # Replay a specific conversation
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Market Discovery
|
|
181
|
+
|
|
182
|
+
| Command | Description |
|
|
183
|
+
|---------|-------------|
|
|
184
|
+
| `minara discover trending` | View currently trending tokens |
|
|
185
|
+
| `minara discover search <keyword>` | Search for tokens or stocks by name |
|
|
186
|
+
| `minara discover fear-greed` | View the crypto Fear & Greed Index |
|
|
187
|
+
| `minara discover btc-metrics` | View Bitcoin on-chain and market metrics |
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
minara discover trending # Top trending tokens right now
|
|
191
|
+
minara discover search SOL # Search for tokens matching "SOL"
|
|
192
|
+
minara discover fear-greed # Current market sentiment index
|
|
193
|
+
minara discover btc-metrics # Bitcoin hashrate, supply, dominance, etc.
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Configuration
|
|
197
|
+
|
|
198
|
+
| Command | Description |
|
|
199
|
+
|---------|-------------|
|
|
200
|
+
| `minara config` | View or update CLI settings (e.g. API base URL) |
|
|
201
|
+
|
|
202
|
+
## Supported Chains
|
|
203
|
+
|
|
204
|
+
Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, Solana, BSC, Berachain, Blast, Manta, Mode, Sonic, and more.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Install dependencies
|
|
210
|
+
npm install
|
|
211
|
+
|
|
212
|
+
# Build
|
|
213
|
+
npm run build
|
|
214
|
+
|
|
215
|
+
# Watch mode
|
|
216
|
+
npm run dev
|
|
217
|
+
|
|
218
|
+
# Run locally
|
|
219
|
+
node dist/index.js --help
|
|
220
|
+
|
|
221
|
+
# Link globally for testing
|
|
222
|
+
npm link
|
|
223
|
+
minara --help
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Testing
|
|
227
|
+
|
|
228
|
+
Test suite built with [Vitest](https://vitest.dev/) — 119 tests covering unit, API, and command integration layers.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npm test # Run all tests
|
|
232
|
+
npm run test:watch # Watch mode
|
|
233
|
+
npm run test:coverage # With coverage report
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Security
|
|
237
|
+
|
|
238
|
+
- Credentials are stored in `~/.minara/credentials.json` with `0600` file permissions
|
|
239
|
+
- The `~/.minara/` directory is created with `0700` permissions
|
|
240
|
+
- Tokens are never logged or printed to the console
|
|
241
|
+
- OAuth login uses a temporary local server that shuts down after the callback
|
|
242
|
+
- Non-TTY environments (CI/pipes) skip interactive prompts and return errors directly
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
[MIT](LICENSE)
|
package/bin/minara.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { EmailCodeDto, EmailVerifyDto, AuthUser, FavoriteTokensPayload, OAuthProvider } from '../types.js';
|
|
2
|
+
/** Send email verification code */
|
|
3
|
+
export declare function sendEmailCode(dto: EmailCodeDto): Promise<import("../types.js").ApiResponse<void>>;
|
|
4
|
+
/** Verify email code → returns user + access_token */
|
|
5
|
+
export declare function verifyEmailCode(dto: EmailVerifyDto): Promise<import("../types.js").ApiResponse<AuthUser>>;
|
|
6
|
+
/** Get current user info */
|
|
7
|
+
export declare function getCurrentUser(token: string): Promise<import("../types.js").ApiResponse<AuthUser>>;
|
|
8
|
+
/** Logout */
|
|
9
|
+
export declare function logout(token: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
10
|
+
/** Get OAuth authorization URL */
|
|
11
|
+
export declare function getOAuthUrl(provider: OAuthProvider, redirectUri: string): Promise<import("../types.js").ApiResponse<{
|
|
12
|
+
url: string;
|
|
13
|
+
}>>;
|
|
14
|
+
/**
|
|
15
|
+
* Exchange OAuth callback params for an access token.
|
|
16
|
+
* Some providers require a separate exchange step.
|
|
17
|
+
*/
|
|
18
|
+
export declare function oauthCallback(provider: OAuthProvider, params: Record<string, string>): Promise<import("../types.js").ApiResponse<AuthUser>>;
|
|
19
|
+
/** Delete account */
|
|
20
|
+
export declare function deleteAccount(token: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
21
|
+
/** Get favorite tokens */
|
|
22
|
+
export declare function getFavoriteTokens(token: string): Promise<import("../types.js").ApiResponse<{
|
|
23
|
+
tokens: string[];
|
|
24
|
+
}>>;
|
|
25
|
+
/** Add favorite tokens */
|
|
26
|
+
export declare function addFavoriteTokens(token: string, payload: FavoriteTokensPayload): Promise<import("../types.js").ApiResponse<{
|
|
27
|
+
tokens: string[];
|
|
28
|
+
}>>;
|
|
29
|
+
/** Get invite history */
|
|
30
|
+
export declare function getInviteHistory(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
package/dist/api/auth.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { get, post, del } from './client.js';
|
|
2
|
+
/** Send email verification code */
|
|
3
|
+
export function sendEmailCode(dto) {
|
|
4
|
+
return post('/auth/email/code', { body: dto });
|
|
5
|
+
}
|
|
6
|
+
/** Verify email code → returns user + access_token */
|
|
7
|
+
export function verifyEmailCode(dto) {
|
|
8
|
+
return post('/auth/email/verify', { body: dto });
|
|
9
|
+
}
|
|
10
|
+
/** Get current user info */
|
|
11
|
+
export function getCurrentUser(token) {
|
|
12
|
+
return get('/auth/me', { token });
|
|
13
|
+
}
|
|
14
|
+
/** Logout */
|
|
15
|
+
export function logout(token) {
|
|
16
|
+
return post('/auth/logout', { token });
|
|
17
|
+
}
|
|
18
|
+
/** Get OAuth authorization URL */
|
|
19
|
+
export function getOAuthUrl(provider, redirectUri) {
|
|
20
|
+
return get(`/auth/${provider}/authorize`, {
|
|
21
|
+
query: { redirect_uri: redirectUri },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Exchange OAuth callback params for an access token.
|
|
26
|
+
* Some providers require a separate exchange step.
|
|
27
|
+
*/
|
|
28
|
+
export function oauthCallback(provider, params) {
|
|
29
|
+
return get(`/auth/${provider}/callback`, { query: params });
|
|
30
|
+
}
|
|
31
|
+
/** Delete account */
|
|
32
|
+
export function deleteAccount(token) {
|
|
33
|
+
return del('/auth/delete-account', { token });
|
|
34
|
+
}
|
|
35
|
+
/** Get favorite tokens */
|
|
36
|
+
export function getFavoriteTokens(token) {
|
|
37
|
+
return get('/auth/favorite-tokens', { token });
|
|
38
|
+
}
|
|
39
|
+
/** Add favorite tokens */
|
|
40
|
+
export function addFavoriteTokens(token, payload) {
|
|
41
|
+
return post('/auth/favorite-tokens', { token, body: payload });
|
|
42
|
+
}
|
|
43
|
+
/** Get invite history */
|
|
44
|
+
export function getInviteHistory(token) {
|
|
45
|
+
return get('/auth/invite-history', { token });
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChatRequestDTO, ChatInfo, ChatMemory } from '../types.js';
|
|
2
|
+
/** Send chat message — returns SSE stream (raw Response) */
|
|
3
|
+
export declare function sendChatStream(token: string, dto: ChatRequestDTO): Promise<Response>;
|
|
4
|
+
/** Stop a running chat */
|
|
5
|
+
export declare function stopChat(token: string, messageId: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
6
|
+
/** List chats */
|
|
7
|
+
export declare function listChats(token: string, limit?: number, offset?: number): Promise<import("../types.js").ApiResponse<ChatInfo[]>>;
|
|
8
|
+
/** Get chat detail */
|
|
9
|
+
export declare function getChat(token: string, chatId: string): Promise<import("../types.js").ApiResponse<ChatInfo>>;
|
|
10
|
+
/** Get chat memories (messages) */
|
|
11
|
+
export declare function getMemories(token: string, chatId: string, limit?: number, nextCursor?: string): Promise<import("../types.js").ApiResponse<ChatMemory[]>>;
|
|
12
|
+
/** Update chat name */
|
|
13
|
+
export declare function updateChatName(token: string, chatId: string, name: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
14
|
+
/** Delete chat */
|
|
15
|
+
export declare function deleteChat(token: string, chatId: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
16
|
+
/** Search chats */
|
|
17
|
+
export declare function searchChats(token: string, query: string, limit?: number, offset?: number): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
18
|
+
/** Get available chat models */
|
|
19
|
+
export declare function getChatModels(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
package/dist/api/chat.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { get, post, put, del } from './client.js';
|
|
2
|
+
import { loadConfig } from '../config.js';
|
|
3
|
+
/** Send chat message — returns SSE stream (raw Response) */
|
|
4
|
+
export async function sendChatStream(token, dto) {
|
|
5
|
+
const config = loadConfig();
|
|
6
|
+
const base = config.baseUrl.replace(/\/$/, '');
|
|
7
|
+
const url = `${base}/v1/chat`;
|
|
8
|
+
const res = await fetch(url, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
'Accept': 'text/event-stream',
|
|
13
|
+
'Authorization': `Bearer ${token}`,
|
|
14
|
+
'origin': 'https://minara.ai',
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify(dto),
|
|
17
|
+
});
|
|
18
|
+
return res;
|
|
19
|
+
}
|
|
20
|
+
/** Stop a running chat */
|
|
21
|
+
export function stopChat(token, messageId) {
|
|
22
|
+
return post('/v1/chat/stop', { token, query: { messageId } });
|
|
23
|
+
}
|
|
24
|
+
/** List chats */
|
|
25
|
+
export function listChats(token, limit = 20, offset = 0) {
|
|
26
|
+
return get('/v1/chats', { token, query: { limit, offset } });
|
|
27
|
+
}
|
|
28
|
+
/** Get chat detail */
|
|
29
|
+
export function getChat(token, chatId) {
|
|
30
|
+
return get(`/v1/chat/${encodeURIComponent(chatId)}`, { token });
|
|
31
|
+
}
|
|
32
|
+
/** Get chat memories (messages) */
|
|
33
|
+
export function getMemories(token, chatId, limit, nextCursor) {
|
|
34
|
+
return get(`/v1/chat/${encodeURIComponent(chatId)}/memories`, {
|
|
35
|
+
token,
|
|
36
|
+
query: { limit, nextCursor },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Update chat name */
|
|
40
|
+
export function updateChatName(token, chatId, name) {
|
|
41
|
+
return put(`/v1/chat/${encodeURIComponent(chatId)}`, { token, body: { name } });
|
|
42
|
+
}
|
|
43
|
+
/** Delete chat */
|
|
44
|
+
export function deleteChat(token, chatId) {
|
|
45
|
+
return del(`/v1/chat/${encodeURIComponent(chatId)}`, { token });
|
|
46
|
+
}
|
|
47
|
+
/** Search chats */
|
|
48
|
+
export function searchChats(token, query, limit = 10, offset = 0) {
|
|
49
|
+
return get('/v1/chat/search', { token, query: { query, limit, offset } });
|
|
50
|
+
}
|
|
51
|
+
/** Get available chat models */
|
|
52
|
+
export function getChatModels(token) {
|
|
53
|
+
return get('/v1/chat/models', { token });
|
|
54
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ApiResponse } from '../types.js';
|
|
2
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
3
|
+
interface RequestOptions {
|
|
4
|
+
method?: HttpMethod;
|
|
5
|
+
body?: unknown;
|
|
6
|
+
query?: Record<string, string | number | undefined>;
|
|
7
|
+
token?: string;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export declare function request<T>(path: string, opts?: RequestOptions): Promise<ApiResponse<T>>;
|
|
11
|
+
export declare function get<T>(path: string, opts?: Omit<RequestOptions, 'method'>): Promise<ApiResponse<T>>;
|
|
12
|
+
export declare function post<T>(path: string, opts?: Omit<RequestOptions, 'method'>): Promise<ApiResponse<T>>;
|
|
13
|
+
export declare function put<T>(path: string, opts?: Omit<RequestOptions, 'method'>): Promise<ApiResponse<T>>;
|
|
14
|
+
export declare function del<T>(path: string, opts?: Omit<RequestOptions, 'method'>): Promise<ApiResponse<T>>;
|
|
15
|
+
export declare function patch<T>(path: string, opts?: Omit<RequestOptions, 'method'>): Promise<ApiResponse<T>>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { loadConfig } from '../config.js';
|
|
2
|
+
function buildUrl(path, query) {
|
|
3
|
+
const config = loadConfig();
|
|
4
|
+
const base = config.baseUrl.replace(/\/$/, '');
|
|
5
|
+
const url = new URL(`${base}${path}`);
|
|
6
|
+
if (query) {
|
|
7
|
+
for (const [k, v] of Object.entries(query)) {
|
|
8
|
+
if (v !== undefined && v !== '') {
|
|
9
|
+
url.searchParams.set(k, String(v));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return url.toString();
|
|
14
|
+
}
|
|
15
|
+
// ── Core request implementation ───────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* @param isRetry When true, skip the 401 re-auth handler to prevent loops.
|
|
18
|
+
*/
|
|
19
|
+
async function requestImpl(path, opts, isRetry) {
|
|
20
|
+
const { method = 'GET', body, query, token, headers: extraHeaders } = opts;
|
|
21
|
+
const url = buildUrl(path, query);
|
|
22
|
+
const headers = {
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
'Accept': 'application/json',
|
|
25
|
+
...extraHeaders,
|
|
26
|
+
};
|
|
27
|
+
if (token) {
|
|
28
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
29
|
+
}
|
|
30
|
+
const init = { method, headers };
|
|
31
|
+
if (body && method !== 'GET') {
|
|
32
|
+
init.body = JSON.stringify(body);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(url, init);
|
|
36
|
+
// ── 401 interception ──────────────────────────────────────────────
|
|
37
|
+
// Only trigger when: (1) authenticated request, (2) not already a retry,
|
|
38
|
+
// (3) running in an interactive terminal (not piped / CI).
|
|
39
|
+
if (res.status === 401 && token && !isRetry) {
|
|
40
|
+
return handleAuthExpired(path, opts);
|
|
41
|
+
}
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
let json;
|
|
44
|
+
try {
|
|
45
|
+
json = JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// non-JSON response
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
return { success: false, error: { code: res.status, message: text || res.statusText } };
|
|
51
|
+
}
|
|
52
|
+
return { success: true, data: text };
|
|
53
|
+
}
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const errBody = json;
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
error: {
|
|
59
|
+
code: res.status,
|
|
60
|
+
message: (errBody.message ?? errBody.error ?? res.statusText),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Adapt: if the API wraps data in { data: ... } or returns directly
|
|
65
|
+
const data = json.data ?? json;
|
|
66
|
+
return { success: true, data: data };
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
+
return { success: false, error: { code: 0, message } };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ── 401 handler ───────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Called when an authenticated request returns 401.
|
|
76
|
+
*
|
|
77
|
+
* - In a TTY (interactive terminal): prompts the user to re-login, then
|
|
78
|
+
* retries the original request with the fresh token.
|
|
79
|
+
* - In a non-TTY (pipe / CI): returns a friendly error immediately.
|
|
80
|
+
*
|
|
81
|
+
* Uses dynamic `import()` for `auth-refresh.ts` to break the circular
|
|
82
|
+
* dependency chain: client → auth-refresh → api/auth → client.
|
|
83
|
+
*/
|
|
84
|
+
async function handleAuthExpired(path, opts) {
|
|
85
|
+
// Non-interactive environments: skip prompts
|
|
86
|
+
if (!process.stdin.isTTY) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: {
|
|
90
|
+
code: 401,
|
|
91
|
+
message: 'Session expired. Run `minara login` to refresh your credentials.',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
// Dynamic import — only loaded when needed, avoids circular deps
|
|
97
|
+
const { attemptReAuth } = await import('../auth-refresh.js');
|
|
98
|
+
const newToken = await attemptReAuth();
|
|
99
|
+
if (newToken) {
|
|
100
|
+
// Retry the original request with the new token (isRetry = true)
|
|
101
|
+
return requestImpl(path, { ...opts, token: newToken }, true);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Module load failure or unexpected error — fall through
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: {
|
|
110
|
+
code: 401,
|
|
111
|
+
message: 'Session expired. Run `minara login` to refresh your credentials.',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// ── Public API ────────────────────────────────────────────────────────────
|
|
116
|
+
export async function request(path, opts = {}) {
|
|
117
|
+
return requestImpl(path, opts, false);
|
|
118
|
+
}
|
|
119
|
+
export function get(path, opts) {
|
|
120
|
+
return request(path, { ...opts, method: 'GET' });
|
|
121
|
+
}
|
|
122
|
+
export function post(path, opts) {
|
|
123
|
+
return request(path, { ...opts, method: 'POST' });
|
|
124
|
+
}
|
|
125
|
+
export function put(path, opts) {
|
|
126
|
+
return request(path, { ...opts, method: 'PUT' });
|
|
127
|
+
}
|
|
128
|
+
export function del(path, opts) {
|
|
129
|
+
return request(path, { ...opts, method: 'DELETE' });
|
|
130
|
+
}
|
|
131
|
+
export function patch(path, opts) {
|
|
132
|
+
return request(path, { ...opts, method: 'PATCH' });
|
|
133
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CreateCopyTradeDto, UpdateCopyTradeDto, CopyTradeInfo } from '../types.js';
|
|
2
|
+
/** Create copy trade */
|
|
3
|
+
export declare function createCopyTrade(token: string, dto: CreateCopyTradeDto): Promise<import("../types.js").ApiResponse<CopyTradeInfo>>;
|
|
4
|
+
/** List user's copy trades */
|
|
5
|
+
export declare function listCopyTrades(token: string): Promise<import("../types.js").ApiResponse<CopyTradeInfo[]>>;
|
|
6
|
+
/** Get copy trade by ID */
|
|
7
|
+
export declare function getCopyTrade(token: string, id: string): Promise<import("../types.js").ApiResponse<CopyTradeInfo>>;
|
|
8
|
+
/** Update copy trade */
|
|
9
|
+
export declare function updateCopyTrade(token: string, id: string, dto: UpdateCopyTradeDto): Promise<import("../types.js").ApiResponse<CopyTradeInfo>>;
|
|
10
|
+
/** Delete copy trade */
|
|
11
|
+
export declare function deleteCopyTrade(token: string, id: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
12
|
+
/** Start copy trade */
|
|
13
|
+
export declare function startCopyTrade(token: string, id: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
14
|
+
/** Stop copy trade */
|
|
15
|
+
export declare function stopCopyTrade(token: string, id: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
16
|
+
/** Get copy trade activity */
|
|
17
|
+
export declare function getCopyTradeActivity(token: string, id: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
18
|
+
/** Get copy trade PnL chart */
|
|
19
|
+
export declare function getCopyTradePnl(token: string, id: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { get, post, patch, del } from './client.js';
|
|
2
|
+
/** Create copy trade */
|
|
3
|
+
export function createCopyTrade(token, dto) {
|
|
4
|
+
return post('/copy-trade', { token, body: dto });
|
|
5
|
+
}
|
|
6
|
+
/** List user's copy trades */
|
|
7
|
+
export function listCopyTrades(token) {
|
|
8
|
+
return get('/copy-trade', { token });
|
|
9
|
+
}
|
|
10
|
+
/** Get copy trade by ID */
|
|
11
|
+
export function getCopyTrade(token, id) {
|
|
12
|
+
return get(`/copy-trade/${encodeURIComponent(id)}`, { token });
|
|
13
|
+
}
|
|
14
|
+
/** Update copy trade */
|
|
15
|
+
export function updateCopyTrade(token, id, dto) {
|
|
16
|
+
return patch(`/copy-trade/${encodeURIComponent(id)}`, { token, body: dto });
|
|
17
|
+
}
|
|
18
|
+
/** Delete copy trade */
|
|
19
|
+
export function deleteCopyTrade(token, id) {
|
|
20
|
+
return del(`/copy-trade/${encodeURIComponent(id)}`, { token });
|
|
21
|
+
}
|
|
22
|
+
/** Start copy trade */
|
|
23
|
+
export function startCopyTrade(token, id) {
|
|
24
|
+
return patch(`/copy-trade/${encodeURIComponent(id)}/start`, { token });
|
|
25
|
+
}
|
|
26
|
+
/** Stop copy trade */
|
|
27
|
+
export function stopCopyTrade(token, id) {
|
|
28
|
+
return patch(`/copy-trade/${encodeURIComponent(id)}/stop`, { token });
|
|
29
|
+
}
|
|
30
|
+
/** Get copy trade activity */
|
|
31
|
+
export function getCopyTradeActivity(token, id) {
|
|
32
|
+
return get(`/copy-trade/${encodeURIComponent(id)}/activity`, { token });
|
|
33
|
+
}
|
|
34
|
+
/** Get copy trade PnL chart */
|
|
35
|
+
export function getCopyTradePnl(token, id) {
|
|
36
|
+
return get(`/copy-trade/${encodeURIComponent(id)}/pnl/chart`, { token });
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CrossChainSwapDto, CrossChainTransferDto, CrossChainActivitiesDto } from '../types.js';
|
|
2
|
+
/** Get cross-chain account info */
|
|
3
|
+
export declare function getAccount(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
4
|
+
/** Get wallet assets */
|
|
5
|
+
export declare function getAssets(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
6
|
+
/** Execute a single swap */
|
|
7
|
+
export declare function swap(token: string, dto: CrossChainSwapDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
8
|
+
/** Execute multiple swaps */
|
|
9
|
+
export declare function swaps(token: string, swapList: CrossChainSwapDto[]): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
10
|
+
/** Simulate swaps (dry-run) */
|
|
11
|
+
export declare function swapsSimulate(token: string, swapList: CrossChainSwapDto[]): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
12
|
+
/** Transfer tokens */
|
|
13
|
+
export declare function transfer(token: string, dto: CrossChainTransferDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
14
|
+
/** Get activities */
|
|
15
|
+
export declare function getActivities(token: string, dto: CrossChainActivitiesDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
16
|
+
/** Get PnL history */
|
|
17
|
+
export declare function getPnlHistory(token: string, type: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
18
|
+
/** Check transaction statuses */
|
|
19
|
+
export declare function getStatuses(token: string, transactionIds: string[]): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { get, post } from './client.js';
|
|
2
|
+
/** Get cross-chain account info */
|
|
3
|
+
export function getAccount(token) {
|
|
4
|
+
return get('/v1/tx/cross-chain/account', { token });
|
|
5
|
+
}
|
|
6
|
+
/** Get wallet assets */
|
|
7
|
+
export function getAssets(token) {
|
|
8
|
+
return get('/v1/tx/cross-chain/assets', { token });
|
|
9
|
+
}
|
|
10
|
+
/** Execute a single swap */
|
|
11
|
+
export function swap(token, dto) {
|
|
12
|
+
return post('/v1/tx/cross-chain/swap', { token, body: dto });
|
|
13
|
+
}
|
|
14
|
+
/** Execute multiple swaps */
|
|
15
|
+
export function swaps(token, swapList) {
|
|
16
|
+
return post('/v1/tx/cross-chain/swaps', {
|
|
17
|
+
token,
|
|
18
|
+
body: { swaps: swapList },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Simulate swaps (dry-run) */
|
|
22
|
+
export function swapsSimulate(token, swapList) {
|
|
23
|
+
return post('/v1/tx/cross-chain/swaps-simulate', {
|
|
24
|
+
token,
|
|
25
|
+
body: { swaps: swapList },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** Transfer tokens */
|
|
29
|
+
export function transfer(token, dto) {
|
|
30
|
+
return post('/v1/tx/cross-chain/transfer', { token, body: dto });
|
|
31
|
+
}
|
|
32
|
+
/** Get activities */
|
|
33
|
+
export function getActivities(token, dto) {
|
|
34
|
+
return post('/v1/tx/cross-chain/activities', { token, body: dto });
|
|
35
|
+
}
|
|
36
|
+
/** Get PnL history */
|
|
37
|
+
export function getPnlHistory(token, type) {
|
|
38
|
+
return get('/v1/tx/cross-chain/pnl/history', { token, query: { type } });
|
|
39
|
+
}
|
|
40
|
+
/** Check transaction statuses */
|
|
41
|
+
export function getStatuses(token, transactionIds) {
|
|
42
|
+
return post('/v1/tx/cross-chain/statuses', { token, body: { transactionIds } });
|
|
43
|
+
}
|