blue-js-sdk 2.0.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/CHANGELOG.md +446 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/ai-path/ADMIN-ELEVATION.md +116 -0
- package/ai-path/AI-MANIFESTO.md +185 -0
- package/ai-path/BREAKING.md +74 -0
- package/ai-path/CHECKLIST.md +619 -0
- package/ai-path/CONNECTION-STEPS.md +724 -0
- package/ai-path/DECISION-TREE.md +378 -0
- package/ai-path/DEPENDENCIES.md +459 -0
- package/ai-path/E2E-FLOW.md +1555 -0
- package/ai-path/FAILURES.md +403 -0
- package/ai-path/GUIDE.md +1217 -0
- package/ai-path/README.md +558 -0
- package/ai-path/SPLIT-TUNNEL.md +266 -0
- package/ai-path/cli.js +535 -0
- package/ai-path/connect.js +884 -0
- package/ai-path/discover.js +178 -0
- package/ai-path/environment.js +266 -0
- package/ai-path/errors.js +86 -0
- package/ai-path/examples/autonomous-agent.mjs +220 -0
- package/ai-path/examples/multi-region.mjs +174 -0
- package/ai-path/examples/one-shot.mjs +31 -0
- package/ai-path/index.js +60 -0
- package/ai-path/pricing.js +136 -0
- package/ai-path/recommend.js +413 -0
- package/ai-path/run-admin.vbs +25 -0
- package/ai-path/setup.js +291 -0
- package/ai-path/wallet.js +137 -0
- package/app-helpers.js +363 -0
- package/app-settings.js +95 -0
- package/app-types.js +267 -0
- package/audit.js +847 -0
- package/batch.js +293 -0
- package/bin/setup.js +376 -0
- package/chain/authz.js +109 -0
- package/chain/broadcast.js +472 -0
- package/chain/client.js +160 -0
- package/chain/fee-grants.js +305 -0
- package/chain/index.js +891 -0
- package/chain/lcd.js +313 -0
- package/chain/queries.js +547 -0
- package/chain/rpc.js +408 -0
- package/chain/wallet.js +141 -0
- package/cli/config.js +143 -0
- package/cli/index.js +463 -0
- package/cli/output.js +182 -0
- package/cli.js +491 -0
- package/client/index.js +251 -0
- package/client.js +271 -0
- package/config/index.js +255 -0
- package/connection/connect.js +849 -0
- package/connection/disconnect.js +180 -0
- package/connection/discovery.js +321 -0
- package/connection/index.js +76 -0
- package/connection/proxy.js +148 -0
- package/connection/resilience.js +428 -0
- package/connection/security.js +232 -0
- package/connection/state.js +369 -0
- package/connection/tunnel.js +691 -0
- package/consumer.js +132 -0
- package/cosmjs-setup.js +1884 -0
- package/defaults.js +366 -0
- package/disk-cache.js +107 -0
- package/dist/client.d.ts +108 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +400 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/errors/index.js +112 -0
- package/errors.js +218 -0
- package/examples/README.md +64 -0
- package/examples/connect-direct.mjs +106 -0
- package/examples/connect-plan.mjs +125 -0
- package/examples/error-handling.mjs +109 -0
- package/examples/query-nodes.mjs +94 -0
- package/examples/wallet-basics.mjs +61 -0
- package/generated/amino/amino.ts +9 -0
- package/generated/cosmos/base/v1beta1/coin.ts +365 -0
- package/generated/cosmos_proto/cosmos.ts +323 -0
- package/generated/gogoproto/gogo.ts +9 -0
- package/generated/google/protobuf/descriptor.ts +7601 -0
- package/generated/google/protobuf/duration.ts +208 -0
- package/generated/google/protobuf/timestamp.ts +238 -0
- package/generated/sentinel/lease/v1/events.ts +924 -0
- package/generated/sentinel/lease/v1/lease.ts +292 -0
- package/generated/sentinel/lease/v1/msg.ts +949 -0
- package/generated/sentinel/lease/v1/params.ts +164 -0
- package/generated/sentinel/node/v3/events.ts +881 -0
- package/generated/sentinel/node/v3/msg.ts +1002 -0
- package/generated/sentinel/node/v3/node.ts +263 -0
- package/generated/sentinel/node/v3/params.ts +183 -0
- package/generated/sentinel/plan/v3/events.ts +675 -0
- package/generated/sentinel/plan/v3/msg.ts +1191 -0
- package/generated/sentinel/plan/v3/plan.ts +283 -0
- package/generated/sentinel/provider/v2/events.ts +171 -0
- package/generated/sentinel/provider/v2/msg.ts +480 -0
- package/generated/sentinel/provider/v2/params.ts +131 -0
- package/generated/sentinel/provider/v2/provider.ts +246 -0
- package/generated/sentinel/session/v3/events.ts +480 -0
- package/generated/sentinel/session/v3/msg.ts +616 -0
- package/generated/sentinel/session/v3/params.ts +260 -0
- package/generated/sentinel/session/v3/proof.ts +180 -0
- package/generated/sentinel/session/v3/session.ts +384 -0
- package/generated/sentinel/subscription/v3/events.ts +1181 -0
- package/generated/sentinel/subscription/v3/msg.ts +1305 -0
- package/generated/sentinel/subscription/v3/params.ts +167 -0
- package/generated/sentinel/subscription/v3/subscription.ts +315 -0
- package/generated/sentinel/types/v1/bandwidth.ts +124 -0
- package/generated/sentinel/types/v1/price.ts +149 -0
- package/generated/sentinel/types/v1/renewal.ts +87 -0
- package/generated/sentinel/types/v1/status.ts +54 -0
- package/generated/typeRegistry.ts +27 -0
- package/index.js +486 -0
- package/node-connect.js +3015 -0
- package/operator.js +134 -0
- package/package.json +113 -0
- package/plan-operations.js +199 -0
- package/preflight.js +352 -0
- package/pricing/index.js +262 -0
- package/proto/amino/amino.proto +84 -0
- package/proto/cosmos/base/v1beta1/coin.proto +61 -0
- package/proto/cosmos_proto/cosmos.proto +112 -0
- package/proto/gogoproto/gogo.proto +145 -0
- package/proto/google/api/annotations.proto +31 -0
- package/proto/google/api/http.proto +370 -0
- package/proto/google/protobuf/any.proto +106 -0
- package/proto/google/protobuf/duration.proto +115 -0
- package/proto/google/protobuf/timestamp.proto +145 -0
- package/proto/sentinel/lease/v1/events.proto +52 -0
- package/proto/sentinel/lease/v1/genesis.proto +15 -0
- package/proto/sentinel/lease/v1/lease.proto +25 -0
- package/proto/sentinel/lease/v1/msg.proto +62 -0
- package/proto/sentinel/lease/v1/params.proto +17 -0
- package/proto/sentinel/node/v3/events.proto +50 -0
- package/proto/sentinel/node/v3/genesis.proto +15 -0
- package/proto/sentinel/node/v3/msg.proto +63 -0
- package/proto/sentinel/node/v3/node.proto +27 -0
- package/proto/sentinel/node/v3/params.proto +21 -0
- package/proto/sentinel/node/v3/querier.proto +63 -0
- package/proto/sentinel/plan/v3/events.proto +41 -0
- package/proto/sentinel/plan/v3/genesis.proto +21 -0
- package/proto/sentinel/plan/v3/msg.proto +83 -0
- package/proto/sentinel/plan/v3/plan.proto +32 -0
- package/proto/sentinel/plan/v3/querier.proto +53 -0
- package/proto/sentinel/provider/v2/events.proto +16 -0
- package/proto/sentinel/provider/v2/genesis.proto +15 -0
- package/proto/sentinel/provider/v2/msg.proto +35 -0
- package/proto/sentinel/provider/v2/params.proto +17 -0
- package/proto/sentinel/provider/v2/provider.proto +24 -0
- package/proto/sentinel/provider/v3/genesis.proto +15 -0
- package/proto/sentinel/provider/v3/params.proto +13 -0
- package/proto/sentinel/session/v3/events.proto +30 -0
- package/proto/sentinel/session/v3/genesis.proto +15 -0
- package/proto/sentinel/session/v3/msg.proto +50 -0
- package/proto/sentinel/session/v3/params.proto +25 -0
- package/proto/sentinel/session/v3/proof.proto +25 -0
- package/proto/sentinel/session/v3/querier.proto +100 -0
- package/proto/sentinel/session/v3/session.proto +50 -0
- package/proto/sentinel/subscription/v2/allocation.proto +21 -0
- package/proto/sentinel/subscription/v2/payout.proto +22 -0
- package/proto/sentinel/subscription/v3/events.proto +65 -0
- package/proto/sentinel/subscription/v3/genesis.proto +17 -0
- package/proto/sentinel/subscription/v3/msg.proto +83 -0
- package/proto/sentinel/subscription/v3/params.proto +21 -0
- package/proto/sentinel/subscription/v3/subscription.proto +33 -0
- package/proto/sentinel/types/v1/bandwidth.proto +19 -0
- package/proto/sentinel/types/v1/price.proto +21 -0
- package/proto/sentinel/types/v1/renewal.proto +21 -0
- package/proto/sentinel/types/v1/status.proto +16 -0
- package/protocol/encoding.js +341 -0
- package/protocol/events.js +361 -0
- package/protocol/handshake.js +297 -0
- package/protocol/index.js +15 -0
- package/protocol/messages.js +346 -0
- package/protocol/plans.js +199 -0
- package/protocol/v2ray.js +268 -0
- package/protocol/v3.js +723 -0
- package/protocol/wireguard.js +125 -0
- package/security/index.js +132 -0
- package/session-manager.js +329 -0
- package/session-tracker.js +80 -0
- package/setup.js +376 -0
- package/speedtest/index.js +528 -0
- package/speedtest.js +567 -0
- package/src/client.ts +502 -0
- package/src/index.ts +20 -0
- package/state/index.js +347 -0
- package/state.js +516 -0
- package/test-all-chain-ops.js +493 -0
- package/test-all-logic.js +199 -0
- package/test-all-msg-types.js +292 -0
- package/test-every-connection.js +208 -0
- package/test-feegrant-connect.js +98 -0
- package/test-logic.js +148 -0
- package/test-mainnet.js +176 -0
- package/test-plan-lifecycle.js +335 -0
- package/tls-trust.js +132 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +34 -0
- package/types/chain.d.ts +746 -0
- package/types/connection.d.ts +425 -0
- package/types/errors.d.ts +174 -0
- package/types/index.d.ts +1380 -0
- package/types/nodes.d.ts +187 -0
- package/types/pricing.d.ts +156 -0
- package/types/protocol.d.ts +332 -0
- package/types/session.d.ts +236 -0
- package/types/settings.d.ts +192 -0
- package/v3protocol.js +1053 -0
- package/wallet/index.js +153 -0
- package/wireguard.js +307 -0
package/cli/index.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sentinel dVPN CLI
|
|
5
|
+
*
|
|
6
|
+
* Command-line interface for the Sentinel SDK.
|
|
7
|
+
* Zero external dependencies — Node.js built-ins + SDK imports only.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* sentinel <command> [options]
|
|
11
|
+
* sentinel nodes --country US --type wireguard
|
|
12
|
+
* sentinel status https://node.example.com:8585
|
|
13
|
+
* sentinel balance
|
|
14
|
+
* sentinel connect sentnode1abc... --gb 2
|
|
15
|
+
* sentinel disconnect
|
|
16
|
+
* sentinel speedtest
|
|
17
|
+
* sentinel google-check
|
|
18
|
+
* sentinel version
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// ─── SDK Imports ─────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
// High-level API
|
|
25
|
+
listNodes,
|
|
26
|
+
connectDirect,
|
|
27
|
+
disconnect,
|
|
28
|
+
isConnected,
|
|
29
|
+
getStatus,
|
|
30
|
+
registerCleanupHandlers,
|
|
31
|
+
verifyConnection,
|
|
32
|
+
filterNodes,
|
|
33
|
+
// Wallet & Chain
|
|
34
|
+
createWallet,
|
|
35
|
+
createClient,
|
|
36
|
+
getBalance,
|
|
37
|
+
formatDvpn,
|
|
38
|
+
// Protocol
|
|
39
|
+
nodeStatusV3,
|
|
40
|
+
// Speed testing
|
|
41
|
+
speedtestDirect,
|
|
42
|
+
// Defaults
|
|
43
|
+
SDK_VERSION,
|
|
44
|
+
DEFAULT_RPC,
|
|
45
|
+
DEFAULT_LCD,
|
|
46
|
+
} from '../index.js';
|
|
47
|
+
|
|
48
|
+
// ─── CLI Modules ─────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
import {
|
|
51
|
+
loadConfig,
|
|
52
|
+
ensureMnemonic,
|
|
53
|
+
getConfigValue,
|
|
54
|
+
} from './config.js';
|
|
55
|
+
|
|
56
|
+
import {
|
|
57
|
+
printJson,
|
|
58
|
+
printTable,
|
|
59
|
+
printStep,
|
|
60
|
+
printHeader,
|
|
61
|
+
die,
|
|
62
|
+
green,
|
|
63
|
+
red,
|
|
64
|
+
yellow,
|
|
65
|
+
cyan,
|
|
66
|
+
bold,
|
|
67
|
+
dim,
|
|
68
|
+
gray,
|
|
69
|
+
pass,
|
|
70
|
+
fail,
|
|
71
|
+
warn,
|
|
72
|
+
fmtNum,
|
|
73
|
+
truncAddr,
|
|
74
|
+
} from './output.js';
|
|
75
|
+
|
|
76
|
+
// ─── Argument Parsing ────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
const argv = process.argv.slice(2);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the value of a --flag from argv.
|
|
82
|
+
* Returns the next argument after the flag, or defaultVal if not found.
|
|
83
|
+
* If the flag exists but has no value (or next arg is also a flag), returns true.
|
|
84
|
+
* @param {string} name - Flag name without --
|
|
85
|
+
* @param {*} [defaultVal=undefined]
|
|
86
|
+
* @returns {*}
|
|
87
|
+
*/
|
|
88
|
+
function flag(name, defaultVal) {
|
|
89
|
+
const idx = argv.indexOf(`--${name}`);
|
|
90
|
+
if (idx === -1) return defaultVal;
|
|
91
|
+
const next = argv[idx + 1];
|
|
92
|
+
if (next === undefined || next.startsWith('--')) return true;
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a boolean flag is present (no value expected).
|
|
98
|
+
* @param {string} name
|
|
99
|
+
* @returns {boolean}
|
|
100
|
+
*/
|
|
101
|
+
function hasFlag(name) {
|
|
102
|
+
return argv.includes(`--${name}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Get positional arguments (non-flag args after the command). */
|
|
106
|
+
function positional(index) {
|
|
107
|
+
let pos = 0;
|
|
108
|
+
for (let i = 1; i < argv.length; i++) {
|
|
109
|
+
if (argv[i].startsWith('--')) {
|
|
110
|
+
// Skip flag and its value
|
|
111
|
+
const next = argv[i + 1];
|
|
112
|
+
if (next && !next.startsWith('--')) i++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (pos === index) return argv[i];
|
|
116
|
+
pos++;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const command = argv[0];
|
|
122
|
+
const jsonMode = hasFlag('json');
|
|
123
|
+
|
|
124
|
+
// ─── Wallet Helpers ──────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
async function getWalletFromConfig() {
|
|
127
|
+
const mnemonic = await ensureMnemonic();
|
|
128
|
+
const { wallet, account } = await createWallet(mnemonic);
|
|
129
|
+
return { wallet, account, address: account.address, mnemonic };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function getRpcClient() {
|
|
133
|
+
const { wallet, account, address, mnemonic } = await getWalletFromConfig();
|
|
134
|
+
const rpc = getConfigValue('rpc', flag('rpc'));
|
|
135
|
+
const client = await createClient(rpc, wallet);
|
|
136
|
+
return { client, wallet, account, address, mnemonic };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─── Commands ────────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
const commands = {};
|
|
142
|
+
|
|
143
|
+
// ─── sentinel nodes ──────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
commands.nodes = async function nodesCmd() {
|
|
146
|
+
const country = flag('country');
|
|
147
|
+
const type = flag('type');
|
|
148
|
+
const limit = parseInt(flag('limit', '50'), 10);
|
|
149
|
+
const lcd = getConfigValue('lcd', flag('lcd'));
|
|
150
|
+
|
|
151
|
+
if (!jsonMode) printStep('Fetching', 'Querying online nodes from chain...');
|
|
152
|
+
|
|
153
|
+
let nodes = await listNodes({
|
|
154
|
+
lcdUrl: lcd,
|
|
155
|
+
serviceType: type || undefined,
|
|
156
|
+
maxNodes: 5000,
|
|
157
|
+
noCache: true,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Apply country filter
|
|
161
|
+
if (country) {
|
|
162
|
+
nodes = filterNodes(nodes, { country });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Limit results
|
|
166
|
+
const total = nodes.length;
|
|
167
|
+
nodes = nodes.slice(0, limit);
|
|
168
|
+
|
|
169
|
+
if (jsonMode) {
|
|
170
|
+
printJson(nodes);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
printHeader(`Online Nodes (${fmtNum(total)} total, showing ${nodes.length})`);
|
|
175
|
+
|
|
176
|
+
if (nodes.length === 0) {
|
|
177
|
+
console.log(' No nodes found matching filters.');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const headers = ['Address', 'Moniker', 'Type', 'Country', 'City', 'Peers'];
|
|
182
|
+
const rows = nodes.map(n => [
|
|
183
|
+
truncAddr(n.address),
|
|
184
|
+
(n.moniker || '').slice(0, 20),
|
|
185
|
+
n.serviceType === 'wireguard' ? green('WG') : cyan('V2'),
|
|
186
|
+
n.country || n.location?.country || '?',
|
|
187
|
+
(n.city || n.location?.city || '').slice(0, 15),
|
|
188
|
+
String(n.peers ?? '?'),
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
printTable(headers, rows, { align: [0, 0, 0, 0, 0, 1] });
|
|
192
|
+
console.log();
|
|
193
|
+
|
|
194
|
+
if (total > limit) {
|
|
195
|
+
console.log(dim(` Showing ${nodes.length} of ${fmtNum(total)}. Use --limit to see more.`));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// ─── sentinel status <nodeUrl> ───────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
commands.status = async function statusCmd() {
|
|
202
|
+
const nodeUrl = positional(0);
|
|
203
|
+
if (!nodeUrl) {
|
|
204
|
+
die('Usage: sentinel status <nodeUrl>\n Example: sentinel status https://1.2.3.4:8585');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!jsonMode) printStep('Querying', nodeUrl);
|
|
208
|
+
|
|
209
|
+
const status = await nodeStatusV3(nodeUrl);
|
|
210
|
+
|
|
211
|
+
if (jsonMode) {
|
|
212
|
+
printJson(status);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
printHeader('Node Status');
|
|
217
|
+
console.log(` Moniker: ${bold(status.moniker || '(none)')}`);
|
|
218
|
+
console.log(` Type: ${status.type === 'wireguard' ? green('WireGuard') : cyan('V2Ray')}`);
|
|
219
|
+
console.log(` Country: ${status.location.country || '?'} / ${status.location.city || '?'}`);
|
|
220
|
+
console.log(` Peers: ${status.peers}`);
|
|
221
|
+
console.log(` Bandwidth: ${dim('down')} ${fmtNum(Math.round(status.bandwidth.download / 1024))} KB/s ${dim('up')} ${fmtNum(Math.round(status.bandwidth.upload / 1024))} KB/s`);
|
|
222
|
+
if (status.clockDriftSec !== null) {
|
|
223
|
+
const drift = status.clockDriftSec;
|
|
224
|
+
const driftStr = Math.abs(drift) > 120 ? red(`${drift}s`) : green(`${drift}s`);
|
|
225
|
+
console.log(` Clock drift: ${driftStr}`);
|
|
226
|
+
}
|
|
227
|
+
console.log();
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// ─── sentinel balance ────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
commands.balance = async function balanceCmd() {
|
|
233
|
+
const { client, address } = await getRpcClient();
|
|
234
|
+
|
|
235
|
+
if (!jsonMode) printStep('Querying', `Balance for ${truncAddr(address)}`);
|
|
236
|
+
|
|
237
|
+
const balance = await getBalance(client, address);
|
|
238
|
+
|
|
239
|
+
if (jsonMode) {
|
|
240
|
+
printJson({ address, udvpn: balance.udvpn, p2p: balance.dvpn });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
printHeader('Wallet Balance');
|
|
245
|
+
console.log(` Address: ${bold(address)}`);
|
|
246
|
+
console.log(` Balance: ${green(formatDvpn(balance.udvpn))} ${dim(`(${fmtNum(balance.udvpn)} udvpn)`)}`);
|
|
247
|
+
console.log();
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// ─── sentinel connect <nodeAddress> ──────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
commands.connect = async function connectCmd() {
|
|
253
|
+
const nodeAddress = positional(0);
|
|
254
|
+
if (!nodeAddress) {
|
|
255
|
+
die('Usage: sentinel connect <nodeAddress> [--gb N]\n Example: sentinel connect sentnode1abc...xyz --gb 2');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
registerCleanupHandlers();
|
|
259
|
+
|
|
260
|
+
const { mnemonic } = await getWalletFromConfig();
|
|
261
|
+
const gb = parseInt(getConfigValue('gigabytes', flag('gb')), 10);
|
|
262
|
+
const rpc = getConfigValue('rpc', flag('rpc'));
|
|
263
|
+
const lcd = getConfigValue('lcd', flag('lcd'));
|
|
264
|
+
|
|
265
|
+
if (!jsonMode) {
|
|
266
|
+
printHeader('Connecting');
|
|
267
|
+
console.log(` Node: ${bold(nodeAddress)}`);
|
|
268
|
+
console.log(` Gigabytes: ${gb}`);
|
|
269
|
+
console.log();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const result = await connectDirect({
|
|
273
|
+
mnemonic,
|
|
274
|
+
nodeAddress,
|
|
275
|
+
gigabytes: gb,
|
|
276
|
+
rpcUrl: rpc,
|
|
277
|
+
lcdUrl: lcd,
|
|
278
|
+
onProgress: (step, detail) => {
|
|
279
|
+
if (!jsonMode) printStep(step, detail);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (jsonMode) {
|
|
284
|
+
printJson(serializeResult(result));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log();
|
|
289
|
+
console.log(pass('Connected successfully'));
|
|
290
|
+
if (result.serviceType) console.log(` Type: ${result.serviceType}`);
|
|
291
|
+
if (result.sessionId) console.log(` Session: ${result.sessionId}`);
|
|
292
|
+
if (result.socksPort) console.log(` SOCKS5: localhost:${result.socksPort}`);
|
|
293
|
+
console.log();
|
|
294
|
+
console.log(dim(' Press Ctrl+C to disconnect.'));
|
|
295
|
+
|
|
296
|
+
// Keep alive until Ctrl+C
|
|
297
|
+
await new Promise(() => {});
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// ─── sentinel disconnect ────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
commands.disconnect = async function disconnectCmd() {
|
|
303
|
+
if (!jsonMode) printStep('Disconnecting', '...');
|
|
304
|
+
|
|
305
|
+
await disconnect();
|
|
306
|
+
|
|
307
|
+
if (jsonMode) {
|
|
308
|
+
printJson({ disconnected: true });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
console.log(pass('Disconnected'));
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// ─── sentinel speedtest ──────────────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
commands.speedtest = async function speedtestCmd() {
|
|
318
|
+
if (!jsonMode) printStep('Testing', 'Running baseline speed test...');
|
|
319
|
+
|
|
320
|
+
const result = await speedtestDirect();
|
|
321
|
+
|
|
322
|
+
if (jsonMode) {
|
|
323
|
+
printJson(result);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
printHeader('Baseline Speed Test');
|
|
328
|
+
if (result.downloadMbps != null) {
|
|
329
|
+
console.log(` Download: ${bold(result.downloadMbps.toFixed(2))} Mbps`);
|
|
330
|
+
}
|
|
331
|
+
if (result.uploadMbps != null) {
|
|
332
|
+
console.log(` Upload: ${bold(result.uploadMbps.toFixed(2))} Mbps`);
|
|
333
|
+
}
|
|
334
|
+
if (result.latencyMs != null) {
|
|
335
|
+
console.log(` Latency: ${bold(String(result.latencyMs))} ms`);
|
|
336
|
+
}
|
|
337
|
+
if (result.ip) {
|
|
338
|
+
console.log(` IP: ${result.ip}`);
|
|
339
|
+
}
|
|
340
|
+
console.log();
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// ─── sentinel google-check ──────────────────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
commands['google-check'] = async function googleCheckCmd() {
|
|
346
|
+
if (!jsonMode) printStep('Checking', 'Google reachability...');
|
|
347
|
+
|
|
348
|
+
const result = await verifyConnection({ timeoutMs: 10000 });
|
|
349
|
+
|
|
350
|
+
if (jsonMode) {
|
|
351
|
+
printJson(result);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
printHeader('Google Reachability Check');
|
|
356
|
+
if (result.working) {
|
|
357
|
+
console.log(pass(`Reachable — IP: ${result.vpnIp}`));
|
|
358
|
+
} else {
|
|
359
|
+
console.log(fail(`Not reachable${result.error ? `: ${result.error}` : ''}`));
|
|
360
|
+
}
|
|
361
|
+
console.log();
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// ─── sentinel version ───────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
commands.version = async function versionCmd() {
|
|
367
|
+
if (jsonMode) {
|
|
368
|
+
printJson({ version: SDK_VERSION });
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
console.log(`Sentinel SDK v${SDK_VERSION}`);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// ─── Help ────────────────────────────────────────────────────────────────────
|
|
375
|
+
|
|
376
|
+
function printHelp() {
|
|
377
|
+
console.log(`
|
|
378
|
+
${bold('Sentinel dVPN CLI')} — command-line tools for Sentinel dVPN
|
|
379
|
+
|
|
380
|
+
${bold('USAGE')}
|
|
381
|
+
sentinel <command> [options]
|
|
382
|
+
|
|
383
|
+
${bold('NODES')}
|
|
384
|
+
nodes List online nodes
|
|
385
|
+
--country <code> Filter by country (e.g. US, DE)
|
|
386
|
+
--type <wireguard|v2ray> Filter by tunnel type
|
|
387
|
+
--limit <n> Max results (default: 50)
|
|
388
|
+
status <nodeUrl> Query node status
|
|
389
|
+
|
|
390
|
+
${bold('WALLET')}
|
|
391
|
+
balance Show wallet balance
|
|
392
|
+
|
|
393
|
+
${bold('CONNECTION')}
|
|
394
|
+
connect <nodeAddress> [--gb N] Connect to a node (default: 1 GB)
|
|
395
|
+
disconnect Disconnect VPN tunnel
|
|
396
|
+
|
|
397
|
+
${bold('DIAGNOSTICS')}
|
|
398
|
+
speedtest Run baseline speed test
|
|
399
|
+
google-check Check internet reachability
|
|
400
|
+
|
|
401
|
+
${bold('INFO')}
|
|
402
|
+
version Show SDK version
|
|
403
|
+
help Show this help
|
|
404
|
+
|
|
405
|
+
${bold('GLOBAL OPTIONS')}
|
|
406
|
+
--json Output as JSON (for scripting)
|
|
407
|
+
--rpc <url> Override RPC endpoint
|
|
408
|
+
--lcd <url> Override LCD endpoint
|
|
409
|
+
|
|
410
|
+
${bold('CONFIGURATION')}
|
|
411
|
+
Config file: ~/.sentinel/config.json
|
|
412
|
+
Mnemonic can also be set via MNEMONIC environment variable.
|
|
413
|
+
|
|
414
|
+
${bold('EXAMPLES')}
|
|
415
|
+
sentinel nodes --country US --type wireguard
|
|
416
|
+
sentinel status https://1.2.3.4:8585
|
|
417
|
+
sentinel balance
|
|
418
|
+
sentinel connect sentnode1abc... --gb 2
|
|
419
|
+
sentinel nodes --json | jq '.[] | .address'
|
|
420
|
+
`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── BigInt Serializer ───────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
function serializeResult(obj) {
|
|
426
|
+
if (obj === null || obj === undefined) return obj;
|
|
427
|
+
if (typeof obj === 'bigint') return obj.toString();
|
|
428
|
+
if (Array.isArray(obj)) return obj.map(serializeResult);
|
|
429
|
+
if (typeof obj === 'object') {
|
|
430
|
+
const out = {};
|
|
431
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
432
|
+
out[k] = serializeResult(v);
|
|
433
|
+
}
|
|
434
|
+
return out;
|
|
435
|
+
}
|
|
436
|
+
return obj;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ─── Command Router ──────────────────────────────────────────────────────────
|
|
440
|
+
|
|
441
|
+
async function main() {
|
|
442
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
443
|
+
printHelp();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const handler = commands[command];
|
|
448
|
+
if (!handler) {
|
|
449
|
+
die(`Unknown command: ${command}\nRun 'sentinel help' for usage.`);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
await handler();
|
|
454
|
+
} catch (err) {
|
|
455
|
+
if (jsonMode) {
|
|
456
|
+
printJson({ error: err.message, code: err.code || 'UNKNOWN' });
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
die(err.message);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
main();
|
package/cli/output.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel CLI — Output Formatting
|
|
3
|
+
*
|
|
4
|
+
* Handles JSON vs human-readable output, colored text, and aligned tables.
|
|
5
|
+
* No external dependencies — ANSI escape codes only.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ─── ANSI Color Codes ───────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
11
|
+
|
|
12
|
+
const CODES = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
cyan: '\x1b[36m',
|
|
20
|
+
gray: '\x1b[90m',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function c(code, text) {
|
|
24
|
+
if (!SUPPORTS_COLOR) return text;
|
|
25
|
+
return `${CODES[code]}${text}${CODES.reset}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Public Color Helpers ────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export const green = (t) => c('green', t);
|
|
31
|
+
export const red = (t) => c('red', t);
|
|
32
|
+
export const yellow = (t) => c('yellow', t);
|
|
33
|
+
export const cyan = (t) => c('cyan', t);
|
|
34
|
+
export const bold = (t) => c('bold', t);
|
|
35
|
+
export const dim = (t) => c('dim', t);
|
|
36
|
+
export const gray = (t) => c('gray', t);
|
|
37
|
+
|
|
38
|
+
// ─── Status Indicators ──────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export function pass(text) {
|
|
41
|
+
return `${green('✓')} ${text}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function fail(text) {
|
|
45
|
+
return `${red('✗')} ${text}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function warn(text) {
|
|
49
|
+
return `${yellow('!')} ${text}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── JSON Output ─────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Print data as JSON. Handles BigInt serialization.
|
|
56
|
+
* @param {*} data
|
|
57
|
+
*/
|
|
58
|
+
export function printJson(data) {
|
|
59
|
+
const json = JSON.stringify(data, (key, val) => {
|
|
60
|
+
if (typeof val === 'bigint') return val.toString();
|
|
61
|
+
return val;
|
|
62
|
+
}, 2);
|
|
63
|
+
console.log(json);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── Table Output ────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Print an aligned table from rows of objects.
|
|
70
|
+
* @param {string[]} headers - Column headers
|
|
71
|
+
* @param {string[][]} rows - Array of row arrays (each row = array of cell strings)
|
|
72
|
+
* @param {object} [opts] - Options
|
|
73
|
+
* @param {number[]} [opts.align] - Per-column alignment: 0=left, 1=right
|
|
74
|
+
*/
|
|
75
|
+
export function printTable(headers, rows, opts = {}) {
|
|
76
|
+
const align = opts.align || [];
|
|
77
|
+
|
|
78
|
+
// Calculate column widths
|
|
79
|
+
const widths = headers.map((h, i) => {
|
|
80
|
+
let max = stripAnsi(h).length;
|
|
81
|
+
for (const row of rows) {
|
|
82
|
+
const cell = row[i] != null ? String(row[i]) : '';
|
|
83
|
+
const len = stripAnsi(cell).length;
|
|
84
|
+
if (len > max) max = len;
|
|
85
|
+
}
|
|
86
|
+
return max;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Print header
|
|
90
|
+
const headerLine = headers.map((h, i) => padCell(h, widths[i], align[i])).join(' ');
|
|
91
|
+
console.log(bold(headerLine));
|
|
92
|
+
console.log(dim('─'.repeat(widths.reduce((s, w) => s + w, 0) + (widths.length - 1) * 2)));
|
|
93
|
+
|
|
94
|
+
// Print rows
|
|
95
|
+
for (const row of rows) {
|
|
96
|
+
const line = headers.map((_, i) => {
|
|
97
|
+
const cell = row[i] != null ? String(row[i]) : '';
|
|
98
|
+
return padCell(cell, widths[i], align[i]);
|
|
99
|
+
}).join(' ');
|
|
100
|
+
console.log(line);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Pad a cell to a given width, respecting ANSI codes.
|
|
106
|
+
* @param {string} text
|
|
107
|
+
* @param {number} width
|
|
108
|
+
* @param {number} [alignment=0] - 0=left, 1=right
|
|
109
|
+
* @returns {string}
|
|
110
|
+
*/
|
|
111
|
+
function padCell(text, width, alignment = 0) {
|
|
112
|
+
const visibleLen = stripAnsi(text).length;
|
|
113
|
+
const padding = Math.max(0, width - visibleLen);
|
|
114
|
+
if (alignment === 1) return ' '.repeat(padding) + text;
|
|
115
|
+
return text + ' '.repeat(padding);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Strip ANSI escape codes from a string to get visible length.
|
|
120
|
+
* @param {string} str
|
|
121
|
+
* @returns {string}
|
|
122
|
+
*/
|
|
123
|
+
function stripAnsi(str) {
|
|
124
|
+
// eslint-disable-next-line no-control-regex
|
|
125
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Progress / Spinner ─────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Print a progress step (for connect flow, etc).
|
|
132
|
+
* @param {string} step - Step name
|
|
133
|
+
* @param {string} detail - Detail text
|
|
134
|
+
*/
|
|
135
|
+
export function printStep(step, detail) {
|
|
136
|
+
process.stderr.write(` ${cyan('→')} ${bold(step)} ${detail}\n`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Print a section header.
|
|
141
|
+
* @param {string} title
|
|
142
|
+
*/
|
|
143
|
+
export function printHeader(title) {
|
|
144
|
+
console.log();
|
|
145
|
+
console.log(bold(` ${title}`));
|
|
146
|
+
console.log(dim(' ' + '─'.repeat(title.length + 2)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Error Output ────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Print an error message to stderr and exit.
|
|
153
|
+
* @param {string} msg
|
|
154
|
+
* @param {number} [code=1]
|
|
155
|
+
*/
|
|
156
|
+
export function die(msg, code = 1) {
|
|
157
|
+
process.stderr.write(`${red('Error:')} ${msg}\n`);
|
|
158
|
+
process.exit(code);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── Formatting Helpers ─────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Format a number with locale separators (e.g. 1,234).
|
|
165
|
+
* @param {number} n
|
|
166
|
+
* @returns {string}
|
|
167
|
+
*/
|
|
168
|
+
export function fmtNum(n) {
|
|
169
|
+
return Number(n).toLocaleString();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Truncate an address for display: "sent1abc...xyz".
|
|
174
|
+
* @param {string} addr
|
|
175
|
+
* @param {number} [start=12]
|
|
176
|
+
* @param {number} [end=6]
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
export function truncAddr(addr, start = 12, end = 6) {
|
|
180
|
+
if (!addr || addr.length <= start + end + 3) return addr || '';
|
|
181
|
+
return `${addr.slice(0, start)}...${addr.slice(-end)}`;
|
|
182
|
+
}
|