@layr-labs/ecloud-cli 0.0.1-dev-rfc.1

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 (44) hide show
  1. package/README.md +195 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/app/create.js +29 -0
  7. package/dist/commands/app/create.js.map +1 -0
  8. package/dist/commands/app/deploy.js +142 -0
  9. package/dist/commands/app/deploy.js.map +1 -0
  10. package/dist/commands/app/logs.js +108 -0
  11. package/dist/commands/app/logs.js.map +1 -0
  12. package/dist/commands/app/start.js +121 -0
  13. package/dist/commands/app/start.js.map +1 -0
  14. package/dist/commands/app/stop.js +121 -0
  15. package/dist/commands/app/stop.js.map +1 -0
  16. package/dist/commands/app/terminate.js +128 -0
  17. package/dist/commands/app/terminate.js.map +1 -0
  18. package/dist/commands/app/upgrade.js +142 -0
  19. package/dist/commands/app/upgrade.js.map +1 -0
  20. package/dist/commands/auth/generate.js +101 -0
  21. package/dist/commands/auth/generate.js.map +1 -0
  22. package/dist/commands/auth/login.js +150 -0
  23. package/dist/commands/auth/login.js.map +1 -0
  24. package/dist/commands/auth/logout.js +64 -0
  25. package/dist/commands/auth/logout.js.map +1 -0
  26. package/dist/commands/auth/migrate.js +129 -0
  27. package/dist/commands/auth/migrate.js.map +1 -0
  28. package/dist/commands/auth/whoami.js +87 -0
  29. package/dist/commands/auth/whoami.js.map +1 -0
  30. package/dist/commands/billing/cancel.js +132 -0
  31. package/dist/commands/billing/cancel.js.map +1 -0
  32. package/dist/commands/billing/status.js +175 -0
  33. package/dist/commands/billing/status.js.map +1 -0
  34. package/dist/commands/billing/subscribe.js +157 -0
  35. package/dist/commands/billing/subscribe.js.map +1 -0
  36. package/dist/keys/mainnet-alpha/prod/kms-encryption-public-key.pem +14 -0
  37. package/dist/keys/mainnet-alpha/prod/kms-signing-public-key.pem +4 -0
  38. package/dist/keys/sepolia/dev/kms-encryption-public-key.pem +14 -0
  39. package/dist/keys/sepolia/dev/kms-signing-public-key.pem +4 -0
  40. package/dist/keys/sepolia/prod/kms-encryption-public-key.pem +14 -0
  41. package/dist/keys/sepolia/prod/kms-signing-public-key.pem +4 -0
  42. package/dist/templates/Dockerfile.layered.tmpl +58 -0
  43. package/dist/templates/compute-source-env.sh.tmpl +110 -0
  44. package/package.json +53 -0
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/billing/status.ts
4
+ import { Command, Flags as Flags2 } from "@oclif/core";
5
+
6
+ // src/client.ts
7
+ import {
8
+ createAppModule,
9
+ createBillingModule,
10
+ getPrivateKeyInteractive as getPrivateKeyInteractive2,
11
+ getEnvironmentConfig,
12
+ requirePrivateKey,
13
+ getPrivateKeyWithSource
14
+ } from "@layr-labs/ecloud-sdk";
15
+
16
+ // src/flags.ts
17
+ import {
18
+ getEnvironmentInteractive,
19
+ getPrivateKeyInteractive,
20
+ getAvailableEnvironments
21
+ } from "@layr-labs/ecloud-sdk";
22
+ import { Flags } from "@oclif/core";
23
+ var getEnvironmentOptions = () => {
24
+ try {
25
+ return getAvailableEnvironments();
26
+ } catch {
27
+ return ["sepolia", "sepolia-dev", "mainnet-alpha"];
28
+ }
29
+ };
30
+ var commonFlags = {
31
+ environment: Flags.string({
32
+ required: false,
33
+ description: "Deployment environment to use",
34
+ options: getEnvironmentOptions(),
35
+ env: "ECLOUD_ENV"
36
+ }),
37
+ "private-key": Flags.string({
38
+ required: false,
39
+ description: "Private key for signing transactions",
40
+ env: "ECLOUD_PRIVATE_KEY"
41
+ }),
42
+ "rpc-url": Flags.string({
43
+ required: false,
44
+ description: "RPC URL to connect to blockchain",
45
+ env: "ECLOUD_RPC_URL"
46
+ }),
47
+ verbose: Flags.boolean({
48
+ required: false,
49
+ description: "Enable verbose logging (default: false)",
50
+ default: false
51
+ })
52
+ };
53
+
54
+ // src/client.ts
55
+ async function createBillingClient(flags) {
56
+ const result = await getPrivateKeyWithSource({
57
+ privateKey: flags["private-key"]
58
+ });
59
+ const privateKey = await getPrivateKeyInteractive2(result?.key);
60
+ return createBillingModule({
61
+ verbose: flags.verbose ?? false,
62
+ privateKey
63
+ });
64
+ }
65
+
66
+ // src/commands/billing/status.ts
67
+ import chalk from "chalk";
68
+ var BillingStatus = class _BillingStatus extends Command {
69
+ static description = "Show subscription status";
70
+ static flags = {
71
+ "private-key": commonFlags["private-key"],
72
+ verbose: commonFlags.verbose,
73
+ product: Flags2.string({
74
+ required: false,
75
+ description: "Product ID",
76
+ default: "compute",
77
+ options: ["compute"],
78
+ env: "ECLOUD_PRODUCT_ID"
79
+ })
80
+ };
81
+ async run() {
82
+ const { flags } = await this.parse(_BillingStatus);
83
+ const billing = await createBillingClient(flags);
84
+ const result = await billing.getStatus({
85
+ productId: flags.product
86
+ });
87
+ const formatExpiry = (timestamp) => timestamp ? ` (expires ${new Date(timestamp * 1e3).toLocaleDateString()})` : "";
88
+ const formatStatus = (status) => {
89
+ switch (status) {
90
+ case "active":
91
+ return `${chalk.green("\u2713 Active")}`;
92
+ case "trialing":
93
+ return `${chalk.green("\u2713 Trial")}`;
94
+ case "past_due":
95
+ return `${chalk.yellow("\u26A0 Past Due")}`;
96
+ case "canceled":
97
+ return `${chalk.red("\u2717 Canceled")}`;
98
+ case "inactive":
99
+ return `${chalk.gray("\u2717 Inactive")}`;
100
+ case "incomplete":
101
+ return `${chalk.yellow("\u26A0 Incomplete")}`;
102
+ case "incomplete_expired":
103
+ return `${chalk.red("\u2717 Expired")}`;
104
+ case "unpaid":
105
+ return `${chalk.yellow("\u26A0 Unpaid")}`;
106
+ case "paused":
107
+ return `${chalk.yellow("\u26A0 Paused")}`;
108
+ default:
109
+ return status;
110
+ }
111
+ };
112
+ this.log(`
113
+ ${chalk.bold("Subscription Status:")}`);
114
+ this.log(` Status: ${formatStatus(result.subscriptionStatus)}`);
115
+ this.log(` Product: ${result.productId}`);
116
+ if (result.currentPeriodStart && result.currentPeriodEnd) {
117
+ const startDate = new Date(result.currentPeriodStart).toLocaleDateString();
118
+ const endDate = new Date(result.currentPeriodEnd).toLocaleDateString();
119
+ this.log(` Current Period: ${startDate} - ${endDate}`);
120
+ }
121
+ if (result.lineItems && result.lineItems.length > 0) {
122
+ this.log(`
123
+ ${chalk.bold(" Line Items:")}`);
124
+ for (const item of result.lineItems) {
125
+ const product = `${flags.product.charAt(0).toUpperCase()}${flags.product.slice(1)}`;
126
+ const chain = item.description.toLowerCase().includes("sepolia") ? "Sepolia" : "Mainnet";
127
+ this.log(
128
+ ` \u2022 ${product} (${chain}): $${item.subtotal.toFixed(2)} (${item.quantity} vCPU hours \xD7 $${item.price.toFixed(3)}/vCPU hour)`
129
+ );
130
+ }
131
+ }
132
+ if (result.creditsApplied !== void 0 && result.creditsApplied > 0) {
133
+ this.log(`
134
+ ${chalk.bold(" Invoice Summary:")}`);
135
+ const subtotal = result.upcomingInvoiceSubtotal ?? result.upcomingInvoiceTotal ?? 0;
136
+ this.log(` Subtotal: $${subtotal.toFixed(2)}`);
137
+ this.log(` Credits Applied: ${chalk.green(`-$${result.creditsApplied.toFixed(2)}`)}`);
138
+ this.log(` ${"\u2500".repeat(21)}`);
139
+ this.log(` Total Due: $${(result.upcomingInvoiceTotal ?? 0).toFixed(2)}`);
140
+ if (result.remainingCredits !== void 0) {
141
+ this.log(`
142
+ ${chalk.bold("Remaining Credits:")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`);
143
+ }
144
+ } else if (result.upcomingInvoiceTotal !== void 0) {
145
+ this.log(
146
+ `
147
+ Upcoming Invoice: $${result.upcomingInvoiceTotal.toFixed(2)}`
148
+ );
149
+ if (result.remainingCredits !== void 0 && result.remainingCredits > 0) {
150
+ this.log(` ${chalk.bold("Available Credits:")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`);
151
+ }
152
+ }
153
+ if (result.cancelAtPeriodEnd) {
154
+ this.log(
155
+ `
156
+ ${chalk.yellow("\u26A0 Subscription will cancel at period end")}`
157
+ );
158
+ }
159
+ if (result.canceledAt) {
160
+ const cancelDate = new Date(result.canceledAt).toLocaleDateString();
161
+ this.log(` Canceled On: ${cancelDate}`);
162
+ }
163
+ if (result.portalUrl) {
164
+ this.log(
165
+ `
166
+ ${chalk.dim("Manage subscription:")} ${chalk.cyan(result.portalUrl)}`
167
+ );
168
+ }
169
+ this.log();
170
+ }
171
+ };
172
+ export {
173
+ BillingStatus as default
174
+ };
175
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/billing/status.ts","../../../src/client.ts","../../../src/flags.ts"],"sourcesContent":["import { Command, Flags } from \"@oclif/core\";\nimport { createBillingClient } from \"../../client\";\nimport { commonFlags } from \"../../flags\";\nimport chalk from \"chalk\";\n\nexport default class BillingStatus extends Command {\n static description = \"Show subscription status\";\n\n static flags = {\n \"private-key\": commonFlags[\"private-key\"],\n verbose: commonFlags.verbose,\n product: Flags.string({\n required: false,\n description: \"Product ID\",\n default: \"compute\",\n options: [\"compute\"],\n env: \"ECLOUD_PRODUCT_ID\",\n }),\n };\n\n async run() {\n const { flags } = await this.parse(BillingStatus);\n const billing = await createBillingClient(flags);\n\n const result = await billing.getStatus({\n productId: flags.product as \"compute\",\n });\n\n const formatExpiry = (timestamp?: number) =>\n timestamp ? ` (expires ${new Date(timestamp * 1000).toLocaleDateString()})` : \"\";\n\n // Format status with appropriate color and symbol\n const formatStatus = (status: string) => {\n switch (status) {\n case \"active\":\n return `${chalk.green(\"✓ Active\")}`;\n case \"trialing\":\n return `${chalk.green(\"✓ Trial\")}`;\n case \"past_due\":\n return `${chalk.yellow(\"⚠ Past Due\")}`;\n case \"canceled\":\n return `${chalk.red(\"✗ Canceled\")}`;\n case \"inactive\":\n return `${chalk.gray(\"✗ Inactive\")}`;\n case \"incomplete\":\n return `${chalk.yellow(\"⚠ Incomplete\")}`;\n case \"incomplete_expired\":\n return `${chalk.red(\"✗ Expired\")}`;\n case \"unpaid\":\n return `${chalk.yellow(\"⚠ Unpaid\")}`;\n case \"paused\":\n return `${chalk.yellow(\"⚠ Paused\")}`;\n default:\n return status;\n }\n };\n\n this.log(`\\n${chalk.bold(\"Subscription Status:\")}`);\n this.log(` Status: ${formatStatus(result.subscriptionStatus)}`);\n this.log(` Product: ${result.productId}`);\n\n // Display billing period\n if (result.currentPeriodStart && result.currentPeriodEnd) {\n const startDate = new Date(result.currentPeriodStart).toLocaleDateString();\n const endDate = new Date(result.currentPeriodEnd).toLocaleDateString();\n this.log(` Current Period: ${startDate} - ${endDate}`);\n }\n\n // Display line items if available\n if (result.lineItems && result.lineItems.length > 0) {\n this.log(`\\n${chalk.bold(\" Line Items:\")}`);\n for (const item of result.lineItems) {\n const product = `${flags.product.charAt(0).toUpperCase()}${flags.product.slice(1)}`;\n const chain = item.description.toLowerCase().includes(\"sepolia\") ? \"Sepolia\" : \"Mainnet\";\n this.log(\n ` • ${product} (${chain}): $${item.subtotal.toFixed(2)} (${item.quantity} vCPU hours × $${item.price.toFixed(3)}/vCPU hour)`\n );\n }\n }\n\n // Display invoice summary with credits\n if (result.creditsApplied !== undefined && result.creditsApplied > 0) {\n this.log(`\\n${chalk.bold(\" Invoice Summary:\")}`);\n const subtotal = result.upcomingInvoiceSubtotal ?? result.upcomingInvoiceTotal ?? 0;\n this.log(` Subtotal: $${subtotal.toFixed(2)}`);\n this.log(` Credits Applied: ${chalk.green(`-$${result.creditsApplied.toFixed(2)}`)}`);\n this.log(` ${\"─\".repeat(21)}`);\n this.log(` Total Due: $${(result.upcomingInvoiceTotal ?? 0).toFixed(2)}`);\n\n if (result.remainingCredits !== undefined) {\n this.log(`\\n ${chalk.bold(\"Remaining Credits:\")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`);\n }\n } else if (result.upcomingInvoiceTotal !== undefined) {\n this.log(\n `\\n Upcoming Invoice: $${result.upcomingInvoiceTotal.toFixed(2)}`\n );\n if (result.remainingCredits !== undefined && result.remainingCredits > 0) {\n this.log(` ${chalk.bold(\"Available Credits:\")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`);\n }\n }\n\n // Display cancellation information\n if (result.cancelAtPeriodEnd) {\n this.log(\n `\\n ${chalk.yellow(\"⚠ Subscription will cancel at period end\")}`\n );\n }\n\n if (result.canceledAt) {\n const cancelDate = new Date(result.canceledAt).toLocaleDateString();\n this.log(` Canceled On: ${cancelDate}`);\n }\n\n // Display portal URL for management\n if (result.portalUrl) {\n this.log(\n `\\n ${chalk.dim(\"Manage subscription:\")} ${chalk.cyan(result.portalUrl)}`\n );\n }\n\n this.log();\n }\n}\n","import {\n createAppModule,\n createBillingModule,\n getPrivateKeyInteractive,\n getEnvironmentConfig,\n requirePrivateKey,\n getPrivateKeyWithSource,\n} from \"@layr-labs/ecloud-sdk\";\nimport { CommonFlags, validateCommonFlags } from \"./flags\";\nimport { Hex } from \"viem\";\n\nexport async function createAppClient(flags: CommonFlags) {\n flags = await validateCommonFlags(flags);\n\n const environment = flags.environment!;\n const environmentConfig = getEnvironmentConfig(environment);\n const rpcUrl = flags[\"rpc-url\"] || environmentConfig.defaultRPCURL;\n const { key: privateKey, source } = await requirePrivateKey({\n privateKey: flags[\"private-key\"],\n });\n\n if (flags.verbose) {\n console.log(`Using private key from: ${source}`);\n }\n\n return createAppModule({\n verbose: flags.verbose,\n privateKey,\n rpcUrl,\n environment,\n });\n}\n\nexport async function createBillingClient(flags: {\n \"private-key\"?: string;\n verbose?: boolean;\n}) {\n const result = await getPrivateKeyWithSource({\n privateKey: flags[\"private-key\"],\n });\n const privateKey = await getPrivateKeyInteractive(result?.key);\n\n return createBillingModule({\n verbose: flags.verbose ?? false,\n privateKey: privateKey as Hex,\n });\n}\n","import {\n getEnvironmentInteractive,\n getPrivateKeyInteractive,\n getAvailableEnvironments,\n} from \"@layr-labs/ecloud-sdk\";\nimport { Flags } from \"@oclif/core\";\n\nexport type CommonFlags = {\n verbose: boolean;\n environment?: string;\n \"private-key\"?: string;\n \"rpc-url\"?: string;\n};\n\n// Get available environments dynamically from SDK based on build type\nconst getEnvironmentOptions = (): string[] => {\n try {\n return getAvailableEnvironments();\n } catch {\n // Fallback to all environments if SDK not available\n return [\"sepolia\", \"sepolia-dev\", \"mainnet-alpha\"];\n }\n};\n\nexport const commonFlags = {\n environment: Flags.string({\n required: false,\n description: \"Deployment environment to use\",\n options: getEnvironmentOptions(),\n env: \"ECLOUD_ENV\",\n }),\n \"private-key\": Flags.string({\n required: false,\n description: \"Private key for signing transactions\",\n env: \"ECLOUD_PRIVATE_KEY\",\n }),\n \"rpc-url\": Flags.string({\n required: false,\n description: \"RPC URL to connect to blockchain\",\n env: \"ECLOUD_RPC_URL\",\n }),\n verbose: Flags.boolean({\n required: false,\n description: \"Enable verbose logging (default: false)\",\n default: false,\n }),\n};\n\n// Validate or prompt for required common flags\nexport async function validateCommonFlags(flags: CommonFlags) {\n flags[\"environment\"] = await getEnvironmentInteractive(flags[\"environment\"]);\n flags[\"private-key\"] = await getPrivateKeyInteractive(flags[\"private-key\"]);\n\n return flags;\n}\n"],"mappings":"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","names":["Flags","getPrivateKeyInteractive","getPrivateKeyInteractive","Flags"]}
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/billing/subscribe.ts
4
+ import { Command, Flags as Flags2 } from "@oclif/core";
5
+ import { isSubscriptionActive } from "@layr-labs/ecloud-sdk";
6
+
7
+ // src/client.ts
8
+ import {
9
+ createAppModule,
10
+ createBillingModule,
11
+ getPrivateKeyInteractive as getPrivateKeyInteractive2,
12
+ getEnvironmentConfig,
13
+ requirePrivateKey,
14
+ getPrivateKeyWithSource
15
+ } from "@layr-labs/ecloud-sdk";
16
+
17
+ // src/flags.ts
18
+ import {
19
+ getEnvironmentInteractive,
20
+ getPrivateKeyInteractive,
21
+ getAvailableEnvironments
22
+ } from "@layr-labs/ecloud-sdk";
23
+ import { Flags } from "@oclif/core";
24
+ var getEnvironmentOptions = () => {
25
+ try {
26
+ return getAvailableEnvironments();
27
+ } catch {
28
+ return ["sepolia", "sepolia-dev", "mainnet-alpha"];
29
+ }
30
+ };
31
+ var commonFlags = {
32
+ environment: Flags.string({
33
+ required: false,
34
+ description: "Deployment environment to use",
35
+ options: getEnvironmentOptions(),
36
+ env: "ECLOUD_ENV"
37
+ }),
38
+ "private-key": Flags.string({
39
+ required: false,
40
+ description: "Private key for signing transactions",
41
+ env: "ECLOUD_PRIVATE_KEY"
42
+ }),
43
+ "rpc-url": Flags.string({
44
+ required: false,
45
+ description: "RPC URL to connect to blockchain",
46
+ env: "ECLOUD_RPC_URL"
47
+ }),
48
+ verbose: Flags.boolean({
49
+ required: false,
50
+ description: "Enable verbose logging (default: false)",
51
+ default: false
52
+ })
53
+ };
54
+
55
+ // src/client.ts
56
+ async function createBillingClient(flags) {
57
+ const result = await getPrivateKeyWithSource({
58
+ privateKey: flags["private-key"]
59
+ });
60
+ const privateKey = await getPrivateKeyInteractive2(result?.key);
61
+ return createBillingModule({
62
+ verbose: flags.verbose ?? false,
63
+ privateKey
64
+ });
65
+ }
66
+
67
+ // src/commands/billing/subscribe.ts
68
+ import chalk from "chalk";
69
+ import open from "open";
70
+ var PAYMENT_TIMEOUT_MS = 5 * 60 * 1e3;
71
+ var POLL_INTERVAL_MS = 3e3;
72
+ var BillingSubscribe = class _BillingSubscribe extends Command {
73
+ static description = "Create subscription to start deploying apps";
74
+ static flags = {
75
+ "private-key": commonFlags["private-key"],
76
+ verbose: commonFlags.verbose,
77
+ product: Flags2.string({
78
+ required: false,
79
+ description: "Product ID",
80
+ default: "compute",
81
+ options: ["compute"],
82
+ env: "ECLOUD_PRODUCT_ID"
83
+ })
84
+ };
85
+ async run() {
86
+ const { flags } = await this.parse(_BillingSubscribe);
87
+ const billing = await createBillingClient(flags);
88
+ this.debug(`
89
+ Checking subscription status for ${flags.product}...`);
90
+ const result = await billing.subscribe({
91
+ productId: flags.product
92
+ });
93
+ if (result.type === "already_active") {
94
+ this.log(
95
+ `
96
+ ${chalk.green("\u2713")} You're already subscribed to ${flags.product}.`
97
+ );
98
+ this.log(chalk.gray("Run 'ecloud billing status' for details."));
99
+ return;
100
+ }
101
+ if (result.type === "payment_issue") {
102
+ this.log(
103
+ `
104
+ ${chalk.yellow("\u26A0")} You already have a subscription on ${flags.product}, but it has a payment issue.`
105
+ );
106
+ this.log("Please update your payment method to restore access.");
107
+ if (result.portalUrl) {
108
+ this.log(`
109
+ ${chalk.bold("Update payment method:")}`);
110
+ this.log(` ${result.portalUrl}`);
111
+ }
112
+ return;
113
+ }
114
+ this.log(`
115
+ ${chalk.gray("Opening checkout in your browser...")}`);
116
+ this.log(chalk.gray(`
117
+ URL: ${result.checkoutUrl}`));
118
+ await open(result.checkoutUrl);
119
+ this.log(`
120
+ ${chalk.gray("Waiting for payment confirmation...")}`);
121
+ const startTime = Date.now();
122
+ while (Date.now() - startTime < PAYMENT_TIMEOUT_MS) {
123
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
124
+ try {
125
+ const status = await billing.getStatus({
126
+ productId: flags.product
127
+ });
128
+ if (isSubscriptionActive(status.subscriptionStatus)) {
129
+ this.log(
130
+ `
131
+ ${chalk.green("\u2713")} Subscription activated successfully for ${flags.product}!`
132
+ );
133
+ this.log(
134
+ `
135
+ ${chalk.gray("Start deploying with:")} ecloud app deploy`
136
+ );
137
+ return;
138
+ }
139
+ } catch (error) {
140
+ this.debug(`Error polling for subscription status: ${error}`);
141
+ }
142
+ }
143
+ this.log(
144
+ `
145
+ ${chalk.yellow("\u26A0")} Payment confirmation timed out after 5 minutes.`
146
+ );
147
+ this.log(
148
+ chalk.gray(
149
+ `If you completed payment, run 'ecloud billing status' to check status.`
150
+ )
151
+ );
152
+ }
153
+ };
154
+ export {
155
+ BillingSubscribe as default
156
+ };
157
+ //# sourceMappingURL=subscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/billing/subscribe.ts","../../../src/client.ts","../../../src/flags.ts"],"sourcesContent":["import { Command, Flags } from \"@oclif/core\";\nimport { isSubscriptionActive } from \"@layr-labs/ecloud-sdk\";\nimport { createBillingClient } from \"../../client\";\nimport { commonFlags } from \"../../flags\";\nimport chalk from \"chalk\";\nimport open from \"open\";\n\nconst PAYMENT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\nconst POLL_INTERVAL_MS = 3_000; // 3 seconds\n\nexport default class BillingSubscribe extends Command {\n static description = \"Create subscription to start deploying apps\";\n\n static flags = {\n \"private-key\": commonFlags[\"private-key\"],\n verbose: commonFlags.verbose,\n product: Flags.string({\n required: false,\n description: \"Product ID\",\n default: \"compute\",\n options: [\"compute\"],\n env: \"ECLOUD_PRODUCT_ID\",\n }),\n };\n\n async run() {\n const { flags } = await this.parse(BillingSubscribe);\n const billing = await createBillingClient(flags);\n\n this.debug(`\\nChecking subscription status for ${flags.product}...`);\n\n const result = await billing.subscribe({\n productId: flags.product as \"compute\",\n });\n\n // Handle already active subscription\n if (result.type === \"already_active\") {\n this.log(\n `\\n${chalk.green(\"✓\")} You're already subscribed to ${flags.product}.`,\n );\n this.log(chalk.gray(\"Run 'ecloud billing status' for details.\"));\n return;\n }\n\n // Handle payment issue\n if (result.type === \"payment_issue\") {\n this.log(\n `\\n${chalk.yellow(\"⚠\")} You already have a subscription on ${flags.product}, but it has a payment issue.`,\n );\n this.log(\"Please update your payment method to restore access.\");\n\n if (result.portalUrl) {\n this.log(`\\n${chalk.bold(\"Update payment method:\")}`);\n this.log(` ${result.portalUrl}`);\n }\n return;\n }\n\n // Open checkout URL in browser\n this.log(`\\n${chalk.gray(\"Opening checkout in your browser...\")}`);\n this.log(chalk.gray(`\\nURL: ${result.checkoutUrl}`));\n await open(result.checkoutUrl);\n\n // Poll for subscription status\n this.log(`\\n${chalk.gray(\"Waiting for payment confirmation...\")}`);\n\n const startTime = Date.now();\n\n while (Date.now() - startTime < PAYMENT_TIMEOUT_MS) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n\n try {\n const status = await billing.getStatus({\n productId: flags.product as \"compute\",\n });\n\n // Check if subscription is now active or trialing\n if (isSubscriptionActive(status.subscriptionStatus)) {\n this.log(\n `\\n${chalk.green(\"✓\")} Subscription activated successfully for ${flags.product}!`,\n );\n this.log(\n `\\n${chalk.gray(\"Start deploying with:\")} ecloud app deploy`,\n );\n return;\n }\n } catch (error) {\n this.debug(`Error polling for subscription status: ${error}`);\n }\n }\n\n // Timeout reached\n this.log(\n `\\n${chalk.yellow(\"⚠\")} Payment confirmation timed out after 5 minutes.`,\n );\n this.log(\n chalk.gray(\n `If you completed payment, run 'ecloud billing status' to check status.`,\n ),\n );\n }\n}\n","import {\n createAppModule,\n createBillingModule,\n getPrivateKeyInteractive,\n getEnvironmentConfig,\n requirePrivateKey,\n getPrivateKeyWithSource,\n} from \"@layr-labs/ecloud-sdk\";\nimport { CommonFlags, validateCommonFlags } from \"./flags\";\nimport { Hex } from \"viem\";\n\nexport async function createAppClient(flags: CommonFlags) {\n flags = await validateCommonFlags(flags);\n\n const environment = flags.environment!;\n const environmentConfig = getEnvironmentConfig(environment);\n const rpcUrl = flags[\"rpc-url\"] || environmentConfig.defaultRPCURL;\n const { key: privateKey, source } = await requirePrivateKey({\n privateKey: flags[\"private-key\"],\n });\n\n if (flags.verbose) {\n console.log(`Using private key from: ${source}`);\n }\n\n return createAppModule({\n verbose: flags.verbose,\n privateKey,\n rpcUrl,\n environment,\n });\n}\n\nexport async function createBillingClient(flags: {\n \"private-key\"?: string;\n verbose?: boolean;\n}) {\n const result = await getPrivateKeyWithSource({\n privateKey: flags[\"private-key\"],\n });\n const privateKey = await getPrivateKeyInteractive(result?.key);\n\n return createBillingModule({\n verbose: flags.verbose ?? false,\n privateKey: privateKey as Hex,\n });\n}\n","import {\n getEnvironmentInteractive,\n getPrivateKeyInteractive,\n getAvailableEnvironments,\n} from \"@layr-labs/ecloud-sdk\";\nimport { Flags } from \"@oclif/core\";\n\nexport type CommonFlags = {\n verbose: boolean;\n environment?: string;\n \"private-key\"?: string;\n \"rpc-url\"?: string;\n};\n\n// Get available environments dynamically from SDK based on build type\nconst getEnvironmentOptions = (): string[] => {\n try {\n return getAvailableEnvironments();\n } catch {\n // Fallback to all environments if SDK not available\n return [\"sepolia\", \"sepolia-dev\", \"mainnet-alpha\"];\n }\n};\n\nexport const commonFlags = {\n environment: Flags.string({\n required: false,\n description: \"Deployment environment to use\",\n options: getEnvironmentOptions(),\n env: \"ECLOUD_ENV\",\n }),\n \"private-key\": Flags.string({\n required: false,\n description: \"Private key for signing transactions\",\n env: \"ECLOUD_PRIVATE_KEY\",\n }),\n \"rpc-url\": Flags.string({\n required: false,\n description: \"RPC URL to connect to blockchain\",\n env: \"ECLOUD_RPC_URL\",\n }),\n verbose: Flags.boolean({\n required: false,\n description: \"Enable verbose logging (default: false)\",\n default: false,\n }),\n};\n\n// Validate or prompt for required common flags\nexport async function validateCommonFlags(flags: CommonFlags) {\n flags[\"environment\"] = await getEnvironmentInteractive(flags[\"environment\"]);\n flags[\"private-key\"] = await getPrivateKeyInteractive(flags[\"private-key\"]);\n\n return flags;\n}\n"],"mappings":"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","names":["Flags","getPrivateKeyInteractive","getPrivateKeyInteractive","Flags"]}
@@ -0,0 +1,14 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0kHU86k17ofCIGcJKDcf
3
+ AFurFhSLeWmOL0bwWLCeVnTPG0MMHtJOq+woE0XXSWw6lzm+jzavBBTwKde1dgal
4
+ Ap91vULAZFMUpiUdd2dNUVtvU89qW0Pgf1Eu5FDj7BkY/SnyECbWJM4ga0BmpiGy
5
+ nQwLNN9mMGhjVoVLn2zwEGZ7JzS9Nz11EZKO/k/9DcO6LaoIFmKuvVf3jl6lvZg8
6
+ aeA0LoZXjkycHlRUt/kfKwZnhakUaYHP1ksV7ZNmolS5GYDTSKGB2KPPNR1s4/Xu
7
+ u8zeEFC8HuGRU8XuuBeaAunitnGhbNVREUNJGff6HZOGB6CIFNXjbQETeZ3p5uro
8
+ 0v+hd1QqQYBv7+DEaMCmGnJNGAyIMr2mn4vr7wGsIj0HonlSHmQ8rmdUhL2ocNTc
9
+ LhKgZiZmBuDpSbFW/r53R2G7CHcqaqGeUBnT54QCH4zsYKw0/4dOtwFxQpTyBf9/
10
+ +k+KaWEJYKkx9d9OzKGyAvzrTDVOFoajddiJ6LPvRlMdOUQr3hl4IAC0/nh9lhHq
11
+ D0R+i5WAU96TkdAe7B7iTGH2D22k0KUPR6Q9W3aF353SLxQAMPNrgG4QQufAdRJn
12
+ AF+8ntun5TkTqjTWRSwAsUJZ1z4wb96DympWJbDi0OciJRZ3Fz3j9+amC43yCHGg
13
+ aaEMjdt35ewbztUSc04F10MCAwEAAQ==
14
+ -----END PUBLIC KEY-----
@@ -0,0 +1,4 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfxbhXJjH4D0DH/iW5/rK1HzWS+f9
3
+ EyooZTrCYjCfezuOEmRuOWNaZLvwXN8SdzrvjWA7gSvOS85hLzp4grANRQ==
4
+ -----END PUBLIC KEY-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr/vqttU6aXX35HtsXavU
3
+ 5teysunDzZB3HyaFM4qcuRnqj+70KxqLOwZsERN5SwZ/56Jm8T2ds1CcXsQCMUMw
4
+ +MPlsF6KMGfzghLtYHONwvKLnn+U9y886aAay6W8a0A7O7YCZehNYD3kQnCXjOIc
5
+ Mj6v8AEvMw+w/lNabjRXnwSBMKVIGp/cSL0hGwt8fGoC3TsxQN9opzvU1Z4rAw9K
6
+ a119l6dlPnqezDva378TCaXDjqKe/jSZOI1CcYpaSK2SJ+95Wbvte5j3lXbg1oT2
7
+ 0rXeJUHEJ68QxMtJplfw0Sg+Ek4CUJ2c/kbdg0u7sIIO5wcB4WHL/Lfbw2XPmcBI
8
+ t0r0EC575D3iHF/aI01Ms2IRA0GDeHnNcr5FJLWJljTjNLEt4tFITrXwBe1Ealm3
9
+ NCxamApl5bBSwQ72Gb5fiQFwB8Fl2/XG3wfGTFInFEvWE4c/H8dtu1wHTsyEFZcG
10
+ B47IkD5GBSZq90Hd9xuZva55dxGpqUVrEJO88SqHGP9Oa+HLTYdEe5AR5Hitw4Mu
11
+ dk1cCH+X5OqY9dfpdoCNbKAM0N2SJvNAnDTU2JKGYheXrnDslXR6atBmU5gDkH+W
12
+ QVryDYl9xbwWIACMQsAQjrrtKw5xqJ4V89+06FN/wyEVF7KWAcJ4AhKiVnCvLqzb
13
+ BbISc+gOkRsefhCDJVPEKDkCAwEAAQ==
14
+ -----END PUBLIC KEY-----
@@ -0,0 +1,4 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb2Q88/cxdic2xi4jS2V0dtYHjLwq
3
+ 4wVFBFmaY8TTXoMXNggKEdU6PuE8EovocVKMpw3SIlaM27z9uxksNVL2xw==
4
+ -----END PUBLIC KEY-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApDvk8pAivkgtiC5li5MP
3
+ xMTJDduTeorBl18ynrooTxp2BwwgPwXfXbJaCA0qRubvc0aO2uh2VDrPM27CqMLH
4
+ o2S9YLtpLii4A1Nl7SE/MdWKWdG6v94xNGpc2YyPP7yWtHfqOkgDWp8sokl3Uq/9
5
+ MS0pjUaI7RyS5boCTy8Qw90BxGMpucjOmqm+luw4EdPWZCrgriUR2bbGRRgAmrT1
6
+ K4ou4IgPp799r120hwHbCWxnOvLdQdpiv2507b900xS/3yZahhnHCAn66146LU/f
7
+ BrRpQKSM0qSpktXrrc9MH/ru2VLR5cGLp89ZcZMQA9cRGglWM5XWVY3Ti2TPJ6Kd
8
+ An1d7qNkGJaSdVa3x3HkOf6c6HeTyqis5/L/6L+PFhUsTRbmKg1FtwD+3xxdyf7h
9
+ abFxryE9rv+WatHL6r6z5ztV0znJ/Fpfs5A45FWA6pfb28fA59RGpi/DQ8RxgdCH
10
+ nZRNvdz8dTgRaXSPgkfGXBcCFqb/QhFmad7XbWDthGzfhbPOxNPtiaGRQ1Dr/Pgq
11
+ n0ugdLbRQLmDOAFgaQcnr0U4y1TUlWJnvoZMETkVN7gmITtXA4F324ALT7Rd+Lgk
12
+ HikW5vG+NjAEwXfPsK0YzT+VbHd7o1lbru9UxiDlN03XVEkz/oRQi47CvSTo3FSr
13
+ 5dB4lz8kov3UUcNJfQFZolMCAwEAAQ==
14
+ -----END PUBLIC KEY-----
@@ -0,0 +1,4 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsk6ZdmmvBqFfKHs+1cYjIemRGN7h
3
+ 1NatIEitFRyx+3q8wmTJ9LknTE1FwWBLcCNTseJDti8Rh+SaVxfGOyJuuA==
4
+ -----END PUBLIC KEY-----
@@ -0,0 +1,58 @@
1
+ {{#if includeTLS}}
2
+ # Get Caddy from official image
3
+ FROM caddy:2.10.2-alpine AS caddy
4
+ {{/if}}
5
+
6
+ FROM {{baseImage}}
7
+
8
+ {{#if originalUser}}
9
+ # Switch to root to perform setup (base image has non-root USER: {{originalUser}})
10
+ USER root
11
+ {{/if}}
12
+
13
+ # Copy core TEE components
14
+ COPY compute-source-env.sh /usr/local/bin/
15
+ COPY kms-client /usr/local/bin/
16
+ COPY kms-signing-public-key.pem /usr/local/bin/
17
+
18
+ {{#if includeTLS}}
19
+ # Copy Caddy from official image
20
+ COPY --from=caddy /usr/bin/caddy /usr/local/bin/caddy
21
+
22
+ # Copy TLS components
23
+ COPY tls-keygen /usr/local/bin/
24
+ COPY Caddyfile /etc/caddy/
25
+ {{/if}}
26
+
27
+ {{#if originalUser}}
28
+ # Make binaries executable (755 for executables, 644 for keys)
29
+ RUN chmod 755 /usr/local/bin/compute-source-env.sh \
30
+ && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \
31
+ && chmod 755 /usr/local/bin/tls-keygen \
32
+ && chmod 755 /usr/local/bin/caddy{{/if}} \
33
+ && chmod 644 /usr/local/bin/kms-signing-public-key.pem
34
+
35
+ # Switch back to the original user from base image
36
+ USER {{originalUser}}
37
+ {{else}}
38
+ # Make binaries executable (preserve existing permissions, just add execute)
39
+ RUN chmod +x /usr/local/bin/compute-source-env.sh \
40
+ && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \
41
+ && chmod +x /usr/local/bin/tls-keygen{{/if}}
42
+ {{/if}}
43
+
44
+ {{#if logRedirect}}
45
+
46
+ LABEL tee.launch_policy.log_redirect={{logRedirect}}
47
+ {{/if}}
48
+
49
+ LABEL eigenx_cli_version={{ecloudCLIVersion}}
50
+ LABEL eigenx_use_ita=True
51
+
52
+ {{#if includeTLS}}
53
+ # Expose both HTTP and HTTPS ports for Caddy
54
+ EXPOSE 80 443
55
+ {{/if}}
56
+
57
+ ENTRYPOINT ["/usr/local/bin/compute-source-env.sh"]
58
+ CMD {{{originalCmd}}}
@@ -0,0 +1,110 @@
1
+ #!/bin/sh
2
+ echo "compute-source-env.sh: Running setup script..."
3
+
4
+ # Fetch and source environment variables from KMS
5
+ echo "Fetching secrets from KMS..."
6
+ if /usr/local/bin/kms-client \
7
+ --kms-server-url "{{kmsServerURL}}" \
8
+ --kms-signing-key-file /usr/local/bin/kms-signing-public-key.pem \
9
+ --userapi-url "{{userAPIURL}}" \
10
+ --output /tmp/.env; then
11
+ echo "compute-source-env.sh: Successfully fetched environment variables from KMS"
12
+ set -a && . /tmp/.env && set +a
13
+ rm -f /tmp/.env
14
+ else
15
+ echo "compute-source-env.sh: ERROR - Failed to fetch environment variables from KMS"
16
+ echo "compute-source-env.sh: Exiting - cannot start user workload without KMS secrets"
17
+ exit 1
18
+ fi
19
+
20
+ # Setup TLS if tls-keygen is present (which means TLS was configured at build time)
21
+ setup_tls() {
22
+ # If tls-keygen isn't present, TLS wasn't configured during build
23
+ if [ ! -x /usr/local/bin/tls-keygen ]; then
24
+ echo "compute-source-env.sh: TLS not configured (no tls-keygen binary)"
25
+ return 0
26
+ fi
27
+
28
+ local domain="${DOMAIN:-}"
29
+ local mnemonic="${MNEMONIC:-}"
30
+
31
+ # Since tls-keygen is present, TLS is expected - validate requirements
32
+ if [ -z "$domain" ] || [ "$domain" = "localhost" ]; then
33
+ echo "compute-source-env.sh: ERROR - TLS binary present but DOMAIN not configured or is localhost"
34
+ echo "compute-source-env.sh: Set DOMAIN environment variable to a valid domain"
35
+ exit 1
36
+ fi
37
+
38
+ if [ -z "$mnemonic" ]; then
39
+ echo "compute-source-env.sh: ERROR - TLS binary present but MNEMONIC not available"
40
+ echo "compute-source-env.sh: Cannot obtain TLS certificate without mnemonic"
41
+ exit 1
42
+ fi
43
+
44
+ if [ ! -x /usr/local/bin/caddy ]; then
45
+ echo "compute-source-env.sh: ERROR - TLS binary present but Caddy not found"
46
+ exit 1
47
+ fi
48
+
49
+ echo "compute-source-env.sh: Setting up TLS for domain: $domain"
50
+
51
+ # Obtain TLS certificate using ACME
52
+ # Default to http-01, but allow override via ACME_CHALLENGE env var
53
+ local challenge="${ACME_CHALLENGE:-http-01}"
54
+
55
+ # Check if we should use staging (for testing)
56
+ local staging_flag=""
57
+ if [ "${ACME_STAGING:-false}" = "true" ]; then
58
+ staging_flag="-staging"
59
+ echo "compute-source-env.sh: Using Let's Encrypt STAGING environment (certificates won't be trusted)"
60
+ fi
61
+
62
+ echo "compute-source-env.sh: Obtaining TLS certificate using $challenge challenge..."
63
+ # Pass the API URL for certificate persistence
64
+ if ! MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \
65
+ -challenge "$challenge" \
66
+ $staging_flag; then
67
+ echo "compute-source-env.sh: ERROR - Failed to obtain TLS certificate"
68
+ echo "compute-source-env.sh: Certificate issuance failed for $domain"
69
+ exit 1
70
+ fi
71
+
72
+ echo "compute-source-env.sh: TLS certificate obtained successfully"
73
+
74
+ # Validate Caddyfile before starting
75
+ if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null; then
76
+ echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
77
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
78
+ exit 1
79
+ fi
80
+
81
+ # Start Caddy in background
82
+ echo "compute-source-env.sh: Starting Caddy reverse proxy..."
83
+
84
+ # Check if Caddy logs should be enabled
85
+ if [ "${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
86
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
87
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
88
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
89
+ exit 1
90
+ fi
91
+ else
92
+ # Redirect Caddy output to /dev/null to silence logs
93
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
94
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
95
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
96
+ exit 1
97
+ fi
98
+ fi
99
+
100
+ # Give Caddy a moment to fully initialize
101
+ sleep 2
102
+ echo "compute-source-env.sh: Caddy started successfully"
103
+ return 0
104
+ }
105
+
106
+ # Run TLS setup
107
+ setup_tls
108
+
109
+ echo "compute-source-env.sh: Environment sourced."
110
+ exec "$@"
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@layr-labs/ecloud-cli",
3
+ "version": "0.0.1-dev-rfc.1",
4
+ "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "bin",
8
+ "README.md"
9
+ ],
10
+ "bin": {
11
+ "ecloud": "./bin/run.js"
12
+ },
13
+ "dependencies": {
14
+ "@inquirer/prompts": "^7.10.1",
15
+ "@layr-labs/ecloud-sdk": "0.0.1-dev-rfc.1",
16
+ "@napi-rs/keyring": "^1.0.5",
17
+ "@oclif/core": "^4.8.0",
18
+ "axios": "^1.13.2",
19
+ "chalk": "^5.6.2",
20
+ "dockerode": "^4.0.9",
21
+ "form-data": "^4.0.5",
22
+ "handlebars": "^4.7.8",
23
+ "jose": "^6.1.2",
24
+ "js-yaml": "^4.1.1",
25
+ "node-forge": "^1.3.1",
26
+ "open": "^11.0.0",
27
+ "viem": "^2.38.6"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup && npm run build:copy-templates",
31
+ "build:copy-templates": "node scripts/copy-templates.js",
32
+ "lint": "eslint .",
33
+ "format": "prettier --check .",
34
+ "format:fix": "prettier --write .",
35
+ "ecloud": "node bin/run.js"
36
+ },
37
+ "oclif": {
38
+ "bin": "ecloud",
39
+ "commands": "./dist/commands",
40
+ "dirname": "",
41
+ "topicSeparator": " ",
42
+ "topics": {
43
+ "billing": {
44
+ "description": "Manage billing and subscriptions"
45
+ }
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "@types/form-data": "^2.5.2",
50
+ "@types/node": "^18",
51
+ "ts-node": "^10.9.2"
52
+ }
53
+ }