@layr-labs/ecloud-cli 1.0.0-devep1 → 1.0.0-devep2

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 (31) hide show
  1. package/VERSION +2 -2
  2. package/dist/commands/billing/__tests__/status.test.js +4 -23
  3. package/dist/commands/billing/__tests__/status.test.js.map +1 -1
  4. package/dist/commands/billing/__tests__/top-up.test.js +273 -89
  5. package/dist/commands/billing/__tests__/top-up.test.js.map +1 -1
  6. package/dist/commands/billing/list-cards.js +409 -0
  7. package/dist/commands/billing/list-cards.js.map +1 -0
  8. package/dist/commands/billing/status.js +4 -23
  9. package/dist/commands/billing/status.js.map +1 -1
  10. package/dist/commands/billing/top-up.js +151 -79
  11. package/dist/commands/billing/top-up.js.map +1 -1
  12. package/dist/commands/compute/app/deploy.js +1 -1
  13. package/dist/commands/compute/app/info.js +1 -1
  14. package/dist/commands/compute/app/list.js +1 -1
  15. package/dist/commands/compute/app/logs.js +1 -1
  16. package/dist/commands/compute/app/profile/set.js +1 -1
  17. package/dist/commands/compute/app/releases.js +1 -1
  18. package/dist/commands/compute/app/start.js +1 -1
  19. package/dist/commands/compute/app/stop.js +1 -1
  20. package/dist/commands/compute/app/terminate.js +1 -1
  21. package/dist/commands/compute/app/upgrade.js +1 -1
  22. package/dist/commands/compute/build/info.js +1 -1
  23. package/dist/commands/compute/build/list.js +1 -1
  24. package/dist/commands/compute/build/logs.js +1 -1
  25. package/dist/commands/compute/build/status.js +1 -1
  26. package/dist/commands/compute/build/submit.js +1 -1
  27. package/dist/commands/compute/build/verify.js +1 -1
  28. package/dist/commands/compute/undelegate.js +1 -1
  29. package/dist/hooks/init/__tests__/version-check.test.js +1 -1
  30. package/dist/hooks/init/version-check.js +1 -1
  31. package/package.json +2 -2
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/billing/list-cards.ts
4
+ import { Command } from "@oclif/core";
5
+
6
+ // src/client.ts
7
+ import {
8
+ createComputeModule,
9
+ createBillingModule,
10
+ createBuildModule,
11
+ getEnvironmentConfig as getEnvironmentConfig3,
12
+ requirePrivateKey
13
+ } from "@layr-labs/ecloud-sdk";
14
+
15
+ // src/flags.ts
16
+ import { Flags } from "@oclif/core";
17
+ import { getBuildType as getBuildType2 } from "@layr-labs/ecloud-sdk";
18
+
19
+ // src/utils/prompts.ts
20
+ import { input, select, password, confirm as inquirerConfirm } from "@inquirer/prompts";
21
+ import chalk from "chalk";
22
+ import fs3 from "fs";
23
+ import path3 from "path";
24
+ import os3 from "os";
25
+ import { isAddress as isAddress2 } from "viem";
26
+ import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
27
+ import {
28
+ getEnvironmentConfig as getEnvironmentConfig2,
29
+ getAvailableEnvironments,
30
+ getAllAppsByDeveloper as getAllAppsByDeveloper2,
31
+ getCategoryDescriptions,
32
+ fetchTemplateCatalog,
33
+ PRIMARY_LANGUAGES,
34
+ validateAppName,
35
+ validateImageReference,
36
+ validateFilePath,
37
+ validatePrivateKeyFormat,
38
+ extractAppNameFromImage,
39
+ UserApiClient as UserApiClient2
40
+ } from "@layr-labs/ecloud-sdk";
41
+
42
+ // src/utils/appResolver.ts
43
+ import { isAddress } from "viem";
44
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
45
+ import {
46
+ UserApiClient,
47
+ getAllAppsByDeveloper
48
+ } from "@layr-labs/ecloud-sdk";
49
+
50
+ // src/utils/viemClients.ts
51
+ import {
52
+ createPublicClient,
53
+ http
54
+ } from "viem";
55
+ import { privateKeyToAccount } from "viem/accounts";
56
+ import {
57
+ getEnvironmentConfig,
58
+ addHexPrefix,
59
+ createViemClients as sdkCreateViemClients,
60
+ getChainFromID
61
+ } from "@layr-labs/ecloud-sdk";
62
+ function createViemClients(options) {
63
+ const privateKey = addHexPrefix(options.privateKey);
64
+ const environmentConfig = getEnvironmentConfig(options.environment);
65
+ const rpcUrl = options.rpcUrl || environmentConfig.defaultRPCURL;
66
+ const chain = getChainFromID(environmentConfig.chainID);
67
+ const { publicClient, walletClient } = sdkCreateViemClients({
68
+ privateKey,
69
+ rpcUrl,
70
+ chainId: environmentConfig.chainID
71
+ });
72
+ const account = privateKeyToAccount(privateKey);
73
+ return {
74
+ publicClient,
75
+ walletClient,
76
+ chain,
77
+ address: account.address
78
+ };
79
+ }
80
+
81
+ // src/utils/globalConfig.ts
82
+ import * as fs from "fs";
83
+ import * as path from "path";
84
+ import * as os from "os";
85
+ import { load as loadYaml, dump as dumpYaml } from "js-yaml";
86
+ import { getBuildType } from "@layr-labs/ecloud-sdk";
87
+ import * as crypto from "crypto";
88
+ var GLOBAL_CONFIG_FILE = "config.yaml";
89
+ var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
90
+ function getGlobalConfigDir() {
91
+ const configHome = process.env.XDG_CONFIG_HOME;
92
+ let baseDir;
93
+ if (configHome && path.isAbsolute(configHome)) {
94
+ baseDir = configHome;
95
+ } else {
96
+ baseDir = path.join(os.homedir(), ".config");
97
+ }
98
+ const buildType = getBuildType();
99
+ const buildSuffix = buildType === "dev" ? "-dev" : "";
100
+ const configDirName = `ecloud${buildSuffix}`;
101
+ return path.join(baseDir, configDirName);
102
+ }
103
+ function getGlobalConfigPath() {
104
+ return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);
105
+ }
106
+ function loadGlobalConfig() {
107
+ const configPath = getGlobalConfigPath();
108
+ if (!fs.existsSync(configPath)) {
109
+ return {
110
+ first_run: true
111
+ };
112
+ }
113
+ try {
114
+ const content = fs.readFileSync(configPath, "utf-8");
115
+ const config = loadYaml(content);
116
+ return config || { first_run: true };
117
+ } catch {
118
+ return {
119
+ first_run: true
120
+ };
121
+ }
122
+ }
123
+ function saveGlobalConfig(config) {
124
+ const configPath = getGlobalConfigPath();
125
+ const configDir = path.dirname(configPath);
126
+ fs.mkdirSync(configDir, { recursive: true, mode: 493 });
127
+ const content = dumpYaml(config, { lineWidth: -1 });
128
+ fs.writeFileSync(configPath, content, { mode: 420 });
129
+ }
130
+ function getDefaultEnvironment() {
131
+ const config = loadGlobalConfig();
132
+ return config.default_environment;
133
+ }
134
+ function getGlobalTelemetryPreference() {
135
+ const config = loadGlobalConfig();
136
+ return config.telemetry_enabled;
137
+ }
138
+ function getOrCreateUserUUID() {
139
+ const config = loadGlobalConfig();
140
+ if (config.user_uuid) {
141
+ return config.user_uuid;
142
+ }
143
+ const uuid = generateUUID();
144
+ config.user_uuid = uuid;
145
+ config.first_run = false;
146
+ saveGlobalConfig(config);
147
+ return uuid;
148
+ }
149
+ function generateUUID() {
150
+ const bytes = crypto.randomBytes(16);
151
+ bytes[6] = bytes[6] & 15 | 64;
152
+ bytes[8] = bytes[8] & 63 | 128;
153
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
154
+ return hex.slice(0, 4).join("") + hex.slice(4, 6).join("") + "-" + hex.slice(6, 8).join("") + "-" + hex.slice(8, 10).join("") + "-" + hex.slice(10, 12).join("") + "-" + hex.slice(12, 16).join("");
155
+ }
156
+
157
+ // src/utils/appNames.ts
158
+ import * as fs2 from "fs";
159
+ import * as path2 from "path";
160
+ import * as os2 from "os";
161
+ import { load as loadYaml2, dump as dumpYaml2 } from "js-yaml";
162
+ var CONFIG_DIR = path2.join(os2.homedir(), ".eigenx");
163
+ var APPS_DIR = path2.join(CONFIG_DIR, "apps");
164
+
165
+ // src/utils/prompts.ts
166
+ import { execSync } from "child_process";
167
+ function ensureInteractive(missingFlagHint) {
168
+ if (!process.stdin.isTTY) {
169
+ throw new Error(
170
+ `Cannot prompt in non-interactive mode. Provide ${missingFlagHint} via CLI flags or environment variables.`
171
+ );
172
+ }
173
+ }
174
+ async function getPrivateKeyInteractive(privateKey) {
175
+ if (privateKey) {
176
+ if (!validatePrivateKeyFormat(privateKey)) {
177
+ throw new Error("Invalid private key format");
178
+ }
179
+ return privateKey;
180
+ }
181
+ const { getPrivateKeyWithSource } = await import("@layr-labs/ecloud-sdk");
182
+ const result = await getPrivateKeyWithSource({ privateKey: void 0 });
183
+ if (result) {
184
+ return result.key;
185
+ }
186
+ ensureInteractive("--private-key or ECLOUD_PRIVATE_KEY");
187
+ const key = await password({
188
+ message: "Enter private key:",
189
+ mask: true,
190
+ validate: (value) => {
191
+ if (!value.trim()) {
192
+ return "Private key is required";
193
+ }
194
+ if (!validatePrivateKeyFormat(value)) {
195
+ return "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)";
196
+ }
197
+ return true;
198
+ }
199
+ });
200
+ return key.trim();
201
+ }
202
+ async function getEnvironmentInteractive(environment) {
203
+ if (environment) {
204
+ getEnvironmentConfig2(environment);
205
+ return environment;
206
+ }
207
+ ensureInteractive("--environment or ECLOUD_ENV");
208
+ const availableEnvs = getAvailableEnvironments();
209
+ let defaultEnv;
210
+ const configDefaultEnv = getDefaultEnvironment();
211
+ if (configDefaultEnv && availableEnvs.includes(configDefaultEnv)) {
212
+ try {
213
+ getEnvironmentConfig2(configDefaultEnv);
214
+ defaultEnv = configDefaultEnv;
215
+ } catch {
216
+ }
217
+ }
218
+ const choices = [];
219
+ if (availableEnvs.includes("sepolia")) {
220
+ choices.push({ name: "sepolia - Ethereum Sepolia testnet", value: "sepolia" });
221
+ }
222
+ if (availableEnvs.includes("sepolia-dev")) {
223
+ choices.push({ name: "sepolia-dev - Ethereum Sepolia testnet (dev)", value: "sepolia-dev" });
224
+ }
225
+ if (availableEnvs.includes("mainnet-alpha")) {
226
+ choices.push({
227
+ name: "mainnet-alpha - Ethereum mainnet (\u26A0\uFE0F uses real funds)",
228
+ value: "mainnet-alpha"
229
+ });
230
+ }
231
+ if (choices.length === 0) {
232
+ throw new Error("No environments available in this build");
233
+ }
234
+ const env = await select({
235
+ message: "Select environment:",
236
+ choices,
237
+ default: defaultEnv
238
+ });
239
+ return env;
240
+ }
241
+ var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
242
+
243
+ // src/flags.ts
244
+ import { formatEther, parseGwei } from "viem";
245
+ var commonFlags = {
246
+ environment: Flags.string({
247
+ required: false,
248
+ description: "Deployment environment to use",
249
+ env: "ECLOUD_ENV",
250
+ default: async () => getDefaultEnvironment() || (getBuildType2() === "dev" ? "sepolia-dev" : "sepolia")
251
+ }),
252
+ "private-key": Flags.string({
253
+ required: false,
254
+ description: "Private key for signing transactions",
255
+ env: "ECLOUD_PRIVATE_KEY"
256
+ }),
257
+ "rpc-url": Flags.string({
258
+ required: false,
259
+ description: "RPC URL to connect to blockchain",
260
+ env: "ECLOUD_RPC_URL"
261
+ }),
262
+ verbose: Flags.boolean({
263
+ required: false,
264
+ description: "Enable verbose logging (default: false)",
265
+ default: false
266
+ }),
267
+ "max-fee-per-gas": Flags.string({
268
+ required: false,
269
+ description: "Override max fee per gas in gwei (e.g., 50)",
270
+ env: "ECLOUD_MAX_FEE_PER_GAS"
271
+ }),
272
+ "max-priority-fee": Flags.string({
273
+ required: false,
274
+ description: "Override max priority fee per gas in gwei (e.g., 5)",
275
+ env: "ECLOUD_MAX_PRIORITY_FEE"
276
+ }),
277
+ nonce: Flags.string({
278
+ required: false,
279
+ description: 'Override transaction nonce (integer or "latest" to replace a stuck transaction)'
280
+ })
281
+ };
282
+ async function validateCommonFlags(flags, options) {
283
+ flags["environment"] = await getEnvironmentInteractive(flags["environment"]);
284
+ if (options?.requirePrivateKey !== false) {
285
+ flags["private-key"] = await getPrivateKeyInteractive(flags["private-key"]);
286
+ }
287
+ return flags;
288
+ }
289
+
290
+ // src/client.ts
291
+ async function createBillingClient(flags) {
292
+ flags = await validateCommonFlags(flags);
293
+ const environment = flags.environment;
294
+ const environmentConfig = getEnvironmentConfig3(environment);
295
+ const rpcUrl = flags["rpc-url"] || environmentConfig.billingRPCURL || environmentConfig.defaultRPCURL;
296
+ const { key: privateKey, source } = await requirePrivateKey({
297
+ privateKey: flags["private-key"]
298
+ });
299
+ if (flags.verbose) {
300
+ console.log(`Using private key from: ${source}`);
301
+ }
302
+ const { walletClient, publicClient } = createViemClients({
303
+ privateKey,
304
+ rpcUrl,
305
+ environment
306
+ });
307
+ return createBillingModule({
308
+ verbose: flags.verbose,
309
+ walletClient,
310
+ publicClient,
311
+ environment,
312
+ skipTelemetry: true
313
+ });
314
+ }
315
+
316
+ // src/commands/billing/list-cards.ts
317
+ import chalk2 from "chalk";
318
+
319
+ // src/telemetry.ts
320
+ import {
321
+ createTelemetryClient,
322
+ createAppEnvironment,
323
+ createMetricsContext,
324
+ addMetric,
325
+ addMetricWithDimensions,
326
+ emitMetrics,
327
+ getBuildType as getBuildType3
328
+ } from "@layr-labs/ecloud-sdk";
329
+ function createCLITelemetryClient() {
330
+ const userUUID = getOrCreateUserUUID();
331
+ const environment = createAppEnvironment(userUUID);
332
+ const telemetryEnabled = getGlobalTelemetryPreference();
333
+ return createTelemetryClient(environment, "ecloud-cli", {
334
+ telemetryEnabled: telemetryEnabled !== false
335
+ // Enabled by default, disabled only if explicitly set to false
336
+ });
337
+ }
338
+ async function withTelemetry(command, action) {
339
+ const client = createCLITelemetryClient();
340
+ const metrics = createMetricsContext();
341
+ metrics.properties["source"] = "ecloud-cli";
342
+ metrics.properties["command"] = command.id || command.constructor.name;
343
+ const environment = getDefaultEnvironment() || "sepolia";
344
+ metrics.properties["environment"] = environment;
345
+ const buildType = getBuildType3() || "prod";
346
+ metrics.properties["build_type"] = buildType;
347
+ const cliVersion = command.config.version;
348
+ if (cliVersion) {
349
+ metrics.properties["cli_version"] = cliVersion;
350
+ }
351
+ addMetric(metrics, "Count", 1);
352
+ let actionError;
353
+ let result;
354
+ try {
355
+ result = await action();
356
+ return result;
357
+ } catch (err) {
358
+ actionError = err instanceof Error ? err : new Error(String(err));
359
+ throw err;
360
+ } finally {
361
+ const resultValue = actionError ? "Failure" : "Success";
362
+ const dimensions = {};
363
+ if (actionError) {
364
+ dimensions["error"] = actionError.message;
365
+ }
366
+ addMetricWithDimensions(metrics, resultValue, 1, dimensions);
367
+ const duration = Date.now() - metrics.startTime.getTime();
368
+ addMetric(metrics, "DurationMilliseconds", duration);
369
+ try {
370
+ await emitMetrics(client, metrics);
371
+ await client.close();
372
+ } catch {
373
+ }
374
+ }
375
+ }
376
+
377
+ // src/commands/billing/list-cards.ts
378
+ var BillingListCards = class _BillingListCards extends Command {
379
+ static description = "List credit cards on file";
380
+ static flags = {
381
+ ...commonFlags
382
+ };
383
+ async run() {
384
+ return withTelemetry(this, async () => {
385
+ const { flags } = await this.parse(_BillingListCards);
386
+ const billing = await createBillingClient(flags);
387
+ const { paymentMethods } = await billing.getPaymentMethods();
388
+ if (paymentMethods.length === 0) {
389
+ this.log(`
390
+ ${chalk2.gray("No cards on file.")}`);
391
+ this.log(` Run ${chalk2.cyan("ecloud billing top-up --method card")} to add one.
392
+ `);
393
+ return;
394
+ }
395
+ this.log(`
396
+ ${chalk2.bold("Cards on file:")}`);
397
+ for (const card of paymentMethods) {
398
+ const brand = card.brand.charAt(0).toUpperCase() + card.brand.slice(1);
399
+ const added = new Date(card.createdAt).toLocaleDateString();
400
+ this.log(` \u2022 ${brand} ending in ${chalk2.bold(card.last4)} ${chalk2.gray(`added ${added}`)}`);
401
+ }
402
+ this.log();
403
+ });
404
+ }
405
+ };
406
+ export {
407
+ BillingListCards as default
408
+ };
409
+ //# sourceMappingURL=list-cards.js.map