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/ai-path/cli.js
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sentinel AI Connect — CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Usage: npx sentinel-ai <command>
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* setup Check dependencies
|
|
9
|
+
* wallet create Generate new wallet
|
|
10
|
+
* wallet balance Check P2P balance
|
|
11
|
+
* wallet import <mnemonic...> Import existing wallet
|
|
12
|
+
* connect [options] Connect to VPN
|
|
13
|
+
* disconnect Disconnect from VPN
|
|
14
|
+
* status Show connection status
|
|
15
|
+
* nodes [options] List available nodes
|
|
16
|
+
* help Show this message
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'fs';
|
|
20
|
+
import { resolve, dirname } from 'path';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
|
+
|
|
23
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
|
|
25
|
+
// ─── ANSI Colors ────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const c = {
|
|
28
|
+
reset: '\x1b[0m',
|
|
29
|
+
bold: '\x1b[1m',
|
|
30
|
+
dim: '\x1b[2m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
green: '\x1b[32m',
|
|
33
|
+
yellow: '\x1b[33m',
|
|
34
|
+
blue: '\x1b[34m',
|
|
35
|
+
magenta: '\x1b[35m',
|
|
36
|
+
cyan: '\x1b[36m',
|
|
37
|
+
white: '\x1b[37m',
|
|
38
|
+
bgBlue: '\x1b[44m',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const ok = `${c.green}+${c.reset}`;
|
|
42
|
+
const warn = `${c.yellow}!${c.reset}`;
|
|
43
|
+
const err = `${c.red}x${c.reset}`;
|
|
44
|
+
const info = `${c.cyan}>${c.reset}`;
|
|
45
|
+
|
|
46
|
+
// ─── .env Loader ────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function loadEnv() {
|
|
49
|
+
const envPath = resolve(__dirname, '.env');
|
|
50
|
+
if (!existsSync(envPath)) return;
|
|
51
|
+
try {
|
|
52
|
+
const lines = readFileSync(envPath, 'utf-8').split('\n');
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
56
|
+
const eq = trimmed.indexOf('=');
|
|
57
|
+
if (eq === -1) continue;
|
|
58
|
+
const key = trimmed.slice(0, eq).trim();
|
|
59
|
+
const val = trimmed.slice(eq + 1).trim();
|
|
60
|
+
if (!process.env[key]) {
|
|
61
|
+
process.env[key] = val;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// .env read failed — non-critical
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Argument Parser ────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
function parseArgs(argv) {
|
|
72
|
+
const args = argv.slice(2);
|
|
73
|
+
const command = [];
|
|
74
|
+
const flags = {};
|
|
75
|
+
const positional = [];
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < args.length; i++) {
|
|
78
|
+
const arg = args[i];
|
|
79
|
+
if (arg.startsWith('--')) {
|
|
80
|
+
const key = arg.slice(2);
|
|
81
|
+
const next = args[i + 1];
|
|
82
|
+
if (next && !next.startsWith('--')) {
|
|
83
|
+
flags[key] = next;
|
|
84
|
+
i++;
|
|
85
|
+
} else {
|
|
86
|
+
flags[key] = true;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
positional.push(arg);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { positional, flags };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Banner ─────────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
function banner() {
|
|
99
|
+
console.log('');
|
|
100
|
+
console.log(`${c.bold}${c.cyan} Sentinel AI Connect${c.reset} ${c.dim}v0.2.0${c.reset}`);
|
|
101
|
+
console.log(`${c.dim} Decentralized VPN for AI agents${c.reset}`);
|
|
102
|
+
console.log('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Help ───────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
function showHelp() {
|
|
108
|
+
banner();
|
|
109
|
+
console.log(`${c.bold}USAGE${c.reset}`);
|
|
110
|
+
console.log(` sentinel-ai <command> [options]`);
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log(`${c.bold}COMMANDS${c.reset}`);
|
|
113
|
+
console.log(` ${c.cyan}setup${c.reset} Check dependencies and environment`);
|
|
114
|
+
console.log(` ${c.cyan}wallet create${c.reset} Generate a new wallet`);
|
|
115
|
+
console.log(` ${c.cyan}wallet balance${c.reset} Check P2P token balance`);
|
|
116
|
+
console.log(` ${c.cyan}wallet import${c.reset} <mnemonic...> Import wallet from mnemonic`);
|
|
117
|
+
console.log(` ${c.cyan}connect${c.reset} [options] Connect to VPN`);
|
|
118
|
+
console.log(` ${c.cyan}disconnect${c.reset} Disconnect from VPN`);
|
|
119
|
+
console.log(` ${c.cyan}status${c.reset} Show connection status`);
|
|
120
|
+
console.log(` ${c.cyan}nodes${c.reset} [options] List available nodes`);
|
|
121
|
+
console.log(` ${c.cyan}help${c.reset} Show this message`);
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log(`${c.bold}CONNECT OPTIONS${c.reset}`);
|
|
124
|
+
console.log(` --country <code> Preferred country (e.g. US, DE, JP)`);
|
|
125
|
+
console.log(` --protocol <type> Protocol: wireguard or v2ray`);
|
|
126
|
+
console.log(` --dns <preset> DNS: google, cloudflare, or hns (Handshake)`);
|
|
127
|
+
console.log(` --node <address> Connect to specific node (sentnode1...)`);
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(`${c.bold}NODES OPTIONS${c.reset}`);
|
|
130
|
+
console.log(` --country <code> Filter by country`);
|
|
131
|
+
console.log(` --limit <n> Max nodes to show (default: 20)`);
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(`${c.bold}ENVIRONMENT${c.reset}`);
|
|
134
|
+
console.log(` MNEMONIC BIP39 mnemonic in .env file`);
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(`${c.bold}EXAMPLES${c.reset}`);
|
|
137
|
+
console.log(` ${c.dim}# First time setup${c.reset}`);
|
|
138
|
+
console.log(` sentinel-ai setup`);
|
|
139
|
+
console.log(` sentinel-ai wallet create`);
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log(` ${c.dim}# Connect to VPN${c.reset}`);
|
|
142
|
+
console.log(` sentinel-ai connect`);
|
|
143
|
+
console.log(` sentinel-ai connect --country DE --protocol wireguard`);
|
|
144
|
+
console.log(` sentinel-ai connect --node sentnode1abc...`);
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log(` ${c.dim}# List nodes${c.reset}`);
|
|
147
|
+
console.log(` sentinel-ai nodes --country US --limit 10`);
|
|
148
|
+
console.log('');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─── Command: setup ─────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
async function cmdSetup() {
|
|
154
|
+
banner();
|
|
155
|
+
console.log(`${info} Running environment checks...`);
|
|
156
|
+
console.log('');
|
|
157
|
+
|
|
158
|
+
// Delegate to setup.js which has all the detection logic
|
|
159
|
+
await import('./setup.js');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── Command: wallet create ─────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
async function cmdWalletCreate() {
|
|
165
|
+
banner();
|
|
166
|
+
console.log(`${info} Generating new wallet...`);
|
|
167
|
+
console.log('');
|
|
168
|
+
|
|
169
|
+
const { createWallet } = await import('./index.js');
|
|
170
|
+
const wallet = await createWallet();
|
|
171
|
+
|
|
172
|
+
// Write mnemonic directly to .env — never print it to stdout
|
|
173
|
+
const envPath = resolve(__dirname, '.env');
|
|
174
|
+
const mnemonicLine = `MNEMONIC=${wallet.mnemonic}`;
|
|
175
|
+
if (existsSync(envPath)) {
|
|
176
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
177
|
+
if (content.includes('MNEMONIC=')) {
|
|
178
|
+
// Replace existing MNEMONIC line
|
|
179
|
+
const updated = content.replace(/^MNEMONIC=.*$/m, mnemonicLine);
|
|
180
|
+
writeFileSync(envPath, updated, 'utf-8');
|
|
181
|
+
} else {
|
|
182
|
+
appendFileSync(envPath, `\n${mnemonicLine}\n`, 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
writeFileSync(envPath, `${mnemonicLine}\n`, 'utf-8');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(`${ok} ${c.bold}Wallet created${c.reset}`);
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log(`${c.bold} Address:${c.reset} ${c.green}${wallet.address}${c.reset}`);
|
|
191
|
+
console.log(`${ok} Mnemonic saved to .env (24 words). ${c.red}${c.bold}NEVER share this.${c.reset}`);
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log(`${info} Next steps:`);
|
|
194
|
+
console.log(` 1. Fund the wallet with P2P tokens`);
|
|
195
|
+
console.log(` 2. Connect: ${c.cyan}sentinel-ai connect${c.reset}`);
|
|
196
|
+
console.log('');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─── Command: wallet balance ────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
async function cmdWalletBalance() {
|
|
202
|
+
banner();
|
|
203
|
+
|
|
204
|
+
const mnemonic = process.env.MNEMONIC;
|
|
205
|
+
delete process.env.MNEMONIC; // Don't keep mnemonic in environment after reading
|
|
206
|
+
if (!mnemonic) {
|
|
207
|
+
console.log(`${err} No MNEMONIC in .env file.`);
|
|
208
|
+
console.log(` Run: ${c.cyan}sentinel-ai wallet create${c.reset}`);
|
|
209
|
+
console.log(` Then add the mnemonic to your .env file.`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(`${info} Checking balance...`);
|
|
214
|
+
|
|
215
|
+
const { getBalance } = await import('./index.js');
|
|
216
|
+
const bal = await getBalance(mnemonic);
|
|
217
|
+
|
|
218
|
+
console.log('');
|
|
219
|
+
console.log(`${ok} ${c.bold}Wallet Balance${c.reset}`);
|
|
220
|
+
console.log(` Address: ${c.cyan}${bal.address}${c.reset}`);
|
|
221
|
+
console.log(` Balance: ${c.bold}${bal.p2p}${c.reset} (${bal.udvpn.toLocaleString()} udvpn)`);
|
|
222
|
+
console.log(` Status: ${bal.funded ? `${c.green}Funded` : `${c.red}Insufficient`}${c.reset}`);
|
|
223
|
+
console.log('');
|
|
224
|
+
|
|
225
|
+
if (!bal.funded) {
|
|
226
|
+
console.log(`${warn} Wallet needs P2P tokens to pay for VPN sessions.`);
|
|
227
|
+
console.log(` Send P2P tokens to: ${c.cyan}${bal.address}${c.reset}`);
|
|
228
|
+
console.log('');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ─── Command: wallet import ─────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
async function cmdWalletImport(words) {
|
|
235
|
+
banner();
|
|
236
|
+
|
|
237
|
+
if (!words || words.length === 0) {
|
|
238
|
+
console.log(`${err} Usage: sentinel-ai wallet import <word1 word2 word3 ...>`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const mnemonic = words.join(' ');
|
|
243
|
+
console.log(`${info} Validating mnemonic (${words.length} words)...`);
|
|
244
|
+
|
|
245
|
+
const { importWallet } = await import('./index.js');
|
|
246
|
+
const result = await importWallet(mnemonic);
|
|
247
|
+
|
|
248
|
+
// Write mnemonic directly to .env — never print it to stdout
|
|
249
|
+
const envPath = resolve(__dirname, '.env');
|
|
250
|
+
const mnemonicLine = `MNEMONIC=${mnemonic}`;
|
|
251
|
+
if (existsSync(envPath)) {
|
|
252
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
253
|
+
if (content.includes('MNEMONIC=')) {
|
|
254
|
+
const updated = content.replace(/^MNEMONIC=.*$/m, mnemonicLine);
|
|
255
|
+
writeFileSync(envPath, updated, 'utf-8');
|
|
256
|
+
} else {
|
|
257
|
+
appendFileSync(envPath, `\n${mnemonicLine}\n`, 'utf-8');
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
writeFileSync(envPath, `${mnemonicLine}\n`, 'utf-8');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
console.log('');
|
|
264
|
+
console.log(`${ok} ${c.bold}Wallet imported${c.reset}`);
|
|
265
|
+
console.log(` Address: ${c.green}${result.address}${c.reset}`);
|
|
266
|
+
console.log(`${ok} Mnemonic saved to .env (${words.length} words). ${c.red}${c.bold}NEVER share this.${c.reset}`);
|
|
267
|
+
console.log('');
|
|
268
|
+
console.warn(` ${c.yellow}WARNING: Your mnemonic was passed as command arguments.${c.reset}`);
|
|
269
|
+
console.warn(` ${c.yellow}Clear your shell history: history -c (bash) or rm ~/.bash_history${c.reset}`);
|
|
270
|
+
console.log('');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ─── Command: connect ───────────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
async function cmdConnect(flags) {
|
|
276
|
+
banner();
|
|
277
|
+
|
|
278
|
+
const mnemonic = process.env.MNEMONIC;
|
|
279
|
+
delete process.env.MNEMONIC; // Don't keep mnemonic in environment after reading
|
|
280
|
+
if (!mnemonic) {
|
|
281
|
+
console.log(`${err} No MNEMONIC in .env file.`);
|
|
282
|
+
console.log(` Run: ${c.cyan}sentinel-ai wallet create${c.reset}`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const opts = {
|
|
287
|
+
mnemonic,
|
|
288
|
+
onProgress: (stage, detail) => {
|
|
289
|
+
const icon = stage === 'error' ? err : stage === 'done' ? ok : info;
|
|
290
|
+
console.log(` ${icon} ${c.dim}[${stage}]${c.reset} ${detail}`);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
if (flags.country) opts.country = flags.country;
|
|
295
|
+
if (flags.protocol) opts.protocol = flags.protocol;
|
|
296
|
+
if (flags.dns) opts.dns = flags.dns;
|
|
297
|
+
if (flags.node) opts.nodeAddress = flags.node;
|
|
298
|
+
|
|
299
|
+
console.log(`${info} Connecting to Sentinel dVPN...`);
|
|
300
|
+
if (flags.country) console.log(` Country: ${c.cyan}${flags.country}${c.reset}`);
|
|
301
|
+
if (flags.protocol) console.log(` Protocol: ${c.cyan}${flags.protocol}${c.reset}`);
|
|
302
|
+
if (flags.dns) console.log(` DNS: ${c.cyan}${flags.dns}${c.reset}`);
|
|
303
|
+
if (flags.node) console.log(` Node: ${c.cyan}${flags.node}${c.reset}`);
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
const { connect, disconnect } = await import('./index.js');
|
|
307
|
+
const vpn = await connect(opts);
|
|
308
|
+
|
|
309
|
+
console.log('');
|
|
310
|
+
console.log(`${ok} ${c.bold}${c.green}Connected!${c.reset}`);
|
|
311
|
+
console.log(` Session: ${c.cyan}${vpn.sessionId}${c.reset}`);
|
|
312
|
+
console.log(` Protocol: ${c.cyan}${vpn.protocol}${c.reset}`);
|
|
313
|
+
console.log(` Node: ${c.cyan}${vpn.nodeAddress}${c.reset}`);
|
|
314
|
+
if (vpn.ip) console.log(` IP: ${c.cyan}${vpn.ip}${c.reset}`);
|
|
315
|
+
if (vpn.socksPort) console.log(` SOCKS5: ${c.cyan}127.0.0.1:${vpn.socksPort}${c.reset}`);
|
|
316
|
+
console.log('');
|
|
317
|
+
console.log(`${c.dim} Press Ctrl+C to disconnect${c.reset}`);
|
|
318
|
+
console.log('');
|
|
319
|
+
|
|
320
|
+
// Keep process alive, handle graceful shutdown
|
|
321
|
+
let disconnecting = false;
|
|
322
|
+
|
|
323
|
+
const cleanup = async () => {
|
|
324
|
+
if (disconnecting) return;
|
|
325
|
+
disconnecting = true;
|
|
326
|
+
console.log('');
|
|
327
|
+
console.log(`${info} Disconnecting...`);
|
|
328
|
+
try {
|
|
329
|
+
await disconnect();
|
|
330
|
+
console.log(`${ok} Disconnected.`);
|
|
331
|
+
} catch (e) {
|
|
332
|
+
console.log(`${warn} Disconnect error: ${e.message}`);
|
|
333
|
+
}
|
|
334
|
+
process.exit(0);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
process.on('SIGINT', cleanup);
|
|
338
|
+
process.on('SIGTERM', cleanup);
|
|
339
|
+
|
|
340
|
+
// Keep alive
|
|
341
|
+
await new Promise(() => {});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ─── Command: disconnect ────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
async function cmdDisconnect() {
|
|
347
|
+
banner();
|
|
348
|
+
console.log(`${info} Disconnecting...`);
|
|
349
|
+
|
|
350
|
+
const { disconnect } = await import('./index.js');
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
await disconnect();
|
|
354
|
+
console.log(`${ok} Disconnected from VPN.`);
|
|
355
|
+
} catch (e) {
|
|
356
|
+
console.log(`${warn} ${e.message}`);
|
|
357
|
+
}
|
|
358
|
+
console.log('');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─── Command: status ────────────────────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
async function cmdStatus() {
|
|
364
|
+
banner();
|
|
365
|
+
|
|
366
|
+
const { status } = await import('./index.js');
|
|
367
|
+
const s = status();
|
|
368
|
+
|
|
369
|
+
if (!s.connected) {
|
|
370
|
+
console.log(`${c.dim} Not connected${c.reset}`);
|
|
371
|
+
console.log('');
|
|
372
|
+
console.log(` Run: ${c.cyan}sentinel-ai connect${c.reset}`);
|
|
373
|
+
} else {
|
|
374
|
+
console.log(`${ok} ${c.bold}${c.green}VPN Active${c.reset}`);
|
|
375
|
+
console.log(` Session: ${c.cyan}${s.sessionId}${c.reset}`);
|
|
376
|
+
console.log(` Protocol: ${c.cyan}${s.protocol}${c.reset}`);
|
|
377
|
+
console.log(` Node: ${c.cyan}${s.nodeAddress}${c.reset}`);
|
|
378
|
+
console.log(` Uptime: ${c.cyan}${s.uptimeFormatted}${c.reset}`);
|
|
379
|
+
if (s.ip) console.log(` IP: ${c.cyan}${s.ip}${c.reset}`);
|
|
380
|
+
if (s.socksPort) console.log(` SOCKS5: ${c.cyan}127.0.0.1:${s.socksPort}${c.reset}`);
|
|
381
|
+
}
|
|
382
|
+
console.log('');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ─── Command: nodes ─────────────────────────────────────────────────────────
|
|
386
|
+
|
|
387
|
+
async function cmdNodes(flags) {
|
|
388
|
+
banner();
|
|
389
|
+
|
|
390
|
+
const limit = parseInt(flags.limit, 10) || 20;
|
|
391
|
+
const country = flags.country || null;
|
|
392
|
+
|
|
393
|
+
console.log(`${info} Fetching online nodes...`);
|
|
394
|
+
if (country) console.log(` Filter: country = ${c.cyan}${country}${c.reset}`);
|
|
395
|
+
console.log('');
|
|
396
|
+
|
|
397
|
+
const { queryOnlineNodes, filterNodes } = await import('../index.js');
|
|
398
|
+
|
|
399
|
+
let nodes = await queryOnlineNodes({
|
|
400
|
+
maxNodes: 200,
|
|
401
|
+
onNodeProbed: ({ total, probed, online }) => {
|
|
402
|
+
process.stdout.write(`\r ${c.dim}Probing: ${probed}/${total} checked, ${online} online${c.reset}`);
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r'); // Clear progress line
|
|
406
|
+
|
|
407
|
+
// Filter by country if requested
|
|
408
|
+
if (country) {
|
|
409
|
+
nodes = filterNodes(nodes, { country });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Limit output
|
|
413
|
+
const display = nodes.slice(0, limit);
|
|
414
|
+
|
|
415
|
+
if (display.length === 0) {
|
|
416
|
+
console.log(`${warn} No nodes found${country ? ` in "${country}"` : ''}.`);
|
|
417
|
+
console.log('');
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
console.log(`${ok} ${c.bold}${nodes.length} nodes found${c.reset}${nodes.length > limit ? ` (showing ${limit})` : ''}`);
|
|
422
|
+
console.log('');
|
|
423
|
+
|
|
424
|
+
// Table header
|
|
425
|
+
console.log(
|
|
426
|
+
` ${c.bold}${pad('#', 4)}${pad('Address', 52)}${pad('Country', 16)}${pad('Type', 12)}${pad('Score', 8)}${pad('Peers', 6)}${c.reset}`,
|
|
427
|
+
);
|
|
428
|
+
console.log(` ${c.dim}${'─'.repeat(96)}${c.reset}`);
|
|
429
|
+
|
|
430
|
+
for (let i = 0; i < display.length; i++) {
|
|
431
|
+
const n = display[i];
|
|
432
|
+
const addr = n.address || '?';
|
|
433
|
+
const short = addr.length > 48 ? addr.slice(0, 20) + '...' + addr.slice(-20) : addr;
|
|
434
|
+
const loc = n.country || n.city || '?';
|
|
435
|
+
const stype = n.serviceType || '?';
|
|
436
|
+
const score = n.qualityScore != null ? n.qualityScore.toFixed(1) : '-';
|
|
437
|
+
const peers = n.peers != null ? String(n.peers) : '-';
|
|
438
|
+
|
|
439
|
+
console.log(
|
|
440
|
+
` ${c.dim}${pad(String(i + 1), 4)}${c.reset}${c.cyan}${pad(short, 52)}${c.reset}${pad(loc, 16)}${pad(stype, 12)}${c.green}${pad(score, 8)}${c.reset}${pad(peers, 6)}`,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
console.log('');
|
|
445
|
+
console.log(`${info} Connect to a node: ${c.cyan}sentinel-ai connect --node <address>${c.reset}`);
|
|
446
|
+
console.log('');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** Pad string to fixed width */
|
|
450
|
+
function pad(str, width) {
|
|
451
|
+
if (str.length >= width) return str.slice(0, width);
|
|
452
|
+
return str + ' '.repeat(width - str.length);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
456
|
+
|
|
457
|
+
async function main() {
|
|
458
|
+
loadEnv();
|
|
459
|
+
|
|
460
|
+
const { positional, flags } = parseArgs(process.argv);
|
|
461
|
+
const cmd = positional[0] || 'help';
|
|
462
|
+
const sub = positional[1] || '';
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
switch (cmd) {
|
|
466
|
+
case 'setup':
|
|
467
|
+
await cmdSetup();
|
|
468
|
+
break;
|
|
469
|
+
|
|
470
|
+
case 'wallet':
|
|
471
|
+
switch (sub) {
|
|
472
|
+
case 'create':
|
|
473
|
+
await cmdWalletCreate();
|
|
474
|
+
break;
|
|
475
|
+
case 'balance':
|
|
476
|
+
await cmdWalletBalance();
|
|
477
|
+
break;
|
|
478
|
+
case 'import':
|
|
479
|
+
await cmdWalletImport(positional.slice(2));
|
|
480
|
+
break;
|
|
481
|
+
default:
|
|
482
|
+
console.log(`${err} Unknown wallet command: ${sub}`);
|
|
483
|
+
console.log(` Available: create, balance, import`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
break;
|
|
487
|
+
|
|
488
|
+
case 'connect':
|
|
489
|
+
await cmdConnect(flags);
|
|
490
|
+
break;
|
|
491
|
+
|
|
492
|
+
case 'disconnect':
|
|
493
|
+
await cmdDisconnect();
|
|
494
|
+
break;
|
|
495
|
+
|
|
496
|
+
case 'status':
|
|
497
|
+
await cmdStatus();
|
|
498
|
+
break;
|
|
499
|
+
|
|
500
|
+
case 'nodes':
|
|
501
|
+
await cmdNodes(flags);
|
|
502
|
+
break;
|
|
503
|
+
|
|
504
|
+
case 'help':
|
|
505
|
+
case '--help':
|
|
506
|
+
case '-h':
|
|
507
|
+
showHelp();
|
|
508
|
+
break;
|
|
509
|
+
|
|
510
|
+
default:
|
|
511
|
+
console.log(`${err} Unknown command: ${cmd}`);
|
|
512
|
+
console.log(` Run: ${c.cyan}sentinel-ai help${c.reset}`);
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
} catch (e) {
|
|
516
|
+
console.log('');
|
|
517
|
+
console.log(`${err} ${c.red}${e.message}${c.reset}`);
|
|
518
|
+
console.log('');
|
|
519
|
+
|
|
520
|
+
// Provide contextual recovery hints
|
|
521
|
+
if (e.message.includes('mnemonic') || e.message.includes('MNEMONIC')) {
|
|
522
|
+
console.log(`${info} Generate a wallet: ${c.cyan}sentinel-ai wallet create${c.reset}`);
|
|
523
|
+
console.log(`${info} Then add MNEMONIC to your .env file`);
|
|
524
|
+
} else if (e.message.includes('balance') || e.message.includes('Insufficient')) {
|
|
525
|
+
console.log(`${info} Check balance: ${c.cyan}sentinel-ai wallet balance${c.reset}`);
|
|
526
|
+
} else if (e.message.includes('V2Ray') || e.message.includes('WireGuard')) {
|
|
527
|
+
console.log(`${info} Run setup: ${c.cyan}sentinel-ai setup${c.reset}`);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
console.log('');
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
main();
|