@bnbagent/studio-cli 0.0.14-alpha.1 → 0.0.14-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.
package/dist/bag.js CHANGED
@@ -36,6 +36,7 @@ import {
36
36
  nativeProtocolOf,
37
37
  normalizeB402PriceUsd,
38
38
  normalizeProtocolFaces,
39
+ platformHostMismatchMessage,
39
40
  pnpmVersion,
40
41
  printErr,
41
42
  printOut,
@@ -70,7 +71,7 @@ import {
70
71
  x402SellerIsFree,
71
72
  x402SellerPricingState,
72
73
  x402SellerUsesB402
73
- } from "./chunk-QVYWAJEK.js";
74
+ } from "./chunk-CFMETPKS.js";
74
75
  import {
75
76
  validateAgentcoreName
76
77
  } from "./chunk-U7IDQ3K5.js";
@@ -769,7 +770,7 @@ import {
769
770
  var CAMPAIGN_DOC_URL = "https://www.bnbchain.org/en/blog/bnb-agent-studio-is-live-on-bnb-chain-ai-agents-from-one-prompt";
770
771
  var CAMPAIGN_CHECK_TIMEOUT_MS = 6e3;
771
772
  async function fetchCampaignActive() {
772
- const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-F3AOM5UO.js");
773
+ const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-AVWKY3GN.js");
773
774
  const controller = new AbortController();
774
775
  const timer = setTimeout(() => controller.abort(), CAMPAIGN_CHECK_TIMEOUT_MS);
775
776
  try {
@@ -988,6 +989,7 @@ import * as path3 from "path";
988
989
 
989
990
  // src/cli/utils/prompt.ts
990
991
  import * as readline from "readline/promises";
992
+ import { Writable } from "stream";
991
993
  var PromptCancelled = class extends Error {
992
994
  constructor() {
993
995
  super("prompt cancelled");
@@ -1032,17 +1034,25 @@ async function promptUserStderr(question) {
1032
1034
  }
1033
1035
  }
1034
1036
  async function promptHidden(question) {
1035
- process.stderr.write(question);
1037
+ const wasRaw = process.stdin.isRaw;
1038
+ const muted = new Writable({
1039
+ write(_chunk, _encoding, done) {
1040
+ done();
1041
+ }
1042
+ });
1036
1043
  const rl = readline.createInterface({
1037
1044
  input: process.stdin,
1038
- // No output stream — the answer is not echoed. (readline still needs
1039
- // terminal:false handling; we simply don't attach stdout.)
1040
- terminal: false
1045
+ output: muted,
1046
+ terminal: Boolean(process.stdin.isTTY),
1047
+ historySize: 0
1041
1048
  });
1042
1049
  try {
1050
+ process.stderr.write(question);
1043
1051
  return await ask(rl, "");
1044
1052
  } finally {
1045
1053
  rl.close();
1054
+ if (process.stdin.isTTY && wasRaw) process.stdin.setRawMode(true);
1055
+ muted.end();
1046
1056
  process.stderr.write("\n");
1047
1057
  }
1048
1058
  }
@@ -1063,22 +1073,22 @@ function stdinIsTty() {
1063
1073
 
1064
1074
  // src/cli/feedback.ts
1065
1075
  var FEEDBACK_FORM_TEMPLATE_ENV = "BAG_FEEDBACK_DEVELOPMENT_FORM_URL_TEMPLATE";
1066
- var FEEDBACK_FORM_URL_SLOT = "https://docs.google.com/forms/d/e/<BNB_AGENT_STUDIO_FORM_ID>/viewform";
1076
+ var FEEDBACK_ISSUES_URL = "https://github.com/bnb-chain/bnbagent-studio/issues/new";
1067
1077
  var DEPLOY_FEEDBACK_NUDGE_MARKER = "feedback-deploy-nudge-v1";
1068
1078
  var TRUSTED_FEEDBACK_FORMS = [];
1069
1079
  function feedbackFormDisplayUrl() {
1070
1080
  const template = TRUSTED_FEEDBACK_FORMS[0]?.template;
1071
- if (!template) return FEEDBACK_FORM_URL_SLOT;
1081
+ if (!template) return FEEDBACK_ISSUES_URL;
1072
1082
  try {
1073
1083
  const url = new URL(template);
1074
1084
  url.search = "";
1075
1085
  return url.toString();
1076
1086
  } catch {
1077
- return FEEDBACK_FORM_URL_SLOT;
1087
+ return FEEDBACK_ISSUES_URL;
1078
1088
  }
1079
1089
  }
1080
1090
  function feedbackErrorHint() {
1081
- return `feedback: run \`bag feedback\` to create a local redacted report; form: ${feedbackFormDisplayUrl()}`;
1091
+ return `feedback: run \`bag feedback\` to create a local redacted report; submit: ${feedbackFormDisplayUrl()}`;
1082
1092
  }
1083
1093
  function deployFeedbackNudgeLines(projectRoot, environment = process.env) {
1084
1094
  if (!feedbackIsInteractive(environment)) return [];
@@ -1095,8 +1105,8 @@ function deployFeedbackNudgeLines(projectRoot, environment = process.env) {
1095
1105
  }
1096
1106
  return [
1097
1107
  "\nFeedback (shown once after a successful deploy):",
1098
- " Run `bag feedback` to review a local redacted report and open the survey.",
1099
- ` Google Form: ${feedbackFormDisplayUrl()}`
1108
+ " Run `bag feedback` to review a local redacted report and open the feedback page.",
1109
+ ` Feedback: ${feedbackFormDisplayUrl()}`
1100
1110
  ];
1101
1111
  }
1102
1112
  var PREFILL_KEYS = /* @__PURE__ */ new Set([
@@ -1141,7 +1151,7 @@ var ERROR_CODE_SET2 = /* @__PURE__ */ new Set([
1141
1151
  var WALLET_SET2 = /* @__PURE__ */ new Set(["altana", "evm-local", "twak"]);
1142
1152
  function registerFeedback(program) {
1143
1153
  program.command("feedback").description(
1144
- "Create a redacted local diagnostic bundle and open the feedback form."
1154
+ "Create a redacted local diagnostic bundle and open the feedback form or issue tracker."
1145
1155
  ).option("--project-root <path>", "Override project root.").option(
1146
1156
  "--allow-development-form",
1147
1157
  "Use the explicitly configured untrusted development form instead of a release-pinned production form."
@@ -1172,6 +1182,21 @@ async function cmdFeedback(opts) {
1172
1182
  printOut(
1173
1183
  "Nothing is uploaded by bag. Review the JSON before attaching it, then submit the form yourself."
1174
1184
  );
1185
+ if (!opts.allowDevelopmentForm && TRUSTED_FEEDBACK_FORMS.length === 0) {
1186
+ printOut(`Feedback: ${FEEDBACK_ISSUES_URL}`);
1187
+ if (feedbackIsInteractive()) {
1188
+ const answer2 = await promptUser(
1189
+ "Open the GitHub feedback page? Review the local bundle before attaching it. [y/N] "
1190
+ );
1191
+ if (/^(y|yes)$/iu.test(answer2.trim())) {
1192
+ if (!await openFeedbackUrl(FEEDBACK_ISSUES_URL))
1193
+ printErr(
1194
+ "warning: could not open a browser; use the feedback URL above."
1195
+ );
1196
+ }
1197
+ }
1198
+ return 0;
1199
+ }
1175
1200
  let formUrl;
1176
1201
  let formId;
1177
1202
  try {
@@ -1187,7 +1212,7 @@ async function cmdFeedback(opts) {
1187
1212
  formId = googleFormId(new URL(formUrl).pathname);
1188
1213
  } catch {
1189
1214
  printErr(
1190
- opts.allowDevelopmentForm ? `error: the explicit development feedback form is missing or invalid. The local bundle remains available; configure ${FEEDBACK_FORM_TEMPLATE_ENV} and retry with --allow-development-form.` : "error: no release-pinned production feedback form is provisioned in this release. The local bundle remains available; a reviewed form ID and entry mapping must be added to the trusted release allowlist."
1215
+ opts.allowDevelopmentForm ? `error: the explicit development feedback form is missing or invalid. The local bundle remains available; set ${FEEDBACK_FORM_TEMPLATE_ENV} to a Google Forms prefilled URL such as https://docs.google.com/forms/d/e/FORM_ID/viewform?entry.123={feedback_id} (replace FORM_ID and entry.123 with your form IDs), then retry with --allow-development-form.` : "error: no release-pinned production feedback form is provisioned in this release. The local bundle remains available; a reviewed form ID and entry mapping must be added to the trusted release allowlist."
1191
1216
  );
1192
1217
  return 2;
1193
1218
  }
@@ -5022,15 +5047,22 @@ async function checkTwakWalletExists(root, _target) {
5022
5047
  if (home) {
5023
5048
  env.HOME = home;
5024
5049
  }
5025
- const result = await runCapture("twak", ["wallet", "address"], {
5026
- env,
5027
- timeoutMs: 15e3
5028
- });
5050
+ env.TWAK_WALLET_PASSWORD = getEnvVar(envLocalPath8(agentRoot2), "TWAK_WALLET_PASSWORD") || process.env.TWAK_WALLET_PASSWORD;
5051
+ const twakBin = await whichTwak(agentRoot2);
5052
+ if (twakBin === null) throw new Error(twakMissingHint());
5053
+ const result = await runCapture(
5054
+ twakBin,
5055
+ ["wallet", "address", "--chain", "bsc", "--json"],
5056
+ {
5057
+ env,
5058
+ timeoutMs: 15e3
5059
+ }
5060
+ );
5029
5061
  if (result.code !== 0) {
5030
5062
  failure = result.stderr.trim() || `twak wallet address exit ${result.code}`;
5031
5063
  } else {
5032
- const m = /0x[0-9a-fA-F]{40}/.exec(result.stdout);
5033
- actual = m ? m[0] : null;
5064
+ const payload = JSON.parse(result.stdout);
5065
+ actual = typeof payload.address === "string" && /^0x[0-9a-fA-F]{40}$/.test(payload.address) ? payload.address : null;
5034
5066
  if (actual === null) {
5035
5067
  failure = "no address in `twak wallet address` output";
5036
5068
  }
@@ -7752,27 +7784,6 @@ function acceptChoices(option, accepted, advertised) {
7752
7784
  });
7753
7785
  }
7754
7786
 
7755
- // src/cli/utils/randomPassword.ts
7756
- import { randomBytes as randomBytes2 } from "crypto";
7757
- var LOCAL_WALLET_PASSWORD_BYTES = 32;
7758
- function generateLocalWalletPassword() {
7759
- let bytes;
7760
- try {
7761
- bytes = randomBytes2(LOCAL_WALLET_PASSWORD_BYTES);
7762
- } catch (cause) {
7763
- throw new Error(
7764
- "failed to generate the local wallet password from the OS cryptographic random source; refusing to create the wallet",
7765
- { cause }
7766
- );
7767
- }
7768
- if (bytes.length !== LOCAL_WALLET_PASSWORD_BYTES) {
7769
- throw new Error(
7770
- "the OS cryptographic random source returned an invalid local wallet password; refusing to create the wallet"
7771
- );
7772
- }
7773
- return bytes.toString("base64url");
7774
- }
7775
-
7776
7787
  // src/cli/wallet.ts
7777
7788
  import * as fs25 from "fs";
7778
7789
  import * as path25 from "path";
@@ -7784,6 +7795,7 @@ import {
7784
7795
  } from "@bnbagent/studio-runtime/config";
7785
7796
  import { tbnbFaucetHint as tbnbFaucetHint2, uFaucetHint as uFaucetHint2 } from "@bnbagent/studio-runtime/networks";
7786
7797
  import { buildPolicy, buildWallet as buildWallet2 } from "@bnbagent/studio-runtime/wallet";
7798
+ import lockfile from "proper-lockfile";
7787
7799
  import { getAddress as toChecksumAddress } from "viem";
7788
7800
 
7789
7801
  // src/cli/_walletCfg.ts
@@ -8013,17 +8025,6 @@ async function currentInteractiveSession() {
8013
8025
  "The faucet requires an interactive GitHub login; unset BNBAGENT_API_TOKEN and run `bag platform login`."
8014
8026
  );
8015
8027
  }
8016
- const refreshed = await runPlatformAccountCommand(["whoami"], {
8017
- json: true,
8018
- quiet: true
8019
- });
8020
- if (refreshed.code !== 0) {
8021
- throw new FaucetClientError(
8022
- "auth.signed_out",
8023
- "Sign in with GitHub first: `bag platform login`.",
8024
- 401
8025
- );
8026
- }
8027
8028
  const file = sessionFile();
8028
8029
  let stat;
8029
8030
  let parsed;
@@ -8032,8 +8033,8 @@ async function currentInteractiveSession() {
8032
8033
  parsed = record(JSON.parse(fs23.readFileSync(file, "utf8")));
8033
8034
  } catch {
8034
8035
  throw new FaucetClientError(
8035
- "auth.session_missing",
8036
- "The platform login succeeded but its local session could not be read; run `bag platform login` again.",
8036
+ "auth.signed_out",
8037
+ "No readable platform login was found. Sign in with GitHub first: `bag platform login`.",
8037
8038
  401
8038
8039
  );
8039
8040
  }
@@ -8056,10 +8057,25 @@ async function currentInteractiveSession() {
8056
8057
  if (new URL(configuredBase).origin !== new URL(sessionBase).origin) {
8057
8058
  throw new FaucetClientError(
8058
8059
  "auth.host_mismatch",
8059
- `The platform session belongs to ${sessionBase}, but Studio targets ${configuredBase}. Run \`bag platform login\` against the intended API.`,
8060
+ platformHostMismatchMessage(
8061
+ new URL(sessionBase).origin,
8062
+ new URL(configuredBase).origin
8063
+ ),
8060
8064
  401
8061
8065
  );
8062
8066
  }
8067
+ const refreshed = await runPlatformAccountCommand(["trial"], {
8068
+ json: true,
8069
+ quiet: true
8070
+ });
8071
+ if (refreshed.code !== 0) {
8072
+ throw new FaucetClientError(
8073
+ "auth.signed_out",
8074
+ "Could not verify platform login. Run `bag platform login` and retry.",
8075
+ 401
8076
+ );
8077
+ }
8078
+ parsed = record(JSON.parse(fs23.readFileSync(file, "utf8")));
8063
8079
  return {
8064
8080
  apiUrl: configuredBase,
8065
8081
  accessToken: requiredString(parsed.access_token, "session access token")
@@ -8160,9 +8176,13 @@ async function requestJson(session, method, endpoint, fetchImpl, opts = {}, retr
8160
8176
  continue;
8161
8177
  }
8162
8178
  const error = record(record(payload).error);
8179
+ let message = typeof error.message === "string" ? error.message : `The faucet request failed with HTTP ${response.status}.`;
8180
+ if (error.code === "faucet.claim_cooldown") {
8181
+ message = `Check your current funds with \`bag wallet balance\`. This cooldown applies to your GitHub account across all wallets. ${message.replace("per GitHub account and wallet address", "per GitHub account (across all wallets)")}`;
8182
+ }
8163
8183
  throw new FaucetClientError(
8164
8184
  typeof error.code === "string" ? error.code : "faucet.request_failed",
8165
- typeof error.message === "string" ? error.message : `The faucet request failed with HTTP ${response.status}.`,
8185
+ message,
8166
8186
  response.status
8167
8187
  );
8168
8188
  }
@@ -8384,7 +8404,9 @@ async function fundWalletFromFaucet(wallet, deps = {}) {
8384
8404
  );
8385
8405
  }
8386
8406
  return {
8387
- outcome: claim.status === "confirmed" ? "confirmed" : claim.status === "partial" ? "partial" : "failed",
8407
+ outcome: claim.status === "confirmed" ? Object.values(claim.assets).some(
8408
+ (asset) => !["confirmed", "skipped_balance_cap"].includes(asset.status)
8409
+ ) ? "partial" : "confirmed" : claim.status === "partial" ? "partial" : "failed",
8388
8410
  claim,
8389
8411
  ...balances ? { balances } : {}
8390
8412
  };
@@ -8395,13 +8417,19 @@ async function fundProjectWallet(projectRoot, deps = {}) {
8395
8417
  if (kind !== "evm-local" && kind !== "altana") {
8396
8418
  throw new FaucetClientError(
8397
8419
  "faucet.wallet_unsupported",
8398
- "Automatic faucet funding currently supports wallet.kind='evm-local' and 'altana'."
8420
+ kind === "twak" ? "This faucet supports local and Altana wallets. TWAK uses sponsored gas for supported testnet operations; use `bag wallet balance` to check funds and the public testnet faucet if tokens are still needed." : "This faucet supports local and Altana wallets. Use `bag wallet balance` and fund this wallet through an external wallet or a public testnet faucet."
8399
8421
  );
8400
8422
  }
8401
8423
  if (projectNetwork(projectRoot) !== "bsc-testnet") {
8402
8424
  throw new FaucetClientError(
8403
8425
  "faucet.network_unsupported",
8404
- "Automatic faucet funding is testnet-only; [network].default must be 'bsc-testnet'."
8426
+ "This faucet only provides testnet tokens. Mainnet has no faucet; transfer BNB and U from an external wallet, then check `bag wallet balance`."
8427
+ );
8428
+ }
8429
+ if (!String(walletCfg.address ?? "").trim()) {
8430
+ throw new FaucetClientError(
8431
+ "faucet.wallet_missing",
8432
+ "This project has no wallet yet. Run `bag wallet new` first (use --generate-password for a new local wallet), then `bag wallet fund`."
8405
8433
  );
8406
8434
  }
8407
8435
  const wallet = kind === "altana" ? (deps.adminWalletFactory ?? buildLocalAdminWallet)(
@@ -8428,12 +8456,17 @@ function printFaucetResult(result) {
8428
8456
  printOut(` claim: ${claim.claimId}`);
8429
8457
  for (const [asset, transfer] of Object.entries(claim.assets)) {
8430
8458
  const tx = transfer.transactionHash ? ` tx=${transfer.transactionHash}` : "";
8459
+ if (transfer.status === "skipped_balance_cap") {
8460
+ printErr(
8461
+ `warning: ${asset} was not sent because its balance cap was reached.`
8462
+ );
8463
+ }
8431
8464
  printOut(` ${asset}: ${transfer.amount} \u2014 ${transfer.status}${tx}`);
8432
8465
  }
8433
8466
  }
8434
8467
  if (result.balances) {
8435
8468
  printOut(
8436
- `\u2713 verified wallet balance: ${result.balances.native} ${result.balances.nativeSymbol}, ${result.balances.u} U`
8469
+ `Current wallet balance: ${result.balances.native} ${result.balances.nativeSymbol}, ${result.balances.u} U`
8437
8470
  );
8438
8471
  } else if (result.outcome === "confirmed") {
8439
8472
  printErr(
@@ -8442,9 +8475,12 @@ function printFaucetResult(result) {
8442
8475
  }
8443
8476
  if (result.outcome === "partial") {
8444
8477
  printErr(
8445
- "warning: faucet funding completed only partially; do not retry blindly if an asset is marked unknown. Keep the claim/transaction details above for support."
8478
+ "warning: faucet funding completed only partially. Assets marked confirmed were sent; unknown or pending transfers are not yet confirmed. Check `bag wallet balance` and the transaction details before retrying; keep the claim ID for support."
8446
8479
  );
8447
8480
  }
8481
+ printOut(
8482
+ "Check funds with `bag wallet balance`; when ready, run `bag doctor`."
8483
+ );
8448
8484
  }
8449
8485
  async function maybeAutoFundProject(projectRoot, deps = {}) {
8450
8486
  try {
@@ -8472,6 +8508,27 @@ async function maybeAutoFundProject(projectRoot, deps = {}) {
8472
8508
  }
8473
8509
  }
8474
8510
 
8511
+ // src/cli/utils/randomPassword.ts
8512
+ import { randomBytes as randomBytes2 } from "crypto";
8513
+ var LOCAL_WALLET_PASSWORD_BYTES = 32;
8514
+ function generateLocalWalletPassword() {
8515
+ let bytes;
8516
+ try {
8517
+ bytes = randomBytes2(LOCAL_WALLET_PASSWORD_BYTES);
8518
+ } catch (cause) {
8519
+ throw new Error(
8520
+ "failed to generate the local wallet password from the OS cryptographic random source; refusing to create the wallet",
8521
+ { cause }
8522
+ );
8523
+ }
8524
+ if (bytes.length !== LOCAL_WALLET_PASSWORD_BYTES) {
8525
+ throw new Error(
8526
+ "the OS cryptographic random source returned an invalid local wallet password; refusing to create the wallet"
8527
+ );
8528
+ }
8529
+ return bytes.toString("base64url");
8530
+ }
8531
+
8475
8532
  // src/cli/walletSession.ts
8476
8533
  import * as fs24 from "fs";
8477
8534
  import * as path24 from "path";
@@ -9132,6 +9189,15 @@ function isFile8(p) {
9132
9189
  }
9133
9190
  function printNextAfterNew(projectRoot, address) {
9134
9191
  const network = projectNetwork(projectRoot);
9192
+ if (network === "bsc-testnet" && ["evm-local", "altana"].includes(
9193
+ walletKind(
9194
+ isFile8(path25.join(projectRoot, "studio.toml")) ? loadWalletCfg(projectRoot) : {}
9195
+ )
9196
+ )) {
9197
+ printErr(
9198
+ "Next: `bag platform login`, then `bag wallet fund` to request testnet tBNB + U."
9199
+ );
9200
+ }
9135
9201
  const fund = network === "bsc-testnet" ? `fund it (free, only needed for paid models / on-chain settle):
9136
9202
  tBNB: ${tbnbFaucetHint2(address)}
9137
9203
  U: ${uFaucetHint2(address)}` : "fund it with BNB (gas) + U before selling paid work";
@@ -9149,7 +9215,7 @@ function requirePassword2() {
9149
9215
  const pw = process.env[PASSWORD_ENV2];
9150
9216
  if (!pw) {
9151
9217
  throw new Error(
9152
- `${PASSWORD_ENV2} env var is required. Set it in your shell or .env.local before running this command.`
9218
+ `${PASSWORD_ENV2} env var is required. For a new wallet, run \`bag wallet new --generate-password\` to generate and save it privately, or set it in .studio/.env.local. For an existing wallet, restore its original password.`
9153
9219
  );
9154
9220
  }
9155
9221
  return pw;
@@ -9271,7 +9337,7 @@ function registerWallet(program) {
9271
9337
  )
9272
9338
  );
9273
9339
  p.command("fund").description(
9274
- "Claim the operator-configured tBNB/U testnet grant using the current GitHub login."
9340
+ "Fund this project wallet with testnet tBNB + U. Requires `bag wallet new` and GitHub login via `bag platform login`."
9275
9341
  ).option("--project-root <path>", "Override project root.").action(
9276
9342
  act(wrap2((opts) => cmdFund(opts.projectRoot)))
9277
9343
  );
@@ -9314,6 +9380,25 @@ async function cmdNew(opts) {
9314
9380
  }
9315
9381
  projectRoot = process.cwd();
9316
9382
  }
9383
+ const lockTarget = path25.dirname(privateEnvPath(projectRoot));
9384
+ let release2;
9385
+ try {
9386
+ release2 = lockfile.lockSync(lockTarget, { realpath: true, retries: 0 });
9387
+ } catch (error) {
9388
+ if (error.code === "ELOCKED") {
9389
+ throw new Error(
9390
+ "Another wallet creation is in progress for this project. Wait for it to finish, then retry."
9391
+ );
9392
+ }
9393
+ throw error;
9394
+ }
9395
+ try {
9396
+ return await createProjectWallet(opts, projectRoot);
9397
+ } finally {
9398
+ release2();
9399
+ }
9400
+ }
9401
+ async function createProjectWallet(opts, projectRoot) {
9317
9402
  let walletCfg = {};
9318
9403
  if (isFile8(path25.join(projectRoot, "studio.toml"))) {
9319
9404
  walletCfg = loadWalletCfg(projectRoot);
@@ -9345,7 +9430,7 @@ async function cmdNew(opts) {
9345
9430
  }
9346
9431
  if (opts.generatePassword && (anchored !== "" || existingKeystores.length > 0)) {
9347
9432
  printErr(
9348
- "error: refusing to generate a replacement password for an existing or anchored evm-local wallet."
9433
+ "error: refusing to generate a replacement password for an existing or anchored evm-local wallet. To adopt a saved wallet, run `bag wallet new` without --generate-password using its original saved password."
9349
9434
  );
9350
9435
  return 1;
9351
9436
  }
@@ -9372,9 +9457,9 @@ async function cmdNew(opts) {
9372
9457
  if (opts.generatePassword) {
9373
9458
  setEnvVar(privateEnvPath(projectRoot), PASSWORD_ENV2, password);
9374
9459
  }
9375
- fs25.mkdirSync(keystoreDir, { recursive: true });
9376
9460
  let provider;
9377
9461
  try {
9462
+ fs25.mkdirSync(keystoreDir, { recursive: true });
9378
9463
  provider = new EVMWalletProvider({
9379
9464
  password,
9380
9465
  ...privateKey !== null ? { privateKey } : {},
@@ -9397,8 +9482,28 @@ async function cmdNew(opts) {
9397
9482
  }
9398
9483
  throw error;
9399
9484
  }
9485
+ try {
9486
+ persistAddress(projectRoot, provider.address, opts.keystoreDir);
9487
+ } catch (cause) {
9488
+ throw new Error(
9489
+ "The wallet and its password were saved, but writing the address to studio.toml failed. Restore file permissions, then run `bag wallet new` without --generate-password to adopt the saved wallet.",
9490
+ { cause }
9491
+ );
9492
+ }
9400
9493
  printOut(provider.address);
9401
- persistAddress(projectRoot, provider.address, opts.keystoreDir);
9494
+ if (opts.generatePassword) {
9495
+ printErr(
9496
+ `Password stored (0600): ${envLocalPath12(projectRoot)} (WALLET_PASSWORD)`
9497
+ );
9498
+ printErr(
9499
+ `Keystore JSON (0600): ${path25.join(keystoreDir, `${provider.address}.json`)}`
9500
+ );
9501
+ if (process.env[PASSWORD_ENV2] && process.env[PASSWORD_ENV2] !== password) {
9502
+ printErr(
9503
+ "Your shell has a different WALLET_PASSWORD. Run `unset WALLET_PASSWORD` before the next bag command so it loads the saved password."
9504
+ );
9505
+ }
9506
+ }
9402
9507
  if (kind === "altana") {
9403
9508
  const network = projectNetwork(projectRoot);
9404
9509
  printErr(
@@ -9581,7 +9686,7 @@ async function collectTwakCredentials() {
9581
9686
  }
9582
9687
  if (!stdinIsTty()) {
9583
9688
  printErr(
9584
- "error: Trust Wallet NaaS credentials are required. In a non-interactive run, set TWAK_ACCESS_ID and TWAK_HMAC_SECRET in .studio/.env.local, then retry `bag wallet twak-init`."
9689
+ "error: Trust Wallet NaaS credentials are required. In a non-interactive run, set TWAK_ACCESS_ID and TWAK_HMAC_SECRET in .studio/.env.local, then retry `bag wallet twak-init`. Create a NaaS app at https://portal.trustwallet.com/dashboard/apps. If you already have ~/.twak/credentials.json, copy accessId and hmacSecret into these variables; Studio uses a separate project credentials file."
9585
9690
  );
9586
9691
  return null;
9587
9692
  }
@@ -9713,7 +9818,7 @@ ${detail}` : `error: \`twak init --json\` did not produce ${credentialsFile}.`
9713
9818
  fs25.chmodSync(credentialsFile, 384);
9714
9819
  const authenticatedRead = await runCapture(
9715
9820
  twakBin,
9716
- ["price", "BNB", "--chain", "bsc", "--json"],
9821
+ ["search", "BNB", "--networks", "20000714", "--limit", "1", "--json"],
9717
9822
  {
9718
9823
  env: twakChildEnv(homeDir),
9719
9824
  timeoutMs: 12e4
@@ -9741,11 +9846,12 @@ ${detail}` : "error: Trust Wallet credential verification failed."
9741
9846
  "TWAK_WALLET_PASSWORD"
9742
9847
  );
9743
9848
  if (walletExisted) {
9744
- const existing = twakPasswordForExistingWallet(projectRoot);
9849
+ const explicitPassword = opts.passwordStdin || opts.passwordFile;
9850
+ const existing = explicitPassword ? await resolveTwakInitPassword(opts) : twakPasswordForExistingWallet(projectRoot);
9745
9851
  if (existing === null) {
9746
9852
  if (!stdinIsTty()) {
9747
9853
  printErr(
9748
- "error: the project twak wallet already exists but TWAK_WALLET_PASSWORD is missing from .studio/.env.local."
9854
+ "error: the project twak wallet already exists but TWAK_WALLET_PASSWORD is missing from .studio/.env.local. Restore the original password with --password-stdin or --password-file <0600 file>."
9749
9855
  );
9750
9856
  return 1;
9751
9857
  }
@@ -9799,9 +9905,6 @@ ${detail}` : `error: \`twak wallet create --no-keychain --json\` did not produce
9799
9905
  }
9800
9906
  }
9801
9907
  fs25.chmodSync(walletFile, 384);
9802
- printErr(
9803
- `\u2713 Studio verified Trust Wallet credentials and prepared the project wallet under ${twakDir}`
9804
- );
9805
9908
  const address = await readTwakAddress(
9806
9909
  twakBin,
9807
9910
  projectRoot,
@@ -9820,6 +9923,9 @@ ${detail}` : `error: \`twak wallet create --no-keychain --json\` did not produce
9820
9923
  if (persistAfterVerification) {
9821
9924
  persistTwakPassword(projectRoot, password);
9822
9925
  }
9926
+ printErr(
9927
+ `\u2713 Studio verified Trust Wallet credentials and prepared the project wallet under ${twakDir}`
9928
+ );
9823
9929
  printOut(address);
9824
9930
  persistAddress(projectRoot, address);
9825
9931
  printNextAfterNew(projectRoot, address);
@@ -12145,7 +12251,8 @@ async function runOnboarding(o) {
12145
12251
  );
12146
12252
  return false;
12147
12253
  }
12148
- let password;
12254
+ const generatePassword = o.walletKind === "evm-local" && o.evmKeystore === void 0;
12255
+ let password = "";
12149
12256
  if (o.walletKind === "evm-local" && o.evmKeystore !== void 0) {
12150
12257
  password = (await promptHidden("Existing evm-local keystore password (hidden): ")).trim();
12151
12258
  if (!password) {
@@ -12158,9 +12265,8 @@ async function runOnboarding(o) {
12158
12265
  "Onboarding (TTY) \u2014 reusing the encrypted evm-local keystore with its original password."
12159
12266
  );
12160
12267
  } else if (o.walletKind === "evm-local") {
12161
- password = generateLocalWalletPassword();
12162
12268
  printOut(
12163
- "Onboarding (TTY) \u2014 generated a private local-wallet password and stored it in .studio/.env.local."
12269
+ "Onboarding (TTY) \u2014 generating a private local-wallet password and saving it in .studio/.env.local."
12164
12270
  );
12165
12271
  } else {
12166
12272
  const prompted = await promptAltanaAdminPassword();
@@ -12173,32 +12279,32 @@ async function runOnboarding(o) {
12173
12279
  const { envLocalPath: envLocalPath21 } = await import("@bnbagent/studio-runtime/config");
12174
12280
  const localEnvPath = envLocalPath21(agentRoot2);
12175
12281
  const priorPersistedPassword = getEnvVar(localEnvPath, "WALLET_PASSWORD");
12176
- setEnvVar(localEnvPath, "WALLET_PASSWORD", password);
12282
+ if (!generatePassword) setEnvVar(localEnvPath, "WALLET_PASSWORD", password);
12177
12283
  const savedCwd = process.cwd();
12178
12284
  const savedPw = process.env.WALLET_PASSWORD;
12179
12285
  let address = null;
12180
12286
  let existingKeystoreVerified = false;
12181
12287
  try {
12182
12288
  process.chdir(agentRoot2);
12183
- process.env.WALLET_PASSWORD = password;
12289
+ if (!generatePassword) process.env.WALLET_PASSWORD = password;
12184
12290
  let rc;
12185
12291
  try {
12186
- rc = await cmdNew({ projectRoot: agentRoot2 });
12292
+ rc = await cmdNew({ projectRoot: agentRoot2, generatePassword });
12187
12293
  } catch (exc) {
12188
12294
  if (exc instanceof CliExit) {
12189
12295
  rc = exc.code;
12190
12296
  } else {
12191
- printErr(
12192
- `warning: wallet creation failed: ${exc instanceof Error ? exc.message : exc}`
12297
+ throw new Error(
12298
+ `Wallet creation failed: ${exc instanceof Error ? exc.message : exc}. Fix the problem, then run \`bag wallet new${generatePassword ? " --generate-password" : ""}\` in app/agent.`,
12299
+ { cause: exc }
12193
12300
  );
12194
- return false;
12195
12301
  }
12196
12302
  }
12197
12303
  if (rc !== 0) {
12198
- printErr(
12199
- "warning: wallet creation returned non-zero; skipping activation."
12200
- );
12201
- return false;
12304
+ throw new CliExit(rc);
12305
+ }
12306
+ if (generatePassword) {
12307
+ process.env.WALLET_PASSWORD = getEnvVar(localEnvPath, "WALLET_PASSWORD") ?? "";
12202
12308
  }
12203
12309
  address = readWalletAddress(path26.join(agentRoot2, "studio.toml"));
12204
12310
  existingKeystoreVerified = address !== null;
@@ -12219,6 +12325,10 @@ async function runOnboarding(o) {
12219
12325
  if (o.storageProvider === "ipfs") {
12220
12326
  await onboardIpfsKey(envLocalPath21(agentRoot2), o.ipfsKey);
12221
12327
  }
12328
+ if (o.network === "bsc-testnet")
12329
+ printOut(
12330
+ "Run `bag platform login`, then `bag wallet fund` to request testnet tBNB + U."
12331
+ );
12222
12332
  if (address) {
12223
12333
  await printFaucetHint(o.network, address);
12224
12334
  if (o.walletKind === "altana") {
@@ -12821,7 +12931,7 @@ function printNextSteps(name, provider, o = {}) {
12821
12931
  printOut(" (cd app/agent && bag wallet new)");
12822
12932
  } else {
12823
12933
  printOut(
12824
- o.evmKeystore ? " # 1. edit .studio/.env.local and set WALLET_PASSWORD to the imported keystore's original password" : " # 1. edit .studio/.env.local and set WALLET_PASSWORD (encrypts the keystore + signs all on-chain ops)"
12934
+ o.evmKeystore ? " # 1. edit .studio/.env.local and set WALLET_PASSWORD to the imported keystore's original password" : " # 1. let Studio generate and privately save the local wallet password"
12825
12935
  );
12826
12936
  printOut(
12827
12937
  " # bag auto-loads it from the owner-only file; never pass it on argv"
@@ -12830,11 +12940,19 @@ function printNextSteps(name, provider, o = {}) {
12830
12940
  printOut(
12831
12941
  o.evmKeystore ? " # 2. verify, adopt, and anchor the copied encrypted keystore" : " # 2. generate the agent's EOA wallet (the SOLE signer key)"
12832
12942
  );
12833
- printOut(" (cd app/agent && bag wallet new)");
12943
+ printOut(
12944
+ o.evmKeystore ? " (cd app/agent && bag wallet new)" : " (cd app/agent && bag wallet new --generate-password)"
12945
+ );
12834
12946
  printOut(
12835
12947
  " (cd app/agent && bag wallet show) # print address \u2014 copy it for funding"
12836
12948
  );
12837
12949
  }
12950
+ if (o.network === "bsc-testnet" && ["evm-local", "altana"].includes(o.walletKind ?? "evm-local")) {
12951
+ printOut(" bag platform login # sign in with GitHub");
12952
+ printOut(
12953
+ " (cd app/agent && bag wallet fund) # request testnet tBNB + U"
12954
+ );
12955
+ }
12838
12956
  printOut("");
12839
12957
  let nextStep;
12840
12958
  if (provider === "pieverse-llm") {
@@ -18872,13 +18990,21 @@ function formatTrialRemaining(seconds) {
18872
18990
  const minutes = Math.floor(safe % 3600 / 60);
18873
18991
  return `${hours}h ${String(minutes).padStart(2, "0")}m`;
18874
18992
  }
18875
- async function trialForProviderSelection() {
18993
+ async function trialForProviderSelection(required = false) {
18876
18994
  try {
18877
18995
  const result = await runPlatformAccountCommand(["trial"], {
18878
18996
  json: true,
18879
18997
  quiet: true
18880
18998
  });
18881
- if (result.code !== 0) return UNKNOWN_TRIAL;
18999
+ if (result.code !== 0) {
19000
+ if (required)
19001
+ throw new Error(
19002
+ String(
19003
+ tableOf13(result.data, "error").message ?? "Could not check platform login. Run `bag platform login` and retry."
19004
+ )
19005
+ );
19006
+ return UNKNOWN_TRIAL;
19007
+ }
18882
19008
  const trial = trialFromDeployCliJson(result.data);
18883
19009
  return {
18884
19010
  state: trial.case,
@@ -18886,7 +19012,8 @@ async function trialForProviderSelection() {
18886
19012
  expiresAt: trial.expiresAt,
18887
19013
  remainingSeconds: trial.remainingSeconds
18888
19014
  };
18889
- } catch {
19015
+ } catch (error) {
19016
+ if (required) throw error;
18890
19017
  return UNKNOWN_TRIAL;
18891
19018
  }
18892
19019
  }
@@ -18927,11 +19054,12 @@ async function cmdDeploy(opts, deployArgs) {
18927
19054
  const unavailable = unavailableProvidersForProject(root);
18928
19055
  const interactive = stdinIsTty();
18929
19056
  const needsTrial = opts.provider === "bnb" || !opts.provider && interactive;
18930
- const trial = needsTrial ? await trialForProviderSelection() : UNKNOWN_TRIAL;
19057
+ const trial = needsTrial ? await trialForProviderSelection(opts.provider === "bnb") : UNKNOWN_TRIAL;
18931
19058
  const visibleDeployments = deployments.map(
18932
19059
  (deployment) => deployment.provider === "bnb" && trial.state === "expired" ? { ...deployment, status: "expired" } : deployment
18933
19060
  );
18934
- renderProviderSelection(visibleDeployments, trial, unavailable);
19061
+ if (!opts.provider)
19062
+ renderProviderSelection(visibleDeployments, trial, unavailable);
18935
19063
  const selection = await selectDeployProvider({
18936
19064
  requested: opts.provider,
18937
19065
  deployments: visibleDeployments,
@@ -20256,15 +20384,11 @@ async function cmdInfo(opts) {
20256
20384
  return 2;
20257
20385
  }
20258
20386
  if (opts.provider === "bnb" && !bnbDeployment) {
20259
- printErr(
20260
- "no recorded BNB deployment; run `bag deploy status` to inspect providers."
20261
- );
20387
+ printMissingDeployment("bnb", opts.json);
20262
20388
  return 1;
20263
20389
  }
20264
20390
  if (opts.provider === "azure" && !azureDeployment) {
20265
- printErr(
20266
- "no recorded Azure deployment; run `bag deploy status` to inspect providers."
20267
- );
20391
+ printMissingDeployment("azure", opts.json);
20268
20392
  return 1;
20269
20393
  }
20270
20394
  if ((opts.provider === "bnb" || !opts.provider) && bnbDeployment) {
@@ -20277,11 +20401,15 @@ async function cmdInfo(opts) {
20277
20401
  if ((opts.provider === "azure" || !opts.provider) && azureDeployment) {
20278
20402
  return printAzureInfo(root, azureDeployment, opts);
20279
20403
  }
20280
- printErr(
20281
- "`bag deploy info` reports the platform buyer-access surface; this project has no recorded BNB deployment. Use `bag deploy status` to inspect recorded providers."
20282
- );
20404
+ printMissingDeployment("bnb", opts.json);
20283
20405
  return 1;
20284
20406
  }
20407
+ function printMissingDeployment(provider, json) {
20408
+ const message = `no recorded ${provider === "bnb" ? "BNB" : "Azure"} deployment. Run \`bag deploy status\` or \`bag deploy --provider ${provider}\`.`;
20409
+ if (json)
20410
+ printOut(JSON.stringify({ provider, status: "not_deployed", message }));
20411
+ else printErr(message);
20412
+ }
20285
20413
  async function selectRecordedForLifecycle(root, requested, operation) {
20286
20414
  const deployments = discoverRecordedDeployments(root);
20287
20415
  if (requested) {
@@ -22102,7 +22230,15 @@ function printChecks(checks) {
22102
22230
  // src/cli/env.ts
22103
22231
  import * as fs42 from "fs";
22104
22232
  import * as path43 from "path";
22105
- var SECRET_KEY_PARTS = ["PASSWORD", "SECRET", "TOKEN", "API_KEY"];
22233
+ var SECRET_KEY_PARTS = [
22234
+ "PASSWORD",
22235
+ "SECRET",
22236
+ "TOKEN",
22237
+ "KEY",
22238
+ "CREDENTIAL",
22239
+ "ACCESS_ID",
22240
+ "SESSION"
22241
+ ];
22106
22242
  var WALLET_PASSWORD_KEYS = /* @__PURE__ */ new Set([
22107
22243
  "WALLET_PASSWORD",
22108
22244
  "TWAK_WALLET_PASSWORD"
@@ -22133,7 +22269,9 @@ function registerEnv(program) {
22133
22269
  (key, value, opts) => cmdSet2(key, value, opts.file, opts.projectRoot)
22134
22270
  )
22135
22271
  );
22136
- p.command("get").description("Print current value of KEY.").argument("<key>").option("--file <path>").option(
22272
+ p.command("get").description(
22273
+ "Print current value of KEY; secret values are always redacted."
22274
+ ).argument("<key>").option("--file <path>").option(
22137
22275
  "--project-root <path>",
22138
22276
  "Resolve .env.local under this project root; ignored when --file is given."
22139
22277
  ).action(
@@ -22211,7 +22349,7 @@ function cmdGet2(key, fileArg, rootArg) {
22211
22349
  if (val === null) {
22212
22350
  return 1;
22213
22351
  }
22214
- printOut(val);
22352
+ printOut(isSecretKey(key) && val ? "<secret value set>" : val);
22215
22353
  return 0;
22216
22354
  }
22217
22355
 
@@ -622,7 +622,7 @@ async function runCaptureOut(cmd, args, opts = {}) {
622
622
  reject: false,
623
623
  stripFinalNewline: false,
624
624
  stdout: "pipe",
625
- stderr: "inherit"
625
+ stderr: opts.quiet ? "pipe" : "inherit"
626
626
  });
627
627
  return {
628
628
  code: result.exitCode ?? 1,
@@ -947,8 +947,8 @@ function packageRoot() {
947
947
  }
948
948
  }
949
949
  function studioCliVersion() {
950
- if ("0.0.14-alpha.1") {
951
- return "0.0.14-alpha.1";
950
+ if ("0.0.14-alpha.2") {
951
+ return "0.0.14-alpha.2";
952
952
  }
953
953
  const file = path4.join(packageRoot(), "package.json");
954
954
  const pkg = JSON.parse(fs4.readFileSync(file, "utf-8"));
@@ -1610,6 +1610,7 @@ async function runDeployCliJson(argv, opts = {}) {
1610
1610
  (result) => result.code !== 0,
1611
1611
  () => runCaptureOut(bin, [...prefix, ...argv, "--json"], {
1612
1612
  cwd: opts.cwd,
1613
+ quiet: opts.quiet,
1613
1614
  env: opts.env ? { ...process.env, ...opts.env } : void 0
1614
1615
  })
1615
1616
  );
@@ -1620,7 +1621,7 @@ async function runDeployCliJson(argv, opts = {}) {
1620
1621
  const parsed = JSON.parse(trimmed);
1621
1622
  data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1622
1623
  } catch {
1623
- printErr("error: bnbagent-deploy returned invalid JSON");
1624
+ if (!opts.quiet) printErr("error: bnbagent-deploy returned invalid JSON");
1624
1625
  return { code: code === 0 ? 1 : code, data: {} };
1625
1626
  }
1626
1627
  }
@@ -1646,7 +1647,38 @@ function bnbEnv() {
1646
1647
  BNBAGENT_CLI_SURFACE: "studio"
1647
1648
  };
1648
1649
  }
1650
+ function platformSessionFile() {
1651
+ return path7.join(
1652
+ process.env.BNBAGENT_DEPLOY_HOME?.trim() || path7.join(os2.homedir(), ".bnbagent-deploy"),
1653
+ "bnb",
1654
+ "session.json"
1655
+ );
1656
+ }
1657
+ function platformHostMismatchMessage(recorded, target) {
1658
+ return `Your platform login points at ${recorded}, but this command targets ${target}. Run \`bag platform logout && bag platform login\` to switch, then retry. If you intended to use the previous host, set BNBAGENT_API_URL to ${recorded} for login and subsequent commands.`;
1659
+ }
1660
+ function platformSessionHostError() {
1661
+ if (process.env.BNBAGENT_API_TOKEN?.trim()) return null;
1662
+ try {
1663
+ const session = JSON.parse(fs6.readFileSync(platformSessionFile(), "utf8"));
1664
+ const recorded = new URL(session.apiUrl).origin;
1665
+ const target = new URL(bnbPlatformApiUrl()).origin;
1666
+ return recorded === target ? null : platformHostMismatchMessage(recorded, target);
1667
+ } catch {
1668
+ return null;
1669
+ }
1670
+ }
1649
1671
  async function runPlatformAccountCommand(argv, opts = {}) {
1672
+ if (!["login", "logout"].includes(argv[0] ?? "")) {
1673
+ const message = platformSessionHostError();
1674
+ if (message) {
1675
+ if (!opts.quiet) printErr(`error: ${message}`);
1676
+ return {
1677
+ code: 1,
1678
+ data: { error: { code: "auth.host_mismatch", message } }
1679
+ };
1680
+ }
1681
+ }
1650
1682
  const run = async (args, cwd) => {
1651
1683
  if (opts.json) {
1652
1684
  return runDeployCliJson(args, { cwd, env: bnbEnv(), quiet: opts.quiet });
@@ -1755,5 +1787,7 @@ export {
1755
1787
  runDeployCliJson,
1756
1788
  trialFromDeployCliJson,
1757
1789
  bnbEnv,
1790
+ platformSessionFile,
1791
+ platformHostMismatchMessage,
1758
1792
  runPlatformAccountCommand
1759
1793
  };
@@ -12,6 +12,8 @@ import {
12
12
  bunxInstallDir,
13
13
  deployCommand,
14
14
  deployEndpointValue,
15
+ platformHostMismatchMessage,
16
+ platformSessionFile,
15
17
  providerPassthrough,
16
18
  renderSecretEnvFile,
17
19
  runDeployCliJson,
@@ -19,7 +21,7 @@ import {
19
21
  runPlatformAccountCommand,
20
22
  trialFromDeployCliJson,
21
23
  withDeployFiles
22
- } from "./chunk-QVYWAJEK.js";
24
+ } from "./chunk-CFMETPKS.js";
23
25
  export {
24
26
  BNB_PLATFORM_API_URL,
25
27
  BNB_PLATFORM_API_URL_ENV,
@@ -33,6 +35,8 @@ export {
33
35
  bunxInstallDir,
34
36
  deployCommand,
35
37
  deployEndpointValue,
38
+ platformHostMismatchMessage,
39
+ platformSessionFile,
36
40
  providerPassthrough,
37
41
  renderSecretEnvFile,
38
42
  runDeployCliJson,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bnbagent/studio-cli",
3
- "version": "0.0.14-alpha.1",
3
+ "version": "0.0.14-alpha.2",
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",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@bnbagent/deploy-cli": "0.5.15",
46
- "@bnbagent/sdk": "0.5.5",
46
+ "@bnbagent/sdk": "0.5.6",
47
47
  "ai": "^7.0.29",
48
48
  "archiver": "^8.0.0",
49
49
  "commander": "^15.0.0",
@@ -55,7 +55,7 @@
55
55
  "tar": "^7.4.0",
56
56
  "viem": "^2.54.0",
57
57
  "yaml": "^2.9.0",
58
- "@bnbagent/studio-runtime": "0.0.14-alpha.1"
58
+ "@bnbagent/studio-runtime": "0.0.14-alpha.2"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@a2a-js/sdk": "^0.3.14",
@@ -11,7 +11,7 @@ node = [
11
11
  # neutral. The agent project's serving deps (@a2a-js/sdk / MCP SDK / ai)
12
12
  # come from the runtimes/<R>/ recipe selected at `bag init` time.
13
13
  "@bnbagent/studio-runtime",
14
- "@bnbagent/sdk@0.5.5",
14
+ "@bnbagent/sdk@0.5.6",
15
15
  "zod@^3.25.0",
16
16
  ]
17
17
 
@@ -6,7 +6,7 @@ status = "v0.0.x"
6
6
  [dependencies]
7
7
  node = [
8
8
  "@bnbagent/studio-runtime",
9
- "@bnbagent/sdk@0.5.5",
9
+ "@bnbagent/sdk@0.5.6",
10
10
  "ai@^7.0.29",
11
11
  "zod@^3.25.0",
12
12
  ]
@@ -32,7 +32,7 @@ node = [
32
32
  # BNBAGENT_RUNTIME_SECRET_ID is set (default secretsmanager mode + platform).
33
33
  "@aws-sdk/client-secrets-manager@^3.600.0",
34
34
  "@bnbagent/studio-runtime",
35
- "@bnbagent/sdk@0.5.5",
35
+ "@bnbagent/sdk@0.5.6",
36
36
  # The LLM work hook (generateText + tools) and the model factory.
37
37
  "ai@^7.0.29",
38
38
  # Tool input schemas (AI SDK tools + MCP registerTool).
@@ -29,7 +29,7 @@ node = [
29
29
  # on either cloud.
30
30
  "@aws-sdk/client-secrets-manager@^3.600.0",
31
31
  "@bnbagent/studio-runtime",
32
- "@bnbagent/sdk@0.5.5",
32
+ "@bnbagent/sdk@0.5.6",
33
33
  # The LLM work hook (generateText + tools) and the model factory
34
34
  # (model.ts buildModel — studio.toml [llm] + the provider key env).
35
35
  "ai@^7.0.29",
@@ -6,7 +6,7 @@ status = "v0.0.x"
6
6
  [dependencies]
7
7
  node = [
8
8
  "@bnbagent/studio-runtime",
9
- "@bnbagent/sdk@0.5.5",
9
+ "@bnbagent/sdk@0.5.6",
10
10
  "ai@^7.0.29", # AI SDK `tool` wrappers around the buyer functions
11
11
  "zod@^3.25.0", # tool input schemas
12
12
  ]
@@ -11,7 +11,7 @@ Altana separates trusted administration from runtime authority:
11
11
  - `.studio/wallets/altana-session.json` is the one bounded, expiring runtime session and must stay mode `0600`.
12
12
  - `WALLET_PASSWORD` is admin-only. The Agent gets `ALTANA_SESSION`, never the password or admin keystore.
13
13
  - Generic signing is refused. ERC-8183 uses `sessionQuoteSigner()` and the approved quote checker.
14
- - The generated project pins `@bnbagent/sdk@0.5.5` and `@altananetwork/sdk@0.7.1`; doctor, readiness, and runtime loading reject version drift. SDK 0.5.4 introduced selector-bound calls, removed session-key token approvals, and requires an admin-provisioned bounded Commerce allowance. Projects upgrading from an older SDK must update it, re-grant with `bag wallet session grant --force`, and redeploy.
14
+ - The generated project pins `@bnbagent/sdk@0.5.6` and `@altananetwork/sdk@0.7.1`; doctor, readiness, and runtime loading reject version drift. SDK 0.5.4 introduced selector-bound calls, removed session-key token approvals, and requires an admin-provisioned bounded Commerce allowance. Projects upgrading from SDK versions older than 0.5.4 must update it, re-grant with `bag wallet session grant --force`, and redeploy.
15
15
  - Deployment ships ONLY the serialized session as the `ALTANA_SESSION` runtime secret; the admin keystore and `WALLET_PASSWORD` never leave the operator machine. Renewal after expiry: `bag wallet session grant --force`, then re-run `bag deploy`. Readiness fails on a missing/expired/address-mismatched session, a group/world-readable session file, a session inside the artifact root, or an unresolvable project-local `@altananetwork/sdk`; it warns under 7 days remaining. `bag deploy verify` needs `--skip-register` (no generic signing for the ERC-8004 register).
16
16
  - Altana refuses generic message signing, so Pieverse SIWE cannot authenticate `bag llm activate` or runtime credit renewal. `bag init --wallet-kind altana --llm-provider pieverse-llm` is rejected outright; use OpenRouter, OpenAI, or Anthropic (API-key providers). `bag llm activate` and `bag doctor` also flag the combination on projects edited by hand.
17
17
 
@@ -25,7 +25,7 @@ This one Studio command:
25
25
 
26
26
  1. Collects the NaaS Access ID and HMAC secret through hidden Studio prompts. It never starts `twak setup` or asks which AI harnesses to wire.
27
27
  2. Runs the project-pinned `twak init --json` with the two credentials in the child environment.
28
- 3. Performs an authenticated `twak price BNB --chain bsc --json` read. Newly written credentials are removed if this verification fails; pre-existing credentials are never deleted.
28
+ 3. Performs an authenticated `twak search BNB --networks 20000714 --limit 1 --json` read. Newly written credentials are removed if this verification fails; pre-existing credentials are never deleted.
29
29
  4. Generates `TWAK_WALLET_PASSWORD` from 32 bytes of the OS cryptographic random source and writes it to `.studio/.env.local` (0600). There is no weak-random fallback and the value is never printed.
30
30
  5. Creates the project-dedicated wallet with `--no-keychain --json`, tightens `.twak/` to 0700 and its credential/wallet files to 0600, reads the BSC address, and anchors it into `studio.toml`.
31
31
  6. Refuses to replace an anchored identity and never overwrites an existing wallet or credential file.
@@ -42,7 +42,7 @@ bag llm activate
42
42
  bag doctor
43
43
  ```
44
44
 
45
- No separate `bag wallet new` step is required. An existing wallet is adopted only after Studio verifies its password and reads its address. If its password is not already in `.studio/.env.local`, the user enters it through a hidden Studio prompt; Studio never invents a replacement password for an existing wallet.
45
+ No separate `bag wallet new` step is required. An existing wallet is adopted only after Studio verifies its password and reads its address. If its password is not already in `.studio/.env.local`, the user enters it through a hidden Studio prompt or supplies the original password using `--password-stdin` / `--password-file <0600 file>`; Studio never invents a replacement password for an existing wallet.
46
46
 
47
47
  ### Other wallet placements
48
48