@layr-labs/ecloud-cli 0.2.0-dev.1 → 0.2.0-dev.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.
- package/VERSION +2 -2
- package/dist/commands/auth/generate.js +2 -2
- package/dist/commands/auth/generate.js.map +1 -1
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/migrate.js.map +1 -1
- package/dist/commands/auth/whoami.js +3 -5
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/billing/cancel.js +3 -6
- package/dist/commands/billing/cancel.js.map +1 -1
- package/dist/commands/billing/status.js +3 -6
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/subscribe.js +7 -12
- package/dist/commands/billing/subscribe.js.map +1 -1
- package/dist/commands/compute/app/create.js.map +1 -1
- package/dist/commands/compute/app/deploy.js +86 -614
- package/dist/commands/compute/app/deploy.js.map +1 -1
- package/dist/commands/compute/app/info.js +10 -59
- package/dist/commands/compute/app/info.js.map +1 -1
- package/dist/commands/compute/app/list.js +9 -10
- package/dist/commands/compute/app/list.js.map +1 -1
- package/dist/commands/compute/app/logs.js +11 -61
- package/dist/commands/compute/app/logs.js.map +1 -1
- package/dist/commands/compute/app/profile/set.js +20 -145
- package/dist/commands/compute/app/profile/set.js.map +1 -1
- package/dist/commands/compute/app/start.js +11 -61
- package/dist/commands/compute/app/start.js.map +1 -1
- package/dist/commands/compute/app/stop.js +11 -61
- package/dist/commands/compute/app/stop.js.map +1 -1
- package/dist/commands/compute/app/terminate.js +11 -61
- package/dist/commands/compute/app/terminate.js.map +1 -1
- package/dist/commands/compute/app/upgrade.js +52 -615
- package/dist/commands/compute/app/upgrade.js.map +1 -1
- package/dist/commands/compute/environment/list.js.map +1 -1
- package/dist/commands/compute/environment/set.js.map +1 -1
- package/dist/commands/compute/environment/show.js.map +1 -1
- package/dist/commands/compute/undelegate.js +9 -11
- package/dist/commands/compute/undelegate.js.map +1 -1
- package/dist/commands/telemetry/disable.js.map +1 -1
- package/dist/commands/telemetry/enable.js.map +1 -1
- package/dist/commands/telemetry/status.js.map +1 -1
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/version.js.map +1 -1
- package/package.json +2 -7
- package/dist/commands/compute/app/releases.js +0 -1111
- package/dist/commands/compute/app/releases.js.map +0 -1
- package/dist/commands/compute/build/info.js +0 -500
- package/dist/commands/compute/build/info.js.map +0 -1
- package/dist/commands/compute/build/list.js +0 -494
- package/dist/commands/compute/build/list.js.map +0 -1
- package/dist/commands/compute/build/logs.js +0 -459
- package/dist/commands/compute/build/logs.js.map +0 -1
- package/dist/commands/compute/build/status.js +0 -481
- package/dist/commands/compute/build/status.js.map +0 -1
- package/dist/commands/compute/build/submit.js +0 -618
- package/dist/commands/compute/build/submit.js.map +0 -1
- package/dist/commands/compute/build/verify.js +0 -439
- package/dist/commands/compute/build/verify.js.map +0 -1
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/compute/app/deploy.ts
|
|
4
4
|
import { Command, Flags as Flags2 } from "@oclif/core";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getEnvironmentConfig as getEnvironmentConfig2,
|
|
7
|
+
UserApiClient as UserApiClient3,
|
|
8
|
+
isMainnet,
|
|
9
|
+
prepareDeploy,
|
|
10
|
+
executeDeploy,
|
|
11
|
+
watchDeployment
|
|
12
|
+
} from "@layr-labs/ecloud-sdk";
|
|
6
13
|
|
|
7
14
|
// src/telemetry.ts
|
|
8
15
|
import {
|
|
@@ -64,29 +71,6 @@ function saveGlobalConfig(config) {
|
|
|
64
71
|
const content = dumpYaml(config, { lineWidth: -1 });
|
|
65
72
|
fs.writeFileSync(configPath, content, { mode: 420 });
|
|
66
73
|
}
|
|
67
|
-
function normalizeDirectoryPath(directoryPath) {
|
|
68
|
-
const resolved = path.resolve(directoryPath);
|
|
69
|
-
try {
|
|
70
|
-
return fs.realpathSync(resolved);
|
|
71
|
-
} catch {
|
|
72
|
-
return resolved;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function setLinkedAppForDirectory(environment, directoryPath, appId) {
|
|
76
|
-
if (!directoryPath || !environment) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const config = loadGlobalConfig();
|
|
80
|
-
if (!config.directory_links) {
|
|
81
|
-
config.directory_links = {};
|
|
82
|
-
}
|
|
83
|
-
if (!config.directory_links[environment]) {
|
|
84
|
-
config.directory_links[environment] = {};
|
|
85
|
-
}
|
|
86
|
-
const normalizedPath = normalizeDirectoryPath(directoryPath);
|
|
87
|
-
config.directory_links[environment][normalizedPath] = appId.toLowerCase();
|
|
88
|
-
saveGlobalConfig(config);
|
|
89
|
-
}
|
|
90
74
|
function getDefaultEnvironment() {
|
|
91
75
|
const config = loadGlobalConfig();
|
|
92
76
|
return config.default_environment;
|
|
@@ -177,7 +161,6 @@ async function withTelemetry(command, action) {
|
|
|
177
161
|
|
|
178
162
|
// src/flags.ts
|
|
179
163
|
import { Flags } from "@oclif/core";
|
|
180
|
-
import { getBuildType as getBuildType3 } from "@layr-labs/ecloud-sdk";
|
|
181
164
|
|
|
182
165
|
// src/utils/prompts.ts
|
|
183
166
|
import { input, select, password, confirm as inquirerConfirm } from "@inquirer/prompts";
|
|
@@ -273,7 +256,7 @@ function findAvailableName(environment, baseName) {
|
|
|
273
256
|
|
|
274
257
|
// src/utils/version.ts
|
|
275
258
|
function getCliVersion() {
|
|
276
|
-
return true ? "0.2.0-dev.
|
|
259
|
+
return true ? "0.2.0-dev.2" : "0.0.0";
|
|
277
260
|
}
|
|
278
261
|
function getClientId() {
|
|
279
262
|
return `ecloud-cli/v${getCliVersion()}`;
|
|
@@ -307,113 +290,6 @@ Found Dockerfile in ${cwd}`);
|
|
|
307
290
|
throw new Error(`Unexpected choice: ${choice}`);
|
|
308
291
|
}
|
|
309
292
|
}
|
|
310
|
-
async function promptUseVerifiableBuild() {
|
|
311
|
-
return confirmWithDefault("Build from verifiable source?", false);
|
|
312
|
-
}
|
|
313
|
-
async function promptVerifiableSourceType() {
|
|
314
|
-
return select({
|
|
315
|
-
message: "Choose verifiable source type:",
|
|
316
|
-
choices: [
|
|
317
|
-
{ name: "Build from git source (public repo required)", value: "git" },
|
|
318
|
-
{ name: "Use a prebuilt verifiable image (eigencloud-containers)", value: "prebuilt" }
|
|
319
|
-
]
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
async function promptVerifiableGitSourceInputs() {
|
|
323
|
-
const repoUrl = (await input({
|
|
324
|
-
message: "Enter public git repository URL:",
|
|
325
|
-
default: "",
|
|
326
|
-
validate: (value) => {
|
|
327
|
-
if (!value.trim()) return "Repository URL is required";
|
|
328
|
-
try {
|
|
329
|
-
const url = new URL(value.trim());
|
|
330
|
-
if (url.protocol !== "https:") return "Repository URL must start with https://";
|
|
331
|
-
if (url.hostname.toLowerCase() !== "github.com")
|
|
332
|
-
return "Repository URL must be a public GitHub HTTPS URL (github.com)";
|
|
333
|
-
const parts = url.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
|
|
334
|
-
if (parts.length < 2) return "Repository URL must be https://github.com/<owner>/<repo>";
|
|
335
|
-
const [owner, repo] = parts;
|
|
336
|
-
if (!owner || !repo) return "Repository URL must be https://github.com/<owner>/<repo>";
|
|
337
|
-
if (repo.toLowerCase() === "settings") return "Repository URL looks invalid";
|
|
338
|
-
if (url.search || url.hash)
|
|
339
|
-
return "Repository URL must not include query params or fragments";
|
|
340
|
-
} catch {
|
|
341
|
-
return "Invalid URL format";
|
|
342
|
-
}
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
})).trim();
|
|
346
|
-
const gitRef = (await input({
|
|
347
|
-
message: "Enter git commit SHA (40 hex chars):",
|
|
348
|
-
default: "",
|
|
349
|
-
validate: (value) => {
|
|
350
|
-
const trimmed = value.trim();
|
|
351
|
-
if (!trimmed) return "Commit SHA is required";
|
|
352
|
-
if (!/^[0-9a-f]{40}$/i.test(trimmed))
|
|
353
|
-
return "Commit must be a 40-character hexadecimal SHA";
|
|
354
|
-
return true;
|
|
355
|
-
}
|
|
356
|
-
})).trim();
|
|
357
|
-
const buildContextPath = (await input({
|
|
358
|
-
message: "Enter build context path (relative to repo):",
|
|
359
|
-
default: ".",
|
|
360
|
-
validate: (value) => value.trim() ? true : "Build context path cannot be empty"
|
|
361
|
-
})).trim();
|
|
362
|
-
const dockerfilePath = (await input({
|
|
363
|
-
message: "Enter Dockerfile path (relative to build context):",
|
|
364
|
-
default: "Dockerfile",
|
|
365
|
-
validate: (value) => value.trim() ? true : "Dockerfile path cannot be empty"
|
|
366
|
-
})).trim();
|
|
367
|
-
const caddyfileRaw = (await input({
|
|
368
|
-
message: "Enter Caddyfile path (relative to build context, optional):",
|
|
369
|
-
default: "",
|
|
370
|
-
validate: (value) => {
|
|
371
|
-
const trimmed = value.trim();
|
|
372
|
-
if (!trimmed) return true;
|
|
373
|
-
if (trimmed.includes("..")) return "Caddyfile path must not contain '..'";
|
|
374
|
-
return true;
|
|
375
|
-
}
|
|
376
|
-
})).trim();
|
|
377
|
-
const depsRaw = (await input({
|
|
378
|
-
message: "Enter dependency digests (comma-separated sha256:..., optional):",
|
|
379
|
-
default: "",
|
|
380
|
-
validate: (value) => {
|
|
381
|
-
const trimmed = value.trim();
|
|
382
|
-
if (!trimmed) return true;
|
|
383
|
-
const parts = trimmed.split(",").map((p) => p.trim()).filter(Boolean);
|
|
384
|
-
for (const p of parts) {
|
|
385
|
-
if (!/^sha256:[0-9a-f]{64}$/i.test(p)) {
|
|
386
|
-
return `Invalid dependency digest: ${p} (expected sha256:<64 hex>)`;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
})).trim();
|
|
392
|
-
const dependencies = depsRaw === "" ? [] : depsRaw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
393
|
-
return {
|
|
394
|
-
repoUrl,
|
|
395
|
-
gitRef,
|
|
396
|
-
dockerfilePath,
|
|
397
|
-
caddyfilePath: caddyfileRaw === "" ? void 0 : caddyfileRaw,
|
|
398
|
-
buildContextPath,
|
|
399
|
-
dependencies
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
async function promptVerifiablePrebuiltImageRef() {
|
|
403
|
-
const ref = await input({
|
|
404
|
-
message: "Enter prebuilt verifiable image ref:",
|
|
405
|
-
default: "docker.io/eigenlayer/eigencloud-containers:",
|
|
406
|
-
validate: (value) => {
|
|
407
|
-
const trimmed = value.trim();
|
|
408
|
-
if (!trimmed) return "Image reference is required";
|
|
409
|
-
if (!/^docker\.io\/eigenlayer\/eigencloud-containers:[^@\s]+$/i.test(trimmed)) {
|
|
410
|
-
return "Image ref must match docker.io/eigenlayer/eigencloud-containers:<tag>";
|
|
411
|
-
}
|
|
412
|
-
return true;
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
return ref.trim();
|
|
416
|
-
}
|
|
417
293
|
function extractHostname(registry) {
|
|
418
294
|
let hostname = registry.replace(/^https?:\/\//, "");
|
|
419
295
|
hostname = hostname.split("/")[0];
|
|
@@ -662,9 +538,9 @@ async function getImageReferenceInteractive(imageRef, buildFromDockerfile = fals
|
|
|
662
538
|
});
|
|
663
539
|
return imageRefInput;
|
|
664
540
|
}
|
|
665
|
-
async function getAvailableAppNameInteractive(environment, imageRef
|
|
666
|
-
const baseName =
|
|
667
|
-
const suggestedName =
|
|
541
|
+
async function getAvailableAppNameInteractive(environment, imageRef) {
|
|
542
|
+
const baseName = extractAppNameFromImage(imageRef);
|
|
543
|
+
const suggestedName = findAvailableName(environment, baseName);
|
|
668
544
|
while (true) {
|
|
669
545
|
console.log("\nApp name selection:");
|
|
670
546
|
const name = await input({
|
|
@@ -687,16 +563,16 @@ async function getAvailableAppNameInteractive(environment, imageRef, suggestedBa
|
|
|
687
563
|
console.log(`Suggested alternative: ${newSuggested}`);
|
|
688
564
|
}
|
|
689
565
|
}
|
|
690
|
-
async function getOrPromptAppName(appName, environment, imageRef
|
|
566
|
+
async function getOrPromptAppName(appName, environment, imageRef) {
|
|
691
567
|
if (appName) {
|
|
692
568
|
validateAppName(appName);
|
|
693
569
|
if (isAppNameAvailable(environment, appName)) {
|
|
694
570
|
return appName;
|
|
695
571
|
}
|
|
696
572
|
console.log(`Warning: App name '${appName}' is already taken.`);
|
|
697
|
-
return getAvailableAppNameInteractive(environment, imageRef
|
|
573
|
+
return getAvailableAppNameInteractive(environment, imageRef);
|
|
698
574
|
}
|
|
699
|
-
return getAvailableAppNameInteractive(environment, imageRef
|
|
575
|
+
return getAvailableAppNameInteractive(environment, imageRef);
|
|
700
576
|
}
|
|
701
577
|
async function getEnvFileInteractive(envFilePath) {
|
|
702
578
|
if (envFilePath && fs3.existsSync(envFilePath)) {
|
|
@@ -834,8 +710,8 @@ async function getPrivateKeyInteractive(privateKey) {
|
|
|
834
710
|
}
|
|
835
711
|
return privateKey;
|
|
836
712
|
}
|
|
837
|
-
const { getPrivateKeyWithSource
|
|
838
|
-
const result = await
|
|
713
|
+
const { getPrivateKeyWithSource } = await import("@layr-labs/ecloud-sdk");
|
|
714
|
+
const result = await getPrivateKeyWithSource({ privateKey: void 0 });
|
|
839
715
|
if (result) {
|
|
840
716
|
return result.key;
|
|
841
717
|
}
|
|
@@ -854,50 +730,6 @@ async function getPrivateKeyInteractive(privateKey) {
|
|
|
854
730
|
});
|
|
855
731
|
return key.trim();
|
|
856
732
|
}
|
|
857
|
-
async function getEnvironmentInteractive(environment) {
|
|
858
|
-
if (environment) {
|
|
859
|
-
try {
|
|
860
|
-
getEnvironmentConfig(environment);
|
|
861
|
-
if (!isEnvironmentAvailable(environment)) {
|
|
862
|
-
throw new Error(`Environment ${environment} is not available in this build`);
|
|
863
|
-
}
|
|
864
|
-
return environment;
|
|
865
|
-
} catch {
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
const availableEnvs = getAvailableEnvironments();
|
|
869
|
-
let defaultEnv;
|
|
870
|
-
const configDefaultEnv = getDefaultEnvironment();
|
|
871
|
-
if (configDefaultEnv && availableEnvs.includes(configDefaultEnv)) {
|
|
872
|
-
try {
|
|
873
|
-
getEnvironmentConfig(configDefaultEnv);
|
|
874
|
-
defaultEnv = configDefaultEnv;
|
|
875
|
-
} catch {
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
const choices = [];
|
|
879
|
-
if (availableEnvs.includes("sepolia")) {
|
|
880
|
-
choices.push({ name: "sepolia - Ethereum Sepolia testnet", value: "sepolia" });
|
|
881
|
-
}
|
|
882
|
-
if (availableEnvs.includes("sepolia-dev")) {
|
|
883
|
-
choices.push({ name: "sepolia-dev - Ethereum Sepolia testnet (dev)", value: "sepolia-dev" });
|
|
884
|
-
}
|
|
885
|
-
if (availableEnvs.includes("mainnet-alpha")) {
|
|
886
|
-
choices.push({
|
|
887
|
-
name: "mainnet-alpha - Ethereum mainnet (\u26A0\uFE0F uses real funds)",
|
|
888
|
-
value: "mainnet-alpha"
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
if (choices.length === 0) {
|
|
892
|
-
throw new Error("No environments available in this build");
|
|
893
|
-
}
|
|
894
|
-
const env = await select({
|
|
895
|
-
message: "Select environment:",
|
|
896
|
-
choices,
|
|
897
|
-
default: defaultEnv
|
|
898
|
-
});
|
|
899
|
-
return env;
|
|
900
|
-
}
|
|
901
733
|
var MAX_DESCRIPTION_LENGTH = 1e3;
|
|
902
734
|
var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
|
|
903
735
|
var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
|
|
@@ -1125,8 +957,7 @@ var commonFlags = {
|
|
|
1125
957
|
environment: Flags.string({
|
|
1126
958
|
required: false,
|
|
1127
959
|
description: "Deployment environment to use",
|
|
1128
|
-
env: "ECLOUD_ENV"
|
|
1129
|
-
default: async () => getDefaultEnvironment() || (getBuildType3() === "dev" ? "sepolia-dev" : "sepolia")
|
|
960
|
+
env: "ECLOUD_ENV"
|
|
1130
961
|
}),
|
|
1131
962
|
"private-key": Flags.string({
|
|
1132
963
|
required: false,
|
|
@@ -1144,227 +975,9 @@ var commonFlags = {
|
|
|
1144
975
|
default: false
|
|
1145
976
|
})
|
|
1146
977
|
};
|
|
1147
|
-
async function validateCommonFlags(flags, options) {
|
|
1148
|
-
flags["environment"] = await getEnvironmentInteractive(flags["environment"]);
|
|
1149
|
-
if (options?.requirePrivateKey !== false) {
|
|
1150
|
-
flags["private-key"] = await getPrivateKeyInteractive(flags["private-key"]);
|
|
1151
|
-
}
|
|
1152
|
-
return flags;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// src/client.ts
|
|
1156
|
-
import {
|
|
1157
|
-
createComputeModule,
|
|
1158
|
-
createBillingModule,
|
|
1159
|
-
createBuildModule,
|
|
1160
|
-
getEnvironmentConfig as getEnvironmentConfig2,
|
|
1161
|
-
requirePrivateKey,
|
|
1162
|
-
getPrivateKeyWithSource
|
|
1163
|
-
} from "@layr-labs/ecloud-sdk";
|
|
1164
|
-
async function createComputeClient(flags) {
|
|
1165
|
-
flags = await validateCommonFlags(flags);
|
|
1166
|
-
const environment = flags.environment;
|
|
1167
|
-
const environmentConfig = getEnvironmentConfig2(environment);
|
|
1168
|
-
const rpcUrl = flags["rpc-url"] || environmentConfig.defaultRPCURL;
|
|
1169
|
-
const { key: privateKey, source } = await requirePrivateKey({
|
|
1170
|
-
privateKey: flags["private-key"]
|
|
1171
|
-
});
|
|
1172
|
-
if (flags.verbose) {
|
|
1173
|
-
console.log(`Using private key from: ${source}`);
|
|
1174
|
-
}
|
|
1175
|
-
return createComputeModule({
|
|
1176
|
-
verbose: flags.verbose,
|
|
1177
|
-
privateKey,
|
|
1178
|
-
rpcUrl,
|
|
1179
|
-
environment,
|
|
1180
|
-
clientId: getClientId(),
|
|
1181
|
-
skipTelemetry: true
|
|
1182
|
-
// CLI already has telemetry, skip SDK telemetry
|
|
1183
|
-
});
|
|
1184
|
-
}
|
|
1185
|
-
async function createBuildClient(flags) {
|
|
1186
|
-
flags = await validateCommonFlags(flags, { requirePrivateKey: false });
|
|
1187
|
-
return createBuildModule({
|
|
1188
|
-
verbose: flags.verbose,
|
|
1189
|
-
privateKey: flags["private-key"],
|
|
1190
|
-
environment: flags.environment,
|
|
1191
|
-
clientId: getClientId(),
|
|
1192
|
-
skipTelemetry: true
|
|
1193
|
-
// CLI already has telemetry, skip SDK telemetry
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
978
|
|
|
1197
979
|
// src/commands/compute/app/deploy.ts
|
|
1198
980
|
import chalk from "chalk";
|
|
1199
|
-
|
|
1200
|
-
// src/utils/build.ts
|
|
1201
|
-
function formatSourceLink(repoUrl, gitRef) {
|
|
1202
|
-
const normalizedRepo = repoUrl.replace(/\.git$/, "");
|
|
1203
|
-
try {
|
|
1204
|
-
const url = new URL(normalizedRepo);
|
|
1205
|
-
const host = url.host.toLowerCase();
|
|
1206
|
-
if (host === "github.com") {
|
|
1207
|
-
const path4 = url.pathname.replace(/\/+$/, "");
|
|
1208
|
-
if (path4.split("/").filter(Boolean).length >= 2) {
|
|
1209
|
-
return `https://github.com${path4}/tree/${gitRef}`;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
} catch {
|
|
1213
|
-
}
|
|
1214
|
-
return `${repoUrl}@${gitRef}`;
|
|
1215
|
-
}
|
|
1216
|
-
function extractRepoName(repoUrl) {
|
|
1217
|
-
const normalized = repoUrl.replace(/\.git$/, "");
|
|
1218
|
-
const match = normalized.match(/\/([^/]+?)$/);
|
|
1219
|
-
return match?.[1];
|
|
1220
|
-
}
|
|
1221
|
-
function formatDependencyLines(dependencies) {
|
|
1222
|
-
if (!dependencies || Object.keys(dependencies).length === 0) return [];
|
|
1223
|
-
const lines = [];
|
|
1224
|
-
lines.push("Dependencies (resolved builds):");
|
|
1225
|
-
for (const [digest, dep] of Object.entries(dependencies)) {
|
|
1226
|
-
const name = extractRepoName(dep.repoUrl);
|
|
1227
|
-
const depSource = formatSourceLink(dep.repoUrl, dep.gitRef);
|
|
1228
|
-
lines.push(` - ${digest} \u2713${name ? ` ${name}` : ""}`);
|
|
1229
|
-
lines.push(` ${depSource}`);
|
|
1230
|
-
}
|
|
1231
|
-
return lines;
|
|
1232
|
-
}
|
|
1233
|
-
function formatVerifiableBuildSummary(options) {
|
|
1234
|
-
const lines = [];
|
|
1235
|
-
lines.push("Build completed successfully \u2713");
|
|
1236
|
-
lines.push("");
|
|
1237
|
-
lines.push(`Image: ${options.imageUrl}`);
|
|
1238
|
-
lines.push(`Digest: ${options.imageDigest}`);
|
|
1239
|
-
lines.push(`Source: ${formatSourceLink(options.repoUrl, options.gitRef)}`);
|
|
1240
|
-
const depLines = formatDependencyLines(options.dependencies);
|
|
1241
|
-
if (depLines.length) {
|
|
1242
|
-
lines.push("");
|
|
1243
|
-
lines.push(...depLines);
|
|
1244
|
-
}
|
|
1245
|
-
lines.push("");
|
|
1246
|
-
lines.push("Provenance signature verified \u2713");
|
|
1247
|
-
lines.push(`provenance_signature: ${options.provenanceSignature}`);
|
|
1248
|
-
if (options.buildId) {
|
|
1249
|
-
lines.push("");
|
|
1250
|
-
lines.push(`Build ID: ${options.buildId}`);
|
|
1251
|
-
}
|
|
1252
|
-
return lines;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
// src/utils/verifiableBuild.ts
|
|
1256
|
-
import { BUILD_STATUS } from "@layr-labs/ecloud-sdk";
|
|
1257
|
-
function assertCommitSha40(commit) {
|
|
1258
|
-
if (!/^[0-9a-f]{40}$/i.test(commit)) {
|
|
1259
|
-
throw new Error("Commit must be a 40-character hexadecimal SHA");
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
async function runVerifiableBuildAndVerify(client, request, options = {}) {
|
|
1263
|
-
const { buildId } = await client.submit(request);
|
|
1264
|
-
const completed = await client.waitForBuild(buildId, { onLog: options.onLog });
|
|
1265
|
-
if (completed.status !== BUILD_STATUS.SUCCESS) {
|
|
1266
|
-
throw new Error(`Build did not complete successfully (status: ${completed.status})`);
|
|
1267
|
-
}
|
|
1268
|
-
const [build, verify] = await Promise.all([client.get(buildId), client.verify(buildId)]);
|
|
1269
|
-
if (verify.status !== "verified") {
|
|
1270
|
-
throw new Error(`Provenance verification failed: ${verify.error}`);
|
|
1271
|
-
}
|
|
1272
|
-
return { build, verified: verify };
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
// src/utils/dockerhub.ts
|
|
1276
|
-
var DOCKERHUB_OWNER = "eigenlayer";
|
|
1277
|
-
var DOCKERHUB_REPO = "eigencloud-containers";
|
|
1278
|
-
function parseEigencloudContainersImageRef(imageRef) {
|
|
1279
|
-
const trimmed = imageRef.trim();
|
|
1280
|
-
const match = /^docker\.io\/([^/]+)\/([^:@]+):([^@\s]+)$/i.exec(trimmed);
|
|
1281
|
-
if (!match) {
|
|
1282
|
-
throw new Error("Image ref must match docker.io/eigenlayer/eigencloud-containers:<tag>");
|
|
1283
|
-
}
|
|
1284
|
-
const owner = match[1].toLowerCase();
|
|
1285
|
-
const repo = match[2].toLowerCase();
|
|
1286
|
-
const tag = match[3];
|
|
1287
|
-
if (owner !== DOCKERHUB_OWNER || repo !== DOCKERHUB_REPO) {
|
|
1288
|
-
throw new Error(`Image ref must be from docker.io/${DOCKERHUB_OWNER}/${DOCKERHUB_REPO}:<tag>`);
|
|
1289
|
-
}
|
|
1290
|
-
if (!tag.trim()) {
|
|
1291
|
-
throw new Error("Image tag cannot be empty");
|
|
1292
|
-
}
|
|
1293
|
-
return { owner, repo, tag };
|
|
1294
|
-
}
|
|
1295
|
-
function assertEigencloudContainersImageRef(imageRef) {
|
|
1296
|
-
parseEigencloudContainersImageRef(imageRef);
|
|
1297
|
-
}
|
|
1298
|
-
async function getDockerHubToken(owner, repo) {
|
|
1299
|
-
const url = new URL("https://auth.docker.io/token");
|
|
1300
|
-
url.searchParams.set("service", "registry.docker.io");
|
|
1301
|
-
url.searchParams.set("scope", `repository:${owner}/${repo}:pull`);
|
|
1302
|
-
const res = await fetch(url.toString(), { method: "GET" });
|
|
1303
|
-
if (!res.ok) {
|
|
1304
|
-
const body = await safeReadText(res);
|
|
1305
|
-
throw new Error(`Failed to fetch Docker Hub token (${res.status}): ${body || res.statusText}`);
|
|
1306
|
-
}
|
|
1307
|
-
const data = await res.json();
|
|
1308
|
-
if (!data.token) {
|
|
1309
|
-
throw new Error("Docker Hub token response missing 'token'");
|
|
1310
|
-
}
|
|
1311
|
-
return data.token;
|
|
1312
|
-
}
|
|
1313
|
-
async function safeReadText(res) {
|
|
1314
|
-
try {
|
|
1315
|
-
return (await res.text()).trim();
|
|
1316
|
-
} catch {
|
|
1317
|
-
return "";
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
async function resolveDockerHubImageDigest(imageRef) {
|
|
1321
|
-
const { owner, repo, tag } = parseEigencloudContainersImageRef(imageRef);
|
|
1322
|
-
const token = await getDockerHubToken(owner, repo);
|
|
1323
|
-
const manifestUrl = `https://registry-1.docker.io/v2/${owner}/${repo}/manifests/${encodeURIComponent(tag)}`;
|
|
1324
|
-
const headers = {
|
|
1325
|
-
Authorization: `Bearer ${token}`,
|
|
1326
|
-
Accept: "application/vnd.docker.distribution.manifest.v2+json"
|
|
1327
|
-
};
|
|
1328
|
-
let res = await fetch(manifestUrl, { method: "HEAD", headers });
|
|
1329
|
-
if (!res.ok) {
|
|
1330
|
-
res = await fetch(manifestUrl, { method: "GET", headers });
|
|
1331
|
-
}
|
|
1332
|
-
if (!res.ok) {
|
|
1333
|
-
const body = await safeReadText(res);
|
|
1334
|
-
throw new Error(
|
|
1335
|
-
`Failed to resolve digest for ${imageRef} (${res.status}) at ${manifestUrl}: ${body || res.statusText}`
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
const digest = res.headers.get("docker-content-digest") || res.headers.get("Docker-Content-Digest");
|
|
1339
|
-
if (!digest) {
|
|
1340
|
-
throw new Error(
|
|
1341
|
-
`Docker registry response missing Docker-Content-Digest header for ${imageRef}`
|
|
1342
|
-
);
|
|
1343
|
-
}
|
|
1344
|
-
if (!/^sha256:[0-9a-f]{64}$/i.test(digest)) {
|
|
1345
|
-
throw new Error(`Unexpected digest format from Docker registry: ${digest}`);
|
|
1346
|
-
}
|
|
1347
|
-
return digest;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
// src/utils/tls.ts
|
|
1351
|
-
import fs4 from "fs";
|
|
1352
|
-
function isTlsEnabledFromDomain(domain) {
|
|
1353
|
-
const d = (domain ?? "").trim();
|
|
1354
|
-
if (!d) return false;
|
|
1355
|
-
if (d.toLowerCase() === "localhost") return false;
|
|
1356
|
-
return true;
|
|
1357
|
-
}
|
|
1358
|
-
function isTlsEnabledFromEnvFile(envFilePath) {
|
|
1359
|
-
if (!envFilePath) return false;
|
|
1360
|
-
if (!fs4.existsSync(envFilePath)) return false;
|
|
1361
|
-
const envContent = fs4.readFileSync(envFilePath, "utf-8");
|
|
1362
|
-
const match = envContent.match(/^DOMAIN=(.+)$/m);
|
|
1363
|
-
if (!match?.[1]) return false;
|
|
1364
|
-
return isTlsEnabledFromDomain(match[1]);
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// src/commands/compute/app/deploy.ts
|
|
1368
981
|
var AppDeploy = class _AppDeploy extends Command {
|
|
1369
982
|
static description = "Deploy new app";
|
|
1370
983
|
static flags = {
|
|
@@ -1427,184 +1040,26 @@ var AppDeploy = class _AppDeploy extends Command {
|
|
|
1427
1040
|
image: Flags2.string({
|
|
1428
1041
|
required: false,
|
|
1429
1042
|
description: "Path to app icon/logo image - JPG/PNG, max 4MB, square recommended (optional)"
|
|
1430
|
-
}),
|
|
1431
|
-
// Verifiable build flags
|
|
1432
|
-
verifiable: Flags2.boolean({
|
|
1433
|
-
description: "Enable verifiable build mode (either build from git source via --repo/--commit, or deploy a prebuilt verifiable image via --image-ref)",
|
|
1434
|
-
default: false
|
|
1435
|
-
}),
|
|
1436
|
-
repo: Flags2.string({
|
|
1437
|
-
description: "Git repository URL (required with --verifiable git source mode)",
|
|
1438
|
-
env: "ECLOUD_BUILD_REPO"
|
|
1439
|
-
}),
|
|
1440
|
-
commit: Flags2.string({
|
|
1441
|
-
description: "Git commit SHA (required with --verifiable git source mode)",
|
|
1442
|
-
env: "ECLOUD_BUILD_COMMIT"
|
|
1443
|
-
}),
|
|
1444
|
-
"build-dockerfile": Flags2.string({
|
|
1445
|
-
description: "Dockerfile path for verifiable build (git source mode)",
|
|
1446
|
-
default: "Dockerfile",
|
|
1447
|
-
env: "ECLOUD_BUILD_DOCKERFILE"
|
|
1448
|
-
}),
|
|
1449
|
-
"build-context": Flags2.string({
|
|
1450
|
-
description: "Build context path for verifiable build (git source mode)",
|
|
1451
|
-
default: ".",
|
|
1452
|
-
env: "ECLOUD_BUILD_CONTEXT"
|
|
1453
|
-
}),
|
|
1454
|
-
"build-dependencies": Flags2.string({
|
|
1455
|
-
description: "Dependency digests for verifiable build (git source mode) (sha256:...)",
|
|
1456
|
-
multiple: true
|
|
1457
1043
|
})
|
|
1458
1044
|
};
|
|
1459
1045
|
async run() {
|
|
1460
1046
|
return withTelemetry(this, async () => {
|
|
1461
1047
|
const { flags } = await this.parse(_AppDeploy);
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
let buildClient;
|
|
1468
|
-
const getBuildClient = async () => {
|
|
1469
|
-
if (buildClient) return buildClient;
|
|
1470
|
-
buildClient = await createBuildClient({
|
|
1471
|
-
...flags,
|
|
1472
|
-
"private-key": privateKey
|
|
1473
|
-
});
|
|
1474
|
-
return buildClient;
|
|
1475
|
-
};
|
|
1476
|
-
let verifiableImageUrl;
|
|
1477
|
-
let verifiableImageDigest;
|
|
1478
|
-
let suggestedAppBaseName;
|
|
1479
|
-
let skipDefaultAppName = false;
|
|
1480
|
-
let verifiableMode = "none";
|
|
1481
|
-
let envFilePath;
|
|
1482
|
-
const suggestAppBaseNameFromRepoUrl = (repoUrl) => {
|
|
1483
|
-
const normalized = String(repoUrl || "").trim().replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
1484
|
-
if (!normalized) return void 0;
|
|
1485
|
-
const lastSlash = normalized.lastIndexOf("/");
|
|
1486
|
-
const lastColon = normalized.lastIndexOf(":");
|
|
1487
|
-
const idx = Math.max(lastSlash, lastColon);
|
|
1488
|
-
const raw = (idx >= 0 ? normalized.slice(idx + 1) : normalized).trim();
|
|
1489
|
-
if (!raw) return void 0;
|
|
1490
|
-
const cleaned = raw.toLowerCase().replace(/_/g, "-").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
1491
|
-
return cleaned || void 0;
|
|
1048
|
+
const logger = {
|
|
1049
|
+
info: (msg) => this.log(msg),
|
|
1050
|
+
warn: (msg) => this.warn(msg),
|
|
1051
|
+
error: (msg) => this.error(msg),
|
|
1052
|
+
debug: (msg) => flags.verbose && this.log(msg)
|
|
1492
1053
|
};
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
if (!flags.commit)
|
|
1499
|
-
this.error("--commit is required when using --verifiable (git source mode)");
|
|
1500
|
-
try {
|
|
1501
|
-
assertCommitSha40(flags.commit);
|
|
1502
|
-
} catch (e) {
|
|
1503
|
-
this.error(e?.message || String(e));
|
|
1504
|
-
}
|
|
1505
|
-
} else if (flags["image-ref"]) {
|
|
1506
|
-
verifiableMode = "prebuilt";
|
|
1507
|
-
try {
|
|
1508
|
-
assertEigencloudContainersImageRef(flags["image-ref"]);
|
|
1509
|
-
} catch (e) {
|
|
1510
|
-
this.error(e?.message || String(e));
|
|
1511
|
-
}
|
|
1512
|
-
} else {
|
|
1513
|
-
this.error(
|
|
1514
|
-
"When using --verifiable, you must provide either --repo/--commit or --image-ref"
|
|
1515
|
-
);
|
|
1516
|
-
}
|
|
1517
|
-
} else {
|
|
1518
|
-
if (!flags.dockerfile) {
|
|
1519
|
-
const useVerifiable = await promptUseVerifiableBuild();
|
|
1520
|
-
if (useVerifiable) {
|
|
1521
|
-
const sourceType = await promptVerifiableSourceType();
|
|
1522
|
-
verifiableMode = sourceType;
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
if (verifiableMode === "git") {
|
|
1527
|
-
const inputs = flags.verifiable ? {
|
|
1528
|
-
repoUrl: flags.repo,
|
|
1529
|
-
gitRef: flags.commit,
|
|
1530
|
-
dockerfilePath: flags["build-dockerfile"],
|
|
1531
|
-
caddyfilePath: void 0,
|
|
1532
|
-
buildContextPath: flags["build-context"],
|
|
1533
|
-
dependencies: flags["build-dependencies"]
|
|
1534
|
-
} : await promptVerifiableGitSourceInputs();
|
|
1535
|
-
envFilePath = await getEnvFileInteractive(flags["env-file"]);
|
|
1536
|
-
const includeTlsCaddyfile = isTlsEnabledFromEnvFile(envFilePath);
|
|
1537
|
-
if (includeTlsCaddyfile && !inputs.caddyfilePath) {
|
|
1538
|
-
inputs.caddyfilePath = "Caddyfile";
|
|
1539
|
-
}
|
|
1540
|
-
this.log(chalk.blue("Building from source with verifiable build..."));
|
|
1541
|
-
this.log("");
|
|
1542
|
-
const buildClient2 = await getBuildClient();
|
|
1543
|
-
const { build, verified } = await runVerifiableBuildAndVerify(buildClient2, inputs, {
|
|
1544
|
-
onLog: (chunk) => process.stdout.write(chunk)
|
|
1545
|
-
});
|
|
1546
|
-
if (!build.imageUrl || !build.imageDigest) {
|
|
1547
|
-
this.error(
|
|
1548
|
-
"Build completed but did not return imageUrl/imageDigest; cannot deploy verifiable build"
|
|
1549
|
-
);
|
|
1550
|
-
}
|
|
1551
|
-
verifiableImageUrl = build.imageUrl;
|
|
1552
|
-
verifiableImageDigest = build.imageDigest;
|
|
1553
|
-
suggestedAppBaseName = suggestAppBaseNameFromRepoUrl(build.repoUrl);
|
|
1554
|
-
for (const line of formatVerifiableBuildSummary({
|
|
1555
|
-
buildId: build.buildId,
|
|
1556
|
-
imageUrl: build.imageUrl,
|
|
1557
|
-
imageDigest: build.imageDigest,
|
|
1558
|
-
repoUrl: build.repoUrl,
|
|
1559
|
-
gitRef: build.gitRef,
|
|
1560
|
-
dependencies: build.dependencies,
|
|
1561
|
-
provenanceSignature: verified.provenanceSignature
|
|
1562
|
-
})) {
|
|
1563
|
-
this.log(line);
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
if (verifiableMode === "prebuilt") {
|
|
1567
|
-
const imageRef2 = flags.verifiable ? flags["image-ref"] : await promptVerifiablePrebuiltImageRef();
|
|
1568
|
-
try {
|
|
1569
|
-
assertEigencloudContainersImageRef(imageRef2);
|
|
1570
|
-
} catch (e) {
|
|
1571
|
-
this.error(e?.message || String(e));
|
|
1572
|
-
}
|
|
1573
|
-
this.log(chalk.blue("Resolving and verifying prebuilt verifiable image..."));
|
|
1574
|
-
this.log("");
|
|
1575
|
-
const digest = await resolveDockerHubImageDigest(imageRef2);
|
|
1576
|
-
const buildClient2 = await getBuildClient();
|
|
1577
|
-
const verify = await buildClient2.verify(digest);
|
|
1578
|
-
if (verify.status !== "verified") {
|
|
1579
|
-
this.error(`Provenance verification failed: ${verify.error}`);
|
|
1580
|
-
}
|
|
1581
|
-
verifiableImageUrl = imageRef2;
|
|
1582
|
-
verifiableImageDigest = digest;
|
|
1583
|
-
skipDefaultAppName = true;
|
|
1584
|
-
for (const line of formatVerifiableBuildSummary({
|
|
1585
|
-
buildId: verify.buildId,
|
|
1586
|
-
imageUrl: imageRef2,
|
|
1587
|
-
imageDigest: digest,
|
|
1588
|
-
repoUrl: verify.repoUrl,
|
|
1589
|
-
gitRef: verify.gitRef,
|
|
1590
|
-
dependencies: void 0,
|
|
1591
|
-
provenanceSignature: verify.provenanceSignature
|
|
1592
|
-
})) {
|
|
1593
|
-
this.log(line);
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
const isVerifiable = verifiableMode !== "none";
|
|
1597
|
-
const dockerfilePath = isVerifiable ? "" : await getDockerfileInteractive(flags.dockerfile);
|
|
1054
|
+
const environment = flags.environment || "sepolia";
|
|
1055
|
+
const environmentConfig = getEnvironmentConfig2(environment);
|
|
1056
|
+
const rpcUrl = flags["rpc-url"] || environmentConfig.defaultRPCURL;
|
|
1057
|
+
const privateKey = await getPrivateKeyInteractive(flags["private-key"]);
|
|
1058
|
+
const dockerfilePath = await getDockerfileInteractive(flags.dockerfile);
|
|
1598
1059
|
const buildFromDockerfile = dockerfilePath !== "";
|
|
1599
|
-
const imageRef =
|
|
1600
|
-
const appName = await getOrPromptAppName(
|
|
1601
|
-
|
|
1602
|
-
environment,
|
|
1603
|
-
imageRef,
|
|
1604
|
-
suggestedAppBaseName,
|
|
1605
|
-
skipDefaultAppName
|
|
1606
|
-
);
|
|
1607
|
-
envFilePath = envFilePath ?? await getEnvFileInteractive(flags["env-file"]);
|
|
1060
|
+
const imageRef = await getImageReferenceInteractive(flags["image-ref"], buildFromDockerfile);
|
|
1061
|
+
const appName = await getOrPromptAppName(flags.name, environment, imageRef);
|
|
1062
|
+
const envFilePath = await getEnvFileInteractive(flags["env-file"]);
|
|
1608
1063
|
const availableTypes = await fetchAvailableInstanceTypes(
|
|
1609
1064
|
environmentConfig,
|
|
1610
1065
|
privateKey,
|
|
@@ -1623,23 +1078,22 @@ var AppDeploy = class _AppDeploy extends Command {
|
|
|
1623
1078
|
flags["resource-usage-monitoring"]
|
|
1624
1079
|
);
|
|
1625
1080
|
const logVisibility = logSettings.publicLogs ? "public" : logSettings.logRedirect ? "private" : "off";
|
|
1626
|
-
const { prepared, gasEstimate } =
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
});
|
|
1081
|
+
const { prepared, gasEstimate } = await prepareDeploy(
|
|
1082
|
+
{
|
|
1083
|
+
privateKey,
|
|
1084
|
+
rpcUrl,
|
|
1085
|
+
environment,
|
|
1086
|
+
dockerfilePath,
|
|
1087
|
+
imageRef,
|
|
1088
|
+
envFilePath,
|
|
1089
|
+
appName,
|
|
1090
|
+
instanceType,
|
|
1091
|
+
logVisibility,
|
|
1092
|
+
resourceUsageMonitoring,
|
|
1093
|
+
skipTelemetry: true
|
|
1094
|
+
},
|
|
1095
|
+
logger
|
|
1096
|
+
);
|
|
1643
1097
|
this.log(`
|
|
1644
1098
|
Estimated transaction cost: ${chalk.cyan(gasEstimate.maxCostEth)} ETH`);
|
|
1645
1099
|
if (isMainnet(environmentConfig)) {
|
|
@@ -1650,10 +1104,16 @@ ${chalk.gray(`Deployment cancelled`)}`);
|
|
|
1650
1104
|
return;
|
|
1651
1105
|
}
|
|
1652
1106
|
}
|
|
1653
|
-
const res = await
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1107
|
+
const res = await executeDeploy(
|
|
1108
|
+
prepared,
|
|
1109
|
+
{
|
|
1110
|
+
maxFeePerGas: gasEstimate.maxFeePerGas,
|
|
1111
|
+
maxPriorityFeePerGas: gasEstimate.maxPriorityFeePerGas
|
|
1112
|
+
},
|
|
1113
|
+
logger,
|
|
1114
|
+
true
|
|
1115
|
+
// skipTelemetry
|
|
1116
|
+
);
|
|
1657
1117
|
if (!flags["skip-profile"]) {
|
|
1658
1118
|
const hasProfileFlags = flags.website || flags.description || flags["x-url"] || flags.image;
|
|
1659
1119
|
let profile = null;
|
|
@@ -1672,35 +1132,47 @@ ${chalk.gray(`Deployment cancelled`)}`);
|
|
|
1672
1132
|
try {
|
|
1673
1133
|
profile = await getAppProfileInteractive(appName, true) || null;
|
|
1674
1134
|
} catch {
|
|
1675
|
-
|
|
1676
|
-
this.log("Profile collection skipped or cancelled");
|
|
1677
|
-
}
|
|
1135
|
+
logger.debug("Profile collection skipped or cancelled");
|
|
1678
1136
|
}
|
|
1679
1137
|
}
|
|
1680
1138
|
if (profile) {
|
|
1681
|
-
|
|
1139
|
+
logger.info("Uploading app profile...");
|
|
1682
1140
|
try {
|
|
1683
|
-
|
|
1684
|
-
|
|
1141
|
+
const userApiClient = new UserApiClient3(
|
|
1142
|
+
environmentConfig,
|
|
1143
|
+
privateKey,
|
|
1144
|
+
rpcUrl,
|
|
1145
|
+
getClientId()
|
|
1146
|
+
);
|
|
1147
|
+
await userApiClient.uploadAppProfile(
|
|
1148
|
+
res.appId,
|
|
1149
|
+
profile.name,
|
|
1150
|
+
profile.website,
|
|
1151
|
+
profile.description,
|
|
1152
|
+
profile.xURL,
|
|
1153
|
+
profile.imagePath
|
|
1154
|
+
);
|
|
1155
|
+
logger.info("\u2713 Profile uploaded successfully");
|
|
1685
1156
|
try {
|
|
1686
1157
|
invalidateProfileCache(environment);
|
|
1687
1158
|
} catch (cacheErr) {
|
|
1688
|
-
|
|
1689
|
-
this.log(`Failed to invalidate profile cache: ${cacheErr.message}`);
|
|
1690
|
-
}
|
|
1159
|
+
logger.debug(`Failed to invalidate profile cache: ${cacheErr.message}`);
|
|
1691
1160
|
}
|
|
1692
1161
|
} catch (uploadErr) {
|
|
1693
|
-
|
|
1162
|
+
logger.warn(`Failed to upload profile: ${uploadErr.message}`);
|
|
1694
1163
|
}
|
|
1695
1164
|
}
|
|
1696
1165
|
}
|
|
1697
|
-
const ipAddress = await
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1166
|
+
const ipAddress = await watchDeployment(
|
|
1167
|
+
res.appId,
|
|
1168
|
+
privateKey,
|
|
1169
|
+
rpcUrl,
|
|
1170
|
+
environment,
|
|
1171
|
+
logger,
|
|
1172
|
+
getClientId(),
|
|
1173
|
+
true
|
|
1174
|
+
// skipTelemetry - CLI already has telemetry
|
|
1175
|
+
);
|
|
1704
1176
|
this.log(
|
|
1705
1177
|
`
|
|
1706
1178
|
\u2705 ${chalk.green(`App deployed successfully ${chalk.bold(`(id: ${res.appId}, ip: ${ipAddress})`)}`)}`
|