@bnbagent/studio-cli 0.0.6-alpha.1 → 0.0.6-alpha.3
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 +948 -190
- package/dist/{chunk-M3ODFCA7.js → chunk-A7NAGZHR.js} +94 -15
- package/dist/{deployCli-N6TPN6XA.js → deployCli-264UE6KB.js} +1 -1
- package/package.json +3 -3
- package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +12 -1
- package/recipes/runtimes/agentcore/code/{{PKG}}/dualMain.ts.tmpl +6 -3
- package/recipes/runtimes/agentcore/code/{{PKG}}/main.ts.tmpl +6 -3
- package/recipes/runtimes/agentcore/code/{{PKG}}/mcpMain.ts.tmpl +7 -5
- package/recipes/runtimes/agentcore/code/{{PKG}}/sellerCore.ts.tmpl +3 -2
- package/skills/bnbagent-studio.md +21 -14
- package/skills/references/bnbagent-studio-adding-to-project.md +17 -1
- package/skills/references/bnbagent-studio-buying-via-8183.md +19 -3
- package/skills/references/bnbagent-studio-operating.md +21 -3
- 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 +42 -18
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +3 -2
- package/skills/references/bnbagent-studio-use-azure-foundry.md +1 -1
- package/skills/references/bnbagent-studio-use-bnb-trial.md +2 -2
- package/skills/references/bnbagent-studio-using-altana-wallet.md +5 -4
- package/skills/references/bnbagent-studio-using-twak-wallet.md +10 -8
package/dist/bag.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
B402_RUNTIME_KEYS,
|
|
4
4
|
CliExit,
|
|
5
|
+
DEFAULT_B402_PRICE_USD,
|
|
6
|
+
DEFAULT_B402_TESTNET_BASE_URL,
|
|
5
7
|
act,
|
|
6
8
|
agentcoreFlavor,
|
|
7
9
|
b402Credentials,
|
|
@@ -18,7 +20,9 @@ import {
|
|
|
18
20
|
hasA2aFace,
|
|
19
21
|
hasMcpFace,
|
|
20
22
|
hasX402Face,
|
|
23
|
+
isB402TestnetBaseUrl,
|
|
21
24
|
nativeProtocolOf,
|
|
25
|
+
normalizeB402PriceUsd,
|
|
22
26
|
normalizeProtocolFaces,
|
|
23
27
|
printErr,
|
|
24
28
|
printOut,
|
|
@@ -34,8 +38,11 @@ import {
|
|
|
34
38
|
trialFromDeployCliJson,
|
|
35
39
|
whichBin,
|
|
36
40
|
withDeployFiles,
|
|
37
|
-
x402DeploySummary
|
|
38
|
-
|
|
41
|
+
x402DeploySummary,
|
|
42
|
+
x402SellerIsFree,
|
|
43
|
+
x402SellerPricingState,
|
|
44
|
+
x402SellerUsesB402
|
|
45
|
+
} from "./chunk-A7NAGZHR.js";
|
|
39
46
|
import {
|
|
40
47
|
TWAK_CLI_MIN_VERSION,
|
|
41
48
|
TWAK_CLI_VERSION,
|
|
@@ -97,7 +104,7 @@ import {
|
|
|
97
104
|
var CAMPAIGN_DOC_URL = "https://www.bnbchain.org/en/blog/bnb-agent-studio-is-live-on-bnb-chain-ai-agents-from-one-prompt";
|
|
98
105
|
var CAMPAIGN_CHECK_TIMEOUT_MS = 6e3;
|
|
99
106
|
async function fetchCampaignActive() {
|
|
100
|
-
const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-
|
|
107
|
+
const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-264UE6KB.js");
|
|
101
108
|
const controller = new AbortController();
|
|
102
109
|
const timer = setTimeout(() => controller.abort(), CAMPAIGN_CHECK_TIMEOUT_MS);
|
|
103
110
|
try {
|
|
@@ -2095,6 +2102,77 @@ function devPortInUse(port = 9e3, timeoutMs = 250) {
|
|
|
2095
2102
|
});
|
|
2096
2103
|
}
|
|
2097
2104
|
|
|
2105
|
+
// src/cli/_erc8183Config.ts
|
|
2106
|
+
var ERC8183_ADDRESS_OVERRIDE_KEYS = [
|
|
2107
|
+
"ERC8183_COMMERCE_ADDRESS",
|
|
2108
|
+
"ERC8183_ROUTER_ADDRESS",
|
|
2109
|
+
"ERC8183_POLICY_ADDRESS"
|
|
2110
|
+
];
|
|
2111
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2112
|
+
function erc8183PricingState(pay) {
|
|
2113
|
+
const priceRaw = pay.price;
|
|
2114
|
+
const price = String(priceRaw ?? "").trim();
|
|
2115
|
+
if (!price) return { kind: "unset" };
|
|
2116
|
+
if (typeof priceRaw !== "string") {
|
|
2117
|
+
return { kind: "invalid", field: "price", value: price };
|
|
2118
|
+
}
|
|
2119
|
+
const minRaw = pay.min_price;
|
|
2120
|
+
const maxRaw = pay.max_price;
|
|
2121
|
+
const minPrice = String(minRaw ?? "").trim() || "0";
|
|
2122
|
+
const maxPrice = String(maxRaw ?? "").trim();
|
|
2123
|
+
if (minRaw !== void 0 && typeof minRaw !== "string") {
|
|
2124
|
+
return { kind: "invalid", field: "min_price", value: minPrice };
|
|
2125
|
+
}
|
|
2126
|
+
if (maxRaw !== void 0 && typeof maxRaw !== "string") {
|
|
2127
|
+
return { kind: "invalid", field: "max_price", value: maxPrice };
|
|
2128
|
+
}
|
|
2129
|
+
for (const [field, value] of [
|
|
2130
|
+
["price", price],
|
|
2131
|
+
["min_price", minPrice],
|
|
2132
|
+
["max_price", maxPrice]
|
|
2133
|
+
]) {
|
|
2134
|
+
if ((field !== "max_price" || value !== "") && !/^\d+$/.test(value)) {
|
|
2135
|
+
return { kind: "invalid", field, value };
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
const list = BigInt(price);
|
|
2139
|
+
const min = BigInt(minPrice);
|
|
2140
|
+
const max = maxPrice ? BigInt(maxPrice) : MAX_UINT256;
|
|
2141
|
+
if (list > MAX_UINT256 || min > MAX_UINT256 || max > MAX_UINT256) {
|
|
2142
|
+
const [field, value] = list > MAX_UINT256 ? ["price", price] : min > MAX_UINT256 ? ["min_price", minPrice] : ["max_price", maxPrice];
|
|
2143
|
+
return { kind: "invalid", field, value };
|
|
2144
|
+
}
|
|
2145
|
+
const clampedToMax = list < max ? list : max;
|
|
2146
|
+
const effective = min > clampedToMax ? min : clampedToMax;
|
|
2147
|
+
const base = {
|
|
2148
|
+
listPrice: list,
|
|
2149
|
+
minPrice: min,
|
|
2150
|
+
maxPrice: max,
|
|
2151
|
+
effectivePrice: effective
|
|
2152
|
+
};
|
|
2153
|
+
if (list > 0n && effective === 0n) {
|
|
2154
|
+
return { kind: "clamped_to_zero", ...base };
|
|
2155
|
+
}
|
|
2156
|
+
return { kind: effective === 0n ? "free" : "paid", ...base };
|
|
2157
|
+
}
|
|
2158
|
+
function erc8183ContractOverrideState(env = process.env) {
|
|
2159
|
+
const present = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
|
|
2160
|
+
(key) => Boolean(env[key]?.trim())
|
|
2161
|
+
);
|
|
2162
|
+
const missing = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
|
|
2163
|
+
(key) => !env[key]?.trim()
|
|
2164
|
+
);
|
|
2165
|
+
const invalid = present.filter(
|
|
2166
|
+
(key) => !/^0x[0-9a-fA-F]{40}$/.test(env[key]?.trim() ?? "")
|
|
2167
|
+
);
|
|
2168
|
+
return {
|
|
2169
|
+
mode: present.length === 0 ? "canonical" : invalid.length > 0 ? "invalid" : missing.length === 0 ? "custom" : "partial",
|
|
2170
|
+
present: [...present],
|
|
2171
|
+
missing: [...missing],
|
|
2172
|
+
invalid
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2098
2176
|
// src/cli/config.ts
|
|
2099
2177
|
var MAX_QUOTE_TTL_SECONDS = NegotiationHandler.MAX_QUOTE_TTL_SECONDS;
|
|
2100
2178
|
function registerConfig(program) {
|
|
@@ -2273,6 +2351,38 @@ function validateKnownKey(parts, value) {
|
|
|
2273
2351
|
}
|
|
2274
2352
|
return null;
|
|
2275
2353
|
}
|
|
2354
|
+
function isErc8183AmountKey(parts) {
|
|
2355
|
+
return parts.length === 3 && parts[0] === "payments" && parts[1] === "erc8183" && ["price", "min_price", "max_price"].includes(parts[2]);
|
|
2356
|
+
}
|
|
2357
|
+
function isB402PriceKey(parts) {
|
|
2358
|
+
return parts.length === 3 && parts[0] === "payments" && parts[1] === "x402_seller" && parts[2] === "price_usd";
|
|
2359
|
+
}
|
|
2360
|
+
function coerceKnownValue(parts, rawValue, typeFlag) {
|
|
2361
|
+
if (!isErc8183AmountKey(parts) && !isB402PriceKey(parts)) {
|
|
2362
|
+
return coerceValue(rawValue, typeFlag);
|
|
2363
|
+
}
|
|
2364
|
+
if (isB402PriceKey(parts)) {
|
|
2365
|
+
if (typeFlag !== "auto" && typeFlag !== "string") {
|
|
2366
|
+
throw new Error(
|
|
2367
|
+
`${parts.join(".")} is stored as a decimal string; use --type string or omit --type.`
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
return normalizeB402PriceUsd(rawValue);
|
|
2371
|
+
}
|
|
2372
|
+
if (typeFlag !== "auto" && typeFlag !== "string") {
|
|
2373
|
+
throw new Error(
|
|
2374
|
+
`${parts.join(".")} is stored as a decimal string; use --type string or omit --type.`
|
|
2375
|
+
);
|
|
2376
|
+
}
|
|
2377
|
+
const value = rawValue.trim();
|
|
2378
|
+
const isUnboundedMax = parts[2] === "max_price" && value === "";
|
|
2379
|
+
if (!isUnboundedMax && !/^\d+$/.test(value)) {
|
|
2380
|
+
throw new Error(
|
|
2381
|
+
`${parts.join(".")} must be a non-negative integer string in token base units; got ${JSON.stringify(rawValue)}.`
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2384
|
+
return value;
|
|
2385
|
+
}
|
|
2276
2386
|
function setDottedKey(text2, parts, value) {
|
|
2277
2387
|
const key = parts[parts.length - 1];
|
|
2278
2388
|
const section = parts.slice(0, -1).join(".");
|
|
@@ -2347,7 +2457,7 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
|
|
|
2347
2457
|
}
|
|
2348
2458
|
let value;
|
|
2349
2459
|
try {
|
|
2350
|
-
value =
|
|
2460
|
+
value = coerceKnownValue(parts, rawValue, typeFlag);
|
|
2351
2461
|
} catch (err) {
|
|
2352
2462
|
printErr(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2353
2463
|
return 2;
|
|
@@ -2357,8 +2467,64 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
|
|
|
2357
2467
|
printErr(`error: ${validationError}`);
|
|
2358
2468
|
return 2;
|
|
2359
2469
|
}
|
|
2360
|
-
|
|
2470
|
+
const updatedText = setDottedKey(text2, parts, value);
|
|
2471
|
+
fs9.writeFileSync(tomlPath, updatedText, "utf-8");
|
|
2361
2472
|
printOut(`set ${key} = ${JSON.stringify(plainValue(value))}`);
|
|
2473
|
+
if (parts.length === 3 && parts[0] === "payments" && parts[1] === "erc8183" && parts[2] === "price") {
|
|
2474
|
+
const updated = parse4(updatedText);
|
|
2475
|
+
const payments = updated.payments !== null && typeof updated.payments === "object" && !Array.isArray(updated.payments) ? updated.payments : {};
|
|
2476
|
+
const erc = payments.erc8183 !== null && typeof payments.erc8183 === "object" && !Array.isArray(payments.erc8183) ? payments.erc8183 : {};
|
|
2477
|
+
const pricing = erc8183PricingState(erc);
|
|
2478
|
+
if (pricing.kind === "invalid" || pricing.kind === "unset") {
|
|
2479
|
+
printErr(
|
|
2480
|
+
"warning: price was saved, but existing min_price/max_price is invalid; run `bag doctor`."
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
if (pricing.kind === "free") {
|
|
2484
|
+
printOut("pricing: FREE \u2014 buyers fund 0 token units; zero token escrow.");
|
|
2485
|
+
const contracts = erc8183ContractOverrideState();
|
|
2486
|
+
if (contracts.mode === "custom") {
|
|
2487
|
+
printOut(
|
|
2488
|
+
"ERC-8183 contracts: custom contract stack selected with all three address overrides."
|
|
2489
|
+
);
|
|
2490
|
+
} else if (contracts.mode === "partial") {
|
|
2491
|
+
printErr(
|
|
2492
|
+
`warning: ERC-8183 contract override is incomplete; set ${contracts.missing.join(", ")} before running a job.`
|
|
2493
|
+
);
|
|
2494
|
+
} else if (contracts.mode === "invalid") {
|
|
2495
|
+
printErr(
|
|
2496
|
+
`warning: invalid ERC-8183 address override(s): ${contracts.invalid.join(", ")}.`
|
|
2497
|
+
);
|
|
2498
|
+
} else {
|
|
2499
|
+
printErr(
|
|
2500
|
+
"warning: FREE requires a zero-price-compatible contract stack; set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together before deploy."
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
} else if (pricing.kind === "clamped_to_zero") {
|
|
2504
|
+
printErr(
|
|
2505
|
+
"warning: PAID list price is clamped to 0; this is not an explicit FREE choice. Fix max_price or set price to 0."
|
|
2506
|
+
);
|
|
2507
|
+
} else if (pricing.kind === "paid") {
|
|
2508
|
+
printOut(
|
|
2509
|
+
`pricing: PAID \u2014 effective price ${pricing.effectivePrice} token base units after clamping.`
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
if (isB402PriceKey(parts)) {
|
|
2514
|
+
const pricing = x402SellerPricingState({ price_usd: value });
|
|
2515
|
+
if (pricing.kind === "free") {
|
|
2516
|
+
printOut(
|
|
2517
|
+
"x402 pricing: FREE \u2014 B402 verify/settle is bypassed; no payment or settlement audit."
|
|
2518
|
+
);
|
|
2519
|
+
printErr(
|
|
2520
|
+
"warning: /x402 is now an anonymous FREE endpoint. Confirm that unrestricted public access is intended."
|
|
2521
|
+
);
|
|
2522
|
+
} else if (pricing.kind === "paid") {
|
|
2523
|
+
printOut(
|
|
2524
|
+
`x402 pricing: PAID \u2014 $${pricing.priceUsd} per request through B402.`
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2362
2528
|
if (parts[0] === "network") {
|
|
2363
2529
|
if (await devPortInUse(8080)) {
|
|
2364
2530
|
printErr(
|
|
@@ -3033,6 +3199,9 @@ function packageRoot() {
|
|
|
3033
3199
|
}
|
|
3034
3200
|
}
|
|
3035
3201
|
function pnpmVersion() {
|
|
3202
|
+
if ("10.24.0") {
|
|
3203
|
+
return "10.24.0";
|
|
3204
|
+
}
|
|
3036
3205
|
const file = path12.join(packageRoot(), "package.json");
|
|
3037
3206
|
const pkg = JSON.parse(fs13.readFileSync(file, "utf-8"));
|
|
3038
3207
|
const value = String(pkg.packageManager ?? "");
|
|
@@ -3061,6 +3230,46 @@ import * as fs14 from "fs";
|
|
|
3061
3230
|
import * as path13 from "path";
|
|
3062
3231
|
import { envLocalPath as envLocalPath7 } from "@bnbagent/studio-runtime/config";
|
|
3063
3232
|
import { resolveTwakHome } from "@bnbagent/studio-runtime/wallet";
|
|
3233
|
+
|
|
3234
|
+
// src/cli/_twakContractTargets.ts
|
|
3235
|
+
import { resolveNetwork } from "@bnbagent/sdk";
|
|
3236
|
+
var CONTRACT_OVERRIDES = [
|
|
3237
|
+
["ERC8004_REGISTRY_ADDRESS", "registryContract"],
|
|
3238
|
+
["ERC8183_COMMERCE_ADDRESS", "commerceContract"],
|
|
3239
|
+
["ERC8183_ROUTER_ADDRESS", "routerContract"],
|
|
3240
|
+
["ERC8183_POLICY_ADDRESS", "policyContract"]
|
|
3241
|
+
];
|
|
3242
|
+
function twakCustomContractOverrides(networkName, env = process.env) {
|
|
3243
|
+
let network;
|
|
3244
|
+
try {
|
|
3245
|
+
network = resolveNetwork(networkName);
|
|
3246
|
+
} catch {
|
|
3247
|
+
return [];
|
|
3248
|
+
}
|
|
3249
|
+
const erc8183 = erc8183ContractOverrideState(env);
|
|
3250
|
+
const out = [];
|
|
3251
|
+
for (const [envKey, field] of CONTRACT_OVERRIDES) {
|
|
3252
|
+
if (envKey.startsWith("ERC8183_") && erc8183.mode !== "custom") {
|
|
3253
|
+
continue;
|
|
3254
|
+
}
|
|
3255
|
+
const configured = env[envKey]?.trim() ?? "";
|
|
3256
|
+
if (!/^0x[0-9a-fA-F]{40}$/u.test(configured)) {
|
|
3257
|
+
continue;
|
|
3258
|
+
}
|
|
3259
|
+
const canonical = network[field];
|
|
3260
|
+
if (configured.toLowerCase() !== canonical.toLowerCase()) {
|
|
3261
|
+
out.push({ envKey, configured, canonical });
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
return out;
|
|
3265
|
+
}
|
|
3266
|
+
function twakUnsupportedContractOverrides(networkName, env = process.env) {
|
|
3267
|
+
return twakCustomContractOverrides(networkName, env).filter(
|
|
3268
|
+
(item) => item.envKey.startsWith("ERC8183_")
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
// src/cli/_deploy/checks/twak.ts
|
|
3064
3273
|
var AGENTCORE_DESCRIPTOR = path13.join("agentcore", "agentcore.json");
|
|
3065
3274
|
function isTwak(data) {
|
|
3066
3275
|
return tableOf2(data, "wallet").kind === "twak";
|
|
@@ -3165,6 +3374,26 @@ async function checkTwakCliPresent(root, _target) {
|
|
|
3165
3374
|
}
|
|
3166
3375
|
return [];
|
|
3167
3376
|
}
|
|
3377
|
+
function checkTwakCustomContractsUnsupported(root, _target) {
|
|
3378
|
+
const data = loadAgentToml(root);
|
|
3379
|
+
if (!isTwak(data)) {
|
|
3380
|
+
return [];
|
|
3381
|
+
}
|
|
3382
|
+
const networkName = String(tableOf2(data, "network").default ?? "bsc-testnet");
|
|
3383
|
+
const overrides = twakUnsupportedContractOverrides(networkName);
|
|
3384
|
+
if (overrides.length === 0) {
|
|
3385
|
+
return [];
|
|
3386
|
+
}
|
|
3387
|
+
const keys = overrides.map((item) => item.envKey);
|
|
3388
|
+
return [
|
|
3389
|
+
{
|
|
3390
|
+
level: Level.CRITICAL,
|
|
3391
|
+
name: "twak_custom_contracts_unsupported",
|
|
3392
|
+
message: `wallet.kind='twak' cannot use the selected custom ERC-8183 targets (${keys.join(", ")}): twak v0.20.0 has no Commerce/Router/Policy address option and would otherwise execute against the wrong canonical contracts. Use wallet.kind='evm-local' for custom ERC-8183 contracts, or remove the overrides and use the canonical stack. ERC8004_REGISTRY_ADDRESS is supported.`,
|
|
3393
|
+
details: { network: networkName, override_keys: keys }
|
|
3394
|
+
}
|
|
3395
|
+
];
|
|
3396
|
+
}
|
|
3168
3397
|
async function checkTwakWalletExists(root, _target) {
|
|
3169
3398
|
const data = loadAgentToml(root);
|
|
3170
3399
|
if (!isTwak(data)) {
|
|
@@ -5197,7 +5426,9 @@ var LEGACY_SKILL_NAMES = [
|
|
|
5197
5426
|
];
|
|
5198
5427
|
var ROUTER_SKILL_NAME = "bnbagent-studio";
|
|
5199
5428
|
var META_FILENAME = ".bag-meta.json";
|
|
5200
|
-
var HIDDEN_SKILL_NAMES = /* @__PURE__ */ new Set(
|
|
5429
|
+
var HIDDEN_SKILL_NAMES = /* @__PURE__ */ new Set([
|
|
5430
|
+
"bnbagent-studio-use-azure-foundry"
|
|
5431
|
+
]);
|
|
5201
5432
|
function isDir3(p) {
|
|
5202
5433
|
try {
|
|
5203
5434
|
return fs19.statSync(p).isDirectory();
|
|
@@ -5603,6 +5834,19 @@ function installSkills(opts) {
|
|
|
5603
5834
|
};
|
|
5604
5835
|
}
|
|
5605
5836
|
|
|
5837
|
+
// src/cli/utils/options.ts
|
|
5838
|
+
import { InvalidArgumentError } from "commander";
|
|
5839
|
+
function acceptChoices(option, accepted, advertised) {
|
|
5840
|
+
return option.argParser((value) => {
|
|
5841
|
+
if (!accepted.includes(value)) {
|
|
5842
|
+
throw new InvalidArgumentError(
|
|
5843
|
+
`Allowed choices are ${advertised.join(", ")}.`
|
|
5844
|
+
);
|
|
5845
|
+
}
|
|
5846
|
+
return value;
|
|
5847
|
+
});
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5606
5850
|
// src/cli/wallet.ts
|
|
5607
5851
|
import * as fs21 from "fs";
|
|
5608
5852
|
import * as path20 from "path";
|
|
@@ -6712,7 +6956,7 @@ var SUPPORTED_STORAGE_PROVIDERS = ["ipfs", "local"];
|
|
|
6712
6956
|
var SUPPORTED_DESTINATIONS = ["self", "platform"];
|
|
6713
6957
|
var RUNTIMES = {
|
|
6714
6958
|
agentcore: { label: "AWS Bedrock AgentCore", hidden: false },
|
|
6715
|
-
"azure-foundry": { label: "Azure AI Foundry", hidden:
|
|
6959
|
+
"azure-foundry": { label: "Azure AI Foundry", hidden: true }
|
|
6716
6960
|
};
|
|
6717
6961
|
var SUPPORTED_RUNTIMES = Object.keys(RUNTIMES);
|
|
6718
6962
|
var APP_DIR = "app";
|
|
@@ -6789,7 +7033,7 @@ every intent to the right playbook via its references.
|
|
|
6789
7033
|
lives in \`src/signing.ts\`.
|
|
6790
7034
|
4. **The quote path is deterministic** (fixed list price, clamp + sign).
|
|
6791
7035
|
Never put an LLM in the quote path.
|
|
6792
|
-
5. **Deploy with \`bag deploy --provider bnb|aws
|
|
7036
|
+
5. **Deploy with \`bag deploy --provider bnb|aws\`.** Every deploy or
|
|
6793
7037
|
redeploy requires a visible provider choice. Studio runs its local business
|
|
6794
7038
|
gates, then delegates cloud credentials, secrets, packaging and lifecycle
|
|
6795
7039
|
calls to the pinned bnbagent-deploy CLI. Do not bypass this boundary with
|
|
@@ -6931,7 +7175,7 @@ function validateProjectName(basename16) {
|
|
|
6931
7175
|
}
|
|
6932
7176
|
if (safe !== basename16) {
|
|
6933
7177
|
throw new Error(
|
|
6934
|
-
`Project name '${basename16}' is not a valid AgentCore runtime name: the default runtime (AWS Bedrock AgentCore) requires ASCII letters and digits only ('-', '_', '.' and other characters are not allowed) \u2014 this is an AgentCore naming constraint, not a general bag restriction. Re-run with an alphanumeric name, e.g. \`bag init ${safe}
|
|
7178
|
+
`Project name '${basename16}' is not a valid AgentCore runtime name: the default runtime (AWS Bedrock AgentCore) requires ASCII letters and digits only ('-', '_', '.' and other characters are not allowed) \u2014 this is an AgentCore naming constraint, not a general bag restriction. Re-run with an alphanumeric name, e.g. \`bag init ${safe}\`.`
|
|
6935
7179
|
);
|
|
6936
7180
|
}
|
|
6937
7181
|
return basename16;
|
|
@@ -6944,21 +7188,15 @@ function registerInit(program) {
|
|
|
6944
7188
|
"<name>",
|
|
6945
7189
|
"Project directory name (created in cwd). Becomes the AgentCore runtime name: ASCII alphanumerics only, must start with a letter, \u226423 chars. Names with '-'/'_'/'.' are rejected (not auto-renamed)."
|
|
6946
7190
|
).addOption(
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
"azure-foundry only: Azure AI Foundry account (resource) name used by the delegated deploy."
|
|
7191
|
+
acceptChoices(
|
|
7192
|
+
new Option2(
|
|
7193
|
+
"--runtime <name>",
|
|
7194
|
+
`Runtime / deploy target to scaffold for (choices: ${visibleRuntimes.join(", ")}); recorded in studio.toml [stack].runtime.`
|
|
7195
|
+
).default("agentcore"),
|
|
7196
|
+
SUPPORTED_RUNTIMES,
|
|
7197
|
+
visibleRuntimes
|
|
6955
7198
|
)
|
|
6956
|
-
).addOption(
|
|
6957
|
-
new Option2(
|
|
6958
|
-
"--azure-project <name>",
|
|
6959
|
-
"azure-foundry only: Azure AI Foundry project name used by the delegated deploy."
|
|
6960
|
-
)
|
|
6961
|
-
).addOption(
|
|
7199
|
+
).addOption(new Option2("--azure-account <name>").hideHelp()).addOption(new Option2("--azure-project <name>").hideHelp()).addOption(
|
|
6962
7200
|
new Option2(
|
|
6963
7201
|
"--destination <dest>",
|
|
6964
7202
|
"Where the agent deploys; recorded in studio.toml [deploy].destination (default: platform while the trial campaign runs; self once it has ended, or when a non-agentcore --runtime or --network bsc-mainnet is passed). 'platform' is a 48h testnet trial on the BNB Chain managed platform (forces runtime=agentcore + bsc-testnet; a trial wallet key is transmitted to the operator \u2014 run `bag wallet new` for a throwaway). 'self' deploys to YOUR own cloud."
|
|
@@ -6983,6 +7221,12 @@ function registerInit(program) {
|
|
|
6983
7221
|
"--rails <rails>",
|
|
6984
7222
|
"Commerce rail(s) to scaffold: 8183 (default), b402, or both."
|
|
6985
7223
|
).choices(["8183", "b402", "both"])
|
|
7224
|
+
).option(
|
|
7225
|
+
"--erc8183-price <base-units>",
|
|
7226
|
+
"ERC-8183 list price as a non-negative integer string in token base units. Use 0 for FREE. Default: 100000000000000000 (0.1 U)."
|
|
7227
|
+
).option(
|
|
7228
|
+
"--b402-price <usd>",
|
|
7229
|
+
"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
7230
|
).addOption(
|
|
6987
7231
|
new Option2(
|
|
6988
7232
|
"--llm-provider <provider>",
|
|
@@ -7099,6 +7343,45 @@ async function cmdInit(nameArg, opts) {
|
|
|
7099
7343
|
opts.llmProviderSource,
|
|
7100
7344
|
isTty
|
|
7101
7345
|
);
|
|
7346
|
+
let rails = await resolveRails(opts.rails, isTty);
|
|
7347
|
+
if (hasX402Face(faces) && rails === "8183") {
|
|
7348
|
+
rails = "both";
|
|
7349
|
+
} else if (hasX402Face(faces) && rails !== "both") {
|
|
7350
|
+
rails = "b402";
|
|
7351
|
+
}
|
|
7352
|
+
if ((rails === "b402" || rails === "both") && !hasX402Face(faces)) {
|
|
7353
|
+
faces = normalizeProtocolFaces([...faces, "X402"]);
|
|
7354
|
+
}
|
|
7355
|
+
if (opts.erc8183Price !== void 0 && rails === "b402") {
|
|
7356
|
+
printErr(
|
|
7357
|
+
"error: --erc8183-price requires the 8183 rail; use --rails 8183 or --rails both."
|
|
7358
|
+
);
|
|
7359
|
+
return 2;
|
|
7360
|
+
}
|
|
7361
|
+
if (opts.b402Price !== void 0 && rails === "8183") {
|
|
7362
|
+
printErr(
|
|
7363
|
+
"error: --b402-price requires the b402 rail; use --rails b402 or --rails both."
|
|
7364
|
+
);
|
|
7365
|
+
return 2;
|
|
7366
|
+
}
|
|
7367
|
+
let erc8183Price = DEFAULT_ERC8183_PRICE;
|
|
7368
|
+
if (rails === "8183" || rails === "both") {
|
|
7369
|
+
try {
|
|
7370
|
+
erc8183Price = await resolveErc8183Price(opts.erc8183Price, isTty);
|
|
7371
|
+
} catch (exc) {
|
|
7372
|
+
printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
|
|
7373
|
+
return 2;
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
let b402Price = DEFAULT_B402_PRICE_USD;
|
|
7377
|
+
if (rails === "b402" || rails === "both") {
|
|
7378
|
+
try {
|
|
7379
|
+
b402Price = await resolveB402Price(opts.b402Price, isTty);
|
|
7380
|
+
} catch (exc) {
|
|
7381
|
+
printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
|
|
7382
|
+
return 2;
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7102
7385
|
const enclosing = enclosingProjectRoot(path21.dirname(target));
|
|
7103
7386
|
if (enclosing !== null) {
|
|
7104
7387
|
printErr(
|
|
@@ -7196,16 +7479,7 @@ async function cmdInit(nameArg, opts) {
|
|
|
7196
7479
|
}
|
|
7197
7480
|
const budgetFlag = opts.enableAutoTopup ? "enable" : opts.autoTopup === false ? "disable" : null;
|
|
7198
7481
|
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)) {
|
|
7482
|
+
if ((rails === "b402" || rails === "both") && x402SellerPricingState({ price_usd: b402Price }).kind === "paid" && !["evm-local", "twak"].includes(walletKind2)) {
|
|
7209
7483
|
printErr(
|
|
7210
7484
|
`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
7485
|
);
|
|
@@ -7220,6 +7494,8 @@ async function cmdInit(nameArg, opts) {
|
|
|
7220
7494
|
runtime,
|
|
7221
7495
|
faces,
|
|
7222
7496
|
rails,
|
|
7497
|
+
erc8183Price,
|
|
7498
|
+
b402Price,
|
|
7223
7499
|
budgetChoice,
|
|
7224
7500
|
ides,
|
|
7225
7501
|
storageProvider: opts.storageProvider,
|
|
@@ -7318,8 +7594,9 @@ async function cmdInit(nameArg, opts) {
|
|
|
7318
7594
|
"info: X402-only publishes only the /x402 gateway face; the native AgentCore A2A protocol declaration is suppressed from discovery."
|
|
7319
7595
|
);
|
|
7320
7596
|
} else if (faces.length === 1 && hasX402Face(faces) && destination === "self") {
|
|
7597
|
+
const free = x402SellerPricingState({ price_usd: b402Price }).kind === "free";
|
|
7321
7598
|
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."
|
|
7599
|
+
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
7600
|
);
|
|
7324
7601
|
}
|
|
7325
7602
|
printConfigSummary(agentDir);
|
|
@@ -7379,9 +7656,10 @@ function derivePackaging(walletKind2, destination, platformArtifact2 = "zip", ru
|
|
|
7379
7656
|
return "codezip";
|
|
7380
7657
|
}
|
|
7381
7658
|
function scaffold(target, name, o) {
|
|
7659
|
+
const packageManagerVersion = pnpmVersion();
|
|
7382
7660
|
fs22.writeFileSync(
|
|
7383
7661
|
path21.join(target, "package.json"),
|
|
7384
|
-
renderWorkspacePackageJson(name)
|
|
7662
|
+
renderWorkspacePackageJson(name, packageManagerVersion)
|
|
7385
7663
|
);
|
|
7386
7664
|
fs22.writeFileSync(
|
|
7387
7665
|
path21.join(target, "pnpm-workspace.yaml"),
|
|
@@ -7438,7 +7716,14 @@ function scaffold(target, name, o) {
|
|
|
7438
7716
|
fs22.mkdirSync(path21.dirname(envLocal), { recursive: true });
|
|
7439
7717
|
fs22.writeFileSync(
|
|
7440
7718
|
envLocal,
|
|
7441
|
-
renderAgentEnvLocal(
|
|
7719
|
+
renderAgentEnvLocal(
|
|
7720
|
+
o.provider,
|
|
7721
|
+
o.storageProvider,
|
|
7722
|
+
o.walletKind,
|
|
7723
|
+
o.rails,
|
|
7724
|
+
o.b402Price,
|
|
7725
|
+
o.network
|
|
7726
|
+
)
|
|
7442
7727
|
);
|
|
7443
7728
|
fs22.writeFileSync(path21.join(agentRoot2, ".gitignore"), renderAgentGitignore());
|
|
7444
7729
|
fs22.writeFileSync(
|
|
@@ -7458,7 +7743,7 @@ function scaffold(target, name, o) {
|
|
|
7458
7743
|
const runtimeCtx = {
|
|
7459
7744
|
PKG: AGENT_SRC,
|
|
7460
7745
|
TWAK_CLI_VERSION,
|
|
7461
|
-
PNPM_VERSION:
|
|
7746
|
+
PNPM_VERSION: packageManagerVersion,
|
|
7462
7747
|
DEPLOYMENT_TYPE: isContainer ? "container" : "direct_code_deploy",
|
|
7463
7748
|
ENTRYPOINT: o.runtime === "azure-foundry" ? a2aEntry : `dist/${stem2}.js`,
|
|
7464
7749
|
// Foundry Hosted Agents have a provider-level container contract that is
|
|
@@ -7547,11 +7832,12 @@ ${deployLine}
|
|
|
7547
7832
|
In Claude Code / Cursor, type \`/bnbagent-studio\` \u2014 the skill drives every step.
|
|
7548
7833
|
`;
|
|
7549
7834
|
}
|
|
7550
|
-
function renderWorkspacePackageJson(name) {
|
|
7835
|
+
function renderWorkspacePackageJson(name, packageManagerVersion) {
|
|
7551
7836
|
return `${JSON.stringify(
|
|
7552
7837
|
{
|
|
7553
7838
|
name: `${name}-workspace`,
|
|
7554
|
-
private: true
|
|
7839
|
+
private: true,
|
|
7840
|
+
packageManager: `pnpm@${packageManagerVersion}`
|
|
7555
7841
|
// NOT a publishable package — the agent lives at app/agent (see
|
|
7556
7842
|
// pnpm-workspace.yaml); this root only anchors the pnpm workspace.
|
|
7557
7843
|
},
|
|
@@ -7755,14 +8041,14 @@ expected_recipient = ""
|
|
|
7755
8041
|
allowed_hosts = ["llm.pieverse.io"]
|
|
7756
8042
|
`;
|
|
7757
8043
|
}
|
|
7758
|
-
function renderX402SellerSection() {
|
|
8044
|
+
function renderX402SellerSection(priceUsd) {
|
|
7759
8045
|
return `
|
|
7760
8046
|
[payments.x402_seller]
|
|
7761
8047
|
# Public seller rail mounted at /x402. Credentials stay in
|
|
7762
8048
|
# <workspace>/.studio/.env.local and are issued per B402 environment.
|
|
7763
8049
|
enabled = true
|
|
7764
|
-
# Decimal USD string; "0"
|
|
7765
|
-
price_usd = "
|
|
8050
|
+
# Decimal USD string; "0" is explicit FREE passthrough and bypasses B402.
|
|
8051
|
+
price_usd = "${priceUsd}"
|
|
7766
8052
|
# v1 supports United Stables (U) through EIP-3009 only.
|
|
7767
8053
|
assets = ["U"]
|
|
7768
8054
|
# Empty means the agent wallet receives payment.
|
|
@@ -7817,7 +8103,7 @@ function renderAgentStudioToml(name, o) {
|
|
|
7817
8103
|
const model = o.model ?? PROVIDER_DEFAULT_MODEL[o.provider] ?? "";
|
|
7818
8104
|
const pieverseSection = o.provider === "pieverse-llm" ? renderPieverseSections() : "";
|
|
7819
8105
|
const x402Section = o.provider === "pieverse-llm" ? renderX402Section() : "";
|
|
7820
|
-
const x402SellerSection = o.rails === "b402" || o.rails === "both" || hasX402Face(o.faces) ? renderX402SellerSection() : "";
|
|
8106
|
+
const x402SellerSection = o.rails === "b402" || o.rails === "both" || hasX402Face(o.faces) ? renderX402SellerSection(o.b402Price) : "";
|
|
7821
8107
|
const budgetSection = renderBudgetSection(o.budgetChoice);
|
|
7822
8108
|
const storageSection = renderAgentStorageSection(o.storageProvider);
|
|
7823
8109
|
const azureSection = o.runtime === "azure-foundry" ? renderAzureSection(name, o.azureAccount, o.azureProject) : "";
|
|
@@ -7828,9 +8114,10 @@ function renderAgentStudioToml(name, o) {
|
|
|
7828
8114
|
[payments.erc8183]
|
|
7829
8115
|
# Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
|
|
7830
8116
|
# freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
|
|
7831
|
-
# LLM never prices.
|
|
8117
|
+
# LLM never prices. price=0 is FREE and needs a zero-price-compatible
|
|
8118
|
+
# ERC-8183 stack selected with all three ERC8183_*_ADDRESS overrides.
|
|
7832
8119
|
currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default
|
|
7833
|
-
price = "
|
|
8120
|
+
price = "${o.erc8183Price}" # token base units; 0 = FREE (zero token escrow)
|
|
7834
8121
|
min_price = "0" # wei \u2014 clamp floor
|
|
7835
8122
|
max_price = "" # wei \u2014 clamp ceiling; SET before going live
|
|
7836
8123
|
quote_ttl_seconds = 900 # seconds a signed quote stays valid; SDK caps at 900 (~15min work window)
|
|
@@ -7868,7 +8155,7 @@ ${pieverseSection}${erc8183Section}
|
|
|
7868
8155
|
${storageSection}
|
|
7869
8156
|
${x402Section}${x402SellerSection}${budgetSection}${azureSection}`;
|
|
7870
8157
|
}
|
|
7871
|
-
function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails) {
|
|
8158
|
+
function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402Price, network) {
|
|
7872
8159
|
const keyEnv = PROVIDER_KEY_ENV[provider];
|
|
7873
8160
|
const lines = [
|
|
7874
8161
|
"# \u26A0\uFE0F SECRETS \u2014 do NOT paste this file into issue trackers, logs, or AI chats.",
|
|
@@ -7931,17 +8218,30 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails) {
|
|
|
7931
8218
|
);
|
|
7932
8219
|
}
|
|
7933
8220
|
if (rails === "b402" || rails === "both") {
|
|
8221
|
+
const free = x402SellerPricingState({ price_usd: b402Price }).kind === "free";
|
|
8222
|
+
const baseUrl = !free && network === "bsc-testnet" ? DEFAULT_B402_TESTNET_BASE_URL : "";
|
|
7934
8223
|
lines.push(
|
|
7935
8224
|
"",
|
|
7936
8225
|
"# B402 merchant credentials (issued per environment after manual approval).",
|
|
7937
|
-
"#
|
|
7938
|
-
|
|
8226
|
+
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.",
|
|
8227
|
+
`B402_BASE_URL=${baseUrl}`,
|
|
7939
8228
|
"B402_CLIENT_ID=",
|
|
7940
8229
|
"B402_ACCESS_TOKEN=",
|
|
7941
8230
|
"B402_PRIVATE_KEY=",
|
|
7942
8231
|
"# (alternative one-line PKCS#8 DER: B402_PRIVATE_KEY_B64=)"
|
|
7943
8232
|
);
|
|
7944
8233
|
}
|
|
8234
|
+
if (rails === "8183" || rails === "both") {
|
|
8235
|
+
lines.push(
|
|
8236
|
+
"",
|
|
8237
|
+
"# Optional ERC-8183 custom contract-stack override. Set all three",
|
|
8238
|
+
"# together; partial overrides can mix incompatible deployments.",
|
|
8239
|
+
"# Required for price=0 while canonical contracts reject zero funding.",
|
|
8240
|
+
"# ERC8183_COMMERCE_ADDRESS=",
|
|
8241
|
+
"# ERC8183_ROUTER_ADDRESS=",
|
|
8242
|
+
"# ERC8183_POLICY_ADDRESS="
|
|
8243
|
+
);
|
|
8244
|
+
}
|
|
7945
8245
|
return `${lines.join("\n")}
|
|
7946
8246
|
`;
|
|
7947
8247
|
}
|
|
@@ -8183,6 +8483,69 @@ async function resolveRails(flagValue, isTty) {
|
|
|
8183
8483
|
}
|
|
8184
8484
|
return "8183";
|
|
8185
8485
|
}
|
|
8486
|
+
var DEFAULT_ERC8183_PRICE = "100000000000000000";
|
|
8487
|
+
async function resolveErc8183Price(flagValue, isTty) {
|
|
8488
|
+
if (flagValue !== void 0) {
|
|
8489
|
+
const value = flagValue.trim();
|
|
8490
|
+
if (!/^\d+$/.test(value)) {
|
|
8491
|
+
throw new Error(
|
|
8492
|
+
`--erc8183-price must be a non-negative integer string in token base units; got ${JSON.stringify(flagValue)}.`
|
|
8493
|
+
);
|
|
8494
|
+
}
|
|
8495
|
+
return value;
|
|
8496
|
+
}
|
|
8497
|
+
if (!isTty) {
|
|
8498
|
+
return DEFAULT_ERC8183_PRICE;
|
|
8499
|
+
}
|
|
8500
|
+
const choice = (await promptUser(
|
|
8501
|
+
"ERC-8183 pricing [paid/free/custom, Enter = paid (0.1 U)]: "
|
|
8502
|
+
)).trim().toLowerCase();
|
|
8503
|
+
if (choice === "free") {
|
|
8504
|
+
return "0";
|
|
8505
|
+
}
|
|
8506
|
+
if (choice === "custom") {
|
|
8507
|
+
const custom = (await promptUser(
|
|
8508
|
+
"ERC-8183 list price in token base units (non-negative integer): "
|
|
8509
|
+
)).trim();
|
|
8510
|
+
if (!/^\d+$/.test(custom)) {
|
|
8511
|
+
throw new Error(
|
|
8512
|
+
`custom ERC-8183 price must be a non-negative integer string; got ${JSON.stringify(custom)}.`
|
|
8513
|
+
);
|
|
8514
|
+
}
|
|
8515
|
+
return custom;
|
|
8516
|
+
}
|
|
8517
|
+
if (choice !== "" && choice !== "paid") {
|
|
8518
|
+
printErr(`hint: unknown ERC-8183 pricing choice '${choice}' \u2014 using paid.`);
|
|
8519
|
+
}
|
|
8520
|
+
return DEFAULT_ERC8183_PRICE;
|
|
8521
|
+
}
|
|
8522
|
+
async function resolveB402Price(flagValue, isTty) {
|
|
8523
|
+
if (flagValue !== void 0) {
|
|
8524
|
+
return normalizeB402PriceUsd(flagValue);
|
|
8525
|
+
}
|
|
8526
|
+
if (!isTty) {
|
|
8527
|
+
return DEFAULT_B402_PRICE_USD;
|
|
8528
|
+
}
|
|
8529
|
+
const choice = (await promptUser(
|
|
8530
|
+
"B402/x402 pricing [paid/free/custom, Enter = paid ($0.01)]: "
|
|
8531
|
+
)).trim().toLowerCase();
|
|
8532
|
+
if (choice === "free") {
|
|
8533
|
+
return "0";
|
|
8534
|
+
}
|
|
8535
|
+
if (choice === "custom") {
|
|
8536
|
+
return normalizeB402PriceUsd(
|
|
8537
|
+
await promptUser(
|
|
8538
|
+
"B402/x402 per-request price in USD (non-negative decimal): "
|
|
8539
|
+
)
|
|
8540
|
+
);
|
|
8541
|
+
}
|
|
8542
|
+
if (choice !== "" && choice !== "paid") {
|
|
8543
|
+
printErr(
|
|
8544
|
+
`hint: unknown B402/x402 pricing choice '${choice}' \u2014 using paid.`
|
|
8545
|
+
);
|
|
8546
|
+
}
|
|
8547
|
+
return DEFAULT_B402_PRICE_USD;
|
|
8548
|
+
}
|
|
8186
8549
|
async function promptPassword() {
|
|
8187
8550
|
printOut("");
|
|
8188
8551
|
printOut("Onboarding (TTY) \u2014 set a wallet password to finish in one go.");
|
|
@@ -8940,10 +9303,10 @@ function printNextSteps(name, provider, o = {}) {
|
|
|
8940
9303
|
nextStep = 4;
|
|
8941
9304
|
}
|
|
8942
9305
|
printOut(
|
|
8943
|
-
` # ${nextStep}.
|
|
9306
|
+
` # ${nextStep}. review ERC-8183 pricing in studio.toml [payments.erc8183]`
|
|
8944
9307
|
);
|
|
8945
9308
|
printOut(
|
|
8946
|
-
" # (
|
|
9309
|
+
" # (price was selected at init; `bag config set payments.erc8183.price 0` explicitly switches to FREE)"
|
|
8947
9310
|
);
|
|
8948
9311
|
nextStep += 1;
|
|
8949
9312
|
printOut(` # ${nextStep}. verify + run the agent locally`);
|
|
@@ -9011,13 +9374,26 @@ function printConfigSummary(agentRoot2) {
|
|
|
9011
9374
|
const shown = (v, unset = "<unset \u2014 fill before going live>") => v === void 0 || v === null || v === "" ? unset : String(v);
|
|
9012
9375
|
const table = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
9013
9376
|
const faces = stackFaces(table(agent.stack), table(agent.payments));
|
|
9377
|
+
const payments = table(agent.payments);
|
|
9378
|
+
const erc8183 = table(payments.erc8183);
|
|
9379
|
+
const x402Seller = table(payments.x402_seller);
|
|
9380
|
+
const hasErc8183 = Object.keys(erc8183).length > 0;
|
|
9381
|
+
const hasX402Seller = x402Seller.enabled === true;
|
|
9382
|
+
const x402Pricing = x402SellerPricingState(x402Seller);
|
|
9383
|
+
const isFreeX402 = hasX402Seller && x402Pricing.kind === "free";
|
|
9384
|
+
const onlyX402 = hasX402Seller && !hasErc8183;
|
|
9014
9385
|
const network = shown(g(agent, "network", "default"), "?");
|
|
9015
9386
|
const provider = shown(g(agent, "llm", "provider"), "?");
|
|
9016
9387
|
const model = shown(g(agent, "llm", "model"), "?");
|
|
9017
9388
|
const modelTag = isFreeModel(String(model)) ? "Free, $0/token" : "paid \u2014 fund + topup to use";
|
|
9018
9389
|
const currency = g(agent, "payments", "erc8183", "currency");
|
|
9019
|
-
const
|
|
9390
|
+
const priceValue = g(agent, "payments", "erc8183", "price");
|
|
9391
|
+
const price = shown(priceValue);
|
|
9392
|
+
const minPrice = shown(g(agent, "payments", "erc8183", "min_price"), "0");
|
|
9020
9393
|
const maxPrice = g(agent, "payments", "erc8183", "max_price");
|
|
9394
|
+
const pricingState = erc8183PricingState(erc8183);
|
|
9395
|
+
const isFreePrice = pricingState.kind === "free";
|
|
9396
|
+
const contractOverrides = erc8183ContractOverrideState();
|
|
9021
9397
|
const storageKind2 = shown(g(agent, "storage", "kind"), "?");
|
|
9022
9398
|
const storageDesc = storageKind2 === "ipfs" ? "IPFS (durable, public)" : "local disk (offline dev only)";
|
|
9023
9399
|
const walletKind2 = shown(g(agent, "wallet", "kind"), "evm-local");
|
|
@@ -9030,16 +9406,41 @@ function printConfigSummary(agentRoot2) {
|
|
|
9030
9406
|
printOut("Your agent's configuration (studio.toml, in plain language)");
|
|
9031
9407
|
printOut(bar);
|
|
9032
9408
|
printOut("");
|
|
9033
|
-
printOut("Per task \u2014 how this seller earns $U:");
|
|
9034
9409
|
printOut(
|
|
9035
|
-
"
|
|
9036
|
-
);
|
|
9037
|
-
printOut(
|
|
9038
|
-
" on payment the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
|
|
9039
|
-
);
|
|
9040
|
-
printOut(
|
|
9041
|
-
" after the dispute window, SETTLE releases the $U to your wallet."
|
|
9410
|
+
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
9411
|
);
|
|
9412
|
+
if (onlyX402 && isFreeX402) {
|
|
9413
|
+
printOut(
|
|
9414
|
+
" anonymous request reaches /x402 \u2192 B402 verify/settle is bypassed \u2192"
|
|
9415
|
+
);
|
|
9416
|
+
printOut(
|
|
9417
|
+
" the Agent runs the work and returns the result with no payment."
|
|
9418
|
+
);
|
|
9419
|
+
} else if (onlyX402) {
|
|
9420
|
+
printOut(
|
|
9421
|
+
" anonymous request receives a 402 challenge \u2192 B402 verifies and settles $U \u2192"
|
|
9422
|
+
);
|
|
9423
|
+
printOut(" the Agent runs the work and returns the paid result.");
|
|
9424
|
+
} else {
|
|
9425
|
+
printOut(
|
|
9426
|
+
" buyer negotiates \u2192 the Agent (sole signer) QUOTES a signed price \u2192"
|
|
9427
|
+
);
|
|
9428
|
+
if (isFreePrice) {
|
|
9429
|
+
printOut(
|
|
9430
|
+
" buyer funds 0 token units \u2192 the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
|
|
9431
|
+
);
|
|
9432
|
+
printOut(
|
|
9433
|
+
" after the dispute window, SETTLE completes the job with no token payout."
|
|
9434
|
+
);
|
|
9435
|
+
} else {
|
|
9436
|
+
printOut(
|
|
9437
|
+
" on payment the Agent FULFILLS (runs the LLM, stores the deliverable) \u2192"
|
|
9438
|
+
);
|
|
9439
|
+
printOut(
|
|
9440
|
+
" after the dispute window, SETTLE releases the $U to your wallet."
|
|
9441
|
+
);
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9043
9444
|
printOut("");
|
|
9044
9445
|
printOut("Agent (the value + the ONLY key-holder/signer)");
|
|
9045
9446
|
printOut(` project : ${shown(g(agent, "project", "name"), "?")}`);
|
|
@@ -9086,19 +9487,35 @@ function printConfigSummary(agentRoot2) {
|
|
|
9086
9487
|
const ar = autoRenew ? "on \u2014 tops the API key up from your Pieverse balance (no wallet spend)" : "off";
|
|
9087
9488
|
printOut(` auto-alloc: ${ar}`);
|
|
9088
9489
|
}
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9490
|
+
if (hasErc8183) {
|
|
9491
|
+
if (isFreePrice) {
|
|
9492
|
+
printOut(
|
|
9493
|
+
` pricing : FREE \u2014 ${weiToU(String(price))} per job; zero token escrow`
|
|
9494
|
+
);
|
|
9495
|
+
printOut(
|
|
9496
|
+
contractOverrides.mode === "custom" ? " contracts: custom stack selected (all three address overrides)" : " contracts: configure all three ERC8183_*_ADDRESS overrides before deploy"
|
|
9497
|
+
);
|
|
9498
|
+
} else {
|
|
9499
|
+
printOut(
|
|
9500
|
+
` pricing : list price ${weiToU(String(price))} per job; clamp ceiling ${shown(maxPrice)}`
|
|
9501
|
+
);
|
|
9502
|
+
}
|
|
9503
|
+
printOut(
|
|
9504
|
+
` quote frozen ${shown(
|
|
9505
|
+
g(agent, "payments", "erc8183", "quote_ttl_seconds"),
|
|
9506
|
+
"?"
|
|
9507
|
+
)}s; job timeout ${shown(
|
|
9508
|
+
g(agent, "payments", "erc8183", "default_estimated_completion_seconds"),
|
|
9509
|
+
"?"
|
|
9510
|
+
)}s`
|
|
9511
|
+
);
|
|
9512
|
+
printOut(` currency ($U token): ${shown(currency)}`);
|
|
9513
|
+
}
|
|
9514
|
+
if (hasX402Seller) {
|
|
9515
|
+
printOut(
|
|
9516
|
+
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`
|
|
9517
|
+
);
|
|
9518
|
+
}
|
|
9102
9519
|
printOut(
|
|
9103
9520
|
` storage : ${storageDesc} (options: local | ipfs \u2014 ipfs required to deploy)`
|
|
9104
9521
|
);
|
|
@@ -9108,12 +9525,22 @@ function printConfigSummary(agentRoot2) {
|
|
|
9108
9525
|
}
|
|
9109
9526
|
printOut("");
|
|
9110
9527
|
const todo = [];
|
|
9111
|
-
if (!currency) {
|
|
9528
|
+
if (hasErc8183 && !currency) {
|
|
9112
9529
|
todo.push("[payments.erc8183].currency \u2014 the $U token address");
|
|
9113
9530
|
}
|
|
9114
|
-
if (!maxPrice) {
|
|
9531
|
+
if (hasErc8183 && !maxPrice && !isFreePrice) {
|
|
9115
9532
|
todo.push("[payments.erc8183].max_price \u2014 the price clamp ceiling");
|
|
9116
9533
|
}
|
|
9534
|
+
if (hasErc8183 && isFreePrice && contractOverrides.mode !== "custom") {
|
|
9535
|
+
todo.push(
|
|
9536
|
+
"ERC8183_COMMERCE_ADDRESS / ROUTER_ADDRESS / POLICY_ADDRESS \u2014 select one zero-price-compatible custom contract stack"
|
|
9537
|
+
);
|
|
9538
|
+
}
|
|
9539
|
+
if (hasX402Seller && !isFreeX402) {
|
|
9540
|
+
todo.push(
|
|
9541
|
+
"B402_BASE_URL / CLIENT_ID / ACCESS_TOKEN / private key \u2014 required for PAID /x402"
|
|
9542
|
+
);
|
|
9543
|
+
}
|
|
9117
9544
|
if (storageKind2 === "local") {
|
|
9118
9545
|
todo.push(
|
|
9119
9546
|
"[storage].kind \u2014 switch to 'ipfs' + set STORAGE_API_URL/STORAGE_API_KEY before deploy (local disk is NOT deployable)"
|
|
@@ -9392,8 +9819,13 @@ function runtimeEnvKeys(agentDir) {
|
|
|
9392
9819
|
if (resolvable("RPC_URL")) {
|
|
9393
9820
|
keys.push("RPC_URL");
|
|
9394
9821
|
}
|
|
9822
|
+
if (commerceRails(cfg).erc8183) {
|
|
9823
|
+
for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
|
|
9824
|
+
if (resolvable(key)) keys.push(key);
|
|
9825
|
+
}
|
|
9826
|
+
}
|
|
9395
9827
|
const runtime = String(tableOf7(cfg, "stack").runtime ?? "agentcore");
|
|
9396
|
-
if (runtime === "agentcore" &&
|
|
9828
|
+
if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
|
|
9397
9829
|
for (const key of B402_RUNTIME_KEYS) {
|
|
9398
9830
|
if (resolvable(key)) keys.push(key);
|
|
9399
9831
|
}
|
|
@@ -9777,8 +10209,11 @@ Switch to Node \u226522 so it wins on PATH, then reopen the shell and retry. Ver
|
|
|
9777
10209
|
);
|
|
9778
10210
|
}
|
|
9779
10211
|
if (hasX402Face(faces)) {
|
|
10212
|
+
const free = x402SellerIsFree(
|
|
10213
|
+
loadStudioToml10(path22.join(agentDir, "studio.toml"))
|
|
10214
|
+
);
|
|
9780
10215
|
printOut(
|
|
9781
|
-
`bag dev: x402 seller at http://localhost:${agentPort}/x402 (active when B402 credentials are configured)`
|
|
10216
|
+
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
10217
|
);
|
|
9783
10218
|
}
|
|
9784
10219
|
await launch();
|
|
@@ -9885,8 +10320,13 @@ function runtimeEnvKeys2(agentRoot2) {
|
|
|
9885
10320
|
if (resolvable("RPC_URL")) {
|
|
9886
10321
|
keys.push("RPC_URL");
|
|
9887
10322
|
}
|
|
10323
|
+
if (commerceRails(cfg).erc8183) {
|
|
10324
|
+
for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
|
|
10325
|
+
if (resolvable(key)) keys.push(key);
|
|
10326
|
+
}
|
|
10327
|
+
}
|
|
9888
10328
|
const runtime = String(tableOf2(cfg, "stack").runtime ?? "agentcore");
|
|
9889
|
-
if (runtime === "agentcore" &&
|
|
10329
|
+
if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
|
|
9890
10330
|
for (const key of B402_RUNTIME_KEYS) {
|
|
9891
10331
|
if (resolvable(key)) keys.push(key);
|
|
9892
10332
|
}
|
|
@@ -10226,51 +10666,76 @@ async function checkPieverseKeyHash(root, _target) {
|
|
|
10226
10666
|
}
|
|
10227
10667
|
];
|
|
10228
10668
|
}
|
|
10229
|
-
var MIN_PRICE_WEI = 1000000000n;
|
|
10230
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
10231
10669
|
function erc8183RailChecks(cfg) {
|
|
10232
10670
|
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
10671
|
const out = [];
|
|
10245
|
-
const
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10672
|
+
const contracts = erc8183ContractOverrideState();
|
|
10673
|
+
if (contracts.mode === "partial") {
|
|
10674
|
+
out.push({
|
|
10675
|
+
level: Level.CRITICAL,
|
|
10676
|
+
name: "commerce_contract_override_incomplete",
|
|
10677
|
+
message: `The ERC-8183 contract override is incomplete. Set ${contracts.missing.join(", ")} so commerce, router, and policy come from one compatible contract stack.`,
|
|
10678
|
+
details: {
|
|
10679
|
+
present: contracts.present,
|
|
10680
|
+
missing: contracts.missing
|
|
10681
|
+
}
|
|
10682
|
+
});
|
|
10683
|
+
} else if (contracts.mode === "invalid") {
|
|
10684
|
+
out.push({
|
|
10685
|
+
level: Level.CRITICAL,
|
|
10686
|
+
name: "commerce_contract_override_invalid",
|
|
10687
|
+
message: `The following ERC-8183 contract overrides are not valid EVM addresses: ${contracts.invalid.join(", ")}.`,
|
|
10688
|
+
details: { invalid: contracts.invalid }
|
|
10689
|
+
});
|
|
10690
|
+
}
|
|
10691
|
+
const pricing = erc8183PricingState(c2);
|
|
10692
|
+
if (pricing.kind === "unset") {
|
|
10693
|
+
out.push({
|
|
10694
|
+
level: Level.CRITICAL,
|
|
10695
|
+
name: "commerce_price_unset",
|
|
10696
|
+
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.",
|
|
10697
|
+
fixCmd: "bag config set payments.erc8183.price 100000000000000000",
|
|
10698
|
+
details: {}
|
|
10699
|
+
});
|
|
10700
|
+
} else if (pricing.kind === "invalid") {
|
|
10261
10701
|
out.push({
|
|
10262
10702
|
level: Level.CRITICAL,
|
|
10263
|
-
name: "
|
|
10264
|
-
message:
|
|
10265
|
-
fixCmd: "bag config set payments.erc8183.price
|
|
10703
|
+
name: "commerce_price_invalid",
|
|
10704
|
+
message: `[payments.erc8183].${pricing.field} must be a non-negative uint256 decimal string; got ${JSON.stringify(pricing.value)}.`,
|
|
10705
|
+
fixCmd: "bag config set payments.erc8183.price 100000000000000000",
|
|
10266
10706
|
details: {
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
min_price_wei: String(minPrice),
|
|
10270
|
-
max_price_wei: String(maxPrice),
|
|
10271
|
-
min_required_wei: String(MIN_PRICE_WEI)
|
|
10707
|
+
configured_field: pricing.field,
|
|
10708
|
+
configured_value: pricing.value
|
|
10272
10709
|
}
|
|
10273
10710
|
});
|
|
10711
|
+
} else if (pricing.kind === "clamped_to_zero") {
|
|
10712
|
+
out.push({
|
|
10713
|
+
level: Level.CRITICAL,
|
|
10714
|
+
name: "commerce_price_clamped_to_zero",
|
|
10715
|
+
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.`,
|
|
10716
|
+
details: {
|
|
10717
|
+
effective_wei: "0",
|
|
10718
|
+
list_price_wei: String(pricing.listPrice),
|
|
10719
|
+
min_price_wei: String(pricing.minPrice),
|
|
10720
|
+
max_price_wei: String(pricing.maxPrice)
|
|
10721
|
+
}
|
|
10722
|
+
});
|
|
10723
|
+
} else if (pricing.kind === "free") {
|
|
10724
|
+
if (contracts.mode === "canonical") {
|
|
10725
|
+
out.push({
|
|
10726
|
+
level: Level.CRITICAL,
|
|
10727
|
+
name: "commerce_zero_price_contract_unsupported",
|
|
10728
|
+
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 custom stack.",
|
|
10729
|
+
details: { effective_wei: "0", contract_profile: "canonical" }
|
|
10730
|
+
});
|
|
10731
|
+
} else if (contracts.mode === "custom") {
|
|
10732
|
+
out.push({
|
|
10733
|
+
level: Level.INFO,
|
|
10734
|
+
name: "commerce_zero_price_enabled",
|
|
10735
|
+
message: "ERC-8183 pricing is FREE \u2014 buyers fund 0 token units with zero token escrow; a complete custom contract stack is selected.",
|
|
10736
|
+
details: { effective_wei: "0", contract_profile: "custom" }
|
|
10737
|
+
});
|
|
10738
|
+
}
|
|
10274
10739
|
}
|
|
10275
10740
|
if (String(c2.currency ?? "").trim() === "") {
|
|
10276
10741
|
out.push({
|
|
@@ -10306,15 +10771,24 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10306
10771
|
const rails = commerceRails(cfg);
|
|
10307
10772
|
const credentials = b402Credentials(agentRoot2);
|
|
10308
10773
|
const out = [];
|
|
10774
|
+
const pricing = x402SellerPricingState(seller);
|
|
10775
|
+
const free = rails.x402 && pricing.kind === "free";
|
|
10776
|
+
const paid = rails.x402 && pricing.kind === "paid";
|
|
10309
10777
|
if (sellerPresent) {
|
|
10310
|
-
|
|
10311
|
-
if (!/^(?:0|[1-9]\d*)(?:\.\d+)?$/.test(price)) {
|
|
10778
|
+
if (pricing.kind === "invalid") {
|
|
10312
10779
|
out.push({
|
|
10313
10780
|
level: Level.CRITICAL,
|
|
10314
10781
|
name: "x402_price_invalid",
|
|
10315
10782
|
message: '[payments.x402_seller].price_usd must be a non-negative decimal string; "0" is valid and enables the free passthrough.',
|
|
10316
10783
|
details: {}
|
|
10317
10784
|
});
|
|
10785
|
+
} else if (free) {
|
|
10786
|
+
out.push({
|
|
10787
|
+
level: Level.INFO,
|
|
10788
|
+
name: "x402_zero_price_enabled",
|
|
10789
|
+
message: "The x402 seller is explicitly FREE. /x402 is anonymous, and B402 verify/settle, token payment, and settlement audit are bypassed.",
|
|
10790
|
+
details: { price_usd: pricing.priceUsd, facilitator_bypassed: true }
|
|
10791
|
+
});
|
|
10318
10792
|
}
|
|
10319
10793
|
const assets = seller.assets ?? ["U"];
|
|
10320
10794
|
if (!Array.isArray(assets) || assets.length !== 1 || assets[0] !== "U") {
|
|
@@ -10336,7 +10810,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10336
10810
|
}
|
|
10337
10811
|
}
|
|
10338
10812
|
const walletKind2 = String(tableOf2(cfg, "wallet").kind ?? "evm-local");
|
|
10339
|
-
if (
|
|
10813
|
+
if (paid && !["evm-local", "twak"].includes(walletKind2)) {
|
|
10340
10814
|
out.push({
|
|
10341
10815
|
level: Level.CRITICAL,
|
|
10342
10816
|
name: "x402_wallet_kind_unsupported",
|
|
@@ -10344,7 +10818,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10344
10818
|
details: { walletKind: walletKind2 }
|
|
10345
10819
|
});
|
|
10346
10820
|
}
|
|
10347
|
-
if (credentials.any && !credentials.complete) {
|
|
10821
|
+
if (paid && credentials.any && !credentials.complete) {
|
|
10348
10822
|
out.push({
|
|
10349
10823
|
level: Level.CRITICAL,
|
|
10350
10824
|
name: "x402_credentials_partial",
|
|
@@ -10352,7 +10826,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10352
10826
|
details: { present: credentials.presentKeys }
|
|
10353
10827
|
});
|
|
10354
10828
|
}
|
|
10355
|
-
if (credentials.bothPrivateKeyFormats) {
|
|
10829
|
+
if (paid && credentials.bothPrivateKeyFormats) {
|
|
10356
10830
|
out.push({
|
|
10357
10831
|
level: Level.WARNING,
|
|
10358
10832
|
name: "x402_private_key_ambiguous",
|
|
@@ -10360,7 +10834,7 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10360
10834
|
details: {}
|
|
10361
10835
|
});
|
|
10362
10836
|
}
|
|
10363
|
-
if (
|
|
10837
|
+
if (paid && !credentials.any) {
|
|
10364
10838
|
out.push({
|
|
10365
10839
|
level: rails.erc8183 ? Level.WARNING : Level.CRITICAL,
|
|
10366
10840
|
name: "x402_credentials_missing",
|
|
@@ -10368,19 +10842,19 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10368
10842
|
details: {}
|
|
10369
10843
|
});
|
|
10370
10844
|
}
|
|
10371
|
-
if (!rails.x402 && credentials.any) {
|
|
10845
|
+
if ((!rails.x402 || free) && credentials.any) {
|
|
10372
10846
|
out.push({
|
|
10373
10847
|
level: Level.WARNING,
|
|
10374
10848
|
name: "x402_credentials_unused",
|
|
10375
|
-
message: "B402 credentials are configured but the x402 seller rail is absent or disabled; they will not be synchronized.",
|
|
10849
|
+
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
10850
|
details: { present: credentials.presentKeys }
|
|
10377
10851
|
});
|
|
10378
10852
|
}
|
|
10379
|
-
if (credentials.baseUrl !== null) {
|
|
10853
|
+
if (paid && credentials.baseUrl !== null) {
|
|
10380
10854
|
const network = String(
|
|
10381
10855
|
tableOf2(cfg, "network").default ?? "bsc-mainnet"
|
|
10382
10856
|
).toLowerCase();
|
|
10383
|
-
const testUrl =
|
|
10857
|
+
const testUrl = isB402TestnetBaseUrl(credentials.baseUrl);
|
|
10384
10858
|
const testNetwork = network.includes("testnet");
|
|
10385
10859
|
if (testUrl !== testNetwork) {
|
|
10386
10860
|
out.push({
|
|
@@ -10393,12 +10867,12 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
|
|
|
10393
10867
|
}
|
|
10394
10868
|
const destination = String(tableOf2(cfg, "deploy").destination ?? "self");
|
|
10395
10869
|
const stackRuntime2 = String(tableOf2(cfg, "stack").runtime ?? "agentcore");
|
|
10396
|
-
if (rails.x402 && credentials.complete && destination !== "platform" && (target !== "agentcore" || stackRuntime2 !== "agentcore")) {
|
|
10870
|
+
if (rails.x402 && (free || credentials.complete) && destination !== "platform" && (target !== "agentcore" || stackRuntime2 !== "agentcore")) {
|
|
10397
10871
|
const blocking = target !== "agentcore" ? target : stackRuntime2;
|
|
10398
10872
|
out.push({
|
|
10399
10873
|
level: Level.WARNING,
|
|
10400
10874
|
name: "x402_forced_dormant_runtime",
|
|
10401
|
-
message: `x402
|
|
10875
|
+
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
10876
|
details: { destination, target, stackRuntime: stackRuntime2 }
|
|
10403
10877
|
});
|
|
10404
10878
|
}
|
|
@@ -10613,6 +11087,7 @@ var allChecks = [
|
|
|
10613
11087
|
// gates on the LEVEL, not on which module produced it.
|
|
10614
11088
|
checkTwakRequiresContainer,
|
|
10615
11089
|
checkTwakCliPresent,
|
|
11090
|
+
checkTwakCustomContractsUnsupported,
|
|
10616
11091
|
checkTwakWalletExists,
|
|
10617
11092
|
checkTwakPasswordEnvSet,
|
|
10618
11093
|
checkTwakCredentialsAvailable,
|
|
@@ -11755,14 +12230,15 @@ async function checkWalletTbnbBalanceSufficient(root, _target) {
|
|
|
11755
12230
|
return [];
|
|
11756
12231
|
}
|
|
11757
12232
|
const isTwak2 = tableOf2(data, "wallet").kind === "twak";
|
|
11758
|
-
|
|
12233
|
+
const customContracts = isTwak2 && twakCustomContractOverrides(netName).length > 0;
|
|
12234
|
+
if (isTwak2 && netName === "bsc-testnet" && !customContracts) {
|
|
11759
12235
|
return [];
|
|
11760
12236
|
}
|
|
11761
12237
|
const bal = await readNativeBalanceRaw(address, netName);
|
|
11762
12238
|
if (bal >= TBNB_MIN_WEI) {
|
|
11763
12239
|
return [];
|
|
11764
12240
|
}
|
|
11765
|
-
const gasNote = isTwak2 ? " (twak: x402 topups are gasless and ERC-8004 registry writes are gas-sponsored, but mainnet ERC-8183 fund/settle self-pay gas)" : "";
|
|
12241
|
+
const gasNote = isTwak2 ? netName === "bsc-testnet" ? " (twak: custom contract sponsorship depends on the paymaster policy; keep fallback tBNB)" : " (twak: x402 topups are gasless and ERC-8004 registry writes are gas-sponsored, but mainnet ERC-8183 fund/settle self-pay gas)" : "";
|
|
11766
12242
|
return [
|
|
11767
12243
|
{
|
|
11768
12244
|
level: Level.WARNING,
|
|
@@ -12243,6 +12719,7 @@ async function runPrepare(opts = {}) {
|
|
|
12243
12719
|
checkLocalKeystoreExists,
|
|
12244
12720
|
checkWalletPasswordEnvSet,
|
|
12245
12721
|
checkLlmProviderKeySet,
|
|
12722
|
+
checkCommerceReady,
|
|
12246
12723
|
...azureFoundryChecks
|
|
12247
12724
|
],
|
|
12248
12725
|
root,
|
|
@@ -12294,6 +12771,21 @@ var PROVIDER_LABELS = {
|
|
|
12294
12771
|
aws: "AWS AgentCore",
|
|
12295
12772
|
azure: "Azure Foundry"
|
|
12296
12773
|
};
|
|
12774
|
+
var PROVIDER_MENU_ORDER = [
|
|
12775
|
+
"bnb",
|
|
12776
|
+
"aws",
|
|
12777
|
+
"azure"
|
|
12778
|
+
];
|
|
12779
|
+
var PROVIDER_SHORT_LABELS = {
|
|
12780
|
+
bnb: "BNB",
|
|
12781
|
+
aws: "AWS",
|
|
12782
|
+
azure: "Azure"
|
|
12783
|
+
};
|
|
12784
|
+
function providerDigit(provider) {
|
|
12785
|
+
return PROVIDER_MENU_ORDER.indexOf(provider) + 1;
|
|
12786
|
+
}
|
|
12787
|
+
var ADVERTISED_PROVIDERS = ["bnb", "aws"];
|
|
12788
|
+
var ADVERTISED_PROVIDERS_HELP = ADVERTISED_PROVIDERS.join("|");
|
|
12297
12789
|
function tableOf10(data, key) {
|
|
12298
12790
|
const value = data[key];
|
|
12299
12791
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -12386,7 +12878,7 @@ function unavailableProvidersForProject(root) {
|
|
|
12386
12878
|
return {};
|
|
12387
12879
|
}
|
|
12388
12880
|
if (runtime === "azure-foundry") {
|
|
12389
|
-
const reason = "this project uses
|
|
12881
|
+
const reason = "this project uses a different container adapter; scaffold an agentcore project before selecting BNB or AWS";
|
|
12390
12882
|
return {
|
|
12391
12883
|
bnb: reason,
|
|
12392
12884
|
aws: reason,
|
|
@@ -12417,9 +12909,12 @@ function unavailableReason(opts, provider) {
|
|
|
12417
12909
|
}
|
|
12418
12910
|
return opts.unavailable?.[provider] ?? null;
|
|
12419
12911
|
}
|
|
12912
|
+
function providerPromptHint() {
|
|
12913
|
+
return PROVIDER_MENU_ORDER.filter((p) => ADVERTISED_PROVIDERS.includes(p)).map((p) => `${providerDigit(p)}=${PROVIDER_SHORT_LABELS[p]}`).join(", ");
|
|
12914
|
+
}
|
|
12420
12915
|
async function promptProvider(opts) {
|
|
12421
12916
|
for (; ; ) {
|
|
12422
|
-
const answer = (await opts.prompt(
|
|
12917
|
+
const answer = (await opts.prompt(`Provider [${providerPromptHint()}]: `)).trim().toLowerCase();
|
|
12423
12918
|
const provider = answer === "1" || answer === "bnb" ? "bnb" : answer === "2" || answer === "aws" ? "aws" : answer === "3" || answer === "azure" ? "azure" : null;
|
|
12424
12919
|
if (provider === null) {
|
|
12425
12920
|
if (answer === "" || answer === "q" || answer === "cancel") {
|
|
@@ -13315,6 +13810,8 @@ function printAccessNextSteps(root, _invokeUrl) {
|
|
|
13315
13810
|
const agentRoot2 = findSubProjectRoot15("agent", root) ?? root;
|
|
13316
13811
|
const access = buyerAccess(agentRoot2);
|
|
13317
13812
|
const x402Only = access.faces.length === 1 && hasX402Face(access.faces);
|
|
13813
|
+
const x402Free = x402SellerIsFree(loadAgentCfg(agentRoot2));
|
|
13814
|
+
const x402RouteLabel = x402Free ? "anonymous FREE route" : "anonymous paid route";
|
|
13318
13815
|
emit("\nAccess your agent \u2014");
|
|
13319
13816
|
if (access.agentId) {
|
|
13320
13817
|
if (!x402Only) {
|
|
@@ -13332,7 +13829,7 @@ function printAccessNextSteps(root, _invokeUrl) {
|
|
|
13332
13829
|
);
|
|
13333
13830
|
}
|
|
13334
13831
|
if (hasX402Face(access.faces)) {
|
|
13335
|
-
emit(` x402 endpoint: ${access.x402Url} (
|
|
13832
|
+
emit(` x402 endpoint: ${access.x402Url} (${x402RouteLabel})`);
|
|
13336
13833
|
}
|
|
13337
13834
|
if (!x402Only) {
|
|
13338
13835
|
emit(
|
|
@@ -13357,7 +13854,7 @@ function printAccessNextSteps(root, _invokeUrl) {
|
|
|
13357
13854
|
);
|
|
13358
13855
|
}
|
|
13359
13856
|
if (hasX402Face(access.faces)) {
|
|
13360
|
-
emit(` x402 endpoint: ${rtTmpl}/x402 (
|
|
13857
|
+
emit(` x402 endpoint: ${rtTmpl}/x402 (${x402RouteLabel})`);
|
|
13361
13858
|
}
|
|
13362
13859
|
if (x402Only) {
|
|
13363
13860
|
emit(" protocol discovery is suppressed for this X402-only deployment.");
|
|
@@ -13388,7 +13885,9 @@ function printAgentClientPrompt(root, invokeUrl, status) {
|
|
|
13388
13885
|
const tokenSteps = ` 1) run \`bag platform invoke-client new\` -> client_id + client_secret (secret shown ONCE).
|
|
13389
13886
|
2) POST client_credentials to ${tokenUrl} with scope \`${scope}\` -> access_token.
|
|
13390
13887
|
3) send \`Authorization: Bearer <token>\`.`;
|
|
13391
|
-
const
|
|
13888
|
+
const agentCfg = loadAgentCfg(agentRoot2);
|
|
13889
|
+
const rails = commerceRails(agentCfg);
|
|
13890
|
+
const x402Free = x402SellerIsFree(agentCfg);
|
|
13392
13891
|
const x402Only = faces.length === 1 && hasX402Face(faces) || rails.x402 && !rails.erc8183;
|
|
13393
13892
|
const x402Summary = x402DeploySummary(agentRoot2, "platform", access.x402Url);
|
|
13394
13893
|
const endpoints = [];
|
|
@@ -13406,7 +13905,7 @@ function printAgentClientPrompt(root, invokeUrl, status) {
|
|
|
13406
13905
|
}
|
|
13407
13906
|
if (x402Only) {
|
|
13408
13907
|
endpoints.push(
|
|
13409
|
-
` [X402] Anonymous paid endpoint: ${access.x402Url ?? `${rt}/x402`}
|
|
13908
|
+
` [X402] Anonymous ${x402Free ? "FREE" : "paid"} endpoint: ${access.x402Url ?? `${rt}/x402`}
|
|
13410
13909
|
Protocol discovery is suppressed.`
|
|
13411
13910
|
);
|
|
13412
13911
|
}
|
|
@@ -13454,7 +13953,7 @@ function printAgentClientPrompt(root, invokeUrl, status) {
|
|
|
13454
13953
|
] : [],
|
|
13455
13954
|
"",
|
|
13456
13955
|
"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.",
|
|
13956
|
+
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
13957
|
CLIENT_PROMPT_RULE
|
|
13459
13958
|
];
|
|
13460
13959
|
const text2 = lines.join("\n");
|
|
@@ -13641,9 +14140,11 @@ function isFile14(p) {
|
|
|
13641
14140
|
|
|
13642
14141
|
// src/cli/_runtime/base.ts
|
|
13643
14142
|
var DEPLOY_RUNTIME_TARGETS = ["agentcore", "azure-foundry"];
|
|
14143
|
+
var ADVERTISED_DEPLOY_RUNTIME_TARGETS = ["agentcore"];
|
|
13644
14144
|
|
|
13645
14145
|
// src/cli/deploy.ts
|
|
13646
14146
|
var TARGET_CHOICES = [...DEPLOY_RUNTIME_TARGETS];
|
|
14147
|
+
var ADVERTISED_TARGET_CHOICES = [...ADVERTISED_DEPLOY_RUNTIME_TARGETS];
|
|
13647
14148
|
function isFile15(p) {
|
|
13648
14149
|
try {
|
|
13649
14150
|
return fs37.statSync(p).isFile();
|
|
@@ -13683,17 +14184,33 @@ function loadAgentCfg2(root) {
|
|
|
13683
14184
|
function registerDeploy(program) {
|
|
13684
14185
|
program.enablePositionalOptions();
|
|
13685
14186
|
const p = program.command("deploy").description(
|
|
13686
|
-
"Deploy the agent after explicitly selecting BNB
|
|
13687
|
-
);
|
|
13688
|
-
const runtimeOption = () =>
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
)
|
|
14187
|
+
"Deploy the agent after explicitly selecting BNB or AWS; lifecycle subcommands inspect recorded deployments."
|
|
14188
|
+
);
|
|
14189
|
+
const runtimeOption = () => acceptChoices(
|
|
14190
|
+
new Option4(
|
|
14191
|
+
"--runtime <name>",
|
|
14192
|
+
"Deploy runtime to validate against (default: studio.toml [stack].runtime, else agentcore)."
|
|
14193
|
+
),
|
|
14194
|
+
TARGET_CHOICES,
|
|
14195
|
+
ADVERTISED_TARGET_CHOICES
|
|
14196
|
+
);
|
|
14197
|
+
const targetAlias = () => acceptChoices(
|
|
14198
|
+
new Option4(
|
|
14199
|
+
"--target <name>",
|
|
14200
|
+
"Deprecated alias of --runtime."
|
|
14201
|
+
).hideHelp(),
|
|
14202
|
+
TARGET_CHOICES,
|
|
14203
|
+
ADVERTISED_TARGET_CHOICES
|
|
14204
|
+
);
|
|
14205
|
+
const providerOption = () => acceptChoices(
|
|
14206
|
+
new Option4(
|
|
14207
|
+
"--provider <provider>",
|
|
14208
|
+
"Provider: bnb or aws. Required for non-interactive deploy; lifecycle commands need it only when multiple records exist."
|
|
14209
|
+
),
|
|
14210
|
+
// Unadvertised providers stay parseable (see ADVERTISED_PROVIDERS).
|
|
14211
|
+
PROVIDER_MENU_ORDER,
|
|
14212
|
+
ADVERTISED_PROVIDERS
|
|
14213
|
+
);
|
|
13697
14214
|
p.addOption(providerOption()).option("--project-root <path>", "Override project root.").option("--skip-prepare", "Skip non-storage local readiness checks.").option(
|
|
13698
14215
|
"--force-deploy-broken-storage",
|
|
13699
14216
|
"DANGEROUS: bypass fatal deliverable-storage checks."
|
|
@@ -13702,7 +14219,7 @@ function registerDeploy(program) {
|
|
|
13702
14219
|
).option("--accept-risk", "Accept provider risk/terms disclosures.").option("--yes", "Confirm the selected deployment plan non-interactively.").option(
|
|
13703
14220
|
"--allow-multiple",
|
|
13704
14221
|
"Allow creating a deployment while another provider remains active."
|
|
13705
|
-
).option("--skip-smoke", "Skip
|
|
14222
|
+
).option("--skip-smoke", "Skip the delegated post-deploy smoke check.").action(act((opts) => cmdDeploy(opts, [])));
|
|
13706
14223
|
p.command("prepare").description("Run the deploy-readiness sweep on this project.").addOption(runtimeOption()).addOption(targetAlias()).option("--project-root <path>", "Override project root.").option("--json", "Emit JSON instead of a table.").option(
|
|
13707
14224
|
"--ignore-warnings",
|
|
13708
14225
|
"Exit 0 even if WARNING-level checks fire (still fails on BLOCKED/CRITICAL)."
|
|
@@ -13741,7 +14258,7 @@ function registerDeploy(program) {
|
|
|
13741
14258
|
"Allow creating a deployment while another provider remains active."
|
|
13742
14259
|
).option(
|
|
13743
14260
|
"--skip-smoke",
|
|
13744
|
-
"
|
|
14261
|
+
"Skip the post-deploy smoke check (omit `--smoke` from the delegated bnbagent-deploy invocation)."
|
|
13745
14262
|
).argument(
|
|
13746
14263
|
"[agentcoreArgs...]",
|
|
13747
14264
|
"Extra args passed straight through to `bnbagent-deploy deploy` (put after `--`)."
|
|
@@ -13799,7 +14316,7 @@ function registerDeploy(program) {
|
|
|
13799
14316
|
(opts) => cmdDestroy(opts)
|
|
13800
14317
|
)
|
|
13801
14318
|
);
|
|
13802
|
-
p.command("logs").description("Show the selected deployment's logs via bnbagent-deploy.").option("--project-root <path>", "Override project root.").addOption(providerOption()).option("--since <dur>", "Lower bound, e.g. 5m/2h/1d (default 10m).", "10m").option("--follow", "Stream new lines (poll ~3s).").option("--limit <n>", "Number of recent lines (
|
|
14319
|
+
p.command("logs").description("Show the selected deployment's logs via bnbagent-deploy.").option("--project-root <path>", "Override project root.").addOption(providerOption()).option("--since <dur>", "Lower bound, e.g. 5m/2h/1d (default 10m).", "10m").option("--follow", "Stream new lines (poll ~3s).").option("--limit <n>", "Number of recent lines (default 50).").option("--session <id>", "Container session id.").addOption(
|
|
13803
14320
|
new Option4(
|
|
13804
14321
|
"--job <id>",
|
|
13805
14322
|
"Deprecated Studio direct-CloudWatch filter."
|
|
@@ -14251,14 +14768,18 @@ function renderProviderSelection(deployments, trial, unavailable) {
|
|
|
14251
14768
|
}
|
|
14252
14769
|
const remaining = formatTrialRemaining(trial.remainingSeconds);
|
|
14253
14770
|
const bnbDetail = unavailable.bnb ? `unavailable \u2014 ${unavailable.bnb}` : trial.state === "expired" ? `unavailable \u2014 trial expired${trial.expiresAt ? ` ${trial.expiresAt}` : ""}` : trial.state === "active" ? `active${remaining ? ` \u2014 ${remaining} remaining` : ""}${trial.expiresAt ? ` \xB7 expires ${trial.expiresAt}` : ""}` : trial.state === "available" ? "available \u2014 free 48h testnet; starts on first successful deploy" : "login required to check trial eligibility";
|
|
14771
|
+
const detail = {
|
|
14772
|
+
bnb: bnbDetail,
|
|
14773
|
+
aws: unavailable.aws ? `unavailable \u2014 ${unavailable.aws}` : "deploy to your AWS account",
|
|
14774
|
+
azure: unavailable.azure ? `unavailable \u2014 ${unavailable.azure}` : "deploy to your Azure account"
|
|
14775
|
+
};
|
|
14254
14776
|
printOut("Deployment providers");
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
)
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
);
|
|
14777
|
+
for (const provider of PROVIDER_MENU_ORDER) {
|
|
14778
|
+
if (!ADVERTISED_PROVIDERS.includes(provider)) continue;
|
|
14779
|
+
printOut(
|
|
14780
|
+
` ${providerDigit(provider)}. ${PROVIDER_LABELS[provider].padEnd(18)}${detail[provider]}`
|
|
14781
|
+
);
|
|
14782
|
+
}
|
|
14262
14783
|
}
|
|
14263
14784
|
async function cmdDeploy(opts, deployArgs) {
|
|
14264
14785
|
const rc = applyProjectRoot(opts.projectRoot);
|
|
@@ -14283,7 +14804,7 @@ async function cmdDeploy(opts, deployArgs) {
|
|
|
14283
14804
|
});
|
|
14284
14805
|
if (selection.kind === "selection_required") {
|
|
14285
14806
|
printErr(
|
|
14286
|
-
|
|
14807
|
+
`error: deployment provider must be selected explicitly in a non-interactive terminal; pass --provider ${ADVERTISED_PROVIDERS_HELP} --yes`
|
|
14287
14808
|
);
|
|
14288
14809
|
return selection.exitCode;
|
|
14289
14810
|
}
|
|
@@ -14706,7 +15227,7 @@ async function printAccessNextSteps2(root, runtime, opts = {}) {
|
|
|
14706
15227
|
return;
|
|
14707
15228
|
}
|
|
14708
15229
|
printOut(
|
|
14709
|
-
" Azure Foundry buyer access is in Preview \u2014 see
|
|
15230
|
+
" Azure Foundry buyer access is in Preview \u2014 see docs/guides/foundry-a2a-access.md in the bnbagent-studio repo. The per-agent A2A card URL and OAuth scope are confirmed only after a live deploy."
|
|
14710
15231
|
);
|
|
14711
15232
|
}
|
|
14712
15233
|
var CLIENT_PROMPT_RULE2 = "\u2500".repeat(62);
|
|
@@ -14731,7 +15252,9 @@ async function printAgentClientPrompt2(root, destination, opts = {}) {
|
|
|
14731
15252
|
const tokenSteps = ` 1) token endpoint ${tokenUrl} (scope \`${scope}\`); ${credentialStep}.
|
|
14732
15253
|
2) POST client_credentials -> access_token.
|
|
14733
15254
|
3) send \`Authorization: Bearer <token>\` AND header \`X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: <stable id >= 33 chars>\`.`;
|
|
14734
|
-
const
|
|
15255
|
+
const agentCfg = loadAgentCfg2(agentRoot2) ?? {};
|
|
15256
|
+
const rails = commerceRails(agentCfg);
|
|
15257
|
+
const x402Free = x402SellerIsFree(agentCfg);
|
|
14735
15258
|
const x402Only = rails.x402 && !rails.erc8183;
|
|
14736
15259
|
const x402Summary = x402DeploySummary(agentRoot2, destination, null);
|
|
14737
15260
|
const faces = agentFacesOf(agentRoot2);
|
|
@@ -14784,7 +15307,7 @@ async function printAgentClientPrompt2(root, destination, opts = {}) {
|
|
|
14784
15307
|
] : [],
|
|
14785
15308
|
"",
|
|
14786
15309
|
"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.",
|
|
15310
|
+
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
15311
|
CLIENT_PROMPT_RULE2
|
|
14789
15312
|
];
|
|
14790
15313
|
const text2 = lines.join("\n");
|
|
@@ -14978,7 +15501,9 @@ async function cmdStatus2(opts) {
|
|
|
14978
15501
|
printOut(JSON.stringify({ deployments: [] }, null, 2));
|
|
14979
15502
|
} else {
|
|
14980
15503
|
printOut(`No recorded deployments${detail}.`);
|
|
14981
|
-
printOut(
|
|
15504
|
+
printOut(
|
|
15505
|
+
`Start one with \`bag deploy --provider ${ADVERTISED_PROVIDERS_HELP}\`.`
|
|
15506
|
+
);
|
|
14982
15507
|
}
|
|
14983
15508
|
return opts.provider ? 2 : 0;
|
|
14984
15509
|
}
|
|
@@ -15135,13 +15660,13 @@ async function selectRecordedForLifecycle(root, requested, operation) {
|
|
|
15135
15660
|
if (deployments.length === 1 && soleDeployment) return soleDeployment;
|
|
15136
15661
|
if (deployments.length === 0) {
|
|
15137
15662
|
printErr(
|
|
15138
|
-
|
|
15663
|
+
`error: no deployment is recorded; run \`bag deploy --provider ${ADVERTISED_PROVIDERS_HELP}\` first`
|
|
15139
15664
|
);
|
|
15140
15665
|
return 2;
|
|
15141
15666
|
}
|
|
15142
15667
|
if (!stdinIsTty()) {
|
|
15143
15668
|
printErr(
|
|
15144
|
-
`error: multiple deployments are active; non-interactive \`${operation}\` requires --provider
|
|
15669
|
+
`error: multiple deployments are active; non-interactive \`${operation}\` requires --provider ${ADVERTISED_PROVIDERS_HELP}`
|
|
15145
15670
|
);
|
|
15146
15671
|
return 2;
|
|
15147
15672
|
}
|
|
@@ -15426,7 +15951,9 @@ async function cmdDoctor(opts) {
|
|
|
15426
15951
|
checks.push(...await checkLlm(data, projectRoot));
|
|
15427
15952
|
checks.push(...await checkNetwork(data, opts.network));
|
|
15428
15953
|
checks.push(...checkCurrency(data));
|
|
15954
|
+
checks.push(...checkErc8183Pricing(data));
|
|
15429
15955
|
checks.push(...checkPriceBounds(data));
|
|
15956
|
+
checks.push(...checkX402Seller(data, projectRoot));
|
|
15430
15957
|
checks.push(...checkStorage(data));
|
|
15431
15958
|
checks.push(...await checkBalances(projectRoot, data, opts.network));
|
|
15432
15959
|
checks.push(...await checkPieverseMainnetU(projectRoot, data));
|
|
@@ -15592,7 +16119,7 @@ function anchoredKeystoreDir(projectRoot, walletCfg) {
|
|
|
15592
16119
|
async function checkWallet(projectRoot, data) {
|
|
15593
16120
|
const walletCfg = tableOf13(data, "wallet");
|
|
15594
16121
|
if (walletCfg.kind === "twak") {
|
|
15595
|
-
return checkWalletTwak(walletCfg, projectRoot);
|
|
16122
|
+
return checkWalletTwak(walletCfg, projectRoot, data);
|
|
15596
16123
|
}
|
|
15597
16124
|
if (walletCfg.kind === "altana") {
|
|
15598
16125
|
return checkWalletAltana(walletCfg, projectRoot);
|
|
@@ -15737,7 +16264,7 @@ function checkWalletAltana(walletCfg, projectRoot) {
|
|
|
15737
16264
|
}
|
|
15738
16265
|
return out;
|
|
15739
16266
|
}
|
|
15740
|
-
async function checkWalletTwak(walletCfg, projectRoot) {
|
|
16267
|
+
async function checkWalletTwak(walletCfg, projectRoot, data = {}) {
|
|
15741
16268
|
const out = [];
|
|
15742
16269
|
if (await whichTwak() !== null) {
|
|
15743
16270
|
const version = await twakInstalledVersion();
|
|
@@ -15762,6 +16289,15 @@ async function checkWalletTwak(walletCfg, projectRoot) {
|
|
|
15762
16289
|
detail: `not on PATH \u2014 npm install -g @trustwallet/cli@${TWAK_CLI_VERSION}`
|
|
15763
16290
|
});
|
|
15764
16291
|
}
|
|
16292
|
+
const networkName = String(tableOf13(data, "network").default ?? "bsc-testnet");
|
|
16293
|
+
const unsupportedTargets = twakUnsupportedContractOverrides(networkName);
|
|
16294
|
+
if (unsupportedTargets.length > 0) {
|
|
16295
|
+
out.push({
|
|
16296
|
+
name: "[wallet] twak contract targets",
|
|
16297
|
+
status: FAIL,
|
|
16298
|
+
detail: `twak v0.20.0 cannot target custom ERC-8183 contracts (${unsupportedTargets.map((item) => item.envKey).join(", ")}) and would use canonical contracts instead. Use wallet.kind='evm-local' for custom ERC-8183 contracts, or remove the overrides. ERC8004_REGISTRY_ADDRESS is supported.`
|
|
16299
|
+
});
|
|
16300
|
+
}
|
|
15765
16301
|
const walletFile = twakWalletFile(walletCfg, projectRoot);
|
|
15766
16302
|
if (isFile16(walletFile)) {
|
|
15767
16303
|
out.push({
|
|
@@ -16034,10 +16570,94 @@ function checkCurrency(data) {
|
|
|
16034
16570
|
}
|
|
16035
16571
|
return [];
|
|
16036
16572
|
}
|
|
16573
|
+
function checkErc8183Pricing(data) {
|
|
16574
|
+
const payValue = tableOf13(data, "payments").erc8183;
|
|
16575
|
+
if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
|
|
16576
|
+
return [];
|
|
16577
|
+
}
|
|
16578
|
+
const pay = payValue;
|
|
16579
|
+
const pricing = erc8183PricingState(pay);
|
|
16580
|
+
if (pricing.kind === "unset") {
|
|
16581
|
+
return [
|
|
16582
|
+
{
|
|
16583
|
+
name: "erc8183 pricing",
|
|
16584
|
+
status: FAIL,
|
|
16585
|
+
detail: "[payments.erc8183].price is unset \u2014 set it explicitly to 0 for FREE or to a positive integer string in token base units."
|
|
16586
|
+
}
|
|
16587
|
+
];
|
|
16588
|
+
}
|
|
16589
|
+
if (pricing.kind === "invalid") {
|
|
16590
|
+
return [
|
|
16591
|
+
{
|
|
16592
|
+
name: "erc8183 pricing",
|
|
16593
|
+
status: FAIL,
|
|
16594
|
+
detail: `[payments.erc8183].${pricing.field} must be a non-negative uint256 decimal string; got ${JSON.stringify(pricing.value)}.`
|
|
16595
|
+
}
|
|
16596
|
+
];
|
|
16597
|
+
}
|
|
16598
|
+
if (pricing.kind === "clamped_to_zero") {
|
|
16599
|
+
return [
|
|
16600
|
+
{
|
|
16601
|
+
name: "erc8183 pricing",
|
|
16602
|
+
status: FAIL,
|
|
16603
|
+
detail: `PAID list price ${pricing.listPrice} is clamped to 0 by max_price. Set price explicitly to 0 for FREE or fix the clamp.`
|
|
16604
|
+
}
|
|
16605
|
+
];
|
|
16606
|
+
}
|
|
16607
|
+
const contracts = erc8183ContractOverrideState();
|
|
16608
|
+
const priceMode = pricing.kind === "free" ? "FREE \u2014 zero token escrow" : `PAID \u2014 effective list price ${pricing.effectivePrice} token base units`;
|
|
16609
|
+
if (contracts.mode === "partial") {
|
|
16610
|
+
return [
|
|
16611
|
+
{
|
|
16612
|
+
name: "erc8183 pricing",
|
|
16613
|
+
status: FAIL,
|
|
16614
|
+
detail: `${priceMode}, but the ERC-8183 contract override is incomplete. Set ${contracts.missing.join(", ")} so commerce, router, and policy come from one compatible stack.`
|
|
16615
|
+
}
|
|
16616
|
+
];
|
|
16617
|
+
}
|
|
16618
|
+
if (contracts.mode === "invalid") {
|
|
16619
|
+
return [
|
|
16620
|
+
{
|
|
16621
|
+
name: "erc8183 pricing",
|
|
16622
|
+
status: FAIL,
|
|
16623
|
+
detail: `${priceMode}, but these contract overrides are not valid EVM addresses: ${contracts.invalid.join(", ")}.`
|
|
16624
|
+
}
|
|
16625
|
+
];
|
|
16626
|
+
}
|
|
16627
|
+
if (pricing.kind === "paid") {
|
|
16628
|
+
return [
|
|
16629
|
+
{
|
|
16630
|
+
name: "erc8183 pricing",
|
|
16631
|
+
status: PASS,
|
|
16632
|
+
detail: `${priceMode}.`
|
|
16633
|
+
}
|
|
16634
|
+
];
|
|
16635
|
+
}
|
|
16636
|
+
if (contracts.mode === "custom") {
|
|
16637
|
+
return [
|
|
16638
|
+
{
|
|
16639
|
+
name: "erc8183 pricing",
|
|
16640
|
+
status: PASS,
|
|
16641
|
+
detail: "FREE \u2014 zero token escrow; custom contract stack selected with all three ERC-8183 address overrides."
|
|
16642
|
+
}
|
|
16643
|
+
];
|
|
16644
|
+
}
|
|
16645
|
+
return [
|
|
16646
|
+
{
|
|
16647
|
+
name: "erc8183 pricing",
|
|
16648
|
+
status: FAIL,
|
|
16649
|
+
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 custom stack."
|
|
16650
|
+
}
|
|
16651
|
+
];
|
|
16652
|
+
}
|
|
16037
16653
|
function checkPriceBounds(data) {
|
|
16038
|
-
const
|
|
16654
|
+
const payValue = tableOf13(data, "payments").erc8183;
|
|
16655
|
+
if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
|
|
16656
|
+
return [];
|
|
16657
|
+
}
|
|
16658
|
+
const pay = payValue;
|
|
16039
16659
|
const maxPrice = String(pay.max_price ?? "").trim();
|
|
16040
|
-
if (!maxPrice
|
|
16660
|
+
if (!maxPrice && erc8183PricingState(pay).kind !== "free") {
|
|
16041
16661
|
return [
|
|
16042
16662
|
{
|
|
16043
16663
|
name: "erc8183 price bounds",
|
|
@@ -16048,6 +16668,84 @@ function checkPriceBounds(data) {
|
|
|
16048
16668
|
}
|
|
16049
16669
|
return [];
|
|
16050
16670
|
}
|
|
16671
|
+
function checkX402Seller(data, agentRoot2) {
|
|
16672
|
+
const payments = tableOf13(data, "payments");
|
|
16673
|
+
const sellerValue = payments.x402_seller;
|
|
16674
|
+
if (sellerValue === null || typeof sellerValue !== "object" || Array.isArray(sellerValue)) {
|
|
16675
|
+
return [];
|
|
16676
|
+
}
|
|
16677
|
+
const seller = sellerValue;
|
|
16678
|
+
if (seller.enabled !== true) return [];
|
|
16679
|
+
const pricing = x402SellerPricingState(seller);
|
|
16680
|
+
if (pricing.kind === "invalid") {
|
|
16681
|
+
return [
|
|
16682
|
+
{
|
|
16683
|
+
name: "x402 pricing",
|
|
16684
|
+
status: FAIL,
|
|
16685
|
+
detail: "[payments.x402_seller].price_usd must be a non-negative decimal string."
|
|
16686
|
+
}
|
|
16687
|
+
];
|
|
16688
|
+
}
|
|
16689
|
+
const runtime = String(tableOf13(data, "stack").runtime ?? "agentcore");
|
|
16690
|
+
const hasErc8183 = payments.erc8183 !== null && typeof payments.erc8183 === "object" && !Array.isArray(payments.erc8183);
|
|
16691
|
+
const fallbackStatus = hasErc8183 ? WARN : FAIL;
|
|
16692
|
+
if (pricing.kind === "free") {
|
|
16693
|
+
const out2 = [
|
|
16694
|
+
{
|
|
16695
|
+
name: "x402 pricing",
|
|
16696
|
+
status: PASS,
|
|
16697
|
+
detail: "FREE \u2014 /x402 is anonymous; B402 verify/settle, token payment, and settlement audit are bypassed."
|
|
16698
|
+
}
|
|
16699
|
+
];
|
|
16700
|
+
const credentials2 = b402Credentials(agentRoot2);
|
|
16701
|
+
if (credentials2.any) {
|
|
16702
|
+
out2.push({
|
|
16703
|
+
name: "x402 credentials",
|
|
16704
|
+
status: INFO,
|
|
16705
|
+
detail: "configured B402 credentials are unused in FREE mode and will not be synchronized."
|
|
16706
|
+
});
|
|
16707
|
+
}
|
|
16708
|
+
if (runtime !== "agentcore") {
|
|
16709
|
+
out2.push({
|
|
16710
|
+
name: "x402 runtime",
|
|
16711
|
+
status: fallbackStatus,
|
|
16712
|
+
detail: `${runtime} has no /x402 route; FREE pricing does not remove that runtime limitation.`
|
|
16713
|
+
});
|
|
16714
|
+
}
|
|
16715
|
+
return out2;
|
|
16716
|
+
}
|
|
16717
|
+
const out = [
|
|
16718
|
+
{
|
|
16719
|
+
name: "x402 pricing",
|
|
16720
|
+
status: PASS,
|
|
16721
|
+
detail: `PAID \u2014 $${pricing.priceUsd} per request through B402.`
|
|
16722
|
+
}
|
|
16723
|
+
];
|
|
16724
|
+
const walletKind2 = String(tableOf13(data, "wallet").kind ?? "evm-local");
|
|
16725
|
+
if (!["evm-local", "twak"].includes(walletKind2)) {
|
|
16726
|
+
out.push({
|
|
16727
|
+
name: "x402 payout wallet",
|
|
16728
|
+
status: FAIL,
|
|
16729
|
+
detail: `PAID B402 supports evm-local or twak, not ${walletKind2}.`
|
|
16730
|
+
});
|
|
16731
|
+
}
|
|
16732
|
+
const credentials = b402Credentials(agentRoot2);
|
|
16733
|
+
if (!credentials.complete) {
|
|
16734
|
+
out.push({
|
|
16735
|
+
name: "x402 credentials",
|
|
16736
|
+
status: credentials.any ? FAIL : fallbackStatus,
|
|
16737
|
+
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."
|
|
16738
|
+
});
|
|
16739
|
+
}
|
|
16740
|
+
if (runtime !== "agentcore") {
|
|
16741
|
+
out.push({
|
|
16742
|
+
name: "x402 runtime",
|
|
16743
|
+
status: fallbackStatus,
|
|
16744
|
+
detail: `${runtime} has no /x402 route; deploy this rail to AgentCore.`
|
|
16745
|
+
});
|
|
16746
|
+
}
|
|
16747
|
+
return out;
|
|
16748
|
+
}
|
|
16051
16749
|
function checkStorage(data) {
|
|
16052
16750
|
const kind = String(tableOf13(data, "storage").kind ?? "local").toLowerCase();
|
|
16053
16751
|
if (kind === "ipfs") {
|
|
@@ -16650,7 +17348,7 @@ function loadAgentCfg3() {
|
|
|
16650
17348
|
return null;
|
|
16651
17349
|
}
|
|
16652
17350
|
}
|
|
16653
|
-
function
|
|
17351
|
+
function resolveNetwork2(override) {
|
|
16654
17352
|
if (override) {
|
|
16655
17353
|
return override;
|
|
16656
17354
|
}
|
|
@@ -16840,7 +17538,7 @@ async function cmdRegister2(opts) {
|
|
|
16840
17538
|
return 2;
|
|
16841
17539
|
}
|
|
16842
17540
|
const wallet = walletRt6.getWallet();
|
|
16843
|
-
const network =
|
|
17541
|
+
const network = resolveNetwork2(opts.network);
|
|
16844
17542
|
const gasErr = await precheckRegisterGas(wallet, network);
|
|
16845
17543
|
if (gasErr !== null) {
|
|
16846
17544
|
printErr(`error: ${gasErr}`);
|
|
@@ -16928,7 +17626,7 @@ async function cmdUpdateEndpoint(opts) {
|
|
|
16928
17626
|
return 2;
|
|
16929
17627
|
}
|
|
16930
17628
|
const wallet = walletRt6.getWallet();
|
|
16931
|
-
const network =
|
|
17629
|
+
const network = resolveNetwork2(opts.network);
|
|
16932
17630
|
const protocol = resolveProtocol(opts.protocol);
|
|
16933
17631
|
let accessDescription = null;
|
|
16934
17632
|
const agentRoot2 = findProjectRoot5();
|
|
@@ -16971,7 +17669,7 @@ async function cmdUpdateEndpoint(opts) {
|
|
|
16971
17669
|
}
|
|
16972
17670
|
async function cmdUpdateMetadata(opts) {
|
|
16973
17671
|
const wallet = walletRt6.getWallet();
|
|
16974
|
-
const network =
|
|
17672
|
+
const network = resolveNetwork2(opts.network);
|
|
16975
17673
|
let txHash;
|
|
16976
17674
|
try {
|
|
16977
17675
|
txHash = await setMetadata(wallet, opts.key, opts.value, {
|
|
@@ -17003,7 +17701,7 @@ async function cmdClearPending(opts) {
|
|
|
17003
17701
|
}
|
|
17004
17702
|
const pendingTx = identity.pending_tx ? String(identity.pending_tx) : null;
|
|
17005
17703
|
if (pendingTx !== null && !opts.force) {
|
|
17006
|
-
const network =
|
|
17704
|
+
const network = resolveNetwork2(opts.network);
|
|
17007
17705
|
let seen;
|
|
17008
17706
|
try {
|
|
17009
17707
|
seen = await readTransactionSeen(pendingTx, network);
|
|
@@ -17034,7 +17732,7 @@ async function cmdGetMetadata(opts) {
|
|
|
17034
17732
|
let value;
|
|
17035
17733
|
try {
|
|
17036
17734
|
value = await getMetadata(wallet, opts.key, {
|
|
17037
|
-
network:
|
|
17735
|
+
network: resolveNetwork2(opts.network)
|
|
17038
17736
|
});
|
|
17039
17737
|
} catch (exc) {
|
|
17040
17738
|
printOut(`error: ${errMsg4(exc)}`);
|
|
@@ -17047,7 +17745,7 @@ async function cmdShow6(networkArg) {
|
|
|
17047
17745
|
const wallet = walletRt6.getWallet();
|
|
17048
17746
|
let record;
|
|
17049
17747
|
try {
|
|
17050
|
-
record = await show2(wallet, { network:
|
|
17748
|
+
record = await show2(wallet, { network: resolveNetwork2(networkArg) });
|
|
17051
17749
|
} catch (exc) {
|
|
17052
17750
|
printOut(`error: ${errMsg4(exc)}`);
|
|
17053
17751
|
return errName2(exc) === "NotRegisteredError" ? 2 : 1;
|
|
@@ -17064,7 +17762,7 @@ async function cmdResolve(agentId, networkArg) {
|
|
|
17064
17762
|
try {
|
|
17065
17763
|
uri = await resolve20(agentId, {
|
|
17066
17764
|
wallet,
|
|
17067
|
-
network:
|
|
17765
|
+
network: resolveNetwork2(networkArg)
|
|
17068
17766
|
});
|
|
17069
17767
|
} catch (exc) {
|
|
17070
17768
|
printOut(`error: ${errMsg4(exc)}`);
|
|
@@ -18576,7 +19274,10 @@ function registerX402(program) {
|
|
|
18576
19274
|
return 1;
|
|
18577
19275
|
})
|
|
18578
19276
|
);
|
|
18579
|
-
sell.command("init").description("Add the x402 seller config and missing B402 placeholders.").
|
|
19277
|
+
sell.command("init").description("Add the x402 seller config and missing B402 placeholders.").option(
|
|
19278
|
+
"--price-usd <amount>",
|
|
19279
|
+
"Per-request decimal USD price; use 0 for FREE passthrough."
|
|
19280
|
+
).action(act((opts) => cmdSellInit(opts.priceUsd)));
|
|
18580
19281
|
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
19282
|
}
|
|
18582
19283
|
function hostOf(url) {
|
|
@@ -18628,7 +19329,7 @@ function loadSellerConfig() {
|
|
|
18628
19329
|
return null;
|
|
18629
19330
|
}
|
|
18630
19331
|
}
|
|
18631
|
-
function
|
|
19332
|
+
function resolveNetwork3(cfg, override) {
|
|
18632
19333
|
if (override) {
|
|
18633
19334
|
return override;
|
|
18634
19335
|
}
|
|
@@ -18661,39 +19362,88 @@ function resolveWallet2() {
|
|
|
18661
19362
|
function printScopeError(host) {
|
|
18662
19363
|
printErr(`error: ${SCOPE_NOTE.replace("{host}", host)}`);
|
|
18663
19364
|
}
|
|
18664
|
-
function cmdSellInit() {
|
|
19365
|
+
async function cmdSellInit(priceFlag) {
|
|
18665
19366
|
const loaded = loadSellerConfig();
|
|
18666
19367
|
if (loaded === null) return 1;
|
|
18667
19368
|
const [root, cfg] = loaded;
|
|
19369
|
+
const tomlPath = path46.join(root, "studio.toml");
|
|
19370
|
+
const original = fs44.readFileSync(tomlPath, "utf8");
|
|
19371
|
+
const sellerExists = hasSection(original, "payments.x402_seller");
|
|
19372
|
+
let priceUsd;
|
|
19373
|
+
try {
|
|
19374
|
+
priceUsd = priceFlag !== void 0 ? normalizeB402PriceUsd(priceFlag) : sellerExists ? X402SellerPolicy.fromToml(cfg).priceUsd : await resolveSellInitPrice(stdinIsTty());
|
|
19375
|
+
} catch (exc) {
|
|
19376
|
+
printErr(`error: ${errMsg6(exc)}`);
|
|
19377
|
+
return 2;
|
|
19378
|
+
}
|
|
19379
|
+
const free = x402SellerPricingState({ price_usd: priceUsd }).kind === "free";
|
|
18668
19380
|
const walletKind2 = String(tableOf15(cfg, "wallet").kind ?? "evm-local");
|
|
18669
|
-
if (!["evm-local", "twak"].includes(walletKind2)) {
|
|
19381
|
+
if (!free && !["evm-local", "twak"].includes(walletKind2)) {
|
|
18670
19382
|
printErr(
|
|
18671
19383
|
`error: the b402 seller rail supports wallet.kind evm-local or twak only; '${walletKind2}' cannot act as the b402 payout wallet.`
|
|
18672
19384
|
);
|
|
18673
19385
|
return 1;
|
|
18674
19386
|
}
|
|
18675
|
-
|
|
18676
|
-
const original = fs44.readFileSync(tomlPath, "utf8");
|
|
18677
|
-
if (!hasSection(original, "payments.x402_seller")) {
|
|
19387
|
+
if (!sellerExists) {
|
|
18678
19388
|
fs44.writeFileSync(
|
|
18679
19389
|
tomlPath,
|
|
18680
19390
|
updateSection(original, "payments.x402_seller", {
|
|
18681
19391
|
enabled: true,
|
|
18682
|
-
price_usd:
|
|
19392
|
+
price_usd: priceUsd,
|
|
18683
19393
|
assets: ["U"],
|
|
18684
19394
|
pay_to: "",
|
|
18685
19395
|
work_timeout_seconds: 60
|
|
18686
19396
|
}),
|
|
18687
19397
|
"utf8"
|
|
18688
19398
|
);
|
|
19399
|
+
} else if (priceFlag !== void 0) {
|
|
19400
|
+
fs44.writeFileSync(
|
|
19401
|
+
tomlPath,
|
|
19402
|
+
updateSection(original, "payments.x402_seller", {
|
|
19403
|
+
price_usd: priceUsd
|
|
19404
|
+
}),
|
|
19405
|
+
"utf8"
|
|
19406
|
+
);
|
|
18689
19407
|
}
|
|
18690
19408
|
const envPath = envLocalPath17(root);
|
|
18691
19409
|
for (const key of B402_ENV_KEYS) {
|
|
18692
|
-
|
|
19410
|
+
const existing = getEnvVar(envPath, key);
|
|
19411
|
+
if (key === "B402_BASE_URL" && !free && resolveNetwork3(cfg).toLowerCase() === "bsc-testnet" && !process.env.B402_BASE_URL && !existing) {
|
|
19412
|
+
setEnvVar(envPath, key, DEFAULT_B402_TESTNET_BASE_URL);
|
|
19413
|
+
} else if (existing === null) {
|
|
19414
|
+
setEnvVar(envPath, key, "");
|
|
19415
|
+
}
|
|
19416
|
+
}
|
|
19417
|
+
printOut(
|
|
19418
|
+
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."
|
|
19419
|
+
);
|
|
19420
|
+
if (free) {
|
|
19421
|
+
printErr(
|
|
19422
|
+
"warning: /x402 is an anonymous FREE endpoint; B402 verify/settle and payment audit are bypassed."
|
|
19423
|
+
);
|
|
18693
19424
|
}
|
|
18694
|
-
printOut("\u2713 x402 seller config and B402 placeholders are ready.");
|
|
18695
19425
|
return 0;
|
|
18696
19426
|
}
|
|
19427
|
+
async function resolveSellInitPrice(isTty) {
|
|
19428
|
+
if (!isTty) return DEFAULT_B402_PRICE_USD;
|
|
19429
|
+
const choice = (await promptUser(
|
|
19430
|
+
"B402/x402 pricing [paid/free/custom, Enter = paid ($0.01)]: "
|
|
19431
|
+
)).trim().toLowerCase();
|
|
19432
|
+
if (choice === "free") return "0";
|
|
19433
|
+
if (choice === "custom") {
|
|
19434
|
+
return normalizeB402PriceUsd(
|
|
19435
|
+
await promptUser(
|
|
19436
|
+
"B402/x402 per-request price in USD (non-negative decimal): "
|
|
19437
|
+
)
|
|
19438
|
+
);
|
|
19439
|
+
}
|
|
19440
|
+
if (choice !== "" && choice !== "paid") {
|
|
19441
|
+
printErr(
|
|
19442
|
+
`hint: unknown B402/x402 pricing choice '${choice}' \u2014 using paid.`
|
|
19443
|
+
);
|
|
19444
|
+
}
|
|
19445
|
+
return DEFAULT_B402_PRICE_USD;
|
|
19446
|
+
}
|
|
18697
19447
|
async function cmdSellStatus(probe) {
|
|
18698
19448
|
const loaded = loadSellerConfig();
|
|
18699
19449
|
if (loaded === null) return 1;
|
|
@@ -18714,9 +19464,13 @@ async function cmdSellStatus(probe) {
|
|
|
18714
19464
|
};
|
|
18715
19465
|
const privateKeySet = presence.B402_PRIVATE_KEY || presence.B402_PRIVATE_KEY_B64;
|
|
18716
19466
|
const credentialsComplete = presence.B402_BASE_URL && presence.B402_CLIENT_ID && presence.B402_ACCESS_TOKEN && privateKeySet;
|
|
18717
|
-
const
|
|
19467
|
+
const free = x402SellerPricingState({ price_usd: policy.priceUsd }).kind === "free";
|
|
19468
|
+
const state = !policy.enabled ? "disabled" : free ? "free" : credentialsComplete ? "active-config" : "dormant";
|
|
18718
19469
|
printOut(`Rail state: ${state}`);
|
|
18719
19470
|
printOut(` price_usd: ${policy.priceUsd}`);
|
|
19471
|
+
printOut(
|
|
19472
|
+
` Pricing: ${free ? "FREE \u2014 anonymous passthrough; no payment" : "PAID \u2014 B402 verify/settle required"}`
|
|
19473
|
+
);
|
|
18720
19474
|
printOut(` assets: ${JSON.stringify(policy.assets)}`);
|
|
18721
19475
|
printOut(` pay_to: ${policy.payTo ?? "(agent wallet)"}`);
|
|
18722
19476
|
printOut(` work_timeout_seconds: ${policy.workTimeoutSeconds}`);
|
|
@@ -18729,11 +19483,15 @@ async function cmdSellStatus(probe) {
|
|
|
18729
19483
|
printOut("B402 probe: skipped (--no-probe)");
|
|
18730
19484
|
return 0;
|
|
18731
19485
|
}
|
|
19486
|
+
if (policy.enabled && free) {
|
|
19487
|
+
printOut("B402 probe: skipped (FREE bypasses B402)");
|
|
19488
|
+
return 0;
|
|
19489
|
+
}
|
|
18732
19490
|
if (!policy.enabled || !credentialsComplete) {
|
|
18733
19491
|
printOut("B402 probe: skipped (rail disabled or credentials incomplete)");
|
|
18734
19492
|
return 0;
|
|
18735
19493
|
}
|
|
18736
|
-
const networkName =
|
|
19494
|
+
const networkName = resolveNetwork3(cfg);
|
|
18737
19495
|
const network = caip2(getNetwork11(networkName).chainId);
|
|
18738
19496
|
const token = SELLER_TOKENS[network];
|
|
18739
19497
|
if (!token) {
|
|
@@ -18827,7 +19585,7 @@ async function cmdBuy2(url, opts) {
|
|
|
18827
19585
|
printErr(`error: ${errMsg6(exc)}`);
|
|
18828
19586
|
return 2;
|
|
18829
19587
|
}
|
|
18830
|
-
const networkName =
|
|
19588
|
+
const networkName = resolveNetwork3(cfg, opts.network);
|
|
18831
19589
|
const wallet = resolveWallet2();
|
|
18832
19590
|
if (wallet === null) {
|
|
18833
19591
|
return 1;
|
|
@@ -19016,7 +19774,7 @@ async function cmdTrust(merchant, opts) {
|
|
|
19016
19774
|
host = rec.domain;
|
|
19017
19775
|
methodHint = rec.probeMethod;
|
|
19018
19776
|
}
|
|
19019
|
-
const networkName = opts.network || (rec ? rec.network :
|
|
19777
|
+
const networkName = opts.network || (rec ? rec.network : resolveNetwork3(cfg, null));
|
|
19020
19778
|
const probed = await probeChallenge(probeUrl, methodHint);
|
|
19021
19779
|
if (probed === null) {
|
|
19022
19780
|
return 1;
|
|
@@ -19162,7 +19920,7 @@ function buildProgram() {
|
|
|
19162
19920
|
return program;
|
|
19163
19921
|
}
|
|
19164
19922
|
function cliVersion() {
|
|
19165
|
-
return "0.0.6-alpha.
|
|
19923
|
+
return "0.0.6-alpha.3";
|
|
19166
19924
|
}
|
|
19167
19925
|
|
|
19168
19926
|
// src/cli/updateCheck.ts
|