shinjuku-shielded 0.3.174 → 0.3.176

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.174. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.176. 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 shinjuku@0.3.174 setup
15
+ npx -y shinjuku@0.3.176 setup
16
16
  ```
17
17
 
18
18
  Setup offers to install the `shinjuku` command and, on Windows, adds npm's
@@ -35,13 +35,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
35
35
  Claude Code:
36
36
 
37
37
  ```sh
38
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.174 mcp
38
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.176 mcp
39
39
  ```
40
40
 
41
41
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
42
42
 
43
43
  ```json
44
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.174", "mcp"] } } }
44
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.176", "mcp"] } } }
45
45
  ```
46
46
 
47
47
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
@@ -54,7 +54,7 @@ Codex (the Codex app and Codex CLI read the same file, `~/.codex/config.toml`):
54
54
  ```toml
55
55
  [mcp_servers.shinjuku]
56
56
  command = "npx"
57
- args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.174", "mcp"]
57
+ args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.176", "mcp"]
58
58
  startup_timeout_sec = 120
59
59
  tool_timeout_sec = 300
60
60
  ```
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": "1018a5346c79bc828bd3200c8c6c7d741cac55e279d5b88a70f895f44cc105c6",
5
- "bytes": 9392332,
6
- "sourceCommit": "1629d7280f4cef53ec8b06e6f303e11c2c8ac3fe",
4
+ "sha256": "eb8212021af756602a223fcb5e329e0eaf488b7b276850014abef3c6a917eb81",
5
+ "bytes": 9401666,
6
+ "sourceCommit": "d6237649219078cad2c31a571967809dc888a3fc",
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-05T13:40:19.887Z"
1041
+ "publishedAt": "2026-11-06T00:15:32.603Z"
1042
1042
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.174",
3
+ "version": "0.3.176",
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": "1018a534",
7
- "shinjukuSha256": "1018a5346c79bc828bd3200c8c6c7d741cac55e279d5b88a70f895f44cc105c6",
6
+ "shinjukuRelease": "eb821202",
7
+ "shinjukuSha256": "eb8212021af756602a223fcb5e329e0eaf488b7b276850014abef3c6a917eb81",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -54474,7 +54474,7 @@ function hostStartFolder(target, entry) {
54474
54474
  }
54475
54475
  function noStartFolderWords(target, entry) {
54476
54476
  if (hasPrefix(entry) || hostStartFolder(target, entry) !== void 0) return "";
54477
- return ` Your wallet folder path has a character that Windows reads as code (% & | < > ^ ! or "), so ${HOST_NAMES[target]} starts the wallet in a folder of its own: a settings file there (.npmrc) can stop the wallet. If the wallet does not start there, set up the wallet again in a folder without these characters.`;
54477
+ return ` Your wallet folder path has a character that Windows reads as code (% & | < > ^ ! " $ or \`), so ${HOST_NAMES[target]} starts the wallet in a folder of its own: a settings file there (.npmrc) can stop the wallet. If the wallet does not start there, set up the wallet again in a folder without these characters.`;
54478
54478
  }
54479
54479
  function cwdFolder(cwd) {
54480
54480
  if (typeof cwd !== "string" || !cwd) return void 0;
@@ -54492,12 +54492,12 @@ function installPackageWord(npmVersion) {
54492
54492
  const version3 = knownNpmVersion(npmVersion);
54493
54493
  return version3 ? `${COMMAND_PACKAGE}@${version3}` : COMMAND_PACKAGE;
54494
54494
  }
54495
- function oneTimeCommandLine(words2, npmVersion) {
54496
- return [npxCommandPrefix(npmVersion, { recovery: RECOVERY_WORDS.test(String(words2 ?? "")) }), words2].join(" ");
54495
+ function oneTimeCommandLine(words2, npmVersion, { npmArgs = [] } = {}) {
54496
+ return [npxCommandPrefix(npmVersion, { recovery: RECOVERY_WORDS.test(String(words2 ?? "")), npmArgs }), words2].join(" ");
54497
54497
  }
54498
- function npxCommandPrefix(npmVersion, { recovery = false } = {}) {
54498
+ function npxCommandPrefix(npmVersion, { recovery = false, npmArgs = [] } = {}) {
54499
54499
  const cache = knownNpmVersion(npmVersion) && !recovery ? npxCacheFlag() : void 0;
54500
- return ["npx", ...cache ? [cache] : [], "-y", ...NPX_INSTALL_FLAGS, npxPackageWord(npmVersion)].join(" ");
54500
+ return ["npx", ...cache ? [cache] : [], "-y", ...NPX_INSTALL_FLAGS, ...npmArgs, npxPackageWord(npmVersion)].join(" ");
54501
54501
  }
54502
54502
  function npxLineFlags(text3, ownVersion) {
54503
54503
  const own = knownNpmVersion(ownVersion);
@@ -54539,7 +54539,7 @@ function mcpServerEntry({ home, platform = process.platform, npmVersion, path, t
54539
54539
  }
54540
54540
  function npmPrefixArgs(home, platform = process.platform) {
54541
54541
  const folder = resolve6(home);
54542
- return platform === "win32" && CMD_ARG_CODE.test(folder) ? [] : [`--prefix=${folder}`];
54542
+ return platform === "win32" && WIN_ARG_CODE.test(folder) ? [] : [`--prefix=${folder}`];
54543
54543
  }
54544
54544
  function homeTorOn(home) {
54545
54545
  try {
@@ -55812,7 +55812,7 @@ function changeFileOnce(file, change, { now, platform, restrict, codec = JSON_CO
55812
55812
  }
55813
55813
  return { changed: true, ...backup ? { backup } : {} };
55814
55814
  }
55815
- var import_web396, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, COMMAND_PACKAGE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, OLD_MCP_SERVER_NAMES, P03_BINDING, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC, HOME_FOLDER, OLDER_HOME_FOLDER, defaultHomeWords, configPath, homeWalletPath, invalid, isPubkey, MAX_OWN_ADDRESSES, isOwnAddressText, salvagePath, clearConfigSalvage, HOST_TARGETS, HOST_NAMES, hostServersKey, CWD_HOSTS, hasPrefix, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RECOVERY_WORDS, NPX_WALLET_LINE, RELEASES_URL, npmStartEnvironment, CMD_ARG_CODE, NPM_START_KEYS, defaultWalletHome, CMD_UNSAFE, programOf, claudeLineDiffers, WINDOWS_SHELL_CODE, CLAUDE_DESKTOP_FILE, claudeDataDirsCache, CONTINUE_HEADER, tomlString, tomlKey, codexEntryOf, HERMES_TRUST, HERMES_TOOL_TIMEOUT_SEC, hermesCwd, HERMES_OWN_KEYS, HERMES_RESERVED_PROFILES, yamlString, hermesEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC, RACED;
55815
+ var import_web396, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, COMMAND_PACKAGE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, OLD_MCP_SERVER_NAMES, P03_BINDING, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC, HOME_FOLDER, OLDER_HOME_FOLDER, defaultHomeWords, configPath, homeWalletPath, invalid, isPubkey, MAX_OWN_ADDRESSES, isOwnAddressText, salvagePath, clearConfigSalvage, HOST_TARGETS, HOST_NAMES, hostServersKey, CWD_HOSTS, hasPrefix, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RECOVERY_WORDS, NPX_WALLET_LINE, RELEASES_URL, npmStartEnvironment, WIN_ARG_CODE, NPM_START_KEYS, defaultWalletHome, CMD_UNSAFE, programOf, claudeLineDiffers, WINDOWS_SHELL_CODE, CLAUDE_DESKTOP_FILE, claudeDataDirsCache, CONTINUE_HEADER, tomlString, tomlKey, codexEntryOf, HERMES_TRUST, HERMES_TOOL_TIMEOUT_SEC, hermesCwd, HERMES_OWN_KEYS, HERMES_RESERVED_PROFILES, yamlString, hermesEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC, RACED;
55816
55816
  var init_config = __esm({
55817
55817
  "wallet/src/setup/config.mjs"() {
55818
55818
  "use strict";
@@ -55901,7 +55901,7 @@ var init_config = __esm({
55901
55901
  NPX_WALLET_LINE = /npx (?:--(?:prefer-)?offline )?-y ((?:--no-audit --no-fund )?(?:shinjuku-shield|shinjuku)@(\d+\.\d+\.\d+))((?: (?:setup|restore))(?=\s|$|[^\w-]))?/g;
55902
55902
  RELEASES_URL = "https://github.com/ShinjukuStaition/shinjuku-shield#releases";
55903
55903
  npmStartEnvironment = (tor) => tor ? { npm_config_offline: "true" } : { npm_config_prefer_offline: "true" };
55904
- CMD_ARG_CODE = /[%&|<>^!"\r\n]/;
55904
+ WIN_ARG_CODE = /[%&|<>^!"$`\r\n]/;
55905
55905
  NPM_START_KEYS = ["npm_config_offline", "npm_config_prefer_offline"];
55906
55906
  defaultWalletHome = (home = homedir()) => setupHome({}, home);
55907
55907
  CMD_UNSAFE = /[%"\r\n]/;
@@ -135122,7 +135122,8 @@ ${canonical5}`;
135122
135122
  return said;
135123
135123
  }
135124
135124
  if (typeof parsed2.next !== "string") return said;
135125
- parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
135125
+ const note = typeof nextNote === "function" ? nextNote(parsed2) : nextNote;
135126
+ parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${note}`;
135126
135127
  const out = JSON.stringify(parsed2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
135127
135128
  return { ...said, text: out, ...blocks ? { content: [{ ...blocks[0], text: out }, ...blocks.slice(1)] } : {} };
135128
135129
  }
@@ -137280,8 +137281,29 @@ ${canonical5}`;
137280
137281
  return "not_known";
137281
137282
  }
137282
137283
  }
137283
- function newestSendCancel() {
137284
- const found = newestSendJob();
137284
+ function sendTransactionOf(requestId, job = jobs.get(`unshield:${toRecordId(requestId)}`)) {
137285
+ const fromJob = typeof job?.result?.exitTransaction === "string" ? job.result.exitTransaction : void 0;
137286
+ let fromRecord;
137287
+ try {
137288
+ const record = session.openStore().readWal(`self-pay-${moneyId(requestId)}`);
137289
+ fromRecord = typeof record?.exitTransaction === "string" ? record.exitTransaction : void 0;
137290
+ } catch {
137291
+ fromRecord = void 0;
137292
+ }
137293
+ if (fromJob && fromRecord && fromJob !== fromRecord) return { conflict: true };
137294
+ const tx = fromJob ?? fromRecord;
137295
+ return tx ? { tx } : {};
137296
+ }
137297
+ const CANCEL_SEND_WAIT_MS = 15e3;
137298
+ async function newestSendCancel({ signal } = {}) {
137299
+ let found = newestSendJob();
137300
+ if (found?.state === "sending" && !signal?.aborted) {
137301
+ const until = Date.now() + CANCEL_SEND_WAIT_MS;
137302
+ while (found.state === "sending" && !signal?.aborted && Date.now() < until) {
137303
+ await stageOrTimeout(found.job, until - Date.now(), signal);
137304
+ found = { job: found.job, state: sendJobState(found.job) };
137305
+ }
137306
+ }
137285
137307
  const receipts2 = "wallet_receipts (kind moves) shows each send of this wallet and its state";
137286
137308
  if (!found) {
137287
137309
  return refuse12(
@@ -137302,8 +137324,15 @@ ${canonical5}`;
137302
137324
  { ...ids, sendState: "confirming" }
137303
137325
  );
137304
137326
  }
137327
+ const sent = state2 === "sent" || state2 === "sending" ? sendTransactionOf(job.requestId, job) : {};
137328
+ const txFields = sent.tx ? { exitTransaction: sent.tx } : {};
137305
137329
  if (state2 === "sent") {
137306
- return refuse12("mcp_unshield_cancel_done", `${what} was already sent; nothing was changed`, `Nothing to stop. Tell the user it was sent; ${receipts2}, with its transaction`, { ...ids, sendState: "sent" });
137330
+ return refuse12(
137331
+ "mcp_unshield_cancel_done",
137332
+ `${what} was already sent${sent.tx ? ` (transaction ${sent.tx})` : ""}; nothing was changed`,
137333
+ sent.tx ? `Nothing to stop. Tell the user it was sent, with transaction ${sent.tx}` : `Nothing to stop. Tell the user it was sent; call wallet_receipts (kind moves) for its transaction and give the user that transaction${sent.conflict ? " (the wallet's two records of it disagree, so check wallet_receipts)" : ""}`,
137334
+ { ...ids, sendState: "sent", ...txFields }
137335
+ );
137307
137336
  }
137308
137337
  if (state2 === "ended") {
137309
137338
  return refuse12(
@@ -137323,11 +137352,31 @@ ${canonical5}`;
137323
137352
  }
137324
137353
  return refuse12(
137325
137354
  "mcp_unshield_cancel_sending",
137326
- `the user confirmed ${what} and it is being sent now; a confirmed send cannot be stopped; nothing was changed`,
137327
- `Nothing was stopped. Tell the user the send goes on and cannot be cancelled; ${receipts2} (it shows when it lands). Do not call wallet_unshield for it again`,
137328
- { ...ids, sendState: "sending" }
137355
+ `the user confirmed ${what} and it is being sent now${sent.tx ? ` (transaction ${sent.tx}; the wallet confirms it)` : ""}; a confirmed send cannot be stopped; nothing was changed`,
137356
+ `Nothing was stopped. Tell the user the send goes on and cannot be cancelled${sent.tx ? `, with transaction ${sent.tx}` : ""}. ` + sendRetryNext(job.requestId, sent, { amountAtomic: job.amountAtomic, to: job.to }),
137357
+ { ...ids, sendState: "sending", ...txFields }
137329
137358
  );
137330
137359
  }
137360
+ const sendRetryNext = (requestId, sent = {}, { amountAtomic, to } = {}) => `Then call wallet_unshield with request_id "${requestId}"${amountAtomic ? `, amount_atomic "${amountAtomic}"` : ""}${to ? `, and to "${to}"` : ""} (the same arguments: it reads this same send, finishes it after a restart, never asks again and never sends twice) until it answers done, and give the user its transaction${sent.conflict ? " (the wallet's two records of it disagree now: check wallet_receipts)" : ""}; do not offer the history in place of it. Do not call wallet_cancel with this request_id: for a send in parts that stops its unsent payments`;
137361
+ function stageOrTimeout(job, ms, signal) {
137362
+ return new Promise((resolve43) => {
137363
+ let timer;
137364
+ const done = () => {
137365
+ clearTimeout(timer);
137366
+ job.stageWaiters?.delete(done);
137367
+ signal?.removeEventListener?.("abort", done);
137368
+ resolve43();
137369
+ };
137370
+ if (signal?.aborted) {
137371
+ resolve43();
137372
+ return;
137373
+ }
137374
+ (job.stageWaiters ??= /* @__PURE__ */ new Set()).add(done);
137375
+ signal?.addEventListener?.("abort", done, { once: true });
137376
+ timer = setTimeout(done, Math.max(0, ms));
137377
+ timer.unref?.();
137378
+ });
137379
+ }
137331
137380
  function cancelMatches(store, requestId) {
137332
137381
  const found = [];
137333
137382
  let float;
@@ -137609,7 +137658,7 @@ ${canonical5}`;
137609
137658
  const cancel = {
137610
137659
  name: "wallet_cancel",
137611
137660
  title: "Close or stop one unfinished request",
137612
- description: `Use this when the user says "stop that send", also after this app stopped a call before its answer (then never say nothing was sent: leave request_id out and this reads the newest send of this session, or says to check wallet_receipts), or wants to stop something unfinished, by its request_id: a payment (pay_for_service or inference_chat) that can no longer settle, so its reserved balance comes back; a private send in smaller payments (an older wallet version started it): its smaller payments not sent yet stop and stay in the balance; a wallet_add_funds address nothing reached: it closes. A single send (one exit, a send of the whole balance too) cannot be stopped: the answer says its state. A private send's stop is written under the wallet lock: when another step of this wallet holds it (one of the smaller payments being sent, a payment), this call waits for that step, up to about ${CALL_BUDGET_MS / 1e3} s. Only an answer with cancelled: true means the stop is saved; an answer with stage cancelling means nothing is stopped yet: call wallet_cancel again. It finds the request by itself; when one request_id names two of these, pass kind. A payment whose bytes never left the wallet closes at once. A sent payment closes only once the chain proves it can never land (usually about two minutes after its quote expired); until then it is refused, because closing earlier could let a retry pay twice. Pays nothing and moves nothing.`,
137661
+ description: `Use this when the user says "stop that send", also after this app stopped a call before its answer (then never say nothing was sent: leave request_id out and this reads the newest send of this session, or says to check wallet_receipts), or wants to stop something unfinished, by its request_id: a payment (pay_for_service or inference_chat) that can no longer settle, so its reserved balance comes back; a private send in smaller payments (an older wallet version started it): its smaller payments not sent yet stop and stay in the balance; a wallet_add_funds address nothing reached: it closes. A single send (one exit, a send of the whole balance too) cannot be stopped: the answer says its state and, once it is out, its transaction (exitTransaction; without request_id it waits up to 15 s for it): give the user that transaction. A private send's stop is written under the wallet lock: when another step of this wallet holds it (one of the smaller payments being sent, a payment), this call waits for that step, up to about ${CALL_BUDGET_MS / 1e3} s. Only an answer with cancelled: true means the stop is saved; an answer with stage cancelling means nothing is stopped yet: call wallet_cancel again. It finds the request by itself; when one request_id names two of these, pass kind. A payment whose bytes never left the wallet closes at once. A sent payment closes only once the chain proves it can never land (usually about two minutes after its quote expired); until then it is refused, because closing earlier could let a retry pay twice. Pays nothing and moves nothing.`,
137613
137662
  inputSchema: {
137614
137663
  type: "object",
137615
137664
  properties: {
@@ -137619,10 +137668,10 @@ ${canonical5}`;
137619
137668
  additionalProperties: false
137620
137669
  },
137621
137670
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
137622
- async run(args, { progress: progress2 } = {}) {
137671
+ async run(args, { progress: progress2, signal } = {}) {
137623
137672
  const startedAt = Date.now();
137624
137673
  const requestId = args?.request_id;
137625
- if (requestId === void 0 && (args?.kind === void 0 || args.kind === "send")) return cancelWords(newestSendCancel());
137674
+ if (requestId === void 0 && (args?.kind === void 0 || args.kind === "send")) return cancelWords(await newestSendCancel({ signal }));
137626
137675
  if (typeof requestId !== "string" || !REQUEST_ID4.test(requestId)) {
137627
137676
  return refuse12("mcp_argument_invalid", "request_id must be 1-128 characters of A-Z a-z 0-9 . _ : -", "pass the request_id of the payment, send, or address to close");
137628
137677
  }
@@ -138013,7 +138062,7 @@ ${canonical5}`;
138013
138062
  const sendStoppedFailure = (job) => ({
138014
138063
  code: SEND_STOPPED,
138015
138064
  detail: `the app stopped the call before the send of ${usdcText3(job.amountAtomic)} USDC was signed, so the wallet stopped it; nothing was sent`,
138016
- next: "Nothing was sent. Tell the user the send was stopped and nothing left the private balance. Send it only if the user asks again: then call wallet_unshield with a NEW request_id (it asks the user again)",
138065
+ next: "Nothing was sent. Tell the user the send was stopped and nothing left the private balance. Send it only if the user asks again: then call wallet_unshield with a NEW request_id (it asks the user again, except for an address the user saved, which sends at once)",
138017
138066
  say: `Nothing was sent: the send of ${usdcText3(job.amountAtomic)} USDC was stopped before it was signed.`
138018
138067
  });
138019
138068
  function jobFailure(kind, job, error, linkHeld = /* @__PURE__ */ new Map()) {
@@ -138797,16 +138846,30 @@ ${canonical5}`;
138797
138846
  return { ok: false, why: error?.timedOut === true ? `no answer in ${Math.round(elicitTimeoutMs / 1e3)} s` : "this app did not answer the confirmation" };
138798
138847
  }
138799
138848
  }
138800
- function intentDoneAtMs(kind) {
138849
+ function intentDoneAtMs(kind, stopped = {}) {
138850
+ const released = () => {
138851
+ stopped.seen = true;
138852
+ return stopped.release === true ? 0 : void 0;
138853
+ };
138801
138854
  return (requestId, store) => {
138802
138855
  const job = jobs.get(`${kind}:${toRecordId(requestId)}`);
138803
138856
  if (job?.state === "done") return job.doneAtMs;
138857
+ if (kind === "unshield" && job?.state === "failed" && job.error?.code === SEND_STOPPED) return released();
138804
138858
  if (job?.state === "failed") return /NEW request_id|Do not retry this request/.test(String(job.error?.next ?? "")) ? 0 : void 0;
138805
138859
  if (job) return void 0;
138806
138860
  const names = kind === "unshield" ? [`self-pay-${moneyId(requestId)}`, privateSendPlanName(moneyId(requestId))] : [KINDS[kind].walName(requestId)];
138807
138861
  for (const name of names) {
138808
138862
  const record = store.readWal(name);
138809
138863
  if (!record) continue;
138864
+ if (kind === "unshield" && name === names[0] && record.schema === SELF_PAY_WAL_SCHEMA && record.state === "started" && record.refusedCode === SEND_STOPPED) {
138865
+ let signed = true;
138866
+ try {
138867
+ signed = selfPayMayHaveSigned(store, moneyId(requestId));
138868
+ } catch {
138869
+ signed = true;
138870
+ }
138871
+ return signed ? void 0 : released();
138872
+ }
138810
138873
  const finished = kind !== "unshield" ? ["published", "foreign_funded", "reclaimed"].includes(record.state) : record.schema === PRIVATE_SEND_WAL_SCHEMA ? record.state === "done" || record.state === "cancelled" && privateSendStatus(store, record).done : ["exited", "released", "abandoned"].includes(record.state);
138811
138874
  if (!finished) return void 0;
138812
138875
  const at = Date.parse(String(record.doneAt ?? "")) || sentAtMs(record);
@@ -138851,13 +138914,30 @@ ${canonical5}`;
138851
138914
  }
138852
138915
  return void 0;
138853
138916
  }
138917
+ const CLAIM_LEASE_WAIT_MS = 8e3;
138918
+ async function claimUnderLease(task) {
138919
+ const until = Date.now() + CLAIM_LEASE_WAIT_MS;
138920
+ for (; ; ) {
138921
+ try {
138922
+ return await underLease(task);
138923
+ } catch (error) {
138924
+ if (!/wallet_process_locked|wallet_locked/.test(String(error?.message ?? error)) || Date.now() >= until) throw error;
138925
+ await new Promise((resolve43) => {
138926
+ const timer = setTimeout(resolve43, 250);
138927
+ timer.unref?.();
138928
+ });
138929
+ }
138930
+ }
138931
+ }
138854
138932
  async function beginWithoutId(kind, args, options) {
138855
138933
  const spec = KINDS[kind];
138856
138934
  const probe = argumentsOf(kind, { ...args, request_id: "intent-probe" }, { probe: true });
138857
138935
  if (probe.refusal) return refuse12(...probe.refusal);
138858
138936
  let claimed;
138937
+ const stopped = {};
138859
138938
  try {
138860
- claimed = intents.claim(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind));
138939
+ const found = intents.find(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind, stopped));
138940
+ if (found && !stopped.seen) claimed = { ...found, created: false };
138861
138941
  } catch (error) {
138862
138942
  return refuse12(
138863
138943
  "mcp_wallet_unreadable",
@@ -138865,13 +138945,21 @@ ${canonical5}`;
138865
138945
  `call ${spec.tool} again, or pass your own request_id`
138866
138946
  );
138867
138947
  }
138948
+ if (!claimed) {
138949
+ try {
138950
+ claimed = await claimUnderLease(() => intents.claim(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind, { release: true })));
138951
+ } catch (error) {
138952
+ return /^(?:wallet_locked|wallet_process_locked)$/.test(errorCode2(error, "wallet_locked")) ? refuse12(
138953
+ "wallet_locked",
138954
+ "another program is using this wallet right now, so this call did not start anything; another app may be running the same request",
138955
+ `This call did not start a ${spec.noun}. Do not tell the user whether money moved: first call ${spec.tool === "wallet_unshield" ? "wallet_cancel with no request_id, or wallet_receipts" : "wallet_receipts"}, or call ${spec.tool} again with the same arguments in about a minute (it reads the same request and never moves money twice). If it repeats, tell the user another program (for example a Shinjuku terminal window) is using the wallet`,
138956
+ { done: false, say: "The wallet is busy with another program right now, so this request did not start here. I will check and try again.", ...lockedFields() }
138957
+ ) : refuse12("mcp_wallet_unreadable", describeError(error, { fallback: "wallet_store_unreadable" }), `call ${spec.tool} again, or pass your own request_id`);
138958
+ }
138959
+ }
138868
138960
  if (claimed.created) log(`MCP_INTENT_NEW kind=${kind} requestId=${claimed.requestId}`);
138869
138961
  const answer = await begin(kind, { ...args, request_id: claimed.requestId }, options);
138870
- return withAutoId(
138871
- answer,
138872
- spec.tool,
138873
- "The same call without request_id within 10 minutes after it ends reads this same request; if the user wants to do it again later, ask after 10 minutes or give a new request_id."
138874
- );
138962
+ return withAutoId(answer, spec.tool, (parsed2) => kind === "unshield" && parsed2.error === SEND_STOPPED ? "The same call without request_id reads this stopped send again; to send it, the user must ask again and you pass a NEW request_id." : "The same call without request_id within 10 minutes after it ends reads this same request; if the user wants to do it again later, ask after 10 minutes or give a new request_id.");
138875
138963
  }
138876
138964
  function stageChanged(job) {
138877
138965
  if (!job.changed) {
@@ -138887,6 +138975,7 @@ ${canonical5}`;
138887
138975
  const changed = job.changed;
138888
138976
  job.changed = void 0;
138889
138977
  changed?.resolve();
138978
+ if (job.stageWaiters?.size) for (const wake of [...job.stageWaiters]) wake();
138890
138979
  }
138891
138980
  async function begin(kind, args, { waitMs, ask = true, startedAt = Date.now(), progress: progress2, canProgress = false, signal, rpcId } = {}) {
138892
138981
  const spec = KINDS[kind];
@@ -139952,20 +140041,22 @@ ${canonical5}`;
139952
140041
  const what = `the send of ${usdcText3(record.amountAtomic)} USDC to ${record.mcpTo.slice(0, 4)}...${record.mcpTo.slice(-4)}`;
139953
140042
  const call = `call wallet_unshield with request_id "${requestId}", amount_atomic "${record.amountAtomic}", and to "${record.mcpTo}"`;
139954
140043
  const ask = `${call} to see its progress (it never sends twice)`;
140044
+ const sent = state2 === "sent" || state2 === "sending" ? sendTransactionOf(requestId) : {};
140045
+ const txFields = sent.tx ? { exitTransaction: sent.tx } : {};
139955
140046
  if (state2 === "sent") {
139956
140047
  return refuse12(
139957
140048
  "mcp_unshield_cancel_done",
139958
- `${what} was already sent; nothing was changed`,
139959
- `Nothing to stop. Tell the user it was sent; ${call} to read its transaction (exitTransaction; it never sends twice)`,
139960
- { requestId, sendState: "sent" }
140049
+ `${what} was already sent${sent.tx ? ` (transaction ${sent.tx})` : ""}; nothing was changed`,
140050
+ sent.tx ? `Nothing to stop. Tell the user it was sent, with transaction ${sent.tx}` : `Nothing to stop. Tell the user it was sent; call wallet_receipts (kind moves) for its transaction and give the user that transaction${sent.conflict ? " (the wallet's two records of it disagree, so check wallet_receipts)" : ""}`,
140051
+ { requestId, sendState: "sent", ...txFields }
139961
140052
  );
139962
140053
  }
139963
140054
  const how = sendNowWords(state2, intent);
139964
140055
  return refuse12(
139965
140056
  state2 === "sending" ? "mcp_unshield_cancel_sending" : "mcp_unshield_cancel_single",
139966
- `${what} ${how}; a send of one exit cannot be stopped; nothing was changed`,
139967
- `Nothing was stopped. Tell the user ${what} ${how} and cannot be cancelled; ${ask}`,
139968
- { requestId, sendState: state2 ?? record.state }
140057
+ `${what} ${how}${sent.tx ? ` (transaction ${sent.tx}; the wallet confirms it)` : ""}; a send of one exit cannot be stopped; nothing was changed`,
140058
+ state2 === "sending" ? `Nothing was stopped. Tell the user ${what} ${how} and cannot be cancelled${sent.tx ? `, with transaction ${sent.tx}` : ""}. ${sendRetryNext(requestId, sent, { amountAtomic: record.amountAtomic, to: record.mcpTo })}` : `Nothing was stopped. Tell the user ${what} ${how} and cannot be cancelled; ${ask}`,
140059
+ { requestId, sendState: state2 ?? record.state, ...txFields }
139969
140060
  );
139970
140061
  }
139971
140062
  async function cancelUnshield(args, { startedAt = Date.now(), progress: progress2 } = {}) {
@@ -140556,7 +140647,7 @@ ${canonical5}`;
140556
140647
  name: "wallet_unshield",
140557
140648
  // UX130-13: a host that defers tools may show only the title: it says where a send goes by default.
140558
140649
  title: "Send USDC privately",
140559
- description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in this app (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped, or one that failed, may still send: call wallet_cancel before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140650
+ description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in a dialog of this app; a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a dialog may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped or calls rejected or interrupted, or one that failed, may still send: call wallet_cancel (no request_id) before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140560
140651
  inputSchema: {
140561
140652
  type: "object",
140562
140653
  properties: {
@@ -145702,7 +145793,7 @@ var init_stdioServer = __esm({
145702
145793
  SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2025-06-18", "2025-03-26", "2024-11-05"]);
145703
145794
  MAX_LINE_BYTES = 4 * 1024 * 1024;
145704
145795
  POSITIVE_ATOMIC7 = /^[1-9][0-9]{0,30}$/;
145705
- INSTRUCTIONS_BASE = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map a plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel (the request_id of what to stop); "pay this URL" -> pay_for_service (no preview needed); "what does this URL cost" -> check_price (only when asked); "find a paid API for X" -> find_paid_services, then pay its url; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "which AI models" -> inference_models; "ask an AI model" -> inference_chat; "give my other AI app a private AI" -> inference_server (a local OpenAI-compatible URL and key). wallet_balance answers ONE balance (balanceAtomic); pay_for_service pays any paid URL from it. Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep (no user command). A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to pay_for_service: the same one never pays or moves money twice (a code ending in _retry_same_request: call again with it). pay_for_service never pays above the spending limits (one payment; 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. After a pay or send call that stopped or failed, call wallet_receipts (a payment) or wallet_cancel (a send) before you say whether it happened; never say nothing moved without that. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and answer you handle; reach a seller or its domain only through these tools, never yourself (no curl, browser, DNS or DNS-over-HTTPS lookup), also when the user asks for no Tor: that shows their IP address to the site or a DNS company. When an answer's poolSwitch.items[] item has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
145796
+ INSTRUCTIONS_BASE = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map a plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel (the request_id of what to stop); "pay this URL" -> pay_for_service (no preview needed); "what does this URL cost" -> check_price (only when asked); "find a paid API for X" -> find_paid_services, then pay its url; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "which AI models" -> inference_models; "ask an AI model" -> inference_chat; "give my other AI app a private AI" -> inference_server. wallet_balance answers ONE balance (balanceAtomic); pay_for_service pays any paid URL from it. Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep. A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to pay_for_service: the same one never pays or moves money twice (a code ending in _retry_same_request: call again with it). pay_for_service never pays above the spending limits (one payment; 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. After a pay or send call that stopped or failed (also one this app calls rejected or interrupted), call wallet_receipts (a payment) or wallet_cancel (a send; no request_id) before you say whether it happened; never say nothing moved without that. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and answer you handle; reach a seller or its domain only through these tools, never yourself (no curl, browser, DNS or DNS-over-HTTPS lookup), also when the user asks for no Tor: that shows their IP address to the site or a DNS company. When an answer's poolSwitch.items[] item has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
145706
145797
  SELL_LINE = "To sell or swap a token the wallet holds: wallet_transfer_token.";
145707
145798
  OWN_ADDRESS_LINE = '"Send to my (main) wallet": wallet_unshield with no to uses the address saved at setup; never ask for it.';
145708
145799
  INSTRUCTIONS = `${INSTRUCTIONS_BASE} ${SELL_LINE} ${OWN_ADDRESS_LINE} ${HOST_BLOCKED_LINE}`;
@@ -157932,7 +158023,41 @@ var PROGRESS = /^(downloading the setup files|tor: |history: )/;
157932
158023
  var LABEL = /^ {2}([a-z][a-z -]{1,12}?)( {2,}|(?<=^ {2}.{12}) )(\S.*)$/;
157933
158024
  var COMMAND = /^ {2}(npx |shinjuku |claude |codex |cursor |\$ )/;
157934
158025
  var NEXT = /^(Next|Status|Home|Then): ?(.*)$/;
157935
- function paintSetupLine(line, paint2) {
158026
+ var NOT_PROSE = /^\s|[\\/`$]|\s--|\bnpx\b|\biwr\b|\bshinjuku [a-z]/;
158027
+ var wrapWidth = (columns) => Number.isSafeInteger(columns) && columns >= 40 ? columns - 1 : void 0;
158028
+ function wrapWords(text3, width) {
158029
+ const lines = [];
158030
+ let line = "";
158031
+ for (const word2 of text3.split(" ")) {
158032
+ if (line && `${line} ${word2}`.length > width) {
158033
+ lines.push(line);
158034
+ line = word2;
158035
+ } else line = line ? `${line} ${word2}` : word2;
158036
+ }
158037
+ if (line) lines.push(line);
158038
+ return lines;
158039
+ }
158040
+ function proseKind(line) {
158041
+ if (NOT_PROSE.test(line)) return void 0;
158042
+ if (DONE.test(line)) return { mark: ["\u25C6 ", "crim"], cls: "verm" };
158043
+ if (FAIL.test(line)) return { mark: ["\u25CB ", "crim"], cls: "crim" };
158044
+ if (WARN.test(line)) return { mark: ["! ", "verm"], cls: "verm" };
158045
+ if (STEP.test(line)) return { mark: ["\u25C6 ", "crim"], cls: "bone" };
158046
+ if (PROGRESS.test(line) || COMMAND.test(line) || LABEL.test(line)) return void 0;
158047
+ if (NEXT.test(line)) return { cls: "bone", next: true };
158048
+ return { cls: "dim" };
158049
+ }
158050
+ function paintSetupLine(line, paint2, columns) {
158051
+ const width = wrapWidth(columns);
158052
+ const kind = width && line.length > width && !line.includes("\x1B[") ? proseKind(line) : void 0;
158053
+ if (kind) {
158054
+ const indent = kind.mark ? " " : "";
158055
+ const pieces = wrapWords(line, width - indent.length);
158056
+ if (pieces.length > 1) {
158057
+ const first = kind.mark ? paint2([kind.mark, [pieces[0], kind.cls]]) : kind.next ? paintSetupLine(pieces[0], paint2) : paint2([[pieces[0], kind.cls]]);
158058
+ return [first, ...pieces.slice(1).map((piece) => paint2([[indent, ""], [piece, kind.cls]]))].join("\n");
158059
+ }
158060
+ }
157936
158061
  if (line === "" || line.includes("\x1B[")) return line;
157937
158062
  if (DONE.test(line)) return paint2([["\u25C6 ", "crim"], [line, "verm"]]);
157938
158063
  if (FAIL.test(line)) return paint2([["\u25CB ", "crim"], [line, "crim"]]);
@@ -157950,20 +158075,20 @@ var THEME_CHILD_ENV = "SHINJUKU_WALLET_THEMED";
157950
158075
  function terminalThemeOn({ stdout = process.stdout, env = process.env } = {}) {
157951
158076
  return Boolean(stdout?.isTTY) && (env.CI ?? "") === "" && env.TERM !== "dumb";
157952
158077
  }
157953
- function paintChunk(chunk, paint2) {
158078
+ function paintChunk(chunk, paint2, columns) {
157954
158079
  if (typeof chunk !== "string") return chunk;
157955
158080
  const text3 = chunk;
157956
158081
  if (text3.includes("\x1B") || text3.length <= 2 || !/[A-Za-z]/.test(text3)) return chunk;
157957
158082
  return text3.split("\n").map((line) => {
157958
158083
  const [, back, rest] = /^(\r*)([^]*)$/.exec(line);
157959
- return rest.trim() === "" ? line : `${back}${paintSetupLine(rest, paint2)}`;
158084
+ return rest.trim() === "" ? line : `${back}${paintSetupLine(rest, paint2, back ? void 0 : columns)}`;
157960
158085
  }).join("\n");
157961
158086
  }
157962
158087
  function themeStreams(paint2, streams = [process.stdout, process.stderr]) {
157963
158088
  const undo = streams.map((stream) => {
157964
158089
  const original = stream.write;
157965
158090
  stream.write = function themedWrite(chunk, ...rest) {
157966
- return original.call(stream, paintChunk(chunk, paint2), ...rest);
158091
+ return original.call(stream, paintChunk(chunk, paint2, stream.isTTY ? stream.columns : void 0), ...rest);
157967
158092
  };
157968
158093
  return () => {
157969
158094
  stream.write = original;
@@ -157990,10 +158115,16 @@ function commandFooter(columns = 80, { stream = process.stdout, env = process.en
157990
158115
  ` : "";
157991
158116
  }
157992
158117
  function themedIo(io, paint2) {
157993
- const draw = (text3) => String(text3).split("\n").map((line) => paintSetupLine(line, paint2)).join("\n");
157994
- return { ...io, out: (text3) => io.out(draw(text3)), err: (text3) => io.err(draw(text3)) };
157995
- }
157996
- var themedAsk = (ask, paint2) => (prompt, ...rest) => String(prompt).startsWith("? ") || String(prompt).includes("\x1B[") ? ask(prompt, ...rest) : ask(paint2([["? ", "crim"], [String(prompt), "bone"]]), ...rest);
158118
+ const draw = (text3, stream) => String(text3).split("\n").map((line) => paintSetupLine(line, paint2, stream?.isTTY ? stream.columns : void 0)).join("\n");
158119
+ return { ...io, out: (text3) => io.out(draw(text3, process.stdout)), err: (text3) => io.err(draw(text3, process.stderr)) };
158120
+ }
158121
+ var themedAsk = (ask, paint2, stream = process.stdout) => (prompt, ...rest) => {
158122
+ const text3 = String(prompt);
158123
+ if (text3.startsWith("? ") || text3.includes("\x1B[")) return ask(prompt, ...rest);
158124
+ const width = stream?.isTTY ? wrapWidth(stream.columns) : void 0;
158125
+ const pieces = width && text3.length + 2 > width && !NOT_PROSE.test(text3) ? wrapWords(text3, width - 2) : [text3];
158126
+ return ask([paint2([["? ", "crim"], [pieces[0], "bone"]]), ...pieces.slice(1).map((piece) => paint2([[" ", ""], [piece, "bone"]]))].join("\n"), ...rest);
158127
+ };
157997
158128
  function setupHeader(paint2, columns = 80, title = "set up the wallet") {
157998
158129
  return `\x1B[0m\x1B[2J\x1B[H${commandHeader(columns, title)}
157999
158130
  `;
@@ -162197,7 +162328,10 @@ async function runSetupCommand(values, deps) {
162197
162328
  readsBack ? "Setup keeps the restored wallet on this machine (your keys never leave it), downloads and checks the setup files, and writes config.json." : "Setup makes the wallet on this machine (your keys never leave it), downloads and checks the setup files, and writes config.json.",
162198
162329
  // 0.3.97 T4, live proof 2026-10-24: the command on its own line.
162199
162330
  "An agent app then starts it with this command:",
162200
- ` ${oneTimeCommandLine("mcp", npmVersion)}`,
162331
+ // Row 25 rerun on 0.3.174 (check C): the line a user may copy into an app setup cannot add has the app entry's
162332
+ // --prefix (npm reads no .npmrc of the folder the app starts in); a Windows home with a cmd sign, $ or ` gets none, as
162333
+ // the entry. Quoted for the user's shell (hostShellWord: single quotes on POSIX; Codex round 1 on 1b3aaa99 item 2).
162334
+ ` ${oneTimeCommandLine("mcp", npmVersion, { npmArgs: npmPrefixArgs(home, deps.platform ?? process.platform).map((word2) => hostShellWord(word2, deps.platform ?? process.platform)) })}`,
162201
162335
  ""
162202
162336
  ],
162203
162337
  // The status line too, when a terminal setup started this run and printed it (AB234's live run 2026-11-04 showed