@layr-labs/ecloud-sdk 0.1.1 → 0.2.0-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.
@@ -0,0 +1,180 @@
1
+ import {
2
+ addHexPrefix,
3
+ calculateBillingAuthSignature,
4
+ getBillingEnvironmentConfig,
5
+ getBuildType,
6
+ getLogger,
7
+ isSubscriptionActive,
8
+ withSDKTelemetry
9
+ } from "./chunk-34DXGQ35.js";
10
+
11
+ // src/client/common/utils/billingapi.ts
12
+ import axios from "axios";
13
+ import { privateKeyToAccount } from "viem/accounts";
14
+ var BillingApiClient = class {
15
+ constructor(config, privateKey) {
16
+ this.account = privateKeyToAccount(privateKey);
17
+ this.config = config;
18
+ }
19
+ async createSubscription(productId = "compute") {
20
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
21
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
22
+ return resp.json();
23
+ }
24
+ async getSubscription(productId = "compute") {
25
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
26
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
27
+ return resp.json();
28
+ }
29
+ async cancelSubscription(productId = "compute") {
30
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
31
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
32
+ }
33
+ /**
34
+ * Make an authenticated request to the billing API
35
+ */
36
+ async makeAuthenticatedRequest(url, method, productId) {
37
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
38
+ const { signature } = await calculateBillingAuthSignature({
39
+ account: this.account,
40
+ product: productId,
41
+ expiry
42
+ });
43
+ const headers = {
44
+ Authorization: `Bearer ${signature}`,
45
+ "X-Account": this.account.address,
46
+ "X-Expiry": expiry.toString()
47
+ };
48
+ try {
49
+ const response = await axios({
50
+ method,
51
+ url,
52
+ headers,
53
+ timeout: 3e4,
54
+ maxRedirects: 0,
55
+ validateStatus: () => true
56
+ // Don't throw on any status
57
+ });
58
+ const status = response.status;
59
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
60
+ if (status < 200 || status >= 300) {
61
+ const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
62
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
63
+ }
64
+ return {
65
+ json: async () => response.data,
66
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
67
+ };
68
+ } catch (error) {
69
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
70
+ const cause = error.cause?.message || error.cause || error.message;
71
+ throw new Error(
72
+ `Failed to connect to BillingAPI at ${url}: ${cause}
73
+ Please check:
74
+ 1. Your internet connection
75
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
76
+ 3. Firewall/proxy settings`
77
+ );
78
+ }
79
+ throw error;
80
+ }
81
+ }
82
+ };
83
+
84
+ // src/client/modules/billing/index.ts
85
+ function createBillingModule(config) {
86
+ const { verbose = false, skipTelemetry = false } = config;
87
+ const privateKey = addHexPrefix(config.privateKey);
88
+ const logger = getLogger(verbose);
89
+ const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
90
+ const billingApi = new BillingApiClient(billingEnvConfig, privateKey);
91
+ return {
92
+ async subscribe(opts) {
93
+ return withSDKTelemetry(
94
+ {
95
+ functionName: "subscribe",
96
+ skipTelemetry,
97
+ // Skip if called from CLI
98
+ properties: { productId: opts?.productId || "compute" }
99
+ },
100
+ async () => {
101
+ const productId = opts?.productId || "compute";
102
+ logger.debug(`Checking existing subscription for ${productId}...`);
103
+ const currentStatus = await billingApi.getSubscription(productId);
104
+ if (isSubscriptionActive(currentStatus.subscriptionStatus)) {
105
+ logger.debug(`Subscription already active: ${currentStatus.subscriptionStatus}`);
106
+ return {
107
+ type: "already_active",
108
+ status: currentStatus.subscriptionStatus
109
+ };
110
+ }
111
+ if (currentStatus.subscriptionStatus === "past_due" || currentStatus.subscriptionStatus === "unpaid") {
112
+ logger.debug(`Subscription has payment issue: ${currentStatus.subscriptionStatus}`);
113
+ return {
114
+ type: "payment_issue",
115
+ status: currentStatus.subscriptionStatus,
116
+ portalUrl: currentStatus.portalUrl
117
+ };
118
+ }
119
+ logger.debug(`Creating subscription for ${productId}...`);
120
+ const result = await billingApi.createSubscription(productId);
121
+ logger.debug(`Checkout URL: ${result.checkoutUrl}`);
122
+ return {
123
+ type: "checkout_created",
124
+ checkoutUrl: result.checkoutUrl
125
+ };
126
+ }
127
+ );
128
+ },
129
+ async getStatus(opts) {
130
+ return withSDKTelemetry(
131
+ {
132
+ functionName: "getStatus",
133
+ skipTelemetry,
134
+ // Skip if called from CLI
135
+ properties: { productId: opts?.productId || "compute" }
136
+ },
137
+ async () => {
138
+ const productId = opts?.productId || "compute";
139
+ logger.debug(`Fetching subscription status for ${productId}...`);
140
+ const result = await billingApi.getSubscription(productId);
141
+ logger.debug(`Subscription status: ${result.subscriptionStatus}`);
142
+ return result;
143
+ }
144
+ );
145
+ },
146
+ async cancel(opts) {
147
+ return withSDKTelemetry(
148
+ {
149
+ functionName: "cancel",
150
+ skipTelemetry,
151
+ // Skip if called from CLI
152
+ properties: { productId: opts?.productId || "compute" }
153
+ },
154
+ async () => {
155
+ const productId = opts?.productId || "compute";
156
+ logger.debug(`Checking subscription status for ${productId}...`);
157
+ const currentStatus = await billingApi.getSubscription(productId);
158
+ if (!isSubscriptionActive(currentStatus.subscriptionStatus)) {
159
+ logger.debug(`No active subscription to cancel: ${currentStatus.subscriptionStatus}`);
160
+ return {
161
+ type: "no_active_subscription",
162
+ status: currentStatus.subscriptionStatus
163
+ };
164
+ }
165
+ logger.debug(`Canceling subscription for ${productId}...`);
166
+ await billingApi.cancelSubscription(productId);
167
+ logger.debug(`Subscription canceled successfully`);
168
+ return {
169
+ type: "canceled"
170
+ };
171
+ }
172
+ );
173
+ }
174
+ };
175
+ }
176
+
177
+ export {
178
+ createBillingModule
179
+ };
180
+ //# sourceMappingURL=chunk-LINGJMAS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/common/utils/billingapi.ts","../src/client/modules/billing/index.ts"],"sourcesContent":["/**\n * BillingAPI Client to manage product subscriptions\n * Standalone client - does not depend on chain infrastructure\n */\n\nimport axios, { AxiosResponse } from \"axios\";\nimport { Hex } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { ProductID, CreateSubscriptionResponse, ProductSubscriptionResponse } from \"../types\";\nimport { calculateBillingAuthSignature } from \"./auth\";\nimport { BillingEnvironmentConfig } from \"../types\";\n\nexport class BillingApiClient {\n private readonly account: ReturnType<typeof privateKeyToAccount>;\n private readonly config: BillingEnvironmentConfig;\n\n constructor(config: BillingEnvironmentConfig, privateKey: Hex) {\n this.account = privateKeyToAccount(privateKey);\n this.config = config;\n }\n\n async createSubscription(productId: ProductID = \"compute\"): Promise<CreateSubscriptionResponse> {\n const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;\n const resp = await this.makeAuthenticatedRequest(endpoint, \"POST\", productId);\n return resp.json();\n }\n\n async getSubscription(productId: ProductID = \"compute\"): Promise<ProductSubscriptionResponse> {\n const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;\n const resp = await this.makeAuthenticatedRequest(endpoint, \"GET\", productId);\n return resp.json();\n }\n\n async cancelSubscription(productId: ProductID = \"compute\"): Promise<void> {\n const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;\n await this.makeAuthenticatedRequest(endpoint, \"DELETE\", productId);\n }\n\n /**\n * Make an authenticated request to the billing API\n */\n private async makeAuthenticatedRequest(\n url: string,\n method: \"GET\" | \"POST\" | \"DELETE\",\n productId: ProductID,\n ): Promise<{ json: () => Promise<any>; text: () => Promise<string> }> {\n // Calculate expiry (5 minutes from now)\n const expiry = BigInt(Math.floor(Date.now() / 1000) + 5 * 60);\n\n // Use EIP-712 typed data signature for billing auth\n const { signature } = await calculateBillingAuthSignature({\n account: this.account,\n product: productId,\n expiry,\n });\n\n // Prepare headers\n const headers: Record<string, string> = {\n Authorization: `Bearer ${signature}`,\n \"X-Account\": this.account.address,\n \"X-Expiry\": expiry.toString(),\n };\n\n try {\n // Use axios to make the request\n const response: AxiosResponse = await axios({\n method,\n url,\n headers,\n timeout: 30_000,\n maxRedirects: 0,\n validateStatus: () => true, // Don't throw on any status\n });\n\n const status = response.status;\n const statusText = status >= 200 && status < 300 ? \"OK\" : \"Error\";\n\n if (status < 200 || status >= 300) {\n const body =\n typeof response.data === \"string\" ? response.data : JSON.stringify(response.data);\n throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);\n }\n\n // Return Response-like object for compatibility\n return {\n json: async () => response.data,\n text: async () =>\n typeof response.data === \"string\" ? response.data : JSON.stringify(response.data),\n };\n } catch (error: any) {\n // Handle network errors\n if (\n error.message?.includes(\"fetch failed\") ||\n error.message?.includes(\"ECONNREFUSED\") ||\n error.message?.includes(\"ENOTFOUND\") ||\n error.cause\n ) {\n const cause = error.cause?.message || error.cause || error.message;\n throw new Error(\n `Failed to connect to BillingAPI at ${url}: ${cause}\\n` +\n `Please check:\\n` +\n `1. Your internet connection\\n` +\n `2. The API server is accessible: ${this.config.billingApiServerURL}\\n` +\n `3. Firewall/proxy settings`,\n );\n }\n // Re-throw other errors as-is\n throw error;\n }\n }\n}\n","/**\n * Main Billing namespace entry point\n */\n\nimport { BillingApiClient } from \"../../common/utils/billingapi\";\nimport { getBillingEnvironmentConfig, getBuildType } from \"../../common/config/environment\";\nimport { getLogger, isSubscriptionActive, addHexPrefix } from \"../../common/utils\";\nimport { withSDKTelemetry } from \"../../common/telemetry/wrapper\";\n\nimport type { Hex } from \"viem\";\nimport type {\n ProductID,\n SubscriptionOpts,\n SubscribeResponse,\n CancelResponse,\n ProductSubscriptionResponse,\n} from \"../../common/types\";\n\nexport interface BillingModule {\n subscribe: (opts?: SubscriptionOpts) => Promise<SubscribeResponse>;\n getStatus: (opts?: SubscriptionOpts) => Promise<ProductSubscriptionResponse>;\n cancel: (opts?: SubscriptionOpts) => Promise<CancelResponse>;\n}\n\nexport interface BillingModuleConfig {\n verbose?: boolean;\n privateKey: Hex;\n skipTelemetry?: boolean; // Skip telemetry when called from CLI\n}\n\nexport function createBillingModule(config: BillingModuleConfig): BillingModule {\n const { verbose = false, skipTelemetry = false } = config;\n const privateKey = addHexPrefix(config.privateKey);\n\n const logger = getLogger(verbose);\n\n // Get billing environment configuration\n const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());\n\n // Create billing API client\n const billingApi = new BillingApiClient(billingEnvConfig, privateKey);\n\n return {\n async subscribe(opts) {\n return withSDKTelemetry(\n {\n functionName: \"subscribe\",\n skipTelemetry: skipTelemetry, // Skip if called from CLI\n properties: { productId: opts?.productId || \"compute\" },\n },\n async () => {\n const productId: ProductID = opts?.productId || \"compute\";\n\n // Check existing subscription status first\n logger.debug(`Checking existing subscription for ${productId}...`);\n const currentStatus = await billingApi.getSubscription(productId);\n\n // If already active or trialing, don't create new checkout\n if (isSubscriptionActive(currentStatus.subscriptionStatus)) {\n logger.debug(`Subscription already active: ${currentStatus.subscriptionStatus}`);\n return {\n type: \"already_active\" as const,\n status: currentStatus.subscriptionStatus,\n };\n }\n\n // If subscription has payment issues, return portal URL instead\n if (\n currentStatus.subscriptionStatus === \"past_due\" ||\n currentStatus.subscriptionStatus === \"unpaid\"\n ) {\n logger.debug(`Subscription has payment issue: ${currentStatus.subscriptionStatus}`);\n return {\n type: \"payment_issue\" as const,\n status: currentStatus.subscriptionStatus,\n portalUrl: currentStatus.portalUrl,\n };\n }\n\n // Create new checkout session\n logger.debug(`Creating subscription for ${productId}...`);\n const result = await billingApi.createSubscription(productId);\n\n logger.debug(`Checkout URL: ${result.checkoutUrl}`);\n return {\n type: \"checkout_created\" as const,\n checkoutUrl: result.checkoutUrl,\n };\n },\n );\n },\n\n async getStatus(opts) {\n return withSDKTelemetry(\n {\n functionName: \"getStatus\",\n skipTelemetry: skipTelemetry, // Skip if called from CLI\n properties: { productId: opts?.productId || \"compute\" },\n },\n async () => {\n const productId: ProductID = opts?.productId || \"compute\";\n logger.debug(`Fetching subscription status for ${productId}...`);\n\n const result = await billingApi.getSubscription(productId);\n\n logger.debug(`Subscription status: ${result.subscriptionStatus}`);\n return result;\n },\n );\n },\n\n async cancel(opts) {\n return withSDKTelemetry(\n {\n functionName: \"cancel\",\n skipTelemetry: skipTelemetry, // Skip if called from CLI\n properties: { productId: opts?.productId || \"compute\" },\n },\n async () => {\n const productId: ProductID = opts?.productId || \"compute\";\n\n // Check existing subscription status first\n logger.debug(`Checking subscription status for ${productId}...`);\n const currentStatus = await billingApi.getSubscription(productId);\n\n // If no active subscription, don't attempt to cancel\n if (!isSubscriptionActive(currentStatus.subscriptionStatus)) {\n logger.debug(`No active subscription to cancel: ${currentStatus.subscriptionStatus}`);\n return {\n type: \"no_active_subscription\" as const,\n status: currentStatus.subscriptionStatus,\n };\n }\n\n // Cancel the subscription\n logger.debug(`Canceling subscription for ${productId}...`);\n await billingApi.cancelSubscription(productId);\n\n logger.debug(`Subscription canceled successfully`);\n return {\n type: \"canceled\" as const,\n };\n },\n );\n },\n };\n}\n"],"mappings":"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","names":[]}
package/dist/compute.cjs CHANGED
@@ -114,7 +114,7 @@ function getEnvironmentConfig(environment, chainID) {
114
114
  };
115
115
  }
116
116
  function getBuildType() {
117
- const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
117
+ const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
118
118
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
119
119
  const buildType = buildTimeType || runtimeType;
120
120
  if (buildType === "dev") {
@@ -2385,12 +2385,23 @@ function stripHexPrefix(value) {
2385
2385
  }
2386
2386
 
2387
2387
  // src/client/common/utils/userapi.ts
2388
+ function isJsonObject(value) {
2389
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2390
+ }
2391
+ function readString(obj, key) {
2392
+ const v = obj[key];
2393
+ return typeof v === "string" ? v : void 0;
2394
+ }
2395
+ function readNumber(obj, key) {
2396
+ const v = obj[key];
2397
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2398
+ }
2388
2399
  var MAX_ADDRESS_COUNT = 5;
2389
2400
  var CanViewAppLogsPermission = "0x2fd3f2fe";
2390
2401
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
2391
2402
  var CanUpdateAppProfilePermission = "0x036fef61";
2392
2403
  function getDefaultClientId() {
2393
- const version = true ? "0.1.1" : "0.0.0";
2404
+ const version = true ? "0.2.0-dev" : "0.0.0";
2394
2405
  return `ecloud-sdk/v${version}`;
2395
2406
  }
2396
2407
  var UserApiClient = class {
@@ -2424,6 +2435,31 @@ var UserApiClient = class {
2424
2435
  };
2425
2436
  });
2426
2437
  }
2438
+ /**
2439
+ * Get app details from UserAPI (includes releases and build/provenance info when available).
2440
+ *
2441
+ * Endpoint: GET /apps/:appAddress
2442
+ */
2443
+ async getApp(appAddress) {
2444
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
2445
+ const res = await this.makeAuthenticatedRequest(endpoint);
2446
+ const raw = await res.json();
2447
+ if (!isJsonObject(raw)) {
2448
+ throw new Error("Unexpected /apps/:id response: expected object");
2449
+ }
2450
+ const id = readString(raw, "id");
2451
+ if (!id) {
2452
+ throw new Error("Unexpected /apps/:id response: missing 'id'");
2453
+ }
2454
+ const releasesRaw = raw.releases;
2455
+ const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
2456
+ return {
2457
+ id,
2458
+ creator: readString(raw, "creator"),
2459
+ contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
2460
+ releases
2461
+ };
2462
+ }
2427
2463
  /**
2428
2464
  * Get available SKUs (instance types) from UserAPI
2429
2465
  */
@@ -2596,6 +2632,48 @@ Please check:
2596
2632
  };
2597
2633
  }
2598
2634
  };
2635
+ function transformAppReleaseBuild(raw) {
2636
+ if (!isJsonObject(raw)) return void 0;
2637
+ const depsRaw = raw.dependencies;
2638
+ const deps = isJsonObject(depsRaw) ? Object.fromEntries(
2639
+ Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
2640
+ const parsed = transformAppReleaseBuild(depRaw);
2641
+ return parsed ? [[digest, parsed]] : [];
2642
+ })
2643
+ ) : void 0;
2644
+ return {
2645
+ buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
2646
+ billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
2647
+ repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
2648
+ gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
2649
+ status: readString(raw, "status"),
2650
+ buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
2651
+ imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
2652
+ imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
2653
+ imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
2654
+ provenanceJson: raw.provenance_json ?? raw.provenanceJson,
2655
+ provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
2656
+ createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
2657
+ updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
2658
+ errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
2659
+ dependencies: deps
2660
+ };
2661
+ }
2662
+ function transformAppRelease(raw) {
2663
+ if (!isJsonObject(raw)) return void 0;
2664
+ return {
2665
+ appId: readString(raw, "appId") ?? readString(raw, "app_id"),
2666
+ rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
2667
+ imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
2668
+ registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
2669
+ publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
2670
+ encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
2671
+ upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
2672
+ createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
2673
+ createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
2674
+ build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
2675
+ };
2676
+ }
2599
2677
 
2600
2678
  // src/client/common/abis/AppController.json
2601
2679
  var AppController_default = [