shinjuku-shielded 0.3.159 → 0.3.161
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 +6 -6
- package/manifest.json +4 -4
- package/package.json +3 -3
- package/shinjuku-wallet.mjs +420 -133
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.
|
|
9
|
+
This is version 0.3.161. 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.
|
|
15
|
+
npm install -g shinjuku@0.3.161
|
|
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.
|
|
21
|
+
installing, type `npx shinjuku@0.3.161 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.
|
|
39
|
+
claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.161 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.
|
|
45
|
+
{ "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.161", "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.
|
|
58
|
+
args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.161", "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": "
|
|
5
|
-
"bytes":
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"sha256": "9bbc535c39d7f3ca35992be853e343d8374075618e0130ef2ab4f526a300b091",
|
|
5
|
+
"bytes": 9260169,
|
|
6
|
+
"sourceCommit": "157e988ee5f4ca6c609f49b296feabd64217eba7",
|
|
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-
|
|
1041
|
+
"publishedAt": "2026-11-04T06:13:30.270Z"
|
|
1042
1042
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shinjuku-shielded",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.161",
|
|
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": "
|
|
7
|
-
"shinjukuSha256": "
|
|
6
|
+
"shinjukuRelease": "9bbc535c",
|
|
7
|
+
"shinjukuSha256": "9bbc535c39d7f3ca35992be853e343d8374075618e0130ef2ab4f526a300b091",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"author": "Shinjuku Shielded",
|
|
10
10
|
"homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
|
package/shinjuku-wallet.mjs
CHANGED
|
@@ -61,7 +61,7 @@ var __toCommonJS = (mod7) => __copyProps(__defProp({}, "__esModule", { value: tr
|
|
|
61
61
|
import { readFileSync as realReadFileSync } from "node:fs";
|
|
62
62
|
import { relative, resolve, sep } from "node:path";
|
|
63
63
|
import { fileURLToPath } from "node:url";
|
|
64
|
-
import { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fchmodSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFile, readSync, readdirSync,
|
|
64
|
+
import { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fchmodSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFile, readSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, statfsSync, truncateSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
65
65
|
function readFileSync(path, options) {
|
|
66
66
|
const target = typeof path === "string" ? path : path instanceof URL ? fileURLToPath(path) : void 0;
|
|
67
67
|
if (target !== void 0) {
|
|
@@ -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;
|
|
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 (
|
|
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" ? "
|
|
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
|
|
48044
|
-
TOR_STOP_OLD_WINDOWS = "Stop your old Tor first:
|
|
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
|
|
54322
|
+
].join("\n");
|
|
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
|
+
""
|
|
54298
54330
|
].join("\n");
|
|
54299
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
|
-
|
|
54597
|
-
|
|
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) =>
|
|
54600
|
-
const existing =
|
|
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(
|
|
54608
|
-
return { ...current,
|
|
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 ===
|
|
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}`)}` :
|
|
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,
|
|
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,
|
|
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({
|
|
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
|
|
|
@@ -125660,6 +125766,8 @@ function clearBackupWithCode(home, code2) {
|
|
|
125660
125766
|
return true;
|
|
125661
125767
|
}
|
|
125662
125768
|
function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
125769
|
+
const roots = cloudRoots({ platform, env, home: homedir2() });
|
|
125770
|
+
const notSynced = (list) => list.filter((place) => !cloudPlace(place.path, { platform, env, roots: () => roots }));
|
|
125663
125771
|
const places = [];
|
|
125664
125772
|
if (platform === "win32") {
|
|
125665
125773
|
try {
|
|
@@ -125681,12 +125789,12 @@ function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
|
125681
125789
|
} catch {
|
|
125682
125790
|
}
|
|
125683
125791
|
const profile = env.USERPROFILE ?? homedir2();
|
|
125684
|
-
for (const [label, path] of [["Desktop", join29(profile, "Desktop")], ["
|
|
125792
|
+
for (const [label, path] of [["Desktop", join29(profile, "Desktop")], ["Documents", join29(profile, "Documents")]]) {
|
|
125685
125793
|
if (isFolder(path) && !places.some((place) => place.path.toLowerCase() === path.toLowerCase())) {
|
|
125686
125794
|
places.push({ label: `${label} (${path}; on this computer: it does not help if this computer breaks)`, path, removable: false });
|
|
125687
125795
|
}
|
|
125688
125796
|
}
|
|
125689
|
-
return places;
|
|
125797
|
+
return notSynced(places);
|
|
125690
125798
|
}
|
|
125691
125799
|
let user = "";
|
|
125692
125800
|
try {
|
|
@@ -125706,7 +125814,66 @@ function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
|
125706
125814
|
for (const path of [join29(homedir2(), "Desktop"), homedir2()]) {
|
|
125707
125815
|
if (isFolder(path)) places.push({ label: `${path} (on this computer: it does not help if this computer breaks)`, path, removable: false });
|
|
125708
125816
|
}
|
|
125709
|
-
return places;
|
|
125817
|
+
return notSynced(places);
|
|
125818
|
+
}
|
|
125819
|
+
function cloudPlace(place, { platform = process.platform, env = process.env, home = homedir2(), roots } = {}) {
|
|
125820
|
+
const text3 = String(place ?? "").trim();
|
|
125821
|
+
if (!text3) return void 0;
|
|
125822
|
+
const fold = platform === "win32" || platform === "darwin";
|
|
125823
|
+
const sep5 = platform === "win32" ? "\\" : "/";
|
|
125824
|
+
const norm = (path) => {
|
|
125825
|
+
const full = (platform === "win32" ? resolve25(localForm(path, platform)) : posix.resolve(path)).replace(/[\\/]+$/, "");
|
|
125826
|
+
return fold ? full.toLowerCase() : full;
|
|
125827
|
+
};
|
|
125828
|
+
const at = norm(text3);
|
|
125829
|
+
for (const root of (roots ?? (() => cloudRoots({ platform, env, home })))()) {
|
|
125830
|
+
const base = norm(root.path);
|
|
125831
|
+
if (base && (at === base || at.startsWith(`${base}${sep5}`))) return root.service;
|
|
125832
|
+
}
|
|
125833
|
+
for (const part of text3.split(/[\\/]+/).map((name) => name.trim().toLowerCase())) {
|
|
125834
|
+
if (part === "onedrive" || part.startsWith("onedrive - ") || part.startsWith("onedrive-")) return "OneDrive";
|
|
125835
|
+
if (part === "dropbox" || part.startsWith("dropbox (") || part.startsWith("dropbox-")) return "Dropbox";
|
|
125836
|
+
if (["my drive", "shared drives", "google drive"].includes(part) || part.startsWith("googledrive")) return "Google Drive";
|
|
125837
|
+
if (["iclouddrive", "icloud drive", "icloud photos", "mobile documents"].includes(part)) return "iCloud";
|
|
125838
|
+
}
|
|
125839
|
+
return void 0;
|
|
125840
|
+
}
|
|
125841
|
+
function cloudRoots({ platform = process.platform, env = process.env, home = homedir2() } = {}) {
|
|
125842
|
+
const roots = [];
|
|
125843
|
+
const add2 = (path, service) => {
|
|
125844
|
+
if (typeof path === "string" && path.trim()) roots.push({ path: path.trim(), service });
|
|
125845
|
+
};
|
|
125846
|
+
const dropbox = (file) => {
|
|
125847
|
+
let info;
|
|
125848
|
+
try {
|
|
125849
|
+
info = JSON.parse(readFileSync(file, "utf8"));
|
|
125850
|
+
} catch {
|
|
125851
|
+
return;
|
|
125852
|
+
}
|
|
125853
|
+
for (const account of Object.values(info ?? {})) add2(account?.path, "Dropbox");
|
|
125854
|
+
};
|
|
125855
|
+
if (platform === "win32") {
|
|
125856
|
+
for (const name of ["OneDrive", "OneDriveConsumer", "OneDriveCommercial"]) add2(env[name], "OneDrive");
|
|
125857
|
+
add2(join29(env.USERPROFILE ?? home, "OneDrive"), "OneDrive");
|
|
125858
|
+
try {
|
|
125859
|
+
const result = spawnSync3(
|
|
125860
|
+
"reg.exe",
|
|
125861
|
+
["query", "HKCU\\Software\\Microsoft\\OneDrive\\Accounts", "/s", "/v", "UserFolder"],
|
|
125862
|
+
{ windowsHide: true, encoding: "utf8", timeout: 1e4 }
|
|
125863
|
+
);
|
|
125864
|
+
for (const match of String(result.stdout ?? "").matchAll(/UserFolder\s+REG_\w+\s+(.+)/g)) add2(match[1], "OneDrive");
|
|
125865
|
+
} catch {
|
|
125866
|
+
}
|
|
125867
|
+
if (env.APPDATA) dropbox(join29(env.APPDATA, "Dropbox", "info.json"));
|
|
125868
|
+
if (env.LOCALAPPDATA) dropbox(join29(env.LOCALAPPDATA, "Dropbox", "info.json"));
|
|
125869
|
+
return roots;
|
|
125870
|
+
}
|
|
125871
|
+
dropbox(posix.join(home, ".dropbox", "info.json"));
|
|
125872
|
+
if (platform === "darwin") {
|
|
125873
|
+
add2(posix.join(home, "Library", "Mobile Documents"), "iCloud");
|
|
125874
|
+
add2(posix.join(home, "Library", "CloudStorage"), "a cloud service");
|
|
125875
|
+
}
|
|
125876
|
+
return roots;
|
|
125710
125877
|
}
|
|
125711
125878
|
async function saveBackup(home, folder, {
|
|
125712
125879
|
removable,
|
|
@@ -125714,7 +125881,8 @@ async function saveBackup(home, folder, {
|
|
|
125714
125881
|
platform = process.platform,
|
|
125715
125882
|
restrict = restrictFolderToOwner,
|
|
125716
125883
|
driveType = windowsDriveType,
|
|
125717
|
-
mounts = posixMounts
|
|
125884
|
+
mounts = posixMounts,
|
|
125885
|
+
env = process.env
|
|
125718
125886
|
} = {}) {
|
|
125719
125887
|
const typed = String(folder ?? "").trim().replace(/^"(.*)"$/, "$1");
|
|
125720
125888
|
const types = /* @__PURE__ */ new Map();
|
|
@@ -125726,85 +125894,173 @@ async function saveBackup(home, folder, {
|
|
|
125726
125894
|
const noFolder = (at) => backupFailed(`there is no folder ${at}. Plug in the drive or type another folder`);
|
|
125727
125895
|
const refuse13 = (found, at) => {
|
|
125728
125896
|
if (found === "missing") throw noFolder(at);
|
|
125897
|
+
if (found === "link") throw backupFailed(LINK_WORDS(at));
|
|
125729
125898
|
if (found) throw backupFailed(found === "unknown" ? UNKNOWN_DRIVE_WORDS(at) : NETWORK_PLACE_WORDS(at));
|
|
125730
125899
|
};
|
|
125900
|
+
const roots = cloudRoots({ platform, env, home: homedir2() });
|
|
125901
|
+
const refuseCloud = (at) => {
|
|
125902
|
+
const service = cloudPlace(at, { platform, env, roots: () => roots });
|
|
125903
|
+
if (service) throw backupFailed(CLOUD_WORDS(at, service));
|
|
125904
|
+
};
|
|
125731
125905
|
refuse13(networkPlace(typed, net4), typed);
|
|
125732
125906
|
const place = localForm(resolve25(typed), platform);
|
|
125733
125907
|
refuse13(networkPlace(place, net4), place);
|
|
125734
|
-
|
|
125735
|
-
|
|
125736
|
-
const
|
|
125737
|
-
|
|
125738
|
-
const
|
|
125739
|
-
let
|
|
125740
|
-
|
|
125741
|
-
|
|
125742
|
-
|
|
125908
|
+
refuseCloud(place);
|
|
125909
|
+
const onWay = linkOnWay(place, platform);
|
|
125910
|
+
const fullPath = isAbsolute7(typed) && (platform !== "win32" || /^(?:[\\/]{2}[?.][\\/])?[A-Za-z]:[\\/]/.test(typed));
|
|
125911
|
+
if (onWay === "missing" && !fullPath) throw noFolder(place);
|
|
125912
|
+
const trail = { files: [], dirs: [] };
|
|
125913
|
+
let made;
|
|
125914
|
+
try {
|
|
125915
|
+
made = onWay === "missing" ? makeMissingFolder(place, net4, refuse13, noFolder, trail) : refuse13(onWay, place);
|
|
125916
|
+
return await saveInFolder();
|
|
125743
125917
|
} catch (error) {
|
|
125744
|
-
|
|
125918
|
+
removeTrail(trail);
|
|
125919
|
+
throw error;
|
|
125745
125920
|
}
|
|
125746
|
-
|
|
125747
|
-
|
|
125921
|
+
async function saveInFolder() {
|
|
125922
|
+
if (!isAbsolute7(place) || !isFolder(place)) throw noFolder(place);
|
|
125923
|
+
const real = realLocal(place, net4, refuse13);
|
|
125924
|
+
refuseCloud(real);
|
|
125925
|
+
if (made && !samePath(real, made.real, platform)) throw backupFailed(MOVED_WORDS(place));
|
|
125926
|
+
const files = backupFiles(home);
|
|
125927
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
125928
|
+
let target = join29(real, `shinjuku-backup-${date}`);
|
|
125929
|
+
for (let n = 2; existsSync(target); n += 1) target = join29(real, `shinjuku-backup-${date}-${n}`);
|
|
125930
|
+
try {
|
|
125931
|
+
mkdirSync(target, { mode: 448 });
|
|
125932
|
+
trail.dirs.push(identityOf(target));
|
|
125933
|
+
} catch (error) {
|
|
125934
|
+
throw backupFailed(`the wallet could not make a folder in ${place} (${error?.code === "EACCES" || error?.code === "EPERM" ? "no permission to write there" : error?.code === "ENOSPC" ? "the drive is full" : "it cannot write there"}). Pick another place`);
|
|
125935
|
+
}
|
|
125936
|
+
const stillHere = (path) => {
|
|
125937
|
+
let now;
|
|
125938
|
+
try {
|
|
125939
|
+
now = localForm(realpathSync.native(path), platform);
|
|
125940
|
+
} catch {
|
|
125941
|
+
now = void 0;
|
|
125942
|
+
}
|
|
125943
|
+
if (!now || !samePath(now, path, platform)) throw backupFailed(MOVED_WORDS(place));
|
|
125944
|
+
refuse13(networkPlace(now, net4), now);
|
|
125945
|
+
refuseCloud(now);
|
|
125946
|
+
};
|
|
125947
|
+
stillHere(target);
|
|
125948
|
+
let access;
|
|
125748
125949
|
try {
|
|
125749
|
-
|
|
125950
|
+
access = restrict(target, platform);
|
|
125750
125951
|
} catch {
|
|
125751
|
-
|
|
125952
|
+
access = "open";
|
|
125953
|
+
}
|
|
125954
|
+
if (access === "foreign") {
|
|
125955
|
+
throw backupFailed(`this folder can be opened by other accounts (${place}); pick another place`);
|
|
125956
|
+
}
|
|
125957
|
+
const ownerOnly = access === "owner";
|
|
125958
|
+
const saved = [];
|
|
125959
|
+
const names = /* @__PURE__ */ new Set();
|
|
125960
|
+
for (const file of files) {
|
|
125961
|
+
let name = basename6(file);
|
|
125962
|
+
for (let n = 2; names.has(name.toLowerCase()); n += 1) name = `${basename6(file, extname(file))}-${n}${extname(file)}`;
|
|
125963
|
+
names.add(name.toLowerCase());
|
|
125964
|
+
const copy = join29(target, name);
|
|
125965
|
+
stillHere(target);
|
|
125966
|
+
try {
|
|
125967
|
+
copyFileSync(file, copy, constants.COPYFILE_EXCL);
|
|
125968
|
+
trail.files.push(identityOf(copy));
|
|
125969
|
+
if (platform !== "win32") chmodSync(copy, 384);
|
|
125970
|
+
} catch (error) {
|
|
125971
|
+
throw backupFailed(`the copy to ${target} failed (${error?.code === "ENOSPC" ? "the drive is full" : "the drive did not take the file"}). Pick another place`);
|
|
125972
|
+
}
|
|
125973
|
+
stillHere(copy);
|
|
125974
|
+
saved.push({ name, source: file, sha256: sha256Of(file) });
|
|
125752
125975
|
}
|
|
125753
|
-
|
|
125754
|
-
|
|
125755
|
-
|
|
125756
|
-
|
|
125757
|
-
|
|
125758
|
-
|
|
125759
|
-
|
|
125760
|
-
} catch {
|
|
125761
|
-
access = "open";
|
|
125976
|
+
for (const entry of saved) {
|
|
125977
|
+
if (sha256Of(join29(target, entry.name)) !== entry.sha256) throw backupFailed(`the copy in ${target} does not match the original. Pick another place`);
|
|
125978
|
+
}
|
|
125979
|
+
const code2 = cleared ? void 0 : backupCode();
|
|
125980
|
+
writeRecord(home, { savedAt: (/* @__PURE__ */ new Date()).toISOString(), folder: target, cleared, ...code2 ? { codeSha256: code2.sha256 } : {} });
|
|
125981
|
+
const warning = removable === true ? "Anyone who gets this USB stick can spend your money: keep it somewhere safe." : !ownerOnly ? "Other people who use this computer or drive may be able to open it: keep it somewhere only you can reach." : void 0;
|
|
125982
|
+
return { folder: target, files: saved.map(({ name, sha256: sha25610 }) => ({ name, sha256: sha25610 })), ownerOnly, ...warning ? { warning } : {}, ...code2 ? { code: code2.code } : {} };
|
|
125762
125983
|
}
|
|
125763
|
-
|
|
125984
|
+
}
|
|
125985
|
+
function localForm(place, platform) {
|
|
125986
|
+
if (platform !== "win32") return place;
|
|
125987
|
+
const local = /^[\\/]{2}[?.][\\/]([A-Za-z]:(?:[\\/].*)?)$/.exec(place)?.[1];
|
|
125988
|
+
return local ? local.length === 2 ? `${local}\\` : local : place;
|
|
125989
|
+
}
|
|
125990
|
+
function makeMissingFolder(place, net4, refuse13, noFolder, trail) {
|
|
125991
|
+
if (!isAbsolute7(place)) throw noFolder(place);
|
|
125992
|
+
const names = [];
|
|
125993
|
+
let parent = place;
|
|
125994
|
+
while (!existsSync(parent)) {
|
|
125995
|
+
const up = dirname28(parent);
|
|
125996
|
+
if (up === parent) throw noFolder(place);
|
|
125997
|
+
names.unshift(basename6(parent));
|
|
125998
|
+
parent = up;
|
|
125999
|
+
}
|
|
126000
|
+
if (!names.length || !isFolder(parent)) throw noFolder(place);
|
|
126001
|
+
if (net4.platform === "win32") {
|
|
126002
|
+
const bad2 = names.find((name) => WINDOWS_RESERVED.test(name.replace(/[. ]+$/, "")) || /[<>:"|?*\u0000-\u001f]/.test(name) || /[. ]$/.test(name));
|
|
126003
|
+
if (bad2) throw backupFailed(`"${bad2}" cannot be a folder name on Windows. Type another folder`);
|
|
126004
|
+
}
|
|
126005
|
+
refuse13(linkOnWay(parent, net4.platform), parent);
|
|
126006
|
+
let at = realLocal(parent, net4, refuse13);
|
|
126007
|
+
const realFolderAt = (path) => {
|
|
126008
|
+
let stat2;
|
|
125764
126009
|
try {
|
|
125765
|
-
|
|
126010
|
+
stat2 = lstatSync(path);
|
|
125766
126011
|
} catch {
|
|
126012
|
+
return false;
|
|
125767
126013
|
}
|
|
125768
|
-
|
|
125769
|
-
|
|
125770
|
-
const ownerOnly = access === "owner";
|
|
125771
|
-
const saved = [];
|
|
125772
|
-
const names = /* @__PURE__ */ new Set();
|
|
125773
|
-
for (const file of files) {
|
|
125774
|
-
let name = basename6(file);
|
|
125775
|
-
for (let n = 2; names.has(name.toLowerCase()); n += 1) name = `${basename6(file, extname(file))}-${n}${extname(file)}`;
|
|
125776
|
-
names.add(name.toLowerCase());
|
|
125777
|
-
const copy = join29(target, name);
|
|
125778
|
-
stillHere(target);
|
|
126014
|
+
if (stat2.isSymbolicLink() || !stat2.isDirectory()) return false;
|
|
126015
|
+
let now;
|
|
125779
126016
|
try {
|
|
125780
|
-
|
|
125781
|
-
|
|
125782
|
-
|
|
125783
|
-
throw backupFailed(`the copy to ${target} failed (${error?.code === "ENOSPC" ? "the drive is full" : "the drive did not take the file"}). Pick another place`);
|
|
126017
|
+
now = localForm(realpathSync.native(path), net4.platform);
|
|
126018
|
+
} catch {
|
|
126019
|
+
return false;
|
|
125784
126020
|
}
|
|
126021
|
+
return samePath(now, path, net4.platform);
|
|
126022
|
+
};
|
|
126023
|
+
for (const name of names) {
|
|
126024
|
+
if (!realFolderAt(at)) throw backupFailed(MOVED_WORDS(place));
|
|
126025
|
+
const next = join29(at, name);
|
|
125785
126026
|
try {
|
|
125786
|
-
|
|
126027
|
+
mkdirSync(next, { mode: 448 });
|
|
125787
126028
|
} catch (error) {
|
|
125788
|
-
|
|
125789
|
-
rmSync(copy, { force: true });
|
|
125790
|
-
} catch {
|
|
125791
|
-
}
|
|
125792
|
-
throw error;
|
|
126029
|
+
throw backupFailed(`the wallet could not make the folder ${place} (${error?.code === "EACCES" || error?.code === "EPERM" ? "no permission to write there" : error?.code === "ENOSPC" ? "the drive is full" : error?.code === "EEXIST" ? "another program made a part of it meanwhile" : "it cannot write there"}). Pick another place`);
|
|
125793
126030
|
}
|
|
125794
|
-
|
|
125795
|
-
|
|
125796
|
-
|
|
125797
|
-
|
|
126031
|
+
trail.dirs.push(identityOf(next));
|
|
126032
|
+
if (!realFolderAt(next)) throw backupFailed(MOVED_WORDS(place));
|
|
126033
|
+
refuse13(networkPlace(next, net4), next);
|
|
126034
|
+
at = next;
|
|
125798
126035
|
}
|
|
125799
|
-
|
|
125800
|
-
writeRecord(home, { savedAt: (/* @__PURE__ */ new Date()).toISOString(), folder: target, cleared, ...code2 ? { codeSha256: code2.sha256 } : {} });
|
|
125801
|
-
const warning = removable === true ? "Anyone who gets this USB stick can spend your money: keep it somewhere safe." : !ownerOnly ? "Other people who use this computer or drive may be able to open it: keep it somewhere only you can reach." : void 0;
|
|
125802
|
-
return { folder: target, files: saved.map(({ name, sha256: sha25610 }) => ({ name, sha256: sha25610 })), ownerOnly, ...warning ? { warning } : {}, ...code2 ? { code: code2.code } : {} };
|
|
126036
|
+
return { real: at };
|
|
125803
126037
|
}
|
|
125804
|
-
function
|
|
125805
|
-
|
|
125806
|
-
|
|
125807
|
-
|
|
126038
|
+
function identityOf(path) {
|
|
126039
|
+
const stat2 = statSync(path, { bigint: true });
|
|
126040
|
+
return { path, dev: stat2.dev, ino: stat2.ino };
|
|
126041
|
+
}
|
|
126042
|
+
function removeTrail(trail) {
|
|
126043
|
+
const same2 = (entry, kind) => {
|
|
126044
|
+
let stat2;
|
|
126045
|
+
try {
|
|
126046
|
+
stat2 = lstatSync(entry.path, { bigint: true });
|
|
126047
|
+
} catch {
|
|
126048
|
+
return false;
|
|
126049
|
+
}
|
|
126050
|
+
return !stat2.isSymbolicLink() && (kind === "file" ? stat2.isFile() : stat2.isDirectory()) && stat2.dev === entry.dev && stat2.ino === entry.ino;
|
|
126051
|
+
};
|
|
126052
|
+
for (const entry of [...trail.files].reverse()) {
|
|
126053
|
+
if (same2(entry, "file")) try {
|
|
126054
|
+
rmSync(entry.path);
|
|
126055
|
+
} catch {
|
|
126056
|
+
}
|
|
126057
|
+
}
|
|
126058
|
+
for (const entry of [...trail.dirs].reverse()) {
|
|
126059
|
+
if (same2(entry, "dir")) try {
|
|
126060
|
+
rmdirSync(entry.path);
|
|
126061
|
+
} catch {
|
|
126062
|
+
}
|
|
126063
|
+
}
|
|
125808
126064
|
}
|
|
125809
126065
|
function realLocal(path, net4, refuse13) {
|
|
125810
126066
|
let real;
|
|
@@ -125838,33 +126094,32 @@ function networkPlace(place, { platform = process.platform, driveType = windowsD
|
|
|
125838
126094
|
const local = LOCAL_FS.has(mount.type) || mount.type === "drvfs" || mount.type === "9p" && /(^|,)aname=drvfs(,|$)/.test(mount.options ?? "");
|
|
125839
126095
|
return local ? false : "unknown";
|
|
125840
126096
|
}
|
|
125841
|
-
function
|
|
125842
|
-
|
|
125843
|
-
|
|
125844
|
-
if (options.platform === "win32" && !/^[A-Za-z]:/.test(local)) return "unknown";
|
|
126097
|
+
function linkOnWay(place, platform) {
|
|
126098
|
+
const local = localForm(place, platform);
|
|
126099
|
+
if (platform === "win32" && !/^[A-Za-z]:/.test(local)) return "unknown";
|
|
125845
126100
|
const parts = local.split(/[\\/]+/).filter((part, index) => index === 0 || part !== "");
|
|
125846
126101
|
for (let end2 = 2; end2 <= parts.length; end2 += 1) {
|
|
125847
|
-
const prefix = parts.slice(0, end2).join(
|
|
126102
|
+
const prefix = parts.slice(0, end2).join(platform === "win32" ? "\\" : "/") || "/";
|
|
125848
126103
|
let stat2;
|
|
125849
126104
|
try {
|
|
125850
|
-
stat2 = lstatSync(
|
|
126105
|
+
stat2 = lstatSync(platform === "win32" && /^[A-Za-z]:$/.test(prefix) ? `${prefix}\\` : prefix);
|
|
125851
126106
|
} catch (error) {
|
|
125852
126107
|
return error?.code === "ENOENT" || error?.code === "ENOTDIR" ? "missing" : "unknown";
|
|
125853
126108
|
}
|
|
125854
|
-
if (
|
|
125855
|
-
let target;
|
|
125856
|
-
try {
|
|
125857
|
-
target = readlinkSync(prefix);
|
|
125858
|
-
} catch {
|
|
125859
|
-
return "unknown";
|
|
125860
|
-
}
|
|
125861
|
-
const next = resolve25(dirname28(prefix), target, ...parts.slice(end2));
|
|
125862
|
-
const found = networkPlace(target, options) || networkPlace(next, options);
|
|
125863
|
-
if (found) return found;
|
|
125864
|
-
return linkToNetwork(next, options, hops + 1);
|
|
126109
|
+
if (stat2.isSymbolicLink() && !systemLink(prefix, stat2, platform)) return "link";
|
|
125865
126110
|
}
|
|
125866
126111
|
return false;
|
|
125867
126112
|
}
|
|
126113
|
+
function systemLink(path, stat2, platform) {
|
|
126114
|
+
if (platform === "win32" || stat2.uid !== 0) return false;
|
|
126115
|
+
let holder;
|
|
126116
|
+
try {
|
|
126117
|
+
holder = lstatSync(posix.dirname(path));
|
|
126118
|
+
} catch {
|
|
126119
|
+
return false;
|
|
126120
|
+
}
|
|
126121
|
+
return holder.isDirectory() && !holder.isSymbolicLink() && holder.uid === 0 && (holder.mode & 18) === 0;
|
|
126122
|
+
}
|
|
125868
126123
|
function windowsDriveType(letter) {
|
|
125869
126124
|
try {
|
|
125870
126125
|
const result = spawnSync3(
|
|
@@ -125938,7 +126193,7 @@ function recordBackupSkip(home) {
|
|
|
125938
126193
|
if (backupRecord(home)?.cleared) return;
|
|
125939
126194
|
writeRecord(home, { skippedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
125940
126195
|
}
|
|
125941
|
-
var backupRecordFile, sha256Of, isFolder, backupFailed, canBackUp, NETWORK_PLACE_WORDS, MOVED_WORDS, samePath, UNKNOWN_DRIVE_WORDS, NETWORK_FS, LOCAL_FS, SYSTEM_SID, ADMINISTRATORS_SID, backupNeeded;
|
|
126196
|
+
var backupRecordFile, sha256Of, isFolder, backupFailed, canBackUp, CLOUD_WORDS, NETWORK_PLACE_WORDS, MOVED_WORDS, WINDOWS_RESERVED, samePath, UNKNOWN_DRIVE_WORDS, NETWORK_FS, LOCAL_FS, LINK_WORDS, SYSTEM_SID, ADMINISTRATORS_SID, backupNeeded;
|
|
125942
126197
|
var init_backup = __esm({
|
|
125943
126198
|
"wallet/src/setup/backup.mjs"() {
|
|
125944
126199
|
"use strict";
|
|
@@ -125963,8 +126218,10 @@ var init_backup = __esm({
|
|
|
125963
126218
|
}
|
|
125964
126219
|
return typeof config?.recoveryFile === "string" && existsSync(config.recoveryFile);
|
|
125965
126220
|
};
|
|
126221
|
+
CLOUD_WORDS = (place, service) => `${place} is synced to ${service === "a cloud service" ? "a cloud service" : `${service}'s cloud`}, so the backup would be uploaded there. Pick a USB drive or a folder that is not synced`;
|
|
125966
126222
|
NETWORK_PLACE_WORDS = (place) => `${place} is on a network share or a network drive. A backup there can be read by the people who run that share, and the computer reaches it outside Tor, so the wallet does not save it there. Pick a USB drive or a folder on this computer`;
|
|
125967
126223
|
MOVED_WORDS = (place) => `the folder ${place} changed while the wallet saved the backup (a part of its path now leads somewhere else), so the wallet stopped and removed what it wrote. Pick a USB drive or a folder on this computer, and try again`;
|
|
126224
|
+
WINDOWS_RESERVED = /^(con|prn|aux|nul|conin\$|conout\$|com[0-9\u00b9\u00b2\u00b3]|lpt[0-9\u00b9\u00b2\u00b3])(\..*)?$/i;
|
|
125968
126225
|
samePath = (a, b, platform) => platform === "win32" ? resolve25(a).toLowerCase() === resolve25(b).toLowerCase() : resolve25(a) === resolve25(b);
|
|
125969
126226
|
UNKNOWN_DRIVE_WORDS = (place) => `the wallet could not tell whether the drive of ${place} is on this computer or on a network, so it does not save the backup there. Pick a USB drive or a folder on this computer, or try again`;
|
|
125970
126227
|
NETWORK_FS = /^(nfs4?|cifs|smb[23]?|smbfs|afpfs|webdav|davfs|ncpfs|afs|ceph|glusterfs|lustre|fuse\.(sshfs|rclone|s3fs|davfs2?|gvfsd-fuse|smbnetfs))$/;
|
|
@@ -125990,6 +126247,7 @@ var init_backup = __esm({
|
|
|
125990
126247
|
"tmpfs",
|
|
125991
126248
|
"ramfs"
|
|
125992
126249
|
]);
|
|
126250
|
+
LINK_WORDS = (place) => `a part of ${place} is a link to another place (a junction or a symbolic link), so the wallet does not save the backup there. Pick a USB drive or a normal folder`;
|
|
125993
126251
|
SYSTEM_SID = "S-1-5-18";
|
|
125994
126252
|
ADMINISTRATORS_SID = "S-1-5-32-544";
|
|
125995
126253
|
backupNeeded = (home) => canBackUp(home) && backupRecord(home)?.cleared !== true && !backupSkipped(home);
|
|
@@ -126064,14 +126322,15 @@ function createBackup({ home, client, elicitTimeoutMs, unshown = () => false, lo
|
|
|
126064
126322
|
});
|
|
126065
126323
|
}
|
|
126066
126324
|
const code2 = done.saved.code;
|
|
126325
|
+
const say3 = code2 ? `Your backup code is ${code2}: say it back to me to add money. This backup is on this computer, so it does not help if this computer breaks; copy it to a USB stick too. ${savedLine(done.saved, done.removable)}` : savedLine(done.saved, done.removable);
|
|
126067
126326
|
return json2({
|
|
126068
|
-
say:
|
|
126327
|
+
say: say3,
|
|
126069
126328
|
ok: true,
|
|
126070
126329
|
folder: done.saved.folder,
|
|
126071
126330
|
files: done.saved.files.map((file) => file.name),
|
|
126072
126331
|
checked: "sha256",
|
|
126073
126332
|
...code2 ? { waitsForCode: true } : {},
|
|
126074
|
-
next: code2 ?
|
|
126333
|
+
next: code2 ? `Say this to the user word for word, with the code (the user needs the code to add money): "${say3}" Only when the user says that code back, call wallet_backup with code set to it; never send the code yourself. A USB stick needs no code.` : "Tell the user where the backup is. Deposits can go on now (call wallet_add_funds if the user asked for it)."
|
|
126075
126334
|
});
|
|
126076
126335
|
}
|
|
126077
126336
|
const boxWords = (asked) => asked.why === "failed" ? `The backup was not saved: ${asked.plain}. ` : asked.why === "declined" ? "The user closed the backup box without a choice. " : asked.why === "no_place" ? "The backup box came back with no place picked. " : asked.why === "timeout" ? "The backup box got no answer in time. " : "";
|
|
@@ -149501,7 +149760,7 @@ function successorChain(body, pool2) {
|
|
|
149501
149760
|
at = next;
|
|
149502
149761
|
}
|
|
149503
149762
|
}
|
|
149504
|
-
function
|
|
149763
|
+
function identityOf2(facilitator, release, successors = []) {
|
|
149505
149764
|
const served = { pool: release.pool, program: release.programId, network: release.network, asset: release.asset };
|
|
149506
149765
|
if (new URL(facilitator).origin === new URL(PRODUCTION.memoService).origin) {
|
|
149507
149766
|
const pool2 = successors.includes(served.pool) ? served.pool : PRODUCTION.pool;
|
|
@@ -149675,7 +149934,7 @@ async function runSetupSteps(options, deps, toolsSignal) {
|
|
|
149675
149934
|
successions = void 0;
|
|
149676
149935
|
}
|
|
149677
149936
|
}
|
|
149678
|
-
identity =
|
|
149937
|
+
identity = identityOf2(facilitator, release, successorChain(successions, PRODUCTION.pool));
|
|
149679
149938
|
try {
|
|
149680
149939
|
releasePools = parseReleasePools(release, facilitator);
|
|
149681
149940
|
} catch {
|
|
@@ -156059,8 +156318,13 @@ function readJsonFile(file) {
|
|
|
156059
156318
|
}
|
|
156060
156319
|
if (size > HOST_CONFIG_MAX_BYTES) return null;
|
|
156061
156320
|
try {
|
|
156062
|
-
const text3 = readFileSync(file, "utf8");
|
|
156063
|
-
|
|
156321
|
+
const text3 = readFileSync(file, "utf8").replace(/^/, "");
|
|
156322
|
+
let value2;
|
|
156323
|
+
try {
|
|
156324
|
+
value2 = text3.trim() ? JSON.parse(text3) : {};
|
|
156325
|
+
} catch {
|
|
156326
|
+
value2 = parseJsonc(text3).value;
|
|
156327
|
+
}
|
|
156064
156328
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
156065
156329
|
} catch {
|
|
156066
156330
|
return null;
|
|
@@ -156093,7 +156357,23 @@ function claudeCodeState(env, home) {
|
|
|
156093
156357
|
function agentHostsOf(env, home) {
|
|
156094
156358
|
const jsonHost = (target) => {
|
|
156095
156359
|
const config = readJsonFile(hostConfigFile(target, { env, home: homedir3() }));
|
|
156096
|
-
return config === void 0 ? false : config === null ? null : runsThisWallet(config
|
|
156360
|
+
return config === void 0 ? false : config === null ? null : runsThisWallet(config[hostServersKey(target)], home);
|
|
156361
|
+
};
|
|
156362
|
+
const continueHost = () => {
|
|
156363
|
+
const file = hostConfigFile("continue", { env, home: homedir3() });
|
|
156364
|
+
let size;
|
|
156365
|
+
try {
|
|
156366
|
+
size = statSync(file).size;
|
|
156367
|
+
} catch (error) {
|
|
156368
|
+
return error?.code === "ENOENT" ? false : null;
|
|
156369
|
+
}
|
|
156370
|
+
if (size > HOST_CONFIG_MAX_BYTES) return null;
|
|
156371
|
+
try {
|
|
156372
|
+
const servers = continueServersIn(readFileSync(file, "utf8"));
|
|
156373
|
+
return servers ? runsThisWallet(servers, home) : null;
|
|
156374
|
+
} catch {
|
|
156375
|
+
return null;
|
|
156376
|
+
}
|
|
156097
156377
|
};
|
|
156098
156378
|
const codexHost = () => {
|
|
156099
156379
|
const file = hostConfigFile("codex", { env, home: homedir3() });
|
|
@@ -156115,7 +156395,9 @@ function agentHostsOf(env, home) {
|
|
|
156115
156395
|
{ app: "Claude Code", ...claudeCodeState(env, home) },
|
|
156116
156396
|
{ app: "Claude Desktop", linked: jsonHost("claude-desktop") },
|
|
156117
156397
|
{ app: "Cursor", linked: jsonHost("cursor") },
|
|
156118
|
-
{ app: "Codex", linked: codexHost() }
|
|
156398
|
+
{ app: "Codex", linked: codexHost() },
|
|
156399
|
+
...["vscode", "devin", "cline", "gemini", "antigravity"].map((target) => ({ app: HOST_NAMES[target], linked: jsonHost(target) })),
|
|
156400
|
+
{ app: HOST_NAMES.continue, linked: continueHost() }
|
|
156119
156401
|
];
|
|
156120
156402
|
}
|
|
156121
156403
|
function readHomeData({ env = process.env, walletVersion: walletVersion2, torRun } = {}) {
|
|
@@ -158738,12 +159020,14 @@ function privateInferencePath(config) {
|
|
|
158738
159020
|
}
|
|
158739
159021
|
function agentAppLines({ skip = /* @__PURE__ */ new Set() }) {
|
|
158740
159022
|
if (inHomeJob()) return "To add the wallet to an agent app (Claude, Codex, Cursor and others), press [c] on the home screen.";
|
|
158741
|
-
const
|
|
158742
|
-
|
|
158743
|
-
|
|
158744
|
-
|
|
158745
|
-
}
|
|
158746
|
-
|
|
159023
|
+
const targets = HOST_TARGETS.filter((target) => !skip.has(target));
|
|
159024
|
+
return targets.length ? hostLines(targets).join("\n") : "";
|
|
159025
|
+
}
|
|
159026
|
+
function hostLines(targets) {
|
|
159027
|
+
const commands = targets.map((target) => `${cli4()} setup --add-to ${target}`);
|
|
159028
|
+
const width = Math.max(...commands.map((command) => command.length));
|
|
159029
|
+
const label = { codex: "Codex (the Codex app or Codex CLI)", devin: "Devin Desktop (the app that was Windsurf)", vscode: "VS Code (GitHub Copilot)" };
|
|
159030
|
+
return targets.map((target, index) => ` ${commands[index].padEnd(width)} ${label[target] ?? HOST_NAMES[target]}`);
|
|
158747
159031
|
}
|
|
158748
159032
|
function setupNextText({ home, deps, npmVersion, config, fundAsked, rerun, targets = [], added = /* @__PURE__ */ new Set() }) {
|
|
158749
159033
|
const parts = [];
|
|
@@ -158756,20 +159040,18 @@ function setupNextText({ home, deps, npmVersion, config, fundAsked, rerun, targe
|
|
|
158756
159040
|
env: deps.env,
|
|
158757
159041
|
platform: deps.platform ?? process.platform,
|
|
158758
159042
|
npmVersion,
|
|
159043
|
+
userHome: deps.homedir(),
|
|
158759
159044
|
skip: new Set(HOST_TARGETS.filter((target) => !failed.includes(target)))
|
|
158760
159045
|
});
|
|
158761
159046
|
if (byHand) parts.push(`Or add it by hand:
|
|
158762
159047
|
${byHand}
|
|
158763
159048
|
`);
|
|
158764
159049
|
} else if (rerun && !targets.length) {
|
|
158765
|
-
parts.push([
|
|
158766
|
-
"To add the wallet to an agent app, run one of these:",
|
|
158767
|
-
...HOST_TARGETS.map((target) => ` ${cli4()} setup --add-to ${target}`),
|
|
158768
|
-
""
|
|
158769
|
-
].join("\n"));
|
|
159050
|
+
parts.push(["To add the wallet to an agent app, run the line for that app:", ...hostLines(HOST_TARGETS), ""].join("\n"));
|
|
158770
159051
|
} else {
|
|
158771
159052
|
const hosts = agentAppLines({ skip: added });
|
|
158772
|
-
|
|
159053
|
+
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)`;
|
|
159054
|
+
if (hosts) parts.push(`${head}:
|
|
158773
159055
|
${hosts}
|
|
158774
159056
|
`);
|
|
158775
159057
|
}
|
|
@@ -159312,7 +159594,7 @@ async function addToHosts(targets, home, deps, { force = false, npmVersion, ask,
|
|
|
159312
159594
|
continue;
|
|
159313
159595
|
}
|
|
159314
159596
|
try {
|
|
159315
|
-
const write = (entry, writeForce) =>
|
|
159597
|
+
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) });
|
|
159316
159598
|
let added = write(mcpServerEntry(options), force);
|
|
159317
159599
|
for (const old of added.replaced ?? []) io.out(`Replaced the old ${old} entry in ${app}.
|
|
159318
159600
|
`);
|
|
@@ -159326,7 +159608,7 @@ async function addToHosts(targets, home, deps, { force = false, npmVersion, ask,
|
|
|
159326
159608
|
done.add(target);
|
|
159327
159609
|
} catch (error) {
|
|
159328
159610
|
const code2 = String(error.message).match(/^(setup_host_config_[a-z_]+): /)?.[1];
|
|
159329
|
-
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`;
|
|
159611
|
+
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`;
|
|
159330
159612
|
io.out(`Not added to ${app}: ${why}.
|
|
159331
159613
|
`);
|
|
159332
159614
|
}
|
|
@@ -160659,7 +160941,7 @@ var COMMANDS = [
|
|
|
160659
160941
|
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})`),
|
|
160660
160942
|
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"),
|
|
160661
160943
|
flag2("--facilitator", "<https origin>", `the server whose release, pool, and setup files setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
|
|
160662
|
-
flag2("--add-to", "
|
|
160944
|
+
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" }),
|
|
160663
160945
|
// 0.3.97 key rename: the host key is MCP_SERVER_NAME; the old keys go by themselves.
|
|
160664
160946
|
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`),
|
|
160665
160947
|
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}`)
|
|
@@ -160677,7 +160959,8 @@ var COMMANDS = [
|
|
|
160677
160959
|
"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.",
|
|
160678
160960
|
`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.`,
|
|
160679
160961
|
"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.",
|
|
160680
|
-
`--add-to
|
|
160962
|
+
`--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.`,
|
|
160963
|
+
`--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.`,
|
|
160681
160964
|
`--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.`,
|
|
160682
160965
|
"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.",
|
|
160683
160966
|
"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.",
|
|
@@ -161412,8 +161695,8 @@ function shortHelpText() {
|
|
|
161412
161695
|
"Everyday commands:",
|
|
161413
161696
|
...rows2.map(([word, about]) => ` ${word.padEnd(width)}${about}`),
|
|
161414
161697
|
"",
|
|
161415
|
-
"Add the wallet to your agent app
|
|
161416
|
-
|
|
161698
|
+
"Add the wallet to your agent app: run the line for that app once.",
|
|
161699
|
+
...hostLines(HOST_TARGETS),
|
|
161417
161700
|
"",
|
|
161418
161701
|
`One command, with an example: ${cli4()} help <command>`,
|
|
161419
161702
|
`All commands: ${cli4()} help all`,
|
|
@@ -161578,6 +161861,9 @@ var flagLabel = (index) => `the flag at position ${index + 1}`;
|
|
|
161578
161861
|
var TRANSFER_START_FLAGS = /* @__PURE__ */ new Set(["--mint", "--token-mint", "--amount-atomic", "--amount", "--to", "--max-slippage-bps", "--allow-impact", "--yes"]);
|
|
161579
161862
|
var NOT_SHOWN = "; the value given is not one (it is not repeated here)";
|
|
161580
161863
|
function checkValue(spec, value2) {
|
|
161864
|
+
if (spec.check === "host" && (Array.isArray(value2) ? value2 : [value2]).some((name) => !HOST_TARGETS.includes(name))) {
|
|
161865
|
+
return `${spec.name} takes the name of an agent app: ${HOST_TARGETS.join(", ")}${NOT_SHOWN}`;
|
|
161866
|
+
}
|
|
161581
161867
|
if (spec.check === "atomic" && !(spec.usdc === "or-all" && value2 === "all") && !/^[1-9][0-9]*$/.test(value2)) {
|
|
161582
161868
|
return `${spec.name} must be a whole number of atomic units above 0 (1 USDC = 1000000)${NOT_SHOWN}`;
|
|
161583
161869
|
}
|
|
@@ -164730,6 +165016,7 @@ ${HOST_TARGETS.map((host, index) => ` ${index + 1} ${hostNames[host] ?? host}`
|
|
|
164730
165016
|
env: deps.env,
|
|
164731
165017
|
platform: deps.platform ?? process.platform,
|
|
164732
165018
|
npmVersion,
|
|
165019
|
+
userHome: deps.homedir(),
|
|
164733
165020
|
skip: new Set(HOST_TARGETS.filter((target) => target !== host))
|
|
164734
165021
|
});
|
|
164735
165022
|
if (byHand) io.out(`${byHand}
|