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

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.
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-RO726HJG.js";
9
9
 
10
10
  // src/cli/_deploy/deployCli.ts
11
- import * as fs4 from "fs";
11
+ import * as fs5 from "fs";
12
12
  import * as os from "os";
13
- import * as path5 from "path";
13
+ import * as path6 from "path";
14
14
  import {
15
15
  findSubProjectRoot as findSubProjectRoot2,
16
16
  loadStudioToml as loadStudioToml3
@@ -662,36 +662,82 @@ function readAwsTarget(root, targetName = DEFAULT_TARGET_NAME) {
662
662
  }
663
663
 
664
664
  // src/cli/_deploy/x402.ts
665
- import * as path4 from "path";
665
+ import * as path5 from "path";
666
666
  import {
667
667
  envLocalPath,
668
668
  findSubProjectRoot,
669
669
  loadStudioToml as loadStudioToml2
670
670
  } from "@bnbagent/studio-runtime/config";
671
671
 
672
- // src/cli/utils/envFile.ts
672
+ // src/cli/_packageMetadata.ts
673
673
  import * as fs3 from "fs";
674
674
  import * as path3 from "path";
675
+ import { fileURLToPath } from "url";
676
+ function packageRoot() {
677
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
678
+ for (; ; ) {
679
+ const packageJson = path3.join(dir, "package.json");
680
+ if (fs3.existsSync(packageJson)) return dir;
681
+ const parent = path3.dirname(dir);
682
+ if (parent === dir) {
683
+ throw new Error("cannot locate the studio-cli package root");
684
+ }
685
+ dir = parent;
686
+ }
687
+ }
688
+ function studioCliVersion() {
689
+ if ("0.0.11-alpha.4") {
690
+ return "0.0.11-alpha.4";
691
+ }
692
+ const file = path3.join(packageRoot(), "package.json");
693
+ const pkg = JSON.parse(fs3.readFileSync(file, "utf-8"));
694
+ const value = String(pkg.version ?? "");
695
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value)) {
696
+ throw new Error(
697
+ `studio-cli package.json must contain a valid version in ${file}`
698
+ );
699
+ }
700
+ return value;
701
+ }
702
+ function pnpmVersion() {
703
+ if ("10.24.0") {
704
+ return "10.24.0";
705
+ }
706
+ const file = path3.join(packageRoot(), "package.json");
707
+ const pkg = JSON.parse(fs3.readFileSync(file, "utf-8"));
708
+ const value = String(pkg.packageManager ?? "");
709
+ const match = /^pnpm@(.+)$/u.exec(value);
710
+ if (!match?.[1]) {
711
+ throw new Error(
712
+ `studio-cli packageManager must pin pnpm (expected "pnpm@<version>" in ${file})`
713
+ );
714
+ }
715
+ return match[1];
716
+ }
717
+
718
+ // src/cli/utils/envFile.ts
719
+ import * as fs4 from "fs";
720
+ import * as path4 from "path";
675
721
  var KEY_LINE_RE = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/;
676
722
  function isValidKey(key) {
677
723
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
678
724
  }
679
725
  function writePrivate(filePath, text) {
680
- fs3.writeFileSync(filePath, text, { encoding: "utf-8", mode: 384 });
681
- fs3.chmodSync(filePath, 384);
726
+ fs4.writeFileSync(filePath, text, { encoding: "utf-8", mode: 384 });
727
+ fs4.chmodSync(filePath, 384);
682
728
  }
683
729
  function setEnvVar(filePath, key, value) {
684
730
  if (!isValidKey(key)) {
685
731
  throw new Error(`invalid env var name: '${key}'`);
686
732
  }
687
733
  const newLine = `${key}=${value}`;
688
- fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
689
- if (!fs3.existsSync(filePath)) {
734
+ fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
735
+ if (!fs4.existsSync(filePath)) {
690
736
  writePrivate(filePath, `${newLine}
691
737
  `);
692
738
  return true;
693
739
  }
694
- const original = fs3.readFileSync(filePath, "utf-8");
740
+ const original = fs4.readFileSync(filePath, "utf-8");
695
741
  const lines = original.split(/\r?\n/);
696
742
  if (lines.length > 0 && lines[lines.length - 1] === "") {
697
743
  lines.pop();
@@ -722,10 +768,10 @@ function setEnvVar(filePath, key, value) {
722
768
  return true;
723
769
  }
724
770
  function getEnvVar(filePath, key) {
725
- if (!fs3.existsSync(filePath)) {
771
+ if (!fs4.existsSync(filePath)) {
726
772
  return null;
727
773
  }
728
- for (const line of fs3.readFileSync(filePath, "utf-8").split(/\r?\n/)) {
774
+ for (const line of fs4.readFileSync(filePath, "utf-8").split(/\r?\n/)) {
729
775
  const m = KEY_LINE_RE.exec(line);
730
776
  if (m && m[1] === key) {
731
777
  const eq = line.indexOf("=");
@@ -779,7 +825,7 @@ function loadDeployConfig(root) {
779
825
  try {
780
826
  return {
781
827
  agentRoot,
782
- cfg: loadStudioToml2(path4.join(agentRoot, "studio.toml"))
828
+ cfg: loadStudioToml2(path5.join(agentRoot, "studio.toml"))
783
829
  };
784
830
  } catch {
785
831
  return { agentRoot, cfg: {} };
@@ -799,7 +845,7 @@ function x402DeploySummary(root, destination, publicUrl) {
799
845
  const azure = runtime === "azure-foundry";
800
846
  const runtimeLabel = azure ? "Azure Foundry" : "AgentCore";
801
847
  const tunnelLine = azure ? 'The front wraps each request as envelope-v1 JSON, {"v":1,"method":"POST","path":"/x402","headers":{...},"body":"<base64>"}, inside an Entra-authenticated Foundry invocation (POST \u2026/agents/<name>/endpoint/protocols/invocations with an https://ai.azure.com bearer). Limits: 1 MiB request, 5 MiB response, no streaming.' : 'The front wraps each request as envelope-v1 JSON, {"v":1,"method":"POST","path":"/x402","headers":{...},"body":"<base64>"}, inside an authenticated AgentCore invocation. The default Bag self-deploy uses Cognito OAuth over HTTPS; IAM runtimes may use SDK/SigV4. Limits: 1 MiB request, 5 MiB response, no streaming.';
802
- const gatewayDocLine = azure ? "Gateway example: https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway-azure.md" : "Gateway example: https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md";
848
+ const gatewayDocLine = azure ? `Gateway example: https://unpkg.com/@bnbagent/studio-cli@${studioCliVersion()}/skills/references/bnbagent-studio-use-azure-foundry.md#x402-external-gateway` : "Gateway example: https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md";
803
849
  if (pricing.kind === "invalid") {
804
850
  return "x402 rail is DORMANT: price_usd is invalid; run `bag doctor`.";
805
851
  }
@@ -858,7 +904,7 @@ function isTable(value) {
858
904
  }
859
905
 
860
906
  // src/cli/_deploy/deployCli.ts
861
- var DEPLOY_CLI_VERSION = "0.5.10";
907
+ var DEPLOY_CLI_VERSION = "0.5.11";
862
908
  var DEPLOY_CLI_PACKAGE = `@bnbagent/deploy-cli@${DEPLOY_CLI_VERSION}`;
863
909
  var BNB_PLATFORM_API_URL = "https://bnbagent-api.bnbchain.world";
864
910
  var BNB_PLATFORM_API_URL_ENV = "BNBAGENT_API_URL";
@@ -899,7 +945,7 @@ function tableOf(data, key) {
899
945
  function descriptorName(root) {
900
946
  try {
901
947
  const raw = JSON.parse(
902
- fs4.readFileSync(path5.join(root, "agentcore", "agentcore.json"), "utf-8")
948
+ fs5.readFileSync(path6.join(root, "agentcore", "agentcore.json"), "utf-8")
903
949
  );
904
950
  if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
905
951
  const name = raw.name;
@@ -936,9 +982,9 @@ function providerPassthrough(studio, table4) {
936
982
  }
937
983
  function buildDeploySpec(root, opts) {
938
984
  const agentRoot = findSubProjectRoot2("agent", root) ?? root;
939
- const studio = loadStudioToml3(path5.join(agentRoot, "studio.toml"));
985
+ const studio = loadStudioToml3(path6.join(agentRoot, "studio.toml"));
940
986
  const name = String(
941
- opts.nameOverride || descriptorName(root) || tableOf(studio, "project").name || path5.basename(root)
987
+ opts.nameOverride || descriptorName(root) || tableOf(studio, "project").name || path6.basename(root)
942
988
  );
943
989
  const stack = tableOf(studio, "stack");
944
990
  const payments = tableOf(studio, "payments");
@@ -960,7 +1006,7 @@ function buildDeploySpec(root, opts) {
960
1006
  OTEL_TRACES_EXPORTER: "none"
961
1007
  };
962
1008
  if (isContainer) {
963
- deploy.dockerfile = path5.join(agentRoot, "Dockerfile");
1009
+ deploy.dockerfile = path6.join(agentRoot, "Dockerfile");
964
1010
  } else {
965
1011
  if (!opts.zip) {
966
1012
  throw new Error("zip packaging requires a prebuilt zip artifact");
@@ -1066,27 +1112,27 @@ function renderSecretEnvFile(payload) {
1066
1112
  `).join("");
1067
1113
  }
1068
1114
  function writePrivateSync(filePath, content) {
1069
- fs4.writeFileSync(filePath, content, { mode: 384 });
1070
- fs4.chmodSync(filePath, 384);
1115
+ fs5.writeFileSync(filePath, content, { mode: 384 });
1116
+ fs5.chmodSync(filePath, 384);
1071
1117
  }
1072
1118
  async function withDeployFiles(root, opts, fn) {
1073
- const scratch = fs4.mkdtempSync(path5.join(os.tmpdir(), "bag-deploy-"));
1119
+ const scratch = fs5.mkdtempSync(path6.join(os.tmpdir(), "bag-deploy-"));
1074
1120
  try {
1075
1121
  let zip;
1076
1122
  if (opts.packaging === "zip") {
1077
1123
  const agentRoot = findSubProjectRoot2("agent", root) ?? root;
1078
1124
  const studio = loadStudioToml3(
1079
- path5.join(agentRoot, "studio.toml")
1125
+ path6.join(agentRoot, "studio.toml")
1080
1126
  );
1081
1127
  const stack = tableOf(studio, "stack");
1082
1128
  const faces = stackFaces(stack, tableOf(studio, "payments"));
1083
- const outZip = path5.join(scratch, "code.zip");
1129
+ const outZip = path6.join(scratch, "code.zip");
1084
1130
  const built = await (opts.buildZipFn ?? buildZip)(agentRoot, outZip, {
1085
1131
  protocols: faces
1086
1132
  });
1087
1133
  zip = { path: outZip, entrypoint: built.entrypoint };
1088
1134
  }
1089
- const secretFile = path5.join(scratch, "secrets.env");
1135
+ const secretFile = path6.join(scratch, "secrets.env");
1090
1136
  const inline = opts.inlineSecrets;
1091
1137
  const payload = opts.secrets ?? {};
1092
1138
  const spec = buildDeploySpec(root, {
@@ -1100,11 +1146,11 @@ async function withDeployFiles(root, opts, fn) {
1100
1146
  if (!inline && Object.keys(payload).length > 0) {
1101
1147
  writePrivateSync(secretFile, renderSecretEnvFile(payload));
1102
1148
  }
1103
- const configPath = path5.join(scratch, "agent-deploy.toml");
1149
+ const configPath = path6.join(scratch, "agent-deploy.toml");
1104
1150
  writePrivateSync(configPath, tomlStringify(spec.doc));
1105
1151
  return await fn({ configPath, scratchDir: scratch }, spec);
1106
1152
  } finally {
1107
- fs4.rmSync(scratch, { recursive: true, force: true });
1153
+ fs5.rmSync(scratch, { recursive: true, force: true });
1108
1154
  }
1109
1155
  }
1110
1156
  var BUNX_HINT = "error: could not start bnbagent-deploy (bunx not found); install Bun 1.3+ or set BNBAGENT_DEPLOY_COMMAND.";
@@ -1129,7 +1175,7 @@ function bunxInstallDir(environment = process.env) {
1129
1175
  return null;
1130
1176
  }
1131
1177
  const uid = typeof process.getuid === "function" ? process.getuid() : null;
1132
- return uid === null ? null : path5.join(os.tmpdir(), `bunx-${uid}-${DEPLOY_CLI_PACKAGE}`);
1178
+ return uid === null ? null : path6.join(os.tmpdir(), `bunx-${uid}-${DEPLOY_CLI_PACKAGE}`);
1133
1179
  }
1134
1180
  async function bunxTreeIsIncomplete() {
1135
1181
  const [bin, ...prefix] = deployCommand();
@@ -1148,14 +1194,14 @@ async function withBunxSelfHeal(hasFailed, attempt) {
1148
1194
  if (dir === null || !await bunxTreeIsIncomplete()) {
1149
1195
  return first;
1150
1196
  }
1151
- if (!fs4.existsSync(dir)) {
1197
+ if (!fs5.existsSync(dir)) {
1152
1198
  printErr(BUNX_TREE_HINT);
1153
1199
  return first;
1154
1200
  }
1155
1201
  printErr(
1156
1202
  `note: ${DEPLOY_CLI_PACKAGE} failed to load its dependencies; clearing its incomplete bunx install and retrying once (${dir}).`
1157
1203
  );
1158
- fs4.rmSync(dir, { recursive: true, force: true });
1204
+ fs5.rmSync(dir, { recursive: true, force: true });
1159
1205
  const second = await attempt();
1160
1206
  if (hasFailed(second)) {
1161
1207
  printErr(BUNX_TREE_HINT);
@@ -1215,7 +1261,10 @@ function trialFromDeployCliJson(data) {
1215
1261
  };
1216
1262
  }
1217
1263
  function bnbEnv() {
1218
- return { [BNB_PLATFORM_API_URL_ENV]: bnbPlatformApiUrl() };
1264
+ return {
1265
+ [BNB_PLATFORM_API_URL_ENV]: bnbPlatformApiUrl(),
1266
+ BNBAGENT_CLI_SURFACE: "studio"
1267
+ };
1219
1268
  }
1220
1269
  async function runPlatformAccountCommand(argv, opts = {}) {
1221
1270
  const run = async (args, cwd) => {
@@ -1228,9 +1277,9 @@ async function runPlatformAccountCommand(argv, opts = {}) {
1228
1277
  if (!opts.slug) {
1229
1278
  return run([...argv, "--provider", "bnb"]);
1230
1279
  }
1231
- const scratch = fs4.mkdtempSync(path5.join(os.tmpdir(), "bag-platform-"));
1280
+ const scratch = fs5.mkdtempSync(path6.join(os.tmpdir(), "bag-platform-"));
1232
1281
  try {
1233
- const configPath = path5.join(scratch, "agent-deploy.toml");
1282
+ const configPath = path6.join(scratch, "agent-deploy.toml");
1234
1283
  writePrivateSync(
1235
1284
  configPath,
1236
1285
  tomlStringify({
@@ -1241,7 +1290,7 @@ async function runPlatformAccountCommand(argv, opts = {}) {
1241
1290
  );
1242
1291
  return await run([...argv, "--provider", "bnb", "-f", configPath], scratch);
1243
1292
  } finally {
1244
- fs4.rmSync(scratch, { recursive: true, force: true });
1293
+ fs5.rmSync(scratch, { recursive: true, force: true });
1245
1294
  }
1246
1295
  }
1247
1296
 
@@ -1281,6 +1330,8 @@ export {
1281
1330
  deployChecks,
1282
1331
  render,
1283
1332
  readAwsTarget,
1333
+ studioCliVersion,
1334
+ pnpmVersion,
1284
1335
  B402_RUNTIME_KEYS,
1285
1336
  X402_CAPABLE_RUNTIMES,
1286
1337
  commerceRails,
@@ -18,7 +18,7 @@ import {
18
18
  runPlatformAccountCommand,
19
19
  trialFromDeployCliJson,
20
20
  withDeployFiles
21
- } from "./chunk-TTPOH453.js";
21
+ } from "./chunk-GKFTJDDJ.js";
22
22
  import "./chunk-RO726HJG.js";
23
23
  export {
24
24
  BNB_PLATFORM_API_URL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bnbagent/studio-cli",
3
- "version": "0.0.11-alpha.2",
3
+ "version": "0.0.11-alpha.4",
4
4
  "description": "Skills-first toolkit and bag CLI for BNB Chain seller agents: ERC-8004 identity, ERC-8183 escrowed commerce, and x402 payments.",
5
5
  "keywords": [
6
6
  "bnb-chain",
@@ -54,7 +54,7 @@
54
54
  "tar": "^7.4.0",
55
55
  "viem": "^2.54.0",
56
56
  "yaml": "^2.9.0",
57
- "@bnbagent/studio-runtime": "0.0.11-alpha.2"
57
+ "@bnbagent/studio-runtime": "0.0.11-alpha.4"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@a2a-js/sdk": "^0.3.14",
@@ -53,7 +53,7 @@ Treat ERC-8183 amounts as decimal strings at CLI/config boundaries and `bigint`
53
53
 
54
54
  ## CLI groups at a glance
55
55
 
56
- `init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `agents`, `config`, `env`, `dev`, `doctor`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws] [--backend aws\|azure]` is the primary deploy command; `--backend` is valid only for provider `bnb` and confirms the recipe-derived managed backend. `prepare`, `verify`, `status`, `info`, `destroy`, `logs`, and `fix-gitignore` remain lifecycle subcommands (`deploy agent` is a deprecated compatibility alias). Provider deploy/status/logs/destroy and deploy-time credential validation are delegated to pinned `@bnbagent/deploy-cli@0.5.10`.
56
+ `init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `agents`, `config`, `env`, `dev`, `doctor`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws] [--backend aws\|azure]` is the primary deploy command; `--backend` is valid only for provider `bnb` and confirms the recipe-derived managed backend. `prepare`, `verify`, `status`, `info`, `destroy`, `logs`, and `fix-gitignore` remain lifecycle subcommands (`deploy agent` is a deprecated compatibility alias). Provider deploy/status/logs/destroy and deploy-time credential validation are delegated to pinned `@bnbagent/deploy-cli@0.5.11`.
57
57
 
58
58
  ## Tool surface
59
59
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bnbagent-studio-selling-via-b402
3
- description: When the user wants a bnbagent-studio agent to sell paid or FREE HTTP requests through the B402-backed x402 rail. Owns the explicit pricing choice and, for PAID mode, per-agent merchant onboarding, RSA key preparation, egress-IP allowlisting, sandbox/production separation, B402 environment setup, seller status checks, and activation by redeploy (managed platform or self-hosted AgentCore).
3
+ description: When the user wants a bnbagent-studio agent to sell paid or FREE HTTP requests through the B402-backed x402 rail. Owns the explicit pricing choice and, for PAID mode, per-agent merchant onboarding, RSA key preparation, egress-IP allowlisting, sandbox/production separation, B402 environment setup, seller status checks, and activation by redeploy (managed platform, self-hosted AgentCore, or self-hosted Azure Foundry).
4
4
  ---
5
5
 
6
6
  > **Reference file** of the `bnbagent-studio` router skill, installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
@@ -30,7 +30,7 @@ bag config set payments.x402_seller.price_usd 0
30
30
 
31
31
  `"0"` means anonymous FREE passthrough. The runtime returns work directly and does not issue a 402 challenge, call B402 `/supported`/verify/settle, transfer U, or write an `x402_sell` settlement audit. B402 credentials are ignored and not synchronized. Run `bag x402 sell status`, `bag doctor`, and `bag deploy prepare`; all must label the route FREE.
32
32
 
33
- This is unrestricted public access. Confirm that intent before continuing. Managed platform still publishes the route through its gateway; self-hosted AgentCore still needs an envelope-v1 front; Azure Foundry remains unsupported. If FREE is the selected product, skip the merchant/RSA/IP sections below.
33
+ This is unrestricted public access. Confirm that intent before continuing. Managed platform still publishes the route through its gateway; self-hosted AgentCore and Azure Foundry deploys still need an envelope-v1 front. If FREE is the selected product, skip the merchant/RSA/IP sections below.
34
34
 
35
35
  ## Generate the agent's RSA material
36
36
 
@@ -64,7 +64,7 @@ The current B402 request-signing contract explicitly requires a 1024-bit RSA key
64
64
 
65
65
  B402 allowlists the merchant's **outbound** (egress) IPs, the addresses the agent's facilitator calls come FROM. Submit every part that applies to your deployment target:
66
66
 
67
- 1. **Platform Relay egress IPs (managed-platform deploys)**: the addresses that the platform B402 Relay uses to reach the facilitator. The managed deployment worker points only the AgentCore runtime copy of `B402_BASE_URL` at this Relay. Refresh and read the interactive session without printing its bearer:
67
+ 1. **Platform Relay egress IPs (managed-platform deploys)**: the addresses that the platform B402 Relay uses to reach the facilitator. The managed deployment worker points the runtime copy of `B402_BASE_URL` at this Relay on both managed backends (AgentCore and Azure Foundry). Refresh and read the interactive session without printing its bearer:
68
68
 
69
69
  ```bash
70
70
  bag platform whoami >/dev/null
@@ -89,6 +89,8 @@ B402 allowlists the merchant's **outbound** (egress) IPs, the addresses the agen
89
89
 
90
90
  As an alternative, use AWS-supported AgentCore VPC mode with a private subnet, NAT Gateway, and Elastic IP. Submit the Elastic IP and keep `B402_BASE_URL` pointed at the facilitator. Studio does not deploy or manage that AWS network.
91
91
 
92
+ 4. **Self-hosted Azure Foundry egress (self-deploys)**: Foundry hosted-agent containers have floating egress just like AgentCore, so the agent must NOT call the facilitator directly. Run the envelope gateway on a Container Apps workload-profiles environment whose subnet has a NAT Gateway with a Standard static public IP, co-host a restricted B402 forwarder there (the AWS guide's Relay example works verbatim — the NAT Gateway replaces its fixed-IP host requirement), point the runtime `B402_BASE_URL` at that forwarder, and submit the NAT Gateway IP. The environment type and VNet cannot be changed after creation; see the [Azure external-gateway reference](bnbagent-studio-use-azure-foundry.md#x402-external-gateway). Studio does not deploy or manage that Azure network.
93
+
92
94
  Do not add the public inbound gateway IP, a transient build-runner IP, or guessed addresses. If the whitelist endpoint is unreachable, stop onboarding and confirm the platform environment with the operator.
93
95
 
94
96
  ## Submit the B402 merchant application
@@ -101,7 +103,7 @@ Apply through the [B402 developer account application](https://developers.binanc
101
103
  | Email | Primary contact email address |
102
104
  | Wallet address | The agent wallet for that environment |
103
105
  | Public key | The contents of `.studio/b402/<environment>/public.der.b64` |
104
- | IP allowlist | For managed deploys, every platform Relay IP from `/v1/b402/whitelist-ips`; for self-hosted deploys, the user's Relay or NAT Elastic IP; add the local public IP when `bag dev` must reach B402 directly |
106
+ | IP allowlist | For managed deploys (both backends), every platform Relay IP from `/v1/b402/whitelist-ips`; for self-hosted AgentCore, the user's Relay or VPC NAT Elastic IP; for self-hosted Azure Foundry, the ACA NAT Gateway static IP; add the local public IP when `bag dev` must reach B402 directly |
105
107
  | Webhook callback URL | Supply only when the integration uses callbacks |
106
108
 
107
109
  Keep the two environments isolated:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bnbagent-studio-use-aws-agentcore
3
- description: When the user wants to deploy or operate a bnbagent-studio project on AWS Bedrock AgentCore - deploy with `bag deploy --provider aws` (all cloud lifecycle mutations are delegated to pinned `@bnbagent/deploy-cli@0.5.10`), inspect with `bag deploy status` / `logs --provider aws` / `verify --provider aws`, and tear down with `bag deploy destroy --provider aws --execute [--purge]`. Also covers AWS credential prerequisites, the optional read-only quota probe, and the runtime-secret channel.
3
+ description: When the user wants to deploy or operate a bnbagent-studio project on AWS Bedrock AgentCore - deploy with `bag deploy --provider aws` (all cloud lifecycle mutations are delegated to pinned `@bnbagent/deploy-cli@0.5.11`), inspect with `bag deploy status` / `logs --provider aws` / `verify --provider aws`, and tear down with `bag deploy destroy --provider aws --execute [--purge]`. Also covers AWS credential prerequisites, the optional read-only quota probe, and the runtime-secret channel.
4
4
  ---
5
5
 
6
6
  > **Reference file** of the `bnbagent-studio` router skill - installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
@@ -1,13 +1,15 @@
1
1
  ---
2
2
  name: bnbagent-studio-use-azure-foundry
3
- description: When the user wants to deploy or operate a bnbagent-studio project on Azure AI Foundry Hosted Agents - scaffold with `bag init --runtime azure-foundry`, deploy either to the managed platform with `bag deploy --provider bnb --backend azure` or directly with `bag deploy --provider azure`; all cloud lifecycle execution is delegated to pinned `@bnbagent/deploy-cli@0.5.10`. Native MCP is not supported on Azure; use AgentCore for MCP.
3
+ description: When the user wants to deploy or operate a bnbagent-studio project on Azure AI Foundry Hosted Agents - scaffold with `bag init --runtime azure-foundry`, deploy either to the managed platform with `bag deploy --provider bnb --backend azure` or directly with `bag deploy --provider azure`; all cloud lifecycle execution is delegated to pinned `@bnbagent/deploy-cli@0.5.11`. Native MCP is not supported on Azure; use AgentCore for MCP.
4
4
  ---
5
5
 
6
6
  > **Reference file** of the `bnbagent-studio` router skill - installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
7
7
 
8
8
  # bnbagent-studio-use-azure-foundry
9
9
 
10
- > **Preview - not advertised in this release.** Azure Foundry support is fully wired but hidden from the `--runtime` menu and the deploy provider menu; these steps still work if you select `azure-foundry` / `--provider azure` explicitly. The most recent end-to-end live verification predates the TypeScript rewrite - treat your first deploy as a verification run.
10
+ Azure Foundry is listed in `bag init --help` and the deploy provider menu. The
11
+ Foundry data plane, Entra scope, unified entrypoint, deployment flow, and x402
12
+ envelope tunnel were verified end to end in 2026-08.
11
13
 
12
14
  Procedure for deploying and operating the seller Agent on **Azure AI Foundry Hosted Agents** (`[stack].runtime = "azure-foundry"`). ALL cloud execution is **delegated to the pinned `@bnbagent/deploy-cli`** (run via `bunx --bun`; override with `BNBAGENT_DEPLOY_COMMAND`), whose Azure provider is **SDK/REST-only**. Studio never shells out to (or requires) the `az` / `azd` CLIs. Direct user-owned deployment uses browser/local credentials and may self-heal RBAC; the managed platform uses environment credentials in `ambient-only` mode and only probes RBAC.
13
15
 
@@ -21,15 +23,15 @@ Procedure for deploying and operating the seller Agent on **Azure AI Foundry Hos
21
23
 
22
24
  > **Deploy model: CONTAINER-ONLY.** The deploy-cli Azure provider rejects Node zip artifacts, so every azure-foundry deploy builds the scaffolded `app/agent/Dockerfile` **locally with Docker** (linux/amd64) and pushes it to the auto-provisioned Azure Container Registry; Foundry Agent Service pulls and runs the image. A running Docker daemon is required.
23
25
 
24
- > **Protocol: A2A projects only for now.** The deployed Node host speaks both Foundry container contracts on `:8088`: `GET /readiness`, pass-through `POST /invocations` for user-owned Azure, and OpenAI-compatible `POST /responses` for managed incoming A2A. Neither endpoint is native MCP streamable HTTP; `bag init` and provider selection reject azure-foundry + MCP instead of deploying a container that can never become ready.
26
+ > **Protocol:** A2A, X402-only, and A2A+X402 projects are supported. The deployed Node host speaks both Foundry container contracts on `:8088`: `GET /readiness`, pass-through `POST /invocations` for user-owned Azure, and OpenAI-compatible `POST /responses` for managed incoming A2A. Neither endpoint is native MCP streamable HTTP; `bag init` and provider selection reject azure-foundry + MCP instead of deploying a container that can never become ready.
25
27
 
26
- > **Auth is a browser login.** The first delegated run opens a browser to sign in to the right tenant/subscription - there is no `az login` / `azd auth login` step and no CLI to install.
28
+ > **Auth is explicit.** User-owned Azure needs a saved delegated browser sign-in, or an ambient Azure credential chain in CI. Deployment itself never shells out to `az` or `azd`, and a non-interactive deploy cannot open a browser.
27
29
 
28
30
  ## Prerequisites
29
31
 
30
32
  1. **Bun 1.3+ (`bunx`) on PATH** - the pinned `@bnbagent/deploy-cli` runs through it.
31
33
  2. **Docker running** - the image is built locally (linux/amd64) before push.
32
- 3. **An Azure subscription** the operator may provision in (Foundry account/project, container registry, hosted agent); sign-in happens via the browser at deploy time.
34
+ 3. **An Azure subscription** the operator may provision in (Foundry account/project, container registry, hosted agent). Before a local self-deploy, run `bunx --bun @bnbagent/deploy-cli@0.5.11 login --provider azure`; use OIDC/service-principal credentials in CI.
33
35
 
34
36
  ## ⚠️ Foundry gotchas (read before deploying)
35
37
 
@@ -47,7 +49,7 @@ Procedure for deploying and operating the seller Agent on **Azure AI Foundry Hos
47
49
 
48
50
  > The encrypted keystore (`.studio/wallets/`) stays at the workspace root and rides only that secret channel - never baked into the image.
49
51
 
50
- Provider-native overrides go in the optional `studio.toml [deploy.foundry]` table (verbatim deploy-spec keys; deploy-cli 0.5.10 consumes `account`, `cpu`, `location`, `memory`, `project`, `projectEndpoint`, `protocol`, `registry` and warns about anything else). The `[azure]` block's `account_name` / `project_name` / `project_endpoint` / `location` win over conflicting `[deploy.foundry]` keys.
52
+ Provider-native overrides go in the optional `studio.toml [deploy.foundry]` table (verbatim deploy-spec keys; deploy-cli 0.5.11 consumes `account`, `cpu`, `location`, `memory`, `project`, `projectEndpoint`, `protocol`, `registry` and warns about anything else). The `[azure]` block's `account_name` / `project_name` / `project_endpoint` / `location` win over conflicting `[deploy.foundry]` keys.
51
53
 
52
54
  ## Typical workflow
53
55
 
@@ -67,20 +69,25 @@ bag init myagent --runtime azure-foundry
67
69
 
68
70
  ```bash
69
71
  bag deploy prepare --runtime azure-foundry # local readiness gate (region/subdomain/Dockerfile)
70
- bag deploy --provider azure # delegated: login → onboard → build+push → CustomKeys → deploy [--smoke]
72
+ bag deploy --provider azure # delegated: onboard → build+push → CustomKeys → deploy [--smoke]
71
73
  ```
72
74
 
73
75
  `bag deploy --provider azure` runs an HTTP contract smoke by default (pass `--skip-smoke` to omit it) and captures the Foundry endpoint into `app/agent/studio.toml [azure].agent_endpoint`. If Foundry creates the resource but that post-create check fails, Studio still records the discovered endpoint so `status`, `logs`, and `destroy` can manage the resource; the deploy command continues to return non-zero.
74
76
 
77
+ For a first deploy in non-interactive automation, pass `--yes`. Studio treats
78
+ that as confirmation of the full deployment plan and delegates explicit
79
+ Foundry project onboarding. Configure `[azure]` account/subscription selection
80
+ when several candidates are accessible so no prompt or guess is required.
81
+
75
82
  ### C. Validate / operate
76
83
 
77
84
  ```bash
78
85
  bag deploy status # all recorded providers + live state
79
- bag deploy info --with-curl # endpoint + Entra token shortcut + request body
86
+ bag deploy info --provider azure --with-curl # endpoint + Entra token shortcut + request body
80
87
  bag deploy logs --provider azure --limit 50 # delegated Hosted Agent logs
81
88
  ```
82
89
 
83
- `bag deploy info` reads the recorded Azure endpoint without recreating the
90
+ `bag deploy info --provider azure` reads the recorded Azure endpoint without recreating the
84
91
  temporary deploy spec. `--with-curl` adds an optional `az account
85
92
  get-access-token --resource https://ai.azure.com` shortcut and a complete
86
93
  Invocations request. This does not make the Azure CLI a deploy prerequisite;
@@ -89,6 +96,31 @@ with `DefaultAzureCredential` or `ClientSecretCredential`.
89
96
 
90
97
  The built-in smoke proves the container contract, not the seller signature. For a release E2E, invoke with a complete `negotiate` envelope and require `response.accepted=true`, a non-empty `negotiation_hash`, and `provider_sig`. The Invocations body is `{"input":"<serialized skill JSON>"}`.
91
98
 
99
+ ### X402 external gateway
100
+
101
+ Foundry does not expose a raw anonymous `/x402` URL. An operator-run HTTPS
102
+ gateway authenticates to the recorded Invocations endpoint with an Entra token
103
+ for `https://ai.azure.com/.default`, wraps the incoming HTTP request as
104
+ `http-envelope-v1`, and unwraps the returned inner status, headers, and base64
105
+ body:
106
+
107
+ ```json
108
+ {
109
+ "v": 1,
110
+ "method": "POST",
111
+ "path": "/x402",
112
+ "query": {},
113
+ "headers": { "content-type": "application/json" },
114
+ "body": "<base64-encoded request body>"
115
+ }
116
+ ```
117
+
118
+ `bag deploy info --provider azure --json` reports the deployed face snapshot,
119
+ seller state, carrier contract, and a no-work route probe. A PAID seller also
120
+ needs fixed facilitator egress; use an Azure Container Apps workload-profiles
121
+ environment with VNet integration and a NAT Gateway static IP, then allowlist
122
+ that IP with B402. Studio does not deploy or manage the gateway or network.
123
+
92
124
  ### D. Tear down
93
125
 
94
126
  ```bash
@@ -9,7 +9,7 @@ description: Use when deploying or operating a bnbagent-studio seller on the BNB
9
9
 
10
10
  Treat this provider as a temporary testnet sandbox. Require a throwaway wallet, keep `bsc-testnet`, and explain that the runtime signing material is transmitted to the operator's managed secret store for the trial. Never use a mainnet key. Exception: `wallet.kind='altana'` ships only the bounded, budget-limited, revocable session - the throwaway-wallet advice does not apply; tighten the session instead (`bag wallet session grant --force --budget-u <small> --expiry-days <short>`) and never run `bag wallet new` on an altana project (it breaks the session's `[wallet].address` anchor).
11
11
 
12
- All auth and cloud lifecycle work must cross the pinned `@bnbagent/deploy-cli@0.5.10` boundary. Do not call a cloud CLI or platform REST routes directly. The managed backend is recipe-derived: `agentcore` uses AWS; `azure-foundry` uses Azure. For headless managed Azure, confirm with `bag deploy --provider bnb --backend azure --yes`; never treat `--backend` as a cross-cloud recipe converter.
12
+ All auth and cloud lifecycle work must cross the pinned `@bnbagent/deploy-cli@0.5.11` boundary. Do not call a cloud CLI or platform REST routes directly. The managed backend is recipe-derived: `agentcore` uses AWS; `azure-foundry` uses Azure. For headless managed Azure, confirm with `bag deploy --provider bnb --backend azure --yes`; never treat `--backend` as a cross-cloud recipe converter.
13
13
 
14
14
  ## Select and authenticate
15
15