shinjuku-shielded 0.3.159 → 0.3.160
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 +238 -85
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.160. 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.160
|
|
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.160 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.160 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.160", "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.160", "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": "eb6f9ba6c43c52189f2d1f9124f1e23b23e471a168ffc0fe008c818c35f6b981",
|
|
5
|
+
"bytes": 9251599,
|
|
6
|
+
"sourceCommit": "58e77ed499568c5b3ec71e010ed2b0a1f75e20f0",
|
|
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-04T04:35:54.717Z"
|
|
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.160",
|
|
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": "eb6f9ba6",
|
|
7
|
+
"shinjukuSha256": "eb6f9ba6c43c52189f2d1f9124f1e23b23e471a168ffc0fe008c818c35f6b981",
|
|
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) {
|
|
@@ -125660,6 +125660,8 @@ function clearBackupWithCode(home, code2) {
|
|
|
125660
125660
|
return true;
|
|
125661
125661
|
}
|
|
125662
125662
|
function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
125663
|
+
const roots = cloudRoots({ platform, env, home: homedir2() });
|
|
125664
|
+
const notSynced = (list) => list.filter((place) => !cloudPlace(place.path, { platform, env, roots: () => roots }));
|
|
125663
125665
|
const places = [];
|
|
125664
125666
|
if (platform === "win32") {
|
|
125665
125667
|
try {
|
|
@@ -125681,12 +125683,12 @@ function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
|
125681
125683
|
} catch {
|
|
125682
125684
|
}
|
|
125683
125685
|
const profile = env.USERPROFILE ?? homedir2();
|
|
125684
|
-
for (const [label, path] of [["Desktop", join29(profile, "Desktop")], ["
|
|
125686
|
+
for (const [label, path] of [["Desktop", join29(profile, "Desktop")], ["Documents", join29(profile, "Documents")]]) {
|
|
125685
125687
|
if (isFolder(path) && !places.some((place) => place.path.toLowerCase() === path.toLowerCase())) {
|
|
125686
125688
|
places.push({ label: `${label} (${path}; on this computer: it does not help if this computer breaks)`, path, removable: false });
|
|
125687
125689
|
}
|
|
125688
125690
|
}
|
|
125689
|
-
return places;
|
|
125691
|
+
return notSynced(places);
|
|
125690
125692
|
}
|
|
125691
125693
|
let user = "";
|
|
125692
125694
|
try {
|
|
@@ -125706,7 +125708,66 @@ function backupPlaces({ platform = process.platform, env = process.env } = {}) {
|
|
|
125706
125708
|
for (const path of [join29(homedir2(), "Desktop"), homedir2()]) {
|
|
125707
125709
|
if (isFolder(path)) places.push({ label: `${path} (on this computer: it does not help if this computer breaks)`, path, removable: false });
|
|
125708
125710
|
}
|
|
125709
|
-
return places;
|
|
125711
|
+
return notSynced(places);
|
|
125712
|
+
}
|
|
125713
|
+
function cloudPlace(place, { platform = process.platform, env = process.env, home = homedir2(), roots } = {}) {
|
|
125714
|
+
const text3 = String(place ?? "").trim();
|
|
125715
|
+
if (!text3) return void 0;
|
|
125716
|
+
const fold = platform === "win32" || platform === "darwin";
|
|
125717
|
+
const sep5 = platform === "win32" ? "\\" : "/";
|
|
125718
|
+
const norm = (path) => {
|
|
125719
|
+
const full = (platform === "win32" ? resolve25(localForm(path, platform)) : posix.resolve(path)).replace(/[\\/]+$/, "");
|
|
125720
|
+
return fold ? full.toLowerCase() : full;
|
|
125721
|
+
};
|
|
125722
|
+
const at = norm(text3);
|
|
125723
|
+
for (const root of (roots ?? (() => cloudRoots({ platform, env, home })))()) {
|
|
125724
|
+
const base = norm(root.path);
|
|
125725
|
+
if (base && (at === base || at.startsWith(`${base}${sep5}`))) return root.service;
|
|
125726
|
+
}
|
|
125727
|
+
for (const part of text3.split(/[\\/]+/).map((name) => name.trim().toLowerCase())) {
|
|
125728
|
+
if (part === "onedrive" || part.startsWith("onedrive - ") || part.startsWith("onedrive-")) return "OneDrive";
|
|
125729
|
+
if (part === "dropbox" || part.startsWith("dropbox (") || part.startsWith("dropbox-")) return "Dropbox";
|
|
125730
|
+
if (["my drive", "shared drives", "google drive"].includes(part) || part.startsWith("googledrive")) return "Google Drive";
|
|
125731
|
+
if (["iclouddrive", "icloud drive", "icloud photos", "mobile documents"].includes(part)) return "iCloud";
|
|
125732
|
+
}
|
|
125733
|
+
return void 0;
|
|
125734
|
+
}
|
|
125735
|
+
function cloudRoots({ platform = process.platform, env = process.env, home = homedir2() } = {}) {
|
|
125736
|
+
const roots = [];
|
|
125737
|
+
const add2 = (path, service) => {
|
|
125738
|
+
if (typeof path === "string" && path.trim()) roots.push({ path: path.trim(), service });
|
|
125739
|
+
};
|
|
125740
|
+
const dropbox = (file) => {
|
|
125741
|
+
let info;
|
|
125742
|
+
try {
|
|
125743
|
+
info = JSON.parse(readFileSync(file, "utf8"));
|
|
125744
|
+
} catch {
|
|
125745
|
+
return;
|
|
125746
|
+
}
|
|
125747
|
+
for (const account of Object.values(info ?? {})) add2(account?.path, "Dropbox");
|
|
125748
|
+
};
|
|
125749
|
+
if (platform === "win32") {
|
|
125750
|
+
for (const name of ["OneDrive", "OneDriveConsumer", "OneDriveCommercial"]) add2(env[name], "OneDrive");
|
|
125751
|
+
add2(join29(env.USERPROFILE ?? home, "OneDrive"), "OneDrive");
|
|
125752
|
+
try {
|
|
125753
|
+
const result = spawnSync3(
|
|
125754
|
+
"reg.exe",
|
|
125755
|
+
["query", "HKCU\\Software\\Microsoft\\OneDrive\\Accounts", "/s", "/v", "UserFolder"],
|
|
125756
|
+
{ windowsHide: true, encoding: "utf8", timeout: 1e4 }
|
|
125757
|
+
);
|
|
125758
|
+
for (const match of String(result.stdout ?? "").matchAll(/UserFolder\s+REG_\w+\s+(.+)/g)) add2(match[1], "OneDrive");
|
|
125759
|
+
} catch {
|
|
125760
|
+
}
|
|
125761
|
+
if (env.APPDATA) dropbox(join29(env.APPDATA, "Dropbox", "info.json"));
|
|
125762
|
+
if (env.LOCALAPPDATA) dropbox(join29(env.LOCALAPPDATA, "Dropbox", "info.json"));
|
|
125763
|
+
return roots;
|
|
125764
|
+
}
|
|
125765
|
+
dropbox(posix.join(home, ".dropbox", "info.json"));
|
|
125766
|
+
if (platform === "darwin") {
|
|
125767
|
+
add2(posix.join(home, "Library", "Mobile Documents"), "iCloud");
|
|
125768
|
+
add2(posix.join(home, "Library", "CloudStorage"), "a cloud service");
|
|
125769
|
+
}
|
|
125770
|
+
return roots;
|
|
125710
125771
|
}
|
|
125711
125772
|
async function saveBackup(home, folder, {
|
|
125712
125773
|
removable,
|
|
@@ -125714,7 +125775,8 @@ async function saveBackup(home, folder, {
|
|
|
125714
125775
|
platform = process.platform,
|
|
125715
125776
|
restrict = restrictFolderToOwner,
|
|
125716
125777
|
driveType = windowsDriveType,
|
|
125717
|
-
mounts = posixMounts
|
|
125778
|
+
mounts = posixMounts,
|
|
125779
|
+
env = process.env
|
|
125718
125780
|
} = {}) {
|
|
125719
125781
|
const typed = String(folder ?? "").trim().replace(/^"(.*)"$/, "$1");
|
|
125720
125782
|
const types = /* @__PURE__ */ new Map();
|
|
@@ -125726,85 +125788,173 @@ async function saveBackup(home, folder, {
|
|
|
125726
125788
|
const noFolder = (at) => backupFailed(`there is no folder ${at}. Plug in the drive or type another folder`);
|
|
125727
125789
|
const refuse13 = (found, at) => {
|
|
125728
125790
|
if (found === "missing") throw noFolder(at);
|
|
125791
|
+
if (found === "link") throw backupFailed(LINK_WORDS(at));
|
|
125729
125792
|
if (found) throw backupFailed(found === "unknown" ? UNKNOWN_DRIVE_WORDS(at) : NETWORK_PLACE_WORDS(at));
|
|
125730
125793
|
};
|
|
125794
|
+
const roots = cloudRoots({ platform, env, home: homedir2() });
|
|
125795
|
+
const refuseCloud = (at) => {
|
|
125796
|
+
const service = cloudPlace(at, { platform, env, roots: () => roots });
|
|
125797
|
+
if (service) throw backupFailed(CLOUD_WORDS(at, service));
|
|
125798
|
+
};
|
|
125731
125799
|
refuse13(networkPlace(typed, net4), typed);
|
|
125732
125800
|
const place = localForm(resolve25(typed), platform);
|
|
125733
125801
|
refuse13(networkPlace(place, net4), place);
|
|
125734
|
-
|
|
125735
|
-
|
|
125736
|
-
const
|
|
125737
|
-
|
|
125738
|
-
const
|
|
125739
|
-
let
|
|
125740
|
-
|
|
125741
|
-
|
|
125742
|
-
|
|
125802
|
+
refuseCloud(place);
|
|
125803
|
+
const onWay = linkOnWay(place, platform);
|
|
125804
|
+
const fullPath = isAbsolute7(typed) && (platform !== "win32" || /^(?:[\\/]{2}[?.][\\/])?[A-Za-z]:[\\/]/.test(typed));
|
|
125805
|
+
if (onWay === "missing" && !fullPath) throw noFolder(place);
|
|
125806
|
+
const trail = { files: [], dirs: [] };
|
|
125807
|
+
let made;
|
|
125808
|
+
try {
|
|
125809
|
+
made = onWay === "missing" ? makeMissingFolder(place, net4, refuse13, noFolder, trail) : refuse13(onWay, place);
|
|
125810
|
+
return await saveInFolder();
|
|
125743
125811
|
} catch (error) {
|
|
125744
|
-
|
|
125812
|
+
removeTrail(trail);
|
|
125813
|
+
throw error;
|
|
125745
125814
|
}
|
|
125746
|
-
|
|
125747
|
-
|
|
125815
|
+
async function saveInFolder() {
|
|
125816
|
+
if (!isAbsolute7(place) || !isFolder(place)) throw noFolder(place);
|
|
125817
|
+
const real = realLocal(place, net4, refuse13);
|
|
125818
|
+
refuseCloud(real);
|
|
125819
|
+
if (made && !samePath(real, made.real, platform)) throw backupFailed(MOVED_WORDS(place));
|
|
125820
|
+
const files = backupFiles(home);
|
|
125821
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
125822
|
+
let target = join29(real, `shinjuku-backup-${date}`);
|
|
125823
|
+
for (let n = 2; existsSync(target); n += 1) target = join29(real, `shinjuku-backup-${date}-${n}`);
|
|
125824
|
+
try {
|
|
125825
|
+
mkdirSync(target, { mode: 448 });
|
|
125826
|
+
trail.dirs.push(identityOf(target));
|
|
125827
|
+
} catch (error) {
|
|
125828
|
+
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`);
|
|
125829
|
+
}
|
|
125830
|
+
const stillHere = (path) => {
|
|
125831
|
+
let now;
|
|
125832
|
+
try {
|
|
125833
|
+
now = localForm(realpathSync.native(path), platform);
|
|
125834
|
+
} catch {
|
|
125835
|
+
now = void 0;
|
|
125836
|
+
}
|
|
125837
|
+
if (!now || !samePath(now, path, platform)) throw backupFailed(MOVED_WORDS(place));
|
|
125838
|
+
refuse13(networkPlace(now, net4), now);
|
|
125839
|
+
refuseCloud(now);
|
|
125840
|
+
};
|
|
125841
|
+
stillHere(target);
|
|
125842
|
+
let access;
|
|
125748
125843
|
try {
|
|
125749
|
-
|
|
125844
|
+
access = restrict(target, platform);
|
|
125750
125845
|
} catch {
|
|
125751
|
-
|
|
125846
|
+
access = "open";
|
|
125847
|
+
}
|
|
125848
|
+
if (access === "foreign") {
|
|
125849
|
+
throw backupFailed(`this folder can be opened by other accounts (${place}); pick another place`);
|
|
125850
|
+
}
|
|
125851
|
+
const ownerOnly = access === "owner";
|
|
125852
|
+
const saved = [];
|
|
125853
|
+
const names = /* @__PURE__ */ new Set();
|
|
125854
|
+
for (const file of files) {
|
|
125855
|
+
let name = basename6(file);
|
|
125856
|
+
for (let n = 2; names.has(name.toLowerCase()); n += 1) name = `${basename6(file, extname(file))}-${n}${extname(file)}`;
|
|
125857
|
+
names.add(name.toLowerCase());
|
|
125858
|
+
const copy = join29(target, name);
|
|
125859
|
+
stillHere(target);
|
|
125860
|
+
try {
|
|
125861
|
+
copyFileSync(file, copy, constants.COPYFILE_EXCL);
|
|
125862
|
+
trail.files.push(identityOf(copy));
|
|
125863
|
+
if (platform !== "win32") chmodSync(copy, 384);
|
|
125864
|
+
} catch (error) {
|
|
125865
|
+
throw backupFailed(`the copy to ${target} failed (${error?.code === "ENOSPC" ? "the drive is full" : "the drive did not take the file"}). Pick another place`);
|
|
125866
|
+
}
|
|
125867
|
+
stillHere(copy);
|
|
125868
|
+
saved.push({ name, source: file, sha256: sha256Of(file) });
|
|
125752
125869
|
}
|
|
125753
|
-
|
|
125754
|
-
|
|
125755
|
-
|
|
125756
|
-
|
|
125757
|
-
|
|
125758
|
-
|
|
125759
|
-
|
|
125760
|
-
} catch {
|
|
125761
|
-
access = "open";
|
|
125870
|
+
for (const entry of saved) {
|
|
125871
|
+
if (sha256Of(join29(target, entry.name)) !== entry.sha256) throw backupFailed(`the copy in ${target} does not match the original. Pick another place`);
|
|
125872
|
+
}
|
|
125873
|
+
const code2 = cleared ? void 0 : backupCode();
|
|
125874
|
+
writeRecord(home, { savedAt: (/* @__PURE__ */ new Date()).toISOString(), folder: target, cleared, ...code2 ? { codeSha256: code2.sha256 } : {} });
|
|
125875
|
+
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;
|
|
125876
|
+
return { folder: target, files: saved.map(({ name, sha256: sha25610 }) => ({ name, sha256: sha25610 })), ownerOnly, ...warning ? { warning } : {}, ...code2 ? { code: code2.code } : {} };
|
|
125762
125877
|
}
|
|
125763
|
-
|
|
125878
|
+
}
|
|
125879
|
+
function localForm(place, platform) {
|
|
125880
|
+
if (platform !== "win32") return place;
|
|
125881
|
+
const local = /^[\\/]{2}[?.][\\/]([A-Za-z]:(?:[\\/].*)?)$/.exec(place)?.[1];
|
|
125882
|
+
return local ? local.length === 2 ? `${local}\\` : local : place;
|
|
125883
|
+
}
|
|
125884
|
+
function makeMissingFolder(place, net4, refuse13, noFolder, trail) {
|
|
125885
|
+
if (!isAbsolute7(place)) throw noFolder(place);
|
|
125886
|
+
const names = [];
|
|
125887
|
+
let parent = place;
|
|
125888
|
+
while (!existsSync(parent)) {
|
|
125889
|
+
const up = dirname28(parent);
|
|
125890
|
+
if (up === parent) throw noFolder(place);
|
|
125891
|
+
names.unshift(basename6(parent));
|
|
125892
|
+
parent = up;
|
|
125893
|
+
}
|
|
125894
|
+
if (!names.length || !isFolder(parent)) throw noFolder(place);
|
|
125895
|
+
if (net4.platform === "win32") {
|
|
125896
|
+
const bad2 = names.find((name) => WINDOWS_RESERVED.test(name.replace(/[. ]+$/, "")) || /[<>:"|?*\u0000-\u001f]/.test(name) || /[. ]$/.test(name));
|
|
125897
|
+
if (bad2) throw backupFailed(`"${bad2}" cannot be a folder name on Windows. Type another folder`);
|
|
125898
|
+
}
|
|
125899
|
+
refuse13(linkOnWay(parent, net4.platform), parent);
|
|
125900
|
+
let at = realLocal(parent, net4, refuse13);
|
|
125901
|
+
const realFolderAt = (path) => {
|
|
125902
|
+
let stat2;
|
|
125764
125903
|
try {
|
|
125765
|
-
|
|
125904
|
+
stat2 = lstatSync(path);
|
|
125766
125905
|
} catch {
|
|
125906
|
+
return false;
|
|
125767
125907
|
}
|
|
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);
|
|
125908
|
+
if (stat2.isSymbolicLink() || !stat2.isDirectory()) return false;
|
|
125909
|
+
let now;
|
|
125779
125910
|
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`);
|
|
125911
|
+
now = localForm(realpathSync.native(path), net4.platform);
|
|
125912
|
+
} catch {
|
|
125913
|
+
return false;
|
|
125784
125914
|
}
|
|
125915
|
+
return samePath(now, path, net4.platform);
|
|
125916
|
+
};
|
|
125917
|
+
for (const name of names) {
|
|
125918
|
+
if (!realFolderAt(at)) throw backupFailed(MOVED_WORDS(place));
|
|
125919
|
+
const next = join29(at, name);
|
|
125785
125920
|
try {
|
|
125786
|
-
|
|
125921
|
+
mkdirSync(next, { mode: 448 });
|
|
125787
125922
|
} catch (error) {
|
|
125788
|
-
|
|
125789
|
-
rmSync(copy, { force: true });
|
|
125790
|
-
} catch {
|
|
125791
|
-
}
|
|
125792
|
-
throw error;
|
|
125923
|
+
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
125924
|
}
|
|
125794
|
-
|
|
125795
|
-
|
|
125796
|
-
|
|
125797
|
-
|
|
125925
|
+
trail.dirs.push(identityOf(next));
|
|
125926
|
+
if (!realFolderAt(next)) throw backupFailed(MOVED_WORDS(place));
|
|
125927
|
+
refuse13(networkPlace(next, net4), next);
|
|
125928
|
+
at = next;
|
|
125798
125929
|
}
|
|
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 } : {} };
|
|
125930
|
+
return { real: at };
|
|
125803
125931
|
}
|
|
125804
|
-
function
|
|
125805
|
-
|
|
125806
|
-
|
|
125807
|
-
|
|
125932
|
+
function identityOf(path) {
|
|
125933
|
+
const stat2 = statSync(path, { bigint: true });
|
|
125934
|
+
return { path, dev: stat2.dev, ino: stat2.ino };
|
|
125935
|
+
}
|
|
125936
|
+
function removeTrail(trail) {
|
|
125937
|
+
const same2 = (entry, kind) => {
|
|
125938
|
+
let stat2;
|
|
125939
|
+
try {
|
|
125940
|
+
stat2 = lstatSync(entry.path, { bigint: true });
|
|
125941
|
+
} catch {
|
|
125942
|
+
return false;
|
|
125943
|
+
}
|
|
125944
|
+
return !stat2.isSymbolicLink() && (kind === "file" ? stat2.isFile() : stat2.isDirectory()) && stat2.dev === entry.dev && stat2.ino === entry.ino;
|
|
125945
|
+
};
|
|
125946
|
+
for (const entry of [...trail.files].reverse()) {
|
|
125947
|
+
if (same2(entry, "file")) try {
|
|
125948
|
+
rmSync(entry.path);
|
|
125949
|
+
} catch {
|
|
125950
|
+
}
|
|
125951
|
+
}
|
|
125952
|
+
for (const entry of [...trail.dirs].reverse()) {
|
|
125953
|
+
if (same2(entry, "dir")) try {
|
|
125954
|
+
rmdirSync(entry.path);
|
|
125955
|
+
} catch {
|
|
125956
|
+
}
|
|
125957
|
+
}
|
|
125808
125958
|
}
|
|
125809
125959
|
function realLocal(path, net4, refuse13) {
|
|
125810
125960
|
let real;
|
|
@@ -125838,33 +125988,32 @@ function networkPlace(place, { platform = process.platform, driveType = windowsD
|
|
|
125838
125988
|
const local = LOCAL_FS.has(mount.type) || mount.type === "drvfs" || mount.type === "9p" && /(^|,)aname=drvfs(,|$)/.test(mount.options ?? "");
|
|
125839
125989
|
return local ? false : "unknown";
|
|
125840
125990
|
}
|
|
125841
|
-
function
|
|
125842
|
-
|
|
125843
|
-
|
|
125844
|
-
if (options.platform === "win32" && !/^[A-Za-z]:/.test(local)) return "unknown";
|
|
125991
|
+
function linkOnWay(place, platform) {
|
|
125992
|
+
const local = localForm(place, platform);
|
|
125993
|
+
if (platform === "win32" && !/^[A-Za-z]:/.test(local)) return "unknown";
|
|
125845
125994
|
const parts = local.split(/[\\/]+/).filter((part, index) => index === 0 || part !== "");
|
|
125846
125995
|
for (let end2 = 2; end2 <= parts.length; end2 += 1) {
|
|
125847
|
-
const prefix = parts.slice(0, end2).join(
|
|
125996
|
+
const prefix = parts.slice(0, end2).join(platform === "win32" ? "\\" : "/") || "/";
|
|
125848
125997
|
let stat2;
|
|
125849
125998
|
try {
|
|
125850
|
-
stat2 = lstatSync(
|
|
125999
|
+
stat2 = lstatSync(platform === "win32" && /^[A-Za-z]:$/.test(prefix) ? `${prefix}\\` : prefix);
|
|
125851
126000
|
} catch (error) {
|
|
125852
126001
|
return error?.code === "ENOENT" || error?.code === "ENOTDIR" ? "missing" : "unknown";
|
|
125853
126002
|
}
|
|
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);
|
|
126003
|
+
if (stat2.isSymbolicLink() && !systemLink(prefix, stat2, platform)) return "link";
|
|
125865
126004
|
}
|
|
125866
126005
|
return false;
|
|
125867
126006
|
}
|
|
126007
|
+
function systemLink(path, stat2, platform) {
|
|
126008
|
+
if (platform === "win32" || stat2.uid !== 0) return false;
|
|
126009
|
+
let holder;
|
|
126010
|
+
try {
|
|
126011
|
+
holder = lstatSync(posix.dirname(path));
|
|
126012
|
+
} catch {
|
|
126013
|
+
return false;
|
|
126014
|
+
}
|
|
126015
|
+
return holder.isDirectory() && !holder.isSymbolicLink() && holder.uid === 0 && (holder.mode & 18) === 0;
|
|
126016
|
+
}
|
|
125868
126017
|
function windowsDriveType(letter) {
|
|
125869
126018
|
try {
|
|
125870
126019
|
const result = spawnSync3(
|
|
@@ -125938,7 +126087,7 @@ function recordBackupSkip(home) {
|
|
|
125938
126087
|
if (backupRecord(home)?.cleared) return;
|
|
125939
126088
|
writeRecord(home, { skippedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
125940
126089
|
}
|
|
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;
|
|
126090
|
+
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
126091
|
var init_backup = __esm({
|
|
125943
126092
|
"wallet/src/setup/backup.mjs"() {
|
|
125944
126093
|
"use strict";
|
|
@@ -125963,8 +126112,10 @@ var init_backup = __esm({
|
|
|
125963
126112
|
}
|
|
125964
126113
|
return typeof config?.recoveryFile === "string" && existsSync(config.recoveryFile);
|
|
125965
126114
|
};
|
|
126115
|
+
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
126116
|
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
126117
|
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`;
|
|
126118
|
+
WINDOWS_RESERVED = /^(con|prn|aux|nul|conin\$|conout\$|com[0-9\u00b9\u00b2\u00b3]|lpt[0-9\u00b9\u00b2\u00b3])(\..*)?$/i;
|
|
125968
126119
|
samePath = (a, b, platform) => platform === "win32" ? resolve25(a).toLowerCase() === resolve25(b).toLowerCase() : resolve25(a) === resolve25(b);
|
|
125969
126120
|
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
126121
|
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 +126141,7 @@ var init_backup = __esm({
|
|
|
125990
126141
|
"tmpfs",
|
|
125991
126142
|
"ramfs"
|
|
125992
126143
|
]);
|
|
126144
|
+
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
126145
|
SYSTEM_SID = "S-1-5-18";
|
|
125994
126146
|
ADMINISTRATORS_SID = "S-1-5-32-544";
|
|
125995
126147
|
backupNeeded = (home) => canBackUp(home) && backupRecord(home)?.cleared !== true && !backupSkipped(home);
|
|
@@ -126064,14 +126216,15 @@ function createBackup({ home, client, elicitTimeoutMs, unshown = () => false, lo
|
|
|
126064
126216
|
});
|
|
126065
126217
|
}
|
|
126066
126218
|
const code2 = done.saved.code;
|
|
126219
|
+
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
126220
|
return json2({
|
|
126068
|
-
say:
|
|
126221
|
+
say: say3,
|
|
126069
126222
|
ok: true,
|
|
126070
126223
|
folder: done.saved.folder,
|
|
126071
126224
|
files: done.saved.files.map((file) => file.name),
|
|
126072
126225
|
checked: "sha256",
|
|
126073
126226
|
...code2 ? { waitsForCode: true } : {},
|
|
126074
|
-
next: code2 ?
|
|
126227
|
+
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
126228
|
});
|
|
126076
126229
|
}
|
|
126077
126230
|
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 +149654,7 @@ function successorChain(body, pool2) {
|
|
|
149501
149654
|
at = next;
|
|
149502
149655
|
}
|
|
149503
149656
|
}
|
|
149504
|
-
function
|
|
149657
|
+
function identityOf2(facilitator, release, successors = []) {
|
|
149505
149658
|
const served = { pool: release.pool, program: release.programId, network: release.network, asset: release.asset };
|
|
149506
149659
|
if (new URL(facilitator).origin === new URL(PRODUCTION.memoService).origin) {
|
|
149507
149660
|
const pool2 = successors.includes(served.pool) ? served.pool : PRODUCTION.pool;
|
|
@@ -149675,7 +149828,7 @@ async function runSetupSteps(options, deps, toolsSignal) {
|
|
|
149675
149828
|
successions = void 0;
|
|
149676
149829
|
}
|
|
149677
149830
|
}
|
|
149678
|
-
identity =
|
|
149831
|
+
identity = identityOf2(facilitator, release, successorChain(successions, PRODUCTION.pool));
|
|
149679
149832
|
try {
|
|
149680
149833
|
releasePools = parseReleasePools(release, facilitator);
|
|
149681
149834
|
} catch {
|