@layr-labs/ecloud-cli 0.4.3 → 0.5.0-dev.3

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 (66) hide show
  1. package/VERSION +2 -2
  2. package/dist/commands/auth/login.js +84 -54
  3. package/dist/commands/auth/login.js.map +1 -1
  4. package/dist/commands/auth/migrate.js +66 -30
  5. package/dist/commands/auth/migrate.js.map +1 -1
  6. package/dist/commands/auth/whoami.js +0 -1
  7. package/dist/commands/auth/whoami.js.map +1 -1
  8. package/dist/commands/billing/__tests__/status.test.js +11 -9
  9. package/dist/commands/billing/__tests__/status.test.js.map +1 -1
  10. package/dist/commands/billing/__tests__/subscribe.test.js +11 -9
  11. package/dist/commands/billing/__tests__/subscribe.test.js.map +1 -1
  12. package/dist/commands/billing/__tests__/top-up.test.js +11 -9
  13. package/dist/commands/billing/__tests__/top-up.test.js.map +1 -1
  14. package/dist/commands/billing/cancel.js +11 -9
  15. package/dist/commands/billing/cancel.js.map +1 -1
  16. package/dist/commands/billing/status.js +11 -9
  17. package/dist/commands/billing/status.js.map +1 -1
  18. package/dist/commands/billing/subscribe.js +11 -9
  19. package/dist/commands/billing/subscribe.js.map +1 -1
  20. package/dist/commands/billing/top-up.js +11 -9
  21. package/dist/commands/billing/top-up.js.map +1 -1
  22. package/dist/commands/compute/app/configure/tls.js +138 -50
  23. package/dist/commands/compute/app/configure/tls.js.map +1 -1
  24. package/dist/commands/compute/app/create.js +0 -1
  25. package/dist/commands/compute/app/create.js.map +1 -1
  26. package/dist/commands/compute/app/deploy.js +76 -29
  27. package/dist/commands/compute/app/deploy.js.map +1 -1
  28. package/dist/commands/compute/app/info.js +17 -17
  29. package/dist/commands/compute/app/info.js.map +1 -1
  30. package/dist/commands/compute/app/list.js +12 -10
  31. package/dist/commands/compute/app/list.js.map +1 -1
  32. package/dist/commands/compute/app/logs.js +17 -17
  33. package/dist/commands/compute/app/logs.js.map +1 -1
  34. package/dist/commands/compute/app/profile/set.js +18 -17
  35. package/dist/commands/compute/app/profile/set.js.map +1 -1
  36. package/dist/commands/compute/app/releases.js +17 -17
  37. package/dist/commands/compute/app/releases.js.map +1 -1
  38. package/dist/commands/compute/app/start.js +29 -20
  39. package/dist/commands/compute/app/start.js.map +1 -1
  40. package/dist/commands/compute/app/stop.js +29 -20
  41. package/dist/commands/compute/app/stop.js.map +1 -1
  42. package/dist/commands/compute/app/terminate.js +22 -17
  43. package/dist/commands/compute/app/terminate.js.map +1 -1
  44. package/dist/commands/compute/app/upgrade.js +115 -32
  45. package/dist/commands/compute/app/upgrade.js.map +1 -1
  46. package/dist/commands/compute/build/info.js +12 -10
  47. package/dist/commands/compute/build/info.js.map +1 -1
  48. package/dist/commands/compute/build/list.js +12 -10
  49. package/dist/commands/compute/build/list.js.map +1 -1
  50. package/dist/commands/compute/build/logs.js +12 -10
  51. package/dist/commands/compute/build/logs.js.map +1 -1
  52. package/dist/commands/compute/build/status.js +12 -10
  53. package/dist/commands/compute/build/status.js.map +1 -1
  54. package/dist/commands/compute/build/submit.js +13 -10
  55. package/dist/commands/compute/build/submit.js.map +1 -1
  56. package/dist/commands/compute/build/verify.js +12 -10
  57. package/dist/commands/compute/build/verify.js.map +1 -1
  58. package/dist/commands/compute/environment/set.js +12 -11
  59. package/dist/commands/compute/environment/set.js.map +1 -1
  60. package/dist/commands/compute/undelegate.js +12 -10
  61. package/dist/commands/compute/undelegate.js.map +1 -1
  62. package/dist/hooks/init/__tests__/version-check.test.js +1 -1
  63. package/dist/hooks/init/__tests__/version-check.test.js.map +1 -1
  64. package/dist/hooks/init/version-check.js +1 -1
  65. package/dist/hooks/init/version-check.js.map +1 -1
  66. package/package.json +26 -21
@@ -131,6 +131,18 @@ function setProfileCache(environment, profiles) {
131
131
  };
132
132
  saveGlobalConfig(config);
133
133
  }
134
+ function invalidateProfileCache(environment) {
135
+ const config = loadGlobalConfig();
136
+ if (!config.profile_cache) {
137
+ return;
138
+ }
139
+ if (environment) {
140
+ delete config.profile_cache[environment];
141
+ } else {
142
+ config.profile_cache = {};
143
+ }
144
+ saveGlobalConfig(config);
145
+ }
134
146
  function getOrCreateUserUUID() {
135
147
  const config = loadGlobalConfig();
136
148
  if (config.user_uuid) {
@@ -214,7 +226,6 @@ import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
214
226
  import {
215
227
  getEnvironmentConfig as getEnvironmentConfig2,
216
228
  getAvailableEnvironments,
217
- isEnvironmentAvailable,
218
229
  getAllAppsByDeveloper as getAllAppsByDeveloper2,
219
230
  getCategoryDescriptions,
220
231
  fetchTemplateCatalog,
@@ -312,7 +323,7 @@ function listApps(environment) {
312
323
 
313
324
  // src/utils/version.ts
314
325
  function getCliVersion() {
315
- return true ? "0.4.3" : "0.0.0";
326
+ return true ? "0.5.0-dev.3" : "0.0.0";
316
327
  }
317
328
  function getClientId() {
318
329
  return `ecloud-cli/v${getCliVersion()}`;
@@ -342,6 +353,13 @@ function addHexPrefix2(value) {
342
353
  }
343
354
  return `0x${value}`;
344
355
  }
356
+ function ensureInteractive(missingFlagHint) {
357
+ if (!process.stdin.isTTY) {
358
+ throw new Error(
359
+ `Cannot prompt in non-interactive mode. Provide ${missingFlagHint} via CLI flags or environment variables.`
360
+ );
361
+ }
362
+ }
345
363
  async function getDockerfileInteractive(dockerfilePath) {
346
364
  if (dockerfilePath) {
347
365
  return dockerfilePath;
@@ -351,6 +369,7 @@ async function getDockerfileInteractive(dockerfilePath) {
351
369
  if (!fs3.existsSync(dockerfilePath_resolved)) {
352
370
  return "";
353
371
  }
372
+ ensureInteractive("--dockerfile or --image-ref");
354
373
  console.log(`
355
374
  Found Dockerfile in ${cwd}`);
356
375
  const choice = await select({
@@ -393,10 +412,12 @@ function detectGitRepoInfo() {
393
412
  return {};
394
413
  }
395
414
  }
396
- async function promptUseVerifiableBuild() {
415
+ async function promptUseVerifiableBuild(force = false) {
416
+ if (force) return false;
397
417
  return confirmWithDefault("Build from verifiable source?", false);
398
418
  }
399
419
  async function promptVerifiableSourceType() {
420
+ ensureInteractive("--verifiable with --repo/--commit or --image-ref");
400
421
  return select({
401
422
  message: "Choose verifiable source type:",
402
423
  choices: [
@@ -406,6 +427,7 @@ async function promptVerifiableSourceType() {
406
427
  });
407
428
  }
408
429
  async function promptVerifiableGitSourceInputs() {
430
+ ensureInteractive("--repo, --commit");
409
431
  const detected = detectGitRepoInfo();
410
432
  const repoUrl = (await input({
411
433
  message: "Enter public git repository URL:",
@@ -487,6 +509,7 @@ async function promptVerifiableGitSourceInputs() {
487
509
  };
488
510
  }
489
511
  async function promptVerifiablePrebuiltImageRef() {
512
+ ensureInteractive("--image-ref");
490
513
  const ref = await input({
491
514
  message: "Enter prebuilt verifiable image ref:",
492
515
  default: "docker.io/eigenlayer/eigencloud-containers:",
@@ -577,6 +600,7 @@ async function getAvailableRegistries() {
577
600
  const auths = config.auths || {};
578
601
  const credsStore = config.credsStore;
579
602
  const gcrProjects = /* @__PURE__ */ new Map();
603
+ const dockerhubUsers = /* @__PURE__ */ new Map();
580
604
  const registries = [];
581
605
  for (const [registry, auth] of Object.entries(auths)) {
582
606
  const authData = auth;
@@ -617,8 +641,17 @@ async function getAvailableRegistries() {
617
641
  }
618
642
  continue;
619
643
  }
644
+ if (registryType === "dockerhub") {
645
+ if (!dockerhubUsers.has(username)) {
646
+ dockerhubUsers.set(username, info);
647
+ }
648
+ continue;
649
+ }
620
650
  registries.push(info);
621
651
  }
652
+ for (const dhInfo of Array.from(dockerhubUsers.values())) {
653
+ registries.push(dhInfo);
654
+ }
622
655
  for (const gcrInfo of Array.from(gcrProjects.values())) {
623
656
  registries.push(gcrInfo);
624
657
  }
@@ -724,6 +757,7 @@ async function getImageReferenceInteractive(imageRef, buildFromDockerfile = fals
724
757
  if (imageRef) {
725
758
  return imageRef;
726
759
  }
760
+ ensureInteractive("--image-ref");
727
761
  const registries = await getAvailableRegistries();
728
762
  const appName = getDefaultAppName();
729
763
  if (buildFromDockerfile) {
@@ -759,6 +793,7 @@ async function getEnvFileInteractive(envFilePath) {
759
793
  if (fs3.existsSync(".env")) {
760
794
  return ".env";
761
795
  }
796
+ ensureInteractive("--env-file");
762
797
  console.log("\nEnvironment file not found.");
763
798
  console.log("Environment files contain variables like RPC_URL, etc.");
764
799
  const choice = await select({
@@ -810,14 +845,19 @@ async function getInstanceTypeInteractive(instanceType, defaultSKU, availableTyp
810
845
  const validSKUs = availableTypes.map((t) => t.sku).join(", ");
811
846
  throw new Error(`Invalid instance-type: ${instanceType} (must be one of: ${validSKUs})`);
812
847
  }
848
+ ensureInteractive("--instance-type");
813
849
  const isCurrentType = defaultSKU !== "";
814
850
  const hasPricing = availableTypes.some((t) => t.monthly_price_usd != null);
815
851
  if (hasPricing) {
816
852
  console.log("\nPay for what you use \u2014 no upfront costs, per-hour billing.\n");
817
853
  console.log(` ${chalk.bold("Shielded VM (vTPM)")}: Verified boot and runtime attestation.`);
818
- console.log(` ${chalk.bold("SEV-SNP (TEE)")}: Verified boot, runtime attestation, and hardware-encrypted memory (AMD).`);
819
- console.log(` ${chalk.bold("TDX (TEE)")}: Verified boot, runtime attestation, and hardware-encrypted memory (Intel).
820
- `);
854
+ console.log(
855
+ ` ${chalk.bold("SEV-SNP (TEE)")}: Verified boot, runtime attestation, and hardware-encrypted memory (AMD).`
856
+ );
857
+ console.log(
858
+ ` ${chalk.bold("TDX (TEE)")}: Verified boot, runtime attestation, and hardware-encrypted memory (Intel).
859
+ `
860
+ );
821
861
  }
822
862
  if (isCurrentType && defaultSKU) {
823
863
  console.log(`Current instance type: ${defaultSKU}
@@ -851,6 +891,7 @@ async function getLogSettingsInteractive(logVisibility) {
851
891
  );
852
892
  }
853
893
  }
894
+ ensureInteractive("--log-visibility");
854
895
  const choice = await select({
855
896
  message: "Do you want to view your app's logs?",
856
897
  choices: [
@@ -944,9 +985,10 @@ async function getOrPromptAppID(appIDOrOptions, environment) {
944
985
  return addHexPrefix2(foundAppID);
945
986
  }
946
987
  throw new Error(
947
- `App name '${options.appID}' not found in environment '${options.environment}'`
988
+ `App name '${options.appID}' not found in environment '${options.environment}'. Name lookup uses a local cache that may be stale. Try using the app ID (0x...) directly, or run 'ecloud compute app list' to refresh the cache.`
948
989
  );
949
990
  }
991
+ ensureInteractive("app-id argument");
950
992
  return getAppIDInteractive(options);
951
993
  }
952
994
  async function getAppIDInteractive(options) {
@@ -979,12 +1021,9 @@ Select an app to ${action}:
979
1021
  let cachedProfiles = getProfileCache(environment);
980
1022
  if (!cachedProfiles) {
981
1023
  try {
982
- const userApiClient = new UserApiClient2(
983
- environmentConfig,
984
- walletClient,
985
- publicClient,
986
- { clientId: getClientId() }
987
- );
1024
+ const userApiClient = new UserApiClient2(environmentConfig, walletClient, publicClient, {
1025
+ clientId: getClientId()
1026
+ });
988
1027
  const appInfos = await getAppInfosChunked(userApiClient, apps);
989
1028
  const freshProfiles = {};
990
1029
  for (const info of appInfos) {
@@ -1184,6 +1223,7 @@ async function getResourceUsageMonitoringInteractive(resourceUsageMonitoring) {
1184
1223
  );
1185
1224
  }
1186
1225
  }
1226
+ ensureInteractive("--resource-usage-monitoring");
1187
1227
  const choice = await select({
1188
1228
  message: "Show resource usage (CPU/memory) for your app?",
1189
1229
  choices: [
@@ -1197,6 +1237,11 @@ async function confirm(prompt) {
1197
1237
  return confirmWithDefault(prompt, false);
1198
1238
  }
1199
1239
  async function confirmWithDefault(prompt, defaultValue = false) {
1240
+ if (!process.stdin.isTTY) {
1241
+ throw new Error(
1242
+ `Cannot confirm "${prompt}" in non-interactive mode. Use --force to skip confirmation prompts.`
1243
+ );
1244
+ }
1200
1245
  return await inquirerConfirm({
1201
1246
  message: prompt,
1202
1247
  default: defaultValue
@@ -1214,6 +1259,7 @@ async function getPrivateKeyInteractive(privateKey) {
1214
1259
  if (result) {
1215
1260
  return result.key;
1216
1261
  }
1262
+ ensureInteractive("--private-key or ECLOUD_PRIVATE_KEY");
1217
1263
  const key = await password({
1218
1264
  message: "Enter private key:",
1219
1265
  mask: true,
@@ -1231,15 +1277,10 @@ async function getPrivateKeyInteractive(privateKey) {
1231
1277
  }
1232
1278
  async function getEnvironmentInteractive(environment) {
1233
1279
  if (environment) {
1234
- try {
1235
- getEnvironmentConfig2(environment);
1236
- if (!isEnvironmentAvailable(environment)) {
1237
- throw new Error(`Environment ${environment} is not available in this build`);
1238
- }
1239
- return environment;
1240
- } catch {
1241
- }
1280
+ getEnvironmentConfig2(environment);
1281
+ return environment;
1242
1282
  }
1283
+ ensureInteractive("--environment or ECLOUD_ENV");
1243
1284
  const availableEnvs = getAvailableEnvironments();
1244
1285
  let defaultEnv;
1245
1286
  const configDefaultEnv = getDefaultEnvironment();
@@ -1655,6 +1696,11 @@ var AppUpgrade = class _AppUpgrade extends Command {
1655
1696
  };
1656
1697
  static flags = {
1657
1698
  ...commonFlags,
1699
+ name: Flags2.string({
1700
+ required: false,
1701
+ description: "Update the app's profile name after upgrade",
1702
+ env: "ECLOUD_NAME"
1703
+ }),
1658
1704
  dockerfile: Flags2.string({
1659
1705
  required: false,
1660
1706
  description: "Path to Dockerfile",
@@ -1724,6 +1770,10 @@ var AppUpgrade = class _AppUpgrade extends Command {
1724
1770
  description: "Optional path to Caddyfile inside the repo (relative to build context). If omitted, auto-detected from env file TLS settings",
1725
1771
  required: false,
1726
1772
  env: "ECLOUD_BUILD_CADDYFILE"
1773
+ }),
1774
+ force: Flags2.boolean({
1775
+ description: "Skip all confirmation prompts",
1776
+ default: false
1727
1777
  })
1728
1778
  };
1729
1779
  async run() {
@@ -1780,7 +1830,7 @@ var AppUpgrade = class _AppUpgrade extends Command {
1780
1830
  }
1781
1831
  } else {
1782
1832
  if (!flags.dockerfile) {
1783
- const useVerifiable = await promptUseVerifiableBuild();
1833
+ const useVerifiable = await promptUseVerifiableBuild(flags.force);
1784
1834
  if (useVerifiable) {
1785
1835
  const sourceType = await promptVerifiableSourceType();
1786
1836
  verifiableMode = sourceType;
@@ -1870,12 +1920,9 @@ var AppUpgrade = class _AppUpgrade extends Command {
1870
1920
  });
1871
1921
  let currentInstanceType = "";
1872
1922
  try {
1873
- const userApiClient = new UserApiClient3(
1874
- environmentConfig,
1875
- walletClient,
1876
- publicClient,
1877
- { clientId: getClientId() }
1878
- );
1923
+ const userApiClient = new UserApiClient3(environmentConfig, walletClient, publicClient, {
1924
+ clientId: getClientId()
1925
+ });
1879
1926
  const infos = await userApiClient.getInfos([appID], 1);
1880
1927
  if (infos.length > 0) {
1881
1928
  currentInstanceType = infos[0].machineType || "";
@@ -1917,15 +1964,19 @@ var AppUpgrade = class _AppUpgrade extends Command {
1917
1964
  });
1918
1965
  const finalTx = await applyTxOverrides(gasEstimate, flags, { publicClient, address });
1919
1966
  if (flags["max-fee-per-gas"] || flags["max-priority-fee"]) {
1920
- this.log(chalk2.yellow(`
1921
- Gas override active \u2014 max fee: ${flags["max-fee-per-gas"] || "estimated"} gwei, priority fee: ${flags["max-priority-fee"] || "estimated"} gwei`));
1967
+ this.log(
1968
+ chalk2.yellow(
1969
+ `
1970
+ Gas override active \u2014 max fee: ${flags["max-fee-per-gas"] || "estimated"} gwei, priority fee: ${flags["max-priority-fee"] || "estimated"} gwei`
1971
+ )
1972
+ );
1922
1973
  }
1923
1974
  if (finalTx.nonce != null) {
1924
1975
  this.log(chalk2.yellow(`Nonce override active \u2014 nonce: ${finalTx.nonce}`));
1925
1976
  }
1926
1977
  this.log(`
1927
1978
  Estimated transaction cost: ${chalk2.cyan(finalTx.maxCostEth)} ETH`);
1928
- if (isMainnet(environmentConfig)) {
1979
+ if (isMainnet(environmentConfig) && !flags.force) {
1929
1980
  const confirmed = await confirm(`Continue with upgrade?`);
1930
1981
  if (!confirmed) {
1931
1982
  this.log(`
@@ -1945,9 +1996,39 @@ ${chalk2.gray(`Upgrade cancelled`)}`);
1945
1996
  `
1946
1997
  \u2705 ${chalk2.green(`App upgraded successfully ${chalk2.bold(`(id: ${res.appId}, image: ${res.imageRef})`)}`)}`
1947
1998
  );
1999
+ if (flags.name) {
2000
+ try {
2001
+ const { publicClient: publicClient2, walletClient: walletClient2 } = createViemClients({
2002
+ privateKey,
2003
+ rpcUrl,
2004
+ environment
2005
+ });
2006
+ const userApiClient = new UserApiClient3(environmentConfig, walletClient2, publicClient2, {
2007
+ clientId: getClientId()
2008
+ });
2009
+ const infos = await userApiClient.getInfos([res.appId], 1);
2010
+ const existing = infos[0]?.profile;
2011
+ await compute.app.setProfile(res.appId, {
2012
+ name: flags.name,
2013
+ website: existing?.website,
2014
+ description: existing?.description,
2015
+ xURL: existing?.xURL
2016
+ });
2017
+ invalidateProfileCache(environment);
2018
+ this.log(`\u2713 Profile name updated to "${flags.name}"`);
2019
+ } catch (err) {
2020
+ this.warn(`Upgrade succeeded but failed to update profile name: ${err.message}`);
2021
+ }
2022
+ }
1948
2023
  const dashboardUrl = getDashboardUrl(environment, res.appId);
1949
2024
  this.log(`
1950
2025
  ${chalk2.gray("View your app:")} ${chalk2.blue.underline(dashboardUrl)}`);
2026
+ this.log(
2027
+ chalk2.gray(
2028
+ `
2029
+ Note: "Running" means the container started. Verify your app is serving traffic before considering the upgrade complete.`
2030
+ )
2031
+ );
1951
2032
  });
1952
2033
  }
1953
2034
  };
@@ -1958,7 +2039,9 @@ async function fetchAvailableInstanceTypes(environment, environmentConfig, priva
1958
2039
  rpcUrl,
1959
2040
  environment
1960
2041
  });
1961
- const userApiClient = new UserApiClient3(environmentConfig, walletClient, publicClient, { clientId: getClientId() });
2042
+ const userApiClient = new UserApiClient3(environmentConfig, walletClient, publicClient, {
2043
+ clientId: getClientId()
2044
+ });
1962
2045
  const skuList = await userApiClient.getSKUs();
1963
2046
  if (skuList.skus.length === 0) {
1964
2047
  throw new Error("No instance types available from server");