@invizi/cli 0.1.10 → 0.1.12
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 +2 -2
- package/dist/commands/trades.js +1 -1
- package/dist/invizi.js +4 -4
- package/dist/track.js +131 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# invizi-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI for Invizi.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ npm install -g @invizi/cli
|
|
|
14
14
|
invizi auth login
|
|
15
15
|
invizi auth status
|
|
16
16
|
invizi --help
|
|
17
|
-
invizi
|
|
17
|
+
invizi track 0x1234...abcd
|
|
18
18
|
invizi setup
|
|
19
19
|
```
|
|
20
20
|
|
package/dist/commands/trades.js
CHANGED
|
@@ -239,7 +239,7 @@ export async function trades(args) {
|
|
|
239
239
|
const config = loadConfig();
|
|
240
240
|
const address = positionals.find(a => /^0x[0-9a-fA-F]{40}$/.test(a)) || config.address;
|
|
241
241
|
if (!address) {
|
|
242
|
-
console.error('No address found. Run: invizi
|
|
242
|
+
console.error('No address found. Run: invizi track <address>');
|
|
243
243
|
console.error('Or pass directly: invizi trades 0x...');
|
|
244
244
|
return 1;
|
|
245
245
|
}
|
package/dist/invizi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { auth, getAuthorizationHeader } from './auth.js';
|
|
3
|
-
import {
|
|
3
|
+
import { track } from './track.js';
|
|
4
4
|
import { getApiUrl, getConfigPath, loadConfig, redactConfig } from './config.js';
|
|
5
5
|
import { setup } from './setup.js';
|
|
6
6
|
import { trades } from './commands/index.js';
|
|
@@ -19,7 +19,7 @@ Account:
|
|
|
19
19
|
invizi login Login
|
|
20
20
|
invizi logout Logout
|
|
21
21
|
invizi auth status Show current identity
|
|
22
|
-
invizi
|
|
22
|
+
invizi track <address> Track a wallet address
|
|
23
23
|
|
|
24
24
|
Setup:
|
|
25
25
|
invizi setup Configure API + install AI skills
|
|
@@ -136,8 +136,8 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
136
136
|
return auth(['login']);
|
|
137
137
|
if (command === 'logout')
|
|
138
138
|
return auth(['logout']);
|
|
139
|
-
if (command === '
|
|
140
|
-
return
|
|
139
|
+
if (command === 'track')
|
|
140
|
+
return track(args.slice(1));
|
|
141
141
|
if (command === 'trades')
|
|
142
142
|
return trades(args.slice(1));
|
|
143
143
|
if (command === 'setup')
|
package/dist/track.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getApiUrl, loadConfig, saveConfig } from './config.js';
|
|
2
|
+
import { getAuthorizationHeader } from './auth.js';
|
|
3
|
+
const HLP_API = 'https://api.hyperliquid.xyz/info';
|
|
4
|
+
function detectChain(address) {
|
|
5
|
+
if (/^0x[0-9a-fA-F]{40}$/.test(address))
|
|
6
|
+
return 'evm';
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const EXCHANGE_CHECKERS = {
|
|
10
|
+
evm: [{ name: 'hyperliquid', check: validateHlpAddress }],
|
|
11
|
+
};
|
|
12
|
+
async function validateHlpAddress(address) {
|
|
13
|
+
const [stateRes, fillsRes] = await Promise.all([
|
|
14
|
+
fetch(HLP_API, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify({ type: 'clearinghouseState', user: address }),
|
|
18
|
+
}),
|
|
19
|
+
fetch(HLP_API, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
body: JSON.stringify({ type: 'userFills', user: address }),
|
|
23
|
+
}),
|
|
24
|
+
]);
|
|
25
|
+
if (!stateRes.ok || !fillsRes.ok) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const state = (await stateRes.json());
|
|
29
|
+
const fills = (await fillsRes.json());
|
|
30
|
+
return {
|
|
31
|
+
positions: state.assetPositions?.length || 0,
|
|
32
|
+
accountValue: Number.parseFloat(state.marginSummary?.accountValue || '0'),
|
|
33
|
+
trades: fills.length || 0,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function registerWithServer(exchange, address, label) {
|
|
37
|
+
const apiUrl = getApiUrl();
|
|
38
|
+
const authHeader = await getAuthorizationHeader();
|
|
39
|
+
if (!authHeader) {
|
|
40
|
+
throw new Error('Not authenticated. Run: invizi auth login');
|
|
41
|
+
}
|
|
42
|
+
const res = await fetch(`${apiUrl}/api/connect`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
Authorization: authHeader,
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ exchange, address, label }),
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
const bodyText = await res.text();
|
|
52
|
+
let parsed = null;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(bodyText);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
parsed = null;
|
|
58
|
+
}
|
|
59
|
+
if (res.status === 403 && parsed?.error?.includes('Tracking limit reached')) {
|
|
60
|
+
const tracked = parsed.trackedAddress;
|
|
61
|
+
const details = tracked
|
|
62
|
+
? `existing ${tracked.exchange}:${tracked.address}${tracked.label ? ` (${tracked.label})` : ''}`
|
|
63
|
+
: 'already at limit';
|
|
64
|
+
throw new Error(`Tracking limit reached (${details}).`);
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`Server error: ${res.status} ${parsed?.error || bodyText}`);
|
|
67
|
+
}
|
|
68
|
+
return (await res.json());
|
|
69
|
+
}
|
|
70
|
+
export async function track(args) {
|
|
71
|
+
const address = args[0];
|
|
72
|
+
const labelIdx = args.indexOf('--label');
|
|
73
|
+
const label = labelIdx !== -1 ? args[labelIdx + 1] || null : null;
|
|
74
|
+
if (!address) {
|
|
75
|
+
console.error('Usage: invizi track <wallet-address> [--label name]');
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
const chain = detectChain(address);
|
|
79
|
+
if (!chain) {
|
|
80
|
+
console.error(`Unrecognized address format: ${address}`);
|
|
81
|
+
console.error('Supported: EVM addresses (0x...)');
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
const checkers = EXCHANGE_CHECKERS[chain];
|
|
85
|
+
if (!checkers || checkers.length === 0) {
|
|
86
|
+
console.error(`No supported exchanges for chain: ${chain}`);
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
console.log(`Scanning exchanges for ${address}...`);
|
|
90
|
+
const found = [];
|
|
91
|
+
for (const { name, check } of checkers) {
|
|
92
|
+
const info = await check(address);
|
|
93
|
+
if (info && (info.positions > 0 || info.trades > 0 || info.accountValue > 0)) {
|
|
94
|
+
found.push({ exchange: name, info });
|
|
95
|
+
console.log(` [ok] ${name}: ${info.positions} positions, ${info.trades} trades, $${info.accountValue.toFixed(2)}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(` [x] ${name}: no activity`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (found.length === 0) {
|
|
102
|
+
console.error('No activity found on any supported exchange for this address.');
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
const selected = found[0];
|
|
106
|
+
try {
|
|
107
|
+
const result = await registerWithServer(selected.exchange, address, label);
|
|
108
|
+
const config = loadConfig();
|
|
109
|
+
config.userId = result.userId;
|
|
110
|
+
config.trackedAddressId = result.trackedAddressId;
|
|
111
|
+
delete config.accountId;
|
|
112
|
+
config.address = address;
|
|
113
|
+
config.exchange = selected.exchange;
|
|
114
|
+
if (label)
|
|
115
|
+
config.label = label;
|
|
116
|
+
saveConfig(config);
|
|
117
|
+
console.log(`Connected watch-only tracking: ${selected.exchange}`);
|
|
118
|
+
if (result.alreadyTracked) {
|
|
119
|
+
console.log(' Already tracked previously.');
|
|
120
|
+
}
|
|
121
|
+
console.log(` Positions: ${selected.info.positions}`);
|
|
122
|
+
console.log(` Trades: ${selected.info.trades}`);
|
|
123
|
+
console.log(` Account value: $${selected.info.accountValue.toFixed(2)}`);
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
128
|
+
console.error(`Failed to register: ${message}`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invizi/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Invizi CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"build": "tsc -p tsconfig.json",
|
|
15
15
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
16
16
|
"test": "jest",
|
|
17
|
-
"prepack": "npm run build"
|
|
17
|
+
"prepack": "npm run build",
|
|
18
|
+
"postinstall": "node -e \"console.log('\\n\\x1b[1m✨ Invizi CLI installed!\\x1b[0m\\n\\nGet started:\\n invizi login Authenticate\\n invizi connect <address> Connect your wallet\\n invizi setup Install AI skills\\n invizi market Check the markets\\n invizi --help See all commands\\n')\""
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@types/jest": "^30.0.0",
|