shinjuku-shield 0.3.102 → 0.3.103

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,13 +6,13 @@ 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.102. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.103. 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
- npx -y --no-audit --no-fund shinjuku-shield@0.3.102 setup
15
+ npx -y --no-audit --no-fund shinjuku-shield@0.3.103 setup
16
16
  ```
17
17
 
18
18
  Setup makes the wallet on this machine, a private passphrase file, and the
@@ -24,7 +24,7 @@ server to that host's config.
24
24
  ## The `shinjuku` command
25
25
 
26
26
  ```sh
27
- npm install -g --no-audit --no-fund shinjuku@0.3.102
27
+ npm install -g --no-audit --no-fund shinjuku@0.3.103
28
28
  shinjuku help
29
29
  ```
30
30
 
@@ -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.102 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.103 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.102", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.103", "mcp"] } } }
46
46
  ```
47
47
 
48
48
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
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": "3a9841626e4a97d756f610278b4e03f2571b556a7b06207c418a850b2b5200bf",
5
- "bytes": 12797194,
6
- "sourceCommit": "ca9b414196f12b2512f5457cb0160b58c8a36b25",
4
+ "sha256": "b5da74cb566cf4df5a7381cbb3eb28797aff16377f170472b6fcefce230db2a2",
5
+ "bytes": 12800520,
6
+ "sourceCommit": "b5ed9d0ff1a26115297f6e4dc56473ca9b053446",
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",
@@ -1297,5 +1297,5 @@
1297
1297
  "version": "1.8.0"
1298
1298
  }
1299
1299
  ],
1300
- "publishedAt": "2026-10-24T14:57:43.313Z"
1300
+ "publishedAt": "2026-10-24T16:04:06.349Z"
1301
1301
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.102",
3
+ "version": "0.3.103",
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": "3a984162",
7
- "shinjukuSha256": "3a9841626e4a97d756f610278b4e03f2571b556a7b06207c418a850b2b5200bf",
6
+ "shinjukuRelease": "b5da74cb",
7
+ "shinjukuSha256": "b5da74cb566cf4df5a7381cbb3eb28797aff16377f170472b6fcefce230db2a2",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -50123,6 +50123,7 @@ function whenRequestsSent(match, { count: count2 = 1, maxMs = 40, settled } = {}
50123
50123
  }
50124
50124
  }
50125
50125
  timer = setTimeout(() => done(false), maxMs);
50126
+ timer.unref?.();
50126
50127
  sent.subscribe(onSent);
50127
50128
  failed.subscribe(onFailed);
50128
50129
  settled?.then(() => done(false), () => done(false));
@@ -96324,8 +96325,12 @@ function returnArrivedFloatRefundBudgets(store, snapshot) {
96324
96325
  for (const name of store.listWal()) {
96325
96326
  if (!name.startsWith(FLOAT_PAYMENT_WAL_PREFIX)) continue;
96326
96327
  const record = store.readWal(name);
96327
- if (record?.schema !== FLOAT_PAYMENT_SCHEMA) continue;
96328
- if (record.state !== "refund_pending" || typeof record.innerRequestId !== "string") continue;
96328
+ if (record?.schema !== FLOAT_PAYMENT_SCHEMA || typeof record.innerRequestId !== "string") continue;
96329
+ if (record.state === "submitted") {
96330
+ if (refundArrived(snapshot, record.refundTo)) releaseRefundedReservation({ store, requestId: record.innerRequestId });
96331
+ continue;
96332
+ }
96333
+ if (record.state !== "refund_pending") continue;
96329
96334
  const arrivedNow = refundArrived(snapshot, record.refundTo);
96330
96335
  if (arrivedNow) releaseRefundedReservation({ store, requestId: record.innerRequestId });
96331
96336
  const outcome = arrivedNow ? "refund_arrived" : record.refundOutcome === void 0 && refundCountStopped(record, refreshedAtMs, knowsRecipients) ? "refund_not_seen" : void 0;
@@ -105409,6 +105414,16 @@ function lazyProofEntry(store, name) {
105409
105414
  return String(read()?.state);
105410
105415
  } };
105411
105416
  }
105417
+ function floatRefundNoteHeld(snapshot, refundTo) {
105418
+ const outputs = Array.isArray(snapshot?.outputs) ? snapshot.outputs : [];
105419
+ let address5;
105420
+ try {
105421
+ address5 = decodeShieldedSolanaP03Address(String(refundTo));
105422
+ } catch {
105423
+ return false;
105424
+ }
105425
+ return outputs.some((output2) => output2?.kind === "note" && output2.diversifier === address5.diversifier.toString() && output2.recipientX === address5.recipientX.toString());
105426
+ }
105412
105427
  function classify(name, record) {
105413
105428
  const state = String(record?.state);
105414
105429
  if (name.startsWith("deposit-") && record?.schema === DEPOSIT_WAL_SCHEMA) {
@@ -105481,8 +105496,10 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
105481
105496
  ...entry.record.relay?.approvedOnChain ? { approvalStanding: true } : {}
105482
105497
  }));
105483
105498
  const receipts = records.filter((entry) => entry.type === "payment" && entry.phase === "done");
105484
- const refunded = new Set(records.filter((entry) => entry.type === "float-payment" && entry.state === "refund_pending").map((entry) => entry.record.innerRequestId).filter((id) => typeof id === "string"));
105485
- const paid = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2) && !refunded.has(entry.record.requestId));
105499
+ const refunded = new Set(records.filter((entry) => entry.type === "float-payment" && (entry.state === "refund_pending" || entry.state === "submitted" && floatRefundNoteHeld(snapshotRead?.snapshot, entry.record.refundTo))).map((entry) => entry.record.innerRequestId).filter((id) => typeof id === "string"));
105500
+ const sellerReceipts = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2));
105501
+ const paid = sellerReceipts.filter((entry) => !refunded.has(entry.record.requestId));
105502
+ const refundedPaid = sellerReceipts.filter((entry) => refunded.has(entry.record.requestId));
105486
105503
  const sum2 = (values) => values.reduce((total, value) => total + BigInt(value ?? "0"), 0n);
105487
105504
  const config = state.config ?? {};
105488
105505
  const capOrNone = (value) => String(value) === NO_LIMIT_ATOMIC2 ? "none" : value;
@@ -105506,6 +105523,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
105506
105523
  pendingDeposits,
105507
105524
  paidAtomic: sum2(paid.map((entry) => paymentAmount(entry.record))).toString(),
105508
105525
  paidReceipts: paid.length,
105526
+ refundedAtomic: sum2(refundedPaid.map((entry) => paymentAmount(entry.record))).toString(),
105527
+ refundedPayments: refundedPaid.length,
105509
105528
  unshieldedAtomic: sum2(records.filter((entry) => entry.type === "unshield" && entry.phase === "done" && !entry.name.startsWith(POCKET_FILL_RECORD) && !entry.name.startsWith(MERGE_RECORD)).map((entry) => entry.record.amountAtomic)).toString(),
105510
105529
  ...budget ? { budget } : {},
105511
105530
  lastRefresh: null
@@ -105833,6 +105852,7 @@ var init_balance = __esm({
105833
105852
  init_config();
105834
105853
  init_commandName();
105835
105854
  init_requestId();
105855
+ init_ShieldedSolanaP03QuoteCrypto();
105836
105856
  PAY_ME_WAL_SCHEMA = "carbon-shielded-wallet/pay-me/v1";
105837
105857
  BALANCE_SNAPSHOT_SCHEMA = "carbon-shielded-wallet/balance-snapshot/v1";
105838
105858
  BALANCE_SNAPSHOT_FILE = "balance-snapshot.json";
@@ -128665,7 +128685,9 @@ ${canonical5}`;
128665
128685
  const sellerTransaction = view2.sellerTransaction ?? knownSellerTransfer(record) ?? (SIGNATURE_TEXT.test(String(read ?? "")) ? read : null);
128666
128686
  row(name.slice(FLOAT_PAYMENT_WAL_PREFIX.length), record, { ...view2, sellerTransaction }, {
128667
128687
  scheme: "exact",
128668
- ...sellerTransaction === null ? { note: "the seller served this request, so it was paid and no refund comes; it sent no transfer signature, and Shinjuku has not reported the landed transfer yet (a later wallet_receipts shows it)" } : {}
128688
+ // RC1 (mcp56 pass of 0.3.102, hypernatt): this said "a later wallet_receipts shows it", but Shinjuku drops a final
128689
+ // row's link fields, landedTransaction too (float.mjs retain, owner decision 4), about 1 s after the settle.
128690
+ ...sellerTransaction === null ? { note: "the seller served this request, so it was paid and no refund comes; the seller sent no transfer signature, and Shinjuku keeps no link between this payment and its transfer once the payment is final, so this receipt has no seller transaction to show" } : {}
128669
128691
  });
128670
128692
  }
128671
128693
  for (const name of names) {
@@ -134319,7 +134341,7 @@ var init_tools = __esm({
134319
134341
  capRaiseText = (flag3) => {
134320
134342
  if (capFlagFromLaunchLine(flag3)) {
134321
134343
  const both = flag3 === "--max-payment" ? " (raise the 24-hour payment limit there too when it is below the new amount: the wallet does not start with it lower)" : "";
134322
- return `The ${capName(flag3)} is set in this app's wallet entry; only the user (your owner) can change it there${both}, with amounts in USDC like 0.5, then reconnect the wallet (restart the agent host). Where the entry is: Claude Desktop: claude_desktop_config.json; Cursor: ~/.cursor/mcp.json; Claude Code: claude mcp get shinjuku-shielded shows the entry, then claude mcp remove shinjuku-shielded and claude mcp add it again with the new value; Hermes: config.yaml. Running setup does not change this limit: the value in the entry wins over the limit setup records.`;
134344
+ return `The ${capName(flag3)} is set in this app's wallet entry; only the user (your owner) can change it there${both}, with amounts in USDC like 0.5, then reconnect the wallet (restart the agent host). Where the entry is: Claude Desktop: claude_desktop_config.json; Cursor: ~/.cursor/mcp.json; Claude Code: claude mcp get ${MCP_SERVER_NAME} shows the entry (an entry added by hand can have another name: claude mcp list shows it), then claude mcp remove ${MCP_SERVER_NAME} and claude mcp add it again with the new value; Hermes: config.yaml. Running setup does not change this limit: the value in the entry wins over the limit setup records.`;
134323
134345
  }
134324
134346
  return `The ${capName(flag3)} comes from the wallet's settings. To change it, the user runs ${terminalCommand2("limits")} in a terminal: it asks each limit as a question (amounts in USDC like 0.5, or Enter for no limit). Then the user restarts the agent host.`;
134325
134347
  };
@@ -136052,6 +136074,7 @@ __export(chat_exports, {
136052
136074
  DEFAULT_MODEL: () => DEFAULT_MODEL2,
136053
136075
  chatCaps: () => chatCaps,
136054
136076
  hideProgressLines: () => hideProgressLines,
136077
+ holdProgressLines: () => holdProgressLines,
136055
136078
  isProgressLine: () => isProgressLine,
136056
136079
  knownPrivateBalance: () => knownPrivateBalance,
136057
136080
  leastAnswerAtomic: () => leastAnswerAtomic,
@@ -136151,6 +136174,33 @@ function hideProgressLines(stream = process.stderr) {
136151
136174
  stream.write = original;
136152
136175
  };
136153
136176
  }
136177
+ function holdProgressLines(stream = process.stderr) {
136178
+ const original = stream.write;
136179
+ const held = [];
136180
+ stream.write = function write(chunk, encoding, callback) {
136181
+ const text3 = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(typeof encoding === "string" ? encoding : "utf8");
136182
+ let passed = "";
136183
+ let kept2 = 0;
136184
+ for (const line of text3.split(/(?<=\n)/)) {
136185
+ if (line.endsWith("\n") && PROGRESS_LINE.test(line)) {
136186
+ held.push(line);
136187
+ kept2 += 1;
136188
+ } else passed += line;
136189
+ }
136190
+ if (kept2 === 0) return original.call(stream, chunk, encoding, callback);
136191
+ const done = typeof encoding === "function" ? encoding : callback;
136192
+ if (passed) return original.call(stream, passed, done);
136193
+ if (typeof done === "function") done();
136194
+ return true;
136195
+ };
136196
+ let released = false;
136197
+ return () => {
136198
+ if (released) return;
136199
+ released = true;
136200
+ stream.write = original;
136201
+ if (held.length) original.call(stream, held.join(""));
136202
+ };
136203
+ }
136154
136204
  function errorCodeOf(error) {
136155
136205
  if (error?.name === "InferenceError" && typeof error.code === "string") return error.code;
136156
136206
  if (error?.name === "SpendRefusalError" && typeof error.error === "string") return error.error;
@@ -162559,7 +162609,7 @@ async function runCurl(values, deps) {
162559
162609
  ${CURL_USEPOD_NEXT}`, { cause: error });
162560
162610
  else throw curlFailure(error);
162561
162611
  } finally {
162562
- await session.close();
162612
+ await closeTimed(session, "curl-price");
162563
162613
  }
162564
162614
  }
162565
162615
  if (response) return curlOutput(response, values, deps, probeId);
@@ -162597,10 +162647,31 @@ ${CURL_USEPOD_NEXT}`, { cause: error });
162597
162647
  }
162598
162648
  throw curlFailure(error);
162599
162649
  }
162650
+ sessionMark(session)("curl-paid-returned");
162651
+ return await curlOutput(response, values, deps, requestId);
162600
162652
  } finally {
162601
- await session.close();
162653
+ await closeTimed(session, "curl");
162654
+ }
162655
+ }
162656
+ function sessionMark(session) {
162657
+ let mark = () => {
162658
+ };
162659
+ try {
162660
+ if (typeof session.timing === "function") mark = session.timing();
162661
+ } catch {
162602
162662
  }
162603
- return curlOutput(response, values, deps, requestId);
162663
+ return (phase) => {
162664
+ try {
162665
+ mark(phase);
162666
+ } catch {
162667
+ }
162668
+ };
162669
+ }
162670
+ async function closeTimed(session, what) {
162671
+ const mark = sessionMark(session);
162672
+ mark(`${what}-close-start`);
162673
+ await session.close();
162674
+ mark(`${what}-closed`);
162604
162675
  }
162605
162676
  async function curlOutput(response, values, deps, requestId) {
162606
162677
  const io = deps.io;
@@ -162823,11 +162894,19 @@ async function runInfer(values, deps) {
162823
162894
  }
162824
162895
  } catch (error) {
162825
162896
  if (quiet && !given && !NOTHING_PAID_TEXT.test(String(error?.message ?? ""))) values.set(INFER_STOPPED_ID, requestId);
162897
+ stopWatch();
162898
+ await closeTimed(session, "infer");
162826
162899
  throw error;
162900
+ }
162901
+ stopWatch();
162902
+ try {
162903
+ sessionMark(session)("infer-paid-returned");
162904
+ inferOutput(result, { values, io, model, quiet, requestId });
162827
162905
  } finally {
162828
- stopWatch();
162829
- await session.close();
162906
+ await closeTimed(session, "infer");
162830
162907
  }
162908
+ }
162909
+ function inferOutput(result, { values, io, model, quiet, requestId }) {
162831
162910
  const paid = String(result.paidAtomic ?? "?");
162832
162911
  const paidText = usdcOr(result.paidAtomic);
162833
162912
  const stopped = result.finishReason === "length" ? " (the answer stopped at --max-tokens)" : "";
@@ -167901,6 +167980,7 @@ function defaultDependencies() {
167901
167980
  * what happened. Ctrl-C, or an input that ends, now stops the command with QuestionCancelled (one plain line).
167902
167981
  */
167903
167982
  ask: (question) => new Promise((resolveAnswer, rejectAnswer) => {
167983
+ const releaseProgress = holdProgressLines(process.stderr);
167904
167984
  const prompt = createInterface2({ input: process.stdin, output: process.stderr, terminal: true });
167905
167985
  let answered2 = false;
167906
167986
  let interrupted = false;
@@ -167910,6 +167990,7 @@ function defaultDependencies() {
167910
167990
  prompt.close();
167911
167991
  });
167912
167992
  prompt.on("close", () => {
167993
+ releaseProgress();
167913
167994
  if (!answered2) rejectAnswer(new QuestionCancelled(interrupted));
167914
167995
  });
167915
167996
  prompt.question(question, (answer) => {