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,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel AI Path — Node Discovery & Selection
|
|
3
|
+
*
|
|
4
|
+
* An AI agent uses these functions to find the best node for its needs:
|
|
5
|
+
* budget, location, protocol, speed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
queryOnlineNodes,
|
|
10
|
+
fetchActiveNodes,
|
|
11
|
+
filterNodes,
|
|
12
|
+
getNodePrices,
|
|
13
|
+
getNetworkOverview,
|
|
14
|
+
groupNodesByCountry,
|
|
15
|
+
formatP2P,
|
|
16
|
+
TRANSPORT_SUCCESS_RATES,
|
|
17
|
+
LCD_ENDPOINTS,
|
|
18
|
+
tryWithFallback,
|
|
19
|
+
// v1.5.0: RPC queries (protobuf via ABCI, ~10x faster than LCD for node lists)
|
|
20
|
+
createRpcQueryClientWithFallback,
|
|
21
|
+
rpcQueryNodes,
|
|
22
|
+
} from '../index.js';
|
|
23
|
+
|
|
24
|
+
// ─── discoverNodes() ─────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Discover all available nodes on the Sentinel network.
|
|
28
|
+
* Returns every active node with address, protocol, pricing, and remote URL.
|
|
29
|
+
*
|
|
30
|
+
* By default queries the blockchain directly (fast, returns ALL nodes).
|
|
31
|
+
* Set opts.probe = true to individually probe each node for live status.
|
|
32
|
+
*
|
|
33
|
+
* @param {object} [opts]
|
|
34
|
+
* @param {string} [opts.country] - Filter by country name or code (e.g. 'Germany', 'DE')
|
|
35
|
+
* @param {string} [opts.protocol] - Filter by protocol: 'wireguard' or 'v2ray'
|
|
36
|
+
* @param {number} [opts.maxPrice] - Max price in udvpn per GB (filter expensive nodes)
|
|
37
|
+
* @param {number} [opts.limit] - Max nodes to return (default: all)
|
|
38
|
+
* @param {boolean} [opts.probe] - If true, probe each node individually for live status (slow). Default: false
|
|
39
|
+
* @param {function} [opts.onProgress] - Progress callback: ({ total, probed, online }) => void
|
|
40
|
+
* @returns {Promise<Array<{
|
|
41
|
+
* address: string,
|
|
42
|
+
* country: string|null,
|
|
43
|
+
* protocol: string,
|
|
44
|
+
* pricePerGb: { udvpn: number, p2p: string }|null,
|
|
45
|
+
* pricePerHour: { udvpn: number, p2p: string }|null,
|
|
46
|
+
* score: number,
|
|
47
|
+
* peers: number,
|
|
48
|
+
* remoteUrl: string,
|
|
49
|
+
* }>>}
|
|
50
|
+
*/
|
|
51
|
+
export async function discoverNodes(opts = {}) {
|
|
52
|
+
if (opts && typeof opts !== 'object') {
|
|
53
|
+
throw new Error('discoverNodes(): opts must be an object or undefined');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let nodes;
|
|
57
|
+
|
|
58
|
+
if (opts.probe) {
|
|
59
|
+
// Slow path: probe each node for live status, peers, location
|
|
60
|
+
const maxNodes = opts.limit ? opts.limit * 3 : 3000;
|
|
61
|
+
nodes = await queryOnlineNodes({
|
|
62
|
+
maxNodes,
|
|
63
|
+
onNodeProbed: opts.onProgress || undefined,
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
// Fast path: query blockchain for all active nodes
|
|
67
|
+
// v1.5.0: Try RPC first (protobuf, ~10x faster), fall back to LCD
|
|
68
|
+
try {
|
|
69
|
+
const rpcClient = await createRpcQueryClientWithFallback();
|
|
70
|
+
nodes = await rpcQueryNodes(rpcClient, { status: 1, limit: 5000 });
|
|
71
|
+
} catch {
|
|
72
|
+
// RPC failed — fall back to LCD
|
|
73
|
+
const { result } = await tryWithFallback(
|
|
74
|
+
LCD_ENDPOINTS,
|
|
75
|
+
fetchActiveNodes,
|
|
76
|
+
'discoverNodes',
|
|
77
|
+
);
|
|
78
|
+
nodes = result;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Apply filters
|
|
83
|
+
if (opts.country) {
|
|
84
|
+
nodes = filterNodes(nodes, { country: opts.country });
|
|
85
|
+
}
|
|
86
|
+
if (opts.protocol) {
|
|
87
|
+
nodes = nodes.filter(n => {
|
|
88
|
+
const type = String(n.serviceType || n.service_type || '').toLowerCase();
|
|
89
|
+
return type === opts.protocol;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Enrich with clean structure
|
|
94
|
+
const enriched = nodes.map(n => {
|
|
95
|
+
const gbPrices = n.gigabyte_prices || [];
|
|
96
|
+
const hrPrices = n.hourly_prices || [];
|
|
97
|
+
const udvpnGb = gbPrices.find(p => p.denom === 'udvpn');
|
|
98
|
+
const udvpnHr = hrPrices.find(p => p.denom === 'udvpn');
|
|
99
|
+
|
|
100
|
+
const pricePerGb = udvpnGb ? {
|
|
101
|
+
udvpn: parseInt(udvpnGb.quote_value || udvpnGb.amount || '0', 10),
|
|
102
|
+
p2p: formatP2P(parseInt(udvpnGb.quote_value || udvpnGb.amount || '0', 10)),
|
|
103
|
+
} : null;
|
|
104
|
+
|
|
105
|
+
const pricePerHour = udvpnHr ? {
|
|
106
|
+
udvpn: parseInt(udvpnHr.quote_value || udvpnHr.amount || '0', 10),
|
|
107
|
+
p2p: formatP2P(parseInt(udvpnHr.quote_value || udvpnHr.amount || '0', 10)),
|
|
108
|
+
} : null;
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
address: n.address || n.acc_address,
|
|
112
|
+
country: n.country || n.location?.country || null,
|
|
113
|
+
protocol: (() => {
|
|
114
|
+
const t = String(n.serviceType || n.service_type || '').toLowerCase();
|
|
115
|
+
return t === 'wireguard' || t === 'v2ray' ? t : null;
|
|
116
|
+
})(),
|
|
117
|
+
pricePerGb,
|
|
118
|
+
pricePerHour,
|
|
119
|
+
score: n.qualityScore ?? n.score ?? 0,
|
|
120
|
+
peers: n.peers ?? null,
|
|
121
|
+
remoteUrl: n.remote_url || n.remote_addrs?.[0] || '',
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Filter by max price
|
|
126
|
+
if (opts.maxPrice && opts.maxPrice > 0) {
|
|
127
|
+
const filtered = enriched.filter(n =>
|
|
128
|
+
n.pricePerGb && n.pricePerGb.udvpn <= opts.maxPrice
|
|
129
|
+
);
|
|
130
|
+
if (filtered.length > 0) {
|
|
131
|
+
const sorted = filtered.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
132
|
+
return opts.limit ? sorted.slice(0, opts.limit) : sorted;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Sort by score descending
|
|
137
|
+
const sorted = enriched.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
138
|
+
return opts.limit ? sorted.slice(0, opts.limit) : sorted;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ─── getNodeInfo() ───────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get detailed info for a specific node including pricing.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} nodeAddress - sentnode1... address
|
|
147
|
+
* @returns {Promise<{ address: string, prices: object, online: boolean, country: string|null }>}
|
|
148
|
+
*/
|
|
149
|
+
export async function getNodeInfo(nodeAddress) {
|
|
150
|
+
const prices = await getNodePrices(nodeAddress);
|
|
151
|
+
return {
|
|
152
|
+
address: nodeAddress,
|
|
153
|
+
prices,
|
|
154
|
+
online: true, // If getNodePrices didn't throw, node is reachable
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ─── getNetworkStats() ───────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get network-wide statistics for an AI agent to understand the landscape.
|
|
162
|
+
*
|
|
163
|
+
* @returns {Promise<{
|
|
164
|
+
* totalNodes: number,
|
|
165
|
+
* byCountry: Record<string, number>,
|
|
166
|
+
* byProtocol: { wireguard: number, v2ray: number },
|
|
167
|
+
* transportReliability: Record<string, number>,
|
|
168
|
+
* }>}
|
|
169
|
+
*/
|
|
170
|
+
export async function getNetworkStats() {
|
|
171
|
+
const overview = await getNetworkOverview();
|
|
172
|
+
return {
|
|
173
|
+
totalNodes: overview.totalNodes || overview.total || 0,
|
|
174
|
+
byCountry: overview.byCountry || {},
|
|
175
|
+
byProtocol: overview.byType || overview.byProtocol || { wireguard: 0, v2ray: 0 },
|
|
176
|
+
transportReliability: { ...TRANSPORT_SUCCESS_RATES },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel AI Path — Environment Detection & Setup
|
|
3
|
+
*
|
|
4
|
+
* Detects OS, checks all dependencies, reports what's available.
|
|
5
|
+
* An AI agent calls setup() first to understand what it can do.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
verifyDependencies,
|
|
10
|
+
IS_ADMIN,
|
|
11
|
+
WG_AVAILABLE,
|
|
12
|
+
V2RAY_VERSION,
|
|
13
|
+
preflight,
|
|
14
|
+
} from '../index.js';
|
|
15
|
+
import { existsSync } from 'fs';
|
|
16
|
+
import { execSync } from 'child_process';
|
|
17
|
+
import { resolve, dirname } from 'path';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
22
|
+
// ─── V2Ray Detection (comprehensive) ────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Find V2Ray binary by checking every known location.
|
|
26
|
+
* This is the authoritative detection — covers env var, SDK paths, system paths.
|
|
27
|
+
*/
|
|
28
|
+
function findV2Ray() {
|
|
29
|
+
const binary = process.platform === 'win32' ? 'v2ray.exe' : 'v2ray';
|
|
30
|
+
|
|
31
|
+
// 1. V2RAY_PATH env var (highest priority)
|
|
32
|
+
if (process.env.V2RAY_PATH && existsSync(process.env.V2RAY_PATH)) {
|
|
33
|
+
return process.env.V2RAY_PATH;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 2. Use Node.js module resolution to find the SDK, then derive bin/ path
|
|
37
|
+
try {
|
|
38
|
+
const sdkMain = import.meta.resolve('sentinel-dvpn-sdk');
|
|
39
|
+
const sdkDir = dirname(fileURLToPath(sdkMain));
|
|
40
|
+
const sdkBin = resolve(sdkDir, 'bin', binary);
|
|
41
|
+
if (existsSync(sdkBin)) return sdkBin;
|
|
42
|
+
} catch {}
|
|
43
|
+
|
|
44
|
+
// 3. Walk up from __dirname looking for sentinel-dvpn-sdk/bin/
|
|
45
|
+
let dir = __dirname;
|
|
46
|
+
for (let i = 0; i < 5; i++) {
|
|
47
|
+
const candidate = resolve(dir, 'node_modules', 'sentinel-dvpn-sdk', 'bin', binary);
|
|
48
|
+
if (existsSync(candidate)) return candidate;
|
|
49
|
+
const sibling = resolve(dir, '..', 'sentinel-dvpn-sdk', 'bin', binary);
|
|
50
|
+
if (existsSync(sibling)) return sibling;
|
|
51
|
+
dir = resolve(dir, '..');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 4. Local bin/
|
|
55
|
+
const localBin = resolve(__dirname, 'bin', binary);
|
|
56
|
+
if (existsSync(localBin)) return localBin;
|
|
57
|
+
|
|
58
|
+
// 5. Parent bin/ (monorepo layout)
|
|
59
|
+
const parentBin = resolve(__dirname, '..', 'bin', binary);
|
|
60
|
+
if (existsSync(parentBin)) return parentBin;
|
|
61
|
+
|
|
62
|
+
// 5. System paths
|
|
63
|
+
const systemPaths = process.platform === 'win32'
|
|
64
|
+
? ['C:\\Program Files\\V2Ray\\v2ray.exe', 'C:\\Program Files (x86)\\V2Ray\\v2ray.exe']
|
|
65
|
+
: ['/usr/local/bin/v2ray', '/usr/bin/v2ray', '/opt/homebrew/bin/v2ray'];
|
|
66
|
+
for (const p of systemPaths) {
|
|
67
|
+
if (existsSync(p)) return p;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 6. System PATH
|
|
71
|
+
try {
|
|
72
|
+
const cmd = process.platform === 'win32' ? 'where v2ray.exe' : 'which v2ray';
|
|
73
|
+
const result = execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
74
|
+
if (result) return result.split('\n')[0];
|
|
75
|
+
} catch { /* not in PATH */ }
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── WireGuard Detection (comprehensive) ─────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
function findWireGuard() {
|
|
83
|
+
if (process.platform === 'win32') {
|
|
84
|
+
const paths = [
|
|
85
|
+
'C:\\Program Files\\WireGuard\\wireguard.exe',
|
|
86
|
+
'C:\\Program Files (x86)\\WireGuard\\wireguard.exe',
|
|
87
|
+
];
|
|
88
|
+
for (const p of paths) {
|
|
89
|
+
if (existsSync(p)) return p;
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
const paths = ['/usr/bin/wg', '/usr/local/bin/wg', '/opt/homebrew/bin/wg'];
|
|
93
|
+
for (const p of paths) {
|
|
94
|
+
if (existsSync(p)) return p;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const cmd = process.platform === 'win32' ? 'where wireguard.exe' : 'which wg';
|
|
99
|
+
const result = execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
100
|
+
if (result) return result.split('\n')[0];
|
|
101
|
+
} catch { /* not in PATH */ }
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── getEnvironment() ────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Detect the current environment without changing anything.
|
|
109
|
+
* Uses comprehensive detection — checks env vars, SDK paths, system paths, PATH.
|
|
110
|
+
*
|
|
111
|
+
* @returns {{
|
|
112
|
+
* os: string,
|
|
113
|
+
* arch: string,
|
|
114
|
+
* platform: string,
|
|
115
|
+
* nodeVersion: string,
|
|
116
|
+
* admin: boolean,
|
|
117
|
+
* v2ray: { available: boolean, version: string|null, path: string|null },
|
|
118
|
+
* wireguard: { available: boolean, path: string|null, requiresAdmin: true },
|
|
119
|
+
* capabilities: string[],
|
|
120
|
+
* recommended: string[],
|
|
121
|
+
* }}
|
|
122
|
+
*/
|
|
123
|
+
export function getEnvironment() {
|
|
124
|
+
const os = process.platform === 'win32' ? 'windows'
|
|
125
|
+
: process.platform === 'darwin' ? 'macos'
|
|
126
|
+
: process.platform === 'linux' ? 'linux'
|
|
127
|
+
: process.platform;
|
|
128
|
+
|
|
129
|
+
// V2Ray: our own comprehensive detection (not just SDK's verifyDependencies)
|
|
130
|
+
const v2rayPath = findV2Ray();
|
|
131
|
+
let v2rayVersion = null;
|
|
132
|
+
if (v2rayPath) {
|
|
133
|
+
try {
|
|
134
|
+
const out = execSync(`"${v2rayPath}" version`, { encoding: 'utf8', stdio: 'pipe', timeout: 5000 });
|
|
135
|
+
const match = out.match(/V2Ray\s+(\d+\.\d+\.\d+)/);
|
|
136
|
+
v2rayVersion = match ? match[1] : null;
|
|
137
|
+
} catch { /* version check optional */ }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const v2ray = {
|
|
141
|
+
available: !!v2rayPath,
|
|
142
|
+
version: v2rayVersion,
|
|
143
|
+
path: v2rayPath,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// WireGuard: our own comprehensive detection
|
|
147
|
+
const wgPath = findWireGuard();
|
|
148
|
+
const wireguard = {
|
|
149
|
+
available: !!wgPath,
|
|
150
|
+
path: wgPath,
|
|
151
|
+
requiresAdmin: true,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// What this environment can do
|
|
155
|
+
const capabilities = [];
|
|
156
|
+
if (v2ray.available) capabilities.push('v2ray');
|
|
157
|
+
if (wireguard.available && IS_ADMIN) capabilities.push('wireguard');
|
|
158
|
+
if (wireguard.available && !IS_ADMIN) capabilities.push('wireguard-needs-admin');
|
|
159
|
+
|
|
160
|
+
// What we recommend installing
|
|
161
|
+
const recommended = [];
|
|
162
|
+
if (!v2ray.available) recommended.push('v2ray — run: node setup.js');
|
|
163
|
+
if (!wireguard.available && os === 'windows') {
|
|
164
|
+
recommended.push('wireguard — run setup.js as admin for auto-install');
|
|
165
|
+
}
|
|
166
|
+
if (!wireguard.available && os === 'macos') {
|
|
167
|
+
recommended.push('wireguard — run: brew install wireguard-tools');
|
|
168
|
+
}
|
|
169
|
+
if (!wireguard.available && os === 'linux') {
|
|
170
|
+
recommended.push('wireguard — run: sudo apt install wireguard-tools');
|
|
171
|
+
}
|
|
172
|
+
if (wireguard.available && !IS_ADMIN) {
|
|
173
|
+
recommended.push('run as admin to use WireGuard nodes (faster, more reliable)');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
os,
|
|
178
|
+
arch: process.arch,
|
|
179
|
+
platform: `${os}-${process.arch}`,
|
|
180
|
+
nodeVersion: process.versions.node,
|
|
181
|
+
admin: IS_ADMIN,
|
|
182
|
+
v2ray,
|
|
183
|
+
wireguard,
|
|
184
|
+
capabilities,
|
|
185
|
+
recommended,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── setup() ─────────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Full environment setup: check deps, install missing ones, report status.
|
|
193
|
+
* Runs preflight checks that verify everything needed for a VPN connection.
|
|
194
|
+
*
|
|
195
|
+
* Returns a FLAT structure — agents access .os, .v2ray, .wireguard directly.
|
|
196
|
+
* No nested .environment wrapper to misread.
|
|
197
|
+
*
|
|
198
|
+
* @returns {Promise<{
|
|
199
|
+
* ready: boolean,
|
|
200
|
+
* os: string,
|
|
201
|
+
* arch: string,
|
|
202
|
+
* platform: string,
|
|
203
|
+
* nodeVersion: string,
|
|
204
|
+
* admin: boolean,
|
|
205
|
+
* v2ray: boolean,
|
|
206
|
+
* v2rayVersion: string|null,
|
|
207
|
+
* v2rayPath: string|null,
|
|
208
|
+
* wireguard: boolean,
|
|
209
|
+
* wireguardPath: string|null,
|
|
210
|
+
* capabilities: string[],
|
|
211
|
+
* recommended: string[],
|
|
212
|
+
* preflight: object|null,
|
|
213
|
+
* issues: string[],
|
|
214
|
+
* }>}
|
|
215
|
+
*/
|
|
216
|
+
export async function setup() {
|
|
217
|
+
const env = getEnvironment();
|
|
218
|
+
const issues = [];
|
|
219
|
+
|
|
220
|
+
// Run preflight checks — pass already-detected V2Ray path to avoid contradiction (BUG-1 fix)
|
|
221
|
+
let preflightResult = null;
|
|
222
|
+
try {
|
|
223
|
+
const preflightOpts = {};
|
|
224
|
+
if (env.v2ray?.path) preflightOpts.v2rayExePath = env.v2ray.path;
|
|
225
|
+
preflightResult = await preflight(preflightOpts);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
issues.push(`Preflight failed: ${err.message}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check critical requirements
|
|
231
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
232
|
+
if (nodeMajor < 20) {
|
|
233
|
+
issues.push(`Node.js ${process.versions.node} too old — need >= 20`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!env.v2ray.available && !env.wireguard.available) {
|
|
237
|
+
issues.push('No tunnel protocol available — install V2Ray or WireGuard');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (env.v2ray.available && env.v2ray.version && env.v2ray.version !== V2RAY_VERSION) {
|
|
241
|
+
issues.push(`V2Ray version ${env.v2ray.version} — need exactly ${V2RAY_VERSION} (5.44.1+ has bugs)`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const ready = issues.length === 0 && env.capabilities.length > 0;
|
|
245
|
+
|
|
246
|
+
// Flat return — agent accesses .os, .v2ray, .admin directly
|
|
247
|
+
return {
|
|
248
|
+
ready,
|
|
249
|
+
os: env.os,
|
|
250
|
+
arch: env.arch,
|
|
251
|
+
platform: env.platform,
|
|
252
|
+
nodeVersion: env.nodeVersion,
|
|
253
|
+
admin: env.admin,
|
|
254
|
+
v2ray: env.v2ray.available,
|
|
255
|
+
v2rayVersion: env.v2ray.version,
|
|
256
|
+
v2rayPath: env.v2ray.path,
|
|
257
|
+
wireguard: env.wireguard.available,
|
|
258
|
+
wireguardPath: env.wireguard.path,
|
|
259
|
+
capabilities: env.capabilities,
|
|
260
|
+
recommended: env.recommended,
|
|
261
|
+
preflight: preflightResult,
|
|
262
|
+
issues,
|
|
263
|
+
// Backward compat — keep nested .environment for existing consumers
|
|
264
|
+
environment: env,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel AI Path — Error Types
|
|
3
|
+
*
|
|
4
|
+
* Typed errors with machine-readable codes for AI agent error handling.
|
|
5
|
+
* Every error includes a `nextAction` field telling the agent what to do.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ─── Error Codes ────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export const AiPathErrorCodes = {
|
|
11
|
+
// Wallet
|
|
12
|
+
MISSING_MNEMONIC: 'MISSING_MNEMONIC',
|
|
13
|
+
INVALID_MNEMONIC: 'INVALID_MNEMONIC',
|
|
14
|
+
INSUFFICIENT_BALANCE: 'INSUFFICIENT_BALANCE',
|
|
15
|
+
|
|
16
|
+
// Environment
|
|
17
|
+
SETUP_FAILED: 'SETUP_FAILED',
|
|
18
|
+
ENVIRONMENT_NOT_READY: 'ENVIRONMENT_NOT_READY',
|
|
19
|
+
V2RAY_NOT_FOUND: 'V2RAY_NOT_FOUND',
|
|
20
|
+
WIREGUARD_NOT_FOUND: 'WIREGUARD_NOT_FOUND',
|
|
21
|
+
ADMIN_REQUIRED: 'ADMIN_REQUIRED',
|
|
22
|
+
|
|
23
|
+
// Connection
|
|
24
|
+
CONNECT_FAILED: 'CONNECT_FAILED',
|
|
25
|
+
DISCONNECT_FAILED: 'DISCONNECT_FAILED',
|
|
26
|
+
ALREADY_CONNECTED: 'ALREADY_CONNECTED',
|
|
27
|
+
ALL_NODES_FAILED: 'ALL_NODES_FAILED',
|
|
28
|
+
NO_NODES_IN_COUNTRY: 'NO_NODES_IN_COUNTRY',
|
|
29
|
+
NODE_OFFLINE: 'NODE_OFFLINE',
|
|
30
|
+
HANDSHAKE_FAILED: 'HANDSHAKE_FAILED',
|
|
31
|
+
TUNNEL_FAILED: 'TUNNEL_FAILED',
|
|
32
|
+
TIMEOUT: 'TIMEOUT',
|
|
33
|
+
|
|
34
|
+
// Validation
|
|
35
|
+
INVALID_OPTIONS: 'INVALID_OPTIONS',
|
|
36
|
+
|
|
37
|
+
// Discovery
|
|
38
|
+
DISCOVERY_FAILED: 'DISCOVERY_FAILED',
|
|
39
|
+
WALLET_FAILED: 'WALLET_FAILED',
|
|
40
|
+
BALANCE_FAILED: 'BALANCE_FAILED',
|
|
41
|
+
VERIFY_FAILED: 'VERIFY_FAILED',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ─── Next Actions (machine-readable) ────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
export const NextActions = {
|
|
47
|
+
CREATE_WALLET: 'create_wallet',
|
|
48
|
+
FUND_WALLET: 'fund_wallet',
|
|
49
|
+
RUN_SETUP: 'run_setup',
|
|
50
|
+
RUN_AS_ADMIN: 'run_as_admin',
|
|
51
|
+
TRY_DIFFERENT_NODE: 'try_different_node',
|
|
52
|
+
TRY_DIFFERENT_COUNTRY: 'try_different_country',
|
|
53
|
+
TRY_V2RAY: 'try_v2ray',
|
|
54
|
+
TRY_WIREGUARD: 'try_wireguard',
|
|
55
|
+
DISCONNECT_FIRST: 'disconnect',
|
|
56
|
+
RETRY: 'retry',
|
|
57
|
+
NONE: 'none',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// ─── Error Class ────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
export class AiPathError extends Error {
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} code - Machine-readable error code from AiPathErrorCodes
|
|
65
|
+
* @param {string} message - Human-readable message
|
|
66
|
+
* @param {object} [details] - Extra context for the agent
|
|
67
|
+
* @param {string} [nextAction] - What the agent should do next (from NextActions)
|
|
68
|
+
*/
|
|
69
|
+
constructor(code, message, details = null, nextAction = NextActions.RETRY) {
|
|
70
|
+
super(message);
|
|
71
|
+
this.name = 'AiPathError';
|
|
72
|
+
this.code = code;
|
|
73
|
+
this.details = details;
|
|
74
|
+
this.nextAction = nextAction;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
toJSON() {
|
|
78
|
+
return {
|
|
79
|
+
name: this.name,
|
|
80
|
+
code: this.code,
|
|
81
|
+
message: this.message,
|
|
82
|
+
details: this.details,
|
|
83
|
+
nextAction: this.nextAction,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|