@nalinsahani/wealtharena-cli 0.0.0-stage → 0.1.1
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 +25 -2
- package/bin/mcp.js +191 -0
- package/bin/wa.js +67 -0
- package/package.json +35 -4
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nalinsahani/wealtharena-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Wealth Arena CLI (`wa`) — unified terminal interface for quantitative trading, options analysis, and automated AI agents (`claude code`, `opencode`, `gemini cli`).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Zero-Install via Bun (Blazing Fast ~15ms)
|
|
8
|
+
```bash
|
|
9
|
+
bunx @nalinsahani/wealtharena-cli --json market quote --symbol NSE:NIFTY50-INDEX
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Zero-Install via Node / NPX
|
|
13
|
+
```bash
|
|
14
|
+
npx -y @nalinsahani/wealtharena-cli --json market quote --symbol NSE:NIFTY50-INDEX
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Global Install
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g @nalinsahani/wealtharena-cli
|
|
20
|
+
wa --json me
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
- **Deterministic JSON output**: Pass `--json` anywhere for machine-readable JSON envelopes.
|
|
25
|
+
- **Fail-closed live gate**: Paper trading is free; live trading refuses without `--live --yes` and margin verification.
|
|
26
|
+
- **AI Agent Native**: Automatic `X-AI-Actor` and `X-AI-Session` tracking with database audit logs.
|
package/bin/mcp.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wealth Arena Model Context Protocol (MCP) Server
|
|
5
|
+
* Lightweight, zero-external-dependency JSON-RPC stdio server compatible with Node.js and Bun.
|
|
6
|
+
* Exposes Wealth Arena tools directly to Claude Code, Gemini CLI, Cursor, and OpenCode.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawnSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
|
|
13
|
+
// Locate the companion wa launcher
|
|
14
|
+
const waLauncher = path.join(__dirname, 'wa.js');
|
|
15
|
+
|
|
16
|
+
function callWa(args) {
|
|
17
|
+
const fullArgs = [waLauncher, '--json', ...args];
|
|
18
|
+
const proc = spawnSync(process.execPath, fullArgs, {
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
env: { ...process.env, WA_AI: '1', WA_ACTOR: 'mcp-server' },
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
status: proc.status,
|
|
24
|
+
stdout: proc.stdout || '',
|
|
25
|
+
stderr: proc.stderr || '',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const TOOLS = [
|
|
30
|
+
{
|
|
31
|
+
name: 'wa_market_quote',
|
|
32
|
+
description: 'Fetch real-time market quote (LTP, change, volume, prev close) for a broker symbol.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
symbol: { type: 'string', description: 'Broker symbol (e.g. "NSE:NIFTY50-INDEX", "NSE:SBIN-EQ")' },
|
|
37
|
+
},
|
|
38
|
+
required: ['symbol'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'wa_chain_snapshot',
|
|
43
|
+
description: 'Fetch option chain snapshot for an underlying asset with Greeks, strikes, and filters.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
symbol: { type: 'string', description: 'Underlying symbol (e.g. "NIFTY", "BANKNIFTY")' },
|
|
48
|
+
strikes: { type: 'string', description: 'Strike count on each side of ATM (default "10")' },
|
|
49
|
+
expiry: { type: 'string', description: 'Expiry date or "all" (empty = nearest)' },
|
|
50
|
+
},
|
|
51
|
+
required: ['symbol'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'wa_broker_status',
|
|
56
|
+
description: 'Check broker connectivity status (fyers, zerodha, indmoney, or all).',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
broker: { type: 'string', description: 'Broker name: "fyers" | "zerodha" | "indmoney" | "all"' },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'wa_paper_margin',
|
|
66
|
+
description: 'Check paper trading simulation margin and buying power.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'wa_halt_status',
|
|
74
|
+
description: 'Check whether the emergency trading halt circuit breaker is active.',
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'wa_live_margin',
|
|
82
|
+
description: 'Check live broker collateral and available trading margin.',
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
function handleToolCall(name, params) {
|
|
91
|
+
let cliArgs = [];
|
|
92
|
+
switch (name) {
|
|
93
|
+
case 'wa_market_quote':
|
|
94
|
+
cliArgs = ['market', 'quote', '--symbol', params.symbol];
|
|
95
|
+
break;
|
|
96
|
+
case 'wa_chain_snapshot':
|
|
97
|
+
cliArgs = ['chain', 'snapshot', '--symbol', params.symbol];
|
|
98
|
+
if (params.strikes) cliArgs.push('--strikes', params.strikes);
|
|
99
|
+
if (params.expiry) cliArgs.push('--expiry', params.expiry);
|
|
100
|
+
break;
|
|
101
|
+
case 'wa_broker_status':
|
|
102
|
+
cliArgs = ['broker', 'status'];
|
|
103
|
+
if (params.broker) cliArgs.push('--broker', params.broker);
|
|
104
|
+
break;
|
|
105
|
+
case 'wa_paper_margin':
|
|
106
|
+
cliArgs = ['paper', 'margin'];
|
|
107
|
+
break;
|
|
108
|
+
case 'wa_halt_status':
|
|
109
|
+
cliArgs = ['halt', 'status'];
|
|
110
|
+
break;
|
|
111
|
+
case 'wa_live_margin':
|
|
112
|
+
cliArgs = ['live', 'margin'];
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
return { isError: true, content: [{ type: 'text', text: `Unknown tool: ${name}` }] };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const res = callWa(cliArgs);
|
|
119
|
+
const textOutput = res.stdout.trim() || res.stderr.trim();
|
|
120
|
+
return {
|
|
121
|
+
isError: res.status !== 0,
|
|
122
|
+
content: [{ type: 'text', text: textOutput }],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// JSON-RPC stdio protocol loop
|
|
127
|
+
const rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout,
|
|
130
|
+
terminal: false,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
function sendResponse(id, result, error) {
|
|
134
|
+
const resp = { jsonrpc: '2.0', id };
|
|
135
|
+
if (error) {
|
|
136
|
+
resp.error = error;
|
|
137
|
+
} else {
|
|
138
|
+
resp.result = result;
|
|
139
|
+
}
|
|
140
|
+
process.stdout.write(JSON.stringify(resp) + '\n');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
rl.on('line', (line) => {
|
|
144
|
+
if (!line.trim()) return;
|
|
145
|
+
let req;
|
|
146
|
+
try {
|
|
147
|
+
req = JSON.parse(line);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
sendResponse(null, null, { code: -32700, message: 'Parse error' });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const { id, method, params } = req;
|
|
154
|
+
|
|
155
|
+
switch (method) {
|
|
156
|
+
case 'initialize':
|
|
157
|
+
sendResponse(id, {
|
|
158
|
+
protocolVersion: '2024-11-05',
|
|
159
|
+
capabilities: { tools: {} },
|
|
160
|
+
serverInfo: { name: 'wealtharena-mcp', version: '0.1.0' },
|
|
161
|
+
});
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case 'notifications/initialized':
|
|
165
|
+
// Notification, no response needed
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case 'ping':
|
|
169
|
+
sendResponse(id, {});
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case 'tools/list':
|
|
173
|
+
sendResponse(id, { tools: TOOLS });
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
case 'tools/call':
|
|
177
|
+
if (!params || !params.name) {
|
|
178
|
+
sendResponse(id, null, { code: -32602, message: 'Invalid params: tool name required' });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const toolRes = handleToolCall(params.name, params.arguments || {});
|
|
182
|
+
sendResponse(id, toolRes);
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
default:
|
|
186
|
+
if (id !== undefined && id !== null) {
|
|
187
|
+
sendResponse(id, null, { code: -32601, message: `Method not found: ${method}` });
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
});
|
package/bin/wa.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wealth Arena CLI Launcher
|
|
5
|
+
* Cross-runtime launcher supporting Node.js (npx) and Bun (bunx).
|
|
6
|
+
* Spawns the pre-compiled native Go binary for the host platform with zero compilation overhead.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawnSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
const platform = os.platform(); // 'linux', 'darwin', 'win32'
|
|
15
|
+
const arch = os.arch(); // 'x64', 'arm64'
|
|
16
|
+
|
|
17
|
+
const binaryName = platform === 'win32' ? 'wa.exe' : 'wa';
|
|
18
|
+
const targetName = `wa-${platform}-${arch}` + (platform === 'win32' ? '.exe' : '');
|
|
19
|
+
const pkgName = `@nalinsahani/wealtharena-cli-${platform}-${arch}`;
|
|
20
|
+
|
|
21
|
+
let binaryPath = null;
|
|
22
|
+
|
|
23
|
+
// 1. Try resolving from platform-specific optionalDependency
|
|
24
|
+
try {
|
|
25
|
+
binaryPath = require.resolve(`${pkgName}/bin/${binaryName}`);
|
|
26
|
+
} catch {
|
|
27
|
+
// Not installed as a platform dependency package, check local paths
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Check local dist/bin directory (development / local builds)
|
|
31
|
+
if (!binaryPath || !fs.existsSync(binaryPath)) {
|
|
32
|
+
const localDist = path.join(__dirname, '..', '..', 'dist', 'bin', targetName);
|
|
33
|
+
if (fs.existsSync(localDist)) {
|
|
34
|
+
binaryPath = localDist;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. Check adjacent root binary (fallback)
|
|
39
|
+
if (!binaryPath || !fs.existsSync(binaryPath)) {
|
|
40
|
+
const rootBin = path.join(__dirname, '..', '..', binaryName);
|
|
41
|
+
if (fs.existsSync(rootBin)) {
|
|
42
|
+
binaryPath = rootBin;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!binaryPath || !fs.existsSync(binaryPath)) {
|
|
47
|
+
console.error(`[wa] Error: Native binary not found for platform ${platform}-${arch}.`);
|
|
48
|
+
console.error(`[wa] Expected platform package: ${pkgName} or local binary at dist/bin/${targetName}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. Ensure binary is executable (on unix systems)
|
|
53
|
+
if (platform !== 'win32') {
|
|
54
|
+
try {
|
|
55
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
56
|
+
} catch {
|
|
57
|
+
// best-effort
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 5. Transparently spawn the Go binary, forwarding args, stdio, and exit code
|
|
62
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
63
|
+
stdio: 'inherit',
|
|
64
|
+
env: process.env,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
process.exit(result.status ?? (result.signal ? 1 : 0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nalinsahani/wealtharena-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Wealth Arena trading & market CLI for traders and AI agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"wa": "./bin/wa.js",
|
|
7
|
+
"wealtharena-cli": "./bin/wa.js",
|
|
8
|
+
"wa-mcp": "./bin/mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18.0.0"
|
|
16
|
+
},
|
|
17
|
+
"optionalDependencies": {
|
|
18
|
+
"@nalinsahani/wealtharena-cli-darwin-arm64": "0.1.0",
|
|
19
|
+
"@nalinsahani/wealtharena-cli-darwin-x64": "0.1.0",
|
|
20
|
+
"@nalinsahani/wealtharena-cli-linux-arm64": "0.1.0",
|
|
21
|
+
"@nalinsahani/wealtharena-cli-linux-x64": "0.1.0",
|
|
22
|
+
"@nalinsahani/wealtharena-cli-win32-x64": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"trading",
|
|
26
|
+
"cli",
|
|
27
|
+
"fyers",
|
|
28
|
+
"zerodha",
|
|
29
|
+
"market-data",
|
|
30
|
+
"options",
|
|
31
|
+
"ai-agent",
|
|
32
|
+
"opencode",
|
|
33
|
+
"claude-code"
|
|
34
|
+
],
|
|
35
|
+
"author": "Nalin Sahani",
|
|
36
|
+
"license": "UNLICENSED"
|
|
37
|
+
}
|