@layr-labs/ecloud-sdk 0.1.0-rc.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,6 +22,8 @@ No SLA - Mainnet Alpha does not have SLAs around support, and uptime of infrastr
22
22
 
23
23
 
24
24
  ## Quick Start
25
+ > [!NOTE]
26
+ > Migrating from `eigenx`? Head over to [Migration guide](./MIGRATION.md) first
25
27
  ### Installation
26
28
 
27
29
  ```bash
@@ -312,7 +314,7 @@ const client = createECloudClient({
312
314
  });
313
315
 
314
316
  // Deploy an application
315
- const result = await client.app.deploy({
317
+ const result = await client.compute.app.deploy({
316
318
  image: "myapp:latest",
317
319
  });
318
320
 
@@ -320,13 +322,13 @@ console.log(`Deployed app ID: ${result.appId}`);
320
322
  console.log(`Transaction hash: ${result.tx}`);
321
323
 
322
324
  // Start an application
323
- await client.app.start(result.appId);
325
+ await client.compute.app.start(result.appId);
324
326
 
325
327
  // Stop an application
326
- await client.app.stop(result.appId);
328
+ await client.compute.app.stop(result.appId);
327
329
 
328
330
  // Terminate an application
329
- await client.app.terminate(result.appId);
331
+ await client.compute.app.terminate(result.appId);
330
332
  ```
331
333
 
332
334
  ## Environments
package/VERSION CHANGED
@@ -1,2 +1,2 @@
1
- version=0.1.0-rc.1
2
- commit=bd1a94f02bdaebb6621925b9137be57d7240075c
1
+ version=0.1.0
2
+ commit=f751ea0fecc2e77d93bc962aeb11aa2497b74b73
@@ -0,0 +1,536 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/billing.ts
31
+ var billing_exports = {};
32
+ __export(billing_exports, {
33
+ createBillingModule: () => createBillingModule
34
+ });
35
+ module.exports = __toCommonJS(billing_exports);
36
+
37
+ // src/client/common/utils/billingapi.ts
38
+ var import_axios = __toESM(require("axios"), 1);
39
+ var import_accounts = require("viem/accounts");
40
+
41
+ // src/client/common/utils/auth.ts
42
+ var import_viem = require("viem");
43
+ var APP_CONTROLLER_ABI = (0, import_viem.parseAbi)([
44
+ "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
45
+ ]);
46
+ async function calculateBillingAuthSignature(options) {
47
+ const { account, product, expiry } = options;
48
+ const signature = await account.signTypedData({
49
+ domain: {
50
+ name: "EigenCloud Billing API",
51
+ version: "1"
52
+ },
53
+ types: {
54
+ BillingAuth: [
55
+ { name: "product", type: "string" },
56
+ { name: "expiry", type: "uint256" }
57
+ ]
58
+ },
59
+ primaryType: "BillingAuth",
60
+ message: {
61
+ product,
62
+ expiry
63
+ }
64
+ });
65
+ return { signature, expiry };
66
+ }
67
+
68
+ // src/client/common/utils/billingapi.ts
69
+ var BillingApiClient = class {
70
+ constructor(config, privateKey) {
71
+ this.account = (0, import_accounts.privateKeyToAccount)(privateKey);
72
+ this.config = config;
73
+ }
74
+ async createSubscription(productId = "compute") {
75
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
76
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
77
+ return resp.json();
78
+ }
79
+ async getSubscription(productId = "compute") {
80
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
81
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
82
+ return resp.json();
83
+ }
84
+ async cancelSubscription(productId = "compute") {
85
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
86
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
87
+ }
88
+ /**
89
+ * Make an authenticated request to the billing API
90
+ */
91
+ async makeAuthenticatedRequest(url, method, productId) {
92
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
93
+ const { signature } = await calculateBillingAuthSignature({
94
+ account: this.account,
95
+ product: productId,
96
+ expiry
97
+ });
98
+ const headers = {
99
+ Authorization: `Bearer ${signature}`,
100
+ "X-Account": this.account.address,
101
+ "X-Expiry": expiry.toString()
102
+ };
103
+ try {
104
+ const response = await (0, import_axios.default)({
105
+ method,
106
+ url,
107
+ headers,
108
+ timeout: 3e4,
109
+ maxRedirects: 0,
110
+ validateStatus: () => true
111
+ // Don't throw on any status
112
+ });
113
+ const status = response.status;
114
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
115
+ if (status < 200 || status >= 300) {
116
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
117
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
118
+ }
119
+ return {
120
+ json: async () => response.data,
121
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
122
+ };
123
+ } catch (error) {
124
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
125
+ const cause = error.cause?.message || error.cause || error.message;
126
+ throw new Error(
127
+ `Failed to connect to BillingAPI at ${url}: ${cause}
128
+ Please check:
129
+ 1. Your internet connection
130
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
131
+ 3. Firewall/proxy settings`
132
+ );
133
+ }
134
+ throw error;
135
+ }
136
+ }
137
+ };
138
+
139
+ // src/client/common/config/environment.ts
140
+ var SEPOLIA_CHAIN_ID = 11155111;
141
+ var MAINNET_CHAIN_ID = 1;
142
+ var CommonAddresses = {
143
+ ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
144
+ };
145
+ var ChainAddresses = {
146
+ [MAINNET_CHAIN_ID]: {
147
+ PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
148
+ },
149
+ [SEPOLIA_CHAIN_ID]: {
150
+ PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
151
+ }
152
+ };
153
+ var BILLING_ENVIRONMENTS = {
154
+ dev: {
155
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
156
+ },
157
+ prod: {
158
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
159
+ }
160
+ };
161
+ var ENVIRONMENTS = {
162
+ "sepolia-dev": {
163
+ name: "sepolia",
164
+ build: "dev",
165
+ appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
166
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
167
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
168
+ kmsServerURL: "http://10.128.0.57:8080",
169
+ userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
170
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
171
+ },
172
+ sepolia: {
173
+ name: "sepolia",
174
+ build: "prod",
175
+ appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
176
+ permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
177
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
178
+ kmsServerURL: "http://10.128.15.203:8080",
179
+ userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
180
+ defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
181
+ },
182
+ "mainnet-alpha": {
183
+ name: "mainnet-alpha",
184
+ build: "prod",
185
+ appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
186
+ permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
187
+ erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
188
+ kmsServerURL: "http://10.128.0.2:8080",
189
+ userApiServerURL: "https://userapi-compute.eigencloud.xyz",
190
+ defaultRPCURL: "https://ethereum-rpc.publicnode.com"
191
+ }
192
+ };
193
+ var CHAIN_ID_TO_ENVIRONMENT = {
194
+ [SEPOLIA_CHAIN_ID.toString()]: "sepolia",
195
+ [MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
196
+ };
197
+ function getBillingEnvironmentConfig(build) {
198
+ const config = BILLING_ENVIRONMENTS[build];
199
+ if (!config) {
200
+ throw new Error(`Unknown billing environment: ${build}`);
201
+ }
202
+ return config;
203
+ }
204
+ function getBuildType() {
205
+ const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
206
+ const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
207
+ const buildType = buildTimeType || runtimeType;
208
+ if (buildType === "dev") {
209
+ return "dev";
210
+ }
211
+ return "prod";
212
+ }
213
+
214
+ // src/client/common/utils/logger.ts
215
+ var getLogger = (verbose) => ({
216
+ info: (...args) => console.info(...args),
217
+ warn: (...args) => console.warn(...args),
218
+ error: (...args) => console.error(...args),
219
+ debug: (...args) => verbose && console.debug(...args)
220
+ });
221
+
222
+ // src/client/common/utils/userapi.ts
223
+ var import_axios2 = __toESM(require("axios"), 1);
224
+ var import_form_data = __toESM(require("form-data"), 1);
225
+ var import_viem3 = require("viem");
226
+ var import_accounts2 = require("viem/accounts");
227
+
228
+ // src/client/common/utils/helpers.ts
229
+ var import_viem2 = require("viem");
230
+ var import_chains2 = require("viem/chains");
231
+
232
+ // src/client/common/constants.ts
233
+ var import_chains = require("viem/chains");
234
+
235
+ // src/client/common/utils/helpers.ts
236
+ function addHexPrefix(value) {
237
+ return value.startsWith("0x") ? value : `0x${value}`;
238
+ }
239
+
240
+ // src/client/common/utils/billing.ts
241
+ function isSubscriptionActive(status) {
242
+ return status === "active" || status === "trialing";
243
+ }
244
+
245
+ // src/client/common/telemetry/noop.ts
246
+ var NoopClient = class {
247
+ /**
248
+ * AddMetric implements the TelemetryClient interface
249
+ */
250
+ async addMetric(_metric) {
251
+ }
252
+ /**
253
+ * Close implements the TelemetryClient interface
254
+ */
255
+ async close() {
256
+ }
257
+ };
258
+ function isNoopClient(client) {
259
+ return client instanceof NoopClient;
260
+ }
261
+
262
+ // src/client/common/telemetry/posthog.ts
263
+ var import_posthog_node = require("posthog-node");
264
+ var PostHogClient = class {
265
+ constructor(environment, namespace, apiKey, endpoint) {
266
+ this.namespace = namespace;
267
+ this.appEnvironment = environment;
268
+ const host = endpoint || "https://us.i.posthog.com";
269
+ this.client = new import_posthog_node.PostHog(apiKey, {
270
+ host,
271
+ flushAt: 1,
272
+ // Flush immediately for CLI/SDK usage
273
+ flushInterval: 0
274
+ // Disable interval flushing
275
+ });
276
+ this.client.identify({
277
+ distinctId: environment.userUUID,
278
+ properties: {
279
+ os: environment.os,
280
+ arch: environment.arch,
281
+ ...environment.cliVersion ? { cliVersion: environment.cliVersion } : {}
282
+ }
283
+ });
284
+ }
285
+ /**
286
+ * AddMetric implements the TelemetryClient interface
287
+ */
288
+ async addMetric(metric) {
289
+ try {
290
+ const props = {
291
+ name: metric.name,
292
+ value: metric.value
293
+ };
294
+ for (const [k, v] of Object.entries(metric.dimensions)) {
295
+ props[k] = v;
296
+ }
297
+ this.client.capture({
298
+ distinctId: this.appEnvironment.userUUID,
299
+ event: this.namespace,
300
+ properties: props
301
+ });
302
+ } catch {
303
+ }
304
+ }
305
+ /**
306
+ * Close implements the TelemetryClient interface
307
+ */
308
+ async close() {
309
+ try {
310
+ this.client.shutdown();
311
+ } catch {
312
+ }
313
+ }
314
+ };
315
+ function getPostHogAPIKey() {
316
+ if (process.env.ECLOUD_POSTHOG_KEY) {
317
+ return process.env.ECLOUD_POSTHOG_KEY;
318
+ }
319
+ return true ? "phc_BiKfywNft5iBI8N7MxmuVCkb4GGZj4mDFXYPmOPUAI8" : void 0;
320
+ }
321
+ function getPostHogEndpoint() {
322
+ return process.env.ECLOUD_POSTHOG_ENDPOINT || "https://us.i.posthog.com";
323
+ }
324
+
325
+ // src/client/common/telemetry/index.ts
326
+ var os = __toESM(require("os"), 1);
327
+
328
+ // src/client/common/telemetry/metricsContext.ts
329
+ function createMetricsContext() {
330
+ return {
331
+ startTime: /* @__PURE__ */ new Date(),
332
+ metrics: [],
333
+ properties: {}
334
+ };
335
+ }
336
+ function addMetric(context, name, value) {
337
+ addMetricWithDimensions(context, name, value, {});
338
+ }
339
+ function addMetricWithDimensions(context, name, value, dimensions) {
340
+ context.metrics.push({
341
+ name,
342
+ value,
343
+ dimensions
344
+ });
345
+ }
346
+
347
+ // src/client/common/telemetry/index.ts
348
+ function createTelemetryClient(environment, namespace, options) {
349
+ const telemetryEnabled = options?.telemetryEnabled === true;
350
+ if (!telemetryEnabled) {
351
+ return new NoopClient();
352
+ }
353
+ const resolvedApiKey = options?.apiKey || getPostHogAPIKey();
354
+ if (!resolvedApiKey) {
355
+ return new NoopClient();
356
+ }
357
+ const endpoint = options?.endpoint || getPostHogEndpoint();
358
+ try {
359
+ return new PostHogClient(environment, namespace, resolvedApiKey, endpoint);
360
+ } catch {
361
+ return new NoopClient();
362
+ }
363
+ }
364
+ function createAppEnvironment(userUUID, cliVersion, osOverride, archOverride) {
365
+ return {
366
+ userUUID,
367
+ cliVersion,
368
+ os: osOverride || os.platform(),
369
+ arch: archOverride || os.arch()
370
+ };
371
+ }
372
+ async function emitMetrics(client, context) {
373
+ if (isNoopClient(client)) {
374
+ return;
375
+ }
376
+ for (const metric of context.metrics) {
377
+ const dimensions = {
378
+ ...metric.dimensions,
379
+ ...context.properties
380
+ };
381
+ const metricWithProperties = {
382
+ ...metric,
383
+ dimensions
384
+ };
385
+ try {
386
+ await client.addMetric(metricWithProperties);
387
+ } catch {
388
+ }
389
+ }
390
+ }
391
+
392
+ // src/client/common/telemetry/wrapper.ts
393
+ var import_crypto = require("crypto");
394
+ function generateRandomUUID() {
395
+ return (0, import_crypto.randomUUID)();
396
+ }
397
+ async function withSDKTelemetry(options, action) {
398
+ if (options.skipTelemetry) {
399
+ return action();
400
+ }
401
+ const userUUID = options.userUUID || generateRandomUUID();
402
+ const environment = createAppEnvironment(userUUID);
403
+ const client = createTelemetryClient(environment, "ecloud-sdk", {
404
+ telemetryEnabled: options.telemetryEnabled,
405
+ apiKey: options.apiKey,
406
+ endpoint: options.endpoint
407
+ });
408
+ const metrics = createMetricsContext();
409
+ metrics.properties["source"] = "ecloud-sdk";
410
+ metrics.properties["function"] = options.functionName;
411
+ if (options.properties) {
412
+ Object.assign(metrics.properties, options.properties);
413
+ }
414
+ addMetric(metrics, "Count", 1);
415
+ let actionError;
416
+ let result;
417
+ try {
418
+ result = await action();
419
+ return result;
420
+ } catch (err) {
421
+ actionError = err instanceof Error ? err : new Error(String(err));
422
+ throw err;
423
+ } finally {
424
+ const resultValue = actionError ? "Failure" : "Success";
425
+ const dimensions = {};
426
+ if (actionError) {
427
+ dimensions["error"] = actionError.message;
428
+ }
429
+ addMetricWithDimensions(metrics, resultValue, 1, dimensions);
430
+ const duration = Date.now() - metrics.startTime.getTime();
431
+ addMetric(metrics, "DurationMilliseconds", duration);
432
+ try {
433
+ await emitMetrics(client, metrics);
434
+ await client.close();
435
+ } catch {
436
+ }
437
+ }
438
+ }
439
+
440
+ // src/client/modules/billing/index.ts
441
+ function createBillingModule(config) {
442
+ const { verbose = false, skipTelemetry = false } = config;
443
+ const privateKey = addHexPrefix(config.privateKey);
444
+ const logger = getLogger(verbose);
445
+ const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
446
+ const billingApi = new BillingApiClient(billingEnvConfig, privateKey);
447
+ return {
448
+ async subscribe(opts) {
449
+ return withSDKTelemetry(
450
+ {
451
+ functionName: "subscribe",
452
+ skipTelemetry,
453
+ // Skip if called from CLI
454
+ properties: { productId: opts?.productId || "compute" }
455
+ },
456
+ async () => {
457
+ const productId = opts?.productId || "compute";
458
+ logger.debug(`Checking existing subscription for ${productId}...`);
459
+ const currentStatus = await billingApi.getSubscription(productId);
460
+ if (isSubscriptionActive(currentStatus.subscriptionStatus)) {
461
+ logger.debug(`Subscription already active: ${currentStatus.subscriptionStatus}`);
462
+ return {
463
+ type: "already_active",
464
+ status: currentStatus.subscriptionStatus
465
+ };
466
+ }
467
+ if (currentStatus.subscriptionStatus === "past_due" || currentStatus.subscriptionStatus === "unpaid") {
468
+ logger.debug(`Subscription has payment issue: ${currentStatus.subscriptionStatus}`);
469
+ return {
470
+ type: "payment_issue",
471
+ status: currentStatus.subscriptionStatus,
472
+ portalUrl: currentStatus.portalUrl
473
+ };
474
+ }
475
+ logger.debug(`Creating subscription for ${productId}...`);
476
+ const result = await billingApi.createSubscription(productId);
477
+ logger.debug(`Checkout URL: ${result.checkoutUrl}`);
478
+ return {
479
+ type: "checkout_created",
480
+ checkoutUrl: result.checkoutUrl
481
+ };
482
+ }
483
+ );
484
+ },
485
+ async getStatus(opts) {
486
+ return withSDKTelemetry(
487
+ {
488
+ functionName: "getStatus",
489
+ skipTelemetry,
490
+ // Skip if called from CLI
491
+ properties: { productId: opts?.productId || "compute" }
492
+ },
493
+ async () => {
494
+ const productId = opts?.productId || "compute";
495
+ logger.debug(`Fetching subscription status for ${productId}...`);
496
+ const result = await billingApi.getSubscription(productId);
497
+ logger.debug(`Subscription status: ${result.subscriptionStatus}`);
498
+ return result;
499
+ }
500
+ );
501
+ },
502
+ async cancel(opts) {
503
+ return withSDKTelemetry(
504
+ {
505
+ functionName: "cancel",
506
+ skipTelemetry,
507
+ // Skip if called from CLI
508
+ properties: { productId: opts?.productId || "compute" }
509
+ },
510
+ async () => {
511
+ const productId = opts?.productId || "compute";
512
+ logger.debug(`Checking subscription status for ${productId}...`);
513
+ const currentStatus = await billingApi.getSubscription(productId);
514
+ if (!isSubscriptionActive(currentStatus.subscriptionStatus)) {
515
+ logger.debug(`No active subscription to cancel: ${currentStatus.subscriptionStatus}`);
516
+ return {
517
+ type: "no_active_subscription",
518
+ status: currentStatus.subscriptionStatus
519
+ };
520
+ }
521
+ logger.debug(`Canceling subscription for ${productId}...`);
522
+ await billingApi.cancelSubscription(productId);
523
+ logger.debug(`Subscription canceled successfully`);
524
+ return {
525
+ type: "canceled"
526
+ };
527
+ }
528
+ );
529
+ }
530
+ };
531
+ }
532
+ // Annotate the CommonJS export names for ESM import in node:
533
+ 0 && (module.exports = {
534
+ createBillingModule
535
+ });
536
+ //# sourceMappingURL=billing.cjs.map