shinjuku-shielded 0.3.59 → 0.3.60

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/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "a18f4dcc8553e48c173aa610e7b3632d8edb25dbe317dd1f820e44673dd0fa39",
5
- "bytes": 12158087,
6
- "sourceCommit": "4274f17fee017d2010ae9d1ca94c5c03d200effc",
4
+ "sha256": "ad143a47257fa8b5e147ba3f6b662576794b06184abbedce42fd4f74993d9b7e",
5
+ "bytes": 12161659,
6
+ "sourceCommit": "187538a77af6d819ea08912f6406834a8e144e22",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
@@ -1282,5 +1282,5 @@
1282
1282
  "version": "1.8.0"
1283
1283
  }
1284
1284
  ],
1285
- "publishedAt": "2026-10-22T04:38:17.814Z"
1285
+ "publishedAt": "2026-10-22T05:12:25.176Z"
1286
1286
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.59",
3
+ "version": "0.3.60",
4
4
  "mcpName": "io.github.ShinjukuStaition/shinjuku-mcp",
5
5
  "description": "Shinjuku Shielded, the private x402 facilitator on Solana: the self-custody wallet CLI and its MCP server. Shield, pay, and unshield USDC from any MCP agent, under caps you set.",
6
- "shinjukuRelease": "a18f4dcc",
7
- "shinjukuSha256": "a18f4dcc8553e48c173aa610e7b3632d8edb25dbe317dd1f820e44673dd0fa39",
6
+ "shinjukuRelease": "ad143a47",
7
+ "shinjukuSha256": "ad143a47257fa8b5e147ba3f6b662576794b06184abbedce42fd4f74993d9b7e",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -42630,7 +42630,15 @@ function warmOnions({ onions = routedOnions(), fetchImpl = globalThis.fetch, tim
42630
42630
  }
42631
42631
  return () => controller.abort();
42632
42632
  }
42633
- function raceOnions(onions, { fetchImpl = globalThis.fetch, timeoutMs = RACE_TIMEOUT_MS, path = "/api/health", onNone } = {}) {
42633
+ function raceOnions(onions, {
42634
+ fetchImpl = globalThis.fetch,
42635
+ timeoutMs = RACE_TIMEOUT_MS,
42636
+ path = "/api/health",
42637
+ onNone,
42638
+ spares = [],
42639
+ spareAfterMs = SPARE_AFTER_MS,
42640
+ onSpares
42641
+ } = {}) {
42634
42642
  const controller = new AbortController();
42635
42643
  const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(timeoutMs)]);
42636
42644
  const probe = (onion) => Promise.resolve().then(() => fetchImpl(`${onion}${path}`, onionInit({ redirect: "error", signal, headers: { accept: "application/json" } }))).then((response) => {
@@ -42639,11 +42647,46 @@ function raceOnions(onions, { fetchImpl = globalThis.fetch, timeoutMs = RACE_TIM
42639
42647
  if (!response?.ok) throw new Error(`onion answered ${response?.status}`);
42640
42648
  return onion;
42641
42649
  });
42642
- const winner = Promise.any(onions.map(probe)).catch(() => {
42643
- if (!controller.signal.aborted) onNone?.();
42644
- return onions[0];
42645
- }).finally(() => controller.abort());
42646
- return { winner, abort: () => controller.abort() };
42650
+ let startSpares = () => {
42651
+ };
42652
+ let spareTimer;
42653
+ const winner = new Promise((resolve38) => {
42654
+ let open3 = 0;
42655
+ let done = false;
42656
+ let sparesStarted = spares.length === 0;
42657
+ const settle = (onion) => {
42658
+ if (done) return;
42659
+ done = true;
42660
+ resolve38(onion);
42661
+ };
42662
+ const failed = () => {
42663
+ open3 -= 1;
42664
+ if (open3 > 0 || done) return;
42665
+ if (!sparesStarted && !signal.aborted) return startSpares();
42666
+ if (!controller.signal.aborted) onNone?.();
42667
+ settle(onions[0]);
42668
+ };
42669
+ const start = (onion) => {
42670
+ open3 += 1;
42671
+ probe(onion).then(settle, failed);
42672
+ };
42673
+ startSpares = () => {
42674
+ if (sparesStarted || done || signal.aborted) return;
42675
+ sparesStarted = true;
42676
+ clearTimeout(spareTimer);
42677
+ onSpares?.();
42678
+ for (const onion of spares) start(onion);
42679
+ };
42680
+ if (!sparesStarted) {
42681
+ spareTimer = setTimeout(() => startSpares(), spareAfterMs);
42682
+ spareTimer.unref?.();
42683
+ }
42684
+ for (const onion of onions) start(onion);
42685
+ }).finally(() => {
42686
+ clearTimeout(spareTimer);
42687
+ controller.abort();
42688
+ });
42689
+ return { winner, abort: () => controller.abort(), startSpares: () => startSpares() };
42647
42690
  }
42648
42691
  function facilitatorOrigins(argv2) {
42649
42692
  const origins = /* @__PURE__ */ new Set();
@@ -42692,7 +42735,8 @@ async function routeFacilitatorsOverOnion(argv2, {
42692
42735
  fetchImpl = globalThis.fetch,
42693
42736
  err = (text3) => process.stderr.write(text3),
42694
42737
  raceTimeoutMs = RACE_TIMEOUT_MS,
42695
- routeWaitMs = ROUTE_WAIT_MS
42738
+ routeWaitMs = ROUTE_WAIT_MS,
42739
+ spareAfterMs = SPARE_AFTER_MS
42696
42740
  } = {}) {
42697
42741
  const routes = /* @__PURE__ */ new Map();
42698
42742
  const groups = /* @__PURE__ */ new Map();
@@ -42700,17 +42744,29 @@ async function routeFacilitatorsOverOnion(argv2, {
42700
42744
  for (const origin of named.length > 0 ? named : [DEFAULT_FACILITATOR_ORIGIN]) {
42701
42745
  const pinned = PINNED_FACILITATOR_ONIONS[origin];
42702
42746
  const onions = (pinned ?? [await publishedOnion(origin, fetchImpl)]).filter((onion) => onion && V3_ONION.test(onion));
42747
+ const spares = pinned ? (PINNED_SPARE_ONIONS[origin] ?? []).filter((onion) => V3_ONION.test(onion)) : [];
42703
42748
  if (onions.length > 0) {
42704
42749
  const key = onions.join(" ");
42705
42750
  if (!groups.has(key)) {
42706
- groups.set(key, { onions, waitMs: routeWaitMs, onNone: () => err(`tor: no onion of ${origin} answered its start probe (${onions.length} tried); its requests go to its onions in turn and wait for them (Tor may still be building circuits)
42707
- `) });
42751
+ const spareHosts = spares.map((onion) => `${new URL(onion).host.slice(0, 8)}...`).join(", ");
42752
+ const group = {
42753
+ onions: [...onions, ...spares],
42754
+ live: onions.length,
42755
+ spares,
42756
+ waitMs: routeWaitMs,
42757
+ onSpares: () => err(`tor: no onion of ${origin} has answered yet; its ${spares.length} spare onions ${spareHosts} join (Tor may still be fetching the onion addresses)
42758
+ `),
42759
+ onNone: () => err(`tor: no onion of ${origin} answered its start probe (${liveOnions(group).length} tried); its requests go to its onions in turn and wait for them (Tor may still be building circuits)
42760
+ `)
42761
+ };
42762
+ groups.set(key, group);
42708
42763
  }
42709
42764
  routes.set(origin, groups.get(key));
42710
42765
  routes.set(payHostOf(origin), groups.get(key));
42711
42766
  const hosts = onions.map((onion) => `${new URL(onion).host.slice(0, 8)}...`).join(", ");
42712
42767
  const source = pinned ? "pinned in this wallet" : `published by ${origin}`;
42713
- err(`tor: ${origin} is reached over its onion service ${hosts} (${onions.length > 1 ? `${source}; the first to answer serves this run` : source}), so no Tor exit and no front such as Cloudflare sees these requests
42768
+ const spareNote = spares.length > 0 ? `; ${spares.length} spares join if none answers in ${Math.round(spareAfterMs / 1e3)} s` : "";
42769
+ err(`tor: ${origin} is reached over its onion service ${hosts} (${onions.length > 1 ? `${source}; the first to answer serves this run` : source}${spareNote}), so no Tor exit and no front such as Cloudflare sees these requests
42714
42770
  `);
42715
42771
  } else {
42716
42772
  err(`tor: ${origin} publishes no onion service; its requests go through a Tor exit, which sees the host name (TLS hides the content)
@@ -42734,12 +42790,18 @@ async function routeFacilitatorsOverOnion(argv2, {
42734
42790
  aborts.push(warmOnions({ onions: group.onions, fetchImpl }));
42735
42791
  continue;
42736
42792
  }
42737
- const race = raceOnions(group.onions, {
42793
+ const spares = group.spares ?? [];
42794
+ const race = raceOnions(liveOnions(group), {
42738
42795
  fetchImpl,
42739
42796
  timeoutMs: raceTimeoutMs,
42740
42797
  ...group.path ? { path: group.path } : {},
42741
- ...group.onNone ? { onNone: group.onNone } : {}
42798
+ ...group.onNone ? { onNone: group.onNone } : {},
42799
+ ...spares.length > 0 ? { spares, spareAfterMs, onSpares: () => {
42800
+ group.live = group.onions.length;
42801
+ group.onSpares?.();
42802
+ } } : {}
42742
42803
  });
42804
+ group.startSpares = race.startSpares;
42743
42805
  group.pending = race.winner.then((onion) => {
42744
42806
  group.winner = onion;
42745
42807
  group.pending = void 0;
@@ -42760,7 +42822,11 @@ async function routeFacilitatorsOverOnion(argv2, {
42760
42822
  const group = url && routes.get(url.origin);
42761
42823
  if (!group) return original(input, init);
42762
42824
  const send3 = (onion) => {
42763
- if (onion === void 0) onion = group.onions[(group.sent = (group.sent ?? 0) + 1) % group.onions.length];
42825
+ if (onion === void 0) {
42826
+ group.startSpares?.();
42827
+ const live = liveOnions(group);
42828
+ onion = live[(group.sent = (group.sent ?? 0) + 1) % live.length];
42829
+ }
42764
42830
  const target = `${onion}${url.pathname}${url.search}`;
42765
42831
  return original(input instanceof Request ? new Request(target, input) : target, onionInit(init));
42766
42832
  };
@@ -42782,7 +42848,7 @@ async function routeFacilitatorsOverOnion(argv2, {
42782
42848
  }
42783
42849
  };
42784
42850
  }
42785
- var SITE_ONIONS, PINNED_FACILITATOR_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, ROUTE_WAIT_MS, routedGroups, routedOnions, routedOrigins, onionsOfOrigin, routesOverOnion, GLOBAL_DISPATCHER, onionDispatchers;
42851
+ var SITE_ONIONS, SITE_SPARE_ONIONS, PINNED_FACILITATOR_ONIONS, PINNED_SPARE_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, ROUTE_WAIT_MS, SPARE_AFTER_MS, routedGroups, liveOnions, routedOnions, routedOrigins, onionsOfOrigin, routesOverOnion, GLOBAL_DISPATCHER, onionDispatchers;
42786
42852
  var init_facilitatorOnion = __esm({
42787
42853
  "wallet/src/net/facilitatorOnion.mjs"() {
42788
42854
  "use strict";
@@ -42791,11 +42857,20 @@ var init_facilitatorOnion = __esm({
42791
42857
  "http://2kfhlfuyuwvhmibjrpxqsg4nrbcxasjgjq7kmnfzgfwzptkhhznz3dad.onion",
42792
42858
  "http://chhgw7nfggqhhjawz54bpsiihdow66s2yvkh6ccnugpcdmhhww6xkkqd.onion"
42793
42859
  ]);
42860
+ SITE_SPARE_ONIONS = Object.freeze([
42861
+ "http://ntr5qkdfgff4gspwderjnqtkzekyhb5a4j76ov4dqncxmcb53gdsmmid.onion",
42862
+ "http://vofhhl336r4bnwn7hid2uvqaa72wx66xkdu2yt4n54xljsxwn2yds6qd.onion"
42863
+ ]);
42794
42864
  PINNED_FACILITATOR_ONIONS = Object.freeze({
42795
42865
  "https://shinjukustaition.com": SITE_ONIONS,
42796
42866
  "https://www.shinjukustaition.com": SITE_ONIONS,
42797
42867
  "https://pay.shinjukustaition.com": SITE_ONIONS
42798
42868
  });
42869
+ PINNED_SPARE_ONIONS = Object.freeze({
42870
+ "https://shinjukustaition.com": SITE_SPARE_ONIONS,
42871
+ "https://www.shinjukustaition.com": SITE_SPARE_ONIONS,
42872
+ "https://pay.shinjukustaition.com": SITE_SPARE_ONIONS
42873
+ });
42799
42874
  HISTORY_ONIONS = Object.freeze([
42800
42875
  "http://d6xg3phhurxhzqcsgh2mh52yf744labhrnd7mducwynlertfsjzyslqd.onion",
42801
42876
  "http://3yccoinnouxbjx3emvdxwhbu56cq4de7sfwgvcp4xb7hveccnpurcrid.onion",
@@ -42810,8 +42885,10 @@ var init_facilitatorOnion = __esm({
42810
42885
  WARM_TIMEOUT_MS = 3e4;
42811
42886
  RACE_TIMEOUT_MS = 9e4;
42812
42887
  ROUTE_WAIT_MS = 15e3;
42888
+ SPARE_AFTER_MS = ROUTE_WAIT_MS;
42813
42889
  routedGroups = [];
42814
- routedOnions = () => [...new Set(routedGroups.flatMap((group) => group.winner ? [group.winner] : group.onions))];
42890
+ liveOnions = (group) => group.onions.slice(0, group.live ?? group.onions.length);
42891
+ routedOnions = () => [...new Set(routedGroups.flatMap((group) => group.winner ? [group.winner] : liveOnions(group)))];
42815
42892
  routedOrigins = /* @__PURE__ */ new Map();
42816
42893
  onionsOfOrigin = (origin) => routedOrigins.get(origin) ?? PINNED_FACILITATOR_ONIONS[origin] ?? [];
42817
42894
  routesOverOnion = (origin) => routedOrigins.has(origin);
@@ -118627,8 +118704,12 @@ ${canonical5}`;
118627
118704
  const linkReads = readOpenLinkHeld(store);
118628
118705
  const finishing = finishSentDeposits(store);
118629
118706
  const finishDeadline = finishing.length ? balanceStartedAt + balanceRefreshWaitMs : void 0;
118707
+ let finishedHere = [];
118630
118708
  if (finishing.length) {
118631
- const finished = Promise.all(finishing).then((outcomes) => outcomes.some((outcome2) => outcome2?.stage === "done" || outcome2?.stage === "partly_shielded") ? refreshShared().then(() => "done") : "passed").catch(() => "failed");
118709
+ const finished = Promise.all(finishing).then((outcomes) => {
118710
+ finishedHere = outcomes.filter((outcome2) => outcome2?.stage === "done" || outcome2?.stage === "partly_shielded");
118711
+ return finishedHere.length ? refreshShared().then(() => "done") : "passed";
118712
+ }).catch(() => "failed");
118632
118713
  const outcome = await Promise.race([
118633
118714
  finished,
118634
118715
  new Promise((resolve38) => {
@@ -118780,6 +118861,22 @@ ${canonical5}`;
118780
118861
  } catch {
118781
118862
  landingAtomic = 0n;
118782
118863
  }
118864
+ if (landingAtomic === 0n && shieldedAtomic === null && !refresh.refreshError && finishedHere.length) {
118865
+ for (const outcome of finishedHere) {
118866
+ const id = outcome.record?.id;
118867
+ const round = outcome.record?.round ?? 1;
118868
+ let amount = payMeAmounts.get(`${id}:${round}`);
118869
+ if (amount === void 0) {
118870
+ try {
118871
+ const deposit = store.readWal(`deposit-${outcome.record?.depositId ?? payMeRoundDepositId(id, round)}`);
118872
+ if (/^[1-9][0-9]*$/.test(String(deposit?.amountAtomic ?? ""))) amount = BigInt(deposit.amountAtomic);
118873
+ } catch {
118874
+ amount = void 0;
118875
+ }
118876
+ }
118877
+ if (typeof amount === "bigint" && amount > 0n) landingAtomic += amount;
118878
+ }
118879
+ }
118783
118880
  const landedNext = landingAtomic > 0n && !refresh.refreshError ? `${usdcText2(landingAtomic)} USDC you added landed on chain and is being counted (landingAtomic, not in balanceAtomic yet): call wallet_balance again in ${POLL_SECONDS3} s; that call waits up to ${waitSeconds} s for it. Tell the user it needs no step from them` : void 0;
118784
118881
  if (landedNext) next = landedNext;
118785
118882
  const stalled = [...resumeRefusals].map(([requestId, error2]) => `"${requestId}" (${error2})`);