@bnbagent/studio-cli 0.0.6-alpha.9 → 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";
@@ -4823,7 +4930,7 @@ async function cmdActivate(opts) {
4823
4930
  const pvCfg = loadPieverseConfig(cfg);
4824
4931
  const replace = opts.replace === true;
4825
4932
  let existingHash = !replace && pvCfg.key_hash ? String(pvCfg.key_hash) : null;
4826
- const envFileValue = replace ? null : getEnvVar(envLocalPath9(root), PIEVERSE_ENV_KEY);
4933
+ const envFileValue = replace ? null : getEnvVar(envLocalPath10(root), PIEVERSE_ENV_KEY);
4827
4934
  const existingEnv = replace ? null : envFileValue || process.env[PIEVERSE_ENV_KEY];
4828
4935
  if (replace) {
4829
4936
  printOut(
@@ -5026,7 +5133,7 @@ Retry: re-run \`bag llm activate\` \u2014 no state was persisted yet.`
5026
5133
  }
5027
5134
  const key = String(result.key);
5028
5135
  const keyHash = String(result.hash);
5029
- writeEnvLocal(PIEVERSE_ENV_KEY, key, envLocalPath9(root));
5136
+ writeEnvLocal(PIEVERSE_ENV_KEY, key, envLocalPath10(root));
5030
5137
  updateStudioTomlSection(root, "llm.pieverse", {
5031
5138
  key_hash: keyHash,
5032
5139
  network: networkName
@@ -5557,7 +5664,7 @@ async function cmdRotate(opts) {
5557
5664
  `warning: could not auto-disable old key \u2014 manually disable ${oldHash} in the Pieverse dashboard.`
5558
5665
  );
5559
5666
  }
5560
- writeEnvLocal(PIEVERSE_ENV_KEY, newKey, envLocalPath9(root));
5667
+ writeEnvLocal(PIEVERSE_ENV_KEY, newKey, envLocalPath10(root));
5561
5668
  updateStudioTomlSection(root, "llm.pieverse", {
5562
5669
  key_hash: newHash,
5563
5670
  network: networkName
@@ -6312,7 +6419,7 @@ import * as os3 from "os";
6312
6419
  import * as path22 from "path";
6313
6420
  import { EVMWalletProvider, SigningPolicy } from "@bnbagent/sdk";
6314
6421
  import {
6315
- envLocalPath as envLocalPath10,
6422
+ envLocalPath as envLocalPath11,
6316
6423
  findProjectRoot as findProjectRoot3,
6317
6424
  loadStudioToml as loadStudioToml10
6318
6425
  } from "@bnbagent/studio-runtime/config";
@@ -6623,8 +6730,8 @@ async function cmdSessionGrant(opts) {
6623
6730
  printOut(granted.publicKey);
6624
6731
  return 0;
6625
6732
  }
6626
- function cmdSessionStatus(projectRootArg) {
6627
- const projectRoot = resolveProjectRootArg(projectRootArg);
6733
+ function cmdSessionStatus(projectRootArg2) {
6734
+ const projectRoot = resolveProjectRootArg(projectRootArg2);
6628
6735
  if (projectRoot === null) return 2;
6629
6736
  const ctx = loadContext(projectRoot);
6630
6737
  const { envelope } = readSession(ctx);
@@ -6857,7 +6964,7 @@ function requirePassword2() {
6857
6964
  }
6858
6965
  function requireDurablePassword(projectRoot) {
6859
6966
  const password = requirePassword2();
6860
- const envPath = envLocalPath10(projectRoot);
6967
+ const envPath = envLocalPath11(projectRoot);
6861
6968
  const persisted = getEnvVar(envPath, PASSWORD_ENV2);
6862
6969
  if (!persisted) {
6863
6970
  throw new Error(
@@ -7230,8 +7337,8 @@ ${detail}` : `error: \`twak wallet create --no-keychain\` did not produce ${wall
7230
7337
  );
7231
7338
  return cmdNewTwak(projectRoot, walletCfg);
7232
7339
  }
7233
- function cmdShow5(projectRootArg) {
7234
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7340
+ function cmdShow5(projectRootArg2) {
7341
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7235
7342
  const walletCfg = loadWalletCfg(projectRoot);
7236
7343
  const kind = walletKind(walletCfg);
7237
7344
  if (kind === "twak") {
@@ -7292,8 +7399,8 @@ function cmdShow5(projectRootArg) {
7292
7399
  }
7293
7400
  return 0;
7294
7401
  }
7295
- function cmdList2(projectRootArg) {
7296
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7402
+ function cmdList2(projectRootArg2) {
7403
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7297
7404
  const walletCfg = loadWalletCfg(projectRoot);
7298
7405
  if (walletKind(walletCfg) === "twak") {
7299
7406
  printOut(twakProvider(projectRoot, walletCfg).address);
@@ -7334,8 +7441,8 @@ function cmdList2(projectRootArg) {
7334
7441
  }
7335
7442
  return 0;
7336
7443
  }
7337
- async function cmdSign(msg, projectRootArg) {
7338
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7444
+ async function cmdSign(msg, projectRootArg2) {
7445
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7339
7446
  const walletCfg = loadWalletCfg(projectRoot);
7340
7447
  const kind = walletKind(walletCfg);
7341
7448
  if (kind === "altana") {
@@ -7428,8 +7535,8 @@ async function cmdBalance(opts) {
7428
7535
  }
7429
7536
  return 0;
7430
7537
  }
7431
- function cmdPolicyShow(asJson, projectRootArg) {
7432
- const projectRoot = resolveWalletProjectRoot(projectRootArg);
7538
+ function cmdPolicyShow(asJson, projectRootArg2) {
7539
+ const projectRoot = resolveWalletProjectRoot(projectRootArg2);
7433
7540
  const walletCfg = loadWalletCfg(projectRoot);
7434
7541
  const kind = walletKind(walletCfg);
7435
7542
  if (kind === "twak") {
@@ -7752,7 +7859,7 @@ function registerInit(program) {
7752
7859
  ).addOption(
7753
7860
  new Option2(
7754
7861
  "--wallet-kind <kind>",
7755
- "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."
7756
7863
  ).choices(["twak", "evm-local", "altana"])
7757
7864
  ).option(
7758
7865
  "--twak-home <path>",
@@ -8128,7 +8235,7 @@ async function cmdInit(nameArg, opts) {
8128
8235
  });
8129
8236
  }
8130
8237
  if (destination === "platform") {
8131
- printPlatformWalletSuggestion();
8238
+ printPlatformWalletSuggestion(walletKind2);
8132
8239
  }
8133
8240
  if (installFailed) {
8134
8241
  printErr(
@@ -8234,7 +8341,7 @@ function scaffold(target, name, o) {
8234
8341
  fs24.writeFileSync(path23.join(agentRoot2, ".gitignore"), renderAgentGitignore());
8235
8342
  fs24.writeFileSync(
8236
8343
  path23.join(agentRoot2, "README.md"),
8237
- renderAgentReadme(name, o.faces, o.destination)
8344
+ renderAgentReadme(name, o.faces, o.destination, o.walletKind)
8238
8345
  );
8239
8346
  const packaging = derivePackaging(
8240
8347
  o.walletKind,
@@ -8620,8 +8727,7 @@ function renderAgentStudioToml(name, o) {
8620
8727
  [payments.erc8183]
8621
8728
  # Read by the Agent: it quotes the FIXED \`price\`, CLAMPS it to [min,max],
8622
8729
  # freezes a short-TTL offer, and EIP-191 signs it \u2014 pricing is rule-based, the
8623
- # LLM never prices. price=0 is FREE and needs a zero-price-compatible
8624
- # 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.
8625
8731
  currency = "${currencyAddr}" # $U token address \u2014 prefilled from [network].default
8626
8732
  price = "${o.erc8183Price}" # token base units; 0 = FREE (zero token escrow)
8627
8733
  min_price = "0" # wei \u2014 clamp floor
@@ -8748,7 +8854,6 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402
8748
8854
  "",
8749
8855
  "# Optional ERC-8183 custom contract-stack override. Set all three",
8750
8856
  "# together; partial overrides can mix incompatible deployments.",
8751
- "# Required for price=0 while canonical contracts reject zero funding.",
8752
8857
  "# ERC8183_COMMERCE_ADDRESS=",
8753
8858
  "# ERC8183_ROUTER_ADDRESS=",
8754
8859
  "# ERC8183_POLICY_ADDRESS="
@@ -8757,7 +8862,7 @@ function renderAgentEnvLocal(provider, storageProvider, walletKind2, rails, b402
8757
8862
  return `${lines.join("\n")}
8758
8863
  `;
8759
8864
  }
8760
- function renderAgentReadme(name, faces, destination) {
8865
+ function renderAgentReadme(name, faces, destination, walletKind2) {
8761
8866
  const proto = faces.join(" + ");
8762
8867
  const mode = recipeModeOf(faces);
8763
8868
  const entry = `src/${entryStemOf(faces)}.ts`;
@@ -8796,8 +8901,7 @@ entrypoint code in \`src/signing.ts\` \u2014 never an LLM-callable tool.
8796
8901
  ${mode === "both" ? filesBoth : mode === "mcp" ? filesMcp : filesA2a}
8797
8902
  ${shared}
8798
8903
  - \`.env.local\` \u2014 Agent secrets; on deploy they are sent to the **operator's**
8799
- Secrets Manager (the scoped, consented commitment-#2 exception). Use a
8800
- 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)`."}
8801
8905
 
8802
8906
  ## Run locally
8803
8907
 
@@ -9189,8 +9293,8 @@ async function runOnboarding(o) {
9189
9293
  return false;
9190
9294
  }
9191
9295
  const agentRoot2 = path23.join(o.workspaceRoot, APP_DIR, AGENT_PKG);
9192
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
9193
- setEnvVar(envLocalPath19(agentRoot2), "WALLET_PASSWORD", password);
9296
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
9297
+ setEnvVar(envLocalPath20(agentRoot2), "WALLET_PASSWORD", password);
9194
9298
  const savedCwd = process.cwd();
9195
9299
  const savedPw = process.env.WALLET_PASSWORD;
9196
9300
  let address = null;
@@ -9229,7 +9333,7 @@ async function runOnboarding(o) {
9229
9333
  }
9230
9334
  }
9231
9335
  if (o.storageProvider !== "local") {
9232
- await onboardIpfsKey(envLocalPath19(agentRoot2), o.ipfsKey);
9336
+ await onboardIpfsKey(envLocalPath20(agentRoot2), o.ipfsKey);
9233
9337
  }
9234
9338
  if (address) {
9235
9339
  await printFaucetHint(o.network, address);
@@ -9363,8 +9467,8 @@ Guide: the bnbagent-studio-using-twak-wallet.md reference (installed by \`bag sk
9363
9467
  process.chdir(savedCwd);
9364
9468
  }
9365
9469
  if (o.storageProvider !== "local") {
9366
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
9367
- await onboardIpfsKey(envLocalPath19(agentRoot2), o.ipfsKey);
9470
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
9471
+ await onboardIpfsKey(envLocalPath20(agentRoot2), o.ipfsKey);
9368
9472
  }
9369
9473
  if (address) {
9370
9474
  await printFaucetHint(o.network, address);
@@ -9545,7 +9649,22 @@ NOTE \u2014 wallet.kind = "altana" (bounded runtime session):
9545
9649
  \`bag wallet session grant --force\` and redeploy.`
9546
9650
  );
9547
9651
  }
9548
- 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
+ }
9549
9668
  printOut(
9550
9669
  `
9551
9670
  NOTE \u2014 destination = "platform" (48h TESTNET TRIAL on the BNB Chain managed platform):
@@ -9901,7 +10020,9 @@ function printConfigSummary(agentRoot2) {
9901
10020
  const maxPrice = g(agent, "payments", "erc8183", "max_price");
9902
10021
  const pricingState = erc8183PricingState(erc8183);
9903
10022
  const isFreePrice = pricingState.kind === "free";
9904
- const contractOverrides = erc8183ContractOverrideState();
10023
+ const contractOverrides = erc8183ContractOverrideState(
10024
+ erc8183ContractEnvForProject(agentRoot2)
10025
+ );
9905
10026
  const storageKind2 = shown(g(agent, "storage", "kind"), "?");
9906
10027
  const storageDesc = storageKind2 === "ipfs" ? "IPFS (durable, public)" : "local disk (offline dev only)";
9907
10028
  const walletKind2 = shown(g(agent, "wallet", "kind"), "evm-local");
@@ -10001,7 +10122,7 @@ function printConfigSummary(agentRoot2) {
10001
10122
  ` pricing : FREE \u2014 ${weiToU(String(price))} per job; zero token escrow`
10002
10123
  );
10003
10124
  printOut(
10004
- 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`"
10005
10126
  );
10006
10127
  } else {
10007
10128
  printOut(
@@ -10039,9 +10160,9 @@ function printConfigSummary(agentRoot2) {
10039
10160
  if (hasErc8183 && !maxPrice && !isFreePrice) {
10040
10161
  todo.push("[payments.erc8183].max_price \u2014 the price clamp ceiling");
10041
10162
  }
10042
- if (hasErc8183 && isFreePrice && contractOverrides.mode !== "custom") {
10163
+ if (hasErc8183 && isFreePrice && (contractOverrides.mode === "partial" || contractOverrides.mode === "invalid")) {
10043
10164
  todo.push(
10044
- "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"
10045
10166
  );
10046
10167
  }
10047
10168
  if (hasX402Seller && !isFreeX402) {
@@ -10307,6 +10428,17 @@ function loadEnvFileForDev(p) {
10307
10428
  if (!isFile10(p)) {
10308
10429
  return;
10309
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
+ }
10310
10442
  for (const line of fs25.readFileSync(p, "utf-8").split(/\r?\n/)) {
10311
10443
  if (!line || line.trimStart().startsWith("#")) {
10312
10444
  continue;
@@ -10314,6 +10446,11 @@ function loadEnvFileForDev(p) {
10314
10446
  const m = KEY_LINE_RE.exec(line);
10315
10447
  if (m) {
10316
10448
  const key = m[1];
10449
+ if (contractStackOwnedByShell && ERC8183_ADDRESS_OVERRIDE_KEYS.includes(
10450
+ key
10451
+ )) {
10452
+ continue;
10453
+ }
10317
10454
  const eq = line.indexOf("=");
10318
10455
  const value = eq >= 0 ? line.slice(eq + 1) : "";
10319
10456
  if (value !== "") {
@@ -10552,8 +10689,8 @@ async function cmdDev(opts) {
10552
10689
  const agentDir = path24.join(workspaceRoot, "agent");
10553
10690
  migrateEnvLocal(agentDir);
10554
10691
  migrateKeystoreOutOfCodelocation(agentDir);
10555
- const { envLocalPath: envLocalPath19 } = await import("@bnbagent/studio-runtime/config");
10556
- loadEnvFileForDev(envLocalPath19(agentDir));
10692
+ const { envLocalPath: envLocalPath20 } = await import("@bnbagent/studio-runtime/config");
10693
+ loadEnvFileForDev(envLocalPath20(agentDir));
10557
10694
  ensureStoragePath(workspaceRoot);
10558
10695
  const ipfsErr = ipfsPreflight(agentDir);
10559
10696
  if (ipfsErr !== null) {
@@ -10798,7 +10935,7 @@ function networkBanner(agentDir) {
10798
10935
  import * as fs26 from "fs";
10799
10936
  import { createRequire } from "module";
10800
10937
  import * as path25 from "path";
10801
- 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";
10802
10939
  import { pieverseKeyHash as pieverseKeyHash2 } from "@bnbagent/studio-runtime/llm";
10803
10940
  var AGENTCORE_DESCRIPTOR2 = "agentcore/agentcore.json";
10804
10941
  function isFile11(p) {
@@ -10834,7 +10971,10 @@ function runtimeEnvKeys2(agentRoot2) {
10834
10971
  } catch {
10835
10972
  cfg = {};
10836
10973
  }
10837
- 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));
10838
10978
  return runtimeEnvKeysCore(cfg, resolvable);
10839
10979
  }
10840
10980
  var require2 = createRequire(import.meta.url);
@@ -11054,7 +11194,7 @@ function studioIgnored(lines) {
11054
11194
  }
11055
11195
  async function checkGitignoreExcludesSecrets(root, _target) {
11056
11196
  const out = [];
11057
- const ws = path25.dirname(path25.dirname(envLocalPath11(root)));
11197
+ const ws = path25.dirname(path25.dirname(envLocalPath12(root)));
11058
11198
  const gi = path25.join(ws, ".gitignore");
11059
11199
  const lines = gitignoreLines(gi);
11060
11200
  if (lines === null) {
@@ -11160,7 +11300,7 @@ async function checkPieverseKeyHash(root, _target) {
11160
11300
  ];
11161
11301
  }
11162
11302
  const agentRoot2 = agentRootOf(root);
11163
- 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;
11164
11304
  if (!apiKey) return [];
11165
11305
  const configured = String(pvCfg.key_hash).trim().toLowerCase().replace(/^0x/u, "");
11166
11306
  const actual = pieverseKeyHash2(apiKey);
@@ -11177,10 +11317,10 @@ async function checkPieverseKeyHash(root, _target) {
11177
11317
  }
11178
11318
  ];
11179
11319
  }
11180
- function erc8183RailChecks(cfg) {
11320
+ function erc8183RailChecks(cfg, env = process.env) {
11181
11321
  const c2 = tableOf2(tableOf2(cfg, "payments"), "erc8183");
11182
11322
  const out = [];
11183
- const contracts = erc8183ContractOverrideState();
11323
+ const contracts = erc8183ContractOverrideState(env);
11184
11324
  if (contracts.mode === "partial") {
11185
11325
  out.push({
11186
11326
  level: Level.CRITICAL,
@@ -11232,19 +11372,13 @@ function erc8183RailChecks(cfg) {
11232
11372
  }
11233
11373
  });
11234
11374
  } else if (pricing.kind === "free") {
11235
- if (contracts.mode === "canonical") {
11236
- out.push({
11237
- level: Level.CRITICAL,
11238
- name: "commerce_zero_price_contract_unsupported",
11239
- 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.",
11240
- details: { effective_wei: "0", contract_profile: "canonical" }
11241
- });
11242
- } else if (contracts.mode === "custom") {
11375
+ if (contracts.mode === "canonical" || contracts.mode === "custom") {
11376
+ const contractProfile = contracts.mode;
11243
11377
  out.push({
11244
11378
  level: Level.INFO,
11245
11379
  name: "commerce_zero_price_enabled",
11246
- message: "ERC-8183 pricing is FREE \u2014 buyers fund 0 token units with zero token escrow; a complete custom contract stack is selected.",
11247
- 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 }
11248
11382
  });
11249
11383
  }
11250
11384
  }
@@ -11405,7 +11539,14 @@ async function checkCommerceReady(root, target) {
11405
11539
  const cfg = loadAgentToml(root);
11406
11540
  const rails = commerceRails(cfg);
11407
11541
  const out = [];
11408
- 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
+ }
11409
11550
  const x402Checks = x402SellerRailChecks(agentRootOf(root), cfg, target);
11410
11551
  if (!rails.erc8183 && rails.x402) {
11411
11552
  for (const check of x402Checks) {
@@ -11510,7 +11651,7 @@ async function checkRuntimeSecretsInjected(root, target) {
11510
11651
  }
11511
11652
  const missing = needed.filter((k) => !present[k]);
11512
11653
  if (missing.length > 0) {
11513
- const envLocal = envLocalPath11(root);
11654
+ const envLocal = envLocalPath12(root);
11514
11655
  const recoverable = missing.filter(
11515
11656
  (k) => Boolean(process.env[k] || getEnvVar(envLocal, k))
11516
11657
  );
@@ -11688,6 +11829,7 @@ var allChecks = [
11688
11829
  checkTwakCredentialsAvailable,
11689
11830
  checkTwakDockerAvailable,
11690
11831
  // altana-kind checks (every one early-returns for other kinds).
11832
+ checkAltanaCustomContractsUnsupported,
11691
11833
  checkAltanaSessionReady,
11692
11834
  checkAltanaSdkResolvable,
11693
11835
  checkAltanaSessionNotInsideAgent,
@@ -11770,7 +11912,7 @@ function readKeystoreJson(agentRoot2) {
11770
11912
  function readEnvLocal(agentRoot2) {
11771
11913
  const values = {};
11772
11914
  try {
11773
- const file = envLocalPath12(agentRoot2);
11915
+ const file = envLocalPath13(agentRoot2);
11774
11916
  if (!isFile12(file)) {
11775
11917
  return values;
11776
11918
  }
@@ -11798,14 +11940,19 @@ function collectRuntimeSecretsDetailed(root) {
11798
11940
  const agentRoot2 = findSubProjectRoot8("agent", root) ?? root;
11799
11941
  const secretKeys = runtimeEnvKeys2(agentRoot2);
11800
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";
11801
11945
  const payload = {};
11802
11946
  const sources = {};
11803
11947
  for (const k of secretKeys) {
11804
11948
  const fromFile = fileValues[k];
11805
- 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];
11806
11953
  if (v) {
11807
11954
  payload[k] = v;
11808
- sources[k] = fromFile ? ".studio/.env.local" : "process.env";
11955
+ sources[k] = isContractOverride ? contractSource : fromFile ? ".studio/.env.local" : "process.env";
11809
11956
  }
11810
11957
  }
11811
11958
  const walletKind2 = deployWalletKind(agentRoot2);
@@ -12239,7 +12386,7 @@ function wireCognitoOutputs(workspaceRoot, outputsFile = null) {
12239
12386
  });
12240
12387
  fs28.writeFileSync(acj, `${JSON.stringify(cfg, null, 2)}
12241
12388
  `, "utf-8");
12242
- const envLocal = envLocalPath13(workspaceRoot);
12389
+ const envLocal = envLocalPath14(workspaceRoot);
12243
12390
  upsertEnv(envLocal, { OAUTH_TOKEN_URL: tokenUrl, OAUTH_SCOPE: scope });
12244
12391
  return {
12245
12392
  discoveryUrl: discovery,
@@ -12833,7 +12980,7 @@ import { BSC_TESTNET_FAUCET_URLS } from "@bnbagent/studio-runtime/networks";
12833
12980
  import * as crypto4 from "crypto";
12834
12981
  import * as fs34 from "fs";
12835
12982
  import * as path33 from "path";
12836
- import { envLocalPath as envLocalPath14 } from "@bnbagent/studio-runtime/config";
12983
+ import { envLocalPath as envLocalPath15 } from "@bnbagent/studio-runtime/config";
12837
12984
  import { show } from "@bnbagent/studio-runtime/erc8004";
12838
12985
  import { DEFAULT_RPC, getNetwork as getNetwork8 } from "@bnbagent/studio-runtime/networks";
12839
12986
  import * as walletRt3 from "@bnbagent/studio-runtime/wallet";
@@ -12978,7 +13125,7 @@ async function checkNetworkMatchesChainId(root, _target) {
12978
13125
  return [];
12979
13126
  }
12980
13127
  async function checkRpcUrlSetForRuntime(root, _target) {
12981
- const envLocal = envLocalPath14(root);
13128
+ const envLocal = envLocalPath15(root);
12982
13129
  if (getEnvVar(envLocal, "RPC_URL")) {
12983
13130
  return [];
12984
13131
  }
@@ -13003,7 +13150,7 @@ async function checkRuntimeRpcReachable(root, _target) {
13003
13150
  return [];
13004
13151
  }
13005
13152
  const expected = getNetwork8(netName).chainId;
13006
- const configured = process.env.RPC_URL || getEnvVar(envLocalPath14(root), "RPC_URL") || "";
13153
+ const configured = process.env.RPC_URL || getEnvVar(envLocalPath15(root), "RPC_URL") || "";
13007
13154
  const rpcUrl = configured || DEFAULT_RPC[netName] || "";
13008
13155
  if (!rpcUrl) {
13009
13156
  return [];
@@ -13312,6 +13459,8 @@ var criticalChecks = [
13312
13459
  checkPlatformLoggedIn,
13313
13460
  checkPlatformDockerAvailable,
13314
13461
  checkPlatformStorageEndpoint,
13462
+ checkTwakCustomContractsUnsupported,
13463
+ checkAltanaCustomContractsUnsupported,
13315
13464
  checkLocalKeystoreExists,
13316
13465
  checkWalletPasswordEnvSet,
13317
13466
  checkLlmProviderKeySet,
@@ -13753,7 +13902,7 @@ async function runVerify(opts) {
13753
13902
  import * as fs37 from "fs";
13754
13903
  import * as path37 from "path";
13755
13904
  import {
13756
- envLocalPath as envLocalPath15,
13905
+ envLocalPath as envLocalPath16,
13757
13906
  findSubProjectRoot as findSubProjectRoot15,
13758
13907
  findWorkspaceRoot as findWorkspaceRoot6,
13759
13908
  loadStudioToml as loadStudioToml19
@@ -14508,7 +14657,7 @@ function persistDeploySummary(root, text2) {
14508
14657
  return null;
14509
14658
  }
14510
14659
  const p = path37.join(
14511
- path37.dirname(envLocalPath15(root)),
14660
+ path37.dirname(envLocalPath16(root)),
14512
14661
  "last-deploy-summary.txt"
14513
14662
  );
14514
14663
  fs37.mkdirSync(path37.dirname(p), { recursive: true });
@@ -15143,10 +15292,10 @@ function writeAgentDeployState(workspaceRoot, arn, record = {}, destination = "s
15143
15292
  patchTomlKv(tomlPath, "deploy", "oauth_discovery_url", discoveryUrl);
15144
15293
  }
15145
15294
  if (tokenUrl) {
15146
- setEnvVar(envLocalPath16(agentRoot2), "OAUTH_TOKEN_URL", tokenUrl);
15295
+ setEnvVar(envLocalPath17(agentRoot2), "OAUTH_TOKEN_URL", tokenUrl);
15147
15296
  }
15148
15297
  if (scope) {
15149
- setEnvVar(envLocalPath16(agentRoot2), "OAUTH_SCOPE", scope);
15298
+ setEnvVar(envLocalPath17(agentRoot2), "OAUTH_SCOPE", scope);
15150
15299
  }
15151
15300
  syncDescriptorOauthFacts(workspaceRoot, {
15152
15301
  tokenUrl,
@@ -15883,7 +16032,7 @@ function persistDeploySummary2(root, text2) {
15883
16032
  return null;
15884
16033
  }
15885
16034
  const p = path38.join(
15886
- path38.dirname(envLocalPath16(root)),
16035
+ path38.dirname(envLocalPath17(root)),
15887
16036
  "last-deploy-summary.txt"
15888
16037
  );
15889
16038
  fs38.mkdirSync(path38.dirname(p), { recursive: true });
@@ -16406,7 +16555,7 @@ import * as fs39 from "fs";
16406
16555
  import * as path39 from "path";
16407
16556
  import { EVMWalletProvider as EVMWalletProvider2 } from "@bnbagent/sdk";
16408
16557
  import {
16409
- envLocalPath as envLocalPath17,
16558
+ envLocalPath as envLocalPath18,
16410
16559
  findProjectRoot as findProjectRoot4,
16411
16560
  findStudioWorkspaceRoot as findStudioWorkspaceRoot4,
16412
16561
  loadStudioToml as loadStudioToml21
@@ -16515,7 +16664,9 @@ async function cmdDoctor(opts) {
16515
16664
  checks.push(...await checkLlm(data, projectRoot));
16516
16665
  checks.push(...await checkNetwork(data, opts.network));
16517
16666
  checks.push(...checkCurrency(data));
16518
- checks.push(...checkErc8183Pricing(data));
16667
+ checks.push(
16668
+ ...checkErc8183Pricing(data, erc8183ContractEnvForProject(projectRoot))
16669
+ );
16519
16670
  checks.push(...checkPriceBounds(data));
16520
16671
  checks.push(...checkX402Seller(data, projectRoot));
16521
16672
  checks.push(...checkStorage(data));
@@ -16546,7 +16697,7 @@ function resolveProjectRoot3(argRoot) {
16546
16697
  }
16547
16698
  function runCheckEnv(projectRoot) {
16548
16699
  const agentDir = workspaceLayers(projectRoot) ?? path39.join(projectRoot, "agent");
16549
- const envPath = envLocalPath17(agentDir);
16700
+ const envPath = envLocalPath18(agentDir);
16550
16701
  printOut(`bag doctor --check-env: ${envPath}`);
16551
16702
  if (!isFile17(envPath)) {
16552
16703
  printOut(` (no ${path39.basename(envPath)} found \u2014 nothing to report)`);
@@ -16589,8 +16740,8 @@ function workspaceLayers(projectRoot) {
16589
16740
  }
16590
16741
  return null;
16591
16742
  }
16592
- function checkAppMain(projectRootArg) {
16593
- let projectRoot = projectRootArg;
16743
+ function checkAppMain(projectRootArg2) {
16744
+ let projectRoot = projectRootArg2;
16594
16745
  const parent = path39.dirname(projectRoot);
16595
16746
  if (isFile17(path39.join(parent, "agent", "studio.toml"))) {
16596
16747
  projectRoot = parent;
@@ -16687,7 +16838,7 @@ async function checkWallet(projectRoot, data) {
16687
16838
  return checkWalletTwak(walletCfg, projectRoot, data);
16688
16839
  }
16689
16840
  if (walletCfg.kind === "altana") {
16690
- return checkWalletAltana(walletCfg, projectRoot);
16841
+ return checkWalletAltana(walletCfg, projectRoot, data);
16691
16842
  }
16692
16843
  if (!isLocalEvmWallet(walletCfg)) {
16693
16844
  return [
@@ -16742,8 +16893,18 @@ async function checkWallet(projectRoot, data) {
16742
16893
  }
16743
16894
  return out;
16744
16895
  }
16745
- function checkWalletAltana(walletCfg, projectRoot) {
16896
+ function checkWalletAltana(walletCfg, projectRoot, data) {
16746
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
+ }
16747
16908
  const keystoreDir = anchoredKeystoreDir(projectRoot, walletCfg);
16748
16909
  const address = String(walletCfg.address ?? "").trim();
16749
16910
  if (!address) {
@@ -16960,7 +17121,7 @@ async function checkLlm(data, projectRoot) {
16960
17121
  }
16961
17122
  ];
16962
17123
  }
16963
- const keyValue = getEnvVar(envLocalPath17(projectRoot), keyEnv) || process.env[keyEnv];
17124
+ const keyValue = getEnvVar(envLocalPath18(projectRoot), keyEnv) || process.env[keyEnv];
16964
17125
  const out = [];
16965
17126
  if (keyValue) {
16966
17127
  out.push({
@@ -17143,7 +17304,7 @@ function checkCurrency(data) {
17143
17304
  }
17144
17305
  return [];
17145
17306
  }
17146
- function checkErc8183Pricing(data) {
17307
+ function checkErc8183Pricing(data, env = process.env) {
17147
17308
  const payValue = tableOf14(data, "payments").erc8183;
17148
17309
  if (payValue === null || typeof payValue !== "object" || Array.isArray(payValue)) {
17149
17310
  return [];
@@ -17177,7 +17338,7 @@ function checkErc8183Pricing(data) {
17177
17338
  }
17178
17339
  ];
17179
17340
  }
17180
- const contracts = erc8183ContractOverrideState();
17341
+ const contracts = erc8183ContractOverrideState(env);
17181
17342
  const priceMode = pricing.kind === "free" ? "FREE \u2014 zero token escrow" : `PAID \u2014 effective list price ${pricing.effectivePrice} token base units`;
17182
17343
  if (contracts.mode === "partial") {
17183
17344
  return [
@@ -17206,20 +17367,11 @@ function checkErc8183Pricing(data) {
17206
17367
  }
17207
17368
  ];
17208
17369
  }
17209
- if (contracts.mode === "custom") {
17210
- return [
17211
- {
17212
- name: "erc8183 pricing",
17213
- status: PASS,
17214
- detail: "FREE \u2014 zero token escrow; custom contract stack selected with all three ERC-8183 address overrides."
17215
- }
17216
- ];
17217
- }
17218
17370
  return [
17219
17371
  {
17220
17372
  name: "erc8183 pricing",
17221
- status: FAIL,
17222
- 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."
17223
17375
  }
17224
17376
  ];
17225
17377
  }
@@ -17730,13 +17882,13 @@ function resolveEnvPath(fileArg, rootArg) {
17730
17882
  if (!fs40.existsSync(path40.join(root2, "studio.toml"))) {
17731
17883
  return [null, `error: no studio.toml under --project-root ${root2}`];
17732
17884
  }
17733
- return [envLocalPath(root2), null];
17885
+ return [envLocalPath2(root2), null];
17734
17886
  }
17735
17887
  const root = findProjectRoot();
17736
17888
  if (root === null) {
17737
17889
  return [".env.local", null];
17738
17890
  }
17739
- const target = envLocalPath(root);
17891
+ const target = envLocalPath2(root);
17740
17892
  let note = null;
17741
17893
  if (path40.dirname(path40.dirname(target)) !== path40.resolve(process.cwd())) {
17742
17894
  note = `\u2192 writing to ${target} (workspace .studio/, where \`bag dev\` reads)`;
@@ -19758,7 +19910,7 @@ import * as fs45 from "fs";
19758
19910
  import * as path47 from "path";
19759
19911
  import { auditedOp as auditedOp4 } from "@bnbagent/studio-runtime/audit";
19760
19912
  import {
19761
- envLocalPath as envLocalPath18,
19913
+ envLocalPath as envLocalPath19,
19762
19914
  findProjectRoot as findProjectRoot6,
19763
19915
  loadStudioToml as loadStudioToml24
19764
19916
  } from "@bnbagent/studio-runtime/config";
@@ -20048,7 +20200,7 @@ async function cmdSellInit(priceFlag) {
20048
20200
  "utf8"
20049
20201
  );
20050
20202
  }
20051
- const envPath = envLocalPath18(root);
20203
+ const envPath = envLocalPath19(root);
20052
20204
  for (const key of B402_ENV_KEYS) {
20053
20205
  const existing = getEnvVar(envPath, key);
20054
20206
  if (key === "B402_BASE_URL" && !free && resolveNetwork3(cfg).toLowerCase() === "bsc-testnet" && !process.env.B402_BASE_URL && !existing) {
@@ -20608,7 +20760,7 @@ function buildProgram() {
20608
20760
  return program;
20609
20761
  }
20610
20762
  function cliVersion() {
20611
- return "0.0.6-alpha.9";
20763
+ return "0.0.6";
20612
20764
  }
20613
20765
 
20614
20766
  // src/cli/updateCheck.ts
@@ -20711,11 +20863,18 @@ async function main(argv) {
20711
20863
  printSetupNudge();
20712
20864
  return 0;
20713
20865
  }
20714
- autoloadProjectEnv();
20866
+ const explicitProjectRoot = projectRootArg(args);
20867
+ const releaseProjectEnv = autoloadProjectEnv(explicitProjectRoot);
20715
20868
  if (args[0] !== "wallet") {
20869
+ const originalCwd = process.cwd();
20716
20870
  try {
20871
+ if (explicitProjectRoot !== void 0) {
20872
+ process.chdir(explicitProjectRoot);
20873
+ }
20717
20874
  await ensureAltanaSessionLoaded();
20718
20875
  } catch {
20876
+ } finally {
20877
+ process.chdir(originalCwd);
20719
20878
  }
20720
20879
  }
20721
20880
  maybeSyncSkills(args.find((a) => !a.startsWith("-")));
@@ -20738,8 +20897,26 @@ async function main(argv) {
20738
20897
  process.stderr.write(`error: ${msg}
20739
20898
  `);
20740
20899
  return 1;
20900
+ } finally {
20901
+ releaseProjectEnv();
20741
20902
  }
20742
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
+ }
20917
+ }
20918
+ return found;
20919
+ }
20743
20920
  function maybeSyncSkills(command) {
20744
20921
  try {
20745
20922
  if (process.env.BAG_NO_SKILL_SYNC) {