shinjuku-shield 0.3.82 → 0.3.83
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 +5 -5
- package/manifest.json +4 -4
- package/package.json +3 -3
- package/shinjuku-wallet.mjs +232 -132
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ answers privately, in a terminal chat or for your agent app. Its `mcp`
|
|
|
6
6
|
command runs a local MCP server (stdio) for your agent. Your keys never
|
|
7
7
|
leave your machine.
|
|
8
8
|
|
|
9
|
-
This is version 0.3.
|
|
9
|
+
This is version 0.3.83. The npm packages `shinjuku-shield` and
|
|
10
10
|
`shinjuku-shielded` hold the same files; `shinjuku` is the short command.
|
|
11
11
|
|
|
12
12
|
## Set up
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
npx -y --no-audit --no-fund shinjuku-shield@0.3.
|
|
15
|
+
npx -y --no-audit --no-fund shinjuku-shield@0.3.83 setup
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Setup makes the wallet on this machine, a private passphrase file, and the
|
|
@@ -24,7 +24,7 @@ server to that host's config.
|
|
|
24
24
|
## The `shinjuku` command
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
-
npm install -g shinjuku@0.3.
|
|
27
|
+
npm install -g shinjuku@0.3.83
|
|
28
28
|
shinjuku help
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -36,13 +36,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
|
|
|
36
36
|
Claude Code:
|
|
37
37
|
|
|
38
38
|
```sh
|
|
39
|
-
claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.
|
|
39
|
+
claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.83 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-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.
|
|
45
|
+
{ "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.83", "mcp"] } } }
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
|
package/manifest.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "carbon-shielded-wallet/bundle-manifest/v1",
|
|
3
3
|
"file": "shinjuku-wallet.mjs",
|
|
4
|
-
"sha256": "
|
|
5
|
-
"bytes":
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"sha256": "a59aa27b98cd1822c4651ed0567cd35b3f03e087818d8e5cfdb71e6320b7ffae",
|
|
5
|
+
"bytes": 12484127,
|
|
6
|
+
"sourceCommit": "ad531c40fe817d116a109d33472895bbbd854158",
|
|
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",
|
|
@@ -1282,5 +1282,5 @@
|
|
|
1282
1282
|
"version": "1.8.0"
|
|
1283
1283
|
}
|
|
1284
1284
|
],
|
|
1285
|
-
"publishedAt": "2026-10-
|
|
1285
|
+
"publishedAt": "2026-10-23T18:18:57.601Z"
|
|
1286
1286
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shinjuku-shield",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.83",
|
|
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": "a59aa27b",
|
|
7
|
+
"shinjukuSha256": "a59aa27b98cd1822c4651ed0567cd35b3f03e087818d8e5cfdb71e6320b7ffae",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"author": "Shinjuku Shielded",
|
|
10
10
|
"homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
|
package/shinjuku-wallet.mjs
CHANGED
|
@@ -47211,8 +47211,9 @@ function reexec(args, mode, plainProxy) {
|
|
|
47211
47211
|
...proxyEnv(proxy),
|
|
47212
47212
|
[MARK]: `${mode} ${proxy}`
|
|
47213
47213
|
};
|
|
47214
|
-
delete env[
|
|
47215
|
-
|
|
47214
|
+
delete env[QUIET_RUN_MARK];
|
|
47215
|
+
const quiet = quietRun(args);
|
|
47216
|
+
if (quiet) env[QUIET_RUN_MARK] = quiet;
|
|
47216
47217
|
return new Promise((resolvePromise) => {
|
|
47217
47218
|
const child = spawn(process.execPath, [...process.execArgv, process.argv[1], ...args], { env, stdio: "inherit" });
|
|
47218
47219
|
const ignore = () => {
|
|
@@ -47232,8 +47233,9 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
|
|
|
47232
47233
|
const proxied = process.env[MARK];
|
|
47233
47234
|
if (proxied) {
|
|
47234
47235
|
const [mode, proxy] = proxied.split(" ");
|
|
47235
|
-
const
|
|
47236
|
-
|
|
47236
|
+
const quietMark = process.env[QUIET_RUN_MARK];
|
|
47237
|
+
const quiet = Object.hasOwn(QUIET_NOTICE, quietMark ?? "") ? QUIET_NOTICE[quietMark] : void 0;
|
|
47238
|
+
delete process.env[QUIET_RUN_MARK];
|
|
47237
47239
|
refuseDirectSockets(proxy);
|
|
47238
47240
|
if (!canSwitchTorTag()) {
|
|
47239
47241
|
err(`shinjuku: proxy_node_unsupported: Node ${process.versions.node} does not send its requests through a proxy (it ignores NODE_USE_ENV_PROXY), so this run cannot keep them private; nothing was sent. Install the current Node 22 or 24 release (Node 22.23 and 24.13 work), then rerun
|
|
@@ -47255,7 +47257,7 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
|
|
|
47255
47257
|
}
|
|
47256
47258
|
return 1;
|
|
47257
47259
|
}
|
|
47258
|
-
err(`${quiet ?
|
|
47260
|
+
err(`${quiet ? quiet[mode] : NOTICE[mode]}
|
|
47259
47261
|
`);
|
|
47260
47262
|
const ohttp = startOhttp(argv2, { tor: mode === "tor", err });
|
|
47261
47263
|
if (ohttp.exitCode !== void 0) return ohttp.exitCode;
|
|
@@ -47289,7 +47291,7 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
|
|
|
47289
47291
|
}
|
|
47290
47292
|
return reexec(options.args, options.mode, options.proxy);
|
|
47291
47293
|
}
|
|
47292
|
-
var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE,
|
|
47294
|
+
var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE, QUIET_RUN_MARK, QUIET_NOTICE, quietRun, TOR_TROUBLE_LINE, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER2, laneDispatchers, paymentWarmStop;
|
|
47293
47295
|
var init_proxy = __esm({
|
|
47294
47296
|
"wallet/src/net/proxy.mjs"() {
|
|
47295
47297
|
"use strict";
|
|
@@ -47349,9 +47351,16 @@ var init_proxy = __esm({
|
|
|
47349
47351
|
tor: "tor: every request of this run goes through Tor, so the facilitator, the RPC provider, and other servers see a Tor exit, not your IP. It does not hide what a request carries or when: a keyed RPC URL still names its account holder (use a key-free RPC), and timing still links your requests.",
|
|
47350
47352
|
proxy: "proxy: every request of this run goes through the proxy, so servers see its address, not yours; the proxy sees every request. It does not hide what a request carries or when: a keyed RPC URL still names its account holder (use a key-free RPC), and timing still links your requests."
|
|
47351
47353
|
};
|
|
47352
|
-
|
|
47353
|
-
QUIET_NOTICE = {
|
|
47354
|
-
|
|
47354
|
+
QUIET_RUN_MARK = "SHINJUKU_WALLET_QUIET_RUN";
|
|
47355
|
+
QUIET_NOTICE = {
|
|
47356
|
+
chat: { tor: "Private: this chat goes through Tor.", proxy: "Private: this chat goes through your proxy." },
|
|
47357
|
+
funds: { tor: "Private: every request of this run goes through Tor.", proxy: "Private: every request of this run goes through your proxy." }
|
|
47358
|
+
};
|
|
47359
|
+
quietRun = (args) => {
|
|
47360
|
+
if (args.includes("--verbose")) return void 0;
|
|
47361
|
+
if (args[0] === "chat") return "chat";
|
|
47362
|
+
return ["add-funds", "funds-status"].includes(args[0]) && process.stdout.isTTY && !args.includes("--json") ? "funds" : void 0;
|
|
47363
|
+
};
|
|
47355
47364
|
TOR_TROUBLE_LINE = /\bno (?:other )?(?:history-proxy )?onion\b|\bgave no answer\b|\bstalled\b|\bHTTP 429\b|\bcannot move\b/;
|
|
47356
47365
|
quietTorErr = (err) => (text3) => {
|
|
47357
47366
|
if (/^tor: /.test(text3) && !TOR_TROUBLE_LINE.test(text3) && !/\bWARNING\b/.test(text3)) return;
|
|
@@ -97243,7 +97252,11 @@ async function payViaFloat({
|
|
|
97243
97252
|
});
|
|
97244
97253
|
}
|
|
97245
97254
|
const watched = watch.verdict();
|
|
97246
|
-
|
|
97255
|
+
let known = watched?.state === "final" || watched?.state === "dead" ? watched : lostAnswer || watched?.state === "landed" ? await readFloatTransferState(record, fetchImpl) : void 0;
|
|
97256
|
+
for (let read = 0; known?.state === "landed" && read < LANDED_READS; read += 1) {
|
|
97257
|
+
await (deps.floatStatusSleep ?? sleep6)(LANDED_POLL_MS);
|
|
97258
|
+
known = await readFloatTransferState(record, fetchImpl) ?? known;
|
|
97259
|
+
}
|
|
97247
97260
|
if (known?.state === "final") {
|
|
97248
97261
|
record = {
|
|
97249
97262
|
...record,
|
|
@@ -97269,7 +97282,7 @@ async function payViaFloat({
|
|
|
97269
97282
|
state: "refund_pending",
|
|
97270
97283
|
sellerStatus: response.status,
|
|
97271
97284
|
refundPendingAtMs: record.refundPendingAtMs ?? Date.now(),
|
|
97272
|
-
...known?.state !== "dead" && (lostAnswer || earlierAmbiguous === true) ? { ambiguous: true } : {}
|
|
97285
|
+
...known?.state !== "dead" && (lostAnswer || earlierAmbiguous === true || known?.state === "landed") ? { ambiguous: true } : {}
|
|
97273
97286
|
};
|
|
97274
97287
|
store.writeWal(walName2, record);
|
|
97275
97288
|
fail18("p03_float_refund_pending", {
|
|
@@ -97553,7 +97566,7 @@ function paidResult(record, replayed) {
|
|
|
97553
97566
|
float: { floatId: record.floatId }
|
|
97554
97567
|
};
|
|
97555
97568
|
}
|
|
97556
|
-
var import_web3109, FLOAT_PAYMENT_SCHEMA, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, FLOAT_MEMO_PATH, FLOAT_STATUS_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, REFUND_OUTCOME_NEWS_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, FLOAT_STATUS_START_MS, FLOAT_STATUS_POLL_MS, FLOAT_STATUS_TIMEOUT_MS, FLOAT_TRANSFER_DEAD, CONFIG_TIMEOUT_MS, MEMO_UPLOAD_TIMEOUT_MS, CONFIG_CACHE_MS, FLOAT_POOL_WARM_MS, DECIMAL3, POSITIVE, PENDING_CODES, FLOAT_FRONT_HEADER, hash8, fail18, FEE_PAYER_RESENDS, FEE_PAYER_RESEND_MS, GLOBAL_DISPATCHER4, sortedJson, MODEL_ID, floatPaymentWalName, floatRecordName, FLOAT_KEEP_BYTES_MS, memoryAnswers, MEMORY_ANSWERS_MAX, memoryAnswerKey, requestHashes, FLOAT_KEEP_OPEN_REQUEST_MS, configCache, configGeneration, configReadSequence, configReads, configRefreshes, SPECULATED_MAX_AGE_MS, speculatedQuotes, poolDomains, FLOAT_FACTS_SCHEMA, floatFacts, floatFactsKey, floatFactsMac, poolDomainReads, poolDomainWarmFailedAt, WARM_RETRY_MS, payerGenerator, etaEntry, loadPayerGenerator, plainText, BROADCAST_REFUSED, BROADCAST_REFUSED_STANDARD, BROADCAST_DEAD, TRANSFER_LANDING_WINDOW_MS, PROOF_DELIVERY_MS, SELF_BROADCAST_LEG1_LEFT_MS, BROADCAST_UNSIGNED, BROADCAST_NO_LOOKUP, BROADCAST_LANDED, BROADCAST_LIMIT_MS, BROADCAST_QUOTE_MARGIN_MS, SELF_BROADCAST_RESEND_MS, SELF_BROADCAST_UNANSWERED_RESEND_MS, SELLER_HEADERS_MAX_COUNT, SELLER_HEADERS_MAX_BYTES, SELLER_HEADERS_KEPT;
|
|
97569
|
+
var import_web3109, FLOAT_PAYMENT_SCHEMA, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, FLOAT_MEMO_PATH, FLOAT_STATUS_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, REFUND_OUTCOME_NEWS_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, FLOAT_STATUS_START_MS, FLOAT_STATUS_POLL_MS, FLOAT_STATUS_TIMEOUT_MS, FLOAT_TRANSFER_DEAD, LANDED_POLL_MS, LANDED_READS, CONFIG_TIMEOUT_MS, MEMO_UPLOAD_TIMEOUT_MS, CONFIG_CACHE_MS, FLOAT_POOL_WARM_MS, DECIMAL3, POSITIVE, PENDING_CODES, FLOAT_FRONT_HEADER, hash8, fail18, FEE_PAYER_RESENDS, FEE_PAYER_RESEND_MS, GLOBAL_DISPATCHER4, sortedJson, MODEL_ID, floatPaymentWalName, floatRecordName, FLOAT_KEEP_BYTES_MS, memoryAnswers, MEMORY_ANSWERS_MAX, memoryAnswerKey, requestHashes, FLOAT_KEEP_OPEN_REQUEST_MS, configCache, configGeneration, configReadSequence, configReads, configRefreshes, SPECULATED_MAX_AGE_MS, speculatedQuotes, poolDomains, FLOAT_FACTS_SCHEMA, floatFacts, floatFactsKey, floatFactsMac, poolDomainReads, poolDomainWarmFailedAt, WARM_RETRY_MS, payerGenerator, etaEntry, loadPayerGenerator, plainText, BROADCAST_REFUSED, BROADCAST_REFUSED_STANDARD, BROADCAST_DEAD, TRANSFER_LANDING_WINDOW_MS, PROOF_DELIVERY_MS, SELF_BROADCAST_LEG1_LEFT_MS, BROADCAST_UNSIGNED, BROADCAST_NO_LOOKUP, BROADCAST_LANDED, BROADCAST_LIMIT_MS, BROADCAST_QUOTE_MARGIN_MS, SELF_BROADCAST_RESEND_MS, SELF_BROADCAST_UNANSWERED_RESEND_MS, SELLER_HEADERS_MAX_COUNT, SELLER_HEADERS_MAX_BYTES, SELLER_HEADERS_KEPT;
|
|
97557
97570
|
var init_floatPay = __esm({
|
|
97558
97571
|
"wallet/src/pay/floatPay.mjs"() {
|
|
97559
97572
|
"use strict";
|
|
@@ -97593,6 +97606,8 @@ var init_floatPay = __esm({
|
|
|
97593
97606
|
FLOAT_STATUS_POLL_MS = 5e3;
|
|
97594
97607
|
FLOAT_STATUS_TIMEOUT_MS = 5e3;
|
|
97595
97608
|
FLOAT_TRANSFER_DEAD = /* @__PURE__ */ new Set(["dead", "refund_queued", "refund_sent", "refunded"]);
|
|
97609
|
+
LANDED_POLL_MS = 2e3;
|
|
97610
|
+
LANDED_READS = 15;
|
|
97596
97611
|
CONFIG_TIMEOUT_MS = 3e4;
|
|
97597
97612
|
MEMO_UPLOAD_TIMEOUT_MS = 1e4;
|
|
97598
97613
|
CONFIG_CACHE_MS = 6e4;
|
|
@@ -110009,15 +110024,16 @@ function refreshErrorWords(code2, lockedBy) {
|
|
|
110009
110024
|
return void 0;
|
|
110010
110025
|
}
|
|
110011
110026
|
function balanceSay(a) {
|
|
110012
|
-
const line = balanceLine(a);
|
|
110013
110027
|
const settled = ATOMIC3.test(String(a.balanceAtomic ?? "")) && a.refreshing !== true && !a.refreshError && !positive(usdcText(a.landingAtomic));
|
|
110014
110028
|
const card = settled && typeof a.card === "string" && a.card ? " The wallet card shows the rest." : "";
|
|
110015
110029
|
const fits = (text3) => words(text3) <= SAY_MAX_WORDS;
|
|
110016
110030
|
const count2 = Array.isArray(a.ownKeyTokens) ? a.ownKeyTokens.length + (Number.isSafeInteger(a.ownKeyTokensMore) ? a.ownKeyTokensMore : 0) : 0;
|
|
110017
|
-
const
|
|
110031
|
+
const tokens = count2 < 1 ? "" : ` The wallet's own key holds ${count2} other token${count2 === 1 ? "" : "s"} (ownKeyTokens).`;
|
|
110032
|
+
const line = balanceLine(a);
|
|
110033
|
+
const withTokens = `${balanceLine(a, SAY_MAX_WORDS - words(tokens))}${tokens}`;
|
|
110018
110034
|
return [`${withTokens}${card}`, withTokens, `${line}${card}`].find(fits) ?? line;
|
|
110019
110035
|
}
|
|
110020
|
-
function balanceLine(a) {
|
|
110036
|
+
function balanceLine(a, room = SAY_MAX_WORDS) {
|
|
110021
110037
|
const balance = usdcText(a.balanceAtomic);
|
|
110022
110038
|
const pub = usdcText(a.unshieldedAtomic);
|
|
110023
110039
|
const landing = usdcText(a.landingAtomic);
|
|
@@ -110072,6 +110088,13 @@ function balanceLine(a) {
|
|
|
110072
110088
|
const held = heldByPaymentSay(a.detail);
|
|
110073
110089
|
if (balance === "0" && held) return held;
|
|
110074
110090
|
if (balance === "0" && !pubPart) return "Your private balance is empty. Use wallet_add_funds to get an address to send USDC to.";
|
|
110091
|
+
const parts = Number.isSafeInteger(a.balanceParts) && a.balanceParts > 1 && !a.sendWaiting ? a.balanceParts : 0;
|
|
110092
|
+
if (parts) {
|
|
110093
|
+
const minutes = Number.isSafeInteger(a.joinMinutes) && a.joinMinutes > 0 ? minutesText(a.joinMinutes) : "";
|
|
110094
|
+
const forms = minutes ? [`sending all of it first joins them, about ${minutes}`, `sending all of it takes about ${minutes}`] : ["sending all of it first joins them"];
|
|
110095
|
+
const split5 = forms.map((form) => have(` (in ${parts} parts: ${form}).${pubPart}`)).find((text3) => words(text3) <= room && (!positive(refund) || /\brefund/.test(text3)));
|
|
110096
|
+
if (split5) return split5;
|
|
110097
|
+
}
|
|
110075
110098
|
return have(`.${pubPart}`);
|
|
110076
110099
|
}
|
|
110077
110100
|
function previewReason(a, npmVersion) {
|
|
@@ -110212,12 +110235,14 @@ function build(a, tool, npmVersion) {
|
|
|
110212
110235
|
const amount = usdcText(a.done === true ? a.sentAtomic ?? a.amountAtomic : a.amountAtomic);
|
|
110213
110236
|
const what = amount ? `${amount} USDC` : "the USDC";
|
|
110214
110237
|
if (a.done !== true) return `Sending ${what} privately to ${a.to}.`;
|
|
110238
|
+
const took = Number.isSafeInteger(a.joinedParts) && a.joinedParts > 1 && Number.isSafeInteger(a.tookSeconds) && a.tookSeconds >= 0 ? ` It took about ${a.tookSeconds} s because the wallet first joined your balance's ${a.joinedParts} parts.` : "";
|
|
110215
110239
|
if (typeof a.exitTransaction === "string" && a.privateSend === void 0) {
|
|
110216
110240
|
const head = `Sent ${what} to ${a.to} (transaction ${a.exitTransaction}).`;
|
|
110241
|
+
if (took) return withBalance(`${head}${took}`, a);
|
|
110217
110242
|
const known = withBalance(head, a, (balance) => ` Your private balance is now ${balance} USDC; nothing to do.`);
|
|
110218
110243
|
return known !== head ? known : `${head} Your private balance shows it in a few seconds; nothing to do.`;
|
|
110219
110244
|
}
|
|
110220
|
-
return withBalance(`Sent ${what} to ${a.to}
|
|
110245
|
+
return withBalance(`Sent ${what} to ${a.to}.${took}`, a);
|
|
110221
110246
|
}
|
|
110222
110247
|
if (typeof a.address === "string" && a.address && typeof a.stage === "string") return linkSay(a);
|
|
110223
110248
|
if (a.stage === "waiting_for_funds" && typeof a.depositAddress === "string") {
|
|
@@ -120879,7 +120904,7 @@ function proofWalkReportOf(store, programId) {
|
|
|
120879
120904
|
if (movedInAfterSnapshot(store, store.state, snapshot) > 0n) return null;
|
|
120880
120905
|
const report = computeBalance({ store, snapshotRead: { snapshot }, source: "local" });
|
|
120881
120906
|
const one = oneBalance(report, store, snapshot);
|
|
120882
|
-
return one.balanceAtomic === null ? null : { report, one };
|
|
120907
|
+
return one.balanceAtomic === null ? null : { report, one, snapshot };
|
|
120883
120908
|
} catch {
|
|
120884
120909
|
return null;
|
|
120885
120910
|
}
|
|
@@ -121326,6 +121351,18 @@ function createWalletTools({
|
|
|
121326
121351
|
if (storeWriteCount() !== writesBefore) lastWriterEndedAt = Date.now();
|
|
121327
121352
|
}
|
|
121328
121353
|
});
|
|
121354
|
+
async function assertNotPromisedFresh(store, amountAtomic) {
|
|
121355
|
+
const shortfall = promisedShortfall(store, amountAtomic);
|
|
121356
|
+
if (shortfall?.error === "spend_private_send_unknown" && shortfall.pendingAtomic !== null) {
|
|
121357
|
+
try {
|
|
121358
|
+
await (ops.refresh ?? refreshWallet)({ session, store });
|
|
121359
|
+
log("MCP_PROMISED_REFRESH ok");
|
|
121360
|
+
} catch (error) {
|
|
121361
|
+
log(`MCP_PROMISED_REFRESH failed ${errorCode2(error, "mcp_refresh_failed")}`);
|
|
121362
|
+
}
|
|
121363
|
+
}
|
|
121364
|
+
assertUnshieldNotPromised(store, amountAtomic);
|
|
121365
|
+
}
|
|
121329
121366
|
const autoPockets = createAutoPockets({
|
|
121330
121367
|
session,
|
|
121331
121368
|
money,
|
|
@@ -122712,7 +122749,7 @@ ${canonical5}`;
|
|
|
122712
122749
|
const balance = {
|
|
122713
122750
|
name: "wallet_balance",
|
|
122714
122751
|
title: "Balance",
|
|
122715
|
-
description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is: at most 8 lines with the private balance, the public USDC, Tor on or off, the last 3 payments (time UTC, seller host, USDC), and example requests. balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet joins its own balance parts and the number counts them; pool_move: value from the old pool arrives and the number counts it; first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. landingAtomic (only when not 0): USDC you added whose deposit landed on chain and that balanceAtomic does not count yet; this call finishes it and waits about 20 s for it, past that it is being counted: call wallet_balance again as next says (at once in the same turn when this call waited its 20 s, else in 30 s). session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
|
|
122752
|
+
description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is: at most 8 lines with the private balance, the public USDC, Tor on or off, the last 3 payments (time UTC, seller host, USDC), and example requests. balanceParts (only above 1): the balance is in that many parts, and a send of all of it joins them first (next says the minutes). balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet joins its own balance parts and the number counts them; pool_move: value from the old pool arrives and the number counts it; first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. landingAtomic (only when not 0): USDC you added whose deposit landed on chain and that balanceAtomic does not count yet; this call finishes it and waits about 20 s for it, past that it is being counted: call wallet_balance again as next says (at once in the same turn when this call waited its 20 s, else in 30 s). session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
|
|
122716
122753
|
inputSchema: { type: "object", properties: {
|
|
122717
122754
|
verbose: { type: "boolean", description: "Optional. true adds `internal`: the engineering numbers (funded accounts, notes, payments without waiting, a 0 set-aside value). Default false." }
|
|
122718
122755
|
}, additionalProperties: false },
|
|
@@ -122963,8 +123000,7 @@ ${canonical5}`;
|
|
|
122963
123000
|
else if (unshielded.unshieldedAtomic && unshielded.unshieldedAtomic !== "0" && shieldEnabled) next = 'to make the public USDC private, call wallet_shield with amount (like "0.37")';
|
|
122964
123001
|
else if (one.balanceAtomic === "0" && shieldEnabled) {
|
|
122965
123002
|
next = elsewhereEnabled ? `the shielded balance is 0, so x402_pay cannot pay yet. To add funds, call wallet_add_funds: it gives a one-time address; up to ${usdcText2(payMeRoundMax())} USDC sent there on Solana becomes private a few minutes after it arrives while this MCP server runs (or call wallet_add_funds with its request_id to finish it at once)` : `the shielded balance is 0, so x402_pay cannot pay yet. To add funds: send USDC on Solana to ownAddress (this wallet's own key), then call wallet_shield with amount (any amount from ${usdcText2(10000n)} to ${usdcText2(MAX_SHIELDED_BALANCE_ATOMIC)} USDC)`;
|
|
122966
|
-
} else
|
|
122967
|
-
else next = "nothing to do; x402_pay pays from balanceAtomic, wallet_unshield sends from breakdown.shieldedAtomic";
|
|
123003
|
+
} else next = "nothing to do; x402_pay pays from balanceAtomic, wallet_unshield sends from breakdown.shieldedAtomic";
|
|
122968
123004
|
let landingAtomic = 0n;
|
|
122969
123005
|
try {
|
|
122970
123006
|
landingAtomic = landedNotCounted(store);
|
|
@@ -123073,6 +123109,10 @@ ${canonical5}`;
|
|
|
123073
123109
|
if (sendWaiting) {
|
|
123074
123110
|
next = `${next}. Tell the user: the send of ${usdcText2(sendWaiting.amountAtomic)} USDC to ${sendWaiting.to.slice(0, 4)}...${sendWaiting.to.slice(-4)} (request_id "${sendWaiting.requestId}") ${sendWaiting.state === "sending" ? "is being sent now" : sendWaiting.state === "retrying" ? retryWords(sendWaiting) : `waits for the join of the balance parts: ${joinAsksText(waitingPlan)}`}; it cannot be cancelled`;
|
|
123075
123111
|
}
|
|
123112
|
+
const balanceParts = parts?.parts > 1 && ATOMIC_TEXT.test(String(one.balanceAtomic ?? "")) ? parts.parts : void 0;
|
|
123113
|
+
if (balanceParts && !sendWaiting) {
|
|
123114
|
+
next = `${next}. The balance is in ${balanceParts} parts: if the user asks to send all of it, tell them first that it takes about ${minutesText2(parts.minutes)}`;
|
|
123115
|
+
}
|
|
123076
123116
|
if (earlierDeposits.length) next = `${next}. Tell the user each earlierDeposits notice`;
|
|
123077
123117
|
const notices = takeRefundNotices();
|
|
123078
123118
|
if (notices.refundNotices) next = `${next}. Tell the user each refundNotices line`;
|
|
@@ -123136,6 +123176,8 @@ ${canonical5}`;
|
|
|
123136
123176
|
balanceAtomic: one.balanceAtomic,
|
|
123137
123177
|
balance: one.balance,
|
|
123138
123178
|
card: walletCard({ one, unshielded, landingAtomic, parts, store }),
|
|
123179
|
+
// joinMinutes: the card's minutes, so say.mjs names them too.
|
|
123180
|
+
...balanceParts ? { balanceParts, ...Number.isSafeInteger(parts.minutes) ? { joinMinutes: parts.minutes } : {} } : {},
|
|
123139
123181
|
...report.detail ? { detail: detailPublic } : {},
|
|
123140
123182
|
breakdown,
|
|
123141
123183
|
...refresh,
|
|
@@ -123206,12 +123248,13 @@ ${canonical5}`;
|
|
|
123206
123248
|
continue;
|
|
123207
123249
|
}
|
|
123208
123250
|
if (record?.state === "submitted") {
|
|
123209
|
-
const
|
|
123210
|
-
if (
|
|
123251
|
+
const age2 = Number.isSafeInteger(record.submittedAtMs) ? Date.now() - record.submittedAtMs : NaN;
|
|
123252
|
+
if (age2 > SUBMITTED_READ_AFTER_MS && age2 < 2 * STALE_REFUND_MS) names.push(name);
|
|
123211
123253
|
continue;
|
|
123212
123254
|
}
|
|
123213
123255
|
if (record?.state !== "refund_pending" || record.refundOutcome === "refund_arrived" || !Number.isSafeInteger(record.refundPendingAtMs)) continue;
|
|
123214
|
-
|
|
123256
|
+
const age = Date.now() - record.refundPendingAtMs;
|
|
123257
|
+
if (age > STALE_REFUND_MS || record.ambiguous === true && age > SUBMITTED_READ_AFTER_MS) names.push(name);
|
|
123215
123258
|
}
|
|
123216
123259
|
return names;
|
|
123217
123260
|
}
|
|
@@ -123280,8 +123323,9 @@ ${canonical5}`;
|
|
|
123280
123323
|
const { ambiguous: _settled, ...rest } = record;
|
|
123281
123324
|
store.writeWal(name, { ...rest, state: "refund_pending", refundPendingAtMs: Date.now() });
|
|
123282
123325
|
} else {
|
|
123326
|
+
const { ambiguous: _known, ...record_ } = record;
|
|
123283
123327
|
store.writeWal(name, {
|
|
123284
|
-
...
|
|
123328
|
+
...record_,
|
|
123285
123329
|
state: "seller_kept",
|
|
123286
123330
|
sellerKeptAtMs: record.sellerKeptAtMs ?? Date.now(),
|
|
123287
123331
|
.../^[1-9A-HJ-NP-Za-km-z]{64,90}$/.test(String(transaction ?? "")) ? { landedTransaction: transaction } : {}
|
|
@@ -123291,6 +123335,12 @@ ${canonical5}`;
|
|
|
123291
123335
|
}
|
|
123292
123336
|
return written;
|
|
123293
123337
|
}
|
|
123338
|
+
const refundStateWords = (floatState) => ({
|
|
123339
|
+
dead: "sure: the transfer to the seller expired, so Shinjuku sends the refund",
|
|
123340
|
+
refund_queued: "sure: Shinjuku is sending the refund",
|
|
123341
|
+
refund_sent: "sent by Shinjuku (refundTransaction)",
|
|
123342
|
+
refunded: "sent by Shinjuku (refundTransaction)"
|
|
123343
|
+
})[floatState] ?? floatState;
|
|
123294
123344
|
function paymentRows(store, names, refundReads = /* @__PURE__ */ new Map()) {
|
|
123295
123345
|
const floats = names.filter((name) => name.startsWith(FLOAT_PAYMENT_WAL_PREFIX)).map((name) => ({ name, record: store.readWal(name) }));
|
|
123296
123346
|
const ENDED = /* @__PURE__ */ new Set(["receipt", "refund_pending", "seller_kept", "submitted"]);
|
|
@@ -123300,7 +123350,7 @@ ${canonical5}`;
|
|
|
123300
123350
|
const refundHeld = (record) => {
|
|
123301
123351
|
if (walk === void 0) {
|
|
123302
123352
|
try {
|
|
123303
|
-
walk = readBalanceSnapshot(store).snapshot ?? null;
|
|
123353
|
+
walk = proofWalkReportOf(store, session.programId)?.snapshot ?? readBalanceSnapshot(store).snapshot ?? null;
|
|
123304
123354
|
} catch {
|
|
123305
123355
|
walk = null;
|
|
123306
123356
|
}
|
|
@@ -123359,7 +123409,7 @@ ${canonical5}`;
|
|
|
123359
123409
|
refundAtomic: paid,
|
|
123360
123410
|
refund: held ? "arrived" : "on its way",
|
|
123361
123411
|
...FLOAT_ID.test(String(record.floatId)) ? { floatId: record.floatId } : {},
|
|
123362
|
-
refundState: shinjuku.floatState,
|
|
123412
|
+
refundState: refundStateWords(shinjuku.floatState),
|
|
123363
123413
|
...shinjuku.refundTransaction ? { refundTransaction: shinjuku.refundTransaction } : {},
|
|
123364
123414
|
note: held ? "the seller did not take this payment; the whole amount came back to the private balance" : sent ? "the seller did not take this payment; Shinjuku sent the refund (refundTransaction), and the private balance counts it at its next read" : "the seller did not take this payment; the whole amount comes back to the private balance by itself"
|
|
123365
123415
|
} : { note: landed ? `the transfer to the seller landed, but the seller has not served the request yet; ${ask} to get it` : `the seller has not served this request yet; ${ask}. If the seller never takes the payment, Shinjuku sends the whole amount back by itself in about ${Math.ceil(FLOAT_REFUND_ETA_SECONDS / 60)} minutes` }
|
|
@@ -123380,7 +123430,7 @@ ${canonical5}`;
|
|
|
123380
123430
|
refundAtomic: paid,
|
|
123381
123431
|
refund: arrived ? "arrived" : notSeen ? "not seen" : "on its way",
|
|
123382
123432
|
...FLOAT_ID.test(String(record.floatId)) ? { floatId: record.floatId } : {},
|
|
123383
|
-
...shinjuku?.floatState ? { refundState: shinjuku.floatState } : {},
|
|
123433
|
+
...shinjuku?.floatState ? { refundState: refundStateWords(shinjuku.floatState) } : {},
|
|
123384
123434
|
...shinjuku?.refundTransaction ? { refundTransaction: shinjuku.refundTransaction } : {},
|
|
123385
123435
|
note: arrived ? "the seller did not take this payment; the whole amount came back to the private balance" : notSeen ? "the seller did not take this payment; its refund is not seen in the balance (the seller may have kept it)" : sent ? "the seller did not take this payment; Shinjuku sent the refund (refundTransaction), and the private balance counts it at its next read" : "the seller did not take this payment; the whole amount comes back to the private balance by itself"
|
|
123386
123436
|
};
|
|
@@ -123629,7 +123679,7 @@ ${canonical5}`;
|
|
|
123629
123679
|
const receipts = {
|
|
123630
123680
|
name: "wallet_receipts",
|
|
123631
123681
|
title: "List recent receipts and wallet moves",
|
|
123632
|
-
description: `List this wallet's most recent receipts, newest first. Payments (kind x402 or inference): seller (the URL paid), sellerHost, item (what was bought), priceAtomic, feeAtomic, amountAtomic (the total), sellerTransaction, privateTransaction (this wallet's private payment), facilitator, answeredAt, and transaction / host (the seller's when known). pay.shinjukustaition.com is Shinjuku, the private payment facilitator, never the seller. A payment the seller did not take shows state refund_pending (the refund is on its way; floatId,
|
|
123682
|
+
description: `List this wallet's most recent receipts, newest first. Payments (kind x402 or inference): seller (the URL paid), sellerHost, item (what was bought), priceAtomic, feeAtomic, amountAtomic (the total), sellerTransaction, privateTransaction (this wallet's private payment), facilitator, answeredAt, and transaction / host (the seller's when known). pay.shinjukustaition.com is Shinjuku, the private payment facilitator, never the seller. A payment the seller did not take shows state refund_pending (the refund is on its way; floatId, refundState (Shinjuku's refund step in words) and refundTransaction from Shinjuku) until the balance holds it, then refunded. A payment the seller has not served yet shows waiting_for_seller (or seller_paid_not_served once the transfer to the seller landed): call x402_pay again with its requestId to get it. The wallet's own moves (kind shield, add-funds, unshield, private-send, token-transfer, or an older deposit kind): requestId, amountAtomic, state, transaction, at; a private-send row with parts still to go shows partsAt (each part's time and state) and next (it is not stuck); a token-transfer row (wallet_transfer_token) shows tokenMint, tokenAmountAtomic, usdcAtomic (what the sell gave) and sentAtomic; an add-funds row also shows heldAtomic, the USDC waiting at its address (it becomes private by itself after a random privacy wait: never close an address that holds USDC). An add-funds row with via "cli" was made by the shinjuku add-funds command in a terminal: fundsId, route, amountAtomic, state (done or not_finished), transaction; it has no requestId, and wallet_add_funds never continues it. requestId is shown for payments made by this MCP process; older ones show requestKey (its SHA-256). kind "payments" or "moves" lists one group only.`,
|
|
123633
123683
|
inputSchema: {
|
|
123634
123684
|
type: "object",
|
|
123635
123685
|
properties: {
|
|
@@ -124259,7 +124309,9 @@ ${canonical5}`;
|
|
|
124259
124309
|
else if (POOL_LAYOUT_CODE.test(code2)) next = `${POOL_LAYOUT_UPDATE}. Then ${again(kind, job)}: it continues where it stopped and never moves the money twice. If the latest release says the same, tell your owner: ${code2}`;
|
|
124260
124310
|
else if (code2 === "mcp_unshield_to_not_allowed") next = unboundNext(job.requestId, job.amountAtomic);
|
|
124261
124311
|
else if (code2 === "mcp_unshield_to_invalid") next = UNSHIELD_TO_INVALID_NEXT;
|
|
124262
|
-
else if (kind === "unshield" &&
|
|
124312
|
+
else if (kind === "unshield" && code2 === "spend_private_send_unknown") {
|
|
124313
|
+
next = `Nothing was sent. The wallet is finishing a part of its private send, so the value free beside it is not known yet. In about a minute, ${again(kind, job)}`;
|
|
124314
|
+
} else if (kind === "unshield" && code2 === "spend_private_send_reserved") next = PROMISED_NEXT;
|
|
124263
124315
|
else if (kind === "unshield" && /send_part_id_taken/.test(code2)) {
|
|
124264
124316
|
next = `Nothing was sent. A part id of this private send (mp<n>-${job.requestId}) already names another unshield of this wallet: call wallet_unshield with a NEW request_id`;
|
|
124265
124317
|
} else if (kind === "unshield" && /accounts_split_merge_first/.test(line)) {
|
|
@@ -124622,8 +124674,21 @@ ${canonical5}`;
|
|
|
124622
124674
|
}
|
|
124623
124675
|
};
|
|
124624
124676
|
let told;
|
|
124677
|
+
let joinedParts;
|
|
124678
|
+
let joinSeen = false;
|
|
124679
|
+
let startLogged = false;
|
|
124680
|
+
let waitIntent;
|
|
124681
|
+
try {
|
|
124682
|
+
[waitIntent] = openJoinIntents(session.openStore(), requestId);
|
|
124683
|
+
} catch {
|
|
124684
|
+
waitIntent = void 0;
|
|
124685
|
+
}
|
|
124686
|
+
if (waitIntent) {
|
|
124687
|
+
joinSeen = true;
|
|
124688
|
+
if (Number.isSafeInteger(waitIntent.parts) && waitIntent.parts > 1) joinedParts = waitIntent.parts;
|
|
124689
|
+
}
|
|
124625
124690
|
joinWaitingCalls += 1;
|
|
124626
|
-
line("wait");
|
|
124691
|
+
line("wait", Number.isSafeInteger(waitIntent?.joinAtMs) ? ` startInMs=${Math.max(0, waitIntent.joinAtMs - Date.now())}` : "");
|
|
124627
124692
|
try {
|
|
124628
124693
|
for (; ; ) {
|
|
124629
124694
|
const job2 = jobs.get(key);
|
|
@@ -124640,9 +124705,9 @@ ${canonical5}`;
|
|
|
124640
124705
|
line("final", Number.isSafeInteger(finalAtMs) ? ` finalMs=${finalAtMs - startedAt}` : "");
|
|
124641
124706
|
}
|
|
124642
124707
|
line("send");
|
|
124643
|
-
await Promise.race([statusWait([job2.promise], Math.max(0, until - Date.now()), progress3, "sending privately").catch(() => {
|
|
124708
|
+
await Promise.race([statusWait([job2.answered ?? job2.promise], Math.max(0, until - Date.now()), progress3, "sending privately").catch(() => {
|
|
124644
124709
|
}), stopped]);
|
|
124645
|
-
line(job2.state === "done" ? "done" : job2.state === "running" ? signal?.aborted ? "cancel" : "cap" : "ended");
|
|
124710
|
+
line(job2.answeredEarly ? "answered" : job2.state === "done" ? "done" : job2.state === "running" ? signal?.aborted ? "cancel" : "cap" : "ended");
|
|
124646
124711
|
break;
|
|
124647
124712
|
}
|
|
124648
124713
|
if (job2.state !== "failed" || job2.error?.code !== "unshield_joining_accounts") break;
|
|
@@ -124665,6 +124730,8 @@ ${canonical5}`;
|
|
|
124665
124730
|
intent = void 0;
|
|
124666
124731
|
}
|
|
124667
124732
|
if (!intent) break;
|
|
124733
|
+
joinSeen = true;
|
|
124734
|
+
if (Number.isSafeInteger(intent.parts) && intent.parts > 1) joinedParts = intent.parts;
|
|
124668
124735
|
const phase = Number.isSafeInteger(intent.joinFinalAtMs) ? "final" : Number.isSafeInteger(intent.joinSentAtMs) ? "join" : void 0;
|
|
124669
124736
|
if (phase && phase !== told) {
|
|
124670
124737
|
told = phase;
|
|
@@ -124675,6 +124742,10 @@ ${canonical5}`;
|
|
|
124675
124742
|
const about = `about ${Math.max(1, left.minutes + (left.asks === 2 ? left.thenMinutes : 0))} min`;
|
|
124676
124743
|
tell(intent.joinAtMs > Date.now() ? `joining your balance's ${parts} first (${about}): the join starts in about ${Math.ceil((intent.joinAtMs - Date.now()) / 1e3)} s (a random wait), then the send` : phase === "join" ? `joining your balance's ${parts} first (${about}): the join is sent; the send follows once it is final` : `joining your balance's ${parts} first (${about}), then the send`);
|
|
124677
124744
|
if (intent.joinAtMs <= Date.now()) {
|
|
124745
|
+
if (!startLogged && phase === void 0) {
|
|
124746
|
+
startLogged = true;
|
|
124747
|
+
line("start");
|
|
124748
|
+
}
|
|
124678
124749
|
const result = await joiner.tick();
|
|
124679
124750
|
if (result?.state === "failed" || result?.skipped === "failed_recently") {
|
|
124680
124751
|
const last = joiner.status().last ?? result;
|
|
@@ -124705,12 +124776,29 @@ ${canonical5}`;
|
|
|
124705
124776
|
joinWaitingCalls -= 1;
|
|
124706
124777
|
}
|
|
124707
124778
|
const job = jobs.get(key);
|
|
124708
|
-
|
|
124709
|
-
|
|
124710
|
-
|
|
124711
|
-
|
|
124712
|
-
|
|
124713
|
-
|
|
124779
|
+
if (!job) {
|
|
124780
|
+
return refuse12(
|
|
124781
|
+
"mcp_unshield_failed",
|
|
124782
|
+
"the send was not found in this process; nothing new was sent by this call",
|
|
124783
|
+
`call wallet_unshield again with request_id "${requestId}" for its state`,
|
|
124784
|
+
{ requestId }
|
|
124785
|
+
);
|
|
124786
|
+
}
|
|
124787
|
+
const answer = jobAnswer("unshield", job);
|
|
124788
|
+
if (job.state !== "done" || !joinSeen) return answer;
|
|
124789
|
+
let done;
|
|
124790
|
+
try {
|
|
124791
|
+
done = JSON.parse(answer.text);
|
|
124792
|
+
} catch {
|
|
124793
|
+
return answer;
|
|
124794
|
+
}
|
|
124795
|
+
const tookSeconds = Math.round((Date.now() - startedAt) / 1e3);
|
|
124796
|
+
return json3({
|
|
124797
|
+
...done,
|
|
124798
|
+
...joinedParts ? { joinedParts } : {},
|
|
124799
|
+
tookSeconds,
|
|
124800
|
+
next: `${done.next}. Tell the user it took about ${tookSeconds} s because the wallet first joined the balance's ${joinedParts ? `${joinedParts} ` : ""}parts (a private move inside the wallet, no fee)`
|
|
124801
|
+
});
|
|
124714
124802
|
}
|
|
124715
124803
|
function joinFailedAnswer(job, last) {
|
|
124716
124804
|
const code2 = typeof last?.code === "string" && last.code ? last.code : "wallet_join_failed";
|
|
@@ -125151,9 +125239,9 @@ ${canonical5}`;
|
|
|
125151
125239
|
try {
|
|
125152
125240
|
shortfall = promisedShortfall(session.openStore(), request.amountAtomic);
|
|
125153
125241
|
} catch {
|
|
125154
|
-
shortfall = { error: "spend_private_send_unknown" };
|
|
125242
|
+
shortfall = { error: "spend_private_send_unknown", pendingAtomic: null };
|
|
125155
125243
|
}
|
|
125156
|
-
if (shortfall) {
|
|
125244
|
+
if (shortfall && !(shortfall.error === "spend_private_send_unknown" && shortfall.pendingAtomic !== null)) {
|
|
125157
125245
|
return refuse12(
|
|
125158
125246
|
shortfall.error,
|
|
125159
125247
|
promisedShortfallText(shortfall, request.amountAtomic).replace(/^[a-z_]+: /, ""),
|
|
@@ -125538,8 +125626,8 @@ ${canonical5}`;
|
|
|
125538
125626
|
}
|
|
125539
125627
|
function runUnshieldJob(job, onStage) {
|
|
125540
125628
|
if (isSplitSend(job)) return runPrivateSendJob(job, onStage);
|
|
125541
|
-
return underLease((opened) => {
|
|
125542
|
-
if (!opened.readWal(`self-pay-${moneyId(job.requestId)}`))
|
|
125629
|
+
return underLease(async (opened) => {
|
|
125630
|
+
if (!opened.readWal(`self-pay-${moneyId(job.requestId)}`)) await assertNotPromisedFresh(opened, job.amountAtomic);
|
|
125543
125631
|
return unshieldPart(opened, job, moneyId(job.requestId), job.amountAtomic, onStage, (answer) => answerUnshieldEarly(job, answer));
|
|
125544
125632
|
});
|
|
125545
125633
|
}
|
|
@@ -125560,9 +125648,9 @@ ${canonical5}`;
|
|
|
125560
125648
|
async function runPrivateSendJob(job, onStage) {
|
|
125561
125649
|
const sendId = moneyId(job.requestId);
|
|
125562
125650
|
let store;
|
|
125563
|
-
const plan = await underLease((opened) => {
|
|
125651
|
+
const plan = await underLease(async (opened) => {
|
|
125564
125652
|
store = opened;
|
|
125565
|
-
if (!opened.readWal(privateSendPlanName(sendId)))
|
|
125653
|
+
if (!opened.readWal(privateSendPlanName(sendId))) await assertNotPromisedFresh(opened, job.amountAtomic);
|
|
125566
125654
|
return planPrivateSend({
|
|
125567
125655
|
store,
|
|
125568
125656
|
sendId,
|
|
@@ -126207,7 +126295,7 @@ ${canonical5}`;
|
|
|
126207
126295
|
const unshield = {
|
|
126208
126296
|
name: "wallet_unshield",
|
|
126209
126297
|
title: "Send USDC privately from the shielded balance",
|
|
126210
|
-
description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (leave \`to\` out for the owner's own wallet when one is set): it sends amount (USDC, like "2"; or amount_atomic) from the SHIELDED balance as plain USDC to \`to\`, a public Solana address (the USDC goes to its USDC token account). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. The facilitator pays every network fee; no SOL is needed. The owner's --unshield-to addresses send at once. Any other address is sent only after the user confirms it in the MCP client (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already hold USDC (have a USDC token account): an address without one is refused before any dialog (mcp_unshield_to_invalid). It takes minutes (one wait of up to 5 minutes is normal): the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the balance is in several parts and the wallet joins them first, then sends; the answer says how many more asks and when (joinAsks, joinMinutes), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). One exception: for a single send (no parts, no delay) whose balance is in several parts, when this MCP app sends a progress token with the call, that one call joins the parts and then sends, taking up to ${JOIN_CALL_MAX_MS / 6e4} minutes, with a progress line every ${JOIN_STEP_MS / 1e3} s ("joining your balance's N parts first (about M min)")
|
|
126298
|
+
description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (leave \`to\` out for the owner's own wallet when one is set): it sends amount (USDC, like "2"; or amount_atomic) from the SHIELDED balance as plain USDC to \`to\`, a public Solana address (the USDC goes to its USDC token account). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. The facilitator pays every network fee; no SOL is needed. The owner's --unshield-to addresses send at once. Any other address is sent only after the user confirms it in the MCP client (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already hold USDC (have a USDC token account): an address without one is refused before any dialog (mcp_unshield_to_invalid). It takes minutes (one wait of up to 5 minutes is normal): the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the balance is in several parts and the wallet joins them first, then sends; the answer says how many more asks and when (joinAsks, joinMinutes), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). One exception: for a single send (no parts, no delay) whose balance is in several parts, when this MCP app sends a progress token with the call, that one call joins the parts and then sends, taking up to ${JOIN_CALL_MAX_MS / 6e4} minutes, with a progress line every ${JOIN_STEP_MS / 1e3} s ("joining your balance's N parts first (about M min)"). Some apps show nothing during this call: BEFORE it, when wallet_balance shows balanceParts above 1, tell the user that sending all of it first joins the parts and takes about the minutes wallet_balance names. Without a progress token it answers within about 20 s with stage joining and when to ask again. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. When it lands, the wallet writes an encrypted backup (backupPath). x402_pay calls wait while it runs. An optional owner cap (--max-unshield) may bound each call and what this wallet sends in any 24 hours, from every MCP process (mcp_unshield_cap_exceeded: tell the owner). Other refusals: mcp_argument_invalid, mcp_request_id_reused, mcp_unshield_to_invalid, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. PRIVATE SEND (optional): when the user says "send it in parts", "spread it out", or "do not send it all at once", pass parts (2-8) WITH max_delay_minutes (1-60; parts with no wait are refused: they would leave together): the amount is split into parts exits of unequal size (each at least 0.01 USDC), each sent after a random wait of up to max_delay_minutes (part 1 too), so the chain shows no single exit of this amount at this time. max_delay_minutes alone delays one exit. ` + SEND_RUNS_ON + ` Late parts never go together. While parts are unsent, x402_pay may spend only the rest of the balance. The plan is saved in the wallet before part 1, so the same request_id (with the same parts and max_delay_minutes) continues after a restart and never sends a part twice; the answer's privateSend lists each part (state, amount, when it may go, transactions), sentAtomic, and remainingAtomic. The user confirms the whole send once. Both left out: one exit, as before. Each part is a full unshield, so it takes as long as one, and the facilitator pays the network fee of each. Privacy limit: every part goes to the SAME \`to\`, so anyone who adds up that address's incoming transfers sees the total; the parts hide the time and the single amount of the exit, not the sum the address received. wallet_cancel with the same request_id stops the parts not sent yet (their value stays in the shielded balance); while a part is being sent, that call waits for the part to finish, up to about ${CALL_BUDGET_MS / 1e3} s.`,
|
|
126211
126299
|
inputSchema: {
|
|
126212
126300
|
type: "object",
|
|
126213
126301
|
properties: {
|
|
@@ -126948,12 +127036,12 @@ ${canonical5}`;
|
|
|
126948
127036
|
send: async (plan) => {
|
|
126949
127037
|
const job = { requestId: plan.id, to: plan.to };
|
|
126950
127038
|
let store;
|
|
126951
|
-
const sendPlan = await withLockRetry(() => underLease((opened) => {
|
|
127039
|
+
const sendPlan = await withLockRetry(() => underLease(async (opened) => {
|
|
126952
127040
|
store = opened;
|
|
126953
127041
|
const name = privateSendPlanName(plan.sendId);
|
|
126954
127042
|
if (!opened.readWal(name) && readTransferToken(opened, plan.id)?.state === "cancelled") return null;
|
|
126955
127043
|
const amount = transferSendAtomic(plan, opened);
|
|
126956
|
-
if (!opened.readWal(name))
|
|
127044
|
+
if (!opened.readWal(name)) await assertNotPromisedFresh(opened, amount);
|
|
126957
127045
|
const written = planPrivateSend({
|
|
126958
127046
|
store: opened,
|
|
126959
127047
|
sendId: plan.sendId,
|
|
@@ -130209,69 +130297,6 @@ var init_stdioServer = __esm({
|
|
|
130209
130297
|
}
|
|
130210
130298
|
});
|
|
130211
130299
|
|
|
130212
|
-
// wallet/src/net/torMissingMcp.mjs
|
|
130213
|
-
var torMissingMcp_exports = {};
|
|
130214
|
-
__export(torMissingMcp_exports, {
|
|
130215
|
-
RESTART: () => RESTART,
|
|
130216
|
-
serveTorMissingMcp: () => serveTorMissingMcp
|
|
130217
|
-
});
|
|
130218
|
-
async function serveTorMissingMcp({ message, recheck = async () => false, input = process.stdin, output: output2 = process.stdout, log = () => {
|
|
130219
|
-
} }) {
|
|
130220
|
-
const error = "mcp_tor_unavailable";
|
|
130221
|
-
const match = /^([a-z][a-z0-9_]*): ([\s\S]*)$/.exec(String(message));
|
|
130222
|
-
const cause = match ? match[1] : "tor_not_running";
|
|
130223
|
-
const detail = match ? match[2] : String(message);
|
|
130224
|
-
const answer = async () => {
|
|
130225
|
-
let ready = false;
|
|
130226
|
-
try {
|
|
130227
|
-
ready = await recheck() === true;
|
|
130228
|
-
} catch {
|
|
130229
|
-
ready = false;
|
|
130230
|
-
}
|
|
130231
|
-
const body = ready ? {
|
|
130232
|
-
say: `Tor runs on this machine now, but this wallet started before it did. ${RESTART}.`,
|
|
130233
|
-
ok: false,
|
|
130234
|
-
error: "mcp_restart_needed",
|
|
130235
|
-
next: `Tell the user: ${RESTART}. Nothing was paid.`
|
|
130236
|
-
} : {
|
|
130237
|
-
say: `The wallet is set to run behind Tor, and Tor is not running on this machine, so nothing was sent and nothing was paid.`,
|
|
130238
|
-
ok: false,
|
|
130239
|
-
error,
|
|
130240
|
-
cause,
|
|
130241
|
-
detail,
|
|
130242
|
-
next: `Tell the user what to install, with the command for their OS from detail; when Tor runs: ${RESTART}.`
|
|
130243
|
-
};
|
|
130244
|
-
log(`MCP_TOR_MISSING_ANSWER ${body.error}`);
|
|
130245
|
-
return { text: JSON.stringify(body, null, 2), isError: true };
|
|
130246
|
-
};
|
|
130247
|
-
const hidden = new Set(MCP_HIDDEN_TOOL_NAMES);
|
|
130248
|
-
const tools = [...MCP_TOOL_NAMES, "wallet_status"].map((name) => ({
|
|
130249
|
-
name,
|
|
130250
|
-
title: name,
|
|
130251
|
-
description: `Not available yet: Tor is not running on this machine. Call it for the install command (it moves no money).`,
|
|
130252
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: true },
|
|
130253
|
-
annotations: { readOnlyHint: true },
|
|
130254
|
-
hidden: hidden.has(name),
|
|
130255
|
-
run: answer
|
|
130256
|
-
}));
|
|
130257
|
-
const dispatch = createMcpDispatcher({
|
|
130258
|
-
tools,
|
|
130259
|
-
aliases: MCP_TOOL_ALIASES,
|
|
130260
|
-
instructions: `The Shinjuku Shield wallet is set to run behind Tor, and Tor is not running on this machine. Call wallet_balance: its answer has the install command for the user's OS. ${RESTART} after Tor starts.`
|
|
130261
|
-
});
|
|
130262
|
-
await serveStdio({ input, output: output2, dispatch, log });
|
|
130263
|
-
return 1;
|
|
130264
|
-
}
|
|
130265
|
-
var RESTART;
|
|
130266
|
-
var init_torMissingMcp = __esm({
|
|
130267
|
-
"wallet/src/net/torMissingMcp.mjs"() {
|
|
130268
|
-
"use strict";
|
|
130269
|
-
init_stdioServer();
|
|
130270
|
-
init_tools();
|
|
130271
|
-
RESTART = "Restart this MCP server in your app (or restart the app), then ask again";
|
|
130272
|
-
}
|
|
130273
|
-
});
|
|
130274
|
-
|
|
130275
130300
|
// wallet/src/inference/chat.mjs
|
|
130276
130301
|
var chat_exports = {};
|
|
130277
130302
|
__export(chat_exports, {
|
|
@@ -130281,7 +130306,7 @@ __export(chat_exports, {
|
|
|
130281
130306
|
isProgressLine: () => isProgressLine,
|
|
130282
130307
|
runChat: () => runChat
|
|
130283
130308
|
});
|
|
130284
|
-
import { randomBytes as
|
|
130309
|
+
import { randomBytes as randomBytes27 } from "node:crypto";
|
|
130285
130310
|
import { createInterface } from "node:readline";
|
|
130286
130311
|
function chosenCap(value) {
|
|
130287
130312
|
const text3 = value === void 0 || value === null ? "" : String(value);
|
|
@@ -130561,7 +130586,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
130561
130586
|
if (state.pending) say6("(The unfinished answer is dropped; this is a new question.)");
|
|
130562
130587
|
state.pending = void 0;
|
|
130563
130588
|
await runTurn({
|
|
130564
|
-
requestId: `chat-${
|
|
130589
|
+
requestId: `chat-${randomBytes27(8).toString("hex")}`,
|
|
130565
130590
|
model: state.model,
|
|
130566
130591
|
messages: [...state.history, { role: "user", content: text3 }],
|
|
130567
130592
|
capAtomic: void 0
|
|
@@ -130612,6 +130637,69 @@ var init_chat = __esm({
|
|
|
130612
130637
|
}
|
|
130613
130638
|
});
|
|
130614
130639
|
|
|
130640
|
+
// wallet/src/net/torMissingMcp.mjs
|
|
130641
|
+
var torMissingMcp_exports = {};
|
|
130642
|
+
__export(torMissingMcp_exports, {
|
|
130643
|
+
RESTART: () => RESTART,
|
|
130644
|
+
serveTorMissingMcp: () => serveTorMissingMcp
|
|
130645
|
+
});
|
|
130646
|
+
async function serveTorMissingMcp({ message, recheck = async () => false, input = process.stdin, output: output2 = process.stdout, log = () => {
|
|
130647
|
+
} }) {
|
|
130648
|
+
const error = "mcp_tor_unavailable";
|
|
130649
|
+
const match = /^([a-z][a-z0-9_]*): ([\s\S]*)$/.exec(String(message));
|
|
130650
|
+
const cause = match ? match[1] : "tor_not_running";
|
|
130651
|
+
const detail = match ? match[2] : String(message);
|
|
130652
|
+
const answer = async () => {
|
|
130653
|
+
let ready = false;
|
|
130654
|
+
try {
|
|
130655
|
+
ready = await recheck() === true;
|
|
130656
|
+
} catch {
|
|
130657
|
+
ready = false;
|
|
130658
|
+
}
|
|
130659
|
+
const body = ready ? {
|
|
130660
|
+
say: `Tor runs on this machine now, but this wallet started before it did. ${RESTART}.`,
|
|
130661
|
+
ok: false,
|
|
130662
|
+
error: "mcp_restart_needed",
|
|
130663
|
+
next: `Tell the user: ${RESTART}. Nothing was paid.`
|
|
130664
|
+
} : {
|
|
130665
|
+
say: `The wallet is set to run behind Tor, and Tor is not running on this machine, so nothing was sent and nothing was paid.`,
|
|
130666
|
+
ok: false,
|
|
130667
|
+
error,
|
|
130668
|
+
cause,
|
|
130669
|
+
detail,
|
|
130670
|
+
next: `Tell the user what to install, with the command for their OS from detail; when Tor runs: ${RESTART}.`
|
|
130671
|
+
};
|
|
130672
|
+
log(`MCP_TOR_MISSING_ANSWER ${body.error}`);
|
|
130673
|
+
return { text: JSON.stringify(body, null, 2), isError: true };
|
|
130674
|
+
};
|
|
130675
|
+
const hidden = new Set(MCP_HIDDEN_TOOL_NAMES);
|
|
130676
|
+
const tools = [...MCP_TOOL_NAMES, "wallet_status"].map((name) => ({
|
|
130677
|
+
name,
|
|
130678
|
+
title: name,
|
|
130679
|
+
description: `Not available yet: Tor is not running on this machine. Call it for the install command (it moves no money).`,
|
|
130680
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: true },
|
|
130681
|
+
annotations: { readOnlyHint: true },
|
|
130682
|
+
hidden: hidden.has(name),
|
|
130683
|
+
run: answer
|
|
130684
|
+
}));
|
|
130685
|
+
const dispatch = createMcpDispatcher({
|
|
130686
|
+
tools,
|
|
130687
|
+
aliases: MCP_TOOL_ALIASES,
|
|
130688
|
+
instructions: `The Shinjuku Shield wallet is set to run behind Tor, and Tor is not running on this machine. Call wallet_balance: its answer has the install command for the user's OS. ${RESTART} after Tor starts.`
|
|
130689
|
+
});
|
|
130690
|
+
await serveStdio({ input, output: output2, dispatch, log });
|
|
130691
|
+
return 1;
|
|
130692
|
+
}
|
|
130693
|
+
var RESTART;
|
|
130694
|
+
var init_torMissingMcp = __esm({
|
|
130695
|
+
"wallet/src/net/torMissingMcp.mjs"() {
|
|
130696
|
+
"use strict";
|
|
130697
|
+
init_stdioServer();
|
|
130698
|
+
init_tools();
|
|
130699
|
+
RESTART = "Restart this MCP server in your app (or restart the app), then ask again";
|
|
130700
|
+
}
|
|
130701
|
+
});
|
|
130702
|
+
|
|
130615
130703
|
// packages/ctvault-v0b/node_modules/@solana/errors/dist/index.node.mjs
|
|
130616
130704
|
function encodeValue16(value) {
|
|
130617
130705
|
if (Array.isArray(value)) {
|
|
@@ -154551,9 +154639,10 @@ init_fs();
|
|
|
154551
154639
|
init_balance();
|
|
154552
154640
|
init_deposit();
|
|
154553
154641
|
import { spawn as spawn8 } from "node:child_process";
|
|
154554
|
-
import { randomBytes as
|
|
154642
|
+
import { randomBytes as randomBytes28, randomInt as randomInt9 } from "node:crypto";
|
|
154555
154643
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
154556
154644
|
import { join as join36, resolve as resolve32 } from "node:path";
|
|
154645
|
+
init_chat();
|
|
154557
154646
|
init_proxy();
|
|
154558
154647
|
init_rpcDefault();
|
|
154559
154648
|
init_usdcWords();
|
|
@@ -154812,8 +154901,12 @@ function fundsSummary(view2) {
|
|
|
154812
154901
|
`Next: ${view2.next}.`
|
|
154813
154902
|
].join("\n");
|
|
154814
154903
|
}
|
|
154815
|
-
function
|
|
154816
|
-
deps.
|
|
154904
|
+
function fundsOutput(values, deps) {
|
|
154905
|
+
const terminal = deps.stdoutIsTTY?.() === true && !values.has("--json");
|
|
154906
|
+
return { terminal, quiet: terminal && !values.has("--verbose") };
|
|
154907
|
+
}
|
|
154908
|
+
function printEnd(deps, view2, terminal) {
|
|
154909
|
+
deps.io.out(terminal ? `${fundsSummary(view2)}
|
|
154817
154910
|
` : `${JSON.stringify(view2, null, 2)}
|
|
154818
154911
|
`);
|
|
154819
154912
|
}
|
|
@@ -154829,14 +154922,14 @@ function fundsStatusSummary(view2) {
|
|
|
154829
154922
|
`Privacy: ${view2.privacy}.`,
|
|
154830
154923
|
...view2.otherRuns?.length ? [`Other add-funds runs of this wallet: ${view2.otherRuns.join(", ")} (add --funds-id <id> to see one).`] : [],
|
|
154831
154924
|
next,
|
|
154832
|
-
"For the whole status as JSON, send this command's output to a file or a program."
|
|
154925
|
+
"For the whole status as JSON, add --json (or send this command's output to a file or a program)."
|
|
154833
154926
|
].join("\n");
|
|
154834
154927
|
}
|
|
154835
|
-
var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |hint: |Help: |next: |Examples?:|Every flag: | shinjuku(?:-wallet)? )/;
|
|
154928
|
+
var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |shinjuku(?:-wallet)?\b.*: waiting for funds, nothing failed \(|hint: |Help: |next: |Examples?:|Every flag: | shinjuku(?:-wallet)? )/;
|
|
154836
154929
|
var STEP_TOR_LINE = /^(?:tor|proxy): /;
|
|
154837
154930
|
async function runStep(ctx, argv2) {
|
|
154838
154931
|
if (ctx.signal.aborted) return { exit: 1, held: [], codes: [], details: {}, aborted: true };
|
|
154839
|
-
const file = join36(ctx.tempDir, `step-${
|
|
154932
|
+
const file = join36(ctx.tempDir, `step-${randomBytes28(8).toString("hex")}.json`);
|
|
154840
154933
|
const held = [];
|
|
154841
154934
|
let last;
|
|
154842
154935
|
const proc = spawn8(process.execPath, [...process.execArgv, ctx.entry, ...ctx.proxyArgs, ...argv2], {
|
|
@@ -154853,6 +154946,7 @@ async function runStep(ctx, argv2) {
|
|
|
154853
154946
|
}
|
|
154854
154947
|
if (STEP_TOR_LINE.test(text3) && !TOR_TROUBLE_LINE.test(text3) && !/\bWARNING\b/.test(text3)) return;
|
|
154855
154948
|
if (text3.trim()) last = text3;
|
|
154949
|
+
if (ctx.quiet && isProgressLine(text3)) return;
|
|
154856
154950
|
if (!text3.trim() || ctx.shown.has(text3)) return;
|
|
154857
154951
|
ctx.shown.add(text3);
|
|
154858
154952
|
ctx.io.err(` ${text3}
|
|
@@ -155234,6 +155328,7 @@ var STAGE_START = Object.freeze({
|
|
|
155234
155328
|
pocket: (plan) => `fills ${plan.pockets} ready pocket${plan.pockets === 1 ? "" : "s"} of ${usdc5(plan.pocketSizeAtomic)} USDC for sellers that take standard exact only (pocket fill); a payment from a pocket takes seconds`,
|
|
155235
155329
|
balance: () => "reads the new balance from the chain (balance --refresh)"
|
|
155236
155330
|
});
|
|
155331
|
+
var STEP_WORDS = / \((?:deposit --relay|balance --refresh|pocket fill|privacy-cash (?:deposit|withdraw))[^)]*\)/g;
|
|
155237
155332
|
function readPrivateLine(path, flag3) {
|
|
155238
155333
|
try {
|
|
155239
155334
|
const text3 = readSecretText(path, flag3);
|
|
@@ -155438,7 +155533,7 @@ async function addFunds(values, deps) {
|
|
|
155438
155533
|
}
|
|
155439
155534
|
const tempDir = mkdtempSync(join36(tmpdir4(), "shinjuku-funds-"));
|
|
155440
155535
|
const abort = new AbortController();
|
|
155441
|
-
const ctx = { io, store, values: run5, tempDir, now: deps.now, signal: abort.signal, shown: /* @__PURE__ */ new Set(), transient: 0, entry: process.argv[1] };
|
|
155536
|
+
const ctx = { io, store, values: run5, tempDir, now: deps.now, signal: abort.signal, shown: /* @__PURE__ */ new Set(), transient: 0, entry: process.argv[1], ...fundsOutput(values, deps) };
|
|
155442
155537
|
const onSignal = (signal) => {
|
|
155443
155538
|
if (abort.signal.aborted) return;
|
|
155444
155539
|
abort.abort();
|
|
@@ -155452,7 +155547,7 @@ async function addFunds(values, deps) {
|
|
|
155452
155547
|
if (resolved.shown) {
|
|
155453
155548
|
io.err(`add-funds ${plan.fundsId} is ready already; nothing new was started (a new top-up needs a new --funds-id)
|
|
155454
155549
|
`);
|
|
155455
|
-
printEnd(deps, fundsView(store, plan, { now: deps.now() }));
|
|
155550
|
+
printEnd(deps, fundsView(store, plan, { now: deps.now() }), ctx.terminal);
|
|
155456
155551
|
return;
|
|
155457
155552
|
}
|
|
155458
155553
|
const save2 = () => store.writeWal(planName(plan.fundsId), plan);
|
|
@@ -155463,7 +155558,7 @@ async function addFunds(values, deps) {
|
|
|
155463
155558
|
plan,
|
|
155464
155559
|
proxyArgs: proxy.args,
|
|
155465
155560
|
childEnv: { ...proxy.env, [PASSPHRASE_ENV]: passphrase2, [RPC_ENV2]: rpcUrl },
|
|
155466
|
-
say: (stage, text3) => io.err(`add-funds ${plan.fundsId} [${stage}] ${text3}
|
|
155561
|
+
say: (stage, text3) => io.err(`add-funds ${plan.fundsId} [${stage}] ${ctx.quiet ? text3.replace(STEP_WORDS, "") : text3}
|
|
155467
155562
|
`),
|
|
155468
155563
|
mark: (stage) => {
|
|
155469
155564
|
if (plan.done?.[stage]) return;
|
|
@@ -155495,8 +155590,8 @@ async function addFunds(values, deps) {
|
|
|
155495
155590
|
plan.status = { stage: "ready", at: iso2(deps.now()) };
|
|
155496
155591
|
save2();
|
|
155497
155592
|
const view3 = fundsView(openStore(), plan, { now: deps.now() });
|
|
155498
|
-
ctx.say("ready", view3.status);
|
|
155499
|
-
printEnd(deps, view3);
|
|
155593
|
+
if (!ctx.terminal) ctx.say("ready", view3.status);
|
|
155594
|
+
printEnd(deps, view3, ctx.terminal);
|
|
155500
155595
|
return;
|
|
155501
155596
|
}
|
|
155502
155597
|
if (stage !== lastStage) {
|
|
@@ -155532,7 +155627,7 @@ async function addFunds(values, deps) {
|
|
|
155532
155627
|
if (values.has("--once")) {
|
|
155533
155628
|
plan.status = { ...plan.status, ended: iso2(deps.now()) };
|
|
155534
155629
|
save2();
|
|
155535
|
-
printEnd(deps, fundsView(store, plan, { now: deps.now(), running: false }));
|
|
155630
|
+
printEnd(deps, fundsView(store, plan, { now: deps.now(), running: false }), ctx.terminal);
|
|
155536
155631
|
return;
|
|
155537
155632
|
}
|
|
155538
155633
|
await sleep3(outcome.ms, abort.signal);
|
|
@@ -155542,7 +155637,7 @@ async function addFunds(values, deps) {
|
|
|
155542
155637
|
const view2 = fundsView(store, plan, { now: deps.now(), running: false });
|
|
155543
155638
|
io.err(`add-funds ${plan.fundsId} stopped at ${view2.stage}: nothing is lost. Rerun this same command to continue
|
|
155544
155639
|
`);
|
|
155545
|
-
printEnd(deps, view2);
|
|
155640
|
+
printEnd(deps, view2, ctx.terminal);
|
|
155546
155641
|
} finally {
|
|
155547
155642
|
process.off("SIGINT", onSignal);
|
|
155548
155643
|
process.off("SIGTERM", onSignal);
|
|
@@ -155559,7 +155654,7 @@ async function fundsStatus(values, deps) {
|
|
|
155559
155654
|
const plans = fundsPlans(store);
|
|
155560
155655
|
const wanted = values.get("--funds-id");
|
|
155561
155656
|
const plan = wanted === void 0 ? plans.at(-1) : plans.find((entry) => entry.fundsId === wanted);
|
|
155562
|
-
const terminal = deps
|
|
155657
|
+
const { terminal } = fundsOutput(values, deps);
|
|
155563
155658
|
if (!plan) {
|
|
155564
155659
|
const none3 = {
|
|
155565
155660
|
status: wanted === void 0 ? "this wallet has no add-funds run yet" : "this wallet has no add-funds run with that --funds-id",
|
|
@@ -157834,7 +157929,9 @@ var COMMANDS = [
|
|
|
157834
157929
|
{ ...TREE_RPC_FLAG, about: `--route privacy-cash: ${TREE_RPC_FLAG.about}` },
|
|
157835
157930
|
flag2("--funds-id", "<id>", "the name of this funding run; default: the unfinished one, else a new funds-<n>. A new top-up after one is ready needs a new name"),
|
|
157836
157931
|
flag2("--split", "<k|auto>", "default 1 (one deposit, one transfer). 1 to 8: k equal deposits of --amount/k (whole USDC each), each from its own one-time address, so k payments run in a row without waiting for a pool anchor. auto: up to 4 deposits, 1 USDC each and the rest in the last (5 USDC: 1, 1, 1 and 2 USDC). Exchange and changenow routes"),
|
|
157837
|
-
bool2("--once", "run what can run now, print the status, and exit instead of waiting (for your own scheduler)")
|
|
157932
|
+
bool2("--once", "run what can run now, print the status, and exit instead of waiting (for your own scheduler)"),
|
|
157933
|
+
bool2("--json", "end with the status as JSON in a terminal too (a file or a program gets it without this flag)"),
|
|
157934
|
+
bool2("--verbose", "also show the wallet's own progress lines in a terminal (Tor, the RPC relay, the pool history, the memo reads) and the command each step runs")
|
|
157838
157935
|
],
|
|
157839
157936
|
examples: [
|
|
157840
157937
|
`${CLI2} add-funds --route exchange --amount 2 ${FUNDS_BASE}`,
|
|
@@ -157856,7 +157953,7 @@ var COMMANDS = [
|
|
|
157856
157953
|
"It changes no rule: the crowd hold, the dwell, whole-USDC deposits, and every refusal are the single commands' own. A refusal that a later run clears (the hold, the crowd count, the dwell, a paused facilitator) is a wait; any other refusal stops it with that command's own line and hint (exit 1), and the rerun continues after your fix. It has no override flag: run the single command (for example privacy-cash withdraw --hold-override) when you choose to give up a wait.",
|
|
157857
157954
|
"--tor (or --proxy): each step runs as its own Tor run, as if you typed it: a new circuit and its own Tor check. With --tor, --service may be the facilitator's http://<v3 onion>/ origin.",
|
|
157858
157955
|
`--history-rpc: the balance --refresh and pocket fill steps (and the dwell check) read the pool's public history from it (${CLI2} help balance says what it sees); the deposit and Privacy Cash steps never get it. Without it, those two steps read from the history proxy this wallet pins for the pool (its onion with --tor, else https on the facilitator's pay host; --history-rpc own keeps them on your own RPC). add-funds does not keep it: give it again on each rerun. It never falls back to your own RPC: a failed source fails the step with history_rpc_failed, and add-funds handles that as it handles the step's other refusals.`,
|
|
157859
|
-
"In a terminal it ends with a short summary (ready with your balance, or where it stopped and what to do); when its output goes to a file or a program, it ends with the status as JSON, as funds-status prints it. While it waits for the USDC it checks the one-time address every 20 s for the first 5 minutes, then every 2 minutes.",
|
|
157956
|
+
"In a terminal it shows one line per step and ends with a short summary (ready with your balance, or where it stopped and what to do); the wallet's own progress lines (Tor, the RPC relay, the pool history, the memo reads) show only with --verbose, and every warning or failure shows always. With --json, or when its output goes to a file or a program, it ends with the status as JSON, as funds-status prints it, and shows every progress line. While it waits for the USDC it checks the one-time address every 20 s for the first 5 minutes, then every 2 minutes.",
|
|
157860
157957
|
'--once: run what can run now, print the status (the summary in a terminal; JSON for your scheduler, with "ready": false while it waits), and exit 0; run it again from your own scheduler.',
|
|
157861
157958
|
"When it is ready, the same command again only shows its status; a new top-up needs a new --funds-id.",
|
|
157862
157959
|
"One deposit by default: one transfer, one token-account rent for the sender. A payment spends one funded account, and its change (a pocket fill's too) can pay only after the next pool anchor (up to 5 minutes; about 30 s on some pools), so a second shielded spend before that anchor waits up to 5 minutes. The wallet sets money aside for payments in the background, where that wait costs you nothing, so one deposit is enough for most agents. --split auto (exchange and changenow) is for an agent that makes several shielded spends at once: up to 4 deposits, 1 USDC each and the rest in the last one (2 USDC: 1 and 1; 3 USDC: 1, 1 and 1; 5 USDC: 1, 1, 1 and 2; 20 USDC: 1, 1, 1 and 17). Each funded account pays one payment or one 1 USDC pocket fill per pool anchor without waiting, and a payment pays from the smallest account that covers it, so the large one stays for a payment above 1 USDC. With 5 USDC and --pockets 1: the pocket fill takes one 1 USDC account, and 3 more spends run before the next anchor without waiting (one deposit: the next spend after the fill waits up to 5 minutes for the anchor). What it costs: each deposit is its own one-time address, and the sender pays each address's USDC token account (about 0.0015 SOL each, so 4 deposits cost about 0.006 SOL), and you send 4 transfers instead of 1. --split <k> (2 to 8): k equal deposits of --amount/k, so the wallet holds k funded accounts. --amount must split into whole USDC (--amount 2 --split 2: two deposits of 1 USDC). add-funds records all k one-time addresses first, then prints them: send exactly --amount/k to EACH one, as k separate withdrawals (or k ChangeNOW exchanges); each address takes one deposit. Each deposit is its own record (deposit-<funds-id>, deposit-<funds-id>-part2, ...), so a rerun continues every part; it is ready when all k landed. What it shows: k public deposit amounts instead of one, read and landing close together, so they can show one depositor (about what one deposit of the total shows); the facilitator pays the fee and rent of each. --split (auto parts follow a new --amount) may change until any address is funded, never below the addresses already recorded. Not with --route privacy-cash. balance shows how many payments can run without waiting."
|
|
@@ -157864,6 +157961,8 @@ var COMMANDS = [
|
|
|
157864
157961
|
next: `see where it is from any terminal: ${CLI2} funds-status --pool ${GUIDE.pool} --passphrase-file passphrase.txt`,
|
|
157865
157962
|
nextInOutput: true,
|
|
157866
157963
|
validate: (values) => validateAddFunds(values),
|
|
157964
|
+
// In a terminal without --json or --verbose: its own progress lines stay off the screen (funds/addFunds.mjs fundsOutput).
|
|
157965
|
+
quietProgress: (values, deps) => fundsOutput(values, deps).quiet,
|
|
157867
157966
|
run: (args, values, deps, io) => deps.fundsMain("add-funds", values, { ...deps, io })
|
|
157868
157967
|
},
|
|
157869
157968
|
{
|
|
@@ -157873,12 +157972,13 @@ var COMMANDS = [
|
|
|
157873
157972
|
flags: [
|
|
157874
157973
|
flag2("--pool", "<address>", "the shielded pool", { required: true, check: "pubkey" }),
|
|
157875
157974
|
flag2("--passphrase-file", "<file>", `the private file that holds the wallet passphrase (the one add-funds uses); or set ${PASSPHRASE_ENV2}`),
|
|
157876
|
-
flag2("--funds-id", "<id>", "one add-funds run; default: the newest")
|
|
157975
|
+
flag2("--funds-id", "<id>", "one add-funds run; default: the newest"),
|
|
157976
|
+
bool2("--json", "print the status as JSON in a terminal too (a file or a program gets it without this flag)")
|
|
157877
157977
|
],
|
|
157878
157978
|
examples: [`${CLI2} funds-status --pool ${GUIDE.pool} --passphrase-file passphrase.txt`],
|
|
157879
157979
|
notes: [
|
|
157880
157980
|
"It reads local files only (the wallet log and the add-funds lock) and makes no network call, so you can run it at any time: while add-funds runs, or after it stopped.",
|
|
157881
|
-
"In a terminal it prints a short summary in plain words: the status, where to send the USDC while add-funds waits for it, the privacy cost, and what to do next.
|
|
157981
|
+
"In a terminal it prints a short summary in plain words: the status, where to send the USDC while add-funds waits for it, the privacy cost, and what to do next. With --json, or when its output goes to a file or a program, it prints the status as JSON, with the fields below.",
|
|
157882
157982
|
'In the JSON, status is one line: the stage and what it waits for (next try at), "not running" when no add-funds runs for this wallet, "stopped" with what needs you, or "ready" with your balance; it ends with what the route shows, and to whom (privacy). running is true while an add-funds run holds this wallet. stages lists every stage: done (with its time), waiting, next, not running, stopped, or later.',
|
|
157883
157983
|
"oneTimeAddress and send show while the deposit waits for USDC: on the exchange and changenow routes, send to it once, and only the amount shown. leftoverAtOneTimeAddress shows USDC beyond the amount that stays at that address, and what taking it back shows. With --split (k or auto): split and parts show each deposit (its amount, state, one-time address while it waits for USDC, and signature once it landed); send goes to every address listed, each with its own amount.",
|
|
157884
157984
|
"Without --funds-id it shows the newest run; otherRuns names the others."
|
|
@@ -158391,7 +158491,7 @@ var COMMANDS = [
|
|
|
158391
158491
|
// The same for its RPC: only the Tor run resolves it, so the relay line shows once, not once per process.
|
|
158392
158492
|
rpcWhen: (values, env) => inProxiedRun(env),
|
|
158393
158493
|
// The Tor run, the one that pays: its progress lines show only with --verbose (inference/chat.mjs hideProgressLines).
|
|
158394
|
-
quietProgress: (values,
|
|
158494
|
+
quietProgress: (values, deps) => inProxiedRun(deps.env) && !values.has("--verbose"),
|
|
158395
158495
|
run: (args, values, deps, io) => deps.chatMain(values, { ...deps, io })
|
|
158396
158496
|
},
|
|
158397
158497
|
{
|
|
@@ -160768,7 +160868,7 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
160768
160868
|
const commandArgs = defaults.args;
|
|
160769
160869
|
const runDeps = defaults.passphrase === void 0 ? deps : { ...deps, env: { ...deps.env, [PASSPHRASE_ENV2]: defaults.passphrase } };
|
|
160770
160870
|
values = parseFlags(command2, commandArgs);
|
|
160771
|
-
if (command2.quietProgress?.(values, runDeps
|
|
160871
|
+
if (command2.quietProgress?.(values, runDeps) === true) showProgressAgain = hideProgressLines();
|
|
160772
160872
|
const skip = command2.beforeValidate?.(values, runDeps, typedArgs, usdc9.oldForm) === SKIP_VALIDATE;
|
|
160773
160873
|
if (!skip) validate3(command2, values, runDeps.env);
|
|
160774
160874
|
retiredPoolGuard(command2, values, io, deps.walletVersion().npm);
|