shinjuku-shield 0.3.99 → 0.3.100
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 +172 -100
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.100. The npm packages `shinjuku-shield` and
|
|
10
10
|
`shinjuku-shielded` hold the same files. The command is `shinjuku`.
|
|
11
11
|
|
|
12
12
|
## Set up
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
npx -y --no-audit --no-fund shinjuku-shield@0.3.
|
|
15
|
+
npx -y --no-audit --no-fund shinjuku-shield@0.3.100 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 --no-audit --no-fund shinjuku@0.3.
|
|
27
|
+
npm install -g --no-audit --no-fund shinjuku@0.3.100
|
|
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 -- npx -y --no-audit --no-fund shinjuku-shield@0.3.
|
|
39
|
+
claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.100 mcp
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
|
|
43
43
|
|
|
44
44
|
```json
|
|
45
|
-
{ "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.
|
|
45
|
+
{ "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.100", "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": "fe6cb068e76316bd9c4fab329dbd3586d980d69f6fe42a0f01fa1a254abb4410",
|
|
5
|
+
"bytes": 12791175,
|
|
6
|
+
"sourceCommit": "779d2665c82980faf7fbc61f8660c8f390b3884c",
|
|
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",
|
|
@@ -1297,5 +1297,5 @@
|
|
|
1297
1297
|
"version": "1.8.0"
|
|
1298
1298
|
}
|
|
1299
1299
|
],
|
|
1300
|
-
"publishedAt": "2026-10-
|
|
1300
|
+
"publishedAt": "2026-10-24T12:55:23.206Z"
|
|
1301
1301
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shinjuku-shield",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.100",
|
|
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": "fe6cb068",
|
|
7
|
+
"shinjukuSha256": "fe6cb068e76316bd9c4fab329dbd3586d980d69f6fe42a0f01fa1a254abb4410",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"author": "Shinjuku Shielded",
|
|
10
10
|
"homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
|
package/shinjuku-wallet.mjs
CHANGED
|
@@ -50934,7 +50934,7 @@ function historyRpcUrl(value) {
|
|
|
50934
50934
|
}
|
|
50935
50935
|
function historyRpcHint(reason, { signsNothing: signsNothing2 = false } = {}) {
|
|
50936
50936
|
if (/\bhistory_rpc_failed\b/.test(reason)) {
|
|
50937
|
-
return `the pool's public history from the history source (--history-rpc, or the history proxy this wallet pins for this pool: its onion in a --tor run, else its https origin) could not be used: the source did not answer, answered an error (for example JSON-RPC -32016: it stayed behind the chain for ${HISTORY_RPC_BEHIND_WAIT_MS / 1e3} s), or served history that does not match the pool
|
|
50937
|
+
return `the pool's public history from the history source (--history-rpc, or the history proxy this wallet pins for this pool: its onion in a --tor run, else its https origin) could not be used: the source did not answer, answered an error (for example JSON-RPC -32016: it stayed behind the chain for ${HISTORY_RPC_BEHIND_WAIT_MS / 1e3} s), or served history that does not match the pool state your own RPC reads, a transaction whose Ed25519 signatures do not verify, or a transaction that differs from the copy your own RPC holds (a code after history_rpc_failed names the check). The wallet never falls back to your own RPC by itself. ` + (signsNothing2 ? "This run signs nothing, so nothing changed on chain. " : "A rerun of the same command continues where it stopped and never pays twice. ") + "Rerun later, or use --history-rpc own (your RPC provider then sees that you read this pool)";
|
|
50938
50938
|
}
|
|
50939
50939
|
if (/\bhistory_rpc_invalid\b/.test(reason)) {
|
|
50940
50940
|
return "--history-rpc takes own, an https URL, or an http://<v3 onion>/ origin with --tor (outside a Tor run an onion is refused before any lookup); nothing was read or signed";
|
|
@@ -51157,8 +51157,8 @@ function connectSplit(own, historyUrl, commitment = "finalized", { pause: pause3
|
|
|
51157
51157
|
const now = Date.now();
|
|
51158
51158
|
if (behindSince === void 0) {
|
|
51159
51159
|
behindSince = now;
|
|
51160
|
-
notice(strictAfterSlot === void 0 ? `history: the history source is behind the chain (JSON-RPC -32016: it has not reached the slot your own RPC read the pool
|
|
51161
|
-
` : `history: the pool
|
|
51160
|
+
notice(strictAfterSlot === void 0 ? `history: the history source is behind the chain (JSON-RPC -32016: it has not reached the slot your own RPC read the pool state at); asking again every 0.15 s for about 2 s, then after 0.5 s, 1 s, 2 s, then every 5 s, for at most ${HISTORY_RPC_BEHIND_WAIT_MS / 1e3} s
|
|
51161
|
+
` : `history: the pool state changed after slot ${strictAfterSlot}, the newest slot the history source had reached (its history did not rebuild the pool state your own RPC reads); asking it again every 0.15 s for about 2 s, then after 0.5 s, 1 s, 2 s, then every 5 s, for at most ${HISTORY_RPC_BEHIND_WAIT_MS / 1e3} s
|
|
51162
51162
|
`);
|
|
51163
51163
|
}
|
|
51164
51164
|
const delay3 = behindWaits < HISTORY_RPC_BEHIND_FAST_TRIES ? HISTORY_RPC_BEHIND_FAST_MS : HISTORY_RPC_BEHIND_RETRY_STEPS_MS[Math.min(behindWaits - HISTORY_RPC_BEHIND_FAST_TRIES, HISTORY_RPC_BEHIND_RETRY_STEPS_MS.length - 1)];
|
|
@@ -107668,12 +107668,19 @@ var init_family = __esm({
|
|
|
107668
107668
|
});
|
|
107669
107669
|
|
|
107670
107670
|
// wallet/src/inference/models.mjs
|
|
107671
|
-
|
|
107671
|
+
function defaultMaxTokens(model) {
|
|
107672
|
+
const entry = MODELS.find((item) => item.id === model);
|
|
107673
|
+
return entry && entry.priceOutPerMAtomic * BigInt(DEFAULT_MAX_TOKENS) > FLOOR_ATOMIC * 1000000n ? SHORT_DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS;
|
|
107674
|
+
}
|
|
107675
|
+
var DEFAULT_MODEL, DEFAULT_MODEL_PAID_WORDS, DEFAULT_MAX_TOKENS, SHORT_DEFAULT_MAX_TOKENS, FLOOR_ATOMIC, MIN_MAX_TOKENS, MODELS;
|
|
107672
107676
|
var init_models = __esm({
|
|
107673
107677
|
"wallet/src/inference/models.mjs"() {
|
|
107674
107678
|
"use strict";
|
|
107675
|
-
DEFAULT_MODEL = "
|
|
107676
|
-
|
|
107679
|
+
DEFAULT_MODEL = "qwen3-8-flash";
|
|
107680
|
+
DEFAULT_MODEL_PAID_WORDS = "0.002175 USDC";
|
|
107681
|
+
DEFAULT_MAX_TOKENS = 2048;
|
|
107682
|
+
SHORT_DEFAULT_MAX_TOKENS = 512;
|
|
107683
|
+
FLOOR_ATOMIC = 10000n;
|
|
107677
107684
|
MIN_MAX_TOKENS = 256;
|
|
107678
107685
|
MODELS = Object.freeze([
|
|
107679
107686
|
Object.freeze({ id: "claude-opus-5", priceInPerMAtomic: 5000000n, priceOutPerMAtomic: 25000000n, note: "strongest; pays at most $5 in / $25 out per 1M tokens" }),
|
|
@@ -109359,7 +109366,7 @@ async function reshieldPocket({ store, connection, options }) {
|
|
|
109359
109366
|
held = 0n;
|
|
109360
109367
|
}
|
|
109361
109368
|
if (!open4 && held === 0n) {
|
|
109362
|
-
return { success: true, pocket: chosen.index, reshieldedAtomic: "0", note: "this pocket
|
|
109369
|
+
return { success: true, pocket: chosen.index, reshieldedAtomic: "0", note: "this pocket holds no USDC; nothing was sent" };
|
|
109363
109370
|
}
|
|
109364
109371
|
const depositId = open4?.depositId ?? `${prefix}${records.length + 1}`;
|
|
109365
109372
|
const proofTools = loadP03PublicPaymentProofTools(resolve20(options["proof-tools"]));
|
|
@@ -112174,7 +112181,7 @@ var init_job = __esm({
|
|
|
112174
112181
|
"a pocket of the old pool is returning its remainder into your private balance (a relayed deposit); it is then moved with your notes",
|
|
112175
112182
|
`run ${terminalCommand("move")} again in a minute; it resumes the same deposit and never sends twice`
|
|
112176
112183
|
],
|
|
112177
|
-
wallet_move_stuck_notes: ["some notes of the old pool
|
|
112184
|
+
wallet_move_stuck_notes: ["some notes of the old pool are where no payment of this wallet can take them", "tell the operator; an unshield of those notes takes them out"],
|
|
112178
112185
|
// Pool succession (succession/scheduler.mjs, WALLET-MCP-SPEC §3.3): the old pool was upgraded to a new pool.
|
|
112179
112186
|
wallet_move_pool_succeeded: [
|
|
112180
112187
|
"the facilitator upgraded this old pool to a new pool, so it takes no payment and this kind of move cannot run there; your money there is safe",
|
|
@@ -114071,16 +114078,16 @@ function pocketCarryReason(carry) {
|
|
|
114071
114078
|
if (carry && CARRY_DONE.has(carry.state)) return { done: true, waits: false, code: null, reason: "" };
|
|
114072
114079
|
if (carry?.state !== "refused") return { done: false, waits: true, code: null, reason: "the wallet has not finished its check of the pool switch" };
|
|
114073
114080
|
const code2 = String(carry.code ?? "");
|
|
114074
|
-
const reason = code2 === "p03_pocket_carry_source_fill_pending" || code2 === "p03_pocket_carry_source_payment_pending" ? "a pocket top-up or a pocket payment is not final yet" : code2 === "wallet_locked" ? "another wallet command uses the old pool's wallet now" : CARRY_WAIT_CODES.has(code2) ? "the wallet has not finished its check of the pool switch" : code2 === "p03_pocket_carry_target_not_empty" ? "the new pool's wallet already has
|
|
114081
|
+
const reason = code2 === "p03_pocket_carry_source_fill_pending" || code2 === "p03_pocket_carry_source_payment_pending" ? "a pocket top-up or a pocket payment is not final yet" : code2 === "wallet_locked" ? "another wallet command uses the old pool's wallet now" : CARRY_WAIT_CODES.has(code2) ? "the wallet has not finished its check of the pool switch" : code2 === "p03_pocket_carry_target_not_empty" ? "the new pool's wallet already has pockets of its own" : "the pocket records of the two wallets do not match";
|
|
114075
114082
|
return { done: false, waits: CARRY_WAIT_CODES.has(code2), code: code2, reason };
|
|
114076
114083
|
}
|
|
114077
114084
|
function pocketCarryText(carry) {
|
|
114078
114085
|
if (carry?.state !== "refused") return "";
|
|
114079
114086
|
const { code: code2, reason } = pocketCarryReason(carry);
|
|
114080
114087
|
if (CARRY_WAIT_CODES.has(code2)) {
|
|
114081
|
-
return ` Your
|
|
114088
|
+
return ` Your pockets stay in the old pool's records for now (${reason}). The money in them is safe. The wallet tries again by itself in a minute.`;
|
|
114082
114089
|
}
|
|
114083
|
-
return ` Your
|
|
114090
|
+
return ` Your pockets stay in the old pool's records (${reason}). The money in them is safe. Tell the operator this code: ${code2}. The wallet tries again by itself every 6 hours.`;
|
|
114084
114091
|
}
|
|
114085
114092
|
function isPermanentSuccessionCode(code2) {
|
|
114086
114093
|
const text3 = String(code2 ?? "");
|
|
@@ -120461,7 +120468,7 @@ async function readJson3(req) {
|
|
|
120461
120468
|
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
120462
120469
|
} catch {
|
|
120463
120470
|
}
|
|
120464
|
-
throw refusal2("inference_messages_invalid",
|
|
120471
|
+
throw refusal2("inference_messages_invalid", `the request body must be one JSON object, like {"model":"${DEFAULT_MODEL}","max_tokens":${DEFAULT_MAX_TOKENS},"messages":[{"role":"user","content":"hi"}]}`);
|
|
120465
120472
|
}
|
|
120466
120473
|
function usageOf(usage2) {
|
|
120467
120474
|
if (!usage2 || typeof usage2 !== "object") return void 0;
|
|
@@ -120472,8 +120479,9 @@ function usageOf(usage2) {
|
|
|
120472
120479
|
function defaultMaxTokensLine({ inference, maxPaymentAtomic }) {
|
|
120473
120480
|
const floor = BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 0n);
|
|
120474
120481
|
const prices = inference.listModels().map((model) => {
|
|
120475
|
-
const
|
|
120476
|
-
|
|
120482
|
+
const tokens = defaultMaxTokens(model.id);
|
|
120483
|
+
const output2 = (BigInt(model.priceOutPerMAtomic) * BigInt(tokens) + 999999n) / 1000000n;
|
|
120484
|
+
return `${usdcWords(output2 > floor ? output2 : floor)} on ${model.id}${tokens === DEFAULT_MAX_TOKENS ? "" : ` (max_tokens ${tokens})`}`;
|
|
120477
120485
|
});
|
|
120478
120486
|
return `a request without max_tokens gets max_tokens ${DEFAULT_MAX_TOKENS}: the AI provider's price for it is at most ${prices.join(", ")}, plus the prompt at the model's input price and the Shinjuku fee, and never more than this server's limit per request (${usdcWords(maxPaymentAtomic)})`;
|
|
120479
120487
|
}
|
|
@@ -120555,10 +120563,10 @@ function createInferenceServer({
|
|
|
120555
120563
|
const given = body.max_tokens ?? body.max_completion_tokens;
|
|
120556
120564
|
const maxTokensDefault = given === void 0 || given === null;
|
|
120557
120565
|
if (!maxTokensDefault && (!Number.isSafeInteger(given) || given < 1)) {
|
|
120558
|
-
throw refusal2("inference_max_tokens_required", `max_tokens must be a whole number of at least 1, or left out for the default ${
|
|
120566
|
+
throw refusal2("inference_max_tokens_required", `max_tokens must be a whole number of at least 1, or left out for the default ${defaultMaxTokens(body.model)}: it sets the ceiling price this request may pay; nothing was paid. Example: "max_tokens": 512`, "max_tokens");
|
|
120559
120567
|
}
|
|
120560
120568
|
const raisedFrom = !maxTokensDefault && given < MIN_MAX_TOKENS ? given : void 0;
|
|
120561
|
-
const maxTokens = maxTokensDefault ?
|
|
120569
|
+
const maxTokens = maxTokensDefault ? defaultMaxTokens(body.model) : raisedFrom === void 0 ? given : MIN_MAX_TOKENS;
|
|
120562
120570
|
if (typeof body.model !== "string" || !body.model) {
|
|
120563
120571
|
const ids = inference.listModels().map((model) => model.id).join(", ");
|
|
120564
120572
|
throw refusal2("inference_model_unknown", `model is required; pick one of: ${ids} (GET /v1/models lists them with prices)`, "model");
|
|
@@ -121409,7 +121417,7 @@ function capsLine(caps) {
|
|
|
121409
121417
|
}
|
|
121410
121418
|
function costLine(caps) {
|
|
121411
121419
|
const payment = usdc2(caps?.maxPaymentAtomic);
|
|
121412
|
-
return `Each answer the app asks for is paid from the private balance: the AI provider's price for the request's max_tokens, up front, plus the 0.001 USDC Shinjuku fee${payment === void 0 ? "" : `, never more than ${payment} USDC a call`}. What the answer does not use is not refunded when it is shorter. A request without max_tokens gets ${DEFAULT_MAX_TOKENS}; one under ${MIN_MAX_TOKENS} is raised to ${MIN_MAX_TOKENS}, so no answer is paid and empty. A request with stream: true works: the answer is paid whole, then arrives at once, at the end.`;
|
|
121420
|
+
return `Each answer the app asks for is paid from the private balance: the AI provider's price for the request's max_tokens, up front, plus the 0.001 USDC Shinjuku fee${payment === void 0 ? "" : `, never more than ${payment} USDC a call`}. What the answer does not use is not refunded when it is shorter. A request without max_tokens gets ${DEFAULT_MAX_TOKENS} (512 for claude-opus-5); one under ${MIN_MAX_TOKENS} is raised to ${MIN_MAX_TOKENS}, so no answer is paid and empty. A request with stream: true works: the answer is paid whole, then arrives at once, at the end.`;
|
|
121413
121421
|
}
|
|
121414
121422
|
function planCaps(planned, launch, given) {
|
|
121415
121423
|
const caps = { source: {} };
|
|
@@ -121694,7 +121702,7 @@ function createLocalAiTool({
|
|
|
121694
121702
|
return {
|
|
121695
121703
|
name: LOCAL_AI_TOOL_NAME,
|
|
121696
121704
|
title: "Private local AI address for agent apps",
|
|
121697
|
-
description: `Use this when the user wants a private AI address for another agent app: an OpenAI-compatible base_url and api key on this machine (${localAiBaseUrl()}). action "start" (the default) runs it in the background and answers the lines to paste into the app: OPENAI_BASE_URL, OPENAI_API_KEY and the model. It keeps running after this app or terminal closes, until action "stop" (or ${terminalCommand3(STOP_WORDS)} in a terminal) or a computer restart. action "status" says whether it runs, its caps, and what it paid. COST: each answer the app asks for is paid from the private balance: the AI provider's price for the request's max_tokens, up front (${DEFAULT_MAX_TOKENS} when the request names none), plus the 0.001 USDC Shinjuku fee; the unused rest is NOT refunded. Caps: the lowest of setup's caps (else 0.05 USDC a call and 1 USDC per server run) and this app's own spending limit; max_payment and max_session (USDC, like "0.02") can only lower them. PRIVACY: it answers only on 127.0.0.1 with its api key, keeps no prompt or answer on disk, and reaches the AI provider through Tor: the AI provider and the company that runs the model see the words, not this machine's IP. This call pays nothing itself. For one answer inside this chat, use inference_chat instead.`,
|
|
121705
|
+
description: `Use this when the user wants a private AI address for another agent app: an OpenAI-compatible base_url and api key on this machine (${localAiBaseUrl()}). action "start" (the default) runs it in the background and answers the lines to paste into the app: OPENAI_BASE_URL, OPENAI_API_KEY and the model. It keeps running after this app or terminal closes, until action "stop" (or ${terminalCommand3(STOP_WORDS)} in a terminal) or a computer restart. action "status" says whether it runs, its caps, and what it paid. COST: each answer the app asks for is paid from the private balance: the AI provider's price for the request's max_tokens, up front (${DEFAULT_MAX_TOKENS} when the request names none, 512 for claude-opus-5), plus the 0.001 USDC Shinjuku fee; the unused rest is NOT refunded. Caps: the lowest of setup's caps (else 0.05 USDC a call and 1 USDC per server run) and this app's own spending limit; max_payment and max_session (USDC, like "0.02") can only lower them. PRIVACY: it answers only on 127.0.0.1 with its api key, keeps no prompt or answer on disk, and reaches the AI provider through Tor: the AI provider and the company that runs the model see the words, not this machine's IP. This call pays nothing itself. For one answer inside this chat, use inference_chat instead.`,
|
|
121698
121706
|
inputSchema: {
|
|
121699
121707
|
type: "object",
|
|
121700
121708
|
properties: {
|
|
@@ -124763,13 +124771,13 @@ function checkMessages(messages) {
|
|
|
124763
124771
|
return { role: message.role, content: message.content };
|
|
124764
124772
|
});
|
|
124765
124773
|
}
|
|
124766
|
-
function checkMaxTokens(maxTokens) {
|
|
124767
|
-
if (maxTokens === void 0 || maxTokens === null) return BigInt(
|
|
124774
|
+
function checkMaxTokens(maxTokens, model) {
|
|
124775
|
+
if (maxTokens === void 0 || maxTokens === null) return BigInt(defaultMaxTokens(model));
|
|
124768
124776
|
if (!Number.isSafeInteger(maxTokens) || maxTokens < 1) {
|
|
124769
124777
|
fail39(
|
|
124770
124778
|
"inference_max_tokens_required",
|
|
124771
|
-
`max_tokens must be a whole number of at least 1, or left out for the default ${
|
|
124772
|
-
{ next: `pass max_tokens 512 for a short answer or
|
|
124779
|
+
`max_tokens must be a whole number of at least 1, or left out for the default ${defaultMaxTokens(model)}: it sets the most the call can cost; nothing was paid`,
|
|
124780
|
+
{ next: `pass max_tokens 512 for a short answer or ${DEFAULT_MAX_TOKENS} for a long one (256 or more: a reasoning model thinks first), or leave it out for ${defaultMaxTokens(model)}` }
|
|
124773
124781
|
);
|
|
124774
124782
|
}
|
|
124775
124783
|
return BigInt(maxTokens);
|
|
@@ -124787,7 +124795,7 @@ function ceilingOf(entry, messages, maxTokens) {
|
|
|
124787
124795
|
}
|
|
124788
124796
|
function ceilingAtomic({ model, messages, maxTokens }) {
|
|
124789
124797
|
const entry = findModel(model);
|
|
124790
|
-
return ceilingOf(entry, checkMessages(messages), checkMaxTokens(maxTokens));
|
|
124798
|
+
return ceilingOf(entry, checkMessages(messages), checkMaxTokens(maxTokens, entry.id));
|
|
124791
124799
|
}
|
|
124792
124800
|
function buildChatRequest({ model, messages, maxTokens, temperature, stream }) {
|
|
124793
124801
|
if (stream === true) {
|
|
@@ -124799,7 +124807,7 @@ function buildChatRequest({ model, messages, maxTokens, temperature, stream }) {
|
|
|
124799
124807
|
}
|
|
124800
124808
|
const entry = findModel(model);
|
|
124801
124809
|
const clean5 = checkMessages(messages);
|
|
124802
|
-
const tokens = checkMaxTokens(maxTokens);
|
|
124810
|
+
const tokens = checkMaxTokens(maxTokens, entry.id);
|
|
124803
124811
|
if (temperature !== void 0 && (typeof temperature !== "number" || !Number.isFinite(temperature) || temperature < 0 || temperature > 2)) {
|
|
124804
124812
|
fail39("inference_temperature_invalid", "temperature must be a number from 0 to 2; nothing was paid", { next: "pass temperature 0.7, or leave it out" });
|
|
124805
124813
|
}
|
|
@@ -124902,7 +124910,7 @@ async function inferenceChat({ payFetch, model, messages, maxTokens, temperature
|
|
|
124902
124910
|
const { url, init } = buildChatRequest({ model, messages, maxTokens, temperature });
|
|
124903
124911
|
const entry = findModel(model);
|
|
124904
124912
|
const clean5 = checkMessages(messages);
|
|
124905
|
-
const tokens = checkMaxTokens(maxTokens);
|
|
124913
|
+
const tokens = checkMaxTokens(maxTokens, entry.id);
|
|
124906
124914
|
const ceiling = ceilingOf(entry, clean5, tokens);
|
|
124907
124915
|
const cap = parseCap(maxPaymentAtomic);
|
|
124908
124916
|
if (ceiling > cap) {
|
|
@@ -130907,7 +130915,7 @@ ${canonical5}`;
|
|
|
130907
130915
|
store: { readWal: (name) => store.readWal(name), writeWal: (name, value) => store.writeWal(name, value) },
|
|
130908
130916
|
plan,
|
|
130909
130917
|
// No time in the stage: it reaches the host's log, and a part's time would point at its exit on chain. The answer has it.
|
|
130910
|
-
onWait: ({ part }) => onStage(`
|
|
130918
|
+
onWait: ({ part }) => onStage(`payment ${part.index}/${plan.parts.length}: waiting`),
|
|
130911
130919
|
// A late part's new time is a plan write: under the lease, so a cancel written meanwhile is kept.
|
|
130912
130920
|
reschedule: (part, notBefore) => underLease((opened) => {
|
|
130913
130921
|
store = opened;
|
|
@@ -130920,7 +130928,7 @@ ${canonical5}`;
|
|
|
130920
130928
|
store = opened;
|
|
130921
130929
|
if (partCancelled(opened, sendId, part)) return { cancelled: true };
|
|
130922
130930
|
if (partState(opened, part).state === "exited") return {};
|
|
130923
|
-
const result = await unshieldPart(opened, job, part.unshieldId, part.amountAtomic, (stage) => onStage(`
|
|
130931
|
+
const result = await unshieldPart(opened, job, part.unshieldId, part.amountAtomic, (stage) => onStage(`payment ${index}/${total}: ${stage}`));
|
|
130924
130932
|
if (result.backupPath !== void 0) backup = { backupPath: result.backupPath };
|
|
130925
130933
|
if (result.backupError !== void 0) backup = { ...backup, backupError: result.backupError };
|
|
130926
130934
|
store = session.openStore();
|
|
@@ -132906,7 +132914,7 @@ ${canonical5}`;
|
|
|
132906
132914
|
const inferenceModule = async () => ops.inference ?? await Promise.resolve().then(() => (init_inference(), inference_exports));
|
|
132907
132915
|
const proxied = () => typeof ops.proxied === "function" ? ops.proxied() : proxyProvenAt() !== void 0;
|
|
132908
132916
|
const usd = (atomic4) => `$${formatAtomic(atomic4, 6)}`;
|
|
132909
|
-
const INFERENCE_COST_WORDS = "A
|
|
132917
|
+
const INFERENCE_COST_WORDS = "A qwen3-8-flash answer (the default, 2048 tokens) paid 0.002175 USDC live: the AI provider's price plus the Shinjuku fee (0.001 USDC). The call's cap (max_payment, else the per-payment limit) must still cover the call's ceiling: max_tokens at the model's output price plus the prompt, and at least 0.01 USDC";
|
|
132910
132918
|
const paysUpToAtomic = (inference, asked) => {
|
|
132911
132919
|
try {
|
|
132912
132920
|
const extra = inference.listModels().find((model) => model.id === asked.model).priceOutPerMAtomic * 100n;
|
|
@@ -132960,7 +132968,7 @@ ${canonical5}`;
|
|
|
132960
132968
|
],
|
|
132961
132969
|
inference_model_unknown: [
|
|
132962
132970
|
"that model id is not one the AI provider serves through this wallet",
|
|
132963
|
-
"call inference_chat with no prompt or messages (it lists the model ids, free), then pass one of its ids, for example
|
|
132971
|
+
"call inference_chat with no prompt or messages (it lists the model ids, free), then pass one of its ids, for example qwen3-8-flash or claude-opus-5"
|
|
132964
132972
|
],
|
|
132965
132973
|
inference_messages_invalid: [
|
|
132966
132974
|
"messages must be a non-empty list of { role, content } with role system, user, or assistant and content text",
|
|
@@ -133005,11 +133013,11 @@ ${canonical5}`;
|
|
|
133005
133013
|
const inferenceChat2 = {
|
|
133006
133014
|
name: "inference_chat",
|
|
133007
133015
|
title: "Ask an AI model, paid privately",
|
|
133008
|
-
description: `Use this when the user wants an AI model's answer paid from the PRIVATE balance: it calls the AI provider's x402 chat endpoint (the AI provider picks the model's provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for AI answers. Only prompt (or messages) is needed: model defaults to
|
|
133016
|
+
description: `Use this when the user wants an AI model's answer paid from the PRIVATE balance: it calls the AI provider's x402 chat endpoint (the AI provider picks the model's provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for AI answers. Only prompt (or messages) is needed: model defaults to qwen3-8-flash and max_tokens to 2048 (512 for claude-opus-5). 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 qwen3-8-flash answer at 2048 tokens paid 0.002175 USDC live (the AI provider's price plus the Shinjuku fee 0.001 USDC). The call's cap (max_payment, in USDC like "0.05", or max_payment_atomic; else the per-payment limit) 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 (the AI provider'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. What max_tokens does not use 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: the AI provider and the company that runs the model see the prompt and the answer; never put keys, passphrases, or secrets in it. It runs only when this wallet runs over Tor or its own 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 the per-payment limit). When the call can cost more than that limit 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.`,
|
|
133009
133017
|
inputSchema: {
|
|
133010
133018
|
type: "object",
|
|
133011
133019
|
properties: {
|
|
133012
|
-
model: { type: "string", description: "Optional, default
|
|
133020
|
+
model: { type: "string", description: "Optional, default qwen3-8-flash. A model id (inference_chat with no prompt lists them)." },
|
|
133013
133021
|
messages: { type: "array", items: {
|
|
133014
133022
|
type: "object",
|
|
133015
133023
|
properties: { role: { type: "string", enum: ["system", "user", "assistant"] }, content: { type: "string" } },
|
|
@@ -133017,7 +133025,7 @@ ${canonical5}`;
|
|
|
133017
133025
|
additionalProperties: false
|
|
133018
133026
|
}, description: "The conversation, oldest first. Or pass prompt instead." },
|
|
133019
133027
|
prompt: { type: "string", description: "One user message (instead of messages)." },
|
|
133020
|
-
max_tokens: { type: "integer", minimum: 1, description: `Optional, default 512, at least ${INFERENCE_MIN_MAX_TOKENS}. The most output tokens; the call pays for all of them up front. A reasoning model spends its first tokens thinking, so fewer can end with no answer.` },
|
|
133028
|
+
max_tokens: { type: "integer", minimum: 1, description: `Optional, default 2048 (512 for claude-opus-5), at least ${INFERENCE_MIN_MAX_TOKENS}. The most output tokens; the call pays for all of them up front. A reasoning model spends its first tokens thinking, so fewer can end with no answer.` },
|
|
133021
133029
|
temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature; leave it out for the model's default." },
|
|
133022
133030
|
max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
|
|
133023
133031
|
max_payment_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "The most this call may pay, in atomic units; at most the spending limit." },
|
|
@@ -133040,10 +133048,11 @@ ${canonical5}`;
|
|
|
133040
133048
|
...body,
|
|
133041
133049
|
paid: false,
|
|
133042
133050
|
detail: "no prompt or messages were given, so nothing was sent or paid; these are the models and prices",
|
|
133043
|
-
next: body.ready ? "call inference_chat with prompt (model defaults to
|
|
133051
|
+
next: body.ready ? "call inference_chat with prompt (model defaults to qwen3-8-flash, max_tokens to 2048)" : body.next
|
|
133044
133052
|
});
|
|
133045
133053
|
}
|
|
133046
|
-
const
|
|
133054
|
+
const model = rawArgs.model ?? DEFAULT_MODEL;
|
|
133055
|
+
const args = { ...rawArgs, model, max_tokens: rawArgs.max_tokens ?? defaultMaxTokens(model) };
|
|
133047
133056
|
if (!proxied()) return torRefusal();
|
|
133048
133057
|
const inference = await inferenceModule();
|
|
133049
133058
|
if (args.prompt !== void 0 && args.messages !== void 0) {
|
|
@@ -133053,7 +133062,7 @@ ${canonical5}`;
|
|
|
133053
133062
|
return refuse12(
|
|
133054
133063
|
"inference_max_tokens_too_low",
|
|
133055
133064
|
`max_tokens ${args.max_tokens} is below ${INFERENCE_MIN_MAX_TOKENS}: a reasoning model (qwen3-8-flash) spends its first tokens thinking, and with max_tokens 32 or 80 it was paid and gave no answer; nothing was paid`,
|
|
133056
|
-
`call inference_chat again with max_tokens ${INFERENCE_MIN_MAX_TOKENS} or more (512
|
|
133065
|
+
`call inference_chat again with max_tokens ${INFERENCE_MIN_MAX_TOKENS} or more (512 answers a short question), or leave max_tokens out; a larger max_tokens costs little more: a qwen3-8-flash answer at 2048 tokens paid 0.002175 USDC`,
|
|
133057
133066
|
{ paid: false, maxTokens: args.max_tokens, minMaxTokens: INFERENCE_MIN_MAX_TOKENS }
|
|
133058
133067
|
);
|
|
133059
133068
|
}
|
|
@@ -136046,7 +136055,8 @@ __export(chat_exports, {
|
|
|
136046
136055
|
isProgressLine: () => isProgressLine,
|
|
136047
136056
|
knownPrivateBalance: () => knownPrivateBalance,
|
|
136048
136057
|
leastAnswerAtomic: () => leastAnswerAtomic,
|
|
136049
|
-
runChat: () => runChat
|
|
136058
|
+
runChat: () => runChat,
|
|
136059
|
+
savedBalance: () => savedBalance
|
|
136050
136060
|
});
|
|
136051
136061
|
import { randomBytes as randomBytes27 } from "node:crypto";
|
|
136052
136062
|
import { createInterface } from "node:readline";
|
|
@@ -136067,16 +136077,16 @@ function answerCostWords(entry, maxTokens) {
|
|
|
136067
136077
|
const most = leastAnswerAtomic(entry, maxTokens);
|
|
136068
136078
|
return `up to about ${usdcWords((most + 99n) / 100n * 100n)}`;
|
|
136069
136079
|
}
|
|
136070
|
-
function
|
|
136080
|
+
function savedBalance(store) {
|
|
136071
136081
|
try {
|
|
136072
136082
|
const report = computeBalance({ store, snapshotRead: readBalanceSnapshot(store), source: "local" });
|
|
136073
136083
|
const value = report.split?.shieldedAtomic;
|
|
136074
136084
|
if (typeof value === "string" && /^[0-9]{1,31}$/.test(value)) {
|
|
136075
|
-
return report.status === "current" || report.status === "empty"
|
|
136085
|
+
return { atomic: BigInt(value), current: (report.status === "current" || report.status === "empty") && !report.detail?.pendingDeposits?.length };
|
|
136076
136086
|
}
|
|
136077
136087
|
} catch {
|
|
136078
136088
|
}
|
|
136079
|
-
return
|
|
136089
|
+
return void 0;
|
|
136080
136090
|
}
|
|
136081
136091
|
function httpStatusOf(error) {
|
|
136082
136092
|
for (let item = error, depth = 0; item && depth < 4; item = item.cause, depth += 1) {
|
|
@@ -136221,6 +136231,18 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136221
136231
|
const state = { model, history: [], spent: 0n, answers: 0, empty: 0, pending: void 0, busy: false, interrupts: 0 };
|
|
136222
136232
|
const say6 = (line) => io.err(`${line}
|
|
136223
136233
|
`);
|
|
136234
|
+
const inPlace = deps.statusInPlace ?? Boolean(process.stderr.isTTY);
|
|
136235
|
+
let lastKnown;
|
|
136236
|
+
const readBalance = () => {
|
|
136237
|
+
let saved;
|
|
136238
|
+
try {
|
|
136239
|
+
saved = savedBalance(session.openStore());
|
|
136240
|
+
} catch {
|
|
136241
|
+
saved = void 0;
|
|
136242
|
+
}
|
|
136243
|
+
if (saved?.current) lastKnown = { atomic: saved.atomic, spentThen: state.spent };
|
|
136244
|
+
return saved;
|
|
136245
|
+
};
|
|
136224
136246
|
const entryOf = (id) => models.find((entry) => entry.id === id);
|
|
136225
136247
|
const tokensFor = (id, messages, limit) => {
|
|
136226
136248
|
try {
|
|
@@ -136229,17 +136251,21 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136229
136251
|
return CHAT_SHORT_TOKENS;
|
|
136230
136252
|
}
|
|
136231
136253
|
};
|
|
136254
|
+
const costWords = (id) => {
|
|
136255
|
+
const tokens = tokensFor(id, [{ role: "user", content: "hi" }], caps.answer);
|
|
136256
|
+
if (id === DEFAULT_MODEL && tokens === CHAT_MAX_TOKENS) return `about ${DEFAULT_MODEL_PAID_WORDS}, as paid in a live test`;
|
|
136257
|
+
return answerCostWords(entryOf(id), tokens);
|
|
136258
|
+
};
|
|
136232
136259
|
const costLine2 = (id) => {
|
|
136233
136260
|
try {
|
|
136234
|
-
|
|
136235
|
-
return `A short answer costs ${answerCostWords(entryOf(id), tokens)}`;
|
|
136261
|
+
return `An answer costs ${costWords(id)}`;
|
|
136236
136262
|
} catch {
|
|
136237
136263
|
return "Each answer is paid";
|
|
136238
136264
|
}
|
|
136239
136265
|
};
|
|
136240
136266
|
const questionsWords = () => {
|
|
136241
136267
|
const asked = state.answers + state.empty;
|
|
136242
|
-
return `${asked} question${asked === 1 ? "" : "s"}${state.empty ? ` (${state.empty}
|
|
136268
|
+
return `${asked} question${asked === 1 ? "" : "s"}${state.empty ? ` (${state.empty} paid, no answer)` : ""}`;
|
|
136243
136269
|
};
|
|
136244
136270
|
const left = () => caps.chat > state.spent ? caps.chat - state.spent : 0n;
|
|
136245
136271
|
const capsLine2 = () => `Caps: ${usdcWords(caps.answer)} an answer and ${usdcWords(caps.chat)} for this chat (${usdcWords(left())} left).`;
|
|
@@ -136249,7 +136275,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136249
136275
|
"Memory only: the chat writes no chat file and no log, and the wallet writes no copy of the conversation, not even encrypted: its payment records keep only the amounts. The conversation is gone when the chat ends. Let an answer arrive before you end the chat: if the chat ends while an answer is on its way, that answer is gone, and a payment already sent may not come back.",
|
|
136250
136276
|
`Cost: each answer pays the AI provider's price for up to ${CHAT_MAX_TOKENS} tokens (${CHAT_SHORT_TOKENS} for a model whose longer answer is above your cap) plus the 0.001 USDC Shinjuku fee, up front; what an answer does not use is not refunded. ${costLine2(state.model)} with ${state.model}. Each question sends the whole conversation, so a long chat costs more (/new starts again).`,
|
|
136251
136277
|
`Caps: ${usdcWords(caps.answer)} an answer and ${usdcWords(caps.chat)} for this chat${capsSourceText(caps)}`,
|
|
136252
|
-
`Commands: /new starts a new conversation, /cost shows what this chat paid, /balance shows your private balance, /model <id> changes the model (${modelIds2}), /help shows this, /exit (or Ctrl+D) ends the chat${state.pending ? ", /retry finishes the unfinished answer" : ""}.`
|
|
136278
|
+
`Commands: /new starts a new conversation, /cost shows what this chat paid, /balance shows your private balance, /model <id> changes the model (${modelIds2}), /help shows this, /exit (or /quit, Ctrl+D) ends the chat${state.pending ? ", /retry finishes the unfinished answer" : ""}.`
|
|
136253
136279
|
].join("\n");
|
|
136254
136280
|
let reader;
|
|
136255
136281
|
const onInterrupt = () => {
|
|
@@ -136276,7 +136302,8 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136276
136302
|
let held;
|
|
136277
136303
|
try {
|
|
136278
136304
|
least = leastAnswerAtomic(entryOf(state.model), CHAT_SHORT_TOKENS);
|
|
136279
|
-
|
|
136305
|
+
const saved = readBalance();
|
|
136306
|
+
held = saved?.current ? saved.atomic : void 0;
|
|
136280
136307
|
} catch {
|
|
136281
136308
|
held = void 0;
|
|
136282
136309
|
}
|
|
@@ -136286,11 +136313,8 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136286
136313
|
const runTurn = async (turn) => {
|
|
136287
136314
|
if (turn.capAtomic === void 0) {
|
|
136288
136315
|
let limit = caps.answer < left() ? caps.answer : left();
|
|
136289
|
-
|
|
136290
|
-
|
|
136291
|
-
if (held2 !== void 0 && held2 < limit) limit = held2;
|
|
136292
|
-
} catch {
|
|
136293
|
-
}
|
|
136316
|
+
const saved = readBalance();
|
|
136317
|
+
if (saved?.current && saved.atomic < limit) limit = saved.atomic;
|
|
136294
136318
|
turn.maxTokens = tokensFor(turn.model, turn.messages, limit);
|
|
136295
136319
|
let ceiling;
|
|
136296
136320
|
try {
|
|
@@ -136310,17 +136334,33 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136310
136334
|
}
|
|
136311
136335
|
turn.capAtomic = caps.answer < room ? caps.answer : room;
|
|
136312
136336
|
}
|
|
136313
|
-
say6(`(asking ${turn.model} privately over Tor; this takes a few seconds)`);
|
|
136314
136337
|
state.busy = true;
|
|
136315
136338
|
const startedAt = Date.now();
|
|
136316
136339
|
let lastLineAt = startedAt;
|
|
136340
|
+
let resent = false;
|
|
136317
136341
|
const seconds2 = () => Math.round((Date.now() - startedAt) / 1e3);
|
|
136342
|
+
const status = () => {
|
|
136343
|
+
const text3 = `(asking ${turn.model} privately over Tor: ${seconds2()} s)`;
|
|
136344
|
+
io.err(`\r\x1B[K${text3.slice(0, Math.max(20, (process.stderr.columns ?? 80) - 1))}`);
|
|
136345
|
+
};
|
|
136346
|
+
const clearStatus = () => {
|
|
136347
|
+
if (inPlace) io.err("\r\x1B[K");
|
|
136348
|
+
};
|
|
136349
|
+
if (inPlace) status();
|
|
136350
|
+
else say6(`(asking ${turn.model} privately over Tor)`);
|
|
136318
136351
|
onPhase = (phase) => {
|
|
136319
|
-
if (phase !== "seller-resend-unanswered") return;
|
|
136352
|
+
if (phase !== "seller-resend-unanswered" || resent) return;
|
|
136353
|
+
resent = true;
|
|
136320
136354
|
lastLineAt = Date.now();
|
|
136321
|
-
|
|
136355
|
+
clearStatus();
|
|
136356
|
+
say6("(the AI provider is slow to answer: the wallet asks it again with the same payment, which never pays twice)");
|
|
136357
|
+
if (inPlace) status();
|
|
136322
136358
|
};
|
|
136323
136359
|
const waiting = setInterval(() => {
|
|
136360
|
+
if (inPlace) {
|
|
136361
|
+
status();
|
|
136362
|
+
return;
|
|
136363
|
+
}
|
|
136324
136364
|
if (Date.now() - lastLineAt < STILL_WAITING_MS) return;
|
|
136325
136365
|
lastLineAt = Date.now();
|
|
136326
136366
|
say6(`(still waiting for the answer, ${seconds2()} s so far)`);
|
|
@@ -136337,10 +136377,13 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136337
136377
|
requestId: turn.requestId
|
|
136338
136378
|
});
|
|
136339
136379
|
} catch (error) {
|
|
136380
|
+
clearInterval(waiting);
|
|
136381
|
+
clearStatus();
|
|
136340
136382
|
failed(error, turn);
|
|
136341
136383
|
return;
|
|
136342
136384
|
} finally {
|
|
136343
136385
|
clearInterval(waiting);
|
|
136386
|
+
clearStatus();
|
|
136344
136387
|
onPhase = void 0;
|
|
136345
136388
|
state.busy = false;
|
|
136346
136389
|
state.interrupts = 0;
|
|
@@ -136385,17 +136428,29 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136385
136428
|
say6("Nothing was paid. The AI provider now asks a kind of payment whose record would keep your words, and a private chat never pays that way. Ask again; if this repeats, the AI provider changed how it takes payment.");
|
|
136386
136429
|
return;
|
|
136387
136430
|
}
|
|
136431
|
+
const status = httpStatusOf(error);
|
|
136432
|
+
if (providerKept(code2, status)) {
|
|
136433
|
+
const kept2 = /^[1-9][0-9]{0,30}$/.test(String(error?.paidAtomic ?? "")) ? BigInt(error.paidAtomic) : void 0;
|
|
136434
|
+
if (kept2 !== void 0) state.spent += kept2;
|
|
136435
|
+
state.empty += 1;
|
|
136436
|
+
const what = `answered with an error (HTTP ${status})`;
|
|
136437
|
+
const others = models.filter((entry) => entry.id !== turn.model).map((entry) => entry.id).join(", ");
|
|
136438
|
+
say6(`Your payment${kept2 === void 0 ? "" : ` (${usdcWords(kept2)})`} went to the AI provider, but it ${what} and sent no answer. That payment does not come back. Ask again (a new question pays again), or pick another model with /model (${others}). This chat paid ${usdcWords(state.spent)} so far.`);
|
|
136439
|
+
return;
|
|
136440
|
+
}
|
|
136441
|
+
if (INSUFFICIENT2.test(code2 ?? "")) {
|
|
136442
|
+
state.pending = turn;
|
|
136443
|
+
say6(`Your private balance is too low for this answer. Nothing was paid. Add funds in another terminal: ${commandLine("add-funds")} (it asks the amount, from 0.01 USDC), or ask your agent to add funds. When it says ready, type /retry: it asks the same question again.`);
|
|
136444
|
+
return;
|
|
136445
|
+
}
|
|
136388
136446
|
const again = surelyNothing ? "ask again" : "type /retry (it finishes the same payment and never pays twice)";
|
|
136389
136447
|
if (!surelyNothing) state.pending = turn;
|
|
136390
136448
|
const paidWords = surelyNothing ? "Nothing was paid." : "It may be paid already.";
|
|
136391
136449
|
if (/seller_unanswered_paid/.test(code2 ?? "")) {
|
|
136392
|
-
const
|
|
136393
|
-
const what = status >= 400 ? `answered with an error (HTTP ${status}) and no answer` : status === 202 ? "has not sent the answer yet" : "did not answer";
|
|
136450
|
+
const what = status === 202 ? "has not sent the answer yet" : "did not answer";
|
|
136394
136451
|
say6(`Your payment reached the AI provider, but it ${what}. Type /retry: it asks again with the same payment and never pays twice.`);
|
|
136395
136452
|
} else if (code2 === "inference_tor_required" || code2 === "ECONNREFUSED" || proxyRefusedConnection(error)) {
|
|
136396
136453
|
say6(`Your private connection (Tor) stopped answering, so the question could not go out. ${paidWords} Start Tor again, then ${again}.`);
|
|
136397
|
-
} else if (INSUFFICIENT2.test(code2 ?? "")) {
|
|
136398
|
-
say6(`Your private balance is too low for this answer. ${paidWords} Add funds in another terminal: ${commandLine("add-funds")} (it asks the amount, from 0.01 USDC), or ask your agent to add funds. Then ${again}.`);
|
|
136399
136454
|
} else if (CAP_CODES.test(code2 ?? "")) {
|
|
136400
136455
|
say6(`This answer costs more than the chat's caps allow. ${paidWords} ${capsLine2()} End the chat (/exit) and start it again with higher caps when you want more: ${commandLine(`chat --max-payment ${higherCap(caps.answer)} --max-session ${higherCap(caps.chat)}`)}`);
|
|
136401
136456
|
} else if (code2 === "inference_no_provider") {
|
|
@@ -136411,7 +136466,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136411
136466
|
const command2 = async (text3) => {
|
|
136412
136467
|
const [name, ...rest] = text3.split(/\s+/);
|
|
136413
136468
|
const arg = rest.join(" ").trim();
|
|
136414
|
-
if (name === "/exit") return true;
|
|
136469
|
+
if (name === "/exit" || name === "/quit" || name === "/q") return true;
|
|
136415
136470
|
if (name === "/new") {
|
|
136416
136471
|
state.history = [];
|
|
136417
136472
|
state.pending = void 0;
|
|
@@ -136420,13 +136475,19 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136420
136475
|
} else if (name === "/cost") {
|
|
136421
136476
|
say6(`This chat paid ${usdcWords(state.spent)} for ${questionsWords()}. ${capsLine2()}`);
|
|
136422
136477
|
} else if (name === "/balance") {
|
|
136423
|
-
|
|
136424
|
-
|
|
136425
|
-
|
|
136426
|
-
|
|
136427
|
-
|
|
136478
|
+
const saved = readBalance();
|
|
136479
|
+
const again = `A deposit since then is not in this number: ${commandLine("balance")} in another terminal reads it again.`;
|
|
136480
|
+
if (saved?.current) {
|
|
136481
|
+
say6(`Your private balance is ${usdcWords(saved.atomic)}.`);
|
|
136482
|
+
} else if (lastKnown) {
|
|
136483
|
+
const since = state.spent - lastKnown.spentThen;
|
|
136484
|
+
const now = lastKnown.atomic > since ? lastKnown.atomic - since : 0n;
|
|
136485
|
+
say6(`Your private balance is about ${usdcWords(now)}: ${usdcWords(lastKnown.atomic)} at its last read, less the ${usdcWords(since)} this chat paid since then. ${again}`);
|
|
136486
|
+
} else if (saved) {
|
|
136487
|
+
say6(`Your private balance was ${usdcWords(saved.atomic)} at its last read; this chat paid ${usdcWords(state.spent)} since it started. ${again}`);
|
|
136488
|
+
} else {
|
|
136489
|
+
say6(`The private balance is not known in this chat yet. ${commandLine("balance")} in another terminal shows it.`);
|
|
136428
136490
|
}
|
|
136429
|
-
say6(line ?? `The private balance is not known in this chat yet. ${commandLine("balance")} in another terminal shows it.`);
|
|
136430
136491
|
} else if (name === "/model") {
|
|
136431
136492
|
const found = models.find((entry) => entry.id === arg);
|
|
136432
136493
|
if (found) {
|
|
@@ -136435,7 +136496,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136435
136496
|
} else {
|
|
136436
136497
|
say6([
|
|
136437
136498
|
`${arg ? `There is no model ${JSON.stringify(arg).slice(0, 80)}. ` : ""}Model now: ${state.model}. Pick one with /model <id>:`,
|
|
136438
|
-
...models.map((entry) => ` ${entry.id}${entry.note ? ` ${entry.note}` : ""} (${costLine2(entry.id).replace(/^
|
|
136499
|
+
...models.map((entry) => ` ${entry.id}${entry.note ? ` ${entry.note}` : ""} (${costLine2(entry.id).replace(/^An answer costs /, "an answer ")})`)
|
|
136439
136500
|
].join("\n"));
|
|
136440
136501
|
}
|
|
136441
136502
|
} else if (name === "/retry") {
|
|
@@ -136476,7 +136537,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136476
136537
|
await session.close();
|
|
136477
136538
|
}
|
|
136478
136539
|
}
|
|
136479
|
-
var DEFAULT_MODEL2, CHAT_DEFAULT_MODEL, CHAT_MAX_TOKENS, CHAT_SHORT_TOKENS, DEFAULT_ANSWER_CAP_ATOMIC, DEFAULT_CHAT_CAP_ATOMIC, CHEAPEST_MODEL, STILL_WAITING_MS, higherCap, POSITIVE_ATOMIC9, SHINJUKU_FEE_ATOMIC, SHORT_QUESTION_TOKENS, PASTE_JOIN_MS, INSUFFICIENT2, CAP_CODES, WAIT_CODES, NO_ANSWER_CODES, NOTHING_PAID, CAP_FROM, PROGRESS_LINE, SHOWN_PROGRESS, isProgressLine, fail46, nothingPaid;
|
|
136540
|
+
var DEFAULT_MODEL2, CHAT_DEFAULT_MODEL, CHAT_MAX_TOKENS, CHAT_SHORT_TOKENS, DEFAULT_ANSWER_CAP_ATOMIC, DEFAULT_CHAT_CAP_ATOMIC, CHEAPEST_MODEL, STILL_WAITING_MS, providerKept, higherCap, POSITIVE_ATOMIC9, SHINJUKU_FEE_ATOMIC, SHORT_QUESTION_TOKENS, PASTE_JOIN_MS, INSUFFICIENT2, CAP_CODES, WAIT_CODES, NO_ANSWER_CODES, NOTHING_PAID, CAP_FROM, PROGRESS_LINE, SHOWN_PROGRESS, SELF_PAY_LINE, isProgressLine, fail46, nothingPaid;
|
|
136480
136541
|
var init_chat = __esm({
|
|
136481
136542
|
"wallet/src/inference/chat.mjs"() {
|
|
136482
136543
|
"use strict";
|
|
@@ -136499,6 +136560,7 @@ var init_chat = __esm({
|
|
|
136499
136560
|
DEFAULT_CHAT_CAP_ATOMIC = 1000000n;
|
|
136500
136561
|
CHEAPEST_MODEL = "deepseek-v4-1-flash";
|
|
136501
136562
|
STILL_WAITING_MS = 2e4;
|
|
136563
|
+
providerKept = (code2, status) => code2 === "p03_float_seller_kept_payment" || /seller_unanswered_paid/.test(code2 ?? "") && Number.isInteger(status) && status >= 500;
|
|
136502
136564
|
higherCap = (atomic4) => usdcFlagText((BigInt(atomic4) * 2n + 9999n) / 10000n * 10000n);
|
|
136503
136565
|
POSITIVE_ATOMIC9 = /^[1-9][0-9]{0,30}$/;
|
|
136504
136566
|
SHINJUKU_FEE_ATOMIC = 1000n;
|
|
@@ -136510,9 +136572,10 @@ var init_chat = __esm({
|
|
|
136510
136572
|
NO_ANSWER_CODES = /^inference_(upstream_failed|response_invalid)$/;
|
|
136511
136573
|
NOTHING_PAID = /\bnothing was (?:sent or )?paid\b|\bnothing was paid or signed\b/i;
|
|
136512
136574
|
CAP_FROM = { line: "from this command line", setup: "set in setup", default: "the default" };
|
|
136513
|
-
PROGRESS_LINE = /^(?:tor|rpc|history|memo service): |^reading \d+ memos? from the memo service|^reading your private balance again\b|^
|
|
136575
|
+
PROGRESS_LINE = /^(?:tor|rpc|history|memo service): |^reading \d+ memos? from the memo service|^reading your private balance again\b|^getting your balance ready: moving \S+ USDC of one deposit\b/;
|
|
136514
136576
|
SHOWN_PROGRESS = new RegExp(`\\bWARNING\\b|\\bminutes?\\b|${TOR_NOTICE_LINE.source}`);
|
|
136515
|
-
|
|
136577
|
+
SELF_PAY_LINE = /^getting your balance ready: moving \S+ USDC of one deposit\b/;
|
|
136578
|
+
isProgressLine = (line) => PROGRESS_LINE.test(line) && (SELF_PAY_LINE.test(line) || !SHOWN_PROGRESS.test(line) && !TOR_TROUBLE_LINE.test(line));
|
|
136516
136579
|
fail46 = (deps, message) => typeof deps.publicFailure === "function" ? deps.publicFailure(message) : new Error(message);
|
|
136517
136580
|
nothingPaid = (error) => error?.name === "InferenceError" && !error.details?.receipt || error?.name === "SpendRefusalError" || NOTHING_PAID.test(String(error?.message ?? ""));
|
|
136518
136581
|
}
|
|
@@ -157682,7 +157745,7 @@ async function main5(args = process.argv.slice(2), { output: output2 = (result)
|
|
|
157682
157745
|
timing: createTimingLog(`${walletPath2}.timing.log`, { component: "payer" })
|
|
157683
157746
|
};
|
|
157684
157747
|
}
|
|
157685
|
-
progress2(`
|
|
157748
|
+
progress2(`getting your balance ready: moving ${usdc9(amountAtomic)} USDC of one deposit inside your private balance (about 2 minutes; it waits for finality)`);
|
|
157686
157749
|
try {
|
|
157687
157750
|
const paid = await selfPay({
|
|
157688
157751
|
store,
|
|
@@ -157714,7 +157777,7 @@ async function main5(args = process.argv.slice(2), { output: output2 = (result)
|
|
|
157714
157777
|
} catch (error) {
|
|
157715
157778
|
if (!DEAD.test(String(error?.message))) throw error;
|
|
157716
157779
|
store.writeWal(`self-pay-${id}`, { ...store.readWal(`self-pay-${id}`), state: "abandoned" });
|
|
157717
|
-
progress2(`step
|
|
157780
|
+
progress2(`that step never landed (nothing was spent); run ${commandLine("balance --refresh")}, then ${commandLine("prepare")} again: it moves that deposit in a new step`);
|
|
157718
157781
|
return { amountAtomic, state: "abandoned" };
|
|
157719
157782
|
}
|
|
157720
157783
|
};
|
|
@@ -160734,11 +160797,16 @@ var ROUTE_LABEL = Object.freeze({
|
|
|
160734
160797
|
changenow: "a ChangeNOW swap",
|
|
160735
160798
|
"privacy-cash": "your own wallet through the Privacy Cash crowd"
|
|
160736
160799
|
});
|
|
160800
|
+
var SETUP_LINES_BELOW = "the setup lines below";
|
|
160737
160801
|
function privacyCost(plan) {
|
|
160738
160802
|
const route = plan.route === "exchange" ? "the wallet or exchange you send from sees the amount and the one-time address, so it can link you to this deposit (an exchange knows who you are); the chain shows only that sender paying a new address" : plan.route === "changenow" ? "ChangeNOW sees the amount, the one-time address, and what you paid with (Monero hides the sender; BTC or ETH can be traced to you); the chain shows only ChangeNOW paying a new address" : `the chain shows your --from-key-file wallet paying into Privacy Cash and, at least a day and 10 deposits later, Privacy Cash paying a new address; ${plan.crowd?.via === "local" ? "Privacy Cash's server sees neither step (--via local), and the chain can pair the two only by amount and time among the deposits between them" : "Privacy Cash's server sees both steps and can link them (--via sdk; --via local avoids that)"}`;
|
|
160739
160803
|
const split5 = splitOf(plan) > 1 ? `. ${plan.parts ? "With --split auto" : `With --split ${splitOf(plan)}`}: ${partsText(plan)}, each amount public; the RPC reads the ${splitOf(plan)} one-time addresses one after another, and the deposits land close together, so both can show they are one depositor's (about what one deposit of the total shows); the facilitator pays the fee and rent of each, and each takes one of the pool's deposit places. Your payments still prove under the same per-epoch anchor as every other payment` : "";
|
|
160740
160804
|
return `${route}. The amount and time of every deposit are public, and Shinjuku's server sees them too; while few other people add money, the time can still link this deposit to your later payments${split5}`;
|
|
160741
160805
|
}
|
|
160806
|
+
function privacyShort(plan) {
|
|
160807
|
+
if (plan.route !== "exchange" || splitOf(plan) > 1) return privacyCost(plan);
|
|
160808
|
+
return "the wallet or exchange you send from sees the amount and this address (an exchange knows who you are). The amount and time are public, and Shinjuku's server sees them; while few people add money, the time can link this deposit to your later payments";
|
|
160809
|
+
}
|
|
160742
160810
|
function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
160743
160811
|
const decimals = Number(store.state.decimals ?? 6);
|
|
160744
160812
|
const stage = currentStage(store, plan);
|
|
@@ -160796,7 +160864,7 @@ function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
|
160796
160864
|
view2.balance = balance ?? null;
|
|
160797
160865
|
const pockets = balance?.pocketsAtomic && balance.pocketsAtomic !== "0" ? `, pockets ${usdc5(balance.pocketsAtomic, decimals)}` : "";
|
|
160798
160866
|
view2.status = `ready since ${plan.done?.ready ?? iso2(now)}: ` + (balance?.total ? `total ${balance.total} USDC (shielded ${balance.shinjukuShielded}${pockets})` : `balance ${balance?.status ?? "unknown"} (${cli()} balance --pool ${store.state.pool} says why)`) + (split5 > 1 && Number.isSafeInteger(balance?.fundedAccounts) ? `; payments without waiting this epoch: up to ${balance.fundedAccounts} (one per funded deposit; ${cli()} balance says why)` : "");
|
|
160799
|
-
view2.next = process.env.SHINJUKU_WALLET_SETUP_NEXT_BY_PARENT === "1" ?
|
|
160867
|
+
view2.next = process.env.SHINJUKU_WALLET_SETUP_NEXT_BY_PARENT === "1" ? SETUP_LINES_BELOW : `
|
|
160800
160868
|
Chat privately in this terminal: ${commandLine("chat")}
|
|
160801
160869
|
Use private AI from other apps: ${commandLine("infer start")}
|
|
160802
160870
|
In your agent app, ask "what is my balance" or ask it to pay an x402 URL.
|
|
@@ -160828,7 +160896,8 @@ function fundsSummary(view2) {
|
|
|
160828
160896
|
return [
|
|
160829
160897
|
view2.ready ? `add-funds ${view2.fundsId} is ready: ${view2.amount} was added to your private balance${total}.` : `add-funds ${view2.fundsId} is not ready yet: ${status}.`,
|
|
160830
160898
|
// 0.3.97 T6: a next step that ends with a command line gets no "." after it. A block of lines starts on its own line.
|
|
160831
|
-
|
|
160899
|
+
// S3 (mcp56, published 0.3.99): inside setup, setup's own Next line follows, so this one is not printed.
|
|
160900
|
+
...view2.next === SETUP_LINES_BELOW ? [] : [`Next:${view2.next.startsWith("\n") ? "" : " "}${sentence(view2.next)}`]
|
|
160832
160901
|
].join("\n");
|
|
160833
160902
|
}
|
|
160834
160903
|
function fundsOutput(values, deps) {
|
|
@@ -160857,7 +160926,7 @@ function fundsStatusSummary(view2) {
|
|
|
160857
160926
|
"For the whole status as JSON, add --json (or send this command's output to a file or a program)."
|
|
160858
160927
|
].join("\n");
|
|
160859
160928
|
}
|
|
160860
|
-
var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |shinjuku(?:-wallet)?\b.*: waiting for funds, nothing failed \(|hint: |Help: |
|
|
160929
|
+
var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |shinjuku(?:-wallet)?\b.*: waiting for funds, nothing failed \(|hint: |Help: |[Nn]ext: |Examples?:|Every flag: | shinjuku(?:-wallet)? | npx -y --no-audit --no-fund shinjuku-shield(?:@\S+)? )/;
|
|
160861
160930
|
var STEP_TOR_LINE = /^(?:tor|proxy): /;
|
|
160862
160931
|
var DENOMINATION_WARNING = /^warning: no pool denomination is configured\b/;
|
|
160863
160932
|
async function runStep(ctx, argv2) {
|
|
@@ -161547,7 +161616,9 @@ async function addFunds(values, deps) {
|
|
|
161547
161616
|
plan,
|
|
161548
161617
|
proxyArgs: proxy.args,
|
|
161549
161618
|
childEnv: { ...proxy.env, [PASSPHRASE_ENV]: passphrase2, [RPC_ENV2]: rpcUrl },
|
|
161550
|
-
|
|
161619
|
+
// A1: a quiet terminal run names no plan id and no stage (funds-status and --verbose do).
|
|
161620
|
+
say: (stage, text3) => io.err(ctx.quiet ? `add-funds: ${text3.replace(STEP_WORDS, "")}
|
|
161621
|
+
` : `add-funds ${plan.fundsId} [${stage}] ${text3}
|
|
161551
161622
|
`),
|
|
161552
161623
|
mark: (stage) => {
|
|
161553
161624
|
if (plan.done?.[stage]) return;
|
|
@@ -161556,12 +161627,12 @@ async function addFunds(values, deps) {
|
|
|
161556
161627
|
}
|
|
161557
161628
|
});
|
|
161558
161629
|
const parts = splitOf(plan) > 1 ? ` in ${partsText(plan)}, each from its own one-time address (${splitOf(plan)} payments in a row without waiting for a pool anchor)` : "";
|
|
161559
|
-
ctx.say("start", `${resolved.created ? "new" : "continues"}: ${usdc5(plan.amountAtomic)} USDC${parts} from ${ROUTE_LABEL[plan.route]}; stages ${stagesOf(plan).join(" > ")}. Ctrl-C is safe at any point: run the same command again to continue.`);
|
|
161630
|
+
ctx.say("start", ctx.quiet ? `${resolved.created ? "adds" : "continues to add"} ${usdc5(plan.amountAtomic)} USDC${parts} from ${ROUTE_LABEL[plan.route]}. Ctrl-C is safe at any point: run the same command again to continue.` : `${resolved.created ? "new" : "continues"}: ${usdc5(plan.amountAtomic)} USDC${parts} from ${ROUTE_LABEL[plan.route]}; stages ${stagesOf(plan).join(" > ")}. Ctrl-C is safe at any point: run the same command again to continue.`);
|
|
161560
161631
|
if (resolved.routeChanged) {
|
|
161561
161632
|
const address5 = relayedDeposit2(store, plan)?.relay?.owner;
|
|
161562
161633
|
ctx.say("start", `the route changes from ${resolved.routeChanged.from} to ${resolved.routeChanged.to}${address5 ? `; the one-time address stays ${address5}` : ""}${address5 && resolved.routeChanged.to === "privacy-cash" ? " (it was read when it was recorded: an RPC operator can pair that read with a Privacy Cash deposit made soon after it)" : ""}`);
|
|
161563
161634
|
}
|
|
161564
|
-
ctx.say("start", `privacy: ${privacyCost(plan)}`);
|
|
161635
|
+
ctx.say("start", `privacy: ${ctx.quiet ? privacyShort(plan) : privacyCost(plan)}`);
|
|
161565
161636
|
const markDone = () => {
|
|
161566
161637
|
const stage = currentStage(store, plan);
|
|
161567
161638
|
for (const done of stagesOf(plan)) {
|
|
@@ -161598,7 +161669,7 @@ async function addFunds(values, deps) {
|
|
|
161598
161669
|
}
|
|
161599
161670
|
const label = outcome.label ?? next ?? stage;
|
|
161600
161671
|
if (outcome.kind === "stop") {
|
|
161601
|
-
const said = outcome.tries ? [] : (outcome.run?.held ?? []).filter((text3) => !/^(Help|
|
|
161672
|
+
const said = outcome.tries ? [] : (outcome.run?.held ?? []).filter((text3) => !/^(Help|[Nn]ext): /.test(text3));
|
|
161602
161673
|
if (!said.length && outcome.run?.last && !outcome.tries) said.push(outcome.run.last);
|
|
161603
161674
|
for (const text3 of said) io.err(` ${text3}
|
|
161604
161675
|
`);
|
|
@@ -162045,8 +162116,9 @@ var GUIDE = Object.freeze({
|
|
|
162045
162116
|
});
|
|
162046
162117
|
var WALLET_FILE = `"$SHIELDED_WALLET_HOME/${GUIDE.pool}/wallet.json"`;
|
|
162047
162118
|
var FROM_SETUP = " (after setup, config.json gives it: no need to type it)";
|
|
162048
|
-
var SETUP_NEXT = `
|
|
162049
|
-
${CLI_WORD}
|
|
162119
|
+
var SETUP_NEXT = `chat privately with an AI in this terminal:
|
|
162120
|
+
${CLI_WORD} chat
|
|
162121
|
+
Or open your agent app and ask "what is my balance".`;
|
|
162050
162122
|
var UNSHIELD_EXAMPLE = `${CLI_WORD} unshield --amount 0.5 --to <your wallet address>`;
|
|
162051
162123
|
function hintForThisRun(text3, values) {
|
|
162052
162124
|
let hint = String(text3);
|
|
@@ -163953,7 +164025,8 @@ async function runSetupCommand(values, deps) {
|
|
|
163953
164025
|
const asksOwnFirst = interactive && Boolean(old) && terminal && !ownAddress;
|
|
163954
164026
|
io.out([
|
|
163955
164027
|
...introduced ? [] : [
|
|
163956
|
-
|
|
164028
|
+
// S2 (mcp56 on the published 0.3.99): the help's words (shortHelpText), not "x402 URLs".
|
|
164029
|
+
"Shinjuku Shield: your private wallet on this computer. It pays for AI answers and paid web services from your private balance.",
|
|
163957
164030
|
"Setup makes the wallet on this machine (your keys never leave it), downloads and checks the proof tools, and writes config.json.",
|
|
163958
164031
|
// 0.3.97 T4, live proof 2026-10-24: the command on its own line.
|
|
163959
164032
|
"An agent app then starts it with this command:",
|
|
@@ -163987,10 +164060,6 @@ async function runSetupCommand(values, deps) {
|
|
|
163987
164060
|
const tor = String(await deps.ask("Send every request through Tor? It hides your IP address; if Tor is not running, setup gives the one command that starts it. [Y/n] ")).trim();
|
|
163988
164061
|
options.tor = !/^n(o)?$/i.test(tor);
|
|
163989
164062
|
}
|
|
163990
|
-
if (privateInference === void 0) {
|
|
163991
|
-
const answer = String(await deps.ask(`Set your LLM to private inference now? ${PRIVATE_INFERENCE_WHY} [y/N] `)).trim();
|
|
163992
|
-
privateInference = /^y(es)?$/i.test(answer);
|
|
163993
|
-
}
|
|
163994
164063
|
if (!ownAddress && terminal) ownAddress = await askOwnAddress(deps);
|
|
163995
164064
|
} else if (asksOwnFirst) {
|
|
163996
164065
|
ownAddress = await askOwnAddress(deps, old.unshieldTo ?? []);
|
|
@@ -164821,8 +164890,8 @@ var COMMANDS = [
|
|
|
164821
164890
|
flags: [
|
|
164822
164891
|
flag2("--prompt", "<text>", 'what to ask; write - to read it from stdin (the private form), or write it alone, in quotes (shinjuku infer "Summarize x402 in one line" ...)', { required: true }),
|
|
164823
164892
|
flag2("--model", "<id>", `the model (default ${DEFAULT_MODEL2}): ${INFER_MODEL_IDS.join(", ")} (${CLI_WORD} infer models lists their prices)`),
|
|
164824
|
-
flag2("--max-tokens", "<n>", "the longest answer to pay for, in tokens; default 512 (give 256 or more; 512 answers a short question: a reasoning model like qwen3-8-flash thinks first, and with fewer tokens it can stop before it answers, still paid); it sets the most this request can cost", { check: "count" }),
|
|
164825
|
-
flag2("--max-payment", "<usdc>",
|
|
164893
|
+
flag2("--max-tokens", "<n>", "the longest answer to pay for, in tokens; default 2048, 512 for claude-opus-5 (give 256 or more; 512 answers a short question: a reasoning model like qwen3-8-flash thinks first, and with fewer tokens it can stop before it answers, still paid); it sets the most this request can cost", { check: "count" }),
|
|
164894
|
+
flag2("--max-payment", "<usdc>", `the most this request may pay in all, in USDC (0.02 = 2 cents). It must be at least 0.01: the wallet never sets a call's ceiling below the AI provider's 0.01 USDC floor on its standard offer, and a call on that offer also pays Shinjuku's fee, so 0.02 covers it. What a call pays is the AI provider's quote for --max-tokens plus Shinjuku's fee: a ${DEFAULT_MODEL2} answer (the default, ${DEFAULT_MAX_TOKENS} tokens) paid ${DEFAULT_MODEL_PAID_WORDS} live (the AI provider's price plus the 0.001 USDC fee) on the offer the wallet pays, 0.01 USDC plus the fee when the AI provider offers only its standard payment. A higher ceiling is refused before anything is paid. Default: the cap setup recorded, else 0.05`, { check: "atomic", usdc: true }),
|
|
164826
164895
|
flag2("--max-session", "<usdc>", "the most this run may pay in total, in USDC; at least --max-payment. Default: the cap setup recorded, else 1", { check: "atomic", usdc: true }),
|
|
164827
164896
|
flag2("--request-id", "<id>", "your stable name for this request; default infer-<random>, printed on stderr before any payment. Rerun with the same id to resume a stopped run: it never pays twice"),
|
|
164828
164897
|
bool2("--json", "print one JSON line (answer, model, usage, paidAtomic, requestId, receipt) instead of the answer text"),
|
|
@@ -164861,7 +164930,7 @@ var COMMANDS = [
|
|
|
164861
164930
|
flags: [bool2("--json", "print the list as one JSON line (prices in atomic USDC per 1M tokens)")],
|
|
164862
164931
|
examples: [`${CLI_WORD} infer models`, `${CLI_WORD} infer models --json`],
|
|
164863
164932
|
notes: [`${INFER_COST_LINE}.`],
|
|
164864
|
-
next: `ask one: echo "Summarize x402 in one line" | ${CLI_WORD} infer - --model
|
|
164933
|
+
next: `ask one: echo "Summarize x402 in one line" | ${CLI_WORD} infer - --model ${DEFAULT_MODEL2}`,
|
|
164865
164934
|
run: (args, values, deps, io) => deps.inferModelsMain(values, { ...deps, io })
|
|
164866
164935
|
},
|
|
164867
164936
|
{
|
|
@@ -164876,12 +164945,12 @@ var COMMANDS = [
|
|
|
164876
164945
|
],
|
|
164877
164946
|
examples: [
|
|
164878
164947
|
`${CLI_WORD} infer serve --max-payment 0.02 --max-session 0.2 ${PAYER_SESSION_BASE} --tor`,
|
|
164879
|
-
`curl http://127.0.0.1:11402/v1/chat/completions -H "Authorization: Bearer <api key it printed>" -H "Content-Type: application/json" -d '{"model":"
|
|
164948
|
+
`curl http://127.0.0.1:11402/v1/chat/completions -H "Authorization: Bearer <api key it printed>" -H "Content-Type: application/json" -d '{"model":"${DEFAULT_MODEL2}","max_tokens":${DEFAULT_MAX_TOKENS},"messages":[{"role":"user","content":"Say hello"}]}'`
|
|
164880
164949
|
],
|
|
164881
164950
|
notes: [
|
|
164882
164951
|
"Any OpenAI client works: set base_url http://127.0.0.1:11402/v1 and the api key it prints at start (required: a request without it gets 401). GET /v1/models lists the models with prices.",
|
|
164883
164952
|
"Send an Idempotency-Key header to make a resend of the same request resume the same payment instead of paying again.",
|
|
164884
|
-
`A request without max_tokens gets max_tokens 512 (max_tokens sets the most a request can cost), and stream: true is answered as a stream of the paid whole answer (the text comes at the end). Models: ${INFER_MODEL_IDS.join(", ")}.`,
|
|
164953
|
+
`A request without max_tokens gets max_tokens ${DEFAULT_MAX_TOKENS}, 512 for claude-opus-5 (max_tokens sets the most a request can cost), and stream: true is answered as a stream of the paid whole answer (the text comes at the end). Models: ${INFER_MODEL_IDS.join(", ")}.`,
|
|
164885
164954
|
"It runs only with --tor (or --proxy <url>), so the AI provider never sees your IP address. Web pages cannot call it: a request with an Origin header is refused.",
|
|
164886
164955
|
`${INFER_COST_LINE}. Each answer carries a shinjuku field with the amount paid and what this server has spent.`,
|
|
164887
164956
|
"Ctrl+C stops it; stderr then says what it paid in all.",
|
|
@@ -164961,7 +165030,7 @@ var COMMANDS = [
|
|
|
164961
165030
|
summary: "Chat privately with an AI model in this terminal: each answer is paid from your private balance, behind Tor.",
|
|
164962
165031
|
env: [],
|
|
164963
165032
|
flags: [
|
|
164964
|
-
flag2("--model", "<id>", `the model to start with (default
|
|
165033
|
+
flag2("--model", "<id>", `the model to start with (default ${DEFAULT_MODEL2}): ${INFER_MODEL_IDS.join(", ")}`),
|
|
164965
165034
|
flag2("--max-payment", "<usdc>", "the most one answer may pay, in USDC; default: the cap setup recorded, else 0.05", { check: "atomic", usdc: true }),
|
|
164966
165035
|
flag2("--max-session", "<usdc>", "the most this chat may pay in all, in USDC; default: the cap setup recorded, else 1", { check: "atomic", usdc: true }),
|
|
164967
165036
|
bool2("--verbose", "also show the wallet's own progress lines between the answers (Tor, the RPC relay, the pool history, the memo reads); without it the chat shows only the conversation, what each answer paid, and every refusal or failure"),
|
|
@@ -165048,7 +165117,7 @@ var COMMANDS = [
|
|
|
165048
165117
|
flag2("--max-unshield", "<usdc>", "optional: the mcp --max-unshield cap, in USDC (one wallet_unshield send, and this wallet in any 24 hours)", { check: "atomic", usdc: true }),
|
|
165049
165118
|
bool2("--prefer-tor", "record Tor for mcp: it then runs behind Tor (needs Tor with HTTPTunnelPort 127.0.0.1:9080); mcp --no-tor skips it for one run. Setup itself then runs behind Tor too. setup --tor does the same: it runs setup behind Tor and records Tor for mcp"),
|
|
165050
165119
|
bool2("--no-tor", "run this setup without Tor although Tor is recorded or asked for (the facilitator then sees this machine's IP address download the release and the proof tools); config.json still records Tor. With --tor it only stops --tor from recording Tor (the run itself still goes over Tor)"),
|
|
165051
|
-
bool2("--private-inference",
|
|
165120
|
+
bool2("--private-inference", "records that your agent buys AI answers from the AI provider, paid from your private balance over Tor. Records inference.enabled and Tor (as --prefer-tor) in config.json, and prints the path to the first private call. Without it, --yes keeps what config.json records"),
|
|
165052
165121
|
flag2("--own-address", "<address>", `optional, in your own terminal only (the same as the answer to setup's address question): your own wallet address (Phantom, Solflare, an exchange's deposit address), for sending money back to yourself: the MCP sends to it without asking each time (as mcp --unshield-to), also in an agent app that cannot show a question. Not this wallet's own address. Refused in a script, a pipe, or an agent's tool (no terminal), also with --yes: an agent must never approve an address. A rerun adds it to the ones config.json records (at most ${MAX_OWN_ADDRESSES})`),
|
|
165053
165122
|
flag2("--passphrase-file", "<file>", "your own passphrase file (8 or more characters); default: setup writes a new random passphrase to <home>/passphrase.txt, readable by you alone. Needed for a wallet that exists already"),
|
|
165054
165123
|
flag2("--facilitator", "<https origin>", `the facilitator whose release, pool, and proof tools setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
|
|
@@ -165063,9 +165132,9 @@ var COMMANDS = [
|
|
|
165063
165132
|
`${CLI_WORD} setup --yes --private-inference`
|
|
165064
165133
|
],
|
|
165065
165134
|
notes: [
|
|
165066
|
-
`npx -y --no-audit --no-fund ${COMMAND_AT_VERSION} setup runs the same command. Without --yes a first setup asks in the terminal: Tor
|
|
165067
|
-
`When Tor is recorded or asked for (a yes, --tor, --prefer-tor, or --private-inference), setup itself runs behind Tor: the release, the proof-tool downloads, and the RPC requests go over Tor, so the facilitator never sees this machine's IP address make the wallet. config.json then records Tor for mcp ("tor": true), so the MCP runs behind Tor too. Tor must run first (if it does not, setup gives the one command that starts it); --no-tor runs it without Tor.`,
|
|
165068
|
-
`Private inference (
|
|
165135
|
+
`npx -y --no-audit --no-fund ${COMMAND_AT_VERSION} setup runs the same command. Without --yes a first setup asks in the terminal: Tor and your own wallet address (Enter skips it). A rerun in a terminal asks only for your own wallet address: on a complete setup an address given there is saved and nothing else runs; Enter checks every piece again.`,
|
|
165136
|
+
`When Tor is recorded or asked for (a yes to the Tor question, --tor, --prefer-tor, or --private-inference), setup itself runs behind Tor: the release, the proof-tool downloads, and the RPC requests go over Tor, so the facilitator never sees this machine's IP address make the wallet. config.json then records Tor for mcp ("tor": true), so the MCP runs behind Tor too. Tor must run first (if it does not, setup gives the one command that starts it); --no-tor runs it without Tor.`,
|
|
165137
|
+
`Private inference (--private-inference): ${PRIVATE_INFERENCE_WHY} Setup records Tor. At the end, a terminal setup first offers to run add-funds (a --yes run prints ${CLI_WORD} add-funds), then prints ${CLI_WORD} chat (a private AI chat in this terminal), ${CLI_WORD} infer start (the local AI server in the background; it prints base_url and the api key when it starts), and the lines that add the wallet to an agent app. Setup never pays an inference call.`,
|
|
165069
165138
|
"The wallet home is SHIELDED_WALLET_HOME, default ~/.carbon-shielded-wallet (every command of this CLI uses it). setup writes there: config.json, passphrase.txt (mode 0600; on Windows an access list for only you on this computer), the wallet (<pool>/wallet.json, made by init with the production pool, program, network, and USDC), a recovery file recovery-<time>.txt, and proof-tools/<dir>/.",
|
|
165070
165139
|
"The passphrase is never printed and no MCP tool takes or returns it. The recovery file holds the seed and the key: copy it to two offline places, then delete it from this machine.",
|
|
165071
165140
|
`The proof tools are the set this wallet build PINS (its SHA256SUMS hash is in the wallet; --trust-proof-tools replaces it). A facilitator whose /api/wallet/release names another set is refused (setup_proof_tools_pin_mismatch: update the wallet, npx -y --no-audit --no-fund ${NPX_PACKAGE}@latest setup). SHA256SUMS must have the pinned SHA-256, and every file must have the SHA-256 its line names, as /skill.md section 7b; bin/ and house/bin/ get the execute bit. A file that does not match is not kept. Each download has a hard size limit.`,
|
|
@@ -166114,12 +166183,13 @@ async function askUnshield(args, values, deps) {
|
|
|
166114
166183
|
let whole;
|
|
166115
166184
|
if (values.get("--amount") === "all") {
|
|
166116
166185
|
try {
|
|
166117
|
-
whole =
|
|
166186
|
+
whole = savedBalance((deps.openWalletStore ?? openWalletStore)(resolve39(values.get("--wallet")), payerPassphrase(values, deps)));
|
|
166118
166187
|
} catch {
|
|
166119
166188
|
whole = void 0;
|
|
166120
166189
|
}
|
|
166121
166190
|
}
|
|
166122
|
-
const
|
|
166191
|
+
const wholeWords = !whole ? "" : whole.current ? ` (${usdcOr(whole.atomic.toString())})` : ` (about ${usdcOr(whole.atomic.toString())} at its last read)`;
|
|
166192
|
+
const what = values.get("--amount") === "all" ? `your whole private balance${wholeWords}` : `${usdcText3(values.get("--amount"))} USDC`;
|
|
166123
166193
|
const answer = String(await deps.ask(`Send ${what} to ${values.get("--to")}? [y/N] `)).trim();
|
|
166124
166194
|
if (!/^y(es)?$/i.test(answer)) throw new PublicFailure(`unshield_declined: you did not answer y, so nothing was sent. To send, run:
|
|
166125
166195
|
${cli2()} unshield`);
|
|
@@ -167233,7 +167303,7 @@ function failureHint(command2, reason, deps, values, floatError) {
|
|
|
167233
167303
|
if (/inference_tor_required/.test(reason)) return "rerun the same command with --tor added (or --proxy <http://host:port>); nothing was paid";
|
|
167234
167304
|
if (/inference_ceiling_over_cap/.test(reason)) return "raise --max-payment to the ceiling it names plus Shinjuku's fee, or lower --max-tokens; nothing was paid";
|
|
167235
167305
|
if (/inference_model_unknown/.test(reason)) return `pass --model with one of: ${INFER_MODEL_IDS.join(", ")} (${cli2()} infer models lists the prices)`;
|
|
167236
|
-
if (/inference_max_tokens_required/.test(reason)) return "add --max-tokens <n> (256 or more; 512 answers a short question), or leave --max-tokens out for 512: it sets the most the request can cost";
|
|
167306
|
+
if (/inference_max_tokens_required/.test(reason)) return "add --max-tokens <n> (256 or more; 512 answers a short question), or leave --max-tokens out for 2048 (512 for claude-opus-5): it sets the most the request can cost";
|
|
167237
167307
|
if (/mcp_session_cap_exceeded/.test(reason)) return "nothing was paid: this process reached --max-session. Restart it with a higher --max-session to pay more";
|
|
167238
167308
|
if (/mcp_cap_invalid/.test(reason)) {
|
|
167239
167309
|
return `start it with --max-session at least as high as --max-payment, for example: ${cli2()} mcp --max-payment 0.05 --max-session 0.2 ${PAYER_SESSION_BASE}`;
|
|
@@ -167877,11 +167947,13 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
167877
167947
|
`);
|
|
167878
167948
|
return 0;
|
|
167879
167949
|
}
|
|
167880
|
-
const
|
|
167881
|
-
const
|
|
167950
|
+
const full = args.length > 1 && args.at(-1) === "--verbose";
|
|
167951
|
+
const words2 = full ? args.slice(0, -1) : args;
|
|
167952
|
+
const threeWords = FAMILIES.has(words2[0]) && COMMAND_BY_NAME.has(words2.slice(0, 3).join(" "));
|
|
167953
|
+
const twoWords = FAMILIES.has(words2[0]) && words2.length > 1;
|
|
167954
|
+
const topicWords = threeWords ? [words2.slice(0, 3).join(" "), ...words2.slice(3)] : twoWords ? [`${words2[0]} ${words2[1]}`, ...words2.slice(2)] : words2;
|
|
167882
167955
|
const topic = COMMAND_BY_NAME.get(topicWords[0]);
|
|
167883
|
-
|
|
167884
|
-
if (!topic || topicWords.length > 1 && !full) {
|
|
167956
|
+
if (!topic || topicWords.length > 1) {
|
|
167885
167957
|
const near = suggestion(args[0], COMMAND_NAMES);
|
|
167886
167958
|
throw new UsageError(`There is no such command to explain.${near ? ` Did you mean ${near}? Run: ${cli2()} help ${near}` : ""}`);
|
|
167887
167959
|
}
|