shinjuku-shielded 0.3.160 → 0.3.162

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.160. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.162. 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.160
15
+ npm install -g shinjuku@0.3.162
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.160 setup`.
21
+ installing, type `npx shinjuku@0.3.162 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.160 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.162 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.160", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.162", "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.160", "mcp"]
58
+ args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.162", "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": "eb6f9ba6c43c52189f2d1f9124f1e23b23e471a168ffc0fe008c818c35f6b981",
5
- "bytes": 9251599,
6
- "sourceCommit": "58e77ed499568c5b3ec71e010ed2b0a1f75e20f0",
4
+ "sha256": "424f0dda8e881f18e02c8fb9b224deaa3c4f65476f26e5f66e4a6dbb30ff2d88",
5
+ "bytes": 9260304,
6
+ "sourceCommit": "d0b7984abb8e01c8c2f58b4f129ec364661902ba",
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-04T04:35:54.717Z"
1041
+ "publishedAt": "2026-11-04T07:53:19.515Z"
1042
1042
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.160",
3
+ "version": "0.3.162",
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": "eb6f9ba6",
7
- "shinjukuSha256": "eb6f9ba6c43c52189f2d1f9124f1e23b23e471a168ffc0fe008c818c35f6b981",
6
+ "shinjukuRelease": "424f0dda",
7
+ "shinjukuSha256": "424f0dda8e881f18e02c8fb9b224deaa3c4f65476f26e5f66e4a6dbb30ff2d88",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -48011,12 +48011,12 @@ var init_proxy = __esm({
48011
48011
  TOR_DEB_KEY = "https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc";
48012
48012
  TOR_DEB_KEYRING = "/usr/share/keyrings/deb.torproject.org-keyring.gpg";
48013
48013
  TOR_INSTALL = Object.freeze({
48014
- windows: `iwr ${TOR_EXPERT_BUNDLE_URL} -OutFile $env:TEMP\\tor.tar.gz; if ((Get-FileHash $env:TEMP\\tor.tar.gz -Algorithm SHA256).Hash -ne '${TOR_EXPERT_BUNDLE_SHA256}') { Remove-Item $env:TEMP\\tor.tar.gz; throw 'The Tor download does not match its pinned sha256, so it was deleted and not run' }; mkdir -Force $env:LOCALAPPDATA\\tor | Out-Null; & $env:SystemRoot\\System32\\tar.exe -xzf $env:TEMP\\tor.tar.gz -C $env:LOCALAPPDATA\\tor; & $env:LOCALAPPDATA\\tor\\tor\\tor.exe --HTTPTunnelPort '127.0.0.1:9080 IsolateDestAddr' --SocksPort 0`,
48014
+ windows: `iwr ${TOR_EXPERT_BUNDLE_URL} -OutFile $env:TEMP\\tor.tar.gz; if ((Get-FileHash $env:TEMP\\tor.tar.gz -Algorithm SHA256).Hash -ne '${TOR_EXPERT_BUNDLE_SHA256}') { Remove-Item $env:TEMP\\tor.tar.gz; throw 'The Tor download does not match its pinned sha256, so it was deleted and not run' }; mkdir -Force $env:LOCALAPPDATA\\tor | Out-Null; & $env:SystemRoot\\System32\\tar.exe -xzf $env:TEMP\\tor.tar.gz -C $env:LOCALAPPDATA\\tor; Start-Process -WindowStyle Hidden -FilePath $env:LOCALAPPDATA\\tor\\tor\\tor.exe -ArgumentList '--HTTPTunnelPort "127.0.0.1:9080 IsolateDestAddr" --SocksPort 0'`,
48015
48015
  macos: `brew install tor && (grep -qxF '${TUNNEL_LINE}' "$(brew --prefix)/etc/tor/torrc" || echo '${TUNNEL_LINE}' >> "$(brew --prefix)/etc/tor/torrc") && brew services restart tor`,
48016
48016
  linux: `sudo apt install -y apt-transport-https gnupg wget && wget -qO- ${TOR_DEB_KEY} | gpg --dearmor | sudo tee ${TOR_DEB_KEYRING} >/dev/null && printf 'Types: deb\\nURIs: https://deb.torproject.org/torproject.org/\\nSuites: %s\\nComponents: main\\nSigned-By: ${TOR_DEB_KEYRING}\\n' "$(. /etc/os-release && echo "$VERSION_CODENAME")" | sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null && sudo apt update && sudo apt install -y tor deb.torproject.org-keyring && (grep -qxF '${TUNNEL_LINE}' /etc/tor/torrc || echo '${TUNNEL_LINE}' | sudo tee -a /etc/tor/torrc) && sudo service tor restart`
48017
48017
  });
48018
48018
  torStart = (platform = process.platform, tunnelHost) => {
48019
- const base = platform === "win32" ? { where: "PowerShell (keep its window open while the wallet runs)", command: TOR_INSTALL.windows } : platform === "darwin" ? { where: "Terminal", command: TOR_INSTALL.macos } : { where: "a terminal", command: TOR_INSTALL.linux };
48019
+ const base = platform === "win32" ? { where: "PowerShell (Tor then runs in the background, so you can close that window)", command: TOR_INSTALL.windows } : platform === "darwin" ? { where: "Terminal", command: TOR_INSTALL.macos } : { where: "a terminal", command: TOR_INSTALL.linux };
48020
48020
  return LOCAL_TUNNEL.test(tunnelHost ?? "") ? { ...base, command: base.command.replaceAll("127.0.0.1:9080", tunnelHost) } : base;
48021
48021
  };
48022
48022
  LOCAL_TUNNEL = /^127\.0\.0\.1:\d{1,5}$/;
@@ -48036,12 +48036,12 @@ ${command}`;
48036
48036
  TOR_WINDOW = Object.freeze({ win32: "PowerShell", darwin: "Terminal" });
48037
48037
  torNotRunningMessage = (address4, platform = process.platform, { fromConfig = false } = {}) => {
48038
48038
  const { command } = torStart(platform);
48039
- const keep = platform === "win32" ? " and keep that window open" : "";
48039
+ const keep = platform === "win32" ? " (Tor then runs in the background, so you can close that window)" : "";
48040
48040
  return `tor_not_running: Tor is not running on this machine, so nothing was sent.${fromConfig ? TOR_FROM_CONFIG_SENTENCE : ""} Tor hides your IP address. Start it with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}${keep}:
48041
48041
  ${command}`;
48042
48042
  };
48043
- torUpdateHow = (platform = process.platform) => platform === "win32" ? `${TOR_STOP_OLD_WINDOWS} Then start a newer Tor with the command below in PowerShell and keep that window open` : `Install a newer Tor with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}`;
48044
- TOR_STOP_OLD_WINDOWS = "Stop your old Tor first: close the window it runs in, or for a Tor in WSL run wsl -u root service tor stop .";
48043
+ torUpdateHow = (platform = process.platform) => platform === "win32" ? `${TOR_STOP_OLD_WINDOWS} Then start a newer Tor with the command below in PowerShell (it runs in the background, so you can close that window)` : `Install a newer Tor with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}`;
48044
+ TOR_STOP_OLD_WINDOWS = "Stop your old Tor first: in PowerShell run Stop-Process -Name tor , or for a Tor in WSL run wsl -u root service tor stop .";
48045
48045
  torTooOldWhy = (version3) => `Your Tor program is too old (${version3}). It has a known fault that can link two of your payments, so the wallet sent nothing.`;
48046
48046
  torTooOldMessage = (version3, platform = process.platform, { fromConfig = false, tunnelHost } = {}) => {
48047
48047
  const { command } = torStart(platform, tunnelHost);
@@ -54272,8 +54272,22 @@ function hostShellWord(word, platform = process.platform) {
54272
54272
  return `"${word}"`;
54273
54273
  }
54274
54274
  function hostConfigFile(target, { platform = process.platform, env = process.env, home = homedir() } = {}) {
54275
+ const set = (name) => typeof env[name] === "string" && env[name].trim() ? env[name].trim() : void 0;
54275
54276
  if (target === "cursor") return join8(home, ".cursor", "mcp.json");
54276
54277
  if (target === "codex") return join8(env.CODEX_HOME || join8(home, ".codex"), "config.toml");
54278
+ if (target === "vscode") {
54279
+ const appData = platform === "win32" ? env.APPDATA || join8(home, "AppData", "Roaming") : platform === "darwin" ? join8(home, "Library", "Application Support") : env.XDG_CONFIG_HOME || join8(home, ".config");
54280
+ return join8(appData, "Code", "User", "mcp.json");
54281
+ }
54282
+ if (target === "devin") {
54283
+ return join8(platform === "win32" ? join8(home, "AppData", "Roaming", "devin") : join8(env.XDG_CONFIG_HOME || join8(home, ".config"), "devin"), "mcp_config.json");
54284
+ }
54285
+ if (target === "cline") {
54286
+ return set("CLINE_MCP_SETTINGS_PATH") ?? join8(set("CLINE_DATA_DIR") ?? join8(set("CLINE_DIR") ?? join8(home, ".cline"), "data"), "settings", "cline_mcp_settings.json");
54287
+ }
54288
+ if (target === "continue") return join8(set("CONTINUE_GLOBAL_DIR") ? resolve6(set("CONTINUE_GLOBAL_DIR")) : join8(home, ".continue"), "mcpServers", `${MCP_SERVER_NAME}.yaml`);
54289
+ if (target === "gemini") return join8(set("GEMINI_CLI_HOME") ?? home, ".gemini", "settings.json");
54290
+ if (target === "antigravity") return join8(home, ".gemini", "config", "mcp_config.json");
54277
54291
  if (target === "claude-desktop") {
54278
54292
  if (platform === "win32") return join8(env.APPDATA || join8(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
54279
54293
  if (platform === "darwin") return join8(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
@@ -54281,8 +54295,17 @@ function hostConfigFile(target, { platform = process.platform, env = process.env
54281
54295
  }
54282
54296
  return void 0;
54283
54297
  }
54284
- function hostConfigText({ home, env = process.env, platform = process.platform, npmVersion, skip = /* @__PURE__ */ new Set() } = {}) {
54298
+ function hostConfigText({ home, env = process.env, platform = process.platform, npmVersion, skip = /* @__PURE__ */ new Set(), userHome = homedir() } = {}) {
54285
54299
  const entry = mcpServerEntry({ home, platform, npmVersion });
54300
+ const more = ["vscode", "devin", "cline", "continue", "gemini", "antigravity"].filter((target) => !skip.has(target)).flatMap((target) => {
54301
+ const file = hostConfigFile(target, { platform, env, home: userHome });
54302
+ if (target === "continue") return [`${HOST_NAMES[target]}: save these lines as ${file}:`, ...continueBlockText(hostEntry(target, entry)).trimEnd().split("\n").map((line) => ` ${line}`)];
54303
+ const key = hostServersKey(target);
54304
+ return [
54305
+ `${HOST_NAMES[target]} (${file}): in that file, add these lines inside the { } after "${key}" (put a comma after the entry before them; a file with no "${key}" gets "${key}": { } first):`,
54306
+ ...`${JSON.stringify(MCP_SERVER_NAME)}: ${JSON.stringify(hostEntry(target, entry), null, 2)}`.split("\n").map((line) => ` ${line}`)
54307
+ ];
54308
+ });
54286
54309
  const member = `${JSON.stringify(MCP_SERVER_NAME)}: ${JSON.stringify(entry, null, 2)}`;
54287
54310
  const files = [["claude-desktop", "Claude Desktop (claude_desktop_config.json)"], ["cursor", "Cursor (~/.cursor/mcp.json)"]].filter(([target]) => !skip.has(target)).map(([, label]) => label);
54288
54311
  return [
@@ -54294,9 +54317,48 @@ function hostConfigText({ home, env = process.env, platform = process.platform,
54294
54317
  ...skip.has("codex") ? [] : [
54295
54318
  "Codex (the Codex app and Codex CLI), at the end of ~/.codex/config.toml:",
54296
54319
  ...codexTomlBlock(entry).trimEnd().split("\n").map((line) => ` ${line}`)
54297
- ]
54320
+ ],
54321
+ ...more
54298
54322
  ].join("\n");
54299
54323
  }
54324
+ function continueBlockText(entry) {
54325
+ return [
54326
+ "# The Shinjuku Shield wallet's server, written by its setup.",
54327
+ ...CONTINUE_HEADER,
54328
+ ` - ${JSON.stringify({ name: MCP_SERVER_NAME, ...entry })}`,
54329
+ ""
54330
+ ].join("\n");
54331
+ }
54332
+ function continueServersIn(text3) {
54333
+ const lines = text3.replace(/^/, "").split(/\r?\n/).filter((line) => line.trim() && !/^\s*#/.test(line));
54334
+ const items = lines.filter((line) => /^\s*-\s/.test(line));
54335
+ if (items.length !== 1 || !sameJson(lines.filter((line) => !/^\s*-\s/.test(line)).map((line) => line.trim()), CONTINUE_HEADER)) return void 0;
54336
+ let value2;
54337
+ try {
54338
+ value2 = JSON.parse(items[0].replace(/^\s*-\s*/, ""));
54339
+ } catch {
54340
+ return void 0;
54341
+ }
54342
+ if (!isPlainObject(value2) || typeof value2.name !== "string") return void 0;
54343
+ const { name, ...entry } = value2;
54344
+ return { [name]: entry };
54345
+ }
54346
+ function addToContinueHost(file, entry, { now = /* @__PURE__ */ new Date(), force = false, platform = process.platform, restrict = restrictToOwnerWindows } = {}) {
54347
+ const result = changeJsonFile(file, (text3) => {
54348
+ if (!text3.trim()) return continueBlockText(entry);
54349
+ const servers = continueServersIn(text3);
54350
+ if (!servers) {
54351
+ throw new Error("setup_host_config_unreadable: Continue's block file for the wallet was changed by hand, so setup left it alone; add the printed entry by hand");
54352
+ }
54353
+ const [[name, existing]] = Object.entries(servers);
54354
+ if (name === MCP_SERVER_NAME && sameJson(existing, entry)) return void 0;
54355
+ if (!force && !(name === MCP_SERVER_NAME && isOlderSetupEntry(existing, entry))) {
54356
+ throw new Error(`setup_host_config_conflict: Continue's block file for the wallet holds a different server, so setup left it alone; rerun setup with --force to replace it (the old file is backed up first), or edit it by hand`);
54357
+ }
54358
+ return continueBlockText(entry);
54359
+ }, { now, platform, restrict, codec: TEXT_CODEC });
54360
+ return { file, ...result, replaced: [] };
54361
+ }
54300
54362
  function codexTomlBlock(entry) {
54301
54363
  const lines = [
54302
54364
  `[mcp_servers.${tomlKey(MCP_SERVER_NAME)}]`,
@@ -54581,31 +54643,32 @@ function restrictToOwnerWindows(file, { run: run2 = execFileSync2 } = {}) {
54581
54643
  throw new Error("setup_host_config_acl_failed: setup could not limit the file to only you on this computer (icacls), so it did not write it; add the printed entry by hand");
54582
54644
  }
54583
54645
  }
54584
- function addToJsonHost(file, entry, { now = /* @__PURE__ */ new Date(), force = false, platform = process.platform, restrict = restrictToOwnerWindows } = {}) {
54646
+ function addToJsonHost(file, entry, { now = /* @__PURE__ */ new Date(), force = false, platform = process.platform, restrict = restrictToOwnerWindows, key = "mcpServers" } = {}) {
54585
54647
  try {
54586
- return addToStrictJsonHost(file, entry, { now, force, platform, restrict });
54648
+ return addToStrictJsonHost(file, entry, { now, force, platform, restrict, key });
54587
54649
  } catch (error) {
54588
54650
  if (error?.jsonc !== true) throw error;
54589
- const result = changeJsonFile(file, (text3) => jsoncWithMember(text3, entry, { force }), { now, platform, restrict, codec: TEXT_CODEC });
54651
+ const result = changeJsonFile(file, (text3) => jsoncWithMember(text3, entry, { force, key }), { now, platform, restrict, codec: TEXT_CODEC });
54590
54652
  return { file, ...result, replaced: [] };
54591
54653
  }
54592
54654
  }
54593
- function addToStrictJsonHost(file, entry, { now, force, platform, restrict }) {
54655
+ function addToStrictJsonHost(file, entry, { now, force, platform, restrict, key }) {
54594
54656
  let replaced = [];
54595
54657
  const result = changeJsonFile(file, (current) => {
54596
- if (current.mcpServers !== void 0 && !isPlainObject(current.mcpServers)) {
54597
- throw new Error("setup_host_config_unreadable: the host's config file has an mcpServers that is not a JSON object, so setup left it alone; fix it or add the printed entry by hand");
54658
+ const held = current[key];
54659
+ if (held !== void 0 && !isPlainObject(held)) {
54660
+ throw new Error(`setup_host_config_unreadable: the host's config file has a ${key} that is not a JSON object, so setup left it alone; fix it or add the printed entry by hand`);
54598
54661
  }
54599
- replaced = OLD_MCP_SERVER_NAMES.filter((name) => current.mcpServers !== void 0 && Object.hasOwn(current.mcpServers, name));
54600
- const existing = current.mcpServers !== void 0 && Object.hasOwn(current.mcpServers, MCP_SERVER_NAME) ? current.mcpServers[MCP_SERVER_NAME] : void 0;
54662
+ replaced = OLD_MCP_SERVER_NAMES.filter((name) => held !== void 0 && Object.hasOwn(held, name));
54663
+ const existing = held !== void 0 && Object.hasOwn(held, MCP_SERVER_NAME) ? held[MCP_SERVER_NAME] : void 0;
54601
54664
  if (existing !== void 0) {
54602
54665
  if (sameJson(existing, entry) && replaced.length === 0) return void 0;
54603
54666
  if (!force && !sameJson(existing, entry) && !isOlderSetupEntry(existing, entry)) {
54604
54667
  throw new Error(`setup_host_config_conflict: the host's config file already has a different "${MCP_SERVER_NAME}" server, so setup left it alone; rerun setup with --force to replace it (the old file is backed up first), or edit it by hand`);
54605
54668
  }
54606
54669
  }
54607
- const servers = Object.fromEntries(Object.entries(current.mcpServers ?? {}).filter(([name]) => !OLD_MCP_SERVER_NAMES.includes(name)));
54608
- return { ...current, mcpServers: { ...servers, [MCP_SERVER_NAME]: entry } };
54670
+ const servers = Object.fromEntries(Object.entries(held ?? {}).filter(([name]) => !OLD_MCP_SERVER_NAMES.includes(name)));
54671
+ return { ...current, [key]: { ...servers, [MCP_SERVER_NAME]: entry } };
54609
54672
  }, { now, platform, restrict });
54610
54673
  return { file, ...result, replaced };
54611
54674
  }
@@ -54725,13 +54788,13 @@ function memberSlot(src, object) {
54725
54788
  }
54726
54789
  return { at: close, comma: src[at] === "," ? void 0 : last.value.end };
54727
54790
  }
54728
- function jsoncWithMember(text3, entry, { force }) {
54791
+ function jsoncWithMember(text3, entry, { force, key = "mcpServers" }) {
54729
54792
  const unreadable = () => {
54730
54793
  throw new Error("setup_host_config_unreadable: the host's config file has comments and names the wallet's server in a way setup does not change by itself, so setup left it alone; add the printed entry by hand");
54731
54794
  };
54732
54795
  const root = parseJsonc(text3);
54733
54796
  if (root.kind !== "object") unreadable();
54734
- const servers = root.members.filter((member) => member.key === "mcpServers");
54797
+ const servers = root.members.filter((member) => member.key === key);
54735
54798
  if (servers.length > 1 || servers[0] && servers[0].value.kind !== "object") unreadable();
54736
54799
  const holder = servers[0]?.value;
54737
54800
  if (holder && holder.members.some((member) => OLD_MCP_SERVER_NAMES.includes(member.key))) unreadable();
@@ -54752,7 +54815,7 @@ ${indent}`) });
54752
54815
  const slot3 = memberSlot(text3, object);
54753
54816
  const indent = `${indentAt(text3, object.start)} `;
54754
54817
  const member = holder ? `${JSON.stringify(MCP_SERVER_NAME)}: ${JSON.stringify(entry, null, 2).replace(/\n/g, `
54755
- ${indent}`)}` : `"mcpServers": ${JSON.stringify({ [MCP_SERVER_NAME]: entry }, null, 2).replace(/\n/g, `
54818
+ ${indent}`)}` : `${JSON.stringify(key)}: ${JSON.stringify({ [MCP_SERVER_NAME]: entry }, null, 2).replace(/\n/g, `
54756
54819
  ${indent}`)}`;
54757
54820
  const lineStart = text3.lastIndexOf("\n", slot3.at - 1) + 1;
54758
54821
  edits.push(/^[ \t]*$/.test(text3.slice(lineStart, slot3.at)) ? { at: lineStart, end: lineStart, text: `${indent}${member}
@@ -54764,11 +54827,34 @@ ${indentAt(text3, slot3.at)}` });
54764
54827
  let next = text3;
54765
54828
  for (const edit of edits.sort((a, b) => b.at - a.at)) next = `${next.slice(0, edit.at)}${edit.text}${next.slice(edit.end)}`;
54766
54829
  const after = parseJsonc(next).value;
54767
- const want = { ...root.value, mcpServers: { ...root.value.mcpServers ?? {}, [MCP_SERVER_NAME]: entry } };
54830
+ const want = { ...root.value, [key]: { ...root.value[key] ?? {}, [MCP_SERVER_NAME]: entry } };
54768
54831
  if (!sameJson(after, want)) unreadable();
54769
54832
  return next;
54770
54833
  }
54771
- function changeJsonFile(file, change, { now, platform, restrict, codec = JSON_CODEC }) {
54834
+ function changeJsonFile(file, change, options) {
54835
+ const target = realFileOf(file);
54836
+ for (let attempt = 0; attempt < 2; attempt += 1) {
54837
+ const result = changeFileOnce(target, change, options, attempt);
54838
+ if (result !== RACED) return result;
54839
+ }
54840
+ throw new Error("setup_host_config_changed: the app changed its settings file while setup wrote it, so setup left the file as the app wrote it; run setup again");
54841
+ }
54842
+ function realFileOf(file) {
54843
+ let link2;
54844
+ try {
54845
+ link2 = lstatSync(file).isSymbolicLink();
54846
+ } catch (error) {
54847
+ if (error?.code === "ENOENT") return file;
54848
+ throw error;
54849
+ }
54850
+ if (!link2) return file;
54851
+ try {
54852
+ return realpathSync(file);
54853
+ } catch {
54854
+ throw new Error("setup_host_config_link_broken: the host's config file is a link to a file that does not exist, so setup did not change it; fix the link or add the printed entry by hand");
54855
+ }
54856
+ }
54857
+ function changeFileOnce(file, change, { now, platform, restrict, codec = JSON_CODEC }, attempt) {
54772
54858
  let current = codec.empty();
54773
54859
  let bytes2;
54774
54860
  if (existsSync(file)) {
@@ -54781,7 +54867,7 @@ function changeJsonFile(file, change, { now, platform, restrict, codec = JSON_CO
54781
54867
  let backup;
54782
54868
  if (bytes2 !== void 0) {
54783
54869
  mode = statSync(file).mode & 511;
54784
- backup = `${file}.bak-${now.toISOString().replace(/[:.]/g, "-")}`;
54870
+ backup = `${file}.bak-${now.toISOString().replace(/[:.]/g, "-")}${attempt ? `-${attempt + 1}` : ""}`;
54785
54871
  const fd = openSync(backup, "wx", 384);
54786
54872
  let restrictFailed = false;
54787
54873
  try {
@@ -54813,6 +54899,11 @@ function changeJsonFile(file, change, { now, platform, restrict, codec = JSON_CO
54813
54899
  writeFileSync(temp, codec.write(next), { mode: mode ?? 384, flag: "wx" });
54814
54900
  }
54815
54901
  if (mode !== void 0 && platform !== "win32") chmodSync(temp, mode);
54902
+ const nowBytes = existsSync(file) ? readFileSync(file) : void 0;
54903
+ if (bytes2 === void 0 ? nowBytes !== void 0 : nowBytes === void 0 || !nowBytes.equals(bytes2)) {
54904
+ rmSync(temp, { force: true });
54905
+ return RACED;
54906
+ }
54816
54907
  renameSync(temp, file);
54817
54908
  } catch (error) {
54818
54909
  rmSync(temp, { force: true });
@@ -54820,7 +54911,7 @@ function changeJsonFile(file, change, { now, platform, restrict, codec = JSON_CO
54820
54911
  }
54821
54912
  return { changed: true, ...backup ? { backup } : {} };
54822
54913
  }
54823
- 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, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RELEASES_URL, npmStartEnvironment, NPM_START_KEYS, defaultWalletHome, WINDOWS_SHELL_CODE, tomlString, tomlKey, codexEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC;
54914
+ 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, hostEntry, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RELEASES_URL, npmStartEnvironment, NPM_START_KEYS, defaultWalletHome, WINDOWS_SHELL_CODE, CONTINUE_HEADER, tomlString, tomlKey, codexEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC, RACED;
54824
54915
  var init_config = __esm({
54825
54916
  "wallet/src/setup/config.mjs"() {
54826
54917
  "use strict";
@@ -54880,8 +54971,21 @@ var init_config = __esm({
54880
54971
  isOwnAddressText = (value2) => isPubkey(value2);
54881
54972
  salvagePath = (home) => join8(home, "config.salvage.json");
54882
54973
  clearConfigSalvage = (home) => rmSync(salvagePath(home), { force: true });
54883
- HOST_TARGETS = Object.freeze(["claude-code", "claude-desktop", "cursor", "codex"]);
54884
- HOST_NAMES = Object.freeze({ "claude-code": "Claude Code", "claude-desktop": "Claude Desktop", cursor: "Cursor", codex: "Codex" });
54974
+ HOST_TARGETS = Object.freeze(["claude-code", "claude-desktop", "cursor", "codex", "vscode", "devin", "cline", "continue", "gemini", "antigravity"]);
54975
+ HOST_NAMES = Object.freeze({
54976
+ "claude-code": "Claude Code",
54977
+ "claude-desktop": "Claude Desktop",
54978
+ cursor: "Cursor",
54979
+ codex: "Codex",
54980
+ vscode: "VS Code",
54981
+ devin: "Devin Desktop",
54982
+ cline: "Cline",
54983
+ continue: "Continue",
54984
+ gemini: "Gemini CLI",
54985
+ antigravity: "Antigravity"
54986
+ });
54987
+ hostServersKey = (target) => target === "vscode" ? "servers" : "mcpServers";
54988
+ hostEntry = (target, entry) => ["vscode", "cline", "continue"].includes(target) ? { type: "stdio", ...entry } : entry;
54885
54989
  CODEX_STARTUP_TIMEOUT_SEC = 120;
54886
54990
  CODEX_TOOL_TIMEOUT_SEC = 300;
54887
54991
  PINNED_PACKAGE = /^shinjuku(?:-shield(?:ed)?)?@(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
@@ -54893,6 +54997,7 @@ var init_config = __esm({
54893
54997
  NPM_START_KEYS = ["npm_config_offline", "npm_config_prefer_offline"];
54894
54998
  defaultWalletHome = (home = homedir()) => setupHome({}, home);
54895
54999
  WINDOWS_SHELL_CODE = /[%$`"\r\n]/;
55000
+ CONTINUE_HEADER = Object.freeze(["name: Shinjuku Shield", "version: 0.0.1", "schema: v1", "mcpServers:"]);
54896
55001
  tomlString = (value2) => `"${String(value2).replace(/[\\"]/g, (c) => `\\${c}`).replace(/[\u0000-\u001f\u007f]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`)}"`;
54897
55002
  tomlKey = (key) => /^[A-Za-z0-9_-]+$/.test(key) ? key : tomlString(key);
54898
55003
  codexEntryOf = (server) => ({
@@ -54947,6 +55052,7 @@ var init_config = __esm({
54947
55052
  });
54948
55053
  indentAt = (src, at) => /^[ \t]*/.exec(src.slice(src.lastIndexOf("\n", at - 1) + 1))[0];
54949
55054
  TEXT_CODEC = Object.freeze({ empty: () => "", read: (text3) => text3, write: (next) => next });
55055
+ RACED = /* @__PURE__ */ Symbol("raced");
54950
55056
  }
54951
55057
  });
54952
55058
 
@@ -109970,13 +110076,14 @@ function refusalSay(a, tool, again, npmVersion) {
109970
110076
  ].find((line) => words(line) <= SAY_MAX_WORDS) ?? `Not known yet if ${seller} kept ${what}. Nothing to do now.`;
109971
110077
  }
109972
110078
  if (code2 === "p03_resource_paid_seller_failed") {
109973
- const failed = Number.isInteger(a.httpStatus) ? `${seller}'s service failed (HTTP ${a.httpStatus})` : `${seller}'s service failed`;
109974
- const paid = total ? `Paid ${total} USDC` : "Paid";
109975
- return [
109976
- `${paid}, but ${failed} after it took the payment. Do not pay again now; try later, or ask ${seller}'s support.`,
109977
- `${paid}, but ${failed}. Do not pay again now; try later, or ask ${seller}.`,
109978
- `${paid}, but ${failed}. Do not pay again now.`
109979
- ].find((line) => words(line) <= SAY_MAX_WORDS) ?? `${paid}, but ${failed}. Do not pay again now.`;
110079
+ const tx = typeof a.transaction === "string" && /^[1-9A-HJ-NP-Za-km-z]{43,90}$/.test(a.transaction) ? a.transaction : void 0;
110080
+ const head = (status) => `${total ? `Paid ${total} USDC` : "Paid"}; ${seller} got paid, but its service failed${status}.`;
110081
+ const statuses = [...Number.isInteger(a.httpStatus) ? [` (HTTP ${a.httpStatus})`] : [], ""];
110082
+ return firstFit(
110083
+ ...tx ? statuses.map((s3) => `${head(s3)} Shinjuku refunds nothing. Do not pay again; ask its support, giving transaction ${tx}.`) : [],
110084
+ ...statuses.map((s3) => `${head(s3)} No refund comes from Shinjuku. Do not pay again; ask its support.`),
110085
+ `${head("")} Shinjuku refunds nothing. Do not pay again.`
110086
+ );
109980
110087
  }
109981
110088
  if (code2 === "p03_float_transfer_expired") {
109982
110089
  const status = Number.isInteger(a.httpStatus);
@@ -156212,8 +156319,13 @@ function readJsonFile(file) {
156212
156319
  }
156213
156320
  if (size > HOST_CONFIG_MAX_BYTES) return null;
156214
156321
  try {
156215
- const text3 = readFileSync(file, "utf8");
156216
- const value2 = text3.trim() ? JSON.parse(text3) : {};
156322
+ const text3 = readFileSync(file, "utf8").replace(/^/, "");
156323
+ let value2;
156324
+ try {
156325
+ value2 = text3.trim() ? JSON.parse(text3) : {};
156326
+ } catch {
156327
+ value2 = parseJsonc(text3).value;
156328
+ }
156217
156329
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
156218
156330
  } catch {
156219
156331
  return null;
@@ -156246,7 +156358,23 @@ function claudeCodeState(env, home) {
156246
156358
  function agentHostsOf(env, home) {
156247
156359
  const jsonHost = (target) => {
156248
156360
  const config = readJsonFile(hostConfigFile(target, { env, home: homedir3() }));
156249
- return config === void 0 ? false : config === null ? null : runsThisWallet(config.mcpServers, home);
156361
+ return config === void 0 ? false : config === null ? null : runsThisWallet(config[hostServersKey(target)], home);
156362
+ };
156363
+ const continueHost = () => {
156364
+ const file = hostConfigFile("continue", { env, home: homedir3() });
156365
+ let size;
156366
+ try {
156367
+ size = statSync(file).size;
156368
+ } catch (error) {
156369
+ return error?.code === "ENOENT" ? false : null;
156370
+ }
156371
+ if (size > HOST_CONFIG_MAX_BYTES) return null;
156372
+ try {
156373
+ const servers = continueServersIn(readFileSync(file, "utf8"));
156374
+ return servers ? runsThisWallet(servers, home) : null;
156375
+ } catch {
156376
+ return null;
156377
+ }
156250
156378
  };
156251
156379
  const codexHost = () => {
156252
156380
  const file = hostConfigFile("codex", { env, home: homedir3() });
@@ -156268,7 +156396,9 @@ function agentHostsOf(env, home) {
156268
156396
  { app: "Claude Code", ...claudeCodeState(env, home) },
156269
156397
  { app: "Claude Desktop", linked: jsonHost("claude-desktop") },
156270
156398
  { app: "Cursor", linked: jsonHost("cursor") },
156271
- { app: "Codex", linked: codexHost() }
156399
+ { app: "Codex", linked: codexHost() },
156400
+ ...["vscode", "devin", "cline", "gemini", "antigravity"].map((target) => ({ app: HOST_NAMES[target], linked: jsonHost(target) })),
156401
+ { app: HOST_NAMES.continue, linked: continueHost() }
156272
156402
  ];
156273
156403
  }
156274
156404
  function readHomeData({ env = process.env, walletVersion: walletVersion2, torRun } = {}) {
@@ -158891,12 +159021,14 @@ function privateInferencePath(config) {
158891
159021
  }
158892
159022
  function agentAppLines({ skip = /* @__PURE__ */ new Set() }) {
158893
159023
  if (inHomeJob()) return "To add the wallet to an agent app (Claude, Codex, Cursor and others), press [c] on the home screen.";
158894
- const lines = [];
158895
- for (const target of HOST_TARGETS) {
158896
- const app = target === "codex" ? "Codex (the Codex app or Codex CLI)" : HOST_NAMES[target];
158897
- if (!skip.has(target)) lines.push(`${app}, run this once (setup adds the wallet there):`, ` ${cli4()} setup --add-to ${target}`);
158898
- }
158899
- return lines.join("\n");
159024
+ const targets = HOST_TARGETS.filter((target) => !skip.has(target));
159025
+ return targets.length ? hostLines(targets).join("\n") : "";
159026
+ }
159027
+ function hostLines(targets) {
159028
+ const commands = targets.map((target) => `${cli4()} setup --add-to ${target}`);
159029
+ const width = Math.max(...commands.map((command) => command.length));
159030
+ const label = { codex: "Codex (the Codex app or Codex CLI)", devin: "Devin Desktop (the app that was Windsurf)", vscode: "VS Code (GitHub Copilot)" };
159031
+ return targets.map((target, index) => ` ${commands[index].padEnd(width)} ${label[target] ?? HOST_NAMES[target]}`);
158900
159032
  }
158901
159033
  function setupNextText({ home, deps, npmVersion, config, fundAsked, rerun, targets = [], added = /* @__PURE__ */ new Set() }) {
158902
159034
  const parts = [];
@@ -158909,20 +159041,18 @@ function setupNextText({ home, deps, npmVersion, config, fundAsked, rerun, targe
158909
159041
  env: deps.env,
158910
159042
  platform: deps.platform ?? process.platform,
158911
159043
  npmVersion,
159044
+ userHome: deps.homedir(),
158912
159045
  skip: new Set(HOST_TARGETS.filter((target) => !failed.includes(target)))
158913
159046
  });
158914
159047
  if (byHand) parts.push(`Or add it by hand:
158915
159048
  ${byHand}
158916
159049
  `);
158917
159050
  } else if (rerun && !targets.length) {
158918
- parts.push([
158919
- "To add the wallet to an agent app, run one of these:",
158920
- ...HOST_TARGETS.map((target) => ` ${cli4()} setup --add-to ${target}`),
158921
- ""
158922
- ].join("\n"));
159051
+ parts.push(["To add the wallet to an agent app, run the line for that app:", ...hostLines(HOST_TARGETS), ""].join("\n"));
158923
159052
  } else {
158924
159053
  const hosts = agentAppLines({ skip: added });
158925
- if (hosts) parts.push(`${added.size ? "To add the wallet to another agent app" : "Use the wallet in your agent app"}:
159054
+ const head = inHomeJob() ? added.size ? "To add the wallet to another agent app" : "Use the wallet in your agent app" : `${added.size ? "To add the wallet to another agent app" : "To use the wallet in your agent app"}, run the line for that app once (setup adds the wallet there)`;
159055
+ if (hosts) parts.push(`${head}:
158926
159056
  ${hosts}
158927
159057
  `);
158928
159058
  }
@@ -159465,7 +159595,7 @@ async function addToHosts(targets, home, deps, { force = false, npmVersion, ask,
159465
159595
  continue;
159466
159596
  }
159467
159597
  try {
159468
- const write = (entry, writeForce) => (target === "codex" ? addToCodexHost : addToJsonHost)(file, entry, { force: writeForce });
159598
+ const write = (entry, writeForce) => target === "codex" ? addToCodexHost(file, entry, { force: writeForce }) : target === "continue" ? addToContinueHost(file, hostEntry(target, entry), { force: writeForce }) : addToJsonHost(file, hostEntry(target, entry), { force: writeForce, key: hostServersKey(target) });
159469
159599
  let added = write(mcpServerEntry(options), force);
159470
159600
  for (const old of added.replaced ?? []) io.out(`Replaced the old ${old} entry in ${app}.
159471
159601
  `);
@@ -159479,7 +159609,7 @@ async function addToHosts(targets, home, deps, { force = false, npmVersion, ask,
159479
159609
  done.add(target);
159480
159610
  } catch (error) {
159481
159611
  const code2 = String(error.message).match(/^(setup_host_config_[a-z_]+): /)?.[1];
159482
- const why = code2 === "setup_host_config_conflict" ? `${file} already has another server named "${MCP_SERVER_NAME}", and the wallet did not change that file; to use the wallet there, put the entry below in its place` : code2 === "setup_host_config_unreadable" ? `the wallet cannot read the settings in ${file}, so it did not change that file; fix that file, then add the entry below to it by hand` : code2 === "setup_host_config_acl_failed" ? `the wallet could not limit a new copy of ${file} to you alone, so it did not change that file; add the entry below to it by hand` : `the wallet could not write ${file}, so that file is unchanged; add the entry below to it by hand`;
159612
+ const why = code2 === "setup_host_config_conflict" ? `${file} already has another server named "${MCP_SERVER_NAME}", and the wallet did not change that file; to use the wallet there, put the entry below in its place` : code2 === "setup_host_config_unreadable" ? `the wallet cannot read the settings in ${file}, so it did not change that file; fix that file, then add the entry below to it by hand` : code2 === "setup_host_config_acl_failed" ? `the wallet could not limit a new copy of ${file} to you alone, so it did not change that file; add the entry below to it by hand` : code2 === "setup_host_config_changed" ? `${app} changed its settings while setup ran, so the wallet left ${file} as ${app} wrote it; run this step again` : code2 === "setup_host_config_link_broken" ? `${file} is a link to a file that does not exist, so the wallet did not change it; fix that link, or add the entry below by hand` : `the wallet could not write ${file}, so that file is unchanged; add the entry below to it by hand`;
159483
159613
  io.out(`Not added to ${app}: ${why}.
159484
159614
  `);
159485
159615
  }
@@ -160812,7 +160942,7 @@ var COMMANDS = [
160812
160942
  flag2("--own-address", "<address>", `optional, in your own terminal only (the same as the answer to setup's address question): your own wallet address (Phantom, Solflare, an exchange's deposit address), for sending money back to yourself: the MCP sends to it without asking each time (as mcp --unshield-to), also in an agent app that cannot show a question. Not this wallet's own key (the address this wallet itself receives on). Refused in a script, a pipe, or an agent's tool (no terminal), also with --yes: an agent must never approve an address. A rerun adds it to the ones config.json records (at most ${MAX_OWN_ADDRESSES})`),
160813
160943
  flag2("--passphrase-file", "<file>", "your own passphrase file (8 or more characters); default: setup writes a new random passphrase to <home>/passphrase.txt, readable by you alone. Needed for a wallet that exists already"),
160814
160944
  flag2("--facilitator", "<https origin>", `the server whose release, pool, and setup files setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
160815
- flag2("--add-to", "<claude-code|claude-desktop|cursor|codex>", `also add the server (npx -y --no-audit --no-fund ${NPX_AT_VERSION} mcp: the host starts this npm version; a wallet not installed from npm writes shinjuku-shield without a version) to this agent host's config; repeat it for more. Without it, setup prints the configs`, { repeat: true }),
160945
+ flag2("--add-to", `<${HOST_TARGETS.join("|")}>`, `also add the server (npx -y --no-audit --no-fund ${NPX_AT_VERSION} mcp: the host starts this npm version; a wallet not installed from npm writes shinjuku-shield without a version) to this agent host's config; repeat it for more. Without it, setup prints the configs`, { repeat: true, check: "host" }),
160816
160946
  // 0.3.97 key rename: the host key is MCP_SERVER_NAME; the old keys go by themselves.
160817
160947
  bool2("--force", `with --add-to: replace a different ${MCP_SERVER_NAME} entry already in a JSON host config (the old file is backed up first, mode 0600), or in Claude Code (claude mcp remove of that one server, then add). An old ${OLD_MCP_SERVER_NAMES.join(" or ")} entry is replaced by itself, without --force`),
160818
160948
  flag2("--trust-proof-tools", "<sha256>", `ADVANCED: install the set of setup files whose SHA256SUMS has this SHA-256 instead of the set this wallet build pins (needed for a server the build does not pin). Check the set out of band first; the normal fix for a new set is to update the wallet: ${UPDATE_SETUP}`)
@@ -160830,7 +160960,8 @@ var COMMANDS = [
160830
160960
  "The passphrase is never printed and no MCP tool takes or returns it. The recovery file holds the seed and the key: copy it to two offline places, then delete it from this machine.",
160831
160961
  `The setup files are the set this wallet build PINS (its SHA256SUMS hash is in the wallet; --trust-proof-tools replaces it). A server whose /api/wallet/release names another set is refused (setup_proof_tools_pin_mismatch; to update the wallet, ${UPDATE_SETUP}). SHA256SUMS must have the pinned SHA-256, and every file must have the SHA-256 its line names, as /skill.md section 7b; bin/ and house/bin/ get the execute bit. A file that does not match is not kept. Each download has a hard size limit.`,
160832
160962
  "Your own --passphrase-file must be private: on Linux and macOS setup refuses a file that group or other may read or write, or that another user owns (setup_passphrase_file_insecure: chmod 600 <file>). On Windows setup cannot read the access list; it accepts the file with a warning.",
160833
- `--add-to for Claude Desktop or Cursor: a host config whose mcpServers is not a JSON object, or that has a different ${MCP_SERVER_NAME} entry (without --force), is left alone. Otherwise the old file is copied to <file>.bak-<time> (mode 0600 on Linux and macOS; on Windows the folder's access list) and the new file replaces it in one rename. An entry an older setup wrote (no version, another version, or an old package name, shinjuku or shinjuku-shielded; the same home) is replaced without --force, in a JSON host and in Claude Code. An entry under an old key (${OLD_MCP_SERVER_NAMES.join(" or ")}) is replaced by itself, in every host, so the host never starts two wallet servers. For Claude Code, a different ${MCP_SERVER_NAME} server (not an older entry of this wallet) needs --force.`,
160963
+ `--add-to writes each app's own user file: vscode (VS Code with Copilot) mcp.json in VS Code's User folder, under "servers"; devin (Devin Desktop, the app that was Windsurf) mcp_config.json in AppData\\Roaming\\devin on Windows, else ~/.config/devin; cline ~/.cline/data/settings/cline_mcp_settings.json (or CLINE_MCP_SETTINGS_PATH, CLINE_DATA_DIR, CLINE_DIR); continue its own block file ~/.continue/mcpServers/shinjuku.yaml (or in CONTINUE_GLOBAL_DIR); gemini (Gemini CLI) ~/.gemini/settings.json (or in GEMINI_CLI_HOME); antigravity (Antigravity) ~/.gemini/config/mcp_config.json. Every other key and server in a file stays as it is.`,
160964
+ `--add-to for a JSON file (Claude Desktop, Cursor and the apps above): a host config whose server list is not a JSON object, or that has a different ${MCP_SERVER_NAME} entry (without --force), is left alone. Otherwise the old file is copied to <file>.bak-<time> (mode 0600 on Linux and macOS; on Windows the folder's access list) and the new file replaces it in one rename. An entry an older setup wrote (no version, another version, or an old package name, shinjuku or shinjuku-shielded; the same home) is replaced without --force, in a JSON host and in Claude Code. An entry under an old key (${OLD_MCP_SERVER_NAMES.join(" or ")}) is replaced by itself, in every host, so the host never starts two wallet servers. For Claude Code, a different ${MCP_SERVER_NAME} server (not an older entry of this wallet) needs --force.`,
160834
160965
  `--add-to codex: the Codex app and Codex CLI read one file, config.toml in CODEX_HOME (else ~/.codex). Setup writes the [mcp_servers.${MCP_SERVER_NAME}] tables at its end with startup_timeout_sec = 120 (a first start downloads the wallet) and tool_timeout_sec = 300, and keeps every other line; the same rules as a JSON host (a different entry stays without --force; the old file is backed up first). Then type /mcp in Codex to see the tools.`,
160835
160966
  "The proof programs run natively: Windows x64, Linux x64 and Linux arm64 each download their own set (no WSL). Setup downloads the programs first and starts each one once before it makes a key; when Windows app control or an antivirus blocks one, setup stops and says which. macOS and other systems: not supported yet; setup says so and creates nothing.",
160836
160967
  "Run it again at any time: it never changes an existing wallet, keeps the recorded caps unless you pass new ones, checks every setup file again, downloads what is missing or newer, and rewrites config.json.",
@@ -161565,8 +161696,8 @@ function shortHelpText() {
161565
161696
  "Everyday commands:",
161566
161697
  ...rows2.map(([word, about]) => ` ${word.padEnd(width)}${about}`),
161567
161698
  "",
161568
- "Add the wallet to your agent app (claude-code, claude-desktop, cursor, or codex):",
161569
- ` ${cli4()} setup --add-to claude-code`,
161699
+ "Add the wallet to your agent app: run the line for that app once.",
161700
+ ...hostLines(HOST_TARGETS),
161570
161701
  "",
161571
161702
  `One command, with an example: ${cli4()} help <command>`,
161572
161703
  `All commands: ${cli4()} help all`,
@@ -161731,6 +161862,9 @@ var flagLabel = (index) => `the flag at position ${index + 1}`;
161731
161862
  var TRANSFER_START_FLAGS = /* @__PURE__ */ new Set(["--mint", "--token-mint", "--amount-atomic", "--amount", "--to", "--max-slippage-bps", "--allow-impact", "--yes"]);
161732
161863
  var NOT_SHOWN = "; the value given is not one (it is not repeated here)";
161733
161864
  function checkValue(spec, value2) {
161865
+ if (spec.check === "host" && (Array.isArray(value2) ? value2 : [value2]).some((name) => !HOST_TARGETS.includes(name))) {
161866
+ return `${spec.name} takes the name of an agent app: ${HOST_TARGETS.join(", ")}${NOT_SHOWN}`;
161867
+ }
161734
161868
  if (spec.check === "atomic" && !(spec.usdc === "or-all" && value2 === "all") && !/^[1-9][0-9]*$/.test(value2)) {
161735
161869
  return `${spec.name} must be a whole number of atomic units above 0 (1 USDC = 1000000)${NOT_SHOWN}`;
161736
161870
  }
@@ -164883,6 +165017,7 @@ ${HOST_TARGETS.map((host, index) => ` ${index + 1} ${hostNames[host] ?? host}`
164883
165017
  env: deps.env,
164884
165018
  platform: deps.platform ?? process.platform,
164885
165019
  npmVersion,
165020
+ userHome: deps.homedir(),
164886
165021
  skip: new Set(HOST_TARGETS.filter((target) => target !== host))
164887
165022
  });
164888
165023
  if (byHand) io.out(`${byHand}