shinjuku-shielded 0.3.153 → 0.3.155

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/README.md CHANGED
@@ -6,19 +6,19 @@ answers privately, in a terminal chat or for your agent app. Its `mcp`
6
6
  command runs a local MCP server (stdio) for your agent. Your keys never
7
7
  leave your machine.
8
8
 
9
- This is version 0.3.153. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.155. The npm packages `shinjuku-shield` and
10
10
  `shinjuku-shielded` hold the same files. The command is `shinjuku`.
11
11
 
12
12
  ## Set up
13
13
 
14
14
  ```sh
15
- npm install -g shinjuku@0.3.153
15
+ npm install -g shinjuku@0.3.155
16
16
  shinjuku setup
17
17
  ```
18
18
 
19
19
  Type the two lines as they are. `npx install shinjuku` does not work: npx
20
20
  reads `install` as the name of another npm package. To set up without
21
- installing, type `npx shinjuku@0.3.153 setup`.
21
+ installing, type `npx shinjuku@0.3.155 setup`.
22
22
 
23
23
  Setup makes the wallet on this machine, a private passphrase file, and the
24
24
  checked proof tools (about 191 MB), and writes `config.json`. It also writes
@@ -36,13 +36,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
36
36
  Claude Code:
37
37
 
38
38
  ```sh
39
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.153 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.155 mcp
40
40
  ```
41
41
 
42
42
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
43
43
 
44
44
  ```json
45
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.153", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.155", "mcp"] } } }
46
46
  ```
47
47
 
48
48
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
@@ -55,7 +55,7 @@ Codex (the Codex app and Codex CLI read the same file, `~/.codex/config.toml`):
55
55
  ```toml
56
56
  [mcp_servers.shinjuku]
57
57
  command = "npx"
58
- args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.153", "mcp"]
58
+ args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.155", "mcp"]
59
59
  startup_timeout_sec = 120
60
60
  tool_timeout_sec = 300
61
61
  ```
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": "d446c36da9b526aea16cf685fb7b1532542f1eea13d0f2acabca373711bace8f",
5
- "bytes": 9048030,
6
- "sourceCommit": "9c091872009ec4e8a82ff9d5540fe00015cc93d1",
4
+ "sha256": "65d2837cf983622ed22a9d2bfb488a3a6ff0f276cb0d561b1be6074415daab0f",
5
+ "bytes": 9055610,
6
+ "sourceCommit": "5645304267acd2efd47b75cf75ae25ffae3096c5",
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",
@@ -1038,5 +1038,5 @@
1038
1038
  "version": "1.8.0"
1039
1039
  }
1040
1040
  ],
1041
- "publishedAt": "2026-11-03T05:08:33.860Z"
1041
+ "publishedAt": "2026-11-03T05:58:09.739Z"
1042
1042
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.153",
3
+ "version": "0.3.155",
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": "d446c36d",
7
- "shinjukuSha256": "d446c36da9b526aea16cf685fb7b1532542f1eea13d0f2acabca373711bace8f",
6
+ "shinjukuRelease": "65d2837c",
7
+ "shinjukuSha256": "65d2837cf983622ed22a9d2bfb488a3a6ff0f276cb0d561b1be6074415daab0f",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -17386,7 +17386,7 @@ var require_receiver = __commonJS({
17386
17386
  var require_sender = __commonJS({
17387
17387
  "node_modules/ws/lib/sender.js"(exports, module) {
17388
17388
  "use strict";
17389
- var { Duplex } = __require("stream");
17389
+ var { Duplex: Duplex2 } = __require("stream");
17390
17390
  var { randomFillSync } = __require("crypto");
17391
17391
  var {
17392
17392
  types: { isUint8Array }
@@ -18264,10 +18264,10 @@ var require_websocket = __commonJS({
18264
18264
  var EventEmitter = __require("events");
18265
18265
  var https = __require("https");
18266
18266
  var http3 = __require("http");
18267
- var net2 = __require("net");
18267
+ var net3 = __require("net");
18268
18268
  var tls = __require("tls");
18269
18269
  var { randomBytes: randomBytes33, createHash: createHash34 } = __require("crypto");
18270
- var { Duplex, Readable } = __require("stream");
18270
+ var { Duplex: Duplex2, Readable } = __require("stream");
18271
18271
  var { URL: URL4 } = __require("url");
18272
18272
  var PerMessageDeflate = require_permessage_deflate();
18273
18273
  var Receiver = require_receiver();
@@ -19008,12 +19008,12 @@ var require_websocket = __commonJS({
19008
19008
  }
19009
19009
  function netConnect(options) {
19010
19010
  options.path = options.socketPath;
19011
- return net2.connect(options);
19011
+ return net3.connect(options);
19012
19012
  }
19013
19013
  function tlsConnect(options) {
19014
19014
  options.path = void 0;
19015
19015
  if (!options.servername && options.servername !== "") {
19016
- options.servername = net2.isIP(options.host) ? "" : options.host;
19016
+ options.servername = net3.isIP(options.host) ? "" : options.host;
19017
19017
  }
19018
19018
  return tls.connect(options);
19019
19019
  }
@@ -19158,7 +19158,7 @@ var require_stream = __commonJS({
19158
19158
  "node_modules/ws/lib/stream.js"(exports, module) {
19159
19159
  "use strict";
19160
19160
  var WebSocket = require_websocket();
19161
- var { Duplex } = __require("stream");
19161
+ var { Duplex: Duplex2 } = __require("stream");
19162
19162
  function emitClose(stream) {
19163
19163
  stream.emit("close");
19164
19164
  }
@@ -19176,7 +19176,7 @@ var require_stream = __commonJS({
19176
19176
  }
19177
19177
  function createWebSocketStream(ws, options) {
19178
19178
  let terminateOnDestroy = true;
19179
- const duplex = new Duplex({
19179
+ const duplex = new Duplex2({
19180
19180
  ...options,
19181
19181
  autoDestroy: false,
19182
19182
  emitClose: false,
@@ -19302,7 +19302,7 @@ var require_websocket_server = __commonJS({
19302
19302
  "use strict";
19303
19303
  var EventEmitter = __require("events");
19304
19304
  var http3 = __require("http");
19305
- var { Duplex } = __require("stream");
19305
+ var { Duplex: Duplex2 } = __require("stream");
19306
19306
  var { createHash: createHash34 } = __require("crypto");
19307
19307
  var extension2 = require_extension();
19308
19308
  var PerMessageDeflate = require_permessage_deflate();
@@ -42866,12 +42866,15 @@ var init_keepAlive = __esm({
42866
42866
  });
42867
42867
 
42868
42868
  // wallet/src/net/tunnelGuard.mjs
42869
- function tunnelClient(Dispatcher, proxyPort) {
42869
+ import net from "node:net";
42870
+ import { Duplex } from "node:stream";
42871
+ function tunnelClient(Dispatcher, proxyPort, proxyHost) {
42870
42872
  const direct = new Dispatcher({ httpProxy: "", httpsProxy: "", noProxy: "*" });
42871
42873
  const closedInRow = /* @__PURE__ */ new Map();
42872
42874
  return {
42873
42875
  async connect(params) {
42874
42876
  const target = String(params?.path ?? "");
42877
+ if (optimisticTarget(target)) return optimisticConnect(params, { proxyHost, proxyPort, closedInRow });
42875
42878
  try {
42876
42879
  const answer = await direct.connect(params);
42877
42880
  closedInRow.delete(target);
@@ -42903,14 +42906,143 @@ function proxyDispatcher(Dispatcher, proxy) {
42903
42906
  noProxy: "",
42904
42907
  keepAliveTimeout: KEEP_ALIVE_MS,
42905
42908
  keepAliveMaxTimeout: KEEP_ALIVE_MS,
42906
- clientFactory: () => tunnelClient(Dispatcher, port)
42909
+ clientFactory: () => tunnelClient(Dispatcher, port, url.hostname)
42910
+ });
42911
+ }
42912
+ function optimisticTarget(target) {
42913
+ return String(process.env[RUN_MARK] ?? "").startsWith("tor ") && /:443$/.test(target);
42914
+ }
42915
+ function tunnelRefused(statusCode) {
42916
+ return Object.assign(new Error(`Proxy response (${statusCode}) !== 200 when HTTP Tunneling`), { name: "AbortError", code: "UND_ERR_ABORTED" });
42917
+ }
42918
+ function optimisticConnect(params, { proxyHost, proxyPort, closedInRow }) {
42919
+ const target = String(params.path);
42920
+ const head = `CONNECT ${target} HTTP/1.1\r
42921
+ ${Object.entries(params.headers ?? {}).filter(([name, value2]) => value2 !== void 0 && !/[\r\n]/.test(`${name}${value2}`)).map(([name, value2]) => `${name}: ${value2}\r
42922
+ `).join("")}\r
42923
+ `;
42924
+ const sent = [];
42925
+ let socket;
42926
+ let ready = false;
42927
+ let open4 = false;
42928
+ let reply = Buffer.alloc(0);
42929
+ let pendingReject;
42930
+ const stream = new Duplex({
42931
+ // Backpressure (Codex review): the tunnel socket pauses when this buffer is full and resumes when TLS reads.
42932
+ read() {
42933
+ if (socket && !socket.destroyed && socket.isPaused()) socket.resume();
42934
+ },
42935
+ write(chunk, encoding, done) {
42936
+ if (!open4) sent.push(Buffer.from(chunk, encoding));
42937
+ if (!ready || socket.destroyed) {
42938
+ done();
42939
+ return;
42940
+ }
42941
+ socket.write(chunk, encoding, done);
42942
+ },
42943
+ final(done) {
42944
+ socket?.end();
42945
+ done();
42946
+ },
42947
+ destroy(error, done) {
42948
+ socket?.destroy();
42949
+ done(error);
42950
+ }
42951
+ });
42952
+ stream.on("error", () => {
42953
+ });
42954
+ const abortReason = () => params.signal?.reason ?? Object.assign(new Error("This operation was aborted"), { name: "AbortError", code: "ABORT_ERR" });
42955
+ if (params.signal?.aborted) return Promise.reject(abortReason());
42956
+ const onAbort = () => {
42957
+ if (pendingReject) {
42958
+ const reject = pendingReject;
42959
+ pendingReject = void 0;
42960
+ socket?.destroy();
42961
+ stream.destroy();
42962
+ reject(abortReason());
42963
+ return;
42964
+ }
42965
+ stream.destroy(abortReason());
42966
+ };
42967
+ params.signal?.addEventListener?.("abort", onAbort, { once: true });
42968
+ const dial = (first) => new Promise((resolve39, reject) => {
42969
+ if (first) pendingReject = reject;
42970
+ reply = Buffer.alloc(0);
42971
+ ready = false;
42972
+ socket = net.connect(proxyPort, proxyHost);
42973
+ socket.setNoDelay(true);
42974
+ let connected = false;
42975
+ socket.once("connect", () => {
42976
+ connected = true;
42977
+ socket.write(head);
42978
+ for (const chunk of sent) socket.write(chunk);
42979
+ ready = true;
42980
+ if (first) pendingReject = void 0;
42981
+ resolve39();
42982
+ });
42983
+ socket.on("data", (chunk) => {
42984
+ if (open4) {
42985
+ if (!stream.push(chunk)) socket.pause();
42986
+ return;
42987
+ }
42988
+ reply = Buffer.concat([reply, chunk]);
42989
+ const end = reply.indexOf("\r\n\r\n");
42990
+ if (end < 0) return;
42991
+ const status = Number(/^HTTP\/1\.[01] (\d{3})/.exec(reply.subarray(0, reply.indexOf("\r\n")).toString("latin1"))?.[1] ?? 0);
42992
+ if (status !== 200) {
42993
+ stream.destroy(tunnelRefused(status));
42994
+ return;
42995
+ }
42996
+ open4 = true;
42997
+ sent.length = 0;
42998
+ closedInRow.delete(target);
42999
+ const rest = reply.subarray(end + 4);
43000
+ reply = Buffer.alloc(0);
43001
+ if (rest.length && !stream.push(rest)) socket.pause();
43002
+ });
43003
+ socket.on("error", (error) => {
43004
+ if (!connected) {
43005
+ if (first) {
43006
+ if (pendingReject) {
43007
+ pendingReject = void 0;
43008
+ reject(error);
43009
+ }
43010
+ } else stream.destroy(error);
43011
+ return;
43012
+ }
43013
+ if (open4) stream.destroy(error);
43014
+ });
43015
+ socket.on("close", () => {
43016
+ if (!connected || stream.destroyed) return;
43017
+ if (open4) {
43018
+ stream.push(null);
43019
+ return;
43020
+ }
43021
+ const closed = (closedInRow.get(target) ?? 0) + 1;
43022
+ if (closed < TUNNEL_CLOSED_TRIES) {
43023
+ closedInRow.set(target, closed);
43024
+ dial(false).catch((error) => stream.destroy(error));
43025
+ return;
43026
+ }
43027
+ closedInRow.delete(target);
43028
+ stream.destroy(Object.assign(
43029
+ new Error(`the local proxy closed ${TUNNEL_CLOSED_TRIES} tunnels in a row with no answer`),
43030
+ { code: "ECONNREFUSED", port: proxyPort, tunnelClosed: true }
43031
+ ));
43032
+ });
43033
+ });
43034
+ stream.once("close", () => params.signal?.removeEventListener?.("abort", onAbort));
43035
+ return dial(true).then(() => ({ statusCode: 200, socket: stream, headers: {} }), (error) => {
43036
+ params.signal?.removeEventListener?.("abort", onAbort);
43037
+ throw error;
42907
43038
  });
42908
43039
  }
42909
- var TUNNEL_CLOSED_TRIES;
43040
+ var RUN_MARK, TUNNEL_CLOSED_TRIES;
42910
43041
  var init_tunnelGuard = __esm({
42911
43042
  "wallet/src/net/tunnelGuard.mjs"() {
42912
43043
  "use strict";
42913
43044
  init_keepAlive();
43045
+ RUN_MARK = "SHINJUKU_WALLET_PROXIED";
42914
43046
  TUNNEL_CLOSED_TRIES = 3;
42915
43047
  }
42916
43048
  });
@@ -47239,7 +47371,7 @@ import { spawn } from "node:child_process";
47239
47371
  import { randomBytes as randomBytes5 } from "node:crypto";
47240
47372
  import dns from "node:dns";
47241
47373
  import http from "node:http";
47242
- import net from "node:net";
47374
+ import net2 from "node:net";
47243
47375
  function proxyRefusedConnection(error) {
47244
47376
  if (ownProxyRefused) return true;
47245
47377
  const proxied = process.env[MARK];
@@ -47335,8 +47467,8 @@ function refuseDirectSockets(proxy) {
47335
47467
  const host = target.hostname.replace(/^\[|\]$/g, "");
47336
47468
  const loopback = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
47337
47469
  const isProxy = (candidateHost, candidatePort) => Number(candidatePort) === port && (candidateHost === host || loopback.has(candidateHost) && loopback.has(host));
47338
- const connect3 = net.Socket.prototype.connect;
47339
- net.Socket.prototype.connect = function guardedConnect(...args) {
47470
+ const connect3 = net2.Socket.prototype.connect;
47471
+ net2.Socket.prototype.connect = function guardedConnect(...args) {
47340
47472
  const [first] = args;
47341
47473
  const options = Array.isArray(first) ? first[0] : first;
47342
47474
  const allowed = options !== null && typeof options === "object" ? options.path !== void 0 || isProxy(options.host ?? "localhost", options.port) : typeof first === "string" && !/^\d+$/.test(first) || isProxy(typeof args[1] === "string" ? args[1] : "localhost", first);
@@ -47365,7 +47497,7 @@ function localTorCheck(proxy) {
47365
47497
  return new Promise((resolvePromise) => {
47366
47498
  let text3 = "";
47367
47499
  let done = false;
47368
- const socket = net.connect({ host, port });
47500
+ const socket = net2.connect({ host, port });
47369
47501
  const finish = (answer, version3) => {
47370
47502
  if (done) return;
47371
47503
  done = true;
@@ -59547,7 +59679,7 @@ async function readCompactP03HistoryNow({
59547
59679
  poolPreRead,
59548
59680
  // walletHistory.mjs, with poolPreRead: page 0 asked beside the same refresh (requestCompactFirstPage).
59549
59681
  // Used as this read's page 0 when this read asks page 0 at all; readPages checks it as any page.
59550
- // Ignored with knownSnapshot.
59682
+ // Ignored with knownSnapshot, except in the read again after a refused tail hit (`tail` false).
59551
59683
  firstPreRead,
59552
59684
  // compactP03HistoryReport: run beside the spot checks once the initialization is verified
59553
59685
  // ({ snapshot, initialization, contexts }); its result comes back as `alongside`.
@@ -59561,7 +59693,7 @@ async function readCompactP03HistoryNow({
59561
59693
  // result and any throw are ignored.
59562
59694
  onList,
59563
59695
  // compactP03HistoryReport: the records of a compact cache that is not warm (cached.served). When the
59564
- // list is read (no knownSnapshot), these records (or `served`'s, when the pool outgrew them) that pass
59696
+ // list is read (no page 0 handed over by a behind wait), these records (or `served`'s, when the pool outgrew them) that pass
59565
59697
  // the start of the pool's last grid page take that page instead of every page (3b, combineCompactTail);
59566
59698
  // page 0 still goes out and is read to its end.
59567
59699
  prefix,
@@ -59570,8 +59702,8 @@ async function readCompactP03HistoryNow({
59570
59702
  // false: no tail path (compactP03HistoryReport's read again after a refused tail hit; it sent its set already).
59571
59703
  tail = true,
59572
59704
  // compactP03HistoryReport's read again (with tail false): the set the refused tail hit sent at this same count
59573
- // (sendRestOfSet's { pages, tail }), so the read again takes its pages instead of asking them a second time.
59574
- // Ignored with knownSnapshot.
59705
+ // (sendRestOfSet's { pages, tail }), so the read again takes its pages instead of asking them a second time,
59706
+ // with knownSnapshot too.
59575
59707
  restPreRead,
59576
59708
  // compactP03HistoryReport: called on a tail hit with { first (page 0's answer promise), snapshot (the pool
59577
59709
  // account read), check, rest (the set sent at the count) }, so a refused list can be read again from that same
@@ -59596,7 +59728,7 @@ async function readCompactP03HistoryNow({
59596
59728
  const hintIndex = knownSnapshot?.currentIndex ?? knownIndex;
59597
59729
  const cachedReachesKnown = hintIndex !== void 0 && served !== void 0 && Array.isArray(served.records) && Boolean(served.initialization) && BigInt(served.records.length) * SUBTREE_LEAVES3 >= hintIndex;
59598
59730
  const reusedFirst = knownSnapshot !== void 0 && knownFirst !== void 0 ? knownFirst : void 0;
59599
- const preFirst = knownSnapshot === void 0 ? firstPreRead : void 0;
59731
+ const preFirst = knownSnapshot === void 0 || !tail ? firstPreRead : void 0;
59600
59732
  const first = reusedFirst ?? (served === void 0 || hintIndex !== void 0 && !cachedReachesKnown ? preFirst ?? requestPage(source, poolString, 0, signal) : void 0);
59601
59733
  if (first !== void 0 && reusedFirst === void 0) markPhase(preFirst !== void 0 ? "compact-page0-handed-over" : "compact-page0-sent");
59602
59734
  let early = heldPicks.early;
@@ -59660,7 +59792,7 @@ async function readCompactP03HistoryNow({
59660
59792
  const count2 = Number(snapshot.currentIndex / SUBTREE_LEAVES3);
59661
59793
  if (!Number.isSafeInteger(count2)) throw refuse3("p03_compact_history_snapshot_invalid");
59662
59794
  const anchors = freezeFinalizedAnchors(snapshot);
59663
- const preRest = knownSnapshot === void 0 ? restPreRead : void 0;
59795
+ const preRest = knownSnapshot === void 0 || !tail ? restPreRead : void 0;
59664
59796
  let rest;
59665
59797
  const restOfSet = () => rest ??= preRest ?? sendRestOfSet(source, poolString, count2, tailGrid, signal);
59666
59798
  let aheadHere;
@@ -59705,7 +59837,7 @@ async function readCompactP03HistoryNow({
59705
59837
  };
59706
59838
  let prefixCheck;
59707
59839
  const readTailOrPages = async () => {
59708
- if (knownSnapshot !== void 0 || !tail) return readServedPages();
59840
+ if (reusedFirst !== void 0 || !tail) return readServedPages();
59709
59841
  const zero = first ?? preFirst ?? requestPage(source, poolString, 0, signal);
59710
59842
  if (first === void 0 && preFirst === void 0) markPhase("compact-page0-sent");
59711
59843
  const from = tailFrom(count2, tailGrid);
@@ -127519,13 +127651,13 @@ function networkPreference(network) {
127519
127651
  return { network: n };
127520
127652
  }
127521
127653
  function offerSupport(a, prefer) {
127522
- const net2 = a.network.toLowerCase();
127654
+ const net3 = a.network.toLowerCase();
127523
127655
  if (prefer.any || prefer.solana) {
127524
- if (net2 !== "solana" && net2 !== SOLANA_MAINNET.toLowerCase()) return prefer.any ? 1 : 0;
127656
+ if (net3 !== "solana" && net3 !== SOLANA_MAINNET.toLowerCase()) return prefer.any ? 1 : 0;
127525
127657
  if (a.scheme === null) return 1;
127526
127658
  return a.asset === SOLANA_USDC && SOLANA_SCHEMES.has(a.scheme) ? 2 : 1;
127527
127659
  }
127528
- return net2 === prefer.network ? 2 : 0;
127660
+ return net3 === prefer.network ? 2 : 0;
127529
127661
  }
127530
127662
  function pickOffer(accepts, prefer) {
127531
127663
  let best = accepts[0];
@@ -127538,8 +127670,8 @@ function pickOffer(accepts, prefer) {
127538
127670
  }
127539
127671
  }
127540
127672
  const atomic4 = best?.amount == null ? null : String(best.amount);
127541
- const net2 = String(best?.network ?? "").toLowerCase();
127542
- const usdc7 = atomic4 !== null && /^[0-9]{1,31}$/.test(atomic4) && best.asset === SOLANA_USDC && (net2 === "solana" || net2 === SOLANA_MAINNET.toLowerCase()) ? formatUsdc(atomic4) : void 0;
127673
+ const net3 = String(best?.network ?? "").toLowerCase();
127674
+ const usdc7 = atomic4 !== null && /^[0-9]{1,31}$/.test(atomic4) && best.asset === SOLANA_USDC && (net3 === "solana" || net3 === SOLANA_MAINNET.toLowerCase()) ? formatUsdc(atomic4) : void 0;
127543
127675
  const price2 = best ? { atomic: atomic4, ...usdc7 === void 0 ? {} : { usdc: usdc7 }, asset: best.asset || null, network: best.network || null, scheme: best.scheme ?? null } : { atomic: null, asset: null, network: null, scheme: null };
127544
127676
  return { price: price2, support };
127545
127677
  }
@@ -144559,13 +144691,30 @@ var init_homeScreen = __esm({
144559
144691
 
144560
144692
  // wallet/src/inference/brand.mjs
144561
144693
  function columnsOf(text3) {
144694
+ const parts = GRAPHEMES ? Array.from(GRAPHEMES.segment(text3), (part) => part.segment) : Array.from(text3);
144562
144695
  let count2 = 0;
144563
- for (const ch of text3) {
144564
- const code2 = ch.codePointAt(0);
144565
- count2 += code2 >= 11904 && code2 <= 42191 || code2 >= 65280 && code2 <= 65376 ? 2 : 1;
144696
+ for (const part of parts) {
144697
+ if (/^[\p{M}\p{Cf}]+$/u.test(part)) continue;
144698
+ count2 += new RegExp("\\p{Extended_Pictographic}|[\\u{1F1E6}-\\u{1F1FF}]|\u20E3", "u").test(part) || wideCode(part.codePointAt(0)) ? 2 : 1;
144566
144699
  }
144567
144700
  return count2;
144568
144701
  }
144702
+ function expandTabs(line, start = 0) {
144703
+ if (!line.includes(" ")) return line;
144704
+ let out = "";
144705
+ let column = start;
144706
+ for (const piece of line.match(/\x1b\[[0-9;]*m|\t|[^\t\x1b]+|\x1b/g) ?? []) {
144707
+ if (piece === " ") {
144708
+ const spaces = 8 - column % 8;
144709
+ out += " ".repeat(spaces);
144710
+ column += spaces;
144711
+ } else {
144712
+ out += piece;
144713
+ if (!piece.startsWith("\x1B[")) column += columnsOf(piece);
144714
+ }
144715
+ }
144716
+ return out;
144717
+ }
144569
144718
  function brandColor(stream = process.stderr, env = process.env) {
144570
144719
  if (!stream?.isTTY || (env.CI ?? "") !== "" || env.TERM === "dumb") return 0;
144571
144720
  return screenColor(stream, env);
@@ -144640,10 +144789,57 @@ function brandLine({ model: model2, tor = true, color = 0 }) {
144640
144789
  ...tor ? [[" \xB7 ", "ash"], ["Tor on", "brass"]] : []
144641
144790
  ], color);
144642
144791
  }
144792
+ function fitColumns(text3, columns, { prefix = "" } = {}) {
144793
+ const width = Math.floor(Number(columns));
144794
+ if (!(width >= 1)) return `${prefix}${text3}`;
144795
+ const visible = (part) => part.replace(/\x1b\[[0-9;]*m/g, "");
144796
+ return String(text3).split("\n").map((raw, index) => {
144797
+ const head = index === 0 ? prefix : "";
144798
+ const headColumns = columnsOf(visible(head));
144799
+ const line = expandTabs(raw, headColumns);
144800
+ if (headColumns + columnsOf(visible(line)) <= width) return `${head}${raw}`;
144801
+ const pad = " ".repeat(Math.min(visible(line).match(/^ */)[0].length, Math.max(0, width - 1)));
144802
+ const out = [];
144803
+ let current = head;
144804
+ let used = headColumns;
144805
+ let hasWord = false;
144806
+ let spaces = "";
144807
+ for (const piece of line.match(/ +|(?:\x1b\[[0-9;]*m|\x1b|[^ \x1b])+/g) ?? []) {
144808
+ if (piece[0] === " ") {
144809
+ if (hasWord) spaces += piece;
144810
+ else {
144811
+ const kept3 = Math.max(0, Math.min(piece.length, width - 1 - used));
144812
+ current += " ".repeat(kept3);
144813
+ used += kept3;
144814
+ }
144815
+ continue;
144816
+ }
144817
+ const seen = columnsOf(visible(piece));
144818
+ if (seen === 0) {
144819
+ current += piece;
144820
+ continue;
144821
+ }
144822
+ if (hasWord && used + spaces.length + seen > width) {
144823
+ out.push(current);
144824
+ current = pad;
144825
+ used = pad.length;
144826
+ } else {
144827
+ current += spaces;
144828
+ used += spaces.length;
144829
+ }
144830
+ spaces = "";
144831
+ current += piece;
144832
+ used += seen;
144833
+ hasWord = true;
144834
+ }
144835
+ out.push(current);
144836
+ return out.join("\n");
144837
+ }).join("\n");
144838
+ }
144643
144839
  function paidLine(atomic4, { color = 0, tail = "" } = {}) {
144644
144840
  return paint([["\u2713 ", "brass"], ["paid privately ", "dim"], [usdcWords(atomic4), "verm"], ...tail ? [[tail, "mute"]] : []], color);
144645
144841
  }
144646
- var HEADER_COLUMNS, STATEMENTS, KATAKANA2, PRIVATE_AI, TONES, joinParts2;
144842
+ var HEADER_COLUMNS, STATEMENTS, KATAKANA2, GRAPHEMES, wideCode, PRIVATE_AI, TONES, joinParts2;
144647
144843
  var init_brand = __esm({
144648
144844
  "wallet/src/inference/brand.mjs"() {
144649
144845
  "use strict";
@@ -144653,6 +144849,8 @@ var init_brand = __esm({
144653
144849
  HEADER_COLUMNS = 80;
144654
144850
  STATEMENTS = "Shield once. Take it everywhere. Pay unseen.";
144655
144851
  KATAKANA2 = Array.from({ length: 65438 - 65382 }, (_, index) => String.fromCharCode(65382 + index));
144852
+ GRAPHEMES = typeof Intl.Segmenter === "function" ? new Intl.Segmenter("en", { granularity: "grapheme" }) : void 0;
144853
+ wideCode = (code2) => code2 >= 4352 && code2 <= 4447 || code2 >= 11904 && code2 <= 42191 || code2 >= 44032 && code2 <= 55203 || code2 >= 63744 && code2 <= 64255 || code2 >= 65072 && code2 <= 65103 || code2 >= 65280 && code2 <= 65376 || code2 >= 65504 && code2 <= 65510 || code2 >= 131072 && code2 <= 262141;
144656
144854
  PRIVATE_AI = [["Private ", "bone"], ["AI", "crim"]];
144657
144855
  TONES = { b: "brass", c: "crim", o: "crimdim" };
144658
144856
  joinParts2 = (cells) => cells.reduce((parts, [ch, cls]) => {
@@ -144947,7 +145145,8 @@ async function runChat({ io, values, deps = {} }) {
144947
145145
  servedNow();
144948
145146
  const color = brandColor(process.stderr, env);
144949
145147
  const answerColor = brandColor(process.stdout, env);
144950
- const say3 = (line) => io.err(`${line}
145148
+ const fitErr = (text3) => fitColumns(text3, process.stderr.isTTY ? process.stderr.columns : 0);
145149
+ const say3 = (line) => io.err(`${fitErr(line)}
144951
145150
  `);
144952
145151
  const modeName = () => state.mode === "each" ? "one question at a time" : "a conversation";
144953
145152
  const youPrompt = () => paint([
@@ -145482,7 +145681,7 @@ When it says ready, type your message here.`);
145482
145681
  lineAbove = (text3) => {
145483
145682
  lastLineAt = Date.now();
145484
145683
  clearStatus();
145485
- io.err(String(text3).endsWith("\n") ? String(text3) : `${text3}
145684
+ io.err(`${fitErr(String(text3).replace(/\n$/, ""))}
145486
145685
  `);
145487
145686
  if (inPlace) status();
145488
145687
  };
@@ -145551,7 +145750,11 @@ When it says ready, type your message here.`);
145551
145750
  }
145552
145751
  state.answers += 1;
145553
145752
  if (state.mode === "talk") state.history = [...turn.messages, { role: "assistant", content }];
145554
- io.out(`${answerColor ? paint([["AI ", "crim"], ["\u203A ", "ash"]], answerColor) : ""}${content}
145753
+ io.out(`${fitColumns(
145754
+ content,
145755
+ process.stdout.isTTY ? process.stdout.columns : 0,
145756
+ { prefix: answerColor ? paint([["AI ", "crim"], ["\u203A ", "ash"]], answerColor) : "" }
145757
+ )}
145555
145758
  `);
145556
145759
  const cut2 = result.finishReason === "length" ? `(the answer stopped at its ${turn.maxTokens}-token limit) ` : "";
145557
145760
  const plainPaid = paid !== void 0 && !replayed && !unpaid;
@@ -148452,8 +148655,8 @@ async function programFeeRule(connection, ids) {
148452
148655
  const data = Buffer.from(info.data);
148453
148656
  return { withdrawalFeeRate: BigInt(data.readUInt16LE(WITHDRAWAL_FEE_RATE_OFFSET)), feeErrorMargin: BigInt(data.readUInt16LE(FEE_ERROR_MARGIN_OFFSET)) };
148454
148657
  }
148455
- function programMinimumFee(net2, rule) {
148456
- const expected = BigInt(net2) * rule.withdrawalFeeRate / 10000n;
148658
+ function programMinimumFee(net3, rule) {
148659
+ const expected = BigInt(net3) * rule.withdrawalFeeRate / 10000n;
148457
148660
  return expected === 0n ? 0n : expected * (10000n - rule.feeErrorMargin) / 10000n;
148458
148661
  }
148459
148662
  function transactAccounts({ ids, nullifiers, signer, signerTokenAccount, recipient, recipientTokenAccount, feeRecipientTokenAccount }) {
@@ -148658,14 +148861,14 @@ async function readRelayConfig2({ serviceUrl: serviceUrl2, ids, fetchImpl = fetc
148658
148861
  computeUnitPrice: BigInt(config.computeUnitPriceMicroLamports)
148659
148862
  });
148660
148863
  }
148661
- async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptionService, notes, tree, net: net2, fee, relay, recipient }) {
148864
+ async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptionService, notes, tree, net: net3, fee, relay, recipient }) {
148662
148865
  const mint = ids.mint;
148663
148866
  const lightWasm = sdk.lightWasm;
148664
148867
  const dummyKey = new internals.UtxoKeypair(encryptionService.deriveUtxoPrivateKey(), lightWasm);
148665
148868
  const inputs = [notes[0].utxo, notes[1]?.utxo ?? new internals.Utxo({ lightWasm, keypair: dummyKey, amount: "0", mintAddress: mint, blinding: blinding() })];
148666
148869
  const paths = [tree.path(notes[0].index).pathElements, notes[1] ? tree.path(notes[1].index).pathElements : zeroPath()];
148667
148870
  const total = inputs.reduce((sum2, utxo) => sum2 + BigInt(utxo.amount.toString()), 0n);
148668
- const change = total - net2 - fee;
148871
+ const change = total - net3 - fee;
148669
148872
  if (change < 0n) fail43("withdraw_balance_insufficient");
148670
148873
  const owner = new import_web3136.PublicKey(recipient);
148671
148874
  const recipientTokenAccount = getAssociatedTokenAddressSync(new import_web3136.PublicKey(mint), owner, false, TOKEN_PROGRAM_ID);
@@ -148678,7 +148881,7 @@ async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptio
148678
148881
  root: tree.root(),
148679
148882
  nextIndex: tree.elements().length,
148680
148883
  outputAmount: change,
148681
- extAmount: -net2,
148884
+ extAmount: -net3,
148682
148885
  fee,
148683
148886
  recipientTokenAccount,
148684
148887
  feeRecipientTokenAccount: relay.feeTokenAccount,
@@ -149850,11 +150053,11 @@ async function privacyCashDeposit({ store, rpcUrl, treeRpcUrl, keyFile: keyFile2
149850
150053
  });
149851
150054
  }
149852
150055
  var withdrawFee = (gross, rate, rentFee) => Math.floor(gross * rate + UNITS * rentFee);
149853
- function grossForNet(net2, rate, rentFee) {
150056
+ function grossForNet(net3, rate, rentFee) {
149854
150057
  const netOf = (gross2) => gross2 - withdrawFee(gross2, rate, rentFee);
149855
- let gross = Math.ceil((net2 + UNITS * rentFee) / (1 - rate));
149856
- while (netOf(gross) < net2) gross += 1;
149857
- while (netOf(gross - 1) >= net2) gross -= 1;
150058
+ let gross = Math.ceil((net3 + UNITS * rentFee) / (1 - rate));
150059
+ while (netOf(gross) < net3) gross += 1;
150060
+ while (netOf(gross - 1) >= net3) gross -= 1;
149858
150061
  return gross;
149859
150062
  }
149860
150063
  async function feeConfig(utils) {
@@ -150004,17 +150207,17 @@ function crowdLines({ crowd, uncounted, gross }) {
150004
150207
  const deposits2 = `deposits of ${usdc4(gross)} USDC or more landed in Privacy Cash after ${ANCHOR_WORDS[crowd.anchor]}${crowd.lowerBound ? ` (at least: ${where})` : ""}, with no withdrawal of any amount within 5 minutes after them`;
150005
150208
  return short ? `--hold-override: only ${crowd.count} ${deposits2}, of the ${crowd.needed} this hold waits for: an observer who pairs deposits and withdrawals by time and amount has few candidates besides yours` : `cover: ${crowd.count} ${deposits2}: to an observer who pairs deposits and withdrawals by time and amount, each could have funded this withdrawal as well as yours`;
150006
150209
  }
150007
- function withdrawLines({ owner, net: net2, fee, via = "sdk", relayer = PRIVACY_CASH.relayer }) {
150210
+ function withdrawLines({ owner, net: net3, fee, via = "sdk", relayer = PRIVACY_CASH.relayer }) {
150008
150211
  const reads = `reads of ${owner}'s USDC balance; no read names your source wallet`;
150009
150212
  if (via === "local") {
150010
150213
  return {
150011
- chain: `our facilitator's relayer (${relayer}) pays ${usdc4(net2)} USDC out of Privacy Cash's pool to the one-time address ${owner} and lets it hold USDC, and takes a ${usdc4(fee)} USDC fee in the same transaction; the amount and the time are public, and no transfer from your source wallet to ${owner} appears`,
150214
+ chain: `our facilitator's relayer (${relayer}) pays ${usdc4(net3)} USDC out of Privacy Cash's pool to the one-time address ${owner} and lets it hold USDC, and takes a ${usdc4(fee)} USDC fee in the same transaction; the amount and the time are public, and no transfer from your source wallet to ${owner} appears`,
150012
150215
  privacyCash: `${PRIVACY_CASH.server} sees nothing: this wallet read the tree from the chain and built the proof itself; our facilitator sees your IP address, ${owner}, the amount, the time, and the notes this withdrawal spends (if it never lands and those notes are spent later, the facilitator can link that spend to ${owner})`,
150013
150216
  rpc: `your IP address (a Tor exit with --tor), the Privacy Cash tree reads (every transaction of its USDC tree), and reads of ${owner}'s USDC balance; no read names your source wallet or one of your notes (this wallet finds which notes are spent in the tree it read), except on a rerun after an earlier proof's spend paid ${owner} or went through a lookup table: that rerun reads the proof's nullifier records on chain`
150014
150217
  };
150015
150218
  }
150016
150219
  return {
150017
- chain: `Privacy Cash's relayer (${PRIVACY_CASH.relayer}) pays ${usdc4(net2)} USDC out of its pool to the one-time address ${owner} and lets it hold USDC; Privacy Cash keeps a ${usdc4(fee)} USDC fee; the amount and the time are public, and no transfer from your source wallet to ${owner} appears`,
150220
+ chain: `Privacy Cash's relayer (${PRIVACY_CASH.relayer}) pays ${usdc4(net3)} USDC out of its pool to the one-time address ${owner} and lets it hold USDC; Privacy Cash keeps a ${usdc4(fee)} USDC fee; the amount and the time are public, and no transfer from your source wallet to ${owner} appears`,
150018
150221
  privacyCash: `${PRIVACY_CASH.server} sees your IP address, which notes are yours (so it can find the deposits that made them), the notes this withdrawal spends, and ${owner}: it can link this withdrawal to your deposit`,
150019
150222
  rpc: `your IP address (a Tor exit with --tor), the reads that check your notes, and ${reads}`
150020
150223
  };