shinjuku-shielded 0.3.183 → 0.3.184

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 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.183. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.184. 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 shinjuku@0.3.183 setup
15
+ npx -y shinjuku@0.3.184 setup
16
16
  ```
17
17
 
18
18
  Setup offers to install the `shinjuku` command and, on Windows, adds npm's
@@ -35,13 +35,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
35
35
  Claude Code:
36
36
 
37
37
  ```sh
38
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.183 mcp
38
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.184 mcp
39
39
  ```
40
40
 
41
41
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
42
42
 
43
43
  ```json
44
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.183", "mcp"] } } }
44
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.184", "mcp"] } } }
45
45
  ```
46
46
 
47
47
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
@@ -54,7 +54,7 @@ Codex (the Codex app and Codex CLI read the same file, `~/.codex/config.toml`):
54
54
  ```toml
55
55
  [mcp_servers.shinjuku]
56
56
  command = "npx"
57
- args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.183", "mcp"]
57
+ args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.184", "mcp"]
58
58
  startup_timeout_sec = 120
59
59
  tool_timeout_sec = 300
60
60
  ```
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": "d67bcc6c4e3910c2c10af366bc4f0dfab14e7023805cd9b15ebe92f68ba41098",
5
- "bytes": 9344406,
6
- "sourceCommit": "d57192d0800307fede3af52d006b6fb758fd0362",
4
+ "sha256": "fed76293d7f682c50f249284a7f4bbf14d2d6c7d8926c052c6ef1f683419b8dd",
5
+ "bytes": 9349716,
6
+ "sourceCommit": "37820c4cf40bdbd57dece9d1b739e1c541f534ed",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
@@ -1038,5 +1038,5 @@
1038
1038
  "version": "1.8.0"
1039
1039
  }
1040
1040
  ],
1041
- "publishedAt": "2026-11-06T15:11:02.328Z"
1041
+ "publishedAt": "2026-11-06T16:14:51.926Z"
1042
1042
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.183",
3
+ "version": "0.3.184",
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": "d67bcc6c",
7
- "shinjukuSha256": "d67bcc6c4e3910c2c10af366bc4f0dfab14e7023805cd9b15ebe92f68ba41098",
6
+ "shinjukuRelease": "fed76293",
7
+ "shinjukuSha256": "fed76293d7f682c50f249284a7f4bbf14d2d6c7d8926c052c6ef1f683419b8dd",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -43143,9 +43143,19 @@ function facilitatorRpcUrl(origin) {
43143
43143
  url = void 0;
43144
43144
  }
43145
43145
  const base = url && (url.protocol === "https:" || url.protocol === "http:") ? payHostOf(url.origin) : DEFAULT_FACILITATOR_ORIGIN;
43146
- relayOrigins.add(base);
43146
+ if (!relayOrigins.has(base)) {
43147
+ relayOrigins.add(base);
43148
+ builtRelayOrigins.add(base);
43149
+ try {
43150
+ process.env[RELAY_ORIGINS_ENV] = [...builtRelayOrigins].join(" ");
43151
+ } catch {
43152
+ }
43153
+ }
43147
43154
  return `${base}${FACILITATOR_RPC_PATH}`;
43148
43155
  }
43156
+ function relayOriginsEnv() {
43157
+ return builtRelayOrigins.size ? { [RELAY_ORIGINS_ENV]: [...builtRelayOrigins].join(" ") } : {};
43158
+ }
43149
43159
  function isShinjukuRelayUrl(input) {
43150
43160
  try {
43151
43161
  const url = new URL(typeof input === "string" || input instanceof URL ? input : input?.url);
@@ -43189,7 +43199,7 @@ function resolveRpcUrl2({ rpcFile, rpc, env = process.env, origin, readFile: rea
43189
43199
  }
43190
43200
  return { url, source: "relay" };
43191
43201
  }
43192
- var DEFAULT_FACILITATOR_ORIGIN, PAY_HOSTS, FACILITATOR_RPC_PATH, RPC_ENV, relayOrigins;
43202
+ var DEFAULT_FACILITATOR_ORIGIN, PAY_HOSTS, FACILITATOR_RPC_PATH, RPC_ENV, RELAY_ORIGINS_ENV, builtRelayOrigins, relayOrigins;
43193
43203
  var init_rpcDefault = __esm({
43194
43204
  "wallet/src/rpcDefault.mjs"() {
43195
43205
  "use strict";
@@ -43201,7 +43211,16 @@ var init_rpcDefault = __esm({
43201
43211
  });
43202
43212
  FACILITATOR_RPC_PATH = "/api/solana-rpc";
43203
43213
  RPC_ENV = "UNDERMARKET_SOLANA_RPC_URL";
43204
- relayOrigins = /* @__PURE__ */ new Set([DEFAULT_FACILITATOR_ORIGIN, ...Object.keys(PAY_HOSTS), ...Object.values(PAY_HOSTS)]);
43214
+ RELAY_ORIGINS_ENV = "SHINJUKU_RELAY_ORIGINS";
43215
+ builtRelayOrigins = new Set(String(process.env[RELAY_ORIGINS_ENV] ?? "").split(/\s+/).flatMap((value2) => {
43216
+ try {
43217
+ const url = new URL(value2);
43218
+ return (url.protocol === "https:" || url.protocol === "http:") && url.origin === value2 ? [value2] : [];
43219
+ } catch {
43220
+ return [];
43221
+ }
43222
+ }));
43223
+ relayOrigins = /* @__PURE__ */ new Set([DEFAULT_FACILITATOR_ORIGIN, ...Object.keys(PAY_HOSTS), ...Object.values(PAY_HOSTS), ...builtRelayOrigins]);
43205
43224
  }
43206
43225
  });
43207
43226
 
@@ -43832,6 +43851,16 @@ async function onOwnTagDispatcher(dispatcher, task) {
43832
43851
  }
43833
43852
  }
43834
43853
  }
43854
+ function routeDispatcherOf(url) {
43855
+ let origin;
43856
+ try {
43857
+ origin = new URL(String(url)).origin;
43858
+ } catch {
43859
+ origin = void 0;
43860
+ }
43861
+ if (origin !== void 0 && routedOrigins.has(origin)) return onionInit({}).dispatcher;
43862
+ return ownTag.getStore()?.dispatcher;
43863
+ }
43835
43864
  function onionInit(init = {}) {
43836
43865
  if (runEnded()) return init;
43837
43866
  const own = ownTagInit(init);
@@ -48313,6 +48342,40 @@ var init_relayLimit = __esm({
48313
48342
  }
48314
48343
  });
48315
48344
 
48345
+ // wallet/src/cli/homeJob.mjs
48346
+ function stepWords(lead, words2, line, { env = process.env, first, inline = false } = {}) {
48347
+ const key = homeKeyOf(words2);
48348
+ const comma = /\bor$/.test(lead) ? "" : ",";
48349
+ if (inHomeJob(env) && key) return `${lead}${comma} ${first ? `${first}, then ` : ""}press [${key}] on the home screen.`;
48350
+ return inline ? `${lead}: ${line}` : `${lead}${comma} run:
48351
+ ${line}`;
48352
+ }
48353
+ function stepName(words2, line, { env = process.env } = {}) {
48354
+ const key = homeKeyOf(words2);
48355
+ return inHomeJob(env) && key ? `press [${key}] on the home screen` : line;
48356
+ }
48357
+ var HOME_JOB_ENV, LAUNCHER_PID_ENV, inHomeJob, HOME_KEY, JOB_NAME, jobLabel, homeKeyOf;
48358
+ var init_homeJob = __esm({
48359
+ "wallet/src/cli/homeJob.mjs"() {
48360
+ "use strict";
48361
+ HOME_JOB_ENV = "SHINJUKU_WALLET_HOME_JOB";
48362
+ LAUNCHER_PID_ENV = "SHINJUKU_WALLET_LAUNCHER_PID";
48363
+ inHomeJob = (env = process.env) => env?.[HOME_JOB_ENV] === "1";
48364
+ HOME_KEY = { "add-funds": "a", unshield: "u", curl: "p", chat: "t", setup: "s", restore: "r", limits: "l" };
48365
+ JOB_NAME = {
48366
+ "add-funds": "add funds",
48367
+ unshield: "send money out",
48368
+ curl: "pay a link",
48369
+ chat: "private AI chat",
48370
+ setup: "setup",
48371
+ restore: "restore",
48372
+ limits: "spending limits"
48373
+ };
48374
+ jobLabel = (name, typed, env = process.env) => inHomeJob(env) && JOB_NAME[name] ? JOB_NAME[name] : typed;
48375
+ homeKeyOf = (words2) => HOME_KEY[String(words2 ?? "").trim().split(/\s+/)[0]];
48376
+ }
48377
+ });
48378
+
48316
48379
  // wallet/src/net/proxy.mjs
48317
48380
  import { spawn } from "node:child_process";
48318
48381
  import { randomBytes as randomBytes5 } from "node:crypto";
@@ -48825,7 +48888,8 @@ function proxiedChild(args, plainProxy, stdio) {
48825
48888
  NODE_USE_ENV_PROXY: "1",
48826
48889
  NODE_OPTIONS: proxiedNodeOptions(process.env.NODE_OPTIONS),
48827
48890
  ...proxyEnv(proxy),
48828
- [MARK]: `tor ${proxy}`
48891
+ [MARK]: `tor ${proxy}`,
48892
+ [LAUNCHER_PID_ENV]: launcherPid()
48829
48893
  };
48830
48894
  delete env[QUIET_RUN_MARK];
48831
48895
  const quiet = quietRun(args);
@@ -48836,7 +48900,7 @@ function reexec(args, plainProxy) {
48836
48900
  return waitForChild(proxiedChild(args, plainProxy, "inherit"));
48837
48901
  }
48838
48902
  function directChild(args) {
48839
- const env = { ...process.env, [DIRECT_RUN_MARK]: "1" };
48903
+ const env = { ...process.env, [DIRECT_RUN_MARK]: "1", [LAUNCHER_PID_ENV]: launcherPid() };
48840
48904
  delete env.NODE_USE_ENV_PROXY;
48841
48905
  if (env.NODE_OPTIONS !== void 0) env.NODE_OPTIONS = withoutEnvProxyFlag(env.NODE_OPTIONS);
48842
48906
  return spawn(
@@ -48981,7 +49045,7 @@ function torChild(argv2, stdio) {
48981
49045
  if (!options.mode) throw new ProxyUsageError("torChild needs --tor");
48982
49046
  return proxiedChild(options.args, options.proxy, stdio);
48983
49047
  }
48984
- var relayKeyOf, READ_ONLY_METHOD, readOnlyPost, TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRunOf, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, PROXY_REMOVED, ENV_PROXY_NAMES, OUTER_PROXY_ENV, DIRECT_RUN_MARK, USE_ENV_PROXY_FLAG, withoutEnvProxyFlag, TOR_MIN_VERSION, localTorProof, TOR_EXPERT_BUNDLE, TOR_EXPERT_BUNDLE_SHA256, TOR_EXPERT_BUNDLE_URL, TUNNEL_LINE, TOR_DEB_KEY, TOR_DEB_KEYRING, TOR_INSTALL, torStart, LOCAL_TUNNEL, torStartText, TOR_WINDOW, torNotRunningMessage, torUpdateHow, TOR_STOP_OLD_WINDOWS, torTooOldWhy, torTooOldMessage, NOTICE, TOR_NOTICE_LINE, ONION_DETAIL_LINE, withoutOnionDetail, QUIET_RUN_MARK, QUIET_RUNS, QUIET_TERMINAL_COMMANDS, INFER_SUBCOMMANDS, quietRun, TOR_TROUBLE_LINE, TOR_WAIT_PLAIN, TOR_TROUBLE_PLAIN, promptRedraw, TOR_TROUBLE_SAID, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER4, laneDispatchers, laneOfDispatcher, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT, TOR_FROM_CONFIG_SENTENCE, torStarter;
49048
+ var relayKeyOf, READ_ONLY_METHOD, readOnlyPost, TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRunOf, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, PROXY_REMOVED, ENV_PROXY_NAMES, OUTER_PROXY_ENV, DIRECT_RUN_MARK, USE_ENV_PROXY_FLAG, withoutEnvProxyFlag, TOR_MIN_VERSION, localTorProof, TOR_EXPERT_BUNDLE, TOR_EXPERT_BUNDLE_SHA256, TOR_EXPERT_BUNDLE_URL, TUNNEL_LINE, TOR_DEB_KEY, TOR_DEB_KEYRING, TOR_INSTALL, torStart, LOCAL_TUNNEL, torStartText, TOR_WINDOW, torNotRunningMessage, torUpdateHow, TOR_STOP_OLD_WINDOWS, torTooOldWhy, torTooOldMessage, NOTICE, TOR_NOTICE_LINE, ONION_DETAIL_LINE, withoutOnionDetail, QUIET_RUN_MARK, QUIET_RUNS, QUIET_TERMINAL_COMMANDS, INFER_SUBCOMMANDS, quietRun, TOR_TROUBLE_LINE, TOR_WAIT_PLAIN, TOR_TROUBLE_PLAIN, promptRedraw, TOR_TROUBLE_SAID, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER4, laneDispatchers, laneOfDispatcher, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT, TOR_FROM_CONFIG_SENTENCE, launcherPid, torStarter;
48985
49049
  var init_proxy = __esm({
48986
49050
  "wallet/src/net/proxy.mjs"() {
48987
49051
  "use strict";
@@ -48993,6 +49057,7 @@ var init_proxy = __esm({
48993
49057
  init_relayLimit();
48994
49058
  init_rpcDefault();
48995
49059
  init_phaseTiming();
49060
+ init_homeJob();
48996
49061
  relayKeyOf = (input) => {
48997
49062
  if (!isShinjukuRelayUrl(input)) return void 0;
48998
49063
  return `${new URL(typeof input === "string" || input instanceof URL ? input : input.url).origin}${FACILITATOR_RPC_PATH}`;
@@ -49181,6 +49246,7 @@ ${command}`;
49181
49246
  TOR_FROM_CONFIG_ENV = "SHINJUKU_WALLET_TOR_FROM_CONFIG";
49182
49247
  TOR_FROM_CONFIG_HINT = '. Tor is on for every command because setup recorded it (config.json "tor": true); to run this one command without Tor, add --no-tor (the servers then see your IP address)';
49183
49248
  TOR_FROM_CONFIG_SENTENCE = " Tor is on for every command because you chose it in setup.";
49249
+ launcherPid = () => process.env[LAUNCHER_PID_ENV] ?? String(process.ppid);
49184
49250
  }
49185
49251
  });
49186
49252
 
@@ -106022,6 +106088,16 @@ function refundEtaLeft(dueAtMs, now = Date.now()) {
106022
106088
  function floatRefundReadDue(record, now = Date.now()) {
106023
106089
  return Number.isSafeInteger(record?.refundDueAtMs) && now >= record.refundDueAtMs - REFUND_READ_EARLY_MS;
106024
106090
  }
106091
+ function sellerWaitMs(record) {
106092
+ const seconds2 = (record?.sellerRequirement ?? record?.requirement)?.maxTimeoutSeconds;
106093
+ if (!Number.isSafeInteger(seconds2) || seconds2 <= 0 || seconds2 > SELLER_TIMEOUT_ABSURD_SECONDS) return SELLER_TIMEOUT_MS;
106094
+ return Math.min(Math.max(seconds2 * 1e3, SELLER_TIMEOUT_MS), FLOAT_KEEP_BYTES_MS);
106095
+ }
106096
+ function sellerAnswerInit(url, init, ms) {
106097
+ const target = () => init.dispatcher ?? routeDispatcherOf(url) ?? globalThis[GLOBAL_DISPATCHER5];
106098
+ if (target() === void 0) return init;
106099
+ return { ...init, dispatcher: { dispatch: (options, handler) => target().dispatch({ ...options, headersTimeout: ms, bodyTimeout: ms }, handler) } };
106100
+ }
106025
106101
  async function sellerRefusalText(response) {
106026
106102
  let text3 = "";
106027
106103
  let reader;
@@ -107488,7 +107564,8 @@ async function payViaFloat({
107488
107564
  let resendUntilMs;
107489
107565
  let answeredAtMs;
107490
107566
  let dispatcher;
107491
- const sellerTimeout = AbortSignal.timeout(SELLER_TIMEOUT_MS);
107567
+ const waitMs = sellerWaitMs(record);
107568
+ const sellerTimeout = AbortSignal.timeout(waitMs);
107492
107569
  const stopResends = () => Date.now() > resendUntilMs || ["landed", "dead", "final", "cancelled"].includes(watch.verdict()?.state);
107493
107570
  for (let resend = 0; ; resend += 1) {
107494
107571
  const init = {
@@ -107500,7 +107577,7 @@ async function payViaFloat({
107500
107577
  };
107501
107578
  const socketWatch = resend === 0 ? watchRequestSocket(record.url) : void 0;
107502
107579
  try {
107503
- response = await fetchImpl(record.url, dispatcher === void 0 ? keepAliveInit(init) : { ...init, dispatcher });
107580
+ response = await fetchImpl(record.url, sellerAnswerInit(record.url, dispatcher === void 0 ? keepAliveInit(init) : { ...init, dispatcher }, waitMs));
107504
107581
  } catch (error) {
107505
107582
  unanswered2(error);
107506
107583
  } finally {
@@ -107802,13 +107879,13 @@ async function selfBroadcastLeg({ store, walName: walName2, record, sendRequest,
107802
107879
  mark("seller-sent");
107803
107880
  let response;
107804
107881
  try {
107805
- response = await fetchImpl(record.url, keepAliveInit({
107882
+ response = await fetchImpl(record.url, sellerAnswerInit(record.url, keepAliveInit({
107806
107883
  method: sendRequest.method,
107807
107884
  headers: { ...Object.fromEntries(sendRequest.headers), "PAYMENT-SIGNATURE": header },
107808
107885
  ...sendRequest.method === "POST" ? { body: Buffer.from(sendRequest.bodyBase64, "base64") } : {},
107809
107886
  redirect: "error",
107810
- signal: AbortSignal.timeout(SELLER_TIMEOUT_MS)
107811
- }));
107887
+ signal: AbortSignal.timeout(sellerWaitMs(record))
107888
+ }), sellerWaitMs(record)));
107812
107889
  } catch (error) {
107813
107890
  const timedOut = error?.name === "TimeoutError" || error?.cause?.name === "TimeoutError";
107814
107891
  if (!timedOut && !proxyRefusedConnection(error) && await resendUnanswered()) {
@@ -107939,7 +108016,7 @@ function paidResult(record, replayed) {
107939
108016
  float: { floatId: record.floatId }
107940
108017
  };
107941
108018
  }
107942
- var import_web3115, FLOAT_PAYMENT_SCHEMA2, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX2, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, FLOAT_MEMO_PATH, FLOAT_STATUS_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_READ_EARLY_MS, REFUND_ETA_MAX_SECONDS, refundDueOf, REFUND_SETTLED_AFTER_MS, REFUND_OUTCOME_NEWS_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, FLOAT_STATUS_START_MS, FLOAT_STATUS_POLL_MS, FLOAT_STATUS_TIMEOUT_MS, FLOAT_TRANSFER_DEAD, FLOAT_PAYMENT_REVERTED, VERDICT_POLL_MS, SELLER_ANSWER_VERDICT_MS, TRANSFER_MIN_BLOCKS_LEFT, TRANSFER_FRESH_MS, BLOCK_HEIGHT_TIMEOUT_MS, CONFIG_TIMEOUT_MS, MEMO_UPLOAD_TIMEOUT_MS, CONFIG_CACHE_MS, FLOAT_POOL_WARM_MS, DECIMAL3, POSITIVE, PENDING_CODES, FLOAT_FRONT_HEADER, hash7, fail22, GENERIC_REASON, SELLER_REASON_MAX, SELLER_REASON_READ_BYTES, FEE_PAYER_RESENDS, FEE_PAYER_RESEND_MS, GLOBAL_DISPATCHER5, sortedJson, MODEL_ID, floatPaymentWalName, floatRecordName, FLOAT_KEEP_BYTES_MS, memoryOnlyNeedsScrub, FLOAT_KEEP_OPEN_REQUEST_MS, configCache, configGeneration, configReadSequence, configReads, configRefreshes, SPECULATED_MAX_AGE_MS, speculatedQuotes, poolDomains, FLOAT_FACTS_SCHEMA, floatFacts, floatFactsKey, floatFactsMac, poolDomainReads, poolDomainWarmFailedAt, WARM_RETRY_MS, payerGenerator, etaEntry, loadPayerGenerator, plainText, BROADCAST_REFUSED, BROADCAST_REFUSED_STANDARD, BROADCAST_DEAD, TRANSFER_LANDING_WINDOW_MS, PROOF_DELIVERY_MS, SELF_BROADCAST_LEG1_LEFT_MS, BROADCAST_UNSIGNED, BROADCAST_NO_LOOKUP, BROADCAST_LANDED, BROADCAST_LIMIT_MS, BROADCAST_QUOTE_MARGIN_MS, SELF_BROADCAST_RESEND_MS, SELF_BROADCAST_UNANSWERED_RESEND_MS, SELLER_HEADERS_MAX_COUNT, SELLER_HEADERS_MAX_BYTES, FINAL_AFTER_202_MS, SELLER_HEADERS_KEPT;
108019
+ var import_web3115, FLOAT_PAYMENT_SCHEMA2, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX2, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, FLOAT_MEMO_PATH, FLOAT_STATUS_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_READ_EARLY_MS, REFUND_ETA_MAX_SECONDS, refundDueOf, REFUND_SETTLED_AFTER_MS, REFUND_OUTCOME_NEWS_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, SELLER_TIMEOUT_ABSURD_SECONDS, FLOAT_STATUS_START_MS, FLOAT_STATUS_POLL_MS, FLOAT_STATUS_TIMEOUT_MS, FLOAT_TRANSFER_DEAD, FLOAT_PAYMENT_REVERTED, VERDICT_POLL_MS, SELLER_ANSWER_VERDICT_MS, TRANSFER_MIN_BLOCKS_LEFT, TRANSFER_FRESH_MS, BLOCK_HEIGHT_TIMEOUT_MS, CONFIG_TIMEOUT_MS, MEMO_UPLOAD_TIMEOUT_MS, CONFIG_CACHE_MS, FLOAT_POOL_WARM_MS, DECIMAL3, POSITIVE, PENDING_CODES, FLOAT_FRONT_HEADER, hash7, fail22, GENERIC_REASON, SELLER_REASON_MAX, SELLER_REASON_READ_BYTES, FEE_PAYER_RESENDS, FEE_PAYER_RESEND_MS, GLOBAL_DISPATCHER5, sortedJson, MODEL_ID, floatPaymentWalName, floatRecordName, FLOAT_KEEP_BYTES_MS, memoryOnlyNeedsScrub, FLOAT_KEEP_OPEN_REQUEST_MS, configCache, configGeneration, configReadSequence, configReads, configRefreshes, SPECULATED_MAX_AGE_MS, speculatedQuotes, poolDomains, FLOAT_FACTS_SCHEMA, floatFacts, floatFactsKey, floatFactsMac, poolDomainReads, poolDomainWarmFailedAt, WARM_RETRY_MS, payerGenerator, etaEntry, loadPayerGenerator, plainText, BROADCAST_REFUSED, BROADCAST_REFUSED_STANDARD, BROADCAST_DEAD, TRANSFER_LANDING_WINDOW_MS, PROOF_DELIVERY_MS, SELF_BROADCAST_LEG1_LEFT_MS, BROADCAST_UNSIGNED, BROADCAST_NO_LOOKUP, BROADCAST_LANDED, BROADCAST_LIMIT_MS, BROADCAST_QUOTE_MARGIN_MS, SELF_BROADCAST_RESEND_MS, SELF_BROADCAST_UNANSWERED_RESEND_MS, SELLER_HEADERS_MAX_COUNT, SELLER_HEADERS_MAX_BYTES, FINAL_AFTER_202_MS, SELLER_HEADERS_KEPT;
107943
108020
  var init_floatPay = __esm({
107944
108021
  "wallet/src/pay/floatPay.mjs"() {
107945
108022
  "use strict";
@@ -107986,6 +108063,7 @@ var init_floatPay = __esm({
107986
108063
  FINISHING_POLL_MS = 2e3;
107987
108064
  FINISHING_LIMIT_MS = 6e4;
107988
108065
  SELLER_TIMEOUT_MS = 12e4;
108066
+ SELLER_TIMEOUT_ABSURD_SECONDS = 86400;
107989
108067
  FLOAT_STATUS_START_MS = 45e3;
107990
108068
  FLOAT_STATUS_POLL_MS = 5e3;
107991
108069
  FLOAT_STATUS_TIMEOUT_MS = 5e3;
@@ -135533,7 +135611,7 @@ ${canonical5}`;
135533
135611
  const balance = {
135534
135612
  name: "wallet_balance",
135535
135613
  title: "Balance",
135536
- description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what a payment can spend) and the public (unshielded) USDC. Always safe: it ${BALANCE_READ_DOES}. On "show my wallet", print \`card\` as it is (at most 8 lines). largestPaymentAtomic: the most ONE payment can spend now; a payment above it is not refused: pay_for_service gets the balance ready first (a tell-first answer, then doneInMinutesLow to doneInMinutesHigh minutes) (changeSettling: 0 only while the last payment's change settles, about changeReadyInSeconds; then one payment can use it all, or up to changeUpToAtomic). balanceParts (above 1): never tell the user about parts, accounts or a merge; say only that a large payment or send first gets the balance ready. balanceAtomic is one number; breakdown names the rest (shieldedAtomic is the same value; privateSendPendingAtomic is what a running private send has not sent yet, already taken off; refundsInFlightAtomic is a refund Shinjuku proved, on its way, not in balanceAtomic; refundsNotCertainAtomic a refund the seller may have kept, in no number; openPaymentsAtomic: paid, waiting on its seller (same request_id finishes it, else refunded); setAsideAtomic (when not 0): value an older version set aside; totalAtomic adds these; during a pool move, pools lists each pool's share and movingAtomic is value on its way to the new pool; the move field shows the move, poolSwitches each switch). unshieldedAtomic is the public USDC on the wallet's own Solana key (ownAddress), which wallet_shield makes private; unshieldedSolLamports is that key's SOL (Shinjuku pays every fee and rent). A stale balance (a new deposit, a received payment) reads the chain first; past about 20 s it answers the last balance with refreshing: true and finishes in the background (call again in 30 s); with a refreshReason that balance is exact and counts what the reason names (payment_settling: a payment that settles; account_join: all of it, while the wallet gets it ready to send; pool_move: value that arrives from the old pool; refund_check: a refund counted from an old read, which the wallet checks now (never tell the user it is still on its way); first_check: a new wallet's own landed deposits (its first history check)). status reading (balanceAtomic null): the first read of a wallet that may hold money is not done; call again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason. ownKeyTokens: the other tokens on ownAddress (not its USDC; [] when none; null when not read now: say nothing of other tokens), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; this wallet has no tool that sells, swaps, or sends them: they stay on ownAddress. earlierDeposits (when any): USDC at an earlier pool's one-time deposit address, each with a notice line to tell the user; the wallet moves it itself. addFunds (when any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when not read in time). startsAtNextRequest true (old name): its deposit starts by itself once USDC is seen there (startsInMinutes; 0: at once). landingAtomic (when not 0): added USDC whose deposit landed on chain but is not in balanceAtomic yet; this call finishes it (up to about 20 s), else call again when next says. session: the spending limits (null or "none": no limit), what is left, and how to change them (session.summary, only when a limit is set). With no limit set, say nothing about limits or the session counters. To add money, use wallet_add_funds. version: this wallet's version. Amounts are atomic (1000000 = 1 USDC); say and usdc give USDC.`,
135614
+ description: `Use this for "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what a payment can spend) and the public (unshielded) USDC. Always safe: it ${BALANCE_READ_DOES}. On "show my wallet", print \`card\` as it is (at most 8 lines). largestPaymentAtomic: the most ONE payment can spend now; a payment above it is not refused: pay_for_service gets the balance ready first (a tell-first answer, then doneInMinutesLow to doneInMinutesHigh minutes) (changeSettling: 0 only while the last payment's change settles, about changeReadyInSeconds; then one payment can use it all, or up to changeUpToAtomic). balanceParts (above 1): never mention parts, accounts or a merge; say only that a large payment or send first gets the balance ready. balanceAtomic is one number; breakdown names the rest (shieldedAtomic is the same value; privateSendPendingAtomic is what a running private send has not sent yet, already taken off; refundsInFlightAtomic is a refund Shinjuku proved, on its way, not in balanceAtomic; refundsNotCertainAtomic a refund the seller may have kept, in no number; openPaymentsAtomic: paid, waiting on its seller (same request_id finishes it, else refunded); setAsideAtomic (when not 0): value an older version set aside; totalAtomic adds these; during a pool move, pools lists each pool's share, movingAtomic is value on its way to the new pool, and move and poolSwitches show the move). unshieldedAtomic is the public USDC on the wallet's own Solana key (ownAddress), which wallet_shield makes private; unshieldedSolLamports is that key's SOL (Shinjuku pays every fee and rent). A stale balance (a new deposit, a received payment) reads the chain first; past about 20 s it answers the last balance with refreshing: true and finishes in the background (call again in 30 s); with a refreshReason that balance is exact and counts what the reason names (payment_settling: a payment that settles; account_join: all of it, while the wallet gets it ready to send; pool_move: value that arrives from the old pool; refund_check: a refund counted from an old read, which the wallet checks now (never say it is still on its way); first_check: a new wallet's own landed deposits (its first history check)). status reading (balanceAtomic null): the first read of a wallet that may hold money is not done; call again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason. ownKeyTokens: the other tokens on ownAddress (not its USDC; [] when none; null when not read now: say nothing of other tokens), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; no wallet tool sells, swaps or sends them: they stay on ownAddress. earlierDeposits (when any): USDC at an earlier pool's one-time deposit address, each with a notice line to tell the user; the wallet moves it itself. addFunds (when any): each open wallet_add_funds address, its state and the USDC known to wait there (heldAtomic; null with heldNote when not read in time). startsAtNextRequest true (old name): it starts by itself once USDC is seen there (startsInMinutes; 0: at once). landingAtomic (when not 0): added USDC that landed on chain, not in balanceAtomic yet; this call finishes it (up to about 20 s), else call again when next says. session: the spending limits (null or "none": no limit), what is left, and how to change them (session.summary, only when a limit is set). With no limit set, say nothing about limits or counters. To add money, use wallet_add_funds. version: this wallet's version. Amounts are atomic (1000000 = 1 USDC); say and usdc give USDC.`,
135537
135615
  inputSchema: { type: "object", properties: {
135538
135616
  verbose: { type: "boolean", description: "Optional. true adds `internal`: the engineering numbers (funded balance records, notes, payments without waiting, a 0 set-aside value). Default false." }
135539
135617
  }, additionalProperties: false },
@@ -140459,7 +140537,7 @@ ${canonical5}`;
140459
140537
  name: "wallet_unshield",
140460
140538
  // UX130-13: a host that defers tools may show only the title: it says where a send goes by default.
140461
140539
  title: "Send USDC privately",
140462
- description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. This wallet's own public address (ownAddress in wallet_balance: it holds the public USDC, wallet_shield deposits from it) is refused as \`to\`: an exit there would show on chain that the private balance is this wallet's, even if that address never deposited (wallet_add_funds deposits come from one-time addresses, not from it; never tell the user they came from it). Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address needs the user's yes in a dialog of this app; a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor. Each call answers within about 20 s (at once when the send ends; a dialog may take longer) with ok: true and stage: confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends, and each call goes on with it (joinSteps: its steps, in order; doneInMinutesLow to doneInMinutesHigh: the time to say). For the status, call wallet_unshield again with the SAME request_id, amount and to. When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: when this app sends a progress token with that next call, the call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional unshield limit the user sets may bound each call and what this wallet sends in any 24 hours, from every app (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped or calls rejected or interrupted, or one that failed, may still send: call wallet_cancel (no request_id) before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140540
+ description: `Use this for "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (never say how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. This wallet's own public address (ownAddress in wallet_balance: it holds the public USDC, wallet_shield deposits from it) is refused as \`to\`: an exit there would show on chain that the private balance is this wallet's, even if that address never deposited (wallet_add_funds deposits come from one-time addresses, not from it; never tell the user they came from it). Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address needs the user's yes in a dialog of this app; a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor. Each call answers within about 20 s (at once when the send ends; a dialog may take longer) with ok: true and stage: confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready, then sends; each call goes on with it (joinSteps: its steps, in order; doneInMinutesLow to doneInMinutesHigh: the time to say). For the status, call wallet_unshield again with the SAME request_id, amount and to. When the balance must first be made ready, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: when this app sends a progress token with that next call, the call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional unshield limit the user sets may bound each call and what this wallet sends in any 24 hours, from every app (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped, rejected or interrupted, or one that failed, may still send: call wallet_cancel (no request_id) before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140463
140541
  inputSchema: {
140464
140542
  type: "object",
140465
140543
  properties: {
@@ -146898,39 +146976,6 @@ var init_consolePaste = __esm({
146898
146976
  }
146899
146977
  });
146900
146978
 
146901
- // wallet/src/cli/homeJob.mjs
146902
- function stepWords(lead, words2, line, { env = process.env, first, inline = false } = {}) {
146903
- const key = homeKeyOf(words2);
146904
- const comma = /\bor$/.test(lead) ? "" : ",";
146905
- if (inHomeJob(env) && key) return `${lead}${comma} ${first ? `${first}, then ` : ""}press [${key}] on the home screen.`;
146906
- return inline ? `${lead}: ${line}` : `${lead}${comma} run:
146907
- ${line}`;
146908
- }
146909
- function stepName(words2, line, { env = process.env } = {}) {
146910
- const key = homeKeyOf(words2);
146911
- return inHomeJob(env) && key ? `press [${key}] on the home screen` : line;
146912
- }
146913
- var HOME_JOB_ENV, inHomeJob, HOME_KEY, JOB_NAME, jobLabel, homeKeyOf;
146914
- var init_homeJob = __esm({
146915
- "wallet/src/cli/homeJob.mjs"() {
146916
- "use strict";
146917
- HOME_JOB_ENV = "SHINJUKU_WALLET_HOME_JOB";
146918
- inHomeJob = (env = process.env) => env?.[HOME_JOB_ENV] === "1";
146919
- HOME_KEY = { "add-funds": "a", unshield: "u", curl: "p", chat: "t", setup: "s", restore: "r", limits: "l" };
146920
- JOB_NAME = {
146921
- "add-funds": "add funds",
146922
- unshield: "send money out",
146923
- curl: "pay a link",
146924
- chat: "private AI chat",
146925
- setup: "setup",
146926
- restore: "restore",
146927
- limits: "spending limits"
146928
- };
146929
- jobLabel = (name, typed, env = process.env) => inHomeJob(env) && JOB_NAME[name] ? JOB_NAME[name] : typed;
146930
- homeKeyOf = (words2) => HOME_KEY[String(words2 ?? "").trim().split(/\s+/)[0]];
146931
- }
146932
- });
146933
-
146934
146979
  // wallet/src/cli/homeMark.mjs
146935
146980
  var MARK_WIDTH, MARK2, MARK_CLASS, EYE, COMPACT_MARK;
146936
146981
  var init_homeMark = __esm({
@@ -147789,7 +147834,15 @@ function homeScreenNeeds() {
147789
147834
  }
147790
147835
  return { columns: MIN_COLUMNS, rows: 80 };
147791
147836
  }
147792
- async function runJobView({ title, run: run2, stdout = process.stdout, stdin = process.stdin, env = process.env } = {}) {
147837
+ async function runJobView({
147838
+ title,
147839
+ run: run2,
147840
+ stdout = process.stdout,
147841
+ stdin = process.stdin,
147842
+ env = process.env,
147843
+ launcherGone = launcherWatch(),
147844
+ platform = process.platform
147845
+ } = {}) {
147793
147846
  const codes = paletteFor(screenColor(stdout, env));
147794
147847
  const paint2 = (parts) => parts.map(([text3, cls]) => codes && cls ? `${codes[cls]}${text3}${RESET2}` : text3).join("");
147795
147848
  const write = (text3) => {
@@ -147811,6 +147864,22 @@ ${rule}
147811
147864
  code2 = 1;
147812
147865
  }
147813
147866
  const end2 = code2 === 0 ? [["\u25C6 ", "crim"], [`${clean4(title)}: done`, "bone"]] : code2 === 130 ? [["\u25C6 ", "crim"], [`${clean4(title)}: stopped (Ctrl+C)`, "bone"]] : [["\u25CB ", "mute"], [`${clean4(title)}: it stopped; its message is above`, "bone"]];
147867
+ const gone = () => {
147868
+ try {
147869
+ return launcherGone();
147870
+ } catch {
147871
+ return false;
147872
+ }
147873
+ };
147874
+ const shellAgain = (first) => write(`${first ? `
147875
+ ${"\u2500".repeat(width)}
147876
+ ${end2.map(([text3]) => text3).join("")}
147877
+ ` : ""}The wallet screen has closed: the command that started it ended with the Ctrl+C.
147878
+ ` + (platform === "win32" ? 'If this window asks "Terminate batch job (Y/N)?", type Y and press Enter. ' : "") + "To open the wallet again, run the same command.\n");
147879
+ if (gone()) {
147880
+ shellAgain(true);
147881
+ return { code: code2, next: "quit", launcherGone: true };
147882
+ }
147814
147883
  write(`
147815
147884
  ${rule}
147816
147885
  ${paint2(end2)}
@@ -147820,15 +147889,24 @@ ${paint2([["any other key, [q] too, goes back to the home screen", "mute"]])}
147820
147889
  if (!stdin?.isTTY || typeof stdin.setRawMode !== "function") return { code: code2, next: "quit" };
147821
147890
  const rawBefore = Boolean(stdin.isRaw);
147822
147891
  let key;
147892
+ let ended2 = false;
147893
+ let poll;
147823
147894
  try {
147824
147895
  stdin.setRawMode(true);
147825
147896
  key = await new Promise((done) => {
147826
147897
  stdin.once("data", (chunk) => done(String(chunk)));
147898
+ poll = setInterval(() => {
147899
+ if (gone()) {
147900
+ ended2 = true;
147901
+ done(void 0);
147902
+ }
147903
+ }, LAUNCHER_POLL_MS);
147827
147904
  stdin.resume();
147828
147905
  });
147829
147906
  } catch {
147830
147907
  key = void 0;
147831
147908
  } finally {
147909
+ clearInterval(poll);
147832
147910
  stdin.pause();
147833
147911
  await stopReading(stdin);
147834
147912
  try {
@@ -147836,8 +147914,24 @@ ${paint2([["any other key, [q] too, goes back to the home screen", "mute"]])}
147836
147914
  } catch {
147837
147915
  }
147838
147916
  }
147917
+ if (ended2) {
147918
+ shellAgain(false);
147919
+ return { code: code2, next: "quit", launcherGone: true };
147920
+ }
147839
147921
  return key === void 0 ? { code: code2, next: "quit" } : { code: code2, next: "back", key };
147840
147922
  }
147923
+ function launcherWatch(launcher = LAUNCHER_AT_LOAD, parent = PARENT_AT_LOAD) {
147924
+ return () => {
147925
+ if (!Number.isSafeInteger(launcher) || launcher <= 1) return false;
147926
+ if (process.ppid !== parent) return true;
147927
+ try {
147928
+ process.kill(launcher, 0);
147929
+ return false;
147930
+ } catch (error) {
147931
+ return error?.code !== "EPERM";
147932
+ }
147933
+ };
147934
+ }
147841
147935
  async function waitForRoom({ stdout = process.stdout, stdin = process.stdin, env = process.env } = {}) {
147842
147936
  if (!stdin?.isTTY || typeof stdin.setRawMode !== "function") return "off";
147843
147937
  const needs = homeScreenNeeds();
@@ -147892,11 +147986,12 @@ async function waitForRoom({ stdout = process.stdout, stdin = process.stdin, env
147892
147986
  }
147893
147987
  }
147894
147988
  }
147895
- var MIN_COLUMNS, WIDE_COLUMNS, WIDE_WIDTH, LEFT_X, LEFT_WIDTH, PANEL_X, PANEL_WIDTH, TOP, BAND_MS, BAND_CHANCE, KATAKANA, FONT, AGENT_KEY, HELP_KEY, QUIT_KEY, KEY_HELP, lettersOf, PANELS, ALL_PIECES, KEYS_LINE, FIRST_LINES, SETUP_KEY, RESTORE_KEY, END_KEYS, RESTORE_STEP, HINT, tooSmallLines, COLORS, COLORS_16, RESET2, DATA_SYMBOLS, HOME_SHAPE, FIRST_SHAPE, WIDE, WORD_TOP, WORD_BOTTOM, WORD_WIDTH, ROMAJI, BIG_MARK, SMALL_MARK, LOCKUP_GAP, LOCKUP_WIDTH, Screen, setupLine, randomKatakana, MARK_TONES, PUBLIC_STEP, stackHeight;
147989
+ var MIN_COLUMNS, WIDE_COLUMNS, WIDE_WIDTH, LEFT_X, LEFT_WIDTH, PANEL_X, PANEL_WIDTH, TOP, BAND_MS, BAND_CHANCE, KATAKANA, FONT, AGENT_KEY, HELP_KEY, QUIT_KEY, KEY_HELP, lettersOf, PANELS, ALL_PIECES, KEYS_LINE, FIRST_LINES, SETUP_KEY, RESTORE_KEY, END_KEYS, RESTORE_STEP, HINT, tooSmallLines, COLORS, COLORS_16, RESET2, DATA_SYMBOLS, HOME_SHAPE, FIRST_SHAPE, WIDE, WORD_TOP, WORD_BOTTOM, WORD_WIDTH, ROMAJI, BIG_MARK, SMALL_MARK, LOCKUP_GAP, LOCKUP_WIDTH, Screen, setupLine, randomKatakana, MARK_TONES, PUBLIC_STEP, stackHeight, LAUNCHER_POLL_MS, LAUNCHER_AT_LOAD, PARENT_AT_LOAD;
147896
147990
  var init_homeScreen = __esm({
147897
147991
  "wallet/src/cli/homeScreen.mjs"() {
147898
147992
  "use strict";
147899
147993
  init_homeMark();
147994
+ init_homeJob();
147900
147995
  MIN_COLUMNS = 80;
147901
147996
  WIDE_COLUMNS = 120;
147902
147997
  WIDE_WIDTH = 119;
@@ -148057,6 +148152,9 @@ var init_homeScreen = __esm({
148057
148152
  "at-most": "your private balance is at its most (5 USDC)."
148058
148153
  };
148059
148154
  stackHeight = (blocks) => blocks.reduce((sum2, block) => sum2 + block.height, 0) + blocks.length - 1;
148155
+ LAUNCHER_POLL_MS = 250;
148156
+ LAUNCHER_AT_LOAD = /^[0-9]+$/.test(process.env[LAUNCHER_PID_ENV] ?? "") ? Number(process.env[LAUNCHER_PID_ENV]) : process.ppid;
148157
+ PARENT_AT_LOAD = process.ppid;
148060
148158
  }
148061
148159
  });
148062
148160
 
@@ -148767,9 +148865,11 @@ async function runChat({ io, values, deps = {} }) {
148767
148865
  return typeof deps.balanceLine === "function" ? deps.balanceLine(parts) : `${balanceWords(parts)}.`;
148768
148866
  };
148769
148867
  const entryOf = (id) => catalog.models.find((entry) => entry.id === id) ?? models.find((entry) => entry.id === id);
148868
+ const fullTokensOf = (id) => defaultMaxTokens(id, liveOf(id) ?? models.find((entry) => entry.id === id));
148770
148869
  const tokensFor = (id, messages, limit) => {
148771
148870
  try {
148772
- return BigInt(inference.ceilingAtomic({ model: id, messages, maxTokens: CHAT_MAX_TOKENS, liveEntry: liveOf(id) })) <= limit ? CHAT_MAX_TOKENS : CHAT_SHORT_TOKENS;
148871
+ const full = fullTokensOf(id);
148872
+ return BigInt(inference.ceilingAtomic({ model: id, messages, maxTokens: full, liveEntry: liveOf(id) })) <= limit ? full : CHAT_SHORT_TOKENS;
148773
148873
  } catch {
148774
148874
  return CHAT_SHORT_TOKENS;
148775
148875
  }
@@ -148778,7 +148878,7 @@ async function runChat({ io, values, deps = {} }) {
148778
148878
  let maxTokens = tokensFor(id, [{ role: "user", content: "x" }], turnLimit());
148779
148879
  const messages = SHORT_QUESTION;
148780
148880
  const ceilingAtomic2 = BigInt(inference.ceilingAtomic({ model: id, messages, maxTokens, liveEntry: liveOf(id) }));
148781
- if (ceilingAtomic2 > caps.answer && maxTokens !== CHAT_MAX_TOKENS) maxTokens = CHAT_MAX_TOKENS;
148881
+ if (ceilingAtomic2 > caps.answer && maxTokens !== fullTokensOf(id)) maxTokens = fullTokensOf(id);
148782
148882
  return priceOf2({ model: id, messages, maxTokens });
148783
148883
  };
148784
148884
  const priceText = answerPriceWords;
@@ -148874,7 +148974,7 @@ async function runChat({ io, values, deps = {} }) {
148874
148974
  "Private: each answer is paid from your private balance, and the chain does not show which wallet paid. Your words go out only through Tor, so the AI provider and the company that runs the model never see your IP address. They do see your words and the answers.",
148875
148975
  // payAndFetch opts.memoryOnly (floatPay.mjs MEMORY ONLY): the payment record holds amounts, ids and hashes only.
148876
148976
  "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.",
148877
- `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 limit) plus the 0.001 USDC Shinjuku fee, up front, so a short answer costs as much as a long one; what an answer does not use is not refunded. With ${state2.model}, ${lowFirst(costLine2(state2.model))}. ` + (state2.mode === "talk" ? "Each question sends the whole conversation, so a long chat costs more (/new starts again)." : "In this mode each question goes alone, so each costs about the same."),
148977
+ `Cost: each answer pays the AI provider's price for up to ${CHAT_MAX_TOKENS} tokens (${CHAT_SHORT_TOKENS} for a costly model, or one whose longer answer is above your limit) plus the 0.001 USDC Shinjuku fee, up front, so a short answer costs as much as a long one; what an answer does not use is not refunded. With ${state2.model}, ${lowFirst(costLine2(state2.model))}. ` + (state2.mode === "talk" ? "Each question sends the whole conversation, so a long chat costs more (/new starts again)." : "In this mode each question goes alone, so each costs about the same."),
148878
148978
  `Mode: ${modeName()}. One question at a time sends each question alone and names each payment; a conversation lets the AI remember the chat and shows the running total. /mode 1 or /mode 2 changes it.`,
148879
148979
  caps.answer >= NO_CAP && caps.chat >= NO_CAP ? `Limits: none${capsSourceText(caps)}` : `Limits: ${capWords(caps.answer)} an answer and ${capWords(caps.chat)} for this chat${capsSourceText(caps)}` + (caps.limits.maxSessionAtomic === void 0 ? "" : ` Your limit of ${usdcWords(caps.limits.maxSessionAtomic)} in any 24 hours counts every payment from this wallet.`),
148880
148980
  `Commands: /new starts a new conversation, /mode changes the mode, /cost shows what this chat paid, /balance shows your private balance, /model picks another model of the same AI company (/model <id> names one), /provider picks another AI company and then its model (${now.models.length ? `${now.models.length} models served now` : "none served right now"}), /help shows this, /exit (or /quit, Ctrl+D) ends the chat${state2.pending ? ", /retry finishes the unfinished answer" : ""}.`
@@ -156037,7 +156137,7 @@ async function addFunds(values, deps) {
156037
156137
  plan,
156038
156138
  save: save2,
156039
156139
  proxyArgs: proxy.args,
156040
- childEnv: { ...proxy.env, [PASSPHRASE_ENV]: passphrase2, [RPC_ENV2]: rpcUrl, [STEP_VERBOSE_ENV]: ctx.verbose ? "1" : "0" },
156140
+ childEnv: { ...proxy.env, [PASSPHRASE_ENV]: passphrase2, [RPC_ENV2]: rpcUrl, ...relayOriginsEnv(), [STEP_VERBOSE_ENV]: ctx.verbose ? "1" : "0" },
156041
156141
  // A1: a quiet terminal run names no plan id and no stage (funds-status and --verbose do).
156042
156142
  say: (stage, text3) => io.err(ctx.quiet ? `add-funds: ${text3.replace(STEP_WORDS, "")}
156043
156143
  ` : `add-funds ${plan.fundsId} [${stage}] ${text3}
@@ -158434,16 +158534,21 @@ var CURL_SHORT_FLAGS = Object.freeze({ "-X": "--method", "-H": "--header", "-d":
158434
158534
  function curlArgs(command, args) {
158435
158535
  const known = new Map(command.flags.map((entry) => [entry.name, entry]));
158436
158536
  const out = [];
158537
+ let afterJsonData = false;
158437
158538
  for (let index = 0; index < args.length; index += 1) {
158438
158539
  const token = args[index];
158439
158540
  const name = CURL_SHORT_FLAGS[token] ?? token;
158440
158541
  if (!name.startsWith("-")) {
158542
+ if (afterJsonData && out.includes("--url")) throw new PublicFailure(CURL_JSON_ARGUMENT_REFUSAL);
158543
+ afterJsonData = false;
158441
158544
  out.push("--url", token);
158442
158545
  continue;
158443
158546
  }
158547
+ afterJsonData = false;
158444
158548
  out.push(name);
158445
158549
  if (known.has(name) && !known.get(name).boolean && index + 1 < args.length) {
158446
158550
  out.push(args[index + 1]);
158551
+ afterJsonData = name === "--data" && /^\s*[[{]/.test(args[index + 1]);
158447
158552
  index += 1;
158448
158553
  }
158449
158554
  }
@@ -158481,13 +158586,34 @@ function validateCurl(values, env) {
158481
158586
  }
158482
158587
  function curlBody(values) {
158483
158588
  const file = values.get("--data-file") ?? (values.get("--data")?.startsWith("@") ? values.get("--data").slice(1) : void 0);
158484
- if (file === void 0) return values.get("--data");
158589
+ let body;
158590
+ if (file === void 0) body = values.get("--data");
158591
+ else {
158592
+ try {
158593
+ body = readFileSync(resolve41(file));
158594
+ } catch {
158595
+ throw new PublicFailure("fetch_options_invalid: the body file (--data-file, or -d @file) could not be read; pass a file you can read. Nothing was sent");
158596
+ }
158597
+ }
158598
+ curlJsonBody(values, body, file !== void 0);
158599
+ return body;
158600
+ }
158601
+ function curlJsonBody(values, body, fromFile) {
158602
+ if (body === void 0) return;
158603
+ const type = (values.get("--header") ?? []).find((line) => line.slice(0, line.indexOf(":")).trim().toLowerCase() === "content-type");
158604
+ const typeValue = type === void 0 ? void 0 : type.slice(type.indexOf(":") + 1).trim().toLowerCase();
158605
+ const jsonType = typeValue !== void 0 && /^application\/(?:[\w.+-]+\+)?json\b/.test(typeValue);
158606
+ const text3 = Buffer.isBuffer(body) ? body.toString("utf8") : String(body);
158607
+ const asJson = fromFile ? jsonType : typeValue === void 0 ? /^\s*[[{]/.test(text3) : jsonType;
158608
+ if (!asJson) return;
158485
158609
  try {
158486
- return readFileSync(resolve41(file));
158610
+ JSON.parse(text3.replace(/^/, ""));
158611
+ return;
158487
158612
  } catch {
158488
- throw new PublicFailure("fetch_options_invalid: the body file (--data-file, or -d @file) could not be read; pass a file you can read. Nothing was sent");
158489
158613
  }
158614
+ throw new PublicFailure(fromFile ? "fetch_options_invalid: the body file is not valid JSON, so it was not sent; nothing was paid. Fix the file (every name and text in double quotes) and run the same command again" : CURL_JSON_ARGUMENT_REFUSAL);
158490
158615
  }
158616
+ var CURL_JSON_ARGUMENT_REFUSAL = "fetch_options_invalid: the body is not valid JSON, so it was not sent; nothing was paid. PowerShell removes the double quotes inside a JSON argument: save the JSON in a file and pass it as -d @<file> (or --data-file <file>), then run the command again";
158491
158617
  function curlFailure(error) {
158492
158618
  if (error instanceof SpendRefusalError && knownErrorCode(error.error)) {
158493
158619
  return new PublicFailure(`${error.error}: ${error.detail}`, { cause: error });
@@ -160264,7 +160390,7 @@ function startKeepWarm(options, env = process.env) {
160264
160390
  ...proxy.env,
160265
160391
  [PASSPHRASE_ENV3]: options.passphrase,
160266
160392
  [KEEP_WARM_PARENT_ENV]: "1",
160267
- ...options.rpcUrl ? { [RPC_ENV3]: options.rpcUrl } : {}
160393
+ ...options.rpcUrl ? { [RPC_ENV3]: options.rpcUrl, ...relayOriginsEnv() } : {}
160268
160394
  },
160269
160395
  stdio: ["pipe", "ignore", "pipe"],
160270
160396
  windowsHide: true