@layr-labs/ecloud-sdk 0.2.0 → 0.2.2-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.
Files changed (40) hide show
  1. package/VERSION +2 -2
  2. package/dist/billing.cjs +48 -43
  3. package/dist/billing.cjs.map +1 -1
  4. package/dist/billing.d.cts +6 -3
  5. package/dist/billing.d.ts +6 -3
  6. package/dist/billing.js +519 -4
  7. package/dist/billing.js.map +1 -1
  8. package/dist/browser.cjs +5034 -0
  9. package/dist/browser.cjs.map +1 -0
  10. package/dist/browser.d.cts +239 -0
  11. package/dist/browser.d.ts +239 -0
  12. package/dist/browser.js +4924 -0
  13. package/dist/browser.js.map +1 -0
  14. package/dist/{compute-CF2HOXed.d.ts → compute-BYhSs8en.d.ts} +15 -96
  15. package/dist/{compute-CbmjA8kJ.d.cts → compute-Bpjb3hYD.d.cts} +15 -96
  16. package/dist/compute.cjs +875 -846
  17. package/dist/compute.cjs.map +1 -1
  18. package/dist/compute.d.cts +2 -2
  19. package/dist/compute.d.ts +2 -2
  20. package/dist/compute.js +7009 -8
  21. package/dist/compute.js.map +1 -1
  22. package/dist/helpers-CEvhJz7f.d.cts +742 -0
  23. package/dist/helpers-CQuBwQnu.d.ts +742 -0
  24. package/dist/index-DeQzn_yM.d.cts +739 -0
  25. package/dist/index-DeQzn_yM.d.ts +739 -0
  26. package/dist/index.cjs +1958 -1758
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +69 -414
  29. package/dist/index.d.ts +69 -414
  30. package/dist/index.js +7977 -134
  31. package/dist/index.js.map +1 -1
  32. package/package.json +17 -2
  33. package/dist/chunk-CA5Y4OVI.js +0 -744
  34. package/dist/chunk-CA5Y4OVI.js.map +0 -1
  35. package/dist/chunk-ZDXN2WKP.js +0 -434
  36. package/dist/chunk-ZDXN2WKP.js.map +0 -1
  37. package/dist/chunk-ZTLKZMSW.js +0 -6719
  38. package/dist/chunk-ZTLKZMSW.js.map +0 -1
  39. package/dist/index-D2QufVB9.d.cts +0 -342
  40. package/dist/index-D2QufVB9.d.ts +0 -342
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,
@@ -41,6 +42,7 @@ __export(index_exports, {
41
42
  NotFoundError: () => NotFoundError,
42
43
  PRIMARY_LANGUAGES: () => PRIMARY_LANGUAGES,
43
44
  PostHogClient: () => PostHogClient,
45
+ SessionError: () => SessionError,
44
46
  TimeoutError: () => TimeoutError,
45
47
  UserApiClient: () => UserApiClient,
46
48
  addHexPrefix: () => addHexPrefix,
@@ -57,7 +59,9 @@ __export(index_exports, {
57
59
  createComputeModule: () => createComputeModule,
58
60
  createECloudClient: () => createECloudClient,
59
61
  createMetricsContext: () => createMetricsContext,
62
+ createSiweMessage: () => createSiweMessage,
60
63
  createTelemetryClient: () => createTelemetryClient,
64
+ createViemClients: () => createClients,
61
65
  deleteLegacyPrivateKey: () => deleteLegacyPrivateKey,
62
66
  deletePrivateKey: () => deletePrivateKey,
63
67
  emitMetrics: () => emitMetrics,
@@ -72,14 +76,18 @@ __export(index_exports, {
72
76
  fetchTemplateCatalog: () => fetchTemplateCatalog,
73
77
  formatETH: () => formatETH,
74
78
  generateNewPrivateKey: () => generateNewPrivateKey,
79
+ generateNonce: () => generateNonce,
75
80
  getAddressFromPrivateKey: () => getAddressFromPrivateKey,
76
81
  getAllAppsByDeveloper: () => getAllAppsByDeveloper,
77
82
  getAppLatestReleaseBlockNumbers: () => getAppLatestReleaseBlockNumbers,
78
83
  getAvailableEnvironments: () => getAvailableEnvironments,
79
84
  getAvailableTemplates: () => getAvailableTemplates,
85
+ getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
80
86
  getBlockTimestamps: () => getBlockTimestamps,
81
87
  getBuildType: () => getBuildType,
82
88
  getCategoryDescriptions: () => getCategoryDescriptions,
89
+ getChainFromID: () => getChainFromID,
90
+ getComputeApiSession: () => getComputeApiSession,
83
91
  getCurrentInstanceType: () => getCurrentInstanceType,
84
92
  getEnvironmentConfig: () => getEnvironmentConfig,
85
93
  getLegacyKeys: () => getLegacyKeys,
@@ -92,10 +100,17 @@ __export(index_exports, {
92
100
  isEnvironmentAvailable: () => isEnvironmentAvailable,
93
101
  isMainnet: () => isMainnet,
94
102
  isNoopClient: () => isNoopClient,
103
+ isSessionValid: () => isSessionValid,
104
+ isSiweMessageExpired: () => isSiweMessageExpired,
105
+ isSiweMessageNotYetValid: () => isSiweMessageNotYetValid,
95
106
  isSubscriptionActive: () => isSubscriptionActive,
96
107
  keyExists: () => keyExists,
97
108
  listStoredKeys: () => listStoredKeys,
109
+ loginToComputeApi: () => loginToComputeApi,
110
+ logoutFromComputeApi: () => logoutFromComputeApi,
98
111
  logs: () => logs,
112
+ noopLogger: () => noopLogger,
113
+ parseSiweMessage: () => parseSiweMessage,
99
114
  prepareDeploy: () => prepareDeploy,
100
115
  prepareDeployFromVerifiableBuild: () => prepareDeployFromVerifiableBuild,
101
116
  prepareUpgrade: () => prepareUpgrade,
@@ -117,7 +132,7 @@ __export(index_exports, {
117
132
  validateInstanceTypeSKU: () => validateInstanceTypeSKU,
118
133
  validateLogVisibility: () => validateLogVisibility,
119
134
  validateLogsParams: () => validateLogsParams,
120
- validatePrivateKey: () => validatePrivateKey2,
135
+ validatePrivateKey: () => validatePrivateKey,
121
136
  validatePrivateKeyFormat: () => validatePrivateKeyFormat,
122
137
  validateResourceUsageMonitoring: () => validateResourceUsageMonitoring,
123
138
  validateURL: () => validateURL,
@@ -130,118 +145,7 @@ __export(index_exports, {
130
145
  module.exports = __toCommonJS(index_exports);
131
146
 
132
147
  // 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 ? "prod"?.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
- }
148
+ var import_viem6 = require("viem");
245
149
 
246
150
  // src/client/common/docker/build.ts
247
151
  var child_process = __toESM(require("child_process"), 1);
@@ -1341,12 +1245,21 @@ function extractRegistryNameNoDocker(imageRef) {
1341
1245
  return name;
1342
1246
  }
1343
1247
 
1344
- // src/client/common/contract/caller.ts
1345
- var import_accounts2 = require("viem/accounts");
1346
-
1347
1248
  // src/client/common/contract/eip7702.ts
1348
1249
  var import_viem = require("viem");
1349
1250
 
1251
+ // src/client/common/types/index.ts
1252
+ var noopLogger = {
1253
+ debug: () => {
1254
+ },
1255
+ info: () => {
1256
+ },
1257
+ warn: () => {
1258
+ },
1259
+ error: () => {
1260
+ }
1261
+ };
1262
+
1350
1263
  // src/client/common/abis/ERC7702Delegator.json
1351
1264
  var ERC7702Delegator_default = [
1352
1265
  {
@@ -2432,7 +2345,7 @@ async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
2432
2345
  const expectedCode = `0xef0100${delegatorAddress.slice(2)}`;
2433
2346
  return code.toLowerCase() === expectedCode.toLowerCase();
2434
2347
  }
2435
- async function executeBatch(options, logger) {
2348
+ async function executeBatch(options, logger = noopLogger) {
2436
2349
  const { walletClient, publicClient, environmentConfig, executions, pendingMessage, gas } = options;
2437
2350
  const account = walletClient.account;
2438
2351
  if (!account) {
@@ -2456,11 +2369,12 @@ async function executeBatch(options, logger) {
2456
2369
  });
2457
2370
  const chainId = await publicClient.getChainId();
2458
2371
  const authorizationNonce = transactionNonce + 1;
2372
+ logger.debug("Using wallet client signing for EIP-7702 authorization");
2459
2373
  const signedAuthorization = await walletClient.signAuthorization({
2460
- account,
2374
+ account: account.address,
2461
2375
  contractAddress: environmentConfig.erc7702DelegatorAddress,
2462
- chainId: Number(chainId),
2463
- nonce: authorizationNonce
2376
+ chainId,
2377
+ nonce: Number(authorizationNonce)
2464
2378
  });
2465
2379
  authorizationList = [signedAuthorization];
2466
2380
  }
@@ -2515,78 +2429,31 @@ async function executeBatch(options, logger) {
2515
2429
  }
2516
2430
 
2517
2431
  // 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");
2432
+ var import_viem3 = require("viem");
2583
2433
 
2584
2434
  // src/client/common/utils/helpers.ts
2585
- var import_viem3 = require("viem");
2435
+ var import_viem2 = require("viem");
2586
2436
  var import_chains2 = require("viem/chains");
2437
+ var import_accounts = require("viem/accounts");
2587
2438
  function getChainFromID(chainID, fallback = import_chains2.sepolia) {
2588
2439
  const id = Number(chainID);
2589
- return (0, import_viem3.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
2440
+ return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
2441
+ }
2442
+ function createClients(options) {
2443
+ const { privateKey, rpcUrl, chainId } = options;
2444
+ const privateKeyHex = addHexPrefix(privateKey);
2445
+ const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
2446
+ const chain = getChainFromID(chainId);
2447
+ const publicClient = (0, import_viem2.createPublicClient)({
2448
+ chain,
2449
+ transport: (0, import_viem2.http)(rpcUrl)
2450
+ });
2451
+ const walletClient = (0, import_viem2.createWalletClient)({
2452
+ account,
2453
+ chain,
2454
+ transport: (0, import_viem2.http)(rpcUrl)
2455
+ });
2456
+ return { walletClient, publicClient };
2590
2457
  }
2591
2458
  function addHexPrefix(value) {
2592
2459
  return value.startsWith("0x") ? value : `0x${value}`;
@@ -2595,302 +2462,6 @@ function stripHexPrefix(value) {
2595
2462
  return value.startsWith("0x") ? value.slice(2) : value;
2596
2463
  }
2597
2464
 
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" : "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
2465
  // src/client/common/abis/AppController.json
2895
2466
  var AppController_default = [
2896
2467
  {
@@ -4446,17 +4017,10 @@ function formatETH(wei) {
4446
4017
  return trimmed;
4447
4018
  }
4448
4019
  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
- });
4020
+ const { publicClient, from, to, data, value = 0n } = options;
4457
4021
  const fees = await publicClient.estimateFeesPerGas();
4458
4022
  const gasLimit = await publicClient.estimateGas({
4459
- account: account.address,
4023
+ account: from,
4460
4024
  to,
4461
4025
  data,
4462
4026
  value
@@ -4473,65 +4037,54 @@ async function estimateTransactionGas(options) {
4473
4037
  maxCostEth
4474
4038
  };
4475
4039
  }
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");
4040
+ async function calculateAppID(options) {
4041
+ const { publicClient, environmentConfig, ownerAddress, salt } = options;
4042
+ const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4485
4043
  const paddedSaltHex = saltHexString.padStart(64, "0");
4486
4044
  const saltHex = `0x${paddedSaltHex}`;
4487
- const accountAddress = typeof account.address === "string" ? account.address : account.address.toString();
4488
4045
  const appID = await publicClient.readContract({
4489
4046
  address: environmentConfig.appControllerAddress,
4490
4047
  abi: AppController_default,
4491
4048
  functionName: "calculateAppId",
4492
- args: [accountAddress, saltHex]
4049
+ args: [ownerAddress, saltHex]
4493
4050
  });
4494
4051
  return appID;
4495
4052
  }
4496
- 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
- });
4053
+ async function prepareDeployBatch(options, logger = noopLogger) {
4054
+ const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
4055
+ const account = walletClient.account;
4056
+ if (!account) {
4057
+ throw new Error("WalletClient must have an account attached");
4058
+ }
4510
4059
  logger.info("Calculating app ID...");
4511
- const appId = await calculateAppID(privateKeyHex, rpcUrl, environmentConfig, salt);
4512
- logger.info(`App ID: ${appId}`);
4060
+ const appId = await calculateAppID({
4061
+ publicClient,
4062
+ environmentConfig,
4063
+ ownerAddress: account.address,
4064
+ salt
4065
+ });
4513
4066
  logger.debug(`App ID calculated: ${appId}`);
4514
4067
  logger.debug(`This address will be used for acceptAdmin call`);
4515
- const saltHexString = Buffer.from(salt).toString("hex");
4068
+ const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4516
4069
  const paddedSaltHex = saltHexString.padStart(64, "0");
4517
4070
  const saltHex = `0x${paddedSaltHex}`;
4518
4071
  const releaseForViem = {
4519
4072
  rmsRelease: {
4520
4073
  artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4521
- digest: `0x${Buffer.from(artifact.digest).toString("hex").padStart(64, "0")}`,
4074
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4522
4075
  registry: artifact.registry
4523
4076
  })),
4524
4077
  upgradeByTime: release.rmsRelease.upgradeByTime
4525
4078
  },
4526
- publicEnv: `0x${Buffer.from(release.publicEnv).toString("hex")}`,
4527
- encryptedEnv: `0x${Buffer.from(release.encryptedEnv).toString("hex")}`
4079
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4080
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4528
4081
  };
4529
- const createData = (0, import_viem5.encodeFunctionData)({
4082
+ const createData = (0, import_viem3.encodeFunctionData)({
4530
4083
  abi: AppController_default,
4531
4084
  functionName: "createApp",
4532
4085
  args: [saltHex, releaseForViem]
4533
4086
  });
4534
- const acceptAdminData = (0, import_viem5.encodeFunctionData)({
4087
+ const acceptAdminData = (0, import_viem3.encodeFunctionData)({
4535
4088
  abi: PermissionController_default,
4536
4089
  functionName: "acceptAdmin",
4537
4090
  args: [appId]
@@ -4549,7 +4102,7 @@ async function prepareDeployBatch(options, logger) {
4549
4102
  }
4550
4103
  ];
4551
4104
  if (publicLogs) {
4552
- const anyoneCanViewLogsData = (0, import_viem5.encodeFunctionData)({
4105
+ const anyoneCanViewLogsData = (0, import_viem3.encodeFunctionData)({
4553
4106
  abi: PermissionController_default,
4554
4107
  functionName: "setAppointee",
4555
4108
  args: [
@@ -4577,7 +4130,7 @@ async function prepareDeployBatch(options, logger) {
4577
4130
  environmentConfig
4578
4131
  };
4579
4132
  }
4580
- async function executeDeployBatch(data, context, gas, logger) {
4133
+ async function executeDeployBatch(data, context, gas, logger = noopLogger) {
4581
4134
  const pendingMessage = "Deploying new app...";
4582
4135
  const txHash = await executeBatch(
4583
4136
  {
@@ -4592,18 +4145,8 @@ async function executeDeployBatch(data, context, gas, logger) {
4592
4145
  );
4593
4146
  return { appId: data.appId, txHash };
4594
4147
  }
4595
- 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
- );
4148
+ async function deployApp(options, logger = noopLogger) {
4149
+ const prepared = await prepareDeployBatch(options, logger);
4607
4150
  const data = {
4608
4151
  appId: prepared.appId,
4609
4152
  salt: prepared.salt,
@@ -4618,41 +4161,29 @@ async function deployApp(options, logger) {
4618
4161
  }
4619
4162
  async function prepareUpgradeBatch(options) {
4620
4163
  const {
4621
- privateKey,
4622
- rpcUrl,
4164
+ walletClient,
4165
+ publicClient,
4623
4166
  environmentConfig,
4624
- appId,
4167
+ appID,
4625
4168
  release,
4626
4169
  publicLogs,
4627
4170
  needsPermissionChange
4628
4171
  } = 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
- });
4641
4172
  const releaseForViem = {
4642
4173
  rmsRelease: {
4643
4174
  artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4644
- digest: `0x${Buffer.from(artifact.digest).toString("hex").padStart(64, "0")}`,
4175
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4645
4176
  registry: artifact.registry
4646
4177
  })),
4647
4178
  upgradeByTime: release.rmsRelease.upgradeByTime
4648
4179
  },
4649
- publicEnv: `0x${Buffer.from(release.publicEnv).toString("hex")}`,
4650
- encryptedEnv: `0x${Buffer.from(release.encryptedEnv).toString("hex")}`
4180
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4181
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4651
4182
  };
4652
- const upgradeData = (0, import_viem5.encodeFunctionData)({
4183
+ const upgradeData = (0, import_viem3.encodeFunctionData)({
4653
4184
  abi: AppController_default,
4654
4185
  functionName: "upgradeApp",
4655
- args: [appId, releaseForViem]
4186
+ args: [appID, releaseForViem]
4656
4187
  });
4657
4188
  const executions = [
4658
4189
  {
@@ -4663,11 +4194,11 @@ async function prepareUpgradeBatch(options) {
4663
4194
  ];
4664
4195
  if (needsPermissionChange) {
4665
4196
  if (publicLogs) {
4666
- const addLogsData = (0, import_viem5.encodeFunctionData)({
4197
+ const addLogsData = (0, import_viem3.encodeFunctionData)({
4667
4198
  abi: PermissionController_default,
4668
4199
  functionName: "setAppointee",
4669
4200
  args: [
4670
- appId,
4201
+ appID,
4671
4202
  "0x493219d9949348178af1f58740655951a8cd110c",
4672
4203
  // AnyoneCanCallAddress
4673
4204
  "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
@@ -4682,11 +4213,11 @@ async function prepareUpgradeBatch(options) {
4682
4213
  callData: addLogsData
4683
4214
  });
4684
4215
  } else {
4685
- const removeLogsData = (0, import_viem5.encodeFunctionData)({
4216
+ const removeLogsData = (0, import_viem3.encodeFunctionData)({
4686
4217
  abi: PermissionController_default,
4687
4218
  functionName: "removeAppointee",
4688
4219
  args: [
4689
- appId,
4220
+ appID,
4690
4221
  "0x493219d9949348178af1f58740655951a8cd110c",
4691
4222
  // AnyoneCanCallAddress
4692
4223
  "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
@@ -4703,14 +4234,14 @@ async function prepareUpgradeBatch(options) {
4703
4234
  }
4704
4235
  }
4705
4236
  return {
4706
- appId,
4237
+ appId: appID,
4707
4238
  executions,
4708
4239
  walletClient,
4709
4240
  publicClient,
4710
4241
  environmentConfig
4711
4242
  };
4712
4243
  }
4713
- async function executeUpgradeBatch(data, context, gas, logger) {
4244
+ async function executeUpgradeBatch(data, context, gas, logger = noopLogger) {
4714
4245
  const pendingMessage = `Upgrading app ${data.appId}...`;
4715
4246
  const txHash = await executeBatch(
4716
4247
  {
@@ -4725,16 +4256,8 @@ async function executeUpgradeBatch(data, context, gas, logger) {
4725
4256
  );
4726
4257
  return txHash;
4727
4258
  }
4728
- 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
- });
4259
+ async function upgradeApp(options, logger = noopLogger) {
4260
+ const prepared = await prepareUpgradeBatch(options);
4738
4261
  const data = {
4739
4262
  appId: prepared.appId,
4740
4263
  executions: prepared.executions
@@ -4746,10 +4269,10 @@ async function upgradeApp(options, logger) {
4746
4269
  };
4747
4270
  return executeUpgradeBatch(data, context, options.gas, logger);
4748
4271
  }
4749
- async function sendAndWaitForTransaction(options, logger) {
4272
+ async function sendAndWaitForTransaction(options, logger = noopLogger) {
4750
4273
  const {
4751
- privateKey,
4752
- rpcUrl,
4274
+ walletClient,
4275
+ publicClient,
4753
4276
  environmentConfig,
4754
4277
  to,
4755
4278
  data,
@@ -4758,18 +4281,11 @@ async function sendAndWaitForTransaction(options, logger) {
4758
4281
  txDescription,
4759
4282
  gas
4760
4283
  } = options;
4761
- const privateKeyHex = addHexPrefix(privateKey);
4762
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4284
+ const account = walletClient.account;
4285
+ if (!account) {
4286
+ throw new Error("WalletClient must have an account attached");
4287
+ }
4763
4288
  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
4289
  if (pendingMessage) {
4774
4290
  logger.info(`
4775
4291
  ${pendingMessage}`);
@@ -4780,7 +4296,10 @@ ${pendingMessage}`);
4780
4296
  data,
4781
4297
  value,
4782
4298
  ...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
4783
- ...gas?.maxPriorityFeePerGas && { maxPriorityFeePerGas: gas.maxPriorityFeePerGas }
4299
+ ...gas?.maxPriorityFeePerGas && {
4300
+ maxPriorityFeePerGas: gas.maxPriorityFeePerGas
4301
+ },
4302
+ chain
4784
4303
  });
4785
4304
  logger.info(`Transaction sent: ${hash}`);
4786
4305
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
@@ -4795,7 +4314,7 @@ ${pendingMessage}`);
4795
4314
  } catch (callError) {
4796
4315
  if (callError.data) {
4797
4316
  try {
4798
- const decoded = (0, import_viem5.decodeErrorResult)({
4317
+ const decoded = (0, import_viem3.decodeErrorResult)({
4799
4318
  abi: AppController_default,
4800
4319
  data: callError.data
4801
4320
  });
@@ -4846,12 +4365,7 @@ function formatAppControllerError(decoded) {
4846
4365
  return new Error(`contract error: ${errorName}`);
4847
4366
  }
4848
4367
  }
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
- });
4368
+ async function getActiveAppCount(publicClient, environmentConfig, user) {
4855
4369
  const count = await publicClient.readContract({
4856
4370
  address: environmentConfig.appControllerAddress,
4857
4371
  abi: AppController_default,
@@ -4860,12 +4374,7 @@ async function getActiveAppCount(rpcUrl, environmentConfig, user) {
4860
4374
  });
4861
4375
  return Number(count);
4862
4376
  }
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
- });
4377
+ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
4869
4378
  const quota = await publicClient.readContract({
4870
4379
  address: environmentConfig.appControllerAddress,
4871
4380
  abi: AppController_default,
@@ -4874,12 +4383,7 @@ async function getMaxActiveAppsPerUser(rpcUrl, environmentConfig, user) {
4874
4383
  });
4875
4384
  return Number(quota);
4876
4385
  }
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
- });
4386
+ async function getAppsByDeveloper(publicClient, environmentConfig, developer, offset, limit) {
4883
4387
  const result = await publicClient.readContract({
4884
4388
  address: environmentConfig.appControllerAddress,
4885
4389
  abi: AppController_default,
@@ -4891,12 +4395,18 @@ async function getAppsByDeveloper(rpcUrl, environmentConfig, developer, offset,
4891
4395
  appConfigs: result[1]
4892
4396
  };
4893
4397
  }
4894
- async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
4398
+ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 100n) {
4895
4399
  let offset = 0n;
4896
4400
  const allApps = [];
4897
4401
  const allConfigs = [];
4898
4402
  while (true) {
4899
- const { apps, appConfigs } = await getAppsByDeveloper(rpcUrl, env, developer, offset, pageSize);
4403
+ const { apps, appConfigs } = await getAppsByDeveloper(
4404
+ publicClient,
4405
+ env,
4406
+ developer,
4407
+ offset,
4408
+ pageSize
4409
+ );
4900
4410
  if (apps.length === 0) break;
4901
4411
  allApps.push(...apps);
4902
4412
  allConfigs.push(...appConfigs);
@@ -4908,12 +4418,7 @@ async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
4908
4418
  appConfigs: allConfigs
4909
4419
  };
4910
4420
  }
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
- });
4421
+ async function getAppLatestReleaseBlockNumbers(publicClient, environmentConfig, appIDs) {
4917
4422
  const results = await Promise.all(
4918
4423
  appIDs.map(
4919
4424
  (appID) => publicClient.readContract({
@@ -4933,12 +4438,7 @@ async function getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, appIDs
4933
4438
  }
4934
4439
  return blockNumbers;
4935
4440
  }
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
- });
4441
+ async function getBlockTimestamps(publicClient, blockNumbers) {
4942
4442
  const uniqueBlockNumbers = [...new Set(blockNumbers)].filter((n) => n > 0);
4943
4443
  const timestamps = /* @__PURE__ */ new Map();
4944
4444
  const blocks = await Promise.all(
@@ -4955,67 +4455,34 @@ async function getBlockTimestamps(rpcUrl, environmentConfig, blockNumbers) {
4955
4455
  return timestamps;
4956
4456
  }
4957
4457
  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
- });
4458
+ const { publicClient, environmentConfig, address } = options;
4966
4459
  return checkERC7702Delegation(
4967
4460
  publicClient,
4968
- account.address,
4461
+ address,
4969
4462
  environmentConfig.erc7702DelegatorAddress
4970
4463
  );
4971
4464
  }
4972
- async function undelegate(options, logger) {
4973
- const { privateKey, rpcUrl, environmentConfig } = options;
4974
- const privateKeyHex = addHexPrefix(privateKey);
4975
- const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
4465
+ async function undelegate(options, logger = noopLogger) {
4466
+ const { walletClient, publicClient, environmentConfig } = options;
4467
+ const account = walletClient.account;
4468
+ if (!account) {
4469
+ throw new Error("WalletClient must have an account attached");
4470
+ }
4976
4471
  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
4472
  const transactionNonce = await publicClient.getTransactionCount({
4987
4473
  address: account.address,
4988
4474
  blockTag: "pending"
4989
4475
  });
4990
4476
  const chainId = await publicClient.getChainId();
4991
4477
  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
4478
+ logger.debug("Signing undelegate authorization");
4479
+ const signedAuthorization = await walletClient.signAuthorization({
4480
+ contractAddress: "0x0000000000000000000000000000000000000000",
4481
+ chainId,
4482
+ nonce: Number(authorizationNonce),
4483
+ account
5006
4484
  });
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
- ];
4485
+ const authorizationList = [signedAuthorization];
5019
4486
  const hash = await walletClient.sendTransaction({
5020
4487
  account,
5021
4488
  to: account.address,
@@ -5023,7 +4490,8 @@ async function undelegate(options, logger) {
5023
4490
  data: "0x",
5024
4491
  // Empty data
5025
4492
  value: 0n,
5026
- authorizationList
4493
+ authorizationList,
4494
+ chain
5027
4495
  });
5028
4496
  logger.info(`Transaction sent: ${hash}`);
5029
4497
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
@@ -5034,340 +4502,874 @@ async function undelegate(options, logger) {
5034
4502
  return hash;
5035
4503
  }
5036
4504
 
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
- }
4505
+ // src/client/common/utils/userapi.ts
4506
+ var import_axios = __toESM(require("axios"), 1);
4507
+
4508
+ // src/client/common/utils/auth.ts
4509
+ var import_viem4 = require("viem");
4510
+ var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
4511
+ "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
4512
+ ]);
4513
+ async function calculatePermissionSignature(options) {
4514
+ const { permission, expiry, appControllerAddress, publicClient, walletClient } = options;
4515
+ const digest = await publicClient.readContract({
4516
+ address: appControllerAddress,
4517
+ abi: APP_CONTROLLER_ABI,
4518
+ functionName: "calculateApiPermissionDigestHash",
4519
+ args: [permission, expiry]
4520
+ });
4521
+ const account = walletClient.account;
4522
+ if (!account) {
4523
+ throw new Error("WalletClient must have an account attached");
5088
4524
  }
4525
+ const signature = await walletClient.signMessage({
4526
+ account,
4527
+ message: { raw: digest }
4528
+ });
4529
+ return { signature, digest };
5089
4530
  }
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`);
4531
+ var generateBillingSigData = (product, expiry) => {
4532
+ return {
4533
+ domain: {
4534
+ name: "EigenCloud Billing API",
4535
+ version: "1"
4536
+ },
4537
+ types: {
4538
+ BillingAuth: [
4539
+ { name: "product", type: "string" },
4540
+ { name: "expiry", type: "uint256" }
4541
+ ]
4542
+ },
4543
+ primaryType: "BillingAuth",
4544
+ message: {
4545
+ product,
4546
+ expiry
5127
4547
  }
5128
- return false;
5129
4548
  };
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
- }
4549
+ };
4550
+ async function calculateBillingAuthSignature(options) {
4551
+ const { walletClient, product, expiry } = options;
4552
+ const account = walletClient.account;
4553
+ if (!account) {
4554
+ throw new Error("WalletClient must have an account attached");
5148
4555
  }
5149
- }
5150
- function sleep(ms) {
5151
- return new Promise((resolve2) => setTimeout(resolve2, ms));
4556
+ const signature = await walletClient.signTypedData({
4557
+ account,
4558
+ ...generateBillingSigData(product, expiry)
4559
+ });
4560
+ return { signature, expiry };
5152
4561
  }
5153
4562
 
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");
4563
+ // src/client/common/auth/session.ts
4564
+ var SessionError = class extends Error {
4565
+ constructor(message, code, statusCode) {
4566
+ super(message);
4567
+ this.code = code;
4568
+ this.statusCode = statusCode;
4569
+ this.name = "SessionError";
5164
4570
  }
5165
- if (name.length > 50) {
5166
- throw new Error("App name cannot be longer than 50 characters");
4571
+ };
4572
+ function stripHexPrefix2(hex) {
4573
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
4574
+ }
4575
+ async function parseErrorResponse(response) {
4576
+ try {
4577
+ const data = await response.json();
4578
+ return data.error || response.statusText;
4579
+ } catch {
4580
+ return response.statusText;
5167
4581
  }
5168
4582
  }
5169
- function validateImageReference(value) {
5170
- if (!value) {
5171
- return "Image reference cannot be empty";
4583
+ async function loginToComputeApi(config, request) {
4584
+ let response;
4585
+ try {
4586
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
4587
+ method: "POST",
4588
+ credentials: "include",
4589
+ // Include cookies for session management
4590
+ headers: {
4591
+ "Content-Type": "application/json"
4592
+ },
4593
+ body: JSON.stringify({
4594
+ message: request.message,
4595
+ signature: stripHexPrefix2(request.signature)
4596
+ })
4597
+ });
4598
+ } catch (error) {
4599
+ throw new SessionError(
4600
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
4601
+ "NETWORK_ERROR"
4602
+ );
5172
4603
  }
5173
- if (!value.includes("/")) {
5174
- return "Image reference must contain at least one /";
4604
+ if (!response.ok) {
4605
+ const errorMessage = await parseErrorResponse(response);
4606
+ const status = response.status;
4607
+ if (status === 400) {
4608
+ if (errorMessage.toLowerCase().includes("siwe")) {
4609
+ throw new SessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
4610
+ }
4611
+ throw new SessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
4612
+ }
4613
+ if (status === 401) {
4614
+ throw new SessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
4615
+ }
4616
+ throw new SessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
5175
4617
  }
5176
- return true;
4618
+ const data = await response.json();
4619
+ return {
4620
+ success: data.success,
4621
+ address: data.address
4622
+ };
5177
4623
  }
5178
- function assertValidImageReference(value) {
5179
- const result = validateImageReference(value);
5180
- if (result !== true) {
5181
- throw new Error(result);
4624
+ async function getComputeApiSession(config) {
4625
+ let response;
4626
+ try {
4627
+ response = await fetch(`${config.baseUrl}/auth/session`, {
4628
+ method: "GET",
4629
+ credentials: "include",
4630
+ // Include cookies for session management
4631
+ headers: {
4632
+ "Content-Type": "application/json"
4633
+ }
4634
+ });
4635
+ } catch {
4636
+ return {
4637
+ authenticated: false
4638
+ };
5182
4639
  }
5183
- }
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];
4640
+ if (response.status === 401) {
4641
+ return {
4642
+ authenticated: false
4643
+ };
5189
4644
  }
5190
- return imageName;
4645
+ if (!response.ok) {
4646
+ const errorMessage = await parseErrorResponse(response);
4647
+ throw new SessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
4648
+ }
4649
+ const data = await response.json();
4650
+ return {
4651
+ authenticated: data.authenticated,
4652
+ address: data.address,
4653
+ chainId: data.chain_id
4654
+ };
5191
4655
  }
5192
- function validateFilePath(value) {
5193
- if (!value) {
5194
- return "File path cannot be empty";
4656
+ async function logoutFromComputeApi(config) {
4657
+ let response;
4658
+ try {
4659
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
4660
+ method: "POST",
4661
+ credentials: "include",
4662
+ // Include cookies for session management
4663
+ headers: {
4664
+ "Content-Type": "application/json"
4665
+ }
4666
+ });
4667
+ } catch (error) {
4668
+ throw new SessionError(
4669
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
4670
+ "NETWORK_ERROR"
4671
+ );
5195
4672
  }
5196
- if (!import_fs.default.existsSync(value)) {
5197
- return "File does not exist";
4673
+ if (response.status === 401) {
4674
+ return;
5198
4675
  }
5199
- return true;
5200
- }
5201
- function assertValidFilePath(value) {
5202
- const result = validateFilePath(value);
5203
- if (result !== true) {
5204
- throw new Error(result);
4676
+ if (!response.ok) {
4677
+ const errorMessage = await parseErrorResponse(response);
4678
+ throw new SessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
5205
4679
  }
5206
4680
  }
5207
- function validateInstanceTypeSKU(sku, availableTypes) {
5208
- if (!sku) {
5209
- throw new Error("Instance type SKU cannot be empty");
5210
- }
5211
- for (const it of availableTypes) {
5212
- if (it.sku === sku) {
5213
- return sku;
5214
- }
5215
- }
5216
- const validSKUs = availableTypes.map((it) => it.sku).join(", ");
5217
- throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
4681
+ async function isSessionValid(config) {
4682
+ const session = await getComputeApiSession(config);
4683
+ return session.authenticated;
5218
4684
  }
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;
4685
+
4686
+ // src/client/common/utils/userapi.ts
4687
+ function isJsonObject(value) {
4688
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5225
4689
  }
5226
- function assertValidPrivateKey(key) {
5227
- if (!key) {
5228
- throw new Error("Private key is required");
5229
- }
5230
- if (!validatePrivateKeyFormat(key)) {
5231
- throw new Error(
5232
- "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
5233
- );
5234
- }
4690
+ function readString(obj, key) {
4691
+ const v = obj[key];
4692
+ return typeof v === "string" ? v : void 0;
5235
4693
  }
5236
- function validateURL(rawURL) {
5237
- if (!rawURL.trim()) {
5238
- return "URL cannot be empty";
4694
+ function readNumber(obj, key) {
4695
+ const v = obj[key];
4696
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
4697
+ }
4698
+ var MAX_ADDRESS_COUNT = 5;
4699
+ var CanViewAppLogsPermission = "0x2fd3f2fe";
4700
+ var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
4701
+ var CanUpdateAppProfilePermission = "0x036fef61";
4702
+ function getDefaultClientId() {
4703
+ const version = true ? "0.2.2-dev" : "0.0.0";
4704
+ return `ecloud-sdk/v${version}`;
4705
+ }
4706
+ var UserApiClient = class {
4707
+ constructor(config, walletClient, publicClient, options) {
4708
+ this.config = config;
4709
+ this.walletClient = walletClient;
4710
+ this.publicClient = publicClient;
4711
+ this.clientId = options?.clientId || getDefaultClientId();
4712
+ this.useSession = options?.useSession ?? false;
5239
4713
  }
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";
4714
+ /**
4715
+ * Get the address of the connected wallet
4716
+ */
4717
+ get address() {
4718
+ const account = this.walletClient.account;
4719
+ if (!account) {
4720
+ throw new Error("WalletClient must have an account attached");
5244
4721
  }
5245
- } catch {
5246
- return "Invalid URL format";
4722
+ return account.address;
5247
4723
  }
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;
4724
+ async getInfos(appIDs, addressCount = 1) {
4725
+ const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
4726
+ const endpoint = `${this.config.userApiServerURL}/info`;
4727
+ const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
4728
+ const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
4729
+ const result = await res.json();
4730
+ return result.apps.map((app, i) => {
4731
+ const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
4732
+ const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
4733
+ return {
4734
+ address: appIDs[i],
4735
+ status: app.app_status,
4736
+ ip: app.ip,
4737
+ machineType: app.machine_type,
4738
+ profile: app.profile,
4739
+ metrics: app.metrics,
4740
+ evmAddresses,
4741
+ solanaAddresses
4742
+ };
4743
+ });
5255
4744
  }
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)";
4745
+ /**
4746
+ * Get app details from UserAPI (includes releases and build/provenance info when available).
4747
+ *
4748
+ * Endpoint: GET /apps/:appAddress
4749
+ */
4750
+ async getApp(appAddress) {
4751
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
4752
+ const res = await this.makeAuthenticatedRequest(endpoint);
4753
+ const raw = await res.json();
4754
+ if (!isJsonObject(raw)) {
4755
+ throw new Error("Unexpected /apps/:id response: expected object");
5261
4756
  }
5262
- if (!url.pathname || url.pathname === "/") {
5263
- return "X URL must include a username or profile path";
4757
+ const id = readString(raw, "id");
4758
+ if (!id) {
4759
+ throw new Error("Unexpected /apps/:id response: missing 'id'");
5264
4760
  }
5265
- } catch {
5266
- return "Invalid X URL format";
4761
+ const releasesRaw = raw.releases;
4762
+ const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
4763
+ return {
4764
+ id,
4765
+ creator: readString(raw, "creator"),
4766
+ contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
4767
+ releases
4768
+ };
5267
4769
  }
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";
4770
+ /**
4771
+ * Get available SKUs (instance types) from UserAPI
4772
+ */
4773
+ async getSKUs() {
4774
+ const endpoint = `${this.config.userApiServerURL}/skus`;
4775
+ const response = await this.makeAuthenticatedRequest(endpoint);
4776
+ const result = await response.json();
4777
+ return {
4778
+ skus: result.skus || result.SKUs || []
4779
+ };
5274
4780
  }
5275
- if (description.length > MAX_DESCRIPTION_LENGTH) {
5276
- return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
4781
+ /**
4782
+ * Get logs for an app
4783
+ */
4784
+ async getLogs(appID) {
4785
+ const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
4786
+ const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
4787
+ return await response.text();
5277
4788
  }
5278
- return void 0;
5279
- }
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";
4789
+ /**
4790
+ * Get statuses for apps
4791
+ */
4792
+ async getStatuses(appIDs) {
4793
+ const endpoint = `${this.config.userApiServerURL}/status`;
4794
+ const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
4795
+ const response = await this.makeAuthenticatedRequest(url);
4796
+ const result = await response.json();
4797
+ const apps = result.apps || result.Apps || [];
4798
+ return apps.map((app, i) => ({
4799
+ address: app.address || appIDs[i],
4800
+ status: app.app_status || app.App_Status || ""
4801
+ }));
5286
4802
  }
5287
- if (!import_fs.default.existsSync(cleanedPath)) {
5288
- return `Image file not found: ${cleanedPath}`;
4803
+ /**
4804
+ * Upload app profile information with optional image
4805
+ *
4806
+ * @param appAddress - The app's contract address
4807
+ * @param name - Display name for the app
4808
+ * @param options - Optional fields including website, description, xURL, and image
4809
+ * @param options.image - Image file as Blob or File (browser: from input element, Node.js: new Blob([buffer]))
4810
+ * @param options.imageName - Filename for the image (required if image is provided)
4811
+ */
4812
+ async uploadAppProfile(appAddress, name, options) {
4813
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
4814
+ const formData = new FormData();
4815
+ formData.append("name", name);
4816
+ if (options?.website) {
4817
+ formData.append("website", options.website);
4818
+ }
4819
+ if (options?.description) {
4820
+ formData.append("description", options.description);
4821
+ }
4822
+ if (options?.xURL) {
4823
+ formData.append("xURL", options.xURL);
4824
+ }
4825
+ if (options?.image) {
4826
+ const fileName = options.image instanceof File ? options.image.name : options.imageName || "image";
4827
+ formData.append("image", options.image, fileName);
4828
+ }
4829
+ const headers = {
4830
+ "x-client-id": this.clientId
4831
+ };
4832
+ if (!this.useSession) {
4833
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
4834
+ const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
4835
+ Object.assign(headers, authHeaders);
4836
+ }
4837
+ try {
4838
+ const response = await import_axios.default.post(endpoint, formData, {
4839
+ headers,
4840
+ maxRedirects: 0,
4841
+ validateStatus: () => true,
4842
+ // Don't throw on any status
4843
+ maxContentLength: Infinity,
4844
+ // Allow large file uploads
4845
+ maxBodyLength: Infinity,
4846
+ // Allow large file uploads
4847
+ withCredentials: true
4848
+ // Include cookies for session auth
4849
+ });
4850
+ const status = response.status;
4851
+ if (status !== 200 && status !== 201) {
4852
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
4853
+ if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
4854
+ throw new Error(
4855
+ `Cloudflare protection is blocking the request. This is likely due to bot detection.
4856
+ Status: ${status}`
4857
+ );
4858
+ }
4859
+ throw new Error(
4860
+ `UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
4861
+ );
4862
+ }
4863
+ return response.data;
4864
+ } catch (error) {
4865
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
4866
+ const cause = error.cause?.message || error.cause || error.message;
4867
+ throw new Error(
4868
+ `Failed to connect to UserAPI at ${endpoint}: ${cause}
4869
+ Please check:
4870
+ 1. Your internet connection
4871
+ 2. The API server is accessible: ${this.config.userApiServerURL}
4872
+ 3. Firewall/proxy settings`
4873
+ );
4874
+ }
4875
+ throw error;
4876
+ }
5289
4877
  }
5290
- const stats = import_fs.default.statSync(cleanedPath);
5291
- if (stats.isDirectory()) {
5292
- return "Path is a directory, not a file";
4878
+ async makeAuthenticatedRequest(url, permission) {
4879
+ const headers = {
4880
+ "x-client-id": this.clientId
4881
+ };
4882
+ if (permission && !this.useSession) {
4883
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
4884
+ const authHeaders = await this.generateAuthHeaders(permission, expiry);
4885
+ Object.assign(headers, authHeaders);
4886
+ }
4887
+ try {
4888
+ const response = await import_axios.default.get(url, {
4889
+ headers,
4890
+ maxRedirects: 0,
4891
+ validateStatus: () => true,
4892
+ // Don't throw on any status
4893
+ withCredentials: true
4894
+ // Include cookies for session auth
4895
+ });
4896
+ const status = response.status;
4897
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
4898
+ if (status < 200 || status >= 300) {
4899
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
4900
+ throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
4901
+ }
4902
+ return {
4903
+ json: async () => response.data,
4904
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
4905
+ };
4906
+ } catch (error) {
4907
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
4908
+ const cause = error.cause?.message || error.cause || error.message;
4909
+ throw new Error(
4910
+ `Failed to connect to UserAPI at ${url}: ${cause}
4911
+ Please check:
4912
+ 1. Your internet connection
4913
+ 2. The API server is accessible: ${this.config.userApiServerURL}
4914
+ 3. Firewall/proxy settings`
4915
+ );
4916
+ }
4917
+ throw error;
4918
+ }
5293
4919
  }
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`;
4920
+ /**
4921
+ * Generate authentication headers for UserAPI requests
4922
+ */
4923
+ async generateAuthHeaders(permission, expiry) {
4924
+ const { signature } = await calculatePermissionSignature({
4925
+ permission,
4926
+ expiry,
4927
+ appControllerAddress: this.config.appControllerAddress,
4928
+ publicClient: this.publicClient,
4929
+ walletClient: this.walletClient
4930
+ });
4931
+ return {
4932
+ Authorization: `Bearer ${stripHexPrefix(signature)}`,
4933
+ "X-eigenx-expiry": expiry.toString()
4934
+ };
5297
4935
  }
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";
4936
+ // ==========================================================================
4937
+ // SIWE Session Management
4938
+ // ==========================================================================
4939
+ /**
4940
+ * Login to the compute API using SIWE (Sign-In with Ethereum)
4941
+ *
4942
+ * This establishes a session with the compute API by verifying the SIWE message
4943
+ * and signature. On success, a session cookie is set in the browser.
4944
+ *
4945
+ * @param request - Login request containing SIWE message and signature
4946
+ * @returns Login result with the authenticated address
4947
+ *
4948
+ * @example
4949
+ * ```typescript
4950
+ * import { createSiweMessage } from "@layr-labs/ecloud-sdk/browser";
4951
+ *
4952
+ * const { message } = createSiweMessage({
4953
+ * address: userAddress,
4954
+ * chainId: 11155111,
4955
+ * domain: window.location.host,
4956
+ * uri: window.location.origin,
4957
+ * });
4958
+ *
4959
+ * const signature = await signMessageAsync({ message });
4960
+ * const result = await client.siweLogin({ message, signature });
4961
+ * ```
4962
+ */
4963
+ async siweLogin(request) {
4964
+ return loginToComputeApi({ baseUrl: this.config.userApiServerURL }, request);
5301
4965
  }
5302
- return void 0;
5303
- }
5304
- function validateAppID(appID) {
5305
- if (!appID) {
5306
- throw new Error("App ID is required");
4966
+ /**
4967
+ * Logout from the compute API
4968
+ *
4969
+ * This destroys the current session and clears the session cookie.
4970
+ *
4971
+ * @example
4972
+ * ```typescript
4973
+ * await client.siweLogout();
4974
+ * ```
4975
+ */
4976
+ async siweLogout() {
4977
+ return logoutFromComputeApi({ baseUrl: this.config.userApiServerURL });
5307
4978
  }
5308
- const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
5309
- if ((0, import_viem6.isAddress)(normalized)) {
5310
- return normalized;
4979
+ /**
4980
+ * Get the current SIWE session status from the compute API
4981
+ *
4982
+ * @returns Session information including authentication status and address
4983
+ *
4984
+ * @example
4985
+ * ```typescript
4986
+ * const session = await client.getSiweSession();
4987
+ * if (session.authenticated) {
4988
+ * console.log(`Logged in as ${session.address}`);
4989
+ * }
4990
+ * ```
4991
+ */
4992
+ async getSiweSession() {
4993
+ return getComputeApiSession({ baseUrl: this.config.userApiServerURL });
5311
4994
  }
5312
- throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
4995
+ };
4996
+ function transformAppReleaseBuild(raw) {
4997
+ if (!isJsonObject(raw)) return void 0;
4998
+ const depsRaw = raw.dependencies;
4999
+ const deps = isJsonObject(depsRaw) ? Object.fromEntries(
5000
+ Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
5001
+ const parsed = transformAppReleaseBuild(depRaw);
5002
+ return parsed ? [[digest, parsed]] : [];
5003
+ })
5004
+ ) : void 0;
5005
+ return {
5006
+ buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
5007
+ billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
5008
+ repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
5009
+ gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
5010
+ status: readString(raw, "status"),
5011
+ buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
5012
+ imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
5013
+ imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
5014
+ imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
5015
+ provenanceJson: raw.provenance_json ?? raw.provenanceJson,
5016
+ provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
5017
+ createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
5018
+ updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
5019
+ errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
5020
+ dependencies: deps
5021
+ };
5313
5022
  }
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
- );
5326
- }
5023
+ function transformAppRelease(raw) {
5024
+ if (!isJsonObject(raw)) return void 0;
5025
+ return {
5026
+ appId: readString(raw, "appId") ?? readString(raw, "app_id"),
5027
+ rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
5028
+ imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
5029
+ registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
5030
+ publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
5031
+ encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
5032
+ upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
5033
+ createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
5034
+ createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
5035
+ build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
5036
+ };
5327
5037
  }
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
- );
5038
+
5039
+ // src/client/common/contract/watcher.ts
5040
+ var WATCH_POLL_INTERVAL_SECONDS = 5;
5041
+ var APP_STATUS_RUNNING = "Running";
5042
+ var APP_STATUS_FAILED = "Failed";
5043
+ async function watchUntilRunning(options, logger) {
5044
+ const { walletClient, publicClient, environmentConfig, appId } = options;
5045
+ const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
5046
+ let initialStatus;
5047
+ let initialIP;
5048
+ let hasChanged = false;
5049
+ const stopCondition = (status, ip) => {
5050
+ if (!initialStatus) {
5051
+ initialStatus = status;
5052
+ initialIP = ip;
5053
+ }
5054
+ if (status !== initialStatus) {
5055
+ hasChanged = true;
5056
+ }
5057
+ if (status === APP_STATUS_RUNNING && ip) {
5058
+ if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
5059
+ if (!initialIP || initialIP === "No IP assigned") {
5060
+ logger.info(`App is now running with IP: ${ip}`);
5061
+ } else {
5062
+ logger.info("App is now running");
5063
+ }
5064
+ return true;
5065
+ }
5066
+ }
5067
+ if (status === APP_STATUS_FAILED) {
5068
+ throw new Error(`App entered ${status} state`);
5069
+ }
5070
+ return false;
5071
+ };
5072
+ while (true) {
5073
+ try {
5074
+ const info = await userApiClient.getInfos([appId], 1);
5075
+ if (info.length === 0) {
5076
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5077
+ continue;
5078
+ }
5079
+ const appInfo = info[0];
5080
+ const currentStatus = appInfo.status;
5081
+ const currentIP = appInfo.ip || "";
5082
+ if (stopCondition(currentStatus, currentIP)) {
5083
+ return currentIP || void 0;
5084
+ }
5085
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5086
+ } catch (error) {
5087
+ logger.warn(`Failed to fetch app info: ${error.message}`);
5088
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5089
+ }
5341
5090
  }
5342
5091
  }
5343
- function hasScheme(rawURL) {
5344
- return rawURL.startsWith("http://") || rawURL.startsWith("https://");
5092
+ var APP_STATUS_STOPPED = "Stopped";
5093
+ async function watchUntilUpgradeComplete(options, logger) {
5094
+ const { walletClient, publicClient, environmentConfig, appId } = options;
5095
+ const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
5096
+ let initialStatus;
5097
+ let initialIP;
5098
+ let hasChanged = false;
5099
+ const stopCondition = (status, ip) => {
5100
+ if (!initialStatus) {
5101
+ initialStatus = status;
5102
+ initialIP = ip;
5103
+ if (status === APP_STATUS_STOPPED && ip) {
5104
+ logger.info("App upgrade complete.");
5105
+ logger.info(`Status: ${status}`);
5106
+ logger.info(`To start the app, run: ecloud compute app start ${appId}`);
5107
+ return true;
5108
+ }
5109
+ }
5110
+ if (status !== initialStatus) {
5111
+ hasChanged = true;
5112
+ }
5113
+ if (status === APP_STATUS_STOPPED && ip && hasChanged) {
5114
+ logger.info("App upgrade complete.");
5115
+ logger.info(`Status: ${status}`);
5116
+ logger.info(`To start the app, run: ecloud compute app start ${appId}`);
5117
+ return true;
5118
+ }
5119
+ if (status === APP_STATUS_RUNNING && ip && hasChanged) {
5120
+ if (!initialIP || initialIP === "No IP assigned") {
5121
+ logger.info(`App is now running with IP: ${ip}`);
5122
+ } else {
5123
+ logger.info("App is now running");
5124
+ }
5125
+ return true;
5126
+ }
5127
+ if (status === APP_STATUS_FAILED) {
5128
+ throw new Error(`App entered ${status} state`);
5129
+ }
5130
+ return false;
5131
+ };
5132
+ while (true) {
5133
+ try {
5134
+ const info = await userApiClient.getInfos([appId], 1);
5135
+ if (info.length === 0) {
5136
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5137
+ continue;
5138
+ }
5139
+ const appInfo = info[0];
5140
+ const currentStatus = appInfo.status;
5141
+ const currentIP = appInfo.ip || "";
5142
+ if (stopCondition(currentStatus, currentIP)) {
5143
+ return;
5144
+ }
5145
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5146
+ } catch (error) {
5147
+ logger.warn(`Failed to fetch app info: ${error.message}`);
5148
+ await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5149
+ }
5150
+ }
5345
5151
  }
5346
- function sanitizeString(s) {
5347
- return s.trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5152
+ function sleep(ms) {
5153
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
5348
5154
  }
5349
- function sanitizeURL(rawURL) {
5350
- rawURL = rawURL.trim();
5351
- if (!hasScheme(rawURL)) {
5352
- rawURL = "https://" + rawURL;
5155
+
5156
+ // src/client/common/utils/validation.ts
5157
+ var import_fs = __toESM(require("fs"), 1);
5158
+ var import_path = __toESM(require("path"), 1);
5159
+ var import_viem5 = require("viem");
5160
+ function validateAppName(name) {
5161
+ if (!name) {
5162
+ throw new Error("App name cannot be empty");
5353
5163
  }
5354
- const err = validateURL(rawURL);
5355
- if (err) {
5356
- throw new Error(err);
5164
+ if (name.includes(" ")) {
5165
+ throw new Error("App name cannot contain spaces");
5166
+ }
5167
+ if (name.length > 50) {
5168
+ throw new Error("App name cannot be longer than 50 characters");
5357
5169
  }
5358
- return rawURL;
5359
5170
  }
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;
5171
+ function validateImageReference(value) {
5172
+ if (!value) {
5173
+ return "Image reference cannot be empty";
5367
5174
  }
5368
- rawURL = rawURL.replace(/twitter\.com/g, "x.com");
5369
- rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
5370
- const err = validateXURL(rawURL);
5175
+ if (!value.includes("/")) {
5176
+ return "Image reference must contain at least one /";
5177
+ }
5178
+ return true;
5179
+ }
5180
+ function assertValidImageReference(value) {
5181
+ const result = validateImageReference(value);
5182
+ if (result !== true) {
5183
+ throw new Error(result);
5184
+ }
5185
+ }
5186
+ function extractAppNameFromImage(imageRef) {
5187
+ const parts = imageRef.split("/");
5188
+ let imageName = parts.length > 1 ? parts[parts.length - 1] : imageRef;
5189
+ if (imageName.includes(":")) {
5190
+ imageName = imageName.split(":")[0];
5191
+ }
5192
+ return imageName;
5193
+ }
5194
+ function validateFilePath(value) {
5195
+ if (!value) {
5196
+ return "File path cannot be empty";
5197
+ }
5198
+ if (!import_fs.default.existsSync(value)) {
5199
+ return "File does not exist";
5200
+ }
5201
+ return true;
5202
+ }
5203
+ function assertValidFilePath(value) {
5204
+ const result = validateFilePath(value);
5205
+ if (result !== true) {
5206
+ throw new Error(result);
5207
+ }
5208
+ }
5209
+ function validateInstanceTypeSKU(sku, availableTypes) {
5210
+ if (!sku) {
5211
+ throw new Error("Instance type SKU cannot be empty");
5212
+ }
5213
+ for (const it of availableTypes) {
5214
+ if (it.sku === sku) {
5215
+ return sku;
5216
+ }
5217
+ }
5218
+ const validSKUs = availableTypes.map((it) => it.sku).join(", ");
5219
+ throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
5220
+ }
5221
+ function validatePrivateKeyFormat(key) {
5222
+ const keyWithoutPrefix = stripHexPrefix(key);
5223
+ if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
5224
+ return false;
5225
+ }
5226
+ return true;
5227
+ }
5228
+ function assertValidPrivateKey(key) {
5229
+ if (!key) {
5230
+ throw new Error("Private key is required");
5231
+ }
5232
+ if (!validatePrivateKeyFormat(key)) {
5233
+ throw new Error(
5234
+ "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
5235
+ );
5236
+ }
5237
+ }
5238
+ function validateURL(rawURL) {
5239
+ if (!rawURL.trim()) {
5240
+ return "URL cannot be empty";
5241
+ }
5242
+ try {
5243
+ const url = new URL(rawURL);
5244
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
5245
+ return "URL scheme must be http or https";
5246
+ }
5247
+ } catch {
5248
+ return "Invalid URL format";
5249
+ }
5250
+ return void 0;
5251
+ }
5252
+ var VALID_X_HOSTS = ["twitter.com", "www.twitter.com", "x.com", "www.x.com"];
5253
+ function validateXURL(rawURL) {
5254
+ const urlErr = validateURL(rawURL);
5255
+ if (urlErr) {
5256
+ return urlErr;
5257
+ }
5258
+ try {
5259
+ const url = new URL(rawURL);
5260
+ const host = url.hostname.toLowerCase();
5261
+ if (!VALID_X_HOSTS.includes(host)) {
5262
+ return "URL must be a valid X/Twitter URL (x.com or twitter.com)";
5263
+ }
5264
+ if (!url.pathname || url.pathname === "/") {
5265
+ return "X URL must include a username or profile path";
5266
+ }
5267
+ } catch {
5268
+ return "Invalid X URL format";
5269
+ }
5270
+ return void 0;
5271
+ }
5272
+ var MAX_DESCRIPTION_LENGTH = 1e3;
5273
+ function validateDescription(description) {
5274
+ if (!description.trim()) {
5275
+ return "Description cannot be empty";
5276
+ }
5277
+ if (description.length > MAX_DESCRIPTION_LENGTH) {
5278
+ return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
5279
+ }
5280
+ return void 0;
5281
+ }
5282
+ var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
5283
+ var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
5284
+ function validateImagePath(filePath) {
5285
+ const cleanedPath = filePath.trim().replace(/^["']|["']$/g, "");
5286
+ if (!cleanedPath) {
5287
+ return "Image path cannot be empty";
5288
+ }
5289
+ if (!import_fs.default.existsSync(cleanedPath)) {
5290
+ return `Image file not found: ${cleanedPath}`;
5291
+ }
5292
+ const stats = import_fs.default.statSync(cleanedPath);
5293
+ if (stats.isDirectory()) {
5294
+ return "Path is a directory, not a file";
5295
+ }
5296
+ if (stats.size > MAX_IMAGE_SIZE) {
5297
+ const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
5298
+ return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
5299
+ }
5300
+ const ext = import_path.default.extname(cleanedPath).toLowerCase();
5301
+ if (!VALID_IMAGE_EXTENSIONS.includes(ext)) {
5302
+ return "Image must be JPG or PNG format";
5303
+ }
5304
+ return void 0;
5305
+ }
5306
+ function validateAppID(appID) {
5307
+ if (!appID) {
5308
+ throw new Error("App ID is required");
5309
+ }
5310
+ const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
5311
+ if ((0, import_viem5.isAddress)(normalized)) {
5312
+ return normalized;
5313
+ }
5314
+ throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
5315
+ }
5316
+ function validateLogVisibility(logVisibility) {
5317
+ switch (logVisibility) {
5318
+ case "public":
5319
+ return { logRedirect: "always", publicLogs: true };
5320
+ case "private":
5321
+ return { logRedirect: "always", publicLogs: false };
5322
+ case "off":
5323
+ return { logRedirect: "", publicLogs: false };
5324
+ default:
5325
+ throw new Error(
5326
+ `Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
5327
+ );
5328
+ }
5329
+ }
5330
+ function validateResourceUsageMonitoring(resourceUsageMonitoring) {
5331
+ if (!resourceUsageMonitoring) {
5332
+ return "always";
5333
+ }
5334
+ switch (resourceUsageMonitoring) {
5335
+ case "enable":
5336
+ return "always";
5337
+ case "disable":
5338
+ return "never";
5339
+ default:
5340
+ throw new Error(
5341
+ `Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
5342
+ );
5343
+ }
5344
+ }
5345
+ function hasScheme(rawURL) {
5346
+ return rawURL.startsWith("http://") || rawURL.startsWith("https://");
5347
+ }
5348
+ function sanitizeString(s) {
5349
+ return s.trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5350
+ }
5351
+ function sanitizeURL(rawURL) {
5352
+ rawURL = rawURL.trim();
5353
+ if (!hasScheme(rawURL)) {
5354
+ rawURL = "https://" + rawURL;
5355
+ }
5356
+ const err = validateURL(rawURL);
5357
+ if (err) {
5358
+ throw new Error(err);
5359
+ }
5360
+ return rawURL;
5361
+ }
5362
+ function sanitizeXURL(rawURL) {
5363
+ rawURL = rawURL.trim();
5364
+ if (!rawURL.includes("://") && !rawURL.includes(".")) {
5365
+ const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
5366
+ rawURL = `https://x.com/${username}`;
5367
+ } else if (!hasScheme(rawURL)) {
5368
+ rawURL = "https://" + rawURL;
5369
+ }
5370
+ rawURL = rawURL.replace(/twitter\.com/g, "x.com");
5371
+ rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
5372
+ const err = validateXURL(rawURL);
5371
5373
  if (err) {
5372
5374
  throw new Error(err);
5373
5375
  }
@@ -5447,84 +5449,268 @@ function validateLogsParams(params) {
5447
5449
  validateAppID(params.appID);
5448
5450
  }
5449
5451
 
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;
5461
- }
5462
- if (!rpcUrl) {
5463
- throw new Error(
5464
- `RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
5465
- );
5466
- }
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}`);
5475
- }
5476
- } catch (err) {
5477
- throw new Error(`Cannot connect to ${environmentConfig.name} RPC at ${rpcUrl}: ${err.message}`);
5478
- }
5479
- const privateKeyHex = addHexPrefix(privateKey);
5480
- const account = (0, import_accounts4.privateKeyToAccount)(privateKeyHex);
5481
- const selfAddress = account.address;
5482
- return {
5483
- privateKey: privateKeyHex,
5484
- rpcUrl,
5485
- environmentConfig,
5486
- account,
5487
- selfAddress
5488
- };
5489
- }
5490
- async function getPrivateKeyOrFail(privateKey) {
5491
- if (privateKey) {
5492
- validatePrivateKey(privateKey);
5493
- return privateKey;
5494
- }
5495
- if (process.env.PRIVATE_KEY) {
5496
- validatePrivateKey(process.env.PRIVATE_KEY);
5497
- return process.env.PRIVATE_KEY;
5498
- }
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
- );
5505
- }
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)");
5510
- }
5511
- }
5512
-
5513
- // src/client/common/telemetry/noop.ts
5514
- var NoopClient = class {
5515
- /**
5516
- * AddMetric implements the TelemetryClient interface
5517
- */
5518
- async addMetric(_metric) {
5519
- }
5520
- /**
5521
- * Close implements the TelemetryClient interface
5522
- */
5523
- async close() {
5452
+ // src/client/common/config/environment.ts
5453
+ var SEPOLIA_CHAIN_ID = 11155111;
5454
+ var MAINNET_CHAIN_ID = 1;
5455
+ var CommonAddresses = {
5456
+ ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
5457
+ };
5458
+ var ChainAddresses = {
5459
+ [MAINNET_CHAIN_ID]: {
5460
+ PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
5461
+ },
5462
+ [SEPOLIA_CHAIN_ID]: {
5463
+ PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
5524
5464
  }
5525
5465
  };
5526
- function isNoopClient(client) {
5527
- return client instanceof NoopClient;
5466
+ var BILLING_ENVIRONMENTS = {
5467
+ dev: {
5468
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
5469
+ },
5470
+ prod: {
5471
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
5472
+ }
5473
+ };
5474
+ var ENVIRONMENTS = {
5475
+ "sepolia-dev": {
5476
+ name: "sepolia",
5477
+ build: "dev",
5478
+ appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
5479
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5480
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5481
+ kmsServerURL: "http://10.128.0.57:8080",
5482
+ userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
5483
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
5484
+ },
5485
+ sepolia: {
5486
+ name: "sepolia",
5487
+ build: "prod",
5488
+ appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
5489
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5490
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5491
+ kmsServerURL: "http://10.128.15.203:8080",
5492
+ userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
5493
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
5494
+ },
5495
+ "mainnet-alpha": {
5496
+ name: "mainnet-alpha",
5497
+ build: "prod",
5498
+ appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
5499
+ permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
5500
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5501
+ kmsServerURL: "http://10.128.0.2:8080",
5502
+ userApiServerURL: "https://userapi-compute.eigencloud.xyz",
5503
+ defaultRPCURL: "https://ethereum-rpc.publicnode.com"
5504
+ }
5505
+ };
5506
+ var CHAIN_ID_TO_ENVIRONMENT = {
5507
+ [SEPOLIA_CHAIN_ID.toString()]: "sepolia",
5508
+ [MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
5509
+ };
5510
+ function getEnvironmentConfig(environment, chainID) {
5511
+ const env = ENVIRONMENTS[environment];
5512
+ if (!env) {
5513
+ throw new Error(`Unknown environment: ${environment}`);
5514
+ }
5515
+ if (!isEnvironmentAvailable(environment)) {
5516
+ throw new Error(
5517
+ `Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
5518
+ );
5519
+ }
5520
+ if (chainID) {
5521
+ const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
5522
+ if (expectedEnv && expectedEnv !== environment) {
5523
+ throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
5524
+ }
5525
+ }
5526
+ const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
5527
+ return {
5528
+ ...env,
5529
+ chainID: BigInt(resolvedChainID)
5530
+ };
5531
+ }
5532
+ function getBillingEnvironmentConfig(build) {
5533
+ const config = BILLING_ENVIRONMENTS[build];
5534
+ if (!config) {
5535
+ throw new Error(`Unknown billing environment: ${build}`);
5536
+ }
5537
+ return config;
5538
+ }
5539
+ function getBuildType() {
5540
+ const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
5541
+ const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
5542
+ const buildType = buildTimeType || runtimeType;
5543
+ if (buildType === "dev") {
5544
+ return "dev";
5545
+ }
5546
+ return "prod";
5547
+ }
5548
+ function getAvailableEnvironments() {
5549
+ const buildType = getBuildType();
5550
+ if (buildType === "dev") {
5551
+ return ["sepolia-dev"];
5552
+ }
5553
+ return ["sepolia", "mainnet-alpha"];
5554
+ }
5555
+ function isEnvironmentAvailable(environment) {
5556
+ return getAvailableEnvironments().includes(environment);
5557
+ }
5558
+ function isMainnet(environmentConfig) {
5559
+ return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
5560
+ }
5561
+
5562
+ // src/client/common/utils/preflight.ts
5563
+ async function doPreflightChecks(options, logger) {
5564
+ const { walletClient, publicClient } = options;
5565
+ logger.debug("Determining environment...");
5566
+ const environmentConfig = getEnvironmentConfig(options.environment || "sepolia");
5567
+ const account = walletClient.account;
5568
+ if (!account) {
5569
+ throw new Error("WalletClient must have an account attached");
5570
+ }
5571
+ logger.debug("Validating chain ID...");
5572
+ try {
5573
+ const chainID = await publicClient.getChainId();
5574
+ if (BigInt(chainID) !== environmentConfig.chainID) {
5575
+ throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
5576
+ }
5577
+ } catch (err) {
5578
+ throw new Error(
5579
+ `Cannot connect to ${environmentConfig.name} RPC at ${publicClient.transport.url}: ${err.message}`
5580
+ );
5581
+ }
5582
+ return {
5583
+ walletClient,
5584
+ publicClient,
5585
+ environmentConfig,
5586
+ selfAddress: account.address
5587
+ };
5588
+ }
5589
+
5590
+ // src/client/common/utils/logger.ts
5591
+ var defaultLogger = {
5592
+ info: (...args) => console.info(...args),
5593
+ warn: (...args) => console.warn(...args),
5594
+ error: (...args) => console.error(...args),
5595
+ debug: (...args) => console.debug(...args)
5596
+ };
5597
+ var getLogger = (verbose) => ({
5598
+ info: (...args) => console.info(...args),
5599
+ warn: (...args) => console.warn(...args),
5600
+ error: (...args) => console.error(...args),
5601
+ debug: (...args) => verbose && console.debug(...args)
5602
+ });
5603
+
5604
+ // src/client/common/utils/billing.ts
5605
+ function isSubscriptionActive(status) {
5606
+ return status === "active" || status === "trialing";
5607
+ }
5608
+
5609
+ // src/client/common/utils/billingapi.ts
5610
+ var import_axios2 = __toESM(require("axios"), 1);
5611
+ var BillingApiClient = class {
5612
+ constructor(config, walletClient) {
5613
+ this.config = config;
5614
+ this.walletClient = walletClient;
5615
+ }
5616
+ /**
5617
+ * Get the address of the connected wallet
5618
+ */
5619
+ get address() {
5620
+ const account = this.walletClient.account;
5621
+ if (!account) {
5622
+ throw new Error("WalletClient must have an account attached");
5623
+ }
5624
+ return account.address;
5625
+ }
5626
+ async createSubscription(productId = "compute", options) {
5627
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5628
+ const body = options ? {
5629
+ success_url: options.successUrl,
5630
+ cancel_url: options.cancelUrl
5631
+ } : void 0;
5632
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId, body);
5633
+ return resp.json();
5634
+ }
5635
+ async getSubscription(productId = "compute") {
5636
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5637
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
5638
+ return resp.json();
5639
+ }
5640
+ async cancelSubscription(productId = "compute") {
5641
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
5642
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
5643
+ }
5644
+ /**
5645
+ * Make an authenticated request to the billing API
5646
+ */
5647
+ async makeAuthenticatedRequest(url, method, productId, body) {
5648
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
5649
+ const { signature } = await calculateBillingAuthSignature({
5650
+ walletClient: this.walletClient,
5651
+ product: productId,
5652
+ expiry
5653
+ });
5654
+ const headers = {
5655
+ Authorization: `Bearer ${signature}`,
5656
+ "X-Account": this.address,
5657
+ "X-Expiry": expiry.toString()
5658
+ };
5659
+ if (body) {
5660
+ headers["Content-Type"] = "application/json";
5661
+ }
5662
+ try {
5663
+ const response = await (0, import_axios2.default)({
5664
+ method,
5665
+ url,
5666
+ headers,
5667
+ data: body,
5668
+ timeout: 3e4,
5669
+ maxRedirects: 0,
5670
+ validateStatus: () => true
5671
+ // Don't throw on any status
5672
+ });
5673
+ const status = response.status;
5674
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
5675
+ if (status < 200 || status >= 300) {
5676
+ const body2 = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
5677
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body2}`);
5678
+ }
5679
+ return {
5680
+ json: async () => response.data,
5681
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
5682
+ };
5683
+ } catch (error) {
5684
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
5685
+ const cause = error.cause?.message || error.cause || error.message;
5686
+ throw new Error(
5687
+ `Failed to connect to BillingAPI at ${url}: ${cause}
5688
+ Please check:
5689
+ 1. Your internet connection
5690
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
5691
+ 3. Firewall/proxy settings`
5692
+ );
5693
+ }
5694
+ throw error;
5695
+ }
5696
+ }
5697
+ };
5698
+
5699
+ // src/client/common/telemetry/noop.ts
5700
+ var NoopClient = class {
5701
+ /**
5702
+ * AddMetric implements the TelemetryClient interface
5703
+ */
5704
+ async addMetric(_metric) {
5705
+ }
5706
+ /**
5707
+ * Close implements the TelemetryClient interface
5708
+ */
5709
+ async close() {
5710
+ }
5711
+ };
5712
+ function isNoopClient(client) {
5713
+ return client instanceof NoopClient;
5528
5714
  }
5529
5715
 
5530
5716
  // src/client/common/telemetry/posthog.ts
@@ -5716,7 +5902,9 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5716
5902
  }
5717
5903
  },
5718
5904
  async () => {
5719
- if (!options.privateKey) throw new Error("privateKey is required for deployment");
5905
+ if (!options.walletClient.account) {
5906
+ throw new Error("WalletClient must have an account attached");
5907
+ }
5720
5908
  if (!options.imageRef) throw new Error("imageRef is required for deployment");
5721
5909
  if (!options.imageDigest) throw new Error("imageDigest is required for deployment");
5722
5910
  assertValidImageReference(options.imageRef);
@@ -5732,8 +5920,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5732
5920
  logger.debug("Performing preflight checks...");
5733
5921
  const preflightCtx = await doPreflightChecks(
5734
5922
  {
5735
- privateKey: options.privateKey,
5736
- rpcUrl: options.rpcUrl,
5923
+ walletClient: options.walletClient,
5924
+ publicClient: options.publicClient,
5737
5925
  environment: options.environment
5738
5926
  },
5739
5927
  logger
@@ -5743,12 +5931,12 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5743
5931
  const salt = generateRandomSalt();
5744
5932
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5745
5933
  logger.debug("Calculating app ID...");
5746
- const appIDToBeDeployed = await calculateAppID(
5747
- preflightCtx.privateKey,
5748
- options.rpcUrl || preflightCtx.rpcUrl,
5749
- preflightCtx.environmentConfig,
5934
+ const appIDToBeDeployed = await calculateAppID({
5935
+ publicClient: preflightCtx.publicClient,
5936
+ environmentConfig: preflightCtx.environmentConfig,
5937
+ ownerAddress: preflightCtx.selfAddress,
5750
5938
  salt
5751
- );
5939
+ });
5752
5940
  logger.info(``);
5753
5941
  logger.info(`App ID: ${appIDToBeDeployed}`);
5754
5942
  logger.info(``);
@@ -5766,12 +5954,13 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5766
5954
  logger.debug("Preparing deploy batch...");
5767
5955
  const batch = await prepareDeployBatch(
5768
5956
  {
5769
- privateKey: preflightCtx.privateKey,
5770
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5957
+ walletClient: preflightCtx.walletClient,
5958
+ publicClient: preflightCtx.publicClient,
5771
5959
  environmentConfig: preflightCtx.environmentConfig,
5772
5960
  salt,
5773
5961
  release,
5774
- publicLogs
5962
+ publicLogs,
5963
+ imageRef: options.imageRef
5775
5964
  },
5776
5965
  logger
5777
5966
  );
@@ -5798,8 +5987,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
5798
5987
  );
5799
5988
  }
5800
5989
  function validateDeployOptions(options) {
5801
- if (!options.privateKey) {
5802
- throw new Error("privateKey is required for deployment");
5990
+ if (!options.walletClient.account) {
5991
+ throw new Error("WalletClient must have an account attached");
5803
5992
  }
5804
5993
  if (!options.dockerfilePath && !options.imageRef) {
5805
5994
  throw new Error("Either dockerfilePath or imageRef is required for deployment");
@@ -5838,8 +6027,8 @@ async function deploy(options, logger = defaultLogger) {
5838
6027
  logger.debug("Performing preflight checks...");
5839
6028
  const preflightCtx = await doPreflightChecks(
5840
6029
  {
5841
- privateKey: options.privateKey,
5842
- rpcUrl: options.rpcUrl,
6030
+ walletClient: options.walletClient,
6031
+ publicClient: options.publicClient,
5843
6032
  environment: options.environment
5844
6033
  },
5845
6034
  logger
@@ -5856,12 +6045,12 @@ async function deploy(options, logger = defaultLogger) {
5856
6045
  const salt = generateRandomSalt();
5857
6046
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5858
6047
  logger.debug("Calculating app ID...");
5859
- const appIDToBeDeployed = await calculateAppID(
5860
- preflightCtx.privateKey,
5861
- options.rpcUrl || preflightCtx.rpcUrl,
5862
- preflightCtx.environmentConfig,
6048
+ const appIDToBeDeployed = await calculateAppID({
6049
+ publicClient: preflightCtx.publicClient,
6050
+ environmentConfig: preflightCtx.environmentConfig,
6051
+ ownerAddress: preflightCtx.selfAddress,
5863
6052
  salt
5864
- );
6053
+ });
5865
6054
  logger.info(``);
5866
6055
  logger.info(`App ID: ${appIDToBeDeployed}`);
5867
6056
  logger.info(``);
@@ -5882,8 +6071,8 @@ async function deploy(options, logger = defaultLogger) {
5882
6071
  logger.info("Deploying on-chain...");
5883
6072
  const deployResult = await deployApp(
5884
6073
  {
5885
- privateKey: preflightCtx.privateKey,
5886
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6074
+ walletClient: preflightCtx.walletClient,
6075
+ publicClient: preflightCtx.publicClient,
5887
6076
  environmentConfig: preflightCtx.environmentConfig,
5888
6077
  salt,
5889
6078
  release,
@@ -5896,8 +6085,8 @@ async function deploy(options, logger = defaultLogger) {
5896
6085
  logger.info("Waiting for app to start...");
5897
6086
  const ipAddress = await watchUntilRunning(
5898
6087
  {
5899
- privateKey: preflightCtx.privateKey,
5900
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6088
+ walletClient: preflightCtx.walletClient,
6089
+ publicClient: preflightCtx.publicClient,
5901
6090
  environmentConfig: preflightCtx.environmentConfig,
5902
6091
  appId: deployResult.appId
5903
6092
  },
@@ -5914,12 +6103,10 @@ async function deploy(options, logger = defaultLogger) {
5914
6103
  );
5915
6104
  }
5916
6105
  async function checkQuotaAvailable(preflightCtx) {
5917
- const rpcUrl = preflightCtx.rpcUrl;
5918
- const environmentConfig = preflightCtx.environmentConfig;
5919
- const userAddress = preflightCtx.selfAddress;
6106
+ const { publicClient, environmentConfig, selfAddress: userAddress } = preflightCtx;
5920
6107
  let maxQuota;
5921
6108
  try {
5922
- maxQuota = await getMaxActiveAppsPerUser(rpcUrl, environmentConfig, userAddress);
6109
+ maxQuota = await getMaxActiveAppsPerUser(publicClient, environmentConfig, userAddress);
5923
6110
  } catch (err) {
5924
6111
  throw new Error(`failed to get quota limit: ${err.message}`);
5925
6112
  }
@@ -5930,7 +6117,7 @@ async function checkQuotaAvailable(preflightCtx) {
5930
6117
  }
5931
6118
  let activeCount;
5932
6119
  try {
5933
- activeCount = await getActiveAppCount(rpcUrl, environmentConfig, userAddress);
6120
+ activeCount = await getActiveAppCount(publicClient, environmentConfig, userAddress);
5934
6121
  } catch (err) {
5935
6122
  throw new Error(`failed to get active app count: ${err.message}`);
5936
6123
  }
@@ -5961,8 +6148,8 @@ async function prepareDeploy(options, logger = defaultLogger) {
5961
6148
  logger.debug("Performing preflight checks...");
5962
6149
  const preflightCtx = await doPreflightChecks(
5963
6150
  {
5964
- privateKey: options.privateKey,
5965
- rpcUrl: options.rpcUrl,
6151
+ walletClient: options.walletClient,
6152
+ publicClient: options.publicClient,
5966
6153
  environment: options.environment
5967
6154
  },
5968
6155
  logger
@@ -5979,12 +6166,12 @@ async function prepareDeploy(options, logger = defaultLogger) {
5979
6166
  const salt = generateRandomSalt();
5980
6167
  logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5981
6168
  logger.debug("Calculating app ID...");
5982
- const appIDToBeDeployed = await calculateAppID(
5983
- preflightCtx.privateKey,
5984
- options.rpcUrl || preflightCtx.rpcUrl,
5985
- preflightCtx.environmentConfig,
6169
+ const appIDToBeDeployed = await calculateAppID({
6170
+ publicClient: preflightCtx.publicClient,
6171
+ environmentConfig: preflightCtx.environmentConfig,
6172
+ ownerAddress: preflightCtx.selfAddress,
5986
6173
  salt
5987
- );
6174
+ });
5988
6175
  logger.info(``);
5989
6176
  logger.info(`App ID: ${appIDToBeDeployed}`);
5990
6177
  logger.info(``);
@@ -6005,12 +6192,13 @@ async function prepareDeploy(options, logger = defaultLogger) {
6005
6192
  logger.debug("Preparing deploy batch...");
6006
6193
  const batch = await prepareDeployBatch(
6007
6194
  {
6008
- privateKey: preflightCtx.privateKey,
6009
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6195
+ walletClient: preflightCtx.walletClient,
6196
+ publicClient: preflightCtx.publicClient,
6010
6197
  environmentConfig: preflightCtx.environmentConfig,
6011
6198
  salt,
6012
6199
  release,
6013
- publicLogs
6200
+ publicLogs,
6201
+ imageRef: finalImageRef
6014
6202
  },
6015
6203
  logger
6016
6204
  );
@@ -6055,25 +6243,23 @@ async function executeDeploy(options) {
6055
6243
  }
6056
6244
  );
6057
6245
  }
6058
- async function watchDeployment(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
6246
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
6059
6247
  return withSDKTelemetry(
6060
6248
  {
6061
6249
  functionName: "watchDeployment",
6062
6250
  skipTelemetry,
6063
6251
  properties: {
6064
- environment
6252
+ environment: environmentConfig.name
6065
6253
  }
6066
6254
  },
6067
6255
  async () => {
6068
- const environmentConfig = getEnvironmentConfig(environment);
6069
6256
  logger.info("Waiting for app to start...");
6070
6257
  return watchUntilRunning(
6071
6258
  {
6072
- privateKey,
6073
- rpcUrl,
6259
+ walletClient,
6260
+ publicClient,
6074
6261
  environmentConfig,
6075
- appId,
6076
- clientId
6262
+ appId
6077
6263
  },
6078
6264
  logger
6079
6265
  );
@@ -6082,18 +6268,12 @@ async function watchDeployment(appId, privateKey, rpcUrl, environment, logger =
6082
6268
  }
6083
6269
 
6084
6270
  // src/client/common/utils/permissions.ts
6085
- var import_viem8 = require("viem");
6086
6271
  var AnyoneCanCallAddress = "0x493219d9949348178af1f58740655951a8cd110c";
6087
6272
  var ApiPermissionsTarget = "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d";
6088
6273
  var CanViewAppLogsPermission2 = "0x2fd3f2fe";
6089
6274
  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
6275
  try {
6096
- const canCall = await publicClient.readContract({
6276
+ const canCall = await preflightCtx.publicClient.readContract({
6097
6277
  address: preflightCtx.environmentConfig.permissionControllerAddress,
6098
6278
  abi: PermissionController_default,
6099
6279
  functionName: "canCall",
@@ -6120,8 +6300,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6120
6300
  logger.debug("Performing preflight checks...");
6121
6301
  const preflightCtx = await doPreflightChecks(
6122
6302
  {
6123
- privateKey: options.privateKey,
6124
- rpcUrl: options.rpcUrl,
6303
+ walletClient: options.walletClient,
6304
+ publicClient: options.publicClient,
6125
6305
  environment: options.environment
6126
6306
  },
6127
6307
  logger
@@ -6153,13 +6333,14 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6153
6333
  const needsPermissionChange = currentlyPublic !== publicLogs;
6154
6334
  logger.debug("Preparing upgrade batch...");
6155
6335
  const batch = await prepareUpgradeBatch({
6156
- privateKey: preflightCtx.privateKey,
6157
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6336
+ walletClient: preflightCtx.walletClient,
6337
+ publicClient: preflightCtx.publicClient,
6158
6338
  environmentConfig: preflightCtx.environmentConfig,
6159
- appId: appID,
6339
+ appID,
6160
6340
  release,
6161
6341
  publicLogs,
6162
- needsPermissionChange
6342
+ needsPermissionChange,
6343
+ imageRef: options.imageRef
6163
6344
  });
6164
6345
  logger.debug("Estimating gas...");
6165
6346
  const gasEstimate = await estimateBatchGas({
@@ -6183,8 +6364,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6183
6364
  );
6184
6365
  }
6185
6366
  function validateUpgradeOptions(options) {
6186
- if (!options.privateKey) {
6187
- throw new Error("privateKey is required for upgrade");
6367
+ if (!options.walletClient?.account) {
6368
+ throw new Error("walletClient with account is required for upgrade");
6188
6369
  }
6189
6370
  if (!options.appId) {
6190
6371
  throw new Error("appId is required for upgrade");
@@ -6221,8 +6402,8 @@ async function upgrade(options, logger = defaultLogger) {
6221
6402
  logger.debug("Performing preflight checks...");
6222
6403
  const preflightCtx = await doPreflightChecks(
6223
6404
  {
6224
- privateKey: options.privateKey,
6225
- rpcUrl: options.rpcUrl,
6405
+ walletClient: options.walletClient,
6406
+ publicClient: options.publicClient,
6226
6407
  environment: options.environment
6227
6408
  },
6228
6409
  logger
@@ -6256,10 +6437,10 @@ async function upgrade(options, logger = defaultLogger) {
6256
6437
  logger.info("Upgrading on-chain...");
6257
6438
  const txHash = await upgradeApp(
6258
6439
  {
6259
- privateKey: preflightCtx.privateKey,
6260
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6440
+ walletClient: preflightCtx.walletClient,
6441
+ publicClient: preflightCtx.publicClient,
6261
6442
  environmentConfig: preflightCtx.environmentConfig,
6262
- appId: appID,
6443
+ appID,
6263
6444
  release,
6264
6445
  publicLogs,
6265
6446
  needsPermissionChange,
@@ -6271,8 +6452,8 @@ async function upgrade(options, logger = defaultLogger) {
6271
6452
  logger.info("Waiting for upgrade to complete...");
6272
6453
  await watchUntilUpgradeComplete(
6273
6454
  {
6274
- privateKey: preflightCtx.privateKey,
6275
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6455
+ walletClient: preflightCtx.walletClient,
6456
+ publicClient: preflightCtx.publicClient,
6276
6457
  environmentConfig: preflightCtx.environmentConfig,
6277
6458
  appId: appID
6278
6459
  },
@@ -6299,8 +6480,8 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6299
6480
  logger.debug("Performing preflight checks...");
6300
6481
  const preflightCtx = await doPreflightChecks(
6301
6482
  {
6302
- privateKey: options.privateKey,
6303
- rpcUrl: options.rpcUrl,
6483
+ walletClient: options.walletClient,
6484
+ publicClient: options.publicClient,
6304
6485
  environment: options.environment
6305
6486
  },
6306
6487
  logger
@@ -6333,13 +6514,14 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6333
6514
  const needsPermissionChange = currentlyPublic !== publicLogs;
6334
6515
  logger.debug("Preparing upgrade batch...");
6335
6516
  const batch = await prepareUpgradeBatch({
6336
- privateKey: preflightCtx.privateKey,
6337
- rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
6517
+ walletClient: preflightCtx.walletClient,
6518
+ publicClient: preflightCtx.publicClient,
6338
6519
  environmentConfig: preflightCtx.environmentConfig,
6339
- appId: appID,
6520
+ appID,
6340
6521
  release,
6341
6522
  publicLogs,
6342
- needsPermissionChange
6523
+ needsPermissionChange,
6524
+ imageRef: finalImageRef
6343
6525
  });
6344
6526
  logger.debug("Estimating gas...");
6345
6527
  const gasEstimate = await estimateBatchGas({
@@ -6380,25 +6562,23 @@ async function executeUpgrade(options) {
6380
6562
  }
6381
6563
  );
6382
6564
  }
6383
- async function watchUpgrade(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
6565
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
6384
6566
  return withSDKTelemetry(
6385
6567
  {
6386
6568
  functionName: "watchUpgrade",
6387
6569
  skipTelemetry,
6388
6570
  properties: {
6389
- environment
6571
+ environment: environmentConfig.name
6390
6572
  }
6391
6573
  },
6392
6574
  async () => {
6393
- const environmentConfig = getEnvironmentConfig(environment);
6394
6575
  logger.info("Waiting for upgrade to complete...");
6395
6576
  await watchUntilUpgradeComplete(
6396
6577
  {
6397
- privateKey,
6398
- rpcUrl,
6578
+ walletClient,
6579
+ publicClient,
6399
6580
  environmentConfig,
6400
- appId,
6401
- clientId
6581
+ appId
6402
6582
  },
6403
6583
  logger
6404
6584
  );
@@ -6962,32 +7142,25 @@ async function watchLogs(appID, userApiClient, initialLogs) {
6962
7142
  }
6963
7143
  console.log("\nStopped watching");
6964
7144
  }
6965
- async function logs(options, logger = defaultLogger, skipTelemetry = false) {
6966
- const skipTelemetryFlag = skipTelemetry || options.skipTelemetry || false;
7145
+ async function logs(options, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry = false) {
6967
7146
  return withSDKTelemetry(
6968
7147
  {
6969
7148
  functionName: "logs",
6970
- skipTelemetry: skipTelemetryFlag,
6971
- properties: { environment: options.environment || "sepolia" }
7149
+ skipTelemetry,
7150
+ properties: { environment: environmentConfig.name }
6972
7151
  },
6973
7152
  async () => {
6974
7153
  console.log();
6975
7154
  if (!options.appID) {
6976
7155
  throw new Error("appID is required for viewing logs");
6977
7156
  }
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
7157
  const appID = validateAppID(options.appID);
6985
7158
  const formattedApp = formatAppDisplay(environmentConfig.name, appID, "");
6986
7159
  const userApiClient = new UserApiClient(
6987
7160
  environmentConfig,
6988
- options.privateKey,
6989
- rpcUrl,
6990
- options.clientId
7161
+ walletClient,
7162
+ publicClient,
7163
+ options.clientId ? { clientId: options.clientId } : void 0
6991
7164
  );
6992
7165
  let logsText;
6993
7166
  let logsError = null;
@@ -7063,35 +7236,39 @@ async function logs(options, logger = defaultLogger, skipTelemetry = false) {
7063
7236
  }
7064
7237
 
7065
7238
  // src/client/modules/compute/app/index.ts
7066
- var CONTROLLER_ABI = (0, import_viem9.parseAbi)([
7239
+ var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
7067
7240
  "function startApp(address appId)",
7068
7241
  "function stopApp(address appId)",
7069
7242
  "function terminateApp(address appId)"
7070
7243
  ]);
7071
7244
  function encodeStartAppData(appId) {
7072
- return (0, import_viem9.encodeFunctionData)({
7245
+ return (0, import_viem6.encodeFunctionData)({
7073
7246
  abi: CONTROLLER_ABI,
7074
7247
  functionName: "startApp",
7075
7248
  args: [appId]
7076
7249
  });
7077
7250
  }
7078
7251
  function encodeStopAppData(appId) {
7079
- return (0, import_viem9.encodeFunctionData)({
7252
+ return (0, import_viem6.encodeFunctionData)({
7080
7253
  abi: CONTROLLER_ABI,
7081
7254
  functionName: "stopApp",
7082
7255
  args: [appId]
7083
7256
  });
7084
7257
  }
7085
7258
  function encodeTerminateAppData(appId) {
7086
- return (0, import_viem9.encodeFunctionData)({
7259
+ return (0, import_viem6.encodeFunctionData)({
7087
7260
  abi: CONTROLLER_ABI,
7088
7261
  functionName: "terminateApp",
7089
7262
  args: [appId]
7090
7263
  });
7091
7264
  }
7092
7265
  function createAppModule(ctx) {
7093
- const privateKey = addHexPrefix(ctx.privateKey);
7266
+ const { walletClient, publicClient } = ctx;
7094
7267
  const skipTelemetry = ctx.skipTelemetry || false;
7268
+ if (!walletClient.account) {
7269
+ throw new Error("WalletClient must have an account attached");
7270
+ }
7271
+ const account = walletClient.account;
7095
7272
  const environment = getEnvironmentConfig(ctx.environment);
7096
7273
  const logger = getLogger(ctx.verbose);
7097
7274
  return {
@@ -7102,8 +7279,8 @@ function createAppModule(ctx) {
7102
7279
  async deploy(opts) {
7103
7280
  const result = await deploy(
7104
7281
  {
7105
- privateKey,
7106
- rpcUrl: ctx.rpcUrl,
7282
+ walletClient,
7283
+ publicClient,
7107
7284
  environment: ctx.environment,
7108
7285
  appName: opts.name,
7109
7286
  instanceType: opts.instanceType,
@@ -7127,8 +7304,8 @@ function createAppModule(ctx) {
7127
7304
  const result = await upgrade(
7128
7305
  {
7129
7306
  appId,
7130
- privateKey,
7131
- rpcUrl: ctx.rpcUrl,
7307
+ walletClient,
7308
+ publicClient,
7132
7309
  environment: ctx.environment,
7133
7310
  instanceType: opts.instanceType,
7134
7311
  dockerfilePath: opts.dockerfile,
@@ -7149,8 +7326,8 @@ function createAppModule(ctx) {
7149
7326
  async prepareDeploy(opts) {
7150
7327
  return prepareDeploy(
7151
7328
  {
7152
- privateKey,
7153
- rpcUrl: ctx.rpcUrl,
7329
+ walletClient,
7330
+ publicClient,
7154
7331
  environment: ctx.environment,
7155
7332
  appName: opts.name,
7156
7333
  instanceType: opts.instanceType,
@@ -7167,9 +7344,9 @@ function createAppModule(ctx) {
7167
7344
  async prepareDeployFromVerifiableBuild(opts) {
7168
7345
  return prepareDeployFromVerifiableBuild(
7169
7346
  {
7170
- privateKey,
7171
- rpcUrl: ctx.rpcUrl,
7172
- environment: ctx.environment,
7347
+ walletClient,
7348
+ publicClient,
7349
+ environment: ctx.environment,
7173
7350
  appName: opts.name,
7174
7351
  instanceType: opts.instanceType,
7175
7352
  envFilePath: opts.envFile,
@@ -7183,17 +7360,6 @@ function createAppModule(ctx) {
7183
7360
  );
7184
7361
  },
7185
7362
  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
7363
  const result = await executeDeploy({
7198
7364
  prepared,
7199
7365
  context: {
@@ -7209,591 +7375,260 @@ function createAppModule(ctx) {
7209
7375
  appId: result.appId,
7210
7376
  txHash: result.txHash,
7211
7377
  appName: result.appName,
7212
- imageRef: result.imageRef
7213
- };
7214
- },
7215
- async watchDeployment(appId) {
7216
- return watchDeployment(
7217
- appId,
7218
- privateKey,
7219
- ctx.rpcUrl,
7220
- ctx.environment,
7221
- logger,
7222
- ctx.clientId,
7223
- skipTelemetry
7224
- );
7225
- },
7226
- // Granular upgrade control
7227
- async prepareUpgrade(appId, opts) {
7228
- return prepareUpgrade(
7229
- {
7230
- appId,
7231
- privateKey,
7232
- rpcUrl: ctx.rpcUrl,
7233
- environment: ctx.environment,
7234
- instanceType: opts.instanceType,
7235
- dockerfilePath: opts.dockerfile,
7236
- envFilePath: opts.envFile,
7237
- imageRef: opts.imageRef,
7238
- logVisibility: opts.logVisibility,
7239
- resourceUsageMonitoring: opts.resourceUsageMonitoring,
7240
- skipTelemetry
7241
- },
7242
- logger
7243
- );
7244
- },
7245
- async prepareUpgradeFromVerifiableBuild(appId, opts) {
7246
- return prepareUpgradeFromVerifiableBuild(
7247
- {
7248
- appId,
7249
- privateKey,
7250
- rpcUrl: ctx.rpcUrl,
7251
- environment: ctx.environment,
7252
- instanceType: opts.instanceType,
7253
- envFilePath: opts.envFile,
7254
- imageRef: opts.imageRef,
7255
- imageDigest: opts.imageDigest,
7256
- logVisibility: opts.logVisibility,
7257
- resourceUsageMonitoring: opts.resourceUsageMonitoring,
7258
- skipTelemetry
7259
- },
7260
- logger
7261
- );
7262
- },
7263
- 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
- const result = await executeUpgrade({
7276
- prepared,
7277
- context: {
7278
- walletClient,
7279
- publicClient,
7280
- environmentConfig: environment
7281
- },
7282
- gas,
7283
- logger,
7284
- skipTelemetry
7285
- });
7286
- return {
7287
- appId: result.appId,
7288
- txHash: result.txHash,
7289
- imageRef: result.imageRef
7290
- };
7291
- },
7292
- async watchUpgrade(appId) {
7293
- return watchUpgrade(
7294
- appId,
7295
- privateKey,
7296
- ctx.rpcUrl,
7297
- ctx.environment,
7298
- logger,
7299
- ctx.clientId,
7300
- skipTelemetry
7301
- );
7302
- },
7303
- // Profile management
7304
- async setProfile(appId, profile) {
7305
- return withSDKTelemetry(
7306
- {
7307
- functionName: "setProfile",
7308
- skipTelemetry,
7309
- properties: { environment: ctx.environment }
7310
- },
7311
- async () => {
7312
- const userApiClient = new UserApiClient(
7313
- environment,
7314
- privateKey,
7315
- ctx.rpcUrl,
7316
- ctx.clientId
7317
- );
7318
- return userApiClient.uploadAppProfile(
7319
- appId,
7320
- profile.name,
7321
- profile.website,
7322
- profile.description,
7323
- profile.xURL,
7324
- profile.imagePath
7325
- );
7326
- }
7327
- );
7328
- },
7329
- async logs(opts) {
7330
- return logs(
7331
- {
7332
- privateKey,
7333
- appID: opts.appID,
7334
- watch: opts.watch,
7335
- environment: ctx.environment,
7336
- clientId: ctx.clientId
7337
- },
7338
- logger,
7339
- skipTelemetry
7340
- // Skip if called from CLI
7341
- );
7342
- },
7343
- async start(appId, opts) {
7344
- return withSDKTelemetry(
7345
- {
7346
- functionName: "start",
7347
- skipTelemetry,
7348
- // Skip if called from CLI
7349
- properties: { environment: ctx.environment }
7350
- },
7351
- async () => {
7352
- const pendingMessage = `Starting app ${appId}...`;
7353
- const data = (0, import_viem9.encodeFunctionData)({
7354
- abi: CONTROLLER_ABI,
7355
- functionName: "startApp",
7356
- args: [appId]
7357
- });
7358
- const tx = await sendAndWaitForTransaction(
7359
- {
7360
- privateKey,
7361
- rpcUrl: ctx.rpcUrl,
7362
- environmentConfig: environment,
7363
- to: environment.appControllerAddress,
7364
- data,
7365
- pendingMessage,
7366
- txDescription: "StartApp",
7367
- gas: opts?.gas
7368
- },
7369
- logger
7370
- );
7371
- return { tx };
7372
- }
7373
- );
7374
- },
7375
- async stop(appId, opts) {
7376
- return withSDKTelemetry(
7377
- {
7378
- functionName: "stop",
7379
- skipTelemetry,
7380
- // Skip if called from CLI
7381
- properties: { environment: ctx.environment }
7382
- },
7383
- async () => {
7384
- const pendingMessage = `Stopping app ${appId}...`;
7385
- const data = (0, import_viem9.encodeFunctionData)({
7386
- abi: CONTROLLER_ABI,
7387
- functionName: "stopApp",
7388
- args: [appId]
7389
- });
7390
- const tx = await sendAndWaitForTransaction(
7391
- {
7392
- privateKey,
7393
- rpcUrl: ctx.rpcUrl,
7394
- environmentConfig: environment,
7395
- to: environment.appControllerAddress,
7396
- data,
7397
- pendingMessage,
7398
- txDescription: "StopApp",
7399
- gas: opts?.gas
7400
- },
7401
- logger
7402
- );
7403
- return { tx };
7404
- }
7405
- );
7406
- },
7407
- async terminate(appId, opts) {
7408
- return withSDKTelemetry(
7409
- {
7410
- functionName: "terminate",
7411
- skipTelemetry,
7412
- // Skip if called from CLI
7413
- properties: { environment: ctx.environment }
7414
- },
7415
- async () => {
7416
- const pendingMessage = `Terminating app ${appId}...`;
7417
- const data = (0, import_viem9.encodeFunctionData)({
7418
- abi: CONTROLLER_ABI,
7419
- functionName: "terminateApp",
7420
- args: [appId]
7421
- });
7422
- const tx = await sendAndWaitForTransaction(
7423
- {
7424
- privateKey,
7425
- rpcUrl: ctx.rpcUrl,
7426
- environmentConfig: environment,
7427
- to: environment.appControllerAddress,
7428
- data,
7429
- pendingMessage,
7430
- txDescription: "TerminateApp",
7431
- gas: opts?.gas
7432
- },
7433
- logger
7434
- );
7435
- return { tx };
7436
- }
7437
- );
7438
- },
7439
- async isDelegated() {
7440
- return isDelegated({
7441
- privateKey,
7442
- rpcUrl: ctx.rpcUrl,
7443
- environmentConfig: environment
7444
- });
7445
- },
7446
- async undelegate() {
7447
- return withSDKTelemetry(
7448
- {
7449
- functionName: "undelegate",
7450
- skipTelemetry,
7451
- // Skip if called from CLI
7452
- properties: { environment: ctx.environment }
7453
- },
7454
- async () => {
7455
- const tx = await undelegate(
7456
- {
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."
7378
+ imageRef: result.imageRef
7379
+ };
7380
+ },
7381
+ async watchDeployment(appId) {
7382
+ return watchDeployment(
7383
+ appId,
7384
+ walletClient,
7385
+ publicClient,
7386
+ environment,
7387
+ logger,
7388
+ skipTelemetry
7735
7389
  );
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."
7390
+ },
7391
+ // Granular upgrade control
7392
+ async prepareUpgrade(appId, opts) {
7393
+ return prepareUpgrade(
7394
+ {
7395
+ appId,
7396
+ walletClient,
7397
+ publicClient,
7398
+ environment: ctx.environment,
7399
+ instanceType: opts.instanceType,
7400
+ dockerfilePath: opts.dockerfile,
7401
+ envFilePath: opts.envFile,
7402
+ imageRef: opts.imageRef,
7403
+ logVisibility: opts.logVisibility,
7404
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
7405
+ skipTelemetry
7406
+ },
7407
+ logger
7408
+ );
7409
+ },
7410
+ async prepareUpgradeFromVerifiableBuild(appId, opts) {
7411
+ return prepareUpgradeFromVerifiableBuild(
7412
+ {
7413
+ appId,
7414
+ walletClient,
7415
+ publicClient,
7416
+ environment: ctx.environment,
7417
+ instanceType: opts.instanceType,
7418
+ envFilePath: opts.envFile,
7419
+ imageRef: opts.imageRef,
7420
+ imageDigest: opts.imageDigest,
7421
+ logVisibility: opts.logVisibility,
7422
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
7423
+ skipTelemetry
7424
+ },
7425
+ logger
7426
+ );
7427
+ },
7428
+ async executeUpgrade(prepared, gas) {
7429
+ const result = await executeUpgrade({
7430
+ prepared,
7431
+ context: {
7432
+ walletClient,
7433
+ publicClient,
7434
+ environmentConfig: environment
7435
+ },
7436
+ gas,
7437
+ logger,
7438
+ skipTelemetry
7439
+ });
7440
+ return {
7441
+ appId: result.appId,
7442
+ txHash: result.txHash,
7443
+ imageRef: result.imageRef
7444
+ };
7445
+ },
7446
+ async watchUpgrade(appId) {
7447
+ return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
7448
+ },
7449
+ // Profile management
7450
+ async setProfile(appId, profile) {
7451
+ return withSDKTelemetry(
7452
+ {
7453
+ functionName: "setProfile",
7454
+ skipTelemetry,
7455
+ properties: { environment: ctx.environment }
7456
+ },
7457
+ async () => {
7458
+ const userApiClient = new UserApiClient(
7459
+ environment,
7460
+ walletClient,
7461
+ publicClient,
7462
+ ctx.clientId ? { clientId: ctx.clientId } : void 0
7463
+ );
7464
+ return userApiClient.uploadAppProfile(appId, profile.name, {
7465
+ website: profile.website,
7466
+ description: profile.description,
7467
+ xURL: profile.xURL,
7468
+ image: profile.image,
7469
+ imageName: profile.imageName
7470
+ });
7471
+ }
7472
+ );
7473
+ },
7474
+ async logs(opts) {
7475
+ return logs(
7476
+ {
7477
+ appID: opts.appID,
7478
+ watch: opts.watch,
7479
+ clientId: ctx.clientId
7480
+ },
7481
+ walletClient,
7482
+ publicClient,
7483
+ environment,
7484
+ logger,
7485
+ skipTelemetry
7486
+ );
7487
+ },
7488
+ async start(appId, opts) {
7489
+ return withSDKTelemetry(
7490
+ {
7491
+ functionName: "start",
7492
+ skipTelemetry,
7493
+ // Skip if called from CLI
7494
+ properties: { environment: ctx.environment }
7495
+ },
7496
+ async () => {
7497
+ const pendingMessage = `Starting app ${appId}...`;
7498
+ const data = (0, import_viem6.encodeFunctionData)({
7499
+ abi: CONTROLLER_ABI,
7500
+ functionName: "startApp",
7501
+ args: [appId]
7502
+ });
7503
+ const tx = await sendAndWaitForTransaction(
7504
+ {
7505
+ walletClient,
7506
+ publicClient,
7507
+ environmentConfig: environment,
7508
+ to: environment.appControllerAddress,
7509
+ data,
7510
+ pendingMessage,
7511
+ txDescription: "StartApp",
7512
+ gas: opts?.gas
7513
+ },
7514
+ logger
7515
+ );
7516
+ return { tx };
7517
+ }
7518
+ );
7519
+ },
7520
+ async stop(appId, opts) {
7521
+ return withSDKTelemetry(
7522
+ {
7523
+ functionName: "stop",
7524
+ skipTelemetry,
7525
+ // Skip if called from CLI
7526
+ properties: { environment: ctx.environment }
7527
+ },
7528
+ async () => {
7529
+ const pendingMessage = `Stopping app ${appId}...`;
7530
+ const data = (0, import_viem6.encodeFunctionData)({
7531
+ abi: CONTROLLER_ABI,
7532
+ functionName: "stopApp",
7533
+ args: [appId]
7534
+ });
7535
+ const tx = await sendAndWaitForTransaction(
7536
+ {
7537
+ walletClient,
7538
+ publicClient,
7539
+ environmentConfig: environment,
7540
+ to: environment.appControllerAddress,
7541
+ data,
7542
+ pendingMessage,
7543
+ txDescription: "StopApp",
7544
+ gas: opts?.gas
7545
+ },
7546
+ logger
7547
+ );
7548
+ return { tx };
7549
+ }
7550
+ );
7551
+ },
7552
+ async terminate(appId, opts) {
7553
+ return withSDKTelemetry(
7554
+ {
7555
+ functionName: "terminate",
7556
+ skipTelemetry,
7557
+ // Skip if called from CLI
7558
+ properties: { environment: ctx.environment }
7559
+ },
7560
+ async () => {
7561
+ const pendingMessage = `Terminating app ${appId}...`;
7562
+ const data = (0, import_viem6.encodeFunctionData)({
7563
+ abi: CONTROLLER_ABI,
7564
+ functionName: "terminateApp",
7565
+ args: [appId]
7566
+ });
7567
+ const tx = await sendAndWaitForTransaction(
7568
+ {
7569
+ walletClient,
7570
+ publicClient,
7571
+ environmentConfig: environment,
7572
+ to: environment.appControllerAddress,
7573
+ data,
7574
+ pendingMessage,
7575
+ txDescription: "TerminateApp",
7576
+ gas: opts?.gas
7577
+ },
7578
+ logger
7579
+ );
7580
+ return { tx };
7581
+ }
7582
+ );
7583
+ },
7584
+ async isDelegated() {
7585
+ return isDelegated({
7586
+ publicClient,
7587
+ environmentConfig: environment,
7588
+ address: account.address
7589
+ });
7590
+ },
7591
+ async undelegate() {
7592
+ return withSDKTelemetry(
7593
+ {
7594
+ functionName: "undelegate",
7595
+ skipTelemetry,
7596
+ // Skip if called from CLI
7597
+ properties: { environment: ctx.environment }
7598
+ },
7599
+ async () => {
7600
+ const tx = await undelegate(
7601
+ {
7602
+ walletClient,
7603
+ publicClient,
7604
+ environmentConfig: environment
7605
+ },
7606
+ logger
7607
+ );
7608
+ return { tx };
7609
+ }
7747
7610
  );
7748
7611
  }
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;
7612
+ };
7776
7613
  }
7777
7614
 
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);
7615
+ // src/client/modules/compute/index.ts
7616
+ function createComputeModule(config) {
7783
7617
  return {
7784
- privateKey,
7785
- address
7618
+ app: createAppModule(config)
7786
7619
  };
7787
7620
  }
7788
7621
 
7789
7622
  // src/client/modules/billing/index.ts
7790
7623
  function createBillingModule(config) {
7791
- const { verbose = false, skipTelemetry = false } = config;
7792
- const privateKey = addHexPrefix(config.privateKey);
7793
- const address = getAddressFromPrivateKey(privateKey);
7624
+ const { verbose = false, skipTelemetry = false, walletClient } = config;
7625
+ if (!walletClient.account) {
7626
+ throw new Error("WalletClient must have an account attached");
7627
+ }
7628
+ const address = walletClient.account.address;
7794
7629
  const logger = getLogger(verbose);
7795
7630
  const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
7796
- const billingApi = new BillingApiClient(billingEnvConfig, privateKey);
7631
+ const billingApi = new BillingApiClient(billingEnvConfig, walletClient);
7797
7632
  return {
7798
7633
  address,
7799
7634
  async subscribe(opts) {
@@ -7883,14 +7718,52 @@ function createBillingModule(config) {
7883
7718
 
7884
7719
  // src/client/common/utils/buildapi.ts
7885
7720
  var import_axios3 = __toESM(require("axios"), 1);
7886
- var import_accounts9 = require("viem/accounts");
7721
+ var MAX_RETRIES = 5;
7722
+ var INITIAL_BACKOFF_MS = 1e3;
7723
+ var MAX_BACKOFF_MS = 3e4;
7724
+ async function sleep2(ms) {
7725
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7726
+ }
7727
+ function getRetryDelay(res, attempt) {
7728
+ const retryAfter = res.headers["retry-after"];
7729
+ if (retryAfter) {
7730
+ const seconds = parseInt(retryAfter, 10);
7731
+ if (!isNaN(seconds)) {
7732
+ return Math.min(seconds * 1e3, MAX_BACKOFF_MS);
7733
+ }
7734
+ }
7735
+ return Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt), MAX_BACKOFF_MS);
7736
+ }
7737
+ async function requestWithRetry(config) {
7738
+ let lastResponse;
7739
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
7740
+ const res = await (0, import_axios3.default)({ ...config, validateStatus: () => true });
7741
+ lastResponse = res;
7742
+ if (res.status !== 429) {
7743
+ return res;
7744
+ }
7745
+ if (attempt < MAX_RETRIES) {
7746
+ const delay = getRetryDelay(res, attempt);
7747
+ await sleep2(delay);
7748
+ }
7749
+ }
7750
+ return lastResponse;
7751
+ }
7887
7752
  var BuildApiClient = class {
7888
7753
  constructor(options) {
7889
7754
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
7890
7755
  this.clientId = options.clientId;
7891
- if (options.privateKey) {
7892
- this.account = (0, import_accounts9.privateKeyToAccount)(options.privateKey);
7756
+ this.walletClient = options.walletClient;
7757
+ }
7758
+ /**
7759
+ * Get the address of the connected wallet
7760
+ */
7761
+ get address() {
7762
+ const account = this.walletClient?.account;
7763
+ if (!account) {
7764
+ throw new Error("WalletClient must have an account attached");
7893
7765
  }
7766
+ return account.address;
7894
7767
  }
7895
7768
  async submitBuild(payload) {
7896
7769
  return this.authenticatedJsonRequest("/builds", "POST", payload);
@@ -7908,74 +7781,74 @@ var BuildApiClient = class {
7908
7781
  return this.authenticatedTextRequest(`/builds/${encodeURIComponent(buildId)}/logs`);
7909
7782
  }
7910
7783
  async listBuilds(params) {
7911
- const res = await (0, import_axios3.default)({
7784
+ const res = await requestWithRetry({
7912
7785
  url: `${this.baseUrl}/builds`,
7913
7786
  method: "GET",
7914
7787
  params,
7915
7788
  headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
7916
- timeout: 6e4,
7917
- validateStatus: () => true
7789
+ timeout: 6e4
7918
7790
  });
7919
7791
  if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
7920
7792
  return res.data;
7921
7793
  }
7922
7794
  async publicJsonRequest(path8) {
7923
- const res = await (0, import_axios3.default)({
7795
+ const res = await requestWithRetry({
7924
7796
  url: `${this.baseUrl}${path8}`,
7925
7797
  method: "GET",
7926
7798
  headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
7927
- timeout: 6e4,
7928
- validateStatus: () => true
7799
+ timeout: 6e4
7929
7800
  });
7930
7801
  if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
7931
7802
  return res.data;
7932
7803
  }
7933
7804
  async authenticatedJsonRequest(path8, method, body) {
7934
- if (!this.account) throw new Error("Private key required for authenticated requests");
7805
+ if (!this.walletClient?.account) {
7806
+ throw new Error("WalletClient with account required for authenticated requests");
7807
+ }
7935
7808
  const headers = {
7936
7809
  "Content-Type": "application/json"
7937
7810
  };
7938
7811
  if (this.clientId) headers["x-client-id"] = this.clientId;
7939
7812
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
7940
7813
  const { signature } = await calculateBillingAuthSignature({
7941
- account: this.account,
7814
+ walletClient: this.walletClient,
7942
7815
  product: "compute",
7943
7816
  expiry
7944
7817
  });
7945
7818
  headers.Authorization = `Bearer ${signature}`;
7946
7819
  headers["X-eigenx-expiry"] = expiry.toString();
7947
- headers["X-Account"] = this.account.address;
7948
- const res = await (0, import_axios3.default)({
7820
+ headers["X-Account"] = this.address;
7821
+ const res = await requestWithRetry({
7949
7822
  url: `${this.baseUrl}${path8}`,
7950
7823
  method,
7951
7824
  headers,
7952
7825
  data: body,
7953
- timeout: 6e4,
7954
- validateStatus: () => true
7826
+ timeout: 6e4
7955
7827
  });
7956
7828
  if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
7957
7829
  return res.data;
7958
7830
  }
7959
7831
  async authenticatedTextRequest(path8) {
7960
- if (!this.account) throw new Error("Private key required for authenticated requests");
7832
+ if (!this.walletClient?.account) {
7833
+ throw new Error("WalletClient with account required for authenticated requests");
7834
+ }
7961
7835
  const headers = {};
7962
7836
  if (this.clientId) headers["x-client-id"] = this.clientId;
7963
7837
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
7964
7838
  const { signature } = await calculateBillingAuthSignature({
7965
- account: this.account,
7839
+ walletClient: this.walletClient,
7966
7840
  product: "compute",
7967
7841
  expiry
7968
7842
  });
7969
7843
  headers.Authorization = `Bearer ${signature}`;
7970
7844
  headers["X-eigenx-expiry"] = expiry.toString();
7971
- headers["X-Account"] = this.account.address;
7972
- const res = await (0, import_axios3.default)({
7845
+ headers["X-Account"] = this.address;
7846
+ const res = await requestWithRetry({
7973
7847
  url: `${this.baseUrl}${path8}`,
7974
7848
  method: "GET",
7975
7849
  headers,
7976
7850
  timeout: 6e4,
7977
- responseType: "text",
7978
- validateStatus: () => true
7851
+ responseType: "text"
7979
7852
  });
7980
7853
  if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
7981
7854
  return typeof res.data === "string" ? res.data : JSON.stringify(res.data);
@@ -8050,13 +7923,13 @@ var BadRequestError = class extends BuildError {
8050
7923
  var DEFAULT_POLL_INTERVAL = 2e3;
8051
7924
  var DEFAULT_TIMEOUT = 30 * 60 * 1e3;
8052
7925
  function createBuildModule(config) {
8053
- const { verbose = false, skipTelemetry = false } = config;
7926
+ const { verbose = false, skipTelemetry = false, walletClient } = config;
8054
7927
  const logger = getLogger(verbose);
8055
7928
  const environment = config.environment || "sepolia";
8056
7929
  const environmentConfig = getEnvironmentConfig(environment);
8057
7930
  const api = new BuildApiClient({
8058
7931
  baseUrl: environmentConfig.userApiServerURL,
8059
- privateKey: config.privateKey ? addHexPrefix(config.privateKey) : void 0,
7932
+ walletClient,
8060
7933
  clientId: config.clientId
8061
7934
  });
8062
7935
  return {
@@ -8068,7 +7941,7 @@ function createBuildModule(config) {
8068
7941
  properties: { environment, repoUrl: request.repoUrl }
8069
7942
  },
8070
7943
  async () => {
8071
- if (!config.privateKey) throw new AuthRequiredError("Private key required for submit()");
7944
+ if (!walletClient) throw new AuthRequiredError("walletClient required for submit()");
8072
7945
  const data = await api.submitBuild({
8073
7946
  repo_url: request.repoUrl,
8074
7947
  git_ref: request.gitRef,
@@ -8127,7 +8000,7 @@ function createBuildModule(config) {
8127
8000
  return withSDKTelemetry(
8128
8001
  { functionName: "build.getLogs", skipTelemetry, properties: { environment, buildId } },
8129
8002
  async () => {
8130
- if (!config.privateKey) throw new AuthRequiredError("Private key required for getLogs()");
8003
+ if (!walletClient) throw new AuthRequiredError("walletClient required for getLogs()");
8131
8004
  return api.getLogs(buildId);
8132
8005
  }
8133
8006
  );
@@ -8164,7 +8037,7 @@ function createBuildModule(config) {
8164
8037
  if (build.status === BUILD_STATUS.FAILED) {
8165
8038
  throw new BuildFailedError(build.errorMessage ?? "Build failed", buildId);
8166
8039
  }
8167
- await sleep2(pollIntervalMs);
8040
+ await sleep3(pollIntervalMs);
8168
8041
  }
8169
8042
  },
8170
8043
  async *streamLogs(buildId, pollIntervalMs = DEFAULT_POLL_INTERVAL) {
@@ -8186,12 +8059,12 @@ function createBuildModule(config) {
8186
8059
  lastLength = logs2.length;
8187
8060
  }
8188
8061
  if (build.status !== BUILD_STATUS.BUILDING) break;
8189
- await sleep2(pollIntervalMs);
8062
+ await sleep3(pollIntervalMs);
8190
8063
  }
8191
8064
  }
8192
8065
  };
8193
8066
  }
8194
- function sleep2(ms) {
8067
+ function sleep3(ms) {
8195
8068
  return new Promise((resolve2) => setTimeout(resolve2, ms));
8196
8069
  }
8197
8070
  function transformBuild(raw) {
@@ -8235,14 +8108,321 @@ function transformVerifyResult(raw) {
8235
8108
  };
8236
8109
  }
8237
8110
 
8111
+ // src/client/common/auth/keyring.ts
8112
+ var import_keyring = require("@napi-rs/keyring");
8113
+ var import_accounts2 = require("viem/accounts");
8114
+ var SERVICE_NAME = "ecloud";
8115
+ var ACCOUNT_NAME = "key";
8116
+ var EIGENX_SERVICE_NAME = "eigenx-cli";
8117
+ var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
8118
+ var EIGENX_ACCOUNT_PREFIX = "eigenx-";
8119
+ var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
8120
+ var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
8121
+ async function storePrivateKey(privateKey) {
8122
+ const normalizedKey = normalizePrivateKey(privateKey);
8123
+ const isValid = validatePrivateKey(normalizedKey);
8124
+ if (!isValid) {
8125
+ throw new Error("Invalid private key format");
8126
+ }
8127
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
8128
+ try {
8129
+ await entry.setPassword(normalizedKey);
8130
+ } catch (err) {
8131
+ throw new Error(
8132
+ `Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
8133
+ );
8134
+ }
8135
+ }
8136
+ async function getPrivateKey() {
8137
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
8138
+ try {
8139
+ const key = await entry.getPassword();
8140
+ if (key && validatePrivateKey(key)) {
8141
+ return key;
8142
+ }
8143
+ } catch {
8144
+ }
8145
+ return null;
8146
+ }
8147
+ async function deletePrivateKey() {
8148
+ const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
8149
+ try {
8150
+ await entry.deletePassword();
8151
+ return true;
8152
+ } catch {
8153
+ console.warn("No key found in keyring");
8154
+ return false;
8155
+ }
8156
+ }
8157
+ async function listStoredKeys() {
8158
+ const keys = [];
8159
+ const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
8160
+ for (const cred of creds) {
8161
+ if (cred.account === ACCOUNT_NAME) {
8162
+ try {
8163
+ const address = getAddressFromPrivateKey(cred.password);
8164
+ keys.push({ address });
8165
+ } catch (err) {
8166
+ console.warn(`Warning: Invalid key found, skipping: ${err}`);
8167
+ }
8168
+ }
8169
+ }
8170
+ return keys;
8171
+ }
8172
+ async function keyExists() {
8173
+ const key = await getPrivateKey();
8174
+ return key !== null;
8175
+ }
8176
+ async function getLegacyKeys() {
8177
+ const keys = [];
8178
+ try {
8179
+ const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
8180
+ for (const cred of eigenxCreds) {
8181
+ const accountName = cred.account;
8182
+ if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
8183
+ continue;
8184
+ }
8185
+ const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
8186
+ try {
8187
+ const decodedKey = decodeGoKeyringValue(cred.password);
8188
+ const address = getAddressFromPrivateKey(decodedKey);
8189
+ keys.push({ environment, address, source: "eigenx" });
8190
+ } catch (err) {
8191
+ console.warn(
8192
+ `Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
8193
+ );
8194
+ }
8195
+ }
8196
+ } catch {
8197
+ }
8198
+ try {
8199
+ const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
8200
+ for (const cred of eigenxDevCreds) {
8201
+ const accountName = cred.account;
8202
+ if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
8203
+ continue;
8204
+ }
8205
+ const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
8206
+ try {
8207
+ const decodedKey = decodeGoKeyringValue(cred.password);
8208
+ const address = getAddressFromPrivateKey(decodedKey);
8209
+ keys.push({ environment, address, source: "eigenx-dev" });
8210
+ } catch (err) {
8211
+ console.warn(
8212
+ `Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
8213
+ );
8214
+ }
8215
+ }
8216
+ } catch {
8217
+ }
8218
+ return keys;
8219
+ }
8220
+ async function getLegacyPrivateKey(environment, source) {
8221
+ const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
8222
+ const accountName = EIGENX_ACCOUNT_PREFIX + environment;
8223
+ const entry = new import_keyring.AsyncEntry(serviceName, accountName);
8224
+ try {
8225
+ const rawKey = await entry.getPassword();
8226
+ if (rawKey) {
8227
+ const decodedKey = decodeGoKeyringValue(rawKey);
8228
+ if (validatePrivateKey(decodedKey)) {
8229
+ return decodedKey;
8230
+ }
8231
+ }
8232
+ } catch {
8233
+ }
8234
+ return null;
8235
+ }
8236
+ async function deleteLegacyPrivateKey(environment, source) {
8237
+ const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
8238
+ const accountName = EIGENX_ACCOUNT_PREFIX + environment;
8239
+ const entry = new import_keyring.AsyncEntry(serviceName, accountName);
8240
+ try {
8241
+ await entry.deletePassword();
8242
+ return true;
8243
+ } catch {
8244
+ console.warn(`No key found for ${environment} in ${source}`);
8245
+ return false;
8246
+ }
8247
+ }
8248
+ function validatePrivateKey(privateKey) {
8249
+ try {
8250
+ getAddressFromPrivateKey(privateKey);
8251
+ return true;
8252
+ } catch {
8253
+ return false;
8254
+ }
8255
+ }
8256
+ function getAddressFromPrivateKey(privateKey) {
8257
+ const normalized = normalizePrivateKey(privateKey);
8258
+ return (0, import_accounts2.privateKeyToAddress)(normalized);
8259
+ }
8260
+ function decodeGoKeyringValue(rawValue) {
8261
+ if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
8262
+ const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
8263
+ try {
8264
+ const decoded = Buffer.from(encoded, "base64").toString("utf8");
8265
+ return decoded;
8266
+ } catch (err) {
8267
+ console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
8268
+ return rawValue;
8269
+ }
8270
+ }
8271
+ if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
8272
+ const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
8273
+ try {
8274
+ const decoded = Buffer.from(encoded, "hex").toString("utf8");
8275
+ return decoded;
8276
+ } catch (err) {
8277
+ console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
8278
+ return rawValue;
8279
+ }
8280
+ }
8281
+ return rawValue;
8282
+ }
8283
+ function normalizePrivateKey(privateKey) {
8284
+ if (!privateKey.startsWith("0x")) {
8285
+ return `0x${privateKey}`;
8286
+ }
8287
+ return privateKey;
8288
+ }
8289
+
8290
+ // src/client/common/auth/resolver.ts
8291
+ async function getPrivateKeyWithSource(options) {
8292
+ if (options.privateKey) {
8293
+ if (!validatePrivateKey(options.privateKey)) {
8294
+ throw new Error(
8295
+ "Invalid private key format provided via command flag. Please check and try again."
8296
+ );
8297
+ }
8298
+ return {
8299
+ key: options.privateKey,
8300
+ source: "command flag"
8301
+ };
8302
+ }
8303
+ const envKey = process.env.ECLOUD_PRIVATE_KEY;
8304
+ if (envKey) {
8305
+ if (!validatePrivateKey(envKey)) {
8306
+ throw new Error(
8307
+ "Invalid private key format provided via environment variable. Please check and try again."
8308
+ );
8309
+ }
8310
+ return {
8311
+ key: envKey,
8312
+ source: "environment variable (ECLOUD_PRIVATE_KEY)"
8313
+ };
8314
+ }
8315
+ const keyringKey = await getPrivateKey();
8316
+ if (keyringKey) {
8317
+ return {
8318
+ key: keyringKey,
8319
+ source: "stored credentials"
8320
+ };
8321
+ }
8322
+ return null;
8323
+ }
8324
+ async function requirePrivateKey(options) {
8325
+ const result = await getPrivateKeyWithSource({
8326
+ privateKey: options.privateKey
8327
+ });
8328
+ if (!result) {
8329
+ throw new Error(
8330
+ `Private key required. Please provide it via:
8331
+ \u2022 Keyring: ecloud auth login
8332
+ \u2022 Flag: --private-key YOUR_KEY
8333
+ \u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
8334
+ );
8335
+ }
8336
+ return result;
8337
+ }
8338
+
8339
+ // src/client/common/auth/generate.ts
8340
+ var import_accounts3 = require("viem/accounts");
8341
+ function generateNewPrivateKey() {
8342
+ const privateKey = (0, import_accounts3.generatePrivateKey)();
8343
+ const address = (0, import_accounts3.privateKeyToAddress)(privateKey);
8344
+ return {
8345
+ privateKey,
8346
+ address
8347
+ };
8348
+ }
8349
+
8350
+ // src/client/common/auth/siwe.ts
8351
+ var import_siwe = require("siwe");
8352
+ var generateNonce = import_siwe.generateNonce;
8353
+ function createSiweMessage(params) {
8354
+ const now = /* @__PURE__ */ new Date();
8355
+ const nonce = params.nonce || generateNonce();
8356
+ const issuedAt = params.issuedAt || now;
8357
+ const expirationTime = params.expirationTime || new Date(now.getTime() + 24 * 60 * 60 * 1e3);
8358
+ const siweMessage = new import_siwe.SiweMessage({
8359
+ domain: params.domain,
8360
+ address: params.address,
8361
+ statement: params.statement,
8362
+ uri: params.uri,
8363
+ version: "1",
8364
+ chainId: params.chainId,
8365
+ nonce,
8366
+ issuedAt: issuedAt.toISOString(),
8367
+ expirationTime: expirationTime.toISOString(),
8368
+ notBefore: params.notBefore?.toISOString(),
8369
+ requestId: params.requestId,
8370
+ resources: params.resources
8371
+ });
8372
+ return {
8373
+ message: siweMessage.prepareMessage(),
8374
+ params: {
8375
+ address: params.address,
8376
+ chainId: params.chainId,
8377
+ domain: params.domain,
8378
+ uri: params.uri,
8379
+ nonce,
8380
+ issuedAt,
8381
+ statement: params.statement,
8382
+ expirationTime,
8383
+ notBefore: params.notBefore,
8384
+ requestId: params.requestId,
8385
+ resources: params.resources
8386
+ }
8387
+ };
8388
+ }
8389
+ function parseSiweMessage(message) {
8390
+ try {
8391
+ const siweMessage = new import_siwe.SiweMessage(message);
8392
+ return {
8393
+ address: siweMessage.address,
8394
+ chainId: siweMessage.chainId,
8395
+ domain: siweMessage.domain,
8396
+ uri: siweMessage.uri,
8397
+ nonce: siweMessage.nonce,
8398
+ statement: siweMessage.statement,
8399
+ issuedAt: siweMessage.issuedAt ? new Date(siweMessage.issuedAt) : void 0,
8400
+ expirationTime: siweMessage.expirationTime ? new Date(siweMessage.expirationTime) : void 0,
8401
+ notBefore: siweMessage.notBefore ? new Date(siweMessage.notBefore) : void 0,
8402
+ requestId: siweMessage.requestId,
8403
+ resources: siweMessage.resources
8404
+ };
8405
+ } catch {
8406
+ return null;
8407
+ }
8408
+ }
8409
+ function isSiweMessageExpired(params) {
8410
+ if (!params.expirationTime) return false;
8411
+ return /* @__PURE__ */ new Date() > params.expirationTime;
8412
+ }
8413
+ function isSiweMessageNotYetValid(params) {
8414
+ if (!params.notBefore) return false;
8415
+ return /* @__PURE__ */ new Date() < params.notBefore;
8416
+ }
8417
+
8238
8418
  // src/client/common/utils/instance.ts
8239
8419
  async function getCurrentInstanceType(preflightCtx, appID, logger, clientId) {
8240
8420
  try {
8241
8421
  const userApiClient = new UserApiClient(
8242
8422
  preflightCtx.environmentConfig,
8243
- preflightCtx.privateKey,
8244
- preflightCtx.rpcUrl,
8245
- clientId
8423
+ preflightCtx.walletClient,
8424
+ preflightCtx.publicClient,
8425
+ clientId ? { clientId } : void 0
8246
8426
  );
8247
8427
  const infos = await userApiClient.getInfos([appID], 1);
8248
8428
  if (infos.length === 0) {
@@ -8274,16 +8454,21 @@ function createECloudClient(cfg) {
8274
8454
  `RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
8275
8455
  );
8276
8456
  }
8457
+ const { walletClient, publicClient } = createClients({
8458
+ privateKey: cfg.privateKey,
8459
+ rpcUrl,
8460
+ chainId: environmentConfig.chainID
8461
+ });
8277
8462
  return {
8278
8463
  compute: createComputeModule({
8279
- rpcUrl,
8280
8464
  verbose: cfg.verbose,
8281
- privateKey: cfg.privateKey,
8465
+ walletClient,
8466
+ publicClient,
8282
8467
  environment: cfg.environment
8283
8468
  }),
8284
8469
  billing: createBillingModule({
8285
8470
  verbose: cfg.verbose,
8286
- privateKey: cfg.privateKey
8471
+ walletClient
8287
8472
  })
8288
8473
  };
8289
8474
  }
@@ -8292,6 +8477,7 @@ function createECloudClient(cfg) {
8292
8477
  AuthRequiredError,
8293
8478
  BUILD_STATUS,
8294
8479
  BadRequestError,
8480
+ BillingApiClient,
8295
8481
  BuildError,
8296
8482
  BuildFailedError,
8297
8483
  ConflictError,
@@ -8300,6 +8486,7 @@ function createECloudClient(cfg) {
8300
8486
  NotFoundError,
8301
8487
  PRIMARY_LANGUAGES,
8302
8488
  PostHogClient,
8489
+ SessionError,
8303
8490
  TimeoutError,
8304
8491
  UserApiClient,
8305
8492
  addHexPrefix,
@@ -8316,7 +8503,9 @@ function createECloudClient(cfg) {
8316
8503
  createComputeModule,
8317
8504
  createECloudClient,
8318
8505
  createMetricsContext,
8506
+ createSiweMessage,
8319
8507
  createTelemetryClient,
8508
+ createViemClients,
8320
8509
  deleteLegacyPrivateKey,
8321
8510
  deletePrivateKey,
8322
8511
  emitMetrics,
@@ -8331,14 +8520,18 @@ function createECloudClient(cfg) {
8331
8520
  fetchTemplateCatalog,
8332
8521
  formatETH,
8333
8522
  generateNewPrivateKey,
8523
+ generateNonce,
8334
8524
  getAddressFromPrivateKey,
8335
8525
  getAllAppsByDeveloper,
8336
8526
  getAppLatestReleaseBlockNumbers,
8337
8527
  getAvailableEnvironments,
8338
8528
  getAvailableTemplates,
8529
+ getBillingEnvironmentConfig,
8339
8530
  getBlockTimestamps,
8340
8531
  getBuildType,
8341
8532
  getCategoryDescriptions,
8533
+ getChainFromID,
8534
+ getComputeApiSession,
8342
8535
  getCurrentInstanceType,
8343
8536
  getEnvironmentConfig,
8344
8537
  getLegacyKeys,
@@ -8351,10 +8544,17 @@ function createECloudClient(cfg) {
8351
8544
  isEnvironmentAvailable,
8352
8545
  isMainnet,
8353
8546
  isNoopClient,
8547
+ isSessionValid,
8548
+ isSiweMessageExpired,
8549
+ isSiweMessageNotYetValid,
8354
8550
  isSubscriptionActive,
8355
8551
  keyExists,
8356
8552
  listStoredKeys,
8553
+ loginToComputeApi,
8554
+ logoutFromComputeApi,
8357
8555
  logs,
8556
+ noopLogger,
8557
+ parseSiweMessage,
8358
8558
  prepareDeploy,
8359
8559
  prepareDeployFromVerifiableBuild,
8360
8560
  prepareUpgrade,