@layr-labs/ecloud-cli 1.0.0-devep7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +2 -2
- package/dist/commands/admin/admins/add.js +411 -0
- package/dist/commands/admin/admins/add.js.map +1 -0
- package/dist/commands/admin/admins/list.js +414 -0
- package/dist/commands/admin/admins/list.js.map +1 -0
- package/dist/commands/admin/admins/remove.js +409 -0
- package/dist/commands/admin/admins/remove.js.map +1 -0
- package/dist/commands/admin/coupons/create.js +424 -0
- package/dist/commands/admin/coupons/create.js.map +1 -0
- package/dist/commands/admin/coupons/deactivate.js +409 -0
- package/dist/commands/admin/coupons/deactivate.js.map +1 -0
- package/dist/commands/admin/coupons/get.js +418 -0
- package/dist/commands/admin/coupons/get.js.map +1 -0
- package/dist/commands/admin/coupons/list.js +442 -0
- package/dist/commands/admin/coupons/list.js.map +1 -0
- package/dist/commands/admin/coupons/redeem.js +413 -0
- package/dist/commands/admin/coupons/redeem.js.map +1 -0
- package/dist/commands/auth/whoami.js +7 -1
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/billing/__tests__/status.test.js +82 -4
- package/dist/commands/billing/__tests__/status.test.js.map +1 -1
- package/dist/commands/billing/__tests__/subscribe.test.js +10 -2
- package/dist/commands/billing/__tests__/subscribe.test.js.map +1 -1
- package/dist/commands/billing/__tests__/top-up.test.js +565 -13
- package/dist/commands/billing/__tests__/top-up.test.js.map +1 -1
- package/dist/commands/billing/cancel.js +11 -4
- package/dist/commands/billing/cancel.js.map +1 -1
- package/dist/commands/billing/list-cards.js +10 -2
- package/dist/commands/billing/list-cards.js.map +1 -1
- package/dist/commands/billing/redeem-coupon.js +421 -0
- package/dist/commands/billing/redeem-coupon.js.map +1 -0
- package/dist/commands/billing/status.js +36 -2
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/subscribe.js +10 -2
- package/dist/commands/billing/subscribe.js.map +1 -1
- package/dist/commands/billing/top-up.js +392 -11
- package/dist/commands/billing/top-up.js.map +1 -1
- package/dist/commands/compute/app/__tests__/status.test.js +1017 -0
- package/dist/commands/compute/app/__tests__/status.test.js.map +1 -0
- package/dist/commands/compute/app/configure/tls.js +70 -30
- package/dist/commands/compute/app/configure/tls.js.map +1 -1
- package/dist/commands/compute/app/create.js.map +1 -1
- package/dist/commands/compute/app/deploy.js +301 -68
- package/dist/commands/compute/app/deploy.js.map +1 -1
- package/dist/commands/compute/app/info.js +8 -2
- package/dist/commands/compute/app/info.js.map +1 -1
- package/dist/commands/compute/app/list.js +8 -2
- package/dist/commands/compute/app/list.js.map +1 -1
- package/dist/commands/compute/app/logs.js +12 -4
- package/dist/commands/compute/app/logs.js.map +1 -1
- package/dist/commands/compute/app/profile/set.js +12 -4
- package/dist/commands/compute/app/profile/set.js.map +1 -1
- package/dist/commands/compute/app/releases.js +8 -2
- package/dist/commands/compute/app/releases.js.map +1 -1
- package/dist/commands/compute/app/start.js +16 -8
- package/dist/commands/compute/app/start.js.map +1 -1
- package/dist/commands/compute/app/status.js +845 -0
- package/dist/commands/compute/app/status.js.map +1 -0
- package/dist/commands/compute/app/stop.js +16 -8
- package/dist/commands/compute/app/stop.js.map +1 -1
- package/dist/commands/compute/app/terminate.js +16 -8
- package/dist/commands/compute/app/terminate.js.map +1 -1
- package/dist/commands/compute/app/upgrade.js +298 -67
- package/dist/commands/compute/app/upgrade.js.map +1 -1
- package/dist/commands/compute/build/info.js +9 -2
- package/dist/commands/compute/build/info.js.map +1 -1
- package/dist/commands/compute/build/list.js +9 -2
- package/dist/commands/compute/build/list.js.map +1 -1
- package/dist/commands/compute/build/logs.js +9 -2
- package/dist/commands/compute/build/logs.js.map +1 -1
- package/dist/commands/compute/build/status.js +9 -2
- package/dist/commands/compute/build/status.js.map +1 -1
- package/dist/commands/compute/build/submit.js +9 -2
- package/dist/commands/compute/build/submit.js.map +1 -1
- package/dist/commands/compute/build/verify.js +9 -2
- package/dist/commands/compute/build/verify.js.map +1 -1
- package/dist/commands/compute/environment/set.js.map +1 -1
- package/dist/commands/compute/undelegate.js +12 -4
- package/dist/commands/compute/undelegate.js.map +1 -1
- package/dist/hooks/init/__tests__/version-check.test.js +32 -2
- package/dist/hooks/init/__tests__/version-check.test.js.map +1 -1
- package/dist/hooks/init/version-check.js +6 -1
- package/dist/hooks/init/version-check.js.map +1 -1
- package/package.json +11 -2
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/compute/app/upgrade.ts
|
|
4
4
|
import { Command, Args, Flags as Flags2 } from "@oclif/core";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getEnvironmentConfig as getEnvironmentConfig4,
|
|
7
|
+
UserApiClient as UserApiClient4,
|
|
8
|
+
isMainnet,
|
|
9
|
+
WatchTimeoutError
|
|
10
|
+
} from "@layr-labs/ecloud-sdk";
|
|
6
11
|
|
|
7
12
|
// src/telemetry.ts
|
|
8
13
|
import {
|
|
@@ -323,7 +328,7 @@ function listApps(environment) {
|
|
|
323
328
|
|
|
324
329
|
// src/utils/version.ts
|
|
325
330
|
function getCliVersion() {
|
|
326
|
-
return true ? "1.0.0
|
|
331
|
+
return true ? "1.0.0" : "0.0.0";
|
|
327
332
|
}
|
|
328
333
|
function getClientId() {
|
|
329
334
|
return `ecloud-cli/v${getCliVersion()}`;
|
|
@@ -360,7 +365,30 @@ function ensureInteractive(missingFlagHint) {
|
|
|
360
365
|
);
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
|
-
|
|
368
|
+
function isNonInteractive(flags) {
|
|
369
|
+
if (flags?.["non-interactive"]) return true;
|
|
370
|
+
if (process.env.CI === "true") return true;
|
|
371
|
+
return !process.stdin.isTTY;
|
|
372
|
+
}
|
|
373
|
+
function warnDefaulted(flagHint, chosen) {
|
|
374
|
+
console.warn(`Warning: ${flagHint} not set in non-interactive mode; defaulting to ${chosen}.`);
|
|
375
|
+
}
|
|
376
|
+
function collectMissingRequiredInputs(state, identityFlag) {
|
|
377
|
+
const missing = [];
|
|
378
|
+
const hasImageSource = !!state.imageRef || !!state.dockerfile || !!state.verifiable && !!state.repo && !!state.commit;
|
|
379
|
+
if (!hasImageSource) {
|
|
380
|
+
missing.push(
|
|
381
|
+
"an image source (one of: --image-ref, --dockerfile, or --verifiable with --repo and --commit)"
|
|
382
|
+
);
|
|
383
|
+
} else if (state.dockerfile && !state.imageRef && !state.verifiable) {
|
|
384
|
+
missing.push("--image-ref (registry destination for the built image)");
|
|
385
|
+
}
|
|
386
|
+
if (identityFlag === "name" && !state.name) {
|
|
387
|
+
missing.push("--name");
|
|
388
|
+
}
|
|
389
|
+
return missing;
|
|
390
|
+
}
|
|
391
|
+
async function getDockerfile(dockerfilePath, nonInteractive = false) {
|
|
364
392
|
if (dockerfilePath) {
|
|
365
393
|
return dockerfilePath;
|
|
366
394
|
}
|
|
@@ -369,6 +397,10 @@ async function getDockerfileInteractive(dockerfilePath) {
|
|
|
369
397
|
if (!fs3.existsSync(dockerfilePath_resolved)) {
|
|
370
398
|
return "";
|
|
371
399
|
}
|
|
400
|
+
if (nonInteractive) {
|
|
401
|
+
warnDefaulted("--dockerfile/--image-ref", `build from '${dockerfilePath_resolved}'`);
|
|
402
|
+
return dockerfilePath_resolved;
|
|
403
|
+
}
|
|
372
404
|
ensureInteractive("--dockerfile or --image-ref");
|
|
373
405
|
console.log(`
|
|
374
406
|
Found Dockerfile in ${cwd}`);
|
|
@@ -786,13 +818,17 @@ async function getImageReferenceInteractive(imageRef, buildFromDockerfile = fals
|
|
|
786
818
|
});
|
|
787
819
|
return imageRefInput;
|
|
788
820
|
}
|
|
789
|
-
async function
|
|
821
|
+
async function getEnvFile(envFilePath, nonInteractive = false) {
|
|
790
822
|
if (envFilePath && fs3.existsSync(envFilePath)) {
|
|
791
823
|
return envFilePath;
|
|
792
824
|
}
|
|
793
825
|
if (fs3.existsSync(".env")) {
|
|
794
826
|
return ".env";
|
|
795
827
|
}
|
|
828
|
+
if (nonInteractive) {
|
|
829
|
+
warnDefaulted("--env-file", "no env file");
|
|
830
|
+
return "";
|
|
831
|
+
}
|
|
796
832
|
ensureInteractive("--env-file");
|
|
797
833
|
console.log("\nEnvironment file not found.");
|
|
798
834
|
console.log("Environment files contain variables like RPC_URL, etc.");
|
|
@@ -820,6 +856,7 @@ async function getEnvFileInteractive(envFilePath) {
|
|
|
820
856
|
throw new Error(`Unexpected choice: ${choice}`);
|
|
821
857
|
}
|
|
822
858
|
}
|
|
859
|
+
var DEFAULT_NONINTERACTIVE_SKU = "g1-standard-2s";
|
|
823
860
|
function formatSkuChoice(it) {
|
|
824
861
|
if (it.vcpus != null && it.memory_mb != null && it.monthly_price_usd != null && it.hourly_price_usd != null) {
|
|
825
862
|
const tier = it.friendly_name ?? it.sku;
|
|
@@ -836,7 +873,7 @@ function formatSkuChoice(it) {
|
|
|
836
873
|
}
|
|
837
874
|
return `${it.sku} - ${it.description}`;
|
|
838
875
|
}
|
|
839
|
-
async function
|
|
876
|
+
async function getInstanceType(instanceType, defaultSKU, availableTypes, nonInteractive = false) {
|
|
840
877
|
if (instanceType) {
|
|
841
878
|
const valid = availableTypes.find((t) => t.sku === instanceType);
|
|
842
879
|
if (valid) {
|
|
@@ -845,6 +882,20 @@ async function getInstanceTypeInteractive(instanceType, defaultSKU, availableTyp
|
|
|
845
882
|
const validSKUs = availableTypes.map((t) => t.sku).join(", ");
|
|
846
883
|
throw new Error(`Invalid instance-type: ${instanceType} (must be one of: ${validSKUs})`);
|
|
847
884
|
}
|
|
885
|
+
if (nonInteractive) {
|
|
886
|
+
if (defaultSKU) {
|
|
887
|
+
warnDefaulted("--instance-type", `current type '${defaultSKU}'`);
|
|
888
|
+
return defaultSKU;
|
|
889
|
+
}
|
|
890
|
+
if (availableTypes.some((t) => t.sku === DEFAULT_NONINTERACTIVE_SKU)) {
|
|
891
|
+
warnDefaulted("--instance-type", `'${DEFAULT_NONINTERACTIVE_SKU}'`);
|
|
892
|
+
return DEFAULT_NONINTERACTIVE_SKU;
|
|
893
|
+
}
|
|
894
|
+
const validSKUs = availableTypes.map((t) => t.sku).join(", ");
|
|
895
|
+
throw new Error(
|
|
896
|
+
`Cannot pick a default --instance-type in non-interactive mode: '${DEFAULT_NONINTERACTIVE_SKU}' not offered (available: ${validSKUs}). Provide --instance-type.`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
848
899
|
ensureInteractive("--instance-type");
|
|
849
900
|
const isCurrentType = defaultSKU !== "";
|
|
850
901
|
const hasPricing = availableTypes.some((t) => t.monthly_price_usd != null);
|
|
@@ -876,7 +927,7 @@ async function getInstanceTypeInteractive(instanceType, defaultSKU, availableTyp
|
|
|
876
927
|
});
|
|
877
928
|
return choice;
|
|
878
929
|
}
|
|
879
|
-
async function
|
|
930
|
+
async function getLogSettings(logVisibility, nonInteractive = false) {
|
|
880
931
|
if (logVisibility) {
|
|
881
932
|
switch (logVisibility) {
|
|
882
933
|
case "public":
|
|
@@ -891,6 +942,10 @@ async function getLogSettingsInteractive(logVisibility) {
|
|
|
891
942
|
);
|
|
892
943
|
}
|
|
893
944
|
}
|
|
945
|
+
if (nonInteractive) {
|
|
946
|
+
warnDefaulted("--log-visibility", "'private'");
|
|
947
|
+
return { logRedirect: "always", publicLogs: false };
|
|
948
|
+
}
|
|
894
949
|
ensureInteractive("--log-visibility");
|
|
895
950
|
const choice = await select({
|
|
896
951
|
message: "Do you want to view your app's logs?",
|
|
@@ -1211,7 +1266,7 @@ Select an app to ${action}:`);
|
|
|
1211
1266
|
}
|
|
1212
1267
|
return addHexPrefix2(selected);
|
|
1213
1268
|
}
|
|
1214
|
-
async function
|
|
1269
|
+
async function getResourceUsageMonitoring(resourceUsageMonitoring, nonInteractive = false) {
|
|
1215
1270
|
if (resourceUsageMonitoring) {
|
|
1216
1271
|
switch (resourceUsageMonitoring) {
|
|
1217
1272
|
case "enable":
|
|
@@ -1223,6 +1278,10 @@ async function getResourceUsageMonitoringInteractive(resourceUsageMonitoring) {
|
|
|
1223
1278
|
);
|
|
1224
1279
|
}
|
|
1225
1280
|
}
|
|
1281
|
+
if (nonInteractive) {
|
|
1282
|
+
warnDefaulted("--resource-usage-monitoring", "'disable'");
|
|
1283
|
+
return "disable";
|
|
1284
|
+
}
|
|
1226
1285
|
ensureInteractive("--resource-usage-monitoring");
|
|
1227
1286
|
const choice = await select({
|
|
1228
1287
|
message: "Show resource usage (CPU/memory) for your app?",
|
|
@@ -1238,9 +1297,7 @@ async function confirm(prompt) {
|
|
|
1238
1297
|
}
|
|
1239
1298
|
async function confirmWithDefault(prompt, defaultValue = false) {
|
|
1240
1299
|
if (!process.stdin.isTTY) {
|
|
1241
|
-
|
|
1242
|
-
`Cannot confirm "${prompt}" in non-interactive mode. Use --force to skip confirmation prompts.`
|
|
1243
|
-
);
|
|
1300
|
+
return defaultValue;
|
|
1244
1301
|
}
|
|
1245
1302
|
return await inquirerConfirm({
|
|
1246
1303
|
message: prompt,
|
|
@@ -1323,7 +1380,7 @@ var commonFlags = {
|
|
|
1323
1380
|
required: false,
|
|
1324
1381
|
description: "Deployment environment to use",
|
|
1325
1382
|
env: "ECLOUD_ENV",
|
|
1326
|
-
default: async () => getDefaultEnvironment() || (getBuildType3() === "dev" ? "sepolia-dev" : "
|
|
1383
|
+
default: async () => getDefaultEnvironment() || (getBuildType3() === "dev" ? "sepolia-dev" : "mainnet-alpha")
|
|
1327
1384
|
}),
|
|
1328
1385
|
"private-key": Flags.string({
|
|
1329
1386
|
required: false,
|
|
@@ -1353,6 +1410,12 @@ var commonFlags = {
|
|
|
1353
1410
|
nonce: Flags.string({
|
|
1354
1411
|
required: false,
|
|
1355
1412
|
description: 'Override transaction nonce (integer or "latest" to replace a stuck transaction)'
|
|
1413
|
+
}),
|
|
1414
|
+
"non-interactive": Flags.boolean({
|
|
1415
|
+
required: false,
|
|
1416
|
+
description: "Assume non-interactive mode: default safe prompts and error all-at-once on missing required inputs",
|
|
1417
|
+
env: "ECLOUD_NON_INTERACTIVE",
|
|
1418
|
+
default: false
|
|
1356
1419
|
})
|
|
1357
1420
|
};
|
|
1358
1421
|
async function applyTxOverrides(estimate, flags, opts) {
|
|
@@ -1386,7 +1449,9 @@ async function applyTxOverrides(estimate, flags, opts) {
|
|
|
1386
1449
|
} else {
|
|
1387
1450
|
const parsed = Number(nonceStr);
|
|
1388
1451
|
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
1389
|
-
throw new Error(
|
|
1452
|
+
throw new Error(
|
|
1453
|
+
`Invalid nonce: "${nonceStr}". Must be a non-negative integer or "latest".`
|
|
1454
|
+
);
|
|
1390
1455
|
}
|
|
1391
1456
|
nonce = parsed;
|
|
1392
1457
|
}
|
|
@@ -1406,10 +1471,11 @@ import {
|
|
|
1406
1471
|
createComputeModule,
|
|
1407
1472
|
createBillingModule,
|
|
1408
1473
|
createBuildModule,
|
|
1474
|
+
createAdminModule,
|
|
1409
1475
|
getEnvironmentConfig as getEnvironmentConfig3,
|
|
1410
1476
|
requirePrivateKey
|
|
1411
1477
|
} from "@layr-labs/ecloud-sdk";
|
|
1412
|
-
async function createComputeClient(flags) {
|
|
1478
|
+
async function createComputeClient(flags, options = {}) {
|
|
1413
1479
|
flags = await validateCommonFlags(flags);
|
|
1414
1480
|
const environment = flags.environment;
|
|
1415
1481
|
const environmentConfig = getEnvironmentConfig3(environment);
|
|
@@ -1431,8 +1497,9 @@ async function createComputeClient(flags) {
|
|
|
1431
1497
|
publicClient,
|
|
1432
1498
|
environment,
|
|
1433
1499
|
clientId: getClientId(),
|
|
1434
|
-
skipTelemetry: true
|
|
1500
|
+
skipTelemetry: true,
|
|
1435
1501
|
// CLI already has telemetry, skip SDK telemetry
|
|
1502
|
+
logger: options.logger
|
|
1436
1503
|
});
|
|
1437
1504
|
}
|
|
1438
1505
|
async function createBuildClient(flags) {
|
|
@@ -1458,6 +1525,29 @@ async function createBuildClient(flags) {
|
|
|
1458
1525
|
});
|
|
1459
1526
|
}
|
|
1460
1527
|
|
|
1528
|
+
// src/utils/instanceTypes.ts
|
|
1529
|
+
import { UserApiClient as UserApiClient3 } from "@layr-labs/ecloud-sdk";
|
|
1530
|
+
async function fetchAvailableInstanceTypes(environment, environmentConfig, privateKey, rpcUrl) {
|
|
1531
|
+
try {
|
|
1532
|
+
const { publicClient, walletClient } = createViemClients({
|
|
1533
|
+
privateKey,
|
|
1534
|
+
rpcUrl,
|
|
1535
|
+
environment
|
|
1536
|
+
});
|
|
1537
|
+
const userApiClient = new UserApiClient3(environmentConfig, walletClient, publicClient, {
|
|
1538
|
+
clientId: getClientId()
|
|
1539
|
+
});
|
|
1540
|
+
const skuList = await userApiClient.getSKUs();
|
|
1541
|
+
if (skuList.skus.length === 0) {
|
|
1542
|
+
throw new Error("No instance types available from server");
|
|
1543
|
+
}
|
|
1544
|
+
return skuList.skus;
|
|
1545
|
+
} catch (err) {
|
|
1546
|
+
console.warn(`Failed to fetch instance types: ${err.message}`);
|
|
1547
|
+
return [{ sku: "g1-standard-4t", description: "4 vCPUs, 16 GB memory, TDX" }];
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1461
1551
|
// src/commands/compute/app/upgrade.ts
|
|
1462
1552
|
import chalk2 from "chalk";
|
|
1463
1553
|
|
|
@@ -1619,8 +1709,63 @@ async function resolveDockerHubImageDigest(imageRef) {
|
|
|
1619
1709
|
if (!/^sha256:[0-9a-f]{64}$/i.test(digest)) {
|
|
1620
1710
|
throw new Error(`Unexpected digest format from Docker registry: ${digest}`);
|
|
1621
1711
|
}
|
|
1712
|
+
await assertImageHasAmd64(owner, repo, tag, token, imageRef);
|
|
1622
1713
|
return digest;
|
|
1623
1714
|
}
|
|
1715
|
+
var AMD64_ACCEPT = [
|
|
1716
|
+
"application/vnd.oci.image.index.v1+json",
|
|
1717
|
+
"application/vnd.docker.distribution.manifest.list.v2+json",
|
|
1718
|
+
"application/vnd.oci.image.manifest.v1+json",
|
|
1719
|
+
"application/vnd.docker.distribution.manifest.v2+json"
|
|
1720
|
+
].join(", ");
|
|
1721
|
+
async function assertImageHasAmd64(owner, repo, tag, token, imageRef) {
|
|
1722
|
+
const base = `https://registry-1.docker.io/v2/${owner}/${repo}`;
|
|
1723
|
+
const headers = { Authorization: `Bearer ${token}`, Accept: AMD64_ACCEPT };
|
|
1724
|
+
const res = await fetch(`${base}/manifests/${encodeURIComponent(tag)}`, { headers });
|
|
1725
|
+
if (!res.ok) {
|
|
1726
|
+
const body = await safeReadText(res);
|
|
1727
|
+
throw new Error(
|
|
1728
|
+
`Failed to read manifest for ${imageRef} (${res.status}): ${body || res.statusText}`
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
const manifest = await res.json();
|
|
1732
|
+
const isAmd64 = (os5, arch) => os5 === "linux" && arch === "amd64";
|
|
1733
|
+
if (Array.isArray(manifest.manifests) && manifest.manifests.length > 0) {
|
|
1734
|
+
const platforms = manifest.manifests.map(
|
|
1735
|
+
(m) => m.platform ? `${m.platform.os}/${m.platform.architecture}` : "unknown"
|
|
1736
|
+
);
|
|
1737
|
+
if (manifest.manifests.some((m) => isAmd64(m.platform?.os, m.platform?.architecture))) {
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
throw amd64Error(imageRef, platforms);
|
|
1741
|
+
}
|
|
1742
|
+
const configDigest = manifest.config?.digest;
|
|
1743
|
+
if (!configDigest) {
|
|
1744
|
+
throw amd64Error(imageRef, ["unknown (no platform info in manifest)"]);
|
|
1745
|
+
}
|
|
1746
|
+
const cfgRes = await fetch(`${base}/blobs/${configDigest}`, {
|
|
1747
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1748
|
+
});
|
|
1749
|
+
if (!cfgRes.ok) {
|
|
1750
|
+
throw amd64Error(imageRef, ["unknown (could not read image config)"]);
|
|
1751
|
+
}
|
|
1752
|
+
const cfg = await cfgRes.json();
|
|
1753
|
+
if (isAmd64(cfg.os, cfg.architecture)) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
throw amd64Error(imageRef, [`${cfg.os ?? "unknown"}/${cfg.architecture ?? "unknown"}`]);
|
|
1757
|
+
}
|
|
1758
|
+
function amd64Error(imageRef, platforms) {
|
|
1759
|
+
return new Error(
|
|
1760
|
+
`ecloud requires linux/amd64 images for TEE deployment.
|
|
1761
|
+
|
|
1762
|
+
Image: ${imageRef}
|
|
1763
|
+
Found platform(s): ${platforms.join(", ")}
|
|
1764
|
+
Required platform: linux/amd64
|
|
1765
|
+
|
|
1766
|
+
To fix: rebuild for linux/amd64 (e.g. docker buildx build --platform linux/amd64 ... --push), or use a verifiable build (--verifiable --repo <repo> --commit <sha>), which builds server-side.`
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1624
1769
|
|
|
1625
1770
|
// src/utils/tls.ts
|
|
1626
1771
|
import fs4 from "fs";
|
|
@@ -1685,12 +1830,45 @@ function mergeInlineEnvVars(envFilePath, inlineEnvVars) {
|
|
|
1685
1830
|
return tmpFile;
|
|
1686
1831
|
}
|
|
1687
1832
|
|
|
1833
|
+
// src/utils/exitCodes.ts
|
|
1834
|
+
import { InsufficientGasError } from "@layr-labs/ecloud-sdk";
|
|
1835
|
+
var EXIT_CODES = {
|
|
1836
|
+
INVALID_INPUT: 2,
|
|
1837
|
+
BUILD_FAILED: 3,
|
|
1838
|
+
ONCHAIN_FAILED: 4
|
|
1839
|
+
};
|
|
1840
|
+
function errorMessage(err) {
|
|
1841
|
+
if (err instanceof Error) return err.message;
|
|
1842
|
+
return String(err);
|
|
1843
|
+
}
|
|
1844
|
+
function stageFailure(operation, stage, err) {
|
|
1845
|
+
const noun = operation === "deploy" ? "deployment" : "upgrade";
|
|
1846
|
+
if (err instanceof InsufficientGasError) {
|
|
1847
|
+
stage = "onchain";
|
|
1848
|
+
}
|
|
1849
|
+
switch (stage) {
|
|
1850
|
+
case "invalid-input":
|
|
1851
|
+
return { message: errorMessage(err), exit: EXIT_CODES.INVALID_INPUT };
|
|
1852
|
+
case "build":
|
|
1853
|
+
return {
|
|
1854
|
+
message: `Build/push failed (no ${noun} was attempted): ${errorMessage(err)}`,
|
|
1855
|
+
exit: EXIT_CODES.BUILD_FAILED
|
|
1856
|
+
};
|
|
1857
|
+
case "onchain":
|
|
1858
|
+
return {
|
|
1859
|
+
message: `On-chain ${noun} failed after the image was built and pushed: ${errorMessage(err)}
|
|
1860
|
+
The image is already pushed \u2014 re-running ${operation} will reuse it.`,
|
|
1861
|
+
exit: EXIT_CODES.ONCHAIN_FAILED
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1688
1866
|
// src/commands/compute/app/upgrade.ts
|
|
1689
1867
|
var AppUpgrade = class _AppUpgrade extends Command {
|
|
1690
1868
|
static description = "Upgrade existing deployment";
|
|
1691
1869
|
static args = {
|
|
1692
1870
|
"app-id": Args.string({
|
|
1693
|
-
description: "App ID or name to upgrade",
|
|
1871
|
+
description: "App ID or name to upgrade (env: ECLOUD_APP_ID)",
|
|
1694
1872
|
required: false
|
|
1695
1873
|
})
|
|
1696
1874
|
};
|
|
@@ -1724,7 +1902,7 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1724
1902
|
}),
|
|
1725
1903
|
"log-visibility": Flags2.string({
|
|
1726
1904
|
required: false,
|
|
1727
|
-
description: "Log visibility setting: public, private, or off",
|
|
1905
|
+
description: "Log visibility setting: public, private, or off (non-interactive default: private)",
|
|
1728
1906
|
options: ["public", "private", "off"],
|
|
1729
1907
|
env: "ECLOUD_LOG_VISIBILITY"
|
|
1730
1908
|
}),
|
|
@@ -1735,7 +1913,7 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1735
1913
|
}),
|
|
1736
1914
|
"resource-usage-monitoring": Flags2.string({
|
|
1737
1915
|
required: false,
|
|
1738
|
-
description: "Resource usage monitoring: enable or disable",
|
|
1916
|
+
description: "Resource usage monitoring: enable or disable (non-interactive default: disable)",
|
|
1739
1917
|
options: ["enable", "disable"],
|
|
1740
1918
|
env: "ECLOUD_RESOURCE_USAGE_MONITORING"
|
|
1741
1919
|
}),
|
|
@@ -1773,19 +1951,50 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1773
1951
|
}),
|
|
1774
1952
|
force: Flags2.boolean({
|
|
1775
1953
|
description: "Skip all confirmation prompts",
|
|
1776
|
-
default: false
|
|
1954
|
+
default: false,
|
|
1955
|
+
env: "ECLOUD_FORCE"
|
|
1956
|
+
}),
|
|
1957
|
+
"watch-timeout": Flags2.integer({
|
|
1958
|
+
description: "Maximum seconds to wait for the upgrade to complete before returning a recovery hint (default: 600)",
|
|
1959
|
+
env: "ECLOUD_WATCH_TIMEOUT_SECONDS"
|
|
1777
1960
|
})
|
|
1778
1961
|
};
|
|
1779
1962
|
async run() {
|
|
1780
1963
|
return withTelemetry(this, async () => {
|
|
1781
1964
|
const { args, flags } = await this.parse(_AppUpgrade);
|
|
1965
|
+
const appIdInput = args["app-id"] ?? process.env.ECLOUD_APP_ID;
|
|
1966
|
+
const nonInteractive = isNonInteractive(flags);
|
|
1967
|
+
if (nonInteractive) {
|
|
1968
|
+
const missing = collectMissingRequiredInputs(
|
|
1969
|
+
{
|
|
1970
|
+
imageRef: flags["image-ref"],
|
|
1971
|
+
dockerfile: flags.dockerfile,
|
|
1972
|
+
verifiable: flags.verifiable,
|
|
1973
|
+
repo: flags.repo,
|
|
1974
|
+
commit: flags.commit
|
|
1975
|
+
},
|
|
1976
|
+
"app-id"
|
|
1977
|
+
);
|
|
1978
|
+
if (!appIdInput) {
|
|
1979
|
+
missing.push("app-id (positional arg or ECLOUD_APP_ID)");
|
|
1980
|
+
}
|
|
1981
|
+
if (missing.length > 0) {
|
|
1982
|
+
const { message, exit } = stageFailure(
|
|
1983
|
+
"upgrade",
|
|
1984
|
+
"invalid-input",
|
|
1985
|
+
`Missing required input(s) for non-interactive upgrade:
|
|
1986
|
+
- ${missing.join("\n - ")}`
|
|
1987
|
+
);
|
|
1988
|
+
this.error(message, { exit });
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1782
1991
|
const compute = await createComputeClient(flags);
|
|
1783
1992
|
const environment = flags.environment;
|
|
1784
1993
|
const environmentConfig = getEnvironmentConfig4(environment);
|
|
1785
1994
|
const rpcUrl = flags["rpc-url"] || environmentConfig.defaultRPCURL;
|
|
1786
1995
|
const privateKey = flags["private-key"];
|
|
1787
1996
|
const appID = await getOrPromptAppID({
|
|
1788
|
-
appID:
|
|
1997
|
+
appID: appIdInput,
|
|
1789
1998
|
environment,
|
|
1790
1999
|
privateKey,
|
|
1791
2000
|
rpcUrl,
|
|
@@ -1846,7 +2055,7 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1846
2055
|
buildContextPath: flags["build-context"],
|
|
1847
2056
|
dependencies: flags["build-dependencies"]
|
|
1848
2057
|
} : await promptVerifiableGitSourceInputs();
|
|
1849
|
-
envFilePath = await
|
|
2058
|
+
envFilePath = await getEnvFile(flags["env-file"], nonInteractive);
|
|
1850
2059
|
const includeTlsCaddyfile = isTlsEnabledFromEnvFile(envFilePath);
|
|
1851
2060
|
if (includeTlsCaddyfile && !inputs.caddyfilePath) {
|
|
1852
2061
|
inputs.caddyfilePath = "Caddyfile";
|
|
@@ -1906,10 +2115,11 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1906
2115
|
}
|
|
1907
2116
|
}
|
|
1908
2117
|
const isVerifiable = verifiableMode !== "none";
|
|
1909
|
-
const
|
|
2118
|
+
const deployExistingImageRef = !!flags["image-ref"] && !flags.dockerfile;
|
|
2119
|
+
const dockerfilePath = isVerifiable || deployExistingImageRef ? "" : await getDockerfile(flags.dockerfile, nonInteractive);
|
|
1910
2120
|
const buildFromDockerfile = dockerfilePath !== "";
|
|
1911
2121
|
const imageRef = verifiableImageUrl ? verifiableImageUrl : await getImageReferenceInteractive(flags["image-ref"], buildFromDockerfile);
|
|
1912
|
-
envFilePath = envFilePath ?? await
|
|
2122
|
+
envFilePath = envFilePath ?? await getEnvFile(flags["env-file"], nonInteractive);
|
|
1913
2123
|
if (flags.env && flags.env.length > 0) {
|
|
1914
2124
|
envFilePath = mergeInlineEnvVars(envFilePath, flags.env);
|
|
1915
2125
|
}
|
|
@@ -1920,7 +2130,7 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1920
2130
|
});
|
|
1921
2131
|
let currentInstanceType = "";
|
|
1922
2132
|
try {
|
|
1923
|
-
const userApiClient = new
|
|
2133
|
+
const userApiClient = new UserApiClient4(environmentConfig, walletClient, publicClient, {
|
|
1924
2134
|
clientId: getClientId()
|
|
1925
2135
|
});
|
|
1926
2136
|
const infos = await userApiClient.getInfos([appID], 1);
|
|
@@ -1935,33 +2145,43 @@ var AppUpgrade = class _AppUpgrade extends Command {
|
|
|
1935
2145
|
privateKey,
|
|
1936
2146
|
rpcUrl
|
|
1937
2147
|
);
|
|
1938
|
-
const instanceType = await
|
|
2148
|
+
const instanceType = await getInstanceType(
|
|
1939
2149
|
flags["instance-type"],
|
|
1940
2150
|
currentInstanceType,
|
|
1941
|
-
availableTypes
|
|
2151
|
+
availableTypes,
|
|
2152
|
+
nonInteractive
|
|
1942
2153
|
);
|
|
1943
|
-
const logSettings = await
|
|
1944
|
-
flags["log-visibility"]
|
|
2154
|
+
const logSettings = await getLogSettings(
|
|
2155
|
+
flags["log-visibility"],
|
|
2156
|
+
nonInteractive
|
|
1945
2157
|
);
|
|
1946
|
-
const resourceUsageMonitoring = await
|
|
1947
|
-
flags["resource-usage-monitoring"]
|
|
2158
|
+
const resourceUsageMonitoring = await getResourceUsageMonitoring(
|
|
2159
|
+
flags["resource-usage-monitoring"],
|
|
2160
|
+
nonInteractive
|
|
1948
2161
|
);
|
|
1949
2162
|
const logVisibility = logSettings.publicLogs ? "public" : logSettings.logRedirect ? "private" : "off";
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2163
|
+
let prepared;
|
|
2164
|
+
let gasEstimate;
|
|
2165
|
+
try {
|
|
2166
|
+
({ prepared, gasEstimate } = verifiableMode === "git" ? await compute.app.prepareUpgradeFromVerifiableBuild(appID, {
|
|
2167
|
+
imageRef,
|
|
2168
|
+
imageDigest: verifiableImageDigest,
|
|
2169
|
+
envFile: envFilePath,
|
|
2170
|
+
instanceType,
|
|
2171
|
+
logVisibility,
|
|
2172
|
+
resourceUsageMonitoring
|
|
2173
|
+
}) : await compute.app.prepareUpgrade(appID, {
|
|
2174
|
+
dockerfile: dockerfilePath,
|
|
2175
|
+
imageRef,
|
|
2176
|
+
envFile: envFilePath,
|
|
2177
|
+
instanceType,
|
|
2178
|
+
logVisibility,
|
|
2179
|
+
resourceUsageMonitoring
|
|
2180
|
+
}));
|
|
2181
|
+
} catch (err) {
|
|
2182
|
+
const { message, exit } = stageFailure("upgrade", "build", err);
|
|
2183
|
+
this.error(message, { exit });
|
|
2184
|
+
}
|
|
1965
2185
|
const finalTx = await applyTxOverrides(gasEstimate, flags, { publicClient, address });
|
|
1966
2186
|
if (flags["max-fee-per-gas"] || flags["max-priority-fee"]) {
|
|
1967
2187
|
this.log(
|
|
@@ -1984,8 +2204,39 @@ ${chalk2.gray(`Upgrade cancelled`)}`);
|
|
|
1984
2204
|
return;
|
|
1985
2205
|
}
|
|
1986
2206
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
2207
|
+
let res;
|
|
2208
|
+
try {
|
|
2209
|
+
res = await compute.app.executeUpgrade(prepared, finalTx);
|
|
2210
|
+
} catch (err) {
|
|
2211
|
+
const { message, exit } = stageFailure("upgrade", "onchain", err);
|
|
2212
|
+
this.error(message, { exit });
|
|
2213
|
+
}
|
|
2214
|
+
try {
|
|
2215
|
+
await compute.app.watchUpgrade(res.appId, { timeoutSeconds: flags["watch-timeout"] });
|
|
2216
|
+
} catch (err) {
|
|
2217
|
+
if (err instanceof WatchTimeoutError) {
|
|
2218
|
+
this.log("");
|
|
2219
|
+
this.log(
|
|
2220
|
+
chalk2.yellow(
|
|
2221
|
+
`Timed out after ${err.elapsedSeconds}s waiting for upgrade to complete (last status: ${err.lastStatus ?? "unknown"}).`
|
|
2222
|
+
)
|
|
2223
|
+
);
|
|
2224
|
+
this.log(chalk2.gray("The on-chain transaction was submitted; the orchestrator may"));
|
|
2225
|
+
this.log(chalk2.gray("still be processing. To check the current status, run:"));
|
|
2226
|
+
this.log("");
|
|
2227
|
+
this.log(` ${chalk2.cyan(`ecloud compute app info ${res.appId}`)}`);
|
|
2228
|
+
this.log("");
|
|
2229
|
+
this.log(chalk2.gray(`appId: ${res.appId}`));
|
|
2230
|
+
this.log(chalk2.gray(`txHash: ${res.txHash}`));
|
|
2231
|
+
this.log(
|
|
2232
|
+
chalk2.gray(
|
|
2233
|
+
`(override the watch deadline with ECLOUD_WATCH_TIMEOUT_SECONDS, currently ${err.timeoutSeconds}s)`
|
|
2234
|
+
)
|
|
2235
|
+
);
|
|
2236
|
+
this.exit(1);
|
|
2237
|
+
}
|
|
2238
|
+
throw err;
|
|
2239
|
+
}
|
|
1989
2240
|
try {
|
|
1990
2241
|
const cwd = process.env.INIT_CWD || process.cwd();
|
|
1991
2242
|
setLinkedAppForDirectory(environment, cwd, res.appId);
|
|
@@ -2003,7 +2254,7 @@ ${chalk2.gray(`Upgrade cancelled`)}`);
|
|
|
2003
2254
|
rpcUrl,
|
|
2004
2255
|
environment
|
|
2005
2256
|
});
|
|
2006
|
-
const userApiClient = new
|
|
2257
|
+
const userApiClient = new UserApiClient4(environmentConfig, walletClient2, publicClient2, {
|
|
2007
2258
|
clientId: getClientId()
|
|
2008
2259
|
});
|
|
2009
2260
|
const infos = await userApiClient.getInfos([res.appId], 1);
|
|
@@ -2032,26 +2283,6 @@ Note: "Running" means the container started. Verify your app is serving traffic
|
|
|
2032
2283
|
});
|
|
2033
2284
|
}
|
|
2034
2285
|
};
|
|
2035
|
-
async function fetchAvailableInstanceTypes(environment, environmentConfig, privateKey, rpcUrl) {
|
|
2036
|
-
try {
|
|
2037
|
-
const { publicClient, walletClient } = createViemClients({
|
|
2038
|
-
privateKey,
|
|
2039
|
-
rpcUrl,
|
|
2040
|
-
environment
|
|
2041
|
-
});
|
|
2042
|
-
const userApiClient = new UserApiClient3(environmentConfig, walletClient, publicClient, {
|
|
2043
|
-
clientId: getClientId()
|
|
2044
|
-
});
|
|
2045
|
-
const skuList = await userApiClient.getSKUs();
|
|
2046
|
-
if (skuList.skus.length === 0) {
|
|
2047
|
-
throw new Error("No instance types available from server");
|
|
2048
|
-
}
|
|
2049
|
-
return skuList.skus;
|
|
2050
|
-
} catch (err) {
|
|
2051
|
-
console.warn(`Failed to fetch instance types: ${err.message}`);
|
|
2052
|
-
return [{ sku: "g1-standard-4t", description: "4 vCPUs, 16 GB memory, TDX" }];
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
2286
|
export {
|
|
2056
2287
|
AppUpgrade as default
|
|
2057
2288
|
};
|