shinjuku-shield 0.3.85 → 0.3.86
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 +151 -10
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.86. 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.86 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.86
|
|
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.86 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.86", "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": "b650bf0682830e05207f52bad2ba426ec7f8fa1bbbfdb7e028057865e3d83d24",
|
|
5
|
+
"bytes": 12522998,
|
|
6
|
+
"sourceCommit": "435a594c2af5c17d16a0f700bf2d9b7ff79af533",
|
|
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-23T21:42:59.534Z"
|
|
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.86",
|
|
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": "b650bf06",
|
|
7
|
+
"shinjukuSha256": "b650bf0682830e05207f52bad2ba426ec7f8fa1bbbfdb7e028057865e3d83d24",
|
|
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
|
@@ -47222,6 +47222,7 @@ function reexec(args, mode, plainProxy) {
|
|
|
47222
47222
|
const env = {
|
|
47223
47223
|
...process.env,
|
|
47224
47224
|
NODE_USE_ENV_PROXY: "1",
|
|
47225
|
+
NODE_OPTIONS: proxiedNodeOptions(process.env.NODE_OPTIONS),
|
|
47225
47226
|
...proxyEnv(proxy),
|
|
47226
47227
|
[MARK]: `${mode} ${proxy}`
|
|
47227
47228
|
};
|
|
@@ -47305,7 +47306,7 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
|
|
|
47305
47306
|
}
|
|
47306
47307
|
return reexec(options.args, options.mode, options.proxy);
|
|
47307
47308
|
}
|
|
47308
|
-
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, PROXY_WARNING_FLAG, proxiedExecArgv, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT;
|
|
47309
|
+
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, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT;
|
|
47309
47310
|
var init_proxy = __esm({
|
|
47310
47311
|
"wallet/src/net/proxy.mjs"() {
|
|
47311
47312
|
"use strict";
|
|
@@ -47402,6 +47403,10 @@ var init_proxy = __esm({
|
|
|
47402
47403
|
laneDispatchers = /* @__PURE__ */ new Map();
|
|
47403
47404
|
PROXY_WARNING_FLAG = "--disable-warning=UNDICI-EHPA";
|
|
47404
47405
|
proxiedExecArgv = (execArgv = process.execArgv) => execArgv.includes(PROXY_WARNING_FLAG) ? [...execArgv] : [...execArgv, PROXY_WARNING_FLAG];
|
|
47406
|
+
proxiedNodeOptions = (nodeOptions = process.env.NODE_OPTIONS) => {
|
|
47407
|
+
const options = String(nodeOptions ?? "").trim();
|
|
47408
|
+
return options.split(/\s+/).includes(PROXY_WARNING_FLAG) ? options : `${options} ${PROXY_WARNING_FLAG}`.trim();
|
|
47409
|
+
};
|
|
47405
47410
|
TOR_FROM_CONFIG_ENV = "SHINJUKU_WALLET_TOR_FROM_CONFIG";
|
|
47406
47411
|
TOR_FROM_CONFIG_HINT = '. Tor is on for every command because setup recorded it (config.json "tor": true); to run this one command without Tor, add --no-tor (the servers then see your IP address)';
|
|
47407
47412
|
}
|
|
@@ -65869,6 +65874,27 @@ var init_errorCodes = __esm({
|
|
|
65869
65874
|
"relay_reclaim_nothing_held",
|
|
65870
65875
|
"relay_refused_deposit_body_invalid",
|
|
65871
65876
|
"relay_refused_deposit_replay_conflict",
|
|
65877
|
+
// The exit relay's refusals (ShieldedSolanaP03ExitRelay.ts, createShieldedFacilitatorRoutes.ts), as
|
|
65878
|
+
// p03_unshield_relay_refused_<reason> (T16, 2026-10-23: an unknown one printed a bare p03_resource_cli_failed).
|
|
65879
|
+
"exit_amount_invalid",
|
|
65880
|
+
"exit_anchor_not_retained",
|
|
65881
|
+
"exit_body_invalid",
|
|
65882
|
+
"exit_buffer_unavailable",
|
|
65883
|
+
"exit_memo_digest_mismatch",
|
|
65884
|
+
"exit_operator_review_required",
|
|
65885
|
+
"exit_pending_retry_same_request",
|
|
65886
|
+
"exit_pool_busy_retry_same_request",
|
|
65887
|
+
"exit_pool_domain_mismatch",
|
|
65888
|
+
"exit_pool_invalid",
|
|
65889
|
+
"exit_proof_invalid",
|
|
65890
|
+
"exit_quote_expired",
|
|
65891
|
+
"exit_relay_busy",
|
|
65892
|
+
"exit_relay_unavailable",
|
|
65893
|
+
"exit_relayer_underfunded",
|
|
65894
|
+
"exit_replay_conflict",
|
|
65895
|
+
"exit_replay_consumed",
|
|
65896
|
+
"exit_settlement_not_landed_retry_same_request",
|
|
65897
|
+
"exit_vault_insufficient",
|
|
65872
65898
|
"relay_relayer_underfunded_retry_later",
|
|
65873
65899
|
"relay_service_required",
|
|
65874
65900
|
"relay_transaction_invalid",
|
|
@@ -107736,10 +107762,23 @@ function markedAnchorRequest(requestAnchor) {
|
|
|
107736
107762
|
}
|
|
107737
107763
|
async function exitWhenAnchored(run5, { now = Date.now, sleep: sleep6 = (ms) => new Promise((resolve40) => setTimeout(resolve40, ms)), ring } = {}) {
|
|
107738
107764
|
const deadline = now() + EXIT_ANCHOR_WAIT_MS;
|
|
107765
|
+
let reproved = false;
|
|
107766
|
+
let clockWaits = 0;
|
|
107739
107767
|
for (; ; ) {
|
|
107740
107768
|
try {
|
|
107741
107769
|
return await run5();
|
|
107742
107770
|
} catch (error) {
|
|
107771
|
+
if (!reproved && /p03_unshield_relay_refused_exit_quote_expired/.test(String(error?.message))) {
|
|
107772
|
+
reproved = true;
|
|
107773
|
+
progress("the exit proof's time window ended before the facilitator's exit transaction landed; nothing was sent. The wallet makes a new exit proof now and sends it");
|
|
107774
|
+
continue;
|
|
107775
|
+
}
|
|
107776
|
+
if (reproved && clockWaits < EXPIRED_PROOF_CLOCK_WAITS && /p03_note_exit_proved_still_valid_rerun_later/.test(String(error?.message))) {
|
|
107777
|
+
clockWaits += 1;
|
|
107778
|
+
progress(`the finalized chain clock has not passed the old exit proof's end yet; waiting ${EXPIRED_PROOF_CLOCK_WAIT_MS / 1e3} s, then the wallet makes the new proof (wait ${clockWaits}/${EXPIRED_PROOF_CLOCK_WAITS}; nothing was sent)`);
|
|
107779
|
+
await sleep6(EXPIRED_PROOF_CLOCK_WAIT_MS);
|
|
107780
|
+
continue;
|
|
107781
|
+
}
|
|
107743
107782
|
if (!/p03_note_exit_anchor_not_current_wait_next_epoch/.test(String(error?.message))) throw error;
|
|
107744
107783
|
const start = now();
|
|
107745
107784
|
const subAnchors = subAnchorPool(start) !== void 0;
|
|
@@ -107776,7 +107815,13 @@ function facilitatorExitRelay(origin, { fetchImpl = fetch, pause: pause3 = (ms)
|
|
|
107776
107815
|
const answer = await response.json().catch(() => ({}));
|
|
107777
107816
|
if (response.status === 200 && answer.success === true && isSolanaSignature(answer.transaction)) return answer.transaction;
|
|
107778
107817
|
if (response.status === 200 && answer.success === true) fail29("relay_transaction_invalid");
|
|
107779
|
-
|
|
107818
|
+
const refused = String(answer.errorReason ?? "unknown").replace(/[^a-z0-9_]/g, "");
|
|
107819
|
+
if (response.status === 200 && RELAY_RETRY_SAME.test(refused) && attempt < attempts) {
|
|
107820
|
+
progress(`waiting: the facilitator's exit relay answered ${refused} (its exit transaction has not landed yet); sending the same exit again in ${RELAY_RETRY_SAME_SECONDS} s (retry ${attempt}/${attempts - 1}). Nothing is lost: the note is still yours, and a rerun of the same command continues`);
|
|
107821
|
+
await pause3(RELAY_RETRY_SAME_SECONDS * 1e3);
|
|
107822
|
+
continue;
|
|
107823
|
+
}
|
|
107824
|
+
if (response.status === 200) fail29(`relay_refused_${refused}`);
|
|
107780
107825
|
if (![429, 503].includes(response.status) || attempt >= attempts) fail29(`relay_status_${response.status}_retry_same_unshield`);
|
|
107781
107826
|
const seconds2 = Number(response.headers.get("retry-after"));
|
|
107782
107827
|
const waitSeconds = Number.isFinite(seconds2) && seconds2 > 0 ? Math.min(seconds2, 120) : 15;
|
|
@@ -107934,7 +107979,7 @@ async function unshieldOnce({ options, origin, historyUrl, walletPath: walletPat
|
|
|
107934
107979
|
store.writeWal(`self-pay-${unshieldId}`, { ...store.readWal(`self-pay-${unshieldId}`), state: "exited", exitTransaction: exit.signature });
|
|
107935
107980
|
return { amountAtomic: String(exit.amountAtomic), selfPayTransaction: paid.transaction, exitTransaction: exit.signature, replayed: exit.replayed };
|
|
107936
107981
|
}
|
|
107937
|
-
var import_web3121, USAGE2, REQUIRED2, OPTIONAL, fail29, progress, EPOCH_MS2, EPOCH_MARGIN_MS, EXIT_ANCHOR_WAIT_MS;
|
|
107982
|
+
var import_web3121, USAGE2, REQUIRED2, OPTIONAL, fail29, RELAY_RETRY_SAME, RELAY_RETRY_SAME_SECONDS, progress, EPOCH_MS2, EPOCH_MARGIN_MS, EXIT_ANCHOR_WAIT_MS, EXPIRED_PROOF_CLOCK_WAITS, EXPIRED_PROOF_CLOCK_WAIT_MS;
|
|
107938
107983
|
var init_cli2 = __esm({
|
|
107939
107984
|
"wallet/src/unshield/cli.mjs"() {
|
|
107940
107985
|
"use strict";
|
|
@@ -107979,6 +108024,8 @@ var init_cli2 = __esm({
|
|
|
107979
108024
|
fail29 = (reason) => {
|
|
107980
108025
|
throw new Error(`p03_unshield_${reason}`);
|
|
107981
108026
|
};
|
|
108027
|
+
RELAY_RETRY_SAME = /_retry_same_request$|^p03_e1_value_pending$|^p03_e1_proof_unavailable$|^p03_validity_proof_unavailable$/;
|
|
108028
|
+
RELAY_RETRY_SAME_SECONDS = 10;
|
|
107982
108029
|
progress = (text3) => {
|
|
107983
108030
|
try {
|
|
107984
108031
|
process.stderr.write(`${text3}
|
|
@@ -107989,6 +108036,8 @@ var init_cli2 = __esm({
|
|
|
107989
108036
|
EPOCH_MS2 = 3e5;
|
|
107990
108037
|
EPOCH_MARGIN_MS = 35e3;
|
|
107991
108038
|
EXIT_ANCHOR_WAIT_MS = EPOCH_MS2 + 6e4;
|
|
108039
|
+
EXPIRED_PROOF_CLOCK_WAITS = 8;
|
|
108040
|
+
EXPIRED_PROOF_CLOCK_WAIT_MS = 15e3;
|
|
107992
108041
|
if (process.argv[1] && resolve19(process.argv[1]) === fileURLToPath13(new URL("__bundled_source__/wallet/src/unshield/cli.mjs", import.meta.url).href)) {
|
|
107993
108042
|
main3().catch((error) => {
|
|
107994
108043
|
console.error(describeCliErrorWithHint(error));
|
|
@@ -109931,6 +109980,12 @@ function refusalSay(a, tool, again, npmVersion) {
|
|
|
109931
109980
|
if (code2 === "mcp_move_count_unreadable") {
|
|
109932
109981
|
return "Nothing was moved. This wallet's 24-hour limit cannot be checked just now. Try again in a minute; if it repeats, tell your owner.";
|
|
109933
109982
|
}
|
|
109983
|
+
if (code2 === "inference_ceiling_over_cap" && a.capKind === "call" && usdcText(a.capAtomic) && usdcText(a.ceilingAtomic) && usdcText(a.capNeededAtomic)) {
|
|
109984
|
+
const needed = usdcText(a.capNeededAtomic);
|
|
109985
|
+
const pays = usdcText(a.paysUpToAtomic);
|
|
109986
|
+
const allow = pays && BigInt(a.paysUpToAtomic) < BigInt(a.capNeededAtomic) ? `Allow ${needed} USDC; it still pays only up to ${pays} USDC.` : `Allow ${needed} USDC to pay it.`;
|
|
109987
|
+
return `Nothing was paid: this request's ${usdcText(a.capAtomic)} USDC cap is below the ${usdcText(a.ceilingAtomic)} USDC ceiling. ${allow}`;
|
|
109988
|
+
}
|
|
109934
109989
|
if ((code2 === "inference_ceiling_over_cap" || code2 === "p03_float_over_maximum") && usdcText(a.totalAtomic) && usdcText(a.capAtomic)) {
|
|
109935
109990
|
return `Nothing was paid: up to ${usdcText(a.totalAtomic)} USDC is over your ${usdcText(a.capAtomic)} USDC limit. ${raiseLine("--max-payment", npmVersion)}`;
|
|
109936
109991
|
}
|
|
@@ -127775,6 +127830,15 @@ ${canonical5}`;
|
|
|
127775
127830
|
const proxied = () => typeof ops.proxied === "function" ? ops.proxied() : proxyProvenAt() !== void 0;
|
|
127776
127831
|
const usd = (atomic4) => `$${formatAtomic(atomic4, 6)}`;
|
|
127777
127832
|
const INFERENCE_COST_WORDS = "A short qwen3-8-flash answer pays about 0.0011 USDC: usePod's price (often 0.00001-0.0001 USDC) plus the Shinjuku fee (0.001 USDC). The call's cap (max_payment, else --max-payment) must still cover the call's ceiling: max_tokens at the model's output price plus the prompt, and at least 0.01 USDC";
|
|
127833
|
+
const paysUpToAtomic = (inference, asked) => {
|
|
127834
|
+
try {
|
|
127835
|
+
const extra = inference.listModels().find((model) => model.id === asked.model).priceOutPerMAtomic * 100n;
|
|
127836
|
+
const lifted = BigInt(inference.ceilingAtomic({ ...asked, maxTokens: asked.maxTokens + 1e8 }));
|
|
127837
|
+
return lifted > BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 10000n) ? lifted - extra + 1000n : void 0;
|
|
127838
|
+
} catch {
|
|
127839
|
+
return void 0;
|
|
127840
|
+
}
|
|
127841
|
+
};
|
|
127778
127842
|
const moreTokens = (maxTokens) => Math.max(1024, 2 * maxTokens);
|
|
127779
127843
|
const torRefusal = () => refuse12(
|
|
127780
127844
|
"inference_tor_required",
|
|
@@ -127864,7 +127928,7 @@ ${canonical5}`;
|
|
|
127864
127928
|
const inferenceChat2 = {
|
|
127865
127929
|
name: "inference_chat",
|
|
127866
127930
|
title: "Ask an AI model, paid privately",
|
|
127867
|
-
description: `Use this when the user wants an AI model's answer paid from the SHIELDED balance: it calls usePod's x402 chat endpoint (usePod picks the provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for usePod. Only prompt (or messages) is needed: model defaults to qwen3-8-flash and max_tokens to 512. Called with neither prompt nor messages, it lists the models and their prices and pays nothing (for example qwen3-8-flash, deepseek-v4-1-flash, claude-opus-5). COST: a short qwen3-8-flash answer pays about 0.0011 USDC (usePod's price, often 0.00001-0.0001 USDC, plus the Shinjuku fee 0.001 USDC). The call's cap (max_payment, in USDC like "0.05", or max_payment_atomic; else --max-payment) must still cover the call's ceiling: max_tokens of output plus the prompt at the model's prices, and at least 0.01 USDC (usePod's floor for its standard offer). The total paid is never above the cap. The unused part is NOT refunded when the answer is shorter. max_tokens must be at least ${INFERENCE_MIN_MAX_TOKENS} (a reasoning model spends its first tokens thinking). Example: { "prompt": "Name three prime numbers" }. PRIVACY: usePod and the model's provider see the prompt and the answer; never put keys, passphrases, or secrets in it. It runs only when this MCP server was started with --tor or --proxy (else inference_tor_required, nothing paid), so they never see this machine's IP. The same caps as x402_pay apply (max_payment can only lower --max-payment). When the call can cost more than --max-payment and the MCP client can ask the user, the wallet asks the user once to allow this one call; else the answer says the cost and the limit. On an error code that ends in _retry_same_request, call inference_chat again with the SAME request_id and arguments: it never pays twice. No streaming.`,
|
|
127931
|
+
description: `Use this when the user wants an AI model's answer paid from the SHIELDED balance: it calls usePod's x402 chat endpoint (usePod picks the provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for usePod. Only prompt (or messages) is needed: model defaults to qwen3-8-flash and max_tokens to 512. Called with neither prompt nor messages, it lists the models and their prices and pays nothing (for example qwen3-8-flash, deepseek-v4-1-flash, claude-opus-5). COST: a short qwen3-8-flash answer pays about 0.0011 USDC (usePod's price, often 0.00001-0.0001 USDC, plus the Shinjuku fee 0.001 USDC). The call's cap (max_payment, in USDC like "0.05", or max_payment_atomic; else --max-payment) must still cover the call's ceiling: max_tokens of output plus the prompt at the model's prices, and at least 0.01 USDC (usePod's floor for its standard offer). The total paid is never above the cap, and the cap is only a limit: a larger cap never pays more. The unused part of max_tokens is NOT refunded when the answer is shorter. max_tokens must be at least ${INFERENCE_MIN_MAX_TOKENS} (a reasoning model spends its first tokens thinking). Example: { "prompt": "Name three prime numbers" }. PRIVACY: usePod and the model's provider see the prompt and the answer; never put keys, passphrases, or secrets in it. It runs only when this MCP server was started with --tor or --proxy (else inference_tor_required, nothing paid), so they never see this machine's IP. The same caps as x402_pay apply (max_payment can only lower --max-payment). When the call can cost more than --max-payment and the MCP client can ask the user, the wallet asks the user once to allow this one call; else the answer says the cost and the limit. On an error code that ends in _retry_same_request, call inference_chat again with the SAME request_id and arguments: it never pays twice. No streaming.`,
|
|
127868
127932
|
inputSchema: {
|
|
127869
127933
|
type: "object",
|
|
127870
127934
|
properties: {
|
|
@@ -127967,15 +128031,24 @@ ${canonical5}`;
|
|
|
127967
128031
|
let askedLimit = false;
|
|
127968
128032
|
let narrowed = cap.maxPaymentAtomic;
|
|
127969
128033
|
if (ceiling > BigInt(narrowed)) {
|
|
127970
|
-
const detail = `up to ${args.max_tokens} output tokens on ${args.model} have a ceiling of ${usdcText2(ceiling)} USDC, above this call's cap of ${usdcText2(narrowed)} USDC; the cap must be at least the ceiling; nothing was paid`;
|
|
128034
|
+
const detail = `up to ${args.max_tokens} output tokens on ${args.model} have a ceiling of ${usdcText2(ceiling)} USDC, above this call's cap of ${usdcText2(narrowed)} USDC; the cap must be at least the ceiling; nothing was paid; the cap is only a limit, not the price`;
|
|
127971
128035
|
const atFloor = ceiling <= BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 10000n);
|
|
127972
|
-
const
|
|
128036
|
+
const paysUpTo = paysUpToAtomic(inference, { model: args.model, messages, maxTokens: args.max_tokens });
|
|
128037
|
+
const pays = `A cap (max_payment, or --max-payment) is only a limit, not the price: the call pays usePod's price for its max_tokens plus the 0.001 USDC Shinjuku fee${paysUpTo === void 0 ? "" : ` (up to ${usdcText2(paysUpTo)} USDC in all at this model's listed prices)`}, and a larger cap never makes it pay more. The cap must still be at least the ceiling ${usdcText2(ceiling)} USDC.`;
|
|
127973
128038
|
if (BigInt(narrowed) !== BigInt(launch)) {
|
|
127974
128039
|
return refuse12(
|
|
127975
128040
|
"inference_ceiling_over_cap",
|
|
127976
128041
|
detail,
|
|
127977
128042
|
atFloor ? `${pays} So pass max_payment "${usdcText2(ceiling + 1000n)}" (the ceiling plus the fee) or more if the spending limit allows it, or leave max_payment out` : `${pays} So lower max_tokens (at least ${INFERENCE_MIN_MAX_TOKENS}), pick a cheaper model (inference_chat with no prompt lists them), or pass max_payment "${usdcText2(ceiling + 1000n)}" (the ceiling plus the fee) if the spending limit allows it`,
|
|
127978
|
-
{
|
|
128043
|
+
{
|
|
128044
|
+
requestId,
|
|
128045
|
+
paid: false,
|
|
128046
|
+
...facts,
|
|
128047
|
+
capKind: "call",
|
|
128048
|
+
capAtomic: String(narrowed),
|
|
128049
|
+
capNeededAtomic: (ceiling + 1000n).toString(),
|
|
128050
|
+
...paysUpTo === void 0 ? {} : { paysUpToAtomic: paysUpTo.toString() }
|
|
128051
|
+
}
|
|
127979
128052
|
);
|
|
127980
128053
|
}
|
|
127981
128054
|
askedLimit = true;
|
|
@@ -155839,6 +155912,7 @@ init_job();
|
|
|
155839
155912
|
init_rpcDefault();
|
|
155840
155913
|
init_adoptOneTime();
|
|
155841
155914
|
init_privateSend();
|
|
155915
|
+
init_selfPay();
|
|
155842
155916
|
init_payMe();
|
|
155843
155917
|
init_transferToken();
|
|
155844
155918
|
init_recoveredPayment();
|
|
@@ -156718,7 +156792,7 @@ function inferPreflight(inference, values, prompt) {
|
|
|
156718
156792
|
const floor = BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 0n);
|
|
156719
156793
|
const worst = ceiling > floor ? ceiling : floor;
|
|
156720
156794
|
if (worst > BigInt(values.get("--max-payment"))) {
|
|
156721
|
-
throw new PublicFailure(`inference_ceiling_over_cap: usePod's price for this request may be up to ${usdcOr(worst)} (ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}), and Shinjuku's fee comes on top; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}); nothing was paid. Raise --max-payment to at least ${usdcFlagText(worst)} (${usdcOr(worst)}) plus Shinjuku's fee${ceiling > floor ? ", or lower --max-tokens" : ""}`);
|
|
156795
|
+
throw new PublicFailure(`inference_ceiling_over_cap: usePod's price for this request may be up to ${usdcOr(worst)} (ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}), and Shinjuku's fee comes on top; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}); nothing was paid. Raise --max-payment to at least ${usdcFlagText(worst)} (${usdcOr(worst)}) plus Shinjuku's fee${ceiling > floor ? ", or lower --max-tokens" : ""}. The cap is only a limit, not the price: the call pays usePod's price for its tokens plus the 0.001 USDC fee, and a larger cap never pays more`);
|
|
156722
156796
|
}
|
|
156723
156797
|
return { model, maxTokens, ceiling, worst };
|
|
156724
156798
|
}
|
|
@@ -159701,6 +159775,23 @@ async function runUnshieldCommand(args, values, deps, io) {
|
|
|
159701
159775
|
setArg("--recipient-owner", owner.toBase58());
|
|
159702
159776
|
}
|
|
159703
159777
|
let id = values.get("--unshield-id");
|
|
159778
|
+
let recorded;
|
|
159779
|
+
if (id !== void 0) {
|
|
159780
|
+
let opened;
|
|
159781
|
+
try {
|
|
159782
|
+
opened = openStore();
|
|
159783
|
+
} catch {
|
|
159784
|
+
opened = void 0;
|
|
159785
|
+
}
|
|
159786
|
+
recorded = opened === void 0 ? void 0 : recordedUnshield(opened, id, values);
|
|
159787
|
+
}
|
|
159788
|
+
if (recorded && values.get("--amount") === "all") {
|
|
159789
|
+
setArg("--amount", recorded.amountAtomic);
|
|
159790
|
+
io.err(`--amount all: unshield ${id} started earlier with ${usdcOr(recorded.amountAtomic)}; this run finishes it (it never sends twice)
|
|
159791
|
+
`);
|
|
159792
|
+
} else if (recorded && values.get("--amount") !== recorded.amountAtomic) {
|
|
159793
|
+
throw unshieldRefusal("id_reused_with_different_amount", `unshield ${id} started earlier with ${usdcOr(recorded.amountAtomic)}, not ${usdcOr(values.get("--amount"))}, and one unshield id always sends the amount it started with; nothing was sent. Finish it: ${CLI2} unshield --amount all${recorded.flags} --to ${owner.toBase58()} --unshield-id ${id} (it never sends twice). To send another amount, leave out --unshield-id`);
|
|
159794
|
+
}
|
|
159704
159795
|
const ensureId = () => {
|
|
159705
159796
|
if (id !== void 0) return;
|
|
159706
159797
|
id = `unshield-${randomBytes35(6).toString("hex")}`;
|
|
@@ -159724,9 +159815,36 @@ async function runUnshieldCommand(args, values, deps, io) {
|
|
|
159724
159815
|
if (!/^[1-9][0-9]*$/.test(String(sent ?? ""))) return;
|
|
159725
159816
|
const walletAt = gated.indexOf("--wallet");
|
|
159726
159817
|
const report = await refreshAfterUnshield(walletAt >= 0 ? gated[walletAt + 1] : values.get("--wallet"), values, deps, io);
|
|
159727
|
-
if (terminal)
|
|
159818
|
+
if (!terminal) return;
|
|
159819
|
+
let unfinished2 = [];
|
|
159820
|
+
try {
|
|
159821
|
+
unfinished2 = unfinishedUnshields(openStore());
|
|
159822
|
+
} catch {
|
|
159823
|
+
}
|
|
159824
|
+
io.out(`${[privateBalanceLine(report, "Private balance now"), ...unfinished2.map(unfinishedUnshieldLine)].join("\n")}
|
|
159728
159825
|
`);
|
|
159729
159826
|
}
|
|
159827
|
+
function recordedUnshield(store, id, values) {
|
|
159828
|
+
if (!/^[0-9a-zA-Z_-]{1,48}$/.test(id)) return void 0;
|
|
159829
|
+
const whole = (atomic4) => /^[1-9][0-9]*$/.test(String(atomic4 ?? ""));
|
|
159830
|
+
if (isSingleSend(values.get("--parts") ?? 1, values.get("--max-delay-minutes") ?? 0)) {
|
|
159831
|
+
const record = store.readWal(`self-pay-${id}`);
|
|
159832
|
+
return record?.schema === SELF_PAY_WAL_SCHEMA && whole(record.amountAtomic) ? { amountAtomic: String(record.amountAtomic), flags: "" } : void 0;
|
|
159833
|
+
}
|
|
159834
|
+
const plan = store.readWal(privateSendPlanName(id));
|
|
159835
|
+
return plan?.schema === PRIVATE_SEND_WAL_SCHEMA && whole(plan.amountAtomic) && Array.isArray(plan.parts) ? { amountAtomic: String(plan.amountAtomic), flags: ` --parts ${plan.parts.length} --max-delay-minutes ${plan.maxDelayMinutes}` } : void 0;
|
|
159836
|
+
}
|
|
159837
|
+
function unfinishedUnshields(store) {
|
|
159838
|
+
const read = (name) => {
|
|
159839
|
+
try {
|
|
159840
|
+
return store.readWal(name);
|
|
159841
|
+
} catch {
|
|
159842
|
+
return void 0;
|
|
159843
|
+
}
|
|
159844
|
+
};
|
|
159845
|
+
return store.listWal().filter((name) => name.startsWith("self-pay-")).map((name) => name.slice("self-pay-".length)).filter((unshieldId) => !unshieldId.startsWith("merge-") && !/^pocket-\d+-f\d+$/.test(unshieldId)).map((unshieldId) => ({ unshieldId, record: read(`self-pay-${unshieldId}`) })).filter(({ record }) => record?.schema === SELF_PAY_WAL_SCHEMA && record.ownerUnshield === true && record.state === "paid" && record.mode !== DIRECT_EXIT_MODE && /^[1-9][0-9]*$/.test(String(record.amountAtomic ?? ""))).map(({ unshieldId, record }) => ({ unshieldId, amountAtomic: String(record.amountAtomic) }));
|
|
159846
|
+
}
|
|
159847
|
+
var unfinishedUnshieldLine = ({ unshieldId, amountAtomic }) => `${usdcOr(amountAtomic)} is in an unshield that has not finished (unshield id ${unshieldId}): run ${CLI2} unshield --amount all --to <the address you sent it to> --unshield-id ${unshieldId} to finish it; it never sends twice`;
|
|
159730
159848
|
function unshieldDoneText(answer, to) {
|
|
159731
159849
|
const send3 = answer.privateSend;
|
|
159732
159850
|
if (!send3) {
|
|
@@ -160442,17 +160560,19 @@ async function runBalance(args, values, deps, io) {
|
|
|
160442
160560
|
}
|
|
160443
160561
|
if (config?.pool === store.state.pool && config.program && config.proofTools && config.memoService) report.next = `${CLI2} balance --refresh --fresh`;
|
|
160444
160562
|
}
|
|
160563
|
+
const unfinished2 = unfinishedUnshields(store);
|
|
160445
160564
|
if (deps.stdoutIsTTY?.() === true && !values.has("--json")) {
|
|
160446
160565
|
const notCurrent = report.status !== "current" && report.status !== "empty";
|
|
160447
160566
|
io.out(`${[
|
|
160448
160567
|
privateBalanceLine(report),
|
|
160568
|
+
...unfinished2.map(unfinishedUnshieldLine),
|
|
160449
160569
|
...shown.map((entry) => entry.notice).filter((notice) => typeof notice === "string" && notice),
|
|
160450
160570
|
...notCurrent && typeof report.next === "string" ? [`next: ${report.next}`] : []
|
|
160451
160571
|
].join("\n")}
|
|
160452
160572
|
`);
|
|
160453
160573
|
return;
|
|
160454
160574
|
}
|
|
160455
|
-
io.out(`${JSON.stringify(shown.length ? {
|
|
160575
|
+
io.out(`${JSON.stringify({ ...report, ...shown.length ? { earlierDeposits: shown } : {}, ...unfinished2.length ? { unfinishedUnshields: unfinished2 } : {} }, null, 2)}
|
|
160456
160576
|
`);
|
|
160457
160577
|
}
|
|
160458
160578
|
async function adoptOneTimeForBalance({ store, values, deps, io }) {
|
|
@@ -160769,6 +160889,8 @@ function failureHint(command2, reason, deps, values, floatError) {
|
|
|
160769
160889
|
if (relayHint) return relayHint;
|
|
160770
160890
|
const privacyCashHint = privacyCashFailureHint(reason);
|
|
160771
160891
|
if (privacyCashHint) return privacyCashHint;
|
|
160892
|
+
const exitRelayHint = exitRelayFailureHint(reason);
|
|
160893
|
+
if (exitRelayHint) return exitRelayHint;
|
|
160772
160894
|
if (/_retry_same_request/.test(reason) && /the quote ended/.test(reason)) {
|
|
160773
160895
|
return `the quote of this request ended, so the same payment can no longer settle. Run ${CLI2} cancel with the same request id (${CLI2} help cancel): it checks on chain that nothing was paid and frees the request. Then pay again with a new --request-id`;
|
|
160774
160896
|
}
|
|
@@ -160787,6 +160909,25 @@ function failureHint(command2, reason, deps, values, floatError) {
|
|
|
160787
160909
|
}
|
|
160788
160910
|
return void 0;
|
|
160789
160911
|
}
|
|
160912
|
+
function exitRelayFailureHint(reason) {
|
|
160913
|
+
if (!/p03_unshield_relay_refused_/.test(reason) || /_retry_same_request|exit_pocket_/.test(reason)) return void 0;
|
|
160914
|
+
if (/relay_refused_exit_quote_expired/.test(reason)) {
|
|
160915
|
+
return "the exit proof's time window ended before the facilitator's exit transaction landed, and the new proof met the same; nothing was sent and the note is still yours. Rerun the same command (same --unshield-id): it makes a new proof, and it never sends twice";
|
|
160916
|
+
}
|
|
160917
|
+
if (/relay_refused_exit_anchor_not_retained/.test(reason)) {
|
|
160918
|
+
return "the pool no longer keeps the anchor this exit proof names; nothing was sent and the note is still yours. Rerun the same command after the proof's time window ends (at most 10 minutes after it was made): the wallet then makes a new proof";
|
|
160919
|
+
}
|
|
160920
|
+
if (/relay_refused_exit_replay_consumed/.test(reason)) {
|
|
160921
|
+
return `the chain shows this exit's note is already spent (an earlier exit of it landed, or another payment of this wallet spent it); nothing new was sent. ${CLI2} balance --refresh shows where the value is now`;
|
|
160922
|
+
}
|
|
160923
|
+
if (/relay_refused_exit_operator_review_required/.test(reason)) {
|
|
160924
|
+
return "the facilitator cannot tell yet whether its exit transaction for this note landed, so it holds this exit for its operator and sends nothing more. Keep the wallet folder as it is and rerun the same command later (same --unshield-id); it never sends twice";
|
|
160925
|
+
}
|
|
160926
|
+
if (/relay_refused_exit_vault_insufficient/.test(reason)) {
|
|
160927
|
+
return "the pool's vault holds less than this exit right now; nothing was sent and the note is still yours. Rerun the same command later (same --unshield-id)";
|
|
160928
|
+
}
|
|
160929
|
+
return "the facilitator's exit relay refused this exit (the code names why); nothing was sent and the note is still yours. Rerun the same command once (same --unshield-id); if the same code comes back, keep the wallet folder as it is and ask for help";
|
|
160930
|
+
}
|
|
160790
160931
|
function relayDepositFailureHint(reason) {
|
|
160791
160932
|
if (/p03_deposit_relay_approval_standing/.test(reason)) {
|
|
160792
160933
|
return `your USDC is still at the one-time address with an approval that names this deposit alone: it can only become your own note, so it is safe to leave. Rerun the same command later; it signs nothing new while the approval stands. To take the USDC back instead: ${CLI2} help deposit-reclaim`;
|