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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +322 -125
  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.2") {
462
+ return "0.0.11-alpha.2";
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");
@@ -3366,7 +3385,11 @@ var PROVIDER_SHORT_LABELS = {
3366
3385
  function providerDigit(provider) {
3367
3386
  return PROVIDER_MENU_ORDER.indexOf(provider) + 1;
3368
3387
  }
3369
- var ADVERTISED_PROVIDERS = ["bnb", "aws"];
3388
+ var ADVERTISED_PROVIDERS = [
3389
+ "bnb",
3390
+ "aws",
3391
+ "azure"
3392
+ ];
3370
3393
  var ADVERTISED_PROVIDERS_HELP = ADVERTISED_PROVIDERS.join("|");
3371
3394
  function tableOf3(data, key) {
3372
3395
  const value = data[key];
@@ -3455,13 +3478,13 @@ function unavailableProvidersForProject(root) {
3455
3478
  const cfg = loadStudioToml6(path13.join(agentRoot2, "studio.toml"));
3456
3479
  runtime = text(tableOf3(cfg, "stack").runtime) ?? "agentcore";
3457
3480
  const faces = stackFaces(tableOf3(cfg, "stack"), tableOf3(cfg, "payments"));
3458
- azureProtocolUnsupported = !hasA2aFace(faces) || hasMcpFace(faces);
3481
+ azureProtocolUnsupported = hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces);
3459
3482
  } catch {
3460
3483
  return {};
3461
3484
  }
3462
3485
  if (runtime === "azure-foundry") {
3463
3486
  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";
3487
+ const azureProtocolReason = "Azure Foundry deploy supports A2A and X402 seller projects; use AgentCore for MCP";
3465
3488
  return {
3466
3489
  aws: reason,
3467
3490
  ...azureProtocolUnsupported ? {
@@ -4488,22 +4511,23 @@ var checkEntrypointAndDockerfile = async (root, _target) => {
4488
4511
  const findings = [];
4489
4512
  const cfg = loadStudioCfg(deployWorkspaceRoot(root));
4490
4513
  const faces = stackFaces(tableOf4(cfg, "stack"), tableOf4(cfg, "payments"));
4491
- if (!hasA2aFace(faces) || hasMcpFace(faces)) {
4514
+ if (hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces)) {
4492
4515
  return [
4493
4516
  {
4494
4517
  level: "CRITICAL",
4495
4518
  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.",
4519
+ 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
4520
  details: { faces }
4498
4521
  }
4499
4522
  ];
4500
4523
  }
4501
- const entry = "src/foundryMain.ts";
4524
+ const entryCandidates = ["src/unifiedMain.ts", "src/foundryMain.ts"];
4525
+ const entry = entryCandidates.find((c2) => isFile6(path17.join(agentRoot2, c2))) ?? entryCandidates[0];
4502
4526
  if (!isFile6(path17.join(agentRoot2, entry))) {
4503
4527
  findings.push({
4504
4528
  level: "CRITICAL",
4505
4529
  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\`.`,
4530
+ message: `app/agent/${entry} is missing \u2014 the A2A entrypoint the container image runs. Re-scaffold with \`bag init \u2026 --runtime azure-foundry\`.`,
4507
4531
  details: { entrypoint: entry }
4508
4532
  });
4509
4533
  }
@@ -4606,7 +4630,11 @@ import * as walletRt2 from "@bnbagent/studio-runtime/wallet";
4606
4630
  import { generateText } from "ai";
4607
4631
 
4608
4632
  // src/cli/_pieverseHelpers.ts
4609
- import { fromRaw as fromRaw2, getNetwork as getNetwork3 } from "@bnbagent/studio-runtime/networks";
4633
+ import {
4634
+ BSC_TESTNET_U_FAUCET_URL,
4635
+ fromRaw as fromRaw2,
4636
+ getNetwork as getNetwork3
4637
+ } from "@bnbagent/studio-runtime/networks";
4610
4638
  import {
4611
4639
  siweLogin
4612
4640
  } from "@bnbagent/studio-runtime/pieverse";
@@ -4659,9 +4687,7 @@ function printUBalanceShortfall(walletAddress, currentRaw, neededRaw, neededUsd,
4659
4687
  ` swap on PancakeSwap: https://pancakeswap.finance/swap?outputCurrency=${u.address}`
4660
4688
  );
4661
4689
  } else {
4662
- printErr(
4663
- " testnet U faucet URL is TBD \u2014 see docs/guides/pieverse-integration.md \xA76"
4664
- );
4690
+ printErr(` testnet U faucet: ${BSC_TESTNET_U_FAUCET_URL}`);
4665
4691
  }
4666
4692
  }
4667
4693
  function writeEnvLocal(key, value, envPath = ".env.local") {
@@ -6450,6 +6476,7 @@ import {
6450
6476
  findProjectRoot as findProjectRoot3,
6451
6477
  loadStudioToml as loadStudioToml10
6452
6478
  } from "@bnbagent/studio-runtime/config";
6479
+ import { tbnbFaucetHint as tbnbFaucetHint2 } from "@bnbagent/studio-runtime/networks";
6453
6480
  import { buildPolicy, buildWallet } from "@bnbagent/studio-runtime/wallet";
6454
6481
 
6455
6482
  // src/cli/walletSession.ts
@@ -6467,8 +6494,8 @@ import {
6467
6494
  import { auditedOp as auditedOp2 } from "@bnbagent/studio-runtime/audit";
6468
6495
  import { loadStudioToml as loadStudioToml9 } from "@bnbagent/studio-runtime/config";
6469
6496
  import {
6470
- BSC_TESTNET_FAUCET_URLS,
6471
6497
  getNetwork as getNetwork5,
6498
+ tbnbFaucetHint,
6472
6499
  toRaw as toRaw2
6473
6500
  } from "@bnbagent/studio-runtime/networks";
6474
6501
  import { writePrivate } from "@bnbagent/studio-runtime/secretWrite";
@@ -6538,7 +6565,7 @@ async function requireAltanaAdminGas(ctx) {
6538
6565
  }
6539
6566
  if (balance !== 0n) return;
6540
6567
  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.";
6568
+ const funding = ctx.networkName === "bsc-testnet" ? ` Fund it and retry \u2014 ${tbnbFaucetHint(ctx.address)}` : " Fund it with BNB and retry.";
6542
6569
  throw new Error(
6543
6570
  `Altana admin wallet ${ctx.address} has 0 ${symbol} on ${ctx.networkName} and cannot pay gas for session administration.${funding}`
6544
6571
  );
@@ -7000,6 +7027,14 @@ function projectNetwork(projectRoot) {
7000
7027
  const dflt = net2 !== null && typeof net2 === "object" ? net2.default : void 0;
7001
7028
  return dflt ? String(dflt) : "bsc-testnet";
7002
7029
  }
7030
+ function printNextAfterNew(projectRoot, address) {
7031
+ const network = projectNetwork(projectRoot);
7032
+ 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";
7033
+ printErr(
7034
+ `Next: ${fund}
7035
+ then \`bag doctor\` to verify the project, \`bag dev\` to run it locally.`
7036
+ );
7037
+ }
7003
7038
  function twakProvider(projectRoot, walletCfg) {
7004
7039
  return buildWallet(walletCfg, projectRoot, {
7005
7040
  network: projectNetwork(projectRoot)
@@ -7193,6 +7228,8 @@ async function cmdNew(opts) {
7193
7228
  printErr(
7194
7229
  "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
7230
  );
7231
+ } else {
7232
+ printNextAfterNew(projectRoot, provider.address);
7196
7233
  }
7197
7234
  return 0;
7198
7235
  }
@@ -7232,6 +7269,7 @@ Install it (Node >=22): npm install -g @trustwallet/cli@${TWAK_CLI_VERSION}`
7232
7269
  const address = provider.address;
7233
7270
  printOut(address);
7234
7271
  persistAddress(projectRoot, address);
7272
+ printNextAfterNew(projectRoot, address);
7235
7273
  return 0;
7236
7274
  }
7237
7275
  function ensureTwakCredentials(walletFile, homeDir) {
@@ -7964,6 +8002,44 @@ function enclosingProjectRoot(start) {
7964
8002
  }
7965
8003
  }
7966
8004
  async function cmdInit(nameArg, opts) {
8005
+ const target = path23.resolve(process.cwd(), nameArg);
8006
+ const targetPreexisted = fs24.existsSync(target);
8007
+ const cleanupOnCancel = () => {
8008
+ if (targetPreexisted || !fs24.existsSync(target)) {
8009
+ printErr("cancelled: no project was created.");
8010
+ return;
8011
+ }
8012
+ const walletsDir = path23.join(target, ".studio", "wallets");
8013
+ const hasWallet = fs24.existsSync(walletsDir) && fs24.readdirSync(walletsDir).length > 0;
8014
+ if (hasWallet) {
8015
+ printErr(
8016
+ `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.`
8017
+ );
8018
+ } else {
8019
+ fs24.rmSync(target, { recursive: true, force: true });
8020
+ printErr(
8021
+ `cancelled: removed the partially created ${target}; re-run \`bag init\` to start over.`
8022
+ );
8023
+ }
8024
+ };
8025
+ const onSigint = () => {
8026
+ cleanupOnCancel();
8027
+ process.exit(130);
8028
+ };
8029
+ process.once("SIGINT", onSigint);
8030
+ try {
8031
+ return await cmdInitFlow(nameArg, opts);
8032
+ } catch (exc) {
8033
+ if (exc instanceof Error && exc.name === "PromptCancelled") {
8034
+ cleanupOnCancel();
8035
+ return 130;
8036
+ }
8037
+ throw exc;
8038
+ } finally {
8039
+ process.removeListener("SIGINT", onSigint);
8040
+ }
8041
+ }
8042
+ async function cmdInitFlow(nameArg, opts) {
7967
8043
  const target = path23.resolve(process.cwd(), nameArg);
7968
8044
  let projectName2 = path23.basename(target);
7969
8045
  if (!projectName2) {
@@ -7972,6 +8048,14 @@ async function cmdInit(nameArg, opts) {
7972
8048
  );
7973
8049
  return 2;
7974
8050
  }
8051
+ if (opts.runtime === "agentcore" || opts.destination === "platform") {
8052
+ try {
8053
+ projectName2 = validateProjectName(projectName2);
8054
+ } catch (exc) {
8055
+ printErr(`error: ${exc instanceof Error ? exc.message : exc}`);
8056
+ return 2;
8057
+ }
8058
+ }
7975
8059
  if (opts.protocols !== void 0 && opts.protocol !== void 0) {
7976
8060
  printErr("error: pass either --protocols or --protocol, not both.");
7977
8061
  return 2;
@@ -8062,7 +8146,9 @@ async function cmdInit(nameArg, opts) {
8062
8146
  return 2;
8063
8147
  }
8064
8148
  if (fs24.existsSync(target) && !opts.force) {
8065
- printErr(`error: ${target} already exists. Use --force to overwrite.`);
8149
+ printErr(
8150
+ `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.`
8151
+ );
8066
8152
  return 2;
8067
8153
  }
8068
8154
  if (opts.destination === "platform" && opts.network === "bsc-mainnet") {
@@ -8086,13 +8172,20 @@ async function cmdInit(nameArg, opts) {
8086
8172
  destination = "self";
8087
8173
  runtime = "agentcore";
8088
8174
  }
8089
- if (destination === "self" && runtime === "azure-foundry" && (!hasA2aFace(faces) || hasMcpFace(faces))) {
8175
+ if (destination === "self" && runtime === "azure-foundry" && (hasMcpFace(faces) || !hasA2aFace(faces) && !hasX402Face(faces))) {
8090
8176
  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."
8177
+ "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
8178
  );
8093
8179
  return 2;
8094
8180
  }
8095
8181
  if (fs24.existsSync(target)) {
8182
+ const walletsDir = path23.join(target, ".studio", "wallets");
8183
+ if (fs24.existsSync(walletsDir) && fs24.readdirSync(walletsDir).length > 0) {
8184
+ printErr(
8185
+ `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.`
8186
+ );
8187
+ return 2;
8188
+ }
8096
8189
  fs24.rmSync(target, { recursive: true, force: true });
8097
8190
  }
8098
8191
  if (destination === "platform") {
@@ -8410,17 +8503,18 @@ function scaffold(target, name, o) {
8410
8503
  const isContainer = packaging === "container";
8411
8504
  const mode = recipeModeOf(o.faces);
8412
8505
  const stem2 = entryStemOf(o.faces);
8413
- const a2aEntry = o.runtime === "azure-foundry" ? "dist/foundryMain.js" : "dist/main.js";
8414
8506
  const runtimeCtx = {
8415
8507
  PKG: AGENT_SRC,
8416
8508
  TWAK_CLI_VERSION,
8417
8509
  PNPM_VERSION: packageManagerVersion,
8418
8510
  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.
8511
+ // The A2A entrypoint (unifiedMain) is cloud-portable: it binds BOTH the
8512
+ // AgentCore (9000) and Foundry (8088) contract ports itself, so the same
8513
+ // rendered image deploys to either cloud no runtime-specific entry.
8514
+ ENTRYPOINT: `dist/${stem2}.js`,
8515
+ // AGENT_PORT only prepends a primary override; the Foundry contract port
8516
+ // stays reachable regardless. MCP keeps its 8000 contract; Foundry
8517
+ // renders 8088 for continuity with the provider's documented default.
8424
8518
  CONTAINER_PORT: o.runtime === "azure-foundry" ? "8088" : String(devPortOf(o.faces))
8425
8519
  };
8426
8520
  emitRecipe(agentRoot2, "agent", { context: runtimeCtx });
@@ -8786,10 +8880,11 @@ function renderAgentStudioToml(name, o) {
8786
8880
  # Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
8787
8881
  # freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
8788
8882
  # 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)
8883
+ currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default; rarely changed
8884
+ # All prices are in wei of the currency token. U has 18 decimals (not 6 like USDC).
8885
+ price = "${o.erc8183Price}" # wei; 0 = FREE (zero token escrow)
8791
8886
  min_price = "0" # wei \u2014 clamp floor
8792
- max_price = "" # wei \u2014 clamp ceiling; SET before going live
8887
+ max_price = "" # wei \u2014 clamp ceiling; empty = unbounded; SET before going live
8793
8888
  quote_ttl_seconds = 900 # seconds a signed quote stays valid; SDK caps at 900 (~15min work window)
8794
8889
  default_estimated_completion_seconds = 600
8795
8890
  poll_interval_seconds = 30
@@ -8927,7 +9022,7 @@ function renderAgentReadme(name, faces, destination, walletKind2) {
8927
9022
  const port = String(devPortOf(faces));
8928
9023
  const x402Only = faces.length === 1 && hasX402Face(faces);
8929
9024
  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).
9025
+ const filesA2a = `- \`src/unifiedMain.ts\` \u2014 unified serving entrypoint (A2A on port 9000 + Foundry invocations on 8088).
8931
9026
  - \`src/executor.ts\` \u2014 the SellerAgentExecutor: the negotiate + notify_funded A2A skills.
8932
9027
  - \`src/agentCard.ts\` \u2014 the discoverable AgentCard (+ OAuth2/Cognito scheme).`;
8933
9028
  const filesMcp = `- \`src/mcpMain.ts\` \u2014 MCP serving entrypoint (streamable HTTP on \`/mcp\`; deploy
@@ -9077,10 +9172,23 @@ async function resolveWalletKind(flagValue, isTty) {
9077
9172
  );
9078
9173
  return "evm-local";
9079
9174
  }
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";
9175
+ printOut("Wallet backend (who holds the Agent's signer key):");
9176
+ printOut(
9177
+ " 1. evm-local [default] \u2014 encrypted local keystore under .studio/; simplest, works everywhere"
9178
+ );
9179
+ printOut(
9180
+ " 2. twak \u2014 Trust Wallet CLI wallet; forces container packaging for deploys"
9181
+ );
9182
+ printOut(
9183
+ " 3. altana \u2014 bounded session signer; incompatible with pieverse-llm and paid b402 selling"
9184
+ );
9185
+ const answer = (await promptUser("Wallet backend [1-3, Enter = evm-local]: ")).trim().toLowerCase();
9186
+ if (answer === "2" || answer === "twak") return "twak";
9187
+ if (answer === "3" || answer === "altana") return "altana";
9188
+ if (answer !== "" && answer !== "1" && answer !== "evm-local") {
9189
+ printErr(`hint: unknown wallet backend '${answer}' \u2014 using evm-local.`);
9190
+ }
9191
+ return "evm-local";
9084
9192
  }
9085
9193
  async function resolveLlmProviderForWallet(walletKind2, configuredProvider, valueSource, isTty) {
9086
9194
  if (walletKind2 !== "altana") {
@@ -9263,12 +9371,22 @@ function readWalletAddress(studioToml) {
9263
9371
  async function printFaucetHint(network, address) {
9264
9372
  printOut("");
9265
9373
  if (network === "bsc-testnet") {
9266
- const { BSC_TESTNET_FAUCET_URLS: BSC_TESTNET_FAUCET_URLS5 } = await import("@bnbagent/studio-runtime/networks");
9374
+ const {
9375
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET: BSC_TESTNET_TBNB_TELEGRAM_FAUCET2,
9376
+ BSC_TESTNET_FAUCET_DOCS_URL: BSC_TESTNET_FAUCET_DOCS_URL2,
9377
+ BSC_TESTNET_U_FAUCET_URL: BSC_TESTNET_U_FAUCET_URL4
9378
+ } = await import("@bnbagent/studio-runtime/networks");
9267
9379
  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
- }
9380
+ printOut("Fund it (free):");
9381
+ printOut(
9382
+ ` tBNB \u2014 message the official Telegram bot ${BSC_TESTNET_TBNB_TELEGRAM_FAUCET2}`
9383
+ );
9384
+ printOut(
9385
+ ` send: "I would like to get tBNB to my wallet ${address}"`
9386
+ );
9387
+ printOut(" (up to 0.3 tBNB per day; it replies with the tx hash)");
9388
+ printOut(` more ways to get tBNB: ${BSC_TESTNET_FAUCET_DOCS_URL2}`);
9389
+ printOut(` $U \u2014 ${BSC_TESTNET_U_FAUCET_URL4}`);
9272
9390
  printOut("(Faucets are needed only for paid models / on-chain settle.");
9273
9391
  printOut(" The default `auto/free` LLM model and AgentCore deploy");
9274
9392
  printOut(" do NOT require any funds.)");
@@ -9632,18 +9750,26 @@ function printMainTwakWalletWarning() {
9632
9750
  );
9633
9751
  }
9634
9752
  async function onboardIpfsKey(envPath, ipfsKey) {
9753
+ const url = (await promptUser(
9754
+ "\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): "
9755
+ )).trim();
9756
+ if (url) {
9757
+ setEnvVar(envPath, "STORAGE_API_URL", url);
9758
+ printOut("\u2713 STORAGE_API_URL written to .studio/.env.local");
9759
+ }
9635
9760
  let key = ipfsKey;
9636
9761
  if (key === void 0) {
9637
9762
  key = (await promptUser(
9638
- "\nIPFS pinning-service write key/JWT for deliverable storage (blank to skip, fill STORAGE_API_KEY later): "
9763
+ "IPFS pinning-service write key/JWT (blank to skip, fill STORAGE_API_KEY later): "
9639
9764
  )).trim();
9640
9765
  }
9641
9766
  if (key) {
9642
9767
  setEnvVar(envPath, "STORAGE_API_KEY", key);
9643
9768
  printOut("\u2713 STORAGE_API_KEY written to .studio/.env.local");
9644
- } else {
9769
+ }
9770
+ if (!url || !key) {
9645
9771
  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."
9772
+ "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
9773
  );
9648
9774
  }
9649
9775
  }
@@ -9665,7 +9791,7 @@ function printCampaignIntro() {
9665
9791
  );
9666
9792
  }
9667
9793
  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.";
9794
+ 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
9795
  printOut(
9670
9796
  `
9671
9797
  NOTE \u2014 wallet.kind = "twak" (Trust Wallet Agent Kit, CLI >= ${TWAK_CLI_VERSION}):
@@ -9772,9 +9898,9 @@ function entrypointHint(faces) {
9772
9898
  return "app/agent/src/mcpMain.ts = MCP entrypoint (build your value here)";
9773
9899
  }
9774
9900
  if (faces.length === 1 && hasX402Face(faces)) {
9775
- return "app/agent/src/main.ts = X402-only runtime entrypoint (build your value here)";
9901
+ return "app/agent/src/unifiedMain.ts = X402-only runtime entrypoint (build your value here)";
9776
9902
  }
9777
- return "app/agent/src/main.ts + executor.ts = A2A entrypoint (build your value here)";
9903
+ return "app/agent/src/unifiedMain.ts + executor.ts = A2A entrypoint (build your value here)";
9778
9904
  }
9779
9905
  function hasVendoredTarballs(agentDir) {
9780
9906
  const vendor = path23.join(agentDir, "vendor");
@@ -9822,18 +9948,17 @@ function printCompactNextSteps(name, o = {}) {
9822
9948
  " pnpm install # one-time: install the agent dependencies"
9823
9949
  );
9824
9950
  }
9951
+ printOut(" bag doctor # verify config + env before funding");
9952
+ printOut(" bag dev # run the Agent locally");
9825
9953
  if (destination === "platform") {
9826
- printOut(" bag dev # run the Agent locally");
9827
9954
  printOut(
9828
9955
  " bag deploy --provider bnb # Agent \u2192 managed platform (48h testnet trial)"
9829
9956
  );
9830
9957
  } else if (runtime === "azure-foundry") {
9831
- printOut(" bag dev # run the Agent locally");
9832
9958
  printOut(
9833
9959
  " bag deploy --provider azure # Agent \u2192 Azure AI Foundry (delegated to bnbagent-deploy; browser login, no azd)"
9834
9960
  );
9835
9961
  } else {
9836
- printOut(" agentcore dev # run the Agent locally on AgentCore");
9837
9962
  printOut(" bag deploy --provider aws # Agent \u2192 AgentCore");
9838
9963
  }
9839
9964
  printOut("");
@@ -9963,19 +10088,7 @@ function printNextSteps(name, provider, o = {}) {
9963
10088
  printOut(" # is `auto/free` (Free) \u2014 no funding required to start.");
9964
10089
  printOut(" bag llm activate");
9965
10090
  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;
10091
+ nextStep = 4;
9979
10092
  } else {
9980
10093
  const keyEnv = PROVIDER_KEY_ENV[provider];
9981
10094
  if (keyEnv) {
@@ -9987,6 +10100,26 @@ function printNextSteps(name, provider, o = {}) {
9987
10100
  }
9988
10101
  nextStep = 4;
9989
10102
  }
10103
+ printOut(` # ${nextStep}. verify + run the agent locally (no funds needed)`);
10104
+ printOut(" bag doctor # diagnose config + env; fix FAILs before deploy");
10105
+ printOut(" bag dev # serve the Agent locally for a smoke test");
10106
+ nextStep += 1;
10107
+ if (provider === "pieverse-llm") {
10108
+ printOut("");
10109
+ printOut(
10110
+ ` # ${nextStep}. (optional) fund the wallet \u2014 only when you need paid model / deploy / publish:`
10111
+ );
10112
+ printOut(
10113
+ " # testnet: send testnet BNB (gas) + testnet U to the address above"
10114
+ );
10115
+ printOut(" # mainnet: swap a small amount via PancakeSwap:");
10116
+ printOut(
10117
+ " # https://pancakeswap.finance/swap?outputCurrency=0xcE24439F2D9C6a2289F741120FE202248B666666"
10118
+ );
10119
+ printOut(" # Then: bag llm topup --amount N (allocate paid credits)");
10120
+ nextStep += 1;
10121
+ }
10122
+ printOut("");
9990
10123
  printOut(
9991
10124
  ` # ${nextStep}. review ERC-8183 pricing in studio.toml [payments.erc8183]`
9992
10125
  );
@@ -9994,10 +10127,6 @@ function printNextSteps(name, provider, o = {}) {
9994
10127
  " # (price was selected at init; `bag config set payments.erc8183.price 0` explicitly switches to FREE)"
9995
10128
  );
9996
10129
  nextStep += 1;
9997
- printOut(` # ${nextStep}. verify + run the agent locally`);
9998
- printOut(" bag doctor");
9999
- printOut(" bag dev");
10000
- nextStep += 1;
10001
10130
  if (destination === "platform") {
10002
10131
  printOut(
10003
10132
  ` # ${nextStep}. deploy: app/agent \u2192 managed platform (48h testnet trial)`
@@ -10298,6 +10427,11 @@ function runtimeEnvKeysCore(cfg, resolvable) {
10298
10427
  if (resolvable("RPC_URL")) {
10299
10428
  keys.push("RPC_URL");
10300
10429
  }
10430
+ const runtimeName = String(tableOf8(cfg, "stack").runtime ?? "agentcore");
10431
+ const publicUrlKeys = runtimeName === "azure-foundry" ? ["BNBAGENT_PUBLIC_URL"] : ["BNBAGENT_PUBLIC_URL", "AGENT_PUBLIC_URL"];
10432
+ for (const key of publicUrlKeys) {
10433
+ if (resolvable(key)) keys.push(key);
10434
+ }
10301
10435
  for (const key of PAYMASTER_URL_ENV_KEYS) {
10302
10436
  if (resolvable(key)) keys.push(key);
10303
10437
  }
@@ -10306,8 +10440,7 @@ function runtimeEnvKeysCore(cfg, resolvable) {
10306
10440
  if (resolvable(key)) keys.push(key);
10307
10441
  }
10308
10442
  }
10309
- const runtime = String(tableOf8(cfg, "stack").runtime ?? "agentcore");
10310
- if (runtime === "agentcore" && x402SellerUsesB402(cfg)) {
10443
+ if (X402_CAPABLE_RUNTIMES.has(runtimeName) && x402SellerUsesB402(cfg)) {
10311
10444
  for (const key of B402_RUNTIME_KEYS) {
10312
10445
  if (resolvable(key)) keys.push(key);
10313
10446
  }
@@ -10413,8 +10546,12 @@ function agentFaces(workspaceRoot) {
10413
10546
  const cfg = loadAgentToml2(workspaceRoot);
10414
10547
  return stackFaces(tableOf9(cfg, "stack"), tableOf9(cfg, "payments"));
10415
10548
  }
10416
- function localEntryFile(faces) {
10417
- return `src/${entryStemOf(faces)}.ts`;
10549
+ function localEntryFile(faces, agentDir) {
10550
+ const stem2 = entryStemOf(faces);
10551
+ const candidates = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].map(
10552
+ (s) => `src/${s}.ts`
10553
+ );
10554
+ return candidates.find((c2) => isFile10(path24.join(agentDir, c2))) ?? candidates[0];
10418
10555
  }
10419
10556
  function defaultAgentPort(faces) {
10420
10557
  return devPortOf(faces);
@@ -10786,11 +10923,11 @@ async function cmdDev(opts) {
10786
10923
  const agentPort = opts.port ?? defaultAgentPort(faces);
10787
10924
  const runtime = agentRuntime(workspaceRoot);
10788
10925
  const container = Boolean(opts.container);
10789
- const entryFile = localEntryFile(faces);
10926
+ const entryFile = localEntryFile(faces, agentDir);
10790
10927
  const agentFile = path24.join(agentDir, entryFile);
10791
10928
  if (!isFile10(agentFile)) {
10792
10929
  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.`
10930
+ `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
10931
  );
10795
10932
  return 2;
10796
10933
  }
@@ -10929,7 +11066,7 @@ Switch to Node \u226522 so it wins on PATH, then reopen the shell and retry. Ver
10929
11066
  printOut(
10930
11067
  `bag dev: starting Agent in-process (\`tsx ${args.join(" ")}\` on :${agentPort}, no Docker \u2014 the entrypoint self-serves; cwd=${agentDir}, runner=${runner2})`
10931
11068
  );
10932
- if (runtime === RUNTIME_AZURE_FOUNDRY) {
11069
+ if (runtime === RUNTIME_AZURE_FOUNDRY && isFile10(path24.join(agentDir, "src/foundryMain.ts"))) {
10933
11070
  printOut(
10934
11071
  "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
11072
  );
@@ -11102,7 +11239,10 @@ async function checkAgentPackagePresent(root, target) {
11102
11239
  const cfg = loadAgentToml(root);
11103
11240
  const faces = stackFaces(tableOf2(cfg, "stack"), tableOf2(cfg, "payments"));
11104
11241
  const stem2 = entryStemOf(faces);
11105
- const sourceEntries = [`src/${stem2}.ts`];
11242
+ const effectiveStem = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].find(
11243
+ (s) => isFile11(path25.join(agentRoot2, `src/${s}.ts`))
11244
+ ) ?? stem2;
11245
+ const sourceEntries = [`src/${effectiveStem}.ts`];
11106
11246
  if (stem2 === "dualMain") {
11107
11247
  sourceEntries.push("src/mcpMain.ts");
11108
11248
  }
@@ -11120,7 +11260,7 @@ async function checkAgentPackagePresent(root, target) {
11120
11260
  }
11121
11261
  ];
11122
11262
  }
11123
- const distEntries = [`dist/${stem2}.js`];
11263
+ const distEntries = [`dist/${effectiveStem}.js`];
11124
11264
  if (stem2 === "dualMain") {
11125
11265
  distEntries.push("dist/mcpMain.js");
11126
11266
  }
@@ -11600,12 +11740,12 @@ function x402SellerRailChecks(agentRoot2, cfg, target) {
11600
11740
  }
11601
11741
  const destination = String(tableOf2(cfg, "deploy").destination ?? "self");
11602
11742
  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;
11743
+ if (rails.x402 && (free || credentials.complete) && !target.startsWith("platform") && !(X402_CAPABLE_RUNTIMES.has(target) && X402_CAPABLE_RUNTIMES.has(stackRuntime2))) {
11744
+ const blocking = !X402_CAPABLE_RUNTIMES.has(target) ? target : stackRuntime2;
11605
11745
  out.push({
11606
11746
  level: Level.WARNING,
11607
11747
  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.`,
11748
+ 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
11749
  details: { destination, target, stackRuntime: stackRuntime2 }
11610
11750
  });
11611
11751
  }
@@ -11656,7 +11796,7 @@ async function checkCommerceReady(root, target) {
11656
11796
  return out;
11657
11797
  }
11658
11798
  async function checkDeployCliRunnable(_root, target) {
11659
- if (target !== "agentcore") {
11799
+ if (target !== "agentcore" && target !== "platform" && target !== "platform-azure") {
11660
11800
  return [];
11661
11801
  }
11662
11802
  const override = (process.env.BNBAGENT_DEPLOY_COMMAND ?? "").trim();
@@ -13062,7 +13202,11 @@ import {
13062
13202
  findSubProjectRoot as findSubProjectRoot13,
13063
13203
  loadStudioToml as loadStudioToml17
13064
13204
  } from "@bnbagent/studio-runtime/config";
13065
- import { BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS2 } from "@bnbagent/studio-runtime/networks";
13205
+ import {
13206
+ BSC_TESTNET_FAUCET_DOCS_URL,
13207
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET,
13208
+ BSC_TESTNET_U_FAUCET_URL as BSC_TESTNET_U_FAUCET_URL2
13209
+ } from "@bnbagent/studio-runtime/networks";
13066
13210
 
13067
13211
  // src/cli/_deploy/checks/warning.ts
13068
13212
  import * as crypto4 from "crypto";
@@ -13372,7 +13516,10 @@ var checkPlatformEntrypointPresent = async (root, _target) => {
13372
13516
  const data = loadAgent(root);
13373
13517
  const faces = stackFaces(tableOf11(data, "stack"), tableOf11(data, "payments"));
13374
13518
  const stem2 = entryStemOf(faces);
13375
- const filenames = [`${stem2}.ts`];
13519
+ const effectiveStem = [stem2, ...LEGACY_ENTRY_STEMS[stem2]].find(
13520
+ (s) => fs35.existsSync(path34.join(agentRoot(root), "src", `${s}.ts`))
13521
+ ) ?? stem2;
13522
+ const filenames = [`${effectiveStem}.ts`];
13376
13523
  if (stem2 === "dualMain") filenames.push("mcpMain.ts");
13377
13524
  const filename = filenames.find(
13378
13525
  (candidate) => !fs35.existsSync(path34.join(agentRoot(root), "src", candidate))
@@ -13536,8 +13683,14 @@ var checkPlatformFundingHint = async (_root, _target) => [
13536
13683
  {
13537
13684
  level: Level.INFO,
13538
13685
  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] }
13686
+ 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}`,
13687
+ details: {
13688
+ faucets: [
13689
+ BSC_TESTNET_TBNB_TELEGRAM_FAUCET,
13690
+ BSC_TESTNET_FAUCET_DOCS_URL,
13691
+ BSC_TESTNET_U_FAUCET_URL2
13692
+ ]
13693
+ }
13541
13694
  }
13542
13695
  ];
13543
13696
  var criticalChecks = [
@@ -13546,6 +13699,7 @@ var criticalChecks = [
13546
13699
  checkPlatformTestnetOnly,
13547
13700
  checkPlatformSlugValid,
13548
13701
  checkPlatformLoggedIn,
13702
+ checkDeployCliRunnable,
13549
13703
  checkPlatformDockerAvailable,
13550
13704
  checkPlatformStorageEndpoint,
13551
13705
  checkTwakCustomContractsUnsupported,
@@ -14308,7 +14462,7 @@ async function platformAgent(opts) {
14308
14462
  const root = opts.root;
14309
14463
  const backend = opts.backend ?? "aws";
14310
14464
  const isTty = opts.isTty ?? stdinIsTty;
14311
- const ask = opts.promptUser ?? promptUser;
14465
+ const ask2 = opts.promptUser ?? promptUser;
14312
14466
  warn(
14313
14467
  `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
14468
  );
@@ -14317,12 +14471,6 @@ async function platformAgent(opts) {
14317
14471
  const cfg2 = loadAgentCfg(root);
14318
14472
  const faces2 = stackFaces(tableOf12(cfg2, "stack"), tableOf12(cfg2, "payments"));
14319
14473
  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
14474
  const runtimeProfile = protocol2 === "A2A" ? "foundry-responses-v1" : "foundry-invocations-v1";
14327
14475
  const capability = await platformCmd(["capabilities"], { json: true });
14328
14476
  const platform = isJsonObject(capability.data.platform) ? capability.data.platform : {};
@@ -14336,6 +14484,12 @@ async function platformAgent(opts) {
14336
14484
  );
14337
14485
  return capability.code || 1;
14338
14486
  }
14487
+ if (hasX402Face(faces2) && azure.x402 !== true) {
14488
+ warn(
14489
+ "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)."
14490
+ );
14491
+ return 1;
14492
+ }
14339
14493
  }
14340
14494
  const campaignProbe = opts.campaignActive ?? (async () => null);
14341
14495
  const trialRes = await platformCmd(["trial"], { json: true });
@@ -14364,7 +14518,7 @@ async function platformAgent(opts) {
14364
14518
  const rc = await termsConsentGate(root, {
14365
14519
  acceptRisk: opts.acceptRisk ?? false,
14366
14520
  isTty,
14367
- promptUser: ask
14521
+ promptUser: ask2
14368
14522
  });
14369
14523
  if (rc !== null) {
14370
14524
  return rc;
@@ -14375,7 +14529,7 @@ async function platformAgent(opts) {
14375
14529
  const confirm = opts.confirmPackaging ?? ((r, p) => confirmPackagingDefault(r, p, {
14376
14530
  acceptRisk: opts.acceptRisk ?? false,
14377
14531
  isTty,
14378
- promptUser: ask
14532
+ promptUser: ask2
14379
14533
  }));
14380
14534
  const rc = await confirm(root, packaging);
14381
14535
  if (rc !== null && rc !== 0) {
@@ -16861,12 +17015,13 @@ import { pieverseKeyHash as pieverseKeyHash4 } from "@bnbagent/studio-runtime/ll
16861
17015
  import {
16862
17016
  BSC_MAINNET_MIN_BNB,
16863
17017
  BSC_MAINNET_MIN_U,
16864
- BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS3,
16865
17018
  BSC_TESTNET_MIN_BNB,
16866
17019
  BSC_TESTNET_MIN_U,
17020
+ BSC_TESTNET_U_FAUCET_URL as BSC_TESTNET_U_FAUCET_URL3,
16867
17021
  ERC8183_SUBMIT_GAS_RESERVE_BNB,
16868
17022
  fromRaw as fromRaw3,
16869
- getNetwork as getNetwork9
17023
+ getNetwork as getNetwork9,
17024
+ tbnbFaucetHint as tbnbFaucetHint3
16870
17025
  } from "@bnbagent/studio-runtime/networks";
16871
17026
  import { listModels as listModels2 } from "@bnbagent/studio-runtime/pieverse";
16872
17027
  import * as walletRt5 from "@bnbagent/studio-runtime/wallet";
@@ -16971,6 +17126,9 @@ async function cmdDoctor(opts) {
16971
17126
  checks.push(...await check8004(projectRoot, data, opts.network));
16972
17127
  const destination = String(tableOf14(data, "deploy").destination ?? "self").trim().toLowerCase();
16973
17128
  checks.push(...await checkDeployTools(data, destination));
17129
+ checks.push(
17130
+ ...await checkPlatformZipBundle(data, destination, projectRoot)
17131
+ );
16974
17132
  checks.push(...checkAgentsMdSkillTrigger(projectRoot));
16975
17133
  printChecks(checks);
16976
17134
  return checks.some((c2) => c2.status === FAIL) ? 1 : 0;
@@ -17053,16 +17211,12 @@ function checkAppMain(projectRootArg2) {
17053
17211
  }
17054
17212
  }
17055
17213
  const stack = tableOf14(config, "stack");
17214
+ const faces = stackFaces(stack, tableOf14(config, "payments"));
17215
+ const stem2 = entryStemOf(faces);
17056
17216
  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
- }
17217
+ const legacyStems = runtime === "azure-foundry" && stem2 === "unifiedMain" ? ["foundryMain", "main"] : LEGACY_ENTRY_STEMS[stem2];
17218
+ const candidates = [stem2, ...legacyStems].map((s) => `src/${s}.ts`);
17219
+ const entrypoint = candidates.find((c2) => isFile17(path39.join(agentDir, c2))) ?? candidates[0];
17066
17220
  const entryFile = path39.join(agentDir, entrypoint);
17067
17221
  if (!isFile17(entryFile)) {
17068
17222
  return [
@@ -17073,7 +17227,7 @@ function checkAppMain(projectRootArg2) {
17073
17227
  }
17074
17228
  ];
17075
17229
  }
17076
- const detail = runtime === "azure-foundry" ? `${entrypoint} (deploy entrypoint; \`bag dev\` serves src/main.ts or src/mcpMain.ts locally)` : entrypoint;
17230
+ const detail = entrypoint === "src/foundryMain.ts" ? `${entrypoint} (deploy entrypoint; \`bag dev\` serves src/main.ts or src/mcpMain.ts locally)` : entrypoint;
17077
17231
  return [{ name: "agent entrypoint", status: PASS, detail }];
17078
17232
  }
17079
17233
  function checkAgentcoreName(projectRoot) {
@@ -17851,7 +18005,8 @@ async function checkBalances(projectRoot, data, networkOverride) {
17851
18005
  const isMainnet = name === "bsc-mainnet";
17852
18006
  const minBnb = isTestnet ? BSC_TESTNET_MIN_BNB : isMainnet ? BSC_MAINNET_MIN_BNB : 0;
17853
18007
  const minBnbWei = BigInt(Math.round(minBnb * 1e6)) * 10n ** 12n;
17854
- const faucetHint = isTestnet ? ` \u2014 faucets: ${BSC_TESTNET_FAUCET_URLS3.join(", ")}` : "";
18008
+ const bnbFaucetHint = isTestnet ? ` \u2014 ${tbnbFaucetHint3(address)}` : "";
18009
+ const uFaucetHint = isTestnet ? ` \u2014 $U faucet: ${BSC_TESTNET_U_FAUCET_URL3}` : "";
17855
18010
  const bnbRaw = await readNativeBalanceRaw(address, name);
17856
18011
  const reserve = ERC8183_SUBMIT_GAS_RESERVE_BNB;
17857
18012
  const reserveHint = ` (each on-chain action reserves \u2248 ${reserve} BNB worst-case)`;
@@ -17872,7 +18027,7 @@ async function checkBalances(projectRoot, data, networkOverride) {
17872
18027
  out.push({
17873
18028
  name: "wallet BNB balance",
17874
18029
  status: WARN,
17875
- detail: `${bnbRaw} wei @ ${address} \u2014 need \u2265 ${minBnb} BNB on ${name} for 8183 escrow / ERC-8004 register${reserveHint}${faucetHint}`
18030
+ detail: `${bnbRaw} wei @ ${address} \u2014 need \u2265 ${minBnb} BNB on ${name} for 8183 escrow / ERC-8004 register${reserveHint}${bnbFaucetHint}`
17876
18031
  });
17877
18032
  }
17878
18033
  const uToken = net2.tokens.U;
@@ -17898,7 +18053,7 @@ async function checkBalances(projectRoot, data, networkOverride) {
17898
18053
  out.push({
17899
18054
  name: "wallet U balance",
17900
18055
  status: WARN,
17901
- detail: `${uRaw} raw @ ${address} \u2014 need \u2265 ${minU} U on ${name} for one auto-topup chunk${faucetHint}`
18056
+ detail: `${uRaw} raw @ ${address} \u2014 need \u2265 ${minU} U on ${name} for one auto-topup chunk${uFaucetHint}`
17902
18057
  });
17903
18058
  }
17904
18059
  return out;
@@ -18047,11 +18202,7 @@ async function checkDeployTools(data, destination) {
18047
18202
  });
18048
18203
  if (destination === "platform") {
18049
18204
  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
- },
18205
+ emit2(await checkBunx()),
18055
18206
  {
18056
18207
  name: "deploy: AWS credentials",
18057
18208
  status: INFO,
@@ -18081,6 +18232,49 @@ async function checkDeployTools(data, destination) {
18081
18232
  }
18082
18233
  return (await deployChecks()).map(emit2);
18083
18234
  }
18235
+ async function checkPlatformZipBundle(data, destination, projectRootArg2) {
18236
+ if (destination !== "platform" || platformPackagingRequiresContainer(data)) {
18237
+ return [];
18238
+ }
18239
+ let projectRoot = projectRootArg2;
18240
+ const parent = path39.dirname(projectRoot);
18241
+ if (isFile17(path39.join(parent, "agent", "studio.toml"))) {
18242
+ projectRoot = parent;
18243
+ }
18244
+ const agentDir = path39.join(projectRoot, "agent");
18245
+ if (!isFile17(path39.join(agentDir, "studio.toml"))) {
18246
+ return [];
18247
+ }
18248
+ if (!fs39.existsSync(path39.join(agentDir, "node_modules"))) {
18249
+ return [
18250
+ {
18251
+ name: "deploy: zip bundle",
18252
+ status: INFO,
18253
+ detail: "skipped \u2014 dependencies not installed yet (run `pnpm install`); re-run `bag doctor` to dry-run the zip bundle."
18254
+ }
18255
+ ];
18256
+ }
18257
+ const stack = tableOf14(data, "stack");
18258
+ const faces = stackFaces(stack, tableOf14(data, "payments"));
18259
+ try {
18260
+ await dryRunBundle(agentDir, { protocols: faces });
18261
+ } catch (exc) {
18262
+ return [
18263
+ {
18264
+ name: "deploy: zip bundle",
18265
+ status: WARN,
18266
+ detail: errMsg3(exc)
18267
+ }
18268
+ ];
18269
+ }
18270
+ return [
18271
+ {
18272
+ name: "deploy: zip bundle",
18273
+ status: PASS,
18274
+ detail: "esbuild dry run OK \u2014 the agent bundles for the platform ZIP runtime."
18275
+ }
18276
+ ];
18277
+ }
18084
18278
  function checkAgentsMdSkillTrigger(projectRoot) {
18085
18279
  const ws = findStudioWorkspaceRoot4(projectRoot);
18086
18280
  if (ws === null) {
@@ -18259,9 +18453,9 @@ import {
18259
18453
  updateServiceEndpoint as updateServiceEndpoint2
18260
18454
  } from "@bnbagent/studio-runtime/erc8004";
18261
18455
  import {
18262
- BSC_TESTNET_FAUCET_URLS as BSC_TESTNET_FAUCET_URLS4,
18263
18456
  fromRaw as fromRaw4,
18264
- getNetwork as getNetwork10
18457
+ getNetwork as getNetwork10,
18458
+ tbnbFaucetHint as tbnbFaucetHint4
18265
18459
  } from "@bnbagent/studio-runtime/networks";
18266
18460
  import * as walletRt6 from "@bnbagent/studio-runtime/wallet";
18267
18461
  import { Option as Option5 } from "commander";
@@ -18423,8 +18617,7 @@ async function precheckRegisterGas(wallet, network) {
18423
18617
  if (balance >= REGISTER_MIN_BNB) {
18424
18618
  return null;
18425
18619
  }
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}`;
18620
+ 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
18621
  }
18429
18622
  function syncAgentRegistry(agentId, op) {
18430
18623
  try {
@@ -19374,7 +19567,7 @@ async function promptInstallScope() {
19374
19567
  }
19375
19568
  function printNextSteps2() {
19376
19569
  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."
19570
+ "\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
19571
  );
19379
19572
  }
19380
19573
  async function installInteractive(flags) {
@@ -21183,6 +21376,10 @@ async function main(argv) {
21183
21376
  if (err instanceof CliExit) {
21184
21377
  return err.code;
21185
21378
  }
21379
+ if (err instanceof Error && err.name === "PromptCancelled") {
21380
+ process.stderr.write("cancelled.\n");
21381
+ return 130;
21382
+ }
21186
21383
  if (isCommanderExit(err)) {
21187
21384
  return err.exitCode;
21188
21385
  }