blue-js-sdk 2.1.0 → 2.1.1

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/ai-path/cli.js CHANGED
@@ -125,6 +125,9 @@ function showHelp() {
125
125
  console.log(` --protocol <type> Protocol: wireguard or v2ray`);
126
126
  console.log(` --dns <preset> DNS: google, cloudflare, or hns (Handshake)`);
127
127
  console.log(` --node <address> Connect to specific node (sentnode1...)`);
128
+ console.log(` --subscription <id> Connect via operator-provisioned subscription`);
129
+ console.log(` --plan <id> Connect via plan (subscribe + start session)`);
130
+ console.log(` --fee-granter <addr> Operator address that pays gas (sent1...)`);
128
131
  console.log('');
129
132
  console.log(`${c.bold}NODES OPTIONS${c.reset}`);
130
133
  console.log(` --country <code> Filter by country`);
@@ -143,6 +146,10 @@ function showHelp() {
143
146
  console.log(` sentinel-ai connect --country DE --protocol wireguard`);
144
147
  console.log(` sentinel-ai connect --node sentnode1abc...`);
145
148
  console.log('');
149
+ console.log(` ${c.dim}# Connect via subscription (operator-provisioned, zero P2P needed)${c.reset}`);
150
+ console.log(` sentinel-ai connect --subscription 1165072 --fee-granter sent1operator...`);
151
+ console.log(` sentinel-ai connect --plan 42 --fee-granter sent1operator...`);
152
+ console.log('');
146
153
  console.log(` ${c.dim}# List nodes${c.reset}`);
147
154
  console.log(` sentinel-ai nodes --country US --limit 10`);
148
155
  console.log('');
@@ -295,12 +302,18 @@ async function cmdConnect(flags) {
295
302
  if (flags.protocol) opts.protocol = flags.protocol;
296
303
  if (flags.dns) opts.dns = flags.dns;
297
304
  if (flags.node) opts.nodeAddress = flags.node;
305
+ if (flags.subscription) opts.subscriptionId = flags.subscription;
306
+ if (flags.plan) opts.planId = flags.plan;
307
+ if (flags['fee-granter']) opts.feeGranter = flags['fee-granter'];
298
308
 
299
309
  console.log(`${info} Connecting to Sentinel dVPN...`);
300
310
  if (flags.country) console.log(` Country: ${c.cyan}${flags.country}${c.reset}`);
301
311
  if (flags.protocol) console.log(` Protocol: ${c.cyan}${flags.protocol}${c.reset}`);
302
312
  if (flags.dns) console.log(` DNS: ${c.cyan}${flags.dns}${c.reset}`);
303
313
  if (flags.node) console.log(` Node: ${c.cyan}${flags.node}${c.reset}`);
314
+ if (flags.subscription) console.log(` Subscription: ${c.cyan}${flags.subscription}${c.reset}`);
315
+ if (flags.plan) console.log(` Plan: ${c.cyan}${flags.plan}${c.reset}`);
316
+ if (flags['fee-granter']) console.log(` Fee granter: ${c.cyan}${flags['fee-granter']}${c.reset}`);
304
317
  console.log('');
305
318
 
306
319
  const { connect, disconnect } = await import('./index.js');
@@ -19,6 +19,8 @@
19
19
  import {
20
20
  connectAuto,
21
21
  connectDirect,
22
+ connectViaSubscription,
23
+ connectViaPlan,
22
24
  disconnect as sdkDisconnect,
23
25
  isConnected,
24
26
  getStatus,
@@ -245,6 +247,14 @@ async function preValidateBalance(mnemonic) {
245
247
  /**
246
248
  * Connect to Sentinel dVPN. The ONE function an AI agent needs.
247
249
  *
250
+ * Three connection modes:
251
+ * 1. Direct payment — agent pays per-session from own wallet (default)
252
+ * 2. Subscription — operator provisioned a subscription for this agent
253
+ * 3. Plan — subscribe to plan + start session (optionally fee-granted)
254
+ *
255
+ * For modes 2 & 3, set opts.feeGranter to the operator's address — the
256
+ * agent can have 0 P2P balance and the operator covers gas.
257
+ *
248
258
  * Every step is logged with numbered phases (STEP 1/7 through STEP 7/7)
249
259
  * so an autonomous agent can track progress and diagnose failures.
250
260
  *
@@ -254,6 +264,9 @@ async function preValidateBalance(mnemonic) {
254
264
  * @param {string} [opts.nodeAddress] - Specific node (sentnode1...). Skips auto-pick.
255
265
  * @param {string} [opts.dns] - DNS preset: 'google', 'cloudflare', 'hns'
256
266
  * @param {string} [opts.protocol] - Preferred protocol: 'wireguard' or 'v2ray'
267
+ * @param {string|number} [opts.subscriptionId] - Connect via existing subscription (operator-provisioned)
268
+ * @param {string|number} [opts.planId] - Connect via plan (subscribes + starts session)
269
+ * @param {string} [opts.feeGranter] - Operator address that pays gas (sent1...). Skips balance check.
257
270
  * @param {function} [opts.onProgress] - Progress callback: (stage, message) => void
258
271
  * @param {number} [opts.timeout] - Connection timeout in ms (default: 120000 — 2 minutes)
259
272
  * @param {boolean} [opts.silent] - If true, suppress step-by-step console output
@@ -338,13 +351,17 @@ export async function connect(opts = {}) {
338
351
  const balCheck = await preValidateBalance(opts.mnemonic);
339
352
  log(3, totalSteps, 'BALANCE', `Balance: ${balCheck.p2p} | Sufficient: ${balCheck.sufficient}`);
340
353
 
341
- if (!balCheck.sufficient && !opts.dryRun) {
354
+ // Skip balance gate when fee granter is set — agent may have 0 P2P, operator pays gas
355
+ if (!balCheck.sufficient && !opts.dryRun && !opts.feeGranter) {
342
356
  const err = new Error(`Insufficient balance: ${balCheck.p2p}. Need at least ${formatP2P(MIN_BALANCE_UDVPN)}. Fund address: ${walletAddress}`);
343
357
  err.code = 'INSUFFICIENT_BALANCE';
344
358
  err.nextAction = 'fund_wallet';
345
359
  err.details = { address: walletAddress, balance: balCheck.p2p, minimum: formatP2P(MIN_BALANCE_UDVPN) };
346
360
  throw err;
347
361
  }
362
+ if (opts.feeGranter && !balCheck.sufficient) {
363
+ log(3, totalSteps, 'BALANCE', `Balance below minimum but fee granter ${opts.feeGranter} covers gas`);
364
+ }
348
365
  timings.balance = Date.now() - t0;
349
366
 
350
367
  // ── STEP 4/7: Node Selection ──────────────────────────────────────────────
@@ -549,7 +566,22 @@ export async function connect(opts = {}) {
549
566
  try {
550
567
  let result;
551
568
 
552
- if (resolvedNodeAddress) {
569
+ // ── Connection mode: subscription > plan > direct > auto ──────────────
570
+ if (opts.subscriptionId) {
571
+ // Subscription mode — operator already provisioned a subscription for this agent
572
+ log(5, totalSteps, 'SESSION', `Connecting via subscription ${opts.subscriptionId}${opts.feeGranter ? ' (fee granted)' : ''}...`);
573
+ sdkOpts.subscriptionId = opts.subscriptionId;
574
+ if (opts.feeGranter) sdkOpts.feeGranter = opts.feeGranter;
575
+ if (resolvedNodeAddress) sdkOpts.nodeAddress = resolvedNodeAddress;
576
+ result = await connectViaSubscription(sdkOpts);
577
+ } else if (opts.planId) {
578
+ // Plan mode — subscribe to plan + start session (optionally fee-granted)
579
+ log(5, totalSteps, 'SESSION', `Connecting via plan ${opts.planId}${opts.feeGranter ? ' (fee granted)' : ''}...`);
580
+ sdkOpts.planId = opts.planId;
581
+ if (opts.feeGranter) sdkOpts.feeGranter = opts.feeGranter;
582
+ if (resolvedNodeAddress) sdkOpts.nodeAddress = resolvedNodeAddress;
583
+ result = await connectViaPlan(sdkOpts);
584
+ } else if (resolvedNodeAddress) {
553
585
  // Direct connection — either user specified nodeAddress or country discovery found one
554
586
  sdkOpts.nodeAddress = resolvedNodeAddress;
555
587
  result = await connectDirect(sdkOpts);
package/ai-path/index.js CHANGED
@@ -65,10 +65,15 @@ export {
65
65
  subscribeToPlan,
66
66
  hasActiveSubscription,
67
67
  querySubscriptions,
68
+ querySubscriptionAllocations,
68
69
  queryPlanNodes,
69
70
  queryFeeGrants,
70
71
  buildFeeGrantMsg,
71
72
  broadcastWithFeeGrant,
73
+ // v2.1.0: Subscription sharing + onboarding (operator provisions agent access)
74
+ shareSubscription,
75
+ shareSubscriptionWithFeeGrant,
76
+ onboardPlanUser,
72
77
  rpcQueryNodesForPlan,
73
78
  rpcQuerySubscriptionsForAccount,
74
79
  } from '../index.js';
@@ -130,7 +130,7 @@ async function connectInternal(opts, paymentStrategy, retryStrategy, state = _de
130
130
  try {
131
131
  const bal = await getBalance(client, account.address);
132
132
  progress(onProgress, logFn, 'wallet', `${account.address} | ${bal.dvpn.toFixed(1)} P2P`);
133
- if (!opts.dryRun && bal.udvpn < 100000) {
133
+ if (!opts.dryRun && !opts.feeGranter && bal.udvpn < 100000) {
134
134
  throw new ChainError(ErrorCodes.INSUFFICIENT_BALANCE,
135
135
  `Wallet has ${bal.dvpn.toFixed(2)} P2P — need at least 0.1 P2P for a session. Fund address ${account.address} with P2P tokens.`,
136
136
  { balance: bal, address: account.address }
@@ -769,8 +769,23 @@ export async function connectViaSubscription(opts) {
769
769
  };
770
770
 
771
771
  checkAborted(signal);
772
- progress(null, opts.log || defaultLog, 'session', `Starting session via subscription ${opts.subscriptionId}...`);
773
- const result = await broadcastWithInactiveRetry(client, account.address, [msg], opts.log || defaultLog, opts.onProgress);
772
+
773
+ // Fee grant: operator pays gas for the agent (e.g., x402 managed plan flow)
774
+ const feeGranter = opts.feeGranter || null;
775
+ progress(null, opts.log || defaultLog, 'session', `Starting session via subscription ${opts.subscriptionId}${feeGranter ? ' (fee granted)' : ''}...`);
776
+
777
+ let result;
778
+ if (feeGranter) {
779
+ try {
780
+ result = await broadcastWithFeeGrant(client, account.address, [msg], feeGranter);
781
+ } catch (feeErr) {
782
+ // Fee grant TX failed — fall back to user-paid
783
+ progress(null, opts.log || defaultLog, 'session', 'Fee grant failed, paying gas from wallet...');
784
+ result = await broadcastWithInactiveRetry(client, account.address, [msg], opts.log || defaultLog, opts.onProgress);
785
+ }
786
+ } else {
787
+ result = await broadcastWithInactiveRetry(client, account.address, [msg], opts.log || defaultLog, opts.onProgress);
788
+ }
774
789
  const extracted = extractId(result, /session/i, ['session_id', 'id']);
775
790
  if (!extracted) throw new ChainError(ErrorCodes.SESSION_EXTRACT_FAILED, 'Failed to extract session ID from subscription TX result', { txHash: result.transactionHash });
776
791
  const sessionId = BigInt(extracted);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blue-js-sdk",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Decentralized VPN SDK for the Sentinel P2P bandwidth network. WireGuard + V2Ray tunnels, Cosmos blockchain, 900+ nodes. Tested on Windows. macOS/Linux support included but untested.",
5
5
  "type": "module",
6
6
  "main": "index.js",