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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +2 -6
  2. package/dist/commands/app/create.js +29 -0
  3. package/dist/commands/app/create.js.map +1 -0
  4. package/dist/commands/app/deploy.js +142 -0
  5. package/dist/commands/app/deploy.js.map +1 -0
  6. package/dist/commands/app/logs.js +108 -0
  7. package/dist/commands/app/logs.js.map +1 -0
  8. package/dist/commands/app/start.js +121 -0
  9. package/dist/commands/app/start.js.map +1 -0
  10. package/dist/commands/app/stop.js +121 -0
  11. package/dist/commands/app/stop.js.map +1 -0
  12. package/dist/commands/app/terminate.js +128 -0
  13. package/dist/commands/app/terminate.js.map +1 -0
  14. package/dist/commands/app/upgrade.js +142 -0
  15. package/dist/commands/app/upgrade.js.map +1 -0
  16. package/dist/commands/auth/generate.js +10 -116
  17. package/dist/commands/auth/generate.js.map +1 -1
  18. package/dist/commands/auth/login.js +35 -37
  19. package/dist/commands/auth/login.js.map +1 -1
  20. package/dist/commands/auth/logout.js +8 -2
  21. package/dist/commands/auth/logout.js.map +1 -1
  22. package/dist/commands/auth/migrate.js +37 -32
  23. package/dist/commands/auth/migrate.js.map +1 -1
  24. package/dist/commands/auth/whoami.js +21 -53
  25. package/dist/commands/auth/whoami.js.map +1 -1
  26. package/dist/commands/billing/cancel.js +22 -83
  27. package/dist/commands/billing/cancel.js.map +1 -1
  28. package/dist/commands/billing/status.js +29 -92
  29. package/dist/commands/billing/status.js.map +1 -1
  30. package/dist/commands/billing/subscribe.js +31 -86
  31. package/dist/commands/billing/subscribe.js.map +1 -1
  32. package/dist/keys/mainnet-alpha/prod/kms-encryption-public-key.pem +14 -0
  33. package/dist/keys/mainnet-alpha/prod/kms-signing-public-key.pem +4 -0
  34. package/dist/keys/sepolia/dev/kms-encryption-public-key.pem +14 -0
  35. package/dist/keys/sepolia/dev/kms-signing-public-key.pem +4 -0
  36. package/dist/keys/sepolia/prod/kms-encryption-public-key.pem +14 -0
  37. package/dist/keys/sepolia/prod/kms-signing-public-key.pem +4 -0
  38. package/dist/templates/Dockerfile.layered.tmpl +58 -0
  39. package/dist/templates/compute-source-env.sh.tmpl +110 -0
  40. package/package.json +4 -29
  41. package/VERSION +0 -2
  42. package/dist/commands/compute/app/configure/tls.js +0 -150
  43. package/dist/commands/compute/app/configure/tls.js.map +0 -1
  44. package/dist/commands/compute/app/create.js +0 -134
  45. package/dist/commands/compute/app/create.js.map +0 -1
  46. package/dist/commands/compute/app/deploy.js +0 -1081
  47. package/dist/commands/compute/app/deploy.js.map +0 -1
  48. package/dist/commands/compute/app/info.js +0 -809
  49. package/dist/commands/compute/app/info.js.map +0 -1
  50. package/dist/commands/compute/app/list.js +0 -570
  51. package/dist/commands/compute/app/list.js.map +0 -1
  52. package/dist/commands/compute/app/logs.js +0 -629
  53. package/dist/commands/compute/app/logs.js.map +0 -1
  54. package/dist/commands/compute/app/profile/set.js +0 -1072
  55. package/dist/commands/compute/app/profile/set.js.map +0 -1
  56. package/dist/commands/compute/app/start.js +0 -665
  57. package/dist/commands/compute/app/start.js.map +0 -1
  58. package/dist/commands/compute/app/stop.js +0 -665
  59. package/dist/commands/compute/app/stop.js.map +0 -1
  60. package/dist/commands/compute/app/terminate.js +0 -671
  61. package/dist/commands/compute/app/terminate.js.map +0 -1
  62. package/dist/commands/compute/app/upgrade.js +0 -1063
  63. package/dist/commands/compute/app/upgrade.js.map +0 -1
  64. package/dist/commands/compute/environment/list.js +0 -89
  65. package/dist/commands/compute/environment/list.js.map +0 -1
  66. package/dist/commands/compute/environment/set.js +0 -215
  67. package/dist/commands/compute/environment/set.js.map +0 -1
  68. package/dist/commands/compute/environment/show.js +0 -96
  69. package/dist/commands/compute/environment/show.js.map +0 -1
  70. package/dist/commands/compute/undelegate.js +0 -250
  71. package/dist/commands/compute/undelegate.js.map +0 -1
  72. package/dist/commands/upgrade.js +0 -91
  73. package/dist/commands/upgrade.js.map +0 -1
  74. package/dist/commands/version.js +0 -65
  75. package/dist/commands/version.js.map +0 -1
@@ -1,809 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/commands/compute/app/info.ts
4
- import { Command, Args, Flags as Flags2 } from "@oclif/core";
5
- import {
6
- getEnvironmentConfig as getEnvironmentConfig2,
7
- getAppLatestReleaseBlockNumbers,
8
- getBlockTimestamps,
9
- UserApiClient as UserApiClient3
10
- } from "@layr-labs/ecloud-sdk";
11
-
12
- // src/flags.ts
13
- import { Flags } from "@oclif/core";
14
-
15
- // src/utils/prompts.ts
16
- import { input, select, password, confirm as inquirerConfirm } from "@inquirer/prompts";
17
- import fs3 from "fs";
18
- import path3 from "path";
19
- import os3 from "os";
20
- import { isAddress as isAddress2 } from "viem";
21
- import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
22
- import {
23
- getEnvironmentConfig,
24
- getAvailableEnvironments,
25
- isEnvironmentAvailable,
26
- getAllAppsByDeveloper as getAllAppsByDeveloper2,
27
- getCategoryDescriptions,
28
- fetchTemplateCatalog,
29
- PRIMARY_LANGUAGES,
30
- validateAppName,
31
- validateImageReference,
32
- validateFilePath,
33
- validatePrivateKeyFormat,
34
- extractAppNameFromImage,
35
- UserApiClient as UserApiClient2
36
- } from "@layr-labs/ecloud-sdk";
37
-
38
- // src/utils/appResolver.ts
39
- import { isAddress } from "viem";
40
- import { privateKeyToAccount } from "viem/accounts";
41
- import {
42
- UserApiClient,
43
- getAllAppsByDeveloper
44
- } from "@layr-labs/ecloud-sdk";
45
-
46
- // src/utils/globalConfig.ts
47
- import * as fs from "fs";
48
- import * as path from "path";
49
- import * as os from "os";
50
- import { load as loadYaml, dump as dumpYaml } from "js-yaml";
51
- import { getBuildType } from "@layr-labs/ecloud-sdk";
52
- var GLOBAL_CONFIG_FILE = "config.yaml";
53
- var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
54
- function getGlobalConfigDir() {
55
- const configHome = process.env.XDG_CONFIG_HOME;
56
- let baseDir;
57
- if (configHome && path.isAbsolute(configHome)) {
58
- baseDir = configHome;
59
- } else {
60
- baseDir = path.join(os.homedir(), ".config");
61
- }
62
- const buildType = getBuildType();
63
- const buildSuffix = buildType === "dev" ? "-dev" : "";
64
- const configDirName = `ecloud${buildSuffix}`;
65
- return path.join(baseDir, configDirName);
66
- }
67
- function getGlobalConfigPath() {
68
- return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);
69
- }
70
- function loadGlobalConfig() {
71
- const configPath = getGlobalConfigPath();
72
- if (!fs.existsSync(configPath)) {
73
- return {
74
- first_run: true
75
- };
76
- }
77
- try {
78
- const content = fs.readFileSync(configPath, "utf-8");
79
- const config = loadYaml(content);
80
- return config || { first_run: true };
81
- } catch {
82
- return {
83
- first_run: true
84
- };
85
- }
86
- }
87
- function saveGlobalConfig(config) {
88
- const configPath = getGlobalConfigPath();
89
- const configDir = path.dirname(configPath);
90
- fs.mkdirSync(configDir, { recursive: true, mode: 493 });
91
- const content = dumpYaml(config, { lineWidth: -1 });
92
- fs.writeFileSync(configPath, content, { mode: 420 });
93
- }
94
- function getDefaultEnvironment() {
95
- const config = loadGlobalConfig();
96
- return config.default_environment;
97
- }
98
- function getProfileCache(environment) {
99
- const config = loadGlobalConfig();
100
- const cacheEntry = config.profile_cache?.[environment];
101
- if (!cacheEntry) {
102
- return null;
103
- }
104
- const now = Date.now();
105
- if (now - cacheEntry.updated_at > PROFILE_CACHE_TTL_MS) {
106
- return null;
107
- }
108
- return cacheEntry.profiles;
109
- }
110
- function setProfileCache(environment, profiles) {
111
- const config = loadGlobalConfig();
112
- if (!config.profile_cache) {
113
- config.profile_cache = {};
114
- }
115
- config.profile_cache[environment] = {
116
- updated_at: Date.now(),
117
- profiles
118
- };
119
- saveGlobalConfig(config);
120
- }
121
-
122
- // src/utils/appNames.ts
123
- import * as fs2 from "fs";
124
- import * as path2 from "path";
125
- import * as os2 from "os";
126
- import { load as loadYaml2, dump as dumpYaml2 } from "js-yaml";
127
- var CONFIG_DIR = path2.join(os2.homedir(), ".eigenx");
128
- var APPS_DIR = path2.join(CONFIG_DIR, "apps");
129
- var APP_REGISTRY_VERSION = "1.0.0";
130
- function getAppRegistryPath(environment) {
131
- return path2.join(APPS_DIR, `${environment}.yaml`);
132
- }
133
- function loadAppRegistry(environment) {
134
- const filePath = getAppRegistryPath(environment);
135
- if (!fs2.existsSync(filePath)) {
136
- return {
137
- version: APP_REGISTRY_VERSION,
138
- apps: {}
139
- };
140
- }
141
- try {
142
- const content = fs2.readFileSync(filePath, "utf-8");
143
- const registry = loadYaml2(content);
144
- if (!registry.apps) {
145
- registry.apps = {};
146
- }
147
- return registry;
148
- } catch {
149
- return {
150
- version: APP_REGISTRY_VERSION,
151
- apps: {}
152
- };
153
- }
154
- }
155
- function listApps(environment) {
156
- const registry = loadAppRegistry(environment);
157
- const result = {};
158
- for (const [name, app] of Object.entries(registry.apps)) {
159
- if (app?.app_id) {
160
- result[name] = String(app.app_id);
161
- }
162
- }
163
- return result;
164
- }
165
-
166
- // src/utils/appResolver.ts
167
- var CHUNK_SIZE = 10;
168
- async function getAppInfosChunked(userApiClient, appIds, addressCount) {
169
- if (appIds.length === 0) {
170
- return [];
171
- }
172
- const chunks = [];
173
- for (let i = 0; i < appIds.length; i += CHUNK_SIZE) {
174
- chunks.push(appIds.slice(i, i + CHUNK_SIZE));
175
- }
176
- const chunkResults = await Promise.all(
177
- chunks.map((chunk) => userApiClient.getInfos(chunk, addressCount))
178
- );
179
- return chunkResults.flat();
180
- }
181
-
182
- // src/utils/prompts.ts
183
- function addHexPrefix(value) {
184
- if (value.startsWith("0x")) {
185
- return value;
186
- }
187
- return `0x${value}`;
188
- }
189
- var ContractAppStatusStarted = 1;
190
- var ContractAppStatusStopped = 2;
191
- var ContractAppStatusTerminated = 3;
192
- var ContractAppStatusSuspended = 4;
193
- function getContractStatusString(status) {
194
- switch (status) {
195
- case ContractAppStatusStarted:
196
- return "Started";
197
- case ContractAppStatusStopped:
198
- return "Stopped";
199
- case ContractAppStatusTerminated:
200
- return "Terminated";
201
- case ContractAppStatusSuspended:
202
- return "Suspended";
203
- default:
204
- return "Unknown";
205
- }
206
- }
207
- function getStatusPriority(status, isExited) {
208
- if (isExited) {
209
- return 1;
210
- }
211
- switch (status) {
212
- case ContractAppStatusStarted:
213
- return 0;
214
- case ContractAppStatusStopped:
215
- return 2;
216
- case ContractAppStatusTerminated:
217
- return 3;
218
- default:
219
- return 4;
220
- }
221
- }
222
- function formatAppDisplay(environmentName, appID, profileName) {
223
- if (profileName) {
224
- return `${profileName} (${environmentName}:${appID})`;
225
- }
226
- return `${environmentName}:${appID}`;
227
- }
228
- async function getOrPromptAppID(appIDOrOptions, environment) {
229
- let options;
230
- if (environment !== void 0) {
231
- options = {
232
- appID: appIDOrOptions,
233
- environment
234
- };
235
- } else if (appIDOrOptions && typeof appIDOrOptions === "object" && "environment" in appIDOrOptions) {
236
- options = appIDOrOptions;
237
- } else {
238
- options = {
239
- appID: appIDOrOptions,
240
- environment: "sepolia"
241
- };
242
- }
243
- if (options.appID) {
244
- const normalized = typeof options.appID === "string" ? addHexPrefix(options.appID) : options.appID;
245
- if (isAddress2(normalized)) {
246
- return normalized;
247
- }
248
- const profileCache = getProfileCache(options.environment);
249
- if (profileCache) {
250
- const searchName = options.appID.toLowerCase();
251
- for (const [appId, name] of Object.entries(profileCache)) {
252
- if (name.toLowerCase() === searchName) {
253
- return appId;
254
- }
255
- }
256
- }
257
- const apps = listApps(options.environment);
258
- const foundAppID = apps[options.appID];
259
- if (foundAppID) {
260
- return addHexPrefix(foundAppID);
261
- }
262
- throw new Error(
263
- `App name '${options.appID}' not found in environment '${options.environment}'`
264
- );
265
- }
266
- return getAppIDInteractive(options);
267
- }
268
- async function getAppIDInteractive(options) {
269
- const action = options.action || "view";
270
- const environment = options.environment || "sepolia";
271
- const environmentConfig = getEnvironmentConfig(environment);
272
- if (!options.privateKey || !options.rpcUrl) {
273
- return getAppIDInteractiveFromRegistry(environment, action);
274
- }
275
- console.log(`
276
- Select an app to ${action}:
277
- `);
278
- const privateKeyHex = addHexPrefix(options.privateKey);
279
- const account = privateKeyToAccount2(privateKeyHex);
280
- const developerAddr = account.address;
281
- const { apps, appConfigs } = await getAllAppsByDeveloper2(
282
- options.rpcUrl,
283
- environmentConfig,
284
- developerAddr
285
- );
286
- if (apps.length === 0) {
287
- throw new Error("no apps found for your address");
288
- }
289
- const profileNames = {};
290
- let cachedProfiles = getProfileCache(environment);
291
- if (!cachedProfiles) {
292
- try {
293
- const userApiClient = new UserApiClient2(
294
- environmentConfig,
295
- options.privateKey,
296
- options.rpcUrl
297
- );
298
- const appInfos = await getAppInfosChunked(userApiClient, apps);
299
- const freshProfiles = {};
300
- for (const info of appInfos) {
301
- if (info.profile?.name) {
302
- const normalizedId = String(info.address).toLowerCase();
303
- freshProfiles[normalizedId] = info.profile.name;
304
- }
305
- }
306
- setProfileCache(environment, freshProfiles);
307
- cachedProfiles = freshProfiles;
308
- } catch {
309
- cachedProfiles = {};
310
- }
311
- }
312
- for (const [appId, name] of Object.entries(cachedProfiles)) {
313
- profileNames[appId.toLowerCase()] = name;
314
- }
315
- const localApps = listApps(environment);
316
- for (const [name, appID] of Object.entries(localApps)) {
317
- const normalizedID = String(appID).toLowerCase();
318
- if (!profileNames[normalizedID]) {
319
- profileNames[normalizedID] = name;
320
- }
321
- }
322
- const isEligible = (status) => {
323
- switch (action) {
324
- case "view":
325
- case "view info for":
326
- case "set profile for":
327
- return true;
328
- case "start":
329
- return status === ContractAppStatusStopped || status === ContractAppStatusSuspended;
330
- case "stop":
331
- return status === ContractAppStatusStarted;
332
- default:
333
- return status !== ContractAppStatusTerminated && status !== ContractAppStatusSuspended;
334
- }
335
- };
336
- const appItems = [];
337
- for (let i = 0; i < apps.length; i++) {
338
- const appAddr = apps[i];
339
- const config = appConfigs[i];
340
- const status = config.status;
341
- if (!isEligible(status)) {
342
- continue;
343
- }
344
- const statusStr = getContractStatusString(status);
345
- const profileName = profileNames[String(appAddr).toLowerCase()] || "";
346
- const displayName = formatAppDisplay(environmentConfig.name, appAddr, profileName);
347
- appItems.push({
348
- addr: appAddr,
349
- display: `${displayName} - ${statusStr}`,
350
- status,
351
- index: i
352
- });
353
- }
354
- appItems.sort((a, b) => {
355
- const aPriority = getStatusPriority(a.status, false);
356
- const bPriority = getStatusPriority(b.status, false);
357
- if (aPriority !== bPriority) {
358
- return aPriority - bPriority;
359
- }
360
- return b.index - a.index;
361
- });
362
- if (appItems.length === 0) {
363
- switch (action) {
364
- case "start":
365
- throw new Error("no startable apps found - only Stopped apps can be started");
366
- case "stop":
367
- throw new Error("no running apps found - only Running apps can be stopped");
368
- default:
369
- throw new Error("no active apps found");
370
- }
371
- }
372
- const choices = appItems.map((item) => ({
373
- name: item.display,
374
- value: item.addr
375
- }));
376
- const selected = await select({
377
- message: "Select app:",
378
- choices
379
- });
380
- return selected;
381
- }
382
- async function getAppIDInteractiveFromRegistry(environment, action) {
383
- const allApps = {};
384
- const cachedProfiles = getProfileCache(environment);
385
- if (cachedProfiles) {
386
- for (const [appId, name] of Object.entries(cachedProfiles)) {
387
- allApps[name] = appId;
388
- }
389
- }
390
- const localApps = listApps(environment);
391
- for (const [name, appId] of Object.entries(localApps)) {
392
- if (!allApps[name]) {
393
- allApps[name] = appId;
394
- }
395
- }
396
- if (Object.keys(allApps).length === 0) {
397
- console.log("\nNo apps found in registry.");
398
- console.log("You can enter an app ID (address) or app name.");
399
- console.log();
400
- const appIDInput = await input({
401
- message: "Enter app ID or name:",
402
- default: "",
403
- validate: (value) => {
404
- if (!value) {
405
- return "App ID or name cannot be empty";
406
- }
407
- const normalized2 = addHexPrefix(value);
408
- if (isAddress2(normalized2)) {
409
- return true;
410
- }
411
- return "Invalid app ID address";
412
- }
413
- });
414
- const normalized = addHexPrefix(appIDInput);
415
- if (isAddress2(normalized)) {
416
- return normalized;
417
- }
418
- throw new Error(`Invalid app ID address: ${appIDInput}`);
419
- }
420
- const choices = Object.entries(allApps).map(([name, appID]) => {
421
- const displayName = `${name} (${appID})`;
422
- return { name: displayName, value: appID };
423
- });
424
- choices.push({ name: "Enter custom app ID or name", value: "custom" });
425
- console.log(`
426
- Select an app to ${action}:`);
427
- const selected = await select({
428
- message: "Choose app:",
429
- choices
430
- });
431
- if (selected === "custom") {
432
- const appIDInput = await input({
433
- message: "Enter app ID or name:",
434
- default: "",
435
- validate: (value) => {
436
- if (!value) {
437
- return "App ID or name cannot be empty";
438
- }
439
- const normalized2 = addHexPrefix(value);
440
- if (isAddress2(normalized2)) {
441
- return true;
442
- }
443
- if (allApps[value]) {
444
- return true;
445
- }
446
- return "Invalid app ID or name not found";
447
- }
448
- });
449
- const normalized = addHexPrefix(appIDInput);
450
- if (isAddress2(normalized)) {
451
- return normalized;
452
- }
453
- const foundAppID = allApps[appIDInput];
454
- if (foundAppID) {
455
- return addHexPrefix(foundAppID);
456
- }
457
- throw new Error(`Failed to resolve app ID from input: ${appIDInput}`);
458
- }
459
- return addHexPrefix(selected);
460
- }
461
- async function getPrivateKeyInteractive(privateKey) {
462
- if (privateKey) {
463
- if (!validatePrivateKeyFormat(privateKey)) {
464
- throw new Error("Invalid private key format");
465
- }
466
- return privateKey;
467
- }
468
- const { getPrivateKeyWithSource } = await import("@layr-labs/ecloud-sdk");
469
- const result = await getPrivateKeyWithSource({ privateKey: void 0 });
470
- if (result) {
471
- return result.key;
472
- }
473
- const key = await password({
474
- message: "Enter private key:",
475
- mask: true,
476
- validate: (value) => {
477
- if (!value.trim()) {
478
- return "Private key is required";
479
- }
480
- if (!validatePrivateKeyFormat(value)) {
481
- return "Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)";
482
- }
483
- return true;
484
- }
485
- });
486
- return key.trim();
487
- }
488
- async function getEnvironmentInteractive(environment) {
489
- if (environment) {
490
- try {
491
- getEnvironmentConfig(environment);
492
- if (!isEnvironmentAvailable(environment)) {
493
- throw new Error(`Environment ${environment} is not available in this build`);
494
- }
495
- return environment;
496
- } catch {
497
- }
498
- }
499
- const availableEnvs = getAvailableEnvironments();
500
- let defaultEnv;
501
- const configDefaultEnv = getDefaultEnvironment();
502
- if (configDefaultEnv && availableEnvs.includes(configDefaultEnv)) {
503
- try {
504
- getEnvironmentConfig(configDefaultEnv);
505
- defaultEnv = configDefaultEnv;
506
- } catch {
507
- }
508
- }
509
- const choices = [];
510
- if (availableEnvs.includes("sepolia")) {
511
- choices.push({ name: "sepolia - Ethereum Sepolia testnet", value: "sepolia" });
512
- }
513
- if (availableEnvs.includes("sepolia-dev")) {
514
- choices.push({ name: "sepolia-dev - Ethereum Sepolia testnet (dev)", value: "sepolia-dev" });
515
- }
516
- if (availableEnvs.includes("mainnet-alpha")) {
517
- choices.push({
518
- name: "mainnet-alpha - Ethereum mainnet (\u26A0\uFE0F uses real funds)",
519
- value: "mainnet-alpha"
520
- });
521
- }
522
- if (choices.length === 0) {
523
- throw new Error("No environments available in this build");
524
- }
525
- const env = await select({
526
- message: "Select environment:",
527
- choices,
528
- default: defaultEnv
529
- });
530
- return env;
531
- }
532
- var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
533
-
534
- // src/flags.ts
535
- var commonFlags = {
536
- environment: Flags.string({
537
- required: false,
538
- description: "Deployment environment to use",
539
- env: "ECLOUD_ENV"
540
- }),
541
- "private-key": Flags.string({
542
- required: false,
543
- description: "Private key for signing transactions",
544
- env: "ECLOUD_PRIVATE_KEY"
545
- }),
546
- "rpc-url": Flags.string({
547
- required: false,
548
- description: "RPC URL to connect to blockchain",
549
- env: "ECLOUD_RPC_URL"
550
- }),
551
- verbose: Flags.boolean({
552
- required: false,
553
- description: "Enable verbose logging (default: false)",
554
- default: false
555
- })
556
- };
557
- async function validateCommonFlags(flags) {
558
- if (!flags["environment"]) {
559
- flags["environment"] = getDefaultEnvironment();
560
- }
561
- flags["environment"] = await getEnvironmentInteractive(flags["environment"]);
562
- flags["private-key"] = await getPrivateKeyInteractive(flags["private-key"]);
563
- return flags;
564
- }
565
-
566
- // src/utils/format.ts
567
- import chalk from "chalk";
568
- function formatBytes(bytes) {
569
- if (bytes === 0) return "0 B";
570
- const k = 1024;
571
- const sizes = ["B", "KB", "MB", "GB", "TB"];
572
- const i = Math.floor(Math.log(bytes) / Math.log(k));
573
- return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
574
- }
575
- function formatStatus(status) {
576
- switch (status.toLowerCase()) {
577
- case "running":
578
- case "started":
579
- return chalk.green(status);
580
- case "stopped":
581
- return chalk.yellow(status);
582
- case "terminated":
583
- return chalk.red(status);
584
- case "suspended":
585
- return chalk.red(status);
586
- case "deploying":
587
- case "upgrading":
588
- case "resuming":
589
- case "stopping":
590
- return chalk.cyan(status);
591
- case "failed":
592
- return chalk.red(status);
593
- default:
594
- return chalk.gray(status);
595
- }
596
- }
597
- function formatAppDisplay2(options) {
598
- const { appInfo, appName, status, releaseTimestamp, showProfileDetails = false } = options;
599
- const displayName = appName || appInfo.profile?.name;
600
- const name = displayName ? chalk.cyan(displayName) : chalk.gray("(unnamed)");
601
- const id = chalk.gray(appInfo.address);
602
- const releaseTime = releaseTimestamp ? chalk.gray(new Date(releaseTimestamp * 1e3).toISOString().replace("T", " ").slice(0, 19)) : chalk.gray("-");
603
- const statusStr = status || appInfo.status;
604
- const formattedStatus = formatStatus(statusStr);
605
- const instance = appInfo.machineType && appInfo.machineType !== "No instance assigned" ? chalk.gray(appInfo.machineType) : chalk.gray("-");
606
- const ip = appInfo.ip && appInfo.ip !== "No IP assigned" ? chalk.white(appInfo.ip) : chalk.gray("No IP assigned");
607
- const metrics = appInfo.metrics;
608
- const cpu = metrics?.cpu_utilization_percent !== void 0 ? chalk.white(`${metrics.cpu_utilization_percent.toFixed(1)}%`) : chalk.gray("-");
609
- const memory = metrics?.memory_utilization_percent !== void 0 ? chalk.white(`${metrics.memory_utilization_percent.toFixed(1)}%`) : chalk.gray("-");
610
- const memoryUsage = metrics?.memory_used_bytes !== void 0 && metrics?.memory_total_bytes !== void 0 ? chalk.gray(
611
- `(${formatBytes(metrics.memory_used_bytes)} / ${formatBytes(metrics.memory_total_bytes)})`
612
- ) : "";
613
- const evmAddresses = (appInfo.evmAddresses || []).map((addr) => ({
614
- address: addr.address,
615
- path: addr.derivationPath
616
- }));
617
- const solanaAddresses = (appInfo.solanaAddresses || []).map((addr) => ({
618
- address: addr.address,
619
- path: addr.derivationPath
620
- }));
621
- let profile;
622
- if (showProfileDetails && appInfo.profile) {
623
- const p = appInfo.profile;
624
- if (p.website || p.description || p.xURL) {
625
- profile = {
626
- website: p.website,
627
- description: p.description,
628
- xURL: p.xURL
629
- };
630
- }
631
- }
632
- return {
633
- name,
634
- id,
635
- releaseTime,
636
- status: formattedStatus,
637
- instance,
638
- ip,
639
- cpu,
640
- memory,
641
- memoryUsage,
642
- evmAddresses,
643
- solanaAddresses,
644
- profile
645
- };
646
- }
647
- function printAppDisplay(display, log, indent = " ", options = {}) {
648
- const { singleAddress = false, showProfile = false } = options;
649
- log(`${indent}ID: ${display.id}`);
650
- log(`${indent}Release Time: ${display.releaseTime}`);
651
- log(`${indent}Status: ${display.status}`);
652
- log(`${indent}Instance: ${display.instance}`);
653
- log(`${indent}IP: ${display.ip}`);
654
- log(`${indent}CPU: ${display.cpu}`);
655
- log(`${indent}Memory: ${display.memory} ${display.memoryUsage}`);
656
- if (display.evmAddresses.length > 0) {
657
- const addrs = singleAddress ? display.evmAddresses.slice(0, 1) : display.evmAddresses;
658
- for (let i = 0; i < addrs.length; i++) {
659
- const addr = addrs[i];
660
- const label = i === 0 ? "EVM Address:" : " ";
661
- log(`${indent}${label} ${chalk.gray(`${addr.address} (path: ${addr.path})`)}`);
662
- }
663
- } else {
664
- log(`${indent}EVM Address: ${chalk.gray("-")}`);
665
- }
666
- if (display.solanaAddresses.length > 0) {
667
- const addrs = singleAddress ? display.solanaAddresses.slice(0, 1) : display.solanaAddresses;
668
- for (let i = 0; i < addrs.length; i++) {
669
- const addr = addrs[i];
670
- const label = i === 0 ? "Solana Address:" : " ";
671
- log(`${indent}${label} ${chalk.gray(`${addr.address} (path: ${addr.path})`)}`);
672
- }
673
- } else {
674
- log(`${indent}Solana Address: ${chalk.gray("-")}`);
675
- }
676
- if (showProfile && display.profile) {
677
- log(
678
- chalk.gray(`${indent}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`)
679
- );
680
- if (display.profile.website) {
681
- log(`${indent}Website: ${chalk.gray(display.profile.website)}`);
682
- }
683
- if (display.profile.description) {
684
- log(`${indent}Description: ${chalk.gray(display.profile.description)}`);
685
- }
686
- if (display.profile.xURL) {
687
- log(`${indent}X (Twitter): ${chalk.gray(display.profile.xURL)}`);
688
- }
689
- }
690
- }
691
-
692
- // src/commands/compute/app/info.ts
693
- import chalk2 from "chalk";
694
- var AppInfo2 = class _AppInfo extends Command {
695
- static description = "Show detailed information for a specific app";
696
- static args = {
697
- "app-id": Args.string({
698
- description: "App ID or name",
699
- required: false
700
- })
701
- };
702
- static flags = {
703
- ...commonFlags,
704
- watch: Flags2.boolean({
705
- description: "Watch mode: refresh every 5 seconds",
706
- char: "w",
707
- default: false
708
- }),
709
- "address-count": Flags2.integer({
710
- description: "Number of derived addresses to show",
711
- default: 1
712
- })
713
- };
714
- async run() {
715
- const { args, flags } = await this.parse(_AppInfo);
716
- const validatedFlags = await validateCommonFlags(flags);
717
- const environment = validatedFlags.environment || "sepolia";
718
- const environmentConfig = getEnvironmentConfig2(environment);
719
- const rpcUrl = validatedFlags["rpc-url"] || environmentConfig.defaultRPCURL;
720
- const privateKey = validatedFlags["private-key"];
721
- const appID = await getOrPromptAppID({
722
- appID: args["app-id"],
723
- environment,
724
- privateKey,
725
- rpcUrl,
726
- action: "view info for"
727
- });
728
- const userApiClient = new UserApiClient3(environmentConfig, privateKey, rpcUrl);
729
- if (flags.watch) {
730
- await this.watchMode(appID, userApiClient, rpcUrl, environmentConfig, flags["address-count"]);
731
- } else {
732
- await this.displayAppInfo(
733
- appID,
734
- userApiClient,
735
- rpcUrl,
736
- environmentConfig,
737
- flags["address-count"]
738
- );
739
- }
740
- }
741
- async displayAppInfo(appID, userApiClient, rpcUrl, environmentConfig, addressCount, clearScreen = false) {
742
- const [appInfos, releaseBlockNumbers] = await Promise.all([
743
- userApiClient.getInfos([appID], addressCount).catch((err) => {
744
- this.warn(`Could not fetch app info: ${err}`);
745
- return [];
746
- }),
747
- getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, [appID]).catch((err) => {
748
- this.warn(`Could not fetch release block numbers: ${err}`);
749
- return /* @__PURE__ */ new Map();
750
- })
751
- ]);
752
- const appInfo = appInfos[0];
753
- if (!appInfo) {
754
- this.error(`App ${appID} not found`);
755
- }
756
- const releaseBlockNumber = releaseBlockNumbers.get(appID);
757
- let releaseTimestamp;
758
- if (releaseBlockNumber && releaseBlockNumber > 0) {
759
- const blockTimestamps = await getBlockTimestamps(rpcUrl, environmentConfig, [
760
- releaseBlockNumber
761
- ]).catch(() => /* @__PURE__ */ new Map());
762
- releaseTimestamp = blockTimestamps.get(releaseBlockNumber);
763
- }
764
- if (clearScreen) {
765
- console.clear();
766
- }
767
- const display = formatAppDisplay2({
768
- appInfo,
769
- releaseTimestamp,
770
- showProfileDetails: true
771
- });
772
- console.log();
773
- const appName = appInfo.profile?.name;
774
- const nameDisplay = appName ? chalk2.cyan.bold(appName) : chalk2.gray("(unnamed)");
775
- this.log(`App: ${nameDisplay}`);
776
- printAppDisplay(display, this.log.bind(this), " ", {
777
- singleAddress: false,
778
- showProfile: true
779
- });
780
- console.log();
781
- }
782
- async watchMode(appID, userApiClient, rpcUrl, environmentConfig, addressCount) {
783
- const REFRESH_INTERVAL_SECONDS = 5;
784
- await this.displayAppInfo(appID, userApiClient, rpcUrl, environmentConfig, addressCount, true);
785
- while (true) {
786
- await showCountdown(REFRESH_INTERVAL_SECONDS);
787
- await this.displayAppInfo(
788
- appID,
789
- userApiClient,
790
- rpcUrl,
791
- environmentConfig,
792
- addressCount,
793
- true
794
- );
795
- }
796
- }
797
- };
798
- async function showCountdown(seconds) {
799
- for (let i = seconds; i >= 0; i--) {
800
- process.stdout.write(chalk2.gray(`\rRefreshing in ${i}...`));
801
- if (i > 0) {
802
- await new Promise((resolve) => setTimeout(resolve, 1e3));
803
- }
804
- }
805
- }
806
- export {
807
- AppInfo2 as default
808
- };
809
- //# sourceMappingURL=info.js.map