@bnbagent/studio-cli 0.0.6-alpha.1 → 0.0.6-alpha.2

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/dist/bag.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  B402_RUNTIME_KEYS,
4
4
  CliExit,
5
+ DEFAULT_B402_PRICE_USD,
5
6
  act,
6
7
  agentcoreFlavor,
7
8
  b402Credentials,
@@ -19,6 +20,7 @@ import {
19
20
  hasMcpFace,
20
21
  hasX402Face,
21
22
  nativeProtocolOf,
23
+ normalizeB402PriceUsd,
22
24
  normalizeProtocolFaces,
23
25
  printErr,
24
26
  printOut,
@@ -34,8 +36,11 @@ import {
34
36
  trialFromDeployCliJson,
35
37
  whichBin,
36
38
  withDeployFiles,
37
- x402DeploySummary
38
- } from "./chunk-M3ODFCA7.js";
39
+ x402DeploySummary,
40
+ x402SellerIsFree,
41
+ x402SellerPricingState,
42
+ x402SellerUsesB402
43
+ } from "./chunk-6EYSXRFD.js";
39
44
  import {
40
45
  TWAK_CLI_MIN_VERSION,
41
46
  TWAK_CLI_VERSION,
@@ -97,7 +102,7 @@ import {
97
102
  var CAMPAIGN_DOC_URL = "https://www.bnbchain.org/en/blog/bnb-agent-studio-is-live-on-bnb-chain-ai-agents-from-one-prompt";
98
103
  var CAMPAIGN_CHECK_TIMEOUT_MS = 6e3;
99
104
  async function fetchCampaignActive() {
100
- const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-N6TPN6XA.js");
105
+ const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-3U2FESF4.js");
101
106
  const controller = new AbortController();
102
107
  const timer = setTimeout(() => controller.abort(), CAMPAIGN_CHECK_TIMEOUT_MS);
103
108
  try {
@@ -2095,6 +2100,77 @@ function devPortInUse(port = 9e3, timeoutMs = 250) {
2095
2100
  });
2096
2101
  }
2097
2102
 
2103
+ // src/cli/_erc8183Config.ts
2104
+ var ERC8183_ADDRESS_OVERRIDE_KEYS = [
2105
+ "ERC8183_COMMERCE_ADDRESS",
2106
+ "ERC8183_ROUTER_ADDRESS",
2107
+ "ERC8183_POLICY_ADDRESS"
2108
+ ];
2109
+ var MAX_UINT256 = 2n ** 256n - 1n;
2110
+ function erc8183PricingState(pay) {
2111
+ const priceRaw = pay.price;
2112
+ const price = String(priceRaw ?? "").trim();
2113
+ if (!price) return { kind: "unset" };
2114
+ if (typeof priceRaw !== "string") {
2115
+ return { kind: "invalid", field: "price", value: price };
2116
+ }
2117
+ const minRaw = pay.min_price;
2118
+ const maxRaw = pay.max_price;
2119
+ const minPrice = String(minRaw ?? "").trim() || "0";
2120
+ const maxPrice = String(maxRaw ?? "").trim();
2121
+ if (minRaw !== void 0 && typeof minRaw !== "string") {
2122
+ return { kind: "invalid", field: "min_price", value: minPrice };
2123
+ }
2124
+ if (maxRaw !== void 0 && typeof maxRaw !== "string") {
2125
+ return { kind: "invalid", field: "max_price", value: maxPrice };
2126
+ }
2127
+ for (const [field, value] of [
2128
+ ["price", price],
2129
+ ["min_price", minPrice],
2130
+ ["max_price", maxPrice]
2131
+ ]) {
2132
+ if ((field !== "max_price" || value !== "") && !/^\d+$/.test(value)) {
2133
+ return { kind: "invalid", field, value };
2134
+ }
2135
+ }
2136
+ const list = BigInt(price);
2137
+ const min = BigInt(minPrice);
2138
+ const max = maxPrice ? BigInt(maxPrice) : MAX_UINT256;
2139
+ if (list > MAX_UINT256 || min > MAX_UINT256 || max > MAX_UINT256) {
2140
+ const [field, value] = list > MAX_UINT256 ? ["price", price] : min > MAX_UINT256 ? ["min_price", minPrice] : ["max_price", maxPrice];
2141
+ return { kind: "invalid", field, value };
2142
+ }
2143
+ const clampedToMax = list < max ? list : max;
2144
+ const effective = min > clampedToMax ? min : clampedToMax;
2145
+ const base = {
2146
+ listPrice: list,
2147
+ minPrice: min,
2148
+ maxPrice: max,
2149
+ effectivePrice: effective
2150
+ };
2151
+ if (list > 0n && effective === 0n) {
2152
+ return { kind: "clamped_to_zero", ...base };
2153
+ }
2154
+ return { kind: effective === 0n ? "free" : "paid", ...base };
2155
+ }
2156
+ function erc8183ContractOverrideState(env = process.env) {
2157
+ const present = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
2158
+ (key) => Boolean(env[key]?.trim())
2159
+ );
2160
+ const missing = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
2161
+ (key) => !env[key]?.trim()
2162
+ );
2163
+ const invalid = present.filter(
2164
+ (key) => !/^0x[0-9a-fA-F]{40}$/.test(env[key]?.trim() ?? "")
2165
+ );
2166
+ return {
2167
+ mode: present.length === 0 ? "canonical" : invalid.length > 0 ? "invalid" : missing.length === 0 ? "custom" : "partial",
2168
+ present: [...present],
2169
+ missing: [...missing],
2170
+ invalid
2171
+ };
2172
+ }
2173
+
2098
2174
  // src/cli/config.ts
2099
2175
  var MAX_QUOTE_TTL_SECONDS = NegotiationHandler.MAX_QUOTE_TTL_SECONDS;
2100
2176
  function registerConfig(program) {
@@ -2273,6 +2349,38 @@ function validateKnownKey(parts, value) {
2273
2349
  }
2274
2350
  return null;
2275
2351
  }
2352
+ function isErc8183AmountKey(parts) {
2353
+ return parts.length === 3 && parts[0] === "payments" && parts[1] === "erc8183" && ["price", "min_price", "max_price"].includes(parts[2]);
2354
+ }
2355
+ function isB402PriceKey(parts) {
2356
+ return parts.length === 3 && parts[0] === "payments" && parts[1] === "x402_seller" && parts[2] === "price_usd";
2357
+ }
2358
+ function coerceKnownValue(parts, rawValue, typeFlag) {
2359
+ if (!isErc8183AmountKey(parts) && !isB402PriceKey(parts)) {
2360
+ return coerceValue(rawValue, typeFlag);
2361
+ }
2362
+ if (isB402PriceKey(parts)) {
2363
+ if (typeFlag !== "auto" && typeFlag !== "string") {
2364
+ throw new Error(
2365
+ `${parts.join(".")} is stored as a decimal string; use --type string or omit --type.`
2366
+ );
2367
+ }
2368
+ return normalizeB402PriceUsd(rawValue);
2369
+ }
2370
+ if (typeFlag !== "auto" && typeFlag !== "string") {
2371
+ throw new Error(
2372
+ `${parts.join(".")} is stored as a decimal string; use --type string or omit --type.`
2373
+ );
2374
+ }
2375
+ const value = rawValue.trim();
2376
+ const isUnboundedMax = parts[2] === "max_price" && value === "";
2377
+ if (!isUnboundedMax && !/^\d+$/.test(value)) {
2378
+ throw new Error(
2379
+ `${parts.join(".")} must be a non-negative integer string in token base units; got ${JSON.stringify(rawValue)}.`
2380
+ );
2381
+ }
2382
+ return value;
2383
+ }
2276
2384
  function setDottedKey(text2, parts, value) {
2277
2385
  const key = parts[parts.length - 1];
2278
2386
  const section = parts.slice(0, -1).join(".");
@@ -2347,7 +2455,7 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
2347
2455
  }
2348
2456
  let value;
2349
2457
  try {
2350
- value = coerceValue(rawValue, typeFlag);
2458
+ value = coerceKnownValue(parts, rawValue, typeFlag);
2351
2459
  } catch (err) {
2352
2460
  printErr(`error: ${err instanceof Error ? err.message : String(err)}`);
2353
2461
  return 2;
@@ -2357,8 +2465,64 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
2357
2465
  printErr(`error: ${validationError}`);
2358
2466
  return 2;
2359
2467
  }
2360
- fs9.writeFileSync(tomlPath, setDottedKey(text2, parts, value), "utf-8");
2468
+ const updatedText = setDottedKey(text2, parts, value);
2469
+ fs9.writeFileSync(tomlPath, updatedText, "utf-8");
2361
2470
  printOut(`set ${key} = ${JSON.stringify(plainValue(value))}`);
2471
+ if (parts.length === 3 && parts[0] === "payments" && parts[1] === "erc8183" && parts[2] === "price") {
2472
+ const updated = parse4(updatedText);
2473
+ const payments = updated.payments !== null && typeof updated.payments === "object" && !Array.isArray(updated.payments) ? updated.payments : {};
2474
+ const erc = payments.erc8183 !== null && typeof payments.erc8183 === "object" && !Array.isArray(payments.erc8183) ? payments.erc8183 : {};
2475
+ const pricing = erc8183PricingState(erc);
2476
+ if (pricing.kind === "invalid" || pricing.kind === "unset") {
2477
+ printErr(
2478
+ "warning: price was saved, but existing min_price/max_price is invalid; run `bag doctor`."
2479
+ );
2480
+ }
2481
+ if (pricing.kind === "free") {
2482
+ printOut("pricing: FREE \u2014 buyers fund 0 token units; zero token escrow.");
2483
+ const contracts = erc8183ContractOverrideState();
2484
+ if (contracts.mode === "custom") {
2485
+ printOut(
2486
+ "ERC-8183 contracts: custom/QA contract stack selected with all three address overrides."
2487
+ );
2488
+ } else if (contracts.mode === "partial") {
2489
+ printErr(
2490
+ `warning: ERC-8183 contract override is incomplete; set ${contracts.missing.join(", ")} before running a job.`
2491
+ );
2492
+ } else if (contracts.mode === "invalid") {
2493
+ printErr(
2494
+ `warning: invalid ERC-8183 address override(s): ${contracts.invalid.join(", ")}.`
2495
+ );
2496
+ } else {
2497
+ printErr(
2498
+ "warning: FREE requires a zero-price-compatible contract stack; set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together before deploy."
2499
+ );
2500
+ }
2501
+ } else if (pricing.kind === "clamped_to_zero") {
2502
+ printErr(
2503
+ "warning: PAID list price is clamped to 0; this is not an explicit FREE choice. Fix max_price or set price to 0."
2504
+ );
2505
+ } else if (pricing.kind === "paid") {
2506
+ printOut(
2507
+ `pricing: PAID \u2014 effective price ${pricing.effectivePrice} token base units after clamping.`
2508
+ );
2509
+ }
2510
+ }
2511
+ if (isB402PriceKey(parts)) {
2512
+ const pricing = x402SellerPricingState({ price_usd: value });
2513
+ if (pricing.kind === "free") {
2514
+ printOut(
2515
+ "x402 pricing: FREE \u2014 B402 verify/settle is bypassed; no payment or settlement audit."
2516
+ );
2517
+ printErr(
2518
+ "warning: /x402 is now an anonymous FREE endpoint. Confirm that unrestricted public access is intended."
2519
+ );
2520
+ } else if (pricing.kind === "paid") {
2521
+ printOut(
2522
+ `x402 pricing: PAID \u2014 $${pricing.priceUsd} per request through B402.`
2523
+ );
2524
+ }
2525
+ }
2362
2526
  if (parts[0] === "network") {
2363
2527
  if (await devPortInUse(8080)) {
2364
2528
  printErr(
@@ -6983,6 +7147,12 @@ function registerInit(program) {
6983
7147
  "--rails <rails>",
6984
7148
  "Commerce rail(s) to scaffold: 8183 (default), b402, or both."
6985
7149
  ).choices(["8183", "b402", "both"])
7150
+ ).option(
7151
+ "--erc8183-price <base-units>",
7152
+ "ERC-8183 list price as a non-negative integer string in token base units. Use 0 for FREE. Default: 100000000000000000 (0.1 U)."
7153
+ ).option(
7154
+ "--b402-price <usd>",
7155
+ "B402/x402 per-request price as a non-negative decimal USD string. Use 0 for FREE passthrough (no B402 verify/settle). Default: 0.01."
6986
7156
  ).addOption(
6987
7157
  new Option2(
6988
7158
  "--llm-provider <provider>",
@@ -7099,6 +7269,45 @@ async function cmdInit(nameArg, opts) {
7099
7269
  opts.llmProviderSource,
7100
7270
  isTty
7101
7271
  );
7272
+ let rails = await resolveRails(opts.rails, isTty);
7273
+ if (hasX402Face(faces) && rails === "8183") {
7274
+ rails = "both";
7275
+ } else if (hasX402Face(faces) && rails !== "both") {
7276
+ rails = "b402";
7277
+ }
7278
+ if ((rails === "b402" || rails === "both") && !hasX402Face(faces)) {
7279
+ faces = normalizeProtocolFaces([...faces, "X402"]);
7280
+ }
7281
+ if (opts.erc8183Price !== void 0 && rails === "b402") {
7282
+ printErr(
7283
+ "error: --erc8183-price requires the 8183 rail; use --rails 8183 or --rails both."
7284
+ );
7285
+ return 2;
7286
+ }
7287
+ if (opts.b402Price !== void 0 && rails === "8183") {
7288
+ printErr(
7289
+ "error: --b402-price requires the b402 rail; use --rails b402 or --rails both."
7290
+ );
7291
+ return 2;
7292
+ }
7293
+ let erc8183Price = DEFAULT_ERC8183_PRICE;
7294
+ if (rails === "8183" || rails === "both") {
7295
+ try {
7296
+ erc8183Price = await resolveErc8183Price(opts.erc8183Price, isTty);
7297
+ } catch (exc) {
7298
+ printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
7299
+ return 2;
7300
+ }
7301
+ }
7302
+ let b402Price = DEFAULT_B402_PRICE_USD;
7303
+ if (rails === "b402" || rails === "both") {
7304
+ try {
7305
+ b402Price = await resolveB402Price(opts.b402Price, isTty);
7306
+ } catch (exc) {
7307
+ printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
7308
+ return 2;
7309
+ }
7310
+ }
7102
7311
  const enclosing = enclosingProjectRoot(path21.dirname(target));
7103
7312
  if (enclosing !== null) {
7104
7313
  printErr(
@@ -7196,16 +7405,7 @@ async function cmdInit(nameArg, opts) {
7196
7405
  }
7197
7406
  const budgetFlag = opts.enableAutoTopup ? "enable" : opts.autoTopup === false ? "disable" : null;
7198
7407
  const budgetChoice = walletKind2 === "altana" && llmProvider !== "pieverse-llm" && budgetFlag === null ? null : await resolveBudgetChoice(budgetFlag);
7199
- let rails = await resolveRails(opts.rails, isTty);
7200
- if (hasX402Face(faces) && rails === "8183") {
7201
- rails = "both";
7202
- } else if (hasX402Face(faces) && rails !== "both") {
7203
- rails = "b402";
7204
- }
7205
- if ((rails === "b402" || rails === "both") && !hasX402Face(faces)) {
7206
- faces = normalizeProtocolFaces([...faces, "X402"]);
7207
- }
7208
- if ((rails === "b402" || rails === "both") && !["evm-local", "twak"].includes(walletKind2)) {
7408
+ if ((rails === "b402" || rails === "both") && x402SellerPricingState({ price_usd: b402Price }).kind === "paid" && !["evm-local", "twak"].includes(walletKind2)) {
7209
7409
  printErr(
7210
7410
  `error: the b402 seller rail supports wallet.kind evm-local or twak only; '${walletKind2}' cannot act as the b402 payout wallet. Re-run with --rails 8183 or a supported --wallet-kind.`
7211
7411
  );
@@ -7220,6 +7420,8 @@ async function cmdInit(nameArg, opts) {
7220
7420
  runtime,
7221
7421
  faces,
7222
7422
  rails,
7423
+ erc8183Price,
7424
+ b402Price,
7223
7425
  budgetChoice,
7224
7426
  ides,
7225
7427
  storageProvider: opts.storageProvider,
@@ -7318,8 +7520,9 @@ async function cmdInit(nameArg, opts) {
7318
7520
  "info: X402-only publishes only the /x402 gateway face; the native AgentCore A2A protocol declaration is suppressed from discovery."
7319
7521
  );
7320
7522
  } else if (faces.length === 1 && hasX402Face(faces) && destination === "self") {
7523
+ const free = x402SellerPricingState({ price_usd: b402Price }).kind === "free";
7321
7524
  printErr(
7322
- "warning: X402-only uses an A2A-native AgentCore process internally, but exposes no public A2A face; the seller stays dormant until the B402 credentials are filled, and a self-hosted AgentCore target needs your own HTTP front to reach /x402."
7525
+ free ? "warning: X402-only FREE mode uses an A2A-native AgentCore process internally, but exposes no public A2A face; a self-hosted AgentCore target still needs your own HTTP front to reach the anonymous /x402 passthrough." : "warning: X402-only uses an A2A-native AgentCore process internally, but exposes no public A2A face; the seller stays dormant until the B402 credentials are filled, and a self-hosted AgentCore target needs your own HTTP front to reach /x402."
7323
7526
  );
7324
7527
  }
7325
7528
  printConfigSummary(agentDir);
@@ -7438,7 +7641,13 @@ function scaffold(target, name, o) {
7438
7641
  fs22.mkdirSync(path21.dirname(envLocal), { recursive: true });
7439
7642
  fs22.writeFileSync(
7440
7643
  envLocal,
7441
- renderAgentEnvLocal(o.provider, o.storageProvider, o.walletKind, o.rails)
7644
+ renderAgentEnvLocal(
7645
+ o.provider,
7646
+ o.storageProvider,
7647
+ o.walletKind,
7648
+ o.rails,
7649
+ o.b402Price
7650
+ )
7442
7651
  );
7443
7652
  fs22.writeFileSync(path21.join(agentRoot2, ".gitignore"), renderAgentGitignore());
7444
7653
  fs22.writeFileSync(
@@ -7755,14 +7964,14 @@ expected_recipient = ""
7755
7964
  allowed_hosts = ["llm.pieverse.io"]
7756
7965
  `;
7757
7966
  }
7758
- function renderX402SellerSection() {
7967
+ function renderX402SellerSection(priceUsd) {
7759
7968
  return `
7760
7969
  [payments.x402_seller]
7761
7970
  # Public seller rail mounted at /x402. Credentials stay in
7762
7971
  # <workspace>/.studio/.env.local and are issued per B402 environment.
7763
7972
  enabled = true
7764
- # Decimal USD string; "0" serves the work for free without a facilitator.
7765
- price_usd = "0.01"
7973
+ # Decimal USD string; "0" is explicit FREE passthrough and bypasses B402.
7974
+ price_usd = "${priceUsd}"
7766
7975
  # v1 supports United Stables (U) through EIP-3009 only.
7767
7976
  assets = ["U"]
7768
7977
  # Empty means the agent wallet receives payment.
@@ -7817,7 +8026,7 @@ function renderAgentStudioToml(name, o) {
7817
8026
  const model = o.model ?? PROVIDER_DEFAULT_MODEL[o.provider] ?? "";
7818
8027
  const pieverseSection = o.provider === "pieverse-llm" ? renderPieverseSections() : "";
7819
8028
  const x402Section = o.provider === "pieverse-llm" ? renderX402Section() : "";
7820
- const x402SellerSection = o.rails === "b402" || o.rails === "both" || hasX402Face(o.faces) ? renderX402SellerSection() : "";
8029
+ const x402SellerSection = o.rails === "b402" || o.rails === "both" || hasX402Face(o.faces) ? renderX402SellerSection(o.b402Price) : "";
7821
8030
  const budgetSection = renderBudgetSection(o.budgetChoice);
7822
8031
  const storageSection = renderAgentStorageSection(o.storageProvider);
7823
8032
  const azureSection = o.runtime === "azure-foundry" ? renderAzureSection(name, o.azureAccount, o.azureProject) : "";
@@ -7828,9 +8037,10 @@ function renderAgentStudioToml(name, o) {
7828
8037
  [payments.erc8183]
7829
8038
  # Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
7830
8039
  # freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
7831
- # LLM never prices.
8040
+ # LLM never prices. price=0 is FREE and needs a zero-price-compatible
8041
+ # ERC-8183 stack selected with all three ERC8183_*_ADDRESS overrides.
7832
8042
  currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default
7833
- price = "100000000000000000" # wei \u2014 your list price (0.1 U); what every quote charges
8043
+ price = "${o.erc8183Price}" # token base units; 0 = FREE (zero token escrow)
7834
8044
  min_price = "0" # wei \u2014 clamp floor
7835
8045
  max_price = "" # wei \u2014 clamp ceiling; SET before going live
7836
8046
  quote_ttl_seconds = 900 # seconds a signed quote stays valid; SDK caps at 900 (~15min work window)
@@ -7868,7 +8078,7 @@ ${pieverseSection}${erc8183Section}
7868
8078
  ${storageSection}
7869
8079
  ${x402Section}${x402SellerSection}${budgetSection}${azureSection}`;
7870
8080
  }
7871
- function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails) {
8081
+ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402Price) {
7872
8082
  const keyEnv = PROVIDER_KEY_ENV[provider];
7873
8083
  const lines = [
7874
8084
  "# \u26A0\uFE0F SECRETS \u2014 do NOT paste this file into issue trackers, logs, or AI chats.",
@@ -7931,10 +8141,11 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails) {
7931
8141
  );
7932
8142
  }
7933
8143
  if (rails === "b402" || rails === "both") {
8144
+ const free = x402SellerPricingState({ price_usd: b402Price }).kind === "free";
7934
8145
  lines.push(
7935
8146
  "",
7936
8147
  "# B402 merchant credentials (issued per environment after manual approval).",
7937
- "# All four absent = x402 rail dormant; partial = boot error naming the missing vars.",
8148
+ free ? "# FREE price 0 does not use these credentials; leave them empty until switching to PAID." : "# PAID requires all four; all absent = dormant, partial = boot error.",
7938
8149
  "B402_BASE_URL=",
7939
8150
  "B402_CLIENT_ID=",
7940
8151
  "B402_ACCESS_TOKEN=",
@@ -7942,6 +8153,17 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails) {
7942
8153
  "# (alternative one-line PKCS#8 DER: B402_PRIVATE_KEY_B64=)"
7943
8154
  );
7944
8155
  }
8156
+ if (rails === "8183" || rails === "both") {
8157
+ lines.push(
8158
+ "",
8159
+ "# Optional ERC-8183 contract-stack override (QA/custom). Set all three",
8160
+ "# together; partial overrides can mix incompatible deployments.",
8161
+ "# Required for price=0 while canonical contracts reject zero funding.",
8162
+ "# ERC8183_COMMERCE_ADDRESS=",
8163
+ "# ERC8183_ROUTER_ADDRESS=",
8164
+ "# ERC8183_POLICY_ADDRESS="
8165
+ );
8166
+ }
7945
8167
  return `${lines.join("\n")}
7946
8168
  `;
7947
8169
  }
@@ -8183,6 +8405,69 @@ async function resolveRails(flagValue, isTty) {
8183
8405
  }
8184
8406
  return "8183";
8185
8407
  }
8408
+ var DEFAULT_ERC8183_PRICE = "100000000000000000";
8409
+ async function resolveErc8183Price(flagValue, isTty) {
8410
+ if (flagValue !== void 0) {
8411
+ const value = flagValue.trim();
8412
+ if (!/^\d+$/.test(value)) {
8413
+ throw new Error(
8414
+ `--erc8183-price must be a non-negative integer string in token base units; got ${JSON.stringify(flagValue)}.`
8415
+ );
8416
+ }
8417
+ return value;
8418
+ }
8419
+ if (!isTty) {
8420
+ return DEFAULT_ERC8183_PRICE;
8421
+ }
8422
+ const choice = (await promptUser(
8423
+ "ERC-8183 pricing [paid/free/custom, Enter = paid (0.1 U)]: "
8424
+ )).trim().toLowerCase();
8425
+ if (choice === "free") {
8426
+ return "0";
8427
+ }
8428
+ if (choice === "custom") {
8429
+ const custom = (await promptUser(
8430
+ "ERC-8183 list price in token base units (non-negative integer): "
8431
+ )).trim();
8432
+ if (!/^\d+$/.test(custom)) {
8433
+ throw new Error(
8434
+ `custom ERC-8183 price must be a non-negative integer string; got ${JSON.stringify(custom)}.`
8435
+ );
8436
+ }
8437
+ return custom;
8438
+ }
8439
+ if (choice !== "" && choice !== "paid") {
8440
+ printErr(`hint: unknown ERC-8183 pricing choice '${choice}' \u2014 using paid.`);
8441
+ }
8442
+ return DEFAULT_ERC8183_PRICE;
8443
+ }
8444
+ async function resolveB402Price(flagValue, isTty) {
8445
+ if (flagValue !== void 0) {
8446
+ return normalizeB402PriceUsd(flagValue);
8447
+ }
8448
+ if (!isTty) {
8449
+ return DEFAULT_B402_PRICE_USD;
8450
+ }
8451
+ const choice = (await promptUser(
8452
+ "B402/x402 pricing [paid/free/custom, Enter = paid ($0.01)]: "
8453
+ )).trim().toLowerCase();
8454
+ if (choice === "free") {
8455
+ return "0";
8456
+ }
8457
+ if (choice === "custom") {
8458
+ return normalizeB402PriceUsd(
8459
+ await promptUser(
8460
+ "B402/x402 per-request price in USD (non-negative decimal): "
8461
+ )
8462
+ );
8463
+ }
8464
+ if (choice !== "" && choice !== "paid") {
8465
+ printErr(
8466
+ `hint: unknown B402/x402 pricing choice '${choice}' \u2014 using paid.`
8467
+ );
8468
+ }
8469
+ return DEFAULT_B402_PRICE_USD;
8470
+ }
8186
8471
  async function promptPassword() {
8187
8472
  printOut("");
8188
8473
  printOut("Onboarding (TTY) \u2014 set a wallet password to finish in one go.");
@@ -8940,10 +9225,10 @@ function printNextSteps(name, provider, o = {}) {
8940
9225
  nextStep = 4;
8941
9226
  }
8942
9227
  printOut(
8943
- ` # ${nextStep}. set ERC-8183 pricing in studio.toml [payments.erc8183]`
9228
+ ` # ${nextStep}. review ERC-8183 pricing in studio.toml [payments.erc8183]`
8944
9229
  );
8945
9230
  printOut(
8946
- " # (currency = $U token; price = list price in wei; min/max_price clamp before sign)"
9231
+ " # (price was selected at init; `bag config set payments.erc8183.price 0` explicitly switches to FREE)"
8947
9232
  );
8948
9233
  nextStep += 1;
8949
9234
  printOut(` # ${nextStep}. verify + run the agent locally`);
@@ -9011,13 +9296,26 @@ function printConfigSummary(agentRoot2) {
9011
9296
  const shown = (v, unset = "<unset \u2014 fill before going live>") => v === void 0 || v === null || v === "" ? unset : String(v);
9012
9297
  const table = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
9013
9298
  const faces = stackFaces(table(agent.stack), table(agent.payments));
9299
+ const payments = table(agent.payments);
9300
+ const erc8183 = table(payments.erc8183);
9301
+ const x402Seller = table(payments.x402_seller);
9302
+ const hasErc8183 = Object.keys(erc8183).length > 0;
9303
+ const hasX402Seller = x402Seller.enabled === true;
9304
+ const x402Pricing = x402SellerPricingState(x402Seller);
9305
+ const isFreeX402 = hasX402Seller && x402Pricing.kind === "free";
9306
+ const onlyX402 = hasX402Seller && !hasErc8183;
9014
9307
  const network = shown(g(agent, "network", "default"), "?");
9015
9308
  const provider = shown(g(agent, "llm", "provider"), "?");
9016
9309
  const model = shown(g(agent, "llm", "model"), "?");
9017
9310
  const modelTag = isFreeModel(String(model)) ? "Free, $0/token" : "paid \u2014 fund + topup to use";
9018
9311
  const currency = g(agent, "payments", "erc8183", "currency");
9019
- const price = shown(g(agent, "payments", "erc8183", "price"), "0");
9312
+ const priceValue = g(agent, "payments", "erc8183", "price");
9313
+ const price = shown(priceValue);
9314
+ const minPrice = shown(g(agent, "payments", "erc8183", "min_price"), "0");
9020
9315
  const maxPrice = g(agent, "payments", "erc8183", "max_price");
9316
+ const pricingState = erc8183PricingState(erc8183);
9317
+ const isFreePrice = pricingState.kind === "free";
9318
+ const contractOverrides = erc8183ContractOverrideState();
9021
9319
  const storageKind2 = shown(g(agent, "storage", "kind"), "?");
9022
9320
  const storageDesc = storageKind2 === "ipfs" ? "IPFS (durable, public)" : "local disk (offline dev only)";
9023
9321
  const walletKind2 = shown(g(agent, "wallet", "kind"), "evm-local");
@@ -9030,16 +9328,41 @@ function printConfigSummary(agentRoot2) {
9030
9328
  printOut("Your agent's configuration (studio.toml, in plain language)");
9031
9329
  printOut(bar);
9032
9330
  printOut("");
9033
- printOut("Per task \u2014 how this seller earns $U:");
9034
- printOut(
9035
- " buyer negotiates \u2192 the Agent (sole signer) QUOTES a signed price \u2192"
9036
- );
9037
- printOut(
9038
- " on payment the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
9039
- );
9040
9331
  printOut(
9041
- " after the dispute window, SETTLE releases the $U to your wallet."
9332
+ onlyX402 && isFreeX402 ? "Per request \u2014 how this FREE x402 seller delivers work:" : onlyX402 ? "Per request \u2014 how this x402 seller earns $U:" : isFreePrice ? "Per task \u2014 how this FREE seller delivers work:" : "Per task \u2014 how this seller earns $U:"
9042
9333
  );
9334
+ if (onlyX402 && isFreeX402) {
9335
+ printOut(
9336
+ " anonymous request reaches /x402 \u2192 B402 verify/settle is bypassed \u2192"
9337
+ );
9338
+ printOut(
9339
+ " the Agent runs the work and returns the result with no payment."
9340
+ );
9341
+ } else if (onlyX402) {
9342
+ printOut(
9343
+ " anonymous request receives a 402 challenge \u2192 B402 verifies and settles $U \u2192"
9344
+ );
9345
+ printOut(" the Agent runs the work and returns the paid result.");
9346
+ } else {
9347
+ printOut(
9348
+ " buyer negotiates \u2192 the Agent (sole signer) QUOTES a signed price \u2192"
9349
+ );
9350
+ if (isFreePrice) {
9351
+ printOut(
9352
+ " buyer funds 0 token units \u2192 the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
9353
+ );
9354
+ printOut(
9355
+ " after the dispute window, SETTLE completes the job with no token payout."
9356
+ );
9357
+ } else {
9358
+ printOut(
9359
+ " on payment the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
9360
+ );
9361
+ printOut(
9362
+ " after the dispute window, SETTLE releases the $U to your wallet."
9363
+ );
9364
+ }
9365
+ }
9043
9366
  printOut("");
9044
9367
  printOut("Agent (the value + the ONLY key-holder/signer)");
9045
9368
  printOut(` project : ${shown(g(agent, "project", "name"), "?")}`);
@@ -9086,19 +9409,35 @@ function printConfigSummary(agentRoot2) {
9086
9409
  const ar = autoRenew ? "on \u2014 tops the API key up from your Pieverse balance (no wallet spend)" : "off";
9087
9410
  printOut(` auto-alloc: ${ar}`);
9088
9411
  }
9089
- printOut(
9090
- ` pricing : list price ${weiToU(String(price))} per job; clamp ceiling ${shown(maxPrice)}`
9091
- );
9092
- printOut(
9093
- ` quote frozen ${shown(
9094
- g(agent, "payments", "erc8183", "quote_ttl_seconds"),
9095
- "?"
9096
- )}s; job timeout ${shown(
9097
- g(agent, "payments", "erc8183", "default_estimated_completion_seconds"),
9098
- "?"
9099
- )}s`
9100
- );
9101
- printOut(` currency ($U token): ${shown(currency)}`);
9412
+ if (hasErc8183) {
9413
+ if (isFreePrice) {
9414
+ printOut(
9415
+ ` pricing : FREE \u2014 ${weiToU(String(price))} per job; zero token escrow`
9416
+ );
9417
+ printOut(
9418
+ contractOverrides.mode === "custom" ? " contracts: custom/QA stack selected (all three address overrides)" : " contracts: configure all three ERC8183_*_ADDRESS overrides before deploy"
9419
+ );
9420
+ } else {
9421
+ printOut(
9422
+ ` pricing : list price ${weiToU(String(price))} per job; clamp ceiling ${shown(maxPrice)}`
9423
+ );
9424
+ }
9425
+ printOut(
9426
+ ` quote frozen ${shown(
9427
+ g(agent, "payments", "erc8183", "quote_ttl_seconds"),
9428
+ "?"
9429
+ )}s; job timeout ${shown(
9430
+ g(agent, "payments", "erc8183", "default_estimated_completion_seconds"),
9431
+ "?"
9432
+ )}s`
9433
+ );
9434
+ printOut(` currency ($U token): ${shown(currency)}`);
9435
+ }
9436
+ if (hasX402Seller) {
9437
+ printOut(
9438
+ isFreeX402 ? " x402 : FREE \u2014 anonymous passthrough; B402 verify/settle is bypassed" : ` x402 : PAID \u2014 $${shown(x402Seller.price_usd, DEFAULT_B402_PRICE_USD)} per request through B402`
9439
+ );
9440
+ }
9102
9441
  printOut(
9103
9442
  ` storage : ${storageDesc} (options: local | ipfs \u2014 ipfs required to deploy)`
9104
9443
  );
@@ -9108,12 +9447,22 @@ function printConfigSummary(agentRoot2) {
9108
9447
  }
9109
9448
  printOut("");
9110
9449
  const todo = [];
9111
- if (!currency) {
9450
+ if (hasErc8183 && !currency) {
9112
9451
  todo.push("[payments.erc8183].currency \u2014 the $U token address");
9113
9452
  }
9114
- if (!maxPrice) {
9453
+ if (hasErc8183 && !maxPrice && !isFreePrice) {
9115
9454
  todo.push("[payments.erc8183].max_price \u2014 the price clamp ceiling");
9116
9455
  }
9456
+ if (hasErc8183 && isFreePrice && contractOverrides.mode !== "custom") {
9457
+ todo.push(
9458
+ "ERC8183_COMMERCE_ADDRESS / ROUTER_ADDRESS / POLICY_ADDRESS \u2014 select one zero-price-compatible QA/custom contract stack"
9459
+ );
9460
+ }
9461
+ if (hasX402Seller && !isFreeX402) {
9462
+ todo.push(
9463
+ "B402_BASE_URL / CLIENT_ID / ACCESS_TOKEN / private key \u2014 required for PAID /x402"
9464
+ );
9465
+ }
9117
9466
  if (storageKind2 === "local") {
9118
9467
  todo.push(
9119
9468
  "[storage].kind \u2014 switch to 'ipfs' + set STORAGE_API_URL/STORAGE_API_KEY before deploy (local disk is NOT deployable)"
@@ -9392,8 +9741,13 @@ function runtimeEnvKeys(agentDir) {
9392
9741
  if (resolvable("RPC_URL")) {
9393
9742
  keys.push("RPC_URL");
9394
9743
  }
9744
+ if (commerceRails(cfg).erc8183) {
9745
+ for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
9746
+ if (resolvable(key)) keys.push(key);
9747
+ }
9748
+ }
9395
9749
  const runtime = String(tableOf7(cfg, "stack").runtime ?? "agentcore");
9396
- if (runtime === "agentcore" && commerceRails(cfg).x402) {
9750
+ if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
9397
9751
  for (const key of B402_RUNTIME_KEYS) {
9398
9752
  if (resolvable(key)) keys.push(key);
9399
9753
  }
@@ -9777,8 +10131,11 @@ Switch to Node \u226522 so it wins on PATH, then reopen the shell and retry. Ver
9777
10131
  );
9778
10132
  }
9779
10133
  if (hasX402Face(faces)) {
10134
+ const free = x402SellerIsFree(
10135
+ loadStudioToml10(path22.join(agentDir, "studio.toml"))
10136
+ );
9780
10137
  printOut(
9781
- `bag dev: x402 seller at http://localhost:${agentPort}/x402 (active when B402 credentials are configured)`
10138
+ free ? `bag dev: x402 seller at http://localhost:${agentPort}/x402 (FREE \u2014 anonymous passthrough; B402 bypassed)` : `bag dev: x402 seller at http://localhost:${agentPort}/x402 (PAID \u2014 active when B402 credentials are configured)`
9782
10139
  );
9783
10140
  }
9784
10141
  await launch();
@@ -9885,8 +10242,13 @@ function runtimeEnvKeys2(agentRoot2) {
9885
10242
  if (resolvable("RPC_URL")) {
9886
10243
  keys.push("RPC_URL");
9887
10244
  }
10245
+ if (commerceRails(cfg).erc8183) {
10246
+ for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
10247
+ if (resolvable(key)) keys.push(key);
10248
+ }
10249
+ }
9888
10250
  const runtime = String(tableOf2(cfg, "stack").runtime ?? "agentcore");
9889
- if (runtime === "agentcore" && commerceRails(cfg).x402) {
10251
+ if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
9890
10252
  for (const key of B402_RUNTIME_KEYS) {
9891
10253
  if (resolvable(key)) keys.push(key);
9892
10254
  }
@@ -10226,51 +10588,76 @@ async function checkPieverseKeyHash(root, _target) {
10226
10588
  }
10227
10589
  ];
10228
10590
  }
10229
- var MIN_PRICE_WEI = 1000000000n;
10230
- var MAX_UINT256 = 2n ** 256n - 1n;
10231
10591
  function erc8183RailChecks(cfg) {
10232
10592
  const c2 = tableOf2(tableOf2(cfg, "payments"), "erc8183");
10233
- const raw = (key, dflt) => {
10234
- const s = String(c2[key] ?? "").trim();
10235
- if (!s) {
10236
- return dflt;
10237
- }
10238
- try {
10239
- return BigInt(s);
10240
- } catch {
10241
- return dflt;
10242
- }
10243
- };
10244
10593
  const out = [];
10245
- const minPrice = raw("min_price", 0n);
10246
- const maxPrice = raw("max_price", MAX_UINT256);
10247
- const listPrice = raw("price", 0n);
10248
- const clampedToMax = listPrice < maxPrice ? listPrice : maxPrice;
10249
- const effective = minPrice > clampedToMax ? minPrice : clampedToMax;
10250
- if (effective < MIN_PRICE_WEI) {
10251
- const priceUnset = !String(c2.price ?? "").trim();
10252
- const clamped = listPrice >= MIN_PRICE_WEI && MIN_PRICE_WEI > effective;
10253
- let cause;
10254
- if (priceUnset) {
10255
- cause = "[payments.erc8183].price is unset, so every quote defaults to 0";
10256
- } else if (clamped) {
10257
- cause = `the list price (${listPrice} wei) is clamped to ${effective} wei by [payments.erc8183].max_price`;
10258
- } else {
10259
- cause = `the effective quote price is ${effective} wei`;
10260
- }
10594
+ const contracts = erc8183ContractOverrideState();
10595
+ if (contracts.mode === "partial") {
10261
10596
  out.push({
10262
10597
  level: Level.CRITICAL,
10263
- name: "commerce_price_below_min",
10264
- message: `${cause}; the agent would quote for free / below the contract minimum of ${MIN_PRICE_WEI} wei (1 gwei). v1 rejects price-0 / sub-gwei tasks, so no job can ever fund. Set [payments.erc8183].price (and check max_price) in studio.toml.`,
10265
- fixCmd: "bag config set payments.erc8183.price 1000000000",
10598
+ name: "commerce_contract_override_incomplete",
10599
+ message: `The ERC-8183 contract override is incomplete. Set ${contracts.missing.join(", ")} so commerce, router, and policy come from one compatible contract stack.`,
10266
10600
  details: {
10267
- effective_wei: String(effective),
10268
- list_price_wei: String(listPrice),
10269
- min_price_wei: String(minPrice),
10270
- max_price_wei: String(maxPrice),
10271
- min_required_wei: String(MIN_PRICE_WEI)
10601
+ present: contracts.present,
10602
+ missing: contracts.missing
10272
10603
  }
10273
10604
  });
10605
+ } else if (contracts.mode === "invalid") {
10606
+ out.push({
10607
+ level: Level.CRITICAL,
10608
+ name: "commerce_contract_override_invalid",
10609
+ message: `The following ERC-8183 contract overrides are not valid EVM addresses: ${contracts.invalid.join(", ")}.`,
10610
+ details: { invalid: contracts.invalid }
10611
+ });
10612
+ }
10613
+ const pricing = erc8183PricingState(c2);
10614
+ if (pricing.kind === "unset") {
10615
+ out.push({
10616
+ level: Level.CRITICAL,
10617
+ name: "commerce_price_unset",
10618
+ message: "[payments.erc8183].price is unset. Set it explicitly to 0 for a free job or to a positive integer amount in token base units.",
10619
+ fixCmd: "bag config set payments.erc8183.price 100000000000000000",
10620
+ details: {}
10621
+ });
10622
+ } else if (pricing.kind === "invalid") {
10623
+ out.push({
10624
+ level: Level.CRITICAL,
10625
+ name: "commerce_price_invalid",
10626
+ message: `[payments.erc8183].${pricing.field} must be a non-negative uint256 decimal string; got ${JSON.stringify(pricing.value)}.`,
10627
+ fixCmd: "bag config set payments.erc8183.price 100000000000000000",
10628
+ details: {
10629
+ configured_field: pricing.field,
10630
+ configured_value: pricing.value
10631
+ }
10632
+ });
10633
+ } else if (pricing.kind === "clamped_to_zero") {
10634
+ out.push({
10635
+ level: Level.CRITICAL,
10636
+ name: "commerce_price_clamped_to_zero",
10637
+ message: `The list price (${pricing.listPrice} token base units) is clamped to 0 by [payments.erc8183].max_price. Set price explicitly to 0 to opt into free jobs, or fix the clamp.`,
10638
+ details: {
10639
+ effective_wei: "0",
10640
+ list_price_wei: String(pricing.listPrice),
10641
+ min_price_wei: String(pricing.minPrice),
10642
+ max_price_wei: String(pricing.maxPrice)
10643
+ }
10644
+ });
10645
+ } else if (pricing.kind === "free") {
10646
+ if (contracts.mode === "canonical") {
10647
+ out.push({
10648
+ level: Level.CRITICAL,
10649
+ name: "commerce_zero_price_contract_unsupported",
10650
+ message: "ERC-8183 pricing is FREE (zero token escrow), but the canonical contract stack rejects zero funding. Set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together to a zero-price-compatible QA/custom stack.",
10651
+ details: { effective_wei: "0", contract_profile: "canonical" }
10652
+ });
10653
+ } else if (contracts.mode === "custom") {
10654
+ out.push({
10655
+ level: Level.INFO,
10656
+ name: "commerce_zero_price_enabled",
10657
+ message: "ERC-8183 pricing is FREE \u2014 buyers fund 0 token units with zero token escrow; a complete custom/QA contract stack is selected.",
10658
+ details: { effective_wei: "0", contract_profile: "custom" }
10659
+ });
10660
+ }
10274
10661
  }
10275
10662
  if (String(c2.currency ?? "").trim() === "") {
10276
10663
  out.push({
@@ -10306,15 +10693,24 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10306
10693
  const rails = commerceRails(cfg);
10307
10694
  const credentials = b402Credentials(agentRoot2);
10308
10695
  const out = [];
10696
+ const pricing = x402SellerPricingState(seller);
10697
+ const free = rails.x402 && pricing.kind === "free";
10698
+ const paid = rails.x402 && pricing.kind === "paid";
10309
10699
  if (sellerPresent) {
10310
- const price = seller.price_usd === void 0 ? "0.01" : typeof seller.price_usd === "string" ? seller.price_usd : "";
10311
- if (!/^(?:0|[1-9]\d*)(?:\.\d+)?$/.test(price)) {
10700
+ if (pricing.kind === "invalid") {
10312
10701
  out.push({
10313
10702
  level: Level.CRITICAL,
10314
10703
  name: "x402_price_invalid",
10315
10704
  message: '[payments.x402_seller].price_usd must be a non-negative decimal string; "0" is valid and enables the free passthrough.',
10316
10705
  details: {}
10317
10706
  });
10707
+ } else if (free) {
10708
+ out.push({
10709
+ level: Level.INFO,
10710
+ name: "x402_zero_price_enabled",
10711
+ message: "The x402 seller is explicitly FREE. /x402 is anonymous, and B402 verify/settle, token payment, and settlement audit are bypassed.",
10712
+ details: { price_usd: pricing.priceUsd, facilitator_bypassed: true }
10713
+ });
10318
10714
  }
10319
10715
  const assets = seller.assets ?? ["U"];
10320
10716
  if (!Array.isArray(assets) || assets.length !== 1 || assets[0] !== "U") {
@@ -10336,7 +10732,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10336
10732
  }
10337
10733
  }
10338
10734
  const walletKind2 = String(tableOf2(cfg, "wallet").kind ?? "evm-local");
10339
- if (rails.x402 && !["evm-local", "twak"].includes(walletKind2)) {
10735
+ if (paid && !["evm-local", "twak"].includes(walletKind2)) {
10340
10736
  out.push({
10341
10737
  level: Level.CRITICAL,
10342
10738
  name: "x402_wallet_kind_unsupported",
@@ -10344,7 +10740,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10344
10740
  details: { walletKind: walletKind2 }
10345
10741
  });
10346
10742
  }
10347
- if (credentials.any && !credentials.complete) {
10743
+ if (paid && credentials.any && !credentials.complete) {
10348
10744
  out.push({
10349
10745
  level: Level.CRITICAL,
10350
10746
  name: "x402_credentials_partial",
@@ -10352,7 +10748,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10352
10748
  details: { present: credentials.presentKeys }
10353
10749
  });
10354
10750
  }
10355
- if (credentials.bothPrivateKeyFormats) {
10751
+ if (paid && credentials.bothPrivateKeyFormats) {
10356
10752
  out.push({
10357
10753
  level: Level.WARNING,
10358
10754
  name: "x402_private_key_ambiguous",
@@ -10360,7 +10756,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10360
10756
  details: {}
10361
10757
  });
10362
10758
  }
10363
- if (rails.x402 && !credentials.any) {
10759
+ if (paid && !credentials.any) {
10364
10760
  out.push({
10365
10761
  level: rails.erc8183 ? Level.WARNING : Level.CRITICAL,
10366
10762
  name: "x402_credentials_missing",
@@ -10368,15 +10764,15 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10368
10764
  details: {}
10369
10765
  });
10370
10766
  }
10371
- if (!rails.x402 && credentials.any) {
10767
+ if ((!rails.x402 || free) && credentials.any) {
10372
10768
  out.push({
10373
10769
  level: Level.WARNING,
10374
10770
  name: "x402_credentials_unused",
10375
- message: "B402 credentials are configured but the x402 seller rail is absent or disabled; they will not be synchronized.",
10771
+ message: free ? "B402 credentials are configured but FREE x402 bypasses the facilitator; they will not be synchronized." : "B402 credentials are configured but the x402 seller rail is absent or disabled; they will not be synchronized.",
10376
10772
  details: { present: credentials.presentKeys }
10377
10773
  });
10378
10774
  }
10379
- if (credentials.baseUrl !== null) {
10775
+ if (paid && credentials.baseUrl !== null) {
10380
10776
  const network = String(
10381
10777
  tableOf2(cfg, "network").default ?? "bsc-mainnet"
10382
10778
  ).toLowerCase();
@@ -10393,12 +10789,12 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
10393
10789
  }
10394
10790
  const destination = String(tableOf2(cfg, "deploy").destination ?? "self");
10395
10791
  const stackRuntime2 = String(tableOf2(cfg, "stack").runtime ?? "agentcore");
10396
- if (rails.x402 && credentials.complete && destination !== "platform" && (target !== "agentcore" || stackRuntime2 !== "agentcore")) {
10792
+ if (rails.x402 && (free || credentials.complete) && destination !== "platform" && (target !== "agentcore" || stackRuntime2 !== "agentcore")) {
10397
10793
  const blocking = target !== "agentcore" ? target : stackRuntime2;
10398
10794
  out.push({
10399
10795
  level: Level.WARNING,
10400
10796
  name: "x402_forced_dormant_runtime",
10401
- message: `x402 seller credentials are complete, but the ${blocking} runtime has no x402 path; this target is forced dormant. Deploy to AgentCore (managed platform or self-hosted) to activate the rail.`,
10797
+ message: `x402 pricing is ready, but the ${blocking} runtime has no x402 path; this target is forced dormant. Deploy to AgentCore (managed platform or self-hosted) to activate the rail.`,
10402
10798
  details: { destination, target, stackRuntime: stackRuntime2 }
10403
10799
  });
10404
10800
  }
@@ -12243,6 +12639,7 @@ async function runPrepare(opts = {}) {
12243
12639
  checkLocalKeystoreExists,
12244
12640
  checkWalletPasswordEnvSet,
12245
12641
  checkLlmProviderKeySet,
12642
+ checkCommerceReady,
12246
12643
  ...azureFoundryChecks
12247
12644
  ],
12248
12645
  root,
@@ -13315,6 +13712,8 @@ function printAccessNextSteps(root, _invokeUrl) {
13315
13712
  const agentRoot2 = findSubProjectRoot15("agent", root) ?? root;
13316
13713
  const access = buyerAccess(agentRoot2);
13317
13714
  const x402Only = access.faces.length === 1 && hasX402Face(access.faces);
13715
+ const x402Free = x402SellerIsFree(loadAgentCfg(agentRoot2));
13716
+ const x402RouteLabel = x402Free ? "anonymous FREE route" : "anonymous paid route";
13318
13717
  emit("\nAccess your agent \u2014");
13319
13718
  if (access.agentId) {
13320
13719
  if (!x402Only) {
@@ -13332,7 +13731,7 @@ function printAccessNextSteps(root, _invokeUrl) {
13332
13731
  );
13333
13732
  }
13334
13733
  if (hasX402Face(access.faces)) {
13335
- emit(` x402 endpoint: ${access.x402Url} (anonymous paid route)`);
13734
+ emit(` x402 endpoint: ${access.x402Url} (${x402RouteLabel})`);
13336
13735
  }
13337
13736
  if (!x402Only) {
13338
13737
  emit(
@@ -13357,7 +13756,7 @@ function printAccessNextSteps(root, _invokeUrl) {
13357
13756
  );
13358
13757
  }
13359
13758
  if (hasX402Face(access.faces)) {
13360
- emit(` x402 endpoint: ${rtTmpl}/x402 (anonymous paid route)`);
13759
+ emit(` x402 endpoint: ${rtTmpl}/x402 (${x402RouteLabel})`);
13361
13760
  }
13362
13761
  if (x402Only) {
13363
13762
  emit(" protocol discovery is suppressed for this X402-only deployment.");
@@ -13388,7 +13787,9 @@ function printAgentClientPrompt(root, invokeUrl, status) {
13388
13787
  const tokenSteps = ` 1) run \`bag platform invoke-client new\` -> client_id + client_secret (secret shown ONCE).
13389
13788
  2) POST client_credentials to ${tokenUrl} with scope \`${scope}\` -> access_token.
13390
13789
  3) send \`Authorization: Bearer <token>\`.`;
13391
- const rails = commerceRails(loadAgentCfg(agentRoot2));
13790
+ const agentCfg = loadAgentCfg(agentRoot2);
13791
+ const rails = commerceRails(agentCfg);
13792
+ const x402Free = x402SellerIsFree(agentCfg);
13392
13793
  const x402Only = faces.length === 1 && hasX402Face(faces) || rails.x402 && !rails.erc8183;
13393
13794
  const x402Summary = x402DeploySummary(agentRoot2, "platform", access.x402Url);
13394
13795
  const endpoints = [];
@@ -13406,7 +13807,7 @@ function printAgentClientPrompt(root, invokeUrl, status) {
13406
13807
  }
13407
13808
  if (x402Only) {
13408
13809
  endpoints.push(
13409
- ` [X402] Anonymous paid endpoint: ${access.x402Url ?? `${rt}/x402`}
13810
+ ` [X402] Anonymous ${x402Free ? "FREE" : "paid"} endpoint: ${access.x402Url ?? `${rt}/x402`}
13410
13811
  Protocol discovery is suppressed.`
13411
13812
  );
13412
13813
  }
@@ -13454,7 +13855,7 @@ function printAgentClientPrompt(root, invokeUrl, status) {
13454
13855
  ] : [],
13455
13856
  "",
13456
13857
  "YOUR TASK",
13457
- x402Only ? "Follow the X402 SELLER activation or anonymous paid-request instructions above. Do not call the OAuth2 ERC-8183 endpoint." : "Write and run a client that (1) obtains a token, (2) calls negotiate with a sample task and prints the signed quote, (3) optionally funds a job on-chain (step 2 of the flow above) and calls notify_funded with the resulting job_id. Use the exact endpoint, headers, and message shapes above. If you don't have client_id/client_secret, ask me for them.",
13858
+ x402Only ? x402Free ? "Call the anonymous FREE /x402 endpoint directly. Do not create a payment or call the OAuth2 ERC-8183 endpoint." : "Follow the X402 SELLER activation or anonymous paid-request instructions above. Do not call the OAuth2 ERC-8183 endpoint." : "Write and run a client that (1) obtains a token, (2) calls negotiate with a sample task and prints the signed quote, (3) optionally funds a job on-chain (step 2 of the flow above) and calls notify_funded with the resulting job_id. Use the exact endpoint, headers, and message shapes above. If you don't have client_id/client_secret, ask me for them.",
13458
13859
  CLIENT_PROMPT_RULE
13459
13860
  ];
13460
13861
  const text2 = lines.join("\n");
@@ -14731,7 +15132,9 @@ async function printAgentClientPrompt2(root, destination, opts = {}) {
14731
15132
  const tokenSteps = ` 1) token endpoint ${tokenUrl} (scope \`${scope}\`); ${credentialStep}.
14732
15133
  2) POST client_credentials -> access_token.
14733
15134
  3) send \`Authorization: Bearer <token>\` AND header \`X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: <stable id >= 33 chars>\`.`;
14734
- const rails = commerceRails(loadAgentCfg2(agentRoot2) ?? {});
15135
+ const agentCfg = loadAgentCfg2(agentRoot2) ?? {};
15136
+ const rails = commerceRails(agentCfg);
15137
+ const x402Free = x402SellerIsFree(agentCfg);
14735
15138
  const x402Only = rails.x402 && !rails.erc8183;
14736
15139
  const x402Summary = x402DeploySummary(agentRoot2, destination, null);
14737
15140
  const faces = agentFacesOf(agentRoot2);
@@ -14784,7 +15187,7 @@ async function printAgentClientPrompt2(root, destination, opts = {}) {
14784
15187
  ] : [],
14785
15188
  "",
14786
15189
  "YOUR TASK",
14787
- x402Only ? "Follow the X402 SELLER activation or anonymous paid-request instructions above. Do not call the OAuth2 ERC-8183 endpoint." : "Write and run a client that (1) obtains a token, (2) calls negotiate with a sample task and prints the signed quote, (3) optionally funds a job on-chain (step 2 of the flow above) and calls notify_funded with the resulting job_id. Use the exact endpoint, headers, and message shapes above. If you don't have client_id/client_secret, ask me for them.",
15190
+ x402Only ? x402Free ? "Call the anonymous FREE /x402 endpoint directly. Do not create a payment or call the OAuth2 ERC-8183 endpoint." : "Follow the X402 SELLER activation or anonymous paid-request instructions above. Do not call the OAuth2 ERC-8183 endpoint." : "Write and run a client that (1) obtains a token, (2) calls negotiate with a sample task and prints the signed quote, (3) optionally funds a job on-chain (step 2 of the flow above) and calls notify_funded with the resulting job_id. Use the exact endpoint, headers, and message shapes above. If you don't have client_id/client_secret, ask me for them.",
14788
15191
  CLIENT_PROMPT_RULE2
14789
15192
  ];
14790
15193
  const text2 = lines.join("\n");
@@ -15426,7 +15829,9 @@ async function cmdDoctor(opts) {
15426
15829
  checks.push(...await checkLlm(data, projectRoot));
15427
15830
  checks.push(...await checkNetwork(data, opts.network));
15428
15831
  checks.push(...checkCurrency(data));
15832
+ checks.push(...checkErc8183Pricing(data));
15429
15833
  checks.push(...checkPriceBounds(data));
15834
+ checks.push(...checkX402Seller(data, projectRoot));
15430
15835
  checks.push(...checkStorage(data));
15431
15836
  checks.push(...await checkBalances(projectRoot, data, opts.network));
15432
15837
  checks.push(...await checkPieverseMainnetU(projectRoot, data));
@@ -16034,10 +16439,94 @@ function checkCurrency(data) {
16034
16439
  }
16035
16440
  return [];
16036
16441
  }
16442
+ function checkErc8183Pricing(data) {
16443
+ const payValue = tableOf13(data, "payments").erc8183;
16444
+ if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
16445
+ return [];
16446
+ }
16447
+ const pay = payValue;
16448
+ const pricing = erc8183PricingState(pay);
16449
+ if (pricing.kind === "unset") {
16450
+ return [
16451
+ {
16452
+ name: "erc8183 pricing",
16453
+ status: FAIL,
16454
+ detail: "[payments.erc8183].price is unset \u2014 set it explicitly to 0 for FREE or to a positive integer string in token base units."
16455
+ }
16456
+ ];
16457
+ }
16458
+ if (pricing.kind === "invalid") {
16459
+ return [
16460
+ {
16461
+ name: "erc8183 pricing",
16462
+ status: FAIL,
16463
+ detail: `[payments.erc8183].${pricing.field} must be a non-negative uint256 decimal string; got ${JSON.stringify(pricing.value)}.`
16464
+ }
16465
+ ];
16466
+ }
16467
+ if (pricing.kind === "clamped_to_zero") {
16468
+ return [
16469
+ {
16470
+ name: "erc8183 pricing",
16471
+ status: FAIL,
16472
+ detail: `PAID list price ${pricing.listPrice} is clamped to 0 by max_price. Set price explicitly to 0 for FREE or fix the clamp.`
16473
+ }
16474
+ ];
16475
+ }
16476
+ const contracts = erc8183ContractOverrideState();
16477
+ const priceMode = pricing.kind === "free" ? "FREE \u2014 zero token escrow" : `PAID \u2014 effective list price ${pricing.effectivePrice} token base units`;
16478
+ if (contracts.mode === "partial") {
16479
+ return [
16480
+ {
16481
+ name: "erc8183 pricing",
16482
+ status: FAIL,
16483
+ detail: `${priceMode}, but the ERC-8183 contract override is incomplete. Set ${contracts.missing.join(", ")} so commerce, router, and policy come from one compatible stack.`
16484
+ }
16485
+ ];
16486
+ }
16487
+ if (contracts.mode === "invalid") {
16488
+ return [
16489
+ {
16490
+ name: "erc8183 pricing",
16491
+ status: FAIL,
16492
+ detail: `${priceMode}, but these contract overrides are not valid EVM addresses: ${contracts.invalid.join(", ")}.`
16493
+ }
16494
+ ];
16495
+ }
16496
+ if (pricing.kind === "paid") {
16497
+ return [
16498
+ {
16499
+ name: "erc8183 pricing",
16500
+ status: PASS,
16501
+ detail: `${priceMode}.`
16502
+ }
16503
+ ];
16504
+ }
16505
+ if (contracts.mode === "custom") {
16506
+ return [
16507
+ {
16508
+ name: "erc8183 pricing",
16509
+ status: PASS,
16510
+ detail: "FREE \u2014 zero token escrow; custom/QA contract stack selected with all three ERC-8183 address overrides."
16511
+ }
16512
+ ];
16513
+ }
16514
+ return [
16515
+ {
16516
+ name: "erc8183 pricing",
16517
+ status: FAIL,
16518
+ detail: "FREE \u2014 zero token escrow, but the canonical ERC-8183 contracts reject zero funding. Set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together to a zero-price-compatible QA/custom stack."
16519
+ }
16520
+ ];
16521
+ }
16037
16522
  function checkPriceBounds(data) {
16038
- const pay = tableOf13(tableOf13(data, "payments"), "erc8183");
16523
+ const payValue = tableOf13(data, "payments").erc8183;
16524
+ if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
16525
+ return [];
16526
+ }
16527
+ const pay = payValue;
16039
16528
  const maxPrice = String(pay.max_price ?? "").trim();
16040
- if (!maxPrice || maxPrice === "0") {
16529
+ if (!maxPrice && erc8183PricingState(pay).kind !== "free") {
16041
16530
  return [
16042
16531
  {
16043
16532
  name: "erc8183 price bounds",
@@ -16048,6 +16537,84 @@ function checkPriceBounds(data) {
16048
16537
  }
16049
16538
  return [];
16050
16539
  }
16540
+ function checkX402Seller(data, agentRoot2) {
16541
+ const payments = tableOf13(data, "payments");
16542
+ const sellerValue = payments.x402_seller;
16543
+ if (sellerValue === null || typeof sellerValue !== "object" || Array.isArray(sellerValue)) {
16544
+ return [];
16545
+ }
16546
+ const seller = sellerValue;
16547
+ if (seller.enabled !== true) return [];
16548
+ const pricing = x402SellerPricingState(seller);
16549
+ if (pricing.kind === "invalid") {
16550
+ return [
16551
+ {
16552
+ name: "x402 pricing",
16553
+ status: FAIL,
16554
+ detail: "[payments.x402_seller].price_usd must be a non-negative decimal string."
16555
+ }
16556
+ ];
16557
+ }
16558
+ const runtime = String(tableOf13(data, "stack").runtime ?? "agentcore");
16559
+ const hasErc8183 = payments.erc8183 !== null && typeof payments.erc8183 === "object" && !Array.isArray(payments.erc8183);
16560
+ const fallbackStatus = hasErc8183 ? WARN : FAIL;
16561
+ if (pricing.kind === "free") {
16562
+ const out2 = [
16563
+ {
16564
+ name: "x402 pricing",
16565
+ status: PASS,
16566
+ detail: "FREE \u2014 /x402 is anonymous; B402 verify/settle, token payment, and settlement audit are bypassed."
16567
+ }
16568
+ ];
16569
+ const credentials2 = b402Credentials(agentRoot2);
16570
+ if (credentials2.any) {
16571
+ out2.push({
16572
+ name: "x402 credentials",
16573
+ status: INFO,
16574
+ detail: "configured B402 credentials are unused in FREE mode and will not be synchronized."
16575
+ });
16576
+ }
16577
+ if (runtime !== "agentcore") {
16578
+ out2.push({
16579
+ name: "x402 runtime",
16580
+ status: fallbackStatus,
16581
+ detail: `${runtime} has no /x402 route; FREE pricing does not remove that runtime limitation.`
16582
+ });
16583
+ }
16584
+ return out2;
16585
+ }
16586
+ const out = [
16587
+ {
16588
+ name: "x402 pricing",
16589
+ status: PASS,
16590
+ detail: `PAID \u2014 $${pricing.priceUsd} per request through B402.`
16591
+ }
16592
+ ];
16593
+ const walletKind2 = String(tableOf13(data, "wallet").kind ?? "evm-local");
16594
+ if (!["evm-local", "twak"].includes(walletKind2)) {
16595
+ out.push({
16596
+ name: "x402 payout wallet",
16597
+ status: FAIL,
16598
+ detail: `PAID B402 supports evm-local or twak, not ${walletKind2}.`
16599
+ });
16600
+ }
16601
+ const credentials = b402Credentials(agentRoot2);
16602
+ if (!credentials.complete) {
16603
+ out.push({
16604
+ name: "x402 credentials",
16605
+ status: credentials.any ? FAIL : fallbackStatus,
16606
+ detail: credentials.any ? "B402 credentials are partial; set BASE_URL, CLIENT_ID, ACCESS_TOKEN, and exactly one private-key form." : "PAID mode needs the four B402 merchant credentials; without them the rail starts dormant."
16607
+ });
16608
+ }
16609
+ if (runtime !== "agentcore") {
16610
+ out.push({
16611
+ name: "x402 runtime",
16612
+ status: fallbackStatus,
16613
+ detail: `${runtime} has no /x402 route; deploy this rail to AgentCore.`
16614
+ });
16615
+ }
16616
+ return out;
16617
+ }
16051
16618
  function checkStorage(data) {
16052
16619
  const kind = String(tableOf13(data, "storage").kind ?? "local").toLowerCase();
16053
16620
  if (kind === "ipfs") {
@@ -18576,7 +19143,10 @@ function registerX402(program) {
18576
19143
  return 1;
18577
19144
  })
18578
19145
  );
18579
- sell.command("init").description("Add the x402 seller config and missing B402 placeholders.").action(act(() => cmdSellInit()));
19146
+ sell.command("init").description("Add the x402 seller config and missing B402 placeholders.").option(
19147
+ "--price-usd <amount>",
19148
+ "Per-request decimal USD price; use 0 for FREE passthrough."
19149
+ ).action(act((opts) => cmdSellInit(opts.priceUsd)));
18580
19150
  sell.command("status").description("Show x402 seller config and B402 capability status.").option("--no-probe", "Skip the authenticated B402 /supported probe.").action(act((opts) => cmdSellStatus(opts.probe)));
18581
19151
  }
18582
19152
  function hostOf(url) {
@@ -18661,39 +19231,83 @@ function resolveWallet2() {
18661
19231
  function printScopeError(host) {
18662
19232
  printErr(`error: ${SCOPE_NOTE.replace("{host}", host)}`);
18663
19233
  }
18664
- function cmdSellInit() {
19234
+ async function cmdSellInit(priceFlag) {
18665
19235
  const loaded = loadSellerConfig();
18666
19236
  if (loaded === null) return 1;
18667
19237
  const [root, cfg] = loaded;
19238
+ const tomlPath = path46.join(root, "studio.toml");
19239
+ const original = fs44.readFileSync(tomlPath, "utf8");
19240
+ const sellerExists = hasSection(original, "payments.x402_seller");
19241
+ let priceUsd;
19242
+ try {
19243
+ priceUsd = priceFlag !== void 0 ? normalizeB402PriceUsd(priceFlag) : sellerExists ? X402SellerPolicy.fromToml(cfg).priceUsd : await resolveSellInitPrice(stdinIsTty());
19244
+ } catch (exc) {
19245
+ printErr(`error: ${errMsg6(exc)}`);
19246
+ return 2;
19247
+ }
19248
+ const free = x402SellerPricingState({ price_usd: priceUsd }).kind === "free";
18668
19249
  const walletKind2 = String(tableOf15(cfg, "wallet").kind ?? "evm-local");
18669
- if (!["evm-local", "twak"].includes(walletKind2)) {
19250
+ if (!free && !["evm-local", "twak"].includes(walletKind2)) {
18670
19251
  printErr(
18671
19252
  `error: the b402 seller rail supports wallet.kind evm-local or twak only; '${walletKind2}' cannot act as the b402 payout wallet.`
18672
19253
  );
18673
19254
  return 1;
18674
19255
  }
18675
- const tomlPath = path46.join(root, "studio.toml");
18676
- const original = fs44.readFileSync(tomlPath, "utf8");
18677
- if (!hasSection(original, "payments.x402_seller")) {
19256
+ if (!sellerExists) {
18678
19257
  fs44.writeFileSync(
18679
19258
  tomlPath,
18680
19259
  updateSection(original, "payments.x402_seller", {
18681
19260
  enabled: true,
18682
- price_usd: "0.01",
19261
+ price_usd: priceUsd,
18683
19262
  assets: ["U"],
18684
19263
  pay_to: "",
18685
19264
  work_timeout_seconds: 60
18686
19265
  }),
18687
19266
  "utf8"
18688
19267
  );
19268
+ } else if (priceFlag !== void 0) {
19269
+ fs44.writeFileSync(
19270
+ tomlPath,
19271
+ updateSection(original, "payments.x402_seller", {
19272
+ price_usd: priceUsd
19273
+ }),
19274
+ "utf8"
19275
+ );
18689
19276
  }
18690
19277
  const envPath = envLocalPath17(root);
18691
19278
  for (const key of B402_ENV_KEYS) {
18692
19279
  if (getEnvVar(envPath, key) === null) setEnvVar(envPath, key, "");
18693
19280
  }
18694
- printOut("\u2713 x402 seller config and B402 placeholders are ready.");
19281
+ printOut(
19282
+ free ? "\u2713 x402 seller config is ready in FREE mode; B402 credentials are not required." : "\u2713 x402 seller config and B402 placeholders are ready for PAID mode."
19283
+ );
19284
+ if (free) {
19285
+ printErr(
19286
+ "warning: /x402 is an anonymous FREE endpoint; B402 verify/settle and payment audit are bypassed."
19287
+ );
19288
+ }
18695
19289
  return 0;
18696
19290
  }
19291
+ async function resolveSellInitPrice(isTty) {
19292
+ if (!isTty) return DEFAULT_B402_PRICE_USD;
19293
+ const choice = (await promptUser(
19294
+ "B402/x402 pricing [paid/free/custom, Enter = paid ($0.01)]: "
19295
+ )).trim().toLowerCase();
19296
+ if (choice === "free") return "0";
19297
+ if (choice === "custom") {
19298
+ return normalizeB402PriceUsd(
19299
+ await promptUser(
19300
+ "B402/x402 per-request price in USD (non-negative decimal): "
19301
+ )
19302
+ );
19303
+ }
19304
+ if (choice !== "" && choice !== "paid") {
19305
+ printErr(
19306
+ `hint: unknown B402/x402 pricing choice '${choice}' \u2014 using paid.`
19307
+ );
19308
+ }
19309
+ return DEFAULT_B402_PRICE_USD;
19310
+ }
18697
19311
  async function cmdSellStatus(probe) {
18698
19312
  const loaded = loadSellerConfig();
18699
19313
  if (loaded === null) return 1;
@@ -18714,9 +19328,13 @@ async function cmdSellStatus(probe) {
18714
19328
  };
18715
19329
  const privateKeySet = presence.B402_PRIVATE_KEY || presence.B402_PRIVATE_KEY_B64;
18716
19330
  const credentialsComplete = presence.B402_BASE_URL && presence.B402_CLIENT_ID && presence.B402_ACCESS_TOKEN && privateKeySet;
18717
- const state = !policy.enabled ? "disabled" : credentialsComplete ? "active-config" : "dormant";
19331
+ const free = x402SellerPricingState({ price_usd: policy.priceUsd }).kind === "free";
19332
+ const state = !policy.enabled ? "disabled" : free ? "free" : credentialsComplete ? "active-config" : "dormant";
18718
19333
  printOut(`Rail state: ${state}`);
18719
19334
  printOut(` price_usd: ${policy.priceUsd}`);
19335
+ printOut(
19336
+ ` Pricing: ${free ? "FREE \u2014 anonymous passthrough; no payment" : "PAID \u2014 B402 verify/settle required"}`
19337
+ );
18720
19338
  printOut(` assets: ${JSON.stringify(policy.assets)}`);
18721
19339
  printOut(` pay_to: ${policy.payTo ?? "(agent wallet)"}`);
18722
19340
  printOut(` work_timeout_seconds: ${policy.workTimeoutSeconds}`);
@@ -18729,6 +19347,10 @@ async function cmdSellStatus(probe) {
18729
19347
  printOut("B402 probe: skipped (--no-probe)");
18730
19348
  return 0;
18731
19349
  }
19350
+ if (policy.enabled && free) {
19351
+ printOut("B402 probe: skipped (FREE bypasses B402)");
19352
+ return 0;
19353
+ }
18732
19354
  if (!policy.enabled || !credentialsComplete) {
18733
19355
  printOut("B402 probe: skipped (rail disabled or credentials incomplete)");
18734
19356
  return 0;
@@ -19162,7 +19784,7 @@ function buildProgram() {
19162
19784
  return program;
19163
19785
  }
19164
19786
  function cliVersion() {
19165
- return "0.0.6-alpha.1";
19787
+ return "0.0.6-alpha.2";
19166
19788
  }
19167
19789
 
19168
19790
  // src/cli/updateCheck.ts