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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous Agent — Production VPN Pattern
|
|
3
|
+
*
|
|
4
|
+
* A complete autonomous AI agent that:
|
|
5
|
+
* 1. Creates or loads a wallet
|
|
6
|
+
* 2. Checks balance, warns if low
|
|
7
|
+
* 3. Connects to VPN with retry logic
|
|
8
|
+
* 4. Performs work (HTTP requests through the tunnel)
|
|
9
|
+
* 5. Monitors connection health
|
|
10
|
+
* 6. Auto-reconnects on failure
|
|
11
|
+
* 7. Gracefully disconnects when done
|
|
12
|
+
*
|
|
13
|
+
* Run: node examples/autonomous-agent.mjs
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import 'dotenv/config';
|
|
17
|
+
import { connect, disconnect, status, isVpnActive, createWallet, getBalance } from '../index.js';
|
|
18
|
+
|
|
19
|
+
// ─── Configuration ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const MAX_RETRIES = 3;
|
|
22
|
+
const RETRY_DELAY_MS = 5000;
|
|
23
|
+
const HEALTH_CHECK_INTERVAL_MS = 30000;
|
|
24
|
+
const WORK_CYCLES = 5;
|
|
25
|
+
|
|
26
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function log(level, msg) {
|
|
29
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
30
|
+
const prefix = { info: '\x1b[36m[INFO]\x1b[0m', warn: '\x1b[33m[WARN]\x1b[0m', err: '\x1b[31m[ERR]\x1b[0m', ok: '\x1b[32m[ OK ]\x1b[0m' };
|
|
31
|
+
console.log(`${ts} ${prefix[level] || prefix.info} ${msg}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function sleep(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Step 1: Wallet Setup ───────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
async function ensureWallet() {
|
|
41
|
+
let mnemonic = process.env.MNEMONIC;
|
|
42
|
+
|
|
43
|
+
if (!mnemonic) {
|
|
44
|
+
log('warn', 'No MNEMONIC in .env — generating a new wallet');
|
|
45
|
+
const wallet = await createWallet();
|
|
46
|
+
mnemonic = wallet.mnemonic;
|
|
47
|
+
log('ok', `Wallet created: ${wallet.address}`);
|
|
48
|
+
log('warn', 'SAVE YOUR MNEMONIC — it will not be shown again.');
|
|
49
|
+
log('warn', `Add to .env: MNEMONIC=<your ${mnemonic.split(' ').length} words>`);
|
|
50
|
+
// SECURITY: Never log the full mnemonic. Write directly to .env file instead.
|
|
51
|
+
const { writeFileSync } = await import('fs');
|
|
52
|
+
writeFileSync('.env', `MNEMONIC=${mnemonic}\n`, { mode: 0o600 });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return mnemonic;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Step 2: Balance Check ──────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
async function checkFunds(mnemonic) {
|
|
61
|
+
log('info', 'Checking wallet balance...');
|
|
62
|
+
const bal = await getBalance(mnemonic);
|
|
63
|
+
log('info', `Address: ${bal.address}`);
|
|
64
|
+
log('info', `Balance: ${bal.p2p} (${bal.udvpn.toLocaleString()} udvpn)`);
|
|
65
|
+
|
|
66
|
+
if (!bal.funded) {
|
|
67
|
+
log('err', 'Insufficient balance. Fund your wallet with P2P tokens.');
|
|
68
|
+
log('info', `Send tokens to: ${bal.address}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
log('ok', 'Wallet funded and ready');
|
|
73
|
+
return bal;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Step 3: Connect with Retries ───────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
async function connectWithRetry(mnemonic) {
|
|
79
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
80
|
+
log('info', `Connection attempt ${attempt}/${MAX_RETRIES}...`);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const vpn = await connect({
|
|
84
|
+
mnemonic,
|
|
85
|
+
protocol: 'v2ray',
|
|
86
|
+
onProgress: (stage, detail) => log('info', ` [${stage}] ${detail}`),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
log('ok', `Connected via ${vpn.protocol} to ${vpn.nodeAddress}`);
|
|
90
|
+
if (vpn.ip) log('ok', `Public IP: ${vpn.ip}`);
|
|
91
|
+
return vpn;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
log('err', `Attempt ${attempt} failed: ${err.message}`);
|
|
94
|
+
|
|
95
|
+
if (attempt < MAX_RETRIES) {
|
|
96
|
+
const delay = RETRY_DELAY_MS * attempt;
|
|
97
|
+
log('info', `Retrying in ${delay / 1000}s...`);
|
|
98
|
+
await sleep(delay);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
log('err', `Failed after ${MAX_RETRIES} attempts`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Step 4: Perform Work ───────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
async function doWork(cycleNum) {
|
|
110
|
+
// Simulated work — replace with your actual task
|
|
111
|
+
// (web scraping, API calls, data collection, etc.)
|
|
112
|
+
log('info', `Work cycle ${cycleNum}: making request through VPN tunnel...`);
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Use dynamic import so this example works without axios installed
|
|
116
|
+
const { default: axios } = await import('axios');
|
|
117
|
+
const res = await axios.get('https://api.ipify.org?format=json', {
|
|
118
|
+
timeout: 15000,
|
|
119
|
+
adapter: 'http',
|
|
120
|
+
});
|
|
121
|
+
log('ok', `Work cycle ${cycleNum}: response from IP ${res.data.ip}`);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log('warn', `Work cycle ${cycleNum}: request failed — ${err.message}`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Step 5: Health Monitor ─────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
function startHealthMonitor(mnemonic) {
|
|
133
|
+
const intervalId = setInterval(async () => {
|
|
134
|
+
if (!isVpnActive()) {
|
|
135
|
+
log('warn', 'VPN connection lost — reconnecting...');
|
|
136
|
+
clearInterval(intervalId);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await connectWithRetry(mnemonic);
|
|
140
|
+
startHealthMonitor(mnemonic);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
log('err', `Auto-reconnect failed: ${err.message}`);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
const s = status();
|
|
146
|
+
log('info', `Health check: connected, uptime ${s.uptimeFormatted || 'unknown'}`);
|
|
147
|
+
}
|
|
148
|
+
}, HEALTH_CHECK_INTERVAL_MS);
|
|
149
|
+
|
|
150
|
+
return intervalId;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ─── Main Agent Loop ────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
async function main() {
|
|
156
|
+
log('info', 'Autonomous agent starting...');
|
|
157
|
+
console.log('');
|
|
158
|
+
|
|
159
|
+
// 1. Wallet
|
|
160
|
+
const mnemonic = await ensureWallet();
|
|
161
|
+
|
|
162
|
+
// 2. Funds
|
|
163
|
+
await checkFunds(mnemonic);
|
|
164
|
+
console.log('');
|
|
165
|
+
|
|
166
|
+
// 3. Connect
|
|
167
|
+
await connectWithRetry(mnemonic);
|
|
168
|
+
console.log('');
|
|
169
|
+
|
|
170
|
+
// 4. Health monitor
|
|
171
|
+
const monitorId = startHealthMonitor(mnemonic);
|
|
172
|
+
|
|
173
|
+
// 5. Work loop
|
|
174
|
+
let failures = 0;
|
|
175
|
+
for (let i = 1; i <= WORK_CYCLES; i++) {
|
|
176
|
+
const success = await doWork(i);
|
|
177
|
+
if (!success) failures++;
|
|
178
|
+
|
|
179
|
+
// If too many consecutive failures, reconnect
|
|
180
|
+
if (failures >= 2) {
|
|
181
|
+
log('warn', 'Multiple work failures — reconnecting...');
|
|
182
|
+
await disconnect().catch(() => {});
|
|
183
|
+
await connectWithRetry(mnemonic);
|
|
184
|
+
failures = 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Pause between work cycles
|
|
188
|
+
if (i < WORK_CYCLES) {
|
|
189
|
+
await sleep(3000);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 6. Cleanup
|
|
194
|
+
console.log('');
|
|
195
|
+
log('info', 'Work complete. Disconnecting...');
|
|
196
|
+
clearInterval(monitorId);
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
await disconnect();
|
|
200
|
+
log('ok', 'Disconnected. Agent finished.');
|
|
201
|
+
} catch (err) {
|
|
202
|
+
log('warn', `Disconnect error: ${err.message}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ─── Graceful Shutdown ──────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
process.on('SIGINT', async () => {
|
|
211
|
+
console.log('');
|
|
212
|
+
log('info', 'Interrupted — shutting down...');
|
|
213
|
+
try { await disconnect(); } catch { /* best effort */ }
|
|
214
|
+
process.exit(0);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
main().catch((err) => {
|
|
218
|
+
log('err', `Fatal: ${err.message}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Region Rotation
|
|
3
|
+
*
|
|
4
|
+
* Connect to VPN nodes in different countries sequentially.
|
|
5
|
+
* Each hop: connect -> perform work -> disconnect -> next country.
|
|
6
|
+
*
|
|
7
|
+
* Use cases:
|
|
8
|
+
* - Data collection from different geographic perspectives
|
|
9
|
+
* - Verifying geo-restricted content availability
|
|
10
|
+
* - Testing regional API responses
|
|
11
|
+
* - Distributing requests across exit regions
|
|
12
|
+
*
|
|
13
|
+
* Run: node examples/multi-region.mjs
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import 'dotenv/config';
|
|
17
|
+
import { connect, disconnect, isVpnActive } from '../index.js';
|
|
18
|
+
|
|
19
|
+
// ─── Configuration ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const REGIONS = [
|
|
22
|
+
{ country: 'DE', label: 'Germany' },
|
|
23
|
+
{ country: 'JP', label: 'Japan' },
|
|
24
|
+
{ country: 'US', label: 'United States' },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const SETTLE_DELAY_MS = 3000;
|
|
28
|
+
|
|
29
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function log(level, msg) {
|
|
32
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
33
|
+
const prefix = { info: '\x1b[36m[INFO]\x1b[0m', warn: '\x1b[33m[WARN]\x1b[0m', err: '\x1b[31m[ERR]\x1b[0m', ok: '\x1b[32m[ OK ]\x1b[0m' };
|
|
34
|
+
console.log(`${ts} ${prefix[level] || prefix.info} ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sleep(ms) {
|
|
38
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Work Function ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Perform work through the VPN tunnel.
|
|
45
|
+
* Replace this with your actual task (scraping, API calls, etc.).
|
|
46
|
+
*/
|
|
47
|
+
async function doWork(region) {
|
|
48
|
+
log('info', `[${region.label}] Performing work through tunnel...`);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const { default: axios } = await import('axios');
|
|
52
|
+
|
|
53
|
+
// Verify we have a new IP
|
|
54
|
+
const ipRes = await axios.get('https://api.ipify.org?format=json', {
|
|
55
|
+
timeout: 15000,
|
|
56
|
+
adapter: 'http',
|
|
57
|
+
});
|
|
58
|
+
log('ok', `[${region.label}] Exit IP: ${ipRes.data.ip}`);
|
|
59
|
+
|
|
60
|
+
// Example: fetch region-specific data
|
|
61
|
+
const geoRes = await axios.get(`https://ipapi.co/${ipRes.data.ip}/json/`, {
|
|
62
|
+
timeout: 15000,
|
|
63
|
+
adapter: 'http',
|
|
64
|
+
});
|
|
65
|
+
const geo = geoRes.data;
|
|
66
|
+
log('ok', `[${region.label}] Geo: ${geo.country_name || '?'}, ${geo.city || '?'}, ${geo.org || '?'}`);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
region: region.label,
|
|
70
|
+
ip: ipRes.data.ip,
|
|
71
|
+
country: geo.country_name || null,
|
|
72
|
+
city: geo.city || null,
|
|
73
|
+
org: geo.org || null,
|
|
74
|
+
};
|
|
75
|
+
} catch (err) {
|
|
76
|
+
log('warn', `[${region.label}] Work failed: ${err.message}`);
|
|
77
|
+
return { region: region.label, ip: null, error: err.message };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Main Loop ──────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
const mnemonic = process.env.MNEMONIC;
|
|
85
|
+
if (!mnemonic) {
|
|
86
|
+
log('err', 'No MNEMONIC in .env file. Run: sentinel-ai wallet create');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
log('info', `Multi-region rotation: ${REGIONS.map(r => r.label).join(' -> ')}`);
|
|
91
|
+
console.log('');
|
|
92
|
+
|
|
93
|
+
const results = [];
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < REGIONS.length; i++) {
|
|
96
|
+
const region = REGIONS[i];
|
|
97
|
+
const step = `[${i + 1}/${REGIONS.length}]`;
|
|
98
|
+
|
|
99
|
+
// ── Connect ──
|
|
100
|
+
log('info', `${step} Connecting to ${region.label} (${region.country})...`);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const vpn = await connect({
|
|
104
|
+
mnemonic,
|
|
105
|
+
country: region.country,
|
|
106
|
+
protocol: 'v2ray',
|
|
107
|
+
onProgress: (stage, detail) => log('info', ` [${stage}] ${detail}`),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
log('ok', `${step} Connected via ${vpn.protocol}, IP: ${vpn.ip || 'checking...'}`);
|
|
111
|
+
|
|
112
|
+
// Let the tunnel settle
|
|
113
|
+
await sleep(SETTLE_DELAY_MS);
|
|
114
|
+
|
|
115
|
+
// ── Work ──
|
|
116
|
+
const result = await doWork(region);
|
|
117
|
+
results.push(result);
|
|
118
|
+
|
|
119
|
+
} catch (err) {
|
|
120
|
+
log('err', `${step} Failed to connect to ${region.label}: ${err.message}`);
|
|
121
|
+
results.push({ region: region.label, ip: null, error: err.message });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Disconnect ──
|
|
125
|
+
if (isVpnActive()) {
|
|
126
|
+
log('info', `${step} Disconnecting from ${region.label}...`);
|
|
127
|
+
try {
|
|
128
|
+
await disconnect();
|
|
129
|
+
log('ok', `${step} Disconnected`);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
log('warn', `${step} Disconnect error: ${err.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Pause between regions (let chain state settle)
|
|
136
|
+
if (i < REGIONS.length - 1) {
|
|
137
|
+
log('info', 'Waiting before next region...');
|
|
138
|
+
await sleep(SETTLE_DELAY_MS);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Summary ────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
console.log('\x1b[1m Region Rotation Summary\x1b[0m');
|
|
147
|
+
console.log(` ${'─'.repeat(60)}`);
|
|
148
|
+
|
|
149
|
+
for (const r of results) {
|
|
150
|
+
if (r.error) {
|
|
151
|
+
console.log(` \x1b[31mx\x1b[0m ${r.region.padEnd(20)} Failed: ${r.error}`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(` \x1b[32m+\x1b[0m ${r.region.padEnd(20)} IP: ${r.ip} (${r.country || '?'}, ${r.city || '?'})`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('');
|
|
158
|
+
const ok = results.filter(r => !r.error).length;
|
|
159
|
+
log('info', `Done. ${ok}/${results.length} regions connected successfully.`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── Graceful Shutdown ──────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
process.on('SIGINT', async () => {
|
|
165
|
+
console.log('');
|
|
166
|
+
log('info', 'Interrupted — disconnecting...');
|
|
167
|
+
try { await disconnect(); } catch { /* best effort */ }
|
|
168
|
+
process.exit(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
main().catch((err) => {
|
|
172
|
+
log('err', `Fatal: ${err.message}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-Shot VPN Connection
|
|
3
|
+
*
|
|
4
|
+
* The absolute minimum code to connect to Sentinel dVPN.
|
|
5
|
+
* 10 lines. That's it.
|
|
6
|
+
*
|
|
7
|
+
* Prerequisites:
|
|
8
|
+
* 1. .env file with MNEMONIC=<your 24-word phrase>
|
|
9
|
+
* 2. Funded wallet (P2P tokens)
|
|
10
|
+
* 3. V2Ray or WireGuard installed (run: sentinel-ai setup)
|
|
11
|
+
*
|
|
12
|
+
* Run: node examples/one-shot.mjs
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import 'dotenv/config';
|
|
16
|
+
import { connect, disconnect } from '../index.js';
|
|
17
|
+
|
|
18
|
+
const vpn = await connect({
|
|
19
|
+
mnemonic: process.env.MNEMONIC,
|
|
20
|
+
onProgress: (step, detail) => console.log(`[${step}] ${detail}`),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
console.log(`Connected! Protocol: ${vpn.protocol}, IP: ${vpn.ip}`);
|
|
24
|
+
console.log('Press Ctrl+C to disconnect.');
|
|
25
|
+
|
|
26
|
+
process.on('SIGINT', async () => {
|
|
27
|
+
await disconnect();
|
|
28
|
+
process.exit(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await new Promise(() => {});
|
package/ai-path/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel AI Path — Decentralized VPN for AI Agents
|
|
3
|
+
*
|
|
4
|
+
* Complete agent flow:
|
|
5
|
+
*
|
|
6
|
+
* 1. Setup: await setup() → { os, node, v2ray, wireguard, admin }
|
|
7
|
+
* 2. Wallet: await createWallet() → { mnemonic, address }
|
|
8
|
+
* 3. Fund: await getBalance(m) → { p2p, funded }
|
|
9
|
+
* 4. Discover: await discoverNodes() → [{ address, country, protocol, price, score }]
|
|
10
|
+
* 5. Estimate: await estimateCost(opts) → { perGb, perHour, forBudget }
|
|
11
|
+
* 6. Connect: await connect(opts) → { sessionId, protocol, ip, socksPort }
|
|
12
|
+
* 7. Verify: await verify() → { connected, ip, latency }
|
|
13
|
+
* 8. Monitor: onEvent(callback) → unsubscribe function
|
|
14
|
+
* 9. Disconnect: await disconnect() → { disconnected: true }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ─── Phase 1: Setup & Environment ────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
export { setup, getEnvironment } from './environment.js';
|
|
20
|
+
|
|
21
|
+
// ─── Phase 2-3: Wallet & Funding ─────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export { createWallet, importWallet, getBalance } from './wallet.js';
|
|
24
|
+
|
|
25
|
+
// ─── Phase 4: Node Discovery ─────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export { discoverNodes, getNodeInfo, getNetworkStats } from './discover.js';
|
|
28
|
+
|
|
29
|
+
// ─── Phase 5: Cost Estimation ────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export { estimateCost, PRICING } from './pricing.js';
|
|
32
|
+
|
|
33
|
+
// ─── Phase 5.5: Decision Engine ───────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export { recommend } from './recommend.js';
|
|
36
|
+
|
|
37
|
+
// ─── Phase 6-7: Connect, Verify, Monitor ─────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export { connect, disconnect, status, isVpnActive, verify, verifySplitTunnel, onEvent } from './connect.js';
|
|
40
|
+
|
|
41
|
+
// ─── Error Handling ─────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export { AiPathError, AiPathErrorCodes } from './errors.js';
|
|
44
|
+
|
|
45
|
+
// ─── SDK Internals (advanced — for agents that need typed access) ───────────
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
// v1.5.0: Typed event parsers (structured TX event handling)
|
|
49
|
+
extractSessionIdTyped,
|
|
50
|
+
NodeEventCreateSession,
|
|
51
|
+
SubscriptionEventCreateSession,
|
|
52
|
+
searchEvent,
|
|
53
|
+
// v1.5.0: TYPE_URLS constants (canonical Sentinel message type URLs)
|
|
54
|
+
TYPE_URLS,
|
|
55
|
+
// v1.5.0: RPC queries (protobuf, ~10x faster than LCD)
|
|
56
|
+
createRpcQueryClientWithFallback,
|
|
57
|
+
rpcQueryNodes,
|
|
58
|
+
rpcQueryBalance,
|
|
59
|
+
rpcQueryNode,
|
|
60
|
+
} from '../index.js';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel AI Path — Cost Estimation & Budget Planning
|
|
3
|
+
*
|
|
4
|
+
* An AI agent uses these to estimate costs before committing tokens.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
estimateSessionCost,
|
|
9
|
+
estimateSessionPrice,
|
|
10
|
+
formatP2P,
|
|
11
|
+
PRICING_REFERENCE,
|
|
12
|
+
DENOM,
|
|
13
|
+
} from '../index.js';
|
|
14
|
+
|
|
15
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reference pricing from the SDK. Based on network-wide observations.
|
|
19
|
+
*
|
|
20
|
+
* @type {{
|
|
21
|
+
* typicalPerGb: { udvpn: number, p2p: string },
|
|
22
|
+
* minBalance: { udvpn: number, p2p: string },
|
|
23
|
+
* gasCost: { udvpn: number, p2p: string },
|
|
24
|
+
* denom: string,
|
|
25
|
+
* }}
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Reference pricing sampled from chain data (2026-03-26, 1030 nodes).
|
|
29
|
+
* THESE ARE APPROXIMATE — node operators set their own prices and can change
|
|
30
|
+
* them at any time. Use estimateCost({ nodeAddress }) for live per-node pricing.
|
|
31
|
+
* Median: ~40 P2P/GB (40152030 udvpn at sample time)
|
|
32
|
+
* Cheapest: ~0.68 P2P/GB (680000 udvpn at sample time)
|
|
33
|
+
*/
|
|
34
|
+
export const PRICING = {
|
|
35
|
+
medianPerGb: {
|
|
36
|
+
udvpn: 40152030,
|
|
37
|
+
p2p: formatP2P(40152030),
|
|
38
|
+
},
|
|
39
|
+
cheapestPerGb: {
|
|
40
|
+
udvpn: 680000,
|
|
41
|
+
p2p: formatP2P(680000),
|
|
42
|
+
},
|
|
43
|
+
typicalPerGb: {
|
|
44
|
+
udvpn: 40152030,
|
|
45
|
+
p2p: formatP2P(40152030),
|
|
46
|
+
},
|
|
47
|
+
minBalance: {
|
|
48
|
+
udvpn: 50000000, // 50 P2P — enough for 1 GB on median node + gas
|
|
49
|
+
p2p: formatP2P(50000000),
|
|
50
|
+
},
|
|
51
|
+
gasCost: {
|
|
52
|
+
udvpn: 40000,
|
|
53
|
+
p2p: formatP2P(40000),
|
|
54
|
+
},
|
|
55
|
+
denom: DENOM || 'udvpn',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ─── estimateCost() ──────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Estimate connection cost before committing tokens.
|
|
62
|
+
*
|
|
63
|
+
* @param {object} opts
|
|
64
|
+
* @param {number} [opts.gigabytes=1] - Planned data usage in GB
|
|
65
|
+
* @param {number} [opts.hours] - Planned session duration in hours (alternative to GB)
|
|
66
|
+
* @param {number} [opts.budget] - Available budget in udvpn — calculates how much you can get
|
|
67
|
+
* @param {string} [opts.nodeAddress] - Specific node for exact pricing (queries chain)
|
|
68
|
+
* @returns {Promise<{
|
|
69
|
+
* perGb: { udvpn: number, p2p: string },
|
|
70
|
+
* total: { udvpn: number, p2p: string },
|
|
71
|
+
* gas: { udvpn: number, p2p: string },
|
|
72
|
+
* grandTotal: { udvpn: number, p2p: string },
|
|
73
|
+
* forBudget?: { gigabytes: number, hours: number|null },
|
|
74
|
+
* mode: 'per-gb'|'hourly'|'estimate',
|
|
75
|
+
* }>}
|
|
76
|
+
*/
|
|
77
|
+
export async function estimateCost(opts = {}) {
|
|
78
|
+
if (opts && typeof opts !== 'object') {
|
|
79
|
+
throw new Error('estimateCost(): opts must be an object or undefined');
|
|
80
|
+
}
|
|
81
|
+
const gb = opts.gigabytes || 1;
|
|
82
|
+
const gasCost = 40000; // ~0.04 P2P per TX
|
|
83
|
+
|
|
84
|
+
// If specific node given, get exact price
|
|
85
|
+
if (opts.nodeAddress) {
|
|
86
|
+
try {
|
|
87
|
+
const price = await estimateSessionPrice(opts.nodeAddress, gb);
|
|
88
|
+
const total = price.udvpn || price.amount || 0;
|
|
89
|
+
const grandTotal = total + gasCost;
|
|
90
|
+
|
|
91
|
+
const result = {
|
|
92
|
+
perGb: { udvpn: Math.round(total / gb), p2p: formatP2P(Math.round(total / gb)) },
|
|
93
|
+
total: { udvpn: total, p2p: formatP2P(total) },
|
|
94
|
+
gas: { udvpn: gasCost, p2p: formatP2P(gasCost) },
|
|
95
|
+
grandTotal: { udvpn: grandTotal, p2p: formatP2P(grandTotal) },
|
|
96
|
+
mode: opts.hours ? 'hourly' : 'per-gb',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (opts.budget) {
|
|
100
|
+
const usable = opts.budget - gasCost;
|
|
101
|
+
const pricePerGb = Math.round(total / gb);
|
|
102
|
+
result.forBudget = {
|
|
103
|
+
gigabytes: pricePerGb > 0 ? Math.floor(usable / pricePerGb) : 0,
|
|
104
|
+
hours: null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
} catch {
|
|
110
|
+
// Fall through to estimate
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Network-wide estimate based on real chain pricing (median across 1030 nodes)
|
|
115
|
+
const typicalPerGb = PRICING.typicalPerGb.udvpn;
|
|
116
|
+
const total = typicalPerGb * gb;
|
|
117
|
+
const grandTotal = total + gasCost;
|
|
118
|
+
|
|
119
|
+
const result = {
|
|
120
|
+
perGb: { udvpn: typicalPerGb, p2p: formatP2P(typicalPerGb) },
|
|
121
|
+
total: { udvpn: total, p2p: formatP2P(total) },
|
|
122
|
+
gas: { udvpn: gasCost, p2p: formatP2P(gasCost) },
|
|
123
|
+
grandTotal: { udvpn: grandTotal, p2p: formatP2P(grandTotal) },
|
|
124
|
+
mode: 'estimate',
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (opts.budget) {
|
|
128
|
+
const usable = opts.budget - gasCost;
|
|
129
|
+
result.forBudget = {
|
|
130
|
+
gigabytes: typicalPerGb > 0 ? Math.floor(usable / typicalPerGb) : 0,
|
|
131
|
+
hours: null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|