@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.
- package/README.md +195 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/app/create.js +29 -0
- package/dist/commands/app/create.js.map +1 -0
- package/dist/commands/app/deploy.js +142 -0
- package/dist/commands/app/deploy.js.map +1 -0
- package/dist/commands/app/logs.js +108 -0
- package/dist/commands/app/logs.js.map +1 -0
- package/dist/commands/app/start.js +121 -0
- package/dist/commands/app/start.js.map +1 -0
- package/dist/commands/app/stop.js +121 -0
- package/dist/commands/app/stop.js.map +1 -0
- package/dist/commands/app/terminate.js +128 -0
- package/dist/commands/app/terminate.js.map +1 -0
- package/dist/commands/app/upgrade.js +142 -0
- package/dist/commands/app/upgrade.js.map +1 -0
- package/dist/commands/auth/generate.js +101 -0
- package/dist/commands/auth/generate.js.map +1 -0
- package/dist/commands/auth/login.js +150 -0
- package/dist/commands/auth/login.js.map +1 -0
- package/dist/commands/auth/logout.js +64 -0
- package/dist/commands/auth/logout.js.map +1 -0
- package/dist/commands/auth/migrate.js +129 -0
- package/dist/commands/auth/migrate.js.map +1 -0
- package/dist/commands/auth/whoami.js +87 -0
- package/dist/commands/auth/whoami.js.map +1 -0
- package/dist/commands/billing/cancel.js +132 -0
- package/dist/commands/billing/cancel.js.map +1 -0
- package/dist/commands/billing/status.js +175 -0
- package/dist/commands/billing/status.js.map +1 -0
- package/dist/commands/billing/subscribe.js +157 -0
- package/dist/commands/billing/subscribe.js.map +1 -0
- package/dist/keys/mainnet-alpha/prod/kms-encryption-public-key.pem +14 -0
- package/dist/keys/mainnet-alpha/prod/kms-signing-public-key.pem +4 -0
- package/dist/keys/sepolia/dev/kms-encryption-public-key.pem +14 -0
- package/dist/keys/sepolia/dev/kms-signing-public-key.pem +4 -0
- package/dist/keys/sepolia/prod/kms-encryption-public-key.pem +14 -0
- package/dist/keys/sepolia/prod/kms-signing-public-key.pem +4 -0
- package/dist/templates/Dockerfile.layered.tmpl +58 -0
- package/dist/templates/compute-source-env.sh.tmpl +110 -0
- 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,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,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,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
|
+
}
|