@bnbagent/studio-cli 0.0.10 → 0.0.11-alpha.1

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.
Files changed (36) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +2 -2
  3. package/dist/_agentcoreName-DZDWEYD3.js +0 -0
  4. package/dist/_twak-4XF4H5PL.js +0 -0
  5. package/dist/bag.js +317 -124
  6. package/dist/chunk-RO726HJG.js +0 -0
  7. package/dist/{chunk-YFEM4564.js → chunk-TTPOH453.js} +79 -37
  8. package/dist/chunk-U7IDQ3K5.js +0 -0
  9. package/dist/{deployCli-NJFCWBSF.js → deployCli-K55GXDVO.js} +1 -1
  10. package/package.json +11 -12
  11. package/recipes/runtimes/agentcore/code/{{PKG}}/Dockerfile.tmpl +20 -21
  12. package/recipes/runtimes/agentcore/code/{{PKG}}/executor.ts.tmpl +36 -0
  13. package/recipes/runtimes/agentcore/code/{{PKG}}/unifiedMain.ts.tmpl +620 -0
  14. package/recipes/runtimes/agentcore/recipe.toml +3 -3
  15. package/recipes/runtimes/azure-foundry/code/{{PKG}}/Dockerfile.tmpl +25 -23
  16. package/recipes/runtimes/azure-foundry/code/{{PKG}}/agentCard.ts.tmpl +16 -12
  17. package/recipes/runtimes/azure-foundry/code/{{PKG}}/executor.ts.tmpl +72 -393
  18. package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +160 -43
  19. package/recipes/runtimes/azure-foundry/code/{{PKG}}/sellerCore.ts.tmpl +504 -0
  20. package/recipes/runtimes/azure-foundry/code/{{PKG}}/unifiedMain.ts.tmpl +620 -0
  21. package/recipes/runtimes/azure-foundry/recipe.toml +19 -11
  22. package/recipes/x402-buyer/code/{{PKG}}/x402Buyer.ts.tmpl +6 -4
  23. package/skills/bnbagent-studio.md +2 -2
  24. package/skills/references/bnbagent-studio-adding-to-project.md +1 -1
  25. package/skills/references/bnbagent-studio-buying-from-bazaar.md +1 -1
  26. package/skills/references/bnbagent-studio-operating.md +4 -4
  27. package/skills/references/bnbagent-studio-scaffolding-agent.md +4 -4
  28. package/skills/references/bnbagent-studio-selling-via-8183.md +3 -3
  29. package/skills/references/bnbagent-studio-selling-via-b402.md +2 -2
  30. package/skills/references/bnbagent-studio-use-aws-agentcore.md +1 -1
  31. package/skills/references/bnbagent-studio-use-azure-foundry.md +3 -3
  32. package/skills/references/bnbagent-studio-use-bnb-trial.md +1 -1
  33. package/skills/references/bnbagent-studio-wiring-llm-tools.md +3 -3
  34. package/recipes/runtimes/agentcore/code/{{PKG}}/main.ts.tmpl +0 -347
  35. package/recipes/runtimes/azure-foundry/code/{{PKG}}/foundryMain.ts.tmpl +0 -422
  36. package/recipes/runtimes/azure-foundry/code/{{PKG}}/main.ts.tmpl +0 -196
package/dist/bag.js CHANGED
@@ -6,17 +6,21 @@ import {
6
6
  CliExit,
7
7
  DEFAULT_B402_PRICE_USD,
8
8
  DEFAULT_B402_TESTNET_BASE_URL,
9
+ LEGACY_ENTRY_STEMS,
10
+ X402_CAPABLE_RUNTIMES,
9
11
  act,
10
12
  agentcoreFlavor,
11
13
  b402Credentials,
12
14
  bnbEnv,
13
15
  bnbPlatformApiUrl,
16
+ checkBunx,
14
17
  checkDocker,
15
18
  checkTwak,
16
19
  commerceRails,
17
20
  deployChecks,
18
21
  deployEndpointValue,
19
22
  devPortOf,
23
+ dryRunBundle,
20
24
  entryStemOf,
21
25
  getEnvVar,
22
26
  hasA2aFace,
@@ -44,7 +48,7 @@ import {
44
48
  x402SellerIsFree,
45
49
  x402SellerPricingState,
46
50
  x402SellerUsesB402
47
- } from "./chunk-YFEM4564.js";
51
+ } from "./chunk-TTPOH453.js";
48
52
  import {
49
53
  TWAK_CLI_MIN_VERSION,
50
54
  TWAK_CLI_VERSION,
@@ -222,7 +226,7 @@ import {
222
226
  var CAMPAIGN_DOC_URL = "https://www.bnbchain.org/en/blog/bnb-agent-studio-is-live-on-bnb-chain-ai-agents-from-one-prompt";
223
227
  var CAMPAIGN_CHECK_TIMEOUT_MS = 6e3;
224
228
  async function fetchCampaignActive() {
225
- const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-NJFCWBSF.js");
229
+ const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-K55GXDVO.js");
226
230
  const controller = new AbortController();
227
231
  const timer = setTimeout(() => controller.abort(), CAMPAIGN_CHECK_TIMEOUT_MS);
228
232
  try {
@@ -454,8 +458,8 @@ function packageRoot() {
454
458
  }
455
459
  }
456
460
  function studioCliVersion() {
457
- if ("0.0.10") {
458
- return "0.0.10";
461
+ if ("0.0.11-alpha.1") {
462
+ return "0.0.11-alpha.1";
459
463
  }
460
464
  const file = path2.join(packageRoot(), "package.json");
461
465
  const pkg = JSON.parse(fs3.readFileSync(file, "utf-8"));
@@ -491,15 +495,34 @@ import { parse, stringify } from "smol-toml";
491
495
 
492
496
  // src/cli/utils/prompt.ts
493
497
  import * as readline from "readline/promises";
498
+ var PromptCancelled = class extends Error {
499
+ constructor() {
500
+ super("prompt cancelled");
501
+ this.name = "PromptCancelled";
502
+ }
503
+ };
504
+ async function ask(rl, question) {
505
+ try {
506
+ return await Promise.race([
507
+ rl.question(question),
508
+ new Promise((_, reject) => {
509
+ rl.once("SIGINT", () => reject(new PromptCancelled()));
510
+ })
511
+ ]);
512
+ } catch (exc) {
513
+ if (exc instanceof PromptCancelled) {
514
+ throw exc;
515
+ }
516
+ return "";
517
+ }
518
+ }
494
519
  async function promptUser(question) {
495
520
  const rl = readline.createInterface({
496
521
  input: process.stdin,
497
522
  output: process.stdout
498
523
  });
499
524
  try {
500
- return await rl.question(question);
501
- } catch {
502
- return "";
525
+ return await ask(rl, question);
503
526
  } finally {
504
527
  rl.close();
505
528
  }
@@ -510,9 +533,7 @@ async function promptUserStderr(question) {
510
533
  output: process.stderr
511
534
  });
512
535
  try {
513
- return await rl.question(question);
514
- } catch {
515
- return "";
536
+ return await ask(rl, question);
516
537
  } finally {
517
538
  rl.close();
518
539
  }
@@ -526,9 +547,7 @@ async function promptHidden(question) {
526
547
  terminal: false
527
548
  });
528
549
  try {
529
- return await rl.question("");
530
- } catch {
531
- return "";
550
+ return await ask(rl, "");
532
551
  } finally {
533
552
  rl.close();
534
553
  process.stderr.write("\n");
@@ -3455,13 +3474,13 @@ function unavailableProvidersForProject(root) {
3455
3474
  const cfg = loadStudioToml6(path13.join(agentRoot2, "studio.toml"));
3456
3475
  runtime = text(tableOf3(cfg, "stack").runtime) ?? "agentcore";
3457
3476
  const faces = stackFaces(tableOf3(cfg, "stack"), tableOf3(cfg, "payments"));
3458
- azureProtocolUnsupported = !hasA2aFace(faces) || hasMcpFace(faces);
3477
+ azureProtocolUnsupported = hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces);
3459
3478
  } catch {
3460
3479
  return {};
3461
3480
  }
3462
3481
  if (runtime === "azure-foundry") {
3463
3482
  const reason = "this project uses a different container adapter; scaffold an agentcore project before selecting this provider";
3464
- const azureProtocolReason = "Azure Foundry deploy currently supports A2A projects only; use an A2A azure-foundry scaffold or AgentCore for MCP";
3483
+ const azureProtocolReason = "Azure Foundry deploy supports A2A and X402 seller projects; use AgentCore for MCP";
3465
3484
  return {
3466
3485
  aws: reason,
3467
3486
  ...azureProtocolUnsupported ? {
@@ -4488,22 +4507,23 @@ var checkEntrypointAndDockerfile = async (root, _target) => {
4488
4507
  const findings = [];
4489
4508
  const cfg = loadStudioCfg(deployWorkspaceRoot(root));
4490
4509
  const faces = stackFaces(tableOf4(cfg, "stack"), tableOf4(cfg, "payments"));
4491
- if (!hasA2aFace(faces) || hasMcpFace(faces)) {
4510
+ if (hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces)) {
4492
4511
  return [
4493
4512
  {
4494
4513
  level: "CRITICAL",
4495
4514
  name: "azure_protocol_supported",
4496
- message: "azure-foundry deploy currently supports protocol A2A only. The delegated Foundry version declares the invocations contract (:8088, /readiness, /invocations), not native MCP /mcp. Re-scaffold with --protocols A2A (optionally X402) or use AgentCore for MCP.",
4515
+ message: "azure-foundry deploy supports the A2A carrier, including X402-only seller configs. The delegated Foundry version declares the invocations contract (:8088, /readiness, /invocations), not native MCP /mcp. Re-scaffold with --protocols A2A, X402, or A2A,X402; or use AgentCore for MCP.",
4497
4516
  details: { faces }
4498
4517
  }
4499
4518
  ];
4500
4519
  }
4501
- const entry = "src/foundryMain.ts";
4520
+ const entryCandidates = ["src/unifiedMain.ts", "src/foundryMain.ts"];
4521
+ const entry = entryCandidates.find((c2) => isFile6(path17.join(agentRoot2, c2))) ?? entryCandidates[0];
4502
4522
  if (!isFile6(path17.join(agentRoot2, entry))) {
4503
4523
  findings.push({
4504
4524
  level: "CRITICAL",
4505
4525
  name: "azure_agent_entrypoint",
4506
- message: `app/agent/${entry} is missing \u2014 the A2A Foundry entrypoint the container image runs. Re-scaffold with \`bag init \u2026 --runtime azure-foundry\`.`,
4526
+ message: `app/agent/${entry} is missing \u2014 the A2A entrypoint the container image runs. Re-scaffold with \`bag init \u2026 --runtime azure-foundry\`.`,
4507
4527
  details: { entrypoint: entry }
4508
4528
  });
4509
4529
  }
@@ -4606,7 +4626,11 @@ import * as walletRt2 from "@bnbagent/studio-runtime/wallet";
4606
4626
  import { generateText } from "ai";
4607
4627
 
4608
4628
  // src/cli/_pieverseHelpers.ts
4609
- import { fromRaw as fromRaw2, getNetwork as getNetwork3 } from "@bnbagent/studio-runtime/networks";
4629
+ import {
4630
+ BSC_TESTNET_U_FAUCET_URL,
4631
+ fromRaw as fromRaw2,
4632
+ getNetwork as getNetwork3
4633
+ } from "@bnbagent/studio-runtime/networks";
4610
4634
  import {
4611
4635
  siweLogin
4612
4636
  } from "@bnbagent/studio-runtime/pieverse";
@@ -4659,9 +4683,7 @@ function printUBalanceShortfall(walletAddress, currentRaw, neededRaw, neededUsd,
4659
4683
  ` swap on PancakeSwap: https://pancakeswap.finance/swap?outputCurrency=${u.address}`
4660
4684
  );
4661
4685
  } else {
4662
- printErr(
4663
- " testnet U faucet URL is TBD \u2014 see docs/guides/pieverse-integration.md \xA76"
4664
- );
4686
+ printErr(` testnet U faucet: ${BSC_TESTNET_U_FAUCET_URL}`);
4665
4687
  }
4666
4688
  }
4667
4689
  function writeEnvLocal(key, value, envPath = ".env.local") {
@@ -6450,6 +6472,7 @@ import {
6450
6472
  findProjectRoot as findProjectRoot3,
6451
6473
  loadStudioToml as loadStudioToml10
6452
6474
  } from "@bnbagent/studio-runtime/config";
6475
+ import { tbnbFaucetHint as tbnbFaucetHint2 } from "@bnbagent/studio-runtime/networks";
6453
6476
  import { buildPolicy, buildWallet } from "@bnbagent/studio-runtime/wallet";
6454
6477
 
6455
6478
  // src/cli/walletSession.ts
@@ -6467,8 +6490,8 @@ import {
6467
6490
  import { auditedOp as auditedOp2 } from "@bnbagent/studio-runtime/audit";
6468
6491
  import { loadStudioToml as loadStudioToml9 } from "@bnbagent/studio-runtime/config";
6469
6492
  import {
6470
- BSC_TESTNET_FAUCET_URLS,
6471
6493
  getNetwork as getNetwork5,
6494
+ tbnbFaucetHint,
6472
6495
  toRaw as toRaw2
6473
6496
  } from "@bnbagent/studio-runtime/networks";
6474
6497
  import { writePrivate } from "@bnbagent/studio-runtime/secretWrite";
@@ -6538,7 +6561,7 @@ async function requireAltanaAdminGas(ctx) {
6538
6561
  }
6539
6562
  if (balance !== 0n) return;
6540
6563
  const symbol = ctx.networkName === "bsc-testnet" ? "tBNB" : "BNB";
6541
- const funding = ctx.networkName === "bsc-testnet" ? ` Fund it and retry. Testnet faucets: ${BSC_TESTNET_FAUCET_URLS.join(", ")}` : " Fund it with BNB and retry.";
6564
+ const funding = ctx.networkName === "bsc-testnet" ? ` Fund it and retry \u2014 ${tbnbFaucetHint(ctx.address)}` : " Fund it with BNB and retry.";
6542
6565
  throw new Error(
6543
6566
  `Altana admin wallet ${ctx.address} has 0 ${symbol} on ${ctx.networkName} and cannot pay gas for session administration.${funding}`
6544
6567
  );
@@ -7000,6 +7023,14 @@ function projectNetwork(projectRoot) {
7000
7023
  const dflt = net2 !== null && typeof net2 === "object" ? net2.default : void 0;
7001
7024
  return dflt ? String(dflt) : "bsc-testnet";
7002
7025
  }
7026
+ function printNextAfterNew(projectRoot, address) {
7027
+ const network = projectNetwork(projectRoot);
7028
+ const fund = network === "bsc-testnet" ? `fund it (free, only needed for paid models / on-chain settle): ${tbnbFaucetHint2(address)}` : "fund it with BNB (gas) + U before selling paid work";
7029
+ printErr(
7030
+ `Next: ${fund}
7031
+ then \`bag doctor\` to verify the project, \`bag dev\` to run it locally.`
7032
+ );
7033
+ }
7003
7034
  function twakProvider(projectRoot, walletCfg) {
7004
7035
  return buildWallet(walletCfg, projectRoot, {
7005
7036
  network: projectNetwork(projectRoot)
@@ -7193,6 +7224,8 @@ async function cmdNew(opts) {
7193
7224
  printErr(
7194
7225
  "Altana admin keystore created. Fund this address with about 0.05 tBNB for testnet bootstrap/registration, then run `bag wallet session grant` to create the bounded runtime session."
7195
7226
  );
7227
+ } else {
7228
+ printNextAfterNew(projectRoot, provider.address);
7196
7229
  }
7197
7230
  return 0;
7198
7231
  }
@@ -7232,6 +7265,7 @@ Install it (Node >=22): npm install -g @trustwallet/cli@${TWAK_CLI_VERSION}`
7232
7265
  const address = provider.address;
7233
7266
  printOut(address);
7234
7267
  persistAddress(projectRoot, address);
7268
+ printNextAfterNew(projectRoot, address);
7235
7269
  return 0;
7236
7270
  }
7237
7271
  function ensureTwakCredentials(walletFile, homeDir) {
@@ -7964,6 +7998,44 @@ function enclosingProjectRoot(start) {
7964
7998
  }
7965
7999
  }
7966
8000
  async function cmdInit(nameArg, opts) {
8001
+ const target = path23.resolve(process.cwd(), nameArg);
8002
+ const targetPreexisted = fs24.existsSync(target);
8003
+ const cleanupOnCancel = () => {
8004
+ if (targetPreexisted || !fs24.existsSync(target)) {
8005
+ printErr("cancelled: no project was created.");
8006
+ return;
8007
+ }
8008
+ const walletsDir = path23.join(target, ".studio", "wallets");
8009
+ const hasWallet = fs24.existsSync(walletsDir) && fs24.readdirSync(walletsDir).length > 0;
8010
+ if (hasWallet) {
8011
+ printErr(
8012
+ `cancelled: ${target} is KEPT \u2014 it already contains a wallet keystore (.studio/wallets/). Finish setup there, or back the keystore up before removing the directory.`
8013
+ );
8014
+ } else {
8015
+ fs24.rmSync(target, { recursive: true, force: true });
8016
+ printErr(
8017
+ `cancelled: removed the partially created ${target}; re-run \`bag init\` to start over.`
8018
+ );
8019
+ }
8020
+ };
8021
+ const onSigint = () => {
8022
+ cleanupOnCancel();
8023
+ process.exit(130);
8024
+ };
8025
+ process.once("SIGINT", onSigint);
8026
+ try {
8027
+ return await cmdInitFlow(nameArg, opts);
8028
+ } catch (exc) {
8029
+ if (exc instanceof Error && exc.name === "PromptCancelled") {
8030
+ cleanupOnCancel();
8031
+ return 130;
8032
+ }
8033
+ throw exc;
8034
+ } finally {
8035
+ process.removeListener("SIGINT", onSigint);
8036
+ }
8037
+ }
8038
+ async function cmdInitFlow(nameArg, opts) {
7967
8039
  const target = path23.resolve(process.cwd(), nameArg);
7968
8040
  let projectName2 = path23.basename(target);
7969
8041
  if (!projectName2) {
@@ -7972,6 +8044,14 @@ async function cmdInit(nameArg, opts) {
7972
8044
  );
7973
8045
  return 2;
7974
8046
  }
8047
+ if (opts.runtime === "agentcore" || opts.destination === "platform") {
8048
+ try {
8049
+ projectName2 = validateProjectName(projectName2);
8050
+ } catch (exc) {
8051
+ printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
8052
+ return 2;
8053
+ }
8054
+ }
7975
8055
  if (opts.protocols !== void 0 && opts.protocol !== void 0) {
7976
8056
  printErr("error: pass either --protocols or --protocol, not both.");
7977
8057
  return 2;
@@ -8062,7 +8142,9 @@ async function cmdInit(nameArg, opts) {
8062
8142
  return 2;
8063
8143
  }
8064
8144
  if (fs24.existsSync(target) && !opts.force) {
8065
- printErr(`error: ${target} already exists. Use --force to overwrite.`);
8145
+ printErr(
8146
+ `error: ${target} already exists (perhaps from an interrupted \`bag init\`). Re-run with --force to overwrite \u2014 this DELETES the whole directory, including any wallet keystore under its .studio/ \u2014 or pick another name.`
8147
+ );
8066
8148
  return 2;
8067
8149
  }
8068
8150
  if (opts.destination === "platform" && opts.network === "bsc-mainnet") {
@@ -8086,13 +8168,20 @@ async function cmdInit(nameArg, opts) {
8086
8168
  destination = "self";
8087
8169
  runtime = "agentcore";
8088
8170
  }
8089
- if (destination === "self" && runtime === "azure-foundry" && (!hasA2aFace(faces) || hasMcpFace(faces))) {
8171
+ if (destination === "self" && runtime === "azure-foundry" && (hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces))) {
8090
8172
  printErr(
8091
- "error: azure-foundry supports protocol A2A only as its native face (X402 is optional) and does not support an MCP face. Foundry is delegated through its invocations container contract (:8088, /readiness, /invocations), not the native MCP /mcp transport. Use --protocols A2A (optionally with X402), or use --runtime agentcore for MCP."
8173
+ "error: azure-foundry supports the A2A carrier (including X402-only seller configs) and does not support an MCP face. Foundry is delegated through its invocations container contract (:8088, /readiness, /invocations), not the native MCP /mcp transport. Use --protocols A2A, X402, or A2A,X402; or use --runtime agentcore for MCP."
8092
8174
  );
8093
8175
  return 2;
8094
8176
  }
8095
8177
  if (fs24.existsSync(target)) {
8178
+ const walletsDir = path23.join(target, ".studio", "wallets");
8179
+ if (fs24.existsSync(walletsDir) && fs24.readdirSync(walletsDir).length > 0) {
8180
+ printErr(
8181
+ `error: refusing --force: ${walletsDir} contains a wallet keystore and overwriting would DELETE the key. Back the keystore up and remove the directory yourself, or init under a different name.`
8182
+ );
8183
+ return 2;
8184
+ }
8096
8185
  fs24.rmSync(target, { recursive: true, force: true });
8097
8186
  }
8098
8187
  if (destination === "platform") {
@@ -8410,17 +8499,18 @@ function scaffold(target, name, o) {
8410
8499
  const isContainer = packaging === "container";
8411
8500
  const mode = recipeModeOf(o.faces);
8412
8501
  const stem2 = entryStemOf(o.faces);
8413
- const a2aEntry = o.runtime === "azure-foundry" ? "dist/foundryMain.js" : "dist/main.js";
8414
8502
  const runtimeCtx = {
8415
8503
  PKG: AGENT_SRC,
8416
8504
  TWAK_CLI_VERSION,
8417
8505
  PNPM_VERSION: packageManagerVersion,
8418
8506
  DEPLOYMENT_TYPE: isContainer ? "container" : "direct_code_deploy",
8419
- ENTRYPOINT: o.runtime === "azure-foundry" ? a2aEntry : `dist/${stem2}.js`,
8420
- // Foundry Hosted Agents have a provider-level container contract that is
8421
- // independent of Studio's local A2A/MCP ports: plain HTTP on :8088 plus
8422
- // GET /readiness and the declared protocol route. The deploy host below
8423
- // declares invocations, so its image must always expose :8088.
8507
+ // The A2A entrypoint (unifiedMain) is cloud-portable: it binds BOTH the
8508
+ // AgentCore (9000) and Foundry (8088) contract ports itself, so the same
8509
+ // rendered image deploys to either cloud no runtime-specific entry.
8510
+ ENTRYPOINT: `dist/${stem2}.js`,
8511
+ // AGENT_PORT only prepends a primary override; the Foundry contract port
8512
+ // stays reachable regardless. MCP keeps its 8000 contract; Foundry
8513
+ // renders 8088 for continuity with the provider's documented default.
8424
8514
  CONTAINER_PORT: o.runtime === "azure-foundry" ? "8088" : String(devPortOf(o.faces))
8425
8515
  };
8426
8516
  emitRecipe(agentRoot2, "agent", { context: runtimeCtx });
@@ -8786,10 +8876,11 @@ function renderAgentStudioToml(name, o) {
8786
8876
  # Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
8787
8877
  # freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
8788
8878
  # LLM never prices. price=0 is FREE and is supported by the canonical stack.
8789
- currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default
8790
- price = "${o.erc8183Price}" # token base units; 0 = FREE (zero token escrow)
8879
+ currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default; rarely changed
8880
+ # All prices are in wei of the currency token. U has 18 decimals (not 6 like USDC).
8881
+ price = "${o.erc8183Price}" # wei; 0 = FREE (zero token escrow)
8791
8882
  min_price = "0" # wei \u2014 clamp floor
8792
- max_price = "" # wei \u2014 clamp ceiling; SET before going live
8883
+ max_price = "" # wei \u2014 clamp ceiling; empty = unbounded; SET before going live
8793
8884
  quote_ttl_seconds = 900 # seconds a signed quote stays valid; SDK caps at 900 (~15min work window)
8794
8885
  default_estimated_completion_seconds = 600
8795
8886
  poll_interval_seconds = 30
@@ -8927,7 +9018,7 @@ function renderAgentReadme(name, faces, destination, walletKind2) {
8927
9018
  const port = String(devPortOf(faces));
8928
9019
  const x402Only = faces.length === 1 && hasX402Face(faces);
8929
9020
  const surface = mode === "both" ? "A2A natively on `0.0.0.0:9000` (agent card + JSON-RPC `message/send`) and buffered streamable-HTTP MCP on `/mcp` through the platform envelope tunnel" : mode === "mcp" ? "streamable-HTTP MCP on `0.0.0.0:8000/mcp` (negotiate + notify_funded sync-delivery + read-only chain tools)" : x402Only ? "only the public `/x402` seller face; AgentCore uses an internal A2A-native process on port 9000 but the gateway suppresses A2A discovery" : "`serveA2a`: the agent card at `/.well-known/agent-card.json` + JSON-RPC `message/send` on `0.0.0.0:9000`";
8930
- const filesA2a = `- \`src/main.ts\` \u2014 A2A serving entrypoint (\`serveA2a\` on port 9000).
9021
+ const filesA2a = `- \`src/unifiedMain.ts\` \u2014 unified serving entrypoint (A2A on port 9000 + Foundry invocations on 8088).
8931
9022
  - \`src/executor.ts\` \u2014 the SellerAgentExecutor: the negotiate + notify_funded A2A skills.
8932
9023
  - \`src/agentCard.ts\` \u2014 the discoverable AgentCard (+ OAuth2/Cognito scheme).`;
8933
9024
  const filesMcp = `- \`src/mcpMain.ts\` \u2014 MCP serving entrypoint (streamable HTTP on \`/mcp\`; deploy
@@ -9077,10 +9168,23 @@ async function resolveWalletKind(flagValue, isTty) {
9077
9168
  );
9078
9169
  return "evm-local";
9079
9170
  }
9080
- const answer = (await promptUser(
9081
- "Wallet backend? [evm-local] local keystore / altana bounded session / twak Trust Wallet CLI: "
9082
- )).trim().toLowerCase();
9083
- return answer === "twak" || answer === "altana" ? answer : "evm-local";
9171
+ printOut("Wallet backend (who holds the Agent's signer key):");
9172
+ printOut(
9173
+ " 1. evm-local [default] \u2014 encrypted local keystore under .studio/; simplest, works everywhere"
9174
+ );
9175
+ printOut(
9176
+ " 2. twak \u2014 Trust Wallet CLI wallet; forces container packaging for deploys"
9177
+ );
9178
+ printOut(
9179
+ " 3. altana \u2014 bounded session signer; incompatible with pieverse-llm and paid b402 selling"
9180
+ );
9181
+ const answer = (await promptUser("Wallet backend [1-3, Enter = evm-local]: ")).trim().toLowerCase();
9182
+ if (answer === "2" || answer === "twak") return "twak";
9183
+ if (answer === "3" || answer === "altana") return "altana";
9184
+ if (answer !== "" && answer !== "1" && answer !== "evm-local") {
9185
+ printErr(`hint: unknown wallet backend '${answer}' \u2014 using evm-local.`);
9186
+ }
9187
+ return "evm-local";
9084
9188
  }
9085
9189
  async function resolveLlmProviderForWallet(walletKind2, configuredProvider, valueSource, isTty) {
9086
9190
  if (walletKind2 !== "altana") {
@@ -9263,12 +9367,22 @@ function readWalletAddress(studioToml) {
9263
9367
  async function printFaucetHint(network, address) {
9264
9368
  printOut("");
9265
9369
  if (network === "bsc-testnet") {
9266
- const { BSC_TESTNET_FAUCET_URLS: BSC_TESTNET_FAUCET_URLS5 } = await import("@bnbagent/studio-runtime/networks");
9370
+ const {
9371
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET: BSC_TESTNET_TBNB_TELEGRAM_FAUCET2,
9372
+ BSC_TESTNET_FAUCET_DOCS_URL: BSC_TESTNET_FAUCET_DOCS_URL2,
9373
+ BSC_TESTNET_U_FAUCET_URL: BSC_TESTNET_U_FAUCET_URL4
9374
+ } = await import("@bnbagent/studio-runtime/networks");
9267
9375
  printOut(`Your testnet wallet address: ${address}`);
9268
- printOut("Fund it (free) from these faucets \u2014 paste the address above:");
9269
- for (const url of BSC_TESTNET_FAUCET_URLS5) {
9270
- printOut(` \xB7 ${url}`);
9271
- }
9376
+ printOut("Fund it (free):");
9377
+ printOut(
9378
+ ` tBNB \u2014 message the official Telegram bot ${BSC_TESTNET_TBNB_TELEGRAM_FAUCET2}`
9379
+ );
9380
+ printOut(
9381
+ ` send: "I would like to get tBNB to my wallet ${address}"`
9382
+ );
9383
+ printOut(" (up to 0.3 tBNB per day; it replies with the tx hash)");
9384
+ printOut(` more ways to get tBNB: ${BSC_TESTNET_FAUCET_DOCS_URL2}`);
9385
+ printOut(` $U \u2014 ${BSC_TESTNET_U_FAUCET_URL4}`);
9272
9386
  printOut("(Faucets are needed only for paid models / on-chain settle.");
9273
9387
  printOut(" The default `auto/free` LLM model and AgentCore deploy");
9274
9388
  printOut(" do NOT require any funds.)");
@@ -9632,18 +9746,26 @@ function printMainTwakWalletWarning() {
9632
9746
  );
9633
9747
  }
9634
9748
  async function onboardIpfsKey(envPath, ipfsKey) {
9749
+ const url = (await promptUser(
9750
+ "\nIPFS pinning-service UPLOAD ENDPOINT for deliverable storage \u2014 REQUIRED before `bag deploy` (e.g. your provider's pin/upload URL or a self-hosted node's /api/v0/add; blank to fill STORAGE_API_URL later): "
9751
+ )).trim();
9752
+ if (url) {
9753
+ setEnvVar(envPath, "STORAGE_API_URL", url);
9754
+ printOut("\u2713 STORAGE_API_URL written to .studio/.env.local");
9755
+ }
9635
9756
  let key = ipfsKey;
9636
9757
  if (key === void 0) {
9637
9758
  key = (await promptUser(
9638
- "\nIPFS pinning-service write key/JWT for deliverable storage (blank to skip, fill STORAGE_API_KEY later): "
9759
+ "IPFS pinning-service write key/JWT (blank to skip, fill STORAGE_API_KEY later): "
9639
9760
  )).trim();
9640
9761
  }
9641
9762
  if (key) {
9642
9763
  setEnvVar(envPath, "STORAGE_API_KEY", key);
9643
9764
  printOut("\u2713 STORAGE_API_KEY written to .studio/.env.local");
9644
- } else {
9765
+ }
9766
+ if (!url || !key) {
9645
9767
  printErr(
9646
- "hint: set STORAGE_API_URL (your pinning service's upload endpoint) and STORAGE_API_KEY (its write key/JWT)\n in .studio/.env.local before `bag dev`, or re-scaffold with --storage-provider local for offline dev."
9768
+ "hint: set STORAGE_API_URL (your pinning service's upload endpoint) and STORAGE_API_KEY (its write key/JWT)\n in .studio/.env.local before `bag dev`, or re-scaffold with --storage-provider local for offline dev.\n `bag deploy prepare` blocks (CRITICAL) until STORAGE_API_URL is set."
9647
9769
  );
9648
9770
  }
9649
9771
  }
@@ -9665,7 +9787,7 @@ function printCampaignIntro() {
9665
9787
  );
9666
9788
  }
9667
9789
  function printTwakNotice(destination = "self") {
9668
- const deployLine = destination === "platform" ? " \xB7 DEPLOY (managed platform): builds + pushes the arm64 twak image\n (Node + the twak CLI) FROM YOUR MACHINE \u2014 local Docker is\n REQUIRED (no CodeBuild fallback on this path); start Docker Desktop." : " \xB7 RUN/DEPLOY: local `bag dev` runs the agent IN-PROCESS by default\n (`tsx src/main.ts`, no Docker); `bag dev --container` runs it via\n `agentcore dev` in Docker for image parity. DEPLOY builds the twak\n container image (Node + the twak CLI) via AWS CodeBuild \u2014\n Docker is optional locally. `bag init` already configured\n build=Container + a Dockerfile.";
9790
+ const deployLine = destination === "platform" ? " \xB7 DEPLOY (managed platform): builds + pushes the arm64 twak image\n (Node + the twak CLI) FROM YOUR MACHINE \u2014 local Docker is\n REQUIRED (no CodeBuild fallback on this path); start Docker Desktop." : " \xB7 RUN/DEPLOY: local `bag dev` runs the agent IN-PROCESS by default\n (`tsx src/unifiedMain.ts`, no Docker); `bag dev --container` runs it via\n `agentcore dev` in Docker for image parity. DEPLOY builds the twak\n container image (Node + the twak CLI) via AWS CodeBuild \u2014\n Docker is optional locally. `bag init` already configured\n build=Container + a Dockerfile.";
9669
9791
  printOut(
9670
9792
  `
9671
9793
  NOTE \u2014 wallet.kind = "twak" (Trust Wallet Agent Kit, CLI >= ${TWAK_CLI_VERSION}):
@@ -9772,9 +9894,9 @@ function entrypointHint(faces) {
9772
9894
  return "app/agent/src/mcpMain.ts = MCP entrypoint (build your value here)";
9773
9895
  }
9774
9896
  if (faces.length === 1 && hasX402Face(faces)) {
9775
- return "app/agent/src/main.ts = X402-only runtime entrypoint (build your value here)";
9897
+ return "app/agent/src/unifiedMain.ts = X402-only runtime entrypoint (build your value here)";
9776
9898
  }
9777
- return "app/agent/src/main.ts + executor.ts = A2A entrypoint (build your value here)";
9899
+ return "app/agent/src/unifiedMain.ts + executor.ts = A2A entrypoint (build your value here)";
9778
9900
  }
9779
9901
  function hasVendoredTarballs(agentDir) {
9780
9902
  const vendor = path23.join(agentDir, "vendor");
@@ -9822,18 +9944,17 @@ function printCompactNextSteps(name, o = {}) {
9822
9944
  " pnpm install # one-time: install the agent dependencies"
9823
9945
  );
9824
9946
  }
9947
+ printOut(" bag doctor # verify config + env before funding");
9948
+ printOut(" bag dev # run the Agent locally");
9825
9949
  if (destination === "platform") {
9826
- printOut(" bag dev # run the Agent locally");
9827
9950
  printOut(
9828
9951
  " bag deploy --provider bnb # Agent \u2192 managed platform (48h testnet trial)"
9829
9952
  );
9830
9953
  } else if (runtime === "azure-foundry") {
9831
- printOut(" bag dev # run the Agent locally");
9832
9954
  printOut(
9833
9955
  " bag deploy --provider azure # Agent \u2192 Azure AI Foundry (delegated to bnbagent-deploy; browser login, no azd)"
9834
9956
  );
9835
9957
  } else {
9836
- printOut(" agentcore dev # run the Agent locally on AgentCore");
9837
9958
  printOut(" bag deploy --provider aws # Agent \u2192 AgentCore");
9838
9959
  }
9839
9960
  printOut("");
@@ -9963,19 +10084,7 @@ function printNextSteps(name, provider, o = {}) {
9963
10084
  printOut(" # is `auto/free` (Free) \u2014 no funding required to start.");
9964
10085
  printOut(" bag llm activate");
9965
10086
  printOut("");
9966
- printOut(
9967
- " # 4. (optional) fund the wallet \u2014 only when you need paid model / deploy / publish:"
9968
- );
9969
- printOut(
9970
- " # testnet: send testnet BNB (gas) + testnet U to the address above"
9971
- );
9972
- printOut(" # mainnet: swap a small amount via PancakeSwap:");
9973
- printOut(
9974
- " # https://pancakeswap.finance/swap?outputCurrency=0xcE24439F2D9C6a2289F741120FE202248B666666"
9975
- );
9976
- printOut(" # Then: bag llm topup --amount N (allocate paid credits)");
9977
- printOut("");
9978
- nextStep = 5;
10087
+ nextStep = 4;
9979
10088
  } else {
9980
10089
  const keyEnv = PROVIDER_KEY_ENV[provider];
9981
10090
  if (keyEnv) {
@@ -9987,6 +10096,26 @@ function printNextSteps(name, provider, o = {}) {
9987
10096
  }
9988
10097
  nextStep = 4;
9989
10098
  }
10099
+ printOut(` # ${nextStep}. verify + run the agent locally (no funds needed)`);
10100
+ printOut(" bag doctor # diagnose config + env; fix FAILs before deploy");
10101
+ printOut(" bag dev # serve the Agent locally for a smoke test");
10102
+ nextStep += 1;
10103
+ if (provider === "pieverse-llm") {
10104
+ printOut("");
10105
+ printOut(
10106
+ ` # ${nextStep}. (optional) fund the wallet \u2014 only when you need paid model / deploy / publish:`
10107
+ );
10108
+ printOut(
10109
+ " # testnet: send testnet BNB (gas) + testnet U to the address above"
10110
+ );
10111
+ printOut(" # mainnet: swap a small amount via PancakeSwap:");
10112
+ printOut(
10113
+ " # https://pancakeswap.finance/swap?outputCurrency=0xcE24439F2D9C6a2289F741120FE202248B666666"
10114
+ );
10115
+ printOut(" # Then: bag llm topup --amount N (allocate paid credits)");
10116
+ nextStep += 1;
10117
+ }
10118
+ printOut("");
9990
10119
  printOut(
9991
10120
  ` # ${nextStep}. review ERC-8183 pricing in studio.toml [payments.erc8183]`
9992
10121
  );
@@ -9994,10 +10123,6 @@ function printNextSteps(name, provider, o = {}) {
9994
10123
  " # (price was selected at init; `bag config set payments.erc8183.price 0` explicitly switches to FREE)"
9995
10124
  );
9996
10125
  nextStep += 1;
9997
- printOut(` # ${nextStep}. verify + run the agent locally`);
9998
- printOut(" bag doctor");
9999
- printOut(" bag dev");
10000
- nextStep += 1;
10001
10126
  if (destination === "platform") {
10002
10127
  printOut(
10003
10128
  ` # ${nextStep}. deploy: app/agent \u2192 managed platform (48h testnet trial)`
@@ -10298,6 +10423,11 @@ function runtimeEnvKeysCore(cfg, resolvable) {
10298
10423
  if (resolvable("RPC_URL")) {
10299
10424
  keys.push("RPC_URL");
10300
10425
  }
10426
+ const runtimeName = String(tableOf8(cfg, "stack").runtime ?? "agentcore");
10427
+ const publicUrlKeys = runtimeName === "azure-foundry" ? ["BNBAGENT_PUBLIC_URL"] : ["BNBAGENT_PUBLIC_URL", "AGENT_PUBLIC_URL"];
10428
+ for (const key of publicUrlKeys) {
10429
+ if (resolvable(key)) keys.push(key);
10430
+ }
10301
10431
  for (const key of PAYMASTER_URL_ENV_KEYS) {
10302
10432
  if (resolvable(key)) keys.push(key);
10303
10433
  }
@@ -10306,8 +10436,7 @@ function runtimeEnvKeysCore(cfg, resolvable) {
10306
10436
  if (resolvable(key)) keys.push(key);
10307
10437
  }
10308
10438
  }
10309
- const runtime = String(tableOf8(cfg, "stack").runtime ?? "agentcore");
10310
- if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
10439
+ if (X402_CAPABLE_RUNTIMES.has(runtimeName) && x402SellerUsesB402(cfg)) {
10311
10440
  for (const key of B402_RUNTIME_KEYS) {
10312
10441
  if (resolvable(key)) keys.push(key);
10313
10442
  }
@@ -10413,8 +10542,12 @@ function agentFaces(workspaceRoot) {
10413
10542
  const cfg = loadAgentToml2(workspaceRoot);
10414
10543
  return stackFaces(tableOf9(cfg, "stack"), tableOf9(cfg, "payments"));
10415
10544
  }
10416
- function localEntryFile(faces) {
10417
- return `src/${entryStemOf(faces)}.ts`;
10545
+ function localEntryFile(faces, agentDir) {
10546
+ const stem2 = entryStemOf(faces);
10547
+ const candidates = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].map(
10548
+ (s) => `src/${s}.ts`
10549
+ );
10550
+ return candidates.find((c2) => isFile10(path24.join(agentDir, c2))) ?? candidates[0];
10418
10551
  }
10419
10552
  function defaultAgentPort(faces) {
10420
10553
  return devPortOf(faces);
@@ -10786,11 +10919,11 @@ async function cmdDev(opts) {
10786
10919
  const agentPort = opts.port ?? defaultAgentPort(faces);
10787
10920
  const runtime = agentRuntime(workspaceRoot);
10788
10921
  const container = Boolean(opts.container);
10789
- const entryFile = localEntryFile(faces);
10922
+ const entryFile = localEntryFile(faces, agentDir);
10790
10923
  const agentFile = path24.join(agentDir, entryFile);
10791
10924
  if (!isFile10(agentFile)) {
10792
10925
  printErr(
10793
- `error: Agent entrypoint file ${agentFile} does not exist. [stack] faces = [${faces.join(", ")}], so the entrypoint is ${entryFile} (A2A/X402-only \u2192 src/main.ts, MCP \u2192 src/mcpMain.ts, A2A+MCP \u2192 src/dualMain.ts). Run \`bag init\` to scaffold the workspace.`
10926
+ `error: Agent entrypoint file ${agentFile} does not exist. [stack] faces = [${faces.join(", ")}], so the entrypoint is ${entryFile} (A2A/X402-only \u2192 src/unifiedMain.ts, MCP \u2192 src/mcpMain.ts, A2A+MCP \u2192 src/dualMain.ts; legacy src/main.ts scaffolds still run). Run \`bag init\` to scaffold the workspace.`
10794
10927
  );
10795
10928
  return 2;
10796
10929
  }
@@ -10929,7 +11062,7 @@ Switch to Node \u226522 so it wins on PATH, then reopen the shell and retry. Ver
10929
11062
  printOut(
10930
11063
  `bag dev: starting Agent in-process (\`tsx ${args.join(" ")}\` on :${agentPort}, no Docker \u2014 the entrypoint self-serves; cwd=${agentDir}, runner=${runner2})`
10931
11064
  );
10932
- if (runtime === RUNTIME_AZURE_FOUNDRY) {
11065
+ if (runtime === RUNTIME_AZURE_FOUNDRY && isFile10(path24.join(agentDir, "src/foundryMain.ts"))) {
10933
11066
  printOut(
10934
11067
  "bag dev: azure-foundry serves this local A2A/MCP entrypoint in-process; src/foundryMain.ts is the deploy-only Foundry host (the entrypoint `bag doctor` reports) and never runs here."
10935
11068
  );
@@ -11102,7 +11235,10 @@ async function checkAgentPackagePresent(root, target) {
11102
11235
  const cfg = loadAgentToml(root);
11103
11236
  const faces = stackFaces(tableOf2(cfg, "stack"), tableOf2(cfg, "payments"));
11104
11237
  const stem2 = entryStemOf(faces);
11105
- const sourceEntries = [`src/${stem2}.ts`];
11238
+ const effectiveStem = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].find(
11239
+ (s) => isFile11(path25.join(agentRoot2, `src/${s}.ts`))
11240
+ ) ?? stem2;
11241
+ const sourceEntries = [`src/${effectiveStem}.ts`];
11106
11242
  if (stem2 === "dualMain") {
11107
11243
  sourceEntries.push("src/mcpMain.ts");
11108
11244
  }
@@ -11120,7 +11256,7 @@ async function checkAgentPackagePresent(root, target) {
11120
11256
  }
11121
11257
  ];
11122
11258
  }
11123
- const distEntries = [`dist/${stem2}.js`];
11259
+ const distEntries = [`dist/${effectiveStem}.js`];
11124
11260
  if (stem2 === "dualMain") {
11125
11261
  distEntries.push("dist/mcpMain.js");
11126
11262
  }
@@ -11600,12 +11736,12 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
11600
11736
  }
11601
11737
  const destination = String(tableOf2(cfg, "deploy").destination ?? "self");
11602
11738
  const stackRuntime2 = String(tableOf2(cfg, "stack").runtime ?? "agentcore");
11603
- if (rails.x402 && (free || credentials.complete) && destination !== "platform" && (target !== "agentcore" || stackRuntime2 !== "agentcore")) {
11604
- const blocking = target !== "agentcore" ? target : stackRuntime2;
11739
+ if (rails.x402 && (free || credentials.complete) && !target.startsWith("platform") && !(X402_CAPABLE_RUNTIMES.has(target) && X402_CAPABLE_RUNTIMES.has(stackRuntime2))) {
11740
+ const blocking = !X402_CAPABLE_RUNTIMES.has(target) ? target : stackRuntime2;
11605
11741
  out.push({
11606
11742
  level: Level.WARNING,
11607
11743
  name: "x402_forced_dormant_runtime",
11608
- 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.`,
11744
+ message: `x402 pricing is ready, but the ${blocking} runtime has no x402 path; this target is forced dormant. Deploy to AgentCore or Azure Foundry (managed platform or self-hosted) to activate the rail.`,
11609
11745
  details: { destination, target, stackRuntime: stackRuntime2 }
11610
11746
  });
11611
11747
  }
@@ -11656,7 +11792,7 @@ async function checkCommerceReady(root, target) {
11656
11792
  return out;
11657
11793
  }
11658
11794
  async function checkDeployCliRunnable(_root, target) {
11659
- if (target !== "agentcore") {
11795
+ if (target !== "agentcore" && target !== "platform" && target !== "platform-azure") {
11660
11796
  return [];
11661
11797
  }
11662
11798
  const override = (process.env.BNBAGENT_DEPLOY_COMMAND ?? "").trim();
@@ -13062,7 +13198,11 @@ import {
13062
13198
  findSubProjectRoot as findSubProjectRoot13,
13063
13199
  loadStudioToml as loadStudioToml17
13064
13200
  } from "@bnbagent/studio-runtime/config";
13065
- import { BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS2 } from "@bnbagent/studio-runtime/networks";
13201
+ import {
13202
+ BSC_TESTNET_FAUCET_DOCS_URL,
13203
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET,
13204
+ BSC_TESTNET_U_FAUCET_URL as BSC_TESTNET_U_FAUCET_URL2
13205
+ } from "@bnbagent/studio-runtime/networks";
13066
13206
 
13067
13207
  // src/cli/_deploy/checks/warning.ts
13068
13208
  import * as crypto4 from "crypto";
@@ -13372,7 +13512,10 @@ var checkPlatformEntrypointPresent = async (root, _target) => {
13372
13512
  const data = loadAgent(root);
13373
13513
  const faces = stackFaces(tableOf11(data, "stack"), tableOf11(data, "payments"));
13374
13514
  const stem2 = entryStemOf(faces);
13375
- const filenames = [`${stem2}.ts`];
13515
+ const effectiveStem = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].find(
13516
+ (s) => fs35.existsSync(path34.join(agentRoot(root), "src", `${s}.ts`))
13517
+ ) ?? stem2;
13518
+ const filenames = [`${effectiveStem}.ts`];
13376
13519
  if (stem2 === "dualMain") filenames.push("mcpMain.ts");
13377
13520
  const filename = filenames.find(
13378
13521
  (candidate) => !fs35.existsSync(path34.join(agentRoot(root), "src", candidate))
@@ -13536,8 +13679,14 @@ var checkPlatformFundingHint = async (_root, _target) => [
13536
13679
  {
13537
13680
  level: Level.INFO,
13538
13681
  name: "platform_funding_hint",
13539
- message: `testnet trial \u2014 keep a little tBNB for gas and testnet $U for commerce. Faucets: ${BSC_TESTNET_FAUCET_URLS2.join(" \xB7 ")}`,
13540
- details: { faucets: [...BSC_TESTNET_FAUCET_URLS2] }
13682
+ message: `testnet trial \u2014 keep a little tBNB for gas and testnet $U for commerce. tBNB: message ${BSC_TESTNET_TBNB_TELEGRAM_FAUCET} (more options: ${BSC_TESTNET_FAUCET_DOCS_URL}) \xB7 $U: ${BSC_TESTNET_U_FAUCET_URL2}`,
13683
+ details: {
13684
+ faucets: [
13685
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET,
13686
+ BSC_TESTNET_FAUCET_DOCS_URL,
13687
+ BSC_TESTNET_U_FAUCET_URL2
13688
+ ]
13689
+ }
13541
13690
  }
13542
13691
  ];
13543
13692
  var criticalChecks = [
@@ -13546,6 +13695,7 @@ var criticalChecks = [
13546
13695
  checkPlatformTestnetOnly,
13547
13696
  checkPlatformSlugValid,
13548
13697
  checkPlatformLoggedIn,
13698
+ checkDeployCliRunnable,
13549
13699
  checkPlatformDockerAvailable,
13550
13700
  checkPlatformStorageEndpoint,
13551
13701
  checkTwakCustomContractsUnsupported,
@@ -14308,7 +14458,7 @@ async function platformAgent(opts) {
14308
14458
  const root = opts.root;
14309
14459
  const backend = opts.backend ?? "aws";
14310
14460
  const isTty = opts.isTty ?? stdinIsTty;
14311
- const ask = opts.promptUser ?? promptUser;
14461
+ const ask2 = opts.promptUser ?? promptUser;
14312
14462
  warn(
14313
14463
  `note: deploying to the BNB Chain managed platform (backend=${backend}) \u2014 a 48h TESTNET trial sandbox (network is forced to bsc-testnet; the runtime is auto-reclaimed at 48h). Not a production seller.`
14314
14464
  );
@@ -14317,12 +14467,6 @@ async function platformAgent(opts) {
14317
14467
  const cfg2 = loadAgentCfg(root);
14318
14468
  const faces2 = stackFaces(tableOf12(cfg2, "stack"), tableOf12(cfg2, "payments"));
14319
14469
  const protocol2 = nativeProtocolOf(faces2);
14320
- if (hasX402Face(faces2)) {
14321
- warn(
14322
- "error: x402 publication is not supported on the managed Azure backend. No build or upload was started."
14323
- );
14324
- return 1;
14325
- }
14326
14470
  const runtimeProfile = protocol2 === "A2A" ? "foundry-responses-v1" : "foundry-invocations-v1";
14327
14471
  const capability = await platformCmd(["capabilities"], { json: true });
14328
14472
  const platform = isJsonObject(capability.data.platform) ? capability.data.platform : {};
@@ -14336,6 +14480,12 @@ async function platformAgent(opts) {
14336
14480
  );
14337
14481
  return capability.code || 1;
14338
14482
  }
14483
+ if (hasX402Face(faces2) && azure.x402 !== true) {
14484
+ warn(
14485
+ "error: this platform deployment does not accept x402 publication on the managed Azure backend yet. No build or upload was started. Deploy without the X402 face, or self-host the x402 gateway (`bag deploy --provider azure` + docs/guides/self-hosted-x402-gateway-azure.md)."
14486
+ );
14487
+ return 1;
14488
+ }
14339
14489
  }
14340
14490
  const campaignProbe = opts.campaignActive ?? (async () => null);
14341
14491
  const trialRes = await platformCmd(["trial"], { json: true });
@@ -14364,7 +14514,7 @@ async function platformAgent(opts) {
14364
14514
  const rc = await termsConsentGate(root, {
14365
14515
  acceptRisk: opts.acceptRisk ?? false,
14366
14516
  isTty,
14367
- promptUser: ask
14517
+ promptUser: ask2
14368
14518
  });
14369
14519
  if (rc !== null) {
14370
14520
  return rc;
@@ -14375,7 +14525,7 @@ async function platformAgent(opts) {
14375
14525
  const confirm = opts.confirmPackaging ?? ((r, p) => confirmPackagingDefault(r, p, {
14376
14526
  acceptRisk: opts.acceptRisk ?? false,
14377
14527
  isTty,
14378
- promptUser: ask
14528
+ promptUser: ask2
14379
14529
  }));
14380
14530
  const rc = await confirm(root, packaging);
14381
14531
  if (rc !== null && rc !== 0) {
@@ -16861,12 +17011,13 @@ import { pieverseKeyHash as pieverseKeyHash4 } from "@bnbagent/studio-runtime/ll
16861
17011
  import {
16862
17012
  BSC_MAINNET_MIN_BNB,
16863
17013
  BSC_MAINNET_MIN_U,
16864
- BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS3,
16865
17014
  BSC_TESTNET_MIN_BNB,
16866
17015
  BSC_TESTNET_MIN_U,
17016
+ BSC_TESTNET_U_FAUCET_URL as BSC_TESTNET_U_FAUCET_URL3,
16867
17017
  ERC8183_SUBMIT_GAS_RESERVE_BNB,
16868
17018
  fromRaw as fromRaw3,
16869
- getNetwork as getNetwork9
17019
+ getNetwork as getNetwork9,
17020
+ tbnbFaucetHint as tbnbFaucetHint3
16870
17021
  } from "@bnbagent/studio-runtime/networks";
16871
17022
  import { listModels as listModels2 } from "@bnbagent/studio-runtime/pieverse";
16872
17023
  import * as walletRt5 from "@bnbagent/studio-runtime/wallet";
@@ -16971,6 +17122,9 @@ async function cmdDoctor(opts) {
16971
17122
  checks.push(...await check8004(projectRoot, data, opts.network));
16972
17123
  const destination = String(tableOf14(data, "deploy").destination ?? "self").trim().toLowerCase();
16973
17124
  checks.push(...await checkDeployTools(data, destination));
17125
+ checks.push(
17126
+ ...await checkPlatformZipBundle(data, destination, projectRoot)
17127
+ );
16974
17128
  checks.push(...checkAgentsMdSkillTrigger(projectRoot));
16975
17129
  printChecks(checks);
16976
17130
  return checks.some((c2) => c2.status === FAIL) ? 1 : 0;
@@ -17053,16 +17207,12 @@ function checkAppMain(projectRootArg2) {
17053
17207
  }
17054
17208
  }
17055
17209
  const stack = tableOf14(config, "stack");
17210
+ const faces = stackFaces(stack, tableOf14(config, "payments"));
17211
+ const stem2 = entryStemOf(faces);
17056
17212
  const runtime = String(stack.runtime ?? "").toLowerCase();
17057
- let entrypoint;
17058
- if (runtime === "azure-foundry") {
17059
- entrypoint = "src/foundryMain.ts";
17060
- } else if (runtime === "agentcore") {
17061
- const faces = stackFaces(stack, tableOf14(config, "payments"));
17062
- entrypoint = `src/${entryStemOf(faces)}.ts`;
17063
- } else {
17064
- entrypoint = "src/main.ts";
17065
- }
17213
+ const legacyStems = runtime === "azure-foundry" && stem2 === "unifiedMain" ? ["foundryMain", "main"] : LEGACY_ENTRY_STEMS[stem2];
17214
+ const candidates = [stem2, ...legacyStems].map((s) => `src/${s}.ts`);
17215
+ const entrypoint = candidates.find((c2) => isFile17(path39.join(agentDir, c2))) ?? candidates[0];
17066
17216
  const entryFile = path39.join(agentDir, entrypoint);
17067
17217
  if (!isFile17(entryFile)) {
17068
17218
  return [
@@ -17073,7 +17223,7 @@ function checkAppMain(projectRootArg2) {
17073
17223
  }
17074
17224
  ];
17075
17225
  }
17076
- const detail = runtime === "azure-foundry" ? `${entrypoint} (deploy entrypoint; \`bag dev\` serves src/main.ts or src/mcpMain.ts locally)` : entrypoint;
17226
+ const detail = entrypoint === "src/foundryMain.ts" ? `${entrypoint} (deploy entrypoint; \`bag dev\` serves src/main.ts or src/mcpMain.ts locally)` : entrypoint;
17077
17227
  return [{ name: "agent entrypoint", status: PASS, detail }];
17078
17228
  }
17079
17229
  function checkAgentcoreName(projectRoot) {
@@ -17851,7 +18001,8 @@ async function checkBalances(projectRoot, data, networkOverride) {
17851
18001
  const isMainnet = name === "bsc-mainnet";
17852
18002
  const minBnb = isTestnet ? BSC_TESTNET_MIN_BNB : isMainnet ? BSC_MAINNET_MIN_BNB : 0;
17853
18003
  const minBnbWei = BigInt(Math.round(minBnb * 1e6)) * 10n ** 12n;
17854
- const faucetHint = isTestnet ? ` \u2014 faucets: ${BSC_TESTNET_FAUCET_URLS3.join(", ")}` : "";
18004
+ const bnbFaucetHint = isTestnet ? ` \u2014 ${tbnbFaucetHint3(address)}` : "";
18005
+ const uFaucetHint = isTestnet ? ` \u2014 $U faucet: ${BSC_TESTNET_U_FAUCET_URL3}` : "";
17855
18006
  const bnbRaw = await readNativeBalanceRaw(address, name);
17856
18007
  const reserve = ERC8183_SUBMIT_GAS_RESERVE_BNB;
17857
18008
  const reserveHint = ` (each on-chain action reserves \u2248 ${reserve} BNB worst-case)`;
@@ -17872,7 +18023,7 @@ async function checkBalances(projectRoot, data, networkOverride) {
17872
18023
  out.push({
17873
18024
  name: "wallet BNB balance",
17874
18025
  status: WARN,
17875
- detail: `${bnbRaw} wei @ ${address} \u2014 need \u2265 ${minBnb} BNB on ${name} for 8183 escrow / ERC-8004 register${reserveHint}${faucetHint}`
18026
+ detail: `${bnbRaw} wei @ ${address} \u2014 need \u2265 ${minBnb} BNB on ${name} for 8183 escrow / ERC-8004 register${reserveHint}${bnbFaucetHint}`
17876
18027
  });
17877
18028
  }
17878
18029
  const uToken = net2.tokens.U;
@@ -17898,7 +18049,7 @@ async function checkBalances(projectRoot, data, networkOverride) {
17898
18049
  out.push({
17899
18050
  name: "wallet U balance",
17900
18051
  status: WARN,
17901
- detail: `${uRaw} raw @ ${address} \u2014 need \u2265 ${minU} U on ${name} for one auto-topup chunk${faucetHint}`
18052
+ detail: `${uRaw} raw @ ${address} \u2014 need \u2265 ${minU} U on ${name} for one auto-topup chunk${uFaucetHint}`
17902
18053
  });
17903
18054
  }
17904
18055
  return out;
@@ -18047,11 +18198,7 @@ async function checkDeployTools(data, destination) {
18047
18198
  });
18048
18199
  if (destination === "platform") {
18049
18200
  const out = [
18050
- {
18051
- name: "deploy: bnbagent-deploy",
18052
- status: INFO,
18053
- detail: "[deploy].destination='platform' also delegates to bnbagent-deploy (bunx); `bag deploy prepare` verifies it."
18054
- },
18201
+ emit2(await checkBunx()),
18055
18202
  {
18056
18203
  name: "deploy: AWS credentials",
18057
18204
  status: INFO,
@@ -18081,6 +18228,49 @@ async function checkDeployTools(data, destination) {
18081
18228
  }
18082
18229
  return (await deployChecks()).map(emit2);
18083
18230
  }
18231
+ async function checkPlatformZipBundle(data, destination, projectRootArg2) {
18232
+ if (destination !== "platform" || platformPackagingRequiresContainer(data)) {
18233
+ return [];
18234
+ }
18235
+ let projectRoot = projectRootArg2;
18236
+ const parent = path39.dirname(projectRoot);
18237
+ if (isFile17(path39.join(parent, "agent", "studio.toml"))) {
18238
+ projectRoot = parent;
18239
+ }
18240
+ const agentDir = path39.join(projectRoot, "agent");
18241
+ if (!isFile17(path39.join(agentDir, "studio.toml"))) {
18242
+ return [];
18243
+ }
18244
+ if (!fs39.existsSync(path39.join(agentDir, "node_modules"))) {
18245
+ return [
18246
+ {
18247
+ name: "deploy: zip bundle",
18248
+ status: INFO,
18249
+ detail: "skipped \u2014 dependencies not installed yet (run `pnpm install`); re-run `bag doctor` to dry-run the zip bundle."
18250
+ }
18251
+ ];
18252
+ }
18253
+ const stack = tableOf14(data, "stack");
18254
+ const faces = stackFaces(stack, tableOf14(data, "payments"));
18255
+ try {
18256
+ await dryRunBundle(agentDir, { protocols: faces });
18257
+ } catch (exc) {
18258
+ return [
18259
+ {
18260
+ name: "deploy: zip bundle",
18261
+ status: WARN,
18262
+ detail: errMsg3(exc)
18263
+ }
18264
+ ];
18265
+ }
18266
+ return [
18267
+ {
18268
+ name: "deploy: zip bundle",
18269
+ status: PASS,
18270
+ detail: "esbuild dry run OK \u2014 the agent bundles for the platform ZIP runtime."
18271
+ }
18272
+ ];
18273
+ }
18084
18274
  function checkAgentsMdSkillTrigger(projectRoot) {
18085
18275
  const ws = findStudioWorkspaceRoot4(projectRoot);
18086
18276
  if (ws === null) {
@@ -18259,9 +18449,9 @@ import {
18259
18449
  updateServiceEndpoint as updateServiceEndpoint2
18260
18450
  } from "@bnbagent/studio-runtime/erc8004";
18261
18451
  import {
18262
- BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS4,
18263
18452
  fromRaw as fromRaw4,
18264
- getNetwork as getNetwork10
18453
+ getNetwork as getNetwork10,
18454
+ tbnbFaucetHint as tbnbFaucetHint4
18265
18455
  } from "@bnbagent/studio-runtime/networks";
18266
18456
  import * as walletRt6 from "@bnbagent/studio-runtime/wallet";
18267
18457
  import { Option as Option5 } from "commander";
@@ -18423,8 +18613,7 @@ async function precheckRegisterGas(wallet, network) {
18423
18613
  if (balance >= REGISTER_MIN_BNB) {
18424
18614
  return null;
18425
18615
  }
18426
- const faucet = BSC_TESTNET_FAUCET_URLS4[0];
18427
- return `ERC-8004 register needs gas to broadcast, but ${wallet.address} holds only ${balance} BNB on ${network} (need ~${REGISTER_MIN_BNB}). Fund a little tBNB and retry. Faucet: ${faucet}`;
18616
+ return `ERC-8004 register needs gas to broadcast, but ${wallet.address} holds only ${balance} BNB on ${network} (need ~${REGISTER_MIN_BNB}). Fund a little tBNB and retry \u2014 ${tbnbFaucetHint4(wallet.address)}`;
18428
18617
  }
18429
18618
  function syncAgentRegistry(agentId, op) {
18430
18619
  try {
@@ -19374,7 +19563,7 @@ async function promptInstallScope() {
19374
19563
  }
19375
19564
  function printNextSteps2() {
19376
19565
  printOut(
19377
- "\nNext: `bag init my-agent` to scaffold a single seller agent project.\nIn your IDE, type /bnbagent-studio to drive any studio task \u2014 the one\ninstalled skill routes everything (its playbooks load on demand).\nSee `bag --help` for the full command surface, or https://github.com/bnb-chain/bnbagent-studio for docs."
19566
+ "\nNext: `bag init myagent` to scaffold a single seller agent project.\nIn your IDE, type /bnbagent-studio to drive any studio task \u2014 the one\ninstalled skill routes everything (its playbooks load on demand).\nSee `bag --help` for the full command surface, or https://github.com/bnb-chain/bnbagent-studio for docs."
19378
19567
  );
19379
19568
  }
19380
19569
  async function installInteractive(flags) {
@@ -21183,6 +21372,10 @@ async function main(argv) {
21183
21372
  if (err instanceof CliExit) {
21184
21373
  return err.code;
21185
21374
  }
21375
+ if (err instanceof Error && err.name === "PromptCancelled") {
21376
+ process.stderr.write("cancelled.\n");
21377
+ return 130;
21378
+ }
21186
21379
  if (isCommanderExit(err)) {
21187
21380
  return err.exitCode;
21188
21381
  }