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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CreateLimitOrderDto, UpdateLimitOrderDto, LimitOrderInfo } from '../types.js';
|
|
2
|
+
/** Create a limit order */
|
|
3
|
+
export declare function createLimitOrder(token: string, dto: CreateLimitOrderDto): Promise<import("../types.js").ApiResponse<LimitOrderInfo>>;
|
|
4
|
+
/** List user's limit orders */
|
|
5
|
+
export declare function listLimitOrders(token: string): Promise<import("../types.js").ApiResponse<LimitOrderInfo[]>>;
|
|
6
|
+
/** Get limit order by ID */
|
|
7
|
+
export declare function getLimitOrder(token: string, id: string): Promise<import("../types.js").ApiResponse<LimitOrderInfo>>;
|
|
8
|
+
/** Update limit order */
|
|
9
|
+
export declare function updateLimitOrder(token: string, id: string, dto: UpdateLimitOrderDto): Promise<import("../types.js").ApiResponse<LimitOrderInfo>>;
|
|
10
|
+
/** Delete limit order */
|
|
11
|
+
export declare function deleteLimitOrder(token: string, id: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
12
|
+
/** Cancel limit order */
|
|
13
|
+
export declare function cancelLimitOrder(token: string, id: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { get, post, patch, del } from './client.js';
|
|
2
|
+
/** Create a limit order */
|
|
3
|
+
export function createLimitOrder(token, dto) {
|
|
4
|
+
return post('/limit-order', { token, body: dto });
|
|
5
|
+
}
|
|
6
|
+
/** List user's limit orders */
|
|
7
|
+
export function listLimitOrders(token) {
|
|
8
|
+
return get('/limit-order', { token });
|
|
9
|
+
}
|
|
10
|
+
/** Get limit order by ID */
|
|
11
|
+
export function getLimitOrder(token, id) {
|
|
12
|
+
return get(`/limit-order/${encodeURIComponent(id)}`, { token });
|
|
13
|
+
}
|
|
14
|
+
/** Update limit order */
|
|
15
|
+
export function updateLimitOrder(token, id, dto) {
|
|
16
|
+
return patch(`/limit-order/${encodeURIComponent(id)}`, { token, body: dto });
|
|
17
|
+
}
|
|
18
|
+
/** Delete limit order */
|
|
19
|
+
export function deleteLimitOrder(token, id) {
|
|
20
|
+
return del(`/limit-order/${encodeURIComponent(id)}`, { token });
|
|
21
|
+
}
|
|
22
|
+
/** Cancel limit order */
|
|
23
|
+
export function cancelLimitOrder(token, id) {
|
|
24
|
+
return post(`/limit-order/${encodeURIComponent(id)}/cancel`, { token });
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PerpsDepositDto, PerpsWithdrawDto, PerpsPlaceOrdersDto, PerpsCancelOrdersDto, UpdateLeverageDto } from '../types.js';
|
|
2
|
+
/** Deposit USDC to perps (min 5 USDC) */
|
|
3
|
+
export declare function deposit(token: string, dto: PerpsDepositDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
4
|
+
/** Withdraw USDC from perps */
|
|
5
|
+
export declare function withdraw(token: string, dto: PerpsWithdrawDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
6
|
+
/** Place perp orders */
|
|
7
|
+
export declare function placeOrders(token: string, dto: PerpsPlaceOrdersDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
8
|
+
/** Cancel perp orders */
|
|
9
|
+
export declare function cancelOrders(token: string, dto: PerpsCancelOrdersDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
10
|
+
/** Modify existing orders */
|
|
11
|
+
export declare function modifyOrders(token: string, dto: PerpsCancelOrdersDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
12
|
+
/** Update leverage */
|
|
13
|
+
export declare function updateLeverage(token: string, dto: UpdateLeverageDto): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
14
|
+
/** Get all positions */
|
|
15
|
+
export declare function getPositions(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
16
|
+
/** Get completed trades */
|
|
17
|
+
export declare function getCompletedTrades(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
18
|
+
/** Get token prices */
|
|
19
|
+
export declare function getTokenPrices(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
20
|
+
/** Get fund records */
|
|
21
|
+
export declare function getFundRecords(token: string, page: number, limit: number): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
22
|
+
/** Get equity history chart */
|
|
23
|
+
export declare function getEquityHistory(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
24
|
+
/** Get all decisions */
|
|
25
|
+
export declare function getDecisions(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
26
|
+
/** Claim rewards */
|
|
27
|
+
export declare function claimRewards(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { get, post } from './client.js';
|
|
2
|
+
/** Deposit USDC to perps (min 5 USDC) */
|
|
3
|
+
export function deposit(token, dto) {
|
|
4
|
+
return post('/v1/tx/perps/deposit', { token, body: dto });
|
|
5
|
+
}
|
|
6
|
+
/** Withdraw USDC from perps */
|
|
7
|
+
export function withdraw(token, dto) {
|
|
8
|
+
return post('/v1/tx/perps/withdraw', { token, body: dto });
|
|
9
|
+
}
|
|
10
|
+
/** Place perp orders */
|
|
11
|
+
export function placeOrders(token, dto) {
|
|
12
|
+
return post('/v1/tx/perps/place-orders', { token, body: dto });
|
|
13
|
+
}
|
|
14
|
+
/** Cancel perp orders */
|
|
15
|
+
export function cancelOrders(token, dto) {
|
|
16
|
+
return post('/v1/tx/perps/cancel-orders', { token, body: dto });
|
|
17
|
+
}
|
|
18
|
+
/** Modify existing orders */
|
|
19
|
+
export function modifyOrders(token, dto) {
|
|
20
|
+
return post('/v1/tx/perps/modify-orders', { token, body: dto });
|
|
21
|
+
}
|
|
22
|
+
/** Update leverage */
|
|
23
|
+
export function updateLeverage(token, dto) {
|
|
24
|
+
return post('/v1/tx/perps/update-leverage', { token, body: dto });
|
|
25
|
+
}
|
|
26
|
+
/** Get all positions */
|
|
27
|
+
export function getPositions(token) {
|
|
28
|
+
return get('/v1/tx/perps/positions/all', { token });
|
|
29
|
+
}
|
|
30
|
+
/** Get completed trades */
|
|
31
|
+
export function getCompletedTrades(token) {
|
|
32
|
+
return get('/v1/tx/perps/completed-trades/all', { token });
|
|
33
|
+
}
|
|
34
|
+
/** Get token prices */
|
|
35
|
+
export function getTokenPrices(token) {
|
|
36
|
+
return get('/v1/tx/perps/token/prices', { token });
|
|
37
|
+
}
|
|
38
|
+
/** Get fund records */
|
|
39
|
+
export function getFundRecords(token, page, limit) {
|
|
40
|
+
return get('/v1/tx/perps/fund-records', { token, query: { page, limit } });
|
|
41
|
+
}
|
|
42
|
+
/** Get equity history chart */
|
|
43
|
+
export function getEquityHistory(token) {
|
|
44
|
+
return get('/v1/tx/perps/equity-history-chart/all', { token });
|
|
45
|
+
}
|
|
46
|
+
/** Get all decisions */
|
|
47
|
+
export function getDecisions(token) {
|
|
48
|
+
return get('/v1/tx/perps/decisions/all', { token });
|
|
49
|
+
}
|
|
50
|
+
/** Claim rewards */
|
|
51
|
+
export function claimRewards(token) {
|
|
52
|
+
return post('/v1/tx/perps/claim-rewards', { token });
|
|
53
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Get trending tokens */
|
|
2
|
+
export declare function getTrendingTokens(): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
3
|
+
/** Search tokens by keyword */
|
|
4
|
+
export declare function searchTokens(keyword: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
5
|
+
/** Get project info for a token */
|
|
6
|
+
export declare function getProjectInfo(symbol: string, address: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
7
|
+
/** Get trending stocks */
|
|
8
|
+
export declare function getTrendingStocks(): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
9
|
+
/** Search stocks */
|
|
10
|
+
export declare function searchStocks(keyword: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
11
|
+
/** Get stock info */
|
|
12
|
+
export declare function getStockInfo(symbol: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
13
|
+
/** Discover tokens by risk preference */
|
|
14
|
+
export declare function discoverTokens(riskPreference: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
15
|
+
/** Get events */
|
|
16
|
+
export declare function getEvents(page: string, pageSize: string, version: string, language: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
17
|
+
/** Get fear & greed index */
|
|
18
|
+
export declare function getFearGreedIndex(): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
19
|
+
/** Get bitcoin metrics */
|
|
20
|
+
export declare function getBitcoinMetrics(): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { get } from './client.js';
|
|
2
|
+
/** Get trending tokens */
|
|
3
|
+
export function getTrendingTokens() {
|
|
4
|
+
return get('/tokens/trending-tokens');
|
|
5
|
+
}
|
|
6
|
+
/** Search tokens by keyword */
|
|
7
|
+
export function searchTokens(keyword) {
|
|
8
|
+
return get('/tokens/search-tokens', { query: { keyword } });
|
|
9
|
+
}
|
|
10
|
+
/** Get project info for a token */
|
|
11
|
+
export function getProjectInfo(symbol, address) {
|
|
12
|
+
return get('/tokens/project-info', { query: { symbol, address } });
|
|
13
|
+
}
|
|
14
|
+
/** Get trending stocks */
|
|
15
|
+
export function getTrendingStocks() {
|
|
16
|
+
return get('/stocks/trending-stocks');
|
|
17
|
+
}
|
|
18
|
+
/** Search stocks */
|
|
19
|
+
export function searchStocks(keyword) {
|
|
20
|
+
return get('/stocks/search-stocks', { query: { keyword } });
|
|
21
|
+
}
|
|
22
|
+
/** Get stock info */
|
|
23
|
+
export function getStockInfo(symbol) {
|
|
24
|
+
return get('/stocks/get-stock-info', { query: { symbol } });
|
|
25
|
+
}
|
|
26
|
+
/** Discover tokens by risk preference */
|
|
27
|
+
export function discoverTokens(riskPreference) {
|
|
28
|
+
return get('/discover/tokens', { query: { riskPreference } });
|
|
29
|
+
}
|
|
30
|
+
/** Get events */
|
|
31
|
+
export function getEvents(page, pageSize, version, language) {
|
|
32
|
+
return get('/discover/events', { query: { page, pageSize, version, language } });
|
|
33
|
+
}
|
|
34
|
+
/** Get fear & greed index */
|
|
35
|
+
export function getFearGreedIndex() {
|
|
36
|
+
return get('/discover/fear-greed-index');
|
|
37
|
+
}
|
|
38
|
+
/** Get bitcoin metrics */
|
|
39
|
+
export function getBitcoinMetrics() {
|
|
40
|
+
return get('/discover/bitcoin-metrics');
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { UserTradeConfig } from '../types.js';
|
|
2
|
+
/** Get user trade config */
|
|
3
|
+
export declare function getTradeConfig(token: string): Promise<import("../types.js").ApiResponse<UserTradeConfig>>;
|
|
4
|
+
/** Create or update trade config */
|
|
5
|
+
export declare function upsertTradeConfig(token: string, config: UserTradeConfig): Promise<import("../types.js").ApiResponse<UserTradeConfig>>;
|
|
6
|
+
/** Delete trade config */
|
|
7
|
+
export declare function deleteTradeConfig(token: string): Promise<import("../types.js").ApiResponse<void>>;
|
|
8
|
+
/** Get gas fees */
|
|
9
|
+
export declare function getGasFees(token: string): Promise<import("../types.js").ApiResponse<unknown>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { get, post, del } from './client.js';
|
|
2
|
+
/** Get user trade config */
|
|
3
|
+
export function getTradeConfig(token) {
|
|
4
|
+
return get('/user-trade-config', { token });
|
|
5
|
+
}
|
|
6
|
+
/** Create or update trade config */
|
|
7
|
+
export function upsertTradeConfig(token, config) {
|
|
8
|
+
return post('/user-trade-config', { token, body: config });
|
|
9
|
+
}
|
|
10
|
+
/** Delete trade config */
|
|
11
|
+
export function deleteTradeConfig(token) {
|
|
12
|
+
return del('/user-trade-config', { token });
|
|
13
|
+
}
|
|
14
|
+
/** Get gas fees */
|
|
15
|
+
export function getGasFees(token) {
|
|
16
|
+
return get('/user-trade-config/gas-fees', { token });
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session refresh module.
|
|
3
|
+
*
|
|
4
|
+
* When the API returns 401, this module provides an interactive re-login
|
|
5
|
+
* flow so the user doesn't have to manually run `minara login` again.
|
|
6
|
+
*
|
|
7
|
+
* Imported DYNAMICALLY by api/client.ts to avoid circular dependencies:
|
|
8
|
+
* client.ts → (dynamic) auth-refresh.ts → api/auth.ts → client.ts
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Attempt to re-authenticate interactively.
|
|
12
|
+
*
|
|
13
|
+
* @returns The new access token on success, or `null` on failure / user decline.
|
|
14
|
+
*/
|
|
15
|
+
export declare function attemptReAuth(): Promise<string | null>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session refresh module.
|
|
3
|
+
*
|
|
4
|
+
* When the API returns 401, this module provides an interactive re-login
|
|
5
|
+
* flow so the user doesn't have to manually run `minara login` again.
|
|
6
|
+
*
|
|
7
|
+
* Imported DYNAMICALLY by api/client.ts to avoid circular dependencies:
|
|
8
|
+
* client.ts → (dynamic) auth-refresh.ts → api/auth.ts → client.ts
|
|
9
|
+
*/
|
|
10
|
+
import { confirm, input } from '@inquirer/prompts';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { loadCredentials, saveCredentials } from './config.js';
|
|
13
|
+
import { sendEmailCode, verifyEmailCode } from './api/auth.js';
|
|
14
|
+
/**
|
|
15
|
+
* Attempt to re-authenticate interactively.
|
|
16
|
+
*
|
|
17
|
+
* @returns The new access token on success, or `null` on failure / user decline.
|
|
18
|
+
*/
|
|
19
|
+
export async function attemptReAuth() {
|
|
20
|
+
try {
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(chalk.yellow('⚠'), chalk.yellow('Your session has expired or is invalid.'));
|
|
23
|
+
// ── Ask user whether to re-login ──────────────────────────────────
|
|
24
|
+
const wantReLogin = await confirm({
|
|
25
|
+
message: 'Would you like to re-login now?',
|
|
26
|
+
default: true,
|
|
27
|
+
});
|
|
28
|
+
if (!wantReLogin) {
|
|
29
|
+
console.log(chalk.dim('Run `minara login` to manually refresh your session.'));
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// ── Determine email ─────────────────────────────────────────────────
|
|
33
|
+
const creds = loadCredentials();
|
|
34
|
+
let email = creds?.email;
|
|
35
|
+
if (!email) {
|
|
36
|
+
email = await input({
|
|
37
|
+
message: 'Email address:',
|
|
38
|
+
validate: (v) => (v.includes('@') ? true : 'Enter a valid email'),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log(chalk.dim(`Sending verification code to ${email}…`));
|
|
43
|
+
}
|
|
44
|
+
// ── Send code ───────────────────────────────────────────────────────
|
|
45
|
+
const codeRes = await sendEmailCode({ email, platform: 'cli' });
|
|
46
|
+
if (!codeRes.success) {
|
|
47
|
+
console.error(chalk.red('✖'), `Failed to send code: ${codeRes.error?.message ?? 'Unknown error'}`);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
console.log(chalk.green('✔'), `Verification code sent to ${chalk.cyan(email)}`);
|
|
51
|
+
// ── Get code from user ──────────────────────────────────────────────
|
|
52
|
+
const code = await input({
|
|
53
|
+
message: 'Enter verification code:',
|
|
54
|
+
validate: (v) => (v.length > 0 ? true : 'Code is required'),
|
|
55
|
+
});
|
|
56
|
+
// ── Verify ──────────────────────────────────────────────────────────
|
|
57
|
+
const verifyRes = await verifyEmailCode({
|
|
58
|
+
email,
|
|
59
|
+
code,
|
|
60
|
+
channel: 'cli',
|
|
61
|
+
deviceType: 'cli',
|
|
62
|
+
});
|
|
63
|
+
if (!verifyRes.success || !verifyRes.data) {
|
|
64
|
+
console.error(chalk.red('✖'), `Verification failed: ${verifyRes.error?.message ?? 'Invalid code'}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const user = verifyRes.data;
|
|
68
|
+
const newToken = user.access_token;
|
|
69
|
+
if (!newToken) {
|
|
70
|
+
console.error(chalk.red('✖'), 'No access token in response.');
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
// ── Save new credentials ────────────────────────────────────────────
|
|
74
|
+
saveCredentials({
|
|
75
|
+
accessToken: newToken,
|
|
76
|
+
userId: user.id,
|
|
77
|
+
email: user.email ?? email,
|
|
78
|
+
displayName: user.displayName,
|
|
79
|
+
});
|
|
80
|
+
console.log(chalk.green('✔'), 'Re-login successful! Retrying your request…');
|
|
81
|
+
console.log('');
|
|
82
|
+
return newToken;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
// Handle Ctrl+C or closed prompt
|
|
86
|
+
if (err instanceof Error && (err.message.includes('closed') || err.message.includes('aborted'))) {
|
|
87
|
+
console.log(chalk.dim('\nRe-login cancelled.'));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.error(chalk.red('✖'), `Re-login error: ${err instanceof Error ? err.message : String(err)}`);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { getCurrentUser } from '../api/auth.js';
|
|
4
|
+
import { requireAuth } from '../config.js';
|
|
5
|
+
import { error, spinner } from '../utils.js';
|
|
6
|
+
export const accountCommand = new Command('account')
|
|
7
|
+
.alias('me')
|
|
8
|
+
.description('View your Minara account info')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
const creds = requireAuth();
|
|
11
|
+
const spin = spinner('Fetching account info…');
|
|
12
|
+
const res = await getCurrentUser(creds.accessToken);
|
|
13
|
+
spin.stop();
|
|
14
|
+
if (!res.success || !res.data) {
|
|
15
|
+
error(res.error?.message ?? 'Failed to fetch account info');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const u = res.data;
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log(chalk.bold('Account Info:'));
|
|
21
|
+
if (u.displayName)
|
|
22
|
+
console.log(` Name : ${chalk.cyan(u.displayName)}`);
|
|
23
|
+
if (u.email)
|
|
24
|
+
console.log(` Email : ${chalk.cyan(u.email)}`);
|
|
25
|
+
if (u.username)
|
|
26
|
+
console.log(` Username : ${u.username}`);
|
|
27
|
+
if (u.id)
|
|
28
|
+
console.log(` User ID : ${chalk.dim(u.id)}`);
|
|
29
|
+
if (u.invitationCode)
|
|
30
|
+
console.log(` Invite Code : ${u.invitationCode}`);
|
|
31
|
+
if (u.wallets && Object.keys(u.wallets).length > 0) {
|
|
32
|
+
console.log(` Wallets:`);
|
|
33
|
+
for (const [type, addr] of Object.entries(u.wallets)) {
|
|
34
|
+
console.log(` ${chalk.dim(type)} : ${addr}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (u.accounts && Object.keys(u.accounts).length > 0) {
|
|
38
|
+
console.log(` Linked:`);
|
|
39
|
+
for (const provider of Object.keys(u.accounts)) {
|
|
40
|
+
console.log(` ${chalk.green('✔')} ${provider}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
console.log('');
|
|
44
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { getAssets } from '../api/crosschain.js';
|
|
4
|
+
import { requireAuth } from '../config.js';
|
|
5
|
+
import { error, spinner } from '../utils.js';
|
|
6
|
+
export const assetsCommand = new Command('assets')
|
|
7
|
+
.description('View your wallet assets across chains')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const creds = requireAuth();
|
|
10
|
+
const spin = spinner('Fetching assets…');
|
|
11
|
+
const res = await getAssets(creds.accessToken);
|
|
12
|
+
spin.stop();
|
|
13
|
+
if (!res.success || !res.data) {
|
|
14
|
+
error(res.error?.message ?? 'Failed to fetch assets');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const data = res.data;
|
|
18
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
19
|
+
console.log(chalk.dim('No assets found.'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(chalk.bold('Your Assets:'));
|
|
24
|
+
console.log(JSON.stringify(data, null, 2));
|
|
25
|
+
console.log('');
|
|
26
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { select } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { sendChatStream, listChats, getMemories } from '../api/chat.js';
|
|
5
|
+
import { requireAuth } from '../config.js';
|
|
6
|
+
import { error, info, spinner } from '../utils.js';
|
|
7
|
+
import { createInterface } from 'node:readline';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
/** Parse SSE stream and yield text chunks */
|
|
10
|
+
async function* parseSSE(response) {
|
|
11
|
+
const reader = response.body?.getReader();
|
|
12
|
+
if (!reader)
|
|
13
|
+
return;
|
|
14
|
+
const decoder = new TextDecoder();
|
|
15
|
+
let buffer = '';
|
|
16
|
+
while (true) {
|
|
17
|
+
const { done, value } = await reader.read();
|
|
18
|
+
if (done)
|
|
19
|
+
break;
|
|
20
|
+
buffer += decoder.decode(value, { stream: true });
|
|
21
|
+
const lines = buffer.split('\n');
|
|
22
|
+
buffer = lines.pop() ?? '';
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
if (line.startsWith('data:')) {
|
|
25
|
+
const data = line.slice(5).trim();
|
|
26
|
+
if (data === '[DONE]')
|
|
27
|
+
return;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(data);
|
|
30
|
+
// Extract text content from various SSE event formats
|
|
31
|
+
const text = parsed?.choices?.[0]?.delta?.content
|
|
32
|
+
?? parsed?.content
|
|
33
|
+
?? parsed?.text
|
|
34
|
+
?? parsed?.data?.text
|
|
35
|
+
?? (typeof parsed === 'string' ? parsed : null);
|
|
36
|
+
if (text)
|
|
37
|
+
yield text;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Non-JSON data line — might be raw text
|
|
41
|
+
if (data && data !== '[DONE]')
|
|
42
|
+
yield data;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export const chatCommand = new Command('chat')
|
|
49
|
+
.description('Chat with Minara AI assistant')
|
|
50
|
+
.argument('[message]', 'Send a single message (non-interactive)')
|
|
51
|
+
.option('-c, --chat-id <id>', 'Continue existing chat')
|
|
52
|
+
.option('--list', 'List past chats')
|
|
53
|
+
.option('--history <chatId>', 'Show chat history')
|
|
54
|
+
.option('--thinking', 'Enable thinking/degen mode')
|
|
55
|
+
.option('--deep-research', 'Enable deep research mode')
|
|
56
|
+
.action(async (messageArg, opts) => {
|
|
57
|
+
const creds = requireAuth();
|
|
58
|
+
// ── List chats ───────────────────────────────────────────────────────
|
|
59
|
+
if (opts?.list) {
|
|
60
|
+
const spin = spinner('Fetching chats…');
|
|
61
|
+
const res = await listChats(creds.accessToken);
|
|
62
|
+
spin.stop();
|
|
63
|
+
if (!res.success || !res.data) {
|
|
64
|
+
error(res.error?.message ?? 'Failed');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const chats = res.data;
|
|
68
|
+
if (!chats || chats.length === 0) {
|
|
69
|
+
console.log(chalk.dim('No chats yet.'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const c of chats) {
|
|
73
|
+
console.log(` ${chalk.bold((c.chatId ?? '').slice(0, 12))}… ${c.name ?? '(untitled)'} ${chalk.dim(c.updatedAt ?? '')}`);
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// ── Show history ─────────────────────────────────────────────────────
|
|
78
|
+
if (opts?.history) {
|
|
79
|
+
const spin = spinner('Loading history…');
|
|
80
|
+
const res = await getMemories(creds.accessToken, opts.history);
|
|
81
|
+
spin.stop();
|
|
82
|
+
if (!res.success || !res.data) {
|
|
83
|
+
error(res.error?.message ?? 'Failed');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const memories = res.data;
|
|
87
|
+
for (const m of memories) {
|
|
88
|
+
const prefix = m.role === 'user' ? chalk.blue.bold('You ') : chalk.green.bold('Minara');
|
|
89
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
|
|
90
|
+
console.log(`${prefix}: ${content}`);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// ── Chat context ─────────────────────────────────────────────────────
|
|
95
|
+
let chatId = opts?.chatId;
|
|
96
|
+
if (!chatId && !messageArg) {
|
|
97
|
+
const mode = await select({
|
|
98
|
+
message: 'Chat mode:',
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: 'Start a new conversation', value: 'new' },
|
|
101
|
+
{ name: 'Continue existing conversation', value: 'continue' },
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
if (mode === 'continue') {
|
|
105
|
+
const spin = spinner('Fetching chats…');
|
|
106
|
+
const res = await listChats(creds.accessToken);
|
|
107
|
+
spin.stop();
|
|
108
|
+
const chats = res.data;
|
|
109
|
+
if (chats && chats.length > 0) {
|
|
110
|
+
chatId = await select({
|
|
111
|
+
message: 'Select chat:',
|
|
112
|
+
choices: chats.map((c) => ({
|
|
113
|
+
name: `${(c.chatId ?? '').slice(0, 12)}… ${c.name ?? '(untitled)'}`,
|
|
114
|
+
value: c.chatId,
|
|
115
|
+
})),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
info('No existing chats. Starting new.');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!chatId)
|
|
124
|
+
chatId = randomUUID();
|
|
125
|
+
// ── Send single message ──────────────────────────────────────────────
|
|
126
|
+
async function sendAndPrint(msg) {
|
|
127
|
+
process.stdout.write(`${chalk.green.bold('Minara')}: `);
|
|
128
|
+
const response = await sendChatStream(creds.accessToken, {
|
|
129
|
+
chatId,
|
|
130
|
+
message: { role: 'user', content: msg },
|
|
131
|
+
thinking: opts?.thinking,
|
|
132
|
+
deepresearch: opts?.deepResearch,
|
|
133
|
+
chartOptions: { chartsCountRecommendedLimit: 0 },
|
|
134
|
+
});
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
const body = await response.text();
|
|
137
|
+
console.log('');
|
|
138
|
+
error(`API error ${response.status}: ${body}`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
for await (const chunk of parseSSE(response)) {
|
|
142
|
+
process.stdout.write(chunk);
|
|
143
|
+
}
|
|
144
|
+
console.log('\n');
|
|
145
|
+
}
|
|
146
|
+
if (messageArg) {
|
|
147
|
+
await sendAndPrint(messageArg);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// ── REPL ─────────────────────────────────────────────────────────────
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(chalk.bold('Minara AI Chat'));
|
|
153
|
+
console.log(chalk.dim('Type your message. "exit" to quit, "/new" for new chat, "/help" for commands.'));
|
|
154
|
+
console.log('');
|
|
155
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
156
|
+
const prompt = () => new Promise((resolve) => {
|
|
157
|
+
rl.question(chalk.blue('You: '), resolve);
|
|
158
|
+
});
|
|
159
|
+
rl.on('close', () => { console.log(chalk.dim('\nGoodbye!')); process.exit(0); });
|
|
160
|
+
while (true) {
|
|
161
|
+
const userMsg = (await prompt()).trim();
|
|
162
|
+
if (!userMsg)
|
|
163
|
+
continue;
|
|
164
|
+
if (userMsg.toLowerCase() === 'exit' || userMsg.toLowerCase() === 'quit') {
|
|
165
|
+
console.log(chalk.dim('Goodbye!'));
|
|
166
|
+
rl.close();
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (userMsg === '/new') {
|
|
170
|
+
chatId = randomUUID();
|
|
171
|
+
info('New conversation started.');
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (userMsg === '/id') {
|
|
175
|
+
console.log(chalk.dim(`Chat ID: ${chatId}`));
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (userMsg === '/help') {
|
|
179
|
+
console.log(chalk.dim(' /new — New conversation\n /id — Show chat ID\n exit — Quit'));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
await sendAndPrint(userMsg);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { input, select } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { loadConfig, saveConfig, getMinaraDir } from '../config.js';
|
|
5
|
+
import { success, info } from '../utils.js';
|
|
6
|
+
export const configCommand = new Command('config')
|
|
7
|
+
.description('View or update CLI configuration')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const action = await select({
|
|
10
|
+
message: 'Configuration:',
|
|
11
|
+
choices: [
|
|
12
|
+
{ name: 'Show current config', value: 'show' },
|
|
13
|
+
{ name: 'Set base URL', value: 'baseUrl' },
|
|
14
|
+
{ name: 'Show config directory path', value: 'path' },
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
const config = loadConfig();
|
|
18
|
+
switch (action) {
|
|
19
|
+
case 'show':
|
|
20
|
+
console.log('');
|
|
21
|
+
console.log(chalk.bold('Current Configuration:'));
|
|
22
|
+
console.log(` Base URL : ${chalk.cyan(config.baseUrl)}`);
|
|
23
|
+
console.log(` Config Dir : ${chalk.dim(getMinaraDir())}`);
|
|
24
|
+
console.log('');
|
|
25
|
+
break;
|
|
26
|
+
case 'baseUrl': {
|
|
27
|
+
const url = await input({
|
|
28
|
+
message: 'Base URL:',
|
|
29
|
+
default: config.baseUrl,
|
|
30
|
+
validate: (v) => {
|
|
31
|
+
try {
|
|
32
|
+
new URL(v);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return 'Please enter a valid URL';
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
saveConfig({ baseUrl: url });
|
|
41
|
+
success(`Base URL set to ${url}`);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'path':
|
|
45
|
+
info(`Config directory: ${getMinaraDir()}`);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
});
|