@bnbagent/studio-cli 0.0.6-alpha.3 → 0.0.6-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/{_twak-5XQMOFUC.js → _twak-4XF4H5PL.js} +1 -1
- package/dist/bag.js +983 -870
- package/dist/{chunk-7RAKL4AS.js → chunk-RO726HJG.js} +12 -9
- package/dist/{chunk-A7NAGZHR.js → chunk-VEOOFDSF.js} +76 -17
- package/dist/{deployCli-264UE6KB.js → deployCli-EEK75T67.js} +4 -2
- package/package.json +3 -2
- package/skills/references/bnbagent-studio-buying-via-8183.md +13 -5
- package/skills/references/bnbagent-studio-extending-signing.md +4 -1
- package/skills/references/bnbagent-studio-operating.md +3 -2
- package/skills/references/bnbagent-studio-scaffolding-agent.md +16 -12
- package/skills/references/bnbagent-studio-selling-via-b402.md +94 -54
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +5 -2
- package/skills/references/bnbagent-studio-using-altana-wallet.md +1 -1
- package/skills/references/bnbagent-studio-using-twak-wallet.md +15 -12
|
@@ -115,12 +115,13 @@ function twakDoubledHomeHint(walletFile) {
|
|
|
115
115
|
return `hint: the resolved wallet path ${walletFile} nests .twak/.twak \u2014 [wallet].twak_home in studio.toml likely points at the .twak directory itself. Set it to the PARENT directory (the runtime appends .twak/wallet.json) and re-run.`;
|
|
116
116
|
}
|
|
117
117
|
function twakCreateGuidance(fields) {
|
|
118
|
-
return `error: wallet.kind = "twak" \u2014 \`bag wallet new\`
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(you choose the password; pick a strong one).
|
|
118
|
+
return `error: wallet.kind = "twak" \u2014 \`bag wallet new\` only adopts an existing
|
|
119
|
+
twak wallet's address. Create the wallet ONCE (you choose the password; pick a
|
|
120
|
+
strong one).
|
|
122
121
|
|
|
123
|
-
RECOMMENDED \u2014 let studio drive the create step
|
|
122
|
+
RECOMMENDED \u2014 let studio drive the create step so you never type the password
|
|
123
|
+
on a command line (twak requires --password on its own argv, so the value does
|
|
124
|
+
reach that short-lived child process):
|
|
124
125
|
|
|
125
126
|
bag wallet twak-init --password-stdin # CI: printf %s "$PW" | bag wallet twak-init --password-stdin
|
|
126
127
|
bag wallet twak-init --password-file <f> # file must be chmod 600
|
|
@@ -136,13 +137,15 @@ secret, then run \`twak setup\` (without them \`twak wallet create\` fails with
|
|
|
136
137
|
|
|
137
138
|
${fields.setupCmd}
|
|
138
139
|
|
|
139
|
-
then create the wallet \u2014 interactive (password on
|
|
140
|
-
and it lands in your shell history \u2014 acceptable for a throwaway hot
|
|
140
|
+
then create the wallet \u2014 interactive (you type the password on argv yourself;
|
|
141
|
+
twak warns, and it lands in your shell history \u2014 acceptable for a throwaway hot
|
|
142
|
+
wallet):
|
|
141
143
|
|
|
142
144
|
${fields.createCmd}
|
|
143
145
|
|
|
144
|
-
\u2014 or, to keep the password
|
|
145
|
-
environment and let \`twak setup\` create the wallet
|
|
146
|
+
\u2014 or, to keep the password out of your shell history (CI / scripts), pass it
|
|
147
|
+
via the environment and let \`twak setup\` create the wallet
|
|
148
|
+
non-interactively:
|
|
146
149
|
|
|
147
150
|
TWAK_NONINTERACTIVE=1 TWAK_SETUP_WALLET=create TWAK_WALLET_PASSWORD=<pw> ${fields.setupCmd}
|
|
148
151
|
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
runCaptureOut,
|
|
6
6
|
runStream,
|
|
7
7
|
whichTwak
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RO726HJG.js";
|
|
9
9
|
|
|
10
10
|
// src/cli/_deploy/deployCli.ts
|
|
11
11
|
import * as fs4 from "fs";
|
|
@@ -296,6 +296,8 @@ function mb(n) {
|
|
|
296
296
|
// src/cli/_x402SellerConfig.ts
|
|
297
297
|
var DEFAULT_B402_PRICE_USD = "0.01";
|
|
298
298
|
var DEFAULT_B402_TESTNET_BASE_URL = "https://qacb.sdtaop.com";
|
|
299
|
+
var B402_DEVELOPER_ACCOUNT_URL = "https://developers.binance.com/en/docs/products/onchainpay-x402/basics/6.apply-developer-account";
|
|
300
|
+
var B402_PAID_ONBOARDING_GUIDANCE = `Create the agent wallet first, then apply for a separate B402 merchant for that exact wallet and environment; never reuse merchant credentials across wallets. Apply: ${B402_DEVELOPER_ACCOUNT_URL}`;
|
|
299
301
|
var PRICE_USD_RE = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
300
302
|
var ZERO_PRICE_USD_RE = /^0(?:\.0+)?$/;
|
|
301
303
|
function x402SellerPricingState(seller) {
|
|
@@ -687,7 +689,7 @@ function x402DeploySummary(root, destination, publicUrl) {
|
|
|
687
689
|
const seller = table3(table3(cfg.payments).x402_seller);
|
|
688
690
|
const pricing = x402SellerPricingState(seller);
|
|
689
691
|
const credentials = b402Credentials(agentRoot);
|
|
690
|
-
const activation =
|
|
692
|
+
const activation = `${B402_PAID_ONBOARDING_GUIDANCE} Then run the bnbagent-studio-selling-via-b402 skill, fill the four B402_* variables in .studio/.env.local, and redeploy.`;
|
|
691
693
|
const runtime = String(table3(cfg.stack).runtime ?? "agentcore");
|
|
692
694
|
if (destination !== "platform" && runtime !== "agentcore") {
|
|
693
695
|
return `x402 rail is FORCED DORMANT: the ${runtime} runtime has no x402 path. Deploy to AgentCore (managed platform or self-hosted) to activate the rail.`;
|
|
@@ -701,7 +703,9 @@ function x402DeploySummary(root, destination, publicUrl) {
|
|
|
701
703
|
"x402 rail is ACTIVE in FREE mode (self-hosted AgentCore).",
|
|
702
704
|
"B402 verify/settle is bypassed; no credentials, token payment, or settlement audit is used.",
|
|
703
705
|
"This target has no anonymous public URL; expose /x402 with your own HTTP front.",
|
|
704
|
-
'The front
|
|
706
|
+
'The front wraps each request as envelope-v1 JSON, {"v":1,"method":"POST","path":"/x402","headers":{...},"body":"<base64>"}, inside an authenticated AgentCore invocation. The default Bag self-deploy uses Cognito OAuth over HTTPS; IAM runtimes may use SDK/SigV4. Limits: 1 MiB request, 5 MiB response, no streaming.',
|
|
707
|
+
"You may invoke AgentCore with envelope-v1 directly, but that is not a standard public x402 HTTP endpoint.",
|
|
708
|
+
"Gateway example: https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md"
|
|
705
709
|
].join("\n");
|
|
706
710
|
}
|
|
707
711
|
const url2 = publicUrl ?? "<available after the platform agentId is assigned>";
|
|
@@ -715,16 +719,18 @@ function x402DeploySummary(root, destination, publicUrl) {
|
|
|
715
719
|
return `x402 rail is DORMANT. To activate: ${activation}`;
|
|
716
720
|
}
|
|
717
721
|
const settlement = [
|
|
718
|
-
"B402 settlement
|
|
719
|
-
"Settlement happens before work. If work later fails, the payment is retained and is not refunded automatically."
|
|
722
|
+
"B402 settlement can add significant latency; buyer timeout must be at least 120 s.",
|
|
723
|
+
"Settlement happens before work. If work later fails, the payment is retained and is not refunded automatically.",
|
|
724
|
+
"Compatibility note: current @bnb-chain/b402@0.1.0 makes one settle call and classifies an asynchronous pending response as outcome unknown; reconcile it and do not replay the paid request."
|
|
720
725
|
];
|
|
721
726
|
if (destination !== "platform") {
|
|
722
727
|
return [
|
|
723
728
|
"x402 rail is ACTIVE (self-hosted AgentCore).",
|
|
724
729
|
"This target has no anonymous public URL; expose /x402 with your own HTTP front.",
|
|
725
|
-
'The front
|
|
726
|
-
"
|
|
727
|
-
|
|
730
|
+
'The front wraps each request as envelope-v1 JSON, {"v":1,"method":"POST","path":"/x402","headers":{...},"body":"<base64>"}, inside an authenticated AgentCore invocation. The default Bag self-deploy uses Cognito OAuth over HTTPS; IAM runtimes may use SDK/SigV4. Limits: 1 MiB request, 5 MiB response, no streaming.',
|
|
731
|
+
"You may invoke AgentCore with envelope-v1 directly, but the caller must unpack the inner HTTP response and the result is not a standard public x402 endpoint.",
|
|
732
|
+
"PAID also needs fixed facilitator egress. Operate a restricted B402 Relay on a fixed-IP host such as a user-managed VPS, or use AgentCore VPC mode with a private subnet, NAT Gateway, and Elastic IP.",
|
|
733
|
+
"Gateway and Relay examples: https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md",
|
|
728
734
|
...settlement
|
|
729
735
|
].join("\n");
|
|
730
736
|
}
|
|
@@ -732,6 +738,8 @@ function x402DeploySummary(root, destination, publicUrl) {
|
|
|
732
738
|
return [
|
|
733
739
|
"x402 rail is ACTIVE.",
|
|
734
740
|
`Anonymous paid URL: ${url}`,
|
|
741
|
+
"The managed platform supplies the x402 HTTP Gateway and the fixed-egress B402 Relay; no user-managed gateway, proxy, or VPC is required.",
|
|
742
|
+
"Payment policy and RSA signing remain inside the AgentCore seller. The B402 Relay only forwards the signed request to its fixed facilitator upstream.",
|
|
735
743
|
...settlement
|
|
736
744
|
].join("\n");
|
|
737
745
|
}
|
|
@@ -996,28 +1004,76 @@ async function ensureRunnable(environment = process.env) {
|
|
|
996
1004
|
}
|
|
997
1005
|
return true;
|
|
998
1006
|
}
|
|
1007
|
+
var MODULE_MISSING_MARKER = "Cannot find module";
|
|
1008
|
+
var BUNX_TREE_HINT = [
|
|
1009
|
+
`error: ${DEPLOY_CLI_PACKAGE} could not load its own dependencies \u2014 its `,
|
|
1010
|
+
"bunx install directory is incomplete. Remove it and retry:\n",
|
|
1011
|
+
' rm -rf "${TMPDIR:-/tmp}"/bunx-*-@bnbagent\n',
|
|
1012
|
+
"If it persists, clear Bun's package cache (`bun pm cache rm`), or point ",
|
|
1013
|
+
"BNBAGENT_DEPLOY_COMMAND at a local checkout."
|
|
1014
|
+
].join("");
|
|
1015
|
+
function bunxInstallDir(environment = process.env) {
|
|
1016
|
+
if (deployCommand(environment)[0] !== "bunx") {
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
1020
|
+
return uid === null ? null : path5.join(os.tmpdir(), `bunx-${uid}-${DEPLOY_CLI_PACKAGE}`);
|
|
1021
|
+
}
|
|
1022
|
+
async function bunxTreeIsIncomplete() {
|
|
1023
|
+
const [bin, ...prefix] = deployCommand();
|
|
1024
|
+
const probe = await runCapture(bin, [...prefix, "--help"], {
|
|
1025
|
+
timeoutMs: 12e4
|
|
1026
|
+
});
|
|
1027
|
+
return probe.code !== 0 && `${probe.stderr}
|
|
1028
|
+
${probe.stdout}`.includes(MODULE_MISSING_MARKER);
|
|
1029
|
+
}
|
|
1030
|
+
async function withBunxSelfHeal(hasFailed, attempt) {
|
|
1031
|
+
const first = await attempt();
|
|
1032
|
+
if (!hasFailed(first)) {
|
|
1033
|
+
return first;
|
|
1034
|
+
}
|
|
1035
|
+
const dir = bunxInstallDir();
|
|
1036
|
+
if (dir === null || !await bunxTreeIsIncomplete()) {
|
|
1037
|
+
return first;
|
|
1038
|
+
}
|
|
1039
|
+
if (!fs4.existsSync(dir)) {
|
|
1040
|
+
printErr(BUNX_TREE_HINT);
|
|
1041
|
+
return first;
|
|
1042
|
+
}
|
|
1043
|
+
printErr(
|
|
1044
|
+
`note: ${DEPLOY_CLI_PACKAGE} failed to load its dependencies; clearing its incomplete bunx install and retrying once (${dir}).`
|
|
1045
|
+
);
|
|
1046
|
+
fs4.rmSync(dir, { recursive: true, force: true });
|
|
1047
|
+
const second = await attempt();
|
|
1048
|
+
if (hasFailed(second)) {
|
|
1049
|
+
printErr(BUNX_TREE_HINT);
|
|
1050
|
+
}
|
|
1051
|
+
return second;
|
|
1052
|
+
}
|
|
999
1053
|
async function runDeployCliStream(argv, opts = {}) {
|
|
1000
1054
|
if (!await ensureRunnable()) {
|
|
1001
1055
|
return 127;
|
|
1002
1056
|
}
|
|
1003
1057
|
const [bin, ...prefix] = deployCommand();
|
|
1004
|
-
return
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1058
|
+
return withBunxSelfHeal(
|
|
1059
|
+
(code) => code !== 0,
|
|
1060
|
+
() => runStream(bin, [...prefix, ...argv], {
|
|
1061
|
+
cwd: opts.cwd,
|
|
1062
|
+
env: opts.env ? { ...process.env, ...opts.env } : void 0
|
|
1063
|
+
})
|
|
1064
|
+
);
|
|
1008
1065
|
}
|
|
1009
1066
|
async function runDeployCliJson(argv, opts = {}) {
|
|
1010
1067
|
if (!await ensureRunnable()) {
|
|
1011
1068
|
return { code: 127, data: {} };
|
|
1012
1069
|
}
|
|
1013
1070
|
const [bin, ...prefix] = deployCommand();
|
|
1014
|
-
const { code, stdout } = await
|
|
1015
|
-
|
|
1016
|
-
[...prefix, ...argv, "--json"],
|
|
1017
|
-
{
|
|
1071
|
+
const { code, stdout } = await withBunxSelfHeal(
|
|
1072
|
+
(result) => result.code !== 0,
|
|
1073
|
+
() => runCaptureOut(bin, [...prefix, ...argv, "--json"], {
|
|
1018
1074
|
cwd: opts.cwd,
|
|
1019
1075
|
env: opts.env ? { ...process.env, ...opts.env } : void 0
|
|
1020
|
-
}
|
|
1076
|
+
})
|
|
1021
1077
|
);
|
|
1022
1078
|
let data = {};
|
|
1023
1079
|
const trimmed = stdout.trim();
|
|
@@ -1093,6 +1149,8 @@ export {
|
|
|
1093
1149
|
recipeModeOf,
|
|
1094
1150
|
DEFAULT_B402_PRICE_USD,
|
|
1095
1151
|
DEFAULT_B402_TESTNET_BASE_URL,
|
|
1152
|
+
B402_DEVELOPER_ACCOUNT_URL,
|
|
1153
|
+
B402_PAID_ONBOARDING_GUIDANCE,
|
|
1096
1154
|
x402SellerPricingState,
|
|
1097
1155
|
normalizeB402PriceUsd,
|
|
1098
1156
|
isB402TestnetBaseUrl,
|
|
@@ -1124,6 +1182,7 @@ export {
|
|
|
1124
1182
|
buildDeploySpec,
|
|
1125
1183
|
renderSecretEnvFile,
|
|
1126
1184
|
withDeployFiles,
|
|
1185
|
+
bunxInstallDir,
|
|
1127
1186
|
runDeployCliStream,
|
|
1128
1187
|
runDeployCliJson,
|
|
1129
1188
|
trialFromDeployCliJson,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
bnbEnv,
|
|
9
9
|
bnbPlatformApiUrl,
|
|
10
10
|
buildDeploySpec,
|
|
11
|
+
bunxInstallDir,
|
|
11
12
|
deployCommand,
|
|
12
13
|
deployEndpointValue,
|
|
13
14
|
providerPassthrough,
|
|
@@ -17,8 +18,8 @@ import {
|
|
|
17
18
|
runPlatformAccountCommand,
|
|
18
19
|
trialFromDeployCliJson,
|
|
19
20
|
withDeployFiles
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-VEOOFDSF.js";
|
|
22
|
+
import "./chunk-RO726HJG.js";
|
|
22
23
|
export {
|
|
23
24
|
BNB_PLATFORM_API_URL,
|
|
24
25
|
BNB_PLATFORM_API_URL_ENV,
|
|
@@ -28,6 +29,7 @@ export {
|
|
|
28
29
|
bnbEnv,
|
|
29
30
|
bnbPlatformApiUrl,
|
|
30
31
|
buildDeploySpec,
|
|
32
|
+
bunxInstallDir,
|
|
31
33
|
deployCommand,
|
|
32
34
|
deployEndpointValue,
|
|
33
35
|
providerPassthrough,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bnbagent/studio-cli",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.5",
|
|
4
4
|
"description": "The `bag` CLI: scaffold, run, deploy, and monetize a single seller agent on BNB Chain (ERC-8004 identity, ERC-8183 commerce, x402 payments).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"skills",
|
|
21
21
|
"recipes",
|
|
22
|
+
"README.md",
|
|
22
23
|
"DISCLAIMER.md"
|
|
23
24
|
],
|
|
24
25
|
"bin": {
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
"tar": "^7.4.0",
|
|
38
39
|
"viem": "^2.54.0",
|
|
39
40
|
"yaml": "^2.9.0",
|
|
40
|
-
"@bnbagent/studio-runtime": "0.0.6-alpha.
|
|
41
|
+
"@bnbagent/studio-runtime": "0.0.6-alpha.5"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@a2a-js/sdk": "^0.3.14",
|
|
@@ -59,14 +59,22 @@ flow now auto-adds dispute_window).
|
|
|
59
59
|
- For a paid job, the wallet has ≥ 0.05 tBNB (gas) and enough U for the budget
|
|
60
60
|
plus slack. On BSC testnet the
|
|
61
61
|
ERC-8183 kernel writes (`createJob` / `fund` deposit / `settle` …) are
|
|
62
|
-
gas-sponsored via the SDK's MegaFuel paymaster
|
|
62
|
+
gas-sponsored via the SDK's MegaFuel paymaster **when they target the
|
|
63
|
+
canonical contracts**, so you spend far less tBNB than
|
|
63
64
|
that — but **not zero**: `fund` sends an ERC-20 `approve` (a token call, not
|
|
64
65
|
sponsored) when the token allowance is too low — typically just the first fund,
|
|
65
66
|
since studio approves a floored cap that later jobs reuse. Keep a little tBNB for
|
|
66
|
-
it. (Mainnet is never sponsored.)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
it. (Mainnet is never sponsored.) Sponsorship is granted per target contract
|
|
68
|
+
by the paymaster policy: a custom/QA stack selected via the
|
|
69
|
+
`ERC8183_*_ADDRESS` overrides is normally **not** covered, so every write
|
|
70
|
+
self-pays gas (the SDK logs `… is not sponsorable on this network;
|
|
71
|
+
self-paying gas` and falls back automatically) — keep tBNB for the whole
|
|
72
|
+
flow, or set `BNBAGENT_USE_PAYMASTER=0` to skip the per-transaction
|
|
73
|
+
sponsorship probe and self-pay directly. For a FREE job, use `--budget-u 0`:
|
|
74
|
+
no U balance, ERC-20 approval, or token escrow is needed, but the ERC-8183
|
|
75
|
+
writes still need the selected gas/paymaster path and a zero-price-compatible
|
|
76
|
+
commerce/router/policy stack — today that means a custom stack, so expect the
|
|
77
|
+
writes to self-pay gas as above.
|
|
70
78
|
- You know the **provider's wallet address** (the seller agent's address)
|
|
71
79
|
- The seller is **reachable** (its A2A agent is deployed somewhere); discoverable
|
|
72
80
|
via the provider's `bag erc8004 resolve <agent_id>` endpoint URI
|
|
@@ -222,6 +222,9 @@ When deeper protocol details are needed:
|
|
|
222
222
|
|
|
223
223
|
- Never bypass the policy with `_DANGEROUS_*` calls in agent code. They exist for tests and incident response.
|
|
224
224
|
- Never instruct the user to widen caps / allowlists "to make it work" without explaining the security tradeoff.
|
|
225
|
-
-
|
|
225
|
+
- Persist the wallet password only in the generated, gitignored, owner-only
|
|
226
|
+
`.studio/.env.local` for local use or a managed secret channel for deploys;
|
|
227
|
+
never put it in argv, application code, logs, or chat.
|
|
228
|
+
- Never persist signed payloads outside the keystore.
|
|
226
229
|
- Always run `bag wallet policy show` after editing `[wallet.signing]` to confirm the change took effect.
|
|
227
230
|
- Always restart the process — SigningPolicy is captured at wallet construction.
|
|
@@ -64,7 +64,8 @@ For seller job-lifecycle decisions (settle / submit / dispute defense), read
|
|
|
64
64
|
```bash
|
|
65
65
|
# Configure secrets (idempotent — does NOT duplicate existing keys).
|
|
66
66
|
# These write into .studio/.env.local (the Agent's secrets):
|
|
67
|
-
|
|
67
|
+
# Edit .studio/.env.local and set WALLET_PASSWORD there; bag auto-loads it.
|
|
68
|
+
# Never put the wallet password on a command line.
|
|
68
69
|
bag env set OPENROUTER_API_KEY <your key> # or whichever provider app/agent/studio.toml [llm] uses
|
|
69
70
|
|
|
70
71
|
# From the workspace root — `bag dev` serves the selected protocol:
|
|
@@ -129,7 +130,7 @@ Returns a rich table of checks across the `app/agent/` sub-project:
|
|
|
129
130
|
| app/agent/studio.toml parseable | Missing or syntax error | `bag init` to regenerate, or hand-fix TOML |
|
|
130
131
|
| Agent entrypoint imports | Protocol entrypoint raises on load (`src/main.ts` for A2A, `src/mcpMain.ts` for MCP) | Fix the error printed by the check (often a missing env var or a broken import) |
|
|
131
132
|
| wallet keystore | No `<workspace>/.studio/wallets/*.json` | `bag wallet new` (writes to the workspace root keystore dir) |
|
|
132
|
-
| WALLET_PASSWORD env | Not in `.studio/.env.local` / not exported |
|
|
133
|
+
| WALLET_PASSWORD env | Not in `.studio/.env.local` / not exported | Edit the owner-only `.studio/.env.local`; never pass the password on argv |
|
|
133
134
|
| LLM provider key | API key env not set | Edit `.studio/.env.local` or export the right `*_API_KEY` |
|
|
134
135
|
| Network reachable | RPC down/wrong URL | Override via `STUDIO_BSC_TESTNET_RPC=...` (testnet) / `STUDIO_BSC_RPC=...` (mainnet) — per-network env vars read by `@bnbagent/studio-runtime/networks` `getNetwork` |
|
|
135
136
|
| Wallet tBNB balance | 0 tBNB | Faucet: testnet.bnbchain.org/faucet-smart |
|
|
@@ -309,10 +309,10 @@ Step 6b when `storage=ipfs`):
|
|
|
309
309
|
- **twak** (fully supported — opt in with `--wallet-kind twak`): `bag init` writes `[wallet].twak_home =
|
|
310
310
|
"../../.studio/twak"` — a **project-dedicated** wallet isolated from your
|
|
311
311
|
main `~/.twak`, so a deploy never pushes the main wallet's key material to
|
|
312
|
-
Secrets Manager.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
Secrets Manager. Setting it up is a ONE-TIME job: the user runs the
|
|
313
|
+
interactive 3-step `twak setup` wizard themselves in their own terminal,
|
|
314
|
+
then `bag wallet twak-init` creates the wallet (so the password never goes
|
|
315
|
+
into a command line). Start with the wizard:
|
|
316
316
|
```bash
|
|
317
317
|
HOME=<workspace>/.studio/twak twak setup
|
|
318
318
|
```
|
|
@@ -332,17 +332,21 @@ Step 6b when `storage=ipfs`):
|
|
|
332
332
|
WalletConnect` = your main wallet.)
|
|
333
333
|
|
|
334
334
|
```bash
|
|
335
|
-
|
|
335
|
+
cd app/agent && bag wallet twak-init # hidden prompt
|
|
336
|
+
printf %s "$PW" | bag wallet twak-init --password-stdin # CI / scripts
|
|
336
337
|
```
|
|
337
338
|
Use UPPER + lower + digit; put that same password in `.studio/.env.local`
|
|
338
|
-
as `TWAK_WALLET_PASSWORD`. `--no-keychain
|
|
339
|
-
keychain (no macOS prompt); studio unlocks via
|
|
340
|
-
macOS prompt *loops
|
|
341
|
-
then re-run with `--no-keychain`.)
|
|
339
|
+
as `TWAK_WALLET_PASSWORD`. `twak-init` passes `--no-keychain`, which keeps
|
|
340
|
+
the password out of the OS keychain (no macOS prompt); studio unlocks via
|
|
341
|
+
the env. (If a macOS prompt *loops* on a manual `twak` run, do NOT "Reset
|
|
342
|
+
Default Keychain" — `pkill -9 -f twak`, then re-run with `--no-keychain`.)
|
|
343
|
+
It also adopts the address into `studio.toml` and echoes it — confirm it's
|
|
344
|
+
the intended wallet before funding/deploy, and there is no separate
|
|
345
|
+
`bag wallet new` step. The manual equivalent is
|
|
346
|
+
`HOME=<workspace>/.studio/twak twak wallet create --password <StrongPw> --no-keychain`
|
|
347
|
+
followed by `bag wallet new`. Full detail: the
|
|
342
348
|
`bnbagent-studio-using-twak-wallet.md` reference (in the router skill's `references/` directory).
|
|
343
|
-
|
|
344
|
-
`studio.toml` (and echoes it — confirm it's the intended wallet before
|
|
345
|
-
funding/deploy). To reuse an EXISTING wallet across agents, scaffold with
|
|
349
|
+
To reuse an EXISTING wallet across agents, scaffold with
|
|
346
350
|
`bag init --twak-home <path-to-its-home>` instead. Reusing your main
|
|
347
351
|
`~/.twak` is opt-in only (`--twak-home ~`) and discouraged. Full detail:
|
|
348
352
|
the `bnbagent-studio-using-twak-wallet.md` reference (in the router skill's `references/` directory).
|
|
@@ -3,7 +3,7 @@ name: bnbagent-studio-selling-via-b402
|
|
|
3
3
|
description: When the user wants a bnbagent-studio agent to sell paid or FREE HTTP requests through the B402-backed x402 rail. Owns the explicit pricing choice and, for PAID mode, per-agent merchant onboarding, RSA key preparation, egress-IP allowlisting, sandbox/production separation, B402 environment setup, seller status checks, and activation by redeploy (managed platform or self-hosted AgentCore).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> **Reference file** of the `bnbagent-studio` router skill
|
|
6
|
+
> **Reference file** of the `bnbagent-studio` router skill, installed at
|
|
7
7
|
> `bnbagent-studio/references/` and loaded on demand (not a standalone skill).
|
|
8
8
|
> Route here via the router's decision tree.
|
|
9
9
|
|
|
@@ -19,10 +19,9 @@ agent wallets, or mix sandbox and production values.
|
|
|
19
19
|
- The agent wallet already exists. In PAID mode its address receives U.
|
|
20
20
|
- The project targets the managed platform or self-hosted AgentCore
|
|
21
21
|
(azure-foundry cannot activate the rail).
|
|
22
|
-
- PAID managed platform only:
|
|
23
|
-
platform
|
|
24
|
-
|
|
25
|
-
`bag platform token`.
|
|
22
|
+
- PAID managed platform only: an interactive GitHub-login session from
|
|
23
|
+
`bag platform login` is available for reading the platform Relay egress
|
|
24
|
+
IPs. A `bnbk_…` CI token does not satisfy this endpoint's GitHub-user check.
|
|
26
25
|
- `[payments.x402_seller]` exists. If not, run
|
|
27
26
|
`bag x402 sell init`.
|
|
28
27
|
|
|
@@ -59,66 +58,86 @@ call under the
|
|
|
59
58
|
the runtime signs each request automatically once the credentials are stored.
|
|
60
59
|
|
|
61
60
|
Work from the workspace root. Keep private material under `.studio/`, which is
|
|
62
|
-
excluded from source and deploy artifacts
|
|
61
|
+
excluded from source and deploy artifacts. Generate a separate key pair for
|
|
62
|
+
each environment:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
umask 077
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
openssl
|
|
69
|
-
-out .studio/b402/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
b402_env=sandbox
|
|
67
|
+
mkdir -p ".studio/b402/$b402_env"
|
|
68
|
+
openssl genpkey -algorithm RSA \
|
|
69
|
+
-out ".studio/b402/$b402_env/private.pem" \
|
|
70
|
+
-pkeyopt rsa_keygen_bits:1024
|
|
71
|
+
openssl pkey -in ".studio/b402/$b402_env/private.pem" -pubout \
|
|
72
|
+
-out ".studio/b402/$b402_env/public.pem"
|
|
73
|
+
openssl pkey -in ".studio/b402/$b402_env/private.pem" -pubout -outform DER |
|
|
74
|
+
openssl base64 -A > ".studio/b402/$b402_env/public.der.b64"
|
|
75
|
+
openssl pkcs8 -topk8 -nocrypt \
|
|
76
|
+
-in ".studio/b402/$b402_env/private.pem" -outform DER |
|
|
77
|
+
openssl base64 -A > ".studio/b402/$b402_env/private.der.b64"
|
|
78
|
+
chmod 600 ".studio/b402/$b402_env/private.pem" \
|
|
79
|
+
".studio/b402/$b402_env/private.der.b64"
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
Do not print the private key or its base64 form. Submit only the public key
|
|
78
83
|
material to B402.
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
The current B402 request-signing contract explicitly requires a 1024-bit RSA
|
|
86
|
+
key and RSA-SHA256. Follow that protocol requirement even if a larger RSA key
|
|
87
|
+
would normally be preferred elsewhere. Repeat with `b402_env=production`
|
|
88
|
+
instead of reusing the sandbox pair.
|
|
84
89
|
|
|
85
90
|
## Collect the IP allowlist
|
|
86
91
|
|
|
87
|
-
B402 allowlists the merchant's **outbound** (egress) IPs
|
|
92
|
+
B402 allowlists the merchant's **outbound** (egress) IPs, the addresses the
|
|
88
93
|
agent's facilitator calls come FROM. Submit every part that applies to your
|
|
89
94
|
deployment target:
|
|
90
95
|
|
|
91
|
-
1. **Platform egress IPs (managed-platform deploys)
|
|
92
|
-
|
|
93
|
-
the
|
|
96
|
+
1. **Platform Relay egress IPs (managed-platform deploys)**: the addresses
|
|
97
|
+
that the platform B402 Relay uses to reach the facilitator. The managed
|
|
98
|
+
deployment worker points only the AgentCore runtime copy of
|
|
99
|
+
`B402_BASE_URL` at this Relay. Refresh and read the interactive session
|
|
100
|
+
without printing its bearer:
|
|
94
101
|
|
|
95
102
|
```bash
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
bag platform whoami >/dev/null
|
|
104
|
+
platform_session="$HOME/.bnbagent-deploy/bnb/session.json"
|
|
105
|
+
platform_access_token="$(jq -er '.access_token' "$platform_session")"
|
|
106
|
+
platform_api_url="$(jq -er '.apiUrl' "$platform_session")"
|
|
107
|
+
curl -H "Authorization: Bearer $platform_access_token" \
|
|
108
|
+
"$platform_api_url/v1/b402/whitelist-ips"
|
|
109
|
+
unset platform_access_token
|
|
98
110
|
# → {"whitelist_ips": ["13.115.15.190", …], "cache_ttl_seconds": 300}
|
|
99
111
|
```
|
|
100
112
|
|
|
101
113
|
Submit every address in `whitelist_ips`. The list is served with a short
|
|
102
|
-
cache TTL and can rotate
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
cache TTL and can rotate, so re-read it right before submitting the form.
|
|
115
|
+
The login session is stored mode `0600`. Do not echo, log, or paste its
|
|
116
|
+
access or refresh token. `bnbk_` tokens from `bag platform token` are
|
|
117
|
+
deliberately rejected by this GitHub-login-only endpoint.
|
|
105
118
|
|
|
106
|
-
2. **Your local public IP
|
|
119
|
+
2. **Your local public IP**: required so a local `bag dev` run can reach
|
|
107
120
|
B402:
|
|
108
121
|
|
|
109
122
|
```bash
|
|
110
123
|
curl ipinfo.io/ip
|
|
111
124
|
```
|
|
112
125
|
|
|
113
|
-
3. **Self-hosted AgentCore egress (self-deploys)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
3. **Self-hosted AgentCore egress (self-deploys)**: operate a restricted B402
|
|
127
|
+
Relay on a host with a fixed public egress IP, such as a user-managed VPS,
|
|
128
|
+
and submit that IP. Set the runtime `B402_BASE_URL` to the Relay base URL.
|
|
129
|
+
The Relay exposes only
|
|
130
|
+
`supported`, `verify`, and `settle`, fixes the upstream facilitator, and
|
|
131
|
+
forwards the signed body and Tesla header allowlist without holding the
|
|
132
|
+
merchant private key or automatically retrying a settlement transport
|
|
133
|
+
failure. See the
|
|
134
|
+
[self-hosted x402 gateway guide](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md)
|
|
135
|
+
for a TypeScript example.
|
|
136
|
+
|
|
137
|
+
As an alternative, use AWS-supported AgentCore VPC mode with a private subnet, NAT
|
|
138
|
+
Gateway, and Elastic IP. Submit the Elastic IP and keep
|
|
139
|
+
`B402_BASE_URL` pointed at the facilitator. Studio does not deploy or
|
|
140
|
+
manage that AWS network.
|
|
122
141
|
|
|
123
142
|
Do not add the public inbound gateway IP, a transient build-runner IP, or
|
|
124
143
|
guessed addresses. If the whitelist endpoint is unreachable, stop onboarding
|
|
@@ -134,10 +153,11 @@ production. Fill the form as follows:
|
|
|
134
153
|
| Field | Value |
|
|
135
154
|
|---|---|
|
|
136
155
|
| Business Name | Your agent or business display name |
|
|
137
|
-
|
|
|
138
|
-
| Wallet | The agent wallet
|
|
139
|
-
|
|
|
140
|
-
| IP allowlist |
|
|
156
|
+
| Email | Primary contact email address |
|
|
157
|
+
| Wallet address | The agent wallet for that environment |
|
|
158
|
+
| Public key | The contents of `.studio/b402/<environment>/public.der.b64` |
|
|
159
|
+
| IP allowlist | For managed deploys, every platform Relay IP from `/v1/b402/whitelist-ips`; for self-hosted deploys, the user's Relay or NAT Elastic IP; add the local public IP when `bag dev` must reach B402 directly |
|
|
160
|
+
| Webhook callback URL | Supply only when the integration uses callbacks |
|
|
141
161
|
|
|
142
162
|
Keep the two environments isolated:
|
|
143
163
|
|
|
@@ -152,20 +172,28 @@ Open the workspace `.studio/.env.local` in an editor and fill exactly four
|
|
|
152
172
|
values:
|
|
153
173
|
|
|
154
174
|
```dotenv
|
|
155
|
-
B402_BASE_URL=
|
|
175
|
+
B402_BASE_URL=
|
|
156
176
|
B402_CLIENT_ID=
|
|
157
177
|
B402_ACCESS_TOKEN=
|
|
158
178
|
B402_PRIVATE_KEY_B64=
|
|
159
179
|
```
|
|
160
180
|
|
|
161
181
|
Copy the single-line DER value from
|
|
162
|
-
`.studio/b402
|
|
163
|
-
|
|
164
|
-
|
|
182
|
+
`.studio/b402/<environment>/private.der.b64` into `B402_PRIVATE_KEY_B64`.
|
|
183
|
+
`B402_BASE_URL`, `B402_CLIENT_ID`, and `B402_ACCESS_TOKEN` are the values
|
|
184
|
+
issued together for that environment. Do not include any value in shell
|
|
185
|
+
history, terminal output, source files, TOML, screenshots, or support tickets.
|
|
165
186
|
|
|
166
187
|
`B402_PRIVATE_KEY` accepts the PEM representation as an alternative. Keep
|
|
167
188
|
exactly one private-key form; do not set both.
|
|
168
189
|
|
|
190
|
+
For the current managed BSC Testnet path, the platform-supported upstream is
|
|
191
|
+
`https://qacb.sdtaop.com`; the worker projects only the AgentCore runtime copy
|
|
192
|
+
to its Relay. For a generic self-hosted environment, use the authenticated
|
|
193
|
+
base URL issued during onboarding. With your own Relay, set the runtime
|
|
194
|
+
`B402_BASE_URL` to that Relay base URL and configure the Relay's fixed upstream
|
|
195
|
+
to the issued URL.
|
|
196
|
+
|
|
169
197
|
## Verify and activate
|
|
170
198
|
|
|
171
199
|
For PAID mode, check names and presence without exposing values:
|
|
@@ -197,22 +225,34 @@ On the managed platform the deploy summary must say `x402 rail is ACTIVE` (or
|
|
|
197
225
|
`ACTIVE in FREE mode`) and print the anonymous `/x402` URL. On a self-hosted
|
|
198
226
|
AgentCore deploy it says `x402 rail is ACTIVE (self-hosted AgentCore)` or
|
|
199
227
|
`ACTIVE in FREE mode (self-hosted AgentCore)`: the rail runs in-process, but
|
|
200
|
-
there is no anonymous URL
|
|
201
|
-
JSON
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
228
|
+
there is no anonymous URL. Operate your own HTTP front that relays envelope-v1
|
|
229
|
+
JSON through an authenticated AgentCore invocation. The default Bag
|
|
230
|
+
self-deploy uses Cognito OAuth over raw HTTPS; AWS SDK/SigV4 is only for a
|
|
231
|
+
runtime deliberately configured with IAM authorization. PAID also needs your
|
|
232
|
+
own fixed-egress B402 Relay or equivalent network path. The complete gateway
|
|
233
|
+
wrapper, response parser, Relay example, and direct-invocation fallback are in
|
|
234
|
+
the
|
|
235
|
+
[self-hosted x402 gateway guide](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md).
|
|
236
|
+
A dormant or forced-dormant summary means the rail was not activated; fix the
|
|
237
|
+
named credential, runtime, network, or tunnel condition and redeploy.
|
|
205
238
|
|
|
206
239
|
## Hard rules
|
|
207
240
|
|
|
208
241
|
- Never log or print any B402 value or private key.
|
|
209
242
|
- Never put a B402 value in `studio.toml` or a deploy descriptor.
|
|
210
|
-
- Never
|
|
243
|
+
- Never replay a paid HTTP request whose outcome is unknown. Follow
|
|
211
244
|
`docs/guides/x402-selling.md` and reconcile `(nonce, network, payer)` first.
|
|
245
|
+
- Binance `/settle` is asynchronous. A parseable `success: false` response
|
|
246
|
+
with a transaction is pending and requires an idempotent poll with the same
|
|
247
|
+
settlement payload. Studio v0.0.6 and the latest
|
|
248
|
+
`@bnb-chain/b402@0.1.0` do not yet perform that poll; they classify pending
|
|
249
|
+
as unknown. Do not claim current mainnet readiness until this is updated.
|
|
212
250
|
- Settlement happens before work. A later work failure retains the payment and
|
|
213
251
|
does not trigger an automatic refund.
|
|
214
252
|
- The rail activates on AgentCore targets only (managed platform or
|
|
215
253
|
self-hosted); azure-foundry stays forced dormant.
|
|
216
|
-
- PAID payout wallets must use `wallet.kind` `evm-local`
|
|
217
|
-
|
|
254
|
+
- PAID B402 payout wallets must use `wallet.kind` `evm-local` (private-key
|
|
255
|
+
wallet stored as an encrypted keystore) or `twak`. Altana is unsupported.
|
|
256
|
+
FREE x402 bypasses B402, has no payout, and must not be described as Altana
|
|
257
|
+
B402 support.
|
|
218
258
|
- Never describe FREE as a zero-value B402 settlement. It bypasses B402.
|
|
@@ -199,8 +199,11 @@ bag deploy destroy --provider aws --execute # delegated teardown (add --purge)
|
|
|
199
199
|
- b402/x402 selling on self-hosted AgentCore: the rail activates in-process
|
|
200
200
|
with complete B402 credentials for PAID, or without them when explicit zero
|
|
201
201
|
selects FREE. There is no anonymous URL — operate your own HTTP front that
|
|
202
|
-
relays envelope-v1
|
|
203
|
-
|
|
202
|
+
relays envelope-v1 through the runtime's configured authorizer. The default
|
|
203
|
+
Bag deploy uses Cognito OAuth over HTTPS, not SDK/SigV4. PAID also needs a
|
|
204
|
+
fixed-egress B402 Relay or VPC/NAT/Elastic-IP path. See
|
|
205
|
+
`bnbagent-studio-selling-via-b402` and
|
|
206
|
+
`docs/guides/self-hosted-x402-gateway.md`.
|
|
204
207
|
- `bag deploy --help` / `bag deploy <command> --help` (authoritative for commands + flags)
|
|
205
208
|
- `agentcore/agentcore.json` — name (resource naming continuity), protocol, authorizer, envVars
|
|
206
209
|
- `agentcore/aws-targets.json` — AWS account + region
|