shinjuku-shield 0.3.99 → 0.3.101
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 +243 -110
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.101. 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.101 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.101
|
|
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.101 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.101", "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": "1c3d204e7ba114b9d80654035efb4a244ac607f038e1961f23663b7af9b45dc5",
|
|
5
|
+
"bytes": 12794106,
|
|
6
|
+
"sourceCommit": "9e761632116ec38e2adb92828c85cc1793b45c90",
|
|
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-24T13:59:01.418Z"
|
|
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.101",
|
|
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": "1c3d204e",
|
|
7
|
+
"shinjukuSha256": "1c3d204e7ba114b9d80654035efb4a244ac607f038e1961f23663b7af9b45dc5",
|
|
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.00312 USDC";
|
|
107681
|
+
DEFAULT_MAX_TOKENS = 4096;
|
|
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, 4096 tokens) paid 0.00312 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 4096 (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 4096 tokens paid 0.00312 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 4096 (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 4096)" : 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 4096 tokens paid 0.00312 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,20 @@ 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
|
|
136085
|
+
return {
|
|
136086
|
+
atomic: BigInt(value),
|
|
136087
|
+
current: (report.status === "current" || report.status === "empty") && !report.detail?.pendingDeposits?.length,
|
|
136088
|
+
atMs: Date.parse(report.detail?.lastRefresh?.at ?? "")
|
|
136089
|
+
};
|
|
136076
136090
|
}
|
|
136077
136091
|
} catch {
|
|
136078
136092
|
}
|
|
136079
|
-
return
|
|
136093
|
+
return void 0;
|
|
136080
136094
|
}
|
|
136081
136095
|
function httpStatusOf(error) {
|
|
136082
136096
|
for (let item = error, depth = 0; item && depth < 4; item = item.cause, depth += 1) {
|
|
@@ -136221,6 +136235,20 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136221
136235
|
const state = { model, history: [], spent: 0n, answers: 0, empty: 0, pending: void 0, busy: false, interrupts: 0 };
|
|
136222
136236
|
const say6 = (line) => io.err(`${line}
|
|
136223
136237
|
`);
|
|
136238
|
+
const inPlace = deps.statusInPlace ?? Boolean(process.stderr.isTTY);
|
|
136239
|
+
let lastKnown;
|
|
136240
|
+
const readBalance = () => {
|
|
136241
|
+
let saved;
|
|
136242
|
+
try {
|
|
136243
|
+
saved = (deps.savedBalance ?? savedBalance)(session.openStore());
|
|
136244
|
+
} catch {
|
|
136245
|
+
saved = void 0;
|
|
136246
|
+
}
|
|
136247
|
+
if (saved && (saved.current || lastKnown && saved.atMs > lastKnown.atMs)) {
|
|
136248
|
+
lastKnown = { atomic: saved.atomic, spentThen: state.spent, atMs: saved.atMs };
|
|
136249
|
+
}
|
|
136250
|
+
return saved;
|
|
136251
|
+
};
|
|
136224
136252
|
const entryOf = (id) => models.find((entry) => entry.id === id);
|
|
136225
136253
|
const tokensFor = (id, messages, limit) => {
|
|
136226
136254
|
try {
|
|
@@ -136229,17 +136257,21 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136229
136257
|
return CHAT_SHORT_TOKENS;
|
|
136230
136258
|
}
|
|
136231
136259
|
};
|
|
136260
|
+
const costWords = (id) => {
|
|
136261
|
+
const tokens = tokensFor(id, [{ role: "user", content: "hi" }], caps.answer);
|
|
136262
|
+
if (id === DEFAULT_MODEL && tokens === CHAT_MAX_TOKENS) return `about ${DEFAULT_MODEL_PAID_WORDS}, as paid in a live test`;
|
|
136263
|
+
return answerCostWords(entryOf(id), tokens);
|
|
136264
|
+
};
|
|
136232
136265
|
const costLine2 = (id) => {
|
|
136233
136266
|
try {
|
|
136234
|
-
|
|
136235
|
-
return `A short answer costs ${answerCostWords(entryOf(id), tokens)}`;
|
|
136267
|
+
return `An answer costs ${costWords(id)}`;
|
|
136236
136268
|
} catch {
|
|
136237
136269
|
return "Each answer is paid";
|
|
136238
136270
|
}
|
|
136239
136271
|
};
|
|
136240
136272
|
const questionsWords = () => {
|
|
136241
136273
|
const asked = state.answers + state.empty;
|
|
136242
|
-
return `${asked} question${asked === 1 ? "" : "s"}${state.empty ? ` (${state.empty}
|
|
136274
|
+
return `${asked} question${asked === 1 ? "" : "s"}${state.empty ? ` (${state.empty} paid, no answer)` : ""}`;
|
|
136243
136275
|
};
|
|
136244
136276
|
const left = () => caps.chat > state.spent ? caps.chat - state.spent : 0n;
|
|
136245
136277
|
const capsLine2 = () => `Caps: ${usdcWords(caps.answer)} an answer and ${usdcWords(caps.chat)} for this chat (${usdcWords(left())} left).`;
|
|
@@ -136249,7 +136281,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136249
136281
|
"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
136282
|
`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
136283
|
`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" : ""}.`
|
|
136284
|
+
`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
136285
|
].join("\n");
|
|
136254
136286
|
let reader;
|
|
136255
136287
|
const onInterrupt = () => {
|
|
@@ -136276,7 +136308,8 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136276
136308
|
let held;
|
|
136277
136309
|
try {
|
|
136278
136310
|
least = leastAnswerAtomic(entryOf(state.model), CHAT_SHORT_TOKENS);
|
|
136279
|
-
|
|
136311
|
+
const saved = readBalance();
|
|
136312
|
+
held = saved?.current ? saved.atomic : void 0;
|
|
136280
136313
|
} catch {
|
|
136281
136314
|
held = void 0;
|
|
136282
136315
|
}
|
|
@@ -136286,11 +136319,8 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136286
136319
|
const runTurn = async (turn) => {
|
|
136287
136320
|
if (turn.capAtomic === void 0) {
|
|
136288
136321
|
let limit = caps.answer < left() ? caps.answer : left();
|
|
136289
|
-
|
|
136290
|
-
|
|
136291
|
-
if (held2 !== void 0 && held2 < limit) limit = held2;
|
|
136292
|
-
} catch {
|
|
136293
|
-
}
|
|
136322
|
+
const saved = readBalance();
|
|
136323
|
+
if (saved?.current && saved.atomic < limit) limit = saved.atomic;
|
|
136294
136324
|
turn.maxTokens = tokensFor(turn.model, turn.messages, limit);
|
|
136295
136325
|
let ceiling;
|
|
136296
136326
|
try {
|
|
@@ -136310,17 +136340,33 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136310
136340
|
}
|
|
136311
136341
|
turn.capAtomic = caps.answer < room ? caps.answer : room;
|
|
136312
136342
|
}
|
|
136313
|
-
say6(`(asking ${turn.model} privately over Tor; this takes a few seconds)`);
|
|
136314
136343
|
state.busy = true;
|
|
136315
136344
|
const startedAt = Date.now();
|
|
136316
136345
|
let lastLineAt = startedAt;
|
|
136346
|
+
let resent = false;
|
|
136317
136347
|
const seconds2 = () => Math.round((Date.now() - startedAt) / 1e3);
|
|
136348
|
+
const status = () => {
|
|
136349
|
+
const text3 = `(asking ${turn.model} privately over Tor: ${seconds2()} s)`;
|
|
136350
|
+
io.err(`\r\x1B[K${text3.slice(0, Math.max(20, (process.stderr.columns ?? 80) - 1))}`);
|
|
136351
|
+
};
|
|
136352
|
+
const clearStatus = () => {
|
|
136353
|
+
if (inPlace) io.err("\r\x1B[K");
|
|
136354
|
+
};
|
|
136355
|
+
if (inPlace) status();
|
|
136356
|
+
else say6(`(asking ${turn.model} privately over Tor)`);
|
|
136318
136357
|
onPhase = (phase) => {
|
|
136319
|
-
if (phase !== "seller-resend-unanswered") return;
|
|
136358
|
+
if (phase !== "seller-resend-unanswered" || resent) return;
|
|
136359
|
+
resent = true;
|
|
136320
136360
|
lastLineAt = Date.now();
|
|
136321
|
-
|
|
136361
|
+
clearStatus();
|
|
136362
|
+
say6("(the AI provider is slow to answer: the wallet asks it again with the same payment, which never pays twice)");
|
|
136363
|
+
if (inPlace) status();
|
|
136322
136364
|
};
|
|
136323
136365
|
const waiting = setInterval(() => {
|
|
136366
|
+
if (inPlace) {
|
|
136367
|
+
status();
|
|
136368
|
+
return;
|
|
136369
|
+
}
|
|
136324
136370
|
if (Date.now() - lastLineAt < STILL_WAITING_MS) return;
|
|
136325
136371
|
lastLineAt = Date.now();
|
|
136326
136372
|
say6(`(still waiting for the answer, ${seconds2()} s so far)`);
|
|
@@ -136337,10 +136383,13 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136337
136383
|
requestId: turn.requestId
|
|
136338
136384
|
});
|
|
136339
136385
|
} catch (error) {
|
|
136386
|
+
clearInterval(waiting);
|
|
136387
|
+
clearStatus();
|
|
136340
136388
|
failed(error, turn);
|
|
136341
136389
|
return;
|
|
136342
136390
|
} finally {
|
|
136343
136391
|
clearInterval(waiting);
|
|
136392
|
+
clearStatus();
|
|
136344
136393
|
onPhase = void 0;
|
|
136345
136394
|
state.busy = false;
|
|
136346
136395
|
state.interrupts = 0;
|
|
@@ -136385,17 +136434,29 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136385
136434
|
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
136435
|
return;
|
|
136387
136436
|
}
|
|
136437
|
+
const status = httpStatusOf(error);
|
|
136438
|
+
if (providerKept(code2, status)) {
|
|
136439
|
+
const kept2 = /^[1-9][0-9]{0,30}$/.test(String(error?.paidAtomic ?? "")) ? BigInt(error.paidAtomic) : void 0;
|
|
136440
|
+
if (kept2 !== void 0) state.spent += kept2;
|
|
136441
|
+
state.empty += 1;
|
|
136442
|
+
const what = `answered with an error (HTTP ${status})`;
|
|
136443
|
+
const others = models.filter((entry) => entry.id !== turn.model).map((entry) => entry.id).join(", ");
|
|
136444
|
+
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.`);
|
|
136445
|
+
return;
|
|
136446
|
+
}
|
|
136447
|
+
if (INSUFFICIENT2.test(code2 ?? "")) {
|
|
136448
|
+
state.pending = turn;
|
|
136449
|
+
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.`);
|
|
136450
|
+
return;
|
|
136451
|
+
}
|
|
136388
136452
|
const again = surelyNothing ? "ask again" : "type /retry (it finishes the same payment and never pays twice)";
|
|
136389
136453
|
if (!surelyNothing) state.pending = turn;
|
|
136390
136454
|
const paidWords = surelyNothing ? "Nothing was paid." : "It may be paid already.";
|
|
136391
136455
|
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";
|
|
136456
|
+
const what = status === 202 ? "has not sent the answer yet" : "did not answer";
|
|
136394
136457
|
say6(`Your payment reached the AI provider, but it ${what}. Type /retry: it asks again with the same payment and never pays twice.`);
|
|
136395
136458
|
} else if (code2 === "inference_tor_required" || code2 === "ECONNREFUSED" || proxyRefusedConnection(error)) {
|
|
136396
136459
|
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
136460
|
} else if (CAP_CODES.test(code2 ?? "")) {
|
|
136400
136461
|
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
136462
|
} else if (code2 === "inference_no_provider") {
|
|
@@ -136411,7 +136472,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136411
136472
|
const command2 = async (text3) => {
|
|
136412
136473
|
const [name, ...rest] = text3.split(/\s+/);
|
|
136413
136474
|
const arg = rest.join(" ").trim();
|
|
136414
|
-
if (name === "/exit") return true;
|
|
136475
|
+
if (name === "/exit" || name === "/quit" || name === "/q") return true;
|
|
136415
136476
|
if (name === "/new") {
|
|
136416
136477
|
state.history = [];
|
|
136417
136478
|
state.pending = void 0;
|
|
@@ -136420,13 +136481,24 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136420
136481
|
} else if (name === "/cost") {
|
|
136421
136482
|
say6(`This chat paid ${usdcWords(state.spent)} for ${questionsWords()}. ${capsLine2()}`);
|
|
136422
136483
|
} else if (name === "/balance") {
|
|
136423
|
-
|
|
136424
|
-
|
|
136425
|
-
|
|
136426
|
-
|
|
136427
|
-
|
|
136484
|
+
const saved = readBalance();
|
|
136485
|
+
const again = `A deposit since then is not in this number: ${commandLine("balance")} in another terminal reads it again.`;
|
|
136486
|
+
if (saved?.current) {
|
|
136487
|
+
say6(`Your private balance is ${usdcWords(saved.atomic)}.`);
|
|
136488
|
+
} else if (lastKnown) {
|
|
136489
|
+
const since = state.spent - lastKnown.spentThen;
|
|
136490
|
+
if (since === 0n) {
|
|
136491
|
+
say6(`Your private balance is about ${usdcWords(lastKnown.atomic)} at its last read. ${again}`);
|
|
136492
|
+
} else if (since > lastKnown.atomic) {
|
|
136493
|
+
say6(`This chat paid ${usdcWords(since)} since the last read of your private balance (${usdcWords(lastKnown.atomic)}), so a deposit came in since then. ${commandLine("balance")} in another terminal reads the balance again.`);
|
|
136494
|
+
} else {
|
|
136495
|
+
say6(`Your private balance is about ${usdcWords(lastKnown.atomic - since)}: ${usdcWords(lastKnown.atomic)} at its last read, less the ${usdcWords(since)} this chat paid since then. ${again}`);
|
|
136496
|
+
}
|
|
136497
|
+
} else if (saved) {
|
|
136498
|
+
say6(`Your private balance was ${usdcWords(saved.atomic)} at its last read; this chat paid ${usdcWords(state.spent)} since it started. ${again}`);
|
|
136499
|
+
} else {
|
|
136500
|
+
say6(`The private balance is not known in this chat yet. ${commandLine("balance")} in another terminal shows it.`);
|
|
136428
136501
|
}
|
|
136429
|
-
say6(line ?? `The private balance is not known in this chat yet. ${commandLine("balance")} in another terminal shows it.`);
|
|
136430
136502
|
} else if (name === "/model") {
|
|
136431
136503
|
const found = models.find((entry) => entry.id === arg);
|
|
136432
136504
|
if (found) {
|
|
@@ -136435,7 +136507,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136435
136507
|
} else {
|
|
136436
136508
|
say6([
|
|
136437
136509
|
`${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(/^
|
|
136510
|
+
...models.map((entry) => ` ${entry.id}${entry.note ? ` ${entry.note}` : ""} (${costLine2(entry.id).replace(/^An answer costs /, "an answer ")})`)
|
|
136439
136511
|
].join("\n"));
|
|
136440
136512
|
}
|
|
136441
136513
|
} else if (name === "/retry") {
|
|
@@ -136476,7 +136548,7 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
136476
136548
|
await session.close();
|
|
136477
136549
|
}
|
|
136478
136550
|
}
|
|
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;
|
|
136551
|
+
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
136552
|
var init_chat = __esm({
|
|
136481
136553
|
"wallet/src/inference/chat.mjs"() {
|
|
136482
136554
|
"use strict";
|
|
@@ -136493,12 +136565,13 @@ var init_chat = __esm({
|
|
|
136493
136565
|
init_models();
|
|
136494
136566
|
DEFAULT_MODEL2 = DEFAULT_MODEL;
|
|
136495
136567
|
CHAT_DEFAULT_MODEL = DEFAULT_MODEL;
|
|
136496
|
-
CHAT_MAX_TOKENS =
|
|
136568
|
+
CHAT_MAX_TOKENS = DEFAULT_MAX_TOKENS;
|
|
136497
136569
|
CHAT_SHORT_TOKENS = 512;
|
|
136498
136570
|
DEFAULT_ANSWER_CAP_ATOMIC = 50000n;
|
|
136499
136571
|
DEFAULT_CHAT_CAP_ATOMIC = 1000000n;
|
|
136500
136572
|
CHEAPEST_MODEL = "deepseek-v4-1-flash";
|
|
136501
136573
|
STILL_WAITING_MS = 2e4;
|
|
136574
|
+
providerKept = (code2, status) => code2 === "p03_float_seller_kept_payment" || /seller_unanswered_paid/.test(code2 ?? "") && Number.isInteger(status) && status >= 500;
|
|
136502
136575
|
higherCap = (atomic4) => usdcFlagText((BigInt(atomic4) * 2n + 9999n) / 10000n * 10000n);
|
|
136503
136576
|
POSITIVE_ATOMIC9 = /^[1-9][0-9]{0,30}$/;
|
|
136504
136577
|
SHINJUKU_FEE_ATOMIC = 1000n;
|
|
@@ -136510,9 +136583,10 @@ var init_chat = __esm({
|
|
|
136510
136583
|
NO_ANSWER_CODES = /^inference_(upstream_failed|response_invalid)$/;
|
|
136511
136584
|
NOTHING_PAID = /\bnothing was (?:sent or )?paid\b|\bnothing was paid or signed\b/i;
|
|
136512
136585
|
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|^
|
|
136586
|
+
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
136587
|
SHOWN_PROGRESS = new RegExp(`\\bWARNING\\b|\\bminutes?\\b|${TOR_NOTICE_LINE.source}`);
|
|
136515
|
-
|
|
136588
|
+
SELF_PAY_LINE = /^getting your balance ready: moving \S+ USDC of one deposit\b/;
|
|
136589
|
+
isProgressLine = (line) => PROGRESS_LINE.test(line) && (SELF_PAY_LINE.test(line) || !SHOWN_PROGRESS.test(line) && !TOR_TROUBLE_LINE.test(line));
|
|
136516
136590
|
fail46 = (deps, message) => typeof deps.publicFailure === "function" ? deps.publicFailure(message) : new Error(message);
|
|
136517
136591
|
nothingPaid = (error) => error?.name === "InferenceError" && !error.details?.receipt || error?.name === "SpendRefusalError" || NOTHING_PAID.test(String(error?.message ?? ""));
|
|
136518
136592
|
}
|
|
@@ -157682,7 +157756,7 @@ async function main5(args = process.argv.slice(2), { output: output2 = (result)
|
|
|
157682
157756
|
timing: createTimingLog(`${walletPath2}.timing.log`, { component: "payer" })
|
|
157683
157757
|
};
|
|
157684
157758
|
}
|
|
157685
|
-
progress2(`
|
|
157759
|
+
progress2(`getting your balance ready: moving ${usdc9(amountAtomic)} USDC of one deposit inside your private balance (about 2 minutes; it waits for finality)`);
|
|
157686
157760
|
try {
|
|
157687
157761
|
const paid = await selfPay({
|
|
157688
157762
|
store,
|
|
@@ -157714,7 +157788,7 @@ async function main5(args = process.argv.slice(2), { output: output2 = (result)
|
|
|
157714
157788
|
} catch (error) {
|
|
157715
157789
|
if (!DEAD.test(String(error?.message))) throw error;
|
|
157716
157790
|
store.writeWal(`self-pay-${id}`, { ...store.readWal(`self-pay-${id}`), state: "abandoned" });
|
|
157717
|
-
progress2(`step
|
|
157791
|
+
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
157792
|
return { amountAtomic, state: "abandoned" };
|
|
157719
157793
|
}
|
|
157720
157794
|
};
|
|
@@ -160734,11 +160808,16 @@ var ROUTE_LABEL = Object.freeze({
|
|
|
160734
160808
|
changenow: "a ChangeNOW swap",
|
|
160735
160809
|
"privacy-cash": "your own wallet through the Privacy Cash crowd"
|
|
160736
160810
|
});
|
|
160811
|
+
var SETUP_LINES_BELOW = "the setup lines below";
|
|
160737
160812
|
function privacyCost(plan) {
|
|
160738
160813
|
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
160814
|
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
160815
|
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
160816
|
}
|
|
160817
|
+
function privacyShort(plan) {
|
|
160818
|
+
if (plan.route !== "exchange" || splitOf(plan) > 1) return privacyCost(plan);
|
|
160819
|
+
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";
|
|
160820
|
+
}
|
|
160742
160821
|
function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
160743
160822
|
const decimals = Number(store.state.decimals ?? 6);
|
|
160744
160823
|
const stage = currentStage(store, plan);
|
|
@@ -160796,7 +160875,7 @@ function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
|
160796
160875
|
view2.balance = balance ?? null;
|
|
160797
160876
|
const pockets = balance?.pocketsAtomic && balance.pocketsAtomic !== "0" ? `, pockets ${usdc5(balance.pocketsAtomic, decimals)}` : "";
|
|
160798
160877
|
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" ?
|
|
160878
|
+
view2.next = process.env.SHINJUKU_WALLET_SETUP_NEXT_BY_PARENT === "1" ? SETUP_LINES_BELOW : `
|
|
160800
160879
|
Chat privately in this terminal: ${commandLine("chat")}
|
|
160801
160880
|
Use private AI from other apps: ${commandLine("infer start")}
|
|
160802
160881
|
In your agent app, ask "what is my balance" or ask it to pay an x402 URL.
|
|
@@ -160821,14 +160900,16 @@ function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
|
160821
160900
|
view2.status += `. Privacy: ${view2.privacy}`;
|
|
160822
160901
|
return view2;
|
|
160823
160902
|
}
|
|
160824
|
-
function fundsSummary(view2) {
|
|
160903
|
+
function fundsSummary(view2, { named = false } = {}) {
|
|
160825
160904
|
const privacy = `. Privacy: ${view2.privacy}`;
|
|
160826
160905
|
const status = view2.status.endsWith(privacy) ? view2.status.slice(0, -privacy.length) : view2.status;
|
|
160827
160906
|
const total = view2.balance?.total ? ` (your balance now: ${plainUsdc(String(view2.balance.total))} USDC)` : "";
|
|
160907
|
+
const run5 = named ? `add-funds ${view2.fundsId}` : "add-funds";
|
|
160828
160908
|
return [
|
|
160829
|
-
view2.ready ?
|
|
160909
|
+
view2.ready ? `${run5} is ready: ${view2.amount} was added to your private balance${total}.` : `${run5} is not ready yet: ${status}.`,
|
|
160830
160910
|
// 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
|
-
|
|
160911
|
+
// S3 (mcp56, published 0.3.99): inside setup, setup's own Next line follows, so this one is not printed.
|
|
160912
|
+
...view2.next === SETUP_LINES_BELOW ? [] : [`Next:${view2.next.startsWith("\n") ? "" : " "}${sentence(view2.next)}`]
|
|
160832
160913
|
].join("\n");
|
|
160833
160914
|
}
|
|
160834
160915
|
function fundsOutput(values, deps) {
|
|
@@ -160841,7 +160922,7 @@ function printEnd(deps, view2, terminal) {
|
|
|
160841
160922
|
`);
|
|
160842
160923
|
}
|
|
160843
160924
|
function fundsStatusSummary(view2) {
|
|
160844
|
-
const [first, ...next] = fundsSummary(view2).split("\n");
|
|
160925
|
+
const [first, ...next] = fundsSummary(view2, { named: true }).split("\n");
|
|
160845
160926
|
const said = view2.stages?.find((entry) => entry.said?.length)?.said ?? [];
|
|
160846
160927
|
const rests = view2.leftoverAtOneTimeAddresses ?? (view2.leftoverAtOneTimeAddress ? [view2.leftoverAtOneTimeAddress] : []);
|
|
160847
160928
|
return [
|
|
@@ -160857,7 +160938,7 @@ function fundsStatusSummary(view2) {
|
|
|
160857
160938
|
"For the whole status as JSON, add --json (or send this command's output to a file or a program)."
|
|
160858
160939
|
].join("\n");
|
|
160859
160940
|
}
|
|
160860
|
-
var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |shinjuku(?:-wallet)?\b.*: waiting for funds, nothing failed \(|hint: |Help: |
|
|
160941
|
+
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
160942
|
var STEP_TOR_LINE = /^(?:tor|proxy): /;
|
|
160862
160943
|
var DENOMINATION_WARNING = /^warning: no pool denomination is configured\b/;
|
|
160863
160944
|
async function runStep(ctx, argv2) {
|
|
@@ -161547,7 +161628,9 @@ async function addFunds(values, deps) {
|
|
|
161547
161628
|
plan,
|
|
161548
161629
|
proxyArgs: proxy.args,
|
|
161549
161630
|
childEnv: { ...proxy.env, [PASSPHRASE_ENV]: passphrase2, [RPC_ENV2]: rpcUrl },
|
|
161550
|
-
|
|
161631
|
+
// A1: a quiet terminal run names no plan id and no stage (funds-status and --verbose do).
|
|
161632
|
+
say: (stage, text3) => io.err(ctx.quiet ? `add-funds: ${text3.replace(STEP_WORDS, "")}
|
|
161633
|
+
` : `add-funds ${plan.fundsId} [${stage}] ${text3}
|
|
161551
161634
|
`),
|
|
161552
161635
|
mark: (stage) => {
|
|
161553
161636
|
if (plan.done?.[stage]) return;
|
|
@@ -161556,12 +161639,12 @@ async function addFunds(values, deps) {
|
|
|
161556
161639
|
}
|
|
161557
161640
|
});
|
|
161558
161641
|
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.`);
|
|
161642
|
+
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
161643
|
if (resolved.routeChanged) {
|
|
161561
161644
|
const address5 = relayedDeposit2(store, plan)?.relay?.owner;
|
|
161562
161645
|
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
161646
|
}
|
|
161564
|
-
ctx.say("start", `privacy: ${privacyCost(plan)}`);
|
|
161647
|
+
ctx.say("start", `privacy: ${ctx.quiet ? privacyShort(plan) : privacyCost(plan)}`);
|
|
161565
161648
|
const markDone = () => {
|
|
161566
161649
|
const stage = currentStage(store, plan);
|
|
161567
161650
|
for (const done of stagesOf(plan)) {
|
|
@@ -161598,7 +161681,7 @@ async function addFunds(values, deps) {
|
|
|
161598
161681
|
}
|
|
161599
161682
|
const label = outcome.label ?? next ?? stage;
|
|
161600
161683
|
if (outcome.kind === "stop") {
|
|
161601
|
-
const said = outcome.tries ? [] : (outcome.run?.held ?? []).filter((text3) => !/^(Help|
|
|
161684
|
+
const said = outcome.tries ? [] : (outcome.run?.held ?? []).filter((text3) => !/^(Help|[Nn]ext): /.test(text3));
|
|
161602
161685
|
if (!said.length && outcome.run?.last && !outcome.tries) said.push(outcome.run.last);
|
|
161603
161686
|
for (const text3 of said) io.err(` ${text3}
|
|
161604
161687
|
`);
|
|
@@ -161632,7 +161715,7 @@ async function addFunds(values, deps) {
|
|
|
161632
161715
|
plan.status = { ...plan.status ?? { stage: currentStage(store, plan) }, ended: iso2(deps.now()) };
|
|
161633
161716
|
save2();
|
|
161634
161717
|
const view2 = fundsView(store, plan, { now: deps.now(), running: false });
|
|
161635
|
-
io.err(`add-funds ${plan.fundsId} stopped at ${view2.stage}: nothing is lost. Run the same command again to continue.
|
|
161718
|
+
io.err(`${ctx.quiet ? "add-funds stopped" : `add-funds ${plan.fundsId} stopped at ${view2.stage}`}: nothing is lost. Run the same command again to continue.
|
|
161636
161719
|
`);
|
|
161637
161720
|
printEnd(deps, view2, ctx.terminal);
|
|
161638
161721
|
} finally {
|
|
@@ -162045,8 +162128,9 @@ var GUIDE = Object.freeze({
|
|
|
162045
162128
|
});
|
|
162046
162129
|
var WALLET_FILE = `"$SHIELDED_WALLET_HOME/${GUIDE.pool}/wallet.json"`;
|
|
162047
162130
|
var FROM_SETUP = " (after setup, config.json gives it: no need to type it)";
|
|
162048
|
-
var SETUP_NEXT = `
|
|
162049
|
-
${CLI_WORD}
|
|
162131
|
+
var SETUP_NEXT = `chat privately with an AI in this terminal:
|
|
162132
|
+
${CLI_WORD} chat
|
|
162133
|
+
Or open your agent app and ask "what is my balance".`;
|
|
162050
162134
|
var UNSHIELD_EXAMPLE = `${CLI_WORD} unshield --amount 0.5 --to <your wallet address>`;
|
|
162051
162135
|
function hintForThisRun(text3, values) {
|
|
162052
162136
|
let hint = String(text3);
|
|
@@ -162092,6 +162176,12 @@ var UsageError = class extends Error {
|
|
|
162092
162176
|
};
|
|
162093
162177
|
var PublicFailure = class extends Error {
|
|
162094
162178
|
};
|
|
162179
|
+
var QuestionCancelled = class extends PublicFailure {
|
|
162180
|
+
// Every terminal question comes before a payment or a send, so this is true at each one.
|
|
162181
|
+
constructor(interrupted) {
|
|
162182
|
+
super(`stopped: ${interrupted ? "you pressed Ctrl-C at a question" : "the input ended before an answer to the question"}, so the command stopped there; nothing was sent or paid. Run the same command again when you are ready.`);
|
|
162183
|
+
}
|
|
162184
|
+
};
|
|
162095
162185
|
var NOT_SET_UP_CODE = "wallet_not_set_up";
|
|
162096
162186
|
var flag2 = (name, value, about, options = {}) => ({ name, value, about, ...options });
|
|
162097
162187
|
var bool2 = (name, about, options = {}) => ({ name, boolean: true, about, ...options });
|
|
@@ -162531,6 +162621,10 @@ var INFER_COST_LINE = "the AI provider charges its quoted cap for --max-tokens a
|
|
|
162531
162621
|
var INFER_STDIN_MAX_BYTES = 1024 * 1024;
|
|
162532
162622
|
var INFER_STDIN_EXAMPLE = `echo "Summarize x402 in one line" | ${CLI_WORD} infer - --model qwen3-8-flash`;
|
|
162533
162623
|
var CURL_USEPOD_NEXT = `use \`${CLI_WORD} infer\` for the AI provider: it sends the prompt only through --tor or --proxy`;
|
|
162624
|
+
function unshieldArgs(command2, args) {
|
|
162625
|
+
if (args.length === 0 || args.includes("--amount") || !/^(all|[0-9]+(\.[0-9]+)?)$/i.test(args[0])) return args;
|
|
162626
|
+
return ["--amount", /^all$/i.test(args[0]) ? "all" : args[0], ...args.slice(1)];
|
|
162627
|
+
}
|
|
162534
162628
|
function inferArgs(command2, args) {
|
|
162535
162629
|
const known = new Map(command2.flags.map((entry) => [entry.name, entry]));
|
|
162536
162630
|
const out = [];
|
|
@@ -163953,7 +164047,8 @@ async function runSetupCommand(values, deps) {
|
|
|
163953
164047
|
const asksOwnFirst = interactive && Boolean(old) && terminal && !ownAddress;
|
|
163954
164048
|
io.out([
|
|
163955
164049
|
...introduced ? [] : [
|
|
163956
|
-
|
|
164050
|
+
// S2 (mcp56 on the published 0.3.99): the help's words (shortHelpText), not "x402 URLs".
|
|
164051
|
+
"Shinjuku Shield: your private wallet on this computer. It pays for AI answers and paid web services from your private balance.",
|
|
163957
164052
|
"Setup makes the wallet on this machine (your keys never leave it), downloads and checks the proof tools, and writes config.json.",
|
|
163958
164053
|
// 0.3.97 T4, live proof 2026-10-24: the command on its own line.
|
|
163959
164054
|
"An agent app then starts it with this command:",
|
|
@@ -163987,10 +164082,6 @@ async function runSetupCommand(values, deps) {
|
|
|
163987
164082
|
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
164083
|
options.tor = !/^n(o)?$/i.test(tor);
|
|
163989
164084
|
}
|
|
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
164085
|
if (!ownAddress && terminal) ownAddress = await askOwnAddress(deps);
|
|
163995
164086
|
} else if (asksOwnFirst) {
|
|
163996
164087
|
ownAddress = await askOwnAddress(deps, old.unshieldTo ?? []);
|
|
@@ -164171,7 +164262,7 @@ async function fillAddFunds(values, deps) {
|
|
|
164171
164262
|
const kept2 = /^[1-9][0-9]{0,19}$/.test(String(plan?.amountAtomic ?? "")) ? plan.amountAtomic : void 0;
|
|
164172
164263
|
if (!plan && !values.has("--route")) values.set("--route", "exchange");
|
|
164173
164264
|
if (!values.has("--amount")) {
|
|
164174
|
-
if (kept2) deps.io.err(`This continues
|
|
164265
|
+
if (kept2) deps.io.err(`This continues the add-funds run of ${usdcText3(kept2)} USDC that is not ready yet.
|
|
164175
164266
|
`);
|
|
164176
164267
|
if (!kept2 && !terminal) {
|
|
164177
164268
|
throw new UsageError("add-funds needs the amount of USDC (--amount, from 0.01) when it runs without a terminal; in a terminal it asks", command2);
|
|
@@ -164821,8 +164912,8 @@ var COMMANDS = [
|
|
|
164821
164912
|
flags: [
|
|
164822
164913
|
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
164914
|
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>",
|
|
164915
|
+
flag2("--max-tokens", "<n>", "the longest answer to pay for, in tokens; default 4096, 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" }),
|
|
164916
|
+
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
164917
|
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
164918
|
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
164919
|
bool2("--json", "print one JSON line (answer, model, usage, paidAtomic, requestId, receipt) instead of the answer text"),
|
|
@@ -164861,7 +164952,7 @@ var COMMANDS = [
|
|
|
164861
164952
|
flags: [bool2("--json", "print the list as one JSON line (prices in atomic USDC per 1M tokens)")],
|
|
164862
164953
|
examples: [`${CLI_WORD} infer models`, `${CLI_WORD} infer models --json`],
|
|
164863
164954
|
notes: [`${INFER_COST_LINE}.`],
|
|
164864
|
-
next: `ask one: echo "Summarize x402 in one line" | ${CLI_WORD} infer - --model
|
|
164955
|
+
next: `ask one: echo "Summarize x402 in one line" | ${CLI_WORD} infer - --model ${DEFAULT_MODEL2}`,
|
|
164865
164956
|
run: (args, values, deps, io) => deps.inferModelsMain(values, { ...deps, io })
|
|
164866
164957
|
},
|
|
164867
164958
|
{
|
|
@@ -164876,12 +164967,12 @@ var COMMANDS = [
|
|
|
164876
164967
|
],
|
|
164877
164968
|
examples: [
|
|
164878
164969
|
`${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":"
|
|
164970
|
+
`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
164971
|
],
|
|
164881
164972
|
notes: [
|
|
164882
164973
|
"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
164974
|
"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(", ")}.`,
|
|
164975
|
+
`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
164976
|
"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
164977
|
`${INFER_COST_LINE}. Each answer carries a shinjuku field with the amount paid and what this server has spent.`,
|
|
164887
164978
|
"Ctrl+C stops it; stderr then says what it paid in all.",
|
|
@@ -164961,7 +165052,7 @@ var COMMANDS = [
|
|
|
164961
165052
|
summary: "Chat privately with an AI model in this terminal: each answer is paid from your private balance, behind Tor.",
|
|
164962
165053
|
env: [],
|
|
164963
165054
|
flags: [
|
|
164964
|
-
flag2("--model", "<id>", `the model to start with (default
|
|
165055
|
+
flag2("--model", "<id>", `the model to start with (default ${DEFAULT_MODEL2}): ${INFER_MODEL_IDS.join(", ")}`),
|
|
164965
165056
|
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
165057
|
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
165058
|
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 +165139,7 @@ var COMMANDS = [
|
|
|
165048
165139
|
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
165140
|
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
165141
|
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",
|
|
165142
|
+
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
165143
|
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
165144
|
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
165145
|
flag2("--facilitator", "<https origin>", `the facilitator whose release, pool, and proof tools setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
|
|
@@ -165063,9 +165154,9 @@ var COMMANDS = [
|
|
|
165063
165154
|
`${CLI_WORD} setup --yes --private-inference`
|
|
165064
165155
|
],
|
|
165065
165156
|
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 (
|
|
165157
|
+
`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.`,
|
|
165158
|
+
`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.`,
|
|
165159
|
+
`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
165160
|
"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
165161
|
"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
165162
|
`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.`,
|
|
@@ -165183,6 +165274,7 @@ var COMMANDS = [
|
|
|
165183
165274
|
name: "unshield",
|
|
165184
165275
|
summary: "Send some or all of your private balance to a Solana wallet address, privately.",
|
|
165185
165276
|
env: [],
|
|
165277
|
+
normalizeArgs: unshieldArgs,
|
|
165186
165278
|
flags: [
|
|
165187
165279
|
// 0.3.97, live proof 2026-10-24: --amount and --to are optional in a terminal (unshield asks both, then a y);
|
|
165188
165280
|
// a script, a pipe, or an agent's tool must give both (askUnshield).
|
|
@@ -165833,7 +165925,8 @@ function parseFlags(command2, args) {
|
|
|
165833
165925
|
for (let index = 0; index < args.length; index += 1) {
|
|
165834
165926
|
const token = args[index];
|
|
165835
165927
|
if (!token.startsWith("--")) {
|
|
165836
|
-
|
|
165928
|
+
const example = known.has("--amount") ? "--amount 0.5" : `--pool ${GUIDE.pool}`;
|
|
165929
|
+
throw new UsageError(`unexpected argument ${index + 1} (it has no flag in front of it): every value follows its flag, like ${example}`, command2);
|
|
165837
165930
|
}
|
|
165838
165931
|
if (token.includes("=")) {
|
|
165839
165932
|
const spec2 = known.get(token.slice(0, token.indexOf("=")));
|
|
@@ -166114,12 +166207,13 @@ async function askUnshield(args, values, deps) {
|
|
|
166114
166207
|
let whole;
|
|
166115
166208
|
if (values.get("--amount") === "all") {
|
|
166116
166209
|
try {
|
|
166117
|
-
whole =
|
|
166210
|
+
whole = savedBalance((deps.openWalletStore ?? openWalletStore)(resolve39(values.get("--wallet")), payerPassphrase(values, deps)));
|
|
166118
166211
|
} catch {
|
|
166119
166212
|
whole = void 0;
|
|
166120
166213
|
}
|
|
166121
166214
|
}
|
|
166122
|
-
const
|
|
166215
|
+
const wholeWords = !whole ? "" : whole.current ? ` (${usdcOr(whole.atomic.toString())})` : ` (about ${usdcOr(whole.atomic.toString())} at its last read)`;
|
|
166216
|
+
const what = values.get("--amount") === "all" ? `your whole private balance${wholeWords}` : `${usdcText3(values.get("--amount"))} USDC`;
|
|
166123
166217
|
const answer = String(await deps.ask(`Send ${what} to ${values.get("--to")}? [y/N] `)).trim();
|
|
166124
166218
|
if (!/^y(es)?$/i.test(answer)) throw new PublicFailure(`unshield_declined: you did not answer y, so nothing was sent. To send, run:
|
|
166125
166219
|
${cli2()} unshield`);
|
|
@@ -167233,7 +167327,7 @@ function failureHint(command2, reason, deps, values, floatError) {
|
|
|
167233
167327
|
if (/inference_tor_required/.test(reason)) return "rerun the same command with --tor added (or --proxy <http://host:port>); nothing was paid";
|
|
167234
167328
|
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
167329
|
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";
|
|
167330
|
+
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 4096 (512 for claude-opus-5): it sets the most the request can cost";
|
|
167237
167331
|
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
167332
|
if (/mcp_cap_invalid/.test(reason)) {
|
|
167239
167333
|
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}`;
|
|
@@ -167740,10 +167834,25 @@ function defaultDependencies() {
|
|
|
167740
167834
|
stdinIsTTY: () => Boolean(process.stdin.isTTY),
|
|
167741
167835
|
/** True when stdout is a terminal: add-funds then ends with a short summary instead of the status as JSON. */
|
|
167742
167836
|
stdoutIsTTY: () => Boolean(process.stdout.isTTY),
|
|
167743
|
-
/**
|
|
167744
|
-
|
|
167837
|
+
/**
|
|
167838
|
+
* One terminal question on stderr (stdout stays for output); the answer text. U7 (mcp56, published 0.3.100): Ctrl-C
|
|
167839
|
+
* at a question paused stdin with no answer, Node then printed "Detected unsettled top-level await" and nothing said
|
|
167840
|
+
* what happened. Ctrl-C, or an input that ends, now stops the command with QuestionCancelled (one plain line).
|
|
167841
|
+
*/
|
|
167842
|
+
ask: (question) => new Promise((resolveAnswer, rejectAnswer) => {
|
|
167745
167843
|
const prompt = createInterface2({ input: process.stdin, output: process.stderr, terminal: true });
|
|
167844
|
+
let answered2 = false;
|
|
167845
|
+
let interrupted = false;
|
|
167846
|
+
prompt.on("SIGINT", () => {
|
|
167847
|
+
interrupted = true;
|
|
167848
|
+
process.stderr.write("\n");
|
|
167849
|
+
prompt.close();
|
|
167850
|
+
});
|
|
167851
|
+
prompt.on("close", () => {
|
|
167852
|
+
if (!answered2) rejectAnswer(new QuestionCancelled(interrupted));
|
|
167853
|
+
});
|
|
167746
167854
|
prompt.question(question, (answer) => {
|
|
167855
|
+
answered2 = true;
|
|
167747
167856
|
prompt.close();
|
|
167748
167857
|
resolveAnswer(answer);
|
|
167749
167858
|
});
|
|
@@ -167827,7 +167936,24 @@ function isHelpRequest(argv2) {
|
|
|
167827
167936
|
args = rest.slice(1);
|
|
167828
167937
|
}
|
|
167829
167938
|
}
|
|
167830
|
-
|
|
167939
|
+
if (command2 === void 0) return false;
|
|
167940
|
+
if (!command2.normalizeArgs || command2.normalizeArgs === unshieldArgs) {
|
|
167941
|
+
if (strayWordIn(command2, command2.normalizeArgs ? command2.normalizeArgs(command2, args) : args)) return true;
|
|
167942
|
+
}
|
|
167943
|
+
return helpFlagIn(command2, args);
|
|
167944
|
+
}
|
|
167945
|
+
function strayWordIn(command2, args) {
|
|
167946
|
+
const known = new Map(command2.flags.map((entry) => [entry.name, entry]));
|
|
167947
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
167948
|
+
const token = args[index];
|
|
167949
|
+
if (!token.startsWith("--")) return true;
|
|
167950
|
+
const spec = known.get(token);
|
|
167951
|
+
if (!spec || token.includes("=")) return false;
|
|
167952
|
+
if (spec.boolean) continue;
|
|
167953
|
+
if (args[index + 1] === void 0 || args[index + 1].startsWith("--")) return false;
|
|
167954
|
+
index += 1;
|
|
167955
|
+
}
|
|
167956
|
+
return false;
|
|
167831
167957
|
}
|
|
167832
167958
|
async function runShinjukuWallet(argv2, overrides = {}) {
|
|
167833
167959
|
const deps = { ...defaultDependencies(), ...overrides };
|
|
@@ -167877,11 +168003,13 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
167877
168003
|
`);
|
|
167878
168004
|
return 0;
|
|
167879
168005
|
}
|
|
167880
|
-
const
|
|
167881
|
-
const
|
|
168006
|
+
const full = args.length > 1 && args.at(-1) === "--verbose";
|
|
168007
|
+
const words2 = full ? args.slice(0, -1) : args;
|
|
168008
|
+
const threeWords = FAMILIES.has(words2[0]) && COMMAND_BY_NAME.has(words2.slice(0, 3).join(" "));
|
|
168009
|
+
const twoWords = FAMILIES.has(words2[0]) && words2.length > 1;
|
|
168010
|
+
const topicWords = threeWords ? [words2.slice(0, 3).join(" "), ...words2.slice(3)] : twoWords ? [`${words2[0]} ${words2[1]}`, ...words2.slice(2)] : words2;
|
|
167882
168011
|
const topic = COMMAND_BY_NAME.get(topicWords[0]);
|
|
167883
|
-
|
|
167884
|
-
if (!topic || topicWords.length > 1 && !full) {
|
|
168012
|
+
if (!topic || topicWords.length > 1) {
|
|
167885
168013
|
const near = suggestion(args[0], COMMAND_NAMES);
|
|
167886
168014
|
throw new UsageError(`There is no such command to explain.${near ? ` Did you mean ${near}? Run: ${cli2()} help ${near}` : ""}`);
|
|
167887
168015
|
}
|
|
@@ -167975,6 +168103,11 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
167975
168103
|
if (reason.startsWith(`${NOT_SET_UP_CODE}: `)) {
|
|
167976
168104
|
const plain = reason.slice(NOT_SET_UP_CODE.length + 2);
|
|
167977
168105
|
io.err(`${plain[0].toUpperCase()}${plain.slice(1)}
|
|
168106
|
+
`);
|
|
168107
|
+
return 1;
|
|
168108
|
+
}
|
|
168109
|
+
if (error instanceof QuestionCancelled) {
|
|
168110
|
+
io.err(`${CLI} ${command2.name} ${error.message}
|
|
167978
168111
|
`);
|
|
167979
168112
|
return 1;
|
|
167980
168113
|
}
|