@bnbagent/studio-cli 0.0.6-alpha.6 → 0.0.6-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bag.js CHANGED
@@ -4284,6 +4284,7 @@ import {
4284
4284
  import { getNetwork as getNetwork4 } from "@bnbagent/studio-runtime/networks";
4285
4285
  import * as pieverseRt from "@bnbagent/studio-runtime/pieverse";
4286
4286
  import {
4287
+ PieverseKeyNotFoundError,
4287
4288
  PieversePolicy,
4288
4289
  RetryPolicy,
4289
4290
  allocateWithSettleRetry,
@@ -4417,7 +4418,10 @@ function registerLlm(program) {
4417
4418
  ).option(
4418
4419
  "--network <name>",
4419
4420
  "Override [llm.pieverse].network (e.g. bsc-mainnet, bsc-testnet)."
4420
- ).option("--name <name>", "Key name (default: bnbagent-studio-default).").action(
4421
+ ).option("--name <name>", "Key name (default: bnbagent-studio-default).").option(
4422
+ "--replace",
4423
+ "Ignore any existing key and mint a fresh one, overwriting PIEVERSE_LLM_API_KEY in .studio/.env.local. Use when the key you inherited (e.g. from a shared env file) no longer exists in Pieverse. The old key is left untouched on the Pieverse side."
4424
+ ).action(
4421
4425
  act(
4422
4426
  (opts) => cmdActivate(opts)
4423
4427
  )
@@ -4626,9 +4630,20 @@ async function cmdActivate(opts) {
4626
4630
  return 2;
4627
4631
  }
4628
4632
  const pvCfg = loadPieverseConfig(cfg);
4629
- let existingHash = pvCfg.key_hash ? String(pvCfg.key_hash) : null;
4630
- const envFileValue = getEnvVar(envLocalPath9(root), PIEVERSE_ENV_KEY);
4631
- const existingEnv = envFileValue || process.env[PIEVERSE_ENV_KEY];
4633
+ const replace = opts.replace === true;
4634
+ let existingHash = !replace && pvCfg.key_hash ? String(pvCfg.key_hash) : null;
4635
+ const envFileValue = replace ? null : getEnvVar(envLocalPath9(root), PIEVERSE_ENV_KEY);
4636
+ const existingEnv = replace ? null : envFileValue || process.env[PIEVERSE_ENV_KEY];
4637
+ if (replace) {
4638
+ printOut(
4639
+ `\u2192 --replace: ignoring any existing ${PIEVERSE_ENV_KEY} and minting a fresh key\u2026`
4640
+ );
4641
+ if (process.env[PIEVERSE_ENV_KEY]) {
4642
+ printErr(
4643
+ `warning: ${PIEVERSE_ENV_KEY} is also exported in this shell. The new key goes to .studio/.env.local, which \`bag\` prefers, but \`unset ${PIEVERSE_ENV_KEY}\` to keep the two from diverging.`
4644
+ );
4645
+ }
4646
+ }
4632
4647
  if (!existingHash && existingEnv) {
4633
4648
  const derivedHash = pieverseKeyHash(existingEnv);
4634
4649
  printOut(
@@ -4637,15 +4652,24 @@ async function cmdActivate(opts) {
4637
4652
  try {
4638
4653
  await withSiweRetry(wallet, (token) => inspectKey(token, derivedHash));
4639
4654
  } catch (exc) {
4655
+ if (!(exc instanceof PieverseKeyNotFoundError)) {
4656
+ printErr(
4657
+ `error: could not verify the existing ${PIEVERSE_ENV_KEY} against Pieverse: ${errMessage(
4658
+ exc
4659
+ )}
4660
+ The existing key was kept and no new key was created. This looks like a transport or login failure rather than a missing key \u2014 retry \`bag llm activate\` once Pieverse is reachable.`
4661
+ );
4662
+ return 2;
4663
+ }
4640
4664
  const sources = [
4641
4665
  envFileValue ? `.studio/.env.local (a ${PIEVERSE_ENV_KEY}= line)` : "",
4642
4666
  process.env[PIEVERSE_ENV_KEY] ? "the shell environment" : ""
4643
4667
  ].filter(Boolean).join(" and ");
4644
4668
  printErr(
4645
- `error: ${PIEVERSE_ENV_KEY} already exists but its derived key_hash could not be verified: ${errMessage(
4669
+ `error: ${PIEVERSE_ENV_KEY} already exists but Pieverse does not know its key_hash: ${errMessage(
4646
4670
  exc
4647
4671
  )}
4648
- The existing key was kept and no new key was created. The key comes from ${sources}; \`bag\` reads both, so clear every source \u2014 remove the ${PIEVERSE_ENV_KEY}= line from .studio/.env.local AND \`unset ${PIEVERSE_ENV_KEY}\` in the shell \u2014 then re-run \`bag llm activate\` to mint a fresh key. (\`bag llm rotate\` replaces a key that is still valid in Pieverse; it cannot recover this one.)`
4672
+ The existing key was kept and no new key was created. The key comes from ${sources}. Re-run \`bag llm activate --replace\` to mint a fresh key and overwrite .studio/.env.local (then \`unset ${PIEVERSE_ENV_KEY}\` if you also exported it in this shell). (\`bag llm rotate\` replaces a key that is still valid in Pieverse; it cannot recover this one.)`
4649
4673
  );
4650
4674
  return 2;
4651
4675
  }
@@ -11366,6 +11390,84 @@ async function checkAwsTargetsPopulated(root, target) {
11366
11390
  }
11367
11391
  ];
11368
11392
  }
11393
+ var AGENTCORE_AGENT_QUOTA_CODE = "L-F4575653";
11394
+ function awsJson(stdout) {
11395
+ try {
11396
+ const parsed = JSON.parse(stdout);
11397
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
11398
+ } catch {
11399
+ return null;
11400
+ }
11401
+ }
11402
+ async function checkAgentcoreQuotaHeadroom(root, target) {
11403
+ if (target !== "agentcore") {
11404
+ return [];
11405
+ }
11406
+ const [, region] = readAwsTarget(root);
11407
+ if (!region || !await whichBin("aws")) {
11408
+ return [];
11409
+ }
11410
+ const quotaRes = await runCapture("aws", [
11411
+ "service-quotas",
11412
+ "get-service-quota",
11413
+ "--service-code",
11414
+ "bedrock-agentcore",
11415
+ "--quota-code",
11416
+ AGENTCORE_AGENT_QUOTA_CODE,
11417
+ "--region",
11418
+ region,
11419
+ "--output",
11420
+ "json"
11421
+ ]);
11422
+ if (quotaRes.code !== 0) {
11423
+ return [];
11424
+ }
11425
+ const quota = awsJson(quotaRes.stdout)?.Quota;
11426
+ const limit = typeof quota?.Value === "number" ? quota.Value : null;
11427
+ if (limit === null) {
11428
+ return [];
11429
+ }
11430
+ const listRes = await runCapture("aws", [
11431
+ "bedrock-agentcore-control",
11432
+ "list-agent-runtimes",
11433
+ "--region",
11434
+ region,
11435
+ "--output",
11436
+ "json"
11437
+ ]);
11438
+ if (listRes.code !== 0) {
11439
+ return [];
11440
+ }
11441
+ const runtimes = awsJson(listRes.stdout)?.agentRuntimes;
11442
+ if (!Array.isArray(runtimes)) {
11443
+ return [];
11444
+ }
11445
+ const used = runtimes.length;
11446
+ const details = {
11447
+ used,
11448
+ limit,
11449
+ region,
11450
+ quota_code: AGENTCORE_AGENT_QUOTA_CODE
11451
+ };
11452
+ if (used < limit) {
11453
+ return [
11454
+ {
11455
+ level: Level.INFO,
11456
+ name: "agentcore_quota_headroom",
11457
+ message: `AgentCore runtimes: ${used}/${limit} used in ${region}.`,
11458
+ details
11459
+ }
11460
+ ];
11461
+ }
11462
+ return [
11463
+ {
11464
+ level: Level.CRITICAL,
11465
+ name: "agentcore_quota_headroom",
11466
+ message: `AgentCore runtimes: ${used}/${limit} used in ${region} \u2014 no slots left, so the deploy would fail with \`maxAgents limit exceeded\` AFTER pushing the image and creating the secret, M2M client, and execution role (all orphans). Delete a runtime you no longer need (\`bag deploy destroy\` in its workspace) or request a quota increase for ${AGENTCORE_AGENT_QUOTA_CODE} ("Total Agents per Account") in the Service Quotas console.`,
11467
+ details
11468
+ }
11469
+ ];
11470
+ }
11369
11471
  var allChecks = [
11370
11472
  checkAgentcoreJsonPresent,
11371
11473
  checkAgentcoreAuthorizerKeyLegacy,
@@ -11390,7 +11492,8 @@ var allChecks = [
11390
11492
  checkCommerceReady,
11391
11493
  checkDeployCliRunnable,
11392
11494
  checkRuntimeSecretsInjected,
11393
- checkAwsTargetsPopulated
11495
+ checkAwsTargetsPopulated,
11496
+ checkAgentcoreQuotaHeadroom
11394
11497
  ];
11395
11498
 
11396
11499
  // src/cli/_deploy/secrets.ts
@@ -11674,6 +11777,12 @@ function cognitoPackageJson() {
11674
11777
  function readme() {
11675
11778
  return `# Cognito M2M client for the AgentCore A2A inbound OAuth2 authorizer
11676
11779
 
11780
+ > **DEPRECATED.** \`bag deploy --provider aws\` now provisions its own user pool
11781
+ > and M2M client, and overwrites the values wired from this stack. Deploying
11782
+ > this app leaves a second, unused pool behind that you have to clean up
11783
+ > yourself. Keep it only if you need to own the pool out of band; otherwise run
11784
+ > \`bag deploy --provider aws\` and hand buyers the credentials it prints.
11785
+
11677
11786
  AgentCore A2A endpoints REQUIRE inbound auth (there is no anonymous mode). This
11678
11787
  seller uses **OAuth2 via Cognito** \u2014 NOT SigV4, because external buyers cannot
11679
11788
  obtain your AWS IAM credentials. Cognito has no public machine-to-machine
@@ -11714,12 +11823,11 @@ This reads the local \`cdk-outputs.json\` and patches:
11714
11823
  | \`Scope\` | \`agentcore.json\` \u2192 \`envVars[] OAUTH_SCOPE\` (+ \`.studio/.env.local\` for local dev) |
11715
11824
 
11716
11825
  \`OAUTH_TOKEN_URL\` / \`OAUTH_SCOPE\` are read by the emitted agent card from
11717
- the environment, so the published A2A agent card advertises the OAuth2 security
11718
- scheme buyers must use. They go into \`agentcore.json\` \`envVars[]\` (non-secret)
11719
- because \`.studio/.env.local\` lives outside the deploy CodeZip \u2014 so envVars[]
11720
- is the channel that actually reaches the runtime; the \`.env.local\` copy only
11721
- serves local \`bag dev\`. (\`bag deploy provision-cognito --wire <file>\` accepts a
11722
- custom outputs path.)
11826
+ the environment, so the agent card advertises the OAuth2 security scheme buyers
11827
+ must use. **A \`bag deploy\` run replaces both values (and the authorizer) with
11828
+ the pool it provisions itself**, so this wiring only takes effect for local
11829
+ \`bag dev\`. (\`bag deploy provision-cognito --wire <file>\` accepts a custom
11830
+ outputs path.)
11723
11831
 
11724
11832
  ## Issue buyer credentials
11725
11833
 
@@ -11770,7 +11878,8 @@ function wireSummary(r) {
11770
11878
  allowedClients = [${r.clientId}]
11771
11879
  - ${path26.basename(r.agentcoreJson)}: envVars[] OAUTH_TOKEN_URL + OAUTH_SCOPE (reach the runtime so the agent card advertises oauth2)
11772
11880
  - ${r.envLocal}: same pair, for local \`bag dev\`
11773
- \u2192 \`bag deploy prepare\` W9 will now pass; deploy when ready.`;
11881
+ \u2192 NOTE: a \`bag deploy\` run provisions its own pool and overwrites these
11882
+ values with the ones it issued. This wiring only feeds local \`bag dev\`.`;
11774
11883
  }
11775
11884
  function flattenCdkOutputs(raw) {
11776
11885
  const flat = {};
@@ -11798,6 +11907,49 @@ function upsertDescriptorEnvvars(cfg, values) {
11798
11907
  }
11799
11908
  }
11800
11909
  }
11910
+ function syncDescriptorOauthFacts(workspaceRoot, facts) {
11911
+ const acj = path26.join(workspaceRoot, "agentcore", "agentcore.json");
11912
+ let cfg;
11913
+ try {
11914
+ cfg = JSON.parse(fs27.readFileSync(acj, "utf-8"));
11915
+ } catch {
11916
+ return;
11917
+ }
11918
+ const envValues = {};
11919
+ if (facts.tokenUrl) {
11920
+ envValues.OAUTH_TOKEN_URL = facts.tokenUrl;
11921
+ }
11922
+ if (facts.scope) {
11923
+ envValues.OAUTH_SCOPE = facts.scope;
11924
+ }
11925
+ const authorizer = facts.discoveryUrl && facts.clientId ? {
11926
+ customJwtAuthorizer: {
11927
+ discoveryUrl: facts.discoveryUrl,
11928
+ allowedClients: [facts.clientId]
11929
+ }
11930
+ } : null;
11931
+ if (Object.keys(envValues).length === 0 && authorizer === null) {
11932
+ return;
11933
+ }
11934
+ if (Object.keys(envValues).length > 0) {
11935
+ upsertDescriptorEnvvars(cfg, envValues);
11936
+ }
11937
+ if (authorizer !== null && Array.isArray(cfg.runtimes)) {
11938
+ for (const rt of cfg.runtimes) {
11939
+ if (rt !== null && typeof rt === "object") {
11940
+ const r = rt;
11941
+ r.authorizerType = "CUSTOM_JWT";
11942
+ r.authorizerConfiguration = authorizer;
11943
+ }
11944
+ }
11945
+ }
11946
+ try {
11947
+ fs27.writeFileSync(acj, `${JSON.stringify(cfg, null, 2)}
11948
+ `, "utf-8");
11949
+ } catch {
11950
+ return;
11951
+ }
11952
+ }
11801
11953
  function upsertEnv(envLocal, values) {
11802
11954
  let lines = [];
11803
11955
  try {
@@ -12725,59 +12877,6 @@ async function checkInstalledRecipesUnchanged(root, _target) {
12725
12877
  }
12726
12878
  return [];
12727
12879
  }
12728
- function agentcoreAuthorizer(root) {
12729
- const descriptor = path32.join(root, "agentcore", "agentcore.json");
12730
- let data;
12731
- try {
12732
- data = JSON.parse(fs33.readFileSync(descriptor, "utf-8"));
12733
- } catch {
12734
- return null;
12735
- }
12736
- if (data === null || typeof data !== "object" || Array.isArray(data)) {
12737
- return null;
12738
- }
12739
- const runtimes = data.runtimes;
12740
- if (!Array.isArray(runtimes)) {
12741
- return null;
12742
- }
12743
- for (const rt of runtimes) {
12744
- if (rt === null || typeof rt !== "object") {
12745
- continue;
12746
- }
12747
- const auth = rt.authorizerConfiguration;
12748
- if (auth !== null && typeof auth === "object" && !Array.isArray(auth)) {
12749
- const jwt = auth.customJwtAuthorizer ?? auth.customJWTAuthorizer;
12750
- if (jwt !== null && typeof jwt === "object" && !Array.isArray(jwt)) {
12751
- return jwt;
12752
- }
12753
- }
12754
- }
12755
- return null;
12756
- }
12757
- async function checkCognitoAuthorizerConfigured(root, target) {
12758
- if (target !== "agentcore") {
12759
- return [];
12760
- }
12761
- const jwt = agentcoreAuthorizer(root);
12762
- const discovery = jwt?.discoveryUrl;
12763
- const clients = jwt?.allowedClients;
12764
- const configured = Boolean(discovery) && Array.isArray(clients) && clients.length > 0;
12765
- if (configured) {
12766
- return [];
12767
- }
12768
- return [
12769
- {
12770
- level: Level.WARNING,
12771
- name: "cognito_authorizer_configured",
12772
- message: "no Cognito authorizer configured in agentcore.json (authorizerConfiguration.customJwtAuthorizer.discoveryUrl/allowedClients) \u2192 the A2A endpoint will require SigV4/AWS creds; external buyers can't reach it. Run `bag deploy provision-cognito`, then paste the CDK outputs into the authorizer.",
12773
- fixCmd: "bag deploy provision-cognito",
12774
- details: {
12775
- discovery_url_set: Boolean(discovery),
12776
- allowed_clients: Array.isArray(clients) ? clients : []
12777
- }
12778
- }
12779
- ];
12780
- }
12781
12880
  async function checkStorageLocalNotDeployable(root, _target) {
12782
12881
  const storage = loadAgentToml(root).storage;
12783
12882
  if (storage === null || typeof storage !== "object" || Array.isArray(storage) || String(storage.kind ?? "").toLowerCase() !== "local") {
@@ -12802,7 +12901,6 @@ var allChecks4 = [
12802
12901
  checkRpcUrlSetForRuntime,
12803
12902
  checkRuntimeRpcReachable,
12804
12903
  checkInstalledRecipesUnchanged,
12805
- checkCognitoAuthorizerConfigured,
12806
12904
  checkStorageLocalNotDeployable
12807
12905
  ];
12808
12906
 
@@ -13256,6 +13354,7 @@ function baseResult(endpoint, runtime, overrides) {
13256
13354
  studioTomlUpdated: false,
13257
13355
  runtime,
13258
13356
  note: null,
13357
+ warning: null,
13259
13358
  error: null,
13260
13359
  exitCode: 0,
13261
13360
  ...overrides
@@ -13311,6 +13410,7 @@ async function runVerify(opts) {
13311
13410
  let action = "none";
13312
13411
  let registerError = null;
13313
13412
  let postWriteError = null;
13413
+ let postWriteWarning = null;
13314
13414
  let registerNote = null;
13315
13415
  let identityPendingReason = null;
13316
13416
  let registerExitCode = 0;
@@ -13343,9 +13443,8 @@ async function runVerify(opts) {
13343
13443
  const cause = e.cause !== null && typeof e.cause === "object" ? e.cause : null;
13344
13444
  const relayTxHash = typeof cause?.txHash === "string" ? cause.txHash : e.txHash;
13345
13445
  action = "register_partial";
13346
- postWriteError = relayTxHash ? `ERC-8004 register created agent_id=${e.agentId}, but setAgentURI relay tx_hash=${relayTxHash} was not observed by the chain RPC. Run \`bag erc8004 clear-pending\` to reconcile, then retry \`bag erc8004 update-endpoint\`.` : `ERC-8004 register created agent_id=${e.agentId}, but the setAgentURI relay submission was not observed by the chain RPC. Run \`bag erc8004 clear-pending\` to reconcile, then retry \`bag erc8004 update-endpoint\`.`;
13446
+ postWriteWarning = relayTxHash ? `ERC-8004 register created agent_id=${e.agentId}, but setAgentURI relay tx_hash=${relayTxHash} was not observed by the chain RPC. Run \`bag erc8004 clear-pending\` to reconcile, then retry \`bag erc8004 update-endpoint\`.` : `ERC-8004 register created agent_id=${e.agentId}, but the setAgentURI relay submission was not observed by the chain RPC. Run \`bag erc8004 clear-pending\` to reconcile, then retry \`bag erc8004 update-endpoint\`.`;
13347
13447
  identityPendingReason = "setAgentURI relay submission unverified; run bag erc8004 clear-pending before retrying";
13348
- registerExitCode = 1;
13349
13448
  } else if (e.txHash) {
13350
13449
  action = "register_pending";
13351
13450
  registerNote = `ERC-8004 register created agent_id=${e.agentId}; setAgentURI tx pending (${e.txHash}). Check the tx before retrying \`bag erc8004 update-endpoint\`.`;
@@ -13439,6 +13538,7 @@ async function runVerify(opts) {
13439
13538
  action,
13440
13539
  studioTomlUpdated,
13441
13540
  note: notes.length > 0 ? notes.join(" | ") : null,
13541
+ warning: postWriteWarning,
13442
13542
  error: postWriteError,
13443
13543
  exitCode: registerExitCode
13444
13544
  });
@@ -14548,8 +14648,8 @@ function registerDeploy(program) {
14548
14648
  p.command("fix-gitignore").description(
14549
14649
  "Ensure the workspace .gitignore excludes .studio/ (the agent's .env.local + keystore)."
14550
14650
  ).option("--project-root <path>", "Override project root.").action(act((opts) => cmdFixGitignore(opts)));
14551
- p.command("provision-cognito").description(
14552
- "Emit an AWS CDK app under agentcore/cognito/ that provisions a Cognito M2M client for the AgentCore A2A inbound OAuth2 authorizer (studio writes IaC + prints steps; you run `cdk deploy`)."
14651
+ p.command("provision-cognito", { hidden: true }).description(
14652
+ "DEPRECATED \u2014 `bag deploy` provisions inbound OAuth by itself. Emit an AWS CDK app under agentcore/cognito/ that provisions a Cognito M2M client for the AgentCore inbound OAuth2 authorizer (studio writes IaC + prints steps; you run `cdk deploy`)."
14553
14653
  ).option("--project-root <path>", "Override project root.").option(
14554
14654
  "--wire [outputsFile]",
14555
14655
  "After you `cdk deploy --outputs-file <file>`, wire those outputs into agentcore.json + .studio/.env.local (default file: agentcore/cognito/cdk-outputs.json). Pure local file I/O \u2014 studio never calls AWS."
@@ -14608,12 +14708,17 @@ function agentDeployDestination(root) {
14608
14708
  }
14609
14709
  function resolveTarget(opts, root, destination = agentDeployDestination(root)) {
14610
14710
  const explicit = opts.runtime ?? opts.target;
14611
- if (explicit) {
14612
- return explicit;
14613
- }
14614
14711
  if (destination === "platform") {
14712
+ if (explicit && explicit !== "platform") {
14713
+ printErr(
14714
+ `warning: --runtime=${explicit} ignored \u2014 studio.toml has [deploy].destination = 'platform', which selects the managed deploy path and its own checks. Use --provider bnb|aws to pick where this agent deploys.`
14715
+ );
14716
+ }
14615
14717
  return "platform";
14616
14718
  }
14719
+ if (explicit) {
14720
+ return explicit;
14721
+ }
14617
14722
  return readStackRuntime(root) ?? "agentcore";
14618
14723
  }
14619
14724
  function validateRuntimeAgainstStack(target, force, root) {
@@ -14820,6 +14925,12 @@ function writeAgentDeployState(workspaceRoot, arn, record = {}, destination = "s
14820
14925
  if (scope) {
14821
14926
  setEnvVar(envLocalPath16(agentRoot2), "OAUTH_SCOPE", scope);
14822
14927
  }
14928
+ syncDescriptorOauthFacts(workspaceRoot, {
14929
+ tokenUrl,
14930
+ scope,
14931
+ discoveryUrl,
14932
+ clientId
14933
+ });
14823
14934
  if (persistedPackaging(agentRoot2) === null) {
14824
14935
  patchTomlKv(
14825
14936
  tomlPath,
@@ -15085,6 +15196,17 @@ async function refreshLocalDeployDependencies(root) {
15085
15196
  return 1;
15086
15197
  }
15087
15198
  }
15199
+ function partialDeployCleanupNote(workspaceRoot) {
15200
+ const project = projectName(workspaceRoot);
15201
+ return [
15202
+ "note: a deploy that fails partway can leave these behind (they are reused by a successful retry, so clean up only if you are giving up):",
15203
+ ` - ECR image bnbagent/${project}`,
15204
+ ` - Secrets Manager bnbagent/${project}/runtime`,
15205
+ ` - Cognito M2M client bnbagent-${project}`,
15206
+ ` - IAM role bnbagent-${project}-runtime`,
15207
+ " `bag deploy destroy --provider aws --execute` removes the runtime, secret, and M2M client; add --purge for the ECR repository and log groups. The IAM role is deleted with the runtime it was created for."
15208
+ ].join("\n");
15209
+ }
15088
15210
  async function cmdAgent(opts, agentcoreArgs) {
15089
15211
  let rc = applyProjectRoot(opts.projectRoot);
15090
15212
  if (rc !== null) {
@@ -15296,6 +15418,7 @@ async function cmdAgent(opts, agentcoreArgs) {
15296
15418
  printErr(
15297
15419
  "note: the pinned bnbagent-deploy adapter automatically restores an owned runtime secret that is inside its recovery window. If AWS denies that operation, update the deployer policy to include secretsmanager:RestoreSecret."
15298
15420
  );
15421
+ printErr(partialDeployCleanupNote(workspaceRoot));
15299
15422
  return code;
15300
15423
  }
15301
15424
  const record = recordOf2(data);
@@ -15638,6 +15761,7 @@ function verifyToJson(r) {
15638
15761
  studio_toml_updated: r.studioTomlUpdated,
15639
15762
  runtime: r.runtime,
15640
15763
  note: r.note,
15764
+ warning: r.warning,
15641
15765
  error: r.error,
15642
15766
  exit_code: r.exitCode
15643
15767
  };
@@ -15679,6 +15803,9 @@ function renderVerifyHuman(r, deployment, liveStatus) {
15679
15803
  if (r.note) {
15680
15804
  printOut(`note: ${r.note}`);
15681
15805
  }
15806
+ if (r.warning) {
15807
+ printOut(`warning: ${r.warning}`);
15808
+ }
15682
15809
  if (r.error) {
15683
15810
  printOut(`error: ${r.error}`);
15684
15811
  }
@@ -16026,6 +16153,7 @@ async function cmdFixGitignore(opts) {
16026
16153
  printOut(`${marker} ${result.name}: ${result.message}`);
16027
16154
  return 0;
16028
16155
  }
16156
+ var PROVISION_COGNITO_DEPRECATION = "warning: `bag deploy provision-cognito` is deprecated and will be removed.\n `bag deploy` provisions the inbound OAuth user pool and M2M client on its\n own; the pool this CDK app stands up is never used by a deploy, and both\n the stack and its client are billable orphans you have to clean up.\n Run `bag deploy --provider aws` directly instead.";
16029
16157
  async function cmdProvisionCognito(opts) {
16030
16158
  const rc = applyProjectRoot(opts.projectRoot, false);
16031
16159
  if (rc !== null) {
@@ -16033,6 +16161,7 @@ async function cmdProvisionCognito(opts) {
16033
16161
  }
16034
16162
  const root = resolveProjectRoot2(opts.projectRoot);
16035
16163
  const workspaceRoot = deployWorkspaceRoot2(root);
16164
+ printOut(PROVISION_COGNITO_DEPRECATION);
16036
16165
  if (opts.wire !== void 0 && opts.wire !== false) {
16037
16166
  const outputsFile = typeof opts.wire === "string" && opts.wire ? opts.wire : null;
16038
16167
  try {
@@ -20254,7 +20383,7 @@ function buildProgram() {
20254
20383
  return program;
20255
20384
  }
20256
20385
  function cliVersion() {
20257
- return "0.0.6-alpha.6";
20386
+ return "0.0.6-alpha.7";
20258
20387
  }
20259
20388
 
20260
20389
  // src/cli/updateCheck.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bnbagent/studio-cli",
3
- "version": "0.0.6-alpha.6",
3
+ "version": "0.0.6-alpha.7",
4
4
  "description": "The `bag` CLI: scaffold, run, deploy, and monetize a single seller agent on BNB Chain (ERC-8004 identity, ERC-8183 commerce, x402 payments).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -38,7 +38,7 @@
38
38
  "tar": "^7.4.0",
39
39
  "viem": "^2.54.0",
40
40
  "yaml": "^2.9.0",
41
- "@bnbagent/studio-runtime": "0.0.6-alpha.6"
41
+ "@bnbagent/studio-runtime": "0.0.6-alpha.7"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@a2a-js/sdk": "^0.3.14",
@@ -97,7 +97,7 @@ prices retain the paid merchant flow.
97
97
 
98
98
  ## CLI groups at a glance
99
99
 
100
- `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]` is the primary deploy command; `prepare`, `verify`, `status`, `info`, `destroy`, `logs`, `fix-gitignore`, and `provision-cognito` 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.4.14`.
100
+ `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]` is the primary deploy command; `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.4.14`.
101
101
 
102
102
  ## Tool surface
103
103
 
@@ -189,16 +189,16 @@ ERC-8004/8183 **last** with the deployed AgentCore endpoint:
189
189
 
190
190
  ```bash
191
191
  bag deploy prepare # readiness sweep
192
- bag deploy provision-cognito # emits the Cognito CDK app you run (cdk deploy); wires discoveryUrl/clientId
193
192
  bag deploy --provider aws # ship the Agent to AgentCore
194
193
  bag deploy verify --provider aws # delegated status + reconcile ERC-8004 identity
195
194
  ```
196
195
 
197
- Gotcha: to serve external buyers, configure the Cognito authorizer
198
- (`provision-cognito` `cdk deploy --outputs-file` `provision-cognito --wire`);
199
- `bag deploy prepare` warns (W9) if unset but does NOT block. With no authorizer
200
- the runtime is IAM/SigV4 owner-only (never anonymous) external buyers without
201
- AWS creds just can't reach it.
196
+ Gotcha: `bag deploy --provider aws` provisions the Cognito user pool and the
197
+ buyer M2M client itself and prints the token URL, client id, and scope — hand
198
+ those to each buyer (Cognito has no public M2M self-registration; retrieve the
199
+ client secret read-only from the AWS Console). Do not run
200
+ `bag deploy provision-cognito`: it is deprecated and its CDK pool is never used
201
+ by a deploy.
202
202
 
203
203
  Gotcha: `dispute_window` is read from the on-chain policy contract (24h on
204
204
  testnet). Buyers can dispute within that window after submit — the Agent can't
@@ -25,7 +25,7 @@ wallet/LLM/budget ops.
25
25
  directory (Claude Code: `~/.claude/skills/bnbagent-studio/references/`; Cursor:
26
26
  `bnbagent-studio/references/` beside the `.mdc` rules). READ
27
27
  the file when the topic comes up — don't answer from memory:
28
- - `bnbagent-studio-use-aws-agentcore.md` — the delegated AgentCore lifecycle (`bag deploy --provider aws` / `status` / `logs` / `verify` / `destroy`, `provision-cognito`) + AWS prerequisites
28
+ - `bnbagent-studio-use-aws-agentcore.md` — the delegated AgentCore lifecycle (`bag deploy --provider aws` / `status` / `logs` / `verify` / `destroy`) + AWS prerequisites
29
29
  - `bnbagent-studio-use-bnb-trial.md` — GitHub device login, 48h eligibility, staging verification, and the delegated BNB trial lifecycle
30
30
  - `bnbagent-studio-using-twak-wallet.md` — `[wallet].kind = "twak"` create / fund / SIWE-bind / container deploy / limitations
31
31
  - `bnbagent-studio-extending-signing.md` — `PolicyViolation` / `X402PolicyError` diagnosis + extending the EIP-712 allowlist
@@ -136,7 +136,6 @@ Returns a rich table of checks across the `app/agent/` sub-project:
136
136
  | Wallet tBNB balance | 0 tBNB | Faucet: testnet.bnbchain.org/faucet-smart |
137
137
  | Wallet U balance | 0 U | Transfer from holder, or ask for sponsor U |
138
138
  | 8004 registered | Not registered | Normally registered automatically at `bag deploy verify`. Manual: `bag erc8004 register --endpoint <url>` (only if you need an on-chain identity before deploy). WARN-only in `bag doctor` — it doesn't block local dev. |
139
- | Cognito authorizer (W9) | External-buyer readiness: `agentcore.json` carries no `authorizerConfiguration` / protocol metadata has placeholder `OAUTH_*` values | WARN-only (does NOT block) — needed to serve **external** buyers, not to deploy. With no authorizer the runtime is IAM/SigV4 owner-only (never anonymous). To open it to buyers: `bag deploy provision-cognito` → `cdk deploy --outputs-file cdk-outputs.json` → `bag deploy provision-cognito --wire`. |
140
139
 
141
140
  WARN-only items don't block; FAIL items do (exit 1).
142
141
 
@@ -490,10 +490,10 @@ Local dev (from workspace root <name>/):
490
490
  # seller's skills). For MCP, use an MCP client.
491
491
 
492
492
  When ready to deploy:
493
- bag deploy provision-cognito # emit the Cognito CDK app — you run `cdk deploy`,
494
- # then its discoveryUrl/clientId wire into the OAuth2 authorizer
495
493
  bag deploy prepare # readiness sweep
496
494
  bag deploy --provider aws # ship the Agent to AgentCore (selected faces);
495
+ # provisions the Cognito pool + buyer M2M client and
496
+ # prints the token URL / client id / scope;
497
497
  # keystore injected via Secrets Manager (never in the CodeZip)
498
498
  bag deploy verify --provider aws --endpoint <url> # delegated status + reconcile ERC-8004
499
499
 
@@ -529,13 +529,12 @@ Edit (from workspace root):
529
529
  `TWAK_WALLET_PASSWORD` for twak — reconstructed at cold start, never in the
530
530
  package; the testnet-only `--secrets-mode envvars` fallback is refused on
531
531
  mainnet.
532
- - **AgentCore seller endpoints are never anonymous.** With no authorizer the runtime defaults
533
- to IAM/SigV4 (owner-only, NOT open); to serve **external** buyers configure the
534
- Cognito OAuth2 authorizer `bag deploy provision-cognito` emits a CDK app the
535
- user `cdk deploy --outputs-file`s, then `provision-cognito --wire` patches
536
- `agentcore.json` + the card env. `bag deploy prepare` warns (W9) if unset but
537
- does NOT block. Locally, `bag dev` runs without Cognito env, so the card omits
538
- the scheme and is reachable without a token.
532
+ - **AgentCore seller endpoints are never anonymous.** `bag deploy --provider aws`
533
+ provisions the Cognito OAuth2 pool + buyer M2M client itself and prints the
534
+ token URL / client id / scope to hand to buyers (`bag deploy provision-cognito`
535
+ is deprecated — a deploy uses its own pool regardless). Locally, `bag dev` runs
536
+ without Cognito env, so the card omits the scheme and is reachable without a
537
+ token.
539
538
  - **ERC-8183 does NOT require ERC-8004** at the protocol level (commerce contract
540
539
  doesn't check the identity registry). Local two-agent dev can run end-to-end
541
540
  without ever touching 8004. Use 8004 only when you actually want discoverable
@@ -160,12 +160,11 @@ Full Pieverse credit decisions live in `funding-pieverse-llm` (project-scope ski
160
160
 
161
161
  ERC-8183 / ERC-8004 registration is a **deploy-time** concern: the public
162
162
  AgentCore endpoint must exist before you register, so register **last**. The
163
- agent endpoint has no anonymous mode, so you must provision the Cognito OAuth2
164
- authorizer first.
163
+ agent endpoint has no anonymous mode; `bag deploy` provisions the Cognito
164
+ OAuth2 authorizer as part of the deploy.
165
165
 
166
166
  ```bash
167
167
  bag deploy prepare # readiness sweep
168
- bag deploy provision-cognito # emit the Cognito CDK app; run `cdk deploy`, wire discoveryUrl/clientId
169
168
  bag deploy --provider aws # ship the agent to AgentCore (selected protocol)
170
169
  bag deploy verify --provider aws # delegated status + reconcile ERC-8004 identity
171
170
  ```
@@ -178,7 +177,7 @@ asks bnbagent-deploy for live status first, then performs the reconcile.
178
177
 
179
178
  **Hard rules**:
180
179
  - The endpoint must be **reachable** when registered. For A2A, smoke test the normalized card URL directly, for example `curl <agentcore-invocations-url>/.well-known/agent-card.json` (or `curl <already-registered-card-url>` if the endpoint already includes `/.well-known/agent-card.json`). For MCP, connect an MCP client to the deployed `/mcp` URL. Chain doesn't verify reachability, but buyers will see failures.
181
- - To serve external buyers, configure the Cognito authorizer (`provision-cognito` `cdk deploy --outputs-file` `provision-cognito --wire`); `bag deploy prepare` warns (W9) if unset but does NOT block. With no authorizer the runtime is IAM/SigV4 owner-only (never anonymous) buyers without AWS creds just can't reach it.
180
+ - `bag deploy --provider aws` provisions the Cognito user pool + buyer M2M client and prints the token URL, client id, and scope. Hand those to each buyer (the client secret is retrieved read-only from the AWS Console — studio never stores it). `bag deploy provision-cognito` is deprecated; its CDK pool is never used by a deploy.
182
181
  - Price bounds (`min_price`/`max_price`) live with the Agent — it clamps + signs the quote.
183
182
 
184
183
  ## Stage 5 — How a SUBMITTED job happens
@@ -106,7 +106,7 @@ flags); cloud execution is the pinned bnbagent-deploy's job.
106
106
  | `bag deploy status [--provider aws]` | List every recorded deployment and delegated live state (read-only); `--no-probe` is local-only. |
107
107
  | `bag deploy logs [--provider aws] [--follow] [--since 10m]` | Delegate runtime logs to bnbagent-deploy. |
108
108
  | `bag deploy destroy [--provider aws]` | Dry-run teardown plan; `--execute` delegates `destroy --yes`; `--purge` also deletes retained ECR/log resources. |
109
- | `bag deploy provision-cognito [--wire]` | Emit (then wire) the optional Cognito CDK app for operator-managed buyer credentials. |
109
+ | `bag deploy provision-cognito [--wire]` | **Deprecated** (hidden from `--help`). Emits a Cognito CDK app whose pool a deploy never uses. |
110
110
 
111
111
  ## Typical workflows
112
112
 
@@ -129,21 +129,19 @@ plus a container engine.
129
129
  > without asking the user first.
130
130
 
131
131
  > 🔒 **Inbound auth is auto-provisioned.** An AgentCore seller endpoint is
132
- > **never anonymous**. When `agentcore.json` carries no authorizer of its own,
133
- > the delegated deploy auto-provisions a Cognito inbound OAuth (account pool +
134
- > per-agent M2M client) so the runtime is token-gated. To mint buyer
135
- > credentials the operator manages directly, use the optional CDK path:
132
+ > **never anonymous**. The delegated deploy provisions a Cognito inbound OAuth
133
+ > (account pool + per-agent M2M client) so the runtime is token-gated, then
134
+ > writes the live token URL, scope, client id, and discovery URL back into
135
+ > `studio.toml`, `agentcore.json`, and the printed buyer block. Hand those to
136
+ > each buyer; the client secret is retrieved read-only in the AWS Console
137
+ > (Cognito → User pools → App clients → "Show client secret") — never
138
+ > persisted by studio.
136
139
  >
137
- > 1. `bag deploy provision-cognito` emits a self-contained Cognito CDK app
138
- > (UserPool + M2M app client) the user runs `cdk deploy --outputs-file
139
- > cdk-outputs.json` themselves.
140
- > 2. `bag deploy provision-cognito --wire` reads that local outputs file and
141
- > patches `agentcore.json`'s `authorizerConfiguration.customJwtAuthorizer` +
142
- > the card's `OAUTH_TOKEN_URL` / `OAUTH_SCOPE` (no AWS call). The client
143
- > secret is retrieved read-only in the AWS Console (Cognito → User pools →
144
- > App clients → "Show client secret") — never persisted by studio.
140
+ > `bag deploy provision-cognito` (the CDK path) is **deprecated**: a deploy
141
+ > provisions and uses its own pool regardless, so the CDK stack is a billable
142
+ > orphan and anything wired from it gets overwritten.
145
143
  >
146
- > Buyers then reach the agent over plain HTTPS + an OAuth2 Bearer (the
144
+ > Buyers reach the agent over plain HTTPS + an OAuth2 Bearer (the
147
145
  > client-credentials grant) — **no AWS SigV4 / IAM credentials**. Locally,
148
146
  > `bag dev` runs without Cognito env, so the card omits the scheme.
149
147