@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 +743 -121
- package/dist/{chunk-M3ODFCA7.js → chunk-6EYSXRFD.js} +78 -14
- package/dist/{deployCli-N6TPN6XA.js → deployCli-3U2FESF4.js} +1 -1
- package/package.json +2 -2
- package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +12 -1
- package/skills/bnbagent-studio.md +13 -4
- package/skills/references/bnbagent-studio-adding-to-project.md +16 -0
- package/skills/references/bnbagent-studio-buying-via-8183.md +19 -3
- package/skills/references/bnbagent-studio-operating.md +19 -0
- package/skills/references/bnbagent-studio-scaffolding-agent.md +18 -3
- package/skills/references/bnbagent-studio-selling-via-8183.md +20 -0
- package/skills/references/bnbagent-studio-selling-via-b402.md +41 -17
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +3 -2
- package/skills/references/bnbagent-studio-using-altana-wallet.md +5 -4
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
|
-
|
|
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-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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"
|
|
7765
|
-
price_usd = "
|
|
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 = "
|
|
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
|
-
"#
|
|
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}.
|
|
9228
|
+
` # ${nextStep}. review ERC-8183 pricing in studio.toml [payments.erc8183]`
|
|
8944
9229
|
);
|
|
8945
9230
|
printOut(
|
|
8946
|
-
" # (
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
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" &&
|
|
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" &&
|
|
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
|
|
10246
|
-
|
|
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: "
|
|
10264
|
-
message:
|
|
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
|
-
|
|
10268
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
|
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} (
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.").
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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.
|
|
19787
|
+
return "0.0.6-alpha.2";
|
|
19166
19788
|
}
|
|
19167
19789
|
|
|
19168
19790
|
// src/cli/updateCheck.ts
|