@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 +243 -105
- package/dist/{chunk-QVYWAJEK.js → chunk-CFMETPKS.js} +38 -4
- package/dist/{deployCli-F3AOM5UO.js → deployCli-AVWKY3GN.js} +5 -1
- package/package.json +3 -3
- package/recipes/agent/recipe.toml +1 -1
- package/recipes/mpp-buyer/recipe.toml +1 -1
- package/recipes/runtimes/agentcore/recipe.toml +1 -1
- package/recipes/runtimes/azure-foundry/recipe.toml +1 -1
- package/recipes/x402-buyer/recipe.toml +1 -1
- package/skills/references/bnbagent-studio-using-altana-wallet.md +1 -1
- package/skills/references/bnbagent-studio-using-twak-wallet.md +2 -2
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-
|
|
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-
|
|
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.
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
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
|
|
1099
|
-
`
|
|
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;
|
|
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
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
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
|
|
5033
|
-
actual =
|
|
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.
|
|
8036
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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" ?
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
["
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
12192
|
-
`
|
|
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
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
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.
|
|
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(
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = [
|
|
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(
|
|
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.
|
|
951
|
-
return "0.0.14-alpha.
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
14
|
+
"@bnbagent/sdk@0.5.6",
|
|
15
15
|
"zod@^3.25.0",
|
|
16
16
|
]
|
|
17
17
|
|
|
@@ -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.
|
|
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.
|
|
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",
|
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|