@bnbagent/studio-cli 0.0.6-alpha.8 → 0.0.6

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
@@ -44,7 +44,7 @@ import {
44
44
  x402SellerIsFree,
45
45
  x402SellerPricingState,
46
46
  x402SellerUsesB402
47
- } from "./chunk-JZAW6HMV.js";
47
+ } from "./chunk-ODCZKKZJ.js";
48
48
  import {
49
49
  TWAK_CLI_MIN_VERSION,
50
50
  TWAK_CLI_VERSION,
@@ -69,35 +69,151 @@ import { ensureAltanaSessionLoaded } from "@bnbagent/studio-runtime/wallet";
69
69
  import * as fs from "fs";
70
70
  import {
71
71
  STUDIO_TOML,
72
- envLocalPath,
72
+ envLocalPath as envLocalPath2,
73
73
  findProjectRoot,
74
74
  findWorkspaceRoot,
75
75
  loadEnv
76
76
  } from "@bnbagent/studio-runtime/config";
77
- function autoloadProjectEnv() {
77
+
78
+ // src/cli/_erc8183Config.ts
79
+ import { envLocalPath } from "@bnbagent/studio-runtime/config";
80
+ var ERC8183_ADDRESS_OVERRIDE_KEYS = [
81
+ "ERC8183_COMMERCE_ADDRESS",
82
+ "ERC8183_ROUTER_ADDRESS",
83
+ "ERC8183_POLICY_ADDRESS"
84
+ ];
85
+ var MAX_UINT256 = 2n ** 256n - 1n;
86
+ function erc8183ContractEnvForProject(agentRoot2, fallback = process.env) {
87
+ const envPath = envLocalPath(agentRoot2);
88
+ const fromFile = {};
89
+ for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
90
+ const raw = getEnvVar(envPath, key);
91
+ if (raw === null) continue;
92
+ const value = raw.trim().replace(/^"(.*)"$/u, "$1").replace(/^'(.*)'$/u, "$1");
93
+ if (value) fromFile[key] = value;
94
+ }
95
+ const fromFallback = {};
96
+ const fallbackOwnsStack = ERC8183_ADDRESS_OVERRIDE_KEYS.some(
97
+ (key) => fallback[key] !== void 0
98
+ );
99
+ for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
100
+ const value = fallback[key]?.trim();
101
+ if (value) fromFallback[key] = value;
102
+ }
103
+ return fallbackOwnsStack ? fromFallback : fromFile;
104
+ }
105
+ function erc8183PricingState(pay) {
106
+ const priceRaw = pay.price;
107
+ const price = String(priceRaw ?? "").trim();
108
+ if (!price) return { kind: "unset" };
109
+ if (typeof priceRaw !== "string") {
110
+ return { kind: "invalid", field: "price", value: price };
111
+ }
112
+ const minRaw = pay.min_price;
113
+ const maxRaw = pay.max_price;
114
+ const minPrice = String(minRaw ?? "").trim() || "0";
115
+ const maxPrice = String(maxRaw ?? "").trim();
116
+ if (minRaw !== void 0 && typeof minRaw !== "string") {
117
+ return { kind: "invalid", field: "min_price", value: minPrice };
118
+ }
119
+ if (maxRaw !== void 0 && typeof maxRaw !== "string") {
120
+ return { kind: "invalid", field: "max_price", value: maxPrice };
121
+ }
122
+ for (const [field, value] of [
123
+ ["price", price],
124
+ ["min_price", minPrice],
125
+ ["max_price", maxPrice]
126
+ ]) {
127
+ if ((field !== "max_price" || value !== "") && !/^\d+$/.test(value)) {
128
+ return { kind: "invalid", field, value };
129
+ }
130
+ }
131
+ const list = BigInt(price);
132
+ const min = BigInt(minPrice);
133
+ const max = maxPrice ? BigInt(maxPrice) : MAX_UINT256;
134
+ if (list > MAX_UINT256 || min > MAX_UINT256 || max > MAX_UINT256) {
135
+ const [field, value] = list > MAX_UINT256 ? ["price", price] : min > MAX_UINT256 ? ["min_price", minPrice] : ["max_price", maxPrice];
136
+ return { kind: "invalid", field, value };
137
+ }
138
+ const clampedToMax = list < max ? list : max;
139
+ const effective = min > clampedToMax ? min : clampedToMax;
140
+ const base = {
141
+ listPrice: list,
142
+ minPrice: min,
143
+ maxPrice: max,
144
+ effectivePrice: effective
145
+ };
146
+ if (list > 0n && effective === 0n) {
147
+ return { kind: "clamped_to_zero", ...base };
148
+ }
149
+ return { kind: effective === 0n ? "free" : "paid", ...base };
150
+ }
151
+ function erc8183ContractOverrideState(env = process.env) {
152
+ const present = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
153
+ (key) => Boolean(env[key]?.trim())
154
+ );
155
+ const missing = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
156
+ (key) => !env[key]?.trim()
157
+ );
158
+ const invalid = present.filter(
159
+ (key) => !/^0x[0-9a-fA-F]{40}$/.test(env[key]?.trim() ?? "")
160
+ );
161
+ return {
162
+ mode: present.length === 0 ? "canonical" : invalid.length > 0 ? "invalid" : missing.length === 0 ? "custom" : "partial",
163
+ present: [...present],
164
+ missing: [...missing],
165
+ invalid
166
+ };
167
+ }
168
+
169
+ // src/cli/_env.ts
170
+ var autoloadedValues = /* @__PURE__ */ new Map();
171
+ function releaseAutoloadedValues() {
172
+ for (const [key, value] of autoloadedValues) {
173
+ if (process.env[key] === value) {
174
+ delete process.env[key];
175
+ }
176
+ }
177
+ autoloadedValues.clear();
178
+ }
179
+ function autoloadProjectEnv(start) {
78
180
  try {
79
- const root = findProjectRoot();
181
+ releaseAutoloadedValues();
182
+ const root = findProjectRoot(start);
80
183
  if (root === null) {
81
- return;
184
+ return releaseAutoloadedValues;
82
185
  }
83
- const envPath = envLocalPath(root);
186
+ const envPath = envLocalPath2(root);
84
187
  if (!fs.statSync(envPath).isFile()) {
85
- return;
188
+ return releaseAutoloadedValues;
86
189
  }
87
- for (const [key, value] of Object.entries(loadEnv(envPath))) {
88
- if (process.env[key] === void 0) {
89
- process.env[key] = value;
190
+ const explicitContractKeys = new Set(
191
+ ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
192
+ (key) => process.env[key] !== void 0
193
+ )
194
+ );
195
+ const loaded = loadEnv(envPath);
196
+ if (explicitContractKeys.size > 0) {
197
+ for (const key of ERC8183_ADDRESS_OVERRIDE_KEYS) {
198
+ if (!explicitContractKeys.has(key) && key in loaded) {
199
+ delete process.env[key];
200
+ delete loaded[key];
201
+ }
90
202
  }
91
203
  }
204
+ for (const [key, value] of Object.entries(loaded)) {
205
+ autoloadedValues.set(key, value);
206
+ }
92
207
  } catch {
93
208
  }
209
+ return releaseAutoloadedValues;
94
210
  }
95
211
 
96
212
  // src/cli/_hosted/campaign.ts
97
213
  import * as fs2 from "fs";
98
214
  import * as path from "path";
99
215
  import {
100
- envLocalPath as envLocalPath2,
216
+ envLocalPath as envLocalPath3,
101
217
  findSubProjectRoot,
102
218
  loadStudioToml
103
219
  } from "@bnbagent/studio-runtime/config";
@@ -106,7 +222,7 @@ import {
106
222
  var CAMPAIGN_DOC_URL = "https://www.bnbchain.org/en/blog/bnb-agent-studio-is-live-on-bnb-chain-ai-agents-from-one-prompt";
107
223
  var CAMPAIGN_CHECK_TIMEOUT_MS = 6e3;
108
224
  async function fetchCampaignActive() {
109
- const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-AJ25A4VK.js");
225
+ const { bnbPlatformApiUrl: bnbPlatformApiUrl2 } = await import("./deployCli-VM5TYKQX.js");
110
226
  const controller = new AbortController();
111
227
  const timer = setTimeout(() => controller.abort(), CAMPAIGN_CHECK_TIMEOUT_MS);
112
228
  try {
@@ -158,7 +274,7 @@ var NUDGE_WINDOWS = [
158
274
  ];
159
275
  var NUDGE_ORDER = ["", "12h", "1h", "expired"];
160
276
  function trialStatePath(start) {
161
- return path.join(path.dirname(envLocalPath2(start)), "platform-trial.json");
277
+ return path.join(path.dirname(envLocalPath3(start)), "platform-trial.json");
162
278
  }
163
279
  function loadTrialState(start) {
164
280
  try {
@@ -521,8 +637,8 @@ function tableOf(cfg, key) {
521
637
  const v = cfg[key];
522
638
  return v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
523
639
  }
524
- function registerProject(projectRootArg, opts = {}) {
525
- const projectRoot = normalizePath(projectRootArg);
640
+ function registerProject(projectRootArg2, opts = {}) {
641
+ const projectRoot = normalizePath(projectRootArg2);
526
642
  if (!isDir(projectRoot)) {
527
643
  throw new Error(`path is not a directory: ${projectRoot}`);
528
644
  }
@@ -1530,7 +1646,7 @@ function cmdDisable(projectRoot) {
1530
1646
  import * as fs8 from "fs";
1531
1647
  import * as path7 from "path";
1532
1648
  import {
1533
- envLocalPath as envLocalPath3,
1649
+ envLocalPath as envLocalPath4,
1534
1650
  findSubProjectRoot as findSubProjectRoot2,
1535
1651
  findWorkspaceRoot as findWorkspaceRoot2,
1536
1652
  loadStudioToml as loadStudioToml2
@@ -1796,6 +1912,14 @@ var EXCLUDED_NAMES = /* @__PURE__ */ new Set([
1796
1912
  ".ruff_cache",
1797
1913
  ".mypy_cache"
1798
1914
  ]);
1915
+ var CREDENTIAL_FILE_NAMES = /* @__PURE__ */ new Set([
1916
+ ".git-credentials",
1917
+ ".netrc",
1918
+ ".npmrc",
1919
+ ".pypirc",
1920
+ ".yarnrc",
1921
+ ".yarnrc.yml"
1922
+ ]);
1799
1923
  function registerBundle(program) {
1800
1924
  program.command("bundle").description(
1801
1925
  "Bundle a seller workspace + pinned SDK/runtime tarballs for handoff."
@@ -1846,6 +1970,11 @@ async function createBundle(opts = {}) {
1846
1970
  const archiveBase = `${safeName}-bundle-${stamp}`;
1847
1971
  const stagingRoot = path7.join(outputDir, `.${archiveBase}.staging`);
1848
1972
  const stagedWorkspace = path7.join(stagingRoot, safeName);
1973
+ if (stagedWorkspace === stagingRoot || !inside(stagedWorkspace, stagingRoot)) {
1974
+ throw new Error(
1975
+ `unsafe project name ${JSON.stringify(projectName2)} escapes the bundle staging directory`
1976
+ );
1977
+ }
1849
1978
  const agentRel = path7.relative(workspaceRoot, agentRoot2);
1850
1979
  const stagedAgent = path7.join(stagedWorkspace, agentRel);
1851
1980
  const archivePath = path7.join(outputDir, `${archiveBase}.tar.gz`);
@@ -1917,7 +2046,8 @@ function readProjectName(agentRoot2) {
1917
2046
  return String(name);
1918
2047
  }
1919
2048
  function excludedName(name) {
1920
- return EXCLUDED_NAMES.has(name) || name.startsWith(".env") || name.endsWith(".egg-info") || name.startsWith(".venv-");
2049
+ const lowerName = name.toLowerCase();
2050
+ return EXCLUDED_NAMES.has(lowerName) || CREDENTIAL_FILE_NAMES.has(lowerName) || lowerName.startsWith(".env") || lowerName.endsWith(".egg-info") || lowerName.startsWith(".venv-");
1921
2051
  }
1922
2052
  function inside(child, parent) {
1923
2053
  const rel = path7.relative(parent, child);
@@ -1968,7 +2098,7 @@ function copyWorkspace(sourceRoot, destinationRoot, outputDir) {
1968
2098
  copy(source, destinationRoot);
1969
2099
  }
1970
2100
  function writeEnvExample(agentRoot2, stagedWorkspace) {
1971
- const sourceEnv = envLocalPath3(agentRoot2);
2101
+ const sourceEnv = envLocalPath4(agentRoot2);
1972
2102
  const targetDir = path7.join(stagedWorkspace, ".studio");
1973
2103
  const target = path7.join(targetDir, ".env.local.example");
1974
2104
  fs8.mkdirSync(targetDir, { recursive: true, mode: 448 });
@@ -1989,8 +2119,13 @@ function writeEnvExample(agentRoot2, stagedWorkspace) {
1989
2119
  ];
1990
2120
  }
1991
2121
  const stripped = lines.map((line) => {
1992
- const match = /^([A-Za-z_][A-Za-z0-9_]*)=/.exec(line);
1993
- return match ? `${match[1]}=` : line;
2122
+ const trimmed = line.trim();
2123
+ if (!trimmed) return line;
2124
+ const commented = /^\s*(?:#\s*)+(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/u.exec(line);
2125
+ if (commented) return `# ${commented[1]}=`;
2126
+ if (trimmed.startsWith("#")) return "# omitted source comment";
2127
+ const match = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
2128
+ return match ? `${match[1]}=` : "# omitted unrecognized dotenv entry";
1994
2129
  });
1995
2130
  fs8.writeFileSync(target, `${[...header, ...stripped].join("\n")}
1996
2131
  `, {
@@ -2004,6 +2139,7 @@ function findStagedLeak(stagedWorkspace) {
2004
2139
  const full = path7.join(dir, name);
2005
2140
  const rel = path7.relative(root, full);
2006
2141
  const stat = fs8.lstatSync(full);
2142
+ const lowerName = name.toLowerCase();
2007
2143
  if (stat.isSymbolicLink()) {
2008
2144
  const link = fs8.readlinkSync(full);
2009
2145
  const target = path7.resolve(path7.dirname(full), link);
@@ -2013,15 +2149,29 @@ function findStagedLeak(stagedWorkspace) {
2013
2149
  continue;
2014
2150
  }
2015
2151
  if (stat.isDirectory()) {
2016
- if (name === "wallets" && path7.basename(path7.dirname(full)) === ".studio") {
2152
+ if (lowerName === "wallets" && path7.basename(path7.dirname(full)).toLowerCase() === ".studio") {
2017
2153
  return `wallet directory ${rel}`;
2018
2154
  }
2019
2155
  const nested = walk(full);
2020
2156
  if (nested !== null) return nested;
2021
2157
  continue;
2022
2158
  }
2023
- if (stat.isFile() && name.startsWith(".env") && name !== ".env.local.example") {
2024
- return `env file ${rel}`;
2159
+ if (stat.isFile() && CREDENTIAL_FILE_NAMES.has(lowerName)) {
2160
+ return `credential file ${rel}`;
2161
+ }
2162
+ if (stat.isFile() && lowerName.startsWith(".env")) {
2163
+ if (lowerName !== ".env.local.example") return `env file ${rel}`;
2164
+ const safeFixedLines = /* @__PURE__ */ new Set([
2165
+ "# Copy to .studio/.env.local and fill in values.",
2166
+ "# Generated by `bag bundle`; secret values were stripped.",
2167
+ "# omitted source comment",
2168
+ "# omitted unrecognized dotenv entry"
2169
+ ]);
2170
+ const unsafeExample = fs8.readFileSync(full, "utf-8").split(/\r?\n/u).some((line) => {
2171
+ if (!line.trim() || safeFixedLines.has(line)) return false;
2172
+ return !/^#?\s*[A-Za-z_][A-Za-z0-9_]*=$/u.test(line);
2173
+ });
2174
+ if (unsafeExample) return `non-empty env example ${rel}`;
2025
2175
  }
2026
2176
  }
2027
2177
  return null;
@@ -2064,6 +2214,7 @@ function writeInstallMd(stagedWorkspace, projectName2) {
2064
2214
  "",
2065
2215
  "- `.studio/.env.local`",
2066
2216
  "- `.studio/wallets/` and TWAK/Altana custody state",
2217
+ "- project-level credential files such as `.npmrc`",
2067
2218
  "- `node_modules/`, build outputs, caches, and repository history",
2068
2219
  ""
2069
2220
  ];
@@ -2104,77 +2255,6 @@ function devPortInUse(port = 9e3, timeoutMs = 250) {
2104
2255
  });
2105
2256
  }
2106
2257
 
2107
- // src/cli/_erc8183Config.ts
2108
- var ERC8183_ADDRESS_OVERRIDE_KEYS = [
2109
- "ERC8183_COMMERCE_ADDRESS",
2110
- "ERC8183_ROUTER_ADDRESS",
2111
- "ERC8183_POLICY_ADDRESS"
2112
- ];
2113
- var MAX_UINT256 = 2n ** 256n - 1n;
2114
- function erc8183PricingState(pay) {
2115
- const priceRaw = pay.price;
2116
- const price = String(priceRaw ?? "").trim();
2117
- if (!price) return { kind: "unset" };
2118
- if (typeof priceRaw !== "string") {
2119
- return { kind: "invalid", field: "price", value: price };
2120
- }
2121
- const minRaw = pay.min_price;
2122
- const maxRaw = pay.max_price;
2123
- const minPrice = String(minRaw ?? "").trim() || "0";
2124
- const maxPrice = String(maxRaw ?? "").trim();
2125
- if (minRaw !== void 0 && typeof minRaw !== "string") {
2126
- return { kind: "invalid", field: "min_price", value: minPrice };
2127
- }
2128
- if (maxRaw !== void 0 && typeof maxRaw !== "string") {
2129
- return { kind: "invalid", field: "max_price", value: maxPrice };
2130
- }
2131
- for (const [field, value] of [
2132
- ["price", price],
2133
- ["min_price", minPrice],
2134
- ["max_price", maxPrice]
2135
- ]) {
2136
- if ((field !== "max_price" || value !== "") && !/^\d+$/.test(value)) {
2137
- return { kind: "invalid", field, value };
2138
- }
2139
- }
2140
- const list = BigInt(price);
2141
- const min = BigInt(minPrice);
2142
- const max = maxPrice ? BigInt(maxPrice) : MAX_UINT256;
2143
- if (list > MAX_UINT256 || min > MAX_UINT256 || max > MAX_UINT256) {
2144
- const [field, value] = list > MAX_UINT256 ? ["price", price] : min > MAX_UINT256 ? ["min_price", minPrice] : ["max_price", maxPrice];
2145
- return { kind: "invalid", field, value };
2146
- }
2147
- const clampedToMax = list < max ? list : max;
2148
- const effective = min > clampedToMax ? min : clampedToMax;
2149
- const base = {
2150
- listPrice: list,
2151
- minPrice: min,
2152
- maxPrice: max,
2153
- effectivePrice: effective
2154
- };
2155
- if (list > 0n && effective === 0n) {
2156
- return { kind: "clamped_to_zero", ...base };
2157
- }
2158
- return { kind: effective === 0n ? "free" : "paid", ...base };
2159
- }
2160
- function erc8183ContractOverrideState(env = process.env) {
2161
- const present = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
2162
- (key) => Boolean(env[key]?.trim())
2163
- );
2164
- const missing = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
2165
- (key) => !env[key]?.trim()
2166
- );
2167
- const invalid = present.filter(
2168
- (key) => !/^0x[0-9a-fA-F]{40}$/.test(env[key]?.trim() ?? "")
2169
- );
2170
- return {
2171
- mode: present.length === 0 ? "canonical" : invalid.length > 0 ? "invalid" : missing.length === 0 ? "custom" : "partial",
2172
- present: [...present],
2173
- missing: [...missing],
2174
- invalid
2175
- };
2176
- }
2177
-
2178
2258
  // src/cli/config.ts
2179
2259
  var MAX_QUOTE_TTL_SECONDS = NegotiationHandler.MAX_QUOTE_TTL_SECONDS;
2180
2260
  function registerConfig(program) {
@@ -2484,7 +2564,9 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
2484
2564
  }
2485
2565
  if (pricing.kind === "free") {
2486
2566
  printOut("pricing: FREE \u2014 buyers fund 0 token units; zero token escrow.");
2487
- const contracts = erc8183ContractOverrideState();
2567
+ const contracts = erc8183ContractOverrideState(
2568
+ erc8183ContractEnvForProject(root)
2569
+ );
2488
2570
  if (contracts.mode === "custom") {
2489
2571
  printOut(
2490
2572
  "ERC-8183 contracts: custom contract stack selected with all three address overrides."
@@ -2498,8 +2580,8 @@ async function cmdSet(key, rawValue, typeFlag, projectRoot) {
2498
2580
  `warning: invalid ERC-8183 address override(s): ${contracts.invalid.join(", ")}.`
2499
2581
  );
2500
2582
  } else {
2501
- printErr(
2502
- "warning: FREE requires a zero-price-compatible contract stack; set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together before deploy."
2583
+ printOut(
2584
+ "ERC-8183 contracts: canonical contract stack selected; zero-price funding is supported."
2503
2585
  );
2504
2586
  }
2505
2587
  } else if (pricing.kind === "clamped_to_zero") {
@@ -2554,7 +2636,7 @@ function cmdListKeys(projectRoot) {
2554
2636
  import * as fs38 from "fs";
2555
2637
  import * as path38 from "path";
2556
2638
  import {
2557
- envLocalPath as envLocalPath16,
2639
+ envLocalPath as envLocalPath17,
2558
2640
  findStudioWorkspaceRoot as findStudioWorkspaceRoot3,
2559
2641
  findSubProjectRoot as findSubProjectRoot16,
2560
2642
  loadStudioToml as loadStudioToml20
@@ -2566,7 +2648,7 @@ import { Option as Option4 } from "commander";
2566
2648
  import * as fs10 from "fs";
2567
2649
  import * as path9 from "path";
2568
2650
  import {
2569
- envLocalPath as envLocalPath4,
2651
+ envLocalPath as envLocalPath5,
2570
2652
  findWorkspaceRoot as findWorkspaceRoot3,
2571
2653
  loadStudioToml as loadStudioToml3
2572
2654
  } from "@bnbagent/studio-runtime/config";
@@ -2649,10 +2731,10 @@ function oauthFacts(workspaceRoot, agentRoot2) {
2649
2731
  let tokenUrl = envValue(env, "OAUTH_TOKEN_URL");
2650
2732
  let scope = envValue(env, "OAUTH_SCOPE");
2651
2733
  if (!tokenUrl) {
2652
- tokenUrl = dotenvValue(envLocalPath4(agentRoot2), "OAUTH_TOKEN_URL");
2734
+ tokenUrl = dotenvValue(envLocalPath5(agentRoot2), "OAUTH_TOKEN_URL");
2653
2735
  }
2654
2736
  if (!scope) {
2655
- scope = dotenvValue(envLocalPath4(agentRoot2), "OAUTH_SCOPE");
2737
+ scope = dotenvValue(envLocalPath5(agentRoot2), "OAUTH_SCOPE");
2656
2738
  }
2657
2739
  return tokenUrl && scope ? [tokenUrl, scope] : null;
2658
2740
  }
@@ -2687,7 +2769,7 @@ function accessSummaryForAgentcore(agentRoot2, opts = {}) {
2687
2769
 
2688
2770
  // src/cli/_deploy/checks/storage.ts
2689
2771
  import {
2690
- envLocalPath as envLocalPath5,
2772
+ envLocalPath as envLocalPath6,
2691
2773
  findSubProjectRoot as findSubProjectRoot4
2692
2774
  } from "@bnbagent/studio-runtime/config";
2693
2775
 
@@ -2945,7 +3027,7 @@ function agentStorageKind(root) {
2945
3027
  }
2946
3028
  function runtimeEnvResolvable(root, key) {
2947
3029
  const agentRoot2 = findSubProjectRoot4("agent", root) ?? root;
2948
- return Boolean(process.env[key] || getEnvVar(envLocalPath5(agentRoot2), key));
3030
+ return Boolean(process.env[key] || getEnvVar(envLocalPath6(agentRoot2), key));
2949
3031
  }
2950
3032
  function storageLocalNotDeployableCheck(root) {
2951
3033
  if (agentStorageKind(root) !== "local") {
@@ -2996,13 +3078,13 @@ function hasIpfsEndpointFinding(result) {
2996
3078
  // src/cli/_deploy/cognitoCdk.ts
2997
3079
  import * as fs28 from "fs";
2998
3080
  import * as path27 from "path";
2999
- import { envLocalPath as envLocalPath13 } from "@bnbagent/studio-runtime/config";
3081
+ import { envLocalPath as envLocalPath14 } from "@bnbagent/studio-runtime/config";
3000
3082
 
3001
3083
  // src/cli/_deploy/secrets.ts
3002
3084
  import * as fs27 from "fs";
3003
3085
  import * as path26 from "path";
3004
3086
  import {
3005
- envLocalPath as envLocalPath12,
3087
+ envLocalPath as envLocalPath13,
3006
3088
  findSubProjectRoot as findSubProjectRoot8,
3007
3089
  loadStudioToml as loadStudioToml13
3008
3090
  } from "@bnbagent/studio-runtime/config";
@@ -3021,7 +3103,7 @@ import { Option as Option3 } from "commander";
3021
3103
  import * as fs12 from "fs";
3022
3104
  import * as path11 from "path";
3023
3105
  import {
3024
- envLocalPath as envLocalPath6,
3106
+ envLocalPath as envLocalPath7,
3025
3107
  findSubProjectRoot as findSubProjectRoot5,
3026
3108
  loadStudioToml as loadStudioToml5
3027
3109
  } from "@bnbagent/studio-runtime/config";
@@ -3054,7 +3136,7 @@ function keystoreJsonFiles(dir) {
3054
3136
  }
3055
3137
  function migrateEnvLocal(start) {
3056
3138
  try {
3057
- const target = envLocalPath6(start);
3139
+ const target = envLocalPath7(start);
3058
3140
  const ws = path11.dirname(path11.dirname(target));
3059
3141
  const agentRoot2 = findSubProjectRoot5("agent", start);
3060
3142
  const sources = [];
@@ -3120,7 +3202,7 @@ function migrateKeystoreOutOfCodelocation(start) {
3120
3202
  if (agentRoot2 === null) {
3121
3203
  return;
3122
3204
  }
3123
- const ws = path11.dirname(path11.dirname(envLocalPath6(start)));
3205
+ const ws = path11.dirname(path11.dirname(envLocalPath7(start)));
3124
3206
  const target = path11.join(ws, ".studio", "wallets");
3125
3207
  const stash = path11.join(ws, ".bag-keystore-stash");
3126
3208
  if (isDir2(stash)) {
@@ -3469,6 +3551,26 @@ var EXPIRY_WARN_SECONDS = 7 * 24 * 60 * 60;
3469
3551
  function isAltana(data) {
3470
3552
  return tableOf2(data, "wallet").kind === "altana";
3471
3553
  }
3554
+ function checkAltanaCustomContractsUnsupported(root, _target) {
3555
+ const data = loadAgentToml(root);
3556
+ if (!isAltana(data)) return [];
3557
+ if (Object.keys(tableOf2(tableOf2(data, "payments"), "erc8183")).length === 0) {
3558
+ return [];
3559
+ }
3560
+ const agentRoot2 = agentRootOf(root);
3561
+ const contracts = erc8183ContractOverrideState(
3562
+ erc8183ContractEnvForProject(agentRoot2)
3563
+ );
3564
+ if (contracts.mode !== "custom") return [];
3565
+ return [
3566
+ {
3567
+ level: Level.CRITICAL,
3568
+ name: "altana_custom_contracts_unsupported",
3569
+ message: "wallet.kind='altana' cannot use custom ERC-8183 targets: its bounded session permissions and quote-checker approval are tied to the canonical Commerce stack. Remove the ERC8183_*_ADDRESS overrides, or use wallet.kind='evm-local' for this custom stack.",
3570
+ details: { override_keys: contracts.present }
3571
+ }
3572
+ ];
3573
+ }
3472
3574
  function isFile4(p) {
3473
3575
  try {
3474
3576
  return fs15.statSync(p).isFile();
@@ -3632,7 +3734,7 @@ function checkAltanaSessionNotInsideAgent(root, _target) {
3632
3734
  // src/cli/_deploy/checks/twak.ts
3633
3735
  import * as fs16 from "fs";
3634
3736
  import * as path15 from "path";
3635
- import { envLocalPath as envLocalPath7 } from "@bnbagent/studio-runtime/config";
3737
+ import { envLocalPath as envLocalPath8 } from "@bnbagent/studio-runtime/config";
3636
3738
  import { resolveTwakHome } from "@bnbagent/studio-runtime/wallet";
3637
3739
 
3638
3740
  // src/cli/_twakContractTargets.ts
@@ -3784,7 +3886,10 @@ function checkTwakCustomContractsUnsupported(root, _target) {
3784
3886
  return [];
3785
3887
  }
3786
3888
  const networkName = String(tableOf2(data, "network").default ?? "bsc-testnet");
3787
- const overrides = twakUnsupportedContractOverrides(networkName);
3889
+ const overrides = twakUnsupportedContractOverrides(
3890
+ networkName,
3891
+ erc8183ContractEnvForProject(agentRootOf(root))
3892
+ );
3788
3893
  if (overrides.length === 0) {
3789
3894
  return [];
3790
3895
  }
@@ -3884,7 +3989,7 @@ async function checkTwakPasswordEnvSet(root, _target) {
3884
3989
  return [];
3885
3990
  }
3886
3991
  const agentRoot2 = agentRootOf(root);
3887
- if (process.env.TWAK_WALLET_PASSWORD || getEnvVar(envLocalPath7(agentRoot2), "TWAK_WALLET_PASSWORD")) {
3992
+ if (process.env.TWAK_WALLET_PASSWORD || getEnvVar(envLocalPath8(agentRoot2), "TWAK_WALLET_PASSWORD")) {
3888
3993
  return [];
3889
3994
  }
3890
3995
  return [
@@ -3935,7 +4040,7 @@ async function checkTwakCredentialsAvailable(root, _target) {
3935
4040
  path15.dirname(twakWalletFile(walletCfg, agentRoot2)),
3936
4041
  "credentials.json"
3937
4042
  );
3938
- const envLocal = envLocalPath7(agentRoot2);
4043
+ const envLocal = envLocalPath8(agentRoot2);
3939
4044
  const resolvable = (key) => Boolean(process.env[key] || getEnvVar(envLocal, key));
3940
4045
  if (isFile5(credentialsFile)) {
3941
4046
  return [];
@@ -3956,9 +4061,9 @@ async function checkTwakCredentialsAvailable(root, _target) {
3956
4061
  // src/cli/_deploy/fixes.ts
3957
4062
  import * as fs17 from "fs";
3958
4063
  import * as path16 from "path";
3959
- import { envLocalPath as envLocalPath8 } from "@bnbagent/studio-runtime/config";
4064
+ import { envLocalPath as envLocalPath9 } from "@bnbagent/studio-runtime/config";
3960
4065
  function fixGitignore(root) {
3961
- const ws = path16.dirname(path16.dirname(envLocalPath8(root)));
4066
+ const ws = path16.dirname(path16.dirname(envLocalPath9(root)));
3962
4067
  const gi = path16.join(ws, ".gitignore");
3963
4068
  const required = [".studio/"];
3964
4069
  let exists = false;
@@ -4432,8 +4537,10 @@ var azureFoundryChecks = [
4432
4537
  checkAccountEqualsSubdomain,
4433
4538
  checkEntrypointAndDockerfile,
4434
4539
  checkLlmExternalProviderReady,
4540
+ checkTwakCustomContractsUnsupported,
4435
4541
  checkTwakPasswordEnvSet,
4436
4542
  checkTwakCredentialsAvailable,
4543
+ checkAltanaCustomContractsUnsupported,
4437
4544
  checkAltanaSessionReady,
4438
4545
  checkAltanaSdkResolvable,
4439
4546
  checkAltanaSessionNotInsideAgent
@@ -4444,7 +4551,7 @@ import * as fs19 from "fs";
4444
4551
  import * as path18 from "path";
4445
4552
  import { query as auditQuery, auditedOp } from "@bnbagent/studio-runtime/audit";
4446
4553
  import {
4447
- envLocalPath as envLocalPath9,
4554
+ envLocalPath as envLocalPath10,
4448
4555
  findProjectRoot as findProjectRoot2,
4449
4556
  loadStudioToml as loadStudioToml8
4450
4557
  } from "@bnbagent/studio-runtime/config";
@@ -4533,7 +4640,17 @@ function printUBalanceShortfall(walletAddress, currentRaw, neededRaw, neededUsd,
4533
4640
  function writeEnvLocal(key, value, envPath = ".env.local") {
4534
4641
  return setEnvVar(envPath, key, value);
4535
4642
  }
4643
+ function ensureSiweCapable(wallet) {
4644
+ const probe = wallet.supports;
4645
+ if (typeof probe !== "function") return;
4646
+ if (probe.call(wallet, "sign.message")) return;
4647
+ const kind = wallet.kind;
4648
+ throw new PieverseCliError(
4649
+ `the ${kind ? `'${kind}' ` : ""}wallet cannot log in to Pieverse: SIWE requires an EIP-191 personal-sign ('sign.message'), which this wallet kind never supports. Pieverse LLM is not usable with this wallet \u2014 set [llm].provider in studio.toml to 'openrouter', 'openai', or 'anthropic' (with the matching API key in .studio/.env.local), or use an 'evm-local' or 'twak' wallet.`
4650
+ );
4651
+ }
4536
4652
  async function withSiweRetry(wallet, call, opts = {}) {
4653
+ ensureSiweCapable(wallet);
4537
4654
  let token = opts.sessionToken ?? await siweLogin(wallet);
4538
4655
  try {
4539
4656
  return await call(token);
@@ -4801,10 +4918,19 @@ async function cmdActivate(opts) {
4801
4918
  if (wallet === null) {
4802
4919
  return 2;
4803
4920
  }
4921
+ try {
4922
+ ensureSiweCapable(wallet);
4923
+ } catch (exc) {
4924
+ if (exc instanceof PieverseCliError) {
4925
+ printErr(`error: ${exc.message}`);
4926
+ return 2;
4927
+ }
4928
+ throw exc;
4929
+ }
4804
4930
  const pvCfg = loadPieverseConfig(cfg);
4805
4931
  const replace = opts.replace === true;
4806
4932
  let existingHash = !replace && pvCfg.key_hash ? String(pvCfg.key_hash) : null;
4807
- const envFileValue = replace ? null : getEnvVar(envLocalPath9(root), PIEVERSE_ENV_KEY);
4933
+ const envFileValue = replace ? null : getEnvVar(envLocalPath10(root), PIEVERSE_ENV_KEY);
4808
4934
  const existingEnv = replace ? null : envFileValue || process.env[PIEVERSE_ENV_KEY];
4809
4935
  if (replace) {
4810
4936
  printOut(
@@ -4824,6 +4950,10 @@ async function cmdActivate(opts) {
4824
4950
  try {
4825
4951
  await withSiweRetry(wallet, (token) => inspectKey(token, derivedHash));
4826
4952
  } catch (exc) {
4953
+ if (exc instanceof PieverseCliError) {
4954
+ printErr(`error: ${exc.message}`);
4955
+ return 2;
4956
+ }
4827
4957
  if (!(exc instanceof PieverseKeyNotFoundError)) {
4828
4958
  printErr(
4829
4959
  `error: could not verify the existing ${PIEVERSE_ENV_KEY} against Pieverse: ${errMessage(
@@ -5003,7 +5133,7 @@ Retry: re-run \`bag llm activate\` \u2014 no state was persisted yet.`
5003
5133
  }
5004
5134
  const key = String(result.key);
5005
5135
  const keyHash = String(result.hash);
5006
- writeEnvLocal(PIEVERSE_ENV_KEY, key, envLocalPath9(root));
5136
+ writeEnvLocal(PIEVERSE_ENV_KEY, key, envLocalPath10(root));
5007
5137
  updateStudioTomlSection(root, "llm.pieverse", {
5008
5138
  key_hash: keyHash,
5009
5139
  network: networkName
@@ -5534,7 +5664,7 @@ async function cmdRotate(opts) {
5534
5664
  `warning: could not auto-disable old key \u2014 manually disable ${oldHash} in the Pieverse dashboard.`
5535
5665
  );
5536
5666
  }
5537
- writeEnvLocal(PIEVERSE_ENV_KEY, newKey, envLocalPath9(root));
5667
+ writeEnvLocal(PIEVERSE_ENV_KEY, newKey, envLocalPath10(root));
5538
5668
  updateStudioTomlSection(root, "llm.pieverse", {
5539
5669
  key_hash: newHash,
5540
5670
  network: networkName
@@ -6289,7 +6419,7 @@ import * as os3 from "os";
6289
6419
  import * as path22 from "path";
6290
6420
  import { EVMWalletProvider, SigningPolicy } from "@bnbagent/sdk";
6291
6421
  import {
6292
- envLocalPath as envLocalPath10,
6422
+ envLocalPath as envLocalPath11,
6293
6423
  findProjectRoot as findProjectRoot3,
6294
6424
  loadStudioToml as loadStudioToml10
6295
6425
  } from "@bnbagent/studio-runtime/config";
@@ -6600,8 +6730,8 @@ async function cmdSessionGrant(opts) {
6600
6730
  printOut(granted.publicKey);
6601
6731
  return 0;
6602
6732
  }
6603
- function cmdSessionStatus(projectRootArg) {
6604
- const projectRoot = resolveProjectRootArg(projectRootArg);
6733
+ function cmdSessionStatus(projectRootArg2) {
6734
+ const projectRoot = resolveProjectRootArg(projectRootArg2);
6605
6735
  if (projectRoot === null) return 2;
6606
6736
  const ctx = loadContext(projectRoot);
6607
6737
  const { envelope } = readSession(ctx);
@@ -6834,7 +6964,7 @@ function requirePassword2() {
6834
6964
  }
6835
6965
  function requireDurablePassword(projectRoot) {
6836
6966
  const password = requirePassword2();
6837
- const envPath = envLocalPath10(projectRoot);
6967
+ const envPath = envLocalPath11(projectRoot);
6838
6968
  const persisted = getEnvVar(envPath, PASSWORD_ENV2);
6839
6969
  if (!persisted) {
6840
6970
  throw new Error(
@@ -7207,8 +7337,8 @@ ${detail}` : `error: \`twak wallet create --no-keychain\` did not produce ${wall
7207
7337
  );
7208
7338
  return cmdNewTwak(projectRoot, walletCfg);
7209
7339
  }
7210
- function cmdShow5(projectRootArg) {
7211
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7340
+ function cmdShow5(projectRootArg2) {
7341
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7212
7342
  const walletCfg = loadWalletCfg(projectRoot);
7213
7343
  const kind = walletKind(walletCfg);
7214
7344
  if (kind === "twak") {
@@ -7269,8 +7399,8 @@ function cmdShow5(projectRootArg) {
7269
7399
  }
7270
7400
  return 0;
7271
7401
  }
7272
- function cmdList2(projectRootArg) {
7273
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7402
+ function cmdList2(projectRootArg2) {
7403
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7274
7404
  const walletCfg = loadWalletCfg(projectRoot);
7275
7405
  if (walletKind(walletCfg) === "twak") {
7276
7406
  printOut(twakProvider(projectRoot, walletCfg).address);
@@ -7311,8 +7441,8 @@ function cmdList2(projectRootArg) {
7311
7441
  }
7312
7442
  return 0;
7313
7443
  }
7314
- async function cmdSign(msg, projectRootArg) {
7315
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7444
+ async function cmdSign(msg, projectRootArg2) {
7445
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7316
7446
  const walletCfg = loadWalletCfg(projectRoot);
7317
7447
  const kind = walletKind(walletCfg);
7318
7448
  if (kind === "altana") {
@@ -7405,8 +7535,8 @@ async function cmdBalance(opts) {
7405
7535
  }
7406
7536
  return 0;
7407
7537
  }
7408
- function cmdPolicyShow(asJson, projectRootArg) {
7409
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7538
+ function cmdPolicyShow(asJson, projectRootArg2) {
7539
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7410
7540
  const walletCfg = loadWalletCfg(projectRoot);
7411
7541
  const kind = walletKind(walletCfg);
7412
7542
  if (kind === "twak") {
@@ -7729,7 +7859,7 @@ function registerInit(program) {
7729
7859
  ).addOption(
7730
7860
  new Option2(
7731
7861
  "--wallet-kind <kind>",
7732
- "Wallet backend for the Agent (default: evm-local = encrypted local keystore at the workspace root .studio/wallets/; CodeZip deploy). altana \u2014 encrypted local admin keystore plus a bounded runtime session (local dev; deployment deferred). twak \u2014 FULLY SUPPORTED, opt in: Trust Wallet Agent Kit CLI, self-custody encrypted mnemonic in a PROJECT-DEDICATED home (.studio/twak); deploys as a container image."
7862
+ "Wallet backend for the Agent (default: evm-local = encrypted local keystore at the workspace root .studio/wallets/; CodeZip deploy). altana \u2014 encrypted local admin keystore plus a bounded runtime session; deploys ship ONLY the session (ALTANA_SESSION secret). twak \u2014 FULLY SUPPORTED, opt in: Trust Wallet Agent Kit CLI, self-custody encrypted mnemonic in a PROJECT-DEDICATED home (.studio/twak); deploys as a container image."
7733
7863
  ).choices(["twak", "evm-local", "altana"])
7734
7864
  ).option(
7735
7865
  "--twak-home <path>",
@@ -7829,6 +7959,9 @@ async function cmdInit(nameArg, opts) {
7829
7959
  opts.llmProviderSource,
7830
7960
  isTty
7831
7961
  );
7962
+ if (llmProvider === null) {
7963
+ return 2;
7964
+ }
7832
7965
  let rails = await resolveRails(opts.rails, isTty);
7833
7966
  if (hasX402Face(faces) && rails === "8183") {
7834
7967
  rails = "both";
@@ -8102,7 +8235,7 @@ async function cmdInit(nameArg, opts) {
8102
8235
  });
8103
8236
  }
8104
8237
  if (destination === "platform") {
8105
- printPlatformWalletSuggestion();
8238
+ printPlatformWalletSuggestion(walletKind2);
8106
8239
  }
8107
8240
  if (installFailed) {
8108
8241
  printErr(
@@ -8208,7 +8341,7 @@ function scaffold(target, name, o) {
8208
8341
  fs24.writeFileSync(path23.join(agentRoot2, ".gitignore"), renderAgentGitignore());
8209
8342
  fs24.writeFileSync(
8210
8343
  path23.join(agentRoot2, "README.md"),
8211
- renderAgentReadme(name, o.faces, o.destination)
8344
+ renderAgentReadme(name, o.faces, o.destination, o.walletKind)
8212
8345
  );
8213
8346
  const packaging = derivePackaging(
8214
8347
  o.walletKind,
@@ -8594,8 +8727,7 @@ function renderAgentStudioToml(name, o) {
8594
8727
  [payments.erc8183]
8595
8728
  # Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
8596
8729
  # freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
8597
- # LLM never prices. price=0 is FREE and needs a zero-price-compatible
8598
- # ERC-8183 stack selected with all three ERC8183_*_ADDRESS overrides.
8730
+ # LLM never prices. price=0 is FREE and is supported by the canonical stack.
8599
8731
  currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default
8600
8732
  price = "${o.erc8183Price}" # token base units; 0 = FREE (zero token escrow)
8601
8733
  min_price = "0" # wei \u2014 clamp floor
@@ -8722,7 +8854,6 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402
8722
8854
  "",
8723
8855
  "# Optional ERC-8183 custom contract-stack override. Set all three",
8724
8856
  "# together; partial overrides can mix incompatible deployments.",
8725
- "# Required for price=0 while canonical contracts reject zero funding.",
8726
8857
  "# ERC8183_COMMERCE_ADDRESS=",
8727
8858
  "# ERC8183_ROUTER_ADDRESS=",
8728
8859
  "# ERC8183_POLICY_ADDRESS="
@@ -8731,7 +8862,7 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402
8731
8862
  return `${lines.join("\n")}
8732
8863
  `;
8733
8864
  }
8734
- function renderAgentReadme(name, faces, destination) {
8865
+ function renderAgentReadme(name, faces, destination, walletKind2) {
8735
8866
  const proto = faces.join(" + ");
8736
8867
  const mode = recipeModeOf(faces);
8737
8868
  const entry = `src/${entryStemOf(faces)}.ts`;
@@ -8770,8 +8901,7 @@ entrypoint code in \`src/signing.ts\` \u2014 never an LLM-callable tool.
8770
8901
  ${mode === "both" ? filesBoth : mode === "mcp" ? filesMcp : filesA2a}
8771
8902
  ${shared}
8772
8903
  - \`.env.local\` \u2014 Agent secrets; on deploy they are sent to the **operator's**
8773
- Secrets Manager (the scoped, consented commitment-#2 exception). Use a
8774
- THROWAWAY testnet wallet \u2014 \`(cd app/agent && bag wallet new)\`.
8904
+ Secrets Manager (the scoped, consented commitment-#2 exception). ${walletKind2 === "altana" ? "Only the\n bounded session ships (as `ALTANA_SESSION`); tighten it with\n `bag wallet session grant --force` \u2014 do not create a new wallet." : "Use a\n THROWAWAY testnet wallet \u2014 `(cd app/agent && bag wallet new)`."}
8775
8905
 
8776
8906
  ## Run locally
8777
8907
 
@@ -8902,8 +9032,9 @@ async function resolveLlmProviderForWallet(walletKind2, configuredProvider, valu
8902
9032
  if (explicitlyConfigured) {
8903
9033
  if (configuredProvider === "pieverse-llm") {
8904
9034
  printErr(
8905
- "warning: Altana + pieverse-llm has limited compatibility: `bag llm activate` and paid-model runtime credit renewal both require SIWE message signing, which Altana intentionally does not expose. An existing Pieverse API key is usable only with the free `auto/free` model; use openrouter, openai, or anthropic for paid models."
9035
+ "error: --wallet-kind altana cannot be combined with --llm-provider pieverse-llm: `bag llm activate` and paid-model runtime credit renewal both require SIWE message signing, which Altana intentionally does not expose \u2014 activation can never succeed. Use --llm-provider openrouter, openai, or anthropic (each needs only an API key)."
8906
9036
  );
9037
+ return null;
8907
9038
  }
8908
9039
  return configuredProvider;
8909
9040
  }
@@ -9162,8 +9293,8 @@ async function runOnboarding(o) {
9162
9293
  return false;
9163
9294
  }
9164
9295
  const agentRoot2 = path23.join(o.workspaceRoot, APP_DIR, AGENT_PKG);
9165
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
9166
- setEnvVar(envLocalPath19(agentRoot2), "WALLET_PASSWORD", password);
9296
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
9297
+ setEnvVar(envLocalPath20(agentRoot2), "WALLET_PASSWORD", password);
9167
9298
  const savedCwd = process.cwd();
9168
9299
  const savedPw = process.env.WALLET_PASSWORD;
9169
9300
  let address = null;
@@ -9202,7 +9333,7 @@ async function runOnboarding(o) {
9202
9333
  }
9203
9334
  }
9204
9335
  if (o.storageProvider !== "local") {
9205
- await onboardIpfsKey(envLocalPath19(agentRoot2), o.ipfsKey);
9336
+ await onboardIpfsKey(envLocalPath20(agentRoot2), o.ipfsKey);
9206
9337
  }
9207
9338
  if (address) {
9208
9339
  await printFaucetHint(o.network, address);
@@ -9336,8 +9467,8 @@ Guide: the bnbagent-studio-using-twak-wallet.md reference (installed by \`bag sk
9336
9467
  process.chdir(savedCwd);
9337
9468
  }
9338
9469
  if (o.storageProvider !== "local") {
9339
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
9340
- await onboardIpfsKey(envLocalPath19(agentRoot2), o.ipfsKey);
9470
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
9471
+ await onboardIpfsKey(envLocalPath20(agentRoot2), o.ipfsKey);
9341
9472
  }
9342
9473
  if (address) {
9343
9474
  await printFaucetHint(o.network, address);
@@ -9518,7 +9649,22 @@ NOTE \u2014 wallet.kind = "altana" (bounded runtime session):
9518
9649
  \`bag wallet session grant --force\` and redeploy.`
9519
9650
  );
9520
9651
  }
9521
- function printPlatformWalletSuggestion() {
9652
+ function printPlatformWalletSuggestion(walletKind2) {
9653
+ if (walletKind2 === "altana") {
9654
+ printOut(
9655
+ `
9656
+ NOTE \u2014 destination = "platform" (48h TESTNET TRIAL on the BNB Chain managed platform):
9657
+ \xB7 This is a sandbox, not a production seller \u2014 the runtime is auto-reclaimed at 48h.
9658
+ \xB7 To sign, ONLY the bounded Altana session is transmitted to the operator's
9659
+ Secrets Manager on deploy \u2014 the admin keystore and WALLET_PASSWORD never
9660
+ ship. The session is budget-limited, expiring, and on-chain revocable.
9661
+ \xB7 Want a smaller blast radius? Re-grant a tighter session before deploying \u2014
9662
+ bag wallet session grant --force --budget-u <small> --expiry-days <short>
9663
+ (do NOT run \`bag wallet new\`: a fresh admin keystore breaks the session's
9664
+ [wallet].address anchor and deploy readiness fails).`
9665
+ );
9666
+ return;
9667
+ }
9522
9668
  printOut(
9523
9669
  `
9524
9670
  NOTE \u2014 destination = "platform" (48h TESTNET TRIAL on the BNB Chain managed platform):
@@ -9751,20 +9897,7 @@ function printNextSteps(name, provider, o = {}) {
9751
9897
  }
9752
9898
  printOut("");
9753
9899
  let nextStep;
9754
- if (provider === "pieverse-llm" && walletKind2 === "altana") {
9755
- printOut(
9756
- " # Pieverse `bag llm activate` needs generic SIWE message signing, which"
9757
- );
9758
- printOut(
9759
- " # Altana intentionally does not expose. Runtime paid-model credit renewal"
9760
- );
9761
- printOut(
9762
- " # needs the same SIWE flow, so an existing key works only with auto/free."
9763
- );
9764
- printOut(" # Use openrouter/openai/anthropic for paid models.");
9765
- printOut("");
9766
- nextStep = 3;
9767
- } else if (provider === "pieverse-llm") {
9900
+ if (provider === "pieverse-llm") {
9768
9901
  printOut(" # 3. activate Pieverse LLM (zero-deposit by default)");
9769
9902
  printOut(
9770
9903
  " # SIWE login + create API key with $0 allocation. Default model"
@@ -9887,7 +10020,9 @@ function printConfigSummary(agentRoot2) {
9887
10020
  const maxPrice = g(agent, "payments", "erc8183", "max_price");
9888
10021
  const pricingState = erc8183PricingState(erc8183);
9889
10022
  const isFreePrice = pricingState.kind === "free";
9890
- const contractOverrides = erc8183ContractOverrideState();
10023
+ const contractOverrides = erc8183ContractOverrideState(
10024
+ erc8183ContractEnvForProject(agentRoot2)
10025
+ );
9891
10026
  const storageKind2 = shown(g(agent, "storage", "kind"), "?");
9892
10027
  const storageDesc = storageKind2 === "ipfs" ? "IPFS (durable, public)" : "local disk (offline dev only)";
9893
10028
  const walletKind2 = shown(g(agent, "wallet", "kind"), "evm-local");
@@ -9987,7 +10122,7 @@ function printConfigSummary(agentRoot2) {
9987
10122
  ` pricing : FREE \u2014 ${weiToU(String(price))} per job; zero token escrow`
9988
10123
  );
9989
10124
  printOut(
9990
- contractOverrides.mode === "custom" ? " contracts: custom stack selected (all three address overrides)" : " contracts: configure all three ERC8183_*_ADDRESS overrides before deploy"
10125
+ contractOverrides.mode === "custom" ? " contracts: custom stack selected (all three address overrides)" : contractOverrides.mode === "canonical" ? " contracts: canonical contract stack selected (zero-price supported)" : " contracts: custom contract overrides are incomplete or invalid; run `bag doctor`"
9991
10126
  );
9992
10127
  } else {
9993
10128
  printOut(
@@ -10025,9 +10160,9 @@ function printConfigSummary(agentRoot2) {
10025
10160
  if (hasErc8183 && !maxPrice && !isFreePrice) {
10026
10161
  todo.push("[payments.erc8183].max_price \u2014 the price clamp ceiling");
10027
10162
  }
10028
- if (hasErc8183 && isFreePrice && contractOverrides.mode !== "custom") {
10163
+ if (hasErc8183 && isFreePrice && (contractOverrides.mode === "partial" || contractOverrides.mode === "invalid")) {
10029
10164
  todo.push(
10030
- "ERC8183_COMMERCE_ADDRESS / ROUTER_ADDRESS / POLICY_ADDRESS \u2014 select one zero-price-compatible custom contract stack"
10165
+ "ERC8183_COMMERCE_ADDRESS / ROUTER_ADDRESS / POLICY_ADDRESS \u2014 fix or remove the incomplete custom contract override"
10031
10166
  );
10032
10167
  }
10033
10168
  if (hasX402Seller && !isFreeX402) {
@@ -10293,6 +10428,17 @@ function loadEnvFileForDev(p) {
10293
10428
  if (!isFile10(p)) {
10294
10429
  return;
10295
10430
  }
10431
+ const contractStackOwnedByShell = ERC8183_ADDRESS_OVERRIDE_KEYS.some(
10432
+ (key) => process.env[key] !== void 0
10433
+ );
10434
+ const configuredContractKeys = ERC8183_ADDRESS_OVERRIDE_KEYS.filter(
10435
+ (key) => Boolean(process.env[key]?.trim())
10436
+ );
10437
+ if (configuredContractKeys.length > 0 && configuredContractKeys.length < ERC8183_ADDRESS_OVERRIDE_KEYS.length) {
10438
+ throw new Error(
10439
+ `ERC-8183 contract override is incomplete; set all of ${ERC8183_ADDRESS_OVERRIDE_KEYS.join(", ")} or unset all three.`
10440
+ );
10441
+ }
10296
10442
  for (const line of fs25.readFileSync(p, "utf-8").split(/\r?\n/)) {
10297
10443
  if (!line || line.trimStart().startsWith("#")) {
10298
10444
  continue;
@@ -10300,6 +10446,11 @@ function loadEnvFileForDev(p) {
10300
10446
  const m = KEY_LINE_RE.exec(line);
10301
10447
  if (m) {
10302
10448
  const key = m[1];
10449
+ if (contractStackOwnedByShell && ERC8183_ADDRESS_OVERRIDE_KEYS.includes(
10450
+ key
10451
+ )) {
10452
+ continue;
10453
+ }
10303
10454
  const eq = line.indexOf("=");
10304
10455
  const value = eq >= 0 ? line.slice(eq + 1) : "";
10305
10456
  if (value !== "") {
@@ -10538,8 +10689,8 @@ async function cmdDev(opts) {
10538
10689
  const agentDir = path24.join(workspaceRoot, "agent");
10539
10690
  migrateEnvLocal(agentDir);
10540
10691
  migrateKeystoreOutOfCodelocation(agentDir);
10541
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
10542
- loadEnvFileForDev(envLocalPath19(agentDir));
10692
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
10693
+ loadEnvFileForDev(envLocalPath20(agentDir));
10543
10694
  ensureStoragePath(workspaceRoot);
10544
10695
  const ipfsErr = ipfsPreflight(agentDir);
10545
10696
  if (ipfsErr !== null) {
@@ -10784,7 +10935,7 @@ function networkBanner(agentDir) {
10784
10935
  import * as fs26 from "fs";
10785
10936
  import { createRequire } from "module";
10786
10937
  import * as path25 from "path";
10787
- import { envLocalPath as envLocalPath11, loadStudioToml as loadStudioToml12 } from "@bnbagent/studio-runtime/config";
10938
+ import { envLocalPath as envLocalPath12, loadStudioToml as loadStudioToml12 } from "@bnbagent/studio-runtime/config";
10788
10939
  import { pieverseKeyHash as pieverseKeyHash2 } from "@bnbagent/studio-runtime/llm";
10789
10940
  var AGENTCORE_DESCRIPTOR2 = "agentcore/agentcore.json";
10790
10941
  function isFile11(p) {
@@ -10820,7 +10971,10 @@ function runtimeEnvKeys2(agentRoot2) {
10820
10971
  } catch {
10821
10972
  cfg = {};
10822
10973
  }
10823
- const resolvable = (key) => Boolean(process.env[key] || getEnvVar(envLocalPath11(agentRoot2), key));
10974
+ const contractEnv = erc8183ContractEnvForProject(agentRoot2);
10975
+ const resolvable = (key) => ERC8183_ADDRESS_OVERRIDE_KEYS.includes(
10976
+ key
10977
+ ) ? Boolean(contractEnv[key]) : Boolean(process.env[key] || getEnvVar(envLocalPath12(agentRoot2), key));
10824
10978
  return runtimeEnvKeysCore(cfg, resolvable);
10825
10979
  }
10826
10980
  var require2 = createRequire(import.meta.url);
@@ -11040,7 +11194,7 @@ function studioIgnored(lines) {
11040
11194
  }
11041
11195
  async function checkGitignoreExcludesSecrets(root, _target) {
11042
11196
  const out = [];
11043
- const ws = path25.dirname(path25.dirname(envLocalPath11(root)));
11197
+ const ws = path25.dirname(path25.dirname(envLocalPath12(root)));
11044
11198
  const gi = path25.join(ws, ".gitignore");
11045
11199
  const lines = gitignoreLines(gi);
11046
11200
  if (lines === null) {
@@ -11146,7 +11300,7 @@ async function checkPieverseKeyHash(root, _target) {
11146
11300
  ];
11147
11301
  }
11148
11302
  const agentRoot2 = agentRootOf(root);
11149
- const apiKey = getEnvVar(envLocalPath11(agentRoot2), "PIEVERSE_LLM_API_KEY") || process.env.PIEVERSE_LLM_API_KEY;
11303
+ const apiKey = getEnvVar(envLocalPath12(agentRoot2), "PIEVERSE_LLM_API_KEY") || process.env.PIEVERSE_LLM_API_KEY;
11150
11304
  if (!apiKey) return [];
11151
11305
  const configured = String(pvCfg.key_hash).trim().toLowerCase().replace(/^0x/u, "");
11152
11306
  const actual = pieverseKeyHash2(apiKey);
@@ -11163,10 +11317,10 @@ async function checkPieverseKeyHash(root, _target) {
11163
11317
  }
11164
11318
  ];
11165
11319
  }
11166
- function erc8183RailChecks(cfg) {
11320
+ function erc8183RailChecks(cfg, env = process.env) {
11167
11321
  const c2 = tableOf2(tableOf2(cfg, "payments"), "erc8183");
11168
11322
  const out = [];
11169
- const contracts = erc8183ContractOverrideState();
11323
+ const contracts = erc8183ContractOverrideState(env);
11170
11324
  if (contracts.mode === "partial") {
11171
11325
  out.push({
11172
11326
  level: Level.CRITICAL,
@@ -11218,19 +11372,13 @@ function erc8183RailChecks(cfg) {
11218
11372
  }
11219
11373
  });
11220
11374
  } else if (pricing.kind === "free") {
11221
- if (contracts.mode === "canonical") {
11222
- out.push({
11223
- level: Level.CRITICAL,
11224
- name: "commerce_zero_price_contract_unsupported",
11225
- message: "ERC-8183 pricing is FREE (zero token escrow), but the canonical contract stack rejects zero funding. Set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together to a zero-price-compatible custom stack.",
11226
- details: { effective_wei: "0", contract_profile: "canonical" }
11227
- });
11228
- } else if (contracts.mode === "custom") {
11375
+ if (contracts.mode === "canonical" || contracts.mode === "custom") {
11376
+ const contractProfile = contracts.mode;
11229
11377
  out.push({
11230
11378
  level: Level.INFO,
11231
11379
  name: "commerce_zero_price_enabled",
11232
- message: "ERC-8183 pricing is FREE \u2014 buyers fund 0 token units with zero token escrow; a complete custom contract stack is selected.",
11233
- details: { effective_wei: "0", contract_profile: "custom" }
11380
+ message: `ERC-8183 pricing is FREE \u2014 buyers fund 0 token units with zero token escrow; the ${contractProfile} contract stack is selected.`,
11381
+ details: { effective_wei: "0", contract_profile: contractProfile }
11234
11382
  });
11235
11383
  }
11236
11384
  }
@@ -11391,7 +11539,14 @@ async function checkCommerceReady(root, target) {
11391
11539
  const cfg = loadAgentToml(root);
11392
11540
  const rails = commerceRails(cfg);
11393
11541
  const out = [];
11394
- if (rails.erc8183) out.push(...erc8183RailChecks(cfg));
11542
+ if (rails.erc8183) {
11543
+ out.push(
11544
+ ...erc8183RailChecks(
11545
+ cfg,
11546
+ erc8183ContractEnvForProject(agentRootOf(root))
11547
+ )
11548
+ );
11549
+ }
11395
11550
  const x402Checks = x402SellerRailChecks(agentRootOf(root), cfg, target);
11396
11551
  if (!rails.erc8183 && rails.x402) {
11397
11552
  for (const check of x402Checks) {
@@ -11496,7 +11651,7 @@ async function checkRuntimeSecretsInjected(root, target) {
11496
11651
  }
11497
11652
  const missing = needed.filter((k) => !present[k]);
11498
11653
  if (missing.length > 0) {
11499
- const envLocal = envLocalPath11(root);
11654
+ const envLocal = envLocalPath12(root);
11500
11655
  const recoverable = missing.filter(
11501
11656
  (k) => Boolean(process.env[k] || getEnvVar(envLocal, k))
11502
11657
  );
@@ -11641,12 +11796,18 @@ async function checkAgentcoreQuotaHeadroom(root, target) {
11641
11796
  }
11642
11797
  ];
11643
11798
  }
11799
+ const quotaRunbook = `Raising ${AGENTCORE_AGENT_QUOTA_CODE} ("Total Agents per Account") is a manual AWS step \u2014 \`bag\` cannot request it for you:
11800
+ 1. Open the AWS Service Quotas console (service "Amazon Bedrock AgentCore", region ${region}) and request an increase; low requests may need an AWS support case, and a first request can be rejected.
11801
+ 2. Check where an earlier request stands: aws service-quotas list-requested-service-quota-change-history --service-code bedrock-agentcore --region ${region}
11802
+ 3. While the increase is pending, deploy to the managed platform instead: bag deploy --provider bnb`;
11803
+ const message = limit <= 0 ? `AgentCore quota is 0 in ${region} (new accounts can start at zero), so ANY deploy would fail with \`maxAgents limit exceeded\` AFTER pushing the image and creating the secret, M2M client, and execution role (all orphans). ${quotaRunbook}` : `AgentCore runtimes: ${used}/${limit} used in ${region} \u2014 no slots left, so the deploy would fail with \`maxAgents limit exceeded\` AFTER pushing the image and creating the secret, M2M client, and execution role (all orphans). Delete a runtime you no longer need (\`bag deploy destroy\` in its workspace), or raise the quota. ${quotaRunbook}`;
11644
11804
  return [
11645
11805
  {
11646
11806
  level: Level.CRITICAL,
11647
11807
  name: "agentcore_quota_headroom",
11648
- message: `AgentCore runtimes: ${used}/${limit} used in ${region} \u2014 no slots left, so the deploy would fail with \`maxAgents limit exceeded\` AFTER pushing the image and creating the secret, M2M client, and execution role (all orphans). Delete a runtime you no longer need (\`bag deploy destroy\` in its workspace) or request a quota increase for ${AGENTCORE_AGENT_QUOTA_CODE} ("Total Agents per Account") in the Service Quotas console.`,
11649
- details
11808
+ message,
11809
+ details,
11810
+ fixCmd: `# raise ${AGENTCORE_AGENT_QUOTA_CODE} in the AWS Service Quotas console (manual), or deploy with: bag deploy --provider bnb`
11650
11811
  }
11651
11812
  ];
11652
11813
  }
@@ -11668,6 +11829,7 @@ var allChecks = [
11668
11829
  checkTwakCredentialsAvailable,
11669
11830
  checkTwakDockerAvailable,
11670
11831
  // altana-kind checks (every one early-returns for other kinds).
11832
+ checkAltanaCustomContractsUnsupported,
11671
11833
  checkAltanaSessionReady,
11672
11834
  checkAltanaSdkResolvable,
11673
11835
  checkAltanaSessionNotInsideAgent,
@@ -11750,7 +11912,7 @@ function readKeystoreJson(agentRoot2) {
11750
11912
  function readEnvLocal(agentRoot2) {
11751
11913
  const values = {};
11752
11914
  try {
11753
- const file = envLocalPath12(agentRoot2);
11915
+ const file = envLocalPath13(agentRoot2);
11754
11916
  if (!isFile12(file)) {
11755
11917
  return values;
11756
11918
  }
@@ -11778,14 +11940,19 @@ function collectRuntimeSecretsDetailed(root) {
11778
11940
  const agentRoot2 = findSubProjectRoot8("agent", root) ?? root;
11779
11941
  const secretKeys = runtimeEnvKeys2(agentRoot2);
11780
11942
  const fileValues = readEnvLocal(agentRoot2);
11943
+ const contractEnv = erc8183ContractEnvForProject(agentRoot2);
11944
+ const contractSource = ERC8183_ADDRESS_OVERRIDE_KEYS.some((key) => process.env[key]?.trim()) ? "process.env" : ".studio/.env.local";
11781
11945
  const payload = {};
11782
11946
  const sources = {};
11783
11947
  for (const k of secretKeys) {
11784
11948
  const fromFile = fileValues[k];
11785
- const v = fromFile || process.env[k];
11949
+ const isContractOverride = ERC8183_ADDRESS_OVERRIDE_KEYS.includes(
11950
+ k
11951
+ );
11952
+ const v = isContractOverride ? contractEnv[k] : fromFile || process.env[k];
11786
11953
  if (v) {
11787
11954
  payload[k] = v;
11788
- sources[k] = fromFile ? ".studio/.env.local" : "process.env";
11955
+ sources[k] = isContractOverride ? contractSource : fromFile ? ".studio/.env.local" : "process.env";
11789
11956
  }
11790
11957
  }
11791
11958
  const walletKind2 = deployWalletKind(agentRoot2);
@@ -12219,7 +12386,7 @@ function wireCognitoOutputs(workspaceRoot, outputsFile = null) {
12219
12386
  });
12220
12387
  fs28.writeFileSync(acj, `${JSON.stringify(cfg, null, 2)}
12221
12388
  `, "utf-8");
12222
- const envLocal = envLocalPath13(workspaceRoot);
12389
+ const envLocal = envLocalPath14(workspaceRoot);
12223
12390
  upsertEnv(envLocal, { OAUTH_TOKEN_URL: tokenUrl, OAUTH_SCOPE: scope });
12224
12391
  return {
12225
12392
  discoveryUrl: discovery,
@@ -12813,7 +12980,7 @@ import { BSC_TESTNET_FAUCET_URLS } from "@bnbagent/studio-runtime/networks";
12813
12980
  import * as crypto4 from "crypto";
12814
12981
  import * as fs34 from "fs";
12815
12982
  import * as path33 from "path";
12816
- import { envLocalPath as envLocalPath14 } from "@bnbagent/studio-runtime/config";
12983
+ import { envLocalPath as envLocalPath15 } from "@bnbagent/studio-runtime/config";
12817
12984
  import { show } from "@bnbagent/studio-runtime/erc8004";
12818
12985
  import { DEFAULT_RPC, getNetwork as getNetwork8 } from "@bnbagent/studio-runtime/networks";
12819
12986
  import * as walletRt3 from "@bnbagent/studio-runtime/wallet";
@@ -12958,7 +13125,7 @@ async function checkNetworkMatchesChainId(root, _target) {
12958
13125
  return [];
12959
13126
  }
12960
13127
  async function checkRpcUrlSetForRuntime(root, _target) {
12961
- const envLocal = envLocalPath14(root);
13128
+ const envLocal = envLocalPath15(root);
12962
13129
  if (getEnvVar(envLocal, "RPC_URL")) {
12963
13130
  return [];
12964
13131
  }
@@ -12983,7 +13150,7 @@ async function checkRuntimeRpcReachable(root, _target) {
12983
13150
  return [];
12984
13151
  }
12985
13152
  const expected = getNetwork8(netName).chainId;
12986
- const configured = process.env.RPC_URL || getEnvVar(envLocalPath14(root), "RPC_URL") || "";
13153
+ const configured = process.env.RPC_URL || getEnvVar(envLocalPath15(root), "RPC_URL") || "";
12987
13154
  const rpcUrl = configured || DEFAULT_RPC[netName] || "";
12988
13155
  if (!rpcUrl) {
12989
13156
  return [];
@@ -13292,6 +13459,8 @@ var criticalChecks = [
13292
13459
  checkPlatformLoggedIn,
13293
13460
  checkPlatformDockerAvailable,
13294
13461
  checkPlatformStorageEndpoint,
13462
+ checkTwakCustomContractsUnsupported,
13463
+ checkAltanaCustomContractsUnsupported,
13295
13464
  checkLocalKeystoreExists,
13296
13465
  checkWalletPasswordEnvSet,
13297
13466
  checkLlmProviderKeySet,
@@ -13733,7 +13902,7 @@ async function runVerify(opts) {
13733
13902
  import * as fs37 from "fs";
13734
13903
  import * as path37 from "path";
13735
13904
  import {
13736
- envLocalPath as envLocalPath15,
13905
+ envLocalPath as envLocalPath16,
13737
13906
  findSubProjectRoot as findSubProjectRoot15,
13738
13907
  findWorkspaceRoot as findWorkspaceRoot6,
13739
13908
  loadStudioToml as loadStudioToml19
@@ -14488,7 +14657,7 @@ function persistDeploySummary(root, text2) {
14488
14657
  return null;
14489
14658
  }
14490
14659
  const p = path37.join(
14491
- path37.dirname(envLocalPath15(root)),
14660
+ path37.dirname(envLocalPath16(root)),
14492
14661
  "last-deploy-summary.txt"
14493
14662
  );
14494
14663
  fs37.mkdirSync(path37.dirname(p), { recursive: true });
@@ -15123,10 +15292,10 @@ function writeAgentDeployState(workspaceRoot, arn, record = {}, destination = "s
15123
15292
  patchTomlKv(tomlPath, "deploy", "oauth_discovery_url", discoveryUrl);
15124
15293
  }
15125
15294
  if (tokenUrl) {
15126
- setEnvVar(envLocalPath16(agentRoot2), "OAUTH_TOKEN_URL", tokenUrl);
15295
+ setEnvVar(envLocalPath17(agentRoot2), "OAUTH_TOKEN_URL", tokenUrl);
15127
15296
  }
15128
15297
  if (scope) {
15129
- setEnvVar(envLocalPath16(agentRoot2), "OAUTH_SCOPE", scope);
15298
+ setEnvVar(envLocalPath17(agentRoot2), "OAUTH_SCOPE", scope);
15130
15299
  }
15131
15300
  syncDescriptorOauthFacts(workspaceRoot, {
15132
15301
  tokenUrl,
@@ -15863,7 +16032,7 @@ function persistDeploySummary2(root, text2) {
15863
16032
  return null;
15864
16033
  }
15865
16034
  const p = path38.join(
15866
- path38.dirname(envLocalPath16(root)),
16035
+ path38.dirname(envLocalPath17(root)),
15867
16036
  "last-deploy-summary.txt"
15868
16037
  );
15869
16038
  fs38.mkdirSync(path38.dirname(p), { recursive: true });
@@ -16386,7 +16555,7 @@ import * as fs39 from "fs";
16386
16555
  import * as path39 from "path";
16387
16556
  import { EVMWalletProvider as EVMWalletProvider2 } from "@bnbagent/sdk";
16388
16557
  import {
16389
- envLocalPath as envLocalPath17,
16558
+ envLocalPath as envLocalPath18,
16390
16559
  findProjectRoot as findProjectRoot4,
16391
16560
  findStudioWorkspaceRoot as findStudioWorkspaceRoot4,
16392
16561
  loadStudioToml as loadStudioToml21
@@ -16495,7 +16664,9 @@ async function cmdDoctor(opts) {
16495
16664
  checks.push(...await checkLlm(data, projectRoot));
16496
16665
  checks.push(...await checkNetwork(data, opts.network));
16497
16666
  checks.push(...checkCurrency(data));
16498
- checks.push(...checkErc8183Pricing(data));
16667
+ checks.push(
16668
+ ...checkErc8183Pricing(data, erc8183ContractEnvForProject(projectRoot))
16669
+ );
16499
16670
  checks.push(...checkPriceBounds(data));
16500
16671
  checks.push(...checkX402Seller(data, projectRoot));
16501
16672
  checks.push(...checkStorage(data));
@@ -16526,7 +16697,7 @@ function resolveProjectRoot3(argRoot) {
16526
16697
  }
16527
16698
  function runCheckEnv(projectRoot) {
16528
16699
  const agentDir = workspaceLayers(projectRoot) ?? path39.join(projectRoot, "agent");
16529
- const envPath = envLocalPath17(agentDir);
16700
+ const envPath = envLocalPath18(agentDir);
16530
16701
  printOut(`bag doctor --check-env: ${envPath}`);
16531
16702
  if (!isFile17(envPath)) {
16532
16703
  printOut(` (no ${path39.basename(envPath)} found \u2014 nothing to report)`);
@@ -16569,8 +16740,8 @@ function workspaceLayers(projectRoot) {
16569
16740
  }
16570
16741
  return null;
16571
16742
  }
16572
- function checkAppMain(projectRootArg) {
16573
- let projectRoot = projectRootArg;
16743
+ function checkAppMain(projectRootArg2) {
16744
+ let projectRoot = projectRootArg2;
16574
16745
  const parent = path39.dirname(projectRoot);
16575
16746
  if (isFile17(path39.join(parent, "agent", "studio.toml"))) {
16576
16747
  projectRoot = parent;
@@ -16667,7 +16838,7 @@ async function checkWallet(projectRoot, data) {
16667
16838
  return checkWalletTwak(walletCfg, projectRoot, data);
16668
16839
  }
16669
16840
  if (walletCfg.kind === "altana") {
16670
- return checkWalletAltana(walletCfg, projectRoot);
16841
+ return checkWalletAltana(walletCfg, projectRoot, data);
16671
16842
  }
16672
16843
  if (!isLocalEvmWallet(walletCfg)) {
16673
16844
  return [
@@ -16722,8 +16893,18 @@ async function checkWallet(projectRoot, data) {
16722
16893
  }
16723
16894
  return out;
16724
16895
  }
16725
- function checkWalletAltana(walletCfg, projectRoot) {
16896
+ function checkWalletAltana(walletCfg, projectRoot, data) {
16726
16897
  const out = [];
16898
+ const contracts = erc8183ContractOverrideState(
16899
+ erc8183ContractEnvForProject(projectRoot)
16900
+ );
16901
+ if (Object.keys(tableOf14(tableOf14(data, "payments"), "erc8183")).length > 0 && contracts.mode === "custom") {
16902
+ out.push({
16903
+ name: "[wallet] Altana contract targets",
16904
+ status: FAIL,
16905
+ detail: "custom ERC-8183 targets are unsupported: this session's permissions and quote-checker approval are tied to the canonical Commerce stack. Remove the ERC8183_*_ADDRESS overrides, or use wallet.kind='evm-local'."
16906
+ });
16907
+ }
16727
16908
  const keystoreDir = anchoredKeystoreDir(projectRoot, walletCfg);
16728
16909
  const address = String(walletCfg.address ?? "").trim();
16729
16910
  if (!address) {
@@ -16940,7 +17121,7 @@ async function checkLlm(data, projectRoot) {
16940
17121
  }
16941
17122
  ];
16942
17123
  }
16943
- const keyValue = getEnvVar(envLocalPath17(projectRoot), keyEnv) || process.env[keyEnv];
17124
+ const keyValue = getEnvVar(envLocalPath18(projectRoot), keyEnv) || process.env[keyEnv];
16944
17125
  const out = [];
16945
17126
  if (keyValue) {
16946
17127
  out.push({
@@ -16956,6 +17137,14 @@ async function checkLlm(data, projectRoot) {
16956
17137
  });
16957
17138
  }
16958
17139
  if (provider === "pieverse-llm") {
17140
+ const walletKind2 = String(tableOf14(data, "wallet").kind ?? "");
17141
+ if (walletKind2 === "altana") {
17142
+ out.push({
17143
+ name: "pieverse wallet compatibility",
17144
+ status: FAIL,
17145
+ detail: "wallet kind 'altana' cannot log in to Pieverse (SIWE needs EIP-191 'sign.message', which altana never exposes) \u2014 `bag llm activate` will always fail. Set [llm].provider to 'openrouter', 'openai', or 'anthropic' in studio.toml."
17146
+ });
17147
+ }
16959
17148
  out.push(...await checkPieverse(data, keyValue));
16960
17149
  }
16961
17150
  return out;
@@ -17115,7 +17304,7 @@ function checkCurrency(data) {
17115
17304
  }
17116
17305
  return [];
17117
17306
  }
17118
- function checkErc8183Pricing(data) {
17307
+ function checkErc8183Pricing(data, env = process.env) {
17119
17308
  const payValue = tableOf14(data, "payments").erc8183;
17120
17309
  if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
17121
17310
  return [];
@@ -17149,7 +17338,7 @@ function checkErc8183Pricing(data) {
17149
17338
  }
17150
17339
  ];
17151
17340
  }
17152
- const contracts = erc8183ContractOverrideState();
17341
+ const contracts = erc8183ContractOverrideState(env);
17153
17342
  const priceMode = pricing.kind === "free" ? "FREE \u2014 zero token escrow" : `PAID \u2014 effective list price ${pricing.effectivePrice} token base units`;
17154
17343
  if (contracts.mode === "partial") {
17155
17344
  return [
@@ -17178,20 +17367,11 @@ function checkErc8183Pricing(data) {
17178
17367
  }
17179
17368
  ];
17180
17369
  }
17181
- if (contracts.mode === "custom") {
17182
- return [
17183
- {
17184
- name: "erc8183 pricing",
17185
- status: PASS,
17186
- detail: "FREE \u2014 zero token escrow; custom contract stack selected with all three ERC-8183 address overrides."
17187
- }
17188
- ];
17189
- }
17190
17370
  return [
17191
17371
  {
17192
17372
  name: "erc8183 pricing",
17193
- status: FAIL,
17194
- detail: "FREE \u2014 zero token escrow, but the canonical ERC-8183 contracts reject zero funding. Set ERC8183_COMMERCE_ADDRESS, ERC8183_ROUTER_ADDRESS, and ERC8183_POLICY_ADDRESS together to a zero-price-compatible custom stack."
17373
+ status: PASS,
17374
+ detail: contracts.mode === "custom" ? "FREE \u2014 zero token escrow; custom contract stack selected with all three ERC-8183 address overrides." : "FREE \u2014 zero token escrow; canonical contract stack selected with zero-price funding support."
17195
17375
  }
17196
17376
  ];
17197
17377
  }
@@ -17702,13 +17882,13 @@ function resolveEnvPath(fileArg, rootArg) {
17702
17882
  if (!fs40.existsSync(path40.join(root2, "studio.toml"))) {
17703
17883
  return [null, `error: no studio.toml under --project-root ${root2}`];
17704
17884
  }
17705
- return [envLocalPath(root2), null];
17885
+ return [envLocalPath2(root2), null];
17706
17886
  }
17707
17887
  const root = findProjectRoot();
17708
17888
  if (root === null) {
17709
17889
  return [".env.local", null];
17710
17890
  }
17711
- const target = envLocalPath(root);
17891
+ const target = envLocalPath2(root);
17712
17892
  let note = null;
17713
17893
  if (path40.dirname(path40.dirname(target)) !== path40.resolve(process.cwd())) {
17714
17894
  note = `\u2192 writing to ${target} (workspace .studio/, where \`bag dev\` reads)`;
@@ -19730,7 +19910,7 @@ import * as fs45 from "fs";
19730
19910
  import * as path47 from "path";
19731
19911
  import { auditedOp as auditedOp4 } from "@bnbagent/studio-runtime/audit";
19732
19912
  import {
19733
- envLocalPath as envLocalPath18,
19913
+ envLocalPath as envLocalPath19,
19734
19914
  findProjectRoot as findProjectRoot6,
19735
19915
  loadStudioToml as loadStudioToml24
19736
19916
  } from "@bnbagent/studio-runtime/config";
@@ -20020,7 +20200,7 @@ async function cmdSellInit(priceFlag) {
20020
20200
  "utf8"
20021
20201
  );
20022
20202
  }
20023
- const envPath = envLocalPath18(root);
20203
+ const envPath = envLocalPath19(root);
20024
20204
  for (const key of B402_ENV_KEYS) {
20025
20205
  const existing = getEnvVar(envPath, key);
20026
20206
  if (key === "B402_BASE_URL" && !free && resolveNetwork3(cfg).toLowerCase() === "bsc-testnet" && !process.env.B402_BASE_URL && !existing) {
@@ -20580,7 +20760,7 @@ function buildProgram() {
20580
20760
  return program;
20581
20761
  }
20582
20762
  function cliVersion() {
20583
- return "0.0.6-alpha.8";
20763
+ return "0.0.6";
20584
20764
  }
20585
20765
 
20586
20766
  // src/cli/updateCheck.ts
@@ -20683,11 +20863,18 @@ async function main(argv) {
20683
20863
  printSetupNudge();
20684
20864
  return 0;
20685
20865
  }
20686
- autoloadProjectEnv();
20866
+ const explicitProjectRoot = projectRootArg(args);
20867
+ const releaseProjectEnv = autoloadProjectEnv(explicitProjectRoot);
20687
20868
  if (args[0] !== "wallet") {
20869
+ const originalCwd = process.cwd();
20688
20870
  try {
20871
+ if (explicitProjectRoot !== void 0) {
20872
+ process.chdir(explicitProjectRoot);
20873
+ }
20689
20874
  await ensureAltanaSessionLoaded();
20690
20875
  } catch {
20876
+ } finally {
20877
+ process.chdir(originalCwd);
20691
20878
  }
20692
20879
  }
20693
20880
  maybeSyncSkills(args.find((a) => !a.startsWith("-")));
@@ -20710,7 +20897,25 @@ async function main(argv) {
20710
20897
  process.stderr.write(`error: ${msg}
20711
20898
  `);
20712
20899
  return 1;
20900
+ } finally {
20901
+ releaseProjectEnv();
20902
+ }
20903
+ }
20904
+ function projectRootArg(args) {
20905
+ let found;
20906
+ for (let i = 0; i < args.length; i += 1) {
20907
+ const arg = args[i];
20908
+ if (arg === "--") break;
20909
+ if (arg === "--project-root") {
20910
+ found = args[i + 1];
20911
+ i += 1;
20912
+ continue;
20913
+ }
20914
+ if (arg.startsWith("--project-root=")) {
20915
+ found = arg.slice("--project-root=".length);
20916
+ }
20713
20917
  }
20918
+ return found;
20714
20919
  }
20715
20920
  function maybeSyncSkills(command) {
20716
20921
  try {