@layr-labs/ecloud-sdk 0.2.0-dev.3 → 0.2.1-dev

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/index.cjs CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  AuthRequiredError: () => AuthRequiredError,
34
34
  BUILD_STATUS: () => BUILD_STATUS,
35
35
  BadRequestError: () => BadRequestError,
36
+ BillingApiClient: () => BillingApiClient,
36
37
  BuildError: () => BuildError,
37
38
  BuildFailedError: () => BuildFailedError,
38
39
  ConflictError: () => ConflictError,
@@ -58,6 +59,7 @@ __export(index_exports, {
58
59
  createECloudClient: () => createECloudClient,
59
60
  createMetricsContext: () => createMetricsContext,
60
61
  createTelemetryClient: () => createTelemetryClient,
62
+ createViemClients: () => createClients,
61
63
  deleteLegacyPrivateKey: () => deleteLegacyPrivateKey,
62
64
  deletePrivateKey: () => deletePrivateKey,
63
65
  emitMetrics: () => emitMetrics,
@@ -77,9 +79,11 @@ __export(index_exports, {
77
79
  getAppLatestReleaseBlockNumbers: () => getAppLatestReleaseBlockNumbers,
78
80
  getAvailableEnvironments: () => getAvailableEnvironments,
79
81
  getAvailableTemplates: () => getAvailableTemplates,
82
+ getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
80
83
  getBlockTimestamps: () => getBlockTimestamps,
81
84
  getBuildType: () => getBuildType,
82
85
  getCategoryDescriptions: () => getCategoryDescriptions,
86
+ getChainFromID: () => getChainFromID,
83
87
  getCurrentInstanceType: () => getCurrentInstanceType,
84
88
  getEnvironmentConfig: () => getEnvironmentConfig,
85
89
  getLegacyKeys: () => getLegacyKeys,
@@ -117,7 +121,7 @@ __export(index_exports, {
117
121
  validateInstanceTypeSKU: () => validateInstanceTypeSKU,
118
122
  validateLogVisibility: () => validateLogVisibility,
119
123
  validateLogsParams: () => validateLogsParams,
120
- validatePrivateKey: () => validatePrivateKey2,
124
+ validatePrivateKey: () => validatePrivateKey,
121
125
  validatePrivateKeyFormat: () => validatePrivateKeyFormat,
122
126
  validateResourceUsageMonitoring: () => validateResourceUsageMonitoring,
123
127
  validateURL: () => validateURL,
@@ -130,118 +134,7 @@ __export(index_exports, {
130
134
  module.exports = __toCommonJS(index_exports);
131
135
 
132
136
  // src/client/modules/compute/app/index.ts
133
- var import_viem9 = require("viem");
134
- var import_accounts5 = require("viem/accounts");
135
-
136
- // src/client/common/config/environment.ts
137
- var SEPOLIA_CHAIN_ID = 11155111;
138
- var MAINNET_CHAIN_ID = 1;
139
- var CommonAddresses = {
140
- ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
141
- };
142
- var ChainAddresses = {
143
- [MAINNET_CHAIN_ID]: {
144
- PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
145
- },
146
- [SEPOLIA_CHAIN_ID]: {
147
- PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
148
- }
149
- };
150
- var BILLING_ENVIRONMENTS = {
151
- dev: {
152
- billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
153
- },
154
- prod: {
155
- billingApiServerURL: "https://billingapi.eigencloud.xyz"
156
- }
157
- };
158
- var ENVIRONMENTS = {
159
- "sepolia-dev": {
160
- name: "sepolia",
161
- build: "dev",
162
- appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
163
- permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
164
- erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
165
- kmsServerURL: "http://10.128.0.57:8080",
166
- userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
167
- defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
168
- },
169
- sepolia: {
170
- name: "sepolia",
171
- build: "prod",
172
- appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
173
- permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
174
- erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
175
- kmsServerURL: "http://10.128.15.203:8080",
176
- userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
177
- defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
178
- },
179
- "mainnet-alpha": {
180
- name: "mainnet-alpha",
181
- build: "prod",
182
- appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
183
- permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
184
- erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
185
- kmsServerURL: "http://10.128.0.2:8080",
186
- userApiServerURL: "https://userapi-compute.eigencloud.xyz",
187
- defaultRPCURL: "https://ethereum-rpc.publicnode.com"
188
- }
189
- };
190
- var CHAIN_ID_TO_ENVIRONMENT = {
191
- [SEPOLIA_CHAIN_ID.toString()]: "sepolia",
192
- [MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
193
- };
194
- function getEnvironmentConfig(environment, chainID) {
195
- const env = ENVIRONMENTS[environment];
196
- if (!env) {
197
- throw new Error(`Unknown environment: ${environment}`);
198
- }
199
- if (!isEnvironmentAvailable(environment)) {
200
- throw new Error(
201
- `Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
202
- );
203
- }
204
- if (chainID) {
205
- const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
206
- if (expectedEnv && expectedEnv !== environment) {
207
- throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
208
- }
209
- }
210
- const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
211
- return {
212
- ...env,
213
- chainID: BigInt(resolvedChainID)
214
- };
215
- }
216
- function getBillingEnvironmentConfig(build) {
217
- const config = BILLING_ENVIRONMENTS[build];
218
- if (!config) {
219
- throw new Error(`Unknown billing environment: ${build}`);
220
- }
221
- return config;
222
- }
223
- function getBuildType() {
224
- const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
225
- const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
226
- const buildType = buildTimeType || runtimeType;
227
- if (buildType === "dev") {
228
- return "dev";
229
- }
230
- return "prod";
231
- }
232
- function getAvailableEnvironments() {
233
- const buildType = getBuildType();
234
- if (buildType === "dev") {
235
- return ["sepolia-dev"];
236
- }
237
- return ["sepolia", "mainnet-alpha"];
238
- }
239
- function isEnvironmentAvailable(environment) {
240
- return getAvailableEnvironments().includes(environment);
241
- }
242
- function isMainnet(environmentConfig) {
243
- return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
244
- }
137
+ var import_viem6 = require("viem");
245
138
 
246
139
  // src/client/common/docker/build.ts
247
140
  var child_process = __toESM(require("child_process"), 1);
@@ -1341,9 +1234,6 @@ function extractRegistryNameNoDocker(imageRef) {
1341
1234
  return name;
1342
1235
  }
1343
1236
 
1344
- // src/client/common/contract/caller.ts
1345
- var import_accounts2 = require("viem/accounts");
1346
-
1347
1237
  // src/client/common/contract/eip7702.ts
1348
1238
  var import_viem = require("viem");
1349
1239
 
@@ -2456,11 +2346,12 @@ async function executeBatch(options, logger) {
2456
2346
  });
2457
2347
  const chainId = await publicClient.getChainId();
2458
2348
  const authorizationNonce = transactionNonce + 1;
2349
+ logger.debug("Using wallet client signing for EIP-7702 authorization");
2459
2350
  const signedAuthorization = await walletClient.signAuthorization({
2460
- account,
2351
+ account: account.address,
2461
2352
  contractAddress: environmentConfig.erc7702DelegatorAddress,
2462
- chainId: Number(chainId),
2463
- nonce: authorizationNonce
2353
+ chainId,
2354
+ nonce: Number(authorizationNonce)
2464
2355
  });
2465
2356
  authorizationList = [signedAuthorization];
2466
2357
  }
@@ -2515,78 +2406,31 @@ async function executeBatch(options, logger) {
2515
2406
  }
2516
2407
 
2517
2408
  // src/client/common/contract/caller.ts
2518
- var import_viem5 = require("viem");
2519
- var import_utils = require("viem/utils");
2520
- var import_accounts3 = require("viem/accounts");
2521
-
2522
- // src/client/common/utils/logger.ts
2523
- var defaultLogger = {
2524
- info: (...args) => console.info(...args),
2525
- warn: (...args) => console.warn(...args),
2526
- error: (...args) => console.error(...args),
2527
- debug: (...args) => console.debug(...args)
2528
- };
2529
- var getLogger = (verbose) => ({
2530
- info: (...args) => console.info(...args),
2531
- warn: (...args) => console.warn(...args),
2532
- error: (...args) => console.error(...args),
2533
- debug: (...args) => verbose && console.debug(...args)
2534
- });
2535
-
2536
- // src/client/common/utils/userapi.ts
2537
- var import_axios = __toESM(require("axios"), 1);
2538
- var import_form_data = __toESM(require("form-data"), 1);
2539
- var import_viem4 = require("viem");
2540
-
2541
- // src/client/common/utils/auth.ts
2542
- var import_viem2 = require("viem");
2543
- var APP_CONTROLLER_ABI = (0, import_viem2.parseAbi)([
2544
- "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
2545
- ]);
2546
- async function calculatePermissionSignature(options) {
2547
- const { permission, expiry, appControllerAddress, publicClient, account } = options;
2548
- const digest = await publicClient.readContract({
2549
- address: appControllerAddress,
2550
- abi: APP_CONTROLLER_ABI,
2551
- functionName: "calculateApiPermissionDigestHash",
2552
- args: [permission, expiry]
2553
- });
2554
- const signature = await account.signMessage({
2555
- message: { raw: digest }
2556
- });
2557
- return { signature, digest };
2558
- }
2559
- async function calculateBillingAuthSignature(options) {
2560
- const { account, product, expiry } = options;
2561
- const signature = await account.signTypedData({
2562
- domain: {
2563
- name: "EigenCloud Billing API",
2564
- version: "1"
2565
- },
2566
- types: {
2567
- BillingAuth: [
2568
- { name: "product", type: "string" },
2569
- { name: "expiry", type: "uint256" }
2570
- ]
2571
- },
2572
- primaryType: "BillingAuth",
2573
- message: {
2574
- product,
2575
- expiry
2576
- }
2577
- });
2578
- return { signature, expiry };
2579
- }
2580
-
2581
- // src/client/common/utils/userapi.ts
2582
- var import_accounts = require("viem/accounts");
2409
+ var import_viem3 = require("viem");
2583
2410
 
2584
2411
  // src/client/common/utils/helpers.ts
2585
- var import_viem3 = require("viem");
2412
+ var import_viem2 = require("viem");
2586
2413
  var import_chains2 = require("viem/chains");
2414
+ var import_accounts = require("viem/accounts");
2587
2415
  function getChainFromID(chainID, fallback = import_chains2.sepolia) {
2588
2416
  const id = Number(chainID);
2589
- return (0, import_viem3.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
2417
+ return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
2418
+ }
2419
+ function createClients(options) {
2420
+ const { privateKey, rpcUrl, chainId } = options;
2421
+ const privateKeyHex = addHexPrefix(privateKey);
2422
+ const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
2423
+ const chain = getChainFromID(chainId);
2424
+ const publicClient = (0, import_viem2.createPublicClient)({
2425
+ chain,
2426
+ transport: (0, import_viem2.http)(rpcUrl)
2427
+ });
2428
+ const walletClient = (0, import_viem2.createWalletClient)({
2429
+ account,
2430
+ chain,
2431
+ transport: (0, import_viem2.http)(rpcUrl)
2432
+ });
2433
+ return { walletClient, publicClient };
2590
2434
  }
2591
2435
  function addHexPrefix(value) {
2592
2436
  return value.startsWith("0x") ? value : `0x${value}`;
@@ -2595,302 +2439,6 @@ function stripHexPrefix(value) {
2595
2439
  return value.startsWith("0x") ? value.slice(2) : value;
2596
2440
  }
2597
2441
 
2598
- // src/client/common/utils/userapi.ts
2599
- function isJsonObject(value) {
2600
- return typeof value === "object" && value !== null && !Array.isArray(value);
2601
- }
2602
- function readString(obj, key) {
2603
- const v = obj[key];
2604
- return typeof v === "string" ? v : void 0;
2605
- }
2606
- function readNumber(obj, key) {
2607
- const v = obj[key];
2608
- return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2609
- }
2610
- var MAX_ADDRESS_COUNT = 5;
2611
- var CanViewAppLogsPermission = "0x2fd3f2fe";
2612
- var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
2613
- var CanUpdateAppProfilePermission = "0x036fef61";
2614
- function getDefaultClientId() {
2615
- const version = true ? "0.2.0-dev.3" : "0.0.0";
2616
- return `ecloud-sdk/v${version}`;
2617
- }
2618
- var UserApiClient = class {
2619
- constructor(config, privateKey, rpcUrl, clientId) {
2620
- this.config = config;
2621
- if (privateKey) {
2622
- const privateKeyHex = addHexPrefix(privateKey);
2623
- this.account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
2624
- }
2625
- this.rpcUrl = rpcUrl;
2626
- this.clientId = clientId || getDefaultClientId();
2627
- }
2628
- async getInfos(appIDs, addressCount = 1) {
2629
- const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
2630
- const endpoint = `${this.config.userApiServerURL}/info`;
2631
- const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
2632
- const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
2633
- const result = await res.json();
2634
- return result.apps.map((app, i) => {
2635
- const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
2636
- const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
2637
- return {
2638
- address: appIDs[i],
2639
- status: app.app_status,
2640
- ip: app.ip,
2641
- machineType: app.machine_type,
2642
- profile: app.profile,
2643
- metrics: app.metrics,
2644
- evmAddresses,
2645
- solanaAddresses
2646
- };
2647
- });
2648
- }
2649
- /**
2650
- * Get app details from UserAPI (includes releases and build/provenance info when available).
2651
- *
2652
- * Endpoint: GET /apps/:appAddress
2653
- */
2654
- async getApp(appAddress) {
2655
- const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
2656
- const res = await this.makeAuthenticatedRequest(endpoint);
2657
- const raw = await res.json();
2658
- if (!isJsonObject(raw)) {
2659
- throw new Error("Unexpected /apps/:id response: expected object");
2660
- }
2661
- const id = readString(raw, "id");
2662
- if (!id) {
2663
- throw new Error("Unexpected /apps/:id response: missing 'id'");
2664
- }
2665
- const releasesRaw = raw.releases;
2666
- const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
2667
- return {
2668
- id,
2669
- creator: readString(raw, "creator"),
2670
- contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
2671
- releases
2672
- };
2673
- }
2674
- /**
2675
- * Get available SKUs (instance types) from UserAPI
2676
- */
2677
- async getSKUs() {
2678
- const endpoint = `${this.config.userApiServerURL}/skus`;
2679
- const response = await this.makeAuthenticatedRequest(endpoint);
2680
- const result = await response.json();
2681
- return {
2682
- skus: result.skus || result.SKUs || []
2683
- };
2684
- }
2685
- /**
2686
- * Get logs for an app
2687
- */
2688
- async getLogs(appID) {
2689
- const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
2690
- const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
2691
- return await response.text();
2692
- }
2693
- /**
2694
- * Get statuses for apps
2695
- */
2696
- async getStatuses(appIDs) {
2697
- const endpoint = `${this.config.userApiServerURL}/status`;
2698
- const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
2699
- const response = await this.makeAuthenticatedRequest(url);
2700
- const result = await response.json();
2701
- const apps = result.apps || result.Apps || [];
2702
- return apps.map((app, i) => ({
2703
- address: app.address || appIDs[i],
2704
- status: app.status || app.Status || ""
2705
- }));
2706
- }
2707
- /**
2708
- * Upload app profile information with optional image
2709
- */
2710
- async uploadAppProfile(appAddress, name, website, description, xURL, imagePath) {
2711
- const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
2712
- const formData = new import_form_data.default();
2713
- formData.append("name", name);
2714
- if (website) {
2715
- formData.append("website", website);
2716
- }
2717
- if (description) {
2718
- formData.append("description", description);
2719
- }
2720
- if (xURL) {
2721
- formData.append("xURL", xURL);
2722
- }
2723
- if (imagePath) {
2724
- const fs8 = await import("fs");
2725
- const path8 = await import("path");
2726
- const fileName = path8.basename(imagePath);
2727
- const fileBuffer = fs8.readFileSync(imagePath);
2728
- formData.append("image", fileBuffer, fileName);
2729
- }
2730
- const headers = {
2731
- "x-client-id": this.clientId,
2732
- ...formData.getHeaders()
2733
- };
2734
- if (this.account) {
2735
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
2736
- const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
2737
- Object.assign(headers, authHeaders);
2738
- }
2739
- try {
2740
- const response = await import_axios.default.post(endpoint, formData, {
2741
- headers,
2742
- maxRedirects: 0,
2743
- validateStatus: () => true,
2744
- // Don't throw on any status
2745
- maxContentLength: Infinity,
2746
- // Allow large file uploads
2747
- maxBodyLength: Infinity
2748
- // Allow large file uploads
2749
- });
2750
- const status = response.status;
2751
- if (status !== 200 && status !== 201) {
2752
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
2753
- if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
2754
- throw new Error(
2755
- `Cloudflare protection is blocking the request. This is likely due to bot detection.
2756
- Status: ${status}`
2757
- );
2758
- }
2759
- throw new Error(
2760
- `UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
2761
- );
2762
- }
2763
- return response.data;
2764
- } catch (error) {
2765
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
2766
- const cause = error.cause?.message || error.cause || error.message;
2767
- throw new Error(
2768
- `Failed to connect to UserAPI at ${endpoint}: ${cause}
2769
- Please check:
2770
- 1. Your internet connection
2771
- 2. The API server is accessible: ${this.config.userApiServerURL}
2772
- 3. Firewall/proxy settings`
2773
- );
2774
- }
2775
- throw error;
2776
- }
2777
- }
2778
- async makeAuthenticatedRequest(url, permission) {
2779
- const headers = {
2780
- "x-client-id": this.clientId
2781
- };
2782
- if (permission && this.account) {
2783
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
2784
- const authHeaders = await this.generateAuthHeaders(permission, expiry);
2785
- Object.assign(headers, authHeaders);
2786
- }
2787
- try {
2788
- const response = await import_axios.default.get(url, {
2789
- headers,
2790
- maxRedirects: 0,
2791
- validateStatus: () => true
2792
- // Don't throw on any status
2793
- });
2794
- const status = response.status;
2795
- const statusText = status >= 200 && status < 300 ? "OK" : "Error";
2796
- if (status < 200 || status >= 300) {
2797
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
2798
- throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
2799
- }
2800
- return {
2801
- json: async () => response.data,
2802
- text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
2803
- };
2804
- } catch (error) {
2805
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
2806
- const cause = error.cause?.message || error.cause || error.message;
2807
- throw new Error(
2808
- `Failed to connect to UserAPI at ${url}: ${cause}
2809
- Please check:
2810
- 1. Your internet connection
2811
- 2. The API server is accessible: ${this.config.userApiServerURL}
2812
- 3. Firewall/proxy settings`
2813
- );
2814
- }
2815
- throw error;
2816
- }
2817
- }
2818
- /**
2819
- * Generate authentication headers for UserAPI requests
2820
- */
2821
- async generateAuthHeaders(permission, expiry) {
2822
- if (!this.account) {
2823
- throw new Error("Private key required for authenticated requests");
2824
- }
2825
- if (!this.rpcUrl) {
2826
- throw new Error("RPC URL required for authenticated requests");
2827
- }
2828
- const chain = getChainFromID(this.config.chainID);
2829
- const publicClient = (0, import_viem4.createPublicClient)({
2830
- chain,
2831
- transport: (0, import_viem4.http)(this.rpcUrl)
2832
- });
2833
- const { signature } = await calculatePermissionSignature({
2834
- permission,
2835
- expiry,
2836
- appControllerAddress: this.config.appControllerAddress,
2837
- publicClient,
2838
- account: this.account
2839
- });
2840
- return {
2841
- Authorization: `Bearer ${stripHexPrefix(signature)}`,
2842
- "X-eigenx-expiry": expiry.toString()
2843
- };
2844
- }
2845
- };
2846
- function transformAppReleaseBuild(raw) {
2847
- if (!isJsonObject(raw)) return void 0;
2848
- const depsRaw = raw.dependencies;
2849
- const deps = isJsonObject(depsRaw) ? Object.fromEntries(
2850
- Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
2851
- const parsed = transformAppReleaseBuild(depRaw);
2852
- return parsed ? [[digest, parsed]] : [];
2853
- })
2854
- ) : void 0;
2855
- return {
2856
- buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
2857
- billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
2858
- repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
2859
- gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
2860
- status: readString(raw, "status"),
2861
- buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
2862
- imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
2863
- imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
2864
- imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
2865
- provenanceJson: raw.provenance_json ?? raw.provenanceJson,
2866
- provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
2867
- createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
2868
- updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
2869
- errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
2870
- dependencies: deps
2871
- };
2872
- }
2873
- function transformAppRelease(raw) {
2874
- if (!isJsonObject(raw)) return void 0;
2875
- return {
2876
- appId: readString(raw, "appId") ?? readString(raw, "app_id"),
2877
- rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
2878
- imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
2879
- registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
2880
- publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
2881
- encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
2882
- upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
2883
- createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
2884
- createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
2885
- build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
2886
- };
2887
- }
2888
-
2889
- // src/client/common/utils/billing.ts
2890
- function isSubscriptionActive(status) {
2891
- return status === "active" || status === "trialing";
2892
- }
2893
-
2894
2442
  // src/client/common/abis/AppController.json
2895
2443
  var AppController_default = [
2896
2444
  {
@@ -4446,17 +3994,10 @@ function formatETH(wei) {
4446
3994
  return trimmed;
4447
3995
  }
4448
3996
  async function estimateTransactionGas(options) {
4449
- const { privateKey, rpcUrl, environmentConfig, to, data, value = 0n } = options;
4450
- const privateKeyHex = addHexPrefix(privateKey);
4451
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4452
- const chain = getChainFromID(environmentConfig.chainID);
4453
- const publicClient = (0, import_viem5.createPublicClient)({
4454
- chain,
4455
- transport: (0, import_viem5.http)(rpcUrl)
4456
- });
3997
+ const { publicClient, from, to, data, value = 0n } = options;
4457
3998
  const fees = await publicClient.estimateFeesPerGas();
4458
3999
  const gasLimit = await publicClient.estimateGas({
4459
- account: account.address,
4000
+ account: from,
4460
4001
  to,
4461
4002
  data,
4462
4003
  value
@@ -4473,65 +4014,54 @@ async function estimateTransactionGas(options) {
4473
4014
  maxCostEth
4474
4015
  };
4475
4016
  }
4476
- async function calculateAppID(privateKey, rpcUrl, environmentConfig, salt) {
4477
- const privateKeyHex = addHexPrefix(privateKey);
4478
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4479
- const chain = getChainFromID(environmentConfig.chainID);
4480
- const publicClient = (0, import_viem5.createPublicClient)({
4481
- chain,
4482
- transport: (0, import_viem5.http)(rpcUrl)
4483
- });
4484
- const saltHexString = Buffer.from(salt).toString("hex");
4017
+ async function calculateAppID(options) {
4018
+ const { publicClient, environmentConfig, ownerAddress, salt } = options;
4019
+ const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4485
4020
  const paddedSaltHex = saltHexString.padStart(64, "0");
4486
4021
  const saltHex = `0x${paddedSaltHex}`;
4487
- const accountAddress = typeof account.address === "string" ? account.address : account.address.toString();
4488
4022
  const appID = await publicClient.readContract({
4489
4023
  address: environmentConfig.appControllerAddress,
4490
4024
  abi: AppController_default,
4491
4025
  functionName: "calculateAppId",
4492
- args: [accountAddress, saltHex]
4026
+ args: [ownerAddress, saltHex]
4493
4027
  });
4494
4028
  return appID;
4495
4029
  }
4496
4030
  async function prepareDeployBatch(options, logger) {
4497
- const { privateKey, rpcUrl, environmentConfig, salt, release, publicLogs } = options;
4498
- const privateKeyHex = addHexPrefix(privateKey);
4499
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4500
- const chain = getChainFromID(environmentConfig.chainID);
4501
- const publicClient = (0, import_viem5.createPublicClient)({
4502
- chain,
4503
- transport: (0, import_viem5.http)(rpcUrl)
4504
- });
4505
- const walletClient = (0, import_viem5.createWalletClient)({
4506
- account,
4507
- chain,
4508
- transport: (0, import_viem5.http)(rpcUrl)
4509
- });
4031
+ const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
4032
+ const account = walletClient.account;
4033
+ if (!account) {
4034
+ throw new Error("WalletClient must have an account attached");
4035
+ }
4510
4036
  logger.info("Calculating app ID...");
4511
- const appId = await calculateAppID(privateKeyHex, rpcUrl, environmentConfig, salt);
4512
- logger.info(`App ID: ${appId}`);
4037
+ const appId = await calculateAppID({
4038
+ publicClient,
4039
+ environmentConfig,
4040
+ ownerAddress: account.address,
4041
+ salt
4042
+ });
4513
4043
  logger.debug(`App ID calculated: ${appId}`);
4514
4044
  logger.debug(`This address will be used for acceptAdmin call`);
4515
- const saltHexString = Buffer.from(salt).toString("hex");
4045
+ const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4516
4046
  const paddedSaltHex = saltHexString.padStart(64, "0");
4517
4047
  const saltHex = `0x${paddedSaltHex}`;
4518
4048
  const releaseForViem = {
4519
4049
  rmsRelease: {
4520
4050
  artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4521
- digest: `0x${Buffer.from(artifact.digest).toString("hex").padStart(64, "0")}`,
4051
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4522
4052
  registry: artifact.registry
4523
4053
  })),
4524
4054
  upgradeByTime: release.rmsRelease.upgradeByTime
4525
4055
  },
4526
- publicEnv: `0x${Buffer.from(release.publicEnv).toString("hex")}`,
4527
- encryptedEnv: `0x${Buffer.from(release.encryptedEnv).toString("hex")}`
4056
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4057
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4528
4058
  };
4529
- const createData = (0, import_viem5.encodeFunctionData)({
4059
+ const createData = (0, import_viem3.encodeFunctionData)({
4530
4060
  abi: AppController_default,
4531
4061
  functionName: "createApp",
4532
4062
  args: [saltHex, releaseForViem]
4533
4063
  });
4534
- const acceptAdminData = (0, import_viem5.encodeFunctionData)({
4064
+ const acceptAdminData = (0, import_viem3.encodeFunctionData)({
4535
4065
  abi: PermissionController_default,
4536
4066
  functionName: "acceptAdmin",
4537
4067
  args: [appId]
@@ -4549,7 +4079,7 @@ async function prepareDeployBatch(options, logger) {
4549
4079
  }
4550
4080
  ];
4551
4081
  if (publicLogs) {
4552
- const anyoneCanViewLogsData = (0, import_viem5.encodeFunctionData)({
4082
+ const anyoneCanViewLogsData = (0, import_viem3.encodeFunctionData)({
4553
4083
  abi: PermissionController_default,
4554
4084
  functionName: "setAppointee",
4555
4085
  args: [
@@ -4593,17 +4123,7 @@ async function executeDeployBatch(data, context, gas, logger) {
4593
4123
  return { appId: data.appId, txHash };
4594
4124
  }
4595
4125
  async function deployApp(options, logger) {
4596
- const prepared = await prepareDeployBatch(
4597
- {
4598
- privateKey: options.privateKey,
4599
- rpcUrl: options.rpcUrl,
4600
- environmentConfig: options.environmentConfig,
4601
- salt: options.salt,
4602
- release: options.release,
4603
- publicLogs: options.publicLogs
4604
- },
4605
- logger
4606
- );
4126
+ const prepared = await prepareDeployBatch(options, logger);
4607
4127
  const data = {
4608
4128
  appId: prepared.appId,
4609
4129
  salt: prepared.salt,
@@ -4617,42 +4137,22 @@ async function deployApp(options, logger) {
4617
4137
  return executeDeployBatch(data, context, options.gas, logger);
4618
4138
  }
4619
4139
  async function prepareUpgradeBatch(options) {
4620
- const {
4621
- privateKey,
4622
- rpcUrl,
4623
- environmentConfig,
4624
- appId,
4625
- release,
4626
- publicLogs,
4627
- needsPermissionChange
4628
- } = options;
4629
- const privateKeyHex = addHexPrefix(privateKey);
4630
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4631
- const chain = getChainFromID(environmentConfig.chainID);
4632
- const publicClient = (0, import_viem5.createPublicClient)({
4633
- chain,
4634
- transport: (0, import_viem5.http)(rpcUrl)
4635
- });
4636
- const walletClient = (0, import_viem5.createWalletClient)({
4637
- account,
4638
- chain,
4639
- transport: (0, import_viem5.http)(rpcUrl)
4640
- });
4140
+ const { walletClient, publicClient, environmentConfig, appID, release, publicLogs, needsPermissionChange } = options;
4641
4141
  const releaseForViem = {
4642
4142
  rmsRelease: {
4643
4143
  artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4644
- digest: `0x${Buffer.from(artifact.digest).toString("hex").padStart(64, "0")}`,
4144
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4645
4145
  registry: artifact.registry
4646
4146
  })),
4647
4147
  upgradeByTime: release.rmsRelease.upgradeByTime
4648
4148
  },
4649
- publicEnv: `0x${Buffer.from(release.publicEnv).toString("hex")}`,
4650
- encryptedEnv: `0x${Buffer.from(release.encryptedEnv).toString("hex")}`
4149
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4150
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4651
4151
  };
4652
- const upgradeData = (0, import_viem5.encodeFunctionData)({
4152
+ const upgradeData = (0, import_viem3.encodeFunctionData)({
4653
4153
  abi: AppController_default,
4654
4154
  functionName: "upgradeApp",
4655
- args: [appId, releaseForViem]
4155
+ args: [appID, releaseForViem]
4656
4156
  });
4657
4157
  const executions = [
4658
4158
  {
@@ -4663,11 +4163,11 @@ async function prepareUpgradeBatch(options) {
4663
4163
  ];
4664
4164
  if (needsPermissionChange) {
4665
4165
  if (publicLogs) {
4666
- const addLogsData = (0, import_viem5.encodeFunctionData)({
4166
+ const addLogsData = (0, import_viem3.encodeFunctionData)({
4667
4167
  abi: PermissionController_default,
4668
4168
  functionName: "setAppointee",
4669
4169
  args: [
4670
- appId,
4170
+ appID,
4671
4171
  "0x493219d9949348178af1f58740655951a8cd110c",
4672
4172
  // AnyoneCanCallAddress
4673
4173
  "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
@@ -4682,11 +4182,11 @@ async function prepareUpgradeBatch(options) {
4682
4182
  callData: addLogsData
4683
4183
  });
4684
4184
  } else {
4685
- const removeLogsData = (0, import_viem5.encodeFunctionData)({
4185
+ const removeLogsData = (0, import_viem3.encodeFunctionData)({
4686
4186
  abi: PermissionController_default,
4687
4187
  functionName: "removeAppointee",
4688
4188
  args: [
4689
- appId,
4189
+ appID,
4690
4190
  "0x493219d9949348178af1f58740655951a8cd110c",
4691
4191
  // AnyoneCanCallAddress
4692
4192
  "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
@@ -4703,7 +4203,7 @@ async function prepareUpgradeBatch(options) {
4703
4203
  }
4704
4204
  }
4705
4205
  return {
4706
- appId,
4206
+ appId: appID,
4707
4207
  executions,
4708
4208
  walletClient,
4709
4209
  publicClient,
@@ -4726,15 +4226,7 @@ async function executeUpgradeBatch(data, context, gas, logger) {
4726
4226
  return txHash;
4727
4227
  }
4728
4228
  async function upgradeApp(options, logger) {
4729
- const prepared = await prepareUpgradeBatch({
4730
- privateKey: options.privateKey,
4731
- rpcUrl: options.rpcUrl,
4732
- environmentConfig: options.environmentConfig,
4733
- appId: options.appId,
4734
- release: options.release,
4735
- publicLogs: options.publicLogs,
4736
- needsPermissionChange: options.needsPermissionChange
4737
- });
4229
+ const prepared = await prepareUpgradeBatch(options);
4738
4230
  const data = {
4739
4231
  appId: prepared.appId,
4740
4232
  executions: prepared.executions
@@ -4747,29 +4239,12 @@ async function upgradeApp(options, logger) {
4747
4239
  return executeUpgradeBatch(data, context, options.gas, logger);
4748
4240
  }
4749
4241
  async function sendAndWaitForTransaction(options, logger) {
4750
- const {
4751
- privateKey,
4752
- rpcUrl,
4753
- environmentConfig,
4754
- to,
4755
- data,
4756
- value = 0n,
4757
- pendingMessage,
4758
- txDescription,
4759
- gas
4760
- } = options;
4761
- const privateKeyHex = addHexPrefix(privateKey);
4762
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4242
+ const { walletClient, publicClient, environmentConfig, to, data, value = 0n, pendingMessage, txDescription, gas } = options;
4243
+ const account = walletClient.account;
4244
+ if (!account) {
4245
+ throw new Error("WalletClient must have an account attached");
4246
+ }
4763
4247
  const chain = getChainFromID(environmentConfig.chainID);
4764
- const publicClient = (0, import_viem5.createPublicClient)({
4765
- chain,
4766
- transport: (0, import_viem5.http)(rpcUrl)
4767
- });
4768
- const walletClient = (0, import_viem5.createWalletClient)({
4769
- account,
4770
- chain,
4771
- transport: (0, import_viem5.http)(rpcUrl)
4772
- });
4773
4248
  if (pendingMessage) {
4774
4249
  logger.info(`
4775
4250
  ${pendingMessage}`);
@@ -4780,7 +4255,10 @@ ${pendingMessage}`);
4780
4255
  data,
4781
4256
  value,
4782
4257
  ...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
4783
- ...gas?.maxPriorityFeePerGas && { maxPriorityFeePerGas: gas.maxPriorityFeePerGas }
4258
+ ...gas?.maxPriorityFeePerGas && {
4259
+ maxPriorityFeePerGas: gas.maxPriorityFeePerGas
4260
+ },
4261
+ chain
4784
4262
  });
4785
4263
  logger.info(`Transaction sent: ${hash}`);
4786
4264
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
@@ -4795,7 +4273,7 @@ ${pendingMessage}`);
4795
4273
  } catch (callError) {
4796
4274
  if (callError.data) {
4797
4275
  try {
4798
- const decoded = (0, import_viem5.decodeErrorResult)({
4276
+ const decoded = (0, import_viem3.decodeErrorResult)({
4799
4277
  abi: AppController_default,
4800
4278
  data: callError.data
4801
4279
  });
@@ -4846,12 +4324,7 @@ function formatAppControllerError(decoded) {
4846
4324
  return new Error(`contract error: ${errorName}`);
4847
4325
  }
4848
4326
  }
4849
- async function getActiveAppCount(rpcUrl, environmentConfig, user) {
4850
- const chain = getChainFromID(environmentConfig.chainID);
4851
- const publicClient = (0, import_viem5.createPublicClient)({
4852
- chain,
4853
- transport: (0, import_viem5.http)(rpcUrl)
4854
- });
4327
+ async function getActiveAppCount(publicClient, environmentConfig, user) {
4855
4328
  const count = await publicClient.readContract({
4856
4329
  address: environmentConfig.appControllerAddress,
4857
4330
  abi: AppController_default,
@@ -4860,12 +4333,7 @@ async function getActiveAppCount(rpcUrl, environmentConfig, user) {
4860
4333
  });
4861
4334
  return Number(count);
4862
4335
  }
4863
- async function getMaxActiveAppsPerUser(rpcUrl, environmentConfig, user) {
4864
- const chain = getChainFromID(environmentConfig.chainID);
4865
- const publicClient = (0, import_viem5.createPublicClient)({
4866
- chain,
4867
- transport: (0, import_viem5.http)(rpcUrl)
4868
- });
4336
+ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
4869
4337
  const quota = await publicClient.readContract({
4870
4338
  address: environmentConfig.appControllerAddress,
4871
4339
  abi: AppController_default,
@@ -4874,12 +4342,7 @@ async function getMaxActiveAppsPerUser(rpcUrl, environmentConfig, user) {
4874
4342
  });
4875
4343
  return Number(quota);
4876
4344
  }
4877
- async function getAppsByDeveloper(rpcUrl, environmentConfig, developer, offset, limit) {
4878
- const chain = getChainFromID(environmentConfig.chainID);
4879
- const publicClient = (0, import_viem5.createPublicClient)({
4880
- chain,
4881
- transport: (0, import_viem5.http)(rpcUrl)
4882
- });
4345
+ async function getAppsByDeveloper(publicClient, environmentConfig, developer, offset, limit) {
4883
4346
  const result = await publicClient.readContract({
4884
4347
  address: environmentConfig.appControllerAddress,
4885
4348
  abi: AppController_default,
@@ -4891,12 +4354,12 @@ async function getAppsByDeveloper(rpcUrl, environmentConfig, developer, offset,
4891
4354
  appConfigs: result[1]
4892
4355
  };
4893
4356
  }
4894
- async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
4357
+ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 100n) {
4895
4358
  let offset = 0n;
4896
4359
  const allApps = [];
4897
4360
  const allConfigs = [];
4898
4361
  while (true) {
4899
- const { apps, appConfigs } = await getAppsByDeveloper(rpcUrl, env, developer, offset, pageSize);
4362
+ const { apps, appConfigs } = await getAppsByDeveloper(publicClient, env, developer, offset, pageSize);
4900
4363
  if (apps.length === 0) break;
4901
4364
  allApps.push(...apps);
4902
4365
  allConfigs.push(...appConfigs);
@@ -4908,12 +4371,7 @@ async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
4908
4371
  appConfigs: allConfigs
4909
4372
  };
4910
4373
  }
4911
- async function getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, appIDs) {
4912
- const chain = getChainFromID(environmentConfig.chainID);
4913
- const publicClient = (0, import_viem5.createPublicClient)({
4914
- chain,
4915
- transport: (0, import_viem5.http)(rpcUrl)
4916
- });
4374
+ async function getAppLatestReleaseBlockNumbers(publicClient, environmentConfig, appIDs) {
4917
4375
  const results = await Promise.all(
4918
4376
  appIDs.map(
4919
4377
  (appID) => publicClient.readContract({
@@ -4933,12 +4391,7 @@ async function getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, appIDs
4933
4391
  }
4934
4392
  return blockNumbers;
4935
4393
  }
4936
- async function getBlockTimestamps(rpcUrl, environmentConfig, blockNumbers) {
4937
- const chain = getChainFromID(environmentConfig.chainID);
4938
- const publicClient = (0, import_viem5.createPublicClient)({
4939
- chain,
4940
- transport: (0, import_viem5.http)(rpcUrl)
4941
- });
4394
+ async function getBlockTimestamps(publicClient, blockNumbers) {
4942
4395
  const uniqueBlockNumbers = [...new Set(blockNumbers)].filter((n) => n > 0);
4943
4396
  const timestamps = /* @__PURE__ */ new Map();
4944
4397
  const blocks = await Promise.all(
@@ -4955,67 +4408,34 @@ async function getBlockTimestamps(rpcUrl, environmentConfig, blockNumbers) {
4955
4408
  return timestamps;
4956
4409
  }
4957
4410
  async function isDelegated(options) {
4958
- const { privateKey, rpcUrl, environmentConfig } = options;
4959
- const privateKeyHex = addHexPrefix(privateKey);
4960
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4961
- const chain = getChainFromID(environmentConfig.chainID);
4962
- const publicClient = (0, import_viem5.createPublicClient)({
4963
- chain,
4964
- transport: (0, import_viem5.http)(rpcUrl)
4965
- });
4411
+ const { publicClient, environmentConfig, address } = options;
4966
4412
  return checkERC7702Delegation(
4967
4413
  publicClient,
4968
- account.address,
4414
+ address,
4969
4415
  environmentConfig.erc7702DelegatorAddress
4970
4416
  );
4971
4417
  }
4972
4418
  async function undelegate(options, logger) {
4973
- const { privateKey, rpcUrl, environmentConfig } = options;
4974
- const privateKeyHex = addHexPrefix(privateKey);
4975
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4419
+ const { walletClient, publicClient, environmentConfig } = options;
4420
+ const account = walletClient.account;
4421
+ if (!account) {
4422
+ throw new Error("WalletClient must have an account attached");
4423
+ }
4976
4424
  const chain = getChainFromID(environmentConfig.chainID);
4977
- const publicClient = (0, import_viem5.createPublicClient)({
4978
- chain,
4979
- transport: (0, import_viem5.http)(rpcUrl)
4980
- });
4981
- const walletClient = (0, import_viem5.createWalletClient)({
4982
- account,
4983
- chain,
4984
- transport: (0, import_viem5.http)(rpcUrl)
4985
- });
4986
4425
  const transactionNonce = await publicClient.getTransactionCount({
4987
4426
  address: account.address,
4988
4427
  blockTag: "pending"
4989
4428
  });
4990
4429
  const chainId = await publicClient.getChainId();
4991
4430
  const authorizationNonce = BigInt(transactionNonce) + 1n;
4992
- const authorization = {
4993
- chainId: Number(chainId),
4994
- address: "0x0000000000000000000000000000000000000000",
4995
- // Empty address = undelegate
4996
- nonce: authorizationNonce
4997
- };
4998
- const sighash = (0, import_utils.hashAuthorization)({
4999
- chainId: authorization.chainId,
5000
- contractAddress: authorization.address,
5001
- nonce: Number(authorization.nonce)
5002
- });
5003
- const sig = await (0, import_accounts3.sign)({
5004
- hash: sighash,
5005
- privateKey: privateKeyHex
4431
+ logger.debug("Signing undelegate authorization");
4432
+ const signedAuthorization = await walletClient.signAuthorization({
4433
+ contractAddress: "0x0000000000000000000000000000000000000000",
4434
+ chainId,
4435
+ nonce: Number(authorizationNonce),
4436
+ account
5006
4437
  });
5007
- const v = Number(sig.v);
5008
- const yParity = v === 27 ? 0 : 1;
5009
- const authorizationList = [
5010
- {
5011
- chainId: authorization.chainId,
5012
- address: authorization.address,
5013
- nonce: Number(authorization.nonce),
5014
- r: sig.r,
5015
- s: sig.s,
5016
- yParity
5017
- }
5018
- ];
4438
+ const authorizationList = [signedAuthorization];
5019
4439
  const hash = await walletClient.sendTransaction({
5020
4440
  account,
5021
4441
  to: account.address,
@@ -5023,7 +4443,8 @@ async function undelegate(options, logger) {
5023
4443
  data: "0x",
5024
4444
  // Empty data
5025
4445
  value: 0n,
5026
- authorizationList
4446
+ authorizationList,
4447
+ chain
5027
4448
  });
5028
4449
  logger.info(`Transaction sent: ${hash}`);
5029
4450
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
@@ -5034,360 +4455,705 @@ async function undelegate(options, logger) {
5034
4455
  return hash;
5035
4456
  }
5036
4457
 
5037
- // src/client/common/contract/watcher.ts
5038
- var WATCH_POLL_INTERVAL_SECONDS = 5;
5039
- var APP_STATUS_RUNNING = "Running";
5040
- var APP_STATUS_FAILED = "Failed";
5041
- async function watchUntilRunning(options, logger) {
5042
- const { environmentConfig, appId, privateKey, rpcUrl, clientId } = options;
5043
- const userApiClient = new UserApiClient(environmentConfig, privateKey, rpcUrl, clientId);
5044
- let initialStatus;
5045
- let initialIP;
5046
- let hasChanged = false;
5047
- const stopCondition = (status, ip) => {
5048
- if (!initialStatus) {
5049
- initialStatus = status;
5050
- initialIP = ip;
5051
- }
5052
- if (status !== initialStatus) {
5053
- hasChanged = true;
5054
- }
5055
- if (status === APP_STATUS_RUNNING && ip) {
5056
- if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
5057
- if (!initialIP || initialIP === "No IP assigned") {
5058
- logger.info(`App is now running with IP: ${ip}`);
5059
- } else {
5060
- logger.info("App is now running");
5061
- }
5062
- return true;
5063
- }
5064
- }
5065
- if (status === APP_STATUS_FAILED) {
5066
- throw new Error(`App entered ${status} state`);
5067
- }
5068
- return false;
5069
- };
5070
- while (true) {
5071
- try {
5072
- const info = await userApiClient.getInfos([appId], 1);
5073
- if (info.length === 0) {
5074
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5075
- continue;
5076
- }
5077
- const appInfo = info[0];
5078
- const currentStatus = appInfo.status;
5079
- const currentIP = appInfo.ip || "";
5080
- if (stopCondition(currentStatus, currentIP)) {
5081
- return currentIP || void 0;
5082
- }
5083
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5084
- } catch (error) {
5085
- logger.warn(`Failed to fetch app info: ${error.message}`);
5086
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5087
- }
4458
+ // src/client/common/utils/userapi.ts
4459
+ var import_axios = __toESM(require("axios"), 1);
4460
+
4461
+ // src/client/common/utils/auth.ts
4462
+ var import_viem4 = require("viem");
4463
+ var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
4464
+ "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
4465
+ ]);
4466
+ async function calculatePermissionSignature(options) {
4467
+ const { permission, expiry, appControllerAddress, publicClient, walletClient } = options;
4468
+ const digest = await publicClient.readContract({
4469
+ address: appControllerAddress,
4470
+ abi: APP_CONTROLLER_ABI,
4471
+ functionName: "calculateApiPermissionDigestHash",
4472
+ args: [permission, expiry]
4473
+ });
4474
+ const account = walletClient.account;
4475
+ if (!account) {
4476
+ throw new Error("WalletClient must have an account attached");
5088
4477
  }
4478
+ const signature = await walletClient.signMessage({
4479
+ account,
4480
+ message: { raw: digest }
4481
+ });
4482
+ return { signature, digest };
5089
4483
  }
5090
- var APP_STATUS_STOPPED = "Stopped";
5091
- async function watchUntilUpgradeComplete(options, logger) {
5092
- const { environmentConfig, appId, privateKey, rpcUrl, clientId } = options;
5093
- const userApiClient = new UserApiClient(environmentConfig, privateKey, rpcUrl, clientId);
5094
- let initialStatus;
5095
- let initialIP;
5096
- let hasChanged = false;
5097
- const stopCondition = (status, ip) => {
5098
- if (!initialStatus) {
5099
- initialStatus = status;
5100
- initialIP = ip;
5101
- if (status === APP_STATUS_STOPPED && ip) {
5102
- logger.info("App upgrade complete.");
5103
- logger.info(`Status: ${status}`);
5104
- logger.info(`To start the app, run: ecloud compute app start ${appId}`);
5105
- return true;
5106
- }
5107
- }
5108
- if (status !== initialStatus) {
5109
- hasChanged = true;
5110
- }
5111
- if (status === APP_STATUS_STOPPED && ip && hasChanged) {
5112
- logger.info("App upgrade complete.");
5113
- logger.info(`Status: ${status}`);
5114
- logger.info(`To start the app, run: ecloud compute app start ${appId}`);
5115
- return true;
5116
- }
5117
- if (status === APP_STATUS_RUNNING && ip && hasChanged) {
5118
- if (!initialIP || initialIP === "No IP assigned") {
5119
- logger.info(`App is now running with IP: ${ip}`);
5120
- } else {
5121
- logger.info("App is now running");
5122
- }
5123
- return true;
5124
- }
5125
- if (status === APP_STATUS_FAILED) {
5126
- throw new Error(`App entered ${status} state`);
4484
+ var generateBillingSigData = (product, expiry) => {
4485
+ return {
4486
+ domain: {
4487
+ name: "EigenCloud Billing API",
4488
+ version: "1"
4489
+ },
4490
+ types: {
4491
+ BillingAuth: [
4492
+ { name: "product", type: "string" },
4493
+ { name: "expiry", type: "uint256" }
4494
+ ]
4495
+ },
4496
+ primaryType: "BillingAuth",
4497
+ message: {
4498
+ product,
4499
+ expiry
5127
4500
  }
5128
- return false;
5129
4501
  };
5130
- while (true) {
5131
- try {
5132
- const info = await userApiClient.getInfos([appId], 1);
5133
- if (info.length === 0) {
5134
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5135
- continue;
5136
- }
5137
- const appInfo = info[0];
5138
- const currentStatus = appInfo.status;
5139
- const currentIP = appInfo.ip || "";
5140
- if (stopCondition(currentStatus, currentIP)) {
5141
- return;
5142
- }
5143
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5144
- } catch (error) {
5145
- logger.warn(`Failed to fetch app info: ${error.message}`);
5146
- await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5147
- }
4502
+ };
4503
+ async function calculateBillingAuthSignature(options) {
4504
+ const { walletClient, product, expiry } = options;
4505
+ const account = walletClient.account;
4506
+ if (!account) {
4507
+ throw new Error("WalletClient must have an account attached");
5148
4508
  }
5149
- }
5150
- function sleep(ms) {
5151
- return new Promise((resolve2) => setTimeout(resolve2, ms));
4509
+ const signature = await walletClient.signTypedData({
4510
+ account,
4511
+ ...generateBillingSigData(product, expiry)
4512
+ });
4513
+ return { signature, expiry };
5152
4514
  }
5153
4515
 
5154
- // src/client/common/utils/validation.ts
5155
- var import_fs = __toESM(require("fs"), 1);
5156
- var import_path = __toESM(require("path"), 1);
5157
- var import_viem6 = require("viem");
5158
- function validateAppName(name) {
5159
- if (!name) {
5160
- throw new Error("App name cannot be empty");
5161
- }
5162
- if (name.includes(" ")) {
5163
- throw new Error("App name cannot contain spaces");
5164
- }
5165
- if (name.length > 50) {
5166
- throw new Error("App name cannot be longer than 50 characters");
5167
- }
5168
- }
5169
- function validateImageReference(value) {
5170
- if (!value) {
5171
- return "Image reference cannot be empty";
5172
- }
5173
- if (!value.includes("/")) {
5174
- return "Image reference must contain at least one /";
5175
- }
5176
- return true;
5177
- }
5178
- function assertValidImageReference(value) {
5179
- const result = validateImageReference(value);
5180
- if (result !== true) {
5181
- throw new Error(result);
5182
- }
4516
+ // src/client/common/utils/userapi.ts
4517
+ function isJsonObject(value) {
4518
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5183
4519
  }
5184
- function extractAppNameFromImage(imageRef) {
5185
- const parts = imageRef.split("/");
5186
- let imageName = parts.length > 1 ? parts[parts.length - 1] : imageRef;
5187
- if (imageName.includes(":")) {
5188
- imageName = imageName.split(":")[0];
5189
- }
5190
- return imageName;
4520
+ function readString(obj, key) {
4521
+ const v = obj[key];
4522
+ return typeof v === "string" ? v : void 0;
5191
4523
  }
5192
- function validateFilePath(value) {
5193
- if (!value) {
5194
- return "File path cannot be empty";
5195
- }
5196
- if (!import_fs.default.existsSync(value)) {
5197
- return "File does not exist";
5198
- }
5199
- return true;
4524
+ function readNumber(obj, key) {
4525
+ const v = obj[key];
4526
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
5200
4527
  }
5201
- function assertValidFilePath(value) {
5202
- const result = validateFilePath(value);
5203
- if (result !== true) {
5204
- throw new Error(result);
5205
- }
4528
+ var MAX_ADDRESS_COUNT = 5;
4529
+ var CanViewAppLogsPermission = "0x2fd3f2fe";
4530
+ var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
4531
+ var CanUpdateAppProfilePermission = "0x036fef61";
4532
+ function getDefaultClientId() {
4533
+ const version = true ? "0.2.1-dev" : "0.0.0";
4534
+ return `ecloud-sdk/v${version}`;
5206
4535
  }
5207
- function validateInstanceTypeSKU(sku, availableTypes) {
5208
- if (!sku) {
5209
- throw new Error("Instance type SKU cannot be empty");
4536
+ var UserApiClient = class {
4537
+ constructor(config, walletClient, publicClient, clientId) {
4538
+ this.config = config;
4539
+ this.walletClient = walletClient;
4540
+ this.publicClient = publicClient;
4541
+ this.clientId = clientId || getDefaultClientId();
5210
4542
  }
5211
- for (const it of availableTypes) {
5212
- if (it.sku === sku) {
5213
- return sku;
4543
+ /**
4544
+ * Get the address of the connected wallet
4545
+ */
4546
+ get address() {
4547
+ const account = this.walletClient.account;
4548
+ if (!account) {
4549
+ throw new Error("WalletClient must have an account attached");
5214
4550
  }
4551
+ return account.address;
5215
4552
  }
5216
- const validSKUs = availableTypes.map((it) => it.sku).join(", ");
5217
- throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
5218
- }
5219
- function validatePrivateKeyFormat(key) {
5220
- const keyWithoutPrefix = stripHexPrefix(key);
5221
- if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
5222
- return false;
5223
- }
5224
- return true;
5225
- }
5226
- function assertValidPrivateKey(key) {
5227
- if (!key) {
5228
- throw new Error("Private key is required");
4553
+ async getInfos(appIDs, addressCount = 1) {
4554
+ const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
4555
+ const endpoint = `${this.config.userApiServerURL}/info`;
4556
+ const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
4557
+ const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
4558
+ const result = await res.json();
4559
+ return result.apps.map((app, i) => {
4560
+ const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
4561
+ const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
4562
+ return {
4563
+ address: appIDs[i],
4564
+ status: app.app_status,
4565
+ ip: app.ip,
4566
+ machineType: app.machine_type,
4567
+ profile: app.profile,
4568
+ metrics: app.metrics,
4569
+ evmAddresses,
4570
+ solanaAddresses
4571
+ };
4572
+ });
5229
4573
  }
5230
- if (!validatePrivateKeyFormat(key)) {
5231
- throw new Error(
5232
- "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
5233
- );
4574
+ /**
4575
+ * Get app details from UserAPI (includes releases and build/provenance info when available).
4576
+ *
4577
+ * Endpoint: GET /apps/:appAddress
4578
+ */
4579
+ async getApp(appAddress) {
4580
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
4581
+ const res = await this.makeAuthenticatedRequest(endpoint);
4582
+ const raw = await res.json();
4583
+ if (!isJsonObject(raw)) {
4584
+ throw new Error("Unexpected /apps/:id response: expected object");
4585
+ }
4586
+ const id = readString(raw, "id");
4587
+ if (!id) {
4588
+ throw new Error("Unexpected /apps/:id response: missing 'id'");
4589
+ }
4590
+ const releasesRaw = raw.releases;
4591
+ const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
4592
+ return {
4593
+ id,
4594
+ creator: readString(raw, "creator"),
4595
+ contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
4596
+ releases
4597
+ };
5234
4598
  }
5235
- }
5236
- function validateURL(rawURL) {
5237
- if (!rawURL.trim()) {
5238
- return "URL cannot be empty";
4599
+ /**
4600
+ * Get available SKUs (instance types) from UserAPI
4601
+ */
4602
+ async getSKUs() {
4603
+ const endpoint = `${this.config.userApiServerURL}/skus`;
4604
+ const response = await this.makeAuthenticatedRequest(endpoint);
4605
+ const result = await response.json();
4606
+ return {
4607
+ skus: result.skus || result.SKUs || []
4608
+ };
5239
4609
  }
5240
- try {
5241
- const url = new URL(rawURL);
5242
- if (url.protocol !== "http:" && url.protocol !== "https:") {
5243
- return "URL scheme must be http or https";
5244
- }
5245
- } catch {
5246
- return "Invalid URL format";
4610
+ /**
4611
+ * Get logs for an app
4612
+ */
4613
+ async getLogs(appID) {
4614
+ const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
4615
+ const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
4616
+ return await response.text();
5247
4617
  }
5248
- return void 0;
5249
- }
5250
- var VALID_X_HOSTS = ["twitter.com", "www.twitter.com", "x.com", "www.x.com"];
5251
- function validateXURL(rawURL) {
5252
- const urlErr = validateURL(rawURL);
5253
- if (urlErr) {
5254
- return urlErr;
4618
+ /**
4619
+ * Get statuses for apps
4620
+ */
4621
+ async getStatuses(appIDs) {
4622
+ const endpoint = `${this.config.userApiServerURL}/status`;
4623
+ const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
4624
+ const response = await this.makeAuthenticatedRequest(url);
4625
+ const result = await response.json();
4626
+ const apps = result.apps || result.Apps || [];
4627
+ return apps.map((app, i) => ({
4628
+ address: app.address || appIDs[i],
4629
+ status: app.status || app.Status || ""
4630
+ }));
5255
4631
  }
5256
- try {
5257
- const url = new URL(rawURL);
5258
- const host = url.hostname.toLowerCase();
5259
- if (!VALID_X_HOSTS.includes(host)) {
5260
- return "URL must be a valid X/Twitter URL (x.com or twitter.com)";
4632
+ /**
4633
+ * Upload app profile information with optional image
4634
+ *
4635
+ * @param appAddress - The app's contract address
4636
+ * @param name - Display name for the app
4637
+ * @param options - Optional fields including website, description, xURL, and image
4638
+ * @param options.image - Image file as Blob or File (browser: from input element, Node.js: new Blob([buffer]))
4639
+ * @param options.imageName - Filename for the image (required if image is provided)
4640
+ */
4641
+ async uploadAppProfile(appAddress, name, options) {
4642
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
4643
+ const formData = new FormData();
4644
+ formData.append("name", name);
4645
+ if (options?.website) {
4646
+ formData.append("website", options.website);
5261
4647
  }
5262
- if (!url.pathname || url.pathname === "/") {
5263
- return "X URL must include a username or profile path";
4648
+ if (options?.description) {
4649
+ formData.append("description", options.description);
4650
+ }
4651
+ if (options?.xURL) {
4652
+ formData.append("xURL", options.xURL);
4653
+ }
4654
+ if (options?.image) {
4655
+ const fileName = options.image instanceof File ? options.image.name : options.imageName || "image";
4656
+ formData.append("image", options.image, fileName);
4657
+ }
4658
+ const headers = {
4659
+ "x-client-id": this.clientId
4660
+ };
4661
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
4662
+ const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
4663
+ Object.assign(headers, authHeaders);
4664
+ try {
4665
+ const response = await import_axios.default.post(endpoint, formData, {
4666
+ headers,
4667
+ maxRedirects: 0,
4668
+ validateStatus: () => true,
4669
+ // Don't throw on any status
4670
+ maxContentLength: Infinity,
4671
+ // Allow large file uploads
4672
+ maxBodyLength: Infinity
4673
+ // Allow large file uploads
4674
+ });
4675
+ const status = response.status;
4676
+ if (status !== 200 && status !== 201) {
4677
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
4678
+ if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
4679
+ throw new Error(
4680
+ `Cloudflare protection is blocking the request. This is likely due to bot detection.
4681
+ Status: ${status}`
4682
+ );
4683
+ }
4684
+ throw new Error(
4685
+ `UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
4686
+ );
4687
+ }
4688
+ return response.data;
4689
+ } catch (error) {
4690
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
4691
+ const cause = error.cause?.message || error.cause || error.message;
4692
+ throw new Error(
4693
+ `Failed to connect to UserAPI at ${endpoint}: ${cause}
4694
+ Please check:
4695
+ 1. Your internet connection
4696
+ 2. The API server is accessible: ${this.config.userApiServerURL}
4697
+ 3. Firewall/proxy settings`
4698
+ );
4699
+ }
4700
+ throw error;
5264
4701
  }
5265
- } catch {
5266
- return "Invalid X URL format";
5267
4702
  }
5268
- return void 0;
5269
- }
5270
- var MAX_DESCRIPTION_LENGTH = 1e3;
5271
- function validateDescription(description) {
5272
- if (!description.trim()) {
5273
- return "Description cannot be empty";
4703
+ async makeAuthenticatedRequest(url, permission) {
4704
+ const headers = {
4705
+ "x-client-id": this.clientId
4706
+ };
4707
+ if (permission) {
4708
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
4709
+ const authHeaders = await this.generateAuthHeaders(permission, expiry);
4710
+ Object.assign(headers, authHeaders);
4711
+ }
4712
+ try {
4713
+ const response = await import_axios.default.get(url, {
4714
+ headers,
4715
+ maxRedirects: 0,
4716
+ validateStatus: () => true
4717
+ // Don't throw on any status
4718
+ });
4719
+ const status = response.status;
4720
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
4721
+ if (status < 200 || status >= 300) {
4722
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
4723
+ throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
4724
+ }
4725
+ return {
4726
+ json: async () => response.data,
4727
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
4728
+ };
4729
+ } catch (error) {
4730
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
4731
+ const cause = error.cause?.message || error.cause || error.message;
4732
+ throw new Error(
4733
+ `Failed to connect to UserAPI at ${url}: ${cause}
4734
+ Please check:
4735
+ 1. Your internet connection
4736
+ 2. The API server is accessible: ${this.config.userApiServerURL}
4737
+ 3. Firewall/proxy settings`
4738
+ );
4739
+ }
4740
+ throw error;
4741
+ }
5274
4742
  }
5275
- if (description.length > MAX_DESCRIPTION_LENGTH) {
5276
- return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
4743
+ /**
4744
+ * Generate authentication headers for UserAPI requests
4745
+ */
4746
+ async generateAuthHeaders(permission, expiry) {
4747
+ const { signature } = await calculatePermissionSignature({
4748
+ permission,
4749
+ expiry,
4750
+ appControllerAddress: this.config.appControllerAddress,
4751
+ publicClient: this.publicClient,
4752
+ walletClient: this.walletClient
4753
+ });
4754
+ return {
4755
+ Authorization: `Bearer ${stripHexPrefix(signature)}`,
4756
+ "X-eigenx-expiry": expiry.toString()
4757
+ };
5277
4758
  }
5278
- return void 0;
4759
+ };
4760
+ function transformAppReleaseBuild(raw) {
4761
+ if (!isJsonObject(raw)) return void 0;
4762
+ const depsRaw = raw.dependencies;
4763
+ const deps = isJsonObject(depsRaw) ? Object.fromEntries(
4764
+ Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
4765
+ const parsed = transformAppReleaseBuild(depRaw);
4766
+ return parsed ? [[digest, parsed]] : [];
4767
+ })
4768
+ ) : void 0;
4769
+ return {
4770
+ buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
4771
+ billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
4772
+ repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
4773
+ gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
4774
+ status: readString(raw, "status"),
4775
+ buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
4776
+ imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
4777
+ imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
4778
+ imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
4779
+ provenanceJson: raw.provenance_json ?? raw.provenanceJson,
4780
+ provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
4781
+ createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
4782
+ updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
4783
+ errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
4784
+ dependencies: deps
4785
+ };
5279
4786
  }
5280
- var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
5281
- var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
5282
- function validateImagePath(filePath) {
5283
- const cleanedPath = filePath.trim().replace(/^["']|["']$/g, "");
5284
- if (!cleanedPath) {
5285
- return "Image path cannot be empty";
4787
+ function transformAppRelease(raw) {
4788
+ if (!isJsonObject(raw)) return void 0;
4789
+ return {
4790
+ appId: readString(raw, "appId") ?? readString(raw, "app_id"),
4791
+ rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
4792
+ imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
4793
+ registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
4794
+ publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
4795
+ encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
4796
+ upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
4797
+ createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
4798
+ createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
4799
+ build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
4800
+ };
4801
+ }
4802
+
4803
+ // src/client/common/contract/watcher.ts
4804
+ var WATCH_POLL_INTERVAL_SECONDS = 5;
4805
+ var APP_STATUS_RUNNING = "Running";
4806
+ var APP_STATUS_FAILED = "Failed";
4807
+ async function watchUntilRunning(options, logger) {
4808
+ const { walletClient, publicClient, environmentConfig, appId } = options;
4809
+ const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
4810
+ let initialStatus;
4811
+ let initialIP;
4812
+ let hasChanged = false;
4813
+ const stopCondition = (status, ip) => {
4814
+ if (!initialStatus) {
4815
+ initialStatus = status;
4816
+ initialIP = ip;
4817
+ }
4818
+ if (status !== initialStatus) {
4819
+ hasChanged = true;
4820
+ }
4821
+ if (status === APP_STATUS_RUNNING && ip) {
4822
+ if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
4823
+ if (!initialIP || initialIP === "No IP assigned") {
4824
+ logger.info(`App is now running with IP: ${ip}`);
4825
+ } else {
4826
+ logger.info("App is now running");
4827
+ }
4828
+ return true;
4829
+ }
4830
+ }
4831
+ if (status === APP_STATUS_FAILED) {
4832
+ throw new Error(`App entered ${status} state`);
4833
+ }
4834
+ return false;
4835
+ };
4836
+ while (true) {
4837
+ try {
4838
+ const info = await userApiClient.getInfos([appId], 1);
4839
+ if (info.length === 0) {
4840
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4841
+ continue;
4842
+ }
4843
+ const appInfo = info[0];
4844
+ const currentStatus = appInfo.status;
4845
+ const currentIP = appInfo.ip || "";
4846
+ if (stopCondition(currentStatus, currentIP)) {
4847
+ return currentIP || void 0;
4848
+ }
4849
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4850
+ } catch (error) {
4851
+ logger.warn(`Failed to fetch app info: ${error.message}`);
4852
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4853
+ }
5286
4854
  }
5287
- if (!import_fs.default.existsSync(cleanedPath)) {
5288
- return `Image file not found: ${cleanedPath}`;
4855
+ }
4856
+ var APP_STATUS_STOPPED = "Stopped";
4857
+ async function watchUntilUpgradeComplete(options, logger) {
4858
+ const { walletClient, publicClient, environmentConfig, appId } = options;
4859
+ const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
4860
+ let initialStatus;
4861
+ let initialIP;
4862
+ let hasChanged = false;
4863
+ const stopCondition = (status, ip) => {
4864
+ if (!initialStatus) {
4865
+ initialStatus = status;
4866
+ initialIP = ip;
4867
+ if (status === APP_STATUS_STOPPED && ip) {
4868
+ logger.info("App upgrade complete.");
4869
+ logger.info(`Status: ${status}`);
4870
+ logger.info(`To start the app, run: ecloud compute app start ${appId}`);
4871
+ return true;
4872
+ }
4873
+ }
4874
+ if (status !== initialStatus) {
4875
+ hasChanged = true;
4876
+ }
4877
+ if (status === APP_STATUS_STOPPED && ip && hasChanged) {
4878
+ logger.info("App upgrade complete.");
4879
+ logger.info(`Status: ${status}`);
4880
+ logger.info(`To start the app, run: ecloud compute app start ${appId}`);
4881
+ return true;
4882
+ }
4883
+ if (status === APP_STATUS_RUNNING && ip && hasChanged) {
4884
+ if (!initialIP || initialIP === "No IP assigned") {
4885
+ logger.info(`App is now running with IP: ${ip}`);
4886
+ } else {
4887
+ logger.info("App is now running");
4888
+ }
4889
+ return true;
4890
+ }
4891
+ if (status === APP_STATUS_FAILED) {
4892
+ throw new Error(`App entered ${status} state`);
4893
+ }
4894
+ return false;
4895
+ };
4896
+ while (true) {
4897
+ try {
4898
+ const info = await userApiClient.getInfos([appId], 1);
4899
+ if (info.length === 0) {
4900
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4901
+ continue;
4902
+ }
4903
+ const appInfo = info[0];
4904
+ const currentStatus = appInfo.status;
4905
+ const currentIP = appInfo.ip || "";
4906
+ if (stopCondition(currentStatus, currentIP)) {
4907
+ return;
4908
+ }
4909
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4910
+ } catch (error) {
4911
+ logger.warn(`Failed to fetch app info: ${error.message}`);
4912
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
4913
+ }
5289
4914
  }
5290
- const stats = import_fs.default.statSync(cleanedPath);
5291
- if (stats.isDirectory()) {
5292
- return "Path is a directory, not a file";
4915
+ }
4916
+ function sleep(ms) {
4917
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4918
+ }
4919
+
4920
+ // src/client/common/utils/validation.ts
4921
+ var import_fs = __toESM(require("fs"), 1);
4922
+ var import_path = __toESM(require("path"), 1);
4923
+ var import_viem5 = require("viem");
4924
+ function validateAppName(name) {
4925
+ if (!name) {
4926
+ throw new Error("App name cannot be empty");
5293
4927
  }
5294
- if (stats.size > MAX_IMAGE_SIZE) {
5295
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
5296
- return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
4928
+ if (name.includes(" ")) {
4929
+ throw new Error("App name cannot contain spaces");
5297
4930
  }
5298
- const ext = import_path.default.extname(cleanedPath).toLowerCase();
5299
- if (!VALID_IMAGE_EXTENSIONS.includes(ext)) {
5300
- return "Image must be JPG or PNG format";
4931
+ if (name.length > 50) {
4932
+ throw new Error("App name cannot be longer than 50 characters");
5301
4933
  }
5302
- return void 0;
5303
4934
  }
5304
- function validateAppID(appID) {
5305
- if (!appID) {
5306
- throw new Error("App ID is required");
4935
+ function validateImageReference(value) {
4936
+ if (!value) {
4937
+ return "Image reference cannot be empty";
5307
4938
  }
5308
- const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
5309
- if ((0, import_viem6.isAddress)(normalized)) {
5310
- return normalized;
4939
+ if (!value.includes("/")) {
4940
+ return "Image reference must contain at least one /";
5311
4941
  }
5312
- throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
4942
+ return true;
5313
4943
  }
5314
- function validateLogVisibility(logVisibility) {
5315
- switch (logVisibility) {
5316
- case "public":
5317
- return { logRedirect: "always", publicLogs: true };
5318
- case "private":
5319
- return { logRedirect: "always", publicLogs: false };
5320
- case "off":
5321
- return { logRedirect: "", publicLogs: false };
5322
- default:
5323
- throw new Error(
5324
- `Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
5325
- );
4944
+ function assertValidImageReference(value) {
4945
+ const result = validateImageReference(value);
4946
+ if (result !== true) {
4947
+ throw new Error(result);
5326
4948
  }
5327
4949
  }
5328
- function validateResourceUsageMonitoring(resourceUsageMonitoring) {
5329
- if (!resourceUsageMonitoring) {
5330
- return "always";
5331
- }
5332
- switch (resourceUsageMonitoring) {
5333
- case "enable":
5334
- return "always";
5335
- case "disable":
5336
- return "never";
5337
- default:
5338
- throw new Error(
5339
- `Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
5340
- );
4950
+ function extractAppNameFromImage(imageRef) {
4951
+ const parts = imageRef.split("/");
4952
+ let imageName = parts.length > 1 ? parts[parts.length - 1] : imageRef;
4953
+ if (imageName.includes(":")) {
4954
+ imageName = imageName.split(":")[0];
5341
4955
  }
4956
+ return imageName;
5342
4957
  }
5343
- function hasScheme(rawURL) {
5344
- return rawURL.startsWith("http://") || rawURL.startsWith("https://");
5345
- }
5346
- function sanitizeString(s) {
5347
- return s.trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5348
- }
5349
- function sanitizeURL(rawURL) {
5350
- rawURL = rawURL.trim();
5351
- if (!hasScheme(rawURL)) {
5352
- rawURL = "https://" + rawURL;
4958
+ function validateFilePath(value) {
4959
+ if (!value) {
4960
+ return "File path cannot be empty";
5353
4961
  }
5354
- const err = validateURL(rawURL);
5355
- if (err) {
5356
- throw new Error(err);
4962
+ if (!import_fs.default.existsSync(value)) {
4963
+ return "File does not exist";
5357
4964
  }
5358
- return rawURL;
4965
+ return true;
5359
4966
  }
5360
- function sanitizeXURL(rawURL) {
5361
- rawURL = rawURL.trim();
5362
- if (!rawURL.includes("://") && !rawURL.includes(".")) {
5363
- const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
5364
- rawURL = `https://x.com/${username}`;
5365
- } else if (!hasScheme(rawURL)) {
5366
- rawURL = "https://" + rawURL;
5367
- }
5368
- rawURL = rawURL.replace(/twitter\.com/g, "x.com");
5369
- rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
5370
- const err = validateXURL(rawURL);
5371
- if (err) {
5372
- throw new Error(err);
4967
+ function assertValidFilePath(value) {
4968
+ const result = validateFilePath(value);
4969
+ if (result !== true) {
4970
+ throw new Error(result);
5373
4971
  }
5374
- return rawURL;
5375
4972
  }
5376
- function validateDeployParams(params) {
5377
- if (!params.dockerfilePath && !params.imageRef) {
5378
- throw new Error("Either dockerfilePath or imageRef is required for deployment");
5379
- }
5380
- if (params.imageRef) {
5381
- assertValidImageReference(params.imageRef);
5382
- }
5383
- if (params.dockerfilePath) {
5384
- assertValidFilePath(params.dockerfilePath);
4973
+ function validateInstanceTypeSKU(sku, availableTypes) {
4974
+ if (!sku) {
4975
+ throw new Error("Instance type SKU cannot be empty");
5385
4976
  }
5386
- if (!params.appName) {
5387
- throw new Error("App name is required");
4977
+ for (const it of availableTypes) {
4978
+ if (it.sku === sku) {
4979
+ return sku;
4980
+ }
5388
4981
  }
5389
- validateAppName(params.appName);
5390
- if (!params.instanceType) {
4982
+ const validSKUs = availableTypes.map((it) => it.sku).join(", ");
4983
+ throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
4984
+ }
4985
+ function validatePrivateKeyFormat(key) {
4986
+ const keyWithoutPrefix = stripHexPrefix(key);
4987
+ if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
4988
+ return false;
4989
+ }
4990
+ return true;
4991
+ }
4992
+ function assertValidPrivateKey(key) {
4993
+ if (!key) {
4994
+ throw new Error("Private key is required");
4995
+ }
4996
+ if (!validatePrivateKeyFormat(key)) {
4997
+ throw new Error(
4998
+ "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
4999
+ );
5000
+ }
5001
+ }
5002
+ function validateURL(rawURL) {
5003
+ if (!rawURL.trim()) {
5004
+ return "URL cannot be empty";
5005
+ }
5006
+ try {
5007
+ const url = new URL(rawURL);
5008
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
5009
+ return "URL scheme must be http or https";
5010
+ }
5011
+ } catch {
5012
+ return "Invalid URL format";
5013
+ }
5014
+ return void 0;
5015
+ }
5016
+ var VALID_X_HOSTS = ["twitter.com", "www.twitter.com", "x.com", "www.x.com"];
5017
+ function validateXURL(rawURL) {
5018
+ const urlErr = validateURL(rawURL);
5019
+ if (urlErr) {
5020
+ return urlErr;
5021
+ }
5022
+ try {
5023
+ const url = new URL(rawURL);
5024
+ const host = url.hostname.toLowerCase();
5025
+ if (!VALID_X_HOSTS.includes(host)) {
5026
+ return "URL must be a valid X/Twitter URL (x.com or twitter.com)";
5027
+ }
5028
+ if (!url.pathname || url.pathname === "/") {
5029
+ return "X URL must include a username or profile path";
5030
+ }
5031
+ } catch {
5032
+ return "Invalid X URL format";
5033
+ }
5034
+ return void 0;
5035
+ }
5036
+ var MAX_DESCRIPTION_LENGTH = 1e3;
5037
+ function validateDescription(description) {
5038
+ if (!description.trim()) {
5039
+ return "Description cannot be empty";
5040
+ }
5041
+ if (description.length > MAX_DESCRIPTION_LENGTH) {
5042
+ return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
5043
+ }
5044
+ return void 0;
5045
+ }
5046
+ var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
5047
+ var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
5048
+ function validateImagePath(filePath) {
5049
+ const cleanedPath = filePath.trim().replace(/^["']|["']$/g, "");
5050
+ if (!cleanedPath) {
5051
+ return "Image path cannot be empty";
5052
+ }
5053
+ if (!import_fs.default.existsSync(cleanedPath)) {
5054
+ return `Image file not found: ${cleanedPath}`;
5055
+ }
5056
+ const stats = import_fs.default.statSync(cleanedPath);
5057
+ if (stats.isDirectory()) {
5058
+ return "Path is a directory, not a file";
5059
+ }
5060
+ if (stats.size > MAX_IMAGE_SIZE) {
5061
+ const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
5062
+ return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
5063
+ }
5064
+ const ext = import_path.default.extname(cleanedPath).toLowerCase();
5065
+ if (!VALID_IMAGE_EXTENSIONS.includes(ext)) {
5066
+ return "Image must be JPG or PNG format";
5067
+ }
5068
+ return void 0;
5069
+ }
5070
+ function validateAppID(appID) {
5071
+ if (!appID) {
5072
+ throw new Error("App ID is required");
5073
+ }
5074
+ const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
5075
+ if ((0, import_viem5.isAddress)(normalized)) {
5076
+ return normalized;
5077
+ }
5078
+ throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
5079
+ }
5080
+ function validateLogVisibility(logVisibility) {
5081
+ switch (logVisibility) {
5082
+ case "public":
5083
+ return { logRedirect: "always", publicLogs: true };
5084
+ case "private":
5085
+ return { logRedirect: "always", publicLogs: false };
5086
+ case "off":
5087
+ return { logRedirect: "", publicLogs: false };
5088
+ default:
5089
+ throw new Error(
5090
+ `Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
5091
+ );
5092
+ }
5093
+ }
5094
+ function validateResourceUsageMonitoring(resourceUsageMonitoring) {
5095
+ if (!resourceUsageMonitoring) {
5096
+ return "always";
5097
+ }
5098
+ switch (resourceUsageMonitoring) {
5099
+ case "enable":
5100
+ return "always";
5101
+ case "disable":
5102
+ return "never";
5103
+ default:
5104
+ throw new Error(
5105
+ `Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
5106
+ );
5107
+ }
5108
+ }
5109
+ function hasScheme(rawURL) {
5110
+ return rawURL.startsWith("http://") || rawURL.startsWith("https://");
5111
+ }
5112
+ function sanitizeString(s) {
5113
+ return s.trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5114
+ }
5115
+ function sanitizeURL(rawURL) {
5116
+ rawURL = rawURL.trim();
5117
+ if (!hasScheme(rawURL)) {
5118
+ rawURL = "https://" + rawURL;
5119
+ }
5120
+ const err = validateURL(rawURL);
5121
+ if (err) {
5122
+ throw new Error(err);
5123
+ }
5124
+ return rawURL;
5125
+ }
5126
+ function sanitizeXURL(rawURL) {
5127
+ rawURL = rawURL.trim();
5128
+ if (!rawURL.includes("://") && !rawURL.includes(".")) {
5129
+ const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
5130
+ rawURL = `https://x.com/${username}`;
5131
+ } else if (!hasScheme(rawURL)) {
5132
+ rawURL = "https://" + rawURL;
5133
+ }
5134
+ rawURL = rawURL.replace(/twitter\.com/g, "x.com");
5135
+ rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
5136
+ const err = validateXURL(rawURL);
5137
+ if (err) {
5138
+ throw new Error(err);
5139
+ }
5140
+ return rawURL;
5141
+ }
5142
+ function validateDeployParams(params) {
5143
+ if (!params.dockerfilePath && !params.imageRef) {
5144
+ throw new Error("Either dockerfilePath or imageRef is required for deployment");
5145
+ }
5146
+ if (params.imageRef) {
5147
+ assertValidImageReference(params.imageRef);
5148
+ }
5149
+ if (params.dockerfilePath) {
5150
+ assertValidFilePath(params.dockerfilePath);
5151
+ }
5152
+ if (!params.appName) {
5153
+ throw new Error("App name is required");
5154
+ }
5155
+ validateAppName(params.appName);
5156
+ if (!params.instanceType) {
5391
5157
  throw new Error("Instance type is required");
5392
5158
  }
5393
5159
  if (!params.logVisibility) {
@@ -5447,69 +5213,245 @@ function validateLogsParams(params) {
5447
5213
  validateAppID(params.appID);
5448
5214
  }
5449
5215
 
5450
- // src/client/common/utils/preflight.ts
5451
- var import_viem7 = require("viem");
5452
- var import_accounts4 = require("viem/accounts");
5453
- async function doPreflightChecks(options, logger) {
5454
- logger.debug("Checking authentication...");
5455
- const privateKey = await getPrivateKeyOrFail(options.privateKey);
5456
- logger.debug("Determining environment...");
5457
- const environmentConfig = getEnvironmentConfig(options.environment || "sepolia");
5458
- let rpcUrl = options.rpcUrl;
5459
- if (!rpcUrl) {
5460
- rpcUrl = process.env.RPC_URL ?? environmentConfig.defaultRPCURL;
5216
+ // src/client/common/config/environment.ts
5217
+ var SEPOLIA_CHAIN_ID = 11155111;
5218
+ var MAINNET_CHAIN_ID = 1;
5219
+ var CommonAddresses = {
5220
+ ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
5221
+ };
5222
+ var ChainAddresses = {
5223
+ [MAINNET_CHAIN_ID]: {
5224
+ PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
5225
+ },
5226
+ [SEPOLIA_CHAIN_ID]: {
5227
+ PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
5461
5228
  }
5462
- if (!rpcUrl) {
5229
+ };
5230
+ var BILLING_ENVIRONMENTS = {
5231
+ dev: {
5232
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
5233
+ },
5234
+ prod: {
5235
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
5236
+ }
5237
+ };
5238
+ var ENVIRONMENTS = {
5239
+ "sepolia-dev": {
5240
+ name: "sepolia",
5241
+ build: "dev",
5242
+ appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
5243
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5244
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5245
+ kmsServerURL: "http://10.128.0.57:8080",
5246
+ userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
5247
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
5248
+ },
5249
+ sepolia: {
5250
+ name: "sepolia",
5251
+ build: "prod",
5252
+ appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
5253
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5254
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5255
+ kmsServerURL: "http://10.128.15.203:8080",
5256
+ userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
5257
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
5258
+ },
5259
+ "mainnet-alpha": {
5260
+ name: "mainnet-alpha",
5261
+ build: "prod",
5262
+ appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
5263
+ permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
5264
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5265
+ kmsServerURL: "http://10.128.0.2:8080",
5266
+ userApiServerURL: "https://userapi-compute.eigencloud.xyz",
5267
+ defaultRPCURL: "https://ethereum-rpc.publicnode.com"
5268
+ }
5269
+ };
5270
+ var CHAIN_ID_TO_ENVIRONMENT = {
5271
+ [SEPOLIA_CHAIN_ID.toString()]: "sepolia",
5272
+ [MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
5273
+ };
5274
+ function getEnvironmentConfig(environment, chainID) {
5275
+ const env = ENVIRONMENTS[environment];
5276
+ if (!env) {
5277
+ throw new Error(`Unknown environment: ${environment}`);
5278
+ }
5279
+ if (!isEnvironmentAvailable(environment)) {
5463
5280
  throw new Error(
5464
- `RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
5281
+ `Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
5465
5282
  );
5466
5283
  }
5467
- logger.debug("Testing network connectivity...");
5468
- const publicClient = (0, import_viem7.createPublicClient)({
5469
- transport: (0, import_viem7.http)(rpcUrl)
5470
- });
5471
- try {
5472
- const chainID = await publicClient.getChainId();
5473
- if (BigInt(chainID) !== environmentConfig.chainID) {
5474
- throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
5284
+ if (chainID) {
5285
+ const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
5286
+ if (expectedEnv && expectedEnv !== environment) {
5287
+ throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
5475
5288
  }
5476
- } catch (err) {
5477
- throw new Error(`Cannot connect to ${environmentConfig.name} RPC at ${rpcUrl}: ${err.message}`);
5478
5289
  }
5479
- const privateKeyHex = addHexPrefix(privateKey);
5480
- const account = (0, import_accounts4.privateKeyToAccount)(privateKeyHex);
5481
- const selfAddress = account.address;
5290
+ const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
5482
5291
  return {
5483
- privateKey: privateKeyHex,
5484
- rpcUrl,
5485
- environmentConfig,
5486
- account,
5487
- selfAddress
5292
+ ...env,
5293
+ chainID: BigInt(resolvedChainID)
5488
5294
  };
5489
5295
  }
5490
- async function getPrivateKeyOrFail(privateKey) {
5491
- if (privateKey) {
5492
- validatePrivateKey(privateKey);
5493
- return privateKey;
5296
+ function getBillingEnvironmentConfig(build) {
5297
+ const config = BILLING_ENVIRONMENTS[build];
5298
+ if (!config) {
5299
+ throw new Error(`Unknown billing environment: ${build}`);
5494
5300
  }
5495
- if (process.env.PRIVATE_KEY) {
5496
- validatePrivateKey(process.env.PRIVATE_KEY);
5497
- return process.env.PRIVATE_KEY;
5301
+ return config;
5302
+ }
5303
+ function getBuildType() {
5304
+ const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
5305
+ const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
5306
+ const buildType = buildTimeType || runtimeType;
5307
+ if (buildType === "dev") {
5308
+ return "dev";
5498
5309
  }
5499
- throw new Error(
5500
- `private key required. Please provide it via:
5501
- \u2022 Option: privateKey in deploy options
5502
- \u2022 Environment: export PRIVATE_KEY=YOUR_KEY
5503
- \u2022 Keyring: (not yet implemented)`
5504
- );
5310
+ return "prod";
5505
5311
  }
5506
- function validatePrivateKey(key) {
5507
- const cleaned = stripHexPrefix(key);
5508
- if (!/^[0-9a-fA-F]{64}$/.test(cleaned)) {
5509
- throw new Error("Invalid private key format (must be 64 hex characters)");
5312
+ function getAvailableEnvironments() {
5313
+ const buildType = getBuildType();
5314
+ if (buildType === "dev") {
5315
+ return ["sepolia-dev"];
5510
5316
  }
5317
+ return ["sepolia", "mainnet-alpha"];
5318
+ }
5319
+ function isEnvironmentAvailable(environment) {
5320
+ return getAvailableEnvironments().includes(environment);
5321
+ }
5322
+ function isMainnet(environmentConfig) {
5323
+ return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
5324
+ }
5325
+
5326
+ // src/client/common/utils/preflight.ts
5327
+ async function doPreflightChecks(options, logger) {
5328
+ const { walletClient, publicClient } = options;
5329
+ logger.debug("Determining environment...");
5330
+ const environmentConfig = getEnvironmentConfig(options.environment || "sepolia");
5331
+ const account = walletClient.account;
5332
+ if (!account) {
5333
+ throw new Error("WalletClient must have an account attached");
5334
+ }
5335
+ logger.debug("Validating chain ID...");
5336
+ try {
5337
+ const chainID = await publicClient.getChainId();
5338
+ if (BigInt(chainID) !== environmentConfig.chainID) {
5339
+ throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
5340
+ }
5341
+ } catch (err) {
5342
+ throw new Error(
5343
+ `Cannot connect to ${environmentConfig.name} RPC at ${publicClient.transport.url}: ${err.message}`
5344
+ );
5345
+ }
5346
+ return {
5347
+ walletClient,
5348
+ publicClient,
5349
+ environmentConfig,
5350
+ selfAddress: account.address
5351
+ };
5352
+ }
5353
+
5354
+ // src/client/common/utils/logger.ts
5355
+ var defaultLogger = {
5356
+ info: (...args) => console.info(...args),
5357
+ warn: (...args) => console.warn(...args),
5358
+ error: (...args) => console.error(...args),
5359
+ debug: (...args) => console.debug(...args)
5360
+ };
5361
+ var getLogger = (verbose) => ({
5362
+ info: (...args) => console.info(...args),
5363
+ warn: (...args) => console.warn(...args),
5364
+ error: (...args) => console.error(...args),
5365
+ debug: (...args) => verbose && console.debug(...args)
5366
+ });
5367
+
5368
+ // src/client/common/utils/billing.ts
5369
+ function isSubscriptionActive(status) {
5370
+ return status === "active" || status === "trialing";
5511
5371
  }
5512
5372
 
5373
+ // src/client/common/utils/billingapi.ts
5374
+ var import_axios2 = __toESM(require("axios"), 1);
5375
+ var BillingApiClient = class {
5376
+ constructor(config, walletClient) {
5377
+ this.config = config;
5378
+ this.walletClient = walletClient;
5379
+ }
5380
+ /**
5381
+ * Get the address of the connected wallet
5382
+ */
5383
+ get address() {
5384
+ const account = this.walletClient.account;
5385
+ if (!account) {
5386
+ throw new Error("WalletClient must have an account attached");
5387
+ }
5388
+ return account.address;
5389
+ }
5390
+ async createSubscription(productId = "compute") {
5391
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5392
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
5393
+ return resp.json();
5394
+ }
5395
+ async getSubscription(productId = "compute") {
5396
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5397
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
5398
+ return resp.json();
5399
+ }
5400
+ async cancelSubscription(productId = "compute") {
5401
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5402
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
5403
+ }
5404
+ /**
5405
+ * Make an authenticated request to the billing API
5406
+ */
5407
+ async makeAuthenticatedRequest(url, method, productId) {
5408
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
5409
+ const { signature } = await calculateBillingAuthSignature({
5410
+ walletClient: this.walletClient,
5411
+ product: productId,
5412
+ expiry
5413
+ });
5414
+ const headers = {
5415
+ Authorization: `Bearer ${signature}`,
5416
+ "X-Account": this.address,
5417
+ "X-Expiry": expiry.toString()
5418
+ };
5419
+ try {
5420
+ const response = await (0, import_axios2.default)({
5421
+ method,
5422
+ url,
5423
+ headers,
5424
+ timeout: 3e4,
5425
+ maxRedirects: 0,
5426
+ validateStatus: () => true
5427
+ // Don't throw on any status
5428
+ });
5429
+ const status = response.status;
5430
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
5431
+ if (status < 200 || status >= 300) {
5432
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
5433
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
5434
+ }
5435
+ return {
5436
+ json: async () => response.data,
5437
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
5438
+ };
5439
+ } catch (error) {
5440
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
5441
+ const cause = error.cause?.message || error.cause || error.message;
5442
+ throw new Error(
5443
+ `Failed to connect to BillingAPI at ${url}: ${cause}
5444
+ Please check:
5445
+ 1. Your internet connection
5446
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
5447
+ 3. Firewall/proxy settings`
5448
+ );
5449
+ }
5450
+ throw error;
5451
+ }
5452
+ }
5453
+ };
5454
+
5513
5455
  // src/client/common/telemetry/noop.ts
5514
5456
  var NoopClient = class {
5515
5457
  /**
@@ -5716,7 +5658,9 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5716
5658
  }
5717
5659
  },
5718
5660
  async () => {
5719
- if (!options.privateKey) throw new Error("privateKey is required for deployment");
5661
+ if (!options.walletClient.account) {
5662
+ throw new Error("WalletClient must have an account attached");
5663
+ }
5720
5664
  if (!options.imageRef) throw new Error("imageRef is required for deployment");
5721
5665
  if (!options.imageDigest) throw new Error("imageDigest is required for deployment");
5722
5666
  assertValidImageReference(options.imageRef);
@@ -5732,8 +5676,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5732
5676
  logger.debug("Performing preflight checks...");
5733
5677
  const preflightCtx = await doPreflightChecks(
5734
5678
  {
5735
- privateKey: options.privateKey,
5736
- rpcUrl: options.rpcUrl,
5679
+ walletClient: options.walletClient,
5680
+ publicClient: options.publicClient,
5737
5681
  environment: options.environment
5738
5682
  },
5739
5683
  logger
@@ -5743,12 +5687,12 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5743
5687
  const salt = generateRandomSalt();
5744
5688
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5745
5689
  logger.debug("Calculating app ID...");
5746
- const appIDToBeDeployed = await calculateAppID(
5747
- preflightCtx.privateKey,
5748
- options.rpcUrl || preflightCtx.rpcUrl,
5749
- preflightCtx.environmentConfig,
5690
+ const appIDToBeDeployed = await calculateAppID({
5691
+ publicClient: preflightCtx.publicClient,
5692
+ environmentConfig: preflightCtx.environmentConfig,
5693
+ ownerAddress: preflightCtx.selfAddress,
5750
5694
  salt
5751
- );
5695
+ });
5752
5696
  logger.info(``);
5753
5697
  logger.info(`App ID: ${appIDToBeDeployed}`);
5754
5698
  logger.info(``);
@@ -5766,12 +5710,13 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5766
5710
  logger.debug("Preparing deploy batch...");
5767
5711
  const batch = await prepareDeployBatch(
5768
5712
  {
5769
- privateKey: preflightCtx.privateKey,
5770
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5713
+ walletClient: preflightCtx.walletClient,
5714
+ publicClient: preflightCtx.publicClient,
5771
5715
  environmentConfig: preflightCtx.environmentConfig,
5772
5716
  salt,
5773
5717
  release,
5774
- publicLogs
5718
+ publicLogs,
5719
+ imageRef: options.imageRef
5775
5720
  },
5776
5721
  logger
5777
5722
  );
@@ -5798,8 +5743,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5798
5743
  );
5799
5744
  }
5800
5745
  function validateDeployOptions(options) {
5801
- if (!options.privateKey) {
5802
- throw new Error("privateKey is required for deployment");
5746
+ if (!options.walletClient.account) {
5747
+ throw new Error("WalletClient must have an account attached");
5803
5748
  }
5804
5749
  if (!options.dockerfilePath && !options.imageRef) {
5805
5750
  throw new Error("Either dockerfilePath or imageRef is required for deployment");
@@ -5838,8 +5783,8 @@ async function deploy(options, logger = defaultLogger) {
5838
5783
  logger.debug("Performing preflight checks...");
5839
5784
  const preflightCtx = await doPreflightChecks(
5840
5785
  {
5841
- privateKey: options.privateKey,
5842
- rpcUrl: options.rpcUrl,
5786
+ walletClient: options.walletClient,
5787
+ publicClient: options.publicClient,
5843
5788
  environment: options.environment
5844
5789
  },
5845
5790
  logger
@@ -5856,12 +5801,12 @@ async function deploy(options, logger = defaultLogger) {
5856
5801
  const salt = generateRandomSalt();
5857
5802
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5858
5803
  logger.debug("Calculating app ID...");
5859
- const appIDToBeDeployed = await calculateAppID(
5860
- preflightCtx.privateKey,
5861
- options.rpcUrl || preflightCtx.rpcUrl,
5862
- preflightCtx.environmentConfig,
5804
+ const appIDToBeDeployed = await calculateAppID({
5805
+ publicClient: preflightCtx.publicClient,
5806
+ environmentConfig: preflightCtx.environmentConfig,
5807
+ ownerAddress: preflightCtx.selfAddress,
5863
5808
  salt
5864
- );
5809
+ });
5865
5810
  logger.info(``);
5866
5811
  logger.info(`App ID: ${appIDToBeDeployed}`);
5867
5812
  logger.info(``);
@@ -5882,8 +5827,8 @@ async function deploy(options, logger = defaultLogger) {
5882
5827
  logger.info("Deploying on-chain...");
5883
5828
  const deployResult = await deployApp(
5884
5829
  {
5885
- privateKey: preflightCtx.privateKey,
5886
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5830
+ walletClient: preflightCtx.walletClient,
5831
+ publicClient: preflightCtx.publicClient,
5887
5832
  environmentConfig: preflightCtx.environmentConfig,
5888
5833
  salt,
5889
5834
  release,
@@ -5896,8 +5841,8 @@ async function deploy(options, logger = defaultLogger) {
5896
5841
  logger.info("Waiting for app to start...");
5897
5842
  const ipAddress = await watchUntilRunning(
5898
5843
  {
5899
- privateKey: preflightCtx.privateKey,
5900
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5844
+ walletClient: preflightCtx.walletClient,
5845
+ publicClient: preflightCtx.publicClient,
5901
5846
  environmentConfig: preflightCtx.environmentConfig,
5902
5847
  appId: deployResult.appId
5903
5848
  },
@@ -5914,12 +5859,10 @@ async function deploy(options, logger = defaultLogger) {
5914
5859
  );
5915
5860
  }
5916
5861
  async function checkQuotaAvailable(preflightCtx) {
5917
- const rpcUrl = preflightCtx.rpcUrl;
5918
- const environmentConfig = preflightCtx.environmentConfig;
5919
- const userAddress = preflightCtx.selfAddress;
5862
+ const { publicClient, environmentConfig, selfAddress: userAddress } = preflightCtx;
5920
5863
  let maxQuota;
5921
5864
  try {
5922
- maxQuota = await getMaxActiveAppsPerUser(rpcUrl, environmentConfig, userAddress);
5865
+ maxQuota = await getMaxActiveAppsPerUser(publicClient, environmentConfig, userAddress);
5923
5866
  } catch (err) {
5924
5867
  throw new Error(`failed to get quota limit: ${err.message}`);
5925
5868
  }
@@ -5930,7 +5873,7 @@ async function checkQuotaAvailable(preflightCtx) {
5930
5873
  }
5931
5874
  let activeCount;
5932
5875
  try {
5933
- activeCount = await getActiveAppCount(rpcUrl, environmentConfig, userAddress);
5876
+ activeCount = await getActiveAppCount(publicClient, environmentConfig, userAddress);
5934
5877
  } catch (err) {
5935
5878
  throw new Error(`failed to get active app count: ${err.message}`);
5936
5879
  }
@@ -5961,8 +5904,8 @@ async function prepareDeploy(options, logger = defaultLogger) {
5961
5904
  logger.debug("Performing preflight checks...");
5962
5905
  const preflightCtx = await doPreflightChecks(
5963
5906
  {
5964
- privateKey: options.privateKey,
5965
- rpcUrl: options.rpcUrl,
5907
+ walletClient: options.walletClient,
5908
+ publicClient: options.publicClient,
5966
5909
  environment: options.environment
5967
5910
  },
5968
5911
  logger
@@ -5979,12 +5922,12 @@ async function prepareDeploy(options, logger = defaultLogger) {
5979
5922
  const salt = generateRandomSalt();
5980
5923
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5981
5924
  logger.debug("Calculating app ID...");
5982
- const appIDToBeDeployed = await calculateAppID(
5983
- preflightCtx.privateKey,
5984
- options.rpcUrl || preflightCtx.rpcUrl,
5985
- preflightCtx.environmentConfig,
5925
+ const appIDToBeDeployed = await calculateAppID({
5926
+ publicClient: preflightCtx.publicClient,
5927
+ environmentConfig: preflightCtx.environmentConfig,
5928
+ ownerAddress: preflightCtx.selfAddress,
5986
5929
  salt
5987
- );
5930
+ });
5988
5931
  logger.info(``);
5989
5932
  logger.info(`App ID: ${appIDToBeDeployed}`);
5990
5933
  logger.info(``);
@@ -6005,12 +5948,13 @@ async function prepareDeploy(options, logger = defaultLogger) {
6005
5948
  logger.debug("Preparing deploy batch...");
6006
5949
  const batch = await prepareDeployBatch(
6007
5950
  {
6008
- privateKey: preflightCtx.privateKey,
6009
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5951
+ walletClient: preflightCtx.walletClient,
5952
+ publicClient: preflightCtx.publicClient,
6010
5953
  environmentConfig: preflightCtx.environmentConfig,
6011
5954
  salt,
6012
5955
  release,
6013
- publicLogs
5956
+ publicLogs,
5957
+ imageRef: finalImageRef
6014
5958
  },
6015
5959
  logger
6016
5960
  );
@@ -6055,25 +5999,23 @@ async function executeDeploy(options) {
6055
5999
  }
6056
6000
  );
6057
6001
  }
6058
- async function watchDeployment(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
6002
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
6059
6003
  return withSDKTelemetry(
6060
6004
  {
6061
6005
  functionName: "watchDeployment",
6062
6006
  skipTelemetry,
6063
6007
  properties: {
6064
- environment
6008
+ environment: environmentConfig.name
6065
6009
  }
6066
6010
  },
6067
6011
  async () => {
6068
- const environmentConfig = getEnvironmentConfig(environment);
6069
6012
  logger.info("Waiting for app to start...");
6070
6013
  return watchUntilRunning(
6071
6014
  {
6072
- privateKey,
6073
- rpcUrl,
6015
+ walletClient,
6016
+ publicClient,
6074
6017
  environmentConfig,
6075
- appId,
6076
- clientId
6018
+ appId
6077
6019
  },
6078
6020
  logger
6079
6021
  );
@@ -6082,18 +6024,12 @@ async function watchDeployment(appId, privateKey, rpcUrl, environment, logger =
6082
6024
  }
6083
6025
 
6084
6026
  // src/client/common/utils/permissions.ts
6085
- var import_viem8 = require("viem");
6086
6027
  var AnyoneCanCallAddress = "0x493219d9949348178af1f58740655951a8cd110c";
6087
6028
  var ApiPermissionsTarget = "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d";
6088
6029
  var CanViewAppLogsPermission2 = "0x2fd3f2fe";
6089
6030
  async function checkAppLogPermission(preflightCtx, appAddress, logger) {
6090
- const chain = getChainFromID(preflightCtx.environmentConfig.chainID);
6091
- const publicClient = (0, import_viem8.createPublicClient)({
6092
- chain,
6093
- transport: (0, import_viem8.http)(preflightCtx.rpcUrl)
6094
- });
6095
6031
  try {
6096
- const canCall = await publicClient.readContract({
6032
+ const canCall = await preflightCtx.publicClient.readContract({
6097
6033
  address: preflightCtx.environmentConfig.permissionControllerAddress,
6098
6034
  abi: PermissionController_default,
6099
6035
  functionName: "canCall",
@@ -6120,8 +6056,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6120
6056
  logger.debug("Performing preflight checks...");
6121
6057
  const preflightCtx = await doPreflightChecks(
6122
6058
  {
6123
- privateKey: options.privateKey,
6124
- rpcUrl: options.rpcUrl,
6059
+ walletClient: options.walletClient,
6060
+ publicClient: options.publicClient,
6125
6061
  environment: options.environment
6126
6062
  },
6127
6063
  logger
@@ -6153,13 +6089,14 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6153
6089
  const needsPermissionChange = currentlyPublic !== publicLogs;
6154
6090
  logger.debug("Preparing upgrade batch...");
6155
6091
  const batch = await prepareUpgradeBatch({
6156
- privateKey: preflightCtx.privateKey,
6157
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6092
+ walletClient: preflightCtx.walletClient,
6093
+ publicClient: preflightCtx.publicClient,
6158
6094
  environmentConfig: preflightCtx.environmentConfig,
6159
- appId: appID,
6095
+ appID,
6160
6096
  release,
6161
6097
  publicLogs,
6162
- needsPermissionChange
6098
+ needsPermissionChange,
6099
+ imageRef: options.imageRef
6163
6100
  });
6164
6101
  logger.debug("Estimating gas...");
6165
6102
  const gasEstimate = await estimateBatchGas({
@@ -6183,8 +6120,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6183
6120
  );
6184
6121
  }
6185
6122
  function validateUpgradeOptions(options) {
6186
- if (!options.privateKey) {
6187
- throw new Error("privateKey is required for upgrade");
6123
+ if (!options.walletClient?.account) {
6124
+ throw new Error("walletClient with account is required for upgrade");
6188
6125
  }
6189
6126
  if (!options.appId) {
6190
6127
  throw new Error("appId is required for upgrade");
@@ -6221,8 +6158,8 @@ async function upgrade(options, logger = defaultLogger) {
6221
6158
  logger.debug("Performing preflight checks...");
6222
6159
  const preflightCtx = await doPreflightChecks(
6223
6160
  {
6224
- privateKey: options.privateKey,
6225
- rpcUrl: options.rpcUrl,
6161
+ walletClient: options.walletClient,
6162
+ publicClient: options.publicClient,
6226
6163
  environment: options.environment
6227
6164
  },
6228
6165
  logger
@@ -6256,10 +6193,10 @@ async function upgrade(options, logger = defaultLogger) {
6256
6193
  logger.info("Upgrading on-chain...");
6257
6194
  const txHash = await upgradeApp(
6258
6195
  {
6259
- privateKey: preflightCtx.privateKey,
6260
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6196
+ walletClient: preflightCtx.walletClient,
6197
+ publicClient: preflightCtx.publicClient,
6261
6198
  environmentConfig: preflightCtx.environmentConfig,
6262
- appId: appID,
6199
+ appID,
6263
6200
  release,
6264
6201
  publicLogs,
6265
6202
  needsPermissionChange,
@@ -6271,8 +6208,8 @@ async function upgrade(options, logger = defaultLogger) {
6271
6208
  logger.info("Waiting for upgrade to complete...");
6272
6209
  await watchUntilUpgradeComplete(
6273
6210
  {
6274
- privateKey: preflightCtx.privateKey,
6275
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6211
+ walletClient: preflightCtx.walletClient,
6212
+ publicClient: preflightCtx.publicClient,
6276
6213
  environmentConfig: preflightCtx.environmentConfig,
6277
6214
  appId: appID
6278
6215
  },
@@ -6299,8 +6236,8 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6299
6236
  logger.debug("Performing preflight checks...");
6300
6237
  const preflightCtx = await doPreflightChecks(
6301
6238
  {
6302
- privateKey: options.privateKey,
6303
- rpcUrl: options.rpcUrl,
6239
+ walletClient: options.walletClient,
6240
+ publicClient: options.publicClient,
6304
6241
  environment: options.environment
6305
6242
  },
6306
6243
  logger
@@ -6333,13 +6270,14 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6333
6270
  const needsPermissionChange = currentlyPublic !== publicLogs;
6334
6271
  logger.debug("Preparing upgrade batch...");
6335
6272
  const batch = await prepareUpgradeBatch({
6336
- privateKey: preflightCtx.privateKey,
6337
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6273
+ walletClient: preflightCtx.walletClient,
6274
+ publicClient: preflightCtx.publicClient,
6338
6275
  environmentConfig: preflightCtx.environmentConfig,
6339
- appId: appID,
6276
+ appID,
6340
6277
  release,
6341
6278
  publicLogs,
6342
- needsPermissionChange
6279
+ needsPermissionChange,
6280
+ imageRef: finalImageRef
6343
6281
  });
6344
6282
  logger.debug("Estimating gas...");
6345
6283
  const gasEstimate = await estimateBatchGas({
@@ -6380,25 +6318,23 @@ async function executeUpgrade(options) {
6380
6318
  }
6381
6319
  );
6382
6320
  }
6383
- async function watchUpgrade(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
6321
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
6384
6322
  return withSDKTelemetry(
6385
6323
  {
6386
6324
  functionName: "watchUpgrade",
6387
6325
  skipTelemetry,
6388
6326
  properties: {
6389
- environment
6327
+ environment: environmentConfig.name
6390
6328
  }
6391
6329
  },
6392
6330
  async () => {
6393
- const environmentConfig = getEnvironmentConfig(environment);
6394
6331
  logger.info("Waiting for upgrade to complete...");
6395
6332
  await watchUntilUpgradeComplete(
6396
6333
  {
6397
- privateKey,
6398
- rpcUrl,
6334
+ walletClient,
6335
+ publicClient,
6399
6336
  environmentConfig,
6400
- appId,
6401
- clientId
6337
+ appId
6402
6338
  },
6403
6339
  logger
6404
6340
  );
@@ -6962,31 +6898,24 @@ async function watchLogs(appID, userApiClient, initialLogs) {
6962
6898
  }
6963
6899
  console.log("\nStopped watching");
6964
6900
  }
6965
- async function logs(options, logger = defaultLogger, skipTelemetry = false) {
6966
- const skipTelemetryFlag = skipTelemetry || options.skipTelemetry || false;
6901
+ async function logs(options, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry = false) {
6967
6902
  return withSDKTelemetry(
6968
6903
  {
6969
6904
  functionName: "logs",
6970
- skipTelemetry: skipTelemetryFlag,
6971
- properties: { environment: options.environment || "sepolia" }
6905
+ skipTelemetry,
6906
+ properties: { environment: environmentConfig.name }
6972
6907
  },
6973
6908
  async () => {
6974
6909
  console.log();
6975
6910
  if (!options.appID) {
6976
6911
  throw new Error("appID is required for viewing logs");
6977
6912
  }
6978
- const environment = options.environment || "sepolia";
6979
- const environmentConfig = getEnvironmentConfig(environment);
6980
- const rpcUrl = options.rpcUrl || environmentConfig.defaultRPCURL;
6981
- if (!rpcUrl) {
6982
- throw new Error("RPC URL is required for authenticated requests");
6983
- }
6984
6913
  const appID = validateAppID(options.appID);
6985
6914
  const formattedApp = formatAppDisplay(environmentConfig.name, appID, "");
6986
6915
  const userApiClient = new UserApiClient(
6987
6916
  environmentConfig,
6988
- options.privateKey,
6989
- rpcUrl,
6917
+ walletClient,
6918
+ publicClient,
6990
6919
  options.clientId
6991
6920
  );
6992
6921
  let logsText;
@@ -7063,35 +6992,39 @@ async function logs(options, logger = defaultLogger, skipTelemetry = false) {
7063
6992
  }
7064
6993
 
7065
6994
  // src/client/modules/compute/app/index.ts
7066
- var CONTROLLER_ABI = (0, import_viem9.parseAbi)([
6995
+ var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
7067
6996
  "function startApp(address appId)",
7068
6997
  "function stopApp(address appId)",
7069
6998
  "function terminateApp(address appId)"
7070
6999
  ]);
7071
7000
  function encodeStartAppData(appId) {
7072
- return (0, import_viem9.encodeFunctionData)({
7001
+ return (0, import_viem6.encodeFunctionData)({
7073
7002
  abi: CONTROLLER_ABI,
7074
7003
  functionName: "startApp",
7075
7004
  args: [appId]
7076
7005
  });
7077
7006
  }
7078
7007
  function encodeStopAppData(appId) {
7079
- return (0, import_viem9.encodeFunctionData)({
7008
+ return (0, import_viem6.encodeFunctionData)({
7080
7009
  abi: CONTROLLER_ABI,
7081
7010
  functionName: "stopApp",
7082
7011
  args: [appId]
7083
7012
  });
7084
7013
  }
7085
7014
  function encodeTerminateAppData(appId) {
7086
- return (0, import_viem9.encodeFunctionData)({
7015
+ return (0, import_viem6.encodeFunctionData)({
7087
7016
  abi: CONTROLLER_ABI,
7088
7017
  functionName: "terminateApp",
7089
7018
  args: [appId]
7090
7019
  });
7091
7020
  }
7092
7021
  function createAppModule(ctx) {
7093
- const privateKey = addHexPrefix(ctx.privateKey);
7022
+ const { walletClient, publicClient } = ctx;
7094
7023
  const skipTelemetry = ctx.skipTelemetry || false;
7024
+ if (!walletClient.account) {
7025
+ throw new Error("WalletClient must have an account attached");
7026
+ }
7027
+ const account = walletClient.account;
7095
7028
  const environment = getEnvironmentConfig(ctx.environment);
7096
7029
  const logger = getLogger(ctx.verbose);
7097
7030
  return {
@@ -7102,8 +7035,8 @@ function createAppModule(ctx) {
7102
7035
  async deploy(opts) {
7103
7036
  const result = await deploy(
7104
7037
  {
7105
- privateKey,
7106
- rpcUrl: ctx.rpcUrl,
7038
+ walletClient,
7039
+ publicClient,
7107
7040
  environment: ctx.environment,
7108
7041
  appName: opts.name,
7109
7042
  instanceType: opts.instanceType,
@@ -7127,8 +7060,8 @@ function createAppModule(ctx) {
7127
7060
  const result = await upgrade(
7128
7061
  {
7129
7062
  appId,
7130
- privateKey,
7131
- rpcUrl: ctx.rpcUrl,
7063
+ walletClient,
7064
+ publicClient,
7132
7065
  environment: ctx.environment,
7133
7066
  instanceType: opts.instanceType,
7134
7067
  dockerfilePath: opts.dockerfile,
@@ -7149,8 +7082,8 @@ function createAppModule(ctx) {
7149
7082
  async prepareDeploy(opts) {
7150
7083
  return prepareDeploy(
7151
7084
  {
7152
- privateKey,
7153
- rpcUrl: ctx.rpcUrl,
7085
+ walletClient,
7086
+ publicClient,
7154
7087
  environment: ctx.environment,
7155
7088
  appName: opts.name,
7156
7089
  instanceType: opts.instanceType,
@@ -7167,8 +7100,8 @@ function createAppModule(ctx) {
7167
7100
  async prepareDeployFromVerifiableBuild(opts) {
7168
7101
  return prepareDeployFromVerifiableBuild(
7169
7102
  {
7170
- privateKey,
7171
- rpcUrl: ctx.rpcUrl,
7103
+ walletClient,
7104
+ publicClient,
7172
7105
  environment: ctx.environment,
7173
7106
  appName: opts.name,
7174
7107
  instanceType: opts.instanceType,
@@ -7183,17 +7116,6 @@ function createAppModule(ctx) {
7183
7116
  );
7184
7117
  },
7185
7118
  async executeDeploy(prepared, gas) {
7186
- const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
7187
- const chain = getChainFromID(environment.chainID);
7188
- const publicClient = (0, import_viem9.createPublicClient)({
7189
- chain,
7190
- transport: (0, import_viem9.http)(ctx.rpcUrl)
7191
- });
7192
- const walletClient = (0, import_viem9.createWalletClient)({
7193
- account,
7194
- chain,
7195
- transport: (0, import_viem9.http)(ctx.rpcUrl)
7196
- });
7197
7119
  const result = await executeDeploy({
7198
7120
  prepared,
7199
7121
  context: {
@@ -7215,11 +7137,10 @@ function createAppModule(ctx) {
7215
7137
  async watchDeployment(appId) {
7216
7138
  return watchDeployment(
7217
7139
  appId,
7218
- privateKey,
7219
- ctx.rpcUrl,
7220
- ctx.environment,
7140
+ walletClient,
7141
+ publicClient,
7142
+ environment,
7221
7143
  logger,
7222
- ctx.clientId,
7223
7144
  skipTelemetry
7224
7145
  );
7225
7146
  },
@@ -7228,8 +7149,8 @@ function createAppModule(ctx) {
7228
7149
  return prepareUpgrade(
7229
7150
  {
7230
7151
  appId,
7231
- privateKey,
7232
- rpcUrl: ctx.rpcUrl,
7152
+ walletClient,
7153
+ publicClient,
7233
7154
  environment: ctx.environment,
7234
7155
  instanceType: opts.instanceType,
7235
7156
  dockerfilePath: opts.dockerfile,
@@ -7246,8 +7167,8 @@ function createAppModule(ctx) {
7246
7167
  return prepareUpgradeFromVerifiableBuild(
7247
7168
  {
7248
7169
  appId,
7249
- privateKey,
7250
- rpcUrl: ctx.rpcUrl,
7170
+ walletClient,
7171
+ publicClient,
7251
7172
  environment: ctx.environment,
7252
7173
  instanceType: opts.instanceType,
7253
7174
  envFilePath: opts.envFile,
@@ -7261,17 +7182,6 @@ function createAppModule(ctx) {
7261
7182
  );
7262
7183
  },
7263
7184
  async executeUpgrade(prepared, gas) {
7264
- const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
7265
- const chain = getChainFromID(environment.chainID);
7266
- const publicClient = (0, import_viem9.createPublicClient)({
7267
- chain,
7268
- transport: (0, import_viem9.http)(ctx.rpcUrl)
7269
- });
7270
- const walletClient = (0, import_viem9.createWalletClient)({
7271
- account,
7272
- chain,
7273
- transport: (0, import_viem9.http)(ctx.rpcUrl)
7274
- });
7275
7185
  const result = await executeUpgrade({
7276
7186
  prepared,
7277
7187
  context: {
@@ -7290,15 +7200,7 @@ function createAppModule(ctx) {
7290
7200
  };
7291
7201
  },
7292
7202
  async watchUpgrade(appId) {
7293
- return watchUpgrade(
7294
- appId,
7295
- privateKey,
7296
- ctx.rpcUrl,
7297
- ctx.environment,
7298
- logger,
7299
- ctx.clientId,
7300
- skipTelemetry
7301
- );
7203
+ return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
7302
7204
  },
7303
7205
  // Profile management
7304
7206
  async setProfile(appId, profile) {
@@ -7311,33 +7213,32 @@ function createAppModule(ctx) {
7311
7213
  async () => {
7312
7214
  const userApiClient = new UserApiClient(
7313
7215
  environment,
7314
- privateKey,
7315
- ctx.rpcUrl,
7216
+ walletClient,
7217
+ publicClient,
7316
7218
  ctx.clientId
7317
7219
  );
7318
- return userApiClient.uploadAppProfile(
7319
- appId,
7320
- profile.name,
7321
- profile.website,
7322
- profile.description,
7323
- profile.xURL,
7324
- profile.imagePath
7325
- );
7220
+ return userApiClient.uploadAppProfile(appId, profile.name, {
7221
+ website: profile.website,
7222
+ description: profile.description,
7223
+ xURL: profile.xURL,
7224
+ image: profile.image,
7225
+ imageName: profile.imageName
7226
+ });
7326
7227
  }
7327
7228
  );
7328
7229
  },
7329
7230
  async logs(opts) {
7330
7231
  return logs(
7331
7232
  {
7332
- privateKey,
7333
7233
  appID: opts.appID,
7334
7234
  watch: opts.watch,
7335
- environment: ctx.environment,
7336
7235
  clientId: ctx.clientId
7337
7236
  },
7237
+ walletClient,
7238
+ publicClient,
7239
+ environment,
7338
7240
  logger,
7339
7241
  skipTelemetry
7340
- // Skip if called from CLI
7341
7242
  );
7342
7243
  },
7343
7244
  async start(appId, opts) {
@@ -7350,15 +7251,15 @@ function createAppModule(ctx) {
7350
7251
  },
7351
7252
  async () => {
7352
7253
  const pendingMessage = `Starting app ${appId}...`;
7353
- const data = (0, import_viem9.encodeFunctionData)({
7254
+ const data = (0, import_viem6.encodeFunctionData)({
7354
7255
  abi: CONTROLLER_ABI,
7355
7256
  functionName: "startApp",
7356
7257
  args: [appId]
7357
7258
  });
7358
7259
  const tx = await sendAndWaitForTransaction(
7359
7260
  {
7360
- privateKey,
7361
- rpcUrl: ctx.rpcUrl,
7261
+ walletClient,
7262
+ publicClient,
7362
7263
  environmentConfig: environment,
7363
7264
  to: environment.appControllerAddress,
7364
7265
  data,
@@ -7382,15 +7283,15 @@ function createAppModule(ctx) {
7382
7283
  },
7383
7284
  async () => {
7384
7285
  const pendingMessage = `Stopping app ${appId}...`;
7385
- const data = (0, import_viem9.encodeFunctionData)({
7286
+ const data = (0, import_viem6.encodeFunctionData)({
7386
7287
  abi: CONTROLLER_ABI,
7387
7288
  functionName: "stopApp",
7388
7289
  args: [appId]
7389
7290
  });
7390
7291
  const tx = await sendAndWaitForTransaction(
7391
7292
  {
7392
- privateKey,
7393
- rpcUrl: ctx.rpcUrl,
7293
+ walletClient,
7294
+ publicClient,
7394
7295
  environmentConfig: environment,
7395
7296
  to: environment.appControllerAddress,
7396
7297
  data,
@@ -7414,15 +7315,15 @@ function createAppModule(ctx) {
7414
7315
  },
7415
7316
  async () => {
7416
7317
  const pendingMessage = `Terminating app ${appId}...`;
7417
- const data = (0, import_viem9.encodeFunctionData)({
7318
+ const data = (0, import_viem6.encodeFunctionData)({
7418
7319
  abi: CONTROLLER_ABI,
7419
7320
  functionName: "terminateApp",
7420
7321
  args: [appId]
7421
7322
  });
7422
7323
  const tx = await sendAndWaitForTransaction(
7423
7324
  {
7424
- privateKey,
7425
- rpcUrl: ctx.rpcUrl,
7325
+ walletClient,
7326
+ publicClient,
7426
7327
  environmentConfig: environment,
7427
7328
  to: environment.appControllerAddress,
7428
7329
  data,
@@ -7438,9 +7339,9 @@ function createAppModule(ctx) {
7438
7339
  },
7439
7340
  async isDelegated() {
7440
7341
  return isDelegated({
7441
- privateKey,
7442
- rpcUrl: ctx.rpcUrl,
7443
- environmentConfig: environment
7342
+ publicClient,
7343
+ environmentConfig: environment,
7344
+ address: account.address
7444
7345
  });
7445
7346
  },
7446
7347
  async undelegate() {
@@ -7454,346 +7355,36 @@ function createAppModule(ctx) {
7454
7355
  async () => {
7455
7356
  const tx = await undelegate(
7456
7357
  {
7457
- privateKey,
7458
- rpcUrl: ctx.rpcUrl,
7459
- environmentConfig: environment
7460
- },
7461
- logger
7462
- );
7463
- return { tx };
7464
- }
7465
- );
7466
- }
7467
- };
7468
- }
7469
-
7470
- // src/client/modules/compute/index.ts
7471
- function createComputeModule(config) {
7472
- return {
7473
- app: createAppModule(config)
7474
- };
7475
- }
7476
-
7477
- // src/client/common/utils/billingapi.ts
7478
- var import_axios2 = __toESM(require("axios"), 1);
7479
- var import_accounts6 = require("viem/accounts");
7480
- var BillingApiClient = class {
7481
- constructor(config, privateKey) {
7482
- this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
7483
- this.config = config;
7484
- }
7485
- async createSubscription(productId = "compute") {
7486
- const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
7487
- const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
7488
- return resp.json();
7489
- }
7490
- async getSubscription(productId = "compute") {
7491
- const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
7492
- const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
7493
- return resp.json();
7494
- }
7495
- async cancelSubscription(productId = "compute") {
7496
- const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
7497
- await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
7498
- }
7499
- /**
7500
- * Make an authenticated request to the billing API
7501
- */
7502
- async makeAuthenticatedRequest(url, method, productId) {
7503
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
7504
- const { signature } = await calculateBillingAuthSignature({
7505
- account: this.account,
7506
- product: productId,
7507
- expiry
7508
- });
7509
- const headers = {
7510
- Authorization: `Bearer ${signature}`,
7511
- "X-Account": this.account.address,
7512
- "X-Expiry": expiry.toString()
7513
- };
7514
- try {
7515
- const response = await (0, import_axios2.default)({
7516
- method,
7517
- url,
7518
- headers,
7519
- timeout: 3e4,
7520
- maxRedirects: 0,
7521
- validateStatus: () => true
7522
- // Don't throw on any status
7523
- });
7524
- const status = response.status;
7525
- const statusText = status >= 200 && status < 300 ? "OK" : "Error";
7526
- if (status < 200 || status >= 300) {
7527
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
7528
- throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
7529
- }
7530
- return {
7531
- json: async () => response.data,
7532
- text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
7533
- };
7534
- } catch (error) {
7535
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
7536
- const cause = error.cause?.message || error.cause || error.message;
7537
- throw new Error(
7538
- `Failed to connect to BillingAPI at ${url}: ${cause}
7539
- Please check:
7540
- 1. Your internet connection
7541
- 2. The API server is accessible: ${this.config.billingApiServerURL}
7542
- 3. Firewall/proxy settings`
7543
- );
7544
- }
7545
- throw error;
7546
- }
7547
- }
7548
- };
7549
-
7550
- // src/client/common/auth/keyring.ts
7551
- var import_keyring = require("@napi-rs/keyring");
7552
- var import_accounts7 = require("viem/accounts");
7553
- var SERVICE_NAME = "ecloud";
7554
- var ACCOUNT_NAME = "key";
7555
- var EIGENX_SERVICE_NAME = "eigenx-cli";
7556
- var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
7557
- var EIGENX_ACCOUNT_PREFIX = "eigenx-";
7558
- var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
7559
- var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
7560
- async function storePrivateKey(privateKey) {
7561
- const normalizedKey = normalizePrivateKey(privateKey);
7562
- const isValid = validatePrivateKey2(normalizedKey);
7563
- if (!isValid) {
7564
- throw new Error("Invalid private key format");
7565
- }
7566
- const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7567
- try {
7568
- await entry.setPassword(normalizedKey);
7569
- } catch (err) {
7570
- throw new Error(
7571
- `Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
7572
- );
7573
- }
7574
- }
7575
- async function getPrivateKey() {
7576
- const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7577
- try {
7578
- const key = await entry.getPassword();
7579
- if (key && validatePrivateKey2(key)) {
7580
- return key;
7581
- }
7582
- } catch {
7583
- }
7584
- return null;
7585
- }
7586
- async function deletePrivateKey() {
7587
- const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7588
- try {
7589
- await entry.deletePassword();
7590
- return true;
7591
- } catch {
7592
- console.warn("No key found in keyring");
7593
- return false;
7594
- }
7595
- }
7596
- async function listStoredKeys() {
7597
- const keys = [];
7598
- const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
7599
- for (const cred of creds) {
7600
- if (cred.account === ACCOUNT_NAME) {
7601
- try {
7602
- const address = getAddressFromPrivateKey(cred.password);
7603
- keys.push({ address });
7604
- } catch (err) {
7605
- console.warn(`Warning: Invalid key found, skipping: ${err}`);
7606
- }
7607
- }
7608
- }
7609
- return keys;
7610
- }
7611
- async function keyExists() {
7612
- const key = await getPrivateKey();
7613
- return key !== null;
7614
- }
7615
- async function getLegacyKeys() {
7616
- const keys = [];
7617
- try {
7618
- const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
7619
- for (const cred of eigenxCreds) {
7620
- const accountName = cred.account;
7621
- if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
7622
- continue;
7623
- }
7624
- const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
7625
- try {
7626
- const decodedKey = decodeGoKeyringValue(cred.password);
7627
- const address = getAddressFromPrivateKey(decodedKey);
7628
- keys.push({ environment, address, source: "eigenx" });
7629
- } catch (err) {
7630
- console.warn(
7631
- `Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
7632
- );
7633
- }
7634
- }
7635
- } catch {
7636
- }
7637
- try {
7638
- const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
7639
- for (const cred of eigenxDevCreds) {
7640
- const accountName = cred.account;
7641
- if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
7642
- continue;
7643
- }
7644
- const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
7645
- try {
7646
- const decodedKey = decodeGoKeyringValue(cred.password);
7647
- const address = getAddressFromPrivateKey(decodedKey);
7648
- keys.push({ environment, address, source: "eigenx-dev" });
7649
- } catch (err) {
7650
- console.warn(
7651
- `Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
7652
- );
7653
- }
7654
- }
7655
- } catch {
7656
- }
7657
- return keys;
7658
- }
7659
- async function getLegacyPrivateKey(environment, source) {
7660
- const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
7661
- const accountName = EIGENX_ACCOUNT_PREFIX + environment;
7662
- const entry = new import_keyring.AsyncEntry(serviceName, accountName);
7663
- try {
7664
- const rawKey = await entry.getPassword();
7665
- if (rawKey) {
7666
- const decodedKey = decodeGoKeyringValue(rawKey);
7667
- if (validatePrivateKey2(decodedKey)) {
7668
- return decodedKey;
7669
- }
7670
- }
7671
- } catch {
7672
- }
7673
- return null;
7674
- }
7675
- async function deleteLegacyPrivateKey(environment, source) {
7676
- const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
7677
- const accountName = EIGENX_ACCOUNT_PREFIX + environment;
7678
- const entry = new import_keyring.AsyncEntry(serviceName, accountName);
7679
- try {
7680
- await entry.deletePassword();
7681
- return true;
7682
- } catch {
7683
- console.warn(`No key found for ${environment} in ${source}`);
7684
- return false;
7685
- }
7686
- }
7687
- function validatePrivateKey2(privateKey) {
7688
- try {
7689
- getAddressFromPrivateKey(privateKey);
7690
- return true;
7691
- } catch {
7692
- return false;
7693
- }
7694
- }
7695
- function getAddressFromPrivateKey(privateKey) {
7696
- const normalized = normalizePrivateKey(privateKey);
7697
- return (0, import_accounts7.privateKeyToAddress)(normalized);
7698
- }
7699
- function decodeGoKeyringValue(rawValue) {
7700
- if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
7701
- const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
7702
- try {
7703
- const decoded = Buffer.from(encoded, "base64").toString("utf8");
7704
- return decoded;
7705
- } catch (err) {
7706
- console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
7707
- return rawValue;
7708
- }
7709
- }
7710
- if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
7711
- const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
7712
- try {
7713
- const decoded = Buffer.from(encoded, "hex").toString("utf8");
7714
- return decoded;
7715
- } catch (err) {
7716
- console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
7717
- return rawValue;
7718
- }
7719
- }
7720
- return rawValue;
7721
- }
7722
- function normalizePrivateKey(privateKey) {
7723
- if (!privateKey.startsWith("0x")) {
7724
- return `0x${privateKey}`;
7725
- }
7726
- return privateKey;
7727
- }
7728
-
7729
- // src/client/common/auth/resolver.ts
7730
- async function getPrivateKeyWithSource(options) {
7731
- if (options.privateKey) {
7732
- if (!validatePrivateKey2(options.privateKey)) {
7733
- throw new Error(
7734
- "Invalid private key format provided via command flag. Please check and try again."
7735
- );
7736
- }
7737
- return {
7738
- key: options.privateKey,
7739
- source: "command flag"
7740
- };
7741
- }
7742
- const envKey = process.env.ECLOUD_PRIVATE_KEY;
7743
- if (envKey) {
7744
- if (!validatePrivateKey2(envKey)) {
7745
- throw new Error(
7746
- "Invalid private key format provided via environment variable. Please check and try again."
7747
- );
7748
- }
7749
- return {
7750
- key: envKey,
7751
- source: "environment variable (ECLOUD_PRIVATE_KEY)"
7752
- };
7753
- }
7754
- const keyringKey = await getPrivateKey();
7755
- if (keyringKey) {
7756
- return {
7757
- key: keyringKey,
7758
- source: "stored credentials"
7759
- };
7760
- }
7761
- return null;
7762
- }
7763
- async function requirePrivateKey(options) {
7764
- const result = await getPrivateKeyWithSource({
7765
- privateKey: options.privateKey
7766
- });
7767
- if (!result) {
7768
- throw new Error(
7769
- `Private key required. Please provide it via:
7770
- \u2022 Keyring: ecloud auth login
7771
- \u2022 Flag: --private-key YOUR_KEY
7772
- \u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
7773
- );
7774
- }
7775
- return result;
7358
+ walletClient,
7359
+ publicClient,
7360
+ environmentConfig: environment
7361
+ },
7362
+ logger
7363
+ );
7364
+ return { tx };
7365
+ }
7366
+ );
7367
+ }
7368
+ };
7776
7369
  }
7777
7370
 
7778
- // src/client/common/auth/generate.ts
7779
- var import_accounts8 = require("viem/accounts");
7780
- function generateNewPrivateKey() {
7781
- const privateKey = (0, import_accounts8.generatePrivateKey)();
7782
- const address = (0, import_accounts8.privateKeyToAddress)(privateKey);
7371
+ // src/client/modules/compute/index.ts
7372
+ function createComputeModule(config) {
7783
7373
  return {
7784
- privateKey,
7785
- address
7374
+ app: createAppModule(config)
7786
7375
  };
7787
7376
  }
7788
7377
 
7789
7378
  // src/client/modules/billing/index.ts
7790
7379
  function createBillingModule(config) {
7791
- const { verbose = false, skipTelemetry = false } = config;
7792
- const privateKey = addHexPrefix(config.privateKey);
7793
- const address = getAddressFromPrivateKey(privateKey);
7380
+ const { verbose = false, skipTelemetry = false, walletClient } = config;
7381
+ if (!walletClient.account) {
7382
+ throw new Error("WalletClient must have an account attached");
7383
+ }
7384
+ const address = walletClient.account.address;
7794
7385
  const logger = getLogger(verbose);
7795
7386
  const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
7796
- const billingApi = new BillingApiClient(billingEnvConfig, privateKey);
7387
+ const billingApi = new BillingApiClient(billingEnvConfig, walletClient);
7797
7388
  return {
7798
7389
  address,
7799
7390
  async subscribe(opts) {
@@ -7883,14 +7474,21 @@ function createBillingModule(config) {
7883
7474
 
7884
7475
  // src/client/common/utils/buildapi.ts
7885
7476
  var import_axios3 = __toESM(require("axios"), 1);
7886
- var import_accounts9 = require("viem/accounts");
7887
7477
  var BuildApiClient = class {
7888
7478
  constructor(options) {
7889
7479
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
7890
7480
  this.clientId = options.clientId;
7891
- if (options.privateKey) {
7892
- this.account = (0, import_accounts9.privateKeyToAccount)(options.privateKey);
7481
+ this.walletClient = options.walletClient;
7482
+ }
7483
+ /**
7484
+ * Get the address of the connected wallet
7485
+ */
7486
+ get address() {
7487
+ const account = this.walletClient?.account;
7488
+ if (!account) {
7489
+ throw new Error("WalletClient must have an account attached");
7893
7490
  }
7491
+ return account.address;
7894
7492
  }
7895
7493
  async submitBuild(payload) {
7896
7494
  return this.authenticatedJsonRequest("/builds", "POST", payload);
@@ -7931,20 +7529,22 @@ var BuildApiClient = class {
7931
7529
  return res.data;
7932
7530
  }
7933
7531
  async authenticatedJsonRequest(path8, method, body) {
7934
- if (!this.account) throw new Error("Private key required for authenticated requests");
7532
+ if (!this.walletClient?.account) {
7533
+ throw new Error("WalletClient with account required for authenticated requests");
7534
+ }
7935
7535
  const headers = {
7936
7536
  "Content-Type": "application/json"
7937
7537
  };
7938
7538
  if (this.clientId) headers["x-client-id"] = this.clientId;
7939
7539
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
7940
7540
  const { signature } = await calculateBillingAuthSignature({
7941
- account: this.account,
7541
+ walletClient: this.walletClient,
7942
7542
  product: "compute",
7943
7543
  expiry
7944
7544
  });
7945
7545
  headers.Authorization = `Bearer ${signature}`;
7946
7546
  headers["X-eigenx-expiry"] = expiry.toString();
7947
- headers["X-Account"] = this.account.address;
7547
+ headers["X-Account"] = this.address;
7948
7548
  const res = await (0, import_axios3.default)({
7949
7549
  url: `${this.baseUrl}${path8}`,
7950
7550
  method,
@@ -7957,18 +7557,20 @@ var BuildApiClient = class {
7957
7557
  return res.data;
7958
7558
  }
7959
7559
  async authenticatedTextRequest(path8) {
7960
- if (!this.account) throw new Error("Private key required for authenticated requests");
7560
+ if (!this.walletClient?.account) {
7561
+ throw new Error("WalletClient with account required for authenticated requests");
7562
+ }
7961
7563
  const headers = {};
7962
7564
  if (this.clientId) headers["x-client-id"] = this.clientId;
7963
7565
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
7964
7566
  const { signature } = await calculateBillingAuthSignature({
7965
- account: this.account,
7567
+ walletClient: this.walletClient,
7966
7568
  product: "compute",
7967
7569
  expiry
7968
7570
  });
7969
7571
  headers.Authorization = `Bearer ${signature}`;
7970
7572
  headers["X-eigenx-expiry"] = expiry.toString();
7971
- headers["X-Account"] = this.account.address;
7573
+ headers["X-Account"] = this.address;
7972
7574
  const res = await (0, import_axios3.default)({
7973
7575
  url: `${this.baseUrl}${path8}`,
7974
7576
  method: "GET",
@@ -8050,13 +7652,13 @@ var BadRequestError = class extends BuildError {
8050
7652
  var DEFAULT_POLL_INTERVAL = 2e3;
8051
7653
  var DEFAULT_TIMEOUT = 30 * 60 * 1e3;
8052
7654
  function createBuildModule(config) {
8053
- const { verbose = false, skipTelemetry = false } = config;
7655
+ const { verbose = false, skipTelemetry = false, walletClient } = config;
8054
7656
  const logger = getLogger(verbose);
8055
7657
  const environment = config.environment || "sepolia";
8056
7658
  const environmentConfig = getEnvironmentConfig(environment);
8057
7659
  const api = new BuildApiClient({
8058
7660
  baseUrl: environmentConfig.userApiServerURL,
8059
- privateKey: config.privateKey ? addHexPrefix(config.privateKey) : void 0,
7661
+ walletClient,
8060
7662
  clientId: config.clientId
8061
7663
  });
8062
7664
  return {
@@ -8068,7 +7670,7 @@ function createBuildModule(config) {
8068
7670
  properties: { environment, repoUrl: request.repoUrl }
8069
7671
  },
8070
7672
  async () => {
8071
- if (!config.privateKey) throw new AuthRequiredError("Private key required for submit()");
7673
+ if (!walletClient) throw new AuthRequiredError("walletClient required for submit()");
8072
7674
  const data = await api.submitBuild({
8073
7675
  repo_url: request.repoUrl,
8074
7676
  git_ref: request.gitRef,
@@ -8127,7 +7729,7 @@ function createBuildModule(config) {
8127
7729
  return withSDKTelemetry(
8128
7730
  { functionName: "build.getLogs", skipTelemetry, properties: { environment, buildId } },
8129
7731
  async () => {
8130
- if (!config.privateKey) throw new AuthRequiredError("Private key required for getLogs()");
7732
+ if (!walletClient) throw new AuthRequiredError("walletClient required for getLogs()");
8131
7733
  return api.getLogs(buildId);
8132
7734
  }
8133
7735
  );
@@ -8235,13 +7837,252 @@ function transformVerifyResult(raw) {
8235
7837
  };
8236
7838
  }
8237
7839
 
7840
+ // src/client/common/auth/keyring.ts
7841
+ var import_keyring = require("@napi-rs/keyring");
7842
+ var import_accounts2 = require("viem/accounts");
7843
+ var SERVICE_NAME = "ecloud";
7844
+ var ACCOUNT_NAME = "key";
7845
+ var EIGENX_SERVICE_NAME = "eigenx-cli";
7846
+ var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
7847
+ var EIGENX_ACCOUNT_PREFIX = "eigenx-";
7848
+ var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
7849
+ var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
7850
+ async function storePrivateKey(privateKey) {
7851
+ const normalizedKey = normalizePrivateKey(privateKey);
7852
+ const isValid = validatePrivateKey(normalizedKey);
7853
+ if (!isValid) {
7854
+ throw new Error("Invalid private key format");
7855
+ }
7856
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7857
+ try {
7858
+ await entry.setPassword(normalizedKey);
7859
+ } catch (err) {
7860
+ throw new Error(
7861
+ `Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
7862
+ );
7863
+ }
7864
+ }
7865
+ async function getPrivateKey() {
7866
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7867
+ try {
7868
+ const key = await entry.getPassword();
7869
+ if (key && validatePrivateKey(key)) {
7870
+ return key;
7871
+ }
7872
+ } catch {
7873
+ }
7874
+ return null;
7875
+ }
7876
+ async function deletePrivateKey() {
7877
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
7878
+ try {
7879
+ await entry.deletePassword();
7880
+ return true;
7881
+ } catch {
7882
+ console.warn("No key found in keyring");
7883
+ return false;
7884
+ }
7885
+ }
7886
+ async function listStoredKeys() {
7887
+ const keys = [];
7888
+ const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
7889
+ for (const cred of creds) {
7890
+ if (cred.account === ACCOUNT_NAME) {
7891
+ try {
7892
+ const address = getAddressFromPrivateKey(cred.password);
7893
+ keys.push({ address });
7894
+ } catch (err) {
7895
+ console.warn(`Warning: Invalid key found, skipping: ${err}`);
7896
+ }
7897
+ }
7898
+ }
7899
+ return keys;
7900
+ }
7901
+ async function keyExists() {
7902
+ const key = await getPrivateKey();
7903
+ return key !== null;
7904
+ }
7905
+ async function getLegacyKeys() {
7906
+ const keys = [];
7907
+ try {
7908
+ const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
7909
+ for (const cred of eigenxCreds) {
7910
+ const accountName = cred.account;
7911
+ if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
7912
+ continue;
7913
+ }
7914
+ const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
7915
+ try {
7916
+ const decodedKey = decodeGoKeyringValue(cred.password);
7917
+ const address = getAddressFromPrivateKey(decodedKey);
7918
+ keys.push({ environment, address, source: "eigenx" });
7919
+ } catch (err) {
7920
+ console.warn(
7921
+ `Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
7922
+ );
7923
+ }
7924
+ }
7925
+ } catch {
7926
+ }
7927
+ try {
7928
+ const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
7929
+ for (const cred of eigenxDevCreds) {
7930
+ const accountName = cred.account;
7931
+ if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
7932
+ continue;
7933
+ }
7934
+ const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
7935
+ try {
7936
+ const decodedKey = decodeGoKeyringValue(cred.password);
7937
+ const address = getAddressFromPrivateKey(decodedKey);
7938
+ keys.push({ environment, address, source: "eigenx-dev" });
7939
+ } catch (err) {
7940
+ console.warn(
7941
+ `Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
7942
+ );
7943
+ }
7944
+ }
7945
+ } catch {
7946
+ }
7947
+ return keys;
7948
+ }
7949
+ async function getLegacyPrivateKey(environment, source) {
7950
+ const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
7951
+ const accountName = EIGENX_ACCOUNT_PREFIX + environment;
7952
+ const entry = new import_keyring.AsyncEntry(serviceName, accountName);
7953
+ try {
7954
+ const rawKey = await entry.getPassword();
7955
+ if (rawKey) {
7956
+ const decodedKey = decodeGoKeyringValue(rawKey);
7957
+ if (validatePrivateKey(decodedKey)) {
7958
+ return decodedKey;
7959
+ }
7960
+ }
7961
+ } catch {
7962
+ }
7963
+ return null;
7964
+ }
7965
+ async function deleteLegacyPrivateKey(environment, source) {
7966
+ const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
7967
+ const accountName = EIGENX_ACCOUNT_PREFIX + environment;
7968
+ const entry = new import_keyring.AsyncEntry(serviceName, accountName);
7969
+ try {
7970
+ await entry.deletePassword();
7971
+ return true;
7972
+ } catch {
7973
+ console.warn(`No key found for ${environment} in ${source}`);
7974
+ return false;
7975
+ }
7976
+ }
7977
+ function validatePrivateKey(privateKey) {
7978
+ try {
7979
+ getAddressFromPrivateKey(privateKey);
7980
+ return true;
7981
+ } catch {
7982
+ return false;
7983
+ }
7984
+ }
7985
+ function getAddressFromPrivateKey(privateKey) {
7986
+ const normalized = normalizePrivateKey(privateKey);
7987
+ return (0, import_accounts2.privateKeyToAddress)(normalized);
7988
+ }
7989
+ function decodeGoKeyringValue(rawValue) {
7990
+ if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
7991
+ const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
7992
+ try {
7993
+ const decoded = Buffer.from(encoded, "base64").toString("utf8");
7994
+ return decoded;
7995
+ } catch (err) {
7996
+ console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
7997
+ return rawValue;
7998
+ }
7999
+ }
8000
+ if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
8001
+ const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
8002
+ try {
8003
+ const decoded = Buffer.from(encoded, "hex").toString("utf8");
8004
+ return decoded;
8005
+ } catch (err) {
8006
+ console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
8007
+ return rawValue;
8008
+ }
8009
+ }
8010
+ return rawValue;
8011
+ }
8012
+ function normalizePrivateKey(privateKey) {
8013
+ if (!privateKey.startsWith("0x")) {
8014
+ return `0x${privateKey}`;
8015
+ }
8016
+ return privateKey;
8017
+ }
8018
+
8019
+ // src/client/common/auth/resolver.ts
8020
+ async function getPrivateKeyWithSource(options) {
8021
+ if (options.privateKey) {
8022
+ if (!validatePrivateKey(options.privateKey)) {
8023
+ throw new Error(
8024
+ "Invalid private key format provided via command flag. Please check and try again."
8025
+ );
8026
+ }
8027
+ return {
8028
+ key: options.privateKey,
8029
+ source: "command flag"
8030
+ };
8031
+ }
8032
+ const envKey = process.env.ECLOUD_PRIVATE_KEY;
8033
+ if (envKey) {
8034
+ if (!validatePrivateKey(envKey)) {
8035
+ throw new Error(
8036
+ "Invalid private key format provided via environment variable. Please check and try again."
8037
+ );
8038
+ }
8039
+ return {
8040
+ key: envKey,
8041
+ source: "environment variable (ECLOUD_PRIVATE_KEY)"
8042
+ };
8043
+ }
8044
+ const keyringKey = await getPrivateKey();
8045
+ if (keyringKey) {
8046
+ return {
8047
+ key: keyringKey,
8048
+ source: "stored credentials"
8049
+ };
8050
+ }
8051
+ return null;
8052
+ }
8053
+ async function requirePrivateKey(options) {
8054
+ const result = await getPrivateKeyWithSource({
8055
+ privateKey: options.privateKey
8056
+ });
8057
+ if (!result) {
8058
+ throw new Error(
8059
+ `Private key required. Please provide it via:
8060
+ \u2022 Keyring: ecloud auth login
8061
+ \u2022 Flag: --private-key YOUR_KEY
8062
+ \u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
8063
+ );
8064
+ }
8065
+ return result;
8066
+ }
8067
+
8068
+ // src/client/common/auth/generate.ts
8069
+ var import_accounts3 = require("viem/accounts");
8070
+ function generateNewPrivateKey() {
8071
+ const privateKey = (0, import_accounts3.generatePrivateKey)();
8072
+ const address = (0, import_accounts3.privateKeyToAddress)(privateKey);
8073
+ return {
8074
+ privateKey,
8075
+ address
8076
+ };
8077
+ }
8078
+
8238
8079
  // src/client/common/utils/instance.ts
8239
8080
  async function getCurrentInstanceType(preflightCtx, appID, logger, clientId) {
8240
8081
  try {
8241
8082
  const userApiClient = new UserApiClient(
8242
8083
  preflightCtx.environmentConfig,
8243
- preflightCtx.privateKey,
8244
- preflightCtx.rpcUrl,
8084
+ preflightCtx.walletClient,
8085
+ preflightCtx.publicClient,
8245
8086
  clientId
8246
8087
  );
8247
8088
  const infos = await userApiClient.getInfos([appID], 1);
@@ -8274,16 +8115,21 @@ function createECloudClient(cfg) {
8274
8115
  `RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
8275
8116
  );
8276
8117
  }
8118
+ const { walletClient, publicClient } = createClients({
8119
+ privateKey: cfg.privateKey,
8120
+ rpcUrl,
8121
+ chainId: environmentConfig.chainID
8122
+ });
8277
8123
  return {
8278
8124
  compute: createComputeModule({
8279
- rpcUrl,
8280
8125
  verbose: cfg.verbose,
8281
- privateKey: cfg.privateKey,
8126
+ walletClient,
8127
+ publicClient,
8282
8128
  environment: cfg.environment
8283
8129
  }),
8284
8130
  billing: createBillingModule({
8285
8131
  verbose: cfg.verbose,
8286
- privateKey: cfg.privateKey
8132
+ walletClient
8287
8133
  })
8288
8134
  };
8289
8135
  }
@@ -8292,6 +8138,7 @@ function createECloudClient(cfg) {
8292
8138
  AuthRequiredError,
8293
8139
  BUILD_STATUS,
8294
8140
  BadRequestError,
8141
+ BillingApiClient,
8295
8142
  BuildError,
8296
8143
  BuildFailedError,
8297
8144
  ConflictError,
@@ -8317,6 +8164,7 @@ function createECloudClient(cfg) {
8317
8164
  createECloudClient,
8318
8165
  createMetricsContext,
8319
8166
  createTelemetryClient,
8167
+ createViemClients,
8320
8168
  deleteLegacyPrivateKey,
8321
8169
  deletePrivateKey,
8322
8170
  emitMetrics,
@@ -8336,9 +8184,11 @@ function createECloudClient(cfg) {
8336
8184
  getAppLatestReleaseBlockNumbers,
8337
8185
  getAvailableEnvironments,
8338
8186
  getAvailableTemplates,
8187
+ getBillingEnvironmentConfig,
8339
8188
  getBlockTimestamps,
8340
8189
  getBuildType,
8341
8190
  getCategoryDescriptions,
8191
+ getChainFromID,
8342
8192
  getCurrentInstanceType,
8343
8193
  getEnvironmentConfig,
8344
8194
  getLegacyKeys,