@layr-labs/ecloud-cli 0.1.0-dev.3 → 0.1.0-rc.2

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 (53) hide show
  1. package/README.md +4 -4
  2. package/VERSION +2 -2
  3. package/dist/commands/auth/generate.js +46 -184
  4. package/dist/commands/auth/generate.js.map +1 -1
  5. package/dist/commands/auth/login.js +93 -234
  6. package/dist/commands/auth/login.js.map +1 -1
  7. package/dist/commands/auth/logout.js +30 -170
  8. package/dist/commands/auth/logout.js.map +1 -1
  9. package/dist/commands/auth/migrate.js +76 -216
  10. package/dist/commands/auth/migrate.js.map +1 -1
  11. package/dist/commands/auth/whoami.js +17 -145
  12. package/dist/commands/auth/whoami.js.map +1 -1
  13. package/dist/commands/billing/cancel.js +30 -164
  14. package/dist/commands/billing/cancel.js.map +1 -1
  15. package/dist/commands/billing/status.js +80 -213
  16. package/dist/commands/billing/status.js.map +1 -1
  17. package/dist/commands/billing/subscribe.js +45 -179
  18. package/dist/commands/billing/subscribe.js.map +1 -1
  19. package/dist/commands/compute/app/create.js +20 -148
  20. package/dist/commands/compute/app/create.js.map +1 -1
  21. package/dist/commands/compute/app/deploy.js +145 -243
  22. package/dist/commands/compute/app/deploy.js.map +1 -1
  23. package/dist/commands/compute/app/info.js +1 -2
  24. package/dist/commands/compute/app/info.js.map +1 -1
  25. package/dist/commands/compute/app/list.js +111 -194
  26. package/dist/commands/compute/app/list.js.map +1 -1
  27. package/dist/commands/compute/app/logs.js +20 -105
  28. package/dist/commands/compute/app/logs.js.map +1 -1
  29. package/dist/commands/compute/app/profile/set.js +64 -153
  30. package/dist/commands/compute/app/profile/set.js.map +1 -1
  31. package/dist/commands/compute/app/start.js +43 -132
  32. package/dist/commands/compute/app/start.js.map +1 -1
  33. package/dist/commands/compute/app/stop.js +43 -132
  34. package/dist/commands/compute/app/stop.js.map +1 -1
  35. package/dist/commands/compute/app/terminate.js +44 -131
  36. package/dist/commands/compute/app/terminate.js.map +1 -1
  37. package/dist/commands/compute/app/upgrade.js +108 -209
  38. package/dist/commands/compute/app/upgrade.js.map +1 -1
  39. package/dist/commands/compute/environment/list.js +12 -104
  40. package/dist/commands/compute/environment/list.js.map +1 -1
  41. package/dist/commands/compute/environment/set.js +18 -103
  42. package/dist/commands/compute/environment/set.js.map +1 -1
  43. package/dist/commands/compute/environment/show.js +30 -122
  44. package/dist/commands/compute/environment/show.js.map +1 -1
  45. package/dist/commands/compute/undelegate.js +18 -112
  46. package/dist/commands/compute/undelegate.js.map +1 -1
  47. package/dist/commands/upgrade.js +19 -159
  48. package/dist/commands/upgrade.js.map +1 -1
  49. package/dist/commands/version.js +23 -163
  50. package/dist/commands/version.js.map +1 -1
  51. package/package.json +2 -2
  52. package/dist/commands/telemetry.js +0 -213
  53. package/dist/commands/telemetry.js.map +0 -1
@@ -35,256 +35,115 @@ function displayWarning(lines) {
35
35
  console.log("");
36
36
  }
37
37
 
38
- // src/telemetry.ts
39
- import {
40
- createTelemetryClient,
41
- createAppEnvironment,
42
- createMetricsContext,
43
- addMetric,
44
- addMetricWithDimensions,
45
- emitMetrics,
46
- getBuildType as getBuildType2
47
- } from "@layr-labs/ecloud-sdk";
48
-
49
- // src/utils/globalConfig.ts
50
- import * as fs from "fs";
51
- import * as path from "path";
52
- import * as os from "os";
53
- import { load as loadYaml, dump as dumpYaml } from "js-yaml";
54
- import { getBuildType } from "@layr-labs/ecloud-sdk";
55
- import * as crypto from "crypto";
56
- var GLOBAL_CONFIG_FILE = "config.yaml";
57
- var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
58
- function getGlobalConfigDir() {
59
- const configHome = process.env.XDG_CONFIG_HOME;
60
- let baseDir;
61
- if (configHome && path.isAbsolute(configHome)) {
62
- baseDir = configHome;
63
- } else {
64
- baseDir = path.join(os.homedir(), ".config");
65
- }
66
- const buildType = getBuildType();
67
- const buildSuffix = buildType === "dev" ? "-dev" : "";
68
- const configDirName = `ecloud${buildSuffix}`;
69
- return path.join(baseDir, configDirName);
70
- }
71
- function getGlobalConfigPath() {
72
- return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);
73
- }
74
- function loadGlobalConfig() {
75
- const configPath = getGlobalConfigPath();
76
- if (!fs.existsSync(configPath)) {
77
- return {
78
- first_run: true
79
- };
80
- }
81
- try {
82
- const content = fs.readFileSync(configPath, "utf-8");
83
- const config = loadYaml(content);
84
- return config || { first_run: true };
85
- } catch {
86
- return {
87
- first_run: true
88
- };
89
- }
90
- }
91
- function saveGlobalConfig(config) {
92
- const configPath = getGlobalConfigPath();
93
- const configDir = path.dirname(configPath);
94
- fs.mkdirSync(configDir, { recursive: true, mode: 493 });
95
- const content = dumpYaml(config, { lineWidth: -1 });
96
- fs.writeFileSync(configPath, content, { mode: 420 });
97
- }
98
- function getDefaultEnvironment() {
99
- const config = loadGlobalConfig();
100
- return config.default_environment;
101
- }
102
- function getGlobalTelemetryPreference() {
103
- const config = loadGlobalConfig();
104
- return config.telemetry_enabled;
105
- }
106
- function getOrCreateUserUUID() {
107
- const config = loadGlobalConfig();
108
- if (config.user_uuid) {
109
- return config.user_uuid;
110
- }
111
- const uuid = generateUUID();
112
- config.user_uuid = uuid;
113
- config.first_run = false;
114
- saveGlobalConfig(config);
115
- return uuid;
116
- }
117
- function generateUUID() {
118
- const bytes = crypto.randomBytes(16);
119
- bytes[6] = bytes[6] & 15 | 64;
120
- bytes[8] = bytes[8] & 63 | 128;
121
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
122
- 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("");
123
- }
124
-
125
- // src/telemetry.ts
126
- function createCLITelemetryClient() {
127
- const userUUID = getOrCreateUserUUID();
128
- const environment = createAppEnvironment(userUUID);
129
- const telemetryEnabled = getGlobalTelemetryPreference();
130
- return createTelemetryClient(environment, "ecloud-cli", {
131
- telemetryEnabled: telemetryEnabled === true
132
- // Only enabled if explicitly set to true
133
- });
134
- }
135
- async function withTelemetry(command, action) {
136
- const client = createCLITelemetryClient();
137
- const metrics = createMetricsContext();
138
- metrics.properties["source"] = "ecloud-cli";
139
- metrics.properties["command"] = command.id || command.constructor.name;
140
- const environment = getDefaultEnvironment() || "sepolia";
141
- metrics.properties["environment"] = environment;
142
- const buildType = getBuildType2() || "prod";
143
- metrics.properties["build_type"] = buildType;
144
- const cliVersion = command.config.version;
145
- if (cliVersion) {
146
- metrics.properties["cli_version"] = cliVersion;
147
- }
148
- addMetric(metrics, "Count", 1);
149
- let actionError;
150
- let result;
151
- try {
152
- result = await action();
153
- return result;
154
- } catch (err) {
155
- actionError = err instanceof Error ? err : new Error(String(err));
156
- throw err;
157
- } finally {
158
- const resultValue = actionError ? "Failure" : "Success";
159
- const dimensions = {};
160
- if (actionError) {
161
- dimensions["error"] = actionError.message;
162
- }
163
- addMetricWithDimensions(metrics, resultValue, 1, dimensions);
164
- const duration = Date.now() - metrics.startTime.getTime();
165
- addMetric(metrics, "DurationMilliseconds", duration);
166
- try {
167
- await emitMetrics(client, metrics);
168
- await client.close();
169
- } catch {
170
- }
171
- }
172
- }
173
-
174
38
  // src/commands/auth/login.ts
175
39
  var AuthLogin = class extends Command {
176
40
  static description = "Store your private key in OS keyring";
177
41
  static examples = ["<%= config.bin %> <%= command.id %>"];
178
42
  async run() {
179
- return withTelemetry(this, async () => {
180
- const exists = await keyExists();
181
- if (exists) {
182
- displayWarning([
183
- "WARNING: A private key for ecloud already exists!",
184
- "Replacing it will cause PERMANENT DATA LOSS if not backed up.",
185
- "The previous key will be lost forever."
186
- ]);
187
- const confirmReplace = await confirm({
188
- message: "Replace existing key?",
189
- default: false
190
- });
191
- if (!confirmReplace) {
192
- this.log("\nLogin cancelled.");
193
- return;
194
- }
43
+ const exists = await keyExists();
44
+ if (exists) {
45
+ displayWarning([
46
+ "WARNING: A private key for ecloud already exists!",
47
+ "Replacing it will cause PERMANENT DATA LOSS if not backed up.",
48
+ "The previous key will be lost forever."
49
+ ]);
50
+ const confirmReplace = await confirm({
51
+ message: "Replace existing key?",
52
+ default: false
53
+ });
54
+ if (!confirmReplace) {
55
+ this.log("\nLogin cancelled.");
56
+ return;
195
57
  }
196
- const legacyKeys = await getLegacyKeys();
197
- let privateKey = null;
198
- let selectedKey = null;
199
- if (legacyKeys.length > 0) {
200
- this.log("\nFound legacy keys from eigenx-cli:");
58
+ }
59
+ const legacyKeys = await getLegacyKeys();
60
+ let privateKey = null;
61
+ let selectedKey = null;
62
+ if (legacyKeys.length > 0) {
63
+ this.log("\nFound legacy keys from eigenx-cli:");
64
+ this.log("");
65
+ for (const key of legacyKeys) {
66
+ this.log(` Address: ${key.address}`);
67
+ this.log(` Environment: ${key.environment}`);
68
+ this.log(` Source: ${key.source}`);
201
69
  this.log("");
202
- for (const key of legacyKeys) {
203
- this.log(` Address: ${key.address}`);
204
- this.log(` Environment: ${key.environment}`);
205
- this.log(` Source: ${key.source}`);
206
- this.log("");
207
- }
208
- const importLegacy = await confirm({
209
- message: "Would you like to import one of these legacy keys?",
210
- default: false
70
+ }
71
+ const importLegacy = await confirm({
72
+ message: "Would you like to import one of these legacy keys?",
73
+ default: false
74
+ });
75
+ if (importLegacy) {
76
+ const choices = legacyKeys.map((key) => ({
77
+ name: `${key.address} (${key.environment} - ${key.source})`,
78
+ value: key
79
+ }));
80
+ selectedKey = await select2({
81
+ message: "Select a key to import:",
82
+ choices
211
83
  });
212
- if (importLegacy) {
213
- const choices = legacyKeys.map((key) => ({
214
- name: `${key.address} (${key.environment} - ${key.source})`,
215
- value: key
216
- }));
217
- selectedKey = await select2({
218
- message: "Select a key to import:",
219
- choices
220
- });
221
- privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);
222
- if (!privateKey) {
223
- this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);
224
- }
225
- this.log(`
226
- Importing key from ${selectedKey.source}:${selectedKey.environment}`);
84
+ privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);
85
+ if (!privateKey) {
86
+ this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);
227
87
  }
88
+ this.log(`
89
+ Importing key from ${selectedKey.source}:${selectedKey.environment}`);
228
90
  }
229
- if (!privateKey) {
230
- privateKey = await getHiddenInput("Enter your private key:");
231
- privateKey = privateKey.trim();
232
- }
233
- if (!validatePrivateKey(privateKey)) {
234
- this.error("Invalid private key format. Please check and try again.");
235
- }
236
- const address = getAddressFromPrivateKey(privateKey);
237
- this.log(`
91
+ }
92
+ if (!privateKey) {
93
+ privateKey = await getHiddenInput("Enter your private key:");
94
+ privateKey = privateKey.trim();
95
+ }
96
+ if (!validatePrivateKey(privateKey)) {
97
+ this.error("Invalid private key format. Please check and try again.");
98
+ }
99
+ const address = getAddressFromPrivateKey(privateKey);
100
+ this.log(`
238
101
  Address: ${address}`);
239
- const confirmStore = await confirm({
240
- message: "Store this key in OS keyring?",
241
- default: true
242
- });
243
- if (!confirmStore) {
244
- this.log("\nLogin cancelled.");
245
- return;
246
- }
247
- try {
248
- await storePrivateKey(privateKey);
249
- this.log("\n\u2713 Private key stored in OS keyring");
250
- this.log(`\u2713 Address: ${address}`);
251
- this.log("\nNote: This key will be used for all environments (mainnet, sepolia, etc.)");
252
- this.log("You can now use ecloud commands without --private-key flag.");
253
- if (selectedKey) {
254
- this.log("");
255
- const confirmDelete = await confirm({
256
- message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,
257
- default: false
258
- });
259
- if (confirmDelete) {
260
- const deleted = await deleteLegacyPrivateKey(
261
- selectedKey.environment,
262
- selectedKey.source
263
- );
264
- if (deleted) {
265
- this.log(
266
- `
102
+ const confirmStore = await confirm({
103
+ message: "Store this key in OS keyring?",
104
+ default: true
105
+ });
106
+ if (!confirmStore) {
107
+ this.log("\nLogin cancelled.");
108
+ return;
109
+ }
110
+ try {
111
+ await storePrivateKey(privateKey);
112
+ this.log("\n\u2713 Private key stored in OS keyring");
113
+ this.log(`\u2713 Address: ${address}`);
114
+ this.log("\nNote: This key will be used for all environments (mainnet, sepolia, etc.)");
115
+ this.log("You can now use ecloud commands without --private-key flag.");
116
+ if (selectedKey) {
117
+ this.log("");
118
+ const confirmDelete = await confirm({
119
+ message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,
120
+ default: false
121
+ });
122
+ if (confirmDelete) {
123
+ const deleted = await deleteLegacyPrivateKey(selectedKey.environment, selectedKey.source);
124
+ if (deleted) {
125
+ this.log(
126
+ `
267
127
  \u2713 Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`
268
- );
269
- this.log("\nNote: The key is now only stored in ecloud. You can still use it with");
270
- this.log("eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.");
271
- } else {
272
- this.log(
273
- `
274
- \u26A0\uFE0F Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`
275
- );
276
- this.log("The key may have already been removed.");
277
- }
128
+ );
129
+ this.log("\nNote: The key is now only stored in ecloud. You can still use it with");
130
+ this.log("eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.");
278
131
  } else {
279
- this.log(`
280
- Legacy key kept in ${selectedKey.source}:${selectedKey.environment}`);
281
- this.log("You can delete it later using 'eigenx auth logout' if needed.");
132
+ this.log(
133
+ `
134
+ \u26A0\uFE0F Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`
135
+ );
136
+ this.log("The key may have already been removed.");
282
137
  }
138
+ } else {
139
+ this.log(`
140
+ Legacy key kept in ${selectedKey.source}:${selectedKey.environment}`);
141
+ this.log("You can delete it later using 'eigenx auth logout' if needed.");
283
142
  }
284
- } catch (err) {
285
- this.error(`Failed to store key: ${err.message}`);
286
143
  }
287
- });
144
+ } catch (err) {
145
+ this.error(`Failed to store key: ${err.message}`);
146
+ }
288
147
  }
289
148
  };
290
149
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/auth/login.ts","../../../src/utils/security.ts","../../../src/telemetry.ts","../../../src/utils/globalConfig.ts"],"sourcesContent":["/**\n * Auth Login Command\n *\n * Store an existing private key in OS keyring\n */\n\nimport { Command } from \"@oclif/core\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport {\n storePrivateKey,\n keyExists,\n validatePrivateKey,\n getAddressFromPrivateKey,\n getLegacyKeys,\n getLegacyPrivateKey,\n deleteLegacyPrivateKey,\n type LegacyKey,\n} from \"@layr-labs/ecloud-sdk\";\nimport { getHiddenInput, displayWarning } from \"../../utils/security\";\nimport { withTelemetry } from \"../../telemetry\";\n\nexport default class AuthLogin extends Command {\n static description = \"Store your private key in OS keyring\";\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n async run(): Promise<void> {\n return withTelemetry(this, async () => {\n // Check if key already exists\n const exists = await keyExists();\n\n if (exists) {\n displayWarning([\n \"WARNING: A private key for ecloud already exists!\",\n \"Replacing it will cause PERMANENT DATA LOSS if not backed up.\",\n \"The previous key will be lost forever.\",\n ]);\n\n const confirmReplace = await confirm({\n message: \"Replace existing key?\",\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n }\n\n // Check for legacy keys from eigenx-cli\n const legacyKeys = await getLegacyKeys();\n let privateKey: string | null = null;\n let selectedKey: LegacyKey | null = null;\n\n if (legacyKeys.length > 0) {\n this.log(\"\\nFound legacy keys from eigenx-cli:\");\n this.log(\"\");\n\n // Display legacy keys\n for (const key of legacyKeys) {\n this.log(` Address: ${key.address}`);\n this.log(` Environment: ${key.environment}`);\n this.log(` Source: ${key.source}`);\n this.log(\"\");\n }\n\n const importLegacy = await confirm({\n message: \"Would you like to import one of these legacy keys?\",\n default: false,\n });\n\n if (importLegacy) {\n // Create choices for selection\n const choices = legacyKeys.map((key) => ({\n name: `${key.address} (${key.environment} - ${key.source})`,\n value: key,\n }));\n\n selectedKey = await select<LegacyKey>({\n message: \"Select a key to import:\",\n choices,\n });\n\n // Retrieve the actual private key\n privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);\n\n if (!privateKey) {\n this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);\n }\n\n this.log(`\\nImporting key from ${selectedKey.source}:${selectedKey.environment}`);\n }\n }\n\n // If no legacy key was selected, prompt for private key input\n if (!privateKey) {\n privateKey = await getHiddenInput(\"Enter your private key:\");\n\n privateKey = privateKey.trim();\n }\n\n if (!validatePrivateKey(privateKey)) {\n this.error(\"Invalid private key format. Please check and try again.\");\n }\n\n // Derive address for confirmation\n const address = getAddressFromPrivateKey(privateKey);\n\n this.log(`\\nAddress: ${address}`);\n\n const confirmStore = await confirm({\n message: \"Store this key in OS keyring?\",\n default: true,\n });\n\n if (!confirmStore) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n\n // Store in keyring\n try {\n await storePrivateKey(privateKey);\n this.log(\"\\n✓ Private key stored in OS keyring\");\n this.log(`✓ Address: ${address}`);\n this.log(\"\\nNote: This key will be used for all environments (mainnet, sepolia, etc.)\");\n this.log(\"You can now use ecloud commands without --private-key flag.\");\n\n // Ask if user wants to delete the legacy key (only if save was successful)\n if (selectedKey) {\n this.log(\"\");\n const confirmDelete = await confirm({\n message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,\n default: false,\n });\n\n if (confirmDelete) {\n const deleted = await deleteLegacyPrivateKey(\n selectedKey.environment,\n selectedKey.source,\n );\n\n if (deleted) {\n this.log(\n `\\n✓ Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"\\nNote: The key is now only stored in ecloud. You can still use it with\");\n this.log(\"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.\");\n } else {\n this.log(\n `\\n⚠️ Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"The key may have already been removed.\");\n }\n } else {\n this.log(`\\nLegacy key kept in ${selectedKey.source}:${selectedKey.environment}`);\n this.log(\"You can delete it later using 'eigenx auth logout' if needed.\");\n }\n }\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\n }\n });\n }\n}\n","/**\n * Security utilities for CLI\n *\n * Functions for securely displaying and handling sensitive content\n * like private keys.\n */\n\nimport { spawn, execSync } from \"child_process\";\nimport { platform } from \"os\";\nimport { select, password } from \"@inquirer/prompts\";\n\n/**\n * Display sensitive content using system pager (less/more)\n * Returns true if content was displayed, false if user aborted\n */\nexport async function showPrivateKey(content: string): Promise<boolean> {\n // Try to use system pager\n const pager = detectPager();\n\n if (pager) {\n try {\n await runPager(pager, content);\n return true;\n } catch (err) {\n console.error(`Failed to run pager: ${err}`);\n // Fall through to fallback\n }\n }\n\n // No pager available - give user a choice\n console.log(\"\\nNo pager (less/more) found on PATH.\");\n console.log(\"For security, avoid printing private keys to the terminal.\");\n console.log(\"\");\n\n const choice = await select({\n message: \"Choose an option:\",\n choices: [\n { name: \"Abort (recommended)\", value: \"abort\" },\n { name: \"Print and clear screen\", value: \"print\" },\n ],\n });\n\n if (choice === \"print\") {\n console.log(content);\n console.log(\"\");\n console.log(\"Press Enter after you have securely saved the key.\");\n console.log(\"The screen will be cleared...\");\n\n // Wait for Enter\n await password({\n message: \"\",\n mask: \"\",\n });\n\n clearTerminal();\n return true;\n }\n\n return false; // User aborted\n}\n\n/**\n * Detect system pager (less or more)\n */\nfunction detectPager(): string | null {\n // Check PAGER env var first\n if (process.env.PAGER) {\n const pagerEnv = process.env.PAGER.trim();\n // Only allow simple command names without arguments or special characters\n if (/^[a-zA-Z0-9_-]+$/.test(pagerEnv)) {\n return pagerEnv;\n }\n }\n\n // Try common pagers\n const pagers = [\"less\", \"more\"];\n\n for (const pagerCmd of pagers) {\n if (commandExists(pagerCmd)) {\n return pagerCmd;\n }\n }\n\n return null;\n}\n\n/**\n * Run pager with content\n */\nfunction runPager(pager: string, content: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(pager, [], {\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Pager exited with code ${code}`));\n }\n });\n\n try {\n const written = child.stdin!.write(content);\n if (!written) {\n child.stdin!.once(\"drain\", () => {\n try {\n child.stdin!.end();\n } catch (err) {\n reject(err);\n }\n });\n } else {\n child.stdin!.end();\n }\n } catch (err) {\n reject(err);\n }\n });\n}\n\n/**\n * Check if command exists\n */\nfunction commandExists(command: string): boolean {\n try {\n const cmd = platform() === \"win32\" ? `where ${command}` : `which ${command}`;\n execSync(cmd, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Clear terminal screen\n */\nexport function clearTerminal(): void {\n if (platform() === \"win32\") {\n process.stdout.write(\"\\x1Bc\");\n } else {\n process.stdout.write(\"\\x1B[2J\\x1B[3J\\x1B[H\");\n }\n}\n\n/**\n * Get hidden input (password-style)\n */\nexport async function getHiddenInput(message: string): Promise<string> {\n return await password({\n message,\n mask: \"*\",\n });\n}\n\n/**\n * Display multi-line warning for destructive operations\n */\nexport function displayWarning(lines: string[]): void {\n const width = lines.length > 0 ? Math.max(...lines.map((l) => l.length)) + 4 : 4;\n const border = \"⚠\".repeat(width);\n\n console.log(\"\");\n console.log(border);\n for (const line of lines) {\n console.log(`⚠ ${line}`);\n }\n console.log(border);\n console.log(\"\");\n}\n","/**\n * Telemetry utilities for CLI commands\n *\n * Provides helpers to wrap command execution with telemetry tracking\n */\n\nimport {\n createTelemetryClient,\n createAppEnvironment,\n createMetricsContext,\n addMetric,\n addMetricWithDimensions,\n emitMetrics,\n type TelemetryClient,\n getBuildType,\n} from \"@layr-labs/ecloud-sdk\";\nimport { Command } from \"@oclif/core\";\nimport {\n getDefaultEnvironment,\n getOrCreateUserUUID,\n getGlobalTelemetryPreference,\n} from \"./utils/globalConfig\";\n\n/**\n * Create a telemetry client for CLI usage\n */\nexport function createCLITelemetryClient(): TelemetryClient {\n // Get user UUID from CLI's globalConfig (handles I/O)\n const userUUID = getOrCreateUserUUID();\n const environment = createAppEnvironment(userUUID);\n\n // Get telemetry preference from CLI's globalConfig\n const telemetryEnabled = getGlobalTelemetryPreference();\n\n return createTelemetryClient(environment, \"ecloud-cli\", {\n telemetryEnabled: telemetryEnabled === true, // Only enabled if explicitly set to true\n });\n}\n\n/**\n * Wrap a command execution with telemetry\n *\n * @param command - The CLI command instance\n * @param action - The command action to execute\n * @returns The result of the action\n */\nexport async function withTelemetry<T>(command: Command, action: () => Promise<T>): Promise<T> {\n const client = createCLITelemetryClient();\n const metrics = createMetricsContext();\n\n // Set source to identify CLI usage\n metrics.properties[\"source\"] = \"ecloud-cli\";\n\n // Set command name in properties\n metrics.properties[\"command\"] = command.id || command.constructor.name;\n\n // Set environment in properties\n const environment = getDefaultEnvironment() || \"sepolia\";\n metrics.properties[\"environment\"] = environment;\n\n // Set buildType in properties\n const buildType = getBuildType() || \"prod\";\n metrics.properties[\"build_type\"] = buildType;\n\n // Set CLI version if available\n const cliVersion = command.config.version;\n if (cliVersion) {\n metrics.properties[\"cli_version\"] = cliVersion;\n }\n\n // Add initial count metric\n addMetric(metrics, \"Count\", 1);\n\n let actionError: Error | undefined;\n let result: T;\n\n try {\n result = await action();\n return result;\n } catch (err) {\n actionError = err instanceof Error ? err : new Error(String(err));\n throw err;\n } finally {\n // Add result metric\n const resultValue = actionError ? \"Failure\" : \"Success\";\n const dimensions: Record<string, string> = {};\n if (actionError) {\n dimensions[\"error\"] = actionError.message;\n }\n addMetricWithDimensions(metrics, resultValue, 1, dimensions);\n\n // Add duration metric\n const duration = Date.now() - metrics.startTime.getTime();\n addMetric(metrics, \"DurationMilliseconds\", duration);\n\n // Emit all metrics\n try {\n await emitMetrics(client, metrics);\n await client.close();\n } catch {\n // Silently ignore telemetry errors\n }\n }\n}\n","/**\n * Global configuration management\n *\n * Stores user-level configuration that persists across all CLI usage.\n * - $XDG_CONFIG_HOME/ecloud[BuildSuffix]/config.yaml (if XDG_CONFIG_HOME is set)\n * - Or ~/.config/ecloud[BuildSuffix]/config.yaml (fallback)\n *\n * Where BuildSuffix is:\n * - \"\" (empty) for production builds\n * - \"-dev\" for development builds\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as os from \"os\";\nimport { load as loadYaml, dump as dumpYaml } from \"js-yaml\";\nimport { getBuildType } from \"@layr-labs/ecloud-sdk\";\nimport * as crypto from \"crypto\";\nconst GLOBAL_CONFIG_FILE = \"config.yaml\";\n\nexport interface ProfileCacheEntry {\n updated_at: number; // Unix timestamp in milliseconds\n profiles: { [appId: string]: string }; // appId -> profile name\n}\n\nexport interface GlobalConfig {\n first_run?: boolean;\n telemetry_enabled?: boolean;\n user_uuid?: string;\n default_environment?: string;\n last_version_check?: number;\n last_known_version?: string;\n profile_cache?: {\n [environment: string]: ProfileCacheEntry;\n };\n}\n\n// Profile cache TTL: 24 hours in milliseconds\nconst PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Get the XDG-compliant directory where global ecloud config should be stored\n */\nfunction getGlobalConfigDir(): string {\n // First check XDG_CONFIG_HOME\n const configHome = process.env.XDG_CONFIG_HOME;\n\n let baseDir: string;\n if (configHome && path.isAbsolute(configHome)) {\n baseDir = configHome;\n } else {\n // Fall back to ~/.config\n baseDir = path.join(os.homedir(), \".config\");\n }\n\n // Use environment-specific config directory\n const buildType = getBuildType();\n const buildSuffix = buildType === \"dev\" ? \"-dev\" : \"\";\n const configDirName = `ecloud${buildSuffix}`;\n\n return path.join(baseDir, configDirName);\n}\n\n/**\n * Get the full path to the global config file\n */\nfunction getGlobalConfigPath(): string {\n return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);\n}\n\n/**\n * Load global configuration, creating defaults if needed\n */\nexport function loadGlobalConfig(): GlobalConfig {\n const configPath = getGlobalConfigPath();\n\n // If file doesn't exist, return defaults for first run\n if (!fs.existsSync(configPath)) {\n return {\n first_run: true,\n };\n }\n\n try {\n const content = fs.readFileSync(configPath, \"utf-8\");\n const config = loadYaml(content) as GlobalConfig;\n return config || { first_run: true };\n } catch {\n // If parsing fails, return defaults\n return {\n first_run: true,\n };\n }\n}\n\n/**\n * Save global configuration to disk\n */\nexport function saveGlobalConfig(config: GlobalConfig): void {\n const configPath = getGlobalConfigPath();\n\n // Ensure directory exists\n const configDir = path.dirname(configPath);\n fs.mkdirSync(configDir, { recursive: true, mode: 0o755 });\n\n // Write config file\n const content = dumpYaml(config, { lineWidth: -1 });\n fs.writeFileSync(configPath, content, { mode: 0o644 });\n}\n\n/**\n * Get the user's preferred deployment environment\n */\nexport function getDefaultEnvironment(): string | undefined {\n const config = loadGlobalConfig();\n return config.default_environment;\n}\n\n/**\n * Set the user's preferred deployment environment\n */\nexport function setDefaultEnvironment(environment: string): void {\n const config = loadGlobalConfig();\n config.default_environment = environment;\n config.first_run = false; // No longer first run after setting environment\n saveGlobalConfig(config);\n}\n\n/**\n * Check if this is the user's first time running the CLI\n */\nexport function isFirstRun(): boolean {\n const config = loadGlobalConfig();\n return config.first_run === true;\n}\n\n/**\n * Mark that the first run has been completed\n */\nexport function markFirstRunComplete(): void {\n const config = loadGlobalConfig();\n config.first_run = false;\n saveGlobalConfig(config);\n}\n\n/**\n * Get the global telemetry preference\n */\nexport function getGlobalTelemetryPreference(): boolean | undefined {\n const config = loadGlobalConfig();\n return config.telemetry_enabled;\n}\n\n/**\n * Set the global telemetry preference\n */\nexport function setGlobalTelemetryPreference(enabled: boolean): void {\n const config = loadGlobalConfig();\n config.telemetry_enabled = enabled;\n config.first_run = false; // No longer first run after setting preference\n saveGlobalConfig(config);\n}\n\n// ==================== Profile Cache Functions ====================\n\n/**\n * Get cached profile names for an environment\n * Returns null if cache is missing or expired (older than 24 hours)\n */\nexport function getProfileCache(environment: string): Record<string, string> | null {\n const config = loadGlobalConfig();\n const cacheEntry = config.profile_cache?.[environment];\n\n if (!cacheEntry) {\n return null;\n }\n\n // Check if cache is expired\n const now = Date.now();\n if (now - cacheEntry.updated_at > PROFILE_CACHE_TTL_MS) {\n return null;\n }\n\n return cacheEntry.profiles;\n}\n\n/**\n * Set cached profile names for an environment\n */\nexport function setProfileCache(environment: string, profiles: Record<string, string>): void {\n const config = loadGlobalConfig();\n\n if (!config.profile_cache) {\n config.profile_cache = {};\n }\n\n config.profile_cache[environment] = {\n updated_at: Date.now(),\n profiles,\n };\n\n saveGlobalConfig(config);\n}\n\n/**\n * Invalidate profile cache for a specific environment or all environments\n */\nexport function invalidateProfileCache(environment?: string): void {\n const config = loadGlobalConfig();\n\n if (!config.profile_cache) {\n return;\n }\n\n if (environment) {\n // Invalidate specific environment\n delete config.profile_cache[environment];\n } else {\n // Invalidate all environments\n config.profile_cache = {};\n }\n\n saveGlobalConfig(config);\n}\n\n/**\n * Update a single profile name in the cache\n * This is useful after deploy or profile set to update just one entry\n */\nexport function updateProfileCacheEntry(\n environment: string,\n appId: string,\n profileName: string,\n): void {\n const config = loadGlobalConfig();\n\n if (!config.profile_cache) {\n config.profile_cache = {};\n }\n\n if (!config.profile_cache[environment]) {\n config.profile_cache[environment] = {\n updated_at: Date.now(),\n profiles: {},\n };\n }\n\n // Normalize appId to lowercase for consistent lookups\n const normalizedAppId = appId.toLowerCase();\n config.profile_cache[environment].profiles[normalizedAppId] = profileName;\n config.profile_cache[environment].updated_at = Date.now();\n\n saveGlobalConfig(config);\n}\n\n/**\n * Get the user UUID from global config, or generate a new one if it doesn't exist\n */\nexport function getOrCreateUserUUID(): string {\n const config = loadGlobalConfig();\n if (config.user_uuid) {\n return config.user_uuid;\n }\n\n // Generate a new UUID (v4)\n const uuid = generateUUID();\n\n // Save it to config\n config.user_uuid = uuid;\n config.first_run = false;\n saveGlobalConfig(config);\n\n return uuid;\n}\n\n/**\n * Generate a UUID v4\n */\nfunction generateUUID(): string {\n // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n // Use cryptographically secure random values.\n const bytes = crypto.randomBytes(16);\n // Per RFC 4122 section 4.4, set bits for version and `clock_seq_hi_and_reserved`\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\"));\n return (\n hex.slice(0, 4).join(\"\") +\n hex.slice(4, 6).join(\"\") +\n \"-\" +\n hex.slice(6, 8).join(\"\") +\n \"-\" +\n hex.slice(8, 10).join(\"\") +\n \"-\" +\n hex.slice(10, 12).join(\"\") +\n \"-\" +\n hex.slice(12, 16).join(\"\")\n );\n}\n\n/**\n * Save user UUID to global config (preserves existing UUID if present)\n */\nexport function saveUserUUID(userUUID: string): void {\n const config = loadGlobalConfig();\n // Only update if not already set\n if (!config.user_uuid) {\n config.user_uuid = userUUID;\n saveGlobalConfig(config);\n }\n}\n"],"mappings":"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","names":["select","getBuildType","getBuildType","select"]}
1
+ {"version":3,"sources":["../../../src/commands/auth/login.ts","../../../src/utils/security.ts"],"sourcesContent":["/**\n * Auth Login Command\n *\n * Store an existing private key in OS keyring\n */\n\nimport { Command } from \"@oclif/core\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport {\n storePrivateKey,\n keyExists,\n validatePrivateKey,\n getAddressFromPrivateKey,\n getLegacyKeys,\n getLegacyPrivateKey,\n deleteLegacyPrivateKey,\n type LegacyKey,\n} from \"@layr-labs/ecloud-sdk\";\nimport { getHiddenInput, displayWarning } from \"../../utils/security\";\n\nexport default class AuthLogin extends Command {\n static description = \"Store your private key in OS keyring\";\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n async run(): Promise<void> {\n // Check if key already exists\n const exists = await keyExists();\n\n if (exists) {\n displayWarning([\n \"WARNING: A private key for ecloud already exists!\",\n \"Replacing it will cause PERMANENT DATA LOSS if not backed up.\",\n \"The previous key will be lost forever.\",\n ]);\n\n const confirmReplace = await confirm({\n message: \"Replace existing key?\",\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n }\n\n // Check for legacy keys from eigenx-cli\n const legacyKeys = await getLegacyKeys();\n let privateKey: string | null = null;\n let selectedKey: LegacyKey | null = null;\n\n if (legacyKeys.length > 0) {\n this.log(\"\\nFound legacy keys from eigenx-cli:\");\n this.log(\"\");\n\n // Display legacy keys\n for (const key of legacyKeys) {\n this.log(` Address: ${key.address}`);\n this.log(` Environment: ${key.environment}`);\n this.log(` Source: ${key.source}`);\n this.log(\"\");\n }\n\n const importLegacy = await confirm({\n message: \"Would you like to import one of these legacy keys?\",\n default: false,\n });\n\n if (importLegacy) {\n // Create choices for selection\n const choices = legacyKeys.map((key) => ({\n name: `${key.address} (${key.environment} - ${key.source})`,\n value: key,\n }));\n\n selectedKey = await select<LegacyKey>({\n message: \"Select a key to import:\",\n choices,\n });\n\n // Retrieve the actual private key\n privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);\n\n if (!privateKey) {\n this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);\n }\n\n this.log(`\\nImporting key from ${selectedKey.source}:${selectedKey.environment}`);\n }\n }\n\n // If no legacy key was selected, prompt for private key input\n if (!privateKey) {\n privateKey = await getHiddenInput(\"Enter your private key:\");\n\n privateKey = privateKey.trim();\n }\n\n if (!validatePrivateKey(privateKey)) {\n this.error(\"Invalid private key format. Please check and try again.\");\n }\n\n // Derive address for confirmation\n const address = getAddressFromPrivateKey(privateKey);\n\n this.log(`\\nAddress: ${address}`);\n\n const confirmStore = await confirm({\n message: \"Store this key in OS keyring?\",\n default: true,\n });\n\n if (!confirmStore) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n\n // Store in keyring\n try {\n await storePrivateKey(privateKey);\n this.log(\"\\n✓ Private key stored in OS keyring\");\n this.log(`✓ Address: ${address}`);\n this.log(\"\\nNote: This key will be used for all environments (mainnet, sepolia, etc.)\");\n this.log(\"You can now use ecloud commands without --private-key flag.\");\n\n // Ask if user wants to delete the legacy key (only if save was successful)\n if (selectedKey) {\n this.log(\"\");\n const confirmDelete = await confirm({\n message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,\n default: false,\n });\n\n if (confirmDelete) {\n const deleted = await deleteLegacyPrivateKey(selectedKey.environment, selectedKey.source);\n\n if (deleted) {\n this.log(\n `\\n✓ Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"\\nNote: The key is now only stored in ecloud. You can still use it with\");\n this.log(\"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.\");\n } else {\n this.log(\n `\\n⚠️ Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"The key may have already been removed.\");\n }\n } else {\n this.log(`\\nLegacy key kept in ${selectedKey.source}:${selectedKey.environment}`);\n this.log(\"You can delete it later using 'eigenx auth logout' if needed.\");\n }\n }\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\n }\n }\n}\n","/**\n * Security utilities for CLI\n *\n * Functions for securely displaying and handling sensitive content\n * like private keys.\n */\n\nimport { spawn, execSync } from \"child_process\";\nimport { platform } from \"os\";\nimport { select, password } from \"@inquirer/prompts\";\n\n/**\n * Display sensitive content using system pager (less/more)\n * Returns true if content was displayed, false if user aborted\n */\nexport async function showPrivateKey(content: string): Promise<boolean> {\n // Try to use system pager\n const pager = detectPager();\n\n if (pager) {\n try {\n await runPager(pager, content);\n return true;\n } catch (err) {\n console.error(`Failed to run pager: ${err}`);\n // Fall through to fallback\n }\n }\n\n // No pager available - give user a choice\n console.log(\"\\nNo pager (less/more) found on PATH.\");\n console.log(\"For security, avoid printing private keys to the terminal.\");\n console.log(\"\");\n\n const choice = await select({\n message: \"Choose an option:\",\n choices: [\n { name: \"Abort (recommended)\", value: \"abort\" },\n { name: \"Print and clear screen\", value: \"print\" },\n ],\n });\n\n if (choice === \"print\") {\n console.log(content);\n console.log(\"\");\n console.log(\"Press Enter after you have securely saved the key.\");\n console.log(\"The screen will be cleared...\");\n\n // Wait for Enter\n await password({\n message: \"\",\n mask: \"\",\n });\n\n clearTerminal();\n return true;\n }\n\n return false; // User aborted\n}\n\n/**\n * Detect system pager (less or more)\n */\nfunction detectPager(): string | null {\n // Check PAGER env var first\n if (process.env.PAGER) {\n const pagerEnv = process.env.PAGER.trim();\n // Only allow simple command names without arguments or special characters\n if (/^[a-zA-Z0-9_-]+$/.test(pagerEnv)) {\n return pagerEnv;\n }\n }\n\n // Try common pagers\n const pagers = [\"less\", \"more\"];\n\n for (const pagerCmd of pagers) {\n if (commandExists(pagerCmd)) {\n return pagerCmd;\n }\n }\n\n return null;\n}\n\n/**\n * Run pager with content\n */\nfunction runPager(pager: string, content: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(pager, [], {\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Pager exited with code ${code}`));\n }\n });\n\n try {\n const written = child.stdin!.write(content);\n if (!written) {\n child.stdin!.once(\"drain\", () => {\n try {\n child.stdin!.end();\n } catch (err) {\n reject(err);\n }\n });\n } else {\n child.stdin!.end();\n }\n } catch (err) {\n reject(err);\n }\n });\n}\n\n/**\n * Check if command exists\n */\nfunction commandExists(command: string): boolean {\n try {\n const cmd = platform() === \"win32\" ? `where ${command}` : `which ${command}`;\n execSync(cmd, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Clear terminal screen\n */\nexport function clearTerminal(): void {\n if (platform() === \"win32\") {\n process.stdout.write(\"\\x1Bc\");\n } else {\n process.stdout.write(\"\\x1B[2J\\x1B[3J\\x1B[H\");\n }\n}\n\n/**\n * Get hidden input (password-style)\n */\nexport async function getHiddenInput(message: string): Promise<string> {\n return await password({\n message,\n mask: \"*\",\n });\n}\n\n/**\n * Display multi-line warning for destructive operations\n */\nexport function displayWarning(lines: string[]): void {\n const width = lines.length > 0 ? Math.max(...lines.map((l) => l.length)) + 4 : 4;\n const border = \"⚠\".repeat(width);\n\n console.log(\"\");\n console.log(border);\n for (const line of lines) {\n console.log(`⚠ ${line}`);\n }\n console.log(border);\n console.log(\"\");\n}\n"],"mappings":"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","names":["select","select"]}
@@ -4,144 +4,6 @@
4
4
  import { Command, Flags } from "@oclif/core";
5
5
  import { confirm } from "@inquirer/prompts";
6
6
  import { deletePrivateKey, getPrivateKey, getAddressFromPrivateKey } from "@layr-labs/ecloud-sdk";
7
-
8
- // src/telemetry.ts
9
- import {
10
- createTelemetryClient,
11
- createAppEnvironment,
12
- createMetricsContext,
13
- addMetric,
14
- addMetricWithDimensions,
15
- emitMetrics,
16
- getBuildType as getBuildType2
17
- } from "@layr-labs/ecloud-sdk";
18
-
19
- // src/utils/globalConfig.ts
20
- import * as fs from "fs";
21
- import * as path from "path";
22
- import * as os from "os";
23
- import { load as loadYaml, dump as dumpYaml } from "js-yaml";
24
- import { getBuildType } from "@layr-labs/ecloud-sdk";
25
- import * as crypto from "crypto";
26
- var GLOBAL_CONFIG_FILE = "config.yaml";
27
- var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
28
- function getGlobalConfigDir() {
29
- const configHome = process.env.XDG_CONFIG_HOME;
30
- let baseDir;
31
- if (configHome && path.isAbsolute(configHome)) {
32
- baseDir = configHome;
33
- } else {
34
- baseDir = path.join(os.homedir(), ".config");
35
- }
36
- const buildType = getBuildType();
37
- const buildSuffix = buildType === "dev" ? "-dev" : "";
38
- const configDirName = `ecloud${buildSuffix}`;
39
- return path.join(baseDir, configDirName);
40
- }
41
- function getGlobalConfigPath() {
42
- return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);
43
- }
44
- function loadGlobalConfig() {
45
- const configPath = getGlobalConfigPath();
46
- if (!fs.existsSync(configPath)) {
47
- return {
48
- first_run: true
49
- };
50
- }
51
- try {
52
- const content = fs.readFileSync(configPath, "utf-8");
53
- const config = loadYaml(content);
54
- return config || { first_run: true };
55
- } catch {
56
- return {
57
- first_run: true
58
- };
59
- }
60
- }
61
- function saveGlobalConfig(config) {
62
- const configPath = getGlobalConfigPath();
63
- const configDir = path.dirname(configPath);
64
- fs.mkdirSync(configDir, { recursive: true, mode: 493 });
65
- const content = dumpYaml(config, { lineWidth: -1 });
66
- fs.writeFileSync(configPath, content, { mode: 420 });
67
- }
68
- function getDefaultEnvironment() {
69
- const config = loadGlobalConfig();
70
- return config.default_environment;
71
- }
72
- function getGlobalTelemetryPreference() {
73
- const config = loadGlobalConfig();
74
- return config.telemetry_enabled;
75
- }
76
- function getOrCreateUserUUID() {
77
- const config = loadGlobalConfig();
78
- if (config.user_uuid) {
79
- return config.user_uuid;
80
- }
81
- const uuid = generateUUID();
82
- config.user_uuid = uuid;
83
- config.first_run = false;
84
- saveGlobalConfig(config);
85
- return uuid;
86
- }
87
- function generateUUID() {
88
- const bytes = crypto.randomBytes(16);
89
- bytes[6] = bytes[6] & 15 | 64;
90
- bytes[8] = bytes[8] & 63 | 128;
91
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
92
- 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("");
93
- }
94
-
95
- // src/telemetry.ts
96
- function createCLITelemetryClient() {
97
- const userUUID = getOrCreateUserUUID();
98
- const environment = createAppEnvironment(userUUID);
99
- const telemetryEnabled = getGlobalTelemetryPreference();
100
- return createTelemetryClient(environment, "ecloud-cli", {
101
- telemetryEnabled: telemetryEnabled === true
102
- // Only enabled if explicitly set to true
103
- });
104
- }
105
- async function withTelemetry(command, action) {
106
- const client = createCLITelemetryClient();
107
- const metrics = createMetricsContext();
108
- metrics.properties["source"] = "ecloud-cli";
109
- metrics.properties["command"] = command.id || command.constructor.name;
110
- const environment = getDefaultEnvironment() || "sepolia";
111
- metrics.properties["environment"] = environment;
112
- const buildType = getBuildType2() || "prod";
113
- metrics.properties["build_type"] = buildType;
114
- const cliVersion = command.config.version;
115
- if (cliVersion) {
116
- metrics.properties["cli_version"] = cliVersion;
117
- }
118
- addMetric(metrics, "Count", 1);
119
- let actionError;
120
- let result;
121
- try {
122
- result = await action();
123
- return result;
124
- } catch (err) {
125
- actionError = err instanceof Error ? err : new Error(String(err));
126
- throw err;
127
- } finally {
128
- const resultValue = actionError ? "Failure" : "Success";
129
- const dimensions = {};
130
- if (actionError) {
131
- dimensions["error"] = actionError.message;
132
- }
133
- addMetricWithDimensions(metrics, resultValue, 1, dimensions);
134
- const duration = Date.now() - metrics.startTime.getTime();
135
- addMetric(metrics, "DurationMilliseconds", duration);
136
- try {
137
- await emitMetrics(client, metrics);
138
- await client.close();
139
- } catch {
140
- }
141
- }
142
- }
143
-
144
- // src/commands/auth/logout.ts
145
7
  var AuthLogout = class _AuthLogout extends Command {
146
8
  static description = "Remove private key from OS keyring";
147
9
  static examples = [
@@ -155,41 +17,39 @@ var AuthLogout = class _AuthLogout extends Command {
155
17
  })
156
18
  };
157
19
  async run() {
158
- return withTelemetry(this, async () => {
159
- const { flags } = await this.parse(_AuthLogout);
160
- const privateKey = await getPrivateKey();
161
- if (!privateKey) {
162
- this.log("No key found in keyring");
163
- this.log("\nNothing to remove.");
20
+ const { flags } = await this.parse(_AuthLogout);
21
+ const privateKey = await getPrivateKey();
22
+ if (!privateKey) {
23
+ this.log("No key found in keyring");
24
+ this.log("\nNothing to remove.");
25
+ return;
26
+ }
27
+ const address = getAddressFromPrivateKey(privateKey);
28
+ this.log("Found stored key:");
29
+ this.log(` Address: ${address}`);
30
+ this.log("");
31
+ if (!flags.force) {
32
+ const confirmed = await confirm({
33
+ message: "Remove private key from keyring?",
34
+ default: false
35
+ });
36
+ if (!confirmed) {
37
+ this.log("Logout cancelled");
164
38
  return;
165
39
  }
166
- const address = getAddressFromPrivateKey(privateKey);
167
- this.log("Found stored key:");
168
- this.log(` Address: ${address}`);
169
- this.log("");
170
- if (!flags.force) {
171
- const confirmed = await confirm({
172
- message: "Remove private key from keyring?",
173
- default: false
174
- });
175
- if (!confirmed) {
176
- this.log("Logout cancelled");
177
- return;
178
- }
179
- }
180
- try {
181
- const deleted = await deletePrivateKey();
182
- if (deleted) {
183
- this.log("\n\u2713 Successfully removed key from keyring");
184
- this.log("\nYou will need to provide --private-key flag for future commands,");
185
- this.log("or run 'ecloud auth login' to store a key again.");
186
- } else {
187
- this.log("\nFailed to remove key (it may have already been removed)");
188
- }
189
- } catch (err) {
190
- this.error(`Failed to remove key: ${err.message}`);
40
+ }
41
+ try {
42
+ const deleted = await deletePrivateKey();
43
+ if (deleted) {
44
+ this.log("\n\u2713 Successfully removed key from keyring");
45
+ this.log("\nYou will need to provide --private-key flag for future commands,");
46
+ this.log("or run 'ecloud auth login' to store a key again.");
47
+ } else {
48
+ this.log("\nFailed to remove key (it may have already been removed)");
191
49
  }
192
- });
50
+ } catch (err) {
51
+ this.error(`Failed to remove key: ${err.message}`);
52
+ }
193
53
  }
194
54
  };
195
55
  export {